diff --git a/.clang-format b/.clang-format index bf20a4e4c4a..41f828787b2 100644 --- a/.clang-format +++ b/.clang-format @@ -180,6 +180,7 @@ ForEachMacros: - CTX_DATA_BEGIN_WITH_ID - DEG_OBJECT_ITER_BEGIN - DEG_OBJECT_ITER_FOR_RENDER_ENGINE_BEGIN + - DRW_ENABLED_ENGINE_ITER - DRIVER_TARGETS_LOOPER_BEGIN - DRIVER_TARGETS_USED_LOOPER_BEGIN - FOREACH_BASE_IN_EDIT_MODE_BEGIN @@ -267,3 +268,6 @@ ForEachMacros: StatementMacros: - PyObject_HEAD - PyObject_VAR_HEAD + +MacroBlockBegin: "^BSDF_CLOSURE_CLASS_BEGIN$" +MacroBlockEnd: "^BSDF_CLOSURE_CLASS_END$" diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000000..93a29565e54 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +This repository is only used as a mirror of git.blender.org. Blender development happens on +https://developer.blender.org. + +To get started with contributing code, please see: +https://wiki.blender.org/wiki/Process/Contributing_Code diff --git a/CMakeLists.txt b/CMakeLists.txt index 47712f0ac1e..62e7d9b2941 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,7 +30,7 @@ if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) "CMake generation for blender is not allowed within the source directory!" "\n Remove \"${CMAKE_SOURCE_DIR}/CMakeCache.txt\" and try again from another folder, e.g.:" "\n " - "\n rm CMakeCache.txt" + "\n rm -rf CMakeCache.txt CMakeFiles" "\n cd .." "\n mkdir cmake-make" "\n cd cmake-make" @@ -156,6 +156,15 @@ get_blender_version() option(WITH_BLENDER "Build blender (disable to build only the blender player)" ON) mark_as_advanced(WITH_BLENDER) +if(APPLE) + # Currently this causes a build error linking, disable. + set(WITH_BLENDER_THUMBNAILER OFF) +elseif(WIN32) + option(WITH_BLENDER_THUMBNAILER "Build \"BlendThumb.dll\" helper for Windows explorer integration" ON) +else() + option(WITH_BLENDER_THUMBNAILER "Build \"blender-thumbnailer\" thumbnail extraction utility" ON) +endif() + option(WITH_INTERNATIONAL "Enable I18N (International fonts and text)" ON) option(WITH_PYTHON "Enable Embedded Python API (only disable for development)" ON) @@ -388,46 +397,55 @@ if(WITH_PYTHON_INSTALL) set(PYTHON_REQUESTS_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'requests' module") mark_as_advanced(PYTHON_REQUESTS_PATH) endif() + + option(WITH_PYTHON_INSTALL_ZSTANDARD "Copy zstandard into the blender install folder" ON) + set(PYTHON_ZSTANDARD_PATH "" CACHE PATH "Path to python site-packages or dist-packages containing 'zstandard' module") + mark_as_advanced(PYTHON_ZSTANDARD_PATH) endif() option(WITH_CPU_SIMD "Enable SIMD instruction if they're detected on the host machine" ON) mark_as_advanced(WITH_CPU_SIMD) # Cycles -option(WITH_CYCLES "Enable Cycles Render Engine" ON) -option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF) -option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF) -option(WITH_CYCLES_OSL "Build Cycles with OSL support" ON) -option(WITH_CYCLES_EMBREE "Build Cycles with Embree support" ON) -option(WITH_CYCLES_CUDA_BINARIES "Build Cycles CUDA binaries" OFF) -option(WITH_CYCLES_CUBIN_COMPILER "Build cubins with nvrtc based compiler instead of nvcc" OFF) -option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF) -mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL) -set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified device types (CPU CUDA OPTIX OPENCL)" ) -set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for") -mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) -unset(PLATFORM_DEFAULT) -option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON) -option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF) -option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF) -option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF) +option(WITH_CYCLES "Enable Cycles Render Engine" ON) +option(WITH_CYCLES_OSL "Build Cycles with OpenShadingLanguage support" ON) +option(WITH_CYCLES_EMBREE "Build Cycles with Embree support" ON) +option(WITH_CYCLES_LOGGING "Build Cycles with logging support" ON) + +option(WITH_CYCLES_STANDALONE "Build Cycles standalone application" OFF) +option(WITH_CYCLES_STANDALONE_GUI "Build Cycles standalone with GUI" OFF) + +option(WITH_CYCLES_DEBUG_NAN "Build Cycles with additional asserts for detecting NaNs and invalid values" OFF) +option(WITH_CYCLES_NATIVE_ONLY "Build Cycles with native kernel only (which fits current CPU, use for development only)" OFF) +option(WITH_CYCLES_KERNEL_ASAN "Build Cycles kernels with address sanitizer when WITH_COMPILER_ASAN is on, even if it's very slow" OFF) +set(CYCLES_TEST_DEVICES CPU CACHE STRING "Run regression tests on the specified device types (CPU CUDA OPTIX HIP)" ) mark_as_advanced(WITH_CYCLES_KERNEL_ASAN) -mark_as_advanced(WITH_CYCLES_CUBIN_COMPILER) mark_as_advanced(WITH_CYCLES_LOGGING) mark_as_advanced(WITH_CYCLES_DEBUG_NAN) mark_as_advanced(WITH_CYCLES_NATIVE_ONLY) -option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles CUDA compute support" ON) -option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles OptiX support" OFF) -option(WITH_CYCLES_DEVICE_OPENCL "Enable Cycles OpenCL compute support" ON) -option(WITH_CYCLES_NETWORK "Enable Cycles compute over network support (EXPERIMENTAL and unfinished)" OFF) +# NVIDIA CUDA & OptiX +option(WITH_CYCLES_DEVICE_CUDA "Enable Cycles NVIDIA CUDA compute support" ON) +option(WITH_CYCLES_DEVICE_OPTIX "Enable Cycles NVIDIA OptiX support" ON) mark_as_advanced(WITH_CYCLES_DEVICE_CUDA) -mark_as_advanced(WITH_CYCLES_DEVICE_OPENCL) -mark_as_advanced(WITH_CYCLES_NETWORK) -option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime" ON) +option(WITH_CYCLES_CUDA_BINARIES "Build Cycles NVIDIA CUDA binaries" OFF) +set(CYCLES_CUDA_BINARIES_ARCH sm_30 sm_35 sm_37 sm_50 sm_52 sm_60 sm_61 sm_70 sm_75 sm_86 compute_75 CACHE STRING "CUDA architectures to build binaries for") +option(WITH_CYCLES_CUBIN_COMPILER "Build cubins with nvrtc based compiler instead of nvcc" OFF) +option(WITH_CYCLES_CUDA_BUILD_SERIAL "Build cubins one after another (useful on machines with limited RAM)" OFF) +option(WITH_CUDA_DYNLOAD "Dynamically load CUDA libraries at runtime (for developers, makes cuda-gdb work)" ON) +mark_as_advanced(CYCLES_CUDA_BINARIES_ARCH) +mark_as_advanced(WITH_CYCLES_CUBIN_COMPILER) +mark_as_advanced(WITH_CYCLES_CUDA_BUILD_SERIAL) mark_as_advanced(WITH_CUDA_DYNLOAD) +# AMD HIP +option(WITH_CYCLES_DEVICE_HIP "Enable Cycles AMD HIP support" OFF) +option(WITH_CYCLES_HIP_BINARIES "Build Cycles AMD HIP binaries" OFF) +set(CYCLES_HIP_BINARIES_ARCH gfx1010 gfx1011 gfx1012 gfx1030 gfx1031 gfx1032 gfx1034 CACHE STRING "AMD HIP architectures to build binaries for") +mark_as_advanced(WITH_CYCLES_DEVICE_HIP) +mark_as_advanced(CYCLES_HIP_BINARIES_ARCH) + # Draw Manager option(WITH_DRAW_DEBUG "Add extra debug capabilities to Draw Manager" OFF) mark_as_advanced(WITH_DRAW_DEBUG) @@ -825,6 +843,11 @@ if(NOT WITH_CUDA_DYNLOAD) endif() endif() +if(WITH_CYCLES_DEVICE_HIP) + # Currently HIP must be dynamically loaded, this may change in future toolkits + set(WITH_HIP_DYNLOAD ON) +endif() + #----------------------------------------------------------------------------- # Check check if submodules are cloned @@ -1720,6 +1743,12 @@ if(WITH_PYTHON) elseif(WITH_PYTHON_INSTALL_REQUESTS) find_python_package(requests "") endif() + + if(WIN32 OR APPLE) + # pass, we have this in lib/python/site-packages + elseif(WITH_PYTHON_INSTALL_ZSTANDARD) + find_python_package(zstandard "") + endif() endif() # Select C++17 as the standard for C++ projects. @@ -1854,6 +1883,9 @@ elseif(WITH_CYCLES_STANDALONE) if(WITH_CUDA_DYNLOAD) add_subdirectory(extern/cuew) endif() + if(WITH_HIP_DYNLOAD) + add_subdirectory(extern/hipew) + endif() if(NOT WITH_SYSTEM_GLEW) add_subdirectory(extern/glew) endif() @@ -1988,6 +2020,7 @@ if(FIRST_RUN) endif() info_cfg_option(WITH_PYTHON_INSTALL) info_cfg_option(WITH_PYTHON_INSTALL_NUMPY) + info_cfg_option(WITH_PYTHON_INSTALL_ZSTANDARD) info_cfg_option(WITH_PYTHON_MODULE) info_cfg_option(WITH_PYTHON_SAFETY) diff --git a/GNUmakefile b/GNUmakefile index d620e5c4363..57892959d2a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -27,7 +27,7 @@ define HELP_TEXT Blender Convenience Targets - Provided for building Blender, (multiple at once can be used). + Provided for building Blender (multiple targets can be used at once). * debug: Build a debug binary. * full: Enable all supported dependencies & options. @@ -40,6 +40,8 @@ Blender Convenience Targets * ninja: Use ninja build tool for faster builds. * ccache: Use ccache for faster rebuilds. + Note: when passing in multiple targets their order is not important. + So for a fast build you can for e.g. run 'make lite ccache ninja'. Note: passing the argument 'BUILD_DIR=path' when calling make will override the default build dir. Note: passing the argument 'BUILD_CMAKE_ARGS=args' lets you add cmake arguments. diff --git a/build_files/build_environment/cmake/numpy.cmake b/build_files/build_environment/cmake/numpy.cmake index 73fa6e75556..1b58fd6aee9 100644 --- a/build_files/build_environment/cmake/numpy.cmake +++ b/build_files/build_environment/cmake/numpy.cmake @@ -38,7 +38,6 @@ ExternalProject_Add(external_numpy PREFIX ${BUILD_DIR}/numpy PATCH_COMMAND ${NUMPY_PATCH} CONFIGURE_COMMAND "" - PATCH_COMMAND COMMAND ${PATCH_CMD} -p 1 -d ${BUILD_DIR}/numpy/src/external_numpy < ${PATCH_DIR}/numpy.diff LOG_BUILD 1 BUILD_COMMAND ${PYTHON_BINARY} ${BUILD_DIR}/numpy/src/external_numpy/setup.py build ${NUMPY_BUILD_OPTION} install --old-and-unmanageable INSTALL_COMMAND "" diff --git a/build_files/build_environment/cmake/python_site_packages.cmake b/build_files/build_environment/cmake/python_site_packages.cmake index 2b75a6a244f..a8918fdb784 100644 --- a/build_files/build_environment/cmake/python_site_packages.cmake +++ b/build_files/build_environment/cmake/python_site_packages.cmake @@ -18,14 +18,20 @@ if(WIN32 AND BUILD_MODE STREQUAL Debug) set(SITE_PACKAGES_EXTRA --global-option build --global-option --debug) + # zstandard is determined to build and link release mode libs in a debug + # configuration, the only way to make it happy is to bend to its will + # and give it a library to link with. + set(PIP_CONFIGURE_COMMAND ${CMAKE_COMMAND} -E copy ${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}_d.lib ${LIBDIR}/python/libs/python${PYTHON_SHORT_VERSION_NO_DOTS}.lib) +else() + set(PIP_CONFIGURE_COMMAND echo ".") endif() ExternalProject_Add(external_python_site_packages DOWNLOAD_COMMAND "" - CONFIGURE_COMMAND "" + CONFIGURE_COMMAND ${PIP_CONFIGURE_COMMAND} BUILD_COMMAND "" PREFIX ${BUILD_DIR}/site_packages - INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} chardet==${CHARDET_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} --no-binary :all: + INSTALL_COMMAND ${PYTHON_BINARY} -m pip install ${SITE_PACKAGES_EXTRA} cython==${CYTHON_VERSION} idna==${IDNA_VERSION} charset-normalizer==${CHARSET_NORMALIZER_VERSION} urllib3==${URLLIB3_VERSION} certifi==${CERTIFI_VERSION} requests==${REQUESTS_VERSION} zstandard==${ZSTANDARD_VERSION} --no-binary :all: ) if(USE_PIP_NUMPY) diff --git a/build_files/build_environment/cmake/versions.cmake b/build_files/build_environment/cmake/versions.cmake index d1675bdddfd..f2c245dc380 100644 --- a/build_files/build_environment/cmake/versions.cmake +++ b/build_files/build_environment/cmake/versions.cmake @@ -189,11 +189,11 @@ set(OSL_HASH 1abd7ce40481771a9fa937f19595d2f2) set(OSL_HASH_TYPE MD5) set(OSL_FILE OpenShadingLanguage-${OSL_VERSION}.tar.gz) -set(PYTHON_VERSION 3.9.2) +set(PYTHON_VERSION 3.9.7) set(PYTHON_SHORT_VERSION 3.9) set(PYTHON_SHORT_VERSION_NO_DOTS 39) set(PYTHON_URI https://www.python.org/ftp/python/${PYTHON_VERSION}/Python-${PYTHON_VERSION}.tar.xz) -set(PYTHON_HASH f0dc9000312abeb16de4eccce9a870ab) +set(PYTHON_HASH fddb060b483bc01850a3f412eea1d954) set(PYTHON_HASH_TYPE MD5) set(PYTHON_FILE Python-${PYTHON_VERSION}.tar.xz) @@ -215,17 +215,18 @@ set(NANOVDB_HASH e7b9e863ec2f3b04ead171dec2322807) set(NANOVDB_HASH_TYPE MD5) set(NANOVDB_FILE nano-vdb-${NANOVDB_GIT_UID}.tar.gz) -set(IDNA_VERSION 2.10) -set(CHARDET_VERSION 4.0.0) -set(URLLIB3_VERSION 1.26.3) -set(CERTIFI_VERSION 2020.12.5) -set(REQUESTS_VERSION 2.25.1) -set(CYTHON_VERSION 0.29.21) +set(IDNA_VERSION 3.2) +set(CHARSET_NORMALIZER_VERSION 2.0.6) +set(URLLIB3_VERSION 1.26.7) +set(CERTIFI_VERSION 2021.10.8) +set(REQUESTS_VERSION 2.26.0) +set(CYTHON_VERSION 0.29.24) +set(ZSTANDARD_VERSION 0.15.2 ) -set(NUMPY_VERSION 1.19.5) -set(NUMPY_SHORT_VERSION 1.19) +set(NUMPY_VERSION 1.21.2) +set(NUMPY_SHORT_VERSION 1.21) set(NUMPY_URI https://github.com/numpy/numpy/releases/download/v${NUMPY_VERSION}/numpy-${NUMPY_VERSION}.zip) -set(NUMPY_HASH f6a1b48717c552bbc18f1adc3cc1fe0e) +set(NUMPY_HASH 5638d5dae3ca387be562912312db842e) set(NUMPY_HASH_TYPE MD5) set(NUMPY_FILE numpy-${NUMPY_VERSION}.zip) diff --git a/build_files/build_environment/install_deps.sh b/build_files/build_environment/install_deps.sh index ecaff307885..2bb7175da00 100755 --- a/build_files/build_environment/install_deps.sh +++ b/build_files/build_environment/install_deps.sh @@ -371,71 +371,78 @@ NO_BUILD=false NO_CONFIRM=false USE_CXX11=true -# Note about versions: Min is inclusive, Max is exclusive (i.e. XXX_VERSION_MIN <= ACTUAL_VERSION < XXX_VERSION_MAX) +# Note about versions: Min is inclusive, Mex is 'minimum exclusive' (i.e. XXX_VERSION_MIN <= ACTUAL_VERSION < XXX_VERSION_MEX) # XXX_VERSION is officially supported/used version in official builds. # XXX_VERSION_SHORT is used for various things, like preferred version (when distribution provides several of them), # and to name shortcuts to built libraries' installation directories... CLANG_FORMAT_VERSION_MIN="6.0" -CLANG_FORMAT_VERSION_MAX="10.0" +CLANG_FORMAT_VERSION_MEX="10.0" -PYTHON_VERSION="3.9.2" +PYTHON_VERSION="3.9.7" PYTHON_VERSION_SHORT="3.9" PYTHON_VERSION_MIN="3.7" -PYTHON_VERSION_MAX="3.11" +PYTHON_VERSION_MEX="3.11" PYTHON_VERSION_INSTALLED=$PYTHON_VERSION_SHORT PYTHON_FORCE_BUILD=false PYTHON_FORCE_REBUILD=false PYTHON_SKIP=false # Additional Python modules. -PYTHON_IDNA_VERSION="2.9" +PYTHON_IDNA_VERSION="3.2" PYTHON_IDNA_VERSION_MIN="2.0" -PYTHON_IDNA_VERSION_MAX="3.0" +PYTHON_IDNA_VERSION_MEX="4.0" PYTHON_IDNA_NAME="idna" -PYTHON_CHARDET_VERSION="3.0.4" -PYTHON_CHARDET_VERSION_MIN="3.0" -PYTHON_CHARDET_VERSION_MAX="5.0" -PYTHON_CHARDET_NAME="chardet" +PYTHON_CHARSET_NORMALIZER_VERSION="2.0.6" +PYTHON_CHARSET_NORMALIZER_VERSION_MIN="2.0.6" +PYTHON_CHARSET_NORMALIZER_VERSION_MEX="2.1.0" # requests uses `charset_normalizer~=2.0.0` +PYTHON_CHARSET_NORMALIZER_NAME="charset-normalizer" -PYTHON_URLLIB3_VERSION="1.25.9" +PYTHON_URLLIB3_VERSION="1.26.7" PYTHON_URLLIB3_VERSION_MIN="1.0" -PYTHON_URLLIB3_VERSION_MAX="2.0" +PYTHON_URLLIB3_VERSION_MEX="2.0" PYTHON_URLLIB3_NAME="urllib3" -PYTHON_CERTIFI_VERSION="2020.4.5.2" -PYTHON_CERTIFI_VERSION_MIN="2020.0" -PYTHON_CERTIFI_VERSION_MAX="2021.0" +PYTHON_CERTIFI_VERSION="2021.10.8" +PYTHON_CERTIFI_VERSION_MIN="2021.0" +PYTHON_CERTIFI_VERSION_MEX="2023.0" PYTHON_CERTIFI_NAME="certifi" PYTHON_REQUESTS_VERSION="2.23.0" PYTHON_REQUESTS_VERSION_MIN="2.0" -PYTHON_REQUESTS_VERSION_MAX="3.0" +PYTHON_REQUESTS_VERSION_MEX="3.0" PYTHON_REQUESTS_NAME="requests" -PYTHON_NUMPY_VERSION="1.19.5" +PYTHON_ZSTANDARD_VERSION="0.15.2" +PYTHON_ZSTANDARD_VERSION_MIN="0.15.2" +PYTHON_ZSTANDARD_VERSION_MEX="0.16.0" +PYTHON_ZSTANDARD_NAME="zstandard" + +PYTHON_NUMPY_VERSION="1.21.2" PYTHON_NUMPY_VERSION_MIN="1.14" -PYTHON_NUMPY_VERSION_MAX="2.0" +PYTHON_NUMPY_VERSION_MEX="2.0" PYTHON_NUMPY_NAME="numpy" # As package-ready parameters (only used with distro packages). PYTHON_MODULES_PACKAGES=( - "$PYTHON_IDNA_NAME $PYTHON_IDNA_VERSION_MIN $PYTHON_IDNA_VERSION_MAX" - "$PYTHON_CHARDET_NAME $PYTHON_CHARDET_VERSION_MIN $PYTHON_CHARDET_VERSION_MAX" - "$PYTHON_URLLIB3_NAME $PYTHON_URLLIB3_VERSION_MIN $PYTHON_URLLIB3_VERSION_MAX" - "$PYTHON_CERTIFI_NAME $PYTHON_CERTIFI_VERSION_MIN $PYTHON_CERTIFI_VERSION_MAX" - "$PYTHON_REQUESTS_NAME $PYTHON_REQUESTS_VERSION_MIN $PYTHON_REQUESTS_VERSION_MAX" - "$PYTHON_NUMPY_NAME $PYTHON_NUMPY_VERSION_MIN $PYTHON_NUMPY_VERSION_MAX" + "$PYTHON_IDNA_NAME $PYTHON_IDNA_VERSION_MIN $PYTHON_IDNA_VERSION_MEX" + "$PYTHON_CHARSET_NORMALIZER_NAME $PYTHON_CHARSET_NORMALIZER_VERSION_MIN $PYTHON_CHARSET_NORMALIZER_VERSION_MEX" + "$PYTHON_URLLIB3_NAME $PYTHON_URLLIB3_VERSION_MIN $PYTHON_URLLIB3_VERSION_MEX" + "$PYTHON_CERTIFI_NAME $PYTHON_CERTIFI_VERSION_MIN $PYTHON_CERTIFI_VERSION_MEX" + "$PYTHON_REQUESTS_NAME $PYTHON_REQUESTS_VERSION_MIN $PYTHON_REQUESTS_VERSION_MEX" + "$PYTHON_ZSTANDARD_NAME $PYTHON_ZSTANDARD_VERSION_MIN $PYTHON_ZSTANDARD_VERSION_MEX" + "$PYTHON_NUMPY_NAME $PYTHON_NUMPY_VERSION_MIN $PYTHON_NUMPY_VERSION_MEX" ) # As pip-ready parameters (only used when building python). PYTHON_MODULES_PIP=( "$PYTHON_IDNA_NAME==$PYTHON_IDNA_VERSION" - "$PYTHON_CHARDET_NAME==$PYTHON_CHARDET_VERSION" + "$PYTHON_CHARSET_NORMALIZER_NAME==$PYTHON_CHARSET_NORMALIZER_VERSION" "$PYTHON_URLLIB3_NAME==$PYTHON_URLLIB3_VERSION" "$PYTHON_CERTIFI_NAME==$PYTHON_CERTIFI_VERSION" "$PYTHON_REQUESTS_NAME==$PYTHON_REQUESTS_VERSION" + "$PYTHON_ZSTANDARD_NAME==$PYTHON_ZSTANDARD_VERSION" "$PYTHON_NUMPY_NAME==$PYTHON_NUMPY_VERSION" ) @@ -443,7 +450,7 @@ PYTHON_MODULES_PIP=( BOOST_VERSION="1.73.0" BOOST_VERSION_SHORT="1.73" BOOST_VERSION_MIN="1.49" -BOOST_VERSION_MAX="2.0" +BOOST_VERSION_MEX="2.0" BOOST_FORCE_BUILD=false BOOST_FORCE_REBUILD=false BOOST_SKIP=false @@ -452,7 +459,7 @@ TBB_VERSION="2020" TBB_VERSION_SHORT="2020" TBB_VERSION_UPDATE="_U2" # Used for source packages... TBB_VERSION_MIN="2018" -TBB_VERSION_MAX="2022" +TBB_VERSION_MEX="2022" TBB_FORCE_BUILD=false TBB_FORCE_REBUILD=false TBB_SKIP=false @@ -460,7 +467,7 @@ TBB_SKIP=false OCIO_VERSION="2.0.0" OCIO_VERSION_SHORT="2.0" OCIO_VERSION_MIN="2.0" -OCIO_VERSION_MAX="3.0" +OCIO_VERSION_MEX="3.0" OCIO_FORCE_BUILD=false OCIO_FORCE_REBUILD=false OCIO_SKIP=false @@ -468,7 +475,7 @@ OCIO_SKIP=false OPENEXR_VERSION="2.5.5" OPENEXR_VERSION_SHORT="2.5" OPENEXR_VERSION_MIN="2.4" -OPENEXR_VERSION_MAX="3.0" +OPENEXR_VERSION_MEX="3.0" OPENEXR_FORCE_BUILD=false OPENEXR_FORCE_REBUILD=false OPENEXR_SKIP=false @@ -477,7 +484,7 @@ _with_built_openexr=false OIIO_VERSION="2.2.15.1" OIIO_VERSION_SHORT="2.2" OIIO_VERSION_MIN="2.1.12" -OIIO_VERSION_MAX="2.3.0" +OIIO_VERSION_MEX="2.3.0" OIIO_FORCE_BUILD=false OIIO_FORCE_REBUILD=false OIIO_SKIP=false @@ -485,7 +492,7 @@ OIIO_SKIP=false LLVM_VERSION="12.0.0" LLVM_VERSION_SHORT="12.0" LLVM_VERSION_MIN="11.0" -LLVM_VERSION_MAX="13.0" +LLVM_VERSION_MEX="13.0" LLVM_VERSION_FOUND="" LLVM_FORCE_BUILD=false LLVM_FORCE_REBUILD=false @@ -495,7 +502,7 @@ LLVM_SKIP=false OSL_VERSION="1.11.14.1" OSL_VERSION_SHORT="1.11" OSL_VERSION_MIN="1.11" -OSL_VERSION_MAX="2.0" +OSL_VERSION_MEX="2.0" OSL_FORCE_BUILD=false OSL_FORCE_REBUILD=false OSL_SKIP=false @@ -504,7 +511,7 @@ OSL_SKIP=false OSD_VERSION="3.4.3" OSD_VERSION_SHORT="3.4" OSD_VERSION_MIN="3.4" -OSD_VERSION_MAX="4.0" +OSD_VERSION_MEX="4.0" OSD_FORCE_BUILD=false OSD_FORCE_REBUILD=false OSD_SKIP=false @@ -515,7 +522,7 @@ OPENVDB_BLOSC_VERSION="1.5.0" OPENVDB_VERSION="8.0.1" OPENVDB_VERSION_SHORT="8.0" OPENVDB_VERSION_MIN="8.0" -OPENVDB_VERSION_MAX="8.1" +OPENVDB_VERSION_MEX="8.1" OPENVDB_FORCE_BUILD=false OPENVDB_FORCE_REBUILD=false OPENVDB_SKIP=false @@ -524,7 +531,7 @@ OPENVDB_SKIP=false ALEMBIC_VERSION="1.7.16" ALEMBIC_VERSION_SHORT="1.7" ALEMBIC_VERSION_MIN="1.7" -ALEMBIC_VERSION_MAX="2.0" +ALEMBIC_VERSION_MEX="2.0" ALEMBIC_FORCE_BUILD=false ALEMBIC_FORCE_REBUILD=false ALEMBIC_SKIP=false @@ -532,7 +539,7 @@ ALEMBIC_SKIP=false USD_VERSION="21.02" USD_VERSION_SHORT="21.02" USD_VERSION_MIN="20.05" -USD_VERSION_MAX="22.00" +USD_VERSION_MEX="22.00" USD_FORCE_BUILD=false USD_FORCE_REBUILD=false USD_SKIP=false @@ -540,7 +547,7 @@ USD_SKIP=false OPENCOLLADA_VERSION="1.6.68" OPENCOLLADA_VERSION_SHORT="1.6" OPENCOLLADA_VERSION_MIN="1.6.68" -OPENCOLLADA_VERSION_MAX="1.7" +OPENCOLLADA_VERSION_MEX="1.7" OPENCOLLADA_FORCE_BUILD=false OPENCOLLADA_FORCE_REBUILD=false OPENCOLLADA_SKIP=false @@ -548,7 +555,7 @@ OPENCOLLADA_SKIP=false EMBREE_VERSION="3.10.0" EMBREE_VERSION_SHORT="3.10" EMBREE_VERSION_MIN="3.10" -EMBREE_VERSION_MAX="4.0" +EMBREE_VERSION_MEX="4.0" EMBREE_FORCE_BUILD=false EMBREE_FORCE_REBUILD=false EMBREE_SKIP=false @@ -556,7 +563,7 @@ EMBREE_SKIP=false OIDN_VERSION="1.4.1" OIDN_VERSION_SHORT="1.4" OIDN_VERSION_MIN="1.4.0" -OIDN_VERSION_MAX="1.5" +OIDN_VERSION_MEX="1.5" OIDN_FORCE_BUILD=false OIDN_FORCE_REBUILD=false OIDN_SKIP=false @@ -566,7 +573,7 @@ ISPC_VERSION="1.16.0" FFMPEG_VERSION="4.4" FFMPEG_VERSION_SHORT="4.4" FFMPEG_VERSION_MIN="3.0" -FFMPEG_VERSION_MAX="5.0" +FFMPEG_VERSION_MEX="5.0" FFMPEG_FORCE_BUILD=false FFMPEG_FORCE_REBUILD=false FFMPEG_SKIP=false @@ -575,7 +582,7 @@ _ffmpeg_list_sep=";" XR_OPENXR_VERSION="1.0.17" XR_OPENXR_VERSION_SHORT="1.0" XR_OPENXR_VERSION_MIN="1.0.8" -XR_OPENXR_VERSION_MAX="2.0" +XR_OPENXR_VERSION_MEX="2.0" XR_OPENXR_FORCE_BUILD=false XR_OPENXR_FORCE_REBUILD=false XR_OPENXR_SKIP=false @@ -1141,10 +1148,11 @@ You may also want to build them yourself (optional ones are [between brackets]): * Python $PYTHON_VERSION (from $PYTHON_SOURCE). ** [IDNA $PYTHON_IDNA_VERSION] (use pip). - ** [Chardet $PYTHON_CHARDET_VERSION] (use pip). + ** [Charset Normalizer $PYTHON_CHARSET_NORMALIZER_VERSION] (use pip). ** [Urllib3 $PYTHON_URLLIB3_VERSION] (use pip). ** [Certifi $PYTHON_CERTIFI_VERSION] (use pip). ** [Requests $PYTHON_REQUESTS_VERSION] (use pip). + ** [ZStandard $PYTHON_ZSTANDARD_VERSION] (use pip). ** [NumPy $PYTHON_NUMPY_VERSION] (use pip). * Boost $BOOST_VERSION (from $BOOST_SOURCE, modules: $BOOST_BUILD_MODULES). * TBB $TBB_VERSION (from $TBB_SOURCE). @@ -2013,7 +2021,7 @@ compile_OIIO() { fi # To be changed each time we make edits that would modify the compiled result! - oiio_magic=17 + oiio_magic=18 _init_oiio # Force having own builds for the dependencies. @@ -2088,6 +2096,7 @@ compile_OIIO() { cmake_d="$cmake_d -D USE_PYTHON=OFF" cmake_d="$cmake_d -D USE_FFMPEG=OFF" cmake_d="$cmake_d -D USE_OPENCV=OFF" + cmake_d="$cmake_d -D USE_OPENVDB=OFF" cmake_d="$cmake_d -D BUILD_TESTING=OFF" cmake_d="$cmake_d -D OIIO_BUILD_TESTS=OFF" cmake_d="$cmake_d -D OIIO_BUILD_TOOLS=OFF" @@ -4027,7 +4036,7 @@ install_DEB() { INFO "Forced Python building, as requested..." _do_compile_python=true else - check_package_version_ge_lt_DEB python3-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MAX + check_package_version_ge_lt_DEB python3-dev $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then PYTHON_VERSION_INSTALLED=$(echo `get_package_version_DEB python3-dev` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') @@ -4040,8 +4049,8 @@ install_DEB() { module=($module) package="python3-${module[0]}" package_vmin=${module[1]} - package_vmax=${module[2]} - check_package_version_ge_lt_DEB "$package" $package_vmin $package_vmax + package_vmex=${module[2]} + check_package_version_ge_lt_DEB "$package" $package_vmin $package_vmex if [ $? -eq 0 ]; then install_packages_DEB "$package" else @@ -4067,7 +4076,7 @@ install_DEB() { INFO "Forced Boost building, as requested..." compile_Boost else - check_package_version_ge_lt_DEB libboost-dev $BOOST_VERSION_MIN $BOOST_VERSION_MAX + check_package_version_ge_lt_DEB libboost-dev $BOOST_VERSION_MIN $BOOST_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libboost-dev @@ -4088,7 +4097,7 @@ install_DEB() { INFO "Forced TBB building, as requested..." compile_TBB else - check_package_version_ge_lt_DEB libtbb-dev $TBB_VERSION_MIN $TBB_VERSION_MAX + check_package_version_ge_lt_DEB libtbb-dev $TBB_VERSION_MIN $TBB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libtbb-dev clean_TBB @@ -4105,7 +4114,7 @@ install_DEB() { INFO "Forced OpenColorIO building, as requested..." compile_OCIO else - check_package_version_ge_lt_DEB libopencolorio-dev $OCIO_VERSION_MIN $OCIO_VERSION_MAX + check_package_version_ge_lt_DEB libopencolorio-dev $OCIO_VERSION_MIN $OCIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopencolorio-dev clean_OCIO @@ -4122,7 +4131,7 @@ install_DEB() { INFO "Forced ILMBase/OpenEXR building, as requested..." compile_OPENEXR else - check_package_version_ge_lt_DEB libopenexr-dev $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MAX + check_package_version_ge_lt_DEB libopenexr-dev $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopenexr-dev OPENEXR_VERSION=`get_package_version_DEB libopenexr-dev` @@ -4143,7 +4152,7 @@ install_DEB() { INFO "Forced OpenImageIO building, as requested..." compile_OIIO else - check_package_version_ge_lt_DEB libopenimageio-dev $OIIO_VERSION_MIN $OIIO_VERSION_MAX + check_package_version_ge_lt_DEB libopenimageio-dev $OIIO_VERSION_MIN $OIIO_VERSION_MEX if [ $? -eq 0 -a "$_with_built_openexr" = false ]; then install_packages_DEB libopenimageio-dev openimageio-tools clean_OIIO @@ -4163,7 +4172,7 @@ install_DEB() { INFO "Forced LLVM building, as requested..." _do_compile_llvm=true else - check_package_version_ge_lt_DEB llvm-dev $LLVM_VERSION_MIN $LLVM_VERSION_MAX + check_package_version_ge_lt_DEB llvm-dev $LLVM_VERSION_MIN $LLVM_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB llvm-dev clang libclang-dev have_llvm=true @@ -4194,7 +4203,7 @@ install_DEB() { INFO "Forced OpenShadingLanguage building, as requested..." _do_compile_osl=true else - check_package_version_ge_lt_DEB libopenshadinglanguage-dev $OSL_VERSION_MIN $OSL_VERSION_MAX + check_package_version_ge_lt_DEB libopenshadinglanguage-dev $OSL_VERSION_MIN $OSL_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopenshadinglanguage-dev clean_OSL @@ -4232,7 +4241,7 @@ install_DEB() { INFO "Forced OpenVDB building, as requested..." compile_OPENVDB else - check_package_version_ge_lt_DEB libopenvdb-dev $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MAX + check_package_version_ge_lt_DEB libopenvdb-dev $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libopenvdb-dev libblosc-dev clean_OPENVDB @@ -4294,7 +4303,7 @@ install_DEB() { _do_compile_embree=true else # There is a package, but it does not provide everything that Blender needs... - #~ check_package_version_ge_lt_DEB libembree-dev $EMBREE_VERSION_MIN $EMBREE_VERSION_MAX + #~ check_package_version_ge_lt_DEB libembree-dev $EMBREE_VERSION_MIN $EMBREE_VERSION_MEX #~ if [ $? -eq 0 ]; then #~ install_packages_DEB libembree-dev #~ clean_Embree @@ -4336,7 +4345,7 @@ install_DEB() { # XXX Debian Testing / Ubuntu 16.04 finally includes FFmpeg, so check as usual check_package_DEB ffmpeg if [ $? -eq 0 ]; then - check_package_version_ge_lt_DEB ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MAX + check_package_version_ge_lt_DEB ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MEX if [ $? -eq 0 ]; then install_packages_DEB libavdevice-dev clean_FFmpeg @@ -4670,7 +4679,7 @@ install_RPM() { INFO "Forced Python building, as requested..." _do_compile_python=true else - check_package_version_ge_lt_RPM python3-devel $PYTHON_VERSION_MIN $PYTHON_VERSION_MAX + check_package_version_ge_lt_RPM python3-devel $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then PYTHON_VERSION_INSTALLED=$(echo `get_package_version_RPM python3-devel` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') @@ -4682,8 +4691,8 @@ install_RPM() { module=($module) package="python3-${module[0]}" package_vmin=${module[1]} - package_vmax=${module[2]} - check_package_version_ge_lt_RPM "$package" $package_vmin $package_vmax + package_vmex=${module[2]} + check_package_version_ge_lt_RPM "$package" $package_vmin $package_vmex if [ $? -eq 0 ]; then install_packages_RPM "$package" else @@ -4710,7 +4719,7 @@ install_RPM() { INFO "Forced Boost building, as requested..." _do_compile_boost=true else - check_package_version_ge_lt_RPM boost-devel $BOOST_VERSION_MIN $BOOST_VERSION_MAX + check_package_version_ge_lt_RPM boost-devel $BOOST_VERSION_MIN $BOOST_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM boost-devel clean_Boost @@ -4737,7 +4746,7 @@ install_RPM() { INFO "Forced TBB building, as requested..." compile_TBB else - check_package_version_ge_lt_RPM tbb-devel $TBB_VERSION_MIN $TBB_VERSION_MAX + check_package_version_ge_lt_RPM tbb-devel $TBB_VERSION_MIN $TBB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM tbb-devel clean_TBB @@ -4755,7 +4764,7 @@ install_RPM() { compile_OCIO else if [ "$RPM" = "SUSE" ]; then - check_package_version_ge_lt_RPM OpenColorIO-devel $OCIO_VERSION_MIN $OCIO_VERSION_MAX + check_package_version_ge_lt_RPM OpenColorIO-devel $OCIO_VERSION_MIN $OCIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM OpenColorIO-devel clean_OCIO @@ -4775,7 +4784,7 @@ install_RPM() { INFO "Forced ILMBase/OpenEXR building, as requested..." compile_OPENEXR else - check_package_version_ge_lt_RPM openexr-devel $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MAX + check_package_version_ge_lt_RPM openexr-devel $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM openexr-devel OPENEXR_VERSION=`get_package_version_RPM openexr-devel` @@ -4793,7 +4802,7 @@ install_RPM() { INFO "Forced OpenImageIO building, as requested..." compile_OIIO else - check_package_version_ge_lt_RPM OpenImageIO-devel $OIIO_VERSION_MIN $OIIO_VERSION_MAX + check_package_version_ge_lt_RPM OpenImageIO-devel $OIIO_VERSION_MIN $OIIO_VERSION_MEX if [ $? -eq 0 -a $_with_built_openexr == false ]; then install_packages_RPM OpenImageIO-devel OpenImageIO-utils clean_OIIO @@ -4818,7 +4827,7 @@ install_RPM() { else CLANG_DEV="clang-devel" fi - check_package_version_ge_lt_RPM llvm-devel $LLVM_VERSION_MIN $LLVM_VERSION_MAX + check_package_version_ge_lt_RPM llvm-devel $LLVM_VERSION_MIN $LLVM_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM llvm-devel $CLANG_DEV have_llvm=true @@ -4854,7 +4863,7 @@ install_RPM() { else OSL_DEV="openshadinglanguage-devel" fi - check_package_version_ge_lt_RPM $OSL_DEV $OSL_VERSION_MIN $OSL_VERSION_MAX + check_package_version_ge_lt_RPM $OSL_DEV $OSL_VERSION_MIN $OSL_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM $OSL_DEV clean_OSL @@ -4949,7 +4958,7 @@ install_RPM() { _do_compile_embree=true else # There is a package, but it does not provide everything that Blender needs... - #~ check_package_version_ge_lt_RPM embree-devel $EMBREE_VERSION_MIN $EMBREE_VERSION_MAX + #~ check_package_version_ge_lt_RPM embree-devel $EMBREE_VERSION_MIN $EMBREE_VERSION_MEX #~ if [ $? -eq 0 ]; then #~ install_packages_RPM embree-devel #~ clean_Embree @@ -4988,7 +4997,7 @@ install_RPM() { INFO "Forced FFMpeg building, as requested..." compile_FFmpeg else - check_package_version_ge_lt_RPM ffmpeg-devel $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MAX + check_package_version_ge_lt_RPM ffmpeg-devel $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MEX if [ $? -eq 0 ]; then install_packages_RPM ffmpeg ffmpeg-devel clean_FFmpeg @@ -5213,7 +5222,7 @@ install_ARCH() { INFO "Forced Python building, as requested..." _do_compile_python=true else - check_package_version_ge_lt_ARCH python $PYTHON_VERSION_MIN $PYTHON_VERSION_MAX + check_package_version_ge_lt_ARCH python $PYTHON_VERSION_MIN $PYTHON_VERSION_MEX if [ $? -eq 0 ]; then PYTHON_VERSION_INSTALLED=$(echo `get_package_version_ARCH python` | sed -r 's/^([0-9]+\.[0-9]+).*/\1/') @@ -5226,8 +5235,8 @@ install_ARCH() { module=($module) package="python-${module[0]}" package_vmin=${module[1]} - package_vmax=${module[2]} - check_package_version_ge_lt_ARCH "$package" $package_vmin $package_vmax + package_vmex=${module[2]} + check_package_version_ge_lt_ARCH "$package" $package_vmin $package_vmex if [ $? -eq 0 ]; then install_packages_ARCH "$package" else @@ -5253,7 +5262,7 @@ install_ARCH() { INFO "Forced Boost building, as requested..." compile_Boost else - check_package_version_ge_lt_ARCH boost $BOOST_VERSION_MIN $BOOST_VERSION_MAX + check_package_version_ge_lt_ARCH boost $BOOST_VERSION_MIN $BOOST_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH boost clean_Boost @@ -5270,7 +5279,7 @@ install_ARCH() { INFO "Forced TBB building, as requested..." compile_TBB else - check_package_version_ge_lt_ARCH intel-tbb $TBB_VERSION_MIN $TBB_VERSION_MAX + check_package_version_ge_lt_ARCH intel-tbb $TBB_VERSION_MIN $TBB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH intel-tbb clean_TBB @@ -5287,7 +5296,7 @@ install_ARCH() { INFO "Forced OpenColorIO building, as requested..." compile_OCIO else - check_package_version_ge_lt_ARCH opencolorio $OCIO_VERSION_MIN $OCIO_VERSION_MAX + check_package_version_ge_lt_ARCH opencolorio $OCIO_VERSION_MIN $OCIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH opencolorio clean_OCIO @@ -5304,7 +5313,7 @@ install_ARCH() { INFO "Forced ILMBase/OpenEXR building, as requested..." compile_OPENEXR else - check_package_version_ge_lt_ARCH openexr $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MAX + check_package_version_ge_lt_ARCH openexr $OPENEXR_VERSION_MIN $OPENEXR_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openexr OPENEXR_VERSION=`get_package_version_ARCH openexr` @@ -5323,7 +5332,7 @@ install_ARCH() { INFO "Forced OpenImageIO building, as requested..." compile_OIIO else - check_package_version_ge_lt_ARCH openimageio $OIIO_VERSION_MIN $OIIO_VERSION_MAX + check_package_version_ge_lt_ARCH openimageio $OIIO_VERSION_MIN $OIIO_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openimageio clean_OIIO @@ -5343,7 +5352,7 @@ install_ARCH() { INFO "Forced LLVM building, as requested..." _do_compile_llvm=true else - check_package_version_ge_lt_ARCH llvm $LLVM_VERSION_MIN $LLVM_VERSION_MAX + check_package_version_ge_lt_ARCH llvm $LLVM_VERSION_MIN $LLVM_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH llvm clang have_llvm=true @@ -5374,7 +5383,7 @@ install_ARCH() { INFO "Forced OpenShadingLanguage building, as requested..." _do_compile_osl=true else - check_package_version_ge_lt_ARCH openshadinglanguage $OSL_VERSION_MIN $OSL_VERSION_MAX + check_package_version_ge_lt_ARCH openshadinglanguage $OSL_VERSION_MIN $OSL_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openshadinglanguage clean_OSL @@ -5400,7 +5409,7 @@ install_ARCH() { INFO "Forced OpenSubdiv building, as requested..." compile_OSD else - check_package_version_ge_lt_ARCH opensubdiv $OSD_VERSION_MIN $OSD_VERSION_MAX + check_package_version_ge_lt_ARCH opensubdiv $OSD_VERSION_MIN $OSD_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH opensubdiv clean_OSD @@ -5417,7 +5426,7 @@ install_ARCH() { INFO "Forced OpenVDB building, as requested..." compile_OPENVDB else - check_package_version_ge_lt_ARCH openvdb $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MAX + check_package_version_ge_lt_ARCH openvdb $OPENVDB_VERSION_MIN $OPENVDB_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH openvdb clean_OPENVDB @@ -5483,7 +5492,7 @@ install_ARCH() { _do_compile_embree=true else # There is a package, but it does not provide everything that Blender needs... - #~ check_package_version_ge_lt_ARCH embree $EMBREE_VERSION_MIN $EMBREE_VERSION_MAX + #~ check_package_version_ge_lt_ARCH embree $EMBREE_VERSION_MIN $EMBREE_VERSION_MEX #~ if [ $? -eq 0 ]; then #~ install_packages_ARCH embree #~ clean_Embree @@ -5522,7 +5531,7 @@ install_ARCH() { INFO "Forced FFMpeg building, as requested..." compile_FFmpeg else - check_package_version_ge_lt_ARCH ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MAX + check_package_version_ge_lt_ARCH ffmpeg $FFMPEG_VERSION_MIN $FFMPEG_VERSION_MEX if [ $? -eq 0 ]; then install_packages_ARCH ffmpeg clean_FFmpeg diff --git a/build_files/build_environment/patches/ffmpeg.diff b/build_files/build_environment/patches/ffmpeg.diff index 5a50a3f8756..c255f7c37a3 100644 --- a/build_files/build_environment/patches/ffmpeg.diff +++ b/build_files/build_environment/patches/ffmpeg.diff @@ -70,16 +70,18 @@ } --- a/libavcodec/rl.c +++ b/libavcodec/rl.c -@@ -71,7 +71,7 @@ av_cold void ff_rl_init(RLTable *rl, +@@ -71,17 +71,19 @@ av_cold void ff_rl_init_vlc(RLTable *rl, unsigned static_size) { int i, q; - VLC_TYPE table[1500][2] = {{0}}; + VLC_TYPE (*table)[2] = av_calloc(sizeof(VLC_TYPE), 1500 * 2); VLC vlc = { .table = table, .table_allocated = static_size }; - av_assert0(static_size <= FF_ARRAY_ELEMS(table)); +- av_assert0(static_size <= FF_ARRAY_ELEMS(table)); ++ av_assert0(static_size < 1500); init_vlc(&vlc, 9, rl->n + 1, &rl->table_vlc[0][1], 4, 2, &rl->table_vlc[0][0], 4, 2, INIT_VLC_USE_NEW_STATIC); -@@ -80,8 +80,10 @@ av_cold void ff_rl_init_vlc(RLTable *rl, unsigned static_size) + + for (q = 0; q < 32; q++) { int qmul = q * 2; int qadd = (q - 1) | 1; @@ -91,7 +93,7 @@ if (q == 0) { qmul = 1; -@@ -113,4 +115,5 @@ av_cold void ff_rl_init_vlc(RLTable *rl, unsigned static_size) +@@ -113,4 +115,5 @@ rl->rl_vlc[q][i].run = run; } } diff --git a/build_files/build_environment/patches/numpy.diff b/build_files/build_environment/patches/numpy.diff deleted file mode 100644 index a9b783dd856..00000000000 --- a/build_files/build_environment/patches/numpy.diff +++ /dev/null @@ -1,27 +0,0 @@ -diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py -index ba2b1f4..b10f7df 100644 ---- a/numpy/distutils/system_info.py -+++ b/numpy/distutils/system_info.py -@@ -2164,8 +2164,8 @@ class accelerate_info(system_info): - 'accelerate' in libraries): - if intel: - args.extend(['-msse3']) -- else: -- args.extend(['-faltivec']) -+# else: -+# args.extend(['-faltivec']) - args.extend([ - '-I/System/Library/Frameworks/vecLib.framework/Headers']) - link_args.extend(['-Wl,-framework', '-Wl,Accelerate']) -@@ -2174,8 +2174,8 @@ class accelerate_info(system_info): - 'veclib' in libraries): - if intel: - args.extend(['-msse3']) -- else: -- args.extend(['-faltivec']) -+# else: -+# args.extend(['-faltivec']) - args.extend([ - '-I/System/Library/Frameworks/vecLib.framework/Headers']) - link_args.extend(['-Wl,-framework', '-Wl,vecLib']) - diff --git a/build_files/build_environment/windows/build_deps.cmd b/build_files/build_environment/windows/build_deps.cmd index 2159055f3bd..5174af8e20d 100644 --- a/build_files/build_environment/windows/build_deps.cmd +++ b/build_files/build_environment/windows/build_deps.cmd @@ -79,6 +79,9 @@ set STAGING=%BUILD_DIR%\S rem for python module build set MSSdk=1 set DISTUTILS_USE_SDK=1 +rem if you let pip pick its own build dirs, it'll stick it somewhere deep inside the user profile +rem and cython will refuse to link due to a path that gets too long. +set TMPDIR=c:\t\ rem for python externals source to be shared between the various archs and compilers mkdir %BUILD_DIR%\downloads\externals diff --git a/build_files/cmake/Modules/FindHIP.cmake b/build_files/cmake/Modules/FindHIP.cmake new file mode 100644 index 00000000000..c331a19eb33 --- /dev/null +++ b/build_files/cmake/Modules/FindHIP.cmake @@ -0,0 +1,81 @@ +# - Find HIP compiler +# +# This module defines +# HIP_HIPCC_EXECUTABLE, the full path to the hipcc executable +# HIP_VERSION, the HIP compiler version +# +# HIP_FOUND, if the HIP toolkit is found. + +#============================================================================= +# Copyright 2021 Blender Foundation. +# +# Distributed under the OSI-approved BSD 3-Clause License, +# see accompanying file BSD-3-Clause-license.txt for details. +#============================================================================= + +# If HIP_ROOT_DIR was defined in the environment, use it. +if(NOT HIP_ROOT_DIR AND NOT $ENV{HIP_ROOT_DIR} STREQUAL "") + set(HIP_ROOT_DIR $ENV{HIP_ROOT_DIR}) +endif() + +set(_hip_SEARCH_DIRS + ${HIP_ROOT_DIR} +) + +find_program(HIP_HIPCC_EXECUTABLE + NAMES + hipcc + HINTS + ${_hip_SEARCH_DIRS} + PATH_SUFFIXES + bin +) + +if(HIP_HIPCC_EXECUTABLE AND NOT EXISTS ${HIP_HIPCC_EXECUTABLE}) + message(WARNING "Cached or directly specified hipcc executable does not exist.") + set(HIP_FOUND FALSE) +elseif(HIP_HIPCC_EXECUTABLE) + set(HIP_FOUND TRUE) + + set(HIP_VERSION_MAJOR 0) + set(HIP_VERSION_MINOR 0) + set(HIP_VERSION_PATCH 0) + + # Get version from the output. + execute_process(COMMAND ${HIP_HIPCC_EXECUTABLE} --version + OUTPUT_VARIABLE HIP_VERSION_RAW + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + # Parse parts. + if(HIP_VERSION_RAW MATCHES "HIP version: .*") + # Strip the HIP prefix and get list of individual version components. + string(REGEX REPLACE + ".*HIP version: ([.0-9]+).*" "\\1" + HIP_SEMANTIC_VERSION "${HIP_VERSION_RAW}") + string(REPLACE "." ";" HIP_VERSION_PARTS "${HIP_SEMANTIC_VERSION}") + list(LENGTH HIP_VERSION_PARTS NUM_HIP_VERSION_PARTS) + + # Extract components into corresponding variables. + if(NUM_HIP_VERSION_PARTS GREATER 0) + list(GET HIP_VERSION_PARTS 0 HIP_VERSION_MAJOR) + endif() + if(NUM_HIP_VERSION_PARTS GREATER 1) + list(GET HIP_VERSION_PARTS 1 HIP_VERSION_MINOR) + endif() + if(NUM_HIP_VERSION_PARTS GREATER 2) + list(GET HIP_VERSION_PARTS 2 HIP_VERSION_PATCH) + endif() + + # Unset temp variables. + unset(NUM_HIP_VERSION_PARTS) + unset(HIP_SEMANTIC_VERSION) + unset(HIP_VERSION_PARTS) + endif() + + # Construct full semantic version. + set(HIP_VERSION "${HIP_VERSION_MAJOR}.${HIP_VERSION_MINOR}.${HIP_VERSION_PATCH}") + unset(HIP_VERSION_RAW) +else() + set(HIP_FOUND FALSE) +endif() diff --git a/build_files/cmake/Modules/FindOptiX.cmake b/build_files/cmake/Modules/FindOptiX.cmake index cfcdd9cd23b..67106740f57 100644 --- a/build_files/cmake/Modules/FindOptiX.cmake +++ b/build_files/cmake/Modules/FindOptiX.cmake @@ -33,11 +33,23 @@ FIND_PATH(OPTIX_INCLUDE_DIR include ) +IF(EXISTS "${OPTIX_INCLUDE_DIR}/optix.h") + FILE(STRINGS "${OPTIX_INCLUDE_DIR}/optix.h" _optix_version REGEX "^#define OPTIX_VERSION[ \t].*$") + STRING(REGEX MATCHALL "[0-9]+" _optix_version ${_optix_version}) + + MATH(EXPR _optix_version_major "${_optix_version} / 10000") + MATH(EXPR _optix_version_minor "(${_optix_version} % 10000) / 100") + MATH(EXPR _optix_version_patch "${_optix_version} % 100") + + SET(OPTIX_VERSION "${_optix_version_major}.${_optix_version_minor}.${_optix_version_patch}") +ENDIF() + # handle the QUIETLY and REQUIRED arguments and set OPTIX_FOUND to TRUE if # all listed variables are TRUE INCLUDE(FindPackageHandleStandardArgs) -FIND_PACKAGE_HANDLE_STANDARD_ARGS(OptiX DEFAULT_MSG - OPTIX_INCLUDE_DIR) +FIND_PACKAGE_HANDLE_STANDARD_ARGS(OptiX + REQUIRED_VARS OPTIX_INCLUDE_DIR + VERSION_VAR OPTIX_VERSION) IF(OPTIX_FOUND) SET(OPTIX_INCLUDE_DIRS ${OPTIX_INCLUDE_DIR}) @@ -45,6 +57,7 @@ ENDIF() MARK_AS_ADVANCED( OPTIX_INCLUDE_DIR + OPTIX_VERSION ) UNSET(_optix_SEARCH_DIRS) diff --git a/build_files/cmake/cmake_static_check_cppcheck.py b/build_files/cmake/cmake_static_check_cppcheck.py index 1eef2efe2b5..0e37b9ba468 100644 --- a/build_files/cmake/cmake_static_check_cppcheck.py +++ b/build_files/cmake/cmake_static_check_cppcheck.py @@ -24,6 +24,7 @@ import project_source_info import subprocess import sys import os +import tempfile from typing import ( Any, @@ -35,7 +36,6 @@ USE_QUIET = (os.environ.get("QUIET", None) is not None) CHECKER_IGNORE_PREFIX = [ "extern", - "intern/moto", ] CHECKER_BIN = "cppcheck" @@ -47,13 +47,19 @@ CHECKER_ARGS = [ "--max-configs=1", # speeds up execution # "--check-config", # when includes are missing "--enable=all", # if you want sixty hundred pedantic suggestions + + # Quiet output, otherwise all defines/includes are printed (overly verbose). + # Only enable this for troubleshooting (if defines are not set as expected for example). + "--quiet", + + # NOTE: `--cppcheck-build-dir=` is added later as a temporary directory. ] if USE_QUIET: CHECKER_ARGS.append("--quiet") -def main() -> None: +def cppcheck() -> None: source_info = project_source_info.build_info(ignore_prefix_list=CHECKER_IGNORE_PREFIX) source_defines = project_source_info.build_defines_as_args() @@ -78,7 +84,10 @@ def main() -> None: percent_str = "[" + ("%.2f]" % percent).rjust(7) + " %:" sys.stdout.flush() - sys.stdout.write("%s " % percent_str) + sys.stdout.write("%s %s\n" % ( + percent_str, + os.path.relpath(c, project_source_info.SOURCE_DIR) + )) return subprocess.Popen(cmd) @@ -90,5 +99,11 @@ def main() -> None: print("Finished!") +def main() -> None: + with tempfile.TemporaryDirectory() as temp_dir: + CHECKER_ARGS.append("--cppcheck-build-dir=" + temp_dir) + cppcheck() + + if __name__ == "__main__": main() diff --git a/build_files/cmake/config/blender_lite.cmake b/build_files/cmake/config/blender_lite.cmake index 0cd886e67d7..89bd46ecd7d 100644 --- a/build_files/cmake/config/blender_lite.cmake +++ b/build_files/cmake/config/blender_lite.cmake @@ -9,6 +9,7 @@ set(WITH_INSTALL_PORTABLE ON CACHE BOOL "" FORCE) set(WITH_ALEMBIC OFF CACHE BOOL "" FORCE) set(WITH_AUDASPACE OFF CACHE BOOL "" FORCE) +set(WITH_BLENDER_THUMBNAILER OFF CACHE BOOL "" FORCE) set(WITH_BOOST OFF CACHE BOOL "" FORCE) set(WITH_BUILDINFO OFF CACHE BOOL "" FORCE) set(WITH_BULLET OFF CACHE BOOL "" FORCE) diff --git a/build_files/cmake/project_source_info.py b/build_files/cmake/project_source_info.py index d2ed80022ca..c2ba7e5b11c 100644 --- a/build_files/cmake/project_source_info.py +++ b/build_files/cmake/project_source_info.py @@ -243,7 +243,9 @@ def build_defines_as_args() -> List[str]: # use this module. def queue_processes( process_funcs: Sequence[Tuple[Callable[..., subprocess.Popen[Any]], Tuple[Any, ...]]], + *, job_total: int =-1, + sleep: float = 0.1, ) -> None: """ Takes a list of function arg pairs, each function must return a process """ @@ -271,14 +273,20 @@ def queue_processes( if len(processes) <= job_total: break - else: - time.sleep(0.1) + time.sleep(sleep) sys.stdout.flush() sys.stderr.flush() processes.append(func(*args)) + # Don't return until all jobs have finished. + while 1: + processes[:] = [p for p in processes if p.poll() is None] + if not processes: + break + time.sleep(sleep) + def main() -> None: if not os.path.exists(join(CMAKE_DIR, "CMakeCache.txt")): diff --git a/build_files/config/pipeline_config.yaml b/build_files/config/pipeline_config.yaml index 5d1a24a30f1..8222f2ff0b9 100644 --- a/build_files/config/pipeline_config.yaml +++ b/build_files/config/pipeline_config.yaml @@ -55,7 +55,7 @@ buildbot: cuda11: version: '11.4.1' optix: - version: '7.1.0' + version: '7.3.0' cmake: default: version: any diff --git a/build_files/utils/make_update.py b/build_files/utils/make_update.py index a6399790bc9..30ef090efbb 100755 --- a/build_files/utils/make_update.py +++ b/build_files/utils/make_update.py @@ -200,15 +200,20 @@ def submodules_update(args, release_version, branch): if msg: skip_msg += submodule_path + " skipped: " + msg + "\n" else: - if make_utils.git_branch(args.git_command) != submodule_branch: - call([args.git_command, "fetch", "origin"]) - call([args.git_command, "checkout", submodule_branch]) - call([args.git_command, "pull", "--rebase", "origin", submodule_branch]) - # If we cannot find the specified branch for this submodule, fallback to default one (aka master). - if make_utils.git_branch(args.git_command) != submodule_branch: - call([args.git_command, "fetch", "origin"]) - call([args.git_command, "checkout", submodule_branch_fallback]) - call([args.git_command, "pull", "--rebase", "origin", submodule_branch_fallback]) + # Find a matching branch that exists. + call([args.git_command, "fetch", "origin"]) + if make_utils.git_branch_exists(args.git_command, submodule_branch): + pass + elif make_utils.git_branch_exists(args.git_command, submodule_branch_fallback): + submodule_branch = submodule_branch_fallback + else: + submodule_branch = None + + # Switch to branch and pull. + if submodule_branch: + if make_utils.git_branch(args.git_command) != submodule_branch: + call([args.git_command, "checkout", submodule_branch]) + call([args.git_command, "pull", "--rebase", "origin", submodule_branch]) finally: os.chdir(cwd) diff --git a/build_files/utils/make_utils.py b/build_files/utils/make_utils.py index db352ff7e16..9def0059ceb 100755 --- a/build_files/utils/make_utils.py +++ b/build_files/utils/make_utils.py @@ -8,14 +8,19 @@ import subprocess import sys -def call(cmd, exit_on_error=True): - print(" ".join(cmd)) +def call(cmd, exit_on_error=True, silent=False): + if not silent: + print(" ".join(cmd)) # Flush to ensure correct order output on Windows. sys.stdout.flush() sys.stderr.flush() - retcode = subprocess.call(cmd) + if silent: + retcode = subprocess.call(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + else: + retcode = subprocess.call(cmd) + if exit_on_error and retcode != 0: sys.exit(retcode) return retcode @@ -38,6 +43,11 @@ def check_output(cmd, exit_on_error=True): return output.strip() +def git_branch_exists(git_command, branch): + return call([git_command, "rev-parse", "--verify", branch], exit_on_error=False, silent=True) == 0 or \ + call([git_command, "rev-parse", "--verify", "remotes/origin/" + branch], exit_on_error=False, silent=True) == 0 + + def git_branch(git_command): # Get current branch name. try: diff --git a/build_files/windows/find_dependencies.cmd b/build_files/windows/find_dependencies.cmd index 0b6ae2d3c32..9fa3b156a4f 100644 --- a/build_files/windows/find_dependencies.cmd +++ b/build_files/windows/find_dependencies.cmd @@ -3,7 +3,7 @@ for %%X in (svn.exe) do (set SVN=%%~$PATH:X) for %%X in (cmake.exe) do (set CMAKE=%%~$PATH:X) for %%X in (ctest.exe) do (set CTEST=%%~$PATH:X) for %%X in (git.exe) do (set GIT=%%~$PATH:X) -set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\37\bin\python.exe +set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe if NOT "%verbose%" == "" ( echo svn : "%SVN%" echo cmake : "%CMAKE%" diff --git a/build_files/windows/format.cmd b/build_files/windows/format.cmd index d19595bf042..d5003c9f8d8 100644 --- a/build_files/windows/format.cmd +++ b/build_files/windows/format.cmd @@ -10,7 +10,7 @@ exit /b 1 echo found clang-format in %CF_PATH% if EXIST %PYTHON% ( - set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\37\bin\python.exe + set PYTHON=%BLENDER_DIR%\..\lib\win64_vc15\python\39\bin\python.exe goto detect_python_done ) diff --git a/build_files/windows/parse_arguments.cmd b/build_files/windows/parse_arguments.cmd index c63f062dfef..dcef46c2c9a 100644 --- a/build_files/windows/parse_arguments.cmd +++ b/build_files/windows/parse_arguments.cmd @@ -116,6 +116,9 @@ if NOT "%1" == "" ( ) else if "%1" == "doc_py" ( set DOC_PY=1 goto EOF + ) else if "%1" == "svnfix" ( + set SVN_FIX=1 + goto EOF ) else ( echo Command "%1" unknown, aborting! goto ERR diff --git a/build_files/windows/svn_fix.cmd b/build_files/windows/svn_fix.cmd new file mode 100644 index 00000000000..a9dcdf36847 --- /dev/null +++ b/build_files/windows/svn_fix.cmd @@ -0,0 +1,26 @@ +if "%BUILD_VS_YEAR%"=="2017" set BUILD_VS_LIBDIRPOST=vc15 +if "%BUILD_VS_YEAR%"=="2019" set BUILD_VS_LIBDIRPOST=vc15 +if "%BUILD_VS_YEAR%"=="2022" set BUILD_VS_LIBDIRPOST=vc15 + +set BUILD_VS_SVNDIR=win64_%BUILD_VS_LIBDIRPOST% +set BUILD_VS_LIBDIR="%BLENDER_DIR%..\lib\%BUILD_VS_SVNDIR%" + +echo Starting cleanup in %BUILD_VS_LIBDIR%. +cd %BUILD_VS_LIBDIR% +:RETRY +"%SVN%" cleanup +"%SVN%" update +if errorlevel 1 ( + set /p LibRetry= "Error during update, retry? y/n" + if /I "!LibRetry!"=="Y" ( + goto RETRY + ) + echo. + echo Error: Download of external libraries failed. + echo This is needed for building, please manually run 'svn cleanup' and 'svn update' in + echo %BUILD_VS_LIBDIR% , until this is resolved you CANNOT make a successful blender build + echo. + exit /b 1 +) +echo Cleanup complete + diff --git a/doc/doxygen/Doxyfile b/doc/doxygen/Doxyfile index 96eb30a852e..89954d8a155 100644 --- a/doc/doxygen/Doxyfile +++ b/doc/doxygen/Doxyfile @@ -38,7 +38,7 @@ PROJECT_NAME = Blender # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = V3.0 +PROJECT_NUMBER = V3.1 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/doc/python_api/examples/aud.py b/doc/python_api/examples/aud.py index a52258c1a45..0eb27647671 100644 --- a/doc/python_api/examples/aud.py +++ b/doc/python_api/examples/aud.py @@ -14,7 +14,7 @@ sound = aud.Sound('music.ogg') # play the audio, this return a handle to control play/pause handle = device.play(sound) # if the audio is not too big and will be used often you can buffer it -sound_buffered = aud.Sound.buffer(sound) +sound_buffered = aud.Sound.cache(sound) handle_buffered = device.play(sound_buffered) # stop the sounds (otherwise they play until their ends) diff --git a/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py b/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py new file mode 100644 index 00000000000..f3cc95dec61 --- /dev/null +++ b/doc/python_api/examples/bpy.types.Bone.convert_local_to_pose.py @@ -0,0 +1,40 @@ +""" +This method enables conversions between Local and Pose space for bones in +the middle of updating the armature without having to update dependencies +after each change, by manually carrying updated matrices in a recursive walk. +""" + +def set_pose_matrices(obj, matrix_map): + "Assign pose space matrices of all bones at once, ignoring constraints." + + def rec(pbone, parent_matrix): + matrix = matrix_map[pbone.name] + + ## Instead of: + # pbone.matrix = matrix + # bpy.context.view_layer.update() + + # Compute and assign local matrix, using the new parent matrix + if pbone.parent: + pbone.matrix_basis = pbone.bone.convert_local_to_pose( + matrix, + pbone.bone.matrix_local, + parent_matrix=parent_matrix, + parent_matrix_local=pbone.parent.bone.matrix_local, + invert=True + ) + else: + pbone.matrix_basis = pbone.bone.convert_local_to_pose( + matrix, + pbone.bone.matrix_local, + invert=True + ) + + # Recursively process children, passing the new matrix through + for child in pbone.children: + rec(child, matrix) + + # Scan all bone trees from their roots + for pbone in obj.pose.bones: + if not pbone.parent: + rec(pbone, None) diff --git a/doc/python_api/examples/gpu.9.py b/doc/python_api/examples/gpu.9.py index b0400ce7809..318c0a74ceb 100644 --- a/doc/python_api/examples/gpu.9.py +++ b/doc/python_api/examples/gpu.9.py @@ -32,7 +32,7 @@ def draw(): context.region, view_matrix, projection_matrix, - True) + do_color_management=True) gpu.state.depth_mask_set(False) draw_texture_2d(offscreen.texture_color, (10, 10), WIDTH, HEIGHT) diff --git a/doc/python_api/sphinx_doc_gen.py b/doc/python_api/sphinx_doc_gen.py index aa0f79646e6..04efe49f778 100644 --- a/doc/python_api/sphinx_doc_gen.py +++ b/doc/python_api/sphinx_doc_gen.py @@ -1101,14 +1101,18 @@ context_type_map = { "scene": ("Scene", False), "sculpt_object": ("Object", False), "selectable_objects": ("Object", True), + "selected_asset_files": ("FileSelectEntry", True), "selected_bones": ("EditBone", True), "selected_editable_bones": ("EditBone", True), "selected_editable_fcurves": ("FCurve", True), "selected_editable_keyframes": ("Keyframe", True), "selected_editable_objects": ("Object", True), "selected_editable_sequences": ("Sequence", True), + "selected_ids": ("ID", True), "selected_files": ("FileSelectEntry", True), + "selected_ids": ("ID", True), "selected_nla_strips": ("NlaStrip", True), + "selected_movieclip_tracks": ("MovieTrackingTrack", True), "selected_nodes": ("Node", True), "selected_objects": ("Object", True), "selected_pose_bones": ("PoseBone", True), @@ -1220,7 +1224,10 @@ def pycontext2sphinx(basepath): while char_array[i] is not None: member = ctypes.string_at(char_array[i]).decode(encoding="ascii") fw(".. data:: %s\n\n" % member) - member_type, is_seq = context_type_map[member] + try: + member_type, is_seq = context_type_map[member] + except KeyError: + raise SystemExit("Error: context key %r not found in context_type_map; update %s" % (member, __file__)) from None fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type)) unique.add(member) i += 1 diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 7f7d91f0765..1fdc8e60167 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -67,9 +67,12 @@ endif() if(WITH_CYCLES OR WITH_COMPOSITOR OR WITH_OPENSUBDIV) add_subdirectory(clew) - if(WITH_CUDA_DYNLOAD) + if((WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX) AND WITH_CUDA_DYNLOAD) add_subdirectory(cuew) endif() + if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) + add_subdirectory(hipew) + endif() endif() if(WITH_GHOST_X11 AND WITH_GHOST_XDND) diff --git a/extern/audaspace/CMakeLists.txt b/extern/audaspace/CMakeLists.txt index 1599c03cbad..8493fe3e67d 100644 --- a/extern/audaspace/CMakeLists.txt +++ b/extern/audaspace/CMakeLists.txt @@ -129,6 +129,7 @@ set(SRC src/util/Barrier.cpp src/util/Buffer.cpp src/util/BufferReader.cpp + src/util/RingBuffer.cpp src/util/StreamBuffer.cpp src/util/ThreadPool.cpp ) @@ -152,6 +153,7 @@ set(PUBLIC_HDR include/devices/ThreadedDevice.h include/Exception.h include/file/File.h + include/file/FileInfo.h include/file/FileManager.h include/file/FileWriter.h include/file/IFileInput.h @@ -244,6 +246,7 @@ set(PUBLIC_HDR include/util/BufferReader.h include/util/ILockable.h include/util/Math3D.h + include/util/RingBuffer.h include/util/StreamBuffer.h include/util/ThreadPool.h ) @@ -960,7 +963,10 @@ endif() if(BUILD_DEMOS) include_directories(${INCLUDE}) - set(DEMOS audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager) + set(DEMOS audainfo audaplay audaconvert audaremap signalgen randsounds dynamicmusic playbackmanager) + + add_executable(audainfo demos/audainfo.cpp) + target_link_libraries(audainfo audaspace) add_executable(audaplay demos/audaplay.cpp) target_link_libraries(audaplay audaspace) diff --git a/extern/audaspace/bindings/C/AUD_PlaybackManager.h b/extern/audaspace/bindings/C/AUD_PlaybackManager.h index 0fa8171599d..a2f5134602a 100644 --- a/extern/audaspace/bindings/C/AUD_PlaybackManager.h +++ b/extern/audaspace/bindings/C/AUD_PlaybackManager.h @@ -39,7 +39,7 @@ extern AUD_API void AUD_PlaybackManager_free(AUD_PlaybackManager* manager); * Plays a sound through the playback manager, adding it into a category. * \param manager The PlaybackManager object. * \param sound The sound to be played. -* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be creatd. +* \param catKey The key of the category into which the sound will be added. If it doesn't exist a new one will be created. */ extern AUD_API void AUD_PlaybackManager_play(AUD_PlaybackManager* manager, AUD_Sound* sound, unsigned int catKey); diff --git a/extern/audaspace/bindings/C/AUD_Sound.cpp b/extern/audaspace/bindings/C/AUD_Sound.cpp index 8c99ce2341f..8a3c9d1bbc9 100644 --- a/extern/audaspace/bindings/C/AUD_Sound.cpp +++ b/extern/audaspace/bindings/C/AUD_Sound.cpp @@ -94,6 +94,40 @@ AUD_API int AUD_Sound_getLength(AUD_Sound* sound) return (*sound)->createReader()->getLength(); } +AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo **stream_infos) +{ + assert(sound); + + std::shared_ptr file = std::dynamic_pointer_cast(*sound); + + if(file) + { + try + { + auto streams = file->queryStreams(); + + size_t size = sizeof(AUD_StreamInfo) * streams.size(); + + if(!size) + { + *stream_infos = nullptr; + return 0; + } + + *stream_infos = reinterpret_cast(std::malloc(size)); + std::memcpy(*stream_infos, streams.data(), size); + + return streams.size(); + } + catch(Exception&) + { + } + } + + *stream_infos = nullptr; + return 0; +} + AUD_API sample_t* AUD_Sound_data(AUD_Sound* sound, int* length, AUD_Specs* specs) { assert(sound); @@ -252,6 +286,12 @@ AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size) return new AUD_Sound(new File(buffer, size)); } +AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream) +{ + assert(buffer); + return new AUD_Sound(new File(buffer, size, stream)); +} + AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound) { assert(sound); @@ -272,6 +312,12 @@ AUD_API AUD_Sound* AUD_Sound_file(const char* filename) return new AUD_Sound(new File(filename)); } +AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream) +{ + assert(filename); + return new AUD_Sound(new File(filename, stream)); +} + AUD_API AUD_Sound* AUD_Sound_sawtooth(float frequency, AUD_SampleRate rate) { return new AUD_Sound(new Sawtooth(frequency, rate)); diff --git a/extern/audaspace/bindings/C/AUD_Sound.h b/extern/audaspace/bindings/C/AUD_Sound.h index 53172616781..fc73a31e15c 100644 --- a/extern/audaspace/bindings/C/AUD_Sound.h +++ b/extern/audaspace/bindings/C/AUD_Sound.h @@ -36,7 +36,15 @@ extern AUD_API AUD_Specs AUD_Sound_getSpecs(AUD_Sound* sound); * \return The length of the sound in samples. * \note This function creates a reader from the sound and deletes it again. */ -extern AUD_API int AUD_getLength(AUD_Sound* sound); +extern AUD_API int AUD_Sound_getLength(AUD_Sound* sound); + +/** + * Retrieves the stream infos of a sound file. + * \param sound The sound to retrieve from which must be a file sound. + * \param infos A pointer to a AUD_StreamInfo array that will be allocated and must afterwards be freed by the caller. + * \return The number of items in the infos array. + */ +extern AUD_API int AUD_Sound_getFileStreams(AUD_Sound* sound, AUD_StreamInfo** stream_infos); /** * Reads a sound's samples into memory. @@ -89,6 +97,15 @@ extern AUD_API AUD_Sound* AUD_Sound_buffer(sample_t* data, int length, AUD_Specs */ extern AUD_API AUD_Sound* AUD_Sound_bufferFile(unsigned char* buffer, int size); +/** + * Loads a sound file from a memory buffer. + * \param buffer The buffer which contains the sound file. + * \param size The size of the buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. + * \return A handle of the sound file. + */ +extern AUD_API AUD_Sound* AUD_Sound_bufferFileStream(unsigned char* buffer, int size, int stream); + /** * Caches a sound into a memory buffer. * \param sound The sound to cache. @@ -103,6 +120,14 @@ extern AUD_API AUD_Sound* AUD_Sound_cache(AUD_Sound* sound); */ extern AUD_API AUD_Sound* AUD_Sound_file(const char* filename); +/** + * Loads a sound file. + * \param filename The filename of the sound file. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. + * \return A handle of the sound file. + */ +extern AUD_API AUD_Sound* AUD_Sound_fileStream(const char* filename, int stream); + /** * Creates a sawtooth sound. * \param frequency The frequency of the generated sawtooth sound. diff --git a/extern/audaspace/bindings/C/AUD_Special.cpp b/extern/audaspace/bindings/C/AUD_Special.cpp index 5cc33525d1d..1ce25dcd41c 100644 --- a/extern/audaspace/bindings/C/AUD_Special.cpp +++ b/extern/audaspace/bindings/C/AUD_Special.cpp @@ -86,7 +86,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) info.specs.channels = AUD_CHANNELS_INVALID; info.specs.rate = AUD_RATE_INVALID; info.length = 0.0f; - info.start_offset = 0.0f; try { @@ -96,7 +95,6 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) { info.specs = convSpecToC(reader->getSpecs()); info.length = reader->getLength() / (float) info.specs.rate; - info.start_offset = reader->getStartOffset(); } } catch(Exception&) @@ -109,7 +107,7 @@ AUD_API AUD_SoundInfo AUD_getInfo(AUD_Sound* sound) AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high, float attack, float release, float threshold, int accumulate, int additive, int square, - float sthreshold, double samplerate, int* length) + float sthreshold, double samplerate, int* length, int stream) { Buffer buffer; DeviceSpecs specs; @@ -117,7 +115,7 @@ AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float high, specs.rate = (SampleRate)samplerate; std::shared_ptr sound; - std::shared_ptr file = std::shared_ptr(new File(filename)); + std::shared_ptr file = std::shared_ptr(new File(filename, stream)); int position = 0; @@ -247,7 +245,7 @@ AUD_API int AUD_readSound(AUD_Sound* sound, float* buffer, int length, int sampl buffer[i * 3] = min; buffer[i * 3 + 1] = max; - buffer[i * 3 + 2] = sqrt(power / len); // RMS + buffer[i * 3 + 2] = std::sqrt(power / len); if(overallmax < max) overallmax = max; diff --git a/extern/audaspace/bindings/C/AUD_Special.h b/extern/audaspace/bindings/C/AUD_Special.h index ce51fa2e04e..2f5d13c6fd9 100644 --- a/extern/audaspace/bindings/C/AUD_Special.h +++ b/extern/audaspace/bindings/C/AUD_Special.h @@ -37,7 +37,7 @@ extern AUD_API float* AUD_readSoundBuffer(const char* filename, float low, float float attack, float release, float threshold, int accumulate, int additive, int square, float sthreshold, double samplerate, - int* length); + int* length, int stream); /** * Pauses a playing sound after a specific amount of time. diff --git a/extern/audaspace/bindings/C/AUD_Types.h b/extern/audaspace/bindings/C/AUD_Types.h index c6a96d30d3f..0f95366bc27 100644 --- a/extern/audaspace/bindings/C/AUD_Types.h +++ b/extern/audaspace/bindings/C/AUD_Types.h @@ -176,5 +176,17 @@ typedef struct { AUD_Specs specs; float length; - double start_offset; } AUD_SoundInfo; + +/// Specification of a sound source. +typedef struct +{ + /// Start time in seconds. + double start; + + /// Duration in seconds. May be estimated or 0 if unknown. + double duration; + + /// Audio data parameters. + AUD_DeviceSpecs specs; +} AUD_StreamInfo; diff --git a/extern/audaspace/bindings/python/PySound.cpp b/extern/audaspace/bindings/python/PySound.cpp index 33628307249..1f149482914 100644 --- a/extern/audaspace/bindings/python/PySound.cpp +++ b/extern/audaspace/bindings/python/PySound.cpp @@ -89,10 +89,11 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds) self = (Sound*)type->tp_alloc(type, 0); if(self != nullptr) { - static const char* kwlist[] = {"filename", nullptr}; + static const char* kwlist[] = {"filename", "stream", nullptr}; const char* filename = nullptr; + int stream = 0; - if(!PyArg_ParseTupleAndKeywords(args, kwds, "s:Sound", const_cast(kwlist), &filename)) + if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|i:Sound", const_cast(kwlist), &filename, &stream)) { Py_DECREF(self); return nullptr; @@ -100,7 +101,7 @@ Sound_new(PyTypeObject* type, PyObject* args, PyObject* kwds) try { - self->sound = new std::shared_ptr(new File(filename)); + self->sound = new std::shared_ptr(new File(filename, stream)); } catch(Exception& e) { @@ -289,7 +290,7 @@ PyDoc_STRVAR(M_aud_Sound_buffer_doc, ".. classmethod:: buffer(data, rate)\n\n" " Creates a sound from a data buffer.\n\n" " :arg data: The data as two dimensional numpy array.\n" - " :type data: numpy.ndarray\n" + " :type data: :class:`numpy.ndarray`\n" " :arg rate: The sample rate.\n" " :type rate: double\n" " :return: The created :class:`Sound` object.\n" @@ -407,8 +408,9 @@ static PyObject * Sound_file(PyTypeObject* type, PyObject* args) { const char* filename = nullptr; + int stream = 0; - if(!PyArg_ParseTuple(args, "s:file", &filename)) + if(!PyArg_ParseTuple(args, "s|i:file", &filename, &stream)) return nullptr; Sound* self; @@ -418,7 +420,7 @@ Sound_file(PyTypeObject* type, PyObject* args) { try { - self->sound = new std::shared_ptr(new File(filename)); + self->sound = new std::shared_ptr(new File(filename, stream)); } catch(Exception& e) { diff --git a/extern/audaspace/include/IReader.h b/extern/audaspace/include/IReader.h index f6070b0f23b..c29900ca579 100644 --- a/extern/audaspace/include/IReader.h +++ b/extern/audaspace/include/IReader.h @@ -70,12 +70,6 @@ public: */ virtual int getPosition() const=0; - /** - * Returns the start offset the sound should have to line up with related sources. - * \return The required start offset in seconds. - */ - virtual double getStartOffset() const { return 0.0;} - /** * Returns the specification of the reader. * \return The Specs structure. diff --git a/extern/audaspace/include/file/File.h b/extern/audaspace/include/file/File.h index 24745a757e8..ac490acba38 100644 --- a/extern/audaspace/include/file/File.h +++ b/extern/audaspace/include/file/File.h @@ -23,9 +23,11 @@ */ #include "ISound.h" +#include "FileInfo.h" #include #include +#include AUD_NAMESPACE_BEGIN @@ -48,6 +50,14 @@ private: */ std::shared_ptr m_buffer; + /** + * The index of the stream within the file if it contains multiple. + * The first audio stream in the file has index 0 and the index increments by one + * for every other audio stream in the file. Other types of streams in the file + * do not count. + */ + int m_stream; + // delete copy constructor and operator= File(const File&) = delete; File& operator=(const File&) = delete; @@ -57,16 +67,25 @@ public: * Creates a new sound. * The file is read from the file system using the given path. * \param filename The sound file path. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - File(std::string filename); + File(std::string filename, int stream = 0); /** * Creates a new sound. * The file is read from memory using the supplied buffer. * \param buffer The buffer to read from. * \param size The size of the buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - File(const data_t* buffer, int size); + File(const data_t* buffer, int size, int stream = 0); + + /** + * Queries the streams of the file. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + std::vector queryStreams(); virtual std::shared_ptr createReader(); }; diff --git a/extern/audaspace/include/file/FileInfo.h b/extern/audaspace/include/file/FileInfo.h new file mode 100644 index 00000000000..53ba99a5f67 --- /dev/null +++ b/extern/audaspace/include/file/FileInfo.h @@ -0,0 +1,42 @@ +/******************************************************************************* + * 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 FileInfo.h + * @ingroup file + * The FileInfo data structures. + */ + +#include "respec/Specification.h" + +AUD_NAMESPACE_BEGIN + +/// Specification of a sound source. +struct StreamInfo +{ + /// Start time in seconds. + double start; + + /// Duration in seconds. May be estimated or 0 if unknown. + double duration; + + /// Audio data parameters. + DeviceSpecs specs; +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/include/file/FileManager.h b/extern/audaspace/include/file/FileManager.h index 56708607ea6..e19eef65b1c 100644 --- a/extern/audaspace/include/file/FileManager.h +++ b/extern/audaspace/include/file/FileManager.h @@ -22,12 +22,14 @@ * The FileManager class. */ +#include "FileInfo.h" #include "respec/Specification.h" #include "IWriter.h" #include #include #include +#include AUD_NAMESPACE_BEGIN @@ -66,18 +68,36 @@ public: /** * Creates a file reader for the given filename if a registed IFileInput is able to read it. * @param filename The path to the file. + * @param stream The index of the audio stream within the file if it contains multiple audio streams. * @return The reader created. * @exception Exception If no file input can read the file an exception is thrown. */ - static std::shared_ptr createReader(std::string filename); + static std::shared_ptr createReader(std::string filename, int stream = 0); /** * Creates a file reader for the given buffer if a registed IFileInput is able to read it. * @param buffer The buffer to read the file from. + * @param stream The index of the audio stream within the file if it contains multiple audio streams. * @return The reader created. * @exception Exception If no file input can read the file an exception is thrown. */ - static std::shared_ptr createReader(std::shared_ptr buffer); + static std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + + /** + * Queries the streams of a sound file. + * \param filename Path to the file to be read. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + static std::vector queryStreams(std::string filename); + + /** + * Queries the streams of a sound file. + * \param buffer The in-memory file buffer. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + static std::vector queryStreams(std::shared_ptr buffer); /** * Creates a file writer that writes a sound to the given file path. diff --git a/extern/audaspace/include/file/IFileInput.h b/extern/audaspace/include/file/IFileInput.h index 64074910d13..4a3fe446852 100644 --- a/extern/audaspace/include/file/IFileInput.h +++ b/extern/audaspace/include/file/IFileInput.h @@ -23,9 +23,11 @@ */ #include "Audaspace.h" +#include "FileInfo.h" #include #include +#include AUD_NAMESPACE_BEGIN @@ -48,18 +50,36 @@ public: /** * Creates a reader for a file to be read. * \param filename Path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \return The reader that reads the file. * \exception Exception Thrown if the file specified cannot be read. */ - virtual std::shared_ptr createReader(std::string filename)=0; + virtual std::shared_ptr createReader(std::string filename, int stream = 0)=0; /** * Creates a reader for a file to be read from memory. * \param buffer The in-memory file buffer. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \return The reader that reads the file. * \exception Exception Thrown if the file specified cannot be read. */ - virtual std::shared_ptr createReader(std::shared_ptr buffer)=0; + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0)=0; + + /** + * Queries the streams of a sound file. + * \param filename Path to the file to be read. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(std::string filename)=0; + + /** + * Queries the streams of a sound file. + * \param buffer The in-memory file buffer. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(std::shared_ptr buffer)=0; }; AUD_NAMESPACE_END diff --git a/extern/audaspace/include/fx/VolumeReader.h b/extern/audaspace/include/fx/VolumeReader.h index f7169f4c78b..13b6845e931 100644 --- a/extern/audaspace/include/fx/VolumeReader.h +++ b/extern/audaspace/include/fx/VolumeReader.h @@ -67,4 +67,4 @@ public: virtual void read(int& length, bool& eos, sample_t* buffer); }; -AUD_NAMESPACE_END +AUD_NAMESPACE_END \ No newline at end of file diff --git a/extern/audaspace/include/util/RingBuffer.h b/extern/audaspace/include/util/RingBuffer.h new file mode 100644 index 00000000000..67bd1cc8640 --- /dev/null +++ b/extern/audaspace/include/util/RingBuffer.h @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright 2009-2021 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 RingBuffer.h + * @ingroup util + * The RingBuffer class. + */ + +#include "Audaspace.h" +#include "Buffer.h" + +#include + +AUD_NAMESPACE_BEGIN + +/** + * This class is a simple ring buffer in RAM which is 32 Byte aligned and provides + * functionality for concurrent reading and writting without locks. + */ +class AUD_API RingBuffer +{ +private: + /// The buffer storing the actual data. + Buffer m_buffer; + + /// The reading pointer. + volatile size_t m_read; + + /// The writing pointer. + volatile size_t m_write; + + // delete copy constructor and operator= + RingBuffer(const RingBuffer&) = delete; + RingBuffer& operator=(const RingBuffer&) = delete; + +public: + /** + * Creates a new ring buffer. + * \param size The size of the buffer in bytes. + */ + RingBuffer(int size = 0); + + /** + * Returns the pointer to the ring buffer in memory. + */ + sample_t* getBuffer() const; + + /** + * Returns the size of the ring buffer in bytes. + */ + int getSize() const; + + size_t getReadSize() const; + + size_t getWriteSize() const; + + size_t read(data_t* target, size_t size); + + size_t write(data_t* source, size_t size); + + /** + * Resets the ring buffer to a state where nothing has been written or read. + */ + void reset(); + + /** + * Resizes the ring buffer. + * \param size The new size of the ring buffer, measured in bytes. + */ + void resize(int size); + + /** + * Makes sure the ring buffer has a minimum size. + * If size is >= current size, nothing will happen. + * Otherwise the ring buffer is resized with keep as parameter. + * \param size The new minimum size of the ring buffer, measured in bytes. + */ + void assureSize(int size); +}; + +AUD_NAMESPACE_END diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp index 3ffe963b2b9..07c0fee691a 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.cpp @@ -35,14 +35,24 @@ void FFMPEG::registerPlugin() FileManager::registerOutput(plugin); } -std::shared_ptr FFMPEG::createReader(std::string filename) +std::shared_ptr FFMPEG::createReader(std::string filename, int stream) { - return std::shared_ptr(new FFMPEGReader(filename)); + return std::shared_ptr(new FFMPEGReader(filename, stream)); } -std::shared_ptr FFMPEG::createReader(std::shared_ptr buffer) +std::shared_ptr FFMPEG::createReader(std::shared_ptr buffer, int stream) { - return std::shared_ptr(new FFMPEGReader(buffer)); + return std::shared_ptr(new FFMPEGReader(buffer, stream)); +} + +std::vector FFMPEG::queryStreams(std::string filename) +{ + return FFMPEGReader(filename).queryStreams(); +} + +std::vector FFMPEG::queryStreams(std::shared_ptr buffer) +{ + return FFMPEGReader(buffer).queryStreams(); } std::shared_ptr FFMPEG::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEG.h b/extern/audaspace/plugins/ffmpeg/FFMPEG.h index 108ba547e0f..fb40ba05573 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEG.h +++ b/extern/audaspace/plugins/ffmpeg/FFMPEG.h @@ -52,8 +52,10 @@ public: */ static void registerPlugin(); - virtual std::shared_ptr createReader(std::string filename); - virtual std::shared_ptr createReader(std::shared_ptr buffer); + virtual std::shared_ptr createReader(std::string filename, int stream = 0); + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + virtual std::vector queryStreams(std::string filename); + virtual std::vector queryStreams(std::shared_ptr buffer); virtual std::shared_ptr createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate); }; diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp index afdc7fcfcc6..de3ca099696 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.cpp @@ -31,6 +31,25 @@ AUD_NAMESPACE_BEGIN #define FFMPEG_OLD_CODE #endif +SampleFormat FFMPEGReader::convertSampleFormat(AVSampleFormat format) +{ + switch(av_get_packed_sample_fmt(format)) + { + case AV_SAMPLE_FMT_U8: + return FORMAT_U8; + case AV_SAMPLE_FMT_S16: + return FORMAT_S16; + case AV_SAMPLE_FMT_S32: + return FORMAT_S32; + case AV_SAMPLE_FMT_FLT: + return FORMAT_FLOAT32; + case AV_SAMPLE_FMT_DBL: + return FORMAT_FLOAT64; + default: + AUD_THROW(FileException, "FFMPEG sample format unknown."); + } +} + int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) { int buf_size = buffer.getSize(); @@ -68,7 +87,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -109,7 +128,7 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) for(int i = 0; i < m_frame->nb_samples; i++) { std::memcpy(((data_t*)buffer.getBuffer()) + buf_pos + ((m_codecCtx->channels * i) + channel) * single_size, - m_frame->data[channel] + i * single_size, single_size); + m_frame->data[channel] + i * single_size, single_size); } } } @@ -123,13 +142,10 @@ int FFMPEGReader::decode(AVPacket& packet, Buffer& buffer) return buf_pos; } -void FFMPEGReader::init() +void FFMPEGReader::init(int stream) { m_position = 0; - m_start_offset = 0.0f; m_pkgbuf_left = 0; - m_st_time = 0; - m_duration = 0; if(avformat_find_stream_info(m_formatCtx, nullptr) < 0) AUD_THROW(FileException, "File couldn't be read, ffmpeg couldn't find the stream info."); @@ -137,43 +153,22 @@ void FFMPEGReader::init() // find audio stream and codec m_stream = -1; - double dur_sec = 0; - for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++) { #ifdef FFMPEG_OLD_CODE - if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) + if((m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) #else - if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) + if((m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) #endif + && (m_stream < 0)) { - AVStream *audio_stream = m_formatCtx->streams[i]; - double audio_timebase = av_q2d(audio_stream->time_base); - - if (audio_stream->start_time != AV_NOPTS_VALUE) + if(stream == 0) { - m_st_time = audio_stream->start_time; - } - - int64_t ctx_start_time = 0; - if (m_formatCtx->start_time != AV_NOPTS_VALUE) { - ctx_start_time = m_formatCtx->start_time; - } - - m_start_offset = m_st_time * audio_timebase - (double)ctx_start_time / AV_TIME_BASE; - - if(audio_stream->duration != AV_NOPTS_VALUE) - { - dur_sec = audio_stream->duration * audio_timebase; + m_stream=i; + break; } else - { - /* If the audio starts after the stream start time, subract this from the total duration. */ - dur_sec = (double)m_formatCtx->duration / AV_TIME_BASE - m_start_offset; - } - - m_stream=i; - break; + stream--; } } @@ -242,10 +237,9 @@ void FFMPEGReader::init() } m_specs.rate = (SampleRate) m_codecCtx->sample_rate; - m_duration = lround(dur_sec * m_codecCtx->sample_rate); } -FFMPEGReader::FFMPEGReader(std::string filename) : +FFMPEGReader::FFMPEGReader(std::string filename, int stream) : m_pkgbuf(), m_formatCtx(nullptr), m_codecCtx(nullptr), @@ -259,7 +253,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) : try { - init(); + init(stream); } catch(Exception&) { @@ -268,7 +262,7 @@ FFMPEGReader::FFMPEGReader(std::string filename) : } } -FFMPEGReader::FFMPEGReader(std::shared_ptr buffer) : +FFMPEGReader::FFMPEGReader(std::shared_ptr buffer, int stream) : m_pkgbuf(), m_codecCtx(nullptr), m_frame(nullptr), @@ -295,7 +289,7 @@ FFMPEGReader::FFMPEGReader(std::shared_ptr buffer) : try { - init(); + init(stream); } catch(Exception&) { @@ -318,6 +312,51 @@ FFMPEGReader::~FFMPEGReader() avformat_close_input(&m_formatCtx); } +std::vector FFMPEGReader::queryStreams() +{ + std::vector result; + + for(unsigned int i = 0; i < m_formatCtx->nb_streams; i++) + { +#ifdef FFMPEG_OLD_CODE + if(m_formatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) +#else + if(m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) +#endif + { + StreamInfo info; + + double time_base = av_q2d(m_formatCtx->streams[i]->time_base); + + if(m_formatCtx->streams[i]->start_time != AV_NOPTS_VALUE) + info.start = m_formatCtx->streams[i]->start_time * time_base; + else + info.start = 0; + + if(m_formatCtx->streams[i]->duration != AV_NOPTS_VALUE) + info.duration = m_formatCtx->streams[i]->duration * time_base; + else if(m_formatCtx->duration != AV_NOPTS_VALUE) + info.duration = double(m_formatCtx->duration) / AV_TIME_BASE - info.start; + else + info.duration = 0; + +#ifdef FFMPEG_OLD_CODE + info.specs.channels = Channels(m_formatCtx->streams[i]->codec->channels); + info.specs.rate = m_formatCtx->streams[i]->codec->sample_rate; + info.specs.format = convertSampleFormat(m_formatCtx->streams[i]->codec->sample_fmt); +#else + info.specs.channels = Channels(m_formatCtx->streams[i]->codecpar->channels); + info.specs.rate = m_formatCtx->streams[i]->codecpar->sample_rate; + info.specs.format = convertSampleFormat(AVSampleFormat(m_formatCtx->streams[i]->codecpar->format)); +#endif + + result.emplace_back(info); + } + } + + return result; +} + int FFMPEGReader::read_packet(void* opaque, uint8_t* buf, int buf_size) { FFMPEGReader* reader = reinterpret_cast(opaque); @@ -368,18 +407,16 @@ void FFMPEGReader::seek(int position) { if(position >= 0) { - double pts_time_base = - av_q2d(m_formatCtx->streams[m_stream]->time_base); + double pts_time_base = av_q2d(m_formatCtx->streams[m_stream]->time_base); - uint64_t seek_pts = (((uint64_t)position) / ((uint64_t)m_specs.rate)) / pts_time_base; + uint64_t st_time = m_formatCtx->streams[m_stream]->start_time; + uint64_t seek_pos = (uint64_t)(position / (pts_time_base * m_specs.rate)); - if(m_st_time != AV_NOPTS_VALUE) { - seek_pts += m_st_time; - } + if(st_time != AV_NOPTS_VALUE) + seek_pos += st_time; // a value < 0 tells us that seeking failed - if(av_seek_frame(m_formatCtx, m_stream, seek_pts, - AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0) + if(av_seek_frame(m_formatCtx, m_stream, seek_pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_ANY) >= 0) { avcodec_flush_buffers(m_codecCtx); m_position = position; @@ -400,7 +437,7 @@ void FFMPEGReader::seek(int position) if(packet.pts != AV_NOPTS_VALUE) { // calculate real position, and read to frame! - m_position = (packet.pts - m_st_time) * pts_time_base * m_specs.rate; + m_position = (packet.pts - (st_time != AV_NOPTS_VALUE ? st_time : 0)) * pts_time_base * m_specs.rate; if(m_position < position) { @@ -430,8 +467,25 @@ void FFMPEGReader::seek(int position) int FFMPEGReader::getLength() const { + auto stream = m_formatCtx->streams[m_stream]; + + double time_base = av_q2d(stream->time_base); + double duration; + + if(stream->duration != AV_NOPTS_VALUE) + duration = stream->duration * time_base; + else if(m_formatCtx->duration != AV_NOPTS_VALUE) + { + duration = float(m_formatCtx->duration) / AV_TIME_BASE; + + if(stream->start_time != AV_NOPTS_VALUE) + duration -= stream->start_time * time_base; + } + else + duration = -1; + // return approximated remaning size - return m_duration - m_position; + return (int)(duration * m_codecCtx->sample_rate) - m_position; } int FFMPEGReader::getPosition() const @@ -439,11 +493,6 @@ int FFMPEGReader::getPosition() const return m_position; } -double FFMPEGReader::getStartOffset() const -{ - return m_start_offset; -} - Specs FFMPEGReader::getSpecs() const { return m_specs.specs; @@ -480,13 +529,11 @@ void FFMPEGReader::read(int& length, bool& eos, sample_t* buffer) // decode the package pkgbuf_pos = decode(packet, m_pkgbuf); - if (packet.pts >= m_st_time) { - // copy to output buffer - data_size = std::min(pkgbuf_pos, left * sample_size); - m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); - buf += data_size / AUD_FORMAT_SIZE(m_specs.format); - left -= data_size / sample_size; - } + // copy to output buffer + data_size = std::min(pkgbuf_pos, left * sample_size); + m_convert((data_t*) buf, (data_t*) m_pkgbuf.getBuffer(), data_size / AUD_FORMAT_SIZE(m_specs.format)); + buf += data_size / AUD_FORMAT_SIZE(m_specs.format); + left -= data_size / sample_size; } av_packet_unref(&packet); } diff --git a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h index d613457c220..70f13911eca 100644 --- a/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h +++ b/extern/audaspace/plugins/ffmpeg/FFMPEGReader.h @@ -29,9 +29,11 @@ #include "respec/ConverterFunctions.h" #include "IReader.h" #include "util/Buffer.h" +#include "file/FileInfo.h" #include #include +#include struct AVCodecContext; extern "C" { @@ -54,22 +56,6 @@ private: */ int m_position; - /** - * The start offset in seconds relative to the media container start time. - * IE how much the sound should be delayed to be kept in sync with the rest of the containter streams. - */ - double m_start_offset; - - /** - * The start time pts of the stream. All packets before this timestamp shouldn't be played back (only decoded). - */ - int64_t m_st_time; - - /** - * The duration of the audio stream in samples. - */ - int64_t m_duration; - /** * The specification of the audio data. */ @@ -135,6 +121,13 @@ private: */ bool m_tointerleave; + /** + * Converts an ffmpeg sample format to an audaspace one. + * \param format The AVSampleFormat sample format. + * \return The sample format as SampleFormat. + */ + AUD_LOCAL static SampleFormat convertSampleFormat(AVSampleFormat format); + /** * Decodes a packet into the given buffer. * \param packet The AVPacket to decode. @@ -145,8 +138,9 @@ private: /** * Initializes the object. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. */ - AUD_LOCAL void init(); + AUD_LOCAL void init(int stream); // delete copy constructor and operator= FFMPEGReader(const FFMPEGReader&) = delete; @@ -156,24 +150,33 @@ public: /** * Creates a new reader. * \param filename The path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the file specified does not exist or * cannot be read with ffmpeg. */ - FFMPEGReader(std::string filename); + FFMPEGReader(std::string filename, int stream = 0); /** * Creates a new reader. * \param buffer The buffer to read from. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the buffer specified cannot be read * with ffmpeg. */ - FFMPEGReader(std::shared_ptr buffer); + FFMPEGReader(std::shared_ptr buffer, int stream = 0); /** * Destroys the reader and closes the file. */ virtual ~FFMPEGReader(); + /** + * Queries the streams of a sound file. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(); + /** * Reads data to a memory buffer. * This function is used for avio only. @@ -198,7 +201,6 @@ public: virtual void seek(int position); virtual int getLength() const; virtual int getPosition() const; - virtual double getStartOffset() const; virtual Specs getSpecs() const; virtual void read(int& length, bool& eos, sample_t* buffer); }; diff --git a/extern/audaspace/plugins/libsndfile/SndFile.cpp b/extern/audaspace/plugins/libsndfile/SndFile.cpp index ba4ff24ad68..39335de9a1a 100644 --- a/extern/audaspace/plugins/libsndfile/SndFile.cpp +++ b/extern/audaspace/plugins/libsndfile/SndFile.cpp @@ -32,16 +32,26 @@ void SndFile::registerPlugin() FileManager::registerOutput(plugin); } -std::shared_ptr SndFile::createReader(std::string filename) +std::shared_ptr SndFile::createReader(std::string filename, int stream) { return std::shared_ptr(new SndFileReader(filename)); } -std::shared_ptr SndFile::createReader(std::shared_ptr buffer) +std::shared_ptr SndFile::createReader(std::shared_ptr buffer, int stream) { return std::shared_ptr(new SndFileReader(buffer)); } +std::vector SndFile::queryStreams(std::string filename) +{ + return SndFileReader(filename).queryStreams(); +} + +std::vector SndFile::queryStreams(std::shared_ptr buffer) +{ + return SndFileReader(buffer).queryStreams(); +} + std::shared_ptr SndFile::createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate) { return std::shared_ptr(new SndFileWriter(filename, specs, format, codec, bitrate)); diff --git a/extern/audaspace/plugins/libsndfile/SndFile.h b/extern/audaspace/plugins/libsndfile/SndFile.h index 61afed1d564..10a7391180f 100644 --- a/extern/audaspace/plugins/libsndfile/SndFile.h +++ b/extern/audaspace/plugins/libsndfile/SndFile.h @@ -52,8 +52,10 @@ public: */ static void registerPlugin(); - virtual std::shared_ptr createReader(std::string filename); - virtual std::shared_ptr createReader(std::shared_ptr buffer); + virtual std::shared_ptr createReader(std::string filename, int stream = 0); + virtual std::shared_ptr createReader(std::shared_ptr buffer, int stream = 0); + virtual std::vector queryStreams(std::string filename); + virtual std::vector queryStreams(std::shared_ptr buffer); virtual std::shared_ptr createWriter(std::string filename, DeviceSpecs specs, Container format, Codec codec, unsigned int bitrate); }; diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.cpp b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp index d2d89814c07..21c733d8117 100644 --- a/extern/audaspace/plugins/libsndfile/SndFileReader.cpp +++ b/extern/audaspace/plugins/libsndfile/SndFileReader.cpp @@ -118,6 +118,21 @@ SndFileReader::~SndFileReader() sf_close(m_sndfile); } +std::vector SndFileReader::queryStreams() +{ + std::vector result; + + StreamInfo info; + info.start = 0; + info.duration = double(getLength()) / m_specs.rate; + info.specs.specs = m_specs; + info.specs.format = FORMAT_FLOAT32; + + result.emplace_back(info); + + return result; +} + bool SndFileReader::isSeekable() const { return m_seekable; diff --git a/extern/audaspace/plugins/libsndfile/SndFileReader.h b/extern/audaspace/plugins/libsndfile/SndFileReader.h index 081c29c686c..b4158d9091a 100644 --- a/extern/audaspace/plugins/libsndfile/SndFileReader.h +++ b/extern/audaspace/plugins/libsndfile/SndFileReader.h @@ -28,9 +28,12 @@ * The SndFileReader class. */ +#include "file/FileInfo.h" + #include #include #include +#include AUD_NAMESPACE_BEGIN @@ -96,6 +99,7 @@ public: /** * Creates a new reader. * \param filename The path to the file to be read. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the file specified does not exist or * cannot be read with libsndfile. */ @@ -104,6 +108,7 @@ public: /** * Creates a new reader. * \param buffer The buffer to read from. + * \param stream The index of the audio stream within the file if it contains multiple audio streams. * \exception Exception Thrown if the buffer specified cannot be read * with libsndfile. */ @@ -114,6 +119,13 @@ public: */ virtual ~SndFileReader(); + /** + * Queries the streams of a sound file. + * \return A vector with as many streams as there are in the file. + * \exception Exception Thrown if the file specified cannot be read. + */ + virtual std::vector queryStreams(); + virtual bool isSeekable() const; virtual void seek(int position); virtual int getLength() const; diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp index bf3fad82620..cddc411cfc6 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.cpp @@ -23,95 +23,121 @@ AUD_NAMESPACE_BEGIN +PulseAudioDevice::PulseAudioSynchronizer::PulseAudioSynchronizer(PulseAudioDevice *device) : + m_device(device) +{ +} + +double PulseAudioDevice::PulseAudioSynchronizer::getPosition(std::shared_ptr handle) +{ + pa_usec_t latency; + int negative; + AUD_pa_stream_get_latency(m_device->m_stream, &latency, &negative); + + double delay = m_device->m_ring_buffer.getReadSize() / (AUD_SAMPLE_SIZE(m_device->m_specs) * m_device->m_specs.rate) + latency * 1.0e-6; + + return handle->getPosition() - delay; +} + +void PulseAudioDevice::updateRingBuffer() +{ + unsigned int samplesize = AUD_SAMPLE_SIZE(m_specs); + + std::unique_lock lock(m_mixingLock); + + Buffer buffer; + + while(m_valid) + { + size_t size = m_ring_buffer.getWriteSize(); + + size_t sample_count = size / samplesize; + + if(sample_count > 0) + { + size = sample_count * samplesize; + + buffer.assureSize(size); + + mix(reinterpret_cast(buffer.getBuffer()), sample_count); + + m_ring_buffer.write(reinterpret_cast(buffer.getBuffer()), size); + } + + m_mixingCondition.wait(lock); + } +} + void PulseAudioDevice::PulseAudio_state_callback(pa_context *context, void *data) { PulseAudioDevice* device = (PulseAudioDevice*)data; - std::lock_guard lock(*device); - device->m_state = AUD_pa_context_get_state(context); + + AUD_pa_threaded_mainloop_signal(device->m_mainloop, 0); } void PulseAudioDevice::PulseAudio_request(pa_stream *stream, size_t total_bytes, void *data) { PulseAudioDevice* device = (PulseAudioDevice*)data; - void* buffer; + data_t* buffer; + + size_t sample_size = AUD_DEVICE_SAMPLE_SIZE(device->m_specs); while(total_bytes > 0) { size_t num_bytes = total_bytes; - AUD_pa_stream_begin_write(stream, &buffer, &num_bytes); + AUD_pa_stream_begin_write(stream, reinterpret_cast(&buffer), &num_bytes); - device->mix((data_t*)buffer, num_bytes / AUD_DEVICE_SAMPLE_SIZE(device->m_specs)); + size_t readsamples = device->m_ring_buffer.getReadSize(); - AUD_pa_stream_write(stream, buffer, num_bytes, nullptr, 0, PA_SEEK_RELATIVE); + readsamples = std::min(readsamples, size_t(num_bytes)) / sample_size; + + device->m_ring_buffer.read(buffer, readsamples * sample_size); + + if(readsamples * sample_size < num_bytes) + std::memset(buffer + readsamples * sample_size, 0, num_bytes - readsamples * sample_size); + + if(device->m_mixingLock.try_lock()) + { + device->m_mixingCondition.notify_all(); + device->m_mixingLock.unlock(); + } + + AUD_pa_stream_write(stream, reinterpret_cast(buffer), num_bytes, nullptr, 0, PA_SEEK_RELATIVE); total_bytes -= num_bytes; } } -void PulseAudioDevice::PulseAudio_underflow(pa_stream *stream, void *data) +void PulseAudioDevice::playing(bool playing) { - PulseAudioDevice* device = (PulseAudioDevice*)data; + m_playback = playing; - DeviceSpecs specs = device->getSpecs(); - - if(++device->m_underflows > 4 && device->m_buffersize < AUD_DEVICE_SAMPLE_SIZE(specs) * specs.rate * 2) - { - device->m_buffersize <<= 1; - device->m_underflows = 0; - - pa_buffer_attr buffer_attr; - - buffer_attr.fragsize = -1U; - buffer_attr.maxlength = -1U; - buffer_attr.minreq = -1U; - buffer_attr.prebuf = -1U; - buffer_attr.tlength = device->m_buffersize; - - AUD_pa_stream_set_buffer_attr(stream, &buffer_attr, nullptr, nullptr); - } -} - -void PulseAudioDevice::runMixingThread() -{ - for(;;) - { - { - std::lock_guard lock(*this); - - if(shouldStop()) - { - AUD_pa_stream_cork(m_stream, 1, nullptr, nullptr); - AUD_pa_stream_flush(m_stream, nullptr, nullptr); - doStop(); - return; - } - } - - if(AUD_pa_stream_is_corked(m_stream)) - AUD_pa_stream_cork(m_stream, 0, nullptr, nullptr); - - // similar to AUD_pa_mainloop_iterate(m_mainloop, false, nullptr); except with a longer timeout - AUD_pa_mainloop_prepare(m_mainloop, 1 << 14); - AUD_pa_mainloop_poll(m_mainloop); - AUD_pa_mainloop_dispatch(m_mainloop); - } + AUD_pa_threaded_mainloop_lock(m_mainloop); + AUD_pa_stream_cork(m_stream, playing ? 0 : 1, nullptr, nullptr); + AUD_pa_threaded_mainloop_unlock(m_mainloop); } PulseAudioDevice::PulseAudioDevice(std::string name, DeviceSpecs specs, int buffersize) : + m_synchronizer(this), + m_playback(false), m_state(PA_CONTEXT_UNCONNECTED), + m_valid(true), m_underflows(0) { - m_mainloop = AUD_pa_mainloop_new(); + m_mainloop = AUD_pa_threaded_mainloop_new(); - m_context = AUD_pa_context_new(AUD_pa_mainloop_get_api(m_mainloop), name.c_str()); + AUD_pa_threaded_mainloop_lock(m_mainloop); + + m_context = AUD_pa_context_new(AUD_pa_threaded_mainloop_get_api(m_mainloop), name.c_str()); if(!m_context) { - AUD_pa_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_unlock(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); } @@ -120,21 +146,26 @@ 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_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect to PulseAudio."); break; default: - AUD_pa_mainloop_iterate(m_mainloop, true, nullptr); + AUD_pa_threaded_mainloop_wait(m_mainloop); break; } } @@ -182,16 +213,18 @@ 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_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not create PulseAudio stream."); } AUD_pa_stream_set_write_callback(m_stream, PulseAudio_request, this); - AUD_pa_stream_set_underflow_callback(m_stream, PulseAudio_underflow, this); buffersize *= AUD_DEVICE_SAMPLE_SIZE(m_specs); m_buffersize = buffersize; @@ -204,31 +237,53 @@ 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_START_CORKED | PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY | PA_STREAM_AUTO_TIMING_UPDATE), nullptr, nullptr) < 0) + m_ring_buffer.resize(buffersize); + + if(AUD_pa_stream_connect_playback(m_stream, nullptr, &buffer_attr, static_cast(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_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); AUD_THROW(DeviceException, "Could not connect PulseAudio stream."); } + AUD_pa_threaded_mainloop_unlock(m_mainloop); + create(); + + m_mixingThread = std::thread(&PulseAudioDevice::updateRingBuffer, this); } PulseAudioDevice::~PulseAudioDevice() { - stopMixingThread(); + m_valid = false; + + m_mixingLock.lock(); + m_mixingCondition.notify_all(); + m_mixingLock.unlock(); + + m_mixingThread.join(); + + AUD_pa_threaded_mainloop_stop(m_mainloop); AUD_pa_context_disconnect(m_context); AUD_pa_context_unref(m_context); - AUD_pa_mainloop_free(m_mainloop); + AUD_pa_threaded_mainloop_free(m_mainloop); destroy(); } +ISynchronizer *PulseAudioDevice::getSynchronizer() +{ + return &m_synchronizer; +} + class PulseAudioDeviceFactory : public IDeviceFactory { private: diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h index 45b813a5755..57359110633 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioDevice.h @@ -26,7 +26,11 @@ * The PulseAudioDevice class. */ -#include "devices/ThreadedDevice.h" +#include "devices/SoftwareDevice.h" +#include "util/RingBuffer.h" + +#include +#include #include @@ -35,17 +39,65 @@ AUD_NAMESPACE_BEGIN /** * This device plays back through PulseAudio, the simple direct media layer. */ -class AUD_PLUGIN_API PulseAudioDevice : public ThreadedDevice +class AUD_PLUGIN_API PulseAudioDevice : public SoftwareDevice { private: - pa_mainloop* m_mainloop; + class PulseAudioSynchronizer : public DefaultSynchronizer + { + PulseAudioDevice* m_device; + + public: + PulseAudioSynchronizer(PulseAudioDevice* device); + + virtual double getPosition(std::shared_ptr handle); + }; + + /// Synchronizer. + PulseAudioSynchronizer m_synchronizer; + + /** + * Whether there is currently playback. + */ + volatile bool m_playback; + + pa_threaded_mainloop* m_mainloop; pa_context* m_context; pa_stream* m_stream; pa_context_state_t m_state; + /** + * The mixing ring buffer. + */ + RingBuffer m_ring_buffer; + + /** + * Whether the device is valid. + */ + bool m_valid; + int m_buffersize; uint32_t m_underflows; + /** + * The mixing thread. + */ + std::thread m_mixingThread; + + /** + * Mutex for mixing. + */ + std::mutex m_mixingLock; + + /** + * Condition for mixing. + */ + std::condition_variable m_mixingCondition; + + /** + * Updates the ring buffer. + */ + AUD_LOCAL void updateRingBuffer(); + /** * Reports the state of the PulseAudio server connection. * \param context The PulseAudio context. @@ -61,23 +113,13 @@ private: */ AUD_LOCAL static void PulseAudio_request(pa_stream* stream, size_t total_bytes, void* data); - /** - * Reports an underflow from the PulseAudio server. - * Automatically adjusts the latency if this happens too often. - * @param stream The PulseAudio stream. - * \param data The PulseAudio device. - */ - 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. @@ -93,6 +135,8 @@ public: */ virtual ~PulseAudioDevice(); + virtual ISynchronizer* getSynchronizer(); + /** * Registers this plugin. */ diff --git a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h index 361aa518087..a33135b6e25 100644 --- a/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h +++ b/extern/audaspace/plugins/pulseaudio/PulseAudioSymbols.h @@ -25,6 +25,7 @@ PULSEAUDIO_SYMBOL(pa_stream_begin_write); PULSEAUDIO_SYMBOL(pa_stream_connect_playback); PULSEAUDIO_SYMBOL(pa_stream_cork); PULSEAUDIO_SYMBOL(pa_stream_flush); +PULSEAUDIO_SYMBOL(pa_stream_get_latency); PULSEAUDIO_SYMBOL(pa_stream_is_corked); PULSEAUDIO_SYMBOL(pa_stream_new); PULSEAUDIO_SYMBOL(pa_stream_set_buffer_attr); @@ -39,3 +40,13 @@ PULSEAUDIO_SYMBOL(pa_mainloop_iterate); PULSEAUDIO_SYMBOL(pa_mainloop_prepare); PULSEAUDIO_SYMBOL(pa_mainloop_poll); PULSEAUDIO_SYMBOL(pa_mainloop_dispatch); + +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); diff --git a/extern/audaspace/src/file/File.cpp b/extern/audaspace/src/file/File.cpp index 0cdecb03657..5d4bae482d6 100644 --- a/extern/audaspace/src/file/File.cpp +++ b/extern/audaspace/src/file/File.cpp @@ -23,23 +23,31 @@ AUD_NAMESPACE_BEGIN -File::File(std::string filename) : - m_filename(filename) +File::File(std::string filename, int stream) : + m_filename(filename), m_stream(stream) { } -File::File(const data_t* buffer, int size) : - m_buffer(new Buffer(size)) +File::File(const data_t* buffer, int size, int stream) : + m_buffer(new Buffer(size)), m_stream(stream) { std::memcpy(m_buffer->getBuffer(), buffer, size); } +std::vector File::queryStreams() +{ + if(m_buffer.get()) + return FileManager::queryStreams(m_buffer); + else + return FileManager::queryStreams(m_filename); +} + std::shared_ptr File::createReader() { if(m_buffer.get()) - return FileManager::createReader(m_buffer); + return FileManager::createReader(m_buffer, m_stream); else - return FileManager::createReader(m_filename); + return FileManager::createReader(m_filename, m_stream); } AUD_NAMESPACE_END diff --git a/extern/audaspace/src/file/FileManager.cpp b/extern/audaspace/src/file/FileManager.cpp index f8ef8deb409..7cbc0318f8c 100644 --- a/extern/audaspace/src/file/FileManager.cpp +++ b/extern/audaspace/src/file/FileManager.cpp @@ -43,13 +43,13 @@ void FileManager::registerOutput(std::shared_ptr output) outputs().push_back(output); } -std::shared_ptr FileManager::createReader(std::string filename) +std::shared_ptr FileManager::createReader(std::string filename, int stream) { for(std::shared_ptr input : inputs()) { try { - return input->createReader(filename); + return input->createReader(filename, stream); } catch(Exception&) {} } @@ -57,13 +57,41 @@ std::shared_ptr FileManager::createReader(std::string filename) AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); } -std::shared_ptr FileManager::createReader(std::shared_ptr buffer) +std::shared_ptr FileManager::createReader(std::shared_ptr buffer, int stream) { for(std::shared_ptr input : inputs()) { try { - return input->createReader(buffer); + return input->createReader(buffer, stream); + } + catch(Exception&) {} + } + + AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); +} + +std::vector FileManager::queryStreams(std::string filename) +{ + for(std::shared_ptr input : inputs()) + { + try + { + return input->queryStreams(filename); + } + catch(Exception&) {} + } + + AUD_THROW(FileException, "The file couldn't be read with any installed file reader."); +} + +std::vector FileManager::queryStreams(std::shared_ptr buffer) +{ + for(std::shared_ptr input : inputs()) + { + try + { + return input->queryStreams(buffer); } catch(Exception&) {} } diff --git a/extern/audaspace/src/fx/VolumeReader.cpp b/extern/audaspace/src/fx/VolumeReader.cpp index 627acbac9ef..ac1d4882a87 100644 --- a/extern/audaspace/src/fx/VolumeReader.cpp +++ b/extern/audaspace/src/fx/VolumeReader.cpp @@ -57,4 +57,4 @@ void VolumeReader::read(int& length, bool& eos, sample_t* buffer) buffer[i] = buffer[i] * m_volumeStorage->getVolume(); } -AUD_NAMESPACE_END +AUD_NAMESPACE_END \ No newline at end of file diff --git a/extern/audaspace/src/util/RingBuffer.cpp b/extern/audaspace/src/util/RingBuffer.cpp new file mode 100644 index 00000000000..3796684aa88 --- /dev/null +++ b/extern/audaspace/src/util/RingBuffer.cpp @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright 2009-2021 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 "util/RingBuffer.h" + +#include +#include +#include + +#define ALIGNMENT 32 +#define ALIGN(a) (a + ALIGNMENT - ((long long)a & (ALIGNMENT-1))) + +AUD_NAMESPACE_BEGIN + +RingBuffer::RingBuffer(int size) : + m_buffer(size), + m_read(0), + m_write(0) +{ +} + +sample_t* RingBuffer::getBuffer() const +{ + return m_buffer.getBuffer(); +} + +int RingBuffer::getSize() const +{ + return m_buffer.getSize(); +} + +size_t RingBuffer::getReadSize() const +{ + size_t read = m_read; + size_t write = m_write; + + if(read > write) + return write + getSize() - read; + else + return write - read; +} + +size_t RingBuffer::getWriteSize() const +{ + size_t read = m_read; + size_t write = m_write; + + if(read > write) + return read - write - 1; + else + return read + getSize() - write - 1; +} + +size_t RingBuffer::read(data_t* target, size_t size) +{ + size = std::min(size, getReadSize()); + + data_t* buffer = reinterpret_cast(m_buffer.getBuffer()); + + if(m_read + size > m_buffer.getSize()) + { + size_t read_first = m_buffer.getSize() - m_read; + size_t read_second = size - read_first; + + std::memcpy(target, buffer + m_read, read_first); + std::memcpy(target + read_first, buffer, read_second); + + m_read = read_second; + } + else + { + std::memcpy(target, buffer + m_read, size); + + m_read += size; + } + + return size; +} + +size_t RingBuffer::write(data_t* source, size_t size) +{ + size = std::min(size, getWriteSize()); + + data_t* buffer = reinterpret_cast(m_buffer.getBuffer()); + + if(m_write + size > m_buffer.getSize()) + { + size_t write_first = m_buffer.getSize() - m_write; + size_t write_second = size - write_first; + + std::memcpy(buffer + m_write, source, write_first); + std::memcpy(buffer, source + write_first, write_second); + + m_write = write_second; + } + else + { + std::memcpy(buffer + m_write, source, size); + + m_write += size; + } + + return size; +} + +void RingBuffer::reset() +{ + m_read = 0; + m_write = 0; +} + +void RingBuffer::resize(int size) +{ + m_buffer.resize(size); + reset(); +} + +void RingBuffer::assureSize(int size) +{ + m_buffer.assureSize(size); + reset(); +} + +AUD_NAMESPACE_END diff --git a/extern/cuew/include/cuew.h b/extern/cuew/include/cuew.h index 0fa0f1291fa..5979f48e43d 100644 --- a/extern/cuew/include/cuew.h +++ b/extern/cuew/include/cuew.h @@ -609,6 +609,7 @@ typedef enum cudaError_enum { CUDA_ERROR_INVALID_GRAPHICS_CONTEXT = 219, CUDA_ERROR_NVLINK_UNCORRECTABLE = 220, CUDA_ERROR_JIT_COMPILER_NOT_FOUND = 221, + CUDA_ERROR_UNSUPPORTED_PTX_VERSION = 222, CUDA_ERROR_INVALID_SOURCE = 300, CUDA_ERROR_FILE_NOT_FOUND = 301, CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND = 302, @@ -645,7 +646,8 @@ typedef enum CUdevice_P2PAttribute_enum { CU_DEVICE_P2P_ATTRIBUTE_PERFORMANCE_RANK = 0x01, CU_DEVICE_P2P_ATTRIBUTE_ACCESS_SUPPORTED = 0x02, CU_DEVICE_P2P_ATTRIBUTE_NATIVE_ATOMIC_SUPPORTED = 0x03, - CU_DEVICE_P2P_ATTRIBUTE_ARRAY_ACCESS_ACCESS_SUPPORTED = 0x04, + CU_DEVICE_P2P_ATTRIBUTE_ACCESS_ACCESS_SUPPORTED = 0x04, + CU_DEVICE_P2P_ATTRIBUTE_CUDA_ARRAY_ACCESS_SUPPORTED = 0x04, } CUdevice_P2PAttribute; typedef void (CUDA_CB *CUstreamCallback)(CUstream hStream, CUresult status, void* userData); diff --git a/extern/cuew/src/cuew.c b/extern/cuew/src/cuew.c index 7a1b0018a24..9eba9306323 100644 --- a/extern/cuew/src/cuew.c +++ b/extern/cuew/src/cuew.c @@ -736,6 +736,7 @@ const char *cuewErrorString(CUresult result) { case CUDA_ERROR_INVALID_GRAPHICS_CONTEXT: return "Invalid graphics context"; case CUDA_ERROR_NVLINK_UNCORRECTABLE: return "Nvlink uncorrectable"; case CUDA_ERROR_JIT_COMPILER_NOT_FOUND: return "Jit compiler not found"; + case CUDA_ERROR_UNSUPPORTED_PTX_VERSION: return "Unsupported PTX version"; case CUDA_ERROR_INVALID_SOURCE: return "Invalid source"; case CUDA_ERROR_FILE_NOT_FOUND: return "File not found"; case CUDA_ERROR_SHARED_OBJECT_SYMBOL_NOT_FOUND: return "Link to a shared object failed to resolve"; diff --git a/extern/hipew/CMakeLists.txt b/extern/hipew/CMakeLists.txt new file mode 100644 index 00000000000..d215ea8c691 --- /dev/null +++ b/extern/hipew/CMakeLists.txt @@ -0,0 +1,39 @@ +# ***** 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) 2021, Blender Foundation +# All rights reserved. +# ***** END GPL LICENSE BLOCK ***** + +set(INC + . + include +) + +set(INC_SYS + +) + +set(SRC + src/hipew.c + + include/hipew.h +) + +set(LIB +) + +blender_add_lib(extern_hipew "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/extern/hipew/include/hipew.h b/extern/hipew/include/hipew.h new file mode 100644 index 00000000000..d18cf67524d --- /dev/null +++ b/extern/hipew/include/hipew.h @@ -0,0 +1,1352 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#ifndef __HIPEW_H__ +#define __HIPEW_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define HIP_IPC_HANDLE_SIZE 64 +#define hipHostMallocDefault 0x00 +#define hipHostMallocPortable 0x01 +#define hipHostMallocMapped 0x02 +#define hipHostMallocWriteCombined 0x04 +#define hipHostMallocNumaUser 0x20000000 +#define hipHostMallocCoherent 0x40000000 +#define hipHostMallocNonCoherent 0x80000000 +#define hipHostRegisterPortable 0x01 +#define hipHostRegisterMapped 0x02 +#define hipHostRegisterIoMemory 0x04 +#define hipCooperativeLaunchMultiDeviceNoPreSync 0x01 +#define hipCooperativeLaunchMultiDeviceNoPostSync 0x02 +#define hipArrayLayered 0x01 +#define hipArraySurfaceLoadStore 0x02 +#define hipArrayCubemap 0x04 +#define hipArrayTextureGather 0x08 +#define HIP_TRSA_OVERRIDE_FORMAT 0x01 +#define HIP_TRSF_READ_AS_INTEGER 0x01 +#define HIP_TRSF_NORMALIZED_COORDINATES 0x02 +#define HIP_LAUNCH_PARAM_END ((void*)0x00) +#define HIP_LAUNCH_PARAM_BUFFER_POINTER ((void*)0x01) +#define HIP_LAUNCH_PARAM_BUFFER_SIZE ((void*)0x02) + +/* Functions which changed 3.1 -> 3.2 for 64 bit stuff, + * the cuda library has both the old ones for compatibility and new + * ones with _v2 postfix, + */ +#define hipModuleGetGlobal hipModuleGetGlobal +#define hipMemGetInfo hipMemGetInfo +#define hipMemAllocPitch hipMemAllocPitch +#define hipMemGetAddressRange hipMemGetAddressRange +#define hipMemcpyHtoD hipMemcpyHtoD +#define hipMemcpyDtoH hipMemcpyDtoH +#define hipMemcpyDtoD hipMemcpyDtoD +#define hipMemcpyHtoA hipMemcpyHtoA +#define hipMemcpyAtoH hipMemcpyAtoH +#define hipMemcpyHtoDAsync hipMemcpyHtoDAsync +#define hipMemcpyDtoHAsync hipMemcpyDtoHAsync +#define hipMemcpyDtoDAsync hipMemcpyDtoDAsync +#define hipMemsetD8 hipMemsetD8 +#define hipMemsetD16 hipMemsetD16 +#define hipMemsetD32 hipMemsetD32 +#define hipArrayCreate hipArrayCreate +#define hipArray3DCreate hipArray3DCreate +#define hipTexRefSetAddress hipTexRefSetAddress +#define hipTexRefGetAddress hipTexRefGetAddress +#define hipStreamDestroy hipStreamDestroy +#define hipEventDestroy hipEventDestroy +#define hipTexRefSetAddress2D hipTexRefSetAddress2D + +/* Types. */ +#ifdef _MSC_VER +typedef unsigned __int32 hipuint32_t; +typedef unsigned __int64 hipuint64_t; +#else +#include +typedef uint32_t hipuint32_t; +typedef uint64_t hipuint64_t; +#endif + +#if defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) || defined (__aarch64__) +typedef unsigned long long hipDeviceptr_t; +#else +typedef unsigned int hipDeviceptr_t; +#endif + + +#ifdef _WIN32 +# define HIPAPI __stdcall +# define HIP_CB __stdcall +#else +# define HIPAPI +# define HIP_CB +#endif + +typedef int hipDevice_t; +typedef struct ihipCtx_t* hipCtx_t; +typedef struct ihipModule_t* hipModule_t; +typedef struct ihipModuleSymbol_t* hipFunction_t; +typedef struct hipArray* hArray; +typedef struct hipMipmappedArray_st* hipMipmappedArray_t; +typedef struct ihipEvent_t* hipEvent_t; +typedef struct ihipStream_t* hipStream_t; +typedef unsigned long long hipTextureObject_t; + +typedef struct HIPuuid_st { + char bytes[16]; +} HIPuuid; + +typedef enum hipChannelFormatKind { + hipChannelFormatKindSigned = 0, + hipChannelFormatKindUnsigned = 1, + hipChannelFormatKindFloat = 2, + hipChannelFormatKindNone = 3, +}hipChannelFormatKind; + +typedef struct hipChannelFormatDesc { + int x; + int y; + int z; + int w; + enum hipChannelFormatKind f; +}hipChannelFormatDesc; + +typedef enum hipTextureFilterMode { + hipFilterModePoint = 0, + hipFilterModeLinear = 1, +} hipTextureFilterMode; + +typedef enum hipArray_Format { + HIP_AD_FORMAT_UNSIGNED_INT8 = 0x01, + HIP_AD_FORMAT_SIGNED_INT8 = 0x08, + HIP_AD_FORMAT_UNSIGNED_INT16 = 0x02, + HIP_AD_FORMAT_SIGNED_INT16 = 0x09, + HIP_AD_FORMAT_UNSIGNED_INT32 = 0x03, + HIP_AD_FORMAT_SIGNED_INT32 = 0x0a, + HIP_AD_FORMAT_HALF = 0x10, + HIP_AD_FORMAT_FLOAT = 0x20, +} hipArray_Format; + +typedef enum hipTextureAddressMode { + hipAddressModeWrap = 0, + hipAddressModeClamp = 1, + hipAddressModeMirror = 2, + hipAddressModeBorder = 3, +} hipTextureAddressMode; + +/** + * hip texture reference + */ +typedef struct textureReference { + int normalized; + //enum hipTextureReadMode readMode;// used only for driver API's + enum hipTextureFilterMode filterMode; + enum hipTextureAddressMode addressMode[3]; // Texture address mode for up to 3 dimensions + struct hipChannelFormatDesc channelDesc; + int sRGB; // Perform sRGB->linear conversion during texture read + unsigned int maxAnisotropy; // Limit to the anisotropy ratio + enum hipTextureFilterMode mipmapFilterMode; + float mipmapLevelBias; + float minMipmapLevelClamp; + float maxMipmapLevelClamp; + + hipTextureObject_t textureObject; + int numChannels; + enum hipArray_Format format; +}textureReference; + +typedef textureReference* hipTexRef; + +typedef enum hipMemoryType { + hipMemoryTypeHost = 0x00, + hipMemoryTypeDevice = 0x01, + hipMemoryTypeArray = 0x02, + hipMemoryTypeUnified = 0x03, +} hipMemoryType; + +/** + * Pointer attributes + */ +typedef struct hipPointerAttribute_t { + enum hipMemoryType memoryType; + int device; + void* devicePointer; + void* hostPointer; + int isManaged; + unsigned allocationFlags; /* flags specified when memory was allocated*/ + /* peers? */ +} hipPointerAttribute_t; + +typedef struct ihipIpcEventHandle_t { + char reserved[HIP_IPC_HANDLE_SIZE]; +} ihipIpcEventHandle_t; + +typedef struct hipIpcMemHandle_st { + char reserved[HIP_IPC_HANDLE_SIZE]; +} hipIpcMemHandle_t; + +typedef enum HIPipcMem_flags_enum { + hipIpcMemLazyEnablePeerAccess = 0x1, +} HIPipcMem_flags; + +typedef enum HIPmemAttach_flags_enum { + hipMemAttachGlobal = 0x1, + hipMemAttachHost = 0x2, + HIP_MEM_ATTACH_SINGLE = 0x4, +} HIPmemAttach_flags; + +typedef enum HIPctx_flags_enum { + hipDeviceScheduleAuto = 0x00, + hipDeviceScheduleSpin = 0x01, + hipDeviceScheduleYield = 0x02, + hipDeviceScheduleBlockingSync = 0x04, + hipDeviceScheduleMask = 0x07, + hipDeviceMapHost = 0x08, + hipDeviceLmemResizeToMax = 0x10, +} HIPctx_flags; + +typedef enum HIPstream_flags_enum { + hipStreamDefault = 0x0, + hipStreamNonBlocking = 0x1, +} HIPstream_flags; + +typedef enum HIPevent_flags_enum { + hipEventDefault = 0x0, + hipEventBlockingSync = 0x1, + hipEventDisableTiming = 0x2, + hipEventInterprocess = 0x4, +} HIPevent_flags; + +typedef enum HIPstreamWaitValue_flags_enum { + HIP_STREAM_WAIT_VALUE_GEQ = 0x0, + HIP_STREAM_WAIT_VALUE_EQ = 0x1, + HIP_STREAM_WAIT_VALUE_AND = 0x2, + HIP_STREAM_WAIT_VALUE_NOR = 0x3, + HIP_STREAM_WAIT_VALUE_FLUSH = (1 << 30), +} HIPstreamWaitValue_flags; + +typedef enum HIPstreamWriteValue_flags_enum { + HIP_STREAM_WRITE_VALUE_DEFAULT = 0x0, + HIP_STREAM_WRITE_VALUE_NO_MEMORY_BARRIER = 0x1, +} HIPstreamWriteValue_flags; + +typedef enum HIPstreamBatchMemOpType_enum { + HIP_STREAM_MEM_OP_WAIT_VALUE_32 = 1, + HIP_STREAM_MEM_OP_WRITE_VALUE_32 = 2, + HIP_STREAM_MEM_OP_WAIT_VALUE_64 = 4, + HIP_STREAM_MEM_OP_WRITE_VALUE_64 = 5, + HIP_STREAM_MEM_OP_FLUSH_REMOTE_WRITES = 3, +} HIPstreamBatchMemOpType; + + +typedef union HIPstreamBatchMemOpParams_union { + HIPstreamBatchMemOpType operation; + struct HIPstreamMemOpWaitValueParams_st { + HIPstreamBatchMemOpType operation; + hipDeviceptr_t address; + union { + hipuint32_t value; + hipuint64_t value64; + }; + unsigned int flags; + hipDeviceptr_t alias; + } waitValue; + struct HIPstreamMemOpWriteValueParams_st { + HIPstreamBatchMemOpType operation; + hipDeviceptr_t address; + union { + hipuint32_t value; + hipuint64_t value64; + }; + unsigned int flags; + hipDeviceptr_t alias; + } writeValue; + struct HIPstreamMemOpFlushRemoteWritesParams_st { + HIPstreamBatchMemOpType operation; + unsigned int flags; + } flushRemoteWrites; + hipuint64_t pad[6]; +} HIPstreamBatchMemOpParams; + +typedef enum HIPoccupancy_flags_enum { + hipOccupancyDefault = 0x0, + HIP_OCCUPANCY_DISABLE_CACHING_OVERRIDE = 0x1, +} HIPoccupancy_flags; + +typedef enum hipDeviceAttribute_t { + hipDeviceAttributeCudaCompatibleBegin = 0, + hipDeviceAttributeEccEnabled = hipDeviceAttributeCudaCompatibleBegin, ///< Whether ECC support is enabled. + hipDeviceAttributeAccessPolicyMaxWindowSize, ///< Cuda only. The maximum size of the window policy in bytes. + hipDeviceAttributeAsyncEngineCount, ///< Cuda only. Asynchronous engines number. + hipDeviceAttributeCanMapHostMemory, ///< Whether host memory can be mapped into device address space + hipDeviceAttributeCanUseHostPointerForRegisteredMem,///< Cuda only. Device can access host registered memory + ///< at the same virtual address as the CPU + hipDeviceAttributeClockRate, ///< Peak clock frequency in kilohertz. + hipDeviceAttributeComputeMode, ///< Compute mode that device is currently in. + hipDeviceAttributeComputePreemptionSupported, ///< Cuda only. Device supports Compute Preemption. + hipDeviceAttributeConcurrentKernels, ///< Device can possibly execute multiple kernels concurrently. + hipDeviceAttributeConcurrentManagedAccess, ///< Device can coherently access managed memory concurrently with the CPU + hipDeviceAttributeCooperativeLaunch, ///< Support cooperative launch + hipDeviceAttributeCooperativeMultiDeviceLaunch, ///< Support cooperative launch on multiple devices + hipDeviceAttributeDeviceOverlap, ///< Cuda only. Device can concurrently copy memory and execute a kernel. + ///< Deprecated. Use instead asyncEngineCount. + hipDeviceAttributeDirectManagedMemAccessFromHost, ///< Host can directly access managed memory on + ///< the device without migration + hipDeviceAttributeGlobalL1CacheSupported, ///< Cuda only. Device supports caching globals in L1 + hipDeviceAttributeHostNativeAtomicSupported, ///< Cuda only. Link between the device and the host supports native atomic operations + hipDeviceAttributeIntegrated, ///< Device is integrated GPU + hipDeviceAttributeIsMultiGpuBoard, ///< Multiple GPU devices. + hipDeviceAttributeKernelExecTimeout, ///< Run time limit for kernels executed on the device + hipDeviceAttributeL2CacheSize, ///< Size of L2 cache in bytes. 0 if the device doesn't have L2 cache. + hipDeviceAttributeLocalL1CacheSupported, ///< caching locals in L1 is supported + hipDeviceAttributeLuid, ///< Cuda only. 8-byte locally unique identifier in 8 bytes. Undefined on TCC and non-Windows platforms + hipDeviceAttributeLuidDeviceNodeMask, ///< Cuda only. Luid device node mask. Undefined on TCC and non-Windows platforms + hipDeviceAttributeComputeCapabilityMajor, ///< Major compute capability version number. + hipDeviceAttributeManagedMemory, ///< Device supports allocating managed memory on this system + hipDeviceAttributeMaxBlocksPerMultiProcessor, ///< Cuda only. Max block size per multiprocessor + hipDeviceAttributeMaxBlockDimX, ///< Max block size in width. + hipDeviceAttributeMaxBlockDimY, ///< Max block size in height. + hipDeviceAttributeMaxBlockDimZ, ///< Max block size in depth. + hipDeviceAttributeMaxGridDimX, ///< Max grid size in width. + hipDeviceAttributeMaxGridDimY, ///< Max grid size in height. + hipDeviceAttributeMaxGridDimZ, ///< Max grid size in depth. + hipDeviceAttributeMaxSurface1D, ///< Maximum size of 1D surface. + hipDeviceAttributeMaxSurface1DLayered, ///< Cuda only. Maximum dimensions of 1D layered surface. + hipDeviceAttributeMaxSurface2D, ///< Maximum dimension (width, height) of 2D surface. + hipDeviceAttributeMaxSurface2DLayered, ///< Cuda only. Maximum dimensions of 2D layered surface. + hipDeviceAttributeMaxSurface3D, ///< Maximum dimension (width, height, depth) of 3D surface. + hipDeviceAttributeMaxSurfaceCubemap, ///< Cuda only. Maximum dimensions of Cubemap surface. + hipDeviceAttributeMaxSurfaceCubemapLayered, ///< Cuda only. Maximum dimension of Cubemap layered surface. + hipDeviceAttributeMaxTexture1DWidth, ///< Maximum size of 1D texture. + hipDeviceAttributeMaxTexture1DLayered, ///< Cuda only. Maximum dimensions of 1D layered texture. + hipDeviceAttributeMaxTexture1DLinear, ///< Maximum number of elements allocatable in a 1D linear texture. + ///< Use cudaDeviceGetTexture1DLinearMaxWidth() instead on Cuda. + hipDeviceAttributeMaxTexture1DMipmap, ///< Cuda only. Maximum size of 1D mipmapped texture. + hipDeviceAttributeMaxTexture2DWidth, ///< Maximum dimension width of 2D texture. + hipDeviceAttributeMaxTexture2DHeight, ///< Maximum dimension hight of 2D texture. + hipDeviceAttributeMaxTexture2DGather, ///< Cuda only. Maximum dimensions of 2D texture if gather operations performed. + hipDeviceAttributeMaxTexture2DLayered, ///< Cuda only. Maximum dimensions of 2D layered texture. + hipDeviceAttributeMaxTexture2DLinear, ///< Cuda only. Maximum dimensions (width, height, pitch) of 2D textures bound to pitched memory. + hipDeviceAttributeMaxTexture2DMipmap, ///< Cuda only. Maximum dimensions of 2D mipmapped texture. + hipDeviceAttributeMaxTexture3DWidth, ///< Maximum dimension width of 3D texture. + hipDeviceAttributeMaxTexture3DHeight, ///< Maximum dimension height of 3D texture. + hipDeviceAttributeMaxTexture3DDepth, ///< Maximum dimension depth of 3D texture. + hipDeviceAttributeMaxTexture3DAlt, ///< Cuda only. Maximum dimensions of alternate 3D texture. + hipDeviceAttributeMaxTextureCubemap, ///< Cuda only. Maximum dimensions of Cubemap texture + hipDeviceAttributeMaxTextureCubemapLayered, ///< Cuda only. Maximum dimensions of Cubemap layered texture. + hipDeviceAttributeMaxThreadsDim, ///< Maximum dimension of a block + hipDeviceAttributeMaxThreadsPerBlock, ///< Maximum number of threads per block. + hipDeviceAttributeMaxThreadsPerMultiProcessor, ///< Maximum resident threads per multiprocessor. + hipDeviceAttributeMaxPitch, ///< Maximum pitch in bytes allowed by memory copies + hipDeviceAttributeMemoryBusWidth, ///< Global memory bus width in bits. + hipDeviceAttributeMemoryClockRate, ///< Peak memory clock frequency in kilohertz. + hipDeviceAttributeComputeCapabilityMinor, ///< Minor compute capability version number. + hipDeviceAttributeMultiGpuBoardGroupID, ///< Cuda only. Unique ID of device group on the same multi-GPU board + hipDeviceAttributeMultiprocessorCount, ///< Number of multiprocessors on the device. + hipDeviceAttributeName, ///< Device name. + hipDeviceAttributePageableMemoryAccess, ///< Device supports coherently accessing pageable memory + ///< without calling hipHostRegister on it + hipDeviceAttributePageableMemoryAccessUsesHostPageTables, ///< Device accesses pageable memory via the host's page tables + hipDeviceAttributePciBusId, ///< PCI Bus ID. + hipDeviceAttributePciDeviceId, ///< PCI Device ID. + hipDeviceAttributePciDomainID, ///< PCI Domain ID. + hipDeviceAttributePersistingL2CacheMaxSize, ///< Cuda11 only. Maximum l2 persisting lines capacity in bytes + hipDeviceAttributeMaxRegistersPerBlock, ///< 32-bit registers available to a thread block. This number is shared + ///< by all thread blocks simultaneously resident on a multiprocessor. + hipDeviceAttributeMaxRegistersPerMultiprocessor, ///< 32-bit registers available per block. + hipDeviceAttributeReservedSharedMemPerBlock, ///< Cuda11 only. Shared memory reserved by CUDA driver per block. + hipDeviceAttributeMaxSharedMemoryPerBlock, ///< Maximum shared memory available per block in bytes. + hipDeviceAttributeSharedMemPerBlockOptin, ///< Cuda only. Maximum shared memory per block usable by special opt in. + hipDeviceAttributeSharedMemPerMultiprocessor, ///< Cuda only. Shared memory available per multiprocessor. + hipDeviceAttributeSingleToDoublePrecisionPerfRatio, ///< Cuda only. Performance ratio of single precision to double precision. + hipDeviceAttributeStreamPrioritiesSupported, ///< Cuda only. Whether to support stream priorities. + hipDeviceAttributeSurfaceAlignment, ///< Cuda only. Alignment requirement for surfaces + hipDeviceAttributeTccDriver, ///< Cuda only. Whether device is a Tesla device using TCC driver + hipDeviceAttributeTextureAlignment, ///< Alignment requirement for textures + hipDeviceAttributeTexturePitchAlignment, ///< Pitch alignment requirement for 2D texture references bound to pitched memory; + hipDeviceAttributeTotalConstantMemory, ///< Constant memory size in bytes. + hipDeviceAttributeTotalGlobalMem, ///< Global memory available on devicice. + hipDeviceAttributeUnifiedAddressing, ///< Cuda only. An unified address space shared with the host. + hipDeviceAttributeUuid, ///< Cuda only. Unique ID in 16 byte. + hipDeviceAttributeWarpSize, ///< Warp size in threads. + hipDeviceAttributeCudaCompatibleEnd = 9999, + hipDeviceAttributeAmdSpecificBegin = 10000, + hipDeviceAttributeClockInstructionRate = hipDeviceAttributeAmdSpecificBegin, ///< Frequency in khz of the timer used by the device-side "clock*" + hipDeviceAttributeArch, ///< Device architecture + hipDeviceAttributeMaxSharedMemoryPerMultiprocessor, ///< Maximum Shared Memory PerMultiprocessor. + hipDeviceAttributeGcnArch, ///< Device gcn architecture + hipDeviceAttributeGcnArchName, ///< Device gcnArch name in 256 bytes + hipDeviceAttributeHdpMemFlushCntl, ///< Address of the HDP_MEM_COHERENCY_FLUSH_CNTL register + hipDeviceAttributeHdpRegFlushCntl, ///< Address of the HDP_REG_COHERENCY_FLUSH_CNTL register + hipDeviceAttributeCooperativeMultiDeviceUnmatchedFunc, ///< Supports cooperative launch on multiple + ///< devices with unmatched functions + hipDeviceAttributeCooperativeMultiDeviceUnmatchedGridDim, ///< Supports cooperative launch on multiple + ///< devices with unmatched grid dimensions + hipDeviceAttributeCooperativeMultiDeviceUnmatchedBlockDim, ///< Supports cooperative launch on multiple + ///< devices with unmatched block dimensions + hipDeviceAttributeCooperativeMultiDeviceUnmatchedSharedMem, ///< Supports cooperative launch on multiple + ///< devices with unmatched shared memories + hipDeviceAttributeIsLargeBar, ///< Whether it is LargeBar + hipDeviceAttributeAsicRevision, ///< Revision of the GPU in this device + hipDeviceAttributeCanUseStreamWaitValue, ///< '1' if Device supports hipStreamWaitValue32() and + ///< hipStreamWaitValue64() , '0' otherwise. + hipDeviceAttributeAmdSpecificEnd = 19999, + hipDeviceAttributeVendorSpecificBegin = 20000, + // Extended attributes for vendors +} hipDeviceAttribute_t; + +typedef struct HIPdevprop_st { + int maxThreadsPerBlock; + int maxThreadsDim[3]; + int maxGridSize[3]; + int sharedMemPerBlock; + int totalConstantMemory; + int SIMDWidth; + int memPitch; + int regsPerBlock; + int clockRate; + int textureAlign; +} HIPdevprop; + +typedef struct { + // 32-bit Atomics + unsigned hasGlobalInt32Atomics : 1; ///< 32-bit integer atomics for global memory. + unsigned hasGlobalFloatAtomicExch : 1; ///< 32-bit float atomic exch for global memory. + unsigned hasSharedInt32Atomics : 1; ///< 32-bit integer atomics for shared memory. + unsigned hasSharedFloatAtomicExch : 1; ///< 32-bit float atomic exch for shared memory. + unsigned hasFloatAtomicAdd : 1; ///< 32-bit float atomic add in global and shared memory. + + // 64-bit Atomics + unsigned hasGlobalInt64Atomics : 1; ///< 64-bit integer atomics for global memory. + unsigned hasSharedInt64Atomics : 1; ///< 64-bit integer atomics for shared memory. + + // Doubles + unsigned hasDoubles : 1; ///< Double-precision floating point. + + // Warp cross-lane operations + unsigned hasWarpVote : 1; ///< Warp vote instructions (__any, __all). + unsigned hasWarpBallot : 1; ///< Warp ballot instructions (__ballot). + unsigned hasWarpShuffle : 1; ///< Warp shuffle operations. (__shfl_*). + unsigned hasFunnelShift : 1; ///< Funnel two words into one with shift&mask caps. + + // Sync + unsigned hasThreadFenceSystem : 1; ///< __threadfence_system. + unsigned hasSyncThreadsExt : 1; ///< __syncthreads_count, syncthreads_and, syncthreads_or. + + // Misc + unsigned hasSurfaceFuncs : 1; ///< Surface functions. + unsigned has3dGrid : 1; ///< Grid and group dims are 3D (rather than 2D). + unsigned hasDynamicParallelism : 1; ///< Dynamic parallelism. +} hipDeviceArch_t; + +typedef struct hipDeviceProp_t { + char name[256]; ///< Device name. + size_t totalGlobalMem; ///< Size of global memory region (in bytes). + size_t sharedMemPerBlock; ///< Size of shared memory region (in bytes). + int regsPerBlock; ///< Registers per block. + int warpSize; ///< Warp size. + int maxThreadsPerBlock; ///< Max work items per work group or workgroup max size. + int maxThreadsDim[3]; ///< Max number of threads in each dimension (XYZ) of a block. + int maxGridSize[3]; ///< Max grid dimensions (XYZ). + int clockRate; ///< Max clock frequency of the multiProcessors in khz. + int memoryClockRate; ///< Max global memory clock frequency in khz. + int memoryBusWidth; ///< Global memory bus width in bits. + size_t totalConstMem; ///< Size of shared memory region (in bytes). + int major; ///< Major compute capability. On HCC, this is an approximation and features may + ///< differ from CUDA CC. See the arch feature flags for portable ways to query + ///< feature caps. + int minor; ///< Minor compute capability. On HCC, this is an approximation and features may + ///< differ from CUDA CC. See the arch feature flags for portable ways to query + ///< feature caps. + int multiProcessorCount; ///< Number of multi-processors (compute units). + int l2CacheSize; ///< L2 cache size. + int maxThreadsPerMultiProcessor; ///< Maximum resident threads per multi-processor. + int computeMode; ///< Compute mode. + int clockInstructionRate; ///< Frequency in khz of the timer used by the device-side "clock*" + ///< instructions. New for HIP. + hipDeviceArch_t arch; ///< Architectural feature flags. New for HIP. + int concurrentKernels; ///< Device can possibly execute multiple kernels concurrently. + int pciDomainID; ///< PCI Domain ID + int pciBusID; ///< PCI Bus ID. + int pciDeviceID; ///< PCI Device ID. + size_t maxSharedMemoryPerMultiProcessor; ///< Maximum Shared Memory Per Multiprocessor. + int isMultiGpuBoard; ///< 1 if device is on a multi-GPU board, 0 if not. + int canMapHostMemory; ///< Check whether HIP can map host memory + int gcnArch; ///< DEPRECATED: use gcnArchName instead + char gcnArchName[256]; ///< AMD GCN Arch Name. + int integrated; ///< APU vs dGPU + int cooperativeLaunch; ///< HIP device supports cooperative launch + int cooperativeMultiDeviceLaunch; ///< HIP device supports cooperative launch on multiple devices + int maxTexture1DLinear; ///< Maximum size for 1D textures bound to linear memory + int maxTexture1D; ///< Maximum number of elements in 1D images + int maxTexture2D[2]; ///< Maximum dimensions (width, height) of 2D images, in image elements + int maxTexture3D[3]; ///< Maximum dimensions (width, height, depth) of 3D images, in image elements + unsigned int* hdpMemFlushCntl; ///< Addres of HDP_MEM_COHERENCY_FLUSH_CNTL register + unsigned int* hdpRegFlushCntl; ///< Addres of HDP_REG_COHERENCY_FLUSH_CNTL register + size_t memPitch; /// +#include +#include +#include +#include + +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# define VC_EXTRALEAN +# include + +/* Utility macros. */ + +typedef HMODULE DynamicLibrary; + +# define dynamic_library_open(path) LoadLibraryA(path) +# define dynamic_library_close(lib) FreeLibrary(lib) +# define dynamic_library_find(lib, symbol) GetProcAddress(lib, symbol) +#else +# include + +typedef void* DynamicLibrary; + +# define dynamic_library_open(path) dlopen(path, RTLD_NOW) +# define dynamic_library_close(lib) dlclose(lib) +# define dynamic_library_find(lib, symbol) dlsym(lib, symbol) +#endif + +#define _LIBRARY_FIND_CHECKED(lib, name) \ + name = (t##name *)dynamic_library_find(lib, #name); \ + assert(name); + +#define _LIBRARY_FIND(lib, name) \ + name = (t##name *)dynamic_library_find(lib, #name); + +#define HIP_LIBRARY_FIND_CHECKED(name) \ + _LIBRARY_FIND_CHECKED(hip_lib, name) +#define HIP_LIBRARY_FIND(name) _LIBRARY_FIND(hip_lib, name) + + +static DynamicLibrary hip_lib; + +/* Function definitions. */ +thipGetErrorName *hipGetErrorName; +thipInit *hipInit; +thipDriverGetVersion *hipDriverGetVersion; +thipGetDevice *hipGetDevice; +thipGetDeviceCount *hipGetDeviceCount; +thipGetDeviceProperties *hipGetDeviceProperties; +thipDeviceGetName *hipDeviceGetName; +thipDeviceGetAttribute *hipDeviceGetAttribute; +thipDeviceComputeCapability *hipDeviceComputeCapability; +thipDevicePrimaryCtxRetain *hipDevicePrimaryCtxRetain; +thipDevicePrimaryCtxRelease *hipDevicePrimaryCtxRelease; +thipDevicePrimaryCtxSetFlags *hipDevicePrimaryCtxSetFlags; +thipDevicePrimaryCtxGetState *hipDevicePrimaryCtxGetState; +thipDevicePrimaryCtxReset *hipDevicePrimaryCtxReset; +thipCtxCreate *hipCtxCreate; +thipCtxDestroy *hipCtxDestroy; +thipCtxPushCurrent *hipCtxPushCurrent; +thipCtxPopCurrent *hipCtxPopCurrent; +thipCtxSetCurrent *hipCtxSetCurrent; +thipCtxGetCurrent *hipCtxGetCurrent; +thipCtxGetDevice *hipCtxGetDevice; +thipCtxGetFlags *hipCtxGetFlags; +thipCtxSynchronize *hipCtxSynchronize; +thipDeviceSynchronize *hipDeviceSynchronize; +thipCtxGetCacheConfig *hipCtxGetCacheConfig; +thipCtxSetCacheConfig *hipCtxSetCacheConfig; +thipCtxGetSharedMemConfig *hipCtxGetSharedMemConfig; +thipCtxSetSharedMemConfig *hipCtxSetSharedMemConfig; +thipCtxGetApiVersion *hipCtxGetApiVersion; +thipModuleLoad *hipModuleLoad; +thipModuleLoadData *hipModuleLoadData; +thipModuleLoadDataEx *hipModuleLoadDataEx; +thipModuleUnload *hipModuleUnload; +thipModuleGetFunction *hipModuleGetFunction; +thipModuleGetGlobal *hipModuleGetGlobal; +thipModuleGetTexRef *hipModuleGetTexRef; +thipMemGetInfo *hipMemGetInfo; +thipMalloc *hipMalloc; +thipMemAllocPitch *hipMemAllocPitch; +thipFree *hipFree; +thipMemGetAddressRange *hipMemGetAddressRange; +thipHostMalloc *hipHostMalloc; +thipHostFree *hipHostFree; +thipHostGetDevicePointer *hipHostGetDevicePointer; +thipHostGetFlags *hipHostGetFlags; +thipMallocManaged *hipMallocManaged; +thipDeviceGetByPCIBusId *hipDeviceGetByPCIBusId; +thipDeviceGetPCIBusId *hipDeviceGetPCIBusId; +thipMemcpyPeer *hipMemcpyPeer; +thipMemcpyHtoD *hipMemcpyHtoD; +thipMemcpyDtoH *hipMemcpyDtoH; +thipMemcpyDtoD *hipMemcpyDtoD; +thipDrvMemcpy2DUnaligned *hipDrvMemcpy2DUnaligned; +thipMemcpyParam2D *hipMemcpyParam2D; +thipDrvMemcpy3D *hipDrvMemcpy3D; +thipMemcpyHtoDAsync *hipMemcpyHtoDAsync; +thipMemcpyDtoHAsync *hipMemcpyDtoHAsync; +thipMemcpyParam2DAsync *hipMemcpyParam2DAsync; +thipDrvMemcpy3DAsync *hipDrvMemcpy3DAsync; +thipMemsetD8 *hipMemsetD8; +thipMemsetD16 *hipMemsetD16; +thipMemsetD32 *hipMemsetD32; +thipMemsetD8Async *hipMemsetD8Async; +thipMemsetD16Async *hipMemsetD16Async; +thipMemsetD32Async *hipMemsetD32Async; +thipArrayCreate *hipArrayCreate; +thipArrayDestroy *hipArrayDestroy; +thipArray3DCreate *hipArray3DCreate; +thipStreamCreateWithFlags *hipStreamCreateWithFlags; +thipStreamCreateWithPriority *hipStreamCreateWithPriority; +thipStreamGetPriority *hipStreamGetPriority; +thipStreamGetFlags *hipStreamGetFlags; +thipStreamWaitEvent *hipStreamWaitEvent; +thipStreamAddCallback *hipStreamAddCallback; +thipStreamQuery *hipStreamQuery; +thipStreamSynchronize *hipStreamSynchronize; +thipStreamDestroy *hipStreamDestroy; +thipEventCreateWithFlags *hipEventCreateWithFlags; +thipEventRecord *hipEventRecord; +thipEventQuery *hipEventQuery; +thipEventSynchronize *hipEventSynchronize; +thipEventDestroy *hipEventDestroy; +thipEventElapsedTime *hipEventElapsedTime; +thipFuncGetAttribute *hipFuncGetAttribute; +thipFuncSetCacheConfig *hipFuncSetCacheConfig; +thipModuleLaunchKernel *hipModuleLaunchKernel; +thipDrvOccupancyMaxActiveBlocksPerMultiprocessor *hipDrvOccupancyMaxActiveBlocksPerMultiprocessor; +thipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags *hipDrvOccupancyMaxActiveBlocksPerMultiprocessorWithFlags; +thipModuleOccupancyMaxPotentialBlockSize *hipModuleOccupancyMaxPotentialBlockSize; +thipTexRefSetArray *hipTexRefSetArray; +thipTexRefSetAddress *hipTexRefSetAddress; +thipTexRefSetAddress2D *hipTexRefSetAddress2D; +thipTexRefSetFormat *hipTexRefSetFormat; +thipTexRefSetAddressMode *hipTexRefSetAddressMode; +thipTexRefSetFilterMode *hipTexRefSetFilterMode; +thipTexRefSetFlags *hipTexRefSetFlags; +thipTexRefGetAddress *hipTexRefGetAddress; +thipTexRefGetArray *hipTexRefGetArray; +thipTexRefGetAddressMode *hipTexRefGetAddressMode; +thipTexObjectCreate *hipTexObjectCreate; +thipTexObjectDestroy *hipTexObjectDestroy; +thipDeviceCanAccessPeer *hipDeviceCanAccessPeer; + +thipCtxEnablePeerAccess *hipCtxEnablePeerAccess; +thipCtxDisablePeerAccess *hipCtxDisablePeerAccess; +thipDeviceGetP2PAttribute *hipDeviceGetP2PAttribute; +thipGraphicsUnregisterResource *hipGraphicsUnregisterResource; +thipGraphicsMapResources *hipGraphicsMapResources; +thipGraphicsUnmapResources *hipGraphicsUnmapResources; +thipGraphicsResourceGetMappedPointer *hipGraphicsResourceGetMappedPointer; + +thipGraphicsGLRegisterBuffer *hipGraphicsGLRegisterBuffer; +thipGLGetDevices *hipGLGetDevices; + +thiprtcGetErrorString* hiprtcGetErrorString; +thiprtcAddNameExpression* hiprtcAddNameExpression; +thiprtcCompileProgram* hiprtcCompileProgram; +thiprtcCreateProgram* hiprtcCreateProgram; +thiprtcDestroyProgram* hiprtcDestroyProgram; +thiprtcGetLoweredName* hiprtcGetLoweredName; +thiprtcGetProgramLog* hiprtcGetProgramLog; +thiprtcGetProgramLogSize* hiprtcGetProgramLogSize; +thiprtcGetCode* hiprtcGetCode; +thiprtcGetCodeSize* hiprtcGetCodeSize; + + + +static DynamicLibrary dynamic_library_open_find(const char **paths) { + int i = 0; + while (paths[i] != NULL) { + DynamicLibrary lib = dynamic_library_open(paths[i]); + if (lib != NULL) { + return lib; + } + ++i; + } + return NULL; +} + +/* Implementation function. */ +static void hipewHipExit(void) { + if (hip_lib != NULL) { + /* Ignore errors. */ + dynamic_library_close(hip_lib); + hip_lib = NULL; + } +} + +static int hipewHipInit(void) { + /* Library paths. */ +#ifdef _WIN32 + /* Expected in c:/windows/system or similar, no path needed. */ + const char *hip_paths[] = {"amdhip64.dll", NULL}; +#elif defined(__APPLE__) + /* Default installation path. */ + const char *hip_paths[] = {"", NULL}; +#else + const char *hip_paths[] = {"/opt/rocm/hip/lib/libamdhip64.so", NULL}; +#endif + static int initialized = 0; + static int result = 0; + int error, driver_version; + + if (initialized) { + return result; + } + + initialized = 1; + + error = atexit(hipewHipExit); + if (error) { + result = HIPEW_ERROR_ATEXIT_FAILED; + return result; + } + + /* Load library. */ + hip_lib = dynamic_library_open_find(hip_paths); + + if (hip_lib == NULL) { + result = HIPEW_ERROR_OPEN_FAILED; + return result; + } + + /* Fetch all function pointers. */ + HIP_LIBRARY_FIND_CHECKED(hipGetErrorName); + HIP_LIBRARY_FIND_CHECKED(hipInit); + HIP_LIBRARY_FIND_CHECKED(hipDriverGetVersion); + HIP_LIBRARY_FIND_CHECKED(hipGetDevice); + HIP_LIBRARY_FIND_CHECKED(hipGetDeviceCount); + HIP_LIBRARY_FIND_CHECKED(hipGetDeviceProperties); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetName); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetAttribute); + HIP_LIBRARY_FIND_CHECKED(hipDeviceComputeCapability); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxRetain); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxRelease); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxSetFlags); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxGetState); + HIP_LIBRARY_FIND_CHECKED(hipDevicePrimaryCtxReset); + HIP_LIBRARY_FIND_CHECKED(hipCtxCreate); + HIP_LIBRARY_FIND_CHECKED(hipCtxDestroy); + HIP_LIBRARY_FIND_CHECKED(hipCtxPushCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxPopCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxSetCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetCurrent); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetDevice); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetFlags); + HIP_LIBRARY_FIND_CHECKED(hipCtxSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipDeviceSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetCacheConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxSetCacheConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetSharedMemConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxSetSharedMemConfig); + HIP_LIBRARY_FIND_CHECKED(hipCtxGetApiVersion); + HIP_LIBRARY_FIND_CHECKED(hipModuleLoad); + HIP_LIBRARY_FIND_CHECKED(hipModuleLoadData); + HIP_LIBRARY_FIND_CHECKED(hipModuleLoadDataEx); + HIP_LIBRARY_FIND_CHECKED(hipModuleUnload); + HIP_LIBRARY_FIND_CHECKED(hipModuleGetFunction); + HIP_LIBRARY_FIND_CHECKED(hipModuleGetGlobal); + HIP_LIBRARY_FIND_CHECKED(hipModuleGetTexRef); + HIP_LIBRARY_FIND_CHECKED(hipMemGetInfo); + HIP_LIBRARY_FIND_CHECKED(hipMalloc); + HIP_LIBRARY_FIND_CHECKED(hipMemAllocPitch); + HIP_LIBRARY_FIND_CHECKED(hipFree); + HIP_LIBRARY_FIND_CHECKED(hipMemGetAddressRange); + HIP_LIBRARY_FIND_CHECKED(hipHostMalloc); + HIP_LIBRARY_FIND_CHECKED(hipHostFree); + HIP_LIBRARY_FIND_CHECKED(hipHostGetDevicePointer); + HIP_LIBRARY_FIND_CHECKED(hipHostGetFlags); + HIP_LIBRARY_FIND_CHECKED(hipMallocManaged); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetByPCIBusId); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetPCIBusId); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyPeer); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyHtoD); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoH); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoD); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyParam2D); + HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy3D); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyHtoDAsync); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyDtoHAsync); + HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy2DUnaligned); + HIP_LIBRARY_FIND_CHECKED(hipMemcpyParam2DAsync); + HIP_LIBRARY_FIND_CHECKED(hipDrvMemcpy3DAsync); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD8); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD16); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD32); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD8Async); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD16Async); + HIP_LIBRARY_FIND_CHECKED(hipMemsetD32Async); + HIP_LIBRARY_FIND_CHECKED(hipArrayCreate); + HIP_LIBRARY_FIND_CHECKED(hipArrayDestroy); + HIP_LIBRARY_FIND_CHECKED(hipArray3DCreate); + HIP_LIBRARY_FIND_CHECKED(hipStreamCreateWithFlags); + HIP_LIBRARY_FIND_CHECKED(hipStreamCreateWithPriority); + HIP_LIBRARY_FIND_CHECKED(hipStreamGetPriority); + HIP_LIBRARY_FIND_CHECKED(hipStreamGetFlags); + HIP_LIBRARY_FIND_CHECKED(hipStreamWaitEvent); + HIP_LIBRARY_FIND_CHECKED(hipStreamAddCallback); + HIP_LIBRARY_FIND_CHECKED(hipStreamQuery); + HIP_LIBRARY_FIND_CHECKED(hipStreamSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipStreamDestroy); + HIP_LIBRARY_FIND_CHECKED(hipEventCreateWithFlags); + HIP_LIBRARY_FIND_CHECKED(hipEventRecord); + HIP_LIBRARY_FIND_CHECKED(hipEventQuery); + HIP_LIBRARY_FIND_CHECKED(hipEventSynchronize); + HIP_LIBRARY_FIND_CHECKED(hipEventDestroy); + HIP_LIBRARY_FIND_CHECKED(hipEventElapsedTime); + HIP_LIBRARY_FIND_CHECKED(hipFuncGetAttribute); + HIP_LIBRARY_FIND_CHECKED(hipFuncSetCacheConfig); + HIP_LIBRARY_FIND_CHECKED(hipModuleLaunchKernel); + HIP_LIBRARY_FIND_CHECKED(hipModuleOccupancyMaxPotentialBlockSize); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetArray); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddress); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddress2D); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFormat); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetAddressMode); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFilterMode); + HIP_LIBRARY_FIND_CHECKED(hipTexRefSetFlags); + HIP_LIBRARY_FIND_CHECKED(hipTexRefGetAddress); + HIP_LIBRARY_FIND_CHECKED(hipTexRefGetAddressMode); + HIP_LIBRARY_FIND_CHECKED(hipTexObjectCreate); + HIP_LIBRARY_FIND_CHECKED(hipTexObjectDestroy); + HIP_LIBRARY_FIND_CHECKED(hipDeviceCanAccessPeer); + HIP_LIBRARY_FIND_CHECKED(hipCtxEnablePeerAccess); + HIP_LIBRARY_FIND_CHECKED(hipCtxDisablePeerAccess); + HIP_LIBRARY_FIND_CHECKED(hipDeviceGetP2PAttribute); +#ifdef _WIN32 + HIP_LIBRARY_FIND_CHECKED(hipGraphicsUnregisterResource); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsMapResources); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsUnmapResources); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsResourceGetMappedPointer); + HIP_LIBRARY_FIND_CHECKED(hipGraphicsGLRegisterBuffer); + HIP_LIBRARY_FIND_CHECKED(hipGLGetDevices); +#endif + HIP_LIBRARY_FIND_CHECKED(hiprtcGetErrorString); + HIP_LIBRARY_FIND_CHECKED(hiprtcAddNameExpression); + HIP_LIBRARY_FIND_CHECKED(hiprtcCompileProgram); + HIP_LIBRARY_FIND_CHECKED(hiprtcCreateProgram); + HIP_LIBRARY_FIND_CHECKED(hiprtcDestroyProgram); + HIP_LIBRARY_FIND_CHECKED(hiprtcGetLoweredName); + HIP_LIBRARY_FIND_CHECKED(hiprtcGetProgramLog); + HIP_LIBRARY_FIND_CHECKED(hiprtcGetProgramLogSize); + HIP_LIBRARY_FIND_CHECKED(hiprtcGetCode); + HIP_LIBRARY_FIND_CHECKED(hiprtcGetCodeSize); + result = HIPEW_SUCCESS; + return result; +} + + + +int hipewInit(hipuint32_t flags) { + int result = HIPEW_SUCCESS; + + if (flags & HIPEW_INIT_HIP) { + result = hipewHipInit(); + if (result != HIPEW_SUCCESS) { + return result; + } + } + + return result; +} + + +const char *hipewErrorString(hipError_t result) { + switch (result) { + case hipSuccess: return "No errors"; + case hipErrorInvalidValue: return "Invalid value"; + case hipErrorOutOfMemory: return "Out of memory"; + case hipErrorNotInitialized: return "Driver not initialized"; + case hipErrorDeinitialized: return "Driver deinitialized"; + case hipErrorProfilerDisabled: return "Profiler disabled"; + case hipErrorProfilerNotInitialized: return "Profiler not initialized"; + case hipErrorProfilerAlreadyStarted: return "Profiler already started"; + case hipErrorProfilerAlreadyStopped: return "Profiler already stopped"; + case hipErrorNoDevice: return "No HIP-capable device available"; + case hipErrorInvalidDevice: return "Invalid device"; + case hipErrorInvalidImage: return "Invalid kernel image"; + case hipErrorInvalidContext: return "Invalid context"; + case hipErrorContextAlreadyCurrent: return "Context already current"; + case hipErrorMapFailed: return "Map failed"; + case hipErrorUnmapFailed: return "Unmap failed"; + case hipErrorArrayIsMapped: return "Array is mapped"; + case hipErrorAlreadyMapped: return "Already mapped"; + case hipErrorNoBinaryForGpu: return "No binary for GPU"; + case hipErrorAlreadyAcquired: return "Already acquired"; + case hipErrorNotMapped: return "Not mapped"; + case hipErrorNotMappedAsArray: return "Mapped resource not available for access as an array"; + case hipErrorNotMappedAsPointer: return "Mapped resource not available for access as a pointer"; + case hipErrorECCNotCorrectable: return "Uncorrectable ECC error detected"; + case hipErrorUnsupportedLimit: return "hipLimit_t not supported by device"; + case hipErrorContextAlreadyInUse: return "Context already in use"; + case hipErrorPeerAccessUnsupported: return "Peer access unsupported"; + case hipErrorInvalidKernelFile: return "Invalid ptx"; + case hipErrorInvalidGraphicsContext: return "Invalid graphics context"; + case hipErrorInvalidSource: return "Invalid source"; + case hipErrorFileNotFound: return "File not found"; + case hipErrorSharedObjectSymbolNotFound: return "Link to a shared object failed to resolve"; + case hipErrorSharedObjectInitFailed: return "Shared object initialization failed"; + case hipErrorOperatingSystem: return "Operating system"; + case hipErrorInvalidHandle: return "Invalid handle"; + case hipErrorNotFound: return "Not found"; + case hipErrorNotReady: return "HIP not ready"; + case hipErrorIllegalAddress: return "Illegal address"; + case hipErrorLaunchOutOfResources: return "Launch exceeded resources"; + case hipErrorLaunchTimeOut: return "Launch exceeded timeout"; + case hipErrorPeerAccessAlreadyEnabled: return "Peer access already enabled"; + case hipErrorPeerAccessNotEnabled: return "Peer access not enabled"; + case hipErrorSetOnActiveProcess: return "Primary context active"; + case hipErrorAssert: return "Assert"; + case hipErrorHostMemoryAlreadyRegistered: return "Host memory already registered"; + case hipErrorHostMemoryNotRegistered: return "Host memory not registered"; + case hipErrorLaunchFailure: return "Launch failed"; + case hipErrorCooperativeLaunchTooLarge: return "Cooperative launch too large"; + case hipErrorNotSupported: return "Not supported"; + case hipErrorUnknown: return "Unknown error"; + default: return "Unknown HIP error value"; + } +} + +static void path_join(const char *path1, + const char *path2, + int maxlen, + char *result) { +#if defined(WIN32) || defined(_WIN32) + const char separator = '\\'; +#else + const char separator = '/'; +#endif + int n = snprintf(result, maxlen, "%s%c%s", path1, separator, path2); + if (n != -1 && n < maxlen) { + result[n] = '\0'; + } + else { + result[maxlen - 1] = '\0'; + } +} + +static int path_exists(const char *path) { + struct stat st; + if (stat(path, &st)) { + return 0; + } + return 1; +} + +const char *hipewCompilerPath(void) { + #ifdef _WIN32 + const char *hipPath = getenv("HIP_ROCCLR_HOME"); + const char *windowsCommand = "perl "; + const char *executable = "bin/hipcc"; + + static char hipcc[65536]; + static char finalCommand[65536]; + if(hipPath) { + path_join(hipPath, executable, sizeof(hipcc), hipcc); + if(path_exists(hipcc)) { + snprintf(finalCommand, sizeof(hipcc), "%s %s", windowsCommand, hipcc); + return finalCommand; + } else { + printf("Could not find hipcc. Make sure HIP_ROCCLR_HOME points to the directory holding /bin/hipcc"); + } + } + #else + const char *hipPath = "opt/rocm/hip/bin"; + const char *executable = "hipcc"; + + static char hipcc[65536]; + if(hipPath) { + path_join(hipPath, executable, sizeof(hipcc), hipcc); + if(path_exists(hipcc)){ + return hipcc; + } + } + #endif + + { +#ifdef _WIN32 + FILE *handle = popen("where hipcc", "r"); +#else + FILE *handle = popen("which hipcc", "r"); +#endif + if (handle) { + char buffer[4096] = {0}; + int len = fread(buffer, 1, sizeof(buffer) - 1, handle); + buffer[len] = '\0'; + pclose(handle); + if (buffer[0]) { + return "hipcc"; + } + } + } + + return NULL; +} + +int hipewCompilerVersion(void) { + const char *path = hipewCompilerPath(); + const char *marker = "Hip compilation tools, release "; + FILE *pipe; + int major, minor; + char *versionstr; + char buf[128]; + char output[65536] = "\0"; + char command[65536] = "\0"; + + if (path == NULL) { + return 0; + } + + /* get --version output */ + strcat(command, "\""); + strncat(command, path, sizeof(command) - 1); + strncat(command, "\" --version", sizeof(command) - strlen(path) - 1); + pipe = popen(command, "r"); + if (!pipe) { + fprintf(stderr, "HIP: failed to run compiler to retrieve version"); + return 0; + } + + while (!feof(pipe)) { + if (fgets(buf, sizeof(buf), pipe) != NULL) { + strncat(output, buf, sizeof(output) - strlen(output) - 1); + } + } + + pclose(pipe); + return 40; +} diff --git a/extern/json/README.blender b/extern/json/README.blender new file mode 100644 index 00000000000..b9d8b02d87e --- /dev/null +++ b/extern/json/README.blender @@ -0,0 +1,5 @@ +Project: JSON +URL: https://github.com/nlohmann/json/ +License: MIT License +Upstream version: 3.10.2 +Local modifications: None diff --git a/extern/json/include/json.hpp b/extern/json/include/json.hpp new file mode 100644 index 00000000000..8959265daea --- /dev/null +++ b/extern/json/include/json.hpp @@ -0,0 +1,26640 @@ +/* + __ _____ _____ _____ + __| | __| | | | JSON for Modern C++ +| | |__ | | | | | | version 3.10.2 +|_____|_____|_____|_|___| https://github.com/nlohmann/json + +Licensed under the MIT License . +SPDX-License-Identifier: MIT +Copyright (c) 2013-2019 Niels Lohmann . + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#ifndef INCLUDE_NLOHMANN_JSON_HPP_ +#define INCLUDE_NLOHMANN_JSON_HPP_ + +#define NLOHMANN_JSON_VERSION_MAJOR 3 +#define NLOHMANN_JSON_VERSION_MINOR 10 +#define NLOHMANN_JSON_VERSION_PATCH 2 + +#include // all_of, find, for_each +#include // nullptr_t, ptrdiff_t, size_t +#include // hash, less +#include // initializer_list +#ifndef JSON_NO_IO + #include // istream, ostream +#endif // JSON_NO_IO +#include // random_access_iterator_tag +#include // unique_ptr +#include // accumulate +#include // string, stoi, to_string +#include // declval, forward, move, pair, swap +#include // vector + +// #include + + +#include +#include + +// #include + + +#include // transform +#include // array +#include // forward_list +#include // inserter, front_inserter, end +#include // map +#include // string +#include // tuple, make_tuple +#include // is_arithmetic, is_same, is_enum, underlying_type, is_convertible +#include // unordered_map +#include // pair, declval +#include // valarray + +// #include + + +#include // exception +#include // runtime_error +#include // to_string +#include // vector + +// #include + + +#include // array +#include // size_t +#include // uint8_t +#include // string + +namespace nlohmann +{ +namespace detail +{ +/////////////////////////// +// JSON type enumeration // +/////////////////////////// + +/*! +@brief the JSON type enumeration + +This enumeration collects the different JSON types. It is internally used to +distinguish the stored values, and the functions @ref basic_json::is_null(), +@ref basic_json::is_object(), @ref basic_json::is_array(), +@ref basic_json::is_string(), @ref basic_json::is_boolean(), +@ref basic_json::is_number() (with @ref basic_json::is_number_integer(), +@ref basic_json::is_number_unsigned(), and @ref basic_json::is_number_float()), +@ref basic_json::is_discarded(), @ref basic_json::is_primitive(), and +@ref basic_json::is_structured() rely on it. + +@note There are three enumeration entries (number_integer, number_unsigned, and +number_float), because the library distinguishes these three types for numbers: +@ref basic_json::number_unsigned_t is used for unsigned integers, +@ref basic_json::number_integer_t is used for signed integers, and +@ref basic_json::number_float_t is used for floating-point numbers or to +approximate integers which do not fit in the limits of their respective type. + +@sa see @ref basic_json::basic_json(const value_t value_type) -- create a JSON +value with the default value for a given type + +@since version 1.0.0 +*/ +enum class value_t : std::uint8_t +{ + null, ///< null value + object, ///< object (unordered set of name/value pairs) + array, ///< array (ordered collection of values) + string, ///< string value + boolean, ///< boolean value + number_integer, ///< number value (signed integer) + number_unsigned, ///< number value (unsigned integer) + number_float, ///< number value (floating-point) + binary, ///< binary array (ordered collection of bytes) + discarded ///< discarded by the parser callback function +}; + +/*! +@brief comparison operator for JSON types + +Returns an ordering that is similar to Python: +- order: null < boolean < number < object < array < string < binary +- furthermore, each type is not smaller than itself +- discarded values are not comparable +- binary is represented as a b"" string in python and directly comparable to a + string; however, making a binary array directly comparable with a string would + be surprising behavior in a JSON file. + +@since version 1.0.0 +*/ +inline bool operator<(const value_t lhs, const value_t rhs) noexcept +{ + static constexpr std::array order = {{ + 0 /* null */, 3 /* object */, 4 /* array */, 5 /* string */, + 1 /* boolean */, 2 /* integer */, 2 /* unsigned */, 2 /* float */, + 6 /* binary */ + } + }; + + const auto l_index = static_cast(lhs); + const auto r_index = static_cast(rhs); + return l_index < order.size() && r_index < order.size() && order[l_index] < order[r_index]; +} +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +// #include + + +#include // pair +// #include + + +/* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + +#if !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < 15) +#if defined(JSON_HEDLEY_VERSION) + #undef JSON_HEDLEY_VERSION +#endif +#define JSON_HEDLEY_VERSION 15 + +#if defined(JSON_HEDLEY_STRINGIFY_EX) + #undef JSON_HEDLEY_STRINGIFY_EX +#endif +#define JSON_HEDLEY_STRINGIFY_EX(x) #x + +#if defined(JSON_HEDLEY_STRINGIFY) + #undef JSON_HEDLEY_STRINGIFY +#endif +#define JSON_HEDLEY_STRINGIFY(x) JSON_HEDLEY_STRINGIFY_EX(x) + +#if defined(JSON_HEDLEY_CONCAT_EX) + #undef JSON_HEDLEY_CONCAT_EX +#endif +#define JSON_HEDLEY_CONCAT_EX(a,b) a##b + +#if defined(JSON_HEDLEY_CONCAT) + #undef JSON_HEDLEY_CONCAT +#endif +#define JSON_HEDLEY_CONCAT(a,b) JSON_HEDLEY_CONCAT_EX(a,b) + +#if defined(JSON_HEDLEY_CONCAT3_EX) + #undef JSON_HEDLEY_CONCAT3_EX +#endif +#define JSON_HEDLEY_CONCAT3_EX(a,b,c) a##b##c + +#if defined(JSON_HEDLEY_CONCAT3) + #undef JSON_HEDLEY_CONCAT3 +#endif +#define JSON_HEDLEY_CONCAT3(a,b,c) JSON_HEDLEY_CONCAT3_EX(a,b,c) + +#if defined(JSON_HEDLEY_VERSION_ENCODE) + #undef JSON_HEDLEY_VERSION_ENCODE +#endif +#define JSON_HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision)) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MAJOR) + #undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_MINOR) + #undef JSON_HEDLEY_VERSION_DECODE_MINOR +#endif +#define JSON_HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + +#if defined(JSON_HEDLEY_VERSION_DECODE_REVISION) + #undef JSON_HEDLEY_VERSION_DECODE_REVISION +#endif +#define JSON_HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000) + +#if defined(JSON_HEDLEY_GNUC_VERSION) + #undef JSON_HEDLEY_GNUC_VERSION +#endif +#if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#elif defined(__GNUC__) + #define JSON_HEDLEY_GNUC_VERSION JSON_HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) +#endif + +#if defined(JSON_HEDLEY_GNUC_VERSION_CHECK) + #undef JSON_HEDLEY_GNUC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GNUC_VERSION) + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GNUC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION) + #undef JSON_HEDLEY_MSVC_VERSION +#endif +#if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) +#elif defined(_MSC_FULL_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) +#elif defined(_MSC_VER) && !defined(__ICL) + #define JSON_HEDLEY_MSVC_VERSION JSON_HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_MSVC_VERSION_CHECK) + #undef JSON_HEDLEY_MSVC_VERSION_CHECK +#endif +#if !defined(JSON_HEDLEY_MSVC_VERSION) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0) +#elif defined(_MSC_VER) && (_MSC_VER >= 1400) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) +#elif defined(_MSC_VER) && (_MSC_VER >= 1200) + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) +#else + #define JSON_HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION) + #undef JSON_HEDLEY_INTEL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) +#elif defined(__INTEL_COMPILER) && !defined(__ICL) + #define JSON_HEDLEY_INTEL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_VERSION) + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #undef JSON_HEDLEY_INTEL_CL_VERSION +#endif +#if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) && defined(__ICL) + #define JSON_HEDLEY_INTEL_CL_VERSION JSON_HEDLEY_VERSION_ENCODE(__INTEL_COMPILER, __INTEL_COMPILER_UPDATE, 0) +#endif + +#if defined(JSON_HEDLEY_INTEL_CL_VERSION_CHECK) + #undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_INTEL_CL_VERSION) + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_INTEL_CL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_INTEL_CL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION) + #undef JSON_HEDLEY_PGI_VERSION +#endif +#if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + #define JSON_HEDLEY_PGI_VERSION JSON_HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) +#endif + +#if defined(JSON_HEDLEY_PGI_VERSION_CHECK) + #undef JSON_HEDLEY_PGI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PGI_VERSION) + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PGI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #undef JSON_HEDLEY_SUNPRO_VERSION +#endif +#if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) +#elif defined(__SUNPRO_C) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) +#elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) +#elif defined(__SUNPRO_CC) + #define JSON_HEDLEY_SUNPRO_VERSION JSON_HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) +#endif + +#if defined(JSON_HEDLEY_SUNPRO_VERSION_CHECK) + #undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_SUNPRO_VERSION) + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_SUNPRO_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#endif +#if defined(__EMSCRIPTEN__) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION JSON_HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) +#endif + +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK) + #undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_EMSCRIPTEN_VERSION) + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_EMSCRIPTEN_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION) + #undef JSON_HEDLEY_ARM_VERSION +#endif +#if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) +#elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + #define JSON_HEDLEY_ARM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) +#endif + +#if defined(JSON_HEDLEY_ARM_VERSION_CHECK) + #undef JSON_HEDLEY_ARM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_ARM_VERSION) + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_ARM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION) + #undef JSON_HEDLEY_IBM_VERSION +#endif +#if defined(__ibmxl__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) +#elif defined(__xlC__) && defined(__xlC_ver__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) +#elif defined(__xlC__) + #define JSON_HEDLEY_IBM_VERSION JSON_HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) +#endif + +#if defined(JSON_HEDLEY_IBM_VERSION_CHECK) + #undef JSON_HEDLEY_IBM_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IBM_VERSION) + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IBM_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_VERSION) + #undef JSON_HEDLEY_TI_VERSION +#endif +#if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) +#if (__TI_COMPILER_VERSION__ >= 16000000) + #define JSON_HEDLEY_TI_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif +#endif + +#if defined(JSON_HEDLEY_TI_VERSION_CHECK) + #undef JSON_HEDLEY_TI_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_VERSION) + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #undef JSON_HEDLEY_TI_CL2000_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + #define JSON_HEDLEY_TI_CL2000_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL2000_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL2000_VERSION) + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL2000_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #undef JSON_HEDLEY_TI_CL430_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + #define JSON_HEDLEY_TI_CL430_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL430_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL430_VERSION) + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL430_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL430_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #undef JSON_HEDLEY_TI_ARMCL_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + #define JSON_HEDLEY_TI_ARMCL_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION_CHECK) + #undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_ARMCL_VERSION) + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_ARMCL_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #undef JSON_HEDLEY_TI_CL6X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + #define JSON_HEDLEY_TI_CL6X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL6X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL6X_VERSION) + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL6X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #undef JSON_HEDLEY_TI_CL7X_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + #define JSON_HEDLEY_TI_CL7X_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CL7X_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CL7X_VERSION) + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CL7X_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #undef JSON_HEDLEY_TI_CLPRU_VERSION +#endif +#if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + #define JSON_HEDLEY_TI_CLPRU_VERSION JSON_HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) +#endif + +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION_CHECK) + #undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TI_CLPRU_VERSION) + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TI_CLPRU_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION) + #undef JSON_HEDLEY_CRAY_VERSION +#endif +#if defined(_CRAYC) + #if defined(_RELEASE_PATCHLEVEL) + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + #else + #define JSON_HEDLEY_CRAY_VERSION JSON_HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_CRAY_VERSION_CHECK) + #undef JSON_HEDLEY_CRAY_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_CRAY_VERSION) + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_CRAY_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION) + #undef JSON_HEDLEY_IAR_VERSION +#endif +#if defined(__IAR_SYSTEMS_ICC__) + #if __VER__ > 1000 + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + #else + #define JSON_HEDLEY_IAR_VERSION JSON_HEDLEY_VERSION_ENCODE(__VER__ / 100, __VER__ % 100, 0) + #endif +#endif + +#if defined(JSON_HEDLEY_IAR_VERSION_CHECK) + #undef JSON_HEDLEY_IAR_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_IAR_VERSION) + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_IAR_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION) + #undef JSON_HEDLEY_TINYC_VERSION +#endif +#if defined(__TINYC__) + #define JSON_HEDLEY_TINYC_VERSION JSON_HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) +#endif + +#if defined(JSON_HEDLEY_TINYC_VERSION_CHECK) + #undef JSON_HEDLEY_TINYC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_TINYC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION) + #undef JSON_HEDLEY_DMC_VERSION +#endif +#if defined(__DMC__) + #define JSON_HEDLEY_DMC_VERSION JSON_HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) +#endif + +#if defined(JSON_HEDLEY_DMC_VERSION_CHECK) + #undef JSON_HEDLEY_DMC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_DMC_VERSION) + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_DMC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #undef JSON_HEDLEY_COMPCERT_VERSION +#endif +#if defined(__COMPCERT_VERSION__) + #define JSON_HEDLEY_COMPCERT_VERSION JSON_HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) +#endif + +#if defined(JSON_HEDLEY_COMPCERT_VERSION_CHECK) + #undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_COMPCERT_VERSION) + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_COMPCERT_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION) + #undef JSON_HEDLEY_PELLES_VERSION +#endif +#if defined(__POCC__) + #define JSON_HEDLEY_PELLES_VERSION JSON_HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) +#endif + +#if defined(JSON_HEDLEY_PELLES_VERSION_CHECK) + #undef JSON_HEDLEY_PELLES_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_PELLES_VERSION) + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_PELLES_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #undef JSON_HEDLEY_MCST_LCC_VERSION +#endif +#if defined(__LCC__) && defined(__LCC_MINOR__) + #define JSON_HEDLEY_MCST_LCC_VERSION JSON_HEDLEY_VERSION_ENCODE(__LCC__ / 100, __LCC__ % 100, __LCC_MINOR__) +#endif + +#if defined(JSON_HEDLEY_MCST_LCC_VERSION_CHECK) + #undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_MCST_LCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_MCST_LCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION) + #undef JSON_HEDLEY_GCC_VERSION +#endif +#if \ + defined(JSON_HEDLEY_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(JSON_HEDLEY_INTEL_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_ARM_VERSION) && \ + !defined(JSON_HEDLEY_CRAY_VERSION) && \ + !defined(JSON_HEDLEY_TI_VERSION) && \ + !defined(JSON_HEDLEY_TI_ARMCL_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL430_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL2000_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL6X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CL7X_VERSION) && \ + !defined(JSON_HEDLEY_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) && \ + !defined(JSON_HEDLEY_MCST_LCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION JSON_HEDLEY_GNUC_VERSION +#endif + +#if defined(JSON_HEDLEY_GCC_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_VERSION_CHECK +#endif +#if defined(JSON_HEDLEY_GCC_VERSION) + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (JSON_HEDLEY_GCC_VERSION >= JSON_HEDLEY_VERSION_ENCODE(major, minor, patch)) +#else + #define JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_ATTRIBUTE +#endif +#if \ + defined(__has_attribute) && \ + ( \ + (!defined(JSON_HEDLEY_IAR_VERSION) || JSON_HEDLEY_IAR_VERSION_CHECK(8,5,9)) \ + ) +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) +#else +# define JSON_HEDLEY_HAS_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#endif +#if defined(__has_attribute) + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#endif +#if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS) + #undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#endif +#if !defined(__cplusplus) || !defined(__has_cpp_attribute) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#elif \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION) && \ + (!defined(JSON_HEDLEY_SUNPRO_VERSION) || JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(JSON_HEDLEY_MSVC_VERSION) || JSON_HEDLEY_MSVC_VERSION_CHECK(19,20,0)) + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(ns::attribute) +#else + #define JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#endif +#if defined(__has_cpp_attribute) && defined(__cplusplus) + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_BUILTIN) + #undef JSON_HEDLEY_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin) +#else + #define JSON_HEDLEY_HAS_BUILTIN(builtin) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_BUILTIN) + #undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_BUILTIN) + #undef JSON_HEDLEY_GCC_HAS_BUILTIN +#endif +#if defined(__has_builtin) + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) +#else + #define JSON_HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_FEATURE) + #undef JSON_HEDLEY_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_HAS_FEATURE(feature) __has_feature(feature) +#else + #define JSON_HEDLEY_HAS_FEATURE(feature) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_FEATURE) + #undef JSON_HEDLEY_GNUC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_FEATURE) + #undef JSON_HEDLEY_GCC_HAS_FEATURE +#endif +#if defined(__has_feature) + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) +#else + #define JSON_HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_EXTENSION) + #undef JSON_HEDLEY_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_HAS_EXTENSION(extension) __has_extension(extension) +#else + #define JSON_HEDLEY_HAS_EXTENSION(extension) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_EXTENSION) + #undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_EXTENSION) + #undef JSON_HEDLEY_GCC_HAS_EXTENSION +#endif +#if defined(__has_extension) + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) +#else + #define JSON_HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#endif +#if defined(__has_declspec_attribute) + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) +#else + #define JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_HAS_WARNING) + #undef JSON_HEDLEY_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_HAS_WARNING(warning) __has_warning(warning) +#else + #define JSON_HEDLEY_HAS_WARNING(warning) (0) +#endif + +#if defined(JSON_HEDLEY_GNUC_HAS_WARNING) + #undef JSON_HEDLEY_GNUC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_GCC_HAS_WARNING) + #undef JSON_HEDLEY_GCC_HAS_WARNING +#endif +#if defined(__has_warning) + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) +#else + #define JSON_HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \ + (JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + #define JSON_HEDLEY_PRAGMA(value) _Pragma(#value) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_PRAGMA(value) __pragma(value) +#else + #define JSON_HEDLEY_PRAGMA(value) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_PUSH) + #undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#endif +#if defined(JSON_HEDLEY_DIAGNOSTIC_POP) + #undef JSON_HEDLEY_DIAGNOSTIC_POP +#endif +#if defined(__clang__) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push)) + #define JSON_HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop)) +#elif JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("pop") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)") + #define JSON_HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_PUSH + #define JSON_HEDLEY_DIAGNOSTIC_POP +#endif + +/* JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat") +# if JSON_HEDLEY_HAS_WARNING("-Wc++17-extensions") +# if JSON_HEDLEY_HAS_WARNING("-Wc++1z-extensions") +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + _Pragma("clang diagnostic ignored \"-Wc++1z-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# else +# define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + JSON_HEDLEY_DIAGNOSTIC_POP +# endif +# endif +#endif +#if !defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x +#endif + +#if defined(JSON_HEDLEY_CONST_CAST) + #undef JSON_HEDLEY_CONST_CAST +#endif +#if defined(__cplusplus) +# define JSON_HEDLEY_CONST_CAST(T, expr) (const_cast(expr)) +#elif \ + JSON_HEDLEY_HAS_WARNING("-Wcast-qual") || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_CONST_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_REINTERPRET_CAST) + #undef JSON_HEDLEY_REINTERPRET_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) +#else + #define JSON_HEDLEY_REINTERPRET_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_STATIC_CAST) + #undef JSON_HEDLEY_STATIC_CAST +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_STATIC_CAST(T, expr) (static_cast(expr)) +#else + #define JSON_HEDLEY_STATIC_CAST(T, expr) ((T) (expr)) +#endif + +#if defined(JSON_HEDLEY_CPP_CAST) + #undef JSON_HEDLEY_CPP_CAST +#endif +#if defined(__cplusplus) +# if JSON_HEDLEY_HAS_WARNING("-Wold-style-cast") +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# elif JSON_HEDLEY_IAR_VERSION_CHECK(8,3,0) +# define JSON_HEDLEY_CPP_CAST(T, expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_CPP_CAST(T, expr) ((T) (expr)) +# endif +#else +# define JSON_HEDLEY_CPP_CAST(T, expr) (expr) +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wdeprecated-declarations") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:1478 1786)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1216,1444,1445") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,90,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:161)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(16,9,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 161") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-attributes") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(4,6,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") +#elif JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:1292)) +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(20,7,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097,1098") +#elif JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") +#elif \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wcast-qual") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") +#elif JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#endif + +#if defined(JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION) + #undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunused-function") + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("clang diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("GCC diagnostic ignored \"-Wunused-function\"") +#elif JSON_HEDLEY_MSVC_VERSION_CHECK(1,0,0) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION __pragma(warning(disable:4505)) +#elif JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION _Pragma("diag_suppress 3142") +#else + #define JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#endif + +#if defined(JSON_HEDLEY_DEPRECATED) + #undef JSON_HEDLEY_DEPRECATED +#endif +#if defined(JSON_HEDLEY_DEPRECATED_FOR) + #undef JSON_HEDLEY_DEPRECATED_FOR +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) +#elif \ + (JSON_HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,3,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,3,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) +#elif defined(__cplusplus) && (__cplusplus >= 201402L) + #define JSON_HEDLEY_DEPRECATED(since) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(deprecated) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_DEPRECATED(since) __attribute__((__deprecated__)) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_PELLES_VERSION_CHECK(6,50,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_DEPRECATED(since) __declspec(deprecated) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_DEPRECATED(since) _Pragma("deprecated") + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") +#else + #define JSON_HEDLEY_DEPRECATED(since) + #define JSON_HEDLEY_DEPRECATED_FOR(since, replacement) +#endif + +#if defined(JSON_HEDLEY_UNAVAILABLE) + #undef JSON_HEDLEY_UNAVAILABLE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warning) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) +#else + #define JSON_HEDLEY_UNAVAILABLE(available_since) +#endif + +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT +#endif +#if defined(JSON_HEDLEY_WARN_UNUSED_RESULT_MSG) + #undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) +#elif (JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(nodiscard) + #define JSON_HEDLEY_WARN_UNUSED_RESULT JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) +#elif defined(_Check_return_) /* SAL */ + #define JSON_HEDLEY_WARN_UNUSED_RESULT _Check_return_ + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ +#else + #define JSON_HEDLEY_WARN_UNUSED_RESULT + #define JSON_HEDLEY_WARN_UNUSED_RESULT_MSG(msg) +#endif + +#if defined(JSON_HEDLEY_SENTINEL) + #undef JSON_HEDLEY_SENTINEL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(sentinel) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position))) +#else + #define JSON_HEDLEY_SENTINEL(position) +#endif + +#if defined(JSON_HEDLEY_NO_RETURN) + #undef JSON_HEDLEY_NO_RETURN +#endif +#if JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NO_RETURN __noreturn +#elif \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define JSON_HEDLEY_NO_RETURN _Noreturn +#elif defined(__cplusplus) && (__cplusplus >= 201103L) + #define JSON_HEDLEY_NO_RETURN JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(noreturn) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,2,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NO_RETURN __attribute__((__noreturn__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_NO_RETURN _Pragma("does_not_return") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NO_RETURN __attribute((noreturn)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NO_RETURN __declspec(noreturn) +#else + #define JSON_HEDLEY_NO_RETURN +#endif + +#if defined(JSON_HEDLEY_NO_ESCAPE) + #undef JSON_HEDLEY_NO_ESCAPE +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(noescape) + #define JSON_HEDLEY_NO_ESCAPE __attribute__((__noescape__)) +#else + #define JSON_HEDLEY_NO_ESCAPE +#endif + +#if defined(JSON_HEDLEY_UNREACHABLE) + #undef JSON_HEDLEY_UNREACHABLE +#endif +#if defined(JSON_HEDLEY_UNREACHABLE_RETURN) + #undef JSON_HEDLEY_UNREACHABLE_RETURN +#endif +#if defined(JSON_HEDLEY_ASSUME) + #undef JSON_HEDLEY_ASSUME +#endif +#if \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_ASSUME(expr) __assume(expr) +#elif JSON_HEDLEY_HAS_BUILTIN(__builtin_assume) + #define JSON_HEDLEY_ASSUME(expr) __builtin_assume(expr) +#elif \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #if defined(__cplusplus) + #define JSON_HEDLEY_ASSUME(expr) std::_nassert(expr) + #else + #define JSON_HEDLEY_ASSUME(expr) _nassert(expr) + #endif +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(JSON_HEDLEY_ARM_VERSION))) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,5,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,10,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,5) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(10,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_UNREACHABLE() __builtin_unreachable() +#elif defined(JSON_HEDLEY_ASSUME) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif +#if !defined(JSON_HEDLEY_ASSUME) + #if defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, ((expr) ? 1 : (JSON_HEDLEY_UNREACHABLE(), 1))) + #else + #define JSON_HEDLEY_ASSUME(expr) JSON_HEDLEY_STATIC_CAST(void, expr) + #endif +#endif +#if defined(JSON_HEDLEY_UNREACHABLE) + #if \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (JSON_HEDLEY_STATIC_CAST(void, JSON_HEDLEY_ASSUME(0)), (value)) + #else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) JSON_HEDLEY_UNREACHABLE() + #endif +#else + #define JSON_HEDLEY_UNREACHABLE_RETURN(value) return (value) +#endif +#if !defined(JSON_HEDLEY_UNREACHABLE) + #define JSON_HEDLEY_UNREACHABLE() JSON_HEDLEY_ASSUME(0) +#endif + +JSON_HEDLEY_DIAGNOSTIC_PUSH +#if JSON_HEDLEY_HAS_WARNING("-Wpedantic") + #pragma clang diagnostic ignored "-Wpedantic" +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + #pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#endif +#if JSON_HEDLEY_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + #if defined(__clang__) + #pragma clang diagnostic ignored "-Wvariadic-macros" + #elif defined(JSON_HEDLEY_GCC_VERSION) + #pragma GCC diagnostic ignored "-Wvariadic-macros" + #endif +#endif +#if defined(JSON_HEDLEY_NON_NULL) + #undef JSON_HEDLEY_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) +#else + #define JSON_HEDLEY_NON_NULL(...) +#endif +JSON_HEDLEY_DIAGNOSTIC_POP + +#if defined(JSON_HEDLEY_PRINTF_FORMAT) + #undef JSON_HEDLEY_PRINTF_FORMAT +#endif +#if defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) +#elif defined(__MINGW32__) && JSON_HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) +#elif \ + JSON_HEDLEY_HAS_ATTRIBUTE(format) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,6,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(6,0,0) + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) +#else + #define JSON_HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) +#endif + +#if defined(JSON_HEDLEY_CONSTEXPR) + #undef JSON_HEDLEY_CONSTEXPR +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_CONSTEXPR JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + #endif +#endif +#if !defined(JSON_HEDLEY_CONSTEXPR) + #define JSON_HEDLEY_CONSTEXPR +#endif + +#if defined(JSON_HEDLEY_PREDICT) + #undef JSON_HEDLEY_PREDICT +#endif +#if defined(JSON_HEDLEY_LIKELY) + #undef JSON_HEDLEY_LIKELY +#endif +#if defined(JSON_HEDLEY_UNLIKELY) + #undef JSON_HEDLEY_UNLIKELY +#endif +#if defined(JSON_HEDLEY_UNPREDICTABLE) + #undef JSON_HEDLEY_UNPREDICTABLE +#endif +#if JSON_HEDLEY_HAS_BUILTIN(__builtin_unpredictable) + #define JSON_HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) +#endif +#if \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) && !defined(JSON_HEDLEY_PGI_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(9,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect (!!(expr), 1 ) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) +#elif \ + (JSON_HEDLEY_HAS_BUILTIN(__builtin_expect) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,27) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (JSON_HEDLEY_STATIC_CAST(void, expected), (expr))) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double hedley_probability_ = (probability); \ + ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) +# define JSON_HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1) +# define JSON_HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0) +#else +# define JSON_HEDLEY_PREDICT(expr, expected, probability) (JSON_HEDLEY_STATIC_CAST(void, expected), (expr)) +# define JSON_HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr)) +# define JSON_HEDLEY_LIKELY(expr) (!!(expr)) +# define JSON_HEDLEY_UNLIKELY(expr) (!!(expr)) +#endif +#if !defined(JSON_HEDLEY_UNPREDICTABLE) + #define JSON_HEDLEY_UNPREDICTABLE(expr) JSON_HEDLEY_PREDICT(expr, 1, 0.5) +#endif + +#if defined(JSON_HEDLEY_MALLOC) + #undef JSON_HEDLEY_MALLOC +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(malloc) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_MALLOC __attribute__((__malloc__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_MALLOC _Pragma("returns_new_memory") +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_MALLOC __declspec(restrict) +#else + #define JSON_HEDLEY_MALLOC +#endif + +#if defined(JSON_HEDLEY_PURE) + #undef JSON_HEDLEY_PURE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(pure) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,96,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PURE __attribute__((__pure__)) +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) +# define JSON_HEDLEY_PURE _Pragma("does_not_write_global_data") +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(2,0,1) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) +# define JSON_HEDLEY_PURE _Pragma("FUNC_IS_PURE;") +#else +# define JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_CONST) + #undef JSON_HEDLEY_CONST +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(const) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(2,5,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_CONST __attribute__((__const__)) +#elif \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) + #define JSON_HEDLEY_CONST _Pragma("no_side_effect") +#else + #define JSON_HEDLEY_CONST JSON_HEDLEY_PURE +#endif + +#if defined(JSON_HEDLEY_RESTRICT) + #undef JSON_HEDLEY_RESTRICT +#endif +#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT restrict +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(17,10,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,4) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RESTRICT __restrict +#elif JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + #define JSON_HEDLEY_RESTRICT _Restrict +#else + #define JSON_HEDLEY_RESTRICT +#endif + +#if defined(JSON_HEDLEY_INLINE) + #undef JSON_HEDLEY_INLINE +#endif +#if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + #define JSON_HEDLEY_INLINE inline +#elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(6,2,0) + #define JSON_HEDLEY_INLINE __inline__ +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(3,1,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,2,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(8,0,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_INLINE __inline +#else + #define JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_ALWAYS_INLINE) + #undef JSON_HEDLEY_ALWAYS_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(always_inline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) +# define JSON_HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) JSON_HEDLEY_INLINE +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_ALWAYS_INLINE __forceinline +#elif defined(__cplusplus) && \ + ( \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_ALWAYS_INLINE _Pragma("inline=forced") +#else +# define JSON_HEDLEY_ALWAYS_INLINE JSON_HEDLEY_INLINE +#endif + +#if defined(JSON_HEDLEY_NEVER_INLINE) + #undef JSON_HEDLEY_NEVER_INLINE +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(noinline) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(10,1,0) || \ + JSON_HEDLEY_TI_VERSION_CHECK(15,12,0) || \ + (JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(4,3,0) || \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) || \ + JSON_HEDLEY_TI_CL7X_VERSION_CHECK(1,2,0) || \ + JSON_HEDLEY_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) || \ + JSON_HEDLEY_IAR_VERSION_CHECK(8,10,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute__((__noinline__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#elif JSON_HEDLEY_PGI_VERSION_CHECK(10,2,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("noinline") +#elif JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) + #define JSON_HEDLEY_NEVER_INLINE _Pragma("inline=never") +#elif JSON_HEDLEY_COMPCERT_VERSION_CHECK(3,2,0) + #define JSON_HEDLEY_NEVER_INLINE __attribute((noinline)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(9,0,0) + #define JSON_HEDLEY_NEVER_INLINE __declspec(noinline) +#else + #define JSON_HEDLEY_NEVER_INLINE +#endif + +#if defined(JSON_HEDLEY_PRIVATE) + #undef JSON_HEDLEY_PRIVATE +#endif +#if defined(JSON_HEDLEY_PUBLIC) + #undef JSON_HEDLEY_PUBLIC +#endif +#if defined(JSON_HEDLEY_IMPORT) + #undef JSON_HEDLEY_IMPORT +#endif +#if defined(_WIN32) || defined(__CYGWIN__) +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC __declspec(dllexport) +# define JSON_HEDLEY_IMPORT __declspec(dllimport) +#else +# if \ + JSON_HEDLEY_HAS_ATTRIBUTE(visibility) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) +# define JSON_HEDLEY_PRIVATE __attribute__((__visibility__("hidden"))) +# define JSON_HEDLEY_PUBLIC __attribute__((__visibility__("default"))) +# else +# define JSON_HEDLEY_PRIVATE +# define JSON_HEDLEY_PUBLIC +# endif +# define JSON_HEDLEY_IMPORT extern +#endif + +#if defined(JSON_HEDLEY_NO_THROW) + #undef JSON_HEDLEY_NO_THROW +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(nothrow) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,3,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_NO_THROW __attribute__((__nothrow__)) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) + #define JSON_HEDLEY_NO_THROW __declspec(nothrow) +#else + #define JSON_HEDLEY_NO_THROW +#endif + +#if defined(JSON_HEDLEY_FALL_THROUGH) + #undef JSON_HEDLEY_FALL_THROUGH +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(fallthrough) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(7,0,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_FALL_THROUGH __attribute__((__fallthrough__)) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) +#elif JSON_HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough) + #define JSON_HEDLEY_FALL_THROUGH JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) +#elif defined(__fallthrough) /* SAL */ + #define JSON_HEDLEY_FALL_THROUGH __fallthrough +#else + #define JSON_HEDLEY_FALL_THROUGH +#endif + +#if defined(JSON_HEDLEY_RETURNS_NON_NULL) + #undef JSON_HEDLEY_RETURNS_NON_NULL +#endif +#if \ + JSON_HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) +#elif defined(_Ret_notnull_) /* SAL */ + #define JSON_HEDLEY_RETURNS_NON_NULL _Ret_notnull_ +#else + #define JSON_HEDLEY_RETURNS_NON_NULL +#endif + +#if defined(JSON_HEDLEY_ARRAY_PARAM) + #undef JSON_HEDLEY_ARRAY_PARAM +#endif +#if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_TINYC_VERSION) + #define JSON_HEDLEY_ARRAY_PARAM(name) (name) +#else + #define JSON_HEDLEY_ARRAY_PARAM(name) +#endif + +#if defined(JSON_HEDLEY_IS_CONSTANT) + #undef JSON_HEDLEY_IS_CONSTANT +#endif +#if defined(JSON_HEDLEY_REQUIRE_CONSTEXPR) + #undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#endif +/* JSON_HEDLEY_IS_CONSTEXPR_ is for + HEDLEY INTERNAL USE ONLY. API subject to change without notice. */ +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #undef JSON_HEDLEY_IS_CONSTEXPR_ +#endif +#if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(4,1,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (JSON_HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_MCST_LCC_VERSION_CHECK(1,25,10) + #define JSON_HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr) +#endif +#if !defined(__cplusplus) +# if \ + JSON_HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(3,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(13,1,0) || \ + JSON_HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,4,0) || \ + JSON_HEDLEY_TINYC_VERSION_CHECK(0,9,24) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) +#endif +# elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(JSON_HEDLEY_SUNPRO_VERSION) && \ + !defined(JSON_HEDLEY_PGI_VERSION) && \ + !defined(JSON_HEDLEY_IAR_VERSION)) || \ + (JSON_HEDLEY_HAS_EXTENSION(c_generic_selections) && !defined(JSON_HEDLEY_IAR_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \ + JSON_HEDLEY_IBM_VERSION_CHECK(12,1,0) || \ + JSON_HEDLEY_ARM_VERSION_CHECK(5,3,0) +#if defined(__INTPTR_TYPE__) + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) +#else + #include + #define JSON_HEDLEY_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) +#endif +# elif \ + defined(JSON_HEDLEY_GCC_VERSION) || \ + defined(JSON_HEDLEY_INTEL_VERSION) || \ + defined(JSON_HEDLEY_TINYC_VERSION) || \ + defined(JSON_HEDLEY_TI_ARMCL_VERSION) || \ + JSON_HEDLEY_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(JSON_HEDLEY_TI_CL2000_VERSION) || \ + defined(JSON_HEDLEY_TI_CL6X_VERSION) || \ + defined(JSON_HEDLEY_TI_CL7X_VERSION) || \ + defined(JSON_HEDLEY_TI_CLPRU_VERSION) || \ + defined(__clang__) +# define JSON_HEDLEY_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ +((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) +# endif +#endif +#if defined(JSON_HEDLEY_IS_CONSTEXPR_) + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) JSON_HEDLEY_IS_CONSTEXPR_(expr) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (JSON_HEDLEY_IS_CONSTEXPR_(expr) ? (expr) : (-1)) +#else + #if !defined(JSON_HEDLEY_IS_CONSTANT) + #define JSON_HEDLEY_IS_CONSTANT(expr) (0) + #endif + #define JSON_HEDLEY_REQUIRE_CONSTEXPR(expr) (expr) +#endif + +#if defined(JSON_HEDLEY_BEGIN_C_DECLS) + #undef JSON_HEDLEY_BEGIN_C_DECLS +#endif +#if defined(JSON_HEDLEY_END_C_DECLS) + #undef JSON_HEDLEY_END_C_DECLS +#endif +#if defined(JSON_HEDLEY_C_DECL) + #undef JSON_HEDLEY_C_DECL +#endif +#if defined(__cplusplus) + #define JSON_HEDLEY_BEGIN_C_DECLS extern "C" { + #define JSON_HEDLEY_END_C_DECLS } + #define JSON_HEDLEY_C_DECL extern "C" +#else + #define JSON_HEDLEY_BEGIN_C_DECLS + #define JSON_HEDLEY_END_C_DECLS + #define JSON_HEDLEY_C_DECL +#endif + +#if defined(JSON_HEDLEY_STATIC_ASSERT) + #undef JSON_HEDLEY_STATIC_ASSERT +#endif +#if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + (JSON_HEDLEY_HAS_FEATURE(c_static_assert) && !defined(JSON_HEDLEY_INTEL_CL_VERSION)) || \ + JSON_HEDLEY_GCC_VERSION_CHECK(6,0,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message) +#elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + JSON_HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) +#else +# define JSON_HEDLEY_STATIC_ASSERT(expr, message) +#endif + +#if defined(JSON_HEDLEY_NULL) + #undef JSON_HEDLEY_NULL +#endif +#if defined(__cplusplus) + #if __cplusplus >= 201103L + #define JSON_HEDLEY_NULL JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + #elif defined(NULL) + #define JSON_HEDLEY_NULL NULL + #else + #define JSON_HEDLEY_NULL JSON_HEDLEY_STATIC_CAST(void*, 0) + #endif +#elif defined(NULL) + #define JSON_HEDLEY_NULL NULL +#else + #define JSON_HEDLEY_NULL ((void*) 0) +#endif + +#if defined(JSON_HEDLEY_MESSAGE) + #undef JSON_HEDLEY_MESSAGE +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_MESSAGE(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(message msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message msg) +#elif JSON_HEDLEY_CRAY_VERSION_CHECK(5,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(_CRI message msg) +#elif JSON_HEDLEY_IAR_VERSION_CHECK(8,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#elif JSON_HEDLEY_PELLES_VERSION_CHECK(2,0,0) +# define JSON_HEDLEY_MESSAGE(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_WARNING) + #undef JSON_HEDLEY_WARNING +#endif +#if JSON_HEDLEY_HAS_WARNING("-Wunknown-pragmas") +# define JSON_HEDLEY_WARNING(msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + JSON_HEDLEY_PRAGMA(clang warning msg) \ + JSON_HEDLEY_DIAGNOSTIC_POP +#elif \ + JSON_HEDLEY_GCC_VERSION_CHECK(4,8,0) || \ + JSON_HEDLEY_PGI_VERSION_CHECK(18,4,0) || \ + JSON_HEDLEY_INTEL_VERSION_CHECK(13,0,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(GCC warning msg) +#elif \ + JSON_HEDLEY_MSVC_VERSION_CHECK(15,0,0) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_PRAGMA(message(msg)) +#else +# define JSON_HEDLEY_WARNING(msg) JSON_HEDLEY_MESSAGE(msg) +#endif + +#if defined(JSON_HEDLEY_REQUIRE) + #undef JSON_HEDLEY_REQUIRE +#endif +#if defined(JSON_HEDLEY_REQUIRE_MSG) + #undef JSON_HEDLEY_REQUIRE_MSG +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(diagnose_if) +# if JSON_HEDLEY_HAS_WARNING("-Wgcc-compat") +# define JSON_HEDLEY_REQUIRE(expr) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + JSON_HEDLEY_DIAGNOSTIC_POP +# else +# define JSON_HEDLEY_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) +# endif +#else +# define JSON_HEDLEY_REQUIRE(expr) +# define JSON_HEDLEY_REQUIRE_MSG(expr,msg) +#endif + +#if defined(JSON_HEDLEY_FLAGS) + #undef JSON_HEDLEY_FLAGS +#endif +#if JSON_HEDLEY_HAS_ATTRIBUTE(flag_enum) && (!defined(__cplusplus) || JSON_HEDLEY_HAS_WARNING("-Wbitfield-enum-conversion")) + #define JSON_HEDLEY_FLAGS __attribute__((__flag_enum__)) +#else + #define JSON_HEDLEY_FLAGS +#endif + +#if defined(JSON_HEDLEY_FLAGS_CAST) + #undef JSON_HEDLEY_FLAGS_CAST +#endif +#if JSON_HEDLEY_INTEL_VERSION_CHECK(19,0,0) +# define JSON_HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \ + JSON_HEDLEY_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + JSON_HEDLEY_DIAGNOSTIC_POP \ + })) +#else +# define JSON_HEDLEY_FLAGS_CAST(T, expr) JSON_HEDLEY_STATIC_CAST(T, expr) +#endif + +#if defined(JSON_HEDLEY_EMPTY_BASES) + #undef JSON_HEDLEY_EMPTY_BASES +#endif +#if \ + (JSON_HEDLEY_MSVC_VERSION_CHECK(19,0,23918) && !JSON_HEDLEY_MSVC_VERSION_CHECK(20,0,0)) || \ + JSON_HEDLEY_INTEL_CL_VERSION_CHECK(2021,1,0) + #define JSON_HEDLEY_EMPTY_BASES __declspec(empty_bases) +#else + #define JSON_HEDLEY_EMPTY_BASES +#endif + +/* Remaining macros are deprecated. */ + +#if defined(JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK) + #undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#endif +#if defined(__clang__) + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) +#else + #define JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) JSON_HEDLEY_GCC_VERSION_CHECK(major,minor,patch) +#endif + +#if defined(JSON_HEDLEY_CLANG_HAS_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_CPP_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_BUILTIN) + #undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#endif +#define JSON_HEDLEY_CLANG_HAS_BUILTIN(builtin) JSON_HEDLEY_HAS_BUILTIN(builtin) + +#if defined(JSON_HEDLEY_CLANG_HAS_FEATURE) + #undef JSON_HEDLEY_CLANG_HAS_FEATURE +#endif +#define JSON_HEDLEY_CLANG_HAS_FEATURE(feature) JSON_HEDLEY_HAS_FEATURE(feature) + +#if defined(JSON_HEDLEY_CLANG_HAS_EXTENSION) + #undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#endif +#define JSON_HEDLEY_CLANG_HAS_EXTENSION(extension) JSON_HEDLEY_HAS_EXTENSION(extension) + +#if defined(JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + #undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#endif +#define JSON_HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) + +#if defined(JSON_HEDLEY_CLANG_HAS_WARNING) + #undef JSON_HEDLEY_CLANG_HAS_WARNING +#endif +#define JSON_HEDLEY_CLANG_HAS_WARNING(warning) JSON_HEDLEY_HAS_WARNING(warning) + +#endif /* !defined(JSON_HEDLEY_VERSION) || (JSON_HEDLEY_VERSION < X) */ + + +// This file contains all internal macro definitions +// You MUST include macro_unscope.hpp at the end of json.hpp to undef all of them + +// exclude unsupported compilers +#if !defined(JSON_SKIP_UNSUPPORTED_COMPILER_CHECK) + #if defined(__clang__) + #if (__clang_major__ * 10000 + __clang_minor__ * 100 + __clang_patchlevel__) < 30400 + #error "unsupported Clang version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #elif defined(__GNUC__) && !(defined(__ICC) || defined(__INTEL_COMPILER)) + #if (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) < 40800 + #error "unsupported GCC version - see https://github.com/nlohmann/json#supported-compilers" + #endif + #endif +#endif + +// C++ language standard detection +// if the user manually specified the used c++ version this is skipped +#if !defined(JSON_HAS_CPP_20) && !defined(JSON_HAS_CPP_17) && !defined(JSON_HAS_CPP_14) && !defined(JSON_HAS_CPP_11) + #if (defined(__cplusplus) && __cplusplus >= 202002L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 202002L) + #define JSON_HAS_CPP_20 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(_HAS_CXX17) && _HAS_CXX17 == 1) // fix for issue #464 + #define JSON_HAS_CPP_17 + #define JSON_HAS_CPP_14 + #elif (defined(__cplusplus) && __cplusplus >= 201402L) || (defined(_HAS_CXX14) && _HAS_CXX14 == 1) + #define JSON_HAS_CPP_14 + #endif + // the cpp 11 flag is always specified because it is the minimal required version + #define JSON_HAS_CPP_11 +#endif + +// disable documentation warnings on clang +#if defined(__clang__) + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdocumentation" + #pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#endif + +// allow to disable exceptions +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)) && !defined(JSON_NOEXCEPTION) + #define JSON_THROW(exception) throw exception + #define JSON_TRY try + #define JSON_CATCH(exception) catch(exception) + #define JSON_INTERNAL_CATCH(exception) catch(exception) +#else + #include + #define JSON_THROW(exception) std::abort() + #define JSON_TRY if(true) + #define JSON_CATCH(exception) if(false) + #define JSON_INTERNAL_CATCH(exception) if(false) +#endif + +// override exception macros +#if defined(JSON_THROW_USER) + #undef JSON_THROW + #define JSON_THROW JSON_THROW_USER +#endif +#if defined(JSON_TRY_USER) + #undef JSON_TRY + #define JSON_TRY JSON_TRY_USER +#endif +#if defined(JSON_CATCH_USER) + #undef JSON_CATCH + #define JSON_CATCH JSON_CATCH_USER + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_CATCH_USER +#endif +#if defined(JSON_INTERNAL_CATCH_USER) + #undef JSON_INTERNAL_CATCH + #define JSON_INTERNAL_CATCH JSON_INTERNAL_CATCH_USER +#endif + +// allow to override assert +#if !defined(JSON_ASSERT) + #include // assert + #define JSON_ASSERT(x) assert(x) +#endif + +// allow to access some private functions (needed by the test suite) +#if defined(JSON_TESTS_PRIVATE) + #define JSON_PRIVATE_UNLESS_TESTED public +#else + #define JSON_PRIVATE_UNLESS_TESTED private +#endif + +/*! +@brief macro to briefly define a mapping between an enum and JSON +@def NLOHMANN_JSON_SERIALIZE_ENUM +@since version 3.4.0 +*/ +#define NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + template \ + inline void to_json(BasicJsonType& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + template \ + inline void from_json(const BasicJsonType& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } + +// Ugly macros to avoid uglier copy-paste when specializing basic_json. They +// may be removed in the future once the class is split. + +#define NLOHMANN_BASIC_JSON_TPL_DECLARATION \ + template class ObjectType, \ + template class ArrayType, \ + class StringType, class BooleanType, class NumberIntegerType, \ + class NumberUnsignedType, class NumberFloatType, \ + template class AllocatorType, \ + template class JSONSerializer, \ + class BinaryType> + +#define NLOHMANN_BASIC_JSON_TPL \ + basic_json + +// Macros to simplify conversion from/to types + +#define NLOHMANN_JSON_EXPAND( x ) x +#define NLOHMANN_JSON_GET_MACRO(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, NAME,...) NAME +#define NLOHMANN_JSON_PASTE(...) NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_GET_MACRO(__VA_ARGS__, \ + NLOHMANN_JSON_PASTE64, \ + NLOHMANN_JSON_PASTE63, \ + NLOHMANN_JSON_PASTE62, \ + NLOHMANN_JSON_PASTE61, \ + NLOHMANN_JSON_PASTE60, \ + NLOHMANN_JSON_PASTE59, \ + NLOHMANN_JSON_PASTE58, \ + NLOHMANN_JSON_PASTE57, \ + NLOHMANN_JSON_PASTE56, \ + NLOHMANN_JSON_PASTE55, \ + NLOHMANN_JSON_PASTE54, \ + NLOHMANN_JSON_PASTE53, \ + NLOHMANN_JSON_PASTE52, \ + NLOHMANN_JSON_PASTE51, \ + NLOHMANN_JSON_PASTE50, \ + NLOHMANN_JSON_PASTE49, \ + NLOHMANN_JSON_PASTE48, \ + NLOHMANN_JSON_PASTE47, \ + NLOHMANN_JSON_PASTE46, \ + NLOHMANN_JSON_PASTE45, \ + NLOHMANN_JSON_PASTE44, \ + NLOHMANN_JSON_PASTE43, \ + NLOHMANN_JSON_PASTE42, \ + NLOHMANN_JSON_PASTE41, \ + NLOHMANN_JSON_PASTE40, \ + NLOHMANN_JSON_PASTE39, \ + NLOHMANN_JSON_PASTE38, \ + NLOHMANN_JSON_PASTE37, \ + NLOHMANN_JSON_PASTE36, \ + NLOHMANN_JSON_PASTE35, \ + NLOHMANN_JSON_PASTE34, \ + NLOHMANN_JSON_PASTE33, \ + NLOHMANN_JSON_PASTE32, \ + NLOHMANN_JSON_PASTE31, \ + NLOHMANN_JSON_PASTE30, \ + NLOHMANN_JSON_PASTE29, \ + NLOHMANN_JSON_PASTE28, \ + NLOHMANN_JSON_PASTE27, \ + NLOHMANN_JSON_PASTE26, \ + NLOHMANN_JSON_PASTE25, \ + NLOHMANN_JSON_PASTE24, \ + NLOHMANN_JSON_PASTE23, \ + NLOHMANN_JSON_PASTE22, \ + NLOHMANN_JSON_PASTE21, \ + NLOHMANN_JSON_PASTE20, \ + NLOHMANN_JSON_PASTE19, \ + NLOHMANN_JSON_PASTE18, \ + NLOHMANN_JSON_PASTE17, \ + NLOHMANN_JSON_PASTE16, \ + NLOHMANN_JSON_PASTE15, \ + NLOHMANN_JSON_PASTE14, \ + NLOHMANN_JSON_PASTE13, \ + NLOHMANN_JSON_PASTE12, \ + NLOHMANN_JSON_PASTE11, \ + NLOHMANN_JSON_PASTE10, \ + NLOHMANN_JSON_PASTE9, \ + NLOHMANN_JSON_PASTE8, \ + NLOHMANN_JSON_PASTE7, \ + NLOHMANN_JSON_PASTE6, \ + NLOHMANN_JSON_PASTE5, \ + NLOHMANN_JSON_PASTE4, \ + NLOHMANN_JSON_PASTE3, \ + NLOHMANN_JSON_PASTE2, \ + NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE2(func, v1) func(v1) +#define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) +#define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) +#define NLOHMANN_JSON_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE4(func, v2, v3, v4) +#define NLOHMANN_JSON_PASTE6(func, v1, v2, v3, v4, v5) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE5(func, v2, v3, v4, v5) +#define NLOHMANN_JSON_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE6(func, v2, v3, v4, v5, v6) +#define NLOHMANN_JSON_PASTE8(func, v1, v2, v3, v4, v5, v6, v7) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE7(func, v2, v3, v4, v5, v6, v7) +#define NLOHMANN_JSON_PASTE9(func, v1, v2, v3, v4, v5, v6, v7, v8) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE8(func, v2, v3, v4, v5, v6, v7, v8) +#define NLOHMANN_JSON_PASTE10(func, v1, v2, v3, v4, v5, v6, v7, v8, v9) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE9(func, v2, v3, v4, v5, v6, v7, v8, v9) +#define NLOHMANN_JSON_PASTE11(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE10(func, v2, v3, v4, v5, v6, v7, v8, v9, v10) +#define NLOHMANN_JSON_PASTE12(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE11(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11) +#define NLOHMANN_JSON_PASTE13(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE12(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12) +#define NLOHMANN_JSON_PASTE14(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE13(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13) +#define NLOHMANN_JSON_PASTE15(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE14(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14) +#define NLOHMANN_JSON_PASTE16(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE15(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15) +#define NLOHMANN_JSON_PASTE17(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE16(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16) +#define NLOHMANN_JSON_PASTE18(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE17(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17) +#define NLOHMANN_JSON_PASTE19(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE18(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18) +#define NLOHMANN_JSON_PASTE20(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE19(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19) +#define NLOHMANN_JSON_PASTE21(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE20(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20) +#define NLOHMANN_JSON_PASTE22(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE21(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21) +#define NLOHMANN_JSON_PASTE23(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE22(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) +#define NLOHMANN_JSON_PASTE24(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE23(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23) +#define NLOHMANN_JSON_PASTE25(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE24(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24) +#define NLOHMANN_JSON_PASTE26(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE25(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25) +#define NLOHMANN_JSON_PASTE27(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE26(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26) +#define NLOHMANN_JSON_PASTE28(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE27(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27) +#define NLOHMANN_JSON_PASTE29(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE28(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28) +#define NLOHMANN_JSON_PASTE30(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE29(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29) +#define NLOHMANN_JSON_PASTE31(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE30(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30) +#define NLOHMANN_JSON_PASTE32(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE31(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31) +#define NLOHMANN_JSON_PASTE33(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE32(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32) +#define NLOHMANN_JSON_PASTE34(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE33(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33) +#define NLOHMANN_JSON_PASTE35(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE34(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34) +#define NLOHMANN_JSON_PASTE36(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE35(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35) +#define NLOHMANN_JSON_PASTE37(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE36(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36) +#define NLOHMANN_JSON_PASTE38(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE37(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37) +#define NLOHMANN_JSON_PASTE39(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE38(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38) +#define NLOHMANN_JSON_PASTE40(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE39(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39) +#define NLOHMANN_JSON_PASTE41(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE40(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40) +#define NLOHMANN_JSON_PASTE42(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE41(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41) +#define NLOHMANN_JSON_PASTE43(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE42(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42) +#define NLOHMANN_JSON_PASTE44(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE43(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43) +#define NLOHMANN_JSON_PASTE45(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE44(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44) +#define NLOHMANN_JSON_PASTE46(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE45(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45) +#define NLOHMANN_JSON_PASTE47(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE46(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46) +#define NLOHMANN_JSON_PASTE48(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE47(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47) +#define NLOHMANN_JSON_PASTE49(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE48(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48) +#define NLOHMANN_JSON_PASTE50(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE49(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49) +#define NLOHMANN_JSON_PASTE51(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE50(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50) +#define NLOHMANN_JSON_PASTE52(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE51(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51) +#define NLOHMANN_JSON_PASTE53(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE52(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52) +#define NLOHMANN_JSON_PASTE54(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE53(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53) +#define NLOHMANN_JSON_PASTE55(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE54(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54) +#define NLOHMANN_JSON_PASTE56(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE55(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55) +#define NLOHMANN_JSON_PASTE57(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE56(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56) +#define NLOHMANN_JSON_PASTE58(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE57(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57) +#define NLOHMANN_JSON_PASTE59(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE58(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58) +#define NLOHMANN_JSON_PASTE60(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE59(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59) +#define NLOHMANN_JSON_PASTE61(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE60(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60) +#define NLOHMANN_JSON_PASTE62(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE61(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61) +#define NLOHMANN_JSON_PASTE63(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE62(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62) +#define NLOHMANN_JSON_PASTE64(func, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE63(func, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49, v50, v51, v52, v53, v54, v55, v56, v57, v58, v59, v60, v61, v62, v63) + +#define NLOHMANN_JSON_TO(v1) nlohmann_json_j[#v1] = nlohmann_json_t.v1; +#define NLOHMANN_JSON_FROM(v1) nlohmann_json_j.at(#v1).get_to(nlohmann_json_t.v1); + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type, ...) \ + friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +/*! +@brief macro +@def NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE +@since version 3.9.0 +*/ +#define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(Type, ...) \ + inline void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_TO, __VA_ARGS__)) } \ + inline void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t) { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + +#ifndef JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_USE_IMPLICIT_CONVERSIONS 1 +#endif + +#if JSON_USE_IMPLICIT_CONVERSIONS + #define JSON_EXPLICIT +#else + #define JSON_EXPLICIT explicit +#endif + +#ifndef JSON_DIAGNOSTICS + #define JSON_DIAGNOSTICS 0 +#endif + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief replace all occurrences of a substring by another string + +@param[in,out] s the string to manipulate; changed so that all + occurrences of @a f are replaced with @a t +@param[in] f the substring to replace with @a t +@param[in] t the string to replace @a f + +@pre The search string @a f must not be empty. **This precondition is +enforced with an assertion.** + +@since version 2.0.0 +*/ +inline void replace_substring(std::string& s, const std::string& f, + const std::string& t) +{ + JSON_ASSERT(!f.empty()); + for (auto pos = s.find(f); // find first occurrence of f + pos != std::string::npos; // make sure f was found + s.replace(pos, f.size(), t), // replace with t, and + pos = s.find(f, pos + t.size())) // find next occurrence of f + {} +} + +/*! + * @brief string escaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to escape + * @return escaped string + * + * Note the order of escaping "~" to "~0" and "/" to "~1" is important. + */ +inline std::string escape(std::string s) +{ + replace_substring(s, "~", "~0"); + replace_substring(s, "/", "~1"); + return s; +} + +/*! + * @brief string unescaping as described in RFC 6901 (Sect. 4) + * @param[in] s string to unescape + * @return unescaped string + * + * Note the order of escaping "~1" to "/" and "~0" to "~" is important. + */ +static void unescape(std::string& s) +{ + replace_substring(s, "~1", "/"); + replace_substring(s, "~0", "~"); +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // size_t + +namespace nlohmann +{ +namespace detail +{ +/// struct to capture the start position of the current token +struct position_t +{ + /// the total number of characters read + std::size_t chars_read_total = 0; + /// the number of characters read in the current line + std::size_t chars_read_current_line = 0; + /// the number of lines read + std::size_t lines_read = 0; + + /// conversion to size_t to preserve SAX interface + constexpr operator size_t() const + { + return chars_read_total; + } +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////////// +// exceptions // +//////////////// + +/*! +@brief general exception of the @ref basic_json class + +This class is an extension of `std::exception` objects with a member @a id for +exception ids. It is used as the base class for all exceptions thrown by the +@ref basic_json class. This class can hence be used as "wildcard" to catch +exceptions. + +Subclasses: +- @ref parse_error for exceptions indicating a parse error +- @ref invalid_iterator for exceptions indicating errors with iterators +- @ref type_error for exceptions indicating executing a member function with + a wrong type +- @ref out_of_range for exceptions indicating access out of the defined range +- @ref other_error for exceptions indicating other library errors + +@internal +@note To have nothrow-copy-constructible exceptions, we internally use + `std::runtime_error` which can cope with arbitrary-length error messages. + Intermediate strings are built with static functions and then passed to + the actual constructor. +@endinternal + +@liveexample{The following code shows how arbitrary library exceptions can be +caught.,exception} + +@since version 3.0.0 +*/ +class exception : public std::exception +{ + public: + /// returns the explanatory string + const char* what() const noexcept override + { + return m.what(); + } + + /// the id of the exception + const int id; // NOLINT(cppcoreguidelines-non-private-member-variables-in-classes) + + protected: + JSON_HEDLEY_NON_NULL(3) + exception(int id_, const char* what_arg) : id(id_), m(what_arg) {} + + static std::string name(const std::string& ename, int id_) + { + return "[json.exception." + ename + "." + std::to_string(id_) + "] "; + } + + template + static std::string diagnostics(const BasicJsonType& leaf_element) + { +#if JSON_DIAGNOSTICS + std::vector tokens; + for (const auto* current = &leaf_element; current->m_parent != nullptr; current = current->m_parent) + { + switch (current->m_parent->type()) + { + case value_t::array: + { + for (std::size_t i = 0; i < current->m_parent->m_value.array->size(); ++i) + { + if (¤t->m_parent->m_value.array->operator[](i) == current) + { + tokens.emplace_back(std::to_string(i)); + break; + } + } + break; + } + + case value_t::object: + { + for (const auto& element : *current->m_parent->m_value.object) + { + if (&element.second == current) + { + tokens.emplace_back(element.first.c_str()); + break; + } + } + break; + } + + case value_t::null: // LCOV_EXCL_LINE + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + break; // LCOV_EXCL_LINE + } + } + + if (tokens.empty()) + { + return ""; + } + + return "(" + std::accumulate(tokens.rbegin(), tokens.rend(), std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }) + ") "; +#else + static_cast(leaf_element); + return ""; +#endif + } + + private: + /// an exception object as storage for error messages + std::runtime_error m; +}; + +/*! +@brief exception indicating a parse error + +This exception is thrown by the library when a parse error occurs. Parse errors +can occur during the deserialization of JSON text, CBOR, MessagePack, as well +as when using JSON Patch. + +Member @a byte holds the byte index of the last read character in the input +file. + +Exceptions have ids 1xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.parse_error.101 | parse error at 2: unexpected end of input; expected string literal | This error indicates a syntax error while deserializing a JSON text. The error message describes that an unexpected token (character) was encountered, and the member @a byte indicates the error position. +json.exception.parse_error.102 | parse error at 14: missing or wrong low surrogate | JSON uses the `\uxxxx` format to describe Unicode characters. Code points above above 0xFFFF are split into two `\uxxxx` entries ("surrogate pairs"). This error indicates that the surrogate pair is incomplete or contains an invalid code point. +json.exception.parse_error.103 | parse error: code points above 0x10FFFF are invalid | Unicode supports code points up to 0x10FFFF. Code points above 0x10FFFF are invalid. +json.exception.parse_error.104 | parse error: JSON patch must be an array of objects | [RFC 6902](https://tools.ietf.org/html/rfc6902) requires a JSON Patch document to be a JSON document that represents an array of objects. +json.exception.parse_error.105 | parse error: operation must have string member 'op' | An operation of a JSON Patch document must contain exactly one "op" member, whose value indicates the operation to perform. Its value must be one of "add", "remove", "replace", "move", "copy", or "test"; other values are errors. +json.exception.parse_error.106 | parse error: array index '01' must not begin with '0' | An array index in a JSON Pointer ([RFC 6901](https://tools.ietf.org/html/rfc6901)) may be `0` or any number without a leading `0`. +json.exception.parse_error.107 | parse error: JSON pointer must be empty or begin with '/' - was: 'foo' | A JSON Pointer must be a Unicode string containing a sequence of zero or more reference tokens, each prefixed by a `/` character. +json.exception.parse_error.108 | parse error: escape character '~' must be followed with '0' or '1' | In a JSON Pointer, only `~0` and `~1` are valid escape sequences. +json.exception.parse_error.109 | parse error: array index 'one' is not a number | A JSON Pointer array index must be a number. +json.exception.parse_error.110 | parse error at 1: cannot read 2 bytes from vector | When parsing CBOR or MessagePack, the byte vector ends before the complete value has been read. +json.exception.parse_error.112 | parse error at 1: error reading CBOR; last byte: 0xF8 | Not all types of CBOR or MessagePack are supported. This exception occurs if an unsupported byte was read. +json.exception.parse_error.113 | parse error at 2: expected a CBOR string; last byte: 0x98 | While parsing a map key, a value that is not a string has been read. +json.exception.parse_error.114 | parse error: Unsupported BSON record type 0x0F | The parsing of the corresponding BSON record type is not implemented (yet). +json.exception.parse_error.115 | parse error at byte 5: syntax error while parsing UBJSON high-precision number: invalid number text: 1A | A UBJSON high-precision number could not be parsed. + +@note For an input with n bytes, 1 is the index of the first character and n+1 + is the index of the terminating null byte or the end of file. This also + holds true when reading a byte vector (CBOR or MessagePack). + +@liveexample{The following code shows how a `parse_error` exception can be +caught.,parse_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class parse_error : public exception +{ + public: + /*! + @brief create a parse error exception + @param[in] id_ the id of the exception + @param[in] pos the position where the error occurred (or with + chars_read_total=0 if the position cannot be + determined) + @param[in] what_arg the explanatory string + @return parse_error object + */ + template + static parse_error create(int id_, const position_t& pos, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + position_string(pos) + ": " + exception::diagnostics(context) + what_arg; + return parse_error(id_, pos.chars_read_total, w.c_str()); + } + + template + static parse_error create(int id_, std::size_t byte_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("parse_error", id_) + "parse error" + + (byte_ != 0 ? (" at byte " + std::to_string(byte_)) : "") + + ": " + exception::diagnostics(context) + what_arg; + return parse_error(id_, byte_, w.c_str()); + } + + /*! + @brief byte index of the parse error + + The byte index of the last read character in the input file. + + @note For an input with n bytes, 1 is the index of the first character and + n+1 is the index of the terminating null byte or the end of file. + This also holds true when reading a byte vector (CBOR or MessagePack). + */ + const std::size_t byte; + + private: + parse_error(int id_, std::size_t byte_, const char* what_arg) + : exception(id_, what_arg), byte(byte_) {} + + static std::string position_string(const position_t& pos) + { + return " at line " + std::to_string(pos.lines_read + 1) + + ", column " + std::to_string(pos.chars_read_current_line); + } +}; + +/*! +@brief exception indicating errors with iterators + +This exception is thrown if iterators passed to a library function do not match +the expected semantics. + +Exceptions have ids 2xx. + +name / id | example message | description +----------------------------------- | --------------- | ------------------------- +json.exception.invalid_iterator.201 | iterators are not compatible | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.202 | iterator does not fit current value | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion. +json.exception.invalid_iterator.203 | iterators do not fit current value | Either iterator passed to function @ref erase(IteratorType first, IteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from. +json.exception.invalid_iterator.204 | iterators out of range | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid. +json.exception.invalid_iterator.205 | iterator out of range | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid. +json.exception.invalid_iterator.206 | cannot construct with iterators from null | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range. +json.exception.invalid_iterator.207 | cannot use key() for non-object iterators | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key. +json.exception.invalid_iterator.208 | cannot use operator[] for object iterators | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.209 | cannot use offsets with object iterators | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered. +json.exception.invalid_iterator.210 | iterators do not fit | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid. +json.exception.invalid_iterator.211 | passed iterators may not belong to container | The iterator range passed to the insert function must not be a subrange of the container to insert to. +json.exception.invalid_iterator.212 | cannot compare iterators of different containers | When two iterators are compared, they must belong to the same container. +json.exception.invalid_iterator.213 | cannot compare order of object iterators | The order of object iterators cannot be compared, because JSON objects are unordered. +json.exception.invalid_iterator.214 | cannot get value | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin(). + +@liveexample{The following code shows how an `invalid_iterator` exception can be +caught.,invalid_iterator} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class invalid_iterator : public exception +{ + public: + template + static invalid_iterator create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("invalid_iterator", id_) + exception::diagnostics(context) + what_arg; + return invalid_iterator(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + invalid_iterator(int id_, const char* what_arg) + : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating executing a member function with a wrong type + +This exception is thrown in case of a type error; that is, a library function is +executed on a JSON value whose type does not match the expected semantics. + +Exceptions have ids 3xx. + +name / id | example message | description +----------------------------- | --------------- | ------------------------- +json.exception.type_error.301 | cannot create object from initializer list | To create an object from an initializer list, the initializer list must consist only of a list of pairs whose first element is a string. When this constraint is violated, an array is created instead. +json.exception.type_error.302 | type must be object, but is array | During implicit or explicit value conversion, the JSON type must be compatible to the target type. For instance, a JSON string can only be converted into string types, but not into numbers or boolean types. +json.exception.type_error.303 | incompatible ReferenceType for get_ref, actual type is object | To retrieve a reference to a value stored in a @ref basic_json object with @ref get_ref, the type of the reference must match the value type. For instance, for a JSON array, the @a ReferenceType must be @ref array_t &. +json.exception.type_error.304 | cannot use at() with string | The @ref at() member functions can only be executed for certain JSON types. +json.exception.type_error.305 | cannot use operator[] with string | The @ref operator[] member functions can only be executed for certain JSON types. +json.exception.type_error.306 | cannot use value() with string | The @ref value() member functions can only be executed for certain JSON types. +json.exception.type_error.307 | cannot use erase() with string | The @ref erase() member functions can only be executed for certain JSON types. +json.exception.type_error.308 | cannot use push_back() with string | The @ref push_back() and @ref operator+= member functions can only be executed for certain JSON types. +json.exception.type_error.309 | cannot use insert() with | The @ref insert() member functions can only be executed for certain JSON types. +json.exception.type_error.310 | cannot use swap() with number | The @ref swap() member functions can only be executed for certain JSON types. +json.exception.type_error.311 | cannot use emplace_back() with string | The @ref emplace_back() member function can only be executed for certain JSON types. +json.exception.type_error.312 | cannot use update() with string | The @ref update() member functions can only be executed for certain JSON types. +json.exception.type_error.313 | invalid value to unflatten | The @ref unflatten function converts an object whose keys are JSON Pointers back into an arbitrary nested JSON value. The JSON Pointers must not overlap, because then the resulting value would not be well defined. +json.exception.type_error.314 | only objects can be unflattened | The @ref unflatten function only works for an object whose keys are JSON Pointers. +json.exception.type_error.315 | values in object must be primitive | The @ref unflatten function only works for an object whose keys are JSON Pointers and whose values are primitive. +json.exception.type_error.316 | invalid UTF-8 byte at index 10: 0x7E | The @ref dump function only works with UTF-8 encoded strings; that is, if you assign a `std::string` to a JSON value, make sure it is UTF-8 encoded. | +json.exception.type_error.317 | JSON value cannot be serialized to requested format | The dynamic type of the object cannot be represented in the requested serialization format (e.g. a raw `true` or `null` JSON object cannot be serialized to BSON) | + +@liveexample{The following code shows how a `type_error` exception can be +caught.,type_error} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref out_of_range for exceptions indicating access out of the defined range +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class type_error : public exception +{ + public: + template + static type_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("type_error", id_) + exception::diagnostics(context) + what_arg; + return type_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + type_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating access out of the defined range + +This exception is thrown in case a library function is called on an input +parameter that exceeds the expected range, for instance in case of array +indices or nonexisting object keys. + +Exceptions have ids 4xx. + +name / id | example message | description +------------------------------- | --------------- | ------------------------- +json.exception.out_of_range.401 | array index 3 is out of range | The provided array index @a i is larger than @a size-1. +json.exception.out_of_range.402 | array index '-' (3) is out of range | The special array index `-` in a JSON Pointer never describes a valid element of the array, but the index past the end. That is, it can only be used to add elements at this position, but not to read it. +json.exception.out_of_range.403 | key 'foo' not found | The provided key was not found in the JSON object. +json.exception.out_of_range.404 | unresolved reference token 'foo' | A reference token in a JSON Pointer could not be resolved. +json.exception.out_of_range.405 | JSON pointer has no parent | The JSON Patch operations 'remove' and 'add' can not be applied to the root element of the JSON value. +json.exception.out_of_range.406 | number overflow parsing '10E1000' | A parsed number could not be stored as without changing it to NaN or INF. +json.exception.out_of_range.407 | number overflow serializing '9223372036854775808' | UBJSON and BSON only support integer numbers up to 9223372036854775807. (until version 3.8.0) | +json.exception.out_of_range.408 | excessive array size: 8658170730974374167 | The size (following `#`) of an UBJSON array or object exceeds the maximal capacity. | +json.exception.out_of_range.409 | BSON key cannot contain code point U+0000 (at byte 2) | Key identifiers to be serialized to BSON cannot contain code point U+0000, since the key is stored as zero-terminated c-string | + +@liveexample{The following code shows how an `out_of_range` exception can be +caught.,out_of_range} + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref other_error for exceptions indicating other library errors + +@since version 3.0.0 +*/ +class out_of_range : public exception +{ + public: + template + static out_of_range create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("out_of_range", id_) + exception::diagnostics(context) + what_arg; + return out_of_range(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + out_of_range(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; + +/*! +@brief exception indicating other library errors + +This exception is thrown in case of errors that cannot be classified with the +other exception types. + +Exceptions have ids 5xx. + +name / id | example message | description +------------------------------ | --------------- | ------------------------- +json.exception.other_error.501 | unsuccessful: {"op":"test","path":"/baz", "value":"bar"} | A JSON Patch operation 'test' failed. The unsuccessful operation is also printed. + +@sa - @ref exception for the base class of the library exceptions +@sa - @ref parse_error for exceptions indicating a parse error +@sa - @ref invalid_iterator for exceptions indicating errors with iterators +@sa - @ref type_error for exceptions indicating executing a member function with + a wrong type +@sa - @ref out_of_range for exceptions indicating access out of the defined range + +@liveexample{The following code shows how an `other_error` exception can be +caught.,other_error} + +@since version 3.0.0 +*/ +class other_error : public exception +{ + public: + template + static other_error create(int id_, const std::string& what_arg, const BasicJsonType& context) + { + std::string w = exception::name("other_error", id_) + exception::diagnostics(context) + what_arg; + return other_error(id_, w.c_str()); + } + + private: + JSON_HEDLEY_NON_NULL(3) + other_error(int id_, const char* what_arg) : exception(id_, what_arg) {} +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // conditional, enable_if, false_type, integral_constant, is_constructible, is_integral, is_same, remove_cv, remove_reference, true_type +#include // index_sequence, make_index_sequence, index_sequence_for + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +template +using uncvref_t = typename std::remove_cv::type>::type; + +#ifdef JSON_HAS_CPP_14 + +// the following utilities are natively available in C++14 +using std::enable_if_t; +using std::index_sequence; +using std::make_index_sequence; +using std::index_sequence_for; + +#else + +// alias templates to reduce boilerplate +template +using enable_if_t = typename std::enable_if::type; + +// The following code is taken from https://github.com/abseil/abseil-cpp/blob/10cb35e459f5ecca5b2ff107635da0bfa41011b4/absl/utility/utility.h +// which is part of Google Abseil (https://github.com/abseil/abseil-cpp), licensed under the Apache License 2.0. + +//// START OF CODE FROM GOOGLE ABSEIL + +// integer_sequence +// +// Class template representing a compile-time integer sequence. An instantiation +// of `integer_sequence` has a sequence of integers encoded in its +// type through its template arguments (which is a common need when +// working with C++11 variadic templates). `absl::integer_sequence` is designed +// to be a drop-in replacement for C++14's `std::integer_sequence`. +// +// Example: +// +// template< class T, T... Ints > +// void user_function(integer_sequence); +// +// int main() +// { +// // user_function's `T` will be deduced to `int` and `Ints...` +// // will be deduced to `0, 1, 2, 3, 4`. +// user_function(make_integer_sequence()); +// } +template +struct integer_sequence +{ + using value_type = T; + static constexpr std::size_t size() noexcept + { + return sizeof...(Ints); + } +}; + +// index_sequence +// +// A helper template for an `integer_sequence` of `size_t`, +// `absl::index_sequence` is designed to be a drop-in replacement for C++14's +// `std::index_sequence`. +template +using index_sequence = integer_sequence; + +namespace utility_internal +{ + +template +struct Extend; + +// Note that SeqSize == sizeof...(Ints). It's passed explicitly for efficiency. +template +struct Extend, SeqSize, 0> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)... >; +}; + +template +struct Extend, SeqSize, 1> +{ + using type = integer_sequence < T, Ints..., (Ints + SeqSize)..., 2 * SeqSize >; +}; + +// Recursion helper for 'make_integer_sequence'. +// 'Gen::type' is an alias for 'integer_sequence'. +template +struct Gen +{ + using type = + typename Extend < typename Gen < T, N / 2 >::type, N / 2, N % 2 >::type; +}; + +template +struct Gen +{ + using type = integer_sequence; +}; + +} // namespace utility_internal + +// Compile-time sequences of integers + +// make_integer_sequence +// +// This template alias is equivalent to +// `integer_sequence`, and is designed to be a drop-in +// replacement for C++14's `std::make_integer_sequence`. +template +using make_integer_sequence = typename utility_internal::Gen::type; + +// make_index_sequence +// +// This template alias is equivalent to `index_sequence<0, 1, ..., N-1>`, +// and is designed to be a drop-in replacement for C++14's +// `std::make_index_sequence`. +template +using make_index_sequence = make_integer_sequence; + +// index_sequence_for +// +// Converts a typename pack into an index sequence of the same length, and +// is designed to be a drop-in replacement for C++14's +// `std::index_sequence_for()` +template +using index_sequence_for = make_index_sequence; + +//// END OF CODE FROM GOOGLE ABSEIL + +#endif + +// dispatch utility (taken from ranges-v3) +template struct priority_tag : priority_tag < N - 1 > {}; +template<> struct priority_tag<0> {}; + +// taken from ranges-v3 +template +struct static_const +{ + static constexpr T value{}; +}; + +template +constexpr T static_const::value; + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// dispatching helper struct +template struct identity_tag {}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // numeric_limits +#include // false_type, is_constructible, is_integral, is_same, true_type +#include // declval +#include // tuple + +// #include + + +#include // random_access_iterator_tag + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template struct make_void +{ + using type = void; +}; +template using void_t = typename make_void::type; +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +struct iterator_types {}; + +template +struct iterator_types < + It, + void_t> +{ + using difference_type = typename It::difference_type; + using value_type = typename It::value_type; + using pointer = typename It::pointer; + using reference = typename It::reference; + using iterator_category = typename It::iterator_category; +}; + +// This is required as some compilers implement std::iterator_traits in a way that +// doesn't work with SFINAE. See https://github.com/nlohmann/json/issues/1341. +template +struct iterator_traits +{ +}; + +template +struct iterator_traits < T, enable_if_t < !std::is_pointer::value >> + : iterator_types +{ +}; + +template +struct iterator_traits::value>> +{ + using iterator_category = std::random_access_iterator_tag; + using value_type = T; + using difference_type = ptrdiff_t; + using pointer = T*; + using reference = T&; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include + +// #include + + +// https://en.cppreference.com/w/cpp/experimental/is_detected +namespace nlohmann +{ +namespace detail +{ +struct nonesuch +{ + nonesuch() = delete; + ~nonesuch() = delete; + nonesuch(nonesuch const&) = delete; + nonesuch(nonesuch const&&) = delete; + void operator=(nonesuch const&) = delete; + void operator=(nonesuch&&) = delete; +}; + +template class Op, + class... Args> +struct detector +{ + using value_t = std::false_type; + using type = Default; +}; + +template class Op, class... Args> +struct detector>, Op, Args...> +{ + using value_t = std::true_type; + using type = Op; +}; + +template class Op, class... Args> +using is_detected = typename detector::value_t; + +template class Op, class... Args> +struct is_detected_lazy : is_detected { }; + +template class Op, class... Args> +using detected_t = typename detector::type; + +template class Op, class... Args> +using detected_or = detector; + +template class Op, class... Args> +using detected_or_t = typename detected_or::type; + +template class Op, class... Args> +using is_detected_exact = std::is_same>; + +template class Op, class... Args> +using is_detected_convertible = + std::is_convertible, To>; +} // namespace detail +} // namespace nlohmann + +// #include +#ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ + +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ +/*! +@brief default JSONSerializer template argument + +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector> +class basic_json; + +/*! +@brief JSON Pointer + +A JSON pointer defines a string syntax for identifying a specific value +within a JSON document. It can be used with functions `at` and +`operator[]`. Furthermore, JSON pointers are the base for JSON patches. + +@sa [RFC 6901](https://tools.ietf.org/html/rfc6901) + +@since version 2.0.0 +*/ +template +class json_pointer; + +/*! +@brief default JSON class + +This type is the default specialization of the @ref basic_json class which +uses the standard template types. + +@since version 1.0.0 +*/ +using json = basic_json<>; + +template +struct ordered_map; + +/*! +@brief ordered JSON class + +This type preserves the insertion order of object keys. + +@since version 3.9.0 +*/ +using ordered_json = basic_json; + +} // namespace nlohmann + +#endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ + + +namespace nlohmann +{ +/*! +@brief detail namespace with internal helper functions + +This namespace collects functions that should not be exposed, +implementations of some @ref basic_json methods, and meta-programming helpers. + +@since version 2.1.0 +*/ +namespace detail +{ +///////////// +// helpers // +///////////// + +// Note to maintainers: +// +// Every trait in this file expects a non CV-qualified type. +// The only exceptions are in the 'aliases for detected' section +// (i.e. those of the form: decltype(T::member_function(std::declval()))) +// +// In this case, T has to be properly CV-qualified to constraint the function arguments +// (e.g. to_json(BasicJsonType&, const T&)) + +template struct is_basic_json : std::false_type {}; + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +struct is_basic_json : std::true_type {}; + +////////////////////// +// json_ref helpers // +////////////////////// + +template +class json_ref; + +template +struct is_json_ref : std::false_type {}; + +template +struct is_json_ref> : std::true_type {}; + +////////////////////////// +// aliases for detected // +////////////////////////// + +template +using mapped_type_t = typename T::mapped_type; + +template +using key_type_t = typename T::key_type; + +template +using value_type_t = typename T::value_type; + +template +using difference_type_t = typename T::difference_type; + +template +using pointer_t = typename T::pointer; + +template +using reference_t = typename T::reference; + +template +using iterator_category_t = typename T::iterator_category; + +template +using iterator_t = typename T::iterator; + +template +using to_json_function = decltype(T::to_json(std::declval()...)); + +template +using from_json_function = decltype(T::from_json(std::declval()...)); + +template +using get_template_function = decltype(std::declval().template get()); + +// trait checking if JSONSerializer::from_json(json const&, udt&) exists +template +struct has_from_json : std::false_type {}; + +// trait checking if j.get is valid +// use this trait instead of std::is_constructible or std::is_convertible, +// both rely on, or make use of implicit conversions, and thus fail when T +// has several constructors/operator= (see https://github.com/nlohmann/json/issues/958) +template +struct is_getable +{ + static constexpr bool value = is_detected::value; +}; + +template +struct has_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if JSONSerializer::from_json(json const&) exists +// this overload is used for non-default-constructible user-defined-types +template +struct has_non_default_from_json : std::false_type {}; + +template +struct has_non_default_from_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + +// This trait checks if BasicJsonType::json_serializer::to_json exists +// Do not evaluate the trait when T is a basic_json type, to avoid template instantiation infinite recursion. +template +struct has_to_json : std::false_type {}; + +template +struct has_to_json < BasicJsonType, T, enable_if_t < !is_basic_json::value >> +{ + using serializer = typename BasicJsonType::template json_serializer; + + static constexpr bool value = + is_detected_exact::value; +}; + + +/////////////////// +// is_ functions // +/////////////////// + +// https://en.cppreference.com/w/cpp/types/conjunction +template struct conjunction : std::true_type { }; +template struct conjunction : B1 { }; +template +struct conjunction +: std::conditional, B1>::type {}; + +// https://en.cppreference.com/w/cpp/types/negation +template struct negation : std::integral_constant < bool, !B::value > { }; + +// Reimplementation of is_constructible and is_default_constructible, due to them being broken for +// std::pair and std::tuple until LWG 2367 fix (see https://cplusplus.github.io/LWG/lwg-defects.html#2367). +// This causes compile errors in e.g. clang 3.5 or gcc 4.9. +template +struct is_default_constructible : std::is_default_constructible {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction, is_default_constructible> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + +template +struct is_default_constructible> + : conjunction...> {}; + + +template +struct is_constructible : std::is_constructible {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + +template +struct is_constructible> : is_default_constructible> {}; + + +template +struct is_iterator_traits : std::false_type {}; + +template +struct is_iterator_traits> +{ + private: + using traits = iterator_traits; + + public: + static constexpr auto value = + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value && + is_detected::value; +}; + +// The following implementation of is_complete_type is taken from +// https://blogs.msdn.microsoft.com/vcblog/2015/12/02/partial-support-for-expression-sfinae-in-vs-2015-update-1/ +// and is written by Xiang Fan who agreed to using it in this library. + +template +struct is_complete_type : std::false_type {}; + +template +struct is_complete_type : std::true_type {}; + +template +struct is_compatible_object_type_impl : std::false_type {}; + +template +struct is_compatible_object_type_impl < + BasicJsonType, CompatibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + // macOS's is_constructible does not play well with nonesuch... + static constexpr bool value = + is_constructible::value && + is_constructible::value; +}; + +template +struct is_compatible_object_type + : is_compatible_object_type_impl {}; + +template +struct is_constructible_object_type_impl : std::false_type {}; + +template +struct is_constructible_object_type_impl < + BasicJsonType, ConstructibleObjectType, + enable_if_t < is_detected::value&& + is_detected::value >> +{ + using object_t = typename BasicJsonType::object_t; + + static constexpr bool value = + (is_default_constructible::value && + (std::is_move_assignable::value || + std::is_copy_assignable::value) && + (is_constructible::value && + std::is_same < + typename object_t::mapped_type, + typename ConstructibleObjectType::mapped_type >::value)) || + (has_from_json::value || + has_non_default_from_json < + BasicJsonType, + typename ConstructibleObjectType::mapped_type >::value); +}; + +template +struct is_constructible_object_type + : is_constructible_object_type_impl {}; + +template +struct is_compatible_string_type_impl : std::false_type {}; + +template +struct is_compatible_string_type_impl < + BasicJsonType, CompatibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_compatible_string_type + : is_compatible_string_type_impl {}; + +template +struct is_constructible_string_type_impl : std::false_type {}; + +template +struct is_constructible_string_type_impl < + BasicJsonType, ConstructibleStringType, + enable_if_t::value >> +{ + static constexpr auto value = + is_constructible::value; +}; + +template +struct is_constructible_string_type + : is_constructible_string_type_impl {}; + +template +struct is_compatible_array_type_impl : std::false_type {}; + +template +struct is_compatible_array_type_impl < + BasicJsonType, CompatibleArrayType, + enable_if_t < is_detected::value&& + is_detected::value&& +// This is needed because json_reverse_iterator has a ::iterator type... +// Therefore it is detected as a CompatibleArrayType. +// The real fix would be to have an Iterable concept. + !is_iterator_traits < + iterator_traits>::value >> +{ + static constexpr bool value = + is_constructible::value; +}; + +template +struct is_compatible_array_type + : is_compatible_array_type_impl {}; + +template +struct is_constructible_array_type_impl : std::false_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t::value >> + : std::true_type {}; + +template +struct is_constructible_array_type_impl < + BasicJsonType, ConstructibleArrayType, + enable_if_t < !std::is_same::value&& + is_default_constructible::value&& +(std::is_move_assignable::value || + std::is_copy_assignable::value)&& +is_detected::value&& +is_detected::value&& +is_complete_type < +detected_t>::value >> +{ + static constexpr bool value = + // This is needed because json_reverse_iterator has a ::iterator type, + // furthermore, std::back_insert_iterator (and other iterators) have a + // base class `iterator`... Therefore it is detected as a + // ConstructibleArrayType. The real fix would be to have an Iterable + // concept. + !is_iterator_traits>::value && + + (std::is_same::value || + has_from_json::value || + has_non_default_from_json < + BasicJsonType, typename ConstructibleArrayType::value_type >::value); +}; + +template +struct is_constructible_array_type + : is_constructible_array_type_impl {}; + +template +struct is_compatible_integer_type_impl : std::false_type {}; + +template +struct is_compatible_integer_type_impl < + RealIntegerType, CompatibleNumberIntegerType, + enable_if_t < std::is_integral::value&& + std::is_integral::value&& + !std::is_same::value >> +{ + // is there an assert somewhere on overflows? + using RealLimits = std::numeric_limits; + using CompatibleLimits = std::numeric_limits; + + static constexpr auto value = + is_constructible::value && + CompatibleLimits::is_integer && + RealLimits::is_signed == CompatibleLimits::is_signed; +}; + +template +struct is_compatible_integer_type + : is_compatible_integer_type_impl {}; + +template +struct is_compatible_type_impl: std::false_type {}; + +template +struct is_compatible_type_impl < + BasicJsonType, CompatibleType, + enable_if_t::value >> +{ + static constexpr bool value = + has_to_json::value; +}; + +template +struct is_compatible_type + : is_compatible_type_impl {}; + +template +struct is_constructible_tuple : std::false_type {}; + +template +struct is_constructible_tuple> : conjunction...> {}; + +// a naive helper to check if a type is an ordered_map (exploits the fact that +// ordered_map inherits capacity() from std::vector) +template +struct is_ordered_map +{ + using one = char; + + struct two + { + char x[2]; // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + }; + + template static one test( decltype(&C::capacity) ) ; + template static two test(...); + + enum { value = sizeof(test(nullptr)) == sizeof(char) }; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) +}; + +// to avoid useless casts (see https://github.com/nlohmann/json/issues/2893#issuecomment-889152324) +template < typename T, typename U, enable_if_t < !std::is_same::value, int > = 0 > +T conditional_static_cast(U value) +{ + return static_cast(value); +} + +template::value, int> = 0> +T conditional_static_cast(U value) +{ + return value; +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void from_json(const BasicJsonType& j, typename std::nullptr_t& n) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_null())) + { + JSON_THROW(type_error::create(302, "type must be null, but is " + std::string(j.type_name()), j)); + } + n = nullptr; +} + +// overloads for basic_json template parameters +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < std::is_arithmetic::value&& + !std::is_same::value, + int > = 0 > +void get_arithmetic_value(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::boolean_t& b) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_boolean())) + { + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(j.type_name()), j)); + } + b = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::string_t& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + s = *j.template get_ptr(); +} + +template < + typename BasicJsonType, typename ConstructibleStringType, + enable_if_t < + is_constructible_string_type::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ConstructibleStringType& s) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_string())) + { + JSON_THROW(type_error::create(302, "type must be string, but is " + std::string(j.type_name()), j)); + } + + s = *j.template get_ptr(); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_float_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_unsigned_t& val) +{ + get_arithmetic_value(j, val); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::number_integer_t& val) +{ + get_arithmetic_value(j, val); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, EnumType& e) +{ + typename std::underlying_type::type val; + get_arithmetic_value(j, val); + e = static_cast(val); +} + +// forward_list doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::forward_list& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.clear(); + std::transform(j.rbegin(), j.rend(), + std::front_inserter(l), [](const BasicJsonType & i) + { + return i.template get(); + }); +} + +// valarray doesn't have an insert method +template::value, int> = 0> +void from_json(const BasicJsonType& j, std::valarray& l) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + l.resize(j.size()); + std::transform(j.begin(), j.end(), std::begin(l), + [](const BasicJsonType & elem) + { + return elem.template get(); + }); +} + +template +auto from_json(const BasicJsonType& j, T (&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template +void from_json_array_impl(const BasicJsonType& j, typename BasicJsonType::array_t& arr, priority_tag<3> /*unused*/) +{ + arr = *j.template get_ptr(); +} + +template +auto from_json_array_impl(const BasicJsonType& j, std::array& arr, + priority_tag<2> /*unused*/) +-> decltype(j.template get(), void()) +{ + for (std::size_t i = 0; i < N; ++i) + { + arr[i] = j.at(i).template get(); + } +} + +template::value, + int> = 0> +auto from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, priority_tag<1> /*unused*/) +-> decltype( + arr.reserve(std::declval()), + j.template get(), + void()) +{ + using std::end; + + ConstructibleArrayType ret; + ret.reserve(j.size()); + std::transform(j.begin(), j.end(), + std::inserter(ret, end(ret)), [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template::value, + int> = 0> +void from_json_array_impl(const BasicJsonType& j, ConstructibleArrayType& arr, + priority_tag<0> /*unused*/) +{ + using std::end; + + ConstructibleArrayType ret; + std::transform( + j.begin(), j.end(), std::inserter(ret, end(ret)), + [](const BasicJsonType & i) + { + // get() returns *this, this won't call a from_json + // method when value_type is BasicJsonType + return i.template get(); + }); + arr = std::move(ret); +} + +template < typename BasicJsonType, typename ConstructibleArrayType, + enable_if_t < + is_constructible_array_type::value&& + !is_constructible_object_type::value&& + !is_constructible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +auto from_json(const BasicJsonType& j, ConstructibleArrayType& arr) +-> decltype(from_json_array_impl(j, arr, priority_tag<3> {}), +j.template get(), +void()) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + from_json_array_impl(j, arr, priority_tag<3> {}); +} + +template < typename BasicJsonType, typename T, std::size_t... Idx > +std::array from_json_inplace_array_impl(BasicJsonType&& j, + identity_tag> /*unused*/, index_sequence /*unused*/) +{ + return { { std::forward(j).at(Idx).template get()... } }; +} + +template < typename BasicJsonType, typename T, std::size_t N > +auto from_json(BasicJsonType&& j, identity_tag> tag) +-> decltype(from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_inplace_array_impl(std::forward(j), tag, make_index_sequence {}); +} + +template +void from_json(const BasicJsonType& j, typename BasicJsonType::binary_t& bin) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_binary())) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(j.type_name()), j)); + } + + bin = *j.template get_ptr(); +} + +template::value, int> = 0> +void from_json(const BasicJsonType& j, ConstructibleObjectType& obj) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(302, "type must be object, but is " + std::string(j.type_name()), j)); + } + + ConstructibleObjectType ret; + const auto* inner_object = j.template get_ptr(); + using value_type = typename ConstructibleObjectType::value_type; + std::transform( + inner_object->begin(), inner_object->end(), + std::inserter(ret, ret.begin()), + [](typename BasicJsonType::object_t::value_type const & p) + { + return value_type(p.first, p.second.template get()); + }); + obj = std::move(ret); +} + +// overload for arithmetic types, not chosen for basic_json template arguments +// (BooleanType, etc..); note: Is it really necessary to provide explicit +// overloads for boolean_t etc. in case of a custom BooleanType which is not +// an arithmetic type? +template < typename BasicJsonType, typename ArithmeticType, + enable_if_t < + std::is_arithmetic::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value&& + !std::is_same::value, + int > = 0 > +void from_json(const BasicJsonType& j, ArithmeticType& val) +{ + switch (static_cast(j)) + { + case value_t::number_unsigned: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_integer: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::number_float: + { + val = static_cast(*j.template get_ptr()); + break; + } + case value_t::boolean: + { + val = static_cast(*j.template get_ptr()); + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::string: + case value_t::binary: + case value_t::discarded: + default: + JSON_THROW(type_error::create(302, "type must be number, but is " + std::string(j.type_name()), j)); + } +} + +template +std::tuple from_json_tuple_impl_base(BasicJsonType&& j, index_sequence /*unused*/) +{ + return std::make_tuple(std::forward(j).at(Idx).template get()...); +} + +template < typename BasicJsonType, class A1, class A2 > +std::pair from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<0> /*unused*/) +{ + return {std::forward(j).at(0).template get(), + std::forward(j).at(1).template get()}; +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::pair& p, priority_tag<1> /*unused*/) +{ + p = from_json_tuple_impl(std::forward(j), identity_tag> {}, priority_tag<0> {}); +} + +template +std::tuple from_json_tuple_impl(BasicJsonType&& j, identity_tag> /*unused*/, priority_tag<2> /*unused*/) +{ + return from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +void from_json_tuple_impl(BasicJsonType&& j, std::tuple& t, priority_tag<3> /*unused*/) +{ + t = from_json_tuple_impl_base(std::forward(j), index_sequence_for {}); +} + +template +auto from_json(BasicJsonType&& j, TupleRelated&& t) +-> decltype(from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {})) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + + return from_json_tuple_impl(std::forward(j), std::forward(t), priority_tag<3> {}); +} + +template < typename BasicJsonType, typename Key, typename Value, typename Compare, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +template < typename BasicJsonType, typename Key, typename Value, typename Hash, typename KeyEqual, typename Allocator, + typename = enable_if_t < !std::is_constructible < + typename BasicJsonType::string_t, Key >::value >> +void from_json(const BasicJsonType& j, std::unordered_map& m) +{ + if (JSON_HEDLEY_UNLIKELY(!j.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(j.type_name()), j)); + } + m.clear(); + for (const auto& p : j) + { + if (JSON_HEDLEY_UNLIKELY(!p.is_array())) + { + JSON_THROW(type_error::create(302, "type must be array, but is " + std::string(p.type_name()), j)); + } + m.emplace(p.at(0).template get(), p.at(1).template get()); + } +} + +struct from_json_fn +{ + template + auto operator()(const BasicJsonType& j, T&& val) const + noexcept(noexcept(from_json(j, std::forward(val)))) + -> decltype(from_json(j, std::forward(val))) + { + return from_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `from_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& from_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + + +#include // copy +#include // begin, end +#include // string +#include // tuple, get +#include // is_same, is_constructible, is_floating_point, is_enum, underlying_type +#include // move, forward, declval, pair +#include // valarray +#include // vector + +// #include + + +#include // size_t +#include // input_iterator_tag +#include // string, to_string +#include // tuple_size, get, tuple_element +#include // move + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +void int_to_string( string_type& target, std::size_t value ) +{ + // For ADL + using std::to_string; + target = to_string(value); +} +template class iteration_proxy_value +{ + public: + using difference_type = std::ptrdiff_t; + using value_type = iteration_proxy_value; + using pointer = value_type * ; + using reference = value_type & ; + using iterator_category = std::input_iterator_tag; + using string_type = typename std::remove_cv< typename std::remove_reference().key() ) >::type >::type; + + private: + /// the iterator + IteratorType anchor; + /// an index for arrays (used to create key names) + std::size_t array_index = 0; + /// last stringified array index + mutable std::size_t array_index_last = 0; + /// a string representation of the array index + mutable string_type array_index_str = "0"; + /// an empty string (to return a reference for primitive values) + const string_type empty_str{}; + + public: + explicit iteration_proxy_value(IteratorType it) noexcept + : anchor(std::move(it)) + {} + + /// dereference operator (needed for range-based for) + iteration_proxy_value& operator*() + { + return *this; + } + + /// increment operator (needed for range-based for) + iteration_proxy_value& operator++() + { + ++anchor; + ++array_index; + + return *this; + } + + /// equality operator (needed for InputIterator) + bool operator==(const iteration_proxy_value& o) const + { + return anchor == o.anchor; + } + + /// inequality operator (needed for range-based for) + bool operator!=(const iteration_proxy_value& o) const + { + return anchor != o.anchor; + } + + /// return key of the iterator + const string_type& key() const + { + JSON_ASSERT(anchor.m_object != nullptr); + + switch (anchor.m_object->type()) + { + // use integer array index as key + case value_t::array: + { + if (array_index != array_index_last) + { + int_to_string( array_index_str, array_index ); + array_index_last = array_index; + } + return array_index_str; + } + + // use key from the object + case value_t::object: + return anchor.key(); + + // use an empty key for all primitive types + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return empty_str; + } + } + + /// return value of the iterator + typename IteratorType::reference value() const + { + return anchor.value(); + } +}; + +/// proxy class for the items() function +template class iteration_proxy +{ + private: + /// the container to iterate + typename IteratorType::reference container; + + public: + /// construct iteration proxy from a container + explicit iteration_proxy(typename IteratorType::reference cont) noexcept + : container(cont) {} + + /// return iterator begin (needed for range-based for) + iteration_proxy_value begin() noexcept + { + return iteration_proxy_value(container.begin()); + } + + /// return iterator end (needed for range-based for) + iteration_proxy_value end() noexcept + { + return iteration_proxy_value(container.end()); + } +}; +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.key()) +{ + return i.key(); +} +// Structured Bindings Support +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +template = 0> +auto get(const nlohmann::detail::iteration_proxy_value& i) -> decltype(i.value()) +{ + return i.value(); +} +} // namespace detail +} // namespace nlohmann + +// The Addition to the STD Namespace is required to add +// Structured Bindings Support to the iteration_proxy_value class +// For further reference see https://blog.tartanllama.xyz/structured-bindings/ +// And see https://github.com/nlohmann/json/pull/1391 +namespace std +{ +#if defined(__clang__) + // Fix: https://github.com/nlohmann/json/issues/1401 + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +template +class tuple_size<::nlohmann::detail::iteration_proxy_value> + : public std::integral_constant {}; + +template +class tuple_element> +{ + public: + using type = decltype( + get(std::declval < + ::nlohmann::detail::iteration_proxy_value> ())); +}; +#if defined(__clang__) + #pragma clang diagnostic pop +#endif +} // namespace std + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +////////////////// +// constructors // +////////////////// + +/* + * Note all external_constructor<>::construct functions need to call + * j.m_value.destroy(j.m_type) to avoid a memory leak in case j contains an + * allocated value (e.g., a string). See bug issue + * https://github.com/nlohmann/json/issues/2865 for more information. + */ + +template struct external_constructor; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::boolean_t b) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::boolean; + j.m_value = b; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::string_t& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = s; + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::string_t&& s) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value = std::move(s); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleStringType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleStringType& str) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::string; + j.m_value.string = j.template create(str); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::binary_t& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(b); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::binary_t&& b) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::binary; + j.m_value = typename BasicJsonType::binary_t(std::move(b)); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_float_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_float; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_unsigned_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_unsigned; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, typename BasicJsonType::number_integer_t val) noexcept + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::number_integer; + j.m_value = val; + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::array_t& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = arr; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::array_t&& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = std::move(arr); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < !std::is_same::value, + int > = 0 > + static void construct(BasicJsonType& j, const CompatibleArrayType& arr) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value.array = j.template create(begin(arr), end(arr)); + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, const std::vector& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->reserve(arr.size()); + for (const bool x : arr) + { + j.m_value.array->push_back(x); + j.set_parent(j.m_value.array->back()); + } + j.assert_invariant(); + } + + template::value, int> = 0> + static void construct(BasicJsonType& j, const std::valarray& arr) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::array; + j.m_value = value_t::array; + j.m_value.array->resize(arr.size()); + if (arr.size() > 0) + { + std::copy(std::begin(arr), std::end(arr), j.m_value.array->begin()); + } + j.set_parents(); + j.assert_invariant(); + } +}; + +template<> +struct external_constructor +{ + template + static void construct(BasicJsonType& j, const typename BasicJsonType::object_t& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = obj; + j.set_parents(); + j.assert_invariant(); + } + + template + static void construct(BasicJsonType& j, typename BasicJsonType::object_t&& obj) + { + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value = std::move(obj); + j.set_parents(); + j.assert_invariant(); + } + + template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < !std::is_same::value, int > = 0 > + static void construct(BasicJsonType& j, const CompatibleObjectType& obj) + { + using std::begin; + using std::end; + + j.m_value.destroy(j.m_type); + j.m_type = value_t::object; + j.m_value.object = j.template create(begin(obj), end(obj)); + j.set_parents(); + j.assert_invariant(); + } +}; + +///////////// +// to_json // +///////////// + +template::value, int> = 0> +void to_json(BasicJsonType& j, T b) noexcept +{ + external_constructor::construct(j, b); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const CompatibleString& s) +{ + external_constructor::construct(j, s); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::string_t&& s) +{ + external_constructor::construct(j, std::move(s)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, FloatType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberUnsignedType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, CompatibleNumberIntegerType val) noexcept +{ + external_constructor::construct(j, static_cast(val)); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, EnumType e) noexcept +{ + using underlying_type = typename std::underlying_type::type; + external_constructor::construct(j, static_cast(e)); +} + +template +void to_json(BasicJsonType& j, const std::vector& e) +{ + external_constructor::construct(j, e); +} + +template < typename BasicJsonType, typename CompatibleArrayType, + enable_if_t < is_compatible_array_type::value&& + !is_compatible_object_type::value&& + !is_compatible_string_type::value&& + !std::is_same::value&& + !is_basic_json::value, + int > = 0 > +void to_json(BasicJsonType& j, const CompatibleArrayType& arr) +{ + external_constructor::construct(j, arr); +} + +template +void to_json(BasicJsonType& j, const typename BasicJsonType::binary_t& bin) +{ + external_constructor::construct(j, bin); +} + +template::value, int> = 0> +void to_json(BasicJsonType& j, const std::valarray& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::array_t&& arr) +{ + external_constructor::construct(j, std::move(arr)); +} + +template < typename BasicJsonType, typename CompatibleObjectType, + enable_if_t < is_compatible_object_type::value&& !is_basic_json::value, int > = 0 > +void to_json(BasicJsonType& j, const CompatibleObjectType& obj) +{ + external_constructor::construct(j, obj); +} + +template +void to_json(BasicJsonType& j, typename BasicJsonType::object_t&& obj) +{ + external_constructor::construct(j, std::move(obj)); +} + +template < + typename BasicJsonType, typename T, std::size_t N, + enable_if_t < !std::is_constructible::value, // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + int > = 0 > +void to_json(BasicJsonType& j, const T(&arr)[N]) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + external_constructor::construct(j, arr); +} + +template < typename BasicJsonType, typename T1, typename T2, enable_if_t < std::is_constructible::value&& std::is_constructible::value, int > = 0 > +void to_json(BasicJsonType& j, const std::pair& p) +{ + j = { p.first, p.second }; +} + +// for https://github.com/nlohmann/json/pull/1134 +template>::value, int> = 0> +void to_json(BasicJsonType& j, const T& b) +{ + j = { {b.key(), b.value()} }; +} + +template +void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence /*unused*/) +{ + j = { std::get(t)... }; +} + +template::value, int > = 0> +void to_json(BasicJsonType& j, const T& t) +{ + to_json_tuple_impl(j, t, make_index_sequence::value> {}); +} + +struct to_json_fn +{ + template + auto operator()(BasicJsonType& j, T&& val) const noexcept(noexcept(to_json(j, std::forward(val)))) + -> decltype(to_json(j, std::forward(val)), void()) + { + return to_json(j, std::forward(val)); + } +}; +} // namespace detail + +/// namespace to hold default `to_json` function +/// to see why this is required: +/// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4381.html +namespace // NOLINT(cert-dcl59-cpp,fuchsia-header-anon-namespaces,google-build-namespaces) +{ +constexpr const auto& to_json = detail::static_const::value; // NOLINT(misc-definitions-in-headers) +} // namespace +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ + +template +struct adl_serializer +{ + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @note This function is chosen for default-constructible value types. + + @param[in] j JSON value to read from + @param[in,out] val value to write to + */ + template + static auto from_json(BasicJsonType && j, TargetType& val) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), val))) + -> decltype(::nlohmann::from_json(std::forward(j), val), void()) + { + ::nlohmann::from_json(std::forward(j), val); + } + + /*! + @brief convert a JSON value to any value type + + This function is usually called by the `get()` function of the + @ref basic_json class (either explicit or via conversion operators). + + @note This function is chosen for value types which are not default-constructible. + + @param[in] j JSON value to read from + + @return copy of the JSON value, converted to @a ValueType + */ + template + static auto from_json(BasicJsonType && j) noexcept( + noexcept(::nlohmann::from_json(std::forward(j), detail::identity_tag {}))) + -> decltype(::nlohmann::from_json(std::forward(j), detail::identity_tag {})) + { + return ::nlohmann::from_json(std::forward(j), detail::identity_tag {}); + } + + /*! + @brief convert any value type to a JSON value + + This function is usually called by the constructors of the @ref basic_json + class. + + @param[in,out] j JSON value to write to + @param[in] val value to read from + */ + template + static auto to_json(BasicJsonType& j, TargetType && val) noexcept( + noexcept(::nlohmann::to_json(j, std::forward(val)))) + -> decltype(::nlohmann::to_json(j, std::forward(val)), void()) + { + ::nlohmann::to_json(j, std::forward(val)); + } +}; +} // namespace nlohmann + +// #include + + +#include // uint8_t, uint64_t +#include // tie +#include // move + +namespace nlohmann +{ + +/*! +@brief an internal type for a backed binary type + +This type extends the template parameter @a BinaryType provided to `basic_json` +with a subtype used by BSON and MessagePack. This type exists so that the user +does not have to specify a type themselves with a specific naming scheme in +order to override the binary type. + +@tparam BinaryType container to store bytes (`std::vector` by + default) + +@since version 3.8.0; changed type of subtypes to std::uint64_t in 3.10.0. +*/ +template +class byte_container_with_subtype : public BinaryType +{ + public: + /// the type of the underlying container + using container_type = BinaryType; + /// the type of the subtype + using subtype_type = std::uint64_t; + + byte_container_with_subtype() noexcept(noexcept(container_type())) + : container_type() + {} + + byte_container_with_subtype(const container_type& b) noexcept(noexcept(container_type(b))) + : container_type(b) + {} + + byte_container_with_subtype(container_type&& b) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + {} + + byte_container_with_subtype(const container_type& b, subtype_type subtype_) noexcept(noexcept(container_type(b))) + : container_type(b) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + byte_container_with_subtype(container_type&& b, subtype_type subtype_) noexcept(noexcept(container_type(std::move(b)))) + : container_type(std::move(b)) + , m_subtype(subtype_) + , m_has_subtype(true) + {} + + bool operator==(const byte_container_with_subtype& rhs) const + { + return std::tie(static_cast(*this), m_subtype, m_has_subtype) == + std::tie(static_cast(rhs), rhs.m_subtype, rhs.m_has_subtype); + } + + bool operator!=(const byte_container_with_subtype& rhs) const + { + return !(rhs == *this); + } + + /*! + @brief sets the binary subtype + + Sets the binary subtype of the value, also flags a binary JSON value as + having a subtype, which has implications for serialization. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref subtype() -- return the binary subtype + @sa see @ref clear_subtype() -- clears the binary subtype + @sa see @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void set_subtype(subtype_type subtype_) noexcept + { + m_subtype = subtype_; + m_has_subtype = true; + } + + /*! + @brief return the binary subtype + + Returns the numerical subtype of the value if it has a subtype. If it does + not have a subtype, this function will return subtype_type(-1) as a sentinel + value. + + @return the numerical subtype of the binary value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref set_subtype() -- sets the binary subtype + @sa see @ref clear_subtype() -- clears the binary subtype + @sa see @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0; fixed return value to properly return + subtype_type(-1) as documented in version 3.10.0 + */ + constexpr subtype_type subtype() const noexcept + { + return m_has_subtype ? m_subtype : subtype_type(-1); + } + + /*! + @brief return whether the value has a subtype + + @return whether the value has a subtype + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref subtype() -- return the binary subtype + @sa see @ref set_subtype() -- sets the binary subtype + @sa see @ref clear_subtype() -- clears the binary subtype + + @since version 3.8.0 + */ + constexpr bool has_subtype() const noexcept + { + return m_has_subtype; + } + + /*! + @brief clears the binary subtype + + Clears the binary subtype and flags the value as not having a subtype, which + has implications for serialization; for instance MessagePack will prefer the + bin family over the ext family. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @sa see @ref subtype() -- return the binary subtype + @sa see @ref set_subtype() -- sets the binary subtype + @sa see @ref has_subtype() -- returns whether or not the binary value has a + subtype + + @since version 3.8.0 + */ + void clear_subtype() noexcept + { + m_subtype = 0; + m_has_subtype = false; + } + + private: + subtype_type m_subtype = 0; + bool m_has_subtype = false; +}; + +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + + +#include // uint8_t +#include // size_t +#include // hash + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +// boost::hash_combine +inline std::size_t combine(std::size_t seed, std::size_t h) noexcept +{ + seed ^= h + 0x9e3779b9 + (seed << 6U) + (seed >> 2U); + return seed; +} + +/*! +@brief hash a JSON value + +The hash function tries to rely on std::hash where possible. Furthermore, the +type of the JSON value is taken into account to have different hash values for +null, 0, 0U, and false, etc. + +@tparam BasicJsonType basic_json specialization +@param j JSON value to hash +@return hash value of j +*/ +template +std::size_t hash(const BasicJsonType& j) +{ + using string_t = typename BasicJsonType::string_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + + const auto type = static_cast(j.type()); + switch (j.type()) + { + case BasicJsonType::value_t::null: + case BasicJsonType::value_t::discarded: + { + return combine(type, 0); + } + + case BasicJsonType::value_t::object: + { + auto seed = combine(type, j.size()); + for (const auto& element : j.items()) + { + const auto h = std::hash {}(element.key()); + seed = combine(seed, h); + seed = combine(seed, hash(element.value())); + } + return seed; + } + + case BasicJsonType::value_t::array: + { + auto seed = combine(type, j.size()); + for (const auto& element : j) + { + seed = combine(seed, hash(element)); + } + return seed; + } + + case BasicJsonType::value_t::string: + { + const auto h = std::hash {}(j.template get_ref()); + return combine(type, h); + } + + case BasicJsonType::value_t::boolean: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_integer: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_unsigned: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::number_float: + { + const auto h = std::hash {}(j.template get()); + return combine(type, h); + } + + case BasicJsonType::value_t::binary: + { + auto seed = combine(type, j.get_binary().size()); + const auto h = std::hash {}(j.get_binary().has_subtype()); + seed = combine(seed, h); + seed = combine(seed, static_cast(j.get_binary().subtype())); + for (const auto byte : j.get_binary()) + { + seed = combine(seed, std::hash {}(byte)); + } + return seed; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return 0; // LCOV_EXCL_LINE + } +} + +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // generate_n +#include // array +#include // ldexp +#include // size_t +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // snprintf +#include // memcpy +#include // back_inserter +#include // numeric_limits +#include // char_traits, string +#include // make_pair, move +#include // vector + +// #include + +// #include + + +#include // array +#include // size_t +#include // strlen +#include // begin, end, iterator_traits, random_access_iterator_tag, distance, next +#include // shared_ptr, make_shared, addressof +#include // accumulate +#include // string, char_traits +#include // enable_if, is_base_of, is_pointer, is_integral, remove_pointer +#include // pair, declval + +#ifndef JSON_NO_IO + #include // FILE * + #include // istream +#endif // JSON_NO_IO + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// the supported input formats +enum class input_format_t { json, cbor, msgpack, ubjson, bson }; + +//////////////////// +// input adapters // +//////////////////// + +#ifndef JSON_NO_IO +/*! +Input adapter for stdio file access. This adapter read only 1 byte and do not use any + buffer. This adapter is a very low level adapter. +*/ +class file_input_adapter +{ + public: + using char_type = char; + + JSON_HEDLEY_NON_NULL(2) + explicit file_input_adapter(std::FILE* f) noexcept + : m_file(f) + {} + + // make class move-only + file_input_adapter(const file_input_adapter&) = delete; + file_input_adapter(file_input_adapter&&) noexcept = default; + file_input_adapter& operator=(const file_input_adapter&) = delete; + file_input_adapter& operator=(file_input_adapter&&) = delete; + ~file_input_adapter() = default; + + std::char_traits::int_type get_character() noexcept + { + return std::fgetc(m_file); + } + + private: + /// the file pointer to read from + std::FILE* m_file; +}; + + +/*! +Input adapter for a (caching) istream. Ignores a UFT Byte Order Mark at +beginning of input. Does not support changing the underlying std::streambuf +in mid-input. Maintains underlying std::istream and std::streambuf to support +subsequent use of standard std::istream operations to process any input +characters following those used in parsing the JSON input. Clears the +std::istream flags; any input errors (e.g., EOF) will be detected by the first +subsequent call for input from the std::istream. +*/ +class input_stream_adapter +{ + public: + using char_type = char; + + ~input_stream_adapter() + { + // clear stream flags; we use underlying streambuf I/O, do not + // maintain ifstream flags, except eof + if (is != nullptr) + { + is->clear(is->rdstate() & std::ios::eofbit); + } + } + + explicit input_stream_adapter(std::istream& i) + : is(&i), sb(i.rdbuf()) + {} + + // delete because of pointer members + input_stream_adapter(const input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&) = delete; + input_stream_adapter& operator=(input_stream_adapter&&) = delete; + + input_stream_adapter(input_stream_adapter&& rhs) noexcept + : is(rhs.is), sb(rhs.sb) + { + rhs.is = nullptr; + rhs.sb = nullptr; + } + + // std::istream/std::streambuf use std::char_traits::to_int_type, to + // ensure that std::char_traits::eof() and the character 0xFF do not + // end up as the same value, eg. 0xFFFFFFFF. + std::char_traits::int_type get_character() + { + auto res = sb->sbumpc(); + // set eof manually, as we don't use the istream interface. + if (JSON_HEDLEY_UNLIKELY(res == std::char_traits::eof())) + { + is->clear(is->rdstate() | std::ios::eofbit); + } + return res; + } + + private: + /// the associated input stream + std::istream* is = nullptr; + std::streambuf* sb = nullptr; +}; +#endif // JSON_NO_IO + +// General-purpose iterator-based adapter. It might not be as fast as +// theoretically possible for some containers, but it is extremely versatile. +template +class iterator_input_adapter +{ + public: + using char_type = typename std::iterator_traits::value_type; + + iterator_input_adapter(IteratorType first, IteratorType last) + : current(std::move(first)), end(std::move(last)) + {} + + typename std::char_traits::int_type get_character() + { + if (JSON_HEDLEY_LIKELY(current != end)) + { + auto result = std::char_traits::to_int_type(*current); + std::advance(current, 1); + return result; + } + + return std::char_traits::eof(); + } + + private: + IteratorType current; + IteratorType end; + + template + friend struct wide_string_input_helper; + + bool empty() const + { + return current == end; + } +}; + + +template +struct wide_string_input_helper; + +template +struct wide_string_input_helper +{ + // UTF-32 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-32 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u) & 0x1Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (wc <= 0xFFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u) & 0x0Fu)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else if (wc <= 0x10FFFF) + { + utf8_bytes[0] = static_cast::int_type>(0xF0u | ((static_cast(wc) >> 18u) & 0x07u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + // unknown character + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } +}; + +template +struct wide_string_input_helper +{ + // UTF-16 + static void fill_buffer(BaseInputAdapter& input, + std::array::int_type, 4>& utf8_bytes, + size_t& utf8_bytes_index, + size_t& utf8_bytes_filled) + { + utf8_bytes_index = 0; + + if (JSON_HEDLEY_UNLIKELY(input.empty())) + { + utf8_bytes[0] = std::char_traits::eof(); + utf8_bytes_filled = 1; + } + else + { + // get the current character + const auto wc = input.get_character(); + + // UTF-16 to UTF-8 encoding + if (wc < 0x80) + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + else if (wc <= 0x7FF) + { + utf8_bytes[0] = static_cast::int_type>(0xC0u | ((static_cast(wc) >> 6u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 2; + } + else if (0xD800 > wc || wc >= 0xE000) + { + utf8_bytes[0] = static_cast::int_type>(0xE0u | ((static_cast(wc) >> 12u))); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((static_cast(wc) >> 6u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | (static_cast(wc) & 0x3Fu)); + utf8_bytes_filled = 3; + } + else + { + if (JSON_HEDLEY_UNLIKELY(!input.empty())) + { + const auto wc2 = static_cast(input.get_character()); + const auto charcode = 0x10000u + (((static_cast(wc) & 0x3FFu) << 10u) | (wc2 & 0x3FFu)); + utf8_bytes[0] = static_cast::int_type>(0xF0u | (charcode >> 18u)); + utf8_bytes[1] = static_cast::int_type>(0x80u | ((charcode >> 12u) & 0x3Fu)); + utf8_bytes[2] = static_cast::int_type>(0x80u | ((charcode >> 6u) & 0x3Fu)); + utf8_bytes[3] = static_cast::int_type>(0x80u | (charcode & 0x3Fu)); + utf8_bytes_filled = 4; + } + else + { + utf8_bytes[0] = static_cast::int_type>(wc); + utf8_bytes_filled = 1; + } + } + } + } +}; + +// Wraps another input apdater to convert wide character types into individual bytes. +template +class wide_string_input_adapter +{ + public: + using char_type = char; + + wide_string_input_adapter(BaseInputAdapter base) + : base_adapter(base) {} + + typename std::char_traits::int_type get_character() noexcept + { + // check if buffer needs to be filled + if (utf8_bytes_index == utf8_bytes_filled) + { + fill_buffer(); + + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index == 0); + } + + // use buffer + JSON_ASSERT(utf8_bytes_filled > 0); + JSON_ASSERT(utf8_bytes_index < utf8_bytes_filled); + return utf8_bytes[utf8_bytes_index++]; + } + + private: + BaseInputAdapter base_adapter; + + template + void fill_buffer() + { + wide_string_input_helper::fill_buffer(base_adapter, utf8_bytes, utf8_bytes_index, utf8_bytes_filled); + } + + /// a buffer for UTF-8 bytes + std::array::int_type, 4> utf8_bytes = {{0, 0, 0, 0}}; + + /// index to the utf8_codes array for the next valid byte + std::size_t utf8_bytes_index = 0; + /// number of valid bytes in the utf8_codes array + std::size_t utf8_bytes_filled = 0; +}; + + +template +struct iterator_input_adapter_factory +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using adapter_type = iterator_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(std::move(first), std::move(last)); + } +}; + +template +struct is_iterator_of_multibyte +{ + using value_type = typename std::iterator_traits::value_type; + enum + { + value = sizeof(value_type) > 1 + }; +}; + +template +struct iterator_input_adapter_factory::value>> +{ + using iterator_type = IteratorType; + using char_type = typename std::iterator_traits::value_type; + using base_adapter_type = iterator_input_adapter; + using adapter_type = wide_string_input_adapter; + + static adapter_type create(IteratorType first, IteratorType last) + { + return adapter_type(base_adapter_type(std::move(first), std::move(last))); + } +}; + +// General purpose iterator-based input +template +typename iterator_input_adapter_factory::adapter_type input_adapter(IteratorType first, IteratorType last) +{ + using factory_type = iterator_input_adapter_factory; + return factory_type::create(first, last); +} + +// Convenience shorthand from container to iterator +// Enables ADL on begin(container) and end(container) +// Encloses the using declarations in namespace for not to leak them to outside scope + +namespace container_input_adapter_factory_impl +{ + +using std::begin; +using std::end; + +template +struct container_input_adapter_factory {}; + +template +struct container_input_adapter_factory< ContainerType, + void_t()), end(std::declval()))>> + { + using adapter_type = decltype(input_adapter(begin(std::declval()), end(std::declval()))); + + static adapter_type create(const ContainerType& container) +{ + return input_adapter(begin(container), end(container)); +} + }; + +} // namespace container_input_adapter_factory_impl + +template +typename container_input_adapter_factory_impl::container_input_adapter_factory::adapter_type input_adapter(const ContainerType& container) +{ + return container_input_adapter_factory_impl::container_input_adapter_factory::create(container); +} + +#ifndef JSON_NO_IO +// Special cases with fast paths +inline file_input_adapter input_adapter(std::FILE* file) +{ + return file_input_adapter(file); +} + +inline input_stream_adapter input_adapter(std::istream& stream) +{ + return input_stream_adapter(stream); +} + +inline input_stream_adapter input_adapter(std::istream&& stream) +{ + return input_stream_adapter(stream); +} +#endif // JSON_NO_IO + +using contiguous_bytes_input_adapter = decltype(input_adapter(std::declval(), std::declval())); + +// Null-delimited strings, and the like. +template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + !std::is_array::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > +contiguous_bytes_input_adapter input_adapter(CharT b) +{ + auto length = std::strlen(reinterpret_cast(b)); + const auto* ptr = reinterpret_cast(b); + return input_adapter(ptr, ptr + length); +} + +template +auto input_adapter(T (&array)[N]) -> decltype(input_adapter(array, array + N)) // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) +{ + return input_adapter(array, array + N); +} + +// This class only handles inputs of input_buffer_adapter type. +// It's required so that expressions like {ptr, len} can be implicitely casted +// to the correct adapter. +class span_input_adapter +{ + public: + template < typename CharT, + typename std::enable_if < + std::is_pointer::value&& + std::is_integral::type>::value&& + sizeof(typename std::remove_pointer::type) == 1, + int >::type = 0 > + span_input_adapter(CharT b, std::size_t l) + : ia(reinterpret_cast(b), reinterpret_cast(b) + l) {} + + template::iterator_category, std::random_access_iterator_tag>::value, + int>::type = 0> + span_input_adapter(IteratorType first, IteratorType last) + : ia(input_adapter(first, last)) {} + + contiguous_bytes_input_adapter&& get() + { + return std::move(ia); // NOLINT(hicpp-move-const-arg,performance-move-const-arg) + } + + private: + contiguous_bytes_input_adapter ia; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include +#include // string +#include // move +#include // vector + +// #include + +// #include + + +namespace nlohmann +{ + +/*! +@brief SAX interface + +This class describes the SAX interface used by @ref nlohmann::json::sax_parse. +Each function is called in different situations while the input is parsed. The +boolean return value informs the parser whether to continue processing the +input. +*/ +template +struct json_sax +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @brief a null value was read + @return whether parsing should proceed + */ + virtual bool null() = 0; + + /*! + @brief a boolean value was read + @param[in] val boolean value + @return whether parsing should proceed + */ + virtual bool boolean(bool val) = 0; + + /*! + @brief an integer number was read + @param[in] val integer value + @return whether parsing should proceed + */ + virtual bool number_integer(number_integer_t val) = 0; + + /*! + @brief an unsigned integer number was read + @param[in] val unsigned integer value + @return whether parsing should proceed + */ + virtual bool number_unsigned(number_unsigned_t val) = 0; + + /*! + @brief an floating-point number was read + @param[in] val floating-point value + @param[in] s raw token value + @return whether parsing should proceed + */ + virtual bool number_float(number_float_t val, const string_t& s) = 0; + + /*! + @brief a string was read + @param[in] val string value + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool string(string_t& val) = 0; + + /*! + @brief a binary string was read + @param[in] val binary value + @return whether parsing should proceed + @note It is safe to move the passed binary. + */ + virtual bool binary(binary_t& val) = 0; + + /*! + @brief the beginning of an object was read + @param[in] elements number of object elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_object(std::size_t elements) = 0; + + /*! + @brief an object key was read + @param[in] val object key + @return whether parsing should proceed + @note It is safe to move the passed string. + */ + virtual bool key(string_t& val) = 0; + + /*! + @brief the end of an object was read + @return whether parsing should proceed + */ + virtual bool end_object() = 0; + + /*! + @brief the beginning of an array was read + @param[in] elements number of array elements or -1 if unknown + @return whether parsing should proceed + @note binary formats may report the number of elements + */ + virtual bool start_array(std::size_t elements) = 0; + + /*! + @brief the end of an array was read + @return whether parsing should proceed + */ + virtual bool end_array() = 0; + + /*! + @brief a parse error occurred + @param[in] position the position in the input where the error occurs + @param[in] last_token the last read token + @param[in] ex an exception object describing the error + @return whether parsing should proceed (must return false) + */ + virtual bool parse_error(std::size_t position, + const std::string& last_token, + const detail::exception& ex) = 0; + + json_sax() = default; + json_sax(const json_sax&) = default; + json_sax(json_sax&&) noexcept = default; + json_sax& operator=(const json_sax&) = default; + json_sax& operator=(json_sax&&) noexcept = default; + virtual ~json_sax() = default; +}; + + +namespace detail +{ +/*! +@brief SAX implementation to create a JSON value from SAX events + +This class implements the @ref json_sax interface and processes the SAX events +to create a JSON value which makes it basically a DOM parser. The structure or +hierarchy of the JSON value is managed by the stack `ref_stack` which contains +a pointer to the respective array or object for each recursion depth. + +After successful parsing, the value that is passed by reference to the +constructor contains the parsed value. + +@tparam BasicJsonType the JSON type +*/ +template +class json_sax_dom_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + /*! + @param[in,out] r reference to a JSON value that is manipulated while + parsing + @param[in] allow_exceptions_ whether parse errors yield exceptions + */ + explicit json_sax_dom_parser(BasicJsonType& r, const bool allow_exceptions_ = true) + : root(r), allow_exceptions(allow_exceptions_) + {} + + // make class move-only + json_sax_dom_parser(const json_sax_dom_parser&) = delete; + json_sax_dom_parser(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_parser& operator=(const json_sax_dom_parser&) = delete; + json_sax_dom_parser& operator=(json_sax_dom_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::object)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + // add null at given key and store the reference for later + object_element = &(ref_stack.back()->m_value.object->operator[](val)); + return true; + } + + bool end_object() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + bool start_array(std::size_t len) + { + ref_stack.push_back(handle_value(BasicJsonType::value_t::array)); + + if (JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + ref_stack.back()->set_parents(); + ref_stack.pop_back(); + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + */ + template + JSON_HEDLEY_RETURNS_NON_NULL + BasicJsonType* handle_value(Value&& v) + { + if (ref_stack.empty()) + { + root = BasicJsonType(std::forward(v)); + return &root; + } + + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::forward(v)); + return &(ref_stack.back()->m_value.array->back()); + } + + JSON_ASSERT(ref_stack.back()->is_object()); + JSON_ASSERT(object_element); + *object_element = BasicJsonType(std::forward(v)); + return object_element; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +template +class json_sax_dom_callback_parser +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using parser_callback_t = typename BasicJsonType::parser_callback_t; + using parse_event_t = typename BasicJsonType::parse_event_t; + + json_sax_dom_callback_parser(BasicJsonType& r, + const parser_callback_t cb, + const bool allow_exceptions_ = true) + : root(r), callback(cb), allow_exceptions(allow_exceptions_) + { + keep_stack.push_back(true); + } + + // make class move-only + json_sax_dom_callback_parser(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + json_sax_dom_callback_parser& operator=(const json_sax_dom_callback_parser&) = delete; + json_sax_dom_callback_parser& operator=(json_sax_dom_callback_parser&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~json_sax_dom_callback_parser() = default; + + bool null() + { + handle_value(nullptr); + return true; + } + + bool boolean(bool val) + { + handle_value(val); + return true; + } + + bool number_integer(number_integer_t val) + { + handle_value(val); + return true; + } + + bool number_unsigned(number_unsigned_t val) + { + handle_value(val); + return true; + } + + bool number_float(number_float_t val, const string_t& /*unused*/) + { + handle_value(val); + return true; + } + + bool string(string_t& val) + { + handle_value(val); + return true; + } + + bool binary(binary_t& val) + { + handle_value(std::move(val)); + return true; + } + + bool start_object(std::size_t len) + { + // check callback for object start + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::object_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::object, true); + ref_stack.push_back(val.second); + + // check object limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive object size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool key(string_t& val) + { + BasicJsonType k = BasicJsonType(val); + + // check callback for key + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::key, k); + key_keep_stack.push_back(keep); + + // add discarded value at given key and store the reference for later + if (keep && ref_stack.back()) + { + object_element = &(ref_stack.back()->m_value.object->operator[](val) = discarded); + } + + return true; + } + + bool end_object() + { + if (ref_stack.back()) + { + if (!callback(static_cast(ref_stack.size()) - 1, parse_event_t::object_end, *ref_stack.back())) + { + // discard object + *ref_stack.back() = discarded; + } + else + { + ref_stack.back()->set_parents(); + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + if (!ref_stack.empty() && ref_stack.back() && ref_stack.back()->is_structured()) + { + // remove discarded value + for (auto it = ref_stack.back()->begin(); it != ref_stack.back()->end(); ++it) + { + if (it->is_discarded()) + { + ref_stack.back()->erase(it); + break; + } + } + } + + return true; + } + + bool start_array(std::size_t len) + { + const bool keep = callback(static_cast(ref_stack.size()), parse_event_t::array_start, discarded); + keep_stack.push_back(keep); + + auto val = handle_value(BasicJsonType::value_t::array, true); + ref_stack.push_back(val.second); + + // check array limit + if (ref_stack.back() && JSON_HEDLEY_UNLIKELY(len != std::size_t(-1) && len > ref_stack.back()->max_size())) + { + JSON_THROW(out_of_range::create(408, "excessive array size: " + std::to_string(len), *ref_stack.back())); + } + + return true; + } + + bool end_array() + { + bool keep = true; + + if (ref_stack.back()) + { + keep = callback(static_cast(ref_stack.size()) - 1, parse_event_t::array_end, *ref_stack.back()); + if (keep) + { + ref_stack.back()->set_parents(); + } + else + { + // discard array + *ref_stack.back() = discarded; + } + } + + JSON_ASSERT(!ref_stack.empty()); + JSON_ASSERT(!keep_stack.empty()); + ref_stack.pop_back(); + keep_stack.pop_back(); + + // remove discarded value + if (!keep && !ref_stack.empty() && ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->pop_back(); + } + + return true; + } + + template + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, + const Exception& ex) + { + errored = true; + static_cast(ex); + if (allow_exceptions) + { + JSON_THROW(ex); + } + return false; + } + + constexpr bool is_errored() const + { + return errored; + } + + private: + /*! + @param[in] v value to add to the JSON value we build during parsing + @param[in] skip_callback whether we should skip calling the callback + function; this is required after start_array() and + start_object() SAX events, because otherwise we would call the + callback function with an empty array or object, respectively. + + @invariant If the ref stack is empty, then the passed value will be the new + root. + @invariant If the ref stack contains a value, then it is an array or an + object to which we can add elements + + @return pair of boolean (whether value should be kept) and pointer (to the + passed value in the ref_stack hierarchy; nullptr if not kept) + */ + template + std::pair handle_value(Value&& v, const bool skip_callback = false) + { + JSON_ASSERT(!keep_stack.empty()); + + // do not handle this value if we know it would be added to a discarded + // container + if (!keep_stack.back()) + { + return {false, nullptr}; + } + + // create value + auto value = BasicJsonType(std::forward(v)); + + // check callback + const bool keep = skip_callback || callback(static_cast(ref_stack.size()), parse_event_t::value, value); + + // do not handle this value if we just learnt it shall be discarded + if (!keep) + { + return {false, nullptr}; + } + + if (ref_stack.empty()) + { + root = std::move(value); + return {true, &root}; + } + + // skip this value if we already decided to skip the parent + // (https://github.com/nlohmann/json/issues/971#issuecomment-413678360) + if (!ref_stack.back()) + { + return {false, nullptr}; + } + + // we now only expect arrays and objects + JSON_ASSERT(ref_stack.back()->is_array() || ref_stack.back()->is_object()); + + // array + if (ref_stack.back()->is_array()) + { + ref_stack.back()->m_value.array->emplace_back(std::move(value)); + return {true, &(ref_stack.back()->m_value.array->back())}; + } + + // object + JSON_ASSERT(ref_stack.back()->is_object()); + // check if we should store an element for the current key + JSON_ASSERT(!key_keep_stack.empty()); + const bool store_element = key_keep_stack.back(); + key_keep_stack.pop_back(); + + if (!store_element) + { + return {false, nullptr}; + } + + JSON_ASSERT(object_element); + *object_element = std::move(value); + return {true, object_element}; + } + + /// the parsed JSON value + BasicJsonType& root; + /// stack to model hierarchy of values + std::vector ref_stack {}; + /// stack to manage which values to keep + std::vector keep_stack {}; + /// stack to manage which object keys to keep + std::vector key_keep_stack {}; + /// helper to hold the reference for the next object element + BasicJsonType* object_element = nullptr; + /// whether a syntax error occurred + bool errored = false; + /// callback function + const parser_callback_t callback = nullptr; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; + /// a discarded value for the callback + BasicJsonType discarded = BasicJsonType::value_t::discarded; +}; + +template +class json_sax_acceptor +{ + public: + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + + bool null() + { + return true; + } + + bool boolean(bool /*unused*/) + { + return true; + } + + bool number_integer(number_integer_t /*unused*/) + { + return true; + } + + bool number_unsigned(number_unsigned_t /*unused*/) + { + return true; + } + + bool number_float(number_float_t /*unused*/, const string_t& /*unused*/) + { + return true; + } + + bool string(string_t& /*unused*/) + { + return true; + } + + bool binary(binary_t& /*unused*/) + { + return true; + } + + bool start_object(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool key(string_t& /*unused*/) + { + return true; + } + + bool end_object() + { + return true; + } + + bool start_array(std::size_t /*unused*/ = std::size_t(-1)) + { + return true; + } + + bool end_array() + { + return true; + } + + bool parse_error(std::size_t /*unused*/, const std::string& /*unused*/, const detail::exception& /*unused*/) + { + return false; + } +}; +} // namespace detail + +} // namespace nlohmann + +// #include + + +#include // array +#include // localeconv +#include // size_t +#include // snprintf +#include // strtof, strtod, strtold, strtoll, strtoull +#include // initializer_list +#include // char_traits, string +#include // move +#include // vector + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////// +// lexer // +/////////// + +template +class lexer_base +{ + public: + /// token types for the parser + enum class token_type + { + uninitialized, ///< indicating the scanner is uninitialized + literal_true, ///< the `true` literal + literal_false, ///< the `false` literal + literal_null, ///< the `null` literal + value_string, ///< a string -- use get_string() for actual value + value_unsigned, ///< an unsigned integer -- use get_number_unsigned() for actual value + value_integer, ///< a signed integer -- use get_number_integer() for actual value + value_float, ///< an floating point number -- use get_number_float() for actual value + begin_array, ///< the character for array begin `[` + begin_object, ///< the character for object begin `{` + end_array, ///< the character for array end `]` + end_object, ///< the character for object end `}` + name_separator, ///< the name separator `:` + value_separator, ///< the value separator `,` + parse_error, ///< indicating a parse error + end_of_input, ///< indicating the end of the input buffer + literal_or_value ///< a literal or the begin of a value (only for diagnostics) + }; + + /// return name of values of type token_type (only used for errors) + JSON_HEDLEY_RETURNS_NON_NULL + JSON_HEDLEY_CONST + static const char* token_type_name(const token_type t) noexcept + { + switch (t) + { + case token_type::uninitialized: + return ""; + case token_type::literal_true: + return "true literal"; + case token_type::literal_false: + return "false literal"; + case token_type::literal_null: + return "null literal"; + case token_type::value_string: + return "string literal"; + case token_type::value_unsigned: + case token_type::value_integer: + case token_type::value_float: + return "number literal"; + case token_type::begin_array: + return "'['"; + case token_type::begin_object: + return "'{'"; + case token_type::end_array: + return "']'"; + case token_type::end_object: + return "'}'"; + case token_type::name_separator: + return "':'"; + case token_type::value_separator: + return "','"; + case token_type::parse_error: + return ""; + case token_type::end_of_input: + return "end of input"; + case token_type::literal_or_value: + return "'[', '{', or a literal"; + // LCOV_EXCL_START + default: // catch non-enum values + return "unknown token"; + // LCOV_EXCL_STOP + } + } +}; +/*! +@brief lexical analysis + +This class organizes the lexical analysis during JSON deserialization. +*/ +template +class lexer : public lexer_base +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + using token_type = typename lexer_base::token_type; + + explicit lexer(InputAdapterType&& adapter, bool ignore_comments_ = false) noexcept + : ia(std::move(adapter)) + , ignore_comments(ignore_comments_) + , decimal_point_char(static_cast(get_decimal_point())) + {} + + // delete because of pointer members + lexer(const lexer&) = delete; + lexer(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + lexer& operator=(lexer&) = delete; + lexer& operator=(lexer&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~lexer() = default; + + private: + ///////////////////// + // locales + ///////////////////// + + /// return the locale-dependent decimal point + JSON_HEDLEY_PURE + static char get_decimal_point() noexcept + { + const auto* loc = localeconv(); + JSON_ASSERT(loc != nullptr); + return (loc->decimal_point == nullptr) ? '.' : *(loc->decimal_point); + } + + ///////////////////// + // scan functions + ///////////////////// + + /*! + @brief get codepoint from 4 hex characters following `\u` + + For input "\u c1 c2 c3 c4" the codepoint is: + (c1 * 0x1000) + (c2 * 0x0100) + (c3 * 0x0010) + c4 + = (c1 << 12) + (c2 << 8) + (c3 << 4) + (c4 << 0) + + Furthermore, the possible characters '0'..'9', 'A'..'F', and 'a'..'f' + must be converted to the integers 0x0..0x9, 0xA..0xF, 0xA..0xF, resp. The + conversion is done by subtracting the offset (0x30, 0x37, and 0x57) + between the ASCII value of the character and the desired integer value. + + @return codepoint (0x0000..0xFFFF) or -1 in case of an error (e.g. EOF or + non-hex character) + */ + int get_codepoint() + { + // this function only makes sense after reading `\u` + JSON_ASSERT(current == 'u'); + int codepoint = 0; + + const auto factors = { 12u, 8u, 4u, 0u }; + for (const auto factor : factors) + { + get(); + + if (current >= '0' && current <= '9') + { + codepoint += static_cast((static_cast(current) - 0x30u) << factor); + } + else if (current >= 'A' && current <= 'F') + { + codepoint += static_cast((static_cast(current) - 0x37u) << factor); + } + else if (current >= 'a' && current <= 'f') + { + codepoint += static_cast((static_cast(current) - 0x57u) << factor); + } + else + { + return -1; + } + } + + JSON_ASSERT(0x0000 <= codepoint && codepoint <= 0xFFFF); + return codepoint; + } + + /*! + @brief check if the next byte(s) are inside a given range + + Adds the current byte and, for each passed range, reads a new byte and + checks if it is inside the range. If a violation was detected, set up an + error message and return false. Otherwise, return true. + + @param[in] ranges list of integers; interpreted as list of pairs of + inclusive lower and upper bound, respectively + + @pre The passed list @a ranges must have 2, 4, or 6 elements; that is, + 1, 2, or 3 pairs. This precondition is enforced by an assertion. + + @return true if and only if no range violation was detected + */ + bool next_byte_in_range(std::initializer_list ranges) + { + JSON_ASSERT(ranges.size() == 2 || ranges.size() == 4 || ranges.size() == 6); + add(current); + + for (auto range = ranges.begin(); range != ranges.end(); ++range) + { + get(); + if (JSON_HEDLEY_LIKELY(*range <= current && current <= *(++range))) + { + add(current); + } + else + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return false; + } + } + + return true; + } + + /*! + @brief scan a string literal + + This function scans a string according to Sect. 7 of RFC 8259. While + scanning, bytes are escaped and copied into buffer token_buffer. Then the + function returns successfully, token_buffer is *not* null-terminated (as it + may contain \0 bytes), and token_buffer.size() is the number of bytes in the + string. + + @return token_type::value_string if string could be successfully scanned, + token_type::parse_error otherwise + + @note In case of errors, variable error_message contains a textual + description. + */ + token_type scan_string() + { + // reset token_buffer (ignore opening quote) + reset(); + + // we entered the function by reading an open quote + JSON_ASSERT(current == '\"'); + + while (true) + { + // get next character + switch (get()) + { + // end of file while parsing string + case std::char_traits::eof(): + { + error_message = "invalid string: missing closing quote"; + return token_type::parse_error; + } + + // closing quote + case '\"': + { + return token_type::value_string; + } + + // escapes + case '\\': + { + switch (get()) + { + // quotation mark + case '\"': + add('\"'); + break; + // reverse solidus + case '\\': + add('\\'); + break; + // solidus + case '/': + add('/'); + break; + // backspace + case 'b': + add('\b'); + break; + // form feed + case 'f': + add('\f'); + break; + // line feed + case 'n': + add('\n'); + break; + // carriage return + case 'r': + add('\r'); + break; + // tab + case 't': + add('\t'); + break; + + // unicode escapes + case 'u': + { + const int codepoint1 = get_codepoint(); + int codepoint = codepoint1; // start with codepoint1 + + if (JSON_HEDLEY_UNLIKELY(codepoint1 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if code point is a high surrogate + if (0xD800 <= codepoint1 && codepoint1 <= 0xDBFF) + { + // expect next \uxxxx entry + if (JSON_HEDLEY_LIKELY(get() == '\\' && get() == 'u')) + { + const int codepoint2 = get_codepoint(); + + if (JSON_HEDLEY_UNLIKELY(codepoint2 == -1)) + { + error_message = "invalid string: '\\u' must be followed by 4 hex digits"; + return token_type::parse_error; + } + + // check if codepoint2 is a low surrogate + if (JSON_HEDLEY_LIKELY(0xDC00 <= codepoint2 && codepoint2 <= 0xDFFF)) + { + // overwrite codepoint + codepoint = static_cast( + // high surrogate occupies the most significant 22 bits + (static_cast(codepoint1) << 10u) + // low surrogate occupies the least significant 15 bits + + static_cast(codepoint2) + // there is still the 0xD800, 0xDC00 and 0x10000 noise + // in the result so we have to subtract with: + // (0xD800 << 10) + DC00 - 0x10000 = 0x35FDC00 + - 0x35FDC00u); + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + error_message = "invalid string: surrogate U+D800..U+DBFF must be followed by U+DC00..U+DFFF"; + return token_type::parse_error; + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(0xDC00 <= codepoint1 && codepoint1 <= 0xDFFF)) + { + error_message = "invalid string: surrogate U+DC00..U+DFFF must follow U+D800..U+DBFF"; + return token_type::parse_error; + } + } + + // result of the above calculation yields a proper codepoint + JSON_ASSERT(0x00 <= codepoint && codepoint <= 0x10FFFF); + + // translate codepoint into bytes + if (codepoint < 0x80) + { + // 1-byte characters: 0xxxxxxx (ASCII) + add(static_cast(codepoint)); + } + else if (codepoint <= 0x7FF) + { + // 2-byte characters: 110xxxxx 10xxxxxx + add(static_cast(0xC0u | (static_cast(codepoint) >> 6u))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else if (codepoint <= 0xFFFF) + { + // 3-byte characters: 1110xxxx 10xxxxxx 10xxxxxx + add(static_cast(0xE0u | (static_cast(codepoint) >> 12u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + else + { + // 4-byte characters: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + add(static_cast(0xF0u | (static_cast(codepoint) >> 18u))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 12u) & 0x3Fu))); + add(static_cast(0x80u | ((static_cast(codepoint) >> 6u) & 0x3Fu))); + add(static_cast(0x80u | (static_cast(codepoint) & 0x3Fu))); + } + + break; + } + + // other characters after escape + default: + error_message = "invalid string: forbidden character after backslash"; + return token_type::parse_error; + } + + break; + } + + // invalid control characters + case 0x00: + { + error_message = "invalid string: control character U+0000 (NUL) must be escaped to \\u0000"; + return token_type::parse_error; + } + + case 0x01: + { + error_message = "invalid string: control character U+0001 (SOH) must be escaped to \\u0001"; + return token_type::parse_error; + } + + case 0x02: + { + error_message = "invalid string: control character U+0002 (STX) must be escaped to \\u0002"; + return token_type::parse_error; + } + + case 0x03: + { + error_message = "invalid string: control character U+0003 (ETX) must be escaped to \\u0003"; + return token_type::parse_error; + } + + case 0x04: + { + error_message = "invalid string: control character U+0004 (EOT) must be escaped to \\u0004"; + return token_type::parse_error; + } + + case 0x05: + { + error_message = "invalid string: control character U+0005 (ENQ) must be escaped to \\u0005"; + return token_type::parse_error; + } + + case 0x06: + { + error_message = "invalid string: control character U+0006 (ACK) must be escaped to \\u0006"; + return token_type::parse_error; + } + + case 0x07: + { + error_message = "invalid string: control character U+0007 (BEL) must be escaped to \\u0007"; + return token_type::parse_error; + } + + case 0x08: + { + error_message = "invalid string: control character U+0008 (BS) must be escaped to \\u0008 or \\b"; + return token_type::parse_error; + } + + case 0x09: + { + error_message = "invalid string: control character U+0009 (HT) must be escaped to \\u0009 or \\t"; + return token_type::parse_error; + } + + case 0x0A: + { + error_message = "invalid string: control character U+000A (LF) must be escaped to \\u000A or \\n"; + return token_type::parse_error; + } + + case 0x0B: + { + error_message = "invalid string: control character U+000B (VT) must be escaped to \\u000B"; + return token_type::parse_error; + } + + case 0x0C: + { + error_message = "invalid string: control character U+000C (FF) must be escaped to \\u000C or \\f"; + return token_type::parse_error; + } + + case 0x0D: + { + error_message = "invalid string: control character U+000D (CR) must be escaped to \\u000D or \\r"; + return token_type::parse_error; + } + + case 0x0E: + { + error_message = "invalid string: control character U+000E (SO) must be escaped to \\u000E"; + return token_type::parse_error; + } + + case 0x0F: + { + error_message = "invalid string: control character U+000F (SI) must be escaped to \\u000F"; + return token_type::parse_error; + } + + case 0x10: + { + error_message = "invalid string: control character U+0010 (DLE) must be escaped to \\u0010"; + return token_type::parse_error; + } + + case 0x11: + { + error_message = "invalid string: control character U+0011 (DC1) must be escaped to \\u0011"; + return token_type::parse_error; + } + + case 0x12: + { + error_message = "invalid string: control character U+0012 (DC2) must be escaped to \\u0012"; + return token_type::parse_error; + } + + case 0x13: + { + error_message = "invalid string: control character U+0013 (DC3) must be escaped to \\u0013"; + return token_type::parse_error; + } + + case 0x14: + { + error_message = "invalid string: control character U+0014 (DC4) must be escaped to \\u0014"; + return token_type::parse_error; + } + + case 0x15: + { + error_message = "invalid string: control character U+0015 (NAK) must be escaped to \\u0015"; + return token_type::parse_error; + } + + case 0x16: + { + error_message = "invalid string: control character U+0016 (SYN) must be escaped to \\u0016"; + return token_type::parse_error; + } + + case 0x17: + { + error_message = "invalid string: control character U+0017 (ETB) must be escaped to \\u0017"; + return token_type::parse_error; + } + + case 0x18: + { + error_message = "invalid string: control character U+0018 (CAN) must be escaped to \\u0018"; + return token_type::parse_error; + } + + case 0x19: + { + error_message = "invalid string: control character U+0019 (EM) must be escaped to \\u0019"; + return token_type::parse_error; + } + + case 0x1A: + { + error_message = "invalid string: control character U+001A (SUB) must be escaped to \\u001A"; + return token_type::parse_error; + } + + case 0x1B: + { + error_message = "invalid string: control character U+001B (ESC) must be escaped to \\u001B"; + return token_type::parse_error; + } + + case 0x1C: + { + error_message = "invalid string: control character U+001C (FS) must be escaped to \\u001C"; + return token_type::parse_error; + } + + case 0x1D: + { + error_message = "invalid string: control character U+001D (GS) must be escaped to \\u001D"; + return token_type::parse_error; + } + + case 0x1E: + { + error_message = "invalid string: control character U+001E (RS) must be escaped to \\u001E"; + return token_type::parse_error; + } + + case 0x1F: + { + error_message = "invalid string: control character U+001F (US) must be escaped to \\u001F"; + return token_type::parse_error; + } + + // U+0020..U+007F (except U+0022 (quote) and U+005C (backspace)) + case 0x20: + case 0x21: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + { + add(current); + break; + } + + // U+0080..U+07FF: bytes C2..DF 80..BF + case 0xC2: + case 0xC3: + case 0xC4: + case 0xC5: + case 0xC6: + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD5: + case 0xD6: + case 0xD7: + case 0xD8: + case 0xD9: + case 0xDA: + case 0xDB: + case 0xDC: + case 0xDD: + case 0xDE: + case 0xDF: + { + if (JSON_HEDLEY_UNLIKELY(!next_byte_in_range({0x80, 0xBF}))) + { + return token_type::parse_error; + } + break; + } + + // U+0800..U+0FFF: bytes E0 A0..BF 80..BF + case 0xE0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0xA0, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+1000..U+CFFF: bytes E1..EC 80..BF 80..BF + // U+E000..U+FFFF: bytes EE..EF 80..BF 80..BF + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xEE: + case 0xEF: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+D000..U+D7FF: bytes ED 80..9F 80..BF + case 0xED: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x9F, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+10000..U+3FFFF F0 90..BF 80..BF 80..BF + case 0xF0: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x90, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+40000..U+FFFFF F1..F3 80..BF 80..BF 80..BF + case 0xF1: + case 0xF2: + case 0xF3: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0xBF, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // U+100000..U+10FFFF F4 80..8F 80..BF 80..BF + case 0xF4: + { + if (JSON_HEDLEY_UNLIKELY(!(next_byte_in_range({0x80, 0x8F, 0x80, 0xBF, 0x80, 0xBF})))) + { + return token_type::parse_error; + } + break; + } + + // remaining bytes (80..C1 and F5..FF) are ill-formed + default: + { + error_message = "invalid string: ill-formed UTF-8 byte"; + return token_type::parse_error; + } + } + } + } + + /*! + * @brief scan a comment + * @return whether comment could be scanned successfully + */ + bool scan_comment() + { + switch (get()) + { + // single-line comments skip input until a newline or EOF is read + case '/': + { + while (true) + { + switch (get()) + { + case '\n': + case '\r': + case std::char_traits::eof(): + case '\0': + return true; + + default: + break; + } + } + } + + // multi-line comments skip input until */ is read + case '*': + { + while (true) + { + switch (get()) + { + case std::char_traits::eof(): + case '\0': + { + error_message = "invalid comment; missing closing '*/'"; + return false; + } + + case '*': + { + switch (get()) + { + case '/': + return true; + + default: + { + unget(); + continue; + } + } + } + + default: + continue; + } + } + } + + // unexpected character after reading '/' + default: + { + error_message = "invalid comment; expecting '/' or '*' after '/'"; + return false; + } + } + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(float& f, const char* str, char** endptr) noexcept + { + f = std::strtof(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(double& f, const char* str, char** endptr) noexcept + { + f = std::strtod(str, endptr); + } + + JSON_HEDLEY_NON_NULL(2) + static void strtof(long double& f, const char* str, char** endptr) noexcept + { + f = std::strtold(str, endptr); + } + + /*! + @brief scan a number literal + + This function scans a string according to Sect. 6 of RFC 8259. + + The function is realized with a deterministic finite state machine derived + from the grammar described in RFC 8259. Starting in state "init", the + input is read and used to determined the next state. Only state "done" + accepts the number. State "error" is a trap state to model errors. In the + table below, "anything" means any character but the ones listed before. + + state | 0 | 1-9 | e E | + | - | . | anything + ---------|----------|----------|----------|---------|---------|----------|----------- + init | zero | any1 | [error] | [error] | minus | [error] | [error] + minus | zero | any1 | [error] | [error] | [error] | [error] | [error] + zero | done | done | exponent | done | done | decimal1 | done + any1 | any1 | any1 | exponent | done | done | decimal1 | done + decimal1 | decimal2 | decimal2 | [error] | [error] | [error] | [error] | [error] + decimal2 | decimal2 | decimal2 | exponent | done | done | done | done + exponent | any2 | any2 | [error] | sign | sign | [error] | [error] + sign | any2 | any2 | [error] | [error] | [error] | [error] | [error] + any2 | any2 | any2 | done | done | done | done | done + + The state machine is realized with one label per state (prefixed with + "scan_number_") and `goto` statements between them. The state machine + contains cycles, but any cycle can be left when EOF is read. Therefore, + the function is guaranteed to terminate. + + During scanning, the read bytes are stored in token_buffer. This string is + then converted to a signed integer, an unsigned integer, or a + floating-point number. + + @return token_type::value_unsigned, token_type::value_integer, or + token_type::value_float if number could be successfully scanned, + token_type::parse_error otherwise + + @note The scanner is independent of the current locale. Internally, the + locale's decimal point is used instead of `.` to work with the + locale-dependent converters. + */ + token_type scan_number() // lgtm [cpp/use-of-goto] + { + // reset token_buffer to store the number's bytes + reset(); + + // the type of the parsed number; initially set to unsigned; will be + // changed if minus sign, decimal point or exponent is read + token_type number_type = token_type::value_unsigned; + + // state (init): we just found out we need to scan a number + switch (current) + { + case '-': + { + add(current); + goto scan_number_minus; + } + + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + // all other characters are rejected outside scan_number() + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + +scan_number_minus: + // state: we just parsed a leading minus sign + number_type = token_type::value_integer; + switch (get()) + { + case '0': + { + add(current); + goto scan_number_zero; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + default: + { + error_message = "invalid number; expected digit after '-'"; + return token_type::parse_error; + } + } + +scan_number_zero: + // state: we just parse a zero (maybe with a leading minus sign) + switch (get()) + { + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_any1: + // state: we just parsed a number 0-9 (maybe with a leading minus sign) + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any1; + } + + case '.': + { + add(decimal_point_char); + goto scan_number_decimal1; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_decimal1: + // state: we just parsed a decimal point + number_type = token_type::value_float; + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + default: + { + error_message = "invalid number; expected digit after '.'"; + return token_type::parse_error; + } + } + +scan_number_decimal2: + // we just parsed at least one number after a decimal point + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_decimal2; + } + + case 'e': + case 'E': + { + add(current); + goto scan_number_exponent; + } + + default: + goto scan_number_done; + } + +scan_number_exponent: + // we just parsed an exponent + number_type = token_type::value_float; + switch (get()) + { + case '+': + case '-': + { + add(current); + goto scan_number_sign; + } + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = + "invalid number; expected '+', '-', or digit after exponent"; + return token_type::parse_error; + } + } + +scan_number_sign: + // we just parsed an exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + { + error_message = "invalid number; expected digit after exponent sign"; + return token_type::parse_error; + } + } + +scan_number_any2: + // we just parsed a number after the exponent or exponent sign + switch (get()) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + add(current); + goto scan_number_any2; + } + + default: + goto scan_number_done; + } + +scan_number_done: + // unget the character after the number (we only read it to know that + // we are done scanning a number) + unget(); + + char* endptr = nullptr; // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + errno = 0; + + // try to parse integers first and fall back to floats + if (number_type == token_type::value_unsigned) + { + const auto x = std::strtoull(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_unsigned = static_cast(x); + if (value_unsigned == x) + { + return token_type::value_unsigned; + } + } + } + else if (number_type == token_type::value_integer) + { + const auto x = std::strtoll(token_buffer.data(), &endptr, 10); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + if (errno == 0) + { + value_integer = static_cast(x); + if (value_integer == x) + { + return token_type::value_integer; + } + } + } + + // this code is reached if we parse a floating-point number or if an + // integer conversion above failed + strtof(value_float, token_buffer.data(), &endptr); + + // we checked the number format before + JSON_ASSERT(endptr == token_buffer.data() + token_buffer.size()); + + return token_type::value_float; + } + + /*! + @param[in] literal_text the literal text to expect + @param[in] length the length of the passed literal text + @param[in] return_type the token type to return on success + */ + JSON_HEDLEY_NON_NULL(2) + token_type scan_literal(const char_type* literal_text, const std::size_t length, + token_type return_type) + { + JSON_ASSERT(std::char_traits::to_char_type(current) == literal_text[0]); + for (std::size_t i = 1; i < length; ++i) + { + if (JSON_HEDLEY_UNLIKELY(std::char_traits::to_char_type(get()) != literal_text[i])) + { + error_message = "invalid literal"; + return token_type::parse_error; + } + } + return return_type; + } + + ///////////////////// + // input management + ///////////////////// + + /// reset token_buffer; current character is beginning of token + void reset() noexcept + { + token_buffer.clear(); + token_string.clear(); + token_string.push_back(std::char_traits::to_char_type(current)); + } + + /* + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a + `std::char_traits::eof()` in that case. Stores the scanned characters + for use in error messages. + + @return character read from the input + */ + char_int_type get() + { + ++position.chars_read_total; + ++position.chars_read_current_line; + + if (next_unget) + { + // just reset the next_unget variable and work with current + next_unget = false; + } + else + { + current = ia.get_character(); + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + token_string.push_back(std::char_traits::to_char_type(current)); + } + + if (current == '\n') + { + ++position.lines_read; + position.chars_read_current_line = 0; + } + + return current; + } + + /*! + @brief unget current character (read it again on next get) + + We implement unget by setting variable next_unget to true. The input is not + changed - we just simulate ungetting by modifying chars_read_total, + chars_read_current_line, and token_string. The next call to get() will + behave as if the unget character is read again. + */ + void unget() + { + next_unget = true; + + --position.chars_read_total; + + // in case we "unget" a newline, we have to also decrement the lines_read + if (position.chars_read_current_line == 0) + { + if (position.lines_read > 0) + { + --position.lines_read; + } + } + else + { + --position.chars_read_current_line; + } + + if (JSON_HEDLEY_LIKELY(current != std::char_traits::eof())) + { + JSON_ASSERT(!token_string.empty()); + token_string.pop_back(); + } + } + + /// add a character to token_buffer + void add(char_int_type c) + { + token_buffer.push_back(static_cast(c)); + } + + public: + ///////////////////// + // value getters + ///////////////////// + + /// return integer value + constexpr number_integer_t get_number_integer() const noexcept + { + return value_integer; + } + + /// return unsigned integer value + constexpr number_unsigned_t get_number_unsigned() const noexcept + { + return value_unsigned; + } + + /// return floating-point value + constexpr number_float_t get_number_float() const noexcept + { + return value_float; + } + + /// return current string value (implicitly resets the token; useful only once) + string_t& get_string() + { + return token_buffer; + } + + ///////////////////// + // diagnostics + ///////////////////// + + /// return position of last read token + constexpr position_t get_position() const noexcept + { + return position; + } + + /// return the last read token (for errors only). Will never contain EOF + /// (an arbitrary value that is not a valid char value, often -1), because + /// 255 may legitimately occur. May contain NUL, which should be escaped. + std::string get_token_string() const + { + // escape control characters + std::string result; + for (const auto c : token_string) + { + if (static_cast(c) <= '\x1F') + { + // escape control characters + std::array cs{{}}; + (std::snprintf)(cs.data(), cs.size(), "", static_cast(c)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + result += cs.data(); + } + else + { + // add character as is + result.push_back(static_cast(c)); + } + } + + return result; + } + + /// return syntax error message + JSON_HEDLEY_RETURNS_NON_NULL + constexpr const char* get_error_message() const noexcept + { + return error_message; + } + + ///////////////////// + // actual scanner + ///////////////////// + + /*! + @brief skip the UTF-8 byte order mark + @return true iff there is no BOM or the correct BOM has been skipped + */ + bool skip_bom() + { + if (get() == 0xEF) + { + // check if we completely parse the BOM + return get() == 0xBB && get() == 0xBF; + } + + // the first character is not the beginning of the BOM; unget it to + // process is later + unget(); + return true; + } + + void skip_whitespace() + { + do + { + get(); + } + while (current == ' ' || current == '\t' || current == '\n' || current == '\r'); + } + + token_type scan() + { + // initially, skip the BOM + if (position.chars_read_total == 0 && !skip_bom()) + { + error_message = "invalid BOM; must be 0xEF 0xBB 0xBF if given"; + return token_type::parse_error; + } + + // read next character and ignore whitespace + skip_whitespace(); + + // ignore comments + while (ignore_comments && current == '/') + { + if (!scan_comment()) + { + return token_type::parse_error; + } + + // skip following whitespace + skip_whitespace(); + } + + switch (current) + { + // structural characters + case '[': + return token_type::begin_array; + case ']': + return token_type::end_array; + case '{': + return token_type::begin_object; + case '}': + return token_type::end_object; + case ':': + return token_type::name_separator; + case ',': + return token_type::value_separator; + + // literals + case 't': + { + std::array true_literal = {{char_type('t'), char_type('r'), char_type('u'), char_type('e')}}; + return scan_literal(true_literal.data(), true_literal.size(), token_type::literal_true); + } + case 'f': + { + std::array false_literal = {{char_type('f'), char_type('a'), char_type('l'), char_type('s'), char_type('e')}}; + return scan_literal(false_literal.data(), false_literal.size(), token_type::literal_false); + } + case 'n': + { + std::array null_literal = {{char_type('n'), char_type('u'), char_type('l'), char_type('l')}}; + return scan_literal(null_literal.data(), null_literal.size(), token_type::literal_null); + } + + // string + case '\"': + return scan_string(); + + // number + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return scan_number(); + + // end of input (the null byte is needed when parsing from + // string literals) + case '\0': + case std::char_traits::eof(): + return token_type::end_of_input; + + // error + default: + error_message = "invalid literal"; + return token_type::parse_error; + } + } + + private: + /// input adapter + InputAdapterType ia; + + /// whether comments should be ignored (true) or signaled as errors (false) + const bool ignore_comments = false; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// whether the next get() call should just return current + bool next_unget = false; + + /// the start position of the current token + position_t position {}; + + /// raw input token string (for error messages) + std::vector token_string {}; + + /// buffer for variable-length tokens (numbers, strings) + string_t token_buffer {}; + + /// a description of occurred lexer errors + const char* error_message = ""; + + // number values + number_integer_t value_integer = 0; + number_unsigned_t value_unsigned = 0; + number_float_t value_float = 0; + + /// the decimal point + const char_int_type decimal_point_char = '.'; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // size_t +#include // declval +#include // string + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +using null_function_t = decltype(std::declval().null()); + +template +using boolean_function_t = + decltype(std::declval().boolean(std::declval())); + +template +using number_integer_function_t = + decltype(std::declval().number_integer(std::declval())); + +template +using number_unsigned_function_t = + decltype(std::declval().number_unsigned(std::declval())); + +template +using number_float_function_t = decltype(std::declval().number_float( + std::declval(), std::declval())); + +template +using string_function_t = + decltype(std::declval().string(std::declval())); + +template +using binary_function_t = + decltype(std::declval().binary(std::declval())); + +template +using start_object_function_t = + decltype(std::declval().start_object(std::declval())); + +template +using key_function_t = + decltype(std::declval().key(std::declval())); + +template +using end_object_function_t = decltype(std::declval().end_object()); + +template +using start_array_function_t = + decltype(std::declval().start_array(std::declval())); + +template +using end_array_function_t = decltype(std::declval().end_array()); + +template +using parse_error_function_t = decltype(std::declval().parse_error( + std::declval(), std::declval(), + std::declval())); + +template +struct is_sax +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static constexpr bool value = + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value && + is_detected_exact::value; +}; + +template +struct is_sax_static_asserts +{ + private: + static_assert(is_basic_json::value, + "BasicJsonType must be of type basic_json<...>"); + + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using exception_t = typename BasicJsonType::exception; + + public: + static_assert(is_detected_exact::value, + "Missing/invalid function: bool null()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool boolean(bool)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_integer(number_integer_t)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool number_unsigned(number_unsigned_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool number_float(number_float_t, const string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool string(string_t&)"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool binary(binary_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_object(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool key(string_t&)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_object()"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool start_array(std::size_t)"); + static_assert(is_detected_exact::value, + "Missing/invalid function: bool end_array()"); + static_assert( + is_detected_exact::value, + "Missing/invalid function: bool parse_error(std::size_t, const " + "std::string&, const exception&)"); +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/// how to treat CBOR tags +enum class cbor_tag_handler_t +{ + error, ///< throw a parse_error exception in case of a tag + ignore, ///< ignore tags + store ///< store tags as binary type +}; + +/*! +@brief determine system byte order + +@return true if and only if system's byte order is little endian + +@note from https://stackoverflow.com/a/1001328/266378 +*/ +static inline bool little_endianess(int num = 1) noexcept +{ + return *reinterpret_cast(&num) == 1; +} + + +/////////////////// +// binary reader // +/////////////////// + +/*! +@brief deserialization of CBOR, MessagePack, and UBJSON values +*/ +template> +class binary_reader +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using json_sax_t = SAX; + using char_type = typename InputAdapterType::char_type; + using char_int_type = typename std::char_traits::int_type; + + public: + /*! + @brief create a binary reader + + @param[in] adapter input adapter to read from + */ + explicit binary_reader(InputAdapterType&& adapter) noexcept : ia(std::move(adapter)) + { + (void)detail::is_sax_static_asserts {}; + } + + // make class move-only + binary_reader(const binary_reader&) = delete; + binary_reader(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + binary_reader& operator=(const binary_reader&) = delete; + binary_reader& operator=(binary_reader&&) = default; // NOLINT(hicpp-noexcept-move,performance-noexcept-move-constructor) + ~binary_reader() = default; + + /*! + @param[in] format the binary format to parse + @param[in] sax_ a SAX event processor + @param[in] strict whether to expect the input to be consumed completed + @param[in] tag_handler how to treat CBOR tags + + @return whether parsing was successful + */ + JSON_HEDLEY_NON_NULL(3) + bool sax_parse(const input_format_t format, + json_sax_t* sax_, + const bool strict = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + sax = sax_; + bool result = false; + + switch (format) + { + case input_format_t::bson: + result = parse_bson_internal(); + break; + + case input_format_t::cbor: + result = parse_cbor_internal(true, tag_handler); + break; + + case input_format_t::msgpack: + result = parse_msgpack_internal(); + break; + + case input_format_t::ubjson: + result = parse_ubjson_internal(); + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + // strict mode: next byte must be EOF + if (result && strict) + { + if (format == input_format_t::ubjson) + { + get_ignore_noop(); + } + else + { + get(); + } + + if (JSON_HEDLEY_UNLIKELY(current != std::char_traits::eof())) + { + return sax->parse_error(chars_read, get_token_string(), + parse_error::create(110, chars_read, exception_message(format, "expected end of input; last byte: 0x" + get_token_string(), "value"), BasicJsonType())); + } + } + + return result; + } + + private: + ////////// + // BSON // + ////////// + + /*! + @brief Reads in a BSON-object and passes it to the SAX-parser. + @return whether a valid BSON-value was passed to the SAX parser + */ + bool parse_bson_internal() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/false))) + { + return false; + } + + return sax->end_object(); + } + + /*! + @brief Parses a C-style string from the BSON input. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @return `true` if the \x00-byte indicating the end of the string was + encountered before the EOF; false` indicates an unexpected EOF. + */ + bool get_bson_cstr(string_t& result) + { + auto out = std::back_inserter(result); + while (true) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "cstring"))) + { + return false; + } + if (current == 0x00) + { + return true; + } + *out++ = static_cast(current); + } + } + + /*! + @brief Parses a zero-terminated string of length @a len from the BSON + input. + @param[in] len The length (including the zero-byte at the end) of the + string to be read. + @param[in,out] result A reference to the string variable where the read + string is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 1 + @return `true` if the string was successfully parsed + */ + template + bool get_bson_string(const NumberType len, string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 1)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "string length must be at least 1, is " + std::to_string(len), "string"), BasicJsonType())); + } + + return get_string(input_format_t::bson, len - static_cast(1), result) && get() != std::char_traits::eof(); + } + + /*! + @brief Parses a byte array input of length @a len from the BSON input. + @param[in] len The length of the byte array to be read. + @param[in,out] result A reference to the binary variable where the read + array is to be stored. + @tparam NumberType The type of the length @a len + @pre len >= 0 + @return `true` if the byte array was successfully parsed + */ + template + bool get_bson_binary(const NumberType len, binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(len < 0)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::bson, "byte array length cannot be negative, is " + std::to_string(len), "binary"), BasicJsonType())); + } + + // All BSON binary values have a subtype + std::uint8_t subtype{}; + get_number(input_format_t::bson, subtype); + result.set_subtype(subtype); + + return get_binary(input_format_t::bson, len, result); + } + + /*! + @brief Read a BSON document element of the given @a element_type. + @param[in] element_type The BSON element type, c.f. http://bsonspec.org/spec.html + @param[in] element_type_parse_position The position in the input stream, + where the `element_type` was read. + @warning Not all BSON element types are supported yet. An unsupported + @a element_type will give rise to a parse_error.114: + Unsupported BSON record type 0x... + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_internal(const char_int_type element_type, + const std::size_t element_type_parse_position) + { + switch (element_type) + { + case 0x01: // double + { + double number{}; + return get_number(input_format_t::bson, number) && sax->number_float(static_cast(number), ""); + } + + case 0x02: // string + { + std::int32_t len{}; + string_t value; + return get_number(input_format_t::bson, len) && get_bson_string(len, value) && sax->string(value); + } + + case 0x03: // object + { + return parse_bson_internal(); + } + + case 0x04: // array + { + return parse_bson_array(); + } + + case 0x05: // binary + { + std::int32_t len{}; + binary_t value; + return get_number(input_format_t::bson, len) && get_bson_binary(len, value) && sax->binary(value); + } + + case 0x08: // boolean + { + return sax->boolean(get() != 0); + } + + case 0x0A: // null + { + return sax->null(); + } + + case 0x10: // int32 + { + std::int32_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + case 0x12: // int64 + { + std::int64_t value{}; + return get_number(input_format_t::bson, value) && sax->number_integer(value); + } + + default: // anything else not supported (yet) + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(element_type)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return sax->parse_error(element_type_parse_position, std::string(cr.data()), parse_error::create(114, element_type_parse_position, "Unsupported BSON record type 0x" + std::string(cr.data()), BasicJsonType())); + } + } + } + + /*! + @brief Read a BSON element list (as specified in the BSON-spec) + + The same binary layout is used for objects and arrays, hence it must be + indicated with the argument @a is_array which one is expected + (true --> array, false --> object). + + @param[in] is_array Determines if the element list being read is to be + treated as an object (@a is_array == false), or as an + array (@a is_array == true). + @return whether a valid BSON-object/array was passed to the SAX parser + */ + bool parse_bson_element_list(const bool is_array) + { + string_t key; + + while (auto element_type = get()) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::bson, "element list"))) + { + return false; + } + + const std::size_t element_type_parse_position = chars_read; + if (JSON_HEDLEY_UNLIKELY(!get_bson_cstr(key))) + { + return false; + } + + if (!is_array && !sax->key(key)) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_internal(element_type, element_type_parse_position))) + { + return false; + } + + // get_bson_cstr only appends + key.clear(); + } + + return true; + } + + /*! + @brief Reads an array from the BSON input and passes it to the SAX-parser. + @return whether a valid BSON-array was passed to the SAX parser + */ + bool parse_bson_array() + { + std::int32_t document_size{}; + get_number(input_format_t::bson, document_size); + + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_bson_element_list(/*is_array*/true))) + { + return false; + } + + return sax->end_array(); + } + + ////////// + // CBOR // + ////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true) or whether the last read character should + be considered instead (false) + @param[in] tag_handler how CBOR tags should be treated + + @return whether a valid CBOR value was passed to the SAX parser + */ + bool parse_cbor_internal(const bool get_char, + const cbor_tag_handler_t tag_handler) + { + switch (get_char ? get() : current) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::cbor, "value"); + + // Integer 0x00..0x17 (0..23) + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + return sax->number_unsigned(static_cast(current)); + + case 0x18: // Unsigned integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x19: // Unsigned integer (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1A: // Unsigned integer (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + case 0x1B: // Unsigned integer (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_unsigned(number); + } + + // Negative integer -1-0x00..-1-0x17 (-1..-24) + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + return sax->number_integer(static_cast(0x20 - 1 - current)); + + case 0x38: // Negative integer (one-byte uint8_t follows) + { + std::uint8_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x39: // Negative integer -1-n (two-byte uint16_t follows) + { + std::uint16_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3A: // Negative integer -1-n (four-byte uint32_t follows) + { + std::uint32_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) - number); + } + + case 0x3B: // Negative integer -1-n (eight-byte uint64_t follows) + { + std::uint64_t number{}; + return get_number(input_format_t::cbor, number) && sax->number_integer(static_cast(-1) + - static_cast(number)); + } + + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: // Binary data (one-byte uint8_t for n follows) + case 0x59: // Binary data (two-byte uint16_t for n follow) + case 0x5A: // Binary data (four-byte uint32_t for n follow) + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + case 0x5F: // Binary data (indefinite length) + { + binary_t b; + return get_cbor_binary(b) && sax->binary(b); + } + + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + case 0x7F: // UTF-8 string (indefinite length) + { + string_t s; + return get_cbor_string(s) && sax->string(s); + } + + // array (0x00..0x17 data items follow) + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + return get_cbor_array(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0x98: // array (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x99: // array (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9A: // array (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(static_cast(len), tag_handler); + } + + case 0x9B: // array (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_array(detail::conditional_static_cast(len), tag_handler); + } + + case 0x9F: // array (indefinite length) + return get_cbor_array(std::size_t(-1), tag_handler); + + // map (0x00..0x17 pairs of data items follow) + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + return get_cbor_object(static_cast(static_cast(current) & 0x1Fu), tag_handler); + + case 0xB8: // map (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xB9: // map (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBA: // map (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(static_cast(len), tag_handler); + } + + case 0xBB: // map (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_cbor_object(detail::conditional_static_cast(len), tag_handler); + } + + case 0xBF: // map (indefinite length) + return get_cbor_object(std::size_t(-1), tag_handler); + + case 0xC6: // tagged item + case 0xC7: + case 0xC8: + case 0xC9: + case 0xCA: + case 0xCB: + case 0xCC: + case 0xCD: + case 0xCE: + case 0xCF: + case 0xD0: + case 0xD1: + case 0xD2: + case 0xD3: + case 0xD4: + case 0xD8: // tagged item (1 bytes follow) + case 0xD9: // tagged item (2 bytes follow) + case 0xDA: // tagged item (4 bytes follow) + case 0xDB: // tagged item (8 bytes follow) + { + switch (tag_handler) + { + case cbor_tag_handler_t::error: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + + case cbor_tag_handler_t::ignore: + { + // ignore binary subtype + switch (current) + { + case 0xD8: + { + std::uint8_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xD9: + { + std::uint16_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDA: + { + std::uint32_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + case 0xDB: + { + std::uint64_t subtype_to_ignore{}; + get_number(input_format_t::cbor, subtype_to_ignore); + break; + } + default: + break; + } + return parse_cbor_internal(true, tag_handler); + } + + case cbor_tag_handler_t::store: + { + binary_t b; + // use binary subtype and store in binary container + switch (current) + { + case 0xD8: + { + std::uint8_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xD9: + { + std::uint16_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDA: + { + std::uint32_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + case 0xDB: + { + std::uint64_t subtype{}; + get_number(input_format_t::cbor, subtype); + b.set_subtype(detail::conditional_static_cast(subtype)); + break; + } + default: + return parse_cbor_internal(true, tag_handler); + } + get(); + return get_cbor_binary(b) && sax->binary(b); + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + case 0xF4: // false + return sax->boolean(false); + + case 0xF5: // true + return sax->boolean(true); + + case 0xF6: // null + return sax->null(); + + case 0xF9: // Half-Precision Float (two-byte IEEE 754) + { + const auto byte1_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + const auto byte2_raw = get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "number"))) + { + return false; + } + + const auto byte1 = static_cast(byte1_raw); + const auto byte2 = static_cast(byte2_raw); + + // code from RFC 7049, Appendix D, Figure 3: + // As half-precision floating-point numbers were only added + // to IEEE 754 in 2008, today's programming platforms often + // still only have limited support for them. It is very + // easy to include at least decoding support for them even + // without such support. An example of a small decoder for + // half-precision floating-point numbers in the C language + // is shown in Fig. 3. + const auto half = static_cast((byte1 << 8u) + byte2); + const double val = [&half] + { + const int exp = (half >> 10u) & 0x1Fu; + const unsigned int mant = half & 0x3FFu; + JSON_ASSERT(0 <= exp&& exp <= 32); + JSON_ASSERT(mant <= 1024); + switch (exp) + { + case 0: + return std::ldexp(mant, -24); + case 31: + return (mant == 0) + ? std::numeric_limits::infinity() + : std::numeric_limits::quiet_NaN(); + default: + return std::ldexp(mant + 1024, exp - 25); + } + }(); + return sax->number_float((half & 0x8000u) != 0 + ? static_cast(-val) + : static_cast(val), ""); + } + + case 0xFA: // Single-Precision Float (four-byte IEEE 754) + { + float number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + case 0xFB: // Double-Precision Float (eight-byte IEEE 754) + { + double number{}; + return get_number(input_format_t::cbor, number) && sax->number_float(static_cast(number), ""); + } + + default: // anything else (0xFF is handled inside the other types) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::cbor, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + Additionally, CBOR's strings with indefinite lengths are supported. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_cbor_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "string"))) + { + return false; + } + + switch (current) + { + // UTF-8 string (0x00..0x17 bytes follow) + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + { + return get_string(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x78: // UTF-8 string (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x79: // UTF-8 string (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7A: // UTF-8 string (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7B: // UTF-8 string (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && get_string(input_format_t::cbor, len, result); + } + + case 0x7F: // UTF-8 string (indefinite length) + { + while (get() != 0xFF) + { + string_t chunk; + if (!get_cbor_string(chunk)) + { + return false; + } + result.append(chunk); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x60-0x7B) or indefinite string type (0x7F); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a CBOR byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into the byte array. + Additionally, CBOR's byte arrays with indefinite lengths are supported. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_cbor_binary(binary_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::cbor, "binary"))) + { + return false; + } + + switch (current) + { + // Binary data (0x00..0x17 bytes follow) + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + { + return get_binary(input_format_t::cbor, static_cast(current) & 0x1Fu, result); + } + + case 0x58: // Binary data (one-byte uint8_t for n follows) + { + std::uint8_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x59: // Binary data (two-byte uint16_t for n follow) + { + std::uint16_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5A: // Binary data (four-byte uint32_t for n follow) + { + std::uint32_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5B: // Binary data (eight-byte uint64_t for n follow) + { + std::uint64_t len{}; + return get_number(input_format_t::cbor, len) && + get_binary(input_format_t::cbor, len, result); + } + + case 0x5F: // Binary data (indefinite length) + { + while (get() != 0xFF) + { + binary_t chunk; + if (!get_cbor_binary(chunk)) + { + return false; + } + result.insert(result.end(), chunk.begin(), chunk.end()); + } + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::cbor, "expected length specification (0x40-0x5B) or indefinite binary array type (0x5F); last byte: 0x" + last_token, "binary"), BasicJsonType())); + } + } + } + + /*! + @param[in] len the length of the array or std::size_t(-1) for an + array of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether array creation completed + */ + bool get_cbor_array(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(false, tag_handler))) + { + return false; + } + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object or std::size_t(-1) for an + object of indefinite size + @param[in] tag_handler how CBOR tags should be treated + @return whether object creation completed + */ + bool get_cbor_object(const std::size_t len, + const cbor_tag_handler_t tag_handler) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + if (len != 0) + { + string_t key; + if (len != std::size_t(-1)) + { + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + else + { + while (get() != 0xFF) + { + if (JSON_HEDLEY_UNLIKELY(!get_cbor_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_cbor_internal(true, tag_handler))) + { + return false; + } + key.clear(); + } + } + } + + return sax->end_object(); + } + + ///////////// + // MsgPack // + ///////////// + + /*! + @return whether a valid MessagePack value was passed to the SAX parser + */ + bool parse_msgpack_internal() + { + switch (get()) + { + // EOF + case std::char_traits::eof(): + return unexpect_eof(input_format_t::msgpack, "value"); + + // positive fixint + case 0x00: + case 0x01: + case 0x02: + case 0x03: + case 0x04: + case 0x05: + case 0x06: + case 0x07: + case 0x08: + case 0x09: + case 0x0A: + case 0x0B: + case 0x0C: + case 0x0D: + case 0x0E: + case 0x0F: + case 0x10: + case 0x11: + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + case 0x19: + case 0x1A: + case 0x1B: + case 0x1C: + case 0x1D: + case 0x1E: + case 0x1F: + case 0x20: + case 0x21: + case 0x22: + case 0x23: + case 0x24: + case 0x25: + case 0x26: + case 0x27: + case 0x28: + case 0x29: + case 0x2A: + case 0x2B: + case 0x2C: + case 0x2D: + case 0x2E: + case 0x2F: + case 0x30: + case 0x31: + case 0x32: + case 0x33: + case 0x34: + case 0x35: + case 0x36: + case 0x37: + case 0x38: + case 0x39: + case 0x3A: + case 0x3B: + case 0x3C: + case 0x3D: + case 0x3E: + case 0x3F: + case 0x40: + case 0x41: + case 0x42: + case 0x43: + case 0x44: + case 0x45: + case 0x46: + case 0x47: + case 0x48: + case 0x49: + case 0x4A: + case 0x4B: + case 0x4C: + case 0x4D: + case 0x4E: + case 0x4F: + case 0x50: + case 0x51: + case 0x52: + case 0x53: + case 0x54: + case 0x55: + case 0x56: + case 0x57: + case 0x58: + case 0x59: + case 0x5A: + case 0x5B: + case 0x5C: + case 0x5D: + case 0x5E: + case 0x5F: + case 0x60: + case 0x61: + case 0x62: + case 0x63: + case 0x64: + case 0x65: + case 0x66: + case 0x67: + case 0x68: + case 0x69: + case 0x6A: + case 0x6B: + case 0x6C: + case 0x6D: + case 0x6E: + case 0x6F: + case 0x70: + case 0x71: + case 0x72: + case 0x73: + case 0x74: + case 0x75: + case 0x76: + case 0x77: + case 0x78: + case 0x79: + case 0x7A: + case 0x7B: + case 0x7C: + case 0x7D: + case 0x7E: + case 0x7F: + return sax->number_unsigned(static_cast(current)); + + // fixmap + case 0x80: + case 0x81: + case 0x82: + case 0x83: + case 0x84: + case 0x85: + case 0x86: + case 0x87: + case 0x88: + case 0x89: + case 0x8A: + case 0x8B: + case 0x8C: + case 0x8D: + case 0x8E: + case 0x8F: + return get_msgpack_object(static_cast(static_cast(current) & 0x0Fu)); + + // fixarray + case 0x90: + case 0x91: + case 0x92: + case 0x93: + case 0x94: + case 0x95: + case 0x96: + case 0x97: + case 0x98: + case 0x99: + case 0x9A: + case 0x9B: + case 0x9C: + case 0x9D: + case 0x9E: + case 0x9F: + return get_msgpack_array(static_cast(static_cast(current) & 0x0Fu)); + + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + case 0xD9: // str 8 + case 0xDA: // str 16 + case 0xDB: // str 32 + { + string_t s; + return get_msgpack_string(s) && sax->string(s); + } + + case 0xC0: // nil + return sax->null(); + + case 0xC2: // false + return sax->boolean(false); + + case 0xC3: // true + return sax->boolean(true); + + case 0xC4: // bin 8 + case 0xC5: // bin 16 + case 0xC6: // bin 32 + case 0xC7: // ext 8 + case 0xC8: // ext 16 + case 0xC9: // ext 32 + case 0xD4: // fixext 1 + case 0xD5: // fixext 2 + case 0xD6: // fixext 4 + case 0xD7: // fixext 8 + case 0xD8: // fixext 16 + { + binary_t b; + return get_msgpack_binary(b) && sax->binary(b); + } + + case 0xCA: // float 32 + { + float number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCB: // float 64 + { + double number{}; + return get_number(input_format_t::msgpack, number) && sax->number_float(static_cast(number), ""); + } + + case 0xCC: // uint 8 + { + std::uint8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCD: // uint 16 + { + std::uint16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCE: // uint 32 + { + std::uint32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xCF: // uint 64 + { + std::uint64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_unsigned(number); + } + + case 0xD0: // int 8 + { + std::int8_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD1: // int 16 + { + std::int16_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD2: // int 32 + { + std::int32_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xD3: // int 64 + { + std::int64_t number{}; + return get_number(input_format_t::msgpack, number) && sax->number_integer(number); + } + + case 0xDC: // array 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDD: // array 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_array(static_cast(len)); + } + + case 0xDE: // map 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + case 0xDF: // map 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_msgpack_object(static_cast(len)); + } + + // negative fixint + case 0xE0: + case 0xE1: + case 0xE2: + case 0xE3: + case 0xE4: + case 0xE5: + case 0xE6: + case 0xE7: + case 0xE8: + case 0xE9: + case 0xEA: + case 0xEB: + case 0xEC: + case 0xED: + case 0xEE: + case 0xEF: + case 0xF0: + case 0xF1: + case 0xF2: + case 0xF3: + case 0xF4: + case 0xF5: + case 0xF6: + case 0xF7: + case 0xF8: + case 0xF9: + case 0xFA: + case 0xFB: + case 0xFC: + case 0xFD: + case 0xFE: + case 0xFF: + return sax->number_integer(static_cast(current)); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::msgpack, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack string + + This function first reads starting bytes to determine the expected + string length and then copies this number of bytes into a string. + + @param[out] result created string + + @return whether string creation completed + */ + bool get_msgpack_string(string_t& result) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::msgpack, "string"))) + { + return false; + } + + switch (current) + { + // fixstr + case 0xA0: + case 0xA1: + case 0xA2: + case 0xA3: + case 0xA4: + case 0xA5: + case 0xA6: + case 0xA7: + case 0xA8: + case 0xA9: + case 0xAA: + case 0xAB: + case 0xAC: + case 0xAD: + case 0xAE: + case 0xAF: + case 0xB0: + case 0xB1: + case 0xB2: + case 0xB3: + case 0xB4: + case 0xB5: + case 0xB6: + case 0xB7: + case 0xB8: + case 0xB9: + case 0xBA: + case 0xBB: + case 0xBC: + case 0xBD: + case 0xBE: + case 0xBF: + { + return get_string(input_format_t::msgpack, static_cast(current) & 0x1Fu, result); + } + + case 0xD9: // str 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDA: // str 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + case 0xDB: // str 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && get_string(input_format_t::msgpack, len, result); + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::msgpack, "expected length specification (0xA0-0xBF, 0xD9-0xDB); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + } + + /*! + @brief reads a MessagePack byte array + + This function first reads starting bytes to determine the expected + byte array length and then copies this number of bytes into a byte array. + + @param[out] result created byte array + + @return whether byte array creation completed + */ + bool get_msgpack_binary(binary_t& result) + { + // helper function to set the subtype + auto assign_and_return_true = [&result](std::int8_t subtype) + { + result.set_subtype(static_cast(subtype)); + return true; + }; + + switch (current) + { + case 0xC4: // bin 8 + { + std::uint8_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC5: // bin 16 + { + std::uint16_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC6: // bin 32 + { + std::uint32_t len{}; + return get_number(input_format_t::msgpack, len) && + get_binary(input_format_t::msgpack, len, result); + } + + case 0xC7: // ext 8 + { + std::uint8_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC8: // ext 16 + { + std::uint16_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xC9: // ext 32 + { + std::uint32_t len{}; + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, len) && + get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, len, result) && + assign_and_return_true(subtype); + } + + case 0xD4: // fixext 1 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 1, result) && + assign_and_return_true(subtype); + } + + case 0xD5: // fixext 2 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 2, result) && + assign_and_return_true(subtype); + } + + case 0xD6: // fixext 4 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 4, result) && + assign_and_return_true(subtype); + } + + case 0xD7: // fixext 8 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 8, result) && + assign_and_return_true(subtype); + } + + case 0xD8: // fixext 16 + { + std::int8_t subtype{}; + return get_number(input_format_t::msgpack, subtype) && + get_binary(input_format_t::msgpack, 16, result) && + assign_and_return_true(subtype); + } + + default: // LCOV_EXCL_LINE + return false; // LCOV_EXCL_LINE + } + } + + /*! + @param[in] len the length of the array + @return whether array creation completed + */ + bool get_msgpack_array(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(len))) + { + return false; + } + + for (std::size_t i = 0; i < len; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + } + + return sax->end_array(); + } + + /*! + @param[in] len the length of the object + @return whether object creation completed + */ + bool get_msgpack_object(const std::size_t len) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(len))) + { + return false; + } + + string_t key; + for (std::size_t i = 0; i < len; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!get_msgpack_string(key) || !sax->key(key))) + { + return false; + } + + if (JSON_HEDLEY_UNLIKELY(!parse_msgpack_internal())) + { + return false; + } + key.clear(); + } + + return sax->end_object(); + } + + //////////// + // UBJSON // + //////////// + + /*! + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether a valid UBJSON value was passed to the SAX parser + */ + bool parse_ubjson_internal(const bool get_char = true) + { + return get_ubjson_value(get_char ? get_ignore_noop() : current); + } + + /*! + @brief reads a UBJSON string + + This function is either called after reading the 'S' byte explicitly + indicating a string, or in case of an object key where the 'S' byte can be + left out. + + @param[out] result created string + @param[in] get_char whether a new character should be retrieved from the + input (true, default) or whether the last read + character should be considered instead + + @return whether string creation completed + */ + bool get_ubjson_string(string_t& result, const bool get_char = true) + { + if (get_char) + { + get(); // TODO(niels): may we ignore N here? + } + + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + + switch (current) + { + case 'U': + { + std::uint8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'i': + { + std::int8_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'I': + { + std::int16_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'l': + { + std::int32_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + case 'L': + { + std::int64_t len{}; + return get_number(input_format_t::ubjson, len) && get_string(input_format_t::ubjson, len, result); + } + + default: + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L); last byte: 0x" + last_token, "string"), BasicJsonType())); + } + } + + /*! + @param[out] result determined size + @return whether size determination completed + */ + bool get_ubjson_size_value(std::size_t& result) + { + switch (get_ignore_noop()) + { + case 'U': + { + std::uint8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'i': + { + std::int8_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); // NOLINT(bugprone-signed-char-misuse,cert-str34-c): number is not a char + return true; + } + + case 'I': + { + std::int16_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'l': + { + std::int32_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + case 'L': + { + std::int64_t number{}; + if (JSON_HEDLEY_UNLIKELY(!get_number(input_format_t::ubjson, number))) + { + return false; + } + result = static_cast(number); + return true; + } + + default: + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "expected length type specification (U, i, I, l, L) after '#'; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + } + } + + /*! + @brief determine the type and size for a container + + In the optimized UBJSON format, a type and a size can be provided to allow + for a more compact representation. + + @param[out] result pair of the size and the type + + @return whether pair creation completed + */ + bool get_ubjson_size_type(std::pair& result) + { + result.first = string_t::npos; // size + result.second = 0; // type + + get_ignore_noop(); + + if (current == '$') + { + result.second = get(); // must not ignore 'N', because 'N' maybe the type + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "type"))) + { + return false; + } + + get_ignore_noop(); + if (JSON_HEDLEY_UNLIKELY(current != '#')) + { + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "value"))) + { + return false; + } + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "expected '#' after type information; last byte: 0x" + last_token, "size"), BasicJsonType())); + } + + return get_ubjson_size_value(result.first); + } + + if (current == '#') + { + return get_ubjson_size_value(result.first); + } + + return true; + } + + /*! + @param prefix the previously read or set type prefix + @return whether value creation completed + */ + bool get_ubjson_value(const char_int_type prefix) + { + switch (prefix) + { + case std::char_traits::eof(): // EOF + return unexpect_eof(input_format_t::ubjson, "value"); + + case 'T': // true + return sax->boolean(true); + case 'F': // false + return sax->boolean(false); + + case 'Z': // null + return sax->null(); + + case 'U': + { + std::uint8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_unsigned(number); + } + + case 'i': + { + std::int8_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'I': + { + std::int16_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'l': + { + std::int32_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'L': + { + std::int64_t number{}; + return get_number(input_format_t::ubjson, number) && sax->number_integer(number); + } + + case 'd': + { + float number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'D': + { + double number{}; + return get_number(input_format_t::ubjson, number) && sax->number_float(static_cast(number), ""); + } + + case 'H': + { + return get_ubjson_high_precision_number(); + } + + case 'C': // char + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "char"))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(current > 127)) + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(113, chars_read, exception_message(input_format_t::ubjson, "byte after 'C' must be in range 0x00..0x7F; last byte: 0x" + last_token, "char"), BasicJsonType())); + } + string_t s(1, static_cast(current)); + return sax->string(s); + } + + case 'S': // string + { + string_t s; + return get_ubjson_string(s) && sax->string(s); + } + + case '[': // array + return get_ubjson_array(); + + case '{': // object + return get_ubjson_object(); + + default: // anything else + { + auto last_token = get_token_string(); + return sax->parse_error(chars_read, last_token, parse_error::create(112, chars_read, exception_message(input_format_t::ubjson, "invalid byte: 0x" + last_token, "value"), BasicJsonType())); + } + } + } + + /*! + @return whether array creation completed + */ + bool get_ubjson_array() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + if (size_and_type.second != 'N') + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + } + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + while (current != ']') + { + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal(false))) + { + return false; + } + get_ignore_noop(); + } + } + + return sax->end_array(); + } + + /*! + @return whether object creation completed + */ + bool get_ubjson_object() + { + std::pair size_and_type; + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_size_type(size_and_type))) + { + return false; + } + + string_t key; + if (size_and_type.first != string_t::npos) + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(size_and_type.first))) + { + return false; + } + + if (size_and_type.second != 0) + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_value(size_and_type.second))) + { + return false; + } + key.clear(); + } + } + else + { + for (std::size_t i = 0; i < size_and_type.first; ++i) + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + key.clear(); + } + } + } + else + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + while (current != '}') + { + if (JSON_HEDLEY_UNLIKELY(!get_ubjson_string(key, false) || !sax->key(key))) + { + return false; + } + if (JSON_HEDLEY_UNLIKELY(!parse_ubjson_internal())) + { + return false; + } + get_ignore_noop(); + key.clear(); + } + } + + return sax->end_object(); + } + + // Note, no reader for UBJSON binary types is implemented because they do + // not exist + + bool get_ubjson_high_precision_number() + { + // get size of following number string + std::size_t size{}; + auto res = get_ubjson_size_value(size); + if (JSON_HEDLEY_UNLIKELY(!res)) + { + return res; + } + + // get number string + std::vector number_vector; + for (std::size_t i = 0; i < size; ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(input_format_t::ubjson, "number"))) + { + return false; + } + number_vector.push_back(static_cast(current)); + } + + // parse number string + using ia_type = decltype(detail::input_adapter(number_vector)); + auto number_lexer = detail::lexer(detail::input_adapter(number_vector), false); + const auto result_number = number_lexer.scan(); + const auto number_string = number_lexer.get_token_string(); + const auto result_remainder = number_lexer.scan(); + + using token_type = typename detail::lexer_base::token_type; + + if (JSON_HEDLEY_UNLIKELY(result_remainder != token_type::end_of_input)) + { + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + + switch (result_number) + { + case token_type::value_integer: + return sax->number_integer(number_lexer.get_number_integer()); + case token_type::value_unsigned: + return sax->number_unsigned(number_lexer.get_number_unsigned()); + case token_type::value_float: + return sax->number_float(number_lexer.get_number_float(), std::move(number_string)); + case token_type::uninitialized: + case token_type::literal_true: + case token_type::literal_false: + case token_type::literal_null: + case token_type::value_string: + case token_type::begin_array: + case token_type::begin_object: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::parse_error: + case token_type::end_of_input: + case token_type::literal_or_value: + default: + return sax->parse_error(chars_read, number_string, parse_error::create(115, chars_read, exception_message(input_format_t::ubjson, "invalid number text: " + number_lexer.get_token_string(), "high-precision number"), BasicJsonType())); + } + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /*! + @brief get next character from the input + + This function provides the interface to the used input adapter. It does + not throw in case the input reached EOF, but returns a -'ve valued + `std::char_traits::eof()` in that case. + + @return character read from the input + */ + char_int_type get() + { + ++chars_read; + return current = ia.get_character(); + } + + /*! + @return character read from the input after ignoring all 'N' entries + */ + char_int_type get_ignore_noop() + { + do + { + get(); + } + while (current == 'N'); + + return current; + } + + /* + @brief read a number from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[out] result number of type @a NumberType + + @return whether conversion completed + + @note This function needs to respect the system's endianess, because + bytes in CBOR, MessagePack, and UBJSON are stored in network order + (big endian) and therefore need reordering on little endian systems. + */ + template + bool get_number(const input_format_t format, NumberType& result) + { + // step 1: read input into array with system's byte order + std::array vec{}; + for (std::size_t i = 0; i < sizeof(NumberType); ++i) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "number"))) + { + return false; + } + + // reverse byte order prior to conversion if necessary + if (is_little_endian != InputIsLittleEndian) + { + vec[sizeof(NumberType) - i - 1] = static_cast(current); + } + else + { + vec[i] = static_cast(current); // LCOV_EXCL_LINE + } + } + + // step 2: convert array into number of type T and return + std::memcpy(&result, vec.data(), sizeof(NumberType)); + return true; + } + + /*! + @brief create a string by reading characters from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of characters to read + @param[out] result string created by reading @a len bytes + + @return whether string creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of string memory. + */ + template + bool get_string(const input_format_t format, + const NumberType len, + string_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "string"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @brief create a byte array by reading bytes from the input + + @tparam NumberType the type of the number + @param[in] format the current format (for diagnostics) + @param[in] len number of bytes to read + @param[out] result byte array created by reading @a len bytes + + @return whether byte array creation completed + + @note We can not reserve @a len bytes for the result, because @a len + may be too large. Usually, @ref unexpect_eof() detects the end of + the input before we run out of memory. + */ + template + bool get_binary(const input_format_t format, + const NumberType len, + binary_t& result) + { + bool success = true; + for (NumberType i = 0; i < len; i++) + { + get(); + if (JSON_HEDLEY_UNLIKELY(!unexpect_eof(format, "binary"))) + { + success = false; + break; + } + result.push_back(static_cast(current)); + } + return success; + } + + /*! + @param[in] format the current format (for diagnostics) + @param[in] context further context information (for diagnostics) + @return whether the last read character is not EOF + */ + JSON_HEDLEY_NON_NULL(3) + bool unexpect_eof(const input_format_t format, const char* context) const + { + if (JSON_HEDLEY_UNLIKELY(current == std::char_traits::eof())) + { + return sax->parse_error(chars_read, "", + parse_error::create(110, chars_read, exception_message(format, "unexpected end of input", context), BasicJsonType())); + } + return true; + } + + /*! + @return a string representation of the last read byte + */ + std::string get_token_string() const + { + std::array cr{{}}; + (std::snprintf)(cr.data(), cr.size(), "%.2hhX", static_cast(current)); // NOLINT(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + return std::string{cr.data()}; + } + + /*! + @param[in] format the current format + @param[in] detail a detailed error message + @param[in] context further context information + @return a message string to use in the parse_error exceptions + */ + std::string exception_message(const input_format_t format, + const std::string& detail, + const std::string& context) const + { + std::string error_msg = "syntax error while parsing "; + + switch (format) + { + case input_format_t::cbor: + error_msg += "CBOR"; + break; + + case input_format_t::msgpack: + error_msg += "MessagePack"; + break; + + case input_format_t::ubjson: + error_msg += "UBJSON"; + break; + + case input_format_t::bson: + error_msg += "BSON"; + break; + + case input_format_t::json: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + + return error_msg + " " + context + ": " + detail; + } + + private: + /// input adapter + InputAdapterType ia; + + /// the current character + char_int_type current = std::char_traits::eof(); + + /// the number of characters read + std::size_t chars_read = 0; + + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the SAX parser + json_sax_t* sax = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // isfinite +#include // uint8_t +#include // function +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +//////////// +// parser // +//////////// + +enum class parse_event_t : std::uint8_t +{ + /// the parser read `{` and started to process a JSON object + object_start, + /// the parser read `}` and finished processing a JSON object + object_end, + /// the parser read `[` and started to process a JSON array + array_start, + /// the parser read `]` and finished processing a JSON array + array_end, + /// the parser read a key of a value in an object + key, + /// the parser finished reading a JSON value + value +}; + +template +using parser_callback_t = + std::function; + +/*! +@brief syntax analysis + +This class implements a recursive descent parser. +*/ +template +class parser +{ + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using number_float_t = typename BasicJsonType::number_float_t; + using string_t = typename BasicJsonType::string_t; + using lexer_t = lexer; + using token_type = typename lexer_t::token_type; + + public: + /// a parser reading from an input adapter + explicit parser(InputAdapterType&& adapter, + const parser_callback_t cb = nullptr, + const bool allow_exceptions_ = true, + const bool skip_comments = false) + : callback(cb) + , m_lexer(std::move(adapter), skip_comments) + , allow_exceptions(allow_exceptions_) + { + // read first token + get_token(); + } + + /*! + @brief public parser interface + + @param[in] strict whether to expect the last token to be EOF + @param[in,out] result parsed JSON value + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + void parse(const bool strict, BasicJsonType& result) + { + if (callback) + { + json_sax_dom_callback_parser sdp(result, callback, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), + exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + + // set top-level value to null if it was discarded by the callback + // function + if (result.is_discarded()) + { + result = nullptr; + } + } + else + { + json_sax_dom_parser sdp(result, allow_exceptions); + sax_parse_internal(&sdp); + + // in strict mode, input must be completely read + if (strict && (get_token() != token_type::end_of_input)) + { + sdp.parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + // in case of an error, return discarded value + if (sdp.is_errored()) + { + result = value_t::discarded; + return; + } + } + + result.assert_invariant(); + } + + /*! + @brief public accept interface + + @param[in] strict whether to expect the last token to be EOF + @return whether the input is a proper JSON text + */ + bool accept(const bool strict = true) + { + json_sax_acceptor sax_acceptor; + return sax_parse(&sax_acceptor, strict); + } + + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse(SAX* sax, const bool strict = true) + { + (void)detail::is_sax_static_asserts {}; + const bool result = sax_parse_internal(sax); + + // strict mode: next byte must be EOF + if (result && strict && (get_token() != token_type::end_of_input)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_of_input, "value"), BasicJsonType())); + } + + return result; + } + + private: + template + JSON_HEDLEY_NON_NULL(2) + bool sax_parse_internal(SAX* sax) + { + // stack to remember the hierarchy of structured values we are parsing + // true = array; false = object + std::vector states; + // value to avoid a goto (see comment where set to true) + bool skip_to_state_evaluation = false; + + while (true) + { + if (!skip_to_state_evaluation) + { + // invariant: get_token() was called before each iteration + switch (last_token) + { + case token_type::begin_object: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_object(std::size_t(-1)))) + { + return false; + } + + // closing } -> we are done + if (get_token() == token_type::end_object) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + break; + } + + // parse key + if (JSON_HEDLEY_UNLIKELY(last_token != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // remember we are now inside an object + states.push_back(false); + + // parse values + get_token(); + continue; + } + + case token_type::begin_array: + { + if (JSON_HEDLEY_UNLIKELY(!sax->start_array(std::size_t(-1)))) + { + return false; + } + + // closing ] -> we are done + if (get_token() == token_type::end_array) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + break; + } + + // remember we are now inside an array + states.push_back(true); + + // parse values (no need to call get_token) + continue; + } + + case token_type::value_float: + { + const auto res = m_lexer.get_number_float(); + + if (JSON_HEDLEY_UNLIKELY(!std::isfinite(res))) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + out_of_range::create(406, "number overflow parsing '" + m_lexer.get_token_string() + "'", BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->number_float(res, m_lexer.get_string()))) + { + return false; + } + + break; + } + + case token_type::literal_false: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(false))) + { + return false; + } + break; + } + + case token_type::literal_null: + { + if (JSON_HEDLEY_UNLIKELY(!sax->null())) + { + return false; + } + break; + } + + case token_type::literal_true: + { + if (JSON_HEDLEY_UNLIKELY(!sax->boolean(true))) + { + return false; + } + break; + } + + case token_type::value_integer: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_integer(m_lexer.get_number_integer()))) + { + return false; + } + break; + } + + case token_type::value_string: + { + if (JSON_HEDLEY_UNLIKELY(!sax->string(m_lexer.get_string()))) + { + return false; + } + break; + } + + case token_type::value_unsigned: + { + if (JSON_HEDLEY_UNLIKELY(!sax->number_unsigned(m_lexer.get_number_unsigned()))) + { + return false; + } + break; + } + + case token_type::parse_error: + { + // using "uninitialized" to avoid "expected" message + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::uninitialized, "value"), BasicJsonType())); + } + + case token_type::uninitialized: + case token_type::end_array: + case token_type::end_object: + case token_type::name_separator: + case token_type::value_separator: + case token_type::end_of_input: + case token_type::literal_or_value: + default: // the last token was unexpected + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::literal_or_value, "value"), BasicJsonType())); + } + } + } + else + { + skip_to_state_evaluation = false; + } + + // we reached this line after we successfully parsed a value + if (states.empty()) + { + // empty stack: we reached the end of the hierarchy: done + return true; + } + + if (states.back()) // array + { + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse a new value + get_token(); + continue; + } + + // closing ] + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_array)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_array())) + { + return false; + } + + // We are done with this array. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_array, "array"), BasicJsonType())); + } + + // states.back() is false -> object + + // comma -> next value + if (get_token() == token_type::value_separator) + { + // parse key + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::value_string)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::value_string, "object key"), BasicJsonType())); + } + + if (JSON_HEDLEY_UNLIKELY(!sax->key(m_lexer.get_string()))) + { + return false; + } + + // parse separator (:) + if (JSON_HEDLEY_UNLIKELY(get_token() != token_type::name_separator)) + { + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::name_separator, "object separator"), BasicJsonType())); + } + + // parse values + get_token(); + continue; + } + + // closing } + if (JSON_HEDLEY_LIKELY(last_token == token_type::end_object)) + { + if (JSON_HEDLEY_UNLIKELY(!sax->end_object())) + { + return false; + } + + // We are done with this object. Before we can parse a + // new value, we need to evaluate the new state first. + // By setting skip_to_state_evaluation to false, we + // are effectively jumping to the beginning of this if. + JSON_ASSERT(!states.empty()); + states.pop_back(); + skip_to_state_evaluation = true; + continue; + } + + return sax->parse_error(m_lexer.get_position(), + m_lexer.get_token_string(), + parse_error::create(101, m_lexer.get_position(), exception_message(token_type::end_object, "object"), BasicJsonType())); + } + } + + /// get next token from lexer + token_type get_token() + { + return last_token = m_lexer.scan(); + } + + std::string exception_message(const token_type expected, const std::string& context) + { + std::string error_msg = "syntax error "; + + if (!context.empty()) + { + error_msg += "while parsing " + context + " "; + } + + error_msg += "- "; + + if (last_token == token_type::parse_error) + { + error_msg += std::string(m_lexer.get_error_message()) + "; last read: '" + + m_lexer.get_token_string() + "'"; + } + else + { + error_msg += "unexpected " + std::string(lexer_t::token_type_name(last_token)); + } + + if (expected != token_type::uninitialized) + { + error_msg += "; expected " + std::string(lexer_t::token_type_name(expected)); + } + + return error_msg; + } + + private: + /// callback function + const parser_callback_t callback = nullptr; + /// the type of the last read token + token_type last_token = token_type::uninitialized; + /// the lexer + lexer_t m_lexer; + /// whether to throw exceptions in case of errors + const bool allow_exceptions = true; +}; + +} // namespace detail +} // namespace nlohmann + +// #include + + +// #include + + +#include // ptrdiff_t +#include // numeric_limits + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/* +@brief an iterator for primitive JSON types + +This class models an iterator for primitive JSON types (boolean, number, +string). It's only purpose is to allow the iterator/const_iterator classes +to "iterate" over primitive values. Internally, the iterator is modeled by +a `difference_type` variable. Value begin_value (`0`) models the begin, +end_value (`1`) models past the end. +*/ +class primitive_iterator_t +{ + private: + using difference_type = std::ptrdiff_t; + static constexpr difference_type begin_value = 0; + static constexpr difference_type end_value = begin_value + 1; + + JSON_PRIVATE_UNLESS_TESTED: + /// iterator as signed integer type + difference_type m_it = (std::numeric_limits::min)(); + + public: + constexpr difference_type get_value() const noexcept + { + return m_it; + } + + /// set iterator to a defined beginning + void set_begin() noexcept + { + m_it = begin_value; + } + + /// set iterator to a defined past the end + void set_end() noexcept + { + m_it = end_value; + } + + /// return whether the iterator can be dereferenced + constexpr bool is_begin() const noexcept + { + return m_it == begin_value; + } + + /// return whether the iterator is at end + constexpr bool is_end() const noexcept + { + return m_it == end_value; + } + + friend constexpr bool operator==(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it == rhs.m_it; + } + + friend constexpr bool operator<(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it < rhs.m_it; + } + + primitive_iterator_t operator+(difference_type n) noexcept + { + auto result = *this; + result += n; + return result; + } + + friend constexpr difference_type operator-(primitive_iterator_t lhs, primitive_iterator_t rhs) noexcept + { + return lhs.m_it - rhs.m_it; + } + + primitive_iterator_t& operator++() noexcept + { + ++m_it; + return *this; + } + + primitive_iterator_t const operator++(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + ++m_it; + return result; + } + + primitive_iterator_t& operator--() noexcept + { + --m_it; + return *this; + } + + primitive_iterator_t const operator--(int) noexcept // NOLINT(readability-const-return-type) + { + auto result = *this; + --m_it; + return result; + } + + primitive_iterator_t& operator+=(difference_type n) noexcept + { + m_it += n; + return *this; + } + + primitive_iterator_t& operator-=(difference_type n) noexcept + { + m_it -= n; + return *this; + } +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/*! +@brief an iterator value + +@note This structure could easily be a union, but MSVC currently does not allow +unions members with complex constructors, see https://github.com/nlohmann/json/pull/105. +*/ +template struct internal_iterator +{ + /// iterator for JSON objects + typename BasicJsonType::object_t::iterator object_iterator {}; + /// iterator for JSON arrays + typename BasicJsonType::array_t::iterator array_iterator {}; + /// generic iterator for all other types + primitive_iterator_t primitive_iterator {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + + +#include // iterator, random_access_iterator_tag, bidirectional_iterator_tag, advance, next +#include // conditional, is_const, remove_const + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +// forward declare, to be able to friend it later on +template class iteration_proxy; +template class iteration_proxy_value; + +/*! +@brief a template for a bidirectional iterator for the @ref basic_json class +This class implements a both iterators (iterator and const_iterator) for the +@ref basic_json class. +@note An iterator is called *initialized* when a pointer to a JSON value has + been set (e.g., by a constructor or a copy assignment). If the iterator is + default-constructed, it is *uninitialized* and most methods are undefined. + **The library uses assertions to detect calls on uninitialized iterators.** +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +@since version 1.0.0, simplified in version 2.0.9, change to bidirectional + iterators in version 3.0.0 (see https://github.com/nlohmann/json/issues/593) +*/ +template +class iter_impl +{ + /// the iterator with BasicJsonType of different const-ness + using other_iter_impl = iter_impl::value, typename std::remove_const::type, const BasicJsonType>::type>; + /// allow basic_json to access private members + friend other_iter_impl; + friend BasicJsonType; + friend iteration_proxy; + friend iteration_proxy_value; + + using object_t = typename BasicJsonType::object_t; + using array_t = typename BasicJsonType::array_t; + // make sure BasicJsonType is basic_json or const basic_json + static_assert(is_basic_json::type>::value, + "iter_impl only accepts (const) basic_json"); + + public: + + /// The std::iterator class template (used as a base class to provide typedefs) is deprecated in C++17. + /// The C++ Standard has never required user-defined iterators to derive from std::iterator. + /// A user-defined iterator should provide publicly accessible typedefs named + /// iterator_category, value_type, difference_type, pointer, and reference. + /// Note that value_type is required to be non-const, even for constant iterators. + using iterator_category = std::bidirectional_iterator_tag; + + /// the type of the values when the iterator is dereferenced + using value_type = typename BasicJsonType::value_type; + /// a type to represent differences between iterators + using difference_type = typename BasicJsonType::difference_type; + /// defines a pointer to the type iterated over (value_type) + using pointer = typename std::conditional::value, + typename BasicJsonType::const_pointer, + typename BasicJsonType::pointer>::type; + /// defines a reference to the type iterated over (value_type) + using reference = + typename std::conditional::value, + typename BasicJsonType::const_reference, + typename BasicJsonType::reference>::type; + + iter_impl() = default; + ~iter_impl() = default; + iter_impl(iter_impl&&) noexcept = default; + iter_impl& operator=(iter_impl&&) noexcept = default; + + /*! + @brief constructor for a given JSON instance + @param[in] object pointer to a JSON object for this iterator + @pre object != nullptr + @post The iterator is initialized; i.e. `m_object != nullptr`. + */ + explicit iter_impl(pointer object) noexcept : m_object(object) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = typename object_t::iterator(); + break; + } + + case value_t::array: + { + m_it.array_iterator = typename array_t::iterator(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator = primitive_iterator_t(); + break; + } + } + } + + /*! + @note The conventional copy constructor and copy assignment are implicitly + defined. Combined with the following converting constructor and + assignment, they support: (1) copy from iterator to iterator, (2) + copy from const iterator to const iterator, and (3) conversion from + iterator to const iterator. However conversion from const iterator + to iterator is not defined. + */ + + /*! + @brief const copy constructor + @param[in] other const iterator to copy from + @note This copy constructor had to be defined explicitly to circumvent a bug + occurring on msvc v19.0 compiler (VS 2015) debug build. For more + information refer to: https://github.com/nlohmann/json/issues/1608 + */ + iter_impl(const iter_impl& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl& other) noexcept + { + if (&other != this) + { + m_object = other.m_object; + m_it = other.m_it; + } + return *this; + } + + /*! + @brief converting constructor + @param[in] other non-const iterator to copy from + @note It is not checked whether @a other is initialized. + */ + iter_impl(const iter_impl::type>& other) noexcept + : m_object(other.m_object), m_it(other.m_it) + {} + + /*! + @brief converting assignment + @param[in] other non-const iterator to copy from + @return const/non-const iterator + @note It is not checked whether @a other is initialized. + */ + iter_impl& operator=(const iter_impl::type>& other) noexcept // NOLINT(cert-oop54-cpp) + { + m_object = other.m_object; + m_it = other.m_it; + return *this; + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief set the iterator to the first value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_begin() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->begin(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->begin(); + break; + } + + case value_t::null: + { + // set to end so begin()==end() is true: null is empty + m_it.primitive_iterator.set_end(); + break; + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_begin(); + break; + } + } + } + + /*! + @brief set the iterator past the last value + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + void set_end() noexcept + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + m_it.object_iterator = m_object->m_value.object->end(); + break; + } + + case value_t::array: + { + m_it.array_iterator = m_object->m_value.array->end(); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator.set_end(); + break; + } + } + } + + public: + /*! + @brief return a reference to the value pointed to by the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator*() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return m_it.object_iterator->second; + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return *m_it.array_iterator; + } + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief dereference the iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + pointer operator->() const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + JSON_ASSERT(m_it.object_iterator != m_object->m_value.object->end()); + return &(m_it.object_iterator->second); + } + + case value_t::array: + { + JSON_ASSERT(m_it.array_iterator != m_object->m_value.array->end()); + return &*m_it.array_iterator; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.is_begin())) + { + return m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief post-increment (it++) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator++(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + ++(*this); + return result; + } + + /*! + @brief pre-increment (++it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator++() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, 1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, 1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + ++m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief post-decrement (it--) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl const operator--(int) // NOLINT(readability-const-return-type) + { + auto result = *this; + --(*this); + return result; + } + + /*! + @brief pre-decrement (--it) + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator--() + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + { + std::advance(m_it.object_iterator, -1); + break; + } + + case value_t::array: + { + std::advance(m_it.array_iterator, -1); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + --m_it.primitive_iterator; + break; + } + } + + return *this; + } + + /*! + @brief comparison: equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator==(const IterImpl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + return (m_it.object_iterator == other.m_it.object_iterator); + + case value_t::array: + return (m_it.array_iterator == other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator == other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: not equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + template < typename IterImpl, detail::enable_if_t < (std::is_same::value || std::is_same::value), std::nullptr_t > = nullptr > + bool operator!=(const IterImpl& other) const + { + return !operator==(other); + } + + /*! + @brief comparison: smaller + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<(const iter_impl& other) const + { + // if objects are not the same, the comparison is undefined + if (JSON_HEDLEY_UNLIKELY(m_object != other.m_object)) + { + JSON_THROW(invalid_iterator::create(212, "cannot compare iterators of different containers", *m_object)); + } + + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(213, "cannot compare order of object iterators", *m_object)); + + case value_t::array: + return (m_it.array_iterator < other.m_it.array_iterator); + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return (m_it.primitive_iterator < other.m_it.primitive_iterator); + } + } + + /*! + @brief comparison: less than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator<=(const iter_impl& other) const + { + return !other.operator < (*this); + } + + /*! + @brief comparison: greater than + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>(const iter_impl& other) const + { + return !operator<=(other); + } + + /*! + @brief comparison: greater than or equal + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + bool operator>=(const iter_impl& other) const + { + return !operator<(other); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator+=(difference_type i) + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + { + std::advance(m_it.array_iterator, i); + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + m_it.primitive_iterator += i; + break; + } + } + + return *this; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl& operator-=(difference_type i) + { + return operator+=(-i); + } + + /*! + @brief add to iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator+(difference_type i) const + { + auto result = *this; + result += i; + return result; + } + + /*! + @brief addition of distance and iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + friend iter_impl operator+(difference_type i, const iter_impl& it) + { + auto result = it; + result += i; + return result; + } + + /*! + @brief subtract from iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + iter_impl operator-(difference_type i) const + { + auto result = *this; + result -= i; + return result; + } + + /*! + @brief return difference + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + difference_type operator-(const iter_impl& other) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(209, "cannot use offsets with object iterators", *m_object)); + + case value_t::array: + return m_it.array_iterator - other.m_it.array_iterator; + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + return m_it.primitive_iterator - other.m_it.primitive_iterator; + } + } + + /*! + @brief access to successor + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference operator[](difference_type n) const + { + JSON_ASSERT(m_object != nullptr); + + switch (m_object->m_type) + { + case value_t::object: + JSON_THROW(invalid_iterator::create(208, "cannot use operator[] for object iterators", *m_object)); + + case value_t::array: + return *std::next(m_it.array_iterator, n); + + case value_t::null: + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + if (JSON_HEDLEY_LIKELY(m_it.primitive_iterator.get_value() == -n)) + { + return *m_object; + } + + JSON_THROW(invalid_iterator::create(214, "cannot get value", *m_object)); + } + } + } + + /*! + @brief return the key of an object iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + const typename object_t::key_type& key() const + { + JSON_ASSERT(m_object != nullptr); + + if (JSON_HEDLEY_LIKELY(m_object->is_object())) + { + return m_it.object_iterator->first; + } + + JSON_THROW(invalid_iterator::create(207, "cannot use key() for non-object iterators", *m_object)); + } + + /*! + @brief return the value of an iterator + @pre The iterator is initialized; i.e. `m_object != nullptr`. + */ + reference value() const + { + return operator*(); + } + + JSON_PRIVATE_UNLESS_TESTED: + /// associated JSON instance + pointer m_object = nullptr; + /// the actual iterator of the associated instance + internal_iterator::type> m_it {}; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // ptrdiff_t +#include // reverse_iterator +#include // declval + +namespace nlohmann +{ +namespace detail +{ +////////////////////// +// reverse_iterator // +////////////////////// + +/*! +@brief a template for a reverse iterator class + +@tparam Base the base iterator type to reverse. Valid types are @ref +iterator (to create @ref reverse_iterator) and @ref const_iterator (to +create @ref const_reverse_iterator). + +@requirement The class satisfies the following concept requirements: +- +[BidirectionalIterator](https://en.cppreference.com/w/cpp/named_req/BidirectionalIterator): + The iterator that can be moved can be moved in both directions (i.e. + incremented and decremented). +- [OutputIterator](https://en.cppreference.com/w/cpp/named_req/OutputIterator): + It is possible to write to the pointed-to element (only if @a Base is + @ref iterator). + +@since version 1.0.0 +*/ +template +class json_reverse_iterator : public std::reverse_iterator +{ + public: + using difference_type = std::ptrdiff_t; + /// shortcut to the reverse iterator adapter + using base_iterator = std::reverse_iterator; + /// the reference type for the pointed-to element + using reference = typename Base::reference; + + /// create reverse iterator from iterator + explicit json_reverse_iterator(const typename base_iterator::iterator_type& it) noexcept + : base_iterator(it) {} + + /// create reverse iterator from base class + explicit json_reverse_iterator(const base_iterator& it) noexcept : base_iterator(it) {} + + /// post-increment (it++) + json_reverse_iterator const operator++(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator++(1)); + } + + /// pre-increment (++it) + json_reverse_iterator& operator++() + { + return static_cast(base_iterator::operator++()); + } + + /// post-decrement (it--) + json_reverse_iterator const operator--(int) // NOLINT(readability-const-return-type) + { + return static_cast(base_iterator::operator--(1)); + } + + /// pre-decrement (--it) + json_reverse_iterator& operator--() + { + return static_cast(base_iterator::operator--()); + } + + /// add to iterator + json_reverse_iterator& operator+=(difference_type i) + { + return static_cast(base_iterator::operator+=(i)); + } + + /// add to iterator + json_reverse_iterator operator+(difference_type i) const + { + return static_cast(base_iterator::operator+(i)); + } + + /// subtract from iterator + json_reverse_iterator operator-(difference_type i) const + { + return static_cast(base_iterator::operator-(i)); + } + + /// return difference + difference_type operator-(const json_reverse_iterator& other) const + { + return base_iterator(*this) - base_iterator(other); + } + + /// access to successor + reference operator[](difference_type n) const + { + return *(this->operator+(n)); + } + + /// return the key of an object iterator + auto key() const -> decltype(std::declval().key()) + { + auto it = --this->base(); + return it.key(); + } + + /// return the value of an iterator + reference value() const + { + auto it = --this->base(); + return it.operator * (); + } +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // all_of +#include // isdigit +#include // max +#include // accumulate +#include // string +#include // move +#include // vector + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +template +class json_pointer +{ + // allow basic_json to access private members + NLOHMANN_BASIC_JSON_TPL_DECLARATION + friend class basic_json; + + public: + /*! + @brief create JSON pointer + + Create a JSON pointer according to the syntax described in + [Section 3 of RFC6901](https://tools.ietf.org/html/rfc6901#section-3). + + @param[in] s string representing the JSON pointer; if omitted, the empty + string is assumed which references the whole JSON value + + @throw parse_error.107 if the given JSON pointer @a s is nonempty and does + not begin with a slash (`/`); see example below + + @throw parse_error.108 if a tilde (`~`) in the given JSON pointer @a s is + not followed by `0` (representing `~`) or `1` (representing `/`); see + example below + + @liveexample{The example shows the construction several valid JSON pointers + as well as the exceptional behavior.,json_pointer} + + @since version 2.0.0 + */ + explicit json_pointer(const std::string& s = "") + : reference_tokens(split(s)) + {} + + /*! + @brief return a string representation of the JSON pointer + + @invariant For each JSON pointer `ptr`, it holds: + @code {.cpp} + ptr == json_pointer(ptr.to_string()); + @endcode + + @return a string representation of the JSON pointer + + @liveexample{The example shows the result of `to_string`.,json_pointer__to_string} + + @since version 2.0.0 + */ + std::string to_string() const + { + return std::accumulate(reference_tokens.begin(), reference_tokens.end(), + std::string{}, + [](const std::string & a, const std::string & b) + { + return a + "/" + detail::escape(b); + }); + } + + /// @copydoc to_string() + operator std::string() const + { + return to_string(); + } + + /*! + @brief append another JSON pointer at the end of this JSON pointer + + @param[in] ptr JSON pointer to append + @return JSON pointer with @a ptr appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Linear in the length of @a ptr. + + @sa see @ref operator/=(std::string) to append a reference token + @sa see @ref operator/=(std::size_t) to append an array index + @sa see @ref operator/(const json_pointer&, const json_pointer&) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(const json_pointer& ptr) + { + reference_tokens.insert(reference_tokens.end(), + ptr.reference_tokens.begin(), + ptr.reference_tokens.end()); + return *this; + } + + /*! + @brief append an unescaped reference token at the end of this JSON pointer + + @param[in] token reference token to append + @return JSON pointer with @a token appended without escaping @a token + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa see @ref operator/=(const json_pointer&) to append a JSON pointer + @sa see @ref operator/=(std::size_t) to append an array index + @sa see @ref operator/(const json_pointer&, std::size_t) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::string token) + { + push_back(std::move(token)); + return *this; + } + + /*! + @brief append an array index at the end of this JSON pointer + + @param[in] array_idx array index to append + @return JSON pointer with @a array_idx appended + + @liveexample{The example shows the usage of `operator/=`.,json_pointer__operator_add} + + @complexity Amortized constant. + + @sa see @ref operator/=(const json_pointer&) to append a JSON pointer + @sa see @ref operator/=(std::string) to append a reference token + @sa see @ref operator/(const json_pointer&, std::string) for a binary operator + + @since version 3.6.0 + */ + json_pointer& operator/=(std::size_t array_idx) + { + return *this /= std::to_string(array_idx); + } + + /*! + @brief create a new JSON pointer by appending the right JSON pointer at the end of the left JSON pointer + + @param[in] lhs JSON pointer + @param[in] rhs JSON pointer + @return a new JSON pointer with @a rhs appended to @a lhs + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a lhs and @a rhs. + + @sa see @ref operator/=(const json_pointer&) to append a JSON pointer + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& lhs, + const json_pointer& rhs) + { + return json_pointer(lhs) /= rhs; + } + + /*! + @brief create a new JSON pointer by appending the unescaped token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] token reference token + @return a new JSON pointer with unescaped @a token appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa see @ref operator/=(std::string) to append a reference token + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::string token) // NOLINT(performance-unnecessary-value-param) + { + return json_pointer(ptr) /= std::move(token); + } + + /*! + @brief create a new JSON pointer by appending the array-index-token at the end of the JSON pointer + + @param[in] ptr JSON pointer + @param[in] array_idx array index + @return a new JSON pointer with @a array_idx appended to @a ptr + + @liveexample{The example shows the usage of `operator/`.,json_pointer__operator_add_binary} + + @complexity Linear in the length of @a ptr. + + @sa see @ref operator/=(std::size_t) to append an array index + + @since version 3.6.0 + */ + friend json_pointer operator/(const json_pointer& ptr, std::size_t array_idx) + { + return json_pointer(ptr) /= array_idx; + } + + /*! + @brief returns the parent of this JSON pointer + + @return parent of this JSON pointer; in case this JSON pointer is the root, + the root itself is returned + + @complexity Linear in the length of the JSON pointer. + + @liveexample{The example shows the result of `parent_pointer` for different + JSON Pointers.,json_pointer__parent_pointer} + + @since version 3.6.0 + */ + json_pointer parent_pointer() const + { + if (empty()) + { + return *this; + } + + json_pointer res = *this; + res.pop_back(); + return res; + } + + /*! + @brief remove last reference token + + @pre not `empty()` + + @liveexample{The example shows the usage of `pop_back`.,json_pointer__pop_back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + void pop_back() + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + reference_tokens.pop_back(); + } + + /*! + @brief return last reference token + + @pre not `empty()` + @return last reference token + + @liveexample{The example shows the usage of `back`.,json_pointer__back} + + @complexity Constant. + + @throw out_of_range.405 if JSON pointer has no parent + + @since version 3.6.0 + */ + const std::string& back() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + return reference_tokens.back(); + } + + /*! + @brief append an unescaped token at the end of the reference pointer + + @param[in] token token to add + + @complexity Amortized constant. + + @liveexample{The example shows the result of `push_back` for different + JSON Pointers.,json_pointer__push_back} + + @since version 3.6.0 + */ + void push_back(const std::string& token) + { + reference_tokens.push_back(token); + } + + /// @copydoc push_back(const std::string&) + void push_back(std::string&& token) + { + reference_tokens.push_back(std::move(token)); + } + + /*! + @brief return whether pointer points to the root document + + @return true iff the JSON pointer points to the root document + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example shows the result of `empty` for different JSON + Pointers.,json_pointer__empty} + + @since version 3.6.0 + */ + bool empty() const noexcept + { + return reference_tokens.empty(); + } + + private: + /*! + @param[in] s reference token to be converted into an array index + + @return integer representation of @a s + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index begins not with a digit + @throw out_of_range.404 if string @a s could not be converted to an integer + @throw out_of_range.410 if an array index exceeds size_type + */ + static typename BasicJsonType::size_type array_index(const std::string& s) + { + using size_type = typename BasicJsonType::size_type; + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && s[0] == '0')) + { + JSON_THROW(detail::parse_error::create(106, 0, "array index '" + s + "' must not begin with '0'", BasicJsonType())); + } + + // error condition (cf. RFC 6901, Sect. 4) + if (JSON_HEDLEY_UNLIKELY(s.size() > 1 && !(s[0] >= '1' && s[0] <= '9'))) + { + JSON_THROW(detail::parse_error::create(109, 0, "array index '" + s + "' is not a number", BasicJsonType())); + } + + std::size_t processed_chars = 0; + unsigned long long res = 0; // NOLINT(runtime/int) + JSON_TRY + { + res = std::stoull(s, &processed_chars); + } + JSON_CATCH(std::out_of_range&) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // check if the string was completely read + if (JSON_HEDLEY_UNLIKELY(processed_chars != s.size())) + { + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + s + "'", BasicJsonType())); + } + + // only triggered on special platforms (like 32bit), see also + // https://github.com/nlohmann/json/pull/2203 + if (res >= static_cast((std::numeric_limits::max)())) // NOLINT(runtime/int) + { + JSON_THROW(detail::out_of_range::create(410, "array index " + s + " exceeds size_type", BasicJsonType())); // LCOV_EXCL_LINE + } + + return static_cast(res); + } + + JSON_PRIVATE_UNLESS_TESTED: + json_pointer top() const + { + if (JSON_HEDLEY_UNLIKELY(empty())) + { + JSON_THROW(detail::out_of_range::create(405, "JSON pointer has no parent", BasicJsonType())); + } + + json_pointer result = *this; + result.reference_tokens = {reference_tokens[0]}; + return result; + } + + private: + /*! + @brief create and return a reference to the pointed to value + + @complexity Linear in the number of reference tokens. + + @throw parse_error.109 if array index is not a number + @throw type_error.313 if value cannot be unflattened + */ + BasicJsonType& get_and_create(BasicJsonType& j) const + { + auto* result = &j; + + // in case no reference tokens exist, return a reference to the JSON value + // j which will be overwritten by a primitive value + for (const auto& reference_token : reference_tokens) + { + switch (result->type()) + { + case detail::value_t::null: + { + if (reference_token == "0") + { + // start a new array if reference token is 0 + result = &result->operator[](0); + } + else + { + // start a new object otherwise + result = &result->operator[](reference_token); + } + break; + } + + case detail::value_t::object: + { + // create an entry in the object + result = &result->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + // create an entry in the array + result = &result->operator[](array_index(reference_token)); + break; + } + + /* + The following code is only reached if there exists a reference + token _and_ the current value is primitive. In this case, we have + an error situation, because primitive values may only occur as + single value; that is, with an empty list of reference tokens. + */ + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::type_error::create(313, "invalid value to unflatten", j)); + } + } + + return *result; + } + + /*! + @brief return a reference to the pointed to value + + @note This version does not throw if a value is not present, but tries to + create nested values instead. For instance, calling this function + with pointer `"/this/that"` on a null value is equivalent to calling + `operator[]("this").operator[]("that")` on that value, effectively + changing the null value to an object. + + @param[in] ptr a JSON value + + @return reference to the JSON value pointed to by the JSON pointer + + @complexity Linear in the length of the JSON pointer. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_unchecked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + // convert null values to arrays or objects before continuing + if (ptr->is_null()) + { + // check if reference token is a number + const bool nums = + std::all_of(reference_token.begin(), reference_token.end(), + [](const unsigned char x) + { + return std::isdigit(x); + }); + + // change value to array for numbers or "-" or to object otherwise + *ptr = (nums || reference_token == "-") + ? detail::value_t::array + : detail::value_t::object; + } + + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (reference_token == "-") + { + // explicitly treat "-" as index beyond the end + ptr = &ptr->operator[](ptr->m_value.array->size()); + } + else + { + // convert array index to number; unchecked access + ptr = &ptr->operator[](array_index(reference_token)); + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + BasicJsonType& get_checked(BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @brief return a const reference to the pointed to value + + @param[in] ptr a JSON value + + @return const reference to the JSON value pointed to by the JSON + pointer + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_unchecked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // use unchecked object access + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" cannot be used for const access + JSON_THROW(detail::out_of_range::create(402, "array index '-' (" + std::to_string(ptr->m_value.array->size()) + ") is out of range", *ptr)); + } + + // use unchecked array access + ptr = &ptr->operator[](array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + */ + const BasicJsonType& get_checked(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + // note: at performs range check + ptr = &ptr->at(reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + JSON_THROW(detail::out_of_range::create(402, + "array index '-' (" + std::to_string(ptr->m_value.array->size()) + + ") is out of range", *ptr)); + } + + // note: at performs range check + ptr = &ptr->at(array_index(reference_token)); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + JSON_THROW(detail::out_of_range::create(404, "unresolved reference token '" + reference_token + "'", *ptr)); + } + } + + return *ptr; + } + + /*! + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + */ + bool contains(const BasicJsonType* ptr) const + { + for (const auto& reference_token : reference_tokens) + { + switch (ptr->type()) + { + case detail::value_t::object: + { + if (!ptr->contains(reference_token)) + { + // we did not find the key in the object + return false; + } + + ptr = &ptr->operator[](reference_token); + break; + } + + case detail::value_t::array: + { + if (JSON_HEDLEY_UNLIKELY(reference_token == "-")) + { + // "-" always fails the range check + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() == 1 && !("0" <= reference_token && reference_token <= "9"))) + { + // invalid char + return false; + } + if (JSON_HEDLEY_UNLIKELY(reference_token.size() > 1)) + { + if (JSON_HEDLEY_UNLIKELY(!('1' <= reference_token[0] && reference_token[0] <= '9'))) + { + // first char should be between '1' and '9' + return false; + } + for (std::size_t i = 1; i < reference_token.size(); i++) + { + if (JSON_HEDLEY_UNLIKELY(!('0' <= reference_token[i] && reference_token[i] <= '9'))) + { + // other char should be between '0' and '9' + return false; + } + } + } + + const auto idx = array_index(reference_token); + if (idx >= ptr->size()) + { + // index out of range + return false; + } + + ptr = &ptr->operator[](idx); + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // we do not expect primitive values if there is still a + // reference token to process + return false; + } + } + } + + // no reference token left means we found a primitive value + return true; + } + + /*! + @brief split the string input to reference tokens + + @note This function is only called by the json_pointer constructor. + All exceptions below are documented there. + + @throw parse_error.107 if the pointer is not empty or begins with '/' + @throw parse_error.108 if character '~' is not followed by '0' or '1' + */ + static std::vector split(const std::string& reference_string) + { + std::vector result; + + // special case: empty reference string -> no reference tokens + if (reference_string.empty()) + { + return result; + } + + // check if nonempty reference string begins with slash + if (JSON_HEDLEY_UNLIKELY(reference_string[0] != '/')) + { + JSON_THROW(detail::parse_error::create(107, 1, "JSON pointer must be empty or begin with '/' - was: '" + reference_string + "'", BasicJsonType())); + } + + // extract the reference tokens: + // - slash: position of the last read slash (or end of string) + // - start: position after the previous slash + for ( + // search for the first slash after the first character + std::size_t slash = reference_string.find_first_of('/', 1), + // set the beginning of the first reference token + start = 1; + // we can stop if start == 0 (if slash == std::string::npos) + start != 0; + // set the beginning of the next reference token + // (will eventually be 0 if slash == std::string::npos) + start = (slash == std::string::npos) ? 0 : slash + 1, + // find next slash + slash = reference_string.find_first_of('/', start)) + { + // use the text between the beginning of the reference token + // (start) and the last slash (slash). + auto reference_token = reference_string.substr(start, slash - start); + + // check reference tokens are properly escaped + for (std::size_t pos = reference_token.find_first_of('~'); + pos != std::string::npos; + pos = reference_token.find_first_of('~', pos + 1)) + { + JSON_ASSERT(reference_token[pos] == '~'); + + // ~ must be followed by 0 or 1 + if (JSON_HEDLEY_UNLIKELY(pos == reference_token.size() - 1 || + (reference_token[pos + 1] != '0' && + reference_token[pos + 1] != '1'))) + { + JSON_THROW(detail::parse_error::create(108, 0, "escape character '~' must be followed with '0' or '1'", BasicJsonType())); + } + } + + // finally, store the reference token + detail::unescape(reference_token); + result.push_back(reference_token); + } + + return result; + } + + private: + /*! + @param[in] reference_string the reference string to the current value + @param[in] value the value to consider + @param[in,out] result the result object to insert values to + + @note Empty objects or arrays are flattened to `null`. + */ + static void flatten(const std::string& reference_string, + const BasicJsonType& value, + BasicJsonType& result) + { + switch (value.type()) + { + case detail::value_t::array: + { + if (value.m_value.array->empty()) + { + // flatten empty array as null + result[reference_string] = nullptr; + } + else + { + // iterate array and use index as reference string + for (std::size_t i = 0; i < value.m_value.array->size(); ++i) + { + flatten(reference_string + "/" + std::to_string(i), + value.m_value.array->operator[](i), result); + } + } + break; + } + + case detail::value_t::object: + { + if (value.m_value.object->empty()) + { + // flatten empty object as null + result[reference_string] = nullptr; + } + else + { + // iterate object and use keys as reference string + for (const auto& element : *value.m_value.object) + { + flatten(reference_string + "/" + detail::escape(element.first), element.second, result); + } + } + break; + } + + case detail::value_t::null: + case detail::value_t::string: + case detail::value_t::boolean: + case detail::value_t::number_integer: + case detail::value_t::number_unsigned: + case detail::value_t::number_float: + case detail::value_t::binary: + case detail::value_t::discarded: + default: + { + // add primitive value with its reference string + result[reference_string] = value; + break; + } + } + } + + /*! + @param[in] value flattened JSON + + @return unflattened JSON + + @throw parse_error.109 if array index is not a number + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + @throw type_error.313 if value cannot be unflattened + */ + static BasicJsonType + unflatten(const BasicJsonType& value) + { + if (JSON_HEDLEY_UNLIKELY(!value.is_object())) + { + JSON_THROW(detail::type_error::create(314, "only objects can be unflattened", value)); + } + + BasicJsonType result; + + // iterate the JSON object values + for (const auto& element : *value.m_value.object) + { + if (JSON_HEDLEY_UNLIKELY(!element.second.is_primitive())) + { + JSON_THROW(detail::type_error::create(315, "values in object must be primitive", element.second)); + } + + // assign value to reference pointed to by JSON pointer; Note that if + // the JSON pointer is "" (i.e., points to the whole value), function + // get_and_create returns a reference to result itself. An assignment + // will then create a primitive value. + json_pointer(element.first).get_and_create(result) = element.second; + } + + return result; + } + + /*! + @brief compares two JSON pointers for equality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is equal to @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator==(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return lhs.reference_tokens == rhs.reference_tokens; + } + + /*! + @brief compares two JSON pointers for inequality + + @param[in] lhs JSON pointer to compare + @param[in] rhs JSON pointer to compare + @return whether @a lhs is not equal @a rhs + + @complexity Linear in the length of the JSON pointer + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + */ + friend bool operator!=(json_pointer const& lhs, + json_pointer const& rhs) noexcept + { + return !(lhs == rhs); + } + + /// the reference tokens + std::vector reference_tokens; +}; +} // namespace nlohmann + +// #include + + +#include +#include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +template +class json_ref +{ + public: + using value_type = BasicJsonType; + + json_ref(value_type&& value) + : owned_value(std::move(value)) + {} + + json_ref(const value_type& value) + : value_ref(&value) + {} + + json_ref(std::initializer_list init) + : owned_value(init) + {} + + template < + class... Args, + enable_if_t::value, int> = 0 > + json_ref(Args && ... args) + : owned_value(std::forward(args)...) + {} + + // class should be movable only + json_ref(json_ref&&) noexcept = default; + json_ref(const json_ref&) = delete; + json_ref& operator=(const json_ref&) = delete; + json_ref& operator=(json_ref&&) = delete; + ~json_ref() = default; + + value_type moved_or_copied() const + { + if (value_ref == nullptr) + { + return std::move(owned_value); + } + return *value_ref; + } + + value_type const& operator*() const + { + return value_ref ? *value_ref : owned_value; + } + + value_type const* operator->() const + { + return &** this; + } + + private: + mutable value_type owned_value = nullptr; + value_type const* value_ref = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + + +#include // reverse +#include // array +#include // isnan, isinf +#include // uint8_t, uint16_t, uint32_t, uint64_t +#include // memcpy +#include // numeric_limits +#include // string +#include // move + +// #include + +// #include + +// #include + + +#include // copy +#include // size_t +#include // back_inserter +#include // shared_ptr, make_shared +#include // basic_string +#include // vector + +#ifndef JSON_NO_IO + #include // streamsize + #include // basic_ostream +#endif // JSON_NO_IO + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/// abstract output adapter interface +template struct output_adapter_protocol +{ + virtual void write_character(CharType c) = 0; + virtual void write_characters(const CharType* s, std::size_t length) = 0; + virtual ~output_adapter_protocol() = default; + + output_adapter_protocol() = default; + output_adapter_protocol(const output_adapter_protocol&) = default; + output_adapter_protocol(output_adapter_protocol&&) noexcept = default; + output_adapter_protocol& operator=(const output_adapter_protocol&) = default; + output_adapter_protocol& operator=(output_adapter_protocol&&) noexcept = default; +}; + +/// a type to simplify interfaces +template +using output_adapter_t = std::shared_ptr>; + +/// output adapter for byte vectors +template +class output_vector_adapter : public output_adapter_protocol +{ + public: + explicit output_vector_adapter(std::vector& vec) noexcept + : v(vec) + {} + + void write_character(CharType c) override + { + v.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + std::copy(s, s + length, std::back_inserter(v)); + } + + private: + std::vector& v; +}; + +#ifndef JSON_NO_IO +/// output adapter for output streams +template +class output_stream_adapter : public output_adapter_protocol +{ + public: + explicit output_stream_adapter(std::basic_ostream& s) noexcept + : stream(s) + {} + + void write_character(CharType c) override + { + stream.put(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + stream.write(s, static_cast(length)); + } + + private: + std::basic_ostream& stream; +}; +#endif // JSON_NO_IO + +/// output adapter for basic_string +template> +class output_string_adapter : public output_adapter_protocol +{ + public: + explicit output_string_adapter(StringType& s) noexcept + : str(s) + {} + + void write_character(CharType c) override + { + str.push_back(c); + } + + JSON_HEDLEY_NON_NULL(2) + void write_characters(const CharType* s, std::size_t length) override + { + str.append(s, length); + } + + private: + StringType& str; +}; + +template> +class output_adapter +{ + public: + output_adapter(std::vector& vec) + : oa(std::make_shared>(vec)) {} + +#ifndef JSON_NO_IO + output_adapter(std::basic_ostream& s) + : oa(std::make_shared>(s)) {} +#endif // JSON_NO_IO + + output_adapter(StringType& s) + : oa(std::make_shared>(s)) {} + + operator output_adapter_t() + { + return oa; + } + + private: + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// binary writer // +/////////////////// + +/*! +@brief serialization to CBOR and MessagePack values +*/ +template +class binary_writer +{ + using string_t = typename BasicJsonType::string_t; + using binary_t = typename BasicJsonType::binary_t; + using number_float_t = typename BasicJsonType::number_float_t; + + public: + /*! + @brief create a binary writer + + @param[in] adapter output adapter to write to + */ + explicit binary_writer(output_adapter_t adapter) : oa(std::move(adapter)) + { + JSON_ASSERT(oa); + } + + /*! + @param[in] j JSON value to serialize + @pre j.type() == value_t::object + */ + void write_bson(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + { + write_bson_object(*j.m_value.object); + break; + } + + case value_t::null: + case value_t::array: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + JSON_THROW(type_error::create(317, "to serialize to BSON, top-level type must be object, but is " + std::string(j.type_name()), j)); + } + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_cbor(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: + { + oa->write_character(to_char_type(0xF6)); + break; + } + + case value_t::boolean: + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xF5) + : to_char_type(0xF4)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // CBOR does not differentiate between positive signed + // integers and unsigned integers. Therefore, we used the + // code from the value_t::number_unsigned case here. + if (j.m_value.number_integer <= 0x17) + { + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_integer)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + // The conversions below encode the sign in the first + // byte, and the value is converted to a positive number. + const auto positive_number = -1 - j.m_value.number_integer; + if (j.m_value.number_integer >= -24) + { + write_number(static_cast(0x20 + positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x38)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x39)); + write_number(static_cast(positive_number)); + } + else if (positive_number <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x3A)); + write_number(static_cast(positive_number)); + } + else + { + oa->write_character(to_char_type(0x3B)); + write_number(static_cast(positive_number)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= 0x17) + { + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x18)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x19)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x1A)); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + oa->write_character(to_char_type(0x1B)); + write_number(static_cast(j.m_value.number_unsigned)); + } + break; + } + + case value_t::number_float: + { + if (std::isnan(j.m_value.number_float)) + { + // NaN is 0xf97e00 in CBOR + oa->write_character(to_char_type(0xF9)); + oa->write_character(to_char_type(0x7E)); + oa->write_character(to_char_type(0x00)); + } + else if (std::isinf(j.m_value.number_float)) + { + // Infinity is 0xf97c00, -Infinity is 0xf9fc00 + oa->write_character(to_char_type(0xf9)); + oa->write_character(j.m_value.number_float > 0 ? to_char_type(0x7C) : to_char_type(0xFC)); + oa->write_character(to_char_type(0x00)); + } + else + { + write_compact_float(j.m_value.number_float, detail::input_format_t::cbor); + } + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 0x17) + { + write_number(static_cast(0x60 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x78)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x79)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x7B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 0x17) + { + write_number(static_cast(0x80 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x98)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x99)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x9B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_cbor(el); + } + break; + } + + case value_t::binary: + { + if (j.m_value.binary->has_subtype()) + { + if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd8)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xd9)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xda)); + write_number(static_cast(j.m_value.binary->subtype())); + } + else if (j.m_value.binary->subtype() <= (std::numeric_limits::max)()) + { + write_number(static_cast(0xdb)); + write_number(static_cast(j.m_value.binary->subtype())); + } + } + + // step 1: write control byte and the binary array size + const auto N = j.m_value.binary->size(); + if (N <= 0x17) + { + write_number(static_cast(0x40 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x58)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x59)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5A)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0x5B)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 0x17) + { + write_number(static_cast(0xA0 + N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB8)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xB9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBA)); + write_number(static_cast(N)); + } + // LCOV_EXCL_START + else if (N <= (std::numeric_limits::max)()) + { + oa->write_character(to_char_type(0xBB)); + write_number(static_cast(N)); + } + // LCOV_EXCL_STOP + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_cbor(el.first); + write_cbor(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + */ + void write_msgpack(const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::null: // nil + { + oa->write_character(to_char_type(0xC0)); + break; + } + + case value_t::boolean: // true and false + { + oa->write_character(j.m_value.boolean + ? to_char_type(0xC3) + : to_char_type(0xC2)); + break; + } + + case value_t::number_integer: + { + if (j.m_value.number_integer >= 0) + { + // MessagePack does not differentiate between positive + // signed integers and unsigned integers. Therefore, we used + // the code from the value_t::number_unsigned case here. + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + } + else + { + if (j.m_value.number_integer >= -32) + { + // negative fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 8 + oa->write_character(to_char_type(0xD0)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 16 + oa->write_character(to_char_type(0xD1)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 32 + oa->write_character(to_char_type(0xD2)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_integer >= (std::numeric_limits::min)() && + j.m_value.number_integer <= (std::numeric_limits::max)()) + { + // int 64 + oa->write_character(to_char_type(0xD3)); + write_number(static_cast(j.m_value.number_integer)); + } + } + break; + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned < 128) + { + // positive fixnum + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 8 + oa->write_character(to_char_type(0xCC)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 16 + oa->write_character(to_char_type(0xCD)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 32 + oa->write_character(to_char_type(0xCE)); + write_number(static_cast(j.m_value.number_integer)); + } + else if (j.m_value.number_unsigned <= (std::numeric_limits::max)()) + { + // uint 64 + oa->write_character(to_char_type(0xCF)); + write_number(static_cast(j.m_value.number_integer)); + } + break; + } + + case value_t::number_float: + { + write_compact_float(j.m_value.number_float, detail::input_format_t::msgpack); + break; + } + + case value_t::string: + { + // step 1: write control byte and the string length + const auto N = j.m_value.string->size(); + if (N <= 31) + { + // fixstr + write_number(static_cast(0xA0 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 8 + oa->write_character(to_char_type(0xD9)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 16 + oa->write_character(to_char_type(0xDA)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // str 32 + oa->write_character(to_char_type(0xDB)); + write_number(static_cast(N)); + } + + // step 2: write the string + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + // step 1: write control byte and the array size + const auto N = j.m_value.array->size(); + if (N <= 15) + { + // fixarray + write_number(static_cast(0x90 | N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 16 + oa->write_character(to_char_type(0xDC)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // array 32 + oa->write_character(to_char_type(0xDD)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.array) + { + write_msgpack(el); + } + break; + } + + case value_t::binary: + { + // step 0: determine if the binary type has a set subtype to + // determine whether or not to use the ext or fixext types + const bool use_ext = j.m_value.binary->has_subtype(); + + // step 1: write control byte and the byte string length + const auto N = j.m_value.binary->size(); + if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type{}; + bool fixed = true; + if (use_ext) + { + switch (N) + { + case 1: + output_type = 0xD4; // fixext 1 + break; + case 2: + output_type = 0xD5; // fixext 2 + break; + case 4: + output_type = 0xD6; // fixext 4 + break; + case 8: + output_type = 0xD7; // fixext 8 + break; + case 16: + output_type = 0xD8; // fixext 16 + break; + default: + output_type = 0xC7; // ext 8 + fixed = false; + break; + } + + } + else + { + output_type = 0xC4; // bin 8 + fixed = false; + } + + oa->write_character(to_char_type(output_type)); + if (!fixed) + { + write_number(static_cast(N)); + } + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC8 // ext 16 + : 0xC5; // bin 16 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + std::uint8_t output_type = use_ext + ? 0xC9 // ext 32 + : 0xC6; // bin 32 + + oa->write_character(to_char_type(output_type)); + write_number(static_cast(N)); + } + + // step 1.5: if this is an ext type, write the subtype + if (use_ext) + { + write_number(static_cast(j.m_value.binary->subtype())); + } + + // step 2: write the byte string + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + N); + + break; + } + + case value_t::object: + { + // step 1: write control byte and the object size + const auto N = j.m_value.object->size(); + if (N <= 15) + { + // fixmap + write_number(static_cast(0x80 | (N & 0xF))); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 16 + oa->write_character(to_char_type(0xDE)); + write_number(static_cast(N)); + } + else if (N <= (std::numeric_limits::max)()) + { + // map 32 + oa->write_character(to_char_type(0xDF)); + write_number(static_cast(N)); + } + + // step 2: write each element + for (const auto& el : *j.m_value.object) + { + write_msgpack(el.first); + write_msgpack(el.second); + } + break; + } + + case value_t::discarded: + default: + break; + } + } + + /*! + @param[in] j JSON value to serialize + @param[in] use_count whether to use '#' prefixes (optimized format) + @param[in] use_type whether to use '$' prefixes (optimized format) + @param[in] add_prefix whether prefixes need to be used for this value + */ + void write_ubjson(const BasicJsonType& j, const bool use_count, + const bool use_type, const bool add_prefix = true) + { + switch (j.type()) + { + case value_t::null: + { + if (add_prefix) + { + oa->write_character(to_char_type('Z')); + } + break; + } + + case value_t::boolean: + { + if (add_prefix) + { + oa->write_character(j.m_value.boolean + ? to_char_type('T') + : to_char_type('F')); + } + break; + } + + case value_t::number_integer: + { + write_number_with_ubjson_prefix(j.m_value.number_integer, add_prefix); + break; + } + + case value_t::number_unsigned: + { + write_number_with_ubjson_prefix(j.m_value.number_unsigned, add_prefix); + break; + } + + case value_t::number_float: + { + write_number_with_ubjson_prefix(j.m_value.number_float, add_prefix); + break; + } + + case value_t::string: + { + if (add_prefix) + { + oa->write_character(to_char_type('S')); + } + write_number_with_ubjson_prefix(j.m_value.string->size(), true); + oa->write_characters( + reinterpret_cast(j.m_value.string->c_str()), + j.m_value.string->size()); + break; + } + + case value_t::array: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.array->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin() + 1, j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.array->size(), true); + } + + for (const auto& el : *j.m_value.array) + { + write_ubjson(el, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::binary: + { + if (add_prefix) + { + oa->write_character(to_char_type('[')); + } + + if (use_type && !j.m_value.binary->empty()) + { + JSON_ASSERT(use_count); + oa->write_character(to_char_type('$')); + oa->write_character('U'); + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.binary->size(), true); + } + + if (use_type) + { + oa->write_characters( + reinterpret_cast(j.m_value.binary->data()), + j.m_value.binary->size()); + } + else + { + for (size_t i = 0; i < j.m_value.binary->size(); ++i) + { + oa->write_character(to_char_type('U')); + oa->write_character(j.m_value.binary->data()[i]); + } + } + + if (!use_count) + { + oa->write_character(to_char_type(']')); + } + + break; + } + + case value_t::object: + { + if (add_prefix) + { + oa->write_character(to_char_type('{')); + } + + bool prefix_required = true; + if (use_type && !j.m_value.object->empty()) + { + JSON_ASSERT(use_count); + const CharType first_prefix = ubjson_prefix(j.front()); + const bool same_prefix = std::all_of(j.begin(), j.end(), + [this, first_prefix](const BasicJsonType & v) + { + return ubjson_prefix(v) == first_prefix; + }); + + if (same_prefix) + { + prefix_required = false; + oa->write_character(to_char_type('$')); + oa->write_character(first_prefix); + } + } + + if (use_count) + { + oa->write_character(to_char_type('#')); + write_number_with_ubjson_prefix(j.m_value.object->size(), true); + } + + for (const auto& el : *j.m_value.object) + { + write_number_with_ubjson_prefix(el.first.size(), true); + oa->write_characters( + reinterpret_cast(el.first.c_str()), + el.first.size()); + write_ubjson(el.second, use_count, use_type, prefix_required); + } + + if (!use_count) + { + oa->write_character(to_char_type('}')); + } + + break; + } + + case value_t::discarded: + default: + break; + } + } + + private: + ////////// + // BSON // + ////////// + + /*! + @return The size of a BSON document entry header, including the id marker + and the entry name size (and its null-terminator). + */ + static std::size_t calc_bson_entry_header_size(const string_t& name, const BasicJsonType& j) + { + const auto it = name.find(static_cast(0)); + if (JSON_HEDLEY_UNLIKELY(it != BasicJsonType::string_t::npos)) + { + JSON_THROW(out_of_range::create(409, "BSON key cannot contain code point U+0000 (at byte " + std::to_string(it) + ")", j)); + static_cast(j); + } + + return /*id*/ 1ul + name.size() + /*zero-terminator*/1u; + } + + /*! + @brief Writes the given @a element_type and @a name to the output adapter + */ + void write_bson_entry_header(const string_t& name, + const std::uint8_t element_type) + { + oa->write_character(to_char_type(element_type)); // boolean + oa->write_characters( + reinterpret_cast(name.c_str()), + name.size() + 1u); + } + + /*! + @brief Writes a BSON element with key @a name and boolean value @a value + */ + void write_bson_boolean(const string_t& name, + const bool value) + { + write_bson_entry_header(name, 0x08); + oa->write_character(value ? to_char_type(0x01) : to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and double value @a value + */ + void write_bson_double(const string_t& name, + const double value) + { + write_bson_entry_header(name, 0x01); + write_number(value); + } + + /*! + @return The size of the BSON-encoded string in @a value + */ + static std::size_t calc_bson_string_size(const string_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and string value @a value + */ + void write_bson_string(const string_t& name, + const string_t& value) + { + write_bson_entry_header(name, 0x02); + + write_number(static_cast(value.size() + 1ul)); + oa->write_characters( + reinterpret_cast(value.c_str()), + value.size() + 1); + } + + /*! + @brief Writes a BSON element with key @a name and null value + */ + void write_bson_null(const string_t& name) + { + write_bson_entry_header(name, 0x0A); + } + + /*! + @return The size of the BSON-encoded integer @a value + */ + static std::size_t calc_bson_integer_size(const std::int64_t value) + { + return (std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)() + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and integer @a value + */ + void write_bson_integer(const string_t& name, + const std::int64_t value) + { + if ((std::numeric_limits::min)() <= value && value <= (std::numeric_limits::max)()) + { + write_bson_entry_header(name, 0x10); // int32 + write_number(static_cast(value)); + } + else + { + write_bson_entry_header(name, 0x12); // int64 + write_number(static_cast(value)); + } + } + + /*! + @return The size of the BSON-encoded unsigned integer in @a j + */ + static constexpr std::size_t calc_bson_unsigned_size(const std::uint64_t value) noexcept + { + return (value <= static_cast((std::numeric_limits::max)())) + ? sizeof(std::int32_t) + : sizeof(std::int64_t); + } + + /*! + @brief Writes a BSON element with key @a name and unsigned @a value + */ + void write_bson_unsigned(const string_t& name, + const BasicJsonType& j) + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x10 /* int32 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + write_bson_entry_header(name, 0x12 /* int64 */); + write_number(static_cast(j.m_value.number_unsigned)); + } + else + { + JSON_THROW(out_of_range::create(407, "integer number " + std::to_string(j.m_value.number_unsigned) + " cannot be represented by BSON as it does not fit int64", j)); + } + } + + /*! + @brief Writes a BSON element with key @a name and object @a value + */ + void write_bson_object_entry(const string_t& name, + const typename BasicJsonType::object_t& value) + { + write_bson_entry_header(name, 0x03); // object + write_bson_object(value); + } + + /*! + @return The size of the BSON-encoded array @a value + */ + static std::size_t calc_bson_array_size(const typename BasicJsonType::array_t& value) + { + std::size_t array_index = 0ul; + + const std::size_t embedded_document_size = std::accumulate(std::begin(value), std::end(value), std::size_t(0), [&array_index](std::size_t result, const typename BasicJsonType::array_t::value_type & el) + { + return result + calc_bson_element_size(std::to_string(array_index++), el); + }); + + return sizeof(std::int32_t) + embedded_document_size + 1ul; + } + + /*! + @return The size of the BSON-encoded binary array @a value + */ + static std::size_t calc_bson_binary_size(const typename BasicJsonType::binary_t& value) + { + return sizeof(std::int32_t) + value.size() + 1ul; + } + + /*! + @brief Writes a BSON element with key @a name and array @a value + */ + void write_bson_array(const string_t& name, + const typename BasicJsonType::array_t& value) + { + write_bson_entry_header(name, 0x04); // array + write_number(static_cast(calc_bson_array_size(value))); + + std::size_t array_index = 0ul; + + for (const auto& el : value) + { + write_bson_element(std::to_string(array_index++), el); + } + + oa->write_character(to_char_type(0x00)); + } + + /*! + @brief Writes a BSON element with key @a name and binary value @a value + */ + void write_bson_binary(const string_t& name, + const binary_t& value) + { + write_bson_entry_header(name, 0x05); + + write_number(static_cast(value.size())); + write_number(value.has_subtype() ? static_cast(value.subtype()) : std::uint8_t(0x00)); + + oa->write_characters(reinterpret_cast(value.data()), value.size()); + } + + /*! + @brief Calculates the size necessary to serialize the JSON value @a j with its @a name + @return The calculated size for the BSON document entry for @a j with the given @a name. + */ + static std::size_t calc_bson_element_size(const string_t& name, + const BasicJsonType& j) + { + const auto header_size = calc_bson_entry_header_size(name, j); + switch (j.type()) + { + case value_t::object: + return header_size + calc_bson_object_size(*j.m_value.object); + + case value_t::array: + return header_size + calc_bson_array_size(*j.m_value.array); + + case value_t::binary: + return header_size + calc_bson_binary_size(*j.m_value.binary); + + case value_t::boolean: + return header_size + 1ul; + + case value_t::number_float: + return header_size + 8ul; + + case value_t::number_integer: + return header_size + calc_bson_integer_size(j.m_value.number_integer); + + case value_t::number_unsigned: + return header_size + calc_bson_unsigned_size(j.m_value.number_unsigned); + + case value_t::string: + return header_size + calc_bson_string_size(*j.m_value.string); + + case value_t::null: + return header_size + 0ul; + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return 0ul; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Serializes the JSON value @a j to BSON and associates it with the + key @a name. + @param name The name to associate with the JSON entity @a j within the + current BSON document + */ + void write_bson_element(const string_t& name, + const BasicJsonType& j) + { + switch (j.type()) + { + case value_t::object: + return write_bson_object_entry(name, *j.m_value.object); + + case value_t::array: + return write_bson_array(name, *j.m_value.array); + + case value_t::binary: + return write_bson_binary(name, *j.m_value.binary); + + case value_t::boolean: + return write_bson_boolean(name, j.m_value.boolean); + + case value_t::number_float: + return write_bson_double(name, j.m_value.number_float); + + case value_t::number_integer: + return write_bson_integer(name, j.m_value.number_integer); + + case value_t::number_unsigned: + return write_bson_unsigned(name, j); + + case value_t::string: + return write_bson_string(name, *j.m_value.string); + + case value_t::null: + return write_bson_null(name); + + // LCOV_EXCL_START + case value_t::discarded: + default: + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) + return; + // LCOV_EXCL_STOP + } + } + + /*! + @brief Calculates the size of the BSON serialization of the given + JSON-object @a j. + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + static std::size_t calc_bson_object_size(const typename BasicJsonType::object_t& value) + { + std::size_t document_size = std::accumulate(value.begin(), value.end(), std::size_t(0), + [](size_t result, const typename BasicJsonType::object_t::value_type & el) + { + return result += calc_bson_element_size(el.first, el.second); + }); + + return sizeof(std::int32_t) + document_size + 1ul; + } + + /*! + @param[in] value JSON value to serialize + @pre value.type() == value_t::object + */ + void write_bson_object(const typename BasicJsonType::object_t& value) + { + write_number(static_cast(calc_bson_object_size(value))); + + for (const auto& el : value) + { + write_bson_element(el.first, el.second); + } + + oa->write_character(to_char_type(0x00)); + } + + ////////// + // CBOR // + ////////// + + static constexpr CharType get_cbor_float_prefix(float /*unused*/) + { + return to_char_type(0xFA); // Single-Precision Float + } + + static constexpr CharType get_cbor_float_prefix(double /*unused*/) + { + return to_char_type(0xFB); // Double-Precision Float + } + + ///////////// + // MsgPack // + ///////////// + + static constexpr CharType get_msgpack_float_prefix(float /*unused*/) + { + return to_char_type(0xCA); // float 32 + } + + static constexpr CharType get_msgpack_float_prefix(double /*unused*/) + { + return to_char_type(0xCB); // float 64 + } + + //////////// + // UBJSON // + //////////// + + // UBJSON: write number (floating point) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (add_prefix) + { + oa->write_character(get_ubjson_float_prefix(n)); + } + write_number(n); + } + + // UBJSON: write number (unsigned integer) + template::value, int>::type = 0> + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if (n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + } + + // UBJSON: write number (signed integer) + template < typename NumberType, typename std::enable_if < + std::is_signed::value&& + !std::is_floating_point::value, int >::type = 0 > + void write_number_with_ubjson_prefix(const NumberType n, + const bool add_prefix) + { + if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('i')); // int8 + } + write_number(static_cast(n)); + } + else if (static_cast((std::numeric_limits::min)()) <= n && n <= static_cast((std::numeric_limits::max)())) + { + if (add_prefix) + { + oa->write_character(to_char_type('U')); // uint8 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('I')); // int16 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('l')); // int32 + } + write_number(static_cast(n)); + } + else if ((std::numeric_limits::min)() <= n && n <= (std::numeric_limits::max)()) + { + if (add_prefix) + { + oa->write_character(to_char_type('L')); // int64 + } + write_number(static_cast(n)); + } + // LCOV_EXCL_START + else + { + if (add_prefix) + { + oa->write_character(to_char_type('H')); // high-precision number + } + + const auto number = BasicJsonType(n).dump(); + write_number_with_ubjson_prefix(number.size(), true); + for (std::size_t i = 0; i < number.size(); ++i) + { + oa->write_character(to_char_type(static_cast(number[i]))); + } + } + // LCOV_EXCL_STOP + } + + /*! + @brief determine the type prefix of container values + */ + CharType ubjson_prefix(const BasicJsonType& j) const noexcept + { + switch (j.type()) + { + case value_t::null: + return 'Z'; + + case value_t::boolean: + return j.m_value.boolean ? 'T' : 'F'; + + case value_t::number_integer: + { + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'i'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'U'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'I'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'l'; + } + if ((std::numeric_limits::min)() <= j.m_value.number_integer && j.m_value.number_integer <= (std::numeric_limits::max)()) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_unsigned: + { + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'i'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'U'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'I'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'l'; + } + if (j.m_value.number_unsigned <= static_cast((std::numeric_limits::max)())) + { + return 'L'; + } + // anything else is treated as high-precision number + return 'H'; // LCOV_EXCL_LINE + } + + case value_t::number_float: + return get_ubjson_float_prefix(j.m_value.number_float); + + case value_t::string: + return 'S'; + + case value_t::array: // fallthrough + case value_t::binary: + return '['; + + case value_t::object: + return '{'; + + case value_t::discarded: + default: // discarded values + return 'N'; + } + } + + static constexpr CharType get_ubjson_float_prefix(float /*unused*/) + { + return 'd'; // float 32 + } + + static constexpr CharType get_ubjson_float_prefix(double /*unused*/) + { + return 'D'; // float 64 + } + + /////////////////////// + // Utility functions // + /////////////////////// + + /* + @brief write a number to output input + @param[in] n number of type @a NumberType + @tparam NumberType the type of the number + @tparam OutputIsLittleEndian Set to true if output data is + required to be little endian + + @note This function needs to respect the system's endianess, because bytes + in CBOR, MessagePack, and UBJSON are stored in network order (big + endian) and therefore need reordering on little endian systems. + */ + template + void write_number(const NumberType n) + { + // step 1: write number to array of length NumberType + std::array vec{}; + std::memcpy(vec.data(), &n, sizeof(NumberType)); + + // step 2: write array to output (with possible reordering) + if (is_little_endian != OutputIsLittleEndian) + { + // reverse byte order prior to conversion if necessary + std::reverse(vec.begin(), vec.end()); + } + + oa->write_characters(vec.data(), sizeof(NumberType)); + } + + void write_compact_float(const number_float_t n, detail::input_format_t format) + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (static_cast(n) >= static_cast(std::numeric_limits::lowest()) && + static_cast(n) <= static_cast((std::numeric_limits::max)()) && + static_cast(static_cast(n)) == static_cast(n)) + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(static_cast(n)) + : get_msgpack_float_prefix(static_cast(n))); + write_number(static_cast(n)); + } + else + { + oa->write_character(format == detail::input_format_t::cbor + ? get_cbor_float_prefix(n) + : get_msgpack_float_prefix(n)); + write_number(n); + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + public: + // The following to_char_type functions are implement the conversion + // between uint8_t and CharType. In case CharType is not unsigned, + // such a conversion is required to allow values greater than 128. + // See for a discussion. + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_signed::value > * = nullptr > + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return *reinterpret_cast(&x); + } + + template < typename C = CharType, + enable_if_t < std::is_signed::value && std::is_unsigned::value > * = nullptr > + static CharType to_char_type(std::uint8_t x) noexcept + { + static_assert(sizeof(std::uint8_t) == sizeof(CharType), "size of CharType must be equal to std::uint8_t"); + static_assert(std::is_trivial::value, "CharType must be trivial"); + CharType result; + std::memcpy(&result, &x, sizeof(x)); + return result; + } + + template::value>* = nullptr> + static constexpr CharType to_char_type(std::uint8_t x) noexcept + { + return x; + } + + template < typename InputCharType, typename C = CharType, + enable_if_t < + std::is_signed::value && + std::is_signed::value && + std::is_same::type>::value + > * = nullptr > + static constexpr CharType to_char_type(InputCharType x) noexcept + { + return x; + } + + private: + /// whether we can assume little endianess + const bool is_little_endian = little_endianess(); + + /// the output + output_adapter_t oa = nullptr; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + + +#include // reverse, remove, fill, find, none_of +#include // array +#include // localeconv, lconv +#include // labs, isfinite, isnan, signbit +#include // size_t, ptrdiff_t +#include // uint8_t +#include // snprintf +#include // numeric_limits +#include // string, char_traits +#include // is_same +#include // move + +// #include + + +#include // array +#include // signbit, isfinite +#include // intN_t, uintN_t +#include // memcpy, memmove +#include // numeric_limits +#include // conditional + +// #include + + +namespace nlohmann +{ +namespace detail +{ + +/*! +@brief implements the Grisu2 algorithm for binary to decimal floating-point +conversion. + +This implementation is a slightly modified version of the reference +implementation which may be obtained from +http://florian.loitsch.com/publications (bench.tar.gz). + +The code is distributed under the MIT license, Copyright (c) 2009 Florian Loitsch. + +For a detailed description of the algorithm see: + +[1] Loitsch, "Printing Floating-Point Numbers Quickly and Accurately with + Integers", Proceedings of the ACM SIGPLAN 2010 Conference on Programming + Language Design and Implementation, PLDI 2010 +[2] Burger, Dybvig, "Printing Floating-Point Numbers Quickly and Accurately", + Proceedings of the ACM SIGPLAN 1996 Conference on Programming Language + Design and Implementation, PLDI 1996 +*/ +namespace dtoa_impl +{ + +template +Target reinterpret_bits(const Source source) +{ + static_assert(sizeof(Target) == sizeof(Source), "size mismatch"); + + Target target; + std::memcpy(&target, &source, sizeof(Source)); + return target; +} + +struct diyfp // f * 2^e +{ + static constexpr int kPrecision = 64; // = q + + std::uint64_t f = 0; + int e = 0; + + constexpr diyfp(std::uint64_t f_, int e_) noexcept : f(f_), e(e_) {} + + /*! + @brief returns x - y + @pre x.e == y.e and x.f >= y.f + */ + static diyfp sub(const diyfp& x, const diyfp& y) noexcept + { + JSON_ASSERT(x.e == y.e); + JSON_ASSERT(x.f >= y.f); + + return {x.f - y.f, x.e}; + } + + /*! + @brief returns x * y + @note The result is rounded. (Only the upper q bits are returned.) + */ + static diyfp mul(const diyfp& x, const diyfp& y) noexcept + { + static_assert(kPrecision == 64, "internal error"); + + // Computes: + // f = round((x.f * y.f) / 2^q) + // e = x.e + y.e + q + + // Emulate the 64-bit * 64-bit multiplication: + // + // p = u * v + // = (u_lo + 2^32 u_hi) (v_lo + 2^32 v_hi) + // = (u_lo v_lo ) + 2^32 ((u_lo v_hi ) + (u_hi v_lo )) + 2^64 (u_hi v_hi ) + // = (p0 ) + 2^32 ((p1 ) + (p2 )) + 2^64 (p3 ) + // = (p0_lo + 2^32 p0_hi) + 2^32 ((p1_lo + 2^32 p1_hi) + (p2_lo + 2^32 p2_hi)) + 2^64 (p3 ) + // = (p0_lo ) + 2^32 (p0_hi + p1_lo + p2_lo ) + 2^64 (p1_hi + p2_hi + p3) + // = (p0_lo ) + 2^32 (Q ) + 2^64 (H ) + // = (p0_lo ) + 2^32 (Q_lo + 2^32 Q_hi ) + 2^64 (H ) + // + // (Since Q might be larger than 2^32 - 1) + // + // = (p0_lo + 2^32 Q_lo) + 2^64 (Q_hi + H) + // + // (Q_hi + H does not overflow a 64-bit int) + // + // = p_lo + 2^64 p_hi + + const std::uint64_t u_lo = x.f & 0xFFFFFFFFu; + const std::uint64_t u_hi = x.f >> 32u; + const std::uint64_t v_lo = y.f & 0xFFFFFFFFu; + const std::uint64_t v_hi = y.f >> 32u; + + const std::uint64_t p0 = u_lo * v_lo; + const std::uint64_t p1 = u_lo * v_hi; + const std::uint64_t p2 = u_hi * v_lo; + const std::uint64_t p3 = u_hi * v_hi; + + const std::uint64_t p0_hi = p0 >> 32u; + const std::uint64_t p1_lo = p1 & 0xFFFFFFFFu; + const std::uint64_t p1_hi = p1 >> 32u; + const std::uint64_t p2_lo = p2 & 0xFFFFFFFFu; + const std::uint64_t p2_hi = p2 >> 32u; + + std::uint64_t Q = p0_hi + p1_lo + p2_lo; + + // The full product might now be computed as + // + // p_hi = p3 + p2_hi + p1_hi + (Q >> 32) + // p_lo = p0_lo + (Q << 32) + // + // But in this particular case here, the full p_lo is not required. + // Effectively we only need to add the highest bit in p_lo to p_hi (and + // Q_hi + 1 does not overflow). + + Q += std::uint64_t{1} << (64u - 32u - 1u); // round, ties up + + const std::uint64_t h = p3 + p2_hi + p1_hi + (Q >> 32u); + + return {h, x.e + y.e + 64}; + } + + /*! + @brief normalize x such that the significand is >= 2^(q-1) + @pre x.f != 0 + */ + static diyfp normalize(diyfp x) noexcept + { + JSON_ASSERT(x.f != 0); + + while ((x.f >> 63u) == 0) + { + x.f <<= 1u; + x.e--; + } + + return x; + } + + /*! + @brief normalize x such that the result has the exponent E + @pre e >= x.e and the upper e - x.e bits of x.f must be zero. + */ + static diyfp normalize_to(const diyfp& x, const int target_exponent) noexcept + { + const int delta = x.e - target_exponent; + + JSON_ASSERT(delta >= 0); + JSON_ASSERT(((x.f << delta) >> delta) == x.f); + + return {x.f << delta, target_exponent}; + } +}; + +struct boundaries +{ + diyfp w; + diyfp minus; + diyfp plus; +}; + +/*! +Compute the (normalized) diyfp representing the input number 'value' and its +boundaries. + +@pre value must be finite and positive +*/ +template +boundaries compute_boundaries(FloatType value) +{ + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // Convert the IEEE representation into a diyfp. + // + // If v is denormal: + // value = 0.F * 2^(1 - bias) = ( F) * 2^(1 - bias - (p-1)) + // If v is normalized: + // value = 1.F * 2^(E - bias) = (2^(p-1) + F) * 2^(E - bias - (p-1)) + + static_assert(std::numeric_limits::is_iec559, + "internal error: dtoa_short requires an IEEE-754 floating-point implementation"); + + constexpr int kPrecision = std::numeric_limits::digits; // = p (includes the hidden bit) + constexpr int kBias = std::numeric_limits::max_exponent - 1 + (kPrecision - 1); + constexpr int kMinExp = 1 - kBias; + constexpr std::uint64_t kHiddenBit = std::uint64_t{1} << (kPrecision - 1); // = 2^(p-1) + + using bits_type = typename std::conditional::type; + + const auto bits = static_cast(reinterpret_bits(value)); + const std::uint64_t E = bits >> (kPrecision - 1); + const std::uint64_t F = bits & (kHiddenBit - 1); + + const bool is_denormal = E == 0; + const diyfp v = is_denormal + ? diyfp(F, kMinExp) + : diyfp(F + kHiddenBit, static_cast(E) - kBias); + + // Compute the boundaries m- and m+ of the floating-point value + // v = f * 2^e. + // + // Determine v- and v+, the floating-point predecessor and successor if v, + // respectively. + // + // v- = v - 2^e if f != 2^(p-1) or e == e_min (A) + // = v - 2^(e-1) if f == 2^(p-1) and e > e_min (B) + // + // v+ = v + 2^e + // + // Let m- = (v- + v) / 2 and m+ = (v + v+) / 2. All real numbers _strictly_ + // between m- and m+ round to v, regardless of how the input rounding + // algorithm breaks ties. + // + // ---+-------------+-------------+-------------+-------------+--- (A) + // v- m- v m+ v+ + // + // -----------------+------+------+-------------+-------------+--- (B) + // v- m- v m+ v+ + + const bool lower_boundary_is_closer = F == 0 && E > 1; + const diyfp m_plus = diyfp(2 * v.f + 1, v.e - 1); + const diyfp m_minus = lower_boundary_is_closer + ? diyfp(4 * v.f - 1, v.e - 2) // (B) + : diyfp(2 * v.f - 1, v.e - 1); // (A) + + // Determine the normalized w+ = m+. + const diyfp w_plus = diyfp::normalize(m_plus); + + // Determine w- = m- such that e_(w-) = e_(w+). + const diyfp w_minus = diyfp::normalize_to(m_minus, w_plus.e); + + return {diyfp::normalize(v), w_minus, w_plus}; +} + +// Given normalized diyfp w, Grisu needs to find a (normalized) cached +// power-of-ten c, such that the exponent of the product c * w = f * 2^e lies +// within a certain range [alpha, gamma] (Definition 3.2 from [1]) +// +// alpha <= e = e_c + e_w + q <= gamma +// +// or +// +// f_c * f_w * 2^alpha <= f_c 2^(e_c) * f_w 2^(e_w) * 2^q +// <= f_c * f_w * 2^gamma +// +// Since c and w are normalized, i.e. 2^(q-1) <= f < 2^q, this implies +// +// 2^(q-1) * 2^(q-1) * 2^alpha <= c * w * 2^q < 2^q * 2^q * 2^gamma +// +// or +// +// 2^(q - 2 + alpha) <= c * w < 2^(q + gamma) +// +// The choice of (alpha,gamma) determines the size of the table and the form of +// the digit generation procedure. Using (alpha,gamma)=(-60,-32) works out well +// in practice: +// +// The idea is to cut the number c * w = f * 2^e into two parts, which can be +// processed independently: An integral part p1, and a fractional part p2: +// +// f * 2^e = ( (f div 2^-e) * 2^-e + (f mod 2^-e) ) * 2^e +// = (f div 2^-e) + (f mod 2^-e) * 2^e +// = p1 + p2 * 2^e +// +// The conversion of p1 into decimal form requires a series of divisions and +// modulos by (a power of) 10. These operations are faster for 32-bit than for +// 64-bit integers, so p1 should ideally fit into a 32-bit integer. This can be +// achieved by choosing +// +// -e >= 32 or e <= -32 := gamma +// +// In order to convert the fractional part +// +// p2 * 2^e = p2 / 2^-e = d[-1] / 10^1 + d[-2] / 10^2 + ... +// +// into decimal form, the fraction is repeatedly multiplied by 10 and the digits +// d[-i] are extracted in order: +// +// (10 * p2) div 2^-e = d[-1] +// (10 * p2) mod 2^-e = d[-2] / 10^1 + ... +// +// The multiplication by 10 must not overflow. It is sufficient to choose +// +// 10 * p2 < 16 * p2 = 2^4 * p2 <= 2^64. +// +// Since p2 = f mod 2^-e < 2^-e, +// +// -e <= 60 or e >= -60 := alpha + +constexpr int kAlpha = -60; +constexpr int kGamma = -32; + +struct cached_power // c = f * 2^e ~= 10^k +{ + std::uint64_t f; + int e; + int k; +}; + +/*! +For a normalized diyfp w = f * 2^e, this function returns a (normalized) cached +power-of-ten c = f_c * 2^e_c, such that the exponent of the product w * c +satisfies (Definition 3.2 from [1]) + + alpha <= e_c + e + q <= gamma. +*/ +inline cached_power get_cached_power_for_binary_exponent(int e) +{ + // Now + // + // alpha <= e_c + e + q <= gamma (1) + // ==> f_c * 2^alpha <= c * 2^e * 2^q + // + // and since the c's are normalized, 2^(q-1) <= f_c, + // + // ==> 2^(q - 1 + alpha) <= c * 2^(e + q) + // ==> 2^(alpha - e - 1) <= c + // + // If c were an exact power of ten, i.e. c = 10^k, one may determine k as + // + // k = ceil( log_10( 2^(alpha - e - 1) ) ) + // = ceil( (alpha - e - 1) * log_10(2) ) + // + // From the paper: + // "In theory the result of the procedure could be wrong since c is rounded, + // and the computation itself is approximated [...]. In practice, however, + // this simple function is sufficient." + // + // For IEEE double precision floating-point numbers converted into + // normalized diyfp's w = f * 2^e, with q = 64, + // + // e >= -1022 (min IEEE exponent) + // -52 (p - 1) + // -52 (p - 1, possibly normalize denormal IEEE numbers) + // -11 (normalize the diyfp) + // = -1137 + // + // and + // + // e <= +1023 (max IEEE exponent) + // -52 (p - 1) + // -11 (normalize the diyfp) + // = 960 + // + // This binary exponent range [-1137,960] results in a decimal exponent + // range [-307,324]. One does not need to store a cached power for each + // k in this range. For each such k it suffices to find a cached power + // such that the exponent of the product lies in [alpha,gamma]. + // This implies that the difference of the decimal exponents of adjacent + // table entries must be less than or equal to + // + // floor( (gamma - alpha) * log_10(2) ) = 8. + // + // (A smaller distance gamma-alpha would require a larger table.) + + // NB: + // Actually this function returns c, such that -60 <= e_c + e + 64 <= -34. + + constexpr int kCachedPowersMinDecExp = -300; + constexpr int kCachedPowersDecStep = 8; + + static constexpr std::array kCachedPowers = + { + { + { 0xAB70FE17C79AC6CA, -1060, -300 }, + { 0xFF77B1FCBEBCDC4F, -1034, -292 }, + { 0xBE5691EF416BD60C, -1007, -284 }, + { 0x8DD01FAD907FFC3C, -980, -276 }, + { 0xD3515C2831559A83, -954, -268 }, + { 0x9D71AC8FADA6C9B5, -927, -260 }, + { 0xEA9C227723EE8BCB, -901, -252 }, + { 0xAECC49914078536D, -874, -244 }, + { 0x823C12795DB6CE57, -847, -236 }, + { 0xC21094364DFB5637, -821, -228 }, + { 0x9096EA6F3848984F, -794, -220 }, + { 0xD77485CB25823AC7, -768, -212 }, + { 0xA086CFCD97BF97F4, -741, -204 }, + { 0xEF340A98172AACE5, -715, -196 }, + { 0xB23867FB2A35B28E, -688, -188 }, + { 0x84C8D4DFD2C63F3B, -661, -180 }, + { 0xC5DD44271AD3CDBA, -635, -172 }, + { 0x936B9FCEBB25C996, -608, -164 }, + { 0xDBAC6C247D62A584, -582, -156 }, + { 0xA3AB66580D5FDAF6, -555, -148 }, + { 0xF3E2F893DEC3F126, -529, -140 }, + { 0xB5B5ADA8AAFF80B8, -502, -132 }, + { 0x87625F056C7C4A8B, -475, -124 }, + { 0xC9BCFF6034C13053, -449, -116 }, + { 0x964E858C91BA2655, -422, -108 }, + { 0xDFF9772470297EBD, -396, -100 }, + { 0xA6DFBD9FB8E5B88F, -369, -92 }, + { 0xF8A95FCF88747D94, -343, -84 }, + { 0xB94470938FA89BCF, -316, -76 }, + { 0x8A08F0F8BF0F156B, -289, -68 }, + { 0xCDB02555653131B6, -263, -60 }, + { 0x993FE2C6D07B7FAC, -236, -52 }, + { 0xE45C10C42A2B3B06, -210, -44 }, + { 0xAA242499697392D3, -183, -36 }, + { 0xFD87B5F28300CA0E, -157, -28 }, + { 0xBCE5086492111AEB, -130, -20 }, + { 0x8CBCCC096F5088CC, -103, -12 }, + { 0xD1B71758E219652C, -77, -4 }, + { 0x9C40000000000000, -50, 4 }, + { 0xE8D4A51000000000, -24, 12 }, + { 0xAD78EBC5AC620000, 3, 20 }, + { 0x813F3978F8940984, 30, 28 }, + { 0xC097CE7BC90715B3, 56, 36 }, + { 0x8F7E32CE7BEA5C70, 83, 44 }, + { 0xD5D238A4ABE98068, 109, 52 }, + { 0x9F4F2726179A2245, 136, 60 }, + { 0xED63A231D4C4FB27, 162, 68 }, + { 0xB0DE65388CC8ADA8, 189, 76 }, + { 0x83C7088E1AAB65DB, 216, 84 }, + { 0xC45D1DF942711D9A, 242, 92 }, + { 0x924D692CA61BE758, 269, 100 }, + { 0xDA01EE641A708DEA, 295, 108 }, + { 0xA26DA3999AEF774A, 322, 116 }, + { 0xF209787BB47D6B85, 348, 124 }, + { 0xB454E4A179DD1877, 375, 132 }, + { 0x865B86925B9BC5C2, 402, 140 }, + { 0xC83553C5C8965D3D, 428, 148 }, + { 0x952AB45CFA97A0B3, 455, 156 }, + { 0xDE469FBD99A05FE3, 481, 164 }, + { 0xA59BC234DB398C25, 508, 172 }, + { 0xF6C69A72A3989F5C, 534, 180 }, + { 0xB7DCBF5354E9BECE, 561, 188 }, + { 0x88FCF317F22241E2, 588, 196 }, + { 0xCC20CE9BD35C78A5, 614, 204 }, + { 0x98165AF37B2153DF, 641, 212 }, + { 0xE2A0B5DC971F303A, 667, 220 }, + { 0xA8D9D1535CE3B396, 694, 228 }, + { 0xFB9B7CD9A4A7443C, 720, 236 }, + { 0xBB764C4CA7A44410, 747, 244 }, + { 0x8BAB8EEFB6409C1A, 774, 252 }, + { 0xD01FEF10A657842C, 800, 260 }, + { 0x9B10A4E5E9913129, 827, 268 }, + { 0xE7109BFBA19C0C9D, 853, 276 }, + { 0xAC2820D9623BF429, 880, 284 }, + { 0x80444B5E7AA7CF85, 907, 292 }, + { 0xBF21E44003ACDD2D, 933, 300 }, + { 0x8E679C2F5E44FF8F, 960, 308 }, + { 0xD433179D9C8CB841, 986, 316 }, + { 0x9E19DB92B4E31BA9, 1013, 324 }, + } + }; + + // This computation gives exactly the same results for k as + // k = ceil((kAlpha - e - 1) * 0.30102999566398114) + // for |e| <= 1500, but doesn't require floating-point operations. + // NB: log_10(2) ~= 78913 / 2^18 + JSON_ASSERT(e >= -1500); + JSON_ASSERT(e <= 1500); + const int f = kAlpha - e - 1; + const int k = (f * 78913) / (1 << 18) + static_cast(f > 0); + + const int index = (-kCachedPowersMinDecExp + k + (kCachedPowersDecStep - 1)) / kCachedPowersDecStep; + JSON_ASSERT(index >= 0); + JSON_ASSERT(static_cast(index) < kCachedPowers.size()); + + const cached_power cached = kCachedPowers[static_cast(index)]; + JSON_ASSERT(kAlpha <= cached.e + e + 64); + JSON_ASSERT(kGamma >= cached.e + e + 64); + + return cached; +} + +/*! +For n != 0, returns k, such that pow10 := 10^(k-1) <= n < 10^k. +For n == 0, returns 1 and sets pow10 := 1. +*/ +inline int find_largest_pow10(const std::uint32_t n, std::uint32_t& pow10) +{ + // LCOV_EXCL_START + if (n >= 1000000000) + { + pow10 = 1000000000; + return 10; + } + // LCOV_EXCL_STOP + if (n >= 100000000) + { + pow10 = 100000000; + return 9; + } + if (n >= 10000000) + { + pow10 = 10000000; + return 8; + } + if (n >= 1000000) + { + pow10 = 1000000; + return 7; + } + if (n >= 100000) + { + pow10 = 100000; + return 6; + } + if (n >= 10000) + { + pow10 = 10000; + return 5; + } + if (n >= 1000) + { + pow10 = 1000; + return 4; + } + if (n >= 100) + { + pow10 = 100; + return 3; + } + if (n >= 10) + { + pow10 = 10; + return 2; + } + + pow10 = 1; + return 1; +} + +inline void grisu2_round(char* buf, int len, std::uint64_t dist, std::uint64_t delta, + std::uint64_t rest, std::uint64_t ten_k) +{ + JSON_ASSERT(len >= 1); + JSON_ASSERT(dist <= delta); + JSON_ASSERT(rest <= delta); + JSON_ASSERT(ten_k > 0); + + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // ten_k + // <------> + // <---- rest ----> + // --------------[------------------+----+--------------]-------------- + // w V + // = buf * 10^k + // + // ten_k represents a unit-in-the-last-place in the decimal representation + // stored in buf. + // Decrement buf by ten_k while this takes buf closer to w. + + // The tests are written in this order to avoid overflow in unsigned + // integer arithmetic. + + while (rest < dist + && delta - rest >= ten_k + && (rest + ten_k < dist || dist - rest > rest + ten_k - dist)) + { + JSON_ASSERT(buf[len - 1] != '0'); + buf[len - 1]--; + rest += ten_k; + } +} + +/*! +Generates V = buffer * 10^decimal_exponent, such that M- <= V <= M+. +M- and M+ must be normalized and share the same exponent -60 <= e <= -32. +*/ +inline void grisu2_digit_gen(char* buffer, int& length, int& decimal_exponent, + diyfp M_minus, diyfp w, diyfp M_plus) +{ + static_assert(kAlpha >= -60, "internal error"); + static_assert(kGamma <= -32, "internal error"); + + // Generates the digits (and the exponent) of a decimal floating-point + // number V = buffer * 10^decimal_exponent in the range [M-, M+]. The diyfp's + // w, M- and M+ share the same exponent e, which satisfies alpha <= e <= gamma. + // + // <--------------------------- delta ----> + // <---- dist ---------> + // --------------[------------------+-------------------]-------------- + // M- w M+ + // + // Grisu2 generates the digits of M+ from left to right and stops as soon as + // V is in [M-,M+]. + + JSON_ASSERT(M_plus.e >= kAlpha); + JSON_ASSERT(M_plus.e <= kGamma); + + std::uint64_t delta = diyfp::sub(M_plus, M_minus).f; // (significand of (M+ - M-), implicit exponent is e) + std::uint64_t dist = diyfp::sub(M_plus, w ).f; // (significand of (M+ - w ), implicit exponent is e) + + // Split M+ = f * 2^e into two parts p1 and p2 (note: e < 0): + // + // M+ = f * 2^e + // = ((f div 2^-e) * 2^-e + (f mod 2^-e)) * 2^e + // = ((p1 ) * 2^-e + (p2 )) * 2^e + // = p1 + p2 * 2^e + + const diyfp one(std::uint64_t{1} << -M_plus.e, M_plus.e); + + auto p1 = static_cast(M_plus.f >> -one.e); // p1 = f div 2^-e (Since -e >= 32, p1 fits into a 32-bit int.) + std::uint64_t p2 = M_plus.f & (one.f - 1); // p2 = f mod 2^-e + + // 1) + // + // Generate the digits of the integral part p1 = d[n-1]...d[1]d[0] + + JSON_ASSERT(p1 > 0); + + std::uint32_t pow10{}; + const int k = find_largest_pow10(p1, pow10); + + // 10^(k-1) <= p1 < 10^k, pow10 = 10^(k-1) + // + // p1 = (p1 div 10^(k-1)) * 10^(k-1) + (p1 mod 10^(k-1)) + // = (d[k-1] ) * 10^(k-1) + (p1 mod 10^(k-1)) + // + // M+ = p1 + p2 * 2^e + // = d[k-1] * 10^(k-1) + (p1 mod 10^(k-1)) + p2 * 2^e + // = d[k-1] * 10^(k-1) + ((p1 mod 10^(k-1)) * 2^-e + p2) * 2^e + // = d[k-1] * 10^(k-1) + ( rest) * 2^e + // + // Now generate the digits d[n] of p1 from left to right (n = k-1,...,0) + // + // p1 = d[k-1]...d[n] * 10^n + d[n-1]...d[0] + // + // but stop as soon as + // + // rest * 2^e = (d[n-1]...d[0] * 2^-e + p2) * 2^e <= delta * 2^e + + int n = k; + while (n > 0) + { + // Invariants: + // M+ = buffer * 10^n + (p1 + p2 * 2^e) (buffer = 0 for n = k) + // pow10 = 10^(n-1) <= p1 < 10^n + // + const std::uint32_t d = p1 / pow10; // d = p1 div 10^(n-1) + const std::uint32_t r = p1 % pow10; // r = p1 mod 10^(n-1) + // + // M+ = buffer * 10^n + (d * 10^(n-1) + r) + p2 * 2^e + // = (buffer * 10 + d) * 10^(n-1) + (r + p2 * 2^e) + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(n-1) + (r + p2 * 2^e) + // + p1 = r; + n--; + // + // M+ = buffer * 10^n + (p1 + p2 * 2^e) + // pow10 = 10^n + // + + // Now check if enough digits have been generated. + // Compute + // + // p1 + p2 * 2^e = (p1 * 2^-e + p2) * 2^e = rest * 2^e + // + // Note: + // Since rest and delta share the same exponent e, it suffices to + // compare the significands. + const std::uint64_t rest = (std::uint64_t{p1} << -one.e) + p2; + if (rest <= delta) + { + // V = buffer * 10^n, with M- <= V <= M+. + + decimal_exponent += n; + + // We may now just stop. But instead look if the buffer could be + // decremented to bring V closer to w. + // + // pow10 = 10^n is now 1 ulp in the decimal representation V. + // The rounding procedure works with diyfp's with an implicit + // exponent of e. + // + // 10^n = (10^n * 2^-e) * 2^e = ulp * 2^e + // + const std::uint64_t ten_n = std::uint64_t{pow10} << -one.e; + grisu2_round(buffer, length, dist, delta, rest, ten_n); + + return; + } + + pow10 /= 10; + // + // pow10 = 10^(n-1) <= p1 < 10^n + // Invariants restored. + } + + // 2) + // + // The digits of the integral part have been generated: + // + // M+ = d[k-1]...d[1]d[0] + p2 * 2^e + // = buffer + p2 * 2^e + // + // Now generate the digits of the fractional part p2 * 2^e. + // + // Note: + // No decimal point is generated: the exponent is adjusted instead. + // + // p2 actually represents the fraction + // + // p2 * 2^e + // = p2 / 2^-e + // = d[-1] / 10^1 + d[-2] / 10^2 + ... + // + // Now generate the digits d[-m] of p1 from left to right (m = 1,2,...) + // + // p2 * 2^e = d[-1]d[-2]...d[-m] * 10^-m + // + 10^-m * (d[-m-1] / 10^1 + d[-m-2] / 10^2 + ...) + // + // using + // + // 10^m * p2 = ((10^m * p2) div 2^-e) * 2^-e + ((10^m * p2) mod 2^-e) + // = ( d) * 2^-e + ( r) + // + // or + // 10^m * p2 * 2^e = d + r * 2^e + // + // i.e. + // + // M+ = buffer + p2 * 2^e + // = buffer + 10^-m * (d + r * 2^e) + // = (buffer * 10^m + d) * 10^-m + 10^-m * r * 2^e + // + // and stop as soon as 10^-m * r * 2^e <= delta * 2^e + + JSON_ASSERT(p2 > delta); + + int m = 0; + for (;;) + { + // Invariant: + // M+ = buffer * 10^-m + 10^-m * (d[-m-1] / 10 + d[-m-2] / 10^2 + ...) * 2^e + // = buffer * 10^-m + 10^-m * (p2 ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (10 * p2) ) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * ((10*p2 div 2^-e) * 2^-e + (10*p2 mod 2^-e)) * 2^e + // + JSON_ASSERT(p2 <= (std::numeric_limits::max)() / 10); + p2 *= 10; + const std::uint64_t d = p2 >> -one.e; // d = (10 * p2) div 2^-e + const std::uint64_t r = p2 & (one.f - 1); // r = (10 * p2) mod 2^-e + // + // M+ = buffer * 10^-m + 10^-m * (1/10 * (d * 2^-e + r) * 2^e + // = buffer * 10^-m + 10^-m * (1/10 * (d + r * 2^e)) + // = (buffer * 10 + d) * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + JSON_ASSERT(d <= 9); + buffer[length++] = static_cast('0' + d); // buffer := buffer * 10 + d + // + // M+ = buffer * 10^(-m-1) + 10^(-m-1) * r * 2^e + // + p2 = r; + m++; + // + // M+ = buffer * 10^-m + 10^-m * p2 * 2^e + // Invariant restored. + + // Check if enough digits have been generated. + // + // 10^-m * p2 * 2^e <= delta * 2^e + // p2 * 2^e <= 10^m * delta * 2^e + // p2 <= 10^m * delta + delta *= 10; + dist *= 10; + if (p2 <= delta) + { + break; + } + } + + // V = buffer * 10^-m, with M- <= V <= M+. + + decimal_exponent -= m; + + // 1 ulp in the decimal representation is now 10^-m. + // Since delta and dist are now scaled by 10^m, we need to do the + // same with ulp in order to keep the units in sync. + // + // 10^m * 10^-m = 1 = 2^-e * 2^e = ten_m * 2^e + // + const std::uint64_t ten_m = one.f; + grisu2_round(buffer, length, dist, delta, p2, ten_m); + + // By construction this algorithm generates the shortest possible decimal + // number (Loitsch, Theorem 6.2) which rounds back to w. + // For an input number of precision p, at least + // + // N = 1 + ceil(p * log_10(2)) + // + // decimal digits are sufficient to identify all binary floating-point + // numbers (Matula, "In-and-Out conversions"). + // This implies that the algorithm does not produce more than N decimal + // digits. + // + // N = 17 for p = 53 (IEEE double precision) + // N = 9 for p = 24 (IEEE single precision) +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +JSON_HEDLEY_NON_NULL(1) +inline void grisu2(char* buf, int& len, int& decimal_exponent, + diyfp m_minus, diyfp v, diyfp m_plus) +{ + JSON_ASSERT(m_plus.e == m_minus.e); + JSON_ASSERT(m_plus.e == v.e); + + // --------(-----------------------+-----------------------)-------- (A) + // m- v m+ + // + // --------------------(-----------+-----------------------)-------- (B) + // m- v m+ + // + // First scale v (and m- and m+) such that the exponent is in the range + // [alpha, gamma]. + + const cached_power cached = get_cached_power_for_binary_exponent(m_plus.e); + + const diyfp c_minus_k(cached.f, cached.e); // = c ~= 10^-k + + // The exponent of the products is = v.e + c_minus_k.e + q and is in the range [alpha,gamma] + const diyfp w = diyfp::mul(v, c_minus_k); + const diyfp w_minus = diyfp::mul(m_minus, c_minus_k); + const diyfp w_plus = diyfp::mul(m_plus, c_minus_k); + + // ----(---+---)---------------(---+---)---------------(---+---)---- + // w- w w+ + // = c*m- = c*v = c*m+ + // + // diyfp::mul rounds its result and c_minus_k is approximated too. w, w- and + // w+ are now off by a small amount. + // In fact: + // + // w - v * 10^k < 1 ulp + // + // To account for this inaccuracy, add resp. subtract 1 ulp. + // + // --------+---[---------------(---+---)---------------]---+-------- + // w- M- w M+ w+ + // + // Now any number in [M-, M+] (bounds included) will round to w when input, + // regardless of how the input rounding algorithm breaks ties. + // + // And digit_gen generates the shortest possible such number in [M-, M+]. + // Note that this does not mean that Grisu2 always generates the shortest + // possible number in the interval (m-, m+). + const diyfp M_minus(w_minus.f + 1, w_minus.e); + const diyfp M_plus (w_plus.f - 1, w_plus.e ); + + decimal_exponent = -cached.k; // = -(-k) = k + + grisu2_digit_gen(buf, len, decimal_exponent, M_minus, w, M_plus); +} + +/*! +v = buf * 10^decimal_exponent +len is the length of the buffer (number of decimal digits) +The buffer must be large enough, i.e. >= max_digits10. +*/ +template +JSON_HEDLEY_NON_NULL(1) +void grisu2(char* buf, int& len, int& decimal_exponent, FloatType value) +{ + static_assert(diyfp::kPrecision >= std::numeric_limits::digits + 3, + "internal error: not enough precision"); + + JSON_ASSERT(std::isfinite(value)); + JSON_ASSERT(value > 0); + + // If the neighbors (and boundaries) of 'value' are always computed for double-precision + // numbers, all float's can be recovered using strtod (and strtof). However, the resulting + // decimal representations are not exactly "short". + // + // The documentation for 'std::to_chars' (https://en.cppreference.com/w/cpp/utility/to_chars) + // says "value is converted to a string as if by std::sprintf in the default ("C") locale" + // and since sprintf promotes float's to double's, I think this is exactly what 'std::to_chars' + // does. + // On the other hand, the documentation for 'std::to_chars' requires that "parsing the + // representation using the corresponding std::from_chars function recovers value exactly". That + // indicates that single precision floating-point numbers should be recovered using + // 'std::strtof'. + // + // NB: If the neighbors are computed for single-precision numbers, there is a single float + // (7.0385307e-26f) which can't be recovered using strtod. The resulting double precision + // value is off by 1 ulp. +#if 0 + const boundaries w = compute_boundaries(static_cast(value)); +#else + const boundaries w = compute_boundaries(value); +#endif + + grisu2(buf, len, decimal_exponent, w.minus, w.w, w.plus); +} + +/*! +@brief appends a decimal representation of e to buf +@return a pointer to the element following the exponent. +@pre -1000 < e < 1000 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* append_exponent(char* buf, int e) +{ + JSON_ASSERT(e > -1000); + JSON_ASSERT(e < 1000); + + if (e < 0) + { + e = -e; + *buf++ = '-'; + } + else + { + *buf++ = '+'; + } + + auto k = static_cast(e); + if (k < 10) + { + // Always print at least two digits in the exponent. + // This is for compatibility with printf("%g"). + *buf++ = '0'; + *buf++ = static_cast('0' + k); + } + else if (k < 100) + { + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + else + { + *buf++ = static_cast('0' + k / 100); + k %= 100; + *buf++ = static_cast('0' + k / 10); + k %= 10; + *buf++ = static_cast('0' + k); + } + + return buf; +} + +/*! +@brief prettify v = buf * 10^decimal_exponent + +If v is in the range [10^min_exp, 10^max_exp) it will be printed in fixed-point +notation. Otherwise it will be printed in exponential notation. + +@pre min_exp < 0 +@pre max_exp > 0 +*/ +JSON_HEDLEY_NON_NULL(1) +JSON_HEDLEY_RETURNS_NON_NULL +inline char* format_buffer(char* buf, int len, int decimal_exponent, + int min_exp, int max_exp) +{ + JSON_ASSERT(min_exp < 0); + JSON_ASSERT(max_exp > 0); + + const int k = len; + const int n = len + decimal_exponent; + + // v = buf * 10^(n-k) + // k is the length of the buffer (number of decimal digits) + // n is the position of the decimal point relative to the start of the buffer. + + if (k <= n && n <= max_exp) + { + // digits[000] + // len <= max_exp + 2 + + std::memset(buf + k, '0', static_cast(n) - static_cast(k)); + // Make it look like a floating-point number (#362, #378) + buf[n + 0] = '.'; + buf[n + 1] = '0'; + return buf + (static_cast(n) + 2); + } + + if (0 < n && n <= max_exp) + { + // dig.its + // len <= max_digits10 + 1 + + JSON_ASSERT(k > n); + + std::memmove(buf + (static_cast(n) + 1), buf + n, static_cast(k) - static_cast(n)); + buf[n] = '.'; + return buf + (static_cast(k) + 1U); + } + + if (min_exp < n && n <= 0) + { + // 0.[000]digits + // len <= 2 + (-min_exp - 1) + max_digits10 + + std::memmove(buf + (2 + static_cast(-n)), buf, static_cast(k)); + buf[0] = '0'; + buf[1] = '.'; + std::memset(buf + 2, '0', static_cast(-n)); + return buf + (2U + static_cast(-n) + static_cast(k)); + } + + if (k == 1) + { + // dE+123 + // len <= 1 + 5 + + buf += 1; + } + else + { + // d.igitsE+123 + // len <= max_digits10 + 1 + 5 + + std::memmove(buf + 2, buf + 1, static_cast(k) - 1); + buf[1] = '.'; + buf += 1 + static_cast(k); + } + + *buf++ = 'e'; + return append_exponent(buf, n - 1); +} + +} // namespace dtoa_impl + +/*! +@brief generates a decimal representation of the floating-point number value in [first, last). + +The format of the resulting decimal representation is similar to printf's %g +format. Returns an iterator pointing past-the-end of the decimal representation. + +@note The input number must be finite, i.e. NaN's and Inf's are not supported. +@note The buffer must be large enough. +@note The result is NOT null-terminated. +*/ +template +JSON_HEDLEY_NON_NULL(1, 2) +JSON_HEDLEY_RETURNS_NON_NULL +char* to_chars(char* first, const char* last, FloatType value) +{ + static_cast(last); // maybe unused - fix warning + JSON_ASSERT(std::isfinite(value)); + + // Use signbit(value) instead of (value < 0) since signbit works for -0. + if (std::signbit(value)) + { + value = -value; + *first++ = '-'; + } + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + if (value == 0) // +-0 + { + *first++ = '0'; + // Make it look like a floating-point number (#362, #378) + *first++ = '.'; + *first++ = '0'; + return first; + } +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10); + + // Compute v = buffer * 10^decimal_exponent. + // The decimal digits are stored in the buffer, which needs to be interpreted + // as an unsigned decimal integer. + // len is the length of the buffer, i.e. the number of decimal digits. + int len = 0; + int decimal_exponent = 0; + dtoa_impl::grisu2(first, len, decimal_exponent, value); + + JSON_ASSERT(len <= std::numeric_limits::max_digits10); + + // Format the buffer like printf("%.*g", prec, value) + constexpr int kMinExp = -4; + // Use digits10 here to increase compatibility with version 2. + constexpr int kMaxExp = std::numeric_limits::digits10; + + JSON_ASSERT(last - first >= kMaxExp + 2); + JSON_ASSERT(last - first >= 2 + (-kMinExp - 1) + std::numeric_limits::max_digits10); + JSON_ASSERT(last - first >= std::numeric_limits::max_digits10 + 6); + + return dtoa_impl::format_buffer(first, len, decimal_exponent, kMinExp, kMaxExp); +} + +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + +// #include + +// #include + +// #include + + +namespace nlohmann +{ +namespace detail +{ +/////////////////// +// serialization // +/////////////////// + +/// how to treat decoding errors +enum class error_handler_t +{ + strict, ///< throw a type_error exception in case of invalid UTF-8 + replace, ///< replace invalid UTF-8 sequences with U+FFFD + ignore ///< ignore invalid UTF-8 sequences +}; + +template +class serializer +{ + using string_t = typename BasicJsonType::string_t; + using number_float_t = typename BasicJsonType::number_float_t; + using number_integer_t = typename BasicJsonType::number_integer_t; + using number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using binary_char_t = typename BasicJsonType::binary_t::value_type; + static constexpr std::uint8_t UTF8_ACCEPT = 0; + static constexpr std::uint8_t UTF8_REJECT = 1; + + public: + /*! + @param[in] s output stream to serialize to + @param[in] ichar indentation character to use + @param[in] error_handler_ how to react on decoding errors + */ + serializer(output_adapter_t s, const char ichar, + error_handler_t error_handler_ = error_handler_t::strict) + : o(std::move(s)) + , loc(std::localeconv()) + , thousands_sep(loc->thousands_sep == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->thousands_sep))) + , decimal_point(loc->decimal_point == nullptr ? '\0' : std::char_traits::to_char_type(* (loc->decimal_point))) + , indent_char(ichar) + , indent_string(512, indent_char) + , error_handler(error_handler_) + {} + + // delete because of pointer members + serializer(const serializer&) = delete; + serializer& operator=(const serializer&) = delete; + serializer(serializer&&) = delete; + serializer& operator=(serializer&&) = delete; + ~serializer() = default; + + /*! + @brief internal implementation of the serialization function + + This function is called by the public member function dump and organizes + the serialization internally. The indentation level is propagated as + additional parameter. In case of arrays and objects, the function is + called recursively. + + - strings and object keys are escaped using `escape_string()` + - integer numbers are converted implicitly via `operator<<` + - floating-point numbers are converted to a string using `"%g"` format + - binary values are serialized as objects containing the subtype and the + byte array + + @param[in] val value to serialize + @param[in] pretty_print whether the output shall be pretty-printed + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] indent_step the indent level + @param[in] current_indent the current indent level (only used internally) + */ + void dump(const BasicJsonType& val, + const bool pretty_print, + const bool ensure_ascii, + const unsigned int indent_step, + const unsigned int current_indent = 0) + { + switch (val.m_type) + { + case value_t::object: + { + if (val.m_value.object->empty()) + { + o->write_characters("{}", 2); + return; + } + + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_characters(indent_string.c_str(), new_indent); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\": ", 3); + dump(i->second, true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_character('{'); + + // first n-1 elements + auto i = val.m_value.object->cbegin(); + for (std::size_t cnt = 0; cnt < val.m_value.object->size() - 1; ++cnt, ++i) + { + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(i != val.m_value.object->cend()); + JSON_ASSERT(std::next(i) == val.m_value.object->cend()); + o->write_character('\"'); + dump_escaped(i->first, ensure_ascii); + o->write_characters("\":", 2); + dump(i->second, false, ensure_ascii, indent_step, current_indent); + + o->write_character('}'); + } + + return; + } + + case value_t::array: + { + if (val.m_value.array->empty()) + { + o->write_characters("[]", 2); + return; + } + + if (pretty_print) + { + o->write_characters("[\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + o->write_characters(indent_string.c_str(), new_indent); + dump(*i, true, ensure_ascii, indent_step, new_indent); + o->write_characters(",\n", 2); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + o->write_characters(indent_string.c_str(), new_indent); + dump(val.m_value.array->back(), true, ensure_ascii, indent_step, new_indent); + + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character(']'); + } + else + { + o->write_character('['); + + // first n-1 elements + for (auto i = val.m_value.array->cbegin(); + i != val.m_value.array->cend() - 1; ++i) + { + dump(*i, false, ensure_ascii, indent_step, current_indent); + o->write_character(','); + } + + // last element + JSON_ASSERT(!val.m_value.array->empty()); + dump(val.m_value.array->back(), false, ensure_ascii, indent_step, current_indent); + + o->write_character(']'); + } + + return; + } + + case value_t::string: + { + o->write_character('\"'); + dump_escaped(*val.m_value.string, ensure_ascii); + o->write_character('\"'); + return; + } + + case value_t::binary: + { + if (pretty_print) + { + o->write_characters("{\n", 2); + + // variable to hold indentation for recursive calls + const auto new_indent = current_indent + indent_step; + if (JSON_HEDLEY_UNLIKELY(indent_string.size() < new_indent)) + { + indent_string.resize(indent_string.size() * 2, ' '); + } + + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"bytes\": [", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_characters(", ", 2); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\n", 3); + o->write_characters(indent_string.c_str(), new_indent); + + o->write_characters("\"subtype\": ", 11); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + } + else + { + o->write_characters("null", 4); + } + o->write_character('\n'); + o->write_characters(indent_string.c_str(), current_indent); + o->write_character('}'); + } + else + { + o->write_characters("{\"bytes\":[", 10); + + if (!val.m_value.binary->empty()) + { + for (auto i = val.m_value.binary->cbegin(); + i != val.m_value.binary->cend() - 1; ++i) + { + dump_integer(*i); + o->write_character(','); + } + dump_integer(val.m_value.binary->back()); + } + + o->write_characters("],\"subtype\":", 12); + if (val.m_value.binary->has_subtype()) + { + dump_integer(val.m_value.binary->subtype()); + o->write_character('}'); + } + else + { + o->write_characters("null}", 5); + } + } + return; + } + + case value_t::boolean: + { + if (val.m_value.boolean) + { + o->write_characters("true", 4); + } + else + { + o->write_characters("false", 5); + } + return; + } + + case value_t::number_integer: + { + dump_integer(val.m_value.number_integer); + return; + } + + case value_t::number_unsigned: + { + dump_integer(val.m_value.number_unsigned); + return; + } + + case value_t::number_float: + { + dump_float(val.m_value.number_float); + return; + } + + case value_t::discarded: + { + o->write_characters("", 11); + return; + } + + case value_t::null: + { + o->write_characters("null", 4); + return; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief dump escaped string + + Escape a string by replacing certain special characters by a sequence of an + escape character (backslash) and another character and other control + characters by a sequence of "\u" followed by a four-digit hex + representation. The escaped string is written to output stream @a o. + + @param[in] s the string to escape + @param[in] ensure_ascii whether to escape non-ASCII characters with + \uXXXX sequences + + @complexity Linear in the length of string @a s. + */ + void dump_escaped(const string_t& s, const bool ensure_ascii) + { + std::uint32_t codepoint{}; + std::uint8_t state = UTF8_ACCEPT; + std::size_t bytes = 0; // number of bytes written to string_buffer + + // number of bytes written at the point of the last valid byte + std::size_t bytes_after_last_accept = 0; + std::size_t undumped_chars = 0; + + for (std::size_t i = 0; i < s.size(); ++i) + { + const auto byte = static_cast(s[i]); + + switch (decode(state, codepoint, byte)) + { + case UTF8_ACCEPT: // decode found a new code point + { + switch (codepoint) + { + case 0x08: // backspace + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'b'; + break; + } + + case 0x09: // horizontal tab + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 't'; + break; + } + + case 0x0A: // newline + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'n'; + break; + } + + case 0x0C: // formfeed + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'f'; + break; + } + + case 0x0D: // carriage return + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'r'; + break; + } + + case 0x22: // quotation mark + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\"'; + break; + } + + case 0x5C: // reverse solidus + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = '\\'; + break; + } + + default: + { + // escape control characters (0x00..0x1F) or, if + // ensure_ascii parameter is used, non-ASCII characters + if ((codepoint <= 0x1F) || (ensure_ascii && (codepoint >= 0x7F))) + { + if (codepoint <= 0xFFFF) + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(string_buffer.data() + bytes, 7, "\\u%04x", + static_cast(codepoint)); + bytes += 6; + } + else + { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(string_buffer.data() + bytes, 13, "\\u%04x\\u%04x", + static_cast(0xD7C0u + (codepoint >> 10u)), + static_cast(0xDC00u + (codepoint & 0x3FFu))); + bytes += 12; + } + } + else + { + // copy byte to buffer (all previous bytes + // been copied have in default case above) + string_buffer[bytes++] = s[i]; + } + break; + } + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + // remember the byte position of this accept + bytes_after_last_accept = bytes; + undumped_chars = 0; + break; + } + + case UTF8_REJECT: // decode found invalid UTF-8 byte + { + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(9, '\0'); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(&sn[0], sn.size(), "%.2X", byte); + JSON_THROW(type_error::create(316, "invalid UTF-8 byte at index " + std::to_string(i) + ": 0x" + sn, BasicJsonType())); + } + + case error_handler_t::ignore: + case error_handler_t::replace: + { + // in case we saw this character the first time, we + // would like to read it again, because the byte + // may be OK for itself, but just not OK for the + // previous sequence + if (undumped_chars > 0) + { + --i; + } + + // reset length buffer to the last accepted index; + // thus removing/ignoring the invalid characters + bytes = bytes_after_last_accept; + + if (error_handler == error_handler_t::replace) + { + // add a replacement character + if (ensure_ascii) + { + string_buffer[bytes++] = '\\'; + string_buffer[bytes++] = 'u'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'f'; + string_buffer[bytes++] = 'd'; + } + else + { + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xEF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBF'); + string_buffer[bytes++] = detail::binary_writer::to_char_type('\xBD'); + } + + // write buffer and reset index; there must be 13 bytes + // left, as this is the maximal number of bytes to be + // written ("\uxxxx\uxxxx\0") for one code point + if (string_buffer.size() - bytes < 13) + { + o->write_characters(string_buffer.data(), bytes); + bytes = 0; + } + + bytes_after_last_accept = bytes; + } + + undumped_chars = 0; + + // continue processing the string + state = UTF8_ACCEPT; + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + break; + } + + default: // decode found yet incomplete multi-byte code point + { + if (!ensure_ascii) + { + // code point will not be escaped - copy byte to buffer + string_buffer[bytes++] = s[i]; + } + ++undumped_chars; + break; + } + } + } + + // we finished processing the string + if (JSON_HEDLEY_LIKELY(state == UTF8_ACCEPT)) + { + // write buffer + if (bytes > 0) + { + o->write_characters(string_buffer.data(), bytes); + } + } + else + { + // we finish reading, but do not accept: string was incomplete + switch (error_handler) + { + case error_handler_t::strict: + { + std::string sn(9, '\0'); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + (std::snprintf)(&sn[0], sn.size(), "%.2X", static_cast(s.back())); + JSON_THROW(type_error::create(316, "incomplete UTF-8 string; last byte: 0x" + sn, BasicJsonType())); + } + + case error_handler_t::ignore: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + break; + } + + case error_handler_t::replace: + { + // write all accepted bytes + o->write_characters(string_buffer.data(), bytes_after_last_accept); + // add a replacement character + if (ensure_ascii) + { + o->write_characters("\\ufffd", 6); + } + else + { + o->write_characters("\xEF\xBF\xBD", 3); + } + break; + } + + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + } + } + + private: + /*! + @brief count digits + + Count the number of decimal (base 10) digits for an input unsigned integer. + + @param[in] x unsigned integer number to count its digits + @return number of decimal digits + */ + inline unsigned int count_digits(number_unsigned_t x) noexcept + { + unsigned int n_digits = 1; + for (;;) + { + if (x < 10) + { + return n_digits; + } + if (x < 100) + { + return n_digits + 1; + } + if (x < 1000) + { + return n_digits + 2; + } + if (x < 10000) + { + return n_digits + 3; + } + x = x / 10000u; + n_digits += 4; + } + } + + /*! + @brief dump an integer + + Dump a given integer to output stream @a o. Works internally with + @a number_buffer. + + @param[in] x integer number (signed or unsigned) to dump + @tparam NumberType either @a number_integer_t or @a number_unsigned_t + */ + template < typename NumberType, detail::enable_if_t < + std::is_integral::value || + std::is_same::value || + std::is_same::value || + std::is_same::value, + int > = 0 > + void dump_integer(NumberType x) + { + static constexpr std::array, 100> digits_to_99 + { + { + {{'0', '0'}}, {{'0', '1'}}, {{'0', '2'}}, {{'0', '3'}}, {{'0', '4'}}, {{'0', '5'}}, {{'0', '6'}}, {{'0', '7'}}, {{'0', '8'}}, {{'0', '9'}}, + {{'1', '0'}}, {{'1', '1'}}, {{'1', '2'}}, {{'1', '3'}}, {{'1', '4'}}, {{'1', '5'}}, {{'1', '6'}}, {{'1', '7'}}, {{'1', '8'}}, {{'1', '9'}}, + {{'2', '0'}}, {{'2', '1'}}, {{'2', '2'}}, {{'2', '3'}}, {{'2', '4'}}, {{'2', '5'}}, {{'2', '6'}}, {{'2', '7'}}, {{'2', '8'}}, {{'2', '9'}}, + {{'3', '0'}}, {{'3', '1'}}, {{'3', '2'}}, {{'3', '3'}}, {{'3', '4'}}, {{'3', '5'}}, {{'3', '6'}}, {{'3', '7'}}, {{'3', '8'}}, {{'3', '9'}}, + {{'4', '0'}}, {{'4', '1'}}, {{'4', '2'}}, {{'4', '3'}}, {{'4', '4'}}, {{'4', '5'}}, {{'4', '6'}}, {{'4', '7'}}, {{'4', '8'}}, {{'4', '9'}}, + {{'5', '0'}}, {{'5', '1'}}, {{'5', '2'}}, {{'5', '3'}}, {{'5', '4'}}, {{'5', '5'}}, {{'5', '6'}}, {{'5', '7'}}, {{'5', '8'}}, {{'5', '9'}}, + {{'6', '0'}}, {{'6', '1'}}, {{'6', '2'}}, {{'6', '3'}}, {{'6', '4'}}, {{'6', '5'}}, {{'6', '6'}}, {{'6', '7'}}, {{'6', '8'}}, {{'6', '9'}}, + {{'7', '0'}}, {{'7', '1'}}, {{'7', '2'}}, {{'7', '3'}}, {{'7', '4'}}, {{'7', '5'}}, {{'7', '6'}}, {{'7', '7'}}, {{'7', '8'}}, {{'7', '9'}}, + {{'8', '0'}}, {{'8', '1'}}, {{'8', '2'}}, {{'8', '3'}}, {{'8', '4'}}, {{'8', '5'}}, {{'8', '6'}}, {{'8', '7'}}, {{'8', '8'}}, {{'8', '9'}}, + {{'9', '0'}}, {{'9', '1'}}, {{'9', '2'}}, {{'9', '3'}}, {{'9', '4'}}, {{'9', '5'}}, {{'9', '6'}}, {{'9', '7'}}, {{'9', '8'}}, {{'9', '9'}}, + } + }; + + // special case for "0" + if (x == 0) + { + o->write_character('0'); + return; + } + + // use a pointer to fill the buffer + auto buffer_ptr = number_buffer.begin(); // NOLINT(llvm-qualified-auto,readability-qualified-auto,cppcoreguidelines-pro-type-vararg,hicpp-vararg) + + const bool is_negative = std::is_signed::value && !(x >= 0); // see issue #755 + number_unsigned_t abs_value; + + unsigned int n_chars{}; + + if (is_negative) + { + *buffer_ptr = '-'; + abs_value = remove_sign(static_cast(x)); + + // account one more byte for the minus sign + n_chars = 1 + count_digits(abs_value); + } + else + { + abs_value = static_cast(x); + n_chars = count_digits(abs_value); + } + + // spare 1 byte for '\0' + JSON_ASSERT(n_chars < number_buffer.size() - 1); + + // jump to the end to generate the string from backward + // so we later avoid reversing the result + buffer_ptr += n_chars; + + // Fast int2ascii implementation inspired by "Fastware" talk by Andrei Alexandrescu + // See: https://www.youtube.com/watch?v=o4-CwDo2zpg + while (abs_value >= 100) + { + const auto digits_index = static_cast((abs_value % 100)); + abs_value /= 100; + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + + if (abs_value >= 10) + { + const auto digits_index = static_cast(abs_value); + *(--buffer_ptr) = digits_to_99[digits_index][1]; + *(--buffer_ptr) = digits_to_99[digits_index][0]; + } + else + { + *(--buffer_ptr) = static_cast('0' + abs_value); + } + + o->write_characters(number_buffer.data(), n_chars); + } + + /*! + @brief dump a floating-point number + + Dump a given floating-point number to output stream @a o. Works internally + with @a number_buffer. + + @param[in] x floating-point number to dump + */ + void dump_float(number_float_t x) + { + // NaN / inf + if (!std::isfinite(x)) + { + o->write_characters("null", 4); + return; + } + + // If number_float_t is an IEEE-754 single or double precision number, + // use the Grisu2 algorithm to produce short numbers which are + // guaranteed to round-trip, using strtof and strtod, resp. + // + // NB: The test below works if == . + static constexpr bool is_ieee_single_or_double + = (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 24 && std::numeric_limits::max_exponent == 128) || + (std::numeric_limits::is_iec559 && std::numeric_limits::digits == 53 && std::numeric_limits::max_exponent == 1024); + + dump_float(x, std::integral_constant()); + } + + void dump_float(number_float_t x, std::true_type /*is_ieee_single_or_double*/) + { + auto* begin = number_buffer.data(); + auto* end = ::nlohmann::detail::to_chars(begin, begin + number_buffer.size(), x); + + o->write_characters(begin, static_cast(end - begin)); + } + + void dump_float(number_float_t x, std::false_type /*is_ieee_single_or_double*/) + { + // get number of digits for a float -> text -> float round-trip + static constexpr auto d = std::numeric_limits::max_digits10; + + // the actual conversion + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-vararg,hicpp-vararg) + std::ptrdiff_t len = (std::snprintf)(number_buffer.data(), number_buffer.size(), "%.*g", d, x); + + // negative value indicates an error + JSON_ASSERT(len > 0); + // check if buffer was large enough + JSON_ASSERT(static_cast(len) < number_buffer.size()); + + // erase thousands separator + if (thousands_sep != '\0') + { + auto* const end = std::remove(number_buffer.begin(), + number_buffer.begin() + len, thousands_sep); + std::fill(end, number_buffer.end(), '\0'); + JSON_ASSERT((end - number_buffer.begin()) <= len); + len = (end - number_buffer.begin()); + } + + // convert decimal point to '.' + if (decimal_point != '\0' && decimal_point != '.') + { + auto* const dec_pos = std::find(number_buffer.begin(), number_buffer.end(), decimal_point); + if (dec_pos != number_buffer.end()) + { + *dec_pos = '.'; + } + } + + o->write_characters(number_buffer.data(), static_cast(len)); + + // determine if need to append ".0" + const bool value_is_int_like = + std::none_of(number_buffer.begin(), number_buffer.begin() + len + 1, + [](char c) + { + return c == '.' || c == 'e'; + }); + + if (value_is_int_like) + { + o->write_characters(".0", 2); + } + } + + /*! + @brief check whether a string is UTF-8 encoded + + The function checks each byte of a string whether it is UTF-8 encoded. The + result of the check is stored in the @a state parameter. The function must + be called initially with state 0 (accept). State 1 means the string must + be rejected, because the current byte is not allowed. If the string is + completely processed, but the state is non-zero, the string ended + prematurely; that is, the last byte indicated more bytes should have + followed. + + @param[in,out] state the state of the decoding + @param[in,out] codep codepoint (valid only if resulting state is UTF8_ACCEPT) + @param[in] byte next byte to decode + @return new state + + @note The function has been edited: a std::array is used. + + @copyright Copyright (c) 2008-2009 Bjoern Hoehrmann + @sa http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ + */ + static std::uint8_t decode(std::uint8_t& state, std::uint32_t& codep, const std::uint8_t byte) noexcept + { + static const std::array utf8d = + { + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7F + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9F + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // A0..BF + 8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0..DF + 0xA, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x4, 0x3, 0x3, // E0..EF + 0xB, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8, // F0..FF + 0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4, 0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2 + 1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4 + 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6 + 1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // s7..s8 + } + }; + + JSON_ASSERT(byte < utf8d.size()); + const std::uint8_t type = utf8d[byte]; + + codep = (state != UTF8_ACCEPT) + ? (byte & 0x3fu) | (codep << 6u) + : (0xFFu >> type) & (byte); + + std::size_t index = 256u + static_cast(state) * 16u + static_cast(type); + JSON_ASSERT(index < 400); + state = utf8d[index]; + return state; + } + + /* + * Overload to make the compiler happy while it is instantiating + * dump_integer for number_unsigned_t. + * Must never be called. + */ + number_unsigned_t remove_sign(number_unsigned_t x) + { + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + return x; // LCOV_EXCL_LINE + } + + /* + * Helper function for dump_integer + * + * This function takes a negative signed integer and returns its absolute + * value as unsigned integer. The plus/minus shuffling is necessary as we can + * not directly remove the sign of an arbitrary signed integer as the + * absolute values of INT_MIN and INT_MAX are usually not the same. See + * #1708 for details. + */ + inline number_unsigned_t remove_sign(number_integer_t x) noexcept + { + JSON_ASSERT(x < 0 && x < (std::numeric_limits::max)()); // NOLINT(misc-redundant-expression) + return static_cast(-(x + 1)) + 1; + } + + private: + /// the output of the serializer + output_adapter_t o = nullptr; + + /// a (hopefully) large enough character buffer + std::array number_buffer{{}}; + + /// the locale + const std::lconv* loc = nullptr; + /// the locale's thousand separator character + const char thousands_sep = '\0'; + /// the locale's decimal point character + const char decimal_point = '\0'; + + /// string buffer + std::array string_buffer{{}}; + + /// the indentation character + const char indent_char; + /// the indentation string + string_t indent_string; + + /// error_handler how to react on decoding errors + const error_handler_t error_handler; +}; +} // namespace detail +} // namespace nlohmann + +// #include + +// #include + +// #include + + +#include // less +#include // initializer_list +#include // input_iterator_tag, iterator_traits +#include // allocator +#include // for out_of_range +#include // enable_if, is_convertible +#include // pair +#include // vector + +// #include + + +namespace nlohmann +{ + +/// ordered_map: a minimal map-like container that preserves insertion order +/// for use within nlohmann::basic_json +template , + class Allocator = std::allocator>> + struct ordered_map : std::vector, Allocator> +{ + using key_type = Key; + using mapped_type = T; + using Container = std::vector, Allocator>; + using typename Container::iterator; + using typename Container::const_iterator; + using typename Container::size_type; + using typename Container::value_type; + + // Explicit constructors instead of `using Container::Container` + // otherwise older compilers choke on it (GCC <= 5.5, xcode <= 9.4) + ordered_map(const Allocator& alloc = Allocator()) : Container{alloc} {} + template + ordered_map(It first, It last, const Allocator& alloc = Allocator()) + : Container{first, last, alloc} {} + ordered_map(std::initializer_list init, const Allocator& alloc = Allocator() ) + : Container{init, alloc} {} + + std::pair emplace(const key_type& key, T&& t) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return {it, false}; + } + } + Container::emplace_back(key, t); + return {--this->end(), true}; + } + + T& operator[](const Key& key) + { + return emplace(key, T{}).first->second; + } + + const T& operator[](const Key& key) const + { + return at(key); + } + + T& at(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + const T& at(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it->second; + } + } + + JSON_THROW(std::out_of_range("key not found")); + } + + size_type erase(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return 1; + } + } + return 0; + } + + iterator erase(iterator pos) + { + auto it = pos; + + // Since we cannot move const Keys, re-construct them in place + for (auto next = it; ++next != this->end(); ++it) + { + it->~value_type(); // Destroy but keep allocation + new (&*it) value_type{std::move(*next)}; + } + Container::pop_back(); + return pos; + } + + size_type count(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return 1; + } + } + return 0; + } + + iterator find(const Key& key) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + const_iterator find(const Key& key) const + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == key) + { + return it; + } + } + return Container::end(); + } + + std::pair insert( value_type&& value ) + { + return emplace(value.first, std::move(value.second)); + } + + std::pair insert( const value_type& value ) + { + for (auto it = this->begin(); it != this->end(); ++it) + { + if (it->first == value.first) + { + return {it, false}; + } + } + Container::push_back(value); + return {--this->end(), true}; + } + + template + using require_input_iter = typename std::enable_if::iterator_category, + std::input_iterator_tag>::value>::type; + + template> + void insert(InputIt first, InputIt last) + { + for (auto it = first; it != last; ++it) + { + insert(*it); + } + } +}; + +} // namespace nlohmann + + +#if defined(JSON_HAS_CPP_17) + #include +#endif + +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +namespace nlohmann +{ + +/*! +@brief a class to store JSON values + +@tparam ObjectType type for JSON objects (`std::map` by default; will be used +in @ref object_t) +@tparam ArrayType type for JSON arrays (`std::vector` by default; will be used +in @ref array_t) +@tparam StringType type for JSON strings and object keys (`std::string` by +default; will be used in @ref string_t) +@tparam BooleanType type for JSON booleans (`bool` by default; will be used +in @ref boolean_t) +@tparam NumberIntegerType type for JSON integer numbers (`int64_t` by +default; will be used in @ref number_integer_t) +@tparam NumberUnsignedType type for JSON unsigned integer numbers (@c +`uint64_t` by default; will be used in @ref number_unsigned_t) +@tparam NumberFloatType type for JSON floating-point numbers (`double` by +default; will be used in @ref number_float_t) +@tparam BinaryType type for packed binary data for compatibility with binary +serialization formats (`std::vector` by default; will be used in +@ref binary_t) +@tparam AllocatorType type of the allocator to use (`std::allocator` by +default) +@tparam JSONSerializer the serializer to resolve internal calls to `to_json()` +and `from_json()` (@ref adl_serializer by default) + +@requirement The class satisfies the following concept requirements: +- Basic + - [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible): + JSON values can be default constructed. The result will be a JSON null + value. + - [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible): + A JSON value can be constructed from an rvalue argument. + - [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible): + A JSON value can be copy-constructed from an lvalue expression. + - [MoveAssignable](https://en.cppreference.com/w/cpp/named_req/MoveAssignable): + A JSON value van be assigned from an rvalue argument. + - [CopyAssignable](https://en.cppreference.com/w/cpp/named_req/CopyAssignable): + A JSON value can be copy-assigned from an lvalue expression. + - [Destructible](https://en.cppreference.com/w/cpp/named_req/Destructible): + JSON values can be destructed. +- Layout + - [StandardLayoutType](https://en.cppreference.com/w/cpp/named_req/StandardLayoutType): + JSON values have + [standard layout](https://en.cppreference.com/w/cpp/language/data_members#Standard_layout): + All non-static data members are private and standard layout types, the + class has no virtual functions or (virtual) base classes. +- Library-wide + - [EqualityComparable](https://en.cppreference.com/w/cpp/named_req/EqualityComparable): + JSON values can be compared with `==`, see @ref + operator==(const_reference,const_reference). + - [LessThanComparable](https://en.cppreference.com/w/cpp/named_req/LessThanComparable): + JSON values can be compared with `<`, see @ref + operator<(const_reference,const_reference). + - [Swappable](https://en.cppreference.com/w/cpp/named_req/Swappable): + Any JSON lvalue or rvalue of can be swapped with any lvalue or rvalue of + other compatible types, using unqualified function call @ref swap(). + - [NullablePointer](https://en.cppreference.com/w/cpp/named_req/NullablePointer): + JSON values can be compared against `std::nullptr_t` objects which are used + to model the `null` value. +- Container + - [Container](https://en.cppreference.com/w/cpp/named_req/Container): + JSON values can be used like STL containers and provide iterator access. + - [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer); + JSON values can be used like STL containers and provide reverse iterator + access. + +@invariant The member variables @a m_value and @a m_type have the following +relationship: +- If `m_type == value_t::object`, then `m_value.object != nullptr`. +- If `m_type == value_t::array`, then `m_value.array != nullptr`. +- If `m_type == value_t::string`, then `m_value.string != nullptr`. +The invariants are checked by member function assert_invariant(). + +@internal +@note ObjectType trick from https://stackoverflow.com/a/9860911 +@endinternal + +@see [RFC 8259: The JavaScript Object Notation (JSON) Data Interchange +Format](https://tools.ietf.org/html/rfc8259) + +@since version 1.0.0 + +@nosubgrouping +*/ +NLOHMANN_BASIC_JSON_TPL_DECLARATION +class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-special-member-functions) +{ + private: + template friend struct detail::external_constructor; + friend ::nlohmann::json_pointer; + + template + friend class ::nlohmann::detail::parser; + friend ::nlohmann::detail::serializer; + template + friend class ::nlohmann::detail::iter_impl; + template + friend class ::nlohmann::detail::binary_writer; + template + friend class ::nlohmann::detail::binary_reader; + template + friend class ::nlohmann::detail::json_sax_dom_parser; + template + friend class ::nlohmann::detail::json_sax_dom_callback_parser; + friend class ::nlohmann::detail::exception; + + /// workaround type for MSVC + using basic_json_t = NLOHMANN_BASIC_JSON_TPL; + + JSON_PRIVATE_UNLESS_TESTED: + // convenience aliases for types residing in namespace detail; + using lexer = ::nlohmann::detail::lexer_base; + + template + static ::nlohmann::detail::parser parser( + InputAdapterType adapter, + detail::parser_callback_tcb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false + ) + { + return ::nlohmann::detail::parser(std::move(adapter), + std::move(cb), allow_exceptions, ignore_comments); + } + + private: + using primitive_iterator_t = ::nlohmann::detail::primitive_iterator_t; + template + using internal_iterator = ::nlohmann::detail::internal_iterator; + template + using iter_impl = ::nlohmann::detail::iter_impl; + template + using iteration_proxy = ::nlohmann::detail::iteration_proxy; + template using json_reverse_iterator = ::nlohmann::detail::json_reverse_iterator; + + template + using output_adapter_t = ::nlohmann::detail::output_adapter_t; + + template + using binary_reader = ::nlohmann::detail::binary_reader; + template using binary_writer = ::nlohmann::detail::binary_writer; + + JSON_PRIVATE_UNLESS_TESTED: + using serializer = ::nlohmann::detail::serializer; + + public: + using value_t = detail::value_t; + /// JSON Pointer, see @ref nlohmann::json_pointer + using json_pointer = ::nlohmann::json_pointer; + template + using json_serializer = JSONSerializer; + /// how to treat decoding errors + using error_handler_t = detail::error_handler_t; + /// how to treat CBOR tags + using cbor_tag_handler_t = detail::cbor_tag_handler_t; + /// helper type for initializer lists of basic_json values + using initializer_list_t = std::initializer_list>; + + using input_format_t = detail::input_format_t; + /// SAX interface type, see @ref nlohmann::json_sax + using json_sax_t = json_sax; + + //////////////// + // exceptions // + //////////////// + + /// @name exceptions + /// Classes to implement user-defined exceptions. + /// @{ + + /// @copydoc detail::exception + using exception = detail::exception; + /// @copydoc detail::parse_error + using parse_error = detail::parse_error; + /// @copydoc detail::invalid_iterator + using invalid_iterator = detail::invalid_iterator; + /// @copydoc detail::type_error + using type_error = detail::type_error; + /// @copydoc detail::out_of_range + using out_of_range = detail::out_of_range; + /// @copydoc detail::other_error + using other_error = detail::other_error; + + /// @} + + + ///////////////////// + // container types // + ///////////////////// + + /// @name container types + /// The canonic container types to use @ref basic_json like any other STL + /// container. + /// @{ + + /// the type of elements in a basic_json container + using value_type = basic_json; + + /// the type of an element reference + using reference = value_type&; + /// the type of an element const reference + using const_reference = const value_type&; + + /// a type to represent differences between iterators + using difference_type = std::ptrdiff_t; + /// a type to represent container sizes + using size_type = std::size_t; + + /// the allocator type + using allocator_type = AllocatorType; + + /// the type of an element pointer + using pointer = typename std::allocator_traits::pointer; + /// the type of an element const pointer + using const_pointer = typename std::allocator_traits::const_pointer; + + /// an iterator for a basic_json container + using iterator = iter_impl; + /// a const iterator for a basic_json container + using const_iterator = iter_impl; + /// a reverse iterator for a basic_json container + using reverse_iterator = json_reverse_iterator; + /// a const reverse iterator for a basic_json container + using const_reverse_iterator = json_reverse_iterator; + + /// @} + + + /*! + @brief returns the allocator associated with the container + */ + static allocator_type get_allocator() + { + return allocator_type(); + } + + /*! + @brief returns version information on the library + + This function returns a JSON object with information about the library, + including the version number and information on the platform and compiler. + + @return JSON object holding version information + key | description + ----------- | --------------- + `compiler` | Information on the used compiler. It is an object with the following keys: `c++` (the used C++ standard), `family` (the compiler family; possible values are `clang`, `icc`, `gcc`, `ilecpp`, `msvc`, `pgcpp`, `sunpro`, and `unknown`), and `version` (the compiler version). + `copyright` | The copyright line for the library as string. + `name` | The name of the library as string. + `platform` | The used platform as string. Possible values are `win32`, `linux`, `apple`, `unix`, and `unknown`. + `url` | The URL of the project as string. + `version` | The version of the library. It is an object with the following keys: `major`, `minor`, and `patch` as defined by [Semantic Versioning](http://semver.org), and `string` (the version string). + + @liveexample{The following code shows an example output of the `meta()` + function.,meta} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @complexity Constant. + + @since 2.1.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json meta() + { + basic_json result; + + result["copyright"] = "(C) 2013-2021 Niels Lohmann"; + result["name"] = "JSON for Modern C++"; + result["url"] = "https://github.com/nlohmann/json"; + result["version"]["string"] = + std::to_string(NLOHMANN_JSON_VERSION_MAJOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_MINOR) + "." + + std::to_string(NLOHMANN_JSON_VERSION_PATCH); + result["version"]["major"] = NLOHMANN_JSON_VERSION_MAJOR; + result["version"]["minor"] = NLOHMANN_JSON_VERSION_MINOR; + result["version"]["patch"] = NLOHMANN_JSON_VERSION_PATCH; + +#ifdef _WIN32 + result["platform"] = "win32"; +#elif defined __linux__ + result["platform"] = "linux"; +#elif defined __APPLE__ + result["platform"] = "apple"; +#elif defined __unix__ + result["platform"] = "unix"; +#else + result["platform"] = "unknown"; +#endif + +#if defined(__ICC) || defined(__INTEL_COMPILER) + result["compiler"] = {{"family", "icc"}, {"version", __INTEL_COMPILER}}; +#elif defined(__clang__) + result["compiler"] = {{"family", "clang"}, {"version", __clang_version__}}; +#elif defined(__GNUC__) || defined(__GNUG__) + result["compiler"] = {{"family", "gcc"}, {"version", std::to_string(__GNUC__) + "." + std::to_string(__GNUC_MINOR__) + "." + std::to_string(__GNUC_PATCHLEVEL__)}}; +#elif defined(__HP_cc) || defined(__HP_aCC) + result["compiler"] = "hp" +#elif defined(__IBMCPP__) + result["compiler"] = {{"family", "ilecpp"}, {"version", __IBMCPP__}}; +#elif defined(_MSC_VER) + result["compiler"] = {{"family", "msvc"}, {"version", _MSC_VER}}; +#elif defined(__PGI) + result["compiler"] = {{"family", "pgcpp"}, {"version", __PGI}}; +#elif defined(__SUNPRO_CC) + result["compiler"] = {{"family", "sunpro"}, {"version", __SUNPRO_CC}}; +#else + result["compiler"] = {{"family", "unknown"}, {"version", "unknown"}}; +#endif + +#ifdef __cplusplus + result["compiler"]["c++"] = std::to_string(__cplusplus); +#else + result["compiler"]["c++"] = "unknown"; +#endif + return result; + } + + + /////////////////////////// + // JSON value data types // + /////////////////////////// + + /// @name JSON value data types + /// The data types to store a JSON value. These types are derived from + /// the template arguments passed to class @ref basic_json. + /// @{ + +#if defined(JSON_HAS_CPP_14) + // Use transparent comparator if possible, combined with perfect forwarding + // on find() and count() calls prevents unnecessary string construction. + using object_comparator_t = std::less<>; +#else + using object_comparator_t = std::less; +#endif + + /*! + @brief a type for an object + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON objects as follows: + > An object is an unordered collection of zero or more name/value pairs, + > where a name is a string and a value is a string, number, boolean, null, + > object, or array. + + To store objects in C++, a type is defined by the template parameters + described below. + + @tparam ObjectType the container to store objects (e.g., `std::map` or + `std::unordered_map`) + @tparam StringType the type of the keys or names (e.g., `std::string`). + The comparison function `std::less` is used to order elements + inside the container. + @tparam AllocatorType the allocator to use for objects (e.g., + `std::allocator`) + + #### Default type + + With the default values for @a ObjectType (`std::map`), @a StringType + (`std::string`), and @a AllocatorType (`std::allocator`), the default + value for @a object_t is: + + @code {.cpp} + std::map< + std::string, // key_type + basic_json, // value_type + std::less, // key_compare + std::allocator> // allocator_type + > + @endcode + + #### Behavior + + The choice of @a object_t influences the behavior of the JSON class. With + the default type, objects have the following behavior: + + - When all names are unique, objects will be interoperable in the sense + that all software implementations receiving that object will agree on + the name-value mappings. + - When the names within an object are not unique, it is unspecified which + one of the values for a given key will be chosen. For instance, + `{"key": 2, "key": 1}` could be equal to either `{"key": 1}` or + `{"key": 2}`. + - Internally, name/value pairs are stored in lexicographical order of the + names. Objects will also be serialized (see @ref dump) in this order. + For instance, `{"b": 1, "a": 2}` and `{"a": 2, "b": 1}` will be stored + and serialized as `{"a": 2, "b": 1}`. + - When comparing objects, the order of the name/value pairs is irrelevant. + This makes objects interoperable in the sense that they will not be + affected by these differences. For instance, `{"b": 1, "a": 2}` and + `{"a": 2, "b": 1}` will be treated as equal. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the object's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON object. + + #### Storage + + Objects are stored as pointers in a @ref basic_json type. That is, for any + access to object values, a pointer of type `object_t*` must be + dereferenced. + + @sa see @ref array_t -- type for an array value + + @since version 1.0.0 + + @note The order name/value pairs are added to the object is *not* + preserved by the library. Therefore, iterating an object may return + name/value pairs in a different order than they were originally stored. In + fact, keys will be traversed in alphabetical order as `std::map` with + `std::less` is used by default. Please note this behavior conforms to [RFC + 8259](https://tools.ietf.org/html/rfc8259), because any order implements the + specified "unordered" nature of JSON objects. + */ + using object_t = ObjectType>>; + + /*! + @brief a type for an array + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON arrays as follows: + > An array is an ordered sequence of zero or more values. + + To store objects in C++, a type is defined by the template parameters + explained below. + + @tparam ArrayType container type to store arrays (e.g., `std::vector` or + `std::list`) + @tparam AllocatorType allocator to use for arrays (e.g., `std::allocator`) + + #### Default type + + With the default values for @a ArrayType (`std::vector`) and @a + AllocatorType (`std::allocator`), the default value for @a array_t is: + + @code {.cpp} + std::vector< + basic_json, // value_type + std::allocator // allocator_type + > + @endcode + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the maximum depth of nesting. + + In this class, the array's limit of nesting is not explicitly constrained. + However, a maximum depth of nesting may be introduced by the compiler or + runtime environment. A theoretical limit can be queried by calling the + @ref max_size function of a JSON array. + + #### Storage + + Arrays are stored as pointers in a @ref basic_json type. That is, for any + access to array values, a pointer of type `array_t*` must be dereferenced. + + @sa see @ref object_t -- type for an object value + + @since version 1.0.0 + */ + using array_t = ArrayType>; + + /*! + @brief a type for a string + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes JSON strings as follows: + > A string is a sequence of zero or more Unicode characters. + + To store objects in C++, a type is defined by the template parameter + described below. Unicode values are split by the JSON class into + byte-sized characters during deserialization. + + @tparam StringType the container to store strings (e.g., `std::string`). + Note this container is used for keys/names in objects, see @ref object_t. + + #### Default type + + With the default values for @a StringType (`std::string`), the default + value for @a string_t is: + + @code {.cpp} + std::string + @endcode + + #### Encoding + + Strings are stored in UTF-8 encoding. Therefore, functions like + `std::string::size()` or `std::string::length()` return the number of + bytes in the string rather than the number of characters or glyphs. + + #### String comparison + + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: + > Software implementations are typically required to test names of object + > members for equality. Implementations that transform the textual + > representation into sequences of Unicode code units and then perform the + > comparison numerically, code unit by code unit, are interoperable in the + > sense that implementations will agree in all cases on equality or + > inequality of two strings. For example, implementations that compare + > strings with escaped characters unconverted may incorrectly find that + > `"a\\b"` and `"a\u005Cb"` are not equal. + + This implementation is interoperable as it does compare strings code unit + by code unit. + + #### Storage + + String values are stored as pointers in a @ref basic_json type. That is, + for any access to string values, a pointer of type `string_t*` must be + dereferenced. + + @since version 1.0.0 + */ + using string_t = StringType; + + /*! + @brief a type for a boolean + + [RFC 8259](https://tools.ietf.org/html/rfc8259) implicitly describes a boolean as a + type which differentiates the two literals `true` and `false`. + + To store objects in C++, a type is defined by the template parameter @a + BooleanType which chooses the type to use. + + #### Default type + + With the default values for @a BooleanType (`bool`), the default value for + @a boolean_t is: + + @code {.cpp} + bool + @endcode + + #### Storage + + Boolean values are stored directly inside a @ref basic_json type. + + @since version 1.0.0 + */ + using boolean_t = BooleanType; + + /*! + @brief a type for a number (integer) + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store integer numbers in C++, a type is defined by the template + parameter @a NumberIntegerType which chooses the type to use. + + #### Default type + + With the default values for @a NumberIntegerType (`int64_t`), the default + value for @a number_integer_t is: + + @code {.cpp} + int64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `9223372036854775807` (INT64_MAX) and the minimal integer number + that can be stored is `-9223372036854775808` (INT64_MIN). Integer numbers + that are out of range will yield over/underflow when used in a + constructor. During deserialization, too large or small integer numbers + will be automatically be stored as @ref number_unsigned_t or @ref + number_float_t. + + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange of the exactly supported range [INT64_MIN, + INT64_MAX], this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa see @ref number_float_t -- type for number values (floating-point) + + @sa see @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_integer_t = NumberIntegerType; + + /*! + @brief a type for a number (unsigned) + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store unsigned integer numbers in C++, a type is defined by the + template parameter @a NumberUnsignedType which chooses the type to use. + + #### Default type + + With the default values for @a NumberUnsignedType (`uint64_t`), the + default value for @a number_unsigned_t is: + + @code {.cpp} + uint64_t + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in integer literals lead to an interpretation as octal + number. Internally, the value will be stored as decimal number. For + instance, the C++ integer literal `010` will be serialized to `8`. + During deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) specifies: + > An implementation may set limits on the range and precision of numbers. + + When the default type is used, the maximal integer number that can be + stored is `18446744073709551615` (UINT64_MAX) and the minimal integer + number that can be stored is `0`. Integer numbers that are out of range + will yield over/underflow when used in a constructor. During + deserialization, too large or small integer numbers will be automatically + be stored as @ref number_integer_t or @ref number_float_t. + + [RFC 8259](https://tools.ietf.org/html/rfc8259) further states: + > Note that when such software is used, numbers that are integers and are + > in the range \f$[-2^{53}+1, 2^{53}-1]\f$ are interoperable in the sense + > that implementations will agree exactly on their numeric values. + + As this range is a subrange (when considered in conjunction with the + number_integer_t type) of the exactly supported range [0, UINT64_MAX], + this class's integer type is interoperable. + + #### Storage + + Integer number values are stored directly inside a @ref basic_json type. + + @sa see @ref number_float_t -- type for number values (floating-point) + @sa see @ref number_integer_t -- type for number values (integer) + + @since version 2.0.0 + */ + using number_unsigned_t = NumberUnsignedType; + + /*! + @brief a type for a number (floating-point) + + [RFC 8259](https://tools.ietf.org/html/rfc8259) describes numbers as follows: + > The representation of numbers is similar to that used in most + > programming languages. A number is represented in base 10 using decimal + > digits. It contains an integer component that may be prefixed with an + > optional minus sign, which may be followed by a fraction part and/or an + > exponent part. Leading zeros are not allowed. (...) Numeric values that + > cannot be represented in the grammar below (such as Infinity and NaN) + > are not permitted. + + This description includes both integer and floating-point numbers. + However, C++ allows more precise storage if it is known whether the number + is a signed integer, an unsigned integer or a floating-point number. + Therefore, three different types, @ref number_integer_t, @ref + number_unsigned_t and @ref number_float_t are used. + + To store floating-point numbers in C++, a type is defined by the template + parameter @a NumberFloatType which chooses the type to use. + + #### Default type + + With the default values for @a NumberFloatType (`double`), the default + value for @a number_float_t is: + + @code {.cpp} + double + @endcode + + #### Default behavior + + - The restrictions about leading zeros is not enforced in C++. Instead, + leading zeros in floating-point literals will be ignored. Internally, + the value will be stored as decimal number. For instance, the C++ + floating-point literal `01.2` will be serialized to `1.2`. During + deserialization, leading zeros yield an error. + - Not-a-number (NaN) values will be serialized to `null`. + + #### Limits + + [RFC 8259](https://tools.ietf.org/html/rfc8259) states: + > This specification allows implementations to set limits on the range and + > precision of numbers accepted. Since software that implements IEEE + > 754-2008 binary64 (double precision) numbers is generally available and + > widely used, good interoperability can be achieved by implementations + > that expect no more precision or range than these provide, in the sense + > that implementations will approximate JSON numbers within the expected + > precision. + + This implementation does exactly follow this approach, as it uses double + precision floating-point numbers. Note values smaller than + `-1.79769313486232e+308` and values greater than `1.79769313486232e+308` + will be stored as NaN internally and be serialized to `null`. + + #### Storage + + Floating-point number values are stored directly inside a @ref basic_json + type. + + @sa see @ref number_integer_t -- type for number values (integer) + + @sa see @ref number_unsigned_t -- type for number values (unsigned integer) + + @since version 1.0.0 + */ + using number_float_t = NumberFloatType; + + /*! + @brief a type for a packed binary type + + This type is a type designed to carry binary data that appears in various + serialized formats, such as CBOR's Major Type 2, MessagePack's bin, and + BSON's generic binary subtype. This type is NOT a part of standard JSON and + exists solely for compatibility with these binary types. As such, it is + simply defined as an ordered sequence of zero or more byte values. + + Additionally, as an implementation detail, the subtype of the binary data is + carried around as a `std::uint8_t`, which is compatible with both of the + binary data formats that use binary subtyping, (though the specific + numbering is incompatible with each other, and it is up to the user to + translate between them). + + [CBOR's RFC 7049](https://tools.ietf.org/html/rfc7049) describes this type + as: + > Major type 2: a byte string. The string's length in bytes is represented + > following the rules for positive integers (major type 0). + + [MessagePack's documentation on the bin type + family](https://github.com/msgpack/msgpack/blob/master/spec.md#bin-format-family) + describes this type as: + > Bin format family stores an byte array in 2, 3, or 5 bytes of extra bytes + > in addition to the size of the byte array. + + [BSON's specifications](http://bsonspec.org/spec.html) describe several + binary types; however, this type is intended to represent the generic binary + type which has the description: + > Generic binary subtype - This is the most commonly used binary subtype and + > should be the 'default' for drivers and tools. + + None of these impose any limitations on the internal representation other + than the basic unit of storage be some type of array whose parts are + decomposable into bytes. + + The default representation of this binary format is a + `std::vector`, which is a very common way to represent a byte + array in modern C++. + + #### Default type + + The default values for @a BinaryType is `std::vector` + + #### Storage + + Binary Arrays are stored as pointers in a @ref basic_json type. That is, + for any access to array values, a pointer of the type `binary_t*` must be + dereferenced. + + #### Notes on subtypes + + - CBOR + - Binary values are represented as byte strings. Subtypes are serialized + as tagged values. + - MessagePack + - If a subtype is given and the binary array contains exactly 1, 2, 4, 8, + or 16 elements, the fixext family (fixext1, fixext2, fixext4, fixext8) + is used. For other sizes, the ext family (ext8, ext16, ext32) is used. + The subtype is then added as singed 8-bit integer. + - If no subtype is given, the bin family (bin8, bin16, bin32) is used. + - BSON + - If a subtype is given, it is used and added as unsigned 8-bit integer. + - If no subtype is given, the generic binary subtype 0x00 is used. + + @sa see @ref binary -- create a binary array + + @since version 3.8.0 + */ + using binary_t = nlohmann::byte_container_with_subtype; + /// @} + + private: + + /// helper for exception-safe object creation + template + JSON_HEDLEY_RETURNS_NON_NULL + static T* create(Args&& ... args) + { + AllocatorType alloc; + using AllocatorTraits = std::allocator_traits>; + + auto deleter = [&](T * obj) + { + AllocatorTraits::deallocate(alloc, obj, 1); + }; + std::unique_ptr obj(AllocatorTraits::allocate(alloc, 1), deleter); + AllocatorTraits::construct(alloc, obj.get(), std::forward(args)...); + JSON_ASSERT(obj != nullptr); + return obj.release(); + } + + //////////////////////// + // JSON value storage // + //////////////////////// + + JSON_PRIVATE_UNLESS_TESTED: + /*! + @brief a JSON value + + The actual storage for a JSON value of the @ref basic_json class. This + union combines the different storage types for the JSON value types + defined in @ref value_t. + + JSON type | value_t type | used type + --------- | --------------- | ------------------------ + object | object | pointer to @ref object_t + array | array | pointer to @ref array_t + string | string | pointer to @ref string_t + boolean | boolean | @ref boolean_t + number | number_integer | @ref number_integer_t + number | number_unsigned | @ref number_unsigned_t + number | number_float | @ref number_float_t + binary | binary | pointer to @ref binary_t + null | null | *no value is stored* + + @note Variable-length types (objects, arrays, and strings) are stored as + pointers. The size of the union should not exceed 64 bits if the default + value types are used. + + @since version 1.0.0 + */ + union json_value + { + /// object (stored with pointer to save storage) + object_t* object; + /// array (stored with pointer to save storage) + array_t* array; + /// string (stored with pointer to save storage) + string_t* string; + /// binary (stored with pointer to save storage) + binary_t* binary; + /// boolean + boolean_t boolean; + /// number (integer) + number_integer_t number_integer; + /// number (unsigned integer) + number_unsigned_t number_unsigned; + /// number (floating-point) + number_float_t number_float; + + /// default constructor (for null values) + json_value() = default; + /// constructor for booleans + json_value(boolean_t v) noexcept : boolean(v) {} + /// constructor for numbers (integer) + json_value(number_integer_t v) noexcept : number_integer(v) {} + /// constructor for numbers (unsigned) + json_value(number_unsigned_t v) noexcept : number_unsigned(v) {} + /// constructor for numbers (floating-point) + json_value(number_float_t v) noexcept : number_float(v) {} + /// constructor for empty values of a given type + json_value(value_t t) + { + switch (t) + { + case value_t::object: + { + object = create(); + break; + } + + case value_t::array: + { + array = create(); + break; + } + + case value_t::string: + { + string = create(""); + break; + } + + case value_t::binary: + { + binary = create(); + break; + } + + case value_t::boolean: + { + boolean = boolean_t(false); + break; + } + + case value_t::number_integer: + { + number_integer = number_integer_t(0); + break; + } + + case value_t::number_unsigned: + { + number_unsigned = number_unsigned_t(0); + break; + } + + case value_t::number_float: + { + number_float = number_float_t(0.0); + break; + } + + case value_t::null: + { + object = nullptr; // silence warning, see #821 + break; + } + + case value_t::discarded: + default: + { + object = nullptr; // silence warning, see #821 + if (JSON_HEDLEY_UNLIKELY(t == value_t::null)) + { + JSON_THROW(other_error::create(500, "961c151d2e87f2686a955a9be24d316f1362bf21 3.10.2", basic_json())); // LCOV_EXCL_LINE + } + break; + } + } + } + + /// constructor for strings + json_value(const string_t& value) + { + string = create(value); + } + + /// constructor for rvalue strings + json_value(string_t&& value) + { + string = create(std::move(value)); + } + + /// constructor for objects + json_value(const object_t& value) + { + object = create(value); + } + + /// constructor for rvalue objects + json_value(object_t&& value) + { + object = create(std::move(value)); + } + + /// constructor for arrays + json_value(const array_t& value) + { + array = create(value); + } + + /// constructor for rvalue arrays + json_value(array_t&& value) + { + array = create(std::move(value)); + } + + /// constructor for binary arrays + json_value(const typename binary_t::container_type& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays + json_value(typename binary_t::container_type&& value) + { + binary = create(std::move(value)); + } + + /// constructor for binary arrays (internal type) + json_value(const binary_t& value) + { + binary = create(value); + } + + /// constructor for rvalue binary arrays (internal type) + json_value(binary_t&& value) + { + binary = create(std::move(value)); + } + + void destroy(value_t t) + { + if (t == value_t::array || t == value_t::object) + { + // flatten the current json_value to a heap-allocated stack + std::vector stack; + + // move the top-level items to stack + if (t == value_t::array) + { + stack.reserve(array->size()); + std::move(array->begin(), array->end(), std::back_inserter(stack)); + } + else + { + stack.reserve(object->size()); + for (auto&& it : *object) + { + stack.push_back(std::move(it.second)); + } + } + + while (!stack.empty()) + { + // move the last item to local variable to be processed + basic_json current_item(std::move(stack.back())); + stack.pop_back(); + + // if current_item is array/object, move + // its children to the stack to be processed later + if (current_item.is_array()) + { + std::move(current_item.m_value.array->begin(), current_item.m_value.array->end(), std::back_inserter(stack)); + + current_item.m_value.array->clear(); + } + else if (current_item.is_object()) + { + for (auto&& it : *current_item.m_value.object) + { + stack.push_back(std::move(it.second)); + } + + current_item.m_value.object->clear(); + } + + // it's now safe that current_item get destructed + // since it doesn't have any children + } + } + + switch (t) + { + case value_t::object: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, object); + std::allocator_traits::deallocate(alloc, object, 1); + break; + } + + case value_t::array: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, array); + std::allocator_traits::deallocate(alloc, array, 1); + break; + } + + case value_t::string: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, string); + std::allocator_traits::deallocate(alloc, string, 1); + break; + } + + case value_t::binary: + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, binary); + std::allocator_traits::deallocate(alloc, binary, 1); + break; + } + + case value_t::null: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::discarded: + default: + { + break; + } + } + } + }; + + private: + /*! + @brief checks the class invariants + + This function asserts the class invariants. It needs to be called at the + end of every constructor to make sure that created objects respect the + invariant. Furthermore, it has to be called each time the type of a JSON + value is changed, because the invariant expresses a relationship between + @a m_type and @a m_value. + + Furthermore, the parent relation is checked for arrays and objects: If + @a check_parents true and the value is an array or object, then the + container's elements must have the current value as parent. + + @param[in] check_parents whether the parent relation should be checked. + The value is true by default and should only be set to false + during destruction of objects when the invariant does not + need to hold. + */ + void assert_invariant(bool check_parents = true) const noexcept + { + JSON_ASSERT(m_type != value_t::object || m_value.object != nullptr); + JSON_ASSERT(m_type != value_t::array || m_value.array != nullptr); + JSON_ASSERT(m_type != value_t::string || m_value.string != nullptr); + JSON_ASSERT(m_type != value_t::binary || m_value.binary != nullptr); + +#if JSON_DIAGNOSTICS + JSON_TRY + { + // cppcheck-suppress assertWithSideEffect + JSON_ASSERT(!check_parents || !is_structured() || std::all_of(begin(), end(), [this](const basic_json & j) + { + return j.m_parent == this; + })); + } + JSON_CATCH(...) {} // LCOV_EXCL_LINE +#endif + static_cast(check_parents); + } + + void set_parents() + { +#if JSON_DIAGNOSTICS + switch (m_type) + { + case value_t::array: + { + for (auto& element : *m_value.array) + { + element.m_parent = this; + } + break; + } + + case value_t::object: + { + for (auto& element : *m_value.object) + { + element.second.m_parent = this; + } + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + break; + } +#endif + } + + iterator set_parents(iterator it, typename iterator::difference_type count) + { +#if JSON_DIAGNOSTICS + for (typename iterator::difference_type i = 0; i < count; ++i) + { + (it + i)->m_parent = this; + } +#else + static_cast(count); +#endif + return it; + } + + reference set_parent(reference j, std::size_t old_capacity = std::size_t(-1)) + { +#if JSON_DIAGNOSTICS + if (old_capacity != std::size_t(-1)) + { + // see https://github.com/nlohmann/json/issues/2838 + JSON_ASSERT(type() == value_t::array); + if (JSON_HEDLEY_UNLIKELY(m_value.array->capacity() != old_capacity)) + { + // capacity has changed: update all parents + set_parents(); + return j; + } + } + + // ordered_json uses a vector internally, so pointers could have + // been invalidated; see https://github.com/nlohmann/json/issues/2962 +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning(push ) +#pragma warning(disable : 4127) // ignore warning to replace if with if constexpr +#endif + if (detail::is_ordered_map::value) + { + set_parents(); + return j; + } +#ifdef JSON_HEDLEY_MSVC_VERSION +#pragma warning( pop ) +#endif + + j.m_parent = this; +#else + static_cast(j); + static_cast(old_capacity); +#endif + return j; + } + + public: + ////////////////////////// + // JSON parser callback // + ////////////////////////// + + /*! + @brief parser event types + + The parser callback distinguishes the following events: + - `object_start`: the parser read `{` and started to process a JSON object + - `key`: the parser read a key of a value in an object + - `object_end`: the parser read `}` and finished processing a JSON object + - `array_start`: the parser read `[` and started to process a JSON array + - `array_end`: the parser read `]` and finished processing a JSON array + - `value`: the parser finished reading a JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + @sa see @ref parser_callback_t for more information and examples + */ + using parse_event_t = detail::parse_event_t; + + /*! + @brief per-element parser callback type + + With a parser callback function, the result of parsing a JSON text can be + influenced. When passed to @ref parse, it is called on certain events + (passed as @ref parse_event_t via parameter @a event) with a set recursion + depth @a depth and context JSON value @a parsed. The return value of the + callback function is a boolean indicating whether the element that emitted + the callback shall be kept or not. + + We distinguish six scenarios (determined by the event type) in which the + callback function can be called. The following table describes the values + of the parameters @a depth, @a event, and @a parsed. + + parameter @a event | description | parameter @a depth | parameter @a parsed + ------------------ | ----------- | ------------------ | ------------------- + parse_event_t::object_start | the parser read `{` and started to process a JSON object | depth of the parent of the JSON object | a JSON value with type discarded + parse_event_t::key | the parser read a key of a value in an object | depth of the currently parsed JSON object | a JSON string containing the key + parse_event_t::object_end | the parser read `}` and finished processing a JSON object | depth of the parent of the JSON object | the parsed JSON object + parse_event_t::array_start | the parser read `[` and started to process a JSON array | depth of the parent of the JSON array | a JSON value with type discarded + parse_event_t::array_end | the parser read `]` and finished processing a JSON array | depth of the parent of the JSON array | the parsed JSON array + parse_event_t::value | the parser finished reading a JSON value | depth of the value | the parsed JSON value + + @image html callback_events.png "Example when certain parse events are triggered" + + Discarding a value (i.e., returning `false`) has different effects + depending on the context in which function was called: + + - Discarded values in structured types are skipped. That is, the parser + will behave as if the discarded value was never read. + - In case a value outside a structured type is skipped, it is replaced + with `null`. This case happens if the top-level element is skipped. + + @param[in] depth the depth of the recursion during parsing + + @param[in] event an event of type parse_event_t indicating the context in + the callback function has been called + + @param[in,out] parsed the current intermediate parse result; note that + writing to this value has no effect for parse_event_t::key events + + @return Whether the JSON value which called the function during parsing + should be kept (`true`) or not (`false`). In the latter case, it is either + skipped completely or replaced by an empty discarded object. + + @sa see @ref parse for examples + + @since version 1.0.0 + */ + using parser_callback_t = detail::parser_callback_t; + + ////////////////// + // constructors // + ////////////////// + + /// @name constructors and destructors + /// Constructors of class @ref basic_json, copy/move constructor, copy + /// assignment, static functions creating objects, and the destructor. + /// @{ + + /*! + @brief create an empty value with a given type + + Create an empty JSON value with a given type. The value will be default + initialized with an empty value which depends on the type: + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + object | `{}` + array | `[]` + binary | empty array + + @param[in] v the type of the value to create + + @complexity Constant. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows the constructor for different @ref + value_t values,basic_json__value_t} + + @sa see @ref clear() -- restores the postcondition of this constructor + + @since version 1.0.0 + */ + basic_json(const value_t v) + : m_type(v), m_value(v) + { + assert_invariant(); + } + + /*! + @brief create a null object + + Create a `null` JSON value. It either takes a null pointer as parameter + (explicitly creating `null`) or no parameter (implicitly creating `null`). + The passed null pointer itself is not read -- it is only used to choose + the right constructor. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @liveexample{The following code shows the constructor with and without a + null pointer parameter.,basic_json__nullptr_t} + + @since version 1.0.0 + */ + basic_json(std::nullptr_t = nullptr) noexcept + : basic_json(value_t::null) + { + assert_invariant(); + } + + /*! + @brief create a JSON value + + This is a "catch all" constructor for all compatible JSON types; that is, + types for which a `to_json()` method exists. The constructor forwards the + parameter @a val to that method (to `json_serializer::to_json` method + with `U = uncvref_t`, to be exact). + + Template type @a CompatibleType includes, but is not limited to, the + following types: + - **arrays**: @ref array_t and all kinds of compatible containers such as + `std::vector`, `std::deque`, `std::list`, `std::forward_list`, + `std::array`, `std::valarray`, `std::set`, `std::unordered_set`, + `std::multiset`, and `std::unordered_multiset` with a `value_type` from + which a @ref basic_json value can be constructed. + - **objects**: @ref object_t and all kinds of compatible associative + containers such as `std::map`, `std::unordered_map`, `std::multimap`, + and `std::unordered_multimap` with a `key_type` compatible to + @ref string_t and a `value_type` from which a @ref basic_json value can + be constructed. + - **strings**: @ref string_t, string literals, and all compatible string + containers can be used. + - **numbers**: @ref number_integer_t, @ref number_unsigned_t, + @ref number_float_t, and all convertible number types such as `int`, + `size_t`, `int64_t`, `float` or `double` can be used. + - **boolean**: @ref boolean_t / `bool` can be used. + - **binary**: @ref binary_t / `std::vector` may be used, + unfortunately because string literals cannot be distinguished from binary + character arrays by the C++ type system, all types compatible with `const + char*` will be directed to the string constructor instead. This is both + for backwards compatibility, and due to the fact that a binary type is not + a standard JSON type. + + See the examples below. + + @tparam CompatibleType a type such that: + - @a CompatibleType is not derived from `std::istream`, + - @a CompatibleType is not @ref basic_json (to avoid hijacking copy/move + constructors), + - @a CompatibleType is not a different @ref basic_json type (i.e. with different template arguments) + - @a CompatibleType is not a @ref basic_json nested type (e.g., + @ref json_pointer, @ref iterator, etc ...) + - `json_serializer` has a `to_json(basic_json_t&, CompatibleType&&)` method + + @tparam U = `uncvref_t` + + @param[in] val the value to be forwarded to the respective constructor + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @liveexample{The following code shows the constructor with several + compatible types.,basic_json__CompatibleType} + + @since version 2.1.0 + */ + template < typename CompatibleType, + typename U = detail::uncvref_t, + detail::enable_if_t < + !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > + basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) + { + JSONSerializer::to_json(*this, std::forward(val)); + set_parents(); + assert_invariant(); + } + + /*! + @brief create a JSON value from an existing one + + This is a constructor for existing @ref basic_json types. + It does not hijack copy/move constructors, since the parameter has different + template arguments than the current ones. + + The constructor tries to convert the internal @ref m_value of the parameter. + + @tparam BasicJsonType a type such that: + - @a BasicJsonType is a @ref basic_json type. + - @a BasicJsonType has different template arguments than @ref basic_json_t. + + @param[in] val the @ref basic_json value to be converted. + + @complexity Usually linear in the size of the passed @a val, also + depending on the implementation of the called `to_json()` + method. + + @exceptionsafety Depends on the called constructor. For types directly + supported by the library (i.e., all types for which no `to_json()` function + was provided), strong guarantee holds: if an exception is thrown, there are + no changes to any JSON value. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value&& !std::is_same::value, int > = 0 > + basic_json(const BasicJsonType& val) + { + using other_boolean_t = typename BasicJsonType::boolean_t; + using other_number_float_t = typename BasicJsonType::number_float_t; + using other_number_integer_t = typename BasicJsonType::number_integer_t; + using other_number_unsigned_t = typename BasicJsonType::number_unsigned_t; + using other_string_t = typename BasicJsonType::string_t; + using other_object_t = typename BasicJsonType::object_t; + using other_array_t = typename BasicJsonType::array_t; + using other_binary_t = typename BasicJsonType::binary_t; + + switch (val.type()) + { + case value_t::boolean: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_float: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_integer: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::number_unsigned: + JSONSerializer::to_json(*this, val.template get()); + break; + case value_t::string: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::object: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::array: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::binary: + JSONSerializer::to_json(*this, val.template get_ref()); + break; + case value_t::null: + *this = nullptr; + break; + case value_t::discarded: + m_type = value_t::discarded; + break; + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + set_parents(); + assert_invariant(); + } + + /*! + @brief create a container (array or object) from an initializer list + + Creates a JSON value of type array or object from the passed initializer + list @a init. In case @a type_deduction is `true` (default), the type of + the JSON value to be created is deducted from the initializer list @a init + according to the following rules: + + 1. If the list is empty, an empty JSON object value `{}` is created. + 2. If the list consists of pairs whose first element is a string, a JSON + object value is created where the first elements of the pairs are + treated as keys and the second elements are as values. + 3. In all other cases, an array is created. + + The rules aim to create the best fit between a C++ initializer list and + JSON values. The rationale is as follows: + + 1. The empty initializer list is written as `{}` which is exactly an empty + JSON object. + 2. C++ has no way of describing mapped types other than to list a list of + pairs. As JSON requires that keys must be of type string, rule 2 is the + weakest constraint one can pose on initializer lists to interpret them + as an object. + 3. In all other cases, the initializer list could not be interpreted as + JSON object type, so interpreting it as JSON array type is safe. + + With the rules described above, the following JSON values cannot be + expressed by an initializer list: + + - the empty array (`[]`): use @ref array(initializer_list_t) + with an empty initializer list in this case + - arrays whose elements satisfy rule 2: use @ref + array(initializer_list_t) with the same initializer list + in this case + + @note When used without parentheses around an empty initializer list, @ref + basic_json() is called instead of this function, yielding the JSON null + value. + + @param[in] init initializer list with JSON values + + @param[in] type_deduction internal parameter; when set to `true`, the type + of the JSON value is deducted from the initializer list @a init; when set + to `false`, the type provided via @a manual_type is forced. This mode is + used by the functions @ref array(initializer_list_t) and + @ref object(initializer_list_t). + + @param[in] manual_type internal parameter; when @a type_deduction is set + to `false`, the created JSON value will use the provided type (only @ref + value_t::array and @ref value_t::object are valid); when @a type_deduction + is set to `true`, this parameter has no effect + + @throw type_error.301 if @a type_deduction is `false`, @a manual_type is + `value_t::object`, but @a init contains an element which is not a pair + whose first element is a string. In this case, the constructor could not + create an object. If @a type_deduction would have be `true`, an array + would have been created. See @ref object(initializer_list_t) + for an example. + + @complexity Linear in the size of the initializer list @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows how JSON values are created from + initializer lists.,basic_json__list_init_t} + + @sa see @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + @sa see @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + basic_json(initializer_list_t init, + bool type_deduction = true, + value_t manual_type = value_t::array) + { + // check if each element is an array with two elements whose first + // element is a string + bool is_an_object = std::all_of(init.begin(), init.end(), + [](const detail::json_ref& element_ref) + { + return element_ref->is_array() && element_ref->size() == 2 && (*element_ref)[0].is_string(); + }); + + // adjust type if type deduction is not wanted + if (!type_deduction) + { + // if array is wanted, do not create an object though possible + if (manual_type == value_t::array) + { + is_an_object = false; + } + + // if object is wanted but impossible, throw an exception + if (JSON_HEDLEY_UNLIKELY(manual_type == value_t::object && !is_an_object)) + { + JSON_THROW(type_error::create(301, "cannot create object from initializer list", basic_json())); + } + } + + if (is_an_object) + { + // the initializer list is a list of pairs -> create object + m_type = value_t::object; + m_value = value_t::object; + + for (auto& element_ref : init) + { + auto element = element_ref.moved_or_copied(); + m_value.object->emplace( + std::move(*((*element.m_value.array)[0].m_value.string)), + std::move((*element.m_value.array)[1])); + } + } + else + { + // the initializer list describes an array -> create array + m_type = value_t::array; + m_value.array = create(init.begin(), init.end()); + } + + set_parents(); + assert_invariant(); + } + + /*! + @brief explicitly create a binary array (without subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = init; + return res; + } + + /*! + @brief explicitly create a binary array (with subtype) + + Creates a JSON binary array value from a given binary container. Binary + values are part of various binary formats, such as CBOR, MessagePack, and + BSON. This constructor is used to create a value for serialization to those + formats. + + @note Note, this function exists because of the difficulty in correctly + specifying the correct template overload in the standard value ctor, as both + JSON arrays and JSON binary arrays are backed with some form of a + `std::vector`. Because JSON binary arrays are a non-standard extension it + was decided that it would be best to prevent automatic initialization of a + binary array type, for backwards compatibility and so it does not happen on + accident. + + @param[in] init container containing bytes to use as binary type + @param[in] subtype subtype to use in MessagePack and BSON + + @return JSON binary array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @since version 3.8.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(const typename binary_t::container_type& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(init, subtype); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = std::move(init); + return res; + } + + /// @copydoc binary(const typename binary_t::container_type&, typename binary_t::subtype_type) + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json binary(typename binary_t::container_type&& init, typename binary_t::subtype_type subtype) + { + auto res = basic_json(); + res.m_type = value_t::binary; + res.m_value = binary_t(std::move(init), subtype); + return res; + } + + /*! + @brief explicitly create an array from an initializer list + + Creates a JSON array value from a given initializer list. That is, given a + list of values `a, b, c`, creates the JSON value `[a, b, c]`. If the + initializer list is empty, the empty array `[]` is created. + + @note This function is only needed to express two edge cases that cannot + be realized with the initializer list constructor (@ref + basic_json(initializer_list_t, bool, value_t)). These cases + are: + 1. creating an array whose elements are all pairs whose first element is a + string -- in this case, the initializer list constructor would create an + object, taking the first elements as keys + 2. creating an empty array -- passing the empty initializer list to the + initializer list constructor yields an empty object + + @param[in] init initializer list with JSON values to create an array from + (optional) + + @return JSON array value + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `array` + function.,array} + + @sa see @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa see @ref object(initializer_list_t) -- create a JSON object + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json array(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::array); + } + + /*! + @brief explicitly create an object from an initializer list + + Creates a JSON object value from a given initializer list. The initializer + lists elements must be pairs, and their first elements must be strings. If + the initializer list is empty, the empty object `{}` is created. + + @note This function is only added for symmetry reasons. In contrast to the + related function @ref array(initializer_list_t), there are + no cases which can only be expressed by this function. That is, any + initializer list @a init can also be passed to the initializer list + constructor @ref basic_json(initializer_list_t, bool, value_t). + + @param[in] init initializer list to create an object from (optional) + + @return JSON object value + + @throw type_error.301 if @a init is not a list of pairs whose first + elements are strings. In this case, no object can be created. When such a + value is passed to @ref basic_json(initializer_list_t, bool, value_t), + an array would have been created from the passed initializer list @a init. + See example below. + + @complexity Linear in the size of @a init. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows an example for the `object` + function.,object} + + @sa see @ref basic_json(initializer_list_t, bool, value_t) -- + create a JSON value from an initializer list + @sa see @ref array(initializer_list_t) -- create a JSON array + value from an initializer list + + @since version 1.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json object(initializer_list_t init = {}) + { + return basic_json(init, false, value_t::object); + } + + /*! + @brief construct an array with count copies of given value + + Constructs a JSON array value by creating @a cnt copies of a passed value. + In case @a cnt is `0`, an empty array is created. + + @param[in] cnt the number of JSON copies of @a val to create + @param[in] val the JSON value to copy + + @post `std::distance(begin(),end()) == cnt` holds. + + @complexity Linear in @a cnt. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The following code shows examples for the @ref + basic_json(size_type\, const basic_json&) + constructor.,basic_json__size_type_basic_json} + + @since version 1.0.0 + */ + basic_json(size_type cnt, const basic_json& val) + : m_type(value_t::array) + { + m_value.array = create(cnt, val); + set_parents(); + assert_invariant(); + } + + /*! + @brief construct a JSON container given an iterator range + + Constructs the JSON value with the contents of the range `[first, last)`. + The semantics depends on the different types a JSON value can have: + - In case of a null type, invalid_iterator.206 is thrown. + - In case of other primitive types (number, boolean, or string), @a first + must be `begin()` and @a last must be `end()`. In this case, the value is + copied. Otherwise, invalid_iterator.204 is thrown. + - In case of structured types (array, object), the constructor behaves as + similar versions for `std::vector` or `std::map`; that is, a JSON array + or object is constructed from the values in the range. + + @tparam InputIT an input iterator type (@ref iterator or @ref + const_iterator) + + @param[in] first begin of the range to copy from (included) + @param[in] last end of the range to copy from (excluded) + + @pre Iterators @a first and @a last must be initialized. **This + precondition is enforced with an assertion (see warning).** If + assertions are switched off, a violation of this precondition yields + undefined behavior. + + @pre Range `[first, last)` is valid. Usually, this precondition cannot be + checked efficiently. Only certain edge cases are detected; see the + description of the exceptions below. A violation of this precondition + yields undefined behavior. + + @warning A precondition is enforced with a runtime assertion that will + result in calling `std::abort` if this precondition is not met. + Assertions can be disabled by defining `NDEBUG` at compile time. + See https://en.cppreference.com/w/cpp/error/assert for more + information. + + @throw invalid_iterator.201 if iterators @a first and @a last are not + compatible (i.e., do not belong to the same JSON value). In this case, + the range `[first, last)` is undefined. + @throw invalid_iterator.204 if iterators @a first and @a last belong to a + primitive type (number, boolean, or string), but @a first does not point + to the first element any more. In this case, the range `[first, last)` is + undefined. See example code below. + @throw invalid_iterator.206 if iterators @a first and @a last belong to a + null value. In this case, the range `[first, last)` is undefined. + + @complexity Linear in distance between @a first and @a last. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @liveexample{The example below shows several ways to create JSON values by + specifying a subrange with iterators.,basic_json__InputIt_InputIt} + + @since version 1.0.0 + */ + template < class InputIT, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type = 0 > + basic_json(InputIT first, InputIT last) + { + JSON_ASSERT(first.m_object != nullptr); + JSON_ASSERT(last.m_object != nullptr); + + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(201, "iterators are not compatible", basic_json())); + } + + // copy type from first iterator + m_type = first.m_object->m_type; + + // check if iterator range is complete for primitive values + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + { + if (JSON_HEDLEY_UNLIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *first.m_object)); + } + break; + } + + case value_t::null: + case value_t::object: + case value_t::array: + case value_t::binary: + case value_t::discarded: + default: + break; + } + + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = first.m_object->m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = first.m_object->m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value.number_float = first.m_object->m_value.number_float; + break; + } + + case value_t::boolean: + { + m_value.boolean = first.m_object->m_value.boolean; + break; + } + + case value_t::string: + { + m_value = *first.m_object->m_value.string; + break; + } + + case value_t::object: + { + m_value.object = create(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + m_value.array = create(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::binary: + { + m_value = *first.m_object->m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(invalid_iterator::create(206, "cannot construct with iterators from " + std::string(first.m_object->type_name()), *first.m_object)); + } + + set_parents(); + assert_invariant(); + } + + + /////////////////////////////////////// + // other constructors and destructor // + /////////////////////////////////////// + + template, + std::is_same>::value, int> = 0 > + basic_json(const JsonRef& ref) : basic_json(ref.moved_or_copied()) {} + + /*! + @brief copy constructor + + Creates a copy of a given JSON value. + + @param[in] other the JSON value to copy + + @post `*this == other` + + @complexity Linear in the size of @a other. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes to any JSON value. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - As postcondition, it holds: `other == basic_json(other)`. + + @liveexample{The following code shows an example for the copy + constructor.,basic_json__basic_json} + + @since version 1.0.0 + */ + basic_json(const basic_json& other) + : m_type(other.m_type) + { + // check of passed value is valid + other.assert_invariant(); + + switch (m_type) + { + case value_t::object: + { + m_value = *other.m_value.object; + break; + } + + case value_t::array: + { + m_value = *other.m_value.array; + break; + } + + case value_t::string: + { + m_value = *other.m_value.string; + break; + } + + case value_t::boolean: + { + m_value = other.m_value.boolean; + break; + } + + case value_t::number_integer: + { + m_value = other.m_value.number_integer; + break; + } + + case value_t::number_unsigned: + { + m_value = other.m_value.number_unsigned; + break; + } + + case value_t::number_float: + { + m_value = other.m_value.number_float; + break; + } + + case value_t::binary: + { + m_value = *other.m_value.binary; + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + + set_parents(); + assert_invariant(); + } + + /*! + @brief move constructor + + Move constructor. Constructs a JSON value with the contents of the given + value @a other using move semantics. It "steals" the resources from @a + other and leaves it as JSON null value. + + @param[in,out] other value to move to this object + + @post `*this` has the same value as @a other before the call. + @post @a other is a JSON null value. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this constructor never throws + exceptions. + + @requirement This function helps `basic_json` satisfying the + [MoveConstructible](https://en.cppreference.com/w/cpp/named_req/MoveConstructible) + requirements. + + @liveexample{The code below shows the move constructor explicitly called + via std::move.,basic_json__moveconstructor} + + @since version 1.0.0 + */ + basic_json(basic_json&& other) noexcept + : m_type(std::move(other.m_type)), + m_value(std::move(other.m_value)) + { + // check that passed value is valid + other.assert_invariant(false); + + // invalidate payload + other.m_type = value_t::null; + other.m_value = {}; + + set_parents(); + assert_invariant(); + } + + /*! + @brief copy assignment + + Copy assignment operator. Copies a JSON value via the "copy and swap" + strategy: It is expressed in terms of the copy constructor, destructor, + and the `swap()` member function. + + @param[in] other value to copy from + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + + @liveexample{The code below shows and example for the copy assignment. It + creates a copy of value `a` which is then swapped with `b`. Finally\, the + copy of `a` (which is the null value after the swap) is + destroyed.,basic_json__copyassignment} + + @since version 1.0.0 + */ + basic_json& operator=(basic_json other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + // check that passed value is valid + other.assert_invariant(); + + using std::swap; + swap(m_type, other.m_type); + swap(m_value, other.m_value); + + set_parents(); + assert_invariant(); + return *this; + } + + /*! + @brief destructor + + Destroys the JSON value and frees all allocated memory. + + @complexity Linear. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is linear. + - All stored elements are destroyed and all memory is freed. + + @since version 1.0.0 + */ + ~basic_json() noexcept + { + assert_invariant(false); + m_value.destroy(m_type); + } + + /// @} + + public: + /////////////////////// + // object inspection // + /////////////////////// + + /// @name object inspection + /// Functions to inspect the type of a JSON value. + /// @{ + + /*! + @brief serialization + + Serialization function for JSON values. The function tries to mimic + Python's `json.dumps()` function, and currently supports its @a indent + and @a ensure_ascii parameters. + + @param[in] indent If indent is nonnegative, then array elements and object + members will be pretty-printed with that indent level. An indent level of + `0` will only insert newlines. `-1` (the default) selects the most compact + representation. + @param[in] indent_char The character to use for indentation if @a indent is + greater than `0`. The default is ` ` (space). + @param[in] ensure_ascii If @a ensure_ascii is true, all non-ASCII characters + in the output are escaped with `\uXXXX` sequences, and the result consists + of ASCII characters only. + @param[in] error_handler how to react on decoding errors; there are three + possible values: `strict` (throws and exception in case a decoding error + occurs; default), `replace` (replace invalid UTF-8 sequences with U+FFFD), + and `ignore` (ignore invalid UTF-8 sequences during serialization; all + bytes are copied to the output unchanged). + + @return string containing the serialization of the JSON value + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded and @a error_handler is set to strict + + @note Binary values are serialized as object containing two keys: + - "bytes": an array of bytes as integers + - "subtype": the subtype as integer or "null" if the binary has no subtype + + @complexity Linear. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @liveexample{The following example shows the effect of different @a indent\, + @a indent_char\, and @a ensure_ascii parameters to the result of the + serialization.,dump} + + @see https://docs.python.org/2/library/json.html#json.dump + + @since version 1.0.0; indentation character @a indent_char, option + @a ensure_ascii and exceptions added in version 3.0.0; error + handlers added in version 3.4.0; serialization of binary values added + in version 3.8.0. + */ + string_t dump(const int indent = -1, + const char indent_char = ' ', + const bool ensure_ascii = false, + const error_handler_t error_handler = error_handler_t::strict) const + { + string_t result; + serializer s(detail::output_adapter(result), indent_char, error_handler); + + if (indent >= 0) + { + s.dump(*this, true, ensure_ascii, static_cast(indent)); + } + else + { + s.dump(*this, false, ensure_ascii, 0); + } + + return result; + } + + /*! + @brief return the type of the JSON value (explicit) + + Return the type of the JSON value as a value from the @ref value_t + enumeration. + + @return the type of the JSON value + Value type | return value + ------------------------- | ------------------------- + null | value_t::null + boolean | value_t::boolean + string | value_t::string + number (integer) | value_t::number_integer + number (unsigned integer) | value_t::number_unsigned + number (floating-point) | value_t::number_float + object | value_t::object + array | value_t::array + binary | value_t::binary + discarded | value_t::discarded + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `type()` for all JSON + types.,type} + + @sa see @ref operator value_t() -- return the type of the JSON value (implicit) + @sa see @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr value_t type() const noexcept + { + return m_type; + } + + /*! + @brief return whether type is primitive + + This function returns true if and only if the JSON type is primitive + (string, number, boolean, or null). + + @return `true` if type is primitive (string, number, boolean, or null), + `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_primitive()` for all JSON + types.,is_primitive} + + @sa see @ref is_structured() -- returns whether JSON value is structured + @sa see @ref is_null() -- returns whether JSON value is `null` + @sa see @ref is_string() -- returns whether JSON value is a string + @sa see @ref is_boolean() -- returns whether JSON value is a boolean + @sa see @ref is_number() -- returns whether JSON value is a number + @sa see @ref is_binary() -- returns whether JSON value is a binary array + + @since version 1.0.0 + */ + constexpr bool is_primitive() const noexcept + { + return is_null() || is_string() || is_boolean() || is_number() || is_binary(); + } + + /*! + @brief return whether type is structured + + This function returns true if and only if the JSON type is structured + (array or object). + + @return `true` if type is structured (array or object), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_structured()` for all JSON + types.,is_structured} + + @sa see @ref is_primitive() -- returns whether value is primitive + @sa see @ref is_array() -- returns whether value is an array + @sa see @ref is_object() -- returns whether value is an object + + @since version 1.0.0 + */ + constexpr bool is_structured() const noexcept + { + return is_array() || is_object(); + } + + /*! + @brief return whether value is null + + This function returns true if and only if the JSON value is null. + + @return `true` if type is null, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_null()` for all JSON + types.,is_null} + + @since version 1.0.0 + */ + constexpr bool is_null() const noexcept + { + return m_type == value_t::null; + } + + /*! + @brief return whether value is a boolean + + This function returns true if and only if the JSON value is a boolean. + + @return `true` if type is boolean, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_boolean()` for all JSON + types.,is_boolean} + + @since version 1.0.0 + */ + constexpr bool is_boolean() const noexcept + { + return m_type == value_t::boolean; + } + + /*! + @brief return whether value is a number + + This function returns true if and only if the JSON value is a number. This + includes both integer (signed and unsigned) and floating-point values. + + @return `true` if type is number (regardless whether integer, unsigned + integer or floating-type), `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number()` for all JSON + types.,is_number} + + @sa see @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa see @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa see @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number() const noexcept + { + return is_number_integer() || is_number_float(); + } + + /*! + @brief return whether value is an integer number + + This function returns true if and only if the JSON value is a signed or + unsigned integer number. This excludes floating-point values. + + @return `true` if type is an integer or unsigned integer number, `false` + otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_integer()` for all + JSON types.,is_number_integer} + + @sa see @ref is_number() -- check if value is a number + @sa see @ref is_number_unsigned() -- check if value is an unsigned integer + number + @sa see @ref is_number_float() -- check if value is a floating-point number + + @since version 1.0.0 + */ + constexpr bool is_number_integer() const noexcept + { + return m_type == value_t::number_integer || m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is an unsigned integer number + + This function returns true if and only if the JSON value is an unsigned + integer number. This excludes floating-point and signed integer values. + + @return `true` if type is an unsigned integer number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_unsigned()` for all + JSON types.,is_number_unsigned} + + @sa see @ref is_number() -- check if value is a number + @sa see @ref is_number_integer() -- check if value is an integer or unsigned + integer number + @sa see @ref is_number_float() -- check if value is a floating-point number + + @since version 2.0.0 + */ + constexpr bool is_number_unsigned() const noexcept + { + return m_type == value_t::number_unsigned; + } + + /*! + @brief return whether value is a floating-point number + + This function returns true if and only if the JSON value is a + floating-point number. This excludes signed and unsigned integer values. + + @return `true` if type is a floating-point number, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_number_float()` for all + JSON types.,is_number_float} + + @sa see @ref is_number() -- check if value is number + @sa see @ref is_number_integer() -- check if value is an integer number + @sa see @ref is_number_unsigned() -- check if value is an unsigned integer + number + + @since version 1.0.0 + */ + constexpr bool is_number_float() const noexcept + { + return m_type == value_t::number_float; + } + + /*! + @brief return whether value is an object + + This function returns true if and only if the JSON value is an object. + + @return `true` if type is object, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_object()` for all JSON + types.,is_object} + + @since version 1.0.0 + */ + constexpr bool is_object() const noexcept + { + return m_type == value_t::object; + } + + /*! + @brief return whether value is an array + + This function returns true if and only if the JSON value is an array. + + @return `true` if type is array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_array()` for all JSON + types.,is_array} + + @since version 1.0.0 + */ + constexpr bool is_array() const noexcept + { + return m_type == value_t::array; + } + + /*! + @brief return whether value is a string + + This function returns true if and only if the JSON value is a string. + + @return `true` if type is string, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_string()` for all JSON + types.,is_string} + + @since version 1.0.0 + */ + constexpr bool is_string() const noexcept + { + return m_type == value_t::string; + } + + /*! + @brief return whether value is a binary array + + This function returns true if and only if the JSON value is a binary array. + + @return `true` if type is binary array, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_binary()` for all JSON + types.,is_binary} + + @since version 3.8.0 + */ + constexpr bool is_binary() const noexcept + { + return m_type == value_t::binary; + } + + /*! + @brief return whether value is discarded + + This function returns true if and only if the JSON value was discarded + during parsing with a callback function (see @ref parser_callback_t). + + @note This function will always be `false` for JSON values after parsing. + That is, discarded values can only occur during parsing, but will be + removed when inside a structured value or replaced by null in other cases. + + @return `true` if type is discarded, `false` otherwise. + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies `is_discarded()` for all JSON + types.,is_discarded} + + @since version 1.0.0 + */ + constexpr bool is_discarded() const noexcept + { + return m_type == value_t::discarded; + } + + /*! + @brief return the type of the JSON value (implicit) + + Implicitly return the type of the JSON value as a value from the @ref + value_t enumeration. + + @return the type of the JSON value + + @complexity Constant. + + @exceptionsafety No-throw guarantee: this member function never throws + exceptions. + + @liveexample{The following code exemplifies the @ref value_t operator for + all JSON types.,operator__value_t} + + @sa see @ref type() -- return the type of the JSON value (explicit) + @sa see @ref type_name() -- return the type as string + + @since version 1.0.0 + */ + constexpr operator value_t() const noexcept + { + return m_type; + } + + /// @} + + private: + ////////////////// + // value access // + ////////////////// + + /// get a boolean (explicit) + boolean_t get_impl(boolean_t* /*unused*/) const + { + if (JSON_HEDLEY_LIKELY(is_boolean())) + { + return m_value.boolean; + } + + JSON_THROW(type_error::create(302, "type must be boolean, but is " + std::string(type_name()), *this)); + } + + /// get a pointer to the value (object) + object_t* get_impl_ptr(object_t* /*unused*/) noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (object) + constexpr const object_t* get_impl_ptr(const object_t* /*unused*/) const noexcept + { + return is_object() ? m_value.object : nullptr; + } + + /// get a pointer to the value (array) + array_t* get_impl_ptr(array_t* /*unused*/) noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (array) + constexpr const array_t* get_impl_ptr(const array_t* /*unused*/) const noexcept + { + return is_array() ? m_value.array : nullptr; + } + + /// get a pointer to the value (string) + string_t* get_impl_ptr(string_t* /*unused*/) noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (string) + constexpr const string_t* get_impl_ptr(const string_t* /*unused*/) const noexcept + { + return is_string() ? m_value.string : nullptr; + } + + /// get a pointer to the value (boolean) + boolean_t* get_impl_ptr(boolean_t* /*unused*/) noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (boolean) + constexpr const boolean_t* get_impl_ptr(const boolean_t* /*unused*/) const noexcept + { + return is_boolean() ? &m_value.boolean : nullptr; + } + + /// get a pointer to the value (integer number) + number_integer_t* get_impl_ptr(number_integer_t* /*unused*/) noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (integer number) + constexpr const number_integer_t* get_impl_ptr(const number_integer_t* /*unused*/) const noexcept + { + return is_number_integer() ? &m_value.number_integer : nullptr; + } + + /// get a pointer to the value (unsigned number) + number_unsigned_t* get_impl_ptr(number_unsigned_t* /*unused*/) noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (unsigned number) + constexpr const number_unsigned_t* get_impl_ptr(const number_unsigned_t* /*unused*/) const noexcept + { + return is_number_unsigned() ? &m_value.number_unsigned : nullptr; + } + + /// get a pointer to the value (floating-point number) + number_float_t* get_impl_ptr(number_float_t* /*unused*/) noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (floating-point number) + constexpr const number_float_t* get_impl_ptr(const number_float_t* /*unused*/) const noexcept + { + return is_number_float() ? &m_value.number_float : nullptr; + } + + /// get a pointer to the value (binary) + binary_t* get_impl_ptr(binary_t* /*unused*/) noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /// get a pointer to the value (binary) + constexpr const binary_t* get_impl_ptr(const binary_t* /*unused*/) const noexcept + { + return is_binary() ? m_value.binary : nullptr; + } + + /*! + @brief helper function to implement get_ref() + + This function helps to implement get_ref() without code duplication for + const and non-const overloads + + @tparam ThisType will be deduced as `basic_json` or `const basic_json` + + @throw type_error.303 if ReferenceType does not match underlying value + type of the current JSON + */ + template + static ReferenceType get_ref_impl(ThisType& obj) + { + // delegate the call to get_ptr<>() + auto* ptr = obj.template get_ptr::type>(); + + if (JSON_HEDLEY_LIKELY(ptr != nullptr)) + { + return *ptr; + } + + JSON_THROW(type_error::create(303, "incompatible ReferenceType for get_ref, actual type is " + std::string(obj.type_name()), obj)); + } + + public: + /// @name value access + /// Direct access to the stored value of a JSON value. + /// @{ + + /*! + @brief get a pointer value (implicit) + + Implicit pointer access to the internally stored JSON value. No copies are + made. + + @warning Writing data to the pointee of the result yields an undefined + state. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. Enforced by a static + assertion. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get_ptr} + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get_ptr() noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() + return get_impl_ptr(static_cast(nullptr)); + } + + /*! + @brief get a pointer value (implicit) + @copydoc get_ptr() + */ + template < typename PointerType, typename std::enable_if < + std::is_pointer::value&& + std::is_const::type>::value, int >::type = 0 > + constexpr auto get_ptr() const noexcept -> decltype(std::declval().get_impl_ptr(std::declval())) + { + // delegate the call to get_impl_ptr<>() const + return get_impl_ptr(static_cast(nullptr)); + } + + private: + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value + which is [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType ret; + JSONSerializer::from_json(*this, ret); + return ret; + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + - @ref json_serializer does not have a `from_json()` method of + the form `ValueType from_json(const basic_json&)` + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get__ValueType_const} + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::is_default_constructible::value&& + detail::has_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), std::declval()))) + { + ValueType ret{}; + JSONSerializer::from_json(*this, ret); + return ret; + } + + /*! + @brief get a value (explicit); special case + + Explicit type conversion between the JSON value and a compatible value + which is **not** [CopyConstructible](https://en.cppreference.com/w/cpp/named_req/CopyConstructible) + and **not** [DefaultConstructible](https://en.cppreference.com/w/cpp/named_req/DefaultConstructible). + The value is converted by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + return JSONSerializer::from_json(*this); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json and + - @ref json_serializer has a `from_json()` method of the form + `ValueType from_json(const basic_json&)` + + @note If @ref json_serializer has both overloads of + `from_json()`, this one is chosen. + + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @a ValueType + + @throw what @ref json_serializer `from_json()` method throws + + @since version 2.1.0 + */ + template < typename ValueType, + detail::enable_if_t < + detail::has_non_default_from_json::value, + int > = 0 > + ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( + JSONSerializer::from_json(std::declval()))) + { + return JSONSerializer::from_json(*this); + } + + /*! + @brief get special-case overload + + This overloads converts the current @ref basic_json in a different + @ref basic_json type + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this, converted into @a BasicJsonType + + @complexity Depending on the implementation of the called `from_json()` + method. + + @since version 3.2.0 + */ + template < typename BasicJsonType, + detail::enable_if_t < + detail::is_basic_json::value, + int > = 0 > + BasicJsonType get_impl(detail::priority_tag<2> /*unused*/) const + { + return *this; + } + + /*! + @brief get special-case overload + + This overloads avoids a lot of template boilerplate, it can be seen as the + identity method + + @tparam BasicJsonType == @ref basic_json + + @return a copy of *this + + @complexity Constant. + + @since version 2.1.0 + */ + template::value, + int> = 0> + basic_json get_impl(detail::priority_tag<3> /*unused*/) const + { + return *this; + } + + /*! + @brief get a pointer value (explicit) + @copydoc get() + */ + template::value, + int> = 0> + constexpr auto get_impl(detail::priority_tag<4> /*unused*/) const noexcept + -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + public: + /*! + @brief get a (pointer) value (explicit) + + Performs explicit type conversion between the JSON value and a compatible value if required. + + - If the requested type is a pointer to the internally stored JSON value that pointer is returned. + No copies are made. + + - If the requested type is the current @ref basic_json, or a different @ref basic_json convertible + from the current @ref basic_json. + + - Otherwise the value is converted by calling the @ref json_serializer `from_json()` + method. + + @tparam ValueTypeCV the provided value type + @tparam ValueType the returned value type + + @return copy of the JSON value, converted to @tparam ValueType if necessary + + @throw what @ref json_serializer `from_json()` method throws if conversion is required + + @since version 2.1.0 + */ + template < typename ValueTypeCV, typename ValueType = detail::uncvref_t> +#if defined(JSON_HAS_CPP_14) + constexpr +#endif + auto get() const noexcept( + noexcept(std::declval().template get_impl(detail::priority_tag<4> {}))) + -> decltype(std::declval().template get_impl(detail::priority_tag<4> {})) + { + // we cannot static_assert on ValueTypeCV being non-const, because + // there is support for get(), which is why we + // still need the uncvref + static_assert(!std::is_reference::value, + "get() cannot be used with reference types, you might want to use get_ref()"); + return get_impl(detail::priority_tag<4> {}); + } + + /*! + @brief get a pointer value (explicit) + + Explicit pointer access to the internally stored JSON value. No copies are + made. + + @warning The pointer becomes invalid if the underlying JSON object + changes. + + @tparam PointerType pointer type; must be a pointer to @ref array_t, @ref + object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, + @ref number_unsigned_t, or @ref number_float_t. + + @return pointer to the internally stored JSON value if the requested + pointer type @a PointerType fits to the JSON value; `nullptr` otherwise + + @complexity Constant. + + @liveexample{The example below shows how pointers to internal values of a + JSON value can be requested. Note that no type conversions are made and a + `nullptr` is returned if the value and the requested pointer type does not + match.,get__PointerType} + + @sa see @ref get_ptr() for explicit pointer-member access + + @since version 1.0.0 + */ + template::value, int>::type = 0> + auto get() noexcept -> decltype(std::declval().template get_ptr()) + { + // delegate the call to get_ptr + return get_ptr(); + } + + /*! + @brief get a value (explicit) + + Explicit type conversion between the JSON value and a compatible value. + The value is filled into the input parameter by calling the @ref json_serializer + `from_json()` method. + + The function is equivalent to executing + @code {.cpp} + ValueType v; + JSONSerializer::from_json(*this, v); + @endcode + + This overloads is chosen if: + - @a ValueType is not @ref basic_json, + - @ref json_serializer has a `from_json()` method of the form + `void from_json(const basic_json&, ValueType&)`, and + + @tparam ValueType the input parameter type. + + @return the input parameter, allowing chaining calls. + + @throw what @ref json_serializer `from_json()` method throws + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,get_to} + + @since version 3.3.0 + */ + template < typename ValueType, + detail::enable_if_t < + !detail::is_basic_json::value&& + detail::has_from_json::value, + int > = 0 > + ValueType & get_to(ValueType& v) const noexcept(noexcept( + JSONSerializer::from_json(std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + // specialization to allow to call get_to with a basic_json value + // see https://github.com/nlohmann/json/issues/2175 + template::value, + int> = 0> + ValueType & get_to(ValueType& v) const + { + v = *this; + return v; + } + + template < + typename T, std::size_t N, + typename Array = T (&)[N], // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + detail::enable_if_t < + detail::has_from_json::value, int > = 0 > + Array get_to(T (&v)[N]) const // NOLINT(cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays) + noexcept(noexcept(JSONSerializer::from_json( + std::declval(), v))) + { + JSONSerializer::from_json(*this, v); + return v; + } + + /*! + @brief get a reference value (implicit) + + Implicit reference access to the internally stored JSON value. No copies + are made. + + @warning Writing data to the referee of the result yields an undefined + state. + + @tparam ReferenceType reference type; must be a reference to @ref array_t, + @ref object_t, @ref string_t, @ref boolean_t, @ref number_integer_t, or + @ref number_float_t. Enforced by static assertion. + + @return reference to the internally stored JSON value if the requested + reference type @a ReferenceType fits to the JSON value; throws + type_error.303 otherwise + + @throw type_error.303 in case passed type @a ReferenceType is incompatible + with the stored JSON value; see example below + + @complexity Constant. + + @liveexample{The example shows several calls to `get_ref()`.,get_ref} + + @since version 1.1.0 + */ + template::value, int>::type = 0> + ReferenceType get_ref() + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a reference value (implicit) + @copydoc get_ref() + */ + template < typename ReferenceType, typename std::enable_if < + std::is_reference::value&& + std::is_const::type>::value, int >::type = 0 > + ReferenceType get_ref() const + { + // delegate call to get_ref_impl + return get_ref_impl(*this); + } + + /*! + @brief get a value (implicit) + + Implicit type conversion between the JSON value and a compatible value. + The call is realized by calling @ref get() const. + + @tparam ValueType non-pointer type compatible to the JSON value, for + instance `int` for JSON integer numbers, `bool` for JSON booleans, or + `std::vector` types for JSON arrays. The character type of @ref string_t + as well as an initializer list of this type is excluded to avoid + ambiguities as these types implicitly convert to `std::string`. + + @return copy of the JSON value, converted to type @a ValueType + + @throw type_error.302 in case passed type @a ValueType is incompatible + to the JSON value type (e.g., the JSON value is of type boolean, but a + string is requested); see example below + + @complexity Linear in the size of the JSON value. + + @liveexample{The example below shows several conversions from JSON values + to other types. There a few things to note: (1) Floating-point numbers can + be converted to integers\, (2) A JSON array can be converted to a standard + `std::vector`\, (3) A JSON object can be converted to C++ + associative containers such as `std::unordered_map`.,operator__ValueType} + + @since version 1.0.0 + */ + template < typename ValueType, typename std::enable_if < + detail::conjunction < + detail::negation>, + detail::negation>>, + detail::negation>, + detail::negation>, + detail::negation>>, + +#if defined(JSON_HAS_CPP_17) && (defined(__GNUC__) || (defined(_MSC_VER) && _MSC_VER >= 1910 && _MSC_VER <= 1914)) + detail::negation>, +#endif + detail::is_detected_lazy + >::value, int >::type = 0 > + JSON_EXPLICIT operator ValueType() const + { + // delegate the call to get<>() const + return get(); + } + + /*! + @return reference to the binary value + + @throw type_error.302 if the value is not binary + + @sa see @ref is_binary() to check if the value is binary + + @since version 3.8.0 + */ + binary_t& get_binary() + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @copydoc get_binary() + const binary_t& get_binary() const + { + if (!is_binary()) + { + JSON_THROW(type_error::create(302, "type must be binary, but is " + std::string(type_name()), *this)); + } + + return *get_ptr(); + } + + /// @} + + + //////////////////// + // element access // + //////////////////// + + /// @name element access + /// Access to the JSON value. + /// @{ + + /*! + @brief access specified array element with bounds checking + + Returns a reference to the element at specified location @a idx, with + bounds checking. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__size_type} + */ + reference at(size_type idx) + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return set_parent(m_value.array->at(idx)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified array element with bounds checking + + Returns a const reference to the element at specified location @a idx, + with bounds checking. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.304 if the JSON value is not an array; in this case, + calling `at` with an index makes no sense. See example below. + @throw out_of_range.401 if the index @a idx is out of range of the array; + that is, `idx >= size()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 1.0.0 + + @liveexample{The example below shows how array elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__size_type_const} + */ + const_reference at(size_type idx) const + { + // at only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + JSON_TRY + { + return m_value.array->at(idx); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a reference to the element at with specified key @a key, with + bounds checking. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa see @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read and + written using `at()`. It also demonstrates the different exceptions that + can be thrown.,at__object_t_key_type} + */ + reference at(const typename object_t::key_type& key) + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return set_parent(m_value.object->at(key)); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified object element with bounds checking + + Returns a const reference to the element at with specified key @a key, + with bounds checking. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @throw type_error.304 if the JSON value is not an object; in this case, + calling `at` with a key makes no sense. See example below. + @throw out_of_range.403 if the key @a key is is not stored in the object; + that is, `find(key) == end()`. See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Logarithmic in the size of the container. + + @sa see @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + + @liveexample{The example below shows how object elements can be read using + `at()`. It also demonstrates the different exceptions that can be thrown., + at__object_t_key_type_const} + */ + const_reference at(const typename object_t::key_type& key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_TRY + { + return m_value.object->at(key); + } + JSON_CATCH (std::out_of_range&) + { + // create better exception explanation + JSON_THROW(out_of_range::create(403, "key '" + key + "' not found", *this)); + } + } + else + { + JSON_THROW(type_error::create(304, "cannot use at() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief access specified array element + + Returns a reference to the element at specified location @a idx. + + @note If @a idx is beyond the range of the array (i.e., `idx >= size()`), + then the array is silently filled up with `null` values to make `idx` a + valid reference to the last stored element. + + @param[in] idx index of the element to access + + @return reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array or null; in that + cases, using the [] operator with an index makes no sense. + + @complexity Constant if @a idx is in the range of the array. Otherwise + linear in `idx - size()`. + + @liveexample{The example below shows how array elements can be read and + written using `[]` operator. Note the addition of `null` + values.,operatorarray__size_type} + + @since version 1.0.0 + */ + reference operator[](size_type idx) + { + // implicitly convert null value to an empty array + if (is_null()) + { + m_type = value_t::array; + m_value.array = create(); + assert_invariant(); + } + + // operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // fill up array with null values if given idx is outside range + if (idx >= m_value.array->size()) + { +#if JSON_DIAGNOSTICS + // remember array size before resizing + const auto previous_size = m_value.array->size(); +#endif + m_value.array->resize(idx + 1); + +#if JSON_DIAGNOSTICS + // set parent for values added above + set_parents(begin() + static_cast(previous_size), static_cast(idx + 1 - previous_size)); +#endif + } + + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified array element + + Returns a const reference to the element at specified location @a idx. + + @param[in] idx index of the element to access + + @return const reference to the element at index @a idx + + @throw type_error.305 if the JSON value is not an array; in that case, + using the [] operator with an index makes no sense. + + @complexity Constant. + + @liveexample{The example below shows how array elements can be read using + the `[]` operator.,operatorarray__size_type_const} + + @since version 1.0.0 + */ + const_reference operator[](size_type idx) const + { + // const operator[] only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + return m_value.array->operator[](idx); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a numeric argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + */ + reference operator[](const typename object_t::key_type& key) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + // operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.0.0 + */ + const_reference operator[](const typename object_t::key_type& key) const + { + // const operator[] only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified object element + + Returns a reference to the element at with specified key @a key. + + @note If @a key is not found in the object, then it is silently added to + the object and filled with a `null` value to make `key` a valid reference. + In case the value was `null` before, it is converted to an object. + + @param[in] key key of the element to access + + @return reference to the element at key @a key + + @throw type_error.305 if the JSON value is not an object or null; in that + cases, using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read and + written using the `[]` operator.,operatorarray__key_type} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + reference operator[](T* key) + { + // implicitly convert null to object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return set_parent(m_value.object->operator[](key)); + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief read-only access specified object element + + Returns a const reference to the element at with specified key @a key. No + bounds checking is performed. + + @warning If the element with key @a key does not exist, the behavior is + undefined. + + @param[in] key key of the element to access + + @return const reference to the element at key @a key + + @pre The element with key @a key must exist. **This precondition is + enforced with an assertion.** + + @throw type_error.305 if the JSON value is not an object; in that case, + using the [] operator with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be read using + the `[]` operator.,operatorarray__key_type_const} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref value() for access by value with a default value + + @since version 1.1.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + const_reference operator[](T* key) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + JSON_ASSERT(m_value.object->find(key) != m_value.object->end()); + return m_value.object->find(key)->second; + } + + JSON_THROW(type_error::create(305, "cannot use operator[] with a string argument with " + std::string(type_name()), *this)); + } + + /*! + @brief access specified object element with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(key); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const typename object_t::key_type&), this function + does not throw if the given key @a key was not found. + + @note Unlike @ref operator[](const typename object_t::key_type& key), this + function does not implicitly add an element to the position defined by @a + key. This function is furthermore also applicable to const objects. + + @param[in] key key of the element to access + @param[in] default_value the value to return if @a key is not found + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a key + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value} + + @sa see @ref at(const typename object_t::key_type&) for access by reference + with range checking + @sa see @ref operator[](const typename object_t::key_type&) for unchecked + access by reference + + @since version 1.0.0 + */ + // using std::is_convertible in a std::enable_if will fail when using explicit conversions + template < class ValueType, typename std::enable_if < + detail::is_getable::value + && !std::is_same::value, int >::type = 0 > + ValueType value(const typename object_t::key_type& key, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if key is found, return value and given default value otherwise + const auto it = find(key); + if (it != end()) + { + return it->template get(); + } + + return default_value; + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const typename object_t::key_type&, const ValueType&) const + */ + string_t value(const typename object_t::key_type& key, const char* default_value) const + { + return value(key, string_t(default_value)); + } + + /*! + @brief access specified object element via JSON Pointer with default value + + Returns either a copy of an object's element at the specified key @a key + or a given default value if no element with key @a key exists. + + The function is basically equivalent to executing + @code {.cpp} + try { + return at(ptr); + } catch(out_of_range) { + return default_value; + } + @endcode + + @note Unlike @ref at(const json_pointer&), this function does not throw + if the given key @a key was not found. + + @param[in] ptr a JSON pointer to the element to access + @param[in] default_value the value to return if @a ptr found no value + + @tparam ValueType type compatible to JSON values, for instance `int` for + JSON integer numbers, `bool` for JSON booleans, or `std::vector` types for + JSON arrays. Note the type of the expected value at @a key and the default + value @a default_value must be compatible. + + @return copy of the element at key @a key or @a default_value if @a key + is not found + + @throw type_error.302 if @a default_value does not match the type of the + value at @a ptr + @throw type_error.306 if the JSON value is not an object; in that case, + using `value()` with a key makes no sense. + + @complexity Logarithmic in the size of the container. + + @liveexample{The example below shows how object elements can be queried + with a default value.,basic_json__value_ptr} + + @sa see @ref operator[](const json_pointer&) for unchecked access by reference + + @since version 2.0.2 + */ + template::value, int>::type = 0> + ValueType value(const json_pointer& ptr, const ValueType& default_value) const + { + // at only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + // if pointer resolves a value, return it or use default value + JSON_TRY + { + return ptr.get_checked(this).template get(); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + return default_value; + } + } + + JSON_THROW(type_error::create(306, "cannot use value() with " + std::string(type_name()), *this)); + } + + /*! + @brief overload for a default value of type const char* + @copydoc basic_json::value(const json_pointer&, ValueType) const + */ + JSON_HEDLEY_NON_NULL(3) + string_t value(const json_pointer& ptr, const char* default_value) const + { + return value(ptr, string_t(default_value)); + } + + /*! + @brief access the first element + + Returns a reference to the first element in the container. For a JSON + container `c`, the expression `c.front()` is equivalent to `*c.begin()`. + + @return In case of a structured type (array or object), a reference to the + first element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on `null` value + + @liveexample{The following code shows an example for `front()`.,front} + + @sa see @ref back() -- access the last element + + @since version 1.0.0 + */ + reference front() + { + return *begin(); + } + + /*! + @copydoc basic_json::front() + */ + const_reference front() const + { + return *cbegin(); + } + + /*! + @brief access the last element + + Returns a reference to the last element in the container. For a JSON + container `c`, the expression `c.back()` is equivalent to + @code {.cpp} + auto tmp = c.end(); + --tmp; + return *tmp; + @endcode + + @return In case of a structured type (array or object), a reference to the + last element is returned. In case of number, string, boolean, or binary + values, a reference to the value is returned. + + @complexity Constant. + + @pre The JSON value must not be `null` (would throw `std::out_of_range`) + or an empty array or object (undefined behavior, **guarded by + assertions**). + @post The JSON value remains unchanged. + + @throw invalid_iterator.214 when called on a `null` value. See example + below. + + @liveexample{The following code shows an example for `back()`.,back} + + @sa see @ref front() -- access the first element + + @since version 1.0.0 + */ + reference back() + { + auto tmp = end(); + --tmp; + return *tmp; + } + + /*! + @copydoc basic_json::back() + */ + const_reference back() const + { + auto tmp = cend(); + --tmp; + return *tmp; + } + + /*! + @brief remove element given an iterator + + Removes the element specified by iterator @a pos. The iterator @a pos must + be valid and dereferenceable. Thus the `end()` iterator (which is valid, + but is not dereferenceable) cannot be used as a value for @a pos. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] pos iterator to the element to remove + @return Iterator following the last removed element. If the iterator @a + pos refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.202 if called on an iterator which does not belong + to the current JSON value; example: `"iterator does not fit current + value"` + @throw invalid_iterator.205 if called on a primitive type with invalid + iterator (i.e., any iterator which is not `begin()`); example: `"iterator + out of range"` + + @complexity The complexity depends on the type: + - objects: amortized constant + - arrays: linear in distance between @a pos and the end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType} + + @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa see @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa see @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType pos) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != pos.m_object)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_UNLIKELY(!pos.m_it.primitive_iterator.is_begin())) + { + JSON_THROW(invalid_iterator::create(205, "iterator out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(pos.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(pos.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /*! + @brief remove elements given an iterator range + + Removes the element specified by the range `[first; last)`. The iterator + @a first does not need to be dereferenceable if `first == last`: erasing + an empty range is a no-op. + + If called on a primitive type other than `null`, the resulting JSON value + will be `null`. + + @param[in] first iterator to the beginning of the range to remove + @param[in] last iterator past the end of the range to remove + @return Iterator following the last removed element. If the iterator @a + second refers to the last element, the `end()` iterator is returned. + + @tparam IteratorType an @ref iterator or @ref const_iterator + + @post Invalidates iterators and references at or after the point of the + erase, including the `end()` iterator. + + @throw type_error.307 if called on a `null` value; example: `"cannot use + erase() with null"` + @throw invalid_iterator.203 if called on iterators which does not belong + to the current JSON value; example: `"iterators do not fit current value"` + @throw invalid_iterator.204 if called on a primitive type with invalid + iterators (i.e., if `first != begin()` and `last != end()`); example: + `"iterators out of range"` + + @complexity The complexity depends on the type: + - objects: `log(size()) + std::distance(first, last)` + - arrays: linear in the distance between @a first and @a last, plus linear + in the distance between @a last and end of the container + - strings and binary: linear in the length of the member + - other types: constant + + @liveexample{The example shows the result of `erase()` for different JSON + types.,erase__IteratorType_IteratorType} + + @sa see @ref erase(IteratorType) -- removes the element at a given position + @sa see @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + @sa see @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + template < class IteratorType, typename std::enable_if < + std::is_same::value || + std::is_same::value, int >::type + = 0 > + IteratorType erase(IteratorType first, IteratorType last) + { + // make sure iterator fits the current value + if (JSON_HEDLEY_UNLIKELY(this != first.m_object || this != last.m_object)) + { + JSON_THROW(invalid_iterator::create(203, "iterators do not fit current value", *this)); + } + + IteratorType result = end(); + + switch (m_type) + { + case value_t::boolean: + case value_t::number_float: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::string: + case value_t::binary: + { + if (JSON_HEDLEY_LIKELY(!first.m_it.primitive_iterator.is_begin() + || !last.m_it.primitive_iterator.is_end())) + { + JSON_THROW(invalid_iterator::create(204, "iterators out of range", *this)); + } + + if (is_string()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.string); + std::allocator_traits::deallocate(alloc, m_value.string, 1); + m_value.string = nullptr; + } + else if (is_binary()) + { + AllocatorType alloc; + std::allocator_traits::destroy(alloc, m_value.binary); + std::allocator_traits::deallocate(alloc, m_value.binary, 1); + m_value.binary = nullptr; + } + + m_type = value_t::null; + assert_invariant(); + break; + } + + case value_t::object: + { + result.m_it.object_iterator = m_value.object->erase(first.m_it.object_iterator, + last.m_it.object_iterator); + break; + } + + case value_t::array: + { + result.m_it.array_iterator = m_value.array->erase(first.m_it.array_iterator, + last.m_it.array_iterator); + break; + } + + case value_t::null: + case value_t::discarded: + default: + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + return result; + } + + /*! + @brief remove element from a JSON object given a key + + Removes elements from a JSON object with the key value @a key. + + @param[in] key value of the elements to remove + + @return Number of elements removed. If @a ObjectType is the default + `std::map` type, the return value will always be `0` (@a key was not + found) or `1` (@a key was found). + + @post References and iterators to the erased elements are invalidated. + Other references and iterators are not affected. + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + + @complexity `log(size()) + count(key)` + + @liveexample{The example shows the effect of `erase()`.,erase__key_type} + + @sa see @ref erase(IteratorType) -- removes the element at a given position + @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa see @ref erase(const size_type) -- removes the element from an array at + the given index + + @since version 1.0.0 + */ + size_type erase(const typename object_t::key_type& key) + { + // this erase only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + return m_value.object->erase(key); + } + + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + + /*! + @brief remove element from a JSON array given an index + + Removes element from a JSON array at the index @a idx. + + @param[in] idx index of the element to remove + + @throw type_error.307 when called on a type other than JSON object; + example: `"cannot use erase() with null"` + @throw out_of_range.401 when `idx >= size()`; example: `"array index 17 + is out of range"` + + @complexity Linear in distance between @a idx and the end of the container. + + @liveexample{The example shows the effect of `erase()`.,erase__size_type} + + @sa see @ref erase(IteratorType) -- removes the element at a given position + @sa see @ref erase(IteratorType, IteratorType) -- removes the elements in + the given range + @sa see @ref erase(const typename object_t::key_type&) -- removes the element + from an object at the given key + + @since version 1.0.0 + */ + void erase(const size_type idx) + { + // this erase only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + if (JSON_HEDLEY_UNLIKELY(idx >= size())) + { + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", *this)); + } + + m_value.array->erase(m_value.array->begin() + static_cast(idx)); + } + else + { + JSON_THROW(type_error::create(307, "cannot use erase() with " + std::string(type_name()), *this)); + } + } + + /// @} + + + //////////// + // lookup // + //////////// + + /// @name lookup + /// @{ + + /*! + @brief find an element in a JSON object + + Finds an element in a JSON object with key equivalent to @a key. If the + element is not found or the JSON value is not an object, end() is + returned. + + @note This method always returns @ref end() when executed on a JSON type + that is not an object. + + @param[in] key key value of the element to search for. + + @return Iterator to an element with key equivalent to @a key. If no such + element is found or the JSON value is not an object, past-the-end (see + @ref end()) iterator is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `find()` is used.,find__key_type} + + @sa see @ref contains(KeyT&&) const -- checks whether a key exists + + @since version 1.0.0 + */ + template + iterator find(KeyT&& key) + { + auto result = end(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief find an element in a JSON object + @copydoc find(KeyT&&) + */ + template + const_iterator find(KeyT&& key) const + { + auto result = cend(); + + if (is_object()) + { + result.m_it.object_iterator = m_value.object->find(std::forward(key)); + } + + return result; + } + + /*! + @brief returns the number of occurrences of a key in a JSON object + + Returns the number of elements with key @a key. If ObjectType is the + default `std::map` type, the return value will always be `0` (@a key was + not found) or `1` (@a key was found). + + @note This method always returns `0` when executed on a JSON type that is + not an object. + + @param[in] key key value of the element to count + + @return Number of elements with key @a key. If the JSON value is not an + object, the return value will be `0`. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The example shows how `count()` is used.,count} + + @since version 1.0.0 + */ + template + size_type count(KeyT&& key) const + { + // return 0 for all nonobject types + return is_object() ? m_value.object->count(std::forward(key)) : 0; + } + + /*! + @brief check the existence of an element in a JSON object + + Check whether an element exists in a JSON object with key equivalent to + @a key. If the element is not found or the JSON value is not an object, + false is returned. + + @note This method always returns false when executed on a JSON type + that is not an object. + + @param[in] key key value to check its existence. + + @return true if an element with specified @a key exists. If no such + element with such key is found or the JSON value is not an object, + false is returned. + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains} + + @sa see @ref find(KeyT&&) -- returns an iterator to an object element + @sa see @ref contains(const json_pointer&) const -- checks the existence for a JSON pointer + + @since version 3.6.0 + */ + template < typename KeyT, typename std::enable_if < + !std::is_same::type, json_pointer>::value, int >::type = 0 > + bool contains(KeyT && key) const + { + return is_object() && m_value.object->find(std::forward(key)) != m_value.object->end(); + } + + /*! + @brief check the existence of an element in a JSON object given a JSON pointer + + Check whether the given JSON pointer @a ptr can be resolved in the current + JSON value. + + @note This method can be executed on any JSON value type. + + @param[in] ptr JSON pointer to check its existence. + + @return true if the JSON pointer can be resolved to a stored value, false + otherwise. + + @post If `j.contains(ptr)` returns true, it is safe to call `j[ptr]`. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + + @complexity Logarithmic in the size of the JSON object. + + @liveexample{The following code shows an example for `contains()`.,contains_json_pointer} + + @sa see @ref contains(KeyT &&) const -- checks the existence of a key + + @since version 3.7.0 + */ + bool contains(const json_pointer& ptr) const + { + return ptr.contains(this); + } + + /// @} + + + /////////////// + // iterators // + /////////////// + + /// @name iterators + /// @{ + + /*! + @brief returns an iterator to the first element + + Returns an iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `begin()`.,begin} + + @sa see @ref cbegin() -- returns a const iterator to the beginning + @sa see @ref end() -- returns an iterator to the end + @sa see @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + iterator begin() noexcept + { + iterator result(this); + result.set_begin(); + return result; + } + + /*! + @copydoc basic_json::cbegin() + */ + const_iterator begin() const noexcept + { + return cbegin(); + } + + /*! + @brief returns a const iterator to the first element + + Returns a const iterator to the first element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator to the first element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).begin()`. + + @liveexample{The following code shows an example for `cbegin()`.,cbegin} + + @sa see @ref begin() -- returns an iterator to the beginning + @sa see @ref end() -- returns an iterator to the end + @sa see @ref cend() -- returns a const iterator to the end + + @since version 1.0.0 + */ + const_iterator cbegin() const noexcept + { + const_iterator result(this); + result.set_begin(); + return result; + } + + /*! + @brief returns an iterator to one past the last element + + Returns an iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + + @liveexample{The following code shows an example for `end()`.,end} + + @sa see @ref cend() -- returns a const iterator to the end + @sa see @ref begin() -- returns an iterator to the beginning + @sa see @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + iterator end() noexcept + { + iterator result(this); + result.set_end(); + return result; + } + + /*! + @copydoc basic_json::cend() + */ + const_iterator end() const noexcept + { + return cend(); + } + + /*! + @brief returns a const iterator to one past the last element + + Returns a const iterator to one past the last element. + + @image html range-begin-end.svg "Illustration from cppreference.com" + + @return const iterator one past the last element + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).end()`. + + @liveexample{The following code shows an example for `cend()`.,cend} + + @sa see @ref end() -- returns an iterator to the end + @sa see @ref begin() -- returns an iterator to the beginning + @sa see @ref cbegin() -- returns a const iterator to the beginning + + @since version 1.0.0 + */ + const_iterator cend() const noexcept + { + const_iterator result(this); + result.set_end(); + return result; + } + + /*! + @brief returns an iterator to the reverse-beginning + + Returns an iterator to the reverse-beginning; that is, the last element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(end())`. + + @liveexample{The following code shows an example for `rbegin()`.,rbegin} + + @sa see @ref crbegin() -- returns a const reverse iterator to the beginning + @sa see @ref rend() -- returns a reverse iterator to the end + @sa see @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + reverse_iterator rbegin() noexcept + { + return reverse_iterator(end()); + } + + /*! + @copydoc basic_json::crbegin() + */ + const_reverse_iterator rbegin() const noexcept + { + return crbegin(); + } + + /*! + @brief returns an iterator to the reverse-end + + Returns an iterator to the reverse-end; that is, one before the first + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `reverse_iterator(begin())`. + + @liveexample{The following code shows an example for `rend()`.,rend} + + @sa see @ref crend() -- returns a const reverse iterator to the end + @sa see @ref rbegin() -- returns a reverse iterator to the beginning + @sa see @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + reverse_iterator rend() noexcept + { + return reverse_iterator(begin()); + } + + /*! + @copydoc basic_json::crend() + */ + const_reverse_iterator rend() const noexcept + { + return crend(); + } + + /*! + @brief returns a const reverse iterator to the last element + + Returns a const iterator to the reverse-beginning; that is, the last + element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rbegin()`. + + @liveexample{The following code shows an example for `crbegin()`.,crbegin} + + @sa see @ref rbegin() -- returns a reverse iterator to the beginning + @sa see @ref rend() -- returns a reverse iterator to the end + @sa see @ref crend() -- returns a const reverse iterator to the end + + @since version 1.0.0 + */ + const_reverse_iterator crbegin() const noexcept + { + return const_reverse_iterator(cend()); + } + + /*! + @brief returns a const reverse iterator to one before the first + + Returns a const reverse iterator to the reverse-end; that is, one before + the first element. + + @image html range-rbegin-rend.svg "Illustration from cppreference.com" + + @complexity Constant. + + @requirement This function helps `basic_json` satisfying the + [ReversibleContainer](https://en.cppreference.com/w/cpp/named_req/ReversibleContainer) + requirements: + - The complexity is constant. + - Has the semantics of `const_cast(*this).rend()`. + + @liveexample{The following code shows an example for `crend()`.,crend} + + @sa see @ref rend() -- returns a reverse iterator to the end + @sa see @ref rbegin() -- returns a reverse iterator to the beginning + @sa see @ref crbegin() -- returns a const reverse iterator to the beginning + + @since version 1.0.0 + */ + const_reverse_iterator crend() const noexcept + { + return const_reverse_iterator(cbegin()); + } + + public: + /*! + @brief wrapper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without iterator_wrapper: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without iterator proxy: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with iterator proxy: + + @code{cpp} + for (auto it : json::iterator_wrapper(j_object)) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). + + @param[in] ref reference to a JSON value + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the wrapper is used,iterator_wrapper} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @note The name of this function is not yet final and may change in the + future. + + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use @ref items() instead; + that is, replace `json::iterator_wrapper(j)` with `j.items()`. + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(reference ref) noexcept + { + return ref.items(); + } + + /*! + @copydoc iterator_wrapper(reference) + */ + JSON_HEDLEY_DEPRECATED_FOR(3.1.0, items()) + static iteration_proxy iterator_wrapper(const_reference ref) noexcept + { + return ref.items(); + } + + /*! + @brief helper to access iterator member functions in range-based for + + This function allows to access @ref iterator::key() and @ref + iterator::value() during range-based for loops. In these loops, a + reference to the JSON values is returned, so there is no access to the + underlying iterator. + + For loop without `items()` function: + + @code{cpp} + for (auto it = j_object.begin(); it != j_object.end(); ++it) + { + std::cout << "key: " << it.key() << ", value:" << it.value() << '\n'; + } + @endcode + + Range-based for loop without `items()` function: + + @code{cpp} + for (auto it : j_object) + { + // "it" is of type json::reference and has no key() member + std::cout << "value: " << it << '\n'; + } + @endcode + + Range-based for loop with `items()` function: + + @code{cpp} + for (auto& el : j_object.items()) + { + std::cout << "key: " << el.key() << ", value:" << el.value() << '\n'; + } + @endcode + + The `items()` function also allows to use + [structured bindings](https://en.cppreference.com/w/cpp/language/structured_binding) + (C++17): + + @code{cpp} + for (auto& [key, val] : j_object.items()) + { + std::cout << "key: " << key << ", value:" << val << '\n'; + } + @endcode + + @note When iterating over an array, `key()` will return the index of the + element as string (see example). For primitive types (e.g., numbers), + `key()` returns an empty string. + + @warning Using `items()` on temporary objects is dangerous. Make sure the + object's lifetime exeeds the iteration. See + for more + information. + + @return iteration proxy object wrapping @a ref with an interface to use in + range-based for loops + + @liveexample{The following code shows how the function is used.,items} + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 3.1.0, structured bindings support since 3.5.0. + */ + iteration_proxy items() noexcept + { + return iteration_proxy(*this); + } + + /*! + @copydoc items() + */ + iteration_proxy items() const noexcept + { + return iteration_proxy(*this); + } + + /// @} + + + ////////////// + // capacity // + ////////////// + + /// @name capacity + /// @{ + + /*! + @brief checks whether the container is empty. + + Checks if a JSON value has no elements (i.e. whether its @ref size is `0`). + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `true` + boolean | `false` + string | `false` + number | `false` + binary | `false` + object | result of function `object_t::empty()` + array | result of function `array_t::empty()` + + @liveexample{The following code uses `empty()` to check if a JSON + object contains any elements.,empty} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `empty()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return whether a string stored as JSON value + is empty - it returns whether the JSON container itself is empty which is + false in the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `begin() == end()`. + + @sa see @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + bool empty() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return true; + } + + case value_t::array: + { + // delegate call to array_t::empty() + return m_value.array->empty(); + } + + case value_t::object: + { + // delegate call to object_t::empty() + return m_value.object->empty(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types are nonempty + return false; + } + } + } + + /*! + @brief returns the number of elements + + Returns the number of elements in a JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` + boolean | `1` + string | `1` + number | `1` + binary | `1` + object | result of function object_t::size() + array | result of function array_t::size() + + @liveexample{The following code calls `size()` on the different value + types.,size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their size() functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @note This function does not return the length of a string stored as JSON + value - it returns the number of elements in the JSON value which is 1 in + the case of a string. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of `std::distance(begin(), end())`. + + @sa see @ref empty() -- checks whether the container is empty + @sa see @ref max_size() -- returns the maximal number of elements + + @since version 1.0.0 + */ + size_type size() const noexcept + { + switch (m_type) + { + case value_t::null: + { + // null values are empty + return 0; + } + + case value_t::array: + { + // delegate call to array_t::size() + return m_value.array->size(); + } + + case value_t::object: + { + // delegate call to object_t::size() + return m_value.object->size(); + } + + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have size 1 + return 1; + } + } + } + + /*! + @brief returns the maximum possible number of elements + + Returns the maximum number of elements a JSON value is able to hold due to + system or library implementation limitations, i.e. `std::distance(begin(), + end())` for the JSON value. + + @return The return value depends on the different types and is + defined as follows: + Value type | return value + ----------- | ------------- + null | `0` (same as `size()`) + boolean | `1` (same as `size()`) + string | `1` (same as `size()`) + number | `1` (same as `size()`) + binary | `1` (same as `size()`) + object | result of function `object_t::max_size()` + array | result of function `array_t::max_size()` + + @liveexample{The following code calls `max_size()` on the different value + types. Note the output is implementation specific.,max_size} + + @complexity Constant, as long as @ref array_t and @ref object_t satisfy + the Container concept; that is, their `max_size()` functions have constant + complexity. + + @iterators No changes. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @requirement This function helps `basic_json` satisfying the + [Container](https://en.cppreference.com/w/cpp/named_req/Container) + requirements: + - The complexity is constant. + - Has the semantics of returning `b.size()` where `b` is the largest + possible JSON value. + + @sa see @ref size() -- returns the number of elements + + @since version 1.0.0 + */ + size_type max_size() const noexcept + { + switch (m_type) + { + case value_t::array: + { + // delegate call to array_t::max_size() + return m_value.array->max_size(); + } + + case value_t::object: + { + // delegate call to object_t::max_size() + return m_value.object->max_size(); + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // all other types have max_size() == size() + return size(); + } + } + } + + /// @} + + + /////////////// + // modifiers // + /////////////// + + /// @name modifiers + /// @{ + + /*! + @brief clears the contents + + Clears the content of a JSON value and resets it to the default value as + if @ref basic_json(value_t) would have been called with the current value + type from @ref type(): + + Value type | initial value + ----------- | ------------- + null | `null` + boolean | `false` + string | `""` + number | `0` + binary | An empty byte vector + object | `{}` + array | `[]` + + @post Has the same effect as calling + @code {.cpp} + *this = basic_json(type()); + @endcode + + @liveexample{The example below shows the effect of `clear()` to different + JSON types.,clear} + + @complexity Linear in the size of the JSON value. + + @iterators All iterators, pointers and references related to this container + are invalidated. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @sa see @ref basic_json(value_t) -- constructor that creates an object with the + same value than calling `clear()` + + @since version 1.0.0 + */ + void clear() noexcept + { + switch (m_type) + { + case value_t::number_integer: + { + m_value.number_integer = 0; + break; + } + + case value_t::number_unsigned: + { + m_value.number_unsigned = 0; + break; + } + + case value_t::number_float: + { + m_value.number_float = 0.0; + break; + } + + case value_t::boolean: + { + m_value.boolean = false; + break; + } + + case value_t::string: + { + m_value.string->clear(); + break; + } + + case value_t::binary: + { + m_value.binary->clear(); + break; + } + + case value_t::array: + { + m_value.array->clear(); + break; + } + + case value_t::object: + { + m_value.object->clear(); + break; + } + + case value_t::null: + case value_t::discarded: + default: + break; + } + } + + /*! + @brief add an object to an array + + Appends the given element @a val to the end of the JSON value. If the + function is called on a JSON null value, an empty array is created before + appending @a val. + + @param[in] val the value to add to the JSON array + + @throw type_error.308 when called on a type other than JSON array or + null; example: `"cannot use push_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON array. Note how the `null` value was silently + converted to a JSON array.,push_back} + + @since version 1.0.0 + */ + void push_back(basic_json&& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (move semantics) + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(std::move(val)); + set_parent(m_value.array->back(), old_capacity); + // if val is moved from, basic_json move constructor marks it null so we do not call the destructor + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(basic_json&& val) + { + push_back(std::move(val)); + return *this; + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + void push_back(const basic_json& val) + { + // push_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array + const auto old_capacity = m_value.array->capacity(); + m_value.array->push_back(val); + set_parent(m_value.array->back(), old_capacity); + } + + /*! + @brief add an object to an array + @copydoc push_back(basic_json&&) + */ + reference operator+=(const basic_json& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + Inserts the given element @a val to the JSON object. If the function is + called on a JSON null value, an empty object is created before inserting + @a val. + + @param[in] val the value to add to the JSON object + + @throw type_error.308 when called on a type other than JSON object or + null; example: `"cannot use push_back() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `push_back()` and `+=` can be used to + add elements to a JSON object. Note how the `null` value was silently + converted to a JSON object.,push_back__object_t__value} + + @since version 1.0.0 + */ + void push_back(const typename object_t::value_type& val) + { + // push_back only works for null objects or objects + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(308, "cannot use push_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to object + auto res = m_value.object->insert(val); + set_parent(res.first->second); + } + + /*! + @brief add an object to an object + @copydoc push_back(const typename object_t::value_type&) + */ + reference operator+=(const typename object_t::value_type& val) + { + push_back(val); + return *this; + } + + /*! + @brief add an object to an object + + This function allows to use `push_back` with an initializer list. In case + + 1. the current value is an object, + 2. the initializer list @a init contains only two elements, and + 3. the first element of @a init is a string, + + @a init is converted into an object element and added using + @ref push_back(const typename object_t::value_type&). Otherwise, @a init + is converted to a JSON value and added using @ref push_back(basic_json&&). + + @param[in] init an initializer list + + @complexity Linear in the size of the initializer list @a init. + + @note This function is required to resolve an ambiguous overload error, + because pairs like `{"key", "value"}` can be both interpreted as + `object_t::value_type` or `std::initializer_list`, see + https://github.com/nlohmann/json/issues/235 for more information. + + @liveexample{The example shows how initializer lists are treated as + objects when possible.,push_back__initializer_list} + */ + void push_back(initializer_list_t init) + { + if (is_object() && init.size() == 2 && (*init.begin())->is_string()) + { + basic_json&& key = init.begin()->moved_or_copied(); + push_back(typename object_t::value_type( + std::move(key.get_ref()), (init.begin() + 1)->moved_or_copied())); + } + else + { + push_back(basic_json(init)); + } + } + + /*! + @brief add an object to an object + @copydoc push_back(initializer_list_t) + */ + reference operator+=(initializer_list_t init) + { + push_back(init); + return *this; + } + + /*! + @brief add an object to an array + + Creates a JSON value from the passed parameters @a args to the end of the + JSON value. If the function is called on a JSON null value, an empty array + is created before appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return reference to the inserted element + + @throw type_error.311 when called on a type other than JSON array or + null; example: `"cannot use emplace_back() with number"` + + @complexity Amortized constant. + + @liveexample{The example shows how `push_back()` can be used to add + elements to a JSON array. Note how the `null` value was silently converted + to a JSON array.,emplace_back} + + @since version 2.0.8, returns reference since 3.7.0 + */ + template + reference emplace_back(Args&& ... args) + { + // emplace_back only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_array()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace_back() with " + std::string(type_name()), *this)); + } + + // transform null object into an array + if (is_null()) + { + m_type = value_t::array; + m_value = value_t::array; + assert_invariant(); + } + + // add element to array (perfect forwarding) + const auto old_capacity = m_value.array->capacity(); + m_value.array->emplace_back(std::forward(args)...); + return set_parent(m_value.array->back(), old_capacity); + } + + /*! + @brief add an object to an object if key does not exist + + Inserts a new element into a JSON object constructed in-place with the + given @a args if there is no element with the key in the container. If the + function is called on a JSON null value, an empty object is created before + appending the value created from @a args. + + @param[in] args arguments to forward to a constructor of @ref basic_json + @tparam Args compatible types to create a @ref basic_json object + + @return a pair consisting of an iterator to the inserted element, or the + already-existing element if no insertion happened, and a bool + denoting whether the insertion took place. + + @throw type_error.311 when called on a type other than JSON object or + null; example: `"cannot use emplace() with number"` + + @complexity Logarithmic in the size of the container, O(log(`size()`)). + + @liveexample{The example shows how `emplace()` can be used to add elements + to a JSON object. Note how the `null` value was silently converted to a + JSON object. Further note how no value is added if there was already one + value stored with the same key.,emplace} + + @since version 2.0.8 + */ + template + std::pair emplace(Args&& ... args) + { + // emplace only works for null objects or arrays + if (JSON_HEDLEY_UNLIKELY(!(is_null() || is_object()))) + { + JSON_THROW(type_error::create(311, "cannot use emplace() with " + std::string(type_name()), *this)); + } + + // transform null object into an object + if (is_null()) + { + m_type = value_t::object; + m_value = value_t::object; + assert_invariant(); + } + + // add element to array (perfect forwarding) + auto res = m_value.object->emplace(std::forward(args)...); + set_parent(res.first->second); + + // create result iterator and set iterator to the result of emplace + auto it = begin(); + it.m_it.object_iterator = res.first; + + // return pair of iterator and boolean + return {it, res.second}; + } + + /// Helper for insertion of an iterator + /// @note: This uses std::distance to support GCC 4.8, + /// see https://github.com/nlohmann/json/pull/1257 + template + iterator insert_iterator(const_iterator pos, Args&& ... args) + { + iterator result(this); + JSON_ASSERT(m_value.array != nullptr); + + auto insert_pos = std::distance(m_value.array->begin(), pos.m_it.array_iterator); + m_value.array->insert(pos.m_it.array_iterator, std::forward(args)...); + result.m_it.array_iterator = m_value.array->begin() + insert_pos; + + // This could have been written as: + // result.m_it.array_iterator = m_value.array->insert(pos.m_it.array_iterator, cnt, val); + // but the return value of insert is missing in GCC 4.8, so it is written this way instead. + + set_parents(); + return result; + } + + /*! + @brief inserts element + + Inserts element @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] val element to insert + @return iterator pointing to the inserted @a val. + + @throw type_error.309 if called on JSON values other than arrays; + example: `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Constant plus linear in the distance between @a pos and end of + the container. + + @liveexample{The example shows how `insert()` is used.,insert} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /*! + @brief inserts element + @copydoc insert(const_iterator, const basic_json&) + */ + iterator insert(const_iterator pos, basic_json&& val) + { + return insert(pos, val); + } + + /*! + @brief inserts elements + + Inserts @a cnt copies of @a val before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] cnt number of copies of @a val to insert + @param[in] val element to insert + @return iterator pointing to the first element inserted, or @a pos if + `cnt==0` + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @complexity Linear in @a cnt plus linear in the distance between @a pos + and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__count} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, size_type cnt, const basic_json& val) + { + // insert only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, cnt, val); + } + + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)` before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + @throw invalid_iterator.211 if @a first or @a last are iterators into + container for which insert is called; example: `"passed iterators may not + belong to container"` + + @return iterator pointing to the first element inserted, or @a pos if + `first==last` + + @complexity Linear in `std::distance(first, last)` plus linear in the + distance between @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__range} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, const_iterator first, const_iterator last) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + if (JSON_HEDLEY_UNLIKELY(first.m_object == this)) + { + JSON_THROW(invalid_iterator::create(211, "passed iterators may not belong to container", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, first.m_it.array_iterator, last.m_it.array_iterator); + } + + /*! + @brief inserts elements + + Inserts elements from initializer list @a ilist before iterator @a pos. + + @param[in] pos iterator before which the content will be inserted; may be + the end() iterator + @param[in] ilist initializer list to insert the values from + + @throw type_error.309 if called on JSON values other than arrays; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if @a pos is not an iterator of *this; + example: `"iterator does not fit current value"` + + @return iterator pointing to the first element inserted, or @a pos if + `ilist` is empty + + @complexity Linear in `ilist.size()` plus linear in the distance between + @a pos and end of the container. + + @liveexample{The example shows how `insert()` is used.,insert__ilist} + + @since version 1.0.0 + */ + iterator insert(const_iterator pos, initializer_list_t ilist) + { + // insert only works for arrays + if (JSON_HEDLEY_UNLIKELY(!is_array())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if iterator pos fits to this JSON value + if (JSON_HEDLEY_UNLIKELY(pos.m_object != this)) + { + JSON_THROW(invalid_iterator::create(202, "iterator does not fit current value", *this)); + } + + // insert to array and return iterator + return insert_iterator(pos, ilist.begin(), ilist.end()); + } + + /*! + @brief inserts elements + + Inserts elements from range `[first, last)`. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.309 if called on JSON values other than objects; example: + `"cannot use insert() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity Logarithmic: `O(N*log(size() + N))`, where `N` is the number + of elements to insert. + + @liveexample{The example shows how `insert()` is used.,insert__range_object} + + @since version 3.0.0 + */ + void insert(const_iterator first, const_iterator last) + { + // insert only works for objects + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(309, "cannot use insert() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + } + + m_value.object->insert(first.m_it.object_iterator, last.m_it.object_iterator); + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from JSON object @a j and overwrites existing keys. + + @param[in] j JSON object to read values from + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_reference j) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + } + if (JSON_HEDLEY_UNLIKELY(!j.is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(j.type_name()), *this)); + } + + for (auto it = j.cbegin(); it != j.cend(); ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief updates a JSON object from another object, overwriting existing keys + + Inserts all values from from range `[first, last)` and overwrites existing + keys. + + @param[in] first begin of the range of elements to insert + @param[in] last end of the range of elements to insert + + @throw type_error.312 if called on JSON values other than objects; example: + `"cannot use update() with string"` + @throw invalid_iterator.202 if iterator @a first or @a last does does not + point to an object; example: `"iterators first and last must point to + objects"` + @throw invalid_iterator.210 if @a first and @a last do not belong to the + same JSON value; example: `"iterators do not fit"` + + @complexity O(N*log(size() + N)), where N is the number of elements to + insert. + + @liveexample{The example shows how `update()` is used__range.,update} + + @sa https://docs.python.org/3.6/library/stdtypes.html#dict.update + + @since version 3.0.0 + */ + void update(const_iterator first, const_iterator last) + { + // implicitly convert null value to an empty object + if (is_null()) + { + m_type = value_t::object; + m_value.object = create(); + assert_invariant(); + } + + if (JSON_HEDLEY_UNLIKELY(!is_object())) + { + JSON_THROW(type_error::create(312, "cannot use update() with " + std::string(type_name()), *this)); + } + + // check if range iterators belong to the same JSON object + if (JSON_HEDLEY_UNLIKELY(first.m_object != last.m_object)) + { + JSON_THROW(invalid_iterator::create(210, "iterators do not fit", *this)); + } + + // passed iterators must belong to objects + if (JSON_HEDLEY_UNLIKELY(!first.m_object->is_object() + || !last.m_object->is_object())) + { + JSON_THROW(invalid_iterator::create(202, "iterators first and last must point to objects", *this)); + } + + for (auto it = first; it != last; ++it) + { + m_value.object->operator[](it.key()) = it.value(); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + void swap(reference other) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + std::swap(m_type, other.m_type); + std::swap(m_value, other.m_value); + + set_parents(); + other.set_parents(); + assert_invariant(); + } + + /*! + @brief exchanges the values + + Exchanges the contents of the JSON value from @a left with those of @a right. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. implemented as a friend function callable via ADL. + + @param[in,out] left JSON value to exchange the contents with + @param[in,out] right JSON value to exchange the contents with + + @complexity Constant. + + @liveexample{The example below shows how JSON values can be swapped with + `swap()`.,swap__reference} + + @since version 1.0.0 + */ + friend void swap(reference left, reference right) noexcept ( + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value&& + std::is_nothrow_move_constructible::value&& + std::is_nothrow_move_assignable::value + ) + { + left.swap(right); + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON array with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other array to exchange the contents with + + @throw type_error.310 when JSON value is not an array; example: `"cannot + use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how arrays can be swapped with + `swap()`.,swap__array_t} + + @since version 1.0.0 + */ + void swap(array_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for arrays + if (JSON_HEDLEY_LIKELY(is_array())) + { + std::swap(*(m_value.array), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON object with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other object to exchange the contents with + + @throw type_error.310 when JSON value is not an object; example: + `"cannot use swap() with string"` + + @complexity Constant. + + @liveexample{The example below shows how objects can be swapped with + `swap()`.,swap__object_t} + + @since version 1.0.0 + */ + void swap(object_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for objects + if (JSON_HEDLEY_LIKELY(is_object())) + { + std::swap(*(m_value.object), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other string to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__string_t} + + @since version 1.0.0 + */ + void swap(string_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_string())) + { + std::swap(*(m_value.string), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /*! + @brief exchanges the values + + Exchanges the contents of a JSON string with those of @a other. Does not + invoke any move, copy, or swap operations on individual elements. All + iterators and references remain valid. The past-the-end iterator is + invalidated. + + @param[in,out] other binary to exchange the contents with + + @throw type_error.310 when JSON value is not a string; example: `"cannot + use swap() with boolean"` + + @complexity Constant. + + @liveexample{The example below shows how strings can be swapped with + `swap()`.,swap__binary_t} + + @since version 3.8.0 + */ + void swap(binary_t& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @copydoc swap(binary_t&) + void swap(typename binary_t::container_type& other) // NOLINT(bugprone-exception-escape) + { + // swap only works for strings + if (JSON_HEDLEY_LIKELY(is_binary())) + { + std::swap(*(m_value.binary), other); + } + else + { + JSON_THROW(type_error::create(310, "cannot use swap() with " + std::string(type_name()), *this)); + } + } + + /// @} + + public: + ////////////////////////////////////////// + // lexicographical comparison operators // + ////////////////////////////////////////// + + /// @name lexicographical comparison operators + /// @{ + + /*! + @brief comparison: equal + + Compares two JSON values for equality according to the following rules: + - Two JSON values are equal if (1) they are from the same type and (2) + their stored values are the same according to their respective + `operator==`. + - Integer and floating-point numbers are automatically converted before + comparison. Note that two NaN values are always treated as unequal. + - Two JSON null values are equal. + + @note Floating-point inside JSON values numbers are compared with + `json::number_float_t::operator==` which is `double::operator==` by + default. To compare floating-point while respecting an epsilon, an alternative + [comparison function](https://github.com/mariokonrad/marnav/blob/master/include/marnav/math/floatingpoint.hpp#L34-#L39) + could be used, for instance + @code {.cpp} + template::value, T>::type> + inline bool is_same(T a, T b, T epsilon = std::numeric_limits::epsilon()) noexcept + { + return std::abs(a - b) <= epsilon; + } + @endcode + Or you can self-defined operator equal function like this: + @code {.cpp} + bool my_equal(const_reference lhs, const_reference rhs) { + const auto lhs_type lhs.type(); + const auto rhs_type rhs.type(); + if (lhs_type == rhs_type) { + switch(lhs_type) + // self_defined case + case value_t::number_float: + return std::abs(lhs - rhs) <= std::numeric_limits::epsilon(); + // other cases remain the same with the original + ... + } + ... + } + @endcode + + @note NaN values never compare equal to themselves or to other NaN values. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are equal + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Linear. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__equal} + + @since version 1.0.0 + */ + friend bool operator==(const_reference lhs, const_reference rhs) noexcept + { +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + return *lhs.m_value.array == *rhs.m_value.array; + + case value_t::object: + return *lhs.m_value.object == *rhs.m_value.object; + + case value_t::null: + return true; + + case value_t::string: + return *lhs.m_value.string == *rhs.m_value.string; + + case value_t::boolean: + return lhs.m_value.boolean == rhs.m_value.boolean; + + case value_t::number_integer: + return lhs.m_value.number_integer == rhs.m_value.number_integer; + + case value_t::number_unsigned: + return lhs.m_value.number_unsigned == rhs.m_value.number_unsigned; + + case value_t::number_float: + return lhs.m_value.number_float == rhs.m_value.number_float; + + case value_t::binary: + return *lhs.m_value.binary == *rhs.m_value.binary; + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float == static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) == rhs.m_value.number_integer; + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer == static_cast(rhs.m_value.number_unsigned); + } + + return false; +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(const_reference lhs, ScalarType rhs) noexcept + { + return lhs == basic_json(rhs); + } + + /*! + @brief comparison: equal + @copydoc operator==(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator==(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) == rhs; + } + + /*! + @brief comparison: not equal + + Compares two JSON values for inequality by calculating `not (lhs == rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether the values @a lhs and @a rhs are not equal + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__notequal} + + @since version 1.0.0 + */ + friend bool operator!=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs == rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs != basic_json(rhs); + } + + /*! + @brief comparison: not equal + @copydoc operator!=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator!=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) != rhs; + } + + /*! + @brief comparison: less than + + Compares whether one JSON value @a lhs is less than another JSON value @a + rhs according to the following rules: + - If @a lhs and @a rhs have the same type, the values are compared using + the default `<` operator. + - Integer and floating-point numbers are automatically converted before + comparison + - In case @a lhs and @a rhs have different types, the values are ignored + and the order of the types is considered, see + @ref operator<(const value_t, const value_t). + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__less} + + @since version 1.0.0 + */ + friend bool operator<(const_reference lhs, const_reference rhs) noexcept + { + const auto lhs_type = lhs.type(); + const auto rhs_type = rhs.type(); + + if (lhs_type == rhs_type) + { + switch (lhs_type) + { + case value_t::array: + // note parentheses are necessary, see + // https://github.com/nlohmann/json/issues/1530 + return (*lhs.m_value.array) < (*rhs.m_value.array); + + case value_t::object: + return (*lhs.m_value.object) < (*rhs.m_value.object); + + case value_t::null: + return false; + + case value_t::string: + return (*lhs.m_value.string) < (*rhs.m_value.string); + + case value_t::boolean: + return (lhs.m_value.boolean) < (rhs.m_value.boolean); + + case value_t::number_integer: + return (lhs.m_value.number_integer) < (rhs.m_value.number_integer); + + case value_t::number_unsigned: + return (lhs.m_value.number_unsigned) < (rhs.m_value.number_unsigned); + + case value_t::number_float: + return (lhs.m_value.number_float) < (rhs.m_value.number_float); + + case value_t::binary: + return (*lhs.m_value.binary) < (*rhs.m_value.binary); + + case value_t::discarded: + default: + return false; + } + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_integer) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_integer) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_integer); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_float) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_float; + } + else if (lhs_type == value_t::number_float && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_float < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_integer && rhs_type == value_t::number_unsigned) + { + return lhs.m_value.number_integer < static_cast(rhs.m_value.number_unsigned); + } + else if (lhs_type == value_t::number_unsigned && rhs_type == value_t::number_integer) + { + return static_cast(lhs.m_value.number_unsigned) < rhs.m_value.number_integer; + } + + // We only reach this line if we cannot compare values. In that case, + // we compare types. Note we have to call the operator explicitly, + // because MSVC has problems otherwise. + return operator<(lhs_type, rhs_type); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(const_reference lhs, ScalarType rhs) noexcept + { + return lhs < basic_json(rhs); + } + + /*! + @brief comparison: less than + @copydoc operator<(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) < rhs; + } + + /*! + @brief comparison: less than or equal + + Compares whether one JSON value @a lhs is less than or equal to another + JSON value by calculating `not (rhs < lhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is less than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greater} + + @since version 1.0.0 + */ + friend bool operator<=(const_reference lhs, const_reference rhs) noexcept + { + return !(rhs < lhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs <= basic_json(rhs); + } + + /*! + @brief comparison: less than or equal + @copydoc operator<=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator<=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) <= rhs; + } + + /*! + @brief comparison: greater than + + Compares whether one JSON value @a lhs is greater than another + JSON value by calculating `not (lhs <= rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__lessequal} + + @since version 1.0.0 + */ + friend bool operator>(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs <= rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(const_reference lhs, ScalarType rhs) noexcept + { + return lhs > basic_json(rhs); + } + + /*! + @brief comparison: greater than + @copydoc operator>(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) > rhs; + } + + /*! + @brief comparison: greater than or equal + + Compares whether one JSON value @a lhs is greater than or equal to another + JSON value by calculating `not (lhs < rhs)`. + + @param[in] lhs first JSON value to consider + @param[in] rhs second JSON value to consider + @return whether @a lhs is greater than or equal to @a rhs + + @complexity Linear. + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @liveexample{The example demonstrates comparing several JSON + types.,operator__greaterequal} + + @since version 1.0.0 + */ + friend bool operator>=(const_reference lhs, const_reference rhs) noexcept + { + return !(lhs < rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(const_reference lhs, ScalarType rhs) noexcept + { + return lhs >= basic_json(rhs); + } + + /*! + @brief comparison: greater than or equal + @copydoc operator>=(const_reference, const_reference) + */ + template::value, int>::type = 0> + friend bool operator>=(ScalarType lhs, const_reference rhs) noexcept + { + return basic_json(lhs) >= rhs; + } + + /// @} + + /////////////////// + // serialization // + /////////////////// + + /// @name serialization + /// @{ +#ifndef JSON_NO_IO + /*! + @brief serialize to stream + + Serialize the given JSON value @a j to the output stream @a o. The JSON + value will be serialized using the @ref dump member function. + + - The indentation of the output can be controlled with the member variable + `width` of the output stream @a o. For instance, using the manipulator + `std::setw(4)` on @a o sets the indentation level to `4` and the + serialization result is the same as calling `dump(4)`. + + - The indentation character can be controlled with the member variable + `fill` of the output stream @a o. For instance, the manipulator + `std::setfill('\\t')` sets indentation to use a tab character rather than + the default space character. + + @param[in,out] o stream to serialize to + @param[in] j JSON value to serialize + + @return the stream @a o + + @throw type_error.316 if a string stored inside the JSON value is not + UTF-8 encoded + + @complexity Linear. + + @liveexample{The example below shows the serialization with different + parameters to `width` to adjust the indentation level.,operator_serialize} + + @since version 1.0.0; indentation character added in version 3.0.0 + */ + friend std::ostream& operator<<(std::ostream& o, const basic_json& j) + { + // read width member and use it as indentation parameter if nonzero + const bool pretty_print = o.width() > 0; + const auto indentation = pretty_print ? o.width() : 0; + + // reset width to 0 for subsequent calls to this stream + o.width(0); + + // do the actual serialization + serializer s(detail::output_adapter(o), o.fill()); + s.dump(j, pretty_print, false, static_cast(indentation)); + return o; + } + + /*! + @brief serialize to stream + @deprecated This stream operator is deprecated and will be removed in + future 4.0.0 of the library. Please use + @ref operator<<(std::ostream&, const basic_json&) + instead; that is, replace calls like `j >> o;` with `o << j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator<<(std::ostream&, const basic_json&)) + friend std::ostream& operator>>(const basic_json& j, std::ostream& o) + { + return o << j; + } +#endif // JSON_NO_IO + /// @} + + + ///////////////////// + // deserialization // + ///////////////////// + + /// @name deserialization + /// @{ + + /*! + @brief deserialize from a compatible input + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the parser callback function + @a cb or reading from the input @a i has a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `parse()` function reading + from an array.,parse__array__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__string__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function with + and without callback function.,parse__istream__parser_callback_t} + + @liveexample{The example below demonstrates the `parse()` function reading + from a contiguous container.,parse__contiguouscontainer__parser_callback_t} + + @since version 2.0.3 (contiguous containers); version 3.9.0 allowed to + ignore comments. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(InputType&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::forward(i)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /*! + @brief deserialize from a pair of character iterators + + The value_type of the iterator must be a integral type with size of 1, 2 or + 4 bytes, which will be interpreted respectively as UTF-8, UTF-16 and UTF-32. + + @param[in] first iterator to start of character range + @param[in] last iterator to end of character range + @param[in] cb a parser callback function of type @ref parser_callback_t + which is used to control the deserialization by filtering unwanted values + (optional) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json parse(IteratorType first, + IteratorType last, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(detail::input_adapter(std::move(first), std::move(last)), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, parse(ptr, ptr + len)) + static basic_json parse(detail::span_input_adapter&& i, + const parser_callback_t cb = nullptr, + const bool allow_exceptions = true, + const bool ignore_comments = false) + { + basic_json result; + parser(i.get(), cb, allow_exceptions, ignore_comments).parse(true, result); + return result; + } + + /*! + @brief check if the input is valid JSON + + Unlike the @ref parse(InputType&&, const parser_callback_t,const bool) + function, this function neither throws an exception in case of invalid JSON + input (i.e., a parse error) nor creates diagnostic information. + + @tparam InputType A compatible input, for instance + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default) + + @return Whether the input read from @a i is valid JSON. + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `accept()` function reading + from a string.,accept__string} + */ + template + static bool accept(InputType&& i, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::forward(i)), nullptr, false, ignore_comments).accept(true); + } + + template + static bool accept(IteratorType first, IteratorType last, + const bool ignore_comments = false) + { + return parser(detail::input_adapter(std::move(first), std::move(last)), nullptr, false, ignore_comments).accept(true); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, accept(ptr, ptr + len)) + static bool accept(detail::span_input_adapter&& i, + const bool ignore_comments = false) + { + return parser(i.get(), nullptr, false, ignore_comments).accept(true); + } + + /*! + @brief generate SAX events + + The SAX event lister must follow the interface of @ref json_sax. + + This function reads from a compatible input. Examples are: + - an std::istream object + - a FILE pointer + - a C-style array of characters + - a pointer to a null-terminated string of single byte characters + - an object obj for which begin(obj) and end(obj) produces a valid pair of + iterators. + + @param[in] i input to read from + @param[in,out] sax SAX event listener + @param[in] format the format to parse (JSON, CBOR, MessagePack, or UBJSON) + @param[in] strict whether the input has to be consumed completely + @param[in] ignore_comments whether comments should be ignored and treated + like whitespace (true) or yield a parse error (true); (optional, false by + default); only applies to the JSON file format. + + @return return value of the last processed SAX event + + @throw parse_error.101 if a parse error occurs; example: `""unexpected end + of input; expected string literal""` + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. The complexity can be higher if the SAX consumer @a sax has + a super-linear complexity. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below demonstrates the `sax_parse()` function + reading from string and processing the events with a user-defined SAX + event consumer.,sax_parse} + + @since version 3.2.0 + */ + template + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(InputType&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::forward(i)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + template + JSON_HEDLEY_NON_NULL(3) + static bool sax_parse(IteratorType first, IteratorType last, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = detail::input_adapter(std::move(first), std::move(last)); + return format == input_format_t::json + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } + + template + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, sax_parse(ptr, ptr + len, ...)) + JSON_HEDLEY_NON_NULL(2) + static bool sax_parse(detail::span_input_adapter&& i, SAX* sax, + input_format_t format = input_format_t::json, + const bool strict = true, + const bool ignore_comments = false) + { + auto ia = i.get(); + return format == input_format_t::json + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + ? parser(std::move(ia), nullptr, true, ignore_comments).sax_parse(sax, strict) + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + : detail::binary_reader(std::move(ia)).sax_parse(format, sax, strict); + } +#ifndef JSON_NO_IO + /*! + @brief deserialize from stream + @deprecated This stream operator is deprecated and will be removed in + version 4.0.0 of the library. Please use + @ref operator>>(std::istream&, basic_json&) + instead; that is, replace calls like `j << i;` with `i >> j;`. + @since version 1.0.0; deprecated since version 3.0.0 + */ + JSON_HEDLEY_DEPRECATED_FOR(3.0.0, operator>>(std::istream&, basic_json&)) + friend std::istream& operator<<(basic_json& j, std::istream& i) + { + return operator>>(i, j); + } + + /*! + @brief deserialize from stream + + Deserializes an input stream to a JSON value. + + @param[in,out] i input stream to read a serialized JSON value from + @param[in,out] j JSON value to write the deserialized input to + + @throw parse_error.101 in case of an unexpected token + @throw parse_error.102 if to_unicode fails or surrogate error + @throw parse_error.103 if to_unicode fails + + @complexity Linear in the length of the input. The parser is a predictive + LL(1) parser. + + @note A UTF-8 byte order mark is silently ignored. + + @liveexample{The example below shows how a JSON value is constructed by + reading a serialization from a stream.,operator_deserialize} + + @sa parse(std::istream&, const parser_callback_t) for a variant with a + parser callback function to filter values while parsing + + @since version 1.0.0 + */ + friend std::istream& operator>>(std::istream& i, basic_json& j) + { + parser(detail::input_adapter(i)).parse(false, j); + return i; + } +#endif // JSON_NO_IO + /// @} + + /////////////////////////// + // convenience functions // + /////////////////////////// + + /*! + @brief return the type as string + + Returns the type name as string to be used in error messages - usually to + indicate that a function was called on a wrong JSON type. + + @return a string representation of a the @a m_type member: + Value type | return value + ----------- | ------------- + null | `"null"` + boolean | `"boolean"` + string | `"string"` + number | `"number"` (for all number types) + object | `"object"` + array | `"array"` + binary | `"binary"` + discarded | `"discarded"` + + @exceptionsafety No-throw guarantee: this function never throws exceptions. + + @complexity Constant. + + @liveexample{The following code exemplifies `type_name()` for all JSON + types.,type_name} + + @sa see @ref type() -- return the type of the JSON value + @sa see @ref operator value_t() -- return the type of the JSON value (implicit) + + @since version 1.0.0, public since 2.1.0, `const char*` and `noexcept` + since 3.0.0 + */ + JSON_HEDLEY_RETURNS_NON_NULL + const char* type_name() const noexcept + { + { + switch (m_type) + { + case value_t::null: + return "null"; + case value_t::object: + return "object"; + case value_t::array: + return "array"; + case value_t::string: + return "string"; + case value_t::boolean: + return "boolean"; + case value_t::binary: + return "binary"; + case value_t::discarded: + return "discarded"; + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + default: + return "number"; + } + } + } + + + JSON_PRIVATE_UNLESS_TESTED: + ////////////////////// + // member variables // + ////////////////////// + + /// the type of the current element + value_t m_type = value_t::null; + + /// the value of the current element + json_value m_value = {}; + +#if JSON_DIAGNOSTICS + /// a pointer to a parent value (for debugging purposes) + basic_json* m_parent = nullptr; +#endif + + ////////////////////////////////////////// + // binary serialization/deserialization // + ////////////////////////////////////////// + + /// @name binary serialization/deserialization support + /// @{ + + public: + /*! + @brief create a CBOR serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the CBOR (Concise + Binary Object Representation) serialization format. CBOR is a binary + serialization format which aims to be more compact than JSON itself, yet + more efficient to parse. + + The library uses the following mapping from JSON values types to + CBOR types according to the CBOR specification (RFC 7049): + + JSON value type | value/range | CBOR type | first byte + --------------- | ------------------------------------------ | ---------------------------------- | --------------- + null | `null` | Null | 0xF6 + boolean | `true` | True | 0xF5 + boolean | `false` | False | 0xF4 + number_integer | -9223372036854775808..-2147483649 | Negative integer (8 bytes follow) | 0x3B + number_integer | -2147483648..-32769 | Negative integer (4 bytes follow) | 0x3A + number_integer | -32768..-129 | Negative integer (2 bytes follow) | 0x39 + number_integer | -128..-25 | Negative integer (1 byte follow) | 0x38 + number_integer | -24..-1 | Negative integer | 0x20..0x37 + number_integer | 0..23 | Integer | 0x00..0x17 + number_integer | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_integer | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_integer | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_integer | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_unsigned | 0..23 | Integer | 0x00..0x17 + number_unsigned | 24..255 | Unsigned integer (1 byte follow) | 0x18 + number_unsigned | 256..65535 | Unsigned integer (2 bytes follow) | 0x19 + number_unsigned | 65536..4294967295 | Unsigned integer (4 bytes follow) | 0x1A + number_unsigned | 4294967296..18446744073709551615 | Unsigned integer (8 bytes follow) | 0x1B + number_float | *any value representable by a float* | Single-Precision Float | 0xFA + number_float | *any value NOT representable by a float* | Double-Precision Float | 0xFB + string | *length*: 0..23 | UTF-8 string | 0x60..0x77 + string | *length*: 23..255 | UTF-8 string (1 byte follow) | 0x78 + string | *length*: 256..65535 | UTF-8 string (2 bytes follow) | 0x79 + string | *length*: 65536..4294967295 | UTF-8 string (4 bytes follow) | 0x7A + string | *length*: 4294967296..18446744073709551615 | UTF-8 string (8 bytes follow) | 0x7B + array | *size*: 0..23 | array | 0x80..0x97 + array | *size*: 23..255 | array (1 byte follow) | 0x98 + array | *size*: 256..65535 | array (2 bytes follow) | 0x99 + array | *size*: 65536..4294967295 | array (4 bytes follow) | 0x9A + array | *size*: 4294967296..18446744073709551615 | array (8 bytes follow) | 0x9B + object | *size*: 0..23 | map | 0xA0..0xB7 + object | *size*: 23..255 | map (1 byte follow) | 0xB8 + object | *size*: 256..65535 | map (2 bytes follow) | 0xB9 + object | *size*: 65536..4294967295 | map (4 bytes follow) | 0xBA + object | *size*: 4294967296..18446744073709551615 | map (8 bytes follow) | 0xBB + binary | *size*: 0..23 | byte string | 0x40..0x57 + binary | *size*: 23..255 | byte string (1 byte follow) | 0x58 + binary | *size*: 256..65535 | byte string (2 bytes follow) | 0x59 + binary | *size*: 65536..4294967295 | byte string (4 bytes follow) | 0x5A + binary | *size*: 4294967296..18446744073709551615 | byte string (8 bytes follow) | 0x5B + + Binary values with subtype are mapped to tagged values (0xD8..0xDB) + depending on the subtype, followed by a byte string, see "binary" cells + in the table above. + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a CBOR value. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The following CBOR types are not used in the conversion: + - UTF-8 strings terminated by "break" (0x7F) + - arrays terminated by "break" (0x9F) + - maps terminated by "break" (0xBF) + - byte strings terminated by "break" (0x5F) + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + - half-precision floats (0xF9) + - break (0xFF) + + @param[in] j JSON value to serialize + @return CBOR serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in CBOR format.,to_cbor} + + @sa http://cbor.io + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + analogous deserialization + @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9; compact representation of floating-point numbers + since version 3.8.0 + */ + static std::vector to_cbor(const basic_json& j) + { + std::vector result; + to_cbor(j, result); + return result; + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + static void to_cbor(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_cbor(j); + } + + /*! + @brief create a MessagePack serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the MessagePack + serialization format. MessagePack is a binary serialization format which + aims to be more compact than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + MessagePack types according to the MessagePack specification: + + JSON value type | value/range | MessagePack type | first byte + --------------- | --------------------------------- | ---------------- | ---------- + null | `null` | nil | 0xC0 + boolean | `true` | true | 0xC3 + boolean | `false` | false | 0xC2 + number_integer | -9223372036854775808..-2147483649 | int64 | 0xD3 + number_integer | -2147483648..-32769 | int32 | 0xD2 + number_integer | -32768..-129 | int16 | 0xD1 + number_integer | -128..-33 | int8 | 0xD0 + number_integer | -32..-1 | negative fixint | 0xE0..0xFF + number_integer | 0..127 | positive fixint | 0x00..0x7F + number_integer | 128..255 | uint 8 | 0xCC + number_integer | 256..65535 | uint 16 | 0xCD + number_integer | 65536..4294967295 | uint 32 | 0xCE + number_integer | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_unsigned | 0..127 | positive fixint | 0x00..0x7F + number_unsigned | 128..255 | uint 8 | 0xCC + number_unsigned | 256..65535 | uint 16 | 0xCD + number_unsigned | 65536..4294967295 | uint 32 | 0xCE + number_unsigned | 4294967296..18446744073709551615 | uint 64 | 0xCF + number_float | *any value representable by a float* | float 32 | 0xCA + number_float | *any value NOT representable by a float* | float 64 | 0xCB + string | *length*: 0..31 | fixstr | 0xA0..0xBF + string | *length*: 32..255 | str 8 | 0xD9 + string | *length*: 256..65535 | str 16 | 0xDA + string | *length*: 65536..4294967295 | str 32 | 0xDB + array | *size*: 0..15 | fixarray | 0x90..0x9F + array | *size*: 16..65535 | array 16 | 0xDC + array | *size*: 65536..4294967295 | array 32 | 0xDD + object | *size*: 0..15 | fix map | 0x80..0x8F + object | *size*: 16..65535 | map 16 | 0xDE + object | *size*: 65536..4294967295 | map 32 | 0xDF + binary | *size*: 0..255 | bin 8 | 0xC4 + binary | *size*: 256..65535 | bin 16 | 0xC5 + binary | *size*: 65536..4294967295 | bin 32 | 0xC6 + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a MessagePack value. + + @note The following values can **not** be converted to a MessagePack value: + - strings with more than 4294967295 bytes + - byte strings with more than 4294967295 bytes + - arrays with more than 4294967295 elements + - objects with more than 4294967295 elements + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @param[in] j JSON value to serialize + @return MessagePack serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in MessagePack format.,to_msgpack} + + @sa http://msgpack.org + @sa see @ref from_msgpack for the analogous deserialization + @sa see @ref to_cbor(const basic_json& for the related CBOR format + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9 + */ + static std::vector to_msgpack(const basic_json& j) + { + std::vector result; + to_msgpack(j, result); + return result; + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + static void to_msgpack(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_msgpack(j); + } + + /*! + @brief create a UBJSON serialization of a given JSON value + + Serializes a given JSON value @a j to a byte vector using the UBJSON + (Universal Binary JSON) serialization format. UBJSON aims to be more compact + than JSON itself, yet more efficient to parse. + + The library uses the following mapping from JSON values types to + UBJSON types according to the UBJSON specification: + + JSON value type | value/range | UBJSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | `Z` + boolean | `true` | true | `T` + boolean | `false` | false | `F` + number_integer | -9223372036854775808..-2147483649 | int64 | `L` + number_integer | -2147483648..-32769 | int32 | `l` + number_integer | -32768..-129 | int16 | `I` + number_integer | -128..127 | int8 | `i` + number_integer | 128..255 | uint8 | `U` + number_integer | 256..32767 | int16 | `I` + number_integer | 32768..2147483647 | int32 | `l` + number_integer | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 0..127 | int8 | `i` + number_unsigned | 128..255 | uint8 | `U` + number_unsigned | 256..32767 | int16 | `I` + number_unsigned | 32768..2147483647 | int32 | `l` + number_unsigned | 2147483648..9223372036854775807 | int64 | `L` + number_unsigned | 2147483649..18446744073709551615 | high-precision | `H` + number_float | *any value* | float64 | `D` + string | *with shortest length indicator* | string | `S` + array | *see notes on optimized format* | array | `[` + object | *see notes on optimized format* | map | `{` + + @note The mapping is **complete** in the sense that any JSON value type + can be converted to a UBJSON value. + + @note The following values can **not** be converted to a UBJSON value: + - strings with more than 9223372036854775807 bytes (theoretical) + + @note The following markers are not used in the conversion: + - `Z`: no-op values are not created. + - `C`: single-byte strings are serialized with `S` markers. + + @note Any UBJSON output created @ref to_ubjson can be successfully parsed + by @ref from_ubjson. + + @note If NaN or Infinity are stored inside a JSON number, they are + serialized properly. This behavior differs from the @ref dump() + function which serializes NaN or Infinity to `null`. + + @note The optimized formats for containers are supported: Parameter + @a use_size adds size information to the beginning of a container and + removes the closing marker. Parameter @a use_type further checks + whether all elements of a container have the same type and adds the + type marker to the beginning of the container. The @a use_type + parameter must only be used together with @a use_size = true. Note + that @a use_size = true alone may result in larger representations - + the benefit of this parameter is that the receiving side is + immediately informed on the number of elements of the container. + + @note If the JSON data contains the binary type, the value stored is a list + of integers, as suggested by the UBJSON documentation. In particular, + this means that serialization and the deserialization of a JSON + containing binary values into UBJSON and back will result in a + different JSON object. + + @param[in] j JSON value to serialize + @param[in] use_size whether to add size annotations to container types + @param[in] use_type whether to add type annotations to container types + (must be combined with @a use_size = true) + @return UBJSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in UBJSON format.,to_ubjson} + + @sa http://ubjson.org + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the + analogous deserialization + @sa see @ref to_cbor(const basic_json& for the related CBOR format + @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format + + @since version 3.1.0 + */ + static std::vector to_ubjson(const basic_json& j, + const bool use_size = false, + const bool use_type = false) + { + std::vector result; + to_ubjson(j, result, use_size, use_type); + return result; + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + static void to_ubjson(const basic_json& j, detail::output_adapter o, + const bool use_size = false, const bool use_type = false) + { + binary_writer(o).write_ubjson(j, use_size, use_type); + } + + + /*! + @brief Serializes the given JSON object `j` to BSON and returns a vector + containing the corresponding BSON-representation. + + BSON (Binary JSON) is a binary format in which zero or more ordered key/value pairs are + stored as a single entity (a so-called document). + + The library uses the following mapping from JSON values types to BSON types: + + JSON value type | value/range | BSON type | marker + --------------- | --------------------------------- | ----------- | ------ + null | `null` | null | 0x0A + boolean | `true`, `false` | boolean | 0x08 + number_integer | -9223372036854775808..-2147483649 | int64 | 0x12 + number_integer | -2147483648..2147483647 | int32 | 0x10 + number_integer | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 0..2147483647 | int32 | 0x10 + number_unsigned | 2147483648..9223372036854775807 | int64 | 0x12 + number_unsigned | 9223372036854775808..18446744073709551615| -- | -- + number_float | *any value* | double | 0x01 + string | *any value* | string | 0x02 + array | *any value* | document | 0x04 + object | *any value* | document | 0x03 + binary | *any value* | binary | 0x05 + + @warning The mapping is **incomplete**, since only JSON-objects (and things + contained therein) can be serialized to BSON. + Also, integers larger than 9223372036854775807 cannot be serialized to BSON, + and the keys may not contain U+0000, since they are serialized a + zero-terminated c-strings. + + @throw out_of_range.407 if `j.is_number_unsigned() && j.get() > 9223372036854775807` + @throw out_of_range.409 if a key in `j` contains a NULL (U+0000) + @throw type_error.317 if `!j.is_object()` + + @pre The input `j` is required to be an object: `j.is_object() == true`. + + @note Any BSON output created via @ref to_bson can be successfully parsed + by @ref from_bson. + + @param[in] j JSON value to serialize + @return BSON serialization as byte vector + + @complexity Linear in the size of the JSON value @a j. + + @liveexample{The example shows the serialization of a JSON value to a byte + vector in BSON format.,to_bson} + + @sa http://bsonspec.org/spec.html + @sa see @ref from_bson(detail::input_adapter&&, const bool strict) for the + analogous deserialization + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + related UBJSON format + @sa see @ref to_cbor(const basic_json&) for the related CBOR format + @sa see @ref to_msgpack(const basic_json&) for the related MessagePack format + */ + static std::vector to_bson(const basic_json& j) + { + std::vector result; + to_bson(j, result); + return result; + } + + /*! + @brief Serializes the given JSON object `j` to BSON and forwards the + corresponding BSON-representation to the given output_adapter `o`. + @param j The JSON object to convert to BSON. + @param o The output adapter that receives the binary BSON representation. + @pre The input `j` shall be an object: `j.is_object() == true` + @sa see @ref to_bson(const basic_json&) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + /*! + @copydoc to_bson(const basic_json&, detail::output_adapter) + */ + static void to_bson(const basic_json& j, detail::output_adapter o) + { + binary_writer(o).write_bson(j); + } + + + /*! + @brief create a JSON value from an input in CBOR format + + Deserializes a given input @a i to a JSON value using the CBOR (Concise + Binary Object Representation) serialization format. + + The library maps CBOR types to JSON value types as follows: + + CBOR type | JSON value type | first byte + ---------------------- | --------------- | ---------- + Integer | number_unsigned | 0x00..0x17 + Unsigned integer | number_unsigned | 0x18 + Unsigned integer | number_unsigned | 0x19 + Unsigned integer | number_unsigned | 0x1A + Unsigned integer | number_unsigned | 0x1B + Negative integer | number_integer | 0x20..0x37 + Negative integer | number_integer | 0x38 + Negative integer | number_integer | 0x39 + Negative integer | number_integer | 0x3A + Negative integer | number_integer | 0x3B + Byte string | binary | 0x40..0x57 + Byte string | binary | 0x58 + Byte string | binary | 0x59 + Byte string | binary | 0x5A + Byte string | binary | 0x5B + UTF-8 string | string | 0x60..0x77 + UTF-8 string | string | 0x78 + UTF-8 string | string | 0x79 + UTF-8 string | string | 0x7A + UTF-8 string | string | 0x7B + UTF-8 string | string | 0x7F + array | array | 0x80..0x97 + array | array | 0x98 + array | array | 0x99 + array | array | 0x9A + array | array | 0x9B + array | array | 0x9F + map | object | 0xA0..0xB7 + map | object | 0xB8 + map | object | 0xB9 + map | object | 0xBA + map | object | 0xBB + map | object | 0xBF + False | `false` | 0xF4 + True | `true` | 0xF5 + Null | `null` | 0xF6 + Half-Precision Float | number_float | 0xF9 + Single-Precision Float | number_float | 0xFA + Double-Precision Float | number_float | 0xFB + + @warning The mapping is **incomplete** in the sense that not all CBOR + types can be converted to a JSON value. The following CBOR types + are not supported and will yield parse errors (parse_error.112): + - date/time (0xC0..0xC1) + - bignum (0xC2..0xC3) + - decimal fraction (0xC4) + - bigfloat (0xC5) + - expected conversions (0xD5..0xD7) + - simple values (0xE0..0xF3, 0xF8) + - undefined (0xF7) + + @warning CBOR allows map keys of any type, whereas JSON only allows + strings as keys in object values. Therefore, CBOR maps with keys + other than UTF-8 strings are rejected (parse_error.113). + + @note Any CBOR output created @ref to_cbor can be successfully parsed by + @ref from_cbor. + + @param[in] i an input in CBOR format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + @param[in] tag_handler how to treat CBOR tags (optional, error by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from CBOR were + used in the given input @a v or if the input is not valid CBOR + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in CBOR + format to a JSON value.,from_cbor} + + @sa http://cbor.io + @sa see @ref to_cbor(const basic_json&) for the analogous serialization + @sa see @ref from_msgpack(InputType&&, const bool, const bool) for the + related MessagePack format + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the + related UBJSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0; added @a tag_handler parameter since 3.9.0. + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_cbor(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + return from_cbor(ptr, ptr + len, strict, allow_exceptions, tag_handler); + } + + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_cbor(ptr, ptr + len)) + static basic_json from_cbor(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true, + const cbor_tag_handler_t tag_handler = cbor_tag_handler_t::error) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::cbor, &sdp, strict, tag_handler); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @brief create a JSON value from an input in MessagePack format + + Deserializes a given input @a i to a JSON value using the MessagePack + serialization format. + + The library maps MessagePack types to JSON value types as follows: + + MessagePack type | JSON value type | first byte + ---------------- | --------------- | ---------- + positive fixint | number_unsigned | 0x00..0x7F + fixmap | object | 0x80..0x8F + fixarray | array | 0x90..0x9F + fixstr | string | 0xA0..0xBF + nil | `null` | 0xC0 + false | `false` | 0xC2 + true | `true` | 0xC3 + float 32 | number_float | 0xCA + float 64 | number_float | 0xCB + uint 8 | number_unsigned | 0xCC + uint 16 | number_unsigned | 0xCD + uint 32 | number_unsigned | 0xCE + uint 64 | number_unsigned | 0xCF + int 8 | number_integer | 0xD0 + int 16 | number_integer | 0xD1 + int 32 | number_integer | 0xD2 + int 64 | number_integer | 0xD3 + str 8 | string | 0xD9 + str 16 | string | 0xDA + str 32 | string | 0xDB + array 16 | array | 0xDC + array 32 | array | 0xDD + map 16 | object | 0xDE + map 32 | object | 0xDF + bin 8 | binary | 0xC4 + bin 16 | binary | 0xC5 + bin 32 | binary | 0xC6 + ext 8 | binary | 0xC7 + ext 16 | binary | 0xC8 + ext 32 | binary | 0xC9 + fixext 1 | binary | 0xD4 + fixext 2 | binary | 0xD5 + fixext 4 | binary | 0xD6 + fixext 8 | binary | 0xD7 + fixext 16 | binary | 0xD8 + negative fixint | number_integer | 0xE0-0xFF + + @note Any MessagePack output created @ref to_msgpack can be successfully + parsed by @ref from_msgpack. + + @param[in] i an input in MessagePack format convertible to an input + adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if unsupported features from MessagePack were + used in the given input @a i or if the input is not valid MessagePack + @throw parse_error.113 if a string was expected as map key, but not found + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + MessagePack format to a JSON value.,from_msgpack} + + @sa http://msgpack.org + @sa see @ref to_msgpack(const basic_json&) for the analogous serialization + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for + the related UBJSON format + @sa see @ref from_bson(InputType&&, const bool, const bool) for + the related BSON format + + @since version 2.0.9; parameter @a start_index since 2.1.1; changed to + consume input adapters, removed start_index parameter, and added + @a strict parameter since 3.0.0; added @a allow_exceptions parameter + since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_msgpack(InputType&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_msgpack(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_msgpack(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_msgpack(ptr, ptr + len)) + static basic_json from_msgpack(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::msgpack, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + /*! + @brief create a JSON value from an input in UBJSON format + + Deserializes a given input @a i to a JSON value using the UBJSON (Universal + Binary JSON) serialization format. + + The library maps UBJSON types to JSON value types as follows: + + UBJSON type | JSON value type | marker + ----------- | --------------------------------------- | ------ + no-op | *no value, next value is read* | `N` + null | `null` | `Z` + false | `false` | `F` + true | `true` | `T` + float32 | number_float | `d` + float64 | number_float | `D` + uint8 | number_unsigned | `U` + int8 | number_integer | `i` + int16 | number_integer | `I` + int32 | number_integer | `l` + int64 | number_integer | `L` + high-precision number | number_integer, number_unsigned, or number_float - depends on number string | 'H' + string | string | `S` + char | string | `C` + array | array (optimized values are supported) | `[` + object | object (optimized values are supported) | `{` + + @note The mapping is **complete** in the sense that any UBJSON value can + be converted to a JSON value. + + @param[in] i an input in UBJSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.110 if the given input ends prematurely or the end of + file was not reached when @a strict was set to true + @throw parse_error.112 if a parse error occurs + @throw parse_error.113 if a string could not be parsed successfully + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + UBJSON format to a JSON value.,from_ubjson} + + @sa http://ubjson.org + @sa see @ref to_ubjson(const basic_json&, const bool, const bool) for the + analogous serialization + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa see @ref from_msgpack(InputType&&, const bool, const bool) for + the related MessagePack format + @sa see @ref from_bson(InputType&&, const bool, const bool) for + the related BSON format + + @since version 3.1.0; added @a allow_exceptions parameter since 3.2.0 + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_ubjson(InputType&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_ubjson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_ubjson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_ubjson(ptr, ptr + len)) + static basic_json from_ubjson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::ubjson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + + /*! + @brief Create a JSON value from an input in BSON format + + Deserializes a given input @a i to a JSON value using the BSON (Binary JSON) + serialization format. + + The library maps BSON record types to JSON value types as follows: + + BSON type | BSON marker byte | JSON value type + --------------- | ---------------- | --------------------------- + double | 0x01 | number_float + string | 0x02 | string + document | 0x03 | object + array | 0x04 | array + binary | 0x05 | binary + undefined | 0x06 | still unsupported + ObjectId | 0x07 | still unsupported + boolean | 0x08 | boolean + UTC Date-Time | 0x09 | still unsupported + null | 0x0A | null + Regular Expr. | 0x0B | still unsupported + DB Pointer | 0x0C | still unsupported + JavaScript Code | 0x0D | still unsupported + Symbol | 0x0E | still unsupported + JavaScript Code | 0x0F | still unsupported + int32 | 0x10 | number_integer + Timestamp | 0x11 | still unsupported + 128-bit decimal float | 0x13 | still unsupported + Max Key | 0x7F | still unsupported + Min Key | 0xFF | still unsupported + + @warning The mapping is **incomplete**. The unsupported mappings + are indicated in the table above. + + @param[in] i an input in BSON format convertible to an input adapter + @param[in] strict whether to expect the input to be consumed until EOF + (true by default) + @param[in] allow_exceptions whether to throw exceptions in case of a + parse error (optional, true by default) + + @return deserialized JSON value; in case of a parse error and + @a allow_exceptions set to `false`, the return value will be + value_t::discarded. + + @throw parse_error.114 if an unsupported BSON record type is encountered + + @complexity Linear in the size of the input @a i. + + @liveexample{The example shows the deserialization of a byte vector in + BSON format to a JSON value.,from_bson} + + @sa http://bsonspec.org/spec.html + @sa see @ref to_bson(const basic_json&) for the analogous serialization + @sa see @ref from_cbor(InputType&&, const bool, const bool, const cbor_tag_handler_t) for the + related CBOR format + @sa see @ref from_msgpack(InputType&&, const bool, const bool) for + the related MessagePack format + @sa see @ref from_ubjson(InputType&&, const bool, const bool) for the + related UBJSON format + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(InputType&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::forward(i)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + /*! + @copydoc from_bson(InputType&&, const bool, const bool) + */ + template + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json from_bson(IteratorType first, IteratorType last, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = detail::input_adapter(std::move(first), std::move(last)); + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + + template + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(const T* ptr, std::size_t len, + const bool strict = true, + const bool allow_exceptions = true) + { + return from_bson(ptr, ptr + len, strict, allow_exceptions); + } + + JSON_HEDLEY_WARN_UNUSED_RESULT + JSON_HEDLEY_DEPRECATED_FOR(3.8.0, from_bson(ptr, ptr + len)) + static basic_json from_bson(detail::span_input_adapter&& i, + const bool strict = true, + const bool allow_exceptions = true) + { + basic_json result; + detail::json_sax_dom_parser sdp(result, allow_exceptions); + auto ia = i.get(); + // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg) + const bool res = binary_reader(std::move(ia)).sax_parse(input_format_t::bson, &sdp, strict); + return res ? result : basic_json(value_t::discarded); + } + /// @} + + ////////////////////////// + // JSON Pointer support // + ////////////////////////// + + /// @name JSON Pointer functions + /// @{ + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. Similar to @ref operator[](const typename + object_t::key_type&), `null` values are created in arrays and objects if + necessary. + + In particular: + - If the JSON pointer points to an object key that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. + - If the JSON pointer points to an array index that does not exist, it + is created an filled with a `null` value before a reference to it + is returned. All indices between the current maximum and the given + index are also filled with `null`. + - The special value `-` is treated as a synonym for the index past the + end. + + @param[in] ptr a JSON pointer + + @return reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer} + + @since version 2.0.0 + */ + reference operator[](const json_pointer& ptr) + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Uses a JSON pointer to retrieve a reference to the respective JSON value. + No bound checking is performed. The function does not change the JSON + value; no `null` values are created. In particular, the special value + `-` yields an exception. + + @param[in] ptr JSON pointer to the desired element + + @return const reference to the element pointed to by @a ptr + + @complexity Constant. + + @throw parse_error.106 if an array index begins with '0' + @throw parse_error.109 if an array index was not a number + @throw out_of_range.402 if the array index '-' is used + @throw out_of_range.404 if the JSON pointer can not be resolved + + @liveexample{The behavior is shown in the example.,operatorjson_pointer_const} + + @since version 2.0.0 + */ + const_reference operator[](const json_pointer& ptr) const + { + return ptr.get_unchecked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a reference to the element at with specified JSON pointer @a ptr, + with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer} + */ + reference at(const json_pointer& ptr) + { + return ptr.get_checked(this); + } + + /*! + @brief access specified element via JSON Pointer + + Returns a const reference to the element at with specified JSON pointer @a + ptr, with bounds checking. + + @param[in] ptr JSON pointer to the desired element + + @return reference to the element pointed to by @a ptr + + @throw parse_error.106 if an array index in the passed JSON pointer @a ptr + begins with '0'. See example below. + + @throw parse_error.109 if an array index in the passed JSON pointer @a ptr + is not a number. See example below. + + @throw out_of_range.401 if an array index in the passed JSON pointer @a ptr + is out of range. See example below. + + @throw out_of_range.402 if the array index '-' is used in the passed JSON + pointer @a ptr. As `at` provides checked access (and no elements are + implicitly inserted), the index '-' is always invalid. See example below. + + @throw out_of_range.403 if the JSON pointer describes a key of an object + which cannot be found. See example below. + + @throw out_of_range.404 if the JSON pointer @a ptr can not be resolved. + See example below. + + @exceptionsafety Strong guarantee: if an exception is thrown, there are no + changes in the JSON value. + + @complexity Constant. + + @since version 2.0.0 + + @liveexample{The behavior is shown in the example.,at_json_pointer_const} + */ + const_reference at(const json_pointer& ptr) const + { + return ptr.get_checked(this); + } + + /*! + @brief return flattened JSON value + + The function creates a JSON object whose keys are JSON pointers (see [RFC + 6901](https://tools.ietf.org/html/rfc6901)) and whose values are all + primitive. The original JSON value can be restored using the @ref + unflatten() function. + + @return an object that maps JSON pointers to primitive values + + @note Empty objects and arrays are flattened to `null` and will not be + reconstructed correctly by the @ref unflatten() function. + + @complexity Linear in the size the JSON value. + + @liveexample{The following code shows how a JSON object is flattened to an + object whose keys consist of JSON pointers.,flatten} + + @sa see @ref unflatten() for the reverse function + + @since version 2.0.0 + */ + basic_json flatten() const + { + basic_json result(value_t::object); + json_pointer::flatten("", *this, result); + return result; + } + + /*! + @brief unflatten a previously flattened JSON value + + The function restores the arbitrary nesting of a JSON value that has been + flattened before using the @ref flatten() function. The JSON value must + meet certain constraints: + 1. The value must be an object. + 2. The keys must be JSON pointers (see + [RFC 6901](https://tools.ietf.org/html/rfc6901)) + 3. The mapped values must be primitive JSON types. + + @return the original JSON from a flattened version + + @note Empty objects and arrays are flattened by @ref flatten() to `null` + values and can not unflattened to their original type. Apart from + this example, for a JSON value `j`, the following is always true: + `j == j.flatten().unflatten()`. + + @complexity Linear in the size the JSON value. + + @throw type_error.314 if value is not an object + @throw type_error.315 if object values are not primitive + + @liveexample{The following code shows how a flattened JSON object is + unflattened into the original nested JSON object.,unflatten} + + @sa see @ref flatten() for the reverse function + + @since version 2.0.0 + */ + basic_json unflatten() const + { + return json_pointer::unflatten(*this); + } + + /// @} + + ////////////////////////// + // JSON Patch functions // + ////////////////////////// + + /// @name JSON Patch functions + /// @{ + + /*! + @brief applies a JSON patch + + [JSON Patch](http://jsonpatch.com) defines a JSON document structure for + expressing a sequence of operations to apply to a JSON) document. With + this function, a JSON Patch is applied to the current JSON value by + executing all operations from the patch. + + @param[in] json_patch JSON patch document + @return patched document + + @note The application of a patch is atomic: Either all operations succeed + and the patched document is returned or an exception is thrown. In + any case, the original value is not changed: the patch is applied + to a copy of the value. + + @throw parse_error.104 if the JSON patch does not consist of an array of + objects + + @throw parse_error.105 if the JSON patch is malformed (e.g., mandatory + attributes are missing); example: `"operation add must have member path"` + + @throw out_of_range.401 if an array index is out of range. + + @throw out_of_range.403 if a JSON pointer inside the patch could not be + resolved successfully in the current JSON value; example: `"key baz not + found"` + + @throw out_of_range.405 if JSON pointer has no parent ("add", "remove", + "move") + + @throw other_error.501 if "test" operation was unsuccessful + + @complexity Linear in the size of the JSON value and the length of the + JSON patch. As usually only a fraction of the JSON value is affected by + the patch, the complexity can usually be neglected. + + @liveexample{The following code shows how a JSON patch is applied to a + value.,patch} + + @sa see @ref diff -- create a JSON patch by comparing two JSON values + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + @sa [RFC 6901 (JSON Pointer)](https://tools.ietf.org/html/rfc6901) + + @since version 2.0.0 + */ + basic_json patch(const basic_json& json_patch) const + { + // make a working copy to apply the patch to + basic_json result = *this; + + // the valid JSON Patch operations + enum class patch_operations {add, remove, replace, move, copy, test, invalid}; + + const auto get_op = [](const std::string & op) + { + if (op == "add") + { + return patch_operations::add; + } + if (op == "remove") + { + return patch_operations::remove; + } + if (op == "replace") + { + return patch_operations::replace; + } + if (op == "move") + { + return patch_operations::move; + } + if (op == "copy") + { + return patch_operations::copy; + } + if (op == "test") + { + return patch_operations::test; + } + + return patch_operations::invalid; + }; + + // wrapper for "add" operation; add value at ptr + const auto operation_add = [&result](json_pointer & ptr, basic_json val) + { + // adding to the root of the target document means replacing it + if (ptr.empty()) + { + result = val; + return; + } + + // make sure the top element of the pointer exists + json_pointer top_pointer = ptr.top(); + if (top_pointer != ptr) + { + result.at(top_pointer); + } + + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result[ptr]; + + switch (parent.m_type) + { + case value_t::null: + case value_t::object: + { + // use operator[] to add value + parent[last_path] = val; + break; + } + + case value_t::array: + { + if (last_path == "-") + { + // special case: append to back + parent.push_back(val); + } + else + { + const auto idx = json_pointer::array_index(last_path); + if (JSON_HEDLEY_UNLIKELY(idx > parent.size())) + { + // avoid undefined behavior + JSON_THROW(out_of_range::create(401, "array index " + std::to_string(idx) + " is out of range", parent)); + } + + // default case: insert add offset + parent.insert(parent.begin() + static_cast(idx), val); + } + break; + } + + // if there exists a parent it cannot be primitive + case value_t::string: // LCOV_EXCL_LINE + case value_t::boolean: // LCOV_EXCL_LINE + case value_t::number_integer: // LCOV_EXCL_LINE + case value_t::number_unsigned: // LCOV_EXCL_LINE + case value_t::number_float: // LCOV_EXCL_LINE + case value_t::binary: // LCOV_EXCL_LINE + case value_t::discarded: // LCOV_EXCL_LINE + default: // LCOV_EXCL_LINE + JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE + } + }; + + // wrapper for "remove" operation; remove value at ptr + const auto operation_remove = [this, &result](json_pointer & ptr) + { + // get reference to parent of JSON pointer ptr + const auto last_path = ptr.back(); + ptr.pop_back(); + basic_json& parent = result.at(ptr); + + // remove child + if (parent.is_object()) + { + // perform range check + auto it = parent.find(last_path); + if (JSON_HEDLEY_LIKELY(it != parent.end())) + { + parent.erase(it); + } + else + { + JSON_THROW(out_of_range::create(403, "key '" + last_path + "' not found", *this)); + } + } + else if (parent.is_array()) + { + // note erase performs range check + parent.erase(json_pointer::array_index(last_path)); + } + }; + + // type check: top level value must be an array + if (JSON_HEDLEY_UNLIKELY(!json_patch.is_array())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", json_patch)); + } + + // iterate and apply the operations + for (const auto& val : json_patch) + { + // wrapper to get a value for an operation + const auto get_value = [&val](const std::string & op, + const std::string & member, + bool string_type) -> basic_json & + { + // find value + auto it = val.m_value.object->find(member); + + // context-sensitive error message + const auto error_msg = (op == "op") ? "operation" : "operation '" + op + "'"; + + // check if desired value is present + if (JSON_HEDLEY_UNLIKELY(it == val.m_value.object->end())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have member '" + member + "'", val)); + } + + // check if result is of type string + if (JSON_HEDLEY_UNLIKELY(string_type && !it->second.is_string())) + { + // NOLINTNEXTLINE(performance-inefficient-string-concatenation) + JSON_THROW(parse_error::create(105, 0, error_msg + " must have string member '" + member + "'", val)); + } + + // no error: return value + return it->second; + }; + + // type check: every element of the array must be an object + if (JSON_HEDLEY_UNLIKELY(!val.is_object())) + { + JSON_THROW(parse_error::create(104, 0, "JSON patch must be an array of objects", val)); + } + + // collect mandatory members + const auto op = get_value("op", "op", true).template get(); + const auto path = get_value(op, "path", true).template get(); + json_pointer ptr(path); + + switch (get_op(op)) + { + case patch_operations::add: + { + operation_add(ptr, get_value("add", "value", false)); + break; + } + + case patch_operations::remove: + { + operation_remove(ptr); + break; + } + + case patch_operations::replace: + { + // the "path" location must exist - use at() + result.at(ptr) = get_value("replace", "value", false); + break; + } + + case patch_operations::move: + { + const auto from_path = get_value("move", "from", true).template get(); + json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The move operation is functionally identical to a + // "remove" operation on the "from" location, followed + // immediately by an "add" operation at the target + // location with the value that was just removed. + operation_remove(from_ptr); + operation_add(ptr, v); + break; + } + + case patch_operations::copy: + { + const auto from_path = get_value("copy", "from", true).template get(); + const json_pointer from_ptr(from_path); + + // the "from" location must exist - use at() + basic_json v = result.at(from_ptr); + + // The copy is functionally identical to an "add" + // operation at the target location using the value + // specified in the "from" member. + operation_add(ptr, v); + break; + } + + case patch_operations::test: + { + bool success = false; + JSON_TRY + { + // check if "value" matches the one at "path" + // the "path" location must exist - use at() + success = (result.at(ptr) == get_value("test", "value", false)); + } + JSON_INTERNAL_CATCH (out_of_range&) + { + // ignore out of range errors: success remains false + } + + // throw an exception if test fails + if (JSON_HEDLEY_UNLIKELY(!success)) + { + JSON_THROW(other_error::create(501, "unsuccessful: " + val.dump(), val)); + } + + break; + } + + case patch_operations::invalid: + default: + { + // op must be "add", "remove", "replace", "move", "copy", or + // "test" + JSON_THROW(parse_error::create(105, 0, "operation value '" + op + "' is invalid", val)); + } + } + } + + return result; + } + + /*! + @brief creates a diff as a JSON patch + + Creates a [JSON Patch](http://jsonpatch.com) so that value @a source can + be changed into the value @a target by calling @ref patch function. + + @invariant For two JSON values @a source and @a target, the following code + yields always `true`: + @code {.cpp} + source.patch(diff(source, target)) == target; + @endcode + + @note Currently, only `remove`, `add`, and `replace` operations are + generated. + + @param[in] source JSON value to compare from + @param[in] target JSON value to compare against + @param[in] path helper value to create JSON pointers + + @return a JSON patch to convert the @a source to @a target + + @complexity Linear in the lengths of @a source and @a target. + + @liveexample{The following code shows how a JSON patch is created as a + diff for two JSON values.,diff} + + @sa see @ref patch -- apply a JSON patch + @sa see @ref merge_patch -- apply a JSON Merge Patch + + @sa [RFC 6902 (JSON Patch)](https://tools.ietf.org/html/rfc6902) + + @since version 2.0.0 + */ + JSON_HEDLEY_WARN_UNUSED_RESULT + static basic_json diff(const basic_json& source, const basic_json& target, + const std::string& path = "") + { + // the patch + basic_json result(value_t::array); + + // if the values are the same, return empty patch + if (source == target) + { + return result; + } + + if (source.type() != target.type()) + { + // different types: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + return result; + } + + switch (source.type()) + { + case value_t::array: + { + // first pass: traverse common elements + std::size_t i = 0; + while (i < source.size() && i < target.size()) + { + // recursive call to compare array values at index i + auto temp_diff = diff(source[i], target[i], path + "/" + std::to_string(i)); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + ++i; + } + + // i now reached the end of at least one array + // in a second pass, traverse the remaining elements + + // remove my remaining elements + const auto end_index = static_cast(result.size()); + while (i < source.size()) + { + // add operations in reverse order to avoid invalid + // indices + result.insert(result.begin() + end_index, object( + { + {"op", "remove"}, + {"path", path + "/" + std::to_string(i)} + })); + ++i; + } + + // add other remaining elements + while (i < target.size()) + { + result.push_back( + { + {"op", "add"}, + {"path", path + "/-"}, + {"value", target[i]} + }); + ++i; + } + + break; + } + + case value_t::object: + { + // first pass: traverse this object's elements + for (auto it = source.cbegin(); it != source.cend(); ++it) + { + // escape the key name to be used in a JSON patch + const auto path_key = path + "/" + detail::escape(it.key()); + + if (target.find(it.key()) != target.end()) + { + // recursive call to compare object values at key it + auto temp_diff = diff(it.value(), target[it.key()], path_key); + result.insert(result.end(), temp_diff.begin(), temp_diff.end()); + } + else + { + // found a key that is not in o -> remove it + result.push_back(object( + { + {"op", "remove"}, {"path", path_key} + })); + } + } + + // second pass: traverse other object's elements + for (auto it = target.cbegin(); it != target.cend(); ++it) + { + if (source.find(it.key()) == source.end()) + { + // found a key that is not in this -> add it + const auto path_key = path + "/" + detail::escape(it.key()); + result.push_back( + { + {"op", "add"}, {"path", path_key}, + {"value", it.value()} + }); + } + } + + break; + } + + case value_t::null: + case value_t::string: + case value_t::boolean: + case value_t::number_integer: + case value_t::number_unsigned: + case value_t::number_float: + case value_t::binary: + case value_t::discarded: + default: + { + // both primitive type: replace value + result.push_back( + { + {"op", "replace"}, {"path", path}, {"value", target} + }); + break; + } + } + + return result; + } + + /// @} + + //////////////////////////////// + // JSON Merge Patch functions // + //////////////////////////////// + + /// @name JSON Merge Patch functions + /// @{ + + /*! + @brief applies a JSON Merge Patch + + The merge patch format is primarily intended for use with the HTTP PATCH + method as a means of describing a set of modifications to a target + resource's content. This function applies a merge patch to the current + JSON value. + + The function implements the following algorithm from Section 2 of + [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396): + + ``` + define MergePatch(Target, Patch): + if Patch is an Object: + if Target is not an Object: + Target = {} // Ignore the contents and set it to an empty Object + for each Name/Value pair in Patch: + if Value is null: + if Name exists in Target: + remove the Name/Value pair from Target + else: + Target[Name] = MergePatch(Target[Name], Value) + return Target + else: + return Patch + ``` + + Thereby, `Target` is the current object; that is, the patch is applied to + the current value. + + @param[in] apply_patch the patch to apply + + @complexity Linear in the lengths of @a patch. + + @liveexample{The following code shows how a JSON Merge Patch is applied to + a JSON document.,merge_patch} + + @sa see @ref patch -- apply a JSON patch + @sa [RFC 7396 (JSON Merge Patch)](https://tools.ietf.org/html/rfc7396) + + @since version 3.0.0 + */ + void merge_patch(const basic_json& apply_patch) + { + if (apply_patch.is_object()) + { + if (!is_object()) + { + *this = object(); + } + for (auto it = apply_patch.begin(); it != apply_patch.end(); ++it) + { + if (it.value().is_null()) + { + erase(it.key()); + } + else + { + operator[](it.key()).merge_patch(it.value()); + } + } + } + else + { + *this = apply_patch; + } + } + + /// @} +}; + +/*! +@brief user-defined to_string function for JSON values + +This function implements a user-defined to_string for JSON objects. + +@param[in] j a JSON object +@return a std::string object +*/ + +NLOHMANN_BASIC_JSON_TPL_DECLARATION +std::string to_string(const NLOHMANN_BASIC_JSON_TPL& j) +{ + return j.dump(); +} +} // namespace nlohmann + +/////////////////////// +// nonmember support // +/////////////////////// + +// specialization of std::swap, and std::hash +namespace std +{ + +/// hash value for JSON objects +template<> +struct hash +{ + /*! + @brief return a hash value for a JSON object + + @since version 1.0.0 + */ + std::size_t operator()(const nlohmann::json& j) const + { + return nlohmann::detail::hash(j); + } +}; + +/// specialization for std::less +/// @note: do not remove the space after '<', +/// see https://github.com/nlohmann/json/pull/679 +template<> +struct less<::nlohmann::detail::value_t> +{ + /*! + @brief compare two value_t enum values + @since version 3.0.0 + */ + bool operator()(nlohmann::detail::value_t lhs, + nlohmann::detail::value_t rhs) const noexcept + { + return nlohmann::detail::operator<(lhs, rhs); + } +}; + +// C++20 prohibit function specialization in the std namespace. +#ifndef JSON_HAS_CPP_20 + +/*! +@brief exchanges the values of two JSON objects + +@since version 1.0.0 +*/ +template<> +inline void swap(nlohmann::json& j1, nlohmann::json& j2) noexcept( // NOLINT(readability-inconsistent-declaration-parameter-name) + is_nothrow_move_constructible::value&& // NOLINT(misc-redundant-expression) + is_nothrow_move_assignable::value + ) +{ + j1.swap(j2); +} + +#endif + +} // namespace std + +/*! +@brief user-defined string literal for JSON values + +This operator implements a user-defined string literal for JSON objects. It +can be used by adding `"_json"` to a string literal and returns a JSON object +if no parse error occurred. + +@param[in] s a string representation of a JSON object +@param[in] n the length of string @a s +@return a JSON object + +@since version 1.0.0 +*/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json operator "" _json(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +/*! +@brief user-defined string literal for JSON pointer + +This operator implements a user-defined string literal for JSON Pointers. It +can be used by adding `"_json_pointer"` to a string literal and returns a JSON pointer +object if no parse error occurred. + +@param[in] s a string representation of a JSON Pointer +@param[in] n the length of string @a s +@return a JSON pointer object + +@since version 2.0.0 +*/ +JSON_HEDLEY_NON_NULL(1) +inline nlohmann::json::json_pointer operator "" _json_pointer(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +// #include + + +// restore clang diagnostic settings +#if defined(__clang__) + #pragma clang diagnostic pop +#endif + +// clean up +#undef JSON_ASSERT +#undef JSON_INTERNAL_CATCH +#undef JSON_CATCH +#undef JSON_THROW +#undef JSON_TRY +#undef JSON_PRIVATE_UNLESS_TESTED +#undef JSON_HAS_CPP_11 +#undef JSON_HAS_CPP_14 +#undef JSON_HAS_CPP_17 +#undef JSON_HAS_CPP_20 +#undef NLOHMANN_BASIC_JSON_TPL_DECLARATION +#undef NLOHMANN_BASIC_JSON_TPL +#undef JSON_EXPLICIT + +// #include + + +#undef JSON_HEDLEY_ALWAYS_INLINE +#undef JSON_HEDLEY_ARM_VERSION +#undef JSON_HEDLEY_ARM_VERSION_CHECK +#undef JSON_HEDLEY_ARRAY_PARAM +#undef JSON_HEDLEY_ASSUME +#undef JSON_HEDLEY_BEGIN_C_DECLS +#undef JSON_HEDLEY_CLANG_HAS_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_BUILTIN +#undef JSON_HEDLEY_CLANG_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_CLANG_HAS_EXTENSION +#undef JSON_HEDLEY_CLANG_HAS_FEATURE +#undef JSON_HEDLEY_CLANG_HAS_WARNING +#undef JSON_HEDLEY_COMPCERT_VERSION +#undef JSON_HEDLEY_COMPCERT_VERSION_CHECK +#undef JSON_HEDLEY_CONCAT +#undef JSON_HEDLEY_CONCAT3 +#undef JSON_HEDLEY_CONCAT3_EX +#undef JSON_HEDLEY_CONCAT_EX +#undef JSON_HEDLEY_CONST +#undef JSON_HEDLEY_CONSTEXPR +#undef JSON_HEDLEY_CONST_CAST +#undef JSON_HEDLEY_CPP_CAST +#undef JSON_HEDLEY_CRAY_VERSION +#undef JSON_HEDLEY_CRAY_VERSION_CHECK +#undef JSON_HEDLEY_C_DECL +#undef JSON_HEDLEY_DEPRECATED +#undef JSON_HEDLEY_DEPRECATED_FOR +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS +#undef JSON_HEDLEY_DIAGNOSTIC_DISABLE_UNUSED_FUNCTION +#undef JSON_HEDLEY_DIAGNOSTIC_POP +#undef JSON_HEDLEY_DIAGNOSTIC_PUSH +#undef JSON_HEDLEY_DMC_VERSION +#undef JSON_HEDLEY_DMC_VERSION_CHECK +#undef JSON_HEDLEY_EMPTY_BASES +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION +#undef JSON_HEDLEY_EMSCRIPTEN_VERSION_CHECK +#undef JSON_HEDLEY_END_C_DECLS +#undef JSON_HEDLEY_FLAGS +#undef JSON_HEDLEY_FLAGS_CAST +#undef JSON_HEDLEY_GCC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_BUILTIN +#undef JSON_HEDLEY_GCC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GCC_HAS_EXTENSION +#undef JSON_HEDLEY_GCC_HAS_FEATURE +#undef JSON_HEDLEY_GCC_HAS_WARNING +#undef JSON_HEDLEY_GCC_NOT_CLANG_VERSION_CHECK +#undef JSON_HEDLEY_GCC_VERSION +#undef JSON_HEDLEY_GCC_VERSION_CHECK +#undef JSON_HEDLEY_GNUC_HAS_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_BUILTIN +#undef JSON_HEDLEY_GNUC_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_GNUC_HAS_EXTENSION +#undef JSON_HEDLEY_GNUC_HAS_FEATURE +#undef JSON_HEDLEY_GNUC_HAS_WARNING +#undef JSON_HEDLEY_GNUC_VERSION +#undef JSON_HEDLEY_GNUC_VERSION_CHECK +#undef JSON_HEDLEY_HAS_ATTRIBUTE +#undef JSON_HEDLEY_HAS_BUILTIN +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE +#undef JSON_HEDLEY_HAS_CPP_ATTRIBUTE_NS +#undef JSON_HEDLEY_HAS_DECLSPEC_ATTRIBUTE +#undef JSON_HEDLEY_HAS_EXTENSION +#undef JSON_HEDLEY_HAS_FEATURE +#undef JSON_HEDLEY_HAS_WARNING +#undef JSON_HEDLEY_IAR_VERSION +#undef JSON_HEDLEY_IAR_VERSION_CHECK +#undef JSON_HEDLEY_IBM_VERSION +#undef JSON_HEDLEY_IBM_VERSION_CHECK +#undef JSON_HEDLEY_IMPORT +#undef JSON_HEDLEY_INLINE +#undef JSON_HEDLEY_INTEL_CL_VERSION +#undef JSON_HEDLEY_INTEL_CL_VERSION_CHECK +#undef JSON_HEDLEY_INTEL_VERSION +#undef JSON_HEDLEY_INTEL_VERSION_CHECK +#undef JSON_HEDLEY_IS_CONSTANT +#undef JSON_HEDLEY_IS_CONSTEXPR_ +#undef JSON_HEDLEY_LIKELY +#undef JSON_HEDLEY_MALLOC +#undef JSON_HEDLEY_MCST_LCC_VERSION +#undef JSON_HEDLEY_MCST_LCC_VERSION_CHECK +#undef JSON_HEDLEY_MESSAGE +#undef JSON_HEDLEY_MSVC_VERSION +#undef JSON_HEDLEY_MSVC_VERSION_CHECK +#undef JSON_HEDLEY_NEVER_INLINE +#undef JSON_HEDLEY_NON_NULL +#undef JSON_HEDLEY_NO_ESCAPE +#undef JSON_HEDLEY_NO_RETURN +#undef JSON_HEDLEY_NO_THROW +#undef JSON_HEDLEY_NULL +#undef JSON_HEDLEY_PELLES_VERSION +#undef JSON_HEDLEY_PELLES_VERSION_CHECK +#undef JSON_HEDLEY_PGI_VERSION +#undef JSON_HEDLEY_PGI_VERSION_CHECK +#undef JSON_HEDLEY_PREDICT +#undef JSON_HEDLEY_PRINTF_FORMAT +#undef JSON_HEDLEY_PRIVATE +#undef JSON_HEDLEY_PUBLIC +#undef JSON_HEDLEY_PURE +#undef JSON_HEDLEY_REINTERPRET_CAST +#undef JSON_HEDLEY_REQUIRE +#undef JSON_HEDLEY_REQUIRE_CONSTEXPR +#undef JSON_HEDLEY_REQUIRE_MSG +#undef JSON_HEDLEY_RESTRICT +#undef JSON_HEDLEY_RETURNS_NON_NULL +#undef JSON_HEDLEY_SENTINEL +#undef JSON_HEDLEY_STATIC_ASSERT +#undef JSON_HEDLEY_STATIC_CAST +#undef JSON_HEDLEY_STRINGIFY +#undef JSON_HEDLEY_STRINGIFY_EX +#undef JSON_HEDLEY_SUNPRO_VERSION +#undef JSON_HEDLEY_SUNPRO_VERSION_CHECK +#undef JSON_HEDLEY_TINYC_VERSION +#undef JSON_HEDLEY_TINYC_VERSION_CHECK +#undef JSON_HEDLEY_TI_ARMCL_VERSION +#undef JSON_HEDLEY_TI_ARMCL_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL2000_VERSION +#undef JSON_HEDLEY_TI_CL2000_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL430_VERSION +#undef JSON_HEDLEY_TI_CL430_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL6X_VERSION +#undef JSON_HEDLEY_TI_CL6X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CL7X_VERSION +#undef JSON_HEDLEY_TI_CL7X_VERSION_CHECK +#undef JSON_HEDLEY_TI_CLPRU_VERSION +#undef JSON_HEDLEY_TI_CLPRU_VERSION_CHECK +#undef JSON_HEDLEY_TI_VERSION +#undef JSON_HEDLEY_TI_VERSION_CHECK +#undef JSON_HEDLEY_UNAVAILABLE +#undef JSON_HEDLEY_UNLIKELY +#undef JSON_HEDLEY_UNPREDICTABLE +#undef JSON_HEDLEY_UNREACHABLE +#undef JSON_HEDLEY_UNREACHABLE_RETURN +#undef JSON_HEDLEY_VERSION +#undef JSON_HEDLEY_VERSION_DECODE_MAJOR +#undef JSON_HEDLEY_VERSION_DECODE_MINOR +#undef JSON_HEDLEY_VERSION_DECODE_REVISION +#undef JSON_HEDLEY_VERSION_ENCODE +#undef JSON_HEDLEY_WARNING +#undef JSON_HEDLEY_WARN_UNUSED_RESULT +#undef JSON_HEDLEY_WARN_UNUSED_RESULT_MSG +#undef JSON_HEDLEY_FALL_THROUGH + + + +#endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/extern/smaa_areatex/smaa_areatex.cpp b/extern/smaa_areatex/smaa_areatex.cpp index 7a4ff3a9831..c61543e10a0 100644 --- a/extern/smaa_areatex/smaa_areatex.cpp +++ b/extern/smaa_areatex/smaa_areatex.cpp @@ -574,6 +574,8 @@ Dbl2 AreaDiag::area(Dbl2 p1, Dbl2 p2, int left) Dbl2 d = p2 - p1; if (d.x == 0.0) return Dbl2(0.0, 1.0); + if (d.y == 0.0) + return Dbl2(1.0, 0.0); double x1 = (double)(1 + left); double x2 = x1 + 1.0; diff --git a/extern/tinygltf/README.blender b/extern/tinygltf/README.blender new file mode 100644 index 00000000000..2aba84dea80 --- /dev/null +++ b/extern/tinygltf/README.blender @@ -0,0 +1,6 @@ +Project: TinyGLTF +URL: https://github.com/syoyo/tinygltf +License: MIT +Upstream version: 2.5.0, 19a41d20ec0 +Local modifications: +* Silence "enum value not handled in switch" warnings due to JSON dependency. diff --git a/extern/tinygltf/patches/TinyGLTF.diff b/extern/tinygltf/patches/TinyGLTF.diff new file mode 100644 index 00000000000..411bd5621f3 Binary files /dev/null and b/extern/tinygltf/patches/TinyGLTF.diff differ diff --git a/extern/tinygltf/tiny_gltf.h b/extern/tinygltf/tiny_gltf.h new file mode 100644 index 00000000000..099e0c76d92 --- /dev/null +++ b/extern/tinygltf/tiny_gltf.h @@ -0,0 +1,7761 @@ +// +// Header-only tiny glTF 2.0 loader and serializer. +// +// +// The MIT License (MIT) +// +// Copyright (c) 2015 - Present Syoyo Fujita, Aurélien Chatelain and many +// contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +// Version: +// - v2.5.0 Add SetPreserveImageChannels() option to load image data as is. +// - v2.4.3 Fix null object output when when material has all default +// parameters. +// - v2.4.2 Decode percent-encoded URI. +// - v2.4.1 Fix some glTF object class does not have `extensions` and/or +// `extras` property. +// - v2.4.0 Experimental RapidJSON and C++14 support(Thanks to @jrkoone). +// - v2.3.1 Set default value of minFilter and magFilter in Sampler to -1. +// - v2.3.0 Modified Material representation according to glTF 2.0 schema +// (and introduced TextureInfo class) +// Change the behavior of `Value::IsNumber`. It return true either the +// value is int or real. +// - v2.2.0 Add loading 16bit PNG support. Add Sparse accessor support(Thanks +// to @Ybalrid) +// - v2.1.0 Add draco compression. +// - v2.0.1 Add comparsion feature(Thanks to @Selmar). +// - v2.0.0 glTF 2.0!. +// +// Tiny glTF loader is using following third party libraries: +// +// - jsonhpp: C++ JSON library. +// - base64: base64 decode/encode library. +// - stb_image: Image loading library. +// +#ifndef TINY_GLTF_H_ +#define TINY_GLTF_H_ + +#include +#include +#include // std::fabs +#include +#include +#include +#include +#include +#include +#include + +#ifndef TINYGLTF_USE_CPP14 +#include +#endif + +#ifdef __ANDROID__ +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS +#include +#endif +#endif + +#ifdef __GNUC__ +#if (__GNUC__ < 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ <= 8)) +#define TINYGLTF_NOEXCEPT +#else +#define TINYGLTF_NOEXCEPT noexcept +#endif +#else +#define TINYGLTF_NOEXCEPT noexcept +#endif + +#define DEFAULT_METHODS(x) \ + ~x() = default; \ + x(const x &) = default; \ + x(x &&) TINYGLTF_NOEXCEPT = default; \ + x &operator=(const x &) = default; \ + x &operator=(x &&) TINYGLTF_NOEXCEPT = default; + +namespace tinygltf { + +#define TINYGLTF_MODE_POINTS (0) +#define TINYGLTF_MODE_LINE (1) +#define TINYGLTF_MODE_LINE_LOOP (2) +#define TINYGLTF_MODE_LINE_STRIP (3) +#define TINYGLTF_MODE_TRIANGLES (4) +#define TINYGLTF_MODE_TRIANGLE_STRIP (5) +#define TINYGLTF_MODE_TRIANGLE_FAN (6) + +#define TINYGLTF_COMPONENT_TYPE_BYTE (5120) +#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE (5121) +#define TINYGLTF_COMPONENT_TYPE_SHORT (5122) +#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT (5123) +#define TINYGLTF_COMPONENT_TYPE_INT (5124) +#define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125) +#define TINYGLTF_COMPONENT_TYPE_FLOAT (5126) +#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130) + +#define TINYGLTF_TEXTURE_FILTER_NEAREST (9728) +#define TINYGLTF_TEXTURE_FILTER_LINEAR (9729) +#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST (9984) +#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST (9985) +#define TINYGLTF_TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR (9986) +#define TINYGLTF_TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR (9987) + +#define TINYGLTF_TEXTURE_WRAP_REPEAT (10497) +#define TINYGLTF_TEXTURE_WRAP_CLAMP_TO_EDGE (33071) +#define TINYGLTF_TEXTURE_WRAP_MIRRORED_REPEAT (33648) + +// Redeclarations of the above for technique.parameters. +#define TINYGLTF_PARAMETER_TYPE_BYTE (5120) +#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_BYTE (5121) +#define TINYGLTF_PARAMETER_TYPE_SHORT (5122) +#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_SHORT (5123) +#define TINYGLTF_PARAMETER_TYPE_INT (5124) +#define TINYGLTF_PARAMETER_TYPE_UNSIGNED_INT (5125) +#define TINYGLTF_PARAMETER_TYPE_FLOAT (5126) + +#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC2 (35664) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC3 (35665) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_VEC4 (35666) + +#define TINYGLTF_PARAMETER_TYPE_INT_VEC2 (35667) +#define TINYGLTF_PARAMETER_TYPE_INT_VEC3 (35668) +#define TINYGLTF_PARAMETER_TYPE_INT_VEC4 (35669) + +#define TINYGLTF_PARAMETER_TYPE_BOOL (35670) +#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC2 (35671) +#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC3 (35672) +#define TINYGLTF_PARAMETER_TYPE_BOOL_VEC4 (35673) + +#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT2 (35674) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT3 (35675) +#define TINYGLTF_PARAMETER_TYPE_FLOAT_MAT4 (35676) + +#define TINYGLTF_PARAMETER_TYPE_SAMPLER_2D (35678) + +// End parameter types + +#define TINYGLTF_TYPE_VEC2 (2) +#define TINYGLTF_TYPE_VEC3 (3) +#define TINYGLTF_TYPE_VEC4 (4) +#define TINYGLTF_TYPE_MAT2 (32 + 2) +#define TINYGLTF_TYPE_MAT3 (32 + 3) +#define TINYGLTF_TYPE_MAT4 (32 + 4) +#define TINYGLTF_TYPE_SCALAR (64 + 1) +#define TINYGLTF_TYPE_VECTOR (64 + 4) +#define TINYGLTF_TYPE_MATRIX (64 + 16) + +#define TINYGLTF_IMAGE_FORMAT_JPEG (0) +#define TINYGLTF_IMAGE_FORMAT_PNG (1) +#define TINYGLTF_IMAGE_FORMAT_BMP (2) +#define TINYGLTF_IMAGE_FORMAT_GIF (3) + +#define TINYGLTF_TEXTURE_FORMAT_ALPHA (6406) +#define TINYGLTF_TEXTURE_FORMAT_RGB (6407) +#define TINYGLTF_TEXTURE_FORMAT_RGBA (6408) +#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE (6409) +#define TINYGLTF_TEXTURE_FORMAT_LUMINANCE_ALPHA (6410) + +#define TINYGLTF_TEXTURE_TARGET_TEXTURE2D (3553) +#define TINYGLTF_TEXTURE_TYPE_UNSIGNED_BYTE (5121) + +#define TINYGLTF_TARGET_ARRAY_BUFFER (34962) +#define TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER (34963) + +#define TINYGLTF_SHADER_TYPE_VERTEX_SHADER (35633) +#define TINYGLTF_SHADER_TYPE_FRAGMENT_SHADER (35632) + +#define TINYGLTF_DOUBLE_EPS (1.e-12) +#define TINYGLTF_DOUBLE_EQUAL(a, b) (std::fabs((b) - (a)) < TINYGLTF_DOUBLE_EPS) + +#ifdef __ANDROID__ +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS +AAssetManager *asset_manager = nullptr; +#endif +#endif + +typedef enum { + NULL_TYPE, + REAL_TYPE, + INT_TYPE, + BOOL_TYPE, + STRING_TYPE, + ARRAY_TYPE, + BINARY_TYPE, + OBJECT_TYPE +} Type; + +static inline int32_t GetComponentSizeInBytes(uint32_t componentType) { + if (componentType == TINYGLTF_COMPONENT_TYPE_BYTE) { + return 1; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { + return 1; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_SHORT) { + return 2; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { + return 2; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_INT) { + return 4; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { + return 4; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) { + return 4; + } else if (componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE) { + return 8; + } else { + // Unknown componenty type + return -1; + } +} + +static inline int32_t GetNumComponentsInType(uint32_t ty) { + if (ty == TINYGLTF_TYPE_SCALAR) { + return 1; + } else if (ty == TINYGLTF_TYPE_VEC2) { + return 2; + } else if (ty == TINYGLTF_TYPE_VEC3) { + return 3; + } else if (ty == TINYGLTF_TYPE_VEC4) { + return 4; + } else if (ty == TINYGLTF_TYPE_MAT2) { + return 4; + } else if (ty == TINYGLTF_TYPE_MAT3) { + return 9; + } else if (ty == TINYGLTF_TYPE_MAT4) { + return 16; + } else { + // Unknown componenty type + return -1; + } +} + +// TODO(syoyo): Move these functions to TinyGLTF class +bool IsDataURI(const std::string &in); +bool DecodeDataURI(std::vector *out, std::string &mime_type, + const std::string &in, size_t reqBytes, bool checkSize); + +#ifdef __clang__ +#pragma clang diagnostic push +// Suppress warning for : static Value null_value +// https://stackoverflow.com/questions/15708411/how-to-deal-with-global-constructor-warning-in-clang +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wpadded" +#endif + +// Simple class to represent JSON object +class Value { + public: + typedef std::vector Array; + typedef std::map Object; + + Value() + : type_(NULL_TYPE), + int_value_(0), + real_value_(0.0), + boolean_value_(false) {} + + explicit Value(bool b) : type_(BOOL_TYPE) { boolean_value_ = b; } + explicit Value(int i) : type_(INT_TYPE) { + int_value_ = i; + real_value_ = i; + } + explicit Value(double n) : type_(REAL_TYPE) { real_value_ = n; } + explicit Value(const std::string &s) : type_(STRING_TYPE) { + string_value_ = s; + } + explicit Value(std::string &&s) + : type_(STRING_TYPE), string_value_(std::move(s)) {} + explicit Value(const unsigned char *p, size_t n) : type_(BINARY_TYPE) { + binary_value_.resize(n); + memcpy(binary_value_.data(), p, n); + } + explicit Value(std::vector &&v) noexcept + : type_(BINARY_TYPE), + binary_value_(std::move(v)) {} + explicit Value(const Array &a) : type_(ARRAY_TYPE) { array_value_ = a; } + explicit Value(Array &&a) noexcept : type_(ARRAY_TYPE), + array_value_(std::move(a)) {} + + explicit Value(const Object &o) : type_(OBJECT_TYPE) { object_value_ = o; } + explicit Value(Object &&o) noexcept : type_(OBJECT_TYPE), + object_value_(std::move(o)) {} + + DEFAULT_METHODS(Value) + + char Type() const { return static_cast(type_); } + + bool IsBool() const { return (type_ == BOOL_TYPE); } + + bool IsInt() const { return (type_ == INT_TYPE); } + + bool IsNumber() const { return (type_ == REAL_TYPE) || (type_ == INT_TYPE); } + + bool IsReal() const { return (type_ == REAL_TYPE); } + + bool IsString() const { return (type_ == STRING_TYPE); } + + bool IsBinary() const { return (type_ == BINARY_TYPE); } + + bool IsArray() const { return (type_ == ARRAY_TYPE); } + + bool IsObject() const { return (type_ == OBJECT_TYPE); } + + // Use this function if you want to have number value as double. + double GetNumberAsDouble() const { + if (type_ == INT_TYPE) { + return double(int_value_); + } else { + return real_value_; + } + } + + // Use this function if you want to have number value as int. + // TODO(syoyo): Support int value larger than 32 bits + int GetNumberAsInt() const { + if (type_ == REAL_TYPE) { + return int(real_value_); + } else { + return int_value_; + } + } + + // Accessor + template + const T &Get() const; + template + T &Get(); + + // Lookup value from an array + const Value &Get(int idx) const { + static Value null_value; + assert(IsArray()); + assert(idx >= 0); + return (static_cast(idx) < array_value_.size()) + ? array_value_[static_cast(idx)] + : null_value; + } + + // Lookup value from a key-value pair + const Value &Get(const std::string &key) const { + static Value null_value; + assert(IsObject()); + Object::const_iterator it = object_value_.find(key); + return (it != object_value_.end()) ? it->second : null_value; + } + + size_t ArrayLen() const { + if (!IsArray()) return 0; + return array_value_.size(); + } + + // Valid only for object type. + bool Has(const std::string &key) const { + if (!IsObject()) return false; + Object::const_iterator it = object_value_.find(key); + return (it != object_value_.end()) ? true : false; + } + + // List keys + std::vector Keys() const { + std::vector keys; + if (!IsObject()) return keys; // empty + + for (Object::const_iterator it = object_value_.begin(); + it != object_value_.end(); ++it) { + keys.push_back(it->first); + } + + return keys; + } + + size_t Size() const { return (IsArray() ? ArrayLen() : Keys().size()); } + + bool operator==(const tinygltf::Value &other) const; + + protected: + int type_ = NULL_TYPE; + + int int_value_ = 0; + double real_value_ = 0.0; + std::string string_value_; + std::vector binary_value_; + Array array_value_; + Object object_value_; + bool boolean_value_ = false; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#define TINYGLTF_VALUE_GET(ctype, var) \ + template <> \ + inline const ctype &Value::Get() const { \ + return var; \ + } \ + template <> \ + inline ctype &Value::Get() { \ + return var; \ + } +TINYGLTF_VALUE_GET(bool, boolean_value_) +TINYGLTF_VALUE_GET(double, real_value_) +TINYGLTF_VALUE_GET(int, int_value_) +TINYGLTF_VALUE_GET(std::string, string_value_) +TINYGLTF_VALUE_GET(std::vector, binary_value_) +TINYGLTF_VALUE_GET(Value::Array, array_value_) +TINYGLTF_VALUE_GET(Value::Object, object_value_) +#undef TINYGLTF_VALUE_GET + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wpadded" +#endif + +/// Agregate object for representing a color +using ColorValue = std::array; + +// === legacy interface ==== +// TODO(syoyo): Deprecate `Parameter` class. +struct Parameter { + bool bool_value = false; + bool has_number_value = false; + std::string string_value; + std::vector number_array; + std::map json_double_value; + double number_value = 0.0; + + // context sensitive methods. depending the type of the Parameter you are + // accessing, these are either valid or not + // If this parameter represent a texture map in a material, will return the + // texture index + + /// Return the index of a texture if this Parameter is a texture map. + /// Returned value is only valid if the parameter represent a texture from a + /// material + int TextureIndex() const { + const auto it = json_double_value.find("index"); + if (it != std::end(json_double_value)) { + return int(it->second); + } + return -1; + } + + /// Return the index of a texture coordinate set if this Parameter is a + /// texture map. Returned value is only valid if the parameter represent a + /// texture from a material + int TextureTexCoord() const { + const auto it = json_double_value.find("texCoord"); + if (it != std::end(json_double_value)) { + return int(it->second); + } + // As per the spec, if texCoord is ommited, this parameter is 0 + return 0; + } + + /// Return the scale of a texture if this Parameter is a normal texture map. + /// Returned value is only valid if the parameter represent a normal texture + /// from a material + double TextureScale() const { + const auto it = json_double_value.find("scale"); + if (it != std::end(json_double_value)) { + return it->second; + } + // As per the spec, if scale is ommited, this paramter is 1 + return 1; + } + + /// Return the strength of a texture if this Parameter is a an occlusion map. + /// Returned value is only valid if the parameter represent an occlusion map + /// from a material + double TextureStrength() const { + const auto it = json_double_value.find("strength"); + if (it != std::end(json_double_value)) { + return it->second; + } + // As per the spec, if strenghth is ommited, this parameter is 1 + return 1; + } + + /// Material factor, like the roughness or metalness of a material + /// Returned value is only valid if the parameter represent a texture from a + /// material + double Factor() const { return number_value; } + + /// Return the color of a material + /// Returned value is only valid if the parameter represent a texture from a + /// material + ColorValue ColorFactor() const { + return { + {// this agregate intialize the std::array object, and uses C++11 RVO. + number_array[0], number_array[1], number_array[2], + (number_array.size() > 3 ? number_array[3] : 1.0)}}; + } + + Parameter() = default; + DEFAULT_METHODS(Parameter) + bool operator==(const Parameter &) const; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wpadded" +#endif + +typedef std::map ParameterMap; +typedef std::map ExtensionMap; + +struct AnimationChannel { + int sampler; // required + int target_node; // required (index of the node to target) + std::string target_path; // required in ["translation", "rotation", "scale", + // "weights"] + Value extras; + ExtensionMap extensions; + ExtensionMap target_extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + std::string target_extensions_json_string; + + AnimationChannel() : sampler(-1), target_node(-1) {} + DEFAULT_METHODS(AnimationChannel) + bool operator==(const AnimationChannel &) const; +}; + +struct AnimationSampler { + int input; // required + int output; // required + std::string interpolation; // "LINEAR", "STEP","CUBICSPLINE" or user defined + // string. default "LINEAR" + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + AnimationSampler() : input(-1), output(-1), interpolation("LINEAR") {} + DEFAULT_METHODS(AnimationSampler) + bool operator==(const AnimationSampler &) const; +}; + +struct Animation { + std::string name; + std::vector channels; + std::vector samplers; + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Animation() = default; + DEFAULT_METHODS(Animation) + bool operator==(const Animation &) const; +}; + +struct Skin { + std::string name; + int inverseBindMatrices; // required here but not in the spec + int skeleton; // The index of the node used as a skeleton root + std::vector joints; // Indices of skeleton nodes + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Skin() { + inverseBindMatrices = -1; + skeleton = -1; + } + DEFAULT_METHODS(Skin) + bool operator==(const Skin &) const; +}; + +struct Sampler { + std::string name; + // glTF 2.0 spec does not define default value for `minFilter` and + // `magFilter`. Set -1 in TinyGLTF(issue #186) + int minFilter = + -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR", + // "NEAREST_MIPMAP_NEAREST", "LINEAR_MIPMAP_NEAREST", + // "NEAREST_MIPMAP_LINEAR", "LINEAR_MIPMAP_LINEAR"] + int magFilter = + -1; // optional. -1 = no filter defined. ["NEAREST", "LINEAR"] + int wrapS = + TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", + // "REPEAT"], default "REPEAT" + int wrapT = + TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", + // "REPEAT"], default "REPEAT" + //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used. + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Sampler() + : minFilter(-1), + magFilter(-1), + wrapS(TINYGLTF_TEXTURE_WRAP_REPEAT), + wrapT(TINYGLTF_TEXTURE_WRAP_REPEAT) {} + DEFAULT_METHODS(Sampler) + bool operator==(const Sampler &) const; +}; + +struct Image { + std::string name; + int width; + int height; + int component; + int bits; // bit depth per channel. 8(byte), 16 or 32. + int pixel_type; // pixel type(TINYGLTF_COMPONENT_TYPE_***). usually + // UBYTE(bits = 8) or USHORT(bits = 16) + std::vector image; + int bufferView; // (required if no uri) + std::string mimeType; // (required if no uri) ["image/jpeg", "image/png", + // "image/bmp", "image/gif"] + std::string uri; // (required if no mimeType) uri is not decoded(e.g. + // whitespace may be represented as %20) + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + // When this flag is true, data is stored to `image` in as-is format(e.g. jpeg + // compressed for "image/jpeg" mime) This feature is good if you use custom + // image loader function. (e.g. delayed decoding of images for faster glTF + // parsing) Default parser for Image does not provide as-is loading feature at + // the moment. (You can manipulate this by providing your own LoadImageData + // function) + bool as_is; + + Image() : as_is(false) { + bufferView = -1; + width = -1; + height = -1; + component = -1; + bits = -1; + pixel_type = -1; + } + DEFAULT_METHODS(Image) + + bool operator==(const Image &) const; +}; + +struct Texture { + std::string name; + + int sampler; + int source; + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Texture() : sampler(-1), source(-1) {} + DEFAULT_METHODS(Texture) + + bool operator==(const Texture &) const; +}; + +struct TextureInfo { + int index = -1; // required. + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + TextureInfo() : index(-1), texCoord(0) {} + DEFAULT_METHODS(TextureInfo) + bool operator==(const TextureInfo &) const; +}; + +struct NormalTextureInfo { + int index = -1; // required + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + double scale; // scaledNormal = normalize(( + // * 2.0 - 1.0) * vec3(, , 1.0)) + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + NormalTextureInfo() : index(-1), texCoord(0), scale(1.0) {} + DEFAULT_METHODS(NormalTextureInfo) + bool operator==(const NormalTextureInfo &) const; +}; + +struct OcclusionTextureInfo { + int index = -1; // required + int texCoord; // The set index of texture's TEXCOORD attribute used for + // texture coordinate mapping. + double strength; // occludedColor = lerp(color, color * , ) + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + OcclusionTextureInfo() : index(-1), texCoord(0), strength(1.0) {} + DEFAULT_METHODS(OcclusionTextureInfo) + bool operator==(const OcclusionTextureInfo &) const; +}; + +// pbrMetallicRoughness class defined in glTF 2.0 spec. +struct PbrMetallicRoughness { + std::vector baseColorFactor; // len = 4. default [1,1,1,1] + TextureInfo baseColorTexture; + double metallicFactor; // default 1 + double roughnessFactor; // default 1 + TextureInfo metallicRoughnessTexture; + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + PbrMetallicRoughness() + : baseColorFactor(std::vector{1.0, 1.0, 1.0, 1.0}), + metallicFactor(1.0), + roughnessFactor(1.0) {} + DEFAULT_METHODS(PbrMetallicRoughness) + bool operator==(const PbrMetallicRoughness &) const; +}; + +// Each extension should be stored in a ParameterMap. +// members not in the values could be included in the ParameterMap +// to keep a single material model +struct Material { + std::string name; + + std::vector emissiveFactor; // length 3. default [0, 0, 0] + std::string alphaMode; // default "OPAQUE" + double alphaCutoff; // default 0.5 + bool doubleSided; // default false; + + PbrMetallicRoughness pbrMetallicRoughness; + + NormalTextureInfo normalTexture; + OcclusionTextureInfo occlusionTexture; + TextureInfo emissiveTexture; + + // For backward compatibility + // TODO(syoyo): Remove `values` and `additionalValues` in the next release. + ParameterMap values; + ParameterMap additionalValues; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Material() : alphaMode("OPAQUE"), alphaCutoff(0.5), doubleSided(false) {} + DEFAULT_METHODS(Material) + + bool operator==(const Material &) const; +}; + +struct BufferView { + std::string name; + int buffer{-1}; // Required + size_t byteOffset{0}; // minimum 0, default 0 + size_t byteLength{0}; // required, minimum 1. 0 = invalid + size_t byteStride{0}; // minimum 4, maximum 252 (multiple of 4), default 0 = + // understood to be tightly packed + int target{0}; // ["ARRAY_BUFFER", "ELEMENT_ARRAY_BUFFER"] for vertex indices + // or atttribs. Could be 0 for other data + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + bool dracoDecoded{false}; // Flag indicating this has been draco decoded + + BufferView() + : buffer(-1), + byteOffset(0), + byteLength(0), + byteStride(0), + target(0), + dracoDecoded(false) {} + DEFAULT_METHODS(BufferView) + bool operator==(const BufferView &) const; +}; + +struct Accessor { + int bufferView; // optional in spec but required here since sparse accessor + // are not supported + std::string name; + size_t byteOffset; + bool normalized; // optional. + int componentType; // (required) One of TINYGLTF_COMPONENT_TYPE_*** + size_t count; // required + int type; // (required) One of TINYGLTF_TYPE_*** .. + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + std::vector + minValues; // optional. integer value is promoted to double + std::vector + maxValues; // optional. integer value is promoted to double + + struct { + int count; + bool isSparse; + struct { + int byteOffset; + int bufferView; + int componentType; // a TINYGLTF_COMPONENT_TYPE_ value + } indices; + struct { + int bufferView; + int byteOffset; + } values; + } sparse; + + /// + /// Utility function to compute byteStride for a given bufferView object. + /// Returns -1 upon invalid glTF value or parameter configuration. + /// + int ByteStride(const BufferView &bufferViewObject) const { + if (bufferViewObject.byteStride == 0) { + // Assume data is tightly packed. + int componentSizeInBytes = + GetComponentSizeInBytes(static_cast(componentType)); + if (componentSizeInBytes <= 0) { + return -1; + } + + int numComponents = GetNumComponentsInType(static_cast(type)); + if (numComponents <= 0) { + return -1; + } + + return componentSizeInBytes * numComponents; + } else { + // Check if byteStride is a mulple of the size of the accessor's component + // type. + int componentSizeInBytes = + GetComponentSizeInBytes(static_cast(componentType)); + if (componentSizeInBytes <= 0) { + return -1; + } + + if ((bufferViewObject.byteStride % uint32_t(componentSizeInBytes)) != 0) { + return -1; + } + return static_cast(bufferViewObject.byteStride); + } + + // unreachable return 0; + } + + Accessor() + : bufferView(-1), + byteOffset(0), + normalized(false), + componentType(-1), + count(0), + type(-1) { + sparse.isSparse = false; + } + DEFAULT_METHODS(Accessor) + bool operator==(const tinygltf::Accessor &) const; +}; + +struct PerspectiveCamera { + double aspectRatio; // min > 0 + double yfov; // required. min > 0 + double zfar; // min > 0 + double znear; // required. min > 0 + + PerspectiveCamera() + : aspectRatio(0.0), + yfov(0.0), + zfar(0.0) // 0 = use infinite projecton matrix + , + znear(0.0) {} + DEFAULT_METHODS(PerspectiveCamera) + bool operator==(const PerspectiveCamera &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct OrthographicCamera { + double xmag; // required. must not be zero. + double ymag; // required. must not be zero. + double zfar; // required. `zfar` must be greater than `znear`. + double znear; // required + + OrthographicCamera() : xmag(0.0), ymag(0.0), zfar(0.0), znear(0.0) {} + DEFAULT_METHODS(OrthographicCamera) + bool operator==(const OrthographicCamera &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Camera { + std::string type; // required. "perspective" or "orthographic" + std::string name; + + PerspectiveCamera perspective; + OrthographicCamera orthographic; + + Camera() {} + DEFAULT_METHODS(Camera) + bool operator==(const Camera &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Primitive { + std::map attributes; // (required) A dictionary object of + // integer, where each integer + // is the index of the accessor + // containing an attribute. + int material; // The index of the material to apply to this primitive + // when rendering. + int indices; // The index of the accessor that contains the indices. + int mode; // one of TINYGLTF_MODE_*** + std::vector > targets; // array of morph targets, + // where each target is a dict with attribues in ["POSITION, "NORMAL", + // "TANGENT"] pointing + // to their corresponding accessors + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Primitive() { + material = -1; + indices = -1; + mode = -1; + } + DEFAULT_METHODS(Primitive) + bool operator==(const Primitive &) const; +}; + +struct Mesh { + std::string name; + std::vector primitives; + std::vector weights; // weights to be applied to the Morph Targets + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Mesh() = default; + DEFAULT_METHODS(Mesh) + bool operator==(const Mesh &) const; +}; + +class Node { + public: + Node() : camera(-1), skin(-1), mesh(-1) {} + + DEFAULT_METHODS(Node) + + bool operator==(const Node &) const; + + int camera; // the index of the camera referenced by this node + + std::string name; + int skin; + int mesh; + std::vector children; + std::vector rotation; // length must be 0 or 4 + std::vector scale; // length must be 0 or 3 + std::vector translation; // length must be 0 or 3 + std::vector matrix; // length must be 0 or 16 + std::vector weights; // The weights of the instantiated Morph Target + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Buffer { + std::string name; + std::vector data; + std::string + uri; // considered as required here but not in the spec (need to clarify) + // uri is not decoded(e.g. whitespace may be represented as %20) + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Buffer() = default; + DEFAULT_METHODS(Buffer) + bool operator==(const Buffer &) const; +}; + +struct Asset { + std::string version = "2.0"; // required + std::string generator; + std::string minVersion; + std::string copyright; + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Asset() = default; + DEFAULT_METHODS(Asset) + bool operator==(const Asset &) const; +}; + +struct Scene { + std::string name; + std::vector nodes; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; + + Scene() = default; + DEFAULT_METHODS(Scene) + bool operator==(const Scene &) const; +}; + +struct SpotLight { + double innerConeAngle; + double outerConeAngle; + + SpotLight() : innerConeAngle(0.0), outerConeAngle(0.7853981634) {} + DEFAULT_METHODS(SpotLight) + bool operator==(const SpotLight &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +struct Light { + std::string name; + std::vector color; + double intensity{1.0}; + std::string type; + double range{0.0}; // 0.0 = inifinite + SpotLight spot; + + Light() : intensity(1.0), range(0.0) {} + DEFAULT_METHODS(Light) + + bool operator==(const Light &) const; + + ExtensionMap extensions; + Value extras; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +class Model { + public: + Model() = default; + DEFAULT_METHODS(Model) + + bool operator==(const Model &) const; + + std::vector accessors; + std::vector animations; + std::vector buffers; + std::vector bufferViews; + std::vector materials; + std::vector meshes; + std::vector nodes; + std::vector textures; + std::vector images; + std::vector skins; + std::vector samplers; + std::vector cameras; + std::vector scenes; + std::vector lights; + + int defaultScene = -1; + std::vector extensionsUsed; + std::vector extensionsRequired; + + Asset asset; + + Value extras; + ExtensionMap extensions; + + // Filled when SetStoreOriginalJSONForExtrasAndExtensions is enabled. + std::string extras_json_string; + std::string extensions_json_string; +}; + +enum SectionCheck { + NO_REQUIRE = 0x00, + REQUIRE_VERSION = 0x01, + REQUIRE_SCENE = 0x02, + REQUIRE_SCENES = 0x04, + REQUIRE_NODES = 0x08, + REQUIRE_ACCESSORS = 0x10, + REQUIRE_BUFFERS = 0x20, + REQUIRE_BUFFER_VIEWS = 0x40, + REQUIRE_ALL = 0x7f +}; + +/// +/// LoadImageDataFunction type. Signature for custom image loading callbacks. +/// +typedef bool (*LoadImageDataFunction)(Image *, const int, std::string *, + std::string *, int, int, + const unsigned char *, int, + void *user_pointer); + +/// +/// WriteImageDataFunction type. Signature for custom image writing callbacks. +/// +typedef bool (*WriteImageDataFunction)(const std::string *, const std::string *, + Image *, bool, void *); + +#ifndef TINYGLTF_NO_STB_IMAGE +// Declaration of default image loader callback +bool LoadImageData(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *); +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +// Declaration of default image writer callback +bool WriteImageData(const std::string *basepath, const std::string *filename, + Image *image, bool embedImages, void *); +#endif + +/// +/// FilExistsFunction type. Signature for custom filesystem callbacks. +/// +typedef bool (*FileExistsFunction)(const std::string &abs_filename, void *); + +/// +/// ExpandFilePathFunction type. Signature for custom filesystem callbacks. +/// +typedef std::string (*ExpandFilePathFunction)(const std::string &, void *); + +/// +/// ReadWholeFileFunction type. Signature for custom filesystem callbacks. +/// +typedef bool (*ReadWholeFileFunction)(std::vector *, + std::string *, const std::string &, + void *); + +/// +/// WriteWholeFileFunction type. Signature for custom filesystem callbacks. +/// +typedef bool (*WriteWholeFileFunction)(std::string *, const std::string &, + const std::vector &, + void *); + +/// +/// A structure containing all required filesystem callbacks and a pointer to +/// their user data. +/// +struct FsCallbacks { + FileExistsFunction FileExists; + ExpandFilePathFunction ExpandFilePath; + ReadWholeFileFunction ReadWholeFile; + WriteWholeFileFunction WriteWholeFile; + + void *user_data; // An argument that is passed to all fs callbacks +}; + +#ifndef TINYGLTF_NO_FS +// Declaration of default filesystem callbacks + +bool FileExists(const std::string &abs_filename, void *); + +/// +/// Expand file path(e.g. `~` to home directory on posix, `%APPDATA%` to +/// `C:\\Users\\tinygltf\\AppData`) +/// +/// @param[in] filepath File path string. Assume UTF-8 +/// @param[in] userdata User data. Set to `nullptr` if you don't need it. +/// +std::string ExpandFilePath(const std::string &filepath, void *userdata); + +bool ReadWholeFile(std::vector *out, std::string *err, + const std::string &filepath, void *); + +bool WriteWholeFile(std::string *err, const std::string &filepath, + const std::vector &contents, void *); +#endif + +/// +/// glTF Parser/Serialier context. +/// +class TinyGLTF { + public: +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +#endif + + TinyGLTF() : bin_data_(nullptr), bin_size_(0), is_binary_(false) {} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + + ~TinyGLTF() {} + + /// + /// Loads glTF ASCII asset from a file. + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadASCIIFromFile(Model *model, std::string *err, std::string *warn, + const std::string &filename, + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Loads glTF ASCII asset from string(memory). + /// `length` = strlen(str); + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, + const char *str, const unsigned int length, + const std::string &base_dir, + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Loads glTF binary asset from a file. + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadBinaryFromFile(Model *model, std::string *err, std::string *warn, + const std::string &filename, + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Loads glTF binary asset from memory. + /// `length` = strlen(str); + /// Set warning message to `warn` for example it fails to load asserts. + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadBinaryFromMemory(Model *model, std::string *err, std::string *warn, + const unsigned char *bytes, + const unsigned int length, + const std::string &base_dir = "", + unsigned int check_sections = REQUIRE_VERSION); + + /// + /// Write glTF to stream, buffers and images will be embeded + /// + bool WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool prettyPrint, bool writeBinary); + + /// + /// Write glTF to file. + /// + bool WriteGltfSceneToFile(Model *model, const std::string &filename, + bool embedImages, bool embedBuffers, + bool prettyPrint, bool writeBinary); + + /// + /// Set callback to use for loading image data + /// + void SetImageLoader(LoadImageDataFunction LoadImageData, void *user_data); + + /// + /// Unset(remove) callback of loading image data + /// + void RemoveImageLoader(); + + /// + /// Set callback to use for writing image data + /// + void SetImageWriter(WriteImageDataFunction WriteImageData, void *user_data); + + /// + /// Set callbacks to use for filesystem (fs) access and their user data + /// + void SetFsCallbacks(FsCallbacks callbacks); + + /// + /// Set serializing default values(default = false). + /// When true, default values are force serialized to .glTF. + /// This may be helpfull if you want to serialize a full description of glTF + /// data. + /// + /// TODO(LTE): Supply parsing option as function arguments to + /// `LoadASCIIFromFile()` and others, not by a class method + /// + void SetSerializeDefaultValues(const bool enabled) { + serialize_default_values_ = enabled; + } + + bool GetSerializeDefaultValues() const { return serialize_default_values_; } + + /// + /// Store original JSON string for `extras` and `extensions`. + /// This feature will be useful when the user want to reconstruct custom data + /// structure from JSON string. + /// + void SetStoreOriginalJSONForExtrasAndExtensions(const bool enabled) { + store_original_json_for_extras_and_extensions_ = enabled; + } + + bool GetStoreOriginalJSONForExtrasAndExtensions() const { + return store_original_json_for_extras_and_extensions_; + } + + /// + /// Specify whether preserve image channales when loading images or not. + /// (Not effective when the user suppy their own LoadImageData callbacks) + /// + void SetPreserveImageChannels(bool onoff) { + preserve_image_channels_ = onoff; + } + + bool GetPreserveImageChannels() const { return preserve_image_channels_; } + + private: + /// + /// Loads glTF asset from string(memory). + /// `length` = strlen(str); + /// Set warning message to `warn` for example it fails to load asserts + /// Returns false and set error string to `err` if there's an error. + /// + bool LoadFromString(Model *model, std::string *err, std::string *warn, + const char *str, const unsigned int length, + const std::string &base_dir, unsigned int check_sections); + + const unsigned char *bin_data_ = nullptr; + size_t bin_size_ = 0; + bool is_binary_ = false; + + bool serialize_default_values_ = false; ///< Serialize default values? + + bool store_original_json_for_extras_and_extensions_ = false; + + bool preserve_image_channels_ = false; /// Default false(expand channels to + /// RGBA) for backward compatibility. + + FsCallbacks fs = { +#ifndef TINYGLTF_NO_FS + &tinygltf::FileExists, &tinygltf::ExpandFilePath, + &tinygltf::ReadWholeFile, &tinygltf::WriteWholeFile, + + nullptr // Fs callback user data +#else + nullptr, nullptr, nullptr, nullptr, + + nullptr // Fs callback user data +#endif + }; + + LoadImageDataFunction LoadImageData = +#ifndef TINYGLTF_NO_STB_IMAGE + &tinygltf::LoadImageData; +#else + nullptr; +#endif + void *load_image_user_data_{nullptr}; + bool user_image_loader_{false}; + + WriteImageDataFunction WriteImageData = +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE + &tinygltf::WriteImageData; +#else + nullptr; +#endif + void *write_image_user_data_{nullptr}; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop // -Wpadded +#endif + +} // namespace tinygltf + +#endif // TINY_GLTF_H_ + +#if defined(TINYGLTF_IMPLEMENTATION) || defined(__INTELLISENSE__) +#include +//#include +#ifndef TINYGLTF_NO_FS +#include +#include +#endif +#include + +#ifdef __clang__ +// Disable some warnings for external files. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wconversion" +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wglobal-constructors" +#if __has_warning("-Wreserved-id-macro") +#pragma clang diagnostic ignored "-Wreserved-id-macro" +#endif +#pragma clang diagnostic ignored "-Wdisabled-macro-expansion" +#pragma clang diagnostic ignored "-Wpadded" +#pragma clang diagnostic ignored "-Wc++98-compat" +#pragma clang diagnostic ignored "-Wc++98-compat-pedantic" +#pragma clang diagnostic ignored "-Wdocumentation-unknown-command" +#pragma clang diagnostic ignored "-Wswitch-enum" +#pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#pragma clang diagnostic ignored "-Wweak-vtables" +#pragma clang diagnostic ignored "-Wcovered-switch-default" +#if __has_warning("-Wdouble-promotion") +#pragma clang diagnostic ignored "-Wdouble-promotion" +#endif +#if __has_warning("-Wcomma") +#pragma clang diagnostic ignored "-Wcomma" +#endif +#if __has_warning("-Wzero-as-null-pointer-constant") +#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" +#endif +#if __has_warning("-Wcast-qual") +#pragma clang diagnostic ignored "-Wcast-qual" +#endif +#if __has_warning("-Wmissing-variable-declarations") +#pragma clang diagnostic ignored "-Wmissing-variable-declarations" +#endif +#if __has_warning("-Wmissing-prototypes") +#pragma clang diagnostic ignored "-Wmissing-prototypes" +#endif +#if __has_warning("-Wcast-align") +#pragma clang diagnostic ignored "-Wcast-align" +#endif +#if __has_warning("-Wnewline-eof") +#pragma clang diagnostic ignored "-Wnewline-eof" +#endif +#if __has_warning("-Wunused-parameter") +#pragma clang diagnostic ignored "-Wunused-parameter" +#endif +#if __has_warning("-Wmismatched-tags") +#pragma clang diagnostic ignored "-Wmismatched-tags" +#endif +#if __has_warning("-Wextra-semi-stmt") +#pragma clang diagnostic ignored "-Wextra-semi-stmt" +#endif +#endif + +// Disable GCC warnigs +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wtype-limits" +#endif // __GNUC__ + +#ifndef TINYGLTF_NO_INCLUDE_JSON +#ifndef TINYGLTF_USE_RAPIDJSON +#include "json.hpp" +#else +#ifndef TINYGLTF_NO_INCLUDE_RAPIDJSON +#include "document.h" +#include "prettywriter.h" +#include "rapidjson.h" +#include "stringbuffer.h" +#include "writer.h" +#endif +#endif +#endif + +#ifdef TINYGLTF_ENABLE_DRACO +#include "draco/compression/decode.h" +#include "draco/core/decoder_buffer.h" +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE +#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE +#include "stb_image.h" +#endif +#endif + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +#ifndef TINYGLTF_NO_INCLUDE_STB_IMAGE_WRITE +#include "stb_image_write.h" +#endif +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#ifdef _WIN32 + +// issue 143. +// Define NOMINMAX to avoid min/max defines, +// but undef it after included windows.h +#ifndef NOMINMAX +#define TINYGLTF_INTERNAL_NOMINMAX +#define NOMINMAX +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#define TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN +#endif +#include // include API for expanding a file path + +#ifdef TINYGLTF_INTERNAL_WIN32_LEAN_AND_MEAN +#undef WIN32_LEAN_AND_MEAN +#endif + +#if defined(TINYGLTF_INTERNAL_NOMINMAX) +#undef NOMINMAX +#endif + +#if defined(__GLIBCXX__) // mingw + +#include // _O_RDONLY + +#include // fstream (all sorts of IO stuff) + stdio_filebuf (=streambuf) + +#endif + +#elif !defined(__ANDROID__) && !defined(__OpenBSD__) +#include +#endif + +#if defined(__sparcv9) +// Big endian +#else +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU +#define TINYGLTF_LITTLE_ENDIAN 1 +#endif +#endif + +namespace { +#ifdef TINYGLTF_USE_RAPIDJSON + +#ifdef TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR +// This uses the RapidJSON CRTAllocator. It is thread safe and multiple +// documents may be active at once. +using json = + rapidjson::GenericValue, rapidjson::CrtAllocator>; +using json_const_iterator = json::ConstMemberIterator; +using json_const_array_iterator = json const *; +using JsonDocument = + rapidjson::GenericDocument, rapidjson::CrtAllocator>; +rapidjson::CrtAllocator s_CrtAllocator; // stateless and thread safe +rapidjson::CrtAllocator &GetAllocator() { return s_CrtAllocator; } +#else +// This uses the default RapidJSON MemoryPoolAllocator. It is very fast, but +// not thread safe. Only a single JsonDocument may be active at any one time, +// meaning only a single gltf load/save can be active any one time. +using json = rapidjson::Value; +using json_const_iterator = json::ConstMemberIterator; +using json_const_array_iterator = json const *; +rapidjson::Document *s_pActiveDocument = nullptr; +rapidjson::Document::AllocatorType &GetAllocator() { + assert(s_pActiveDocument); // Root json node must be JsonDocument type + return s_pActiveDocument->GetAllocator(); +} + +#ifdef __clang__ +#pragma clang diagnostic push +// Suppress JsonDocument(JsonDocument &&rhs) noexcept +#pragma clang diagnostic ignored "-Wunused-member-function" +#endif + +struct JsonDocument : public rapidjson::Document { + JsonDocument() { + assert(s_pActiveDocument == + nullptr); // When using default allocator, only one document can be + // active at a time, if you need multiple active at once, + // define TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR + s_pActiveDocument = this; + } + JsonDocument(const JsonDocument &) = delete; + JsonDocument(JsonDocument &&rhs) noexcept + : rapidjson::Document(std::move(rhs)) { + s_pActiveDocument = this; + rhs.isNil = true; + } + ~JsonDocument() { + if (!isNil) { + s_pActiveDocument = nullptr; + } + } + + private: + bool isNil = false; +}; + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TINYGLTF_USE_RAPIDJSON_CRTALLOCATOR + +#else +using nlohmann::json; +using json_const_iterator = json::const_iterator; +using json_const_array_iterator = json_const_iterator; +using JsonDocument = json; +#endif + +void JsonParse(JsonDocument &doc, const char *str, size_t length, + bool throwExc = false) { +#ifdef TINYGLTF_USE_RAPIDJSON + (void)throwExc; + doc.Parse(str, length); +#else + doc = json::parse(str, str + length, nullptr, throwExc); +#endif +} +} // namespace + +#ifdef __APPLE__ +#include "TargetConditionals.h" +#endif + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc++98-compat" +#endif + +namespace tinygltf { + +/// +/// Internal LoadImageDataOption struct. +/// This struct is passed through `user_pointer` in LoadImageData. +/// The struct is not passed when the user supply their own LoadImageData +/// callbacks. +/// +struct LoadImageDataOption { + // true: preserve image channels(e.g. load as RGB image if the image has RGB + // channels) default `false`(channels are expanded to RGBA for backward + // compatiblity). + bool preserve_channels{false}; +}; + +// Equals function for Value, for recursivity +static bool Equals(const tinygltf::Value &one, const tinygltf::Value &other) { + if (one.Type() != other.Type()) return false; + + switch (one.Type()) { + case NULL_TYPE: + return true; + case BOOL_TYPE: + return one.Get() == other.Get(); + case REAL_TYPE: + return TINYGLTF_DOUBLE_EQUAL(one.Get(), other.Get()); + case INT_TYPE: + return one.Get() == other.Get(); + case OBJECT_TYPE: { + auto oneObj = one.Get(); + auto otherObj = other.Get(); + if (oneObj.size() != otherObj.size()) return false; + for (auto &it : oneObj) { + auto otherIt = otherObj.find(it.first); + if (otherIt == otherObj.end()) return false; + + if (!Equals(it.second, otherIt->second)) return false; + } + return true; + } + case ARRAY_TYPE: { + if (one.Size() != other.Size()) return false; + for (int i = 0; i < int(one.Size()); ++i) + if (!Equals(one.Get(i), other.Get(i))) return false; + return true; + } + case STRING_TYPE: + return one.Get() == other.Get(); + case BINARY_TYPE: + return one.Get >() == + other.Get >(); + default: { + // unhandled type + return false; + } + } +} + +// Equals function for std::vector using TINYGLTF_DOUBLE_EPSILON +static bool Equals(const std::vector &one, + const std::vector &other) { + if (one.size() != other.size()) return false; + for (int i = 0; i < int(one.size()); ++i) { + if (!TINYGLTF_DOUBLE_EQUAL(one[size_t(i)], other[size_t(i)])) return false; + } + return true; +} + +bool Accessor::operator==(const Accessor &other) const { + return this->bufferView == other.bufferView && + this->byteOffset == other.byteOffset && + this->componentType == other.componentType && + this->count == other.count && this->extensions == other.extensions && + this->extras == other.extras && + Equals(this->maxValues, other.maxValues) && + Equals(this->minValues, other.minValues) && this->name == other.name && + this->normalized == other.normalized && this->type == other.type; +} +bool Animation::operator==(const Animation &other) const { + return this->channels == other.channels && + this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && this->samplers == other.samplers; +} +bool AnimationChannel::operator==(const AnimationChannel &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->target_node == other.target_node && + this->target_path == other.target_path && + this->sampler == other.sampler; +} +bool AnimationSampler::operator==(const AnimationSampler &other) const { + return this->extras == other.extras && this->extensions == other.extensions && + this->input == other.input && + this->interpolation == other.interpolation && + this->output == other.output; +} +bool Asset::operator==(const Asset &other) const { + return this->copyright == other.copyright && + this->extensions == other.extensions && this->extras == other.extras && + this->generator == other.generator && + this->minVersion == other.minVersion && this->version == other.version; +} +bool Buffer::operator==(const Buffer &other) const { + return this->data == other.data && this->extensions == other.extensions && + this->extras == other.extras && this->name == other.name && + this->uri == other.uri; +} +bool BufferView::operator==(const BufferView &other) const { + return this->buffer == other.buffer && this->byteLength == other.byteLength && + this->byteOffset == other.byteOffset && + this->byteStride == other.byteStride && this->name == other.name && + this->target == other.target && this->extensions == other.extensions && + this->extras == other.extras && + this->dracoDecoded == other.dracoDecoded; +} +bool Camera::operator==(const Camera &other) const { + return this->name == other.name && this->extensions == other.extensions && + this->extras == other.extras && + this->orthographic == other.orthographic && + this->perspective == other.perspective && this->type == other.type; +} +bool Image::operator==(const Image &other) const { + return this->bufferView == other.bufferView && + this->component == other.component && + this->extensions == other.extensions && this->extras == other.extras && + this->height == other.height && this->image == other.image && + this->mimeType == other.mimeType && this->name == other.name && + this->uri == other.uri && this->width == other.width; +} +bool Light::operator==(const Light &other) const { + return Equals(this->color, other.color) && this->name == other.name && + this->type == other.type; +} +bool Material::operator==(const Material &other) const { + return (this->pbrMetallicRoughness == other.pbrMetallicRoughness) && + (this->normalTexture == other.normalTexture) && + (this->occlusionTexture == other.occlusionTexture) && + (this->emissiveTexture == other.emissiveTexture) && + Equals(this->emissiveFactor, other.emissiveFactor) && + (this->alphaMode == other.alphaMode) && + TINYGLTF_DOUBLE_EQUAL(this->alphaCutoff, other.alphaCutoff) && + (this->doubleSided == other.doubleSided) && + (this->extensions == other.extensions) && + (this->extras == other.extras) && (this->values == other.values) && + (this->additionalValues == other.additionalValues) && + (this->name == other.name); +} +bool Mesh::operator==(const Mesh &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && Equals(this->weights, other.weights) && + this->primitives == other.primitives; +} +bool Model::operator==(const Model &other) const { + return this->accessors == other.accessors && + this->animations == other.animations && this->asset == other.asset && + this->buffers == other.buffers && + this->bufferViews == other.bufferViews && + this->cameras == other.cameras && + this->defaultScene == other.defaultScene && + this->extensions == other.extensions && + this->extensionsRequired == other.extensionsRequired && + this->extensionsUsed == other.extensionsUsed && + this->extras == other.extras && this->images == other.images && + this->lights == other.lights && this->materials == other.materials && + this->meshes == other.meshes && this->nodes == other.nodes && + this->samplers == other.samplers && this->scenes == other.scenes && + this->skins == other.skins && this->textures == other.textures; +} +bool Node::operator==(const Node &other) const { + return this->camera == other.camera && this->children == other.children && + this->extensions == other.extensions && this->extras == other.extras && + Equals(this->matrix, other.matrix) && this->mesh == other.mesh && + this->name == other.name && Equals(this->rotation, other.rotation) && + Equals(this->scale, other.scale) && this->skin == other.skin && + Equals(this->translation, other.translation) && + Equals(this->weights, other.weights); +} +bool SpotLight::operator==(const SpotLight &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->innerConeAngle, other.innerConeAngle) && + TINYGLTF_DOUBLE_EQUAL(this->outerConeAngle, other.outerConeAngle); +} +bool OrthographicCamera::operator==(const OrthographicCamera &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->xmag, other.xmag) && + TINYGLTF_DOUBLE_EQUAL(this->ymag, other.ymag) && + TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && + TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); +} +bool Parameter::operator==(const Parameter &other) const { + if (this->bool_value != other.bool_value || + this->has_number_value != other.has_number_value) + return false; + + if (!TINYGLTF_DOUBLE_EQUAL(this->number_value, other.number_value)) + return false; + + if (this->json_double_value.size() != other.json_double_value.size()) + return false; + for (auto &it : this->json_double_value) { + auto otherIt = other.json_double_value.find(it.first); + if (otherIt == other.json_double_value.end()) return false; + + if (!TINYGLTF_DOUBLE_EQUAL(it.second, otherIt->second)) return false; + } + + if (!Equals(this->number_array, other.number_array)) return false; + + if (this->string_value != other.string_value) return false; + + return true; +} +bool PerspectiveCamera::operator==(const PerspectiveCamera &other) const { + return TINYGLTF_DOUBLE_EQUAL(this->aspectRatio, other.aspectRatio) && + this->extensions == other.extensions && this->extras == other.extras && + TINYGLTF_DOUBLE_EQUAL(this->yfov, other.yfov) && + TINYGLTF_DOUBLE_EQUAL(this->zfar, other.zfar) && + TINYGLTF_DOUBLE_EQUAL(this->znear, other.znear); +} +bool Primitive::operator==(const Primitive &other) const { + return this->attributes == other.attributes && this->extras == other.extras && + this->indices == other.indices && this->material == other.material && + this->mode == other.mode && this->targets == other.targets; +} +bool Sampler::operator==(const Sampler &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->magFilter == other.magFilter && + this->minFilter == other.minFilter && this->name == other.name && + this->wrapT == other.wrapT; + + //this->wrapR == other.wrapR && this->wrapS == other.wrapS && +} +bool Scene::operator==(const Scene &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && this->nodes == other.nodes; +} +bool Skin::operator==(const Skin &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->inverseBindMatrices == other.inverseBindMatrices && + this->joints == other.joints && this->name == other.name && + this->skeleton == other.skeleton; +} +bool Texture::operator==(const Texture &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->name == other.name && this->sampler == other.sampler && + this->source == other.source; +} +bool TextureInfo::operator==(const TextureInfo &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->index == other.index && this->texCoord == other.texCoord; +} +bool NormalTextureInfo::operator==(const NormalTextureInfo &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->index == other.index && this->texCoord == other.texCoord && + TINYGLTF_DOUBLE_EQUAL(this->scale, other.scale); +} +bool OcclusionTextureInfo::operator==(const OcclusionTextureInfo &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + this->index == other.index && this->texCoord == other.texCoord && + TINYGLTF_DOUBLE_EQUAL(this->strength, other.strength); +} +bool PbrMetallicRoughness::operator==(const PbrMetallicRoughness &other) const { + return this->extensions == other.extensions && this->extras == other.extras && + (this->baseColorTexture == other.baseColorTexture) && + (this->metallicRoughnessTexture == other.metallicRoughnessTexture) && + Equals(this->baseColorFactor, other.baseColorFactor) && + TINYGLTF_DOUBLE_EQUAL(this->metallicFactor, other.metallicFactor) && + TINYGLTF_DOUBLE_EQUAL(this->roughnessFactor, other.roughnessFactor); +} +bool Value::operator==(const Value &other) const { + return Equals(*this, other); +} + +static void swap4(unsigned int *val) { +#ifdef TINYGLTF_LITTLE_ENDIAN + (void)val; +#else + unsigned int tmp = *val; + unsigned char *dst = reinterpret_cast(val); + unsigned char *src = reinterpret_cast(&tmp); + + dst[0] = src[3]; + dst[1] = src[2]; + dst[2] = src[1]; + dst[3] = src[0]; +#endif +} + +static std::string JoinPath(const std::string &path0, + const std::string &path1) { + if (path0.empty()) { + return path1; + } else { + // check '/' + char lastChar = *path0.rbegin(); + if (lastChar != '/') { + return path0 + std::string("/") + path1; + } else { + return path0 + path1; + } + } +} + +static std::string FindFile(const std::vector &paths, + const std::string &filepath, FsCallbacks *fs) { + if (fs == nullptr || fs->ExpandFilePath == nullptr || + fs->FileExists == nullptr) { + // Error, fs callback[s] missing + return std::string(); + } + + for (size_t i = 0; i < paths.size(); i++) { + std::string absPath = + fs->ExpandFilePath(JoinPath(paths[i], filepath), fs->user_data); + if (fs->FileExists(absPath, fs->user_data)) { + return absPath; + } + } + + return std::string(); +} + +static std::string GetFilePathExtension(const std::string &FileName) { + if (FileName.find_last_of(".") != std::string::npos) + return FileName.substr(FileName.find_last_of(".") + 1); + return ""; +} + +static std::string GetBaseDir(const std::string &filepath) { + if (filepath.find_last_of("/\\") != std::string::npos) + return filepath.substr(0, filepath.find_last_of("/\\")); + return ""; +} + +// https://stackoverflow.com/questions/8520560/get-a-file-name-from-a-path +static std::string GetBaseFilename(const std::string &filepath) { + return filepath.substr(filepath.find_last_of("/\\") + 1); +} + +std::string base64_encode(unsigned char const *, unsigned int len); +std::string base64_decode(std::string const &s); + +/* + base64.cpp and base64.h + + Copyright (C) 2004-2008 René Nyffenegger + + This source code is provided 'as-is', without any express or implied + warranty. In no event will the author be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this source code must not be misrepresented; you must not + claim that you wrote the original source code. If you use this source code + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original source code. + + 3. This notice may not be removed or altered from any source distribution. + + René Nyffenegger rene.nyffenegger@adp-gmbh.ch + +*/ + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wsign-conversion" +#pragma clang diagnostic ignored "-Wconversion" +#endif + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_encode(unsigned char const *bytes_to_encode, + unsigned int in_len) { + std::string ret; + int i = 0; + int j = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + const char *base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = + ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = + ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (i = 0; (i < 4); i++) ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) { + for (j = i; j < 3; j++) char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = + ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = + ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + + for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; + + while ((i++ < 3)) ret += '='; + } + + return ret; +} + +std::string base64_decode(std::string const &encoded_string) { + int in_len = static_cast(encoded_string.size()); + int i = 0; + int j = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + std::string ret; + + const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + + while (in_len-- && (encoded_string[in_] != '=') && + is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; + in_++; + if (i == 4) { + for (i = 0; i < 4; i++) + char_array_4[i] = + static_cast(base64_chars.find(char_array_4[i])); + + char_array_3[0] = + (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = + ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (j = i; j < 4; j++) char_array_4[j] = 0; + + for (j = 0; j < 4; j++) + char_array_4[j] = + static_cast(base64_chars.find(char_array_4[j])); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = + ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; + } + + return ret; +} +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +// https://github.com/syoyo/tinygltf/issues/228 +// TODO(syoyo): Use uriparser https://uriparser.github.io/ for stricter Uri +// decoding? +// +// https://stackoverflow.com/questions/18307429/encode-decode-url-in-c +// http://dlib.net/dlib/server/server_http.cpp.html + +// --- dlib beign ------------------------------------------------------------ +// Copyright (C) 2003 Davis E. King (davis@dlib.net) +// License: Boost Software License See LICENSE.txt for the full license. + +namespace dlib { + +#if 0 + inline unsigned char to_hex( unsigned char x ) + { + return x + (x > 9 ? ('A'-10) : '0'); + } + + const std::string urlencode( const std::string& s ) + { + std::ostringstream os; + + for ( std::string::const_iterator ci = s.begin(); ci != s.end(); ++ci ) + { + if ( (*ci >= 'a' && *ci <= 'z') || + (*ci >= 'A' && *ci <= 'Z') || + (*ci >= '0' && *ci <= '9') ) + { // allowed + os << *ci; + } + else if ( *ci == ' ') + { + os << '+'; + } + else + { + os << '%' << to_hex(static_cast(*ci >> 4)) << to_hex(static_cast(*ci % 16)); + } + } + + return os.str(); + } +#endif + +inline unsigned char from_hex(unsigned char ch) { + if (ch <= '9' && ch >= '0') + ch -= '0'; + else if (ch <= 'f' && ch >= 'a') + ch -= 'a' - 10; + else if (ch <= 'F' && ch >= 'A') + ch -= 'A' - 10; + else + ch = 0; + return ch; +} + +static const std::string urldecode(const std::string &str) { + using namespace std; + string result; + string::size_type i; + for (i = 0; i < str.size(); ++i) { + if (str[i] == '+') { + result += ' '; + } else if (str[i] == '%' && str.size() > i + 2) { + const unsigned char ch1 = + from_hex(static_cast(str[i + 1])); + const unsigned char ch2 = + from_hex(static_cast(str[i + 2])); + const unsigned char ch = static_cast((ch1 << 4) | ch2); + result += static_cast(ch); + i += 2; + } else { + result += str[i]; + } + } + return result; +} + +} // namespace dlib +// --- dlib end -------------------------------------------------------------- + +static bool LoadExternalFile(std::vector *out, std::string *err, + std::string *warn, const std::string &filename, + const std::string &basedir, bool required, + size_t reqBytes, bool checkSize, FsCallbacks *fs) { + if (fs == nullptr || fs->FileExists == nullptr || + fs->ExpandFilePath == nullptr || fs->ReadWholeFile == nullptr) { + // This is a developer error, assert() ? + if (err) { + (*err) += "FS callback[s] not set\n"; + } + return false; + } + + std::string *failMsgOut = required ? err : warn; + + out->clear(); + + std::vector paths; + paths.push_back(basedir); + paths.push_back("."); + + std::string filepath = FindFile(paths, filename, fs); + if (filepath.empty() || filename.empty()) { + if (failMsgOut) { + (*failMsgOut) += "File not found : " + filename + "\n"; + } + return false; + } + + std::vector buf; + std::string fileReadErr; + bool fileRead = + fs->ReadWholeFile(&buf, &fileReadErr, filepath, fs->user_data); + if (!fileRead) { + if (failMsgOut) { + (*failMsgOut) += + "File read error : " + filepath + " : " + fileReadErr + "\n"; + } + return false; + } + + size_t sz = buf.size(); + if (sz == 0) { + if (failMsgOut) { + (*failMsgOut) += "File is empty : " + filepath + "\n"; + } + return false; + } + + if (checkSize) { + if (reqBytes == sz) { + out->swap(buf); + return true; + } else { + std::stringstream ss; + ss << "File size mismatch : " << filepath << ", requestedBytes " + << reqBytes << ", but got " << sz << std::endl; + if (failMsgOut) { + (*failMsgOut) += ss.str(); + } + return false; + } + } + + out->swap(buf); + return true; +} + +void TinyGLTF::SetImageLoader(LoadImageDataFunction func, void *user_data) { + LoadImageData = func; + load_image_user_data_ = user_data; + user_image_loader_ = true; +} + +void TinyGLTF::RemoveImageLoader() { + LoadImageData = +#ifndef TINYGLTF_NO_STB_IMAGE + &tinygltf::LoadImageData; +#else + nullptr; +#endif + + load_image_user_data_ = nullptr; + user_image_loader_ = false; +} + +#ifndef TINYGLTF_NO_STB_IMAGE +bool LoadImageData(Image *image, const int image_idx, std::string *err, + std::string *warn, int req_width, int req_height, + const unsigned char *bytes, int size, void *user_data) { + (void)warn; + + LoadImageDataOption option; + if (user_data) { + option = *reinterpret_cast(user_data); + } + + int w = 0, h = 0, comp = 0, req_comp = 0; + + unsigned char *data = nullptr; + + // preserve_channels true: Use channels stored in the image file. + // false: force 32-bit textures for common Vulkan compatibility. It appears + // that some GPU drivers do not support 24-bit images for Vulkan + req_comp = option.preserve_channels ? 0 : 4; + int bits = 8; + int pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE; + + // It is possible that the image we want to load is a 16bit per channel image + // We are going to attempt to load it as 16bit per channel, and if it worked, + // set the image data accodingly. We are casting the returned pointer into + // unsigned char, because we are representing "bytes". But we are updating + // the Image metadata to signal that this image uses 2 bytes (16bits) per + // channel: + if (stbi_is_16_bit_from_memory(bytes, size)) { + data = reinterpret_cast( + stbi_load_16_from_memory(bytes, size, &w, &h, &comp, req_comp)); + if (data) { + bits = 16; + pixel_type = TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT; + } + } + + // at this point, if data is still NULL, it means that the image wasn't + // 16bit per channel, we are going to load it as a normal 8bit per channel + // mage as we used to do: + // if image cannot be decoded, ignore parsing and keep it by its path + // don't break in this case + // FIXME we should only enter this function if the image is embedded. If + // image->uri references + // an image file, it should be left as it is. Image loading should not be + // mandatory (to support other formats) + if (!data) data = stbi_load_from_memory(bytes, size, &w, &h, &comp, req_comp); + if (!data) { + // NOTE: you can use `warn` instead of `err` + if (err) { + (*err) += + "Unknown image format. STB cannot decode image data for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\".\n"; + } + return false; + } + + if ((w < 1) || (h < 1)) { + stbi_image_free(data); + if (err) { + (*err) += "Invalid image data for image[" + std::to_string(image_idx) + + "] name = \"" + image->name + "\"\n"; + } + return false; + } + + if (req_width > 0) { + if (req_width != w) { + stbi_image_free(data); + if (err) { + (*err) += "Image width mismatch for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + } + + if (req_height > 0) { + if (req_height != h) { + stbi_image_free(data); + if (err) { + (*err) += "Image height mismatch. for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + } + + if (req_comp != 0) { + // loaded data has `req_comp` channels(components) + comp = req_comp; + } + + image->width = w; + image->height = h; + image->component = comp; + image->bits = bits; + image->pixel_type = pixel_type; + image->image.resize(static_cast(w * h * comp) * size_t(bits / 8)); + std::copy(data, data + w * h * comp * (bits / 8), image->image.begin()); + stbi_image_free(data); + + return true; +} +#endif + +void TinyGLTF::SetImageWriter(WriteImageDataFunction func, void *user_data) { + WriteImageData = func; + write_image_user_data_ = user_data; +} + +#ifndef TINYGLTF_NO_STB_IMAGE_WRITE +static void WriteToMemory_stbi(void *context, void *data, int size) { + std::vector *buffer = + reinterpret_cast *>(context); + + unsigned char *pData = reinterpret_cast(data); + + buffer->insert(buffer->end(), pData, pData + size); +} + +bool WriteImageData(const std::string *basepath, const std::string *filename, + Image *image, bool embedImages, void *fsPtr) { + const std::string ext = GetFilePathExtension(*filename); + + // Write image to temporary buffer + std::string header; + std::vector data; + + if (ext == "png") { + if ((image->bits != 8) || + (image->pixel_type != TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE)) { + // Unsupported pixel format + return false; + } + + if (!stbi_write_png_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0], 0)) { + return false; + } + header = "data:image/png;base64,"; + } else if (ext == "jpg") { + if (!stbi_write_jpg_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0], 100)) { + return false; + } + header = "data:image/jpeg;base64,"; + } else if (ext == "bmp") { + if (!stbi_write_bmp_to_func(WriteToMemory_stbi, &data, image->width, + image->height, image->component, + &image->image[0])) { + return false; + } + header = "data:image/bmp;base64,"; + } else if (!embedImages) { + // Error: can't output requested format to file + return false; + } + + if (embedImages) { + // Embed base64-encoded image into URI + if (data.size()) { + image->uri = + header + + base64_encode(&data[0], static_cast(data.size())); + } else { + // Throw error? + } + } else { + // Write image to disc + FsCallbacks *fs = reinterpret_cast(fsPtr); + if ((fs != nullptr) && (fs->WriteWholeFile != nullptr)) { + const std::string imagefilepath = JoinPath(*basepath, *filename); + std::string writeError; + if (!fs->WriteWholeFile(&writeError, imagefilepath, data, + fs->user_data)) { + // Could not write image file to disc; Throw error ? + return false; + } + } else { + // Throw error? + } + image->uri = *filename; + } + + return true; +} +#endif + +void TinyGLTF::SetFsCallbacks(FsCallbacks callbacks) { fs = callbacks; } + +#ifdef _WIN32 +static inline std::wstring UTF8ToWchar(const std::string &str) { + int wstr_size = + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), nullptr, 0); + std::wstring wstr(wstr_size, 0); + MultiByteToWideChar(CP_UTF8, 0, str.data(), (int)str.size(), &wstr[0], + (int)wstr.size()); + return wstr; +} + +static inline std::string WcharToUTF8(const std::wstring &wstr) { + int str_size = WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), + nullptr, 0, NULL, NULL); + std::string str(str_size, 0); + WideCharToMultiByte(CP_UTF8, 0, wstr.data(), (int)wstr.size(), &str[0], + (int)str.size(), NULL, NULL); + return str; +} +#endif + +#ifndef TINYGLTF_NO_FS +// Default implementations of filesystem functions + +bool FileExists(const std::string &abs_filename, void *) { + bool ret; +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS + if (asset_manager) { + AAsset *asset = AAssetManager_open(asset_manager, abs_filename.c_str(), + AASSET_MODE_STREAMING); + if (!asset) { + return false; + } + AAsset_close(asset); + ret = true; + } else { + return false; + } +#else +#ifdef _WIN32 +#if defined(_MSC_VER) || defined(__GLIBCXX__) + FILE *fp = nullptr; + errno_t err = _wfopen_s(&fp, UTF8ToWchar(abs_filename).c_str(), L"rb"); + if (err != 0) { + return false; + } +#else + FILE *fp = nullptr; + errno_t err = fopen_s(&fp, abs_filename.c_str(), "rb"); + if (err != 0) { + return false; + } +#endif + +#else + FILE *fp = fopen(abs_filename.c_str(), "rb"); +#endif + if (fp) { + ret = true; + fclose(fp); + } else { + ret = false; + } +#endif + + return ret; +} + +std::string ExpandFilePath(const std::string &filepath, void *) { +#ifdef _WIN32 + // Assume input `filepath` is encoded in UTF-8 + std::wstring wfilepath = UTF8ToWchar(filepath); + DWORD wlen = ExpandEnvironmentStringsW(wfilepath.c_str(), nullptr, 0); + wchar_t *wstr = new wchar_t[wlen]; + ExpandEnvironmentStringsW(wfilepath.c_str(), wstr, wlen); + + std::wstring ws(wstr); + delete[] wstr; + return WcharToUTF8(ws); + +#else + +#if defined(TARGET_OS_IPHONE) || defined(TARGET_IPHONE_SIMULATOR) || \ + defined(__ANDROID__) || defined(__EMSCRIPTEN__) || defined(__OpenBSD__) + // no expansion + std::string s = filepath; +#else + std::string s; + wordexp_t p; + + if (filepath.empty()) { + return ""; + } + + // Quote the string to keep any spaces in filepath intact. + std::string quoted_path = "\"" + filepath + "\""; + // char** w; + int ret = wordexp(quoted_path.c_str(), &p, 0); + if (ret) { + // err + s = filepath; + return s; + } + + // Use first element only. + if (p.we_wordv) { + s = std::string(p.we_wordv[0]); + wordfree(&p); + } else { + s = filepath; + } + +#endif + + return s; +#endif +} + +bool ReadWholeFile(std::vector *out, std::string *err, + const std::string &filepath, void *) { +#ifdef TINYGLTF_ANDROID_LOAD_FROM_ASSETS + if (asset_manager) { + AAsset *asset = AAssetManager_open(asset_manager, filepath.c_str(), + AASSET_MODE_STREAMING); + if (!asset) { + if (err) { + (*err) += "File open error : " + filepath + "\n"; + } + return false; + } + size_t size = AAsset_getLength(asset); + if (size == 0) { + if (err) { + (*err) += "Invalid file size : " + filepath + + " (does the path point to a directory?)"; + } + return false; + } + out->resize(size); + AAsset_read(asset, reinterpret_cast(&out->at(0)), size); + AAsset_close(asset); + return true; + } else { + if (err) { + (*err) += "No asset manager specified : " + filepath + "\n"; + } + return false; + } +#else +#ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = + _wopen(UTF8ToWchar(filepath).c_str(), _O_RDONLY | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf(file_descriptor, std::ios_base::in); + std::istream f(&wfile_buf); +#elif defined(_MSC_VER) || defined(_LIBCPP_VERSION) + // For libcxx, assume _LIBCPP_HAS_OPEN_WITH_WCHAR is defined to accept + // `wchar_t *` + std::ifstream f(UTF8ToWchar(filepath).c_str(), std::ifstream::binary); +#else + // Unknown compiler/runtime + std::ifstream f(filepath.c_str(), std::ifstream::binary); +#endif +#else + std::ifstream f(filepath.c_str(), std::ifstream::binary); +#endif + if (!f) { + if (err) { + (*err) += "File open error : " + filepath + "\n"; + } + return false; + } + + f.seekg(0, f.end); + size_t sz = static_cast(f.tellg()); + f.seekg(0, f.beg); + + if (int64_t(sz) < 0) { + if (err) { + (*err) += "Invalid file size : " + filepath + + " (does the path point to a directory?)"; + } + return false; + } else if (sz == 0) { + if (err) { + (*err) += "File is empty : " + filepath + "\n"; + } + return false; + } + + out->resize(sz); + f.read(reinterpret_cast(&out->at(0)), + static_cast(sz)); + + return true; +#endif +} + +bool WriteWholeFile(std::string *err, const std::string &filepath, + const std::vector &contents, void *) { +#ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = _wopen(UTF8ToWchar(filepath).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream f(&wfile_buf); +#elif defined(_MSC_VER) + std::ofstream f(UTF8ToWchar(filepath).c_str(), std::ofstream::binary); +#else // clang? + std::ofstream f(filepath.c_str(), std::ofstream::binary); +#endif +#else + std::ofstream f(filepath.c_str(), std::ofstream::binary); +#endif + if (!f) { + if (err) { + (*err) += "File open error for writing : " + filepath + "\n"; + } + return false; + } + + f.write(reinterpret_cast(&contents.at(0)), + static_cast(contents.size())); + if (!f) { + if (err) { + (*err) += "File write error: " + filepath + "\n"; + } + return false; + } + + return true; +} + +#endif // TINYGLTF_NO_FS + +static std::string MimeToExt(const std::string &mimeType) { + if (mimeType == "image/jpeg") { + return "jpg"; + } else if (mimeType == "image/png") { + return "png"; + } else if (mimeType == "image/bmp") { + return "bmp"; + } else if (mimeType == "image/gif") { + return "gif"; + } + + return ""; +} + +static void UpdateImageObject(Image &image, std::string &baseDir, int index, + bool embedImages, + WriteImageDataFunction *WriteImageData = nullptr, + void *user_data = nullptr) { + std::string filename; + std::string ext; + // If image has uri, use it it as a filename + if (image.uri.size()) { + filename = GetBaseFilename(image.uri); + ext = GetFilePathExtension(filename); + } else if (image.bufferView != -1) { + // If there's no URI and the data exists in a buffer, + // don't change properties or write images + } else if (image.name.size()) { + ext = MimeToExt(image.mimeType); + // Otherwise use name as filename + filename = image.name + "." + ext; + } else { + ext = MimeToExt(image.mimeType); + // Fallback to index of image as filename + filename = std::to_string(index) + "." + ext; + } + + // If callback is set, modify image data object + if (*WriteImageData != nullptr && !filename.empty()) { + std::string uri; + (*WriteImageData)(&baseDir, &filename, &image, embedImages, user_data); + } +} + +bool IsDataURI(const std::string &in) { + std::string header = "data:application/octet-stream;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/jpeg;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/png;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/bmp;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:image/gif;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:text/plain;base64,"; + if (in.find(header) == 0) { + return true; + } + + header = "data:application/gltf-buffer;base64,"; + if (in.find(header) == 0) { + return true; + } + + return false; +} + +bool DecodeDataURI(std::vector *out, std::string &mime_type, + const std::string &in, size_t reqBytes, bool checkSize) { + std::string header = "data:application/octet-stream;base64,"; + std::string data; + if (in.find(header) == 0) { + data = base64_decode(in.substr(header.size())); // cut mime string. + } + + if (data.empty()) { + header = "data:image/jpeg;base64,"; + if (in.find(header) == 0) { + mime_type = "image/jpeg"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:image/png;base64,"; + if (in.find(header) == 0) { + mime_type = "image/png"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:image/bmp;base64,"; + if (in.find(header) == 0) { + mime_type = "image/bmp"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:image/gif;base64,"; + if (in.find(header) == 0) { + mime_type = "image/gif"; + data = base64_decode(in.substr(header.size())); // cut mime string. + } + } + + if (data.empty()) { + header = "data:text/plain;base64,"; + if (in.find(header) == 0) { + mime_type = "text/plain"; + data = base64_decode(in.substr(header.size())); + } + } + + if (data.empty()) { + header = "data:application/gltf-buffer;base64,"; + if (in.find(header) == 0) { + data = base64_decode(in.substr(header.size())); + } + } + + // TODO(syoyo): Allow empty buffer? #229 + if (data.empty()) { + return false; + } + + if (checkSize) { + if (data.size() != reqBytes) { + return false; + } + out->resize(reqBytes); + } else { + out->resize(data.size()); + } + std::copy(data.begin(), data.end(), out->begin()); + return true; +} + +namespace { +bool GetInt(const json &o, int &val) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsDouble()) { + if (o.IsInt()) { + val = o.GetInt(); + return true; + } else if (o.IsUint()) { + val = static_cast(o.GetUint()); + return true; + } else if (o.IsInt64()) { + val = static_cast(o.GetInt64()); + return true; + } else if (o.IsUint64()) { + val = static_cast(o.GetUint64()); + return true; + } + } + + return false; +#else + auto type = o.type(); + + if ((type == json::value_t::number_integer) || + (type == json::value_t::number_unsigned)) { + val = static_cast(o.get()); + return true; + } + + return false; +#endif +} + +#ifdef TINYGLTF_USE_RAPIDJSON +bool GetDouble(const json &o, double &val) { + if (o.IsDouble()) { + val = o.GetDouble(); + return true; + } + + return false; +} +#endif + +bool GetNumber(const json &o, double &val) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (o.IsNumber()) { + val = o.GetDouble(); + return true; + } + + return false; +#else + if (o.is_number()) { + val = o.get(); + return true; + } + + return false; +#endif +} + +bool GetString(const json &o, std::string &val) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (o.IsString()) { + val = o.GetString(); + return true; + } + + return false; +#else + if (o.type() == json::value_t::string) { + val = o.get(); + return true; + } + + return false; +#endif +} + +bool IsArray(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.IsArray(); +#else + return o.is_array(); +#endif +} + +json_const_array_iterator ArrayBegin(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.Begin(); +#else + return o.begin(); +#endif +} + +json_const_array_iterator ArrayEnd(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.End(); +#else + return o.end(); +#endif +} + +bool IsObject(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.IsObject(); +#else + return o.is_object(); +#endif +} + +json_const_iterator ObjectBegin(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.MemberBegin(); +#else + return o.begin(); +#endif +} + +json_const_iterator ObjectEnd(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.MemberEnd(); +#else + return o.end(); +#endif +} + +// Making this a const char* results in a pointer to a temporary when +// TINYGLTF_USE_RAPIDJSON is off. +std::string GetKey(json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + return it->name.GetString(); +#else + return it.key().c_str(); +#endif +} + +bool FindMember(const json &o, const char *member, json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsObject()) { + return false; + } + it = o.FindMember(member); + return it != o.MemberEnd(); +#else + it = o.find(member); + return it != o.end(); +#endif +} + +const json &GetValue(json_const_iterator &it) { +#ifdef TINYGLTF_USE_RAPIDJSON + return it->value; +#else + return it.value(); +#endif +} + +std::string JsonToString(const json &o, int spacing = -1) { +#ifdef TINYGLTF_USE_RAPIDJSON + using namespace rapidjson; + StringBuffer buffer; + if (spacing == -1) { + Writer writer(buffer); + o.Accept(writer); + } else { + PrettyWriter writer(buffer); + writer.SetIndent(' ', uint32_t(spacing)); + o.Accept(writer); + } + return buffer.GetString(); +#else + return o.dump(spacing); +#endif +} + +} // namespace + +static bool ParseJsonAsValue(Value *ret, const json &o) { + Value val{}; +#ifdef TINYGLTF_USE_RAPIDJSON + using rapidjson::Type; + switch (o.GetType()) { + case Type::kObjectType: { + Value::Object value_object; + for (auto it = o.MemberBegin(); it != o.MemberEnd(); ++it) { + Value entry; + ParseJsonAsValue(&entry, it->value); + if (entry.Type() != NULL_TYPE) + value_object.emplace(GetKey(it), std::move(entry)); + } + if (value_object.size() > 0) val = Value(std::move(value_object)); + } break; + case Type::kArrayType: { + Value::Array value_array; + value_array.reserve(o.Size()); + for (auto it = o.Begin(); it != o.End(); ++it) { + Value entry; + ParseJsonAsValue(&entry, *it); + if (entry.Type() != NULL_TYPE) + value_array.emplace_back(std::move(entry)); + } + if (value_array.size() > 0) val = Value(std::move(value_array)); + } break; + case Type::kStringType: + val = Value(std::string(o.GetString())); + break; + case Type::kFalseType: + case Type::kTrueType: + val = Value(o.GetBool()); + break; + case Type::kNumberType: + if (!o.IsDouble()) { + int i = 0; + GetInt(o, i); + val = Value(i); + } else { + double d = 0.0; + GetDouble(o, d); + val = Value(d); + } + break; + case Type::kNullType: + break; + // all types are covered, so no `case default` + } +#else + switch (o.type()) { + case json::value_t::object: { + Value::Object value_object; + for (auto it = o.begin(); it != o.end(); it++) { + Value entry; + ParseJsonAsValue(&entry, it.value()); + if (entry.Type() != NULL_TYPE) + value_object.emplace(it.key(), std::move(entry)); + } + if (value_object.size() > 0) val = Value(std::move(value_object)); + } break; + case json::value_t::array: { + Value::Array value_array; + value_array.reserve(o.size()); + for (auto it = o.begin(); it != o.end(); it++) { + Value entry; + ParseJsonAsValue(&entry, it.value()); + if (entry.Type() != NULL_TYPE) + value_array.emplace_back(std::move(entry)); + } + if (value_array.size() > 0) val = Value(std::move(value_array)); + } break; + case json::value_t::string: + val = Value(o.get()); + break; + case json::value_t::boolean: + val = Value(o.get()); + break; + case json::value_t::number_integer: + case json::value_t::number_unsigned: + val = Value(static_cast(o.get())); + break; + case json::value_t::number_float: + val = Value(o.get()); + break; + case json::value_t::null: + case json::value_t::binary: + case json::value_t::discarded: + // default: + break; + } +#endif + if (ret) *ret = std::move(val); + + return val.Type() != NULL_TYPE; +} + +static bool ParseExtrasProperty(Value *ret, const json &o) { + json_const_iterator it; + if (!FindMember(o, "extras", it)) { + return false; + } + + return ParseJsonAsValue(ret, GetValue(it)); +} + +static bool ParseBooleanProperty(bool *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + auto &value = GetValue(it); + + bool isBoolean; + bool boolValue = false; +#ifdef TINYGLTF_USE_RAPIDJSON + isBoolean = value.IsBool(); + if (isBoolean) { + boolValue = value.GetBool(); + } +#else + isBoolean = value.is_boolean(); + if (isBoolean) { + boolValue = value.get(); + } +#endif + if (!isBoolean) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a bool type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = boolValue; + } + + return true; +} + +static bool ParseIntegerProperty(int *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + int intValue; + bool isInt = GetInt(GetValue(it), intValue); + if (!isInt) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an integer type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = intValue; + } + + return true; +} + +static bool ParseUnsignedProperty(size_t *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + auto &value = GetValue(it); + + size_t uValue = 0; + bool isUValue; +#ifdef TINYGLTF_USE_RAPIDJSON + isUValue = false; + if (value.IsUint()) { + uValue = value.GetUint(); + isUValue = true; + } else if (value.IsUint64()) { + uValue = value.GetUint64(); + isUValue = true; + } +#else + isUValue = value.is_number_unsigned(); + if (isUValue) { + uValue = value.get(); + } +#endif + if (!isUValue) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a positive integer.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = uValue; + } + + return true; +} + +static bool ParseNumberProperty(double *ret, std::string *err, const json &o, + const std::string &property, + const bool required, + const std::string &parent_node = "") { + json_const_iterator it; + + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + double numberValue; + bool isNumber = GetNumber(GetValue(it), numberValue); + + if (!isNumber) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a number type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = numberValue; + } + + return true; +} + +static bool ParseNumberArrayProperty(std::vector *ret, std::string *err, + const json &o, const std::string &property, + bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + if (!IsArray(GetValue(it))) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an array"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + ret->clear(); + auto end = ArrayEnd(GetValue(it)); + for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { + double numberValue; + const bool isNumber = GetNumber(*i, numberValue); + if (!isNumber) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a number.\n"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + ret->push_back(numberValue); + } + + return true; +} + +static bool ParseIntegerArrayProperty(std::vector *ret, std::string *err, + const json &o, + const std::string &property, + bool required, + const std::string &parent_node = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + if (!IsArray(GetValue(it))) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an array"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + + ret->clear(); + auto end = ArrayEnd(GetValue(it)); + for (auto i = ArrayBegin(GetValue(it)); i != end; ++i) { + int numberValue; + bool isNumber = GetInt(*i, numberValue); + if (!isNumber) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an integer type.\n"; + if (!parent_node.empty()) { + (*err) += " in " + parent_node; + } + (*err) += ".\n"; + } + } + return false; + } + ret->push_back(numberValue); + } + + return true; +} + +static bool ParseStringProperty( + std::string *ret, std::string *err, const json &o, + const std::string &property, bool required, + const std::string &parent_node = std::string()) { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing"; + if (parent_node.empty()) { + (*err) += ".\n"; + } else { + (*err) += " in `" + parent_node + "'.\n"; + } + } + } + return false; + } + + std::string strValue; + if (!GetString(GetValue(it), strValue)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a string type.\n"; + } + } + return false; + } + + if (ret) { + (*ret) = std::move(strValue); + } + + return true; +} + +static bool ParseStringIntegerProperty(std::map *ret, + std::string *err, const json &o, + const std::string &property, + bool required, + const std::string &parent = "") { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + if (!parent.empty()) { + (*err) += + "'" + property + "' property is missing in " + parent + ".\n"; + } else { + (*err) += "'" + property + "' property is missing.\n"; + } + } + } + return false; + } + + const json &dict = GetValue(it); + + // Make sure we are dealing with an object / dictionary. + if (!IsObject(dict)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not an object.\n"; + } + } + return false; + } + + ret->clear(); + + json_const_iterator dictIt(ObjectBegin(dict)); + json_const_iterator dictItEnd(ObjectEnd(dict)); + + for (; dictIt != dictItEnd; ++dictIt) { + int intVal; + if (!GetInt(GetValue(dictIt), intVal)) { + if (required) { + if (err) { + (*err) += "'" + property + "' value is not an integer type.\n"; + } + } + return false; + } + + // Insert into the list. + (*ret)[GetKey(dictIt)] = intVal; + } + return true; +} + +static bool ParseJSONProperty(std::map *ret, + std::string *err, const json &o, + const std::string &property, bool required) { + json_const_iterator it; + if (!FindMember(o, property.c_str(), it)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is missing. \n'"; + } + } + return false; + } + + const json &obj = GetValue(it); + + if (!IsObject(obj)) { + if (required) { + if (err) { + (*err) += "'" + property + "' property is not a JSON object.\n"; + } + } + return false; + } + + ret->clear(); + + json_const_iterator it2(ObjectBegin(obj)); + json_const_iterator itEnd(ObjectEnd(obj)); + for (; it2 != itEnd; ++it2) { + double numVal; + if (GetNumber(GetValue(it2), numVal)) + ret->emplace(std::string(GetKey(it2)), numVal); + } + + return true; +} + +static bool ParseParameterProperty(Parameter *param, std::string *err, + const json &o, const std::string &prop, + bool required) { + // A parameter value can either be a string or an array of either a boolean or + // a number. Booleans of any kind aren't supported here. Granted, it + // complicates the Parameter structure and breaks it semantically in the sense + // that the client probably works off the assumption that if the string is + // empty the vector is used, etc. Would a tagged union work? + if (ParseStringProperty(¶m->string_value, err, o, prop, false)) { + // Found string property. + return true; + } else if (ParseNumberArrayProperty(¶m->number_array, err, o, prop, + false)) { + // Found a number array. + return true; + } else if (ParseNumberProperty(¶m->number_value, err, o, prop, false)) { + return param->has_number_value = true; + } else if (ParseJSONProperty(¶m->json_double_value, err, o, prop, + false)) { + return true; + } else if (ParseBooleanProperty(¶m->bool_value, err, o, prop, false)) { + return true; + } else { + if (required) { + if (err) { + (*err) += "parameter must be a string or number / number array.\n"; + } + } + return false; + } +} + +static bool ParseExtensionsProperty(ExtensionMap *ret, std::string *err, + const json &o) { + (void)err; + + json_const_iterator it; + if (!FindMember(o, "extensions", it)) { + return false; + } + + auto &obj = GetValue(it); + if (!IsObject(obj)) { + return false; + } + ExtensionMap extensions; + json_const_iterator extIt = ObjectBegin(obj); // it.value().begin(); + json_const_iterator extEnd = ObjectEnd(obj); + for (; extIt != extEnd; ++extIt) { + auto &itObj = GetValue(extIt); + if (!IsObject(itObj)) continue; + std::string key(GetKey(extIt)); + if (!ParseJsonAsValue(&extensions[key], itObj)) { + if (!key.empty()) { + // create empty object so that an extension object is still of type + // object + extensions[key] = Value{Value::Object{}}; + } + } + } + if (ret) { + (*ret) = std::move(extensions); + } + return true; +} + +static bool ParseAsset(Asset *asset, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&asset->version, err, o, "version", true, "Asset"); + ParseStringProperty(&asset->generator, err, o, "generator", false, "Asset"); + ParseStringProperty(&asset->minVersion, err, o, "minVersion", false, "Asset"); + ParseStringProperty(&asset->copyright, err, o, "copyright", false, "Asset"); + + ParseExtensionsProperty(&asset->extensions, err, o); + + // Unity exporter version is added as extra here + ParseExtrasProperty(&(asset->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + asset->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + asset->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseImage(Image *image, const int image_idx, std::string *err, + std::string *warn, const json &o, + bool store_original_json_for_extras_and_extensions, + const std::string &basedir, FsCallbacks *fs, + LoadImageDataFunction *LoadImageData = nullptr, + void *load_image_user_data = nullptr) { + // A glTF image must either reference a bufferView or an image uri + + // schema says oneOf [`bufferView`, `uri`] + // TODO(syoyo): Check the type of each parameters. + json_const_iterator it; + bool hasBufferView = FindMember(o, "bufferView", it); + bool hasURI = FindMember(o, "uri", it); + + ParseStringProperty(&image->name, err, o, "name", false); + + if (hasBufferView && hasURI) { + // Should not both defined. + if (err) { + (*err) += + "Only one of `bufferView` or `uri` should be defined, but both are " + "defined for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + "\"\n"; + } + return false; + } + + if (!hasBufferView && !hasURI) { + if (err) { + (*err) += "Neither required `bufferView` nor `uri` defined for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + + ParseExtensionsProperty(&image->extensions, err, o); + ParseExtrasProperty(&image->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator eit; + if (FindMember(o, "extensions", eit)) { + image->extensions_json_string = JsonToString(GetValue(eit)); + } + } + { + json_const_iterator eit; + if (FindMember(o, "extras", eit)) { + image->extras_json_string = JsonToString(GetValue(eit)); + } + } + } + + if (hasBufferView) { + int bufferView = -1; + if (!ParseIntegerProperty(&bufferView, err, o, "bufferView", true)) { + if (err) { + (*err) += "Failed to parse `bufferView` for image[" + + std::to_string(image_idx) + "] name = \"" + image->name + + "\"\n"; + } + return false; + } + + std::string mime_type; + ParseStringProperty(&mime_type, err, o, "mimeType", false); + + int width = 0; + ParseIntegerProperty(&width, err, o, "width", false); + + int height = 0; + ParseIntegerProperty(&height, err, o, "height", false); + + // Just only save some information here. Loading actual image data from + // bufferView is done after this `ParseImage` function. + image->bufferView = bufferView; + image->mimeType = mime_type; + image->width = width; + image->height = height; + + return true; + } + + // Parse URI & Load image data. + + std::string uri; + std::string tmp_err; + if (!ParseStringProperty(&uri, &tmp_err, o, "uri", true)) { + if (err) { + (*err) += "Failed to parse `uri` for image[" + std::to_string(image_idx) + + "] name = \"" + image->name + "\".\n"; + } + return false; + } + + std::vector img; + + if (IsDataURI(uri)) { + if (!DecodeDataURI(&img, image->mimeType, uri, 0, false)) { + if (err) { + (*err) += "Failed to decode 'uri' for image[" + + std::to_string(image_idx) + "] name = [" + image->name + + "]\n"; + } + return false; + } + } else { + // Assume external file + // Keep texture path (for textures that cannot be decoded) + image->uri = uri; +#ifdef TINYGLTF_NO_EXTERNAL_IMAGE + return true; +#endif + std::string decoded_uri = dlib::urldecode(uri); + if (!LoadExternalFile(&img, err, warn, decoded_uri, basedir, + /* required */ false, /* required bytes */ 0, + /* checksize */ false, fs)) { + if (warn) { + (*warn) += "Failed to load external 'uri' for image[" + + std::to_string(image_idx) + "] name = [" + image->name + + "]\n"; + } + // If the image cannot be loaded, keep uri as image->uri. + return true; + } + + if (img.empty()) { + if (warn) { + (*warn) += "Image data is empty for image[" + + std::to_string(image_idx) + "] name = [" + image->name + + "] \n"; + } + return false; + } + } + + if (*LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } + return false; + } + return (*LoadImageData)(image, image_idx, err, warn, 0, 0, &img.at(0), + static_cast(img.size()), load_image_user_data); +} + +static bool ParseTexture(Texture *texture, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions, + const std::string &basedir) { + (void)basedir; + int sampler = -1; + int source = -1; + ParseIntegerProperty(&sampler, err, o, "sampler", false); + + ParseIntegerProperty(&source, err, o, "source", false); + + texture->sampler = sampler; + texture->source = source; + + ParseExtensionsProperty(&texture->extensions, err, o); + ParseExtrasProperty(&texture->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texture->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texture->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + ParseStringProperty(&texture->name, err, o, "name", false); + + return true; +} + +static bool ParseTextureInfo( + TextureInfo *texinfo, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "TextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texinfo->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseNormalTextureInfo( + NormalTextureInfo *texinfo, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "NormalTextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + ParseNumberProperty(&texinfo->scale, err, o, "scale", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texinfo->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseOcclusionTextureInfo( + OcclusionTextureInfo *texinfo, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (texinfo == nullptr) { + return false; + } + + if (!ParseIntegerProperty(&texinfo->index, err, o, "index", + /* required */ true, "NormalTextureInfo")) { + return false; + } + + ParseIntegerProperty(&texinfo->texCoord, err, o, "texCoord", false); + ParseNumberProperty(&texinfo->strength, err, o, "strength", false); + + ParseExtensionsProperty(&texinfo->extensions, err, o); + ParseExtrasProperty(&texinfo->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + texinfo->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + texinfo->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseBuffer(Buffer *buffer, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions, + FsCallbacks *fs, const std::string &basedir, + bool is_binary = false, + const unsigned char *bin_data = nullptr, + size_t bin_size = 0) { + size_t byteLength; + if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, + "Buffer")) { + return false; + } + + // In glTF 2.0, uri is not mandatory anymore + buffer->uri.clear(); + ParseStringProperty(&buffer->uri, err, o, "uri", false, "Buffer"); + + // having an empty uri for a non embedded image should not be valid + if (!is_binary && buffer->uri.empty()) { + if (err) { + (*err) += "'uri' is missing from non binary glTF file buffer.\n"; + } + } + + json_const_iterator type; + if (FindMember(o, "type", type)) { + std::string typeStr; + if (GetString(GetValue(type), typeStr)) { + if (typeStr.compare("arraybuffer") == 0) { + // buffer.type = "arraybuffer"; + } + } + } + + if (is_binary) { + // Still binary glTF accepts external dataURI. + if (!buffer->uri.empty()) { + // First try embedded data URI. + if (IsDataURI(buffer->uri)) { + std::string mime_type; + if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, + true)) { + if (err) { + (*err) += + "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; + } + return false; + } + } else { + // External .bin file. + std::string decoded_uri = dlib::urldecode(buffer->uri); + if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, + decoded_uri, basedir, /* required */ true, + byteLength, /* checkSize */ true, fs)) { + return false; + } + } + } else { + // load data from (embedded) binary data + + if ((bin_size == 0) || (bin_data == nullptr)) { + if (err) { + (*err) += "Invalid binary data in `Buffer'.\n"; + } + return false; + } + + if (byteLength > bin_size) { + if (err) { + std::stringstream ss; + ss << "Invalid `byteLength'. Must be equal or less than binary size: " + "`byteLength' = " + << byteLength << ", binary size = " << bin_size << std::endl; + (*err) += ss.str(); + } + return false; + } + + // Read buffer data + buffer->data.resize(static_cast(byteLength)); + memcpy(&(buffer->data.at(0)), bin_data, static_cast(byteLength)); + } + + } else { + if (IsDataURI(buffer->uri)) { + std::string mime_type; + if (!DecodeDataURI(&buffer->data, mime_type, buffer->uri, byteLength, + true)) { + if (err) { + (*err) += "Failed to decode 'uri' : " + buffer->uri + " in Buffer\n"; + } + return false; + } + } else { + // Assume external .bin file. + std::string decoded_uri = dlib::urldecode(buffer->uri); + if (!LoadExternalFile(&buffer->data, err, /* warn */ nullptr, decoded_uri, + basedir, /* required */ true, byteLength, + /* checkSize */ true, fs)) { + return false; + } + } + } + + ParseStringProperty(&buffer->name, err, o, "name", false); + + ParseExtensionsProperty(&buffer->extensions, err, o); + ParseExtrasProperty(&buffer->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + buffer->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + buffer->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseBufferView( + BufferView *bufferView, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + int buffer = -1; + if (!ParseIntegerProperty(&buffer, err, o, "buffer", true, "BufferView")) { + return false; + } + + size_t byteOffset = 0; + ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false); + + size_t byteLength = 1; + if (!ParseUnsignedProperty(&byteLength, err, o, "byteLength", true, + "BufferView")) { + return false; + } + + size_t byteStride = 0; + if (!ParseUnsignedProperty(&byteStride, err, o, "byteStride", false)) { + // Spec says: When byteStride of referenced bufferView is not defined, it + // means that accessor elements are tightly packed, i.e., effective stride + // equals the size of the element. + // We cannot determine the actual byteStride until Accessor are parsed, thus + // set 0(= tightly packed) here(as done in OpenGL's VertexAttribPoiner) + byteStride = 0; + } + + if ((byteStride > 252) || ((byteStride % 4) != 0)) { + if (err) { + std::stringstream ss; + ss << "Invalid `byteStride' value. `byteStride' must be the multiple of " + "4 : " + << byteStride << std::endl; + + (*err) += ss.str(); + } + return false; + } + + int target = 0; + ParseIntegerProperty(&target, err, o, "target", false); + if ((target == TINYGLTF_TARGET_ARRAY_BUFFER) || + (target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER)) { + // OK + } else { + target = 0; + } + bufferView->target = target; + + ParseStringProperty(&bufferView->name, err, o, "name", false); + + ParseExtensionsProperty(&bufferView->extensions, err, o); + ParseExtrasProperty(&bufferView->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + bufferView->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + bufferView->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + bufferView->buffer = buffer; + bufferView->byteOffset = byteOffset; + bufferView->byteLength = byteLength; + bufferView->byteStride = byteStride; + return true; +} + +static bool ParseSparseAccessor(Accessor *accessor, std::string *err, + const json &o) { + accessor->sparse.isSparse = true; + + int count = 0; + ParseIntegerProperty(&count, err, o, "count", true); + + json_const_iterator indices_iterator; + json_const_iterator values_iterator; + if (!FindMember(o, "indices", indices_iterator)) { + (*err) = "the sparse object of this accessor doesn't have indices"; + return false; + } + + if (!FindMember(o, "values", values_iterator)) { + (*err) = "the sparse object ob ths accessor doesn't have values"; + return false; + } + + const json &indices_obj = GetValue(indices_iterator); + const json &values_obj = GetValue(values_iterator); + + int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; + ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView", + true); + ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", + true); + ParseIntegerProperty(&component_type, err, indices_obj, "componentType", + true); + + int values_buffer_view = 0, values_byte_offset = 0; + ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", + true); + ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", + true); + + accessor->sparse.count = count; + accessor->sparse.indices.bufferView = indices_buffer_view; + accessor->sparse.indices.byteOffset = indices_byte_offset; + accessor->sparse.indices.componentType = component_type; + accessor->sparse.values.bufferView = values_buffer_view; + accessor->sparse.values.byteOffset = values_byte_offset; + + // todo check theses values + + return true; +} + +static bool ParseAccessor(Accessor *accessor, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + int bufferView = -1; + ParseIntegerProperty(&bufferView, err, o, "bufferView", false, "Accessor"); + + size_t byteOffset = 0; + ParseUnsignedProperty(&byteOffset, err, o, "byteOffset", false, "Accessor"); + + bool normalized = false; + ParseBooleanProperty(&normalized, err, o, "normalized", false, "Accessor"); + + size_t componentType = 0; + if (!ParseUnsignedProperty(&componentType, err, o, "componentType", true, + "Accessor")) { + return false; + } + + size_t count = 0; + if (!ParseUnsignedProperty(&count, err, o, "count", true, "Accessor")) { + return false; + } + + std::string type; + if (!ParseStringProperty(&type, err, o, "type", true, "Accessor")) { + return false; + } + + if (type.compare("SCALAR") == 0) { + accessor->type = TINYGLTF_TYPE_SCALAR; + } else if (type.compare("VEC2") == 0) { + accessor->type = TINYGLTF_TYPE_VEC2; + } else if (type.compare("VEC3") == 0) { + accessor->type = TINYGLTF_TYPE_VEC3; + } else if (type.compare("VEC4") == 0) { + accessor->type = TINYGLTF_TYPE_VEC4; + } else if (type.compare("MAT2") == 0) { + accessor->type = TINYGLTF_TYPE_MAT2; + } else if (type.compare("MAT3") == 0) { + accessor->type = TINYGLTF_TYPE_MAT3; + } else if (type.compare("MAT4") == 0) { + accessor->type = TINYGLTF_TYPE_MAT4; + } else { + std::stringstream ss; + ss << "Unsupported `type` for accessor object. Got \"" << type << "\"\n"; + if (err) { + (*err) += ss.str(); + } + return false; + } + + ParseStringProperty(&accessor->name, err, o, "name", false); + + accessor->minValues.clear(); + accessor->maxValues.clear(); + ParseNumberArrayProperty(&accessor->minValues, err, o, "min", false, + "Accessor"); + + ParseNumberArrayProperty(&accessor->maxValues, err, o, "max", false, + "Accessor"); + + accessor->count = count; + accessor->bufferView = bufferView; + accessor->byteOffset = byteOffset; + accessor->normalized = normalized; + { + if (componentType >= TINYGLTF_COMPONENT_TYPE_BYTE && + componentType <= TINYGLTF_COMPONENT_TYPE_DOUBLE) { + // OK + accessor->componentType = int(componentType); + } else { + std::stringstream ss; + ss << "Invalid `componentType` in accessor. Got " << componentType + << "\n"; + if (err) { + (*err) += ss.str(); + } + return false; + } + } + + ParseExtensionsProperty(&(accessor->extensions), err, o); + ParseExtrasProperty(&(accessor->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + accessor->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + accessor->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + // check if accessor has a "sparse" object: + json_const_iterator iterator; + if (FindMember(o, "sparse", iterator)) { + // here this accessor has a "sparse" subobject + return ParseSparseAccessor(accessor, err, GetValue(iterator)); + } + + return true; +} + +#ifdef TINYGLTF_ENABLE_DRACO + +static void DecodeIndexBuffer(draco::Mesh *mesh, size_t componentSize, + std::vector &outBuffer) { + if (componentSize == 4) { + assert(sizeof(mesh->face(draco::FaceIndex(0))[0]) == componentSize); + memcpy(outBuffer.data(), &mesh->face(draco::FaceIndex(0))[0], + outBuffer.size()); + } else { + size_t faceStride = componentSize * 3; + for (draco::FaceIndex f(0); f < mesh->num_faces(); ++f) { + const draco::Mesh::Face &face = mesh->face(f); + if (componentSize == 2) { + uint16_t indices[3] = {(uint16_t)face[0].value(), + (uint16_t)face[1].value(), + (uint16_t)face[2].value()}; + memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], + faceStride); + } else { + uint8_t indices[3] = {(uint8_t)face[0].value(), + (uint8_t)face[1].value(), + (uint8_t)face[2].value()}; + memcpy(outBuffer.data() + f.value() * faceStride, &indices[0], + faceStride); + } + } + } +} + +template +static bool GetAttributeForAllPoints(draco::Mesh *mesh, + const draco::PointAttribute *pAttribute, + std::vector &outBuffer) { + size_t byteOffset = 0; + T values[4] = {0, 0, 0, 0}; + for (draco::PointIndex i(0); i < mesh->num_points(); ++i) { + const draco::AttributeValueIndex val_index = pAttribute->mapped_index(i); + if (!pAttribute->ConvertValue(val_index, pAttribute->num_components(), + values)) + return false; + + memcpy(outBuffer.data() + byteOffset, &values[0], + sizeof(T) * pAttribute->num_components()); + byteOffset += sizeof(T) * pAttribute->num_components(); + } + + return true; +} + +static bool GetAttributeForAllPoints(uint32_t componentType, draco::Mesh *mesh, + const draco::PointAttribute *pAttribute, + std::vector &outBuffer) { + bool decodeResult = false; + switch (componentType) { + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_BYTE: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_SHORT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_INT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_FLOAT: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + case TINYGLTF_COMPONENT_TYPE_DOUBLE: + decodeResult = + GetAttributeForAllPoints(mesh, pAttribute, outBuffer); + break; + default: + return false; + } + + return decodeResult; +} + +static bool ParseDracoExtension(Primitive *primitive, Model *model, + std::string *err, + const Value &dracoExtensionValue) { + (void)err; + auto bufferViewValue = dracoExtensionValue.Get("bufferView"); + if (!bufferViewValue.IsInt()) return false; + auto attributesValue = dracoExtensionValue.Get("attributes"); + if (!attributesValue.IsObject()) return false; + + auto attributesObject = attributesValue.Get(); + int bufferView = bufferViewValue.Get(); + + BufferView &view = model->bufferViews[bufferView]; + Buffer &buffer = model->buffers[view.buffer]; + // BufferView has already been decoded + if (view.dracoDecoded) return true; + view.dracoDecoded = true; + + const char *bufferViewData = + reinterpret_cast(buffer.data.data() + view.byteOffset); + size_t bufferViewSize = view.byteLength; + + // decode draco + draco::DecoderBuffer decoderBuffer; + decoderBuffer.Init(bufferViewData, bufferViewSize); + draco::Decoder decoder; + auto decodeResult = decoder.DecodeMeshFromBuffer(&decoderBuffer); + if (!decodeResult.ok()) { + return false; + } + const std::unique_ptr &mesh = decodeResult.value(); + + // create new bufferView for indices + if (primitive->indices >= 0) { + int32_t componentSize = GetComponentSizeInBytes( + model->accessors[primitive->indices].componentType); + Buffer decodedIndexBuffer; + decodedIndexBuffer.data.resize(mesh->num_faces() * 3 * componentSize); + + DecodeIndexBuffer(mesh.get(), componentSize, decodedIndexBuffer.data); + + model->buffers.emplace_back(std::move(decodedIndexBuffer)); + + BufferView decodedIndexBufferView; + decodedIndexBufferView.buffer = int(model->buffers.size() - 1); + decodedIndexBufferView.byteLength = + int(mesh->num_faces() * 3 * componentSize); + decodedIndexBufferView.byteOffset = 0; + decodedIndexBufferView.byteStride = 0; + decodedIndexBufferView.target = TINYGLTF_TARGET_ARRAY_BUFFER; + model->bufferViews.emplace_back(std::move(decodedIndexBufferView)); + + model->accessors[primitive->indices].bufferView = + int(model->bufferViews.size() - 1); + model->accessors[primitive->indices].count = int(mesh->num_faces() * 3); + } + + for (const auto &attribute : attributesObject) { + if (!attribute.second.IsInt()) return false; + auto primitiveAttribute = primitive->attributes.find(attribute.first); + if (primitiveAttribute == primitive->attributes.end()) return false; + + int dracoAttributeIndex = attribute.second.Get(); + const auto pAttribute = mesh->GetAttributeByUniqueId(dracoAttributeIndex); + const auto componentType = + model->accessors[primitiveAttribute->second].componentType; + + // Create a new buffer for this decoded buffer + Buffer decodedBuffer; + size_t bufferSize = mesh->num_points() * pAttribute->num_components() * + GetComponentSizeInBytes(componentType); + decodedBuffer.data.resize(bufferSize); + + if (!GetAttributeForAllPoints(componentType, mesh.get(), pAttribute, + decodedBuffer.data)) + return false; + + model->buffers.emplace_back(std::move(decodedBuffer)); + + BufferView decodedBufferView; + decodedBufferView.buffer = int(model->buffers.size() - 1); + decodedBufferView.byteLength = bufferSize; + decodedBufferView.byteOffset = pAttribute->byte_offset(); + decodedBufferView.byteStride = pAttribute->byte_stride(); + decodedBufferView.target = primitive->indices >= 0 + ? TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER + : TINYGLTF_TARGET_ARRAY_BUFFER; + model->bufferViews.emplace_back(std::move(decodedBufferView)); + + model->accessors[primitiveAttribute->second].bufferView = + int(model->bufferViews.size() - 1); + model->accessors[primitiveAttribute->second].count = + int(mesh->num_points()); + } + + return true; +} +#endif + +static bool ParsePrimitive(Primitive *primitive, Model *model, std::string *err, + const json &o, + bool store_original_json_for_extras_and_extensions) { + int material = -1; + ParseIntegerProperty(&material, err, o, "material", false); + primitive->material = material; + + int mode = TINYGLTF_MODE_TRIANGLES; + ParseIntegerProperty(&mode, err, o, "mode", false); + primitive->mode = mode; // Why only triangled were supported ? + + int indices = -1; + ParseIntegerProperty(&indices, err, o, "indices", false); + primitive->indices = indices; + if (!ParseStringIntegerProperty(&primitive->attributes, err, o, "attributes", + true, "Primitive")) { + return false; + } + + // Look for morph targets + json_const_iterator targetsObject; + if (FindMember(o, "targets", targetsObject) && + IsArray(GetValue(targetsObject))) { + auto targetsObjectEnd = ArrayEnd(GetValue(targetsObject)); + for (json_const_array_iterator i = ArrayBegin(GetValue(targetsObject)); + i != targetsObjectEnd; ++i) { + std::map targetAttribues; + + const json &dict = *i; + if (IsObject(dict)) { + json_const_iterator dictIt(ObjectBegin(dict)); + json_const_iterator dictItEnd(ObjectEnd(dict)); + + for (; dictIt != dictItEnd; ++dictIt) { + int iVal; + if (GetInt(GetValue(dictIt), iVal)) + targetAttribues[GetKey(dictIt)] = iVal; + } + primitive->targets.emplace_back(std::move(targetAttribues)); + } + } + } + + ParseExtrasProperty(&(primitive->extras), o); + ParseExtensionsProperty(&primitive->extensions, err, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + primitive->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + primitive->extras_json_string = JsonToString(GetValue(it)); + } + } + } + +#ifdef TINYGLTF_ENABLE_DRACO + auto dracoExtension = + primitive->extensions.find("KHR_draco_mesh_compression"); + if (dracoExtension != primitive->extensions.end()) { + ParseDracoExtension(primitive, model, err, dracoExtension->second); + } +#else + (void)model; +#endif + + return true; +} + +static bool ParseMesh(Mesh *mesh, Model *model, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&mesh->name, err, o, "name", false); + + mesh->primitives.clear(); + json_const_iterator primObject; + if (FindMember(o, "primitives", primObject) && + IsArray(GetValue(primObject))) { + json_const_array_iterator primEnd = ArrayEnd(GetValue(primObject)); + for (json_const_array_iterator i = ArrayBegin(GetValue(primObject)); + i != primEnd; ++i) { + Primitive primitive; + if (ParsePrimitive(&primitive, model, err, *i, + store_original_json_for_extras_and_extensions)) { + // Only add the primitive if the parsing succeeds. + mesh->primitives.emplace_back(std::move(primitive)); + } + } + } + + // Should probably check if has targets and if dimensions fit + ParseNumberArrayProperty(&mesh->weights, err, o, "weights", false); + + ParseExtensionsProperty(&mesh->extensions, err, o); + ParseExtrasProperty(&(mesh->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + mesh->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + mesh->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseNode(Node *node, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&node->name, err, o, "name", false); + + int skin = -1; + ParseIntegerProperty(&skin, err, o, "skin", false); + node->skin = skin; + + // Matrix and T/R/S are exclusive + if (!ParseNumberArrayProperty(&node->matrix, err, o, "matrix", false)) { + ParseNumberArrayProperty(&node->rotation, err, o, "rotation", false); + ParseNumberArrayProperty(&node->scale, err, o, "scale", false); + ParseNumberArrayProperty(&node->translation, err, o, "translation", false); + } + + int camera = -1; + ParseIntegerProperty(&camera, err, o, "camera", false); + node->camera = camera; + + int mesh = -1; + ParseIntegerProperty(&mesh, err, o, "mesh", false); + node->mesh = mesh; + + node->children.clear(); + ParseIntegerArrayProperty(&node->children, err, o, "children", false); + + ParseNumberArrayProperty(&node->weights, err, o, "weights", false); + + ParseExtensionsProperty(&node->extensions, err, o); + ParseExtrasProperty(&(node->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + node->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + node->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParsePbrMetallicRoughness( + PbrMetallicRoughness *pbr, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (pbr == nullptr) { + return false; + } + + std::vector baseColorFactor; + if (ParseNumberArrayProperty(&baseColorFactor, err, o, "baseColorFactor", + /* required */ false)) { + if (baseColorFactor.size() != 4) { + if (err) { + (*err) += + "Array length of `baseColorFactor` parameter in " + "pbrMetallicRoughness must be 4, but got " + + std::to_string(baseColorFactor.size()) + "\n"; + } + return false; + } + pbr->baseColorFactor = baseColorFactor; + } + + { + json_const_iterator it; + if (FindMember(o, "baseColorTexture", it)) { + ParseTextureInfo(&pbr->baseColorTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "metallicRoughnessTexture", it)) { + ParseTextureInfo(&pbr->metallicRoughnessTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + ParseNumberProperty(&pbr->metallicFactor, err, o, "metallicFactor", false); + ParseNumberProperty(&pbr->roughnessFactor, err, o, "roughnessFactor", false); + + ParseExtensionsProperty(&pbr->extensions, err, o); + ParseExtrasProperty(&pbr->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + pbr->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + pbr->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseMaterial(Material *material, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&material->name, err, o, "name", /* required */ false); + + if (ParseNumberArrayProperty(&material->emissiveFactor, err, o, + "emissiveFactor", + /* required */ false)) { + if (material->emissiveFactor.size() != 3) { + if (err) { + (*err) += + "Array length of `emissiveFactor` parameter in " + "material must be 3, but got " + + std::to_string(material->emissiveFactor.size()) + "\n"; + } + return false; + } + } else { + // fill with default values + material->emissiveFactor = {0.0, 0.0, 0.0}; + } + + ParseStringProperty(&material->alphaMode, err, o, "alphaMode", + /* required */ false); + ParseNumberProperty(&material->alphaCutoff, err, o, "alphaCutoff", + /* required */ false); + ParseBooleanProperty(&material->doubleSided, err, o, "doubleSided", + /* required */ false); + + { + json_const_iterator it; + if (FindMember(o, "pbrMetallicRoughness", it)) { + ParsePbrMetallicRoughness(&material->pbrMetallicRoughness, err, + GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "normalTexture", it)) { + ParseNormalTextureInfo(&material->normalTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "occlusionTexture", it)) { + ParseOcclusionTextureInfo(&material->occlusionTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + { + json_const_iterator it; + if (FindMember(o, "emissiveTexture", it)) { + ParseTextureInfo(&material->emissiveTexture, err, GetValue(it), + store_original_json_for_extras_and_extensions); + } + } + + // Old code path. For backward compatibility, we still store material values + // as Parameter. This will create duplicated information for + // example(pbrMetallicRoughness), but should be neglible in terms of memory + // consumption. + // TODO(syoyo): Remove in the next major release. + material->values.clear(); + material->additionalValues.clear(); + + json_const_iterator it(ObjectBegin(o)); + json_const_iterator itEnd(ObjectEnd(o)); + + for (; it != itEnd; ++it) { + std::string key(GetKey(it)); + if (key == "pbrMetallicRoughness") { + if (IsObject(GetValue(it))) { + const json &values_object = GetValue(it); + + json_const_iterator itVal(ObjectBegin(values_object)); + json_const_iterator itValEnd(ObjectEnd(values_object)); + + for (; itVal != itValEnd; ++itVal) { + Parameter param; + if (ParseParameterProperty(¶m, err, values_object, GetKey(itVal), + false)) { + material->values.emplace(GetKey(itVal), std::move(param)); + } + } + } + } else if (key == "extensions" || key == "extras") { + // done later, skip, otherwise poorly parsed contents will be saved in the + // parametermap and serialized again later + } else { + Parameter param; + if (ParseParameterProperty(¶m, err, o, key, false)) { + // names of materials have already been parsed. Putting it in this map + // doesn't correctly reflext the glTF specification + if (key != "name") + material->additionalValues.emplace(std::move(key), std::move(param)); + } + } + } + + material->extensions.clear(); + ParseExtensionsProperty(&material->extensions, err, o); + ParseExtrasProperty(&(material->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator eit; + if (FindMember(o, "extensions", eit)) { + material->extensions_json_string = JsonToString(GetValue(eit)); + } + } + { + json_const_iterator eit; + if (FindMember(o, "extras", eit)) { + material->extras_json_string = JsonToString(GetValue(eit)); + } + } + } + + return true; +} + +static bool ParseAnimationChannel( + AnimationChannel *channel, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + int samplerIndex = -1; + int targetIndex = -1; + if (!ParseIntegerProperty(&samplerIndex, err, o, "sampler", true, + "AnimationChannel")) { + if (err) { + (*err) += "`sampler` field is missing in animation channels\n"; + } + return false; + } + + json_const_iterator targetIt; + if (FindMember(o, "target", targetIt) && IsObject(GetValue(targetIt))) { + const json &target_object = GetValue(targetIt); + + if (!ParseIntegerProperty(&targetIndex, err, target_object, "node", true)) { + if (err) { + (*err) += "`node` field is missing in animation.channels.target\n"; + } + return false; + } + + if (!ParseStringProperty(&channel->target_path, err, target_object, "path", + true)) { + if (err) { + (*err) += "`path` field is missing in animation.channels.target\n"; + } + return false; + } + ParseExtensionsProperty(&channel->target_extensions, err, target_object); + if (store_original_json_for_extras_and_extensions) { + json_const_iterator it; + if (FindMember(target_object, "extensions", it)) { + channel->target_extensions_json_string = JsonToString(GetValue(it)); + } + } + } + + channel->sampler = samplerIndex; + channel->target_node = targetIndex; + + ParseExtensionsProperty(&channel->extensions, err, o); + ParseExtrasProperty(&(channel->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + channel->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + channel->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseAnimation(Animation *animation, std::string *err, + const json &o, + bool store_original_json_for_extras_and_extensions) { + { + json_const_iterator channelsIt; + if (FindMember(o, "channels", channelsIt) && + IsArray(GetValue(channelsIt))) { + json_const_array_iterator channelEnd = ArrayEnd(GetValue(channelsIt)); + for (json_const_array_iterator i = ArrayBegin(GetValue(channelsIt)); + i != channelEnd; ++i) { + AnimationChannel channel; + if (ParseAnimationChannel( + &channel, err, *i, + store_original_json_for_extras_and_extensions)) { + // Only add the channel if the parsing succeeds. + animation->channels.emplace_back(std::move(channel)); + } + } + } + } + + { + json_const_iterator samplerIt; + if (FindMember(o, "samplers", samplerIt) && IsArray(GetValue(samplerIt))) { + const json &sampler_array = GetValue(samplerIt); + + json_const_array_iterator it = ArrayBegin(sampler_array); + json_const_array_iterator itEnd = ArrayEnd(sampler_array); + + for (; it != itEnd; ++it) { + const json &s = *it; + + AnimationSampler sampler; + int inputIndex = -1; + int outputIndex = -1; + if (!ParseIntegerProperty(&inputIndex, err, s, "input", true)) { + if (err) { + (*err) += "`input` field is missing in animation.sampler\n"; + } + return false; + } + ParseStringProperty(&sampler.interpolation, err, s, "interpolation", + false); + if (!ParseIntegerProperty(&outputIndex, err, s, "output", true)) { + if (err) { + (*err) += "`output` field is missing in animation.sampler\n"; + } + return false; + } + sampler.input = inputIndex; + sampler.output = outputIndex; + ParseExtensionsProperty(&(sampler.extensions), err, o); + ParseExtrasProperty(&(sampler.extras), s); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator eit; + if (FindMember(o, "extensions", eit)) { + sampler.extensions_json_string = JsonToString(GetValue(eit)); + } + } + { + json_const_iterator eit; + if (FindMember(o, "extras", eit)) { + sampler.extras_json_string = JsonToString(GetValue(eit)); + } + } + } + + animation->samplers.emplace_back(std::move(sampler)); + } + } + } + + ParseStringProperty(&animation->name, err, o, "name", false); + + ParseExtensionsProperty(&animation->extensions, err, o); + ParseExtrasProperty(&(animation->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + animation->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + animation->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&sampler->name, err, o, "name", false); + + int minFilter = -1; + int magFilter = -1; + int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT; + int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; + //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; + ParseIntegerProperty(&minFilter, err, o, "minFilter", false); + ParseIntegerProperty(&magFilter, err, o, "magFilter", false); + ParseIntegerProperty(&wrapS, err, o, "wrapS", false); + ParseIntegerProperty(&wrapT, err, o, "wrapT", false); + //ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension + + // TODO(syoyo): Check the value is alloed one. + // (e.g. we allow 9728(NEAREST), but don't allow 9727) + + sampler->minFilter = minFilter; + sampler->magFilter = magFilter; + sampler->wrapS = wrapS; + sampler->wrapT = wrapT; + //sampler->wrapR = wrapR; + + ParseExtensionsProperty(&(sampler->extensions), err, o); + ParseExtrasProperty(&(sampler->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + sampler->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + sampler->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseSkin(Skin *skin, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseStringProperty(&skin->name, err, o, "name", false, "Skin"); + + std::vector joints; + if (!ParseIntegerArrayProperty(&joints, err, o, "joints", false, "Skin")) { + return false; + } + skin->joints = std::move(joints); + + int skeleton = -1; + ParseIntegerProperty(&skeleton, err, o, "skeleton", false, "Skin"); + skin->skeleton = skeleton; + + int invBind = -1; + ParseIntegerProperty(&invBind, err, o, "inverseBindMatrices", true, "Skin"); + skin->inverseBindMatrices = invBind; + + ParseExtensionsProperty(&(skin->extensions), err, o); + ParseExtrasProperty(&(skin->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + skin->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + skin->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParsePerspectiveCamera( + PerspectiveCamera *camera, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + double yfov = 0.0; + if (!ParseNumberProperty(&yfov, err, o, "yfov", true, "OrthographicCamera")) { + return false; + } + + double znear = 0.0; + if (!ParseNumberProperty(&znear, err, o, "znear", true, + "PerspectiveCamera")) { + return false; + } + + double aspectRatio = 0.0; // = invalid + ParseNumberProperty(&aspectRatio, err, o, "aspectRatio", false, + "PerspectiveCamera"); + + double zfar = 0.0; // = invalid + ParseNumberProperty(&zfar, err, o, "zfar", false, "PerspectiveCamera"); + + camera->aspectRatio = aspectRatio; + camera->zfar = zfar; + camera->yfov = yfov; + camera->znear = znear; + + ParseExtensionsProperty(&camera->extensions, err, o); + ParseExtrasProperty(&(camera->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + camera->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + camera->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + // TODO(syoyo): Validate parameter values. + + return true; +} + +static bool ParseSpotLight(SpotLight *light, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + ParseNumberProperty(&light->innerConeAngle, err, o, "innerConeAngle", false); + ParseNumberProperty(&light->outerConeAngle, err, o, "outerConeAngle", false); + + ParseExtensionsProperty(&light->extensions, err, o); + ParseExtrasProperty(&light->extras, o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + light->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + light->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + // TODO(syoyo): Validate parameter values. + + return true; +} + +static bool ParseOrthographicCamera( + OrthographicCamera *camera, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + double xmag = 0.0; + if (!ParseNumberProperty(&xmag, err, o, "xmag", true, "OrthographicCamera")) { + return false; + } + + double ymag = 0.0; + if (!ParseNumberProperty(&ymag, err, o, "ymag", true, "OrthographicCamera")) { + return false; + } + + double zfar = 0.0; + if (!ParseNumberProperty(&zfar, err, o, "zfar", true, "OrthographicCamera")) { + return false; + } + + double znear = 0.0; + if (!ParseNumberProperty(&znear, err, o, "znear", true, + "OrthographicCamera")) { + return false; + } + + ParseExtensionsProperty(&camera->extensions, err, o); + ParseExtrasProperty(&(camera->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + camera->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + camera->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + camera->xmag = xmag; + camera->ymag = ymag; + camera->zfar = zfar; + camera->znear = znear; + + // TODO(syoyo): Validate parameter values. + + return true; +} + +static bool ParseCamera(Camera *camera, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&camera->type, err, o, "type", true, "Camera")) { + return false; + } + + if (camera->type.compare("orthographic") == 0) { + json_const_iterator orthoIt; + if (!FindMember(o, "orthographic", orthoIt)) { + if (err) { + std::stringstream ss; + ss << "Orhographic camera description not found." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const json &v = GetValue(orthoIt); + if (!IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"orthographic\" is not a JSON object." << std::endl; + (*err) += ss.str(); + } + return false; + } + + if (!ParseOrthographicCamera( + &camera->orthographic, err, v, + store_original_json_for_extras_and_extensions)) { + return false; + } + } else if (camera->type.compare("perspective") == 0) { + json_const_iterator perspIt; + if (!FindMember(o, "perspective", perspIt)) { + if (err) { + std::stringstream ss; + ss << "Perspective camera description not found." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const json &v = GetValue(perspIt); + if (!IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"perspective\" is not a JSON object." << std::endl; + (*err) += ss.str(); + } + return false; + } + + if (!ParsePerspectiveCamera( + &camera->perspective, err, v, + store_original_json_for_extras_and_extensions)) { + return false; + } + } else { + if (err) { + std::stringstream ss; + ss << "Invalid camera type: \"" << camera->type + << "\". Must be \"perspective\" or \"orthographic\"" << std::endl; + (*err) += ss.str(); + } + return false; + } + + ParseStringProperty(&camera->name, err, o, "name", false); + + ParseExtensionsProperty(&camera->extensions, err, o); + ParseExtrasProperty(&(camera->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + camera->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + camera->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +static bool ParseLight(Light *light, std::string *err, const json &o, + bool store_original_json_for_extras_and_extensions) { + if (!ParseStringProperty(&light->type, err, o, "type", true)) { + return false; + } + + if (light->type == "spot") { + json_const_iterator spotIt; + if (!FindMember(o, "spot", spotIt)) { + if (err) { + std::stringstream ss; + ss << "Spot light description not found." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const json &v = GetValue(spotIt); + if (!IsObject(v)) { + if (err) { + std::stringstream ss; + ss << "\"spot\" is not a JSON object." << std::endl; + (*err) += ss.str(); + } + return false; + } + + if (!ParseSpotLight(&light->spot, err, v, + store_original_json_for_extras_and_extensions)) { + return false; + } + } + + ParseStringProperty(&light->name, err, o, "name", false); + ParseNumberArrayProperty(&light->color, err, o, "color", false); + ParseNumberProperty(&light->range, err, o, "range", false); + ParseNumberProperty(&light->intensity, err, o, "intensity", false); + ParseExtensionsProperty(&light->extensions, err, o); + ParseExtrasProperty(&(light->extras), o); + + if (store_original_json_for_extras_and_extensions) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + light->extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + light->extras_json_string = JsonToString(GetValue(it)); + } + } + } + + return true; +} + +bool TinyGLTF::LoadFromString(Model *model, std::string *err, std::string *warn, + const char *json_str, + unsigned int json_str_length, + const std::string &base_dir, + unsigned int check_sections) { + if (json_str_length < 4) { + if (err) { + (*err) = "JSON string too short.\n"; + } + return false; + } + + JsonDocument v; + +#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || \ + defined(_CPPUNWIND)) && \ + !defined(TINYGLTF_NOEXCEPTION) + try { + JsonParse(v, json_str, json_str_length, true); + + } catch (const std::exception &e) { + if (err) { + (*err) = e.what(); + } + return false; + } +#else + { + JsonParse(v, json_str, json_str_length); + + if (!IsObject(v)) { + // Assume parsing was failed. + if (err) { + (*err) = "Failed to parse JSON object\n"; + } + return false; + } + } +#endif + + if (!IsObject(v)) { + // root is not an object. + if (err) { + (*err) = "Root element is not a JSON object\n"; + } + return false; + } + + { + bool version_found = false; + json_const_iterator it; + if (FindMember(v, "asset", it) && IsObject(GetValue(it))) { + auto &itObj = GetValue(it); + json_const_iterator version_it; + std::string versionStr; + if (FindMember(itObj, "version", version_it) && + GetString(GetValue(version_it), versionStr)) { + version_found = true; + } + } + if (version_found) { + // OK + } else if (check_sections & REQUIRE_VERSION) { + if (err) { + (*err) += "\"asset\" object not found in .gltf or not an object type\n"; + } + return false; + } + } + + // scene is not mandatory. + // FIXME Maybe a better way to handle it than removing the code + + auto IsArrayMemberPresent = [](const json &_v, const char *name) -> bool { + json_const_iterator it; + return FindMember(_v, name, it) && IsArray(GetValue(it)); + }; + + { + if ((check_sections & REQUIRE_SCENES) && + !IsArrayMemberPresent(v, "scenes")) { + if (err) { + (*err) += "\"scenes\" object not found in .gltf or not an array type\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_NODES) && !IsArrayMemberPresent(v, "nodes")) { + if (err) { + (*err) += "\"nodes\" object not found in .gltf\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_ACCESSORS) && + !IsArrayMemberPresent(v, "accessors")) { + if (err) { + (*err) += "\"accessors\" object not found in .gltf\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_BUFFERS) && + !IsArrayMemberPresent(v, "buffers")) { + if (err) { + (*err) += "\"buffers\" object not found in .gltf\n"; + } + return false; + } + } + + { + if ((check_sections & REQUIRE_BUFFER_VIEWS) && + !IsArrayMemberPresent(v, "bufferViews")) { + if (err) { + (*err) += "\"bufferViews\" object not found in .gltf\n"; + } + return false; + } + } + + model->buffers.clear(); + model->bufferViews.clear(); + model->accessors.clear(); + model->meshes.clear(); + model->cameras.clear(); + model->nodes.clear(); + model->extensionsUsed.clear(); + model->extensionsRequired.clear(); + model->extensions.clear(); + model->defaultScene = -1; + + // 1. Parse Asset + { + json_const_iterator it; + if (FindMember(v, "asset", it) && IsObject(GetValue(it))) { + const json &root = GetValue(it); + + ParseAsset(&model->asset, err, root, + store_original_json_for_extras_and_extensions_); + } + } + +#ifdef TINYGLTF_USE_CPP14 + auto ForEachInArray = [](const json &_v, const char *member, + const auto &cb) -> bool +#else + // The std::function<> implementation can be less efficient because it will + // allocate heap when the size of the captured lambda is above 16 bytes with + // clang and gcc, but it does not require C++14. + auto ForEachInArray = [](const json &_v, const char *member, + const std::function &cb) -> bool +#endif + { + json_const_iterator itm; + if (FindMember(_v, member, itm) && IsArray(GetValue(itm))) { + const json &root = GetValue(itm); + auto it = ArrayBegin(root); + auto end = ArrayEnd(root); + for (; it != end; ++it) { + if (!cb(*it)) return false; + } + } + return true; + }; + + // 2. Parse extensionUsed + { + ForEachInArray(v, "extensionsUsed", [&](const json &o) { + std::string str; + GetString(o, str); + model->extensionsUsed.emplace_back(std::move(str)); + return true; + }); + } + + { + ForEachInArray(v, "extensionsRequired", [&](const json &o) { + std::string str; + GetString(o, str); + model->extensionsRequired.emplace_back(std::move(str)); + return true; + }); + } + + // 3. Parse Buffer + { + bool success = ForEachInArray(v, "buffers", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`buffers' does not contain an JSON object."; + } + return false; + } + Buffer buffer; + if (!ParseBuffer(&buffer, err, o, + store_original_json_for_extras_and_extensions_, &fs, + base_dir, is_binary_, bin_data_, bin_size_)) { + return false; + } + + model->buffers.emplace_back(std::move(buffer)); + return true; + }); + + if (!success) { + return false; + } + } + // 4. Parse BufferView + { + bool success = ForEachInArray(v, "bufferViews", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`bufferViews' does not contain an JSON object."; + } + return false; + } + BufferView bufferView; + if (!ParseBufferView(&bufferView, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->bufferViews.emplace_back(std::move(bufferView)); + return true; + }); + + if (!success) { + return false; + } + } + + // 5. Parse Accessor + { + bool success = ForEachInArray(v, "accessors", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`accessors' does not contain an JSON object."; + } + return false; + } + Accessor accessor; + if (!ParseAccessor(&accessor, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->accessors.emplace_back(std::move(accessor)); + return true; + }); + + if (!success) { + return false; + } + } + + // 6. Parse Mesh + { + bool success = ForEachInArray(v, "meshes", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`meshes' does not contain an JSON object."; + } + return false; + } + Mesh mesh; + if (!ParseMesh(&mesh, model, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->meshes.emplace_back(std::move(mesh)); + return true; + }); + + if (!success) { + return false; + } + } + + // Assign missing bufferView target types + // - Look for missing Mesh indices + // - Look for missing Mesh attributes + for (auto &mesh : model->meshes) { + for (auto &primitive : mesh.primitives) { + if (primitive.indices > + -1) // has indices from parsing step, must be Element Array Buffer + { + if (size_t(primitive.indices) >= model->accessors.size()) { + if (err) { + (*err) += "primitive indices accessor out of bounds"; + } + return false; + } + + auto bufferView = + model->accessors[size_t(primitive.indices)].bufferView; + if (bufferView < 0 || size_t(bufferView) >= model->bufferViews.size()) { + if (err) { + (*err) += "accessor[" + std::to_string(primitive.indices) + + "] invalid bufferView"; + } + return false; + } + + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER; + // we could optionally check if acessors' bufferView type is Scalar, as + // it should be + } + + for (auto &attribute : primitive.attributes) { + model + ->bufferViews[size_t( + model->accessors[size_t(attribute.second)].bufferView)] + .target = TINYGLTF_TARGET_ARRAY_BUFFER; + } + + for (auto &target : primitive.targets) { + for (auto &attribute : target) { + auto bufferView = + model->accessors[size_t(attribute.second)].bufferView; + // bufferView could be null(-1) for sparse morph target + if (bufferView >= 0) { + model->bufferViews[size_t(bufferView)].target = + TINYGLTF_TARGET_ARRAY_BUFFER; + } + } + } + } + } + + // 7. Parse Node + { + bool success = ForEachInArray(v, "nodes", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`nodes' does not contain an JSON object."; + } + return false; + } + Node node; + if (!ParseNode(&node, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->nodes.emplace_back(std::move(node)); + return true; + }); + + if (!success) { + return false; + } + } + + // 8. Parse scenes. + { + bool success = ForEachInArray(v, "scenes", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`scenes' does not contain an JSON object."; + } + return false; + } + std::vector nodes; + ParseIntegerArrayProperty(&nodes, err, o, "nodes", false); + + Scene scene; + scene.nodes = std::move(nodes); + + ParseStringProperty(&scene.name, err, o, "name", false); + + ParseExtensionsProperty(&scene.extensions, err, o); + ParseExtrasProperty(&scene.extras, o); + + if (store_original_json_for_extras_and_extensions_) { + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + scene.extensions_json_string = JsonToString(GetValue(it)); + } + } + { + json_const_iterator it; + if (FindMember(o, "extras", it)) { + scene.extras_json_string = JsonToString(GetValue(it)); + } + } + } + + model->scenes.emplace_back(std::move(scene)); + return true; + }); + + if (!success) { + return false; + } + } + + // 9. Parse default scenes. + { + json_const_iterator rootIt; + int iVal; + if (FindMember(v, "scene", rootIt) && GetInt(GetValue(rootIt), iVal)) { + model->defaultScene = iVal; + } + } + + // 10. Parse Material + { + bool success = ForEachInArray(v, "materials", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`materials' does not contain an JSON object."; + } + return false; + } + Material material; + ParseStringProperty(&material.name, err, o, "name", false); + + if (!ParseMaterial(&material, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->materials.emplace_back(std::move(material)); + return true; + }); + + if (!success) { + return false; + } + } + + // 11. Parse Image + void *load_image_user_data{nullptr}; + + LoadImageDataOption load_image_option; + + if (user_image_loader_) { + // Use user supplied pointer + load_image_user_data = load_image_user_data_; + } else { + load_image_option.preserve_channels = preserve_image_channels_; + load_image_user_data = reinterpret_cast(&load_image_option); + } + + { + int idx = 0; + bool success = ForEachInArray(v, "images", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "image[" + std::to_string(idx) + "] is not a JSON object."; + } + return false; + } + Image image; + if (!ParseImage(&image, idx, err, warn, o, + store_original_json_for_extras_and_extensions_, base_dir, + &fs, &this->LoadImageData, load_image_user_data)) { + return false; + } + + if (image.bufferView != -1) { + // Load image from the buffer view. + if (size_t(image.bufferView) >= model->bufferViews.size()) { + if (err) { + std::stringstream ss; + ss << "image[" << idx << "] bufferView \"" << image.bufferView + << "\" not found in the scene." << std::endl; + (*err) += ss.str(); + } + return false; + } + + const BufferView &bufferView = + model->bufferViews[size_t(image.bufferView)]; + if (size_t(bufferView.buffer) >= model->buffers.size()) { + if (err) { + std::stringstream ss; + ss << "image[" << idx << "] buffer \"" << bufferView.buffer + << "\" not found in the scene." << std::endl; + (*err) += ss.str(); + } + return false; + } + const Buffer &buffer = model->buffers[size_t(bufferView.buffer)]; + + if (*LoadImageData == nullptr) { + if (err) { + (*err) += "No LoadImageData callback specified.\n"; + } + return false; + } + bool ret = LoadImageData( + &image, idx, err, warn, image.width, image.height, + &buffer.data[bufferView.byteOffset], + static_cast(bufferView.byteLength), load_image_user_data); + if (!ret) { + return false; + } + } + + model->images.emplace_back(std::move(image)); + ++idx; + return true; + }); + + if (!success) { + return false; + } + } + + // 12. Parse Texture + { + bool success = ForEachInArray(v, "textures", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`textures' does not contain an JSON object."; + } + return false; + } + Texture texture; + if (!ParseTexture(&texture, err, o, + store_original_json_for_extras_and_extensions_, + base_dir)) { + return false; + } + + model->textures.emplace_back(std::move(texture)); + return true; + }); + + if (!success) { + return false; + } + } + + // 13. Parse Animation + { + bool success = ForEachInArray(v, "animations", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`animations' does not contain an JSON object."; + } + return false; + } + Animation animation; + if (!ParseAnimation(&animation, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->animations.emplace_back(std::move(animation)); + return true; + }); + + if (!success) { + return false; + } + } + + // 14. Parse Skin + { + bool success = ForEachInArray(v, "skins", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`skins' does not contain an JSON object."; + } + return false; + } + Skin skin; + if (!ParseSkin(&skin, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->skins.emplace_back(std::move(skin)); + return true; + }); + + if (!success) { + return false; + } + } + + // 15. Parse Sampler + { + bool success = ForEachInArray(v, "samplers", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`samplers' does not contain an JSON object."; + } + return false; + } + Sampler sampler; + if (!ParseSampler(&sampler, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->samplers.emplace_back(std::move(sampler)); + return true; + }); + + if (!success) { + return false; + } + } + + // 16. Parse Camera + { + bool success = ForEachInArray(v, "cameras", [&](const json &o) { + if (!IsObject(o)) { + if (err) { + (*err) += "`cameras' does not contain an JSON object."; + } + return false; + } + Camera camera; + if (!ParseCamera(&camera, err, o, + store_original_json_for_extras_and_extensions_)) { + return false; + } + + model->cameras.emplace_back(std::move(camera)); + return true; + }); + + if (!success) { + return false; + } + } + + // 17. Parse Extensions + ParseExtensionsProperty(&model->extensions, err, v); + + // 18. Specific extension implementations + { + json_const_iterator rootIt; + if (FindMember(v, "extensions", rootIt) && IsObject(GetValue(rootIt))) { + const json &root = GetValue(rootIt); + + json_const_iterator it(ObjectBegin(root)); + json_const_iterator itEnd(ObjectEnd(root)); + for (; it != itEnd; ++it) { + // parse KHR_lights_punctual extension + std::string key(GetKey(it)); + if ((key == "KHR_lights_punctual") && IsObject(GetValue(it))) { + const json &object = GetValue(it); + json_const_iterator itLight; + if (FindMember(object, "lights", itLight)) { + const json &lights = GetValue(itLight); + if (!IsArray(lights)) { + continue; + } + + auto arrayIt(ArrayBegin(lights)); + auto arrayItEnd(ArrayEnd(lights)); + for (; arrayIt != arrayItEnd; ++arrayIt) { + Light light; + if (!ParseLight(&light, err, *arrayIt, + store_original_json_for_extras_and_extensions_)) { + return false; + } + model->lights.emplace_back(std::move(light)); + } + } + } + } + } + } + + // 19. Parse Extras + ParseExtrasProperty(&model->extras, v); + + if (store_original_json_for_extras_and_extensions_) { + model->extras_json_string = JsonToString(v["extras"]); + model->extensions_json_string = JsonToString(v["extensions"]); + } + + return true; +} + +bool TinyGLTF::LoadASCIIFromString(Model *model, std::string *err, + std::string *warn, const char *str, + unsigned int length, + const std::string &base_dir, + unsigned int check_sections) { + is_binary_ = false; + bin_data_ = nullptr; + bin_size_ = 0; + + return LoadFromString(model, err, warn, str, length, base_dir, + check_sections); +} + +bool TinyGLTF::LoadASCIIFromFile(Model *model, std::string *err, + std::string *warn, const std::string &filename, + unsigned int check_sections) { + std::stringstream ss; + + if (fs.ReadWholeFile == nullptr) { + // Programmer error, assert() ? + ss << "Failed to read file: " << filename + << ": one or more FS callback not set" << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + std::vector data; + std::string fileerr; + bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); + if (!fileread) { + ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + size_t sz = data.size(); + if (sz == 0) { + if (err) { + (*err) = "Empty file."; + } + return false; + } + + std::string basedir = GetBaseDir(filename); + + bool ret = LoadASCIIFromString( + model, err, warn, reinterpret_cast(&data.at(0)), + static_cast(data.size()), basedir, check_sections); + + return ret; +} + +bool TinyGLTF::LoadBinaryFromMemory(Model *model, std::string *err, + std::string *warn, + const unsigned char *bytes, + unsigned int size, + const std::string &base_dir, + unsigned int check_sections) { + if (size < 20) { + if (err) { + (*err) = "Too short data size for glTF Binary."; + } + return false; + } + + if (bytes[0] == 'g' && bytes[1] == 'l' && bytes[2] == 'T' && + bytes[3] == 'F') { + // ok + } else { + if (err) { + (*err) = "Invalid magic."; + } + return false; + } + + unsigned int version; // 4 bytes + unsigned int length; // 4 bytes + unsigned int model_length; // 4 bytes + unsigned int model_format; // 4 bytes; + + // @todo { Endian swap for big endian machine. } + memcpy(&version, bytes + 4, 4); + swap4(&version); + memcpy(&length, bytes + 8, 4); + swap4(&length); + memcpy(&model_length, bytes + 12, 4); + swap4(&model_length); + memcpy(&model_format, bytes + 16, 4); + swap4(&model_format); + + // In case the Bin buffer is not present, the size is exactly 20 + size of + // JSON contents, + // so use "greater than" operator. + if ((20 + model_length > size) || (model_length < 1) || (length > size) || + (20 + model_length > length) || + (model_format != 0x4E4F534A)) { // 0x4E4F534A = JSON format. + if (err) { + (*err) = "Invalid glTF binary."; + } + return false; + } + + // Extract JSON string. + std::string jsonString(reinterpret_cast(&bytes[20]), + model_length); + + is_binary_ = true; + bin_data_ = bytes + 20 + model_length + + 8; // 4 bytes (buffer_length) + 4 bytes(buffer_format) + bin_size_ = + length - (20 + model_length); // extract header + JSON scene data. + + bool ret = LoadFromString(model, err, warn, + reinterpret_cast(&bytes[20]), + model_length, base_dir, check_sections); + if (!ret) { + return ret; + } + + return true; +} + +bool TinyGLTF::LoadBinaryFromFile(Model *model, std::string *err, + std::string *warn, + const std::string &filename, + unsigned int check_sections) { + std::stringstream ss; + + if (fs.ReadWholeFile == nullptr) { + // Programmer error, assert() ? + ss << "Failed to read file: " << filename + << ": one or more FS callback not set" << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + std::vector data; + std::string fileerr; + bool fileread = fs.ReadWholeFile(&data, &fileerr, filename, fs.user_data); + if (!fileread) { + ss << "Failed to read file: " << filename << ": " << fileerr << std::endl; + if (err) { + (*err) = ss.str(); + } + return false; + } + + std::string basedir = GetBaseDir(filename); + + bool ret = LoadBinaryFromMemory(model, err, warn, &data.at(0), + static_cast(data.size()), + basedir, check_sections); + + return ret; +} + +/////////////////////// +// GLTF Serialization +/////////////////////// +namespace { +json JsonFromString(const char *s) { +#ifdef TINYGLTF_USE_RAPIDJSON + return json(s, GetAllocator()); +#else + return json(s); +#endif +} + +void JsonAssign(json &dest, const json &src) { +#ifdef TINYGLTF_USE_RAPIDJSON + dest.CopyFrom(src, GetAllocator()); +#else + dest = src; +#endif +} + +void JsonAddMember(json &o, const char *key, json &&value) { +#ifdef TINYGLTF_USE_RAPIDJSON + if (!o.IsObject()) { + o.SetObject(); + } + o.AddMember(json(key, GetAllocator()), std::move(value), GetAllocator()); +#else + o[key] = std::move(value); +#endif +} + +void JsonPushBack(json &o, json &&value) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.PushBack(std::move(value), GetAllocator()); +#else + o.push_back(std::move(value)); +#endif +} + +bool JsonIsNull(const json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + return o.IsNull(); +#else + return o.is_null(); +#endif +} + +void JsonSetObject(json &o) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.SetObject(); +#else + o = o.object({}); +#endif +} + +void JsonReserveArray(json &o, size_t s) { +#ifdef TINYGLTF_USE_RAPIDJSON + o.SetArray(); + o.Reserve(static_cast(s), GetAllocator()); +#endif + (void)(o); + (void)(s); +} +} // namespace + +// typedef std::pair json_object_pair; + +template +static void SerializeNumberProperty(const std::string &key, T number, + json &obj) { + // obj.insert( + // json_object_pair(key, json(static_cast(number)))); + // obj[key] = static_cast(number); + JsonAddMember(obj, key.c_str(), json(number)); +} + +#ifdef TINYGLTF_USE_RAPIDJSON +template <> +void SerializeNumberProperty(const std::string &key, size_t number, json &obj) { + JsonAddMember(obj, key.c_str(), json(static_cast(number))); +} +#endif + +template +static void SerializeNumberArrayProperty(const std::string &key, + const std::vector &value, + json &obj) { + if (value.empty()) return; + + json ary; + JsonReserveArray(ary, value.size()); + for (const auto &s : value) { + JsonPushBack(ary, json(s)); + } + JsonAddMember(obj, key.c_str(), std::move(ary)); +} + +static void SerializeStringProperty(const std::string &key, + const std::string &value, json &obj) { + JsonAddMember(obj, key.c_str(), JsonFromString(value.c_str())); +} + +static void SerializeStringArrayProperty(const std::string &key, + const std::vector &value, + json &obj) { + json ary; + JsonReserveArray(ary, value.size()); + for (auto &s : value) { + JsonPushBack(ary, JsonFromString(s.c_str())); + } + JsonAddMember(obj, key.c_str(), std::move(ary)); +} + +static bool ValueToJson(const Value &value, json *ret) { + json obj; +#ifdef TINYGLTF_USE_RAPIDJSON + switch (value.Type()) { + case REAL_TYPE: + obj.SetDouble(value.Get()); + break; + case INT_TYPE: + obj.SetInt(value.Get()); + break; + case BOOL_TYPE: + obj.SetBool(value.Get()); + break; + case STRING_TYPE: + obj.SetString(value.Get().c_str(), GetAllocator()); + break; + case ARRAY_TYPE: { + obj.SetArray(); + obj.Reserve(static_cast(value.ArrayLen()), + GetAllocator()); + for (unsigned int i = 0; i < value.ArrayLen(); ++i) { + Value elementValue = value.Get(int(i)); + json elementJson; + if (ValueToJson(value.Get(int(i)), &elementJson)) + obj.PushBack(std::move(elementJson), GetAllocator()); + } + break; + } + case BINARY_TYPE: + // TODO + // obj = json(value.Get>()); + return false; + break; + case OBJECT_TYPE: { + obj.SetObject(); + Value::Object objMap = value.Get(); + for (auto &it : objMap) { + json elementJson; + if (ValueToJson(it.second, &elementJson)) { + obj.AddMember(json(it.first.c_str(), GetAllocator()), + std::move(elementJson), GetAllocator()); + } + } + break; + } + case NULL_TYPE: + default: + return false; + } +#else + switch (value.Type()) { + case REAL_TYPE: + obj = json(value.Get()); + break; + case INT_TYPE: + obj = json(value.Get()); + break; + case BOOL_TYPE: + obj = json(value.Get()); + break; + case STRING_TYPE: + obj = json(value.Get()); + break; + case ARRAY_TYPE: { + for (unsigned int i = 0; i < value.ArrayLen(); ++i) { + Value elementValue = value.Get(int(i)); + json elementJson; + if (ValueToJson(value.Get(int(i)), &elementJson)) + obj.push_back(elementJson); + } + break; + } + case BINARY_TYPE: + // TODO + // obj = json(value.Get>()); + return false; + break; + case OBJECT_TYPE: { + Value::Object objMap = value.Get(); + for (auto &it : objMap) { + json elementJson; + if (ValueToJson(it.second, &elementJson)) obj[it.first] = elementJson; + } + break; + } + case NULL_TYPE: + default: + return false; + } +#endif + if (ret) *ret = std::move(obj); + return true; +} + +static void SerializeValue(const std::string &key, const Value &value, + json &obj) { + json ret; + if (ValueToJson(value, &ret)) { + JsonAddMember(obj, key.c_str(), std::move(ret)); + } +} + +static void SerializeGltfBufferData(const std::vector &data, + json &o) { + std::string header = "data:application/octet-stream;base64,"; + if (data.size() > 0) { + std::string encodedData = + base64_encode(&data[0], static_cast(data.size())); + SerializeStringProperty("uri", header + encodedData, o); + } else { + // Issue #229 + // size 0 is allowd. Just emit mime header. + SerializeStringProperty("uri", header, o); + } +} + +static bool SerializeGltfBufferData(const std::vector &data, + const std::string &binFilename) { +#ifdef _WIN32 +#if defined(__GLIBCXX__) // mingw + int file_descriptor = _wopen(UTF8ToWchar(binFilename).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream output(&wfile_buf); + if (!wfile_buf.is_open()) return false; +#elif defined(_MSC_VER) + std::ofstream output(UTF8ToWchar(binFilename).c_str(), std::ofstream::binary); + if (!output.is_open()) return false; +#else + std::ofstream output(binFilename.c_str(), std::ofstream::binary); + if (!output.is_open()) return false; +#endif +#else + std::ofstream output(binFilename.c_str(), std::ofstream::binary); + if (!output.is_open()) return false; +#endif + if (data.size() > 0) { + output.write(reinterpret_cast(&data[0]), + std::streamsize(data.size())); + } else { + // Issue #229 + // size 0 will be still valid buffer data. + // write empty file. + } + return true; +} + +#if 0 // FIXME(syoyo): not used. will be removed in the future release. +static void SerializeParameterMap(ParameterMap ¶m, json &o) { + for (ParameterMap::iterator paramIt = param.begin(); paramIt != param.end(); + ++paramIt) { + if (paramIt->second.number_array.size()) { + SerializeNumberArrayProperty(paramIt->first, + paramIt->second.number_array, o); + } else if (paramIt->second.json_double_value.size()) { + json json_double_value; + for (std::map::iterator it = + paramIt->second.json_double_value.begin(); + it != paramIt->second.json_double_value.end(); ++it) { + if (it->first == "index") { + json_double_value[it->first] = paramIt->second.TextureIndex(); + } else { + json_double_value[it->first] = it->second; + } + } + + o[paramIt->first] = json_double_value; + } else if (!paramIt->second.string_value.empty()) { + SerializeStringProperty(paramIt->first, paramIt->second.string_value, o); + } else if (paramIt->second.has_number_value) { + o[paramIt->first] = paramIt->second.number_value; + } else { + o[paramIt->first] = paramIt->second.bool_value; + } + } +} +#endif + +static void SerializeExtensionMap(const ExtensionMap &extensions, json &o) { + if (!extensions.size()) return; + + json extMap; + for (ExtensionMap::const_iterator extIt = extensions.begin(); + extIt != extensions.end(); ++extIt) { + // Allow an empty object for extension(#97) + json ret; + bool isNull = true; + if (ValueToJson(extIt->second, &ret)) { + isNull = JsonIsNull(ret); + JsonAddMember(extMap, extIt->first.c_str(), std::move(ret)); + } + if (isNull) { + if (!(extIt->first.empty())) { // name should not be empty, but for sure + // create empty object so that an extension name is still included in + // json. + json empty; + JsonSetObject(empty); + JsonAddMember(extMap, extIt->first.c_str(), std::move(empty)); + } + } + } + JsonAddMember(o, "extensions", std::move(extMap)); +} + +static void SerializeGltfAccessor(Accessor &accessor, json &o) { + if (accessor.bufferView >= 0) + SerializeNumberProperty("bufferView", accessor.bufferView, o); + + if (accessor.byteOffset != 0) + SerializeNumberProperty("byteOffset", int(accessor.byteOffset), o); + + SerializeNumberProperty("componentType", accessor.componentType, o); + SerializeNumberProperty("count", accessor.count, o); + + if ((accessor.componentType == TINYGLTF_COMPONENT_TYPE_FLOAT) || + (accessor.componentType == TINYGLTF_COMPONENT_TYPE_DOUBLE)) { + SerializeNumberArrayProperty("min", accessor.minValues, o); + SerializeNumberArrayProperty("max", accessor.maxValues, o); + } else { + // Issue #301. Serialize as integer. + // Assume int value is within [-2**31-1, 2**31-1] + { + std::vector values; + std::transform(accessor.minValues.begin(), accessor.minValues.end(), + std::back_inserter(values), + [](double v) { return static_cast(v); }); + + SerializeNumberArrayProperty("min", values, o); + } + + { + std::vector values; + std::transform(accessor.maxValues.begin(), accessor.maxValues.end(), + std::back_inserter(values), + [](double v) { return static_cast(v); }); + + SerializeNumberArrayProperty("max", values, o); + } + } + + if (accessor.normalized) + SerializeValue("normalized", Value(accessor.normalized), o); + std::string type; + switch (accessor.type) { + case TINYGLTF_TYPE_SCALAR: + type = "SCALAR"; + break; + case TINYGLTF_TYPE_VEC2: + type = "VEC2"; + break; + case TINYGLTF_TYPE_VEC3: + type = "VEC3"; + break; + case TINYGLTF_TYPE_VEC4: + type = "VEC4"; + break; + case TINYGLTF_TYPE_MAT2: + type = "MAT2"; + break; + case TINYGLTF_TYPE_MAT3: + type = "MAT3"; + break; + case TINYGLTF_TYPE_MAT4: + type = "MAT4"; + break; + } + + SerializeStringProperty("type", type, o); + if (!accessor.name.empty()) SerializeStringProperty("name", accessor.name, o); + + if (accessor.extras.Type() != NULL_TYPE) { + SerializeValue("extras", accessor.extras, o); + } +} + +static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { + SerializeNumberProperty("sampler", channel.sampler, o); + { + json target; + SerializeNumberProperty("node", channel.target_node, target); + SerializeStringProperty("path", channel.target_path, target); + + SerializeExtensionMap(channel.target_extensions, target); + + JsonAddMember(o, "target", std::move(target)); + } + + if (channel.extras.Type() != NULL_TYPE) { + SerializeValue("extras", channel.extras, o); + } + + SerializeExtensionMap(channel.extensions, o); +} + +static void SerializeGltfAnimationSampler(AnimationSampler &sampler, json &o) { + SerializeNumberProperty("input", sampler.input, o); + SerializeNumberProperty("output", sampler.output, o); + SerializeStringProperty("interpolation", sampler.interpolation, o); + + if (sampler.extras.Type() != NULL_TYPE) { + SerializeValue("extras", sampler.extras, o); + } +} + +static void SerializeGltfAnimation(Animation &animation, json &o) { + if (!animation.name.empty()) + SerializeStringProperty("name", animation.name, o); + + { + json channels; + JsonReserveArray(channels, animation.channels.size()); + for (unsigned int i = 0; i < animation.channels.size(); ++i) { + json channel; + AnimationChannel gltfChannel = animation.channels[i]; + SerializeGltfAnimationChannel(gltfChannel, channel); + JsonPushBack(channels, std::move(channel)); + } + + JsonAddMember(o, "channels", std::move(channels)); + } + + { + json samplers; + JsonReserveArray(samplers, animation.samplers.size()); + for (unsigned int i = 0; i < animation.samplers.size(); ++i) { + json sampler; + AnimationSampler gltfSampler = animation.samplers[i]; + SerializeGltfAnimationSampler(gltfSampler, sampler); + JsonPushBack(samplers, std::move(sampler)); + } + JsonAddMember(o, "samplers", std::move(samplers)); + } + + if (animation.extras.Type() != NULL_TYPE) { + SerializeValue("extras", animation.extras, o); + } + + SerializeExtensionMap(animation.extensions, o); +} + +static void SerializeGltfAsset(Asset &asset, json &o) { + if (!asset.generator.empty()) { + SerializeStringProperty("generator", asset.generator, o); + } + + if (!asset.copyright.empty()) { + SerializeStringProperty("copyright", asset.copyright, o); + } + + if (asset.version.empty()) { + // Just in case + // `version` must be defined + asset.version = "2.0"; + } + + // TODO(syoyo): Do we need to check if `version` is greater or equal to 2.0? + SerializeStringProperty("version", asset.version, o); + + if (asset.extras.Keys().size()) { + SerializeValue("extras", asset.extras, o); + } + + SerializeExtensionMap(asset.extensions, o); +} + +static void SerializeGltfBufferBin(Buffer &buffer, json &o, + std::vector &binBuffer) { + SerializeNumberProperty("byteLength", buffer.data.size(), o); + binBuffer = buffer.data; + + if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); + + if (buffer.extras.Type() != NULL_TYPE) { + SerializeValue("extras", buffer.extras, o); + } +} + +static void SerializeGltfBuffer(Buffer &buffer, json &o) { + SerializeNumberProperty("byteLength", buffer.data.size(), o); + SerializeGltfBufferData(buffer.data, o); + + if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); + + if (buffer.extras.Type() != NULL_TYPE) { + SerializeValue("extras", buffer.extras, o); + } +} + +static bool SerializeGltfBuffer(Buffer &buffer, json &o, + const std::string &binFilename, + const std::string &binBaseFilename) { + if (!SerializeGltfBufferData(buffer.data, binFilename)) return false; + SerializeNumberProperty("byteLength", buffer.data.size(), o); + SerializeStringProperty("uri", binBaseFilename, o); + + if (buffer.name.size()) SerializeStringProperty("name", buffer.name, o); + + if (buffer.extras.Type() != NULL_TYPE) { + SerializeValue("extras", buffer.extras, o); + } + return true; +} + +static void SerializeGltfBufferView(BufferView &bufferView, json &o) { + SerializeNumberProperty("buffer", bufferView.buffer, o); + SerializeNumberProperty("byteLength", bufferView.byteLength, o); + + // byteStride is optional, minimum allowed is 4 + if (bufferView.byteStride >= 4) { + SerializeNumberProperty("byteStride", bufferView.byteStride, o); + } + // byteOffset is optional, default is 0 + if (bufferView.byteOffset > 0) { + SerializeNumberProperty("byteOffset", bufferView.byteOffset, o); + } + // Target is optional, check if it contains a valid value + if (bufferView.target == TINYGLTF_TARGET_ARRAY_BUFFER || + bufferView.target == TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER) { + SerializeNumberProperty("target", bufferView.target, o); + } + if (bufferView.name.size()) { + SerializeStringProperty("name", bufferView.name, o); + } + + if (bufferView.extras.Type() != NULL_TYPE) { + SerializeValue("extras", bufferView.extras, o); + } +} + +static void SerializeGltfImage(Image &image, json &o) { + // if uri empty, the mimeType and bufferview should be set + if (image.uri.empty()) { + SerializeStringProperty("mimeType", image.mimeType, o); + SerializeNumberProperty("bufferView", image.bufferView, o); + } else { + // TODO(syoyo): dlib::urilencode? + SerializeStringProperty("uri", image.uri, o); + } + + if (image.name.size()) { + SerializeStringProperty("name", image.name, o); + } + + if (image.extras.Type() != NULL_TYPE) { + SerializeValue("extras", image.extras, o); + } + + SerializeExtensionMap(image.extensions, o); +} + +static void SerializeGltfTextureInfo(TextureInfo &texinfo, json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfNormalTextureInfo(NormalTextureInfo &texinfo, + json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(texinfo.scale, 1.0)) { + SerializeNumberProperty("scale", texinfo.scale, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfOcclusionTextureInfo(OcclusionTextureInfo &texinfo, + json &o) { + SerializeNumberProperty("index", texinfo.index, o); + + if (texinfo.texCoord != 0) { + SerializeNumberProperty("texCoord", texinfo.texCoord, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(texinfo.strength, 1.0)) { + SerializeNumberProperty("strength", texinfo.strength, o); + } + + if (texinfo.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texinfo.extras, o); + } + + SerializeExtensionMap(texinfo.extensions, o); +} + +static void SerializeGltfPbrMetallicRoughness(PbrMetallicRoughness &pbr, + json &o) { + std::vector default_baseColorFactor = {1.0, 1.0, 1.0, 1.0}; + if (!Equals(pbr.baseColorFactor, default_baseColorFactor)) { + SerializeNumberArrayProperty("baseColorFactor", pbr.baseColorFactor, + o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(pbr.metallicFactor, 1.0)) { + SerializeNumberProperty("metallicFactor", pbr.metallicFactor, o); + } + + if (!TINYGLTF_DOUBLE_EQUAL(pbr.roughnessFactor, 1.0)) { + SerializeNumberProperty("roughnessFactor", pbr.roughnessFactor, o); + } + + if (pbr.baseColorTexture.index > -1) { + json texinfo; + SerializeGltfTextureInfo(pbr.baseColorTexture, texinfo); + JsonAddMember(o, "baseColorTexture", std::move(texinfo)); + } + + if (pbr.metallicRoughnessTexture.index > -1) { + json texinfo; + SerializeGltfTextureInfo(pbr.metallicRoughnessTexture, texinfo); + JsonAddMember(o, "metallicRoughnessTexture", std::move(texinfo)); + } + + SerializeExtensionMap(pbr.extensions, o); + + if (pbr.extras.Type() != NULL_TYPE) { + SerializeValue("extras", pbr.extras, o); + } +} + +static void SerializeGltfMaterial(Material &material, json &o) { + if (material.name.size()) { + SerializeStringProperty("name", material.name, o); + } + + // QUESTION(syoyo): Write material parameters regardless of its default value? + + if (!TINYGLTF_DOUBLE_EQUAL(material.alphaCutoff, 0.5)) { + SerializeNumberProperty("alphaCutoff", material.alphaCutoff, o); + } + + if (material.alphaMode.compare("OPAQUE") != 0) { + SerializeStringProperty("alphaMode", material.alphaMode, o); + } + + if (material.doubleSided != false) + JsonAddMember(o, "doubleSided", json(material.doubleSided)); + + if (material.normalTexture.index > -1) { + json texinfo; + SerializeGltfNormalTextureInfo(material.normalTexture, texinfo); + JsonAddMember(o, "normalTexture", std::move(texinfo)); + } + + if (material.occlusionTexture.index > -1) { + json texinfo; + SerializeGltfOcclusionTextureInfo(material.occlusionTexture, texinfo); + JsonAddMember(o, "occlusionTexture", std::move(texinfo)); + } + + if (material.emissiveTexture.index > -1) { + json texinfo; + SerializeGltfTextureInfo(material.emissiveTexture, texinfo); + JsonAddMember(o, "emissiveTexture", std::move(texinfo)); + } + + std::vector default_emissiveFactor = {0.0, 0.0, 0.0}; + if (!Equals(material.emissiveFactor, default_emissiveFactor)) { + SerializeNumberArrayProperty("emissiveFactor", + material.emissiveFactor, o); + } + + { + json pbrMetallicRoughness; + SerializeGltfPbrMetallicRoughness(material.pbrMetallicRoughness, + pbrMetallicRoughness); + // Issue 204 + // Do not serialize `pbrMetallicRoughness` if pbrMetallicRoughness has all + // default values(json is null). Otherwise it will serialize to + // `pbrMetallicRoughness : null`, which cannot be read by other glTF + // importers(and validators). + // + if (!JsonIsNull(pbrMetallicRoughness)) { + JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + } + } + +#if 0 // legacy way. just for the record. + if (material.values.size()) { + json pbrMetallicRoughness; + SerializeParameterMap(material.values, pbrMetallicRoughness); + JsonAddMember(o, "pbrMetallicRoughness", std::move(pbrMetallicRoughness)); + } + + SerializeParameterMap(material.additionalValues, o); +#else + +#endif + + SerializeExtensionMap(material.extensions, o); + + if (material.extras.Type() != NULL_TYPE) { + SerializeValue("extras", material.extras, o); + } +} + +static void SerializeGltfMesh(Mesh &mesh, json &o) { + json primitives; + JsonReserveArray(primitives, mesh.primitives.size()); + for (unsigned int i = 0; i < mesh.primitives.size(); ++i) { + json primitive; + const Primitive &gltfPrimitive = mesh.primitives[i]; // don't make a copy + { + json attributes; + for (auto attrIt = gltfPrimitive.attributes.begin(); + attrIt != gltfPrimitive.attributes.end(); ++attrIt) { + SerializeNumberProperty(attrIt->first, attrIt->second, attributes); + } + + JsonAddMember(primitive, "attributes", std::move(attributes)); + } + + // Indicies is optional + if (gltfPrimitive.indices > -1) { + SerializeNumberProperty("indices", gltfPrimitive.indices, primitive); + } + // Material is optional + if (gltfPrimitive.material > -1) { + SerializeNumberProperty("material", gltfPrimitive.material, + primitive); + } + SerializeNumberProperty("mode", gltfPrimitive.mode, primitive); + + // Morph targets + if (gltfPrimitive.targets.size()) { + json targets; + JsonReserveArray(targets, gltfPrimitive.targets.size()); + for (unsigned int k = 0; k < gltfPrimitive.targets.size(); ++k) { + json targetAttributes; + std::map targetData = gltfPrimitive.targets[k]; + for (std::map::iterator attrIt = targetData.begin(); + attrIt != targetData.end(); ++attrIt) { + SerializeNumberProperty(attrIt->first, attrIt->second, + targetAttributes); + } + JsonPushBack(targets, std::move(targetAttributes)); + } + JsonAddMember(primitive, "targets", std::move(targets)); + } + + SerializeExtensionMap(gltfPrimitive.extensions, primitive); + + if (gltfPrimitive.extras.Type() != NULL_TYPE) { + SerializeValue("extras", gltfPrimitive.extras, primitive); + } + + JsonPushBack(primitives, std::move(primitive)); + } + + JsonAddMember(o, "primitives", std::move(primitives)); + + if (mesh.weights.size()) { + SerializeNumberArrayProperty("weights", mesh.weights, o); + } + + if (mesh.name.size()) { + SerializeStringProperty("name", mesh.name, o); + } + + SerializeExtensionMap(mesh.extensions, o); + if (mesh.extras.Type() != NULL_TYPE) { + SerializeValue("extras", mesh.extras, o); + } +} + +static void SerializeSpotLight(SpotLight &spot, json &o) { + SerializeNumberProperty("innerConeAngle", spot.innerConeAngle, o); + SerializeNumberProperty("outerConeAngle", spot.outerConeAngle, o); + SerializeExtensionMap(spot.extensions, o); + if (spot.extras.Type() != NULL_TYPE) { + SerializeValue("extras", spot.extras, o); + } +} + +static void SerializeGltfLight(Light &light, json &o) { + if (!light.name.empty()) SerializeStringProperty("name", light.name, o); + SerializeNumberProperty("intensity", light.intensity, o); + if (light.range > 0.0) { + SerializeNumberProperty("range", light.range, o); + } + SerializeNumberArrayProperty("color", light.color, o); + SerializeStringProperty("type", light.type, o); + if (light.type == "spot") { + json spot; + SerializeSpotLight(light.spot, spot); + JsonAddMember(o, "spot", std::move(spot)); + } + SerializeExtensionMap(light.extensions, o); + if (light.extras.Type() != NULL_TYPE) { + SerializeValue("extras", light.extras, o); + } +} + +static void SerializeGltfNode(Node &node, json &o) { + if (node.translation.size() > 0) { + SerializeNumberArrayProperty("translation", node.translation, o); + } + if (node.rotation.size() > 0) { + SerializeNumberArrayProperty("rotation", node.rotation, o); + } + if (node.scale.size() > 0) { + SerializeNumberArrayProperty("scale", node.scale, o); + } + if (node.matrix.size() > 0) { + SerializeNumberArrayProperty("matrix", node.matrix, o); + } + if (node.mesh != -1) { + SerializeNumberProperty("mesh", node.mesh, o); + } + + if (node.skin != -1) { + SerializeNumberProperty("skin", node.skin, o); + } + + if (node.camera != -1) { + SerializeNumberProperty("camera", node.camera, o); + } + + if (node.weights.size() > 0) { + SerializeNumberArrayProperty("weights", node.weights, o); + } + + if (node.extras.Type() != NULL_TYPE) { + SerializeValue("extras", node.extras, o); + } + + SerializeExtensionMap(node.extensions, o); + if (!node.name.empty()) SerializeStringProperty("name", node.name, o); + SerializeNumberArrayProperty("children", node.children, o); +} + +static void SerializeGltfSampler(Sampler &sampler, json &o) { + if (sampler.magFilter != -1) { + SerializeNumberProperty("magFilter", sampler.magFilter, o); + } + if (sampler.minFilter != -1) { + SerializeNumberProperty("minFilter", sampler.minFilter, o); + } + //SerializeNumberProperty("wrapR", sampler.wrapR, o); + SerializeNumberProperty("wrapS", sampler.wrapS, o); + SerializeNumberProperty("wrapT", sampler.wrapT, o); + + if (sampler.extras.Type() != NULL_TYPE) { + SerializeValue("extras", sampler.extras, o); + } +} + +static void SerializeGltfOrthographicCamera(const OrthographicCamera &camera, + json &o) { + SerializeNumberProperty("zfar", camera.zfar, o); + SerializeNumberProperty("znear", camera.znear, o); + SerializeNumberProperty("xmag", camera.xmag, o); + SerializeNumberProperty("ymag", camera.ymag, o); + + if (camera.extras.Type() != NULL_TYPE) { + SerializeValue("extras", camera.extras, o); + } +} + +static void SerializeGltfPerspectiveCamera(const PerspectiveCamera &camera, + json &o) { + SerializeNumberProperty("zfar", camera.zfar, o); + SerializeNumberProperty("znear", camera.znear, o); + if (camera.aspectRatio > 0) { + SerializeNumberProperty("aspectRatio", camera.aspectRatio, o); + } + + if (camera.yfov > 0) { + SerializeNumberProperty("yfov", camera.yfov, o); + } + + if (camera.extras.Type() != NULL_TYPE) { + SerializeValue("extras", camera.extras, o); + } +} + +static void SerializeGltfCamera(const Camera &camera, json &o) { + SerializeStringProperty("type", camera.type, o); + if (!camera.name.empty()) { + SerializeStringProperty("name", camera.name, o); + } + + if (camera.type.compare("orthographic") == 0) { + json orthographic; + SerializeGltfOrthographicCamera(camera.orthographic, orthographic); + JsonAddMember(o, "orthographic", std::move(orthographic)); + } else if (camera.type.compare("perspective") == 0) { + json perspective; + SerializeGltfPerspectiveCamera(camera.perspective, perspective); + JsonAddMember(o, "perspective", std::move(perspective)); + } else { + // ??? + } + + if (camera.extras.Type() != NULL_TYPE) { + SerializeValue("extras", camera.extras, o); + } + SerializeExtensionMap(camera.extensions, o); +} + +static void SerializeGltfScene(Scene &scene, json &o) { + SerializeNumberArrayProperty("nodes", scene.nodes, o); + + if (scene.name.size()) { + SerializeStringProperty("name", scene.name, o); + } + if (scene.extras.Type() != NULL_TYPE) { + SerializeValue("extras", scene.extras, o); + } + SerializeExtensionMap(scene.extensions, o); +} + +static void SerializeGltfSkin(Skin &skin, json &o) { + // required + SerializeNumberArrayProperty("joints", skin.joints, o); + + if (skin.inverseBindMatrices >= 0) { + SerializeNumberProperty("inverseBindMatrices", skin.inverseBindMatrices, o); + } + + if (skin.skeleton >= 0) { + SerializeNumberProperty("skeleton", skin.skeleton, o); + } + + if (skin.name.size()) { + SerializeStringProperty("name", skin.name, o); + } +} + +static void SerializeGltfTexture(Texture &texture, json &o) { + if (texture.sampler > -1) { + SerializeNumberProperty("sampler", texture.sampler, o); + } + if (texture.source > -1) { + SerializeNumberProperty("source", texture.source, o); + } + if (texture.name.size()) { + SerializeStringProperty("name", texture.name, o); + } + if (texture.extras.Type() != NULL_TYPE) { + SerializeValue("extras", texture.extras, o); + } + SerializeExtensionMap(texture.extensions, o); +} + +/// +/// Serialize all properties except buffers and images. +/// +static void SerializeGltfModel(Model *model, json &o) { + // ACCESSORS + if (model->accessors.size()) { + json accessors; + JsonReserveArray(accessors, model->accessors.size()); + for (unsigned int i = 0; i < model->accessors.size(); ++i) { + json accessor; + SerializeGltfAccessor(model->accessors[i], accessor); + JsonPushBack(accessors, std::move(accessor)); + } + JsonAddMember(o, "accessors", std::move(accessors)); + } + + // ANIMATIONS + if (model->animations.size()) { + json animations; + JsonReserveArray(animations, model->animations.size()); + for (unsigned int i = 0; i < model->animations.size(); ++i) { + if (model->animations[i].channels.size()) { + json animation; + SerializeGltfAnimation(model->animations[i], animation); + JsonPushBack(animations, std::move(animation)); + } + } + + JsonAddMember(o, "animations", std::move(animations)); + } + + // ASSET + json asset; + SerializeGltfAsset(model->asset, asset); + JsonAddMember(o, "asset", std::move(asset)); + + // BUFFERVIEWS + if (model->bufferViews.size()) { + json bufferViews; + JsonReserveArray(bufferViews, model->bufferViews.size()); + for (unsigned int i = 0; i < model->bufferViews.size(); ++i) { + json bufferView; + SerializeGltfBufferView(model->bufferViews[i], bufferView); + JsonPushBack(bufferViews, std::move(bufferView)); + } + JsonAddMember(o, "bufferViews", std::move(bufferViews)); + } + + // Extensions required + if (model->extensionsRequired.size()) { + SerializeStringArrayProperty("extensionsRequired", + model->extensionsRequired, o); + } + + // MATERIALS + if (model->materials.size()) { + json materials; + JsonReserveArray(materials, model->materials.size()); + for (unsigned int i = 0; i < model->materials.size(); ++i) { + json material; + SerializeGltfMaterial(model->materials[i], material); + + if (JsonIsNull(material)) { + // Issue 294. + // `material` does not have any required parameters + // so the result may be null(unmodified) when all material parameters + // have default value. + // + // null is not allowed thus we create an empty JSON object. + JsonSetObject(material); + } + JsonPushBack(materials, std::move(material)); + } + JsonAddMember(o, "materials", std::move(materials)); + } + + // MESHES + if (model->meshes.size()) { + json meshes; + JsonReserveArray(meshes, model->meshes.size()); + for (unsigned int i = 0; i < model->meshes.size(); ++i) { + json mesh; + SerializeGltfMesh(model->meshes[i], mesh); + JsonPushBack(meshes, std::move(mesh)); + } + JsonAddMember(o, "meshes", std::move(meshes)); + } + + // NODES + if (model->nodes.size()) { + json nodes; + JsonReserveArray(nodes, model->nodes.size()); + for (unsigned int i = 0; i < model->nodes.size(); ++i) { + json node; + SerializeGltfNode(model->nodes[i], node); + JsonPushBack(nodes, std::move(node)); + } + JsonAddMember(o, "nodes", std::move(nodes)); + } + + // SCENE + if (model->defaultScene > -1) { + SerializeNumberProperty("scene", model->defaultScene, o); + } + + // SCENES + if (model->scenes.size()) { + json scenes; + JsonReserveArray(scenes, model->scenes.size()); + for (unsigned int i = 0; i < model->scenes.size(); ++i) { + json currentScene; + SerializeGltfScene(model->scenes[i], currentScene); + JsonPushBack(scenes, std::move(currentScene)); + } + JsonAddMember(o, "scenes", std::move(scenes)); + } + + // SKINS + if (model->skins.size()) { + json skins; + JsonReserveArray(skins, model->skins.size()); + for (unsigned int i = 0; i < model->skins.size(); ++i) { + json skin; + SerializeGltfSkin(model->skins[i], skin); + JsonPushBack(skins, std::move(skin)); + } + JsonAddMember(o, "skins", std::move(skins)); + } + + // TEXTURES + if (model->textures.size()) { + json textures; + JsonReserveArray(textures, model->textures.size()); + for (unsigned int i = 0; i < model->textures.size(); ++i) { + json texture; + SerializeGltfTexture(model->textures[i], texture); + JsonPushBack(textures, std::move(texture)); + } + JsonAddMember(o, "textures", std::move(textures)); + } + + // SAMPLERS + if (model->samplers.size()) { + json samplers; + JsonReserveArray(samplers, model->samplers.size()); + for (unsigned int i = 0; i < model->samplers.size(); ++i) { + json sampler; + SerializeGltfSampler(model->samplers[i], sampler); + JsonPushBack(samplers, std::move(sampler)); + } + JsonAddMember(o, "samplers", std::move(samplers)); + } + + // CAMERAS + if (model->cameras.size()) { + json cameras; + JsonReserveArray(cameras, model->cameras.size()); + for (unsigned int i = 0; i < model->cameras.size(); ++i) { + json camera; + SerializeGltfCamera(model->cameras[i], camera); + JsonPushBack(cameras, std::move(camera)); + } + JsonAddMember(o, "cameras", std::move(cameras)); + } + + // EXTENSIONS + SerializeExtensionMap(model->extensions, o); + + auto extensionsUsed = model->extensionsUsed; + + // LIGHTS as KHR_lights_punctual + if (model->lights.size()) { + json lights; + JsonReserveArray(lights, model->lights.size()); + for (unsigned int i = 0; i < model->lights.size(); ++i) { + json light; + SerializeGltfLight(model->lights[i], light); + JsonPushBack(lights, std::move(light)); + } + json khr_lights_cmn; + JsonAddMember(khr_lights_cmn, "lights", std::move(lights)); + json ext_j; + + { + json_const_iterator it; + if (FindMember(o, "extensions", it)) { + JsonAssign(ext_j, GetValue(it)); + } + } + + JsonAddMember(ext_j, "KHR_lights_punctual", std::move(khr_lights_cmn)); + + JsonAddMember(o, "extensions", std::move(ext_j)); + + // Also add "KHR_lights_punctual" to `extensionsUsed` + { + auto has_khr_lights_punctual = + std::find_if(extensionsUsed.begin(), extensionsUsed.end(), + [](const std::string &s) { + return (s.compare("KHR_lights_punctual") == 0); + }); + + if (has_khr_lights_punctual == extensionsUsed.end()) { + extensionsUsed.push_back("KHR_lights_punctual"); + } + } + } + + // Extensions used + if (extensionsUsed.size()) { + SerializeStringArrayProperty("extensionsUsed", extensionsUsed, o); + } + + // EXTRAS + if (model->extras.Type() != NULL_TYPE) { + SerializeValue("extras", model->extras, o); + } +} + +static bool WriteGltfStream(std::ostream &stream, const std::string &content) { + stream << content << std::endl; + return true; +} + +static bool WriteGltfFile(const std::string &output, + const std::string &content) { +#ifdef _WIN32 +#if defined(_MSC_VER) + std::ofstream gltfFile(UTF8ToWchar(output).c_str()); +#elif defined(__GLIBCXX__) + int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream gltfFile(&wfile_buf); + if (!wfile_buf.is_open()) return false; +#else + std::ofstream gltfFile(output.c_str()); + if (!gltfFile.is_open()) return false; +#endif +#else + std::ofstream gltfFile(output.c_str()); + if (!gltfFile.is_open()) return false; +#endif + return WriteGltfStream(gltfFile, content); +} + +static void WriteBinaryGltfStream(std::ostream &stream, + const std::string &content, + const std::vector &binBuffer) { + const std::string header = "glTF"; + const int version = 2; + + // https://stackoverflow.com/questions/3407012/c-rounding-up-to-the-nearest-multiple-of-a-number + auto roundUp = [](uint32_t numToRound, uint32_t multiple) { + if (multiple == 0) return numToRound; + + uint32_t remainder = numToRound % multiple; + if (remainder == 0) return numToRound; + + return numToRound + multiple - remainder; + }; + + const uint32_t padding_size = + roundUp(uint32_t(content.size()), 4) - uint32_t(content.size()); + + // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info. + // Chunk data must be located at 4-byte boundary. + const uint32_t length = + 12 + 8 + roundUp(uint32_t(content.size()), 4) + + (binBuffer.size() ? (8 + roundUp(uint32_t(binBuffer.size()), 4)) : 0); + + stream.write(header.c_str(), std::streamsize(header.size())); + stream.write(reinterpret_cast(&version), sizeof(version)); + stream.write(reinterpret_cast(&length), sizeof(length)); + + // JSON chunk info, then JSON data + const uint32_t model_length = uint32_t(content.size()) + padding_size; + const uint32_t model_format = 0x4E4F534A; + stream.write(reinterpret_cast(&model_length), + sizeof(model_length)); + stream.write(reinterpret_cast(&model_format), + sizeof(model_format)); + stream.write(content.c_str(), std::streamsize(content.size())); + + // Chunk must be multiplies of 4, so pad with spaces + if (padding_size > 0) { + const std::string padding = std::string(size_t(padding_size), ' '); + stream.write(padding.c_str(), std::streamsize(padding.size())); + } + if (binBuffer.size() > 0) { + const uint32_t bin_padding_size = + roundUp(uint32_t(binBuffer.size()), 4) - uint32_t(binBuffer.size()); + // BIN chunk info, then BIN data + const uint32_t bin_length = uint32_t(binBuffer.size()) + bin_padding_size; + const uint32_t bin_format = 0x004e4942; + stream.write(reinterpret_cast(&bin_length), + sizeof(bin_length)); + stream.write(reinterpret_cast(&bin_format), + sizeof(bin_format)); + stream.write(reinterpret_cast(binBuffer.data()), + std::streamsize(binBuffer.size())); + // Chunksize must be multiplies of 4, so pad with zeroes + if (bin_padding_size > 0) { + const std::vector padding = + std::vector(size_t(bin_padding_size), 0); + stream.write(reinterpret_cast(padding.data()), + std::streamsize(padding.size())); + } + } +} + +static void WriteBinaryGltfFile(const std::string &output, + const std::string &content, + const std::vector &binBuffer) { +#ifdef _WIN32 +#if defined(_MSC_VER) + std::ofstream gltfFile(UTF8ToWchar(output).c_str(), std::ios::binary); +#elif defined(__GLIBCXX__) + int file_descriptor = _wopen(UTF8ToWchar(output).c_str(), + _O_CREAT | _O_WRONLY | _O_TRUNC | _O_BINARY); + __gnu_cxx::stdio_filebuf wfile_buf( + file_descriptor, std::ios_base::out | std::ios_base::binary); + std::ostream gltfFile(&wfile_buf); +#else + std::ofstream gltfFile(output.c_str(), std::ios::binary); +#endif +#else + std::ofstream gltfFile(output.c_str(), std::ios::binary); +#endif + WriteBinaryGltfStream(gltfFile, content, binBuffer); +} + +bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, + bool prettyPrint = true, + bool writeBinary = false) { + JsonDocument output; + + /// Serialize all properties except buffers and images. + SerializeGltfModel(model, output); + + // BUFFERS + std::vector binBuffer; + if (model->buffers.size()) { + json buffers; + JsonReserveArray(buffers, model->buffers.size()); + for (unsigned int i = 0; i < model->buffers.size(); ++i) { + json buffer; + if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { + SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); + } else { + SerializeGltfBuffer(model->buffers[i], buffer); + } + JsonPushBack(buffers, std::move(buffer)); + } + JsonAddMember(output, "buffers", std::move(buffers)); + } + + // IMAGES + if (model->images.size()) { + json images; + JsonReserveArray(images, model->images.size()); + for (unsigned int i = 0; i < model->images.size(); ++i) { + json image; + + std::string dummystring = ""; + // UpdateImageObject need baseDir but only uses it if embeddedImages is + // enabled, since we won't write separate images when writing to a stream + // we + UpdateImageObject(model->images[i], dummystring, int(i), false, + &this->WriteImageData, this->write_image_user_data_); + SerializeGltfImage(model->images[i], image); + JsonPushBack(images, std::move(image)); + } + JsonAddMember(output, "images", std::move(images)); + } + + if (writeBinary) { + WriteBinaryGltfStream(stream, JsonToString(output), binBuffer); + } else { + WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1)); + } + + return true; +} + +bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, + bool embedImages = false, + bool embedBuffers = false, + bool prettyPrint = true, + bool writeBinary = false) { + JsonDocument output; + std::string defaultBinFilename = GetBaseFilename(filename); + std::string defaultBinFileExt = ".bin"; + std::string::size_type pos = + defaultBinFilename.rfind('.', defaultBinFilename.length()); + + if (pos != std::string::npos) { + defaultBinFilename = defaultBinFilename.substr(0, pos); + } + std::string baseDir = GetBaseDir(filename); + if (baseDir.empty()) { + baseDir = "./"; + } + /// Serialize all properties except buffers and images. + SerializeGltfModel(model, output); + + // BUFFERS + std::vector usedUris; + std::vector binBuffer; + if (model->buffers.size()) { + json buffers; + JsonReserveArray(buffers, model->buffers.size()); + for (unsigned int i = 0; i < model->buffers.size(); ++i) { + json buffer; + if (writeBinary && i == 0 && model->buffers[i].uri.empty()) { + SerializeGltfBufferBin(model->buffers[i], buffer, binBuffer); + } else if (embedBuffers) { + SerializeGltfBuffer(model->buffers[i], buffer); + } else { + std::string binSavePath; + std::string binUri; + if (!model->buffers[i].uri.empty() && + !IsDataURI(model->buffers[i].uri)) { + binUri = model->buffers[i].uri; + } else { + binUri = defaultBinFilename + defaultBinFileExt; + bool inUse = true; + int numUsed = 0; + while (inUse) { + inUse = false; + for (const std::string &usedName : usedUris) { + if (binUri.compare(usedName) != 0) continue; + inUse = true; + binUri = defaultBinFilename + std::to_string(numUsed++) + + defaultBinFileExt; + break; + } + } + } + usedUris.push_back(binUri); + binSavePath = JoinPath(baseDir, binUri); + if (!SerializeGltfBuffer(model->buffers[i], buffer, binSavePath, + binUri)) { + return false; + } + } + JsonPushBack(buffers, std::move(buffer)); + } + JsonAddMember(output, "buffers", std::move(buffers)); + } + + // IMAGES + if (model->images.size()) { + json images; + JsonReserveArray(images, model->images.size()); + for (unsigned int i = 0; i < model->images.size(); ++i) { + json image; + + UpdateImageObject(model->images[i], baseDir, int(i), embedImages, + &this->WriteImageData, this->write_image_user_data_); + SerializeGltfImage(model->images[i], image); + JsonPushBack(images, std::move(image)); + } + JsonAddMember(output, "images", std::move(images)); + } + + if (writeBinary) { + WriteBinaryGltfFile(filename, JsonToString(output), binBuffer); + } else { + WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1))); + } + + return true; +} + +} // namespace tinygltf + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + +#endif // TINYGLTF_IMPLEMENTATION diff --git a/intern/cycles/CMakeLists.txt b/intern/cycles/CMakeLists.txt index 381248e9bf1..1500743763b 100644 --- a/intern/cycles/CMakeLists.txt +++ b/intern/cycles/CMakeLists.txt @@ -247,7 +247,7 @@ if(WITH_CYCLES_OSL) endif() if(WITH_CYCLES_DEVICE_OPTIX) - find_package(OptiX) + find_package(OptiX 7.3.0) if(OPTIX_FOUND) add_definitions(-DWITH_OPTIX) @@ -286,14 +286,14 @@ if(WITH_OPENSUBDIV) ) endif() -if(WITH_CYCLES_STANDALONE) - set(WITH_CYCLES_DEVICE_OPENCL TRUE) - set(WITH_CYCLES_DEVICE_CUDA TRUE) - # Experimental and unfinished. - set(WITH_CYCLES_NETWORK FALSE) +if(WITH_OPENIMAGEDENOISE) + add_definitions(-DWITH_OPENIMAGEDENOISE) + add_definitions(-DOIDN_STATIC_LIB) + include_directories( + SYSTEM + ${OPENIMAGEDENOISE_INCLUDE_DIRS} + ) endif() -# TODO(sergey): Consider removing it, only causes confusion in interface. -set(WITH_CYCLES_DEVICE_MULTI TRUE) # Logging capabilities using GLog library. if(WITH_CYCLES_LOGGING) @@ -386,20 +386,15 @@ if(WITH_CYCLES_BLENDER) add_subdirectory(blender) endif() -if(WITH_CYCLES_NETWORK) - add_definitions(-DWITH_NETWORK) -endif() - -if(WITH_CYCLES_STANDALONE OR WITH_CYCLES_NETWORK OR WITH_CYCLES_CUBIN_COMPILER) - add_subdirectory(app) -endif() - +add_subdirectory(app) add_subdirectory(bvh) add_subdirectory(device) add_subdirectory(doc) add_subdirectory(graph) +add_subdirectory(integrator) add_subdirectory(kernel) -add_subdirectory(render) +add_subdirectory(scene) +add_subdirectory(session) add_subdirectory(subd) add_subdirectory(util) diff --git a/intern/cycles/app/CMakeLists.txt b/intern/cycles/app/CMakeLists.txt index 7a1e5d62dd2..50600793854 100644 --- a/intern/cycles/app/CMakeLists.txt +++ b/intern/cycles/app/CMakeLists.txt @@ -25,7 +25,8 @@ set(INC_SYS set(LIBRARIES cycles_device cycles_kernel - cycles_render + cycles_scene + cycles_session cycles_bvh cycles_subd cycles_graph @@ -64,6 +65,8 @@ if(WITH_CYCLES_STANDALONE) cycles_standalone.cpp cycles_xml.cpp cycles_xml.h + oiio_output_driver.cpp + oiio_output_driver.h ) add_executable(cycles ${SRC} ${INC} ${INC_SYS}) unset(SRC) @@ -73,7 +76,7 @@ if(WITH_CYCLES_STANDALONE) if(APPLE) if(WITH_OPENCOLORIO) - set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit") + set_property(TARGET cycles APPEND_STRING PROPERTY LINK_FLAGS " -framework IOKit -framework Carbon") endif() if(WITH_OPENIMAGEDENOISE AND "${CMAKE_OSX_ARCHITECTURES}" STREQUAL "arm64") # OpenImageDenoise uses BNNS from the Accelerate framework. @@ -90,24 +93,6 @@ if(WITH_CYCLES_STANDALONE) endif() endif() -##################################################################### -# Cycles network server executable -##################################################################### - -if(WITH_CYCLES_NETWORK) - set(SRC - cycles_server.cpp - ) - add_executable(cycles_server ${SRC}) - target_link_libraries(cycles_server ${LIBRARIES}) - cycles_target_link_libraries(cycles_server) - - if(UNIX AND NOT APPLE) - set_target_properties(cycles_server PROPERTIES INSTALL_RPATH $ORIGIN/lib) - endif() - unset(SRC) -endif() - ##################################################################### # Cycles cubin compiler executable ##################################################################### diff --git a/intern/cycles/app/cycles_server.cpp b/intern/cycles/app/cycles_server.cpp index 1ad70a376ed..38771b8aed8 100644 --- a/intern/cycles/app/cycles_server.cpp +++ b/intern/cycles/app/cycles_server.cpp @@ -18,13 +18,13 @@ #include "device/device.h" -#include "util/util_args.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_task.h" +#include "util/args.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/path.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/task.h" using namespace ccl; diff --git a/intern/cycles/app/cycles_standalone.cpp b/intern/cycles/app/cycles_standalone.cpp index 6b3513b065a..0032938b116 100644 --- a/intern/cycles/app/cycles_standalone.cpp +++ b/intern/cycles/app/cycles_standalone.cpp @@ -17,27 +17,30 @@ #include #include "device/device.h" -#include "render/buffers.h" -#include "render/camera.h" -#include "render/integrator.h" -#include "render/scene.h" -#include "render/session.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/scene.h" +#include "session/buffers.h" +#include "session/session.h" -#include "util/util_args.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_progress.h" -#include "util/util_string.h" -#include "util/util_time.h" -#include "util/util_transform.h" -#include "util/util_unique_ptr.h" -#include "util/util_version.h" +#include "util/args.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/image.h" +#include "util/log.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/string.h" +#include "util/time.h" +#include "util/transform.h" +#include "util/unique_ptr.h" +#include "util/version.h" + +#include "app/cycles_xml.h" +#include "app/oiio_output_driver.h" #ifdef WITH_CYCLES_STANDALONE_GUI -# include "util/util_view.h" +# include "util/view.h" #endif #include "app/cycles_xml.h" @@ -53,7 +56,8 @@ struct Options { SessionParams session_params; bool quiet; bool show_help, interactive, pause; - string output_path; + string output_filepath; + string output_pass; } options; static void session_print(const string &str) @@ -89,30 +93,6 @@ static void session_print_status() session_print(status); } -static bool write_render(const uchar *pixels, int w, int h, int channels) -{ - string msg = string_printf("Writing image %s", options.output_path.c_str()); - session_print(msg); - - unique_ptr out = unique_ptr(ImageOutput::create(options.output_path)); - if (!out) { - return false; - } - - ImageSpec spec(w, h, channels, TypeDesc::UINT8); - if (!out->open(options.output_path, spec)) { - return false; - } - - /* conversion for different top/bottom convention */ - out->write_image( - TypeDesc::UINT8, pixels + (h - 1) * w * channels, AutoStride, -w * channels, AutoStride); - - out->close(); - - return true; -} - static BufferParams &session_buffer_params() { static BufferParams buffer_params; @@ -126,7 +106,7 @@ static BufferParams &session_buffer_params() static void scene_init() { - options.scene = new Scene(options.scene_params, options.session->device); + options.scene = options.session->scene; /* Read XML */ xml_read_file(options.scene, options.filepath.c_str()); @@ -147,8 +127,13 @@ static void scene_init() static void session_init() { - options.session_params.write_render_cb = write_render; - options.session = new Session(options.session_params); + options.output_pass = "combined"; + options.session = new Session(options.session_params, options.scene_params); + + if (!options.output_filepath.empty()) { + options.session->set_output_driver(make_unique( + options.output_filepath, options.output_pass, session_print)); + } if (options.session_params.background && !options.quiet) options.session->progress.set_update_callback(function_bind(&session_print_status)); @@ -159,9 +144,13 @@ static void session_init() /* load scene */ scene_init(); - options.session->scene = options.scene; - options.session->reset(session_buffer_params(), options.session_params.samples); + /* add pass for output. */ + Pass *pass = options.scene->create_node(); + pass->set_name(ustring(options.output_pass.c_str())); + pass->set_type(PASS_COMBINED); + + options.session->reset(options.session_params, session_buffer_params()); options.session->start(); } @@ -223,9 +212,7 @@ static void display_info(Progress &progress) static void display() { - static DeviceDrawParams draw_params = DeviceDrawParams(); - - options.session->draw(session_buffer_params(), draw_params); + options.session->draw(); display_info(options.session->progress); } @@ -255,7 +242,7 @@ static void motion(int x, int y, int button) options.session->scene->camera->need_flags_update = true; options.session->scene->camera->need_device_update = true; - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } } @@ -272,7 +259,7 @@ static void resize(int width, int height) options.session->scene->camera->need_flags_update = true; options.session->scene->camera->need_device_update = true; - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } } @@ -284,7 +271,7 @@ static void keyboard(unsigned char key) /* Reset */ else if (key == 'r') - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); /* Cancel */ else if (key == 27) // escape @@ -321,7 +308,7 @@ static void keyboard(unsigned char key) options.session->scene->camera->need_flags_update = true; options.session->scene->camera->need_device_update = true; - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } /* Set Max Bounces */ @@ -347,7 +334,7 @@ static void keyboard(unsigned char key) options.session->scene->integrator->set_max_bounce(bounce); - options.session->reset(session_buffer_params(), options.session_params.samples); + options.session->reset(options.session_params, session_buffer_params()); } } #endif @@ -362,11 +349,13 @@ static int files_parse(int argc, const char *argv[]) static void options_parse(int argc, const char **argv) { - options.width = 0; - options.height = 0; + options.width = 1024; + options.height = 512; options.filepath = ""; options.session = NULL; options.quiet = false; + options.session_params.use_auto_tile = false; + options.session_params.tile_size = 0; /* device names */ string device_names = ""; @@ -412,7 +401,7 @@ static void options_parse(int argc, const char **argv) &options.session_params.samples, "Number of samples to render", "--output %s", - &options.output_path, + &options.output_filepath, "File path to write output image", "--threads %d", &options.session_params.threads, @@ -423,12 +412,9 @@ static void options_parse(int argc, const char **argv) "--height %d", &options.height, "Window height in pixel", - "--tile-width %d", - &options.session_params.tile_size.x, - "Tile width in pixels", - "--tile-height %d", - &options.session_params.tile_size.y, - "Tile height in pixels", + "--tile-size %d", + &options.session_params.tile_size, + "Tile size in pixels", "--list-devices", &list, "List information about all available devices", @@ -490,8 +476,9 @@ static void options_parse(int argc, const char **argv) options.session_params.background = true; #endif - /* Use progressive rendering */ - options.session_params.progressive = true; + if (options.session_params.tile_size > 0) { + options.session_params.use_auto_tile = true; + } /* find matching device */ DeviceType device_type = Device::type_from_string(devicename.c_str()); @@ -527,9 +514,6 @@ static void options_parse(int argc, const char **argv) fprintf(stderr, "No file path specified\n"); exit(EXIT_FAILURE); } - - /* For smoother Viewport */ - options.session_params.start_resolution = 64; } CCL_NAMESPACE_END diff --git a/intern/cycles/app/cycles_xml.cpp b/intern/cycles/app/cycles_xml.cpp index 276d850f1b3..6144d2c60a9 100644 --- a/intern/cycles/app/cycles_xml.cpp +++ b/intern/cycles/app/cycles_xml.cpp @@ -22,27 +22,27 @@ #include "graph/node_xml.h" -#include "render/background.h" -#include "render/camera.h" -#include "render/film.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" -#include "subd/subd_patch.h" -#include "subd/subd_split.h" +#include "subd/patch.h" +#include "subd/split.h" -#include "util/util_foreach.h" -#include "util/util_path.h" -#include "util/util_projection.h" -#include "util/util_transform.h" -#include "util/util_xml.h" +#include "util/foreach.h" +#include "util/path.h" +#include "util/projection.h" +#include "util/transform.h" +#include "util/xml.h" #include "app/cycles_xml.h" @@ -333,6 +333,7 @@ static void xml_read_shader_graph(XMLReadState &state, Shader *shader, xml_node } snode = (ShaderNode *)node_type->create(node_type); + snode->set_owner(graph); } xml_read_node(graph_reader, snode, node); @@ -703,7 +704,7 @@ void xml_read_file(Scene *scene, const char *filepath) xml_read_include(state, path_filename(filepath)); - scene->params.bvh_type = SceneParams::BVH_STATIC; + scene->params.bvh_type = BVH_TYPE_STATIC; } CCL_NAMESPACE_END diff --git a/intern/cycles/app/oiio_output_driver.cpp b/intern/cycles/app/oiio_output_driver.cpp new file mode 100644 index 00000000000..d791c89772f --- /dev/null +++ b/intern/cycles/app/oiio_output_driver.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "app/oiio_output_driver.h" + +CCL_NAMESPACE_BEGIN + +OIIOOutputDriver::OIIOOutputDriver(const string_view filepath, + const string_view pass, + LogFunction log) + : filepath_(filepath), pass_(pass), log_(log) +{ +} + +OIIOOutputDriver::~OIIOOutputDriver() +{ +} + +void OIIOOutputDriver::write_render_tile(const Tile &tile) +{ + /* Only write the full buffer, no intermediate tiles. */ + if (!(tile.size == tile.full_size)) { + return; + } + + log_(string_printf("Writing image %s", filepath_.c_str())); + + unique_ptr image_output(ImageOutput::create(filepath_)); + if (image_output == nullptr) { + log_("Failed to create image file"); + return; + } + + const int width = tile.size.x; + const int height = tile.size.y; + + ImageSpec spec(width, height, 4, TypeDesc::FLOAT); + if (!image_output->open(filepath_, spec)) { + log_("Failed to create image file"); + return; + } + + vector pixels(width * height * 4); + if (!tile.get_pass_pixels(pass_, 4, pixels.data())) { + log_("Failed to read render pass pixels"); + return; + } + + /* Manipulate offset and stride to convert from bottom-up to top-down convention. */ + image_output->write_image(TypeDesc::FLOAT, + pixels.data() + (height - 1) * width * 4, + AutoStride, + -width * 4 * sizeof(float), + AutoStride); + image_output->close(); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/app/oiio_output_driver.h b/intern/cycles/app/oiio_output_driver.h new file mode 100644 index 00000000000..a5c88e0e890 --- /dev/null +++ b/intern/cycles/app/oiio_output_driver.h @@ -0,0 +1,42 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "session/output_driver.h" + +#include "util/function.h" +#include "util/image.h" +#include "util/string.h" +#include "util/unique_ptr.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class OIIOOutputDriver : public OutputDriver { + public: + typedef function LogFunction; + + OIIOOutputDriver(const string_view filepath, const string_view pass, LogFunction log); + virtual ~OIIOOutputDriver(); + + void write_render_tile(const Tile &tile) override; + + protected: + string filepath_; + string pass_; + LogFunction log_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/CMakeLists.txt b/intern/cycles/blender/CMakeLists.txt index ee5c6157338..f0540486656 100644 --- a/intern/cycles/blender/CMakeLists.txt +++ b/intern/cycles/blender/CMakeLists.txt @@ -29,35 +29,39 @@ set(INC_SYS ) set(SRC - blender_camera.cpp - blender_device.cpp - blender_image.cpp - blender_geometry.cpp - blender_light.cpp - blender_mesh.cpp - blender_object.cpp - blender_object_cull.cpp - blender_particles.cpp - blender_curves.cpp - blender_logging.cpp - blender_python.cpp - blender_session.cpp - blender_shader.cpp - blender_sync.cpp - blender_texture.cpp - blender_viewport.cpp - blender_volume.cpp + camera.cpp + device.cpp + display_driver.cpp + image.cpp + geometry.cpp + light.cpp + mesh.cpp + object.cpp + object_cull.cpp + output_driver.cpp + particles.cpp + curves.cpp + logging.cpp + python.cpp + session.cpp + shader.cpp + sync.cpp + texture.cpp + viewport.cpp + volume.cpp CCL_api.h - blender_device.h - blender_id_map.h - blender_image.h - blender_object_cull.h - blender_sync.h - blender_session.h - blender_texture.h - blender_util.h - blender_viewport.h + device.h + display_driver.h + id_map.h + image.h + object_cull.h + output_driver.h + sync.h + session.h + texture.h + util.h + viewport.h ) set(LIB @@ -65,7 +69,8 @@ set(LIB cycles_device cycles_graph cycles_kernel - cycles_render + cycles_scene + cycles_session cycles_subd cycles_util @@ -93,14 +98,9 @@ set(ADDON_FILES add_definitions(${GL_DEFINITIONS}) -if(WITH_CYCLES_DEVICE_OPENCL) - add_definitions(-DWITH_OPENCL) +if(WITH_CYCLES_DEVICE_HIP) + add_definitions(-DWITH_HIP) endif() - -if(WITH_CYCLES_NETWORK) - add_definitions(-DWITH_NETWORK) -endif() - if(WITH_MOD_FLUID) add_definitions(-DWITH_FLUID) endif() @@ -138,11 +138,6 @@ endif() blender_add_lib(bf_intern_cycles "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") -# avoid link failure with clang 3.4 debug -if(CMAKE_C_COMPILER_ID MATCHES "Clang" AND NOT ${CMAKE_C_COMPILER_VERSION} VERSION_LESS '3.4') - string(APPEND CMAKE_CXX_FLAGS_DEBUG " -gline-tables-only") -endif() - add_dependencies(bf_intern_cycles bf_rna) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${ADDON_FILES}" ${CYCLES_INSTALL_PATH}) diff --git a/intern/cycles/blender/addon/__init__.py b/intern/cycles/blender/addon/__init__.py index f728050a3cf..1ce25a253f9 100644 --- a/intern/cycles/blender/addon/__init__.py +++ b/intern/cycles/blender/addon/__init__.py @@ -58,7 +58,6 @@ class CyclesRender(bpy.types.RenderEngine): bl_use_eevee_viewport = True bl_use_preview = True bl_use_exclude_layers = True - bl_use_save_buffers = True bl_use_spherical_stereo = True bl_use_custom_freestyle = True bl_use_alembic_procedural = True @@ -85,6 +84,12 @@ class CyclesRender(bpy.types.RenderEngine): def render(self, depsgraph): engine.render(self, depsgraph) + def render_frame_finish(self): + engine.render_frame_finish(self) + + def draw(self, context, depsgraph): + engine.draw(self, depsgraph, context.space_data) + def bake(self, depsgraph, obj, pass_type, pass_filter, width, height): engine.bake(self, depsgraph, obj, pass_type, pass_filter, width, height) @@ -98,7 +103,7 @@ class CyclesRender(bpy.types.RenderEngine): engine.sync(self, depsgraph, context.blend_data) def view_draw(self, context, depsgraph): - engine.draw(self, depsgraph, context.region, context.space_data, context.region_data) + engine.view_draw(self, depsgraph, context.region, context.space_data, context.region_data) def update_script_node(self, node): if engine.with_osl(): diff --git a/intern/cycles/blender/addon/engine.py b/intern/cycles/blender/addon/engine.py index 489a883f098..e5bb77a834a 100644 --- a/intern/cycles/blender/addon/engine.py +++ b/intern/cycles/blender/addon/engine.py @@ -18,62 +18,17 @@ from __future__ import annotations -def _is_using_buggy_driver(): - import gpu - # We need to be conservative here because in multi-GPU systems display card - # might be quite old, but others one might be just good. - # - # So We shouldn't disable possible good dedicated cards just because display - # card seems weak. And instead we only blacklist configurations which are - # proven to cause problems. - if gpu.platform.vendor_get() == "ATI Technologies Inc.": - import re - version = gpu.platform.version_get() - if version.endswith("Compatibility Profile Context"): - # Old HD 4xxx and 5xxx series drivers did not have driver version - # in the version string, but those cards do not quite work and - # causing crashes. - return True - regex = re.compile(".*Compatibility Profile Context ([0-9]+(\\.[0-9]+)+)$") - if not regex.match(version): - # Skip cards like FireGL - return False - version = regex.sub("\\1", version).split('.') - return int(version[0]) == 8 - return False - - -def _workaround_buggy_drivers(): - if _is_using_buggy_driver(): - import _cycles - if hasattr(_cycles, "opencl_disable"): - print("Cycles: OpenGL driver known to be buggy, disabling OpenCL platform.") - _cycles.opencl_disable() - - def _configure_argument_parser(): import argparse # No help because it conflicts with general Python scripts argument parsing parser = argparse.ArgumentParser(description="Cycles Addon argument parser", add_help=False) - parser.add_argument("--cycles-resumable-num-chunks", - help="Number of chunks to split sample range into", - default=None) - parser.add_argument("--cycles-resumable-current-chunk", - help="Current chunk of samples range to render", - default=None) - parser.add_argument("--cycles-resumable-start-chunk", - help="Start chunk to render", - default=None) - parser.add_argument("--cycles-resumable-end-chunk", - help="End chunk to render", - default=None) parser.add_argument("--cycles-print-stats", help="Print rendering statistics to stderr", action='store_true') parser.add_argument("--cycles-device", help="Set the device to use for Cycles, overriding user preferences and the scene setting." - "Valid options are 'CPU', 'CUDA', 'OPTIX' or 'OPENCL'." + "Valid options are 'CPU', 'CUDA', 'OPTIX', or 'HIP'" "Additionally, you can append '+CPU' to any GPU type for hybrid rendering.", default=None) return parser @@ -89,21 +44,6 @@ def _parse_command_line(): parser = _configure_argument_parser() args, _ = parser.parse_known_args(argv[argv.index("--") + 1:]) - if args.cycles_resumable_num_chunks is not None: - if args.cycles_resumable_current_chunk is not None: - import _cycles - _cycles.set_resumable_chunk( - int(args.cycles_resumable_num_chunks), - int(args.cycles_resumable_current_chunk), - ) - elif args.cycles_resumable_start_chunk is not None and \ - args.cycles_resumable_end_chunk: - import _cycles - _cycles.set_resumable_chunk_range( - int(args.cycles_resumable_num_chunks), - int(args.cycles_resumable_start_chunk), - int(args.cycles_resumable_end_chunk), - ) if args.cycles_print_stats: import _cycles _cycles.enable_print_stats() @@ -118,23 +58,11 @@ def init(): import _cycles import os.path - # Workaround possibly buggy legacy drivers which crashes on the OpenCL - # device enumeration. - # - # This checks are not really correct because they might still fail - # in the case of multiple GPUs. However, currently buggy drivers - # are really old and likely to be used in single GPU systems only - # anyway. - # - # Can't do it in the background mode, so we hope OpenCL is no enabled - # in the user preferences. - if not bpy.app.background: - _workaround_buggy_drivers() - path = os.path.dirname(__file__) user_path = os.path.dirname(os.path.abspath(bpy.utils.user_resource('CONFIG', path=''))) + temp_path = bpy.app.tempdir - _cycles.init(path, user_path, bpy.app.background) + _cycles.init(path, user_path, temp_path, bpy.app.background) _parse_command_line() @@ -177,6 +105,25 @@ def render(engine, depsgraph): _cycles.render(engine.session, depsgraph.as_pointer()) +def render_frame_finish(engine): + if not engine.session: + return + + import _cycles + _cycles.render_frame_finish(engine.session) + +def draw(engine, depsgraph, space_image): + if not engine.session: + return + + depsgraph_ptr = depsgraph.as_pointer() + space_image_ptr = space_image.as_pointer() + screen_ptr = space_image.id_data.as_pointer() + + import _cycles + _cycles.draw(engine.session, depsgraph_ptr, screen_ptr, space_image_ptr) + + def bake(engine, depsgraph, obj, pass_type, pass_filter, width, height): import _cycles session = getattr(engine, "session", None) @@ -204,14 +151,14 @@ def sync(engine, depsgraph, data): _cycles.sync(engine.session, depsgraph.as_pointer()) -def draw(engine, depsgraph, region, v3d, rv3d): +def view_draw(engine, depsgraph, region, v3d, rv3d): import _cycles depsgraph = depsgraph.as_pointer() v3d = v3d.as_pointer() rv3d = rv3d.as_pointer() # draw render image - _cycles.draw(engine.session, depsgraph, v3d, rv3d) + _cycles.view_draw(engine.session, depsgraph, v3d, rv3d) def available_devices(): @@ -224,11 +171,6 @@ def with_osl(): return _cycles.with_osl -def with_network(): - import _cycles - return _cycles.with_network - - def system_info(): import _cycles return _cycles.system_info() @@ -243,6 +185,7 @@ def list_render_passes(scene, srl): # Data passes. if srl.use_pass_z: yield ("Depth", "Z", 'VALUE') if srl.use_pass_mist: yield ("Mist", "Z", 'VALUE') + if srl.use_pass_position: yield ("Position", "XYZ", 'VECTOR') if srl.use_pass_normal: yield ("Normal", "XYZ", 'VECTOR') if srl.use_pass_vector: yield ("Vector", "XYZW", 'VECTOR') if srl.use_pass_uv: yield ("UV", "UVA", 'VECTOR') @@ -265,9 +208,9 @@ def list_render_passes(scene, srl): if srl.use_pass_environment: yield ("Env", "RGB", 'COLOR') if srl.use_pass_shadow: yield ("Shadow", "RGB", 'COLOR') if srl.use_pass_ambient_occlusion: yield ("AO", "RGB", 'COLOR') + if crl.use_pass_shadow_catcher: yield ("Shadow Catcher", "RGB", 'COLOR') # Debug passes. - if crl.pass_debug_render_time: yield ("Debug Render Time", "X", 'VALUE') if crl.pass_debug_sample_count: yield ("Debug Sample Count", "X", 'VALUE') # Cryptomatte passes. @@ -283,30 +226,21 @@ def list_render_passes(scene, srl): yield ("CryptoAsset" + '{:02d}'.format(i), "RGBA", 'COLOR') # Denoising passes. - if (scene.cycles.use_denoising and crl.use_denoising) or crl.denoising_store_passes: + if scene.cycles.use_denoising and crl.use_denoising: yield ("Noisy Image", "RGBA", 'COLOR') - if crl.denoising_store_passes: - yield ("Denoising Normal", "XYZ", 'VECTOR') - yield ("Denoising Albedo", "RGB", 'COLOR') - yield ("Denoising Depth", "Z", 'VALUE') - - if scene.cycles.denoiser == 'NLM': - yield ("Denoising Shadowing", "X", 'VALUE') - yield ("Denoising Variance", "RGB", 'COLOR') - yield ("Denoising Intensity", "X", 'VALUE') - - clean_options = ("denoising_diffuse_direct", "denoising_diffuse_indirect", - "denoising_glossy_direct", "denoising_glossy_indirect", - "denoising_transmission_direct", "denoising_transmission_indirect") - if any(getattr(crl, option) for option in clean_options): - yield ("Denoising Clean", "RGB", 'COLOR') + if crl.use_pass_shadow_catcher: + yield ("Noisy Shadow Catcher", "RGBA", 'COLOR') + if crl.denoising_store_passes: + yield ("Denoising Normal", "XYZ", 'VECTOR') + yield ("Denoising Albedo", "RGB", 'COLOR') + yield ("Denoising Depth", "Z", 'VALUE') # Custom AOV passes. for aov in srl.aovs: if aov.type == 'VALUE': yield (aov.name, "X", 'VALUE') else: - yield (aov.name, "RGBA", 'COLOR') + yield (aov.name, "RGB", 'COLOR') def register_passes(engine, scene, view_layer): diff --git a/intern/cycles/blender/addon/presets.py b/intern/cycles/blender/addon/presets.py index bf33e5dc010..50093438bda 100644 --- a/intern/cycles/blender/addon/presets.py +++ b/intern/cycles/blender/addon/presets.py @@ -40,10 +40,10 @@ class AddPresetIntegrator(AddPresetBase, Operator): "cycles.transparent_max_bounces", "cycles.caustics_reflective", "cycles.caustics_refractive", - "cycles.blur_glossy" - "cycles.use_fast_gi" - "cycles.ao_bounces" - "cycles.ao_bounces_render" + "cycles.blur_glossy", + "cycles.use_fast_gi", + "cycles.ao_bounces", + "cycles.ao_bounces_render", ] preset_subdir = "cycles/integrator" @@ -60,32 +60,48 @@ class AddPresetSampling(AddPresetBase, Operator): ] preset_values = [ + "cycles.use_adaptive_sampling", "cycles.samples", - "cycles.preview_samples", - "cycles.aa_samples", - "cycles.preview_aa_samples", - "cycles.diffuse_samples", - "cycles.glossy_samples", - "cycles.transmission_samples", - "cycles.ao_samples", - "cycles.mesh_light_samples", - "cycles.subsurface_samples", - "cycles.volume_samples", - "cycles.use_square_samples", - "cycles.progressive", - "cycles.seed", - "cycles.sample_clamp_direct", - "cycles.sample_clamp_indirect", - "cycles.sample_all_lights_direct", - "cycles.sample_all_lights_indirect", + "cycles.adaptive_threshold", + "cycles.adaptive_min_samples", + "cycles.time_limit", + "cycles.use_denoising", + "cycles.denoiser", + "cycles.denoising_input_passes", + "cycles.denoising_prefilter", ] preset_subdir = "cycles/sampling" +class AddPresetViewportSampling(AddPresetBase, Operator): + '''Add a Viewport Sampling Preset''' + bl_idname = "render.cycles_viewport_sampling_preset_add" + bl_label = "Add Viewport Sampling Preset" + preset_menu = "CYCLES_PT_viewport_sampling_presets" + + preset_defines = [ + "cycles = bpy.context.scene.cycles" + ] + + preset_values = [ + "cycles.use_preview_adaptive_sampling", + "cycles.preview_samples", + "cycles.preview_adaptive_threshold", + "cycles.preview_adaptive_min_samples", + "cycles.use_preview_denoising", + "cycles.preview_denoiser", + "cycles.preview_denoising_input_passes", + "cycles.preview_denoising_prefilter", + "cycles.preview_denoising_start_sample", + ] + + preset_subdir = "cycles/viewport_sampling" + classes = ( AddPresetIntegrator, AddPresetSampling, + AddPresetViewportSampling, ) diff --git a/intern/cycles/blender/addon/properties.py b/intern/cycles/blender/addon/properties.py index 0c3af3fabeb..3c90ea07cc6 100644 --- a/intern/cycles/blender/addon/properties.py +++ b/intern/cycles/blender/addon/properties.py @@ -39,11 +39,6 @@ enum_devices = ( ('GPU', "GPU Compute", "Use GPU compute device for rendering, configured in the system tab in the user preferences"), ) -from _cycles import with_network -if with_network: - enum_devices += (('NETWORK', "Networked Device", "Use networked device for rendering"),) -del with_network - enum_feature_set = ( ('SUPPORTED', "Supported", "Only use finished and supported features"), ('EXPERIMENTAL', "Experimental", "Use experimental and incomplete features that might be broken or change in the future", 'ERROR', 1), @@ -84,15 +79,6 @@ enum_curve_shape = ( ('THICK', "3D Curves", "Render hair as 3D curve, for accurate results when viewing hair close up"), ) -enum_tile_order = ( - ('CENTER', "Center", "Render from center to the edges"), - ('RIGHT_TO_LEFT', "Right to Left", "Render from right to left"), - ('LEFT_TO_RIGHT', "Left to Right", "Render from left to right"), - ('TOP_TO_BOTTOM', "Top to Bottom", "Render from top to bottom"), - ('BOTTOM_TO_TOP', "Bottom to Top", "Render from bottom to top"), - ('HILBERT_SPIRAL', "Hilbert Spiral", "Render in a Hilbert Spiral"), -) - enum_use_layer_samples = ( ('USE', "Use", "Per render layer number of samples override scene samples"), ('BOUNDED', "Bounded", "Bound per render layer number of samples by global samples"), @@ -100,14 +86,8 @@ enum_use_layer_samples = ( ) enum_sampling_pattern = ( - ('SOBOL', "Sobol", "Use Sobol random sampling pattern"), - ('CORRELATED_MUTI_JITTER', "Correlated Multi-Jitter", "Use Correlated Multi-Jitter random sampling pattern"), - ('PROGRESSIVE_MUTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern"), -) - -enum_integrator = ( - ('BRANCHED_PATH', "Branched Path Tracing", "Path tracing integrator that branches on the first bounce, giving more control over the number of light and material samples"), - ('PATH', "Path Tracing", "Pure path tracing integrator"), + ('SOBOL', "Sobol", "Use Sobol random sampling pattern", 0), + ('PROGRESSIVE_MULTI_JITTER', "Progressive Multi-Jitter", "Use Progressive Multi-Jitter random sampling pattern", 1), ) enum_volume_sampling = ( @@ -131,7 +111,7 @@ enum_device_type = ( ('CPU', "CPU", "CPU", 0), ('CUDA', "CUDA", "CUDA", 1), ('OPTIX', "OptiX", "OptiX", 3), - ('OPENCL', "OpenCL", "OpenCL", 2) + ("HIP", "HIP", "HIP", 4) ) enum_texture_limit = ( @@ -145,38 +125,50 @@ enum_texture_limit = ( ('8192', "8192", "Limit texture size to 8192 pixels", 7), ) +enum_fast_gi_method = ( + ('REPLACE', "Replace", "Replace global illumination with ambient occlusion after a specified number of bounces"), + ('ADD', "Add", "Add ambient occlusion to diffuse surfaces"), +) + +# NOTE: Identifiers are expected to be an upper case version of identifiers from `Pass::get_type_enum()` enum_view3d_shading_render_pass = ( ('', "General", ""), - ('COMBINED', "Combined", "Show the Combined Render pass", 1), - ('EMISSION', "Emission", "Show the Emission render pass", 33), - ('BACKGROUND', "Background", "Show the Background render pass", 34), - ('AO', "Ambient Occlusion", "Show the Ambient Occlusion render pass", 35), + ('COMBINED', "Combined", "Show the Combined Render pass"), + ('EMISSION', "Emission", "Show the Emission render pass"), + ('BACKGROUND', "Background", "Show the Background render pass"), + ('AO', "Ambient Occlusion", "Show the Ambient Occlusion render pass"), + ('SHADOW', "Shadow", "Show the Shadow render pass"), + ('SHADOW_CATCHER', "Shadow Catcher", "Show the Shadow Catcher render pass"), ('', "Light", ""), - ('DIFFUSE_DIRECT', "Diffuse Direct", "Show the Diffuse Direct render pass", 38), - ('DIFFUSE_INDIRECT', "Diffuse Indirect", "Show the Diffuse Indirect render pass", 39), - ('DIFFUSE_COLOR', "Diffuse Color", "Show the Diffuse Color render pass", 40), + ('DIFFUSE_DIRECT', "Diffuse Direct", "Show the Diffuse Direct render pass"), + ('DIFFUSE_INDIRECT', "Diffuse Indirect", "Show the Diffuse Indirect render pass"), + ('DIFFUSE_COLOR', "Diffuse Color", "Show the Diffuse Color render pass"), - ('GLOSSY_DIRECT', "Glossy Direct", "Show the Glossy Direct render pass", 41), - ('GLOSSY_INDIRECT', "Glossy Indirect", "Show the Glossy Indirect render pass", 42), - ('GLOSSY_COLOR', "Glossy Color", "Show the Glossy Color render pass", 43), + ('GLOSSY_DIRECT', "Glossy Direct", "Show the Glossy Direct render pass"), + ('GLOSSY_INDIRECT', "Glossy Indirect", "Show the Glossy Indirect render pass"), + ('GLOSSY_COLOR', "Glossy Color", "Show the Glossy Color render pass"), ('', "", ""), - ('TRANSMISSION_DIRECT', "Transmission Direct", "Show the Transmission Direct render pass", 44), - ('TRANSMISSION_INDIRECT', "Transmission Indirect", "Show the Transmission Indirect render pass", 45), - ('TRANSMISSION_COLOR', "Transmission Color", "Show the Transmission Color render pass", 46), + ('TRANSMISSION_DIRECT', "Transmission Direct", "Show the Transmission Direct render pass"), + ('TRANSMISSION_INDIRECT', "Transmission Indirect", "Show the Transmission Indirect render pass"), + ('TRANSMISSION_COLOR', "Transmission Color", "Show the Transmission Color render pass"), - ('VOLUME_DIRECT', "Volume Direct", "Show the Volume Direct render pass", 50), - ('VOLUME_INDIRECT', "Volume Indirect", "Show the Volume Indirect render pass", 51), + ('VOLUME_DIRECT', "Volume Direct", "Show the Volume Direct render pass"), + ('VOLUME_INDIRECT', "Volume Indirect", "Show the Volume Indirect render pass"), ('', "Data", ""), - ('NORMAL', "Normal", "Show the Normal render pass", 3), - ('UV', "UV", "Show the UV render pass", 4), - ('MIST', "Mist", "Show the Mist render pass", 32), + ('POSITION', "Position", "Show the Position render pass"), + ('NORMAL', "Normal", "Show the Normal render pass"), + ('UV', "UV", "Show the UV render pass"), + ('MIST', "Mist", "Show the Mist render pass"), + ('DENOISING_ALBEDO', "Denoising Albedo", "Albedo pass used by denoiser"), + ('DENOISING_NORMAL', "Denoising Normal", "Normal pass used by denoiser"), + ('SAMPLE_COUNT', "Sample Count", "Per-pixel number of samples"), ) @@ -208,18 +200,23 @@ def enum_preview_denoiser(self, context): def enum_denoiser(self, context): - items = [('NLM', "NLM", "Cycles native non-local means denoiser, running on any compute device", 1)] + items = [] items += enum_optix_denoiser(self, context) items += enum_openimagedenoise_denoiser(self, context) return items enum_denoising_input_passes = ( - ('RGB', "Color", "Use only color as input", 1), - ('RGB_ALBEDO', "Color + Albedo", "Use color and albedo data as input", 2), - ('RGB_ALBEDO_NORMAL', "Color + Albedo + Normal", "Use color, albedo and normal data as input", 3), + ('RGB', "None", "Don't use utility passes for denoising", 1), + ('RGB_ALBEDO', "Albedo", "Use albedo pass for denoising", 2), + ('RGB_ALBEDO_NORMAL', "Albedo and Normal", "Use albedo and normal passes for denoising", 3), ) +enum_denoising_prefilter = ( + ('NONE', "None", "No prefiltering, use when guiding passes are noise-free", 1), + ('FAST', "Fast", "Denoise color and guiding passes together. Improves quality when guiding passes are noisy using least amount of extra processing time", 2), + ('ACCURATE', "Accurate", "Prefilter noisy guiding passes before denoising color. Improves quality when guiding passes are noisy using extra processing time", 3), +) def update_render_passes(self, context): scene = context.scene @@ -252,13 +249,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Use Open Shading Language (CPU rendering only)", ) - progressive: EnumProperty( - name="Integrator", - description="Method to sample lights and materials", - items=enum_integrator, - default='PATH', - ) - preview_pause: BoolProperty( name="Pause Preview", description="Pause all viewport preview renders", @@ -268,110 +258,106 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): use_denoising: BoolProperty( name="Use Denoising", description="Denoise the rendered image", - default=False, + default=True, update=update_render_passes, ) + denoiser: EnumProperty( + name="Denoiser", + description="Denoise the image with the selected denoiser. " + "For denoising the image after rendering", + items=enum_denoiser, + default=4, # Use integer to avoid error in builds without OpenImageDenoise. + update=update_render_passes, + ) + denoising_prefilter: EnumProperty( + name="Denoising Prefilter", + description="Prefilter noisy guiding (albedo and normal) passes to improve denoising quality when using OpenImageDenoiser", + items=enum_denoising_prefilter, + default='ACCURATE', + ) + denoising_input_passes: EnumProperty( + name="Denoising Input Passes", + description="Passes used by the denoiser to distinguish noise from shader and geometry detail", + items=enum_denoising_input_passes, + default='RGB_ALBEDO_NORMAL', + ) + use_preview_denoising: BoolProperty( name="Use Viewport Denoising", description="Denoise the image in the 3D viewport", default=False, ) - - denoiser: EnumProperty( - name="Denoiser", - description="Denoise the image with the selected denoiser. " - "For denoising the image after rendering, denoising data render passes " - "also adapt to the selected denoiser", - items=enum_denoiser, - default=1, - update=update_render_passes, - ) preview_denoiser: EnumProperty( name="Viewport Denoiser", description="Denoise the image after each preview update with the selected denoiser", items=enum_preview_denoiser, default=0, ) - - use_square_samples: BoolProperty( - name="Square Samples", - description="Square sampling values for easier artist control", - default=False, + preview_denoising_prefilter: EnumProperty( + name="Viewport Denoising Prefilter", + description="Prefilter noisy guiding (albedo and normal) passes to improve denoising quality when using OpenImageDenoiser", + items=enum_denoising_prefilter, + default='FAST', + ) + preview_denoising_input_passes: EnumProperty( + name="Viewport Denoising Input Passes", + description="Passes used by the denoiser to distinguish noise from shader and geometry detail", + items=enum_denoising_input_passes, + default='RGB_ALBEDO', + ) + preview_denoising_start_sample: IntProperty( + name="Start Denoising", + description="Sample to start denoising the preview at", + min=0, max=(1 << 24), + default=1, ) samples: IntProperty( name="Samples", description="Number of samples to render for each pixel", min=1, max=(1 << 24), - default=128, + default=4096, ) preview_samples: IntProperty( name="Viewport Samples", description="Number of samples to render in the viewport, unlimited if 0", min=0, max=(1 << 24), - default=32, - ) - aa_samples: IntProperty( - name="AA Samples", - description="Number of antialiasing samples to render for each pixel", - min=1, max=2097151, - default=128, - ) - preview_aa_samples: IntProperty( - name="AA Samples", - description="Number of antialiasing samples to render in the viewport, unlimited if 0", - min=0, max=2097151, - default=32, + default=1024, ) - diffuse_samples: IntProperty( - name="Diffuse Samples", - description="Number of diffuse bounce samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - glossy_samples: IntProperty( - name="Glossy Samples", - description="Number of glossy bounce samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - transmission_samples: IntProperty( - name="Transmission Samples", - description="Number of transmission bounce samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - ao_samples: IntProperty( - name="Ambient Occlusion Samples", - description="Number of ambient occlusion samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - mesh_light_samples: IntProperty( - name="Mesh Light Samples", - description="Number of mesh emission light samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - subsurface_samples: IntProperty( - name="Subsurface Samples", - description="Number of subsurface scattering samples to render for each AA sample", - min=1, max=1024, - default=1, - ) - volume_samples: IntProperty( - name="Volume Samples", - description="Number of volume scattering samples to render for each AA sample", - min=1, max=1024, - default=1, + time_limit: FloatProperty( + name="Time Limit", + description="Limit the render time (excluding synchronization time)." + "Zero disables the limit", + min=0.0, + default=0.0, + step=100.0, + unit='TIME_ABSOLUTE', ) sampling_pattern: EnumProperty( name="Sampling Pattern", - description="Random sampling pattern used by the integrator", + description="Random sampling pattern used by the integrator. When adaptive sampling is enabled, Progressive Multi-Jitter is always used instead of Sobol", items=enum_sampling_pattern, - default='SOBOL', + default='PROGRESSIVE_MULTI_JITTER', + ) + + scrambling_distance: FloatProperty( + name="Scrambling Distance", + default=1.0, + min=0.0, max=1.0, + description="Lower values give faster rendering with GPU rendering and less noise with all devices at the cost of possible artifacts if set too low. Only works when not using adaptive sampling", + ) + preview_scrambling_distance: BoolProperty( + name="Scrambling Distance viewport", + default=False, + description="Uses the Scrambling Distance value for the viewport. Faster but may flicker", + ) + + adaptive_scrambling_distance: BoolProperty( + name="Adaptive Scrambling Distance", + default=False, + description="Uses a formula to adapt the scrambling distance strength based on the sample count", ) use_layer_samples: EnumProperty( @@ -381,17 +367,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default='USE', ) - sample_all_lights_direct: BoolProperty( - name="Sample All Direct Lights", - description="Sample all lights (for direct samples), rather than randomly picking one", - default=True, - ) - - sample_all_lights_indirect: BoolProperty( - name="Sample All Indirect Lights", - description="Sample all lights (for indirect samples), rather than randomly picking one", - default=True, - ) light_sampling_threshold: FloatProperty( name="Light Sampling Threshold", description="Probabilistically terminate light samples when the light contribution is below this threshold (more noise but faster rendering). " @@ -403,19 +378,39 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): use_adaptive_sampling: BoolProperty( name="Use Adaptive Sampling", description="Automatically reduce the number of samples per pixel based on estimated noise level", - default=False, + default=True, ) - adaptive_threshold: FloatProperty( name="Adaptive Sampling Threshold", description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples", min=0.0, max=1.0, - default=0.0, + soft_min=0.001, + default=0.01, precision=4, ) adaptive_min_samples: IntProperty( name="Adaptive Min Samples", - description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on number of AA samples", + description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on noise threshold", + min=0, max=4096, + default=0, + ) + + use_preview_adaptive_sampling: BoolProperty( + name="Use Adaptive Sampling", + description="Automatically reduce the number of samples per pixel based on estimated noise level, for viewport renders", + default=True, + ) + preview_adaptive_threshold: FloatProperty( + name="Adaptive Sampling Threshold", + description="Noise level step to stop sampling at, lower values reduce noise at the cost of render time. Zero for automatic setting based on number of AA samples, for viewport renders", + min=0.0, max=1.0, + soft_min=0.001, + default=0.1, + precision=4, + ) + preview_adaptive_min_samples: IntProperty( + name="Adaptive Min Samples", + description="Minimum AA samples for adaptive sampling, to discover noisy features before stopping sampling. Zero for automatic setting based on noise threshold, for viewport renders", min=0, max=4096, default=0, ) @@ -632,53 +627,6 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default=10.0, ) - debug_tile_size: IntProperty( - name="Tile Size", - description="", - min=1, max=4096, - default=1024, - ) - - preview_start_resolution: IntProperty( - name="Start Resolution", - description="Resolution to start rendering preview at, " - "progressively increasing it to the full viewport size", - min=8, max=16384, - default=64, - subtype='PIXEL' - ) - preview_denoising_start_sample: IntProperty( - name="Start Denoising", - description="Sample to start denoising the preview at", - min=0, max=(1 << 24), - default=1, - ) - preview_denoising_input_passes: EnumProperty( - name="Viewport Input Passes", - description="Passes used by the denoiser to distinguish noise from shader and geometry detail", - items=enum_denoising_input_passes, - default='RGB_ALBEDO', - ) - - debug_reset_timeout: FloatProperty( - name="Reset timeout", - description="", - min=0.01, max=10.0, - default=0.1, - ) - debug_cancel_timeout: FloatProperty( - name="Cancel timeout", - description="", - min=0.01, max=10.0, - default=0.1, - ) - debug_text_timeout: FloatProperty( - name="Text timeout", - description="", - min=0.01, max=10.0, - default=1.0, - ) - debug_bvh_type: EnumProperty( name="Viewport BVH Type", description="Choose between faster updates, or faster render", @@ -701,38 +649,24 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): default=0, min=0, max=16, ) - tile_order: EnumProperty( - name="Tile Order", - description="Tile order for rendering", - items=enum_tile_order, - default='HILBERT_SPIRAL', - options=set(), # Not animatable! - ) - use_progressive_refine: BoolProperty( - name="Progressive Refine", - description="Instead of rendering each tile until it is finished, " - "refine the whole image progressively " - "(this renders somewhat slower, " - "but time can be saved by manually stopping the render when the noise is low enough)", - default=False, - ) bake_type: EnumProperty( name="Bake Type", default='COMBINED', description="Type of pass to bake", items=( - ('COMBINED', "Combined", ""), - ('AO', "Ambient Occlusion", ""), - ('SHADOW', "Shadow", ""), - ('NORMAL', "Normal", ""), - ('UV', "UV", ""), - ('ROUGHNESS', "Roughness", ""), - ('EMIT', "Emit", ""), - ('ENVIRONMENT', "Environment", ""), - ('DIFFUSE', "Diffuse", ""), - ('GLOSSY', "Glossy", ""), - ('TRANSMISSION', "Transmission", ""), + ('COMBINED', "Combined", "", 0), + ('AO', "Ambient Occlusion", "", 1), + ('SHADOW', "Shadow", "", 2), + ('POSITION', "Position", "", 11), + ('NORMAL', "Normal", "", 3), + ('UV', "UV", "", 4), + ('ROUGHNESS', "Roughness", "", 5), + ('EMIT', "Emit", "", 6), + ('ENVIRONMENT', "Environment", "", 7), + ('DIFFUSE', "Diffuse", "", 8), + ('GLOSSY', "Glossy", "", 9), + ('TRANSMISSION', "Transmission", "", 10), ), ) @@ -813,6 +747,14 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): description="Approximate diffuse indirect light with background tinted ambient occlusion. This provides fast alternative to full global illumination, for interactive viewport rendering or final renders with reduced quality", default=False, ) + + fast_gi_method: EnumProperty( + name="Fast GI Method", + default='REPLACE', + description="Fast GI approximation method", + items=enum_fast_gi_method + ) + ao_bounces: IntProperty( name="AO Bounces", default=1, @@ -827,6 +769,18 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): min=0, max=1024, ) + use_auto_tile: BoolProperty( + name="Auto Tiles", + description="Automatically render high resolution images in tiles to reduce memory usage, using the specified tile size. Tiles are cached to disk while rendering to save memory", + default=True, + ) + tile_size: IntProperty( + name="Tile Size", + default=2048, + description="", + min=8, max=16384, + ) + # Various fine-tuning debug flags def _devices_update_callback(self, context): @@ -844,45 +798,13 @@ class CyclesRenderSettings(bpy.types.PropertyGroup): items=enum_bvh_layouts, default='EMBREE', ) - debug_use_cpu_split_kernel: BoolProperty(name="Split Kernel", default=False) debug_use_cuda_adaptive_compile: BoolProperty(name="Adaptive Compile", default=False) - debug_use_cuda_split_kernel: BoolProperty(name="Split Kernel", default=False) - debug_optix_cuda_streams: IntProperty(name="CUDA Streams", default=1, min=1) - debug_optix_curves_api: BoolProperty(name="Native OptiX Curve Primitive", default=False) - - debug_opencl_kernel_type: EnumProperty( - name="OpenCL Kernel Type", - default='DEFAULT', - items=( - ('DEFAULT', "Default", ""), - ('MEGA', "Mega", ""), - ('SPLIT', "Split", ""), - ), - update=CyclesRenderSettings._devices_update_callback - ) - - debug_opencl_device_type: EnumProperty( - name="OpenCL Device Type", - default='ALL', - items=( - ('NONE', "None", ""), - ('ALL', "All", ""), - ('DEFAULT', "Default", ""), - ('CPU', "CPU", ""), - ('GPU', "GPU", ""), - ('ACCELERATOR', "Accelerator", ""), - ), - update=CyclesRenderSettings._devices_update_callback - ) - - debug_use_opencl_debug: BoolProperty(name="Debug OpenCL", default=False) - - debug_opencl_mem_limit: IntProperty( - name="Memory limit", - default=0, - description="Artificial limit on OpenCL memory usage in MB (0 to disable limit)" + debug_use_optix_debug: BoolProperty( + name="OptiX Module Debug", + description="Load OptiX module in debug mode: lower logging verbosity level, enable validations, and lower optimization level", + default=False ) @classmethod @@ -1031,12 +953,6 @@ class CyclesLightSettings(bpy.types.PropertyGroup): description="Light casts shadows", default=True, ) - samples: IntProperty( - name="Samples", - description="Number of light samples to render for each AA sample", - min=1, max=10000, - default=1, - ) max_bounces: IntProperty( name="Max Bounces", description="Maximum number of bounces the light will contribute to the render", @@ -1084,12 +1000,6 @@ class CyclesWorldSettings(bpy.types.PropertyGroup): min=4, max=8192, default=1024, ) - samples: IntProperty( - name="Samples", - description="Number of light samples to render for each AA sample", - min=1, max=10000, - default=1, - ) max_bounces: IntProperty( name="Max Bounces", description="Maximum number of bounces the background light will contribute to the render", @@ -1318,12 +1228,6 @@ class CyclesCurveRenderSettings(bpy.types.PropertyGroup): class CyclesRenderLayerSettings(bpy.types.PropertyGroup): - pass_debug_render_time: BoolProperty( - name="Debug Render Time", - description="Render time in milliseconds per sample and pixel", - default=False, - update=update_render_passes, - ) pass_debug_sample_count: BoolProperty( name="Debug Sample Count", description="Number of samples/camera rays per pixel", @@ -1343,91 +1247,25 @@ class CyclesRenderLayerSettings(bpy.types.PropertyGroup): update=update_render_passes, ) + use_pass_shadow_catcher: BoolProperty( + name="Shadow Catcher", + description="Pass containing shadows and light which is to be multiplied into backdrop", + default=False, + update=update_render_passes, + ) + use_denoising: BoolProperty( name="Use Denoising", description="Denoise the rendered image", default=True, update=update_render_passes, ) - denoising_diffuse_direct: BoolProperty( - name="Diffuse Direct", - description="Denoise the direct diffuse lighting", - default=True, - ) - denoising_diffuse_indirect: BoolProperty( - name="Diffuse Indirect", - description="Denoise the indirect diffuse lighting", - default=True, - ) - denoising_glossy_direct: BoolProperty( - name="Glossy Direct", - description="Denoise the direct glossy lighting", - default=True, - ) - denoising_glossy_indirect: BoolProperty( - name="Glossy Indirect", - description="Denoise the indirect glossy lighting", - default=True, - ) - denoising_transmission_direct: BoolProperty( - name="Transmission Direct", - description="Denoise the direct transmission lighting", - default=True, - ) - denoising_transmission_indirect: BoolProperty( - name="Transmission Indirect", - description="Denoise the indirect transmission lighting", - default=True, - ) - denoising_strength: FloatProperty( - name="Denoising Strength", - description="Controls neighbor pixel weighting for the denoising filter (lower values preserve more detail, but aren't as smooth)", - min=0.0, max=1.0, - default=0.5, - ) - denoising_feature_strength: FloatProperty( - name="Denoising Feature Strength", - description="Controls removal of noisy image feature passes (lower values preserve more detail, but aren't as smooth)", - min=0.0, max=1.0, - default=0.5, - ) - denoising_radius: IntProperty( - name="Denoising Radius", - description="Size of the image area that's used to denoise a pixel (higher values are smoother, but might lose detail and are slower)", - min=1, max=25, - default=8, - subtype="PIXEL", - ) - denoising_relative_pca: BoolProperty( - name="Relative Filter", - description="When removing pixels that don't carry information, use a relative threshold instead of an absolute one (can help to reduce artifacts, but might cause detail loss around edges)", - default=False, - ) denoising_store_passes: BoolProperty( name="Store Denoising Passes", description="Store the denoising feature passes and the noisy image. The passes adapt to the denoiser selected for rendering", default=False, update=update_render_passes, ) - denoising_neighbor_frames: IntProperty( - name="Neighbor Frames", - description="Number of neighboring frames to use for denoising animations (more frames produce smoother results at the cost of performance)", - min=0, max=7, - default=0, - ) - - denoising_optix_input_passes: EnumProperty( - name="Input Passes", - description="Passes used by the denoiser to distinguish noise from shader and geometry detail", - items=enum_denoising_input_passes, - default='RGB_ALBEDO', - ) - denoising_openimagedenoise_input_passes: EnumProperty( - name="Input Passes", - description="Passes used by the denoiser to distinguish noise from shader and geometry detail", - items=enum_denoising_input_passes, - default='RGB_ALBEDO_NORMAL', - ) @classmethod def register(cls): @@ -1454,14 +1292,16 @@ class CyclesPreferences(bpy.types.AddonPreferences): def get_device_types(self, context): import _cycles - has_cuda, has_optix, has_opencl = _cycles.get_device_types() + has_cuda, has_optix, has_hip = _cycles.get_device_types() + list = [('NONE', "None", "Don't use compute device", 0)] if has_cuda: list.append(('CUDA', "CUDA", "Use CUDA for GPU acceleration", 1)) if has_optix: list.append(('OPTIX', "OptiX", "Use OptiX for GPU acceleration", 3)) - if has_opencl: - list.append(('OPENCL', "OpenCL", "Use OpenCL for GPU acceleration", 2)) + if has_hip: + list.append(('HIP', "HIP", "Use HIP for GPU acceleration", 4)) + return list compute_device_type: EnumProperty( @@ -1486,7 +1326,7 @@ class CyclesPreferences(bpy.types.AddonPreferences): def update_device_entries(self, device_list): for device in device_list: - if not device[1] in {'CUDA', 'OPTIX', 'OPENCL', 'CPU'}: + if not device[1] in {'CUDA', 'OPTIX', 'CPU', 'HIP'}: continue # Try to find existing Device entry entry = self.find_existing_device_entry(device) @@ -1520,29 +1360,36 @@ class CyclesPreferences(bpy.types.AddonPreferences): elif entry.type == 'CPU': cpu_devices.append(entry) # Extend all GPU devices with CPU. - if compute_device_type in {'CUDA', 'OPTIX', 'OPENCL'}: + if len(devices) and compute_device_type != 'CPU': devices.extend(cpu_devices) return devices - # For backwards compatibility, only returns CUDA and OpenCL but still - # refreshes all devices. - def get_devices(self, compute_device_type=''): + # Refresh device list. This does not happen automatically on Blender + # startup due to unstable OpenCL implementations that can cause crashes. + def refresh_devices(self): import _cycles # Ensure `self.devices` is not re-allocated when the second call to # get_devices_for_type is made, freeing items from the first list. - for device_type in ('CUDA', 'OPTIX', 'OPENCL'): + for device_type in ('CUDA', 'OPTIX', 'HIP'): self.update_device_entries(_cycles.available_devices(device_type)) - cuda_devices = self.get_devices_for_type('CUDA') - opencl_devices = self.get_devices_for_type('OPENCL') - return cuda_devices, opencl_devices + # Deprecated: use refresh_devices instead. + def get_devices(self, compute_device_type=''): + self.refresh_devices() + return None + + def get_compute_device_type(self): + if self.compute_device_type == '': + return 'NONE' + return self.compute_device_type def get_num_gpu_devices(self): import _cycles - device_list = _cycles.available_devices(self.compute_device_type) + compute_device_type = self.get_compute_device_type() + device_list = _cycles.available_devices(compute_device_type) num = 0 for device in device_list: - if device[1] != self.compute_device_type: + if device[1] != compute_device_type: continue for dev in self.devices: if dev.use and dev.id == device[2]: @@ -1563,8 +1410,18 @@ class CyclesPreferences(bpy.types.AddonPreferences): if not found_device: col = box.column(align=True) - col.label(text="No compatible GPUs found for path tracing", icon='INFO') - col.label(text="Cycles will render on the CPU", icon='BLANK1') + col.label(text="No compatible GPUs found for Cycles", icon='INFO') + + if device_type == 'CUDA': + col.label(text="Requires NVIDIA GPU with compute capability 3.0", icon='BLANK1') + elif device_type == 'OPTIX': + col.label(text="Requires NVIDIA GPU with compute capability 5.0", icon='BLANK1') + col.label(text="and NVIDIA driver version 470 or newer", icon='BLANK1') + elif device_type == 'HIP': + import sys + col.label(text="Requires discrete AMD GPU with ??? architecture", icon='BLANK1') + if sys.platform[:3] == "win": + col.label(text="and AMD driver version ??? or newer", icon='BLANK1') return for device in devices: @@ -1574,15 +1431,16 @@ class CyclesPreferences(bpy.types.AddonPreferences): row = layout.row() row.prop(self, "compute_device_type", expand=True) - if self.compute_device_type == 'NONE': + compute_device_type = self.get_compute_device_type() + if compute_device_type == 'NONE': return row = layout.row() - devices = self.get_devices_for_type(self.compute_device_type) - self._draw_devices(row, self.compute_device_type, devices) + devices = self.get_devices_for_type(compute_device_type) + self._draw_devices(row, compute_device_type, devices) import _cycles has_peer_memory = 0 - for device in _cycles.available_devices(self.compute_device_type): + for device in _cycles.available_devices(compute_device_type): if device[3] and self.find_existing_device_entry(device).use: has_peer_memory += 1 if has_peer_memory > 1: @@ -1601,6 +1459,10 @@ class CyclesView3DShadingSettings(bpy.types.PropertyGroup): items=enum_view3d_shading_render_pass, default='COMBINED', ) + show_active_pixels: BoolProperty( + name="Show Active Pixels", + description="When using adaptive sampling highlight pixels which are being sampled", + ) def register(): diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py index 47f7b4c6d73..0c9179b4ccf 100644 --- a/intern/cycles/blender/addon/ui.py +++ b/intern/cycles/blender/addon/ui.py @@ -26,21 +26,31 @@ from bpy.types import Panel from bl_ui.properties_grease_pencil_common import GreasePencilSimplifyPanel from bl_ui.properties_view_layer import ViewLayerCryptomattePanel, ViewLayerAOVPanel +class CyclesPresetPanel(PresetPanel, Panel): + COMPAT_ENGINES = {'CYCLES'} + preset_operator = "script.execute_preset" -class CYCLES_PT_sampling_presets(PresetPanel, Panel): + @staticmethod + def post_cb(context): + # Modify an arbitrary built-in scene property to force a depsgraph + # update, because add-on properties don't. (see T62325) + render = context.scene.render + render.filter_size = render.filter_size + +class CYCLES_PT_sampling_presets(CyclesPresetPanel): bl_label = "Sampling Presets" preset_subdir = "cycles/sampling" - preset_operator = "script.execute_preset" preset_add_operator = "render.cycles_sampling_preset_add" - COMPAT_ENGINES = {'CYCLES'} +class CYCLES_PT_viewport_sampling_presets(CyclesPresetPanel): + bl_label = "Viewport Sampling Presets" + preset_subdir = "cycles/viewport_sampling" + preset_add_operator = "render.cycles_viewport_sampling_preset_add" -class CYCLES_PT_integrator_presets(PresetPanel, Panel): +class CYCLES_PT_integrator_presets(CyclesPresetPanel): bl_label = "Integrator Presets" preset_subdir = "cycles/integrator" - preset_operator = "script.execute_preset" preset_add_operator = "render.cycles_integrator_preset_add" - COMPAT_ENGINES = {'CYCLES'} class CyclesButtonsPanel: @@ -54,6 +64,15 @@ class CyclesButtonsPanel: return context.engine in cls.COMPAT_ENGINES +class CyclesDebugButtonsPanel(CyclesButtonsPanel): + @classmethod + def poll(cls, context): + prefs = bpy.context.preferences + return (CyclesButtonsPanel.poll(context) + and prefs.experimental.use_cycles_debug + and prefs.view.show_developer_ui) + + # Adapt properties editor panel to display in node editor. We have to # copy the class rather than inherit due to the way bpy registration works. def node_panel(cls): @@ -78,30 +97,23 @@ def use_cpu(context): return (get_device_type(context) == 'NONE' or cscene.device == 'CPU') -def use_opencl(context): - cscene = context.scene.cycles - - return (get_device_type(context) == 'OPENCL' and cscene.device == 'GPU') - - def use_cuda(context): cscene = context.scene.cycles return (get_device_type(context) == 'CUDA' and cscene.device == 'GPU') +def use_hip(context): + cscene = context.scene.cycles + + return (get_device_type(context) == 'HIP' and cscene.device == 'GPU') + def use_optix(context): cscene = context.scene.cycles return (get_device_type(context) == 'OPTIX' and cscene.device == 'GPU') -def use_branched_path(context): - cscene = context.scene.cycles - - return (cscene.progressive == 'BRANCHED_PATH' and not use_optix(context)) - - def use_sample_all_lights(context): cscene = context.scene.cycles @@ -115,55 +127,93 @@ def show_device_active(context): return context.preferences.addons[__package__].preferences.has_active_device() -def draw_samples_info(layout, context): - cscene = context.scene.cycles - integrator = cscene.progressive +def get_effective_preview_denoiser(context): + scene = context.scene + cscene = scene.cycles - # Calculate sample values - if integrator == 'PATH': - aa = cscene.samples - if cscene.use_square_samples: - aa = aa * aa - else: - aa = cscene.aa_samples - d = cscene.diffuse_samples - g = cscene.glossy_samples - t = cscene.transmission_samples - ao = cscene.ao_samples - ml = cscene.mesh_light_samples - sss = cscene.subsurface_samples - vol = cscene.volume_samples + if cscene.preview_denoiser != "AUTO": + return cscene.preview_denoiser - if cscene.use_square_samples: - aa = aa * aa - d = d * d - g = g * g - t = t * t - ao = ao * ao - ml = ml * ml - sss = sss * sss - vol = vol * vol + if context.preferences.addons[__package__].preferences.get_devices_for_type('OPTIX'): + return 'OPTIX' + + return 'OIDN' - # Draw interface - # Do not draw for progressive, when Square Samples are disabled - if use_branched_path(context) or (cscene.use_square_samples and integrator == 'PATH'): - col = layout.column(align=True) - col.scale_y = 0.6 - col.label(text="Total Samples:") - col.separator() - if integrator == 'PATH': - col.label(text="%s AA" % aa) - else: - col.label(text="%s AA, %s Diffuse, %s Glossy, %s Transmission" % - (aa, d * aa, g * aa, t * aa)) - col.separator() - col.label(text="%s AO, %s Mesh Light, %s Subsurface, %s Volume" % - (ao * aa, ml * aa, sss * aa, vol * aa)) class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel): bl_label = "Sampling" + def draw(self, context): + pass + + +class CYCLES_RENDER_PT_sampling_viewport(CyclesButtonsPanel, Panel): + bl_label = "Viewport" + bl_parent_id = "CYCLES_RENDER_PT_sampling" + + def draw_header_preset(self, context): + CYCLES_PT_viewport_sampling_presets.draw_panel_header(self.layout) + + def draw(self, context): + layout = self.layout + + scene = context.scene + cscene = scene.cycles + + layout.use_property_split = True + layout.use_property_decorate = False + + heading = layout.column(align=True, heading="Noise Threshold") + row = heading.row(align=True) + row.prop(cscene, "use_preview_adaptive_sampling", text="") + sub = row.row() + sub.active = cscene.use_preview_adaptive_sampling + sub.prop(cscene, "preview_adaptive_threshold", text="") + + if cscene.use_preview_adaptive_sampling: + col = layout.column(align=True) + col.prop(cscene, "preview_samples", text=" Max Samples") + col.prop(cscene, "preview_adaptive_min_samples", text="Min Samples") + else: + layout.prop(cscene, "preview_samples", text="Samples") + + +class CYCLES_RENDER_PT_sampling_viewport_denoise(CyclesButtonsPanel, Panel): + bl_label = "Denoise" + bl_parent_id = 'CYCLES_RENDER_PT_sampling_viewport' + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, context): + scene = context.scene + cscene = scene.cycles + + self.layout.prop(context.scene.cycles, "use_preview_denoising", text="") + + def draw(self, context): + layout = self.layout + layout.use_property_split = True + layout.use_property_decorate = False + + scene = context.scene + cscene = scene.cycles + + col = layout.column() + col.active = cscene.use_preview_denoising + col.prop(cscene, "preview_denoiser", text="Denoiser") + col.prop(cscene, "preview_denoising_input_passes", text="Passes") + + effective_preview_denoiser = get_effective_preview_denoiser(context) + if effective_preview_denoiser == 'OPENIMAGEDENOISE': + col.prop(cscene, "preview_denoising_prefilter", text="Prefilter") + + col.prop(cscene, "preview_denoising_start_sample", text="Start Sample") + + +class CYCLES_RENDER_PT_sampling_render(CyclesButtonsPanel, Panel): + bl_label = "Render" + bl_parent_id = "CYCLES_RENDER_PT_sampling" + def draw_header_preset(self, context): CYCLES_PT_sampling_presets.draw_panel_header(self.layout) @@ -176,64 +226,32 @@ class CYCLES_RENDER_PT_sampling(CyclesButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False - if not use_optix(context): - layout.prop(cscene, "progressive") - - if not use_branched_path(context): - col = layout.column(align=True) - col.prop(cscene, "samples", text="Render") - col.prop(cscene, "preview_samples", text="Viewport") - else: - col = layout.column(align=True) - col.prop(cscene, "aa_samples", text="Render") - col.prop(cscene, "preview_aa_samples", text="Viewport") - - if not use_branched_path(context): - draw_samples_info(layout, context) - - -class CYCLES_RENDER_PT_sampling_sub_samples(CyclesButtonsPanel, Panel): - bl_label = "Sub Samples" - bl_parent_id = "CYCLES_RENDER_PT_sampling" - - @classmethod - def poll(cls, context): - return use_branched_path(context) - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - scene = context.scene - cscene = scene.cycles + heading = layout.column(align=True, heading="Noise Threshold") + row = heading.row(align=True) + row.prop(cscene, "use_adaptive_sampling", text="") + sub = row.row() + sub.active = cscene.use_adaptive_sampling + sub.prop(cscene, "adaptive_threshold", text="") col = layout.column(align=True) - col.prop(cscene, "diffuse_samples", text="Diffuse") - col.prop(cscene, "glossy_samples", text="Glossy") - col.prop(cscene, "transmission_samples", text="Transmission") - col.prop(cscene, "ao_samples", text="AO") - - sub = col.row(align=True) - sub.active = use_sample_all_lights(context) - sub.prop(cscene, "mesh_light_samples", text="Mesh Light") - col.prop(cscene, "subsurface_samples", text="Subsurface") - col.prop(cscene, "volume_samples", text="Volume") - - draw_samples_info(layout, context) + if cscene.use_adaptive_sampling: + col.prop(cscene, "samples", text=" Max Samples") + col.prop(cscene, "adaptive_min_samples", text="Min Samples") + else: + col.prop(cscene, "samples", text="Samples") + col.prop(cscene, "time_limit") -class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel): - bl_label = "Adaptive Sampling" - bl_parent_id = "CYCLES_RENDER_PT_sampling" +class CYCLES_RENDER_PT_sampling_render_denoise(CyclesButtonsPanel, Panel): + bl_label = "Denoise" + bl_parent_id = 'CYCLES_RENDER_PT_sampling_render' bl_options = {'DEFAULT_CLOSED'} def draw_header(self, context): - layout = self.layout scene = context.scene cscene = scene.cycles - layout.prop(cscene, "use_adaptive_sampling", text="") + self.layout.prop(context.scene.cycles, "use_denoising", text="") def draw(self, context): layout = self.layout @@ -243,53 +261,12 @@ class CYCLES_RENDER_PT_sampling_adaptive(CyclesButtonsPanel, Panel): scene = context.scene cscene = scene.cycles - layout.active = cscene.use_adaptive_sampling - - col = layout.column(align=True) - col.prop(cscene, "adaptive_threshold", text="Noise Threshold") - col.prop(cscene, "adaptive_min_samples", text="Min Samples") - - -class CYCLES_RENDER_PT_sampling_denoising(CyclesButtonsPanel, Panel): - bl_label = "Denoising" - bl_parent_id = "CYCLES_RENDER_PT_sampling" - bl_options = {'DEFAULT_CLOSED'} - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - scene = context.scene - cscene = scene.cycles - - heading = layout.column(align=True, heading="Render") - row = heading.row(align=True) - row.prop(cscene, "use_denoising", text="") - sub = row.row() - - sub.active = cscene.use_denoising - for view_layer in scene.view_layers: - if view_layer.cycles.denoising_store_passes: - sub.active = True - - sub.prop(cscene, "denoiser", text="") - - layout.separator() - - heading = layout.column(align=False, heading="Viewport") - row = heading.row(align=True) - row.prop(cscene, "use_preview_denoising", text="") - sub = row.row() - sub.active = cscene.use_preview_denoising - sub.prop(cscene, "preview_denoiser", text="") - - sub = heading.row(align=True) - sub.active = cscene.use_preview_denoising - sub.prop(cscene, "preview_denoising_start_sample", text="Start Sample") - sub = heading.row(align=True) - sub.active = cscene.use_preview_denoising - sub.prop(cscene, "preview_denoising_input_passes", text="Input Passes") + col = layout.column() + col.active = cscene.use_denoising + col.prop(cscene, "denoiser", text="Denoiser") + col.prop(cscene, "denoising_input_passes", text="Passes") + if cscene.denoiser == 'OPENIMAGEDENOISE': + col.prop(cscene, "denoising_prefilter", text="Prefilter") class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): @@ -310,10 +287,18 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): row.prop(cscene, "use_animated_seed", text="", icon='TIME') col = layout.column(align=True) - col.active = not(cscene.use_adaptive_sampling) + col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) col.prop(cscene, "sampling_pattern", text="Pattern") - layout.prop(cscene, "use_square_samples") + layout.separator() + + col = layout.column(align=True) + col.active = not (cscene.use_adaptive_sampling and cscene.use_preview_adaptive_sampling) + col.prop(cscene, "scrambling_distance", text="Scrambling Distance") + col.prop(cscene, "adaptive_scrambling_distance", text="Adaptive") + sub = col.row(align=True) + sub.active = not cscene.use_preview_adaptive_sampling + sub.prop(cscene, "preview_scrambling_distance", text="Viewport") layout.separator() @@ -322,11 +307,6 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): col.prop(cscene, "min_transparent_bounces") col.prop(cscene, "light_sampling_threshold", text="Light Threshold") - if cscene.progressive != 'PATH' and use_branched_path(context): - col = layout.column(align=True) - col.prop(cscene, "sample_all_lights_direct") - col.prop(cscene, "sample_all_lights_indirect") - for view_layer in scene.view_layers: if view_layer.samples > 0: layout.separator() @@ -334,62 +314,6 @@ class CYCLES_RENDER_PT_sampling_advanced(CyclesButtonsPanel, Panel): break -class CYCLES_RENDER_PT_sampling_total(CyclesButtonsPanel, Panel): - bl_label = "Total Samples" - bl_parent_id = "CYCLES_RENDER_PT_sampling" - - @classmethod - def poll(cls, context): - scene = context.scene - cscene = scene.cycles - - if cscene.use_square_samples: - return True - - return cscene.progressive != 'PATH' and use_branched_path(context) - - def draw(self, context): - layout = self.layout - cscene = context.scene.cycles - integrator = cscene.progressive - - # Calculate sample values - if integrator == 'PATH': - aa = cscene.samples - if cscene.use_square_samples: - aa = aa * aa - else: - aa = cscene.aa_samples - d = cscene.diffuse_samples - g = cscene.glossy_samples - t = cscene.transmission_samples - ao = cscene.ao_samples - ml = cscene.mesh_light_samples - sss = cscene.subsurface_samples - vol = cscene.volume_samples - - if cscene.use_square_samples: - aa = aa * aa - d = d * d - g = g * g - t = t * t - ao = ao * ao - ml = ml * ml - sss = sss * sss - vol = vol * vol - - col = layout.column(align=True) - col.scale_y = 0.6 - if integrator == 'PATH': - col.label(text="%s AA" % aa) - else: - col.label(text="%s AA, %s Diffuse, %s Glossy, %s Transmission" % - (aa, d * aa, g * aa, t * aa)) - col.separator() - col.label(text="%s AO, %s Mesh Light, %s Subsurface, %s Volume" % - (ao * aa, ml * aa, sss * aa, vol * aa)) - - class CYCLES_RENDER_PT_subdivision(CyclesButtonsPanel, Panel): bl_label = "Subdivision" bl_options = {'DEFAULT_CLOSED'} @@ -548,9 +472,10 @@ class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel): layout.use_property_split = True layout.use_property_decorate = False + layout.active = cscene.use_fast_gi + col = layout.column(align=True) - col.prop(cscene, "ao_bounces", text="Viewport Bounces") - col.prop(cscene, "ao_bounces_render", text="Render Bounces") + col.prop(cscene, "fast_gi_method", text="Method") if world: light = world.light_settings @@ -558,6 +483,11 @@ class CYCLES_RENDER_PT_light_paths_fast_gi(CyclesButtonsPanel, Panel): col.prop(light, "ao_factor", text="AO Factor") col.prop(light, "distance", text="AO Distance") + if cscene.fast_gi_method == 'REPLACE': + col = layout.column(align=True) + col.prop(cscene, "ao_bounces", text="Viewport Bounces") + col.prop(cscene, "ao_bounces_render", text="Render Bounces") + class CYCLES_RENDER_PT_motion_blur(CyclesButtonsPanel, Panel): bl_label = "Motion Blur" @@ -706,8 +636,8 @@ class CYCLES_RENDER_PT_performance_threads(CyclesButtonsPanel, Panel): sub.prop(rd, "threads") -class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel): - bl_label = "Tiles" +class CYCLES_RENDER_PT_performance_memory(CyclesButtonsPanel, Panel): + bl_label = "Memory" bl_parent_id = "CYCLES_RENDER_PT_performance" def draw(self, context): @@ -716,19 +646,13 @@ class CYCLES_RENDER_PT_performance_tiles(CyclesButtonsPanel, Panel): layout.use_property_decorate = False scene = context.scene - rd = scene.render cscene = scene.cycles col = layout.column() - - sub = col.column(align=True) - sub.prop(rd, "tile_x", text="Tiles X") - sub.prop(rd, "tile_y", text="Y") - col.prop(cscene, "tile_order", text="Order") - + col.prop(cscene, "use_auto_tile") sub = col.column() - sub.active = not rd.use_save_buffers and not cscene.use_adaptive_sampling - sub.prop(cscene, "use_progressive_refine") + sub.active = cscene.use_auto_tile + sub.prop(cscene, "tile_size") class CYCLES_RENDER_PT_performance_acceleration_structure(CyclesButtonsPanel, Panel): @@ -778,7 +702,6 @@ class CYCLES_RENDER_PT_performance_final_render(CyclesButtonsPanel, Panel): col = layout.column() - col.prop(rd, "use_save_buffers") col.prop(rd, "use_persistent_data", text="Persistent Data") @@ -797,7 +720,6 @@ class CYCLES_RENDER_PT_performance_viewport(CyclesButtonsPanel, Panel): col = layout.column() col.prop(rd, "preview_pixel_size", text="Pixel Size") - col.prop(cscene, "preview_start_resolution", text="Start Pixels") class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): @@ -818,7 +740,6 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): col = layout.column(heading="Include") col.prop(view_layer, "use_sky", text="Environment") - col.prop(view_layer, "use_ao", text="Ambient Occlusion") col.prop(view_layer, "use_solid", text="Surfaces") col.prop(view_layer, "use_strand", text="Hair") col.prop(view_layer, "use_volumes", text="Volumes") @@ -827,6 +748,9 @@ class CYCLES_RENDER_PT_filter(CyclesButtonsPanel, Panel): sub = col.row() sub.prop(view_layer, "use_motion_blur", text="Motion Blur") sub.active = rd.use_motion_blur + sub = col.row() + sub.prop(view_layer.cycles, 'use_denoising', text='Denoising') + sub.active = scene.cycles.use_denoising class CYCLES_RENDER_PT_override(CyclesButtonsPanel, Panel): @@ -872,6 +796,7 @@ class CYCLES_RENDER_PT_passes_data(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_pass_combined") col.prop(view_layer, "use_pass_z") col.prop(view_layer, "use_pass_mist") + col.prop(view_layer, "use_pass_position") col.prop(view_layer, "use_pass_normal") sub = col.column() sub.active = not rd.use_motion_blur @@ -885,7 +810,6 @@ class CYCLES_RENDER_PT_passes_data(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_pass_material_index") col = layout.column(heading="Debug", align=True) - col.prop(cycles_view_layer, "pass_debug_render_time", text="Render Time") col.prop(cycles_view_layer, "pass_debug_sample_count", text="Sample Count") layout.prop(view_layer, "pass_alpha_threshold") @@ -928,6 +852,7 @@ class CYCLES_RENDER_PT_passes_light(CyclesButtonsPanel, Panel): col.prop(view_layer, "use_pass_environment") col.prop(view_layer, "use_pass_shadow") col.prop(view_layer, "use_pass_ambient_occlusion", text="Ambient Occlusion") + col.prop(cycles_view_layer, "use_pass_shadow_catcher") class CYCLES_RENDER_PT_passes_crypto(CyclesButtonsPanel, ViewLayerCryptomattePanel, Panel): @@ -942,70 +867,6 @@ class CYCLES_RENDER_PT_passes_aov(CyclesButtonsPanel, ViewLayerAOVPanel): bl_parent_id = "CYCLES_RENDER_PT_passes" -class CYCLES_RENDER_PT_denoising(CyclesButtonsPanel, Panel): - bl_label = "Denoising" - bl_context = "view_layer" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - cscene = context.scene.cycles - return CyclesButtonsPanel.poll(context) and cscene.use_denoising - - def draw_header(self, context): - scene = context.scene - view_layer = context.view_layer - cycles_view_layer = view_layer.cycles - - layout = self.layout - layout.prop(cycles_view_layer, "use_denoising", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - scene = context.scene - view_layer = context.view_layer - cycles_view_layer = view_layer.cycles - denoiser = scene.cycles.denoiser - - layout.active = denoiser != 'NONE' and cycles_view_layer.use_denoising - - col = layout.column() - - if denoiser == 'OPTIX': - col.prop(cycles_view_layer, "denoising_optix_input_passes") - return - elif denoiser == 'OPENIMAGEDENOISE': - col.prop(cycles_view_layer, "denoising_openimagedenoise_input_passes") - return - - col.prop(cycles_view_layer, "denoising_radius", text="Radius") - - col = layout.column() - col.prop(cycles_view_layer, "denoising_strength", slider=True, text="Strength") - col.prop(cycles_view_layer, "denoising_feature_strength", slider=True, text="Feature Strength") - col.prop(cycles_view_layer, "denoising_relative_pca") - - layout.separator() - - col = layout.column() - col.active = cycles_view_layer.use_denoising or cycles_view_layer.denoising_store_passes - - row = col.row(heading="Diffuse", align=True) - row.prop(cycles_view_layer, "denoising_diffuse_direct", text="Direct", toggle=True) - row.prop(cycles_view_layer, "denoising_diffuse_indirect", text="Indirect", toggle=True) - - row = col.row(heading="Glossy", align=True) - row.prop(cycles_view_layer, "denoising_glossy_direct", text="Direct", toggle=True) - row.prop(cycles_view_layer, "denoising_glossy_indirect", text="Indirect", toggle=True) - - row = col.row(heading="Transmission", align=True) - row.prop(cycles_view_layer, "denoising_transmission_direct", text="Direct", toggle=True) - row.prop(cycles_view_layer, "denoising_transmission_indirect", text="Indirect", toggle=True) - - class CYCLES_PT_post_processing(CyclesButtonsPanel, Panel): bl_label = "Post Processing" bl_options = {'DEFAULT_CLOSED'} @@ -1138,8 +999,8 @@ class CYCLES_PT_context_material(CyclesButtonsPanel, Panel): row.prop(slot, "link", text="", icon=icon_link, icon_only=True) elif mat: - split.template_ID(space, "pin_id") - split.separator() + layout.template_ID(space, "pin_id") + layout.separator() class CYCLES_OBJECT_PT_motion_blur(CyclesButtonsPanel, Panel): @@ -1417,10 +1278,6 @@ class CYCLES_LIGHT_PT_light(CyclesButtonsPanel, Panel): if not (light.type == 'AREA' and clamp.is_portal): sub = col.column() - if use_branched_path(context): - subsub = sub.row(align=True) - subsub.active = use_sample_all_lights(context) - subsub.prop(clamp, "samples") sub.prop(clamp, "max_bounces") sub = col.column(align=True) @@ -1526,34 +1383,6 @@ class CYCLES_WORLD_PT_volume(CyclesButtonsPanel, Panel): panel_node_draw(layout, world, 'OUTPUT_WORLD', 'Volume') -class CYCLES_WORLD_PT_ambient_occlusion(CyclesButtonsPanel, Panel): - bl_label = "Ambient Occlusion" - bl_context = "world" - bl_options = {'DEFAULT_CLOSED'} - - @classmethod - def poll(cls, context): - return context.world and CyclesButtonsPanel.poll(context) - - def draw_header(self, context): - light = context.world.light_settings - self.layout.prop(light, "use_ambient_occlusion", text="") - - def draw(self, context): - layout = self.layout - layout.use_property_split = True - layout.use_property_decorate = False - - light = context.world.light_settings - scene = context.scene - - col = layout.column() - sub = col.column() - sub.active = light.use_ambient_occlusion or scene.render.use_simplify - sub.prop(light, "ao_factor", text="Factor") - col.prop(light, "distance", text="Distance") - - class CYCLES_WORLD_PT_mist(CyclesButtonsPanel, Panel): bl_label = "Mist Pass" bl_context = "world" @@ -1650,10 +1479,6 @@ class CYCLES_WORLD_PT_settings_surface(CyclesButtonsPanel, Panel): subsub = sub.row(align=True) subsub.active = cworld.sampling_method == 'MANUAL' subsub.prop(cworld, "sample_map_resolution") - if use_branched_path(context): - subsub = sub.column(align=True) - subsub.active = use_sample_all_lights(context) - subsub.prop(cworld, "samples") sub.prop(cworld, "max_bounces") @@ -1677,8 +1502,7 @@ class CYCLES_WORLD_PT_settings_volume(CyclesButtonsPanel, Panel): col = layout.column() sub = col.column() - sub.active = use_cpu(context) - sub.prop(cworld, "volume_sampling", text="Sampling") + col.prop(cworld, "volume_sampling", text="Sampling") col.prop(cworld, "volume_interpolation", text="Interpolation") col.prop(cworld, "homogeneous_volume", text="Homogeneous") sub = col.column() @@ -1817,8 +1641,7 @@ class CYCLES_MATERIAL_PT_settings_volume(CyclesButtonsPanel, Panel): col = layout.column() sub = col.column() - sub.active = use_cpu(context) - sub.prop(cmat, "volume_sampling", text="Sampling") + col.prop(cmat, "volume_sampling", text="Sampling") col.prop(cmat, "volume_interpolation", text="Interpolation") col.prop(cmat, "homogeneous_volume", text="Homogeneous") sub = col.column() @@ -1845,9 +1668,6 @@ class CYCLES_RENDER_PT_bake(CyclesButtonsPanel, Panel): cbk = scene.render.bake rd = scene.render - if use_optix(context): - layout.label(text="Baking is performed using CUDA instead of OptiX", icon='INFO') - if rd.use_bake_multires: layout.operator("object.bake_image", icon='RENDER_STILL') layout.prop(rd, "use_bake_multires") @@ -1905,7 +1725,6 @@ class CYCLES_RENDER_PT_bake_influence(CyclesButtonsPanel, Panel): col.prop(cbk, "use_pass_diffuse") col.prop(cbk, "use_pass_glossy") col.prop(cbk, "use_pass_transmission") - col.prop(cbk, "use_pass_ambient_occlusion") col.prop(cbk, "use_pass_emit") elif cscene.bake_type in {'DIFFUSE', 'GLOSSY', 'TRANSMISSION'}: @@ -1989,19 +1808,12 @@ class CYCLES_RENDER_PT_bake_output(CyclesButtonsPanel, Panel): layout.prop(cbk, "use_clear", text="Clear Image") -class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel): +class CYCLES_RENDER_PT_debug(CyclesDebugButtonsPanel, Panel): bl_label = "Debug" bl_context = "render" bl_options = {'DEFAULT_CLOSED'} COMPAT_ENGINES = {'CYCLES'} - @classmethod - def poll(cls, context): - prefs = bpy.context.preferences - return (CyclesButtonsPanel.poll(context) - and prefs.experimental.use_cycles_debug - and prefs.view.show_developer_ui) - def draw(self, context): layout = self.layout @@ -2018,29 +1830,18 @@ class CYCLES_RENDER_PT_debug(CyclesButtonsPanel, Panel): row.prop(cscene, "debug_use_cpu_avx", toggle=True) row.prop(cscene, "debug_use_cpu_avx2", toggle=True) col.prop(cscene, "debug_bvh_layout") - col.prop(cscene, "debug_use_cpu_split_kernel") col.separator() col = layout.column() col.label(text="CUDA Flags:") col.prop(cscene, "debug_use_cuda_adaptive_compile") - col.prop(cscene, "debug_use_cuda_split_kernel") col.separator() col = layout.column() col.label(text="OptiX Flags:") - col.prop(cscene, "debug_optix_cuda_streams") - col.prop(cscene, "debug_optix_curves_api") - - col.separator() - - col = layout.column() - col.label(text="OpenCL Flags:") - col.prop(cscene, "debug_opencl_device_type", text="Device") - col.prop(cscene, "debug_use_opencl_debug", text="Debug") - col.prop(cscene, "debug_opencl_mem_limit") + col.prop(cscene, "debug_use_optix_debug") col.separator() @@ -2141,20 +1942,22 @@ class CYCLES_RENDER_PT_simplify_culling(CyclesButtonsPanel, Panel): sub.prop(cscene, "distance_cull_margin", text="") -class CYCLES_VIEW3D_PT_shading_render_pass(Panel): +class CyclesShadingButtonsPanel(CyclesButtonsPanel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' - bl_label = "Render Pass" bl_parent_id = 'VIEW3D_PT_shading' - COMPAT_ENGINES = {'CYCLES'} @classmethod def poll(cls, context): return ( - context.engine in cls.COMPAT_ENGINES and + CyclesButtonsPanel.poll(context) and context.space_data.shading.type == 'RENDERED' ) + +class CYCLES_VIEW3D_PT_shading_render_pass(CyclesShadingButtonsPanel, Panel): + bl_label = "Render Pass" + def draw(self, context): shading = context.space_data.shading @@ -2162,6 +1965,26 @@ class CYCLES_VIEW3D_PT_shading_render_pass(Panel): layout.prop(shading.cycles, "render_pass", text="") +class CYCLES_VIEW3D_PT_shading_debug(CyclesDebugButtonsPanel, + CyclesShadingButtonsPanel, + Panel): + bl_label = "Debug" + + @classmethod + def poll(cls, context): + return ( + CyclesDebugButtonsPanel.poll(context) and + CyclesShadingButtonsPanel.poll(context) + ) + + def draw(self, context): + shading = context.space_data.shading + + layout = self.layout + layout.active = context.scene.cycles.use_preview_adaptive_sampling + layout.prop(shading.cycles, "show_active_pixels") + + class CYCLES_VIEW3D_PT_shading_lighting(Panel): bl_space_type = 'VIEW_3D' bl_region_type = 'HEADER' @@ -2275,11 +2098,13 @@ def get_panels(): classes = ( CYCLES_PT_sampling_presets, + CYCLES_PT_viewport_sampling_presets, CYCLES_PT_integrator_presets, CYCLES_RENDER_PT_sampling, - CYCLES_RENDER_PT_sampling_sub_samples, - CYCLES_RENDER_PT_sampling_adaptive, - CYCLES_RENDER_PT_sampling_denoising, + CYCLES_RENDER_PT_sampling_viewport, + CYCLES_RENDER_PT_sampling_viewport_denoise, + CYCLES_RENDER_PT_sampling_render, + CYCLES_RENDER_PT_sampling_render_denoise, CYCLES_RENDER_PT_sampling_advanced, CYCLES_RENDER_PT_light_paths, CYCLES_RENDER_PT_light_paths_max_bounces, @@ -2296,6 +2121,7 @@ classes = ( CYCLES_VIEW3D_PT_simplify_greasepencil, CYCLES_VIEW3D_PT_shading_lighting, CYCLES_VIEW3D_PT_shading_render_pass, + CYCLES_VIEW3D_PT_shading_debug, CYCLES_RENDER_PT_motion_blur, CYCLES_RENDER_PT_motion_blur_curve, CYCLES_RENDER_PT_film, @@ -2303,7 +2129,7 @@ classes = ( CYCLES_RENDER_PT_film_transparency, CYCLES_RENDER_PT_performance, CYCLES_RENDER_PT_performance_threads, - CYCLES_RENDER_PT_performance_tiles, + CYCLES_RENDER_PT_performance_memory, CYCLES_RENDER_PT_performance_acceleration_structure, CYCLES_RENDER_PT_performance_final_render, CYCLES_RENDER_PT_performance_viewport, @@ -2314,7 +2140,6 @@ classes = ( CYCLES_RENDER_PT_passes_aov, CYCLES_RENDER_PT_filter, CYCLES_RENDER_PT_override, - CYCLES_RENDER_PT_denoising, CYCLES_PT_post_processing, CYCLES_CAMERA_PT_dof, CYCLES_CAMERA_PT_dof_aperture, @@ -2333,7 +2158,6 @@ classes = ( CYCLES_WORLD_PT_preview, CYCLES_WORLD_PT_surface, CYCLES_WORLD_PT_volume, - CYCLES_WORLD_PT_ambient_occlusion, CYCLES_WORLD_PT_mist, CYCLES_WORLD_PT_ray_visibility, CYCLES_WORLD_PT_settings, diff --git a/intern/cycles/blender/addon/version_update.py b/intern/cycles/blender/addon/version_update.py index 827f84b9873..dab62713525 100644 --- a/intern/cycles/blender/addon/version_update.py +++ b/intern/cycles/blender/addon/version_update.py @@ -86,7 +86,7 @@ def do_versions(self): # Device might not currently be available so this can fail try: if system.legacy_compute_device_type == 1: - prop.compute_device_type = 'OPENCL' + prop.compute_device_type = 'NONE' # Was OpenCL elif system.legacy_compute_device_type == 2: prop.compute_device_type = 'CUDA' else: @@ -97,6 +97,12 @@ def do_versions(self): # Init device list for UI prop.get_devices(prop.compute_device_type) + if bpy.context.preferences.version <= (3, 0, 40): + # Disable OpenCL device + prop = bpy.context.preferences.addons[__package__].preferences + if prop.is_property_set("compute_device_type") and prop['compute_device_type'] == 4: + prop.compute_device_type = 'NONE' + # We don't modify startup file because it assumes to # have all the default values only. if not bpy.data.is_saved: @@ -109,7 +115,7 @@ def do_versions(self): library_versions.setdefault(library.version, []).append(library) # Do versioning per library, since they might have different versions. - max_need_versioning = (2, 93, 7) + max_need_versioning = (3, 0, 25) for version, libraries in library_versions.items(): if version > max_need_versioning: continue @@ -166,10 +172,6 @@ def do_versions(self): if not cscene.is_property_set("filter_type"): cscene.pixel_filter_type = 'GAUSSIAN' - # Tile Order - if not cscene.is_property_set("tile_order"): - cscene.tile_order = 'CENTER' - if version <= (2, 76, 10): cscene = scene.cycles if cscene.is_property_set("filter_type"): @@ -186,10 +188,6 @@ def do_versions(self): if version <= (2, 79, 0): cscene = scene.cycles # Default changes - if not cscene.is_property_set("aa_samples"): - cscene.aa_samples = 4 - if not cscene.is_property_set("preview_aa_samples"): - cscene.preview_aa_samples = 4 if not cscene.is_property_set("blur_glossy"): cscene.blur_glossy = 0.0 if not cscene.is_property_set("sample_clamp_indirect"): @@ -203,7 +201,6 @@ def do_versions(self): view_layer.use_pass_cryptomatte_material = cview_layer.get("use_pass_crypto_material", False) view_layer.use_pass_cryptomatte_asset = cview_layer.get("use_pass_crypto_asset", False) view_layer.pass_cryptomatte_depth = cview_layer.get("pass_crypto_depth", 6) - view_layer.use_pass_cryptomatte_accurate = cview_layer.get("pass_crypto_accurate", True) if version <= (2, 93, 7): if scene.render.engine == 'CYCLES': @@ -229,6 +226,36 @@ def do_versions(self): cscene.ao_bounces = 1 cscene.ao_bounces_render = 1 + if version <= (3, 0, 25): + cscene = scene.cycles + + # Default changes. + if not cscene.is_property_set("samples"): + cscene.samples = 128 + if not cscene.is_property_set("preview_samples"): + cscene.preview_samples = 32 + if not cscene.is_property_set("use_adaptive_sampling"): + cscene.use_adaptive_sampling = False + cscene.use_preview_adaptive_sampling = False + if not cscene.is_property_set("use_denoising"): + cscene.use_denoising = False + if not cscene.is_property_set("use_preview_denoising"): + cscene.use_preview_denoising = False + if not cscene.is_property_set("sampling_pattern") or \ + cscene.get('sampling_pattern') >= 2: + cscene.sampling_pattern = 'PROGRESSIVE_MULTI_JITTER' + + # Removal of square samples. + cscene = scene.cycles + use_square_samples = cscene.get("use_square_samples", False) + + if use_square_samples: + cscene.samples *= cscene.samples + cscene.preview_samples *= cscene.preview_samples + for layer in scene.view_layers: + layer.samples *= layer.samples + cscene["use_square_samples"] = False + # Lamps for light in bpy.data.lights: if light.library not in libraries: @@ -249,10 +276,6 @@ def do_versions(self): if version <= (2, 76, 9): cworld = world.cycles - # World MIS Samples - if not cworld.is_property_set("samples"): - cworld.samples = 4 - # World MIS Resolution if not cworld.is_property_set("sample_map_resolution"): cworld.sample_map_resolution = 256 diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/camera.cpp similarity index 93% rename from intern/cycles/blender/blender_camera.cpp rename to intern/cycles/blender/camera.cpp index 6954c5c2f26..b5afcfa7fda 100644 --- a/intern/cycles/blender/blender_camera.cpp +++ b/intern/cycles/blender/camera.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/scene.h" +#include "scene/camera.h" +#include "scene/scene.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_logging.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN @@ -80,8 +80,9 @@ struct BlenderCamera { int render_height; BoundBox2D border; - BoundBox2D pano_viewplane; BoundBox2D viewport_camera_border; + BoundBox2D pano_viewplane; + float pano_aspectratio; float passepartout_alpha; @@ -123,10 +124,11 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings &b_rende bcam->motion_position = Camera::MOTION_POSITION_CENTER; bcam->border.right = 1.0f; bcam->border.top = 1.0f; - bcam->pano_viewplane.right = 1.0f; - bcam->pano_viewplane.top = 1.0f; bcam->viewport_camera_border.right = 1.0f; bcam->viewport_camera_border.top = 1.0f; + bcam->pano_viewplane.right = 1.0f; + bcam->pano_viewplane.top = 1.0f; + bcam->pano_aspectratio = 0.0f; bcam->passepartout_alpha = 0.5f; bcam->offscreen_dicing_scale = 1.0f; bcam->matrix = transform_identity(); @@ -358,9 +360,21 @@ static void blender_camera_viewplane(BlenderCamera *bcam, } if (bcam->type == CAMERA_PANORAMA) { - /* set viewplane */ + /* Set viewplane for panoramic camera. */ if (viewplane != NULL) { *viewplane = bcam->pano_viewplane; + + /* Modify viewplane for camera shift. */ + const float shift_factor = (bcam->pano_aspectratio == 0.0f) ? + 1.0f : + *aspectratio / bcam->pano_aspectratio; + const float dx = bcam->shift.x * shift_factor; + const float dy = bcam->shift.y * shift_factor; + + viewplane->left += dx; + viewplane->right += dx; + viewplane->bottom += dy; + viewplane->top += dy; } } else { @@ -375,8 +389,8 @@ static void blender_camera_viewplane(BlenderCamera *bcam, *viewplane = (*viewplane) * bcam->zoom; /* modify viewplane with camera shift and 3d camera view offset */ - float dx = 2.0f * (*aspectratio * bcam->shift.x + bcam->offset.x * xaspect * 2.0f); - float dy = 2.0f * (*aspectratio * bcam->shift.y + bcam->offset.y * yaspect * 2.0f); + const float dx = 2.0f * (*aspectratio * bcam->shift.x + bcam->offset.x * xaspect * 2.0f); + const float dy = 2.0f * (*aspectratio * bcam->shift.y + bcam->offset.y * yaspect * 2.0f); viewplane->left += dx; viewplane->right += dx; @@ -625,7 +639,7 @@ void BlenderSync::sync_camera_motion( /* TODO(sergey): De-duplicate calculation with camera sync. */ float fov = 2.0f * atanf((0.5f * sensor_size) / bcam.lens / aspectratio); if (fov != cam->get_fov()) { - VLOG(1) << "Camera " << b_ob.name() << " FOV change detected."; + VLOG(3) << "Camera " << b_ob.name() << " FOV change detected."; if (motion_time == 0.0f) { cam->set_fov(fov); } @@ -652,7 +666,8 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine, int width, int height, BoundBox2D *view_box, - BoundBox2D *cam_box); + BoundBox2D *cam_box, + float *view_aspect); static void blender_camera_from_view(BlenderCamera *bcam, BL::RenderEngine &b_engine, @@ -682,6 +697,7 @@ static void blender_camera_from_view(BlenderCamera *bcam, if (!skip_panorama && bcam->type == CAMERA_PANORAMA) { /* in panorama camera view, we map viewplane to camera border */ BoundBox2D view_box, cam_box; + float view_aspect; BL::RenderSettings b_render_settings(b_scene.render()); blender_camera_view_subset(b_engine, @@ -693,9 +709,11 @@ static void blender_camera_from_view(BlenderCamera *bcam, width, height, &view_box, - &cam_box); + &cam_box, + &view_aspect); bcam->pano_viewplane = view_box.make_relative_to(cam_box); + bcam->pano_aspectratio = view_aspect; } else { /* magic zoom formula */ @@ -743,17 +761,18 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine, int width, int height, BoundBox2D *view_box, - BoundBox2D *cam_box) + BoundBox2D *cam_box, + float *view_aspect) { BoundBox2D cam, view; - float view_aspect, cam_aspect, sensor_size; + float cam_aspect, sensor_size; /* Get viewport viewplane. */ BlenderCamera view_bcam; blender_camera_init(&view_bcam, b_render); blender_camera_from_view(&view_bcam, b_engine, b_scene, b_v3d, b_rv3d, width, height, true); - blender_camera_viewplane(&view_bcam, width, height, &view, &view_aspect, &sensor_size); + blender_camera_viewplane(&view_bcam, width, height, &view, view_aspect, &sensor_size); /* Get camera viewplane. */ BlenderCamera cam_bcam; @@ -768,7 +787,7 @@ static void blender_camera_view_subset(BL::RenderEngine &b_engine, &cam_bcam, cam_bcam.full_width, cam_bcam.full_height, &cam, &cam_aspect, &sensor_size); /* Return */ - *view_box = view * (1.0f / view_aspect); + *view_box = view * (1.0f / *view_aspect); *cam_box = cam * (1.0f / cam_aspect); } @@ -785,8 +804,18 @@ static void blender_camera_border_subset(BL::RenderEngine &b_engine, { /* Determine camera viewport subset. */ BoundBox2D view_box, cam_box; - blender_camera_view_subset( - b_engine, b_render, b_scene, b_ob, b_v3d, b_rv3d, width, height, &view_box, &cam_box); + float view_aspect; + blender_camera_view_subset(b_engine, + b_render, + b_scene, + b_ob, + b_v3d, + b_rv3d, + width, + height, + &view_box, + &cam_box, + &view_aspect); /* Determine viewport subset matching given border. */ cam_box = cam_box.make_relative_to(view_box); @@ -894,12 +923,8 @@ void BlenderSync::sync_view(BL::SpaceView3D &b_v3d, } } -BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, - BL::RegionView3D &b_rv3d, - Camera *cam, - int width, - int height, - const bool use_denoiser) +BufferParams BlenderSync::get_buffer_params( + BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, int height) { BufferParams params; bool use_border = false; @@ -931,10 +956,8 @@ BufferParams BlenderSync::get_buffer_params(BL::SpaceView3D &b_v3d, params.height = height; } - PassType display_pass = update_viewport_display_passes(b_v3d, params.passes); - - /* Can only denoise the combined image pass */ - params.denoising_data_pass = display_pass == PASS_COMBINED && use_denoiser; + params.window_width = params.width; + params.window_height = params.height; return params; } diff --git a/intern/cycles/blender/blender_curves.cpp b/intern/cycles/blender/curves.cpp similarity index 96% rename from intern/cycles/blender/blender_curves.cpp rename to intern/cycles/blender/curves.cpp index 6fe5ea41fff..ffe0c553738 100644 --- a/intern/cycles/blender/blender_curves.cpp +++ b/intern/cycles/blender/curves.cpp @@ -14,19 +14,20 @@ * limitations under the License. */ -#include "render/attribute.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/object.h" -#include "render/scene.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "scene/attribute.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/object.h" +#include "scene/scene.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" +#include "util/color.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN @@ -283,10 +284,13 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa return; Attribute *attr_intercept = NULL; + Attribute *attr_length = NULL; Attribute *attr_random = NULL; if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT); + if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) + attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH); if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM); @@ -300,10 +304,6 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa } } - if (num_curves > 0) { - VLOG(1) << "Exporting curve segments for mesh " << hair->name; - } - hair->reserve_curves(hair->num_curves() + num_curves, hair->get_curve_keys().size() + num_keys); num_keys = 0; @@ -336,6 +336,10 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa num_curve_keys++; } + if (attr_length != NULL) { + attr_length->add(CData->curve_length[curve]); + } + if (attr_random != NULL) { attr_random->add(hash_uint2_to_float(num_curves, 0)); } @@ -348,7 +352,7 @@ static void ExportCurveSegments(Scene *scene, Hair *hair, ParticleCurveData *CDa /* check allocation */ if ((hair->get_curve_keys().size() != num_keys) || (hair->num_curves() != num_curves)) { - VLOG(1) << "Allocation failed, clearing data"; + VLOG(1) << "Hair memory allocation failed, clearing data."; hair->clear(true); } } @@ -404,16 +408,11 @@ static void export_hair_motion_validate_attribute(Hair *hair, if (num_motion_keys != num_keys || !have_motion) { /* No motion or hair "topology" changed, remove attributes again. */ if (num_motion_keys != num_keys) { - VLOG(1) << "Hair topology changed, removing attribute."; - } - else { - VLOG(1) << "No motion, removing attribute."; + VLOG(1) << "Hair topology changed, removing motion attribute."; } hair->attributes.remove(ATTR_STD_MOTION_VERTEX_POSITION); } else if (motion_step > 0) { - VLOG(1) << "Filling in new motion vertex position for motion_step " << motion_step; - /* Motion, fill up previous steps that we might have skipped because * they had no motion, but we need them anyway now. */ for (int step = 0; step < motion_step; step++) { @@ -429,16 +428,12 @@ static void export_hair_motion_validate_attribute(Hair *hair, static void ExportCurveSegmentsMotion(Hair *hair, ParticleCurveData *CData, int motion_step) { - VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step " - << motion_step; - /* find attribute */ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); bool new_attribute = false; /* add new attribute if it doesn't exist already */ if (!attr_mP) { - VLOG(1) << "Creating new motion vertex position attribute"; attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); new_attribute = true; } @@ -657,11 +652,15 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) /* Add requested attributes. */ Attribute *attr_intercept = NULL; + Attribute *attr_length = NULL; Attribute *attr_random = NULL; if (hair->need_attribute(scene, ATTR_STD_CURVE_INTERCEPT)) { attr_intercept = hair->attributes.add(ATTR_STD_CURVE_INTERCEPT); } + if (hair->need_attribute(scene, ATTR_STD_CURVE_LENGTH)) { + attr_length = hair->attributes.add(ATTR_STD_CURVE_LENGTH); + } if (hair->need_attribute(scene, ATTR_STD_CURVE_RANDOM)) { attr_random = hair->attributes.add(ATTR_STD_CURVE_RANDOM); } @@ -670,10 +669,6 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) const int num_keys = b_hair.points.length(); const int num_curves = b_hair.curves.length(); - if (num_curves > 0) { - VLOG(1) << "Exporting curve segments for hair " << hair->name; - } - hair->reserve_curves(num_curves, num_keys); /* Export curves and points. */ @@ -714,6 +709,10 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) } } + if (attr_length) { + attr_length->add(length); + } + /* Random number per curve. */ if (attr_random != NULL) { attr_random->add(hash_uint2_to_float(b_curve.index(), 0)); @@ -727,15 +726,11 @@ static void export_hair_curves(Scene *scene, Hair *hair, BL::Hair b_hair) static void export_hair_curves_motion(Hair *hair, BL::Hair b_hair, int motion_step) { - VLOG(1) << "Exporting curve motion segments for hair " << hair->name << ", motion step " - << motion_step; - /* Find or add attribute. */ Attribute *attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); bool new_attribute = false; if (!attr_mP) { - VLOG(1) << "Creating new motion vertex position attribute"; attr_mP = hair->attributes.add(ATTR_STD_MOTION_VERTEX_POSITION); new_attribute = true; } diff --git a/intern/cycles/blender/blender_device.cpp b/intern/cycles/blender/device.cpp similarity index 87% rename from intern/cycles/blender/blender_device.cpp rename to intern/cycles/blender/device.cpp index d51b31de638..9fabc33a96b 100644 --- a/intern/cycles/blender/blender_device.cpp +++ b/intern/cycles/blender/device.cpp @@ -14,19 +14,19 @@ * limitations under the License. */ -#include "blender/blender_device.h" -#include "blender/blender_session.h" -#include "blender/blender_util.h" +#include "blender/device.h" +#include "blender/session.h" +#include "blender/util.h" -#include "util/util_foreach.h" +#include "util/foreach.h" CCL_NAMESPACE_BEGIN enum ComputeDevice { COMPUTE_DEVICE_CPU = 0, COMPUTE_DEVICE_CUDA = 1, - COMPUTE_DEVICE_OPENCL = 2, COMPUTE_DEVICE_OPTIX = 3, + COMPUTE_DEVICE_HIP = 4, COMPUTE_DEVICE_NUM }; @@ -68,13 +68,6 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen device = Device::get_multi_device(devices, threads, background); } } - else if (get_enum(cscene, "device") == 2) { - /* Find network device. */ - vector devices = Device::available_devices(DEVICE_MASK_NETWORK); - if (!devices.empty()) { - device = devices.front(); - } - } else if (get_enum(cscene, "device") == 1) { /* Test if we are using GPU devices. */ ComputeDevice compute_device = (ComputeDevice)get_enum( @@ -89,8 +82,8 @@ DeviceInfo blender_device_info(BL::Preferences &b_preferences, BL::Scene &b_scen else if (compute_device == COMPUTE_DEVICE_OPTIX) { mask |= DEVICE_MASK_OPTIX; } - else if (compute_device == COMPUTE_DEVICE_OPENCL) { - mask |= DEVICE_MASK_OPENCL; + else if (compute_device == COMPUTE_DEVICE_HIP) { + mask |= DEVICE_MASK_HIP; } vector devices = Device::available_devices(mask); diff --git a/intern/cycles/blender/blender_device.h b/intern/cycles/blender/device.h similarity index 100% rename from intern/cycles/blender/blender_device.h rename to intern/cycles/blender/device.h diff --git a/intern/cycles/blender/display_driver.cpp b/intern/cycles/blender/display_driver.cpp new file mode 100644 index 00000000000..d5f6d85251e --- /dev/null +++ b/intern/cycles/blender/display_driver.cpp @@ -0,0 +1,771 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "blender/display_driver.h" + +#include "device/device.h" +#include "util/log.h" +#include "util/opengl.h" + +extern "C" { +struct RenderEngine; + +bool RE_engine_has_render_context(struct RenderEngine *engine); +void RE_engine_render_context_enable(struct RenderEngine *engine); +void RE_engine_render_context_disable(struct RenderEngine *engine); + +bool DRW_opengl_context_release(); +void DRW_opengl_context_activate(bool drw_state); + +void *WM_opengl_context_create(); +void WM_opengl_context_activate(void *gl_context); +void WM_opengl_context_dispose(void *gl_context); +void WM_opengl_context_release(void *context); +} + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * BlenderDisplayShader. + */ + +unique_ptr BlenderDisplayShader::create(BL::RenderEngine &b_engine, + BL::Scene &b_scene) +{ + if (b_engine.support_display_space_shader(b_scene)) { + return make_unique(b_engine, b_scene); + } + + return make_unique(); +} + +int BlenderDisplayShader::get_position_attrib_location() +{ + if (position_attribute_location_ == -1) { + const uint shader_program = get_shader_program(); + position_attribute_location_ = glGetAttribLocation(shader_program, position_attribute_name); + } + return position_attribute_location_; +} + +int BlenderDisplayShader::get_tex_coord_attrib_location() +{ + if (tex_coord_attribute_location_ == -1) { + const uint shader_program = get_shader_program(); + tex_coord_attribute_location_ = glGetAttribLocation(shader_program, tex_coord_attribute_name); + } + return tex_coord_attribute_location_; +} + +/* -------------------------------------------------------------------- + * BlenderFallbackDisplayShader. + */ + +/* TODO move shaders to standalone .glsl file. */ +static const char *FALLBACK_VERTEX_SHADER = + "#version 330\n" + "uniform vec2 fullscreen;\n" + "in vec2 texCoord;\n" + "in vec2 pos;\n" + "out vec2 texCoord_interp;\n" + "\n" + "vec2 normalize_coordinates()\n" + "{\n" + " return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n" + "}\n" + "\n" + "void main()\n" + "{\n" + " gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n" + " texCoord_interp = texCoord;\n" + "}\n\0"; + +static const char *FALLBACK_FRAGMENT_SHADER = + "#version 330\n" + "uniform sampler2D image_texture;\n" + "in vec2 texCoord_interp;\n" + "out vec4 fragColor;\n" + "\n" + "void main()\n" + "{\n" + " fragColor = texture(image_texture, texCoord_interp);\n" + "}\n\0"; + +static void shader_print_errors(const char *task, const char *log, const char *code) +{ + LOG(ERROR) << "Shader: " << task << " error:"; + LOG(ERROR) << "===== shader string ===="; + + stringstream stream(code); + string partial; + + int line = 1; + while (getline(stream, partial, '\n')) { + if (line < 10) { + LOG(ERROR) << " " << line << " " << partial; + } + else { + LOG(ERROR) << line << " " << partial; + } + line++; + } + LOG(ERROR) << log; +} + +static int compile_fallback_shader(void) +{ + const struct Shader { + const char *source; + const GLenum type; + } shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER}, + {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}}; + + const GLuint program = glCreateProgram(); + + for (int i = 0; i < 2; i++) { + const GLuint shader = glCreateShader(shaders[i].type); + + string source_str = shaders[i].source; + const char *c_str = source_str.c_str(); + + glShaderSource(shader, 1, &c_str, NULL); + glCompileShader(shader); + + GLint compile_status; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compile_status); + + if (!compile_status) { + GLchar log[5000]; + GLsizei length = 0; + glGetShaderInfoLog(shader, sizeof(log), &length, log); + shader_print_errors("compile", log, c_str); + return 0; + } + + glAttachShader(program, shader); + } + + /* Link output. */ + glBindFragDataLocation(program, 0, "fragColor"); + + /* Link and error check. */ + glLinkProgram(program); + + /* TODO(sergey): Find a way to nicely de-duplicate the error checking. */ + GLint link_status; + glGetProgramiv(program, GL_LINK_STATUS, &link_status); + if (!link_status) { + GLchar log[5000]; + GLsizei length = 0; + /* TODO(sergey): Is it really program passed to glGetShaderInfoLog? */ + glGetShaderInfoLog(program, sizeof(log), &length, log); + shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER); + shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER); + return 0; + } + + return program; +} + +void BlenderFallbackDisplayShader::bind(int width, int height) +{ + create_shader_if_needed(); + + if (!shader_program_) { + return; + } + + glUseProgram(shader_program_); + glUniform1i(image_texture_location_, 0); + glUniform2f(fullscreen_location_, width, height); +} + +void BlenderFallbackDisplayShader::unbind() +{ +} + +uint BlenderFallbackDisplayShader::get_shader_program() +{ + return shader_program_; +} + +void BlenderFallbackDisplayShader::create_shader_if_needed() +{ + if (shader_program_ || shader_compile_attempted_) { + return; + } + + shader_compile_attempted_ = true; + + shader_program_ = compile_fallback_shader(); + if (!shader_program_) { + return; + } + + glUseProgram(shader_program_); + + image_texture_location_ = glGetUniformLocation(shader_program_, "image_texture"); + if (image_texture_location_ < 0) { + LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform."; + destroy_shader(); + return; + } + + fullscreen_location_ = glGetUniformLocation(shader_program_, "fullscreen"); + if (fullscreen_location_ < 0) { + LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform."; + destroy_shader(); + return; + } +} + +void BlenderFallbackDisplayShader::destroy_shader() +{ + glDeleteProgram(shader_program_); + shader_program_ = 0; +} + +/* -------------------------------------------------------------------- + * BlenderDisplaySpaceShader. + */ + +BlenderDisplaySpaceShader::BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, + BL::Scene &b_scene) + : b_engine_(b_engine), b_scene_(b_scene) +{ + DCHECK(b_engine_.support_display_space_shader(b_scene_)); +} + +void BlenderDisplaySpaceShader::bind(int /*width*/, int /*height*/) +{ + b_engine_.bind_display_space_shader(b_scene_); +} + +void BlenderDisplaySpaceShader::unbind() +{ + b_engine_.unbind_display_space_shader(); +} + +uint BlenderDisplaySpaceShader::get_shader_program() +{ + if (!shader_program_) { + glGetIntegerv(GL_CURRENT_PROGRAM, reinterpret_cast(&shader_program_)); + } + + if (!shader_program_) { + LOG(ERROR) << "Error retrieving shader program for display space shader."; + } + + return shader_program_; +} + +/* -------------------------------------------------------------------- + * BlenderDisplayDriver. + */ + +BlenderDisplayDriver::BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene) + : b_engine_(b_engine), display_shader_(BlenderDisplayShader::create(b_engine, b_scene)) +{ + /* Create context while on the main thread. */ + gl_context_create(); +} + +BlenderDisplayDriver::~BlenderDisplayDriver() +{ + gl_resources_destroy(); +} + +/* -------------------------------------------------------------------- + * Update procedure. + */ + +bool BlenderDisplayDriver::update_begin(const Params ¶ms, + int texture_width, + int texture_height) +{ + /* Note that it's the responsibility of BlenderDisplayDriver to ensure updating and drawing + * the texture does not happen at the same time. This is achieved indirectly. + * + * When enabling the OpenGL context, it uses an internal mutex lock DST.gl_context_lock. + * This same lock is also held when do_draw() is called, which together ensure mutual + * exclusion. + * + * This locking is not performed on the Cycles side, because that would cause lock inversion. */ + if (!gl_context_enable()) { + return false; + } + + if (gl_render_sync_) { + glWaitSync((GLsync)gl_render_sync_, 0, GL_TIMEOUT_IGNORED); + } + + if (!gl_texture_resources_ensure()) { + gl_context_disable(); + return false; + } + + /* Update texture dimensions if needed. */ + if (texture_.width != texture_width || texture_.height != texture_height) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + glTexImage2D( + GL_TEXTURE_2D, 0, GL_RGBA16F, texture_width, texture_height, 0, GL_RGBA, GL_HALF_FLOAT, 0); + texture_.width = texture_width; + texture_.height = texture_height; + glBindTexture(GL_TEXTURE_2D, 0); + + /* Texture did change, and no pixel storage was provided. Tag for an explicit zeroing out to + * avoid undefined content. */ + texture_.need_clear = true; + } + + /* Update PBO dimensions if needed. + * + * NOTE: Allocate the PBO for the the size which will fit the final render resolution (as in, + * at a resolution divider 1. This was we don't need to recreate graphics interoperability + * objects which are costly and which are tied to the specific underlying buffer size. + * The downside of this approach is that when graphics interoperability is not used we are + * sending too much data to GPU when resolution divider is not 1. */ + /* TODO(sergey): Investigate whether keeping the PBO exact size of the texture makes non-interop + * mode faster. */ + const int buffer_width = params.full_size.x; + const int buffer_height = params.full_size.y; + if (texture_.buffer_width != buffer_width || texture_.buffer_height != buffer_height) { + const size_t size_in_bytes = sizeof(half4) * buffer_width * buffer_height; + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + glBufferData(GL_PIXEL_UNPACK_BUFFER, size_in_bytes, 0, GL_DYNAMIC_DRAW); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + texture_.buffer_width = buffer_width; + texture_.buffer_height = buffer_height; + } + + /* New content will be provided to the texture in one way or another, so mark this in a + * centralized place. */ + texture_.need_update = true; + + texture_.params = params; + + return true; +} + +void BlenderDisplayDriver::update_end() +{ + gl_upload_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + + gl_context_disable(); +} + +/* -------------------------------------------------------------------- + * Texture buffer mapping. + */ + +half4 *BlenderDisplayDriver::map_texture_buffer() +{ + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + + half4 *mapped_rgba_pixels = reinterpret_cast( + glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY)); + if (!mapped_rgba_pixels) { + LOG(ERROR) << "Error mapping BlenderDisplayDriver pixel buffer object."; + } + + if (texture_.need_clear) { + const int64_t texture_width = texture_.width; + const int64_t texture_height = texture_.height; + memset(reinterpret_cast(mapped_rgba_pixels), + 0, + texture_width * texture_height * sizeof(half4)); + texture_.need_clear = false; + } + + return mapped_rgba_pixels; +} + +void BlenderDisplayDriver::unmap_texture_buffer() +{ + glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); +} + +/* -------------------------------------------------------------------- + * Graphics interoperability. + */ + +BlenderDisplayDriver::GraphicsInterop BlenderDisplayDriver::graphics_interop_get() +{ + GraphicsInterop interop_dst; + + interop_dst.buffer_width = texture_.buffer_width; + interop_dst.buffer_height = texture_.buffer_height; + interop_dst.opengl_pbo_id = texture_.gl_pbo_id; + + interop_dst.need_clear = texture_.need_clear; + texture_.need_clear = false; + + return interop_dst; +} + +void BlenderDisplayDriver::graphics_interop_activate() +{ + gl_context_enable(); +} + +void BlenderDisplayDriver::graphics_interop_deactivate() +{ + gl_context_disable(); +} + +/* -------------------------------------------------------------------- + * Drawing. + */ + +void BlenderDisplayDriver::clear() +{ + texture_.need_clear = true; +} + +void BlenderDisplayDriver::set_zoom(float zoom_x, float zoom_y) +{ + zoom_ = make_float2(zoom_x, zoom_y); +} + +void BlenderDisplayDriver::draw(const Params ¶ms) +{ + /* See do_update_begin() for why no locking is required here. */ + const bool transparent = true; // TODO(sergey): Derive this from Film. + + if (!gl_draw_resources_ensure()) { + return; + } + + if (use_gl_context_) { + gl_context_mutex_.lock(); + } + + if (texture_.need_clear) { + /* Texture is requested to be cleared and was not yet cleared. + * + * Do early return which should be equivalent of drawing all-zero texture. + * Watch out for the lock though so that the clear happening during update is properly + * synchronized here. */ + gl_context_mutex_.unlock(); + return; + } + + if (gl_upload_sync_) { + glWaitSync((GLsync)gl_upload_sync_, 0, GL_TIMEOUT_IGNORED); + } + + if (transparent) { + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + + display_shader_->bind(params.full_size.x, params.full_size.y); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + + /* Trick to keep sharp rendering without jagged edges on all GPUs. + * + * The idea here is to enforce driver to use linear interpolation when the image is not zoomed + * in. + * For the render result with a resolution divider in effect we always use nearest interpolation. + * + * Use explicit MIN assignment to make sure the driver does not have an undefined behavior at + * the zoom level 1. The MAG filter is always NEAREST. */ + const float zoomed_width = params.size.x * zoom_.x; + const float zoomed_height = params.size.y * zoom_.y; + if (texture_.width != params.size.x || texture_.height != params.size.y) { + /* Resolution divider is different from 1, force nearest interpolation. */ + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + else if (zoomed_width - params.size.x > 0.5f || zoomed_height - params.size.y > 0.5f) { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + } + else { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } + + glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer_); + + texture_update_if_needed(); + vertex_buffer_update(params); + + /* TODO(sergey): Does it make sense/possible to cache/reuse the VAO? */ + GLuint vertex_array_object; + glGenVertexArrays(1, &vertex_array_object); + glBindVertexArray(vertex_array_object); + + const int texcoord_attribute = display_shader_->get_tex_coord_attrib_location(); + const int position_attribute = display_shader_->get_position_attrib_location(); + + glEnableVertexAttribArray(texcoord_attribute); + glEnableVertexAttribArray(position_attribute); + + glVertexAttribPointer( + texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); + glVertexAttribPointer(position_attribute, + 2, + GL_FLOAT, + GL_FALSE, + 4 * sizeof(float), + (const GLvoid *)(sizeof(float) * 2)); + + glDrawArrays(GL_TRIANGLE_FAN, 0, 4); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); + + glDeleteVertexArrays(1, &vertex_array_object); + + display_shader_->unbind(); + + if (transparent) { + glDisable(GL_BLEND); + } + + gl_render_sync_ = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + glFlush(); + + if (use_gl_context_) { + gl_context_mutex_.unlock(); + } +} + +void BlenderDisplayDriver::gl_context_create() +{ + /* When rendering in viewport there is no render context available via engine. + * Check whether own context is to be created here. + * + * NOTE: If the `b_engine_`'s context is not available, we are expected to be on a main thread + * here. */ + use_gl_context_ = !RE_engine_has_render_context( + reinterpret_cast(b_engine_.ptr.data)); + + if (use_gl_context_) { + const bool drw_state = DRW_opengl_context_release(); + gl_context_ = WM_opengl_context_create(); + if (gl_context_) { + /* On Windows an old context is restored after creation, and subsequent release of context + * generates a Win32 error. Harmless for users, but annoying to have possible misleading + * error prints in the console. */ +#ifndef _WIN32 + WM_opengl_context_release(gl_context_); +#endif + } + else { + LOG(ERROR) << "Error creating OpenGL context."; + } + + DRW_opengl_context_activate(drw_state); + } +} + +bool BlenderDisplayDriver::gl_context_enable() +{ + if (use_gl_context_) { + if (!gl_context_) { + return false; + } + gl_context_mutex_.lock(); + WM_opengl_context_activate(gl_context_); + return true; + } + + RE_engine_render_context_enable(reinterpret_cast(b_engine_.ptr.data)); + return true; +} + +void BlenderDisplayDriver::gl_context_disable() +{ + if (use_gl_context_) { + if (gl_context_) { + WM_opengl_context_release(gl_context_); + gl_context_mutex_.unlock(); + } + return; + } + + RE_engine_render_context_disable(reinterpret_cast(b_engine_.ptr.data)); +} + +void BlenderDisplayDriver::gl_context_dispose() +{ + if (gl_context_) { + const bool drw_state = DRW_opengl_context_release(); + + WM_opengl_context_activate(gl_context_); + WM_opengl_context_dispose(gl_context_); + + DRW_opengl_context_activate(drw_state); + } +} + +bool BlenderDisplayDriver::gl_draw_resources_ensure() +{ + if (!texture_.gl_id) { + /* If there is no texture allocated, there is nothing to draw. Inform the draw call that it can + * can not continue. Note that this is not an unrecoverable error, so once the texture is known + * we will come back here and create all the GPU resources needed for draw. */ + return false; + } + + if (gl_draw_resource_creation_attempted_) { + return gl_draw_resources_created_; + } + gl_draw_resource_creation_attempted_ = true; + + if (!vertex_buffer_) { + glGenBuffers(1, &vertex_buffer_); + if (!vertex_buffer_) { + LOG(ERROR) << "Error creating vertex buffer."; + return false; + } + } + + gl_draw_resources_created_ = true; + + return true; +} + +void BlenderDisplayDriver::gl_resources_destroy() +{ + gl_context_enable(); + + if (vertex_buffer_ != 0) { + glDeleteBuffers(1, &vertex_buffer_); + } + + if (texture_.gl_pbo_id) { + glDeleteBuffers(1, &texture_.gl_pbo_id); + texture_.gl_pbo_id = 0; + } + + if (texture_.gl_id) { + glDeleteTextures(1, &texture_.gl_id); + texture_.gl_id = 0; + } + + gl_context_disable(); + + gl_context_dispose(); +} + +bool BlenderDisplayDriver::gl_texture_resources_ensure() +{ + if (texture_.creation_attempted) { + return texture_.is_created; + } + texture_.creation_attempted = true; + + DCHECK(!texture_.gl_id); + DCHECK(!texture_.gl_pbo_id); + + /* Create texture. */ + glGenTextures(1, &texture_.gl_id); + if (!texture_.gl_id) { + LOG(ERROR) << "Error creating texture."; + return false; + } + + /* Configure the texture. */ + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_.gl_id); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); + + /* Create PBO for the texture. */ + glGenBuffers(1, &texture_.gl_pbo_id); + if (!texture_.gl_pbo_id) { + LOG(ERROR) << "Error creating texture pixel buffer object."; + return false; + } + + /* Creation finished with a success. */ + texture_.is_created = true; + + return true; +} + +void BlenderDisplayDriver::texture_update_if_needed() +{ + if (!texture_.need_update) { + return; + } + + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, texture_.gl_pbo_id); + glTexSubImage2D( + GL_TEXTURE_2D, 0, 0, 0, texture_.width, texture_.height, GL_RGBA, GL_HALF_FLOAT, 0); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + + texture_.need_update = false; +} + +void BlenderDisplayDriver::vertex_buffer_update(const Params & /*params*/) +{ + /* Draw at the parameters for which the texture has been updated for. This allows to always draw + * texture during bordered-rendered camera view without flickering. The validness of the display + * parameters for a texture is guaranteed by the initial "clear" state which makes drawing to + * have an early output. + * + * Such approach can cause some extra "jelly" effect during panning, but it is not more jelly + * than overlay of selected objects. Also, it's possible to redraw texture at an intersection of + * the texture draw parameters and the latest updated draw parameters (although, complexity of + * doing it might not worth it. */ + const int x = texture_.params.full_offset.x; + const int y = texture_.params.full_offset.y; + + const int width = texture_.params.size.x; + const int height = texture_.params.size.y; + + /* Invalidate old contents - avoids stalling if the buffer is still waiting in queue to be + * rendered. */ + glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); + + float *vpointer = reinterpret_cast(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY)); + if (!vpointer) { + return; + } + + vpointer[0] = 0.0f; + vpointer[1] = 0.0f; + vpointer[2] = x; + vpointer[3] = y; + + vpointer[4] = 1.0f; + vpointer[5] = 0.0f; + vpointer[6] = x + width; + vpointer[7] = y; + + vpointer[8] = 1.0f; + vpointer[9] = 1.0f; + vpointer[10] = x + width; + vpointer[11] = y + height; + + vpointer[12] = 0.0f; + vpointer[13] = 1.0f; + vpointer[14] = x; + vpointer[15] = y + height; + + glUnmapBuffer(GL_ARRAY_BUFFER); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/display_driver.h b/intern/cycles/blender/display_driver.h new file mode 100644 index 00000000000..66cfc8cffcc --- /dev/null +++ b/intern/cycles/blender/display_driver.h @@ -0,0 +1,213 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "MEM_guardedalloc.h" + +#include "RNA_blender_cpp.h" + +#include "session/display_driver.h" + +#include "util/thread.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +/* Base class of shader used for display driver rendering. */ +class BlenderDisplayShader { + public: + static constexpr const char *position_attribute_name = "pos"; + static constexpr const char *tex_coord_attribute_name = "texCoord"; + + /* Create shader implementation suitable for the given render engine and scene configuration. */ + static unique_ptr create(BL::RenderEngine &b_engine, BL::Scene &b_scene); + + BlenderDisplayShader() = default; + virtual ~BlenderDisplayShader() = default; + + virtual void bind(int width, int height) = 0; + virtual void unbind() = 0; + + /* Get attribute location for position and texture coordinate respectively. + * NOTE: The shader needs to be bound to have access to those. */ + virtual int get_position_attrib_location(); + virtual int get_tex_coord_attrib_location(); + + protected: + /* Get program of this display shader. + * NOTE: The shader needs to be bound to have access to this. */ + virtual uint get_shader_program() = 0; + + /* Cached values of various OpenGL resources. */ + int position_attribute_location_ = -1; + int tex_coord_attribute_location_ = -1; +}; + +/* Implementation of display rendering shader used in the case when render engine does not support + * display space shader. */ +class BlenderFallbackDisplayShader : public BlenderDisplayShader { + public: + virtual void bind(int width, int height) override; + virtual void unbind() override; + + protected: + virtual uint get_shader_program() override; + + void create_shader_if_needed(); + void destroy_shader(); + + uint shader_program_ = 0; + int image_texture_location_ = -1; + int fullscreen_location_ = -1; + + /* Shader compilation attempted. Which means, that if the shader program is 0 then compilation or + * linking has failed. Do not attempt to re-compile the shader. */ + bool shader_compile_attempted_ = false; +}; + +class BlenderDisplaySpaceShader : public BlenderDisplayShader { + public: + BlenderDisplaySpaceShader(BL::RenderEngine &b_engine, BL::Scene &b_scene); + + virtual void bind(int width, int height) override; + virtual void unbind() override; + + protected: + virtual uint get_shader_program() override; + + BL::RenderEngine b_engine_; + BL::Scene &b_scene_; + + /* Cached values of various OpenGL resources. */ + uint shader_program_ = 0; +}; + +/* Display driver implementation which is specific for Blender viewport integration. */ +class BlenderDisplayDriver : public DisplayDriver { + public: + BlenderDisplayDriver(BL::RenderEngine &b_engine, BL::Scene &b_scene); + ~BlenderDisplayDriver(); + + virtual void graphics_interop_activate() override; + virtual void graphics_interop_deactivate() override; + + virtual void clear() override; + + void set_zoom(float zoom_x, float zoom_y); + + protected: + virtual bool update_begin(const Params ¶ms, int texture_width, int texture_height) override; + virtual void update_end() override; + + virtual half4 *map_texture_buffer() override; + virtual void unmap_texture_buffer() override; + + virtual GraphicsInterop graphics_interop_get() override; + + virtual void draw(const Params ¶ms) override; + + /* Helper function which allocates new GPU context. */ + void gl_context_create(); + bool gl_context_enable(); + void gl_context_disable(); + void gl_context_dispose(); + + /* Make sure texture is allocated and its initial configuration is performed. */ + bool gl_texture_resources_ensure(); + + /* Ensure all runtime GPU resources needed for drawing are allocated. + * Returns true if all resources needed for drawing are available. */ + bool gl_draw_resources_ensure(); + + /* Destroy all GPU resources which are being used by this object. */ + void gl_resources_destroy(); + + /* Update GPU texture dimensions and content if needed (new pixel data was provided). + * + * NOTE: The texture needs to be bound. */ + void texture_update_if_needed(); + + /* Update vertex buffer with new coordinates of vertex positions and texture coordinates. + * This buffer is used to render texture in the viewport. + * + * NOTE: The buffer needs to be bound. */ + void vertex_buffer_update(const Params ¶ms); + + BL::RenderEngine b_engine_; + + /* OpenGL context which is used the render engine doesn't have its own. */ + void *gl_context_ = nullptr; + /* The when Blender RenderEngine side context is not available and the DisplayDriver is to create + * its own context. */ + bool use_gl_context_ = false; + /* Mutex used to guard the `gl_context_`. */ + thread_mutex gl_context_mutex_; + + /* Texture which contains pixels of the render result. */ + struct { + /* Indicates whether texture creation was attempted and succeeded. + * Used to avoid multiple attempts of texture creation on GPU issues or GPU context + * misconfiguration. */ + bool creation_attempted = false; + bool is_created = false; + + /* OpenGL resource IDs of the texture itself and Pixel Buffer Object (PBO) used to write + * pixels to it. + * + * NOTE: Allocated on the engine's context. */ + uint gl_id = 0; + uint gl_pbo_id = 0; + + /* Is true when new data was written to the PBO, meaning, the texture might need to be resized + * and new data is to be uploaded to the GPU. */ + bool need_update = false; + + /* Content of the texture is to be filled with zeroes. */ + std::atomic need_clear = true; + + /* Dimensions of the texture in pixels. */ + int width = 0; + int height = 0; + + /* Dimensions of the underlying PBO. */ + int buffer_width = 0; + int buffer_height = 0; + + /* Display parameters the texture has been updated for. */ + Params params; + } texture_; + + unique_ptr display_shader_; + + /* Special track of whether GPU resources were attempted to be created, to avoid attempts of + * their re-creation on failure on every redraw. */ + bool gl_draw_resource_creation_attempted_ = false; + bool gl_draw_resources_created_ = false; + + /* Vertex buffer which hold vertices of a triangle fan which is textures with the texture + * holding the render result. */ + uint vertex_buffer_ = 0; + + void *gl_render_sync_ = nullptr; + void *gl_upload_sync_ = nullptr; + + float2 zoom_ = make_float2(1.0f, 1.0f); +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_geometry.cpp b/intern/cycles/blender/geometry.cpp similarity index 92% rename from intern/cycles/blender/blender_geometry.cpp rename to intern/cycles/blender/geometry.cpp index b1de37dac10..479e76f68bc 100644 --- a/intern/cycles/blender/blender_geometry.cpp +++ b/intern/cycles/blender/geometry.cpp @@ -15,17 +15,17 @@ * limitations under the License. */ -#include "render/curves.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/volume.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/volume.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_foreach.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN @@ -80,7 +80,11 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, { /* Test if we can instance or if the object is modified. */ Geometry::Type geom_type = determine_geom_type(b_ob_info, use_particle_hair); - GeometryKey key(b_ob_info.object_data, geom_type); + BL::ID b_key_id = (b_ob_info.is_real_object_data() && + BKE_object_is_modified(b_ob_info.real_object)) ? + b_ob_info.real_object : + b_ob_info.object_data; + GeometryKey key(b_key_id.ptr.data, geom_type); /* Find shader indices. */ array used_shaders = find_used_shaders(b_ob_info.iter_object); @@ -110,7 +114,7 @@ Geometry *BlenderSync::sync_geometry(BL::Depsgraph &b_depsgraph, } else { /* Test if we need to update existing geometry. */ - sync = geometry_map.update(geom, b_ob_info.object_data); + sync = geometry_map.update(geom, b_key_id); } if (!sync) { diff --git a/intern/cycles/blender/blender_id_map.h b/intern/cycles/blender/id_map.h similarity index 97% rename from intern/cycles/blender/blender_id_map.h rename to intern/cycles/blender/id_map.h index 198cfb4b29a..c1b800026c3 100644 --- a/intern/cycles/blender/blender_id_map.h +++ b/intern/cycles/blender/id_map.h @@ -19,12 +19,12 @@ #include -#include "render/geometry.h" -#include "render/scene.h" +#include "scene/geometry.h" +#include "scene/scene.h" -#include "util/util_map.h" -#include "util/util_set.h" -#include "util/util_vector.h" +#include "util/map.h" +#include "util/set.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_image.cpp b/intern/cycles/blender/image.cpp similarity index 98% rename from intern/cycles/blender/blender_image.cpp rename to intern/cycles/blender/image.cpp index f27275bd457..3ea3a47c1f4 100644 --- a/intern/cycles/blender/blender_image.cpp +++ b/intern/cycles/blender/image.cpp @@ -16,9 +16,9 @@ #include "MEM_guardedalloc.h" -#include "blender/blender_image.h" -#include "blender/blender_session.h" -#include "blender/blender_util.h" +#include "blender/image.h" +#include "blender/session.h" +#include "blender/util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_image.h b/intern/cycles/blender/image.h similarity index 98% rename from intern/cycles/blender/blender_image.h rename to intern/cycles/blender/image.h index fddbbfd9c37..6f1e72c21af 100644 --- a/intern/cycles/blender/blender_image.h +++ b/intern/cycles/blender/image.h @@ -19,7 +19,7 @@ #include "RNA_blender_cpp.h" -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_light.cpp b/intern/cycles/blender/light.cpp similarity index 90% rename from intern/cycles/blender/blender_light.cpp rename to intern/cycles/blender/light.cpp index 542028f4b2f..1e4cc0f1d14 100644 --- a/intern/cycles/blender/blender_light.cpp +++ b/intern/cycles/blender/light.cpp @@ -16,12 +16,12 @@ * limitations under the License. */ -#include "render/light.h" +#include "scene/light.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_hash.h" +#include "util/hash.h" CCL_NAMESPACE_BEGIN @@ -125,17 +125,10 @@ void BlenderSync::sync_light(BL::Object &b_parent, light->set_shader(static_cast(used_shaders[0])); /* shadow */ - PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); PointerRNA clight = RNA_pointer_get(&b_light.ptr, "cycles"); light->set_cast_shadow(get_boolean(clight, "cast_shadow")); light->set_use_mis(get_boolean(clight, "use_multiple_importance_sampling")); - int samples = get_int(clight, "samples"); - if (get_boolean(cscene, "use_square_samples")) - light->set_samples(samples * samples); - else - light->set_samples(samples); - light->set_max_bounces(get_int(clight, "max_bounces")); if (b_ob_info.real_object != b_ob_info.iter_object) { @@ -155,10 +148,12 @@ void BlenderSync::sync_light(BL::Object &b_parent, /* visibility */ uint visibility = object_ray_visibility(b_ob_info.real_object); + light->set_use_camera((visibility & PATH_RAY_CAMERA) != 0); light->set_use_diffuse((visibility & PATH_RAY_DIFFUSE) != 0); light->set_use_glossy((visibility & PATH_RAY_GLOSSY) != 0); light->set_use_transmission((visibility & PATH_RAY_TRANSMIT) != 0); light->set_use_scatter((visibility & PATH_RAY_VOLUME_SCATTER) != 0); + light->set_is_shadow_catcher(b_ob_info.real_object.is_shadow_catcher()); /* tag */ light->tag_update(scene); @@ -169,7 +164,6 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) BL::World b_world = b_scene.world(); if (b_world) { - PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); PointerRNA cworld = RNA_pointer_get(&b_world.ptr, "cycles"); enum SamplingMethod { SAMPLING_NONE = 0, SAMPLING_AUTOMATIC, SAMPLING_MANUAL, SAMPLING_NUM }; @@ -197,12 +191,6 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) /* force enable light again when world is resynced */ light->set_is_enabled(true); - int samples = get_int(cworld, "samples"); - if (get_boolean(cscene, "use_square_samples")) - light->set_samples(samples * samples); - else - light->set_samples(samples); - light->tag_update(scene); light_map.set_recalc(b_world); } @@ -211,7 +199,7 @@ void BlenderSync::sync_background_light(BL::SpaceView3D &b_v3d, bool use_portal) world_map = b_world.ptr.data; world_recalc = false; - viewport_parameters = BlenderViewportParameters(b_v3d); + viewport_parameters = BlenderViewportParameters(b_v3d, use_developer_ui); } CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_logging.cpp b/intern/cycles/blender/logging.cpp similarity index 96% rename from intern/cycles/blender/blender_logging.cpp rename to intern/cycles/blender/logging.cpp index b42a1f47821..613b4084aa8 100644 --- a/intern/cycles/blender/blender_logging.cpp +++ b/intern/cycles/blender/logging.cpp @@ -15,7 +15,7 @@ */ #include "blender/CCL_api.h" -#include "util/util_logging.h" +#include "util/log.h" void CCL_init_logging(const char *argv0) { diff --git a/intern/cycles/blender/blender_mesh.cpp b/intern/cycles/blender/mesh.cpp similarity index 98% rename from intern/cycles/blender/blender_mesh.cpp rename to intern/cycles/blender/mesh.cpp index 7ec430eb7fe..b69bf88c213 100644 --- a/intern/cycles/blender/blender_mesh.cpp +++ b/intern/cycles/blender/mesh.cpp @@ -14,25 +14,26 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/colorspace.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "scene/camera.h" +#include "scene/colorspace.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" -#include "subd/subd_patch.h" -#include "subd/subd_split.h" +#include "subd/patch.h" +#include "subd/split.h" -#include "util/util_algorithm.h" -#include "util/util_disjoint_set.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_math.h" +#include "util/algorithm.h" +#include "util/color.h" +#include "util/disjoint_set.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/math.h" #include "mikktspace.h" diff --git a/intern/cycles/blender/blender_object.cpp b/intern/cycles/blender/object.cpp similarity index 97% rename from intern/cycles/blender/blender_object.cpp rename to intern/cycles/blender/object.cpp index 22d6edeb099..9919b9d1836 100644 --- a/intern/cycles/blender/blender_object.cpp +++ b/intern/cycles/blender/object.cpp @@ -14,26 +14,26 @@ * limitations under the License. */ -#include "render/alembic.h" -#include "render/camera.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/particles.h" -#include "render/scene.h" -#include "render/shader.h" +#include "blender/object_cull.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "blender/blender_object_cull.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "scene/alembic.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/particles.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN @@ -104,23 +104,22 @@ void BlenderSync::sync_object_motion_init(BL::Object &b_parent, BL::Object &b_ob array motion; object->set_motion(motion); - Scene::MotionType need_motion = scene->need_motion(); - if (need_motion == Scene::MOTION_NONE || !object->get_geometry()) { + Geometry *geom = object->get_geometry(); + if (!geom) { return; } - Geometry *geom = object->get_geometry(); - int motion_steps = 0; bool use_motion_blur = false; + Scene::MotionType need_motion = scene->need_motion(); if (need_motion == Scene::MOTION_BLUR) { motion_steps = object_motion_steps(b_parent, b_ob, Object::MAX_MOTION_STEPS); if (motion_steps && object_use_deform_motion(b_parent, b_ob)) { use_motion_blur = true; } } - else { + else if (need_motion != Scene::MOTION_NONE) { motion_steps = 3; } @@ -568,7 +567,7 @@ void BlenderSync::sync_objects(BL::Depsgraph &b_depsgraph, /* object loop */ bool cancel = false; bool use_portal = false; - const bool show_lights = BlenderViewportParameters(b_v3d).use_scene_lights; + const bool show_lights = BlenderViewportParameters(b_v3d, use_developer_ui).use_scene_lights; BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); BL::Depsgraph::object_instances_iterator b_instance_iter; diff --git a/intern/cycles/blender/blender_object_cull.cpp b/intern/cycles/blender/object_cull.cpp similarity index 97% rename from intern/cycles/blender/blender_object_cull.cpp rename to intern/cycles/blender/object_cull.cpp index cb7827b3c4a..c2493be26dd 100644 --- a/intern/cycles/blender/blender_object_cull.cpp +++ b/intern/cycles/blender/object_cull.cpp @@ -16,10 +16,10 @@ #include -#include "render/camera.h" +#include "scene/camera.h" -#include "blender/blender_object_cull.h" -#include "blender/blender_util.h" +#include "blender/object_cull.h" +#include "blender/util.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_object_cull.h b/intern/cycles/blender/object_cull.h similarity index 95% rename from intern/cycles/blender/blender_object_cull.h rename to intern/cycles/blender/object_cull.h index 0879db4f802..be3068ef4e7 100644 --- a/intern/cycles/blender/blender_object_cull.h +++ b/intern/cycles/blender/object_cull.h @@ -17,8 +17,8 @@ #ifndef __BLENDER_OBJECT_CULL_H__ #define __BLENDER_OBJECT_CULL_H__ -#include "blender/blender_sync.h" -#include "util/util_types.h" +#include "blender/sync.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/output_driver.cpp b/intern/cycles/blender/output_driver.cpp new file mode 100644 index 00000000000..2b3586af668 --- /dev/null +++ b/intern/cycles/blender/output_driver.cpp @@ -0,0 +1,126 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "blender/output_driver.h" + +CCL_NAMESPACE_BEGIN + +BlenderOutputDriver::BlenderOutputDriver(BL::RenderEngine &b_engine) : b_engine_(b_engine) +{ +} + +BlenderOutputDriver::~BlenderOutputDriver() +{ +} + +bool BlenderOutputDriver::read_render_tile(const Tile &tile) +{ + /* Get render result. */ + BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x, + tile.offset.y, + tile.size.x, + tile.size.y, + tile.layer.c_str(), + tile.view.c_str()); + + /* Can happen if the intersected rectangle gives 0 width or height. */ + if (b_rr.ptr.data == NULL) { + return false; + } + + BL::RenderResult::layers_iterator b_single_rlay; + b_rr.layers.begin(b_single_rlay); + + /* layer will be missing if it was disabled in the UI */ + if (b_single_rlay == b_rr.layers.end()) { + return false; + } + + BL::RenderLayer b_rlay = *b_single_rlay; + + vector pixels(tile.size.x * tile.size.y * 4); + + /* Copy each pass. + * TODO:copy only the required ones for better performance? */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + tile.set_pass_pixels(b_pass.name(), b_pass.channels(), (float *)b_pass.rect()); + } + + b_engine_.end_result(b_rr, false, false, false); + + return true; +} + +bool BlenderOutputDriver::update_render_tile(const Tile &tile) +{ + /* Use final write for preview renders, otherwise render result wouldn't be be updated + * quickly on Blender side. For all other cases we use the display driver. */ + if (b_engine_.is_preview()) { + write_render_tile(tile); + return true; + } + + /* Don't highlight full-frame tile. */ + if (!(tile.size == tile.full_size)) { + b_engine_.tile_highlight_clear_all(); + b_engine_.tile_highlight_set(tile.offset.x, tile.offset.y, tile.size.x, tile.size.y, true); + } + + return false; +} + +void BlenderOutputDriver::write_render_tile(const Tile &tile) +{ + b_engine_.tile_highlight_clear_all(); + + /* Get render result. */ + BL::RenderResult b_rr = b_engine_.begin_result(tile.offset.x, + tile.offset.y, + tile.size.x, + tile.size.y, + tile.layer.c_str(), + tile.view.c_str()); + + /* Can happen if the intersected rectangle gives 0 width or height. */ + if (b_rr.ptr.data == NULL) { + return; + } + + BL::RenderResult::layers_iterator b_single_rlay; + b_rr.layers.begin(b_single_rlay); + + /* Layer will be missing if it was disabled in the UI. */ + if (b_single_rlay == b_rr.layers.end()) { + return; + } + + BL::RenderLayer b_rlay = *b_single_rlay; + + vector pixels(tile.size.x * tile.size.y * 4); + + /* Copy each pass. */ + for (BL::RenderPass &b_pass : b_rlay.passes) { + if (!tile.get_pass_pixels(b_pass.name(), b_pass.channels(), &pixels[0])) { + memset(&pixels[0], 0, pixels.size() * sizeof(float)); + } + + b_pass.rect(&pixels[0]); + } + + b_engine_.end_result(b_rr, true, false, true); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/output_driver.h b/intern/cycles/blender/output_driver.h new file mode 100644 index 00000000000..1d016f8bcb9 --- /dev/null +++ b/intern/cycles/blender/output_driver.h @@ -0,0 +1,40 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "MEM_guardedalloc.h" + +#include "RNA_blender_cpp.h" + +#include "session/output_driver.h" + +CCL_NAMESPACE_BEGIN + +class BlenderOutputDriver : public OutputDriver { + public: + explicit BlenderOutputDriver(BL::RenderEngine &b_engine); + ~BlenderOutputDriver(); + + virtual void write_render_tile(const Tile &tile) override; + virtual bool update_render_tile(const Tile &tile) override; + virtual bool read_render_tile(const Tile &tile) override; + + protected: + BL::RenderEngine b_engine_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_particles.cpp b/intern/cycles/blender/particles.cpp similarity index 93% rename from intern/cycles/blender/blender_particles.cpp rename to intern/cycles/blender/particles.cpp index 206ee24a093..3a2c1b0ecf9 100644 --- a/intern/cycles/blender/blender_particles.cpp +++ b/intern/cycles/blender/particles.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "render/mesh.h" -#include "render/object.h" -#include "render/particles.h" +#include "scene/particles.h" +#include "scene/mesh.h" +#include "scene/object.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_foreach.h" +#include "util/foreach.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_python.cpp b/intern/cycles/blender/python.cpp similarity index 79% rename from intern/cycles/blender/blender_python.cpp rename to intern/cycles/blender/python.cpp index 6e06b6a468f..bb9b0a74424 100644 --- a/intern/cycles/blender/blender_python.cpp +++ b/intern/cycles/blender/python.cpp @@ -18,37 +18,33 @@ #include "blender/CCL_api.h" -#include "blender/blender_device.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/device.h" +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "render/denoising.h" -#include "render/merge.h" +#include "session/denoising.h" +#include "session/merge.h" -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_md5.h" -#include "util/util_opengl.h" -#include "util/util_openimagedenoise.h" -#include "util/util_path.h" -#include "util/util_string.h" -#include "util/util_task.h" -#include "util/util_tbb.h" -#include "util/util_types.h" +#include "util/debug.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/md5.h" +#include "util/opengl.h" +#include "util/openimagedenoise.h" +#include "util/path.h" +#include "util/string.h" +#include "util/task.h" +#include "util/tbb.h" +#include "util/types.h" #ifdef WITH_OSL -# include "render/osl.h" +# include "scene/osl.h" # include # include #endif -#ifdef WITH_OPENCL -# include "device/device_intern.h" -#endif - CCL_NAMESPACE_BEGIN namespace { @@ -72,12 +68,10 @@ PyObject *pyunicode_from_string(const char *str) /* Synchronize debug flags from a given Blender scene. * Return truth when device list needs invalidation. */ -bool debug_flags_sync_from_scene(BL::Scene b_scene) +static void debug_flags_sync_from_scene(BL::Scene b_scene) { DebugFlagsRef flags = DebugFlags(); PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - /* Backup some settings for comparison. */ - DebugFlags::OpenCL::DeviceType opencl_device_type = flags.opencl.device_type; /* Synchronize shared flags. */ flags.viewport_static_bvh = get_enum(cscene, "debug_bvh_type"); /* Synchronize CPU flags. */ @@ -87,50 +81,19 @@ bool debug_flags_sync_from_scene(BL::Scene b_scene) flags.cpu.sse3 = get_boolean(cscene, "debug_use_cpu_sse3"); flags.cpu.sse2 = get_boolean(cscene, "debug_use_cpu_sse2"); flags.cpu.bvh_layout = (BVHLayout)get_enum(cscene, "debug_bvh_layout"); - flags.cpu.split_kernel = get_boolean(cscene, "debug_use_cpu_split_kernel"); /* Synchronize CUDA flags. */ flags.cuda.adaptive_compile = get_boolean(cscene, "debug_use_cuda_adaptive_compile"); - flags.cuda.split_kernel = get_boolean(cscene, "debug_use_cuda_split_kernel"); /* Synchronize OptiX flags. */ - flags.optix.cuda_streams = get_int(cscene, "debug_optix_cuda_streams"); - flags.optix.curves_api = get_boolean(cscene, "debug_optix_curves_api"); - /* Synchronize OpenCL device type. */ - switch (get_enum(cscene, "debug_opencl_device_type")) { - case 0: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_NONE; - break; - case 1: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_ALL; - break; - case 2: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_DEFAULT; - break; - case 3: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_CPU; - break; - case 4: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_GPU; - break; - case 5: - flags.opencl.device_type = DebugFlags::OpenCL::DEVICE_ACCELERATOR; - break; - } - /* Synchronize other OpenCL flags. */ - flags.opencl.debug = get_boolean(cscene, "debug_use_opencl_debug"); - flags.opencl.mem_limit = ((size_t)get_int(cscene, "debug_opencl_mem_limit")) * 1024 * 1024; - return flags.opencl.device_type != opencl_device_type; + flags.optix.use_debug = get_boolean(cscene, "debug_use_optix_debug"); } /* Reset debug flags to default values. * Return truth when device list needs invalidation. */ -bool debug_flags_reset() +static void debug_flags_reset() { DebugFlagsRef flags = DebugFlags(); - /* Backup some settings for comparison. */ - DebugFlags::OpenCL::DeviceType opencl_device_type = flags.opencl.device_type; flags.reset(); - return flags.opencl.device_type != opencl_device_type; } } /* namespace */ @@ -175,25 +138,25 @@ static const char *PyC_UnicodeAsByte(PyObject *py_str, PyObject **coerce) static PyObject *init_func(PyObject * /*self*/, PyObject *args) { - PyObject *path, *user_path; + PyObject *path, *user_path, *temp_path; int headless; - if (!PyArg_ParseTuple(args, "OOi", &path, &user_path, &headless)) { - return NULL; + if (!PyArg_ParseTuple(args, "OOOi", &path, &user_path, &temp_path, &headless)) { + return nullptr; } - PyObject *path_coerce = NULL, *user_path_coerce = NULL; + PyObject *path_coerce = nullptr, *user_path_coerce = nullptr, *temp_path_coerce = nullptr; path_init(PyC_UnicodeAsByte(path, &path_coerce), - PyC_UnicodeAsByte(user_path, &user_path_coerce)); + PyC_UnicodeAsByte(user_path, &user_path_coerce), + PyC_UnicodeAsByte(temp_path, &temp_path_coerce)); Py_XDECREF(path_coerce); Py_XDECREF(user_path_coerce); + Py_XDECREF(temp_path_coerce); BlenderSession::headless = headless; DebugFlags().running_inside_blender = true; - VLOG(2) << "Debug flags initialized to:\n" << DebugFlags(); - Py_RETURN_NONE; } @@ -299,6 +262,50 @@ static PyObject *render_func(PyObject * /*self*/, PyObject *args) Py_RETURN_NONE; } +static PyObject *render_frame_finish_func(PyObject * /*self*/, PyObject *args) +{ + PyObject *pysession; + + if (!PyArg_ParseTuple(args, "O", &pysession)) { + return nullptr; + } + + BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(pysession); + + /* Allow Blender to execute other Python scripts. */ + python_thread_state_save(&session->python_thread_state); + + session->render_frame_finish(); + + python_thread_state_restore(&session->python_thread_state); + + Py_RETURN_NONE; +} + +static PyObject *draw_func(PyObject * /*self*/, PyObject *args) +{ + PyObject *py_session, *py_graph, *py_screen, *py_space_image; + + if (!PyArg_ParseTuple(args, "OOOO", &py_session, &py_graph, &py_screen, &py_space_image)) { + return nullptr; + } + + BlenderSession *session = (BlenderSession *)PyLong_AsVoidPtr(py_session); + + ID *b_screen = (ID *)PyLong_AsVoidPtr(py_screen); + + PointerRNA b_space_image_ptr; + RNA_pointer_create(b_screen, + &RNA_SpaceImageEditor, + pylong_as_voidptr_typesafe(py_space_image), + &b_space_image_ptr); + BL::SpaceImageEditor b_space_image(b_space_image_ptr); + + session->draw(b_space_image); + + Py_RETURN_NONE; +} + /* pixel_array and result passed as pointers */ static PyObject *bake_func(PyObject * /*self*/, PyObject *args) { @@ -336,7 +343,7 @@ static PyObject *bake_func(PyObject * /*self*/, PyObject *args) Py_RETURN_NONE; } -static PyObject *draw_func(PyObject * /*self*/, PyObject *args) +static PyObject *view_draw_func(PyObject * /*self*/, PyObject *args) { PyObject *pysession, *pygraph, *pyv3d, *pyrv3d; @@ -350,7 +357,7 @@ static PyObject *draw_func(PyObject * /*self*/, PyObject *args) int viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); - session->draw(viewport[2], viewport[3]); + session->view_draw(viewport[2], viewport[3]); } Py_RETURN_NONE; @@ -697,40 +704,6 @@ static PyObject *system_info_func(PyObject * /*self*/, PyObject * /*value*/) return pyunicode_from_string(system_info.c_str()); } -#ifdef WITH_OPENCL -static PyObject *opencl_disable_func(PyObject * /*self*/, PyObject * /*value*/) -{ - VLOG(2) << "Disabling OpenCL platform."; - DebugFlags().opencl.device_type = DebugFlags::OpenCL::DEVICE_NONE; - Py_RETURN_NONE; -} - -static PyObject *opencl_compile_func(PyObject * /*self*/, PyObject *args) -{ - PyObject *sequence = PySequence_Fast(args, "Arguments must be a sequence"); - if (sequence == NULL) { - Py_RETURN_FALSE; - } - - vector parameters; - for (Py_ssize_t i = 0; i < PySequence_Fast_GET_SIZE(sequence); i++) { - PyObject *item = PySequence_Fast_GET_ITEM(sequence, i); - PyObject *item_as_string = PyObject_Str(item); - const char *parameter_string = PyUnicode_AsUTF8(item_as_string); - parameters.push_back(parameter_string); - Py_DECREF(item_as_string); - } - Py_DECREF(sequence); - - if (device_opencl_compile_kernel(parameters)) { - Py_RETURN_TRUE; - } - else { - Py_RETURN_FALSE; - } -} -#endif - static bool image_parse_filepaths(PyObject *pyfilepaths, vector &filepaths) { if (PyUnicode_Check(pyfilepaths)) { @@ -762,6 +735,10 @@ static bool image_parse_filepaths(PyObject *pyfilepaths, vector &filepat static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *keywords) { +#if 1 + (void)args; + (void)keywords; +#else static const char *keyword_list[] = { "preferences", "scene", "view_layer", "input", "output", "tile_size", "samples", NULL}; PyObject *pypreferences, *pyscene, *pyviewlayer; @@ -835,7 +812,7 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key } /* Create denoiser. */ - Denoiser denoiser(device); + DenoiserPipeline denoiser(device); denoiser.params = params; denoiser.input = input; denoiser.output = output; @@ -852,6 +829,7 @@ static PyObject *denoise_func(PyObject * /*self*/, PyObject *args, PyObject *key PyErr_SetString(PyExc_ValueError, denoiser.error.c_str()); return NULL; } +#endif Py_RETURN_NONE; } @@ -903,12 +881,7 @@ static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args) RNA_id_pointer_create((ID *)PyLong_AsVoidPtr(pyscene), &sceneptr); BL::Scene b_scene(sceneptr); - if (debug_flags_sync_from_scene(b_scene)) { - VLOG(2) << "Tagging device list for update."; - Device::tag_update(); - } - - VLOG(2) << "Debug flags set to:\n" << DebugFlags(); + debug_flags_sync_from_scene(b_scene); debug_flags_set = true; @@ -917,95 +890,13 @@ static PyObject *debug_flags_update_func(PyObject * /*self*/, PyObject *args) static PyObject *debug_flags_reset_func(PyObject * /*self*/, PyObject * /*args*/) { - if (debug_flags_reset()) { - VLOG(2) << "Tagging device list for update."; - Device::tag_update(); - } + debug_flags_reset(); if (debug_flags_set) { - VLOG(2) << "Debug flags reset to:\n" << DebugFlags(); debug_flags_set = false; } Py_RETURN_NONE; } -static PyObject *set_resumable_chunk_func(PyObject * /*self*/, PyObject *args) -{ - int num_resumable_chunks, current_resumable_chunk; - if (!PyArg_ParseTuple(args, "ii", &num_resumable_chunks, ¤t_resumable_chunk)) { - Py_RETURN_NONE; - } - - if (num_resumable_chunks <= 0) { - fprintf(stderr, "Cycles: Bad value for number of resumable chunks.\n"); - abort(); - Py_RETURN_NONE; - } - if (current_resumable_chunk < 1 || current_resumable_chunk > num_resumable_chunks) { - fprintf(stderr, "Cycles: Bad value for current resumable chunk number.\n"); - abort(); - Py_RETURN_NONE; - } - - VLOG(1) << "Initialized resumable render: " - << "num_resumable_chunks=" << num_resumable_chunks << ", " - << "current_resumable_chunk=" << current_resumable_chunk; - BlenderSession::num_resumable_chunks = num_resumable_chunks; - BlenderSession::current_resumable_chunk = current_resumable_chunk; - - printf("Cycles: Will render chunk %d of %d\n", current_resumable_chunk, num_resumable_chunks); - - Py_RETURN_NONE; -} - -static PyObject *set_resumable_chunk_range_func(PyObject * /*self*/, PyObject *args) -{ - int num_chunks, start_chunk, end_chunk; - if (!PyArg_ParseTuple(args, "iii", &num_chunks, &start_chunk, &end_chunk)) { - Py_RETURN_NONE; - } - - if (num_chunks <= 0) { - fprintf(stderr, "Cycles: Bad value for number of resumable chunks.\n"); - abort(); - Py_RETURN_NONE; - } - if (start_chunk < 1 || start_chunk > num_chunks) { - fprintf(stderr, "Cycles: Bad value for start chunk number.\n"); - abort(); - Py_RETURN_NONE; - } - if (end_chunk < 1 || end_chunk > num_chunks) { - fprintf(stderr, "Cycles: Bad value for start chunk number.\n"); - abort(); - Py_RETURN_NONE; - } - if (start_chunk > end_chunk) { - fprintf(stderr, "Cycles: End chunk should be higher than start one.\n"); - abort(); - Py_RETURN_NONE; - } - - VLOG(1) << "Initialized resumable render: " - << "num_resumable_chunks=" << num_chunks << ", " - << "start_resumable_chunk=" << start_chunk << "end_resumable_chunk=" << end_chunk; - BlenderSession::num_resumable_chunks = num_chunks; - BlenderSession::start_resumable_chunk = start_chunk; - BlenderSession::end_resumable_chunk = end_chunk; - - printf("Cycles: Will render chunks %d to %d of %d\n", start_chunk, end_chunk, num_chunks); - - Py_RETURN_NONE; -} - -static PyObject *clear_resumable_chunk_func(PyObject * /*self*/, PyObject * /*value*/) -{ - VLOG(1) << "Clear resumable render"; - BlenderSession::num_resumable_chunks = 0; - BlenderSession::current_resumable_chunk = 0; - - Py_RETURN_NONE; -} - static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args*/) { BlenderSession::print_render_stats = true; @@ -1015,16 +906,16 @@ static PyObject *enable_print_stats_func(PyObject * /*self*/, PyObject * /*args* static PyObject *get_device_types_func(PyObject * /*self*/, PyObject * /*args*/) { vector device_types = Device::available_types(); - bool has_cuda = false, has_optix = false, has_opencl = false; + bool has_cuda = false, has_optix = false, has_hip = false; foreach (DeviceType device_type, device_types) { has_cuda |= (device_type == DEVICE_CUDA); has_optix |= (device_type == DEVICE_OPTIX); - has_opencl |= (device_type == DEVICE_OPENCL); + has_hip |= (device_type == DEVICE_HIP); } PyObject *list = PyTuple_New(3); PyTuple_SET_ITEM(list, 0, PyBool_FromLong(has_cuda)); PyTuple_SET_ITEM(list, 1, PyBool_FromLong(has_optix)); - PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_opencl)); + PyTuple_SET_ITEM(list, 2, PyBool_FromLong(has_hip)); return list; } @@ -1044,15 +935,15 @@ static PyObject *set_device_override_func(PyObject * /*self*/, PyObject *arg) if (override == "CPU") { BlenderSession::device_override = DEVICE_MASK_CPU; } - else if (override == "OPENCL") { - BlenderSession::device_override = DEVICE_MASK_OPENCL; - } else if (override == "CUDA") { BlenderSession::device_override = DEVICE_MASK_CUDA; } else if (override == "OPTIX") { BlenderSession::device_override = DEVICE_MASK_OPTIX; } + else if (override == "HIP") { + BlenderSession::device_override = DEVICE_MASK_HIP; + } else { printf("\nError: %s is not a valid Cycles device.\n", override.c_str()); Py_RETURN_FALSE; @@ -1072,8 +963,10 @@ static PyMethodDef methods[] = { {"create", create_func, METH_VARARGS, ""}, {"free", free_func, METH_O, ""}, {"render", render_func, METH_VARARGS, ""}, - {"bake", bake_func, METH_VARARGS, ""}, + {"render_frame_finish", render_frame_finish_func, METH_VARARGS, ""}, {"draw", draw_func, METH_VARARGS, ""}, + {"bake", bake_func, METH_VARARGS, ""}, + {"view_draw", view_draw_func, METH_VARARGS, ""}, {"sync", sync_func, METH_VARARGS, ""}, {"reset", reset_func, METH_VARARGS, ""}, #ifdef WITH_OSL @@ -1082,10 +975,6 @@ static PyMethodDef methods[] = { #endif {"available_devices", available_devices_func, METH_VARARGS, ""}, {"system_info", system_info_func, METH_NOARGS, ""}, -#ifdef WITH_OPENCL - {"opencl_disable", opencl_disable_func, METH_NOARGS, ""}, - {"opencl_compile", opencl_compile_func, METH_VARARGS, ""}, -#endif /* Standalone denoising */ {"denoise", (PyCFunction)denoise_func, METH_VARARGS | METH_KEYWORDS, ""}, @@ -1098,11 +987,6 @@ static PyMethodDef methods[] = { /* Statistics. */ {"enable_print_stats", enable_print_stats_func, METH_NOARGS, ""}, - /* Resumable render */ - {"set_resumable_chunk", set_resumable_chunk_func, METH_VARARGS, ""}, - {"set_resumable_chunk_range", set_resumable_chunk_range_func, METH_VARARGS, ""}, - {"clear_resumable_chunk", clear_resumable_chunk_func, METH_NOARGS, ""}, - /* Compute Device selection */ {"get_device_types", get_device_types_func, METH_VARARGS, ""}, {"set_device_override", set_device_override_func, METH_O, ""}, @@ -1153,14 +1037,6 @@ void *CCL_python_module_init() PyModule_AddStringConstant(mod, "osl_version_string", "unknown"); #endif -#ifdef WITH_NETWORK - PyModule_AddObject(mod, "with_network", Py_True); - Py_INCREF(Py_True); -#else /* WITH_NETWORK */ - PyModule_AddObject(mod, "with_network", Py_False); - Py_INCREF(Py_False); -#endif /* WITH_NETWORK */ - #ifdef WITH_EMBREE PyModule_AddObject(mod, "with_embree", Py_True); Py_INCREF(Py_True); diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/session.cpp similarity index 52% rename from intern/cycles/blender/blender_session.cpp rename to intern/cycles/blender/session.cpp index 29de886e4ff..d9a2d3d3029 100644 --- a/intern/cycles/blender/blender_session.cpp +++ b/intern/cycles/blender/session.cpp @@ -17,42 +17,41 @@ #include #include "device/device.h" -#include "render/background.h" -#include "render/buffers.h" -#include "render/camera.h" -#include "render/colorspace.h" -#include "render/film.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/session.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/colorspace.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/stats.h" +#include "session/buffers.h" +#include "session/session.h" -#include "util/util_algorithm.h" -#include "util/util_color.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_murmurhash.h" -#include "util/util_progress.h" -#include "util/util_time.h" +#include "util/algorithm.h" +#include "util/color.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/murmurhash.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/time.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/display_driver.h" +#include "blender/output_driver.h" +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" CCL_NAMESPACE_BEGIN DeviceTypeMask BlenderSession::device_override = DEVICE_MASK_ALL; bool BlenderSession::headless = false; -int BlenderSession::num_resumable_chunks = 0; -int BlenderSession::current_resumable_chunk = 0; -int BlenderSession::start_resumable_chunk = 0; -int BlenderSession::end_resumable_chunk = 0; bool BlenderSession::print_render_stats = false; BlenderSession::BlenderSession(BL::RenderEngine &b_engine, @@ -73,7 +72,8 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine, width(0), height(0), preview_osl(preview_osl), - python_thread_state(NULL) + python_thread_state(NULL), + use_developer_ui(false) { /* offline render */ background = true; @@ -103,7 +103,9 @@ BlenderSession::BlenderSession(BL::RenderEngine &b_engine, width(width), height(height), preview_osl(false), - python_thread_state(NULL) + python_thread_state(NULL), + use_developer_ui(b_userpref.experimental().use_cycles_debug() && + b_userpref.view().show_developer_ui()) { /* 3d view render */ background = false; @@ -119,10 +121,10 @@ BlenderSession::~BlenderSession() void BlenderSession::create_session() { - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); - bool session_pause = BlenderSync::get_session_pause(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const bool session_pause = BlenderSync::get_session_pause(b_scene, background); /* reset status/progress */ last_status = ""; @@ -131,20 +133,18 @@ void BlenderSession::create_session() start_resize_time = 0.0; /* create session */ - session = new Session(session_params); - session->scene = scene; + session = new Session(session_params, scene_params); session->progress.set_update_callback(function_bind(&BlenderSession::tag_redraw, this)); session->progress.set_cancel_callback(function_bind(&BlenderSession::test_cancel, this)); session->set_pause(session_pause); /* create scene */ - scene = new Scene(scene_params, session->device); + scene = session->scene; scene->name = b_scene.name(); - session->scene = scene; - /* create sync */ - sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress); + sync = new BlenderSync( + b_engine, b_data, b_scene, scene, !background, use_developer_ui, session->progress); BL::Object b_camera_override(b_engine.camera_override()); if (b_v3d) { sync->sync_view(b_v3d, b_rv3d, width, height); @@ -154,13 +154,18 @@ void BlenderSession::create_session() } /* set buffer parameters */ - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); - session->reset(buffer_params, session_params.samples); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_v3d, b_rv3d, scene->camera, width, height); + session->reset(session_params, buffer_params); - b_engine.use_highlight_tiles(session_params.progressive_refine == false); - - update_resumable_tile_manager(session_params.samples); + /* Viewport and preview (as in, material preview) does not do tiled rendering, so can inform + * engine that no tracking of the tiles state is needed. + * The offline rendering will make a decision when tile is being written. The penalty of asking + * the engine to keep track of tiles state is minimal, so there is nothing to worry about here + * about possible single-tiled final render. */ + if (!b_engine.is_preview() && !b_v3d) { + b_engine.use_highlight_tiles(true); + } } void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsgraph) @@ -202,9 +207,9 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg return; } - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); if (scene->params.modified(scene_params) || session->params.modified(session_params) || !this->b_render.use_persistent_data()) { @@ -220,8 +225,6 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg session->progress.reset(); - session->tile_manager.set_tile_order(session_params.tile_order); - /* peak memory usage should show current render peak, not peak for all renders * made by this render session */ @@ -230,7 +233,8 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg if (is_new_session) { /* Sync object should be re-created for new scene. */ delete sync; - sync = new BlenderSync(b_engine, b_data, b_scene, scene, !background, session->progress); + sync = new BlenderSync( + b_engine, b_data, b_scene, scene, !background, use_developer_ui, session->progress); } else { /* Sync recalculations to do just the required updates. */ @@ -242,171 +246,37 @@ void BlenderSession::reset_session(BL::BlendData &b_data, BL::Depsgraph &b_depsg BL::SpaceView3D b_null_space_view3d(PointerRNA_NULL); BL::RegionView3D b_null_region_view3d(PointerRNA_NULL); - BufferParams buffer_params = BlenderSync::get_buffer_params(b_null_space_view3d, - b_null_region_view3d, - scene->camera, - width, - height, - session_params.denoising.use); - session->reset(buffer_params, session_params.samples); - - b_engine.use_highlight_tiles(session_params.progressive_refine == false); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_null_space_view3d, b_null_region_view3d, scene->camera, width, height); + session->reset(session_params, buffer_params); /* reset time */ start_resize_time = 0.0; + + { + thread_scoped_lock lock(draw_state_.mutex); + draw_state_.last_pass_index = -1; + } } void BlenderSession::free_session() { - session->cancel(); + if (session) { + session->cancel(true); + } delete sync; + sync = nullptr; + delete session; + session = nullptr; + + display_driver_ = nullptr; } -static ShaderEvalType get_shader_type(const string &pass_type) +void BlenderSession::full_buffer_written(string_view filename) { - const char *shader_type = pass_type.c_str(); - - /* data passes */ - if (strcmp(shader_type, "NORMAL") == 0) - return SHADER_EVAL_NORMAL; - else if (strcmp(shader_type, "UV") == 0) - return SHADER_EVAL_UV; - else if (strcmp(shader_type, "ROUGHNESS") == 0) - return SHADER_EVAL_ROUGHNESS; - else if (strcmp(shader_type, "DIFFUSE_COLOR") == 0) - return SHADER_EVAL_DIFFUSE_COLOR; - else if (strcmp(shader_type, "GLOSSY_COLOR") == 0) - return SHADER_EVAL_GLOSSY_COLOR; - else if (strcmp(shader_type, "TRANSMISSION_COLOR") == 0) - return SHADER_EVAL_TRANSMISSION_COLOR; - else if (strcmp(shader_type, "EMIT") == 0) - return SHADER_EVAL_EMISSION; - - /* light passes */ - else if (strcmp(shader_type, "AO") == 0) - return SHADER_EVAL_AO; - else if (strcmp(shader_type, "COMBINED") == 0) - return SHADER_EVAL_COMBINED; - else if (strcmp(shader_type, "SHADOW") == 0) - return SHADER_EVAL_SHADOW; - else if (strcmp(shader_type, "DIFFUSE") == 0) - return SHADER_EVAL_DIFFUSE; - else if (strcmp(shader_type, "GLOSSY") == 0) - return SHADER_EVAL_GLOSSY; - else if (strcmp(shader_type, "TRANSMISSION") == 0) - return SHADER_EVAL_TRANSMISSION; - - /* extra */ - else if (strcmp(shader_type, "ENVIRONMENT") == 0) - return SHADER_EVAL_ENVIRONMENT; - - else - return SHADER_EVAL_BAKE; -} - -static BL::RenderResult begin_render_result(BL::RenderEngine &b_engine, - int x, - int y, - int w, - int h, - const char *layername, - const char *viewname) -{ - return b_engine.begin_result(x, y, w, h, layername, viewname); -} - -static void end_render_result(BL::RenderEngine &b_engine, - BL::RenderResult &b_rr, - bool cancel, - bool highlight, - bool do_merge_results) -{ - b_engine.end_result(b_rr, (int)cancel, (int)highlight, (int)do_merge_results); -} - -void BlenderSession::do_write_update_render_tile(RenderTile &rtile, - bool do_update_only, - bool do_read_only, - bool highlight) -{ - int x = rtile.x - session->tile_manager.params.full_x; - int y = rtile.y - session->tile_manager.params.full_y; - int w = rtile.w; - int h = rtile.h; - - /* get render result */ - BL::RenderResult b_rr = begin_render_result( - b_engine, x, y, w, h, b_rlay_name.c_str(), b_rview_name.c_str()); - - /* can happen if the intersected rectangle gives 0 width or height */ - if (b_rr.ptr.data == NULL) { - return; - } - - BL::RenderResult::layers_iterator b_single_rlay; - b_rr.layers.begin(b_single_rlay); - - /* layer will be missing if it was disabled in the UI */ - if (b_single_rlay == b_rr.layers.end()) - return; - - BL::RenderLayer b_rlay = *b_single_rlay; - - if (do_read_only) { - /* copy each pass */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - /* find matching pass type */ - PassType pass_type = BlenderSync::get_pass_type(b_pass); - int components = b_pass.channels(); - - rtile.buffers->set_pass_rect( - pass_type, components, (float *)b_pass.rect(), rtile.num_samples); - } - - end_render_result(b_engine, b_rr, false, false, false); - } - else if (do_update_only) { - /* Sample would be zero at initial tile update, which is only needed - * to tag tile form blender side as IN PROGRESS for proper highlight - * no buffers should be sent to blender yet. For denoise we also - * keep showing the noisy buffers until denoise is done. */ - bool merge = (rtile.sample != 0) && (rtile.task != RenderTile::DENOISE); - - if (merge) { - update_render_result(b_rlay, rtile); - } - - end_render_result(b_engine, b_rr, true, highlight, merge); - } - else { - /* Write final render result. */ - write_render_result(b_rlay, rtile); - end_render_result(b_engine, b_rr, false, false, true); - } -} - -void BlenderSession::read_render_tile(RenderTile &rtile) -{ - do_write_update_render_tile(rtile, false, true, false); -} - -void BlenderSession::write_render_tile(RenderTile &rtile) -{ - do_write_update_render_tile(rtile, false, false, false); -} - -void BlenderSession::update_render_tile(RenderTile &rtile, bool highlight) -{ - /* use final write for preview renders, otherwise render result wouldn't be - * be updated in blender side - * would need to be investigated a bit further, but for now shall be fine - */ - if (!b_engine.is_preview()) - do_write_update_render_tile(rtile, true, false, highlight); - else - do_write_update_render_tile(rtile, false, false, false); + full_buffer_files_.emplace_back(filename); } static void add_cryptomatte_layer(BL::RenderResult &b_rr, string name, string manifest) @@ -430,12 +300,15 @@ void BlenderSession::stamp_view_layer_metadata(Scene *scene, const string &view_ to_string(session->params.samples).c_str()); /* Store ranged samples information. */ + /* TODO(sergey): Need to bring this information back. */ +#if 0 if (session->tile_manager.range_num_samples != -1) { b_rr.stamp_data_add_field((prefix + "range_start_sample").c_str(), to_string(session->tile_manager.range_start_sample).c_str()); b_rr.stamp_data_add_field((prefix + "range_num_samples").c_str(), to_string(session->tile_manager.range_num_samples).c_str()); } +#endif /* Write cryptomatte metadata. */ if (scene->film->get_cryptomatte_passes() & CRYPT_OBJECT) { @@ -474,39 +347,36 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) return; } - /* set callback to write out render results */ - session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1); - session->update_render_tile_cb = function_bind( - &BlenderSession::update_render_tile, this, _1, _2); + /* Create driver to write out render results. */ + ensure_display_driver_if_needed(); + session->set_output_driver(make_unique(b_engine)); + + session->full_buffer_written_cb = [&](string_view filename) { full_buffer_written(filename); }; BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); /* get buffer parameters */ - SessionParams session_params = BlenderSync::get_session_params( - b_engine, b_userpref, b_scene, background, b_view_layer); + const SessionParams session_params = BlenderSync::get_session_params( + b_engine, b_userpref, b_scene, background); BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); + b_v3d, b_rv3d, scene->camera, width, height); /* temporary render result to find needed passes and views */ - BL::RenderResult b_rr = begin_render_result( - b_engine, 0, 0, 1, 1, b_view_layer.name().c_str(), NULL); + BL::RenderResult b_rr = b_engine.begin_result(0, 0, 1, 1, b_view_layer.name().c_str(), NULL); BL::RenderResult::layers_iterator b_single_rlay; b_rr.layers.begin(b_single_rlay); BL::RenderLayer b_rlay = *b_single_rlay; - b_rlay_name = b_view_layer.name(); - /* Update denoising parameters. */ - session->set_denoising(session_params.denoising); + { + thread_scoped_lock lock(draw_state_.mutex); + b_rlay_name = b_view_layer.name(); + + /* Signal that the display pass is to be updated. */ + draw_state_.last_pass_index = -1; + } /* Compute render passes and film settings. */ - vector passes = sync->sync_render_passes( - b_scene, b_rlay, b_view_layer, session_params.adaptive_sampling, session_params.denoising); - - /* Set buffer params, using film settings from sync_render_passes. */ - buffer_params.passes = passes; - buffer_params.denoising_data_pass = scene->film->get_denoising_data_pass(); - buffer_params.denoising_clean_pass = scene->film->get_denoising_clean_pass(); - buffer_params.denoising_prefiltered_pass = scene->film->get_denoising_prefiltered_pass(); + sync->sync_render_passes(b_rlay, b_view_layer); BL::RenderResult::views_iterator b_view_iter; @@ -520,6 +390,9 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) ++b_view_iter, ++view_index) { b_rview_name = b_view_iter->name(); + buffer_params.layer = b_view_layer.name(); + buffer_params.view = b_rview_name; + /* set the current view */ b_engine.active_view_set(b_rview_name.c_str()); @@ -549,20 +422,16 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) } /* Update number of samples per layer. */ - int samples = sync->get_layer_samples(); - bool bound_samples = sync->get_layer_bound_samples(); - int effective_layer_samples; + const int samples = sync->get_layer_samples(); + const bool bound_samples = sync->get_layer_bound_samples(); - if (samples != 0 && (!bound_samples || (samples < session_params.samples))) - effective_layer_samples = samples; - else - effective_layer_samples = session_params.samples; - - /* Update tile manager if we're doing resumable render. */ - update_resumable_tile_manager(effective_layer_samples); + SessionParams effective_session_params = session_params; + if (samples != 0 && (!bound_samples || (samples < session_params.samples))) { + effective_session_params.samples = samples; + } /* Update session itself. */ - session->reset(buffer_params, effective_layer_samples); + session->reset(effective_session_params, buffer_params); /* render */ if (!b_engine.is_preview() && background && print_render_stats) { @@ -586,65 +455,159 @@ void BlenderSession::render(BL::Depsgraph &b_depsgraph_) stamp_view_layer_metadata(scene, b_rlay_name); /* free result without merging */ - end_render_result(b_engine, b_rr, true, true, false); + b_engine.end_result(b_rr, true, false, false); + + /* When tiled rendering is used there will be no "write" done for the tile. Forcefully clear + * highlighted tiles now, so that the highlight will be removed while processing full frame from + * file. */ + b_engine.tile_highlight_clear_all(); double total_time, render_time; session->progress.get_time(total_time, render_time); VLOG(1) << "Total render time: " << total_time; VLOG(1) << "Render time (without synchronization): " << render_time; - - /* clear callback */ - session->write_render_tile_cb = function_null; - session->update_render_tile_cb = function_null; } -static int bake_pass_filter_get(const int pass_filter) +void BlenderSession::render_frame_finish() { - int flag = BAKE_FILTER_NONE; + /* Processing of all layers and views is done. Clear the strings so that we can communicate + * progress about reading files and denoising them. */ + b_rlay_name = ""; + b_rview_name = ""; - if ((pass_filter & BL::BakeSettings::pass_filter_DIRECT) != 0) - flag |= BAKE_FILTER_DIRECT; - if ((pass_filter & BL::BakeSettings::pass_filter_INDIRECT) != 0) - flag |= BAKE_FILTER_INDIRECT; - if ((pass_filter & BL::BakeSettings::pass_filter_COLOR) != 0) - flag |= BAKE_FILTER_COLOR; + if (!b_render.use_persistent_data()) { + /* Free the sync object so that it can properly dereference nodes from the scene graph before + * the graph is freed. */ + delete sync; + sync = nullptr; - if ((pass_filter & BL::BakeSettings::pass_filter_DIFFUSE) != 0) - flag |= BAKE_FILTER_DIFFUSE; - if ((pass_filter & BL::BakeSettings::pass_filter_GLOSSY) != 0) - flag |= BAKE_FILTER_GLOSSY; - if ((pass_filter & BL::BakeSettings::pass_filter_TRANSMISSION) != 0) - flag |= BAKE_FILTER_TRANSMISSION; + session->device_free(); + } - if ((pass_filter & BL::BakeSettings::pass_filter_EMIT) != 0) - flag |= BAKE_FILTER_EMISSION; - if ((pass_filter & BL::BakeSettings::pass_filter_AO) != 0) - flag |= BAKE_FILTER_AO; + for (string_view filename : full_buffer_files_) { + session->process_full_buffer_from_disk(filename); + if (check_and_report_session_error()) { + break; + } + } - return flag; + for (string_view filename : full_buffer_files_) { + path_remove(filename); + } + + /* Clear driver. */ + session->set_output_driver(nullptr); + session->full_buffer_written_cb = function_null; + + /* All the files are handled. + * Clear the list so that this session can be re-used by Persistent Data. */ + full_buffer_files_.clear(); +} + +static PassType bake_type_to_pass(const string &bake_type_str, const int bake_filter) +{ + const char *bake_type = bake_type_str.c_str(); + + /* data passes */ + if (strcmp(bake_type, "POSITION") == 0) { + return PASS_POSITION; + } + else if (strcmp(bake_type, "NORMAL") == 0) { + return PASS_NORMAL; + } + else if (strcmp(bake_type, "UV") == 0) { + return PASS_UV; + } + else if (strcmp(bake_type, "ROUGHNESS") == 0) { + return PASS_ROUGHNESS; + } + else if (strcmp(bake_type, "EMIT") == 0) { + return PASS_EMISSION; + } + /* light passes */ + else if (strcmp(bake_type, "AO") == 0) { + return PASS_AO; + } + else if (strcmp(bake_type, "COMBINED") == 0) { + return PASS_COMBINED; + } + else if (strcmp(bake_type, "SHADOW") == 0) { + return PASS_SHADOW; + } + else if (strcmp(bake_type, "DIFFUSE") == 0) { + if ((bake_filter & BL::BakeSettings::pass_filter_DIRECT) && + bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_DIFFUSE; + } + else if (bake_filter & BL::BakeSettings::pass_filter_DIRECT) { + return PASS_DIFFUSE_DIRECT; + } + else if (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_DIFFUSE_INDIRECT; + } + else { + return PASS_DIFFUSE_COLOR; + } + } + else if (strcmp(bake_type, "GLOSSY") == 0) { + if ((bake_filter & BL::BakeSettings::pass_filter_DIRECT) && + bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_GLOSSY; + } + else if (bake_filter & BL::BakeSettings::pass_filter_DIRECT) { + return PASS_GLOSSY_DIRECT; + } + else if (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_GLOSSY_INDIRECT; + } + else { + return PASS_GLOSSY_COLOR; + } + } + else if (strcmp(bake_type, "TRANSMISSION") == 0) { + if ((bake_filter & BL::BakeSettings::pass_filter_DIRECT) && + bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_TRANSMISSION; + } + else if (bake_filter & BL::BakeSettings::pass_filter_DIRECT) { + return PASS_TRANSMISSION_DIRECT; + } + else if (bake_filter & BL::BakeSettings::pass_filter_INDIRECT) { + return PASS_TRANSMISSION_INDIRECT; + } + else { + return PASS_TRANSMISSION_COLOR; + } + } + /* extra */ + else if (strcmp(bake_type, "ENVIRONMENT") == 0) { + return PASS_BACKGROUND; + } + + return PASS_COMBINED; } void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, BL::Object &b_object, - const string &pass_type, - const int pass_filter, + const string &bake_type, + const int bake_filter, const int bake_width, const int bake_height) { b_depsgraph = b_depsgraph_; - ShaderEvalType shader_type = get_shader_type(pass_type); - int bake_pass_filter = bake_pass_filter_get(pass_filter); - /* Initialize bake manager, before we load the baking kernels. */ - scene->bake_manager->set(scene, b_object.name(), shader_type, bake_pass_filter); + scene->bake_manager->set(scene, b_object.name()); - /* Passes are identified by name, so in order to return the combined pass we need to set the - * name. */ - Pass::add(PASS_COMBINED, scene->passes, "Combined"); + /* Add render pass that we want to bake, and name it Combined so that it is + * used as that on the Blender side. */ + Pass *pass = scene->create_node(); + pass->set_name(ustring("Combined")); + pass->set_type(bake_type_to_pass(bake_type, bake_filter)); + pass->set_include_albedo((bake_filter & BL::BakeSettings::pass_filter_COLOR)); - session->read_bake_tile_cb = function_bind(&BlenderSession::read_render_tile, this, _1); - session->write_render_tile_cb = function_bind(&BlenderSession::write_render_tile, this, _1); + session->set_display_driver(nullptr); + session->set_output_driver(make_unique(b_engine)); if (!session->progress.get_cancel()) { /* Sync scene. */ @@ -667,18 +630,15 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, if (object_found && !session->progress.get_cancel()) { /* Get session and buffer parameters. */ - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - session_params.progressive_refine = false; BufferParams buffer_params; buffer_params.width = bake_width; buffer_params.height = bake_height; - buffer_params.passes = scene->passes; /* Update session. */ - session->tile_manager.set_samples(session_params.samples); - session->reset(buffer_params, session_params.samples); + session->reset(session_params, buffer_params); session->progress.set_update_callback( function_bind(&BlenderSession::update_bake_progress, this)); @@ -690,71 +650,7 @@ void BlenderSession::bake(BL::Depsgraph &b_depsgraph_, session->wait(); } - session->read_bake_tile_cb = function_null; - session->write_render_tile_cb = function_null; -} - -void BlenderSession::do_write_update_render_result(BL::RenderLayer &b_rlay, - RenderTile &rtile, - bool do_update_only) -{ - RenderBuffers *buffers = rtile.buffers; - - /* copy data from device */ - if (!buffers->copy_from_device()) - return; - - float exposure = scene->film->get_exposure(); - - vector pixels(rtile.w * rtile.h * 4); - - /* Adjust absolute sample number to the range. */ - int sample = rtile.sample; - const int range_start_sample = session->tile_manager.range_start_sample; - if (range_start_sample != -1) { - sample -= range_start_sample; - } - - if (!do_update_only) { - /* copy each pass */ - for (BL::RenderPass &b_pass : b_rlay.passes) { - int components = b_pass.channels(); - - /* Copy pixels from regular render passes. */ - bool read = buffers->get_pass_rect(b_pass.name(), exposure, sample, components, &pixels[0]); - - /* If denoising pass, */ - if (!read) { - int denoising_offset = BlenderSync::get_denoising_pass(b_pass); - if (denoising_offset >= 0) { - read = buffers->get_denoising_pass_rect( - denoising_offset, exposure, sample, components, &pixels[0]); - } - } - - if (!read) { - memset(&pixels[0], 0, pixels.size() * sizeof(float)); - } - - b_pass.rect(&pixels[0]); - } - } - else { - /* copy combined pass */ - BL::RenderPass b_combined_pass(b_rlay.passes.find_by_name("Combined", b_rview_name.c_str())); - if (buffers->get_pass_rect("Combined", exposure, sample, 4, &pixels[0])) - b_combined_pass.rect(&pixels[0]); - } -} - -void BlenderSession::write_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile) -{ - do_write_update_render_result(b_rlay, rtile, false); -} - -void BlenderSession::update_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile) -{ - do_write_update_render_result(b_rlay, rtile, true); + session->set_output_driver(nullptr); } void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) @@ -764,19 +660,21 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) return; /* on session/scene parameter changes, we recreate session entirely */ - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); - bool session_pause = BlenderSync::get_session_pause(b_scene, background); + const SceneParams scene_params = BlenderSync::get_scene_params(b_scene, background); + const bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session->params.modified(session_params) || scene->params.modified(scene_params)) { free_session(); create_session(); } - /* increase samples, but never decrease */ + ensure_display_driver_if_needed(); + + /* increase samples and render time, but never decrease */ session->set_samples(session_params.samples); - session->set_denoising_start_sample(session_params.denoising.start_sample); + session->set_time_limit(session_params.time_limit); session->set_pause(session_pause); /* copy recalc flags, outside of mutex so we can decide to do the real @@ -808,21 +706,12 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) sync->sync_camera(b_render, b_camera_override, width, height, ""); /* get buffer parameters */ - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); - - if (!buffer_params.denoising_data_pass) { - session_params.denoising.use = false; - } - - session->set_denoising(session_params.denoising); - - /* Update film if denoising data was enabled or disabled. */ - scene->film->set_denoising_data_pass(buffer_params.denoising_data_pass); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_v3d, b_rv3d, scene->camera, width, height); /* reset if needed */ if (scene->need_reset()) { - session->reset(buffer_params, session_params.samples); + session->reset(session_params, buffer_params); /* After session reset, so device is not accessing image data anymore. */ builtin_images_load(); @@ -839,7 +728,46 @@ void BlenderSession::synchronize(BL::Depsgraph &b_depsgraph_) session->start(); } -bool BlenderSession::draw(int w, int h) +void BlenderSession::draw(BL::SpaceImageEditor &space_image) +{ + if (!session || !session->scene) { + /* Offline render drawing does not force the render engine update, which means it's possible + * that the Session is not created yet. */ + return; + } + + thread_scoped_lock lock(draw_state_.mutex); + + const int pass_index = space_image.image_user().multilayer_pass(); + if (pass_index != draw_state_.last_pass_index) { + BL::RenderPass b_display_pass(b_engine.pass_by_index_get(b_rlay_name.c_str(), pass_index)); + if (!b_display_pass) { + return; + } + + Scene *scene = session->scene; + + thread_scoped_lock lock(scene->mutex); + + const Pass *pass = Pass::find(scene->passes, b_display_pass.name()); + if (!pass) { + return; + } + + scene->film->set_display_pass(pass->get_type()); + + draw_state_.last_pass_index = pass_index; + } + + if (display_driver_) { + BL::Array zoom = space_image.zoom(); + display_driver_->set_zoom(zoom[0], zoom[1]); + } + + session->draw(); +} + +void BlenderSession::view_draw(int w, int h) { /* pause in redraw in case update is not being called due to final render */ session->set_pause(BlenderSync::get_session_pause(b_scene, background)); @@ -885,14 +813,14 @@ bool BlenderSession::draw(int w, int h) /* reset if requested */ if (reset) { - SessionParams session_params = BlenderSync::get_session_params( + const SessionParams session_params = BlenderSync::get_session_params( b_engine, b_userpref, b_scene, background); - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session_params.denoising.use); - bool session_pause = BlenderSync::get_session_pause(b_scene, background); + const BufferParams buffer_params = BlenderSync::get_buffer_params( + b_v3d, b_rv3d, scene->camera, width, height); + const bool session_pause = BlenderSync::get_session_pause(b_scene, background); if (session_pause == false) { - session->reset(buffer_params, session_params.samples); + session->reset(session_params, buffer_params); start_resize_time = 0.0; } } @@ -905,18 +833,7 @@ bool BlenderSession::draw(int w, int h) update_status_progress(); /* draw */ - BufferParams buffer_params = BlenderSync::get_buffer_params( - b_v3d, b_rv3d, scene->camera, width, height, session->params.denoising.use); - DeviceDrawParams draw_params; - - if (session->params.display_buffer_linear) { - draw_params.bind_display_space_shader_cb = function_bind( - &BL::RenderEngine::bind_display_space_shader, &b_engine, b_scene); - draw_params.unbind_display_space_shader_cb = function_bind( - &BL::RenderEngine::unbind_display_space_shader, &b_engine); - } - - return !session->draw(buffer_params, draw_params); + session->draw(); } void BlenderSession::get_status(string &status, string &substatus) @@ -924,11 +841,6 @@ void BlenderSession::get_status(string &status, string &substatus) session->progress.get_status(status, substatus); } -void BlenderSession::get_kernel_status(string &kernel_status) -{ - session->progress.get_kernel_status(kernel_status); -} - void BlenderSession::get_progress(float &progress, double &total_time, double &render_time) { session->progress.get_time(total_time, render_time); @@ -947,7 +859,7 @@ void BlenderSession::update_bake_progress() void BlenderSession::update_status_progress() { - string timestatus, status, substatus, kernel_status; + string timestatus, status, substatus; string scene_status = ""; float progress; double total_time, remaining_time = 0, render_time; @@ -955,11 +867,11 @@ void BlenderSession::update_status_progress() float mem_peak = (float)session->stats.mem_peak / 1024.0f / 1024.0f; get_status(status, substatus); - get_kernel_status(kernel_status); get_progress(progress, total_time, render_time); - if (progress > 0) - remaining_time = (1.0 - (double)progress) * (render_time / (double)progress); + if (progress > 0) { + remaining_time = session->get_estimated_remaining_time(); + } if (background) { if (scene) @@ -980,14 +892,12 @@ void BlenderSession::update_status_progress() status = " | " + status; if (substatus.size() > 0) status += " | " + substatus; - if (kernel_status.size() > 0) - status += " | " + kernel_status; } double current_time = time_dt(); - /* When rendering in a window, redraw the status at least once per second to keep the elapsed and - * remaining time up-to-date. For headless rendering, only report when something significant - * changes to keep the console output readable. */ + /* When rendering in a window, redraw the status at least once per second to keep the elapsed + * and remaining time up-to-date. For headless rendering, only report when something + * significant changes to keep the console output readable. */ if (status != last_status || (!headless && (current_time - last_status_time) > 1.0)) { b_engine.update_stats("", (timestatus + scene_status + status).c_str()); b_engine.update_memory_stats(mem_used, mem_peak); @@ -999,20 +909,27 @@ void BlenderSession::update_status_progress() last_progress = progress; } - if (session->progress.get_error()) { - string error = session->progress.get_error_message(); - if (error != last_error) { - /* TODO(sergey): Currently C++ RNA API doesn't let us to - * use mnemonic name for the variable. Would be nice to - * have this figured out. - * - * For until then, 1 << 5 means RPT_ERROR. - */ - b_engine.report(1 << 5, error.c_str()); - b_engine.error_set(error.c_str()); - last_error = error; - } + check_and_report_session_error(); +} + +bool BlenderSession::check_and_report_session_error() +{ + if (!session->progress.get_error()) { + return false; } + + const string error = session->progress.get_error_message(); + if (error != last_error) { + /* TODO(sergey): Currently C++ RNA API doesn't let us to use mnemonic name for the variable. + * Would be nice to have this figured out. + * + * For until then, 1 << 5 means RPT_ERROR. */ + b_engine.report(1 << 5, error.c_str()); + b_engine.error_set(error.c_str()); + last_error = error; + } + + return true; } void BlenderSession::tag_update() @@ -1048,56 +965,6 @@ void BlenderSession::test_cancel() session->progress.set_cancel("Cancelled"); } -void BlenderSession::update_resumable_tile_manager(int num_samples) -{ - const int num_resumable_chunks = BlenderSession::num_resumable_chunks, - current_resumable_chunk = BlenderSession::current_resumable_chunk; - if (num_resumable_chunks == 0) { - return; - } - - if (num_resumable_chunks > num_samples) { - fprintf(stderr, - "Cycles warning: more sample chunks (%d) than samples (%d), " - "this will cause some samples to be included in multiple chunks.\n", - num_resumable_chunks, - num_samples); - } - - const float num_samples_per_chunk = (float)num_samples / num_resumable_chunks; - - float range_start_sample, range_num_samples; - if (current_resumable_chunk != 0) { - /* Single chunk rendering. */ - range_start_sample = num_samples_per_chunk * (current_resumable_chunk - 1); - range_num_samples = num_samples_per_chunk; - } - else { - /* Ranged-chunks. */ - const int num_chunks = end_resumable_chunk - start_resumable_chunk + 1; - range_start_sample = num_samples_per_chunk * (start_resumable_chunk - 1); - range_num_samples = num_chunks * num_samples_per_chunk; - } - - /* Round after doing the multiplications with num_chunks and num_samples_per_chunk - * to allow for many small chunks. */ - int rounded_range_start_sample = (int)floorf(range_start_sample + 0.5f); - int rounded_range_num_samples = max((int)floorf(range_num_samples + 0.5f), 1); - - /* Make sure we don't overshoot. */ - if (rounded_range_start_sample + rounded_range_num_samples > num_samples) { - rounded_range_num_samples = num_samples - rounded_range_num_samples; - } - - VLOG(1) << "Samples range start is " << range_start_sample << ", " - << "number of samples to render is " << range_num_samples; - - scene->integrator->set_start_sample(rounded_range_start_sample); - - session->tile_manager.range_start_sample = rounded_range_start_sample; - session->tile_manager.range_num_samples = rounded_range_num_samples; -} - void BlenderSession::free_blender_memory_if_possible() { if (!background) { @@ -1110,4 +977,27 @@ void BlenderSession::free_blender_memory_if_possible() b_engine.free_blender_memory(); } +void BlenderSession::ensure_display_driver_if_needed() +{ + if (display_driver_) { + /* Driver is already created. */ + return; + } + + if (headless) { + /* No display needed for headless. */ + return; + } + + if (b_engine.is_preview()) { + /* TODO(sergey): Investigate whether DisplayDriver can be used for the preview as well. */ + return; + } + + unique_ptr display_driver = make_unique(b_engine, + b_scene); + display_driver_ = display_driver.get(); + session->set_display_driver(move(display_driver)); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/session.h similarity index 68% rename from intern/cycles/blender/blender_session.h rename to intern/cycles/blender/session.h index d967b81c854..fa24b5f7467 100644 --- a/intern/cycles/blender/blender_session.h +++ b/intern/cycles/blender/session.h @@ -17,24 +17,25 @@ #ifndef __BLENDER_SESSION_H__ #define __BLENDER_SESSION_H__ +#include "MEM_guardedalloc.h" + #include "RNA_blender_cpp.h" #include "device/device.h" -#include "render/bake.h" -#include "render/scene.h" -#include "render/session.h" +#include "scene/bake.h" +#include "scene/scene.h" +#include "session/session.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN +class BlenderDisplayDriver; class BlenderSync; class ImageMetaData; class Scene; class Session; -class RenderBuffers; -class RenderTile; class BlenderSession { public: @@ -62,6 +63,8 @@ class BlenderSession { /* offline render */ void render(BL::Depsgraph &b_depsgraph); + void render_frame_finish(); + void bake(BL::Depsgraph &b_depsgrah, BL::Object &b_object, const string &pass_type, @@ -69,24 +72,16 @@ class BlenderSession { const int bake_width, const int bake_height); - void write_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile); - void write_render_tile(RenderTile &rtile); - void read_render_tile(RenderTile &rtile); - - /* update functions are used to update display buffer only after sample was rendered - * only needed for better visual feedback */ - void update_render_result(BL::RenderLayer &b_rlay, RenderTile &rtile); - void update_render_tile(RenderTile &rtile, bool highlight); - + void full_buffer_written(string_view filename); /* interactive updates */ void synchronize(BL::Depsgraph &b_depsgraph); /* drawing */ - bool draw(int w, int h); + void draw(BL::SpaceImageEditor &space_image); + void view_draw(int w, int h); void tag_redraw(); void tag_update(); void get_status(string &status, string &substatus); - void get_kernel_status(string &kernel_status); void get_progress(float &progress, double &total_time, double &render_time); void test_cancel(); void update_status_progress(); @@ -104,8 +99,7 @@ class BlenderSession { BL::RenderSettings b_render; BL::Depsgraph b_depsgraph; /* NOTE: Blender's scene might become invalid after call - * free_blender_memory_if_possible(). - */ + * #free_blender_memory_if_possible(). */ BL::Scene b_scene; BL::SpaceView3D b_v3d; BL::RegionView3D b_rv3d; @@ -123,6 +117,8 @@ class BlenderSession { void *python_thread_state; + bool use_developer_ui; + /* Global state which is common for all render sessions created from Blender. * Usually denotes command line arguments. */ @@ -134,41 +130,35 @@ class BlenderSession { */ static bool headless; - /* ** Resumable render ** */ - - /* Overall number of chunks in which the sample range is to be divided. */ - static int num_resumable_chunks; - - /* Current resumable chunk index to render. */ - static int current_resumable_chunk; - - /* Alternative to single-chunk rendering to render a range of chunks. */ - static int start_resumable_chunk; - static int end_resumable_chunk; - static bool print_render_stats; protected: void stamp_view_layer_metadata(Scene *scene, const string &view_layer_name); - void do_write_update_render_result(BL::RenderLayer &b_rlay, - RenderTile &rtile, - bool do_update_only); - void do_write_update_render_tile(RenderTile &rtile, - bool do_update_only, - bool do_read_only, - bool highlight); + /* Check whether session error happened. + * If so, it is reported to the render engine and true is returned. + * Otherwise false is returned. */ + bool check_and_report_session_error(); void builtin_images_load(); - /* Update tile manager to reflect resumable render settings. */ - void update_resumable_tile_manager(int num_samples); - /* Is used after each render layer synchronization is done with the goal * of freeing render engine data which is held from Blender side (for * example, dependency graph). */ void free_blender_memory_if_possible(); + + void ensure_display_driver_if_needed(); + + struct { + thread_mutex mutex; + int last_pass_index = -1; + } draw_state_; + + /* NOTE: The BlenderSession references the display driver. */ + BlenderDisplayDriver *display_driver_ = nullptr; + + vector full_buffer_files_; }; CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_shader.cpp b/intern/cycles/blender/shader.cpp similarity index 95% rename from intern/cycles/blender/blender_shader.cpp rename to intern/cycles/blender/shader.cpp index de7b2761d00..0cd9052b47a 100644 --- a/intern/cycles/blender/blender_shader.cpp +++ b/intern/cycles/blender/shader.cpp @@ -14,25 +14,26 @@ * limitations under the License. */ -#include "render/background.h" -#include "render/colorspace.h" -#include "render/graph.h" -#include "render/light.h" -#include "render/nodes.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/shader.h" +#include "scene/background.h" +#include "scene/colorspace.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" -#include "blender/blender_image.h" -#include "blender/blender_sync.h" -#include "blender/blender_texture.h" -#include "blender/blender_util.h" +#include "blender/image.h" +#include "blender/sync.h" +#include "blender/texture.h" +#include "blender/util.h" -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_task.h" +#include "util/debug.h" +#include "util/foreach.h" +#include "util/set.h" +#include "util/string.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN @@ -278,7 +279,7 @@ static ShaderNode *add_node(Scene *scene, array curve_mapping_curves; float min_x, max_x; curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, true); - curvemapping_minmax(mapping, true, &min_x, &max_x); + curvemapping_minmax(mapping, 4, &min_x, &max_x); curves->set_min_x(min_x); curves->set_max_x(max_x); curves->set_curves(curve_mapping_curves); @@ -291,12 +292,25 @@ static ShaderNode *add_node(Scene *scene, array curve_mapping_curves; float min_x, max_x; curvemapping_color_to_array(mapping, curve_mapping_curves, RAMP_TABLE_SIZE, false); - curvemapping_minmax(mapping, false, &min_x, &max_x); + curvemapping_minmax(mapping, 3, &min_x, &max_x); curves->set_min_x(min_x); curves->set_max_x(max_x); curves->set_curves(curve_mapping_curves); node = curves; } + else if (b_node.is_a(&RNA_ShaderNodeFloatCurve)) { + BL::ShaderNodeFloatCurve b_curve_node(b_node); + BL::CurveMapping mapping(b_curve_node.mapping()); + FloatCurveNode *curve = graph->create_node(); + array curve_mapping_curve; + float min_x, max_x; + curvemapping_float_to_array(mapping, curve_mapping_curve, RAMP_TABLE_SIZE); + curvemapping_minmax(mapping, 1, &min_x, &max_x); + curve->set_min_x(min_x); + curve->set_max_x(max_x); + curve->set_curve(curve_mapping_curve); + node = curve; + } else if (b_node.is_a(&RNA_ShaderNodeValToRGB)) { RGBRampNode *ramp = graph->create_node(); BL::ShaderNodeValToRGB b_ramp_node(b_node); @@ -475,17 +489,14 @@ static ShaderNode *add_node(Scene *scene, SubsurfaceScatteringNode *subsurface = graph->create_node(); switch (b_subsurface_node.falloff()) { - case BL::ShaderNodeSubsurfaceScattering::falloff_CUBIC: - subsurface->set_falloff(CLOSURE_BSSRDF_CUBIC_ID); - break; - case BL::ShaderNodeSubsurfaceScattering::falloff_GAUSSIAN: - subsurface->set_falloff(CLOSURE_BSSRDF_GAUSSIAN_ID); - break; case BL::ShaderNodeSubsurfaceScattering::falloff_BURLEY: - subsurface->set_falloff(CLOSURE_BSSRDF_BURLEY_ID); + subsurface->set_method(CLOSURE_BSSRDF_BURLEY_ID); + break; + case BL::ShaderNodeSubsurfaceScattering::falloff_RANDOM_WALK_FIXED_RADIUS: + subsurface->set_method(CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); break; case BL::ShaderNodeSubsurfaceScattering::falloff_RANDOM_WALK: - subsurface->set_falloff(CLOSURE_BSSRDF_RANDOM_WALK_ID); + subsurface->set_method(CLOSURE_BSSRDF_RANDOM_WALK_ID); break; } @@ -598,10 +609,13 @@ static ShaderNode *add_node(Scene *scene, } switch (b_principled_node.subsurface_method()) { case BL::ShaderNodeBsdfPrincipled::subsurface_method_BURLEY: - principled->set_subsurface_method(CLOSURE_BSSRDF_PRINCIPLED_ID); + principled->set_subsurface_method(CLOSURE_BSSRDF_BURLEY_ID); + break; + case BL::ShaderNodeBsdfPrincipled::subsurface_method_RANDOM_WALK_FIXED_RADIUS: + principled->set_subsurface_method(CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); break; case BL::ShaderNodeBsdfPrincipled::subsurface_method_RANDOM_WALK: - principled->set_subsurface_method(CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID); + principled->set_subsurface_method(CLOSURE_BSSRDF_RANDOM_WALK_ID); break; } node = principled; @@ -1360,10 +1374,12 @@ void BlenderSync::sync_materials(BL::Depsgraph &b_depsgraph, bool update_all) void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, bool update_all) { Background *background = scene->background; + Integrator *integrator = scene->integrator; + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); BL::World b_world = b_scene.world(); - BlenderViewportParameters new_viewport_parameters(b_v3d); + BlenderViewportParameters new_viewport_parameters(b_v3d, use_developer_ui); if (world_recalc || update_all || b_world.ptr.data != world_map || viewport_parameters.shader_modified(new_viewport_parameters)) { @@ -1451,15 +1467,8 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, graph->connect(background->output("Background"), out->input("Surface")); } + /* Visibility */ if (b_world) { - /* AO */ - BL::WorldLighting b_light = b_world.light_settings(); - - background->set_use_ao(b_light.use_ambient_occlusion()); - background->set_ao_factor(b_light.ao_factor()); - background->set_ao_distance(b_light.distance()); - - /* visibility */ PointerRNA cvisibility = RNA_pointer_get(&b_world.ptr, "cycles_visibility"); uint visibility = 0; @@ -1471,17 +1480,38 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_visibility(visibility); } - else { - background->set_use_ao(false); - background->set_ao_factor(0.0f); - background->set_ao_distance(FLT_MAX); - } shader->set_graph(graph); shader->tag_update(scene); } - PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + /* Fast GI */ + if (b_world) { + BL::WorldLighting b_light = b_world.light_settings(); + enum { FAST_GI_METHOD_REPLACE = 0, FAST_GI_METHOD_ADD = 1, FAST_GI_METHOD_NUM }; + + const bool use_fast_gi = get_boolean(cscene, "use_fast_gi"); + if (use_fast_gi) { + const int fast_gi_method = get_enum( + cscene, "fast_gi_method", FAST_GI_METHOD_NUM, FAST_GI_METHOD_REPLACE); + integrator->set_ao_factor((fast_gi_method == FAST_GI_METHOD_REPLACE) ? b_light.ao_factor() : + 0.0f); + integrator->set_ao_additive_factor( + (fast_gi_method == FAST_GI_METHOD_ADD) ? b_light.ao_factor() : 0.0f); + } + else { + integrator->set_ao_factor(0.0f); + integrator->set_ao_additive_factor(0.0f); + } + + integrator->set_ao_distance(b_light.distance()); + } + else { + integrator->set_ao_factor(0.0f); + integrator->set_ao_additive_factor(0.0f); + integrator->set_ao_distance(10.0f); + } + background->set_transparent(b_scene.render().film_transparent()); if (background->get_transparent()) { @@ -1496,7 +1526,6 @@ void BlenderSync::sync_world(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d, background->set_use_shader(view_layer.use_background_shader || viewport_parameters.use_custom_shader()); - background->set_use_ao(background->get_use_ao() && view_layer.use_background_ao); background->tag_update(scene); } diff --git a/intern/cycles/blender/blender_sync.cpp b/intern/cycles/blender/sync.cpp similarity index 67% rename from intern/cycles/blender/blender_sync.cpp rename to intern/cycles/blender/sync.cpp index 26d64b7bf85..cf0b77a9b16 100644 --- a/intern/cycles/blender/blender_sync.cpp +++ b/intern/cycles/blender/sync.cpp @@ -14,33 +14,33 @@ * limitations under the License. */ -#include "render/background.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/film.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/procedural.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #include "device/device.h" -#include "blender/blender_device.h" -#include "blender/blender_session.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/device.h" +#include "blender/session.h" +#include "blender/sync.h" +#include "blender/util.h" -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_opengl.h" -#include "util/util_openimagedenoise.h" +#include "util/debug.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/opengl.h" +#include "util/openimagedenoise.h" CCL_NAMESPACE_BEGIN @@ -53,6 +53,7 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine, BL::Scene &b_scene, Scene *scene, bool preview, + bool use_developer_ui, Progress &progress) : b_engine(b_engine), b_data(b_data), @@ -68,6 +69,7 @@ BlenderSync::BlenderSync(BL::RenderEngine &b_engine, scene(scene), preview(preview), experimental(false), + use_developer_ui(use_developer_ui), dicing_rate(1.0f), max_subdivisions(12), progress(progress), @@ -224,7 +226,7 @@ void BlenderSync::sync_recalc(BL::Depsgraph &b_depsgraph, BL::SpaceView3D &b_v3d } if (b_v3d) { - BlenderViewportParameters new_viewport_parameters(b_v3d); + BlenderViewportParameters new_viewport_parameters(b_v3d, use_developer_ui); if (viewport_parameters.shader_modified(new_viewport_parameters)) { world_recalc = true; @@ -251,9 +253,13 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, BL::ViewLayer b_view_layer = b_depsgraph.view_layer_eval(); + /* TODO(sergey): This feels weak to pass view layer to the integrator, and even weaker to have an + * implicit check on whether it is a background render or not. What is the nicer thing here? */ + const bool background = !b_v3d; + sync_view_layer(b_view_layer); - sync_integrator(); - sync_film(b_v3d); + sync_integrator(b_view_layer, background); + sync_film(b_view_layer, b_v3d); sync_shaders(b_depsgraph, b_v3d); sync_images(); @@ -280,7 +286,7 @@ void BlenderSync::sync_data(BL::RenderSettings &b_render, /* Integrator */ -void BlenderSync::sync_integrator() +void BlenderSync::sync_integrator(BL::ViewLayer &b_view_layer, bool background) { PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); @@ -328,58 +334,42 @@ void BlenderSync::sync_integrator() integrator->set_motion_blur(view_layer.use_motion_blur); } - integrator->set_method((Integrator::Method)get_enum( - cscene, "progressive", Integrator::NUM_METHODS, Integrator::PATH)); - - integrator->set_sample_all_lights_direct(get_boolean(cscene, "sample_all_lights_direct")); - integrator->set_sample_all_lights_indirect(get_boolean(cscene, "sample_all_lights_indirect")); integrator->set_light_sampling_threshold(get_float(cscene, "light_sampling_threshold")); SamplingPattern sampling_pattern = (SamplingPattern)get_enum( cscene, "sampling_pattern", SAMPLING_NUM_PATTERNS, SAMPLING_PATTERN_SOBOL); - - int adaptive_min_samples = INT_MAX; - - if (RNA_boolean_get(&cscene, "use_adaptive_sampling")) { - sampling_pattern = SAMPLING_PATTERN_PMJ; - adaptive_min_samples = get_int(cscene, "adaptive_min_samples"); - integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold")); - } - else { - integrator->set_adaptive_threshold(0.0f); - } - integrator->set_sampling_pattern(sampling_pattern); - int diffuse_samples = get_int(cscene, "diffuse_samples"); - int glossy_samples = get_int(cscene, "glossy_samples"); - int transmission_samples = get_int(cscene, "transmission_samples"); - int ao_samples = get_int(cscene, "ao_samples"); - int mesh_light_samples = get_int(cscene, "mesh_light_samples"); - int subsurface_samples = get_int(cscene, "subsurface_samples"); - int volume_samples = get_int(cscene, "volume_samples"); - - if (get_boolean(cscene, "use_square_samples")) { - integrator->set_diffuse_samples(diffuse_samples * diffuse_samples); - integrator->set_glossy_samples(glossy_samples * glossy_samples); - integrator->set_transmission_samples(transmission_samples * transmission_samples); - integrator->set_ao_samples(ao_samples * ao_samples); - integrator->set_mesh_light_samples(mesh_light_samples * mesh_light_samples); - integrator->set_subsurface_samples(subsurface_samples * subsurface_samples); - integrator->set_volume_samples(volume_samples * volume_samples); - adaptive_min_samples = min(adaptive_min_samples * adaptive_min_samples, INT_MAX); + bool use_adaptive_sampling = false; + if (preview) { + use_adaptive_sampling = RNA_boolean_get(&cscene, "use_preview_adaptive_sampling"); + integrator->set_use_adaptive_sampling(use_adaptive_sampling); + integrator->set_adaptive_threshold(get_float(cscene, "preview_adaptive_threshold")); + integrator->set_adaptive_min_samples(get_int(cscene, "preview_adaptive_min_samples")); } else { - integrator->set_diffuse_samples(diffuse_samples); - integrator->set_glossy_samples(glossy_samples); - integrator->set_transmission_samples(transmission_samples); - integrator->set_ao_samples(ao_samples); - integrator->set_mesh_light_samples(mesh_light_samples); - integrator->set_subsurface_samples(subsurface_samples); - integrator->set_volume_samples(volume_samples); + use_adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling"); + integrator->set_use_adaptive_sampling(use_adaptive_sampling); + integrator->set_adaptive_threshold(get_float(cscene, "adaptive_threshold")); + integrator->set_adaptive_min_samples(get_int(cscene, "adaptive_min_samples")); } - integrator->set_adaptive_min_samples(adaptive_min_samples); + int samples = get_int(cscene, "samples"); + float scrambling_distance = get_float(cscene, "scrambling_distance"); + bool adaptive_scrambling_distance = get_boolean(cscene, "adaptive_scrambling_distance"); + if (adaptive_scrambling_distance) { + scrambling_distance *= 4.0f / sqrtf(samples); + } + + /* only use scrambling distance in the viewport if user wants to and disable with AS */ + bool preview_scrambling_distance = get_boolean(cscene, "preview_scrambling_distance"); + if ((preview && !preview_scrambling_distance) || use_adaptive_sampling) + scrambling_distance = 1.0f; + + if (scrambling_distance != 1.0f) { + VLOG(3) << "Using scrambling distance: " << scrambling_distance; + } + integrator->set_scrambling_distance(scrambling_distance); if (get_boolean(cscene, "use_fast_gi")) { if (preview) { @@ -393,20 +383,38 @@ void BlenderSync::sync_integrator() integrator->set_ao_bounces(0); } - /* UPDATE_NONE as we don't want to tag the integrator as modified, just tag dependent things */ + const DenoiseParams denoise_params = get_denoise_params(b_scene, b_view_layer, background); + integrator->set_use_denoise(denoise_params.use); + + /* Only update denoiser parameters if the denoiser is actually used. This allows to tweak + * denoiser parameters before enabling it without render resetting on every change. The downside + * is that the interface and the integrator are technically out of sync. */ + if (denoise_params.use) { + integrator->set_denoiser_type(denoise_params.type); + integrator->set_denoise_start_sample(denoise_params.start_sample); + integrator->set_use_denoise_pass_albedo(denoise_params.use_pass_albedo); + integrator->set_use_denoise_pass_normal(denoise_params.use_pass_normal); + integrator->set_denoiser_prefilter(denoise_params.prefilter); + } + + /* UPDATE_NONE as we don't want to tag the integrator as modified (this was done by the + * set calls above), but we need to make sure that the dependent things are tagged. */ integrator->tag_update(scene, Integrator::UPDATE_NONE); } /* Film */ -void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) +void BlenderSync::sync_film(BL::ViewLayer &b_view_layer, BL::SpaceView3D &b_v3d) { PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles"); Film *film = scene->film; if (b_v3d) { - film->set_display_pass(update_viewport_display_passes(b_v3d, scene->passes)); + const BlenderViewportParameters new_viewport_parameters(b_v3d, use_developer_ui); + film->set_display_pass(new_viewport_parameters.display_pass); + film->set_show_active_pixels(new_viewport_parameters.show_active_pixels); } film->set_exposure(get_float(cscene, "film_exposure")); @@ -434,6 +442,15 @@ void BlenderSync::sync_film(BL::SpaceView3D &b_v3d) break; } } + + /* Blender viewport does not support proper shadow catcher compositing, so force an approximate + * mode to improve visual feedback. */ + if (b_v3d) { + film->set_use_approximate_shadow_catcher(true); + } + else { + film->set_use_approximate_shadow_catcher(!get_boolean(crl, "use_pass_shadow_catcher")); + } } /* Render Layer */ @@ -444,7 +461,6 @@ void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer) /* Filter. */ view_layer.use_background_shader = b_view_layer.use_sky(); - view_layer.use_background_ao = b_view_layer.use_ao(); /* Always enable surfaces for baking, otherwise there is nothing to bake to. */ view_layer.use_surfaces = b_view_layer.use_solid() || scene->bake_manager->get_baking(); view_layer.use_hair = b_view_layer.use_strand(); @@ -464,10 +480,7 @@ void BlenderSync::sync_view_layer(BL::ViewLayer &b_view_layer) if (use_layer_samples != 2) { int samples = b_view_layer.samples(); - if (get_boolean(cscene, "use_square_samples")) - view_layer.samples = samples * samples; - else - view_layer.samples = samples; + view_layer.samples = samples; } } @@ -499,7 +512,8 @@ void BlenderSync::sync_images() } /* Passes */ -PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass) + +static PassType get_blender_pass_type(BL::RenderPass &b_pass) { string name = b_pass.name(); #define MAP_PASS(passname, passtype) \ @@ -507,10 +521,15 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass) return passtype; \ } \ ((void)0) + /* NOTE: Keep in sync with defined names from DNA_scene_types.h */ + MAP_PASS("Combined", PASS_COMBINED); + MAP_PASS("Noisy Image", PASS_COMBINED); + MAP_PASS("Depth", PASS_DEPTH); MAP_PASS("Mist", PASS_MIST); + MAP_PASS("Position", PASS_POSITION); MAP_PASS("Normal", PASS_NORMAL); MAP_PASS("IndexOB", PASS_OBJECT_ID); MAP_PASS("UV", PASS_UV); @@ -539,118 +558,87 @@ PassType BlenderSync::get_pass_type(BL::RenderPass &b_pass) MAP_PASS("BakePrimitive", PASS_BAKE_PRIMITIVE); MAP_PASS("BakeDifferential", PASS_BAKE_DIFFERENTIAL); - MAP_PASS("Debug Render Time", PASS_RENDER_TIME); + MAP_PASS("Denoising Normal", PASS_DENOISING_NORMAL); + MAP_PASS("Denoising Albedo", PASS_DENOISING_ALBEDO); + MAP_PASS("Denoising Depth", PASS_DENOISING_DEPTH); + + MAP_PASS("Shadow Catcher", PASS_SHADOW_CATCHER); + MAP_PASS("Noisy Shadow Catcher", PASS_SHADOW_CATCHER); + MAP_PASS("AdaptiveAuxBuffer", PASS_ADAPTIVE_AUX_BUFFER); MAP_PASS("Debug Sample Count", PASS_SAMPLE_COUNT); + if (string_startswith(name, cryptomatte_prefix)) { return PASS_CRYPTOMATTE; } + #undef MAP_PASS return PASS_NONE; } -int BlenderSync::get_denoising_pass(BL::RenderPass &b_pass) +static Pass *pass_add(Scene *scene, + PassType type, + const char *name, + PassMode mode = PassMode::DENOISED) { - string name = b_pass.name(); + Pass *pass = scene->create_node(); - if (name == "Noisy Image") - return DENOISING_PASS_PREFILTERED_COLOR; + pass->set_type(type); + pass->set_name(ustring(name)); + pass->set_mode(mode); - if (name.substr(0, 10) != "Denoising ") { - return -1; - } - name = name.substr(10); - -#define MAP_PASS(passname, offset) \ - if (name == passname) { \ - return offset; \ - } \ - ((void)0) - MAP_PASS("Normal", DENOISING_PASS_PREFILTERED_NORMAL); - MAP_PASS("Albedo", DENOISING_PASS_PREFILTERED_ALBEDO); - MAP_PASS("Depth", DENOISING_PASS_PREFILTERED_DEPTH); - MAP_PASS("Shadowing", DENOISING_PASS_PREFILTERED_SHADOWING); - MAP_PASS("Variance", DENOISING_PASS_PREFILTERED_VARIANCE); - MAP_PASS("Intensity", DENOISING_PASS_PREFILTERED_INTENSITY); - MAP_PASS("Clean", DENOISING_PASS_CLEAN); -#undef MAP_PASS - - return -1; + return pass; } -vector BlenderSync::sync_render_passes(BL::Scene &b_scene, - BL::RenderLayer &b_rlay, - BL::ViewLayer &b_view_layer, - bool adaptive_sampling, - const DenoiseParams &denoising) +void BlenderSync::sync_render_passes(BL::RenderLayer &b_rlay, BL::ViewLayer &b_view_layer) { - vector passes; + PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); - /* loop over passes */ + /* Delete all existing passes. */ + set clear_passes(scene->passes.begin(), scene->passes.end()); + scene->delete_nodes(clear_passes); + + /* Always add combined pass. */ + pass_add(scene, PASS_COMBINED, "Combined"); + + /* Blender built-in data and light passes. */ for (BL::RenderPass &b_pass : b_rlay.passes) { - PassType pass_type = get_pass_type(b_pass); + const PassType pass_type = get_blender_pass_type(b_pass); + + if (pass_type == PASS_NONE) { + LOG(ERROR) << "Unknown pass " << b_pass.name(); + continue; + } if (pass_type == PASS_MOTION && (b_view_layer.use_motion_blur() && b_scene.render().use_motion_blur())) { continue; } - if (pass_type != PASS_NONE) - Pass::add(pass_type, passes, b_pass.name().c_str()); + + pass_add(scene, pass_type, b_pass.name().c_str()); } PointerRNA crl = RNA_pointer_get(&b_view_layer.ptr, "cycles"); - int denoising_flags = 0; - if (denoising.use || denoising.store_passes) { - if (denoising.type == DENOISER_NLM) { -#define MAP_OPTION(name, flag) \ - if (!get_boolean(crl, name)) { \ - denoising_flags |= flag; \ - } \ - ((void)0) - MAP_OPTION("denoising_diffuse_direct", DENOISING_CLEAN_DIFFUSE_DIR); - MAP_OPTION("denoising_diffuse_indirect", DENOISING_CLEAN_DIFFUSE_IND); - MAP_OPTION("denoising_glossy_direct", DENOISING_CLEAN_GLOSSY_DIR); - MAP_OPTION("denoising_glossy_indirect", DENOISING_CLEAN_GLOSSY_IND); - MAP_OPTION("denoising_transmission_direct", DENOISING_CLEAN_TRANSMISSION_DIR); - MAP_OPTION("denoising_transmission_indirect", DENOISING_CLEAN_TRANSMISSION_IND); -#undef MAP_OPTION - } - b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str()); - } - scene->film->set_denoising_flags(denoising_flags); - - if (denoising.store_passes) { - b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str()); - if (denoising.type == DENOISER_NLM) { - b_engine.add_pass("Denoising Shadowing", 1, "X", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Variance", 3, "RGB", b_view_layer.name().c_str()); - b_engine.add_pass("Denoising Intensity", 1, "X", b_view_layer.name().c_str()); - } - - if (scene->film->get_denoising_flags() & DENOISING_CLEAN_ALL_PASSES) { - b_engine.add_pass("Denoising Clean", 3, "RGB", b_view_layer.name().c_str()); - } - } - - if (get_boolean(crl, "pass_debug_render_time")) { - b_engine.add_pass("Debug Render Time", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_RENDER_TIME, passes, "Debug Render Time"); - } + /* Debug passes. */ if (get_boolean(crl, "pass_debug_sample_count")) { b_engine.add_pass("Debug Sample Count", 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_SAMPLE_COUNT, passes, "Debug Sample Count"); + pass_add(scene, PASS_SAMPLE_COUNT, "Debug Sample Count"); } + + /* Cycles specific passes. */ if (get_boolean(crl, "use_pass_volume_direct")) { b_engine.add_pass("VolumeDir", 3, "RGB", b_view_layer.name().c_str()); - Pass::add(PASS_VOLUME_DIRECT, passes, "VolumeDir"); + pass_add(scene, PASS_VOLUME_DIRECT, "VolumeDir"); } if (get_boolean(crl, "use_pass_volume_indirect")) { b_engine.add_pass("VolumeInd", 3, "RGB", b_view_layer.name().c_str()); - Pass::add(PASS_VOLUME_INDIRECT, passes, "VolumeInd"); + pass_add(scene, PASS_VOLUME_INDIRECT, "VolumeInd"); + } + if (get_boolean(crl, "use_pass_shadow_catcher")) { + b_engine.add_pass("Shadow Catcher", 3, "RGB", b_view_layer.name().c_str()); + pass_add(scene, PASS_SHADOW_CATCHER, "Shadow Catcher"); } /* Cryptomatte stores two ID/weight pairs per RGBA layer. @@ -662,7 +650,7 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, for (int i = 0; i < crypto_depth; i++) { string passname = cryptomatte_prefix + string_printf("Object%02d", i); b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str()); + pass_add(scene, PASS_CRYPTOMATTE, passname.c_str()); } cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_OBJECT); } @@ -670,7 +658,7 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, for (int i = 0; i < crypto_depth; i++) { string passname = cryptomatte_prefix + string_printf("Material%02d", i); b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str()); + pass_add(scene, PASS_CRYPTOMATTE, passname.c_str()); } cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_MATERIAL); } @@ -678,22 +666,36 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, for (int i = 0; i < crypto_depth; i++) { string passname = cryptomatte_prefix + string_printf("Asset%02d", i); b_engine.add_pass(passname.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_CRYPTOMATTE, passes, passname.c_str()); + pass_add(scene, PASS_CRYPTOMATTE, passname.c_str()); } cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_ASSET); } - if (b_view_layer.use_pass_cryptomatte_accurate() && cryptomatte_passes != CRYPT_NONE) { - cryptomatte_passes = (CryptomatteType)(cryptomatte_passes | CRYPT_ACCURATE); - } scene->film->set_cryptomatte_passes(cryptomatte_passes); - if (adaptive_sampling) { - Pass::add(PASS_ADAPTIVE_AUX_BUFFER, passes); - if (!get_boolean(crl, "pass_debug_sample_count")) { - Pass::add(PASS_SAMPLE_COUNT, passes); + /* Denoising passes. */ + const bool use_denoising = get_boolean(cscene, "use_denoising") && + get_boolean(crl, "use_denoising"); + const bool store_denoising_passes = get_boolean(crl, "denoising_store_passes"); + if (use_denoising) { + b_engine.add_pass("Noisy Image", 4, "RGBA", b_view_layer.name().c_str()); + pass_add(scene, PASS_COMBINED, "Noisy Image", PassMode::NOISY); + if (get_boolean(crl, "use_pass_shadow_catcher")) { + b_engine.add_pass("Noisy Shadow Catcher", 3, "RGB", b_view_layer.name().c_str()); + pass_add(scene, PASS_SHADOW_CATCHER, "Noisy Shadow Catcher", PassMode::NOISY); } } + if (store_denoising_passes) { + b_engine.add_pass("Denoising Normal", 3, "XYZ", b_view_layer.name().c_str()); + pass_add(scene, PASS_DENOISING_NORMAL, "Denoising Normal", PassMode::NOISY); + b_engine.add_pass("Denoising Albedo", 3, "RGB", b_view_layer.name().c_str()); + pass_add(scene, PASS_DENOISING_ALBEDO, "Denoising Albedo", PassMode::NOISY); + + b_engine.add_pass("Denoising Depth", 1, "Z", b_view_layer.name().c_str()); + pass_add(scene, PASS_DENOISING_DEPTH, "Denoising Depth", PassMode::NOISY); + } + + /* Custom AOV passes. */ BL::ViewLayer::aovs_iterator b_aov_iter; for (b_view_layer.aovs.begin(b_aov_iter); b_aov_iter != b_view_layer.aovs.end(); ++b_aov_iter) { BL::AOV b_aov(*b_aov_iter); @@ -706,28 +708,15 @@ vector BlenderSync::sync_render_passes(BL::Scene &b_scene, if (is_color) { b_engine.add_pass(name.c_str(), 4, "RGBA", b_view_layer.name().c_str()); - Pass::add(PASS_AOV_COLOR, passes, name.c_str()); + pass_add(scene, PASS_AOV_COLOR, name.c_str()); } else { b_engine.add_pass(name.c_str(), 1, "X", b_view_layer.name().c_str()); - Pass::add(PASS_AOV_VALUE, passes, name.c_str()); + pass_add(scene, PASS_AOV_VALUE, name.c_str()); } } - scene->film->set_denoising_data_pass(denoising.use || denoising.store_passes); - scene->film->set_denoising_clean_pass(scene->film->get_denoising_flags() & - DENOISING_CLEAN_ALL_PASSES); - scene->film->set_denoising_prefiltered_pass(denoising.store_passes && - denoising.type == DENOISER_NLM); scene->film->set_pass_alpha_threshold(b_view_layer.pass_alpha_threshold()); - - if (!Pass::equals(passes, scene->passes)) { - scene->film->tag_passes_update(scene, passes); - scene->film->tag_modified(); - scene->integrator->tag_update(scene, Integrator::UPDATE_ALL); - } - - return passes; } void BlenderSync::free_data_after_sync(BL::Depsgraph &b_depsgraph) @@ -773,9 +762,9 @@ SceneParams BlenderSync::get_scene_params(BL::Scene &b_scene, bool background) params.shadingsystem = SHADINGSYSTEM_OSL; if (background || DebugFlags().viewport_static_bvh) - params.bvh_type = SceneParams::BVH_STATIC; + params.bvh_type = BVH_TYPE_STATIC; else - params.bvh_type = SceneParams::BVH_DYNAMIC; + params.bvh_type = BVH_TYPE_DYNAMIC; params.use_bvh_spatial_split = RNA_boolean_get(&cscene, "debug_use_spatial_splits"); params.use_bvh_unaligned_nodes = RNA_boolean_get(&cscene, "debug_use_hair_bvh"); @@ -818,8 +807,7 @@ bool BlenderSync::get_session_pause(BL::Scene &b_scene, bool background) SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, BL::Preferences &b_preferences, BL::Scene &b_scene, - bool background, - BL::ViewLayer b_view_layer) + bool background) { SessionParams params; PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); @@ -827,7 +815,8 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, /* feature set */ params.experimental = (get_enum(cscene, "feature_set") != 0); - /* Background */ + /* Headless and background rendering. */ + params.headless = BlenderSession::headless; params.background = background; /* Device */ @@ -836,111 +825,26 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, /* samples */ int samples = get_int(cscene, "samples"); - int aa_samples = get_int(cscene, "aa_samples"); int preview_samples = get_int(cscene, "preview_samples"); - int preview_aa_samples = get_int(cscene, "preview_aa_samples"); - if (get_boolean(cscene, "use_square_samples")) { - aa_samples = aa_samples * aa_samples; - preview_aa_samples = preview_aa_samples * preview_aa_samples; - - samples = samples * samples; - preview_samples = preview_samples * preview_samples; - } - - if (get_enum(cscene, "progressive") == 0 && params.device.has_branched_path) { - if (background) { - params.samples = aa_samples; - } - else { - params.samples = preview_aa_samples; - if (params.samples == 0) - params.samples = INT_MAX; - } + if (background) { + params.samples = samples; } else { - if (background) { - params.samples = samples; - } - else { - params.samples = preview_samples; - if (params.samples == 0) - params.samples = INT_MAX; - } + params.samples = preview_samples; + if (params.samples == 0) + params.samples = INT_MAX; } /* Clamp samples. */ params.samples = min(params.samples, Integrator::MAX_SAMPLES); - /* Adaptive sampling. */ - params.adaptive_sampling = RNA_boolean_get(&cscene, "use_adaptive_sampling"); - - /* tiles */ - const bool is_cpu = (params.device.type == DEVICE_CPU); - if (!is_cpu && !background) { - /* currently GPU could be much slower than CPU when using tiles, - * still need to be investigated, but meanwhile make it possible - * to work in viewport smoothly - */ - int debug_tile_size = get_int(cscene, "debug_tile_size"); - - params.tile_size = make_int2(debug_tile_size, debug_tile_size); - } - else { - int tile_x = b_engine.tile_x(); - int tile_y = b_engine.tile_y(); - - params.tile_size = make_int2(tile_x, tile_y); - } - - if ((BlenderSession::headless == false) && background) { - params.tile_order = (TileOrder)get_enum(cscene, "tile_order"); - } - else { - params.tile_order = TILE_BOTTOM_TO_TOP; - } - - /* Denoising */ - params.denoising = get_denoise_params(b_scene, b_view_layer, background); - - if (params.denoising.use) { - /* Add additional denoising devices if we are rendering and denoising - * with different devices. */ - params.device.add_denoising_devices(params.denoising.type); - - /* Check if denoiser is supported by device. */ - if (!(params.device.denoisers & params.denoising.type)) { - params.denoising.use = false; - } - } - /* Viewport Performance */ - params.start_resolution = get_int(cscene, "preview_start_resolution"); params.pixel_size = b_engine.get_preview_pixel_size(b_scene); - /* other parameters */ - params.cancel_timeout = (double)get_float(cscene, "debug_cancel_timeout"); - params.reset_timeout = (double)get_float(cscene, "debug_reset_timeout"); - params.text_timeout = (double)get_float(cscene, "debug_text_timeout"); - - /* progressive refine */ - BL::RenderSettings b_r = b_scene.render(); - params.progressive_refine = b_engine.is_preview() || - get_boolean(cscene, "use_progressive_refine"); - if (b_r.use_save_buffers() || params.adaptive_sampling) - params.progressive_refine = false; - if (background) { - if (params.progressive_refine) - params.progressive = true; - else - params.progressive = false; - - params.start_resolution = INT_MAX; params.pixel_size = 1; } - else - params.progressive = true; /* shading system - scene level needs full refresh */ const bool shadingsystem = RNA_boolean_get(&cscene, "shading_system"); @@ -950,19 +854,30 @@ SessionParams BlenderSync::get_session_params(BL::RenderEngine &b_engine, else if (shadingsystem == 1) params.shadingsystem = SHADINGSYSTEM_OSL; - /* Color management. */ - params.display_buffer_linear = b_engine.support_display_space_shader(b_scene); - - if (b_engine.is_preview()) { - /* For preview rendering we're using same timeout as - * blender's job update. - */ - params.progressive_update_timeout = 0.1; + /* Time limit. */ + if (background) { + params.time_limit = get_float(cscene, "time_limit"); + } + else { + /* For the viewport it kind of makes more sense to think in terms of the noise floor, which is + * usually higher than acceptable level for the final frame. */ + /* TODO: It might be useful to support time limit in the viewport as well, but needs some + * extra thoughts and input. */ + params.time_limit = 0.0; } + /* Profiling. */ params.use_profiling = params.device.has_profiling && !b_engine.is_preview() && background && BlenderSession::print_render_stats; + if (background) { + params.use_auto_tile = RNA_boolean_get(&cscene, "use_auto_tile"); + params.tile_size = max(get_int(cscene, "tile_size"), 8); + } + else { + params.use_auto_tile = false; + } + return params; } @@ -970,33 +885,34 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene, BL::ViewLayer &b_view_layer, bool background) { + enum DenoiserInput { + DENOISER_INPUT_RGB = 1, + DENOISER_INPUT_RGB_ALBEDO = 2, + DENOISER_INPUT_RGB_ALBEDO_NORMAL = 3, + + DENOISER_INPUT_NUM, + }; + DenoiseParams denoising; PointerRNA cscene = RNA_pointer_get(&b_scene.ptr, "cycles"); + int input_passes = -1; + if (background) { /* Final Render Denoising */ denoising.use = get_boolean(cscene, "use_denoising"); denoising.type = (DenoiserType)get_enum(cscene, "denoiser", DENOISER_NUM, DENOISER_NONE); + denoising.prefilter = (DenoiserPrefilter)get_enum( + cscene, "denoising_prefilter", DENOISER_PREFILTER_NUM, DENOISER_PREFILTER_NONE); + + input_passes = (DenoiserInput)get_enum( + cscene, "denoising_input_passes", DENOISER_INPUT_NUM, DENOISER_INPUT_RGB_ALBEDO_NORMAL); if (b_view_layer) { PointerRNA clayer = RNA_pointer_get(&b_view_layer.ptr, "cycles"); if (!get_boolean(clayer, "use_denoising")) { denoising.use = false; } - - denoising.radius = get_int(clayer, "denoising_radius"); - denoising.strength = get_float(clayer, "denoising_strength"); - denoising.feature_strength = get_float(clayer, "denoising_feature_strength"); - denoising.relative_pca = get_boolean(clayer, "denoising_relative_pca"); - - denoising.input_passes = (DenoiserInput)get_enum( - clayer, - (denoising.type == DENOISER_OPTIX) ? "denoising_optix_input_passes" : - "denoising_openimagedenoise_input_passes", - DENOISER_INPUT_NUM, - DENOISER_INPUT_RGB_ALBEDO_NORMAL); - - denoising.store_passes = get_boolean(clayer, "denoising_store_passes"); } } else { @@ -1004,10 +920,12 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene, denoising.use = get_boolean(cscene, "use_preview_denoising"); denoising.type = (DenoiserType)get_enum( cscene, "preview_denoiser", DENOISER_NUM, DENOISER_NONE); + denoising.prefilter = (DenoiserPrefilter)get_enum( + cscene, "preview_denoising_prefilter", DENOISER_PREFILTER_NUM, DENOISER_PREFILTER_FAST); denoising.start_sample = get_int(cscene, "preview_denoising_start_sample"); - denoising.input_passes = (DenoiserInput)get_enum( - cscene, "preview_denoising_input_passes", DENOISER_INPUT_NUM, (int)denoising.input_passes); + input_passes = (DenoiserInput)get_enum( + cscene, "preview_denoising_input_passes", DENOISER_INPUT_NUM, DENOISER_INPUT_RGB_ALBEDO); /* Auto select fastest denoiser. */ if (denoising.type == DENOISER_NONE) { @@ -1023,6 +941,27 @@ DenoiseParams BlenderSync::get_denoise_params(BL::Scene &b_scene, } } + switch (input_passes) { + case DENOISER_INPUT_RGB: + denoising.use_pass_albedo = false; + denoising.use_pass_normal = false; + break; + + case DENOISER_INPUT_RGB_ALBEDO: + denoising.use_pass_albedo = true; + denoising.use_pass_normal = false; + break; + + case DENOISER_INPUT_RGB_ALBEDO_NORMAL: + denoising.use_pass_albedo = true; + denoising.use_pass_normal = true; + break; + + default: + LOG(ERROR) << "Unhandled input passes enum " << input_passes; + break; + } + return denoising; } diff --git a/intern/cycles/blender/blender_sync.h b/intern/cycles/blender/sync.h similarity index 85% rename from intern/cycles/blender/blender_sync.h rename to intern/cycles/blender/sync.h index d25c0ce1bc3..c2377406876 100644 --- a/intern/cycles/blender/blender_sync.h +++ b/intern/cycles/blender/sync.h @@ -22,17 +22,17 @@ #include "RNA_blender_cpp.h" #include "RNA_types.h" -#include "blender/blender_id_map.h" -#include "blender/blender_util.h" -#include "blender/blender_viewport.h" +#include "blender/id_map.h" +#include "blender/util.h" +#include "blender/viewport.h" -#include "render/scene.h" -#include "render/session.h" +#include "scene/scene.h" +#include "session/session.h" -#include "util/util_map.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "util/map.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -60,6 +60,7 @@ class BlenderSync { BL::Scene &b_scene, Scene *scene, bool preview, + bool use_developer_ui, Progress &progress); ~BlenderSync(); @@ -75,12 +76,8 @@ class BlenderSync { int height, void **python_thread_state); void sync_view_layer(BL::ViewLayer &b_view_layer); - vector sync_render_passes(BL::Scene &b_scene, - BL::RenderLayer &b_render_layer, - BL::ViewLayer &b_view_layer, - bool adaptive_sampling, - const DenoiseParams &denoising); - void sync_integrator(); + void sync_render_passes(BL::RenderLayer &b_render_layer, BL::ViewLayer &b_view_layer); + void sync_integrator(BL::ViewLayer &b_view_layer, bool background); void sync_camera(BL::RenderSettings &b_render, BL::Object &b_override, int width, @@ -98,22 +95,13 @@ class BlenderSync { /* get parameters */ static SceneParams get_scene_params(BL::Scene &b_scene, bool background); - static SessionParams get_session_params( - BL::RenderEngine &b_engine, - BL::Preferences &b_userpref, - BL::Scene &b_scene, - bool background, - BL::ViewLayer b_view_layer = BL::ViewLayer(PointerRNA_NULL)); + static SessionParams get_session_params(BL::RenderEngine &b_engine, + BL::Preferences &b_userpref, + BL::Scene &b_scene, + bool background); static bool get_session_pause(BL::Scene &b_scene, bool background); - static BufferParams get_buffer_params(BL::SpaceView3D &b_v3d, - BL::RegionView3D &b_rv3d, - Camera *cam, - int width, - int height, - const bool use_denoiser); - - static PassType get_pass_type(BL::RenderPass &b_pass); - static int get_denoising_pass(BL::RenderPass &b_pass); + static BufferParams get_buffer_params( + BL::SpaceView3D &b_v3d, BL::RegionView3D &b_rv3d, Camera *cam, int width, int height); private: static DenoiseParams get_denoise_params(BL::Scene &b_scene, @@ -131,7 +119,7 @@ class BlenderSync { int width, int height, void **python_thread_state); - void sync_film(BL::SpaceView3D &b_v3d); + void sync_film(BL::ViewLayer &b_view_layer, BL::SpaceView3D &b_v3d); void sync_view(); /* Shader */ @@ -245,6 +233,7 @@ class BlenderSync { Scene *scene; bool preview; bool experimental; + bool use_developer_ui; float dicing_rate; int max_subdivisions; @@ -253,7 +242,6 @@ class BlenderSync { RenderLayerInfo() : material_override(PointerRNA_NULL), use_background_shader(true), - use_background_ao(true), use_surfaces(true), use_hair(true), use_volumes(true), @@ -266,7 +254,6 @@ class BlenderSync { string name; BL::Material material_override; bool use_background_shader; - bool use_background_ao; bool use_surfaces; bool use_hair; bool use_volumes; diff --git a/intern/cycles/blender/blender_texture.cpp b/intern/cycles/blender/texture.cpp similarity index 97% rename from intern/cycles/blender/blender_texture.cpp rename to intern/cycles/blender/texture.cpp index 0d593f2b385..43745bb8376 100644 --- a/intern/cycles/blender/blender_texture.cpp +++ b/intern/cycles/blender/texture.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "blender/blender_texture.h" +#include "blender/texture.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_texture.h b/intern/cycles/blender/texture.h similarity index 96% rename from intern/cycles/blender/blender_texture.h rename to intern/cycles/blender/texture.h index 8ab061aaed9..ead0c4e631b 100644 --- a/intern/cycles/blender/blender_texture.h +++ b/intern/cycles/blender/texture.h @@ -17,7 +17,7 @@ #ifndef __BLENDER_TEXTURE_H__ #define __BLENDER_TEXTURE_H__ -#include "blender/blender_sync.h" +#include "blender/sync.h" #include CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/blender/blender_util.h b/intern/cycles/blender/util.h similarity index 92% rename from intern/cycles/blender/blender_util.h rename to intern/cycles/blender/util.h index 04008d77d89..33fd2c416c8 100644 --- a/intern/cycles/blender/blender_util.h +++ b/intern/cycles/blender/util.h @@ -17,16 +17,16 @@ #ifndef __BLENDER_UTIL_H__ #define __BLENDER_UTIL_H__ -#include "render/mesh.h" +#include "scene/mesh.h" -#include "util/util_algorithm.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_path.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/algorithm.h" +#include "util/array.h" +#include "util/map.h" +#include "util/path.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/types.h" +#include "util/vector.h" /* Hacks to hook into Blender API * todo: clean this up ... */ @@ -90,26 +90,27 @@ static inline BL::Mesh object_to_mesh(BL::BlendData & /*data*/, } #endif - BL::Mesh mesh(PointerRNA_NULL); - if (b_ob_info.object_data.is_a(&RNA_Mesh)) { - /* TODO: calc_undeformed is not used. */ - mesh = BL::Mesh(b_ob_info.object_data); + BL::Mesh mesh = (b_ob_info.object_data.is_a(&RNA_Mesh)) ? BL::Mesh(b_ob_info.object_data) : + BL::Mesh(PointerRNA_NULL); - /* Make a copy to split faces if we use autosmooth, otherwise not needed. - * Also in edit mode do we need to make a copy, to ensure data layers like - * UV are not empty. */ - if (mesh.is_editmode() || - (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) { + if (b_ob_info.is_real_object_data()) { + if (mesh) { + /* Make a copy to split faces if we use autosmooth, otherwise not needed. + * Also in edit mode do we need to make a copy, to ensure data layers like + * UV are not empty. */ + if (mesh.is_editmode() || + (mesh.use_auto_smooth() && subdivision_type == Mesh::SUBDIVISION_NONE)) { + BL::Depsgraph depsgraph(PointerRNA_NULL); + mesh = b_ob_info.real_object.to_mesh(false, depsgraph); + } + } + else { BL::Depsgraph depsgraph(PointerRNA_NULL); - assert(b_ob_info.is_real_object_data()); mesh = b_ob_info.real_object.to_mesh(false, depsgraph); } } else { - BL::Depsgraph depsgraph(PointerRNA_NULL); - if (b_ob_info.is_real_object_data()) { - mesh = b_ob_info.real_object.to_mesh(false, depsgraph); - } + /* TODO: what to do about non-mesh geometry instances? */ } #if 0 @@ -170,12 +171,11 @@ static inline void curvemap_minmax_curve(/*const*/ BL::CurveMap &curve, float *m } static inline void curvemapping_minmax(/*const*/ BL::CurveMapping &cumap, - bool rgb_curve, + int num_curves, float *min_x, float *max_x) { // const int num_curves = cumap.curves.length(); /* Gives linking error so far. */ - const int num_curves = rgb_curve ? 4 : 3; *min_x = FLT_MAX; *max_x = -FLT_MAX; for (int i = 0; i < num_curves; ++i) { @@ -195,6 +195,28 @@ static inline void curvemapping_to_array(BL::CurveMapping &cumap, array & } } +static inline void curvemapping_float_to_array(BL::CurveMapping &cumap, + array &data, + int size) +{ + float min = 0.0f, max = 1.0f; + + curvemapping_minmax(cumap, 1, &min, &max); + + const float range = max - min; + + cumap.update(); + + BL::CurveMap map = cumap.curves[0]; + + data.resize(size); + + for (int i = 0; i < size; i++) { + float t = min + (float)i / (float)(size - 1) * range; + data[i] = cumap.evaluate(map, t); + } +} + static inline void curvemapping_color_to_array(BL::CurveMapping &cumap, array &data, int size, @@ -213,7 +235,8 @@ static inline void curvemapping_color_to_array(BL::CurveMapping &cumap, * * There might be some better estimations here tho. */ - curvemapping_minmax(cumap, rgb_curve, &min_x, &max_x); + const int num_curves = rgb_curve ? 4 : 3; + curvemapping_minmax(cumap, num_curves, &min_x, &max_x); const float range_x = max_x - min_x; diff --git a/intern/cycles/blender/blender_viewport.cpp b/intern/cycles/blender/viewport.cpp similarity index 71% rename from intern/cycles/blender/blender_viewport.cpp rename to intern/cycles/blender/viewport.cpp index 18bdfc74de0..2a6f7e3ecee 100644 --- a/intern/cycles/blender/blender_viewport.cpp +++ b/intern/cycles/blender/viewport.cpp @@ -14,9 +14,12 @@ * limitations under the License. */ -#include "blender_viewport.h" +#include "blender/viewport.h" +#include "blender/util.h" -#include "blender_util.h" +#include "scene/pass.h" + +#include "util/log.h" CCL_NAMESPACE_BEGIN @@ -26,11 +29,12 @@ BlenderViewportParameters::BlenderViewportParameters() studiolight_rotate_z(0.0f), studiolight_intensity(1.0f), studiolight_background_alpha(1.0f), - display_pass(PASS_COMBINED) + display_pass(PASS_COMBINED), + show_active_pixels(false) { } -BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) +BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d, bool use_developer_ui) : BlenderViewportParameters() { if (!b_v3d) { @@ -55,7 +59,25 @@ BlenderViewportParameters::BlenderViewportParameters(BL::SpaceView3D &b_v3d) } /* Film. */ - display_pass = (PassType)get_enum(cshading, "render_pass", -1, -1); + + /* Lookup display pass based on the enum identifier. + * This is because integer values of python enum are not aligned with the passes definition in + * the kernel. */ + + display_pass = PASS_COMBINED; + + const string display_pass_identifier = get_enum_identifier(cshading, "render_pass"); + if (!display_pass_identifier.empty()) { + const ustring pass_type_identifier(string_to_lower(display_pass_identifier)); + const NodeEnum *pass_type_enum = Pass::get_type_enum(); + if (pass_type_enum->exists(pass_type_identifier)) { + display_pass = static_cast((*pass_type_enum)[pass_type_identifier]); + } + } + + if (use_developer_ui) { + show_active_pixels = get_boolean(cshading, "show_active_pixels"); + } } bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters &other) const @@ -69,7 +91,7 @@ bool BlenderViewportParameters::shader_modified(const BlenderViewportParameters bool BlenderViewportParameters::film_modified(const BlenderViewportParameters &other) const { - return display_pass != other.display_pass; + return display_pass != other.display_pass || show_active_pixels != other.show_active_pixels; } bool BlenderViewportParameters::modified(const BlenderViewportParameters &other) const @@ -82,18 +104,4 @@ bool BlenderViewportParameters::use_custom_shader() const return !(use_scene_world && use_scene_lights); } -PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes) -{ - if (b_v3d) { - const BlenderViewportParameters viewport_parameters(b_v3d); - const PassType display_pass = viewport_parameters.display_pass; - - passes.clear(); - Pass::add(display_pass, passes); - - return display_pass; - } - return PASS_NONE; -} - CCL_NAMESPACE_END diff --git a/intern/cycles/blender/blender_viewport.h b/intern/cycles/blender/viewport.h similarity index 91% rename from intern/cycles/blender/blender_viewport.h rename to intern/cycles/blender/viewport.h index d6518597053..a445973f4d2 100644 --- a/intern/cycles/blender/blender_viewport.h +++ b/intern/cycles/blender/viewport.h @@ -23,7 +23,7 @@ #include "RNA_blender_cpp.h" #include "RNA_types.h" -#include "render/film.h" +#include "scene/film.h" CCL_NAMESPACE_BEGIN @@ -39,9 +39,10 @@ class BlenderViewportParameters { /* Film. */ PassType display_pass; + bool show_active_pixels; BlenderViewportParameters(); - explicit BlenderViewportParameters(BL::SpaceView3D &b_v3d); + BlenderViewportParameters(BL::SpaceView3D &b_v3d, bool use_developer_ui); /* Check whether any of shading related settings are different from the given parameters. */ bool shader_modified(const BlenderViewportParameters &other) const; @@ -57,8 +58,6 @@ class BlenderViewportParameters { bool use_custom_shader() const; }; -PassType update_viewport_display_passes(BL::SpaceView3D &b_v3d, vector &passes); - CCL_NAMESPACE_END #endif diff --git a/intern/cycles/blender/blender_volume.cpp b/intern/cycles/blender/volume.cpp similarity index 98% rename from intern/cycles/blender/blender_volume.cpp rename to intern/cycles/blender/volume.cpp index 0a5b19d7d4c..a41e15621a7 100644 --- a/intern/cycles/blender/blender_volume.cpp +++ b/intern/cycles/blender/volume.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "render/colorspace.h" -#include "render/image.h" -#include "render/image_vdb.h" -#include "render/object.h" -#include "render/volume.h" +#include "scene/volume.h" +#include "scene/colorspace.h" +#include "scene/image.h" +#include "scene/image_vdb.h" +#include "scene/object.h" -#include "blender/blender_sync.h" -#include "blender/blender_util.h" +#include "blender/sync.h" +#include "blender/util.h" #ifdef WITH_OPENVDB # include diff --git a/intern/cycles/bvh/CMakeLists.txt b/intern/cycles/bvh/CMakeLists.txt index 8cc72359757..9edc30cf9c4 100644 --- a/intern/cycles/bvh/CMakeLists.txt +++ b/intern/cycles/bvh/CMakeLists.txt @@ -22,34 +22,34 @@ set(INC_SYS set(SRC bvh.cpp bvh2.cpp - bvh_binning.cpp - bvh_build.cpp - bvh_embree.cpp - bvh_multi.cpp - bvh_node.cpp - bvh_optix.cpp - bvh_sort.cpp - bvh_split.cpp - bvh_unaligned.cpp + binning.cpp + build.cpp + embree.cpp + multi.cpp + node.cpp + optix.cpp + sort.cpp + split.cpp + unaligned.cpp ) set(SRC_HEADERS bvh.h bvh2.h - bvh_binning.h - bvh_build.h - bvh_embree.h - bvh_multi.h - bvh_node.h - bvh_optix.h - bvh_params.h - bvh_sort.h - bvh_split.h - bvh_unaligned.h + binning.h + build.h + embree.h + multi.h + node.h + optix.h + params.h + sort.h + split.h + unaligned.h ) set(LIB - cycles_render + cycles_scene cycles_util ) diff --git a/intern/cycles/bvh/bvh_binning.cpp b/intern/cycles/bvh/binning.cpp similarity index 98% rename from intern/cycles/bvh/bvh_binning.cpp rename to intern/cycles/bvh/binning.cpp index 1cc38275d11..da591ef5cea 100644 --- a/intern/cycles/bvh/bvh_binning.cpp +++ b/intern/cycles/bvh/binning.cpp @@ -17,13 +17,13 @@ //#define __KERNEL_SSE__ -#include "bvh/bvh_binning.h" +#include "bvh/binning.h" #include -#include "util/util_algorithm.h" -#include "util/util_boundbox.h" -#include "util/util_types.h" +#include "util/algorithm.h" +#include "util/boundbox.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_binning.h b/intern/cycles/bvh/binning.h similarity index 97% rename from intern/cycles/bvh/bvh_binning.h rename to intern/cycles/bvh/binning.h index ae6dba2805d..876500ec540 100644 --- a/intern/cycles/bvh/bvh_binning.h +++ b/intern/cycles/bvh/binning.h @@ -18,10 +18,10 @@ #ifndef __BVH_BINNING_H__ #define __BVH_BINNING_H__ -#include "bvh/bvh_params.h" -#include "bvh/bvh_unaligned.h" +#include "bvh/params.h" +#include "bvh/unaligned.h" -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_build.cpp b/intern/cycles/bvh/build.cpp similarity index 94% rename from intern/cycles/bvh/bvh_build.cpp rename to intern/cycles/bvh/build.cpp index 048c2b95e40..3ce268dfb25 100644 --- a/intern/cycles/bvh/bvh_build.cpp +++ b/intern/cycles/bvh/build.cpp @@ -15,27 +15,27 @@ * limitations under the License. */ -#include "bvh/bvh_build.h" +#include "bvh/build.h" -#include "bvh/bvh_binning.h" -#include "bvh/bvh_node.h" -#include "bvh/bvh_params.h" -#include "bvh_split.h" +#include "bvh/binning.h" +#include "bvh/node.h" +#include "bvh/params.h" +#include "bvh/split.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_queue.h" -#include "util/util_simd.h" -#include "util/util_stack_allocator.h" -#include "util/util_time.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/queue.h" +#include "util/simd.h" +#include "util/stack_allocator.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN @@ -67,8 +67,12 @@ BVHBuild::~BVHBuild() /* Adding References */ -void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *mesh, int i) +void BVHBuild::add_reference_triangles(BoundBox &root, + BoundBox ¢er, + Mesh *mesh, + int object_index) { + const PrimitiveType primitive_type = mesh->primitive_type(); const Attribute *attr_mP = NULL; if (mesh->has_motion_blur()) { attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); @@ -81,7 +85,7 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m BoundBox bounds = BoundBox::empty; t.bounds_grow(verts, bounds); if (bounds.valid() && t.valid(verts)) { - references.push_back(BVHReference(bounds, j, i, PRIMITIVE_TRIANGLE)); + references.push_back(BVHReference(bounds, j, object_index, primitive_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -101,7 +105,7 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m t.bounds_grow(vert_steps + step * num_verts, bounds); } if (bounds.valid()) { - references.push_back(BVHReference(bounds, j, i, PRIMITIVE_MOTION_TRIANGLE)); + references.push_back(BVHReference(bounds, j, object_index, primitive_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -140,7 +144,7 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m if (bounds.valid()) { const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; references.push_back( - BVHReference(bounds, j, i, PRIMITIVE_MOTION_TRIANGLE, prev_time, curr_time)); + BVHReference(bounds, j, object_index, primitive_type, prev_time, curr_time)); root.grow(bounds); center.grow(bounds.center2()); } @@ -153,18 +157,14 @@ void BVHBuild::add_reference_triangles(BoundBox &root, BoundBox ¢er, Mesh *m } } -void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair, int i) +void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair, int object_index) { const Attribute *curve_attr_mP = NULL; if (hair->has_motion_blur()) { curve_attr_mP = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); } - const PrimitiveType primitive_type = - (curve_attr_mP != NULL) ? - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((hair->curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); + const PrimitiveType primitive_type = hair->primitive_type(); const size_t num_curves = hair->num_curves(); for (uint j = 0; j < num_curves; j++) { @@ -177,7 +177,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair curve.bounds_grow(k, &hair->get_curve_keys()[0], curve_radius, bounds); if (bounds.valid()) { int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k); - references.push_back(BVHReference(bounds, j, i, packed_type)); + references.push_back(BVHReference(bounds, j, object_index, packed_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -198,7 +198,7 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair } if (bounds.valid()) { int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k); - references.push_back(BVHReference(bounds, j, i, packed_type)); + references.push_back(BVHReference(bounds, j, object_index, packed_type)); root.grow(bounds); center.grow(bounds.center2()); } @@ -254,7 +254,8 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair if (bounds.valid()) { const float prev_time = (float)(bvh_step - 1) * num_bvh_steps_inv_1; int packed_type = PRIMITIVE_PACK_SEGMENT(primitive_type, k); - references.push_back(BVHReference(bounds, j, i, packed_type, prev_time, curr_time)); + references.push_back( + BVHReference(bounds, j, object_index, packed_type, prev_time, curr_time)); root.grow(bounds); center.grow(bounds.center2()); } @@ -268,15 +269,18 @@ void BVHBuild::add_reference_curves(BoundBox &root, BoundBox ¢er, Hair *hair } } -void BVHBuild::add_reference_geometry(BoundBox &root, BoundBox ¢er, Geometry *geom, int i) +void BVHBuild::add_reference_geometry(BoundBox &root, + BoundBox ¢er, + Geometry *geom, + int object_index) { if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { Mesh *mesh = static_cast(geom); - add_reference_triangles(root, center, mesh, i); + add_reference_triangles(root, center, mesh, object_index); } else if (geom->geometry_type == Geometry::HAIR) { Hair *hair = static_cast(geom); - add_reference_curves(root, center, hair, i); + add_reference_curves(root, center, hair, object_index); } } @@ -832,18 +836,18 @@ BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vector LeafTimeStackAllocator; typedef StackAllocator<256, BVHReference> LeafReferenceStackAllocator; - vector p_type[PRIMITIVE_NUM_TOTAL]; - vector p_index[PRIMITIVE_NUM_TOTAL]; - vector p_object[PRIMITIVE_NUM_TOTAL]; - vector p_time[PRIMITIVE_NUM_TOTAL]; - vector p_ref[PRIMITIVE_NUM_TOTAL]; + vector p_type[PRIMITIVE_NUM]; + vector p_index[PRIMITIVE_NUM]; + vector p_object[PRIMITIVE_NUM]; + vector p_time[PRIMITIVE_NUM]; + vector p_ref[PRIMITIVE_NUM]; /* TODO(sergey): In theory we should be able to store references. */ vector object_references; - uint visibility[PRIMITIVE_NUM_TOTAL] = {0}; + uint visibility[PRIMITIVE_NUM] = {0}; /* NOTE: Keep initialization in sync with actual number of primitives. */ - BoundBox bounds[PRIMITIVE_NUM_TOTAL] = { + BoundBox bounds[PRIMITIVE_NUM] = { BoundBox::empty, BoundBox::empty, BoundBox::empty, BoundBox::empty}; int ob_num = 0; int num_new_prims = 0; @@ -877,7 +881,7 @@ BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vector local_prim_type, local_prim_index, local_prim_object; @@ -888,7 +892,7 @@ BVHNode *BVHBuild::create_leaf_node(const BVHRange &range, const vector -#include "bvh/bvh_params.h" -#include "bvh/bvh_unaligned.h" +#include "bvh/params.h" +#include "bvh/unaligned.h" -#include "util/util_array.h" -#include "util/util_task.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/task.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh.cpp b/intern/cycles/bvh/bvh.cpp index 050e090bddf..ae6655eb27b 100644 --- a/intern/cycles/bvh/bvh.cpp +++ b/intern/cycles/bvh/bvh.cpp @@ -18,12 +18,12 @@ #include "bvh/bvh.h" #include "bvh/bvh2.h" -#include "bvh/bvh_embree.h" -#include "bvh/bvh_multi.h" -#include "bvh/bvh_optix.h" +#include "bvh/embree.h" +#include "bvh/multi.h" +#include "bvh/optix.h" -#include "util/util_logging.h" -#include "util/util_progress.h" +#include "util/log.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh.h b/intern/cycles/bvh/bvh.h index 94935c26f10..c1f55ee917e 100644 --- a/intern/cycles/bvh/bvh.h +++ b/intern/cycles/bvh/bvh.h @@ -18,10 +18,10 @@ #ifndef __BVH_H__ #define __BVH_H__ -#include "bvh/bvh_params.h" -#include "util/util_array.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "bvh/params.h" +#include "util/array.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -50,13 +50,9 @@ struct PackedBVH { array leaf_nodes; /* object index to BVH node index mapping for instances */ array object_node; - /* Mapping from primitive index to index in triangle array. */ - array prim_tri_index; - /* Continuous storage of triangle vertices. */ - array prim_tri_verts; /* primitive type - triangle or strand */ array prim_type; - /* visibility visibilitys for primitives */ + /* Visibility visibilities for primitives. */ array prim_visibility; /* mapping from BVH primitive index to true primitive index, as primitives * may be duplicated due to spatial splits. -1 for instances. */ diff --git a/intern/cycles/bvh/bvh2.cpp b/intern/cycles/bvh/bvh2.cpp index 379ae9b25ff..04290602145 100644 --- a/intern/cycles/bvh/bvh2.cpp +++ b/intern/cycles/bvh/bvh2.cpp @@ -17,16 +17,16 @@ #include "bvh/bvh2.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" -#include "bvh/bvh_build.h" -#include "bvh/bvh_node.h" -#include "bvh/bvh_unaligned.h" +#include "bvh/build.h" +#include "bvh/node.h" +#include "bvh/unaligned.h" -#include "util/util_foreach.h" -#include "util/util_progress.h" +#include "util/foreach.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN @@ -439,61 +439,20 @@ void BVH2::refit_primitives(int start, int end, BoundBox &bbox, uint &visibility /* Triangles */ -void BVH2::pack_triangle(int idx, float4 tri_verts[3]) -{ - int tob = pack.prim_object[idx]; - assert(tob >= 0 && tob < objects.size()); - const Mesh *mesh = static_cast(objects[tob]->get_geometry()); - - int tidx = pack.prim_index[idx]; - Mesh::Triangle t = mesh->get_triangle(tidx); - const float3 *vpos = &mesh->verts[0]; - float3 v0 = vpos[t.v[0]]; - float3 v1 = vpos[t.v[1]]; - float3 v2 = vpos[t.v[2]]; - - tri_verts[0] = float3_to_float4(v0); - tri_verts[1] = float3_to_float4(v1); - tri_verts[2] = float3_to_float4(v2); -} - void BVH2::pack_primitives() { const size_t tidx_size = pack.prim_index.size(); - size_t num_prim_triangles = 0; - /* Count number of triangles primitives in BVH. */ - for (unsigned int i = 0; i < tidx_size; i++) { - if ((pack.prim_index[i] != -1)) { - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - ++num_prim_triangles; - } - } - } /* Reserve size for arrays. */ - pack.prim_tri_index.clear(); - pack.prim_tri_index.resize(tidx_size); - pack.prim_tri_verts.clear(); - pack.prim_tri_verts.resize(num_prim_triangles * 3); pack.prim_visibility.clear(); pack.prim_visibility.resize(tidx_size); /* Fill in all the arrays. */ - size_t prim_triangle_index = 0; for (unsigned int i = 0; i < tidx_size; i++) { if (pack.prim_index[i] != -1) { int tob = pack.prim_object[i]; Object *ob = objects[tob]; - if ((pack.prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - pack_triangle(i, (float4 *)&pack.prim_tri_verts[3 * prim_triangle_index]); - pack.prim_tri_index[i] = 3 * prim_triangle_index; - ++prim_triangle_index; - } - else { - pack.prim_tri_index[i] = -1; - } pack.prim_visibility[i] = ob->visibility_for_tracing(); } else { - pack.prim_tri_index[i] = -1; pack.prim_visibility[i] = 0; } } @@ -522,10 +481,8 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) /* reserve */ size_t prim_index_size = pack.prim_index.size(); - size_t prim_tri_verts_size = pack.prim_tri_verts.size(); size_t pack_prim_index_offset = prim_index_size; - size_t pack_prim_tri_verts_offset = prim_tri_verts_size; size_t pack_nodes_offset = nodes_size; size_t pack_leaf_nodes_offset = leaf_nodes_size; size_t object_offset = 0; @@ -535,7 +492,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) if (geom->need_build_bvh(params.bvh_layout)) { prim_index_size += bvh->pack.prim_index.size(); - prim_tri_verts_size += bvh->pack.prim_tri_verts.size(); nodes_size += bvh->pack.nodes.size(); leaf_nodes_size += bvh->pack.leaf_nodes.size(); } @@ -545,8 +501,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) pack.prim_type.resize(prim_index_size); pack.prim_object.resize(prim_index_size); pack.prim_visibility.resize(prim_index_size); - pack.prim_tri_verts.resize(prim_tri_verts_size); - pack.prim_tri_index.resize(prim_index_size); pack.nodes.resize(nodes_size); pack.leaf_nodes.resize(leaf_nodes_size); pack.object_node.resize(objects.size()); @@ -559,8 +513,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) int *pack_prim_type = (pack.prim_type.size()) ? &pack.prim_type[0] : NULL; int *pack_prim_object = (pack.prim_object.size()) ? &pack.prim_object[0] : NULL; uint *pack_prim_visibility = (pack.prim_visibility.size()) ? &pack.prim_visibility[0] : NULL; - float4 *pack_prim_tri_verts = (pack.prim_tri_verts.size()) ? &pack.prim_tri_verts[0] : NULL; - uint *pack_prim_tri_index = (pack.prim_tri_index.size()) ? &pack.prim_tri_index[0] : NULL; int4 *pack_nodes = (pack.nodes.size()) ? &pack.nodes[0] : NULL; int4 *pack_leaf_nodes = (pack.leaf_nodes.size()) ? &pack.leaf_nodes[0] : NULL; float2 *pack_prim_time = (pack.prim_time.size()) ? &pack.prim_time[0] : NULL; @@ -609,18 +561,14 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) int *bvh_prim_index = &bvh->pack.prim_index[0]; int *bvh_prim_type = &bvh->pack.prim_type[0]; uint *bvh_prim_visibility = &bvh->pack.prim_visibility[0]; - uint *bvh_prim_tri_index = &bvh->pack.prim_tri_index[0]; float2 *bvh_prim_time = bvh->pack.prim_time.size() ? &bvh->pack.prim_time[0] : NULL; for (size_t i = 0; i < bvh_prim_index_size; i++) { if (bvh->pack.prim_type[i] & PRIMITIVE_ALL_CURVE) { pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = -1; } else { pack_prim_index[pack_prim_index_offset] = bvh_prim_index[i] + geom_prim_offset; - pack_prim_tri_index[pack_prim_index_offset] = bvh_prim_tri_index[i] + - pack_prim_tri_verts_offset; } pack_prim_type[pack_prim_index_offset] = bvh_prim_type[i]; @@ -633,15 +581,6 @@ void BVH2::pack_instances(size_t nodes_size, size_t leaf_nodes_size) } } - /* Merge triangle vertices data. */ - if (bvh->pack.prim_tri_verts.size()) { - const size_t prim_tri_size = bvh->pack.prim_tri_verts.size(); - memcpy(pack_prim_tri_verts + pack_prim_tri_verts_offset, - &bvh->pack.prim_tri_verts[0], - prim_tri_size * sizeof(float4)); - pack_prim_tri_verts_offset += prim_tri_size; - } - /* merge nodes */ if (bvh->pack.leaf_nodes.size()) { int4 *leaf_nodes_offset = &bvh->pack.leaf_nodes[0]; diff --git a/intern/cycles/bvh/bvh2.h b/intern/cycles/bvh/bvh2.h index 1030a0f76c7..7937288f271 100644 --- a/intern/cycles/bvh/bvh2.h +++ b/intern/cycles/bvh/bvh2.h @@ -19,10 +19,10 @@ #define __BVH2_H__ #include "bvh/bvh.h" -#include "bvh/bvh_params.h" +#include "bvh/params.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_embree.cpp b/intern/cycles/bvh/embree.cpp similarity index 82% rename from intern/cycles/bvh/bvh_embree.cpp rename to intern/cycles/bvh/embree.cpp index 62f543941a9..944a84ce0da 100644 --- a/intern/cycles/bvh/bvh_embree.cpp +++ b/intern/cycles/bvh/embree.cpp @@ -32,24 +32,24 @@ # include -# include "bvh/bvh_embree.h" +# include "bvh/embree.h" /* Kernel includes are necessary so that the filter function for Embree can access the packed BVH. */ -# include "kernel/bvh/bvh_embree.h" -# include "kernel/kernel_compat_cpu.h" -# include "kernel/kernel_globals.h" -# include "kernel/kernel_random.h" -# include "kernel/split/kernel_split_data_types.h" +# include "kernel/bvh/embree.h" +# include "kernel/bvh/util.h" +# include "kernel/device/cpu/compat.h" +# include "kernel/device/cpu/globals.h" +# include "kernel/sample/lcg.h" -# include "render/hair.h" -# include "render/mesh.h" -# include "render/object.h" +# include "scene/hair.h" +# include "scene/mesh.h" +# include "scene/object.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_progress.h" -# include "util/util_stats.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/progress.h" +# include "util/stats.h" CCL_NAMESPACE_BEGIN @@ -73,46 +73,75 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) const RTCRay *ray = (RTCRay *)args->ray; RTCHit *hit = (RTCHit *)args->hit; CCLIntersectContext *ctx = ((IntersectContext *)args->context)->userRayExt; - KernelGlobals *kg = ctx->kg; + const KernelGlobalsCPU *kg = ctx->kg; switch (ctx->type) { case CCLIntersectContext::RAY_SHADOW_ALL: { - /* Append the intersection to the end of the array. */ - if (ctx->num_hits < ctx->max_hits) { - Intersection current_isect; - kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - for (size_t i = 0; i < ctx->max_hits; ++i) { - if (current_isect.object == ctx->isect_s[i].object && - current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { - /* This intersection was already recorded, skip it. */ - *args->valid = 0; - break; - } - } - Intersection *isect = &ctx->isect_s[ctx->num_hits]; - ++ctx->num_hits; - *isect = current_isect; - int prim = kernel_tex_fetch(__prim_index, isect->prim); - int shader = 0; - if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) { - shader = kernel_tex_fetch(__tri_shader, prim); + Intersection current_isect; + kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); + + /* If no transparent shadows or max number of hits exceeded, all light is blocked. */ + const int flags = intersection_get_shader_flags(kg, current_isect.prim, current_isect.type); + if (!(flags & (SD_HAS_TRANSPARENT_SHADOW)) || ctx->num_hits >= ctx->max_hits) { + ctx->opaque_hit = true; + return; + } + + ++ctx->num_hits; + + /* Always use baked shadow transparency for curves. */ + if (current_isect.type & PRIMITIVE_ALL_CURVE) { + ctx->throughput *= intersection_curve_shadow_transparency( + kg, current_isect.object, current_isect.prim, current_isect.u); + + if (ctx->throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { + ctx->opaque_hit = true; + return; } else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } - int flag = kernel_tex_fetch(__shaders, shader & SHADER_MASK).flags; - /* If no transparent shadows, all light is blocked. */ - if (flag & (SD_HAS_TRANSPARENT_SHADOW)) { - /* This tells Embree to continue tracing. */ *args->valid = 0; + return; } } - else { - /* Increase the number of hits beyond ray.max_hits - * so that the caller can detect this as opaque. */ - ++ctx->num_hits; + + /* Test if we need to record this transparent intersection. */ + const uint max_record_hits = min(ctx->max_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + if (ctx->num_recorded_hits < max_record_hits || ray->tfar < ctx->max_t) { + /* If maximum number of hits was reached, replace the intersection with the + * highest distance. We want to find the N closest intersections. */ + const uint num_recorded_hits = min(ctx->num_recorded_hits, max_record_hits); + uint isect_index = num_recorded_hits; + if (num_recorded_hits + 1 >= max_record_hits) { + float max_t = ctx->isect_s[0].t; + uint max_recorded_hit = 0; + + for (uint i = 1; i < num_recorded_hits; ++i) { + if (ctx->isect_s[i].t > max_t) { + max_recorded_hit = i; + max_t = ctx->isect_s[i].t; + } + } + + if (num_recorded_hits >= max_record_hits) { + isect_index = max_recorded_hit; + } + + /* Limit the ray distance and stop counting hits beyond this. + * TODO: is there some way we can tell Embree to stop intersecting beyond + * this distance when max number of hits is reached?. Or maybe it will + * become irrelevant if we make max_hits a very high number on the CPU. */ + ctx->max_t = max(current_isect.t, max_t); + } + + ctx->isect_s[isect_index] = current_isect; } + + /* Always increase the number of recorded hits, even beyond the maximum, + * so that we can detect this and trace another ray if needed. */ + ++ctx->num_recorded_hits; + + /* This tells Embree to continue tracing. */ + *args->valid = 0; break; } case CCLIntersectContext::RAY_LOCAL: @@ -124,10 +153,7 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) } else { kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - int object = (current_isect.object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, current_isect.prim) : - current_isect.object; - if (ctx->local_object_id != object) { + if (ctx->local_object_id != current_isect.object) { /* This tells Embree to continue tracing. */ *args->valid = 0; break; @@ -146,41 +172,49 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) break; } - /* See triangle_intersect_subsurface() for the native equivalent. */ - for (int i = min(ctx->max_hits, ctx->local_isect->num_hits) - 1; i >= 0; --i) { - if (ctx->local_isect->hits[i].t == ray->tfar) { - /* This tells Embree to continue tracing. */ - *args->valid = 0; - break; - } - } - + LocalIntersection *local_isect = ctx->local_isect; int hit_idx = 0; if (ctx->lcg_state) { + /* See triangle_intersect_subsurface() for the native equivalent. */ + for (int i = min((int)ctx->max_hits, local_isect->num_hits) - 1; i >= 0; --i) { + if (local_isect->hits[i].t == ray->tfar) { + /* This tells Embree to continue tracing. */ + *args->valid = 0; + return; + } + } - ++ctx->local_isect->num_hits; - if (ctx->local_isect->num_hits <= ctx->max_hits) { - hit_idx = ctx->local_isect->num_hits - 1; + local_isect->num_hits++; + + if (local_isect->num_hits <= ctx->max_hits) { + hit_idx = local_isect->num_hits - 1; } else { /* reservoir sampling: if we are at the maximum number of * hits, randomly replace element or skip it */ - hit_idx = lcg_step_uint(ctx->lcg_state) % ctx->local_isect->num_hits; + hit_idx = lcg_step_uint(ctx->lcg_state) % local_isect->num_hits; if (hit_idx >= ctx->max_hits) { /* This tells Embree to continue tracing. */ *args->valid = 0; - break; + return; } } } else { - ctx->local_isect->num_hits = 1; + /* Record closest intersection only. */ + if (local_isect->num_hits && current_isect.t > local_isect->hits[0].t) { + *args->valid = 0; + return; + } + + local_isect->num_hits = 1; } + /* record intersection */ - ctx->local_isect->hits[hit_idx] = current_isect; - ctx->local_isect->Ng[hit_idx] = normalize(make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)); + local_isect->hits[hit_idx] = current_isect; + local_isect->Ng[hit_idx] = normalize(make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)); /* This tells Embree to continue tracing. */ *args->valid = 0; break; @@ -190,21 +224,11 @@ static void rtc_filter_occluded_func(const RTCFilterFunctionNArguments *args) if (ctx->num_hits < ctx->max_hits) { Intersection current_isect; kernel_embree_convert_hit(kg, ray, hit, ¤t_isect); - for (size_t i = 0; i < ctx->max_hits; ++i) { - if (current_isect.object == ctx->isect_s[i].object && - current_isect.prim == ctx->isect_s[i].prim && current_isect.t == ctx->isect_s[i].t) { - /* This intersection was already recorded, skip it. */ - *args->valid = 0; - break; - } - } Intersection *isect = &ctx->isect_s[ctx->num_hits]; ++ctx->num_hits; *isect = current_isect; /* Only primitives from volume object. */ - uint tri_object = (isect->object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, isect->prim) : - isect->object; + uint tri_object = isect->object; int object_flag = kernel_tex_fetch(__object_flag, tri_object); if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { --ctx->num_hits; @@ -226,7 +250,7 @@ static void rtc_filter_func_thick_curve(const RTCFilterFunctionNArguments *args) const RTCRay *ray = (RTCRay *)args->ray; RTCHit *hit = (RTCHit *)args->hit; - /* Always ignore backfacing intersections. */ + /* Always ignore back-facing intersections. */ if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { *args->valid = 0; @@ -239,7 +263,7 @@ static void rtc_filter_occluded_func_thick_curve(const RTCFilterFunctionNArgumen const RTCRay *ray = (RTCRay *)args->ray; RTCHit *hit = (RTCHit *)args->hit; - /* Always ignore backfacing intersections. */ + /* Always ignore back-facing intersections. */ if (dot(make_float3(ray->dir_x, ray->dir_y, ray->dir_z), make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z)) > 0.0f) { *args->valid = 0; @@ -329,7 +353,7 @@ void BVHEmbree::build(Progress &progress, Stats *stats, RTCDevice rtc_device_) scene = NULL; } - const bool dynamic = params.bvh_type == SceneParams::BVH_DYNAMIC; + const bool dynamic = params.bvh_type == BVH_TYPE_DYNAMIC; scene = rtcNewScene(rtc_device); const RTCSceneFlags scene_flags = (dynamic ? RTC_SCENE_FLAG_DYNAMIC : RTC_SCENE_FLAG_NONE) | @@ -433,7 +457,7 @@ void BVHEmbree::add_instance(Object *ob, int i) void BVHEmbree::add_triangles(const Object *ob, const Mesh *mesh, int i) { - size_t prim_offset = mesh->optix_prim_offset; + size_t prim_offset = mesh->prim_offset; const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; @@ -602,7 +626,7 @@ void BVHEmbree::set_curve_vertex_buffer(RTCGeometry geom_id, const Hair *hair, c void BVHEmbree::add_curves(const Object *ob, const Hair *hair, int i) { - size_t prim_offset = hair->optix_prim_offset; + size_t prim_offset = hair->curve_segment_offset; const Attribute *attr_mP = NULL; size_t num_motion_steps = 1; @@ -679,7 +703,7 @@ void BVHEmbree::refit(Progress &progress) if (mesh->num_triangles() > 0) { RTCGeometry geom = rtcGetGeometry(scene, geom_id); set_tri_vertex_buffer(geom, mesh, true); - rtcSetGeometryUserData(geom, (void *)mesh->optix_prim_offset); + rtcSetGeometryUserData(geom, (void *)mesh->prim_offset); rtcCommitGeometry(geom); } } @@ -688,7 +712,7 @@ void BVHEmbree::refit(Progress &progress) if (hair->num_curves() > 0) { RTCGeometry geom = rtcGetGeometry(scene, geom_id + 1); set_curve_vertex_buffer(geom, hair, true); - rtcSetGeometryUserData(geom, (void *)hair->optix_prim_offset); + rtcSetGeometryUserData(geom, (void *)hair->curve_segment_offset); rtcCommitGeometry(geom); } } diff --git a/intern/cycles/bvh/bvh_embree.h b/intern/cycles/bvh/embree.h similarity index 93% rename from intern/cycles/bvh/bvh_embree.h rename to intern/cycles/bvh/embree.h index 01636fbd1dc..746ca97b504 100644 --- a/intern/cycles/bvh/bvh_embree.h +++ b/intern/cycles/bvh/embree.h @@ -23,11 +23,11 @@ # include # include "bvh/bvh.h" -# include "bvh/bvh_params.h" +# include "bvh/params.h" -# include "util/util_thread.h" -# include "util/util_types.h" -# include "util/util_vector.h" +# include "util/thread.h" +# include "util/types.h" +# include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_multi.cpp b/intern/cycles/bvh/multi.cpp similarity index 94% rename from intern/cycles/bvh/bvh_multi.cpp rename to intern/cycles/bvh/multi.cpp index a9e771f20f1..db0ff5c7847 100644 --- a/intern/cycles/bvh/bvh_multi.cpp +++ b/intern/cycles/bvh/multi.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "bvh/bvh_multi.h" +#include "bvh/multi.h" -#include "util/util_foreach.h" +#include "util/foreach.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_multi.h b/intern/cycles/bvh/multi.h similarity index 97% rename from intern/cycles/bvh/bvh_multi.h rename to intern/cycles/bvh/multi.h index 840438c5d0c..88a459605c3 100644 --- a/intern/cycles/bvh/bvh_multi.h +++ b/intern/cycles/bvh/multi.h @@ -18,7 +18,7 @@ #define __BVH_MULTI_H__ #include "bvh/bvh.h" -#include "bvh/bvh_params.h" +#include "bvh/params.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_node.cpp b/intern/cycles/bvh/node.cpp similarity index 98% rename from intern/cycles/bvh/bvh_node.cpp rename to intern/cycles/bvh/node.cpp index 38b554acfbf..d3a665adfe7 100644 --- a/intern/cycles/bvh/bvh_node.cpp +++ b/intern/cycles/bvh/node.cpp @@ -15,12 +15,12 @@ * limitations under the License. */ -#include "bvh/bvh_node.h" +#include "bvh/node.h" +#include "bvh/build.h" #include "bvh/bvh.h" -#include "bvh/bvh_build.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_node.h b/intern/cycles/bvh/node.h similarity index 98% rename from intern/cycles/bvh/bvh_node.h rename to intern/cycles/bvh/node.h index b3b5c43a394..d5de9e062fc 100644 --- a/intern/cycles/bvh/bvh_node.h +++ b/intern/cycles/bvh/node.h @@ -18,8 +18,8 @@ #ifndef __BVH_NODE_H__ #define __BVH_NODE_H__ -#include "util/util_boundbox.h" -#include "util/util_types.h" +#include "util/boundbox.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_optix.cpp b/intern/cycles/bvh/optix.cpp similarity index 97% rename from intern/cycles/bvh/bvh_optix.cpp rename to intern/cycles/bvh/optix.cpp index cd266f72f89..ebc3fa68b97 100644 --- a/intern/cycles/bvh/bvh_optix.cpp +++ b/intern/cycles/bvh/optix.cpp @@ -19,7 +19,7 @@ # include "device/device.h" -# include "bvh/bvh_optix.h" +# include "bvh/optix.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_optix.h b/intern/cycles/bvh/optix.h similarity index 94% rename from intern/cycles/bvh/bvh_optix.h rename to intern/cycles/bvh/optix.h index ba5d90471d1..037e54980bd 100644 --- a/intern/cycles/bvh/bvh_optix.h +++ b/intern/cycles/bvh/optix.h @@ -21,8 +21,9 @@ #ifdef WITH_OPTIX # include "bvh/bvh.h" -# include "bvh/bvh_params.h" -# include "device/device_memory.h" +# include "bvh/params.h" + +# include "device/memory.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_params.h b/intern/cycles/bvh/params.h similarity index 91% rename from intern/cycles/bvh/bvh_params.h rename to intern/cycles/bvh/params.h index 2dc10f30363..8f185a2640f 100644 --- a/intern/cycles/bvh/bvh_params.h +++ b/intern/cycles/bvh/params.h @@ -18,9 +18,9 @@ #ifndef __BVH_PARAMS_H__ #define __BVH_PARAMS_H__ -#include "util/util_boundbox.h" +#include "util/boundbox.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" CCL_NAMESPACE_BEGIN @@ -31,6 +31,27 @@ CCL_NAMESPACE_BEGIN */ typedef KernelBVHLayout BVHLayout; +/* Type of BVH, in terms whether it is supported dynamic updates of meshes + * or whether modifying geometry requires full BVH rebuild. + */ +enum BVHType { + /* BVH supports dynamic updates of geometry. + * + * Faster for updating BVH tree when doing modifications in viewport, + * but slower for rendering. + */ + BVH_TYPE_DYNAMIC = 0, + /* BVH tree is calculated for specific scene, updates in geometry + * requires full tree rebuild. + * + * Slower to update BVH tree when modifying objects in viewport, also + * slower to build final BVH tree but gives best possible render speed. + */ + BVH_TYPE_STATIC = 1, + + BVH_NUM_TYPES, +}; + /* Names bitflag type to denote which BVH layouts are supported by * particular area. * diff --git a/intern/cycles/bvh/bvh_sort.cpp b/intern/cycles/bvh/sort.cpp similarity index 98% rename from intern/cycles/bvh/bvh_sort.cpp rename to intern/cycles/bvh/sort.cpp index b01785b547a..a9975ce6bb2 100644 --- a/intern/cycles/bvh/bvh_sort.cpp +++ b/intern/cycles/bvh/sort.cpp @@ -15,12 +15,12 @@ * limitations under the License. */ -#include "bvh/bvh_sort.h" +#include "bvh/sort.h" -#include "bvh/bvh_build.h" +#include "bvh/build.h" -#include "util/util_algorithm.h" -#include "util/util_task.h" +#include "util/algorithm.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_sort.h b/intern/cycles/bvh/sort.h similarity index 100% rename from intern/cycles/bvh/bvh_sort.h rename to intern/cycles/bvh/sort.h diff --git a/intern/cycles/bvh/bvh_split.cpp b/intern/cycles/bvh/split.cpp similarity index 98% rename from intern/cycles/bvh/bvh_split.cpp rename to intern/cycles/bvh/split.cpp index d4c79253834..102c50e2979 100644 --- a/intern/cycles/bvh/bvh_split.cpp +++ b/intern/cycles/bvh/split.cpp @@ -15,16 +15,16 @@ * limitations under the License. */ -#include "bvh/bvh_split.h" +#include "bvh/split.h" -#include "bvh/bvh_build.h" -#include "bvh/bvh_sort.h" +#include "bvh/build.h" +#include "bvh/sort.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" -#include "util/util_algorithm.h" +#include "util/algorithm.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_split.h b/intern/cycles/bvh/split.h similarity index 99% rename from intern/cycles/bvh/bvh_split.h rename to intern/cycles/bvh/split.h index 5582d90bf83..2650a500ea9 100644 --- a/intern/cycles/bvh/bvh_split.h +++ b/intern/cycles/bvh/split.h @@ -18,8 +18,8 @@ #ifndef __BVH_SPLIT_H__ #define __BVH_SPLIT_H__ -#include "bvh/bvh_build.h" -#include "bvh/bvh_params.h" +#include "bvh/build.h" +#include "bvh/params.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_unaligned.cpp b/intern/cycles/bvh/unaligned.cpp similarity index 96% rename from intern/cycles/bvh/bvh_unaligned.cpp rename to intern/cycles/bvh/unaligned.cpp index 38e55307848..3c4a600fe58 100644 --- a/intern/cycles/bvh/bvh_unaligned.cpp +++ b/intern/cycles/bvh/unaligned.cpp @@ -14,16 +14,16 @@ * limitations under the License. */ -#include "bvh/bvh_unaligned.h" +#include "bvh/unaligned.h" -#include "render/hair.h" -#include "render/object.h" +#include "scene/hair.h" +#include "scene/object.h" -#include "bvh/bvh_binning.h" -#include "bvh_params.h" +#include "bvh/binning.h" +#include "bvh/params.h" -#include "util/util_boundbox.h" -#include "util/util_transform.h" +#include "util/boundbox.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/bvh/bvh_unaligned.h b/intern/cycles/bvh/unaligned.h similarity index 98% rename from intern/cycles/bvh/bvh_unaligned.h rename to intern/cycles/bvh/unaligned.h index e8a9a25daa8..33e584ea8ed 100644 --- a/intern/cycles/bvh/bvh_unaligned.h +++ b/intern/cycles/bvh/unaligned.h @@ -17,7 +17,7 @@ #ifndef __BVH_UNALIGNED_H__ #define __BVH_UNALIGNED_H__ -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/cmake/external_libs.cmake b/intern/cycles/cmake/external_libs.cmake index 04ff598621a..c1244ab740b 100644 --- a/intern/cycles/cmake/external_libs.cmake +++ b/intern/cycles/cmake/external_libs.cmake @@ -287,9 +287,6 @@ if(CYCLES_STANDALONE_REPOSITORY) endif() set(__boost_packages filesystem regex system thread date_time) - if(WITH_CYCLES_NETWORK) - list(APPEND __boost_packages serialization) - endif() if(WITH_CYCLES_OSL) list(APPEND __boost_packages wave) endif() @@ -524,7 +521,7 @@ endif() if(WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD) find_package(CUDA) # Try to auto locate CUDA toolkit if(CUDA_FOUND) - message(STATUS "CUDA nvcc = ${CUDA_NVCC_EXECUTABLE}") + message(STATUS "Found CUDA ${CUDA_NVCC_EXECUTABLE} (${CUDA_VERSION})") else() message(STATUS "CUDA compiler not found, disabling WITH_CYCLES_CUDA_BINARIES") set(WITH_CYCLES_CUDA_BINARIES OFF) @@ -535,4 +532,23 @@ if(WITH_CYCLES_CUDA_BINARIES OR NOT WITH_CUDA_DYNLOAD) endif() endif() + +########################################################################### +# HIP +########################################################################### + +if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) + find_package(HIP) + if(HIP_FOUND) + message(STATUS "Found HIP ${HIP_HIPCC_EXECUTABLE} (${HIP_VERSION})") + else() + message(STATUS "HIP compiler not found, disabling WITH_CYCLES_HIP_BINARIES") + set(WITH_CYCLES_HIP_BINARIES OFF) + endif() +endif() + +if(NOT WITH_HIP_DYNLOAD) + set(WITH_HIP_DYNLOAD ON) +endif() + unset(_cycles_lib_dir) diff --git a/intern/cycles/cmake/macros.cmake b/intern/cycles/cmake/macros.cmake index 47196dfd1ce..a470fb9c574 100644 --- a/intern/cycles/cmake/macros.cmake +++ b/intern/cycles/cmake/macros.cmake @@ -156,10 +156,16 @@ macro(cycles_target_link_libraries target) ${PLATFORM_LINKLIBS} ) - if(WITH_CUDA_DYNLOAD) - target_link_libraries(${target} extern_cuew) - else() - target_link_libraries(${target} ${CUDA_CUDA_LIBRARY}) + if(WITH_CYCLES_DEVICE_CUDA OR WITH_CYCLES_DEVICE_OPTIX) + if(WITH_CUDA_DYNLOAD) + target_link_libraries(${target} extern_cuew) + else() + target_link_libraries(${target} ${CUDA_CUDA_LIBRARY}) + endif() + endif() + + if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) + target_link_libraries(${target} extern_hipew) endif() if(CYCLES_STANDALONE_REPOSITORY) diff --git a/intern/cycles/device/CMakeLists.txt b/intern/cycles/device/CMakeLists.txt index 928249931a3..99b1fc8135d 100644 --- a/intern/cycles/device/CMakeLists.txt +++ b/intern/cycles/device/CMakeLists.txt @@ -22,111 +22,166 @@ set(INC_SYS ../../../extern/clew/include ) -if(WITH_CUDA_DYNLOAD) +if(WITH_CYCLES_DEVICE_OPTIX OR WITH_CYCLES_DEVICE_CUDA) + if(WITH_CUDA_DYNLOAD) + list(APPEND INC + ../../../extern/cuew/include + ) + add_definitions(-DWITH_CUDA_DYNLOAD) + else() + list(APPEND INC_SYS + ${CUDA_TOOLKIT_INCLUDE} + ) + add_definitions(-DCYCLES_CUDA_NVCC_EXECUTABLE="${CUDA_NVCC_EXECUTABLE}") + endif() +endif() + +if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) list(APPEND INC - ../../../extern/cuew/include + ../../../extern/hipew/include ) - add_definitions(-DWITH_CUDA_DYNLOAD) -else() - list(APPEND INC_SYS - ${CUDA_TOOLKIT_INCLUDE} - ) - add_definitions(-DCYCLES_CUDA_NVCC_EXECUTABLE="${CUDA_NVCC_EXECUTABLE}") + add_definitions(-DWITH_HIP_DYNLOAD) endif() set(SRC device.cpp - device_cpu.cpp - device_cuda.cpp - device_denoising.cpp - device_dummy.cpp - device_memory.cpp - device_multi.cpp - device_opencl.cpp - device_optix.cpp - device_split_kernel.cpp - device_task.cpp + denoise.cpp + graphics_interop.cpp + kernel.cpp + memory.cpp + queue.cpp +) + +set(SRC_CPU + cpu/device.cpp + cpu/device.h + cpu/device_impl.cpp + cpu/device_impl.h + cpu/kernel.cpp + cpu/kernel.h + cpu/kernel_function.h + cpu/kernel_thread_globals.cpp + cpu/kernel_thread_globals.h ) set(SRC_CUDA - cuda/device_cuda.h - cuda/device_cuda_impl.cpp + cuda/device.cpp + cuda/device.h + cuda/device_impl.cpp + cuda/device_impl.h + cuda/graphics_interop.cpp + cuda/graphics_interop.h + cuda/kernel.cpp + cuda/kernel.h + cuda/queue.cpp + cuda/queue.h + cuda/util.cpp + cuda/util.h ) -set(SRC_OPENCL - opencl/device_opencl.h - opencl/device_opencl_impl.cpp - opencl/memory_manager.h - opencl/memory_manager.cpp - opencl/opencl_util.cpp +set(SRC_HIP + hip/device.cpp + hip/device.h + hip/device_impl.cpp + hip/device_impl.h + hip/graphics_interop.cpp + hip/graphics_interop.h + hip/kernel.cpp + hip/kernel.h + hip/queue.cpp + hip/queue.h + hip/util.cpp + hip/util.h ) -if(WITH_CYCLES_NETWORK) - list(APPEND SRC - device_network.cpp - ) -endif() +set(SRC_DUMMY + dummy/device.cpp + dummy/device.h +) + +set(SRC_MULTI + multi/device.cpp + multi/device.h +) + +set(SRC_OPTIX + optix/device.cpp + optix/device.h + optix/device_impl.cpp + optix/device_impl.h + optix/queue.cpp + optix/queue.h + optix/util.h +) set(SRC_HEADERS device.h - device_denoising.h - device_memory.h - device_intern.h - device_network.h - device_split_kernel.h - device_task.h + denoise.h + graphics_interop.h + memory.h + kernel.h + queue.h ) set(LIB - cycles_render cycles_kernel cycles_util ${CYCLES_GL_LIBRARIES} ) -if(WITH_CUDA_DYNLOAD) +if(WITH_CYCLES_DEVICE_OPTIX OR WITH_CYCLES_DEVICE_CUDA) + if(WITH_CUDA_DYNLOAD) + list(APPEND LIB + extern_cuew + ) + else() + list(APPEND LIB + ${CUDA_CUDA_LIBRARY} + ) + endif() +endif() + +if(WITH_CYCLES_DEVICE_HIP AND WITH_HIP_DYNLOAD) list(APPEND LIB - extern_cuew - ) -else() - list(APPEND LIB - ${CUDA_CUDA_LIBRARY} + extern_hipew ) endif() add_definitions(${GL_DEFINITIONS}) -if(WITH_CYCLES_NETWORK) - add_definitions(-DWITH_NETWORK) -endif() -if(WITH_CYCLES_DEVICE_OPENCL) - list(APPEND LIB - extern_clew - ) - add_definitions(-DWITH_OPENCL) -endif() + if(WITH_CYCLES_DEVICE_CUDA) add_definitions(-DWITH_CUDA) endif() +if(WITH_CYCLES_DEVICE_HIP) + add_definitions(-DWITH_HIP) +endif() if(WITH_CYCLES_DEVICE_OPTIX) add_definitions(-DWITH_OPTIX) endif() -if(WITH_CYCLES_DEVICE_MULTI) - add_definitions(-DWITH_MULTI) -endif() if(WITH_OPENIMAGEDENOISE) - add_definitions(-DWITH_OPENIMAGEDENOISE) - add_definitions(-DOIDN_STATIC_LIB) - list(APPEND INC_SYS - ${OPENIMAGEDENOISE_INCLUDE_DIRS} - ) list(APPEND LIB ${OPENIMAGEDENOISE_LIBRARIES} - ${TBB_LIBRARIES} ) endif() include_directories(${INC}) include_directories(SYSTEM ${INC_SYS}) -cycles_add_library(cycles_device "${LIB}" ${SRC} ${SRC_CUDA} ${SRC_OPENCL} ${SRC_HEADERS}) +cycles_add_library(cycles_device "${LIB}" + ${SRC} + ${SRC_CPU} + ${SRC_CUDA} + ${SRC_HIP} + ${SRC_DUMMY} + ${SRC_MULTI} + ${SRC_OPTIX} + ${SRC_HEADERS} +) + +source_group("cpu" FILES ${SRC_CPU}) +source_group("cuda" FILES ${SRC_CUDA}) +source_group("dummy" FILES ${SRC_DUMMY}) +source_group("multi" FILES ${SRC_MULTI}) +source_group("optix" FILES ${SRC_OPTIX}) +source_group("common" FILES ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/device/cpu/device.cpp b/intern/cycles/device/cpu/device.cpp new file mode 100644 index 00000000000..5aabed8702a --- /dev/null +++ b/intern/cycles/device/cpu/device.cpp @@ -0,0 +1,63 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/device.h" +#include "device/cpu/device_impl.h" + +/* Used for `info.denoisers`. */ +/* TODO(sergey): The denoisers are probably to be moved completely out of the device into their + * own class. But until then keep API consistent with how it used to work before. */ +#include "util/openimagedenoise.h" + +CCL_NAMESPACE_BEGIN + +Device *device_cpu_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ + return new CPUDevice(info, stats, profiler); +} + +void device_cpu_info(vector &devices) +{ + DeviceInfo info; + + info.type = DEVICE_CPU; + info.description = system_cpu_brand_string(); + info.id = "CPU"; + info.num = 0; + info.has_osl = true; + info.has_nanovdb = true; + info.has_profiling = true; + if (openimagedenoise_supported()) { + info.denoisers |= DENOISER_OPENIMAGEDENOISE; + } + + devices.insert(devices.begin(), info); +} + +string device_cpu_capabilities() +{ + string capabilities = ""; + capabilities += system_cpu_support_sse2() ? "SSE2 " : ""; + capabilities += system_cpu_support_sse3() ? "SSE3 " : ""; + capabilities += system_cpu_support_sse41() ? "SSE41 " : ""; + capabilities += system_cpu_support_avx() ? "AVX " : ""; + capabilities += system_cpu_support_avx2() ? "AVX2" : ""; + if (capabilities[capabilities.size() - 1] == ' ') + capabilities.resize(capabilities.size() - 1); + return capabilities; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/device.h b/intern/cycles/device/cpu/device.h new file mode 100644 index 00000000000..c53bc338127 --- /dev/null +++ b/intern/cycles/device/cpu/device.h @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/string.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +Device *device_cpu_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_cpu_info(vector &devices); + +string device_cpu_capabilities(); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/device_impl.cpp b/intern/cycles/device/cpu/device_impl.cpp new file mode 100644 index 00000000000..d494b40f71d --- /dev/null +++ b/intern/cycles/device/cpu/device_impl.cpp @@ -0,0 +1,331 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/device_impl.h" + +#include +#include + +/* So ImathMath is included before our kernel_cpu_compat. */ +#ifdef WITH_OSL +/* So no context pollution happens from indirectly included windows.h */ +# include "util/windows.h" +# include +#endif + +#ifdef WITH_EMBREE +# include +#endif + +#include "device/cpu/kernel.h" +#include "device/cpu/kernel_thread_globals.h" + +#include "device/device.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" +#include "kernel/device/cpu/kernel.h" +#include "kernel/types.h" + +#include "kernel/osl/shader.h" +#include "kernel/osl/globals.h" +// clang-format on + +#include "bvh/embree.h" + +#include "session/buffers.h" + +#include "util/debug.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/log.h" +#include "util/map.h" +#include "util/openimagedenoise.h" +#include "util/optimization.h" +#include "util/progress.h" +#include "util/system.h" +#include "util/task.h" +#include "util/thread.h" + +CCL_NAMESPACE_BEGIN + +CPUDevice::CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_) + : Device(info_, stats_, profiler_), texture_info(this, "__texture_info", MEM_GLOBAL) +{ + /* Pick any kernel, all of them are supposed to have same level of microarchitecture + * optimization. */ + VLOG(1) << "Using " << kernels.integrator_init_from_camera.get_uarch_name() << " CPU kernels."; + + if (info.cpu_threads == 0) { + info.cpu_threads = TaskScheduler::num_threads(); + } + +#ifdef WITH_OSL + kernel_globals.osl = &osl_globals; +#endif +#ifdef WITH_EMBREE + embree_device = rtcNewDevice("verbose=0"); +#endif + need_texture_info = false; +} + +CPUDevice::~CPUDevice() +{ +#ifdef WITH_EMBREE + rtcReleaseDevice(embree_device); +#endif + + texture_info.free(); +} + +bool CPUDevice::show_samples() const +{ + return (info.cpu_threads == 1); +} + +BVHLayoutMask CPUDevice::get_bvh_layout_mask() const +{ + BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_BVH2; +#ifdef WITH_EMBREE + bvh_layout_mask |= BVH_LAYOUT_EMBREE; +#endif /* WITH_EMBREE */ + return bvh_layout_mask; +} + +bool CPUDevice::load_texture_info() +{ + if (!need_texture_info) { + return false; + } + + texture_info.copy_to_device(); + need_texture_info = false; + + return true; +} + +void CPUDevice::mem_alloc(device_memory &mem) +{ + if (mem.type == MEM_TEXTURE) { + assert(!"mem_alloc not supported for textures."); + } + else if (mem.type == MEM_GLOBAL) { + assert(!"mem_alloc not supported for global memory."); + } + else { + if (mem.name) { + VLOG(1) << "Buffer allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + } + + if (mem.type == MEM_DEVICE_ONLY) { + assert(!mem.host_pointer); + size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES; + void *data = util_aligned_malloc(mem.memory_size(), alignment); + mem.device_pointer = (device_ptr)data; + } + else { + mem.device_pointer = (device_ptr)mem.host_pointer; + } + + mem.device_size = mem.memory_size(); + stats.mem_alloc(mem.device_size); + } +} + +void CPUDevice::mem_copy_to(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + global_alloc(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + tex_alloc((device_texture &)mem); + } + else { + if (!mem.device_pointer) { + mem_alloc(mem); + } + + /* copy is no-op */ + } +} + +void CPUDevice::mem_copy_from( + device_memory & /*mem*/, size_t /*y*/, size_t /*w*/, size_t /*h*/, size_t /*elem*/) +{ + /* no-op */ +} + +void CPUDevice::mem_zero(device_memory &mem) +{ + if (!mem.device_pointer) { + mem_alloc(mem); + } + + if (mem.device_pointer) { + memset((void *)mem.device_pointer, 0, mem.memory_size()); + } +} + +void CPUDevice::mem_free(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + } + else if (mem.device_pointer) { + if (mem.type == MEM_DEVICE_ONLY) { + util_aligned_free((void *)mem.device_pointer); + } + mem.device_pointer = 0; + stats.mem_free(mem.device_size); + mem.device_size = 0; + } +} + +device_ptr CPUDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) +{ + return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); +} + +void CPUDevice::const_copy_to(const char *name, void *host, size_t size) +{ +#if WITH_EMBREE + if (strcmp(name, "__data") == 0) { + assert(size <= sizeof(KernelData)); + + // Update scene handle (since it is different for each device on multi devices) + KernelData *const data = (KernelData *)host; + data->bvh.scene = embree_scene; + } +#endif + kernel_const_copy(&kernel_globals, name, host, size); +} + +void CPUDevice::global_alloc(device_memory &mem) +{ + VLOG(1) << "Global memory allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + kernel_global_memory_copy(&kernel_globals, mem.name, mem.host_pointer, mem.data_size); + + mem.device_pointer = (device_ptr)mem.host_pointer; + mem.device_size = mem.memory_size(); + stats.mem_alloc(mem.device_size); +} + +void CPUDevice::global_free(device_memory &mem) +{ + if (mem.device_pointer) { + mem.device_pointer = 0; + stats.mem_free(mem.device_size); + mem.device_size = 0; + } +} + +void CPUDevice::tex_alloc(device_texture &mem) +{ + VLOG(1) << "Texture allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + mem.device_pointer = (device_ptr)mem.host_pointer; + mem.device_size = mem.memory_size(); + stats.mem_alloc(mem.device_size); + + const uint slot = mem.slot; + if (slot >= texture_info.size()) { + /* Allocate some slots in advance, to reduce amount of re-allocations. */ + texture_info.resize(slot + 128); + } + + texture_info[slot] = mem.info; + texture_info[slot].data = (uint64_t)mem.host_pointer; + need_texture_info = true; +} + +void CPUDevice::tex_free(device_texture &mem) +{ + if (mem.device_pointer) { + mem.device_pointer = 0; + stats.mem_free(mem.device_size); + mem.device_size = 0; + need_texture_info = true; + } +} + +void CPUDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) +{ +#ifdef WITH_EMBREE + if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE || + bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) { + BVHEmbree *const bvh_embree = static_cast(bvh); + if (refit) { + bvh_embree->refit(progress); + } + else { + bvh_embree->build(progress, &stats, embree_device); + } + + if (bvh->params.top_level) { + embree_scene = bvh_embree->scene; + } + } + else +#endif + Device::build_bvh(bvh, progress, refit); +} + +const CPUKernels *CPUDevice::get_cpu_kernels() const +{ + return &kernels; +} + +void CPUDevice::get_cpu_kernel_thread_globals( + vector &kernel_thread_globals) +{ + /* Ensure latest texture info is loaded into kernel globals before returning. */ + load_texture_info(); + + kernel_thread_globals.clear(); + void *osl_memory = get_cpu_osl_memory(); + for (int i = 0; i < info.cpu_threads; i++) { + kernel_thread_globals.emplace_back(kernel_globals, osl_memory, profiler); + } +} + +void *CPUDevice::get_cpu_osl_memory() +{ +#ifdef WITH_OSL + return &osl_globals; +#else + return NULL; +#endif +} + +bool CPUDevice::load_kernels(const uint /*kernel_features*/) +{ + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/device_impl.h b/intern/cycles/device/cpu/device_impl.h new file mode 100644 index 00000000000..553728ccc3b --- /dev/null +++ b/intern/cycles/device/cpu/device_impl.h @@ -0,0 +1,102 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* So ImathMath is included before our kernel_cpu_compat. */ +#ifdef WITH_OSL +/* So no context pollution happens from indirectly included windows.h */ +# include "util/windows.h" +# include +#endif + +#ifdef WITH_EMBREE +# include +#endif + +#include "device/cpu/kernel.h" +#include "device/device.h" +#include "device/memory.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/kernel.h" +#include "kernel/device/cpu/globals.h" + +#include "kernel/osl/shader.h" +#include "kernel/osl/globals.h" +// clang-format on + +CCL_NAMESPACE_BEGIN + +class CPUDevice : public Device { + public: + KernelGlobalsCPU kernel_globals; + + device_vector texture_info; + bool need_texture_info; + +#ifdef WITH_OSL + OSLGlobals osl_globals; +#endif +#ifdef WITH_EMBREE + RTCScene embree_scene = NULL; + RTCDevice embree_device; +#endif + + CPUKernels kernels; + + CPUDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_); + ~CPUDevice(); + + virtual bool show_samples() const override; + + virtual BVHLayoutMask get_bvh_layout_mask() const override; + + /* Returns true if the texture info was copied to the device (meaning, some more + * re-initialization might be needed). */ + bool load_texture_info(); + + virtual void mem_alloc(device_memory &mem) override; + virtual void mem_copy_to(device_memory &mem) override; + virtual void mem_copy_from( + device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override; + virtual void mem_zero(device_memory &mem) override; + virtual void mem_free(device_memory &mem) override; + virtual device_ptr mem_alloc_sub_ptr(device_memory &mem, + size_t offset, + size_t /*size*/) override; + + virtual void const_copy_to(const char *name, void *host, size_t size) override; + + void global_alloc(device_memory &mem); + void global_free(device_memory &mem); + + void tex_alloc(device_texture &mem); + void tex_free(device_texture &mem); + + void build_bvh(BVH *bvh, Progress &progress, bool refit) override; + + virtual const CPUKernels *get_cpu_kernels() const override; + virtual void get_cpu_kernel_thread_globals( + vector &kernel_thread_globals) override; + virtual void *get_cpu_osl_memory() override; + + protected: + virtual bool load_kernels(uint /*kernel_features*/) override; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel.cpp b/intern/cycles/device/cpu/kernel.cpp new file mode 100644 index 00000000000..3b253c094fd --- /dev/null +++ b/intern/cycles/device/cpu/kernel.cpp @@ -0,0 +1,60 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/kernel.h" + +#include "kernel/device/cpu/kernel.h" + +CCL_NAMESPACE_BEGIN + +#define KERNEL_FUNCTIONS(name) \ + KERNEL_NAME_EVAL(cpu, name), KERNEL_NAME_EVAL(cpu_sse2, name), \ + KERNEL_NAME_EVAL(cpu_sse3, name), KERNEL_NAME_EVAL(cpu_sse41, name), \ + KERNEL_NAME_EVAL(cpu_avx, name), KERNEL_NAME_EVAL(cpu_avx2, name) + +#define REGISTER_KERNEL(name) name(KERNEL_FUNCTIONS(name)) + +CPUKernels::CPUKernels() + : /* Integrator. */ + REGISTER_KERNEL(integrator_init_from_camera), + REGISTER_KERNEL(integrator_init_from_bake), + REGISTER_KERNEL(integrator_intersect_closest), + REGISTER_KERNEL(integrator_intersect_shadow), + REGISTER_KERNEL(integrator_intersect_subsurface), + REGISTER_KERNEL(integrator_intersect_volume_stack), + REGISTER_KERNEL(integrator_shade_background), + REGISTER_KERNEL(integrator_shade_light), + REGISTER_KERNEL(integrator_shade_shadow), + REGISTER_KERNEL(integrator_shade_surface), + REGISTER_KERNEL(integrator_shade_volume), + REGISTER_KERNEL(integrator_megakernel), + /* Shader evaluation. */ + REGISTER_KERNEL(shader_eval_displace), + REGISTER_KERNEL(shader_eval_background), + REGISTER_KERNEL(shader_eval_curve_shadow_transparency), + /* Adaptive sampling. */ + REGISTER_KERNEL(adaptive_sampling_convergence_check), + REGISTER_KERNEL(adaptive_sampling_filter_x), + REGISTER_KERNEL(adaptive_sampling_filter_y), + /* Cryptomatte. */ + REGISTER_KERNEL(cryptomatte_postprocess) +{ +} + +#undef REGISTER_KERNEL +#undef KERNEL_FUNCTIONS + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel.h b/intern/cycles/device/cpu/kernel.h new file mode 100644 index 00000000000..5beeaf148a1 --- /dev/null +++ b/intern/cycles/device/cpu/kernel.h @@ -0,0 +1,108 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/cpu/kernel_function.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +struct KernelGlobalsCPU; +struct IntegratorStateCPU; +struct TileInfo; + +class CPUKernels { + public: + /* Integrator. */ + + using IntegratorFunction = + CPUKernelFunction; + using IntegratorShadeFunction = CPUKernelFunction; + using IntegratorInitFunction = CPUKernelFunction; + + IntegratorInitFunction integrator_init_from_camera; + IntegratorInitFunction integrator_init_from_bake; + IntegratorFunction integrator_intersect_closest; + IntegratorFunction integrator_intersect_shadow; + IntegratorFunction integrator_intersect_subsurface; + IntegratorFunction integrator_intersect_volume_stack; + IntegratorShadeFunction integrator_shade_background; + IntegratorShadeFunction integrator_shade_light; + IntegratorShadeFunction integrator_shade_shadow; + IntegratorShadeFunction integrator_shade_surface; + IntegratorShadeFunction integrator_shade_volume; + IntegratorShadeFunction integrator_megakernel; + + /* Shader evaluation. */ + + using ShaderEvalFunction = CPUKernelFunction; + + ShaderEvalFunction shader_eval_displace; + ShaderEvalFunction shader_eval_background; + ShaderEvalFunction shader_eval_curve_shadow_transparency; + + /* Adaptive stopping. */ + + using AdaptiveSamplingConvergenceCheckFunction = + CPUKernelFunction; + + using AdaptiveSamplingFilterXFunction = + CPUKernelFunction; + + using AdaptiveSamplingFilterYFunction = + CPUKernelFunction; + + AdaptiveSamplingConvergenceCheckFunction adaptive_sampling_convergence_check; + + AdaptiveSamplingFilterXFunction adaptive_sampling_filter_x; + AdaptiveSamplingFilterYFunction adaptive_sampling_filter_y; + + /* Cryptomatte. */ + + using CryptomattePostprocessFunction = CPUKernelFunction; + + CryptomattePostprocessFunction cryptomatte_postprocess; + + CPUKernels(); +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel_function.h b/intern/cycles/device/cpu/kernel_function.h new file mode 100644 index 00000000000..5ff55499d48 --- /dev/null +++ b/intern/cycles/device/cpu/kernel_function.h @@ -0,0 +1,124 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/debug.h" +#include "util/system.h" + +CCL_NAMESPACE_BEGIN + +/* A wrapper around per-microarchitecture variant of a kernel function. + * + * Provides a function-call-like API which gets routed to the most suitable implementation. + * + * For example, on a computer which only has SSE4.1 the kernel_sse41 will be used. */ +template class CPUKernelFunction { + public: + CPUKernelFunction(FunctionType kernel_default, + FunctionType kernel_sse2, + FunctionType kernel_sse3, + FunctionType kernel_sse41, + FunctionType kernel_avx, + FunctionType kernel_avx2) + { + kernel_info_ = get_best_kernel_info( + kernel_default, kernel_sse2, kernel_sse3, kernel_sse41, kernel_avx, kernel_avx2); + } + + template inline auto operator()(Args... args) const + { + assert(kernel_info_.kernel); + + return kernel_info_.kernel(args...); + } + + const char *get_uarch_name() const + { + return kernel_info_.uarch_name; + } + + protected: + /* Helper class which allows to pass human-readable microarchitecture name together with function + * pointer. */ + class KernelInfo { + public: + KernelInfo() : KernelInfo("", nullptr) + { + } + + /* TODO(sergey): Use string view, to have higher-level functionality (i.e. comparison) without + * memory allocation. */ + KernelInfo(const char *uarch_name, FunctionType kernel) + : uarch_name(uarch_name), kernel(kernel) + { + } + + const char *uarch_name; + FunctionType kernel; + }; + + KernelInfo get_best_kernel_info(FunctionType kernel_default, + FunctionType kernel_sse2, + FunctionType kernel_sse3, + FunctionType kernel_sse41, + FunctionType kernel_avx, + FunctionType kernel_avx2) + { + /* Silence warnings about unused variables when compiling without some architectures. */ + (void)kernel_sse2; + (void)kernel_sse3; + (void)kernel_sse41; + (void)kernel_avx; + (void)kernel_avx2; + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 + if (DebugFlags().cpu.has_avx2() && system_cpu_support_avx2()) { + return KernelInfo("AVX2", kernel_avx2); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX + if (DebugFlags().cpu.has_avx() && system_cpu_support_avx()) { + return KernelInfo("AVX", kernel_avx); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 + if (DebugFlags().cpu.has_sse41() && system_cpu_support_sse41()) { + return KernelInfo("SSE4.1", kernel_sse41); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 + if (DebugFlags().cpu.has_sse3() && system_cpu_support_sse3()) { + return KernelInfo("SSE3", kernel_sse3); + } +#endif + +#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 + if (DebugFlags().cpu.has_sse2() && system_cpu_support_sse2()) { + return KernelInfo("SSE2", kernel_sse2); + } +#endif + + return KernelInfo("default", kernel_default); + } + + KernelInfo kernel_info_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel_thread_globals.cpp b/intern/cycles/device/cpu/kernel_thread_globals.cpp new file mode 100644 index 00000000000..739b6460318 --- /dev/null +++ b/intern/cycles/device/cpu/kernel_thread_globals.cpp @@ -0,0 +1,85 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/cpu/kernel_thread_globals.h" + +// clang-format off +#include "kernel/osl/shader.h" +#include "kernel/osl/globals.h" +// clang-format on + +#include "util/profiling.h" + +CCL_NAMESPACE_BEGIN + +CPUKernelThreadGlobals::CPUKernelThreadGlobals(const KernelGlobalsCPU &kernel_globals, + void *osl_globals_memory, + Profiler &cpu_profiler) + : KernelGlobalsCPU(kernel_globals), cpu_profiler_(cpu_profiler) +{ + reset_runtime_memory(); + +#ifdef WITH_OSL + OSLShader::thread_init(this, reinterpret_cast(osl_globals_memory)); +#else + (void)osl_globals_memory; +#endif +} + +CPUKernelThreadGlobals::CPUKernelThreadGlobals(CPUKernelThreadGlobals &&other) noexcept + : KernelGlobalsCPU(std::move(other)), cpu_profiler_(other.cpu_profiler_) +{ + other.reset_runtime_memory(); +} + +CPUKernelThreadGlobals::~CPUKernelThreadGlobals() +{ +#ifdef WITH_OSL + OSLShader::thread_free(this); +#endif +} + +CPUKernelThreadGlobals &CPUKernelThreadGlobals::operator=(CPUKernelThreadGlobals &&other) +{ + if (this == &other) { + return *this; + } + + *static_cast(this) = *static_cast(&other); + + other.reset_runtime_memory(); + + return *this; +} + +void CPUKernelThreadGlobals::reset_runtime_memory() +{ +#ifdef WITH_OSL + osl = nullptr; +#endif +} + +void CPUKernelThreadGlobals::start_profiling() +{ + cpu_profiler_.add_state(&profiler); +} + +void CPUKernelThreadGlobals::stop_profiling() +{ + cpu_profiler_.remove_state(&profiler); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cpu/kernel_thread_globals.h b/intern/cycles/device/cpu/kernel_thread_globals.h new file mode 100644 index 00000000000..5aeeaf678d0 --- /dev/null +++ b/intern/cycles/device/cpu/kernel_thread_globals.h @@ -0,0 +1,57 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + +CCL_NAMESPACE_BEGIN + +class Profiler; + +/* A special class which extends memory ownership of the `KernelGlobalsCPU` decoupling any resource + * which is not thread-safe for access. Every worker thread which needs to operate on + * `KernelGlobalsCPU` needs to initialize its own copy of this object. + * + * NOTE: Only minimal subset of objects are copied: `KernelData` is never copied. This means that + * there is no unnecessary data duplication happening when using this object. */ +class CPUKernelThreadGlobals : public KernelGlobalsCPU { + public: + /* TODO(sergey): Would be nice to have properly typed OSLGlobals even in the case when building + * without OSL support. Will avoid need to those unnamed pointers and casts. */ + CPUKernelThreadGlobals(const KernelGlobalsCPU &kernel_globals, + void *osl_globals_memory, + Profiler &cpu_profiler); + + ~CPUKernelThreadGlobals(); + + CPUKernelThreadGlobals(const CPUKernelThreadGlobals &other) = delete; + CPUKernelThreadGlobals(CPUKernelThreadGlobals &&other) noexcept; + + CPUKernelThreadGlobals &operator=(const CPUKernelThreadGlobals &other) = delete; + CPUKernelThreadGlobals &operator=(CPUKernelThreadGlobals &&other); + + void start_profiling(); + void stop_profiling(); + + protected: + void reset_runtime_memory(); + + Profiler &cpu_profiler_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_cuda.cpp b/intern/cycles/device/cuda/device.cpp similarity index 91% rename from intern/cycles/device/device_cuda.cpp rename to intern/cycles/device/cuda/device.cpp index 2e225ecfaf8..0d9e6c72466 100644 --- a/intern/cycles/device/device_cuda.cpp +++ b/intern/cycles/device/cuda/device.cpp @@ -14,21 +14,25 @@ * limitations under the License. */ +#include "device/cuda/device.h" + +#include "util/log.h" + #ifdef WITH_CUDA - -# include "device/cuda/device_cuda.h" +# include "device/cuda/device_impl.h" # include "device/device.h" -# include "device/device_intern.h" -# include "util/util_logging.h" -# include "util/util_string.h" -# include "util/util_windows.h" +# include "util/string.h" +# include "util/windows.h" +#endif /* WITH_CUDA */ CCL_NAMESPACE_BEGIN bool device_cuda_init() { -# ifdef WITH_CUDA_DYNLOAD +#if !defined(WITH_CUDA) + return false; +#elif defined(WITH_CUDA_DYNLOAD) static bool initialized = false; static bool result = false; @@ -59,16 +63,27 @@ bool device_cuda_init() } return result; -# else /* WITH_CUDA_DYNLOAD */ +#else /* WITH_CUDA_DYNLOAD */ return true; -# endif /* WITH_CUDA_DYNLOAD */ +#endif /* WITH_CUDA_DYNLOAD */ } -Device *device_cuda_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) +Device *device_cuda_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) { - return new CUDADevice(info, stats, profiler, background); +#ifdef WITH_CUDA + return new CUDADevice(info, stats, profiler); +#else + (void)info; + (void)stats; + (void)profiler; + + LOG(FATAL) << "Request to create CUDA device without compiled-in support. Should never happen."; + + return nullptr; +#endif } +#ifdef WITH_CUDA static CUresult device_cuda_safe_init() { # ifdef _WIN32 @@ -86,9 +101,11 @@ static CUresult device_cuda_safe_init() return cuInit(0); # endif } +#endif /* WITH_CUDA */ void device_cuda_info(vector &devices) { +#ifdef WITH_CUDA CUresult result = device_cuda_safe_init(); if (result != CUDA_SUCCESS) { if (result != CUDA_ERROR_NO_DEVICE) @@ -127,11 +144,10 @@ void device_cuda_info(vector &devices) info.description = string(name); 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; + info.denoisers = 0; + + info.has_gpu_queue = true; /* Check if the device has P2P access to any other device in the system. */ for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) { @@ -182,10 +198,14 @@ void device_cuda_info(vector &devices) if (!display_devices.empty()) devices.insert(devices.end(), display_devices.begin(), display_devices.end()); +#else /* WITH_CUDA */ + (void)devices; +#endif /* WITH_CUDA */ } string device_cuda_capabilities() { +#ifdef WITH_CUDA CUresult result = device_cuda_safe_init(); if (result != CUDA_SUCCESS) { if (result != CUDA_ERROR_NO_DEVICE) { @@ -310,8 +330,10 @@ string device_cuda_capabilities() } return capabilities; + +#else /* WITH_CUDA */ + return ""; +#endif /* WITH_CUDA */ } CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/cuda/device.h b/intern/cycles/device/cuda/device.h new file mode 100644 index 00000000000..7142ad19857 --- /dev/null +++ b/intern/cycles/device/cuda/device.h @@ -0,0 +1,37 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/string.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +bool device_cuda_init(); + +Device *device_cuda_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_cuda_info(vector &devices); + +string device_cuda_capabilities(); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/cuda/device_cuda.h b/intern/cycles/device/cuda/device_cuda.h deleted file mode 100644 index c3271c3cfcf..00000000000 --- a/intern/cycles/device/cuda/device_cuda.h +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_CUDA - -# include "device/device.h" -# include "device/device_denoising.h" -# include "device/device_split_kernel.h" - -# include "util/util_map.h" -# include "util/util_task.h" - -# ifdef WITH_CUDA_DYNLOAD -# include "cuew.h" -# else -# include "util/util_opengl.h" -# include -# include -# endif - -CCL_NAMESPACE_BEGIN - -class CUDASplitKernel; - -class CUDADevice : public Device { - - friend class CUDASplitKernelFunction; - friend class CUDASplitKernel; - friend class CUDAContextScope; - - public: - DedicatedTaskPool task_pool; - CUdevice cuDevice; - CUcontext cuContext; - CUmodule cuModule, cuFilterModule; - size_t device_texture_headroom; - size_t device_working_headroom; - bool move_texture_to_host; - size_t map_host_used; - size_t map_host_limit; - int can_map_host; - int pitch_alignment; - int cuDevId; - int cuDevArchitecture; - bool first_error; - CUDASplitKernel *split_kernel; - - struct CUDAMem { - CUDAMem() : texobject(0), array(0), use_mapped_host(false) - { - } - - CUtexObject texobject; - CUarray array; - - /* If true, a mapped host memory in shared_pointer is being used. */ - bool use_mapped_host; - }; - typedef map CUDAMemMap; - CUDAMemMap cuda_mem_map; - thread_mutex cuda_mem_map_mutex; - - struct PixelMem { - GLuint cuPBO; - CUgraphicsResource cuPBOresource; - GLuint cuTexId; - int w, h; - }; - map pixel_mem_map; - - /* Bindless Textures */ - device_vector texture_info; - bool need_texture_info; - - /* Kernels */ - struct { - bool loaded; - - CUfunction adaptive_stopping; - CUfunction adaptive_filter_x; - CUfunction adaptive_filter_y; - CUfunction adaptive_scale_samples; - int adaptive_num_threads_per_block; - } functions; - - static bool have_precompiled_kernels(); - - virtual bool show_samples() const override; - - virtual BVHLayoutMask get_bvh_layout_mask() const override; - - void set_error(const string &error) override; - - CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_); - - virtual ~CUDADevice(); - - bool support_device(const DeviceRequestedFeatures & /*requested_features*/); - - bool check_peer_access(Device *peer_device) override; - - bool use_adaptive_compilation(); - - bool use_split_kernel(); - - virtual string compile_kernel_get_common_cflags( - const DeviceRequestedFeatures &requested_features, bool filter = false, bool split = false); - - string compile_kernel(const DeviceRequestedFeatures &requested_features, - const char *name, - const char *base = "cuda", - bool force_ptx = false); - - virtual bool load_kernels(const DeviceRequestedFeatures &requested_features) override; - - void load_functions(); - - void reserve_local_memory(const DeviceRequestedFeatures &requested_features); - - void init_host_memory(); - - void load_texture_info(); - - void move_textures_to_host(size_t size, bool for_texture); - - CUDAMem *generic_alloc(device_memory &mem, size_t pitch_padding = 0); - - void generic_copy_to(device_memory &mem); - - void generic_free(device_memory &mem); - - void mem_alloc(device_memory &mem) override; - - void mem_copy_to(device_memory &mem) override; - - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override; - - void mem_zero(device_memory &mem) override; - - void mem_free(device_memory &mem) override; - - device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override; - - virtual void const_copy_to(const char *name, void *host, size_t size) override; - - void global_alloc(device_memory &mem); - - void global_free(device_memory &mem); - - void tex_alloc(device_texture &mem); - - void tex_free(device_texture &mem); - - bool denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task); - - bool denoising_construct_transform(DenoisingTask *task); - - bool denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task); - - bool denoising_solve(device_ptr output_ptr, DenoisingTask *task); - - bool denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task); - - bool denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task); - - bool denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task); - - bool denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task); - - bool denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task); - - void denoise(RenderTile &rtile, DenoisingTask &denoising); - - void adaptive_sampling_filter(uint filter_sample, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream = 0); - void adaptive_sampling_post(RenderTile &rtile, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream = 0); - - void render(DeviceTask &task, RenderTile &rtile, device_vector &work_tiles); - - void film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half); - - void shader(DeviceTask &task); - - CUdeviceptr map_pixels(device_ptr mem); - - void unmap_pixels(device_ptr mem); - - void pixels_alloc(device_memory &mem); - - void pixels_copy_from(device_memory &mem, int y, int w, int h); - - void pixels_free(device_memory &mem); - - void draw_pixels(device_memory &mem, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) override; - - void thread_run(DeviceTask &task); - - virtual void task_add(DeviceTask &task) override; - - virtual void task_wait() override; - - virtual void task_cancel() override; -}; - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/cuda/device_cuda_impl.cpp b/intern/cycles/device/cuda/device_cuda_impl.cpp deleted file mode 100644 index 2d2fcb38705..00000000000 --- a/intern/cycles/device/cuda/device_cuda_impl.cpp +++ /dev/null @@ -1,2714 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_CUDA - -# include -# include -# include -# include -# include - -# include "device/cuda/device_cuda.h" -# include "device/device_intern.h" -# include "device/device_split_kernel.h" - -# include "render/buffers.h" - -# include "kernel/filter/filter_defines.h" - -# include "util/util_debug.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_map.h" -# include "util/util_md5.h" -# include "util/util_opengl.h" -# include "util/util_path.h" -# include "util/util_string.h" -# include "util/util_system.h" -# include "util/util_time.h" -# include "util/util_types.h" -# include "util/util_windows.h" - -# include "kernel/split/kernel_split_data_types.h" - -CCL_NAMESPACE_BEGIN - -# ifndef WITH_CUDA_DYNLOAD - -/* Transparently implement some functions, so majority of the file does not need - * to worry about difference between dynamically loaded and linked CUDA at all. - */ - -namespace { - -const char *cuewErrorString(CUresult result) -{ - /* We can only give error code here without major code duplication, that - * should be enough since dynamic loading is only being disabled by folks - * who knows what they're doing anyway. - * - * NOTE: Avoid call from several threads. - */ - static string error; - error = string_printf("%d", result); - return error.c_str(); -} - -const char *cuewCompilerPath() -{ - return CYCLES_CUDA_NVCC_EXECUTABLE; -} - -int cuewCompilerVersion() -{ - return (CUDA_VERSION / 100) + (CUDA_VERSION % 100 / 10); -} - -} /* namespace */ -# endif /* WITH_CUDA_DYNLOAD */ - -class CUDADevice; - -class CUDASplitKernel : public DeviceSplitKernel { - CUDADevice *device; - - public: - explicit CUDASplitKernel(CUDADevice *device); - - virtual uint64_t state_buffer_size(device_memory &kg, device_memory &data, size_t num_threads); - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data_, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs); - - virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &); - virtual int2 split_kernel_local_size(); - virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask &task); -}; - -/* Utility to push/pop CUDA context. */ -class CUDAContextScope { - public: - CUDAContextScope(CUDADevice *device); - ~CUDAContextScope(); - - private: - CUDADevice *device; -}; - -bool CUDADevice::have_precompiled_kernels() -{ - string cubins_path = path_get("lib"); - return path_exists(cubins_path); -} - -bool CUDADevice::show_samples() const -{ - /* The CUDADevice only processes one tile at a time, so showing samples is fine. */ - return true; -} - -BVHLayoutMask CUDADevice::get_bvh_layout_mask() const -{ - return BVH_LAYOUT_BVH2; -} - -void CUDADevice::set_error(const string &error) -{ - Device::set_error(error); - - if (first_error) { - fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n"); - fprintf(stderr, - "https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html\n\n"); - first_error = false; - } -} - -# define cuda_assert(stmt) \ - { \ - CUresult result = stmt; \ - if (result != CUDA_SUCCESS) { \ - const char *name = cuewErrorString(result); \ - set_error(string_printf("%s in %s (device_cuda_impl.cpp:%d)", name, #stmt, __LINE__)); \ - } \ - } \ - (void)0 - -CUDADevice::CUDADevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background_) - : Device(info, stats, profiler, background_), texture_info(this, "__texture_info", MEM_GLOBAL) -{ - first_error = true; - background = background_; - - cuDevId = info.num; - cuDevice = 0; - cuContext = 0; - - cuModule = 0; - cuFilterModule = 0; - - split_kernel = NULL; - - need_texture_info = false; - - device_texture_headroom = 0; - device_working_headroom = 0; - move_texture_to_host = false; - map_host_limit = 0; - map_host_used = 0; - can_map_host = 0; - pitch_alignment = 0; - - functions.loaded = false; - - /* Initialize CUDA. */ - CUresult result = cuInit(0); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to initialize CUDA runtime (%s)", cuewErrorString(result))); - return; - } - - /* Setup device and context. */ - result = cuDeviceGet(&cuDevice, cuDevId); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to get CUDA device handle from ordinal (%s)", - cuewErrorString(result))); - return; - } - - /* CU_CTX_MAP_HOST for mapping host memory when out of device memory. - * CU_CTX_LMEM_RESIZE_TO_MAX for reserving local memory ahead of render, - * so we can predict which memory to map to host. */ - cuda_assert( - cuDeviceGetAttribute(&can_map_host, CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY, cuDevice)); - - cuda_assert(cuDeviceGetAttribute( - &pitch_alignment, CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT, cuDevice)); - - unsigned int ctx_flags = CU_CTX_LMEM_RESIZE_TO_MAX; - if (can_map_host) { - ctx_flags |= CU_CTX_MAP_HOST; - init_host_memory(); - } - - /* Create context. */ - if (background) { - result = cuCtxCreate(&cuContext, ctx_flags, cuDevice); - } - else { - result = cuGLCtxCreate(&cuContext, ctx_flags, cuDevice); - - if (result != CUDA_SUCCESS) { - result = cuCtxCreate(&cuContext, ctx_flags, cuDevice); - background = true; - } - } - - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to create CUDA context (%s)", cuewErrorString(result))); - return; - } - - int major, minor; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); - cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); - cuDevArchitecture = major * 100 + minor * 10; - - /* Pop context set by cuCtxCreate. */ - cuCtxPopCurrent(NULL); -} - -CUDADevice::~CUDADevice() -{ - task_pool.cancel(); - - delete split_kernel; - - texture_info.free(); - - cuda_assert(cuCtxDestroy(cuContext)); -} - -bool CUDADevice::support_device(const DeviceRequestedFeatures & /*requested_features*/) -{ - int major, minor; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); - cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); - - /* We only support sm_30 and above */ - if (major < 3) { - set_error(string_printf( - "CUDA backend requires compute capability 3.0 or up, but found %d.%d.", major, minor)); - return false; - } - - return true; -} - -bool CUDADevice::check_peer_access(Device *peer_device) -{ - if (peer_device == this) { - return false; - } - if (peer_device->info.type != DEVICE_CUDA && peer_device->info.type != DEVICE_OPTIX) { - return false; - } - - CUDADevice *const peer_device_cuda = static_cast(peer_device); - - int can_access = 0; - cuda_assert(cuDeviceCanAccessPeer(&can_access, cuDevice, peer_device_cuda->cuDevice)); - if (can_access == 0) { - return false; - } - - // Ensure array access over the link is possible as well (for 3D textures) - cuda_assert(cuDeviceGetP2PAttribute(&can_access, - CU_DEVICE_P2P_ATTRIBUTE_ARRAY_ACCESS_ACCESS_SUPPORTED, - cuDevice, - peer_device_cuda->cuDevice)); - if (can_access == 0) { - return false; - } - - // Enable peer access in both directions - { - const CUDAContextScope scope(this); - CUresult result = cuCtxEnablePeerAccess(peer_device_cuda->cuContext, 0); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to enable peer access on CUDA context (%s)", - cuewErrorString(result))); - return false; - } - } - { - const CUDAContextScope scope(peer_device_cuda); - CUresult result = cuCtxEnablePeerAccess(cuContext, 0); - if (result != CUDA_SUCCESS) { - set_error(string_printf("Failed to enable peer access on CUDA context (%s)", - cuewErrorString(result))); - return false; - } - } - - return true; -} - -bool CUDADevice::use_adaptive_compilation() -{ - return DebugFlags().cuda.adaptive_compile; -} - -bool CUDADevice::use_split_kernel() -{ - return DebugFlags().cuda.split_kernel; -} - -/* Common NVCC flags which stays the same regardless of shading model, - * kernel sources md5 and only depends on compiler or compilation settings. - */ -string CUDADevice::compile_kernel_get_common_cflags( - const DeviceRequestedFeatures &requested_features, bool filter, bool split) -{ - const int machine = system_cpu_bits(); - const string source_path = path_get("source"); - const string include_path = source_path; - string cflags = string_printf( - "-m%d " - "--ptxas-options=\"-v\" " - "--use_fast_math " - "-DNVCC " - "-I\"%s\"", - machine, - include_path.c_str()); - if (!filter && use_adaptive_compilation()) { - cflags += " " + requested_features.get_build_options(); - } - const char *extra_cflags = getenv("CYCLES_CUDA_EXTRA_CFLAGS"); - if (extra_cflags) { - cflags += string(" ") + string(extra_cflags); - } - - if (split) { - cflags += " -D__SPLIT__"; - } - -# ifdef WITH_NANOVDB - cflags += " -DWITH_NANOVDB"; -# endif - - return cflags; -} - -string CUDADevice::compile_kernel(const DeviceRequestedFeatures &requested_features, - const char *name, - const char *base, - bool force_ptx) -{ - /* Compute kernel name. */ - int major, minor; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); - cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); - - /* Attempt to use kernel provided with Blender. */ - if (!use_adaptive_compilation()) { - if (!force_ptx) { - const string cubin = path_get(string_printf("lib/%s_sm_%d%d.cubin", name, major, minor)); - VLOG(1) << "Testing for pre-compiled kernel " << cubin << "."; - if (path_exists(cubin)) { - VLOG(1) << "Using precompiled kernel."; - return cubin; - } - } - - /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */ - int ptx_major = major, ptx_minor = minor; - while (ptx_major >= 3) { - const string ptx = path_get( - string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor)); - VLOG(1) << "Testing for pre-compiled kernel " << ptx << "."; - if (path_exists(ptx)) { - VLOG(1) << "Using precompiled kernel."; - return ptx; - } - - if (ptx_minor > 0) { - ptx_minor--; - } - else { - ptx_major--; - ptx_minor = 9; - } - } - } - - /* Try to use locally compiled kernel. */ - string source_path = path_get("source"); - const string source_md5 = path_files_md5_hash(source_path); - - /* We include cflags into md5 so changing cuda toolkit or changing other - * compiler command line arguments makes sure cubin gets re-built. - */ - string common_cflags = compile_kernel_get_common_cflags( - requested_features, strstr(name, "filter") != NULL, strstr(name, "split") != NULL); - const string kernel_md5 = util_md5_string(source_md5 + common_cflags); - - const char *const kernel_ext = force_ptx ? "ptx" : "cubin"; - const char *const kernel_arch = force_ptx ? "compute" : "sm"; - const string cubin_file = string_printf( - "cycles_%s_%s_%d%d_%s.%s", name, kernel_arch, major, minor, kernel_md5.c_str(), kernel_ext); - const string cubin = path_cache_get(path_join("kernels", cubin_file)); - VLOG(1) << "Testing for locally compiled kernel " << cubin << "."; - if (path_exists(cubin)) { - VLOG(1) << "Using locally compiled kernel."; - return cubin; - } - -# ifdef _WIN32 - if (!use_adaptive_compilation() && have_precompiled_kernels()) { - if (major < 3) { - set_error( - string_printf("CUDA backend requires compute capability 3.0 or up, but found %d.%d. " - "Your GPU is not supported.", - major, - minor)); - } - else { - set_error( - string_printf("CUDA binary kernel for this graphics card compute " - "capability (%d.%d) not found.", - major, - minor)); - } - return string(); - } -# endif - - /* Compile. */ - const char *const nvcc = cuewCompilerPath(); - if (nvcc == NULL) { - set_error( - "CUDA nvcc compiler not found. " - "Install CUDA toolkit in default location."); - return string(); - } - - const int nvcc_cuda_version = cuewCompilerVersion(); - VLOG(1) << "Found nvcc " << nvcc << ", CUDA version " << nvcc_cuda_version << "."; - if (nvcc_cuda_version < 101) { - printf( - "Unsupported CUDA version %d.%d detected, " - "you need CUDA 10.1 or newer.\n", - nvcc_cuda_version / 10, - nvcc_cuda_version % 10); - return string(); - } - else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102 || nvcc_cuda_version == 111 || - nvcc_cuda_version == 112 || nvcc_cuda_version == 113 || nvcc_cuda_version == 114)) { - printf( - "CUDA version %d.%d detected, build may succeed but only " - "CUDA 10.1 to 11.4 are officially supported.\n", - nvcc_cuda_version / 10, - nvcc_cuda_version % 10); - } - - double starttime = time_dt(); - - path_create_directories(cubin); - - source_path = path_join(path_join(source_path, "kernel"), - path_join("kernels", path_join(base, string_printf("%s.cu", name)))); - - string command = string_printf( - "\"%s\" " - "-arch=%s_%d%d " - "--%s \"%s\" " - "-o \"%s\" " - "%s", - nvcc, - kernel_arch, - major, - minor, - kernel_ext, - source_path.c_str(), - cubin.c_str(), - common_cflags.c_str()); - - printf("Compiling CUDA kernel ...\n%s\n", command.c_str()); - -# ifdef _WIN32 - command = "call " + command; -# endif - if (system(command.c_str()) != 0) { - set_error( - "Failed to execute compilation command, " - "see console for details."); - return string(); - } - - /* Verify if compilation succeeded */ - if (!path_exists(cubin)) { - set_error( - "CUDA kernel compilation failed, " - "see console for details."); - return string(); - } - - printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime); - - return cubin; -} - -bool CUDADevice::load_kernels(const DeviceRequestedFeatures &requested_features) -{ - /* TODO(sergey): Support kernels re-load for CUDA devices. - * - * Currently re-loading kernel will invalidate memory pointers, - * causing problems in cuCtxSynchronize. - */ - if (cuFilterModule && cuModule) { - VLOG(1) << "Skipping kernel reload, not currently supported."; - return true; - } - - /* check if cuda init succeeded */ - if (cuContext == 0) - return false; - - /* check if GPU is supported */ - if (!support_device(requested_features)) - return false; - - /* get kernel */ - const char *kernel_name = use_split_kernel() ? "kernel_split" : "kernel"; - string cubin = compile_kernel(requested_features, kernel_name); - if (cubin.empty()) - return false; - - const char *filter_name = "filter"; - string filter_cubin = compile_kernel(requested_features, filter_name); - if (filter_cubin.empty()) - return false; - - /* open module */ - CUDAContextScope scope(this); - - string cubin_data; - CUresult result; - - if (path_read_text(cubin, cubin_data)) - result = cuModuleLoadData(&cuModule, cubin_data.c_str()); - else - result = CUDA_ERROR_FILE_NOT_FOUND; - - if (result != CUDA_SUCCESS) - set_error(string_printf( - "Failed to load CUDA kernel from '%s' (%s)", cubin.c_str(), cuewErrorString(result))); - - if (path_read_text(filter_cubin, cubin_data)) - result = cuModuleLoadData(&cuFilterModule, cubin_data.c_str()); - else - result = CUDA_ERROR_FILE_NOT_FOUND; - - if (result != CUDA_SUCCESS) - set_error(string_printf("Failed to load CUDA kernel from '%s' (%s)", - filter_cubin.c_str(), - cuewErrorString(result))); - - if (result == CUDA_SUCCESS) { - reserve_local_memory(requested_features); - } - - load_functions(); - - return (result == CUDA_SUCCESS); -} - -void CUDADevice::load_functions() -{ - /* TODO: load all functions here. */ - if (functions.loaded) { - return; - } - functions.loaded = true; - - cuda_assert(cuModuleGetFunction( - &functions.adaptive_stopping, cuModule, "kernel_cuda_adaptive_stopping")); - cuda_assert(cuModuleGetFunction( - &functions.adaptive_filter_x, cuModule, "kernel_cuda_adaptive_filter_x")); - cuda_assert(cuModuleGetFunction( - &functions.adaptive_filter_y, cuModule, "kernel_cuda_adaptive_filter_y")); - cuda_assert(cuModuleGetFunction( - &functions.adaptive_scale_samples, cuModule, "kernel_cuda_adaptive_scale_samples")); - - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_stopping, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_filter_x, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_filter_y, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(functions.adaptive_scale_samples, CU_FUNC_CACHE_PREFER_L1)); - - int unused_min_blocks; - cuda_assert(cuOccupancyMaxPotentialBlockSize(&unused_min_blocks, - &functions.adaptive_num_threads_per_block, - functions.adaptive_scale_samples, - NULL, - 0, - 0)); -} - -void CUDADevice::reserve_local_memory(const DeviceRequestedFeatures &requested_features) -{ - if (use_split_kernel()) { - /* Split kernel mostly uses global memory and adaptive compilation, - * difficult to predict how much is needed currently. */ - return; - } - - /* Together with CU_CTX_LMEM_RESIZE_TO_MAX, this reserves local memory - * needed for kernel launches, so that we can reliably figure out when - * to allocate scene data in mapped host memory. */ - CUDAContextScope scope(this); - - size_t total = 0, free_before = 0, free_after = 0; - cuMemGetInfo(&free_before, &total); - - /* Get kernel function. */ - CUfunction cuRender; - - if (requested_features.use_baking) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake")); - } - else if (requested_features.use_integrator_branched) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_branched_path_trace")); - } - else { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace")); - } - - cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1)); - - int min_blocks, num_threads_per_block; - cuda_assert( - cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0)); - - /* Launch kernel, using just 1 block appears sufficient to reserve - * memory for all multiprocessors. It would be good to do this in - * parallel for the multi GPU case still to make it faster. */ - CUdeviceptr d_work_tiles = 0; - uint total_work_size = 0; - - void *args[] = {&d_work_tiles, &total_work_size}; - - cuda_assert(cuLaunchKernel(cuRender, 1, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); - - cuda_assert(cuCtxSynchronize()); - - cuMemGetInfo(&free_after, &total); - VLOG(1) << "Local memory reserved " << string_human_readable_number(free_before - free_after) - << " bytes. (" << string_human_readable_size(free_before - free_after) << ")"; - -# if 0 - /* For testing mapped host memory, fill up device memory. */ - const size_t keep_mb = 1024; - - while (free_after > keep_mb * 1024 * 1024LL) { - CUdeviceptr tmp; - cuda_assert(cuMemAlloc(&tmp, 10 * 1024 * 1024LL)); - cuMemGetInfo(&free_after, &total); - } -# endif -} - -void CUDADevice::init_host_memory() -{ - /* Limit amount of host mapped memory, because allocating too much can - * cause system instability. Leave at least half or 4 GB of system - * memory free, whichever is smaller. */ - size_t default_limit = 4 * 1024 * 1024 * 1024LL; - size_t system_ram = system_physical_ram(); - - if (system_ram > 0) { - if (system_ram / 2 > default_limit) { - map_host_limit = system_ram - default_limit; - } - else { - map_host_limit = system_ram / 2; - } - } - else { - VLOG(1) << "Mapped host memory disabled, failed to get system RAM"; - map_host_limit = 0; - } - - /* Amount of device memory to keep is free after texture memory - * and working memory allocations respectively. We set the working - * memory limit headroom lower so that some space is left after all - * texture memory allocations. */ - device_working_headroom = 32 * 1024 * 1024LL; // 32MB - device_texture_headroom = 128 * 1024 * 1024LL; // 128MB - - VLOG(1) << "Mapped host memory limit set to " << string_human_readable_number(map_host_limit) - << " bytes. (" << string_human_readable_size(map_host_limit) << ")"; -} - -void CUDADevice::load_texture_info() -{ - if (need_texture_info) { - /* Unset flag before copying, so this does not loop indefinitely if the copy below calls - * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ - need_texture_info = false; - texture_info.copy_to_device(); - } -} - -void CUDADevice::move_textures_to_host(size_t size, bool for_texture) -{ - /* Break out of recursive call, which can happen when moving memory on a multi device. */ - static bool any_device_moving_textures_to_host = false; - if (any_device_moving_textures_to_host) { - return; - } - - /* Signal to reallocate textures in host memory only. */ - move_texture_to_host = true; - - while (size > 0) { - /* Find suitable memory allocation to move. */ - device_memory *max_mem = NULL; - size_t max_size = 0; - bool max_is_image = false; - - thread_scoped_lock lock(cuda_mem_map_mutex); - foreach (CUDAMemMap::value_type &pair, cuda_mem_map) { - device_memory &mem = *pair.first; - CUDAMem *cmem = &pair.second; - - /* Can only move textures allocated on this device (and not those from peer devices). - * And need to ignore memory that is already on the host. */ - if (!mem.is_resident(this) || cmem->use_mapped_host) { - continue; - } - - bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && - (&mem != &texture_info); - bool is_image = is_texture && (mem.data_height > 1); - - /* Can't move this type of memory. */ - if (!is_texture || cmem->array) { - continue; - } - - /* For other textures, only move image textures. */ - if (for_texture && !is_image) { - continue; - } - - /* Try to move largest allocation, prefer moving images. */ - if (is_image > max_is_image || (is_image == max_is_image && mem.device_size > max_size)) { - max_is_image = is_image; - max_size = mem.device_size; - max_mem = &mem; - } - } - lock.unlock(); - - /* Move to host memory. This part is mutex protected since - * multiple CUDA devices could be moving the memory. The - * first one will do it, and the rest will adopt the pointer. */ - if (max_mem) { - VLOG(1) << "Move memory from device to host: " << max_mem->name; - - static thread_mutex move_mutex; - thread_scoped_lock lock(move_mutex); - - any_device_moving_textures_to_host = true; - - /* Potentially need to call back into multi device, so pointer mapping - * and peer devices are updated. This is also necessary since the device - * pointer may just be a key here, so cannot be accessed and freed directly. - * Unfortunately it does mean that memory is reallocated on all other - * devices as well, which is potentially dangerous when still in use (since - * a thread rendering on another devices would only be caught in this mutex - * if it so happens to do an allocation at the same time as well. */ - max_mem->device_copy_to(); - size = (max_size >= size) ? 0 : size - max_size; - - any_device_moving_textures_to_host = false; - } - else { - break; - } - } - - /* Unset flag before texture info is reloaded, since it should stay in device memory. */ - move_texture_to_host = false; - - /* Update texture info array with new pointers. */ - load_texture_info(); -} - -CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_padding) -{ - CUDAContextScope scope(this); - - CUdeviceptr device_pointer = 0; - size_t size = mem.memory_size() + pitch_padding; - - CUresult mem_alloc_result = CUDA_ERROR_OUT_OF_MEMORY; - const char *status = ""; - - /* First try allocating in device memory, respecting headroom. We make - * an exception for texture info. It is small and frequently accessed, - * so treat it as working memory. - * - * If there is not enough room for working memory, we will try to move - * textures to host memory, assuming the performance impact would have - * been worse for working memory. */ - bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && (&mem != &texture_info); - bool is_image = is_texture && (mem.data_height > 1); - - size_t headroom = (is_texture) ? device_texture_headroom : device_working_headroom; - - size_t total = 0, free = 0; - cuMemGetInfo(&free, &total); - - /* Move textures to host memory if needed. */ - if (!move_texture_to_host && !is_image && (size + headroom) >= free && can_map_host) { - move_textures_to_host(size + headroom - free, is_texture); - cuMemGetInfo(&free, &total); - } - - /* Allocate in device memory. */ - if (!move_texture_to_host && (size + headroom) < free) { - mem_alloc_result = cuMemAlloc(&device_pointer, size); - if (mem_alloc_result == CUDA_SUCCESS) { - status = " in device memory"; - } - } - - /* Fall back to mapped host memory if needed and possible. */ - - void *shared_pointer = 0; - - if (mem_alloc_result != CUDA_SUCCESS && can_map_host && mem.type != MEM_DEVICE_ONLY) { - if (mem.shared_pointer) { - /* Another device already allocated host memory. */ - mem_alloc_result = CUDA_SUCCESS; - shared_pointer = mem.shared_pointer; - } - else if (map_host_used + size < map_host_limit) { - /* Allocate host memory ourselves. */ - mem_alloc_result = cuMemHostAlloc( - &shared_pointer, size, CU_MEMHOSTALLOC_DEVICEMAP | CU_MEMHOSTALLOC_WRITECOMBINED); - - assert((mem_alloc_result == CUDA_SUCCESS && shared_pointer != 0) || - (mem_alloc_result != CUDA_SUCCESS && shared_pointer == 0)); - } - - if (mem_alloc_result == CUDA_SUCCESS) { - cuda_assert(cuMemHostGetDevicePointer_v2(&device_pointer, shared_pointer, 0)); - map_host_used += size; - status = " in host memory"; - } - } - - if (mem_alloc_result != CUDA_SUCCESS) { - if (mem.type == MEM_DEVICE_ONLY) { - status = " failed, out of device memory"; - set_error("System is out of GPU memory"); - } - else { - status = " failed, out of device and host memory"; - set_error("System is out of GPU and shared host memory"); - } - } - - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")" << status; - } - - mem.device_pointer = (device_ptr)device_pointer; - mem.device_size = size; - stats.mem_alloc(size); - - if (!mem.device_pointer) { - return NULL; - } - - /* Insert into map of allocations. */ - thread_scoped_lock lock(cuda_mem_map_mutex); - CUDAMem *cmem = &cuda_mem_map[&mem]; - if (shared_pointer != 0) { - /* Replace host pointer with our host allocation. Only works if - * CUDA memory layout is the same and has no pitch padding. Also - * does not work if we move textures to host during a render, - * since other devices might be using the memory. */ - - if (!move_texture_to_host && pitch_padding == 0 && mem.host_pointer && - mem.host_pointer != shared_pointer) { - memcpy(shared_pointer, mem.host_pointer, size); - - /* A Call to device_memory::host_free() should be preceded by - * a call to device_memory::device_free() for host memory - * allocated by a device to be handled properly. Two exceptions - * are here and a call in OptiXDevice::generic_alloc(), where - * the current host memory can be assumed to be allocated by - * device_memory::host_alloc(), not by a device */ - - mem.host_free(); - mem.host_pointer = shared_pointer; - } - mem.shared_pointer = shared_pointer; - mem.shared_counter++; - cmem->use_mapped_host = true; - } - else { - cmem->use_mapped_host = false; - } - - return cmem; -} - -void CUDADevice::generic_copy_to(device_memory &mem) -{ - if (!mem.host_pointer || !mem.device_pointer) { - return; - } - - /* If use_mapped_host of mem is false, the current device only uses device memory allocated by - * cuMemAlloc regardless of mem.host_pointer and mem.shared_pointer, and should copy data from - * mem.host_pointer. */ - thread_scoped_lock lock(cuda_mem_map_mutex); - if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { - const CUDAContextScope scope(this); - cuda_assert( - cuMemcpyHtoD((CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size())); - } -} - -void CUDADevice::generic_free(device_memory &mem) -{ - if (mem.device_pointer) { - CUDAContextScope scope(this); - thread_scoped_lock lock(cuda_mem_map_mutex); - const CUDAMem &cmem = cuda_mem_map[&mem]; - - /* If cmem.use_mapped_host is true, reference counting is used - * to safely free a mapped host memory. */ - - if (cmem.use_mapped_host) { - assert(mem.shared_pointer); - if (mem.shared_pointer) { - assert(mem.shared_counter > 0); - if (--mem.shared_counter == 0) { - if (mem.host_pointer == mem.shared_pointer) { - mem.host_pointer = 0; - } - cuMemFreeHost(mem.shared_pointer); - mem.shared_pointer = 0; - } - } - map_host_used -= mem.device_size; - } - else { - /* Free device memory. */ - cuda_assert(cuMemFree(mem.device_pointer)); - } - - stats.mem_free(mem.device_size); - mem.device_pointer = 0; - mem.device_size = 0; - - cuda_mem_map.erase(cuda_mem_map.find(&mem)); - } -} - -void CUDADevice::mem_alloc(device_memory &mem) -{ - if (mem.type == MEM_PIXELS && !background) { - pixels_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - assert(!"mem_alloc not supported for textures."); - } - else if (mem.type == MEM_GLOBAL) { - assert(!"mem_alloc not supported for global memory."); - } - else { - generic_alloc(mem); - } -} - -void CUDADevice::mem_copy_to(device_memory &mem) -{ - if (mem.type == MEM_PIXELS) { - assert(!"mem_copy_to not supported for pixels."); - } - else if (mem.type == MEM_GLOBAL) { - global_free(mem); - global_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - tex_alloc((device_texture &)mem); - } - else { - if (!mem.device_pointer) { - generic_alloc(mem); - } - generic_copy_to(mem); - } -} - -void CUDADevice::mem_copy_from(device_memory &mem, int y, int w, int h, int elem) -{ - if (mem.type == MEM_PIXELS && !background) { - pixels_copy_from(mem, y, w, h); - } - else if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) { - assert(!"mem_copy_from not supported for textures."); - } - else if (mem.host_pointer) { - const size_t size = elem * w * h; - const size_t offset = elem * y * w; - - if (mem.device_pointer) { - const CUDAContextScope scope(this); - cuda_assert(cuMemcpyDtoH( - (char *)mem.host_pointer + offset, (CUdeviceptr)mem.device_pointer + offset, size)); - } - else { - memset((char *)mem.host_pointer + offset, 0, size); - } - } -} - -void CUDADevice::mem_zero(device_memory &mem) -{ - if (!mem.device_pointer) { - mem_alloc(mem); - } - if (!mem.device_pointer) { - return; - } - - /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory - * regardless of mem.host_pointer and mem.shared_pointer. */ - thread_scoped_lock lock(cuda_mem_map_mutex); - if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { - const CUDAContextScope scope(this); - cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size())); - } - else if (mem.host_pointer) { - memset(mem.host_pointer, 0, mem.memory_size()); - } -} - -void CUDADevice::mem_free(device_memory &mem) -{ - if (mem.type == MEM_PIXELS && !background) { - pixels_free(mem); - } - else if (mem.type == MEM_GLOBAL) { - global_free(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - } - else { - generic_free(mem); - } -} - -device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) -{ - return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); -} - -void CUDADevice::const_copy_to(const char *name, void *host, size_t size) -{ - CUDAContextScope scope(this); - CUdeviceptr mem; - size_t bytes; - - cuda_assert(cuModuleGetGlobal(&mem, &bytes, cuModule, name)); - // assert(bytes == size); - cuda_assert(cuMemcpyHtoD(mem, host, size)); -} - -void CUDADevice::global_alloc(device_memory &mem) -{ - if (mem.is_resident(this)) { - generic_alloc(mem); - generic_copy_to(mem); - } - - const_copy_to(mem.name, &mem.device_pointer, sizeof(mem.device_pointer)); -} - -void CUDADevice::global_free(device_memory &mem) -{ - if (mem.is_resident(this) && mem.device_pointer) { - generic_free(mem); - } -} - -void CUDADevice::tex_alloc(device_texture &mem) -{ - CUDAContextScope scope(this); - - /* General variables for both architectures */ - string bind_name = mem.name; - size_t dsize = datatype_size(mem.data_type); - size_t size = mem.memory_size(); - - CUaddress_mode address_mode = CU_TR_ADDRESS_MODE_WRAP; - switch (mem.info.extension) { - case EXTENSION_REPEAT: - address_mode = CU_TR_ADDRESS_MODE_WRAP; - break; - case EXTENSION_EXTEND: - address_mode = CU_TR_ADDRESS_MODE_CLAMP; - break; - case EXTENSION_CLIP: - address_mode = CU_TR_ADDRESS_MODE_BORDER; - break; - default: - assert(0); - break; - } - - CUfilter_mode filter_mode; - if (mem.info.interpolation == INTERPOLATION_CLOSEST) { - filter_mode = CU_TR_FILTER_MODE_POINT; - } - else { - filter_mode = CU_TR_FILTER_MODE_LINEAR; - } - - /* Image Texture Storage */ - CUarray_format_enum format; - switch (mem.data_type) { - case TYPE_UCHAR: - format = CU_AD_FORMAT_UNSIGNED_INT8; - break; - case TYPE_UINT16: - format = CU_AD_FORMAT_UNSIGNED_INT16; - break; - case TYPE_UINT: - format = CU_AD_FORMAT_UNSIGNED_INT32; - break; - case TYPE_INT: - format = CU_AD_FORMAT_SIGNED_INT32; - break; - case TYPE_FLOAT: - format = CU_AD_FORMAT_FLOAT; - break; - case TYPE_HALF: - format = CU_AD_FORMAT_HALF; - break; - default: - assert(0); - return; - } - - CUDAMem *cmem = NULL; - CUarray array_3d = NULL; - size_t src_pitch = mem.data_width * dsize * mem.data_elements; - size_t dst_pitch = src_pitch; - - if (!mem.is_resident(this)) { - thread_scoped_lock lock(cuda_mem_map_mutex); - cmem = &cuda_mem_map[&mem]; - cmem->texobject = 0; - - if (mem.data_depth > 1) { - array_3d = (CUarray)mem.device_pointer; - cmem->array = array_3d; - } - else if (mem.data_height > 0) { - dst_pitch = align_up(src_pitch, pitch_alignment); - } - } - else if (mem.data_depth > 1) { - /* 3D texture using array, there is no API for linear memory. */ - CUDA_ARRAY3D_DESCRIPTOR desc; - - desc.Width = mem.data_width; - desc.Height = mem.data_height; - desc.Depth = mem.data_depth; - desc.Format = format; - desc.NumChannels = mem.data_elements; - desc.Flags = 0; - - VLOG(1) << "Array 3D allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - cuda_assert(cuArray3DCreate(&array_3d, &desc)); - - if (!array_3d) { - return; - } - - CUDA_MEMCPY3D param; - memset(¶m, 0, sizeof(param)); - param.dstMemoryType = CU_MEMORYTYPE_ARRAY; - param.dstArray = array_3d; - param.srcMemoryType = CU_MEMORYTYPE_HOST; - param.srcHost = mem.host_pointer; - param.srcPitch = src_pitch; - param.WidthInBytes = param.srcPitch; - param.Height = mem.data_height; - param.Depth = mem.data_depth; - - cuda_assert(cuMemcpy3D(¶m)); - - mem.device_pointer = (device_ptr)array_3d; - mem.device_size = size; - stats.mem_alloc(size); - - thread_scoped_lock lock(cuda_mem_map_mutex); - cmem = &cuda_mem_map[&mem]; - cmem->texobject = 0; - cmem->array = array_3d; - } - else if (mem.data_height > 0) { - /* 2D texture, using pitch aligned linear memory. */ - dst_pitch = align_up(src_pitch, pitch_alignment); - size_t dst_size = dst_pitch * mem.data_height; - - cmem = generic_alloc(mem, dst_size - mem.memory_size()); - if (!cmem) { - return; - } - - CUDA_MEMCPY2D param; - memset(¶m, 0, sizeof(param)); - param.dstMemoryType = CU_MEMORYTYPE_DEVICE; - param.dstDevice = mem.device_pointer; - param.dstPitch = dst_pitch; - param.srcMemoryType = CU_MEMORYTYPE_HOST; - param.srcHost = mem.host_pointer; - param.srcPitch = src_pitch; - param.WidthInBytes = param.srcPitch; - param.Height = mem.data_height; - - cuda_assert(cuMemcpy2DUnaligned(¶m)); - } - else { - /* 1D texture, using linear memory. */ - cmem = generic_alloc(mem); - if (!cmem) { - return; - } - - cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); - } - - /* Resize once */ - const uint slot = mem.slot; - if (slot >= texture_info.size()) { - /* Allocate some slots in advance, to reduce amount - * of re-allocations. */ - texture_info.resize(slot + 128); - } - - /* Set Mapping and tag that we need to (re-)upload to device */ - texture_info[slot] = mem.info; - need_texture_info = true; - - if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { - /* Kepler+, bindless textures. */ - CUDA_RESOURCE_DESC resDesc; - memset(&resDesc, 0, sizeof(resDesc)); - - if (array_3d) { - resDesc.resType = CU_RESOURCE_TYPE_ARRAY; - resDesc.res.array.hArray = array_3d; - resDesc.flags = 0; - } - else if (mem.data_height > 0) { - resDesc.resType = CU_RESOURCE_TYPE_PITCH2D; - resDesc.res.pitch2D.devPtr = mem.device_pointer; - resDesc.res.pitch2D.format = format; - resDesc.res.pitch2D.numChannels = mem.data_elements; - resDesc.res.pitch2D.height = mem.data_height; - resDesc.res.pitch2D.width = mem.data_width; - resDesc.res.pitch2D.pitchInBytes = dst_pitch; - } - else { - resDesc.resType = CU_RESOURCE_TYPE_LINEAR; - resDesc.res.linear.devPtr = mem.device_pointer; - resDesc.res.linear.format = format; - resDesc.res.linear.numChannels = mem.data_elements; - resDesc.res.linear.sizeInBytes = mem.device_size; - } - - CUDA_TEXTURE_DESC texDesc; - memset(&texDesc, 0, sizeof(texDesc)); - texDesc.addressMode[0] = address_mode; - texDesc.addressMode[1] = address_mode; - texDesc.addressMode[2] = address_mode; - texDesc.filterMode = filter_mode; - texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; - - thread_scoped_lock lock(cuda_mem_map_mutex); - cmem = &cuda_mem_map[&mem]; - - cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); - - texture_info[slot].data = (uint64_t)cmem->texobject; - } - else { - texture_info[slot].data = (uint64_t)mem.device_pointer; - } -} - -void CUDADevice::tex_free(device_texture &mem) -{ - if (mem.device_pointer) { - CUDAContextScope scope(this); - thread_scoped_lock lock(cuda_mem_map_mutex); - const CUDAMem &cmem = cuda_mem_map[&mem]; - - if (cmem.texobject) { - /* Free bindless texture. */ - cuTexObjectDestroy(cmem.texobject); - } - - if (!mem.is_resident(this)) { - /* Do not free memory here, since it was allocated on a different device. */ - cuda_mem_map.erase(cuda_mem_map.find(&mem)); - } - else if (cmem.array) { - /* Free array. */ - cuArrayDestroy(cmem.array); - stats.mem_free(mem.device_size); - mem.device_pointer = 0; - mem.device_size = 0; - - cuda_mem_map.erase(cuda_mem_map.find(&mem)); - } - else { - lock.unlock(); - generic_free(mem); - } - } -} - -# define CUDA_GET_BLOCKSIZE(func, w, h) \ - int threads_per_block; \ - cuda_assert( \ - cuFuncGetAttribute(&threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); \ - int threads = (int)sqrt((float)threads_per_block); \ - int xblocks = ((w) + threads - 1) / threads; \ - int yblocks = ((h) + threads - 1) / threads; - -# define CUDA_LAUNCH_KERNEL(func, args) \ - cuda_assert(cuLaunchKernel(func, xblocks, yblocks, 1, threads, threads, 1, 0, 0, args, 0)); - -/* Similar as above, but for 1-dimensional blocks. */ -# define CUDA_GET_BLOCKSIZE_1D(func, w, h) \ - int threads_per_block; \ - cuda_assert( \ - cuFuncGetAttribute(&threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); \ - int xblocks = ((w) + threads_per_block - 1) / threads_per_block; \ - int yblocks = h; - -# define CUDA_LAUNCH_KERNEL_1D(func, args) \ - cuda_assert(cuLaunchKernel(func, xblocks, yblocks, 1, threads_per_block, 1, 1, 0, 0, args, 0)); - -bool CUDADevice::denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - int stride = task->buffer.stride; - int w = task->buffer.width; - int h = task->buffer.h; - int r = task->nlm_state.r; - int f = task->nlm_state.f; - float a = task->nlm_state.a; - float k_2 = task->nlm_state.k_2; - - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - int channel_offset = task->nlm_state.is_color ? task->buffer.pass_stride : 0; - int frame_offset = 0; - - if (have_error()) - return false; - - CUdeviceptr difference = (CUdeviceptr)task->buffer.temporary_mem.device_pointer; - CUdeviceptr blurDifference = difference + sizeof(float) * pass_stride * num_shifts; - CUdeviceptr weightAccum = difference + 2 * sizeof(float) * pass_stride * num_shifts; - CUdeviceptr scale_ptr = 0; - - cuda_assert(cuMemsetD8(weightAccum, 0, sizeof(float) * pass_stride)); - cuda_assert(cuMemsetD8(out_ptr, 0, sizeof(float) * pass_stride)); - - { - CUfunction cuNLMCalcDifference, cuNLMBlur, cuNLMCalcWeight, cuNLMUpdateOutput; - cuda_assert(cuModuleGetFunction( - &cuNLMCalcDifference, cuFilterModule, "kernel_cuda_filter_nlm_calc_difference")); - cuda_assert(cuModuleGetFunction(&cuNLMBlur, cuFilterModule, "kernel_cuda_filter_nlm_blur")); - cuda_assert(cuModuleGetFunction( - &cuNLMCalcWeight, cuFilterModule, "kernel_cuda_filter_nlm_calc_weight")); - cuda_assert(cuModuleGetFunction( - &cuNLMUpdateOutput, cuFilterModule, "kernel_cuda_filter_nlm_update_output")); - - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcDifference, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMBlur, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcWeight, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMUpdateOutput, CU_FUNC_CACHE_PREFER_L1)); - - CUDA_GET_BLOCKSIZE_1D(cuNLMCalcDifference, w * h, num_shifts); - - void *calc_difference_args[] = {&guide_ptr, - &variance_ptr, - &scale_ptr, - &difference, - &w, - &h, - &stride, - &pass_stride, - &r, - &channel_offset, - &frame_offset, - &a, - &k_2}; - void *blur_args[] = {&difference, &blurDifference, &w, &h, &stride, &pass_stride, &r, &f}; - void *calc_weight_args[] = { - &blurDifference, &difference, &w, &h, &stride, &pass_stride, &r, &f}; - void *update_output_args[] = {&blurDifference, - &image_ptr, - &out_ptr, - &weightAccum, - &w, - &h, - &stride, - &pass_stride, - &channel_offset, - &r, - &f}; - - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcDifference, calc_difference_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcWeight, calc_weight_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMUpdateOutput, update_output_args); - } - - { - CUfunction cuNLMNormalize; - cuda_assert( - cuModuleGetFunction(&cuNLMNormalize, cuFilterModule, "kernel_cuda_filter_nlm_normalize")); - cuda_assert(cuFuncSetCacheConfig(cuNLMNormalize, CU_FUNC_CACHE_PREFER_L1)); - void *normalize_args[] = {&out_ptr, &weightAccum, &w, &h, &stride}; - CUDA_GET_BLOCKSIZE(cuNLMNormalize, w, h); - CUDA_LAUNCH_KERNEL(cuNLMNormalize, normalize_args); - cuda_assert(cuCtxSynchronize()); - } - - return !have_error(); -} - -bool CUDADevice::denoising_construct_transform(DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterConstructTransform; - cuda_assert(cuModuleGetFunction( - &cuFilterConstructTransform, cuFilterModule, "kernel_cuda_filter_construct_transform")); - cuda_assert(cuFuncSetCacheConfig(cuFilterConstructTransform, CU_FUNC_CACHE_PREFER_SHARED)); - CUDA_GET_BLOCKSIZE(cuFilterConstructTransform, task->storage.w, task->storage.h); - - void *args[] = {&task->buffer.mem.device_pointer, - &task->tile_info_mem.device_pointer, - &task->storage.transform.device_pointer, - &task->storage.rank.device_pointer, - &task->filter_area, - &task->rect, - &task->radius, - &task->pca_threshold, - &task->buffer.pass_stride, - &task->buffer.frame_stride, - &task->buffer.use_time}; - CUDA_LAUNCH_KERNEL(cuFilterConstructTransform, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - int r = task->radius; - int f = 4; - float a = 1.0f; - float k_2 = task->nlm_k_2; - - int w = task->reconstruction_state.source_w; - int h = task->reconstruction_state.source_h; - int stride = task->buffer.stride; - int frame_offset = frame * task->buffer.frame_stride; - int t = task->tile_info->frames[frame]; - - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - - if (have_error()) - return false; - - CUdeviceptr difference = (CUdeviceptr)task->buffer.temporary_mem.device_pointer; - CUdeviceptr blurDifference = difference + sizeof(float) * pass_stride * num_shifts; - - CUfunction cuNLMCalcDifference, cuNLMBlur, cuNLMCalcWeight, cuNLMConstructGramian; - cuda_assert(cuModuleGetFunction( - &cuNLMCalcDifference, cuFilterModule, "kernel_cuda_filter_nlm_calc_difference")); - cuda_assert(cuModuleGetFunction(&cuNLMBlur, cuFilterModule, "kernel_cuda_filter_nlm_blur")); - cuda_assert( - cuModuleGetFunction(&cuNLMCalcWeight, cuFilterModule, "kernel_cuda_filter_nlm_calc_weight")); - cuda_assert(cuModuleGetFunction( - &cuNLMConstructGramian, cuFilterModule, "kernel_cuda_filter_nlm_construct_gramian")); - - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcDifference, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMBlur, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMCalcWeight, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuFuncSetCacheConfig(cuNLMConstructGramian, CU_FUNC_CACHE_PREFER_SHARED)); - - CUDA_GET_BLOCKSIZE_1D(cuNLMCalcDifference, - task->reconstruction_state.source_w * task->reconstruction_state.source_h, - num_shifts); - - void *calc_difference_args[] = {&color_ptr, - &color_variance_ptr, - &scale_ptr, - &difference, - &w, - &h, - &stride, - &pass_stride, - &r, - &pass_stride, - &frame_offset, - &a, - &k_2}; - void *blur_args[] = {&difference, &blurDifference, &w, &h, &stride, &pass_stride, &r, &f}; - void *calc_weight_args[] = {&blurDifference, &difference, &w, &h, &stride, &pass_stride, &r, &f}; - void *construct_gramian_args[] = {&t, - &blurDifference, - &task->buffer.mem.device_pointer, - &task->storage.transform.device_pointer, - &task->storage.rank.device_pointer, - &task->storage.XtWX.device_pointer, - &task->storage.XtWY.device_pointer, - &task->reconstruction_state.filter_window, - &w, - &h, - &stride, - &pass_stride, - &r, - &f, - &frame_offset, - &task->buffer.use_time}; - - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcDifference, calc_difference_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMCalcWeight, calc_weight_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMBlur, blur_args); - CUDA_LAUNCH_KERNEL_1D(cuNLMConstructGramian, construct_gramian_args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_solve(device_ptr output_ptr, DenoisingTask *task) -{ - CUfunction cuFinalize; - cuda_assert(cuModuleGetFunction(&cuFinalize, cuFilterModule, "kernel_cuda_filter_finalize")); - cuda_assert(cuFuncSetCacheConfig(cuFinalize, CU_FUNC_CACHE_PREFER_L1)); - void *finalize_args[] = {&output_ptr, - &task->storage.rank.device_pointer, - &task->storage.XtWX.device_pointer, - &task->storage.XtWY.device_pointer, - &task->filter_area, - &task->reconstruction_state.buffer_params.x, - &task->render_buffer.samples}; - CUDA_GET_BLOCKSIZE( - cuFinalize, task->reconstruction_state.source_w, task->reconstruction_state.source_h); - CUDA_LAUNCH_KERNEL(cuFinalize, finalize_args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterCombineHalves; - cuda_assert(cuModuleGetFunction( - &cuFilterCombineHalves, cuFilterModule, "kernel_cuda_filter_combine_halves")); - cuda_assert(cuFuncSetCacheConfig(cuFilterCombineHalves, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE( - cuFilterCombineHalves, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = {&mean_ptr, &variance_ptr, &a_ptr, &b_ptr, &rect, &r}; - CUDA_LAUNCH_KERNEL(cuFilterCombineHalves, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterDivideShadow; - cuda_assert(cuModuleGetFunction( - &cuFilterDivideShadow, cuFilterModule, "kernel_cuda_filter_divide_shadow")); - cuda_assert(cuFuncSetCacheConfig(cuFilterDivideShadow, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE( - cuFilterDivideShadow, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = {&task->render_buffer.samples, - &task->tile_info_mem.device_pointer, - &a_ptr, - &b_ptr, - &sample_variance_ptr, - &sv_variance_ptr, - &buffer_variance_ptr, - &task->rect, - &task->render_buffer.pass_stride, - &task->render_buffer.offset}; - CUDA_LAUNCH_KERNEL(cuFilterDivideShadow, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterGetFeature; - cuda_assert( - cuModuleGetFunction(&cuFilterGetFeature, cuFilterModule, "kernel_cuda_filter_get_feature")); - cuda_assert(cuFuncSetCacheConfig(cuFilterGetFeature, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE(cuFilterGetFeature, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = {&task->render_buffer.samples, - &task->tile_info_mem.device_pointer, - &mean_offset, - &variance_offset, - &mean_ptr, - &variance_ptr, - &scale, - &task->rect, - &task->render_buffer.pass_stride, - &task->render_buffer.offset}; - CUDA_LAUNCH_KERNEL(cuFilterGetFeature, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterWriteFeature; - cuda_assert(cuModuleGetFunction( - &cuFilterWriteFeature, cuFilterModule, "kernel_cuda_filter_write_feature")); - cuda_assert(cuFuncSetCacheConfig(cuFilterWriteFeature, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE(cuFilterWriteFeature, task->filter_area.z, task->filter_area.w); - - void *args[] = {&task->render_buffer.samples, - &task->reconstruction_state.buffer_params, - &task->filter_area, - &from_ptr, - &buffer_ptr, - &out_offset, - &task->rect}; - CUDA_LAUNCH_KERNEL(cuFilterWriteFeature, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -bool CUDADevice::denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task) -{ - if (have_error()) - return false; - - CUDAContextScope scope(this); - - CUfunction cuFilterDetectOutliers; - cuda_assert(cuModuleGetFunction( - &cuFilterDetectOutliers, cuFilterModule, "kernel_cuda_filter_detect_outliers")); - cuda_assert(cuFuncSetCacheConfig(cuFilterDetectOutliers, CU_FUNC_CACHE_PREFER_L1)); - CUDA_GET_BLOCKSIZE( - cuFilterDetectOutliers, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - void *args[] = { - &image_ptr, &variance_ptr, &depth_ptr, &output_ptr, &task->rect, &task->buffer.pass_stride}; - - CUDA_LAUNCH_KERNEL(cuFilterDetectOutliers, args); - cuda_assert(cuCtxSynchronize()); - - return !have_error(); -} - -void CUDADevice::denoise(RenderTile &rtile, DenoisingTask &denoising) -{ - denoising.functions.construct_transform = function_bind( - &CUDADevice::denoising_construct_transform, this, &denoising); - denoising.functions.accumulate = function_bind( - &CUDADevice::denoising_accumulate, this, _1, _2, _3, _4, &denoising); - denoising.functions.solve = function_bind(&CUDADevice::denoising_solve, this, _1, &denoising); - denoising.functions.divide_shadow = function_bind( - &CUDADevice::denoising_divide_shadow, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.non_local_means = function_bind( - &CUDADevice::denoising_non_local_means, this, _1, _2, _3, _4, &denoising); - denoising.functions.combine_halves = function_bind( - &CUDADevice::denoising_combine_halves, this, _1, _2, _3, _4, _5, _6, &denoising); - denoising.functions.get_feature = function_bind( - &CUDADevice::denoising_get_feature, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.write_feature = function_bind( - &CUDADevice::denoising_write_feature, this, _1, _2, _3, &denoising); - denoising.functions.detect_outliers = function_bind( - &CUDADevice::denoising_detect_outliers, this, _1, _2, _3, _4, &denoising); - - denoising.filter_area = make_int4(rtile.x, rtile.y, rtile.w, rtile.h); - denoising.render_buffer.samples = rtile.sample; - denoising.buffer.gpu_temporary_mem = true; - - denoising.run_denoising(rtile); -} - -void CUDADevice::adaptive_sampling_filter(uint filter_sample, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream) -{ - const int num_threads_per_block = functions.adaptive_num_threads_per_block; - - /* These are a series of tiny kernels because there is no grid synchronization - * from within a kernel, so multiple kernel launches it is. */ - uint total_work_size = wtile->h * wtile->w; - void *args2[] = {&d_wtile, &filter_sample, &total_work_size}; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_stopping, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args2, - 0)); - total_work_size = wtile->h; - num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_filter_x, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args2, - 0)); - total_work_size = wtile->w; - num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_filter_y, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args2, - 0)); -} - -void CUDADevice::adaptive_sampling_post(RenderTile &rtile, - WorkTile *wtile, - CUdeviceptr d_wtile, - CUstream stream) -{ - const int num_threads_per_block = functions.adaptive_num_threads_per_block; - uint total_work_size = wtile->h * wtile->w; - - void *args[] = {&d_wtile, &rtile.start_sample, &rtile.sample, &total_work_size}; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - cuda_assert(cuLaunchKernel(functions.adaptive_scale_samples, - num_blocks, - 1, - 1, - num_threads_per_block, - 1, - 1, - 0, - stream, - args, - 0)); -} - -void CUDADevice::render(DeviceTask &task, RenderTile &rtile, device_vector &work_tiles) -{ - scoped_timer timer(&rtile.buffers->render_time); - - if (have_error()) - return; - - CUDAContextScope scope(this); - CUfunction cuRender; - - /* Get kernel function. */ - if (rtile.task == RenderTile::BAKE) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_bake")); - } - else if (task.integrator_branched) { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_branched_path_trace")); - } - else { - cuda_assert(cuModuleGetFunction(&cuRender, cuModule, "kernel_cuda_path_trace")); - } - - if (have_error()) { - return; - } - - cuda_assert(cuFuncSetCacheConfig(cuRender, CU_FUNC_CACHE_PREFER_L1)); - - /* Allocate work tile. */ - work_tiles.alloc(1); - - WorkTile *wtile = work_tiles.data(); - wtile->x = rtile.x; - wtile->y = rtile.y; - wtile->w = rtile.w; - wtile->h = rtile.h; - wtile->offset = rtile.offset; - wtile->stride = rtile.stride; - wtile->buffer = (float *)(CUdeviceptr)rtile.buffer; - - /* Prepare work size. More step samples render faster, but for now we - * remain conservative for GPUs connected to a display to avoid driver - * timeouts and display freezing. */ - int min_blocks, num_threads_per_block; - cuda_assert( - cuOccupancyMaxPotentialBlockSize(&min_blocks, &num_threads_per_block, cuRender, NULL, 0, 0)); - if (!info.display_device) { - min_blocks *= 8; - } - - uint step_samples = divide_up(min_blocks * num_threads_per_block, wtile->w * wtile->h); - - /* Render all samples. */ - int start_sample = rtile.start_sample; - int end_sample = rtile.start_sample + rtile.num_samples; - - for (int sample = start_sample; sample < end_sample;) { - /* Setup and copy work tile to device. */ - wtile->start_sample = sample; - wtile->num_samples = step_samples; - if (task.adaptive_sampling.use) { - wtile->num_samples = task.adaptive_sampling.align_samples(sample, step_samples); - } - wtile->num_samples = min(wtile->num_samples, end_sample - sample); - work_tiles.copy_to_device(); - - CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; - uint total_work_size = wtile->w * wtile->h * wtile->num_samples; - uint num_blocks = divide_up(total_work_size, num_threads_per_block); - - /* Launch kernel. */ - void *args[] = {&d_work_tiles, &total_work_size}; - - cuda_assert( - cuLaunchKernel(cuRender, num_blocks, 1, 1, num_threads_per_block, 1, 1, 0, 0, args, 0)); - - /* Run the adaptive sampling kernels at selected samples aligned to step samples. */ - uint filter_sample = sample + wtile->num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - adaptive_sampling_filter(filter_sample, wtile, d_work_tiles); - } - - cuda_assert(cuCtxSynchronize()); - - /* Update progress. */ - sample += wtile->num_samples; - rtile.sample = sample; - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - /* Finalize adaptive sampling. */ - if (task.adaptive_sampling.use) { - CUdeviceptr d_work_tiles = (CUdeviceptr)work_tiles.device_pointer; - adaptive_sampling_post(rtile, wtile, d_work_tiles); - cuda_assert(cuCtxSynchronize()); - task.update_progress(&rtile, rtile.w * rtile.h * wtile->num_samples); - } -} - -void CUDADevice::film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half) -{ - if (have_error()) - return; - - CUDAContextScope scope(this); - - CUfunction cuFilmConvert; - CUdeviceptr d_rgba = map_pixels((rgba_byte) ? rgba_byte : rgba_half); - CUdeviceptr d_buffer = (CUdeviceptr)buffer; - - /* get kernel function */ - if (rgba_half) { - cuda_assert( - cuModuleGetFunction(&cuFilmConvert, cuModule, "kernel_cuda_convert_to_half_float")); - } - else { - cuda_assert(cuModuleGetFunction(&cuFilmConvert, cuModule, "kernel_cuda_convert_to_byte")); - } - - float sample_scale = 1.0f / (task.sample + 1); - - /* pass in parameters */ - void *args[] = {&d_rgba, - &d_buffer, - &sample_scale, - &task.x, - &task.y, - &task.w, - &task.h, - &task.offset, - &task.stride}; - - /* launch kernel */ - int threads_per_block; - cuda_assert(cuFuncGetAttribute( - &threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, cuFilmConvert)); - - int xthreads = (int)sqrt(threads_per_block); - int ythreads = (int)sqrt(threads_per_block); - int xblocks = (task.w + xthreads - 1) / xthreads; - int yblocks = (task.h + ythreads - 1) / ythreads; - - cuda_assert(cuFuncSetCacheConfig(cuFilmConvert, CU_FUNC_CACHE_PREFER_L1)); - - cuda_assert(cuLaunchKernel(cuFilmConvert, - xblocks, - yblocks, - 1, /* blocks */ - xthreads, - ythreads, - 1, /* threads */ - 0, - 0, - args, - 0)); - - unmap_pixels((rgba_byte) ? rgba_byte : rgba_half); - - cuda_assert(cuCtxSynchronize()); -} - -void CUDADevice::shader(DeviceTask &task) -{ - if (have_error()) - return; - - CUDAContextScope scope(this); - - CUfunction cuShader; - CUdeviceptr d_input = (CUdeviceptr)task.shader_input; - CUdeviceptr d_output = (CUdeviceptr)task.shader_output; - - /* get kernel function */ - if (task.shader_eval_type == SHADER_EVAL_DISPLACE) { - cuda_assert(cuModuleGetFunction(&cuShader, cuModule, "kernel_cuda_displace")); - } - else { - cuda_assert(cuModuleGetFunction(&cuShader, cuModule, "kernel_cuda_background")); - } - - /* do tasks in smaller chunks, so we can cancel it */ - const int shader_chunk_size = 65536; - const int start = task.shader_x; - const int end = task.shader_x + task.shader_w; - int offset = task.offset; - - bool canceled = false; - for (int sample = 0; sample < task.num_samples && !canceled; sample++) { - for (int shader_x = start; shader_x < end; shader_x += shader_chunk_size) { - int shader_w = min(shader_chunk_size, end - shader_x); - - /* pass in parameters */ - void *args[8]; - int arg = 0; - args[arg++] = &d_input; - args[arg++] = &d_output; - args[arg++] = &task.shader_eval_type; - if (task.shader_eval_type >= SHADER_EVAL_BAKE) { - args[arg++] = &task.shader_filter; - } - args[arg++] = &shader_x; - args[arg++] = &shader_w; - args[arg++] = &offset; - args[arg++] = &sample; - - /* launch kernel */ - int threads_per_block; - cuda_assert(cuFuncGetAttribute( - &threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, cuShader)); - - int xblocks = (shader_w + threads_per_block - 1) / threads_per_block; - - cuda_assert(cuFuncSetCacheConfig(cuShader, CU_FUNC_CACHE_PREFER_L1)); - cuda_assert(cuLaunchKernel(cuShader, - xblocks, - 1, - 1, /* blocks */ - threads_per_block, - 1, - 1, /* threads */ - 0, - 0, - args, - 0)); - - cuda_assert(cuCtxSynchronize()); - - if (task.get_cancel()) { - canceled = true; - break; - } - } - - task.update_progress(NULL); - } -} - -CUdeviceptr CUDADevice::map_pixels(device_ptr mem) -{ - if (!background) { - PixelMem pmem = pixel_mem_map[mem]; - CUdeviceptr buffer; - - size_t bytes; - cuda_assert(cuGraphicsMapResources(1, &pmem.cuPBOresource, 0)); - cuda_assert(cuGraphicsResourceGetMappedPointer(&buffer, &bytes, pmem.cuPBOresource)); - - return buffer; - } - - return (CUdeviceptr)mem; -} - -void CUDADevice::unmap_pixels(device_ptr mem) -{ - if (!background) { - PixelMem pmem = pixel_mem_map[mem]; - - cuda_assert(cuGraphicsUnmapResources(1, &pmem.cuPBOresource, 0)); - } -} - -void CUDADevice::pixels_alloc(device_memory &mem) -{ - PixelMem pmem; - - pmem.w = mem.data_width; - pmem.h = mem.data_height; - - CUDAContextScope scope(this); - - glGenBuffers(1, &pmem.cuPBO); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO); - if (mem.data_type == TYPE_HALF) - glBufferData( - GL_PIXEL_UNPACK_BUFFER, pmem.w * pmem.h * sizeof(GLhalf) * 4, NULL, GL_DYNAMIC_DRAW); - else - glBufferData( - GL_PIXEL_UNPACK_BUFFER, pmem.w * pmem.h * sizeof(uint8_t) * 4, NULL, GL_DYNAMIC_DRAW); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &pmem.cuTexId); - glBindTexture(GL_TEXTURE_2D, pmem.cuTexId); - if (mem.data_type == TYPE_HALF) - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, pmem.w, pmem.h, 0, GL_RGBA, GL_HALF_FLOAT, NULL); - else - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, pmem.w, pmem.h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glBindTexture(GL_TEXTURE_2D, 0); - - CUresult result = cuGraphicsGLRegisterBuffer( - &pmem.cuPBOresource, pmem.cuPBO, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE); - - if (result == CUDA_SUCCESS) { - mem.device_pointer = pmem.cuTexId; - pixel_mem_map[mem.device_pointer] = pmem; - - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - - return; - } - else { - /* failed to register buffer, fallback to no interop */ - glDeleteBuffers(1, &pmem.cuPBO); - glDeleteTextures(1, &pmem.cuTexId); - - background = true; - } -} - -void CUDADevice::pixels_copy_from(device_memory &mem, int y, int w, int h) -{ - PixelMem pmem = pixel_mem_map[mem.device_pointer]; - - CUDAContextScope scope(this); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO); - uchar *pixels = (uchar *)glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_READ_ONLY); - size_t offset = sizeof(uchar) * 4 * y * w; - memcpy((uchar *)mem.host_pointer + offset, pixels + offset, sizeof(uchar) * 4 * w * h); - glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); -} - -void CUDADevice::pixels_free(device_memory &mem) -{ - if (mem.device_pointer) { - PixelMem pmem = pixel_mem_map[mem.device_pointer]; - - CUDAContextScope scope(this); - - cuda_assert(cuGraphicsUnregisterResource(pmem.cuPBOresource)); - glDeleteBuffers(1, &pmem.cuPBO); - glDeleteTextures(1, &pmem.cuTexId); - - pixel_mem_map.erase(pixel_mem_map.find(mem.device_pointer)); - mem.device_pointer = 0; - - stats.mem_free(mem.device_size); - mem.device_size = 0; - } -} - -void CUDADevice::draw_pixels(device_memory &mem, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) -{ - assert(mem.type == MEM_PIXELS); - - if (!background) { - const bool use_fallback_shader = (draw_params.bind_display_space_shader_cb == NULL); - PixelMem pmem = pixel_mem_map[mem.device_pointer]; - float *vpointer; - - CUDAContextScope scope(this); - - /* for multi devices, this assumes the inefficient method that we allocate - * all pixels on the device even though we only render to a subset */ - size_t offset = 4 * y * w; - - if (mem.data_type == TYPE_HALF) - offset *= sizeof(GLhalf); - else - offset *= sizeof(uint8_t); - - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pmem.cuPBO); - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, pmem.cuTexId); - if (mem.data_type == TYPE_HALF) { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_HALF_FLOAT, (void *)offset); - } - else { - glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, (void *)offset); - } - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); - - if (transparent) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } - - GLint shader_program; - if (use_fallback_shader) { - if (!bind_fallback_display_space_shader(dw, dh)) { - return; - } - shader_program = fallback_shader_program; - } - else { - draw_params.bind_display_space_shader_cb(); - glGetIntegerv(GL_CURRENT_PROGRAM, &shader_program); - } - - if (!vertex_buffer) { - glGenBuffers(1, &vertex_buffer); - } - - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); - /* invalidate old contents - - * avoids stalling if buffer is still waiting in queue to be rendered */ - glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); - - vpointer = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - - if (vpointer) { - /* texture coordinate - vertex pair */ - vpointer[0] = 0.0f; - vpointer[1] = 0.0f; - vpointer[2] = dx; - vpointer[3] = dy; - - vpointer[4] = (float)w / (float)pmem.w; - vpointer[5] = 0.0f; - vpointer[6] = (float)width + dx; - vpointer[7] = dy; - - vpointer[8] = (float)w / (float)pmem.w; - vpointer[9] = (float)h / (float)pmem.h; - vpointer[10] = (float)width + dx; - vpointer[11] = (float)height + dy; - - vpointer[12] = 0.0f; - vpointer[13] = (float)h / (float)pmem.h; - vpointer[14] = dx; - vpointer[15] = (float)height + dy; - - glUnmapBuffer(GL_ARRAY_BUFFER); - } - - GLuint vertex_array_object; - GLuint position_attribute, texcoord_attribute; - - glGenVertexArrays(1, &vertex_array_object); - glBindVertexArray(vertex_array_object); - - texcoord_attribute = glGetAttribLocation(shader_program, "texCoord"); - position_attribute = glGetAttribLocation(shader_program, "pos"); - - glEnableVertexAttribArray(texcoord_attribute); - glEnableVertexAttribArray(position_attribute); - - glVertexAttribPointer( - texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); - glVertexAttribPointer(position_attribute, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(float), - (const GLvoid *)(sizeof(float) * 2)); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - if (use_fallback_shader) { - glUseProgram(0); - } - else { - draw_params.unbind_display_space_shader_cb(); - } - - if (transparent) { - glDisable(GL_BLEND); - } - - glBindTexture(GL_TEXTURE_2D, 0); - - return; - } - - Device::draw_pixels(mem, y, w, h, width, height, dx, dy, dw, dh, transparent, draw_params); -} - -void CUDADevice::thread_run(DeviceTask &task) -{ - CUDAContextScope scope(this); - - if (task.type == DeviceTask::RENDER) { - DeviceRequestedFeatures requested_features; - if (use_split_kernel()) { - if (split_kernel == NULL) { - split_kernel = new CUDASplitKernel(this); - split_kernel->load_kernels(requested_features); - } - } - - device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); - - /* keep rendering tiles until done */ - RenderTile tile; - DenoisingTask denoising(this, task); - - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - if (use_split_kernel()) { - device_only_memory void_buffer(this, "void_buffer"); - split_kernel->path_trace(task, tile, void_buffer, void_buffer); - } - else { - render(task, tile, work_tiles); - } - } - else if (tile.task == RenderTile::BAKE) { - render(task, tile, work_tiles); - } - else if (tile.task == RenderTile::DENOISE) { - tile.sample = tile.start_sample + tile.num_samples; - - denoise(tile, denoising); - - task.update_progress(&tile, tile.w * tile.h); - } - - task.release_tile(tile); - - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - } - - work_tiles.free(); - } - else if (task.type == DeviceTask::SHADER) { - shader(task); - - cuda_assert(cuCtxSynchronize()); - } - else if (task.type == DeviceTask::DENOISE_BUFFER) { - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.sample = task.sample + task.num_samples; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - DenoisingTask denoising(this, task); - denoise(tile, denoising); - task.update_progress(&tile, tile.w * tile.h); - } -} - -void CUDADevice::task_add(DeviceTask &task) -{ - CUDAContextScope scope(this); - - /* Load texture info. */ - load_texture_info(); - - /* Synchronize all memory copies before executing task. */ - cuda_assert(cuCtxSynchronize()); - - if (task.type == DeviceTask::FILM_CONVERT) { - /* must be done in main thread due to opengl access */ - film_convert(task, task.buffer, task.rgba_byte, task.rgba_half); - } - else { - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy); - }); - } -} - -void CUDADevice::task_wait() -{ - task_pool.wait(); -} - -void CUDADevice::task_cancel() -{ - task_pool.cancel(); -} - -/* redefine the cuda_assert macro so it can be used outside of the CUDADevice class - * now that the definition of that class is complete - */ -# undef cuda_assert -# define cuda_assert(stmt) \ - { \ - CUresult result = stmt; \ - if (result != CUDA_SUCCESS) { \ - const char *name = cuewErrorString(result); \ - device->set_error( \ - string_printf("%s in %s (device_cuda_impl.cpp:%d)", name, #stmt, __LINE__)); \ - } \ - } \ - (void)0 - -/* CUDA context scope. */ - -CUDAContextScope::CUDAContextScope(CUDADevice *device) : device(device) -{ - cuda_assert(cuCtxPushCurrent(device->cuContext)); -} - -CUDAContextScope::~CUDAContextScope() -{ - cuda_assert(cuCtxPopCurrent(NULL)); -} - -/* split kernel */ - -class CUDASplitKernelFunction : public SplitKernelFunction { - CUDADevice *device; - CUfunction func; - - public: - CUDASplitKernelFunction(CUDADevice *device, CUfunction func) : device(device), func(func) - { - } - - /* enqueue the kernel, returns false if there is an error */ - bool enqueue(const KernelDimensions &dim, device_memory & /*kg*/, device_memory & /*data*/) - { - return enqueue(dim, NULL); - } - - /* enqueue the kernel, returns false if there is an error */ - bool enqueue(const KernelDimensions &dim, void *args[]) - { - if (device->have_error()) - return false; - - CUDAContextScope scope(device); - - /* we ignore dim.local_size for now, as this is faster */ - int threads_per_block; - cuda_assert( - cuFuncGetAttribute(&threads_per_block, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); - - int xblocks = (dim.global_size[0] * dim.global_size[1] + threads_per_block - 1) / - threads_per_block; - - cuda_assert(cuFuncSetCacheConfig(func, CU_FUNC_CACHE_PREFER_L1)); - - cuda_assert(cuLaunchKernel(func, - xblocks, - 1, - 1, /* blocks */ - threads_per_block, - 1, - 1, /* threads */ - 0, - 0, - args, - 0)); - - return !device->have_error(); - } -}; - -CUDASplitKernel::CUDASplitKernel(CUDADevice *device) : DeviceSplitKernel(device), device(device) -{ -} - -uint64_t CUDASplitKernel::state_buffer_size(device_memory & /*kg*/, - device_memory & /*data*/, - size_t num_threads) -{ - CUDAContextScope scope(device); - - device_vector size_buffer(device, "size_buffer", MEM_READ_WRITE); - size_buffer.alloc(1); - size_buffer.zero_to_device(); - - uint threads = num_threads; - CUdeviceptr d_size = (CUdeviceptr)size_buffer.device_pointer; - - struct args_t { - uint *num_threads; - CUdeviceptr *size; - }; - - args_t args = {&threads, &d_size}; - - CUfunction state_buffer_size; - cuda_assert( - cuModuleGetFunction(&state_buffer_size, device->cuModule, "kernel_cuda_state_buffer_size")); - - cuda_assert(cuLaunchKernel(state_buffer_size, 1, 1, 1, 1, 1, 1, 0, 0, (void **)&args, 0)); - - size_buffer.copy_from_device(0, 1, 1); - size_t size = size_buffer[0]; - size_buffer.free(); - - return size; -} - -bool CUDASplitKernel::enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory & /*kernel_globals*/, - device_memory & /*kernel_data*/, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs) -{ - CUDAContextScope scope(device); - - CUdeviceptr d_split_data = (CUdeviceptr)split_data.device_pointer; - CUdeviceptr d_ray_state = (CUdeviceptr)ray_state.device_pointer; - CUdeviceptr d_queue_index = (CUdeviceptr)queue_index.device_pointer; - CUdeviceptr d_use_queues_flag = (CUdeviceptr)use_queues_flag.device_pointer; - CUdeviceptr d_work_pool_wgs = (CUdeviceptr)work_pool_wgs.device_pointer; - - CUdeviceptr d_buffer = (CUdeviceptr)rtile.buffer; - - int end_sample = rtile.start_sample + rtile.num_samples; - int queue_size = dim.global_size[0] * dim.global_size[1]; - - struct args_t { - CUdeviceptr *split_data_buffer; - int *num_elements; - CUdeviceptr *ray_state; - int *start_sample; - int *end_sample; - int *sx; - int *sy; - int *sw; - int *sh; - int *offset; - int *stride; - CUdeviceptr *queue_index; - int *queuesize; - CUdeviceptr *use_queues_flag; - CUdeviceptr *work_pool_wgs; - int *num_samples; - CUdeviceptr *buffer; - }; - - args_t args = {&d_split_data, - &num_global_elements, - &d_ray_state, - &rtile.start_sample, - &end_sample, - &rtile.x, - &rtile.y, - &rtile.w, - &rtile.h, - &rtile.offset, - &rtile.stride, - &d_queue_index, - &queue_size, - &d_use_queues_flag, - &d_work_pool_wgs, - &rtile.num_samples, - &d_buffer}; - - CUfunction data_init; - cuda_assert( - cuModuleGetFunction(&data_init, device->cuModule, "kernel_cuda_path_trace_data_init")); - if (device->have_error()) { - return false; - } - - CUDASplitKernelFunction(device, data_init).enqueue(dim, (void **)&args); - - return !device->have_error(); -} - -SplitKernelFunction *CUDASplitKernel::get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &) -{ - const CUDAContextScope scope(device); - - CUfunction func; - const CUresult result = cuModuleGetFunction( - &func, device->cuModule, (string("kernel_cuda_") + kernel_name).data()); - if (result != CUDA_SUCCESS) { - device->set_error(string_printf("Could not find kernel \"kernel_cuda_%s\" in module (%s)", - kernel_name.data(), - cuewErrorString(result))); - return NULL; - } - - return new CUDASplitKernelFunction(device, func); -} - -int2 CUDASplitKernel::split_kernel_local_size() -{ - return make_int2(32, 1); -} - -int2 CUDASplitKernel::split_kernel_global_size(device_memory &kg, - device_memory &data, - DeviceTask & /*task*/) -{ - CUDAContextScope scope(device); - size_t free; - size_t total; - - cuda_assert(cuMemGetInfo(&free, &total)); - - VLOG(1) << "Maximum device allocation size: " << string_human_readable_number(free) - << " bytes. (" << string_human_readable_size(free) << ")."; - - size_t num_elements = max_elements_for_max_buffer_size(kg, data, free / 2); - size_t side = round_down((int)sqrt(num_elements), 32); - int2 global_size = make_int2(side, round_down(num_elements / side, 16)); - VLOG(1) << "Global size: " << global_size << "."; - return global_size; -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/cuda/device_impl.cpp b/intern/cycles/device/cuda/device_impl.cpp new file mode 100644 index 00000000000..2bb0592bcc5 --- /dev/null +++ b/intern/cycles/device/cuda/device_impl.cpp @@ -0,0 +1,1238 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include +# include +# include +# include +# include + +# include "device/cuda/device_impl.h" + +# include "util/debug.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/map.h" +# include "util/md5.h" +# include "util/path.h" +# include "util/string.h" +# include "util/system.h" +# include "util/time.h" +# include "util/types.h" +# include "util/windows.h" + +CCL_NAMESPACE_BEGIN + +class CUDADevice; + +bool CUDADevice::have_precompiled_kernels() +{ + string cubins_path = path_get("lib"); + return path_exists(cubins_path); +} + +bool CUDADevice::show_samples() const +{ + /* The CUDADevice only processes one tile at a time, so showing samples is fine. */ + return true; +} + +BVHLayoutMask CUDADevice::get_bvh_layout_mask() const +{ + return BVH_LAYOUT_BVH2; +} + +void CUDADevice::set_error(const string &error) +{ + Device::set_error(error); + + if (first_error) { + fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n"); + fprintf(stderr, + "https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html\n\n"); + first_error = false; + } +} + +CUDADevice::CUDADevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : Device(info, stats, profiler), texture_info(this, "__texture_info", MEM_GLOBAL) +{ + first_error = true; + + cuDevId = info.num; + cuDevice = 0; + cuContext = 0; + + cuModule = 0; + + need_texture_info = false; + + device_texture_headroom = 0; + device_working_headroom = 0; + move_texture_to_host = false; + map_host_limit = 0; + map_host_used = 0; + can_map_host = 0; + pitch_alignment = 0; + + /* Initialize CUDA. */ + CUresult result = cuInit(0); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to initialize CUDA runtime (%s)", cuewErrorString(result))); + return; + } + + /* Setup device and context. */ + result = cuDeviceGet(&cuDevice, cuDevId); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to get CUDA device handle from ordinal (%s)", + cuewErrorString(result))); + return; + } + + /* CU_CTX_MAP_HOST for mapping host memory when out of device memory. + * CU_CTX_LMEM_RESIZE_TO_MAX for reserving local memory ahead of render, + * so we can predict which memory to map to host. */ + cuda_assert( + cuDeviceGetAttribute(&can_map_host, CU_DEVICE_ATTRIBUTE_CAN_MAP_HOST_MEMORY, cuDevice)); + + cuda_assert(cuDeviceGetAttribute( + &pitch_alignment, CU_DEVICE_ATTRIBUTE_TEXTURE_PITCH_ALIGNMENT, cuDevice)); + + unsigned int ctx_flags = CU_CTX_LMEM_RESIZE_TO_MAX; + if (can_map_host) { + ctx_flags |= CU_CTX_MAP_HOST; + init_host_memory(); + } + + /* Create context. */ + result = cuCtxCreate(&cuContext, ctx_flags, cuDevice); + + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to create CUDA context (%s)", cuewErrorString(result))); + return; + } + + int major, minor; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); + cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); + cuDevArchitecture = major * 100 + minor * 10; + + /* Pop context set by cuCtxCreate. */ + cuCtxPopCurrent(NULL); +} + +CUDADevice::~CUDADevice() +{ + texture_info.free(); + + cuda_assert(cuCtxDestroy(cuContext)); +} + +bool CUDADevice::support_device(const uint /*kernel_features*/) +{ + int major, minor; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); + cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); + + /* We only support sm_30 and above */ + if (major < 3) { + set_error(string_printf( + "CUDA backend requires compute capability 3.0 or up, but found %d.%d.", major, minor)); + return false; + } + + return true; +} + +bool CUDADevice::check_peer_access(Device *peer_device) +{ + if (peer_device == this) { + return false; + } + if (peer_device->info.type != DEVICE_CUDA && peer_device->info.type != DEVICE_OPTIX) { + return false; + } + + CUDADevice *const peer_device_cuda = static_cast(peer_device); + + int can_access = 0; + cuda_assert(cuDeviceCanAccessPeer(&can_access, cuDevice, peer_device_cuda->cuDevice)); + if (can_access == 0) { + return false; + } + + // Ensure array access over the link is possible as well (for 3D textures) + cuda_assert(cuDeviceGetP2PAttribute(&can_access, + CU_DEVICE_P2P_ATTRIBUTE_CUDA_ARRAY_ACCESS_SUPPORTED, + cuDevice, + peer_device_cuda->cuDevice)); + if (can_access == 0) { + return false; + } + + // Enable peer access in both directions + { + const CUDAContextScope scope(this); + CUresult result = cuCtxEnablePeerAccess(peer_device_cuda->cuContext, 0); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to enable peer access on CUDA context (%s)", + cuewErrorString(result))); + return false; + } + } + { + const CUDAContextScope scope(peer_device_cuda); + CUresult result = cuCtxEnablePeerAccess(cuContext, 0); + if (result != CUDA_SUCCESS) { + set_error(string_printf("Failed to enable peer access on CUDA context (%s)", + cuewErrorString(result))); + return false; + } + } + + return true; +} + +bool CUDADevice::use_adaptive_compilation() +{ + return DebugFlags().cuda.adaptive_compile; +} + +/* Common NVCC flags which stays the same regardless of shading model, + * kernel sources md5 and only depends on compiler or compilation settings. + */ +string CUDADevice::compile_kernel_get_common_cflags(const uint kernel_features) +{ + const int machine = system_cpu_bits(); + const string source_path = path_get("source"); + const string include_path = source_path; + string cflags = string_printf( + "-m%d " + "--ptxas-options=\"-v\" " + "--use_fast_math " + "-DNVCC " + "-I\"%s\"", + machine, + include_path.c_str()); + if (use_adaptive_compilation()) { + cflags += " -D__KERNEL_FEATURES__=" + to_string(kernel_features); + } + const char *extra_cflags = getenv("CYCLES_CUDA_EXTRA_CFLAGS"); + if (extra_cflags) { + cflags += string(" ") + string(extra_cflags); + } + +# ifdef WITH_NANOVDB + cflags += " -DWITH_NANOVDB"; +# endif + + return cflags; +} + +string CUDADevice::compile_kernel(const uint kernel_features, + const char *name, + const char *base, + bool force_ptx) +{ + /* Compute kernel name. */ + int major, minor; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, cuDevId); + cuDeviceGetAttribute(&minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, cuDevId); + + /* Attempt to use kernel provided with Blender. */ + if (!use_adaptive_compilation()) { + if (!force_ptx) { + const string cubin = path_get(string_printf("lib/%s_sm_%d%d.cubin", name, major, minor)); + VLOG(1) << "Testing for pre-compiled kernel " << cubin << "."; + if (path_exists(cubin)) { + VLOG(1) << "Using precompiled kernel."; + return cubin; + } + } + + /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */ + int ptx_major = major, ptx_minor = minor; + while (ptx_major >= 3) { + const string ptx = path_get( + string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor)); + VLOG(1) << "Testing for pre-compiled kernel " << ptx << "."; + if (path_exists(ptx)) { + VLOG(1) << "Using precompiled kernel."; + return ptx; + } + + if (ptx_minor > 0) { + ptx_minor--; + } + else { + ptx_major--; + ptx_minor = 9; + } + } + } + + /* Try to use locally compiled kernel. */ + string source_path = path_get("source"); + const string source_md5 = path_files_md5_hash(source_path); + + /* We include cflags into md5 so changing cuda toolkit or changing other + * compiler command line arguments makes sure cubin gets re-built. + */ + string common_cflags = compile_kernel_get_common_cflags(kernel_features); + const string kernel_md5 = util_md5_string(source_md5 + common_cflags); + + const char *const kernel_ext = force_ptx ? "ptx" : "cubin"; + const char *const kernel_arch = force_ptx ? "compute" : "sm"; + const string cubin_file = string_printf( + "cycles_%s_%s_%d%d_%s.%s", name, kernel_arch, major, minor, kernel_md5.c_str(), kernel_ext); + const string cubin = path_cache_get(path_join("kernels", cubin_file)); + VLOG(1) << "Testing for locally compiled kernel " << cubin << "."; + if (path_exists(cubin)) { + VLOG(1) << "Using locally compiled kernel."; + return cubin; + } + +# ifdef _WIN32 + if (!use_adaptive_compilation() && have_precompiled_kernels()) { + if (major < 3) { + set_error( + string_printf("CUDA backend requires compute capability 3.0 or up, but found %d.%d. " + "Your GPU is not supported.", + major, + minor)); + } + else { + set_error( + string_printf("CUDA binary kernel for this graphics card compute " + "capability (%d.%d) not found.", + major, + minor)); + } + return string(); + } +# endif + + /* Compile. */ + const char *const nvcc = cuewCompilerPath(); + if (nvcc == NULL) { + set_error( + "CUDA nvcc compiler not found. " + "Install CUDA toolkit in default location."); + return string(); + } + + const int nvcc_cuda_version = cuewCompilerVersion(); + VLOG(1) << "Found nvcc " << nvcc << ", CUDA version " << nvcc_cuda_version << "."; + if (nvcc_cuda_version < 101) { + printf( + "Unsupported CUDA version %d.%d detected, " + "you need CUDA 10.1 or newer.\n", + nvcc_cuda_version / 10, + nvcc_cuda_version % 10); + return string(); + } + else if (!(nvcc_cuda_version == 101 || nvcc_cuda_version == 102 || nvcc_cuda_version == 111 || + nvcc_cuda_version == 112 || nvcc_cuda_version == 113 || nvcc_cuda_version == 114)) { + printf( + "CUDA version %d.%d detected, build may succeed but only " + "CUDA 10.1 to 11.4 are officially supported.\n", + nvcc_cuda_version / 10, + nvcc_cuda_version % 10); + } + + double starttime = time_dt(); + + path_create_directories(cubin); + + source_path = path_join(path_join(source_path, "kernel"), + path_join("device", path_join(base, string_printf("%s.cu", name)))); + + string command = string_printf( + "\"%s\" " + "-arch=%s_%d%d " + "--%s \"%s\" " + "-o \"%s\" " + "%s", + nvcc, + kernel_arch, + major, + minor, + kernel_ext, + source_path.c_str(), + cubin.c_str(), + common_cflags.c_str()); + + printf("Compiling %sCUDA kernel ...\n%s\n", + (use_adaptive_compilation()) ? "adaptive " : "", + command.c_str()); + +# ifdef _WIN32 + command = "call " + command; +# endif + if (system(command.c_str()) != 0) { + set_error( + "Failed to execute compilation command, " + "see console for details."); + return string(); + } + + /* Verify if compilation succeeded */ + if (!path_exists(cubin)) { + set_error( + "CUDA kernel compilation failed, " + "see console for details."); + return string(); + } + + printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime); + + return cubin; +} + +bool CUDADevice::load_kernels(const uint kernel_features) +{ + /* TODO(sergey): Support kernels re-load for CUDA devices adaptive compile. + * + * Currently re-loading kernel will invalidate memory pointers, + * causing problems in cuCtxSynchronize. + */ + if (cuModule) { + if (use_adaptive_compilation()) { + VLOG(1) << "Skipping CUDA kernel reload for adaptive compilation, not currently supported."; + } + return true; + } + + /* check if cuda init succeeded */ + if (cuContext == 0) + return false; + + /* check if GPU is supported */ + if (!support_device(kernel_features)) + return false; + + /* get kernel */ + const char *kernel_name = "kernel"; + string cubin = compile_kernel(kernel_features, kernel_name); + if (cubin.empty()) + return false; + + /* open module */ + CUDAContextScope scope(this); + + string cubin_data; + CUresult result; + + if (path_read_text(cubin, cubin_data)) + result = cuModuleLoadData(&cuModule, cubin_data.c_str()); + else + result = CUDA_ERROR_FILE_NOT_FOUND; + + if (result != CUDA_SUCCESS) + set_error(string_printf( + "Failed to load CUDA kernel from '%s' (%s)", cubin.c_str(), cuewErrorString(result))); + + if (result == CUDA_SUCCESS) { + kernels.load(this); + reserve_local_memory(kernel_features); + } + + return (result == CUDA_SUCCESS); +} + +void CUDADevice::reserve_local_memory(const uint kernel_features) +{ + /* Together with CU_CTX_LMEM_RESIZE_TO_MAX, this reserves local memory + * needed for kernel launches, so that we can reliably figure out when + * to allocate scene data in mapped host memory. */ + size_t total = 0, free_before = 0, free_after = 0; + + { + CUDAContextScope scope(this); + cuMemGetInfo(&free_before, &total); + } + + { + /* Use the biggest kernel for estimation. */ + const DeviceKernel test_kernel = (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE : + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE; + + /* Launch kernel, using just 1 block appears sufficient to reserve memory for all + * multiprocessors. It would be good to do this in parallel for the multi GPU case + * still to make it faster. */ + CUDADeviceQueue queue(this); + + void *d_path_index = nullptr; + void *d_render_buffer = nullptr; + int d_work_size = 0; + void *args[] = {&d_path_index, &d_render_buffer, &d_work_size}; + + queue.init_execution(); + queue.enqueue(test_kernel, 1, args); + queue.synchronize(); + } + + { + CUDAContextScope scope(this); + cuMemGetInfo(&free_after, &total); + } + + VLOG(1) << "Local memory reserved " << string_human_readable_number(free_before - free_after) + << " bytes. (" << string_human_readable_size(free_before - free_after) << ")"; + +# if 0 + /* For testing mapped host memory, fill up device memory. */ + const size_t keep_mb = 1024; + + while (free_after > keep_mb * 1024 * 1024LL) { + CUdeviceptr tmp; + cuda_assert(cuMemAlloc(&tmp, 10 * 1024 * 1024LL)); + cuMemGetInfo(&free_after, &total); + } +# endif +} + +void CUDADevice::init_host_memory() +{ + /* Limit amount of host mapped memory, because allocating too much can + * cause system instability. Leave at least half or 4 GB of system + * memory free, whichever is smaller. */ + size_t default_limit = 4 * 1024 * 1024 * 1024LL; + size_t system_ram = system_physical_ram(); + + if (system_ram > 0) { + if (system_ram / 2 > default_limit) { + map_host_limit = system_ram - default_limit; + } + else { + map_host_limit = system_ram / 2; + } + } + else { + VLOG(1) << "Mapped host memory disabled, failed to get system RAM"; + map_host_limit = 0; + } + + /* Amount of device memory to keep is free after texture memory + * and working memory allocations respectively. We set the working + * memory limit headroom lower so that some space is left after all + * texture memory allocations. */ + device_working_headroom = 32 * 1024 * 1024LL; // 32MB + device_texture_headroom = 128 * 1024 * 1024LL; // 128MB + + VLOG(1) << "Mapped host memory limit set to " << string_human_readable_number(map_host_limit) + << " bytes. (" << string_human_readable_size(map_host_limit) << ")"; +} + +void CUDADevice::load_texture_info() +{ + if (need_texture_info) { + /* Unset flag before copying, so this does not loop indefinitely if the copy below calls + * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ + need_texture_info = false; + texture_info.copy_to_device(); + } +} + +void CUDADevice::move_textures_to_host(size_t size, bool for_texture) +{ + /* Break out of recursive call, which can happen when moving memory on a multi device. */ + static bool any_device_moving_textures_to_host = false; + if (any_device_moving_textures_to_host) { + return; + } + + /* Signal to reallocate textures in host memory only. */ + move_texture_to_host = true; + + while (size > 0) { + /* Find suitable memory allocation to move. */ + device_memory *max_mem = NULL; + size_t max_size = 0; + bool max_is_image = false; + + thread_scoped_lock lock(cuda_mem_map_mutex); + foreach (CUDAMemMap::value_type &pair, cuda_mem_map) { + device_memory &mem = *pair.first; + CUDAMem *cmem = &pair.second; + + /* Can only move textures allocated on this device (and not those from peer devices). + * And need to ignore memory that is already on the host. */ + if (!mem.is_resident(this) || cmem->use_mapped_host) { + continue; + } + + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && + (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + /* Can't move this type of memory. */ + if (!is_texture || cmem->array) { + continue; + } + + /* For other textures, only move image textures. */ + if (for_texture && !is_image) { + continue; + } + + /* Try to move largest allocation, prefer moving images. */ + if (is_image > max_is_image || (is_image == max_is_image && mem.device_size > max_size)) { + max_is_image = is_image; + max_size = mem.device_size; + max_mem = &mem; + } + } + lock.unlock(); + + /* Move to host memory. This part is mutex protected since + * multiple CUDA devices could be moving the memory. The + * first one will do it, and the rest will adopt the pointer. */ + if (max_mem) { + VLOG(1) << "Move memory from device to host: " << max_mem->name; + + static thread_mutex move_mutex; + thread_scoped_lock lock(move_mutex); + + any_device_moving_textures_to_host = true; + + /* Potentially need to call back into multi device, so pointer mapping + * and peer devices are updated. This is also necessary since the device + * pointer may just be a key here, so cannot be accessed and freed directly. + * Unfortunately it does mean that memory is reallocated on all other + * devices as well, which is potentially dangerous when still in use (since + * a thread rendering on another devices would only be caught in this mutex + * if it so happens to do an allocation at the same time as well. */ + max_mem->device_copy_to(); + size = (max_size >= size) ? 0 : size - max_size; + + any_device_moving_textures_to_host = false; + } + else { + break; + } + } + + /* Unset flag before texture info is reloaded, since it should stay in device memory. */ + move_texture_to_host = false; + + /* Update texture info array with new pointers. */ + load_texture_info(); +} + +CUDADevice::CUDAMem *CUDADevice::generic_alloc(device_memory &mem, size_t pitch_padding) +{ + CUDAContextScope scope(this); + + CUdeviceptr device_pointer = 0; + size_t size = mem.memory_size() + pitch_padding; + + CUresult mem_alloc_result = CUDA_ERROR_OUT_OF_MEMORY; + const char *status = ""; + + /* First try allocating in device memory, respecting headroom. We make + * an exception for texture info. It is small and frequently accessed, + * so treat it as working memory. + * + * If there is not enough room for working memory, we will try to move + * textures to host memory, assuming the performance impact would have + * been worse for working memory. */ + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + size_t headroom = (is_texture) ? device_texture_headroom : device_working_headroom; + + size_t total = 0, free = 0; + cuMemGetInfo(&free, &total); + + /* Move textures to host memory if needed. */ + if (!move_texture_to_host && !is_image && (size + headroom) >= free && can_map_host) { + move_textures_to_host(size + headroom - free, is_texture); + cuMemGetInfo(&free, &total); + } + + /* Allocate in device memory. */ + if (!move_texture_to_host && (size + headroom) < free) { + mem_alloc_result = cuMemAlloc(&device_pointer, size); + if (mem_alloc_result == CUDA_SUCCESS) { + status = " in device memory"; + } + } + + /* Fall back to mapped host memory if needed and possible. */ + + void *shared_pointer = 0; + + if (mem_alloc_result != CUDA_SUCCESS && can_map_host) { + if (mem.shared_pointer) { + /* Another device already allocated host memory. */ + mem_alloc_result = CUDA_SUCCESS; + shared_pointer = mem.shared_pointer; + } + else if (map_host_used + size < map_host_limit) { + /* Allocate host memory ourselves. */ + mem_alloc_result = cuMemHostAlloc( + &shared_pointer, size, CU_MEMHOSTALLOC_DEVICEMAP | CU_MEMHOSTALLOC_WRITECOMBINED); + + assert((mem_alloc_result == CUDA_SUCCESS && shared_pointer != 0) || + (mem_alloc_result != CUDA_SUCCESS && shared_pointer == 0)); + } + + if (mem_alloc_result == CUDA_SUCCESS) { + cuda_assert(cuMemHostGetDevicePointer_v2(&device_pointer, shared_pointer, 0)); + map_host_used += size; + status = " in host memory"; + } + } + + if (mem_alloc_result != CUDA_SUCCESS) { + status = " failed, out of device and host memory"; + set_error("System is out of GPU and shared host memory"); + } + + if (mem.name) { + VLOG(1) << "Buffer allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")" << status; + } + + mem.device_pointer = (device_ptr)device_pointer; + mem.device_size = size; + stats.mem_alloc(size); + + if (!mem.device_pointer) { + return NULL; + } + + /* Insert into map of allocations. */ + thread_scoped_lock lock(cuda_mem_map_mutex); + CUDAMem *cmem = &cuda_mem_map[&mem]; + if (shared_pointer != 0) { + /* Replace host pointer with our host allocation. Only works if + * CUDA memory layout is the same and has no pitch padding. Also + * does not work if we move textures to host during a render, + * since other devices might be using the memory. */ + + if (!move_texture_to_host && pitch_padding == 0 && mem.host_pointer && + mem.host_pointer != shared_pointer) { + memcpy(shared_pointer, mem.host_pointer, size); + + /* A Call to device_memory::host_free() should be preceded by + * a call to device_memory::device_free() for host memory + * allocated by a device to be handled properly. Two exceptions + * are here and a call in OptiXDevice::generic_alloc(), where + * the current host memory can be assumed to be allocated by + * device_memory::host_alloc(), not by a device */ + + mem.host_free(); + mem.host_pointer = shared_pointer; + } + mem.shared_pointer = shared_pointer; + mem.shared_counter++; + cmem->use_mapped_host = true; + } + else { + cmem->use_mapped_host = false; + } + + return cmem; +} + +void CUDADevice::generic_copy_to(device_memory &mem) +{ + if (!mem.host_pointer || !mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, the current device only uses device memory allocated by + * cuMemAlloc regardless of mem.host_pointer and mem.shared_pointer, and should copy data from + * mem.host_pointer. */ + thread_scoped_lock lock(cuda_mem_map_mutex); + if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const CUDAContextScope scope(this); + cuda_assert( + cuMemcpyHtoD((CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size())); + } +} + +void CUDADevice::generic_free(device_memory &mem) +{ + if (mem.device_pointer) { + CUDAContextScope scope(this); + thread_scoped_lock lock(cuda_mem_map_mutex); + const CUDAMem &cmem = cuda_mem_map[&mem]; + + /* If cmem.use_mapped_host is true, reference counting is used + * to safely free a mapped host memory. */ + + if (cmem.use_mapped_host) { + assert(mem.shared_pointer); + if (mem.shared_pointer) { + assert(mem.shared_counter > 0); + if (--mem.shared_counter == 0) { + if (mem.host_pointer == mem.shared_pointer) { + mem.host_pointer = 0; + } + cuMemFreeHost(mem.shared_pointer); + mem.shared_pointer = 0; + } + } + map_host_used -= mem.device_size; + } + else { + /* Free device memory. */ + cuda_assert(cuMemFree(mem.device_pointer)); + } + + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + cuda_mem_map.erase(cuda_mem_map.find(&mem)); + } +} + +void CUDADevice::mem_alloc(device_memory &mem) +{ + if (mem.type == MEM_TEXTURE) { + assert(!"mem_alloc not supported for textures."); + } + else if (mem.type == MEM_GLOBAL) { + assert(!"mem_alloc not supported for global memory."); + } + else { + generic_alloc(mem); + } +} + +void CUDADevice::mem_copy_to(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + global_alloc(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + tex_alloc((device_texture &)mem); + } + else { + if (!mem.device_pointer) { + generic_alloc(mem); + } + generic_copy_to(mem); + } +} + +void CUDADevice::mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) +{ + if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) { + assert(!"mem_copy_from not supported for textures."); + } + else if (mem.host_pointer) { + const size_t size = elem * w * h; + const size_t offset = elem * y * w; + + if (mem.device_pointer) { + const CUDAContextScope scope(this); + cuda_assert(cuMemcpyDtoH( + (char *)mem.host_pointer + offset, (CUdeviceptr)mem.device_pointer + offset, size)); + } + else { + memset((char *)mem.host_pointer + offset, 0, size); + } + } +} + +void CUDADevice::mem_zero(device_memory &mem) +{ + if (!mem.device_pointer) { + mem_alloc(mem); + } + if (!mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory + * regardless of mem.host_pointer and mem.shared_pointer. */ + thread_scoped_lock lock(cuda_mem_map_mutex); + if (!cuda_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const CUDAContextScope scope(this); + cuda_assert(cuMemsetD8((CUdeviceptr)mem.device_pointer, 0, mem.memory_size())); + } + else if (mem.host_pointer) { + memset(mem.host_pointer, 0, mem.memory_size()); + } +} + +void CUDADevice::mem_free(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + } + else { + generic_free(mem); + } +} + +device_ptr CUDADevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) +{ + return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); +} + +void CUDADevice::const_copy_to(const char *name, void *host, size_t size) +{ + CUDAContextScope scope(this); + CUdeviceptr mem; + size_t bytes; + + cuda_assert(cuModuleGetGlobal(&mem, &bytes, cuModule, name)); + // assert(bytes == size); + cuda_assert(cuMemcpyHtoD(mem, host, size)); +} + +void CUDADevice::global_alloc(device_memory &mem) +{ + if (mem.is_resident(this)) { + generic_alloc(mem); + generic_copy_to(mem); + } + + const_copy_to(mem.name, &mem.device_pointer, sizeof(mem.device_pointer)); +} + +void CUDADevice::global_free(device_memory &mem) +{ + if (mem.is_resident(this) && mem.device_pointer) { + generic_free(mem); + } +} + +void CUDADevice::tex_alloc(device_texture &mem) +{ + CUDAContextScope scope(this); + + /* General variables for both architectures */ + string bind_name = mem.name; + size_t dsize = datatype_size(mem.data_type); + size_t size = mem.memory_size(); + + CUaddress_mode address_mode = CU_TR_ADDRESS_MODE_WRAP; + switch (mem.info.extension) { + case EXTENSION_REPEAT: + address_mode = CU_TR_ADDRESS_MODE_WRAP; + break; + case EXTENSION_EXTEND: + address_mode = CU_TR_ADDRESS_MODE_CLAMP; + break; + case EXTENSION_CLIP: + address_mode = CU_TR_ADDRESS_MODE_BORDER; + break; + default: + assert(0); + break; + } + + CUfilter_mode filter_mode; + if (mem.info.interpolation == INTERPOLATION_CLOSEST) { + filter_mode = CU_TR_FILTER_MODE_POINT; + } + else { + filter_mode = CU_TR_FILTER_MODE_LINEAR; + } + + /* Image Texture Storage */ + CUarray_format_enum format; + switch (mem.data_type) { + case TYPE_UCHAR: + format = CU_AD_FORMAT_UNSIGNED_INT8; + break; + case TYPE_UINT16: + format = CU_AD_FORMAT_UNSIGNED_INT16; + break; + case TYPE_UINT: + format = CU_AD_FORMAT_UNSIGNED_INT32; + break; + case TYPE_INT: + format = CU_AD_FORMAT_SIGNED_INT32; + break; + case TYPE_FLOAT: + format = CU_AD_FORMAT_FLOAT; + break; + case TYPE_HALF: + format = CU_AD_FORMAT_HALF; + break; + default: + assert(0); + return; + } + + CUDAMem *cmem = NULL; + CUarray array_3d = NULL; + size_t src_pitch = mem.data_width * dsize * mem.data_elements; + size_t dst_pitch = src_pitch; + + if (!mem.is_resident(this)) { + thread_scoped_lock lock(cuda_mem_map_mutex); + cmem = &cuda_mem_map[&mem]; + cmem->texobject = 0; + + if (mem.data_depth > 1) { + array_3d = (CUarray)mem.device_pointer; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + dst_pitch = align_up(src_pitch, pitch_alignment); + } + } + else if (mem.data_depth > 1) { + /* 3D texture using array, there is no API for linear memory. */ + CUDA_ARRAY3D_DESCRIPTOR desc; + + desc.Width = mem.data_width; + desc.Height = mem.data_height; + desc.Depth = mem.data_depth; + desc.Format = format; + desc.NumChannels = mem.data_elements; + desc.Flags = 0; + + VLOG(1) << "Array 3D allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + cuda_assert(cuArray3DCreate(&array_3d, &desc)); + + if (!array_3d) { + return; + } + + CUDA_MEMCPY3D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = CU_MEMORYTYPE_ARRAY; + param.dstArray = array_3d; + param.srcMemoryType = CU_MEMORYTYPE_HOST; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + param.Depth = mem.data_depth; + + cuda_assert(cuMemcpy3D(¶m)); + + mem.device_pointer = (device_ptr)array_3d; + mem.device_size = size; + stats.mem_alloc(size); + + thread_scoped_lock lock(cuda_mem_map_mutex); + cmem = &cuda_mem_map[&mem]; + cmem->texobject = 0; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + /* 2D texture, using pitch aligned linear memory. */ + dst_pitch = align_up(src_pitch, pitch_alignment); + size_t dst_size = dst_pitch * mem.data_height; + + cmem = generic_alloc(mem, dst_size - mem.memory_size()); + if (!cmem) { + return; + } + + CUDA_MEMCPY2D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = CU_MEMORYTYPE_DEVICE; + param.dstDevice = mem.device_pointer; + param.dstPitch = dst_pitch; + param.srcMemoryType = CU_MEMORYTYPE_HOST; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + + cuda_assert(cuMemcpy2DUnaligned(¶m)); + } + else { + /* 1D texture, using linear memory. */ + cmem = generic_alloc(mem); + if (!cmem) { + return; + } + + cuda_assert(cuMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); + } + + /* Resize once */ + const uint slot = mem.slot; + if (slot >= texture_info.size()) { + /* Allocate some slots in advance, to reduce amount + * of re-allocations. */ + texture_info.resize(slot + 128); + } + + /* Set Mapping and tag that we need to (re-)upload to device */ + texture_info[slot] = mem.info; + need_texture_info = true; + + if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { + /* Kepler+, bindless textures. */ + CUDA_RESOURCE_DESC resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + + if (array_3d) { + resDesc.resType = CU_RESOURCE_TYPE_ARRAY; + resDesc.res.array.hArray = array_3d; + resDesc.flags = 0; + } + else if (mem.data_height > 0) { + resDesc.resType = CU_RESOURCE_TYPE_PITCH2D; + resDesc.res.pitch2D.devPtr = mem.device_pointer; + resDesc.res.pitch2D.format = format; + resDesc.res.pitch2D.numChannels = mem.data_elements; + resDesc.res.pitch2D.height = mem.data_height; + resDesc.res.pitch2D.width = mem.data_width; + resDesc.res.pitch2D.pitchInBytes = dst_pitch; + } + else { + resDesc.resType = CU_RESOURCE_TYPE_LINEAR; + resDesc.res.linear.devPtr = mem.device_pointer; + resDesc.res.linear.format = format; + resDesc.res.linear.numChannels = mem.data_elements; + resDesc.res.linear.sizeInBytes = mem.device_size; + } + + CUDA_TEXTURE_DESC texDesc; + memset(&texDesc, 0, sizeof(texDesc)); + texDesc.addressMode[0] = address_mode; + texDesc.addressMode[1] = address_mode; + texDesc.addressMode[2] = address_mode; + texDesc.filterMode = filter_mode; + texDesc.flags = CU_TRSF_NORMALIZED_COORDINATES; + + thread_scoped_lock lock(cuda_mem_map_mutex); + cmem = &cuda_mem_map[&mem]; + + cuda_assert(cuTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); + + texture_info[slot].data = (uint64_t)cmem->texobject; + } + else { + texture_info[slot].data = (uint64_t)mem.device_pointer; + } +} + +void CUDADevice::tex_free(device_texture &mem) +{ + if (mem.device_pointer) { + CUDAContextScope scope(this); + thread_scoped_lock lock(cuda_mem_map_mutex); + const CUDAMem &cmem = cuda_mem_map[&mem]; + + if (cmem.texobject) { + /* Free bindless texture. */ + cuTexObjectDestroy(cmem.texobject); + } + + if (!mem.is_resident(this)) { + /* Do not free memory here, since it was allocated on a different device. */ + cuda_mem_map.erase(cuda_mem_map.find(&mem)); + } + else if (cmem.array) { + /* Free array. */ + cuArrayDestroy(cmem.array); + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + cuda_mem_map.erase(cuda_mem_map.find(&mem)); + } + else { + lock.unlock(); + generic_free(mem); + } + } +} + +unique_ptr CUDADevice::gpu_queue_create() +{ + return make_unique(this); +} + +bool CUDADevice::should_use_graphics_interop() +{ + /* Check whether this device is part of OpenGL context. + * + * Using CUDA device for graphics interoperability which is not part of the OpenGL context is + * possible, but from the empiric measurements it can be considerably slower than using naive + * pixels copy. */ + + CUDAContextScope scope(this); + + int num_all_devices = 0; + cuda_assert(cuDeviceGetCount(&num_all_devices)); + + if (num_all_devices == 0) { + return false; + } + + vector gl_devices(num_all_devices); + uint num_gl_devices; + cuGLGetDevices(&num_gl_devices, gl_devices.data(), num_all_devices, CU_GL_DEVICE_LIST_ALL); + + for (CUdevice gl_device : gl_devices) { + if (gl_device == cuDevice) { + return true; + } + } + + return false; +} + +int CUDADevice::get_num_multiprocessors() +{ + return get_device_default_attribute(CU_DEVICE_ATTRIBUTE_MULTIPROCESSOR_COUNT, 0); +} + +int CUDADevice::get_max_num_threads_per_multiprocessor() +{ + return get_device_default_attribute(CU_DEVICE_ATTRIBUTE_MAX_THREADS_PER_MULTIPROCESSOR, 0); +} + +bool CUDADevice::get_device_attribute(CUdevice_attribute attribute, int *value) +{ + CUDAContextScope scope(this); + + return cuDeviceGetAttribute(value, attribute, cuDevice) == CUDA_SUCCESS; +} + +int CUDADevice::get_device_default_attribute(CUdevice_attribute attribute, int default_value) +{ + int value = 0; + if (!get_device_attribute(attribute, &value)) { + return default_value; + } + return value; +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/device_impl.h b/intern/cycles/device/cuda/device_impl.h new file mode 100644 index 00000000000..72d4108d1bf --- /dev/null +++ b/intern/cycles/device/cuda/device_impl.h @@ -0,0 +1,154 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/kernel.h" +# include "device/cuda/queue.h" +# include "device/cuda/util.h" +# include "device/device.h" + +# include "util/map.h" + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include +# include +# endif + +CCL_NAMESPACE_BEGIN + +class DeviceQueue; + +class CUDADevice : public Device { + + friend class CUDAContextScope; + + public: + CUdevice cuDevice; + CUcontext cuContext; + CUmodule cuModule; + size_t device_texture_headroom; + size_t device_working_headroom; + bool move_texture_to_host; + size_t map_host_used; + size_t map_host_limit; + int can_map_host; + int pitch_alignment; + int cuDevId; + int cuDevArchitecture; + bool first_error; + + struct CUDAMem { + CUDAMem() : texobject(0), array(0), use_mapped_host(false) + { + } + + CUtexObject texobject; + CUarray array; + + /* If true, a mapped host memory in shared_pointer is being used. */ + bool use_mapped_host; + }; + typedef map CUDAMemMap; + CUDAMemMap cuda_mem_map; + thread_mutex cuda_mem_map_mutex; + + /* Bindless Textures */ + device_vector texture_info; + bool need_texture_info; + + CUDADeviceKernels kernels; + + static bool have_precompiled_kernels(); + + virtual bool show_samples() const override; + + virtual BVHLayoutMask get_bvh_layout_mask() const override; + + void set_error(const string &error) override; + + CUDADevice(const DeviceInfo &info, Stats &stats, Profiler &profiler); + + virtual ~CUDADevice(); + + bool support_device(const uint /*kernel_features*/); + + bool check_peer_access(Device *peer_device) override; + + bool use_adaptive_compilation(); + + virtual string compile_kernel_get_common_cflags(const uint kernel_features); + + string compile_kernel(const uint kernel_features, + const char *name, + const char *base = "cuda", + bool force_ptx = false); + + virtual bool load_kernels(const uint kernel_features) override; + + void reserve_local_memory(const uint kernel_features); + + void init_host_memory(); + + void load_texture_info(); + + void move_textures_to_host(size_t size, bool for_texture); + + CUDAMem *generic_alloc(device_memory &mem, size_t pitch_padding = 0); + + void generic_copy_to(device_memory &mem); + + void generic_free(device_memory &mem); + + void mem_alloc(device_memory &mem) override; + + void mem_copy_to(device_memory &mem) override; + + void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override; + + void mem_zero(device_memory &mem) override; + + void mem_free(device_memory &mem) override; + + device_ptr mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) override; + + virtual void const_copy_to(const char *name, void *host, size_t size) override; + + void global_alloc(device_memory &mem); + + void global_free(device_memory &mem); + + void tex_alloc(device_texture &mem); + + void tex_free(device_texture &mem); + + virtual bool should_use_graphics_interop() override; + + virtual unique_ptr gpu_queue_create() override; + + int get_num_multiprocessors(); + int get_max_num_threads_per_multiprocessor(); + + protected: + bool get_device_attribute(CUdevice_attribute attribute, int *value); + int get_device_default_attribute(CUdevice_attribute attribute, int default_value); +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/graphics_interop.cpp b/intern/cycles/device/cuda/graphics_interop.cpp new file mode 100644 index 00000000000..30efefd9b6b --- /dev/null +++ b/intern/cycles/device/cuda/graphics_interop.cpp @@ -0,0 +1,103 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/graphics_interop.h" + +# include "device/cuda/device_impl.h" +# include "device/cuda/util.h" + +CCL_NAMESPACE_BEGIN + +CUDADeviceGraphicsInterop::CUDADeviceGraphicsInterop(CUDADeviceQueue *queue) + : queue_(queue), device_(static_cast(queue->device)) +{ +} + +CUDADeviceGraphicsInterop::~CUDADeviceGraphicsInterop() +{ + CUDAContextScope scope(device_); + + if (cu_graphics_resource_) { + cuda_device_assert(device_, cuGraphicsUnregisterResource(cu_graphics_resource_)); + } +} + +void CUDADeviceGraphicsInterop::set_display_interop( + const DisplayDriver::GraphicsInterop &display_interop) +{ + const int64_t new_buffer_area = int64_t(display_interop.buffer_width) * + display_interop.buffer_height; + + need_clear_ = display_interop.need_clear; + + if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) { + return; + } + + CUDAContextScope scope(device_); + + if (cu_graphics_resource_) { + cuda_device_assert(device_, cuGraphicsUnregisterResource(cu_graphics_resource_)); + } + + const CUresult result = cuGraphicsGLRegisterBuffer( + &cu_graphics_resource_, display_interop.opengl_pbo_id, CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE); + if (result != CUDA_SUCCESS) { + LOG(ERROR) << "Error registering OpenGL buffer: " << cuewErrorString(result); + } + + opengl_pbo_id_ = display_interop.opengl_pbo_id; + buffer_area_ = new_buffer_area; +} + +device_ptr CUDADeviceGraphicsInterop::map() +{ + if (!cu_graphics_resource_) { + return 0; + } + + CUDAContextScope scope(device_); + + CUdeviceptr cu_buffer; + size_t bytes; + + cuda_device_assert(device_, cuGraphicsMapResources(1, &cu_graphics_resource_, queue_->stream())); + cuda_device_assert( + device_, cuGraphicsResourceGetMappedPointer(&cu_buffer, &bytes, cu_graphics_resource_)); + + if (need_clear_) { + cuda_device_assert( + device_, cuMemsetD8Async(static_cast(cu_buffer), 0, bytes, queue_->stream())); + + need_clear_ = false; + } + + return static_cast(cu_buffer); +} + +void CUDADeviceGraphicsInterop::unmap() +{ + CUDAContextScope scope(device_); + + cuda_device_assert(device_, + cuGraphicsUnmapResources(1, &cu_graphics_resource_, queue_->stream())); +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/graphics_interop.h b/intern/cycles/device/cuda/graphics_interop.h new file mode 100644 index 00000000000..a00a837efea --- /dev/null +++ b/intern/cycles/device/cuda/graphics_interop.h @@ -0,0 +1,66 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/graphics_interop.h" + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include +# endif + +CCL_NAMESPACE_BEGIN + +class CUDADevice; +class CUDADeviceQueue; + +class CUDADeviceGraphicsInterop : public DeviceGraphicsInterop { + public: + explicit CUDADeviceGraphicsInterop(CUDADeviceQueue *queue); + + CUDADeviceGraphicsInterop(const CUDADeviceGraphicsInterop &other) = delete; + CUDADeviceGraphicsInterop(CUDADeviceGraphicsInterop &&other) noexcept = delete; + + ~CUDADeviceGraphicsInterop(); + + CUDADeviceGraphicsInterop &operator=(const CUDADeviceGraphicsInterop &other) = delete; + CUDADeviceGraphicsInterop &operator=(CUDADeviceGraphicsInterop &&other) = delete; + + virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override; + + virtual device_ptr map() override; + virtual void unmap() override; + + protected: + CUDADeviceQueue *queue_ = nullptr; + CUDADevice *device_ = nullptr; + + /* OpenGL PBO which is currently registered as the destination for the CUDA buffer. */ + uint opengl_pbo_id_ = 0; + /* Buffer area in pixels of the corresponding PBO. */ + int64_t buffer_area_ = 0; + + /* The destination was requested to be cleared. */ + bool need_clear_ = false; + + CUgraphicsResource cu_graphics_resource_ = nullptr; +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/cuda/kernel.cpp b/intern/cycles/device/cuda/kernel.cpp new file mode 100644 index 00000000000..a4a7bfabce0 --- /dev/null +++ b/intern/cycles/device/cuda/kernel.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/kernel.h" +# include "device/cuda/device_impl.h" + +CCL_NAMESPACE_BEGIN + +void CUDADeviceKernels::load(CUDADevice *device) +{ + CUmodule cuModule = device->cuModule; + + for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) { + CUDADeviceKernel &kernel = kernels_[i]; + + /* No mega-kernel used for GPU. */ + if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + continue; + } + + const std::string function_name = std::string("kernel_gpu_") + + device_kernel_as_string((DeviceKernel)i); + cuda_device_assert(device, + cuModuleGetFunction(&kernel.function, cuModule, function_name.c_str())); + + if (kernel.function) { + cuda_device_assert(device, cuFuncSetCacheConfig(kernel.function, CU_FUNC_CACHE_PREFER_L1)); + + cuda_device_assert( + device, + cuOccupancyMaxPotentialBlockSize( + &kernel.min_blocks, &kernel.num_threads_per_block, kernel.function, NULL, 0, 0)); + } + else { + LOG(ERROR) << "Unable to load kernel " << function_name; + } + } + + loaded = true; +} + +const CUDADeviceKernel &CUDADeviceKernels::get(DeviceKernel kernel) const +{ + return kernels_[(int)kernel]; +} + +bool CUDADeviceKernels::available(DeviceKernel kernel) const +{ + return kernels_[(int)kernel].function != nullptr; +} + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA*/ diff --git a/intern/cycles/device/cuda/kernel.h b/intern/cycles/device/cuda/kernel.h new file mode 100644 index 00000000000..e1650ea5b5b --- /dev/null +++ b/intern/cycles/device/cuda/kernel.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_CUDA + +# include "device/kernel.h" + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include +# endif + +CCL_NAMESPACE_BEGIN + +class CUDADevice; + +/* CUDA kernel and associate occupancy information. */ +class CUDADeviceKernel { + public: + CUfunction function = nullptr; + + int num_threads_per_block = 0; + int min_blocks = 0; +}; + +/* Cache of CUDA kernels for each DeviceKernel. */ +class CUDADeviceKernels { + public: + void load(CUDADevice *device); + const CUDADeviceKernel &get(DeviceKernel kernel) const; + bool available(DeviceKernel kernel) const; + + protected: + CUDADeviceKernel kernels_[DEVICE_KERNEL_NUM]; + bool loaded = false; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/queue.cpp b/intern/cycles/device/cuda/queue.cpp new file mode 100644 index 00000000000..09352a84181 --- /dev/null +++ b/intern/cycles/device/cuda/queue.cpp @@ -0,0 +1,238 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/queue.h" + +# include "device/cuda/device_impl.h" +# include "device/cuda/graphics_interop.h" +# include "device/cuda/kernel.h" + +CCL_NAMESPACE_BEGIN + +/* CUDADeviceQueue */ + +CUDADeviceQueue::CUDADeviceQueue(CUDADevice *device) + : DeviceQueue(device), cuda_device_(device), cuda_stream_(nullptr) +{ + const CUDAContextScope scope(cuda_device_); + cuda_device_assert(cuda_device_, cuStreamCreate(&cuda_stream_, CU_STREAM_NON_BLOCKING)); +} + +CUDADeviceQueue::~CUDADeviceQueue() +{ + const CUDAContextScope scope(cuda_device_); + cuStreamDestroy(cuda_stream_); +} + +int CUDADeviceQueue::num_concurrent_states(const size_t state_size) const +{ + const int max_num_threads = cuda_device_->get_num_multiprocessors() * + cuda_device_->get_max_num_threads_per_multiprocessor(); + int num_states = max(max_num_threads, 65536) * 16; + + const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR"); + if (factor_str) { + const float factor = (float)atof(factor_str); + if (factor != 0.0f) { + num_states = max((int)(num_states * factor), 1024); + } + else { + VLOG(3) << "CYCLES_CONCURRENT_STATES_FACTOR evaluated to 0"; + } + } + + VLOG(3) << "GPU queue concurrent states: " << num_states << ", using up to " + << string_human_readable_size(num_states * state_size); + + return num_states; +} + +int CUDADeviceQueue::num_concurrent_busy_states() const +{ + const int max_num_threads = cuda_device_->get_num_multiprocessors() * + cuda_device_->get_max_num_threads_per_multiprocessor(); + + if (max_num_threads == 0) { + return 65536; + } + + return 4 * max_num_threads; +} + +void CUDADeviceQueue::init_execution() +{ + /* Synchronize all textures and memory copies before executing task. */ + CUDAContextScope scope(cuda_device_); + cuda_device_->load_texture_info(); + cuda_device_assert(cuda_device_, cuCtxSynchronize()); + + debug_init_execution(); +} + +bool CUDADeviceQueue::kernel_available(DeviceKernel kernel) const +{ + return cuda_device_->kernels.available(kernel); +} + +bool CUDADeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *args[]) +{ + if (cuda_device_->have_error()) { + return false; + } + + debug_enqueue(kernel, work_size); + + const CUDAContextScope scope(cuda_device_); + const CUDADeviceKernel &cuda_kernel = cuda_device_->kernels.get(kernel); + + /* Compute kernel launch parameters. */ + const int num_threads_per_block = cuda_kernel.num_threads_per_block; + const int num_blocks = divide_up(work_size, num_threads_per_block); + + int shared_mem_bytes = 0; + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: + /* See parall_active_index.h for why this amount of shared memory is needed. */ + shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int); + break; + + default: + break; + } + + /* Launch kernel. */ + assert_success(cuLaunchKernel(cuda_kernel.function, + num_blocks, + 1, + 1, + num_threads_per_block, + 1, + 1, + shared_mem_bytes, + cuda_stream_, + args, + 0), + "enqueue"); + + return !(cuda_device_->have_error()); +} + +bool CUDADeviceQueue::synchronize() +{ + if (cuda_device_->have_error()) { + return false; + } + + const CUDAContextScope scope(cuda_device_); + assert_success(cuStreamSynchronize(cuda_stream_), "synchronize"); + + debug_synchronize(); + + return !(cuda_device_->have_error()); +} + +void CUDADeviceQueue::zero_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + cuda_device_->mem_alloc(mem); + } + + /* Zero memory on device. */ + assert(mem.device_pointer != 0); + + const CUDAContextScope scope(cuda_device_); + assert_success( + cuMemsetD8Async((CUdeviceptr)mem.device_pointer, 0, mem.memory_size(), cuda_stream_), + "zero_to_device"); +} + +void CUDADeviceQueue::copy_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + cuda_device_->mem_alloc(mem); + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory to device. */ + const CUDAContextScope scope(cuda_device_); + assert_success( + cuMemcpyHtoDAsync( + (CUdeviceptr)mem.device_pointer, mem.host_pointer, mem.memory_size(), cuda_stream_), + "copy_to_device"); +} + +void CUDADeviceQueue::copy_from_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory from device. */ + const CUDAContextScope scope(cuda_device_); + assert_success( + cuMemcpyDtoHAsync( + mem.host_pointer, (CUdeviceptr)mem.device_pointer, mem.memory_size(), cuda_stream_), + "copy_from_device"); +} + +void CUDADeviceQueue::assert_success(CUresult result, const char *operation) +{ + if (result != CUDA_SUCCESS) { + const char *name = cuewErrorString(result); + cuda_device_->set_error(string_printf( + "%s in CUDA queue %s (%s)", name, operation, debug_active_kernels().c_str())); + } +} + +unique_ptr CUDADeviceQueue::graphics_interop_create() +{ + return make_unique(this); +} + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/queue.h b/intern/cycles/device/cuda/queue.h new file mode 100644 index 00000000000..28613cda071 --- /dev/null +++ b/intern/cycles/device/cuda/queue.h @@ -0,0 +1,69 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_CUDA + +# include "device/kernel.h" +# include "device/memory.h" +# include "device/queue.h" + +# include "device/cuda/util.h" + +CCL_NAMESPACE_BEGIN + +class CUDADevice; +class device_memory; + +/* Base class for CUDA queues. */ +class CUDADeviceQueue : public DeviceQueue { + public: + CUDADeviceQueue(CUDADevice *device); + ~CUDADeviceQueue(); + + virtual int num_concurrent_states(const size_t state_size) const override; + virtual int num_concurrent_busy_states() const override; + + virtual void init_execution() override; + + virtual bool kernel_available(DeviceKernel kernel) const override; + + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) override; + + virtual bool synchronize() override; + + virtual void zero_to_device(device_memory &mem) override; + virtual void copy_to_device(device_memory &mem) override; + virtual void copy_from_device(device_memory &mem) override; + + virtual CUstream stream() + { + return cuda_stream_; + } + + virtual unique_ptr graphics_interop_create() override; + + protected: + CUDADevice *cuda_device_; + CUstream cuda_stream_; + + void assert_success(CUresult result, const char *operation); +}; + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/util.cpp b/intern/cycles/device/cuda/util.cpp new file mode 100644 index 00000000000..8f657cc10fe --- /dev/null +++ b/intern/cycles/device/cuda/util.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_CUDA + +# include "device/cuda/util.h" +# include "device/cuda/device_impl.h" + +CCL_NAMESPACE_BEGIN + +CUDAContextScope::CUDAContextScope(CUDADevice *device) : device(device) +{ + cuda_device_assert(device, cuCtxPushCurrent(device->cuContext)); +} + +CUDAContextScope::~CUDAContextScope() +{ + cuda_device_assert(device, cuCtxPopCurrent(NULL)); +} + +# ifndef WITH_CUDA_DYNLOAD +const char *cuewErrorString(CUresult result) +{ + /* We can only give error code here without major code duplication, that + * should be enough since dynamic loading is only being disabled by folks + * who knows what they're doing anyway. + * + * NOTE: Avoid call from several threads. + */ + static string error; + error = string_printf("%d", result); + return error.c_str(); +} + +const char *cuewCompilerPath() +{ + return CYCLES_CUDA_NVCC_EXECUTABLE; +} + +int cuewCompilerVersion() +{ + return (CUDA_VERSION / 100) + (CUDA_VERSION % 100 / 10); +} +# endif + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/cuda/util.h b/intern/cycles/device/cuda/util.h new file mode 100644 index 00000000000..a0898094c08 --- /dev/null +++ b/intern/cycles/device/cuda/util.h @@ -0,0 +1,65 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_CUDA + +# ifdef WITH_CUDA_DYNLOAD +# include "cuew.h" +# else +# include +# endif + +CCL_NAMESPACE_BEGIN + +class CUDADevice; + +/* Utility to push/pop CUDA context. */ +class CUDAContextScope { + public: + CUDAContextScope(CUDADevice *device); + ~CUDAContextScope(); + + private: + CUDADevice *device; +}; + +/* Utility for checking return values of CUDA function calls. */ +# define cuda_device_assert(cuda_device, stmt) \ + { \ + CUresult result = stmt; \ + if (result != CUDA_SUCCESS) { \ + const char *name = cuewErrorString(result); \ + cuda_device->set_error( \ + string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \ + } \ + } \ + (void)0 + +# define cuda_assert(stmt) cuda_device_assert(this, stmt) + +# ifndef WITH_CUDA_DYNLOAD +/* Transparently implement some functions, so majority of the file does not need + * to worry about difference between dynamically loaded and linked CUDA at all. */ +const char *cuewErrorString(CUresult result); +const char *cuewCompilerPath(); +int cuewCompilerVersion(); +# endif /* WITH_CUDA_DYNLOAD */ + +CCL_NAMESPACE_END + +#endif /* WITH_CUDA */ diff --git a/intern/cycles/device/denoise.cpp b/intern/cycles/device/denoise.cpp new file mode 100644 index 00000000000..c291a7a0adb --- /dev/null +++ b/intern/cycles/device/denoise.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/denoise.h" + +CCL_NAMESPACE_BEGIN + +const char *denoiserTypeToHumanReadable(DenoiserType type) +{ + switch (type) { + case DENOISER_OPTIX: + return "OptiX"; + case DENOISER_OPENIMAGEDENOISE: + return "OpenImageDenoise"; + + case DENOISER_NUM: + case DENOISER_NONE: + case DENOISER_ALL: + return "UNKNOWN"; + } + + return "UNKNOWN"; +} + +const NodeEnum *DenoiseParams::get_type_enum() +{ + static NodeEnum type_enum; + + if (type_enum.empty()) { + type_enum.insert("optix", DENOISER_OPTIX); + type_enum.insert("openimageio", DENOISER_OPENIMAGEDENOISE); + } + + return &type_enum; +} + +const NodeEnum *DenoiseParams::get_prefilter_enum() +{ + static NodeEnum prefilter_enum; + + if (prefilter_enum.empty()) { + prefilter_enum.insert("none", DENOISER_PREFILTER_NONE); + prefilter_enum.insert("fast", DENOISER_PREFILTER_FAST); + prefilter_enum.insert("accurate", DENOISER_PREFILTER_ACCURATE); + } + + return &prefilter_enum; +} + +NODE_DEFINE(DenoiseParams) +{ + NodeType *type = NodeType::add("denoise_params", create); + + const NodeEnum *type_enum = get_type_enum(); + const NodeEnum *prefilter_enum = get_prefilter_enum(); + + SOCKET_BOOLEAN(use, "Use", false); + + SOCKET_ENUM(type, "Type", *type_enum, DENOISER_OPENIMAGEDENOISE); + + SOCKET_INT(start_sample, "Start Sample", 0); + + SOCKET_BOOLEAN(use_pass_albedo, "Use Pass Albedo", true); + SOCKET_BOOLEAN(use_pass_normal, "Use Pass Normal", false); + + SOCKET_ENUM(prefilter, "Prefilter", *prefilter_enum, DENOISER_PREFILTER_FAST); + + return type; +} + +DenoiseParams::DenoiseParams() : Node(get_node_type()) +{ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/denoise.h b/intern/cycles/device/denoise.h new file mode 100644 index 00000000000..3f30506ae06 --- /dev/null +++ b/intern/cycles/device/denoise.h @@ -0,0 +1,110 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/memory.h" +#include "graph/node.h" +#include "session/buffers.h" + +CCL_NAMESPACE_BEGIN + +enum DenoiserType { + DENOISER_OPTIX = 2, + DENOISER_OPENIMAGEDENOISE = 4, + DENOISER_NUM, + + DENOISER_NONE = 0, + DENOISER_ALL = ~0, +}; + +/* COnstruct human-readable string which denotes the denoiser type. */ +const char *denoiserTypeToHumanReadable(DenoiserType type); + +typedef int DenoiserTypeMask; + +enum DenoiserPrefilter { + /* Best quality of the result without extra processing time, but requires guiding passes to be + * noise-free. */ + DENOISER_PREFILTER_NONE = 1, + + /* Denoise color and guiding passes together. + * Improves quality when guiding passes are noisy using least amount of extra processing time. */ + DENOISER_PREFILTER_FAST = 2, + + /* Prefilter noisy guiding passes before denoising color. + * Improves quality when guiding passes are noisy using extra processing time. */ + DENOISER_PREFILTER_ACCURATE = 3, + + DENOISER_PREFILTER_NUM, +}; + +/* NOTE: Is not a real scene node. Using Node API for ease of (de)serialization. + * The default values here do not really matter as they are always initialized from the + * Integrator node. */ +class DenoiseParams : public Node { + public: + NODE_DECLARE + + /* Apply denoiser to image. */ + bool use = false; + + /* Denoiser type. */ + DenoiserType type = DENOISER_OPENIMAGEDENOISE; + + /* Viewport start sample. */ + int start_sample = 0; + + /* Auxiliary passes. */ + bool use_pass_albedo = true; + bool use_pass_normal = true; + + DenoiserPrefilter prefilter = DENOISER_PREFILTER_FAST; + + static const NodeEnum *get_type_enum(); + static const NodeEnum *get_prefilter_enum(); + + DenoiseParams(); + + bool modified(const DenoiseParams &other) const + { + return !(use == other.use && type == other.type && start_sample == other.start_sample && + use_pass_albedo == other.use_pass_albedo && + use_pass_normal == other.use_pass_normal && prefilter == other.prefilter); + } +}; + +/* All the parameters needed to perform buffer denoising on a device. + * Is not really a task in its canonical terms (as in, is not an asynchronous running task). Is + * more like a wrapper for all the arguments and parameters needed to perform denoising. Is a + * single place where they are all listed, so that it's not required to modify all device methods + * when these parameters do change. */ +class DeviceDenoiseTask { + public: + DenoiseParams params; + + int num_samples; + + RenderBuffers *render_buffers; + BufferParams buffer_params; + + /* Allow to do in-place modification of the input passes (scaling them down i.e.). This will + * lower the memory footprint of the denoiser but will make input passes "invalid" (from path + * tracer) point of view. */ + bool allow_inplace_modification; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device.cpp b/intern/cycles/device/device.cpp index ed53fbb54ae..3756fc251c0 100644 --- a/intern/cycles/device/device.cpp +++ b/intern/cycles/device/device.cpp @@ -20,350 +20,40 @@ #include "bvh/bvh2.h" #include "device/device.h" -#include "device/device_intern.h" +#include "device/queue.h" -#include "util/util_foreach.h" -#include "util/util_half.h" -#include "util/util_logging.h" -#include "util/util_math.h" -#include "util/util_opengl.h" -#include "util/util_string.h" -#include "util/util_system.h" -#include "util/util_time.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "device/cpu/device.h" +#include "device/cuda/device.h" +#include "device/dummy/device.h" +#include "device/hip/device.h" +#include "device/multi/device.h" +#include "device/optix/device.h" + +#include "util/foreach.h" +#include "util/half.h" +#include "util/log.h" +#include "util/math.h" +#include "util/string.h" +#include "util/system.h" +#include "util/time.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN bool Device::need_types_update = true; bool Device::need_devices_update = true; thread_mutex Device::device_mutex; -vector Device::opencl_devices; vector Device::cuda_devices; vector Device::optix_devices; vector Device::cpu_devices; -vector Device::network_devices; +vector Device::hip_devices; uint Device::devices_initialized_mask = 0; -/* Device Requested Features */ - -std::ostream &operator<<(std::ostream &os, const DeviceRequestedFeatures &requested_features) -{ - os << "Experimental features: " << (requested_features.experimental ? "On" : "Off") << std::endl; - os << "Max nodes group: " << requested_features.max_nodes_group << std::endl; - /* TODO(sergey): Decode bitflag into list of names. */ - os << "Nodes features: " << requested_features.nodes_features << std::endl; - os << "Use Hair: " << string_from_bool(requested_features.use_hair) << std::endl; - os << "Use Object Motion: " << string_from_bool(requested_features.use_object_motion) - << std::endl; - os << "Use Camera Motion: " << string_from_bool(requested_features.use_camera_motion) - << std::endl; - os << "Use Baking: " << string_from_bool(requested_features.use_baking) << std::endl; - os << "Use Subsurface: " << string_from_bool(requested_features.use_subsurface) << std::endl; - os << "Use Volume: " << string_from_bool(requested_features.use_volume) << std::endl; - os << "Use Branched Integrator: " << string_from_bool(requested_features.use_integrator_branched) - << std::endl; - os << "Use Patch Evaluation: " << string_from_bool(requested_features.use_patch_evaluation) - << std::endl; - os << "Use Transparent Shadows: " << string_from_bool(requested_features.use_transparent) - << std::endl; - os << "Use Principled BSDF: " << string_from_bool(requested_features.use_principled) - << std::endl; - os << "Use Denoising: " << string_from_bool(requested_features.use_denoising) << std::endl; - os << "Use Displacement: " << string_from_bool(requested_features.use_true_displacement) - << std::endl; - os << "Use Background Light: " << string_from_bool(requested_features.use_background_light) - << std::endl; - return os; -} - /* Device */ Device::~Device() noexcept(false) { - if (!background) { - if (vertex_buffer != 0) { - glDeleteBuffers(1, &vertex_buffer); - } - if (fallback_shader_program != 0) { - glDeleteProgram(fallback_shader_program); - } - } -} - -/* TODO move shaders to standalone .glsl file. */ -const char *FALLBACK_VERTEX_SHADER = - "#version 330\n" - "uniform vec2 fullscreen;\n" - "in vec2 texCoord;\n" - "in vec2 pos;\n" - "out vec2 texCoord_interp;\n" - "\n" - "vec2 normalize_coordinates()\n" - "{\n" - " return (vec2(2.0) * (pos / fullscreen)) - vec2(1.0);\n" - "}\n" - "\n" - "void main()\n" - "{\n" - " gl_Position = vec4(normalize_coordinates(), 0.0, 1.0);\n" - " texCoord_interp = texCoord;\n" - "}\n\0"; - -const char *FALLBACK_FRAGMENT_SHADER = - "#version 330\n" - "uniform sampler2D image_texture;\n" - "in vec2 texCoord_interp;\n" - "out vec4 fragColor;\n" - "\n" - "void main()\n" - "{\n" - " fragColor = texture(image_texture, texCoord_interp);\n" - "}\n\0"; - -static void shader_print_errors(const char *task, const char *log, const char *code) -{ - LOG(ERROR) << "Shader: " << task << " error:"; - LOG(ERROR) << "===== shader string ===="; - - stringstream stream(code); - string partial; - - int line = 1; - while (getline(stream, partial, '\n')) { - if (line < 10) { - LOG(ERROR) << " " << line << " " << partial; - } - else { - LOG(ERROR) << line << " " << partial; - } - line++; - } - LOG(ERROR) << log; -} - -static int bind_fallback_shader(void) -{ - GLint status; - GLchar log[5000]; - GLsizei length = 0; - GLuint program = 0; - - struct Shader { - const char *source; - GLenum type; - } shaders[2] = {{FALLBACK_VERTEX_SHADER, GL_VERTEX_SHADER}, - {FALLBACK_FRAGMENT_SHADER, GL_FRAGMENT_SHADER}}; - - program = glCreateProgram(); - - for (int i = 0; i < 2; i++) { - GLuint shader = glCreateShader(shaders[i].type); - - string source_str = shaders[i].source; - const char *c_str = source_str.c_str(); - - glShaderSource(shader, 1, &c_str, NULL); - glCompileShader(shader); - - glGetShaderiv(shader, GL_COMPILE_STATUS, &status); - - if (!status) { - glGetShaderInfoLog(shader, sizeof(log), &length, log); - shader_print_errors("compile", log, c_str); - return 0; - } - - glAttachShader(program, shader); - } - - /* Link output. */ - glBindFragDataLocation(program, 0, "fragColor"); - - /* Link and error check. */ - glLinkProgram(program); - - glGetProgramiv(program, GL_LINK_STATUS, &status); - if (!status) { - glGetShaderInfoLog(program, sizeof(log), &length, log); - shader_print_errors("linking", log, FALLBACK_VERTEX_SHADER); - shader_print_errors("linking", log, FALLBACK_FRAGMENT_SHADER); - return 0; - } - - return program; -} - -bool Device::bind_fallback_display_space_shader(const float width, const float height) -{ - if (fallback_status == FALLBACK_SHADER_STATUS_ERROR) { - return false; - } - - if (fallback_status == FALLBACK_SHADER_STATUS_NONE) { - fallback_shader_program = bind_fallback_shader(); - fallback_status = FALLBACK_SHADER_STATUS_ERROR; - - if (fallback_shader_program == 0) { - return false; - } - - glUseProgram(fallback_shader_program); - image_texture_location = glGetUniformLocation(fallback_shader_program, "image_texture"); - if (image_texture_location < 0) { - LOG(ERROR) << "Shader doesn't contain the 'image_texture' uniform."; - return false; - } - - fullscreen_location = glGetUniformLocation(fallback_shader_program, "fullscreen"); - if (fullscreen_location < 0) { - LOG(ERROR) << "Shader doesn't contain the 'fullscreen' uniform."; - return false; - } - - fallback_status = FALLBACK_SHADER_STATUS_SUCCESS; - } - - /* Run this every time. */ - glUseProgram(fallback_shader_program); - glUniform1i(image_texture_location, 0); - glUniform2f(fullscreen_location, width, height); - return true; -} - -void Device::draw_pixels(device_memory &rgba, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) -{ - const bool use_fallback_shader = (draw_params.bind_display_space_shader_cb == NULL); - - assert(rgba.type == MEM_PIXELS); - mem_copy_from(rgba, y, w, h, rgba.memory_elements_size(1)); - - GLuint texid; - glActiveTexture(GL_TEXTURE0); - glGenTextures(1, &texid); - glBindTexture(GL_TEXTURE_2D, texid); - - if (rgba.data_type == TYPE_HALF) { - GLhalf *data_pointer = (GLhalf *)rgba.host_pointer; - data_pointer += 4 * y * w; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, w, h, 0, GL_RGBA, GL_HALF_FLOAT, data_pointer); - } - else { - uint8_t *data_pointer = (uint8_t *)rgba.host_pointer; - data_pointer += 4 * y * w; - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data_pointer); - } - - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - - if (transparent) { - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); - } - - GLint shader_program; - if (use_fallback_shader) { - if (!bind_fallback_display_space_shader(dw, dh)) { - return; - } - shader_program = fallback_shader_program; - } - else { - draw_params.bind_display_space_shader_cb(); - glGetIntegerv(GL_CURRENT_PROGRAM, &shader_program); - } - - if (!vertex_buffer) { - glGenBuffers(1, &vertex_buffer); - } - - glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); - /* invalidate old contents - avoids stalling if buffer is still waiting in queue to be rendered - */ - glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(float), NULL, GL_STREAM_DRAW); - - float *vpointer = (float *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY); - - if (vpointer) { - /* texture coordinate - vertex pair */ - vpointer[0] = 0.0f; - vpointer[1] = 0.0f; - vpointer[2] = dx; - vpointer[3] = dy; - - vpointer[4] = 1.0f; - vpointer[5] = 0.0f; - vpointer[6] = (float)width + dx; - vpointer[7] = dy; - - vpointer[8] = 1.0f; - vpointer[9] = 1.0f; - vpointer[10] = (float)width + dx; - vpointer[11] = (float)height + dy; - - vpointer[12] = 0.0f; - vpointer[13] = 1.0f; - vpointer[14] = dx; - vpointer[15] = (float)height + dy; - - if (vertex_buffer) { - glUnmapBuffer(GL_ARRAY_BUFFER); - } - } - - GLuint vertex_array_object; - GLuint position_attribute, texcoord_attribute; - - glGenVertexArrays(1, &vertex_array_object); - glBindVertexArray(vertex_array_object); - - texcoord_attribute = glGetAttribLocation(shader_program, "texCoord"); - position_attribute = glGetAttribLocation(shader_program, "pos"); - - glEnableVertexAttribArray(texcoord_attribute); - glEnableVertexAttribArray(position_attribute); - - glVertexAttribPointer( - texcoord_attribute, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (const GLvoid *)0); - glVertexAttribPointer(position_attribute, - 2, - GL_FLOAT, - GL_FALSE, - 4 * sizeof(float), - (const GLvoid *)(sizeof(float) * 2)); - - glDrawArrays(GL_TRIANGLE_FAN, 0, 4); - - if (vertex_buffer) { - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - - if (use_fallback_shader) { - glUseProgram(0); - } - else { - draw_params.unbind_display_space_shader_cb(); - } - - glDeleteVertexArrays(1, &vertex_array_object); - glBindTexture(GL_TEXTURE_2D, 0); - glDeleteTextures(1, &texid); - - if (transparent) { - glDisable(GL_BLEND); - } } void Device::build_bvh(BVH *bvh, Progress &progress, bool refit) @@ -379,52 +69,47 @@ void Device::build_bvh(BVH *bvh, Progress &progress, bool refit) } } -Device *Device::create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) +Device *Device::create(const DeviceInfo &info, Stats &stats, Profiler &profiler) { -#ifdef WITH_MULTI if (!info.multi_devices.empty()) { /* Always create a multi device when info contains multiple devices. * This is done so that the type can still be e.g. DEVICE_CPU to indicate * that it is a homogeneous collection of devices, which simplifies checks. */ - return device_multi_create(info, stats, profiler, background); + return device_multi_create(info, stats, profiler); } -#endif Device *device = NULL; switch (info.type) { case DEVICE_CPU: - device = device_cpu_create(info, stats, profiler, background); + device = device_cpu_create(info, stats, profiler); break; #ifdef WITH_CUDA case DEVICE_CUDA: if (device_cuda_init()) - device = device_cuda_create(info, stats, profiler, background); + device = device_cuda_create(info, stats, profiler); break; #endif #ifdef WITH_OPTIX case DEVICE_OPTIX: if (device_optix_init()) - device = device_optix_create(info, stats, profiler, background); + device = device_optix_create(info, stats, profiler); break; #endif -#ifdef WITH_NETWORK - case DEVICE_NETWORK: - device = device_network_create(info, stats, profiler, "127.0.0.1"); - break; -#endif -#ifdef WITH_OPENCL - case DEVICE_OPENCL: - if (device_opencl_init()) - device = device_opencl_create(info, stats, profiler, background); + +#ifdef WITH_HIP + case DEVICE_HIP: + if (device_hip_init()) + device = device_hip_create(info, stats, profiler); break; #endif + default: break; } if (device == NULL) { - device = device_dummy_create(info, stats, profiler, background); + device = device_dummy_create(info, stats, profiler); } return device; @@ -438,12 +123,10 @@ DeviceType Device::type_from_string(const char *name) return DEVICE_CUDA; else if (strcmp(name, "OPTIX") == 0) return DEVICE_OPTIX; - else if (strcmp(name, "OPENCL") == 0) - return DEVICE_OPENCL; - else if (strcmp(name, "NETWORK") == 0) - return DEVICE_NETWORK; else if (strcmp(name, "MULTI") == 0) return DEVICE_MULTI; + else if (strcmp(name, "HIP") == 0) + return DEVICE_HIP; return DEVICE_NONE; } @@ -456,12 +139,10 @@ string Device::string_from_type(DeviceType type) return "CUDA"; else if (type == DEVICE_OPTIX) return "OPTIX"; - else if (type == DEVICE_OPENCL) - return "OPENCL"; - else if (type == DEVICE_NETWORK) - return "NETWORK"; else if (type == DEVICE_MULTI) return "MULTI"; + else if (type == DEVICE_HIP) + return "HIP"; return ""; } @@ -476,12 +157,10 @@ vector Device::available_types() #ifdef WITH_OPTIX types.push_back(DEVICE_OPTIX); #endif -#ifdef WITH_OPENCL - types.push_back(DEVICE_OPENCL); -#endif -#ifdef WITH_NETWORK - types.push_back(DEVICE_NETWORK); +#ifdef WITH_HIP + types.push_back(DEVICE_HIP); #endif + return types; } @@ -493,20 +172,6 @@ vector Device::available_devices(uint mask) thread_scoped_lock lock(device_mutex); vector devices; -#ifdef WITH_OPENCL - if (mask & DEVICE_MASK_OPENCL) { - if (!(devices_initialized_mask & DEVICE_MASK_OPENCL)) { - if (device_opencl_init()) { - device_opencl_info(opencl_devices); - } - devices_initialized_mask |= DEVICE_MASK_OPENCL; - } - foreach (DeviceInfo &info, opencl_devices) { - devices.push_back(info); - } - } -#endif - #if defined(WITH_CUDA) || defined(WITH_OPTIX) if (mask & (DEVICE_MASK_CUDA | DEVICE_MASK_OPTIX)) { if (!(devices_initialized_mask & DEVICE_MASK_CUDA)) { @@ -537,6 +202,20 @@ vector Device::available_devices(uint mask) } #endif +#ifdef WITH_HIP + if (mask & DEVICE_MASK_HIP) { + if (!(devices_initialized_mask & DEVICE_MASK_HIP)) { + if (device_hip_init()) { + device_hip_info(hip_devices); + } + devices_initialized_mask |= DEVICE_MASK_HIP; + } + foreach (DeviceInfo &info, hip_devices) { + devices.push_back(info); + } + } +#endif + if (mask & DEVICE_MASK_CPU) { if (!(devices_initialized_mask & DEVICE_MASK_CPU)) { device_cpu_info(cpu_devices); @@ -547,18 +226,6 @@ vector Device::available_devices(uint mask) } } -#ifdef WITH_NETWORK - if (mask & DEVICE_MASK_NETWORK) { - if (!(devices_initialized_mask & DEVICE_MASK_NETWORK)) { - device_network_info(network_devices); - devices_initialized_mask |= DEVICE_MASK_NETWORK; - } - foreach (DeviceInfo &info, network_devices) { - devices.push_back(info); - } - } -#endif - return devices; } @@ -580,15 +247,6 @@ string Device::device_capabilities(uint mask) capabilities += device_cpu_capabilities() + "\n"; } -#ifdef WITH_OPENCL - if (mask & DEVICE_MASK_OPENCL) { - if (device_opencl_init()) { - capabilities += "\nOpenCL device capabilities:\n"; - capabilities += device_opencl_capabilities(); - } - } -#endif - #ifdef WITH_CUDA if (mask & DEVICE_MASK_CUDA) { if (device_cuda_init()) { @@ -598,6 +256,15 @@ string Device::device_capabilities(uint mask) } #endif +#ifdef WITH_HIP + if (mask & DEVICE_MASK_HIP) { + if (device_hip_init()) { + capabilities += "\nHIP device capabilities:\n"; + capabilities += device_hip_capabilities(); + } + } +#endif + return capabilities; } @@ -613,16 +280,12 @@ DeviceInfo Device::get_multi_device(const vector &subdevices, } DeviceInfo info; - info.type = subdevices.front().type; + info.type = DEVICE_NONE; info.id = "MULTI"; info.description = "Multi Device"; 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; info.has_osl = true; info.has_profiling = true; info.has_peer_memory = false; @@ -660,16 +323,15 @@ DeviceInfo Device::get_multi_device(const vector &subdevices, info.id += device.id; /* Set device type to MULTI if subdevices are not of a common type. */ - if (device.type != info.type) { + if (info.type == DEVICE_NONE) { + info.type = device.type; + } + else if (device.type != info.type) { info.type = DEVICE_MULTI; } /* 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; info.has_osl &= device.has_osl; info.has_profiling &= device.has_profiling; info.has_peer_memory |= device.has_peer_memory; @@ -689,60 +351,33 @@ void Device::free_memory() devices_initialized_mask = 0; cuda_devices.free_memory(); optix_devices.free_memory(); - opencl_devices.free_memory(); + hip_devices.free_memory(); cpu_devices.free_memory(); - network_devices.free_memory(); +} + +unique_ptr Device::gpu_queue_create() +{ + LOG(FATAL) << "Device does not support queues."; + return nullptr; +} + +const CPUKernels *Device::get_cpu_kernels() const +{ + LOG(FATAL) << "Device does not support CPU kernels."; + return nullptr; +} + +void Device::get_cpu_kernel_thread_globals( + vector & /*kernel_thread_globals*/) +{ + LOG(FATAL) << "Device does not support CPU kernels."; +} + +void *Device::get_cpu_osl_memory() +{ + return nullptr; } /* DeviceInfo */ -void DeviceInfo::add_denoising_devices(DenoiserType denoiser_type) -{ - assert(denoising_devices.empty()); - - if (denoiser_type == DENOISER_OPTIX && type != DEVICE_OPTIX) { - vector optix_devices = Device::available_devices(DEVICE_MASK_OPTIX); - if (!optix_devices.empty()) { - /* Convert to a special multi device with separate denoising devices. */ - if (multi_devices.empty()) { - multi_devices.push_back(*this); - } - - /* Try to use the same physical devices for denoising. */ - for (const DeviceInfo &cuda_device : multi_devices) { - if (cuda_device.type == DEVICE_CUDA) { - for (const DeviceInfo &optix_device : optix_devices) { - if (cuda_device.num == optix_device.num) { - id += optix_device.id; - denoising_devices.push_back(optix_device); - break; - } - } - } - } - - if (denoising_devices.empty()) { - /* Simply use the first available OptiX device. */ - const DeviceInfo optix_device = optix_devices.front(); - id += optix_device.id; /* Uniquely identify this special multi device. */ - denoising_devices.push_back(optix_device); - } - - denoisers = denoiser_type; - } - } - else if (denoiser_type == DENOISER_OPENIMAGEDENOISE && type != DEVICE_CPU) { - /* Convert to a special multi device with separate denoising devices. */ - if (multi_devices.empty()) { - multi_devices.push_back(*this); - } - - /* Add CPU denoising devices. */ - DeviceInfo cpu_device = Device::available_devices(DEVICE_MASK_CPU).front(); - denoising_devices.push_back(cpu_device); - - denoisers = denoiser_type; - } -} - CCL_NAMESPACE_END diff --git a/intern/cycles/device/device.h b/intern/cycles/device/device.h index ecf79bcdfa6..fdd78b3aa4c 100644 --- a/intern/cycles/device/device.h +++ b/intern/cycles/device/device.h @@ -19,54 +19,50 @@ #include -#include "bvh/bvh_params.h" +#include "bvh/params.h" -#include "device/device_memory.h" -#include "device/device_task.h" +#include "device/denoise.h" +#include "device/memory.h" -#include "util/util_list.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_texture.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/function.h" +#include "util/list.h" +#include "util/log.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/texture.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/unique_ptr.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN class BVH; +class DeviceQueue; class Progress; -class RenderTile; +class CPUKernels; +class CPUKernelThreadGlobals; /* Device Types */ enum DeviceType { DEVICE_NONE = 0, DEVICE_CPU, - DEVICE_OPENCL, DEVICE_CUDA, - DEVICE_NETWORK, DEVICE_MULTI, DEVICE_OPTIX, + DEVICE_HIP, DEVICE_DUMMY, }; enum DeviceTypeMask { DEVICE_MASK_CPU = (1 << DEVICE_CPU), - DEVICE_MASK_OPENCL = (1 << DEVICE_OPENCL), DEVICE_MASK_CUDA = (1 << DEVICE_CUDA), DEVICE_MASK_OPTIX = (1 << DEVICE_OPTIX), - DEVICE_MASK_NETWORK = (1 << DEVICE_NETWORK), + DEVICE_MASK_HIP = (1 << DEVICE_HIP), DEVICE_MASK_ALL = ~0 }; -enum DeviceKernelStatus { - DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE, - DEVICE_KERNEL_USING_FEATURE_KERNEL, - DEVICE_KERNEL_FEATURE_KERNEL_INVALID, - DEVICE_KERNEL_UNKNOWN, -}; - #define DEVICE_MASK(type) (DeviceTypeMask)(1 << type) class DeviceInfo { @@ -75,20 +71,15 @@ class DeviceInfo { string description; string id; /* used for user preferences, should stay fixed with changing hardware config */ int num; - bool display_device; /* GPU is used as a display device. */ - bool has_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. */ - bool has_osl; /* Support Open Shading Language. */ - bool use_split_kernel; /* Use split or mega kernel. */ - bool has_profiling; /* Supports runtime collection of profiling info. */ - bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */ - DenoiserTypeMask denoisers; /* Supported denoiser types. */ + bool display_device; /* GPU is used as a display device. */ + bool has_nanovdb; /* Support NanoVDB volumes. */ + bool has_osl; /* Support Open Shading Language. */ + bool has_profiling; /* Supports runtime collection of profiling info. */ + bool has_peer_memory; /* GPU has P2P access to memory of another GPU. */ + bool has_gpu_queue; /* Device supports GPU queue. */ + DenoiserTypeMask denoisers; /* Supported denoiser types. */ int cpu_threads; vector multi_devices; - vector denoising_devices; string error_msg; DeviceInfo() @@ -98,230 +89,37 @@ class DeviceInfo { num = 0; 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; has_osl = false; - use_split_kernel = false; has_profiling = false; has_peer_memory = false; + has_gpu_queue = false; denoisers = DENOISER_NONE; } - bool operator==(const DeviceInfo &info) + bool operator==(const DeviceInfo &info) const { /* Multiple Devices with the same ID would be very bad. */ assert(id != info.id || (type == info.type && num == info.num && description == info.description)); return id == info.id; } - - /* Add additional devices needed for the specified denoiser. */ - void add_denoising_devices(DenoiserType denoiser_type); }; -class DeviceRequestedFeatures { - public: - /* Use experimental feature set. */ - bool experimental; - - /* Selective nodes compilation. */ - - /* Identifier of a node group up to which all the nodes needs to be - * compiled in. Nodes from higher group indices will be ignores. - */ - int max_nodes_group; - - /* Features bitfield indicating which features from the requested group - * will be compiled in. Nodes which corresponds to features which are not - * in this bitfield will be ignored even if they're in the requested group. - */ - int nodes_features; - - /* BVH/sampling kernel features. */ - bool use_hair; - bool use_hair_thick; - bool use_object_motion; - bool use_camera_motion; - - /* Denotes whether baking functionality is needed. */ - bool use_baking; - - /* Use subsurface scattering materials. */ - bool use_subsurface; - - /* Use volume materials. */ - bool use_volume; - - /* Use branched integrator. */ - bool use_integrator_branched; - - /* Use OpenSubdiv patch evaluation */ - bool use_patch_evaluation; - - /* Use Transparent shadows */ - bool use_transparent; - - /* Use various shadow tricks, such as shadow catcher. */ - bool use_shadow_tricks; - - /* Per-uber shader usage flags. */ - bool use_principled; - - /* Denoising features. */ - bool use_denoising; - - /* Use raytracing in shaders. */ - bool use_shader_raytrace; - - /* Use true displacement */ - bool use_true_displacement; - - /* Use background lights */ - bool use_background_light; - - DeviceRequestedFeatures() - { - /* TODO(sergey): Find more meaningful defaults. */ - max_nodes_group = 0; - nodes_features = 0; - use_hair = false; - use_hair_thick = false; - use_object_motion = false; - use_camera_motion = false; - use_baking = false; - use_subsurface = false; - use_volume = false; - use_integrator_branched = false; - use_patch_evaluation = false; - use_transparent = false; - use_shadow_tricks = false; - use_principled = false; - use_denoising = false; - use_shader_raytrace = false; - use_true_displacement = false; - use_background_light = false; - } - - bool modified(const DeviceRequestedFeatures &requested_features) - { - return !(max_nodes_group == requested_features.max_nodes_group && - nodes_features == requested_features.nodes_features && - use_hair == requested_features.use_hair && - use_hair_thick == requested_features.use_hair_thick && - use_object_motion == requested_features.use_object_motion && - use_camera_motion == requested_features.use_camera_motion && - use_baking == requested_features.use_baking && - use_subsurface == requested_features.use_subsurface && - use_volume == requested_features.use_volume && - use_integrator_branched == requested_features.use_integrator_branched && - use_patch_evaluation == requested_features.use_patch_evaluation && - use_transparent == requested_features.use_transparent && - use_shadow_tricks == requested_features.use_shadow_tricks && - use_principled == requested_features.use_principled && - use_denoising == requested_features.use_denoising && - use_shader_raytrace == requested_features.use_shader_raytrace && - use_true_displacement == requested_features.use_true_displacement && - use_background_light == requested_features.use_background_light); - } - - /* Convert the requested features structure to a build options, - * which could then be passed to compilers. - */ - string get_build_options() const - { - string build_options = ""; - if (experimental) { - build_options += "-D__KERNEL_EXPERIMENTAL__ "; - } - build_options += "-D__NODES_MAX_GROUP__=" + string_printf("%d", max_nodes_group); - build_options += " -D__NODES_FEATURES__=" + string_printf("%d", nodes_features); - if (!use_hair) { - build_options += " -D__NO_HAIR__"; - } - if (!use_object_motion) { - build_options += " -D__NO_OBJECT_MOTION__"; - } - if (!use_camera_motion) { - build_options += " -D__NO_CAMERA_MOTION__"; - } - if (!use_baking) { - build_options += " -D__NO_BAKING__"; - } - if (!use_volume) { - build_options += " -D__NO_VOLUME__"; - } - if (!use_subsurface) { - build_options += " -D__NO_SUBSURFACE__"; - } - if (!use_integrator_branched) { - build_options += " -D__NO_BRANCHED_PATH__"; - } - if (!use_patch_evaluation) { - build_options += " -D__NO_PATCH_EVAL__"; - } - if (!use_transparent && !use_volume) { - build_options += " -D__NO_TRANSPARENT__"; - } - if (!use_shadow_tricks) { - build_options += " -D__NO_SHADOW_TRICKS__"; - } - if (!use_principled) { - build_options += " -D__NO_PRINCIPLED__"; - } - if (!use_denoising) { - build_options += " -D__NO_DENOISING__"; - } - if (!use_shader_raytrace) { - build_options += " -D__NO_SHADER_RAYTRACE__"; - } - return build_options; - } -}; - -std::ostream &operator<<(std::ostream &os, const DeviceRequestedFeatures &requested_features); - /* Device */ -struct DeviceDrawParams { - function bind_display_space_shader_cb; - function unbind_display_space_shader_cb; -}; - class Device { friend class device_sub_ptr; protected: - enum { - FALLBACK_SHADER_STATUS_NONE = 0, - FALLBACK_SHADER_STATUS_ERROR, - FALLBACK_SHADER_STATUS_SUCCESS, - }; - - Device(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background) - : background(background), - vertex_buffer(0), - fallback_status(FALLBACK_SHADER_STATUS_NONE), - fallback_shader_program(0), - info(info_), - stats(stats_), - profiler(profiler_) + Device(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_) + : info(info_), stats(stats_), profiler(profiler_) { } - bool background; string error_msg; - /* used for real time display */ - unsigned int vertex_buffer; - int fallback_status, fallback_shader_program; - int image_texture_location, fullscreen_location; - - bool bind_fallback_display_space_shader(const float width, const float height); - - virtual device_ptr mem_alloc_sub_ptr(device_memory & /*mem*/, int /*offset*/, int /*size*/) + virtual device_ptr mem_alloc_sub_ptr(device_memory & /*mem*/, size_t /*offset*/, size_t /*size*/) { /* Only required for devices that implement denoising. */ assert(false); @@ -361,67 +159,31 @@ class Device { Stats &stats; Profiler &profiler; - /* memory alignment */ - virtual int mem_sub_ptr_alignment() - { - return MIN_ALIGNMENT_CPU_DATA_TYPES; - } - /* constant memory */ virtual void const_copy_to(const char *name, void *host, size_t size) = 0; - /* open shading language, only for CPU device */ - virtual void *osl_memory() - { - return NULL; - } - /* load/compile kernels, must be called before adding tasks */ - virtual bool load_kernels(const DeviceRequestedFeatures & /*requested_features*/) + virtual bool load_kernels(uint /*kernel_features*/) { return true; } - /* Wait for device to become available to upload data and receive tasks - * This method is used by the OpenCL device to load the - * optimized kernels or when not (yet) available load the - * generic kernels (only during foreground rendering) */ - virtual bool wait_for_availability(const DeviceRequestedFeatures & /*requested_features*/) - { - return true; - } - /* Check if there are 'better' kernels available to be used - * We can switch over to these kernels - * This method is used to determine if we can switch the preview kernels - * to regular kernels */ - virtual DeviceKernelStatus get_active_kernel_switch_state() - { - return DEVICE_KERNEL_USING_FEATURE_KERNEL; - } + /* GPU device only functions. + * These may not be used on CPU or multi-devices. */ - /* tasks */ - virtual int get_split_task_count(DeviceTask &) - { - return 1; - } + /* Create new queue for executing kernels in. */ + virtual unique_ptr gpu_queue_create(); - virtual void task_add(DeviceTask &task) = 0; - virtual void task_wait() = 0; - virtual void task_cancel() = 0; + /* CPU device only functions. + * These may not be used on GPU or multi-devices. */ - /* opengl drawing */ - virtual void draw_pixels(device_memory &mem, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params); + /* Get CPU kernel functions for native instruction set. */ + virtual const CPUKernels *get_cpu_kernels() const; + /* Get kernel globals to pass to kernels. */ + virtual void get_cpu_kernel_thread_globals( + vector & /*kernel_thread_globals*/); + /* Get OpenShadingLanguage memory buffer. */ + virtual void *get_cpu_osl_memory(); /* acceleration structure building */ virtual void build_bvh(BVH *bvh, Progress &progress, bool refit); @@ -429,25 +191,11 @@ class Device { /* OptiX specific destructor. */ virtual void release_optix_bvh(BVH * /*bvh*/){}; -#ifdef WITH_NETWORK - /* networking */ - void server_run(); -#endif - /* multi device */ - virtual void map_tile(Device * /*sub_device*/, RenderTile & /*tile*/) - { - } virtual int device_number(Device * /*sub_device*/) { return 0; } - virtual void map_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/) - { - } - virtual void unmap_neighbor_tiles(Device * /*sub_device*/, RenderTileNeighbors & /*neighbors*/) - { - } virtual bool is_resident(device_ptr /*key*/, Device *sub_device) { @@ -460,11 +208,47 @@ class Device { return false; } + /* Graphics resources interoperability. + * + * The interoperability comes here by the meaning that the device is capable of computing result + * directly into an OpenGL (or other graphics library) buffer. */ + + /* Check display is to be updated using graphics interoperability. + * The interoperability can not be used is it is not supported by the device. But the device + * might also force disable the interoperability if it detects that it will be slower than + * copying pixels from the render buffer. */ + virtual bool should_use_graphics_interop() + { + return false; + } + + /* Buffer denoising. */ + + /* Returns true if task is fully handled. */ + virtual bool denoise_buffer(const DeviceDenoiseTask & /*task*/) + { + LOG(ERROR) << "Request buffer denoising from a device which does not support it."; + return false; + } + + virtual DeviceQueue *get_denoise_queue() + { + LOG(ERROR) << "Request denoising queue from a device which does not support it."; + return nullptr; + } + + /* Sub-devices */ + + /* Run given callback for every individual device which will be handling rendering. + * For the single device the callback is called for the device itself. For the multi-device the + * callback is only called for the sub-devices. */ + virtual void foreach_device(const function &callback) + { + callback(this); + } + /* static */ - static Device *create(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - bool background = true); + static Device *create(const DeviceInfo &info, Stats &stats, Profiler &profiler); static DeviceType type_from_string(const char *name); static string string_from_type(DeviceType type); @@ -489,7 +273,7 @@ class Device { virtual void mem_alloc(device_memory &mem) = 0; virtual void mem_copy_to(device_memory &mem) = 0; - virtual void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) = 0; + virtual void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) = 0; virtual void mem_zero(device_memory &mem) = 0; virtual void mem_free(device_memory &mem) = 0; @@ -499,9 +283,8 @@ class Device { static thread_mutex device_mutex; static vector cuda_devices; static vector optix_devices; - static vector opencl_devices; static vector cpu_devices; - static vector network_devices; + static vector hip_devices; static uint devices_initialized_mask; }; diff --git a/intern/cycles/device/device_cpu.cpp b/intern/cycles/device/device_cpu.cpp deleted file mode 100644 index 4a6e77d6eaa..00000000000 --- a/intern/cycles/device/device_cpu.cpp +++ /dev/null @@ -1,1680 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -/* So ImathMath is included before our kernel_cpu_compat. */ -#ifdef WITH_OSL -/* So no context pollution happens from indirectly included windows.h */ -# include "util/util_windows.h" -# include -#endif - -#ifdef WITH_EMBREE -# include -#endif - -#include "device/device.h" -#include "device/device_denoising.h" -#include "device/device_intern.h" -#include "device/device_split_kernel.h" - -// clang-format off -#include "kernel/kernel.h" -#include "kernel/kernel_compat_cpu.h" -#include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_adaptive_sampling.h" - -#include "kernel/filter/filter.h" - -#include "kernel/osl/osl_shader.h" -#include "kernel/osl/osl_globals.h" -// clang-format on - -#include "bvh/bvh_embree.h" - -#include "render/buffers.h" -#include "render/coverage.h" - -#include "util/util_debug.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_opengl.h" -#include "util/util_openimagedenoise.h" -#include "util/util_optimization.h" -#include "util/util_progress.h" -#include "util/util_system.h" -#include "util/util_task.h" -#include "util/util_thread.h" - -CCL_NAMESPACE_BEGIN - -class CPUDevice; - -/* Has to be outside of the class to be shared across template instantiations. */ -static const char *logged_architecture = ""; - -template class KernelFunctions { - public: - KernelFunctions() - { - kernel = (F)NULL; - } - - KernelFunctions( - F kernel_default, F kernel_sse2, F kernel_sse3, F kernel_sse41, F kernel_avx, F kernel_avx2) - { - const char *architecture_name = "default"; - kernel = kernel_default; - - /* Silence potential warnings about unused variables - * when compiling without some architectures. */ - (void)kernel_sse2; - (void)kernel_sse3; - (void)kernel_sse41; - (void)kernel_avx; - (void)kernel_avx2; -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 - if (DebugFlags().cpu.has_avx2() && system_cpu_support_avx2()) { - architecture_name = "AVX2"; - kernel = kernel_avx2; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_AVX - if (DebugFlags().cpu.has_avx() && system_cpu_support_avx()) { - architecture_name = "AVX"; - kernel = kernel_avx; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 - if (DebugFlags().cpu.has_sse41() && system_cpu_support_sse41()) { - architecture_name = "SSE4.1"; - kernel = kernel_sse41; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 - if (DebugFlags().cpu.has_sse3() && system_cpu_support_sse3()) { - architecture_name = "SSE3"; - kernel = kernel_sse3; - } - else -#endif -#ifdef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 - if (DebugFlags().cpu.has_sse2() && system_cpu_support_sse2()) { - architecture_name = "SSE2"; - kernel = kernel_sse2; - } -#else - { - /* Dummy to prevent the architecture if below become - * conditional when WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 - * is not defined. */ - } -#endif - - if (strcmp(architecture_name, logged_architecture) != 0) { - VLOG(1) << "Will be using " << architecture_name << " kernels."; - logged_architecture = architecture_name; - } - } - - inline F operator()() const - { - assert(kernel); - return kernel; - } - - protected: - F kernel; -}; - -class CPUSplitKernel : public DeviceSplitKernel { - CPUDevice *device; - - public: - explicit CPUSplitKernel(CPUDevice *device); - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data_, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs); - - virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &); - virtual int2 split_kernel_local_size(); - virtual int2 split_kernel_global_size(device_memory &kg, device_memory &data, DeviceTask &task); - virtual uint64_t state_buffer_size(device_memory &kg, device_memory &data, size_t num_threads); -}; - -class CPUDevice : public Device { - public: - TaskPool task_pool; - KernelGlobals kernel_globals; - - device_vector texture_info; - bool need_texture_info; - -#ifdef WITH_OSL - OSLGlobals osl_globals; -#endif -#ifdef WITH_OPENIMAGEDENOISE - oidn::DeviceRef oidn_device; - oidn::FilterRef oidn_filter; -#endif - thread_spin_lock oidn_task_lock; -#ifdef WITH_EMBREE - RTCScene embree_scene = NULL; - RTCDevice embree_device; -#endif - - bool use_split_kernel; - - DeviceRequestedFeatures requested_features; - - KernelFunctions path_trace_kernel; - KernelFunctions - convert_to_half_float_kernel; - KernelFunctions - convert_to_byte_kernel; - KernelFunctions - shader_kernel; - KernelFunctions bake_kernel; - - KernelFunctions - filter_divide_shadow_kernel; - KernelFunctions - filter_get_feature_kernel; - KernelFunctions - filter_write_feature_kernel; - KernelFunctions - filter_detect_outliers_kernel; - KernelFunctions - filter_combine_halves_kernel; - - KernelFunctions - filter_nlm_calc_difference_kernel; - KernelFunctions filter_nlm_blur_kernel; - KernelFunctions filter_nlm_calc_weight_kernel; - KernelFunctions - filter_nlm_update_output_kernel; - KernelFunctions filter_nlm_normalize_kernel; - - KernelFunctions - filter_construct_transform_kernel; - KernelFunctions - filter_nlm_construct_gramian_kernel; - KernelFunctions - filter_finalize_kernel; - - KernelFunctions - data_init_kernel; - unordered_map> split_kernels; - -#define KERNEL_FUNCTIONS(name) \ - KERNEL_NAME_EVAL(cpu, name), KERNEL_NAME_EVAL(cpu_sse2, name), \ - KERNEL_NAME_EVAL(cpu_sse3, name), KERNEL_NAME_EVAL(cpu_sse41, name), \ - KERNEL_NAME_EVAL(cpu_avx, name), KERNEL_NAME_EVAL(cpu_avx2, name) - - CPUDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_) - : Device(info_, stats_, profiler_, background_), - texture_info(this, "__texture_info", MEM_GLOBAL), -#define REGISTER_KERNEL(name) name##_kernel(KERNEL_FUNCTIONS(name)) - REGISTER_KERNEL(path_trace), - REGISTER_KERNEL(convert_to_half_float), - REGISTER_KERNEL(convert_to_byte), - REGISTER_KERNEL(shader), - REGISTER_KERNEL(bake), - REGISTER_KERNEL(filter_divide_shadow), - REGISTER_KERNEL(filter_get_feature), - REGISTER_KERNEL(filter_write_feature), - REGISTER_KERNEL(filter_detect_outliers), - REGISTER_KERNEL(filter_combine_halves), - REGISTER_KERNEL(filter_nlm_calc_difference), - REGISTER_KERNEL(filter_nlm_blur), - REGISTER_KERNEL(filter_nlm_calc_weight), - REGISTER_KERNEL(filter_nlm_update_output), - REGISTER_KERNEL(filter_nlm_normalize), - REGISTER_KERNEL(filter_construct_transform), - REGISTER_KERNEL(filter_nlm_construct_gramian), - REGISTER_KERNEL(filter_finalize), - REGISTER_KERNEL(data_init) -#undef REGISTER_KERNEL - { - if (info.cpu_threads == 0) { - info.cpu_threads = TaskScheduler::num_threads(); - } - -#ifdef WITH_OSL - kernel_globals.osl = &osl_globals; -#endif -#ifdef WITH_EMBREE - embree_device = rtcNewDevice("verbose=0"); -#endif - use_split_kernel = DebugFlags().cpu.split_kernel; - if (use_split_kernel) { - VLOG(1) << "Will be using split kernel."; - } - need_texture_info = false; - -#define REGISTER_SPLIT_KERNEL(name) \ - split_kernels[#name] = KernelFunctions( \ - KERNEL_FUNCTIONS(name)) - REGISTER_SPLIT_KERNEL(path_init); - REGISTER_SPLIT_KERNEL(scene_intersect); - REGISTER_SPLIT_KERNEL(lamp_emission); - REGISTER_SPLIT_KERNEL(do_volume); - REGISTER_SPLIT_KERNEL(queue_enqueue); - REGISTER_SPLIT_KERNEL(indirect_background); - REGISTER_SPLIT_KERNEL(shader_setup); - REGISTER_SPLIT_KERNEL(shader_sort); - REGISTER_SPLIT_KERNEL(shader_eval); - REGISTER_SPLIT_KERNEL(holdout_emission_blurring_pathtermination_ao); - REGISTER_SPLIT_KERNEL(subsurface_scatter); - REGISTER_SPLIT_KERNEL(direct_lighting); - REGISTER_SPLIT_KERNEL(shadow_blocked_ao); - REGISTER_SPLIT_KERNEL(shadow_blocked_dl); - REGISTER_SPLIT_KERNEL(enqueue_inactive); - REGISTER_SPLIT_KERNEL(next_iteration_setup); - REGISTER_SPLIT_KERNEL(indirect_subsurface); - REGISTER_SPLIT_KERNEL(buffer_update); - REGISTER_SPLIT_KERNEL(adaptive_stopping); - REGISTER_SPLIT_KERNEL(adaptive_filter_x); - REGISTER_SPLIT_KERNEL(adaptive_filter_y); - REGISTER_SPLIT_KERNEL(adaptive_adjust_samples); -#undef REGISTER_SPLIT_KERNEL -#undef KERNEL_FUNCTIONS - } - - ~CPUDevice() - { -#ifdef WITH_EMBREE - rtcReleaseDevice(embree_device); -#endif - task_pool.cancel(); - texture_info.free(); - } - - virtual bool show_samples() const override - { - return (info.cpu_threads == 1); - } - - virtual BVHLayoutMask get_bvh_layout_mask() const override - { - BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_BVH2; -#ifdef WITH_EMBREE - bvh_layout_mask |= BVH_LAYOUT_EMBREE; -#endif /* WITH_EMBREE */ - return bvh_layout_mask; - } - - void load_texture_info() - { - if (need_texture_info) { - texture_info.copy_to_device(); - need_texture_info = false; - } - } - - virtual void mem_alloc(device_memory &mem) override - { - if (mem.type == MEM_TEXTURE) { - assert(!"mem_alloc not supported for textures."); - } - else if (mem.type == MEM_GLOBAL) { - assert(!"mem_alloc not supported for global memory."); - } - else { - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - } - - if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) { - size_t alignment = MIN_ALIGNMENT_CPU_DATA_TYPES; - void *data = util_aligned_malloc(mem.memory_size(), alignment); - mem.device_pointer = (device_ptr)data; - } - else { - mem.device_pointer = (device_ptr)mem.host_pointer; - } - - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - } - } - - virtual void mem_copy_to(device_memory &mem) override - { - if (mem.type == MEM_GLOBAL) { - global_free(mem); - global_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - tex_alloc((device_texture &)mem); - } - else if (mem.type == MEM_PIXELS) { - assert(!"mem_copy_to not supported for pixels."); - } - else { - if (!mem.device_pointer) { - mem_alloc(mem); - } - - /* copy is no-op */ - } - } - - virtual void mem_copy_from( - device_memory & /*mem*/, int /*y*/, int /*w*/, int /*h*/, int /*elem*/) override - { - /* no-op */ - } - - virtual void mem_zero(device_memory &mem) override - { - if (!mem.device_pointer) { - mem_alloc(mem); - } - - if (mem.device_pointer) { - memset((void *)mem.device_pointer, 0, mem.memory_size()); - } - } - - virtual void mem_free(device_memory &mem) override - { - if (mem.type == MEM_GLOBAL) { - global_free(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - } - else if (mem.device_pointer) { - if (mem.type == MEM_DEVICE_ONLY || !mem.host_pointer) { - util_aligned_free((void *)mem.device_pointer); - } - mem.device_pointer = 0; - stats.mem_free(mem.device_size); - mem.device_size = 0; - } - } - - virtual device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int /*size*/) override - { - return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); - } - - virtual void const_copy_to(const char *name, void *host, size_t size) override - { -#if WITH_EMBREE - if (strcmp(name, "__data") == 0) { - assert(size <= sizeof(KernelData)); - - // Update scene handle (since it is different for each device on multi devices) - KernelData *const data = (KernelData *)host; - data->bvh.scene = embree_scene; - } -#endif - kernel_const_copy(&kernel_globals, name, host, size); - } - - void global_alloc(device_memory &mem) - { - VLOG(1) << "Global memory allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - kernel_global_memory_copy(&kernel_globals, mem.name, mem.host_pointer, mem.data_size); - - mem.device_pointer = (device_ptr)mem.host_pointer; - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - } - - void global_free(device_memory &mem) - { - if (mem.device_pointer) { - mem.device_pointer = 0; - stats.mem_free(mem.device_size); - mem.device_size = 0; - } - } - - void tex_alloc(device_texture &mem) - { - VLOG(1) << "Texture allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - mem.device_pointer = (device_ptr)mem.host_pointer; - mem.device_size = mem.memory_size(); - stats.mem_alloc(mem.device_size); - - const uint slot = mem.slot; - if (slot >= texture_info.size()) { - /* Allocate some slots in advance, to reduce amount of re-allocations. */ - texture_info.resize(slot + 128); - } - - texture_info[slot] = mem.info; - texture_info[slot].data = (uint64_t)mem.host_pointer; - need_texture_info = true; - } - - void tex_free(device_texture &mem) - { - if (mem.device_pointer) { - mem.device_pointer = 0; - stats.mem_free(mem.device_size); - mem.device_size = 0; - need_texture_info = true; - } - } - - virtual void *osl_memory() override - { -#ifdef WITH_OSL - return &osl_globals; -#else - return NULL; -#endif - } - - void build_bvh(BVH *bvh, Progress &progress, bool refit) override - { -#ifdef WITH_EMBREE - if (bvh->params.bvh_layout == BVH_LAYOUT_EMBREE || - bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) { - BVHEmbree *const bvh_embree = static_cast(bvh); - if (refit) { - bvh_embree->refit(progress); - } - else { - bvh_embree->build(progress, &stats, embree_device); - } - - if (bvh->params.top_level) { - embree_scene = bvh_embree->scene; - } - } - else -#endif - Device::build_bvh(bvh, progress, refit); - } - - void thread_run(DeviceTask &task) - { - if (task.type == DeviceTask::RENDER) - thread_render(task); - else if (task.type == DeviceTask::SHADER) - thread_shader(task); - else if (task.type == DeviceTask::FILM_CONVERT) - thread_film_convert(task); - else if (task.type == DeviceTask::DENOISE_BUFFER) - thread_denoise(task); - } - - bool denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_NON_LOCAL_MEANS); - - int4 rect = task->rect; - int r = task->nlm_state.r; - int f = task->nlm_state.f; - float a = task->nlm_state.a; - float k_2 = task->nlm_state.k_2; - - int w = align_up(rect.z - rect.x, 4); - int h = rect.w - rect.y; - int stride = task->buffer.stride; - int channel_offset = task->nlm_state.is_color ? task->buffer.pass_stride : 0; - - float *temporary_mem = (float *)task->buffer.temporary_mem.device_pointer; - float *blurDifference = temporary_mem; - float *difference = temporary_mem + task->buffer.pass_stride; - float *weightAccum = temporary_mem + 2 * task->buffer.pass_stride; - - memset(weightAccum, 0, sizeof(float) * w * h); - memset((float *)out_ptr, 0, sizeof(float) * w * h); - - for (int i = 0; i < (2 * r + 1) * (2 * r + 1); i++) { - int dy = i / (2 * r + 1) - r; - int dx = i % (2 * r + 1) - r; - - int local_rect[4] = { - max(0, -dx), max(0, -dy), rect.z - rect.x - max(0, dx), rect.w - rect.y - max(0, dy)}; - filter_nlm_calc_difference_kernel()(dx, - dy, - (float *)guide_ptr, - (float *)variance_ptr, - NULL, - difference, - local_rect, - w, - channel_offset, - 0, - a, - k_2); - - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, w, f); - filter_nlm_calc_weight_kernel()(blurDifference, difference, local_rect, w, f); - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, w, f); - - filter_nlm_update_output_kernel()(dx, - dy, - blurDifference, - (float *)image_ptr, - difference, - (float *)out_ptr, - weightAccum, - local_rect, - channel_offset, - stride, - f); - } - - int local_rect[4] = {0, 0, rect.z - rect.x, rect.w - rect.y}; - filter_nlm_normalize_kernel()((float *)out_ptr, weightAccum, local_rect, w); - - return true; - } - - bool denoising_construct_transform(DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_CONSTRUCT_TRANSFORM); - - for (int y = 0; y < task->filter_area.w; y++) { - for (int x = 0; x < task->filter_area.z; x++) { - filter_construct_transform_kernel()((float *)task->buffer.mem.device_pointer, - task->tile_info, - x + task->filter_area.x, - y + task->filter_area.y, - y * task->filter_area.z + x, - (float *)task->storage.transform.device_pointer, - (int *)task->storage.rank.device_pointer, - &task->rect.x, - task->buffer.pass_stride, - task->buffer.frame_stride, - task->buffer.use_time, - task->radius, - task->pca_threshold); - } - } - return true; - } - - bool denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_RECONSTRUCT); - - float *temporary_mem = (float *)task->buffer.temporary_mem.device_pointer; - float *difference = temporary_mem; - float *blurDifference = temporary_mem + task->buffer.pass_stride; - - int r = task->radius; - int frame_offset = frame * task->buffer.frame_stride; - for (int i = 0; i < (2 * r + 1) * (2 * r + 1); i++) { - int dy = i / (2 * r + 1) - r; - int dx = i % (2 * r + 1) - r; - - int local_rect[4] = {max(0, -dx), - max(0, -dy), - task->reconstruction_state.source_w - max(0, dx), - task->reconstruction_state.source_h - max(0, dy)}; - filter_nlm_calc_difference_kernel()(dx, - dy, - (float *)color_ptr, - (float *)color_variance_ptr, - (float *)scale_ptr, - difference, - local_rect, - task->buffer.stride, - task->buffer.pass_stride, - frame_offset, - 1.0f, - task->nlm_k_2); - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, task->buffer.stride, 4); - filter_nlm_calc_weight_kernel()( - blurDifference, difference, local_rect, task->buffer.stride, 4); - filter_nlm_blur_kernel()(difference, blurDifference, local_rect, task->buffer.stride, 4); - filter_nlm_construct_gramian_kernel()(dx, - dy, - task->tile_info->frames[frame], - blurDifference, - (float *)task->buffer.mem.device_pointer, - (float *)task->storage.transform.device_pointer, - (int *)task->storage.rank.device_pointer, - (float *)task->storage.XtWX.device_pointer, - (float3 *)task->storage.XtWY.device_pointer, - local_rect, - &task->reconstruction_state.filter_window.x, - task->buffer.stride, - 4, - task->buffer.pass_stride, - frame_offset, - task->buffer.use_time); - } - - return true; - } - - bool denoising_solve(device_ptr output_ptr, DenoisingTask *task) - { - for (int y = 0; y < task->filter_area.w; y++) { - for (int x = 0; x < task->filter_area.z; x++) { - filter_finalize_kernel()(x, - y, - y * task->filter_area.z + x, - (float *)output_ptr, - (int *)task->storage.rank.device_pointer, - (float *)task->storage.XtWX.device_pointer, - (float3 *)task->storage.XtWY.device_pointer, - &task->reconstruction_state.buffer_params.x, - task->render_buffer.samples); - } - } - return true; - } - - bool denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_COMBINE_HALVES); - - for (int y = rect.y; y < rect.w; y++) { - for (int x = rect.x; x < rect.z; x++) { - filter_combine_halves_kernel()(x, - y, - (float *)mean_ptr, - (float *)variance_ptr, - (float *)a_ptr, - (float *)b_ptr, - &rect.x, - r); - } - } - return true; - } - - bool denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_DIVIDE_SHADOW); - - for (int y = task->rect.y; y < task->rect.w; y++) { - for (int x = task->rect.x; x < task->rect.z; x++) { - filter_divide_shadow_kernel()(task->render_buffer.samples, - task->tile_info, - x, - y, - (float *)a_ptr, - (float *)b_ptr, - (float *)sample_variance_ptr, - (float *)sv_variance_ptr, - (float *)buffer_variance_ptr, - &task->rect.x, - task->render_buffer.pass_stride, - task->render_buffer.offset); - } - } - return true; - } - - bool denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_GET_FEATURE); - - for (int y = task->rect.y; y < task->rect.w; y++) { - for (int x = task->rect.x; x < task->rect.z; x++) { - filter_get_feature_kernel()(task->render_buffer.samples, - task->tile_info, - mean_offset, - variance_offset, - x, - y, - (float *)mean_ptr, - (float *)variance_ptr, - scale, - &task->rect.x, - task->render_buffer.pass_stride, - task->render_buffer.offset); - } - } - return true; - } - - bool denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task) - { - for (int y = 0; y < task->filter_area.w; y++) { - for (int x = 0; x < task->filter_area.z; x++) { - filter_write_feature_kernel()(task->render_buffer.samples, - x + task->filter_area.x, - y + task->filter_area.y, - &task->reconstruction_state.buffer_params.x, - (float *)from_ptr, - (float *)buffer_ptr, - out_offset, - &task->rect.x); - } - } - return true; - } - - bool denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task) - { - ProfilingHelper profiling(task->profiler, PROFILING_DENOISING_DETECT_OUTLIERS); - - for (int y = task->rect.y; y < task->rect.w; y++) { - for (int x = task->rect.x; x < task->rect.z; x++) { - filter_detect_outliers_kernel()(x, - y, - (float *)image_ptr, - (float *)variance_ptr, - (float *)depth_ptr, - (float *)output_ptr, - &task->rect.x, - task->buffer.pass_stride); - } - } - return true; - } - - bool adaptive_sampling_filter(KernelGlobals *kg, RenderTile &tile, int sample) - { - WorkTile wtile; - wtile.x = tile.x; - wtile.y = tile.y; - wtile.w = tile.w; - wtile.h = tile.h; - wtile.offset = tile.offset; - wtile.stride = tile.stride; - wtile.buffer = (float *)tile.buffer; - - /* For CPU we do adaptive stopping per sample so we can stop earlier, but - * for combined CPU + GPU rendering we match the GPU and do it per tile - * after a given number of sample steps. */ - if (!kernel_data.integrator.adaptive_stop_per_sample) { - for (int y = wtile.y; y < wtile.y + wtile.h; ++y) { - for (int x = wtile.x; x < wtile.x + wtile.w; ++x) { - const int index = wtile.offset + x + y * wtile.stride; - float *buffer = wtile.buffer + index * kernel_data.film.pass_stride; - kernel_do_adaptive_stopping(kg, buffer, sample); - } - } - } - - bool any = false; - for (int y = wtile.y; y < wtile.y + wtile.h; ++y) { - any |= kernel_do_adaptive_filter_x(kg, y, &wtile); - } - for (int x = wtile.x; x < wtile.x + wtile.w; ++x) { - any |= kernel_do_adaptive_filter_y(kg, x, &wtile); - } - return (!any); - } - - void adaptive_sampling_post(const RenderTile &tile, KernelGlobals *kg) - { - float *render_buffer = (float *)tile.buffer; - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - int index = tile.offset + x + y * tile.stride; - ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; - if (buffer[kernel_data.film.pass_sample_count] < 0.0f) { - buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count]; - float sample_multiplier = tile.sample / buffer[kernel_data.film.pass_sample_count]; - if (sample_multiplier != 1.0f) { - kernel_adaptive_post_adjust(kg, buffer, sample_multiplier); - } - } - else { - kernel_adaptive_post_adjust(kg, buffer, tile.sample / (tile.sample - 1.0f)); - } - } - } - } - - void render(DeviceTask &task, RenderTile &tile, KernelGlobals *kg) - { - const bool use_coverage = kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE; - - scoped_timer timer(&tile.buffers->render_time); - - Coverage coverage(kg, tile); - if (use_coverage) { - coverage.init_path_trace(); - } - - float *render_buffer = (float *)tile.buffer; - int start_sample = tile.start_sample; - int end_sample = tile.start_sample + tile.num_samples; - - /* Needed for Embree. */ - SIMD_SET_FLUSH_TO_ZERO; - - for (int sample = start_sample; sample < end_sample; sample++) { - if (task.get_cancel() || TaskPool::canceled()) { - if (task.need_finish_queue == false) - break; - } - - if (tile.stealing_state == RenderTile::CAN_BE_STOLEN && task.get_tile_stolen()) { - tile.stealing_state = RenderTile::WAS_STOLEN; - break; - } - - if (tile.task == RenderTile::PATH_TRACE) { - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - if (use_coverage) { - coverage.init_pixel(x, y); - } - path_trace_kernel()(kg, render_buffer, sample, x, y, tile.offset, tile.stride); - } - } - } - else { - for (int y = tile.y; y < tile.y + tile.h; y++) { - for (int x = tile.x; x < tile.x + tile.w; x++) { - bake_kernel()(kg, render_buffer, sample, x, y, tile.offset, tile.stride); - } - } - } - tile.sample = sample + 1; - - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(sample)) { - const bool stop = adaptive_sampling_filter(kg, tile, sample); - if (stop) { - const int num_progress_samples = end_sample - sample; - tile.sample = end_sample; - task.update_progress(&tile, tile.w * tile.h * num_progress_samples); - break; - } - } - - task.update_progress(&tile, tile.w * tile.h); - } - if (use_coverage) { - coverage.finalize(); - } - - if (task.adaptive_sampling.use && (tile.stealing_state != RenderTile::WAS_STOLEN)) { - adaptive_sampling_post(tile, kg); - } - } - - void denoise_openimagedenoise_buffer(DeviceTask &task, - float *buffer, - const size_t offset, - const size_t stride, - const size_t x, - const size_t y, - const size_t w, - const size_t h, - const float scale) - { -#ifdef WITH_OPENIMAGEDENOISE - assert(openimagedenoise_supported()); - - /* Only one at a time, since OpenImageDenoise itself is multithreaded for full - * buffers, and for tiled rendering because creating multiple devices and filters - * is slow and memory hungry as well. - * - * TODO: optimize tiled rendering case, by batching together denoising of many - * tiles somehow? */ - static thread_mutex mutex; - thread_scoped_lock lock(mutex); - - /* Create device and filter, cached for reuse. */ - if (!oidn_device) { - oidn_device = oidn::newDevice(); - oidn_device.commit(); - } - if (!oidn_filter) { - oidn_filter = oidn_device.newFilter("RT"); - oidn_filter.set("hdr", true); - oidn_filter.set("srgb", false); - } - - /* Set images with appropriate stride for our interleaved pass storage. */ - struct { - const char *name; - const int offset; - const bool scale; - const bool use; - array scaled_buffer; - } passes[] = {{"color", task.pass_denoising_data + DENOISING_PASS_COLOR, false, true}, - {"albedo", - task.pass_denoising_data + DENOISING_PASS_ALBEDO, - true, - task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO}, - {"normal", - task.pass_denoising_data + DENOISING_PASS_NORMAL, - true, - task.denoising.input_passes >= DENOISER_INPUT_RGB_ALBEDO_NORMAL}, - {"output", 0, false, true}, - { NULL, - 0 }}; - - for (int i = 0; passes[i].name; i++) { - if (!passes[i].use) { - continue; - } - - const int64_t pixel_offset = offset + x + y * stride; - const int64_t buffer_offset = (pixel_offset * task.pass_stride + passes[i].offset); - const int64_t pixel_stride = task.pass_stride; - const int64_t row_stride = stride * pixel_stride; - - if (passes[i].scale && scale != 1.0f) { - /* Normalize albedo and normal passes as they are scaled by the number of samples. - * For the color passes OIDN will perform auto-exposure making it unnecessary. */ - array &scaled_buffer = passes[i].scaled_buffer; - scaled_buffer.resize(w * h * 3); - - for (int y = 0; y < h; y++) { - const float *pass_row = buffer + buffer_offset + y * row_stride; - float *scaled_row = scaled_buffer.data() + y * w * 3; - - for (int x = 0; x < w; x++) { - scaled_row[x * 3 + 0] = pass_row[x * pixel_stride + 0] * scale; - scaled_row[x * 3 + 1] = pass_row[x * pixel_stride + 1] * scale; - scaled_row[x * 3 + 2] = pass_row[x * pixel_stride + 2] * scale; - } - } - - oidn_filter.setImage( - passes[i].name, scaled_buffer.data(), oidn::Format::Float3, w, h, 0, 0, 0); - } - else { - oidn_filter.setImage(passes[i].name, - buffer + buffer_offset, - oidn::Format::Float3, - w, - h, - 0, - pixel_stride * sizeof(float), - row_stride * sizeof(float)); - } - } - - /* Execute filter. */ - oidn_filter.commit(); - oidn_filter.execute(); -#else - (void)task; - (void)buffer; - (void)offset; - (void)stride; - (void)x; - (void)y; - (void)w; - (void)h; - (void)scale; -#endif - } - - void denoise_openimagedenoise(DeviceTask &task, RenderTile &rtile) - { - if (task.type == DeviceTask::DENOISE_BUFFER) { - /* Copy pixels from compute device to CPU (no-op for CPU device). */ - rtile.buffers->buffer.copy_from_device(); - - denoise_openimagedenoise_buffer(task, - (float *)rtile.buffer, - rtile.offset, - rtile.stride, - rtile.x, - rtile.y, - rtile.w, - rtile.h, - 1.0f / rtile.sample); - - /* todo: it may be possible to avoid this copy, but we have to ensure that - * when other code copies data from the device it doesn't overwrite the - * denoiser buffers. */ - rtile.buffers->buffer.copy_to_device(); - } - else { - /* Per-tile denoising. */ - rtile.sample = rtile.start_sample + rtile.num_samples; - const float scale = 1.0f / rtile.sample; - const float invscale = rtile.sample; - const size_t pass_stride = task.pass_stride; - - /* Map neighboring tiles into one buffer for denoising. */ - RenderTileNeighbors neighbors(rtile); - task.map_neighbor_tiles(neighbors, this); - RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; - rtile = center_tile; - - /* Calculate size of the tile to denoise (including overlap). The overlap - * size was chosen empirically. OpenImageDenoise specifies an overlap size - * of 128 but this is significantly bigger than typical tile size. */ - const int4 rect = rect_clip(rect_expand(center_tile.bounds(), 64), neighbors.bounds()); - const int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y); - - /* Adjacent tiles are in separate memory regions, copy into single buffer. */ - array merged(rect_size.x * rect_size.y * task.pass_stride); - - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &ntile = neighbors.tiles[i]; - if (!ntile.buffer) { - continue; - } - - const int xmin = max(ntile.x, rect.x); - const int ymin = max(ntile.y, rect.y); - const int xmax = min(ntile.x + ntile.w, rect.z); - const int ymax = min(ntile.y + ntile.h, rect.w); - - const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride; - const float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride; - - const size_t merged_stride = rect_size.x; - const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride; - float *merged_buffer = merged.data() + merged_offset * pass_stride; - - for (int y = ymin; y < ymax; y++) { - for (int x = 0; x < pass_stride * (xmax - xmin); x++) { - merged_buffer[x] = tile_buffer[x] * scale; - } - tile_buffer += ntile.stride * pass_stride; - merged_buffer += merged_stride * pass_stride; - } - } - - /* Denoise */ - denoise_openimagedenoise_buffer( - task, merged.data(), 0, rect_size.x, 0, 0, rect_size.x, rect_size.y, 1.0f); - - /* Copy back result from merged buffer. */ - RenderTile &ntile = neighbors.target; - if (ntile.buffer) { - const int xmin = max(ntile.x, rect.x); - const int ymin = max(ntile.y, rect.y); - const int xmax = min(ntile.x + ntile.w, rect.z); - const int ymax = min(ntile.y + ntile.h, rect.w); - - const size_t tile_offset = ntile.offset + xmin + ymin * ntile.stride; - float *tile_buffer = (float *)ntile.buffer + tile_offset * pass_stride; - - const size_t merged_stride = rect_size.x; - const size_t merged_offset = (xmin - rect.x) + (ymin - rect.y) * merged_stride; - const float *merged_buffer = merged.data() + merged_offset * pass_stride; - - for (int y = ymin; y < ymax; y++) { - for (int x = 0; x < pass_stride * (xmax - xmin); x += pass_stride) { - tile_buffer[x + 0] = merged_buffer[x + 0] * invscale; - tile_buffer[x + 1] = merged_buffer[x + 1] * invscale; - tile_buffer[x + 2] = merged_buffer[x + 2] * invscale; - } - tile_buffer += ntile.stride * pass_stride; - merged_buffer += merged_stride * pass_stride; - } - } - - task.unmap_neighbor_tiles(neighbors, this); - } - } - - void denoise_nlm(DenoisingTask &denoising, RenderTile &tile) - { - ProfilingHelper profiling(denoising.profiler, PROFILING_DENOISING); - - tile.sample = tile.start_sample + tile.num_samples; - - denoising.functions.construct_transform = function_bind( - &CPUDevice::denoising_construct_transform, this, &denoising); - denoising.functions.accumulate = function_bind( - &CPUDevice::denoising_accumulate, this, _1, _2, _3, _4, &denoising); - denoising.functions.solve = function_bind(&CPUDevice::denoising_solve, this, _1, &denoising); - denoising.functions.divide_shadow = function_bind( - &CPUDevice::denoising_divide_shadow, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.non_local_means = function_bind( - &CPUDevice::denoising_non_local_means, this, _1, _2, _3, _4, &denoising); - denoising.functions.combine_halves = function_bind( - &CPUDevice::denoising_combine_halves, this, _1, _2, _3, _4, _5, _6, &denoising); - denoising.functions.get_feature = function_bind( - &CPUDevice::denoising_get_feature, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.write_feature = function_bind( - &CPUDevice::denoising_write_feature, this, _1, _2, _3, &denoising); - denoising.functions.detect_outliers = function_bind( - &CPUDevice::denoising_detect_outliers, this, _1, _2, _3, _4, &denoising); - - denoising.filter_area = make_int4(tile.x, tile.y, tile.w, tile.h); - denoising.render_buffer.samples = tile.sample; - denoising.buffer.gpu_temporary_mem = false; - - denoising.run_denoising(tile); - } - - void thread_render(DeviceTask &task) - { - if (TaskPool::canceled()) { - if (task.need_finish_queue == false) - return; - } - - /* allocate buffer for kernel globals */ - device_only_memory kgbuffer(this, "kernel_globals"); - kgbuffer.alloc_to_device(1); - - KernelGlobals *kg = new ((void *)kgbuffer.device_pointer) - KernelGlobals(thread_kernel_globals_init()); - - profiler.add_state(&kg->profiler); - - CPUSplitKernel *split_kernel = NULL; - if (use_split_kernel) { - split_kernel = new CPUSplitKernel(this); - if (!split_kernel->load_kernels(requested_features)) { - thread_kernel_globals_free((KernelGlobals *)kgbuffer.device_pointer); - kgbuffer.free(); - delete split_kernel; - return; - } - } - - /* NLM denoiser. */ - DenoisingTask *denoising = NULL; - - /* OpenImageDenoise: we can only denoise with one thread at a time, so to - * avoid waiting with mutex locks in the denoiser, we let only a single - * thread acquire denoising tiles. */ - uint tile_types = task.tile_types; - bool hold_denoise_lock = false; - if ((tile_types & RenderTile::DENOISE) && task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - if (!oidn_task_lock.try_lock()) { - tile_types &= ~RenderTile::DENOISE; - hold_denoise_lock = true; - } - } - - RenderTile tile; - while (task.acquire_tile(this, tile, tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - if (use_split_kernel) { - device_only_memory void_buffer(this, "void_buffer"); - split_kernel->path_trace(task, tile, kgbuffer, void_buffer); - } - else { - render(task, tile, kg); - } - } - else if (tile.task == RenderTile::BAKE) { - render(task, tile, kg); - } - else if (tile.task == RenderTile::DENOISE) { - if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - denoise_openimagedenoise(task, tile); - } - else if (task.denoising.type == DENOISER_NLM) { - if (denoising == NULL) { - denoising = new DenoisingTask(this, task); - denoising->profiler = &kg->profiler; - } - denoise_nlm(*denoising, tile); - } - task.update_progress(&tile, tile.w * tile.h); - } - - task.release_tile(tile); - - if (TaskPool::canceled()) { - if (task.need_finish_queue == false) - break; - } - } - - if (hold_denoise_lock) { - oidn_task_lock.unlock(); - } - - profiler.remove_state(&kg->profiler); - - thread_kernel_globals_free((KernelGlobals *)kgbuffer.device_pointer); - kg->~KernelGlobals(); - kgbuffer.free(); - delete split_kernel; - delete denoising; - } - - void thread_denoise(DeviceTask &task) - { - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.sample = task.sample + task.num_samples; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - if (task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - denoise_openimagedenoise(task, tile); - } - else { - DenoisingTask denoising(this, task); - - ProfilingState denoising_profiler_state; - profiler.add_state(&denoising_profiler_state); - denoising.profiler = &denoising_profiler_state; - - denoise_nlm(denoising, tile); - - profiler.remove_state(&denoising_profiler_state); - } - - task.update_progress(&tile, tile.w * tile.h); - } - - void thread_film_convert(DeviceTask &task) - { - float sample_scale = 1.0f / (task.sample + 1); - - if (task.rgba_half) { - for (int y = task.y; y < task.y + task.h; y++) - for (int x = task.x; x < task.x + task.w; x++) - convert_to_half_float_kernel()(&kernel_globals, - (uchar4 *)task.rgba_half, - (float *)task.buffer, - sample_scale, - x, - y, - task.offset, - task.stride); - } - else { - for (int y = task.y; y < task.y + task.h; y++) - for (int x = task.x; x < task.x + task.w; x++) - convert_to_byte_kernel()(&kernel_globals, - (uchar4 *)task.rgba_byte, - (float *)task.buffer, - sample_scale, - x, - y, - task.offset, - task.stride); - } - } - - void thread_shader(DeviceTask &task) - { - KernelGlobals *kg = new KernelGlobals(thread_kernel_globals_init()); - - for (int sample = 0; sample < task.num_samples; sample++) { - for (int x = task.shader_x; x < task.shader_x + task.shader_w; x++) - shader_kernel()(kg, - (uint4 *)task.shader_input, - (float4 *)task.shader_output, - task.shader_eval_type, - task.shader_filter, - x, - task.offset, - sample); - - if (task.get_cancel() || TaskPool::canceled()) - break; - - task.update_progress(NULL); - } - - thread_kernel_globals_free(kg); - delete kg; - } - - virtual int get_split_task_count(DeviceTask &task) override - { - if (task.type == DeviceTask::SHADER) - return task.get_subtask_count(info.cpu_threads, 256); - else - return task.get_subtask_count(info.cpu_threads); - } - - virtual void task_add(DeviceTask &task) override - { - /* Load texture info. */ - load_texture_info(); - - /* split task into smaller ones */ - list tasks; - - if (task.type == DeviceTask::DENOISE_BUFFER && - task.denoising.type == DENOISER_OPENIMAGEDENOISE) { - /* Denoise entire buffer at once with OIDN, it has own threading. */ - tasks.push_back(task); - } - else if (task.type == DeviceTask::SHADER) { - task.split(tasks, info.cpu_threads, 256); - } - else { - task.split(tasks, info.cpu_threads); - } - - foreach (DeviceTask &task, tasks) { - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy); - }); - } - } - - virtual void task_wait() override - { - task_pool.wait_work(); - } - - virtual void task_cancel() override - { - task_pool.cancel(); - } - - protected: - inline KernelGlobals thread_kernel_globals_init() - { - KernelGlobals kg = kernel_globals; - kg.transparent_shadow_intersections = NULL; - const int decoupled_count = sizeof(kg.decoupled_volume_steps) / - sizeof(*kg.decoupled_volume_steps); - for (int i = 0; i < decoupled_count; ++i) { - kg.decoupled_volume_steps[i] = NULL; - } - kg.decoupled_volume_steps_index = 0; - kg.coverage_asset = kg.coverage_object = kg.coverage_material = NULL; -#ifdef WITH_OSL - OSLShader::thread_init(&kg, &kernel_globals, &osl_globals); -#endif - return kg; - } - - inline void thread_kernel_globals_free(KernelGlobals *kg) - { - if (kg == NULL) { - return; - } - - if (kg->transparent_shadow_intersections != NULL) { - free(kg->transparent_shadow_intersections); - } - const int decoupled_count = sizeof(kg->decoupled_volume_steps) / - sizeof(*kg->decoupled_volume_steps); - for (int i = 0; i < decoupled_count; ++i) { - if (kg->decoupled_volume_steps[i] != NULL) { - free(kg->decoupled_volume_steps[i]); - } - } -#ifdef WITH_OSL - OSLShader::thread_free(kg); -#endif - } - - virtual bool load_kernels(const DeviceRequestedFeatures &requested_features_) override - { - requested_features = requested_features_; - - return true; - } -}; - -/* split kernel */ - -class CPUSplitKernelFunction : public SplitKernelFunction { - public: - CPUDevice *device; - void (*func)(KernelGlobals *kg, KernelData *data); - - CPUSplitKernelFunction(CPUDevice *device) : device(device), func(NULL) - { - } - ~CPUSplitKernelFunction() - { - } - - virtual bool enqueue(const KernelDimensions &dim, - device_memory &kernel_globals, - device_memory &data) - { - if (!func) { - return false; - } - - KernelGlobals *kg = (KernelGlobals *)kernel_globals.device_pointer; - kg->global_size = make_int2(dim.global_size[0], dim.global_size[1]); - - for (int y = 0; y < dim.global_size[1]; y++) { - for (int x = 0; x < dim.global_size[0]; x++) { - kg->global_id = make_int2(x, y); - - func(kg, (KernelData *)data.device_pointer); - } - } - - return true; - } -}; - -CPUSplitKernel::CPUSplitKernel(CPUDevice *device) : DeviceSplitKernel(device), device(device) -{ -} - -bool CPUSplitKernel::enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &data, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flags, - device_memory &work_pool_wgs) -{ - KernelGlobals *kg = (KernelGlobals *)kernel_globals.device_pointer; - kg->global_size = make_int2(dim.global_size[0], dim.global_size[1]); - - for (int y = 0; y < dim.global_size[1]; y++) { - for (int x = 0; x < dim.global_size[0]; x++) { - kg->global_id = make_int2(x, y); - - device->data_init_kernel()((KernelGlobals *)kernel_globals.device_pointer, - (KernelData *)data.device_pointer, - (void *)split_data.device_pointer, - num_global_elements, - (char *)ray_state.device_pointer, - rtile.start_sample, - rtile.start_sample + rtile.num_samples, - rtile.x, - rtile.y, - rtile.w, - rtile.h, - rtile.offset, - rtile.stride, - (int *)queue_index.device_pointer, - dim.global_size[0] * dim.global_size[1], - (char *)use_queues_flags.device_pointer, - (uint *)work_pool_wgs.device_pointer, - rtile.num_samples, - (float *)rtile.buffer); - } - } - - return true; -} - -SplitKernelFunction *CPUSplitKernel::get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &) -{ - CPUSplitKernelFunction *kernel = new CPUSplitKernelFunction(device); - - kernel->func = device->split_kernels[kernel_name](); - if (!kernel->func) { - delete kernel; - return NULL; - } - - return kernel; -} - -int2 CPUSplitKernel::split_kernel_local_size() -{ - return make_int2(1, 1); -} - -int2 CPUSplitKernel::split_kernel_global_size(device_memory & /*kg*/, - device_memory & /*data*/, - DeviceTask & /*task*/) -{ - return make_int2(1, 1); -} - -uint64_t CPUSplitKernel::state_buffer_size(device_memory &kernel_globals, - device_memory & /*data*/, - size_t num_threads) -{ - KernelGlobals *kg = (KernelGlobals *)kernel_globals.device_pointer; - - return split_data_buffer_size(kg, num_threads); -} - -Device *device_cpu_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return new CPUDevice(info, stats, profiler, background); -} - -void device_cpu_info(vector &devices) -{ - DeviceInfo info; - - info.type = DEVICE_CPU; - info.description = system_cpu_brand_string(); - info.id = "CPU"; - info.num = 0; - info.has_volume_decoupled = true; - 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()) { - info.denoisers |= DENOISER_OPENIMAGEDENOISE; - } - - devices.insert(devices.begin(), info); -} - -string device_cpu_capabilities() -{ - string capabilities = ""; - capabilities += system_cpu_support_sse2() ? "SSE2 " : ""; - capabilities += system_cpu_support_sse3() ? "SSE3 " : ""; - capabilities += system_cpu_support_sse41() ? "SSE41 " : ""; - capabilities += system_cpu_support_avx() ? "AVX " : ""; - capabilities += system_cpu_support_avx2() ? "AVX2" : ""; - if (capabilities[capabilities.size() - 1] == ' ') - capabilities.resize(capabilities.size() - 1); - return capabilities; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_denoising.cpp b/intern/cycles/device/device_denoising.cpp deleted file mode 100644 index 38c42d15cab..00000000000 --- a/intern/cycles/device/device_denoising.cpp +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "device/device_denoising.h" - -#include "kernel/filter/filter_defines.h" - -CCL_NAMESPACE_BEGIN - -DenoisingTask::DenoisingTask(Device *device, const DeviceTask &task) - : tile_info_mem(device, "denoising tile info mem", MEM_READ_WRITE), - profiler(NULL), - storage(device), - buffer(device), - device(device) -{ - radius = task.denoising.radius; - nlm_k_2 = powf(2.0f, lerp(-5.0f, 3.0f, task.denoising.strength)); - if (task.denoising.relative_pca) { - pca_threshold = -powf(10.0f, lerp(-8.0f, 0.0f, task.denoising.feature_strength)); - } - else { - pca_threshold = powf(10.0f, lerp(-5.0f, 3.0f, task.denoising.feature_strength)); - } - - render_buffer.frame_stride = task.frame_stride; - render_buffer.pass_stride = task.pass_stride; - render_buffer.offset = task.pass_denoising_data; - - target_buffer.pass_stride = task.target_pass_stride; - target_buffer.denoising_clean_offset = task.pass_denoising_clean; - target_buffer.offset = 0; - - functions.map_neighbor_tiles = function_bind(task.map_neighbor_tiles, _1, device); - functions.unmap_neighbor_tiles = function_bind(task.unmap_neighbor_tiles, _1, device); - - tile_info = (TileInfo *)tile_info_mem.alloc(sizeof(TileInfo) / sizeof(int)); - tile_info->from_render = task.denoising_from_render ? 1 : 0; - - tile_info->frames[0] = 0; - tile_info->num_frames = min(task.denoising_frames.size() + 1, DENOISE_MAX_FRAMES); - for (int i = 1; i < tile_info->num_frames; i++) { - tile_info->frames[i] = task.denoising_frames[i - 1]; - } - - do_prefilter = task.denoising.store_passes && task.denoising.type == DENOISER_NLM; - do_filter = task.denoising.use && task.denoising.type == DENOISER_NLM; -} - -DenoisingTask::~DenoisingTask() -{ - storage.XtWX.free(); - storage.XtWY.free(); - storage.transform.free(); - storage.rank.free(); - buffer.mem.free(); - buffer.temporary_mem.free(); - tile_info_mem.free(); -} - -void DenoisingTask::set_render_buffer(RenderTileNeighbors &neighbors) -{ - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &rtile = neighbors.tiles[i]; - tile_info->offsets[i] = rtile.offset; - tile_info->strides[i] = rtile.stride; - tile_info->buffers[i] = rtile.buffer; - } - tile_info->x[0] = neighbors.tiles[3].x; - tile_info->x[1] = neighbors.tiles[4].x; - tile_info->x[2] = neighbors.tiles[5].x; - tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w; - tile_info->y[0] = neighbors.tiles[1].y; - tile_info->y[1] = neighbors.tiles[4].y; - tile_info->y[2] = neighbors.tiles[7].y; - tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h; - - target_buffer.offset = neighbors.target.offset; - target_buffer.stride = neighbors.target.stride; - target_buffer.ptr = neighbors.target.buffer; - - if (do_prefilter && neighbors.target.buffers) { - target_buffer.denoising_output_offset = - neighbors.target.buffers->params.get_denoising_prefiltered_offset(); - } - else { - target_buffer.denoising_output_offset = 0; - } - - tile_info_mem.copy_to_device(); -} - -void DenoisingTask::setup_denoising_buffer() -{ - /* Expand filter_area by radius pixels and clamp the result to the extent of the neighboring - * tiles */ - rect = rect_from_shape(filter_area.x, filter_area.y, filter_area.z, filter_area.w); - rect = rect_expand(rect, radius); - rect = rect_clip(rect, - make_int4(tile_info->x[0], tile_info->y[0], tile_info->x[3], tile_info->y[3])); - - buffer.use_intensity = do_prefilter || (tile_info->num_frames > 1); - buffer.passes = buffer.use_intensity ? 15 : 14; - buffer.width = rect.z - rect.x; - buffer.stride = align_up(buffer.width, 4); - buffer.h = rect.w - rect.y; - int alignment_floats = divide_up(device->mem_sub_ptr_alignment(), sizeof(float)); - buffer.pass_stride = align_up(buffer.stride * buffer.h, alignment_floats); - buffer.frame_stride = buffer.pass_stride * buffer.passes; - /* Pad the total size by four floats since the SIMD kernels might go a bit over the end. */ - int mem_size = align_up(tile_info->num_frames * buffer.frame_stride + 4, alignment_floats); - buffer.mem.alloc_to_device(mem_size, false); - buffer.use_time = (tile_info->num_frames > 1); - - /* CPUs process shifts sequentially while GPUs process them in parallel. */ - int num_layers; - if (buffer.gpu_temporary_mem) { - /* Shadowing prefiltering uses a radius of 6, so allocate at least that much. */ - int max_radius = max(radius, 6); - int num_shifts = (2 * max_radius + 1) * (2 * max_radius + 1); - num_layers = 2 * num_shifts + 1; - } - else { - num_layers = 3; - } - /* Allocate two layers per shift as well as one for the weight accumulation. */ - buffer.temporary_mem.alloc_to_device(num_layers * buffer.pass_stride); -} - -void DenoisingTask::prefilter_shadowing() -{ - device_ptr null_ptr = (device_ptr)0; - - device_sub_ptr unfiltered_a(buffer.mem, 0, buffer.pass_stride); - device_sub_ptr unfiltered_b(buffer.mem, 1 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr sample_var(buffer.mem, 2 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr sample_var_var(buffer.mem, 3 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr buffer_var(buffer.mem, 5 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr filtered_var(buffer.mem, 6 * buffer.pass_stride, buffer.pass_stride); - - /* Get the A/B unfiltered passes, the combined sample variance, the estimated variance of the - * sample variance and the buffer variance. */ - functions.divide_shadow(*unfiltered_a, *unfiltered_b, *sample_var, *sample_var_var, *buffer_var); - - /* Smooth the (generally pretty noisy) buffer variance using the spatial information from the - * sample variance. */ - nlm_state.set_parameters(6, 3, 4.0f, 1.0f, false); - functions.non_local_means(*buffer_var, *sample_var, *sample_var_var, *filtered_var); - - /* Reuse memory, the previous data isn't needed anymore. */ - device_ptr filtered_a = *buffer_var, filtered_b = *sample_var; - /* Use the smoothed variance to filter the two shadow half images using each other for weight - * calculation. */ - nlm_state.set_parameters(5, 3, 1.0f, 0.25f, false); - functions.non_local_means(*unfiltered_a, *unfiltered_b, *filtered_var, filtered_a); - functions.non_local_means(*unfiltered_b, *unfiltered_a, *filtered_var, filtered_b); - - device_ptr residual_var = *sample_var_var; - /* Estimate the residual variance between the two filtered halves. */ - functions.combine_halves(filtered_a, filtered_b, null_ptr, residual_var, 2, rect); - - device_ptr final_a = *unfiltered_a, final_b = *unfiltered_b; - /* Use the residual variance for a second filter pass. */ - nlm_state.set_parameters(4, 2, 1.0f, 0.5f, false); - functions.non_local_means(filtered_a, filtered_b, residual_var, final_a); - functions.non_local_means(filtered_b, filtered_a, residual_var, final_b); - - /* Combine the two double-filtered halves to a final shadow feature. */ - device_sub_ptr shadow_pass(buffer.mem, 4 * buffer.pass_stride, buffer.pass_stride); - functions.combine_halves(final_a, final_b, *shadow_pass, null_ptr, 0, rect); -} - -void DenoisingTask::prefilter_features() -{ - device_sub_ptr unfiltered(buffer.mem, 8 * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr variance(buffer.mem, 9 * buffer.pass_stride, buffer.pass_stride); - - int mean_from[] = {0, 1, 2, 12, 6, 7, 8}; - int variance_from[] = {3, 4, 5, 13, 9, 10, 11}; - int pass_to[] = {1, 2, 3, 0, 5, 6, 7}; - for (int pass = 0; pass < 7; pass++) { - device_sub_ptr feature_pass( - buffer.mem, pass_to[pass] * buffer.pass_stride, buffer.pass_stride); - /* Get the unfiltered pass and its variance from the RenderBuffers. */ - functions.get_feature(mean_from[pass], - variance_from[pass], - *unfiltered, - *variance, - 1.0f / render_buffer.samples); - /* Smooth the pass and store the result in the denoising buffers. */ - nlm_state.set_parameters(2, 2, 1.0f, 0.25f, false); - functions.non_local_means(*unfiltered, *unfiltered, *variance, *feature_pass); - } -} - -void DenoisingTask::prefilter_color() -{ - int mean_from[] = {20, 21, 22}; - int variance_from[] = {23, 24, 25}; - int mean_to[] = {8, 9, 10}; - int variance_to[] = {11, 12, 13}; - int num_color_passes = 3; - - device_only_memory temporary_color(device, "denoising temporary color"); - temporary_color.alloc_to_device(6 * buffer.pass_stride, false); - - for (int pass = 0; pass < num_color_passes; pass++) { - device_sub_ptr color_pass(temporary_color, pass * buffer.pass_stride, buffer.pass_stride); - device_sub_ptr color_var_pass( - temporary_color, (pass + 3) * buffer.pass_stride, buffer.pass_stride); - functions.get_feature(mean_from[pass], - variance_from[pass], - *color_pass, - *color_var_pass, - 1.0f / render_buffer.samples); - } - - device_sub_ptr depth_pass(buffer.mem, 0, buffer.pass_stride); - device_sub_ptr color_var_pass( - buffer.mem, variance_to[0] * buffer.pass_stride, 3 * buffer.pass_stride); - device_sub_ptr output_pass(buffer.mem, mean_to[0] * buffer.pass_stride, 3 * buffer.pass_stride); - functions.detect_outliers( - temporary_color.device_pointer, *color_var_pass, *depth_pass, *output_pass); - - if (buffer.use_intensity) { - device_sub_ptr intensity_pass(buffer.mem, 14 * buffer.pass_stride, buffer.pass_stride); - nlm_state.set_parameters(radius, 4, 2.0f, nlm_k_2 * 4.0f, true); - functions.non_local_means(*output_pass, *output_pass, *color_var_pass, *intensity_pass); - } -} - -void DenoisingTask::load_buffer() -{ - device_ptr null_ptr = (device_ptr)0; - - int original_offset = render_buffer.offset; - - int num_passes = buffer.use_intensity ? 15 : 14; - for (int i = 0; i < tile_info->num_frames; i++) { - for (int pass = 0; pass < num_passes; pass++) { - device_sub_ptr to_pass( - buffer.mem, i * buffer.frame_stride + pass * buffer.pass_stride, buffer.pass_stride); - bool is_variance = (pass >= 11) && (pass <= 13); - functions.get_feature( - pass, -1, *to_pass, null_ptr, is_variance ? (1.0f / render_buffer.samples) : 1.0f); - } - render_buffer.offset += render_buffer.frame_stride; - } - - render_buffer.offset = original_offset; -} - -void DenoisingTask::write_buffer() -{ - reconstruction_state.buffer_params = make_int4(target_buffer.offset, - target_buffer.stride, - target_buffer.pass_stride, - target_buffer.denoising_clean_offset); - int num_passes = buffer.use_intensity ? 15 : 14; - for (int pass = 0; pass < num_passes; pass++) { - device_sub_ptr from_pass(buffer.mem, pass * buffer.pass_stride, buffer.pass_stride); - int out_offset = pass + target_buffer.denoising_output_offset; - functions.write_feature(out_offset, *from_pass, target_buffer.ptr); - } -} - -void DenoisingTask::construct_transform() -{ - storage.w = filter_area.z; - storage.h = filter_area.w; - - storage.transform.alloc_to_device(storage.w * storage.h * TRANSFORM_SIZE, false); - storage.rank.alloc_to_device(storage.w * storage.h, false); - - functions.construct_transform(); -} - -void DenoisingTask::reconstruct() -{ - storage.XtWX.alloc_to_device(storage.w * storage.h * XTWX_SIZE, false); - storage.XtWY.alloc_to_device(storage.w * storage.h * XTWY_SIZE, false); - storage.XtWX.zero_to_device(); - storage.XtWY.zero_to_device(); - - reconstruction_state.filter_window = rect_from_shape( - filter_area.x - rect.x, filter_area.y - rect.y, storage.w, storage.h); - int tile_coordinate_offset = filter_area.y * target_buffer.stride + filter_area.x; - reconstruction_state.buffer_params = make_int4(target_buffer.offset + tile_coordinate_offset, - target_buffer.stride, - target_buffer.pass_stride, - target_buffer.denoising_clean_offset); - reconstruction_state.source_w = rect.z - rect.x; - reconstruction_state.source_h = rect.w - rect.y; - - device_sub_ptr color_ptr(buffer.mem, 8 * buffer.pass_stride, 3 * buffer.pass_stride); - device_sub_ptr color_var_ptr(buffer.mem, 11 * buffer.pass_stride, 3 * buffer.pass_stride); - for (int f = 0; f < tile_info->num_frames; f++) { - device_ptr scale_ptr = 0; - device_sub_ptr *scale_sub_ptr = NULL; - if (tile_info->frames[f] != 0 && (tile_info->num_frames > 1)) { - scale_sub_ptr = new device_sub_ptr(buffer.mem, 14 * buffer.pass_stride, buffer.pass_stride); - scale_ptr = **scale_sub_ptr; - } - - functions.accumulate(*color_ptr, *color_var_ptr, scale_ptr, f); - delete scale_sub_ptr; - } - functions.solve(target_buffer.ptr); -} - -void DenoisingTask::run_denoising(RenderTile &tile) -{ - RenderTileNeighbors neighbors(tile); - functions.map_neighbor_tiles(neighbors); - set_render_buffer(neighbors); - - setup_denoising_buffer(); - - if (tile_info->from_render) { - prefilter_shadowing(); - prefilter_features(); - prefilter_color(); - } - else { - load_buffer(); - } - - if (do_filter) { - construct_transform(); - reconstruct(); - } - - if (do_prefilter) { - write_buffer(); - } - - functions.unmap_neighbor_tiles(neighbors); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_denoising.h b/intern/cycles/device/device_denoising.h deleted file mode 100644 index bb8bdfdd225..00000000000 --- a/intern/cycles/device/device_denoising.h +++ /dev/null @@ -1,197 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_DENOISING_H__ -#define __DEVICE_DENOISING_H__ - -#include "device/device.h" - -#include "render/buffers.h" - -#include "kernel/filter/filter_defines.h" - -#include "util/util_profiling.h" - -CCL_NAMESPACE_BEGIN - -class DenoisingTask { - public: - /* Parameters of the denoising algorithm. */ - int radius; - float nlm_k_2; - float pca_threshold; - - /* Parameters of the RenderBuffers. */ - struct RenderBuffers { - int offset; - int pass_stride; - int frame_stride; - int samples; - } render_buffer; - - /* Pointer and parameters of the target buffer. */ - struct TargetBuffer { - int offset; - int stride; - int pass_stride; - int denoising_clean_offset; - int denoising_output_offset; - device_ptr ptr; - } target_buffer; - - TileInfo *tile_info; - device_vector tile_info_mem; - - ProfilingState *profiler; - - int4 rect; - int4 filter_area; - - bool do_prefilter; - bool do_filter; - - struct DeviceFunctions { - function - non_local_means; - function - accumulate; - function solve; - function construct_transform; - - function - combine_halves; - function - divide_shadow; - function - get_feature; - function - detect_outliers; - function write_feature; - function map_neighbor_tiles; - function unmap_neighbor_tiles; - } functions; - - /* Stores state of the current Reconstruction operation, - * which is accessed by the device in order to perform the operation. */ - struct ReconstructionState { - int4 filter_window; - int4 buffer_params; - - int source_w; - int source_h; - } reconstruction_state; - - /* Stores state of the current NLM operation, - * which is accessed by the device in order to perform the operation. */ - struct NLMState { - int r; /* Search radius of the filter. */ - int f; /* Patch size of the filter. */ - float a; /* Variance compensation factor in the MSE estimation. */ - float k_2; /* Squared value of the k parameter of the filter. */ - bool is_color; - - void set_parameters(int r_, int f_, float a_, float k_2_, bool is_color_) - { - r = r_; - f = f_; - a = a_, k_2 = k_2_; - is_color = is_color_; - } - } nlm_state; - - struct Storage { - device_only_memory transform; - device_only_memory rank; - device_only_memory XtWX; - device_only_memory XtWY; - int w; - int h; - - Storage(Device *device) - : transform(device, "denoising transform"), - rank(device, "denoising rank"), - XtWX(device, "denoising XtWX"), - XtWY(device, "denoising XtWY") - { - } - } storage; - - DenoisingTask(Device *device, const DeviceTask &task); - ~DenoisingTask(); - - void run_denoising(RenderTile &tile); - - struct DenoiseBuffers { - int pass_stride; - int passes; - int stride; - int h; - int width; - int frame_stride; - device_only_memory mem; - device_only_memory temporary_mem; - bool use_time; - bool use_intensity; - - bool gpu_temporary_mem; - - DenoiseBuffers(Device *device) - : mem(device, "denoising pixel buffer"), - temporary_mem(device, "denoising temporary mem", true) - { - } - } buffer; - - protected: - Device *device; - - void set_render_buffer(RenderTileNeighbors &neighbors); - void setup_denoising_buffer(); - void prefilter_shadowing(); - void prefilter_features(); - void prefilter_color(); - void construct_transform(); - void reconstruct(); - - void load_buffer(); - void write_buffer(); -}; - -CCL_NAMESPACE_END - -#endif /* __DEVICE_DENOISING_H__ */ diff --git a/intern/cycles/device/device_intern.h b/intern/cycles/device/device_intern.h deleted file mode 100644 index ecc79c5d7ee..00000000000 --- a/intern/cycles/device/device_intern.h +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_INTERN_H__ -#define __DEVICE_INTERN_H__ - -#include "util/util_string.h" -#include "util/util_vector.h" - -CCL_NAMESPACE_BEGIN - -class Device; -class DeviceInfo; -class Profiler; -class Stats; - -Device *device_cpu_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -bool device_opencl_init(); -Device *device_opencl_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -bool device_opencl_compile_kernel(const vector ¶meters); -bool device_cuda_init(); -Device *device_cuda_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -bool device_optix_init(); -Device *device_optix_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); -Device *device_dummy_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); - -Device *device_network_create(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - const char *address); -Device *device_multi_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); - -void device_cpu_info(vector &devices); -void device_opencl_info(vector &devices); -void device_cuda_info(vector &devices); -void device_optix_info(const vector &cuda_devices, vector &devices); -void device_network_info(vector &devices); - -string device_cpu_capabilities(); -string device_opencl_capabilities(); -string device_cuda_capabilities(); - -CCL_NAMESPACE_END - -#endif /* __DEVICE_INTERN_H__ */ diff --git a/intern/cycles/device/device_multi.cpp b/intern/cycles/device/device_multi.cpp deleted file mode 100644 index 85ffa5fcd52..00000000000 --- a/intern/cycles/device/device_multi.cpp +++ /dev/null @@ -1,826 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "bvh/bvh_multi.h" - -#include "device/device.h" -#include "device/device_intern.h" -#include "device/device_network.h" - -#include "render/buffers.h" -#include "render/geometry.h" - -#include "util/util_foreach.h" -#include "util/util_list.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -class MultiDevice : public Device { - public: - struct SubDevice { - Stats stats; - Device *device; - map ptr_map; - int peer_island_index = -1; - }; - - list devices, denoising_devices; - device_ptr unique_key; - vector> 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), - use_denoising(!info.denoising_devices.empty()) - { - foreach (DeviceInfo &subinfo, info.multi_devices) { - /* Always add CPU devices at the back since GPU devices can change - * host memory pointers, which CPU uses as device pointer. */ - SubDevice *sub; - if (subinfo.type == DEVICE_CPU) { - devices.emplace_back(); - sub = &devices.back(); - } - else { - devices.emplace_front(); - sub = &devices.front(); - } - - /* The pointer to 'sub->stats' will stay valid even after new devices - * are added, since 'devices' is a linked list. */ - sub->device = Device::create(subinfo, sub->stats, profiler, background); - } - - foreach (DeviceInfo &subinfo, info.denoising_devices) { - denoising_devices.emplace_front(); - SubDevice *sub = &denoising_devices.front(); - - sub->device = Device::create(subinfo, sub->stats, profiler, background); - } - - /* Build a list of peer islands for the available render devices */ - foreach (SubDevice &sub, devices) { - /* First ensure that every device is in at least once peer island */ - if (sub.peer_island_index < 0) { - peer_islands.emplace_back(); - sub.peer_island_index = (int)peer_islands.size() - 1; - peer_islands[sub.peer_island_index].push_back(&sub); - } - - if (!info.has_peer_memory) { - continue; - } - - /* Second check peer access between devices and fill up the islands accordingly */ - foreach (SubDevice &peer_sub, devices) { - if (peer_sub.peer_island_index < 0 && - peer_sub.device->info.type == sub.device->info.type && - peer_sub.device->check_peer_access(sub.device)) { - peer_sub.peer_island_index = sub.peer_island_index; - peer_islands[sub.peer_island_index].push_back(&peer_sub); - } - } - } - - /* Try to re-use memory when denoising and render devices use the same physical devices - * (e.g. OptiX denoising and CUDA rendering device pointing to the same GPU). - * Ordering has to match as well, so that 'DeviceTask::split' behaves consistent. */ - matching_rendering_and_denoising_devices = denoising_devices.empty() || - (devices.size() == denoising_devices.size()); - if (matching_rendering_and_denoising_devices) { - for (list::iterator device_it = devices.begin(), - denoising_device_it = denoising_devices.begin(); - device_it != devices.end() && denoising_device_it != denoising_devices.end(); - ++device_it, ++denoising_device_it) { - const DeviceInfo &info = device_it->device->info; - const DeviceInfo &denoising_info = denoising_device_it->device->info; - if ((info.type != DEVICE_CUDA && info.type != DEVICE_OPTIX) || - (denoising_info.type != DEVICE_CUDA && denoising_info.type != DEVICE_OPTIX) || - info.num != denoising_info.num) { - matching_rendering_and_denoising_devices = false; - break; - } - } - } - -#ifdef WITH_NETWORK - /* try to add network devices */ - ServerDiscovery discovery(true); - time_sleep(1.0); - - vector servers = discovery.get_server_list(); - - foreach (string &server, servers) { - Device *device = device_network_create(info, stats, profiler, server.c_str()); - if (device) - devices.push_back(SubDevice(device)); - } -#endif - } - - ~MultiDevice() - { - foreach (SubDevice &sub, devices) - delete sub.device; - foreach (SubDevice &sub, denoising_devices) - delete sub.device; - } - - const string &error_message() override - { - error_msg.clear(); - - foreach (SubDevice &sub, devices) - error_msg += sub.device->error_message(); - foreach (SubDevice &sub, denoising_devices) - error_msg += sub.device->error_message(); - - return error_msg; - } - - virtual bool show_samples() const override - { - if (devices.size() > 1) { - return false; - } - return devices.front().device->show_samples(); - } - - virtual BVHLayoutMask get_bvh_layout_mask() const override - { - BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL; - BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE; - foreach (const SubDevice &sub_device, devices) { - BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask(); - bvh_layout_mask &= device_bvh_layout_mask; - bvh_layout_mask_all |= device_bvh_layout_mask; - } - - /* With multiple OptiX devices, every device needs its own acceleration structure */ - if (bvh_layout_mask == BVH_LAYOUT_OPTIX) { - return BVH_LAYOUT_MULTI_OPTIX; - } - - /* When devices do not share a common BVH layout, fall back to creating one for each */ - const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE); - if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) { - return BVH_LAYOUT_MULTI_OPTIX_EMBREE; - } - - return bvh_layout_mask; - } - - bool load_kernels(const DeviceRequestedFeatures &requested_features) override - { - foreach (SubDevice &sub, devices) - 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; - denoising_features.use_denoising = true; - foreach (SubDevice &sub, denoising_devices) - if (!sub.device->load_kernels(denoising_features)) - return false; - } - - return true; - } - - bool wait_for_availability(const DeviceRequestedFeatures &requested_features) override - { - foreach (SubDevice &sub, devices) - if (!sub.device->wait_for_availability(requested_features)) - return false; - - if (requested_features.use_denoising) { - foreach (SubDevice &sub, denoising_devices) - if (!sub.device->wait_for_availability(requested_features)) - return false; - } - - return true; - } - - DeviceKernelStatus get_active_kernel_switch_state() override - { - DeviceKernelStatus result = DEVICE_KERNEL_USING_FEATURE_KERNEL; - - foreach (SubDevice &sub, devices) { - DeviceKernelStatus subresult = sub.device->get_active_kernel_switch_state(); - switch (subresult) { - case DEVICE_KERNEL_FEATURE_KERNEL_INVALID: - case DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE: - return subresult; - - case DEVICE_KERNEL_USING_FEATURE_KERNEL: - case DEVICE_KERNEL_UNKNOWN: - break; - } - } - - return result; - } - - void build_bvh(BVH *bvh, Progress &progress, bool refit) override - { - /* Try to build and share a single acceleration structure, if possible */ - if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2 || bvh->params.bvh_layout == BVH_LAYOUT_EMBREE) { - devices.back().device->build_bvh(bvh, progress, refit); - return; - } - - assert(bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX || - bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE); - - BVHMulti *const bvh_multi = static_cast(bvh); - bvh_multi->sub_bvhs.resize(devices.size()); - - vector geom_bvhs; - geom_bvhs.reserve(bvh->geometry.size()); - foreach (Geometry *geom, bvh->geometry) { - geom_bvhs.push_back(static_cast(geom->bvh)); - } - - /* Broadcast acceleration structure build to all render devices */ - size_t i = 0; - foreach (SubDevice &sub, devices) { - /* Change geometry BVH pointers to the sub BVH */ - for (size_t k = 0; k < bvh->geometry.size(); ++k) { - bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i]; - } - - if (!bvh_multi->sub_bvhs[i]) { - BVHParams params = bvh->params; - if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX) - params.bvh_layout = BVH_LAYOUT_OPTIX; - else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) - params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX : - BVH_LAYOUT_EMBREE; - - /* Skip building a bottom level acceleration structure for non-instanced geometry on Embree - * (since they are put into the top level directly, see bvh_embree.cpp) */ - if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE && - !bvh->geometry[0]->is_instanced()) { - i++; - continue; - } - - bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device); - } - - sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit); - i++; - } - - /* Change geometry BVH pointers back to the multi BVH. */ - for (size_t k = 0; k < bvh->geometry.size(); ++k) { - bvh->geometry[k]->bvh = geom_bvhs[k]; - } - } - - virtual void *osl_memory() override - { - if (devices.size() > 1) { - return NULL; - } - return devices.front().device->osl_memory(); - } - - bool is_resident(device_ptr key, Device *sub_device) override - { - foreach (SubDevice &sub, devices) { - if (sub.device == sub_device) { - return find_matching_mem_device(key, sub)->device == sub_device; - } - } - return false; - } - - SubDevice *find_matching_mem_device(device_ptr key, SubDevice &sub) - { - assert(key != 0 && (sub.peer_island_index >= 0 || sub.ptr_map.find(key) != sub.ptr_map.end())); - - /* Get the memory owner of this key (first try current device, then peer devices) */ - SubDevice *owner_sub = ⊂ - if (owner_sub->ptr_map.find(key) == owner_sub->ptr_map.end()) { - foreach (SubDevice *island_sub, peer_islands[sub.peer_island_index]) { - if (island_sub != owner_sub && - island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) { - owner_sub = island_sub; - } - } - } - return owner_sub; - } - - SubDevice *find_suitable_mem_device(device_ptr key, const vector &island) - { - assert(!island.empty()); - - /* Get the memory owner of this key or the device with the lowest memory usage when new */ - SubDevice *owner_sub = island.front(); - foreach (SubDevice *island_sub, island) { - if (key ? (island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) : - (island_sub->device->stats.mem_used < owner_sub->device->stats.mem_used)) { - owner_sub = island_sub; - } - } - return owner_sub; - } - - inline device_ptr find_matching_mem(device_ptr key, SubDevice &sub) - { - return find_matching_mem_device(key, sub)->ptr_map[key]; - } - - void mem_alloc(device_memory &mem) override - { - device_ptr key = unique_key++; - - if (mem.type == MEM_PIXELS) { - /* Always allocate pixels memory on all devices - * This is necessary to ensure PBOs are registered everywhere, which FILM_CONVERT uses */ - foreach (SubDevice &sub, devices) { - mem.device = sub.device; - mem.device_pointer = 0; - mem.device_size = 0; - - sub.device->mem_alloc(mem); - sub.ptr_map[key] = mem.device_pointer; - } - } - else { - assert(mem.type == MEM_READ_ONLY || mem.type == MEM_READ_WRITE || - mem.type == MEM_DEVICE_ONLY); - /* The remaining memory types can be distributed across devices */ - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_suitable_mem_device(key, island); - mem.device = owner_sub->device; - mem.device_pointer = 0; - mem.device_size = 0; - - owner_sub->device->mem_alloc(mem); - owner_sub->ptr_map[key] = mem.device_pointer; - } - } - - mem.device = this; - mem.device_pointer = key; - stats.mem_alloc(mem.device_size); - } - - void mem_copy_to(device_memory &mem) override - { - device_ptr existing_key = mem.device_pointer; - device_ptr key = (existing_key) ? existing_key : unique_key++; - 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 && use_denoising) { - foreach (SubDevice &sub, devices) { - mem.device = sub.device; - mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - sub.device->mem_copy_to(mem); - sub.ptr_map[key] = mem.device_pointer; - } - } - else { - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); - mem.device = owner_sub->device; - mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - owner_sub->device->mem_copy_to(mem); - owner_sub->ptr_map[key] = mem.device_pointer; - - if (mem.type == MEM_GLOBAL || mem.type == MEM_TEXTURE) { - /* Need to create texture objects and update pointer in kernel globals on all devices */ - foreach (SubDevice *island_sub, island) { - if (island_sub != owner_sub) { - island_sub->device->mem_copy_to(mem); - } - } - } - } - } - - mem.device = this; - mem.device_pointer = key; - stats.mem_alloc(mem.device_size - existing_size); - } - - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) override - { - device_ptr key = mem.device_pointer; - int i = 0, sub_h = h / devices.size(); - - foreach (SubDevice &sub, devices) { - int sy = y + i * sub_h; - int sh = (i == (int)devices.size() - 1) ? h - sub_h * i : sub_h; - - SubDevice *owner_sub = find_matching_mem_device(key, sub); - mem.device = owner_sub->device; - mem.device_pointer = owner_sub->ptr_map[key]; - - owner_sub->device->mem_copy_from(mem, sy, w, sh, elem); - i++; - } - - mem.device = this; - mem.device_pointer = key; - } - - void mem_zero(device_memory &mem) override - { - device_ptr existing_key = mem.device_pointer; - device_ptr key = (existing_key) ? existing_key : unique_key++; - size_t existing_size = mem.device_size; - - /* 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 && use_denoising) { - vector device_pointers; - device_pointers.reserve(devices.size()); - - foreach (SubDevice &sub, devices) { - mem.device = sub.device; - mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - sub.device->mem_zero(mem); - sub.ptr_map[key] = mem.device_pointer; - - device_pointers.push_back(mem.device_pointer); - } - foreach (SubDevice &sub, denoising_devices) { - if (matching_rendering_and_denoising_devices) { - sub.ptr_map[key] = device_pointers.front(); - device_pointers.erase(device_pointers.begin()); - } - else { - mem.device = sub.device; - mem.device_pointer = (existing_key) ? sub.ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - sub.device->mem_zero(mem); - sub.ptr_map[key] = mem.device_pointer; - } - } - } - else { - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); - mem.device = owner_sub->device; - mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; - mem.device_size = existing_size; - - owner_sub->device->mem_zero(mem); - owner_sub->ptr_map[key] = mem.device_pointer; - } - } - - mem.device = this; - mem.device_pointer = key; - stats.mem_alloc(mem.device_size - existing_size); - } - - void mem_free(device_memory &mem) override - { - device_ptr key = mem.device_pointer; - size_t existing_size = mem.device_size; - - /* Free memory that was allocated for all devices (see above) on each device */ - 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]; - mem.device_size = existing_size; - - sub.device->mem_free(mem); - sub.ptr_map.erase(sub.ptr_map.find(key)); - } - foreach (SubDevice &sub, denoising_devices) { - if (matching_rendering_and_denoising_devices) { - sub.ptr_map.erase(key); - } - else { - mem.device = sub.device; - mem.device_pointer = sub.ptr_map[key]; - mem.device_size = existing_size; - - sub.device->mem_free(mem); - sub.ptr_map.erase(sub.ptr_map.find(key)); - } - } - } - else { - foreach (const vector &island, peer_islands) { - SubDevice *owner_sub = find_matching_mem_device(key, *island.front()); - mem.device = owner_sub->device; - mem.device_pointer = owner_sub->ptr_map[key]; - mem.device_size = existing_size; - - owner_sub->device->mem_free(mem); - owner_sub->ptr_map.erase(owner_sub->ptr_map.find(key)); - - if (mem.type == MEM_TEXTURE) { - /* Free texture objects on all devices */ - foreach (SubDevice *island_sub, island) { - if (island_sub != owner_sub) { - island_sub->device->mem_free(mem); - } - } - } - } - } - - mem.device = this; - mem.device_pointer = 0; - mem.device_size = 0; - stats.mem_free(existing_size); - } - - void const_copy_to(const char *name, void *host, size_t size) override - { - foreach (SubDevice &sub, devices) - sub.device->const_copy_to(name, host, size); - } - - void draw_pixels(device_memory &rgba, - int y, - int w, - int h, - int width, - int height, - int dx, - int dy, - int dw, - int dh, - bool transparent, - const DeviceDrawParams &draw_params) override - { - assert(rgba.type == MEM_PIXELS); - - device_ptr key = rgba.device_pointer; - int i = 0, sub_h = h / devices.size(); - int sub_height = height / devices.size(); - - foreach (SubDevice &sub, devices) { - int sy = y + i * sub_h; - int sh = (i == (int)devices.size() - 1) ? h - sub_h * i : sub_h; - int sheight = (i == (int)devices.size() - 1) ? height - sub_height * i : sub_height; - int sdy = dy + i * sub_height; - /* adjust math for w/width */ - - rgba.device_pointer = sub.ptr_map[key]; - sub.device->draw_pixels( - rgba, sy, w, sh, width, sheight, dx, sdy, dw, dh, transparent, draw_params); - i++; - } - - rgba.device_pointer = key; - } - - void map_tile(Device *sub_device, RenderTile &tile) override - { - if (!tile.buffer) { - return; - } - - foreach (SubDevice &sub, devices) { - if (sub.device == sub_device) { - tile.buffer = find_matching_mem(tile.buffer, sub); - return; - } - } - - foreach (SubDevice &sub, denoising_devices) { - if (sub.device == sub_device) { - tile.buffer = sub.ptr_map[tile.buffer]; - return; - } - } - } - - int device_number(Device *sub_device) override - { - int i = 0; - - foreach (SubDevice &sub, devices) { - if (sub.device == sub_device) - return i; - i++; - } - - foreach (SubDevice &sub, denoising_devices) { - if (sub.device == sub_device) - return i; - i++; - } - - return -1; - } - - void map_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override - { - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &tile = neighbors.tiles[i]; - - if (!tile.buffers) { - continue; - } - - device_vector &mem = tile.buffers->buffer; - tile.buffer = mem.device_pointer; - - if (mem.device == this && matching_rendering_and_denoising_devices) { - /* Skip unnecessary copies in viewport mode (buffer covers the - * whole image), but still need to fix up the tile device pointer. */ - map_tile(sub_device, tile); - continue; - } - - /* If the tile was rendered on another device, copy its memory to - * to the current device now, for the duration of the denoising task. - * Note that this temporarily modifies the RenderBuffers and calls - * the device, so this function is not thread safe. */ - if (mem.device != sub_device) { - /* Only copy from device to host once. This is faster, but - * also required for the case where a CPU thread is denoising - * a tile rendered on the GPU. In that case we have to avoid - * overwriting the buffer being de-noised by the CPU thread. */ - if (!tile.buffers->map_neighbor_copied) { - tile.buffers->map_neighbor_copied = true; - mem.copy_from_device(); - } - - if (mem.device == this) { - /* Can re-use memory if tile is already allocated on the sub device. */ - map_tile(sub_device, tile); - mem.swap_device(sub_device, mem.device_size, tile.buffer); - } - else { - mem.swap_device(sub_device, 0, 0); - } - - mem.copy_to_device(); - - tile.buffer = mem.device_pointer; - tile.device_size = mem.device_size; - - mem.restore_device(); - } - } - } - - void unmap_neighbor_tiles(Device *sub_device, RenderTileNeighbors &neighbors) override - { - RenderTile &target_tile = neighbors.target; - device_vector &mem = target_tile.buffers->buffer; - - if (mem.device == this && matching_rendering_and_denoising_devices) { - return; - } - - /* Copy denoised result back to the host. */ - mem.swap_device(sub_device, target_tile.device_size, target_tile.buffer); - mem.copy_from_device(); - mem.restore_device(); - - /* Copy denoised result to the original device. */ - mem.copy_to_device(); - - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - RenderTile &tile = neighbors.tiles[i]; - if (!tile.buffers) { - continue; - } - - device_vector &mem = tile.buffers->buffer; - - if (mem.device != sub_device && mem.device != this) { - /* Free up memory again if it was allocated for the copy above. */ - mem.swap_device(sub_device, tile.device_size, tile.buffer); - sub_device->mem_free(mem); - mem.restore_device(); - } - } - } - - int get_split_task_count(DeviceTask &task) override - { - int total_tasks = 0; - list tasks; - task.split(tasks, devices.size()); - foreach (SubDevice &sub, devices) { - if (!tasks.empty()) { - DeviceTask subtask = tasks.front(); - tasks.pop_front(); - - total_tasks += sub.device->get_split_task_count(subtask); - } - } - return total_tasks; - } - - void task_add(DeviceTask &task) override - { - list task_devices = devices; - if (!denoising_devices.empty()) { - if (task.type == DeviceTask::DENOISE_BUFFER) { - /* Denoising tasks should be redirected to the denoising devices entirely. */ - task_devices = denoising_devices; - } - else if (task.type == DeviceTask::RENDER && (task.tile_types & RenderTile::DENOISE)) { - const uint tile_types = task.tile_types; - /* For normal rendering tasks only redirect the denoising part to the denoising devices. - * Do not need to split the task here, since they all run through 'acquire_tile'. */ - task.tile_types = RenderTile::DENOISE; - foreach (SubDevice &sub, denoising_devices) { - sub.device->task_add(task); - } - /* Rendering itself should still be executed on the rendering devices. */ - task.tile_types = tile_types ^ RenderTile::DENOISE; - } - } - - list tasks; - task.split(tasks, task_devices.size()); - - foreach (SubDevice &sub, task_devices) { - if (!tasks.empty()) { - DeviceTask subtask = tasks.front(); - tasks.pop_front(); - - if (task.buffer) - subtask.buffer = find_matching_mem(task.buffer, sub); - if (task.rgba_byte) - subtask.rgba_byte = sub.ptr_map[task.rgba_byte]; - if (task.rgba_half) - subtask.rgba_half = sub.ptr_map[task.rgba_half]; - if (task.shader_input) - subtask.shader_input = find_matching_mem(task.shader_input, sub); - if (task.shader_output) - subtask.shader_output = find_matching_mem(task.shader_output, sub); - - sub.device->task_add(subtask); - - if (task.buffers && task.buffers->buffer.device == this) { - /* Synchronize access to RenderBuffers, since 'map_neighbor_tiles' is not thread-safe. */ - sub.device->task_wait(); - } - } - } - } - - void task_wait() override - { - foreach (SubDevice &sub, devices) - sub.device->task_wait(); - foreach (SubDevice &sub, denoising_devices) - sub.device->task_wait(); - } - - void task_cancel() override - { - foreach (SubDevice &sub, devices) - sub.device->task_cancel(); - foreach (SubDevice &sub, denoising_devices) - sub.device->task_cancel(); - } -}; - -Device *device_multi_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return new MultiDevice(info, stats, profiler, background); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_network.cpp b/intern/cycles/device/device_network.cpp deleted file mode 100644 index 8904b517e92..00000000000 --- a/intern/cycles/device/device_network.cpp +++ /dev/null @@ -1,812 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "device/device_network.h" -#include "device/device.h" -#include "device/device_intern.h" - -#include "util/util_foreach.h" -#include "util/util_logging.h" - -#if defined(WITH_NETWORK) - -CCL_NAMESPACE_BEGIN - -typedef map PtrMap; -typedef vector DataVector; -typedef map DataMap; - -/* tile list */ -typedef vector TileList; - -/* search a list of tiles and find the one that matches the passed render tile */ -static TileList::iterator tile_list_find(TileList &tile_list, RenderTile &tile) -{ - for (TileList::iterator it = tile_list.begin(); it != tile_list.end(); ++it) - if (tile.x == it->x && tile.y == it->y && tile.start_sample == it->start_sample) - return it; - return tile_list.end(); -} - -class NetworkDevice : public Device { - public: - boost::asio::io_service io_service; - tcp::socket socket; - device_ptr mem_counter; - DeviceTask the_task; /* todo: handle multiple tasks */ - - thread_mutex rpc_lock; - - virtual bool show_samples() const - { - return false; - } - - NetworkDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, const char *address) - : Device(info, stats, profiler, true), socket(io_service) - { - error_func = NetworkError(); - stringstream portstr; - portstr << SERVER_PORT; - - tcp::resolver resolver(io_service); - tcp::resolver::query query(address, portstr.str()); - tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); - tcp::resolver::iterator end; - - boost::system::error_code error = boost::asio::error::host_not_found; - while (error && endpoint_iterator != end) { - socket.close(); - socket.connect(*endpoint_iterator++, error); - } - - if (error) - error_func.network_error(error.message()); - - mem_counter = 0; - } - - ~NetworkDevice() - { - RPCSend snd(socket, &error_func, "stop"); - snd.write(); - } - - virtual BVHLayoutMask get_bvh_layout_mask() const - { - return BVH_LAYOUT_BVH2; - } - - void mem_alloc(device_memory &mem) - { - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - } - - thread_scoped_lock lock(rpc_lock); - - mem.device_pointer = ++mem_counter; - - RPCSend snd(socket, &error_func, "mem_alloc"); - snd.add(mem); - snd.write(); - } - - void mem_copy_to(device_memory &mem) - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "mem_copy_to"); - - snd.add(mem); - snd.write(); - snd.write_buffer(mem.host_pointer, mem.memory_size()); - } - - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem) - { - thread_scoped_lock lock(rpc_lock); - - size_t data_size = mem.memory_size(); - - RPCSend snd(socket, &error_func, "mem_copy_from"); - - snd.add(mem); - snd.add(y); - snd.add(w); - snd.add(h); - snd.add(elem); - snd.write(); - - RPCReceive rcv(socket, &error_func); - rcv.read_buffer(mem.host_pointer, data_size); - } - - void mem_zero(device_memory &mem) - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "mem_zero"); - - snd.add(mem); - snd.write(); - } - - void mem_free(device_memory &mem) - { - if (mem.device_pointer) { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "mem_free"); - - snd.add(mem); - snd.write(); - - mem.device_pointer = 0; - } - } - - void const_copy_to(const char *name, void *host, size_t size) - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "const_copy_to"); - - string name_string(name); - - snd.add(name_string); - snd.add(size); - snd.write(); - snd.write_buffer(host, size); - } - - bool load_kernels(const DeviceRequestedFeatures &requested_features) - { - if (error_func.have_error()) - return false; - - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "load_kernels"); - snd.add(requested_features.experimental); - snd.add(requested_features.max_closure); - snd.add(requested_features.max_nodes_group); - snd.add(requested_features.nodes_features); - snd.write(); - - bool result; - RPCReceive rcv(socket, &error_func); - rcv.read(result); - - return result; - } - - void task_add(DeviceTask &task) - { - thread_scoped_lock lock(rpc_lock); - - the_task = task; - - RPCSend snd(socket, &error_func, "task_add"); - snd.add(task); - snd.write(); - } - - void task_wait() - { - thread_scoped_lock lock(rpc_lock); - - RPCSend snd(socket, &error_func, "task_wait"); - snd.write(); - - lock.unlock(); - - TileList the_tiles; - - /* todo: run this threaded for connecting to multiple clients */ - for (;;) { - if (error_func.have_error()) - break; - - RenderTile tile; - - lock.lock(); - RPCReceive rcv(socket, &error_func); - - if (rcv.name == "acquire_tile") { - lock.unlock(); - - /* todo: watch out for recursive calls! */ - if (the_task.acquire_tile(this, tile)) { /* write return as bool */ - the_tiles.push_back(tile); - - lock.lock(); - RPCSend snd(socket, &error_func, "acquire_tile"); - snd.add(tile); - snd.write(); - lock.unlock(); - } - else { - lock.lock(); - RPCSend snd(socket, &error_func, "acquire_tile_none"); - snd.write(); - lock.unlock(); - } - } - else if (rcv.name == "release_tile") { - rcv.read(tile); - lock.unlock(); - - TileList::iterator it = tile_list_find(the_tiles, tile); - if (it != the_tiles.end()) { - tile.buffers = it->buffers; - the_tiles.erase(it); - } - - assert(tile.buffers != NULL); - - the_task.release_tile(tile); - - lock.lock(); - RPCSend snd(socket, &error_func, "release_tile"); - snd.write(); - lock.unlock(); - } - else if (rcv.name == "task_wait_done") { - lock.unlock(); - break; - } - else - lock.unlock(); - } - } - - void task_cancel() - { - thread_scoped_lock lock(rpc_lock); - RPCSend snd(socket, &error_func, "task_cancel"); - snd.write(); - } - - int get_split_task_count(DeviceTask &) - { - return 1; - } - - private: - NetworkError error_func; -}; - -Device *device_network_create(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - const char *address) -{ - return new NetworkDevice(info, stats, profiler, address); -} - -void device_network_info(vector &devices) -{ - DeviceInfo info; - - info.type = DEVICE_NETWORK; - info.description = "Network Device"; - info.id = "NETWORK"; - info.num = 0; - - /* todo: get this info from device */ - info.has_volume_decoupled = false; - info.has_adaptive_stop_per_sample = false; - info.has_osl = false; - info.denoisers = DENOISER_NONE; - - devices.push_back(info); -} - -class DeviceServer { - public: - thread_mutex rpc_lock; - - void network_error(const string &message) - { - error_func.network_error(message); - } - - bool have_error() - { - return error_func.have_error(); - } - - DeviceServer(Device *device_, tcp::socket &socket_) - : device(device_), socket(socket_), stop(false), blocked_waiting(false) - { - error_func = NetworkError(); - } - - void listen() - { - /* receive remote function calls */ - for (;;) { - listen_step(); - - if (stop) - break; - } - } - - protected: - void listen_step() - { - thread_scoped_lock lock(rpc_lock); - RPCReceive rcv(socket, &error_func); - - if (rcv.name == "stop") - stop = true; - else - process(rcv, lock); - } - - /* create a memory buffer for a device buffer and insert it into mem_data */ - DataVector &data_vector_insert(device_ptr client_pointer, size_t data_size) - { - /* create a new DataVector and insert it into mem_data */ - pair data_ins = mem_data.insert( - DataMap::value_type(client_pointer, DataVector())); - - /* make sure it was a unique insertion */ - assert(data_ins.second); - - /* get a reference to the inserted vector */ - DataVector &data_v = data_ins.first->second; - - /* size the vector */ - data_v.resize(data_size); - - return data_v; - } - - DataVector &data_vector_find(device_ptr client_pointer) - { - DataMap::iterator i = mem_data.find(client_pointer); - assert(i != mem_data.end()); - return i->second; - } - - /* setup mapping and reverse mapping of client_pointer<->real_pointer */ - void pointer_mapping_insert(device_ptr client_pointer, device_ptr real_pointer) - { - pair mapins; - - /* insert mapping from client pointer to our real device pointer */ - mapins = ptr_map.insert(PtrMap::value_type(client_pointer, real_pointer)); - assert(mapins.second); - - /* insert reverse mapping from real our device pointer to client pointer */ - mapins = ptr_imap.insert(PtrMap::value_type(real_pointer, client_pointer)); - assert(mapins.second); - } - - device_ptr device_ptr_from_client_pointer(device_ptr client_pointer) - { - PtrMap::iterator i = ptr_map.find(client_pointer); - assert(i != ptr_map.end()); - return i->second; - } - - device_ptr device_ptr_from_client_pointer_erase(device_ptr client_pointer) - { - PtrMap::iterator i = ptr_map.find(client_pointer); - assert(i != ptr_map.end()); - - device_ptr result = i->second; - - /* erase the mapping */ - ptr_map.erase(i); - - /* erase the reverse mapping */ - PtrMap::iterator irev = ptr_imap.find(result); - assert(irev != ptr_imap.end()); - ptr_imap.erase(irev); - - /* erase the data vector */ - DataMap::iterator idata = mem_data.find(client_pointer); - assert(idata != mem_data.end()); - mem_data.erase(idata); - - return result; - } - - /* note that the lock must be already acquired upon entry. - * This is necessary because the caller often peeks at - * the header and delegates control to here when it doesn't - * specifically handle the current RPC. - * The lock must be unlocked before returning */ - void process(RPCReceive &rcv, thread_scoped_lock &lock) - { - if (rcv.name == "mem_alloc") { - string name; - network_device_memory mem(device); - rcv.read(mem, name); - lock.unlock(); - - /* Allocate host side data buffer. */ - size_t data_size = mem.memory_size(); - device_ptr client_pointer = mem.device_pointer; - - DataVector &data_v = data_vector_insert(client_pointer, data_size); - mem.host_pointer = (data_size) ? (void *)&(data_v[0]) : 0; - - /* Perform the allocation on the actual device. */ - device->mem_alloc(mem); - - /* Store a mapping to/from client_pointer and real device pointer. */ - pointer_mapping_insert(client_pointer, mem.device_pointer); - } - else if (rcv.name == "mem_copy_to") { - string name; - network_device_memory mem(device); - rcv.read(mem, name); - lock.unlock(); - - size_t data_size = mem.memory_size(); - device_ptr client_pointer = mem.device_pointer; - - if (client_pointer) { - /* Lookup existing host side data buffer. */ - DataVector &data_v = data_vector_find(client_pointer); - mem.host_pointer = (void *)&data_v[0]; - - /* Translate the client pointer to a real device pointer. */ - mem.device_pointer = device_ptr_from_client_pointer(client_pointer); - } - else { - /* Allocate host side data buffer. */ - DataVector &data_v = data_vector_insert(client_pointer, data_size); - mem.host_pointer = (data_size) ? (void *)&(data_v[0]) : 0; - } - - /* Copy data from network into memory buffer. */ - rcv.read_buffer((uint8_t *)mem.host_pointer, data_size); - - /* Copy the data from the memory buffer to the device buffer. */ - device->mem_copy_to(mem); - - if (!client_pointer) { - /* Store a mapping to/from client_pointer and real device pointer. */ - pointer_mapping_insert(client_pointer, mem.device_pointer); - } - } - else if (rcv.name == "mem_copy_from") { - string name; - network_device_memory mem(device); - int y, w, h, elem; - - rcv.read(mem, name); - rcv.read(y); - rcv.read(w); - rcv.read(h); - rcv.read(elem); - - device_ptr client_pointer = mem.device_pointer; - mem.device_pointer = device_ptr_from_client_pointer(client_pointer); - - DataVector &data_v = data_vector_find(client_pointer); - - mem.host_pointer = (device_ptr) & (data_v[0]); - - device->mem_copy_from(mem, y, w, h, elem); - - size_t data_size = mem.memory_size(); - - RPCSend snd(socket, &error_func, "mem_copy_from"); - snd.write(); - snd.write_buffer((uint8_t *)mem.host_pointer, data_size); - lock.unlock(); - } - else if (rcv.name == "mem_zero") { - string name; - network_device_memory mem(device); - rcv.read(mem, name); - lock.unlock(); - - size_t data_size = mem.memory_size(); - device_ptr client_pointer = mem.device_pointer; - - if (client_pointer) { - /* Lookup existing host side data buffer. */ - DataVector &data_v = data_vector_find(client_pointer); - mem.host_pointer = (void *)&data_v[0]; - - /* Translate the client pointer to a real device pointer. */ - mem.device_pointer = device_ptr_from_client_pointer(client_pointer); - } - else { - /* Allocate host side data buffer. */ - DataVector &data_v = data_vector_insert(client_pointer, data_size); - mem.host_pointer = (void *) ? (device_ptr) & (data_v[0]) : 0; - } - - /* Zero memory. */ - device->mem_zero(mem); - - if (!client_pointer) { - /* Store a mapping to/from client_pointer and real device pointer. */ - pointer_mapping_insert(client_pointer, mem.device_pointer); - } - } - else if (rcv.name == "mem_free") { - string name; - network_device_memory mem(device); - - rcv.read(mem, name); - lock.unlock(); - - device_ptr client_pointer = mem.device_pointer; - - mem.device_pointer = device_ptr_from_client_pointer_erase(client_pointer); - - device->mem_free(mem); - } - else if (rcv.name == "const_copy_to") { - string name_string; - size_t size; - - rcv.read(name_string); - rcv.read(size); - - vector host_vector(size); - rcv.read_buffer(&host_vector[0], size); - lock.unlock(); - - device->const_copy_to(name_string.c_str(), &host_vector[0], size); - } - else if (rcv.name == "load_kernels") { - DeviceRequestedFeatures requested_features; - rcv.read(requested_features.experimental); - rcv.read(requested_features.max_closure); - rcv.read(requested_features.max_nodes_group); - rcv.read(requested_features.nodes_features); - - bool result; - result = device->load_kernels(requested_features); - RPCSend snd(socket, &error_func, "load_kernels"); - snd.add(result); - snd.write(); - lock.unlock(); - } - else if (rcv.name == "task_add") { - DeviceTask task; - - rcv.read(task); - lock.unlock(); - - if (task.buffer) - task.buffer = device_ptr_from_client_pointer(task.buffer); - - if (task.rgba_half) - task.rgba_half = device_ptr_from_client_pointer(task.rgba_half); - - if (task.rgba_byte) - task.rgba_byte = device_ptr_from_client_pointer(task.rgba_byte); - - if (task.shader_input) - task.shader_input = device_ptr_from_client_pointer(task.shader_input); - - if (task.shader_output) - task.shader_output = device_ptr_from_client_pointer(task.shader_output); - - task.acquire_tile = function_bind(&DeviceServer::task_acquire_tile, this, _1, _2); - task.release_tile = function_bind(&DeviceServer::task_release_tile, this, _1); - task.update_progress_sample = function_bind(&DeviceServer::task_update_progress_sample, - this); - task.update_tile_sample = function_bind(&DeviceServer::task_update_tile_sample, this, _1); - task.get_cancel = function_bind(&DeviceServer::task_get_cancel, this); - - device->task_add(task); - } - else if (rcv.name == "task_wait") { - lock.unlock(); - - blocked_waiting = true; - device->task_wait(); - blocked_waiting = false; - - lock.lock(); - RPCSend snd(socket, &error_func, "task_wait_done"); - snd.write(); - lock.unlock(); - } - else if (rcv.name == "task_cancel") { - lock.unlock(); - device->task_cancel(); - } - else if (rcv.name == "acquire_tile") { - AcquireEntry entry; - entry.name = rcv.name; - rcv.read(entry.tile); - acquire_queue.push_back(entry); - lock.unlock(); - } - else if (rcv.name == "acquire_tile_none") { - AcquireEntry entry; - entry.name = rcv.name; - acquire_queue.push_back(entry); - lock.unlock(); - } - else if (rcv.name == "release_tile") { - AcquireEntry entry; - entry.name = rcv.name; - acquire_queue.push_back(entry); - lock.unlock(); - } - else { - cout << "Error: unexpected RPC receive call \"" + rcv.name + "\"\n"; - lock.unlock(); - } - } - - bool task_acquire_tile(Device *, RenderTile &tile) - { - thread_scoped_lock acquire_lock(acquire_mutex); - - bool result = false; - - RPCSend snd(socket, &error_func, "acquire_tile"); - snd.write(); - - do { - if (blocked_waiting) - listen_step(); - - /* todo: avoid busy wait loop */ - thread_scoped_lock lock(rpc_lock); - - if (!acquire_queue.empty()) { - AcquireEntry entry = acquire_queue.front(); - acquire_queue.pop_front(); - - if (entry.name == "acquire_tile") { - tile = entry.tile; - - if (tile.buffer) - tile.buffer = ptr_map[tile.buffer]; - - result = true; - break; - } - else if (entry.name == "acquire_tile_none") { - break; - } - else { - cout << "Error: unexpected acquire RPC receive call \"" + entry.name + "\"\n"; - } - } - } while (acquire_queue.empty() && !stop && !have_error()); - - return result; - } - - void task_update_progress_sample() - { - ; /* skip */ - } - - void task_update_tile_sample(RenderTile &) - { - ; /* skip */ - } - - void task_release_tile(RenderTile &tile) - { - thread_scoped_lock acquire_lock(acquire_mutex); - - if (tile.buffer) - tile.buffer = ptr_imap[tile.buffer]; - - { - thread_scoped_lock lock(rpc_lock); - RPCSend snd(socket, &error_func, "release_tile"); - snd.add(tile); - snd.write(); - lock.unlock(); - } - - do { - if (blocked_waiting) - listen_step(); - - /* todo: avoid busy wait loop */ - thread_scoped_lock lock(rpc_lock); - - if (!acquire_queue.empty()) { - AcquireEntry entry = acquire_queue.front(); - acquire_queue.pop_front(); - - if (entry.name == "release_tile") { - lock.unlock(); - break; - } - else { - cout << "Error: unexpected release RPC receive call \"" + entry.name + "\"\n"; - } - } - } while (acquire_queue.empty() && !stop); - } - - bool task_get_cancel() - { - return false; - } - - /* properties */ - Device *device; - tcp::socket &socket; - - /* mapping of remote to local pointer */ - PtrMap ptr_map; - PtrMap ptr_imap; - DataMap mem_data; - - struct AcquireEntry { - string name; - RenderTile tile; - }; - - thread_mutex acquire_mutex; - list acquire_queue; - - bool stop; - bool blocked_waiting; - - private: - NetworkError error_func; - - /* todo: free memory and device (osl) on network error */ -}; - -void Device::server_run() -{ - try { - /* starts thread that responds to discovery requests */ - ServerDiscovery discovery; - - for (;;) { - /* accept connection */ - boost::asio::io_service io_service; - tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), SERVER_PORT)); - - tcp::socket socket(io_service); - acceptor.accept(socket); - - string remote_address = socket.remote_endpoint().address().to_string(); - printf("Connected to remote client at: %s\n", remote_address.c_str()); - - DeviceServer server(this, socket); - server.listen(); - - printf("Disconnected.\n"); - } - } - catch (exception &e) { - fprintf(stderr, "Network server exception: %s\n", e.what()); - } -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/device_network.h b/intern/cycles/device/device_network.h deleted file mode 100644 index b3a0f6daa57..00000000000 --- a/intern/cycles/device/device_network.h +++ /dev/null @@ -1,490 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_NETWORK_H__ -#define __DEVICE_NETWORK_H__ - -#ifdef WITH_NETWORK - -# include -# include -# include -# include -# include -# include -# include -# include -# include - -# include -# include -# include - -# include "render/buffers.h" - -# include "util/util_foreach.h" -# include "util/util_list.h" -# include "util/util_map.h" -# include "util/util_param.h" -# include "util/util_string.h" - -CCL_NAMESPACE_BEGIN - -using std::cerr; -using std::cout; -using std::exception; -using std::hex; -using std::setw; - -using boost::asio::ip::tcp; - -static const int SERVER_PORT = 5120; -static const int DISCOVER_PORT = 5121; -static const string DISCOVER_REQUEST_MSG = "REQUEST_RENDER_SERVER_IP"; -static const string DISCOVER_REPLY_MSG = "REPLY_RENDER_SERVER_IP"; - -# if 0 -typedef boost::archive::text_oarchive o_archive; -typedef boost::archive::text_iarchive i_archive; -# else -typedef boost::archive::binary_oarchive o_archive; -typedef boost::archive::binary_iarchive i_archive; -# endif - -/* Serialization of device memory */ - -class network_device_memory : public device_memory { - public: - network_device_memory(Device *device) : device_memory(device, "", MEM_READ_ONLY) - { - } - - ~network_device_memory() - { - device_pointer = 0; - }; - - vector local_data; -}; - -/* Common network error function / object for both DeviceNetwork and DeviceServer. */ -class NetworkError { - public: - NetworkError() - { - error = ""; - error_count = 0; - } - - ~NetworkError() - { - } - - void network_error(const string &message) - { - error = message; - error_count += 1; - } - - bool have_error() - { - return true ? error_count > 0 : false; - } - - private: - string error; - int error_count; -}; - -/* Remote procedure call Send */ - -class RPCSend { - public: - RPCSend(tcp::socket &socket_, NetworkError *e, const string &name_ = "") - : name(name_), socket(socket_), archive(archive_stream), sent(false) - { - archive &name_; - error_func = e; - fprintf(stderr, "rpc send %s\n", name.c_str()); - } - - ~RPCSend() - { - } - - void add(const device_memory &mem) - { - archive &mem.data_type &mem.data_elements &mem.data_size; - archive &mem.data_width &mem.data_height &mem.data_depth &mem.device_pointer; - archive &mem.type &string(mem.name); - archive &mem.interpolation &mem.extension; - archive &mem.device_pointer; - } - - template void add(const T &data) - { - archive &data; - } - - void add(const DeviceTask &task) - { - int type = (int)task.type; - archive &type &task.x &task.y &task.w &task.h; - archive &task.rgba_byte &task.rgba_half &task.buffer &task.sample &task.num_samples; - archive &task.offset &task.stride; - archive &task.shader_input &task.shader_output &task.shader_eval_type; - archive &task.shader_x &task.shader_w; - archive &task.need_finish_queue; - } - - void add(const RenderTile &tile) - { - archive &tile.x &tile.y &tile.w &tile.h; - archive &tile.start_sample &tile.num_samples &tile.sample; - archive &tile.resolution &tile.offset &tile.stride; - archive &tile.buffer; - } - - void write() - { - boost::system::error_code error; - - /* get string from stream */ - string archive_str = archive_stream.str(); - - /* first send fixed size header with size of following data */ - ostringstream header_stream; - header_stream << setw(8) << hex << archive_str.size(); - string header_str = header_stream.str(); - - boost::asio::write( - socket, boost::asio::buffer(header_str), boost::asio::transfer_all(), error); - - if (error.value()) - error_func->network_error(error.message()); - - /* then send actual data */ - boost::asio::write( - socket, boost::asio::buffer(archive_str), boost::asio::transfer_all(), error); - - if (error.value()) - error_func->network_error(error.message()); - - sent = true; - } - - void write_buffer(void *buffer, size_t size) - { - boost::system::error_code error; - - boost::asio::write( - socket, boost::asio::buffer(buffer, size), boost::asio::transfer_all(), error); - - if (error.value()) - error_func->network_error(error.message()); - } - - protected: - string name; - tcp::socket &socket; - ostringstream archive_stream; - o_archive archive; - bool sent; - NetworkError *error_func; -}; - -/* Remote procedure call Receive */ - -class RPCReceive { - public: - RPCReceive(tcp::socket &socket_, NetworkError *e) - : socket(socket_), archive_stream(NULL), archive(NULL) - { - error_func = e; - /* read head with fixed size */ - vector header(8); - boost::system::error_code error; - size_t len = boost::asio::read(socket, boost::asio::buffer(header), error); - - if (error.value()) { - error_func->network_error(error.message()); - } - - /* verify if we got something */ - if (len == header.size()) { - /* decode header */ - string header_str(&header[0], header.size()); - istringstream header_stream(header_str); - - size_t data_size; - - if ((header_stream >> hex >> data_size)) { - - vector data(data_size); - size_t len = boost::asio::read(socket, boost::asio::buffer(data), error); - - if (error.value()) - error_func->network_error(error.message()); - - if (len == data_size) { - archive_str = (data.size()) ? string(&data[0], data.size()) : string(""); - - archive_stream = new istringstream(archive_str); - archive = new i_archive(*archive_stream); - - *archive &name; - fprintf(stderr, "rpc receive %s\n", name.c_str()); - } - else { - error_func->network_error("Network receive error: data size doesn't match header"); - } - } - else { - error_func->network_error("Network receive error: can't decode data size from header"); - } - } - else { - error_func->network_error("Network receive error: invalid header size"); - } - } - - ~RPCReceive() - { - delete archive; - delete archive_stream; - } - - void read(network_device_memory &mem, string &name) - { - *archive &mem.data_type &mem.data_elements &mem.data_size; - *archive &mem.data_width &mem.data_height &mem.data_depth &mem.device_pointer; - *archive &mem.type &name; - *archive &mem.interpolation &mem.extension; - *archive &mem.device_pointer; - - mem.name = name.c_str(); - mem.host_pointer = 0; - - /* Can't transfer OpenGL texture over network. */ - if (mem.type == MEM_PIXELS) { - mem.type = MEM_READ_WRITE; - } - } - - template void read(T &data) - { - *archive &data; - } - - void read_buffer(void *buffer, size_t size) - { - boost::system::error_code error; - size_t len = boost::asio::read(socket, boost::asio::buffer(buffer, size), error); - - if (error.value()) { - error_func->network_error(error.message()); - } - - if (len != size) - cout << "Network receive error: buffer size doesn't match expected size\n"; - } - - void read(DeviceTask &task) - { - int type; - - *archive &type &task.x &task.y &task.w &task.h; - *archive &task.rgba_byte &task.rgba_half &task.buffer &task.sample &task.num_samples; - *archive &task.offset &task.stride; - *archive &task.shader_input &task.shader_output &task.shader_eval_type; - *archive &task.shader_x &task.shader_w; - *archive &task.need_finish_queue; - - task.type = (DeviceTask::Type)type; - } - - void read(RenderTile &tile) - { - *archive &tile.x &tile.y &tile.w &tile.h; - *archive &tile.start_sample &tile.num_samples &tile.sample; - *archive &tile.resolution &tile.offset &tile.stride; - *archive &tile.buffer; - - tile.buffers = NULL; - } - - string name; - - protected: - tcp::socket &socket; - string archive_str; - istringstream *archive_stream; - i_archive *archive; - NetworkError *error_func; -}; - -/* Server auto discovery */ - -class ServerDiscovery { - public: - explicit ServerDiscovery(bool discover = false) - : listen_socket(io_service), collect_servers(false) - { - /* setup listen socket */ - listen_endpoint.address(boost::asio::ip::address_v4::any()); - listen_endpoint.port(DISCOVER_PORT); - - listen_socket.open(listen_endpoint.protocol()); - - boost::asio::socket_base::reuse_address option(true); - listen_socket.set_option(option); - - listen_socket.bind(listen_endpoint); - - /* setup receive callback */ - async_receive(); - - /* start server discovery */ - if (discover) { - collect_servers = true; - servers.clear(); - - broadcast_message(DISCOVER_REQUEST_MSG); - } - - /* start thread */ - work = new boost::asio::io_service::work(io_service); - thread = new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service)); - } - - ~ServerDiscovery() - { - io_service.stop(); - thread->join(); - delete thread; - delete work; - } - - vector get_server_list() - { - vector result; - - mutex.lock(); - result = vector(servers.begin(), servers.end()); - mutex.unlock(); - - return result; - } - - private: - void handle_receive_from(const boost::system::error_code &error, size_t size) - { - if (error) { - cout << "Server discovery receive error: " << error.message() << "\n"; - return; - } - - if (size > 0) { - string msg = string(receive_buffer, size); - - /* handle incoming message */ - if (collect_servers) { - if (msg == DISCOVER_REPLY_MSG) { - string address = receive_endpoint.address().to_string(); - - mutex.lock(); - - /* add address if it's not already in the list */ - bool found = std::find(servers.begin(), servers.end(), address) != servers.end(); - - if (!found) - servers.push_back(address); - - mutex.unlock(); - } - } - else { - /* reply to request */ - if (msg == DISCOVER_REQUEST_MSG) - broadcast_message(DISCOVER_REPLY_MSG); - } - } - - async_receive(); - } - - void async_receive() - { - listen_socket.async_receive_from(boost::asio::buffer(receive_buffer), - receive_endpoint, - boost::bind(&ServerDiscovery::handle_receive_from, - this, - boost::asio::placeholders::error, - boost::asio::placeholders::bytes_transferred)); - } - - void broadcast_message(const string &msg) - { - /* setup broadcast socket */ - boost::asio::ip::udp::socket socket(io_service); - - socket.open(boost::asio::ip::udp::v4()); - - boost::asio::socket_base::broadcast option(true); - socket.set_option(option); - - boost::asio::ip::udp::endpoint broadcast_endpoint( - boost::asio::ip::address::from_string("255.255.255.255"), DISCOVER_PORT); - - /* broadcast message */ - socket.send_to(boost::asio::buffer(msg), broadcast_endpoint); - } - - /* network service and socket */ - boost::asio::io_service io_service; - boost::asio::ip::udp::endpoint listen_endpoint; - boost::asio::ip::udp::socket listen_socket; - - /* threading */ - boost::thread *thread; - boost::asio::io_service::work *work; - boost::mutex mutex; - - /* buffer and endpoint for receiving messages */ - char receive_buffer[256]; - boost::asio::ip::udp::endpoint receive_endpoint; - - // os, version, devices, status, host name, group name, ip as far as fields go - struct ServerInfo { - string cycles_version; - string os; - int device_count; - string status; - string host_name; - string group_name; - string host_addr; - }; - - /* collection of server addresses in list */ - bool collect_servers; - vector servers; -}; - -CCL_NAMESPACE_END - -#endif - -#endif /* __DEVICE_NETWORK_H__ */ diff --git a/intern/cycles/device/device_opencl.cpp b/intern/cycles/device/device_opencl.cpp deleted file mode 100644 index 9abb7cfb7fe..00000000000 --- a/intern/cycles/device/device_opencl.cpp +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/opencl/device_opencl.h" -# include "device/device.h" -# include "device/device_intern.h" - -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_set.h" -# include "util/util_string.h" - -CCL_NAMESPACE_BEGIN - -Device *device_opencl_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return opencl_create_split_device(info, stats, profiler, background); -} - -bool device_opencl_init() -{ - static bool initialized = false; - static bool result = false; - - if (initialized) - return result; - - initialized = true; - - if (OpenCLInfo::device_type() != 0) { - int clew_result = clewInit(); - if (clew_result == CLEW_SUCCESS) { - VLOG(1) << "CLEW initialization succeeded."; - result = true; - } - else { - VLOG(1) << "CLEW initialization failed: " - << ((clew_result == CLEW_ERROR_ATEXIT_FAILED) ? "Error setting up atexit() handler" : - "Error opening the library"); - } - } - else { - VLOG(1) << "Skip initializing CLEW, platform is force disabled."; - result = false; - } - - return result; -} - -static cl_int device_opencl_get_num_platforms_safe(cl_uint *num_platforms) -{ -# ifdef _WIN32 - __try { - return clGetPlatformIDs(0, NULL, num_platforms); - } - __except (EXCEPTION_EXECUTE_HANDLER) { - /* Ignore crashes inside the OpenCL driver and hope we can - * survive even with corrupted OpenCL installs. */ - fprintf(stderr, "Cycles OpenCL: driver crashed, continuing without OpenCL.\n"); - } - - *num_platforms = 0; - return CL_DEVICE_NOT_FOUND; -# else - return clGetPlatformIDs(0, NULL, num_platforms); -# endif -} - -void device_opencl_info(vector &devices) -{ - cl_uint num_platforms = 0; - device_opencl_get_num_platforms_safe(&num_platforms); - if (num_platforms == 0) { - return; - } - - vector usable_devices; - OpenCLInfo::get_usable_devices(&usable_devices); - /* Devices are numbered consecutively across platforms. */ - int num_devices = 0; - set unique_ids; - foreach (OpenCLPlatformDevice &platform_device, usable_devices) { - /* Compute unique ID for persistent user preferences. */ - const string &platform_name = platform_device.platform_name; - const string &device_name = platform_device.device_name; - string hardware_id = platform_device.hardware_id; - if (hardware_id == "") { - hardware_id = string_printf("ID_%d", num_devices); - } - string id = string("OPENCL_") + platform_name + "_" + device_name + "_" + hardware_id; - - /* Hardware ID might not be unique, add device number in that case. */ - if (unique_ids.find(id) != unique_ids.end()) { - id += string_printf("_ID_%d", num_devices); - } - unique_ids.insert(id); - - /* Create DeviceInfo. */ - DeviceInfo info; - info.type = DEVICE_OPENCL; - info.description = string_remove_trademark(string(device_name)); - info.num = num_devices; - /* We don't know if it's used for display, but assume it is. */ - info.display_device = true; - info.use_split_kernel = true; - info.has_volume_decoupled = false; - info.has_adaptive_stop_per_sample = false; - info.denoisers = DENOISER_NLM; - info.id = id; - - /* 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++; - } -} - -string device_opencl_capabilities() -{ - if (OpenCLInfo::device_type() == 0) { - return "All OpenCL devices are forced to be OFF"; - } - string result = ""; - string error_msg = ""; /* Only used by opencl_assert(), but in the future - * it could also be nicely reported to the console. - */ - cl_uint num_platforms = 0; - opencl_assert(device_opencl_get_num_platforms_safe(&num_platforms)); - if (num_platforms == 0) { - return "No OpenCL platforms found\n"; - } - result += string_printf("Number of platforms: %u\n", num_platforms); - - vector platform_ids; - platform_ids.resize(num_platforms); - opencl_assert(clGetPlatformIDs(num_platforms, &platform_ids[0], NULL)); - -# define APPEND_INFO(func, id, name, what, type) \ - do { \ - type data; \ - memset(&data, 0, sizeof(data)); \ - opencl_assert(func(id, what, sizeof(data), &data, NULL)); \ - result += string_printf("%s: %s\n", name, to_string(data).c_str()); \ - } while (false) -# define APPEND_STRING_INFO_IMPL(func, id, name, what, is_optional) \ - do { \ - string value; \ - size_t length = 0; \ - if (func(id, what, 0, NULL, &length) == CL_SUCCESS) { \ - vector buffer(length + 1); \ - if (func(id, what, buffer.size(), buffer.data(), NULL) == CL_SUCCESS) { \ - value = string(buffer.data()); \ - } \ - } \ - if (is_optional && !(length != 0 && value[0] != '\0')) { \ - break; \ - } \ - result += string_printf("%s: %s\n", name, value.c_str()); \ - } while (false) -# define APPEND_PLATFORM_STRING_INFO(id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetPlatformInfo, id, "\tPlatform " name, what, false) -# define APPEND_STRING_EXTENSION_INFO(func, id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetPlatformInfo, id, "\tPlatform " name, what, true) -# define APPEND_PLATFORM_INFO(id, name, what, type) \ - APPEND_INFO(clGetPlatformInfo, id, "\tPlatform " name, what, type) -# define APPEND_DEVICE_INFO(id, name, what, type) \ - APPEND_INFO(clGetDeviceInfo, id, "\t\t\tDevice " name, what, type) -# define APPEND_DEVICE_STRING_INFO(id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetDeviceInfo, id, "\t\t\tDevice " name, what, false) -# define APPEND_DEVICE_STRING_EXTENSION_INFO(id, name, what) \ - APPEND_STRING_INFO_IMPL(clGetDeviceInfo, id, "\t\t\tDevice " name, what, true) - - vector device_ids; - for (cl_uint platform = 0; platform < num_platforms; ++platform) { - cl_platform_id platform_id = platform_ids[platform]; - - result += string_printf("Platform #%u\n", platform); - - APPEND_PLATFORM_STRING_INFO(platform_id, "Name", CL_PLATFORM_NAME); - APPEND_PLATFORM_STRING_INFO(platform_id, "Vendor", CL_PLATFORM_VENDOR); - APPEND_PLATFORM_STRING_INFO(platform_id, "Version", CL_PLATFORM_VERSION); - APPEND_PLATFORM_STRING_INFO(platform_id, "Profile", CL_PLATFORM_PROFILE); - APPEND_PLATFORM_STRING_INFO(platform_id, "Extensions", CL_PLATFORM_EXTENSIONS); - - cl_uint num_devices = 0; - opencl_assert( - clGetDeviceIDs(platform_ids[platform], CL_DEVICE_TYPE_ALL, 0, NULL, &num_devices)); - result += string_printf("\tNumber of devices: %u\n", num_devices); - - device_ids.resize(num_devices); - opencl_assert(clGetDeviceIDs( - platform_ids[platform], CL_DEVICE_TYPE_ALL, num_devices, &device_ids[0], NULL)); - for (cl_uint device = 0; device < num_devices; ++device) { - cl_device_id device_id = device_ids[device]; - - result += string_printf("\t\tDevice: #%u\n", device); - - APPEND_DEVICE_STRING_INFO(device_id, "Name", CL_DEVICE_NAME); - APPEND_DEVICE_STRING_EXTENSION_INFO(device_id, "Board Name", CL_DEVICE_BOARD_NAME_AMD); - APPEND_DEVICE_STRING_INFO(device_id, "Vendor", CL_DEVICE_VENDOR); - APPEND_DEVICE_STRING_INFO(device_id, "OpenCL C Version", CL_DEVICE_OPENCL_C_VERSION); - APPEND_DEVICE_STRING_INFO(device_id, "Profile", CL_DEVICE_PROFILE); - APPEND_DEVICE_STRING_INFO(device_id, "Version", CL_DEVICE_VERSION); - APPEND_DEVICE_STRING_INFO(device_id, "Extensions", CL_DEVICE_EXTENSIONS); - APPEND_DEVICE_INFO( - device_id, "Max clock frequency (MHz)", CL_DEVICE_MAX_CLOCK_FREQUENCY, cl_uint); - APPEND_DEVICE_INFO(device_id, "Max compute units", CL_DEVICE_MAX_COMPUTE_UNITS, cl_uint); - APPEND_DEVICE_INFO(device_id, "Max work group size", CL_DEVICE_MAX_WORK_GROUP_SIZE, size_t); - } - } - -# undef APPEND_INFO -# undef APPEND_STRING_INFO_IMPL -# undef APPEND_PLATFORM_STRING_INFO -# undef APPEND_STRING_EXTENSION_INFO -# undef APPEND_PLATFORM_INFO -# undef APPEND_DEVICE_INFO -# undef APPEND_DEVICE_STRING_INFO -# undef APPEND_DEVICE_STRING_EXTENSION_INFO - - return result; -} - -CCL_NAMESPACE_END - -#endif /* WITH_OPENCL */ diff --git a/intern/cycles/device/device_optix.cpp b/intern/cycles/device/device_optix.cpp deleted file mode 100644 index 6f9a7943722..00000000000 --- a/intern/cycles/device/device_optix.cpp +++ /dev/null @@ -1,1936 +0,0 @@ -/* - * Copyright 2019, NVIDIA Corporation. - * Copyright 2019, Blender Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPTIX - -# include "bvh/bvh.h" -# include "bvh/bvh_optix.h" -# include "device/cuda/device_cuda.h" -# include "device/device_denoising.h" -# include "device/device_intern.h" -# include "render/buffers.h" -# include "render/hair.h" -# include "render/mesh.h" -# include "render/object.h" -# include "render/scene.h" -# include "util/util_debug.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_progress.h" -# include "util/util_time.h" - -# ifdef WITH_CUDA_DYNLOAD -# include -// Do not use CUDA SDK headers when using CUEW -# define OPTIX_DONT_INCLUDE_CUDA -# endif -# include -# include - -// TODO(pmours): Disable this once drivers have native support -# define OPTIX_DENOISER_NO_PIXEL_STRIDE 1 - -CCL_NAMESPACE_BEGIN - -/* Make sure this stays in sync with kernel_globals.h */ -struct ShaderParams { - uint4 *input; - float4 *output; - int type; - int filter; - int sx; - int offset; - int sample; -}; -struct KernelParams { - WorkTile tile; - KernelData data; - ShaderParams shader; -# define KERNEL_TEX(type, name) const type *name; -# include "kernel/kernel_textures.h" -# undef KERNEL_TEX -}; - -# define check_result_cuda(stmt) \ - { \ - CUresult res = stmt; \ - if (res != CUDA_SUCCESS) { \ - const char *name; \ - cuGetErrorName(res, &name); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return; \ - } \ - } \ - (void)0 -# define check_result_cuda_ret(stmt) \ - { \ - CUresult res = stmt; \ - if (res != CUDA_SUCCESS) { \ - const char *name; \ - cuGetErrorName(res, &name); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return false; \ - } \ - } \ - (void)0 - -# define check_result_optix(stmt) \ - { \ - enum OptixResult res = stmt; \ - if (res != OPTIX_SUCCESS) { \ - const char *name = optixGetErrorName(res); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return; \ - } \ - } \ - (void)0 -# define check_result_optix_ret(stmt) \ - { \ - enum OptixResult res = stmt; \ - if (res != OPTIX_SUCCESS) { \ - const char *name = optixGetErrorName(res); \ - set_error(string_printf("%s in %s (device_optix.cpp:%d)", name, #stmt, __LINE__)); \ - return false; \ - } \ - } \ - (void)0 - -# define launch_filter_kernel(func_name, w, h, args) \ - { \ - CUfunction func; \ - check_result_cuda_ret(cuModuleGetFunction(&func, cuFilterModule, func_name)); \ - check_result_cuda_ret(cuFuncSetCacheConfig(func, CU_FUNC_CACHE_PREFER_L1)); \ - int threads; \ - check_result_cuda_ret( \ - cuFuncGetAttribute(&threads, CU_FUNC_ATTRIBUTE_MAX_THREADS_PER_BLOCK, func)); \ - threads = (int)sqrt((float)threads); \ - int xblocks = ((w) + threads - 1) / threads; \ - int yblocks = ((h) + threads - 1) / threads; \ - check_result_cuda_ret( \ - cuLaunchKernel(func, xblocks, yblocks, 1, threads, threads, 1, 0, 0, args, 0)); \ - } \ - (void)0 - -class OptiXDevice : public CUDADevice { - - // List of OptiX program groups - enum { - PG_RGEN, - PG_MISS, - PG_HITD, // Default hit group - PG_HITS, // __SHADOW_RECORD_ALL__ hit group - PG_HITL, // __BVH_LOCAL__ hit group (only used for triangles) -# if OPTIX_ABI_VERSION >= 36 - PG_HITD_MOTION, - PG_HITS_MOTION, -# endif - PG_BAKE, // kernel_bake_evaluate - PG_DISP, // kernel_displace_evaluate - PG_BACK, // kernel_background_evaluate - PG_CALL, - NUM_PROGRAM_GROUPS = PG_CALL + 3 - }; - - // List of OptiX pipelines - enum { PIP_PATH_TRACE, PIP_SHADER_EVAL, NUM_PIPELINES }; - - // A single shader binding table entry - struct SbtRecord { - char header[OPTIX_SBT_RECORD_HEADER_SIZE]; - }; - - // Information stored about CUDA memory allocations - struct CUDAMem { - bool free_map_host = false; - CUarray array = NULL; - CUtexObject texobject = 0; - bool use_mapped_host = false; - }; - - // Helper class to manage current CUDA context - struct CUDAContextScope { - CUDAContextScope(CUcontext ctx) - { - cuCtxPushCurrent(ctx); - } - ~CUDAContextScope() - { - cuCtxPopCurrent(NULL); - } - }; - - // Use a pool with multiple threads to support launches with multiple CUDA streams - TaskPool task_pool; - - vector cuda_stream; - OptixDeviceContext context = NULL; - - OptixModule optix_module = NULL; // All necessary OptiX kernels are in one module - OptixModule builtin_modules[2] = {}; - OptixPipeline pipelines[NUM_PIPELINES] = {}; - - bool motion_blur = false; - device_vector sbt_data; - device_only_memory launch_params; - OptixTraversableHandle tlas_handle = 0; - - OptixDenoiser denoiser = NULL; - device_only_memory denoiser_state; - int denoiser_input_passes = 0; - - vector> delayed_free_bvh_memory; - thread_mutex delayed_free_bvh_mutex; - - public: - OptiXDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_) - : CUDADevice(info_, stats_, profiler_, background_), - sbt_data(this, "__sbt", MEM_READ_ONLY), - launch_params(this, "__params", false), - denoiser_state(this, "__denoiser_state", true) - { - // Store number of CUDA streams in device info - info.cpu_threads = DebugFlags().optix.cuda_streams; - - // Make the CUDA context current - if (!cuContext) { - return; // Do not initialize if CUDA context creation failed already - } - const CUDAContextScope scope(cuContext); - - // Create OptiX context for this device - OptixDeviceContextOptions options = {}; -# ifdef WITH_CYCLES_LOGGING - options.logCallbackLevel = 4; // Fatal = 1, Error = 2, Warning = 3, Print = 4 - options.logCallbackFunction = - [](unsigned int level, const char *, const char *message, void *) { - switch (level) { - case 1: - LOG_IF(FATAL, VLOG_IS_ON(1)) << message; - break; - case 2: - LOG_IF(ERROR, VLOG_IS_ON(1)) << message; - break; - case 3: - LOG_IF(WARNING, VLOG_IS_ON(1)) << message; - break; - case 4: - LOG_IF(INFO, VLOG_IS_ON(1)) << message; - break; - } - }; -# endif - check_result_optix(optixDeviceContextCreate(cuContext, &options, &context)); -# ifdef WITH_CYCLES_LOGGING - check_result_optix(optixDeviceContextSetLogCallback( - context, options.logCallbackFunction, options.logCallbackData, options.logCallbackLevel)); -# endif - - // Create launch streams - cuda_stream.resize(info.cpu_threads); - for (int i = 0; i < info.cpu_threads; ++i) - check_result_cuda(cuStreamCreate(&cuda_stream[i], CU_STREAM_NON_BLOCKING)); - - // Fix weird compiler bug that assigns wrong size - launch_params.data_elements = sizeof(KernelParams); - // Allocate launch parameter buffer memory on device - launch_params.alloc_to_device(info.cpu_threads); - } - ~OptiXDevice() - { - // Stop processing any more tasks - task_pool.cancel(); - - // Make CUDA context current - const CUDAContextScope scope(cuContext); - - free_bvh_memory_delayed(); - - sbt_data.free(); - texture_info.free(); - launch_params.free(); - denoiser_state.free(); - - // Unload modules - if (optix_module != NULL) - optixModuleDestroy(optix_module); - for (unsigned int i = 0; i < 2; ++i) - if (builtin_modules[i] != NULL) - optixModuleDestroy(builtin_modules[i]); - for (unsigned int i = 0; i < NUM_PIPELINES; ++i) - if (pipelines[i] != NULL) - optixPipelineDestroy(pipelines[i]); - - // Destroy launch streams - for (CUstream stream : cuda_stream) - cuStreamDestroy(stream); - - if (denoiser != NULL) - optixDenoiserDestroy(denoiser); - - optixDeviceContextDestroy(context); - } - - private: - bool show_samples() const override - { - // Only show samples if not rendering multiple tiles in parallel - return info.cpu_threads == 1; - } - - BVHLayoutMask get_bvh_layout_mask() const override - { - // CUDA kernels are used when doing baking, so need to build a BVH those can understand too! - if (optix_module == NULL) - return CUDADevice::get_bvh_layout_mask(); - - // OptiX has its own internal acceleration structure format - return BVH_LAYOUT_OPTIX; - } - - string compile_kernel_get_common_cflags(const DeviceRequestedFeatures &requested_features, - bool filter, - bool /*split*/) override - { - // Split kernel is not supported in OptiX - string common_cflags = CUDADevice::compile_kernel_get_common_cflags( - requested_features, filter, false); - - // Add OptiX SDK include directory to include paths - const char *optix_sdk_path = getenv("OPTIX_ROOT_DIR"); - if (optix_sdk_path) { - common_cflags += string_printf(" -I\"%s/include\"", optix_sdk_path); - } - - // Specialization for shader raytracing - if (requested_features.use_shader_raytrace) { - common_cflags += " --keep-device-functions"; - } - else { - common_cflags += " -D __NO_SHADER_RAYTRACE__"; - } - - return common_cflags; - } - - bool load_kernels(const DeviceRequestedFeatures &requested_features) override - { - if (have_error()) { - // Abort early if context creation failed already - return false; - } - - // Load CUDA modules because we need some of the utility kernels - if (!CUDADevice::load_kernels(requested_features)) { - return false; - } - - // Baking is currently performed using CUDA, so no need to load OptiX kernels - if (requested_features.use_baking) { - return true; - } - - const CUDAContextScope scope(cuContext); - - // Unload existing OptiX module and pipelines first - if (optix_module != NULL) { - optixModuleDestroy(optix_module); - optix_module = NULL; - } - for (unsigned int i = 0; i < 2; ++i) { - if (builtin_modules[i] != NULL) { - optixModuleDestroy(builtin_modules[i]); - builtin_modules[i] = NULL; - } - } - for (unsigned int i = 0; i < NUM_PIPELINES; ++i) { - if (pipelines[i] != NULL) { - optixPipelineDestroy(pipelines[i]); - pipelines[i] = NULL; - } - } - - OptixModuleCompileOptions module_options = {}; - module_options.maxRegisterCount = 0; // Do not set an explicit register limit - module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3; - module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; - -# if OPTIX_ABI_VERSION >= 41 - module_options.boundValues = nullptr; - module_options.numBoundValues = 0; -# endif - - OptixPipelineCompileOptions pipeline_options = {}; - // Default to no motion blur and two-level graph, since it is the fastest option - pipeline_options.usesMotionBlur = false; - pipeline_options.traversableGraphFlags = - OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING; - pipeline_options.numPayloadValues = 6; - pipeline_options.numAttributeValues = 2; // u, v - pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; - pipeline_options.pipelineLaunchParamsVariableName = "__params"; // See kernel_globals.h - -# if OPTIX_ABI_VERSION >= 36 - pipeline_options.usesPrimitiveTypeFlags = OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE; - if (requested_features.use_hair) { - if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) { - pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_ROUND_CUBIC_BSPLINE; - } - else { - pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM; - } - } -# endif - - // Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds - // This is necessary since objects may be reported to have motion if the Vector pass is - // active, but may still need to be rendered without motion blur if that isn't active as well - motion_blur = requested_features.use_object_motion; - - if (motion_blur) { - pipeline_options.usesMotionBlur = true; - // Motion blur can insert motion transforms into the traversal graph - // It is no longer a two-level graph then, so need to set flags to allow any configuration - pipeline_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_ANY; - } - - { // Load and compile PTX module with OptiX kernels - string ptx_data, ptx_filename = path_get(requested_features.use_shader_raytrace ? - "lib/kernel_optix_shader_raytrace.ptx" : - "lib/kernel_optix.ptx"); - if (use_adaptive_compilation() || path_file_size(ptx_filename) == -1) { - if (!getenv("OPTIX_ROOT_DIR")) { - set_error( - "Missing OPTIX_ROOT_DIR environment variable (which must be set with the path to " - "the Optix SDK to be able to compile Optix kernels on demand)."); - return false; - } - ptx_filename = compile_kernel(requested_features, "kernel_optix", "optix", true); - } - if (ptx_filename.empty() || !path_read_text(ptx_filename, ptx_data)) { - set_error("Failed to load OptiX kernel from '" + ptx_filename + "'"); - return false; - } - - check_result_optix_ret(optixModuleCreateFromPTX(context, - &module_options, - &pipeline_options, - ptx_data.data(), - ptx_data.size(), - nullptr, - 0, - &optix_module)); - } - - // Create program groups - OptixProgramGroup groups[NUM_PROGRAM_GROUPS] = {}; - OptixProgramGroupDesc group_descs[NUM_PROGRAM_GROUPS] = {}; - OptixProgramGroupOptions group_options = {}; // There are no options currently - group_descs[PG_RGEN].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_RGEN].raygen.module = optix_module; - // Ignore branched integrator for now (see "requested_features.use_integrator_branched") - group_descs[PG_RGEN].raygen.entryFunctionName = "__raygen__kernel_optix_path_trace"; - group_descs[PG_MISS].kind = OPTIX_PROGRAM_GROUP_KIND_MISS; - group_descs[PG_MISS].miss.module = optix_module; - group_descs[PG_MISS].miss.entryFunctionName = "__miss__kernel_optix_miss"; - group_descs[PG_HITD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; - group_descs[PG_HITD].hitgroup.moduleCH = optix_module; - group_descs[PG_HITD].hitgroup.entryFunctionNameCH = "__closesthit__kernel_optix_hit"; - group_descs[PG_HITD].hitgroup.moduleAH = optix_module; - group_descs[PG_HITD].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_visibility_test"; - group_descs[PG_HITS].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; - group_descs[PG_HITS].hitgroup.moduleAH = optix_module; - group_descs[PG_HITS].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_shadow_all_hit"; - - if (requested_features.use_hair) { - group_descs[PG_HITD].hitgroup.moduleIS = optix_module; - group_descs[PG_HITS].hitgroup.moduleIS = optix_module; - - // Add curve intersection programs - if (requested_features.use_hair_thick) { - // Slower programs for thick hair since that also slows down ribbons. - // Ideally this should not be needed. - group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_all"; - group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_all"; - } - else { - group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; - group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; - } - -# if OPTIX_ABI_VERSION >= 36 - if (DebugFlags().optix.curves_api && requested_features.use_hair_thick) { - OptixBuiltinISOptions builtin_options = {}; - builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; - builtin_options.usesMotionBlur = false; - - check_result_optix_ret(optixBuiltinISModuleGet( - context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[0])); - - group_descs[PG_HITD].hitgroup.moduleIS = builtin_modules[0]; - group_descs[PG_HITD].hitgroup.entryFunctionNameIS = nullptr; - group_descs[PG_HITS].hitgroup.moduleIS = builtin_modules[0]; - group_descs[PG_HITS].hitgroup.entryFunctionNameIS = nullptr; - - if (motion_blur) { - builtin_options.usesMotionBlur = true; - - check_result_optix_ret(optixBuiltinISModuleGet( - context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[1])); - - group_descs[PG_HITD_MOTION] = group_descs[PG_HITD]; - group_descs[PG_HITD_MOTION].hitgroup.moduleIS = builtin_modules[1]; - group_descs[PG_HITS_MOTION] = group_descs[PG_HITS]; - group_descs[PG_HITS_MOTION].hitgroup.moduleIS = builtin_modules[1]; - } - } -# endif - } - - if (requested_features.use_subsurface || requested_features.use_shader_raytrace) { - // Add hit group for local intersections - group_descs[PG_HITL].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; - group_descs[PG_HITL].hitgroup.moduleAH = optix_module; - group_descs[PG_HITL].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_local_hit"; - } - - if (requested_features.use_baking) { - group_descs[PG_BAKE].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_BAKE].raygen.module = optix_module; - group_descs[PG_BAKE].raygen.entryFunctionName = "__raygen__kernel_optix_bake"; - } - - if (requested_features.use_true_displacement) { - group_descs[PG_DISP].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_DISP].raygen.module = optix_module; - group_descs[PG_DISP].raygen.entryFunctionName = "__raygen__kernel_optix_displace"; - } - - if (requested_features.use_background_light) { - group_descs[PG_BACK].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; - group_descs[PG_BACK].raygen.module = optix_module; - group_descs[PG_BACK].raygen.entryFunctionName = "__raygen__kernel_optix_background"; - } - - // Shader raytracing replaces some functions with direct callables - if (requested_features.use_shader_raytrace) { - group_descs[PG_CALL + 0].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; - group_descs[PG_CALL + 0].callables.moduleDC = optix_module; - group_descs[PG_CALL + 0].callables.entryFunctionNameDC = "__direct_callable__svm_eval_nodes"; - group_descs[PG_CALL + 1].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; - group_descs[PG_CALL + 1].callables.moduleDC = optix_module; - group_descs[PG_CALL + 1].callables.entryFunctionNameDC = - "__direct_callable__kernel_volume_shadow"; - group_descs[PG_CALL + 2].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; - group_descs[PG_CALL + 2].callables.moduleDC = optix_module; - group_descs[PG_CALL + 2].callables.entryFunctionNameDC = - "__direct_callable__subsurface_scatter_multi_setup"; - } - - check_result_optix_ret(optixProgramGroupCreate( - context, group_descs, NUM_PROGRAM_GROUPS, &group_options, nullptr, 0, groups)); - - // Get program stack sizes - OptixStackSizes stack_size[NUM_PROGRAM_GROUPS] = {}; - // Set up SBT, which in this case is used only to select between different programs - sbt_data.alloc(NUM_PROGRAM_GROUPS); - memset(sbt_data.host_pointer, 0, sizeof(SbtRecord) * NUM_PROGRAM_GROUPS); - for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { - check_result_optix_ret(optixSbtRecordPackHeader(groups[i], &sbt_data[i])); - check_result_optix_ret(optixProgramGroupGetStackSize(groups[i], &stack_size[i])); - } - sbt_data.copy_to_device(); // Upload SBT to device - - // Calculate maximum trace continuation stack size - unsigned int trace_css = stack_size[PG_HITD].cssCH; - // This is based on the maximum of closest-hit and any-hit/intersection programs - trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH); - trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH); - trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH); -# if OPTIX_ABI_VERSION >= 36 - trace_css = std::max(trace_css, - stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH); - trace_css = std::max(trace_css, - stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH); -# endif - - OptixPipelineLinkOptions link_options = {}; - link_options.maxTraceDepth = 1; - link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; -# if OPTIX_ABI_VERSION < 24 - link_options.overrideUsesMotionBlur = motion_blur; -# endif - - { // Create path tracing pipeline - vector pipeline_groups; - pipeline_groups.reserve(NUM_PROGRAM_GROUPS); - pipeline_groups.push_back(groups[PG_RGEN]); - pipeline_groups.push_back(groups[PG_MISS]); - pipeline_groups.push_back(groups[PG_HITD]); - pipeline_groups.push_back(groups[PG_HITS]); - pipeline_groups.push_back(groups[PG_HITL]); -# if OPTIX_ABI_VERSION >= 36 - if (motion_blur) { - pipeline_groups.push_back(groups[PG_HITD_MOTION]); - pipeline_groups.push_back(groups[PG_HITS_MOTION]); - } -# endif - if (requested_features.use_shader_raytrace) { - pipeline_groups.push_back(groups[PG_CALL + 0]); - pipeline_groups.push_back(groups[PG_CALL + 1]); - pipeline_groups.push_back(groups[PG_CALL + 2]); - } - - check_result_optix_ret(optixPipelineCreate(context, - &pipeline_options, - &link_options, - pipeline_groups.data(), - pipeline_groups.size(), - nullptr, - 0, - &pipelines[PIP_PATH_TRACE])); - - // Combine ray generation and trace continuation stack size - const unsigned int css = stack_size[PG_RGEN].cssRG + link_options.maxTraceDepth * trace_css; - // Max direct callable depth is one of the following, so combine accordingly - // - __raygen__ -> svm_eval_nodes - // - __raygen__ -> kernel_volume_shadow -> svm_eval_nodes - // - __raygen__ -> subsurface_scatter_multi_setup -> svm_eval_nodes - const unsigned int dss = stack_size[PG_CALL + 0].dssDC + - std::max(stack_size[PG_CALL + 1].dssDC, - stack_size[PG_CALL + 2].dssDC); - - // Set stack size depending on pipeline options - check_result_optix_ret( - optixPipelineSetStackSize(pipelines[PIP_PATH_TRACE], - 0, - requested_features.use_shader_raytrace ? dss : 0, - css, - motion_blur ? 3 : 2)); - } - - // Only need to create shader evaluation pipeline if one of these features is used: - const bool use_shader_eval_pipeline = requested_features.use_baking || - requested_features.use_background_light || - requested_features.use_true_displacement; - - if (use_shader_eval_pipeline) { // Create shader evaluation pipeline - vector pipeline_groups; - pipeline_groups.reserve(NUM_PROGRAM_GROUPS); - pipeline_groups.push_back(groups[PG_BAKE]); - pipeline_groups.push_back(groups[PG_DISP]); - pipeline_groups.push_back(groups[PG_BACK]); - pipeline_groups.push_back(groups[PG_MISS]); - pipeline_groups.push_back(groups[PG_HITD]); - pipeline_groups.push_back(groups[PG_HITS]); - pipeline_groups.push_back(groups[PG_HITL]); -# if OPTIX_ABI_VERSION >= 36 - if (motion_blur) { - pipeline_groups.push_back(groups[PG_HITD_MOTION]); - pipeline_groups.push_back(groups[PG_HITS_MOTION]); - } -# endif - if (requested_features.use_shader_raytrace) { - pipeline_groups.push_back(groups[PG_CALL + 0]); - pipeline_groups.push_back(groups[PG_CALL + 1]); - pipeline_groups.push_back(groups[PG_CALL + 2]); - } - - check_result_optix_ret(optixPipelineCreate(context, - &pipeline_options, - &link_options, - pipeline_groups.data(), - pipeline_groups.size(), - nullptr, - 0, - &pipelines[PIP_SHADER_EVAL])); - - // Calculate continuation stack size based on the maximum of all ray generation stack sizes - const unsigned int css = std::max(stack_size[PG_BAKE].cssRG, - std::max(stack_size[PG_DISP].cssRG, - stack_size[PG_BACK].cssRG)) + - link_options.maxTraceDepth * trace_css; - const unsigned int dss = stack_size[PG_CALL + 0].dssDC + - std::max(stack_size[PG_CALL + 1].dssDC, - stack_size[PG_CALL + 2].dssDC); - - check_result_optix_ret( - optixPipelineSetStackSize(pipelines[PIP_SHADER_EVAL], - 0, - requested_features.use_shader_raytrace ? dss : 0, - css, - motion_blur ? 3 : 2)); - } - - // Clean up program group objects - for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { - optixProgramGroupDestroy(groups[i]); - } - - return true; - } - - void thread_run(DeviceTask &task, int thread_index) // Main task entry point - { - if (have_error()) - return; // Abort early if there was an error previously - - if (task.type == DeviceTask::RENDER) { - if (thread_index != 0) { - // Only execute denoising in a single thread (see also 'task_add') - task.tile_types &= ~RenderTile::DENOISE; - } - - RenderTile tile; - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) - launch_render(task, tile, thread_index); - else if (tile.task == RenderTile::BAKE) { - // Perform baking using CUDA, since it is not currently implemented in OptiX - device_vector work_tiles(this, "work_tiles", MEM_READ_ONLY); - CUDADevice::render(task, tile, work_tiles); - } - else if (tile.task == RenderTile::DENOISE) - launch_denoise(task, tile); - task.release_tile(tile); - if (task.get_cancel() && !task.need_finish_queue) - break; // User requested cancellation - else if (have_error()) - break; // Abort rendering when encountering an error - } - } - else if (task.type == DeviceTask::SHADER) { - // CUDA kernels are used when doing baking - if (optix_module == NULL) - CUDADevice::shader(task); - else - launch_shader_eval(task, thread_index); - } - else if (task.type == DeviceTask::DENOISE_BUFFER) { - // Set up a single tile that covers the whole task and denoise it - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - launch_denoise(task, tile); - } - } - - void launch_render(DeviceTask &task, RenderTile &rtile, int thread_index) - { - assert(thread_index < launch_params.data_size); - - // Keep track of total render time of this tile - const scoped_timer timer(&rtile.buffers->render_time); - - WorkTile wtile; - wtile.x = rtile.x; - wtile.y = rtile.y; - wtile.w = rtile.w; - wtile.h = rtile.h; - wtile.offset = rtile.offset; - wtile.stride = rtile.stride; - wtile.buffer = (float *)rtile.buffer; - - const int end_sample = rtile.start_sample + rtile.num_samples; - // Keep this number reasonable to avoid running into TDRs - int step_samples = (info.display_device ? 8 : 32); - - // Offset into launch params buffer so that streams use separate data - device_ptr launch_params_ptr = launch_params.device_pointer + - thread_index * launch_params.data_elements; - - const CUDAContextScope scope(cuContext); - - for (int sample = rtile.start_sample; sample < end_sample;) { - // Copy work tile information to device - wtile.start_sample = sample; - wtile.num_samples = step_samples; - if (task.adaptive_sampling.use) { - wtile.num_samples = task.adaptive_sampling.align_samples(sample, step_samples); - } - wtile.num_samples = min(wtile.num_samples, end_sample - sample); - device_ptr d_wtile_ptr = launch_params_ptr + offsetof(KernelParams, tile); - check_result_cuda( - cuMemcpyHtoDAsync(d_wtile_ptr, &wtile, sizeof(wtile), cuda_stream[thread_index])); - - OptixShaderBindingTable sbt_params = {}; - sbt_params.raygenRecord = sbt_data.device_pointer + PG_RGEN * sizeof(SbtRecord); - sbt_params.missRecordBase = sbt_data.device_pointer + PG_MISS * sizeof(SbtRecord); - sbt_params.missRecordStrideInBytes = sizeof(SbtRecord); - sbt_params.missRecordCount = 1; - sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord); - sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); -# if OPTIX_ABI_VERSION >= 36 - sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL -# else - sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL -# endif - sbt_params.callablesRecordBase = sbt_data.device_pointer + PG_CALL * sizeof(SbtRecord); - sbt_params.callablesRecordCount = 3; - sbt_params.callablesRecordStrideInBytes = sizeof(SbtRecord); - - // Launch the ray generation program - check_result_optix(optixLaunch(pipelines[PIP_PATH_TRACE], - cuda_stream[thread_index], - launch_params_ptr, - launch_params.data_elements, - &sbt_params, - // Launch with samples close to each other for better locality - wtile.w * wtile.num_samples, - wtile.h, - 1)); - - // Run the adaptive sampling kernels at selected samples aligned to step samples. - uint filter_sample = wtile.start_sample + wtile.num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - adaptive_sampling_filter(filter_sample, &wtile, d_wtile_ptr, cuda_stream[thread_index]); - } - - // Wait for launch to finish - check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index])); - - // Update current sample, so it is displayed correctly - sample += wtile.num_samples; - rtile.sample = sample; - // Update task progress after the kernel completed rendering - task.update_progress(&rtile, wtile.w * wtile.h * wtile.num_samples); - - if (task.get_cancel() && !task.need_finish_queue) - return; // Cancel rendering - } - - // Finalize adaptive sampling - if (task.adaptive_sampling.use) { - device_ptr d_wtile_ptr = launch_params_ptr + offsetof(KernelParams, tile); - adaptive_sampling_post(rtile, &wtile, d_wtile_ptr, cuda_stream[thread_index]); - check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index])); - task.update_progress(&rtile, rtile.w * rtile.h * wtile.num_samples); - } - } - - bool launch_denoise(DeviceTask &task, RenderTile &rtile) - { - // Update current sample (for display and NLM denoising task) - rtile.sample = rtile.start_sample + rtile.num_samples; - - // Make CUDA context current now, since it is used for both denoising tasks - const CUDAContextScope scope(cuContext); - - // Choose between OptiX and NLM denoising - if (task.denoising.type == DENOISER_OPTIX) { - // Map neighboring tiles onto this device, indices are as following: - // Where index 4 is the center tile and index 9 is the target for the result. - // 0 1 2 - // 3 4 5 - // 6 7 8 9 - RenderTileNeighbors neighbors(rtile); - task.map_neighbor_tiles(neighbors, this); - RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; - RenderTile &target_tile = neighbors.target; - rtile = center_tile; // Tile may have been modified by mapping code - - // Calculate size of the tile to denoise (including overlap) - int4 rect = center_tile.bounds(); - // Overlap between tiles has to be at least 64 pixels - // TODO(pmours): Query this value from OptiX - rect = rect_expand(rect, 64); - int4 clip_rect = neighbors.bounds(); - rect = rect_clip(rect, clip_rect); - int2 rect_size = make_int2(rect.z - rect.x, rect.w - rect.y); - int2 overlap_offset = make_int2(rtile.x - rect.x, rtile.y - rect.y); - - // Calculate byte offsets and strides - int pixel_stride = task.pass_stride * (int)sizeof(float); - int pixel_offset = (rtile.offset + rtile.x + rtile.y * rtile.stride) * pixel_stride; - const int pass_offset[3] = { - (task.pass_denoising_data + DENOISING_PASS_COLOR) * (int)sizeof(float), - (task.pass_denoising_data + DENOISING_PASS_ALBEDO) * (int)sizeof(float), - (task.pass_denoising_data + DENOISING_PASS_NORMAL) * (int)sizeof(float)}; - - // Start with the current tile pointer offset - int input_stride = pixel_stride; - device_ptr input_ptr = rtile.buffer + pixel_offset; - - // Copy tile data into a common buffer if necessary - device_only_memory input(this, "denoiser input", true); - device_vector tile_info_mem(this, "denoiser tile info", MEM_READ_ONLY); - - bool contiguous_memory = true; - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - if (neighbors.tiles[i].buffer && neighbors.tiles[i].buffer != rtile.buffer) { - contiguous_memory = false; - } - } - - if (contiguous_memory) { - // Tiles are in continous memory, so can just subtract overlap offset - input_ptr -= (overlap_offset.x + overlap_offset.y * rtile.stride) * pixel_stride; - // Stride covers the whole width of the image and not just a single tile - input_stride *= rtile.stride; - } - else { - // Adjacent tiles are in separate memory regions, so need to copy them into a single one - input.alloc_to_device(rect_size.x * rect_size.y * task.pass_stride); - // Start with the new input buffer - input_ptr = input.device_pointer; - // Stride covers the width of the new input buffer, which includes tile width and overlap - input_stride *= rect_size.x; - - TileInfo *tile_info = tile_info_mem.alloc(1); - for (int i = 0; i < RenderTileNeighbors::SIZE; i++) { - tile_info->offsets[i] = neighbors.tiles[i].offset; - tile_info->strides[i] = neighbors.tiles[i].stride; - tile_info->buffers[i] = neighbors.tiles[i].buffer; - } - tile_info->x[0] = neighbors.tiles[3].x; - tile_info->x[1] = neighbors.tiles[4].x; - tile_info->x[2] = neighbors.tiles[5].x; - tile_info->x[3] = neighbors.tiles[5].x + neighbors.tiles[5].w; - tile_info->y[0] = neighbors.tiles[1].y; - tile_info->y[1] = neighbors.tiles[4].y; - tile_info->y[2] = neighbors.tiles[7].y; - tile_info->y[3] = neighbors.tiles[7].y + neighbors.tiles[7].h; - tile_info_mem.copy_to_device(); - - void *args[] = { - &input.device_pointer, &tile_info_mem.device_pointer, &rect.x, &task.pass_stride}; - launch_filter_kernel("kernel_cuda_filter_copy_input", rect_size.x, rect_size.y, args); - } - -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - device_only_memory input_rgb(this, "denoiser input rgb", true); - input_rgb.alloc_to_device(rect_size.x * rect_size.y * 3 * task.denoising.input_passes); - - void *input_args[] = {&input_rgb.device_pointer, - &input_ptr, - &rect_size.x, - &rect_size.y, - &input_stride, - &task.pass_stride, - const_cast(pass_offset), - &task.denoising.input_passes, - &rtile.sample}; - launch_filter_kernel( - "kernel_cuda_filter_convert_to_rgb", rect_size.x, rect_size.y, input_args); - - input_ptr = input_rgb.device_pointer; - pixel_stride = 3 * sizeof(float); - input_stride = rect_size.x * pixel_stride; -# endif - - const bool recreate_denoiser = (denoiser == NULL) || - (task.denoising.input_passes != denoiser_input_passes); - if (recreate_denoiser) { - // Destroy existing handle before creating new one - if (denoiser != NULL) { - optixDenoiserDestroy(denoiser); - } - - // Create OptiX denoiser handle on demand when it is first used - OptixDenoiserOptions denoiser_options = {}; - assert(task.denoising.input_passes >= 1 && task.denoising.input_passes <= 3); -# if OPTIX_ABI_VERSION >= 47 - denoiser_options.guideAlbedo = task.denoising.input_passes >= 2; - denoiser_options.guideNormal = task.denoising.input_passes >= 3; - check_result_optix_ret(optixDenoiserCreate( - context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser)); -# else - denoiser_options.inputKind = static_cast( - OPTIX_DENOISER_INPUT_RGB + (task.denoising.input_passes - 1)); -# if OPTIX_ABI_VERSION < 28 - denoiser_options.pixelFormat = OPTIX_PIXEL_FORMAT_FLOAT3; -# endif - check_result_optix_ret(optixDenoiserCreate(context, &denoiser_options, &denoiser)); - check_result_optix_ret( - optixDenoiserSetModel(denoiser, OPTIX_DENOISER_MODEL_KIND_HDR, NULL, 0)); -# endif - - // OptiX denoiser handle was created with the requested number of input passes - denoiser_input_passes = task.denoising.input_passes; - } - - OptixDenoiserSizes sizes = {}; - check_result_optix_ret( - optixDenoiserComputeMemoryResources(denoiser, rect_size.x, rect_size.y, &sizes)); - -# if OPTIX_ABI_VERSION < 28 - const size_t scratch_size = sizes.recommendedScratchSizeInBytes; -# else - const size_t scratch_size = sizes.withOverlapScratchSizeInBytes; -# endif - const size_t scratch_offset = sizes.stateSizeInBytes; - - // Allocate denoiser state if tile size has changed since last setup - if (recreate_denoiser || (denoiser_state.data_width != rect_size.x || - denoiser_state.data_height != rect_size.y)) { - denoiser_state.alloc_to_device(scratch_offset + scratch_size); - - // Initialize denoiser state for the current tile size - check_result_optix_ret(optixDenoiserSetup(denoiser, - 0, - rect_size.x, - rect_size.y, - denoiser_state.device_pointer, - scratch_offset, - denoiser_state.device_pointer + scratch_offset, - scratch_size)); - - denoiser_state.data_width = rect_size.x; - denoiser_state.data_height = rect_size.y; - } - - // Set up input and output layer information - OptixImage2D input_layers[3] = {}; - OptixImage2D output_layers[1] = {}; - - for (int i = 0; i < 3; ++i) { -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - input_layers[i].data = input_ptr + (rect_size.x * rect_size.y * pixel_stride * i); -# else - input_layers[i].data = input_ptr + pass_offset[i]; -# endif - input_layers[i].width = rect_size.x; - input_layers[i].height = rect_size.y; - input_layers[i].rowStrideInBytes = input_stride; - input_layers[i].pixelStrideInBytes = pixel_stride; - input_layers[i].format = OPTIX_PIXEL_FORMAT_FLOAT3; - } - -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - output_layers[0].data = input_ptr; - output_layers[0].width = rect_size.x; - output_layers[0].height = rect_size.y; - output_layers[0].rowStrideInBytes = input_stride; - output_layers[0].pixelStrideInBytes = pixel_stride; - int2 output_offset = overlap_offset; - overlap_offset = make_int2(0, 0); // Not supported by denoiser API, so apply manually -# else - output_layers[0].data = target_tile.buffer + pixel_offset; - output_layers[0].width = target_tile.w; - output_layers[0].height = target_tile.h; - output_layers[0].rowStrideInBytes = target_tile.stride * pixel_stride; - output_layers[0].pixelStrideInBytes = pixel_stride; -# endif - output_layers[0].format = OPTIX_PIXEL_FORMAT_FLOAT3; - -# if OPTIX_ABI_VERSION >= 47 - OptixDenoiserLayer image_layers = {}; - image_layers.input = input_layers[0]; - image_layers.output = output_layers[0]; - - OptixDenoiserGuideLayer guide_layers = {}; - guide_layers.albedo = input_layers[1]; - guide_layers.normal = input_layers[2]; -# endif - - // Finally run denonising - OptixDenoiserParams params = {}; // All parameters are disabled/zero -# if OPTIX_ABI_VERSION >= 47 - check_result_optix_ret(optixDenoiserInvoke(denoiser, - NULL, - ¶ms, - denoiser_state.device_pointer, - scratch_offset, - &guide_layers, - &image_layers, - 1, - overlap_offset.x, - overlap_offset.y, - denoiser_state.device_pointer + scratch_offset, - scratch_size)); -# else - check_result_optix_ret(optixDenoiserInvoke(denoiser, - NULL, - ¶ms, - denoiser_state.device_pointer, - scratch_offset, - input_layers, - task.denoising.input_passes, - overlap_offset.x, - overlap_offset.y, - output_layers, - denoiser_state.device_pointer + scratch_offset, - scratch_size)); -# endif - -# if OPTIX_DENOISER_NO_PIXEL_STRIDE - void *output_args[] = {&input_ptr, - &target_tile.buffer, - &output_offset.x, - &output_offset.y, - &rect_size.x, - &rect_size.y, - &target_tile.x, - &target_tile.y, - &target_tile.w, - &target_tile.h, - &target_tile.offset, - &target_tile.stride, - &task.pass_stride, - &rtile.sample}; - launch_filter_kernel( - "kernel_cuda_filter_convert_from_rgb", target_tile.w, target_tile.h, output_args); -# endif - - check_result_cuda_ret(cuStreamSynchronize(0)); - - task.unmap_neighbor_tiles(neighbors, this); - } - else { - // Run CUDA denoising kernels - DenoisingTask denoising(this, task); - CUDADevice::denoise(rtile, denoising); - } - - // Update task progress after the denoiser completed processing - task.update_progress(&rtile, rtile.w * rtile.h); - - return true; - } - - void launch_shader_eval(DeviceTask &task, int thread_index) - { - unsigned int rgen_index = PG_BACK; - if (task.shader_eval_type >= SHADER_EVAL_BAKE) - rgen_index = PG_BAKE; - if (task.shader_eval_type == SHADER_EVAL_DISPLACE) - rgen_index = PG_DISP; - - const CUDAContextScope scope(cuContext); - - device_ptr launch_params_ptr = launch_params.device_pointer + - thread_index * launch_params.data_elements; - - for (int sample = 0; sample < task.num_samples; ++sample) { - ShaderParams params; - params.input = (uint4 *)task.shader_input; - params.output = (float4 *)task.shader_output; - params.type = task.shader_eval_type; - params.filter = task.shader_filter; - params.sx = task.shader_x; - params.offset = task.offset; - params.sample = sample; - - check_result_cuda(cuMemcpyHtoDAsync(launch_params_ptr + offsetof(KernelParams, shader), - ¶ms, - sizeof(params), - cuda_stream[thread_index])); - - OptixShaderBindingTable sbt_params = {}; - sbt_params.raygenRecord = sbt_data.device_pointer + rgen_index * sizeof(SbtRecord); - sbt_params.missRecordBase = sbt_data.device_pointer + PG_MISS * sizeof(SbtRecord); - sbt_params.missRecordStrideInBytes = sizeof(SbtRecord); - sbt_params.missRecordCount = 1; - sbt_params.hitgroupRecordBase = sbt_data.device_pointer + PG_HITD * sizeof(SbtRecord); - sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); -# if OPTIX_ABI_VERSION >= 36 - sbt_params.hitgroupRecordCount = 5; // PG_HITD(_MOTION), PG_HITS(_MOTION), PG_HITL -# else - sbt_params.hitgroupRecordCount = 3; // PG_HITD, PG_HITS, PG_HITL -# endif - sbt_params.callablesRecordBase = sbt_data.device_pointer + PG_CALL * sizeof(SbtRecord); - sbt_params.callablesRecordCount = 3; - sbt_params.callablesRecordStrideInBytes = sizeof(SbtRecord); - - check_result_optix(optixLaunch(pipelines[PIP_SHADER_EVAL], - cuda_stream[thread_index], - launch_params_ptr, - launch_params.data_elements, - &sbt_params, - task.shader_w, - 1, - 1)); - - check_result_cuda(cuStreamSynchronize(cuda_stream[thread_index])); - - task.update_progress(NULL); - } - } - - bool build_optix_bvh(BVHOptiX *bvh, - OptixBuildOperation operation, - const OptixBuildInput &build_input, - uint16_t num_motion_steps) - { - /* Allocate and build acceleration structures only one at a time, to prevent parallel builds - * from running out of memory (since both original and compacted acceleration structure memory - * may be allocated at the same time for the duration of this function). The builds would - * otherwise happen on the same CUDA stream anyway. */ - static thread_mutex mutex; - thread_scoped_lock lock(mutex); - - const CUDAContextScope scope(cuContext); - - const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC); - - // Compute memory usage - OptixAccelBufferSizes sizes = {}; - OptixAccelBuildOptions options = {}; - options.operation = operation; - if (use_fast_trace_bvh) { - VLOG(2) << "Using fast to trace OptiX BVH"; - options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION; - } - else { - VLOG(2) << "Using fast to update OptiX BVH"; - options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_BUILD | OPTIX_BUILD_FLAG_ALLOW_UPDATE; - } - - options.motionOptions.numKeys = num_motion_steps; - options.motionOptions.flags = OPTIX_MOTION_FLAG_START_VANISH | OPTIX_MOTION_FLAG_END_VANISH; - options.motionOptions.timeBegin = 0.0f; - options.motionOptions.timeEnd = 1.0f; - - check_result_optix_ret( - optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes)); - - // Allocate required output buffers - device_only_memory temp_mem(this, "optix temp as build mem", true); - temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8); - if (!temp_mem.device_pointer) - return false; // Make sure temporary memory allocation succeeded - - // Acceleration structure memory has to be allocated on the device (not allowed to be on host) - device_only_memory &out_data = bvh->as_data; - if (operation == OPTIX_BUILD_OPERATION_BUILD) { - assert(out_data.device == this); - out_data.alloc_to_device(sizes.outputSizeInBytes); - if (!out_data.device_pointer) - return false; - } - else { - assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes); - } - - // Finally build the acceleration structure - OptixAccelEmitDesc compacted_size_prop = {}; - compacted_size_prop.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE; - // A tiny space was allocated for this property at the end of the temporary buffer above - // Make sure this pointer is 8-byte aligned - compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8); - - OptixTraversableHandle out_handle = 0; - check_result_optix_ret(optixAccelBuild(context, - NULL, - &options, - &build_input, - 1, - temp_mem.device_pointer, - sizes.tempSizeInBytes, - out_data.device_pointer, - sizes.outputSizeInBytes, - &out_handle, - use_fast_trace_bvh ? &compacted_size_prop : NULL, - use_fast_trace_bvh ? 1 : 0)); - bvh->traversable_handle = static_cast(out_handle); - - // Wait for all operations to finish - check_result_cuda_ret(cuStreamSynchronize(NULL)); - - // Compact acceleration structure to save memory (only if using fast trace as the - // OPTIX_BUILD_FLAG_ALLOW_COMPACTION flag is only set in this case). - if (use_fast_trace_bvh) { - uint64_t compacted_size = sizes.outputSizeInBytes; - check_result_cuda_ret( - cuMemcpyDtoH(&compacted_size, compacted_size_prop.result, sizeof(compacted_size))); - - // Temporary memory is no longer needed, so free it now to make space - temp_mem.free(); - - // There is no point compacting if the size does not change - if (compacted_size < sizes.outputSizeInBytes) { - device_only_memory compacted_data(this, "optix compacted as", false); - compacted_data.alloc_to_device(compacted_size); - if (!compacted_data.device_pointer) - // Do not compact if memory allocation for compacted acceleration structure fails - // Can just use the uncompacted one then, so succeed here regardless - return true; - - check_result_optix_ret(optixAccelCompact(context, - NULL, - out_handle, - compacted_data.device_pointer, - compacted_size, - &out_handle)); - bvh->traversable_handle = static_cast(out_handle); - - // Wait for compaction to finish - check_result_cuda_ret(cuStreamSynchronize(NULL)); - - std::swap(out_data.device_size, compacted_data.device_size); - std::swap(out_data.device_pointer, compacted_data.device_pointer); - // Original acceleration structure memory is freed when 'compacted_data' goes out of scope - } - } - - return true; - } - - void build_bvh(BVH *bvh, Progress &progress, bool refit) override - { - if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2) { - /* For baking CUDA is used, build appropriate BVH for that. */ - Device::build_bvh(bvh, progress, refit); - return; - } - - const bool use_fast_trace_bvh = (bvh->params.bvh_type == SceneParams::BVH_STATIC); - - free_bvh_memory_delayed(); - - BVHOptiX *const bvh_optix = static_cast(bvh); - - progress.set_substatus("Building OptiX acceleration structure"); - - if (!bvh->params.top_level) { - assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1); - - OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD; - /* Refit is only possible when using fast to trace BVH (because AS is built with - * OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above). */ - if (refit && !use_fast_trace_bvh) { - assert(bvh_optix->traversable_handle != 0); - operation = OPTIX_BUILD_OPERATION_UPDATE; - } - else { - bvh_optix->as_data.free(); - bvh_optix->traversable_handle = 0; - } - - // Build bottom level acceleration structures (BLAS) - Geometry *const geom = bvh->geometry[0]; - if (geom->geometry_type == Geometry::HAIR) { - // Build BLAS for curve primitives - Hair *const hair = static_cast(geom); - if (hair->num_curves() == 0) { - return; - } - - const size_t num_segments = hair->num_segments(); - - size_t num_motion_steps = 1; - Attribute *motion_keys = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if (motion_blur && hair->get_use_motion_blur() && motion_keys) { - num_motion_steps = hair->get_motion_steps(); - } - - device_vector aabb_data(this, "optix temp aabb data", MEM_READ_ONLY); -# if OPTIX_ABI_VERSION >= 36 - device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); - device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); - // Four control points for each curve segment - const size_t num_vertices = num_segments * 4; - if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { - index_data.alloc(num_segments); - vertex_data.alloc(num_vertices * num_motion_steps); - } - else -# endif - aabb_data.alloc(num_segments * num_motion_steps); - - // Get AABBs for each motion step - for (size_t step = 0; step < num_motion_steps; ++step) { - // The center step for motion vertices is not stored in the attribute - const float3 *keys = hair->get_curve_keys().data(); - size_t center_step = (num_motion_steps - 1) / 2; - if (step != center_step) { - size_t attr_offset = (step > center_step) ? step - 1 : step; - // Technically this is a float4 array, but sizeof(float3) == sizeof(float4) - keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size(); - } - - for (size_t j = 0, i = 0; j < hair->num_curves(); ++j) { - const Hair::Curve curve = hair->get_curve(j); -# if OPTIX_ABI_VERSION >= 36 - const array &curve_radius = hair->get_curve_radius(); -# endif - - for (int segment = 0; segment < curve.num_segments(); ++segment, ++i) { -# if OPTIX_ABI_VERSION >= 36 - if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { - int k0 = curve.first_key + segment; - int k1 = k0 + 1; - int ka = max(k0 - 1, curve.first_key); - int kb = min(k1 + 1, curve.first_key + curve.num_keys - 1); - - const float4 px = make_float4(keys[ka].x, keys[k0].x, keys[k1].x, keys[kb].x); - const float4 py = make_float4(keys[ka].y, keys[k0].y, keys[k1].y, keys[kb].y); - const float4 pz = make_float4(keys[ka].z, keys[k0].z, keys[k1].z, keys[kb].z); - const float4 pw = make_float4( - curve_radius[ka], curve_radius[k0], curve_radius[k1], curve_radius[kb]); - - // Convert Catmull-Rom data to Bezier spline - static const float4 cr2bsp0 = make_float4(+7, -4, +5, -2) / 6.f; - static const float4 cr2bsp1 = make_float4(-2, 11, -4, +1) / 6.f; - static const float4 cr2bsp2 = make_float4(+1, -4, 11, -2) / 6.f; - static const float4 cr2bsp3 = make_float4(-2, +5, -4, +7) / 6.f; - - index_data[i] = i * 4; - float4 *const v = vertex_data.data() + step * num_vertices + index_data[i]; - v[0] = make_float4( - dot(cr2bsp0, px), dot(cr2bsp0, py), dot(cr2bsp0, pz), dot(cr2bsp0, pw)); - v[1] = make_float4( - dot(cr2bsp1, px), dot(cr2bsp1, py), dot(cr2bsp1, pz), dot(cr2bsp1, pw)); - v[2] = make_float4( - dot(cr2bsp2, px), dot(cr2bsp2, py), dot(cr2bsp2, pz), dot(cr2bsp2, pw)); - v[3] = make_float4( - dot(cr2bsp3, px), dot(cr2bsp3, py), dot(cr2bsp3, pz), dot(cr2bsp3, pw)); - } - else -# endif - { - BoundBox bounds = BoundBox::empty; - curve.bounds_grow(segment, keys, hair->get_curve_radius().data(), bounds); - - const size_t index = step * num_segments + i; - aabb_data[index].minX = bounds.min.x; - aabb_data[index].minY = bounds.min.y; - aabb_data[index].minZ = bounds.min.z; - aabb_data[index].maxX = bounds.max.x; - aabb_data[index].maxY = bounds.max.y; - aabb_data[index].maxZ = bounds.max.z; - } - } - } - } - - // Upload AABB data to GPU - aabb_data.copy_to_device(); -# if OPTIX_ABI_VERSION >= 36 - index_data.copy_to_device(); - vertex_data.copy_to_device(); -# endif - - vector aabb_ptrs; - aabb_ptrs.reserve(num_motion_steps); -# if OPTIX_ABI_VERSION >= 36 - vector width_ptrs; - vector vertex_ptrs; - width_ptrs.reserve(num_motion_steps); - vertex_ptrs.reserve(num_motion_steps); -# endif - for (size_t step = 0; step < num_motion_steps; ++step) { - aabb_ptrs.push_back(aabb_data.device_pointer + step * num_segments * sizeof(OptixAabb)); -# if OPTIX_ABI_VERSION >= 36 - const device_ptr base_ptr = vertex_data.device_pointer + - step * num_vertices * sizeof(float4); - width_ptrs.push_back(base_ptr + 3 * sizeof(float)); // Offset by vertex size - vertex_ptrs.push_back(base_ptr); -# endif - } - - // Force a single any-hit call, so shadow record-all behavior works correctly - unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; - OptixBuildInput build_input = {}; -# if OPTIX_ABI_VERSION >= 36 - if (DebugFlags().optix.curves_api && hair->curve_shape == CURVE_THICK) { - build_input.type = OPTIX_BUILD_INPUT_TYPE_CURVES; - build_input.curveArray.curveType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; - build_input.curveArray.numPrimitives = num_segments; - build_input.curveArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); - build_input.curveArray.numVertices = num_vertices; - build_input.curveArray.vertexStrideInBytes = sizeof(float4); - build_input.curveArray.widthBuffers = (CUdeviceptr *)width_ptrs.data(); - build_input.curveArray.widthStrideInBytes = sizeof(float4); - build_input.curveArray.indexBuffer = (CUdeviceptr)index_data.device_pointer; - build_input.curveArray.indexStrideInBytes = sizeof(int); - build_input.curveArray.flag = build_flags; - build_input.curveArray.primitiveIndexOffset = hair->optix_prim_offset; - } - else -# endif - { - // Disable visibility test any-hit program, since it is already checked during - // intersection. Those trace calls that require anyhit can force it with a ray flag. - build_flags |= OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT; - - build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES; -# if OPTIX_ABI_VERSION < 23 - build_input.aabbArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); - build_input.aabbArray.numPrimitives = num_segments; - build_input.aabbArray.strideInBytes = sizeof(OptixAabb); - build_input.aabbArray.flags = &build_flags; - build_input.aabbArray.numSbtRecords = 1; - build_input.aabbArray.primitiveIndexOffset = hair->optix_prim_offset; -# else - build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); - build_input.customPrimitiveArray.numPrimitives = num_segments; - build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb); - build_input.customPrimitiveArray.flags = &build_flags; - build_input.customPrimitiveArray.numSbtRecords = 1; - build_input.customPrimitiveArray.primitiveIndexOffset = hair->optix_prim_offset; -# endif - } - - if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { - progress.set_error("Failed to build OptiX acceleration structure"); - } - } - else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - // Build BLAS for triangle primitives - Mesh *const mesh = static_cast(geom); - if (mesh->num_triangles() == 0) { - return; - } - - const size_t num_verts = mesh->get_verts().size(); - - size_t num_motion_steps = 1; - Attribute *motion_keys = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - if (motion_blur && mesh->get_use_motion_blur() && motion_keys) { - num_motion_steps = mesh->get_motion_steps(); - } - - device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); - index_data.alloc(mesh->get_triangles().size()); - memcpy(index_data.data(), - mesh->get_triangles().data(), - mesh->get_triangles().size() * sizeof(int)); - device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); - vertex_data.alloc(num_verts * num_motion_steps); - - for (size_t step = 0; step < num_motion_steps; ++step) { - const float3 *verts = mesh->get_verts().data(); - - size_t center_step = (num_motion_steps - 1) / 2; - // The center step for motion vertices is not stored in the attribute - if (step != center_step) { - verts = motion_keys->data_float3() + - (step > center_step ? step - 1 : step) * num_verts; - } - - memcpy(vertex_data.data() + num_verts * step, verts, num_verts * sizeof(float3)); - } - - // Upload triangle data to GPU - index_data.copy_to_device(); - vertex_data.copy_to_device(); - - vector vertex_ptrs; - vertex_ptrs.reserve(num_motion_steps); - for (size_t step = 0; step < num_motion_steps; ++step) { - vertex_ptrs.push_back(vertex_data.device_pointer + num_verts * step * sizeof(float3)); - } - - // Force a single any-hit call, so shadow record-all behavior works correctly - unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; - OptixBuildInput build_input = {}; - build_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES; - build_input.triangleArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); - build_input.triangleArray.numVertices = num_verts; - build_input.triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3; - build_input.triangleArray.vertexStrideInBytes = sizeof(float3); - build_input.triangleArray.indexBuffer = index_data.device_pointer; - build_input.triangleArray.numIndexTriplets = mesh->num_triangles(); - build_input.triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3; - build_input.triangleArray.indexStrideInBytes = 3 * sizeof(int); - build_input.triangleArray.flags = &build_flags; - // The SBT does not store per primitive data since Cycles already allocates separate - // buffers for that purpose. OptiX does not allow this to be zero though, so just pass in - // one and rely on that having the same meaning in this case. - build_input.triangleArray.numSbtRecords = 1; - build_input.triangleArray.primitiveIndexOffset = mesh->optix_prim_offset; - - if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { - progress.set_error("Failed to build OptiX acceleration structure"); - } - } - } - else { - unsigned int num_instances = 0; - unsigned int max_num_instances = 0xFFFFFFFF; - - bvh_optix->as_data.free(); - bvh_optix->traversable_handle = 0; - bvh_optix->motion_transform_data.free(); - - optixDeviceContextGetProperty(context, - OPTIX_DEVICE_PROPERTY_LIMIT_MAX_INSTANCE_ID, - &max_num_instances, - sizeof(max_num_instances)); - // Do not count first bit, which is used to distinguish instanced and non-instanced objects - max_num_instances >>= 1; - if (bvh->objects.size() > max_num_instances) { - progress.set_error( - "Failed to build OptiX acceleration structure because there are too many instances"); - return; - } - - // Fill instance descriptions -# if OPTIX_ABI_VERSION < 41 - device_vector aabbs(this, "optix tlas aabbs", MEM_READ_ONLY); - aabbs.alloc(bvh->objects.size()); -# endif - device_vector instances(this, "optix tlas instances", MEM_READ_ONLY); - instances.alloc(bvh->objects.size()); - - // Calculate total motion transform size and allocate memory for them - size_t motion_transform_offset = 0; - if (motion_blur) { - size_t total_motion_transform_size = 0; - for (Object *const ob : bvh->objects) { - if (ob->is_traceable() && ob->use_motion()) { - total_motion_transform_size = align_up(total_motion_transform_size, - OPTIX_TRANSFORM_BYTE_ALIGNMENT); - const size_t motion_keys = max(ob->get_motion().size(), 2) - 2; - total_motion_transform_size = total_motion_transform_size + - sizeof(OptixSRTMotionTransform) + - motion_keys * sizeof(OptixSRTData); - } - } - - assert(bvh_optix->motion_transform_data.device == this); - bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size); - } - - for (Object *ob : bvh->objects) { - // Skip non-traceable objects - if (!ob->is_traceable()) - continue; - - BVHOptiX *const blas = static_cast(ob->get_geometry()->bvh); - OptixTraversableHandle handle = blas->traversable_handle; - -# if OPTIX_ABI_VERSION < 41 - OptixAabb &aabb = aabbs[num_instances]; - aabb.minX = ob->bounds.min.x; - aabb.minY = ob->bounds.min.y; - aabb.minZ = ob->bounds.min.z; - aabb.maxX = ob->bounds.max.x; - aabb.maxY = ob->bounds.max.y; - aabb.maxZ = ob->bounds.max.z; -# endif - - OptixInstance &instance = instances[num_instances++]; - memset(&instance, 0, sizeof(instance)); - - // Clear transform to identity matrix - instance.transform[0] = 1.0f; - instance.transform[5] = 1.0f; - instance.transform[10] = 1.0f; - - // Set user instance ID to object index (but leave low bit blank) - instance.instanceId = ob->get_device_index() << 1; - - // Have to have at least one bit in the mask, or else instance would always be culled - instance.visibilityMask = 1; - - if (ob->get_geometry()->has_volume) { - // Volumes have a special bit set in the visibility mask so a trace can mask only volumes - instance.visibilityMask |= 2; - } - - if (ob->get_geometry()->geometry_type == Geometry::HAIR) { - // Same applies to curves (so they can be skipped in local trace calls) - instance.visibilityMask |= 4; - -# if OPTIX_ABI_VERSION >= 36 - if (motion_blur && ob->get_geometry()->has_motion_blur() && - DebugFlags().optix.curves_api && - static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { - // Select between motion blur and non-motion blur built-in intersection module - instance.sbtOffset = PG_HITD_MOTION - PG_HITD; - } -# endif - } - - // Insert motion traversable if object has motion - if (motion_blur && ob->use_motion()) { - size_t motion_keys = max(ob->get_motion().size(), 2) - 2; - size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + - motion_keys * sizeof(OptixSRTData); - - const CUDAContextScope scope(cuContext); - - motion_transform_offset = align_up(motion_transform_offset, - OPTIX_TRANSFORM_BYTE_ALIGNMENT); - CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer + - motion_transform_offset; - motion_transform_offset += motion_transform_size; - - // Allocate host side memory for motion transform and fill it with transform data - OptixSRTMotionTransform &motion_transform = *reinterpret_cast( - new uint8_t[motion_transform_size]); - motion_transform.child = handle; - motion_transform.motionOptions.numKeys = ob->get_motion().size(); - motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; - motion_transform.motionOptions.timeBegin = 0.0f; - motion_transform.motionOptions.timeEnd = 1.0f; - - OptixSRTData *const srt_data = motion_transform.srtData; - array decomp(ob->get_motion().size()); - transform_motion_decompose( - decomp.data(), ob->get_motion().data(), ob->get_motion().size()); - - for (size_t i = 0; i < ob->get_motion().size(); ++i) { - // Scale - srt_data[i].sx = decomp[i].y.w; // scale.x.x - srt_data[i].sy = decomp[i].z.w; // scale.y.y - srt_data[i].sz = decomp[i].w.w; // scale.z.z - - // Shear - srt_data[i].a = decomp[i].z.x; // scale.x.y - srt_data[i].b = decomp[i].z.y; // scale.x.z - srt_data[i].c = decomp[i].w.x; // scale.y.z - assert(decomp[i].z.z == 0.0f); // scale.y.x - assert(decomp[i].w.y == 0.0f); // scale.z.x - assert(decomp[i].w.z == 0.0f); // scale.z.y - - // Pivot point - srt_data[i].pvx = 0.0f; - srt_data[i].pvy = 0.0f; - srt_data[i].pvz = 0.0f; - - // Rotation - srt_data[i].qx = decomp[i].x.x; - srt_data[i].qy = decomp[i].x.y; - srt_data[i].qz = decomp[i].x.z; - srt_data[i].qw = decomp[i].x.w; - - // Translation - srt_data[i].tx = decomp[i].y.x; - srt_data[i].ty = decomp[i].y.y; - srt_data[i].tz = decomp[i].y.z; - } - - // Upload motion transform to GPU - cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); - delete[] reinterpret_cast(&motion_transform); - - // Disable instance transform if object uses motion transform already - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - - // Get traversable handle to motion transform - optixConvertPointerToTraversableHandle(context, - motion_transform_gpu, - OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, - &instance.traversableHandle); - } - else { - instance.traversableHandle = handle; - - if (ob->get_geometry()->is_instanced()) { - // Set transform matrix - memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); - } - else { - // Disable instance transform if geometry already has it applied to vertex data - instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; - // Non-instanced objects read ID from 'prim_object', so distinguish - // them from instanced objects with the low bit set - instance.instanceId |= 1; - } - } - } - - // Upload instance descriptions -# if OPTIX_ABI_VERSION < 41 - aabbs.resize(num_instances); - aabbs.copy_to_device(); -# endif - instances.resize(num_instances); - instances.copy_to_device(); - - // Build top-level acceleration structure (TLAS) - OptixBuildInput build_input = {}; - build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; -# if OPTIX_ABI_VERSION < 41 // Instance AABBs no longer need to be set since OptiX 7.2 - build_input.instanceArray.aabbs = aabbs.device_pointer; - build_input.instanceArray.numAabbs = num_instances; -# endif - build_input.instanceArray.instances = instances.device_pointer; - build_input.instanceArray.numInstances = num_instances; - - if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) { - progress.set_error("Failed to build OptiX acceleration structure"); - } - tlas_handle = bvh_optix->traversable_handle; - } - } - - void release_optix_bvh(BVH *bvh) override - { - thread_scoped_lock lock(delayed_free_bvh_mutex); - /* Do delayed free of BVH memory, since geometry holding BVH might be deleted - * while GPU is still rendering. */ - BVHOptiX *const bvh_optix = static_cast(bvh); - - delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->as_data)); - delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->motion_transform_data)); - bvh_optix->traversable_handle = 0; - } - - void free_bvh_memory_delayed() - { - thread_scoped_lock lock(delayed_free_bvh_mutex); - delayed_free_bvh_memory.free_memory(); - } - - void const_copy_to(const char *name, void *host, size_t size) override - { - // Set constant memory for CUDA module - // TODO(pmours): This is only used for tonemapping (see 'film_convert'). - // Could be removed by moving those functions to filter CUDA module. - CUDADevice::const_copy_to(name, host, size); - - if (strcmp(name, "__data") == 0) { - assert(size <= sizeof(KernelData)); - - // Update traversable handle (since it is different for each device on multi devices) - KernelData *const data = (KernelData *)host; - *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; - - update_launch_params(offsetof(KernelParams, data), host, size); - return; - } - - // Update data storage pointers in launch parameters -# define KERNEL_TEX(data_type, tex_name) \ - if (strcmp(name, #tex_name) == 0) { \ - update_launch_params(offsetof(KernelParams, tex_name), host, size); \ - return; \ - } -# include "kernel/kernel_textures.h" -# undef KERNEL_TEX - } - - void update_launch_params(size_t offset, void *data, size_t data_size) - { - const CUDAContextScope scope(cuContext); - - for (int i = 0; i < info.cpu_threads; ++i) - check_result_cuda( - cuMemcpyHtoD(launch_params.device_pointer + i * launch_params.data_elements + offset, - data, - data_size)); - } - - void task_add(DeviceTask &task) override - { - // Upload texture information to device if it has changed since last launch - load_texture_info(); - - if (task.type == DeviceTask::FILM_CONVERT) { - // Execute in main thread because of OpenGL access - film_convert(task, task.buffer, task.rgba_byte, task.rgba_half); - return; - } - - if (task.type == DeviceTask::DENOISE_BUFFER) { - // Execute denoising in a single thread (e.g. to avoid race conditions during creation) - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy, 0); - }); - return; - } - - // Split task into smaller ones - list tasks; - task.split(tasks, info.cpu_threads); - - // Queue tasks in internal task pool - int task_index = 0; - for (DeviceTask &task : tasks) { - task_pool.push([=] { - // Using task index parameter instead of thread index, since number of CUDA streams may - // differ from number of threads - DeviceTask task_copy = task; - thread_run(task_copy, task_index); - }); - task_index++; - } - } - - void task_wait() override - { - // Wait for all queued tasks to finish - task_pool.wait_work(); - } - - void task_cancel() override - { - // Cancel any remaining tasks in the internal pool - task_pool.cancel(); - } -}; - -bool device_optix_init() -{ - if (g_optixFunctionTable.optixDeviceContextCreate != NULL) - return true; // Already initialized function table - - // Need to initialize CUDA as well - if (!device_cuda_init()) - return false; - - const OptixResult result = optixInit(); - - if (result == OPTIX_ERROR_UNSUPPORTED_ABI_VERSION) { - VLOG(1) << "OptiX initialization failed because the installed NVIDIA driver is too old. " - "Please update to the latest driver first!"; - return false; - } - else if (result != OPTIX_SUCCESS) { - VLOG(1) << "OptiX initialization failed with error code " << (unsigned int)result; - return false; - } - - // Loaded OptiX successfully! - return true; -} - -void device_optix_info(const vector &cuda_devices, vector &devices) -{ - devices.reserve(cuda_devices.size()); - - // Simply add all supported CUDA devices as OptiX devices again - for (DeviceInfo info : cuda_devices) { - assert(info.type == DEVICE_CUDA); - - int major; - cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, info.num); - if (major < 5) { - continue; // Only Maxwell and up are supported by OptiX - } - - info.type = DEVICE_OPTIX; - info.id += "_OptiX"; - info.denoisers |= DENOISER_OPTIX; - info.has_branched_path = false; - - devices.push_back(info); - } -} - -Device *device_optix_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) -{ - return new OptiXDevice(info, stats, profiler, background); -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/device_split_kernel.cpp b/intern/cycles/device/device_split_kernel.cpp deleted file mode 100644 index 9889f688aaa..00000000000 --- a/intern/cycles/device/device_split_kernel.cpp +++ /dev/null @@ -1,389 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "device/device_split_kernel.h" - -#include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data_types.h" - -#include "util/util_logging.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -static const double alpha = 0.1; /* alpha for rolling average */ - -DeviceSplitKernel::DeviceSplitKernel(Device *device) - : device(device), - split_data(device, "split_data"), - ray_state(device, "ray_state", MEM_READ_WRITE), - queue_index(device, "queue_index"), - use_queues_flag(device, "use_queues_flag"), - work_pool_wgs(device, "work_pool_wgs"), - kernel_data_initialized(false) -{ - avg_time_per_sample = 0.0; - - kernel_path_init = NULL; - kernel_scene_intersect = NULL; - kernel_lamp_emission = NULL; - kernel_do_volume = NULL; - kernel_queue_enqueue = NULL; - kernel_indirect_background = NULL; - kernel_shader_setup = NULL; - kernel_shader_sort = NULL; - kernel_shader_eval = NULL; - kernel_holdout_emission_blurring_pathtermination_ao = NULL; - kernel_subsurface_scatter = NULL; - kernel_direct_lighting = NULL; - kernel_shadow_blocked_ao = NULL; - kernel_shadow_blocked_dl = NULL; - kernel_enqueue_inactive = NULL; - kernel_next_iteration_setup = NULL; - kernel_indirect_subsurface = NULL; - kernel_buffer_update = NULL; - kernel_adaptive_stopping = NULL; - kernel_adaptive_filter_x = NULL; - kernel_adaptive_filter_y = NULL; - kernel_adaptive_adjust_samples = NULL; -} - -DeviceSplitKernel::~DeviceSplitKernel() -{ - split_data.free(); - ray_state.free(); - use_queues_flag.free(); - queue_index.free(); - work_pool_wgs.free(); - - delete kernel_path_init; - delete kernel_scene_intersect; - delete kernel_lamp_emission; - delete kernel_do_volume; - delete kernel_queue_enqueue; - delete kernel_indirect_background; - delete kernel_shader_setup; - delete kernel_shader_sort; - delete kernel_shader_eval; - delete kernel_holdout_emission_blurring_pathtermination_ao; - delete kernel_subsurface_scatter; - delete kernel_direct_lighting; - delete kernel_shadow_blocked_ao; - delete kernel_shadow_blocked_dl; - delete kernel_enqueue_inactive; - delete kernel_next_iteration_setup; - delete kernel_indirect_subsurface; - delete kernel_buffer_update; - delete kernel_adaptive_stopping; - delete kernel_adaptive_filter_x; - delete kernel_adaptive_filter_y; - delete kernel_adaptive_adjust_samples; -} - -bool DeviceSplitKernel::load_kernels(const DeviceRequestedFeatures &requested_features) -{ -#define LOAD_KERNEL(name) \ - kernel_##name = get_split_kernel_function(#name, requested_features); \ - if (!kernel_##name) { \ - device->set_error(string("Split kernel error: failed to load kernel_") + #name); \ - return false; \ - } - - LOAD_KERNEL(path_init); - LOAD_KERNEL(scene_intersect); - LOAD_KERNEL(lamp_emission); - if (requested_features.use_volume) { - LOAD_KERNEL(do_volume); - } - LOAD_KERNEL(queue_enqueue); - LOAD_KERNEL(indirect_background); - LOAD_KERNEL(shader_setup); - LOAD_KERNEL(shader_sort); - LOAD_KERNEL(shader_eval); - LOAD_KERNEL(holdout_emission_blurring_pathtermination_ao); - LOAD_KERNEL(subsurface_scatter); - LOAD_KERNEL(direct_lighting); - LOAD_KERNEL(shadow_blocked_ao); - LOAD_KERNEL(shadow_blocked_dl); - LOAD_KERNEL(enqueue_inactive); - LOAD_KERNEL(next_iteration_setup); - LOAD_KERNEL(indirect_subsurface); - LOAD_KERNEL(buffer_update); - LOAD_KERNEL(adaptive_stopping); - LOAD_KERNEL(adaptive_filter_x); - LOAD_KERNEL(adaptive_filter_y); - LOAD_KERNEL(adaptive_adjust_samples); - -#undef LOAD_KERNEL - - /* Re-initialiaze kernel-dependent data when kernels change. */ - kernel_data_initialized = false; - - return true; -} - -size_t DeviceSplitKernel::max_elements_for_max_buffer_size(device_memory &kg, - device_memory &data, - uint64_t max_buffer_size) -{ - uint64_t size_per_element = state_buffer_size(kg, data, 1024) / 1024; - VLOG(1) << "Split state element size: " << string_human_readable_number(size_per_element) - << " bytes. (" << string_human_readable_size(size_per_element) << ")."; - return max_buffer_size / size_per_element; -} - -bool DeviceSplitKernel::path_trace(DeviceTask &task, - RenderTile &tile, - device_memory &kgbuffer, - device_memory &kernel_data) -{ - if (device->have_error()) { - return false; - } - - /* Allocate all required global memory once. */ - if (!kernel_data_initialized) { - kernel_data_initialized = true; - - /* Set local size */ - int2 lsize = split_kernel_local_size(); - local_size[0] = lsize[0]; - local_size[1] = lsize[1]; - - /* Set global size */ - int2 gsize = split_kernel_global_size(kgbuffer, kernel_data, task); - - /* Make sure that set work size is a multiple of local - * work size dimensions. - */ - global_size[0] = round_up(gsize[0], local_size[0]); - global_size[1] = round_up(gsize[1], local_size[1]); - - int num_global_elements = global_size[0] * global_size[1]; - assert(num_global_elements % WORK_POOL_SIZE == 0); - - /* Calculate max groups */ - - /* Denotes the maximum work groups possible w.r.t. current requested tile size. */ - unsigned int work_pool_size = (device->info.type == DEVICE_CPU) ? WORK_POOL_SIZE_CPU : - WORK_POOL_SIZE_GPU; - unsigned int max_work_groups = num_global_elements / work_pool_size + 1; - - /* Allocate work_pool_wgs memory. */ - work_pool_wgs.alloc_to_device(max_work_groups); - queue_index.alloc_to_device(NUM_QUEUES); - use_queues_flag.alloc_to_device(1); - split_data.alloc_to_device(state_buffer_size(kgbuffer, kernel_data, num_global_elements)); - ray_state.alloc(num_global_elements); - } - - /* Number of elements in the global state buffer */ - int num_global_elements = global_size[0] * global_size[1]; - -#define ENQUEUE_SPLIT_KERNEL(name, global_size, local_size) \ - if (device->have_error()) { \ - return false; \ - } \ - if (!kernel_##name->enqueue( \ - KernelDimensions(global_size, local_size), kgbuffer, kernel_data)) { \ - return false; \ - } - - tile.sample = tile.start_sample; - - /* for exponential increase between tile updates */ - int time_multiplier = 1; - - while (tile.sample < tile.start_sample + tile.num_samples) { - /* to keep track of how long it takes to run a number of samples */ - double start_time = time_dt(); - - /* initial guess to start rolling average */ - const int initial_num_samples = 1; - /* approx number of samples per second */ - const int samples_per_second = (avg_time_per_sample > 0.0) ? - int(double(time_multiplier) / avg_time_per_sample) + 1 : - initial_num_samples; - - RenderTile subtile = tile; - subtile.start_sample = tile.sample; - subtile.num_samples = samples_per_second; - - if (task.adaptive_sampling.use) { - subtile.num_samples = task.adaptive_sampling.align_samples(subtile.start_sample, - subtile.num_samples); - } - - /* Don't go beyond requested number of samples. */ - subtile.num_samples = min(subtile.num_samples, - tile.start_sample + tile.num_samples - tile.sample); - - if (device->have_error()) { - return false; - } - - /* reset state memory here as global size for data_init - * kernel might not be large enough to do in kernel - */ - work_pool_wgs.zero_to_device(); - split_data.zero_to_device(); - ray_state.zero_to_device(); - - if (!enqueue_split_kernel_data_init(KernelDimensions(global_size, local_size), - subtile, - num_global_elements, - kgbuffer, - kernel_data, - split_data, - ray_state, - queue_index, - use_queues_flag, - work_pool_wgs)) { - return false; - } - - ENQUEUE_SPLIT_KERNEL(path_init, global_size, local_size); - - bool activeRaysAvailable = true; - double cancel_time = DBL_MAX; - - while (activeRaysAvailable) { - /* Do path-iteration in host [Enqueue Path-iteration kernels. */ - for (int PathIter = 0; PathIter < 16; PathIter++) { - ENQUEUE_SPLIT_KERNEL(scene_intersect, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(lamp_emission, global_size, local_size); - if (kernel_do_volume) { - ENQUEUE_SPLIT_KERNEL(do_volume, global_size, local_size); - } - ENQUEUE_SPLIT_KERNEL(queue_enqueue, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(indirect_background, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shader_setup, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shader_sort, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shader_eval, global_size, local_size); - ENQUEUE_SPLIT_KERNEL( - holdout_emission_blurring_pathtermination_ao, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(subsurface_scatter, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(queue_enqueue, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(direct_lighting, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shadow_blocked_ao, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(shadow_blocked_dl, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(enqueue_inactive, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(next_iteration_setup, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(indirect_subsurface, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(queue_enqueue, global_size, local_size); - ENQUEUE_SPLIT_KERNEL(buffer_update, global_size, local_size); - - if (task.get_cancel() && cancel_time == DBL_MAX) { - /* Wait up to twice as many seconds for current samples to finish - * to avoid artifacts in render result from ending too soon. - */ - cancel_time = time_dt() + 2.0 * time_multiplier; - } - - if (time_dt() > cancel_time) { - return true; - } - } - - /* Decide if we should exit path-iteration in host. */ - ray_state.copy_from_device(0, global_size[0] * global_size[1], 1); - - activeRaysAvailable = false; - - for (int rayStateIter = 0; rayStateIter < global_size[0] * global_size[1]; ++rayStateIter) { - if (!IS_STATE(ray_state.data(), rayStateIter, RAY_INACTIVE)) { - if (IS_STATE(ray_state.data(), rayStateIter, RAY_INVALID)) { - /* Something went wrong, abort to avoid looping endlessly. */ - device->set_error("Split kernel error: invalid ray state"); - return false; - } - - /* Not all rays are RAY_INACTIVE. */ - activeRaysAvailable = true; - break; - } - } - - if (time_dt() > cancel_time) { - return true; - } - } - - int filter_sample = tile.sample + subtile.num_samples - 1; - if (task.adaptive_sampling.use && task.adaptive_sampling.need_filter(filter_sample)) { - size_t buffer_size[2]; - buffer_size[0] = round_up(tile.w, local_size[0]); - buffer_size[1] = round_up(tile.h, local_size[1]); - kernel_adaptive_stopping->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - buffer_size[0] = round_up(tile.h, local_size[0]); - buffer_size[1] = round_up(1, local_size[1]); - kernel_adaptive_filter_x->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - buffer_size[0] = round_up(tile.w, local_size[0]); - buffer_size[1] = round_up(1, local_size[1]); - kernel_adaptive_filter_y->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - } - - double time_per_sample = ((time_dt() - start_time) / subtile.num_samples); - - if (avg_time_per_sample == 0.0) { - /* start rolling average */ - avg_time_per_sample = time_per_sample; - } - else { - avg_time_per_sample = alpha * time_per_sample + (1.0 - alpha) * avg_time_per_sample; - } - -#undef ENQUEUE_SPLIT_KERNEL - - tile.sample += subtile.num_samples; - task.update_progress(&tile, tile.w * tile.h * subtile.num_samples); - - time_multiplier = min(time_multiplier << 1, 10); - - if (task.get_cancel()) { - return true; - } - } - - if (task.adaptive_sampling.use) { - /* Reset the start samples. */ - RenderTile subtile = tile; - subtile.start_sample = tile.start_sample; - subtile.num_samples = tile.sample - tile.start_sample; - enqueue_split_kernel_data_init(KernelDimensions(global_size, local_size), - subtile, - num_global_elements, - kgbuffer, - kernel_data, - split_data, - ray_state, - queue_index, - use_queues_flag, - work_pool_wgs); - size_t buffer_size[2]; - buffer_size[0] = round_up(tile.w, local_size[0]); - buffer_size[1] = round_up(tile.h, local_size[1]); - kernel_adaptive_adjust_samples->enqueue( - KernelDimensions(buffer_size, local_size), kgbuffer, kernel_data); - } - - return true; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_split_kernel.h b/intern/cycles/device/device_split_kernel.h deleted file mode 100644 index 07a21b10299..00000000000 --- a/intern/cycles/device/device_split_kernel.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_SPLIT_KERNEL_H__ -#define __DEVICE_SPLIT_KERNEL_H__ - -#include "device/device.h" -#include "render/buffers.h" - -CCL_NAMESPACE_BEGIN - -/* When allocate global memory in chunks. We may not be able to - * allocate exactly "CL_DEVICE_MAX_MEM_ALLOC_SIZE" bytes in chunks; - * Since some bytes may be needed for aligning chunks of memory; - * This is the amount of memory that we dedicate for that purpose. - */ -#define DATA_ALLOCATION_MEM_FACTOR 5000000 // 5MB - -/* Types used for split kernel */ - -class KernelDimensions { - public: - size_t global_size[2]; - size_t local_size[2]; - - KernelDimensions(size_t global_size_[2], size_t local_size_[2]) - { - memcpy(global_size, global_size_, sizeof(global_size)); - memcpy(local_size, local_size_, sizeof(local_size)); - } -}; - -class SplitKernelFunction { - public: - virtual ~SplitKernelFunction() - { - } - - /* enqueue the kernel, returns false if there is an error */ - virtual bool enqueue(const KernelDimensions &dim, device_memory &kg, device_memory &data) = 0; -}; - -class DeviceSplitKernel { - private: - Device *device; - - SplitKernelFunction *kernel_path_init; - SplitKernelFunction *kernel_scene_intersect; - SplitKernelFunction *kernel_lamp_emission; - SplitKernelFunction *kernel_do_volume; - SplitKernelFunction *kernel_queue_enqueue; - SplitKernelFunction *kernel_indirect_background; - SplitKernelFunction *kernel_shader_setup; - SplitKernelFunction *kernel_shader_sort; - SplitKernelFunction *kernel_shader_eval; - SplitKernelFunction *kernel_holdout_emission_blurring_pathtermination_ao; - SplitKernelFunction *kernel_subsurface_scatter; - SplitKernelFunction *kernel_direct_lighting; - SplitKernelFunction *kernel_shadow_blocked_ao; - SplitKernelFunction *kernel_shadow_blocked_dl; - SplitKernelFunction *kernel_enqueue_inactive; - SplitKernelFunction *kernel_next_iteration_setup; - SplitKernelFunction *kernel_indirect_subsurface; - SplitKernelFunction *kernel_buffer_update; - SplitKernelFunction *kernel_adaptive_stopping; - SplitKernelFunction *kernel_adaptive_filter_x; - SplitKernelFunction *kernel_adaptive_filter_y; - SplitKernelFunction *kernel_adaptive_adjust_samples; - - /* Global memory variables [porting]; These memory is used for - * co-operation between different kernels; Data written by one - * kernel will be available to another kernel via this global - * memory. - */ - device_only_memory split_data; - device_vector ray_state; - device_only_memory - queue_index; /* Array of size num_queues that tracks the size of each queue. */ - - /* Flag to make sceneintersect and lampemission kernel use queues. */ - device_only_memory use_queues_flag; - - /* Approximate time it takes to complete one sample */ - double avg_time_per_sample; - - /* Work pool with respect to each work group. */ - device_only_memory work_pool_wgs; - - /* Cached kernel-dependent data, initialized once. */ - bool kernel_data_initialized; - size_t local_size[2]; - size_t global_size[2]; - - public: - explicit DeviceSplitKernel(Device *device); - virtual ~DeviceSplitKernel(); - - bool load_kernels(const DeviceRequestedFeatures &requested_features); - bool path_trace(DeviceTask &task, - RenderTile &rtile, - device_memory &kgbuffer, - device_memory &kernel_data); - - virtual uint64_t state_buffer_size(device_memory &kg, - device_memory &data, - size_t num_threads) = 0; - size_t max_elements_for_max_buffer_size(device_memory &kg, - device_memory &data, - uint64_t max_buffer_size); - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data_, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs) = 0; - - virtual SplitKernelFunction *get_split_kernel_function(const string &kernel_name, - const DeviceRequestedFeatures &) = 0; - virtual int2 split_kernel_local_size() = 0; - virtual int2 split_kernel_global_size(device_memory &kg, - device_memory &data, - DeviceTask &task) = 0; -}; - -CCL_NAMESPACE_END - -#endif /* __DEVICE_SPLIT_KERNEL_H__ */ diff --git a/intern/cycles/device/device_task.cpp b/intern/cycles/device/device_task.cpp deleted file mode 100644 index 55fbaa31e42..00000000000 --- a/intern/cycles/device/device_task.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "device/device_task.h" - -#include "render/buffers.h" - -#include "util/util_algorithm.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -/* Device Task */ - -DeviceTask::DeviceTask(Type type_) - : type(type_), - x(0), - y(0), - w(0), - h(0), - rgba_byte(0), - rgba_half(0), - buffer(0), - sample(0), - num_samples(1), - shader_input(0), - shader_output(0), - shader_eval_type(0), - shader_filter(0), - shader_x(0), - shader_w(0), - buffers(nullptr), - tile_types(0), - denoising_from_render(false), - pass_stride(0), - frame_stride(0), - target_pass_stride(0), - pass_denoising_data(0), - pass_denoising_clean(0), - need_finish_queue(false), - integrator_branched(false) -{ - last_update_time = time_dt(); -} - -int DeviceTask::get_subtask_count(int num, int max_size) const -{ - if (max_size != 0) { - int max_size_num; - - if (type == SHADER) { - max_size_num = (shader_w + max_size - 1) / max_size; - } - else { - max_size = max(1, max_size / w); - max_size_num = (h + max_size - 1) / max_size; - } - - num = max(max_size_num, num); - } - - if (type == SHADER) { - num = min(shader_w, num); - } - else if (type == RENDER) { - } - else { - num = min(h, num); - } - - return num; -} - -void DeviceTask::split(list &tasks, int num, int max_size) const -{ - num = get_subtask_count(num, max_size); - - if (type == SHADER) { - for (int i = 0; i < num; i++) { - int tx = shader_x + (shader_w / num) * i; - int tw = (i == num - 1) ? shader_w - i * (shader_w / num) : shader_w / num; - - DeviceTask task = *this; - - task.shader_x = tx; - task.shader_w = tw; - - tasks.push_back(task); - } - } - else if (type == RENDER) { - for (int i = 0; i < num; i++) - tasks.push_back(*this); - } - else { - for (int i = 0; i < num; i++) { - int ty = y + (h / num) * i; - int th = (i == num - 1) ? h - i * (h / num) : h / num; - - DeviceTask task = *this; - - task.y = ty; - task.h = th; - - tasks.push_back(task); - } - } -} - -void DeviceTask::update_progress(RenderTile *rtile, int pixel_samples) -{ - if (type == FILM_CONVERT) - return; - - if (update_progress_sample) { - if (pixel_samples == -1) { - pixel_samples = shader_w; - } - update_progress_sample(pixel_samples, rtile ? rtile->sample : 0); - } - - if (update_tile_sample) { - double current_time = time_dt(); - - if (current_time - last_update_time >= 1.0) { - update_tile_sample(*rtile); - - last_update_time = current_time; - } - } -} - -/* Adaptive Sampling */ - -AdaptiveSampling::AdaptiveSampling() : use(true), adaptive_step(0), min_samples(0) -{ -} - -/* Render samples in steps that align with the adaptive filtering. */ -int AdaptiveSampling::align_samples(int sample, int num_samples) const -{ - int end_sample = sample + num_samples; - - /* Round down end sample to the nearest sample that needs filtering. */ - end_sample &= ~(adaptive_step - 1); - - if (end_sample <= sample) { - /* In order to reach the next sample that needs filtering, we'd need - * to increase num_samples. We don't do that in this function, so - * just keep it as is and don't filter this time around. */ - return num_samples; - } - return end_sample - sample; -} - -bool AdaptiveSampling::need_filter(int sample) const -{ - if (sample > min_samples) { - return (sample & (adaptive_step - 1)) == (adaptive_step - 1); - } - else { - return false; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_task.h b/intern/cycles/device/device_task.h deleted file mode 100644 index 3f7cf47b692..00000000000 --- a/intern/cycles/device/device_task.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __DEVICE_TASK_H__ -#define __DEVICE_TASK_H__ - -#include "device/device_memory.h" - -#include "util/util_function.h" -#include "util/util_list.h" - -CCL_NAMESPACE_BEGIN - -/* Device Task */ - -class Device; -class RenderBuffers; -class RenderTile; -class RenderTileNeighbors; -class Tile; - -enum DenoiserType { - DENOISER_NLM = 1, - DENOISER_OPTIX = 2, - DENOISER_OPENIMAGEDENOISE = 4, - DENOISER_NUM, - - DENOISER_NONE = 0, - DENOISER_ALL = ~0, -}; - -enum DenoiserInput { - DENOISER_INPUT_RGB = 1, - DENOISER_INPUT_RGB_ALBEDO = 2, - DENOISER_INPUT_RGB_ALBEDO_NORMAL = 3, - - DENOISER_INPUT_NUM, -}; - -typedef int DenoiserTypeMask; - -class DenoiseParams { - public: - /* Apply denoiser to image. */ - bool use; - /* Output denoising data passes (possibly without applying the denoiser). */ - bool store_passes; - - /* Denoiser type. */ - DenoiserType type; - - /* Viewport start sample. */ - int start_sample; - - /** Native Denoiser. */ - - /* Pixel radius for neighboring pixels to take into account. */ - int radius; - /* Controls neighbor pixel weighting for the denoising filter. */ - float strength; - /* Preserve more or less detail based on feature passes. */ - float feature_strength; - /* When removing pixels that don't carry information, - * use a relative threshold instead of an absolute one. */ - bool relative_pca; - /* How many frames before and after the current center frame are included. */ - int neighbor_frames; - /* Clamp the input to the range of +-1e8. Should be enough for any legitimate data. */ - bool clamp_input; - - /** OIDN/Optix Denoiser. */ - - /* Passes handed over to the OIDN/OptiX denoiser (default to color + albedo). */ - DenoiserInput input_passes; - - DenoiseParams() - { - use = false; - store_passes = false; - - type = DENOISER_NLM; - - radius = 8; - strength = 0.5f; - feature_strength = 0.5f; - relative_pca = false; - neighbor_frames = 2; - clamp_input = true; - - /* Default to color + albedo only, since normal input does not always have the desired effect - * when denoising with OptiX. */ - input_passes = DENOISER_INPUT_RGB_ALBEDO; - - start_sample = 0; - } - - /* Test if a denoising task needs to run, also to prefilter passes for the native - * denoiser when we are not applying denoising to the combined image. */ - bool need_denoising_task() const - { - return (use || (store_passes && type == DENOISER_NLM)); - } -}; - -class AdaptiveSampling { - public: - AdaptiveSampling(); - - int align_samples(int sample, int num_samples) const; - bool need_filter(int sample) const; - - bool use; - int adaptive_step; - int min_samples; -}; - -class DeviceTask { - public: - typedef enum { RENDER, FILM_CONVERT, SHADER, DENOISE_BUFFER } Type; - Type type; - - int x, y, w, h; - device_ptr rgba_byte; - device_ptr rgba_half; - device_ptr buffer; - int sample; - int num_samples; - int offset, stride; - - device_ptr shader_input; - device_ptr shader_output; - int shader_eval_type; - int shader_filter; - int shader_x, shader_w; - - RenderBuffers *buffers; - - explicit DeviceTask(Type type = RENDER); - - int get_subtask_count(int num, int max_size = 0) const; - void split(list &tasks, int num, int max_size = 0) const; - - void update_progress(RenderTile *rtile, int pixel_samples = -1); - - function acquire_tile; - function update_progress_sample; - function update_tile_sample; - function release_tile; - function get_cancel; - function get_tile_stolen; - function map_neighbor_tiles; - function unmap_neighbor_tiles; - - uint tile_types; - DenoiseParams denoising; - bool denoising_from_render; - vector denoising_frames; - - int pass_stride; - int frame_stride; - int target_pass_stride; - int pass_denoising_data; - int pass_denoising_clean; - - bool need_finish_queue; - bool integrator_branched; - AdaptiveSampling adaptive_sampling; - - protected: - double last_update_time; -}; - -CCL_NAMESPACE_END - -#endif /* __DEVICE_TASK_H__ */ diff --git a/intern/cycles/device/device_dummy.cpp b/intern/cycles/device/dummy/device.cpp similarity index 69% rename from intern/cycles/device/device_dummy.cpp rename to intern/cycles/device/dummy/device.cpp index 5112fc152e5..64f6b0eb58c 100644 --- a/intern/cycles/device/device_dummy.cpp +++ b/intern/cycles/device/dummy/device.cpp @@ -14,8 +14,10 @@ * limitations under the License. */ +#include "device/dummy/device.h" + #include "device/device.h" -#include "device/device_intern.h" +#include "device/queue.h" CCL_NAMESPACE_BEGIN @@ -23,8 +25,8 @@ CCL_NAMESPACE_BEGIN class DummyDevice : public Device { public: - DummyDevice(DeviceInfo &info_, Stats &stats_, Profiler &profiler_, bool background_) - : Device(info_, stats_, profiler_, background_) + DummyDevice(const DeviceInfo &info_, Stats &stats_, Profiler &profiler_) + : Device(info_, stats_, profiler_) { error_msg = info.error_msg; } @@ -46,7 +48,7 @@ class DummyDevice : public Device { { } - virtual void mem_copy_from(device_memory &, int, int, int, int) override + virtual void mem_copy_from(device_memory &, size_t, size_t, size_t, size_t) override { } @@ -61,23 +63,11 @@ class DummyDevice : public Device { virtual void const_copy_to(const char *, void *, size_t) override { } - - virtual void task_add(DeviceTask &) override - { - } - - virtual void task_wait() override - { - } - - virtual void task_cancel() override - { - } }; -Device *device_dummy_create(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) +Device *device_dummy_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) { - return new DummyDevice(info, stats, profiler, background); + return new DummyDevice(info, stats, profiler); } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_scene_intersect.cl b/intern/cycles/device/dummy/device.h similarity index 65% rename from intern/cycles/kernel/kernels/opencl/kernel_scene_intersect.cl rename to intern/cycles/device/dummy/device.h index 10d09377ba9..c45eb036ca5 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_scene_intersect.cl +++ b/intern/cycles/device/dummy/device.h @@ -1,5 +1,5 @@ /* - * Copyright 2011-2015 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,18 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_scene_intersect.h" +#pragma once -#define KERNEL_NAME scene_intersect -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +#include "util/string.h" +#include "util/vector.h" +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +Device *device_dummy_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_path_init.cl b/intern/cycles/device/graphics_interop.cpp similarity index 66% rename from intern/cycles/kernel/kernels/opencl/kernel_path_init.cl rename to intern/cycles/device/graphics_interop.cpp index fa210e747c0..0b092711b61 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_path_init.cl +++ b/intern/cycles/device/graphics_interop.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,8 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_path_init.h" +#include "device/graphics_interop.h" -#define KERNEL_NAME path_init -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +CCL_NAMESPACE_BEGIN +CCL_NAMESPACE_END diff --git a/intern/cycles/device/graphics_interop.h b/intern/cycles/device/graphics_interop.h new file mode 100644 index 00000000000..f1661146ddd --- /dev/null +++ b/intern/cycles/device/graphics_interop.h @@ -0,0 +1,42 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "session/display_driver.h" + +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +/* Device-side graphics interoperability support. + * + * Takes care of holding all the handlers needed by the device to implement interoperability with + * the graphics library. */ +class DeviceGraphicsInterop { + public: + DeviceGraphicsInterop() = default; + virtual ~DeviceGraphicsInterop() = default; + + /* Update this device-side graphics interoperability object with the given destination resource + * information. */ + virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) = 0; + + virtual device_ptr map() = 0; + virtual void unmap() = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/hip/device.cpp b/intern/cycles/device/hip/device.cpp new file mode 100644 index 00000000000..41b4f3e41fd --- /dev/null +++ b/intern/cycles/device/hip/device.cpp @@ -0,0 +1,275 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/hip/device.h" + +#include "util/log.h" + +#ifdef WITH_HIP +# include "device/device.h" +# include "device/hip/device_impl.h" + +# include "util/string.h" +# include "util/windows.h" +#endif /* WITH_HIP */ + +CCL_NAMESPACE_BEGIN + +bool device_hip_init() +{ +#if !defined(WITH_HIP) + return false; +#elif defined(WITH_HIP_DYNLOAD) + static bool initialized = false; + static bool result = false; + + if (initialized) + return result; + + initialized = true; + int hipew_result = hipewInit(HIPEW_INIT_HIP); + if (hipew_result == HIPEW_SUCCESS) { + VLOG(1) << "HIPEW initialization succeeded"; + if (HIPDevice::have_precompiled_kernels()) { + VLOG(1) << "Found precompiled kernels"; + result = true; + } + else if (hipewCompilerPath() != NULL) { + VLOG(1) << "Found HIPCC " << hipewCompilerPath(); + result = true; + } + else { + VLOG(1) << "Neither precompiled kernels nor HIPCC was found," + << " unable to use HIP"; + } + } + else { + VLOG(1) << "HIPEW initialization failed: " + << ((hipew_result == HIPEW_ERROR_ATEXIT_FAILED) ? "Error setting up atexit() handler" : + "Error opening the library"); + } + + return result; +#else /* WITH_HIP_DYNLOAD */ + return true; +#endif /* WITH_HIP_DYNLOAD */ +} + +Device *device_hip_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ +#ifdef WITH_HIP + return new HIPDevice(info, stats, profiler); +#else + (void)info; + (void)stats; + (void)profiler; + + LOG(FATAL) << "Request to create HIP device without compiled-in support. Should never happen."; + + return nullptr; +#endif +} + +#ifdef WITH_HIP +static hipError_t device_hip_safe_init() +{ +# ifdef _WIN32 + __try { + return hipInit(0); + } + __except (EXCEPTION_EXECUTE_HANDLER) { + /* Ignore crashes inside the HIP driver and hope we can + * survive even with corrupted HIP installs. */ + fprintf(stderr, "Cycles HIP: driver crashed, continuing without HIP.\n"); + } + + return hipErrorNoDevice; +# else + return hipInit(0); +# endif +} +#endif /* WITH_HIP */ + +void device_hip_info(vector &devices) +{ +#ifdef WITH_HIP + hipError_t result = device_hip_safe_init(); + if (result != hipSuccess) { + if (result != hipErrorNoDevice) + fprintf(stderr, "HIP hipInit: %s\n", hipewErrorString(result)); + return; + } + + int count = 0; + result = hipGetDeviceCount(&count); + if (result != hipSuccess) { + fprintf(stderr, "HIP hipGetDeviceCount: %s\n", hipewErrorString(result)); + return; + } + + vector display_devices; + + for (int num = 0; num < count; num++) { + char name[256]; + + result = hipDeviceGetName(name, 256, num); + if (result != hipSuccess) { + fprintf(stderr, "HIP :hipDeviceGetName: %s\n", hipewErrorString(result)); + continue; + } + + int major; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, num); + // TODO : (Arya) What is the last major version we are supporting? + + DeviceInfo info; + + info.type = DEVICE_HIP; + info.description = string(name); + info.num = num; + + info.has_nanovdb = true; + info.denoisers = 0; + + info.has_gpu_queue = true; + /* Check if the device has P2P access to any other device in the system. */ + for (int peer_num = 0; peer_num < count && !info.has_peer_memory; peer_num++) { + if (num != peer_num) { + int can_access = 0; + hipDeviceCanAccessPeer(&can_access, num, peer_num); + info.has_peer_memory = (can_access != 0); + } + } + + int pci_location[3] = {0, 0, 0}; + hipDeviceGetAttribute(&pci_location[0], hipDeviceAttributePciDomainID, num); + hipDeviceGetAttribute(&pci_location[1], hipDeviceAttributePciBusId, num); + hipDeviceGetAttribute(&pci_location[2], hipDeviceAttributePciDeviceId, num); + info.id = string_printf("HIP_%s_%04x:%02x:%02x", + name, + (unsigned int)pci_location[0], + (unsigned int)pci_location[1], + (unsigned int)pci_location[2]); + + /* If device has a kernel timeout and no compute preemption, we assume + * it is connected to a display and will freeze the display while doing + * computations. */ + int timeout_attr = 0, preempt_attr = 0; + hipDeviceGetAttribute(&timeout_attr, hipDeviceAttributeKernelExecTimeout, num); + + if (timeout_attr && !preempt_attr) { + VLOG(1) << "Device is recognized as display."; + info.description += " (Display)"; + info.display_device = true; + display_devices.push_back(info); + } + else { + VLOG(1) << "Device has compute preemption or is not used for display."; + devices.push_back(info); + } + VLOG(1) << "Added device \"" << name << "\" with id \"" << info.id << "\"."; + } + + if (!display_devices.empty()) + devices.insert(devices.end(), display_devices.begin(), display_devices.end()); +#else /* WITH_HIP */ + (void)devices; +#endif /* WITH_HIP */ +} + +string device_hip_capabilities() +{ +#ifdef WITH_HIP + hipError_t result = device_hip_safe_init(); + if (result != hipSuccess) { + if (result != hipErrorNoDevice) { + return string("Error initializing HIP: ") + hipewErrorString(result); + } + return "No HIP device found\n"; + } + + int count; + result = hipGetDeviceCount(&count); + if (result != hipSuccess) { + return string("Error getting devices: ") + hipewErrorString(result); + } + + string capabilities = ""; + for (int num = 0; num < count; num++) { + char name[256]; + if (hipDeviceGetName(name, 256, num) != hipSuccess) { + continue; + } + capabilities += string("\t") + name + "\n"; + int value; +# define GET_ATTR(attr) \ + { \ + if (hipDeviceGetAttribute(&value, hipDeviceAttribute##attr, num) == hipSuccess) { \ + capabilities += string_printf("\t\thipDeviceAttribute" #attr "\t\t\t%d\n", value); \ + } \ + } \ + (void)0 + /* TODO(sergey): Strip all attributes which are not useful for us + * or does not depend on the driver. + */ + GET_ATTR(MaxThreadsPerBlock); + GET_ATTR(MaxBlockDimX); + GET_ATTR(MaxBlockDimY); + GET_ATTR(MaxBlockDimZ); + GET_ATTR(MaxGridDimX); + GET_ATTR(MaxGridDimY); + GET_ATTR(MaxGridDimZ); + GET_ATTR(MaxSharedMemoryPerBlock); + GET_ATTR(TotalConstantMemory); + GET_ATTR(WarpSize); + GET_ATTR(MaxPitch); + GET_ATTR(MaxRegistersPerBlock); + GET_ATTR(ClockRate); + GET_ATTR(TextureAlignment); + GET_ATTR(MultiprocessorCount); + GET_ATTR(KernelExecTimeout); + GET_ATTR(Integrated); + GET_ATTR(CanMapHostMemory); + GET_ATTR(ComputeMode); + GET_ATTR(MaxTexture1DWidth); + GET_ATTR(MaxTexture2DWidth); + GET_ATTR(MaxTexture2DHeight); + GET_ATTR(MaxTexture3DWidth); + GET_ATTR(MaxTexture3DHeight); + GET_ATTR(MaxTexture3DDepth); + GET_ATTR(ConcurrentKernels); + GET_ATTR(EccEnabled); + GET_ATTR(MemoryClockRate); + GET_ATTR(MemoryBusWidth); + GET_ATTR(L2CacheSize); + GET_ATTR(MaxThreadsPerMultiProcessor); + GET_ATTR(ComputeCapabilityMajor); + GET_ATTR(ComputeCapabilityMinor); + GET_ATTR(MaxSharedMemoryPerMultiprocessor); + GET_ATTR(ManagedMemory); + GET_ATTR(IsMultiGpuBoard); +# undef GET_ATTR + capabilities += "\n"; + } + + return capabilities; + +#else /* WITH_HIP */ + return ""; +#endif /* WITH_HIP */ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/hip/device.h b/intern/cycles/device/hip/device.h new file mode 100644 index 00000000000..cdbe364b2b3 --- /dev/null +++ b/intern/cycles/device/hip/device.h @@ -0,0 +1,37 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/string.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +bool device_hip_init(); + +Device *device_hip_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_hip_info(vector &devices); + +string device_hip_capabilities(); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/hip/device_impl.cpp b/intern/cycles/device/hip/device_impl.cpp new file mode 100644 index 00000000000..1ea387513d5 --- /dev/null +++ b/intern/cycles/device/hip/device_impl.cpp @@ -0,0 +1,1221 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include +# include +# include +# include +# include + +# include "device/hip/device_impl.h" + +# include "util/debug.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/map.h" +# include "util/md5.h" +# include "util/opengl.h" +# include "util/path.h" +# include "util/string.h" +# include "util/system.h" +# include "util/time.h" +# include "util/types.h" +# include "util/windows.h" + +CCL_NAMESPACE_BEGIN + +class HIPDevice; + +bool HIPDevice::have_precompiled_kernels() +{ + string fatbins_path = path_get("lib"); + return path_exists(fatbins_path); +} + +bool HIPDevice::show_samples() const +{ + /* The HIPDevice only processes one tile at a time, so showing samples is fine. */ + return true; +} + +BVHLayoutMask HIPDevice::get_bvh_layout_mask() const +{ + return BVH_LAYOUT_BVH2; +} + +void HIPDevice::set_error(const string &error) +{ + Device::set_error(error); + + if (first_error) { + fprintf(stderr, "\nRefer to the Cycles GPU rendering documentation for possible solutions:\n"); + fprintf(stderr, + "https://docs.blender.org/manual/en/latest/render/cycles/gpu_rendering.html\n\n"); + first_error = false; + } +} + +HIPDevice::HIPDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : Device(info, stats, profiler), texture_info(this, "__texture_info", MEM_GLOBAL) +{ + first_error = true; + + hipDevId = info.num; + hipDevice = 0; + hipContext = 0; + + hipModule = 0; + + need_texture_info = false; + + device_texture_headroom = 0; + device_working_headroom = 0; + move_texture_to_host = false; + map_host_limit = 0; + map_host_used = 0; + can_map_host = 0; + pitch_alignment = 0; + + /* Initialize HIP. */ + hipError_t result = hipInit(0); + if (result != hipSuccess) { + set_error(string_printf("Failed to initialize HIP runtime (%s)", hipewErrorString(result))); + return; + } + + /* Setup device and context. */ + result = hipGetDevice(&hipDevice, hipDevId); + if (result != hipSuccess) { + set_error(string_printf("Failed to get HIP device handle from ordinal (%s)", + hipewErrorString(result))); + return; + } + + /* hipDeviceMapHost for mapping host memory when out of device memory. + * hipDeviceLmemResizeToMax for reserving local memory ahead of render, + * so we can predict which memory to map to host. */ + hip_assert(hipDeviceGetAttribute(&can_map_host, hipDeviceAttributeCanMapHostMemory, hipDevice)); + + hip_assert( + hipDeviceGetAttribute(&pitch_alignment, hipDeviceAttributeTexturePitchAlignment, hipDevice)); + + unsigned int ctx_flags = hipDeviceLmemResizeToMax; + if (can_map_host) { + ctx_flags |= hipDeviceMapHost; + init_host_memory(); + } + + /* Create context. */ + result = hipCtxCreate(&hipContext, ctx_flags, hipDevice); + + if (result != hipSuccess) { + set_error(string_printf("Failed to create HIP context (%s)", hipewErrorString(result))); + return; + } + + int major, minor; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); + hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); + hipDevArchitecture = major * 100 + minor * 10; + + /* Pop context set by hipCtxCreate. */ + hipCtxPopCurrent(NULL); +} + +HIPDevice::~HIPDevice() +{ + texture_info.free(); + + hip_assert(hipCtxDestroy(hipContext)); +} + +bool HIPDevice::support_device(const uint /*kernel_features*/) +{ + int major, minor; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); + hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); + + // TODO : (Arya) What versions do we plan to support? + return true; +} + +bool HIPDevice::check_peer_access(Device *peer_device) +{ + if (peer_device == this) { + return false; + } + if (peer_device->info.type != DEVICE_HIP && peer_device->info.type != DEVICE_OPTIX) { + return false; + } + + HIPDevice *const peer_device_hip = static_cast(peer_device); + + int can_access = 0; + hip_assert(hipDeviceCanAccessPeer(&can_access, hipDevice, peer_device_hip->hipDevice)); + if (can_access == 0) { + return false; + } + + // Ensure array access over the link is possible as well (for 3D textures) + hip_assert(hipDeviceGetP2PAttribute( + &can_access, hipDevP2PAttrHipArrayAccessSupported, hipDevice, peer_device_hip->hipDevice)); + if (can_access == 0) { + return false; + } + + // Enable peer access in both directions + { + const HIPContextScope scope(this); + hipError_t result = hipCtxEnablePeerAccess(peer_device_hip->hipContext, 0); + if (result != hipSuccess) { + set_error(string_printf("Failed to enable peer access on HIP context (%s)", + hipewErrorString(result))); + return false; + } + } + { + const HIPContextScope scope(peer_device_hip); + hipError_t result = hipCtxEnablePeerAccess(hipContext, 0); + if (result != hipSuccess) { + set_error(string_printf("Failed to enable peer access on HIP context (%s)", + hipewErrorString(result))); + return false; + } + } + + return true; +} + +bool HIPDevice::use_adaptive_compilation() +{ + return DebugFlags().hip.adaptive_compile; +} + +/* Common HIPCC flags which stays the same regardless of shading model, + * kernel sources md5 and only depends on compiler or compilation settings. + */ +string HIPDevice::compile_kernel_get_common_cflags(const uint kernel_features) +{ + const int machine = system_cpu_bits(); + const string source_path = path_get("source"); + const string include_path = source_path; + string cflags = string_printf( + "-m%d " + "--ptxas-options=\"-v\" " + "--use_fast_math " + "-DHIPCC " + "-I\"%s\"", + machine, + include_path.c_str()); + if (use_adaptive_compilation()) { + cflags += " -D__KERNEL_FEATURES__=" + to_string(kernel_features); + } + return cflags; +} + +string HIPDevice::compile_kernel(const uint kernel_features, + const char *name, + const char *base, + bool force_ptx) +{ + /* Compute kernel name. */ + int major, minor; + hipDeviceGetAttribute(&major, hipDeviceAttributeComputeCapabilityMajor, hipDevId); + hipDeviceGetAttribute(&minor, hipDeviceAttributeComputeCapabilityMinor, hipDevId); + hipDeviceProp_t props; + hipGetDeviceProperties(&props, hipDevId); + + /* Attempt to use kernel provided with Blender. */ + if (!use_adaptive_compilation()) { + if (!force_ptx) { + const string fatbin = path_get(string_printf("lib/%s_%s.fatbin", name, props.gcnArchName)); + VLOG(1) << "Testing for pre-compiled kernel " << fatbin << "."; + if (path_exists(fatbin)) { + VLOG(1) << "Using precompiled kernel."; + return fatbin; + } + } + + /* The driver can JIT-compile PTX generated for older generations, so find the closest one. */ + int ptx_major = major, ptx_minor = minor; + while (ptx_major >= 3) { + const string ptx = path_get( + string_printf("lib/%s_compute_%d%d.ptx", name, ptx_major, ptx_minor)); + VLOG(1) << "Testing for pre-compiled kernel " << ptx << "."; + if (path_exists(ptx)) { + VLOG(1) << "Using precompiled kernel."; + return ptx; + } + + if (ptx_minor > 0) { + ptx_minor--; + } + else { + ptx_major--; + ptx_minor = 9; + } + } + } + + /* Try to use locally compiled kernel. */ + string source_path = path_get("source"); + const string source_md5 = path_files_md5_hash(source_path); + + /* We include cflags into md5 so changing hip toolkit or changing other + * compiler command line arguments makes sure fatbin gets re-built. + */ + string common_cflags = compile_kernel_get_common_cflags(kernel_features); + const string kernel_md5 = util_md5_string(source_md5 + common_cflags); + + const char *const kernel_ext = "genco"; + std::string options; +# ifdef _WIN32 + options.append("Wno-parentheses-equality -Wno-unused-value --hipcc-func-supp -ffast-math"); +# else + options.append("Wno-parentheses-equality -Wno-unused-value --hipcc-func-supp -O3 -ffast-math"); +# endif +# ifdef _DEBUG + options.append(" -save-temps"); +# endif + options.append(" --amdgpu-target=").append(props.gcnArchName); + + const string include_path = source_path; + const char *const kernel_arch = props.gcnArchName; + const string fatbin_file = string_printf( + "cycles_%s_%s_%s", name, kernel_arch, kernel_md5.c_str()); + const string fatbin = path_cache_get(path_join("kernels", fatbin_file)); + VLOG(1) << "Testing for locally compiled kernel " << fatbin << "."; + if (path_exists(fatbin)) { + VLOG(1) << "Using locally compiled kernel."; + return fatbin; + } + +# ifdef _WIN32 + if (!use_adaptive_compilation() && have_precompiled_kernels()) { + if (major < 3) { + set_error( + string_printf("HIP backend requires compute capability 3.0 or up, but found %d.%d. " + "Your GPU is not supported.", + major, + minor)); + } + else { + set_error( + string_printf("HIP binary kernel for this graphics card compute " + "capability (%d.%d) not found.", + major, + minor)); + } + return string(); + } +# endif + + /* Compile. */ + const char *const hipcc = hipewCompilerPath(); + if (hipcc == NULL) { + set_error( + "HIP hipcc compiler not found. " + "Install HIP toolkit in default location."); + return string(); + } + + const int hipcc_hip_version = hipewCompilerVersion(); + VLOG(1) << "Found hipcc " << hipcc << ", HIP version " << hipcc_hip_version << "."; + if (hipcc_hip_version < 40) { + printf( + "Unsupported HIP version %d.%d detected, " + "you need HIP 4.0 or newer.\n", + hipcc_hip_version / 10, + hipcc_hip_version % 10); + return string(); + } + + double starttime = time_dt(); + + path_create_directories(fatbin); + + source_path = path_join(path_join(source_path, "kernel"), + path_join("device", path_join(base, string_printf("%s.cpp", name)))); + + string command = string_printf("%s -%s -I %s --%s %s -o \"%s\"", + hipcc, + options.c_str(), + include_path.c_str(), + kernel_ext, + source_path.c_str(), + fatbin.c_str()); + + printf("Compiling %sHIP kernel ...\n%s\n", + (use_adaptive_compilation()) ? "adaptive " : "", + command.c_str()); + +# ifdef _WIN32 + command = "call " + command; +# endif + if (system(command.c_str()) != 0) { + set_error( + "Failed to execute compilation command, " + "see console for details."); + return string(); + } + + /* Verify if compilation succeeded */ + if (!path_exists(fatbin)) { + set_error( + "HIP kernel compilation failed, " + "see console for details."); + return string(); + } + + printf("Kernel compilation finished in %.2lfs.\n", time_dt() - starttime); + + return fatbin; +} + +bool HIPDevice::load_kernels(const uint kernel_features) +{ + /* TODO(sergey): Support kernels re-load for CUDA devices adaptive compile. + * + * Currently re-loading kernel will invalidate memory pointers, + * causing problems in cuCtxSynchronize. + */ + if (hipModule) { + if (use_adaptive_compilation()) { + VLOG(1) << "Skipping HIP kernel reload for adaptive compilation, not currently supported."; + } + return true; + } + + /* check if hip init succeeded */ + if (hipContext == 0) + return false; + + /* check if GPU is supported */ + if (!support_device(kernel_features)) + return false; + + /* get kernel */ + const char *kernel_name = "kernel"; + string fatbin = compile_kernel(kernel_features, kernel_name); + if (fatbin.empty()) + return false; + + /* open module */ + HIPContextScope scope(this); + + string fatbin_data; + hipError_t result; + + if (path_read_text(fatbin, fatbin_data)) + result = hipModuleLoadData(&hipModule, fatbin_data.c_str()); + else + result = hipErrorFileNotFound; + + if (result != hipSuccess) + set_error(string_printf( + "Failed to load HIP kernel from '%s' (%s)", fatbin.c_str(), hipewErrorString(result))); + + if (result == hipSuccess) { + kernels.load(this); + reserve_local_memory(kernel_features); + } + + return (result == hipSuccess); +} + +void HIPDevice::reserve_local_memory(const uint kernel_features) +{ + /* Together with hipDeviceLmemResizeToMax, this reserves local memory + * needed for kernel launches, so that we can reliably figure out when + * to allocate scene data in mapped host memory. */ + size_t total = 0, free_before = 0, free_after = 0; + + { + HIPContextScope scope(this); + hipMemGetInfo(&free_before, &total); + } + + { + /* Use the biggest kernel for estimation. */ + const DeviceKernel test_kernel = (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE : + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE; + + /* Launch kernel, using just 1 block appears sufficient to reserve memory for all + * multiprocessors. It would be good to do this in parallel for the multi GPU case + * still to make it faster. */ + HIPDeviceQueue queue(this); + + void *d_path_index = nullptr; + void *d_render_buffer = nullptr; + int d_work_size = 0; + void *args[] = {&d_path_index, &d_render_buffer, &d_work_size}; + + queue.init_execution(); + queue.enqueue(test_kernel, 1, args); + queue.synchronize(); + } + + { + HIPContextScope scope(this); + hipMemGetInfo(&free_after, &total); + } + + VLOG(1) << "Local memory reserved " << string_human_readable_number(free_before - free_after) + << " bytes. (" << string_human_readable_size(free_before - free_after) << ")"; + +# if 0 + /* For testing mapped host memory, fill up device memory. */ + const size_t keep_mb = 1024; + + while (free_after > keep_mb * 1024 * 1024LL) { + hipDeviceptr_t tmp; + hip_assert(hipMalloc(&tmp, 10 * 1024 * 1024LL)); + hipMemGetInfo(&free_after, &total); + } +# endif +} + +void HIPDevice::init_host_memory() +{ + /* Limit amount of host mapped memory, because allocating too much can + * cause system instability. Leave at least half or 4 GB of system + * memory free, whichever is smaller. */ + size_t default_limit = 4 * 1024 * 1024 * 1024LL; + size_t system_ram = system_physical_ram(); + + if (system_ram > 0) { + if (system_ram / 2 > default_limit) { + map_host_limit = system_ram - default_limit; + } + else { + map_host_limit = system_ram / 2; + } + } + else { + VLOG(1) << "Mapped host memory disabled, failed to get system RAM"; + map_host_limit = 0; + } + + /* Amount of device memory to keep is free after texture memory + * and working memory allocations respectively. We set the working + * memory limit headroom lower so that some space is left after all + * texture memory allocations. */ + device_working_headroom = 32 * 1024 * 1024LL; // 32MB + device_texture_headroom = 128 * 1024 * 1024LL; // 128MB + + VLOG(1) << "Mapped host memory limit set to " << string_human_readable_number(map_host_limit) + << " bytes. (" << string_human_readable_size(map_host_limit) << ")"; +} + +void HIPDevice::load_texture_info() +{ + if (need_texture_info) { + /* Unset flag before copying, so this does not loop indefinitely if the copy below calls + * into 'move_textures_to_host' (which calls 'load_texture_info' again). */ + need_texture_info = false; + texture_info.copy_to_device(); + } +} + +void HIPDevice::move_textures_to_host(size_t size, bool for_texture) +{ + /* Break out of recursive call, which can happen when moving memory on a multi device. */ + static bool any_device_moving_textures_to_host = false; + if (any_device_moving_textures_to_host) { + return; + } + + /* Signal to reallocate textures in host memory only. */ + move_texture_to_host = true; + + while (size > 0) { + /* Find suitable memory allocation to move. */ + device_memory *max_mem = NULL; + size_t max_size = 0; + bool max_is_image = false; + + thread_scoped_lock lock(hip_mem_map_mutex); + foreach (HIPMemMap::value_type &pair, hip_mem_map) { + device_memory &mem = *pair.first; + HIPMem *cmem = &pair.second; + + /* Can only move textures allocated on this device (and not those from peer devices). + * And need to ignore memory that is already on the host. */ + if (!mem.is_resident(this) || cmem->use_mapped_host) { + continue; + } + + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && + (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + /* Can't move this type of memory. */ + if (!is_texture || cmem->array) { + continue; + } + + /* For other textures, only move image textures. */ + if (for_texture && !is_image) { + continue; + } + + /* Try to move largest allocation, prefer moving images. */ + if (is_image > max_is_image || (is_image == max_is_image && mem.device_size > max_size)) { + max_is_image = is_image; + max_size = mem.device_size; + max_mem = &mem; + } + } + lock.unlock(); + + /* Move to host memory. This part is mutex protected since + * multiple HIP devices could be moving the memory. The + * first one will do it, and the rest will adopt the pointer. */ + if (max_mem) { + VLOG(1) << "Move memory from device to host: " << max_mem->name; + + static thread_mutex move_mutex; + thread_scoped_lock lock(move_mutex); + + any_device_moving_textures_to_host = true; + + /* Potentially need to call back into multi device, so pointer mapping + * and peer devices are updated. This is also necessary since the device + * pointer may just be a key here, so cannot be accessed and freed directly. + * Unfortunately it does mean that memory is reallocated on all other + * devices as well, which is potentially dangerous when still in use (since + * a thread rendering on another devices would only be caught in this mutex + * if it so happens to do an allocation at the same time as well. */ + max_mem->device_copy_to(); + size = (max_size >= size) ? 0 : size - max_size; + + any_device_moving_textures_to_host = false; + } + else { + break; + } + } + + /* Unset flag before texture info is reloaded, since it should stay in device memory. */ + move_texture_to_host = false; + + /* Update texture info array with new pointers. */ + load_texture_info(); +} + +HIPDevice::HIPMem *HIPDevice::generic_alloc(device_memory &mem, size_t pitch_padding) +{ + HIPContextScope scope(this); + + hipDeviceptr_t device_pointer = 0; + size_t size = mem.memory_size() + pitch_padding; + + hipError_t mem_alloc_result = hipErrorOutOfMemory; + const char *status = ""; + + /* First try allocating in device memory, respecting headroom. We make + * an exception for texture info. It is small and frequently accessed, + * so treat it as working memory. + * + * If there is not enough room for working memory, we will try to move + * textures to host memory, assuming the performance impact would have + * been worse for working memory. */ + bool is_texture = (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) && (&mem != &texture_info); + bool is_image = is_texture && (mem.data_height > 1); + + size_t headroom = (is_texture) ? device_texture_headroom : device_working_headroom; + + size_t total = 0, free = 0; + hipMemGetInfo(&free, &total); + + /* Move textures to host memory if needed. */ + if (!move_texture_to_host && !is_image && (size + headroom) >= free && can_map_host) { + move_textures_to_host(size + headroom - free, is_texture); + hipMemGetInfo(&free, &total); + } + + /* Allocate in device memory. */ + if (!move_texture_to_host && (size + headroom) < free) { + mem_alloc_result = hipMalloc(&device_pointer, size); + if (mem_alloc_result == hipSuccess) { + status = " in device memory"; + } + } + + /* Fall back to mapped host memory if needed and possible. */ + + void *shared_pointer = 0; + + if (mem_alloc_result != hipSuccess && can_map_host) { + if (mem.shared_pointer) { + /* Another device already allocated host memory. */ + mem_alloc_result = hipSuccess; + shared_pointer = mem.shared_pointer; + } + else if (map_host_used + size < map_host_limit) { + /* Allocate host memory ourselves. */ + mem_alloc_result = hipHostMalloc( + &shared_pointer, size, hipHostMallocMapped | hipHostMallocWriteCombined); + + assert((mem_alloc_result == hipSuccess && shared_pointer != 0) || + (mem_alloc_result != hipSuccess && shared_pointer == 0)); + } + + if (mem_alloc_result == hipSuccess) { + hip_assert(hipHostGetDevicePointer(&device_pointer, shared_pointer, 0)); + map_host_used += size; + status = " in host memory"; + } + } + + if (mem_alloc_result != hipSuccess) { + status = " failed, out of device and host memory"; + set_error("System is out of GPU and shared host memory"); + } + + if (mem.name) { + VLOG(1) << "Buffer allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")" << status; + } + + mem.device_pointer = (device_ptr)device_pointer; + mem.device_size = size; + stats.mem_alloc(size); + + if (!mem.device_pointer) { + return NULL; + } + + /* Insert into map of allocations. */ + thread_scoped_lock lock(hip_mem_map_mutex); + HIPMem *cmem = &hip_mem_map[&mem]; + if (shared_pointer != 0) { + /* Replace host pointer with our host allocation. Only works if + * HIP memory layout is the same and has no pitch padding. Also + * does not work if we move textures to host during a render, + * since other devices might be using the memory. */ + + if (!move_texture_to_host && pitch_padding == 0 && mem.host_pointer && + mem.host_pointer != shared_pointer) { + memcpy(shared_pointer, mem.host_pointer, size); + + /* A Call to device_memory::host_free() should be preceded by + * a call to device_memory::device_free() for host memory + * allocated by a device to be handled properly. Two exceptions + * are here and a call in OptiXDevice::generic_alloc(), where + * the current host memory can be assumed to be allocated by + * device_memory::host_alloc(), not by a device */ + + mem.host_free(); + mem.host_pointer = shared_pointer; + } + mem.shared_pointer = shared_pointer; + mem.shared_counter++; + cmem->use_mapped_host = true; + } + else { + cmem->use_mapped_host = false; + } + + return cmem; +} + +void HIPDevice::generic_copy_to(device_memory &mem) +{ + if (!mem.host_pointer || !mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, the current device only uses device memory allocated by + * hipMalloc regardless of mem.host_pointer and mem.shared_pointer, and should copy data from + * mem.host_pointer. */ + thread_scoped_lock lock(hip_mem_map_mutex); + if (!hip_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const HIPContextScope scope(this); + hip_assert( + hipMemcpyHtoD((hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size())); + } +} + +void HIPDevice::generic_free(device_memory &mem) +{ + if (mem.device_pointer) { + HIPContextScope scope(this); + thread_scoped_lock lock(hip_mem_map_mutex); + const HIPMem &cmem = hip_mem_map[&mem]; + + /* If cmem.use_mapped_host is true, reference counting is used + * to safely free a mapped host memory. */ + + if (cmem.use_mapped_host) { + assert(mem.shared_pointer); + if (mem.shared_pointer) { + assert(mem.shared_counter > 0); + if (--mem.shared_counter == 0) { + if (mem.host_pointer == mem.shared_pointer) { + mem.host_pointer = 0; + } + hipHostFree(mem.shared_pointer); + mem.shared_pointer = 0; + } + } + map_host_used -= mem.device_size; + } + else { + /* Free device memory. */ + hip_assert(hipFree(mem.device_pointer)); + } + + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + hip_mem_map.erase(hip_mem_map.find(&mem)); + } +} + +void HIPDevice::mem_alloc(device_memory &mem) +{ + if (mem.type == MEM_TEXTURE) { + assert(!"mem_alloc not supported for textures."); + } + else if (mem.type == MEM_GLOBAL) { + assert(!"mem_alloc not supported for global memory."); + } + else { + generic_alloc(mem); + } +} + +void HIPDevice::mem_copy_to(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + global_alloc(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + tex_alloc((device_texture &)mem); + } + else { + if (!mem.device_pointer) { + generic_alloc(mem); + } + generic_copy_to(mem); + } +} + +void HIPDevice::mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) +{ + if (mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) { + assert(!"mem_copy_from not supported for textures."); + } + else if (mem.host_pointer) { + const size_t size = elem * w * h; + const size_t offset = elem * y * w; + + if (mem.device_pointer) { + const HIPContextScope scope(this); + hip_assert(hipMemcpyDtoH( + (char *)mem.host_pointer + offset, (hipDeviceptr_t)mem.device_pointer + offset, size)); + } + else { + memset((char *)mem.host_pointer + offset, 0, size); + } + } +} + +void HIPDevice::mem_zero(device_memory &mem) +{ + if (!mem.device_pointer) { + mem_alloc(mem); + } + if (!mem.device_pointer) { + return; + } + + /* If use_mapped_host of mem is false, mem.device_pointer currently refers to device memory + * regardless of mem.host_pointer and mem.shared_pointer. */ + thread_scoped_lock lock(hip_mem_map_mutex); + if (!hip_mem_map[&mem].use_mapped_host || mem.host_pointer != mem.shared_pointer) { + const HIPContextScope scope(this); + hip_assert(hipMemsetD8((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size())); + } + else if (mem.host_pointer) { + memset(mem.host_pointer, 0, mem.memory_size()); + } +} + +void HIPDevice::mem_free(device_memory &mem) +{ + if (mem.type == MEM_GLOBAL) { + global_free(mem); + } + else if (mem.type == MEM_TEXTURE) { + tex_free((device_texture &)mem); + } + else { + generic_free(mem); + } +} + +device_ptr HIPDevice::mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) +{ + return (device_ptr)(((char *)mem.device_pointer) + mem.memory_elements_size(offset)); +} + +void HIPDevice::const_copy_to(const char *name, void *host, size_t size) +{ + HIPContextScope scope(this); + hipDeviceptr_t mem; + size_t bytes; + + hip_assert(hipModuleGetGlobal(&mem, &bytes, hipModule, name)); + hip_assert(hipMemcpyHtoD(mem, host, size)); +} + +void HIPDevice::global_alloc(device_memory &mem) +{ + if (mem.is_resident(this)) { + generic_alloc(mem); + generic_copy_to(mem); + } + + const_copy_to(mem.name, &mem.device_pointer, sizeof(mem.device_pointer)); +} + +void HIPDevice::global_free(device_memory &mem) +{ + if (mem.is_resident(this) && mem.device_pointer) { + generic_free(mem); + } +} + +void HIPDevice::tex_alloc(device_texture &mem) +{ + HIPContextScope scope(this); + + /* General variables for both architectures */ + string bind_name = mem.name; + size_t dsize = datatype_size(mem.data_type); + size_t size = mem.memory_size(); + + hipTextureAddressMode address_mode = hipAddressModeWrap; + switch (mem.info.extension) { + case EXTENSION_REPEAT: + address_mode = hipAddressModeWrap; + break; + case EXTENSION_EXTEND: + address_mode = hipAddressModeClamp; + break; + case EXTENSION_CLIP: + // TODO : (Arya) setting this to Mode Clamp instead of Mode Border because it's unsupported + // in hip + address_mode = hipAddressModeClamp; + break; + default: + assert(0); + break; + } + + hipTextureFilterMode filter_mode; + if (mem.info.interpolation == INTERPOLATION_CLOSEST) { + filter_mode = hipFilterModePoint; + } + else { + filter_mode = hipFilterModeLinear; + } + + /* Image Texture Storage */ + hipArray_Format format; + switch (mem.data_type) { + case TYPE_UCHAR: + format = HIP_AD_FORMAT_UNSIGNED_INT8; + break; + case TYPE_UINT16: + format = HIP_AD_FORMAT_UNSIGNED_INT16; + break; + case TYPE_UINT: + format = HIP_AD_FORMAT_UNSIGNED_INT32; + break; + case TYPE_INT: + format = HIP_AD_FORMAT_SIGNED_INT32; + break; + case TYPE_FLOAT: + format = HIP_AD_FORMAT_FLOAT; + break; + case TYPE_HALF: + format = HIP_AD_FORMAT_HALF; + break; + default: + assert(0); + return; + } + + HIPMem *cmem = NULL; + hArray array_3d = NULL; + size_t src_pitch = mem.data_width * dsize * mem.data_elements; + size_t dst_pitch = src_pitch; + + if (!mem.is_resident(this)) { + thread_scoped_lock lock(hip_mem_map_mutex); + cmem = &hip_mem_map[&mem]; + cmem->texobject = 0; + + if (mem.data_depth > 1) { + array_3d = (hArray)mem.device_pointer; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + dst_pitch = align_up(src_pitch, pitch_alignment); + } + } + else if (mem.data_depth > 1) { + /* 3D texture using array, there is no API for linear memory. */ + HIP_ARRAY3D_DESCRIPTOR desc; + + desc.Width = mem.data_width; + desc.Height = mem.data_height; + desc.Depth = mem.data_depth; + desc.Format = format; + desc.NumChannels = mem.data_elements; + desc.Flags = 0; + + VLOG(1) << "Array 3D allocate: " << mem.name << ", " + << string_human_readable_number(mem.memory_size()) << " bytes. (" + << string_human_readable_size(mem.memory_size()) << ")"; + + hip_assert(hipArray3DCreate(&array_3d, &desc)); + + if (!array_3d) { + return; + } + + HIP_MEMCPY3D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = hipMemoryTypeArray; + param.dstArray = &array_3d; + param.srcMemoryType = hipMemoryTypeHost; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + param.Depth = mem.data_depth; + + hip_assert(hipDrvMemcpy3D(¶m)); + + mem.device_pointer = (device_ptr)array_3d; + mem.device_size = size; + stats.mem_alloc(size); + + thread_scoped_lock lock(hip_mem_map_mutex); + cmem = &hip_mem_map[&mem]; + cmem->texobject = 0; + cmem->array = array_3d; + } + else if (mem.data_height > 0) { + /* 2D texture, using pitch aligned linear memory. */ + dst_pitch = align_up(src_pitch, pitch_alignment); + size_t dst_size = dst_pitch * mem.data_height; + + cmem = generic_alloc(mem, dst_size - mem.memory_size()); + if (!cmem) { + return; + } + + hip_Memcpy2D param; + memset(¶m, 0, sizeof(param)); + param.dstMemoryType = hipMemoryTypeDevice; + param.dstDevice = mem.device_pointer; + param.dstPitch = dst_pitch; + param.srcMemoryType = hipMemoryTypeHost; + param.srcHost = mem.host_pointer; + param.srcPitch = src_pitch; + param.WidthInBytes = param.srcPitch; + param.Height = mem.data_height; + + hip_assert(hipDrvMemcpy2DUnaligned(¶m)); + } + else { + /* 1D texture, using linear memory. */ + cmem = generic_alloc(mem); + if (!cmem) { + return; + } + + hip_assert(hipMemcpyHtoD(mem.device_pointer, mem.host_pointer, size)); + } + + /* Resize once */ + const uint slot = mem.slot; + if (slot >= texture_info.size()) { + /* Allocate some slots in advance, to reduce amount + * of re-allocations. */ + texture_info.resize(slot + 128); + } + + /* Set Mapping and tag that we need to (re-)upload to device */ + texture_info[slot] = mem.info; + need_texture_info = true; + + if (mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && + mem.info.data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { + /* Kepler+, bindless textures. */ + hipResourceDesc resDesc; + memset(&resDesc, 0, sizeof(resDesc)); + + if (array_3d) { + resDesc.resType = hipResourceTypeArray; + resDesc.res.array.h_Array = &array_3d; + resDesc.flags = 0; + } + else if (mem.data_height > 0) { + resDesc.resType = hipResourceTypePitch2D; + resDesc.res.pitch2D.devPtr = mem.device_pointer; + resDesc.res.pitch2D.format = format; + resDesc.res.pitch2D.numChannels = mem.data_elements; + resDesc.res.pitch2D.height = mem.data_height; + resDesc.res.pitch2D.width = mem.data_width; + resDesc.res.pitch2D.pitchInBytes = dst_pitch; + } + else { + resDesc.resType = hipResourceTypeLinear; + resDesc.res.linear.devPtr = mem.device_pointer; + resDesc.res.linear.format = format; + resDesc.res.linear.numChannels = mem.data_elements; + resDesc.res.linear.sizeInBytes = mem.device_size; + } + + hipTextureDesc texDesc; + memset(&texDesc, 0, sizeof(texDesc)); + texDesc.addressMode[0] = address_mode; + texDesc.addressMode[1] = address_mode; + texDesc.addressMode[2] = address_mode; + texDesc.filterMode = filter_mode; + texDesc.flags = HIP_TRSF_NORMALIZED_COORDINATES; + + thread_scoped_lock lock(hip_mem_map_mutex); + cmem = &hip_mem_map[&mem]; + + hip_assert(hipTexObjectCreate(&cmem->texobject, &resDesc, &texDesc, NULL)); + + texture_info[slot].data = (uint64_t)cmem->texobject; + } + else { + texture_info[slot].data = (uint64_t)mem.device_pointer; + } +} + +void HIPDevice::tex_free(device_texture &mem) +{ + if (mem.device_pointer) { + HIPContextScope scope(this); + thread_scoped_lock lock(hip_mem_map_mutex); + const HIPMem &cmem = hip_mem_map[&mem]; + + if (cmem.texobject) { + /* Free bindless texture. */ + hipTexObjectDestroy(cmem.texobject); + } + + if (!mem.is_resident(this)) { + /* Do not free memory here, since it was allocated on a different device. */ + hip_mem_map.erase(hip_mem_map.find(&mem)); + } + else if (cmem.array) { + /* Free array. */ + hipArrayDestroy(cmem.array); + stats.mem_free(mem.device_size); + mem.device_pointer = 0; + mem.device_size = 0; + + hip_mem_map.erase(hip_mem_map.find(&mem)); + } + else { + lock.unlock(); + generic_free(mem); + } + } +} + +unique_ptr HIPDevice::gpu_queue_create() +{ + return make_unique(this); +} + +bool HIPDevice::should_use_graphics_interop() +{ + /* Check whether this device is part of OpenGL context. + * + * Using HIP device for graphics interoperability which is not part of the OpenGL context is + * possible, but from the empiric measurements it can be considerably slower than using naive + * pixels copy. */ + + HIPContextScope scope(this); + + int num_all_devices = 0; + hip_assert(hipGetDeviceCount(&num_all_devices)); + + if (num_all_devices == 0) { + return false; + } + + vector gl_devices(num_all_devices); + uint num_gl_devices = 0; + hipGLGetDevices(&num_gl_devices, gl_devices.data(), num_all_devices, hipGLDeviceListAll); + + for (hipDevice_t gl_device : gl_devices) { + if (gl_device == hipDevice) { + return true; + } + } + + return false; +} + +int HIPDevice::get_num_multiprocessors() +{ + return get_device_default_attribute(hipDeviceAttributeMultiprocessorCount, 0); +} + +int HIPDevice::get_max_num_threads_per_multiprocessor() +{ + return get_device_default_attribute(hipDeviceAttributeMaxThreadsPerMultiProcessor, 0); +} + +bool HIPDevice::get_device_attribute(hipDeviceAttribute_t attribute, int *value) +{ + HIPContextScope scope(this); + + return hipDeviceGetAttribute(value, attribute, hipDevice) == hipSuccess; +} + +int HIPDevice::get_device_default_attribute(hipDeviceAttribute_t attribute, int default_value) +{ + int value = 0; + if (!get_device_attribute(attribute, &value)) { + return default_value; + } + return value; +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/device_impl.h b/intern/cycles/device/hip/device_impl.h new file mode 100644 index 00000000000..8d81291d15e --- /dev/null +++ b/intern/cycles/device/hip/device_impl.h @@ -0,0 +1,153 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/device.h" +# include "device/hip/kernel.h" +# include "device/hip/queue.h" +# include "device/hip/util.h" + +# include "util/map.h" + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# else +# include "util/opengl.h" +# endif + +CCL_NAMESPACE_BEGIN + +class DeviceQueue; + +class HIPDevice : public Device { + + friend class HIPContextScope; + + public: + hipDevice_t hipDevice; + hipCtx_t hipContext; + hipModule_t hipModule; + size_t device_texture_headroom; + size_t device_working_headroom; + bool move_texture_to_host; + size_t map_host_used; + size_t map_host_limit; + int can_map_host; + int pitch_alignment; + int hipDevId; + int hipDevArchitecture; + bool first_error; + + struct HIPMem { + HIPMem() : texobject(0), array(0), use_mapped_host(false) + { + } + + hipTextureObject_t texobject; + hArray array; + + /* If true, a mapped host memory in shared_pointer is being used. */ + bool use_mapped_host; + }; + typedef map HIPMemMap; + HIPMemMap hip_mem_map; + thread_mutex hip_mem_map_mutex; + + /* Bindless Textures */ + device_vector texture_info; + bool need_texture_info; + + HIPDeviceKernels kernels; + + static bool have_precompiled_kernels(); + + virtual bool show_samples() const override; + + virtual BVHLayoutMask get_bvh_layout_mask() const override; + + void set_error(const string &error) override; + + HIPDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler); + + virtual ~HIPDevice(); + + bool support_device(const uint /*kernel_features*/); + + bool check_peer_access(Device *peer_device) override; + + bool use_adaptive_compilation(); + + virtual string compile_kernel_get_common_cflags(const uint kernel_features); + + string compile_kernel(const uint kernel_features, + const char *name, + const char *base = "hip", + bool force_ptx = false); + + virtual bool load_kernels(const uint kernel_features) override; + void reserve_local_memory(const uint kernel_features); + + void init_host_memory(); + + void load_texture_info(); + + void move_textures_to_host(size_t size, bool for_texture); + + HIPMem *generic_alloc(device_memory &mem, size_t pitch_padding = 0); + + void generic_copy_to(device_memory &mem); + + void generic_free(device_memory &mem); + + void mem_alloc(device_memory &mem) override; + + void mem_copy_to(device_memory &mem) override; + + void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override; + + void mem_zero(device_memory &mem) override; + + void mem_free(device_memory &mem) override; + + device_ptr mem_alloc_sub_ptr(device_memory &mem, size_t offset, size_t /*size*/) override; + + virtual void const_copy_to(const char *name, void *host, size_t size) override; + + void global_alloc(device_memory &mem); + + void global_free(device_memory &mem); + + void tex_alloc(device_texture &mem); + + void tex_free(device_texture &mem); + + /* Graphics resources interoperability. */ + virtual bool should_use_graphics_interop() override; + + virtual unique_ptr gpu_queue_create() override; + + int get_num_multiprocessors(); + int get_max_num_threads_per_multiprocessor(); + + protected: + bool get_device_attribute(hipDeviceAttribute_t attribute, int *value); + int get_device_default_attribute(hipDeviceAttribute_t attribute, int default_value); +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/graphics_interop.cpp b/intern/cycles/device/hip/graphics_interop.cpp new file mode 100644 index 00000000000..0d5d71019b3 --- /dev/null +++ b/intern/cycles/device/hip/graphics_interop.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/graphics_interop.h" + +# include "device/hip/device_impl.h" +# include "device/hip/util.h" + +CCL_NAMESPACE_BEGIN + +HIPDeviceGraphicsInterop::HIPDeviceGraphicsInterop(HIPDeviceQueue *queue) + : queue_(queue), device_(static_cast(queue->device)) +{ +} + +HIPDeviceGraphicsInterop::~HIPDeviceGraphicsInterop() +{ + HIPContextScope scope(device_); + + if (hip_graphics_resource_) { + hip_device_assert(device_, hipGraphicsUnregisterResource(hip_graphics_resource_)); + } +} + +void HIPDeviceGraphicsInterop::set_display_interop( + const DisplayDriver::GraphicsInterop &display_interop) +{ + const int64_t new_buffer_area = int64_t(display_interop.buffer_width) * + display_interop.buffer_height; + + need_clear_ = display_interop.need_clear; + + if (opengl_pbo_id_ == display_interop.opengl_pbo_id && buffer_area_ == new_buffer_area) { + return; + } + + HIPContextScope scope(device_); + + if (hip_graphics_resource_) { + hip_device_assert(device_, hipGraphicsUnregisterResource(hip_graphics_resource_)); + } + + const hipError_t result = hipGraphicsGLRegisterBuffer( + &hip_graphics_resource_, display_interop.opengl_pbo_id, hipGraphicsRegisterFlagsNone); + if (result != hipSuccess) { + LOG(ERROR) << "Error registering OpenGL buffer: " << hipewErrorString(result); + } + + opengl_pbo_id_ = display_interop.opengl_pbo_id; + buffer_area_ = new_buffer_area; +} + +device_ptr HIPDeviceGraphicsInterop::map() +{ + if (!hip_graphics_resource_) { + return 0; + } + + HIPContextScope scope(device_); + + hipDeviceptr_t hip_buffer; + size_t bytes; + + hip_device_assert(device_, + hipGraphicsMapResources(1, &hip_graphics_resource_, queue_->stream())); + hip_device_assert( + device_, hipGraphicsResourceGetMappedPointer(&hip_buffer, &bytes, hip_graphics_resource_)); + + if (need_clear_) { + hip_device_assert( + device_, + hipMemsetD8Async(static_cast(hip_buffer), 0, bytes, queue_->stream())); + + need_clear_ = false; + } + + return static_cast(hip_buffer); +} + +void HIPDeviceGraphicsInterop::unmap() +{ + HIPContextScope scope(device_); + + hip_device_assert(device_, + hipGraphicsUnmapResources(1, &hip_graphics_resource_, queue_->stream())); +} + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/graphics_interop.h b/intern/cycles/device/hip/graphics_interop.h new file mode 100644 index 00000000000..8314405e670 --- /dev/null +++ b/intern/cycles/device/hip/graphics_interop.h @@ -0,0 +1,64 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/graphics_interop.h" + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# endif + +CCL_NAMESPACE_BEGIN + +class HIPDevice; +class HIPDeviceQueue; + +class HIPDeviceGraphicsInterop : public DeviceGraphicsInterop { + public: + explicit HIPDeviceGraphicsInterop(HIPDeviceQueue *queue); + + HIPDeviceGraphicsInterop(const HIPDeviceGraphicsInterop &other) = delete; + HIPDeviceGraphicsInterop(HIPDeviceGraphicsInterop &&other) noexcept = delete; + + ~HIPDeviceGraphicsInterop(); + + HIPDeviceGraphicsInterop &operator=(const HIPDeviceGraphicsInterop &other) = delete; + HIPDeviceGraphicsInterop &operator=(HIPDeviceGraphicsInterop &&other) = delete; + + virtual void set_display_interop(const DisplayDriver::GraphicsInterop &display_interop) override; + + virtual device_ptr map() override; + virtual void unmap() override; + + protected: + HIPDeviceQueue *queue_ = nullptr; + HIPDevice *device_ = nullptr; + + /* OpenGL PBO which is currently registered as the destination for the CUDA buffer. */ + uint opengl_pbo_id_ = 0; + /* Buffer area in pixels of the corresponding PBO. */ + int64_t buffer_area_ = 0; + + /* The destination was requested to be cleared. */ + bool need_clear_ = false; + + hipGraphicsResource hip_graphics_resource_ = nullptr; +}; + +CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/device/hip/kernel.cpp b/intern/cycles/device/hip/kernel.cpp new file mode 100644 index 00000000000..9ede8507a0c --- /dev/null +++ b/intern/cycles/device/hip/kernel.cpp @@ -0,0 +1,69 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/kernel.h" +# include "device/hip/device_impl.h" + +CCL_NAMESPACE_BEGIN + +void HIPDeviceKernels::load(HIPDevice *device) +{ + hipModule_t hipModule = device->hipModule; + + for (int i = 0; i < (int)DEVICE_KERNEL_NUM; i++) { + HIPDeviceKernel &kernel = kernels_[i]; + + /* No mega-kernel used for GPU. */ + if (i == DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL) { + continue; + } + + const std::string function_name = std::string("kernel_gpu_") + + device_kernel_as_string((DeviceKernel)i); + hip_device_assert(device, + hipModuleGetFunction(&kernel.function, hipModule, function_name.c_str())); + + if (kernel.function) { + hip_device_assert(device, hipFuncSetCacheConfig(kernel.function, hipFuncCachePreferL1)); + + hip_device_assert( + device, + hipModuleOccupancyMaxPotentialBlockSize( + &kernel.min_blocks, &kernel.num_threads_per_block, kernel.function, 0, 0)); + } + else { + LOG(ERROR) << "Unable to load kernel " << function_name; + } + } + + loaded = true; +} + +const HIPDeviceKernel &HIPDeviceKernels::get(DeviceKernel kernel) const +{ + return kernels_[(int)kernel]; +} + +bool HIPDeviceKernels::available(DeviceKernel kernel) const +{ + return kernels_[(int)kernel].function != nullptr; +} + +CCL_NAMESPACE_END + +#endif /* WITH_HIP*/ diff --git a/intern/cycles/device/hip/kernel.h b/intern/cycles/device/hip/kernel.h new file mode 100644 index 00000000000..f1378f8eebf --- /dev/null +++ b/intern/cycles/device/hip/kernel.h @@ -0,0 +1,54 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_HIP + +# include "device/kernel.h" + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# endif + +CCL_NAMESPACE_BEGIN + +class HIPDevice; + +/* HIP kernel and associate occupancy information. */ +class HIPDeviceKernel { + public: + hipFunction_t function = nullptr; + + int num_threads_per_block = 0; + int min_blocks = 0; +}; + +/* Cache of HIP kernels for each DeviceKernel. */ +class HIPDeviceKernels { + public: + void load(HIPDevice *device); + const HIPDeviceKernel &get(DeviceKernel kernel) const; + bool available(DeviceKernel kernel) const; + + protected: + HIPDeviceKernel kernels_[DEVICE_KERNEL_NUM]; + bool loaded = false; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/queue.cpp b/intern/cycles/device/hip/queue.cpp new file mode 100644 index 00000000000..0f053ccbeb5 --- /dev/null +++ b/intern/cycles/device/hip/queue.cpp @@ -0,0 +1,236 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/queue.h" + +# include "device/hip/device_impl.h" +# include "device/hip/graphics_interop.h" +# include "device/hip/kernel.h" + +CCL_NAMESPACE_BEGIN + +/* HIPDeviceQueue */ + +HIPDeviceQueue::HIPDeviceQueue(HIPDevice *device) + : DeviceQueue(device), hip_device_(device), hip_stream_(nullptr) +{ + const HIPContextScope scope(hip_device_); + hip_device_assert(hip_device_, hipStreamCreateWithFlags(&hip_stream_, hipStreamNonBlocking)); +} + +HIPDeviceQueue::~HIPDeviceQueue() +{ + const HIPContextScope scope(hip_device_); + hipStreamDestroy(hip_stream_); +} + +int HIPDeviceQueue::num_concurrent_states(const size_t state_size) const +{ + const int max_num_threads = hip_device_->get_num_multiprocessors() * + hip_device_->get_max_num_threads_per_multiprocessor(); + int num_states = ((max_num_threads == 0) ? 65536 : max_num_threads) * 16; + + const char *factor_str = getenv("CYCLES_CONCURRENT_STATES_FACTOR"); + if (factor_str) { + const float factor = (float)atof(factor_str); + if (factor != 0.0f) { + num_states = max((int)(num_states * factor), 1024); + } + else { + VLOG(3) << "CYCLES_CONCURRENT_STATES_FACTOR evaluated to 0"; + } + } + + VLOG(3) << "GPU queue concurrent states: " << num_states << ", using up to " + << string_human_readable_size(num_states * state_size); + + return num_states; +} + +int HIPDeviceQueue::num_concurrent_busy_states() const +{ + const int max_num_threads = hip_device_->get_num_multiprocessors() * + hip_device_->get_max_num_threads_per_multiprocessor(); + + if (max_num_threads == 0) { + return 65536; + } + + return 4 * max_num_threads; +} + +void HIPDeviceQueue::init_execution() +{ + /* Synchronize all textures and memory copies before executing task. */ + HIPContextScope scope(hip_device_); + hip_device_->load_texture_info(); + hip_device_assert(hip_device_, hipDeviceSynchronize()); + + debug_init_execution(); +} + +bool HIPDeviceQueue::kernel_available(DeviceKernel kernel) const +{ + return hip_device_->kernels.available(kernel); +} + +bool HIPDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *args[]) +{ + if (hip_device_->have_error()) { + return false; + } + + debug_enqueue(kernel, work_size); + + const HIPContextScope scope(hip_device_); + const HIPDeviceKernel &hip_kernel = hip_device_->kernels.get(kernel); + + /* Compute kernel launch parameters. */ + const int num_threads_per_block = hip_kernel.num_threads_per_block; + const int num_blocks = divide_up(work_size, num_threads_per_block); + + int shared_mem_bytes = 0; + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY: + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: + /* See parall_active_index.h for why this amount of shared memory is needed. */ + shared_mem_bytes = (num_threads_per_block + 1) * sizeof(int); + break; + default: + break; + } + + /* Launch kernel. */ + assert_success(hipModuleLaunchKernel(hip_kernel.function, + num_blocks, + 1, + 1, + num_threads_per_block, + 1, + 1, + shared_mem_bytes, + hip_stream_, + args, + 0), + "enqueue"); + + return !(hip_device_->have_error()); +} + +bool HIPDeviceQueue::synchronize() +{ + if (hip_device_->have_error()) { + return false; + } + + const HIPContextScope scope(hip_device_); + assert_success(hipStreamSynchronize(hip_stream_), "synchronize"); + debug_synchronize(); + + return !(hip_device_->have_error()); +} + +void HIPDeviceQueue::zero_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + hip_device_->mem_alloc(mem); + } + + /* Zero memory on device. */ + assert(mem.device_pointer != 0); + + const HIPContextScope scope(hip_device_); + assert_success( + hipMemsetD8Async((hipDeviceptr_t)mem.device_pointer, 0, mem.memory_size(), hip_stream_), + "zero_to_device"); +} + +void HIPDeviceQueue::copy_to_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + /* Allocate on demand. */ + if (mem.device_pointer == 0) { + hip_device_->mem_alloc(mem); + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory to device. */ + const HIPContextScope scope(hip_device_); + assert_success( + hipMemcpyHtoDAsync( + (hipDeviceptr_t)mem.device_pointer, mem.host_pointer, mem.memory_size(), hip_stream_), + "copy_to_device"); +} + +void HIPDeviceQueue::copy_from_device(device_memory &mem) +{ + assert(mem.type != MEM_GLOBAL && mem.type != MEM_TEXTURE); + + if (mem.memory_size() == 0) { + return; + } + + assert(mem.device_pointer != 0); + assert(mem.host_pointer != nullptr); + + /* Copy memory from device. */ + const HIPContextScope scope(hip_device_); + assert_success( + hipMemcpyDtoHAsync( + mem.host_pointer, (hipDeviceptr_t)mem.device_pointer, mem.memory_size(), hip_stream_), + "copy_from_device"); +} + +void HIPDeviceQueue::assert_success(hipError_t result, const char *operation) +{ + if (result != hipSuccess) { + const char *name = hipewErrorString(result); + hip_device_->set_error( + string_printf("%s in HIP queue %s (%s)", name, operation, debug_active_kernels().c_str())); + } +} + +unique_ptr HIPDeviceQueue::graphics_interop_create() +{ + return make_unique(this); +} + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/queue.h b/intern/cycles/device/hip/queue.h new file mode 100644 index 00000000000..95d1afaff0f --- /dev/null +++ b/intern/cycles/device/hip/queue.h @@ -0,0 +1,69 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_HIP + +# include "device/kernel.h" +# include "device/memory.h" +# include "device/queue.h" + +# include "device/hip/util.h" + +CCL_NAMESPACE_BEGIN + +class HIPDevice; +class device_memory; + +/* Base class for HIP queues. */ +class HIPDeviceQueue : public DeviceQueue { + public: + HIPDeviceQueue(HIPDevice *device); + ~HIPDeviceQueue(); + + virtual int num_concurrent_states(const size_t state_size) const override; + virtual int num_concurrent_busy_states() const override; + + virtual void init_execution() override; + + virtual bool kernel_available(DeviceKernel kernel) const override; + + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) override; + + virtual bool synchronize() override; + + virtual void zero_to_device(device_memory &mem) override; + virtual void copy_to_device(device_memory &mem) override; + virtual void copy_from_device(device_memory &mem) override; + + virtual hipStream_t stream() + { + return hip_stream_; + } + + virtual unique_ptr graphics_interop_create() override; + + protected: + HIPDevice *hip_device_; + hipStream_t hip_stream_; + + void assert_success(hipError_t result, const char *operation); +}; + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/util.cpp b/intern/cycles/device/hip/util.cpp new file mode 100644 index 00000000000..44f52c4e17b --- /dev/null +++ b/intern/cycles/device/hip/util.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_HIP + +# include "device/hip/util.h" +# include "device/hip/device_impl.h" + +CCL_NAMESPACE_BEGIN + +HIPContextScope::HIPContextScope(HIPDevice *device) : device(device) +{ + hip_device_assert(device, hipCtxPushCurrent(device->hipContext)); +} + +HIPContextScope::~HIPContextScope() +{ + hip_device_assert(device, hipCtxPopCurrent(NULL)); +} + +# ifndef WITH_HIP_DYNLOAD +const char *hipewErrorString(hipError_t result) +{ + /* We can only give error code here without major code duplication, that + * should be enough since dynamic loading is only being disabled by folks + * who knows what they're doing anyway. + * + * NOTE: Avoid call from several threads. + */ + static string error; + error = string_printf("%d", result); + return error.c_str(); +} + +const char *hipewCompilerPath() +{ + return CYCLES_HIP_HIPCC_EXECUTABLE; +} + +int hipewCompilerVersion() +{ + return (HIP_VERSION / 100) + (HIP_VERSION % 100 / 10); +} +# endif + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/hip/util.h b/intern/cycles/device/hip/util.h new file mode 100644 index 00000000000..0db5174a3db --- /dev/null +++ b/intern/cycles/device/hip/util.h @@ -0,0 +1,63 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_HIP + +# ifdef WITH_HIP_DYNLOAD +# include "hipew.h" +# endif + +CCL_NAMESPACE_BEGIN + +class HIPDevice; + +/* Utility to push/pop HIP context. */ +class HIPContextScope { + public: + HIPContextScope(HIPDevice *device); + ~HIPContextScope(); + + private: + HIPDevice *device; +}; + +/* Utility for checking return values of HIP function calls. */ +# define hip_device_assert(hip_device, stmt) \ + { \ + hipError_t result = stmt; \ + if (result != hipSuccess) { \ + const char *name = hipewErrorString(result); \ + hip_device->set_error( \ + string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \ + } \ + } \ + (void)0 + +# define hip_assert(stmt) hip_device_assert(this, stmt) + +# ifndef WITH_HIP_DYNLOAD +/* Transparently implement some functions, so majority of the file does not need + * to worry about difference between dynamically loaded and linked HIP at all. */ +const char *hipewErrorString(hipError_t result); +const char *hipewCompilerPath(); +int hipewCompilerVersion(); +# endif /* WITH_HIP_DYNLOAD */ + +CCL_NAMESPACE_END + +#endif /* WITH_HIP */ diff --git a/intern/cycles/device/kernel.cpp b/intern/cycles/device/kernel.cpp new file mode 100644 index 00000000000..1e4f0c48f18 --- /dev/null +++ b/intern/cycles/device/kernel.cpp @@ -0,0 +1,165 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/kernel.h" + +#include "util/log.h" + +CCL_NAMESPACE_BEGIN + +const char *device_kernel_as_string(DeviceKernel kernel) +{ + switch (kernel) { + /* Integrator. */ + case DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA: + return "integrator_init_from_camera"; + case DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE: + return "integrator_init_from_bake"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + return "integrator_intersect_closest"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + return "integrator_intersect_shadow"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + return "integrator_intersect_subsurface"; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: + return "integrator_intersect_volume_stack"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: + return "integrator_shade_background"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT: + return "integrator_shade_light"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + return "integrator_shade_shadow"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: + return "integrator_shade_surface"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + return "integrator_shade_surface_raytrace"; + case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: + return "integrator_shade_volume"; + case DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL: + return "integrator_megakernel"; + case DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY: + return "integrator_queued_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY: + return "integrator_queued_shadow_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY: + return "integrator_active_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY: + return "integrator_terminated_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY: + return "integrator_sorted_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY: + return "integrator_compact_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES: + return "integrator_compact_states"; + case DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY: + return "integrator_terminated_shadow_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY: + return "integrator_compact_shadow_paths_array"; + case DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES: + return "integrator_compact_shadow_states"; + case DEVICE_KERNEL_INTEGRATOR_RESET: + return "integrator_reset"; + case DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS: + return "integrator_shadow_catcher_count_possible_splits"; + + /* Shader evaluation. */ + case DEVICE_KERNEL_SHADER_EVAL_DISPLACE: + return "shader_eval_displace"; + case DEVICE_KERNEL_SHADER_EVAL_BACKGROUND: + return "shader_eval_background"; + case DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY: + return "shader_eval_curve_shadow_transparency"; + + /* Film. */ + +#define FILM_CONVERT_KERNEL_AS_STRING(variant, variant_lowercase) \ + case DEVICE_KERNEL_FILM_CONVERT_##variant: \ + return "film_convert_" #variant_lowercase; \ + case DEVICE_KERNEL_FILM_CONVERT_##variant##_HALF_RGBA: \ + return "film_convert_" #variant_lowercase "_half_rgba"; + + FILM_CONVERT_KERNEL_AS_STRING(DEPTH, depth) + FILM_CONVERT_KERNEL_AS_STRING(MIST, mist) + FILM_CONVERT_KERNEL_AS_STRING(SAMPLE_COUNT, sample_count) + FILM_CONVERT_KERNEL_AS_STRING(FLOAT, float) + FILM_CONVERT_KERNEL_AS_STRING(LIGHT_PATH, light_path) + FILM_CONVERT_KERNEL_AS_STRING(FLOAT3, float3) + FILM_CONVERT_KERNEL_AS_STRING(MOTION, motion) + FILM_CONVERT_KERNEL_AS_STRING(CRYPTOMATTE, cryptomatte) + FILM_CONVERT_KERNEL_AS_STRING(SHADOW_CATCHER, shadow_catcher) + FILM_CONVERT_KERNEL_AS_STRING(SHADOW_CATCHER_MATTE_WITH_SHADOW, + shadow_catcher_matte_with_shadow) + FILM_CONVERT_KERNEL_AS_STRING(COMBINED, combined) + FILM_CONVERT_KERNEL_AS_STRING(FLOAT4, float4) + +#undef FILM_CONVERT_KERNEL_AS_STRING + + /* Adaptive sampling. */ + case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_CHECK: + return "adaptive_sampling_convergence_check"; + case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_X: + return "adaptive_sampling_filter_x"; + case DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y: + return "adaptive_sampling_filter_y"; + + /* Denoising. */ + case DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS: + return "filter_guiding_preprocess"; + case DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO: + return "filter_guiding_set_fake_albedo"; + case DEVICE_KERNEL_FILTER_COLOR_PREPROCESS: + return "filter_color_preprocess"; + case DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS: + return "filter_color_postprocess"; + + /* Cryptomatte. */ + case DEVICE_KERNEL_CRYPTOMATTE_POSTPROCESS: + return "cryptomatte_postprocess"; + + /* Generic */ + case DEVICE_KERNEL_PREFIX_SUM: + return "prefix_sum"; + + case DEVICE_KERNEL_NUM: + break; + }; + LOG(FATAL) << "Unhandled kernel " << static_cast(kernel) << ", should never happen."; + return "UNKNOWN"; +} + +std::ostream &operator<<(std::ostream &os, DeviceKernel kernel) +{ + os << device_kernel_as_string(kernel); + return os; +} + +string device_kernel_mask_as_string(DeviceKernelMask mask) +{ + string str; + + for (uint64_t i = 0; i < sizeof(DeviceKernelMask) * 8; i++) { + if (mask & (uint64_t(1) << i)) { + if (!str.empty()) { + str += " "; + } + str += device_kernel_as_string((DeviceKernel)i); + } + } + + return str; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/kernel.h b/intern/cycles/device/kernel.h new file mode 100644 index 00000000000..780ead2d28a --- /dev/null +++ b/intern/cycles/device/kernel.h @@ -0,0 +1,33 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/types.h" + +#include "util/string.h" + +#include // NOLINT + +CCL_NAMESPACE_BEGIN + +const char *device_kernel_as_string(DeviceKernel kernel); +std::ostream &operator<<(std::ostream &os, DeviceKernel kernel); + +typedef uint64_t DeviceKernelMask; +string device_kernel_mask_as_string(DeviceKernelMask mask); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/device_memory.cpp b/intern/cycles/device/memory.cpp similarity index 94% rename from intern/cycles/device/device_memory.cpp rename to intern/cycles/device/memory.cpp index 80a05fc32fe..f162b00d9f7 100644 --- a/intern/cycles/device/device_memory.cpp +++ b/intern/cycles/device/memory.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "device/device_memory.h" +#include "device/memory.h" #include "device/device.h" CCL_NAMESPACE_BEGIN @@ -23,7 +23,7 @@ CCL_NAMESPACE_BEGIN device_memory::device_memory(Device *device, const char *name, MemoryType type) : data_type(device_type_traits::data_type), - data_elements(device_type_traits::num_elements), + data_elements(device_type_traits::num_elements_cpu), data_size(0), device_size(0), data_width(0), @@ -136,7 +136,7 @@ void device_memory::device_copy_to() } } -void device_memory::device_copy_from(int y, int w, int h, int elem) +void device_memory::device_copy_from(size_t y, size_t w, size_t h, size_t elem) { assert(type != MEM_TEXTURE && type != MEM_READ_ONLY && type != MEM_GLOBAL); device->mem_copy_from(*this, y, w, h, elem); @@ -149,6 +149,11 @@ void device_memory::device_zero() } } +bool device_memory::device_is_cpu() +{ + return (device->info.type == DEVICE_CPU); +} + void device_memory::swap_device(Device *new_device, size_t new_device_size, device_ptr new_device_ptr) @@ -176,7 +181,7 @@ bool device_memory::is_resident(Device *sub_device) const /* Device Sub Ptr */ -device_sub_ptr::device_sub_ptr(device_memory &mem, int offset, int size) : device(mem.device) +device_sub_ptr::device_sub_ptr(device_memory &mem, size_t offset, size_t size) : device(mem.device) { ptr = device->mem_alloc_sub_ptr(mem, offset, size); } diff --git a/intern/cycles/device/device_memory.h b/intern/cycles/device/memory.h similarity index 72% rename from intern/cycles/device/device_memory.h rename to intern/cycles/device/memory.h index 80f4d7b0468..281c54cc6a5 100644 --- a/intern/cycles/device/device_memory.h +++ b/intern/cycles/device/memory.h @@ -21,12 +21,12 @@ * * Data types for allocating, copying and freeing device memory. */ -#include "util/util_array.h" -#include "util/util_half.h" -#include "util/util_string.h" -#include "util/util_texture.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/half.h" +#include "util/string.h" +#include "util/texture.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -38,7 +38,6 @@ enum MemoryType { MEM_DEVICE_ONLY, MEM_GLOBAL, MEM_TEXTURE, - MEM_PIXELS }; /* Supported Data Types */ @@ -54,7 +53,7 @@ enum DataType { TYPE_UINT64, }; -static inline size_t datatype_size(DataType datatype) +static constexpr size_t datatype_size(DataType datatype) { switch (datatype) { case TYPE_UNKNOWN: @@ -82,112 +81,155 @@ static inline size_t datatype_size(DataType datatype) template struct device_type_traits { static const DataType data_type = TYPE_UNKNOWN; - static const int num_elements = sizeof(T); + static const size_t num_elements_cpu = sizeof(T); + static const size_t num_elements_gpu = sizeof(T); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; + static_assert(sizeof(uchar) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; + static_assert(sizeof(uchar2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 3; + static const size_t num_elements_cpu = 3; + static const size_t num_elements_gpu = 3; + static_assert(sizeof(uchar3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UCHAR; - static const int num_elements = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; + static_assert(sizeof(uchar4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; + static_assert(sizeof(uint) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; + static_assert(sizeof(uint2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 3; + static const size_t num_elements_cpu = 3; + static const size_t num_elements_gpu = 3; + static_assert(sizeof(uint3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT; - static const int num_elements = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; + static_assert(sizeof(uint4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; + static_assert(sizeof(int) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; + static_assert(sizeof(int2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 3; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 3; + static_assert(sizeof(int3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_INT; - static const int num_elements = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; + static_assert(sizeof(int4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; + static_assert(sizeof(float) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 2; + static const size_t num_elements_cpu = 2; + static const size_t num_elements_gpu = 2; + static_assert(sizeof(float2) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 3; + static_assert(sizeof(float3) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_FLOAT; - static const int num_elements = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; + static_assert(sizeof(float4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_HALF; - static const int num_elements = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; + static_assert(sizeof(half) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT16; - static const int num_elements = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; + static_assert(sizeof(ushort4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT16; - static const int num_elements = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; + static_assert(sizeof(uint16_t) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_HALF; - static const int num_elements = 4; + static const size_t num_elements_cpu = 4; + static const size_t num_elements_gpu = 4; + static_assert(sizeof(half4) == num_elements_cpu * datatype_size(data_type)); }; template<> struct device_type_traits { static const DataType data_type = TYPE_UINT64; - static const int num_elements = 1; + static const size_t num_elements_cpu = 1; + static const size_t num_elements_gpu = 1; + static_assert(sizeof(uint64_t) == num_elements_cpu * datatype_size(data_type)); }; /* Device Memory @@ -235,6 +277,7 @@ class device_memory { protected: friend class CUDADevice; friend class OptiXDevice; + friend class HIPDevice; /* Only create through subclasses. */ device_memory(Device *device, const char *name, MemoryType type); @@ -254,9 +297,11 @@ class device_memory { void device_alloc(); void device_free(); void device_copy_to(); - void device_copy_from(int y, int w, int h, int elem); + void device_copy_from(size_t y, size_t w, size_t h, size_t elem); void device_zero(); + bool device_is_cpu(); + device_ptr original_device_ptr; size_t original_device_size; Device *original_device; @@ -275,7 +320,9 @@ template class device_only_memory : public device_memory { : device_memory(device, name, allow_host_memory_fallback ? MEM_READ_WRITE : MEM_DEVICE_ONLY) { data_type = device_type_traits::data_type; - data_elements = max(device_type_traits::num_elements, 1); + data_elements = max(device_is_cpu() ? device_type_traits::num_elements_cpu : + device_type_traits::num_elements_gpu, + 1); } device_only_memory(device_only_memory &&other) noexcept : device_memory(std::move(other)) @@ -331,11 +378,15 @@ template class device_only_memory : public device_memory { template class device_vector : public device_memory { public: + /* Can only use this for types that have the same size on CPU and GPU. */ + static_assert(device_type_traits::num_elements_cpu == + device_type_traits::num_elements_gpu); + device_vector(Device *device, const char *name, MemoryType type) : device_memory(device, name, type) { data_type = device_type_traits::data_type; - data_elements = device_type_traits::num_elements; + data_elements = device_type_traits::num_elements_cpu; modified = true; need_realloc_ = true; @@ -477,6 +528,11 @@ template class device_vector : public device_memory { return (T *)host_pointer; } + const T *data() const + { + return (T *)host_pointer; + } + T &operator[](size_t i) { assert(i < data_size); @@ -507,10 +563,10 @@ template class device_vector : public device_memory { void copy_from_device() { - device_copy_from(0, data_width, data_height, sizeof(T)); + device_copy_from(0, data_width, (data_height == 0) ? 1 : data_height, sizeof(T)); } - void copy_from_device(int y, int w, int h) + void copy_from_device(size_t y, size_t w, size_t h) { device_copy_from(y, w, h, sizeof(T)); } @@ -535,33 +591,6 @@ template class device_vector : public device_memory { } }; -/* Pixel Memory - * - * Device memory to efficiently draw as pixels to the screen in interactive - * rendering. Only copying pixels from the device is supported, not copying to. */ - -template class device_pixels : public device_vector { - public: - device_pixels(Device *device, const char *name) : device_vector(device, name, MEM_PIXELS) - { - } - - void alloc_to_device(size_t width, size_t height, size_t depth = 0) - { - device_vector::alloc(width, height, depth); - - if (!device_memory::device_pointer) { - device_memory::device_alloc(); - } - } - - T *copy_from_device(int y, int w, int h) - { - device_memory::device_copy_from(y, w, h, sizeof(T)); - return device_vector::data(); - } -}; - /* Device Sub Memory * * Pointer into existing memory. It is not allocated separately, but created @@ -573,7 +602,7 @@ template class device_pixels : public device_vector { class device_sub_ptr { public: - device_sub_ptr(device_memory &mem, int offset, int size); + device_sub_ptr(device_memory &mem, size_t offset, size_t size); ~device_sub_ptr(); device_ptr operator*() const diff --git a/intern/cycles/device/multi/device.cpp b/intern/cycles/device/multi/device.cpp new file mode 100644 index 00000000000..56efec3e131 --- /dev/null +++ b/intern/cycles/device/multi/device.cpp @@ -0,0 +1,422 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/multi/device.h" + +#include +#include + +#include "bvh/multi.h" + +#include "device/device.h" +#include "device/queue.h" + +#include "scene/geometry.h" + +#include "util/foreach.h" +#include "util/list.h" +#include "util/log.h" +#include "util/map.h" +#include "util/time.h" + +CCL_NAMESPACE_BEGIN + +class MultiDevice : public Device { + public: + struct SubDevice { + Stats stats; + Device *device; + map ptr_map; + int peer_island_index = -1; + }; + + list devices; + device_ptr unique_key; + vector> peer_islands; + + MultiDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : Device(info, stats, profiler), unique_key(1) + { + foreach (const DeviceInfo &subinfo, info.multi_devices) { + /* Always add CPU devices at the back since GPU devices can change + * host memory pointers, which CPU uses as device pointer. */ + SubDevice *sub; + if (subinfo.type == DEVICE_CPU) { + devices.emplace_back(); + sub = &devices.back(); + } + else { + devices.emplace_front(); + sub = &devices.front(); + } + + /* The pointer to 'sub->stats' will stay valid even after new devices + * are added, since 'devices' is a linked list. */ + sub->device = Device::create(subinfo, sub->stats, profiler); + } + + /* Build a list of peer islands for the available render devices */ + foreach (SubDevice &sub, devices) { + /* First ensure that every device is in at least once peer island */ + if (sub.peer_island_index < 0) { + peer_islands.emplace_back(); + sub.peer_island_index = (int)peer_islands.size() - 1; + peer_islands[sub.peer_island_index].push_back(&sub); + } + + if (!info.has_peer_memory) { + continue; + } + + /* Second check peer access between devices and fill up the islands accordingly */ + foreach (SubDevice &peer_sub, devices) { + if (peer_sub.peer_island_index < 0 && + peer_sub.device->info.type == sub.device->info.type && + peer_sub.device->check_peer_access(sub.device)) { + peer_sub.peer_island_index = sub.peer_island_index; + peer_islands[sub.peer_island_index].push_back(&peer_sub); + } + } + } + } + + ~MultiDevice() + { + foreach (SubDevice &sub, devices) + delete sub.device; + } + + const string &error_message() override + { + error_msg.clear(); + + foreach (SubDevice &sub, devices) + error_msg += sub.device->error_message(); + + return error_msg; + } + + virtual bool show_samples() const override + { + if (devices.size() > 1) { + return false; + } + return devices.front().device->show_samples(); + } + + virtual BVHLayoutMask get_bvh_layout_mask() const override + { + BVHLayoutMask bvh_layout_mask = BVH_LAYOUT_ALL; + BVHLayoutMask bvh_layout_mask_all = BVH_LAYOUT_NONE; + foreach (const SubDevice &sub_device, devices) { + BVHLayoutMask device_bvh_layout_mask = sub_device.device->get_bvh_layout_mask(); + bvh_layout_mask &= device_bvh_layout_mask; + bvh_layout_mask_all |= device_bvh_layout_mask; + } + + /* With multiple OptiX devices, every device needs its own acceleration structure */ + if (bvh_layout_mask == BVH_LAYOUT_OPTIX) { + return BVH_LAYOUT_MULTI_OPTIX; + } + + /* When devices do not share a common BVH layout, fall back to creating one for each */ + const BVHLayoutMask BVH_LAYOUT_OPTIX_EMBREE = (BVH_LAYOUT_OPTIX | BVH_LAYOUT_EMBREE); + if ((bvh_layout_mask_all & BVH_LAYOUT_OPTIX_EMBREE) == BVH_LAYOUT_OPTIX_EMBREE) { + return BVH_LAYOUT_MULTI_OPTIX_EMBREE; + } + + return bvh_layout_mask; + } + + bool load_kernels(const uint kernel_features) override + { + foreach (SubDevice &sub, devices) + if (!sub.device->load_kernels(kernel_features)) + return false; + + return true; + } + + void build_bvh(BVH *bvh, Progress &progress, bool refit) override + { + /* Try to build and share a single acceleration structure, if possible */ + if (bvh->params.bvh_layout == BVH_LAYOUT_BVH2 || bvh->params.bvh_layout == BVH_LAYOUT_EMBREE) { + devices.back().device->build_bvh(bvh, progress, refit); + return; + } + + assert(bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX || + bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE); + + BVHMulti *const bvh_multi = static_cast(bvh); + bvh_multi->sub_bvhs.resize(devices.size()); + + vector geom_bvhs; + geom_bvhs.reserve(bvh->geometry.size()); + foreach (Geometry *geom, bvh->geometry) { + geom_bvhs.push_back(static_cast(geom->bvh)); + } + + /* Broadcast acceleration structure build to all render devices */ + size_t i = 0; + foreach (SubDevice &sub, devices) { + /* Change geometry BVH pointers to the sub BVH */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]->sub_bvhs[i]; + } + + if (!bvh_multi->sub_bvhs[i]) { + BVHParams params = bvh->params; + if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX) + params.bvh_layout = BVH_LAYOUT_OPTIX; + else if (bvh->params.bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE) + params.bvh_layout = sub.device->info.type == DEVICE_OPTIX ? BVH_LAYOUT_OPTIX : + BVH_LAYOUT_EMBREE; + + /* Skip building a bottom level acceleration structure for non-instanced geometry on Embree + * (since they are put into the top level directly, see bvh_embree.cpp) */ + if (!params.top_level && params.bvh_layout == BVH_LAYOUT_EMBREE && + !bvh->geometry[0]->is_instanced()) { + i++; + continue; + } + + bvh_multi->sub_bvhs[i] = BVH::create(params, bvh->geometry, bvh->objects, sub.device); + } + + sub.device->build_bvh(bvh_multi->sub_bvhs[i], progress, refit); + i++; + } + + /* Change geometry BVH pointers back to the multi BVH. */ + for (size_t k = 0; k < bvh->geometry.size(); ++k) { + bvh->geometry[k]->bvh = geom_bvhs[k]; + } + } + + virtual void *get_cpu_osl_memory() override + { + if (devices.size() > 1) { + return NULL; + } + return devices.front().device->get_cpu_osl_memory(); + } + + bool is_resident(device_ptr key, Device *sub_device) override + { + foreach (SubDevice &sub, devices) { + if (sub.device == sub_device) { + return find_matching_mem_device(key, sub)->device == sub_device; + } + } + return false; + } + + SubDevice *find_matching_mem_device(device_ptr key, SubDevice &sub) + { + assert(key != 0 && (sub.peer_island_index >= 0 || sub.ptr_map.find(key) != sub.ptr_map.end())); + + /* Get the memory owner of this key (first try current device, then peer devices) */ + SubDevice *owner_sub = ⊂ + if (owner_sub->ptr_map.find(key) == owner_sub->ptr_map.end()) { + foreach (SubDevice *island_sub, peer_islands[sub.peer_island_index]) { + if (island_sub != owner_sub && + island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) { + owner_sub = island_sub; + } + } + } + return owner_sub; + } + + SubDevice *find_suitable_mem_device(device_ptr key, const vector &island) + { + assert(!island.empty()); + + /* Get the memory owner of this key or the device with the lowest memory usage when new */ + SubDevice *owner_sub = island.front(); + foreach (SubDevice *island_sub, island) { + if (key ? (island_sub->ptr_map.find(key) != island_sub->ptr_map.end()) : + (island_sub->device->stats.mem_used < owner_sub->device->stats.mem_used)) { + owner_sub = island_sub; + } + } + return owner_sub; + } + + inline device_ptr find_matching_mem(device_ptr key, SubDevice &sub) + { + return find_matching_mem_device(key, sub)->ptr_map[key]; + } + + void mem_alloc(device_memory &mem) override + { + device_ptr key = unique_key++; + + assert(mem.type == MEM_READ_ONLY || mem.type == MEM_READ_WRITE || mem.type == MEM_DEVICE_ONLY); + /* The remaining memory types can be distributed across devices */ + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_suitable_mem_device(key, island); + mem.device = owner_sub->device; + mem.device_pointer = 0; + mem.device_size = 0; + + owner_sub->device->mem_alloc(mem); + owner_sub->ptr_map[key] = mem.device_pointer; + } + + mem.device = this; + mem.device_pointer = key; + stats.mem_alloc(mem.device_size); + } + + void mem_copy_to(device_memory &mem) override + { + device_ptr existing_key = mem.device_pointer; + device_ptr key = (existing_key) ? existing_key : unique_key++; + size_t existing_size = mem.device_size; + + /* The tile buffers are allocated on each device (see below), so copy to all of them */ + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); + mem.device = owner_sub->device; + mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; + mem.device_size = existing_size; + + owner_sub->device->mem_copy_to(mem); + owner_sub->ptr_map[key] = mem.device_pointer; + + if (mem.type == MEM_GLOBAL || mem.type == MEM_TEXTURE) { + /* Need to create texture objects and update pointer in kernel globals on all devices */ + foreach (SubDevice *island_sub, island) { + if (island_sub != owner_sub) { + island_sub->device->mem_copy_to(mem); + } + } + } + } + + mem.device = this; + mem.device_pointer = key; + stats.mem_alloc(mem.device_size - existing_size); + } + + void mem_copy_from(device_memory &mem, size_t y, size_t w, size_t h, size_t elem) override + { + device_ptr key = mem.device_pointer; + size_t i = 0, sub_h = h / devices.size(); + + foreach (SubDevice &sub, devices) { + size_t sy = y + i * sub_h; + size_t sh = (i == (size_t)devices.size() - 1) ? h - sub_h * i : sub_h; + + SubDevice *owner_sub = find_matching_mem_device(key, sub); + mem.device = owner_sub->device; + mem.device_pointer = owner_sub->ptr_map[key]; + + owner_sub->device->mem_copy_from(mem, sy, w, sh, elem); + i++; + } + + mem.device = this; + mem.device_pointer = key; + } + + void mem_zero(device_memory &mem) override + { + device_ptr existing_key = mem.device_pointer; + device_ptr key = (existing_key) ? existing_key : unique_key++; + size_t existing_size = mem.device_size; + + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_suitable_mem_device(existing_key, island); + mem.device = owner_sub->device; + mem.device_pointer = (existing_key) ? owner_sub->ptr_map[existing_key] : 0; + mem.device_size = existing_size; + + owner_sub->device->mem_zero(mem); + owner_sub->ptr_map[key] = mem.device_pointer; + } + + mem.device = this; + mem.device_pointer = key; + stats.mem_alloc(mem.device_size - existing_size); + } + + void mem_free(device_memory &mem) override + { + device_ptr key = mem.device_pointer; + size_t existing_size = mem.device_size; + + /* Free memory that was allocated for all devices (see above) on each device */ + foreach (const vector &island, peer_islands) { + SubDevice *owner_sub = find_matching_mem_device(key, *island.front()); + mem.device = owner_sub->device; + mem.device_pointer = owner_sub->ptr_map[key]; + mem.device_size = existing_size; + + owner_sub->device->mem_free(mem); + owner_sub->ptr_map.erase(owner_sub->ptr_map.find(key)); + + if (mem.type == MEM_TEXTURE) { + /* Free texture objects on all devices */ + foreach (SubDevice *island_sub, island) { + if (island_sub != owner_sub) { + island_sub->device->mem_free(mem); + } + } + } + } + + mem.device = this; + mem.device_pointer = 0; + mem.device_size = 0; + stats.mem_free(existing_size); + } + + void const_copy_to(const char *name, void *host, size_t size) override + { + foreach (SubDevice &sub, devices) + sub.device->const_copy_to(name, host, size); + } + + int device_number(Device *sub_device) override + { + int i = 0; + + foreach (SubDevice &sub, devices) { + if (sub.device == sub_device) + return i; + i++; + } + + return -1; + } + + virtual void foreach_device(const function &callback) override + { + foreach (SubDevice &sub, devices) { + sub.device->foreach_device(callback); + } + } +}; + +Device *device_multi_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ + return new MultiDevice(info, stats, profiler); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/multi/device.h b/intern/cycles/device/multi/device.h new file mode 100644 index 00000000000..ac77f6574ef --- /dev/null +++ b/intern/cycles/device/multi/device.h @@ -0,0 +1,31 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/string.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +Device *device_multi_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/opencl/device_opencl.h b/intern/cycles/device/opencl/device_opencl.h deleted file mode 100644 index a65e764b0d4..00000000000 --- a/intern/cycles/device/opencl/device_opencl.h +++ /dev/null @@ -1,658 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/device.h" -# include "device/device_denoising.h" -# include "device/device_split_kernel.h" - -# include "util/util_map.h" -# include "util/util_param.h" -# include "util/util_string.h" -# include "util/util_task.h" - -# include "clew.h" - -# include "device/opencl/memory_manager.h" - -CCL_NAMESPACE_BEGIN - -/* Disable workarounds, seems to be working fine on latest drivers. */ -# define CYCLES_DISABLE_DRIVER_WORKAROUNDS - -/* Define CYCLES_DISABLE_DRIVER_WORKAROUNDS to disable workarounds for testing. */ -# ifndef CYCLES_DISABLE_DRIVER_WORKAROUNDS -/* Work around AMD driver hangs by ensuring each command is finished before doing anything else. */ -# undef clEnqueueNDRangeKernel -# define clEnqueueNDRangeKernel(a, b, c, d, e, f, g, h, i) \ - CLEW_GET_FUN(__clewEnqueueNDRangeKernel)(a, b, c, d, e, f, g, h, i); \ - clFinish(a); - -# undef clEnqueueWriteBuffer -# define clEnqueueWriteBuffer(a, b, c, d, e, f, g, h, i) \ - CLEW_GET_FUN(__clewEnqueueWriteBuffer)(a, b, c, d, e, f, g, h, i); \ - clFinish(a); - -# undef clEnqueueReadBuffer -# define clEnqueueReadBuffer(a, b, c, d, e, f, g, h, i) \ - CLEW_GET_FUN(__clewEnqueueReadBuffer)(a, b, c, d, e, f, g, h, i); \ - clFinish(a); -# endif /* CYCLES_DISABLE_DRIVER_WORKAROUNDS */ - -# define CL_MEM_PTR(p) ((cl_mem)(uintptr_t)(p)) - -struct OpenCLPlatformDevice { - OpenCLPlatformDevice(cl_platform_id platform_id, - const string &platform_name, - cl_device_id device_id, - cl_device_type device_type, - const string &device_name, - const string &hardware_id, - const string &device_extensions) - : platform_id(platform_id), - platform_name(platform_name), - device_id(device_id), - device_type(device_type), - device_name(device_name), - hardware_id(hardware_id), - device_extensions(device_extensions) - { - } - cl_platform_id platform_id; - string platform_name; - cl_device_id device_id; - cl_device_type device_type; - string device_name; - string hardware_id; - string device_extensions; -}; - -/* Contains all static OpenCL helper functions. */ -class OpenCLInfo { - public: - static cl_device_type device_type(); - static bool use_debug(); - static bool device_supported(const string &platform_name, const cl_device_id device_id); - static bool platform_version_check(cl_platform_id platform, string *error = NULL); - static bool device_version_check(cl_device_id device, string *error = NULL); - static bool get_device_version(cl_device_id device, - int *r_major, - int *r_minor, - string *error = NULL); - static string get_hardware_id(const string &platform_name, cl_device_id device_id); - static void get_usable_devices(vector *usable_devices); - - /* ** Some handy shortcuts to low level cl*GetInfo() functions. ** */ - - /* Platform information. */ - static bool get_num_platforms(cl_uint *num_platforms, cl_int *error = NULL); - static cl_uint get_num_platforms(); - - static bool get_platforms(vector *platform_ids, cl_int *error = NULL); - static vector get_platforms(); - - static bool get_platform_name(cl_platform_id platform_id, string *platform_name); - static string get_platform_name(cl_platform_id platform_id); - - static bool get_num_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - cl_uint *num_devices, - cl_int *error = NULL); - static cl_uint get_num_platform_devices(cl_platform_id platform_id, cl_device_type device_type); - - static bool get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - vector *device_ids, - cl_int *error = NULL); - static vector get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type); - - /* Device information. */ - static bool get_device_name(cl_device_id device_id, string *device_name, cl_int *error = NULL); - - static string get_device_name(cl_device_id device_id); - - static bool get_device_extensions(cl_device_id device_id, - string *device_extensions, - cl_int *error = NULL); - - static string get_device_extensions(cl_device_id device_id); - - static bool get_device_type(cl_device_id device_id, - cl_device_type *device_type, - cl_int *error = NULL); - static cl_device_type get_device_type(cl_device_id device_id); - - static bool get_driver_version(cl_device_id device_id, - int *major, - int *minor, - cl_int *error = NULL); - - static int mem_sub_ptr_alignment(cl_device_id device_id); - - /* Get somewhat more readable device name. - * Main difference is AMD OpenCL here which only gives code name - * for the regular device name. This will give more sane device - * name using some extensions. - */ - static string get_readable_device_name(cl_device_id device_id); -}; - -/* Thread safe cache for contexts and programs. - */ -class OpenCLCache { - struct Slot { - struct ProgramEntry { - ProgramEntry(); - ProgramEntry(const ProgramEntry &rhs); - ~ProgramEntry(); - cl_program program; - thread_mutex *mutex; - }; - - Slot(); - Slot(const Slot &rhs); - ~Slot(); - - thread_mutex *context_mutex; - cl_context context; - typedef map EntryMap; - EntryMap programs; - }; - - /* key is combination of platform ID and device ID */ - typedef pair PlatformDevicePair; - - /* map of Slot objects */ - typedef map CacheMap; - CacheMap cache; - - /* MD5 hash of the kernel source. */ - string kernel_md5; - - thread_mutex cache_lock; - thread_mutex kernel_md5_lock; - - /* lazy instantiate */ - static OpenCLCache &global_instance(); - - public: - enum ProgramName { - OCL_DEV_BASE_PROGRAM, - OCL_DEV_MEGAKERNEL_PROGRAM, - }; - - /* Lookup context in the cache. If this returns NULL, slot_locker - * will be holding a lock for the cache. slot_locker should refer to a - * default constructed thread_scoped_lock. */ - static cl_context get_context(cl_platform_id platform, - cl_device_id device, - thread_scoped_lock &slot_locker); - /* Same as above. */ - static cl_program get_program(cl_platform_id platform, - cl_device_id device, - ustring key, - thread_scoped_lock &slot_locker); - - /* Store context in the cache. You MUST have tried to get the item before storing to it. */ - static void store_context(cl_platform_id platform, - cl_device_id device, - cl_context context, - thread_scoped_lock &slot_locker); - /* Same as above. */ - static void store_program(cl_platform_id platform, - cl_device_id device, - cl_program program, - ustring key, - thread_scoped_lock &slot_locker); - - static string get_kernel_md5(); -}; - -# define opencl_device_assert(device, stmt) \ - { \ - cl_int err = stmt; \ -\ - if (err != CL_SUCCESS) { \ - string message = string_printf( \ - "OpenCL error: %s in %s (%s:%d)", clewErrorString(err), #stmt, __FILE__, __LINE__); \ - if ((device)->error_message() == "") { \ - (device)->set_error(message); \ - } \ - fprintf(stderr, "%s\n", message.c_str()); \ - } \ - } \ - (void)0 - -# define opencl_assert(stmt) \ - { \ - cl_int err = stmt; \ -\ - if (err != CL_SUCCESS) { \ - string message = string_printf( \ - "OpenCL error: %s in %s (%s:%d)", clewErrorString(err), #stmt, __FILE__, __LINE__); \ - if (error_msg == "") { \ - error_msg = message; \ - } \ - fprintf(stderr, "%s\n", message.c_str()); \ - } \ - } \ - (void)0 - -class OpenCLDevice : public Device { - public: - DedicatedTaskPool task_pool; - - /* Task pool for required kernels (base, AO kernels during foreground rendering) */ - TaskPool load_required_kernel_task_pool; - /* Task pool for optional kernels (feature kernels during foreground rendering) */ - TaskPool load_kernel_task_pool; - std::atomic load_kernel_num_compiling; - - cl_context cxContext; - cl_command_queue cqCommandQueue; - cl_platform_id cpPlatform; - cl_device_id cdDevice; - cl_int ciErr; - int device_num; - - class OpenCLProgram { - public: - OpenCLProgram() : loaded(false), needs_compiling(true), program(NULL), device(NULL) - { - } - OpenCLProgram(OpenCLDevice *device, - const string &program_name, - const string &kernel_name, - const string &kernel_build_options, - bool use_stdout = true); - ~OpenCLProgram(); - - void add_kernel(ustring name); - - /* Try to load the program from device cache or disk */ - bool load(); - /* Compile the kernel (first separate, fail-back to local). */ - void compile(); - /* Create the OpenCL kernels after loading or compiling */ - void create_kernels(); - - bool is_loaded() const - { - return loaded; - } - const string &get_log() const - { - return log; - } - void report_error(); - - /* Wait until this kernel is available to be used - * It will return true when the kernel is available. - * It will return false when the kernel is not available - * or could not be loaded. */ - bool wait_for_availability(); - - cl_kernel operator()(); - cl_kernel operator()(ustring name); - - void release(); - - private: - bool build_kernel(const string *debug_src); - /* Build the program by calling the own process. - * This is required for multithreaded OpenCL compilation, since most Frameworks serialize - * build calls internally if they come from the same process. - * If that is not supported, this function just returns false. - */ - bool compile_separate(const string &clbin); - /* Build the program by calling OpenCL directly. */ - bool compile_kernel(const string *debug_src); - /* Loading and saving the program from/to disk. */ - bool load_binary(const string &clbin, const string *debug_src = NULL); - bool save_binary(const string &clbin); - - void add_log(const string &msg, bool is_debug); - void add_error(const string &msg); - - bool loaded; - bool needs_compiling; - - cl_program program; - OpenCLDevice *device; - - /* Used for the OpenCLCache key. */ - string program_name; - - string kernel_file, kernel_build_options, device_md5; - - bool use_stdout; - string log, error_msg; - string compile_output; - - map kernels; - }; - - /* Container for all types of split programs. */ - class OpenCLSplitPrograms { - public: - OpenCLDevice *device; - OpenCLProgram program_split; - OpenCLProgram program_lamp_emission; - OpenCLProgram program_do_volume; - OpenCLProgram program_indirect_background; - OpenCLProgram program_shader_eval; - OpenCLProgram program_holdout_emission_blurring_pathtermination_ao; - OpenCLProgram program_subsurface_scatter; - OpenCLProgram program_direct_lighting; - OpenCLProgram program_shadow_blocked_ao; - OpenCLProgram program_shadow_blocked_dl; - - OpenCLSplitPrograms(OpenCLDevice *device); - ~OpenCLSplitPrograms(); - - /* Load the kernels and put the created kernels in the given - * `programs` parameter. */ - void load_kernels(vector &programs, - const DeviceRequestedFeatures &requested_features); - }; - - DeviceSplitKernel *split_kernel; - - OpenCLProgram base_program; - OpenCLProgram bake_program; - OpenCLProgram displace_program; - OpenCLProgram background_program; - OpenCLProgram denoising_program; - - OpenCLSplitPrograms kernel_programs; - - typedef map *> ConstMemMap; - typedef map MemMap; - - ConstMemMap const_mem_map; - MemMap mem_map; - - bool device_initialized; - string platform_name; - string device_name; - - bool opencl_error(cl_int err); - void opencl_error(const string &message); - void opencl_assert_err(cl_int err, const char *where); - - OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background); - ~OpenCLDevice(); - - static void CL_CALLBACK context_notify_callback(const char *err_info, - const void * /*private_info*/, - size_t /*cb*/, - void *user_data); - - bool opencl_version_check(); - OpenCLSplitPrograms *get_split_programs(); - - string device_md5_hash(string kernel_custom_build_options = ""); - bool load_kernels(const DeviceRequestedFeatures &requested_features); - void load_required_kernels(const DeviceRequestedFeatures &requested_features); - - bool wait_for_availability(const DeviceRequestedFeatures &requested_features); - DeviceKernelStatus get_active_kernel_switch_state(); - - /* Get the name of the opencl program for the given kernel */ - const string get_opencl_program_name(const string &kernel_name); - /* Get the program file name to compile (*.cl) for the given kernel */ - const string get_opencl_program_filename(const string &kernel_name); - string get_build_options(const DeviceRequestedFeatures &requested_features, - const string &opencl_program_name); - /* Enable the default features to reduce recompilation events */ - void enable_default_features(DeviceRequestedFeatures &features); - - void mem_alloc(device_memory &mem); - void mem_copy_to(device_memory &mem); - void mem_copy_from(device_memory &mem, int y, int w, int h, int elem); - void mem_zero(device_memory &mem); - void mem_free(device_memory &mem); - - int mem_sub_ptr_alignment(); - - void const_copy_to(const char *name, void *host, size_t size); - void global_alloc(device_memory &mem); - void global_free(device_memory &mem); - void tex_alloc(device_texture &mem); - void tex_free(device_texture &mem); - - size_t global_size_round_up(int group_size, int global_size); - void enqueue_kernel(cl_kernel kernel, - size_t w, - size_t h, - bool x_workgroups = false, - size_t max_workgroup_size = -1); - void set_kernel_arg_mem(cl_kernel kernel, cl_uint *narg, const char *name); - void set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg); - - void film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half); - void shader(DeviceTask &task); - void update_adaptive(DeviceTask &task, RenderTile &tile, int sample); - void bake(DeviceTask &task, RenderTile &tile); - - void denoise(RenderTile &tile, DenoisingTask &denoising); - - int get_split_task_count(DeviceTask & /*task*/) - { - return 1; - } - - void task_add(DeviceTask &task) - { - task_pool.push([=] { - DeviceTask task_copy = task; - thread_run(task_copy); - }); - } - - void task_wait() - { - task_pool.wait(); - } - - void task_cancel() - { - task_pool.cancel(); - } - - void thread_run(DeviceTask &task); - - virtual BVHLayoutMask get_bvh_layout_mask() const - { - return BVH_LAYOUT_BVH2; - } - - virtual bool show_samples() const - { - return true; - } - - protected: - string kernel_build_options(const string *debug_src = NULL); - - void mem_zero_kernel(device_ptr ptr, size_t size); - - bool denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task); - bool denoising_construct_transform(DenoisingTask *task); - bool denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task); - bool denoising_solve(device_ptr output_ptr, DenoisingTask *task); - bool denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task); - bool denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task); - bool denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task); - bool denoising_write_feature(int to_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task); - bool denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task); - - device_ptr mem_alloc_sub_ptr(device_memory &mem, int offset, int size); - void mem_free_sub_ptr(device_ptr ptr); - - class ArgumentWrapper { - public: - ArgumentWrapper() : size(0), pointer(NULL) - { - } - - ArgumentWrapper(device_memory &argument) - : size(sizeof(void *)), pointer((void *)(&argument.device_pointer)) - { - } - - template - ArgumentWrapper(device_vector &argument) - : size(sizeof(void *)), pointer((void *)(&argument.device_pointer)) - { - } - - template - ArgumentWrapper(device_only_memory &argument) - : size(sizeof(void *)), pointer((void *)(&argument.device_pointer)) - { - } - template ArgumentWrapper(T &argument) : size(sizeof(argument)), pointer(&argument) - { - } - - ArgumentWrapper(int argument) : size(sizeof(int)), int_value(argument), pointer(&int_value) - { - } - - ArgumentWrapper(float argument) - : size(sizeof(float)), float_value(argument), pointer(&float_value) - { - } - - size_t size; - int int_value; - float float_value; - void *pointer; - }; - - /* TODO(sergey): In the future we can use variadic templates, once - * C++0x is allowed. Should allow to clean this up a bit. - */ - int kernel_set_args(cl_kernel kernel, - int start_argument_index, - const ArgumentWrapper &arg1 = ArgumentWrapper(), - const ArgumentWrapper &arg2 = ArgumentWrapper(), - const ArgumentWrapper &arg3 = ArgumentWrapper(), - const ArgumentWrapper &arg4 = ArgumentWrapper(), - const ArgumentWrapper &arg5 = ArgumentWrapper(), - const ArgumentWrapper &arg6 = ArgumentWrapper(), - const ArgumentWrapper &arg7 = ArgumentWrapper(), - const ArgumentWrapper &arg8 = ArgumentWrapper(), - const ArgumentWrapper &arg9 = ArgumentWrapper(), - const ArgumentWrapper &arg10 = ArgumentWrapper(), - const ArgumentWrapper &arg11 = ArgumentWrapper(), - const ArgumentWrapper &arg12 = ArgumentWrapper(), - const ArgumentWrapper &arg13 = ArgumentWrapper(), - const ArgumentWrapper &arg14 = ArgumentWrapper(), - const ArgumentWrapper &arg15 = ArgumentWrapper(), - const ArgumentWrapper &arg16 = ArgumentWrapper(), - const ArgumentWrapper &arg17 = ArgumentWrapper(), - const ArgumentWrapper &arg18 = ArgumentWrapper(), - const ArgumentWrapper &arg19 = ArgumentWrapper(), - const ArgumentWrapper &arg20 = ArgumentWrapper(), - const ArgumentWrapper &arg21 = ArgumentWrapper(), - const ArgumentWrapper &arg22 = ArgumentWrapper(), - const ArgumentWrapper &arg23 = ArgumentWrapper(), - const ArgumentWrapper &arg24 = ArgumentWrapper(), - const ArgumentWrapper &arg25 = ArgumentWrapper(), - const ArgumentWrapper &arg26 = ArgumentWrapper(), - const ArgumentWrapper &arg27 = ArgumentWrapper(), - const ArgumentWrapper &arg28 = ArgumentWrapper(), - const ArgumentWrapper &arg29 = ArgumentWrapper(), - const ArgumentWrapper &arg30 = ArgumentWrapper(), - const ArgumentWrapper &arg31 = ArgumentWrapper(), - const ArgumentWrapper &arg32 = ArgumentWrapper(), - const ArgumentWrapper &arg33 = ArgumentWrapper()); - - void release_kernel_safe(cl_kernel kernel); - void release_mem_object_safe(cl_mem mem); - void release_program_safe(cl_program program); - - /* ** Those guys are for working around some compiler-specific bugs ** */ - - cl_program load_cached_kernel(ustring key, thread_scoped_lock &cache_locker); - - void store_cached_kernel(cl_program program, ustring key, thread_scoped_lock &cache_locker); - - private: - MemoryManager memory_manager; - friend class MemoryManager; - - static_assert_align(TextureInfo, 16); - device_vector texture_info; - - typedef map TexturesMap; - TexturesMap textures; - - bool textures_need_update; - - protected: - void flush_texture_buffers(); - - friend class OpenCLSplitKernel; - friend class OpenCLSplitKernelFunction; -}; - -Device *opencl_create_split_device(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - bool background); - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/opencl/device_opencl_impl.cpp b/intern/cycles/device/opencl/device_opencl_impl.cpp deleted file mode 100644 index 31a2265700c..00000000000 --- a/intern/cycles/device/opencl/device_opencl_impl.cpp +++ /dev/null @@ -1,2113 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/opencl/device_opencl.h" - -# include "kernel/kernel_types.h" -# include "kernel/split/kernel_split_data_types.h" - -# include "util/util_algorithm.h" -# include "util/util_debug.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -struct texture_slot_t { - texture_slot_t(const string &name, int slot) : name(name), slot(slot) - { - } - string name; - int slot; -}; - -static const string NON_SPLIT_KERNELS = - "denoising " - "base " - "background " - "displace "; - -static const string SPLIT_BUNDLE_KERNELS = - "data_init " - "path_init " - "state_buffer_size " - "scene_intersect " - "queue_enqueue " - "shader_setup " - "shader_sort " - "enqueue_inactive " - "next_iteration_setup " - "indirect_subsurface " - "buffer_update " - "adaptive_stopping " - "adaptive_filter_x " - "adaptive_filter_y " - "adaptive_adjust_samples"; - -const string OpenCLDevice::get_opencl_program_name(const string &kernel_name) -{ - if (NON_SPLIT_KERNELS.find(kernel_name) != std::string::npos) { - return kernel_name; - } - else if (SPLIT_BUNDLE_KERNELS.find(kernel_name) != std::string::npos) { - return "split_bundle"; - } - else { - return "split_" + kernel_name; - } -} - -const string OpenCLDevice::get_opencl_program_filename(const string &kernel_name) -{ - if (kernel_name == "denoising") { - return "filter.cl"; - } - else if (SPLIT_BUNDLE_KERNELS.find(kernel_name) != std::string::npos) { - return "kernel_split_bundle.cl"; - } - else { - return "kernel_" + kernel_name + ".cl"; - } -} - -/* Enable features that we always want to compile to reduce recompilation events */ -void OpenCLDevice::enable_default_features(DeviceRequestedFeatures &features) -{ - features.use_transparent = true; - features.use_shadow_tricks = true; - features.use_principled = true; - features.use_denoising = true; - - if (!background) { - features.max_nodes_group = NODE_GROUP_LEVEL_MAX; - features.nodes_features = NODE_FEATURE_ALL; - features.use_hair = true; - features.use_subsurface = true; - features.use_camera_motion = false; - features.use_object_motion = false; - } -} - -string OpenCLDevice::get_build_options(const DeviceRequestedFeatures &requested_features, - const string &opencl_program_name) -{ - /* first check for non-split kernel programs */ - if (opencl_program_name == "base" || opencl_program_name == "denoising") { - return ""; - } - else if (opencl_program_name == "bake") { - /* Note: get_build_options for bake is only requested when baking is enabled. - * displace and background are always requested. - * `__SPLIT_KERNEL__` must not be present in the compile directives for bake */ - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - features.use_denoising = false; - features.use_object_motion = false; - features.use_camera_motion = false; - features.use_hair = true; - features.use_subsurface = true; - features.max_nodes_group = NODE_GROUP_LEVEL_MAX; - features.nodes_features = NODE_FEATURE_ALL; - features.use_integrator_branched = false; - return features.get_build_options(); - } - else if (opencl_program_name == "displace") { - /* As displacement does not use any nodes from the Shading group (eg BSDF). - * We disable all features that are related to shading. */ - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - features.use_denoising = false; - features.use_object_motion = false; - features.use_camera_motion = false; - features.use_baking = false; - features.use_transparent = false; - features.use_shadow_tricks = false; - features.use_subsurface = false; - features.use_volume = false; - features.nodes_features &= ~NODE_FEATURE_VOLUME; - features.use_denoising = false; - features.use_principled = false; - features.use_integrator_branched = false; - return features.get_build_options(); - } - else if (opencl_program_name == "background") { - /* Background uses Background shading - * It is save to disable shadow features, subsurface and volumetric. */ - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - features.use_baking = false; - features.use_object_motion = false; - features.use_camera_motion = false; - features.use_transparent = false; - features.use_shadow_tricks = false; - features.use_denoising = false; - /* NOTE: currently possible to use surface nodes like `Hair Info`, `Bump` node. - * Perhaps we should remove them in UI as it does not make any sense when - * rendering background. */ - features.nodes_features &= ~NODE_FEATURE_VOLUME; - features.use_subsurface = false; - features.use_volume = false; - features.use_shader_raytrace = false; - features.use_patch_evaluation = false; - features.use_integrator_branched = false; - return features.get_build_options(); - } - - string build_options = "-D__SPLIT_KERNEL__ "; - /* Set compute device build option. */ - cl_device_type device_type; - OpenCLInfo::get_device_type(this->cdDevice, &device_type, &this->ciErr); - assert(this->ciErr == CL_SUCCESS); - if (device_type == CL_DEVICE_TYPE_GPU) { - build_options += "-D__COMPUTE_DEVICE_GPU__ "; - } - - DeviceRequestedFeatures nofeatures; - enable_default_features(nofeatures); - - /* Add program specific optimized compile directives */ - if (opencl_program_name == "split_do_volume" && !requested_features.use_volume) { - build_options += nofeatures.get_build_options(); - } - else { - DeviceRequestedFeatures features(requested_features); - enable_default_features(features); - - /* Always turn off baking at this point. Baking is only useful when building the bake kernel. - * this also makes sure that the kernels that are build during baking can be reused - * when not doing any baking. */ - features.use_baking = false; - - /* Do not vary on shaders when program doesn't do any shading. - * We have bundled them in a single program. */ - if (opencl_program_name == "split_bundle") { - features.max_nodes_group = 0; - features.nodes_features = 0; - features.use_shader_raytrace = false; - } - - /* No specific settings, just add the regular ones */ - build_options += features.get_build_options(); - } - - return build_options; -} - -OpenCLDevice::OpenCLSplitPrograms::OpenCLSplitPrograms(OpenCLDevice *device_) -{ - device = device_; -} - -OpenCLDevice::OpenCLSplitPrograms::~OpenCLSplitPrograms() -{ - program_split.release(); - program_lamp_emission.release(); - program_do_volume.release(); - program_indirect_background.release(); - program_shader_eval.release(); - program_holdout_emission_blurring_pathtermination_ao.release(); - program_subsurface_scatter.release(); - program_direct_lighting.release(); - program_shadow_blocked_ao.release(); - program_shadow_blocked_dl.release(); -} - -void OpenCLDevice::OpenCLSplitPrograms::load_kernels( - vector &programs, const DeviceRequestedFeatures &requested_features) -{ - if (!requested_features.use_baking) { -# define ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(kernel_name) \ - program_split.add_kernel(ustring("path_trace_" #kernel_name)); -# define ADD_SPLIT_KERNEL_PROGRAM(kernel_name) \ - const string program_name_##kernel_name = "split_" #kernel_name; \ - program_##kernel_name = OpenCLDevice::OpenCLProgram( \ - device, \ - program_name_##kernel_name, \ - "kernel_" #kernel_name ".cl", \ - device->get_build_options(requested_features, program_name_##kernel_name)); \ - program_##kernel_name.add_kernel(ustring("path_trace_" #kernel_name)); \ - programs.push_back(&program_##kernel_name); - - /* Ordered with most complex kernels first, to reduce overall compile time. */ - ADD_SPLIT_KERNEL_PROGRAM(subsurface_scatter); - ADD_SPLIT_KERNEL_PROGRAM(direct_lighting); - ADD_SPLIT_KERNEL_PROGRAM(indirect_background); - if (requested_features.use_volume) { - ADD_SPLIT_KERNEL_PROGRAM(do_volume); - } - ADD_SPLIT_KERNEL_PROGRAM(shader_eval); - ADD_SPLIT_KERNEL_PROGRAM(lamp_emission); - ADD_SPLIT_KERNEL_PROGRAM(holdout_emission_blurring_pathtermination_ao); - ADD_SPLIT_KERNEL_PROGRAM(shadow_blocked_dl); - ADD_SPLIT_KERNEL_PROGRAM(shadow_blocked_ao); - - /* Quick kernels bundled in a single program to reduce overhead of starting - * Blender processes. */ - program_split = OpenCLDevice::OpenCLProgram( - device, - "split_bundle", - "kernel_split_bundle.cl", - device->get_build_options(requested_features, "split_bundle")); - - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(data_init); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(state_buffer_size); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(path_init); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(scene_intersect); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(queue_enqueue); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(shader_setup); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(shader_sort); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(enqueue_inactive); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(next_iteration_setup); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(indirect_subsurface); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(buffer_update); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_stopping); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_filter_x); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_filter_y); - ADD_SPLIT_KERNEL_BUNDLE_PROGRAM(adaptive_adjust_samples); - programs.push_back(&program_split); - -# undef ADD_SPLIT_KERNEL_PROGRAM -# undef ADD_SPLIT_KERNEL_BUNDLE_PROGRAM - } -} - -namespace { - -/* Copy dummy KernelGlobals related to OpenCL from kernel_globals.h to - * fetch its size. - */ -typedef struct KernelGlobalsDummy { - ccl_constant KernelData *data; - ccl_global char *buffers[8]; - -# define KERNEL_TEX(type, name) TextureInfo name; -# include "kernel/kernel_textures.h" -# undef KERNEL_TEX - SplitData split_data; - SplitParams split_param_data; -} KernelGlobalsDummy; - -} // namespace - -struct CachedSplitMemory { - int id; - device_memory *split_data; - device_memory *ray_state; - device_memory *queue_index; - device_memory *use_queues_flag; - device_memory *work_pools; - device_ptr *buffer; -}; - -class OpenCLSplitKernelFunction : public SplitKernelFunction { - public: - OpenCLDevice *device; - OpenCLDevice::OpenCLProgram program; - CachedSplitMemory &cached_memory; - int cached_id; - - OpenCLSplitKernelFunction(OpenCLDevice *device, CachedSplitMemory &cached_memory) - : device(device), cached_memory(cached_memory), cached_id(cached_memory.id - 1) - { - } - - ~OpenCLSplitKernelFunction() - { - program.release(); - } - - virtual bool enqueue(const KernelDimensions &dim, device_memory &kg, device_memory &data) - { - if (cached_id != cached_memory.id) { - cl_uint start_arg_index = device->kernel_set_args( - program(), 0, kg, data, *cached_memory.split_data, *cached_memory.ray_state); - - device->set_kernel_arg_buffers(program(), &start_arg_index); - - start_arg_index += device->kernel_set_args(program(), - start_arg_index, - *cached_memory.queue_index, - *cached_memory.use_queues_flag, - *cached_memory.work_pools, - *cached_memory.buffer); - - cached_id = cached_memory.id; - } - - device->ciErr = clEnqueueNDRangeKernel(device->cqCommandQueue, - program(), - 2, - NULL, - dim.global_size, - dim.local_size, - 0, - NULL, - NULL); - - device->opencl_assert_err(device->ciErr, "clEnqueueNDRangeKernel"); - - if (device->ciErr != CL_SUCCESS) { - string message = string_printf("OpenCL error: %s in clEnqueueNDRangeKernel()", - clewErrorString(device->ciErr)); - device->opencl_error(message); - return false; - } - - return true; - } -}; - -class OpenCLSplitKernel : public DeviceSplitKernel { - OpenCLDevice *device; - CachedSplitMemory cached_memory; - - public: - explicit OpenCLSplitKernel(OpenCLDevice *device) : DeviceSplitKernel(device), device(device) - { - } - - virtual SplitKernelFunction *get_split_kernel_function( - const string &kernel_name, const DeviceRequestedFeatures &requested_features) - { - OpenCLSplitKernelFunction *kernel = new OpenCLSplitKernelFunction(device, cached_memory); - - const string program_name = device->get_opencl_program_name(kernel_name); - kernel->program = OpenCLDevice::OpenCLProgram( - device, - program_name, - device->get_opencl_program_filename(kernel_name), - device->get_build_options(requested_features, program_name)); - - kernel->program.add_kernel(ustring("path_trace_" + kernel_name)); - kernel->program.load(); - - if (!kernel->program.is_loaded()) { - delete kernel; - return NULL; - } - - return kernel; - } - - virtual uint64_t state_buffer_size(device_memory &kg, device_memory &data, size_t num_threads) - { - device_vector size_buffer(device, "size_buffer", MEM_READ_WRITE); - size_buffer.alloc(1); - size_buffer.zero_to_device(); - - uint threads = num_threads; - OpenCLDevice::OpenCLSplitPrograms *programs = device->get_split_programs(); - cl_kernel kernel_state_buffer_size = programs->program_split( - ustring("path_trace_state_buffer_size")); - device->kernel_set_args(kernel_state_buffer_size, 0, kg, data, threads, size_buffer); - - size_t global_size = 64; - device->ciErr = clEnqueueNDRangeKernel(device->cqCommandQueue, - kernel_state_buffer_size, - 1, - NULL, - &global_size, - NULL, - 0, - NULL, - NULL); - - device->opencl_assert_err(device->ciErr, "clEnqueueNDRangeKernel"); - - size_buffer.copy_from_device(0, 1, 1); - size_t size = size_buffer[0]; - size_buffer.free(); - - if (device->ciErr != CL_SUCCESS) { - string message = string_printf("OpenCL error: %s in clEnqueueNDRangeKernel()", - clewErrorString(device->ciErr)); - device->opencl_error(message); - return 0; - } - - return size; - } - - virtual bool enqueue_split_kernel_data_init(const KernelDimensions &dim, - RenderTile &rtile, - int num_global_elements, - device_memory &kernel_globals, - device_memory &kernel_data, - device_memory &split_data, - device_memory &ray_state, - device_memory &queue_index, - device_memory &use_queues_flag, - device_memory &work_pool_wgs) - { - cl_int dQueue_size = dim.global_size[0] * dim.global_size[1]; - - /* Set the range of samples to be processed for every ray in - * path-regeneration logic. - */ - cl_int start_sample = rtile.start_sample; - cl_int end_sample = rtile.start_sample + rtile.num_samples; - - OpenCLDevice::OpenCLSplitPrograms *programs = device->get_split_programs(); - cl_kernel kernel_data_init = programs->program_split(ustring("path_trace_data_init")); - - cl_uint start_arg_index = device->kernel_set_args(kernel_data_init, - 0, - kernel_globals, - kernel_data, - split_data, - num_global_elements, - ray_state); - - device->set_kernel_arg_buffers(kernel_data_init, &start_arg_index); - - start_arg_index += device->kernel_set_args(kernel_data_init, - start_arg_index, - start_sample, - end_sample, - rtile.x, - rtile.y, - rtile.w, - rtile.h, - rtile.offset, - rtile.stride, - queue_index, - dQueue_size, - use_queues_flag, - work_pool_wgs, - rtile.num_samples, - rtile.buffer); - - /* Enqueue ckPathTraceKernel_data_init kernel. */ - device->ciErr = clEnqueueNDRangeKernel(device->cqCommandQueue, - kernel_data_init, - 2, - NULL, - dim.global_size, - dim.local_size, - 0, - NULL, - NULL); - - device->opencl_assert_err(device->ciErr, "clEnqueueNDRangeKernel"); - - if (device->ciErr != CL_SUCCESS) { - string message = string_printf("OpenCL error: %s in clEnqueueNDRangeKernel()", - clewErrorString(device->ciErr)); - device->opencl_error(message); - return false; - } - - cached_memory.split_data = &split_data; - cached_memory.ray_state = &ray_state; - cached_memory.queue_index = &queue_index; - cached_memory.use_queues_flag = &use_queues_flag; - cached_memory.work_pools = &work_pool_wgs; - cached_memory.buffer = &rtile.buffer; - cached_memory.id++; - - return true; - } - - virtual int2 split_kernel_local_size() - { - return make_int2(64, 1); - } - - virtual int2 split_kernel_global_size(device_memory &kg, - device_memory &data, - DeviceTask & /*task*/) - { - cl_device_type type = OpenCLInfo::get_device_type(device->cdDevice); - /* Use small global size on CPU devices as it seems to be much faster. */ - if (type == CL_DEVICE_TYPE_CPU) { - VLOG(1) << "Global size: (64, 64)."; - return make_int2(64, 64); - } - - cl_ulong max_buffer_size; - clGetDeviceInfo( - device->cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_buffer_size, NULL); - - if (DebugFlags().opencl.mem_limit) { - max_buffer_size = min(max_buffer_size, - cl_ulong(DebugFlags().opencl.mem_limit - device->stats.mem_used)); - } - - VLOG(1) << "Maximum device allocation size: " << string_human_readable_number(max_buffer_size) - << " bytes. (" << string_human_readable_size(max_buffer_size) << ")."; - - /* Limit to 2gb, as we shouldn't need more than that and some devices may support much more. */ - max_buffer_size = min(max_buffer_size / 2, (cl_ulong)2l * 1024 * 1024 * 1024); - - size_t num_elements = max_elements_for_max_buffer_size(kg, data, max_buffer_size); - int2 global_size = make_int2(max(round_down((int)sqrt(num_elements), 64), 64), - (int)sqrt(num_elements)); - - if (device->info.description.find("Intel") != string::npos) { - global_size = make_int2(min(512, global_size.x), min(512, global_size.y)); - } - - VLOG(1) << "Global size: " << global_size << "."; - return global_size; - } -}; - -bool OpenCLDevice::opencl_error(cl_int err) -{ - if (err != CL_SUCCESS) { - string message = string_printf("OpenCL error (%d): %s", err, clewErrorString(err)); - if (error_msg == "") - error_msg = message; - fprintf(stderr, "%s\n", message.c_str()); - return true; - } - - return false; -} - -void OpenCLDevice::opencl_error(const string &message) -{ - if (error_msg == "") - error_msg = message; - fprintf(stderr, "%s\n", message.c_str()); -} - -void OpenCLDevice::opencl_assert_err(cl_int err, const char *where) -{ - if (err != CL_SUCCESS) { - string message = string_printf( - "OpenCL error (%d): %s in %s", err, clewErrorString(err), where); - if (error_msg == "") - error_msg = message; - fprintf(stderr, "%s\n", message.c_str()); -# ifndef NDEBUG - abort(); -# endif - } -} - -OpenCLDevice::OpenCLDevice(DeviceInfo &info, Stats &stats, Profiler &profiler, bool background) - : Device(info, stats, profiler, background), - load_kernel_num_compiling(0), - kernel_programs(this), - memory_manager(this), - texture_info(this, "__texture_info", MEM_GLOBAL) -{ - cpPlatform = NULL; - cdDevice = NULL; - cxContext = NULL; - cqCommandQueue = NULL; - device_initialized = false; - textures_need_update = true; - - vector usable_devices; - OpenCLInfo::get_usable_devices(&usable_devices); - if (usable_devices.size() == 0) { - opencl_error("OpenCL: no devices found."); - return; - } - assert(info.num < usable_devices.size()); - OpenCLPlatformDevice &platform_device = usable_devices[info.num]; - device_num = info.num; - cpPlatform = platform_device.platform_id; - cdDevice = platform_device.device_id; - platform_name = platform_device.platform_name; - device_name = platform_device.device_name; - VLOG(2) << "Creating new Cycles device for OpenCL platform " << platform_name << ", device " - << device_name << "."; - - { - /* try to use cached context */ - thread_scoped_lock cache_locker; - cxContext = OpenCLCache::get_context(cpPlatform, cdDevice, cache_locker); - - if (cxContext == NULL) { - /* create context properties array to specify platform */ - const cl_context_properties context_props[] = { - CL_CONTEXT_PLATFORM, (cl_context_properties)cpPlatform, 0, 0}; - - /* create context */ - cxContext = clCreateContext( - context_props, 1, &cdDevice, context_notify_callback, cdDevice, &ciErr); - - if (opencl_error(ciErr)) { - opencl_error("OpenCL: clCreateContext failed"); - return; - } - - /* cache it */ - OpenCLCache::store_context(cpPlatform, cdDevice, cxContext, cache_locker); - } - } - - cqCommandQueue = clCreateCommandQueue(cxContext, cdDevice, 0, &ciErr); - if (opencl_error(ciErr)) { - opencl_error("OpenCL: Error creating command queue"); - return; - } - - /* Allocate this right away so that texture_info - * is placed at offset 0 in the device memory buffers. */ - texture_info.resize(1); - memory_manager.alloc("texture_info", texture_info); - - device_initialized = true; - - split_kernel = new OpenCLSplitKernel(this); -} - -OpenCLDevice::~OpenCLDevice() -{ - task_pool.cancel(); - load_required_kernel_task_pool.cancel(); - load_kernel_task_pool.cancel(); - - memory_manager.free(); - - ConstMemMap::iterator mt; - for (mt = const_mem_map.begin(); mt != const_mem_map.end(); mt++) { - delete mt->second; - } - - base_program.release(); - bake_program.release(); - displace_program.release(); - background_program.release(); - denoising_program.release(); - - if (cqCommandQueue) - clReleaseCommandQueue(cqCommandQueue); - if (cxContext) - clReleaseContext(cxContext); - - delete split_kernel; -} - -void CL_CALLBACK OpenCLDevice::context_notify_callback(const char *err_info, - const void * /*private_info*/, - size_t /*cb*/, - void *user_data) -{ - string device_name = OpenCLInfo::get_device_name((cl_device_id)user_data); - fprintf(stderr, "OpenCL error (%s): %s\n", device_name.c_str(), err_info); -} - -bool OpenCLDevice::opencl_version_check() -{ - string error; - if (!OpenCLInfo::platform_version_check(cpPlatform, &error)) { - opencl_error(error); - return false; - } - if (!OpenCLInfo::device_version_check(cdDevice, &error)) { - opencl_error(error); - return false; - } - return true; -} - -string OpenCLDevice::device_md5_hash(string kernel_custom_build_options) -{ - MD5Hash md5; - char version[256], driver[256], name[256], vendor[256]; - - clGetPlatformInfo(cpPlatform, CL_PLATFORM_VENDOR, sizeof(vendor), &vendor, NULL); - clGetDeviceInfo(cdDevice, CL_DEVICE_VERSION, sizeof(version), &version, NULL); - clGetDeviceInfo(cdDevice, CL_DEVICE_NAME, sizeof(name), &name, NULL); - clGetDeviceInfo(cdDevice, CL_DRIVER_VERSION, sizeof(driver), &driver, NULL); - - md5.append((uint8_t *)vendor, strlen(vendor)); - md5.append((uint8_t *)version, strlen(version)); - md5.append((uint8_t *)name, strlen(name)); - md5.append((uint8_t *)driver, strlen(driver)); - - string options = kernel_build_options(); - options += kernel_custom_build_options; - md5.append((uint8_t *)options.c_str(), options.size()); - - return md5.get_hex(); -} - -bool OpenCLDevice::load_kernels(const DeviceRequestedFeatures &requested_features) -{ - VLOG(2) << "Loading kernels for platform " << platform_name << ", device " << device_name << "."; - /* Verify if device was initialized. */ - if (!device_initialized) { - fprintf(stderr, "OpenCL: failed to initialize device.\n"); - return false; - } - - /* Verify we have right opencl version. */ - if (!opencl_version_check()) - return false; - - load_required_kernels(requested_features); - - vector programs; - kernel_programs.load_kernels(programs, requested_features); - - if (!requested_features.use_baking && requested_features.use_denoising) { - denoising_program = OpenCLProgram( - this, "denoising", "filter.cl", get_build_options(requested_features, "denoising")); - denoising_program.add_kernel(ustring("filter_divide_shadow")); - denoising_program.add_kernel(ustring("filter_get_feature")); - denoising_program.add_kernel(ustring("filter_write_feature")); - denoising_program.add_kernel(ustring("filter_detect_outliers")); - denoising_program.add_kernel(ustring("filter_combine_halves")); - denoising_program.add_kernel(ustring("filter_construct_transform")); - denoising_program.add_kernel(ustring("filter_nlm_calc_difference")); - denoising_program.add_kernel(ustring("filter_nlm_blur")); - denoising_program.add_kernel(ustring("filter_nlm_calc_weight")); - denoising_program.add_kernel(ustring("filter_nlm_update_output")); - denoising_program.add_kernel(ustring("filter_nlm_normalize")); - denoising_program.add_kernel(ustring("filter_nlm_construct_gramian")); - denoising_program.add_kernel(ustring("filter_finalize")); - programs.push_back(&denoising_program); - } - - load_required_kernel_task_pool.wait_work(); - - /* Parallel compilation of Cycles kernels, this launches multiple - * processes to workaround OpenCL frameworks serializing the calls - * internally within a single process. */ - foreach (OpenCLProgram *program, programs) { - if (!program->load()) { - load_kernel_num_compiling++; - load_kernel_task_pool.push([=] { - program->compile(); - load_kernel_num_compiling--; - }); - } - } - return true; -} - -void OpenCLDevice::load_required_kernels(const DeviceRequestedFeatures &requested_features) -{ - vector programs; - base_program = OpenCLProgram( - this, "base", "kernel_base.cl", get_build_options(requested_features, "base")); - base_program.add_kernel(ustring("convert_to_byte")); - base_program.add_kernel(ustring("convert_to_half_float")); - base_program.add_kernel(ustring("zero_buffer")); - programs.push_back(&base_program); - - if (requested_features.use_true_displacement) { - displace_program = OpenCLProgram( - this, "displace", "kernel_displace.cl", get_build_options(requested_features, "displace")); - displace_program.add_kernel(ustring("displace")); - programs.push_back(&displace_program); - } - - if (requested_features.use_background_light) { - background_program = OpenCLProgram(this, - "background", - "kernel_background.cl", - get_build_options(requested_features, "background")); - background_program.add_kernel(ustring("background")); - programs.push_back(&background_program); - } - - if (requested_features.use_baking) { - bake_program = OpenCLProgram( - this, "bake", "kernel_bake.cl", get_build_options(requested_features, "bake")); - bake_program.add_kernel(ustring("bake")); - programs.push_back(&bake_program); - } - - foreach (OpenCLProgram *program, programs) { - if (!program->load()) { - load_required_kernel_task_pool.push(function_bind(&OpenCLProgram::compile, program)); - } - } -} - -bool OpenCLDevice::wait_for_availability(const DeviceRequestedFeatures &requested_features) -{ - if (requested_features.use_baking) { - /* For baking, kernels have already been loaded in load_required_kernels(). */ - return true; - } - - load_kernel_task_pool.wait_work(); - return split_kernel->load_kernels(requested_features); -} - -OpenCLDevice::OpenCLSplitPrograms *OpenCLDevice::get_split_programs() -{ - return &kernel_programs; -} - -DeviceKernelStatus OpenCLDevice::get_active_kernel_switch_state() -{ - return DEVICE_KERNEL_USING_FEATURE_KERNEL; -} - -void OpenCLDevice::mem_alloc(device_memory &mem) -{ - if (mem.name) { - VLOG(1) << "Buffer allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - } - - size_t size = mem.memory_size(); - - /* check there is enough memory available for the allocation */ - cl_ulong max_alloc_size = 0; - clGetDeviceInfo(cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_alloc_size, NULL); - - if (DebugFlags().opencl.mem_limit) { - max_alloc_size = min(max_alloc_size, cl_ulong(DebugFlags().opencl.mem_limit - stats.mem_used)); - } - - if (size > max_alloc_size) { - string error = "Scene too complex to fit in available memory."; - if (mem.name != NULL) { - error += string_printf(" (allocating buffer %s failed.)", mem.name); - } - set_error(error); - - return; - } - - cl_mem_flags mem_flag; - void *mem_ptr = NULL; - - if (mem.type == MEM_READ_ONLY || mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) - mem_flag = CL_MEM_READ_ONLY; - else - mem_flag = CL_MEM_READ_WRITE; - - /* Zero-size allocation might be invoked by render, but not really - * supported by OpenCL. Using NULL as device pointer also doesn't really - * work for some reason, so for the time being we'll use special case - * will null_mem buffer. - */ - if (size != 0) { - mem.device_pointer = (device_ptr)clCreateBuffer(cxContext, mem_flag, size, mem_ptr, &ciErr); - opencl_assert_err(ciErr, "clCreateBuffer"); - } - else { - mem.device_pointer = 0; - } - - stats.mem_alloc(size); - mem.device_size = size; -} - -void OpenCLDevice::mem_copy_to(device_memory &mem) -{ - if (mem.type == MEM_GLOBAL) { - global_free(mem); - global_alloc(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - tex_alloc((device_texture &)mem); - } - else { - if (!mem.device_pointer) { - mem_alloc(mem); - } - - /* this is blocking */ - size_t size = mem.memory_size(); - if (size != 0) { - opencl_assert(clEnqueueWriteBuffer(cqCommandQueue, - CL_MEM_PTR(mem.device_pointer), - CL_TRUE, - 0, - size, - mem.host_pointer, - 0, - NULL, - NULL)); - } - } -} - -void OpenCLDevice::mem_copy_from(device_memory &mem, int y, int w, int h, int elem) -{ - size_t offset = elem * y * w; - size_t size = elem * w * h; - assert(size != 0); - opencl_assert(clEnqueueReadBuffer(cqCommandQueue, - CL_MEM_PTR(mem.device_pointer), - CL_TRUE, - offset, - size, - (uchar *)mem.host_pointer + offset, - 0, - NULL, - NULL)); -} - -void OpenCLDevice::mem_zero_kernel(device_ptr mem, size_t size) -{ - base_program.wait_for_availability(); - cl_kernel ckZeroBuffer = base_program(ustring("zero_buffer")); - - size_t global_size[] = {1024, 1024}; - size_t num_threads = global_size[0] * global_size[1]; - - cl_mem d_buffer = CL_MEM_PTR(mem); - cl_ulong d_offset = 0; - cl_ulong d_size = 0; - - while (d_offset < size) { - d_size = std::min(num_threads * sizeof(float4), size - d_offset); - - kernel_set_args(ckZeroBuffer, 0, d_buffer, d_size, d_offset); - - ciErr = clEnqueueNDRangeKernel( - cqCommandQueue, ckZeroBuffer, 2, NULL, global_size, NULL, 0, NULL, NULL); - opencl_assert_err(ciErr, "clEnqueueNDRangeKernel"); - - d_offset += d_size; - } -} - -void OpenCLDevice::mem_zero(device_memory &mem) -{ - if (!mem.device_pointer) { - mem_alloc(mem); - } - - if (mem.device_pointer) { - if (base_program.is_loaded()) { - mem_zero_kernel(mem.device_pointer, mem.memory_size()); - } - - if (mem.host_pointer) { - memset(mem.host_pointer, 0, mem.memory_size()); - } - - if (!base_program.is_loaded()) { - void *zero = mem.host_pointer; - - if (!mem.host_pointer) { - zero = util_aligned_malloc(mem.memory_size(), 16); - memset(zero, 0, mem.memory_size()); - } - - opencl_assert(clEnqueueWriteBuffer(cqCommandQueue, - CL_MEM_PTR(mem.device_pointer), - CL_TRUE, - 0, - mem.memory_size(), - zero, - 0, - NULL, - NULL)); - - if (!mem.host_pointer) { - util_aligned_free(zero); - } - } - } -} - -void OpenCLDevice::mem_free(device_memory &mem) -{ - if (mem.type == MEM_GLOBAL) { - global_free(mem); - } - else if (mem.type == MEM_TEXTURE) { - tex_free((device_texture &)mem); - } - else { - if (mem.device_pointer) { - if (mem.device_pointer != 0) { - opencl_assert(clReleaseMemObject(CL_MEM_PTR(mem.device_pointer))); - } - mem.device_pointer = 0; - - stats.mem_free(mem.device_size); - mem.device_size = 0; - } - } -} - -int OpenCLDevice::mem_sub_ptr_alignment() -{ - return OpenCLInfo::mem_sub_ptr_alignment(cdDevice); -} - -device_ptr OpenCLDevice::mem_alloc_sub_ptr(device_memory &mem, int offset, int size) -{ - cl_mem_flags mem_flag; - if (mem.type == MEM_READ_ONLY || mem.type == MEM_TEXTURE || mem.type == MEM_GLOBAL) - mem_flag = CL_MEM_READ_ONLY; - else - mem_flag = CL_MEM_READ_WRITE; - - cl_buffer_region info; - info.origin = mem.memory_elements_size(offset); - info.size = mem.memory_elements_size(size); - - device_ptr sub_buf = (device_ptr)clCreateSubBuffer( - CL_MEM_PTR(mem.device_pointer), mem_flag, CL_BUFFER_CREATE_TYPE_REGION, &info, &ciErr); - opencl_assert_err(ciErr, "clCreateSubBuffer"); - return sub_buf; -} - -void OpenCLDevice::mem_free_sub_ptr(device_ptr device_pointer) -{ - if (device_pointer != 0) { - opencl_assert(clReleaseMemObject(CL_MEM_PTR(device_pointer))); - } -} - -void OpenCLDevice::const_copy_to(const char *name, void *host, size_t size) -{ - ConstMemMap::iterator i = const_mem_map.find(name); - device_vector *data; - - if (i == const_mem_map.end()) { - data = new device_vector(this, name, MEM_READ_ONLY); - data->alloc(size); - const_mem_map.insert(ConstMemMap::value_type(name, data)); - } - else { - data = i->second; - } - - memcpy(data->data(), host, size); - data->copy_to_device(); -} - -void OpenCLDevice::global_alloc(device_memory &mem) -{ - VLOG(1) << "Global memory allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - memory_manager.alloc(mem.name, mem); - /* Set the pointer to non-null to keep code that inspects its value from thinking its - * unallocated. */ - mem.device_pointer = 1; - textures[mem.name] = &mem; - textures_need_update = true; -} - -void OpenCLDevice::global_free(device_memory &mem) -{ - if (mem.device_pointer) { - mem.device_pointer = 0; - - if (memory_manager.free(mem)) { - textures_need_update = true; - } - - foreach (TexturesMap::value_type &value, textures) { - if (value.second == &mem) { - textures.erase(value.first); - break; - } - } - } -} - -void OpenCLDevice::tex_alloc(device_texture &mem) -{ - VLOG(1) << "Texture allocate: " << mem.name << ", " - << string_human_readable_number(mem.memory_size()) << " bytes. (" - << string_human_readable_size(mem.memory_size()) << ")"; - - memory_manager.alloc(mem.name, mem); - /* Set the pointer to non-null to keep code that inspects its value from thinking its - * unallocated. */ - mem.device_pointer = 1; - textures[mem.name] = &mem; - textures_need_update = true; -} - -void OpenCLDevice::tex_free(device_texture &mem) -{ - global_free(mem); -} - -size_t OpenCLDevice::global_size_round_up(int group_size, int global_size) -{ - int r = global_size % group_size; - return global_size + ((r == 0) ? 0 : group_size - r); -} - -void OpenCLDevice::enqueue_kernel( - cl_kernel kernel, size_t w, size_t h, bool x_workgroups, size_t max_workgroup_size) -{ - size_t workgroup_size, max_work_items[3]; - - clGetKernelWorkGroupInfo( - kernel, cdDevice, CL_KERNEL_WORK_GROUP_SIZE, sizeof(size_t), &workgroup_size, NULL); - clGetDeviceInfo( - cdDevice, CL_DEVICE_MAX_WORK_ITEM_SIZES, sizeof(size_t) * 3, max_work_items, NULL); - - if (max_workgroup_size > 0 && workgroup_size > max_workgroup_size) { - workgroup_size = max_workgroup_size; - } - - /* Try to divide evenly over 2 dimensions. */ - size_t local_size[2]; - if (x_workgroups) { - local_size[0] = workgroup_size; - local_size[1] = 1; - } - else { - size_t sqrt_workgroup_size = max((size_t)sqrt((double)workgroup_size), 1); - local_size[0] = local_size[1] = sqrt_workgroup_size; - } - - /* Some implementations have max size 1 on 2nd dimension. */ - if (local_size[1] > max_work_items[1]) { - local_size[0] = workgroup_size / max_work_items[1]; - local_size[1] = max_work_items[1]; - } - - size_t global_size[2] = {global_size_round_up(local_size[0], w), - global_size_round_up(local_size[1], h)}; - - /* Vertical size of 1 is coming from bake/shade kernels where we should - * not round anything up because otherwise we'll either be doing too - * much work per pixel (if we don't check global ID on Y axis) or will - * be checking for global ID to always have Y of 0. - */ - if (h == 1) { - global_size[h] = 1; - } - - /* run kernel */ - opencl_assert( - clEnqueueNDRangeKernel(cqCommandQueue, kernel, 2, NULL, global_size, NULL, 0, NULL, NULL)); - opencl_assert(clFlush(cqCommandQueue)); -} - -void OpenCLDevice::set_kernel_arg_mem(cl_kernel kernel, cl_uint *narg, const char *name) -{ - cl_mem ptr; - - MemMap::iterator i = mem_map.find(name); - if (i != mem_map.end()) { - ptr = CL_MEM_PTR(i->second); - } - else { - ptr = 0; - } - - opencl_assert(clSetKernelArg(kernel, (*narg)++, sizeof(ptr), (void *)&ptr)); -} - -void OpenCLDevice::set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg) -{ - flush_texture_buffers(); - - memory_manager.set_kernel_arg_buffers(kernel, narg); -} - -void OpenCLDevice::flush_texture_buffers() -{ - if (!textures_need_update) { - return; - } - textures_need_update = false; - - /* Setup slots for textures. */ - int num_slots = 0; - - vector texture_slots; - -# define KERNEL_TEX(type, name) \ - if (textures.find(#name) != textures.end()) { \ - texture_slots.push_back(texture_slot_t(#name, num_slots)); \ - } \ - num_slots++; -# include "kernel/kernel_textures.h" - - int num_data_slots = num_slots; - - foreach (TexturesMap::value_type &tex, textures) { - string name = tex.first; - device_memory *mem = tex.second; - - if (mem->type == MEM_TEXTURE) { - const uint id = ((device_texture *)mem)->slot; - texture_slots.push_back(texture_slot_t(name, num_data_slots + id)); - num_slots = max(num_slots, num_data_slots + id + 1); - } - } - - /* Realloc texture descriptors buffer. */ - memory_manager.free(texture_info); - texture_info.resize(num_slots); - memory_manager.alloc("texture_info", texture_info); - - /* Fill in descriptors */ - foreach (texture_slot_t &slot, texture_slots) { - device_memory *mem = textures[slot.name]; - TextureInfo &info = texture_info[slot.slot]; - - MemoryManager::BufferDescriptor desc = memory_manager.get_descriptor(slot.name); - - if (mem->type == MEM_TEXTURE) { - info = ((device_texture *)mem)->info; - } - else { - memset(&info, 0, sizeof(TextureInfo)); - } - - info.data = desc.offset; - info.cl_buffer = desc.device_buffer; - } - - /* Force write of descriptors. */ - memory_manager.free(texture_info); - memory_manager.alloc("texture_info", texture_info); -} - -void OpenCLDevice::thread_run(DeviceTask &task) -{ - flush_texture_buffers(); - - if (task.type == DeviceTask::RENDER) { - RenderTile tile; - DenoisingTask denoising(this, task); - - /* Allocate buffer for kernel globals */ - device_only_memory kgbuffer(this, "kernel_globals"); - kgbuffer.alloc_to_device(1); - - /* Keep rendering tiles until done. */ - while (task.acquire_tile(this, tile, task.tile_types)) { - if (tile.task == RenderTile::PATH_TRACE) { - assert(tile.task == RenderTile::PATH_TRACE); - scoped_timer timer(&tile.buffers->render_time); - - split_kernel->path_trace(task, tile, kgbuffer, *const_mem_map["__data"]); - - /* Complete kernel execution before release tile. */ - /* This helps in multi-device render; - * The device that reaches the critical-section function - * release_tile waits (stalling other devices from entering - * release_tile) for all kernels to complete. If device1 (a - * slow-render device) reaches release_tile first then it would - * stall device2 (a fast-render device) from proceeding to render - * next tile. - */ - clFinish(cqCommandQueue); - } - else if (tile.task == RenderTile::BAKE) { - bake(task, tile); - } - else if (tile.task == RenderTile::DENOISE) { - tile.sample = tile.start_sample + tile.num_samples; - denoise(tile, denoising); - task.update_progress(&tile, tile.w * tile.h); - } - - task.release_tile(tile); - } - - kgbuffer.free(); - } - else if (task.type == DeviceTask::SHADER) { - shader(task); - } - else if (task.type == DeviceTask::FILM_CONVERT) { - film_convert(task, task.buffer, task.rgba_byte, task.rgba_half); - } - else if (task.type == DeviceTask::DENOISE_BUFFER) { - RenderTile tile; - tile.x = task.x; - tile.y = task.y; - tile.w = task.w; - tile.h = task.h; - tile.buffer = task.buffer; - tile.sample = task.sample + task.num_samples; - tile.num_samples = task.num_samples; - tile.start_sample = task.sample; - tile.offset = task.offset; - tile.stride = task.stride; - tile.buffers = task.buffers; - - DenoisingTask denoising(this, task); - denoise(tile, denoising); - task.update_progress(&tile, tile.w * tile.h); - } -} - -void OpenCLDevice::film_convert(DeviceTask &task, - device_ptr buffer, - device_ptr rgba_byte, - device_ptr rgba_half) -{ - /* cast arguments to cl types */ - cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer); - cl_mem d_rgba = (rgba_byte) ? CL_MEM_PTR(rgba_byte) : CL_MEM_PTR(rgba_half); - cl_mem d_buffer = CL_MEM_PTR(buffer); - cl_int d_x = task.x; - cl_int d_y = task.y; - cl_int d_w = task.w; - cl_int d_h = task.h; - cl_float d_sample_scale = 1.0f / (task.sample + 1); - cl_int d_offset = task.offset; - cl_int d_stride = task.stride; - - cl_kernel ckFilmConvertKernel = (rgba_byte) ? base_program(ustring("convert_to_byte")) : - base_program(ustring("convert_to_half_float")); - - cl_uint start_arg_index = kernel_set_args(ckFilmConvertKernel, 0, d_data, d_rgba, d_buffer); - - set_kernel_arg_buffers(ckFilmConvertKernel, &start_arg_index); - - start_arg_index += kernel_set_args(ckFilmConvertKernel, - start_arg_index, - d_sample_scale, - d_x, - d_y, - d_w, - d_h, - d_offset, - d_stride); - - enqueue_kernel(ckFilmConvertKernel, d_w, d_h); -} - -bool OpenCLDevice::denoising_non_local_means(device_ptr image_ptr, - device_ptr guide_ptr, - device_ptr variance_ptr, - device_ptr out_ptr, - DenoisingTask *task) -{ - int stride = task->buffer.stride; - int w = task->buffer.width; - int h = task->buffer.h; - int r = task->nlm_state.r; - int f = task->nlm_state.f; - float a = task->nlm_state.a; - float k_2 = task->nlm_state.k_2; - - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - int channel_offset = task->nlm_state.is_color ? task->buffer.pass_stride : 0; - - device_sub_ptr difference(task->buffer.temporary_mem, 0, pass_stride * num_shifts); - device_sub_ptr blurDifference( - task->buffer.temporary_mem, pass_stride * num_shifts, pass_stride * num_shifts); - device_sub_ptr weightAccum( - task->buffer.temporary_mem, 2 * pass_stride * num_shifts, pass_stride); - cl_mem weightAccum_mem = CL_MEM_PTR(*weightAccum); - cl_mem difference_mem = CL_MEM_PTR(*difference); - cl_mem blurDifference_mem = CL_MEM_PTR(*blurDifference); - - cl_mem image_mem = CL_MEM_PTR(image_ptr); - cl_mem guide_mem = CL_MEM_PTR(guide_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - cl_mem out_mem = CL_MEM_PTR(out_ptr); - cl_mem scale_mem = NULL; - - mem_zero_kernel(*weightAccum, sizeof(float) * pass_stride); - mem_zero_kernel(out_ptr, sizeof(float) * pass_stride); - - cl_kernel ckNLMCalcDifference = denoising_program(ustring("filter_nlm_calc_difference")); - cl_kernel ckNLMBlur = denoising_program(ustring("filter_nlm_blur")); - cl_kernel ckNLMCalcWeight = denoising_program(ustring("filter_nlm_calc_weight")); - cl_kernel ckNLMUpdateOutput = denoising_program(ustring("filter_nlm_update_output")); - cl_kernel ckNLMNormalize = denoising_program(ustring("filter_nlm_normalize")); - - kernel_set_args(ckNLMCalcDifference, - 0, - guide_mem, - variance_mem, - scale_mem, - difference_mem, - w, - h, - stride, - pass_stride, - r, - channel_offset, - 0, - a, - k_2); - kernel_set_args( - ckNLMBlur, 0, difference_mem, blurDifference_mem, w, h, stride, pass_stride, r, f); - kernel_set_args( - ckNLMCalcWeight, 0, blurDifference_mem, difference_mem, w, h, stride, pass_stride, r, f); - kernel_set_args(ckNLMUpdateOutput, - 0, - blurDifference_mem, - image_mem, - out_mem, - weightAccum_mem, - w, - h, - stride, - pass_stride, - channel_offset, - r, - f); - - enqueue_kernel(ckNLMCalcDifference, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMCalcWeight, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMUpdateOutput, w * h, num_shifts, true); - - kernel_set_args(ckNLMNormalize, 0, out_mem, weightAccum_mem, w, h, stride); - enqueue_kernel(ckNLMNormalize, w, h); - - return true; -} - -bool OpenCLDevice::denoising_construct_transform(DenoisingTask *task) -{ - cl_mem buffer_mem = CL_MEM_PTR(task->buffer.mem.device_pointer); - cl_mem transform_mem = CL_MEM_PTR(task->storage.transform.device_pointer); - cl_mem rank_mem = CL_MEM_PTR(task->storage.rank.device_pointer); - cl_mem tile_info_mem = CL_MEM_PTR(task->tile_info_mem.device_pointer); - - char use_time = task->buffer.use_time ? 1 : 0; - - cl_kernel ckFilterConstructTransform = denoising_program(ustring("filter_construct_transform")); - - int arg_ofs = kernel_set_args(ckFilterConstructTransform, 0, buffer_mem, tile_info_mem); - cl_mem buffers[9]; - for (int i = 0; i < 9; i++) { - buffers[i] = CL_MEM_PTR(task->tile_info->buffers[i]); - arg_ofs += kernel_set_args(ckFilterConstructTransform, arg_ofs, buffers[i]); - } - kernel_set_args(ckFilterConstructTransform, - arg_ofs, - transform_mem, - rank_mem, - task->filter_area, - task->rect, - task->buffer.pass_stride, - task->buffer.frame_stride, - use_time, - task->radius, - task->pca_threshold); - - enqueue_kernel(ckFilterConstructTransform, task->storage.w, task->storage.h, 256); - - return true; -} - -bool OpenCLDevice::denoising_accumulate(device_ptr color_ptr, - device_ptr color_variance_ptr, - device_ptr scale_ptr, - int frame, - DenoisingTask *task) -{ - cl_mem color_mem = CL_MEM_PTR(color_ptr); - cl_mem color_variance_mem = CL_MEM_PTR(color_variance_ptr); - cl_mem scale_mem = CL_MEM_PTR(scale_ptr); - - cl_mem buffer_mem = CL_MEM_PTR(task->buffer.mem.device_pointer); - cl_mem transform_mem = CL_MEM_PTR(task->storage.transform.device_pointer); - cl_mem rank_mem = CL_MEM_PTR(task->storage.rank.device_pointer); - cl_mem XtWX_mem = CL_MEM_PTR(task->storage.XtWX.device_pointer); - cl_mem XtWY_mem = CL_MEM_PTR(task->storage.XtWY.device_pointer); - - cl_kernel ckNLMCalcDifference = denoising_program(ustring("filter_nlm_calc_difference")); - cl_kernel ckNLMBlur = denoising_program(ustring("filter_nlm_blur")); - cl_kernel ckNLMCalcWeight = denoising_program(ustring("filter_nlm_calc_weight")); - cl_kernel ckNLMConstructGramian = denoising_program(ustring("filter_nlm_construct_gramian")); - - int w = task->reconstruction_state.source_w; - int h = task->reconstruction_state.source_h; - int stride = task->buffer.stride; - int frame_offset = frame * task->buffer.frame_stride; - int t = task->tile_info->frames[frame]; - char use_time = task->buffer.use_time ? 1 : 0; - - int r = task->radius; - int pass_stride = task->buffer.pass_stride; - int num_shifts = (2 * r + 1) * (2 * r + 1); - - device_sub_ptr difference(task->buffer.temporary_mem, 0, pass_stride * num_shifts); - device_sub_ptr blurDifference( - task->buffer.temporary_mem, pass_stride * num_shifts, pass_stride * num_shifts); - cl_mem difference_mem = CL_MEM_PTR(*difference); - cl_mem blurDifference_mem = CL_MEM_PTR(*blurDifference); - - kernel_set_args(ckNLMCalcDifference, - 0, - color_mem, - color_variance_mem, - scale_mem, - difference_mem, - w, - h, - stride, - pass_stride, - r, - pass_stride, - frame_offset, - 1.0f, - task->nlm_k_2); - kernel_set_args( - ckNLMBlur, 0, difference_mem, blurDifference_mem, w, h, stride, pass_stride, r, 4); - kernel_set_args( - ckNLMCalcWeight, 0, blurDifference_mem, difference_mem, w, h, stride, pass_stride, r, 4); - kernel_set_args(ckNLMConstructGramian, - 0, - t, - blurDifference_mem, - buffer_mem, - transform_mem, - rank_mem, - XtWX_mem, - XtWY_mem, - task->reconstruction_state.filter_window, - w, - h, - stride, - pass_stride, - r, - 4, - frame_offset, - use_time); - - enqueue_kernel(ckNLMCalcDifference, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMCalcWeight, w * h, num_shifts, true); - enqueue_kernel(ckNLMBlur, w * h, num_shifts, true); - enqueue_kernel(ckNLMConstructGramian, w * h, num_shifts, true, 256); - - return true; -} - -bool OpenCLDevice::denoising_solve(device_ptr output_ptr, DenoisingTask *task) -{ - cl_kernel ckFinalize = denoising_program(ustring("filter_finalize")); - - cl_mem output_mem = CL_MEM_PTR(output_ptr); - cl_mem rank_mem = CL_MEM_PTR(task->storage.rank.device_pointer); - cl_mem XtWX_mem = CL_MEM_PTR(task->storage.XtWX.device_pointer); - cl_mem XtWY_mem = CL_MEM_PTR(task->storage.XtWY.device_pointer); - - int w = task->reconstruction_state.source_w; - int h = task->reconstruction_state.source_h; - - kernel_set_args(ckFinalize, - 0, - output_mem, - rank_mem, - XtWX_mem, - XtWY_mem, - task->filter_area, - task->reconstruction_state.buffer_params, - task->render_buffer.samples); - enqueue_kernel(ckFinalize, w, h); - - return true; -} - -bool OpenCLDevice::denoising_combine_halves(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr mean_ptr, - device_ptr variance_ptr, - int r, - int4 rect, - DenoisingTask *task) -{ - cl_mem a_mem = CL_MEM_PTR(a_ptr); - cl_mem b_mem = CL_MEM_PTR(b_ptr); - cl_mem mean_mem = CL_MEM_PTR(mean_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - - cl_kernel ckFilterCombineHalves = denoising_program(ustring("filter_combine_halves")); - - kernel_set_args(ckFilterCombineHalves, 0, mean_mem, variance_mem, a_mem, b_mem, rect, r); - enqueue_kernel(ckFilterCombineHalves, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -bool OpenCLDevice::denoising_divide_shadow(device_ptr a_ptr, - device_ptr b_ptr, - device_ptr sample_variance_ptr, - device_ptr sv_variance_ptr, - device_ptr buffer_variance_ptr, - DenoisingTask *task) -{ - cl_mem a_mem = CL_MEM_PTR(a_ptr); - cl_mem b_mem = CL_MEM_PTR(b_ptr); - cl_mem sample_variance_mem = CL_MEM_PTR(sample_variance_ptr); - cl_mem sv_variance_mem = CL_MEM_PTR(sv_variance_ptr); - cl_mem buffer_variance_mem = CL_MEM_PTR(buffer_variance_ptr); - - cl_mem tile_info_mem = CL_MEM_PTR(task->tile_info_mem.device_pointer); - - cl_kernel ckFilterDivideShadow = denoising_program(ustring("filter_divide_shadow")); - - int arg_ofs = kernel_set_args( - ckFilterDivideShadow, 0, task->render_buffer.samples, tile_info_mem); - cl_mem buffers[9]; - for (int i = 0; i < 9; i++) { - buffers[i] = CL_MEM_PTR(task->tile_info->buffers[i]); - arg_ofs += kernel_set_args(ckFilterDivideShadow, arg_ofs, buffers[i]); - } - kernel_set_args(ckFilterDivideShadow, - arg_ofs, - a_mem, - b_mem, - sample_variance_mem, - sv_variance_mem, - buffer_variance_mem, - task->rect, - task->render_buffer.pass_stride, - task->render_buffer.offset); - enqueue_kernel(ckFilterDivideShadow, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -bool OpenCLDevice::denoising_get_feature(int mean_offset, - int variance_offset, - device_ptr mean_ptr, - device_ptr variance_ptr, - float scale, - DenoisingTask *task) -{ - cl_mem mean_mem = CL_MEM_PTR(mean_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - - cl_mem tile_info_mem = CL_MEM_PTR(task->tile_info_mem.device_pointer); - - cl_kernel ckFilterGetFeature = denoising_program(ustring("filter_get_feature")); - - int arg_ofs = kernel_set_args(ckFilterGetFeature, 0, task->render_buffer.samples, tile_info_mem); - cl_mem buffers[9]; - for (int i = 0; i < 9; i++) { - buffers[i] = CL_MEM_PTR(task->tile_info->buffers[i]); - arg_ofs += kernel_set_args(ckFilterGetFeature, arg_ofs, buffers[i]); - } - kernel_set_args(ckFilterGetFeature, - arg_ofs, - mean_offset, - variance_offset, - mean_mem, - variance_mem, - scale, - task->rect, - task->render_buffer.pass_stride, - task->render_buffer.offset); - enqueue_kernel(ckFilterGetFeature, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -bool OpenCLDevice::denoising_write_feature(int out_offset, - device_ptr from_ptr, - device_ptr buffer_ptr, - DenoisingTask *task) -{ - cl_mem from_mem = CL_MEM_PTR(from_ptr); - cl_mem buffer_mem = CL_MEM_PTR(buffer_ptr); - - cl_kernel ckFilterWriteFeature = denoising_program(ustring("filter_write_feature")); - - kernel_set_args(ckFilterWriteFeature, - 0, - task->render_buffer.samples, - task->reconstruction_state.buffer_params, - task->filter_area, - from_mem, - buffer_mem, - out_offset, - task->rect); - enqueue_kernel(ckFilterWriteFeature, task->filter_area.z, task->filter_area.w); - - return true; -} - -bool OpenCLDevice::denoising_detect_outliers(device_ptr image_ptr, - device_ptr variance_ptr, - device_ptr depth_ptr, - device_ptr output_ptr, - DenoisingTask *task) -{ - cl_mem image_mem = CL_MEM_PTR(image_ptr); - cl_mem variance_mem = CL_MEM_PTR(variance_ptr); - cl_mem depth_mem = CL_MEM_PTR(depth_ptr); - cl_mem output_mem = CL_MEM_PTR(output_ptr); - - cl_kernel ckFilterDetectOutliers = denoising_program(ustring("filter_detect_outliers")); - - kernel_set_args(ckFilterDetectOutliers, - 0, - image_mem, - variance_mem, - depth_mem, - output_mem, - task->rect, - task->buffer.pass_stride); - enqueue_kernel(ckFilterDetectOutliers, task->rect.z - task->rect.x, task->rect.w - task->rect.y); - - return true; -} - -void OpenCLDevice::denoise(RenderTile &rtile, DenoisingTask &denoising) -{ - denoising.functions.construct_transform = function_bind( - &OpenCLDevice::denoising_construct_transform, this, &denoising); - denoising.functions.accumulate = function_bind( - &OpenCLDevice::denoising_accumulate, this, _1, _2, _3, _4, &denoising); - denoising.functions.solve = function_bind(&OpenCLDevice::denoising_solve, this, _1, &denoising); - denoising.functions.divide_shadow = function_bind( - &OpenCLDevice::denoising_divide_shadow, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.non_local_means = function_bind( - &OpenCLDevice::denoising_non_local_means, this, _1, _2, _3, _4, &denoising); - denoising.functions.combine_halves = function_bind( - &OpenCLDevice::denoising_combine_halves, this, _1, _2, _3, _4, _5, _6, &denoising); - denoising.functions.get_feature = function_bind( - &OpenCLDevice::denoising_get_feature, this, _1, _2, _3, _4, _5, &denoising); - denoising.functions.write_feature = function_bind( - &OpenCLDevice::denoising_write_feature, this, _1, _2, _3, &denoising); - denoising.functions.detect_outliers = function_bind( - &OpenCLDevice::denoising_detect_outliers, this, _1, _2, _3, _4, &denoising); - - denoising.filter_area = make_int4(rtile.x, rtile.y, rtile.w, rtile.h); - denoising.render_buffer.samples = rtile.sample; - denoising.buffer.gpu_temporary_mem = true; - - denoising.run_denoising(rtile); -} - -void OpenCLDevice::shader(DeviceTask &task) -{ - /* cast arguments to cl types */ - cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer); - cl_mem d_input = CL_MEM_PTR(task.shader_input); - cl_mem d_output = CL_MEM_PTR(task.shader_output); - cl_int d_shader_eval_type = task.shader_eval_type; - cl_int d_shader_filter = task.shader_filter; - cl_int d_shader_x = task.shader_x; - cl_int d_shader_w = task.shader_w; - cl_int d_offset = task.offset; - - OpenCLDevice::OpenCLProgram *program = &background_program; - if (task.shader_eval_type == SHADER_EVAL_DISPLACE) { - program = &displace_program; - } - program->wait_for_availability(); - cl_kernel kernel = (*program)(); - - cl_uint start_arg_index = kernel_set_args(kernel, 0, d_data, d_input, d_output); - - set_kernel_arg_buffers(kernel, &start_arg_index); - - start_arg_index += kernel_set_args(kernel, start_arg_index, d_shader_eval_type); - if (task.shader_eval_type >= SHADER_EVAL_BAKE) { - start_arg_index += kernel_set_args(kernel, start_arg_index, d_shader_filter); - } - start_arg_index += kernel_set_args(kernel, start_arg_index, d_shader_x, d_shader_w, d_offset); - - for (int sample = 0; sample < task.num_samples; sample++) { - - if (task.get_cancel()) - break; - - kernel_set_args(kernel, start_arg_index, sample); - - enqueue_kernel(kernel, task.shader_w, 1); - - clFinish(cqCommandQueue); - - task.update_progress(NULL); - } -} - -void OpenCLDevice::bake(DeviceTask &task, RenderTile &rtile) -{ - scoped_timer timer(&rtile.buffers->render_time); - - /* Cast arguments to cl types. */ - cl_mem d_data = CL_MEM_PTR(const_mem_map["__data"]->device_pointer); - cl_mem d_buffer = CL_MEM_PTR(rtile.buffer); - cl_int d_x = rtile.x; - cl_int d_y = rtile.y; - cl_int d_w = rtile.w; - cl_int d_h = rtile.h; - cl_int d_offset = rtile.offset; - cl_int d_stride = rtile.stride; - - bake_program.wait_for_availability(); - cl_kernel kernel = bake_program(); - - cl_uint start_arg_index = kernel_set_args(kernel, 0, d_data, d_buffer); - - set_kernel_arg_buffers(kernel, &start_arg_index); - - start_arg_index += kernel_set_args( - kernel, start_arg_index, d_x, d_y, d_w, d_h, d_offset, d_stride); - - int start_sample = rtile.start_sample; - int end_sample = rtile.start_sample + rtile.num_samples; - - for (int sample = start_sample; sample < end_sample; sample++) { - if (task.get_cancel()) { - if (task.need_finish_queue == false) - break; - } - - kernel_set_args(kernel, start_arg_index, sample); - - enqueue_kernel(kernel, d_w, d_h); - clFinish(cqCommandQueue); - - rtile.sample = sample + 1; - - task.update_progress(&rtile, rtile.w * rtile.h); - } -} - -static bool kernel_build_opencl_2(cl_device_id cdDevice) -{ - /* Build with OpenCL 2.0 if available, this improves performance - * with AMD OpenCL drivers on Windows and Linux (legacy drivers). - * Note that OpenCL selects the highest 1.x version by default, - * only for 2.0 do we need the explicit compiler flag. */ - int version_major, version_minor; - if (OpenCLInfo::get_device_version(cdDevice, &version_major, &version_minor)) { - if (version_major >= 2) { - /* This appears to trigger a driver bug in Radeon RX cards with certain - * driver version, so don't use OpenCL 2.0 for those. */ - string device_name = OpenCLInfo::get_readable_device_name(cdDevice); - if (string_startswith(device_name, "Radeon RX 4") || - string_startswith(device_name, "Radeon (TM) RX 4") || - string_startswith(device_name, "Radeon RX 5") || - string_startswith(device_name, "Radeon (TM) RX 5")) { - char version[256] = ""; - int driver_major, driver_minor; - clGetDeviceInfo(cdDevice, CL_DEVICE_VERSION, sizeof(version), &version, NULL); - if (sscanf(version, "OpenCL 2.0 AMD-APP (%d.%d)", &driver_major, &driver_minor) == 2) { - return !(driver_major == 3075 && driver_minor <= 12); - } - } - - return true; - } - } - - return false; -} - -string OpenCLDevice::kernel_build_options(const string *debug_src) -{ - string build_options = "-cl-no-signed-zeros -cl-mad-enable "; - - if (kernel_build_opencl_2(cdDevice)) { - build_options += "-cl-std=CL2.0 "; - } - - if (platform_name == "NVIDIA CUDA") { - build_options += - "-D__KERNEL_OPENCL_NVIDIA__ " - "-cl-nv-maxrregcount=32 " - "-cl-nv-verbose "; - - uint compute_capability_major, compute_capability_minor; - clGetDeviceInfo(cdDevice, - CL_DEVICE_COMPUTE_CAPABILITY_MAJOR_NV, - sizeof(cl_uint), - &compute_capability_major, - NULL); - clGetDeviceInfo(cdDevice, - CL_DEVICE_COMPUTE_CAPABILITY_MINOR_NV, - sizeof(cl_uint), - &compute_capability_minor, - NULL); - - build_options += string_printf("-D__COMPUTE_CAPABILITY__=%u ", - compute_capability_major * 100 + compute_capability_minor * 10); - } - - else if (platform_name == "Apple") - build_options += "-D__KERNEL_OPENCL_APPLE__ "; - - else if (platform_name == "AMD Accelerated Parallel Processing") - build_options += "-D__KERNEL_OPENCL_AMD__ "; - - else if (platform_name == "Intel(R) OpenCL") { - build_options += "-D__KERNEL_OPENCL_INTEL_CPU__ "; - - /* Options for gdb source level kernel debugging. - * this segfaults on linux currently. - */ - if (OpenCLInfo::use_debug() && debug_src) - build_options += "-g -s \"" + *debug_src + "\" "; - } - - if (info.has_half_images) { - build_options += "-D__KERNEL_CL_KHR_FP16__ "; - } - - if (OpenCLInfo::use_debug()) { - build_options += "-D__KERNEL_OPENCL_DEBUG__ "; - } - -# ifdef WITH_NANOVDB - if (info.has_nanovdb) { - build_options += "-DWITH_NANOVDB "; - } -# endif - - return build_options; -} - -/* TODO(sergey): In the future we can use variadic templates, once - * C++0x is allowed. Should allow to clean this up a bit. - */ -int OpenCLDevice::kernel_set_args(cl_kernel kernel, - int start_argument_index, - const ArgumentWrapper &arg1, - const ArgumentWrapper &arg2, - const ArgumentWrapper &arg3, - const ArgumentWrapper &arg4, - const ArgumentWrapper &arg5, - const ArgumentWrapper &arg6, - const ArgumentWrapper &arg7, - const ArgumentWrapper &arg8, - const ArgumentWrapper &arg9, - const ArgumentWrapper &arg10, - const ArgumentWrapper &arg11, - const ArgumentWrapper &arg12, - const ArgumentWrapper &arg13, - const ArgumentWrapper &arg14, - const ArgumentWrapper &arg15, - const ArgumentWrapper &arg16, - const ArgumentWrapper &arg17, - const ArgumentWrapper &arg18, - const ArgumentWrapper &arg19, - const ArgumentWrapper &arg20, - const ArgumentWrapper &arg21, - const ArgumentWrapper &arg22, - const ArgumentWrapper &arg23, - const ArgumentWrapper &arg24, - const ArgumentWrapper &arg25, - const ArgumentWrapper &arg26, - const ArgumentWrapper &arg27, - const ArgumentWrapper &arg28, - const ArgumentWrapper &arg29, - const ArgumentWrapper &arg30, - const ArgumentWrapper &arg31, - const ArgumentWrapper &arg32, - const ArgumentWrapper &arg33) -{ - int current_arg_index = 0; -# define FAKE_VARARG_HANDLE_ARG(arg) \ - do { \ - if (arg.pointer != NULL) { \ - opencl_assert(clSetKernelArg( \ - kernel, start_argument_index + current_arg_index, arg.size, arg.pointer)); \ - ++current_arg_index; \ - } \ - else { \ - return current_arg_index; \ - } \ - } while (false) - FAKE_VARARG_HANDLE_ARG(arg1); - FAKE_VARARG_HANDLE_ARG(arg2); - FAKE_VARARG_HANDLE_ARG(arg3); - FAKE_VARARG_HANDLE_ARG(arg4); - FAKE_VARARG_HANDLE_ARG(arg5); - FAKE_VARARG_HANDLE_ARG(arg6); - FAKE_VARARG_HANDLE_ARG(arg7); - FAKE_VARARG_HANDLE_ARG(arg8); - FAKE_VARARG_HANDLE_ARG(arg9); - FAKE_VARARG_HANDLE_ARG(arg10); - FAKE_VARARG_HANDLE_ARG(arg11); - FAKE_VARARG_HANDLE_ARG(arg12); - FAKE_VARARG_HANDLE_ARG(arg13); - FAKE_VARARG_HANDLE_ARG(arg14); - FAKE_VARARG_HANDLE_ARG(arg15); - FAKE_VARARG_HANDLE_ARG(arg16); - FAKE_VARARG_HANDLE_ARG(arg17); - FAKE_VARARG_HANDLE_ARG(arg18); - FAKE_VARARG_HANDLE_ARG(arg19); - FAKE_VARARG_HANDLE_ARG(arg20); - FAKE_VARARG_HANDLE_ARG(arg21); - FAKE_VARARG_HANDLE_ARG(arg22); - FAKE_VARARG_HANDLE_ARG(arg23); - FAKE_VARARG_HANDLE_ARG(arg24); - FAKE_VARARG_HANDLE_ARG(arg25); - FAKE_VARARG_HANDLE_ARG(arg26); - FAKE_VARARG_HANDLE_ARG(arg27); - FAKE_VARARG_HANDLE_ARG(arg28); - FAKE_VARARG_HANDLE_ARG(arg29); - FAKE_VARARG_HANDLE_ARG(arg30); - FAKE_VARARG_HANDLE_ARG(arg31); - FAKE_VARARG_HANDLE_ARG(arg32); - FAKE_VARARG_HANDLE_ARG(arg33); -# undef FAKE_VARARG_HANDLE_ARG - return current_arg_index; -} - -void OpenCLDevice::release_kernel_safe(cl_kernel kernel) -{ - if (kernel) { - clReleaseKernel(kernel); - } -} - -void OpenCLDevice::release_mem_object_safe(cl_mem mem) -{ - if (mem != NULL) { - clReleaseMemObject(mem); - } -} - -void OpenCLDevice::release_program_safe(cl_program program) -{ - if (program) { - clReleaseProgram(program); - } -} - -/* ** Those guys are for working around some compiler-specific bugs ** */ - -cl_program OpenCLDevice::load_cached_kernel(ustring key, thread_scoped_lock &cache_locker) -{ - return OpenCLCache::get_program(cpPlatform, cdDevice, key, cache_locker); -} - -void OpenCLDevice::store_cached_kernel(cl_program program, - ustring key, - thread_scoped_lock &cache_locker) -{ - OpenCLCache::store_program(cpPlatform, cdDevice, program, key, cache_locker); -} - -Device *opencl_create_split_device(DeviceInfo &info, - Stats &stats, - Profiler &profiler, - bool background) -{ - return new OpenCLDevice(info, stats, profiler, background); -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/opencl/memory_manager.cpp b/intern/cycles/device/opencl/memory_manager.cpp deleted file mode 100644 index 4330e07cb37..00000000000 --- a/intern/cycles/device/opencl/memory_manager.cpp +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "util/util_foreach.h" - -# include "device/opencl/device_opencl.h" -# include "device/opencl/memory_manager.h" - -CCL_NAMESPACE_BEGIN - -void MemoryManager::DeviceBuffer::add_allocation(Allocation &allocation) -{ - allocations.push_back(&allocation); -} - -void MemoryManager::DeviceBuffer::update_device_memory(OpenCLDevice *device) -{ - bool need_realloc = false; - - /* Calculate total size and remove any freed. */ - size_t total_size = 0; - - for (int i = allocations.size() - 1; i >= 0; i--) { - Allocation *allocation = allocations[i]; - - /* Remove allocations that have been freed. */ - if (!allocation->mem || allocation->mem->memory_size() == 0) { - allocation->device_buffer = NULL; - allocation->size = 0; - - allocations.erase(allocations.begin() + i); - - need_realloc = true; - - continue; - } - - /* Get actual size for allocation. */ - size_t alloc_size = align_up(allocation->mem->memory_size(), 16); - - if (allocation->size != alloc_size) { - /* Allocation is either new or resized. */ - allocation->size = alloc_size; - allocation->needs_copy_to_device = true; - - need_realloc = true; - } - - total_size += alloc_size; - } - - /* Always allocate non-empty buffer, NULL pointers cause problems with some drivers. */ - total_size = std::max(total_size, (size_t)16); - - if (need_realloc) { - cl_ulong max_buffer_size; - clGetDeviceInfo( - device->cdDevice, CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), &max_buffer_size, NULL); - - if (total_size > max_buffer_size) { - device->set_error("Scene too complex to fit in available memory."); - return; - } - - device_only_memory *new_buffer = new device_only_memory(device, - "memory manager buffer"); - - new_buffer->alloc_to_device(total_size); - - size_t offset = 0; - - foreach (Allocation *allocation, allocations) { - if (allocation->needs_copy_to_device) { - /* Copy from host to device. */ - opencl_device_assert(device, - clEnqueueWriteBuffer(device->cqCommandQueue, - CL_MEM_PTR(new_buffer->device_pointer), - CL_FALSE, - offset, - allocation->mem->memory_size(), - allocation->mem->host_pointer, - 0, - NULL, - NULL)); - - allocation->needs_copy_to_device = false; - } - else { - /* Fast copy from memory already on device. */ - opencl_device_assert(device, - clEnqueueCopyBuffer(device->cqCommandQueue, - CL_MEM_PTR(buffer->device_pointer), - CL_MEM_PTR(new_buffer->device_pointer), - allocation->desc.offset, - offset, - allocation->mem->memory_size(), - 0, - NULL, - NULL)); - } - - allocation->desc.offset = offset; - offset += allocation->size; - } - - delete buffer; - - buffer = new_buffer; - } - else { - assert(total_size == buffer->data_size); - - size_t offset = 0; - - foreach (Allocation *allocation, allocations) { - if (allocation->needs_copy_to_device) { - /* Copy from host to device. */ - opencl_device_assert(device, - clEnqueueWriteBuffer(device->cqCommandQueue, - CL_MEM_PTR(buffer->device_pointer), - CL_FALSE, - offset, - allocation->mem->memory_size(), - allocation->mem->host_pointer, - 0, - NULL, - NULL)); - - allocation->needs_copy_to_device = false; - } - - offset += allocation->size; - } - } - - /* Not really necessary, but seems to improve responsiveness for some reason. */ - clFinish(device->cqCommandQueue); -} - -void MemoryManager::DeviceBuffer::free(OpenCLDevice *) -{ - buffer->free(); -} - -MemoryManager::DeviceBuffer *MemoryManager::smallest_device_buffer() -{ - DeviceBuffer *smallest = device_buffers; - - foreach (DeviceBuffer &device_buffer, device_buffers) { - if (device_buffer.size < smallest->size) { - smallest = &device_buffer; - } - } - - return smallest; -} - -MemoryManager::MemoryManager(OpenCLDevice *device) : device(device), need_update(false) -{ - foreach (DeviceBuffer &device_buffer, device_buffers) { - device_buffer.buffer = new device_only_memory(device, "memory manager buffer"); - } -} - -void MemoryManager::free() -{ - foreach (DeviceBuffer &device_buffer, device_buffers) { - device_buffer.free(device); - } -} - -void MemoryManager::alloc(const char *name, device_memory &mem) -{ - Allocation &allocation = allocations[name]; - - allocation.mem = &mem; - allocation.needs_copy_to_device = true; - - if (!allocation.device_buffer) { - DeviceBuffer *device_buffer = smallest_device_buffer(); - allocation.device_buffer = device_buffer; - - allocation.desc.device_buffer = device_buffer - device_buffers; - - device_buffer->add_allocation(allocation); - - device_buffer->size += mem.memory_size(); - } - - need_update = true; -} - -bool MemoryManager::free(device_memory &mem) -{ - foreach (AllocationsMap::value_type &value, allocations) { - Allocation &allocation = value.second; - if (allocation.mem == &mem) { - - allocation.device_buffer->size -= mem.memory_size(); - - allocation.mem = NULL; - allocation.needs_copy_to_device = false; - - need_update = true; - return true; - } - } - - return false; -} - -MemoryManager::BufferDescriptor MemoryManager::get_descriptor(string name) -{ - update_device_memory(); - - Allocation &allocation = allocations[name]; - return allocation.desc; -} - -void MemoryManager::update_device_memory() -{ - if (!need_update) { - return; - } - - need_update = false; - - foreach (DeviceBuffer &device_buffer, device_buffers) { - device_buffer.update_device_memory(device); - } -} - -void MemoryManager::set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg) -{ - update_device_memory(); - - foreach (DeviceBuffer &device_buffer, device_buffers) { - if (device_buffer.buffer->device_pointer) { - device->kernel_set_args(kernel, (*narg)++, *device_buffer.buffer); - } - else { - device->kernel_set_args(kernel, (*narg)++); - } - } -} - -CCL_NAMESPACE_END - -#endif /* WITH_OPENCL */ diff --git a/intern/cycles/device/opencl/memory_manager.h b/intern/cycles/device/opencl/memory_manager.h deleted file mode 100644 index 23624f837a6..00000000000 --- a/intern/cycles/device/opencl/memory_manager.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "device/device.h" - -#include "util/util_map.h" -#include "util/util_string.h" -#include "util/util_vector.h" - -#include "clew.h" - -CCL_NAMESPACE_BEGIN - -class OpenCLDevice; - -class MemoryManager { - public: - static const int NUM_DEVICE_BUFFERS = 8; - - struct BufferDescriptor { - uint device_buffer; - cl_ulong offset; - }; - - private: - struct DeviceBuffer; - - struct Allocation { - device_memory *mem; - - DeviceBuffer *device_buffer; - size_t size; /* Size of actual allocation, may be larger than requested. */ - - BufferDescriptor desc; - - bool needs_copy_to_device; - - Allocation() : mem(NULL), device_buffer(NULL), size(0), needs_copy_to_device(false) - { - } - }; - - struct DeviceBuffer { - device_only_memory *buffer; - vector allocations; - size_t size; /* Size of all allocations. */ - - DeviceBuffer() : buffer(NULL), size(0) - { - } - - ~DeviceBuffer() - { - delete buffer; - buffer = NULL; - } - - void add_allocation(Allocation &allocation); - - void update_device_memory(OpenCLDevice *device); - - void free(OpenCLDevice *device); - }; - - OpenCLDevice *device; - - DeviceBuffer device_buffers[NUM_DEVICE_BUFFERS]; - - typedef unordered_map AllocationsMap; - AllocationsMap allocations; - - bool need_update; - - DeviceBuffer *smallest_device_buffer(); - - public: - MemoryManager(OpenCLDevice *device); - - void free(); /* Free all memory. */ - - void alloc(const char *name, device_memory &mem); - bool free(device_memory &mem); - - BufferDescriptor get_descriptor(string name); - - void update_device_memory(); - void set_kernel_arg_buffers(cl_kernel kernel, cl_uint *narg); -}; - -CCL_NAMESPACE_END diff --git a/intern/cycles/device/opencl/opencl_util.cpp b/intern/cycles/device/opencl/opencl_util.cpp deleted file mode 100644 index 3929cf77f15..00000000000 --- a/intern/cycles/device/opencl/opencl_util.cpp +++ /dev/null @@ -1,1326 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_OPENCL - -# include "device/device_intern.h" -# include "device/opencl/device_opencl.h" - -# include "util/util_debug.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_semaphore.h" -# include "util/util_system.h" -# include "util/util_time.h" - -using std::cerr; -using std::endl; - -CCL_NAMESPACE_BEGIN - -OpenCLCache::Slot::ProgramEntry::ProgramEntry() : program(NULL), mutex(NULL) -{ -} - -OpenCLCache::Slot::ProgramEntry::ProgramEntry(const ProgramEntry &rhs) - : program(rhs.program), mutex(NULL) -{ -} - -OpenCLCache::Slot::ProgramEntry::~ProgramEntry() -{ - delete mutex; -} - -OpenCLCache::Slot::Slot() : context_mutex(NULL), context(NULL) -{ -} - -OpenCLCache::Slot::Slot(const Slot &rhs) - : context_mutex(NULL), context(NULL), programs(rhs.programs) -{ -} - -OpenCLCache::Slot::~Slot() -{ - delete context_mutex; -} - -OpenCLCache &OpenCLCache::global_instance() -{ - static OpenCLCache instance; - return instance; -} - -cl_context OpenCLCache::get_context(cl_platform_id platform, - cl_device_id device, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - - pair ins = self.cache.insert( - CacheMap::value_type(PlatformDevicePair(platform, device), Slot())); - - Slot &slot = ins.first->second; - - /* create slot lock only while holding cache lock */ - if (!slot.context_mutex) - slot.context_mutex = new thread_mutex; - - /* need to unlock cache before locking slot, to allow store to complete */ - cache_lock.unlock(); - - /* lock the slot */ - slot_locker = thread_scoped_lock(*slot.context_mutex); - - /* If the thing isn't cached */ - if (slot.context == NULL) { - /* return with the caller's lock holder holding the slot lock */ - return NULL; - } - - /* the item was already cached, release the slot lock */ - slot_locker.unlock(); - - cl_int ciErr = clRetainContext(slot.context); - assert(ciErr == CL_SUCCESS); - (void)ciErr; - - return slot.context; -} - -cl_program OpenCLCache::get_program(cl_platform_id platform, - cl_device_id device, - ustring key, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - - pair ins = self.cache.insert( - CacheMap::value_type(PlatformDevicePair(platform, device), Slot())); - - Slot &slot = ins.first->second; - - pair ins2 = slot.programs.insert( - Slot::EntryMap::value_type(key, Slot::ProgramEntry())); - - Slot::ProgramEntry &entry = ins2.first->second; - - /* create slot lock only while holding cache lock */ - if (!entry.mutex) - entry.mutex = new thread_mutex; - - /* need to unlock cache before locking slot, to allow store to complete */ - cache_lock.unlock(); - - /* lock the slot */ - slot_locker = thread_scoped_lock(*entry.mutex); - - /* If the thing isn't cached */ - if (entry.program == NULL) { - /* return with the caller's lock holder holding the slot lock */ - return NULL; - } - - /* the item was already cached, release the slot lock */ - slot_locker.unlock(); - - cl_int ciErr = clRetainProgram(entry.program); - assert(ciErr == CL_SUCCESS); - (void)ciErr; - - return entry.program; -} - -void OpenCLCache::store_context(cl_platform_id platform, - cl_device_id device, - cl_context context, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - assert(device != NULL); - assert(context != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - CacheMap::iterator i = self.cache.find(PlatformDevicePair(platform, device)); - cache_lock.unlock(); - - Slot &slot = i->second; - - /* sanity check */ - assert(i != self.cache.end()); - assert(slot.context == NULL); - - slot.context = context; - - /* unlock the slot */ - slot_locker.unlock(); - - /* increment reference count in OpenCL. - * The caller is going to release the object when done with it. */ - cl_int ciErr = clRetainContext(context); - assert(ciErr == CL_SUCCESS); - (void)ciErr; -} - -void OpenCLCache::store_program(cl_platform_id platform, - cl_device_id device, - cl_program program, - ustring key, - thread_scoped_lock &slot_locker) -{ - assert(platform != NULL); - assert(device != NULL); - assert(program != NULL); - - OpenCLCache &self = global_instance(); - - thread_scoped_lock cache_lock(self.cache_lock); - - CacheMap::iterator i = self.cache.find(PlatformDevicePair(platform, device)); - assert(i != self.cache.end()); - Slot &slot = i->second; - - Slot::EntryMap::iterator i2 = slot.programs.find(key); - assert(i2 != slot.programs.end()); - Slot::ProgramEntry &entry = i2->second; - - assert(entry.program == NULL); - - cache_lock.unlock(); - - entry.program = program; - - /* unlock the slot */ - slot_locker.unlock(); - - /* Increment reference count in OpenCL. - * The caller is going to release the object when done with it. - */ - cl_int ciErr = clRetainProgram(program); - assert(ciErr == CL_SUCCESS); - (void)ciErr; -} - -string OpenCLCache::get_kernel_md5() -{ - OpenCLCache &self = global_instance(); - thread_scoped_lock lock(self.kernel_md5_lock); - - if (self.kernel_md5.empty()) { - self.kernel_md5 = path_files_md5_hash(path_get("source")); - } - return self.kernel_md5; -} - -static string get_program_source(const string &kernel_file) -{ - string source = "#include \"kernel/kernels/opencl/" + kernel_file + "\"\n"; - /* We compile kernels consisting of many files. unfortunately OpenCL - * kernel caches do not seem to recognize changes in included files. - * so we force recompile on changes by adding the md5 hash of all files. - */ - source = path_source_replace_includes(source, path_get("source")); - source += "\n// " + util_md5_string(source) + "\n"; - return source; -} - -OpenCLDevice::OpenCLProgram::OpenCLProgram(OpenCLDevice *device, - const string &program_name, - const string &kernel_file, - const string &kernel_build_options, - bool use_stdout) - : device(device), - program_name(program_name), - kernel_file(kernel_file), - kernel_build_options(kernel_build_options), - use_stdout(use_stdout) -{ - loaded = false; - needs_compiling = true; - program = NULL; -} - -OpenCLDevice::OpenCLProgram::~OpenCLProgram() -{ - release(); -} - -void OpenCLDevice::OpenCLProgram::release() -{ - for (map::iterator kernel = kernels.begin(); kernel != kernels.end(); - ++kernel) { - if (kernel->second) { - clReleaseKernel(kernel->second); - kernel->second = NULL; - } - } - if (program) { - clReleaseProgram(program); - program = NULL; - } -} - -void OpenCLDevice::OpenCLProgram::add_log(const string &msg, bool debug) -{ - if (!use_stdout) { - log += msg + "\n"; - } - else if (!debug) { - printf("%s\n", msg.c_str()); - fflush(stdout); - } - else { - VLOG(2) << msg; - } -} - -void OpenCLDevice::OpenCLProgram::add_error(const string &msg) -{ - if (use_stdout) { - fprintf(stderr, "%s\n", msg.c_str()); - } - if (error_msg == "") { - error_msg += "\n"; - } - error_msg += msg; -} - -void OpenCLDevice::OpenCLProgram::add_kernel(ustring name) -{ - if (!kernels.count(name)) { - kernels[name] = NULL; - } -} - -bool OpenCLDevice::OpenCLProgram::build_kernel(const string *debug_src) -{ - string build_options; - build_options = device->kernel_build_options(debug_src) + kernel_build_options; - - VLOG(1) << "Build options passed to clBuildProgram: '" << build_options << "'."; - cl_int ciErr = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL); - - /* show warnings even if build is successful */ - size_t ret_val_size = 0; - - clGetProgramBuildInfo(program, device->cdDevice, CL_PROGRAM_BUILD_LOG, 0, NULL, &ret_val_size); - - if (ciErr != CL_SUCCESS) { - add_error(string("OpenCL build failed with error ") + clewErrorString(ciErr) + - ", errors in console."); - } - - if (ret_val_size > 1) { - vector build_log(ret_val_size + 1); - clGetProgramBuildInfo( - program, device->cdDevice, CL_PROGRAM_BUILD_LOG, ret_val_size, &build_log[0], NULL); - - build_log[ret_val_size] = '\0'; - /* Skip meaningless empty output from the NVidia compiler. */ - if (!(ret_val_size == 2 && build_log[0] == '\n')) { - add_log(string("OpenCL program ") + program_name + " build output: " + string(&build_log[0]), - ciErr == CL_SUCCESS); - } - } - - return (ciErr == CL_SUCCESS); -} - -bool OpenCLDevice::OpenCLProgram::compile_kernel(const string *debug_src) -{ - string source = get_program_source(kernel_file); - - if (debug_src) { - path_write_text(*debug_src, source); - } - - size_t source_len = source.size(); - const char *source_str = source.c_str(); - cl_int ciErr; - - program = clCreateProgramWithSource(device->cxContext, 1, &source_str, &source_len, &ciErr); - - if (ciErr != CL_SUCCESS) { - add_error(string("OpenCL program creation failed: ") + clewErrorString(ciErr)); - return false; - } - - double starttime = time_dt(); - add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); - add_log(string("Build flags: ") + kernel_build_options, true); - - if (!build_kernel(debug_src)) - return false; - - double elapsed = time_dt() - starttime; - add_log( - string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), - false); - - return true; -} - -static void escape_python_string(string &str) -{ - /* Escape string to be passed as a Python raw string with '' quotes'. */ - string_replace(str, "'", "\'"); -} - -static int opencl_compile_process_limit() -{ - /* Limit number of concurrent processes compiling, with a heuristic based - * on total physical RAM and estimate of memory usage needed when compiling - * with all Cycles features enabled. - * - * This is somewhat arbitrary as we don't know the actual available RAM or - * how much the kernel compilation will needed depending on the features, but - * better than not limiting at all. */ - static const int64_t GB = 1024LL * 1024LL * 1024LL; - static const int64_t process_memory = 2 * GB; - static const int64_t base_memory = 2 * GB; - static const int64_t system_memory = system_physical_ram(); - static const int64_t process_limit = (system_memory - base_memory) / process_memory; - - return max((int)process_limit, 1); -} - -bool OpenCLDevice::OpenCLProgram::compile_separate(const string &clbin) -{ - /* Construct arguments. */ - vector args; - args.push_back("--background"); - args.push_back("--factory-startup"); - args.push_back("--python-expr"); - - int device_platform_id = device->device_num; - string device_name = device->device_name; - string platform_name = device->platform_name; - string build_options = device->kernel_build_options(NULL) + kernel_build_options; - string kernel_file_escaped = kernel_file; - string clbin_escaped = clbin; - - escape_python_string(device_name); - escape_python_string(platform_name); - escape_python_string(build_options); - escape_python_string(kernel_file_escaped); - escape_python_string(clbin_escaped); - - args.push_back(string_printf( - "import _cycles; _cycles.opencl_compile(r'%d', r'%s', r'%s', r'%s', r'%s', r'%s')", - device_platform_id, - device_name.c_str(), - platform_name.c_str(), - build_options.c_str(), - kernel_file_escaped.c_str(), - clbin_escaped.c_str())); - - /* Limit number of concurrent processes compiling. */ - static thread_counting_semaphore semaphore(opencl_compile_process_limit()); - semaphore.acquire(); - - /* Compile. */ - const double starttime = time_dt(); - add_log(string("Cycles: compiling OpenCL program ") + program_name + "...", false); - add_log(string("Build flags: ") + kernel_build_options, true); - const bool success = system_call_self(args); - const double elapsed = time_dt() - starttime; - - semaphore.release(); - - if (!success || !path_exists(clbin)) { - return false; - } - - add_log( - string_printf("Kernel compilation of %s finished in %.2lfs.", program_name.c_str(), elapsed), - false); - - return load_binary(clbin); -} - -/* Compile opencl kernel. This method is called from the _cycles Python - * module compile kernels. Parameters must match function above. */ -bool device_opencl_compile_kernel(const vector ¶meters) -{ - int device_platform_id = std::stoi(parameters[0]); - const string &device_name = parameters[1]; - const string &platform_name = parameters[2]; - const string &build_options = parameters[3]; - const string &kernel_file = parameters[4]; - const string &binary_path = parameters[5]; - - if (clewInit() != CLEW_SUCCESS) { - return false; - } - - vector usable_devices; - OpenCLInfo::get_usable_devices(&usable_devices); - if (device_platform_id >= usable_devices.size()) { - return false; - } - - OpenCLPlatformDevice &platform_device = usable_devices[device_platform_id]; - if (platform_device.platform_name != platform_name || - platform_device.device_name != device_name) { - return false; - } - - cl_platform_id platform = platform_device.platform_id; - cl_device_id device = platform_device.device_id; - const cl_context_properties context_props[] = { - CL_CONTEXT_PLATFORM, (cl_context_properties)platform, 0, 0}; - - cl_int err; - cl_context context = clCreateContext(context_props, 1, &device, NULL, NULL, &err); - if (err != CL_SUCCESS) { - return false; - } - - string source = get_program_source(kernel_file); - size_t source_len = source.size(); - const char *source_str = source.c_str(); - cl_program program = clCreateProgramWithSource(context, 1, &source_str, &source_len, &err); - bool result = false; - - if (err == CL_SUCCESS) { - err = clBuildProgram(program, 0, NULL, build_options.c_str(), NULL, NULL); - - if (err == CL_SUCCESS) { - size_t size = 0; - clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL); - if (size > 0) { - vector binary(size); - uint8_t *bytes = &binary[0]; - clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t *), &bytes, NULL); - result = path_write_binary(binary_path, binary); - } - } - clReleaseProgram(program); - } - - clReleaseContext(context); - - return result; -} - -bool OpenCLDevice::OpenCLProgram::load_binary(const string &clbin, const string *debug_src) -{ - /* read binary into memory */ - vector binary; - - if (!path_read_binary(clbin, binary)) { - add_error(string_printf("OpenCL failed to read cached binary %s.", clbin.c_str())); - return false; - } - - /* create program */ - cl_int status, ciErr; - size_t size = binary.size(); - const uint8_t *bytes = &binary[0]; - - program = clCreateProgramWithBinary( - device->cxContext, 1, &device->cdDevice, &size, &bytes, &status, &ciErr); - - if (status != CL_SUCCESS || ciErr != CL_SUCCESS) { - add_error(string("OpenCL failed create program from cached binary ") + clbin + ": " + - clewErrorString(status) + " " + clewErrorString(ciErr)); - return false; - } - - if (!build_kernel(debug_src)) - return false; - - return true; -} - -bool OpenCLDevice::OpenCLProgram::save_binary(const string &clbin) -{ - size_t size = 0; - clGetProgramInfo(program, CL_PROGRAM_BINARY_SIZES, sizeof(size_t), &size, NULL); - - if (!size) - return false; - - vector binary(size); - uint8_t *bytes = &binary[0]; - - clGetProgramInfo(program, CL_PROGRAM_BINARIES, sizeof(uint8_t *), &bytes, NULL); - - return path_write_binary(clbin, binary); -} - -bool OpenCLDevice::OpenCLProgram::load() -{ - loaded = false; - string device_md5 = device->device_md5_hash(kernel_build_options); - - /* Try to use cached kernel. */ - thread_scoped_lock cache_locker; - ustring cache_key(program_name + device_md5); - program = device->load_cached_kernel(cache_key, cache_locker); - if (!program) { - add_log(string("OpenCL program ") + program_name + " not found in cache.", true); - - /* need to create source to get md5 */ - string source = get_program_source(kernel_file); - - string basename = "cycles_kernel_" + program_name + "_" + device_md5 + "_" + - util_md5_string(source); - basename = path_cache_get(path_join("kernels", basename)); - string clbin = basename + ".clbin"; - - /* If binary kernel exists already, try use it. */ - if (path_exists(clbin) && load_binary(clbin)) { - /* Kernel loaded from binary, nothing to do. */ - add_log(string("Loaded program from ") + clbin + ".", true); - - /* Cache the program. */ - device->store_cached_kernel(program, cache_key, cache_locker); - } - else { - add_log(string("OpenCL program ") + program_name + " not found on disk.", true); - cache_locker.unlock(); - } - } - - if (program) { - create_kernels(); - loaded = true; - needs_compiling = false; - } - - return loaded; -} - -void OpenCLDevice::OpenCLProgram::compile() -{ - assert(device); - - string device_md5 = device->device_md5_hash(kernel_build_options); - - /* Try to use cached kernel. */ - thread_scoped_lock cache_locker; - ustring cache_key(program_name + device_md5); - program = device->load_cached_kernel(cache_key, cache_locker); - - if (!program) { - - add_log(string("OpenCL program ") + program_name + " not found in cache.", true); - - /* need to create source to get md5 */ - string source = get_program_source(kernel_file); - - string basename = "cycles_kernel_" + program_name + "_" + device_md5 + "_" + - util_md5_string(source); - basename = path_cache_get(path_join("kernels", basename)); - string clbin = basename + ".clbin"; - - /* path to preprocessed source for debugging */ - string clsrc, *debug_src = NULL; - - if (OpenCLInfo::use_debug()) { - clsrc = basename + ".cl"; - debug_src = &clsrc; - } - - if (DebugFlags().running_inside_blender && compile_separate(clbin)) { - add_log(string("Built and loaded program from ") + clbin + ".", true); - loaded = true; - } - else { - if (DebugFlags().running_inside_blender) { - add_log(string("Separate-process building of ") + clbin + - " failed, will fall back to regular building.", - true); - } - - /* If does not exist or loading binary failed, compile kernel. */ - if (!compile_kernel(debug_src)) { - needs_compiling = false; - return; - } - - /* Save binary for reuse. */ - if (!save_binary(clbin)) { - add_log(string("Saving compiled OpenCL kernel to ") + clbin + " failed!", true); - } - } - - /* Cache the program. */ - device->store_cached_kernel(program, cache_key, cache_locker); - } - - create_kernels(); - needs_compiling = false; - loaded = true; -} - -void OpenCLDevice::OpenCLProgram::create_kernels() -{ - for (map::iterator kernel = kernels.begin(); kernel != kernels.end(); - ++kernel) { - assert(kernel->second == NULL); - cl_int ciErr; - string name = "kernel_ocl_" + kernel->first.string(); - kernel->second = clCreateKernel(program, name.c_str(), &ciErr); - if (device->opencl_error(ciErr)) { - add_error(string("Error getting kernel ") + name + " from program " + program_name + ": " + - clewErrorString(ciErr)); - return; - } - } -} - -bool OpenCLDevice::OpenCLProgram::wait_for_availability() -{ - add_log(string("Waiting for availability of ") + program_name + ".", true); - while (needs_compiling) { - time_sleep(0.1); - } - return loaded; -} - -void OpenCLDevice::OpenCLProgram::report_error() -{ - /* If loaded is true, there was no error. */ - if (loaded) - return; - /* if use_stdout is true, the error was already reported. */ - if (use_stdout) - return; - - cerr << error_msg << endl; - if (!compile_output.empty()) { - cerr << "OpenCL kernel build output for " << program_name << ":" << endl; - cerr << compile_output << endl; - } -} - -cl_kernel OpenCLDevice::OpenCLProgram::operator()() -{ - assert(kernels.size() == 1); - return kernels.begin()->second; -} - -cl_kernel OpenCLDevice::OpenCLProgram::operator()(ustring name) -{ - assert(kernels.count(name)); - return kernels[name]; -} - -cl_device_type OpenCLInfo::device_type() -{ - switch (DebugFlags().opencl.device_type) { - case DebugFlags::OpenCL::DEVICE_NONE: - return 0; - case DebugFlags::OpenCL::DEVICE_ALL: - return CL_DEVICE_TYPE_ALL; - case DebugFlags::OpenCL::DEVICE_DEFAULT: - return CL_DEVICE_TYPE_DEFAULT; - case DebugFlags::OpenCL::DEVICE_CPU: - return CL_DEVICE_TYPE_CPU; - case DebugFlags::OpenCL::DEVICE_GPU: - return CL_DEVICE_TYPE_GPU; - case DebugFlags::OpenCL::DEVICE_ACCELERATOR: - return CL_DEVICE_TYPE_ACCELERATOR; - default: - return CL_DEVICE_TYPE_ALL; - } -} - -bool OpenCLInfo::use_debug() -{ - return DebugFlags().opencl.debug; -} - -bool OpenCLInfo::device_supported(const string &platform_name, const cl_device_id device_id) -{ - cl_device_type device_type; - if (!get_device_type(device_id, &device_type)) { - return false; - } - string device_name; - if (!get_device_name(device_id, &device_name)) { - return false; - } - - int driver_major = 0; - int driver_minor = 0; - if (!get_driver_version(device_id, &driver_major, &driver_minor)) { - return false; - } - VLOG(3) << "OpenCL driver version " << driver_major << "." << driver_minor; - - if (getenv("CYCLES_OPENCL_TEST")) { - return true; - } - - /* Allow Intel GPUs on Intel OpenCL platform. */ - if (platform_name.find("Intel") != string::npos) { - if (device_type != CL_DEVICE_TYPE_GPU) { - /* OpenCL on Intel CPU is not an officially supported configuration. - * Use hybrid CPU+GPU rendering to utilize both GPU and CPU. */ - return false; - } - -# ifdef __APPLE__ - /* Apple uses own framework, which can also put Iris onto AMD frame-work. - * This isn't supported configuration. */ - return false; -# else - if (device_name.find("Iris") != string::npos || device_name.find("Xe") != string::npos) { - return true; - } -# endif - } - - if (platform_name == "AMD Accelerated Parallel Processing" && - device_type == CL_DEVICE_TYPE_GPU) { - if (driver_major < 2236) { - VLOG(1) << "AMD driver version " << driver_major << "." << driver_minor << " not supported."; - return false; - } - const char *blacklist[] = {/* GCN 1 */ - "Tahiti", - "Pitcairn", - "Capeverde", - "Oland", - "Hainan", - NULL}; - for (int i = 0; blacklist[i] != NULL; i++) { - if (device_name == blacklist[i]) { - VLOG(1) << "AMD device " << device_name << " not supported"; - return false; - } - } - return true; - } - if (platform_name == "Apple" && device_type == CL_DEVICE_TYPE_GPU) { - return false; - } - return false; -} - -bool OpenCLInfo::platform_version_check(cl_platform_id platform, string *error) -{ - const int req_major = 1, req_minor = 1; - int major, minor; - char version[256]; - clGetPlatformInfo(platform, CL_PLATFORM_VERSION, sizeof(version), &version, NULL); - if (sscanf(version, "OpenCL %d.%d", &major, &minor) < 2) { - if (error != NULL) { - *error = string_printf("OpenCL: failed to parse platform version string (%s).", version); - } - return false; - } - if (!((major == req_major && minor >= req_minor) || (major > req_major))) { - if (error != NULL) { - *error = string_printf( - "OpenCL: platform version 1.1 or later required, found %d.%d", major, minor); - } - return false; - } - if (error != NULL) { - *error = ""; - } - return true; -} - -bool OpenCLInfo::get_device_version(cl_device_id device, int *r_major, int *r_minor, string *error) -{ - char version[256]; - clGetDeviceInfo(device, CL_DEVICE_OPENCL_C_VERSION, sizeof(version), &version, NULL); - if (sscanf(version, "OpenCL C %d.%d", r_major, r_minor) < 2) { - if (error != NULL) { - *error = string_printf("OpenCL: failed to parse OpenCL C version string (%s).", version); - } - return false; - } - if (error != NULL) { - *error = ""; - } - return true; -} - -bool OpenCLInfo::device_version_check(cl_device_id device, string *error) -{ - const int req_major = 1, req_minor = 1; - int major, minor; - if (!get_device_version(device, &major, &minor, error)) { - return false; - } - - if (!((major == req_major && minor >= req_minor) || (major > req_major))) { - if (error != NULL) { - *error = string_printf("OpenCL: C version 1.1 or later required, found %d.%d", major, minor); - } - return false; - } - if (error != NULL) { - *error = ""; - } - return true; -} - -string OpenCLInfo::get_hardware_id(const string &platform_name, cl_device_id device_id) -{ - if (platform_name == "AMD Accelerated Parallel Processing" || platform_name == "Apple") { - /* Use cl_amd_device_topology extension. */ - cl_char topology[24]; - if (clGetDeviceInfo(device_id, 0x4037, sizeof(topology), topology, NULL) == CL_SUCCESS && - topology[0] == 1) { - return string_printf("%02x:%02x.%01x", - (unsigned int)topology[21], - (unsigned int)topology[22], - (unsigned int)topology[23]); - } - } - else if (platform_name == "NVIDIA CUDA") { - /* Use two undocumented options of the cl_nv_device_attribute_query extension. */ - cl_int bus_id, slot_id; - if (clGetDeviceInfo(device_id, 0x4008, sizeof(cl_int), &bus_id, NULL) == CL_SUCCESS && - clGetDeviceInfo(device_id, 0x4009, sizeof(cl_int), &slot_id, NULL) == CL_SUCCESS) { - return string_printf("%02x:%02x.%01x", - (unsigned int)(bus_id), - (unsigned int)(slot_id >> 3), - (unsigned int)(slot_id & 0x7)); - } - } - /* No general way to get a hardware ID from OpenCL => give up. */ - return ""; -} - -void OpenCLInfo::get_usable_devices(vector *usable_devices) -{ - const cl_device_type device_type = OpenCLInfo::device_type(); - static bool first_time = true; -# define FIRST_VLOG(severity) \ - if (first_time) \ - VLOG(severity) - - usable_devices->clear(); - - if (device_type == 0) { - FIRST_VLOG(2) << "OpenCL devices are forced to be disabled."; - first_time = false; - return; - } - - cl_int error; - vector device_ids; - vector platform_ids; - - /* Get platforms. */ - if (!get_platforms(&platform_ids, &error)) { - FIRST_VLOG(2) << "Error fetching platforms:" << string(clewErrorString(error)); - first_time = false; - return; - } - if (platform_ids.size() == 0) { - FIRST_VLOG(2) << "No OpenCL platforms were found."; - first_time = false; - return; - } - /* Devices are numbered consecutively across platforms. */ - for (int platform = 0; platform < platform_ids.size(); platform++) { - cl_platform_id platform_id = platform_ids[platform]; - string platform_name; - if (!get_platform_name(platform_id, &platform_name)) { - FIRST_VLOG(2) << "Failed to get platform name, ignoring."; - continue; - } - FIRST_VLOG(2) << "Enumerating devices for platform " << platform_name << "."; - if (!platform_version_check(platform_id)) { - FIRST_VLOG(2) << "Ignoring platform " << platform_name - << " due to too old compiler version."; - continue; - } - if (!get_platform_devices(platform_id, device_type, &device_ids, &error)) { - FIRST_VLOG(2) << "Ignoring platform " << platform_name - << ", failed to fetch of devices: " << string(clewErrorString(error)); - continue; - } - if (device_ids.size() == 0) { - FIRST_VLOG(2) << "Ignoring platform " << platform_name << ", it has no devices."; - continue; - } - for (int num = 0; num < device_ids.size(); num++) { - const cl_device_id device_id = device_ids[num]; - string device_name; - if (!get_device_name(device_id, &device_name, &error)) { - FIRST_VLOG(2) << "Failed to fetch device name: " << string(clewErrorString(error)) - << ", ignoring."; - continue; - } - if (!device_version_check(device_id)) { - FIRST_VLOG(2) << "Ignoring device " << device_name << " due to old compiler version."; - continue; - } - if (device_supported(platform_name, device_id)) { - cl_device_type device_type; - if (!get_device_type(device_id, &device_type, &error)) { - FIRST_VLOG(2) << "Ignoring device " << device_name - << ", failed to fetch device type:" << string(clewErrorString(error)); - continue; - } - string readable_device_name = get_readable_device_name(device_id); - if (readable_device_name != device_name) { - FIRST_VLOG(2) << "Using more readable device name: " << readable_device_name; - } - FIRST_VLOG(2) << "Adding new device " << readable_device_name << "."; - string hardware_id = get_hardware_id(platform_name, device_id); - string device_extensions = get_device_extensions(device_id); - usable_devices->push_back(OpenCLPlatformDevice(platform_id, - platform_name, - device_id, - device_type, - readable_device_name, - hardware_id, - device_extensions)); - } - else { - FIRST_VLOG(2) << "Ignoring device " << device_name << ", not officially supported yet."; - } - } - } - first_time = false; -} - -bool OpenCLInfo::get_platforms(vector *platform_ids, cl_int *error) -{ - /* Reset from possible previous state. */ - platform_ids->resize(0); - cl_uint num_platforms; - if (!get_num_platforms(&num_platforms, error)) { - return false; - } - /* Get actual platforms. */ - cl_int err; - platform_ids->resize(num_platforms); - if ((err = clGetPlatformIDs(num_platforms, &platform_ids->at(0), NULL)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -vector OpenCLInfo::get_platforms() -{ - vector platform_ids; - get_platforms(&platform_ids); - return platform_ids; -} - -bool OpenCLInfo::get_num_platforms(cl_uint *num_platforms, cl_int *error) -{ - cl_int err; - if ((err = clGetPlatformIDs(0, NULL, num_platforms)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *num_platforms = 0; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -cl_uint OpenCLInfo::get_num_platforms() -{ - cl_uint num_platforms; - if (!get_num_platforms(&num_platforms)) { - return 0; - } - return num_platforms; -} - -bool OpenCLInfo::get_platform_name(cl_platform_id platform_id, string *platform_name) -{ - char buffer[256]; - if (clGetPlatformInfo(platform_id, CL_PLATFORM_NAME, sizeof(buffer), &buffer, NULL) != - CL_SUCCESS) { - *platform_name = ""; - return false; - } - *platform_name = buffer; - return true; -} - -string OpenCLInfo::get_platform_name(cl_platform_id platform_id) -{ - string platform_name; - if (!get_platform_name(platform_id, &platform_name)) { - return ""; - } - return platform_name; -} - -bool OpenCLInfo::get_num_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - cl_uint *num_devices, - cl_int *error) -{ - cl_int err; - if ((err = clGetDeviceIDs(platform_id, device_type, 0, NULL, num_devices)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *num_devices = 0; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -cl_uint OpenCLInfo::get_num_platform_devices(cl_platform_id platform_id, - cl_device_type device_type) -{ - cl_uint num_devices; - if (!get_num_platform_devices(platform_id, device_type, &num_devices)) { - return 0; - } - return num_devices; -} - -bool OpenCLInfo::get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type, - vector *device_ids, - cl_int *error) -{ - /* Reset from possible previous state. */ - device_ids->resize(0); - /* Get number of devices to pre-allocate memory. */ - cl_uint num_devices; - if (!get_num_platform_devices(platform_id, device_type, &num_devices, error)) { - return false; - } - /* Get actual device list. */ - device_ids->resize(num_devices); - cl_int err; - if ((err = clGetDeviceIDs(platform_id, device_type, num_devices, &device_ids->at(0), NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -vector OpenCLInfo::get_platform_devices(cl_platform_id platform_id, - cl_device_type device_type) -{ - vector devices; - get_platform_devices(platform_id, device_type, &devices); - return devices; -} - -bool OpenCLInfo::get_device_name(cl_device_id device_id, string *device_name, cl_int *error) -{ - char buffer[1024]; - cl_int err; - if ((err = clGetDeviceInfo(device_id, CL_DEVICE_NAME, sizeof(buffer), &buffer, NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_name = ""; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - *device_name = buffer; - return true; -} - -string OpenCLInfo::get_device_name(cl_device_id device_id) -{ - string device_name; - if (!get_device_name(device_id, &device_name)) { - return ""; - } - return device_name; -} - -bool OpenCLInfo::get_device_extensions(cl_device_id device_id, - string *device_extensions, - cl_int *error) -{ - size_t extension_length = 0; - cl_int err; - /* Determine the size of the extension string. */ - if ((err = clGetDeviceInfo(device_id, CL_DEVICE_EXTENSIONS, 0, 0, &extension_length)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_extensions = ""; - return false; - } - vector buffer(extension_length); - if ((err = clGetDeviceInfo( - device_id, CL_DEVICE_EXTENSIONS, extension_length, buffer.data(), NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_extensions = ""; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - *device_extensions = string(buffer.data()); - return true; -} - -string OpenCLInfo::get_device_extensions(cl_device_id device_id) -{ - string device_extensions; - if (!get_device_extensions(device_id, &device_extensions)) { - return ""; - } - return device_extensions; -} - -bool OpenCLInfo::get_device_type(cl_device_id device_id, - cl_device_type *device_type, - cl_int *error) -{ - cl_int err; - if ((err = clGetDeviceInfo( - device_id, CL_DEVICE_TYPE, sizeof(cl_device_type), device_type, NULL)) != CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - *device_type = 0; - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - return true; -} - -cl_device_type OpenCLInfo::get_device_type(cl_device_id device_id) -{ - cl_device_type device_type; - if (!get_device_type(device_id, &device_type)) { - return 0; - } - return device_type; -} - -string OpenCLInfo::get_readable_device_name(cl_device_id device_id) -{ - string name = ""; - char board_name[1024]; - size_t length = 0; - if (clGetDeviceInfo( - device_id, CL_DEVICE_BOARD_NAME_AMD, sizeof(board_name), &board_name, &length) == - CL_SUCCESS) { - if (length != 0 && board_name[0] != '\0') { - name = board_name; - } - } - - /* Fallback to standard device name API. */ - if (name.empty()) { - name = get_device_name(device_id); - } - - /* Special exception for AMD Vega, need to be able to tell - * Vega 56 from 64 apart. - */ - if (name == "Radeon RX Vega") { - cl_int max_compute_units = 0; - if (clGetDeviceInfo(device_id, - CL_DEVICE_MAX_COMPUTE_UNITS, - sizeof(max_compute_units), - &max_compute_units, - NULL) == CL_SUCCESS) { - name += " " + to_string(max_compute_units); - } - } - - /* Distinguish from our native CPU device. */ - if (get_device_type(device_id) & CL_DEVICE_TYPE_CPU) { - name += " (OpenCL)"; - } - - return name; -} - -bool OpenCLInfo::get_driver_version(cl_device_id device_id, int *major, int *minor, cl_int *error) -{ - char buffer[1024]; - cl_int err; - if ((err = clGetDeviceInfo(device_id, CL_DRIVER_VERSION, sizeof(buffer), &buffer, NULL)) != - CL_SUCCESS) { - if (error != NULL) { - *error = err; - } - return false; - } - if (error != NULL) { - *error = CL_SUCCESS; - } - if (sscanf(buffer, "%d.%d", major, minor) < 2) { - VLOG(1) << string_printf("OpenCL: failed to parse driver version string (%s).", buffer); - return false; - } - return true; -} - -int OpenCLInfo::mem_sub_ptr_alignment(cl_device_id device_id) -{ - int base_align_bits; - if (clGetDeviceInfo( - device_id, CL_DEVICE_MEM_BASE_ADDR_ALIGN, sizeof(int), &base_align_bits, NULL) == - CL_SUCCESS) { - return base_align_bits / 8; - } - return 1; -} - -CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/device/optix/device.cpp b/intern/cycles/device/optix/device.cpp new file mode 100644 index 00000000000..a00169e719f --- /dev/null +++ b/intern/cycles/device/optix/device.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2019, NVIDIA Corporation. + * Copyright 2019, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/optix/device.h" + +#include "device/cuda/device.h" +#include "device/optix/device_impl.h" + +#include "util/log.h" + +#ifdef WITH_OPTIX +# include +#endif + +CCL_NAMESPACE_BEGIN + +bool device_optix_init() +{ +#ifdef WITH_OPTIX + if (g_optixFunctionTable.optixDeviceContextCreate != NULL) { + /* Already initialized function table. */ + return true; + } + + /* Need to initialize CUDA as well. */ + if (!device_cuda_init()) { + return false; + } + + const OptixResult result = optixInit(); + + if (result == OPTIX_ERROR_UNSUPPORTED_ABI_VERSION) { + VLOG(1) << "OptiX initialization failed because the installed NVIDIA driver is too old. " + "Please update to the latest driver first!"; + return false; + } + else if (result != OPTIX_SUCCESS) { + VLOG(1) << "OptiX initialization failed with error code " << (unsigned int)result; + return false; + } + + /* Loaded OptiX successfully! */ + return true; +#else + return false; +#endif +} + +void device_optix_info(const vector &cuda_devices, vector &devices) +{ +#ifdef WITH_OPTIX + devices.reserve(cuda_devices.size()); + + /* Simply add all supported CUDA devices as OptiX devices again. */ + for (DeviceInfo info : cuda_devices) { + assert(info.type == DEVICE_CUDA); + + int major; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, info.num); + if (major < 5) { + /* Only Maxwell and up are supported by OptiX. */ + continue; + } + + info.type = DEVICE_OPTIX; + info.id += "_OptiX"; + info.denoisers |= DENOISER_OPTIX; + + devices.push_back(info); + } +#else + (void)cuda_devices; + (void)devices; +#endif +} + +Device *device_optix_create(const DeviceInfo &info, Stats &stats, Profiler &profiler) +{ +#ifdef WITH_OPTIX + return new OptiXDevice(info, stats, profiler); +#else + (void)info; + (void)stats; + (void)profiler; + + LOG(FATAL) << "Request to create OptiX device without compiled-in support. Should never happen."; + + return nullptr; +#endif +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/optix/device.h b/intern/cycles/device/optix/device.h new file mode 100644 index 00000000000..dd60a7aa6e2 --- /dev/null +++ b/intern/cycles/device/optix/device.h @@ -0,0 +1,35 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/string.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class DeviceInfo; +class Profiler; +class Stats; + +bool device_optix_init(); + +Device *device_optix_create(const DeviceInfo &info, Stats &stats, Profiler &profiler); + +void device_optix_info(const vector &cuda_devices, vector &devices); + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/optix/device_impl.cpp b/intern/cycles/device/optix/device_impl.cpp new file mode 100644 index 00000000000..9b9a5ac0de7 --- /dev/null +++ b/intern/cycles/device/optix/device_impl.cpp @@ -0,0 +1,1591 @@ +/* + * Copyright 2019, NVIDIA Corporation. + * Copyright 2019, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_OPTIX + +# include "device/optix/device_impl.h" + +# include "bvh/bvh.h" +# include "bvh/optix.h" + +# include "integrator/pass_accessor_gpu.h" + +# include "scene/hair.h" +# include "scene/mesh.h" +# include "scene/object.h" +# include "scene/pass.h" +# include "scene/scene.h" + +# include "util/debug.h" +# include "util/log.h" +# include "util/md5.h" +# include "util/path.h" +# include "util/progress.h" +# include "util/time.h" + +# undef __KERNEL_CPU__ +# define __KERNEL_OPTIX__ +# include "kernel/device/optix/globals.h" + +CCL_NAMESPACE_BEGIN + +OptiXDevice::Denoiser::Denoiser(OptiXDevice *device) + : device(device), queue(device), state(device, "__denoiser_state") +{ +} + +OptiXDevice::Denoiser::~Denoiser() +{ + const CUDAContextScope scope(device); + if (optix_denoiser != nullptr) { + optixDenoiserDestroy(optix_denoiser); + } +} + +OptiXDevice::OptiXDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler) + : CUDADevice(info, stats, profiler), + sbt_data(this, "__sbt", MEM_READ_ONLY), + launch_params(this, "__params"), + denoiser_(this) +{ + /* Make the CUDA context current. */ + if (!cuContext) { + /* Do not initialize if CUDA context creation failed already. */ + return; + } + const CUDAContextScope scope(this); + + /* Create OptiX context for this device. */ + OptixDeviceContextOptions options = {}; +# ifdef WITH_CYCLES_LOGGING + options.logCallbackLevel = 4; /* Fatal = 1, Error = 2, Warning = 3, Print = 4. */ + options.logCallbackFunction = [](unsigned int level, const char *, const char *message, void *) { + switch (level) { + case 1: + LOG_IF(FATAL, VLOG_IS_ON(1)) << message; + break; + case 2: + LOG_IF(ERROR, VLOG_IS_ON(1)) << message; + break; + case 3: + LOG_IF(WARNING, VLOG_IS_ON(1)) << message; + break; + case 4: + LOG_IF(INFO, VLOG_IS_ON(1)) << message; + break; + } + }; +# endif + if (DebugFlags().optix.use_debug) { + VLOG(1) << "Using OptiX debug mode."; + options.validationMode = OPTIX_DEVICE_CONTEXT_VALIDATION_MODE_ALL; + } + optix_assert(optixDeviceContextCreate(cuContext, &options, &context)); +# ifdef WITH_CYCLES_LOGGING + optix_assert(optixDeviceContextSetLogCallback( + context, options.logCallbackFunction, options.logCallbackData, options.logCallbackLevel)); +# endif + + /* Fix weird compiler bug that assigns wrong size. */ + launch_params.data_elements = sizeof(KernelParamsOptiX); + + /* Allocate launch parameter buffer memory on device. */ + launch_params.alloc_to_device(1); +} + +OptiXDevice::~OptiXDevice() +{ + /* Make CUDA context current. */ + const CUDAContextScope scope(this); + + free_bvh_memory_delayed(); + + sbt_data.free(); + texture_info.free(); + launch_params.free(); + + /* Unload modules. */ + if (optix_module != NULL) { + optixModuleDestroy(optix_module); + } + for (unsigned int i = 0; i < 2; ++i) { + if (builtin_modules[i] != NULL) { + optixModuleDestroy(builtin_modules[i]); + } + } + for (unsigned int i = 0; i < NUM_PIPELINES; ++i) { + if (pipelines[i] != NULL) { + optixPipelineDestroy(pipelines[i]); + } + } + + optixDeviceContextDestroy(context); +} + +unique_ptr OptiXDevice::gpu_queue_create() +{ + return make_unique(this); +} + +BVHLayoutMask OptiXDevice::get_bvh_layout_mask() const +{ + /* OptiX has its own internal acceleration structure format. */ + return BVH_LAYOUT_OPTIX; +} + +string OptiXDevice::compile_kernel_get_common_cflags(const uint kernel_features) +{ + string common_cflags = CUDADevice::compile_kernel_get_common_cflags(kernel_features); + + /* Add OptiX SDK include directory to include paths. */ + const char *optix_sdk_path = getenv("OPTIX_ROOT_DIR"); + if (optix_sdk_path) { + common_cflags += string_printf(" -I\"%s/include\"", optix_sdk_path); + } + + /* Specialization for shader raytracing. */ + if (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + common_cflags += " --keep-device-functions"; + } + + return common_cflags; +} + +bool OptiXDevice::load_kernels(const uint kernel_features) +{ + if (have_error()) { + /* Abort early if context creation failed already. */ + return false; + } + + /* Load CUDA modules because we need some of the utility kernels. */ + if (!CUDADevice::load_kernels(kernel_features)) { + return false; + } + + /* Skip creating OptiX module if only doing denoising. */ + if (!(kernel_features & (KERNEL_FEATURE_PATH_TRACING | KERNEL_FEATURE_BAKING))) { + return true; + } + + const CUDAContextScope scope(this); + + /* Unload existing OptiX module and pipelines first. */ + if (optix_module != NULL) { + optixModuleDestroy(optix_module); + optix_module = NULL; + } + for (unsigned int i = 0; i < 2; ++i) { + if (builtin_modules[i] != NULL) { + optixModuleDestroy(builtin_modules[i]); + builtin_modules[i] = NULL; + } + } + for (unsigned int i = 0; i < NUM_PIPELINES; ++i) { + if (pipelines[i] != NULL) { + optixPipelineDestroy(pipelines[i]); + pipelines[i] = NULL; + } + } + + OptixModuleCompileOptions module_options = {}; + module_options.maxRegisterCount = 0; /* Do not set an explicit register limit. */ + + if (DebugFlags().optix.use_debug) { + module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_0; + module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL; + } + else { + module_options.optLevel = OPTIX_COMPILE_OPTIMIZATION_LEVEL_3; + module_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; + } + + module_options.boundValues = nullptr; + module_options.numBoundValues = 0; + + OptixPipelineCompileOptions pipeline_options = {}; + /* Default to no motion blur and two-level graph, since it is the fastest option. */ + pipeline_options.usesMotionBlur = false; + pipeline_options.traversableGraphFlags = + OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_SINGLE_LEVEL_INSTANCING; + pipeline_options.numPayloadValues = 6; + pipeline_options.numAttributeValues = 2; /* u, v */ + pipeline_options.exceptionFlags = OPTIX_EXCEPTION_FLAG_NONE; + pipeline_options.pipelineLaunchParamsVariableName = "__params"; /* See globals.h */ + + pipeline_options.usesPrimitiveTypeFlags = OPTIX_PRIMITIVE_TYPE_FLAGS_TRIANGLE; + if (kernel_features & KERNEL_FEATURE_HAIR) { + if (kernel_features & KERNEL_FEATURE_HAIR_THICK) { + pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_ROUND_CUBIC_BSPLINE; + } + else + pipeline_options.usesPrimitiveTypeFlags |= OPTIX_PRIMITIVE_TYPE_FLAGS_CUSTOM; + } + + /* Keep track of whether motion blur is enabled, so to enable/disable motion in BVH builds + * This is necessary since objects may be reported to have motion if the Vector pass is + * active, but may still need to be rendered without motion blur if that isn't active as well. */ + motion_blur = (kernel_features & KERNEL_FEATURE_OBJECT_MOTION) != 0; + + if (motion_blur) { + pipeline_options.usesMotionBlur = true; + /* Motion blur can insert motion transforms into the traversal graph. + * It is no longer a two-level graph then, so need to set flags to allow any configuration. */ + pipeline_options.traversableGraphFlags = OPTIX_TRAVERSABLE_GRAPH_FLAG_ALLOW_ANY; + } + + { /* Load and compile PTX module with OptiX kernels. */ + string ptx_data, ptx_filename = path_get((kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? + "lib/kernel_optix_shader_raytrace.ptx" : + "lib/kernel_optix.ptx"); + if (use_adaptive_compilation() || path_file_size(ptx_filename) == -1) { + if (!getenv("OPTIX_ROOT_DIR")) { + set_error( + "Missing OPTIX_ROOT_DIR environment variable (which must be set with the path to " + "the Optix SDK to be able to compile Optix kernels on demand)."); + return false; + } + ptx_filename = compile_kernel( + kernel_features, + (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) ? "kernel_shader_raytrace" : "kernel", + "optix", + true); + } + if (ptx_filename.empty() || !path_read_text(ptx_filename, ptx_data)) { + set_error(string_printf("Failed to load OptiX kernel from '%s'", ptx_filename.c_str())); + return false; + } + + const OptixResult result = optixModuleCreateFromPTX(context, + &module_options, + &pipeline_options, + ptx_data.data(), + ptx_data.size(), + nullptr, + 0, + &optix_module); + if (result != OPTIX_SUCCESS) { + set_error(string_printf("Failed to load OptiX kernel from '%s' (%s)", + ptx_filename.c_str(), + optixGetErrorName(result))); + return false; + } + } + + /* Create program groups. */ + OptixProgramGroup groups[NUM_PROGRAM_GROUPS] = {}; + OptixProgramGroupDesc group_descs[NUM_PROGRAM_GROUPS] = {}; + OptixProgramGroupOptions group_options = {}; /* There are no options currently. */ + group_descs[PG_RGEN_INTERSECT_CLOSEST].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_CLOSEST].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_CLOSEST].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_closest"; + group_descs[PG_RGEN_INTERSECT_SHADOW].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_SHADOW].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_SHADOW].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_shadow"; + group_descs[PG_RGEN_INTERSECT_SUBSURFACE].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_SUBSURFACE].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_SUBSURFACE].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_subsurface"; + group_descs[PG_RGEN_INTERSECT_VOLUME_STACK].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_INTERSECT_VOLUME_STACK].raygen.module = optix_module; + group_descs[PG_RGEN_INTERSECT_VOLUME_STACK].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_intersect_volume_stack"; + group_descs[PG_MISS].kind = OPTIX_PROGRAM_GROUP_KIND_MISS; + group_descs[PG_MISS].miss.module = optix_module; + group_descs[PG_MISS].miss.entryFunctionName = "__miss__kernel_optix_miss"; + group_descs[PG_HITD].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITD].hitgroup.moduleCH = optix_module; + group_descs[PG_HITD].hitgroup.entryFunctionNameCH = "__closesthit__kernel_optix_hit"; + group_descs[PG_HITD].hitgroup.moduleAH = optix_module; + group_descs[PG_HITD].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_visibility_test"; + group_descs[PG_HITS].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITS].hitgroup.moduleAH = optix_module; + group_descs[PG_HITS].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_shadow_all_hit"; + group_descs[PG_HITV].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITV].hitgroup.moduleCH = optix_module; + group_descs[PG_HITV].hitgroup.entryFunctionNameCH = "__closesthit__kernel_optix_hit"; + group_descs[PG_HITV].hitgroup.moduleAH = optix_module; + group_descs[PG_HITV].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_volume_test"; + + if (kernel_features & KERNEL_FEATURE_HAIR) { + if (kernel_features & KERNEL_FEATURE_HAIR_THICK) { + /* Built-in thick curve intersection. */ + OptixBuiltinISOptions builtin_options = {}; + builtin_options.builtinISModuleType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; + builtin_options.usesMotionBlur = false; + + optix_assert(optixBuiltinISModuleGet( + context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[0])); + + group_descs[PG_HITD].hitgroup.moduleIS = builtin_modules[0]; + group_descs[PG_HITD].hitgroup.entryFunctionNameIS = nullptr; + group_descs[PG_HITS].hitgroup.moduleIS = builtin_modules[0]; + group_descs[PG_HITS].hitgroup.entryFunctionNameIS = nullptr; + + if (motion_blur) { + builtin_options.usesMotionBlur = true; + + optix_assert(optixBuiltinISModuleGet( + context, &module_options, &pipeline_options, &builtin_options, &builtin_modules[1])); + + group_descs[PG_HITD_MOTION] = group_descs[PG_HITD]; + group_descs[PG_HITD_MOTION].hitgroup.moduleIS = builtin_modules[1]; + group_descs[PG_HITS_MOTION] = group_descs[PG_HITS]; + group_descs[PG_HITS_MOTION].hitgroup.moduleIS = builtin_modules[1]; + } + } + else { + /* Custom ribbon intersection. */ + group_descs[PG_HITD].hitgroup.moduleIS = optix_module; + group_descs[PG_HITS].hitgroup.moduleIS = optix_module; + group_descs[PG_HITD].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; + group_descs[PG_HITS].hitgroup.entryFunctionNameIS = "__intersection__curve_ribbon"; + } + } + + if (kernel_features & (KERNEL_FEATURE_SUBSURFACE | KERNEL_FEATURE_NODE_RAYTRACE)) { + /* Add hit group for local intersections. */ + group_descs[PG_HITL].kind = OPTIX_PROGRAM_GROUP_KIND_HITGROUP; + group_descs[PG_HITL].hitgroup.moduleAH = optix_module; + group_descs[PG_HITL].hitgroup.entryFunctionNameAH = "__anyhit__kernel_optix_local_hit"; + } + + /* Shader raytracing replaces some functions with direct callables. */ + if (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + group_descs[PG_RGEN_SHADE_SURFACE_RAYTRACE].kind = OPTIX_PROGRAM_GROUP_KIND_RAYGEN; + group_descs[PG_RGEN_SHADE_SURFACE_RAYTRACE].raygen.module = optix_module; + group_descs[PG_RGEN_SHADE_SURFACE_RAYTRACE].raygen.entryFunctionName = + "__raygen__kernel_optix_integrator_shade_surface_raytrace"; + group_descs[PG_CALL_SVM_AO].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; + group_descs[PG_CALL_SVM_AO].callables.moduleDC = optix_module; + group_descs[PG_CALL_SVM_AO].callables.entryFunctionNameDC = "__direct_callable__svm_node_ao"; + group_descs[PG_CALL_SVM_BEVEL].kind = OPTIX_PROGRAM_GROUP_KIND_CALLABLES; + group_descs[PG_CALL_SVM_BEVEL].callables.moduleDC = optix_module; + group_descs[PG_CALL_SVM_BEVEL].callables.entryFunctionNameDC = + "__direct_callable__svm_node_bevel"; + } + + optix_assert(optixProgramGroupCreate( + context, group_descs, NUM_PROGRAM_GROUPS, &group_options, nullptr, 0, groups)); + + /* Get program stack sizes. */ + OptixStackSizes stack_size[NUM_PROGRAM_GROUPS] = {}; + /* Set up SBT, which in this case is used only to select between different programs. */ + sbt_data.alloc(NUM_PROGRAM_GROUPS); + memset(sbt_data.host_pointer, 0, sizeof(SbtRecord) * NUM_PROGRAM_GROUPS); + for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { + optix_assert(optixSbtRecordPackHeader(groups[i], &sbt_data[i])); + optix_assert(optixProgramGroupGetStackSize(groups[i], &stack_size[i])); + } + sbt_data.copy_to_device(); /* Upload SBT to device. */ + + /* Calculate maximum trace continuation stack size. */ + unsigned int trace_css = stack_size[PG_HITD].cssCH; + /* This is based on the maximum of closest-hit and any-hit/intersection programs. */ + trace_css = std::max(trace_css, stack_size[PG_HITD].cssIS + stack_size[PG_HITD].cssAH); + trace_css = std::max(trace_css, stack_size[PG_HITS].cssIS + stack_size[PG_HITS].cssAH); + trace_css = std::max(trace_css, stack_size[PG_HITL].cssIS + stack_size[PG_HITL].cssAH); + trace_css = std::max(trace_css, stack_size[PG_HITV].cssIS + stack_size[PG_HITV].cssAH); + trace_css = std::max(trace_css, + stack_size[PG_HITD_MOTION].cssIS + stack_size[PG_HITD_MOTION].cssAH); + trace_css = std::max(trace_css, + stack_size[PG_HITS_MOTION].cssIS + stack_size[PG_HITS_MOTION].cssAH); + + OptixPipelineLinkOptions link_options = {}; + link_options.maxTraceDepth = 1; + + if (DebugFlags().optix.use_debug) { + link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_FULL; + } + else { + link_options.debugLevel = OPTIX_COMPILE_DEBUG_LEVEL_LINEINFO; + } + + if (kernel_features & KERNEL_FEATURE_NODE_RAYTRACE) { + /* Create shader raytracing pipeline. */ + vector pipeline_groups; + pipeline_groups.reserve(NUM_PROGRAM_GROUPS); + pipeline_groups.push_back(groups[PG_RGEN_SHADE_SURFACE_RAYTRACE]); + pipeline_groups.push_back(groups[PG_MISS]); + pipeline_groups.push_back(groups[PG_HITD]); + pipeline_groups.push_back(groups[PG_HITS]); + pipeline_groups.push_back(groups[PG_HITL]); + pipeline_groups.push_back(groups[PG_HITV]); + if (motion_blur) { + pipeline_groups.push_back(groups[PG_HITD_MOTION]); + pipeline_groups.push_back(groups[PG_HITS_MOTION]); + } + pipeline_groups.push_back(groups[PG_CALL_SVM_AO]); + pipeline_groups.push_back(groups[PG_CALL_SVM_BEVEL]); + + optix_assert(optixPipelineCreate(context, + &pipeline_options, + &link_options, + pipeline_groups.data(), + pipeline_groups.size(), + nullptr, + 0, + &pipelines[PIP_SHADE_RAYTRACE])); + + /* Combine ray generation and trace continuation stack size. */ + const unsigned int css = stack_size[PG_RGEN_SHADE_SURFACE_RAYTRACE].cssRG + + link_options.maxTraceDepth * trace_css; + const unsigned int dss = std::max(stack_size[PG_CALL_SVM_AO].dssDC, + stack_size[PG_CALL_SVM_BEVEL].dssDC); + + /* Set stack size depending on pipeline options. */ + optix_assert(optixPipelineSetStackSize( + pipelines[PIP_SHADE_RAYTRACE], 0, dss, css, motion_blur ? 3 : 2)); + } + + { /* Create intersection-only pipeline. */ + vector pipeline_groups; + pipeline_groups.reserve(NUM_PROGRAM_GROUPS); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_CLOSEST]); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_SHADOW]); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_SUBSURFACE]); + pipeline_groups.push_back(groups[PG_RGEN_INTERSECT_VOLUME_STACK]); + pipeline_groups.push_back(groups[PG_MISS]); + pipeline_groups.push_back(groups[PG_HITD]); + pipeline_groups.push_back(groups[PG_HITS]); + pipeline_groups.push_back(groups[PG_HITL]); + pipeline_groups.push_back(groups[PG_HITV]); + if (motion_blur) { + pipeline_groups.push_back(groups[PG_HITD_MOTION]); + pipeline_groups.push_back(groups[PG_HITS_MOTION]); + } + + optix_assert(optixPipelineCreate(context, + &pipeline_options, + &link_options, + pipeline_groups.data(), + pipeline_groups.size(), + nullptr, + 0, + &pipelines[PIP_INTERSECT])); + + /* Calculate continuation stack size based on the maximum of all ray generation stack sizes. */ + const unsigned int css = + std::max(stack_size[PG_RGEN_INTERSECT_CLOSEST].cssRG, + std::max(stack_size[PG_RGEN_INTERSECT_SHADOW].cssRG, + std::max(stack_size[PG_RGEN_INTERSECT_SUBSURFACE].cssRG, + stack_size[PG_RGEN_INTERSECT_VOLUME_STACK].cssRG))) + + link_options.maxTraceDepth * trace_css; + + optix_assert( + optixPipelineSetStackSize(pipelines[PIP_INTERSECT], 0, 0, css, motion_blur ? 3 : 2)); + } + + /* Clean up program group objects. */ + for (unsigned int i = 0; i < NUM_PROGRAM_GROUPS; ++i) { + optixProgramGroupDestroy(groups[i]); + } + + return true; +} + +/* -------------------------------------------------------------------- + * Buffer denoising. + */ + +class OptiXDevice::DenoiseContext { + public: + explicit DenoiseContext(OptiXDevice *device, const DeviceDenoiseTask &task) + : denoise_params(task.params), + render_buffers(task.render_buffers), + buffer_params(task.buffer_params), + guiding_buffer(device, "denoiser guiding passes buffer"), + num_samples(task.num_samples) + { + num_input_passes = 1; + if (denoise_params.use_pass_albedo) { + num_input_passes += 1; + use_pass_albedo = true; + pass_denoising_albedo = buffer_params.get_pass_offset(PASS_DENOISING_ALBEDO); + if (denoise_params.use_pass_normal) { + num_input_passes += 1; + use_pass_normal = true; + pass_denoising_normal = buffer_params.get_pass_offset(PASS_DENOISING_NORMAL); + } + } + + const int num_guiding_passes = num_input_passes - 1; + + if (num_guiding_passes) { + if (task.allow_inplace_modification) { + guiding_params.device_pointer = render_buffers->buffer.device_pointer; + + guiding_params.pass_albedo = pass_denoising_albedo; + guiding_params.pass_normal = pass_denoising_normal; + + guiding_params.stride = buffer_params.stride; + guiding_params.pass_stride = buffer_params.pass_stride; + } + else { + guiding_params.pass_stride = 0; + if (use_pass_albedo) { + guiding_params.pass_albedo = guiding_params.pass_stride; + guiding_params.pass_stride += 3; + } + if (use_pass_normal) { + guiding_params.pass_normal = guiding_params.pass_stride; + guiding_params.pass_stride += 3; + } + + guiding_params.stride = buffer_params.width; + + guiding_buffer.alloc_to_device(buffer_params.width * buffer_params.height * + guiding_params.pass_stride); + guiding_params.device_pointer = guiding_buffer.device_pointer; + } + } + + pass_sample_count = buffer_params.get_pass_offset(PASS_SAMPLE_COUNT); + } + + const DenoiseParams &denoise_params; + + RenderBuffers *render_buffers = nullptr; + const BufferParams &buffer_params; + + /* Device-side storage of the guiding passes. */ + device_only_memory guiding_buffer; + + struct { + device_ptr device_pointer = 0; + + /* NOTE: Are only initialized when the corresponding guiding pass is enabled. */ + int pass_albedo = PASS_UNUSED; + int pass_normal = PASS_UNUSED; + + int stride = -1; + int pass_stride = -1; + } guiding_params; + + /* Number of input passes. Including the color and extra auxiliary passes. */ + int num_input_passes = 0; + bool use_pass_albedo = false; + bool use_pass_normal = false; + + int num_samples = 0; + + int pass_sample_count = PASS_UNUSED; + + /* NOTE: Are only initialized when the corresponding guiding pass is enabled. */ + int pass_denoising_albedo = PASS_UNUSED; + int pass_denoising_normal = PASS_UNUSED; + + /* For passes which don't need albedo channel for denoising we replace the actual albedo with + * the (0.5, 0.5, 0.5). This flag indicates that the real albedo pass has been replaced with + * the fake values and denoising of passes which do need albedo can no longer happen. */ + bool albedo_replaced_with_fake = false; +}; + +class OptiXDevice::DenoisePass { + public: + DenoisePass(const PassType type, const BufferParams &buffer_params) : type(type) + { + noisy_offset = buffer_params.get_pass_offset(type, PassMode::NOISY); + denoised_offset = buffer_params.get_pass_offset(type, PassMode::DENOISED); + + const PassInfo pass_info = Pass::get_info(type); + num_components = pass_info.num_components; + use_compositing = pass_info.use_compositing; + use_denoising_albedo = pass_info.use_denoising_albedo; + } + + PassType type; + + int noisy_offset; + int denoised_offset; + + int num_components; + bool use_compositing; + bool use_denoising_albedo; +}; + +bool OptiXDevice::denoise_buffer(const DeviceDenoiseTask &task) +{ + const CUDAContextScope scope(this); + + DenoiseContext context(this, task); + + if (!denoise_ensure(context)) { + return false; + } + + if (!denoise_filter_guiding_preprocess(context)) { + LOG(ERROR) << "Error preprocessing guiding passes."; + return false; + } + + /* Passes which will use real albedo when it is available. */ + denoise_pass(context, PASS_COMBINED); + denoise_pass(context, PASS_SHADOW_CATCHER_MATTE); + + /* Passes which do not need albedo and hence if real is present it needs to become fake. */ + denoise_pass(context, PASS_SHADOW_CATCHER); + + return true; +} + +DeviceQueue *OptiXDevice::get_denoise_queue() +{ + return &denoiser_.queue; +} + +bool OptiXDevice::denoise_filter_guiding_preprocess(DenoiseContext &context) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {const_cast(&context.guiding_params.device_pointer), + const_cast(&context.guiding_params.pass_stride), + const_cast(&context.guiding_params.pass_albedo), + const_cast(&context.guiding_params.pass_normal), + &context.render_buffers->buffer.device_pointer, + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&buffer_params.pass_stride), + const_cast(&context.pass_sample_count), + const_cast(&context.pass_denoising_albedo), + const_cast(&context.pass_denoising_normal), + const_cast(&buffer_params.full_x), + const_cast(&buffer_params.full_y), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height), + const_cast(&context.num_samples)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS, work_size, args); +} + +bool OptiXDevice::denoise_filter_guiding_set_fake_albedo(DenoiseContext &context) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {const_cast(&context.guiding_params.device_pointer), + const_cast(&context.guiding_params.pass_stride), + const_cast(&context.guiding_params.pass_albedo), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO, work_size, args); +} + +void OptiXDevice::denoise_pass(DenoiseContext &context, PassType pass_type) +{ + const BufferParams &buffer_params = context.buffer_params; + + const DenoisePass pass(pass_type, buffer_params); + + if (pass.noisy_offset == PASS_UNUSED) { + return; + } + if (pass.denoised_offset == PASS_UNUSED) { + LOG(DFATAL) << "Missing denoised pass " << pass_type_as_string(pass_type); + return; + } + + if (pass.use_denoising_albedo) { + if (context.albedo_replaced_with_fake) { + LOG(ERROR) << "Pass which requires albedo is denoised after fake albedo has been set."; + return; + } + } + else if (!context.albedo_replaced_with_fake) { + context.albedo_replaced_with_fake = true; + if (!denoise_filter_guiding_set_fake_albedo(context)) { + LOG(ERROR) << "Error replacing real albedo with the fake one."; + return; + } + } + + /* Read and preprocess noisy color input pass. */ + denoise_color_read(context, pass); + if (!denoise_filter_color_preprocess(context, pass)) { + LOG(ERROR) << "Error connverting denoising passes to RGB buffer."; + return; + } + + if (!denoise_run(context, pass)) { + LOG(ERROR) << "Error running OptiX denoiser."; + return; + } + + /* Store result in the combined pass of the render buffer. + * + * This will scale the denoiser result up to match the number of, possibly per-pixel, samples. */ + if (!denoise_filter_color_postprocess(context, pass)) { + LOG(ERROR) << "Error copying denoiser result to the denoised pass."; + return; + } + + denoiser_.queue.synchronize(); +} + +void OptiXDevice::denoise_color_read(DenoiseContext &context, const DenoisePass &pass) +{ + PassAccessor::PassAccessInfo pass_access_info; + pass_access_info.type = pass.type; + pass_access_info.mode = PassMode::NOISY; + pass_access_info.offset = pass.noisy_offset; + + /* Denoiser operates on passes which are used to calculate the approximation, and is never used + * on the approximation. The latter is not even possible because OptiX does not support + * denoising of semi-transparent pixels. */ + pass_access_info.use_approximate_shadow_catcher = false; + pass_access_info.use_approximate_shadow_catcher_background = false; + pass_access_info.show_active_pixels = false; + + /* TODO(sergey): Consider adding support of actual exposure, to avoid clamping in extreme cases. + */ + const PassAccessorGPU pass_accessor( + &denoiser_.queue, pass_access_info, 1.0f, context.num_samples); + + PassAccessor::Destination destination(pass_access_info.type); + destination.d_pixels = context.render_buffers->buffer.device_pointer + + pass.denoised_offset * sizeof(float); + destination.num_components = 3; + destination.pixel_stride = context.buffer_params.pass_stride; + + BufferParams buffer_params = context.buffer_params; + buffer_params.window_x = 0; + buffer_params.window_y = 0; + buffer_params.window_width = buffer_params.width; + buffer_params.window_height = buffer_params.height; + + pass_accessor.get_render_tile_pixels(context.render_buffers, buffer_params, destination); +} + +bool OptiXDevice::denoise_filter_color_preprocess(DenoiseContext &context, const DenoisePass &pass) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {&context.render_buffers->buffer.device_pointer, + const_cast(&buffer_params.full_x), + const_cast(&buffer_params.full_y), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height), + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&buffer_params.pass_stride), + const_cast(&pass.denoised_offset)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_COLOR_PREPROCESS, work_size, args); +} + +bool OptiXDevice::denoise_filter_color_postprocess(DenoiseContext &context, + const DenoisePass &pass) +{ + const BufferParams &buffer_params = context.buffer_params; + + const int work_size = buffer_params.width * buffer_params.height; + + void *args[] = {&context.render_buffers->buffer.device_pointer, + const_cast(&buffer_params.full_x), + const_cast(&buffer_params.full_y), + const_cast(&buffer_params.width), + const_cast(&buffer_params.height), + const_cast(&buffer_params.offset), + const_cast(&buffer_params.stride), + const_cast(&buffer_params.pass_stride), + const_cast(&context.num_samples), + const_cast(&pass.noisy_offset), + const_cast(&pass.denoised_offset), + const_cast(&context.pass_sample_count), + const_cast(&pass.num_components), + const_cast(&pass.use_compositing)}; + + return denoiser_.queue.enqueue(DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS, work_size, args); +} + +bool OptiXDevice::denoise_ensure(DenoiseContext &context) +{ + if (!denoise_create_if_needed(context)) { + LOG(ERROR) << "OptiX denoiser creation has failed."; + return false; + } + + if (!denoise_configure_if_needed(context)) { + LOG(ERROR) << "OptiX denoiser configuration has failed."; + return false; + } + + return true; +} + +bool OptiXDevice::denoise_create_if_needed(DenoiseContext &context) +{ + const bool recreate_denoiser = (denoiser_.optix_denoiser == nullptr) || + (denoiser_.use_pass_albedo != context.use_pass_albedo) || + (denoiser_.use_pass_normal != context.use_pass_normal); + if (!recreate_denoiser) { + return true; + } + + /* Destroy existing handle before creating new one. */ + if (denoiser_.optix_denoiser) { + optixDenoiserDestroy(denoiser_.optix_denoiser); + } + + /* Create OptiX denoiser handle on demand when it is first used. */ + OptixDenoiserOptions denoiser_options = {}; + denoiser_options.guideAlbedo = context.use_pass_albedo; + denoiser_options.guideNormal = context.use_pass_normal; + const OptixResult result = optixDenoiserCreate( + this->context, OPTIX_DENOISER_MODEL_KIND_HDR, &denoiser_options, &denoiser_.optix_denoiser); + + if (result != OPTIX_SUCCESS) { + set_error("Failed to create OptiX denoiser"); + return false; + } + + /* OptiX denoiser handle was created with the requested number of input passes. */ + denoiser_.use_pass_albedo = context.use_pass_albedo; + denoiser_.use_pass_normal = context.use_pass_normal; + + /* OptiX denoiser has been created, but it needs configuration. */ + denoiser_.is_configured = false; + + return true; +} + +bool OptiXDevice::denoise_configure_if_needed(DenoiseContext &context) +{ + if (denoiser_.is_configured && (denoiser_.configured_size.x == context.buffer_params.width && + denoiser_.configured_size.y == context.buffer_params.height)) { + return true; + } + + const BufferParams &buffer_params = context.buffer_params; + + OptixDenoiserSizes sizes = {}; + optix_assert(optixDenoiserComputeMemoryResources( + denoiser_.optix_denoiser, buffer_params.width, buffer_params.height, &sizes)); + + denoiser_.scratch_size = sizes.withOverlapScratchSizeInBytes; + denoiser_.scratch_offset = sizes.stateSizeInBytes; + + /* Allocate denoiser state if tile size has changed since last setup. */ + denoiser_.state.alloc_to_device(denoiser_.scratch_offset + denoiser_.scratch_size); + + /* Initialize denoiser state for the current tile size. */ + const OptixResult result = optixDenoiserSetup(denoiser_.optix_denoiser, + denoiser_.queue.stream(), + buffer_params.width, + buffer_params.height, + denoiser_.state.device_pointer, + denoiser_.scratch_offset, + denoiser_.state.device_pointer + + denoiser_.scratch_offset, + denoiser_.scratch_size); + if (result != OPTIX_SUCCESS) { + set_error("Failed to set up OptiX denoiser"); + return false; + } + + denoiser_.is_configured = true; + denoiser_.configured_size.x = buffer_params.width; + denoiser_.configured_size.y = buffer_params.height; + + return true; +} + +bool OptiXDevice::denoise_run(DenoiseContext &context, const DenoisePass &pass) +{ + const BufferParams &buffer_params = context.buffer_params; + const int width = buffer_params.width; + const int height = buffer_params.height; + + /* Set up input and output layer information. */ + OptixImage2D color_layer = {0}; + OptixImage2D albedo_layer = {0}; + OptixImage2D normal_layer = {0}; + + OptixImage2D output_layer = {0}; + + /* Color pass. */ + { + const int pass_denoised = pass.denoised_offset; + const int64_t pass_stride_in_bytes = context.buffer_params.pass_stride * sizeof(float); + + color_layer.data = context.render_buffers->buffer.device_pointer + + pass_denoised * sizeof(float); + color_layer.width = width; + color_layer.height = height; + color_layer.rowStrideInBytes = pass_stride_in_bytes * context.buffer_params.stride; + color_layer.pixelStrideInBytes = pass_stride_in_bytes; + color_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; + } + + device_vector fake_albedo(this, "fake_albedo", MEM_READ_WRITE); + + /* Optional albedo and color passes. */ + if (context.num_input_passes > 1) { + const device_ptr d_guiding_buffer = context.guiding_params.device_pointer; + const int64_t pixel_stride_in_bytes = context.guiding_params.pass_stride * sizeof(float); + const int64_t row_stride_in_bytes = context.guiding_params.stride * pixel_stride_in_bytes; + + if (context.use_pass_albedo) { + albedo_layer.data = d_guiding_buffer + context.guiding_params.pass_albedo * sizeof(float); + albedo_layer.width = width; + albedo_layer.height = height; + albedo_layer.rowStrideInBytes = row_stride_in_bytes; + albedo_layer.pixelStrideInBytes = pixel_stride_in_bytes; + albedo_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; + } + + if (context.use_pass_normal) { + normal_layer.data = d_guiding_buffer + context.guiding_params.pass_normal * sizeof(float); + normal_layer.width = width; + normal_layer.height = height; + normal_layer.rowStrideInBytes = row_stride_in_bytes; + normal_layer.pixelStrideInBytes = pixel_stride_in_bytes; + normal_layer.format = OPTIX_PIXEL_FORMAT_FLOAT3; + } + } + + /* Denoise in-place of the noisy input in the render buffers. */ + output_layer = color_layer; + + /* Finally run denoising. */ + OptixDenoiserParams params = {}; /* All parameters are disabled/zero. */ + OptixDenoiserLayer image_layers = {}; + image_layers.input = color_layer; + image_layers.output = output_layer; + + OptixDenoiserGuideLayer guide_layers = {}; + guide_layers.albedo = albedo_layer; + guide_layers.normal = normal_layer; + + optix_assert(optixDenoiserInvoke(denoiser_.optix_denoiser, + denoiser_.queue.stream(), + ¶ms, + denoiser_.state.device_pointer, + denoiser_.scratch_offset, + &guide_layers, + &image_layers, + 1, + 0, + 0, + denoiser_.state.device_pointer + denoiser_.scratch_offset, + denoiser_.scratch_size)); + + return true; +} + +bool OptiXDevice::build_optix_bvh(BVHOptiX *bvh, + OptixBuildOperation operation, + const OptixBuildInput &build_input, + uint16_t num_motion_steps) +{ + const CUDAContextScope scope(this); + + const bool use_fast_trace_bvh = (bvh->params.bvh_type == BVH_TYPE_STATIC); + + /* Compute memory usage. */ + OptixAccelBufferSizes sizes = {}; + OptixAccelBuildOptions options = {}; + options.operation = operation; + if (use_fast_trace_bvh) { + VLOG(2) << "Using fast to trace OptiX BVH"; + options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_TRACE | OPTIX_BUILD_FLAG_ALLOW_COMPACTION; + } + else { + VLOG(2) << "Using fast to update OptiX BVH"; + options.buildFlags = OPTIX_BUILD_FLAG_PREFER_FAST_BUILD | OPTIX_BUILD_FLAG_ALLOW_UPDATE; + } + + options.motionOptions.numKeys = num_motion_steps; + options.motionOptions.flags = OPTIX_MOTION_FLAG_START_VANISH | OPTIX_MOTION_FLAG_END_VANISH; + options.motionOptions.timeBegin = 0.0f; + options.motionOptions.timeEnd = 1.0f; + + optix_assert(optixAccelComputeMemoryUsage(context, &options, &build_input, 1, &sizes)); + + /* Allocate required output buffers. */ + device_only_memory temp_mem(this, "optix temp as build mem"); + temp_mem.alloc_to_device(align_up(sizes.tempSizeInBytes, 8) + 8); + if (!temp_mem.device_pointer) { + /* Make sure temporary memory allocation succeeded. */ + return false; + } + + device_only_memory &out_data = bvh->as_data; + if (operation == OPTIX_BUILD_OPERATION_BUILD) { + assert(out_data.device == this); + out_data.alloc_to_device(sizes.outputSizeInBytes); + if (!out_data.device_pointer) { + return false; + } + } + else { + assert(out_data.device_pointer && out_data.device_size >= sizes.outputSizeInBytes); + } + + /* Finally build the acceleration structure. */ + OptixAccelEmitDesc compacted_size_prop = {}; + compacted_size_prop.type = OPTIX_PROPERTY_TYPE_COMPACTED_SIZE; + /* A tiny space was allocated for this property at the end of the temporary buffer above. + * Make sure this pointer is 8-byte aligned. */ + compacted_size_prop.result = align_up(temp_mem.device_pointer + sizes.tempSizeInBytes, 8); + + OptixTraversableHandle out_handle = 0; + optix_assert(optixAccelBuild(context, + NULL, + &options, + &build_input, + 1, + temp_mem.device_pointer, + sizes.tempSizeInBytes, + out_data.device_pointer, + sizes.outputSizeInBytes, + &out_handle, + use_fast_trace_bvh ? &compacted_size_prop : NULL, + use_fast_trace_bvh ? 1 : 0)); + bvh->traversable_handle = static_cast(out_handle); + + /* Wait for all operations to finish. */ + cuda_assert(cuStreamSynchronize(NULL)); + + /* Compact acceleration structure to save memory (do not do this in viewport for faster builds). + */ + if (use_fast_trace_bvh) { + uint64_t compacted_size = sizes.outputSizeInBytes; + cuda_assert(cuMemcpyDtoH(&compacted_size, compacted_size_prop.result, sizeof(compacted_size))); + + /* Temporary memory is no longer needed, so free it now to make space. */ + temp_mem.free(); + + /* There is no point compacting if the size does not change. */ + if (compacted_size < sizes.outputSizeInBytes) { + device_only_memory compacted_data(this, "optix compacted as"); + compacted_data.alloc_to_device(compacted_size); + if (!compacted_data.device_pointer) + /* Do not compact if memory allocation for compacted acceleration structure fails. + * Can just use the uncompacted one then, so succeed here regardless. */ + return !have_error(); + + optix_assert(optixAccelCompact( + context, NULL, out_handle, compacted_data.device_pointer, compacted_size, &out_handle)); + bvh->traversable_handle = static_cast(out_handle); + + /* Wait for compaction to finish. */ + cuda_assert(cuStreamSynchronize(NULL)); + + std::swap(out_data.device_size, compacted_data.device_size); + std::swap(out_data.device_pointer, compacted_data.device_pointer); + } + } + + return !have_error(); +} + +void OptiXDevice::build_bvh(BVH *bvh, Progress &progress, bool refit) +{ + const bool use_fast_trace_bvh = (bvh->params.bvh_type == BVH_TYPE_STATIC); + + free_bvh_memory_delayed(); + + BVHOptiX *const bvh_optix = static_cast(bvh); + + progress.set_substatus("Building OptiX acceleration structure"); + + if (!bvh->params.top_level) { + assert(bvh->objects.size() == 1 && bvh->geometry.size() == 1); + + /* Refit is only possible in viewport for now (because AS is built with + * OPTIX_BUILD_FLAG_ALLOW_UPDATE only there, see above). */ + OptixBuildOperation operation = OPTIX_BUILD_OPERATION_BUILD; + if (refit && !use_fast_trace_bvh) { + assert(bvh_optix->traversable_handle != 0); + operation = OPTIX_BUILD_OPERATION_UPDATE; + } + else { + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; + } + + /* Build bottom level acceleration structures (BLAS). */ + Geometry *const geom = bvh->geometry[0]; + if (geom->geometry_type == Geometry::HAIR) { + /* Build BLAS for curve primitives. */ + Hair *const hair = static_cast(geom); + if (hair->num_curves() == 0) { + return; + } + + const size_t num_segments = hair->num_segments(); + + size_t num_motion_steps = 1; + Attribute *motion_keys = hair->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (motion_blur && hair->get_use_motion_blur() && motion_keys) { + num_motion_steps = hair->get_motion_steps(); + } + + device_vector aabb_data(this, "optix temp aabb data", MEM_READ_ONLY); + device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); + device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); + /* Four control points for each curve segment. */ + const size_t num_vertices = num_segments * 4; + if (hair->curve_shape == CURVE_THICK) { + index_data.alloc(num_segments); + vertex_data.alloc(num_vertices * num_motion_steps); + } + else + aabb_data.alloc(num_segments * num_motion_steps); + + /* Get AABBs for each motion step. */ + for (size_t step = 0; step < num_motion_steps; ++step) { + /* The center step for motion vertices is not stored in the attribute. */ + const float3 *keys = hair->get_curve_keys().data(); + size_t center_step = (num_motion_steps - 1) / 2; + if (step != center_step) { + size_t attr_offset = (step > center_step) ? step - 1 : step; + /* Technically this is a float4 array, but sizeof(float3) == sizeof(float4). */ + keys = motion_keys->data_float3() + attr_offset * hair->get_curve_keys().size(); + } + + for (size_t j = 0, i = 0; j < hair->num_curves(); ++j) { + const Hair::Curve curve = hair->get_curve(j); + const array &curve_radius = hair->get_curve_radius(); + + for (int segment = 0; segment < curve.num_segments(); ++segment, ++i) { + if (hair->curve_shape == CURVE_THICK) { + int k0 = curve.first_key + segment; + int k1 = k0 + 1; + int ka = max(k0 - 1, curve.first_key); + int kb = min(k1 + 1, curve.first_key + curve.num_keys - 1); + + const float4 px = make_float4(keys[ka].x, keys[k0].x, keys[k1].x, keys[kb].x); + const float4 py = make_float4(keys[ka].y, keys[k0].y, keys[k1].y, keys[kb].y); + const float4 pz = make_float4(keys[ka].z, keys[k0].z, keys[k1].z, keys[kb].z); + const float4 pw = make_float4( + curve_radius[ka], curve_radius[k0], curve_radius[k1], curve_radius[kb]); + + /* Convert Catmull-Rom data to Bezier spline. */ + static const float4 cr2bsp0 = make_float4(+7, -4, +5, -2) / 6.f; + static const float4 cr2bsp1 = make_float4(-2, 11, -4, +1) / 6.f; + static const float4 cr2bsp2 = make_float4(+1, -4, 11, -2) / 6.f; + static const float4 cr2bsp3 = make_float4(-2, +5, -4, +7) / 6.f; + + index_data[i] = i * 4; + float4 *const v = vertex_data.data() + step * num_vertices + index_data[i]; + v[0] = make_float4( + dot(cr2bsp0, px), dot(cr2bsp0, py), dot(cr2bsp0, pz), dot(cr2bsp0, pw)); + v[1] = make_float4( + dot(cr2bsp1, px), dot(cr2bsp1, py), dot(cr2bsp1, pz), dot(cr2bsp1, pw)); + v[2] = make_float4( + dot(cr2bsp2, px), dot(cr2bsp2, py), dot(cr2bsp2, pz), dot(cr2bsp2, pw)); + v[3] = make_float4( + dot(cr2bsp3, px), dot(cr2bsp3, py), dot(cr2bsp3, pz), dot(cr2bsp3, pw)); + } + else { + BoundBox bounds = BoundBox::empty; + curve.bounds_grow(segment, keys, hair->get_curve_radius().data(), bounds); + + const size_t index = step * num_segments + i; + aabb_data[index].minX = bounds.min.x; + aabb_data[index].minY = bounds.min.y; + aabb_data[index].minZ = bounds.min.z; + aabb_data[index].maxX = bounds.max.x; + aabb_data[index].maxY = bounds.max.y; + aabb_data[index].maxZ = bounds.max.z; + } + } + } + } + + /* Upload AABB data to GPU. */ + aabb_data.copy_to_device(); + index_data.copy_to_device(); + vertex_data.copy_to_device(); + + vector aabb_ptrs; + aabb_ptrs.reserve(num_motion_steps); + vector width_ptrs; + vector vertex_ptrs; + width_ptrs.reserve(num_motion_steps); + vertex_ptrs.reserve(num_motion_steps); + for (size_t step = 0; step < num_motion_steps; ++step) { + aabb_ptrs.push_back(aabb_data.device_pointer + step * num_segments * sizeof(OptixAabb)); + const device_ptr base_ptr = vertex_data.device_pointer + + step * num_vertices * sizeof(float4); + width_ptrs.push_back(base_ptr + 3 * sizeof(float)); /* Offset by vertex size. */ + vertex_ptrs.push_back(base_ptr); + } + + /* Force a single any-hit call, so shadow record-all behavior works correctly. */ + unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; + OptixBuildInput build_input = {}; + if (hair->curve_shape == CURVE_THICK) { + build_input.type = OPTIX_BUILD_INPUT_TYPE_CURVES; + build_input.curveArray.curveType = OPTIX_PRIMITIVE_TYPE_ROUND_CUBIC_BSPLINE; + build_input.curveArray.numPrimitives = num_segments; + build_input.curveArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); + build_input.curveArray.numVertices = num_vertices; + build_input.curveArray.vertexStrideInBytes = sizeof(float4); + build_input.curveArray.widthBuffers = (CUdeviceptr *)width_ptrs.data(); + build_input.curveArray.widthStrideInBytes = sizeof(float4); + build_input.curveArray.indexBuffer = (CUdeviceptr)index_data.device_pointer; + build_input.curveArray.indexStrideInBytes = sizeof(int); + build_input.curveArray.flag = build_flags; + build_input.curveArray.primitiveIndexOffset = hair->curve_segment_offset; + } + else { + /* Disable visibility test any-hit program, since it is already checked during + * intersection. Those trace calls that require anyhit can force it with a ray flag. */ + build_flags |= OPTIX_GEOMETRY_FLAG_DISABLE_ANYHIT; + + build_input.type = OPTIX_BUILD_INPUT_TYPE_CUSTOM_PRIMITIVES; + build_input.customPrimitiveArray.aabbBuffers = (CUdeviceptr *)aabb_ptrs.data(); + build_input.customPrimitiveArray.numPrimitives = num_segments; + build_input.customPrimitiveArray.strideInBytes = sizeof(OptixAabb); + build_input.customPrimitiveArray.flags = &build_flags; + build_input.customPrimitiveArray.numSbtRecords = 1; + build_input.customPrimitiveArray.primitiveIndexOffset = hair->curve_segment_offset; + } + + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + } + else if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { + /* Build BLAS for triangle primitives. */ + Mesh *const mesh = static_cast(geom); + if (mesh->num_triangles() == 0) { + return; + } + + const size_t num_verts = mesh->get_verts().size(); + + size_t num_motion_steps = 1; + Attribute *motion_keys = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); + if (motion_blur && mesh->get_use_motion_blur() && motion_keys) { + num_motion_steps = mesh->get_motion_steps(); + } + + device_vector index_data(this, "optix temp index data", MEM_READ_ONLY); + index_data.alloc(mesh->get_triangles().size()); + memcpy(index_data.data(), + mesh->get_triangles().data(), + mesh->get_triangles().size() * sizeof(int)); + device_vector vertex_data(this, "optix temp vertex data", MEM_READ_ONLY); + vertex_data.alloc(num_verts * num_motion_steps); + + for (size_t step = 0; step < num_motion_steps; ++step) { + const float3 *verts = mesh->get_verts().data(); + + size_t center_step = (num_motion_steps - 1) / 2; + /* The center step for motion vertices is not stored in the attribute. */ + if (step != center_step) { + verts = motion_keys->data_float3() + (step > center_step ? step - 1 : step) * num_verts; + } + + memcpy(vertex_data.data() + num_verts * step, verts, num_verts * sizeof(float3)); + } + + /* Upload triangle data to GPU. */ + index_data.copy_to_device(); + vertex_data.copy_to_device(); + + vector vertex_ptrs; + vertex_ptrs.reserve(num_motion_steps); + for (size_t step = 0; step < num_motion_steps; ++step) { + vertex_ptrs.push_back(vertex_data.device_pointer + num_verts * step * sizeof(float3)); + } + + /* Force a single any-hit call, so shadow record-all behavior works correctly. */ + unsigned int build_flags = OPTIX_GEOMETRY_FLAG_REQUIRE_SINGLE_ANYHIT_CALL; + OptixBuildInput build_input = {}; + build_input.type = OPTIX_BUILD_INPUT_TYPE_TRIANGLES; + build_input.triangleArray.vertexBuffers = (CUdeviceptr *)vertex_ptrs.data(); + build_input.triangleArray.numVertices = num_verts; + build_input.triangleArray.vertexFormat = OPTIX_VERTEX_FORMAT_FLOAT3; + build_input.triangleArray.vertexStrideInBytes = sizeof(float4); + build_input.triangleArray.indexBuffer = index_data.device_pointer; + build_input.triangleArray.numIndexTriplets = mesh->num_triangles(); + build_input.triangleArray.indexFormat = OPTIX_INDICES_FORMAT_UNSIGNED_INT3; + build_input.triangleArray.indexStrideInBytes = 3 * sizeof(int); + build_input.triangleArray.flags = &build_flags; + /* The SBT does not store per primitive data since Cycles already allocates separate + * buffers for that purpose. OptiX does not allow this to be zero though, so just pass in + * one and rely on that having the same meaning in this case. */ + build_input.triangleArray.numSbtRecords = 1; + build_input.triangleArray.primitiveIndexOffset = mesh->prim_offset; + + if (!build_optix_bvh(bvh_optix, operation, build_input, num_motion_steps)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + } + } + else { + unsigned int num_instances = 0; + unsigned int max_num_instances = 0xFFFFFFFF; + + bvh_optix->as_data.free(); + bvh_optix->traversable_handle = 0; + bvh_optix->motion_transform_data.free(); + + optixDeviceContextGetProperty(context, + OPTIX_DEVICE_PROPERTY_LIMIT_MAX_INSTANCE_ID, + &max_num_instances, + sizeof(max_num_instances)); + /* Do not count first bit, which is used to distinguish instanced and non-instanced objects. */ + max_num_instances >>= 1; + if (bvh->objects.size() > max_num_instances) { + progress.set_error( + "Failed to build OptiX acceleration structure because there are too many instances"); + return; + } + + /* Fill instance descriptions. */ + device_vector instances(this, "optix tlas instances", MEM_READ_ONLY); + instances.alloc(bvh->objects.size()); + + /* Calculate total motion transform size and allocate memory for them. */ + size_t motion_transform_offset = 0; + if (motion_blur) { + size_t total_motion_transform_size = 0; + for (Object *const ob : bvh->objects) { + if (ob->is_traceable() && ob->use_motion()) { + total_motion_transform_size = align_up(total_motion_transform_size, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + const size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + total_motion_transform_size = total_motion_transform_size + + sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); + } + } + + assert(bvh_optix->motion_transform_data.device == this); + bvh_optix->motion_transform_data.alloc_to_device(total_motion_transform_size); + } + + for (Object *ob : bvh->objects) { + /* Skip non-traceable objects. */ + if (!ob->is_traceable()) { + continue; + } + + BVHOptiX *const blas = static_cast(ob->get_geometry()->bvh); + OptixTraversableHandle handle = blas->traversable_handle; + + OptixInstance &instance = instances[num_instances++]; + memset(&instance, 0, sizeof(instance)); + + /* Clear transform to identity matrix. */ + instance.transform[0] = 1.0f; + instance.transform[5] = 1.0f; + instance.transform[10] = 1.0f; + + /* Set user instance ID to object index. */ + instance.instanceId = ob->get_device_index(); + + /* Add some of the object visibility bits to the mask. + * __prim_visibility contains the combined visibility bits of all instances, so is not + * reliable if they differ between instances. But the OptiX visibility mask can only contain + * 8 bits, so have to trade-off here and select just a few important ones. + */ + instance.visibilityMask = ob->visibility_for_tracing() & 0xFF; + + /* Have to have at least one bit in the mask, or else instance would always be culled. */ + if (0 == instance.visibilityMask) { + instance.visibilityMask = 0xFF; + } + + if (ob->get_geometry()->geometry_type == Geometry::HAIR && + static_cast(ob->get_geometry())->curve_shape == CURVE_THICK) { + if (motion_blur && ob->get_geometry()->has_motion_blur()) { + /* Select between motion blur and non-motion blur built-in intersection module. */ + instance.sbtOffset = PG_HITD_MOTION - PG_HITD; + } + } + else { + /* Can disable __anyhit__kernel_optix_visibility_test by default (except for thick curves, + * since it needs to filter out end-caps there). + * It is enabled where necessary (visibility mask exceeds 8 bits or the other any-hit + * programs like __anyhit__kernel_optix_shadow_all_hit) via OPTIX_RAY_FLAG_ENFORCE_ANYHIT. + */ + instance.flags = OPTIX_INSTANCE_FLAG_DISABLE_ANYHIT; + } + + /* Insert motion traversable if object has motion. */ + if (motion_blur && ob->use_motion()) { + size_t motion_keys = max(ob->get_motion().size(), 2) - 2; + size_t motion_transform_size = sizeof(OptixSRTMotionTransform) + + motion_keys * sizeof(OptixSRTData); + + const CUDAContextScope scope(this); + + motion_transform_offset = align_up(motion_transform_offset, + OPTIX_TRANSFORM_BYTE_ALIGNMENT); + CUdeviceptr motion_transform_gpu = bvh_optix->motion_transform_data.device_pointer + + motion_transform_offset; + motion_transform_offset += motion_transform_size; + + /* Allocate host side memory for motion transform and fill it with transform data. */ + OptixSRTMotionTransform &motion_transform = *reinterpret_cast( + new uint8_t[motion_transform_size]); + motion_transform.child = handle; + motion_transform.motionOptions.numKeys = ob->get_motion().size(); + motion_transform.motionOptions.flags = OPTIX_MOTION_FLAG_NONE; + motion_transform.motionOptions.timeBegin = 0.0f; + motion_transform.motionOptions.timeEnd = 1.0f; + + OptixSRTData *const srt_data = motion_transform.srtData; + array decomp(ob->get_motion().size()); + transform_motion_decompose( + decomp.data(), ob->get_motion().data(), ob->get_motion().size()); + + for (size_t i = 0; i < ob->get_motion().size(); ++i) { + /* Scale. */ + srt_data[i].sx = decomp[i].y.w; /* scale.x.x */ + srt_data[i].sy = decomp[i].z.w; /* scale.y.y */ + srt_data[i].sz = decomp[i].w.w; /* scale.z.z */ + + /* Shear. */ + srt_data[i].a = decomp[i].z.x; /* scale.x.y */ + srt_data[i].b = decomp[i].z.y; /* scale.x.z */ + srt_data[i].c = decomp[i].w.x; /* scale.y.z */ + assert(decomp[i].z.z == 0.0f); /* scale.y.x */ + assert(decomp[i].w.y == 0.0f); /* scale.z.x */ + assert(decomp[i].w.z == 0.0f); /* scale.z.y */ + + /* Pivot point. */ + srt_data[i].pvx = 0.0f; + srt_data[i].pvy = 0.0f; + srt_data[i].pvz = 0.0f; + + /* Rotation. */ + srt_data[i].qx = decomp[i].x.x; + srt_data[i].qy = decomp[i].x.y; + srt_data[i].qz = decomp[i].x.z; + srt_data[i].qw = decomp[i].x.w; + + /* Translation. */ + srt_data[i].tx = decomp[i].y.x; + srt_data[i].ty = decomp[i].y.y; + srt_data[i].tz = decomp[i].y.z; + } + + /* Upload motion transform to GPU. */ + cuMemcpyHtoD(motion_transform_gpu, &motion_transform, motion_transform_size); + delete[] reinterpret_cast(&motion_transform); + + /* Disable instance transform if object uses motion transform already. */ + instance.flags |= OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + + /* Get traversable handle to motion transform. */ + optixConvertPointerToTraversableHandle(context, + motion_transform_gpu, + OPTIX_TRAVERSABLE_TYPE_SRT_MOTION_TRANSFORM, + &instance.traversableHandle); + } + else { + instance.traversableHandle = handle; + + if (ob->get_geometry()->is_instanced()) { + /* Set transform matrix. */ + memcpy(instance.transform, &ob->get_tfm(), sizeof(instance.transform)); + } + else { + /* Disable instance transform if geometry already has it applied to vertex data. */ + instance.flags |= OPTIX_INSTANCE_FLAG_DISABLE_TRANSFORM; + } + } + } + + /* Upload instance descriptions. */ + instances.resize(num_instances); + instances.copy_to_device(); + + /* Build top-level acceleration structure (TLAS) */ + OptixBuildInput build_input = {}; + build_input.type = OPTIX_BUILD_INPUT_TYPE_INSTANCES; + build_input.instanceArray.instances = instances.device_pointer; + build_input.instanceArray.numInstances = num_instances; + + if (!build_optix_bvh(bvh_optix, OPTIX_BUILD_OPERATION_BUILD, build_input, 0)) { + progress.set_error("Failed to build OptiX acceleration structure"); + } + tlas_handle = bvh_optix->traversable_handle; + } +} + +void OptiXDevice::release_optix_bvh(BVH *bvh) +{ + thread_scoped_lock lock(delayed_free_bvh_mutex); + /* Do delayed free of BVH memory, since geometry holding BVH might be deleted + * while GPU is still rendering. */ + BVHOptiX *const bvh_optix = static_cast(bvh); + + delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->as_data)); + delayed_free_bvh_memory.emplace_back(std::move(bvh_optix->motion_transform_data)); + bvh_optix->traversable_handle = 0; +} + +void OptiXDevice::free_bvh_memory_delayed() +{ + thread_scoped_lock lock(delayed_free_bvh_mutex); + delayed_free_bvh_memory.free_memory(); +} + +void OptiXDevice::const_copy_to(const char *name, void *host, size_t size) +{ + /* Set constant memory for CUDA module. */ + CUDADevice::const_copy_to(name, host, size); + + if (strcmp(name, "__data") == 0) { + assert(size <= sizeof(KernelData)); + + /* Update traversable handle (since it is different for each device on multi devices). */ + KernelData *const data = (KernelData *)host; + *(OptixTraversableHandle *)&data->bvh.scene = tlas_handle; + + update_launch_params(offsetof(KernelParamsOptiX, data), host, size); + return; + } + + /* Update data storage pointers in launch parameters. */ +# define KERNEL_TEX(data_type, tex_name) \ + if (strcmp(name, #tex_name) == 0) { \ + update_launch_params(offsetof(KernelParamsOptiX, tex_name), host, size); \ + return; \ + } + KERNEL_TEX(IntegratorStateGPU, __integrator_state) +# include "kernel/textures.h" +# undef KERNEL_TEX +} + +void OptiXDevice::update_launch_params(size_t offset, void *data, size_t data_size) +{ + const CUDAContextScope scope(this); + + cuda_assert(cuMemcpyHtoD(launch_params.device_pointer + offset, data, data_size)); +} + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/device_impl.h b/intern/cycles/device/optix/device_impl.h new file mode 100644 index 00000000000..3ec98098eb7 --- /dev/null +++ b/intern/cycles/device/optix/device_impl.h @@ -0,0 +1,186 @@ +/* + * Copyright 2019, NVIDIA Corporation. + * Copyright 2019, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_OPTIX + +# include "device/cuda/device_impl.h" +# include "device/optix/queue.h" +# include "device/optix/util.h" +# include "kernel/types.h" + +CCL_NAMESPACE_BEGIN + +class BVHOptiX; +struct KernelParamsOptiX; + +/* List of OptiX program groups. */ +enum { + PG_RGEN_INTERSECT_CLOSEST, + PG_RGEN_INTERSECT_SHADOW, + PG_RGEN_INTERSECT_SUBSURFACE, + PG_RGEN_INTERSECT_VOLUME_STACK, + PG_RGEN_SHADE_SURFACE_RAYTRACE, + PG_MISS, + PG_HITD, /* Default hit group. */ + PG_HITS, /* __SHADOW_RECORD_ALL__ hit group. */ + PG_HITL, /* __BVH_LOCAL__ hit group (only used for triangles). */ + PG_HITV, /* __VOLUME__ hit group. */ + PG_HITD_MOTION, + PG_HITS_MOTION, + PG_CALL_SVM_AO, + PG_CALL_SVM_BEVEL, + NUM_PROGRAM_GROUPS +}; + +static const int MISS_PROGRAM_GROUP_OFFSET = PG_MISS; +static const int NUM_MIS_PROGRAM_GROUPS = 1; +static const int HIT_PROGAM_GROUP_OFFSET = PG_HITD; +static const int NUM_HIT_PROGRAM_GROUPS = 6; +static const int CALLABLE_PROGRAM_GROUPS_BASE = PG_CALL_SVM_AO; +static const int NUM_CALLABLE_PROGRAM_GROUPS = 3; + +/* List of OptiX pipelines. */ +enum { PIP_SHADE_RAYTRACE, PIP_INTERSECT, NUM_PIPELINES }; + +/* A single shader binding table entry. */ +struct SbtRecord { + char header[OPTIX_SBT_RECORD_HEADER_SIZE]; +}; + +class OptiXDevice : public CUDADevice { + public: + OptixDeviceContext context = NULL; + + OptixModule optix_module = NULL; /* All necessary OptiX kernels are in one module. */ + OptixModule builtin_modules[2] = {}; + OptixPipeline pipelines[NUM_PIPELINES] = {}; + + bool motion_blur = false; + device_vector sbt_data; + device_only_memory launch_params; + OptixTraversableHandle tlas_handle = 0; + + vector> delayed_free_bvh_memory; + thread_mutex delayed_free_bvh_mutex; + + class Denoiser { + public: + explicit Denoiser(OptiXDevice *device); + ~Denoiser(); + + OptiXDevice *device; + OptiXDeviceQueue queue; + + OptixDenoiser optix_denoiser = nullptr; + + /* Configuration size, as provided to `optixDenoiserSetup`. + * If the `optixDenoiserSetup()` was never used on the current `optix_denoiser` the + * `is_configured` will be false. */ + bool is_configured = false; + int2 configured_size = make_int2(0, 0); + + /* OptiX denoiser state and scratch buffers, stored in a single memory buffer. + * The memory layout goes as following: [denoiser state][scratch buffer]. */ + device_only_memory state; + size_t scratch_offset = 0; + size_t scratch_size = 0; + + bool use_pass_albedo = false; + bool use_pass_normal = false; + }; + Denoiser denoiser_; + + public: + OptiXDevice(const DeviceInfo &info, Stats &stats, Profiler &profiler); + ~OptiXDevice(); + + private: + BVHLayoutMask get_bvh_layout_mask() const override; + + string compile_kernel_get_common_cflags(const uint kernel_features) override; + + bool load_kernels(const uint kernel_features) override; + + bool build_optix_bvh(BVHOptiX *bvh, + OptixBuildOperation operation, + const OptixBuildInput &build_input, + uint16_t num_motion_steps); + + void build_bvh(BVH *bvh, Progress &progress, bool refit) override; + + void release_optix_bvh(BVH *bvh) override; + void free_bvh_memory_delayed(); + + void const_copy_to(const char *name, void *host, size_t size) override; + + void update_launch_params(size_t offset, void *data, size_t data_size); + + virtual unique_ptr gpu_queue_create() override; + + /* -------------------------------------------------------------------- + * Denoising. + */ + + class DenoiseContext; + class DenoisePass; + + virtual bool denoise_buffer(const DeviceDenoiseTask &task) override; + virtual DeviceQueue *get_denoise_queue() override; + + /* Read guiding passes from the render buffers, preprocess them in a way which is expected by + * OptiX and store in the guiding passes memory within the given context. + * + * Pre=-processing of the guiding passes is to only happen once per context lifetime. DO not + * preprocess them for every pass which is being denoised. */ + bool denoise_filter_guiding_preprocess(DenoiseContext &context); + + /* Set fake albedo pixels in the albedo guiding pass storage. + * After this point only passes which do not need albedo for denoising can be processed. */ + bool denoise_filter_guiding_set_fake_albedo(DenoiseContext &context); + + void denoise_pass(DenoiseContext &context, PassType pass_type); + + /* Read input color pass from the render buffer into the memory which corresponds to the noisy + * input within the given context. Pixels are scaled to the number of samples, but are not + * preprocessed yet. */ + void denoise_color_read(DenoiseContext &context, const DenoisePass &pass); + + /* Run corresponding filter kernels, preparing data for the denoiser or copying data from the + * denoiser result to the render buffer. */ + bool denoise_filter_color_preprocess(DenoiseContext &context, const DenoisePass &pass); + bool denoise_filter_color_postprocess(DenoiseContext &context, const DenoisePass &pass); + + /* Make sure the OptiX denoiser is created and configured. */ + bool denoise_ensure(DenoiseContext &context); + + /* Create OptiX denoiser descriptor if needed. + * Will do nothing if the current OptiX descriptor is usable for the given parameters. + * If the OptiX denoiser descriptor did re-allocate here it is left unconfigured. */ + bool denoise_create_if_needed(DenoiseContext &context); + + /* Configure existing OptiX denoiser descriptor for the use for the given task. */ + bool denoise_configure_if_needed(DenoiseContext &context); + + /* Run configured denoiser. */ + bool denoise_run(DenoiseContext &context, const DenoisePass &pass); +}; + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/queue.cpp b/intern/cycles/device/optix/queue.cpp new file mode 100644 index 00000000000..f5bfd916ccf --- /dev/null +++ b/intern/cycles/device/optix/queue.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef WITH_OPTIX + +# include "device/optix/queue.h" +# include "device/optix/device_impl.h" + +# include "util/time.h" + +# undef __KERNEL_CPU__ +# define __KERNEL_OPTIX__ +# include "kernel/device/optix/globals.h" + +CCL_NAMESPACE_BEGIN + +/* CUDADeviceQueue */ + +OptiXDeviceQueue::OptiXDeviceQueue(OptiXDevice *device) : CUDADeviceQueue(device) +{ +} + +void OptiXDeviceQueue::init_execution() +{ + CUDADeviceQueue::init_execution(); +} + +static bool is_optix_specific_kernel(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); +} + +bool OptiXDeviceQueue::enqueue(DeviceKernel kernel, const int work_size, void *args[]) +{ + if (!is_optix_specific_kernel(kernel)) { + return CUDADeviceQueue::enqueue(kernel, work_size, args); + } + + if (cuda_device_->have_error()) { + return false; + } + + debug_enqueue(kernel, work_size); + + const CUDAContextScope scope(cuda_device_); + + OptiXDevice *const optix_device = static_cast(cuda_device_); + + const device_ptr sbt_data_ptr = optix_device->sbt_data.device_pointer; + const device_ptr launch_params_ptr = optix_device->launch_params.device_pointer; + + cuda_device_assert( + cuda_device_, + cuMemcpyHtoDAsync(launch_params_ptr + offsetof(KernelParamsOptiX, path_index_array), + args[0], // &d_path_index + sizeof(device_ptr), + cuda_stream_)); + + if (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE) { + cuda_device_assert( + cuda_device_, + cuMemcpyHtoDAsync(launch_params_ptr + offsetof(KernelParamsOptiX, render_buffer), + args[1], // &d_render_buffer + sizeof(device_ptr), + cuda_stream_)); + } + + cuda_device_assert(cuda_device_, cuStreamSynchronize(cuda_stream_)); + + OptixPipeline pipeline = nullptr; + OptixShaderBindingTable sbt_params = {}; + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + pipeline = optix_device->pipelines[PIP_SHADE_RAYTRACE]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_SHADE_SURFACE_RAYTRACE * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_CLOSEST * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_SHADOW * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_SUBSURFACE * sizeof(SbtRecord); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: + pipeline = optix_device->pipelines[PIP_INTERSECT]; + sbt_params.raygenRecord = sbt_data_ptr + PG_RGEN_INTERSECT_VOLUME_STACK * sizeof(SbtRecord); + break; + + default: + LOG(ERROR) << "Invalid kernel " << device_kernel_as_string(kernel) + << " is attempted to be enqueued."; + return false; + } + + sbt_params.missRecordBase = sbt_data_ptr + MISS_PROGRAM_GROUP_OFFSET * sizeof(SbtRecord); + sbt_params.missRecordStrideInBytes = sizeof(SbtRecord); + sbt_params.missRecordCount = NUM_MIS_PROGRAM_GROUPS; + sbt_params.hitgroupRecordBase = sbt_data_ptr + HIT_PROGAM_GROUP_OFFSET * sizeof(SbtRecord); + sbt_params.hitgroupRecordStrideInBytes = sizeof(SbtRecord); + sbt_params.hitgroupRecordCount = NUM_HIT_PROGRAM_GROUPS; + sbt_params.callablesRecordBase = sbt_data_ptr + CALLABLE_PROGRAM_GROUPS_BASE * sizeof(SbtRecord); + sbt_params.callablesRecordCount = NUM_CALLABLE_PROGRAM_GROUPS; + sbt_params.callablesRecordStrideInBytes = sizeof(SbtRecord); + + /* Launch the ray generation program. */ + optix_device_assert(optix_device, + optixLaunch(pipeline, + cuda_stream_, + launch_params_ptr, + optix_device->launch_params.data_elements, + &sbt_params, + work_size, + 1, + 1)); + + return !(optix_device->have_error()); +} + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/queue.h b/intern/cycles/device/optix/queue.h new file mode 100644 index 00000000000..0de422ccc71 --- /dev/null +++ b/intern/cycles/device/optix/queue.h @@ -0,0 +1,39 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_OPTIX + +# include "device/cuda/queue.h" + +CCL_NAMESPACE_BEGIN + +class OptiXDevice; + +/* Base class for CUDA queues. */ +class OptiXDeviceQueue : public CUDADeviceQueue { + public: + OptiXDeviceQueue(OptiXDevice *device); + + virtual void init_execution() override; + + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) override; +}; + +CCL_NAMESPACE_END + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/optix/util.h b/intern/cycles/device/optix/util.h new file mode 100644 index 00000000000..34ae5bb5609 --- /dev/null +++ b/intern/cycles/device/optix/util.h @@ -0,0 +1,45 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#ifdef WITH_OPTIX + +# include "device/cuda/util.h" + +# ifdef WITH_CUDA_DYNLOAD +# include +// Do not use CUDA SDK headers when using CUEW +# define OPTIX_DONT_INCLUDE_CUDA +# endif + +# include + +/* Utility for checking return values of OptiX function calls. */ +# define optix_device_assert(optix_device, stmt) \ + { \ + OptixResult result = stmt; \ + if (result != OPTIX_SUCCESS) { \ + const char *name = optixGetErrorName(result); \ + optix_device->set_error( \ + string_printf("%s in %s (%s:%d)", name, #stmt, __FILE__, __LINE__)); \ + } \ + } \ + (void)0 + +# define optix_assert(stmt) optix_device_assert(this, stmt) + +#endif /* WITH_OPTIX */ diff --git a/intern/cycles/device/queue.cpp b/intern/cycles/device/queue.cpp new file mode 100644 index 00000000000..556dc97f23b --- /dev/null +++ b/intern/cycles/device/queue.cpp @@ -0,0 +1,95 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/queue.h" + +#include "util/algorithm.h" +#include "util/log.h" +#include "util/time.h" + +#include + +CCL_NAMESPACE_BEGIN + +DeviceQueue::DeviceQueue(Device *device) + : device(device), last_kernels_enqueued_(0), last_sync_time_(0.0) +{ + DCHECK_NE(device, nullptr); +} + +DeviceQueue::~DeviceQueue() +{ + if (VLOG_IS_ON(3)) { + /* Print kernel execution times sorted by time. */ + vector> stats_sorted; + for (const auto &stat : stats_kernel_time_) { + stats_sorted.push_back(stat); + } + + sort(stats_sorted.begin(), + stats_sorted.end(), + [](const pair &a, const pair &b) { + return a.second > b.second; + }); + + VLOG(3) << "GPU queue stats:"; + for (const auto &[mask, time] : stats_sorted) { + VLOG(3) << " " << std::setfill(' ') << std::setw(10) << std::fixed << std::setprecision(5) + << std::right << time << "s: " << device_kernel_mask_as_string(mask); + } + } +} + +void DeviceQueue::debug_init_execution() +{ + if (VLOG_IS_ON(3)) { + last_sync_time_ = time_dt(); + } + + last_kernels_enqueued_ = 0; +} + +void DeviceQueue::debug_enqueue(DeviceKernel kernel, const int work_size) +{ + if (VLOG_IS_ON(3)) { + VLOG(4) << "GPU queue launch " << device_kernel_as_string(kernel) << ", work_size " + << work_size; + } + + last_kernels_enqueued_ |= (uint64_t(1) << (uint64_t)kernel); +} + +void DeviceQueue::debug_synchronize() +{ + if (VLOG_IS_ON(3)) { + const double new_time = time_dt(); + const double elapsed_time = new_time - last_sync_time_; + VLOG(4) << "GPU queue synchronize, elapsed " << std::setw(10) << elapsed_time << "s"; + + stats_kernel_time_[last_kernels_enqueued_] += elapsed_time; + + last_sync_time_ = new_time; + } + + last_kernels_enqueued_ = 0; +} + +string DeviceQueue::debug_active_kernels() +{ + return device_kernel_mask_as_string(last_kernels_enqueued_); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/device/queue.h b/intern/cycles/device/queue.h new file mode 100644 index 00000000000..188162f4b74 --- /dev/null +++ b/intern/cycles/device/queue.h @@ -0,0 +1,115 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/kernel.h" + +#include "device/graphics_interop.h" +#include "util/log.h" +#include "util/map.h" +#include "util/string.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class device_memory; + +struct KernelWorkTile; + +/* Abstraction of a command queue for a device. + * Provides API to schedule kernel execution in a specific queue with minimal possible overhead + * from driver side. + * + * This class encapsulates all properties needed for commands execution. */ +class DeviceQueue { + public: + virtual ~DeviceQueue(); + + /* Number of concurrent states to process for integrator, + * based on number of cores and/or available memory. */ + virtual int num_concurrent_states(const size_t state_size) const = 0; + + /* Number of states which keeps the device occupied with work without loosing performance. + * The renderer will add more work (when available) when number of active paths falls below this + * value. */ + virtual int num_concurrent_busy_states() const = 0; + + /* Initialize execution of kernels on this queue. + * + * Will, for example, load all data required by the kernels from Device to global or path state. + * + * Use this method after device synchronization has finished before enqueueing any kernels. */ + virtual void init_execution() = 0; + + /* Test if an optional device kernel is available. */ + virtual bool kernel_available(DeviceKernel kernel) const = 0; + + /* Enqueue kernel execution. + * + * Execute the kernel work_size times on the device. + * Supported arguments types: + * - int: pass pointer to the int + * - device memory: pass pointer to device_memory.device_pointer + * Return false if there was an error executing this or a previous kernel. */ + virtual bool enqueue(DeviceKernel kernel, const int work_size, void *args[]) = 0; + + /* Wait unit all enqueued kernels have finished execution. + * Return false if there was an error executing any of the enqueued kernels. */ + virtual bool synchronize() = 0; + + /* Copy memory to/from device as part of the command queue, to ensure + * operations are done in order without having to synchronize. */ + virtual void zero_to_device(device_memory &mem) = 0; + virtual void copy_to_device(device_memory &mem) = 0; + virtual void copy_from_device(device_memory &mem) = 0; + + /* Graphics resources interoperability. + * + * The interoperability comes here by the meaning that the device is capable of computing result + * directly into an OpenGL (or other graphics library) buffer. */ + + /* Create graphics interoperability context which will be taking care of mapping graphics + * resource as a buffer writable by kernels of this device. */ + virtual unique_ptr graphics_interop_create() + { + LOG(FATAL) << "Request of GPU interop of a device which does not support it."; + return nullptr; + } + + /* Device this queue has been created for. */ + Device *device; + + protected: + /* Hide construction so that allocation via `Device` API is enforced. */ + explicit DeviceQueue(Device *device); + + /* Implementations call these from the corresponding methods to generate debugging logs. */ + void debug_init_execution(); + void debug_enqueue(DeviceKernel kernel, const int work_size); + void debug_synchronize(); + string debug_active_kernels(); + + /* Combination of kernels enqueued together sync last synchronize. */ + DeviceKernelMask last_kernels_enqueued_; + /* Time of synchronize call. */ + double last_sync_time_; + /* Accumulated execution time for combinations of kernels launched together. */ + map stats_kernel_time_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/graph/node.cpp b/intern/cycles/graph/node.cpp index 57f25283f85..a3d75e53afd 100644 --- a/intern/cycles/graph/node.cpp +++ b/intern/cycles/graph/node.cpp @@ -17,10 +17,10 @@ #include "graph/node.h" #include "graph/node_type.h" -#include "util/util_foreach.h" -#include "util/util_md5.h" -#include "util/util_param.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/md5.h" +#include "util/param.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN @@ -814,7 +814,7 @@ bool Node::socket_is_modified(const SocketType &input) const return (socket_modified & input.modified_flag_bit) != 0; } -bool Node::is_modified() +bool Node::is_modified() const { return socket_modified != 0; } diff --git a/intern/cycles/graph/node.h b/intern/cycles/graph/node.h index aa365baeccd..a00162a3b9a 100644 --- a/intern/cycles/graph/node.h +++ b/intern/cycles/graph/node.h @@ -16,11 +16,13 @@ #pragma once +#include + #include "graph/node_type.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_param.h" +#include "util/array.h" +#include "util/map.h" +#include "util/param.h" CCL_NAMESPACE_BEGIN @@ -34,7 +36,10 @@ struct Transform; #define NODE_SOCKET_API_BASE_METHODS(type_, name, string_name) \ const SocketType *get_##name##_socket() const \ { \ - static const SocketType *socket = type->find_input(ustring(string_name)); \ + /* Explicitly cast to base class to use `Node::type` even if the derived class defines \ + * `type`. */ \ + const Node *self_node = this; \ + static const SocketType *socket = self_node->type->find_input(ustring(string_name)); \ return socket; \ } \ bool name##_is_modified() const \ @@ -111,6 +116,15 @@ struct Node { void set(const SocketType &input, const Transform &value); void set(const SocketType &input, Node *value); + /* Implicitly cast enums and enum classes to integer, which matches an internal way of how + * enumerator values are stored and accessed in a generic API. */ + template> * = nullptr> + void set(const SocketType &input, const ValueType &value) + { + static_assert(sizeof(ValueType) <= sizeof(int), "Enumerator type should fit int"); + set(input, static_cast(value)); + } + /* set array values. the memory from the input array will taken over * by the node and the input array will be empty after return */ void set(const SocketType &input, array &value); @@ -164,7 +178,7 @@ struct Node { bool socket_is_modified(const SocketType &input) const; - bool is_modified(); + bool is_modified() const; void tag_modified(); void clear_modified(); diff --git a/intern/cycles/graph/node_enum.h b/intern/cycles/graph/node_enum.h index d3ed0928a4f..831c6e4a9c4 100644 --- a/intern/cycles/graph/node_enum.h +++ b/intern/cycles/graph/node_enum.h @@ -16,8 +16,8 @@ #pragma once -#include "util/util_map.h" -#include "util/util_param.h" +#include "util/map.h" +#include "util/param.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_type.cpp b/intern/cycles/graph/node_type.cpp index 4efbd6725ee..bce98c694e1 100644 --- a/intern/cycles/graph/node_type.cpp +++ b/intern/cycles/graph/node_type.cpp @@ -15,8 +15,8 @@ */ #include "graph/node_type.h" -#include "util/util_foreach.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_type.h b/intern/cycles/graph/node_type.h index 8b37398fa17..71639341617 100644 --- a/intern/cycles/graph/node_type.h +++ b/intern/cycles/graph/node_type.h @@ -17,11 +17,11 @@ #pragma once #include "graph/node_enum.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/map.h" +#include "util/param.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_xml.cpp b/intern/cycles/graph/node_xml.cpp index 43462662b6a..b0c863ad4b5 100644 --- a/intern/cycles/graph/node_xml.cpp +++ b/intern/cycles/graph/node_xml.cpp @@ -16,9 +16,9 @@ #include "graph/node_xml.h" -#include "util/util_foreach.h" -#include "util/util_string.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/string.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/graph/node_xml.h b/intern/cycles/graph/node_xml.h index 15bbf5d5621..ddbc5213ab1 100644 --- a/intern/cycles/graph/node_xml.h +++ b/intern/cycles/graph/node_xml.h @@ -18,9 +18,9 @@ #include "graph/node.h" -#include "util/util_map.h" -#include "util/util_string.h" -#include "util/util_xml.h" +#include "util/map.h" +#include "util/string.h" +#include "util/xml.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/integrator/CMakeLists.txt b/intern/cycles/integrator/CMakeLists.txt new file mode 100644 index 00000000000..dedde513409 --- /dev/null +++ b/intern/cycles/integrator/CMakeLists.txt @@ -0,0 +1,82 @@ +# Copyright 2011-2021 Blender Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(INC + .. +) + +set(SRC + adaptive_sampling.cpp + denoiser.cpp + denoiser_device.cpp + denoiser_oidn.cpp + denoiser_optix.cpp + path_trace.cpp + tile.cpp + pass_accessor.cpp + pass_accessor_cpu.cpp + pass_accessor_gpu.cpp + path_trace_display.cpp + path_trace_tile.cpp + path_trace_work.cpp + path_trace_work_cpu.cpp + path_trace_work_gpu.cpp + render_scheduler.cpp + shader_eval.cpp + work_balancer.cpp + work_tile_scheduler.cpp +) + +set(SRC_HEADERS + adaptive_sampling.h + denoiser.h + denoiser_device.h + denoiser_oidn.h + denoiser_optix.h + path_trace.h + tile.h + pass_accessor.h + pass_accessor_cpu.h + pass_accessor_gpu.h + path_trace_display.h + path_trace_tile.h + path_trace_work.h + path_trace_work_cpu.h + path_trace_work_gpu.h + render_scheduler.h + shader_eval.h + work_balancer.h + work_tile_scheduler.h +) + +set(LIB + cycles_device + + # NOTE: Is required for RenderBuffers access. Might consider moving files around a bit to + # avoid such cyclic dependency. + cycles_session + + cycles_util +) + +if(WITH_OPENIMAGEDENOISE) + list(APPEND LIB + ${OPENIMAGEDENOISE_LIBRARIES} + ) +endif() + +include_directories(${INC}) +include_directories(SYSTEM ${INC_SYS}) + +cycles_add_library(cycles_integrator "${LIB}" ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/integrator/adaptive_sampling.cpp b/intern/cycles/integrator/adaptive_sampling.cpp new file mode 100644 index 00000000000..253879d67e3 --- /dev/null +++ b/intern/cycles/integrator/adaptive_sampling.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/adaptive_sampling.h" + +#include "util/math.h" + +CCL_NAMESPACE_BEGIN + +AdaptiveSampling::AdaptiveSampling() +{ +} + +int AdaptiveSampling::align_samples(int start_sample, int num_samples) const +{ + if (!use) { + return num_samples; + } + + /* + * The naive implementation goes as following: + * + * int count = 1; + * while (!need_filter(start_sample + count - 1) && count < num_samples) { + * ++count; + * } + * return count; + */ + + /* 0-based sample index at which first filtering will happen. */ + const int first_filter_sample = (min_samples + 1) | (adaptive_step - 1); + + /* Allow as many samples as possible until the first filter sample. */ + if (start_sample + num_samples <= first_filter_sample) { + return num_samples; + } + + const int next_filter_sample = max(first_filter_sample, start_sample | (adaptive_step - 1)); + + const int num_samples_until_filter = next_filter_sample - start_sample + 1; + + return min(num_samples_until_filter, num_samples); +} + +bool AdaptiveSampling::need_filter(int sample) const +{ + if (!use) { + return false; + } + + if (sample <= min_samples) { + return false; + } + + return (sample & (adaptive_step - 1)) == (adaptive_step - 1); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/adaptive_sampling.h b/intern/cycles/integrator/adaptive_sampling.h new file mode 100644 index 00000000000..d98edd9894c --- /dev/null +++ b/intern/cycles/integrator/adaptive_sampling.h @@ -0,0 +1,55 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +class AdaptiveSampling { + public: + AdaptiveSampling(); + + /* Align number of samples so that they align with the adaptive filtering. + * + * Returns the new value for the `num_samples` so that after rendering so many samples on top + * of `start_sample` filtering is required. + * + * The alignment happens in a way that allows to render as many samples as possible without + * missing any filtering point. This means that the result is "clamped" by the nearest sample + * at which filtering is needed. This is part of mechanism which ensures that all devices will + * perform same exact filtering and adaptive sampling, regardless of their performance. + * + * `start_sample` is the 0-based index of sample. + * + * NOTE: The start sample is included into the number of samples to render. This means that + * if the number of samples is 1, then the path tracer will render samples [align_samples], + * if the number of samples is 2, then the path tracer will render samples [align_samples, + * align_samples + 1] and so on. */ + int align_samples(int start_sample, int num_samples) const; + + /* Check whether adaptive sampling filter should happen at this sample. + * Returns false if the adaptive sampling is not use. + * + * `sample` is the 0-based index of sample. */ + bool need_filter(int sample) const; + + bool use = false; + int adaptive_step = 0; + int min_samples = 0; + float threshold = 0.0f; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser.cpp b/intern/cycles/integrator/denoiser.cpp new file mode 100644 index 00000000000..b89024e0c85 --- /dev/null +++ b/intern/cycles/integrator/denoiser.cpp @@ -0,0 +1,192 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/denoiser.h" + +#include "device/device.h" +#include "integrator/denoiser_oidn.h" +#include "integrator/denoiser_optix.h" +#include "session/buffers.h" +#include "util/log.h" +#include "util/progress.h" + +CCL_NAMESPACE_BEGIN + +unique_ptr Denoiser::create(Device *path_trace_device, const DenoiseParams ¶ms) +{ + DCHECK(params.use); + + if (params.type == DENOISER_OPTIX && Device::available_devices(DEVICE_MASK_OPTIX).size()) { + return make_unique(path_trace_device, params); + } + + return make_unique(path_trace_device, params); +} + +Denoiser::Denoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : path_trace_device_(path_trace_device), params_(params) +{ + DCHECK(params.use); +} + +void Denoiser::set_params(const DenoiseParams ¶ms) +{ + DCHECK_EQ(params.type, params_.type); + + if (params.type == params_.type) { + params_ = params; + } + else { + LOG(ERROR) << "Attempt to change denoiser type."; + } +} + +const DenoiseParams &Denoiser::get_params() const +{ + return params_; +} + +bool Denoiser::load_kernels(Progress *progress) +{ + const Device *denoiser_device = ensure_denoiser_device(progress); + + if (!denoiser_device) { + path_trace_device_->set_error("No device available to denoise on"); + return false; + } + + VLOG(3) << "Will denoise on " << denoiser_device->info.description << " (" + << denoiser_device->info.id << ")"; + + return true; +} + +Device *Denoiser::get_denoiser_device() const +{ + return denoiser_device_; +} + +/* Check whether given device is single (not a MultiDevice) and supports requested denoiser. */ +static bool is_single_supported_device(Device *device, DenoiserType type) +{ + if (device->info.type == DEVICE_MULTI) { + /* Assume multi-device is never created with a single sub-device. + * If one requests such configuration it should be checked on the session level. */ + return false; + } + + if (!device->info.multi_devices.empty()) { + /* Some configurations will use multi_devices, but keep the type of an individual device. + * This does simplify checks for homogenous setups, but here we really need a single device. */ + return false; + } + + /* Check the denoiser type is supported. */ + return (device->info.denoisers & type); +} + +/* Find best suitable device to perform denoiser on. Will iterate over possible sub-devices of + * multi-device. + * + * If there is no device available which supports given denoiser type nullptr is returned. */ +static Device *find_best_device(Device *device, DenoiserType type) +{ + Device *best_device = nullptr; + + device->foreach_device([&](Device *sub_device) { + if ((sub_device->info.denoisers & type) == 0) { + return; + } + if (!best_device) { + best_device = sub_device; + } + else { + /* TODO(sergey): Choose fastest device from available ones. Taking into account performance + * of the device and data transfer cost. */ + } + }); + + return best_device; +} + +static unique_ptr create_denoiser_device(Device *path_trace_device, + const uint device_type_mask) +{ + const vector device_infos = Device::available_devices(device_type_mask); + if (device_infos.empty()) { + return nullptr; + } + + /* TODO(sergey): Use one of the already configured devices, so that OptiX denoising can happen on + * a physical CUDA device which is already used for rendering. */ + + /* TODO(sergey): Choose fastest device for denoising. */ + + const DeviceInfo denoiser_device_info = device_infos.front(); + + unique_ptr denoiser_device( + Device::create(denoiser_device_info, path_trace_device->stats, path_trace_device->profiler)); + + if (!denoiser_device) { + return nullptr; + } + + if (denoiser_device->have_error()) { + return nullptr; + } + + /* Only need denoising feature, everything else is unused. */ + if (!denoiser_device->load_kernels(KERNEL_FEATURE_DENOISING)) { + return nullptr; + } + + return denoiser_device; +} + +Device *Denoiser::ensure_denoiser_device(Progress *progress) +{ + /* The best device has been found already, avoid sequential lookups. + * Additionally, avoid device re-creation if it has failed once. */ + if (denoiser_device_ || device_creation_attempted_) { + return denoiser_device_; + } + + /* Simple case: rendering happens on a single device which also supports denoiser. */ + if (is_single_supported_device(path_trace_device_, params_.type)) { + denoiser_device_ = path_trace_device_; + return denoiser_device_; + } + + /* Find best device from the ones which are already used for rendering. */ + denoiser_device_ = find_best_device(path_trace_device_, params_.type); + if (denoiser_device_) { + return denoiser_device_; + } + + if (progress) { + progress->set_status("Loading denoising kernels (may take a few minutes the first time)"); + } + + device_creation_attempted_ = true; + + const uint device_type_mask = get_device_type_mask(); + local_denoiser_device_ = create_denoiser_device(path_trace_device_, device_type_mask); + denoiser_device_ = local_denoiser_device_.get(); + + return denoiser_device_; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser.h b/intern/cycles/integrator/denoiser.h new file mode 100644 index 00000000000..8d7a644f8d9 --- /dev/null +++ b/intern/cycles/integrator/denoiser.h @@ -0,0 +1,135 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +/* TODO(sergey): The integrator folder might not be the best. Is easy to move files around if the + * better place is figured out. */ + +#include "device/denoise.h" +#include "device/device.h" +#include "util/function.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; +class Device; +class RenderBuffers; +class Progress; + +/* Implementation of a specific denoising algorithm. + * + * This class takes care of breaking down denoising algorithm into a series of device calls or to + * calls of an external API to denoise given input. + * + * TODO(sergey): Are we better with device or a queue here? */ +class Denoiser { + public: + /* Create denoiser for the given path trace device. + * + * Notes: + * - The denoiser must be configured. This means that `params.use` must be true. + * This is checked in debug builds. + * - The device might be MultiDevice. */ + static unique_ptr create(Device *path_trace_device, const DenoiseParams ¶ms); + + virtual ~Denoiser() = default; + + void set_params(const DenoiseParams ¶ms); + const DenoiseParams &get_params() const; + + /* Create devices and load kernels needed for denoising. + * The progress is used to communicate state when kernels actually needs to be loaded. + * + * NOTE: The `progress` is an optional argument, can be nullptr. */ + virtual bool load_kernels(Progress *progress); + + /* Denoise the entire buffer. + * + * Buffer parameters denotes an effective parameters used during rendering. It could be + * a lower resolution render into a bigger allocated buffer, which is used in viewport during + * navigation and non-unit pixel size. Use that instead of render_buffers->params. + * + * The buffer might be coming from a "foreign" device from what this denoise is created for. + * This means that in general case the denoiser will make sure the input data is available on + * the denoiser device, perform denoising, and put data back to the device where the buffer + * came from. + * + * The `num_samples` corresponds to the number of samples in the render buffers. It is used + * to scale buffers down to the "final" value in algorithms which don't do automatic exposure, + * or which needs "final" value for data passes. + * + * The `allow_inplace_modification` means that the denoiser is allowed to do in-place + * modification of the input passes (scaling them down i.e.). This will lower the memory + * footprint of the denoiser but will make input passes "invalid" (from path tracer) point of + * view. + * + * Returns true when all passes are denoised. Will return false if there is a denoiser error (for + * example, caused by misconfigured denoiser) or when user requested to cancel rendering. */ + virtual bool denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) = 0; + + /* Get a device which is used to perform actual denoising. + * + * Notes: + * + * - The device is lazily initialized via `load_kernels()`, so it will be nullptr until then, + * + * - The device can be different from the path tracing device. This happens, for example, when + * using OptiX denoiser and rendering on CPU. + * + * - No threading safety is ensured in this call. This means, that it is up to caller to ensure + * that there is no threading-conflict between denoising task lazily initializing the device + * and access to this device happen. */ + Device *get_denoiser_device() const; + + function is_cancelled_cb; + + bool is_cancelled() const + { + if (!is_cancelled_cb) { + return false; + } + return is_cancelled_cb(); + } + + protected: + Denoiser(Device *path_trace_device, const DenoiseParams ¶ms); + + /* Make sure denoising device is initialized. */ + virtual Device *ensure_denoiser_device(Progress *progress); + + /* Get device type mask which is used to filter available devices when new device needs to be + * created. */ + virtual uint get_device_type_mask() const = 0; + + Device *path_trace_device_; + DenoiseParams params_; + + /* Cached pointer to the device on which denoising will happen. + * Used to avoid lookup of a device for every denoising request. */ + Device *denoiser_device_ = nullptr; + + /* Denoiser device which was created to perform denoising in the case the none of the rendering + * devices are capable of denoising. */ + unique_ptr local_denoiser_device_; + bool device_creation_attempted_ = false; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_device.cpp b/intern/cycles/integrator/denoiser_device.cpp new file mode 100644 index 00000000000..2121dee500a --- /dev/null +++ b/intern/cycles/integrator/denoiser_device.cpp @@ -0,0 +1,106 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/denoiser_device.h" + +#include "device/denoise.h" +#include "device/device.h" +#include "device/memory.h" +#include "device/queue.h" +#include "session/buffers.h" +#include "util/log.h" +#include "util/progress.h" + +CCL_NAMESPACE_BEGIN + +DeviceDenoiser::DeviceDenoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : Denoiser(path_trace_device, params) +{ +} + +DeviceDenoiser::~DeviceDenoiser() +{ + /* Explicit implementation, to allow forward declaration of Device in the header. */ +} + +bool DeviceDenoiser::denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) +{ + Device *denoiser_device = get_denoiser_device(); + if (!denoiser_device) { + return false; + } + + DeviceDenoiseTask task; + task.params = params_; + task.num_samples = num_samples; + task.buffer_params = buffer_params; + task.allow_inplace_modification = allow_inplace_modification; + + RenderBuffers local_render_buffers(denoiser_device); + bool local_buffer_used = false; + + if (denoiser_device == render_buffers->buffer.device) { + /* The device can access an existing buffer pointer. */ + local_buffer_used = false; + task.render_buffers = render_buffers; + } + else { + VLOG(3) << "Creating temporary buffer on denoiser device."; + + DeviceQueue *queue = denoiser_device->get_denoise_queue(); + + /* Create buffer which is available by the device used by denoiser. */ + + /* TODO(sergey): Optimize data transfers. For example, only copy denoising related passes, + * ignoring other light ad data passes. */ + + local_buffer_used = true; + + render_buffers->copy_from_device(); + + local_render_buffers.reset(buffer_params); + + /* NOTE: The local buffer is allocated for an exact size of the effective render size, while + * the input render buffer is allocated for the lowest resolution divider possible. So it is + * important to only copy actually needed part of the input buffer. */ + memcpy(local_render_buffers.buffer.data(), + render_buffers->buffer.data(), + sizeof(float) * local_render_buffers.buffer.size()); + + queue->copy_to_device(local_render_buffers.buffer); + + task.render_buffers = &local_render_buffers; + task.allow_inplace_modification = true; + } + + const bool denoise_result = denoiser_device->denoise_buffer(task); + + if (local_buffer_used) { + local_render_buffers.copy_from_device(); + + render_buffers_host_copy_denoised( + render_buffers, buffer_params, &local_render_buffers, local_render_buffers.params); + + render_buffers->copy_to_device(); + } + + return denoise_result; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_device.h b/intern/cycles/integrator/denoiser_device.h new file mode 100644 index 00000000000..2bacecaa2a2 --- /dev/null +++ b/intern/cycles/integrator/denoiser_device.h @@ -0,0 +1,40 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/denoiser.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +/* Denoiser which uses device-specific denoising implementation, such as OptiX denoiser which are + * implemented as a part of a driver of specific device. + * + * This implementation makes sure the to-be-denoised buffer is available on the denoising device + * and invoke denoising kernel via device API. */ +class DeviceDenoiser : public Denoiser { + public: + DeviceDenoiser(Device *path_trace_device, const DenoiseParams ¶ms); + ~DeviceDenoiser(); + + virtual bool denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) override; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_oidn.cpp b/intern/cycles/integrator/denoiser_oidn.cpp new file mode 100644 index 00000000000..b09b95a11b0 --- /dev/null +++ b/intern/cycles/integrator/denoiser_oidn.cpp @@ -0,0 +1,635 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/denoiser_oidn.h" + +#include + +#include "device/device.h" +#include "device/queue.h" +#include "integrator/pass_accessor_cpu.h" +#include "session/buffers.h" +#include "util/array.h" +#include "util/log.h" +#include "util/openimagedenoise.h" + +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/kernel.h" + +CCL_NAMESPACE_BEGIN + +thread_mutex OIDNDenoiser::mutex_; + +OIDNDenoiser::OIDNDenoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : Denoiser(path_trace_device, params) +{ + DCHECK_EQ(params.type, DENOISER_OPENIMAGEDENOISE); + + DCHECK(openimagedenoise_supported()) << "OpenImageDenoiser is not supported on this platform."; +} + +#ifdef WITH_OPENIMAGEDENOISE +static bool oidn_progress_monitor_function(void *user_ptr, double /*n*/) +{ + OIDNDenoiser *oidn_denoiser = reinterpret_cast(user_ptr); + return !oidn_denoiser->is_cancelled(); +} +#endif + +#ifdef WITH_OPENIMAGEDENOISE + +class OIDNPass { + public: + OIDNPass() = default; + + OIDNPass(const BufferParams &buffer_params, + const char *name, + PassType type, + PassMode mode = PassMode::NOISY) + : name(name), type(type), mode(mode) + { + offset = buffer_params.get_pass_offset(type, mode); + need_scale = (type == PASS_DENOISING_ALBEDO || type == PASS_DENOISING_NORMAL); + + const PassInfo pass_info = Pass::get_info(type); + num_components = pass_info.num_components; + use_compositing = pass_info.use_compositing; + use_denoising_albedo = pass_info.use_denoising_albedo; + } + + inline operator bool() const + { + return name[0] != '\0'; + } + + /* Name of an image which will be passed to the OIDN library. + * Should be one of the following: color, albedo, normal, output. + * The albedo and normal images are optional. */ + const char *name = ""; + + PassType type = PASS_NONE; + PassMode mode = PassMode::NOISY; + int num_components = -1; + bool use_compositing = false; + bool use_denoising_albedo = true; + + /* Offset of beginning of this pass in the render buffers. */ + int offset = -1; + + /* Denotes whether the data is to be scaled down with the number of passes. + * Is required for albedo and normal passes. The color pass OIDN will perform auto-exposure, so + * scaling is not needed for the color pass unless adaptive sampling is used. + * + * NOTE: Do not scale the output pass, as that requires to be a pointer in the original buffer. + * All the scaling on the output needed for integration with adaptive sampling will happen + * outside of generic pass handling. */ + bool need_scale = false; + + /* The content of the pass has been pre-filtered. */ + bool is_filtered = false; + + /* For the scaled passes, the data which holds values of scaled pixels. */ + array scaled_buffer; +}; + +class OIDNDenoiseContext { + public: + OIDNDenoiseContext(OIDNDenoiser *denoiser, + const DenoiseParams &denoise_params, + const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + const bool allow_inplace_modification) + : denoiser_(denoiser), + denoise_params_(denoise_params), + buffer_params_(buffer_params), + render_buffers_(render_buffers), + num_samples_(num_samples), + allow_inplace_modification_(allow_inplace_modification), + pass_sample_count_(buffer_params_.get_pass_offset(PASS_SAMPLE_COUNT)) + { + if (denoise_params_.use_pass_albedo) { + oidn_albedo_pass_ = OIDNPass(buffer_params_, "albedo", PASS_DENOISING_ALBEDO); + } + + if (denoise_params_.use_pass_normal) { + oidn_normal_pass_ = OIDNPass(buffer_params_, "normal", PASS_DENOISING_NORMAL); + } + } + + bool need_denoising() const + { + if (buffer_params_.width == 0 && buffer_params_.height == 0) { + return false; + } + + return true; + } + + /* Make the guiding passes available by a sequential denoising of various passes. */ + void read_guiding_passes() + { + read_guiding_pass(oidn_albedo_pass_); + read_guiding_pass(oidn_normal_pass_); + } + + void denoise_pass(const PassType pass_type) + { + OIDNPass oidn_color_pass(buffer_params_, "color", pass_type); + if (oidn_color_pass.offset == PASS_UNUSED) { + return; + } + + if (oidn_color_pass.use_denoising_albedo) { + if (albedo_replaced_with_fake_) { + LOG(ERROR) << "Pass which requires albedo is denoised after fake albedo has been set."; + return; + } + } + + OIDNPass oidn_output_pass(buffer_params_, "output", pass_type, PassMode::DENOISED); + if (oidn_output_pass.offset == PASS_UNUSED) { + LOG(DFATAL) << "Missing denoised pass " << pass_type_as_string(pass_type); + return; + } + + OIDNPass oidn_color_access_pass = read_input_pass(oidn_color_pass, oidn_output_pass); + + oidn::DeviceRef oidn_device = oidn::newDevice(); + oidn_device.set("setAffinity", false); + oidn_device.commit(); + + /* Create a filter for denoising a beauty (color) image using prefiltered auxiliary images too. + */ + oidn::FilterRef oidn_filter = oidn_device.newFilter("RT"); + set_input_pass(oidn_filter, oidn_color_access_pass); + set_guiding_passes(oidn_filter, oidn_color_pass); + set_output_pass(oidn_filter, oidn_output_pass); + oidn_filter.setProgressMonitorFunction(oidn_progress_monitor_function, denoiser_); + oidn_filter.set("hdr", true); + oidn_filter.set("srgb", false); + if (denoise_params_.prefilter == DENOISER_PREFILTER_NONE || + denoise_params_.prefilter == DENOISER_PREFILTER_ACCURATE) { + oidn_filter.set("cleanAux", true); + } + oidn_filter.commit(); + + filter_guiding_pass_if_needed(oidn_device, oidn_albedo_pass_); + filter_guiding_pass_if_needed(oidn_device, oidn_normal_pass_); + + /* Filter the beauty image. */ + oidn_filter.execute(); + + /* Check for errors. */ + const char *error_message; + const oidn::Error error = oidn_device.getError(error_message); + if (error != oidn::Error::None && error != oidn::Error::Cancelled) { + LOG(ERROR) << "OpenImageDenoise error: " << error_message; + } + + postprocess_output(oidn_color_pass, oidn_output_pass); + } + + protected: + void filter_guiding_pass_if_needed(oidn::DeviceRef &oidn_device, OIDNPass &oidn_pass) + { + if (denoise_params_.prefilter != DENOISER_PREFILTER_ACCURATE || !oidn_pass || + oidn_pass.is_filtered) { + return; + } + + oidn::FilterRef oidn_filter = oidn_device.newFilter("RT"); + set_pass(oidn_filter, oidn_pass); + set_output_pass(oidn_filter, oidn_pass); + oidn_filter.commit(); + oidn_filter.execute(); + + oidn_pass.is_filtered = true; + } + + /* Make pixels of a guiding pass available by the denoiser. */ + void read_guiding_pass(OIDNPass &oidn_pass) + { + if (!oidn_pass) { + return; + } + + DCHECK(!oidn_pass.use_compositing); + + if (denoise_params_.prefilter != DENOISER_PREFILTER_ACCURATE && + !is_pass_scale_needed(oidn_pass)) { + /* Pass data is available as-is from the render buffers. */ + return; + } + + if (allow_inplace_modification_) { + scale_pass_in_render_buffers(oidn_pass); + return; + } + + read_pass_pixels_into_buffer(oidn_pass); + } + + /* Special reader of the input pass. + * To save memory it will read pixels into the output, and let the denoiser to perform an + * in-place operation. */ + OIDNPass read_input_pass(OIDNPass &oidn_input_pass, const OIDNPass &oidn_output_pass) + { + const bool use_compositing = oidn_input_pass.use_compositing; + + /* Simple case: no compositing is involved, no scaling is needed. + * The pass pixels will be referenced as-is, without extra processing. */ + if (!use_compositing && !is_pass_scale_needed(oidn_input_pass)) { + return oidn_input_pass; + } + + float *buffer_data = render_buffers_->buffer.data(); + float *pass_data = buffer_data + oidn_output_pass.offset; + + PassAccessor::Destination destination(pass_data, 3); + destination.pixel_stride = buffer_params_.pass_stride; + + read_pass_pixels(oidn_input_pass, destination); + + OIDNPass oidn_input_pass_at_output = oidn_input_pass; + oidn_input_pass_at_output.offset = oidn_output_pass.offset; + + return oidn_input_pass_at_output; + } + + /* Read pass pixels using PassAccessor into the given destination. */ + void read_pass_pixels(const OIDNPass &oidn_pass, const PassAccessor::Destination &destination) + { + PassAccessor::PassAccessInfo pass_access_info; + pass_access_info.type = oidn_pass.type; + pass_access_info.mode = oidn_pass.mode; + pass_access_info.offset = oidn_pass.offset; + + /* Denoiser operates on passes which are used to calculate the approximation, and is never used + * on the approximation. The latter is not even possible because OIDN does not support + * denoising of semi-transparent pixels. */ + pass_access_info.use_approximate_shadow_catcher = false; + pass_access_info.use_approximate_shadow_catcher_background = false; + pass_access_info.show_active_pixels = false; + + /* OIDN will perform an auto-exposure, so it is not required to know exact exposure configured + * by users. What is important is to use same exposure for read and write access of the pass + * pixels. */ + const PassAccessorCPU pass_accessor(pass_access_info, 1.0f, num_samples_); + + BufferParams buffer_params = buffer_params_; + buffer_params.window_x = 0; + buffer_params.window_y = 0; + buffer_params.window_width = buffer_params.width; + buffer_params.window_height = buffer_params.height; + + pass_accessor.get_render_tile_pixels(render_buffers_, buffer_params, destination); + } + + /* Read pass pixels using PassAccessor into a temporary buffer which is owned by the pass.. */ + void read_pass_pixels_into_buffer(OIDNPass &oidn_pass) + { + VLOG(3) << "Allocating temporary buffer for pass " << oidn_pass.name << " (" + << pass_type_as_string(oidn_pass.type) << ")"; + + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + array &scaled_buffer = oidn_pass.scaled_buffer; + scaled_buffer.resize(width * height * 3); + + const PassAccessor::Destination destination(scaled_buffer.data(), 3); + + read_pass_pixels(oidn_pass, destination); + } + + /* Set OIDN image to reference pixels from the given render buffer pass. + * No transform to the pixels is done, no additional memory is used. */ + void set_pass_referenced(oidn::FilterRef &oidn_filter, + const char *name, + const OIDNPass &oidn_pass) + { + const int64_t x = buffer_params_.full_x; + const int64_t y = buffer_params_.full_y; + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + const int64_t offset = buffer_params_.offset; + const int64_t stride = buffer_params_.stride; + const int64_t pass_stride = buffer_params_.pass_stride; + + const int64_t pixel_index = offset + x + y * stride; + const int64_t buffer_offset = pixel_index * pass_stride; + + float *buffer_data = render_buffers_->buffer.data(); + + oidn_filter.setImage(name, + buffer_data + buffer_offset + oidn_pass.offset, + oidn::Format::Float3, + width, + height, + 0, + pass_stride * sizeof(float), + stride * pass_stride * sizeof(float)); + } + + void set_pass_from_buffer(oidn::FilterRef &oidn_filter, const char *name, OIDNPass &oidn_pass) + { + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + oidn_filter.setImage( + name, oidn_pass.scaled_buffer.data(), oidn::Format::Float3, width, height, 0, 0, 0); + } + + void set_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + set_pass(oidn_filter, oidn_pass.name, oidn_pass); + } + void set_pass(oidn::FilterRef &oidn_filter, const char *name, OIDNPass &oidn_pass) + { + if (oidn_pass.scaled_buffer.empty()) { + set_pass_referenced(oidn_filter, name, oidn_pass); + } + else { + set_pass_from_buffer(oidn_filter, name, oidn_pass); + } + } + + void set_input_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + set_pass_referenced(oidn_filter, oidn_pass.name, oidn_pass); + } + + void set_guiding_passes(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + if (oidn_albedo_pass_) { + if (oidn_pass.use_denoising_albedo) { + set_pass(oidn_filter, oidn_albedo_pass_); + } + else { + /* NOTE: OpenImageDenoise library implicitly expects albedo pass when normal pass has been + * provided. */ + set_fake_albedo_pass(oidn_filter); + } + } + + if (oidn_normal_pass_) { + set_pass(oidn_filter, oidn_normal_pass_); + } + } + + void set_fake_albedo_pass(oidn::FilterRef &oidn_filter) + { + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + + if (!albedo_replaced_with_fake_) { + const int64_t num_pixel_components = width * height * 3; + oidn_albedo_pass_.scaled_buffer.resize(num_pixel_components); + + for (int i = 0; i < num_pixel_components; ++i) { + oidn_albedo_pass_.scaled_buffer[i] = 0.5f; + } + + albedo_replaced_with_fake_ = true; + } + + set_pass(oidn_filter, oidn_albedo_pass_); + } + + void set_output_pass(oidn::FilterRef &oidn_filter, OIDNPass &oidn_pass) + { + set_pass(oidn_filter, "output", oidn_pass); + } + + /* Scale output pass to match adaptive sampling per-pixel scale, as well as bring alpha channel + * back. */ + void postprocess_output(const OIDNPass &oidn_input_pass, const OIDNPass &oidn_output_pass) + { + kernel_assert(oidn_input_pass.num_components == oidn_output_pass.num_components); + + const int64_t x = buffer_params_.full_x; + const int64_t y = buffer_params_.full_y; + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + const int64_t offset = buffer_params_.offset; + const int64_t stride = buffer_params_.stride; + const int64_t pass_stride = buffer_params_.pass_stride; + const int64_t row_stride = stride * pass_stride; + + const int64_t pixel_offset = offset + x + y * stride; + const int64_t buffer_offset = (pixel_offset * pass_stride); + + float *buffer_data = render_buffers_->buffer.data(); + + const bool has_pass_sample_count = (pass_sample_count_ != PASS_UNUSED); + const bool need_scale = has_pass_sample_count || oidn_input_pass.use_compositing; + + for (int y = 0; y < height; ++y) { + float *buffer_row = buffer_data + buffer_offset + y * row_stride; + for (int x = 0; x < width; ++x) { + float *buffer_pixel = buffer_row + x * pass_stride; + float *denoised_pixel = buffer_pixel + oidn_output_pass.offset; + + if (need_scale) { + const float pixel_scale = has_pass_sample_count ? + __float_as_uint(buffer_pixel[pass_sample_count_]) : + num_samples_; + + denoised_pixel[0] = denoised_pixel[0] * pixel_scale; + denoised_pixel[1] = denoised_pixel[1] * pixel_scale; + denoised_pixel[2] = denoised_pixel[2] * pixel_scale; + } + + if (oidn_output_pass.num_components == 3) { + /* Pass without alpha channel. */ + } + else if (!oidn_input_pass.use_compositing) { + /* Currently compositing passes are either 3-component (derived by dividing light passes) + * or do not have transparency (shadow catcher). Implicitly rely on this logic, as it + * simplifies logic and avoids extra memory allocation. */ + const float *noisy_pixel = buffer_pixel + oidn_input_pass.offset; + denoised_pixel[3] = noisy_pixel[3]; + } + else { + /* Assigning to zero since this is a default alpha value for 3-component passes, and it + * is an opaque pixel for 4 component passes. */ + denoised_pixel[3] = 0; + } + } + } + } + + bool is_pass_scale_needed(OIDNPass &oidn_pass) const + { + if (pass_sample_count_ != PASS_UNUSED) { + /* With adaptive sampling pixels will have different number of samples in them, so need to + * always scale the pass to make pixels uniformly sampled. */ + return true; + } + + if (!oidn_pass.need_scale) { + return false; + } + + if (num_samples_ == 1) { + /* If the avoid scaling if there is only one sample, to save up time (so we don't divide + * buffer by 1). */ + return false; + } + + return true; + } + + void scale_pass_in_render_buffers(OIDNPass &oidn_pass) + { + const int64_t x = buffer_params_.full_x; + const int64_t y = buffer_params_.full_y; + const int64_t width = buffer_params_.width; + const int64_t height = buffer_params_.height; + const int64_t offset = buffer_params_.offset; + const int64_t stride = buffer_params_.stride; + const int64_t pass_stride = buffer_params_.pass_stride; + const int64_t row_stride = stride * pass_stride; + + const int64_t pixel_offset = offset + x + y * stride; + const int64_t buffer_offset = (pixel_offset * pass_stride); + + float *buffer_data = render_buffers_->buffer.data(); + + const bool has_pass_sample_count = (pass_sample_count_ != PASS_UNUSED); + + for (int y = 0; y < height; ++y) { + float *buffer_row = buffer_data + buffer_offset + y * row_stride; + for (int x = 0; x < width; ++x) { + float *buffer_pixel = buffer_row + x * pass_stride; + float *pass_pixel = buffer_pixel + oidn_pass.offset; + + const float pixel_scale = 1.0f / (has_pass_sample_count ? + __float_as_uint(buffer_pixel[pass_sample_count_]) : + num_samples_); + + pass_pixel[0] = pass_pixel[0] * pixel_scale; + pass_pixel[1] = pass_pixel[1] * pixel_scale; + pass_pixel[2] = pass_pixel[2] * pixel_scale; + } + } + } + + OIDNDenoiser *denoiser_ = nullptr; + + const DenoiseParams &denoise_params_; + const BufferParams &buffer_params_; + RenderBuffers *render_buffers_ = nullptr; + int num_samples_ = 0; + bool allow_inplace_modification_ = false; + int pass_sample_count_ = PASS_UNUSED; + + /* Optional albedo and normal passes, reused by denoising of different pass types. */ + OIDNPass oidn_albedo_pass_; + OIDNPass oidn_normal_pass_; + + /* For passes which don't need albedo channel for denoising we replace the actual albedo with + * the (0.5, 0.5, 0.5). This flag indicates that the real albedo pass has been replaced with + * the fake values and denoising of passes which do need albedo can no longer happen. */ + bool albedo_replaced_with_fake_ = false; +}; +#endif + +static unique_ptr create_device_queue(const RenderBuffers *render_buffers) +{ + Device *device = render_buffers->buffer.device; + if (device->info.has_gpu_queue) { + return device->gpu_queue_create(); + } + return nullptr; +} + +static void copy_render_buffers_from_device(unique_ptr &queue, + RenderBuffers *render_buffers) +{ + if (queue) { + queue->copy_from_device(render_buffers->buffer); + queue->synchronize(); + } + else { + render_buffers->copy_from_device(); + } +} + +static void copy_render_buffers_to_device(unique_ptr &queue, + RenderBuffers *render_buffers) +{ + if (queue) { + queue->copy_to_device(render_buffers->buffer); + queue->synchronize(); + } + else { + render_buffers->copy_to_device(); + } +} + +bool OIDNDenoiser::denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) +{ + thread_scoped_lock lock(mutex_); + + /* Make sure the host-side data is available for denoising. */ + unique_ptr queue = create_device_queue(render_buffers); + copy_render_buffers_from_device(queue, render_buffers); + +#ifdef WITH_OPENIMAGEDENOISE + OIDNDenoiseContext context( + this, params_, buffer_params, render_buffers, num_samples, allow_inplace_modification); + + if (context.need_denoising()) { + context.read_guiding_passes(); + + const std::array passes = { + {/* Passes which will use real albedo when it is available. */ + PASS_COMBINED, + PASS_SHADOW_CATCHER_MATTE, + + /* Passes which do not need albedo and hence if real is present it needs to become fake. + */ + PASS_SHADOW_CATCHER}}; + + for (const PassType pass_type : passes) { + context.denoise_pass(pass_type); + if (is_cancelled()) { + return false; + } + } + + /* TODO: It may be possible to avoid this copy, but we have to ensure that when other code + * copies data from the device it doesn't overwrite the denoiser buffers. */ + copy_render_buffers_to_device(queue, render_buffers); + } +#endif + + /* This code is not supposed to run when compiled without OIDN support, so can assume if we made + * it up here all passes are properly denoised. */ + return true; +} + +uint OIDNDenoiser::get_device_type_mask() const +{ + return DEVICE_MASK_CPU; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_oidn.h b/intern/cycles/integrator/denoiser_oidn.h new file mode 100644 index 00000000000..a0ec3e26b9c --- /dev/null +++ b/intern/cycles/integrator/denoiser_oidn.h @@ -0,0 +1,47 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/denoiser.h" +#include "util/thread.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +/* Implementation of denoising API which uses OpenImageDenoise library. */ +class OIDNDenoiser : public Denoiser { + public: + /* Forwardly declared state which might be using compile-flag specific fields, such as + * OpenImageDenoise device and filter handles. */ + class State; + + OIDNDenoiser(Device *path_trace_device, const DenoiseParams ¶ms); + + virtual bool denoise_buffer(const BufferParams &buffer_params, + RenderBuffers *render_buffers, + const int num_samples, + bool allow_inplace_modification) override; + + protected: + virtual uint get_device_type_mask() const override; + + /* We only perform one denoising at a time, since OpenImageDenoise itself is multithreaded. + * Use this mutex whenever images are passed to the OIDN and needs to be denoised. */ + static thread_mutex mutex_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_optix.cpp b/intern/cycles/integrator/denoiser_optix.cpp new file mode 100644 index 00000000000..ebd95d62ae4 --- /dev/null +++ b/intern/cycles/integrator/denoiser_optix.cpp @@ -0,0 +1,34 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/denoiser_optix.h" + +#include "device/denoise.h" +#include "device/device.h" + +CCL_NAMESPACE_BEGIN + +OptiXDenoiser::OptiXDenoiser(Device *path_trace_device, const DenoiseParams ¶ms) + : DeviceDenoiser(path_trace_device, params) +{ +} + +uint OptiXDenoiser::get_device_type_mask() const +{ + return DEVICE_MASK_OPTIX; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/denoiser_optix.h b/intern/cycles/integrator/denoiser_optix.h new file mode 100644 index 00000000000..a8df770ecf7 --- /dev/null +++ b/intern/cycles/integrator/denoiser_optix.h @@ -0,0 +1,31 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/denoiser_device.h" + +CCL_NAMESPACE_BEGIN + +class OptiXDenoiser : public DeviceDenoiser { + public: + OptiXDenoiser(Device *path_trace_device, const DenoiseParams ¶ms); + + protected: + virtual uint get_device_type_mask() const override; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor.cpp b/intern/cycles/integrator/pass_accessor.cpp new file mode 100644 index 00000000000..7e19de51daa --- /dev/null +++ b/intern/cycles/integrator/pass_accessor.cpp @@ -0,0 +1,318 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/pass_accessor.h" + +#include "session/buffers.h" +#include "util/log.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/types.h" +// clang-format on + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Pass input information. + */ + +PassAccessor::PassAccessInfo::PassAccessInfo(const BufferPass &pass) + : type(pass.type), mode(pass.mode), include_albedo(pass.include_albedo), offset(pass.offset) +{ +} + +/* -------------------------------------------------------------------- + * Pass destination. + */ + +PassAccessor::Destination::Destination(float *pixels, int num_components) + : pixels(pixels), num_components(num_components) +{ +} + +PassAccessor::Destination::Destination(const PassType pass_type, half4 *pixels) + : Destination(pass_type) +{ + pixels_half_rgba = pixels; +} + +PassAccessor::Destination::Destination(const PassType pass_type) +{ + const PassInfo pass_info = Pass::get_info(pass_type); + num_components = pass_info.num_components; +} + +/* -------------------------------------------------------------------- + * Pass source. + */ + +PassAccessor::Source::Source(const float *pixels, int num_components) + : pixels(pixels), num_components(num_components) +{ +} + +/* -------------------------------------------------------------------- + * Pass accessor. + */ + +PassAccessor::PassAccessor(const PassAccessInfo &pass_access_info, float exposure, int num_samples) + : pass_access_info_(pass_access_info), exposure_(exposure), num_samples_(num_samples) +{ +} + +bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers, + const Destination &destination) const +{ + if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { + return false; + } + + return get_render_tile_pixels(render_buffers, render_buffers->params, destination); +} + +static void pad_pixels(const BufferParams &buffer_params, + const PassAccessor::Destination &destination, + const int src_num_components) +{ + /* When requesting a single channel pass as RGBA, or RGB pass as RGBA, + * fill in the additional components for convenience. */ + const int dest_num_components = destination.num_components; + + if (src_num_components >= dest_num_components) { + return; + } + + const size_t size = static_cast(buffer_params.width) * buffer_params.height; + if (destination.pixels) { + const size_t pixel_stride = destination.pixel_stride ? destination.pixel_stride : + destination.num_components; + + float *pixel = destination.pixels + pixel_stride * destination.offset; + + for (size_t i = 0; i < size; i++, pixel += dest_num_components) { + if (dest_num_components >= 3 && src_num_components == 1) { + pixel[1] = pixel[0]; + pixel[2] = pixel[0]; + } + if (dest_num_components >= 4) { + pixel[3] = 1.0f; + } + } + } + + if (destination.pixels_half_rgba) { + const half one = float_to_half_display(1.0f); + half4 *pixel = destination.pixels_half_rgba + destination.offset; + + for (size_t i = 0; i < size; i++, pixel++) { + if (dest_num_components >= 3 && src_num_components == 1) { + pixel[0].y = pixel[0].x; + pixel[0].z = pixel[0].x; + } + if (dest_num_components >= 4) { + pixel[0].w = one; + } + } + } +} + +bool PassAccessor::get_render_tile_pixels(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const +{ + if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { + return false; + } + + if (pass_access_info_.offset == PASS_UNUSED) { + return false; + } + + const PassType type = pass_access_info_.type; + const PassMode mode = pass_access_info_.mode; + const PassInfo pass_info = Pass::get_info(type, pass_access_info_.include_albedo); + + if (pass_info.num_components == 1) { + /* Single channel passes. */ + if (mode == PassMode::DENOISED) { + /* Denoised passes store their final pixels, no need in special calculation. */ + get_pass_float(render_buffers, buffer_params, destination); + } + else if (type == PASS_DEPTH) { + get_pass_depth(render_buffers, buffer_params, destination); + } + else if (type == PASS_MIST) { + get_pass_mist(render_buffers, buffer_params, destination); + } + else if (type == PASS_SAMPLE_COUNT) { + get_pass_sample_count(render_buffers, buffer_params, destination); + } + else { + get_pass_float(render_buffers, buffer_params, destination); + } + } + else if (type == PASS_MOTION) { + /* Motion pass. */ + DCHECK_EQ(destination.num_components, 4) << "Motion pass must have 4 components"; + get_pass_motion(render_buffers, buffer_params, destination); + } + else if (type == PASS_CRYPTOMATTE) { + /* Cryptomatte pass. */ + DCHECK_EQ(destination.num_components, 4) << "Cryptomatte pass must have 4 components"; + get_pass_cryptomatte(render_buffers, buffer_params, destination); + } + else { + /* RGB, RGBA and vector passes. */ + DCHECK(destination.num_components == 3 || destination.num_components == 4) + << pass_type_as_string(type) << " pass must have 3 or 4 components"; + + if (type == PASS_SHADOW_CATCHER_MATTE && pass_access_info_.use_approximate_shadow_catcher) { + /* Denoised matte with shadow needs to do calculation (will use denoised shadow catcher pass + * to approximate shadow with). */ + get_pass_shadow_catcher_matte_with_shadow(render_buffers, buffer_params, destination); + } + else if (type == PASS_SHADOW_CATCHER && mode != PassMode::DENOISED) { + /* Shadow catcher pass. */ + get_pass_shadow_catcher(render_buffers, buffer_params, destination); + } + else if ((pass_info.divide_type != PASS_NONE || pass_info.direct_type != PASS_NONE || + pass_info.indirect_type != PASS_NONE) && + mode != PassMode::DENOISED) { + /* RGB lighting passes that need to divide out color and/or sum direct and indirect. */ + get_pass_light_path(render_buffers, buffer_params, destination); + } + else { + /* Passes that need no special computation, or denoised passes that already + * had the computation done. */ + if (pass_info.num_components == 3) { + get_pass_float3(render_buffers, buffer_params, destination); + } + else if (pass_info.num_components == 4) { + if (destination.num_components == 3) { + /* Special case for denoiser access of RGBA passes ignoring alpha channel. */ + get_pass_float3(render_buffers, buffer_params, destination); + } + else if (type == PASS_COMBINED || type == PASS_SHADOW_CATCHER || + type == PASS_SHADOW_CATCHER_MATTE) { + /* Passes with transparency as 4th component. */ + get_pass_combined(render_buffers, buffer_params, destination); + } + else { + /* Passes with alpha as 4th component. */ + get_pass_float4(render_buffers, buffer_params, destination); + } + } + } + } + + pad_pixels(buffer_params, destination, pass_info.num_components); + + return true; +} + +void PassAccessor::init_kernel_film_convert(KernelFilmConvert *kfilm_convert, + const BufferParams &buffer_params, + const Destination &destination) const +{ + const PassMode mode = pass_access_info_.mode; + const PassInfo &pass_info = Pass::get_info(pass_access_info_.type, + pass_access_info_.include_albedo); + + kfilm_convert->pass_offset = pass_access_info_.offset; + kfilm_convert->pass_stride = buffer_params.pass_stride; + + kfilm_convert->pass_use_exposure = pass_info.use_exposure; + kfilm_convert->pass_use_filter = pass_info.use_filter; + + /* TODO(sergey): Some of the passes needs to become denoised when denoised pass is accessed. */ + if (pass_info.direct_type != PASS_NONE) { + kfilm_convert->pass_offset = buffer_params.get_pass_offset(pass_info.direct_type); + } + kfilm_convert->pass_indirect = buffer_params.get_pass_offset(pass_info.indirect_type); + kfilm_convert->pass_divide = buffer_params.get_pass_offset(pass_info.divide_type); + + kfilm_convert->pass_combined = buffer_params.get_pass_offset(PASS_COMBINED); + kfilm_convert->pass_sample_count = buffer_params.get_pass_offset(PASS_SAMPLE_COUNT); + kfilm_convert->pass_adaptive_aux_buffer = buffer_params.get_pass_offset( + PASS_ADAPTIVE_AUX_BUFFER); + kfilm_convert->pass_motion_weight = buffer_params.get_pass_offset(PASS_MOTION_WEIGHT); + kfilm_convert->pass_shadow_catcher = buffer_params.get_pass_offset(PASS_SHADOW_CATCHER, mode); + kfilm_convert->pass_shadow_catcher_sample_count = buffer_params.get_pass_offset( + PASS_SHADOW_CATCHER_SAMPLE_COUNT); + kfilm_convert->pass_shadow_catcher_matte = buffer_params.get_pass_offset( + PASS_SHADOW_CATCHER_MATTE, mode); + + /* Background is not denoised, so always use noisy pass. */ + kfilm_convert->pass_background = buffer_params.get_pass_offset(PASS_BACKGROUND); + + if (pass_info.use_filter) { + kfilm_convert->scale = num_samples_ != 0 ? 1.0f / num_samples_ : 0.0f; + } + else { + kfilm_convert->scale = 1.0f; + } + + if (pass_info.use_exposure) { + kfilm_convert->exposure = exposure_; + } + else { + kfilm_convert->exposure = 1.0f; + } + + kfilm_convert->scale_exposure = kfilm_convert->scale * kfilm_convert->exposure; + + kfilm_convert->use_approximate_shadow_catcher = pass_access_info_.use_approximate_shadow_catcher; + kfilm_convert->use_approximate_shadow_catcher_background = + pass_access_info_.use_approximate_shadow_catcher_background; + kfilm_convert->show_active_pixels = pass_access_info_.show_active_pixels; + + kfilm_convert->num_components = destination.num_components; + kfilm_convert->pixel_stride = destination.pixel_stride ? destination.pixel_stride : + destination.num_components; + + kfilm_convert->is_denoised = (mode == PassMode::DENOISED); +} + +bool PassAccessor::set_render_tile_pixels(RenderBuffers *render_buffers, const Source &source) +{ + if (render_buffers == nullptr || render_buffers->buffer.data() == nullptr) { + return false; + } + + const PassInfo pass_info = Pass::get_info(pass_access_info_.type, + pass_access_info_.include_albedo); + + const BufferParams &buffer_params = render_buffers->params; + + float *buffer_data = render_buffers->buffer.data(); + const int size = buffer_params.width * buffer_params.height; + + const int out_stride = buffer_params.pass_stride; + const int in_stride = source.num_components; + const int num_components_to_copy = min(source.num_components, pass_info.num_components); + + float *out = buffer_data + pass_access_info_.offset; + const float *in = source.pixels + source.offset * in_stride; + + for (int i = 0; i < size; i++, out += out_stride, in += in_stride) { + memcpy(out, in, sizeof(float) * num_components_to_copy); + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor.h b/intern/cycles/integrator/pass_accessor.h new file mode 100644 index 00000000000..09eae0156c9 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor.h @@ -0,0 +1,160 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "scene/pass.h" +#include "util/half.h" +#include "util/string.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +class RenderBuffers; +class BufferPass; +class BufferParams; +struct KernelFilmConvert; + +/* Helper class which allows to access pass data. + * Is designed in a way that it is created once when the pass data is known, and then pixels gets + * progressively update from various render buffers. */ +class PassAccessor { + public: + class PassAccessInfo { + public: + PassAccessInfo() = default; + explicit PassAccessInfo(const BufferPass &pass); + + PassType type = PASS_NONE; + PassMode mode = PassMode::NOISY; + bool include_albedo = false; + int offset = -1; + + /* For the shadow catcher matte pass: whether to approximate shadow catcher pass into its + * matte pass, so that both artificial objects and shadows can be alpha-overed onto a backdrop. + */ + bool use_approximate_shadow_catcher = false; + + /* When approximate shadow catcher matte is used alpha-over the result on top of background. */ + bool use_approximate_shadow_catcher_background = false; + + bool show_active_pixels = false; + }; + + class Destination { + public: + Destination() = default; + Destination(float *pixels, int num_components); + Destination(const PassType pass_type, half4 *pixels); + + /* Destination will be initialized with the number of components which is native for the given + * pass type. */ + explicit Destination(const PassType pass_type); + + /* CPU-side pointers. only usable by the `PassAccessorCPU`. */ + float *pixels = nullptr; + half4 *pixels_half_rgba = nullptr; + + /* Device-side pointers. */ + device_ptr d_pixels = 0; + device_ptr d_pixels_half_rgba = 0; + + /* Number of components per pixel in the floating-point destination. + * Is ignored for half4 destination (where number of components is implied to be 4). */ + int num_components = 0; + + /* Offset in pixels from the beginning of pixels storage. + * Allows to get pixels of render buffer into a partial slice of the destination. */ + int offset = 0; + + /* Number of floats per pixel. When zero is the same as `num_components`. + * + * NOTE: Is ignored for half4 destination, as the half4 pixels are always 4-component + * half-floats. */ + int pixel_stride = 0; + + /* Row stride in pixel elements: + * - For the float destination stride is a number of floats per row. + * - For the half4 destination stride is a number of half4 per row. */ + int stride = 0; + }; + + class Source { + public: + Source() = default; + Source(const float *pixels, int num_components); + + /* CPU-side pointers. only usable by the `PassAccessorCPU`. */ + const float *pixels = nullptr; + int num_components = 0; + + /* Offset in pixels from the beginning of pixels storage. + * Allows to get pixels of render buffer into a partial slice of the destination. */ + int offset = 0; + }; + + PassAccessor(const PassAccessInfo &pass_access_info, float exposure, int num_samples); + + virtual ~PassAccessor() = default; + + /* Get pass data from the given render buffers, perform needed filtering, and store result into + * the pixels. + * The result is stored sequentially starting from the very beginning of the pixels memory. */ + bool get_render_tile_pixels(const RenderBuffers *render_buffers, + const Destination &destination) const; + bool get_render_tile_pixels(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const; + /* Set pass data for the given render buffers. Used for baking to read from passes. */ + bool set_render_tile_pixels(RenderBuffers *render_buffers, const Source &source); + + protected: + virtual void init_kernel_film_convert(KernelFilmConvert *kfilm_convert, + const BufferParams &buffer_params, + const Destination &destination) const; + +#define DECLARE_PASS_ACCESSOR(pass) \ + virtual void get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const = 0; + + /* Float (scalar) passes. */ + DECLARE_PASS_ACCESSOR(depth) + DECLARE_PASS_ACCESSOR(mist) + DECLARE_PASS_ACCESSOR(sample_count) + DECLARE_PASS_ACCESSOR(float) + + /* Float3 passes. */ + DECLARE_PASS_ACCESSOR(light_path) + DECLARE_PASS_ACCESSOR(shadow_catcher) + DECLARE_PASS_ACCESSOR(float3) + + /* Float4 passes. */ + DECLARE_PASS_ACCESSOR(motion) + DECLARE_PASS_ACCESSOR(cryptomatte) + DECLARE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow) + DECLARE_PASS_ACCESSOR(combined) + DECLARE_PASS_ACCESSOR(float4) + +#undef DECLARE_PASS_ACCESSOR + + PassAccessInfo pass_access_info_; + + float exposure_ = 0.0f; + int num_samples_ = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_cpu.cpp b/intern/cycles/integrator/pass_accessor_cpu.cpp new file mode 100644 index 00000000000..820da757be0 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_cpu.cpp @@ -0,0 +1,190 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/pass_accessor_cpu.h" + +#include "session/buffers.h" +#include "util/log.h" +#include "util/tbb.h" + +// clang-format off +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" +#include "kernel/types.h" +#include "kernel/film/read.h" +// clang-format on + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Kernel processing. + */ + +template +inline void PassAccessorCPU::run_get_pass_kernel_processor(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const +{ + KernelFilmConvert kfilm_convert; + init_kernel_film_convert(&kfilm_convert, buffer_params, destination); + + if (destination.pixels) { + /* NOTE: No overlays are applied since they are not used for final renders. + * Can be supported via some sort of specialization to avoid code duplication. */ + + run_get_pass_kernel_processor_float( + &kfilm_convert, render_buffers, buffer_params, destination, processor); + } + + if (destination.pixels_half_rgba) { + /* TODO(sergey): Consider adding specialization to avoid per-pixel overlay check. */ + + if (destination.num_components == 1) { + run_get_pass_kernel_processor_half_rgba(&kfilm_convert, + render_buffers, + buffer_params, + destination, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + float pixel; + processor(kfilm_convert, buffer, &pixel); + + pixel_rgba[0] = pixel; + pixel_rgba[1] = pixel; + pixel_rgba[2] = pixel; + pixel_rgba[3] = 1.0f; + }); + } + else if (destination.num_components == 3) { + run_get_pass_kernel_processor_half_rgba(&kfilm_convert, + render_buffers, + buffer_params, + destination, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + processor(kfilm_convert, buffer, pixel_rgba); + pixel_rgba[3] = 1.0f; + }); + } + else if (destination.num_components == 4) { + run_get_pass_kernel_processor_half_rgba( + &kfilm_convert, render_buffers, buffer_params, destination, processor); + } + } +} + +template +inline void PassAccessorCPU::run_get_pass_kernel_processor_float( + const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const +{ + DCHECK_EQ(destination.stride, 0) << "Custom stride for float destination is not implemented."; + + const int64_t pass_stride = buffer_params.pass_stride; + const int64_t buffer_row_stride = buffer_params.stride * buffer_params.pass_stride; + + const float *window_data = render_buffers->buffer.data() + buffer_params.window_x * pass_stride + + buffer_params.window_y * buffer_row_stride; + + const int pixel_stride = destination.pixel_stride ? destination.pixel_stride : + destination.num_components; + + tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) { + const float *buffer = window_data + y * buffer_row_stride; + float *pixel = destination.pixels + + (y * buffer_params.width + destination.offset) * pixel_stride; + + for (int64_t x = 0; x < buffer_params.window_width; + ++x, buffer += pass_stride, pixel += pixel_stride) { + processor(kfilm_convert, buffer, pixel); + } + }); +} + +template +inline void PassAccessorCPU::run_get_pass_kernel_processor_half_rgba( + const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const +{ + const int64_t pass_stride = buffer_params.pass_stride; + const int64_t buffer_row_stride = buffer_params.stride * buffer_params.pass_stride; + + const float *window_data = render_buffers->buffer.data() + buffer_params.window_x * pass_stride + + buffer_params.window_y * buffer_row_stride; + + half4 *dst_start = destination.pixels_half_rgba + destination.offset; + const int destination_stride = destination.stride != 0 ? destination.stride : + buffer_params.width; + + tbb::parallel_for(0, buffer_params.window_height, [&](int64_t y) { + const float *buffer = window_data + y * buffer_row_stride; + half4 *pixel = dst_start + y * destination_stride; + for (int64_t x = 0; x < buffer_params.window_width; ++x, buffer += pass_stride, ++pixel) { + + float pixel_rgba[4]; + processor(kfilm_convert, buffer, pixel_rgba); + + film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel_rgba); + + *pixel = float4_to_half4_display( + make_float4(pixel_rgba[0], pixel_rgba[1], pixel_rgba[2], pixel_rgba[3])); + } + }); +} + +/* -------------------------------------------------------------------- + * Pass accessors. + */ + +#define DEFINE_PASS_ACCESSOR(pass) \ + void PassAccessorCPU::get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const \ + { \ + run_get_pass_kernel_processor( \ + render_buffers, buffer_params, destination, film_get_pass_pixel_##pass); \ + } + +/* Float (scalar) passes. */ +DEFINE_PASS_ACCESSOR(depth) +DEFINE_PASS_ACCESSOR(mist) +DEFINE_PASS_ACCESSOR(sample_count) +DEFINE_PASS_ACCESSOR(float) + +/* Float3 passes. */ +DEFINE_PASS_ACCESSOR(light_path) +DEFINE_PASS_ACCESSOR(shadow_catcher) +DEFINE_PASS_ACCESSOR(float3) + +/* Float4 passes. */ +DEFINE_PASS_ACCESSOR(motion) +DEFINE_PASS_ACCESSOR(cryptomatte) +DEFINE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow) +DEFINE_PASS_ACCESSOR(combined) +DEFINE_PASS_ACCESSOR(float4) + +#undef DEFINE_PASS_ACCESSOR + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_cpu.h b/intern/cycles/integrator/pass_accessor_cpu.h new file mode 100644 index 00000000000..0313dc5bb0d --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_cpu.h @@ -0,0 +1,77 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/pass_accessor.h" + +CCL_NAMESPACE_BEGIN + +struct KernelFilmConvert; + +/* Pass accessor implementation for CPU side. */ +class PassAccessorCPU : public PassAccessor { + public: + using PassAccessor::PassAccessor; + + protected: + template + inline void run_get_pass_kernel_processor(const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const; + + template + inline void run_get_pass_kernel_processor_float(const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const; + + template + inline void run_get_pass_kernel_processor_half_rgba(const KernelFilmConvert *kfilm_convert, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination, + const Processor &processor) const; + +#define DECLARE_PASS_ACCESSOR(pass) \ + virtual void get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const override; + + /* Float (scalar) passes. */ + DECLARE_PASS_ACCESSOR(depth) + DECLARE_PASS_ACCESSOR(mist) + DECLARE_PASS_ACCESSOR(sample_count) + DECLARE_PASS_ACCESSOR(float) + + /* Float3 passes. */ + DECLARE_PASS_ACCESSOR(light_path) + DECLARE_PASS_ACCESSOR(shadow_catcher) + DECLARE_PASS_ACCESSOR(float3) + + /* Float4 passes. */ + DECLARE_PASS_ACCESSOR(motion) + DECLARE_PASS_ACCESSOR(cryptomatte) + DECLARE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow) + DECLARE_PASS_ACCESSOR(combined) + DECLARE_PASS_ACCESSOR(float4) + +#undef DECLARE_PASS_ACCESSOR +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_gpu.cpp b/intern/cycles/integrator/pass_accessor_gpu.cpp new file mode 100644 index 00000000000..c03ef64a2b2 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_gpu.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/pass_accessor_gpu.h" + +#include "device/queue.h" +#include "session/buffers.h" +#include "util/log.h" + +CCL_NAMESPACE_BEGIN + +PassAccessorGPU::PassAccessorGPU(DeviceQueue *queue, + const PassAccessInfo &pass_access_info, + float exposure, + int num_samples) + : PassAccessor(pass_access_info, exposure, num_samples), queue_(queue) + +{ +} + +/* -------------------------------------------------------------------- + * Kernel execution. + */ + +void PassAccessorGPU::run_film_convert_kernels(DeviceKernel kernel, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const +{ + KernelFilmConvert kfilm_convert; + init_kernel_film_convert(&kfilm_convert, buffer_params, destination); + + const int work_size = buffer_params.window_width * buffer_params.window_height; + + const int destination_stride = destination.stride != 0 ? destination.stride : + buffer_params.window_width; + + const int offset = buffer_params.window_x * buffer_params.pass_stride + + buffer_params.window_y * buffer_params.stride * buffer_params.pass_stride; + + if (destination.d_pixels) { + DCHECK_EQ(destination.stride, 0) << "Custom stride for float destination is not implemented."; + + void *args[] = {const_cast(&kfilm_convert), + const_cast(&destination.d_pixels), + const_cast(&render_buffers->buffer.device_pointer), + const_cast(&work_size), + const_cast(&buffer_params.window_width), + const_cast(&offset), + const_cast(&buffer_params.stride), + const_cast(&destination.offset), + const_cast(&destination_stride)}; + + queue_->enqueue(kernel, work_size, args); + } + if (destination.d_pixels_half_rgba) { + const DeviceKernel kernel_half_float = static_cast(kernel + 1); + + void *args[] = {const_cast(&kfilm_convert), + const_cast(&destination.d_pixels_half_rgba), + const_cast(&render_buffers->buffer.device_pointer), + const_cast(&work_size), + const_cast(&buffer_params.window_width), + const_cast(&offset), + const_cast(&buffer_params.stride), + const_cast(&destination.offset), + const_cast(&destination_stride)}; + + queue_->enqueue(kernel_half_float, work_size, args); + } + + queue_->synchronize(); +} + +/* -------------------------------------------------------------------- + * Pass accessors. + */ + +#define DEFINE_PASS_ACCESSOR(pass, kernel_pass) \ + void PassAccessorGPU::get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const \ + { \ + run_film_convert_kernels( \ + DEVICE_KERNEL_FILM_CONVERT_##kernel_pass, render_buffers, buffer_params, destination); \ + } + +/* Float (scalar) passes. */ +DEFINE_PASS_ACCESSOR(depth, DEPTH); +DEFINE_PASS_ACCESSOR(mist, MIST); +DEFINE_PASS_ACCESSOR(sample_count, SAMPLE_COUNT); +DEFINE_PASS_ACCESSOR(float, FLOAT); + +/* Float3 passes. */ +DEFINE_PASS_ACCESSOR(light_path, LIGHT_PATH); +DEFINE_PASS_ACCESSOR(float3, FLOAT3); + +/* Float4 passes. */ +DEFINE_PASS_ACCESSOR(motion, MOTION); +DEFINE_PASS_ACCESSOR(cryptomatte, CRYPTOMATTE); +DEFINE_PASS_ACCESSOR(shadow_catcher, SHADOW_CATCHER); +DEFINE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow, SHADOW_CATCHER_MATTE_WITH_SHADOW); +DEFINE_PASS_ACCESSOR(combined, COMBINED); +DEFINE_PASS_ACCESSOR(float4, FLOAT4); + +#undef DEFINE_PASS_ACCESSOR + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/pass_accessor_gpu.h b/intern/cycles/integrator/pass_accessor_gpu.h new file mode 100644 index 00000000000..f3442d90013 --- /dev/null +++ b/intern/cycles/integrator/pass_accessor_gpu.h @@ -0,0 +1,68 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/pass_accessor.h" +#include "kernel/types.h" + +CCL_NAMESPACE_BEGIN + +class DeviceQueue; + +/* Pass accessor implementation for GPU side. */ +class PassAccessorGPU : public PassAccessor { + public: + PassAccessorGPU(DeviceQueue *queue, + const PassAccessInfo &pass_access_info, + float exposure, + int num_samples); + + protected: + void run_film_convert_kernels(DeviceKernel kernel, + const RenderBuffers *render_buffers, + const BufferParams &buffer_params, + const Destination &destination) const; + +#define DECLARE_PASS_ACCESSOR(pass) \ + virtual void get_pass_##pass(const RenderBuffers *render_buffers, \ + const BufferParams &buffer_params, \ + const Destination &destination) const override; + + /* Float (scalar) passes. */ + DECLARE_PASS_ACCESSOR(depth); + DECLARE_PASS_ACCESSOR(mist); + DECLARE_PASS_ACCESSOR(sample_count); + DECLARE_PASS_ACCESSOR(float); + + /* Float3 passes. */ + DECLARE_PASS_ACCESSOR(light_path); + DECLARE_PASS_ACCESSOR(float3); + + /* Float4 passes. */ + DECLARE_PASS_ACCESSOR(motion); + DECLARE_PASS_ACCESSOR(cryptomatte); + DECLARE_PASS_ACCESSOR(shadow_catcher); + DECLARE_PASS_ACCESSOR(shadow_catcher_matte_with_shadow); + DECLARE_PASS_ACCESSOR(combined); + DECLARE_PASS_ACCESSOR(float4); + +#undef DECLARE_PASS_ACCESSOR + + DeviceQueue *queue_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace.cpp b/intern/cycles/integrator/path_trace.cpp new file mode 100644 index 00000000000..daf270d6686 --- /dev/null +++ b/intern/cycles/integrator/path_trace.cpp @@ -0,0 +1,1196 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace.h" + +#include "device/cpu/device.h" +#include "device/device.h" +#include "integrator/pass_accessor.h" +#include "integrator/path_trace_display.h" +#include "integrator/path_trace_tile.h" +#include "integrator/render_scheduler.h" +#include "scene/pass.h" +#include "scene/scene.h" +#include "session/tile.h" +#include "util/algorithm.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/tbb.h" +#include "util/time.h" + +CCL_NAMESPACE_BEGIN + +PathTrace::PathTrace(Device *device, + Film *film, + DeviceScene *device_scene, + RenderScheduler &render_scheduler, + TileManager &tile_manager) + : device_(device), + device_scene_(device_scene), + render_scheduler_(render_scheduler), + tile_manager_(tile_manager) +{ + DCHECK_NE(device_, nullptr); + + { + vector cpu_devices; + device_cpu_info(cpu_devices); + + cpu_device_.reset(device_cpu_create(cpu_devices[0], device->stats, device->profiler)); + } + + /* Create path tracing work in advance, so that it can be reused by incremental sampling as much + * as possible. */ + device_->foreach_device([&](Device *path_trace_device) { + path_trace_works_.emplace_back(PathTraceWork::create( + path_trace_device, film, device_scene, &render_cancel_.is_requested)); + }); + + work_balance_infos_.resize(path_trace_works_.size()); + work_balance_do_initial(work_balance_infos_); + + render_scheduler.set_need_schedule_rebalance(path_trace_works_.size() > 1); +} + +PathTrace::~PathTrace() +{ + /* Destroy any GPU resource which was used for graphics interop. + * Need to have access to the PathTraceDisplay as it is the only source of drawing context which + * is used for interop. */ + if (display_) { + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->destroy_gpu_resources(display_.get()); + } + } +} + +void PathTrace::load_kernels() +{ + if (denoiser_) { + denoiser_->load_kernels(progress_); + } +} + +void PathTrace::alloc_work_memory() +{ + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->alloc_work_memory(); + } +} + +bool PathTrace::ready_to_reset() +{ + /* The logic here is optimized for the best feedback in the viewport, which implies having a GPU + * display. Of there is no such display, the logic here will break. */ + DCHECK(display_); + + /* The logic here tries to provide behavior which feels the most interactive feel to artists. + * General idea is to be able to reset as quickly as possible, while still providing interactive + * feel. + * + * If the render result was ever drawn after previous reset, consider that reset is now possible. + * This way camera navigation gives the quickest feedback of rendered pixels, regardless of + * whether CPU or GPU drawing pipeline is used. + * + * Consider reset happening after redraw "slow" enough to not clog anything. This is a bit + * arbitrary, but seems to work very well with viewport navigation in Blender. */ + + if (did_draw_after_reset_) { + return true; + } + + return false; +} + +void PathTrace::reset(const BufferParams &full_params, const BufferParams &big_tile_params) +{ + if (big_tile_params_.modified(big_tile_params)) { + big_tile_params_ = big_tile_params; + render_state_.need_reset_params = true; + } + + full_params_ = full_params; + + /* NOTE: GPU display checks for buffer modification and avoids unnecessary re-allocation. + * It is requires to inform about reset whenever it happens, so that the redraw state tracking is + * properly updated. */ + if (display_) { + display_->reset(full_params); + } + + render_state_.has_denoised_result = false; + render_state_.tile_written = false; + + did_draw_after_reset_ = false; +} + +void PathTrace::device_free() +{ + /* Free render buffers used by the path trace work to reduce memory peak. */ + BufferParams empty_params; + empty_params.pass_stride = 0; + empty_params.update_offset_stride(); + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->get_render_buffers()->reset(empty_params); + } + render_state_.need_reset_params = true; +} + +void PathTrace::set_progress(Progress *progress) +{ + progress_ = progress; +} + +void PathTrace::render(const RenderWork &render_work) +{ + /* Indicate that rendering has started and that it can be requested to cancel. */ + { + thread_scoped_lock lock(render_cancel_.mutex); + if (render_cancel_.is_requested) { + return; + } + render_cancel_.is_rendering = true; + } + + render_pipeline(render_work); + + /* Indicate that rendering has finished, making it so thread which requested `cancel()` can carry + * on. */ + { + thread_scoped_lock lock(render_cancel_.mutex); + render_cancel_.is_rendering = false; + render_cancel_.condition.notify_one(); + } +} + +void PathTrace::render_pipeline(RenderWork render_work) +{ + /* NOTE: Only check for "instant" cancel here. The user-requested cancel via progress is + * checked in Session and the work in the event of cancel is to be finished here. */ + + render_scheduler_.set_need_schedule_cryptomatte(device_scene_->data.film.cryptomatte_passes != + 0); + + render_init_kernel_execution(); + + render_scheduler_.report_work_begin(render_work); + + init_render_buffers(render_work); + + rebalance(render_work); + + path_trace(render_work); + if (render_cancel_.is_requested) { + return; + } + + adaptive_sample(render_work); + if (render_cancel_.is_requested) { + return; + } + + cryptomatte_postprocess(render_work); + if (render_cancel_.is_requested) { + return; + } + + denoise(render_work); + if (render_cancel_.is_requested) { + return; + } + + write_tile_buffer(render_work); + update_display(render_work); + + progress_update_if_needed(render_work); + + finalize_full_buffer_on_disk(render_work); +} + +void PathTrace::render_init_kernel_execution() +{ + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->init_execution(); + } +} + +/* TODO(sergey): Look into `std::function` rather than using a template. Should not be a + * measurable performance impact at runtime, but will make compilation faster and binary somewhat + * smaller. */ +template +static void foreach_sliced_buffer_params(const vector> &path_trace_works, + const vector &work_balance_infos, + const BufferParams &buffer_params, + const int overscan, + const Callback &callback) +{ + const int num_works = path_trace_works.size(); + const int window_height = buffer_params.window_height; + + int current_y = 0; + for (int i = 0; i < num_works; ++i) { + const double weight = work_balance_infos[i].weight; + const int slice_window_full_y = buffer_params.full_y + buffer_params.window_y + current_y; + const int slice_window_height = max(lround(window_height * weight), 1); + + /* Disallow negative values to deal with situations when there are more compute devices than + * scan-lines. */ + const int remaining_window_height = max(0, window_height - current_y); + + BufferParams slice_params = buffer_params; + + slice_params.full_y = max(slice_window_full_y - overscan, buffer_params.full_y); + slice_params.window_y = slice_window_full_y - slice_params.full_y; + + if (i < num_works - 1) { + slice_params.window_height = min(slice_window_height, remaining_window_height); + } + else { + slice_params.window_height = remaining_window_height; + } + + slice_params.height = slice_params.window_y + slice_params.window_height + overscan; + slice_params.height = min(slice_params.height, + buffer_params.height + buffer_params.full_y - slice_params.full_y); + + slice_params.update_offset_stride(); + + callback(path_trace_works[i].get(), slice_params); + + current_y += slice_params.window_height; + } +} + +void PathTrace::update_allocated_work_buffer_params() +{ + const int overscan = tile_manager_.get_tile_overscan(); + foreach_sliced_buffer_params(path_trace_works_, + work_balance_infos_, + big_tile_params_, + overscan, + [](PathTraceWork *path_trace_work, const BufferParams ¶ms) { + RenderBuffers *buffers = path_trace_work->get_render_buffers(); + buffers->reset(params); + }); +} + +static BufferParams scale_buffer_params(const BufferParams ¶ms, int resolution_divider) +{ + BufferParams scaled_params = params; + + scaled_params.width = max(1, params.width / resolution_divider); + scaled_params.height = max(1, params.height / resolution_divider); + + scaled_params.window_x = params.window_x / resolution_divider; + scaled_params.window_y = params.window_y / resolution_divider; + scaled_params.window_width = params.window_width / resolution_divider; + scaled_params.window_height = params.window_height / resolution_divider; + + scaled_params.full_x = params.full_x / resolution_divider; + scaled_params.full_y = params.full_y / resolution_divider; + scaled_params.full_width = params.full_width / resolution_divider; + scaled_params.full_height = params.full_height / resolution_divider; + + scaled_params.update_offset_stride(); + + return scaled_params; +} + +void PathTrace::update_effective_work_buffer_params(const RenderWork &render_work) +{ + const int resolution_divider = render_work.resolution_divider; + + const BufferParams scaled_full_params = scale_buffer_params(full_params_, resolution_divider); + const BufferParams scaled_big_tile_params = scale_buffer_params(big_tile_params_, + resolution_divider); + + const int overscan = tile_manager_.get_tile_overscan(); + + foreach_sliced_buffer_params(path_trace_works_, + work_balance_infos_, + scaled_big_tile_params, + overscan, + [&](PathTraceWork *path_trace_work, const BufferParams params) { + path_trace_work->set_effective_buffer_params( + scaled_full_params, scaled_big_tile_params, params); + }); + + render_state_.effective_big_tile_params = scaled_big_tile_params; +} + +void PathTrace::update_work_buffer_params_if_needed(const RenderWork &render_work) +{ + if (render_state_.need_reset_params) { + update_allocated_work_buffer_params(); + } + + if (render_state_.need_reset_params || + render_state_.resolution_divider != render_work.resolution_divider) { + update_effective_work_buffer_params(render_work); + } + + render_state_.resolution_divider = render_work.resolution_divider; + render_state_.need_reset_params = false; +} + +void PathTrace::init_render_buffers(const RenderWork &render_work) +{ + update_work_buffer_params_if_needed(render_work); + + /* Handle initialization scheduled by the render scheduler. */ + if (render_work.init_render_buffers) { + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + path_trace_work->zero_render_buffers(); + }); + + tile_buffer_read(); + } +} + +void PathTrace::path_trace(RenderWork &render_work) +{ + if (!render_work.path_trace.num_samples) { + return; + } + + VLOG(3) << "Will path trace " << render_work.path_trace.num_samples + << " samples at the resolution divider " << render_work.resolution_divider; + + const double start_time = time_dt(); + + const int num_works = path_trace_works_.size(); + + tbb::parallel_for(0, num_works, [&](int i) { + const double work_start_time = time_dt(); + const int num_samples = render_work.path_trace.num_samples; + + PathTraceWork *path_trace_work = path_trace_works_[i].get(); + + PathTraceWork::RenderStatistics statistics; + path_trace_work->render_samples(statistics, render_work.path_trace.start_sample, num_samples); + + const double work_time = time_dt() - work_start_time; + work_balance_infos_[i].time_spent += work_time; + work_balance_infos_[i].occupancy = statistics.occupancy; + + VLOG(3) << "Rendered " << num_samples << " samples in " << work_time << " seconds (" + << work_time / num_samples + << " seconds per sample), occupancy: " << statistics.occupancy; + }); + + float occupancy_accum = 0.0f; + for (const WorkBalanceInfo &balance_info : work_balance_infos_) { + occupancy_accum += balance_info.occupancy; + } + const float occupancy = occupancy_accum / num_works; + render_scheduler_.report_path_trace_occupancy(render_work, occupancy); + + render_scheduler_.report_path_trace_time( + render_work, time_dt() - start_time, is_cancel_requested()); +} + +void PathTrace::adaptive_sample(RenderWork &render_work) +{ + if (!render_work.adaptive_sampling.filter) { + return; + } + + bool did_reschedule_on_idle = false; + + while (true) { + VLOG(3) << "Will filter adaptive stopping buffer, threshold " + << render_work.adaptive_sampling.threshold; + if (render_work.adaptive_sampling.reset) { + VLOG(3) << "Will re-calculate convergency flag for currently converged pixels."; + } + + const double start_time = time_dt(); + + uint num_active_pixels = 0; + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + const uint num_active_pixels_in_work = + path_trace_work->adaptive_sampling_converge_filter_count_active( + render_work.adaptive_sampling.threshold, render_work.adaptive_sampling.reset); + if (num_active_pixels_in_work) { + atomic_add_and_fetch_u(&num_active_pixels, num_active_pixels_in_work); + } + }); + + render_scheduler_.report_adaptive_filter_time( + render_work, time_dt() - start_time, is_cancel_requested()); + + if (num_active_pixels == 0) { + VLOG(3) << "All pixels converged."; + if (!render_scheduler_.render_work_reschedule_on_converge(render_work)) { + break; + } + VLOG(3) << "Continuing with lower threshold."; + } + else if (did_reschedule_on_idle) { + break; + } + else if (num_active_pixels < 128 * 128) { + /* NOTE: The hardcoded value of 128^2 is more of an empirical value to keep GPU busy so that + * there is no performance loss from the progressive noise floor feature. + * + * A better heuristic is possible here: for example, use maximum of 128^2 and percentage of + * the final resolution. */ + if (!render_scheduler_.render_work_reschedule_on_idle(render_work)) { + VLOG(3) << "Rescheduling is not possible: final threshold is reached."; + break; + } + VLOG(3) << "Rescheduling lower threshold."; + did_reschedule_on_idle = true; + } + else { + break; + } + } +} + +void PathTrace::set_denoiser_params(const DenoiseParams ¶ms) +{ + render_scheduler_.set_denoiser_params(params); + + if (!params.use) { + denoiser_.reset(); + return; + } + + if (denoiser_) { + const DenoiseParams old_denoiser_params = denoiser_->get_params(); + if (old_denoiser_params.type == params.type) { + denoiser_->set_params(params); + return; + } + } + + denoiser_ = Denoiser::create(device_, params); + denoiser_->is_cancelled_cb = [this]() { return is_cancel_requested(); }; +} + +void PathTrace::set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling) +{ + render_scheduler_.set_adaptive_sampling(adaptive_sampling); +} + +void PathTrace::cryptomatte_postprocess(const RenderWork &render_work) +{ + if (!render_work.cryptomatte.postprocess) { + return; + } + VLOG(3) << "Perform cryptomatte work."; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + path_trace_work->cryptomatte_postproces(); + }); +} + +void PathTrace::denoise(const RenderWork &render_work) +{ + if (!render_work.tile.denoise) { + return; + } + + if (!denoiser_) { + /* Denoiser was not configured, so nothing to do here. */ + return; + } + + VLOG(3) << "Perform denoising work."; + + const double start_time = time_dt(); + + RenderBuffers *buffer_to_denoise = nullptr; + + unique_ptr multi_device_buffers; + bool allow_inplace_modification = false; + + if (path_trace_works_.size() == 1) { + buffer_to_denoise = path_trace_works_.front()->get_render_buffers(); + } + else { + Device *denoiser_device = denoiser_->get_denoiser_device(); + if (!denoiser_device) { + return; + } + + multi_device_buffers = make_unique(denoiser_device); + multi_device_buffers->reset(render_state_.effective_big_tile_params); + + buffer_to_denoise = multi_device_buffers.get(); + + copy_to_render_buffers(multi_device_buffers.get()); + + allow_inplace_modification = true; + } + + if (denoiser_->denoise_buffer(render_state_.effective_big_tile_params, + buffer_to_denoise, + get_num_samples_in_buffer(), + allow_inplace_modification)) { + render_state_.has_denoised_result = true; + } + + if (multi_device_buffers) { + multi_device_buffers->copy_from_device(); + tbb::parallel_for_each( + path_trace_works_, [&multi_device_buffers](unique_ptr &path_trace_work) { + path_trace_work->copy_from_denoised_render_buffers(multi_device_buffers.get()); + }); + } + + render_scheduler_.report_denoise_time(render_work, time_dt() - start_time); +} + +void PathTrace::set_output_driver(unique_ptr driver) +{ + output_driver_ = move(driver); +} + +void PathTrace::set_display_driver(unique_ptr driver) +{ + if (driver) { + display_ = make_unique(move(driver)); + } + else { + display_ = nullptr; + } +} + +void PathTrace::clear_display() +{ + if (display_) { + display_->clear(); + } +} + +void PathTrace::draw() +{ + if (!display_) { + return; + } + + did_draw_after_reset_ |= display_->draw(); +} + +void PathTrace::update_display(const RenderWork &render_work) +{ + if (!render_work.display.update) { + return; + } + + if (!display_ && !output_driver_) { + VLOG(3) << "Ignore display update."; + return; + } + + if (full_params_.width == 0 || full_params_.height == 0) { + VLOG(3) << "Skipping PathTraceDisplay update due to 0 size of the render buffer."; + return; + } + + const double start_time = time_dt(); + + if (output_driver_) { + VLOG(3) << "Invoke buffer update callback."; + + PathTraceTile tile(*this); + output_driver_->update_render_tile(tile); + } + + if (display_) { + VLOG(3) << "Perform copy to GPUDisplay work."; + + const int resolution_divider = render_work.resolution_divider; + const int texture_width = max(1, full_params_.width / resolution_divider); + const int texture_height = max(1, full_params_.height / resolution_divider); + if (!display_->update_begin(texture_width, texture_height)) { + LOG(ERROR) << "Error beginning GPUDisplay update."; + return; + } + + const PassMode pass_mode = render_work.display.use_denoised_result && + render_state_.has_denoised_result ? + PassMode::DENOISED : + PassMode::NOISY; + + /* TODO(sergey): When using multi-device rendering map the GPUDisplay once and copy data from + * all works in parallel. */ + const int num_samples = get_num_samples_in_buffer(); + for (auto &&path_trace_work : path_trace_works_) { + path_trace_work->copy_to_display(display_.get(), pass_mode, num_samples); + } + + display_->update_end(); + } + + render_scheduler_.report_display_update_time(render_work, time_dt() - start_time); +} + +void PathTrace::rebalance(const RenderWork &render_work) +{ + static const int kLogLevel = 3; + + if (!render_work.rebalance) { + return; + } + + const int num_works = path_trace_works_.size(); + + if (num_works == 1) { + VLOG(kLogLevel) << "Ignoring rebalance work due to single device render."; + return; + } + + const double start_time = time_dt(); + + if (VLOG_IS_ON(kLogLevel)) { + VLOG(kLogLevel) << "Perform rebalance work."; + VLOG(kLogLevel) << "Per-device path tracing time (seconds):"; + for (int i = 0; i < num_works; ++i) { + VLOG(kLogLevel) << path_trace_works_[i]->get_device()->info.description << ": " + << work_balance_infos_[i].time_spent; + } + } + + const bool did_rebalance = work_balance_do_rebalance(work_balance_infos_); + + if (VLOG_IS_ON(kLogLevel)) { + VLOG(kLogLevel) << "Calculated per-device weights for works:"; + for (int i = 0; i < num_works; ++i) { + VLOG(kLogLevel) << path_trace_works_[i]->get_device()->info.description << ": " + << work_balance_infos_[i].weight; + } + } + + if (!did_rebalance) { + VLOG(kLogLevel) << "Balance in path trace works did not change."; + render_scheduler_.report_rebalance_time(render_work, time_dt() - start_time, false); + return; + } + + RenderBuffers big_tile_cpu_buffers(cpu_device_.get()); + big_tile_cpu_buffers.reset(render_state_.effective_big_tile_params); + + copy_to_render_buffers(&big_tile_cpu_buffers); + + render_state_.need_reset_params = true; + update_work_buffer_params_if_needed(render_work); + + copy_from_render_buffers(&big_tile_cpu_buffers); + + render_scheduler_.report_rebalance_time(render_work, time_dt() - start_time, true); +} + +void PathTrace::write_tile_buffer(const RenderWork &render_work) +{ + if (!render_work.tile.write) { + return; + } + + VLOG(3) << "Write tile result."; + + render_state_.tile_written = true; + + const bool has_multiple_tiles = tile_manager_.has_multiple_tiles(); + + /* Write render tile result, but only if not using tiled rendering. + * + * Tiles are written to a file during rendering, and written to the software at the end + * of rendering (wither when all tiles are finished, or when rendering was requested to be + * canceled). + * + * Important thing is: tile should be written to the software via callback only once. */ + if (!has_multiple_tiles) { + VLOG(3) << "Write tile result via buffer write callback."; + tile_buffer_write(); + } + + /* Write tile to disk, so that the render work's render buffer can be re-used for the next tile. + */ + if (has_multiple_tiles) { + VLOG(3) << "Write tile result into ."; + tile_buffer_write_to_disk(); + } +} + +void PathTrace::finalize_full_buffer_on_disk(const RenderWork &render_work) +{ + if (!render_work.full.write) { + return; + } + + VLOG(3) << "Handle full-frame render buffer work."; + + if (!tile_manager_.has_written_tiles()) { + VLOG(3) << "No tiles on disk."; + return; + } + + /* Make sure writing to the file is fully finished. + * This will include writing all possible missing tiles, ensuring validness of the file. */ + tile_manager_.finish_write_tiles(); + + /* NOTE: The rest of full-frame post-processing (such as full-frame denoising) will be done after + * all scenes and layers are rendered by the Session (which happens after freeing Session memory, + * so that we never hold scene and full-frame buffer in memory at the same time). */ +} + +void PathTrace::cancel() +{ + thread_scoped_lock lock(render_cancel_.mutex); + + render_cancel_.is_requested = true; + + while (render_cancel_.is_rendering) { + render_cancel_.condition.wait(lock); + } + + render_cancel_.is_requested = false; +} + +int PathTrace::get_num_samples_in_buffer() +{ + return render_scheduler_.get_num_rendered_samples(); +} + +bool PathTrace::is_cancel_requested() +{ + if (render_cancel_.is_requested) { + return true; + } + + if (progress_ != nullptr) { + if (progress_->get_cancel()) { + return true; + } + } + + return false; +} + +void PathTrace::tile_buffer_write() +{ + if (!output_driver_) { + return; + } + + PathTraceTile tile(*this); + output_driver_->write_render_tile(tile); +} + +void PathTrace::tile_buffer_read() +{ + if (!device_scene_->data.bake.use) { + return; + } + + if (!output_driver_) { + return; + } + + PathTraceTile tile(*this); + if (output_driver_->read_render_tile(tile)) { + tbb::parallel_for_each(path_trace_works_, [](unique_ptr &path_trace_work) { + path_trace_work->copy_render_buffers_to_device(); + }); + } +} + +void PathTrace::tile_buffer_write_to_disk() +{ + /* Sample count pass is required to support per-tile partial results stored in the file. */ + DCHECK_NE(big_tile_params_.get_pass_offset(PASS_SAMPLE_COUNT), PASS_UNUSED); + + const int num_rendered_samples = render_scheduler_.get_num_rendered_samples(); + + if (num_rendered_samples == 0) { + /* The tile has zero samples, no need to write it. */ + return; + } + + /* Get access to the CPU-side render buffers of the current big tile. */ + RenderBuffers *buffers; + RenderBuffers big_tile_cpu_buffers(cpu_device_.get()); + + if (path_trace_works_.size() == 1) { + path_trace_works_[0]->copy_render_buffers_from_device(); + buffers = path_trace_works_[0]->get_render_buffers(); + } + else { + big_tile_cpu_buffers.reset(render_state_.effective_big_tile_params); + copy_to_render_buffers(&big_tile_cpu_buffers); + + buffers = &big_tile_cpu_buffers; + } + + if (!tile_manager_.write_tile(*buffers)) { + device_->set_error("Error writing tile to file"); + } +} + +void PathTrace::progress_update_if_needed(const RenderWork &render_work) +{ + if (progress_ != nullptr) { + const int2 tile_size = get_render_tile_size(); + const int num_samples_added = tile_size.x * tile_size.y * render_work.path_trace.num_samples; + const int current_sample = render_work.path_trace.start_sample + + render_work.path_trace.num_samples; + progress_->add_samples(num_samples_added, current_sample); + } + + if (progress_update_cb) { + progress_update_cb(); + } +} + +void PathTrace::progress_set_status(const string &status, const string &substatus) +{ + if (progress_ != nullptr) { + progress_->set_status(status, substatus); + } +} + +void PathTrace::copy_to_render_buffers(RenderBuffers *render_buffers) +{ + tbb::parallel_for_each(path_trace_works_, + [&render_buffers](unique_ptr &path_trace_work) { + path_trace_work->copy_to_render_buffers(render_buffers); + }); + render_buffers->copy_to_device(); +} + +void PathTrace::copy_from_render_buffers(RenderBuffers *render_buffers) +{ + render_buffers->copy_from_device(); + tbb::parallel_for_each(path_trace_works_, + [&render_buffers](unique_ptr &path_trace_work) { + path_trace_work->copy_from_render_buffers(render_buffers); + }); +} + +bool PathTrace::copy_render_tile_from_device() +{ + if (full_frame_state_.render_buffers) { + /* Full-frame buffer is always allocated on CPU. */ + return true; + } + + bool success = true; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + if (!success) { + return; + } + if (!path_trace_work->copy_render_buffers_from_device()) { + success = false; + } + }); + + return success; +} + +static string get_layer_view_name(const RenderBuffers &buffers) +{ + string result; + + if (buffers.params.layer.size()) { + result += string(buffers.params.layer); + } + + if (buffers.params.view.size()) { + if (!result.empty()) { + result += ", "; + } + result += string(buffers.params.view); + } + + return result; +} + +void PathTrace::process_full_buffer_from_disk(string_view filename) +{ + VLOG(3) << "Processing full frame buffer file " << filename; + + progress_set_status("Reading full buffer from disk"); + + RenderBuffers full_frame_buffers(cpu_device_.get()); + + DenoiseParams denoise_params; + if (!tile_manager_.read_full_buffer_from_disk(filename, &full_frame_buffers, &denoise_params)) { + const string error_message = "Error reading tiles from file"; + if (progress_) { + progress_->set_error(error_message); + progress_->set_cancel(error_message); + } + else { + LOG(ERROR) << error_message; + } + return; + } + + const string layer_view_name = get_layer_view_name(full_frame_buffers); + + render_state_.has_denoised_result = false; + + if (denoise_params.use) { + progress_set_status(layer_view_name, "Denoising"); + + /* Re-use the denoiser as much as possible, avoiding possible device re-initialization. + * + * It will not conflict with the regular rendering as: + * - Rendering is supposed to be finished here. + * - The next rendering will go via Session's `run_update_for_next_iteration` which will + * ensure proper denoiser is used. */ + set_denoiser_params(denoise_params); + + /* Number of samples doesn't matter too much, since the samples count pass will be used. */ + denoiser_->denoise_buffer(full_frame_buffers.params, &full_frame_buffers, 0, false); + + render_state_.has_denoised_result = true; + } + + full_frame_state_.render_buffers = &full_frame_buffers; + + progress_set_status(layer_view_name, "Finishing"); + + /* Write the full result pretending that there is a single tile. + * Requires some state change, but allows to use same communication API with the software. */ + tile_buffer_write(); + + full_frame_state_.render_buffers = nullptr; +} + +int PathTrace::get_num_render_tile_samples() const +{ + if (full_frame_state_.render_buffers) { + return full_frame_state_.render_buffers->params.samples; + } + + return render_scheduler_.get_num_rendered_samples(); +} + +bool PathTrace::get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination) +{ + if (full_frame_state_.render_buffers) { + return pass_accessor.get_render_tile_pixels(full_frame_state_.render_buffers, destination); + } + + bool success = true; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + if (!success) { + return; + } + if (!path_trace_work->get_render_tile_pixels(pass_accessor, destination)) { + success = false; + } + }); + + return success; +} + +bool PathTrace::set_render_tile_pixels(PassAccessor &pass_accessor, + const PassAccessor::Source &source) +{ + bool success = true; + + tbb::parallel_for_each(path_trace_works_, [&](unique_ptr &path_trace_work) { + if (!success) { + return; + } + if (!path_trace_work->set_render_tile_pixels(pass_accessor, source)) { + success = false; + } + }); + + return success; +} + +int2 PathTrace::get_render_tile_size() const +{ + if (full_frame_state_.render_buffers) { + return make_int2(full_frame_state_.render_buffers->params.window_width, + full_frame_state_.render_buffers->params.window_height); + } + + const Tile &tile = tile_manager_.get_current_tile(); + return make_int2(tile.window_width, tile.window_height); +} + +int2 PathTrace::get_render_tile_offset() const +{ + if (full_frame_state_.render_buffers) { + return make_int2(0, 0); + } + + const Tile &tile = tile_manager_.get_current_tile(); + return make_int2(tile.x + tile.window_x, tile.y + tile.window_y); +} + +int2 PathTrace::get_render_size() const +{ + return tile_manager_.get_size(); +} + +const BufferParams &PathTrace::get_render_tile_params() const +{ + if (full_frame_state_.render_buffers) { + return full_frame_state_.render_buffers->params; + } + + return big_tile_params_; +} + +bool PathTrace::has_denoised_result() const +{ + return render_state_.has_denoised_result; +} + +/* -------------------------------------------------------------------- + * Report generation. + */ + +static const char *device_type_for_description(const DeviceType type) +{ + switch (type) { + case DEVICE_NONE: + return "None"; + + case DEVICE_CPU: + return "CPU"; + case DEVICE_CUDA: + return "CUDA"; + case DEVICE_OPTIX: + return "OptiX"; + case DEVICE_HIP: + return "HIP"; + case DEVICE_DUMMY: + return "Dummy"; + case DEVICE_MULTI: + return "Multi"; + } + + return "UNKNOWN"; +} + +/* Construct description of the device which will appear in the full report. */ +/* TODO(sergey): Consider making it more reusable utility. */ +static string full_device_info_description(const DeviceInfo &device_info) +{ + string full_description = device_info.description; + + full_description += " (" + string(device_type_for_description(device_info.type)) + ")"; + + if (device_info.display_device) { + full_description += " (display)"; + } + + if (device_info.type == DEVICE_CPU) { + full_description += " (" + to_string(device_info.cpu_threads) + " threads)"; + } + + full_description += " [" + device_info.id + "]"; + + return full_description; +} + +/* Construct string which will contain information about devices, possibly multiple of the devices. + * + * In the simple case the result looks like: + * + * Message: Full Device Description + * + * If there are multiple devices then the result looks like: + * + * Message: Full First Device Description + * Full Second Device Description + * + * Note that the newlines are placed in a way so that the result can be easily concatenated to the + * full report. */ +static string device_info_list_report(const string &message, const DeviceInfo &device_info) +{ + string result = "\n" + message + ": "; + const string pad(message.length() + 2, ' '); + + if (device_info.multi_devices.empty()) { + result += full_device_info_description(device_info) + "\n"; + return result; + } + + bool is_first = true; + for (const DeviceInfo &sub_device_info : device_info.multi_devices) { + if (!is_first) { + result += pad; + } + + result += full_device_info_description(sub_device_info) + "\n"; + + is_first = false; + } + + return result; +} + +static string path_trace_devices_report(const vector> &path_trace_works) +{ + DeviceInfo device_info; + device_info.type = DEVICE_MULTI; + + for (auto &&path_trace_work : path_trace_works) { + device_info.multi_devices.push_back(path_trace_work->get_device()->info); + } + + return device_info_list_report("Path tracing on", device_info); +} + +static string denoiser_device_report(const Denoiser *denoiser) +{ + if (!denoiser) { + return ""; + } + + if (!denoiser->get_params().use) { + return ""; + } + + const Device *denoiser_device = denoiser->get_denoiser_device(); + if (!denoiser_device) { + return ""; + } + + return device_info_list_report("Denoising on", denoiser_device->info); +} + +string PathTrace::full_report() const +{ + string result = "\nFull path tracing report\n"; + + result += path_trace_devices_report(path_trace_works_); + result += denoiser_device_report(denoiser_.get()); + + /* Report from the render scheduler, which includes: + * - Render mode (interactive, offline, headless) + * - Adaptive sampling and denoiser parameters + * - Breakdown of timing. */ + result += render_scheduler_.full_report(); + + return result; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace.h b/intern/cycles/integrator/path_trace.h new file mode 100644 index 00000000000..9b079352a63 --- /dev/null +++ b/intern/cycles/integrator/path_trace.h @@ -0,0 +1,322 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/denoiser.h" +#include "integrator/pass_accessor.h" +#include "integrator/path_trace_work.h" +#include "integrator/work_balancer.h" +#include "session/buffers.h" +#include "util/function.h" +#include "util/thread.h" +#include "util/unique_ptr.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class AdaptiveSampling; +class Device; +class DeviceScene; +class DisplayDriver; +class Film; +class RenderBuffers; +class RenderScheduler; +class RenderWork; +class PathTraceDisplay; +class OutputDriver; +class Progress; +class TileManager; + +/* PathTrace class takes care of kernel graph and scheduling on a (multi)device. It takes care of + * all the common steps of path tracing which are not device-specific. The list of tasks includes + * but is not limited to: + * - Kernel graph. + * - Scheduling logic. + * - Queues management. + * - Adaptive stopping. */ +class PathTrace { + public: + /* Render scheduler is used to report timing information and access things like start/finish + * sample. */ + PathTrace(Device *device, + Film *film, + DeviceScene *device_scene, + RenderScheduler &render_scheduler, + TileManager &tile_manager); + ~PathTrace(); + + /* Create devices and load kernels which are created on-demand (for example, denoising devices). + * The progress is reported to the currently configure progress object (via `set_progress`). */ + void load_kernels(); + + /* Allocate working memory. This runs before allocating scene memory so that we can estimate + * more accurately which scene device memory may need to allocated on the host. */ + void alloc_work_memory(); + + /* Check whether now it is a good time to reset rendering. + * Used to avoid very often resets in the viewport, giving it a chance to draw intermediate + * render result. */ + bool ready_to_reset(); + + void reset(const BufferParams &full_params, const BufferParams &big_tile_params); + + void device_free(); + + /* Set progress tracker. + * Used to communicate details about the progress to the outer world, check whether rendering is + * to be canceled. + * + * The path tracer writes to this object, and then at a convenient moment runs + * progress_update_cb() callback. */ + void set_progress(Progress *progress); + + /* NOTE: This is a blocking call. Meaning, it will not return until given number of samples are + * rendered (or until rendering is requested to be canceled). */ + void render(const RenderWork &render_work); + + /* TODO(sergey): Decide whether denoiser is really a part of path tracer. Currently it is + * convenient to have it here because then its easy to access render buffer. But the downside is + * that this adds too much of entities which can live separately with some clear API. */ + + /* Set denoiser parameters. + * Use this to configure the denoiser before rendering any samples. */ + void set_denoiser_params(const DenoiseParams ¶ms); + + /* Set parameters used for adaptive sampling. + * Use this to configure the adaptive sampler before rendering any samples. */ + void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling); + + /* Sets output driver for render buffer output. */ + void set_output_driver(unique_ptr driver); + + /* Set display driver for interactive render buffer display. */ + void set_display_driver(unique_ptr driver); + + /* Clear the display buffer by filling it in with all zeroes. */ + void clear_display(); + + /* Perform drawing of the current state of the DisplayDriver. */ + void draw(); + + /* Cancel rendering process as soon as possible, without waiting for full tile to be sampled. + * Used in cases like reset of render session. + * + * This is a blocking call, which returns as soon as there is no running `render_samples()` call. + */ + void cancel(); + + /* Copy an entire render buffer to/from the path trace. */ + + /* Copy happens via CPU side buffer: data will be copied from every device of the path trace, and + * the data will be copied to the device of the given render buffers. */ + void copy_to_render_buffers(RenderBuffers *render_buffers); + + /* Copy happens via CPU side buffer: data will be copied from the device of the given render + * buffers and will be copied to all devices of the path trace. */ + void copy_from_render_buffers(RenderBuffers *render_buffers); + + /* Copy render buffers of the big tile from the device to host. + * Return true if all copies are successful. */ + bool copy_render_tile_from_device(); + + /* Read given full-frame file from disk, perform needed processing and write it to the software + * via the write callback. */ + void process_full_buffer_from_disk(string_view filename); + + /* Get number of samples in the current big tile render buffers. */ + int get_num_render_tile_samples() const; + + /* Get pass data of the entire big tile. + * This call puts pass render result from all devices into the final pixels storage. + * + * NOTE: Expects buffers to be copied to the host using `copy_render_tile_from_device()`. + * + * Returns false if any of the accessor's `get_render_tile_pixels()` returned false. */ + bool get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination); + + /* Set pass data for baking. */ + bool set_render_tile_pixels(PassAccessor &pass_accessor, const PassAccessor::Source &source); + + /* Check whether denoiser was run and denoised passes are available. */ + bool has_denoised_result() const; + + /* Get size and offset (relative to the buffer's full x/y) of the currently rendering tile. + * In the case of tiled rendering this will return full-frame after all tiles has been rendered. + * + * NOTE: If the full-frame buffer processing is in progress, returns parameters of the full-frame + * instead. */ + int2 get_render_tile_size() const; + int2 get_render_tile_offset() const; + int2 get_render_size() const; + + /* Get buffer parameters of the current tile. + * + * NOTE: If the full-frame buffer processing is in progress, returns parameters of the full-frame + * instead. */ + const BufferParams &get_render_tile_params() const; + + /* Generate full multi-line report of the rendering process, including rendering parameters, + * times, and so on. */ + string full_report() const; + + /* Callback which is called to report current rendering progress. + * + * It is supposed to be cheaper than buffer update/write, hence can be called more often. + * Additionally, it might be called form the middle of wavefront (meaning, it is not guaranteed + * that the buffer is "uniformly" sampled at the moment of this callback). */ + function progress_update_cb; + + protected: + /* Actual implementation of the rendering pipeline. + * Calls steps in order, checking for the cancel to be requested in between. + * + * Is separate from `render()` to simplify dealing with the early outputs and keeping + * `render_cancel_` in the consistent state. */ + void render_pipeline(RenderWork render_work); + + /* Initialize kernel execution on all integrator queues. */ + void render_init_kernel_execution(); + + /* Make sure both allocated and effective buffer parameters of path tracer works are up to date + * with the current big tile parameters, performance-dependent slicing, and resolution divider. + */ + void update_work_buffer_params_if_needed(const RenderWork &render_work); + void update_allocated_work_buffer_params(); + void update_effective_work_buffer_params(const RenderWork &render_work); + + /* Perform various steps of the render work. + * + * Note that some steps might modify the work, forcing some steps to happen within this iteration + * of rendering. */ + void init_render_buffers(const RenderWork &render_work); + void path_trace(RenderWork &render_work); + void adaptive_sample(RenderWork &render_work); + void denoise(const RenderWork &render_work); + void cryptomatte_postprocess(const RenderWork &render_work); + void update_display(const RenderWork &render_work); + void rebalance(const RenderWork &render_work); + void write_tile_buffer(const RenderWork &render_work); + void finalize_full_buffer_on_disk(const RenderWork &render_work); + + /* Get number of samples in the current state of the render buffers. */ + int get_num_samples_in_buffer(); + + /* Check whether user requested to cancel rendering, so that path tracing is to be finished as + * soon as possible. */ + bool is_cancel_requested(); + + /* Write the big tile render buffer via the write callback. */ + void tile_buffer_write(); + + /* Read the big tile render buffer via the read callback. */ + void tile_buffer_read(); + + /* Write current tile into the file on disk. */ + void tile_buffer_write_to_disk(); + + /* Run the progress_update_cb callback if it is needed. */ + void progress_update_if_needed(const RenderWork &render_work); + + void progress_set_status(const string &status, const string &substatus = ""); + + /* Pointer to a device which is configured to be used for path tracing. If multiple devices + * are configured this is a `MultiDevice`. */ + Device *device_ = nullptr; + + /* CPU device for creating temporary render buffers on the CPU side. */ + unique_ptr cpu_device_; + + DeviceScene *device_scene_; + + RenderScheduler &render_scheduler_; + TileManager &tile_manager_; + + /* Display driver for interactive render buffer display. */ + unique_ptr display_; + + /* Output driver to write render buffer to. */ + unique_ptr output_driver_; + + /* Per-compute device descriptors of work which is responsible for path tracing on its configured + * device. */ + vector> path_trace_works_; + + /* Per-path trace work information needed for multi-device balancing. */ + vector work_balance_infos_; + + /* Render buffer parameters of the full frame and current big tile. */ + BufferParams full_params_; + BufferParams big_tile_params_; + + /* Denoiser which takes care of denoising the big tile. */ + unique_ptr denoiser_; + + /* State which is common for all the steps of the render work. + * Is brought up to date in the `render()` call and is accessed from all the steps involved into + * rendering the work. */ + struct { + /* Denotes whether render buffers parameters of path trace works are to be reset for the new + * value of the big tile parameters. */ + bool need_reset_params = false; + + /* Divider of the resolution for faster previews. + * + * Allows to re-use same render buffer, but have less pixels rendered into in it. The way to + * think of render buffer in this case is as an over-allocated array: the resolution divider + * affects both resolution and stride as visible by the integrator kernels. */ + int resolution_divider = 0; + + /* Parameters of the big tile with the current resolution divider applied. */ + BufferParams effective_big_tile_params; + + /* Denoiser was run and there are denoised versions of the passes in the render buffers. */ + bool has_denoised_result = false; + + /* Current tile has been written (to either disk or callback. + * Indicates that no more work will be done on this tile. */ + bool tile_written = false; + } render_state_; + + /* Progress object which is used to communicate sample progress. */ + Progress *progress_; + + /* Fields required for canceling render on demand, as quickly as possible. */ + struct { + /* Indicates whether there is an on-going `render_samples()` call. */ + bool is_rendering = false; + + /* Indicates whether rendering is requested to be canceled by `cancel()`. */ + bool is_requested = false; + + /* Synchronization between thread which does `render_samples()` and thread which does + * `cancel()`. */ + thread_mutex mutex; + thread_condition_variable condition; + } render_cancel_; + + /* Indicates whether a render result was drawn after latest session reset. + * Used by `ready_to_reset()` to implement logic which feels the most interactive. */ + bool did_draw_after_reset_ = true; + + /* State of the full frame processing and writing to the software. */ + struct { + RenderBuffers *render_buffers = nullptr; + } full_frame_state_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_display.cpp b/intern/cycles/integrator/path_trace_display.cpp new file mode 100644 index 00000000000..c1cade923b1 --- /dev/null +++ b/intern/cycles/integrator/path_trace_display.cpp @@ -0,0 +1,251 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_display.h" + +#include "session/buffers.h" + +#include "util/log.h" + +CCL_NAMESPACE_BEGIN + +PathTraceDisplay::PathTraceDisplay(unique_ptr driver) : driver_(move(driver)) +{ +} + +void PathTraceDisplay::reset(const BufferParams &buffer_params) +{ + thread_scoped_lock lock(mutex_); + + params_.full_offset = make_int2(buffer_params.full_x, buffer_params.full_y); + params_.full_size = make_int2(buffer_params.full_width, buffer_params.full_height); + params_.size = make_int2(buffer_params.width, buffer_params.height); + + texture_state_.is_outdated = true; +} + +void PathTraceDisplay::mark_texture_updated() +{ + texture_state_.is_outdated = false; +} + +/* -------------------------------------------------------------------- + * Update procedure. + */ + +bool PathTraceDisplay::update_begin(int texture_width, int texture_height) +{ + DCHECK(!update_state_.is_active); + + if (update_state_.is_active) { + LOG(ERROR) << "Attempt to re-activate update process."; + return false; + } + + /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. + * The update itself is non-blocking however, for better performance and to avoid + * potential deadlocks due to locks held by the subclass. */ + DisplayDriver::Params params; + { + thread_scoped_lock lock(mutex_); + params = params_; + texture_state_.size = make_int2(texture_width, texture_height); + } + + if (!driver_->update_begin(params, texture_width, texture_height)) { + LOG(ERROR) << "PathTraceDisplay implementation could not begin update."; + return false; + } + + update_state_.is_active = true; + + return true; +} + +void PathTraceDisplay::update_end() +{ + DCHECK(update_state_.is_active); + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to deactivate inactive update process."; + return; + } + + driver_->update_end(); + + update_state_.is_active = false; +} + +int2 PathTraceDisplay::get_texture_size() const +{ + return texture_state_.size; +} + +/* -------------------------------------------------------------------- + * Texture update from CPU buffer. + */ + +void PathTraceDisplay::copy_pixels_to_texture( + const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height) +{ + DCHECK(update_state_.is_active); + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update."; + return; + } + + mark_texture_updated(); + + /* This call copies pixels to a mapped texture buffer which is typically much cheaper from CPU + * time point of view than to copy data directly to a texture. + * + * The possible downside of this approach is that it might require a higher peak memory when + * doing partial updates of the texture (although, in practice even partial updates might peak + * with a full-frame buffer stored on the CPU if the GPU is currently occupied). */ + half4 *mapped_rgba_pixels = map_texture_buffer(); + if (!mapped_rgba_pixels) { + return; + } + + const int texture_width = texture_state_.size.x; + const int texture_height = texture_state_.size.y; + + if (texture_x == 0 && texture_y == 0 && pixels_width == texture_width && + pixels_height == texture_height) { + const size_t size_in_bytes = sizeof(half4) * texture_width * texture_height; + memcpy(mapped_rgba_pixels, rgba_pixels, size_in_bytes); + } + else { + const half4 *rgba_row = rgba_pixels; + half4 *mapped_rgba_row = mapped_rgba_pixels + texture_y * texture_width + texture_x; + for (int y = 0; y < pixels_height; + ++y, rgba_row += pixels_width, mapped_rgba_row += texture_width) { + memcpy(mapped_rgba_row, rgba_row, sizeof(half4) * pixels_width); + } + } + + unmap_texture_buffer(); +} + +/* -------------------------------------------------------------------- + * Texture buffer mapping. + */ + +half4 *PathTraceDisplay::map_texture_buffer() +{ + DCHECK(!texture_buffer_state_.is_mapped); + DCHECK(update_state_.is_active); + + if (texture_buffer_state_.is_mapped) { + LOG(ERROR) << "Attempt to re-map an already mapped texture buffer."; + return nullptr; + } + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to copy pixels data outside of PathTraceDisplay update."; + return nullptr; + } + + half4 *mapped_rgba_pixels = driver_->map_texture_buffer(); + + if (mapped_rgba_pixels) { + texture_buffer_state_.is_mapped = true; + } + + return mapped_rgba_pixels; +} + +void PathTraceDisplay::unmap_texture_buffer() +{ + DCHECK(texture_buffer_state_.is_mapped); + + if (!texture_buffer_state_.is_mapped) { + LOG(ERROR) << "Attempt to unmap non-mapped texture buffer."; + return; + } + + texture_buffer_state_.is_mapped = false; + + mark_texture_updated(); + driver_->unmap_texture_buffer(); +} + +/* -------------------------------------------------------------------- + * Graphics interoperability. + */ + +DisplayDriver::GraphicsInterop PathTraceDisplay::graphics_interop_get() +{ + DCHECK(!texture_buffer_state_.is_mapped); + DCHECK(update_state_.is_active); + + if (texture_buffer_state_.is_mapped) { + LOG(ERROR) + << "Attempt to use graphics interoperability mode while the texture buffer is mapped."; + return DisplayDriver::GraphicsInterop(); + } + + if (!update_state_.is_active) { + LOG(ERROR) << "Attempt to use graphics interoperability outside of PathTraceDisplay update."; + return DisplayDriver::GraphicsInterop(); + } + + /* Assume that interop will write new values to the texture. */ + mark_texture_updated(); + + return driver_->graphics_interop_get(); +} + +void PathTraceDisplay::graphics_interop_activate() +{ + driver_->graphics_interop_activate(); +} + +void PathTraceDisplay::graphics_interop_deactivate() +{ + driver_->graphics_interop_deactivate(); +} + +/* -------------------------------------------------------------------- + * Drawing. + */ + +void PathTraceDisplay::clear() +{ + driver_->clear(); +} + +bool PathTraceDisplay::draw() +{ + /* Get parameters within a mutex lock, to avoid reset() modifying them at the same time. + * The drawing itself is non-blocking however, for better performance and to avoid + * potential deadlocks due to locks held by the subclass. */ + DisplayDriver::Params params; + bool is_outdated; + + { + thread_scoped_lock lock(mutex_); + params = params_; + is_outdated = texture_state_.is_outdated; + } + + driver_->draw(params); + + return !is_outdated; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_display.h b/intern/cycles/integrator/path_trace_display.h new file mode 100644 index 00000000000..b69ee85fbbc --- /dev/null +++ b/intern/cycles/integrator/path_trace_display.h @@ -0,0 +1,192 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "session/display_driver.h" + +#include "util/half.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; + +/* PathTraceDisplay is used for efficient render buffer display. + * + * The host applications implements a DisplayDriver, storing a render pass in a GPU-side + * textures. This texture is continuously updated by the path tracer and drawn by the host + * application. + * + * PathTraceDisplay is a wrapper around the DisplayDriver, adding thread safety, state tracking + * and error checking. */ + +class PathTraceDisplay { + public: + PathTraceDisplay(unique_ptr driver); + virtual ~PathTraceDisplay() = default; + + /* Reset the display for the new state of render session. Is called whenever session is reset, + * which happens on changes like viewport navigation or viewport dimension change. + * + * This call will configure parameters for a changed buffer and reset the texture state. */ + void reset(const BufferParams &buffer_params); + + /* -------------------------------------------------------------------- + * Update procedure. + * + * These calls indicates a desire of the caller to update content of the displayed texture. */ + + /* Returns true when update is ready. Update should be finished with update_end(). + * + * If false is returned then no update is possible, and no update_end() call is needed. + * + * The texture width and height denotes an actual resolution of the underlying render result. */ + bool update_begin(int texture_width, int texture_height); + + void update_end(); + + /* Get currently configured texture size of the display (as configured by `update_begin()`. */ + int2 get_texture_size() const; + + /* -------------------------------------------------------------------- + * Texture update from CPU buffer. + * + * NOTE: The PathTraceDisplay should be marked for an update being in process with + * `update_begin()`. + * + * Most portable implementation, which must be supported by all platforms. Might not be the most + * efficient one. + */ + + /* Copy buffer of rendered pixels of a given size into a given position of the texture. + * + * This function does not acquire a lock. The reason for this is is to allow use of this function + * for partial updates from different devices. In this case the caller will acquire the lock + * once, update all the slices and release + * the lock once. This will ensure that draw() will never use partially updated texture. */ + void copy_pixels_to_texture( + const half4 *rgba_pixels, int texture_x, int texture_y, int pixels_width, int pixels_height); + + /* -------------------------------------------------------------------- + * Texture buffer mapping. + * + * This functionality is used to update GPU-side texture content without need to maintain CPU + * side buffer on the caller. + * + * NOTE: The PathTraceDisplay should be marked for an update being in process with + * `update_begin()`. + * + * NOTE: Texture buffer can not be mapped while graphics interoperability is active. This means + * that `map_texture_buffer()` is not allowed between `graphics_interop_begin()` and + * `graphics_interop_end()` calls. + */ + + /* Map pixels memory form texture to a buffer available for write from CPU. Width and height will + * define a requested size of the texture to write to. + * Upon success a non-null pointer is returned and the texture buffer is to be unmapped. + * If an error happens during mapping, or if mapping is not supported by this GPU display a + * null pointer is returned and the buffer is NOT to be unmapped. + * + * NOTE: Usually the implementation will rely on a GPU context of some sort, and the GPU context + * is often can not be bound to two threads simultaneously, and can not be released from a + * different thread. This means that the mapping API should be used from the single thread only, + */ + half4 *map_texture_buffer(); + void unmap_texture_buffer(); + + /* -------------------------------------------------------------------- + * Graphics interoperability. + * + * A special code path which allows to update texture content directly from the GPU compute + * device. Complementary part of DeviceGraphicsInterop. + * + * NOTE: Graphics interoperability can not be used while the texture buffer is mapped. This means + * that `graphics_interop_get()` is not allowed between `map_texture_buffer()` and + * `unmap_texture_buffer()` calls. */ + + /* Get PathTraceDisplay graphics interoperability information which acts as a destination for the + * device API. */ + DisplayDriver::GraphicsInterop graphics_interop_get(); + + /* (De)activate GPU display for graphics interoperability outside of regular display update + * routines. */ + void graphics_interop_activate(); + void graphics_interop_deactivate(); + + /* -------------------------------------------------------------------- + * Drawing. + */ + + /* Clear the texture by filling it with all zeroes. + * + * This call might happen in parallel with draw, but can never happen in parallel with the + * update. + * + * The actual zeroing can be deferred to a later moment. What is important is that after clear + * and before pixels update the drawing texture will be fully empty, and that partial update + * after clear will write new pixel values for an updating area, leaving everything else zeroed. + * + * If the GPU display supports graphics interoperability then the zeroing the display is to be + * delegated to the device via the `DisplayDriver::GraphicsInterop`. */ + void clear(); + + /* Draw the current state of the texture. + * + * Returns true if this call did draw an updated state of the texture. */ + bool draw(); + + private: + /* Display driver implemented by the host application. */ + unique_ptr driver_; + + /* Current display parameters */ + thread_mutex mutex_; + DisplayDriver::Params params_; + + /* Mark texture as its content has been updated. + * Used from places which knows that the texture content has been brought up-to-date, so that the + * drawing knows whether it can be performed, and whether drawing happened with an up-to-date + * texture state. */ + void mark_texture_updated(); + + /* State of the update process. */ + struct { + /* True when update is in process, indicated by `update_begin()` / `update_end()`. */ + bool is_active = false; + } update_state_; + + /* State of the texture, which is needed for an integration with render session and interactive + * updates and navigation. */ + struct { + /* Texture is considered outdated after `reset()` until the next call of + * `copy_pixels_to_texture()`. */ + bool is_outdated = true; + + /* Texture size in pixels. */ + int2 size = make_int2(0, 0); + } texture_state_; + + /* State of the texture buffer. Is tracked to perform sanity checks. */ + struct { + /* True when the texture buffer is mapped with `map_texture_buffer()`. */ + bool is_mapped = false; + } texture_buffer_state_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_tile.cpp b/intern/cycles/integrator/path_trace_tile.cpp new file mode 100644 index 00000000000..4834769f476 --- /dev/null +++ b/intern/cycles/integrator/path_trace_tile.cpp @@ -0,0 +1,107 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_tile.h" +#include "integrator/pass_accessor_cpu.h" +#include "integrator/path_trace.h" + +#include "scene/film.h" +#include "scene/pass.h" +#include "scene/scene.h" +#include "session/buffers.h" + +CCL_NAMESPACE_BEGIN + +PathTraceTile::PathTraceTile(PathTrace &path_trace) + : OutputDriver::Tile(path_trace.get_render_tile_offset(), + path_trace.get_render_tile_size(), + path_trace.get_render_size(), + path_trace.get_render_tile_params().layer, + path_trace.get_render_tile_params().view), + path_trace_(path_trace), + copied_from_device_(false) +{ +} + +bool PathTraceTile::get_pass_pixels(const string_view pass_name, + const int num_channels, + float *pixels) const +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happening while this function runs. */ + + if (!copied_from_device_) { + /* Copy from device on demand. */ + path_trace_.copy_render_tile_from_device(); + const_cast(this)->copied_from_device_ = true; + } + + const BufferParams &buffer_params = path_trace_.get_render_tile_params(); + + const BufferPass *pass = buffer_params.find_pass(pass_name); + if (pass == nullptr) { + return false; + } + + const bool has_denoised_result = path_trace_.has_denoised_result(); + if (pass->mode == PassMode::DENOISED && !has_denoised_result) { + pass = buffer_params.find_pass(pass->type); + if (pass == nullptr) { + /* Happens when denoised result pass is requested but is never written by the kernel. */ + return false; + } + } + + pass = buffer_params.get_actual_display_pass(pass); + + const float exposure = buffer_params.exposure; + const int num_samples = path_trace_.get_num_render_tile_samples(); + + PassAccessor::PassAccessInfo pass_access_info(*pass); + pass_access_info.use_approximate_shadow_catcher = buffer_params.use_approximate_shadow_catcher; + pass_access_info.use_approximate_shadow_catcher_background = + pass_access_info.use_approximate_shadow_catcher && !buffer_params.use_transparent_background; + + const PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + const PassAccessor::Destination destination(pixels, num_channels); + + return path_trace_.get_render_tile_pixels(pass_accessor, destination); +} + +bool PathTraceTile::set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const +{ + /* NOTE: The code relies on a fact that session is fully update and no scene/buffer modification + * is happening while this function runs. */ + + const BufferParams &buffer_params = path_trace_.get_render_tile_params(); + const BufferPass *pass = buffer_params.find_pass(pass_name); + if (!pass) { + return false; + } + + const float exposure = buffer_params.exposure; + const int num_samples = 1; + + const PassAccessor::PassAccessInfo pass_access_info(*pass); + PassAccessorCPU pass_accessor(pass_access_info, exposure, num_samples); + PassAccessor::Source source(pixels, num_channels); + + return path_trace_.set_render_tile_pixels(pass_accessor, source); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_tile.h b/intern/cycles/integrator/path_trace_tile.h new file mode 100644 index 00000000000..6c7bddf2ca5 --- /dev/null +++ b/intern/cycles/integrator/path_trace_tile.h @@ -0,0 +1,43 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "session/output_driver.h" + +CCL_NAMESPACE_BEGIN + +/* PathTraceTile + * + * Implementation of OutputDriver::Tile interface for path tracer. */ + +class PathTrace; + +class PathTraceTile : public OutputDriver::Tile { + public: + PathTraceTile(PathTrace &path_trace); + + bool get_pass_pixels(const string_view pass_name, const int num_channels, float *pixels) const; + bool set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const; + + private: + PathTrace &path_trace_; + bool copied_from_device_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work.cpp b/intern/cycles/integrator/path_trace_work.cpp new file mode 100644 index 00000000000..b0c40cfe15c --- /dev/null +++ b/intern/cycles/integrator/path_trace_work.cpp @@ -0,0 +1,208 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "device/device.h" + +#include "integrator/path_trace_display.h" +#include "integrator/path_trace_work.h" +#include "integrator/path_trace_work_cpu.h" +#include "integrator/path_trace_work_gpu.h" +#include "scene/film.h" +#include "scene/scene.h" +#include "session/buffers.h" + +#include "kernel/types.h" + +CCL_NAMESPACE_BEGIN + +unique_ptr PathTraceWork::create(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) +{ + if (device->info.type == DEVICE_CPU) { + return make_unique(device, film, device_scene, cancel_requested_flag); + } + + return make_unique(device, film, device_scene, cancel_requested_flag); +} + +PathTraceWork::PathTraceWork(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) + : device_(device), + film_(film), + device_scene_(device_scene), + buffers_(make_unique(device)), + effective_buffer_params_(buffers_->params), + cancel_requested_flag_(cancel_requested_flag) +{ +} + +PathTraceWork::~PathTraceWork() +{ +} + +RenderBuffers *PathTraceWork::get_render_buffers() +{ + return buffers_.get(); +} + +void PathTraceWork::set_effective_buffer_params(const BufferParams &effective_full_params, + const BufferParams &effective_big_tile_params, + const BufferParams &effective_buffer_params) +{ + effective_full_params_ = effective_full_params; + effective_big_tile_params_ = effective_big_tile_params; + effective_buffer_params_ = effective_buffer_params; +} + +bool PathTraceWork::has_multiple_works() const +{ + /* Assume if there are multiple works working on the same big tile none of the works gets the + * entire big tile to work on. */ + return !(effective_big_tile_params_.width == effective_buffer_params_.width && + effective_big_tile_params_.height == effective_buffer_params_.height && + effective_big_tile_params_.full_x == effective_buffer_params_.full_x && + effective_big_tile_params_.full_y == effective_buffer_params_.full_y); +} + +void PathTraceWork::copy_to_render_buffers(RenderBuffers *render_buffers) +{ + copy_render_buffers_from_device(); + + const int64_t width = effective_buffer_params_.width; + const int64_t height = effective_buffer_params_.height; + const int64_t pass_stride = effective_buffer_params_.pass_stride; + const int64_t row_stride = width * pass_stride; + const int64_t data_size = row_stride * height * sizeof(float); + + const int64_t offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int64_t offset_in_floats = offset_y * row_stride; + + const float *src = buffers_->buffer.data(); + float *dst = render_buffers->buffer.data() + offset_in_floats; + + memcpy(dst, src, data_size); +} + +void PathTraceWork::copy_from_render_buffers(const RenderBuffers *render_buffers) +{ + const int64_t width = effective_buffer_params_.width; + const int64_t height = effective_buffer_params_.height; + const int64_t pass_stride = effective_buffer_params_.pass_stride; + const int64_t row_stride = width * pass_stride; + const int64_t data_size = row_stride * height * sizeof(float); + + const int64_t offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int64_t offset_in_floats = offset_y * row_stride; + + const float *src = render_buffers->buffer.data() + offset_in_floats; + float *dst = buffers_->buffer.data(); + + memcpy(dst, src, data_size); + + copy_render_buffers_to_device(); +} + +void PathTraceWork::copy_from_denoised_render_buffers(const RenderBuffers *render_buffers) +{ + const int64_t width = effective_buffer_params_.width; + const int64_t offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int64_t offset = offset_y * width; + + render_buffers_host_copy_denoised( + buffers_.get(), effective_buffer_params_, render_buffers, effective_buffer_params_, offset); + + copy_render_buffers_to_device(); +} + +bool PathTraceWork::get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination) +{ + const int offset_y = (effective_buffer_params_.full_y + effective_buffer_params_.window_y) - + (effective_big_tile_params_.full_y + effective_big_tile_params_.window_y); + const int width = effective_buffer_params_.width; + + PassAccessor::Destination slice_destination = destination; + slice_destination.offset += offset_y * width; + + return pass_accessor.get_render_tile_pixels(buffers_.get(), slice_destination); +} + +bool PathTraceWork::set_render_tile_pixels(PassAccessor &pass_accessor, + const PassAccessor::Source &source) +{ + const int offset_y = effective_buffer_params_.full_y - effective_big_tile_params_.full_y; + const int width = effective_buffer_params_.width; + + PassAccessor::Source slice_source = source; + slice_source.offset += offset_y * width; + + return pass_accessor.set_render_tile_pixels(buffers_.get(), slice_source); +} + +PassAccessor::PassAccessInfo PathTraceWork::get_display_pass_access_info(PassMode pass_mode) const +{ + const KernelFilm &kfilm = device_scene_->data.film; + const KernelBackground &kbackground = device_scene_->data.background; + + const BufferParams ¶ms = buffers_->params; + + const BufferPass *display_pass = params.get_actual_display_pass(film_->get_display_pass()); + + PassAccessor::PassAccessInfo pass_access_info; + pass_access_info.type = display_pass->type; + pass_access_info.offset = PASS_UNUSED; + + if (pass_mode == PassMode::DENOISED) { + pass_access_info.mode = PassMode::DENOISED; + pass_access_info.offset = params.get_pass_offset(pass_access_info.type, PassMode::DENOISED); + } + + if (pass_access_info.offset == PASS_UNUSED) { + pass_access_info.mode = PassMode::NOISY; + pass_access_info.offset = params.get_pass_offset(pass_access_info.type); + } + + pass_access_info.use_approximate_shadow_catcher = kfilm.use_approximate_shadow_catcher; + pass_access_info.use_approximate_shadow_catcher_background = + kfilm.use_approximate_shadow_catcher && !kbackground.transparent; + + pass_access_info.show_active_pixels = film_->get_show_active_pixels(); + + return pass_access_info; +} + +PassAccessor::Destination PathTraceWork::get_display_destination_template( + const PathTraceDisplay *display) const +{ + PassAccessor::Destination destination(film_->get_display_pass()); + + const int2 display_texture_size = display->get_texture_size(); + const int texture_x = effective_buffer_params_.full_x - effective_full_params_.full_x + + effective_buffer_params_.window_x; + const int texture_y = effective_buffer_params_.full_y - effective_full_params_.full_y + + effective_buffer_params_.window_y; + + destination.offset = texture_y * display_texture_size.x + texture_x; + destination.stride = display_texture_size.x; + + return destination; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work.h b/intern/cycles/integrator/path_trace_work.h new file mode 100644 index 00000000000..0dc7cd2f896 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work.h @@ -0,0 +1,192 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/pass_accessor.h" +#include "scene/pass.h" +#include "session/buffers.h" +#include "util/types.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; +class Device; +class DeviceScene; +class Film; +class PathTraceDisplay; +class RenderBuffers; + +class PathTraceWork { + public: + struct RenderStatistics { + float occupancy = 1.0f; + }; + + /* Create path trace work which fits best the device. + * + * The cancel request flag is used for a cheap check whether cancel is to be performed as soon as + * possible. This could be, for example, request to cancel rendering on camera navigation in + * viewport. */ + static unique_ptr create(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + virtual ~PathTraceWork(); + + /* Access the render buffers. + * + * Is only supposed to be used by the PathTrace to update buffer allocation and slicing to + * correspond to the big tile size and relative device performance. */ + RenderBuffers *get_render_buffers(); + + /* Set effective parameters of the big tile and the work itself. */ + void set_effective_buffer_params(const BufferParams &effective_full_params, + const BufferParams &effective_big_tile_params, + const BufferParams &effective_buffer_params); + + /* Check whether the big tile is being worked on by multiple path trace works. */ + bool has_multiple_works() const; + + /* Allocate working memory for execution. Must be called before init_execution(). */ + virtual void alloc_work_memory(){}; + + /* Initialize execution of kernels. + * Will ensure that all device queues are initialized for execution. + * + * This method is to be called after any change in the scene. It is not needed to call it prior + * to an every call of the `render_samples()`. */ + virtual void init_execution() = 0; + + /* Render given number of samples as a synchronous blocking call. + * The samples are added to the render buffer associated with this work. */ + virtual void render_samples(RenderStatistics &statistics, int start_sample, int samples_num) = 0; + + /* Copy render result from this work to the corresponding place of the GPU display. + * + * The `pass_mode` indicates whether to access denoised or noisy version of the display pass. The + * noisy pass mode will be passed here when it is known that the buffer does not have denoised + * passes yet (because denoiser did not run). If the denoised pass is requested and denoiser is + * not used then this function will fall-back to the noisy pass instead. */ + virtual void copy_to_display(PathTraceDisplay *display, PassMode pass_mode, int num_samples) = 0; + + virtual void destroy_gpu_resources(PathTraceDisplay *display) = 0; + + /* Copy data from/to given render buffers. + * Will copy pixels from a corresponding place (from multi-device point of view) of the render + * buffers, and copy work's render buffers to the corresponding place of the destination. */ + + /* Notes: + * - Copies work's render buffer from the device. + * - Copies CPU-side buffer of the given buffer + * - Does not copy the buffer to its device. */ + void copy_to_render_buffers(RenderBuffers *render_buffers); + + /* Notes: + * - Does not copy given render buffers from the device. + * - Copies work's render buffer to its device. */ + void copy_from_render_buffers(const RenderBuffers *render_buffers); + + /* Special version of the `copy_from_render_buffers()` which only copies denoised passes from the + * given render buffers, leaving rest of the passes. + * + * Same notes about device copying applies to this call as well. */ + void copy_from_denoised_render_buffers(const RenderBuffers *render_buffers); + + /* Copy render buffers to/from device using an appropriate device queue when needed so that + * things are executed in order with the `render_samples()`. */ + virtual bool copy_render_buffers_from_device() = 0; + virtual bool copy_render_buffers_to_device() = 0; + + /* Zero render buffers to/from device using an appropriate device queue when needed so that + * things are executed in order with the `render_samples()`. */ + virtual bool zero_render_buffers() = 0; + + /* Access pixels rendered by this work and copy them to the corresponding location in the + * destination. + * + * NOTE: Does not perform copy of buffers from the device. Use `copy_render_tile_from_device()` + * to update host-side data. */ + bool get_render_tile_pixels(const PassAccessor &pass_accessor, + const PassAccessor::Destination &destination); + + /* Set pass data for baking. */ + bool set_render_tile_pixels(PassAccessor &pass_accessor, const PassAccessor::Source &source); + + /* Perform convergence test on the render buffer, and filter the convergence mask. + * Returns number of active pixels (the ones which did not converge yet). */ + virtual int adaptive_sampling_converge_filter_count_active(float threshold, bool reset) = 0; + + /* Run cryptomatte pass post-processing kernels. */ + virtual void cryptomatte_postproces() = 0; + + /* Cheap-ish request to see whether rendering is requested and is to be stopped as soon as + * possible, without waiting for any samples to be finished. */ + inline bool is_cancel_requested() const + { + /* NOTE: Rely on the fact that on x86 CPU reading scalar can happen without atomic even in + * threaded environment. */ + return *cancel_requested_flag_; + } + + /* Access to the device which is used to path trace this work on. */ + Device *get_device() const + { + return device_; + } + + protected: + PathTraceWork(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + PassAccessor::PassAccessInfo get_display_pass_access_info(PassMode pass_mode) const; + + /* Get destination which offset and stride are configured so that writing to it will write to a + * proper location of GPU display texture, taking current tile and device slice into account. */ + PassAccessor::Destination get_display_destination_template( + const PathTraceDisplay *display) const; + + /* Device which will be used for path tracing. + * Note that it is an actual render device (and never is a multi-device). */ + Device *device_; + + /* Film is used to access display pass configuration for GPU display update. + * Note that only fields which are not a part of kernel data can be accessed via the Film. */ + Film *film_; + + /* Device side scene storage, that may be used for integrator logic. */ + DeviceScene *device_scene_; + + /* Render buffers where sampling is being accumulated into, allocated for a fraction of the big + * tile which is being rendered by this work. + * It also defines possible subset of a big tile in the case of multi-device rendering. */ + unique_ptr buffers_; + + /* Effective parameters of the full, big tile, and current work render buffer. + * The latter might be different from `buffers_->params` when there is a resolution divider + * involved. */ + BufferParams effective_full_params_; + BufferParams effective_big_tile_params_; + BufferParams effective_buffer_params_; + + bool *cancel_requested_flag_ = nullptr; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_cpu.cpp b/intern/cycles/integrator/path_trace_work_cpu.cpp new file mode 100644 index 00000000000..541a7eca02f --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_cpu.cpp @@ -0,0 +1,287 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_work_cpu.h" + +#include "device/cpu/kernel.h" +#include "device/device.h" + +#include "kernel/integrator/path_state.h" + +#include "integrator/pass_accessor_cpu.h" +#include "integrator/path_trace_display.h" + +#include "scene/scene.h" +#include "session/buffers.h" + +#include "util/atomic.h" +#include "util/log.h" +#include "util/tbb.h" + +CCL_NAMESPACE_BEGIN + +/* Create TBB arena for execution of path tracing and rendering tasks. */ +static inline tbb::task_arena local_tbb_arena_create(const Device *device) +{ + /* TODO: limit this to number of threads of CPU device, it may be smaller than + * the system number of threads when we reduce the number of CPU threads in + * CPU + GPU rendering to dedicate some cores to handling the GPU device. */ + return tbb::task_arena(device->info.cpu_threads); +} + +/* Get CPUKernelThreadGlobals for the current thread. */ +static inline CPUKernelThreadGlobals *kernel_thread_globals_get( + vector &kernel_thread_globals) +{ + const int thread_index = tbb::this_task_arena::current_thread_index(); + DCHECK_GE(thread_index, 0); + DCHECK_LE(thread_index, kernel_thread_globals.size()); + + return &kernel_thread_globals[thread_index]; +} + +PathTraceWorkCPU::PathTraceWorkCPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) + : PathTraceWork(device, film, device_scene, cancel_requested_flag), + kernels_(*(device->get_cpu_kernels())) +{ + DCHECK_EQ(device->info.type, DEVICE_CPU); +} + +void PathTraceWorkCPU::init_execution() +{ + /* Cache per-thread kernel globals. */ + device_->get_cpu_kernel_thread_globals(kernel_thread_globals_); +} + +void PathTraceWorkCPU::render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) +{ + const int64_t image_width = effective_buffer_params_.width; + const int64_t image_height = effective_buffer_params_.height; + const int64_t total_pixels_num = image_width * image_height; + + for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) { + kernel_globals.start_profiling(); + } + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + local_arena.execute([&]() { + tbb::parallel_for(int64_t(0), total_pixels_num, [&](int64_t work_index) { + if (is_cancel_requested()) { + return; + } + + const int y = work_index / image_width; + const int x = work_index - y * image_width; + + KernelWorkTile work_tile; + work_tile.x = effective_buffer_params_.full_x + x; + work_tile.y = effective_buffer_params_.full_y + y; + work_tile.w = 1; + work_tile.h = 1; + work_tile.start_sample = start_sample; + work_tile.num_samples = 1; + work_tile.offset = effective_buffer_params_.offset; + work_tile.stride = effective_buffer_params_.stride; + + CPUKernelThreadGlobals *kernel_globals = kernel_thread_globals_get(kernel_thread_globals_); + + render_samples_full_pipeline(kernel_globals, work_tile, samples_num); + }); + }); + + for (CPUKernelThreadGlobals &kernel_globals : kernel_thread_globals_) { + kernel_globals.stop_profiling(); + } + + statistics.occupancy = 1.0f; +} + +void PathTraceWorkCPU::render_samples_full_pipeline(KernelGlobalsCPU *kernel_globals, + const KernelWorkTile &work_tile, + const int samples_num) +{ + const bool has_bake = device_scene_->data.bake.use; + + IntegratorStateCPU integrator_states[2]; + + IntegratorStateCPU *state = &integrator_states[0]; + IntegratorStateCPU *shadow_catcher_state = nullptr; + + if (device_scene_->data.integrator.has_shadow_catcher) { + shadow_catcher_state = &integrator_states[1]; + path_state_init_queues(shadow_catcher_state); + } + + KernelWorkTile sample_work_tile = work_tile; + float *render_buffer = buffers_->buffer.data(); + + for (int sample = 0; sample < samples_num; ++sample) { + if (is_cancel_requested()) { + break; + } + + if (has_bake) { + if (!kernels_.integrator_init_from_bake( + kernel_globals, state, &sample_work_tile, render_buffer)) { + break; + } + } + else { + if (!kernels_.integrator_init_from_camera( + kernel_globals, state, &sample_work_tile, render_buffer)) { + break; + } + } + + kernels_.integrator_megakernel(kernel_globals, state, render_buffer); + + if (shadow_catcher_state) { + kernels_.integrator_megakernel(kernel_globals, shadow_catcher_state, render_buffer); + } + + ++sample_work_tile.start_sample; + } +} + +void PathTraceWorkCPU::copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) +{ + half4 *rgba_half = display->map_texture_buffer(); + if (!rgba_half) { + /* TODO(sergey): Look into using copy_to_display() if mapping failed. Might be needed for + * some implementations of PathTraceDisplay which can not map memory? */ + return; + } + + const KernelFilm &kfilm = device_scene_->data.film; + + const PassAccessor::PassAccessInfo pass_access_info = get_display_pass_access_info(pass_mode); + + const PassAccessorCPU pass_accessor(pass_access_info, kfilm.exposure, num_samples); + + PassAccessor::Destination destination = get_display_destination_template(display); + destination.pixels_half_rgba = rgba_half; + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + local_arena.execute([&]() { + pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination); + }); + + display->unmap_texture_buffer(); +} + +void PathTraceWorkCPU::destroy_gpu_resources(PathTraceDisplay * /*display*/) +{ +} + +bool PathTraceWorkCPU::copy_render_buffers_from_device() +{ + return buffers_->copy_from_device(); +} + +bool PathTraceWorkCPU::copy_render_buffers_to_device() +{ + buffers_->buffer.copy_to_device(); + return true; +} + +bool PathTraceWorkCPU::zero_render_buffers() +{ + buffers_->zero(); + return true; +} + +int PathTraceWorkCPU::adaptive_sampling_converge_filter_count_active(float threshold, bool reset) +{ + const int full_x = effective_buffer_params_.full_x; + const int full_y = effective_buffer_params_.full_y; + const int width = effective_buffer_params_.width; + const int height = effective_buffer_params_.height; + const int offset = effective_buffer_params_.offset; + const int stride = effective_buffer_params_.stride; + + float *render_buffer = buffers_->buffer.data(); + + uint num_active_pixels = 0; + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + + /* Check convergency and do x-filter in a single `parallel_for`, to reduce threading overhead. */ + local_arena.execute([&]() { + tbb::parallel_for(full_y, full_y + height, [&](int y) { + CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; + + bool row_converged = true; + uint num_row_pixels_active = 0; + for (int x = 0; x < width; ++x) { + if (!kernels_.adaptive_sampling_convergence_check( + kernel_globals, render_buffer, full_x + x, y, threshold, reset, offset, stride)) { + ++num_row_pixels_active; + row_converged = false; + } + } + + atomic_fetch_and_add_uint32(&num_active_pixels, num_row_pixels_active); + + if (!row_converged) { + kernels_.adaptive_sampling_filter_x( + kernel_globals, render_buffer, y, full_x, width, offset, stride); + } + }); + }); + + if (num_active_pixels) { + local_arena.execute([&]() { + tbb::parallel_for(full_x, full_x + width, [&](int x) { + CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; + kernels_.adaptive_sampling_filter_y( + kernel_globals, render_buffer, x, full_y, height, offset, stride); + }); + }); + } + + return num_active_pixels; +} + +void PathTraceWorkCPU::cryptomatte_postproces() +{ + const int width = effective_buffer_params_.width; + const int height = effective_buffer_params_.height; + + float *render_buffer = buffers_->buffer.data(); + + tbb::task_arena local_arena = local_tbb_arena_create(device_); + + /* Check convergency and do x-filter in a single `parallel_for`, to reduce threading overhead. */ + local_arena.execute([&]() { + tbb::parallel_for(0, height, [&](int y) { + CPUKernelThreadGlobals *kernel_globals = &kernel_thread_globals_[0]; + int pixel_index = y * width; + + for (int x = 0; x < width; ++x, ++pixel_index) { + kernels_.cryptomatte_postprocess(kernel_globals, render_buffer, pixel_index); + } + }); + }); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_cpu.h b/intern/cycles/integrator/path_trace_work_cpu.h new file mode 100644 index 00000000000..6e734690811 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_cpu.h @@ -0,0 +1,82 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/state.h" + +#include "device/cpu/kernel_thread_globals.h" +#include "device/queue.h" + +#include "integrator/path_trace_work.h" + +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +struct KernelWorkTile; +struct KernelGlobalsCPU; + +class CPUKernels; + +/* Implementation of PathTraceWork which schedules work on to queues pixel-by-pixel, + * for CPU devices. + * + * NOTE: For the CPU rendering there are assumptions about TBB arena size and number of concurrent + * queues on the render device which makes this work be only usable on CPU. */ +class PathTraceWorkCPU : public PathTraceWork { + public: + PathTraceWorkCPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + virtual void init_execution() override; + + virtual void render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) override; + + virtual void copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) override; + virtual void destroy_gpu_resources(PathTraceDisplay *display) override; + + virtual bool copy_render_buffers_from_device() override; + virtual bool copy_render_buffers_to_device() override; + virtual bool zero_render_buffers() override; + + virtual int adaptive_sampling_converge_filter_count_active(float threshold, bool reset) override; + virtual void cryptomatte_postproces() override; + + protected: + /* Core path tracing routine. Renders given work time on the given queue. */ + void render_samples_full_pipeline(KernelGlobalsCPU *kernel_globals, + const KernelWorkTile &work_tile, + const int samples_num); + + /* CPU kernels. */ + const CPUKernels &kernels_; + + /* Copy of kernel globals which is suitable for concurrent access from multiple threads. + * + * More specifically, the `kernel_globals_` is local to each threads and nobody else is + * accessing it, but some "localization" is required to decouple from kernel globals stored + * on the device level. */ + vector kernel_thread_globals_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_gpu.cpp b/intern/cycles/integrator/path_trace_work_gpu.cpp new file mode 100644 index 00000000000..dfc1362ab09 --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_gpu.cpp @@ -0,0 +1,1105 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/path_trace_work_gpu.h" +#include "integrator/path_trace_display.h" + +#include "device/device.h" + +#include "integrator/pass_accessor_gpu.h" +#include "scene/scene.h" +#include "session/buffers.h" +#include "util/log.h" +#include "util/string.h" +#include "util/tbb.h" +#include "util/time.h" + +#include "kernel/types.h" + +CCL_NAMESPACE_BEGIN + +static size_t estimate_single_state_size() +{ + size_t state_size = 0; + +#define KERNEL_STRUCT_BEGIN(name) for (int array_index = 0;; array_index++) { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) state_size += sizeof(type); +#define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) state_size += sizeof(type); +#define KERNEL_STRUCT_END(name) \ + break; \ + } +#define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ + if (array_index >= gpu_array_size - 1) { \ + break; \ + } \ + } +/* TODO(sergey): Look into better estimation for fields which depend on scene features. Maybe + * maximum state calculation should happen as `alloc_work_memory()`, so that we can react to an + * updated scene state here. + * For until then use common value. Currently this size is only used for logging, but is weak to + * rely on this. */ +#define KERNEL_STRUCT_VOLUME_STACK_SIZE 4 + +#include "kernel/integrator/state_template.h" + +#include "kernel/integrator/shadow_state_template.h" + +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE + + return state_size; +} + +PathTraceWorkGPU::PathTraceWorkGPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag) + : PathTraceWork(device, film, device_scene, cancel_requested_flag), + queue_(device->gpu_queue_create()), + integrator_state_soa_kernel_features_(0), + integrator_queue_counter_(device, "integrator_queue_counter", MEM_READ_WRITE), + integrator_shader_sort_counter_(device, "integrator_shader_sort_counter", MEM_READ_WRITE), + integrator_shader_raytrace_sort_counter_( + device, "integrator_shader_raytrace_sort_counter", MEM_READ_WRITE), + integrator_shader_sort_prefix_sum_( + device, "integrator_shader_sort_prefix_sum", MEM_READ_WRITE), + integrator_next_main_path_index_(device, "integrator_next_main_path_index", MEM_READ_WRITE), + integrator_next_shadow_path_index_( + device, "integrator_next_shadow_path_index", MEM_READ_WRITE), + queued_paths_(device, "queued_paths", MEM_READ_WRITE), + num_queued_paths_(device, "num_queued_paths", MEM_READ_WRITE), + work_tiles_(device, "work_tiles", MEM_READ_WRITE), + display_rgba_half_(device, "display buffer half", MEM_READ_WRITE), + max_num_paths_(queue_->num_concurrent_states(estimate_single_state_size())), + min_num_active_main_paths_(queue_->num_concurrent_busy_states()), + max_active_main_path_index_(0) +{ + memset(&integrator_state_gpu_, 0, sizeof(integrator_state_gpu_)); + + /* Limit number of active paths to the half of the overall state. This is due to the logic in the + * path compaction which relies on the fact that regeneration does not happen sooner than half of + * the states are available again. */ + min_num_active_main_paths_ = min(min_num_active_main_paths_, max_num_paths_ / 2); +} + +void PathTraceWorkGPU::alloc_integrator_soa() +{ + /* IntegrateState allocated as structure of arrays. */ + + /* Check if we already allocated memory for the required features. */ + const int requested_volume_stack_size = device_scene_->data.volume_stack_size; + const uint kernel_features = device_scene_->data.kernel_features; + if ((integrator_state_soa_kernel_features_ & kernel_features) == kernel_features && + integrator_state_soa_volume_stack_size_ >= requested_volume_stack_size) { + return; + } + integrator_state_soa_kernel_features_ = kernel_features; + integrator_state_soa_volume_stack_size_ = max(integrator_state_soa_volume_stack_size_, + requested_volume_stack_size); + + /* Allocate a device only memory buffer before for each struct member, and then + * write the pointers into a struct that resides in constant memory. + * + * TODO: store float3 in separate XYZ arrays. */ +#define KERNEL_STRUCT_BEGIN(name) for (int array_index = 0;; array_index++) { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \ + if ((kernel_features & (feature)) && (integrator_state_gpu_.parent_struct.name == nullptr)) { \ + device_only_memory *array = new device_only_memory(device_, \ + "integrator_state_" #name); \ + array->alloc_to_device(max_num_paths_); \ + integrator_state_soa_.emplace_back(array); \ + integrator_state_gpu_.parent_struct.name = (type *)array->device_pointer; \ + } +#define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \ + if ((kernel_features & (feature)) && \ + (integrator_state_gpu_.parent_struct[array_index].name == nullptr)) { \ + device_only_memory *array = new device_only_memory(device_, \ + "integrator_state_" #name); \ + array->alloc_to_device(max_num_paths_); \ + integrator_state_soa_.emplace_back(array); \ + integrator_state_gpu_.parent_struct[array_index].name = (type *)array->device_pointer; \ + } +#define KERNEL_STRUCT_END(name) \ + break; \ + } +#define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ + if (array_index >= gpu_array_size - 1) { \ + break; \ + } \ + } +#define KERNEL_STRUCT_VOLUME_STACK_SIZE (integrator_state_soa_volume_stack_size_) + +#include "kernel/integrator/state_template.h" + +#include "kernel/integrator/shadow_state_template.h" + +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE + + if (VLOG_IS_ON(3)) { + size_t total_soa_size = 0; + for (auto &&soa_memory : integrator_state_soa_) { + total_soa_size += soa_memory->memory_size(); + } + + VLOG(3) << "GPU SoA state size: " << string_human_readable_size(total_soa_size); + } +} + +void PathTraceWorkGPU::alloc_integrator_queue() +{ + if (integrator_queue_counter_.size() == 0) { + integrator_queue_counter_.alloc(1); + integrator_queue_counter_.zero_to_device(); + integrator_queue_counter_.copy_from_device(); + integrator_state_gpu_.queue_counter = (IntegratorQueueCounter *) + integrator_queue_counter_.device_pointer; + } + + /* Allocate data for active path index arrays. */ + if (num_queued_paths_.size() == 0) { + num_queued_paths_.alloc(1); + num_queued_paths_.zero_to_device(); + } + + if (queued_paths_.size() == 0) { + queued_paths_.alloc(max_num_paths_); + /* TODO: this could be skip if we had a function to just allocate on device. */ + queued_paths_.zero_to_device(); + } +} + +void PathTraceWorkGPU::alloc_integrator_sorting() +{ + /* Allocate arrays for shader sorting. */ + const int max_shaders = device_scene_->data.max_shaders; + if (integrator_shader_sort_counter_.size() < max_shaders) { + integrator_shader_sort_counter_.alloc(max_shaders); + integrator_shader_sort_counter_.zero_to_device(); + + integrator_shader_raytrace_sort_counter_.alloc(max_shaders); + integrator_shader_raytrace_sort_counter_.zero_to_device(); + + integrator_shader_sort_prefix_sum_.alloc(max_shaders); + integrator_shader_sort_prefix_sum_.zero_to_device(); + + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE] = + (int *)integrator_shader_sort_counter_.device_pointer; + integrator_state_gpu_.sort_key_counter[DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE] = + (int *)integrator_shader_raytrace_sort_counter_.device_pointer; + } +} + +void PathTraceWorkGPU::alloc_integrator_path_split() +{ + if (integrator_next_shadow_path_index_.size() == 0) { + integrator_next_shadow_path_index_.alloc(1); + integrator_next_shadow_path_index_.zero_to_device(); + + integrator_state_gpu_.next_shadow_path_index = + (int *)integrator_next_shadow_path_index_.device_pointer; + } + + if (integrator_next_main_path_index_.size() == 0) { + integrator_next_main_path_index_.alloc(1); + integrator_next_shadow_path_index_.data()[0] = 0; + integrator_next_main_path_index_.zero_to_device(); + + integrator_state_gpu_.next_main_path_index = + (int *)integrator_next_main_path_index_.device_pointer; + } +} + +void PathTraceWorkGPU::alloc_work_memory() +{ + alloc_integrator_soa(); + alloc_integrator_queue(); + alloc_integrator_sorting(); + alloc_integrator_path_split(); +} + +void PathTraceWorkGPU::init_execution() +{ + queue_->init_execution(); + + /* Copy to device side struct in constant memory. */ + device_->const_copy_to( + "__integrator_state", &integrator_state_gpu_, sizeof(integrator_state_gpu_)); +} + +void PathTraceWorkGPU::render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) +{ + /* Limit number of states for the tile and rely on a greedy scheduling of tiles. This allows to + * add more work (because tiles are smaller, so there is higher chance that more paths will + * become busy after adding new tiles). This is especially important for the shadow catcher which + * schedules work in halves of available number of paths. */ + work_tile_scheduler_.set_max_num_path_states(max_num_paths_ / 8); + + work_tile_scheduler_.reset(effective_buffer_params_, + start_sample, + samples_num, + device_scene_->data.integrator.scrambling_distance); + + enqueue_reset(); + + int num_iterations = 0; + uint64_t num_busy_accum = 0; + + /* TODO: set a hard limit in case of undetected kernel failures? */ + while (true) { + /* Enqueue work from the scheduler, on start or when there are not enough + * paths to keep the device occupied. */ + bool finished; + if (enqueue_work_tiles(finished)) { + /* Copy stats from the device. */ + queue_->copy_from_device(integrator_queue_counter_); + + if (!queue_->synchronize()) { + break; /* Stop on error. */ + } + } + + if (is_cancel_requested()) { + break; + } + + /* Stop if no more work remaining. */ + if (finished) { + break; + } + + /* Enqueue on of the path iteration kernels. */ + if (enqueue_path_iteration()) { + /* Copy stats from the device. */ + queue_->copy_from_device(integrator_queue_counter_); + + if (!queue_->synchronize()) { + break; /* Stop on error. */ + } + } + + if (is_cancel_requested()) { + break; + } + + num_busy_accum += num_active_main_paths_paths(); + ++num_iterations; + } + + statistics.occupancy = static_cast(num_busy_accum) / num_iterations / max_num_paths_; +} + +DeviceKernel PathTraceWorkGPU::get_most_queued_kernel() const +{ + const IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + + int max_num_queued = 0; + DeviceKernel kernel = DEVICE_KERNEL_NUM; + + for (int i = 0; i < DEVICE_KERNEL_INTEGRATOR_NUM; i++) { + if (queue_counter->num_queued[i] > max_num_queued) { + kernel = (DeviceKernel)i; + max_num_queued = queue_counter->num_queued[i]; + } + } + + return kernel; +} + +void PathTraceWorkGPU::enqueue_reset() +{ + void *args[] = {&max_num_paths_}; + queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_RESET, max_num_paths_, args); + queue_->zero_to_device(integrator_queue_counter_); + queue_->zero_to_device(integrator_shader_sort_counter_); + queue_->zero_to_device(integrator_shader_raytrace_sort_counter_); + + /* Tiles enqueue need to know number of active paths, which is based on this counter. Zero the + * counter on the host side because `zero_to_device()` is not doing it. */ + if (integrator_queue_counter_.host_pointer) { + memset(integrator_queue_counter_.data(), 0, integrator_queue_counter_.memory_size()); + } +} + +bool PathTraceWorkGPU::enqueue_path_iteration() +{ + /* Find kernel to execute, with max number of queued paths. */ + const IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + + int num_active_paths = 0; + for (int i = 0; i < DEVICE_KERNEL_INTEGRATOR_NUM; i++) { + num_active_paths += queue_counter->num_queued[i]; + } + + if (num_active_paths == 0) { + return false; + } + + /* Find kernel to execute, with max number of queued paths. */ + const DeviceKernel kernel = get_most_queued_kernel(); + if (kernel == DEVICE_KERNEL_NUM) { + return false; + } + + /* For kernels that add shadow paths, check if there is enough space available. + * If not, schedule shadow kernels first to clear out the shadow paths. */ + int num_paths_limit = INT_MAX; + + if (kernel_creates_shadow_paths(kernel)) { + compact_shadow_paths(); + + const int available_shadow_paths = max_num_paths_ - + integrator_next_shadow_path_index_.data()[0]; + if (available_shadow_paths < queue_counter->num_queued[kernel]) { + if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW]) { + enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + return true; + } + else if (queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]) { + enqueue_path_iteration(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return true; + } + } + else if (kernel_creates_ao_paths(kernel)) { + /* AO kernel creates two shadow paths, so limit number of states to schedule. */ + num_paths_limit = available_shadow_paths / 2; + } + } + + /* Schedule kernel with maximum number of queued items. */ + enqueue_path_iteration(kernel, num_paths_limit); + + /* Update next shadow path index for kernels that can add shadow paths. */ + if (kernel_creates_shadow_paths(kernel)) { + queue_->copy_from_device(integrator_next_shadow_path_index_); + } + + return true; +} + +void PathTraceWorkGPU::enqueue_path_iteration(DeviceKernel kernel, const int num_paths_limit) +{ + void *d_path_index = (void *)NULL; + + /* Create array of path indices for which this kernel is queued to be executed. */ + int work_size = kernel_max_active_main_path_index(kernel); + + IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + int num_queued = queue_counter->num_queued[kernel]; + + if (kernel_uses_sorting(kernel)) { + /* Compute array of active paths, sorted by shader. */ + work_size = num_queued; + d_path_index = (void *)queued_paths_.device_pointer; + + compute_sorted_queued_paths( + DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY, kernel, num_paths_limit); + } + else if (num_queued < work_size) { + work_size = num_queued; + d_path_index = (void *)queued_paths_.device_pointer; + + if (kernel_is_shadow_path(kernel)) { + /* Compute array of active shadow paths for specific kernel. */ + compute_queued_paths(DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY, kernel); + } + else { + /* Compute array of active paths for specific kernel. */ + compute_queued_paths(DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY, kernel); + } + } + + work_size = min(work_size, num_paths_limit); + + DCHECK_LE(work_size, max_num_paths_); + + switch (kernel) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: { + /* Ray intersection kernels with integrator state. */ + void *args[] = {&d_path_index, const_cast(&work_size)}; + + queue_->enqueue(kernel, work_size, args); + break; + } + case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: + case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT: + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: { + /* Shading kernels with integrator state and render buffer. */ + void *d_render_buffer = (void *)buffers_->buffer.device_pointer; + void *args[] = {&d_path_index, &d_render_buffer, const_cast(&work_size)}; + + queue_->enqueue(kernel, work_size, args); + break; + } + + default: + LOG(FATAL) << "Unhandled kernel " << device_kernel_as_string(kernel) + << " used for path iteration, should never happen."; + break; + } +} + +void PathTraceWorkGPU::compute_sorted_queued_paths(DeviceKernel kernel, + DeviceKernel queued_kernel, + const int num_paths_limit) +{ + int d_queued_kernel = queued_kernel; + void *d_counter = integrator_state_gpu_.sort_key_counter[d_queued_kernel]; + void *d_prefix_sum = (void *)integrator_shader_sort_prefix_sum_.device_pointer; + assert(d_counter != nullptr && d_prefix_sum != nullptr); + + /* Compute prefix sum of number of active paths with each shader. */ + { + const int work_size = 1; + int max_shaders = device_scene_->data.max_shaders; + void *args[] = {&d_counter, &d_prefix_sum, &max_shaders}; + queue_->enqueue(DEVICE_KERNEL_PREFIX_SUM, work_size, args); + } + + queue_->zero_to_device(num_queued_paths_); + + /* Launch kernel to fill the active paths arrays. */ + { + /* TODO: this could be smaller for terminated paths based on amount of work we want + * to schedule, and also based on num_paths_limit. + * + * Also, when the number paths is limited it may be better to prefer paths from the + * end of the array since compaction would need to do less work. */ + const int work_size = kernel_max_active_main_path_index(queued_kernel); + + void *d_queued_paths = (void *)queued_paths_.device_pointer; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + void *args[] = {const_cast(&work_size), + const_cast(&num_paths_limit), + &d_queued_paths, + &d_num_queued_paths, + &d_counter, + &d_prefix_sum, + &d_queued_kernel}; + + queue_->enqueue(kernel, work_size, args); + } +} + +void PathTraceWorkGPU::compute_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel) +{ + int d_queued_kernel = queued_kernel; + + /* Launch kernel to fill the active paths arrays. */ + const int work_size = kernel_max_active_main_path_index(queued_kernel); + void *d_queued_paths = (void *)queued_paths_.device_pointer; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + void *args[] = { + const_cast(&work_size), &d_queued_paths, &d_num_queued_paths, &d_queued_kernel}; + + queue_->zero_to_device(num_queued_paths_); + queue_->enqueue(kernel, work_size, args); +} + +void PathTraceWorkGPU::compact_main_paths(const int num_active_paths) +{ + /* Early out if there is nothing that needs to be compacted. */ + if (num_active_paths == 0) { + max_active_main_path_index_ = 0; + return; + } + + const int min_compact_paths = 32; + if (max_active_main_path_index_ == num_active_paths || + max_active_main_path_index_ < min_compact_paths) { + return; + } + + /* Compact. */ + compact_paths(num_active_paths, + max_active_main_path_index_, + DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES); + + /* Adjust max active path index now we know which part of the array is actually used. */ + max_active_main_path_index_ = num_active_paths; +} + +void PathTraceWorkGPU::compact_shadow_paths() +{ + IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + const int num_active_paths = + queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW] + + queue_counter->num_queued[DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW]; + + /* Early out if there is nothing that needs to be compacted. */ + if (num_active_paths == 0) { + if (integrator_next_shadow_path_index_.data()[0] != 0) { + integrator_next_shadow_path_index_.data()[0] = 0; + queue_->copy_to_device(integrator_next_shadow_path_index_); + } + return; + } + + /* Compact if we can reduce the space used by half. Not always since + * compaction has a cost. */ + const float shadow_compact_ratio = 0.5f; + const int min_compact_paths = 32; + if (integrator_next_shadow_path_index_.data()[0] < num_active_paths * shadow_compact_ratio || + integrator_next_shadow_path_index_.data()[0] < min_compact_paths) { + return; + } + + /* Compact. */ + compact_paths(num_active_paths, + integrator_next_shadow_path_index_.data()[0], + DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES); + + /* Adjust max active path index now we know which part of the array is actually used. */ + integrator_next_shadow_path_index_.data()[0] = num_active_paths; + queue_->copy_to_device(integrator_next_shadow_path_index_); +} + +void PathTraceWorkGPU::compact_paths(const int num_active_paths, + const int max_active_path_index, + DeviceKernel terminated_paths_kernel, + DeviceKernel compact_paths_kernel, + DeviceKernel compact_kernel) +{ + /* Compact fragmented path states into the start of the array, moving any paths + * with index higher than the number of active paths into the gaps. */ + void *d_compact_paths = (void *)queued_paths_.device_pointer; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + + /* Create array with terminated paths that we can write to. */ + { + /* TODO: can the work size be reduced here? */ + int offset = num_active_paths; + int work_size = num_active_paths; + void *args[] = {&work_size, &d_compact_paths, &d_num_queued_paths, &offset}; + queue_->zero_to_device(num_queued_paths_); + queue_->enqueue(terminated_paths_kernel, work_size, args); + } + + /* Create array of paths that we need to compact, where the path index is bigger + * than the number of active paths. */ + { + int work_size = max_active_path_index; + void *args[] = { + &work_size, &d_compact_paths, &d_num_queued_paths, const_cast(&num_active_paths)}; + queue_->zero_to_device(num_queued_paths_); + queue_->enqueue(compact_paths_kernel, work_size, args); + } + + queue_->copy_from_device(num_queued_paths_); + queue_->synchronize(); + + int num_compact_paths = num_queued_paths_.data()[0]; + + /* Move paths into gaps. */ + if (num_compact_paths > 0) { + int work_size = num_compact_paths; + int active_states_offset = 0; + int terminated_states_offset = num_active_paths; + void *args[] = { + &d_compact_paths, &active_states_offset, &terminated_states_offset, &work_size}; + queue_->enqueue(compact_kernel, work_size, args); + } +} + +bool PathTraceWorkGPU::enqueue_work_tiles(bool &finished) +{ + /* If there are existing paths wait them to go to intersect closest kernel, which will align the + * wavefront of the existing and newly added paths. */ + /* TODO: Check whether counting new intersection kernels here will have positive affect on the + * performance. */ + const DeviceKernel kernel = get_most_queued_kernel(); + if (kernel != DEVICE_KERNEL_NUM && kernel != DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST) { + return false; + } + + int num_active_paths = num_active_main_paths_paths(); + + /* Don't schedule more work if canceling. */ + if (is_cancel_requested()) { + if (num_active_paths == 0) { + finished = true; + } + return false; + } + + finished = false; + + vector work_tiles; + + int max_num_camera_paths = max_num_paths_; + int num_predicted_splits = 0; + + if (has_shadow_catcher()) { + /* When there are shadow catchers in the scene bounce from them will split the state. So we + * make sure there is enough space in the path states array to fit split states. + * + * Basically, when adding N new paths we ensure that there is 2*N available path states, so + * that all the new paths can be split. + * + * Note that it is possible that some of the current states can still split, so need to make + * sure there is enough space for them as well. */ + + /* Number of currently in-flight states which can still split. */ + const int num_scheduled_possible_split = shadow_catcher_count_possible_splits(); + + const int num_available_paths = max_num_paths_ - num_active_paths; + const int num_new_paths = num_available_paths / 2; + max_num_camera_paths = max(num_active_paths, + num_active_paths + num_new_paths - num_scheduled_possible_split); + num_predicted_splits += num_scheduled_possible_split + num_new_paths; + } + + /* Schedule when we're out of paths or there are too few paths to keep the + * device occupied. */ + int num_paths = num_active_paths; + if (num_paths == 0 || num_paths < min_num_active_main_paths_) { + /* Get work tiles until the maximum number of path is reached. */ + while (num_paths < max_num_camera_paths) { + KernelWorkTile work_tile; + if (work_tile_scheduler_.get_work(&work_tile, max_num_camera_paths - num_paths)) { + work_tiles.push_back(work_tile); + num_paths += work_tile.w * work_tile.h * work_tile.num_samples; + } + else { + break; + } + } + + /* If we couldn't get any more tiles, we're done. */ + if (work_tiles.size() == 0 && num_paths == 0) { + finished = true; + return false; + } + } + + /* Initialize paths from work tiles. */ + if (work_tiles.size() == 0) { + return false; + } + + /* Compact state array when number of paths becomes small relative to the + * known maximum path index, which makes computing active index arrays slow. */ + compact_main_paths(num_active_paths); + + if (has_shadow_catcher()) { + integrator_next_main_path_index_.data()[0] = num_paths; + queue_->copy_to_device(integrator_next_main_path_index_); + } + + enqueue_work_tiles((device_scene_->data.bake.use) ? DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE : + DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA, + work_tiles.data(), + work_tiles.size(), + num_active_paths, + num_predicted_splits); + + return true; +} + +void PathTraceWorkGPU::enqueue_work_tiles(DeviceKernel kernel, + const KernelWorkTile work_tiles[], + const int num_work_tiles, + const int num_active_paths, + const int num_predicted_splits) +{ + /* Copy work tiles to device. */ + if (work_tiles_.size() < num_work_tiles) { + work_tiles_.alloc(num_work_tiles); + } + + int path_index_offset = num_active_paths; + int max_tile_work_size = 0; + for (int i = 0; i < num_work_tiles; i++) { + KernelWorkTile &work_tile = work_tiles_.data()[i]; + work_tile = work_tiles[i]; + + const int tile_work_size = work_tile.w * work_tile.h * work_tile.num_samples; + + work_tile.path_index_offset = path_index_offset; + work_tile.work_size = tile_work_size; + + path_index_offset += tile_work_size; + + max_tile_work_size = max(max_tile_work_size, tile_work_size); + } + + queue_->copy_to_device(work_tiles_); + + void *d_work_tiles = (void *)work_tiles_.device_pointer; + void *d_render_buffer = (void *)buffers_->buffer.device_pointer; + + /* Launch kernel. */ + void *args[] = {&d_work_tiles, + const_cast(&num_work_tiles), + &d_render_buffer, + const_cast(&max_tile_work_size)}; + + queue_->enqueue(kernel, max_tile_work_size * num_work_tiles, args); + + max_active_main_path_index_ = path_index_offset + num_predicted_splits; +} + +int PathTraceWorkGPU::num_active_main_paths_paths() +{ + IntegratorQueueCounter *queue_counter = integrator_queue_counter_.data(); + + int num_paths = 0; + for (int i = 0; i < DEVICE_KERNEL_INTEGRATOR_NUM; i++) { + DCHECK_GE(queue_counter->num_queued[i], 0) + << "Invalid number of queued states for kernel " + << device_kernel_as_string(static_cast(i)); + + if (!kernel_is_shadow_path((DeviceKernel)i)) { + num_paths += queue_counter->num_queued[i]; + } + } + + return num_paths; +} + +bool PathTraceWorkGPU::should_use_graphics_interop() +{ + /* There are few aspects with the graphics interop when using multiple devices caused by the fact + * that the PathTraceDisplay has a single texture: + * + * CUDA will return `CUDA_ERROR_NOT_SUPPORTED` from `cuGraphicsGLRegisterBuffer()` when + * attempting to register OpenGL PBO which has been mapped. Which makes sense, because + * otherwise one would run into a conflict of where the source of truth is. */ + if (has_multiple_works()) { + return false; + } + + if (!interop_use_checked_) { + Device *device = queue_->device; + interop_use_ = device->should_use_graphics_interop(); + + if (interop_use_) { + VLOG(2) << "Using graphics interop GPU display update."; + } + else { + VLOG(2) << "Using naive GPU display update."; + } + + interop_use_checked_ = true; + } + + return interop_use_; +} + +void PathTraceWorkGPU::copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) +{ + if (device_->have_error()) { + /* Don't attempt to update GPU display if the device has errors: the error state will make + * wrong decisions to happen about interop, causing more chained bugs. */ + return; + } + + if (!buffers_->buffer.device_pointer) { + LOG(WARNING) << "Request for GPU display update without allocated render buffers."; + return; + } + + if (should_use_graphics_interop()) { + if (copy_to_display_interop(display, pass_mode, num_samples)) { + return; + } + + /* If error happens when trying to use graphics interop fallback to the native implementation + * and don't attempt to use interop for the further updates. */ + interop_use_ = false; + } + + copy_to_display_naive(display, pass_mode, num_samples); +} + +void PathTraceWorkGPU::copy_to_display_naive(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) +{ + const int full_x = effective_buffer_params_.full_x; + const int full_y = effective_buffer_params_.full_y; + const int width = effective_buffer_params_.window_width; + const int height = effective_buffer_params_.window_height; + const int final_width = buffers_->params.window_width; + const int final_height = buffers_->params.window_height; + + const int texture_x = full_x - effective_full_params_.full_x + effective_buffer_params_.window_x; + const int texture_y = full_y - effective_full_params_.full_y + effective_buffer_params_.window_y; + + /* Re-allocate display memory if needed, and make sure the device pointer is allocated. + * + * NOTE: allocation happens to the final resolution so that no re-allocation happens on every + * change of the resolution divider. However, if the display becomes smaller, shrink the + * allocated memory as well. */ + if (display_rgba_half_.data_width != final_width || + display_rgba_half_.data_height != final_height) { + display_rgba_half_.alloc(final_width, final_height); + /* TODO(sergey): There should be a way to make sure device-side memory is allocated without + * transferring zeroes to the device. */ + queue_->zero_to_device(display_rgba_half_); + } + + PassAccessor::Destination destination(film_->get_display_pass()); + destination.d_pixels_half_rgba = display_rgba_half_.device_pointer; + + get_render_tile_film_pixels(destination, pass_mode, num_samples); + + queue_->copy_from_device(display_rgba_half_); + queue_->synchronize(); + + display->copy_pixels_to_texture(display_rgba_half_.data(), texture_x, texture_y, width, height); +} + +bool PathTraceWorkGPU::copy_to_display_interop(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) +{ + if (!device_graphics_interop_) { + device_graphics_interop_ = queue_->graphics_interop_create(); + } + + const DisplayDriver::GraphicsInterop graphics_interop_dst = display->graphics_interop_get(); + device_graphics_interop_->set_display_interop(graphics_interop_dst); + + const device_ptr d_rgba_half = device_graphics_interop_->map(); + if (!d_rgba_half) { + return false; + } + + PassAccessor::Destination destination = get_display_destination_template(display); + destination.d_pixels_half_rgba = d_rgba_half; + + get_render_tile_film_pixels(destination, pass_mode, num_samples); + + device_graphics_interop_->unmap(); + + return true; +} + +void PathTraceWorkGPU::destroy_gpu_resources(PathTraceDisplay *display) +{ + if (!device_graphics_interop_) { + return; + } + display->graphics_interop_activate(); + device_graphics_interop_ = nullptr; + display->graphics_interop_deactivate(); +} + +void PathTraceWorkGPU::get_render_tile_film_pixels(const PassAccessor::Destination &destination, + PassMode pass_mode, + int num_samples) +{ + const KernelFilm &kfilm = device_scene_->data.film; + + const PassAccessor::PassAccessInfo pass_access_info = get_display_pass_access_info(pass_mode); + const PassAccessorGPU pass_accessor(queue_.get(), pass_access_info, kfilm.exposure, num_samples); + + pass_accessor.get_render_tile_pixels(buffers_.get(), effective_buffer_params_, destination); +} + +int PathTraceWorkGPU::adaptive_sampling_converge_filter_count_active(float threshold, bool reset) +{ + const int num_active_pixels = adaptive_sampling_convergence_check_count_active(threshold, reset); + + if (num_active_pixels) { + enqueue_adaptive_sampling_filter_x(); + enqueue_adaptive_sampling_filter_y(); + queue_->synchronize(); + } + + return num_active_pixels; +} + +int PathTraceWorkGPU::adaptive_sampling_convergence_check_count_active(float threshold, bool reset) +{ + device_vector num_active_pixels(device_, "num_active_pixels", MEM_READ_WRITE); + num_active_pixels.alloc(1); + + queue_->zero_to_device(num_active_pixels); + + const int work_size = effective_buffer_params_.width * effective_buffer_params_.height; + + void *args[] = {&buffers_->buffer.device_pointer, + const_cast(&effective_buffer_params_.full_x), + const_cast(&effective_buffer_params_.full_y), + const_cast(&effective_buffer_params_.width), + const_cast(&effective_buffer_params_.height), + &threshold, + &reset, + &effective_buffer_params_.offset, + &effective_buffer_params_.stride, + &num_active_pixels.device_pointer}; + + queue_->enqueue(DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_CHECK, work_size, args); + + queue_->copy_from_device(num_active_pixels); + queue_->synchronize(); + + return num_active_pixels.data()[0]; +} + +void PathTraceWorkGPU::enqueue_adaptive_sampling_filter_x() +{ + const int work_size = effective_buffer_params_.height; + + void *args[] = {&buffers_->buffer.device_pointer, + &effective_buffer_params_.full_x, + &effective_buffer_params_.full_y, + &effective_buffer_params_.width, + &effective_buffer_params_.height, + &effective_buffer_params_.offset, + &effective_buffer_params_.stride}; + + queue_->enqueue(DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_X, work_size, args); +} + +void PathTraceWorkGPU::enqueue_adaptive_sampling_filter_y() +{ + const int work_size = effective_buffer_params_.width; + + void *args[] = {&buffers_->buffer.device_pointer, + &effective_buffer_params_.full_x, + &effective_buffer_params_.full_y, + &effective_buffer_params_.width, + &effective_buffer_params_.height, + &effective_buffer_params_.offset, + &effective_buffer_params_.stride}; + + queue_->enqueue(DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y, work_size, args); +} + +void PathTraceWorkGPU::cryptomatte_postproces() +{ + const int work_size = effective_buffer_params_.width * effective_buffer_params_.height; + + void *args[] = {&buffers_->buffer.device_pointer, + const_cast(&work_size), + &effective_buffer_params_.offset, + &effective_buffer_params_.stride}; + + queue_->enqueue(DEVICE_KERNEL_CRYPTOMATTE_POSTPROCESS, work_size, args); +} + +bool PathTraceWorkGPU::copy_render_buffers_from_device() +{ + queue_->copy_from_device(buffers_->buffer); + + /* Synchronize so that the CPU-side buffer is available at the exit of this function. */ + return queue_->synchronize(); +} + +bool PathTraceWorkGPU::copy_render_buffers_to_device() +{ + queue_->copy_to_device(buffers_->buffer); + + /* NOTE: The direct device access to the buffers only happens within this path trace work. The + * rest of communication happens via API calls which involves `copy_render_buffers_from_device()` + * which will perform synchronization as needed. */ + + return true; +} + +bool PathTraceWorkGPU::zero_render_buffers() +{ + queue_->zero_to_device(buffers_->buffer); + + return true; +} + +bool PathTraceWorkGPU::has_shadow_catcher() const +{ + return device_scene_->data.integrator.has_shadow_catcher; +} + +int PathTraceWorkGPU::shadow_catcher_count_possible_splits() +{ + if (max_active_main_path_index_ == 0) { + return 0; + } + + if (!has_shadow_catcher()) { + return 0; + } + + queue_->zero_to_device(num_queued_paths_); + + const int work_size = max_active_main_path_index_; + void *d_num_queued_paths = (void *)num_queued_paths_.device_pointer; + void *args[] = {const_cast(&work_size), &d_num_queued_paths}; + + queue_->enqueue(DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS, work_size, args); + queue_->copy_from_device(num_queued_paths_); + queue_->synchronize(); + + return num_queued_paths_.data()[0]; +} + +bool PathTraceWorkGPU::kernel_uses_sorting(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE); +} + +bool PathTraceWorkGPU::kernel_creates_shadow_paths(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); +} + +bool PathTraceWorkGPU::kernel_creates_ao_paths(DeviceKernel kernel) +{ + return (device_scene_->data.kernel_features & KERNEL_FEATURE_AO) && + (kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE); +} + +bool PathTraceWorkGPU::kernel_is_shadow_path(DeviceKernel kernel) +{ + return (kernel == DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW || + kernel == DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); +} + +int PathTraceWorkGPU::kernel_max_active_main_path_index(DeviceKernel kernel) +{ + return (kernel_is_shadow_path(kernel)) ? integrator_next_shadow_path_index_.data()[0] : + max_active_main_path_index_; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/path_trace_work_gpu.h b/intern/cycles/integrator/path_trace_work_gpu.h new file mode 100644 index 00000000000..c5e291e72db --- /dev/null +++ b/intern/cycles/integrator/path_trace_work_gpu.h @@ -0,0 +1,183 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/state.h" + +#include "device/graphics_interop.h" +#include "device/memory.h" +#include "device/queue.h" + +#include "integrator/path_trace_work.h" +#include "integrator/work_tile_scheduler.h" + +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +struct KernelWorkTile; + +/* Implementation of PathTraceWork which schedules work to the device in tiles which are sized + * to match device queue's number of path states. + * This implementation suits best devices which have a lot of integrator states, such as GPU. */ +class PathTraceWorkGPU : public PathTraceWork { + public: + PathTraceWorkGPU(Device *device, + Film *film, + DeviceScene *device_scene, + bool *cancel_requested_flag); + + virtual void alloc_work_memory() override; + virtual void init_execution() override; + + virtual void render_samples(RenderStatistics &statistics, + int start_sample, + int samples_num) override; + + virtual void copy_to_display(PathTraceDisplay *display, + PassMode pass_mode, + int num_samples) override; + virtual void destroy_gpu_resources(PathTraceDisplay *display) override; + + virtual bool copy_render_buffers_from_device() override; + virtual bool copy_render_buffers_to_device() override; + virtual bool zero_render_buffers() override; + + virtual int adaptive_sampling_converge_filter_count_active(float threshold, bool reset) override; + virtual void cryptomatte_postproces() override; + + protected: + void alloc_integrator_soa(); + void alloc_integrator_queue(); + void alloc_integrator_sorting(); + void alloc_integrator_path_split(); + + /* Returns DEVICE_KERNEL_NUM if there are no scheduled kernels. */ + DeviceKernel get_most_queued_kernel() const; + + void enqueue_reset(); + + bool enqueue_work_tiles(bool &finished); + void enqueue_work_tiles(DeviceKernel kernel, + const KernelWorkTile work_tiles[], + const int num_work_tiles, + const int num_active_paths, + const int num_predicted_splits); + + bool enqueue_path_iteration(); + void enqueue_path_iteration(DeviceKernel kernel, const int num_paths_limit = INT_MAX); + + void compute_queued_paths(DeviceKernel kernel, DeviceKernel queued_kernel); + void compute_sorted_queued_paths(DeviceKernel kernel, + DeviceKernel queued_kernel, + const int num_paths_limit); + + void compact_main_paths(const int num_active_paths); + void compact_shadow_paths(); + void compact_paths(const int num_active_paths, + const int max_active_path_index, + DeviceKernel terminated_paths_kernel, + DeviceKernel compact_paths_kernel, + DeviceKernel compact_kernel); + + int num_active_main_paths_paths(); + + /* Check whether graphics interop can be used for the PathTraceDisplay update. */ + bool should_use_graphics_interop(); + + /* Naive implementation of the `copy_to_display()` which performs film conversion on the + * device, then copies pixels to the host and pushes them to the `display`. */ + void copy_to_display_naive(PathTraceDisplay *display, PassMode pass_mode, int num_samples); + + /* Implementation of `copy_to_display()` which uses driver's OpenGL/GPU interoperability + * functionality, avoiding copy of pixels to the host. */ + bool copy_to_display_interop(PathTraceDisplay *display, PassMode pass_mode, int num_samples); + + /* Synchronously run film conversion kernel and store display result in the given destination. */ + void get_render_tile_film_pixels(const PassAccessor::Destination &destination, + PassMode pass_mode, + int num_samples); + + int adaptive_sampling_convergence_check_count_active(float threshold, bool reset); + void enqueue_adaptive_sampling_filter_x(); + void enqueue_adaptive_sampling_filter_y(); + + bool has_shadow_catcher() const; + + /* Count how many currently scheduled paths can still split. */ + int shadow_catcher_count_possible_splits(); + + /* Kernel properties. */ + bool kernel_uses_sorting(DeviceKernel kernel); + bool kernel_creates_shadow_paths(DeviceKernel kernel); + bool kernel_creates_ao_paths(DeviceKernel kernel); + bool kernel_is_shadow_path(DeviceKernel kernel); + int kernel_max_active_main_path_index(DeviceKernel kernel); + + /* Integrator queue. */ + unique_ptr queue_; + + /* Scheduler which gives work to path tracing threads. */ + WorkTileScheduler work_tile_scheduler_; + + /* Integrate state for paths. */ + IntegratorStateGPU integrator_state_gpu_; + /* SoA arrays for integrator state. */ + vector> integrator_state_soa_; + uint integrator_state_soa_kernel_features_; + int integrator_state_soa_volume_stack_size_ = 0; + /* Keep track of number of queued kernels. */ + device_vector integrator_queue_counter_; + /* Shader sorting. */ + device_vector integrator_shader_sort_counter_; + device_vector integrator_shader_raytrace_sort_counter_; + device_vector integrator_shader_sort_prefix_sum_; + /* Path split. */ + device_vector integrator_next_main_path_index_; + device_vector integrator_next_shadow_path_index_; + + /* Temporary buffer to get an array of queued path for a particular kernel. */ + device_vector queued_paths_; + device_vector num_queued_paths_; + + /* Temporary buffer for passing work tiles to kernel. */ + device_vector work_tiles_; + + /* Temporary buffer used by the copy_to_display() whenever graphics interoperability is not + * available. Is allocated on-demand. */ + device_vector display_rgba_half_; + + unique_ptr device_graphics_interop_; + + /* Cached result of device->should_use_graphics_interop(). */ + bool interop_use_checked_ = false; + bool interop_use_ = false; + + /* Maximum number of concurrent integrator states. */ + int max_num_paths_; + + /* Minimum number of paths which keeps the device bust. If the actual number of paths falls below + * this value more work will be scheduled. */ + int min_num_active_main_paths_; + + /* Maximum path index, effective number of paths used may be smaller than + * the size of the integrator_state_ buffer so can avoid iterating over the + * full buffer. */ + int max_active_main_path_index_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/render_scheduler.cpp b/intern/cycles/integrator/render_scheduler.cpp new file mode 100644 index 00000000000..f776d01ef67 --- /dev/null +++ b/intern/cycles/integrator/render_scheduler.cpp @@ -0,0 +1,1193 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/render_scheduler.h" + +#include "session/session.h" +#include "session/tile.h" +#include "util/log.h" +#include "util/math.h" +#include "util/time.h" + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Render scheduler. + */ + +RenderScheduler::RenderScheduler(TileManager &tile_manager, const SessionParams ¶ms) + : headless_(params.headless), + background_(params.background), + pixel_size_(params.pixel_size), + tile_manager_(tile_manager), + default_start_resolution_divider_(pixel_size_ * 8) +{ + use_progressive_noise_floor_ = !background_; +} + +void RenderScheduler::set_need_schedule_cryptomatte(bool need_schedule_cryptomatte) +{ + need_schedule_cryptomatte_ = need_schedule_cryptomatte; +} + +void RenderScheduler::set_need_schedule_rebalance(bool need_schedule_rebalance) +{ + need_schedule_rebalance_works_ = need_schedule_rebalance; +} + +bool RenderScheduler::is_background() const +{ + return background_; +} + +void RenderScheduler::set_denoiser_params(const DenoiseParams ¶ms) +{ + denoiser_params_ = params; +} + +void RenderScheduler::set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling) +{ + adaptive_sampling_ = adaptive_sampling; +} + +bool RenderScheduler::is_adaptive_sampling_used() const +{ + return adaptive_sampling_.use; +} + +void RenderScheduler::set_start_sample(int start_sample) +{ + start_sample_ = start_sample; +} + +int RenderScheduler::get_start_sample() const +{ + return start_sample_; +} + +void RenderScheduler::set_num_samples(int num_samples) +{ + num_samples_ = num_samples; +} + +int RenderScheduler::get_num_samples() const +{ + return num_samples_; +} + +void RenderScheduler::set_time_limit(double time_limit) +{ + time_limit_ = time_limit; +} + +double RenderScheduler::get_time_limit() const +{ + return time_limit_; +} + +int RenderScheduler::get_rendered_sample() const +{ + DCHECK_GT(get_num_rendered_samples(), 0); + + return start_sample_ + get_num_rendered_samples() - 1; +} + +int RenderScheduler::get_num_rendered_samples() const +{ + return state_.num_rendered_samples; +} + +void RenderScheduler::reset(const BufferParams &buffer_params, int num_samples) +{ + buffer_params_ = buffer_params; + + update_start_resolution_divider(); + + set_num_samples(num_samples); + + /* In background mode never do lower resolution render preview, as it is not really supported + * by the software. */ + if (background_) { + state_.resolution_divider = 1; + } + else { + /* NOTE: Divide by 2 because of the way how scheduling works: it advances resolution divider + * first and then initialized render work. */ + state_.resolution_divider = start_resolution_divider_ * 2; + } + + state_.num_rendered_samples = 0; + state_.last_display_update_time = 0.0; + state_.last_display_update_sample = -1; + + state_.last_rebalance_time = 0.0; + state_.num_rebalance_requested = 0; + state_.num_rebalance_changes = 0; + state_.last_rebalance_changed = false; + state_.need_rebalance_at_next_work = false; + + /* TODO(sergey): Choose better initial value. */ + /* NOTE: The adaptive sampling settings might not be available here yet. */ + state_.adaptive_sampling_threshold = 0.4f; + + state_.last_work_tile_was_denoised = false; + state_.tile_result_was_written = false; + state_.postprocess_work_scheduled = false; + state_.full_frame_work_scheduled = false; + state_.full_frame_was_written = false; + + state_.path_trace_finished = false; + + state_.start_render_time = 0.0; + state_.end_render_time = 0.0; + state_.time_limit_reached = false; + + state_.occupancy_num_samples = 0; + state_.occupancy = 1.0f; + + first_render_time_.path_trace_per_sample = 0.0; + first_render_time_.denoise_time = 0.0; + first_render_time_.display_update_time = 0.0; + + path_trace_time_.reset(); + denoise_time_.reset(); + adaptive_filter_time_.reset(); + display_update_time_.reset(); + rebalance_time_.reset(); +} + +void RenderScheduler::reset_for_next_tile() +{ + reset(buffer_params_, num_samples_); +} + +bool RenderScheduler::render_work_reschedule_on_converge(RenderWork &render_work) +{ + /* Move to the next resolution divider. Assume adaptive filtering is not needed during + * navigation. */ + if (state_.resolution_divider != pixel_size_) { + return false; + } + + if (render_work_reschedule_on_idle(render_work)) { + return true; + } + + state_.path_trace_finished = true; + + bool denoiser_delayed, denoiser_ready_to_display; + render_work.tile.denoise = work_need_denoise(denoiser_delayed, denoiser_ready_to_display); + + render_work.display.update = work_need_update_display(denoiser_delayed); + render_work.display.use_denoised_result = denoiser_ready_to_display; + + return false; +} + +bool RenderScheduler::render_work_reschedule_on_idle(RenderWork &render_work) +{ + if (!use_progressive_noise_floor_) { + return false; + } + + /* Move to the next resolution divider. Assume adaptive filtering is not needed during + * navigation. */ + if (state_.resolution_divider != pixel_size_) { + return false; + } + + if (adaptive_sampling_.use) { + if (state_.adaptive_sampling_threshold > adaptive_sampling_.threshold) { + state_.adaptive_sampling_threshold = max(state_.adaptive_sampling_threshold / 2, + adaptive_sampling_.threshold); + + render_work.adaptive_sampling.threshold = state_.adaptive_sampling_threshold; + render_work.adaptive_sampling.reset = true; + + return true; + } + } + + return false; +} + +void RenderScheduler::render_work_reschedule_on_cancel(RenderWork &render_work) +{ + VLOG(3) << "Schedule work for cancel."; + + /* Un-schedule samples: they will not be rendered and should not be counted. */ + state_.num_rendered_samples -= render_work.path_trace.num_samples; + + const bool has_rendered_samples = get_num_rendered_samples() != 0; + + /* Reset all fields of the previous work, canceling things like adaptive sampling filtering and + * denoising. + * However, need to preserve write requests, since those will not be possible to recover and + * writes are only to happen once. */ + const bool tile_write = render_work.tile.write; + const bool full_write = render_work.full.write; + + render_work = RenderWork(); + + render_work.tile.write = tile_write; + render_work.full.write = full_write; + + /* Do not write tile if it has zero samples it it, treat it similarly to all other tiles which + * got canceled. */ + if (!state_.tile_result_was_written && has_rendered_samples) { + render_work.tile.write = true; + } + + if (!state_.full_frame_was_written) { + render_work.full.write = true; + } + + /* Update current tile, but only if any sample was rendered. + * Allows to have latest state of tile visible while full buffer is being processed. + * + * Note that if there are no samples in the current tile its render buffer might have pixels + * remained from previous state. + * + * If the full result was written, then there is no way any updates were made to the render + * buffers. And the buffers might have been freed from the device, so display update is not + * possible. */ + if (has_rendered_samples && !state_.full_frame_was_written) { + render_work.display.update = true; + } +} + +bool RenderScheduler::done() const +{ + if (state_.resolution_divider != pixel_size_) { + return false; + } + + if (state_.path_trace_finished || state_.time_limit_reached) { + return true; + } + + return get_num_rendered_samples() >= num_samples_; +} + +RenderWork RenderScheduler::get_render_work() +{ + check_time_limit_reached(); + + const double time_now = time_dt(); + + if (done()) { + RenderWork render_work; + render_work.resolution_divider = state_.resolution_divider; + + if (!set_postprocess_render_work(&render_work)) { + set_full_frame_render_work(&render_work); + } + + if (!render_work) { + state_.end_render_time = time_now; + } + + update_state_for_render_work(render_work); + + return render_work; + } + + RenderWork render_work; + + if (state_.resolution_divider != pixel_size_) { + state_.resolution_divider = max(state_.resolution_divider / 2, pixel_size_); + state_.num_rendered_samples = 0; + state_.last_display_update_sample = -1; + } + + render_work.resolution_divider = state_.resolution_divider; + + render_work.path_trace.start_sample = get_start_sample_to_path_trace(); + render_work.path_trace.num_samples = get_num_samples_to_path_trace(); + + render_work.init_render_buffers = (render_work.path_trace.start_sample == get_start_sample()); + + /* NOTE: Rebalance scheduler requires current number of samples to not be advanced forward. */ + render_work.rebalance = work_need_rebalance(); + + /* NOTE: Advance number of samples now, so that filter and denoising check can see that all the + * samples are rendered. */ + state_.num_rendered_samples += render_work.path_trace.num_samples; + + render_work.adaptive_sampling.filter = work_need_adaptive_filter(); + render_work.adaptive_sampling.threshold = work_adaptive_threshold(); + render_work.adaptive_sampling.reset = false; + + bool denoiser_delayed, denoiser_ready_to_display; + render_work.tile.denoise = work_need_denoise(denoiser_delayed, denoiser_ready_to_display); + + render_work.tile.write = done(); + + render_work.display.update = work_need_update_display(denoiser_delayed); + render_work.display.use_denoised_result = denoiser_ready_to_display; + + if (done()) { + set_postprocess_render_work(&render_work); + } + + update_state_for_render_work(render_work); + + return render_work; +} + +void RenderScheduler::update_state_for_render_work(const RenderWork &render_work) +{ + const double time_now = time_dt(); + + if (render_work.rebalance) { + state_.last_rebalance_time = time_now; + ++state_.num_rebalance_requested; + } + + /* A fallback display update time, for the case there is an error of display update, or when + * there is no display at all. */ + if (render_work.display.update) { + state_.last_display_update_time = time_now; + state_.last_display_update_sample = state_.num_rendered_samples; + } + + state_.last_work_tile_was_denoised = render_work.tile.denoise; + state_.tile_result_was_written |= render_work.tile.write; + state_.full_frame_was_written |= render_work.full.write; +} + +bool RenderScheduler::set_postprocess_render_work(RenderWork *render_work) +{ + if (state_.postprocess_work_scheduled) { + return false; + } + state_.postprocess_work_scheduled = true; + + bool any_scheduled = false; + + if (need_schedule_cryptomatte_) { + render_work->cryptomatte.postprocess = true; + any_scheduled = true; + } + + if (denoiser_params_.use && !state_.last_work_tile_was_denoised) { + render_work->tile.denoise = !tile_manager_.has_multiple_tiles(); + any_scheduled = true; + } + + if (!state_.tile_result_was_written) { + render_work->tile.write = true; + any_scheduled = true; + } + + if (any_scheduled) { + render_work->display.update = true; + } + + return any_scheduled; +} + +void RenderScheduler::set_full_frame_render_work(RenderWork *render_work) +{ + if (state_.full_frame_work_scheduled) { + return; + } + + if (!tile_manager_.has_multiple_tiles()) { + /* There is only single tile, so all work has been performed already. */ + return; + } + + if (!tile_manager_.done()) { + /* There are still tiles to be rendered. */ + return; + } + + if (state_.full_frame_was_written) { + return; + } + + state_.full_frame_work_scheduled = true; + + render_work->full.write = true; +} + +/* Knowing time which it took to complete a task at the current resolution divider approximate how + * long it would have taken to complete it at a final resolution. */ +static double approximate_final_time(const RenderWork &render_work, double time) +{ + if (render_work.resolution_divider == 1) { + return time; + } + + const double resolution_divider_sq = render_work.resolution_divider * + render_work.resolution_divider; + return time * resolution_divider_sq; +} + +void RenderScheduler::report_work_begin(const RenderWork &render_work) +{ + /* Start counting render time when rendering samples at their final resolution. + * + * NOTE: The work might have the path trace part be all zero: this happens when a post-processing + * work is scheduled after the path tracing. Checking for just a start sample doesn't work here + * because it might be wrongly 0. Check for whether path tracing is actually happening as it is + * expected to happen in the first work. */ + if (render_work.resolution_divider == pixel_size_ && render_work.path_trace.num_samples != 0 && + render_work.path_trace.start_sample == get_start_sample()) { + state_.start_render_time = time_dt(); + } +} + +void RenderScheduler::report_path_trace_time(const RenderWork &render_work, + double time, + bool is_cancelled) +{ + path_trace_time_.add_wall(time); + + if (is_cancelled) { + return; + } + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_is_usable_for_first_render_estimation(render_work)) { + first_render_time_.path_trace_per_sample = final_time_approx / + render_work.path_trace.num_samples; + } + + if (work_report_reset_average(render_work)) { + path_trace_time_.reset_average(); + } + + path_trace_time_.add_average(final_time_approx, render_work.path_trace.num_samples); + + VLOG(4) << "Average path tracing time: " << path_trace_time_.get_average() << " seconds."; +} + +void RenderScheduler::report_path_trace_occupancy(const RenderWork &render_work, float occupancy) +{ + state_.occupancy_num_samples = render_work.path_trace.num_samples; + state_.occupancy = occupancy; + VLOG(4) << "Measured path tracing occupancy: " << occupancy; +} + +void RenderScheduler::report_adaptive_filter_time(const RenderWork &render_work, + double time, + bool is_cancelled) +{ + adaptive_filter_time_.add_wall(time); + + if (is_cancelled) { + return; + } + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_report_reset_average(render_work)) { + adaptive_filter_time_.reset_average(); + } + + adaptive_filter_time_.add_average(final_time_approx, render_work.path_trace.num_samples); + + VLOG(4) << "Average adaptive sampling filter time: " << adaptive_filter_time_.get_average() + << " seconds."; +} + +void RenderScheduler::report_denoise_time(const RenderWork &render_work, double time) +{ + denoise_time_.add_wall(time); + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_is_usable_for_first_render_estimation(render_work)) { + first_render_time_.denoise_time = final_time_approx; + } + + if (work_report_reset_average(render_work)) { + denoise_time_.reset_average(); + } + + denoise_time_.add_average(final_time_approx); + + VLOG(4) << "Average denoising time: " << denoise_time_.get_average() << " seconds."; +} + +void RenderScheduler::report_display_update_time(const RenderWork &render_work, double time) +{ + display_update_time_.add_wall(time); + + const double final_time_approx = approximate_final_time(render_work, time); + + if (work_is_usable_for_first_render_estimation(render_work)) { + first_render_time_.display_update_time = final_time_approx; + } + + if (work_report_reset_average(render_work)) { + display_update_time_.reset_average(); + } + + display_update_time_.add_average(final_time_approx); + + VLOG(4) << "Average display update time: " << display_update_time_.get_average() << " seconds."; + + /* Move the display update moment further in time, so that logic which checks when last update + * did happen have more reliable point in time (without path tracing and denoising parts of the + * render work). */ + state_.last_display_update_time = time_dt(); +} + +void RenderScheduler::report_rebalance_time(const RenderWork &render_work, + double time, + bool balance_changed) +{ + rebalance_time_.add_wall(time); + + if (work_report_reset_average(render_work)) { + rebalance_time_.reset_average(); + } + + rebalance_time_.add_average(time); + + if (balance_changed) { + ++state_.num_rebalance_changes; + } + + state_.last_rebalance_changed = balance_changed; + + VLOG(4) << "Average rebalance time: " << rebalance_time_.get_average() << " seconds."; +} + +string RenderScheduler::full_report() const +{ + const double render_wall_time = state_.end_render_time - state_.start_render_time; + const int num_rendered_samples = get_num_rendered_samples(); + + string result = "\nRender Scheduler Summary\n\n"; + + { + string mode; + if (headless_) { + mode = "Headless"; + } + else if (background_) { + mode = "Background"; + } + else { + mode = "Interactive"; + } + result += "Mode: " + mode + "\n"; + } + + result += "Resolution: " + to_string(buffer_params_.width) + "x" + + to_string(buffer_params_.height) + "\n"; + + result += "\nAdaptive sampling:\n"; + result += " Use: " + string_from_bool(adaptive_sampling_.use) + "\n"; + if (adaptive_sampling_.use) { + result += " Step: " + to_string(adaptive_sampling_.adaptive_step) + "\n"; + result += " Min Samples: " + to_string(adaptive_sampling_.min_samples) + "\n"; + result += " Threshold: " + to_string(adaptive_sampling_.threshold) + "\n"; + } + + result += "\nDenoiser:\n"; + result += " Use: " + string_from_bool(denoiser_params_.use) + "\n"; + if (denoiser_params_.use) { + result += " Type: " + string(denoiserTypeToHumanReadable(denoiser_params_.type)) + "\n"; + result += " Start Sample: " + to_string(denoiser_params_.start_sample) + "\n"; + + string passes = "Color"; + if (denoiser_params_.use_pass_albedo) { + passes += ", Albedo"; + } + if (denoiser_params_.use_pass_normal) { + passes += ", Normal"; + } + + result += " Passes: " + passes + "\n"; + } + + if (state_.num_rebalance_requested) { + result += "\nRebalancer:\n"; + result += " Number of requested rebalances: " + to_string(state_.num_rebalance_requested) + + "\n"; + result += " Number of performed rebalances: " + to_string(state_.num_rebalance_changes) + + "\n"; + } + + result += "\nTime (in seconds):\n"; + result += string_printf(" %20s %20s %20s\n", "", "Wall", "Average"); + result += string_printf(" %20s %20f %20f\n", + "Path Tracing", + path_trace_time_.get_wall(), + path_trace_time_.get_average()); + + if (adaptive_sampling_.use) { + result += string_printf(" %20s %20f %20f\n", + "Adaptive Filter", + adaptive_filter_time_.get_wall(), + adaptive_filter_time_.get_average()); + } + + if (denoiser_params_.use) { + result += string_printf( + " %20s %20f %20f\n", "Denoiser", denoise_time_.get_wall(), denoise_time_.get_average()); + } + + result += string_printf(" %20s %20f %20f\n", + "Display Update", + display_update_time_.get_wall(), + display_update_time_.get_average()); + + if (state_.num_rebalance_requested) { + result += string_printf(" %20s %20f %20f\n", + "Rebalance", + rebalance_time_.get_wall(), + rebalance_time_.get_average()); + } + + const double total_time = path_trace_time_.get_wall() + adaptive_filter_time_.get_wall() + + denoise_time_.get_wall() + display_update_time_.get_wall(); + result += "\n Total: " + to_string(total_time) + "\n"; + + result += string_printf( + "\nRendered %d samples in %f seconds\n", num_rendered_samples, render_wall_time); + + /* When adaptive sampling is used the average time becomes meaningless, because different samples + * will likely render different number of pixels. */ + if (!adaptive_sampling_.use) { + result += string_printf("Average time per sample: %f seconds\n", + render_wall_time / num_rendered_samples); + } + + return result; +} + +double RenderScheduler::guess_display_update_interval_in_seconds() const +{ + return guess_display_update_interval_in_seconds_for_num_samples(state_.num_rendered_samples); +} + +double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples( + int num_rendered_samples) const +{ + double update_interval = guess_display_update_interval_in_seconds_for_num_samples_no_limit( + num_rendered_samples); + + if (time_limit_ != 0.0 && state_.start_render_time != 0.0) { + const double remaining_render_time = max(0.0, + time_limit_ - (time_dt() - state_.start_render_time)); + + update_interval = min(update_interval, remaining_render_time); + } + + return update_interval; +} + +/* TODO(sergey): This is just a quick implementation, exact values might need to be tweaked based + * on a more careful experiments with viewport rendering. */ +double RenderScheduler::guess_display_update_interval_in_seconds_for_num_samples_no_limit( + int num_rendered_samples) const +{ + /* TODO(sergey): Need a decision on whether this should be using number of samples rendered + * within the current render session, or use absolute number of samples with the start sample + * taken into account. It will depend on whether the start sample offset clears the render + * buffer. */ + + if (state_.need_rebalance_at_next_work) { + return 0.1; + } + if (state_.last_rebalance_changed) { + return 0.2; + } + + if (headless_) { + /* In headless mode do rare updates, so that the device occupancy is high, but there are still + * progress messages printed to the logs. */ + return 30.0; + } + + if (background_) { + if (num_rendered_samples < 32) { + return 1.0; + } + return 2.0; + } + + /* Render time and number of samples rendered are used to figure out the display update interval. + * Render time is used to allow for fast display updates in the first few seconds of rendering + * on fast devices. Number of samples rendered is used to allow for potentially quicker display + * updates on slow devices during the first few samples. */ + const double render_time = path_trace_time_.get_wall(); + if (render_time < 1) { + return 0.1; + } + if (render_time < 2) { + return 0.25; + } + if (render_time < 4) { + return 0.5; + } + if (render_time < 8 || num_rendered_samples < 32) { + return 1.0; + } + return 2.0; +} + +int RenderScheduler::calculate_num_samples_per_update() const +{ + const double time_per_sample_average = path_trace_time_.get_average(); + const double num_samples_in_second = pixel_size_ * pixel_size_ / time_per_sample_average; + + const double update_interval_in_seconds = guess_display_update_interval_in_seconds(); + + return max(int(num_samples_in_second * update_interval_in_seconds), 1); +} + +int RenderScheduler::get_start_sample_to_path_trace() const +{ + return start_sample_ + state_.num_rendered_samples; +} + +/* Round number of samples to the closest power of two. + * Rounding might happen to higher or lower value depending on which one is closer. Such behavior + * allows to have number of samples to be power of two without diverging from the planned number of + * samples too much. */ +static inline uint round_num_samples_to_power_of_2(const uint num_samples) +{ + if (num_samples == 1) { + return 1; + } + + if (is_power_of_two(num_samples)) { + return num_samples; + } + + const uint num_samples_up = next_power_of_two(num_samples); + const uint num_samples_down = num_samples_up - (num_samples_up >> 1); + + const uint delta_up = num_samples_up - num_samples; + const uint delta_down = num_samples - num_samples_down; + + if (delta_up <= delta_down) { + return num_samples_up; + } + + return num_samples_down; +} + +int RenderScheduler::get_num_samples_to_path_trace() const +{ + if (state_.resolution_divider != pixel_size_) { + return get_num_samples_during_navigation(state_.resolution_divider); + } + + /* Always start full resolution render with a single sample. Gives more instant feedback to + * artists, and allows to gather information for a subsequent path tracing works. Do it in the + * headless mode as well, to give some estimate of how long samples are taking. */ + if (state_.num_rendered_samples == 0) { + return 1; + } + + const int num_samples_per_update = calculate_num_samples_per_update(); + const int path_trace_start_sample = get_start_sample_to_path_trace(); + + /* Round number of samples to a power of two, so that division of path states into tiles goes in + * a more integer manner. + * This might make it so updates happens more rarely due to rounding up. In the test scenes this + * is not huge deal because it is not seen that more than 8 samples can be rendered between + * updates. If that becomes a problem we can add some extra rules like never allow to round up + * more than N samples. */ + const int num_samples_pot = round_num_samples_to_power_of_2(num_samples_per_update); + + const int max_num_samples_to_render = start_sample_ + num_samples_ - path_trace_start_sample; + + int num_samples_to_render = min(num_samples_pot, max_num_samples_to_render); + + /* When enough statistics is available and doing an offline rendering prefer to keep device + * occupied. */ + if (state_.occupancy_num_samples && (background_ || headless_)) { + /* Keep occupancy at about 0.5 (this is more of an empirical figure which seems to match scenes + * with good performance without forcing occupancy to be higher). */ + int num_samples_to_occupy = state_.occupancy_num_samples; + if (state_.occupancy < 0.5f) { + num_samples_to_occupy = lround(state_.occupancy_num_samples * 0.7f / state_.occupancy); + } + + num_samples_to_render = max(num_samples_to_render, + min(num_samples_to_occupy, max_num_samples_to_render)); + } + + /* If adaptive sampling is not use, render as many samples per update as possible, keeping the + * device fully occupied, without much overhead of display updates. */ + if (!adaptive_sampling_.use) { + return num_samples_to_render; + } + + /* TODO(sergey): Add extra "clamping" here so that none of the filtering points is missing. This + * is to ensure that the final render is pixel-matched regardless of how many samples per second + * compute device can do. */ + + return adaptive_sampling_.align_samples(path_trace_start_sample, num_samples_to_render); +} + +int RenderScheduler::get_num_samples_during_navigation(int resolution_divider) const +{ + /* Special trick for fast navigation: schedule multiple samples during fast navigation + * (which will prefer to use lower resolution to keep up with refresh rate). This gives more + * usable visual feedback for artists. There are a couple of tricks though. */ + + if (is_denoise_active_during_update()) { + /* When denoising is used during navigation prefer using a higher resolution with less samples + * (scheduling less samples here will make it so the resolution_divider calculation will use a + * lower value for the divider). This is because both OpenImageDenoiser and OptiX denoiser + * give visually better results on a higher resolution image with less samples. */ + return 1; + } + + if (resolution_divider <= pixel_size_) { + /* When resolution divider is at or below pixel size, schedule one sample. This doesn't effect + * the sample count at this resolution division, but instead assists in the calculation of + * the resolution divider. */ + return 1; + } + + if (resolution_divider == pixel_size_ * 2) { + /* When resolution divider is the previous step to the final resolution, schedule two samples. + * This is so that rendering on lower resolution does not exceed time that it takes to render + * first sample at the full resolution. */ + return 2; + } + + /* Always render 4 samples, even if scene is configured for less. + * The idea here is to have enough information on the screen. Resolution divider of 2 allows us + * to have 4 time extra samples, so overall worst case timing is the same as the final resolution + * at one sample. */ + return 4; +} + +bool RenderScheduler::work_need_adaptive_filter() const +{ + return adaptive_sampling_.need_filter(get_rendered_sample()); +} + +float RenderScheduler::work_adaptive_threshold() const +{ + if (!use_progressive_noise_floor_) { + return adaptive_sampling_.threshold; + } + + return max(state_.adaptive_sampling_threshold, adaptive_sampling_.threshold); +} + +bool RenderScheduler::work_need_denoise(bool &delayed, bool &ready_to_display) +{ + delayed = false; + ready_to_display = true; + + if (!denoiser_params_.use) { + /* Denoising is disabled, no need to scheduler work for it. */ + return false; + } + + /* When multiple tiles are used the full frame will be denoised. + * Avoid per-tile denoising to save up render time. */ + if (tile_manager_.has_multiple_tiles()) { + return false; + } + + if (done()) { + /* Always denoise at the last sample. */ + return true; + } + + if (background_) { + /* Background render, only denoise when rendering the last sample. */ + /* TODO(sergey): Follow similar logic to viewport, giving an overview of how final denoised + * image looks like even for the background rendering. */ + return false; + } + + /* Viewport render. */ + + /* Navigation might render multiple samples at a lower resolution. Those are not to be counted as + * final samples. */ + const int num_samples_finished = state_.resolution_divider == pixel_size_ ? + state_.num_rendered_samples : + 1; + + /* Immediately denoise when we reach the start sample or last sample. */ + if (num_samples_finished == denoiser_params_.start_sample || + num_samples_finished == num_samples_) { + return true; + } + + /* Do not denoise until the sample at which denoising should start is reached. */ + if (num_samples_finished < denoiser_params_.start_sample) { + ready_to_display = false; + return false; + } + + /* Avoid excessive denoising in viewport after reaching a certain sample count and render time. + */ + /* TODO(sergey): Consider making time interval and sample configurable. */ + delayed = (path_trace_time_.get_wall() > 4 && num_samples_finished >= 20 && + (time_dt() - state_.last_display_update_time) < 1.0); + + return !delayed; +} + +bool RenderScheduler::work_need_update_display(const bool denoiser_delayed) +{ + if (headless_) { + /* Force disable display update in headless mode. There will be nothing to display the + * in-progress result. */ + return false; + } + + if (denoiser_delayed) { + /* If denoiser has been delayed the display can not be updated as it will not contain + * up-to-date state of the render result. */ + return false; + } + + if (!adaptive_sampling_.use) { + /* When adaptive sampling is not used the work is scheduled in a way that they keep render + * device busy for long enough, so that the display update can happen right after the + * rendering. */ + return true; + } + + if (done() || state_.last_display_update_sample == -1) { + /* Make sure an initial and final results of adaptive sampling is communicated ot the display. + */ + return true; + } + + /* For the development purposes of adaptive sampling it might be very useful to see all updates + * of active pixels after convergence check. However, it would cause a slowdown for regular usage + * users. Possibly, make it a debug panel option to allow rapid update to ease development + * without need to re-compiled. */ + // if (work_need_adaptive_filter()) { + // return true; + // } + + /* When adaptive sampling is used, its possible that only handful of samples of a very simple + * scene will be scheduled to a powerful device (in order to not "miss" any of filtering points). + * We take care of skipping updates here based on when previous display update did happen. */ + const double update_interval = guess_display_update_interval_in_seconds_for_num_samples( + state_.last_display_update_sample); + return (time_dt() - state_.last_display_update_time) > update_interval; +} + +bool RenderScheduler::work_need_rebalance() +{ + /* This is the minimum time, as the rebalancing can not happen more often than the path trace + * work. */ + static const double kRebalanceIntervalInSeconds = 1; + + if (!need_schedule_rebalance_works_) { + return false; + } + + if (state_.resolution_divider != pixel_size_) { + /* Don't rebalance at a non-final resolution divider. Some reasons for this: + * - It will introduce unnecessary during navigation. + * - Per-render device timing information is not very reliable yet. */ + return false; + } + + if (state_.num_rendered_samples == 0) { + state_.need_rebalance_at_next_work = true; + return false; + } + + if (state_.need_rebalance_at_next_work) { + state_.need_rebalance_at_next_work = false; + return true; + } + + if (state_.last_rebalance_changed) { + return true; + } + + return (time_dt() - state_.last_rebalance_time) > kRebalanceIntervalInSeconds; +} + +void RenderScheduler::update_start_resolution_divider() +{ + if (start_resolution_divider_ == 0) { + /* Resolution divider has never been calculated before: use default resolution, so that we have + * somewhat good initial behavior, giving a chance to collect real numbers. */ + start_resolution_divider_ = default_start_resolution_divider_; + VLOG(3) << "Initial resolution divider is " << start_resolution_divider_; + return; + } + + if (first_render_time_.path_trace_per_sample == 0.0) { + /* Not enough information to calculate better resolution, keep the existing one. */ + return; + } + + const double desired_update_interval_in_seconds = + guess_viewport_navigation_update_interval_in_seconds(); + + const double actual_time_per_update = first_render_time_.path_trace_per_sample + + first_render_time_.denoise_time + + first_render_time_.display_update_time; + + /* Allow some percent of tolerance, so that if the render time is close enough to the higher + * resolution we prefer to use it instead of going way lower resolution and time way below the + * desired one. */ + const int resolution_divider_for_update = calculate_resolution_divider_for_time( + desired_update_interval_in_seconds * 1.4, actual_time_per_update); + + /* TODO(sergey): Need to add hysteresis to avoid resolution divider bouncing around when actual + * render time is somewhere on a boundary between two resolutions. */ + + /* Never increase resolution to higher than the pixel size (which is possible if the scene is + * simple and compute device is fast). */ + start_resolution_divider_ = max(resolution_divider_for_update, pixel_size_); + + VLOG(3) << "Calculated resolution divider is " << start_resolution_divider_; +} + +double RenderScheduler::guess_viewport_navigation_update_interval_in_seconds() const +{ + if (is_denoise_active_during_update()) { + /* Use lower value than the non-denoised case to allow having more pixels to reconstruct the + * image from. With the faster updates and extra compute required the resolution becomes too + * low to give usable feedback. */ + /* NOTE: Based on performance of OpenImageDenoiser on CPU. For OptiX denoiser or other denoiser + * on GPU the value might need to become lower for faster navigation. */ + return 1.0 / 12.0; + } + + /* For the best match with the Blender's viewport the refresh ratio should be 60fps. This will + * avoid "jelly" effects. However, on a non-trivial scenes this can only be achieved with high + * values of the resolution divider which does not give very pleasant updates during navigation. + * Choose less frequent updates to allow more noise-free and higher resolution updates. */ + + /* TODO(sergey): Can look into heuristic which will allow to have 60fps if the resolution divider + * is not too high. Alternatively, synchronize Blender's overlays updates to Cycles updates. */ + + return 1.0 / 30.0; +} + +bool RenderScheduler::is_denoise_active_during_update() const +{ + if (!denoiser_params_.use) { + return false; + } + + if (denoiser_params_.start_sample > 1) { + return false; + } + + return true; +} + +bool RenderScheduler::work_is_usable_for_first_render_estimation(const RenderWork &render_work) +{ + return render_work.resolution_divider == pixel_size_ && + render_work.path_trace.start_sample == start_sample_; +} + +bool RenderScheduler::work_report_reset_average(const RenderWork &render_work) +{ + /* When rendering at a non-final resolution divider time average is not very useful because it + * will either bias average down (due to lower render times on the smaller images) or will give + * incorrect result when trying to estimate time which would have spent on the final resolution. + * + * So we only accumulate average for the latest resolution divider which was rendered. */ + return render_work.resolution_divider != pixel_size_; +} + +void RenderScheduler::check_time_limit_reached() +{ + if (time_limit_ == 0.0) { + /* No limit is enforced. */ + return; + } + + if (state_.start_render_time == 0.0) { + /* Rendering did not start yet. */ + return; + } + + const double current_time = time_dt(); + + if (current_time - state_.start_render_time < time_limit_) { + /* Time limit is not reached yet. */ + return; + } + + state_.time_limit_reached = true; + state_.end_render_time = current_time; +} + +/* -------------------------------------------------------------------- + * Utility functions. + */ + +int RenderScheduler::calculate_resolution_divider_for_time(double desired_time, double actual_time) +{ + /* TODO(sergey): There should a non-iterative analytical formula here. */ + + int resolution_divider = 1; + + /* This algorithm iterates through resolution dividers until a divider is found that achieves + * the desired render time. A limit of default_start_resolution_divider_ is put in place as the + * maximum resolution divider to avoid an unreadable viewport due to a low resolution. + * pre_resolution_division_samples and post_resolution_division_samples are used in this + * calculation to better predict the performance impact of changing resolution divisions as + * the sample count can also change between resolution divisions. */ + while (actual_time > desired_time && resolution_divider < default_start_resolution_divider_) { + int pre_resolution_division_samples = get_num_samples_during_navigation(resolution_divider); + resolution_divider = resolution_divider * 2; + int post_resolution_division_samples = get_num_samples_during_navigation(resolution_divider); + actual_time /= 4.0 * pre_resolution_division_samples / post_resolution_division_samples; + } + + return resolution_divider; +} + +int calculate_resolution_divider_for_resolution(int width, int height, int resolution) +{ + if (resolution == INT_MAX) { + return 1; + } + + int resolution_divider = 1; + while (width * height > resolution * resolution) { + width = max(1, width / 2); + height = max(1, height / 2); + + resolution_divider <<= 1; + } + + return resolution_divider; +} + +int calculate_resolution_for_divider(int width, int height, int resolution_divider) +{ + const int pixel_area = width * height; + const int resolution = lround(sqrt(pixel_area)); + + return resolution / resolution_divider; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/render_scheduler.h b/intern/cycles/integrator/render_scheduler.h new file mode 100644 index 00000000000..d7b7413ae31 --- /dev/null +++ b/intern/cycles/integrator/render_scheduler.h @@ -0,0 +1,466 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/adaptive_sampling.h" +#include "integrator/denoiser.h" /* For DenoiseParams. */ +#include "session/buffers.h" +#include "util/string.h" + +CCL_NAMESPACE_BEGIN + +class SessionParams; +class TileManager; + +class RenderWork { + public: + int resolution_divider = 1; + + /* Initialize render buffers. + * Includes steps like zeroing the buffer on the device, and optional reading of pixels from the + * baking target. */ + bool init_render_buffers = false; + + /* Path tracing samples information. */ + struct { + int start_sample = 0; + int num_samples = 0; + } path_trace; + + struct { + /* Check for convergency and filter the mask. */ + bool filter = false; + + float threshold = 0.0f; + + /* Reset convergency flag when filtering, forcing a re-check of whether pixel did converge. */ + bool reset = false; + } adaptive_sampling; + + struct { + bool postprocess = false; + } cryptomatte; + + /* Work related on the current tile. */ + struct { + /* Write render buffers of the current tile. + * + * It is up to the path trace to decide whether writing should happen via user-provided + * callback into the rendering software, or via tile manager into a partial file. */ + bool write = false; + + bool denoise = false; + } tile; + + /* Work related on the full-frame render buffer. */ + struct { + /* Write full render result. + * Implies reading the partial file from disk. */ + bool write = false; + } full; + + /* Display which is used to visualize render result. */ + struct { + /* Display needs to be updated for the new render. */ + bool update = false; + + /* Display can use denoised result if available. */ + bool use_denoised_result = true; + } display; + + /* Re-balance multi-device scheduling after rendering this work. + * Note that the scheduler does not know anything about devices, so if there is only a single + * device used, then it is up for the PathTracer to ignore the balancing. */ + bool rebalance = false; + + /* Conversion to bool, to simplify checks about whether there is anything to be done for this + * work. */ + inline operator bool() const + { + return path_trace.num_samples || adaptive_sampling.filter || display.update || tile.denoise || + tile.write || full.write; + } +}; + +class RenderScheduler { + public: + RenderScheduler(TileManager &tile_manager, const SessionParams ¶ms); + + /* Specify whether cryptomatte-related works are to be scheduled. */ + void set_need_schedule_cryptomatte(bool need_schedule_cryptomatte); + + /* Allows to disable work re-balancing works, allowing to schedule as much to a single device + * as possible. */ + void set_need_schedule_rebalance(bool need_schedule_rebalance); + + bool is_background() const; + + void set_denoiser_params(const DenoiseParams ¶ms); + void set_adaptive_sampling(const AdaptiveSampling &adaptive_sampling); + + bool is_adaptive_sampling_used() const; + + /* Start sample for path tracing. + * The scheduler will schedule work using this sample as the first one. */ + void set_start_sample(int start_sample); + int get_start_sample() const; + + /* Number of samples to render, starting from start sample. + * The scheduler will schedule work in the range of + * [start_sample, start_sample + num_samples - 1], inclusively. */ + void set_num_samples(int num_samples); + int get_num_samples() const; + + /* Time limit for the path tracing tasks, in minutes. + * Zero disables the limit. */ + void set_time_limit(double time_limit); + double get_time_limit() const; + + /* Get sample up to which rendering has been done. + * This is an absolute 0-based value. + * + * For example, if start sample is 10 and and 5 samples were rendered, then this call will + * return 14. + * + * If there were no samples rendered, then the behavior is undefined. */ + int get_rendered_sample() const; + + /* Get number of samples rendered within the current scheduling session. + * + * For example, if start sample is 10 and and 5 samples were rendered, then this call will + * return 5. + * + * Note that this is based on the scheduling information. In practice this means that if someone + * requested for work to render the scheduler considers the work done. */ + int get_num_rendered_samples() const; + + /* Reset scheduler, indicating that rendering will happen from scratch. + * Resets current rendered state, as well as scheduling information. */ + void reset(const BufferParams &buffer_params, int num_samples); + + /* Reset scheduler upon switching to a next tile. + * Will keep the same number of samples and full-frame render parameters, but will reset progress + * and allow schedule renders works from the beginning of the new tile. */ + void reset_for_next_tile(); + + /* Reschedule adaptive sampling work when all pixels did converge. + * If there is nothing else to be done for the adaptive sampling (pixels did converge to the + * final threshold) then false is returned and the render scheduler will stop scheduling path + * tracing works. Otherwise will modify the work's adaptive sampling settings to continue with + * a lower threshold. */ + bool render_work_reschedule_on_converge(RenderWork &render_work); + + /* Reschedule adaptive sampling work when the device is mostly on idle, but not all pixels yet + * converged. + * If re-scheduling is not possible (adaptive sampling is happening with the final threshold, and + * the path tracer is to finish the current pixels) then false is returned. */ + bool render_work_reschedule_on_idle(RenderWork &render_work); + + /* Reschedule work when rendering has been requested to cancel. + * + * Will skip all work which is not needed anymore because no more samples will be added (for + * example, adaptive sampling filtering and convergence check will be skipped). + * Will enable all work needed to make sure all passes are communicated to the software. + * + * NOTE: Should be used before passing work to `PathTrace::render_samples()`. */ + void render_work_reschedule_on_cancel(RenderWork &render_work); + + RenderWork get_render_work(); + + /* Report that the path tracer started to work, after scene update and loading kernels. */ + void report_work_begin(const RenderWork &render_work); + + /* Report time (in seconds) which corresponding part of work took. */ + void report_path_trace_time(const RenderWork &render_work, double time, bool is_cancelled); + void report_path_trace_occupancy(const RenderWork &render_work, float occupancy); + void report_adaptive_filter_time(const RenderWork &render_work, double time, bool is_cancelled); + void report_denoise_time(const RenderWork &render_work, double time); + void report_display_update_time(const RenderWork &render_work, double time); + void report_rebalance_time(const RenderWork &render_work, double time, bool balance_changed); + + /* Generate full multi-line report of the rendering process, including rendering parameters, + * times, and so on. */ + string full_report() const; + + protected: + /* Check whether all work has been scheduled and time limit was not exceeded. + * + * NOTE: Tricky bit: if the time limit was reached the done() is considered to be true, but some + * extra work needs to be scheduled to denoise and write final result. */ + bool done() const; + + /* Update scheduling state for a newly scheduled work. + * Takes care of things like checking whether work was ever denoised, tile was written and states + * like that. */ + void update_state_for_render_work(const RenderWork &render_work); + + /* Returns true if any work was scheduled. */ + bool set_postprocess_render_work(RenderWork *render_work); + + /* Set work which is to be performed after all tiles has been rendered. */ + void set_full_frame_render_work(RenderWork *render_work); + + /* Update start resolution divider based on the accumulated timing information, preserving nice + * feeling navigation feel. */ + void update_start_resolution_divider(); + + /* Calculate desired update interval in seconds based on the current timings and settings. + * Will give an interval which provides good feeling updates during viewport navigation. */ + double guess_viewport_navigation_update_interval_in_seconds() const; + + /* Check whether denoising is active during interactive update while resolution divider is not + * unit. */ + bool is_denoise_active_during_update() const; + + /* Heuristic which aims to give perceptually pleasant update of display interval in a way that at + * lower samples and near the beginning of rendering, updates happen more often, but with higher + * number of samples and later in the render, updates happen less often but device occupancy + * goes higher. */ + double guess_display_update_interval_in_seconds() const; + double guess_display_update_interval_in_seconds_for_num_samples(int num_rendered_samples) const; + double guess_display_update_interval_in_seconds_for_num_samples_no_limit( + int num_rendered_samples) const; + + /* Calculate number of samples which can be rendered within current desired update interval which + * is calculated by `guess_update_interval_in_seconds()`. */ + int calculate_num_samples_per_update() const; + + /* Get start sample and the number of samples which are to be path traces in the current work. */ + int get_start_sample_to_path_trace() const; + int get_num_samples_to_path_trace() const; + + /* Calculate how many samples there are to be rendered for the very first path trace after reset. + */ + int get_num_samples_during_navigation(int resolution_divier) const; + + /* Whether adaptive sampling convergence check and filter is to happen. */ + bool work_need_adaptive_filter() const; + + /* Calculate threshold for adaptive sampling. */ + float work_adaptive_threshold() const; + + /* Check whether current work needs denoising. + * Denoising is not needed if the denoiser is not configured, or when denoising is happening too + * often. + * + * The delayed will be true when the denoiser is configured for use, but it was delayed for a + * later sample, to reduce overhead. + * + * ready_to_display will be false if we may have a denoised result that is outdated due to + * increased samples. */ + bool work_need_denoise(bool &delayed, bool &ready_to_display); + + /* Check whether current work need to update display. + * + * The `denoiser_delayed` is what `work_need_denoise()` returned as delayed denoiser flag. */ + bool work_need_update_display(const bool denoiser_delayed); + + /* Check whether it is time to perform rebalancing for the render work, */ + bool work_need_rebalance(); + + /* Check whether timing of the given work are usable to store timings in the `first_render_time_` + * for the resolution divider calculation. */ + bool work_is_usable_for_first_render_estimation(const RenderWork &render_work); + + /* Check whether timing report about the given work need to reset accumulated average time. */ + bool work_report_reset_average(const RenderWork &render_work); + + /* CHeck whether render time limit has been reached (or exceeded), and if so store related + * information in the state so that rendering is considered finished, and is possible to report + * average render time information. */ + void check_time_limit_reached(); + + /* Helper class to keep track of task timing. + * + * Contains two parts: wall time and average. The wall time is an actual wall time of how long it + * took to complete all tasks of a type. Is always advanced when PathTracer reports time update. + * + * The average time is used for scheduling purposes. It is estimated to be a time of how long it + * takes to perform task on the final resolution. */ + class TimeWithAverage { + public: + inline void reset() + { + total_wall_time_ = 0.0; + + average_time_accumulator_ = 0.0; + num_average_times_ = 0; + } + + inline void add_wall(double time) + { + total_wall_time_ += time; + } + + inline void add_average(double time, int num_measurements = 1) + { + average_time_accumulator_ += time; + num_average_times_ += num_measurements; + } + + inline double get_wall() const + { + return total_wall_time_; + } + + inline double get_average() const + { + if (num_average_times_ == 0) { + return 0; + } + return average_time_accumulator_ / num_average_times_; + } + + inline void reset_average() + { + average_time_accumulator_ = 0.0; + num_average_times_ = 0; + } + + protected: + double total_wall_time_ = 0.0; + + double average_time_accumulator_ = 0.0; + int num_average_times_ = 0; + }; + + struct { + int resolution_divider = 1; + + /* Number of rendered samples on top of the start sample. */ + int num_rendered_samples = 0; + + /* Point in time the latest PathTraceDisplay work has been scheduled. */ + double last_display_update_time = 0.0; + /* Value of -1 means display was never updated. */ + int last_display_update_sample = -1; + + /* Point in time at which last rebalance has been performed. */ + double last_rebalance_time = 0.0; + + /* Number of rebalance works which has been requested to be performed. + * The path tracer might ignore the work if there is a single device rendering. */ + int num_rebalance_requested = 0; + + /* Number of rebalance works handled which did change balance across devices. */ + int num_rebalance_changes = 0; + + bool need_rebalance_at_next_work = false; + + /* Denotes whether the latest performed rebalance work cause an actual rebalance of work across + * devices. */ + bool last_rebalance_changed = false; + + /* Threshold for adaptive sampling which will be scheduled to work when not using progressive + * noise floor. */ + float adaptive_sampling_threshold = 0.0f; + + bool last_work_tile_was_denoised = false; + bool tile_result_was_written = false; + bool postprocess_work_scheduled = false; + bool full_frame_work_scheduled = false; + bool full_frame_was_written = false; + + bool path_trace_finished = false; + bool time_limit_reached = false; + + /* Time at which rendering started and finished. */ + double start_render_time = 0.0; + double end_render_time = 0.0; + + /* Measured occupancy of the render devices measured normalized to the number of samples. + * + * In a way it is "trailing": when scheduling new work this occupancy is measured when the + * previous work was rendered. */ + int occupancy_num_samples = 0; + float occupancy = 1.0f; + } state_; + + /* Timing of tasks which were performed at the very first render work at 100% of the + * resolution. This timing information is used to estimate resolution divider for fats + * navigation. */ + struct { + double path_trace_per_sample; + double denoise_time; + double display_update_time; + } first_render_time_; + + TimeWithAverage path_trace_time_; + TimeWithAverage adaptive_filter_time_; + TimeWithAverage denoise_time_; + TimeWithAverage display_update_time_; + TimeWithAverage rebalance_time_; + + /* Whether cryptomatte-related work will be scheduled. */ + bool need_schedule_cryptomatte_ = false; + + /* Whether to schedule device load rebalance works. + * Rebalancing requires some special treatment for update intervals and such, so if it's known + * that the rebalance will be ignored (due to single-device rendering i.e.) is better to fully + * ignore rebalancing logic. */ + bool need_schedule_rebalance_works_ = false; + + /* Path tracing work will be scheduled for samples from within + * [start_sample_, start_sample_ + num_samples_ - 1] range, inclusively. */ + int start_sample_ = 0; + int num_samples_ = 0; + + /* Limit in seconds for how long path tracing is allowed to happen. + * Zero means no limit is applied. */ + double time_limit_ = 0.0; + + /* Headless rendering without interface. */ + bool headless_; + + /* Background (offline) rendering. */ + bool background_; + + /* Pixel size is used to force lower resolution render for final pass. Useful for retina or other + * types of hi-dpi displays. */ + int pixel_size_ = 1; + + TileManager &tile_manager_; + + BufferParams buffer_params_; + DenoiseParams denoiser_params_; + + AdaptiveSampling adaptive_sampling_; + + /* Progressively lower adaptive sampling threshold level, keeping the image at a uniform noise + * level. */ + bool use_progressive_noise_floor_ = false; + + /* Default value for the resolution divider which will be used when there is no render time + * information available yet. + * It is also what defines the upper limit of the automatically calculated resolution divider. */ + int default_start_resolution_divider_ = 1; + + /* Initial resolution divider which will be used on render scheduler reset. */ + int start_resolution_divider_ = 0; + + /* Calculate smallest resolution divider which will bring down actual rendering time below the + * desired one. This call assumes linear dependency of render time from number of pixels + * (quadratic dependency from the resolution divider): resolution divider of 2 brings render time + * down by a factor of 4. */ + int calculate_resolution_divider_for_time(double desired_time, double actual_time); +}; + +int calculate_resolution_divider_for_resolution(int width, int height, int resolution); + +int calculate_resolution_for_divider(int width, int height, int resolution_divider); + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/shader_eval.cpp b/intern/cycles/integrator/shader_eval.cpp new file mode 100644 index 00000000000..42cbf87f254 --- /dev/null +++ b/intern/cycles/integrator/shader_eval.cpp @@ -0,0 +1,181 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/shader_eval.h" + +#include "device/device.h" +#include "device/queue.h" + +#include "device/cpu/kernel.h" +#include "device/cpu/kernel_thread_globals.h" + +#include "util/log.h" +#include "util/progress.h" +#include "util/tbb.h" + +CCL_NAMESPACE_BEGIN + +ShaderEval::ShaderEval(Device *device, Progress &progress) : device_(device), progress_(progress) +{ + DCHECK_NE(device_, nullptr); +} + +bool ShaderEval::eval(const ShaderEvalType type, + const int max_num_inputs, + const int num_channels, + const function &)> &fill_input, + const function &)> &read_output) +{ + bool first_device = true; + bool success = true; + + device_->foreach_device([&](Device *device) { + if (!first_device) { + LOG(ERROR) << "Multi-devices are not yet fully implemented, will evaluate shader on a " + "single device."; + return; + } + first_device = false; + + device_vector input(device, "ShaderEval input", MEM_READ_ONLY); + device_vector output(device, "ShaderEval output", MEM_READ_WRITE); + + /* Allocate and copy device buffers. */ + DCHECK_EQ(input.device, device); + DCHECK_EQ(output.device, device); + DCHECK_LE(output.size(), input.size()); + + input.alloc(max_num_inputs); + int num_points = fill_input(input); + if (num_points == 0) { + return; + } + + input.copy_to_device(); + output.alloc(num_points * num_channels); + output.zero_to_device(); + + /* Evaluate on CPU or GPU. */ + success = (device->info.type == DEVICE_CPU) ? + eval_cpu(device, type, input, output, num_points) : + eval_gpu(device, type, input, output, num_points); + + /* Copy data back from device if not canceled. */ + if (success) { + output.copy_from_device(0, 1, output.size()); + read_output(output); + } + + input.free(); + output.free(); + }); + + return success; +} + +bool ShaderEval::eval_cpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output, + const int64_t work_size) +{ + vector kernel_thread_globals; + device->get_cpu_kernel_thread_globals(kernel_thread_globals); + + /* Find required kernel function. */ + const CPUKernels &kernels = *(device->get_cpu_kernels()); + + /* Simple parallel_for over all work items. */ + KernelShaderEvalInput *input_data = input.data(); + float *output_data = output.data(); + bool success = true; + + tbb::task_arena local_arena(device->info.cpu_threads); + local_arena.execute([&]() { + tbb::parallel_for(int64_t(0), work_size, [&](int64_t work_index) { + /* TODO: is this fast enough? */ + if (progress_.get_cancel()) { + success = false; + return; + } + + const int thread_index = tbb::this_task_arena::current_thread_index(); + const KernelGlobalsCPU *kg = &kernel_thread_globals[thread_index]; + + switch (type) { + case SHADER_EVAL_DISPLACE: + kernels.shader_eval_displace(kg, input_data, output_data, work_index); + break; + case SHADER_EVAL_BACKGROUND: + kernels.shader_eval_background(kg, input_data, output_data, work_index); + break; + case SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY: + kernels.shader_eval_curve_shadow_transparency(kg, input_data, output_data, work_index); + break; + } + }); + }); + + return success; +} + +bool ShaderEval::eval_gpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output, + const int64_t work_size) +{ + /* Find required kernel function. */ + DeviceKernel kernel; + switch (type) { + case SHADER_EVAL_DISPLACE: + kernel = DEVICE_KERNEL_SHADER_EVAL_DISPLACE; + break; + case SHADER_EVAL_BACKGROUND: + kernel = DEVICE_KERNEL_SHADER_EVAL_BACKGROUND; + break; + case SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY: + kernel = DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY; + break; + }; + + /* Create device queue. */ + unique_ptr queue = device->gpu_queue_create(); + queue->init_execution(); + + /* Execute work on GPU in chunk, so we can cancel. + * TODO : query appropriate size from device.*/ + const int64_t chunk_size = 65536; + + void *d_input = (void *)input.device_pointer; + void *d_output = (void *)output.device_pointer; + + for (int64_t d_offset = 0; d_offset < work_size; d_offset += chunk_size) { + int64_t d_work_size = std::min(chunk_size, work_size - d_offset); + void *args[] = {&d_input, &d_output, &d_offset, &d_work_size}; + + queue->enqueue(kernel, d_work_size, args); + queue->synchronize(); + + if (progress_.get_cancel()) { + return false; + } + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/shader_eval.h b/intern/cycles/integrator/shader_eval.h new file mode 100644 index 00000000000..3ae63b84d04 --- /dev/null +++ b/intern/cycles/integrator/shader_eval.h @@ -0,0 +1,65 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "device/memory.h" + +#include "kernel/types.h" + +#include "util/function.h" + +CCL_NAMESPACE_BEGIN + +class Device; +class Progress; + +enum ShaderEvalType { + SHADER_EVAL_DISPLACE, + SHADER_EVAL_BACKGROUND, + SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY, +}; + +/* ShaderEval class performs shader evaluation for background light and displacement. */ +class ShaderEval { + public: + ShaderEval(Device *device, Progress &progress); + + /* Evaluate shader at points specified by KernelShaderEvalInput and write out + * RGBA colors to output. */ + bool eval(const ShaderEvalType type, + const int max_num_inputs, + const int num_channels, + const function &)> &fill_input, + const function &)> &read_output); + + protected: + bool eval_cpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output, + const int64_t work_size); + bool eval_gpu(Device *device, + const ShaderEvalType type, + device_vector &input, + device_vector &output, + const int64_t work_size); + + Device *device_; + Progress &progress_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/tile.cpp b/intern/cycles/integrator/tile.cpp new file mode 100644 index 00000000000..4a1558cce09 --- /dev/null +++ b/intern/cycles/integrator/tile.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/tile.h" + +#include "util/log.h" +#include "util/math.h" + +CCL_NAMESPACE_BEGIN + +std::ostream &operator<<(std::ostream &os, const TileSize &tile_size) +{ + os << "size: (" << tile_size.width << ", " << tile_size.height << ")"; + os << ", num_samples: " << tile_size.num_samples; + return os; +} + +ccl_device_inline uint round_down_to_power_of_two(uint x) +{ + if (is_power_of_two(x)) { + return x; + } + + return prev_power_of_two(x); +} + +ccl_device_inline uint round_up_to_power_of_two(uint x) +{ + if (is_power_of_two(x)) { + return x; + } + + return next_power_of_two(x); +} + +TileSize tile_calculate_best_size(const int2 &image_size, + const int num_samples, + const int max_num_path_states, + const float scrambling_distance) +{ + if (max_num_path_states == 1) { + /* Simple case: avoid any calculation, which could cause rounding issues. */ + return TileSize(1, 1, 1); + } + + const int64_t num_pixels = image_size.x * image_size.y; + const int64_t num_pixel_samples = num_pixels * num_samples; + + if (max_num_path_states >= num_pixel_samples) { + /* Image fully fits into the state (could be border render, for example). */ + return TileSize(image_size.x, image_size.y, num_samples); + } + + /* The idea here is to keep number of samples per tile as much as possible to improve coherency + * across threads. + * + * Some general ideas: + * - Prefer smaller tiles with more samples, which improves spatial coherency of paths. + * - Keep values a power of two, for more integer fit into the maximum number of paths. */ + + TileSize tile_size; + const int num_path_states_per_sample = max_num_path_states / num_samples; + if (scrambling_distance < 0.9f) { + /* Prefer large tiles for scrambling distance, bounded by max num path states. */ + tile_size.width = min(image_size.x, max_num_path_states); + tile_size.height = min(image_size.y, max(max_num_path_states / tile_size.width, 1)); + } + else { + /* Calculate tile size as if it is the most possible one to fit an entire range of samples. + * The idea here is to keep tiles as small as possible, and keep device occupied by scheduling + * multiple tiles with the same coordinates rendering different samples. */ + + if (num_path_states_per_sample != 0) { + tile_size.width = round_down_to_power_of_two(lround(sqrt(num_path_states_per_sample))); + tile_size.height = tile_size.width; + } + else { + tile_size.width = tile_size.height = 1; + } + } + + if (num_samples == 1) { + tile_size.num_samples = 1; + } + else { + /* Heuristic here is to have more uniform division of the sample range: for example prefer + * [32 <38 times>, 8] over [1024, 200]. This allows to greedily add more tiles early on. */ + tile_size.num_samples = min(round_up_to_power_of_two(lround(sqrt(num_samples / 2))), + static_cast(num_samples)); + + const int tile_area = tile_size.width * tile_size.height; + tile_size.num_samples = min(tile_size.num_samples, max_num_path_states / tile_area); + } + + DCHECK_GE(tile_size.width, 1); + DCHECK_GE(tile_size.height, 1); + DCHECK_GE(tile_size.num_samples, 1); + DCHECK_LE(tile_size.width * tile_size.height * tile_size.num_samples, max_num_path_states); + + return tile_size; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/tile.h b/intern/cycles/integrator/tile.h new file mode 100644 index 00000000000..61f7d736115 --- /dev/null +++ b/intern/cycles/integrator/tile.h @@ -0,0 +1,57 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +struct TileSize { + TileSize() = default; + + inline TileSize(int width, int height, int num_samples) + : width(width), height(height), num_samples(num_samples) + { + } + + inline bool operator==(const TileSize &other) const + { + return width == other.width && height == other.height && num_samples == other.num_samples; + } + inline bool operator!=(const TileSize &other) const + { + return !(*this == other); + } + + int width = 0, height = 0; + int num_samples = 0; +}; + +std::ostream &operator<<(std::ostream &os, const TileSize &tile_size); + +/* Calculate tile size which is best suitable for rendering image of a given size with given number + * of active path states. + * Will attempt to provide best guess to keep path tracing threads of a device as localized as + * possible, and have as many threads active for every tile as possible. */ +TileSize tile_calculate_best_size(const int2 &image_size, + const int num_samples, + const int max_num_path_states, + const float scrambling_distance); + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_balancer.cpp b/intern/cycles/integrator/work_balancer.cpp new file mode 100644 index 00000000000..4c6fa341f35 --- /dev/null +++ b/intern/cycles/integrator/work_balancer.cpp @@ -0,0 +1,99 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/work_balancer.h" + +#include "util/math.h" + +#include "util/log.h" + +CCL_NAMESPACE_BEGIN + +void work_balance_do_initial(vector &work_balance_infos) +{ + const int num_infos = work_balance_infos.size(); + + if (num_infos == 1) { + work_balance_infos[0].weight = 1.0; + return; + } + + /* There is no statistics available, so start with an equal distribution. */ + const double weight = 1.0 / num_infos; + for (WorkBalanceInfo &balance_info : work_balance_infos) { + balance_info.weight = weight; + } +} + +static double calculate_total_time(const vector &work_balance_infos) +{ + double total_time = 0; + for (const WorkBalanceInfo &info : work_balance_infos) { + total_time += info.time_spent; + } + return total_time; +} + +/* The balance is based on equalizing time which devices spent performing a task. Assume that + * average of the observed times is usable for estimating whether more or less work is to be + * scheduled, and how difference in the work scheduling is needed. */ + +bool work_balance_do_rebalance(vector &work_balance_infos) +{ + const int num_infos = work_balance_infos.size(); + + const double total_time = calculate_total_time(work_balance_infos); + const double time_average = total_time / num_infos; + + double total_weight = 0; + vector new_weights; + new_weights.reserve(num_infos); + + /* Equalize the overall average time. This means that we don't make it so every work will perform + * amount of work based on the current average, but that after the weights changes the time will + * equalize. + * Can think of it that if one of the devices is 10% faster than another, then one device needs + * to do 5% less of the current work, and another needs to do 5% more. */ + const double lerp_weight = 1.0 / num_infos; + + bool has_big_difference = false; + + for (const WorkBalanceInfo &info : work_balance_infos) { + const double time_target = lerp(info.time_spent, time_average, lerp_weight); + const double new_weight = info.weight * time_target / info.time_spent; + new_weights.push_back(new_weight); + total_weight += new_weight; + + if (std::fabs(1.0 - time_target / time_average) > 0.02) { + has_big_difference = true; + } + } + + if (!has_big_difference) { + return false; + } + + const double total_weight_inv = 1.0 / total_weight; + for (int i = 0; i < num_infos; ++i) { + WorkBalanceInfo &info = work_balance_infos[i]; + info.weight = new_weights[i] * total_weight_inv; + info.time_spent = 0; + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_balancer.h b/intern/cycles/integrator/work_balancer.h new file mode 100644 index 00000000000..86ff9335f91 --- /dev/null +++ b/intern/cycles/integrator/work_balancer.h @@ -0,0 +1,42 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +struct WorkBalanceInfo { + /* Time spent performing corresponding work. */ + double time_spent = 0; + + /* Average occupancy of the device while performing the work. */ + float occupancy = 1.0f; + + /* Normalized weight, which is ready to be used for work balancing (like calculating fraction of + * the big tile which is to be rendered on the device). */ + double weight = 1.0; +}; + +/* Balance work for an initial render integration, before any statistics is known. */ +void work_balance_do_initial(vector &work_balance_infos); + +/* Rebalance work after statistics has been accumulated. + * Returns true if the balancing did change. */ +bool work_balance_do_rebalance(vector &work_balance_infos); + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_tile_scheduler.cpp b/intern/cycles/integrator/work_tile_scheduler.cpp new file mode 100644 index 00000000000..2d1ac07db7f --- /dev/null +++ b/intern/cycles/integrator/work_tile_scheduler.cpp @@ -0,0 +1,139 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "integrator/work_tile_scheduler.h" + +#include "device/queue.h" +#include "integrator/tile.h" +#include "session/buffers.h" +#include "util/atomic.h" +#include "util/log.h" + +CCL_NAMESPACE_BEGIN + +WorkTileScheduler::WorkTileScheduler() +{ +} + +void WorkTileScheduler::set_max_num_path_states(int max_num_path_states) +{ + max_num_path_states_ = max_num_path_states; +} + +void WorkTileScheduler::reset(const BufferParams &buffer_params, + int sample_start, + int samples_num, + float scrambling_distance) +{ + /* Image buffer parameters. */ + image_full_offset_px_.x = buffer_params.full_x; + image_full_offset_px_.y = buffer_params.full_y; + + image_size_px_ = make_int2(buffer_params.width, buffer_params.height); + scrambling_distance_ = scrambling_distance; + + offset_ = buffer_params.offset; + stride_ = buffer_params.stride; + + /* Samples parameters. */ + sample_start_ = sample_start; + samples_num_ = samples_num; + + /* Initialize new scheduling. */ + reset_scheduler_state(); +} + +void WorkTileScheduler::reset_scheduler_state() +{ + tile_size_ = tile_calculate_best_size( + image_size_px_, samples_num_, max_num_path_states_, scrambling_distance_); + + VLOG(3) << "Will schedule tiles of size " << tile_size_; + + if (VLOG_IS_ON(3)) { + /* The logging is based on multiple tiles scheduled, ignoring overhead of multi-tile scheduling + * and purely focusing on the number of used path states. */ + const int num_path_states_in_tile = tile_size_.width * tile_size_.height * + tile_size_.num_samples; + const int num_tiles = max_num_path_states_ / num_path_states_in_tile; + VLOG(3) << "Number of unused path states: " + << max_num_path_states_ - num_tiles * num_path_states_in_tile; + } + + num_tiles_x_ = divide_up(image_size_px_.x, tile_size_.width); + num_tiles_y_ = divide_up(image_size_px_.y, tile_size_.height); + + total_tiles_num_ = num_tiles_x_ * num_tiles_y_; + num_tiles_per_sample_range_ = divide_up(samples_num_, tile_size_.num_samples); + + next_work_index_ = 0; + total_work_size_ = total_tiles_num_ * num_tiles_per_sample_range_; +} + +bool WorkTileScheduler::get_work(KernelWorkTile *work_tile_, const int max_work_size) +{ + /* Note that the `max_work_size` can be higher than the `max_num_path_states_`: this is because + * the path trace work can decide to use smaller tile sizes and greedily schedule multiple tiles, + * improving overall device occupancy. + * So the `max_num_path_states_` is a "scheduling unit", and the `max_work_size` is a "scheduling + * limit". */ + + DCHECK_NE(max_num_path_states_, 0); + + const int work_index = next_work_index_++; + if (work_index >= total_work_size_) { + return false; + } + + const int sample_range_index = work_index % num_tiles_per_sample_range_; + const int start_sample = sample_range_index * tile_size_.num_samples; + const int tile_index = work_index / num_tiles_per_sample_range_; + const int tile_y = tile_index / num_tiles_x_; + const int tile_x = tile_index - tile_y * num_tiles_x_; + + KernelWorkTile work_tile; + work_tile.x = tile_x * tile_size_.width; + work_tile.y = tile_y * tile_size_.height; + work_tile.w = tile_size_.width; + work_tile.h = tile_size_.height; + work_tile.start_sample = sample_start_ + start_sample; + work_tile.num_samples = min(tile_size_.num_samples, samples_num_ - start_sample); + work_tile.offset = offset_; + work_tile.stride = stride_; + + work_tile.w = min(work_tile.w, image_size_px_.x - work_tile.x); + work_tile.h = min(work_tile.h, image_size_px_.y - work_tile.y); + + work_tile.x += image_full_offset_px_.x; + work_tile.y += image_full_offset_px_.y; + + const int tile_work_size = work_tile.w * work_tile.h * work_tile.num_samples; + + DCHECK_GT(tile_work_size, 0); + + if (max_work_size && tile_work_size > max_work_size) { + /* The work did not fit into the requested limit of the work size. Unschedule the tile, + * so it can be picked up again later. */ + next_work_index_--; + return false; + } + + *work_tile_ = work_tile; + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/integrator/work_tile_scheduler.h b/intern/cycles/integrator/work_tile_scheduler.h new file mode 100644 index 00000000000..d9fa7e84431 --- /dev/null +++ b/intern/cycles/integrator/work_tile_scheduler.h @@ -0,0 +1,104 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "integrator/tile.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; + +struct KernelWorkTile; + +/* Scheduler of device work tiles. + * Takes care of feeding multiple devices running in parallel a work which needs to be done. */ +class WorkTileScheduler { + public: + WorkTileScheduler(); + + /* MAximum path states which are allowed to be used by a single scheduled work tile. + * + * Affects the scheduled work size: the work size will be as big as possible, but will not exceed + * this number of states. */ + void set_max_num_path_states(int max_num_path_states); + + /* Scheduling will happen for pixels within a big tile denotes by its parameters. */ + void reset(const BufferParams &buffer_params, + int sample_start, + int samples_num, + float scrambling_distance); + + /* Get work for a device. + * Returns true if there is still work to be done and initialize the work tile to all + * parameters of this work. If there is nothing remaining to be done, returns false and the + * work tile is kept unchanged. + * + * Optionally pass max_work_size to do nothing if there is no tile small enough. */ + bool get_work(KernelWorkTile *work_tile, const int max_work_size = 0); + + protected: + void reset_scheduler_state(); + + /* Maximum allowed path states to be used. + * + * TODO(sergey): Naming can be improved. The fact that this is a limiting factor based on the + * number of path states is kind of a detail. Is there a more generic term from the scheduler + * point of view? */ + int max_num_path_states_ = 0; + + /* Offset in pixels within a global buffer. */ + int2 image_full_offset_px_ = make_int2(0, 0); + + /* dimensions of the currently rendering image in pixels. */ + int2 image_size_px_ = make_int2(0, 0); + + /* Offset and stride of the buffer within which scheduling is happening. + * Will be passed over to the KernelWorkTile. */ + int offset_, stride_; + + /* Scrambling Distance requires adapted tile size */ + float scrambling_distance_; + + /* Start sample of index and number of samples which are to be rendered. + * The scheduler will cover samples range of [start, start + num] over the entire image + * (splitting into a smaller work tiles). */ + int sample_start_ = 0; + int samples_num_ = 0; + + /* Tile size which be scheduled for rendering. */ + TileSize tile_size_; + + /* Number of tiles in X and Y axis of the image. */ + int num_tiles_x_, num_tiles_y_; + + /* Total number of tiles on the image. + * Pre-calculated as `num_tiles_x_ * num_tiles_y_` and re-used in the `get_work()`. + * + * TODO(sergey): Is this an over-optimization? Maybe it's unmeasurable to calculate the value + * in the `get_work()`? */ + int total_tiles_num_ = 0; + + /* In the case when the number of samples in the `tile_size_` is lower than samples_num_ denotes + * how many tiles are to be "stacked" to cover the entire requested range of samples. */ + int num_tiles_per_sample_range_ = 0; + + int next_work_index_ = 0; + int total_work_size_ = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/CMakeLists.txt b/intern/cycles/kernel/CMakeLists.txt index 0ce33c51778..29ff69df864 100644 --- a/intern/cycles/kernel/CMakeLists.txt +++ b/intern/cycles/kernel/CMakeLists.txt @@ -22,149 +22,64 @@ set(INC_SYS ) -set(SRC_CPU_KERNELS - kernels/cpu/kernel.cpp - kernels/cpu/kernel_sse2.cpp - kernels/cpu/kernel_sse3.cpp - kernels/cpu/kernel_sse41.cpp - kernels/cpu/kernel_avx.cpp - kernels/cpu/kernel_avx2.cpp - kernels/cpu/kernel_split.cpp - kernels/cpu/kernel_split_sse2.cpp - kernels/cpu/kernel_split_sse3.cpp - kernels/cpu/kernel_split_sse41.cpp - kernels/cpu/kernel_split_avx.cpp - kernels/cpu/kernel_split_avx2.cpp - kernels/cpu/filter.cpp - kernels/cpu/filter_sse2.cpp - kernels/cpu/filter_sse3.cpp - kernels/cpu/filter_sse41.cpp - kernels/cpu/filter_avx.cpp - kernels/cpu/filter_avx2.cpp +set(SRC_KERNEL_DEVICE_CPU + device/cpu/kernel.cpp + device/cpu/kernel_sse2.cpp + device/cpu/kernel_sse3.cpp + device/cpu/kernel_sse41.cpp + device/cpu/kernel_avx.cpp + device/cpu/kernel_avx2.cpp ) -set(SRC_CUDA_KERNELS - kernels/cuda/kernel.cu - kernels/cuda/kernel_split.cu - kernels/cuda/filter.cu +set(SRC_KERNEL_DEVICE_CUDA + device/cuda/kernel.cu ) -set(SRC_OPENCL_KERNELS - kernels/opencl/kernel_adaptive_stopping.cl - kernels/opencl/kernel_adaptive_filter_x.cl - kernels/opencl/kernel_adaptive_filter_y.cl - kernels/opencl/kernel_adaptive_adjust_samples.cl - kernels/opencl/kernel_bake.cl - kernels/opencl/kernel_base.cl - kernels/opencl/kernel_displace.cl - kernels/opencl/kernel_background.cl - kernels/opencl/kernel_state_buffer_size.cl - kernels/opencl/kernel_split_bundle.cl - kernels/opencl/kernel_data_init.cl - kernels/opencl/kernel_path_init.cl - kernels/opencl/kernel_queue_enqueue.cl - kernels/opencl/kernel_scene_intersect.cl - kernels/opencl/kernel_lamp_emission.cl - kernels/opencl/kernel_do_volume.cl - kernels/opencl/kernel_indirect_background.cl - kernels/opencl/kernel_shader_setup.cl - kernels/opencl/kernel_shader_sort.cl - kernels/opencl/kernel_shader_eval.cl - kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl - kernels/opencl/kernel_subsurface_scatter.cl - kernels/opencl/kernel_direct_lighting.cl - kernels/opencl/kernel_shadow_blocked_ao.cl - kernels/opencl/kernel_shadow_blocked_dl.cl - kernels/opencl/kernel_enqueue_inactive.cl - kernels/opencl/kernel_next_iteration_setup.cl - kernels/opencl/kernel_indirect_subsurface.cl - kernels/opencl/kernel_buffer_update.cl - kernels/opencl/filter.cl +set(SRC_KERNEL_DEVICE_HIP + device/hip/kernel.cpp ) -set(SRC_OPTIX_KERNELS - kernels/optix/kernel_optix.cu +set(SRC_KERNEL_DEVICE_OPTIX + device/optix/kernel.cu + device/optix/kernel_shader_raytrace.cu ) -set(SRC_BVH_HEADERS - bvh/bvh.h - bvh/bvh_nodes.h - bvh/bvh_shadow_all.h - bvh/bvh_local.h - bvh/bvh_traversal.h - bvh/bvh_types.h - bvh/bvh_util.h - bvh/bvh_volume.h - bvh/bvh_volume_all.h - bvh/bvh_embree.h +set(SRC_KERNEL_DEVICE_CPU_HEADERS + device/cpu/compat.h + device/cpu/image.h + device/cpu/globals.h + device/cpu/kernel.h + device/cpu/kernel_arch.h + device/cpu/kernel_arch_impl.h +) +set(SRC_KERNEL_DEVICE_GPU_HEADERS + device/gpu/image.h + device/gpu/kernel.h + device/gpu/parallel_active_index.h + device/gpu/parallel_prefix_sum.h + device/gpu/parallel_reduce.h + device/gpu/parallel_sorted_index.h + device/gpu/work_stealing.h ) -set(SRC_HEADERS - kernel_accumulate.h - kernel_adaptive_sampling.h - kernel_bake.h - kernel_camera.h - kernel_color.h - kernel_compat_cpu.h - kernel_compat_cuda.h - kernel_compat_optix.h - kernel_compat_opencl.h - kernel_differential.h - kernel_emission.h - kernel_film.h - kernel_globals.h - kernel_id_passes.h - kernel_jitter.h - kernel_light.h - kernel_light_background.h - kernel_light_common.h - kernel_math.h - kernel_montecarlo.h - kernel_passes.h - kernel_path.h - kernel_path_branched.h - kernel_path_common.h - kernel_path_state.h - kernel_path_surface.h - kernel_path_subsurface.h - kernel_path_volume.h - kernel_profiling.h - kernel_projection.h - kernel_queues.h - kernel_random.h - kernel_shader.h - kernel_shadow.h - kernel_subsurface.h - kernel_textures.h - kernel_types.h - kernel_volume.h - kernel_work_stealing.h - kernel_write_passes.h +set(SRC_KERNEL_DEVICE_CUDA_HEADERS + device/cuda/compat.h + device/cuda/config.h + device/cuda/globals.h ) -set(SRC_KERNELS_CPU_HEADERS - kernel.h - kernels/cpu/kernel_cpu.h - kernels/cpu/kernel_cpu_impl.h - kernels/cpu/kernel_cpu_image.h - kernels/cpu/filter_cpu.h - kernels/cpu/filter_cpu_impl.h +set(SRC_KERNEL_DEVICE_HIP_HEADERS + device/hip/compat.h + device/hip/config.h + device/hip/globals.h ) -set(SRC_KERNELS_CUDA_HEADERS - kernels/cuda/kernel_config.h - kernels/cuda/kernel_cuda_image.h +set(SRC_KERNEL_DEVICE_OPTIX_HEADERS + device/optix/compat.h + device/optix/globals.h ) -set(SRC_KERNELS_OPTIX_HEADERS -) - -set(SRC_KERNELS_OPENCL_HEADERS - kernels/opencl/kernel_split_function.h - kernels/opencl/kernel_opencl_image.h -) - -set(SRC_CLOSURE_HEADERS +set(SRC_KERNEL_CLOSURE_HEADERS closure/alloc.h closure/bsdf.h closure/bsdf_ashikhmin_velvet.h @@ -190,177 +105,233 @@ set(SRC_CLOSURE_HEADERS closure/bsdf_hair_principled.h ) -set(SRC_SVM_HEADERS +set(SRC_KERNEL_SVM_HEADERS svm/svm.h - svm/svm_ao.h - svm/svm_aov.h - svm/svm_attribute.h - svm/svm_bevel.h - svm/svm_blackbody.h - svm/svm_bump.h - svm/svm_camera.h - svm/svm_clamp.h - svm/svm_closure.h - svm/svm_convert.h - svm/svm_checker.h - svm/svm_color_util.h - svm/svm_brick.h - svm/svm_displace.h - svm/svm_fresnel.h - svm/svm_wireframe.h - svm/svm_wavelength.h - svm/svm_gamma.h - svm/svm_brightness.h - svm/svm_geometry.h - svm/svm_gradient.h - svm/svm_hsv.h - svm/svm_ies.h - svm/svm_image.h - svm/svm_invert.h - svm/svm_light_path.h - svm/svm_magic.h - svm/svm_map_range.h - svm/svm_mapping.h - svm/svm_mapping_util.h - svm/svm_math.h - svm/svm_math_util.h - svm/svm_mix.h - svm/svm_musgrave.h - svm/svm_noise.h - svm/svm_noisetex.h - svm/svm_normal.h - svm/svm_ramp.h - svm/svm_ramp_util.h - svm/svm_sepcomb_hsv.h - svm/svm_sepcomb_vector.h - svm/svm_sky.h - svm/svm_tex_coord.h - svm/svm_fractal_noise.h - svm/svm_types.h - svm/svm_value.h - svm/svm_vector_rotate.h - svm/svm_vector_transform.h - svm/svm_voronoi.h - svm/svm_voxel.h - svm/svm_wave.h - svm/svm_white_noise.h - svm/svm_vertex_color.h + svm/ao.h + svm/aov.h + svm/attribute.h + svm/bevel.h + svm/blackbody.h + svm/bump.h + svm/camera.h + svm/clamp.h + svm/closure.h + svm/convert.h + svm/checker.h + svm/color_util.h + svm/brick.h + svm/displace.h + svm/fresnel.h + svm/wireframe.h + svm/wavelength.h + svm/gamma.h + svm/brightness.h + svm/geometry.h + svm/gradient.h + svm/hsv.h + svm/ies.h + svm/image.h + svm/invert.h + svm/light_path.h + svm/magic.h + svm/map_range.h + svm/mapping.h + svm/mapping_util.h + svm/math.h + svm/math_util.h + svm/mix.h + svm/musgrave.h + svm/noise.h + svm/noisetex.h + svm/normal.h + svm/ramp.h + svm/ramp_util.h + svm/sepcomb_hsv.h + svm/sepcomb_vector.h + svm/sky.h + svm/tex_coord.h + svm/fractal_noise.h + svm/types.h + svm/value.h + svm/vector_rotate.h + svm/vector_transform.h + svm/voronoi.h + svm/voxel.h + svm/wave.h + svm/white_noise.h + svm/vertex_color.h ) -set(SRC_GEOM_HEADERS +set(SRC_KERNEL_GEOM_HEADERS geom/geom.h - geom/geom_attribute.h - geom/geom_curve.h - geom/geom_curve_intersect.h - geom/geom_motion_curve.h - geom/geom_motion_triangle.h - geom/geom_motion_triangle_intersect.h - geom/geom_motion_triangle_shader.h - geom/geom_object.h - geom/geom_patch.h - geom/geom_primitive.h - geom/geom_subd_triangle.h - geom/geom_triangle.h - geom/geom_triangle_intersect.h - geom/geom_volume.h + geom/attribute.h + geom/curve.h + geom/curve_intersect.h + geom/motion_curve.h + geom/motion_triangle.h + geom/motion_triangle_intersect.h + geom/motion_triangle_shader.h + geom/object.h + geom/patch.h + geom/primitive.h + geom/shader_data.h + geom/subd_triangle.h + geom/triangle.h + geom/triangle_intersect.h + geom/volume.h ) -set(SRC_FILTER_HEADERS - filter/filter.h - filter/filter_defines.h - filter/filter_features.h - filter/filter_features_sse.h - filter/filter_kernel.h - filter/filter_nlm_cpu.h - filter/filter_nlm_gpu.h - filter/filter_prefilter.h - filter/filter_reconstruction.h - filter/filter_transform.h - filter/filter_transform_gpu.h - filter/filter_transform_sse.h +set(SRC_KERNEL_BAKE_HEADERS + bake/bake.h +) + +set(SRC_KERNEL_BVH_HEADERS + bvh/bvh.h + bvh/nodes.h + bvh/shadow_all.h + bvh/local.h + bvh/traversal.h + bvh/types.h + bvh/util.h + bvh/volume.h + bvh/volume_all.h + bvh/embree.h +) + +set(SRC_KERNEL_CAMERA_HEADERS + camera/camera.h + camera/projection.h +) + +set(SRC_KERNEL_FILM_HEADERS + film/accumulate.h + film/adaptive_sampling.h + film/id_passes.h + film/passes.h + film/read.h + film/write_passes.h +) + +set(SRC_KERNEL_INTEGRATOR_HEADERS + integrator/init_from_bake.h + integrator/init_from_camera.h + integrator/intersect_closest.h + integrator/intersect_shadow.h + integrator/intersect_subsurface.h + integrator/intersect_volume_stack.h + integrator/megakernel.h + integrator/path_state.h + integrator/shade_background.h + integrator/shade_light.h + integrator/shader_eval.h + integrator/shade_shadow.h + integrator/shade_surface.h + integrator/shade_volume.h + integrator/shadow_catcher.h + integrator/shadow_state_template.h + integrator/state_flow.h + integrator/state.h + integrator/state_template.h + integrator/state_util.h + integrator/subsurface_disk.h + integrator/subsurface.h + integrator/subsurface_random_walk.h + integrator/volume_stack.h +) + +set(SRC_KERNEL_LIGHT_HEADERS + light/light.h + light/background.h + light/common.h + light/sample.h +) + +set(SRC_KERNEL_SAMPLE_HEADERS + sample/jitter.h + sample/lcg.h + sample/mapping.h + sample/mis.h + sample/pattern.h +) + +set(SRC_KERNEL_UTIL_HEADERS + util/color.h + util/differential.h + util/lookup_table.h + util/profiling.h +) + +set(SRC_KERNEL_TYPES_HEADERS + textures.h + types.h +) + +set(SRC_KERNEL_HEADERS + ${SRC_KERNEL_BAKE_HEADERS} + ${SRC_KERNEL_BVH_HEADERS} + ${SRC_KERNEL_CAMERA_HEADERS} + ${SRC_KERNEL_CLOSURE_HEADERS} + ${SRC_KERNEL_FILM_HEADERS} + ${SRC_KERNEL_GEOM_HEADERS} + ${SRC_KERNEL_INTEGRATOR_HEADERS} + ${SRC_KERNEL_LIGHT_HEADERS} + ${SRC_KERNEL_SAMPLE_HEADERS} + ${SRC_KERNEL_SVM_HEADERS} + ${SRC_KERNEL_TYPES_HEADERS} + ${SRC_KERNEL_UTIL_HEADERS} ) set(SRC_UTIL_HEADERS - ../util/util_atomic.h - ../util/util_color.h - ../util/util_defines.h - ../util/util_half.h - ../util/util_hash.h - ../util/util_math.h - ../util/util_math_fast.h - ../util/util_math_intersect.h - ../util/util_math_float2.h - ../util/util_math_float3.h - ../util/util_math_float4.h - ../util/util_math_int2.h - ../util/util_math_int3.h - ../util/util_math_int4.h - ../util/util_math_matrix.h - ../util/util_projection.h - ../util/util_rect.h - ../util/util_static_assert.h - ../util/util_transform.h - ../util/util_texture.h - ../util/util_types.h - ../util/util_types_float2.h - ../util/util_types_float2_impl.h - ../util/util_types_float3.h - ../util/util_types_float3_impl.h - ../util/util_types_float4.h - ../util/util_types_float4_impl.h - ../util/util_types_float8.h - ../util/util_types_float8_impl.h - ../util/util_types_int2.h - ../util/util_types_int2_impl.h - ../util/util_types_int3.h - ../util/util_types_int3_impl.h - ../util/util_types_int4.h - ../util/util_types_int4_impl.h - ../util/util_types_uchar2.h - ../util/util_types_uchar2_impl.h - ../util/util_types_uchar3.h - ../util/util_types_uchar3_impl.h - ../util/util_types_uchar4.h - ../util/util_types_uchar4_impl.h - ../util/util_types_uint2.h - ../util/util_types_uint2_impl.h - ../util/util_types_uint3.h - ../util/util_types_uint3_impl.h - ../util/util_types_uint4.h - ../util/util_types_uint4_impl.h - ../util/util_types_ushort4.h - ../util/util_types_vector3.h - ../util/util_types_vector3_impl.h -) - -set(SRC_SPLIT_HEADERS - split/kernel_adaptive_adjust_samples.h - split/kernel_adaptive_filter_x.h - split/kernel_adaptive_filter_y.h - split/kernel_adaptive_stopping.h - split/kernel_branched.h - split/kernel_buffer_update.h - split/kernel_data_init.h - split/kernel_direct_lighting.h - split/kernel_do_volume.h - split/kernel_enqueue_inactive.h - split/kernel_holdout_emission_blurring_pathtermination_ao.h - split/kernel_indirect_background.h - split/kernel_indirect_subsurface.h - split/kernel_lamp_emission.h - split/kernel_next_iteration_setup.h - split/kernel_path_init.h - split/kernel_queue_enqueue.h - split/kernel_scene_intersect.h - split/kernel_shader_setup.h - split/kernel_shader_sort.h - split/kernel_shader_eval.h - split/kernel_shadow_blocked_ao.h - split/kernel_shadow_blocked_dl.h - split/kernel_split_common.h - split/kernel_split_data.h - split/kernel_split_data_types.h - split/kernel_subsurface_scatter.h + ../util/atomic.h + ../util/color.h + ../util/defines.h + ../util/half.h + ../util/hash.h + ../util/math.h + ../util/math_fast.h + ../util/math_intersect.h + ../util/math_float2.h + ../util/math_float3.h + ../util/math_float4.h + ../util/math_int2.h + ../util/math_int3.h + ../util/math_int4.h + ../util/math_matrix.h + ../util/projection.h + ../util/rect.h + ../util/static_assert.h + ../util/transform.h + ../util/texture.h + ../util/types.h + ../util/types_float2.h + ../util/types_float2_impl.h + ../util/types_float3.h + ../util/types_float3_impl.h + ../util/types_float4.h + ../util/types_float4_impl.h + ../util/types_float8.h + ../util/types_float8_impl.h + ../util/types_int2.h + ../util/types_int2_impl.h + ../util/types_int3.h + ../util/types_int3_impl.h + ../util/types_int4.h + ../util/types_int4_impl.h + ../util/types_uchar2.h + ../util/types_uchar2_impl.h + ../util/types_uchar3.h + ../util/types_uchar3_impl.h + ../util/types_uchar4.h + ../util/types_uchar4_impl.h + ../util/types_uint2.h + ../util/types_uint2_impl.h + ../util/types_uint3.h + ../util/types_uint3_impl.h + ../util/types_uint4.h + ../util/types_uint4_impl.h + ../util/types_ushort4.h + ../util/types_vector3.h + ../util/types_vector3_impl.h ) set(LIB @@ -393,19 +364,10 @@ if(WITH_CYCLES_CUDA_BINARIES) endif() # build for each arch - set(cuda_sources kernels/cuda/kernel.cu kernels/cuda/kernel_split.cu - ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_CLOSURE_HEADERS} - ${SRC_UTIL_HEADERS} - ) - set(cuda_filter_sources kernels/cuda/filter.cu - ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_FILTER_HEADERS} + set(cuda_sources device/cuda/kernel.cu + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} ${SRC_UTIL_HEADERS} ) set(cuda_cubins) @@ -427,7 +389,7 @@ if(WITH_CYCLES_CUDA_BINARIES) endif() endif() - set(cuda_kernel_src "/kernels/cuda/${name}.cu") + set(cuda_kernel_src "/device/cuda/${name}.cu") set(cuda_flags ${flags} -D CCL_NAMESPACE_BEGIN= @@ -435,7 +397,7 @@ if(WITH_CYCLES_CUDA_BINARIES) -D NVCC -m ${CUDA_BITS} -I ${CMAKE_CURRENT_SOURCE_DIR}/.. - -I ${CMAKE_CURRENT_SOURCE_DIR}/kernels/cuda + -I ${CMAKE_CURRENT_SOURCE_DIR}/device/cuda --use_fast_math -o ${CMAKE_CURRENT_BINARY_DIR}/${cuda_file}) @@ -474,16 +436,27 @@ if(WITH_CYCLES_CUDA_BINARIES) -cuda-toolkit-dir "${cuda_toolkit_root_dir}" DEPENDS ${kernel_sources} cycles_cubin_cc) else() - add_custom_command( - OUTPUT ${cuda_file} - COMMAND ${cuda_nvcc_executable} + set(_cuda_nvcc_args -arch=${arch} ${CUDA_NVCC_FLAGS} --${format} ${CMAKE_CURRENT_SOURCE_DIR}${cuda_kernel_src} --ptxas-options="-v" - ${cuda_flags} - DEPENDS ${kernel_sources}) + ${cuda_flags}) + + if(WITH_COMPILER_CCACHE AND CCACHE_PROGRAM) + add_custom_command( + OUTPUT ${cuda_file} + COMMAND ${CCACHE_PROGRAM} ${cuda_nvcc_executable} ${_cuda_nvcc_args} + DEPENDS ${kernel_sources}) + else() + add_custom_command( + OUTPUT ${cuda_file} + COMMAND ${cuda_nvcc_executable} ${_cuda_nvcc_args} + DEPENDS ${kernel_sources}) + endif() + + unset(_cuda_nvcc_args) endif() delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${cuda_file}" ${CYCLES_INSTALL_PATH}/lib) list(APPEND cuda_cubins ${cuda_file}) @@ -523,14 +496,8 @@ if(WITH_CYCLES_CUDA_BINARIES) endif() if(DEFINED cuda_nvcc_executable AND DEFINED cuda_toolkit_root_dir) # Compile regular kernel - CYCLES_CUDA_KERNEL_ADD(${arch} ${prev_arch} filter "" "${cuda_filter_sources}" FALSE) CYCLES_CUDA_KERNEL_ADD(${arch} ${prev_arch} kernel "" "${cuda_sources}" FALSE) - if(WITH_CYCLES_CUDA_SPLIT_KERNEL_BINARIES) - # Compile split kernel - CYCLES_CUDA_KERNEL_ADD(${arch} ${prev_arch} kernel_split "-D __SPLIT__" "${cuda_sources}" FALSE) - endif() - if(WITH_CYCLES_CUDA_BUILD_SERIAL) set(prev_arch ${arch}) endif() @@ -544,18 +511,94 @@ if(WITH_CYCLES_CUDA_BINARIES) cycles_set_solution_folder(cycles_kernel_cuda) endif() +####################################################### START + +# HIP module + +if(WITH_CYCLES_HIP_BINARIES AND WITH_CYCLES_DEVICE_HIP) + # build for each arch + set(hip_sources device/hip/kernel.cpp + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_HIP_HEADERS} + ${SRC_UTIL_HEADERS} + ) + set(hip_fatbins) + + macro(CYCLES_HIP_KERNEL_ADD arch name flags sources experimental) + set(format "fatbin") + set(hip_file ${name}_${arch}.${format}) + set(kernel_sources ${sources}) + + set(hip_kernel_src "/device/hip/${name}.cpp") + + if(WIN32) + set(hip_command ${CMAKE_COMMAND}) + set(hip_flags + -E env "HIP_PATH=${HIP_ROOT_DIR}" "PATH=${HIP_PERL_DIR}" + ${HIP_HIPCC_EXECUTABLE}.bat) + else() + set(hip_command ${HIP_HIPCC_EXECUTABLE}) + set(hip_flags) + endif() + + set(hip_flags + ${hip_flags} + --amdgpu-target=${arch} + ${HIP_HIPCC_FLAGS} + --genco + ${CMAKE_CURRENT_SOURCE_DIR}${hip_kernel_src} + ${flags} + -D CCL_NAMESPACE_BEGIN= + -D CCL_NAMESPACE_END= + -D HIPCC + -I ${CMAKE_CURRENT_SOURCE_DIR}/.. + -I ${CMAKE_CURRENT_SOURCE_DIR}/device/hip + -Wno-parentheses-equality + -Wno-unused-value + --hipcc-func-supp + -ffast-math + -o ${CMAKE_CURRENT_BINARY_DIR}/${hip_file}) + + if(${experimental}) + set(hip_flags ${hip_flags} -D __KERNEL_EXPERIMENTAL__) + set(name ${name}_experimental) + endif() + + if(WITH_CYCLES_DEBUG) + set(hip_flags ${hip_flags} -D __KERNEL_DEBUG__) + endif() + + add_custom_command( + OUTPUT ${hip_file} + COMMAND ${hip_command} ${hip_flags} + DEPENDS ${kernel_sources}) + delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${hip_file}" ${CYCLES_INSTALL_PATH}/lib) + list(APPEND hip_fatbins ${hip_file}) + endmacro() + + foreach(arch ${CYCLES_HIP_BINARIES_ARCH}) + # Compile regular kernel + CYCLES_HIP_KERNEL_ADD(${arch} kernel "" "${hip_sources}" FALSE) + endforeach() + + add_custom_target(cycles_kernel_hip ALL DEPENDS ${hip_fatbins}) + cycles_set_solution_folder(cycles_kernel_hip) +endif() + +####################################################### END # OptiX PTX modules if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) - macro(CYCLES_OPTIX_KERNEL_ADD name flags) - set(input "kernels/optix/kernel_optix.cu") + macro(CYCLES_OPTIX_KERNEL_ADD name input flags) set(output "${CMAKE_CURRENT_BINARY_DIR}/${name}.ptx") set(cuda_flags ${flags} -I "${OPTIX_INCLUDE_DIR}" -I "${CMAKE_CURRENT_SOURCE_DIR}/.." - -I "${CMAKE_CURRENT_SOURCE_DIR}/kernels/cuda" + -I "${CMAKE_CURRENT_SOURCE_DIR}/device/cuda" --use_fast_math + -Wno-deprecated-gpu-targets -o ${output}) if(WITH_NANOVDB) @@ -579,13 +622,10 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) OUTPUT ${output} DEPENDS ${input} - ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_KERNELS_OPTIX_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_CLOSURE_HEADERS} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} + ${SRC_KERNEL_DEVICE_OPTIX_HEADERS} ${SRC_UTIL_HEADERS} COMMAND ${CUBIN_CC_ENV} "$" @@ -602,13 +642,10 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) ${output} DEPENDS ${input} - ${SRC_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_KERNELS_OPTIX_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_CLOSURE_HEADERS} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} + ${SRC_KERNEL_DEVICE_OPTIX_HEADERS} ${SRC_UTIL_HEADERS} COMMAND ${CUDA_NVCC_EXECUTABLE} @@ -624,8 +661,14 @@ if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) delayed_install("${CMAKE_CURRENT_BINARY_DIR}" "${output}" ${CYCLES_INSTALL_PATH}/lib) endmacro() - CYCLES_OPTIX_KERNEL_ADD(kernel_optix "-D __NO_SHADER_RAYTRACE__") - CYCLES_OPTIX_KERNEL_ADD(kernel_optix_shader_raytrace "--keep-device-functions") + CYCLES_OPTIX_KERNEL_ADD( + kernel_optix + "device/optix/kernel.cu" + "") + CYCLES_OPTIX_KERNEL_ADD( + kernel_optix_shader_raytrace + "device/optix/kernel_shader_raytrace.cu" + "--keep-device-functions") add_custom_target(cycles_kernel_optix ALL DEPENDS ${optix_ptx}) cycles_set_solution_folder(cycles_kernel_optix) @@ -638,7 +681,7 @@ if(WITH_CYCLES_OSL) cycles_kernel_osl ) add_subdirectory(osl) - add_subdirectory(shaders) + add_subdirectory(osl/shaders) endif() # CPU module @@ -659,63 +702,52 @@ if(WITH_COMPILER_ASAN) endif() endif() -set_source_files_properties(kernels/cpu/kernel.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") -set_source_files_properties(kernels/cpu/kernel_split.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") -set_source_files_properties(kernels/cpu/filter.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") +set_source_files_properties(device/cpu/kernel.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_KERNEL_FLAGS}") if(CXX_HAS_SSE) - set_source_files_properties(kernels/cpu/kernel_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_sse2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE2_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_sse3.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE3_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_sse41.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_SSE41_KERNEL_FLAGS}") endif() if(CXX_HAS_AVX) - set_source_files_properties(kernels/cpu/kernel_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_avx.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX_KERNEL_FLAGS}") endif() if(CXX_HAS_AVX2) - set_source_files_properties(kernels/cpu/kernel_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/kernel_split_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") - set_source_files_properties(kernels/cpu/filter_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") + set_source_files_properties(device/cpu/kernel_avx2.cpp PROPERTIES COMPILE_FLAGS "${CYCLES_AVX2_KERNEL_FLAGS}") endif() cycles_add_library(cycles_kernel "${LIB}" - ${SRC_CPU_KERNELS} - ${SRC_CUDA_KERNELS} - ${SRC_OPTIX_KERNELS} - ${SRC_OPENCL_KERNELS} - ${SRC_HEADERS} - ${SRC_KERNELS_CPU_HEADERS} - ${SRC_KERNELS_CUDA_HEADERS} - ${SRC_KERNELS_OPTIX_HEADERS} - ${SRC_KERNELS_OPENCL_HEADERS} - ${SRC_BVH_HEADERS} - ${SRC_CLOSURE_HEADERS} - ${SRC_FILTER_HEADERS} - ${SRC_SVM_HEADERS} - ${SRC_GEOM_HEADERS} - ${SRC_SPLIT_HEADERS} + ${SRC_KERNEL_DEVICE_CPU} + ${SRC_KERNEL_DEVICE_CUDA} + ${SRC_KERNEL_DEVICE_HIP} + ${SRC_KERNEL_DEVICE_OPTIX} + ${SRC_KERNEL_HEADERS} + ${SRC_KERNEL_DEVICE_CPU_HEADERS} + ${SRC_KERNEL_DEVICE_GPU_HEADERS} + ${SRC_KERNEL_DEVICE_CUDA_HEADERS} + ${SRC_KERNEL_DEVICE_HIP_HEADERS} + ${SRC_KERNEL_DEVICE_OPTIX_HEADERS} ) -source_group("bvh" FILES ${SRC_BVH_HEADERS}) -source_group("closure" FILES ${SRC_CLOSURE_HEADERS}) -source_group("filter" FILES ${SRC_FILTER_HEADERS}) -source_group("geom" FILES ${SRC_GEOM_HEADERS}) -source_group("kernel" FILES ${SRC_HEADERS}) -source_group("kernel\\split" FILES ${SRC_SPLIT_HEADERS}) -source_group("kernels\\cpu" FILES ${SRC_CPU_KERNELS} ${SRC_KERNELS_CPU_HEADERS}) -source_group("kernels\\cuda" FILES ${SRC_CUDA_KERNELS} ${SRC_KERNELS_CUDA_HEADERS}) -source_group("kernels\\opencl" FILES ${SRC_OPENCL_KERNELS} ${SRC_KERNELS_OPENCL_HEADERS}) -source_group("kernels\\optix" FILES ${SRC_OPTIX_KERNELS} ${SRC_KERNELS_OPTIX_HEADERS}) -source_group("svm" FILES ${SRC_SVM_HEADERS}) +source_group("bake" FILES ${SRC_KERNEL_BAKE_HEADERS}) +source_group("bvh" FILES ${SRC_KERNEL_BVH_HEADERS}) +source_group("camera" FILES ${SRC_KERNEL_CAMERA_HEADERS}) +source_group("closure" FILES ${SRC_KERNEL_CLOSURE_HEADERS}) +source_group("device\\cpu" FILES ${SRC_KERNEL_DEVICE_CPU} ${SRC_KERNEL_DEVICE_CPU_HEADERS}) +source_group("device\\cuda" FILES ${SRC_KERNEL_DEVICE_CUDA} ${SRC_KERNEL_DEVICE_CUDA_HEADERS}) +source_group("device\\gpu" FILES ${SRC_KERNEL_DEVICE_GPU_HEADERS}) +source_group("device\\hip" FILES ${SRC_KERNEL_DEVICE_HIP} ${SRC_KERNEL_DEVICE_HIP_HEADERS}) +source_group("device\\optix" FILES ${SRC_KERNEL_DEVICE_OPTIX} ${SRC_KERNEL_DEVICE_OPTIX_HEADERS}) +source_group("film" FILES ${SRC_KERNEL_FILM_HEADERS}) +source_group("geom" FILES ${SRC_KERNEL_GEOM_HEADERS}) +source_group("integrator" FILES ${SRC_KERNEL_INTEGRATOR_HEADERS}) +source_group("kernel" FILES ${SRC_KERNEL_TYPES_HEADERS}) +source_group("light" FILES ${SRC_KERNEL_LIGHT_HEADERS}) +source_group("sample" FILES ${SRC_KERNEL_SAMPLE_HEADERS}) +source_group("svm" FILES ${SRC_KERNEL_SVM_HEADERS}) +source_group("util" FILES ${SRC_KERNEL_UTIL_HEADERS}) if(WITH_CYCLES_CUDA) add_dependencies(cycles_kernel cycles_kernel_cuda) @@ -723,32 +755,32 @@ endif() if(WITH_CYCLES_DEVICE_OPTIX AND WITH_CYCLES_CUDA_BINARIES) add_dependencies(cycles_kernel cycles_kernel_optix) endif() +if(WITH_CYCLES_HIP) + add_dependencies(cycles_kernel cycles_kernel_hip) +endif() -# OpenCL kernel +# Install kernel source for runtime compilation -# set(KERNEL_PREPROCESSED ${CMAKE_CURRENT_BINARY_DIR}/kernel_preprocessed.cl) -# add_custom_command( -# OUTPUT ${KERNEL_PREPROCESSED} -# COMMAND gcc -x c++ -E ${CMAKE_CURRENT_SOURCE_DIR}/kernel.cl -I ${CMAKE_CURRENT_SOURCE_DIR}/../util/ -DCCL_NAMESPACE_BEGIN= -DCCL_NAMESPACE_END= -o ${KERNEL_PREPROCESSED} -# DEPENDS ${SRC_KERNEL} ${SRC_UTIL_HEADERS}) -# add_custom_target(cycles_kernel_preprocess ALL DEPENDS ${KERNEL_PREPROCESSED}) -# delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${KERNEL_PREPROCESSED}" ${CYCLES_INSTALL_PATH}/kernel) - -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_OPENCL_KERNELS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/opencl) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_CUDA_KERNELS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/cuda) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_OPTIX_KERNELS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/optix) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNELS_OPENCL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/opencl) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNELS_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/cuda) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNELS_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/kernels/optix) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_FILTER_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/filter) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_SVM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/svm) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_GEOM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/geom) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_BAKE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bake) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_BVH_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/bvh) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_CAMERA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/camera) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_CLOSURE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/closure) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_CUDA}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_CUDA_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/cuda) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_GPU_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/gpu) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIP}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_HIP_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/hip) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_DEVICE_OPTIX_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/device/optix) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_FILM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/film) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_GEOM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/geom) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_INTEGRATOR_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/integrator) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_LIGHT_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/light) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_SAMPLE_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/sample) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_SVM_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/svm) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_TYPES_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel) +delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_KERNEL_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/util) delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_UTIL_HEADERS}" ${CYCLES_INSTALL_PATH}/source/util) -delayed_install(${CMAKE_CURRENT_SOURCE_DIR} "${SRC_SPLIT_HEADERS}" ${CYCLES_INSTALL_PATH}/source/kernel/split) - if(WITH_NANOVDB) set(SRC_NANOVDB_HEADERS diff --git a/intern/cycles/kernel/bake/bake.h b/intern/cycles/kernel/bake/bake.h new file mode 100644 index 00000000000..0a78a635d75 --- /dev/null +++ b/intern/cycles/kernel/bake/bake.h @@ -0,0 +1,119 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/camera/projection.h" +#include "kernel/integrator/shader_eval.h" + +#include "kernel/geom/geom.h" + +CCL_NAMESPACE_BEGIN + +ccl_device void kernel_displace_evaluate(KernelGlobals kg, + ccl_global const KernelShaderEvalInput *input, + ccl_global float *output, + const int offset) +{ + /* Setup shader data. */ + const KernelShaderEvalInput in = input[offset]; + + ShaderData sd; + shader_setup_from_displace(kg, &sd, in.object, in.prim, in.u, in.v); + + /* Evaluate displacement shader. */ + const float3 P = sd.P; + shader_eval_displacement(kg, INTEGRATOR_STATE_NULL, &sd); + float3 D = sd.P - P; + + object_inverse_dir_transform(kg, &sd, &D); + +#ifdef __KERNEL_DEBUG_NAN__ + if (!isfinite3_safe(D)) { + kernel_assert(!"Cycles displacement with non-finite value detected"); + } +#endif + + /* Ensure finite displacement, preventing BVH from becoming degenerate and avoiding possible + * traversal issues caused by non-finite math. */ + D = ensure_finite3(D); + + /* Write output. */ + output[offset * 3 + 0] += D.x; + output[offset * 3 + 1] += D.y; + output[offset * 3 + 2] += D.z; +} + +ccl_device void kernel_background_evaluate(KernelGlobals kg, + ccl_global const KernelShaderEvalInput *input, + ccl_global float *output, + const int offset) +{ + /* Setup ray */ + const KernelShaderEvalInput in = input[offset]; + const float3 ray_P = zero_float3(); + const float3 ray_D = equirectangular_to_direction(in.u, in.v); + const float ray_time = 0.5f; + + /* Setup shader data. */ + ShaderData sd; + shader_setup_from_background(kg, &sd, ray_P, ray_D, ray_time); + + /* Evaluate shader. + * This is being evaluated for all BSDFs, so path flag does not contain a specific type. */ + const uint32_t path_flag = PATH_RAY_EMISSION; + shader_eval_surface( + kg, INTEGRATOR_STATE_NULL, &sd, NULL, path_flag); + float3 color = shader_background_eval(&sd); + +#ifdef __KERNEL_DEBUG_NAN__ + if (!isfinite3_safe(color)) { + kernel_assert(!"Cycles background with non-finite value detected"); + } +#endif + + /* Ensure finite color, avoiding possible numerical instabilities in the path tracing kernels. */ + color = ensure_finite3(color); + + /* Write output. */ + output[offset * 3 + 0] += color.x; + output[offset * 3 + 1] += color.y; + output[offset * 3 + 2] += color.z; +} + +ccl_device void kernel_curve_shadow_transparency_evaluate( + KernelGlobals kg, + ccl_global const KernelShaderEvalInput *input, + ccl_global float *output, + const int offset) +{ + /* Setup shader data. */ + const KernelShaderEvalInput in = input[offset]; + + ShaderData sd; + shader_setup_from_curve(kg, &sd, in.object, in.prim, __float_as_int(in.v), in.u); + + /* Evaluate transparency. */ + shader_eval_surface( + kg, INTEGRATOR_STATE_NULL, &sd, NULL, PATH_RAY_SHADOW); + + /* Write output. */ + output[offset] = clamp(average(shader_bsdf_transparency(kg, &sd)), 0.0f, 1.0f); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh.h b/intern/cycles/kernel/bvh/bvh.h index acf29cf1baf..0e083812355 100644 --- a/intern/cycles/kernel/bvh/bvh.h +++ b/intern/cycles/kernel/bvh/bvh.h @@ -25,12 +25,16 @@ * the code has been extended and modified to support more primitives and work * with CPU/CUDA/OpenCL. */ +#pragma once + #ifdef __EMBREE__ -# include "kernel/bvh/bvh_embree.h" +# include "kernel/bvh/embree.h" #endif -#include "kernel/bvh/bvh_types.h" -#include "kernel/bvh/bvh_util.h" +#include "kernel/bvh/types.h" +#include "kernel/bvh/util.h" + +#include "kernel/integrator/state_util.h" CCL_NAMESPACE_BEGIN @@ -38,28 +42,28 @@ CCL_NAMESPACE_BEGIN /* Regular BVH traversal */ -# include "kernel/bvh/bvh_nodes.h" +# include "kernel/bvh/nodes.h" # define BVH_FUNCTION_NAME bvh_intersect # define BVH_FUNCTION_FEATURES 0 -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # if defined(__HAIR__) # define BVH_FUNCTION_NAME bvh_intersect_hair # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # endif # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_motion # define BVH_FUNCTION_FEATURES BVH_MOTION -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # endif # if defined(__HAIR__) && defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_hair_motion # define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION -# include "kernel/bvh/bvh_traversal.h" +# include "kernel/bvh/traversal.h" # endif /* Subsurface scattering BVH traversal */ @@ -67,12 +71,12 @@ CCL_NAMESPACE_BEGIN # if defined(__BVH_LOCAL__) # define BVH_FUNCTION_NAME bvh_intersect_local # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_local.h" +# include "kernel/bvh/local.h" # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_local_motion # define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR -# include "kernel/bvh/bvh_local.h" +# include "kernel/bvh/local.h" # endif # endif /* __BVH_LOCAL__ */ @@ -81,12 +85,12 @@ CCL_NAMESPACE_BEGIN # if defined(__VOLUME__) # define BVH_FUNCTION_NAME bvh_intersect_volume # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_volume.h" +# include "kernel/bvh/volume.h" # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_volume_motion # define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR -# include "kernel/bvh/bvh_volume.h" +# include "kernel/bvh/volume.h" # endif # endif /* __VOLUME__ */ @@ -95,24 +99,24 @@ CCL_NAMESPACE_BEGIN # if defined(__SHADOW_RECORD_ALL__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all # define BVH_FUNCTION_FEATURES 0 -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # if defined(__HAIR__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # endif # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_motion # define BVH_FUNCTION_FEATURES BVH_MOTION -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # endif # if defined(__HAIR__) && defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_shadow_all_hair_motion # define BVH_FUNCTION_FEATURES BVH_HAIR | BVH_MOTION -# include "kernel/bvh/bvh_shadow_all.h" +# include "kernel/bvh/shadow_all.h" # endif # endif /* __SHADOW_RECORD_ALL__ */ @@ -121,12 +125,12 @@ CCL_NAMESPACE_BEGIN # if defined(__VOLUME_RECORD_ALL__) # define BVH_FUNCTION_NAME bvh_intersect_volume_all # define BVH_FUNCTION_FEATURES BVH_HAIR -# include "kernel/bvh/bvh_volume_all.h" +# include "kernel/bvh/volume_all.h" # if defined(__OBJECT_MOTION__) # define BVH_FUNCTION_NAME bvh_intersect_volume_all_motion # define BVH_FUNCTION_FEATURES BVH_MOTION | BVH_HAIR -# include "kernel/bvh/bvh_volume_all.h" +# include "kernel/bvh/volume_all.h" # endif # endif /* __VOLUME_RECORD_ALL__ */ @@ -137,7 +141,7 @@ CCL_NAMESPACE_BEGIN #endif /* __KERNEL_OPTIX__ */ -ccl_device_inline bool scene_intersect_valid(const Ray *ray) +ccl_device_inline bool scene_intersect_valid(ccl_private const Ray *ray) { /* NOTE: Due to some vectorization code non-finite origin point might * cause lots of false-positive intersections which will overflow traversal @@ -152,13 +156,11 @@ ccl_device_inline bool scene_intersect_valid(const Ray *ray) return isfinite_safe(ray->P.x) && isfinite_safe(ray->D.x) && len_squared(ray->D) != 0.0f; } -ccl_device_intersect bool scene_intersect(KernelGlobals *kg, - const Ray *ray, +ccl_device_intersect bool scene_intersect(KernelGlobals kg, + ccl_private const Ray *ray, const uint visibility, - Intersection *isect) + ccl_private Intersection *isect) { - PROFILING_INIT(kg, PROFILING_INTERSECT); - #ifdef __KERNEL_OPTIX__ uint p0 = 0; uint p1 = 0; @@ -167,15 +169,25 @@ ccl_device_intersect bool scene_intersect(KernelGlobals *kg, uint p4 = visibility; uint p5 = PRIMITIVE_NONE; + uint ray_mask = visibility & 0xFF; + uint ray_flags = OPTIX_RAY_FLAG_NONE; + if (0 == ray_mask && (visibility & ~0xFF) != 0) { + ray_mask = 0xFF; + ray_flags = OPTIX_RAY_FLAG_ENFORCE_ANYHIT; + } + else if (visibility & PATH_RAY_SHADOW_OPAQUE) { + ray_flags = OPTIX_RAY_FLAG_TERMINATE_ON_FIRST_HIT; + } + optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, ray->D, 0.0f, ray->t, ray->time, - 0xF, - OPTIX_RAY_FLAG_NONE, - 0, // SBT offset for PG_HITD + ray_mask, + ray_flags, + 0, /* SBT offset for PG_HITD */ 0, 0, p0, @@ -238,26 +250,24 @@ ccl_device_intersect bool scene_intersect(KernelGlobals *kg, } #ifdef __BVH_LOCAL__ -ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, - const Ray *ray, - LocalIntersection *local_isect, +ccl_device_intersect bool scene_intersect_local(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private LocalIntersection *local_isect, int local_object, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { - PROFILING_INIT(kg, PROFILING_INTERSECT_LOCAL); - # ifdef __KERNEL_OPTIX__ - uint p0 = ((uint64_t)lcg_state) & 0xFFFFFFFF; - uint p1 = (((uint64_t)lcg_state) >> 32) & 0xFFFFFFFF; - uint p2 = ((uint64_t)local_isect) & 0xFFFFFFFF; - uint p3 = (((uint64_t)local_isect) >> 32) & 0xFFFFFFFF; + uint p0 = pointer_pack_to_uint_0(lcg_state); + uint p1 = pointer_pack_to_uint_1(lcg_state); + uint p2 = pointer_pack_to_uint_0(local_isect); + uint p3 = pointer_pack_to_uint_1(local_isect); uint p4 = local_object; - // Is set to zero on miss or if ray is aborted, so can be used as return value + /* Is set to zero on miss or if ray is aborted, so can be used as return value. */ uint p5 = max_hits; if (local_isect) { - local_isect->num_hits = 0; // Initialize hit count to zero + local_isect->num_hits = 0; /* Initialize hit count to zero. */ } optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, @@ -265,11 +275,10 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, 0.0f, ray->t, ray->time, - // Skip curves - 0x3, - // Need to always call into __anyhit__kernel_optix_local_hit + 0xFF, + /* Need to always call into __anyhit__kernel_optix_local_hit. */ OPTIX_RAY_FLAG_ENFORCE_ANYHIT, - 2, // SBT offset for PG_HITL + 2, /* SBT offset for PG_HITL */ 0, 0, p0, @@ -313,8 +322,8 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, float3 dir = ray->D; float3 idir = ray->D; Transform ob_itfm; - rtc_ray.tfar = bvh_instance_motion_push( - kg, local_object, ray, &P, &dir, &idir, ray->t, &ob_itfm); + rtc_ray.tfar = ray->t * + bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); /* bvh_instance_motion_push() returns the inverse transform but * it's not needed here. */ (void)ob_itfm; @@ -353,65 +362,71 @@ ccl_device_intersect bool scene_intersect_local(KernelGlobals *kg, #endif #ifdef __SHADOW_RECORD_ALL__ -ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals kg, + IntegratorShadowState state, + ccl_private const Ray *ray, uint visibility, uint max_hits, - uint *num_hits) + ccl_private uint *num_recorded_hits, + ccl_private float *throughput) { - PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW_ALL); - # ifdef __KERNEL_OPTIX__ - uint p0 = ((uint64_t)isect) & 0xFFFFFFFF; - uint p1 = (((uint64_t)isect) >> 32) & 0xFFFFFFFF; + uint p0 = state; + uint p1 = __float_as_uint(1.0f); /* Throughput. */ + uint p2 = 0; /* Number of hits. */ uint p3 = max_hits; uint p4 = visibility; uint p5 = false; - *num_hits = 0; // Initialize hit count to zero + uint ray_mask = visibility & 0xFF; + if (0 == ray_mask && (visibility & ~0xFF) != 0) { + ray_mask = 0xFF; + } + optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, ray->D, 0.0f, ray->t, ray->time, - 0xF, - // Need to always call into __anyhit__kernel_optix_shadow_all_hit + ray_mask, + /* Need to always call into __anyhit__kernel_optix_shadow_all_hit. */ OPTIX_RAY_FLAG_ENFORCE_ANYHIT, - 1, // SBT offset for PG_HITS + 1, /* SBT offset for PG_HITS */ 0, 0, p0, p1, - *num_hits, + p2, p3, p4, p5); + *num_recorded_hits = uint16_unpack_from_uint_0(p2); + *throughput = __uint_as_float(p1); + return p5; # else /* __KERNEL_OPTIX__ */ if (!scene_intersect_valid(ray)) { - *num_hits = 0; + *num_recorded_hits = 0; + *throughput = 1.0f; return false; } # ifdef __EMBREE__ if (kernel_data.bvh.scene) { CCLIntersectContext ctx(kg, CCLIntersectContext::RAY_SHADOW_ALL); - ctx.isect_s = isect; + Intersection *isect_array = (Intersection *)state->shadow_isect; + ctx.isect_s = isect_array; ctx.max_hits = max_hits; - ctx.num_hits = 0; IntersectContext rtc_ctx(&ctx); RTCRay rtc_ray; kernel_embree_setup_ray(*ray, rtc_ray, visibility); rtcOccluded1(kernel_data.bvh.scene, &rtc_ctx.context, &rtc_ray); - if (ctx.num_hits > max_hits) { - return true; - } - *num_hits = ctx.num_hits; - return rtc_ray.tfar == -INFINITY; + *num_recorded_hits = ctx.num_recorded_hits; + *throughput = ctx.throughput; + return ctx.opaque_hit; } # endif /* __EMBREE__ */ @@ -419,33 +434,35 @@ ccl_device_intersect bool scene_intersect_shadow_all(KernelGlobals *kg, if (kernel_data.bvh.have_motion) { # ifdef __HAIR__ if (kernel_data.bvh.have_curves) { - return bvh_intersect_shadow_all_hair_motion(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_hair_motion( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } # endif /* __HAIR__ */ - return bvh_intersect_shadow_all_motion(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_motion( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } # endif /* __OBJECT_MOTION__ */ # ifdef __HAIR__ if (kernel_data.bvh.have_curves) { - return bvh_intersect_shadow_all_hair(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all_hair( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } # endif /* __HAIR__ */ - return bvh_intersect_shadow_all(kg, ray, isect, visibility, max_hits, num_hits); + return bvh_intersect_shadow_all( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); # endif /* __KERNEL_OPTIX__ */ } #endif /* __SHADOW_RECORD_ALL__ */ #ifdef __VOLUME__ -ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_intersect bool scene_intersect_volume(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { - PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME); - # ifdef __KERNEL_OPTIX__ uint p0 = 0; uint p1 = 0; @@ -454,16 +471,21 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg, uint p4 = visibility; uint p5 = PRIMITIVE_NONE; + uint ray_mask = visibility & 0xFF; + if (0 == ray_mask && (visibility & ~0xFF) != 0) { + ray_mask = 0xFF; + } + optixTrace(scene_intersect_valid(ray) ? kernel_data.bvh.scene : 0, ray->P, ray->D, 0.0f, ray->t, ray->time, - // Skip everything but volumes - 0x2, - OPTIX_RAY_FLAG_NONE, - 0, // SBT offset for PG_HITD + ray_mask, + /* Need to always call into __anyhit__kernel_optix_volume_test. */ + OPTIX_RAY_FLAG_ENFORCE_ANYHIT, + 3, /* SBT offset for PG_HITV */ 0, 0, p0, @@ -498,14 +520,12 @@ ccl_device_intersect bool scene_intersect_volume(KernelGlobals *kg, #endif /* __VOLUME__ */ #ifdef __VOLUME_RECORD_ALL__ -ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_intersect uint scene_intersect_volume_all(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint max_hits, const uint visibility) { - PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_ALL); - if (!scene_intersect_valid(ray)) { return false; } diff --git a/intern/cycles/kernel/bvh/bvh_util.h b/intern/cycles/kernel/bvh/bvh_util.h deleted file mode 100644 index b1faebce957..00000000000 --- a/intern/cycles/kernel/bvh/bvh_util.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -CCL_NAMESPACE_BEGIN - -/* Ray offset to avoid self intersection. - * - * This function should be used to compute a modified ray start position for - * rays leaving from a surface. */ - -ccl_device_inline float3 ray_offset(float3 P, float3 Ng) -{ -#ifdef __INTERSECTION_REFINE__ - const float epsilon_f = 1e-5f; - /* ideally this should match epsilon_f, but instancing and motion blur - * precision makes it problematic */ - const float epsilon_test = 1.0f; - const int epsilon_i = 32; - - float3 res; - - /* x component */ - if (fabsf(P.x) < epsilon_test) { - res.x = P.x + Ng.x * epsilon_f; - } - else { - uint ix = __float_as_uint(P.x); - ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i; - res.x = __uint_as_float(ix); - } - - /* y component */ - if (fabsf(P.y) < epsilon_test) { - res.y = P.y + Ng.y * epsilon_f; - } - else { - uint iy = __float_as_uint(P.y); - iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i; - res.y = __uint_as_float(iy); - } - - /* z component */ - if (fabsf(P.z) < epsilon_test) { - res.z = P.z + Ng.z * epsilon_f; - } - else { - uint iz = __float_as_uint(P.z); - iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i; - res.z = __uint_as_float(iz); - } - - return res; -#else - const float epsilon_f = 1e-4f; - return P + epsilon_f * Ng; -#endif -} - -/* This function should be used to compute a modified ray start position for - * rays leaving from a surface. The algorithm slightly distorts flat surface - * of a triangle. Surface is lifted by amount h along normal n in the incident - * point. */ - -ccl_device_inline float3 smooth_surface_offset(KernelGlobals *kg, ShaderData *sd, float3 Ng) -{ - float3 V[3], N[3]; - triangle_vertices_and_normals(kg, sd->prim, V, N); - - const float u = sd->u, v = sd->v; - const float w = 1 - u - v; - float3 P = V[0] * u + V[1] * v + V[2] * w; /* Local space */ - float3 n = N[0] * u + N[1] * v + N[2] * w; /* We get away without normalization */ - - object_normal_transform(kg, sd, &n); /* Normal x scale, world space */ - - /* Parabolic approximation */ - float a = dot(N[2] - N[0], V[0] - V[2]); - float b = dot(N[2] - N[1], V[1] - V[2]); - float c = dot(N[1] - N[0], V[1] - V[0]); - float h = a * u * (u - 1) + (a + b + c) * u * v + b * v * (v - 1); - - /* Check flipped normals */ - if (dot(n, Ng) > 0) { - /* Local linear envelope */ - float h0 = max(max(dot(V[1] - V[0], N[0]), dot(V[2] - V[0], N[0])), 0.0f); - float h1 = max(max(dot(V[0] - V[1], N[1]), dot(V[2] - V[1], N[1])), 0.0f); - float h2 = max(max(dot(V[0] - V[2], N[2]), dot(V[1] - V[2], N[2])), 0.0f); - h0 = max(dot(V[0] - P, N[0]) + h0, 0.0f); - h1 = max(dot(V[1] - P, N[1]) + h1, 0.0f); - h2 = max(dot(V[2] - P, N[2]) + h2, 0.0f); - h = max(min(min(h0, h1), h2), h * 0.5f); - } - else { - float h0 = max(max(dot(V[0] - V[1], N[0]), dot(V[0] - V[2], N[0])), 0.0f); - float h1 = max(max(dot(V[1] - V[0], N[1]), dot(V[1] - V[2], N[1])), 0.0f); - float h2 = max(max(dot(V[2] - V[0], N[2]), dot(V[2] - V[1], N[2])), 0.0f); - h0 = max(dot(P - V[0], N[0]) + h0, 0.0f); - h1 = max(dot(P - V[1], N[1]) + h1, 0.0f); - h2 = max(dot(P - V[2], N[2]) + h2, 0.0f); - h = min(-min(min(h0, h1), h2), h * 0.5f); - } - - return n * h; -} - -/* Ray offset to avoid shadow terminator artifact. */ - -ccl_device_inline float3 ray_offset_shadow(KernelGlobals *kg, ShaderData *sd, float3 L) -{ - float NL = dot(sd->N, L); - bool transmit = (NL < 0.0f); - float3 Ng = (transmit ? -sd->Ng : sd->Ng); - float3 P = ray_offset(sd->P, Ng); - - if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) { - const float offset_cutoff = - kernel_tex_fetch(__objects, sd->object).shadow_terminator_geometry_offset; - /* Do ray offset (heavy stuff) only for close to be terminated triangles: - * offset_cutoff = 0.1f means that 10-20% of rays will be affected. Also - * make a smooth transition near the threshold. */ - if (offset_cutoff > 0.0f) { - float NgL = dot(Ng, L); - float offset_amount = 0.0f; - if (NL < offset_cutoff) { - offset_amount = clamp(2.0f - (NgL + NL) / offset_cutoff, 0.0f, 1.0f); - } - else { - offset_amount = clamp(1.0f - NgL / offset_cutoff, 0.0f, 1.0f); - } - if (offset_amount > 0.0f) { - P += smooth_surface_offset(kg, sd, Ng) * offset_amount; - } - } - } - - return P; -} - -#if defined(__VOLUME_RECORD_ALL__) || (defined(__SHADOW_RECORD_ALL__) && defined(__KERNEL_CPU__)) -/* ToDo: Move to another file? */ -ccl_device int intersections_compare(const void *a, const void *b) -{ - const Intersection *isect_a = (const Intersection *)a; - const Intersection *isect_b = (const Intersection *)b; - - if (isect_a->t < isect_b->t) - return -1; - else if (isect_a->t > isect_b->t) - return 1; - else - return 0; -} -#endif - -#if defined(__SHADOW_RECORD_ALL__) -ccl_device_inline void sort_intersections(Intersection *hits, uint num_hits) -{ - kernel_assert(num_hits > 0); - -# ifdef __KERNEL_GPU__ - /* Use bubble sort which has more friendly memory pattern on GPU. */ - bool swapped; - do { - swapped = false; - for (int j = 0; j < num_hits - 1; ++j) { - if (hits[j].t > hits[j + 1].t) { - struct Intersection tmp = hits[j]; - hits[j] = hits[j + 1]; - hits[j + 1] = tmp; - swapped = true; - } - } - --num_hits; - } while (swapped); -# else - qsort(hits, num_hits, sizeof(Intersection), intersections_compare); -# endif -} -#endif /* __SHADOW_RECORD_ALL__ | __VOLUME_RECORD_ALL__ */ - -/* Utility to quickly get a shader flags from an intersection. */ - -ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals *ccl_restrict kg, - const Intersection *isect) -{ - const int prim = kernel_tex_fetch(__prim_index, isect->prim); - int shader = 0; - -#ifdef __HAIR__ - if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) -#endif - { - shader = kernel_tex_fetch(__tri_shader, prim); - } -#ifdef __HAIR__ - else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } -#endif - - return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; -} - -ccl_device_forceinline int intersection_get_shader(KernelGlobals *ccl_restrict kg, - const Intersection *isect) -{ - const int prim = kernel_tex_fetch(__prim_index, isect->prim); - int shader = 0; - -#ifdef __HAIR__ - if (kernel_tex_fetch(__prim_type, isect->prim) & PRIMITIVE_ALL_TRIANGLE) -#endif - { - shader = kernel_tex_fetch(__tri_shader, prim); - } -#ifdef __HAIR__ - else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } -#endif - - return shader & SHADER_MASK; -} - -ccl_device_forceinline int intersection_get_object(KernelGlobals *ccl_restrict kg, - const Intersection *ccl_restrict isect) -{ - if (isect->object != OBJECT_NONE) { - return isect->object; - } - - return kernel_tex_fetch(__prim_object, isect->prim); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh_embree.h b/intern/cycles/kernel/bvh/embree.h similarity index 72% rename from intern/cycles/kernel/bvh/bvh_embree.h rename to intern/cycles/kernel/bvh/embree.h index 4605c3ea51d..9edd4f90a7e 100644 --- a/intern/cycles/kernel/bvh/bvh_embree.h +++ b/intern/cycles/kernel/bvh/embree.h @@ -14,16 +14,15 @@ * limitations under the License. */ +#pragma once + #include #include -// clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" -// clang-format on +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" -#include "util/util_vector.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -36,25 +35,33 @@ struct CCLIntersectContext { RAY_VOLUME_ALL = 4, } RayType; - KernelGlobals *kg; + KernelGlobals kg; RayType type; /* for shadow rays */ Intersection *isect_s; - int max_hits; - int num_hits; + uint max_hits; + uint num_hits; + uint num_recorded_hits; + float throughput; + float max_t; + bool opaque_hit; /* for SSS Rays: */ LocalIntersection *local_isect; int local_object_id; uint *lcg_state; - CCLIntersectContext(KernelGlobals *kg_, RayType type_) + CCLIntersectContext(KernelGlobals kg_, RayType type_) { kg = kg_; type = type_; max_hits = 1; num_hits = 0; + num_recorded_hits = 0; + throughput = 1.0f; + max_t = FLT_MAX; + opaque_hit = false; isect_s = NULL; local_isect = NULL; local_object_id = -1; @@ -98,16 +105,12 @@ ccl_device_inline void kernel_embree_setup_rayhit(const Ray &ray, rayhit.hit.primID = RTC_INVALID_GEOMETRY_ID; } -ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg, +ccl_device_inline void kernel_embree_convert_hit(KernelGlobals kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect) { - bool is_hair = hit->geomID & 1; - isect->u = is_hair ? hit->u : 1.0f - hit->v - hit->u; - isect->v = is_hair ? hit->v : hit->u; isect->t = ray->tfar; - isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); if (hit->instID[0] != RTC_INVALID_GEOMETRY_ID) { RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, hit->instID[0])); @@ -118,27 +121,36 @@ ccl_device_inline void kernel_embree_convert_hit(KernelGlobals *kg, else { isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData( rtcGetGeometry(kernel_data.bvh.scene, hit->geomID)); - isect->object = OBJECT_NONE; + isect->object = hit->geomID / 2; + } + + const bool is_hair = hit->geomID & 1; + if (is_hair) { + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, isect->prim); + isect->type = segment.type; + isect->prim = segment.prim; + isect->u = hit->u; + isect->v = hit->v; + } + else { + isect->type = kernel_tex_fetch(__objects, isect->object).primitive_type; + isect->u = 1.0f - hit->v - hit->u; + isect->v = hit->u; } - isect->type = kernel_tex_fetch(__prim_type, isect->prim); } -ccl_device_inline void kernel_embree_convert_sss_hit(KernelGlobals *kg, - const RTCRay *ray, - const RTCHit *hit, - Intersection *isect, - int local_object_id) +ccl_device_inline void kernel_embree_convert_sss_hit( + KernelGlobals kg, const RTCRay *ray, const RTCHit *hit, Intersection *isect, int object) { isect->u = 1.0f - hit->v - hit->u; isect->v = hit->u; isect->t = ray->tfar; - isect->Ng = make_float3(hit->Ng_x, hit->Ng_y, hit->Ng_z); RTCScene inst_scene = (RTCScene)rtcGetGeometryUserData( - rtcGetGeometry(kernel_data.bvh.scene, local_object_id * 2)); + rtcGetGeometry(kernel_data.bvh.scene, object * 2)); isect->prim = hit->primID + (intptr_t)rtcGetGeometryUserData(rtcGetGeometry(inst_scene, hit->geomID)); - isect->object = local_object_id; - isect->type = kernel_tex_fetch(__prim_type, isect->prim); + isect->object = object; + isect->type = kernel_tex_fetch(__objects, object).primitive_type; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh_local.h b/intern/cycles/kernel/bvh/local.h similarity index 90% rename from intern/cycles/kernel/bvh/bvh_local.h rename to intern/cycles/kernel/bvh/local.h index 4006c9c1632..79cde69699e 100644 --- a/intern/cycles/kernel/bvh/bvh_local.h +++ b/intern/cycles/kernel/bvh/local.h @@ -36,11 +36,11 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, - const Ray *ray, - LocalIntersection *local_isect, + bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private LocalIntersection *local_isect, int local_object, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { /* todo: @@ -74,9 +74,9 @@ ccl_device_inline if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; - isect_t = bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, isect_t, &ob_itfm); + isect_t *= bvh_instance_motion_push(kg, local_object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t = bvh_instance_push(kg, local_object, ray, &P, &dir, &idir, isect_t); + isect_t *= bvh_instance_push(kg, local_object, ray, &P, &dir, &idir); #endif object = local_object; } @@ -196,11 +196,11 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, - const Ray *ray, - LocalIntersection *local_isect, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private LocalIntersection *local_isect, int local_object, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, local_isect, local_object, lcg_state, max_hits); diff --git a/intern/cycles/kernel/bvh/bvh_nodes.h b/intern/cycles/kernel/bvh/nodes.h similarity index 98% rename from intern/cycles/kernel/bvh/bvh_nodes.h rename to intern/cycles/kernel/bvh/nodes.h index 5367bdb633c..71122085f69 100644 --- a/intern/cycles/kernel/bvh/bvh_nodes.h +++ b/intern/cycles/kernel/bvh/nodes.h @@ -16,7 +16,7 @@ // TODO(sergey): Look into avoid use of full Transform and use 3x3 matrix and // 3-vector which might be faster. -ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals *kg, +ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals kg, int node_addr, int child) { @@ -28,7 +28,7 @@ ccl_device_forceinline Transform bvh_unaligned_node_fetch_space(KernelGlobals *k return space; } -ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg, +ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals kg, const float3 P, const float3 idir, const float t, @@ -76,7 +76,7 @@ ccl_device_forceinline int bvh_aligned_node_intersect(KernelGlobals *kg, #endif } -ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals *kg, +ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals kg, const float3 P, const float3 dir, const float t, @@ -102,7 +102,7 @@ ccl_device_forceinline bool bvh_unaligned_node_intersect_child(KernelGlobals *kg return tnear <= tfar; } -ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg, +ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, @@ -134,7 +134,7 @@ ccl_device_forceinline int bvh_unaligned_node_intersect(KernelGlobals *kg, return mask; } -ccl_device_forceinline int bvh_node_intersect(KernelGlobals *kg, +ccl_device_forceinline int bvh_node_intersect(KernelGlobals kg, const float3 P, const float3 dir, const float3 idir, diff --git a/intern/cycles/kernel/bvh/bvh_shadow_all.h b/intern/cycles/kernel/bvh/shadow_all.h similarity index 51% rename from intern/cycles/kernel/bvh/bvh_shadow_all.h rename to intern/cycles/kernel/bvh/shadow_all.h index 2e94b1d7c37..049c6a03fe0 100644 --- a/intern/cycles/kernel/bvh/bvh_shadow_all.h +++ b/intern/cycles/kernel/bvh/shadow_all.h @@ -36,12 +36,13 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, - const Ray *ray, - Intersection *isect_array, + bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, + ccl_private const Ray *ray, + IntegratorShadowState state, const uint visibility, const uint max_hits, - uint *num_hits) + ccl_private uint *num_recorded_hits, + ccl_private float *throughput) { /* todo: * - likely and unlikely for if() statements @@ -57,21 +58,29 @@ ccl_device_inline int node_addr = kernel_data.bvh.root; /* ray parameters in registers */ - const float tmax = ray->t; float3 P = ray->P; float3 dir = bvh_clamp_direction(ray->D); float3 idir = bvh_inverse_direction(dir); int object = OBJECT_NONE; - float isect_t = tmax; + uint num_hits = 0; #if BVH_FEATURE(BVH_MOTION) Transform ob_itfm; #endif - int num_hits_in_instance = 0; + /* Max distance in world space. May be dynamically reduced when max number of + * recorded hits is exceeded and we no longer need to find hits beyond the max + * distance found. */ + float t_max_world = ray->t; + /* Equal to t_max_world when traversing top level BVH, transformed into local + * space when entering instances. */ + float t_max_current = t_max_world; + /* Conversion from world to local space for the current instance if any, 1.0 + * otherwise. */ + float t_world_to_instance = 1.0f; - *num_hits = 0; - isect_array->t = tmax; + *num_recorded_hits = 0; + *throughput = 1.0f; /* traversal loop */ do { @@ -88,7 +97,7 @@ ccl_device_inline dir, #endif idir, - isect_t, + t_max_current, node_addr, visibility, dist); @@ -130,7 +139,6 @@ ccl_device_inline if (prim_addr >= 0) { const int prim_addr2 = __float_as_int(leaf.y); const uint type = __float_as_int(leaf.w); - const uint p_type = type & PRIMITIVE_ALL; /* pop */ node_addr = traversal_stack[stack_ptr]; @@ -138,22 +146,25 @@ ccl_device_inline /* primitive intersection */ while (prim_addr < prim_addr2) { - kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == p_type); + kernel_assert((kernel_tex_fetch(__prim_type, prim_addr) & PRIMITIVE_ALL) == + (type & PRIMITIVE_ALL)); bool hit; /* todo: specialized intersect functions which don't fill in * isect unless needed and check SD_HAS_TRANSPARENT_SHADOW? * might give a few % performance improvement */ + Intersection isect ccl_optional_struct_init; - switch (p_type) { + switch (type & PRIMITIVE_ALL) { case PRIMITIVE_TRIANGLE: { - hit = triangle_intersect(kg, isect_array, P, dir, visibility, object, prim_addr); + hit = triangle_intersect( + kg, &isect, P, dir, t_max_current, visibility, object, prim_addr); break; } #if BVH_FEATURE(BVH_MOTION) case PRIMITIVE_MOTION_TRIANGLE: { hit = motion_triangle_intersect( - kg, isect_array, P, dir, ray->time, visibility, object, prim_addr); + kg, &isect, P, dir, t_max_current, ray->time, visibility, object, prim_addr); break; } #endif @@ -162,9 +173,29 @@ ccl_device_inline case PRIMITIVE_MOTION_CURVE_THICK: case PRIMITIVE_CURVE_RIBBON: case PRIMITIVE_MOTION_CURVE_RIBBON: { - const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); - hit = curve_intersect( - kg, isect_array, P, dir, visibility, object, prim_addr, ray->time, curve_type); + if ((type & PRIMITIVE_ALL_MOTION) && kernel_data.bvh.use_bvh_steps) { + const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr); + if (ray->time < prim_time.x || ray->time > prim_time.y) { + hit = false; + break; + } + } + + const int curve_object = (object == OBJECT_NONE) ? + kernel_tex_fetch(__prim_object, prim_addr) : + object; + const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); + const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr); + hit = curve_intersect(kg, + &isect, + P, + dir, + t_max_current, + curve_object, + curve_prim, + ray->time, + curve_type); + break; } #endif @@ -176,27 +207,70 @@ ccl_device_inline /* shadow ray early termination */ if (hit) { - /* detect if this surface has a shader with transparent shadows */ + /* Convert intersection distance to world space. */ + isect.t /= t_world_to_instance; + /* detect if this surface has a shader with transparent shadows */ /* todo: optimize so primitive visibility flag indicates if * the primitive has a transparent shadow shader? */ - const int flags = intersection_get_shader_flags(kg, isect_array); + const int flags = intersection_get_shader_flags(kg, isect.prim, isect.type); - /* if no transparent shadows, all light is blocked */ - if (!(flags & SD_HAS_TRANSPARENT_SHADOW)) { - return true; - } - /* if maximum number of hits reached, block all light */ - else if (*num_hits == max_hits) { + if (!(flags & SD_HAS_TRANSPARENT_SHADOW) || num_hits >= max_hits) { + /* If no transparent shadows, all light is blocked and we can + * stop immediately. */ return true; } - /* move on to next entry in intersections array */ - isect_array++; - (*num_hits)++; - num_hits_in_instance++; + num_hits++; - isect_array->t = isect_t; + bool record_intersection = true; + + /* Always use baked shadow transparency for curves. */ + if (isect.type & PRIMITIVE_ALL_CURVE) { + *throughput *= intersection_curve_shadow_transparency( + kg, isect.object, isect.prim, isect.u); + + if (*throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { + return true; + } + else { + record_intersection = false; + } + } + + if (record_intersection) { + /* Increase the number of hits, possibly beyond max_hits, we will + * simply not record those and only keep the max_hits closest. */ + uint record_index = (*num_recorded_hits)++; + + const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + if (record_index >= max_record_hits - 1) { + /* If maximum number of hits reached, find the intersection with + * the largest distance to potentially replace when another hit + * is found. */ + const int num_recorded_hits = min(max_record_hits, record_index); + float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); + int max_recorded_hit = 0; + + for (int i = 1; i < num_recorded_hits; i++) { + const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); + if (isect_t > max_recorded_t) { + max_recorded_t = isect_t; + max_recorded_hit = i; + } + } + + if (record_index >= max_record_hits) { + record_index = max_recorded_hit; + } + + /* Limit the ray distance and stop counting hits beyond this. */ + t_max_world = max(max_recorded_t, isect.t); + t_max_current = t_max_world * t_world_to_instance; + } + + integrator_state_write_shadow_isect(state, &isect, record_index); + } } prim_addr++; @@ -207,13 +281,14 @@ ccl_device_inline object = kernel_tex_fetch(__prim_object, -prim_addr - 1); #if BVH_FEATURE(BVH_MOTION) - isect_t = bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm); + t_world_to_instance = bvh_instance_motion_push( + kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t); + t_world_to_instance = bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif - num_hits_in_instance = 0; - isect_array->t = isect_t; + /* Convert intersection to object space. */ + t_max_current *= t_world_to_instance; ++stack_ptr; kernel_assert(stack_ptr < BVH_STACK_SIZE); @@ -228,32 +303,17 @@ ccl_device_inline kernel_assert(object != OBJECT_NONE); /* Instance pop. */ - if (num_hits_in_instance) { - float t_fac; - #if BVH_FEATURE(BVH_MOTION) - bvh_instance_motion_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac, &ob_itfm); + bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm); #else - bvh_instance_pop_factor(kg, object, ray, &P, &dir, &idir, &t_fac); + bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX); #endif - /* scale isect->t to adjust for instancing */ - for (int i = 0; i < num_hits_in_instance; i++) { - (isect_array - i - 1)->t *= t_fac; - } - } - else { -#if BVH_FEATURE(BVH_MOTION) - bvh_instance_motion_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX, &ob_itfm); -#else - bvh_instance_pop(kg, object, ray, &P, &dir, &idir, FLT_MAX); -#endif - } - - isect_t = tmax; - isect_array->t = isect_t; + /* Restore world space ray length. */ + t_max_current = t_max_world; object = OBJECT_NONE; + t_world_to_instance = 1.0f; node_addr = traversal_stack[stack_ptr]; --stack_ptr; } @@ -262,14 +322,16 @@ ccl_device_inline return false; } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, - const Ray *ray, - Intersection *isect_array, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, + ccl_private const Ray *ray, + IntegratorShadowState state, const uint visibility, const uint max_hits, - uint *num_hits) + ccl_private uint *num_recorded_hits, + ccl_private float *throughput) { - return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect_array, visibility, max_hits, num_hits); + return BVH_FUNCTION_FULL_NAME(BVH)( + kg, ray, state, visibility, max_hits, num_recorded_hits, throughput); } #undef BVH_FUNCTION_NAME diff --git a/intern/cycles/kernel/bvh/bvh_traversal.h b/intern/cycles/kernel/bvh/traversal.h similarity index 81% rename from intern/cycles/kernel/bvh/bvh_traversal.h rename to intern/cycles/kernel/bvh/traversal.h index 89250a8d60a..1c17ebf767f 100644 --- a/intern/cycles/kernel/bvh/bvh_traversal.h +++ b/intern/cycles/kernel/bvh/traversal.h @@ -31,9 +31,9 @@ * BVH_MOTION: motion blur rendering */ -ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { /* todo: @@ -136,7 +136,8 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, case PRIMITIVE_TRIANGLE: { for (; prim_addr < prim_addr2; prim_addr++) { kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); - if (triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr)) { + if (triangle_intersect( + kg, isect, P, dir, isect->t, visibility, object, prim_addr)) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; @@ -149,7 +150,7 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, for (; prim_addr < prim_addr2; prim_addr++) { kernel_assert(kernel_tex_fetch(__prim_type, prim_addr) == type); if (motion_triangle_intersect( - kg, isect, P, dir, ray->time, visibility, object, prim_addr)) { + kg, isect, P, dir, isect->t, ray->time, visibility, object, prim_addr)) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) return true; @@ -164,10 +165,20 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, case PRIMITIVE_CURVE_RIBBON: case PRIMITIVE_MOTION_CURVE_RIBBON: { for (; prim_addr < prim_addr2; prim_addr++) { - const uint curve_type = kernel_tex_fetch(__prim_type, prim_addr); - kernel_assert((curve_type & PRIMITIVE_ALL) == (type & PRIMITIVE_ALL)); + if ((type & PRIMITIVE_ALL_MOTION) && kernel_data.bvh.use_bvh_steps) { + const float2 prim_time = kernel_tex_fetch(__prim_time, prim_addr); + if (ray->time < prim_time.x || ray->time > prim_time.y) { + continue; + } + } + + const int curve_object = (object == OBJECT_NONE) ? + kernel_tex_fetch(__prim_object, prim_addr) : + object; + const int curve_prim = kernel_tex_fetch(__prim_index, prim_addr); + const int curve_type = kernel_tex_fetch(__prim_type, prim_addr); const bool hit = curve_intersect( - kg, isect, P, dir, visibility, object, prim_addr, ray->time, curve_type); + kg, isect, P, dir, isect->t, curve_object, curve_prim, ray->time, curve_type); if (hit) { /* shadow ray early termination */ if (visibility & PATH_RAY_SHADOW_OPAQUE) @@ -184,10 +195,9 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, object = kernel_tex_fetch(__prim_object, -prim_addr - 1); #if BVH_FEATURE(BVH_MOTION) - isect->t = bvh_instance_motion_push( - kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm); + isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t); + isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif ++stack_ptr; @@ -218,9 +228,9 @@ ccl_device_noinline bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility); diff --git a/intern/cycles/kernel/bvh/bvh_types.h b/intern/cycles/kernel/bvh/types.h similarity index 94% rename from intern/cycles/kernel/bvh/bvh_types.h rename to intern/cycles/kernel/bvh/types.h index 98e6ec25d15..6039e707fc3 100644 --- a/intern/cycles/kernel/bvh/bvh_types.h +++ b/intern/cycles/kernel/bvh/types.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __BVH_TYPES__ -#define __BVH_TYPES__ +#pragma once CCL_NAMESPACE_BEGIN @@ -43,5 +42,3 @@ CCL_NAMESPACE_BEGIN #define BVH_FEATURE(f) (((BVH_FUNCTION_FEATURES) & (f)) != 0) CCL_NAMESPACE_END - -#endif /* __BVH_TYPES__ */ diff --git a/intern/cycles/kernel/bvh/util.h b/intern/cycles/kernel/bvh/util.h new file mode 100644 index 00000000000..8686f887021 --- /dev/null +++ b/intern/cycles/kernel/bvh/util.h @@ -0,0 +1,226 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Ray offset to avoid self intersection. + * + * This function should be used to compute a modified ray start position for + * rays leaving from a surface. */ + +ccl_device_inline float3 ray_offset(float3 P, float3 Ng) +{ +#ifdef __INTERSECTION_REFINE__ + const float epsilon_f = 1e-5f; + /* ideally this should match epsilon_f, but instancing and motion blur + * precision makes it problematic */ + const float epsilon_test = 1.0f; + const int epsilon_i = 32; + + float3 res; + + /* x component */ + if (fabsf(P.x) < epsilon_test) { + res.x = P.x + Ng.x * epsilon_f; + } + else { + uint ix = __float_as_uint(P.x); + ix += ((ix ^ __float_as_uint(Ng.x)) >> 31) ? -epsilon_i : epsilon_i; + res.x = __uint_as_float(ix); + } + + /* y component */ + if (fabsf(P.y) < epsilon_test) { + res.y = P.y + Ng.y * epsilon_f; + } + else { + uint iy = __float_as_uint(P.y); + iy += ((iy ^ __float_as_uint(Ng.y)) >> 31) ? -epsilon_i : epsilon_i; + res.y = __uint_as_float(iy); + } + + /* z component */ + if (fabsf(P.z) < epsilon_test) { + res.z = P.z + Ng.z * epsilon_f; + } + else { + uint iz = __float_as_uint(P.z); + iz += ((iz ^ __float_as_uint(Ng.z)) >> 31) ? -epsilon_i : epsilon_i; + res.z = __uint_as_float(iz); + } + + return res; +#else + const float epsilon_f = 1e-4f; + return P + epsilon_f * Ng; +#endif +} + +#if defined(__KERNEL_CPU__) +ccl_device int intersections_compare(const void *a, const void *b) +{ + const Intersection *isect_a = (const Intersection *)a; + const Intersection *isect_b = (const Intersection *)b; + + if (isect_a->t < isect_b->t) + return -1; + else if (isect_a->t > isect_b->t) + return 1; + else + return 0; +} +#endif + +/* For subsurface scattering, only sorting a small amount of intersections + * so bubble sort is fine for CPU and GPU. */ +ccl_device_inline void sort_intersections_and_normals(ccl_private Intersection *hits, + ccl_private float3 *Ng, + uint num_hits) +{ + bool swapped; + do { + swapped = false; + for (int j = 0; j < num_hits - 1; ++j) { + if (hits[j].t > hits[j + 1].t) { + struct Intersection tmp_hit = hits[j]; + float3 tmp_Ng = Ng[j]; + hits[j] = hits[j + 1]; + Ng[j] = Ng[j + 1]; + hits[j + 1] = tmp_hit; + Ng[j + 1] = tmp_Ng; + swapped = true; + } + } + --num_hits; + } while (swapped); +} + +/* Utility to quickly get flags from an intersection. */ + +ccl_device_forceinline int intersection_get_shader_flags(KernelGlobals kg, + const int prim, + const int type) +{ + int shader = 0; + +#ifdef __HAIR__ + if (type & PRIMITIVE_ALL_TRIANGLE) +#endif + { + shader = kernel_tex_fetch(__tri_shader, prim); + } +#ifdef __HAIR__ + else { + shader = kernel_tex_fetch(__curves, prim).shader_id; + } +#endif + + return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; +} + +ccl_device_forceinline int intersection_get_shader_from_isect_prim(KernelGlobals kg, + const int prim, + const int isect_type) +{ + int shader = 0; + +#ifdef __HAIR__ + if (isect_type & PRIMITIVE_ALL_TRIANGLE) +#endif + { + shader = kernel_tex_fetch(__tri_shader, prim); + } +#ifdef __HAIR__ + else { + shader = kernel_tex_fetch(__curves, prim).shader_id; + } +#endif + + return shader & SHADER_MASK; +} + +ccl_device_forceinline int intersection_get_shader( + KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect) +{ + return intersection_get_shader_from_isect_prim(kg, isect->prim, isect->type); +} + +ccl_device_forceinline int intersection_get_object_flags( + KernelGlobals kg, ccl_private const Intersection *ccl_restrict isect) +{ + return kernel_tex_fetch(__object_flag, isect->object); +} + +/* TODO: find a better (faster) solution for this. Maybe store offset per object for + * attributes needed in intersection? */ +ccl_device_inline int intersection_find_attribute(KernelGlobals kg, + const int object, + const uint id) +{ + uint attr_offset = kernel_tex_fetch(__objects, object).attribute_map_offset; + uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); + + while (attr_map.x != id) { + if (UNLIKELY(attr_map.x == ATTR_STD_NONE)) { + if (UNLIKELY(attr_map.y == 0)) { + return (int)ATTR_STD_NOT_FOUND; + } + else { + /* Chain jump to a different part of the table. */ + attr_offset = attr_map.z; + } + } + else { + attr_offset += ATTR_PRIM_TYPES; + } + attr_map = kernel_tex_fetch(__attributes_map, attr_offset); + } + + /* return result */ + return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; +} + +/* Transparent Shadows */ + +/* Cut-off value to stop transparent shadow tracing when practically opaque. */ +#define CURVE_SHADOW_TRANSPARENCY_CUTOFF 0.001f + +ccl_device_inline float intersection_curve_shadow_transparency(KernelGlobals kg, + const int object, + const int prim, + const float u) +{ + /* Find attribute. */ + const int offset = intersection_find_attribute(kg, object, ATTR_STD_SHADOW_TRANSPARENCY); + if (offset == ATTR_STD_NOT_FOUND) { + /* If no shadow transparency attribute, assume opaque. */ + return 0.0f; + } + + /* Interpolate transparency between curve keys. */ + const KernelCurve kcurve = kernel_tex_fetch(__curves, prim); + const int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(kcurve.type); + const int k1 = k0 + 1; + + const float f0 = kernel_tex_fetch(__attributes_float, offset + k0); + const float f1 = kernel_tex_fetch(__attributes_float, offset + k1); + + return (1.0f - u) * f0 + u * f1; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/bvh/bvh_volume.h b/intern/cycles/kernel/bvh/volume.h similarity index 90% rename from intern/cycles/kernel/bvh/bvh_volume.h rename to intern/cycles/kernel/bvh/volume.h index 1f2ea47269b..fa56bd02bef 100644 --- a/intern/cycles/kernel/bvh/bvh_volume.h +++ b/intern/cycles/kernel/bvh/volume.h @@ -35,9 +35,9 @@ ccl_device #else ccl_device_inline #endif - bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, - const Ray *ray, - Intersection *isect, + bool BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { /* todo: @@ -147,7 +147,7 @@ ccl_device_inline if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; } - triangle_intersect(kg, isect, P, dir, visibility, object, prim_addr); + triangle_intersect(kg, isect, P, dir, isect->t, visibility, object, prim_addr); } break; } @@ -165,7 +165,7 @@ ccl_device_inline continue; } motion_triangle_intersect( - kg, isect, P, dir, ray->time, visibility, object, prim_addr); + kg, isect, P, dir, isect->t, ray->time, visibility, object, prim_addr); } break; } @@ -181,10 +181,9 @@ ccl_device_inline int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect->t = bvh_instance_motion_push( - kg, object, ray, &P, &dir, &idir, isect->t, &ob_itfm); + isect->t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect->t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect->t); + isect->t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif ++stack_ptr; @@ -222,9 +221,9 @@ ccl_device_inline return (isect->prim != PRIM_NONE); } -ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals *kg, - const Ray *ray, - Intersection *isect, +ccl_device_inline bool BVH_FUNCTION_NAME(KernelGlobals kg, + ccl_private const Ray *ray, + ccl_private Intersection *isect, const uint visibility) { return BVH_FUNCTION_FULL_NAME(BVH)(kg, ray, isect, visibility); diff --git a/intern/cycles/kernel/bvh/bvh_volume_all.h b/intern/cycles/kernel/bvh/volume_all.h similarity index 93% rename from intern/cycles/kernel/bvh/bvh_volume_all.h rename to intern/cycles/kernel/bvh/volume_all.h index a8664cc4331..1d7d942e736 100644 --- a/intern/cycles/kernel/bvh/bvh_volume_all.h +++ b/intern/cycles/kernel/bvh/volume_all.h @@ -35,8 +35,8 @@ ccl_device #else ccl_device_inline #endif - uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals *kg, - const Ray *ray, + uint BVH_FUNCTION_FULL_NAME(BVH)(KernelGlobals kg, + ccl_private const Ray *ray, Intersection *isect_array, const uint max_hits, const uint visibility) @@ -150,7 +150,8 @@ ccl_device_inline if ((object_flag & SD_OBJECT_HAS_VOLUME) == 0) { continue; } - hit = triangle_intersect(kg, isect_array, P, dir, visibility, object, prim_addr); + hit = triangle_intersect( + kg, isect_array, P, dir, isect_t, visibility, object, prim_addr); if (hit) { /* Move on to next entry in intersections array. */ isect_array++; @@ -190,7 +191,7 @@ ccl_device_inline continue; } hit = motion_triangle_intersect( - kg, isect_array, P, dir, ray->time, visibility, object, prim_addr); + kg, isect_array, P, dir, isect_t, ray->time, visibility, object, prim_addr); if (hit) { /* Move on to next entry in intersections array. */ isect_array++; @@ -228,10 +229,9 @@ ccl_device_inline int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_HAS_VOLUME) { #if BVH_FEATURE(BVH_MOTION) - isect_t = bvh_instance_motion_push( - kg, object, ray, &P, &dir, &idir, isect_t, &ob_itfm); + isect_t *= bvh_instance_motion_push(kg, object, ray, &P, &dir, &idir, &ob_itfm); #else - isect_t = bvh_instance_push(kg, object, ray, &P, &dir, &idir, isect_t); + isect_t *= bvh_instance_push(kg, object, ray, &P, &dir, &idir); #endif num_hits_in_instance = 0; @@ -289,8 +289,8 @@ ccl_device_inline return num_hits; } -ccl_device_inline uint BVH_FUNCTION_NAME(KernelGlobals *kg, - const Ray *ray, +ccl_device_inline uint BVH_FUNCTION_NAME(KernelGlobals kg, + ccl_private const Ray *ray, Intersection *isect_array, const uint max_hits, const uint visibility) diff --git a/intern/cycles/kernel/kernel_camera.h b/intern/cycles/kernel/camera/camera.h similarity index 87% rename from intern/cycles/kernel/kernel_camera.h rename to intern/cycles/kernel/camera/camera.h index 1bfac37158d..4f3931583de 100644 --- a/intern/cycles/kernel/kernel_camera.h +++ b/intern/cycles/kernel/camera/camera.h @@ -14,6 +14,13 @@ * limitations under the License. */ +#pragma once + +#include "kernel/camera/projection.h" +#include "kernel/sample/mapping.h" +#include "kernel/util/differential.h" +#include "kernel/util/lookup_table.h" + CCL_NAMESPACE_BEGIN /* Perspective Camera */ @@ -39,12 +46,12 @@ ccl_device float2 camera_sample_aperture(ccl_constant KernelCamera *cam, float u return bokeh; } -ccl_device void camera_sample_perspective(KernelGlobals *kg, +ccl_device void camera_sample_perspective(KernelGlobals kg, float raster_x, float raster_y, float lens_u, float lens_v, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { /* create ray form raster position */ ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; @@ -113,10 +120,14 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, #ifdef __RAY_DIFFERENTIALS__ float3 Dcenter = transform_direction(&cameratoworld, Pcamera); + float3 Dcenter_normalized = normalize(Dcenter); - ray->dP = differential3_zero(); - ray->dD.dx = normalize(Dcenter + float4_to_float3(kernel_data.cam.dx)) - normalize(Dcenter); - ray->dD.dy = normalize(Dcenter + float4_to_float3(kernel_data.cam.dy)) - normalize(Dcenter); + /* TODO: can this be optimized to give compact differentials directly? */ + ray->dP = differential_zero_compact(); + differential3 dD; + dD.dx = normalize(Dcenter + float4_to_float3(kernel_data.cam.dx)) - Dcenter_normalized; + dD.dy = normalize(Dcenter + float4_to_float3(kernel_data.cam.dy)) - Dcenter_normalized; + ray->dD = differential_make_compact(dD); #endif } else { @@ -143,8 +154,10 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, Dx = normalize(transform_direction(&cameratoworld, Dx)); spherical_stereo_transform(&kernel_data.cam, &Px, &Dx); - ray->dP.dx = Px - Pcenter; - ray->dD.dx = Dx - Dcenter; + differential3 dP, dD; + + dP.dx = Px - Pcenter; + dD.dx = Dx - Dcenter; float3 Py = Pnostereo; float3 Dy = transform_perspective(&rastertocamera, @@ -152,8 +165,10 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, Dy = normalize(transform_direction(&cameratoworld, Dy)); spherical_stereo_transform(&kernel_data.cam, &Py, &Dy); - ray->dP.dy = Py - Pcenter; - ray->dD.dy = Dy - Dcenter; + dP.dy = Py - Pcenter; + dD.dy = Dy - Dcenter; + ray->dD = differential_make_compact(dD); + ray->dP = differential_make_compact(dP); #endif } @@ -162,8 +177,7 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, float z_inv = 1.0f / normalize(Pcamera).z; float nearclip = kernel_data.cam.nearclip * z_inv; ray->P += nearclip * ray->D; - ray->dP.dx += nearclip * ray->dD.dx; - ray->dP.dy += nearclip * ray->dD.dy; + ray->dP += nearclip * ray->dD; ray->t = kernel_data.cam.cliplength * z_inv; #else ray->t = FLT_MAX; @@ -171,12 +185,12 @@ ccl_device void camera_sample_perspective(KernelGlobals *kg, } /* Orthographic Camera */ -ccl_device void camera_sample_orthographic(KernelGlobals *kg, +ccl_device void camera_sample_orthographic(KernelGlobals kg, float raster_x, float raster_y, float lens_u, float lens_v, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { /* create ray form raster position */ ProjectionTransform rastertocamera = kernel_data.cam.rastertocamera; @@ -220,10 +234,12 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, #ifdef __RAY_DIFFERENTIALS__ /* ray differential */ - ray->dP.dx = float4_to_float3(kernel_data.cam.dx); - ray->dP.dy = float4_to_float3(kernel_data.cam.dy); + differential3 dP; + dP.dx = float4_to_float3(kernel_data.cam.dx); + dP.dy = float4_to_float3(kernel_data.cam.dx); - ray->dD = differential3_zero(); + ray->dP = differential_make_compact(dP); + ray->dD = differential_zero_compact(); #endif #ifdef __CAMERA_CLIPPING__ @@ -238,13 +254,13 @@ ccl_device void camera_sample_orthographic(KernelGlobals *kg, ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, #ifdef __CAMERA_MOTION__ - const ccl_global DecomposedTransform *cam_motion, + ccl_global const DecomposedTransform *cam_motion, #endif float raster_x, float raster_y, float lens_u, float lens_v, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { ProjectionTransform rastertocamera = cam->rastertocamera; float3 Pcamera = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y, 0.0f)); @@ -290,15 +306,15 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, } #endif - P = transform_point(&cameratoworld, P); - D = normalize(transform_direction(&cameratoworld, D)); - /* Stereo transform */ bool use_stereo = cam->interocular_offset != 0.0f; if (use_stereo) { spherical_stereo_transform(cam, &P, &D); } + P = transform_point(&cameratoworld, P); + D = normalize(transform_direction(&cameratoworld, D)); + ray->P = P; ray->D = D; @@ -309,41 +325,43 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, * and simply take their differences. */ float3 Pcenter = Pcamera; float3 Dcenter = panorama_to_direction(cam, Pcenter.x, Pcenter.y); - Pcenter = transform_point(&cameratoworld, Pcenter); - Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); if (use_stereo) { spherical_stereo_transform(cam, &Pcenter, &Dcenter); } + Pcenter = transform_point(&cameratoworld, Pcenter); + Dcenter = normalize(transform_direction(&cameratoworld, Dcenter)); float3 Px = transform_perspective(&rastertocamera, make_float3(raster_x + 1.0f, raster_y, 0.0f)); float3 Dx = panorama_to_direction(cam, Px.x, Px.y); - Px = transform_point(&cameratoworld, Px); - Dx = normalize(transform_direction(&cameratoworld, Dx)); if (use_stereo) { spherical_stereo_transform(cam, &Px, &Dx); } + Px = transform_point(&cameratoworld, Px); + Dx = normalize(transform_direction(&cameratoworld, Dx)); - ray->dP.dx = Px - Pcenter; - ray->dD.dx = Dx - Dcenter; + differential3 dP, dD; + dP.dx = Px - Pcenter; + dD.dx = Dx - Dcenter; float3 Py = transform_perspective(&rastertocamera, make_float3(raster_x, raster_y + 1.0f, 0.0f)); float3 Dy = panorama_to_direction(cam, Py.x, Py.y); - Py = transform_point(&cameratoworld, Py); - Dy = normalize(transform_direction(&cameratoworld, Dy)); if (use_stereo) { spherical_stereo_transform(cam, &Py, &Dy); } + Py = transform_point(&cameratoworld, Py); + Dy = normalize(transform_direction(&cameratoworld, Dy)); - ray->dP.dy = Py - Pcenter; - ray->dD.dy = Dy - Dcenter; + dP.dy = Py - Pcenter; + dD.dy = Dy - Dcenter; + ray->dD = differential_make_compact(dD); + ray->dP = differential_make_compact(dP); #endif #ifdef __CAMERA_CLIPPING__ /* clipping */ float nearclip = cam->nearclip; ray->P += nearclip * ray->D; - ray->dP.dx += nearclip * ray->dD.dx; - ray->dP.dy += nearclip * ray->dD.dy; + ray->dP += nearclip * ray->dD; ray->t = cam->cliplength; #else ray->t = FLT_MAX; @@ -352,7 +370,7 @@ ccl_device_inline void camera_sample_panorama(ccl_constant KernelCamera *cam, /* Common */ -ccl_device_inline void camera_sample(KernelGlobals *kg, +ccl_device_inline void camera_sample(KernelGlobals kg, int x, int y, float filter_u, @@ -360,7 +378,7 @@ ccl_device_inline void camera_sample(KernelGlobals *kg, float lens_u, float lens_v, float time, - ccl_addr_space Ray *ray) + ccl_private Ray *ray) { /* pixel filter */ int filter_table_offset = kernel_data.film.filter_table_offset; @@ -416,7 +434,7 @@ ccl_device_inline void camera_sample(KernelGlobals *kg, } else { #ifdef __CAMERA_MOTION__ - const ccl_global DecomposedTransform *cam_motion = kernel_tex_array(__camera_motion); + ccl_global const DecomposedTransform *cam_motion = kernel_tex_array(__camera_motion); camera_sample_panorama(&kernel_data.cam, cam_motion, raster_x, raster_y, lens_u, lens_v, ray); #else camera_sample_panorama(&kernel_data.cam, raster_x, raster_y, lens_u, lens_v, ray); @@ -426,13 +444,13 @@ ccl_device_inline void camera_sample(KernelGlobals *kg, /* Utilities */ -ccl_device_inline float3 camera_position(KernelGlobals *kg) +ccl_device_inline float3 camera_position(KernelGlobals kg) { Transform cameratoworld = kernel_data.cam.cameratoworld; return make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); } -ccl_device_inline float camera_distance(KernelGlobals *kg, float3 P) +ccl_device_inline float camera_distance(KernelGlobals kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; float3 camP = make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w); @@ -446,7 +464,7 @@ ccl_device_inline float camera_distance(KernelGlobals *kg, float3 P) } } -ccl_device_inline float camera_z_depth(KernelGlobals *kg, float3 P) +ccl_device_inline float camera_z_depth(KernelGlobals kg, float3 P) { if (kernel_data.cam.type != CAMERA_PANORAMA) { Transform worldtocamera = kernel_data.cam.worldtocamera; @@ -459,7 +477,7 @@ ccl_device_inline float camera_z_depth(KernelGlobals *kg, float3 P) } } -ccl_device_inline float3 camera_direction_from_point(KernelGlobals *kg, float3 P) +ccl_device_inline float3 camera_direction_from_point(KernelGlobals kg, float3 P) { Transform cameratoworld = kernel_data.cam.cameratoworld; @@ -473,7 +491,9 @@ ccl_device_inline float3 camera_direction_from_point(KernelGlobals *kg, float3 P } } -ccl_device_inline float3 camera_world_to_ndc(KernelGlobals *kg, ShaderData *sd, float3 P) +ccl_device_inline float3 camera_world_to_ndc(KernelGlobals kg, + ccl_private ShaderData *sd, + float3 P) { if (kernel_data.cam.type != CAMERA_PANORAMA) { /* perspective / ortho */ diff --git a/intern/cycles/kernel/kernel_projection.h b/intern/cycles/kernel/camera/projection.h similarity index 97% rename from intern/cycles/kernel/kernel_projection.h rename to intern/cycles/kernel/camera/projection.h index c33d7150b5c..0aea82fa812 100644 --- a/intern/cycles/kernel/kernel_projection.h +++ b/intern/cycles/kernel/camera/projection.h @@ -30,8 +30,7 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __KERNEL_PROJECTION_CL__ -#define __KERNEL_PROJECTION_CL__ +#pragma once CCL_NAMESPACE_BEGIN @@ -216,8 +215,8 @@ ccl_device_inline float2 direction_to_panorama(ccl_constant KernelCamera *cam, f } ccl_device_inline void spherical_stereo_transform(ccl_constant KernelCamera *cam, - float3 *P, - float3 *D) + ccl_private float3 *P, + ccl_private float3 *D) { float interocular_offset = cam->interocular_offset; @@ -257,5 +256,3 @@ ccl_device_inline void spherical_stereo_transform(ccl_constant KernelCamera *cam } CCL_NAMESPACE_END - -#endif /* __KERNEL_PROJECTION_CL__ */ diff --git a/intern/cycles/kernel/closure/alloc.h b/intern/cycles/kernel/closure/alloc.h index 99a5a675976..211eedbddbd 100644 --- a/intern/cycles/kernel/closure/alloc.h +++ b/intern/cycles/kernel/closure/alloc.h @@ -14,16 +14,21 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device ShaderClosure *closure_alloc(ShaderData *sd, int size, ClosureType type, float3 weight) +ccl_device ccl_private ShaderClosure *closure_alloc(ccl_private ShaderData *sd, + int size, + ClosureType type, + float3 weight) { kernel_assert(size <= sizeof(ShaderClosure)); if (sd->num_closure_left == 0) return NULL; - ShaderClosure *sc = &sd->closure[sd->num_closure]; + ccl_private ShaderClosure *sc = &sd->closure[sd->num_closure]; sc->type = type; sc->weight = weight; @@ -34,7 +39,7 @@ ccl_device ShaderClosure *closure_alloc(ShaderData *sd, int size, ClosureType ty return sc; } -ccl_device ccl_addr_space void *closure_alloc_extra(ShaderData *sd, int size) +ccl_device ccl_private void *closure_alloc_extra(ccl_private ShaderData *sd, int size) { /* Allocate extra space for closure that need more parameters. We allocate * in chunks of sizeof(ShaderClosure) starting from the end of the closure @@ -52,10 +57,12 @@ ccl_device ccl_addr_space void *closure_alloc_extra(ShaderData *sd, int size) } sd->num_closure_left -= num_extra; - return (ccl_addr_space void *)(sd->closure + sd->num_closure + sd->num_closure_left); + return (ccl_private void *)(sd->closure + sd->num_closure + sd->num_closure_left); } -ccl_device_inline ShaderClosure *bsdf_alloc(ShaderData *sd, int size, float3 weight) +ccl_device_inline ccl_private ShaderClosure *bsdf_alloc(ccl_private ShaderData *sd, + int size, + float3 weight) { kernel_assert(isfinite3_safe(weight)); @@ -64,7 +71,7 @@ ccl_device_inline ShaderClosure *bsdf_alloc(ShaderData *sd, int size, float3 wei /* Use comparison this way to help dealing with non-finite weight: if the average is not finite * we will not allocate new closure. */ if (sample_weight >= CLOSURE_WEIGHT_CUTOFF) { - ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight); + ccl_private ShaderClosure *sc = closure_alloc(sd, size, CLOSURE_NONE_ID, weight); if (sc == NULL) { return NULL; } diff --git a/intern/cycles/kernel/closure/bsdf.h b/intern/cycles/kernel/closure/bsdf.h index 6f2f2ebb202..28c889f2841 100644 --- a/intern/cycles/kernel/closure/bsdf.h +++ b/intern/cycles/kernel/closure/bsdf.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + // clang-format off #include "kernel/closure/bsdf_ashikhmin_velvet.h" #include "kernel/closure/bsdf_diffuse.h" @@ -39,32 +41,32 @@ CCL_NAMESPACE_BEGIN /* Returns the square of the roughness of the closure if it has roughness, * 0 for singular closures and 1 otherwise. */ -ccl_device_inline float bsdf_get_specular_roughness_squared(const ShaderClosure *sc) +ccl_device_inline float bsdf_get_specular_roughness_squared(ccl_private const ShaderClosure *sc) { if (CLOSURE_IS_BSDF_SINGULAR(sc->type)) { return 0.0f; } if (CLOSURE_IS_BSDF_MICROFACET(sc->type)) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; return bsdf->alpha_x * bsdf->alpha_y; } return 1.0f; } -ccl_device_inline float bsdf_get_roughness_squared(const ShaderClosure *sc) +ccl_device_inline float bsdf_get_roughness_squared(ccl_private const ShaderClosure *sc) { /* This version includes diffuse, mainly for baking Principled BSDF * where specular and metallic zero otherwise does not bake the * specified roughness parameter. */ if (sc->type == CLOSURE_BSDF_OREN_NAYAR_ID) { - OrenNayarBsdf *bsdf = (OrenNayarBsdf *)sc; + ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)sc; return sqr(sqr(bsdf->roughness)); } if (sc->type == CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID) { - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)sc; + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)sc; return sqr(sqr(bsdf->roughness)); } @@ -109,15 +111,15 @@ ccl_device_inline float shift_cos_in(float cos_in, const float frequency_multipl return val; } -ccl_device_inline int bsdf_sample(KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, +ccl_device_inline int bsdf_sample(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc, float randu, float randv, - float3 *eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) { /* For curves use the smooth normal, particularly for ribbons the geometric * normal gives too much darkening otherwise. */ @@ -126,7 +128,6 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg, switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: label = bsdf_diffuse_sample(sc, Ng, sd->I, @@ -399,7 +400,6 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg, break; # ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: label = bsdf_principled_diffuse_sample(sc, Ng, sd->I, @@ -428,21 +428,6 @@ ccl_device_inline int bsdf_sample(KernelGlobals *kg, pdf); break; # endif /* __PRINCIPLED__ */ -#endif -#ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - label = volume_henyey_greenstein_sample(sc, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); - break; #endif default: label = LABEL_NONE; @@ -482,18 +467,18 @@ ccl_device ccl_device_inline #endif float3 - bsdf_eval(KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, + bsdf_eval(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc, const float3 omega_in, - float *pdf) + const bool is_transmission, + ccl_private float *pdf) { - float3 eval; + float3 eval = zero_float3(); - if (dot(sd->N, omega_in) >= 0.0f) { + if (!is_transmission) { switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: eval = bsdf_diffuse_eval_reflect(sc, sd->I, omega_in, pdf); break; #ifdef __SVM__ @@ -562,21 +547,14 @@ ccl_device_inline break; # ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: eval = bsdf_principled_diffuse_eval_reflect(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: eval = bsdf_principled_sheen_eval_reflect(sc, sd->I, omega_in, pdf); break; # endif /* __PRINCIPLED__ */ -#endif -#ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - eval = volume_henyey_greenstein_eval_phase(sc, sd->I, omega_in, pdf); - break; #endif default: - eval = make_float3(0.0f, 0.0f, 0.0f); break; } if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { @@ -594,7 +572,6 @@ ccl_device_inline else { switch (sc->type) { case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: eval = bsdf_diffuse_eval_transmit(sc, sd->I, omega_in, pdf); break; #ifdef __SVM__ @@ -655,21 +632,14 @@ ccl_device_inline break; # ifdef __PRINCIPLED__ case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: eval = bsdf_principled_diffuse_eval_transmit(sc, sd->I, omega_in, pdf); break; case CLOSURE_BSDF_PRINCIPLED_SHEEN_ID: eval = bsdf_principled_sheen_eval_transmit(sc, sd->I, omega_in, pdf); break; # endif /* __PRINCIPLED__ */ -#endif -#ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - eval = volume_henyey_greenstein_eval_phase(sc, sd->I, omega_in, pdf); - break; #endif default: - eval = make_float3(0.0f, 0.0f, 0.0f); break; } if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { @@ -682,9 +652,9 @@ ccl_device_inline return eval; } -ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness) +ccl_device void bsdf_blur(KernelGlobals kg, ccl_private ShaderClosure *sc, float roughness) { - /* ToDo: do we want to blur volume closures? */ + /* TODO: do we want to blur volume closures? */ #ifdef __SVM__ switch (sc->type) { case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: @@ -715,55 +685,4 @@ ccl_device void bsdf_blur(KernelGlobals *kg, ShaderClosure *sc, float roughness) #endif } -ccl_device bool bsdf_merge(ShaderClosure *a, ShaderClosure *b) -{ -#ifdef __SVM__ - switch (a->type) { - case CLOSURE_BSDF_TRANSPARENT_ID: - return true; - case CLOSURE_BSDF_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_ID: - case CLOSURE_BSDF_TRANSLUCENT_ID: - return bsdf_diffuse_merge(a, b); - case CLOSURE_BSDF_OREN_NAYAR_ID: - return bsdf_oren_nayar_merge(a, b); - case CLOSURE_BSDF_REFLECTION_ID: - case CLOSURE_BSDF_REFRACTION_ID: - case CLOSURE_BSDF_MICROFACET_GGX_ID: - case CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID: - case CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID: - case CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_FRESNEL_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID: - case CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID: - case CLOSURE_BSDF_MICROFACET_BECKMANN_ID: - case CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID: - case CLOSURE_BSDF_ASHIKHMIN_SHIRLEY_ID: - return bsdf_microfacet_merge(a, b); - case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: - return bsdf_ashikhmin_velvet_merge(a, b); - case CLOSURE_BSDF_DIFFUSE_TOON_ID: - case CLOSURE_BSDF_GLOSSY_TOON_ID: - return bsdf_toon_merge(a, b); - case CLOSURE_BSDF_HAIR_REFLECTION_ID: - case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: - return bsdf_hair_merge(a, b); -# ifdef __PRINCIPLED__ - case CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID: - case CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID: - return bsdf_principled_diffuse_merge(a, b); -# endif -# ifdef __VOLUME__ - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - return volume_henyey_greenstein_merge(a, b); -# endif - default: - return false; - } -#else - return false; -#endif -} - CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h index 9814a7cf5c9..6cd8739ce39 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_shirley.h @@ -14,24 +14,23 @@ * limitations under the License. */ -#ifndef __BSDF_ASHIKHMIN_SHIRLEY_H__ -#define __BSDF_ASHIKHMIN_SHIRLEY_H__ - /* -ASHIKHMIN SHIRLEY BSDF + * ASHIKHMIN SHIRLEY BSDF + * + * Implementation of + * Michael Ashikhmin and Peter Shirley: "An Anisotropic Phong BRDF Model" (2000) + * + * The Fresnel factor is missing to get a separable bsdf (intensity*color), as is + * the case with all other microfacet-based BSDF implementations in Cycles. + * + * Other than that, the implementation directly follows the paper. + */ -Implementation of -Michael Ashikhmin and Peter Shirley: "An Anisotropic Phong BRDF Model" (2000) - -The Fresnel factor is missing to get a separable bsdf (intensity*color), as is -the case with all other microfacet-based BSDF implementations in Cycles. - -Other than that, the implementation directly follows the paper. -*/ +#pragma once CCL_NAMESPACE_BEGIN -ccl_device int bsdf_ashikhmin_shirley_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_ashikhmin_shirley_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f); @@ -40,9 +39,9 @@ ccl_device int bsdf_ashikhmin_shirley_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_ashikhmin_shirley_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_ashikhmin_shirley_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); @@ -53,12 +52,13 @@ ccl_device_inline float bsdf_ashikhmin_shirley_roughness_to_exponent(float rough return 2.0f / (roughness * roughness) - 2.0f; } -ccl_device_forceinline float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - float *pdf) +ccl_device_forceinline float3 +bsdf_ashikhmin_shirley_eval_reflect(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 N = bsdf->N; float NdotI = dot(N, I); /* in Cycles/OSL convention I is omega_out */ @@ -120,16 +120,20 @@ ccl_device_forceinline float3 bsdf_ashikhmin_shirley_eval_reflect(const ShaderCl return make_float3(out, out, out); } -ccl_device float3 bsdf_ashikhmin_shirley_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_ashikhmin_shirley_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant( - float n_x, float n_y, float randu, float randv, float *phi, float *cos_theta) +ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant(float n_x, + float n_y, + float randu, + float randv, + ccl_private float *phi, + ccl_private float *cos_theta) { *phi = atanf(sqrtf((n_x + 1.0f) / (n_y + 1.0f)) * tanf(M_PI_2_F * randu)); float cos_phi = cosf(*phi); @@ -137,20 +141,20 @@ ccl_device_inline void bsdf_ashikhmin_shirley_sample_first_quadrant( *cos_theta = powf(randv, 1.0f / (n_x * cos_phi * cos_phi + n_y * sin_phi * sin_phi + 1.0f)); } -ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, +ccl_device int bsdf_ashikhmin_shirley_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 N = bsdf->N; int label = LABEL_REFLECT | LABEL_GLOSSY; @@ -240,5 +244,3 @@ ccl_device int bsdf_ashikhmin_shirley_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_ASHIKHMIN_SHIRLEY_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h index 3d3f20edab3..b2a9c9555c3 100644 --- a/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h +++ b/intern/cycles/kernel/closure/bsdf_ashikhmin_velvet.h @@ -30,12 +30,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_ASHIKHMIN_VELVET_H__ -#define __BSDF_ASHIKHMIN_VELVET_H__ +#pragma once + +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct VelvetBsdf { +typedef struct VelvetBsdf { SHADER_CLOSURE_BASE; float sigma; @@ -44,7 +45,7 @@ typedef ccl_addr_space struct VelvetBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(VelvetBsdf), "VelvetBsdf is too large!"); -ccl_device int bsdf_ashikhmin_velvet_setup(VelvetBsdf *bsdf) +ccl_device int bsdf_ashikhmin_velvet_setup(ccl_private VelvetBsdf *bsdf) { float sigma = fmaxf(bsdf->sigma, 0.01f); bsdf->invsigma2 = 1.0f / (sigma * sigma); @@ -54,20 +55,12 @@ ccl_device int bsdf_ashikhmin_velvet_setup(VelvetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_ashikhmin_velvet_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const VelvetBsdf *bsdf_a = (const VelvetBsdf *)a; - const VelvetBsdf *bsdf_b = (const VelvetBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->sigma == bsdf_b->sigma); -} - -ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const VelvetBsdf *bsdf = (const VelvetBsdf *)sc; + ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc; float m_invsigma2 = bsdf->invsigma2; float3 N = bsdf->N; @@ -104,28 +97,28 @@ ccl_device float3 bsdf_ashikhmin_velvet_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_ashikhmin_velvet_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_ashikhmin_velvet_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_ashikhmin_velvet_sample(const ShaderClosure *sc, +ccl_device int bsdf_ashikhmin_velvet_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const VelvetBsdf *bsdf = (const VelvetBsdf *)sc; + ccl_private const VelvetBsdf *bsdf = (ccl_private const VelvetBsdf *)sc; float m_invsigma2 = bsdf->invsigma2; float3 N = bsdf->N; @@ -175,5 +168,3 @@ ccl_device int bsdf_ashikhmin_velvet_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_ASHIKHMIN_VELVET_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_diffuse.h b/intern/cycles/kernel/closure/bsdf_diffuse.h index ea604ed0311..3139cb612fa 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse.h @@ -30,12 +30,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_DIFFUSE_H__ -#define __BSDF_DIFFUSE_H__ +#pragma once + +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct DiffuseBsdf { +typedef struct DiffuseBsdf { SHADER_CLOSURE_BASE; } DiffuseBsdf; @@ -43,26 +44,18 @@ static_assert(sizeof(ShaderClosure) >= sizeof(DiffuseBsdf), "DiffuseBsdf is too /* DIFFUSE */ -ccl_device int bsdf_diffuse_setup(DiffuseBsdf *bsdf) +ccl_device int bsdf_diffuse_setup(ccl_private DiffuseBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_DIFFUSE_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_diffuse_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const DiffuseBsdf *bsdf_a = (const DiffuseBsdf *)a; - const DiffuseBsdf *bsdf_b = (const DiffuseBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)); -} - -ccl_device float3 bsdf_diffuse_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; float cos_pi = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; @@ -70,28 +63,28 @@ ccl_device float3 bsdf_diffuse_eval_reflect(const ShaderClosure *sc, return make_float3(cos_pi, cos_pi, cos_pi); } -ccl_device float3 bsdf_diffuse_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_diffuse_sample(const ShaderClosure *sc, +ccl_device int bsdf_diffuse_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; // distribution over the hemisphere @@ -113,26 +106,26 @@ ccl_device int bsdf_diffuse_sample(const ShaderClosure *sc, /* TRANSLUCENT */ -ccl_device int bsdf_translucent_setup(DiffuseBsdf *bsdf) +ccl_device int bsdf_translucent_setup(ccl_private DiffuseBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_TRANSLUCENT_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_translucent_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_translucent_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_translucent_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_translucent_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; float cos_pi = fmaxf(-dot(N, omega_in), 0.0f) * M_1_PI_F; @@ -140,20 +133,20 @@ ccl_device float3 bsdf_translucent_eval_transmit(const ShaderClosure *sc, return make_float3(cos_pi, cos_pi, cos_pi); } -ccl_device int bsdf_translucent_sample(const ShaderClosure *sc, +ccl_device int bsdf_translucent_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; float3 N = bsdf->N; // we are viewing the surface from the right side - send a ray out with cosine @@ -174,5 +167,3 @@ ccl_device int bsdf_translucent_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_DIFFUSE_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h index aa62c1c7ceb..fbb82617dad 100644 --- a/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_diffuse_ramp.h @@ -30,17 +30,18 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_DIFFUSE_RAMP_H__ -#define __BSDF_DIFFUSE_RAMP_H__ +#pragma once + +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN #ifdef __OSL__ -typedef ccl_addr_space struct DiffuseRampBsdf { +typedef struct DiffuseRampBsdf { SHADER_CLOSURE_BASE; - float3 *colors; + ccl_private float3 *colors; } DiffuseRampBsdf; static_assert(sizeof(ShaderClosure) >= sizeof(DiffuseRampBsdf), "DiffuseRampBsdf is too large!"); @@ -65,14 +66,14 @@ ccl_device int bsdf_diffuse_ramp_setup(DiffuseRampBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_diffuse_ramp_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_diffuse_ramp_blur(ccl_private ShaderClosure *sc, float roughness) { } -ccl_device float3 bsdf_diffuse_ramp_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_ramp_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc; float3 N = bsdf->N; @@ -82,26 +83,26 @@ ccl_device float3 bsdf_diffuse_ramp_eval_reflect(const ShaderClosure *sc, return bsdf_diffuse_ramp_get_color(bsdf->colors, cos_pi) * M_1_PI_F; } -ccl_device float3 bsdf_diffuse_ramp_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_ramp_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc, +ccl_device int bsdf_diffuse_ramp_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { const DiffuseRampBsdf *bsdf = (const DiffuseRampBsdf *)sc; float3 N = bsdf->N; @@ -125,5 +126,3 @@ ccl_device int bsdf_diffuse_ramp_sample(const ShaderClosure *sc, #endif /* __OSL__ */ CCL_NAMESPACE_END - -#endif /* __BSDF_DIFFUSE_RAMP_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_hair.h b/intern/cycles/kernel/closure/bsdf_hair.h index 7ca9424b815..449a314a90e 100644 --- a/intern/cycles/kernel/closure/bsdf_hair.h +++ b/intern/cycles/kernel/closure/bsdf_hair.h @@ -30,12 +30,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_HAIR_H__ -#define __BSDF_HAIR_H__ +#pragma once CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct HairBsdf { +typedef struct HairBsdf { SHADER_CLOSURE_BASE; float3 T; @@ -46,7 +45,7 @@ typedef ccl_addr_space struct HairBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(HairBsdf), "HairBsdf is too large!"); -ccl_device int bsdf_hair_reflection_setup(HairBsdf *bsdf) +ccl_device int bsdf_hair_reflection_setup(ccl_private HairBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_HAIR_REFLECTION_ID; bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f); @@ -54,7 +53,7 @@ ccl_device int bsdf_hair_reflection_setup(HairBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device int bsdf_hair_transmission_setup(HairBsdf *bsdf) +ccl_device int bsdf_hair_transmission_setup(ccl_private HairBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_HAIR_TRANSMISSION_ID; bsdf->roughness1 = clamp(bsdf->roughness1, 0.001f, 1.0f); @@ -62,21 +61,12 @@ ccl_device int bsdf_hair_transmission_setup(HairBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_hair_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const HairBsdf *bsdf_a = (const HairBsdf *)a; - const HairBsdf *bsdf_b = (const HairBsdf *)b; - - return (isequal_float3(bsdf_a->T, bsdf_b->T)) && (bsdf_a->roughness1 == bsdf_b->roughness1) && - (bsdf_a->roughness2 == bsdf_b->roughness2) && (bsdf_a->offset == bsdf_b->offset); -} - -ccl_device float3 bsdf_hair_reflection_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_reflection_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; @@ -118,28 +108,28 @@ ccl_device float3 bsdf_hair_reflection_eval_reflect(const ShaderClosure *sc, return make_float3(*pdf, *pdf, *pdf); } -ccl_device float3 bsdf_hair_transmission_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_transmission_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_hair_reflection_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_reflection_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_hair_transmission_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; @@ -180,20 +170,20 @@ ccl_device float3 bsdf_hair_transmission_eval_transmit(const ShaderClosure *sc, return make_float3(*pdf, *pdf, *pdf); } -ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, +ccl_device int bsdf_hair_reflection_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; @@ -241,20 +231,20 @@ ccl_device int bsdf_hair_reflection_sample(const ShaderClosure *sc, return LABEL_REFLECT | LABEL_GLOSSY; } -ccl_device int bsdf_hair_transmission_sample(const ShaderClosure *sc, +ccl_device int bsdf_hair_transmission_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const HairBsdf *bsdf = (const HairBsdf *)sc; + ccl_private const HairBsdf *bsdf = (ccl_private const HairBsdf *)sc; float offset = bsdf->offset; float3 Tg = bsdf->T; float roughness1 = bsdf->roughness1; @@ -309,5 +299,3 @@ ccl_device int bsdf_hair_transmission_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_HAIR_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_hair_principled.h b/intern/cycles/kernel/closure/bsdf_hair_principled.h index f12661b3095..f55ea0f6a2e 100644 --- a/intern/cycles/kernel/closure/bsdf_hair_principled.h +++ b/intern/cycles/kernel/closure/bsdf_hair_principled.h @@ -14,23 +14,22 @@ * limitations under the License. */ +#pragma once + #ifdef __KERNEL_CPU__ # include #endif -#include "kernel/kernel_color.h" - -#ifndef __BSDF_HAIR_PRINCIPLED_H__ -# define __BSDF_HAIR_PRINCIPLED_H__ +#include "kernel/util/color.h" CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct PrincipledHairExtra { +typedef struct PrincipledHairExtra { /* Geometry data. */ float4 geom; } PrincipledHairExtra; -typedef ccl_addr_space struct PrincipledHairBSDF { +typedef struct PrincipledHairBSDF { SHADER_CLOSURE_BASE; /* Absorption coefficient. */ @@ -47,7 +46,7 @@ typedef ccl_addr_space struct PrincipledHairBSDF { float m0_roughness; /* Extra closure. */ - PrincipledHairExtra *extra; + ccl_private PrincipledHairExtra *extra; } PrincipledHairBSDF; static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledHairBSDF), @@ -181,14 +180,15 @@ ccl_device_inline float longitudinal_scattering( } /* Combine the three values using their luminances. */ -ccl_device_inline float4 combine_with_energy(KernelGlobals *kg, float3 c) +ccl_device_inline float4 combine_with_energy(KernelGlobals kg, float3 c) { return make_float4(c.x, c.y, c.z, linear_rgb_to_gray(kg, c)); } -# ifdef __HAIR__ +#ifdef __HAIR__ /* Set up the hair closure. */ -ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bsdf) +ccl_device int bsdf_principled_hair_setup(ccl_private ShaderData *sd, + ccl_private PrincipledHairBSDF *bsdf) { bsdf->type = CLOSURE_BSDF_HAIR_PRINCIPLED_ID; bsdf->v = clamp(bsdf->v, 0.001f, 1.0f); @@ -226,10 +226,13 @@ ccl_device int bsdf_principled_hair_setup(ShaderData *sd, PrincipledHairBSDF *bs return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -# endif /* __HAIR__ */ +#endif /* __HAIR__ */ /* Given the Fresnel term and transmittance, generate the attenuation terms for each bounce. */ -ccl_device_inline void hair_attenuation(KernelGlobals *kg, float f, float3 T, float4 *Ap) +ccl_device_inline void hair_attenuation(KernelGlobals kg, + float f, + float3 T, + ccl_private float4 *Ap) { /* Primary specular (R). */ Ap[0] = make_float4(f, f, f, f); @@ -260,7 +263,7 @@ ccl_device_inline void hair_attenuation(KernelGlobals *kg, float f, float3 T, fl ccl_device_inline void hair_alpha_angles(float sin_theta_i, float cos_theta_i, float alpha, - float *angles) + ccl_private float *angles) { float sin_1alpha = sinf(alpha); float cos_1alpha = cos_from_sin(sin_1alpha); @@ -278,15 +281,15 @@ ccl_device_inline void hair_alpha_angles(float sin_theta_i, } /* Evaluation function for our shader. */ -ccl_device float3 bsdf_principled_hair_eval(KernelGlobals *kg, - const ShaderData *sd, - const ShaderClosure *sc, +ccl_device float3 bsdf_principled_hair_eval(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderClosure *sc, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { kernel_assert(isfinite3_safe(sd->P) && isfinite_safe(sd->ray_length)); - const PrincipledHairBSDF *bsdf = (const PrincipledHairBSDF *)sc; + ccl_private const PrincipledHairBSDF *bsdf = (ccl_private const PrincipledHairBSDF *)sc; float3 Y = float4_to_float3(bsdf->extra->geom); float3 X = safe_normalize(sd->dPdu); @@ -356,18 +359,18 @@ ccl_device float3 bsdf_principled_hair_eval(KernelGlobals *kg, } /* Sampling function for the hair shader. */ -ccl_device int bsdf_principled_hair_sample(KernelGlobals *kg, - const ShaderClosure *sc, - ShaderData *sd, +ccl_device int bsdf_principled_hair_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, + ccl_private ShaderData *sd, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)sc; + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; float3 Y = float4_to_float3(bsdf->extra->geom); @@ -379,8 +382,8 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals *kg, float2 u[2]; u[0] = make_float2(randu, randv); - u[1].x = lcg_step_float_addrspace(&sd->lcg_state); - u[1].y = lcg_step_float_addrspace(&sd->lcg_state); + u[1].x = lcg_step_float(&sd->lcg_state); + u[1].y = lcg_step_float(&sd->lcg_state); float sin_theta_o = wo.x; float cos_theta_o = cos_from_sin(sin_theta_o); @@ -473,19 +476,19 @@ ccl_device int bsdf_principled_hair_sample(KernelGlobals *kg, *omega_in = X * sin_theta_i + Y * cos_theta_i * cosf(phi_i) + Z * cos_theta_i * sinf(phi_i); -# ifdef __RAY_DIFFERENTIALS__ +#ifdef __RAY_DIFFERENTIALS__ float3 N = safe_normalize(sd->I + *omega_in); *domega_in_dx = (2 * dot(N, sd->dI.dx)) * N - sd->dI.dx; *domega_in_dy = (2 * dot(N, sd->dI.dy)) * N - sd->dI.dy; -# endif +#endif return LABEL_GLOSSY | ((p == 0) ? LABEL_REFLECT : LABEL_TRANSMIT); } /* Implements Filter Glossy by capping the effective roughness. */ -ccl_device void bsdf_principled_hair_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_principled_hair_blur(ccl_private ShaderClosure *sc, float roughness) { - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)sc; + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; bsdf->v = fmaxf(roughness, bsdf->v); bsdf->s = fmaxf(roughness, bsdf->s); @@ -501,9 +504,9 @@ ccl_device_inline float bsdf_principled_hair_albedo_roughness_scale( return (((((0.245f * x) + 5.574f) * x - 10.73f) * x + 2.532f) * x - 0.215f) * x + 5.969f; } -ccl_device float3 bsdf_principled_hair_albedo(ShaderClosure *sc) +ccl_device float3 bsdf_principled_hair_albedo(ccl_private const ShaderClosure *sc) { - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)sc; + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)sc; return exp3(-sqrt(bsdf->sigma) * bsdf_principled_hair_albedo_roughness_scale(bsdf->v)); } @@ -523,5 +526,3 @@ ccl_device_inline float3 bsdf_principled_hair_sigma_from_concentration(const flo } CCL_NAMESPACE_END - -#endif /* __BSDF_HAIR_PRINCIPLED_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_microfacet.h b/intern/cycles/kernel/closure/bsdf_microfacet.h index af03bab39f7..466ba3e229e 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet.h @@ -30,22 +30,27 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_MICROFACET_H__ -#define __BSDF_MICROFACET_H__ +#pragma once + +#include "kernel/closure/bsdf_util.h" + +#include "kernel/sample/pattern.h" + +#include "kernel/util/lookup_table.h" CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct MicrofacetExtra { +typedef struct MicrofacetExtra { float3 color, cspec0; float3 fresnel_color; float clearcoat; } MicrofacetExtra; -typedef ccl_addr_space struct MicrofacetBsdf { +typedef struct MicrofacetBsdf { SHADER_CLOSURE_BASE; float alpha_x, alpha_y, ior; - MicrofacetExtra *extra; + ccl_private MicrofacetExtra *extra; float3 T; } MicrofacetBsdf; @@ -53,14 +58,14 @@ static_assert(sizeof(ShaderClosure) >= sizeof(MicrofacetBsdf), "MicrofacetBsdf i /* Beckmann and GGX microfacet importance sampling. */ -ccl_device_inline void microfacet_beckmann_sample_slopes(KernelGlobals *kg, +ccl_device_inline void microfacet_beckmann_sample_slopes(KernelGlobals kg, const float cos_theta_i, const float sin_theta_i, float randu, float randv, - float *slope_x, - float *slope_y, - float *G1i) + ccl_private float *slope_x, + ccl_private float *slope_y, + ccl_private float *G1i) { /* special case (normal incidence) */ if (cos_theta_i >= 0.99999f) { @@ -144,9 +149,9 @@ ccl_device_inline void microfacet_ggx_sample_slopes(const float cos_theta_i, const float sin_theta_i, float randu, float randv, - float *slope_x, - float *slope_y, - float *G1i) + ccl_private float *slope_x, + ccl_private float *slope_y, + ccl_private float *G1i) { /* special case (normal incidence) */ if (cos_theta_i >= 0.99999f) { @@ -193,14 +198,14 @@ ccl_device_inline void microfacet_ggx_sample_slopes(const float cos_theta_i, *slope_y = S * z * safe_sqrtf(1.0f + (*slope_x) * (*slope_x)); } -ccl_device_forceinline float3 microfacet_sample_stretched(KernelGlobals *kg, +ccl_device_forceinline float3 microfacet_sample_stretched(KernelGlobals kg, const float3 omega_i, const float alpha_x, const float alpha_y, const float randu, const float randv, bool beckmann, - float *G1i) + ccl_private float *G1i) { /* 1. stretch omega_i */ float3 omega_i_ = make_float3(alpha_x * omega_i.x, alpha_y * omega_i.y, omega_i.z); @@ -252,7 +257,9 @@ ccl_device_forceinline float3 microfacet_sample_stretched(KernelGlobals *kg, * * Else it is simply white */ -ccl_device_forceinline float3 reflection_color(const MicrofacetBsdf *bsdf, float3 L, float3 H) +ccl_device_forceinline float3 reflection_color(ccl_private const MicrofacetBsdf *bsdf, + float3 L, + float3 H) { float3 F = make_float3(1.0f, 1.0f, 1.0f); bool use_fresnel = (bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID || @@ -275,8 +282,8 @@ ccl_device_forceinline float D_GTR1(float NdotH, float alpha) return (alpha2 - 1.0f) / (M_PI_F * logf(alpha2) * t); } -ccl_device_forceinline void bsdf_microfacet_fresnel_color(const ShaderData *sd, - MicrofacetBsdf *bsdf) +ccl_device_forceinline void bsdf_microfacet_fresnel_color(ccl_private const ShaderData *sd, + ccl_private MicrofacetBsdf *bsdf) { kernel_assert(CLOSURE_IS_BSDF_MICROFACET_FRESNEL(bsdf->type)); @@ -304,12 +311,12 @@ ccl_device_forceinline void bsdf_microfacet_fresnel_color(const ShaderData *sd, * Anisotropy is only supported for reflection currently, but adding it for * transmission is just a matter of copying code from reflection if needed. */ -ccl_device int bsdf_microfacet_ggx_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_ggx_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->extra = NULL; - bsdf->alpha_x = saturate(bsdf->alpha_x); - bsdf->alpha_y = saturate(bsdf->alpha_y); + bsdf->alpha_x = saturatef(bsdf->alpha_x); + bsdf->alpha_y = saturatef(bsdf->alpha_y); bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_ID; @@ -317,19 +324,20 @@ ccl_device int bsdf_microfacet_ggx_setup(MicrofacetBsdf *bsdf) } /* Required to maintain OSL interface. */ -ccl_device int bsdf_microfacet_ggx_isotropic_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_ggx_isotropic_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_y = bsdf->alpha_x; return bsdf_microfacet_ggx_setup(bsdf); } -ccl_device int bsdf_microfacet_ggx_fresnel_setup(MicrofacetBsdf *bsdf, const ShaderData *sd) +ccl_device int bsdf_microfacet_ggx_fresnel_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { bsdf->extra->cspec0 = saturate3(bsdf->extra->cspec0); - bsdf->alpha_x = saturate(bsdf->alpha_x); - bsdf->alpha_y = saturate(bsdf->alpha_y); + bsdf->alpha_x = saturatef(bsdf->alpha_x); + bsdf->alpha_y = saturatef(bsdf->alpha_y); bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID; @@ -338,11 +346,12 @@ ccl_device int bsdf_microfacet_ggx_fresnel_setup(MicrofacetBsdf *bsdf, const Sha return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device int bsdf_microfacet_ggx_clearcoat_setup(MicrofacetBsdf *bsdf, const ShaderData *sd) +ccl_device int bsdf_microfacet_ggx_clearcoat_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { bsdf->extra->cspec0 = saturate3(bsdf->extra->cspec0); - bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_x = saturatef(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID; @@ -352,26 +361,11 @@ ccl_device int bsdf_microfacet_ggx_clearcoat_setup(MicrofacetBsdf *bsdf, const S return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_microfacet_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const MicrofacetBsdf *bsdf_a = (const MicrofacetBsdf *)a; - const MicrofacetBsdf *bsdf_b = (const MicrofacetBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->alpha_x == bsdf_b->alpha_x) && - (bsdf_a->alpha_y == bsdf_b->alpha_y) && (isequal_float3(bsdf_a->T, bsdf_b->T)) && - (bsdf_a->ior == bsdf_b->ior) && - ((bsdf_a->extra == NULL && bsdf_b->extra == NULL) || - ((bsdf_a->extra && bsdf_b->extra) && - (isequal_float3(bsdf_a->extra->color, bsdf_b->extra->color)) && - (isequal_float3(bsdf_a->extra->cspec0, bsdf_b->extra->cspec0)) && - (bsdf_a->extra->clearcoat == bsdf_b->extra->clearcoat))); -} - -ccl_device int bsdf_microfacet_ggx_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_ggx_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->extra = NULL; - bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_x = saturatef(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; bsdf->type = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; @@ -379,20 +373,20 @@ ccl_device int bsdf_microfacet_ggx_refraction_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_microfacet_ggx_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_microfacet_ggx_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); } -ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_ggx_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; @@ -500,12 +494,12 @@ ccl_device float3 bsdf_microfacet_ggx_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_ggx_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; float m_eta = bsdf->ior; @@ -558,21 +552,21 @@ ccl_device float3 bsdf_microfacet_ggx_eval_transmit(const ShaderClosure *sc, return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; @@ -787,35 +781,35 @@ ccl_device int bsdf_microfacet_ggx_sample(KernelGlobals *kg, * Microfacet Models for Refraction through Rough Surfaces * B. Walter, S. R. Marschner, H. Li, K. E. Torrance, EGSR 2007 */ -ccl_device int bsdf_microfacet_beckmann_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_beckmann_setup(ccl_private MicrofacetBsdf *bsdf) { - bsdf->alpha_x = saturate(bsdf->alpha_x); - bsdf->alpha_y = saturate(bsdf->alpha_y); + bsdf->alpha_x = saturatef(bsdf->alpha_x); + bsdf->alpha_y = saturatef(bsdf->alpha_y); bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; } /* Required to maintain OSL interface. */ -ccl_device int bsdf_microfacet_beckmann_isotropic_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_beckmann_isotropic_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_y = bsdf->alpha_x; return bsdf_microfacet_beckmann_setup(bsdf); } -ccl_device int bsdf_microfacet_beckmann_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_beckmann_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { - bsdf->alpha_x = saturate(bsdf->alpha_x); + bsdf->alpha_x = saturatef(bsdf->alpha_x); bsdf->alpha_y = bsdf->alpha_x; bsdf->type = CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device void bsdf_microfacet_beckmann_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_microfacet_beckmann_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); @@ -852,12 +846,12 @@ ccl_device_inline float bsdf_beckmann_aniso_G1( return ((2.181f * a + 3.535f) * a) / ((2.577f * a + 2.276f) * a + 1.0f); } -ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; @@ -931,12 +925,12 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; float m_eta = bsdf->ior; @@ -986,21 +980,21 @@ ccl_device float3 bsdf_microfacet_beckmann_eval_transmit(const ShaderClosure *sc return make_float3(out, out, out); } -ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float alpha_x = bsdf->alpha_x; float alpha_y = bsdf->alpha_y; bool m_refractive = bsdf->type == CLOSURE_BSDF_MICROFACET_BECKMANN_REFRACTION_ID; @@ -1175,5 +1169,3 @@ ccl_device int bsdf_microfacet_beckmann_sample(KernelGlobals *kg, } CCL_NAMESPACE_END - -#endif /* __BSDF_MICROFACET_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h index 9795c8da065..5badbe9aa80 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi.h @@ -14,6 +14,11 @@ * limitations under the License. */ +#pragma once + +#include "kernel/sample/lcg.h" +#include "kernel/sample/mapping.h" + CCL_NAMESPACE_BEGIN /* Most of the code is based on the supplemental implementations from @@ -103,7 +108,7 @@ ccl_device_forceinline float3 mf_sample_vndf(const float3 wi, /* Phase function for reflective materials. */ ccl_device_forceinline float3 mf_sample_phase_glossy(const float3 wi, - float3 *weight, + ccl_private float3 *weight, const float3 wm) { return -wi + 2.0f * wm * dot(wi, wm); @@ -138,8 +143,11 @@ ccl_device_forceinline float3 mf_eval_phase_glossy(const float3 w, /* Phase function for dielectric transmissive materials, including both reflection and refraction * according to the dielectric fresnel term. */ -ccl_device_forceinline float3 mf_sample_phase_glass( - const float3 wi, const float eta, const float3 wm, const float randV, bool *outside) +ccl_device_forceinline float3 mf_sample_phase_glass(const float3 wi, + const float eta, + const float3 wm, + const float randV, + ccl_private bool *outside) { float cosI = dot(wi, wm); float f = fresnel_dielectric_cos(cosI, eta); @@ -212,12 +220,12 @@ ccl_device_forceinline float mf_lambda(const float3 w, const float2 alpha) /* Height distribution CDF (based on page 4 of the supplemental implementation). */ ccl_device_forceinline float mf_invC1(const float h) { - return 2.0f * saturate(h) - 1.0f; + return 2.0f * saturatef(h) - 1.0f; } ccl_device_forceinline float mf_C1(const float h) { - return saturate(0.5f * (h + 1.0f)); + return saturatef(0.5f * (h + 1.0f)); } /* Masking function (based on page 16 of the supplemental implementation). */ @@ -232,8 +240,12 @@ ccl_device_forceinline float mf_G1(const float3 w, const float C1, const float l /* Sampling from the visible height distribution (based on page 17 of the supplemental * implementation). */ -ccl_device_forceinline bool mf_sample_height( - const float3 w, float *h, float *C1, float *G1, float *lambda, const float U) +ccl_device_forceinline bool mf_sample_height(const float3 w, + ccl_private float *h, + ccl_private float *C1, + ccl_private float *G1, + ccl_private float *lambda, + const float U) { if (w.z > 0.9999f) return false; @@ -272,7 +284,7 @@ ccl_device_forceinline float mf_ggx_albedo(float r) 0.027803f) * r + 0.00568739f; - return saturate(albedo); + return saturatef(albedo); } ccl_device_inline float mf_ggx_transmission_albedo(float a, float ior) @@ -280,7 +292,7 @@ ccl_device_inline float mf_ggx_transmission_albedo(float a, float ior) if (ior < 1.0f) { ior = 1.0f / ior; } - a = saturate(a); + a = saturatef(a); ior = clamp(ior, 1.0f, 3.0f); float I_1 = 0.0476898f * expf(-0.978352f * (ior - 0.65657f) * (ior - 0.65657f)) - 0.033756f * ior + 0.993261f; @@ -290,7 +302,7 @@ ccl_device_inline float mf_ggx_transmission_albedo(float a, float ior) float R_2 = ((((5.3725f * a - 24.9307f) * a + 22.7437f) * a - 3.40751f) * a + 0.0986325f) * a + 0.00493504f; - return saturate(1.0f + I_2 * R_2 * 0.0019127f - (1.0f - I_1) * (1.0f - R_1) * 9.3205f); + return saturatef(1.0f + I_2 * R_2 * 0.0019127f - (1.0f - I_1) * (1.0f - R_1) * 9.3205f); } ccl_device_forceinline float mf_ggx_pdf(const float3 wi, const float3 wo, const float alpha) @@ -362,9 +374,9 @@ ccl_device_forceinline float mf_glass_pdf(const float3 wi, #define MF_MULTI_GLOSSY #include "kernel/closure/bsdf_microfacet_multi_impl.h" -ccl_device void bsdf_microfacet_multi_ggx_blur(ShaderClosure *sc, float roughness) +ccl_device void bsdf_microfacet_multi_ggx_blur(ccl_private ShaderClosure *sc, float roughness) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; bsdf->alpha_x = fmaxf(roughness, bsdf->alpha_x); bsdf->alpha_y = fmaxf(roughness, bsdf->alpha_y); @@ -374,7 +386,7 @@ ccl_device void bsdf_microfacet_multi_ggx_blur(ShaderClosure *sc, float roughnes /* Multiscattering GGX Glossy closure */ -ccl_device int bsdf_microfacet_multi_ggx_common_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_common_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = clamp(bsdf->alpha_y, 1e-4f, 1.0f); @@ -384,7 +396,7 @@ ccl_device int bsdf_microfacet_multi_ggx_common_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -ccl_device int bsdf_microfacet_multi_ggx_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_setup(ccl_private MicrofacetBsdf *bsdf) { if (is_zero(bsdf->T)) bsdf->T = make_float3(1.0f, 0.0f, 0.0f); @@ -394,7 +406,8 @@ ccl_device int bsdf_microfacet_multi_ggx_setup(MicrofacetBsdf *bsdf) return bsdf_microfacet_multi_ggx_common_setup(bsdf); } -ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(MicrofacetBsdf *bsdf, const ShaderData *sd) +ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { if (is_zero(bsdf->T)) bsdf->T = make_float3(1.0f, 0.0f, 0.0f); @@ -406,7 +419,7 @@ ccl_device int bsdf_microfacet_multi_ggx_fresnel_setup(MicrofacetBsdf *bsdf, con return bsdf_microfacet_multi_ggx_common_setup(bsdf); } -ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_y = bsdf->alpha_x; @@ -415,23 +428,23 @@ ccl_device int bsdf_microfacet_multi_ggx_refraction_setup(MicrofacetBsdf *bsdf) return bsdf_microfacet_multi_ggx_common_setup(bsdf); } -ccl_device float3 bsdf_microfacet_multi_ggx_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_multi_ggx_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float *pdf, + ccl_private uint *lcg_state) { *pdf = 0.0f; return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { return make_float3(0.0f, 0.0f, 0.0f); @@ -466,22 +479,22 @@ ccl_device float3 bsdf_microfacet_multi_ggx_eval_reflect(const ShaderClosure *sc bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 X, Y, Z; Z = bsdf->N; @@ -534,7 +547,7 @@ ccl_device int bsdf_microfacet_multi_ggx_sample(KernelGlobals *kg, /* Multiscattering GGX Glass closure */ -ccl_device int bsdf_microfacet_multi_ggx_glass_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_microfacet_multi_ggx_glass_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = bsdf->alpha_x; @@ -546,8 +559,8 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_setup(MicrofacetBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(MicrofacetBsdf *bsdf, - const ShaderData *sd) +ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(ccl_private MicrofacetBsdf *bsdf, + ccl_private const ShaderData *sd) { bsdf->alpha_x = clamp(bsdf->alpha_x, 1e-4f, 1.0f); bsdf->alpha_y = bsdf->alpha_x; @@ -562,13 +575,14 @@ ccl_device int bsdf_microfacet_multi_ggx_glass_fresnel_setup(MicrofacetBsdf *bsd return SD_BSDF | SD_BSDF_HAS_EVAL | SD_BSDF_NEEDS_LCG; } -ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_transmit(const ShaderClosure *sc, - const float3 I, - const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) +ccl_device float3 +bsdf_microfacet_multi_ggx_glass_eval_transmit(ccl_private const ShaderClosure *sc, + const float3 I, + const float3 omega_in, + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { return make_float3(0.0f, 0.0f, 0.0f); @@ -594,13 +608,13 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_transmit(const ShaderClos bsdf->extra->color); } -ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; if (bsdf->alpha_x * bsdf->alpha_y < 1e-7f) { return make_float3(0.0f, 0.0f, 0.0f); @@ -628,22 +642,22 @@ ccl_device float3 bsdf_microfacet_multi_ggx_glass_eval_reflect(const ShaderClosu bsdf->extra->cspec0); } -ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals *kg, - const ShaderClosure *sc, +ccl_device int bsdf_microfacet_multi_ggx_glass_sample(KernelGlobals kg, + ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf, - ccl_addr_space uint *lcg_state) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf, + ccl_private uint *lcg_state) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 X, Y, Z; Z = bsdf->N; diff --git a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h index 04d9b22d7d2..d23cc16cff3 100644 --- a/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h +++ b/intern/cycles/kernel/closure/bsdf_microfacet_multi_impl.h @@ -31,7 +31,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, const float3 color, const float alpha_x, const float alpha_y, - ccl_addr_space uint *lcg_state, + ccl_private uint *lcg_state, const float eta, bool use_fresnel, const float3 cspec0) @@ -101,12 +101,12 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, for (int order = 0; order < 10; order++) { /* Sample microfacet height. */ - float height_rand = lcg_step_float_addrspace(lcg_state); + float height_rand = lcg_step_float(lcg_state); if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand)) break; /* Sample microfacet normal. */ - float vndf_rand_y = lcg_step_float_addrspace(lcg_state); - float vndf_rand_x = lcg_step_float_addrspace(lcg_state); + float vndf_rand_y = lcg_step_float(lcg_state); + float vndf_rand_x = lcg_step_float(lcg_state); float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y); #ifdef MF_MULTI_GLASS @@ -145,7 +145,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, #ifdef MF_MULTI_GLASS bool next_outside; float3 wi_prev = -wr; - float phase_rand = lcg_step_float_addrspace(lcg_state); + float phase_rand = lcg_step_float(lcg_state); wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside); if (!next_outside) { outside = !outside; @@ -186,11 +186,11 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_eval)(float3 wi, * reflection losses due to coloring or fresnel absorption in conductors, the sampling is optimal. */ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, - float3 *wo, + ccl_private float3 *wo, const float3 color, const float alpha_x, const float alpha_y, - ccl_addr_space uint *lcg_state, + ccl_private uint *lcg_state, const float eta, bool use_fresnel, const float3 cspec0) @@ -213,15 +213,15 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, int order; for (order = 0; order < 10; order++) { /* Sample microfacet height. */ - float height_rand = lcg_step_float_addrspace(lcg_state); + float height_rand = lcg_step_float(lcg_state); if (!mf_sample_height(wr, &hr, &C1_r, &G1_r, &lambda_r, height_rand)) { /* The random walk has left the surface. */ *wo = outside ? wr : -wr; return throughput; } /* Sample microfacet normal. */ - float vndf_rand_y = lcg_step_float_addrspace(lcg_state); - float vndf_rand_x = lcg_step_float_addrspace(lcg_state); + float vndf_rand_y = lcg_step_float(lcg_state); + float vndf_rand_x = lcg_step_float(lcg_state); float3 wm = mf_sample_vndf(-wr, alpha, vndf_rand_x, vndf_rand_y); /* First-bounce color is already accounted for in mix weight. */ @@ -232,7 +232,7 @@ ccl_device_forceinline float3 MF_FUNCTION_FULL_NAME(mf_sample)(float3 wi, #ifdef MF_MULTI_GLASS bool next_outside; float3 wi_prev = -wr; - float phase_rand = lcg_step_float_addrspace(lcg_state); + float phase_rand = lcg_step_float(lcg_state); wr = mf_sample_phase_glass(-wr, outside ? eta : 1.0f / eta, wm, phase_rand, &next_outside); if (!next_outside) { hr = -hr; diff --git a/intern/cycles/kernel/closure/bsdf_oren_nayar.h b/intern/cycles/kernel/closure/bsdf_oren_nayar.h index 41e5736bf49..8827309a811 100644 --- a/intern/cycles/kernel/closure/bsdf_oren_nayar.h +++ b/intern/cycles/kernel/closure/bsdf_oren_nayar.h @@ -14,12 +14,11 @@ * limitations under the License. */ -#ifndef __BSDF_OREN_NAYAR_H__ -#define __BSDF_OREN_NAYAR_H__ +#pragma once CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct OrenNayarBsdf { +typedef struct OrenNayarBsdf { SHADER_CLOSURE_BASE; float roughness; @@ -29,12 +28,12 @@ typedef ccl_addr_space struct OrenNayarBsdf { static_assert(sizeof(ShaderClosure) >= sizeof(OrenNayarBsdf), "OrenNayarBsdf is too large!"); -ccl_device float3 bsdf_oren_nayar_get_intensity(const ShaderClosure *sc, +ccl_device float3 bsdf_oren_nayar_get_intensity(ccl_private const ShaderClosure *sc, float3 n, float3 v, float3 l) { - const OrenNayarBsdf *bsdf = (const OrenNayarBsdf *)sc; + ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; float nl = max(dot(n, l), 0.0f); float nv = max(dot(n, v), 0.0f); float t = dot(l, v) - nl * nv; @@ -45,13 +44,13 @@ ccl_device float3 bsdf_oren_nayar_get_intensity(const ShaderClosure *sc, return make_float3(is, is, is); } -ccl_device int bsdf_oren_nayar_setup(OrenNayarBsdf *bsdf) +ccl_device int bsdf_oren_nayar_setup(ccl_private OrenNayarBsdf *bsdf) { float sigma = bsdf->roughness; bsdf->type = CLOSURE_BSDF_OREN_NAYAR_ID; - sigma = saturate(sigma); + sigma = saturatef(sigma); float div = 1.0f / (M_PI_F + ((3.0f * M_PI_F - 4.0f) / 6.0f) * sigma); @@ -61,20 +60,12 @@ ccl_device int bsdf_oren_nayar_setup(OrenNayarBsdf *bsdf) return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_oren_nayar_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const OrenNayarBsdf *bsdf_a = (const OrenNayarBsdf *)a; - const OrenNayarBsdf *bsdf_b = (const OrenNayarBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->roughness == bsdf_b->roughness); -} - -ccl_device float3 bsdf_oren_nayar_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_oren_nayar_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const OrenNayarBsdf *bsdf = (const OrenNayarBsdf *)sc; + ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; if (dot(bsdf->N, omega_in) > 0.0f) { *pdf = 0.5f * M_1_PI_F; return bsdf_oren_nayar_get_intensity(sc, bsdf->N, I, omega_in); @@ -85,28 +76,28 @@ ccl_device float3 bsdf_oren_nayar_eval_reflect(const ShaderClosure *sc, } } -ccl_device float3 bsdf_oren_nayar_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_oren_nayar_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_oren_nayar_sample(const ShaderClosure *sc, +ccl_device int bsdf_oren_nayar_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const OrenNayarBsdf *bsdf = (const OrenNayarBsdf *)sc; + ccl_private const OrenNayarBsdf *bsdf = (ccl_private const OrenNayarBsdf *)sc; sample_uniform_hemisphere(bsdf->N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0.0f) { @@ -127,5 +118,3 @@ ccl_device int bsdf_oren_nayar_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_OREN_NAYAR_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_phong_ramp.h b/intern/cycles/kernel/closure/bsdf_phong_ramp.h index cf5484383f2..74cc62d917b 100644 --- a/intern/cycles/kernel/closure/bsdf_phong_ramp.h +++ b/intern/cycles/kernel/closure/bsdf_phong_ramp.h @@ -30,18 +30,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_PHONG_RAMP_H__ -#define __BSDF_PHONG_RAMP_H__ +#pragma once CCL_NAMESPACE_BEGIN #ifdef __OSL__ -typedef ccl_addr_space struct PhongRampBsdf { +typedef struct PhongRampBsdf { SHADER_CLOSURE_BASE; float exponent; - float3 *colors; + ccl_private float3 *colors; } PhongRampBsdf; static_assert(sizeof(ShaderClosure) >= sizeof(PhongRampBsdf), "PhongRampBsdf is too large!"); @@ -60,19 +59,19 @@ ccl_device float3 bsdf_phong_ramp_get_color(const float3 colors[8], float pos) return colors[ipos] * (1.0f - offset) + colors[ipos + 1] * offset; } -ccl_device int bsdf_phong_ramp_setup(PhongRampBsdf *bsdf) +ccl_device int bsdf_phong_ramp_setup(ccl_private PhongRampBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_PHONG_RAMP_ID; bsdf->exponent = max(bsdf->exponent, 0.0f); return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_phong_ramp_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_phong_ramp_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const PhongRampBsdf *bsdf = (const PhongRampBsdf *)sc; + ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc; float m_exponent = bsdf->exponent; float cosNI = dot(bsdf->N, omega_in); float cosNO = dot(bsdf->N, I); @@ -93,28 +92,28 @@ ccl_device float3 bsdf_phong_ramp_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_phong_ramp_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_phong_ramp_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, +ccl_device int bsdf_phong_ramp_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const PhongRampBsdf *bsdf = (const PhongRampBsdf *)sc; + ccl_private const PhongRampBsdf *bsdf = (ccl_private const PhongRampBsdf *)sc; float cosNO = dot(bsdf->N, I); float m_exponent = bsdf->exponent; @@ -153,5 +152,3 @@ ccl_device int bsdf_phong_ramp_sample(const ShaderClosure *sc, #endif /* __OSL__ */ CCL_NAMESPACE_END - -#endif /* __BSDF_PHONG_RAMP_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h index d5d012068ff..69376c1294d 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_diffuse.h +++ b/intern/cycles/kernel/closure/bsdf_principled_diffuse.h @@ -14,76 +14,128 @@ * limitations under the License. */ -#ifndef __BSDF_PRINCIPLED_DIFFUSE_H__ -#define __BSDF_PRINCIPLED_DIFFUSE_H__ +#pragma once /* DISNEY PRINCIPLED DIFFUSE BRDF * * Shading model by Brent Burley (Disney): "Physically Based Shading at Disney" (2012) + * + * "Extending the Disney BRDF to a BSDF with Integrated Subsurface Scattering" (2015) + * For the separation of retro-reflection, "2.3 Dielectric BRDF with integrated + * subsurface scattering" */ +#include "kernel/closure/bsdf_util.h" + +#include "kernel/sample/mapping.h" + CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct PrincipledDiffuseBsdf { +enum PrincipledDiffuseBsdfComponents { + PRINCIPLED_DIFFUSE_FULL = 1, + PRINCIPLED_DIFFUSE_LAMBERT = 2, + PRINCIPLED_DIFFUSE_LAMBERT_EXIT = 4, + PRINCIPLED_DIFFUSE_RETRO_REFLECTION = 8, +}; + +typedef struct PrincipledDiffuseBsdf { SHADER_CLOSURE_BASE; float roughness; + int components; } PrincipledDiffuseBsdf; static_assert(sizeof(ShaderClosure) >= sizeof(PrincipledDiffuseBsdf), "PrincipledDiffuseBsdf is too large!"); -ccl_device float3 calculate_principled_diffuse_brdf( - const PrincipledDiffuseBsdf *bsdf, float3 N, float3 V, float3 L, float3 H, float *pdf) +ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf *bsdf) { - float NdotL = dot(N, L); - float NdotV = dot(N, V); + bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; + bsdf->components = PRINCIPLED_DIFFUSE_FULL; + return SD_BSDF | SD_BSDF_HAS_EVAL; +} - if (NdotL <= 0 || NdotV <= 0) { - *pdf = 0.0f; +ccl_device float3 +bsdf_principled_diffuse_compute_brdf(ccl_private const PrincipledDiffuseBsdf *bsdf, + float3 N, + float3 V, + float3 L, + ccl_private float *pdf) +{ + const float NdotL = dot(N, L); + + if (NdotL <= 0) { return make_float3(0.0f, 0.0f, 0.0f); } - float LdotH = dot(L, H); + const float NdotV = dot(N, V); - float FL = schlick_fresnel(NdotL), FV = schlick_fresnel(NdotV); - const float Fd90 = 0.5f + 2.0f * LdotH * LdotH * bsdf->roughness; - float Fd = (1.0f * (1.0f - FL) + Fd90 * FL) * (1.0f * (1.0f - FV) + Fd90 * FV); + const float FV = schlick_fresnel(NdotV); + const float FL = schlick_fresnel(NdotL); - float value = M_1_PI_F * NdotL * Fd; + float f = 0.0f; + + /* Lambertian component. */ + if (bsdf->components & (PRINCIPLED_DIFFUSE_FULL | PRINCIPLED_DIFFUSE_LAMBERT)) { + f += (1.0f - 0.5f * FV) * (1.0f - 0.5f * FL); + } + else if (bsdf->components & PRINCIPLED_DIFFUSE_LAMBERT_EXIT) { + f += (1.0f - 0.5f * FL); + } + + /* Retro-reflection component. */ + if (bsdf->components & (PRINCIPLED_DIFFUSE_FULL | PRINCIPLED_DIFFUSE_RETRO_REFLECTION)) { + /* H = normalize(L + V); // Bisector of an angle between L and V + * LH2 = 2 * dot(L, H)^2 = 2cos(x)^2 = cos(2x) + 1 = dot(L, V) + 1, + * half-angle x between L and V is at most 90 deg. */ + const float LH2 = dot(L, V) + 1; + const float RR = bsdf->roughness * LH2; + f += RR * (FL + FV + FL * FV * (RR - 1.0f)); + } + + float value = M_1_PI_F * NdotL * f; return make_float3(value, value, value); } -ccl_device int bsdf_principled_diffuse_setup(PrincipledDiffuseBsdf *bsdf) +/* Compute Fresnel at entry point, to be combined with #PRINCIPLED_DIFFUSE_LAMBERT_EXIT + * at the exit point to get the complete BSDF. */ +ccl_device_inline float bsdf_principled_diffuse_compute_entry_fresnel(const float NdotV) +{ + const float FV = schlick_fresnel(NdotV); + return (1.0f - 0.5f * FV); +} + +/* Ad-hoc weight adjustment to avoid retro-reflection taking away half the + * samples from BSSRDF. */ +ccl_device_inline float bsdf_principled_diffuse_retro_reflection_sample_weight( + ccl_private PrincipledDiffuseBsdf *bsdf, const float3 I) +{ + return bsdf->roughness * schlick_fresnel(dot(bsdf->N, I)); +} + +ccl_device int bsdf_principled_diffuse_setup(ccl_private PrincipledDiffuseBsdf *bsdf, + int components) { bsdf->type = CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID; + bsdf->components = components; return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_principled_diffuse_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const PrincipledDiffuseBsdf *bsdf_a = (const PrincipledDiffuseBsdf *)a; - const PrincipledDiffuseBsdf *bsdf_b = (const PrincipledDiffuseBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N) && bsdf_a->roughness == bsdf_b->roughness); -} - -ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_diffuse_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const PrincipledDiffuseBsdf *bsdf = (const PrincipledDiffuseBsdf *)sc; + ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc; float3 N = bsdf->N; float3 V = I; // outgoing float3 L = omega_in; // incoming - float3 H = normalize(L + V); if (dot(N, omega_in) > 0.0f) { *pdf = fmaxf(dot(N, omega_in), 0.0f) * M_1_PI_F; - return calculate_principled_diffuse_brdf(bsdf, N, V, L, H, pdf); + return bsdf_principled_diffuse_compute_brdf(bsdf, N, V, L, pdf); } else { *pdf = 0.0f; @@ -91,37 +143,35 @@ ccl_device float3 bsdf_principled_diffuse_eval_reflect(const ShaderClosure *sc, } } -ccl_device float3 bsdf_principled_diffuse_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_diffuse_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc, +ccl_device int bsdf_principled_diffuse_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const PrincipledDiffuseBsdf *bsdf = (const PrincipledDiffuseBsdf *)sc; + ccl_private const PrincipledDiffuseBsdf *bsdf = (ccl_private const PrincipledDiffuseBsdf *)sc; float3 N = bsdf->N; sample_cos_hemisphere(N, randu, randv, omega_in, pdf); if (dot(Ng, *omega_in) > 0) { - float3 H = normalize(I + *omega_in); - - *eval = calculate_principled_diffuse_brdf(bsdf, N, I, *omega_in, H, pdf); + *eval = bsdf_principled_diffuse_compute_brdf(bsdf, N, I, *omega_in, pdf); #ifdef __RAY_DIFFERENTIALS__ // TODO: find a better approximation for the diffuse bounce @@ -136,5 +186,3 @@ ccl_device int bsdf_principled_diffuse_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_PRINCIPLED_DIFFUSE_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_principled_sheen.h b/intern/cycles/kernel/closure/bsdf_principled_sheen.h index 3707de29d73..cc0a5accb95 100644 --- a/intern/cycles/kernel/closure/bsdf_principled_sheen.h +++ b/intern/cycles/kernel/closure/bsdf_principled_sheen.h @@ -14,17 +14,18 @@ * limitations under the License. */ -#ifndef __BSDF_PRINCIPLED_SHEEN_H__ -#define __BSDF_PRINCIPLED_SHEEN_H__ +#pragma once /* DISNEY PRINCIPLED SHEEN BRDF * * Shading model by Brent Burley (Disney): "Physically Based Shading at Disney" (2012) */ +#include "kernel/closure/bsdf_util.h" + CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct PrincipledSheenBsdf { +typedef struct PrincipledSheenBsdf { SHADER_CLOSURE_BASE; float avg_value; } PrincipledSheenBsdf; @@ -45,7 +46,7 @@ ccl_device_inline float calculate_avg_principled_sheen_brdf(float3 N, float3 I) } ccl_device float3 -calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, float *pdf) +calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, ccl_private float *pdf) { float NdotL = dot(N, L); float NdotV = dot(N, V); @@ -62,7 +63,8 @@ calculate_principled_sheen_brdf(float3 N, float3 V, float3 L, float3 H, float *p return make_float3(value, value, value); } -ccl_device int bsdf_principled_sheen_setup(const ShaderData *sd, PrincipledSheenBsdf *bsdf) +ccl_device int bsdf_principled_sheen_setup(ccl_private const ShaderData *sd, + ccl_private PrincipledSheenBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_PRINCIPLED_SHEEN_ID; bsdf->avg_value = calculate_avg_principled_sheen_brdf(bsdf->N, sd->I); @@ -70,12 +72,12 @@ ccl_device int bsdf_principled_sheen_setup(const ShaderData *sd, PrincipledSheen return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_principled_sheen_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_sheen_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const PrincipledSheenBsdf *bsdf = (const PrincipledSheenBsdf *)sc; + ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc; float3 N = bsdf->N; float3 V = I; // outgoing @@ -92,28 +94,28 @@ ccl_device float3 bsdf_principled_sheen_eval_reflect(const ShaderClosure *sc, } } -ccl_device float3 bsdf_principled_sheen_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_principled_sheen_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_principled_sheen_sample(const ShaderClosure *sc, +ccl_device int bsdf_principled_sheen_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const PrincipledSheenBsdf *bsdf = (const PrincipledSheenBsdf *)sc; + ccl_private const PrincipledSheenBsdf *bsdf = (ccl_private const PrincipledSheenBsdf *)sc; float3 N = bsdf->N; @@ -137,5 +139,3 @@ ccl_device int bsdf_principled_sheen_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_PRINCIPLED_SHEEN_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_reflection.h b/intern/cycles/kernel/closure/bsdf_reflection.h index c24ba170915..758bfd2b2d0 100644 --- a/intern/cycles/kernel/closure/bsdf_reflection.h +++ b/intern/cycles/kernel/closure/bsdf_reflection.h @@ -30,49 +30,48 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_REFLECTION_H__ -#define __BSDF_REFLECTION_H__ +#pragma once CCL_NAMESPACE_BEGIN /* REFLECTION */ -ccl_device int bsdf_reflection_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_reflection_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_REFLECTION_ID; return SD_BSDF; } -ccl_device float3 bsdf_reflection_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_reflection_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_reflection_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_reflection_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_reflection_sample(const ShaderClosure *sc, +ccl_device int bsdf_reflection_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float3 N = bsdf->N; // only one direction is possible @@ -93,5 +92,3 @@ ccl_device int bsdf_reflection_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_REFLECTION_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_refraction.h b/intern/cycles/kernel/closure/bsdf_refraction.h index d4fbe86dac0..74e149b059e 100644 --- a/intern/cycles/kernel/closure/bsdf_refraction.h +++ b/intern/cycles/kernel/closure/bsdf_refraction.h @@ -30,49 +30,48 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_REFRACTION_H__ -#define __BSDF_REFRACTION_H__ +#pragma once CCL_NAMESPACE_BEGIN /* REFRACTION */ -ccl_device int bsdf_refraction_setup(MicrofacetBsdf *bsdf) +ccl_device int bsdf_refraction_setup(ccl_private MicrofacetBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_REFRACTION_ID; return SD_BSDF; } -ccl_device float3 bsdf_refraction_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_refraction_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_refraction_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_refraction_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_refraction_sample(const ShaderClosure *sc, +ccl_device int bsdf_refraction_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const MicrofacetBsdf *bsdf = (const MicrofacetBsdf *)sc; + ccl_private const MicrofacetBsdf *bsdf = (ccl_private const MicrofacetBsdf *)sc; float m_eta = bsdf->ior; float3 N = bsdf->N; @@ -111,5 +110,3 @@ ccl_device int bsdf_refraction_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_REFRACTION_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_toon.h b/intern/cycles/kernel/closure/bsdf_toon.h index cc5de21ed0e..20f3b8f0074 100644 --- a/intern/cycles/kernel/closure/bsdf_toon.h +++ b/intern/cycles/kernel/closure/bsdf_toon.h @@ -30,12 +30,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_TOON_H__ -#define __BSDF_TOON_H__ +#pragma once CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct ToonBsdf { +typedef struct ToonBsdf { SHADER_CLOSURE_BASE; float size; @@ -46,24 +45,15 @@ static_assert(sizeof(ShaderClosure) >= sizeof(ToonBsdf), "ToonBsdf is too large! /* DIFFUSE TOON */ -ccl_device int bsdf_diffuse_toon_setup(ToonBsdf *bsdf) +ccl_device int bsdf_diffuse_toon_setup(ccl_private ToonBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_DIFFUSE_TOON_ID; - bsdf->size = saturate(bsdf->size); - bsdf->smooth = saturate(bsdf->smooth); + bsdf->size = saturatef(bsdf->size); + bsdf->smooth = saturatef(bsdf->smooth); return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device bool bsdf_toon_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const ToonBsdf *bsdf_a = (const ToonBsdf *)a; - const ToonBsdf *bsdf_b = (const ToonBsdf *)b; - - return (isequal_float3(bsdf_a->N, bsdf_b->N)) && (bsdf_a->size == bsdf_b->size) && - (bsdf_a->smooth == bsdf_b->smooth); -} - ccl_device float3 bsdf_toon_get_intensity(float max_angle, float smooth, float angle) { float is; @@ -83,12 +73,12 @@ ccl_device float bsdf_toon_get_sample_angle(float max_angle, float smooth) return fminf(max_angle + smooth, M_PI_2_F); } -ccl_device float3 bsdf_diffuse_toon_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_toon_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float angle = safe_acosf(fmaxf(dot(bsdf->N, omega_in), 0.0f)); @@ -105,28 +95,28 @@ ccl_device float3 bsdf_diffuse_toon_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_diffuse_toon_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_diffuse_toon_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_diffuse_toon_sample(const ShaderClosure *sc, +ccl_device int bsdf_diffuse_toon_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float sample_angle = bsdf_toon_get_sample_angle(max_angle, smooth); @@ -153,21 +143,21 @@ ccl_device int bsdf_diffuse_toon_sample(const ShaderClosure *sc, /* GLOSSY TOON */ -ccl_device int bsdf_glossy_toon_setup(ToonBsdf *bsdf) +ccl_device int bsdf_glossy_toon_setup(ccl_private ToonBsdf *bsdf) { bsdf->type = CLOSURE_BSDF_GLOSSY_TOON_ID; - bsdf->size = saturate(bsdf->size); - bsdf->smooth = saturate(bsdf->smooth); + bsdf->size = saturatef(bsdf->size); + bsdf->smooth = saturatef(bsdf->smooth); return SD_BSDF | SD_BSDF_HAS_EVAL; } -ccl_device float3 bsdf_glossy_toon_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_glossy_toon_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float cosNI = dot(bsdf->N, omega_in); @@ -190,28 +180,28 @@ ccl_device float3 bsdf_glossy_toon_eval_reflect(const ShaderClosure *sc, return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_glossy_toon_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_glossy_toon_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_glossy_toon_sample(const ShaderClosure *sc, +ccl_device int bsdf_glossy_toon_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const ToonBsdf *bsdf = (const ToonBsdf *)sc; + ccl_private const ToonBsdf *bsdf = (ccl_private const ToonBsdf *)sc; float max_angle = bsdf->size * M_PI_2_F; float smooth = bsdf->smooth * M_PI_2_F; float cosNO = dot(bsdf->N, I); @@ -248,5 +238,3 @@ ccl_device int bsdf_glossy_toon_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_TOON_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_transparent.h b/intern/cycles/kernel/closure/bsdf_transparent.h index 4e5513499e8..e801b6ea1a3 100644 --- a/intern/cycles/kernel/closure/bsdf_transparent.h +++ b/intern/cycles/kernel/closure/bsdf_transparent.h @@ -30,12 +30,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_TRANSPARENT_H__ -#define __BSDF_TRANSPARENT_H__ +#pragma once CCL_NAMESPACE_BEGIN -ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int path_flag) +ccl_device void bsdf_transparent_setup(ccl_private ShaderData *sd, + const float3 weight, + uint32_t path_flag) { /* Check cutoff weight. */ float sample_weight = fabsf(average(weight)); @@ -48,7 +49,7 @@ ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int /* Add weight to existing transparent BSDF. */ for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; + ccl_private ShaderClosure *sc = &sd->closure[i]; if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { sc->weight += weight; @@ -69,7 +70,7 @@ ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int } /* Create new transparent BSDF. */ - ShaderClosure *bsdf = closure_alloc( + ccl_private ShaderClosure *bsdf = closure_alloc( sd, sizeof(ShaderClosure), CLOSURE_BSDF_TRANSPARENT_ID, weight); if (bsdf) { @@ -82,34 +83,34 @@ ccl_device void bsdf_transparent_setup(ShaderData *sd, const float3 weight, int } } -ccl_device float3 bsdf_transparent_eval_reflect(const ShaderClosure *sc, +ccl_device float3 bsdf_transparent_eval_reflect(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device float3 bsdf_transparent_eval_transmit(const ShaderClosure *sc, +ccl_device float3 bsdf_transparent_eval_transmit(ccl_private const ShaderClosure *sc, const float3 I, const float3 omega_in, - float *pdf) + ccl_private float *pdf) { return make_float3(0.0f, 0.0f, 0.0f); } -ccl_device int bsdf_transparent_sample(const ShaderClosure *sc, +ccl_device int bsdf_transparent_sample(ccl_private const ShaderClosure *sc, float3 Ng, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { // only one direction is possible *omega_in = -I; @@ -123,5 +124,3 @@ ccl_device int bsdf_transparent_sample(const ShaderClosure *sc, } CCL_NAMESPACE_END - -#endif /* __BSDF_TRANSPARENT_H__ */ diff --git a/intern/cycles/kernel/closure/bsdf_util.h b/intern/cycles/kernel/closure/bsdf_util.h index a73dee1b045..b1c16a037df 100644 --- a/intern/cycles/kernel/closure/bsdf_util.h +++ b/intern/cycles/kernel/closure/bsdf_util.h @@ -30,25 +30,24 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __BSDF_UTIL_H__ -#define __BSDF_UTIL_H__ +#pragma once CCL_NAMESPACE_BEGIN ccl_device float fresnel_dielectric(float eta, const float3 N, const float3 I, - float3 *R, - float3 *T, + ccl_private float3 *R, + ccl_private float3 *T, #ifdef __RAY_DIFFERENTIALS__ const float3 dIdx, const float3 dIdy, - float3 *dRdx, - float3 *dRdy, - float3 *dTdx, - float3 *dTdy, + ccl_private float3 *dRdx, + ccl_private float3 *dRdy, + ccl_private float3 *dTdx, + ccl_private float3 *dTdy, #endif - bool *is_inside) + ccl_private bool *is_inside) { float cos = dot(N, I), neta; float3 Nn; @@ -149,6 +148,110 @@ interpolate_fresnel_color(float3 L, float3 H, float ior, float F0, float3 cspec0 return cspec0 * (1.0f - FH) + make_float3(1.0f, 1.0f, 1.0f) * FH; } -CCL_NAMESPACE_END +ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) +{ + float3 R = 2 * dot(N, I) * N - I; -#endif /* __BSDF_UTIL_H__ */ + /* Reflection rays may always be at least as shallow as the incoming ray. */ + float threshold = min(0.9f * dot(Ng, I), 0.01f); + if (dot(Ng, R) >= threshold) { + return N; + } + + /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane. + * The X axis is found by normalizing the component of N that's orthogonal to Ng. + * The Y axis isn't actually needed. + */ + float NdotNg = dot(N, Ng); + float3 X = normalize(N - NdotNg * Ng); + + /* Keep math expressions. */ + /* clang-format off */ + /* Calculate N.z and N.x in the local coordinate system. + * + * The goal of this computation is to find a N' that is rotated towards Ng just enough + * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t. + * + * According to the standard reflection equation, + * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t. + * + * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get + * 2*dot(N', I)*N'.z - I.z = t. + * + * The rotation is simple to express in the coordinate system we formed - + * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane, + * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z . + * + * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2). + * + * With these simplifications, + * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t. + * + * The only unknown here is N'.z, so we can solve for that. + * + * The equation has four solutions in general: + * + * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2)) + * We can simplify this expression a bit by grouping terms: + * + * a = I.x^2 + I.z^2 + * b = sqrt(I.x^2 * (a - t^2)) + * c = I.z*t + a + * N'.z = +-sqrt(0.5*(+-b + c)/a) + * + * Two solutions can immediately be discarded because they're negative so N' would lie in the + * lower hemisphere. + */ + /* clang-format on */ + + float Ix = dot(I, X), Iz = dot(I, Ng); + float Ix2 = sqr(Ix), Iz2 = sqr(Iz); + float a = Ix2 + Iz2; + + float b = safe_sqrtf(Ix2 * (a - sqr(threshold))); + float c = Iz * threshold + a; + + /* Evaluate both solutions. + * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than + * one), so check for that first. If no option is viable (might happen in extreme cases like N + * being in the wrong hemisphere), give up and return Ng. */ + float fac = 0.5f / a; + float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); + bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f)); + bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f)); + + float2 N_new; + if (valid1 && valid2) { + /* If both are possible, do the expensive reflection-based check. */ + float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2)); + float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2)); + + float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; + float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; + + valid1 = (R1 >= 1e-5f); + valid2 = (R2 >= 1e-5f); + if (valid1 && valid2) { + /* If both solutions are valid, return the one with the shallower reflection since it will be + * closer to the input (if the original reflection wasn't shallow, we would not be in this + * part of the function). */ + N_new = (R1 < R2) ? N1 : N2; + } + else { + /* If only one reflection is valid (= positive), pick that one. */ + N_new = (R1 > R2) ? N1 : N2; + } + } + else if (valid1 || valid2) { + /* Only one solution passes the N'.z criterium, so pick that one. */ + float Nz2 = valid1 ? N1_z2 : N2_z2; + N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2)); + } + else { + return Ng; + } + + return N_new.x * X + N_new.y * Ng; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/closure/bssrdf.h b/intern/cycles/kernel/closure/bssrdf.h index 562daf1286d..9df69e073c1 100644 --- a/intern/cycles/kernel/closure/bssrdf.h +++ b/intern/cycles/kernel/closure/bssrdf.h @@ -14,182 +14,92 @@ * limitations under the License. */ -#ifndef __KERNEL_BSSRDF_H__ -#define __KERNEL_BSSRDF_H__ +#pragma once CCL_NAMESPACE_BEGIN -typedef ccl_addr_space struct Bssrdf { +typedef struct Bssrdf { SHADER_CLOSURE_BASE; float3 radius; float3 albedo; - float sharpness; - float texture_blur; float roughness; - float channels; + float anisotropy; } Bssrdf; static_assert(sizeof(ShaderClosure) >= sizeof(Bssrdf), "Bssrdf is too large!"); -/* Planar Truncated Gaussian - * - * Note how this is different from the typical gaussian, this one integrates - * to 1 over the plane (where you get an extra 2*pi*x factor). We are lucky - * that integrating x*exp(-x) gives a nice closed form solution. */ +/* Random Walk BSSRDF */ -/* paper suggests 1/12.46 which is much too small, suspect it's *12.46 */ -#define GAUSS_TRUNCATE 12.46f - -ccl_device float bssrdf_gaussian_eval(const float radius, float r) +ccl_device float bssrdf_dipole_compute_Rd(float alpha_prime, float fourthirdA) { - /* integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) from 0 to Rm - * = 1 - exp(-Rm*Rm/(2*v)) */ - const float v = radius * radius * (0.25f * 0.25f); - const float Rm = sqrtf(v * GAUSS_TRUNCATE); + float s = sqrtf(3.0f * (1.0f - alpha_prime)); + return 0.5f * alpha_prime * (1.0f + expf(-fourthirdA * s)) * expf(-s); +} - if (r >= Rm) +ccl_device float bssrdf_dipole_compute_alpha_prime(float rd, float fourthirdA) +{ + /* Little Newton solver. */ + if (rd < 1e-4f) { return 0.0f; - - return expf(-r * r / (2.0f * v)) / (2.0f * M_PI_F * v); -} - -ccl_device float bssrdf_gaussian_pdf(const float radius, float r) -{ - /* 1.0 - expf(-Rm*Rm/(2*v)) simplified */ - const float area_truncated = 1.0f - expf(-0.5f * GAUSS_TRUNCATE); - - return bssrdf_gaussian_eval(radius, r) * (1.0f / (area_truncated)); -} - -ccl_device void bssrdf_gaussian_sample(const float radius, float xi, float *r, float *h) -{ - /* xi = integrate (2*pi*r * exp(-r*r/(2*v)))/(2*pi*v)) = -exp(-r^2/(2*v)) - * r = sqrt(-2*v*logf(xi)) */ - const float v = radius * radius * (0.25f * 0.25f); - const float Rm = sqrtf(v * GAUSS_TRUNCATE); - - /* 1.0 - expf(-Rm*Rm/(2*v)) simplified */ - const float area_truncated = 1.0f - expf(-0.5f * GAUSS_TRUNCATE); - - /* r(xi) */ - const float r_squared = -2.0f * v * logf(1.0f - xi * area_truncated); - *r = sqrtf(r_squared); - - /* h^2 + r^2 = Rm^2 */ - *h = safe_sqrtf(Rm * Rm - r_squared); -} - -/* Planar Cubic BSSRDF falloff - * - * This is basically (Rm - x)^3, with some factors to normalize it. For sampling - * we integrate 2*pi*x * (Rm - x)^3, which gives us a quintic equation that as - * far as I can tell has no closed form solution. So we get an iterative solution - * instead with newton-raphson. */ - -ccl_device float bssrdf_cubic_eval(const float radius, const float sharpness, float r) -{ - if (sharpness == 0.0f) { - const float Rm = radius; - - if (r >= Rm) - return 0.0f; - - /* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */ - const float Rm5 = (Rm * Rm) * (Rm * Rm) * Rm; - const float f = Rm - r; - const float num = f * f * f; - - return (10.0f * num) / (Rm5 * M_PI_F); } - else { - float Rm = radius * (1.0f + sharpness); + if (rd >= 0.995f) { + return 0.999999f; + } - if (r >= Rm) - return 0.0f; + float x0 = 0.0f; + float x1 = 1.0f; + float xmid, fmid; - /* custom variation with extra sharpness, to match the previous code */ - const float y = 1.0f / (1.0f + sharpness); - float Rmy, ry, ryinv; - - if (sharpness == 1.0f) { - Rmy = sqrtf(Rm); - ry = sqrtf(r); - ryinv = (ry > 0.0f) ? 1.0f / ry : 0.0f; + constexpr const int max_num_iterations = 12; + for (int i = 0; i < max_num_iterations; ++i) { + xmid = 0.5f * (x0 + x1); + fmid = bssrdf_dipole_compute_Rd(xmid, fourthirdA); + if (fmid < rd) { + x0 = xmid; } else { - Rmy = powf(Rm, y); - ry = powf(r, y); - ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f; + x1 = xmid; } + } - const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy; - const float f = Rmy - ry; - const float num = f * (f * f) * (y * ryinv); + return xmid; +} - return (10.0f * num) / (Rmy5 * M_PI_F); +ccl_device void bssrdf_setup_radius(ccl_private Bssrdf *bssrdf, + const ClosureType type, + const float eta) +{ + if (type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) { + /* Scale mean free path length so it gives similar looking result to older + * Cubic, Gaussian and Burley models. */ + bssrdf->radius *= 0.25f * M_1_PI_F; + } + else { + /* Adjust radius based on IOR and albedo. */ + const float inv_eta = 1.0f / eta; + const float F_dr = inv_eta * (-1.440f * inv_eta + 0.710f) + 0.668f + 0.0636f * eta; + const float fourthirdA = (4.0f / 3.0f) * (1.0f + F_dr) / + (1.0f - F_dr); /* From Jensen's `Fdr` ratio formula. */ + + const float3 alpha_prime = make_float3( + bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.x, fourthirdA), + bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.y, fourthirdA), + bssrdf_dipole_compute_alpha_prime(bssrdf->albedo.z, fourthirdA)); + + bssrdf->radius *= sqrt(3.0f * (one_float3() - alpha_prime)); } } -ccl_device float bssrdf_cubic_pdf(const float radius, const float sharpness, float r) -{ - return bssrdf_cubic_eval(radius, sharpness, r); -} - -/* solve 10x^2 - 20x^3 + 15x^4 - 4x^5 - xi == 0 */ -ccl_device_forceinline float bssrdf_cubic_quintic_root_find(float xi) -{ - /* newton-raphson iteration, usually succeeds in 2-4 iterations, except - * outside 0.02 ... 0.98 where it can go up to 10, so overall performance - * should not be too bad */ - const float tolerance = 1e-6f; - const int max_iteration_count = 10; - float x = 0.25f; - int i; - - for (i = 0; i < max_iteration_count; i++) { - float x2 = x * x; - float x3 = x2 * x; - float nx = (1.0f - x); - - float f = 10.0f * x2 - 20.0f * x3 + 15.0f * x2 * x2 - 4.0f * x2 * x3 - xi; - float f_ = 20.0f * (x * nx) * (nx * nx); - - if (fabsf(f) < tolerance || f_ == 0.0f) - break; - - x = saturate(x - f / f_); - } - - return x; -} - -ccl_device void bssrdf_cubic_sample( - const float radius, const float sharpness, float xi, float *r, float *h) -{ - float Rm = radius; - float r_ = bssrdf_cubic_quintic_root_find(xi); - - if (sharpness != 0.0f) { - r_ = powf(r_, 1.0f + sharpness); - Rm *= (1.0f + sharpness); - } - - r_ *= Rm; - *r = r_; - - /* h^2 + r^2 = Rm^2 */ - *h = safe_sqrtf(Rm * Rm - r_ * r_); -} - -/* Approximate Reflectance Profiles +/* Christensen-Burley BSSRDF. + * + * Approximate Reflectance Profiles from * http://graphics.pixar.com/library/ApproxBSSRDF/paper.pdf */ /* This is a bit arbitrary, just need big enough radius so it matches * the mean free length, but still not too big so sampling is still - * effective. Might need some further tweaks. - */ + * effective. */ #define BURLEY_TRUNCATE 16.0f #define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) @@ -200,14 +110,13 @@ ccl_device_inline float bssrdf_burley_fitting(float A) } /* Scale mean free path length so it gives similar looking result - * to Cubic and Gaussian models. - */ + * to Cubic and Gaussian models. */ ccl_device_inline float3 bssrdf_burley_compatible_mfp(float3 r) { return 0.25f * M_1_PI_F * r; } -ccl_device void bssrdf_burley_setup(Bssrdf *bssrdf) +ccl_device void bssrdf_burley_setup(ccl_private Bssrdf *bssrdf) { /* Mean free path length. */ const float3 l = bssrdf_burley_compatible_mfp(bssrdf->radius); @@ -229,10 +138,10 @@ ccl_device float bssrdf_burley_eval(const float d, float r) /* Burley reflectance profile, equation (3). * * NOTES: - * - Surface albedo is already included into sc->weight, no need to + * - Surface albedo is already included into `sc->weight`, no need to * multiply by this term here. * - This is normalized diffuse model, so the equation is multiplied - * by 2*pi, which also matches cdf(). + * by `2*pi`, which also matches `cdf()`. */ float exp_r_3_d = expf(-r / (3.0f * d)); float exp_r_d = exp_r_3_d * exp_r_3_d * exp_r_3_d; @@ -241,6 +150,10 @@ ccl_device float bssrdf_burley_eval(const float d, float r) ccl_device float bssrdf_burley_pdf(const float d, float r) { + if (r == 0.0f) { + return 0.0f; + } + return bssrdf_burley_eval(d, r) * (1.0f / BURLEY_TRUNCATE_CDF); } @@ -284,7 +197,10 @@ ccl_device_forceinline float bssrdf_burley_root_find(float xi) return r; } -ccl_device void bssrdf_burley_sample(const float d, float xi, float *r, float *h) +ccl_device void bssrdf_burley_sample(const float d, + float xi, + ccl_private float *r, + ccl_private float *h) { const float Rm = BURLEY_TRUNCATE * d; const float r_ = bssrdf_burley_root_find(xi * BURLEY_TRUNCATE_CDF) * d; @@ -295,43 +211,68 @@ ccl_device void bssrdf_burley_sample(const float d, float xi, float *r, float *h *h = safe_sqrtf(Rm * Rm - r_ * r_); } -/* None BSSRDF falloff - * - * Samples distributed over disk with no falloff, for reference. */ - -ccl_device float bssrdf_none_eval(const float radius, float r) +ccl_device float bssrdf_num_channels(const float3 radius) { - const float Rm = radius; - return (r < Rm) ? 1.0f : 0.0f; + float channels = 0; + if (radius.x > 0.0f) { + channels += 1.0f; + } + if (radius.y > 0.0f) { + channels += 1.0f; + } + if (radius.z > 0.0f) { + channels += 1.0f; + } + return channels; } -ccl_device float bssrdf_none_pdf(const float radius, float r) +ccl_device void bssrdf_sample(const float3 radius, + float xi, + ccl_private float *r, + ccl_private float *h) { - /* integrate (2*pi*r)/(pi*Rm*Rm) from 0 to Rm = 1 */ - const float Rm = radius; - const float area = (M_PI_F * Rm * Rm); + const float num_channels = bssrdf_num_channels(radius); + float sampled_radius; - return bssrdf_none_eval(radius, r) / area; + /* Sample color channel and reuse random number. Only a subset of channels + * may be used if their radius was too small to handle as BSSRDF. */ + xi *= num_channels; + + if (xi < 1.0f) { + sampled_radius = (radius.x > 0.0f) ? radius.x : (radius.y > 0.0f) ? radius.y : radius.z; + } + else if (xi < 2.0f) { + xi -= 1.0f; + sampled_radius = (radius.x > 0.0f && radius.y > 0.0f) ? radius.y : radius.z; + } + else { + xi -= 2.0f; + sampled_radius = radius.z; + } + + /* Sample BSSRDF. */ + bssrdf_burley_sample(sampled_radius, xi, r, h); } -ccl_device void bssrdf_none_sample(const float radius, float xi, float *r, float *h) +ccl_device_forceinline float3 bssrdf_eval(const float3 radius, float r) { - /* xi = integrate (2*pi*r)/(pi*Rm*Rm) = r^2/Rm^2 - * r = sqrt(xi)*Rm */ - const float Rm = radius; - const float r_ = sqrtf(xi) * Rm; - - *r = r_; - - /* h^2 + r^2 = Rm^2 */ - *h = safe_sqrtf(Rm * Rm - r_ * r_); + return make_float3(bssrdf_burley_pdf(radius.x, r), + bssrdf_burley_pdf(radius.y, r), + bssrdf_burley_pdf(radius.z, r)); } -/* Generic */ - -ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) +ccl_device_forceinline float bssrdf_pdf(const float3 radius, float r) { - Bssrdf *bssrdf = (Bssrdf *)closure_alloc(sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight); + float3 pdf = bssrdf_eval(radius, r); + return (pdf.x + pdf.y + pdf.z) / bssrdf_num_channels(radius); +} + +/* Setup */ + +ccl_device_inline ccl_private Bssrdf *bssrdf_alloc(ccl_private ShaderData *sd, float3 weight) +{ + ccl_private Bssrdf *bssrdf = (ccl_private Bssrdf *)closure_alloc( + sd, sizeof(Bssrdf), CLOSURE_NONE_ID, weight); if (bssrdf == NULL) { return NULL; @@ -342,13 +283,33 @@ ccl_device_inline Bssrdf *bssrdf_alloc(ShaderData *sd, float3 weight) return (sample_weight >= CLOSURE_WEIGHT_CUTOFF) ? bssrdf : NULL; } -ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) +ccl_device int bssrdf_setup(ccl_private ShaderData *sd, + ccl_private Bssrdf *bssrdf, + ClosureType type, + const float ior) { int flag = 0; + + /* Add retro-reflection component as separate diffuse BSDF. */ + if (bssrdf->roughness != FLT_MAX) { + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( + sd, sizeof(PrincipledDiffuseBsdf), bssrdf->weight); + + if (bsdf) { + bsdf->N = bssrdf->N; + bsdf->roughness = bssrdf->roughness; + flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_RETRO_REFLECTION); + + /* Ad-hoc weight adjustment to avoid retro-reflection taking away half the + * samples from BSSRDF. */ + bsdf->sample_weight *= bsdf_principled_diffuse_retro_reflection_sample_weight(bsdf, sd->I); + } + } + + /* Verify if the radii are large enough to sample without precision issues. */ int bssrdf_channels = 3; float3 diffuse_weight = make_float3(0.0f, 0.0f, 0.0f); - /* Verify if the radii are large enough to sample without precision issues. */ if (bssrdf->radius.x < BSSRDF_MIN_RADIUS) { diffuse_weight.x = bssrdf->weight.x; bssrdf->weight.x = 0.0f; @@ -371,27 +332,23 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) if (bssrdf_channels < 3) { /* Add diffuse BSDF if any radius too small. */ #ifdef __PRINCIPLED__ - if (type == CLOSURE_BSSRDF_PRINCIPLED_ID || type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) { - float roughness = bssrdf->roughness; - float3 N = bssrdf->N; - - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + if (bssrdf->roughness != FLT_MAX) { + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), diffuse_weight); if (bsdf) { - bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; - bsdf->N = N; - bsdf->roughness = roughness; - flag |= bsdf_principled_diffuse_setup(bsdf); + bsdf->N = bssrdf->N; + bsdf->roughness = bssrdf->roughness; + flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_LAMBERT); } } else #endif /* __PRINCIPLED__ */ { - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), diffuse_weight); + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), diffuse_weight); if (bsdf) { - bsdf->type = CLOSURE_BSDF_BSSRDF_ID; bsdf->N = bssrdf->N; flag |= bsdf_diffuse_setup(bsdf); } @@ -401,16 +358,9 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) /* Setup BSSRDF if radius is large enough. */ if (bssrdf_channels > 0) { bssrdf->type = type; - bssrdf->channels = bssrdf_channels; - bssrdf->sample_weight = fabsf(average(bssrdf->weight)) * bssrdf->channels; - bssrdf->texture_blur = saturate(bssrdf->texture_blur); - bssrdf->sharpness = saturate(bssrdf->sharpness); + bssrdf->sample_weight = fabsf(average(bssrdf->weight)) * bssrdf_channels; - if (type == CLOSURE_BSSRDF_BURLEY_ID || type == CLOSURE_BSSRDF_PRINCIPLED_ID || - type == CLOSURE_BSSRDF_RANDOM_WALK_ID || - type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) { - bssrdf_burley_setup(bssrdf); - } + bssrdf_setup_radius(bssrdf, type, ior); flag |= SD_BSSRDF; } @@ -422,77 +372,4 @@ ccl_device int bssrdf_setup(ShaderData *sd, Bssrdf *bssrdf, ClosureType type) return flag; } -ccl_device void bssrdf_sample(const ShaderClosure *sc, float xi, float *r, float *h) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - float radius; - - /* Sample color channel and reuse random number. Only a subset of channels - * may be used if their radius was too small to handle as BSSRDF. */ - xi *= bssrdf->channels; - - if (xi < 1.0f) { - radius = (bssrdf->radius.x > 0.0f) ? bssrdf->radius.x : - (bssrdf->radius.y > 0.0f) ? bssrdf->radius.y : - bssrdf->radius.z; - } - else if (xi < 2.0f) { - xi -= 1.0f; - radius = (bssrdf->radius.x > 0.0f && bssrdf->radius.y > 0.0f) ? bssrdf->radius.y : - bssrdf->radius.z; - } - else { - xi -= 2.0f; - radius = bssrdf->radius.z; - } - - /* Sample BSSRDF. */ - if (bssrdf->type == CLOSURE_BSSRDF_CUBIC_ID) { - bssrdf_cubic_sample(radius, bssrdf->sharpness, xi, r, h); - } - else if (bssrdf->type == CLOSURE_BSSRDF_GAUSSIAN_ID) { - bssrdf_gaussian_sample(radius, xi, r, h); - } - else { /* if (bssrdf->type == CLOSURE_BSSRDF_BURLEY_ID || - * bssrdf->type == CLOSURE_BSSRDF_PRINCIPLED_ID) */ - bssrdf_burley_sample(radius, xi, r, h); - } -} - -ccl_device float bssrdf_channel_pdf(const Bssrdf *bssrdf, float radius, float r) -{ - if (radius == 0.0f) { - return 0.0f; - } - else if (bssrdf->type == CLOSURE_BSSRDF_CUBIC_ID) { - return bssrdf_cubic_pdf(radius, bssrdf->sharpness, r); - } - else if (bssrdf->type == CLOSURE_BSSRDF_GAUSSIAN_ID) { - return bssrdf_gaussian_pdf(radius, r); - } - else { /* if (bssrdf->type == CLOSURE_BSSRDF_BURLEY_ID || - * bssrdf->type == CLOSURE_BSSRDF_PRINCIPLED_ID)*/ - return bssrdf_burley_pdf(radius, r); - } -} - -ccl_device_forceinline float3 bssrdf_eval(const ShaderClosure *sc, float r) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - - return make_float3(bssrdf_channel_pdf(bssrdf, bssrdf->radius.x, r), - bssrdf_channel_pdf(bssrdf, bssrdf->radius.y, r), - bssrdf_channel_pdf(bssrdf, bssrdf->radius.z, r)); -} - -ccl_device_forceinline float bssrdf_pdf(const ShaderClosure *sc, float r) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - float3 pdf = bssrdf_eval(sc, r); - - return (pdf.x + pdf.y + pdf.z) / bssrdf->channels; -} - CCL_NAMESPACE_END - -#endif /* __KERNEL_BSSRDF_H__ */ diff --git a/intern/cycles/kernel/closure/emissive.h b/intern/cycles/kernel/closure/emissive.h index 911382e6865..3d56e989522 100644 --- a/intern/cycles/kernel/closure/emissive.h +++ b/intern/cycles/kernel/closure/emissive.h @@ -30,11 +30,13 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + CCL_NAMESPACE_BEGIN /* BACKGROUND CLOSURE */ -ccl_device void background_setup(ShaderData *sd, const float3 weight) +ccl_device void background_setup(ccl_private ShaderData *sd, const float3 weight) { if (sd->flag & SD_EMISSION) { sd->closure_emission_background += weight; @@ -47,7 +49,7 @@ ccl_device void background_setup(ShaderData *sd, const float3 weight) /* EMISSION CLOSURE */ -ccl_device void emission_setup(ShaderData *sd, const float3 weight) +ccl_device void emission_setup(ccl_private ShaderData *sd, const float3 weight) { if (sd->flag & SD_EMISSION) { sd->closure_emission_background += weight; @@ -67,8 +69,11 @@ ccl_device float emissive_pdf(const float3 Ng, const float3 I) return (cosNO > 0.0f) ? 1.0f : 0.0f; } -ccl_device void emissive_sample( - const float3 Ng, float randu, float randv, float3 *omega_out, float *pdf) +ccl_device void emissive_sample(const float3 Ng, + float randu, + float randv, + ccl_private float3 *omega_out, + ccl_private float *pdf) { /* todo: not implemented and used yet */ } diff --git a/intern/cycles/kernel/closure/volume.h b/intern/cycles/kernel/closure/volume.h index 1430f712701..023fb3ac4ea 100644 --- a/intern/cycles/kernel/closure/volume.h +++ b/intern/cycles/kernel/closure/volume.h @@ -14,14 +14,13 @@ * limitations under the License. */ -#ifndef __VOLUME_H__ -#define __VOLUME_H__ +#pragma once CCL_NAMESPACE_BEGIN /* VOLUME EXTINCTION */ -ccl_device void volume_extinction_setup(ShaderData *sd, float3 weight) +ccl_device void volume_extinction_setup(ccl_private ShaderData *sd, float3 weight) { if (sd->flag & SD_EXTINCTION) { sd->closure_transparent_extinction += weight; @@ -34,7 +33,7 @@ ccl_device void volume_extinction_setup(ShaderData *sd, float3 weight) /* HENYEY-GREENSTEIN CLOSURE */ -typedef ccl_addr_space struct HenyeyGreensteinVolume { +typedef struct HenyeyGreensteinVolume { SHADER_CLOSURE_BASE; float g; @@ -52,7 +51,7 @@ ccl_device float single_peaked_henyey_greenstein(float cos_theta, float g) (M_1_PI_F * 0.25f); }; -ccl_device int volume_henyey_greenstein_setup(HenyeyGreensteinVolume *volume) +ccl_device int volume_henyey_greenstein_setup(ccl_private HenyeyGreensteinVolume *volume) { volume->type = CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID; @@ -62,21 +61,12 @@ ccl_device int volume_henyey_greenstein_setup(HenyeyGreensteinVolume *volume) return SD_SCATTER; } -ccl_device bool volume_henyey_greenstein_merge(const ShaderClosure *a, const ShaderClosure *b) -{ - const HenyeyGreensteinVolume *volume_a = (const HenyeyGreensteinVolume *)a; - const HenyeyGreensteinVolume *volume_b = (const HenyeyGreensteinVolume *)b; - - return (volume_a->g == volume_b->g); -} - -ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, +ccl_device float3 volume_henyey_greenstein_eval_phase(ccl_private const ShaderVolumeClosure *svc, const float3 I, float3 omega_in, - float *pdf) + ccl_private float *pdf) { - const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume *)sc; - float g = volume->g; + float g = svc->g; /* note that I points towards the viewer */ if (fabsf(g) < 1e-3f) { @@ -91,7 +81,7 @@ ccl_device float3 volume_henyey_greenstein_eval_phase(const ShaderClosure *sc, } ccl_device float3 -henyey_greenstrein_sample(float3 D, float g, float randu, float randv, float *pdf) +henyey_greenstrein_sample(float3 D, float g, float randu, float randv, ccl_private float *pdf) { /* match pdf for small g */ float cos_theta; @@ -122,20 +112,19 @@ henyey_greenstrein_sample(float3 D, float g, float randu, float randv, float *pd return dir; } -ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, +ccl_device int volume_henyey_greenstein_sample(ccl_private const ShaderVolumeClosure *svc, float3 I, float3 dIdx, float3 dIdy, float randu, float randv, - float3 *eval, - float3 *omega_in, - float3 *domega_in_dx, - float3 *domega_in_dy, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private float3 *domega_in_dx, + ccl_private float3 *domega_in_dy, + ccl_private float *pdf) { - const HenyeyGreensteinVolume *volume = (const HenyeyGreensteinVolume *)sc; - float g = volume->g; + float g = svc->g; /* note that I points towards the viewer and so is used negated */ *omega_in = henyey_greenstrein_sample(-I, g, randu, randv, pdf); @@ -152,50 +141,85 @@ ccl_device int volume_henyey_greenstein_sample(const ShaderClosure *sc, /* VOLUME CLOSURE */ -ccl_device float3 volume_phase_eval(const ShaderData *sd, - const ShaderClosure *sc, +ccl_device float3 volume_phase_eval(ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *svc, float3 omega_in, - float *pdf) + ccl_private float *pdf) { - kernel_assert(sc->type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID); - - return volume_henyey_greenstein_eval_phase(sc, sd->I, omega_in, pdf); + return volume_henyey_greenstein_eval_phase(svc, sd->I, omega_in, pdf); } -ccl_device int volume_phase_sample(const ShaderData *sd, - const ShaderClosure *sc, +ccl_device int volume_phase_sample(ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *svc, float randu, float randv, - float3 *eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) + ccl_private float3 *eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) { - int label; + return volume_henyey_greenstein_sample(svc, + sd->I, + sd->dI.dx, + sd->dI.dy, + randu, + randv, + eval, + omega_in, + &domega_in->dx, + &domega_in->dy, + pdf); +} - switch (sc->type) { - case CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID: - label = volume_henyey_greenstein_sample(sc, - sd->I, - sd->dI.dx, - sd->dI.dy, - randu, - randv, - eval, - omega_in, - &domega_in->dx, - &domega_in->dy, - pdf); - break; - default: - *eval = make_float3(0.0f, 0.0f, 0.0f); - label = LABEL_NONE; - break; +/* Volume sampling utilities. */ + +/* todo: this value could be tweaked or turned into a probability to avoid + * unnecessary work in volumes and subsurface scattering. */ +#define VOLUME_THROUGHPUT_EPSILON 1e-6f + +ccl_device float3 volume_color_transmittance(float3 sigma, float t) +{ + return exp3(-sigma * t); +} + +ccl_device float volume_channel_get(float3 value, int channel) +{ + return (channel == 0) ? value.x : ((channel == 1) ? value.y : value.z); +} + +ccl_device int volume_sample_channel(float3 albedo, + float3 throughput, + float rand, + ccl_private float3 *pdf) +{ + /* Sample color channel proportional to throughput and single scattering + * albedo, to significantly reduce noise with many bounce, following: + * + * "Practical and Controllable Subsurface Scattering for Production Path + * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ + float3 weights = fabs(throughput * albedo); + float sum_weights = weights.x + weights.y + weights.z; + float3 weights_pdf; + + if (sum_weights > 0.0f) { + weights_pdf = weights / sum_weights; + } + else { + weights_pdf = make_float3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f); } - return label; + *pdf = weights_pdf; + + /* OpenCL does not support -> on float3, so don't use pdf->x. */ + if (rand < weights_pdf.x) { + return 0; + } + else if (rand < weights_pdf.x + weights_pdf.y) { + return 1; + } + else { + return 2; + } } CCL_NAMESPACE_END - -#endif diff --git a/intern/cycles/kernel/kernel_compat_cpu.h b/intern/cycles/kernel/device/cpu/compat.h similarity index 57% rename from intern/cycles/kernel/kernel_compat_cpu.h rename to intern/cycles/kernel/device/cpu/compat.h index 88f6a264a5a..5ccca52255f 100644 --- a/intern/cycles/kernel/kernel_compat_cpu.h +++ b/intern/cycles/kernel/device/cpu/compat.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_COMPAT_CPU_H__ -#define __KERNEL_COMPAT_CPU_H__ +#pragma once #define __KERNEL_CPU__ @@ -27,30 +26,11 @@ # pragma GCC diagnostic ignored "-Wuninitialized" #endif -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL -#endif - -#include "util/util_half.h" -#include "util/util_math.h" -#include "util/util_simd.h" -#include "util/util_texture.h" -#include "util/util_types.h" - -#define ccl_addr_space - -#define ccl_local_id(d) 0 -#define ccl_global_id(d) (kg->global_id[d]) - -#define ccl_local_size(d) 1 -#define ccl_global_size(d) (kg->global_size[d]) - -#define ccl_group_id(d) ccl_global_id(d) -#define ccl_num_groups(d) ccl_global_size(d) +#include "util/half.h" +#include "util/math.h" +#include "util/simd.h" +#include "util/texture.h" +#include "util/types.h" /* On x86_64, versions of glibc < 2.16 have an issue where expf is * much slower than the double version. This was fixed in glibc 2.16. @@ -72,37 +52,11 @@ CCL_NAMESPACE_BEGIN * simple arrays and after inlining fetch hopefully revert to being a simple * pointer lookup. */ template struct texture { - ccl_always_inline const T &fetch(int index) + ccl_always_inline const T &fetch(int index) const { kernel_assert(index >= 0 && index < width); return data[index]; } -#if defined(__KERNEL_AVX__) || defined(__KERNEL_AVX2__) - /* Reads 256 bytes but indexes in blocks of 128 bytes to maintain - * compatibility with existing indices and data structures. - */ - ccl_always_inline avxf fetch_avxf(const int index) - { - kernel_assert(index >= 0 && (index + 1) < width); - ssef *ssef_data = (ssef *)data; - ssef *ssef_node_data = &ssef_data[index]; - return _mm256_loadu_ps((float *)ssef_node_data); - } -#endif - -#ifdef __KERNEL_SSE2__ - ccl_always_inline ssef fetch_ssef(int index) - { - kernel_assert(index >= 0 && index < width); - return ((ssef *)data)[index]; - } - - ccl_always_inline ssei fetch_ssei(int index) - { - kernel_assert(index >= 0 && index < width); - return ((ssei *)data)[index]; - } -#endif T *data; int width; @@ -110,15 +64,6 @@ template struct texture { /* Macros to handle different memory storage on different devices */ -#define kernel_tex_fetch(tex, index) (kg->tex.fetch(index)) -#define kernel_tex_fetch_avxf(tex, index) (kg->tex.fetch_avxf(index)) -#define kernel_tex_fetch_ssef(tex, index) (kg->tex.fetch_ssef(index)) -#define kernel_tex_fetch_ssei(tex, index) (kg->tex.fetch_ssei(index)) -#define kernel_tex_lookup(tex, t, offset, size) (kg->tex.lookup(t, offset, size)) -#define kernel_tex_array(tex) (kg->tex.data) - -#define kernel_data (kg->__data) - #ifdef __KERNEL_SSE2__ typedef vector3 sse3b; typedef vector3 sse3f; @@ -152,5 +97,3 @@ typedef vector3 avx3f; #endif CCL_NAMESPACE_END - -#endif /* __KERNEL_COMPAT_CPU_H__ */ diff --git a/intern/cycles/kernel/device/cpu/globals.h b/intern/cycles/kernel/device/cpu/globals.h new file mode 100644 index 00000000000..dd0327b3f94 --- /dev/null +++ b/intern/cycles/kernel/device/cpu/globals.h @@ -0,0 +1,63 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/types.h" +#include "kernel/util/profiling.h" + +CCL_NAMESPACE_BEGIN + +/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in + * the kernel, to access constant data. These are all stored as "textures", but + * these are really just standard arrays. We can't use actually globals because + * multiple renders may be running inside the same process. */ + +#ifdef __OSL__ +struct OSLGlobals; +struct OSLThreadData; +struct OSLShadingSystem; +#endif + +typedef struct KernelGlobalsCPU { +#define KERNEL_TEX(type, name) texture name; +#include "kernel/textures.h" + + KernelData __data; + +#ifdef __OSL__ + /* On the CPU, we also have the OSL globals here. Most data structures are shared + * with SVM, the difference is in the shaders and object/mesh attributes. */ + OSLGlobals *osl; + OSLShadingSystem *osl_ss; + OSLThreadData *osl_tdata; +#endif + + /* **** Run-time data **** */ + + ProfilingState profiler; +} KernelGlobalsCPU; + +typedef const KernelGlobalsCPU *ccl_restrict KernelGlobals; + +/* Abstraction macros */ +#define kernel_tex_fetch(tex, index) (kg->tex.fetch(index)) +#define kernel_tex_array(tex) (kg->tex.data) +#define kernel_data (kg->__data) + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h b/intern/cycles/kernel/device/cpu/image.h similarity index 98% rename from intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h rename to intern/cycles/kernel/device/cpu/image.h index 59b96c86c50..93f956e354d 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_image.h +++ b/intern/cycles/kernel/device/cpu/image.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_CPU_IMAGE_H__ -#define __KERNEL_CPU_IMAGE_H__ +#pragma once #ifdef WITH_NANOVDB # define NANOVDB_USE_INTRINSICS @@ -73,12 +72,12 @@ template struct TextureInterpolator { static ccl_always_inline float4 read(half4 r) { - return half4_to_float4(r); + return half4_to_float4_image(r); } static ccl_always_inline float4 read(half r) { - float f = half_to_float(r); + float f = half_to_float_image(r); return make_float4(f, f, f, 1.0f); } @@ -584,7 +583,7 @@ template struct NanoVDBInterpolator { #undef SET_CUBIC_SPLINE_WEIGHTS -ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y) +ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, float y) { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); @@ -612,7 +611,7 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, fl } } -ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, +ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, int id, float3 P, InterpolationType interp) @@ -656,5 +655,3 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, } /* Namespace. */ CCL_NAMESPACE_END - -#endif // __KERNEL_CPU_IMAGE_H__ diff --git a/intern/cycles/kernel/kernels/cpu/kernel.cpp b/intern/cycles/kernel/device/cpu/kernel.cpp similarity index 86% rename from intern/cycles/kernel/kernels/cpu/kernel.cpp rename to intern/cycles/kernel/device/cpu/kernel.cpp index 8040bfb7b33..a16c637d5ac 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel.cpp +++ b/intern/cycles/kernel/device/cpu/kernel.cpp @@ -56,15 +56,15 @@ /* do nothing */ #endif -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" CCL_NAMESPACE_BEGIN /* Memory Copy */ -void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t) +void kernel_const_copy(KernelGlobalsCPU *kg, const char *name, void *host, size_t) { if (strcmp(name, "__data") == 0) { kg->__data = *(KernelData *)host; @@ -74,7 +74,7 @@ void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t) } } -void kernel_global_memory_copy(KernelGlobals *kg, const char *name, void *mem, size_t size) +void kernel_global_memory_copy(KernelGlobalsCPU *kg, const char *name, void *mem, size_t size) { if (0) { } @@ -85,7 +85,7 @@ void kernel_global_memory_copy(KernelGlobals *kg, const char *name, void *mem, s kg->tname.data = (type *)mem; \ kg->tname.width = size; \ } -#include "kernel/kernel_textures.h" +#include "kernel/textures.h" else { assert(0); } diff --git a/intern/cycles/kernel/kernel.h b/intern/cycles/kernel/device/cpu/kernel.h similarity index 58% rename from intern/cycles/kernel/kernel.h rename to intern/cycles/kernel/device/cpu/kernel.h index b907c6a2bac..c49d7ca445a 100644 --- a/intern/cycles/kernel/kernel.h +++ b/intern/cycles/kernel/device/cpu/kernel.h @@ -14,13 +14,13 @@ * limitations under the License. */ -#ifndef __KERNEL_H__ -#define __KERNEL_H__ +#pragma once /* CPU Kernel Interface */ -#include "kernel/kernel_types.h" -#include "util/util_types.h" +#include "util/types.h" + +#include "kernel/types.h" CCL_NAMESPACE_BEGIN @@ -28,36 +28,35 @@ CCL_NAMESPACE_BEGIN #define KERNEL_NAME_EVAL(arch, name) KERNEL_NAME_JOIN(kernel, arch, name) #define KERNEL_FUNCTION_FULL_NAME(name) KERNEL_NAME_EVAL(KERNEL_ARCH, name) -struct KernelGlobals; +struct IntegratorStateCPU; +struct KernelGlobalsCPU; struct KernelData; -KernelGlobals *kernel_globals_create(); -void kernel_globals_free(KernelGlobals *kg); +KernelGlobalsCPU *kernel_globals_create(); +void kernel_globals_free(KernelGlobalsCPU *kg); -void *kernel_osl_memory(KernelGlobals *kg); -bool kernel_osl_use(KernelGlobals *kg); +void *kernel_osl_memory(const KernelGlobalsCPU *kg); +bool kernel_osl_use(const KernelGlobalsCPU *kg); -void kernel_const_copy(KernelGlobals *kg, const char *name, void *host, size_t size); -void kernel_global_memory_copy(KernelGlobals *kg, const char *name, void *mem, size_t size); +void kernel_const_copy(KernelGlobalsCPU *kg, const char *name, void *host, size_t size); +void kernel_global_memory_copy(KernelGlobalsCPU *kg, const char *name, void *mem, size_t size); #define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" #define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/kernel_cpu.h" +#include "kernel/device/cpu/kernel_arch.h" CCL_NAMESPACE_END - -#endif /* __KERNEL_H__ */ diff --git a/intern/cycles/kernel/device/cpu/kernel_arch.h b/intern/cycles/kernel/device/cpu/kernel_arch.h new file mode 100644 index 00000000000..432ac5e15a9 --- /dev/null +++ b/intern/cycles/kernel/device/cpu/kernel_arch.h @@ -0,0 +1,110 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Templated common declaration part of all CPU kernels. */ + +/* -------------------------------------------------------------------- + * Integrator. + */ + +#define KERNEL_INTEGRATOR_FUNCTION(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *ccl_restrict kg, \ + IntegratorStateCPU *state) + +#define KERNEL_INTEGRATOR_SHADE_FUNCTION(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *ccl_restrict kg, \ + IntegratorStateCPU *state, \ + ccl_global float *render_buffer) + +#define KERNEL_INTEGRATOR_INIT_FUNCTION(name) \ + bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *ccl_restrict kg, \ + IntegratorStateCPU *state, \ + KernelWorkTile *tile, \ + ccl_global float *render_buffer) + +KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_camera); +KERNEL_INTEGRATOR_INIT_FUNCTION(init_from_bake); +KERNEL_INTEGRATOR_FUNCTION(intersect_closest); +KERNEL_INTEGRATOR_FUNCTION(intersect_shadow); +KERNEL_INTEGRATOR_FUNCTION(intersect_subsurface); +KERNEL_INTEGRATOR_FUNCTION(intersect_volume_stack); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_background); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_light); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_shadow); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_surface); +KERNEL_INTEGRATOR_SHADE_FUNCTION(shade_volume); +KERNEL_INTEGRATOR_SHADE_FUNCTION(megakernel); + +#undef KERNEL_INTEGRATOR_FUNCTION +#undef KERNEL_INTEGRATOR_INIT_FUNCTION +#undef KERNEL_INTEGRATOR_SHADE_FUNCTION + +/* -------------------------------------------------------------------- + * Shader evaluation. + */ + +void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset); +void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset); +void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)( + const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset); + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( + const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int x, + int y, + float threshold, + bool reset, + int offset, + int stride); + +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int y, + int start_x, + int width, + int offset, + int stride); +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int x, + int start_y, + int height, + int offset, + int stride); + +/* -------------------------------------------------------------------- + * Cryptomatte. + */ + +void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int pixel_index); + +#undef KERNEL_ARCH diff --git a/intern/cycles/kernel/device/cpu/kernel_arch_impl.h b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h new file mode 100644 index 00000000000..6df5d7787fc --- /dev/null +++ b/intern/cycles/kernel/device/cpu/kernel_arch_impl.h @@ -0,0 +1,244 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Templated common implementation part of all CPU kernels. + * + * The idea is that particular .cpp files sets needed optimization flags and + * simply includes this file without worry of copying actual implementation over. + */ + +#pragma once + +// clang-format off +#include "kernel/device/cpu/compat.h" + +#ifndef KERNEL_STUB +# include "kernel/device/cpu/globals.h" +# include "kernel/device/cpu/image.h" + +# include "kernel/integrator/state.h" +# include "kernel/integrator/state_flow.h" +# include "kernel/integrator/state_util.h" + +# include "kernel/integrator/init_from_camera.h" +# include "kernel/integrator/init_from_bake.h" +# include "kernel/integrator/intersect_closest.h" +# include "kernel/integrator/intersect_shadow.h" +# include "kernel/integrator/intersect_subsurface.h" +# include "kernel/integrator/intersect_volume_stack.h" +# include "kernel/integrator/shade_background.h" +# include "kernel/integrator/shade_light.h" +# include "kernel/integrator/shade_shadow.h" +# include "kernel/integrator/shade_surface.h" +# include "kernel/integrator/shade_volume.h" +# include "kernel/integrator/megakernel.h" + +# include "kernel/film/adaptive_sampling.h" +# include "kernel/film/read.h" +# include "kernel/film/id_passes.h" + +# include "kernel/bake/bake.h" + +#else +# define STUB_ASSERT(arch, name) \ + assert(!(#name " kernel stub for architecture " #arch " was called!")) +#endif /* KERNEL_STUB */ +// clang-format on + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Integrator. + */ + +#ifdef KERNEL_STUB +# define KERNEL_INVOKE(name, ...) (STUB_ASSERT(KERNEL_ARCH, name), 0) +#else +# define KERNEL_INVOKE(name, ...) integrator_##name(__VA_ARGS__) +#endif + +/* TODO: Either use something like get_work_pixel(), or simplify tile which is passed here, so + * that it does not contain unused fields. */ +#define DEFINE_INTEGRATOR_INIT_KERNEL(name) \ + bool KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ + IntegratorStateCPU *state, \ + KernelWorkTile *tile, \ + ccl_global float *render_buffer) \ + { \ + return KERNEL_INVOKE( \ + name, kg, state, tile, render_buffer, tile->x, tile->y, tile->start_sample); \ + } + +#define DEFINE_INTEGRATOR_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ + IntegratorStateCPU *state) \ + { \ + KERNEL_INVOKE(name, kg, state); \ + } + +#define DEFINE_INTEGRATOR_SHADE_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \ + const KernelGlobalsCPU *kg, IntegratorStateCPU *state, ccl_global float *render_buffer) \ + { \ + KERNEL_INVOKE(name, kg, state, render_buffer); \ + } + +#define DEFINE_INTEGRATOR_SHADOW_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)(const KernelGlobalsCPU *kg, \ + IntegratorStateCPU *state) \ + { \ + KERNEL_INVOKE(name, kg, &state->shadow); \ + } + +#define DEFINE_INTEGRATOR_SHADOW_SHADE_KERNEL(name) \ + void KERNEL_FUNCTION_FULL_NAME(integrator_##name)( \ + const KernelGlobalsCPU *kg, IntegratorStateCPU *state, ccl_global float *render_buffer) \ + { \ + KERNEL_INVOKE(name, kg, &state->shadow, render_buffer); \ + } + +DEFINE_INTEGRATOR_INIT_KERNEL(init_from_camera) +DEFINE_INTEGRATOR_INIT_KERNEL(init_from_bake) +DEFINE_INTEGRATOR_KERNEL(intersect_closest) +DEFINE_INTEGRATOR_KERNEL(intersect_subsurface) +DEFINE_INTEGRATOR_KERNEL(intersect_volume_stack) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_background) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_light) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_surface) +DEFINE_INTEGRATOR_SHADE_KERNEL(shade_volume) +DEFINE_INTEGRATOR_SHADE_KERNEL(megakernel) +DEFINE_INTEGRATOR_SHADOW_KERNEL(intersect_shadow) +DEFINE_INTEGRATOR_SHADOW_SHADE_KERNEL(shade_shadow) + +/* -------------------------------------------------------------------- + * Shader evaluation. + */ + +void KERNEL_FUNCTION_FULL_NAME(shader_eval_displace)(const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, shader_eval_displace); +#else + kernel_displace_evaluate(kg, input, output, offset); +#endif +} + +void KERNEL_FUNCTION_FULL_NAME(shader_eval_background)(const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, shader_eval_background); +#else + kernel_background_evaluate(kg, input, output, offset); +#endif +} + +void KERNEL_FUNCTION_FULL_NAME(shader_eval_curve_shadow_transparency)( + const KernelGlobalsCPU *kg, + const KernelShaderEvalInput *input, + float *output, + const int offset) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, shader_eval_curve_shadow_transparency); +#else + kernel_curve_shadow_transparency_evaluate(kg, input, output, offset); +#endif +} + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +bool KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_convergence_check)( + const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int x, + int y, + float threshold, + bool reset, + int offset, + int stride) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_convergence_check); + return false; +#else + return kernel_adaptive_sampling_convergence_check( + kg, render_buffer, x, y, threshold, reset, offset, stride); +#endif +} + +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_x)(const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int y, + int start_x, + int width, + int offset, + int stride) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_x); +#else + kernel_adaptive_sampling_filter_x(kg, render_buffer, y, start_x, width, offset, stride); +#endif +} + +void KERNEL_FUNCTION_FULL_NAME(adaptive_sampling_filter_y)(const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int x, + int start_y, + int height, + int offset, + int stride) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, adaptive_sampling_filter_y); +#else + kernel_adaptive_sampling_filter_y(kg, render_buffer, x, start_y, height, offset, stride); +#endif +} + +/* -------------------------------------------------------------------- + * Cryptomatte. + */ + +void KERNEL_FUNCTION_FULL_NAME(cryptomatte_postprocess)(const KernelGlobalsCPU *kg, + ccl_global float *render_buffer, + int pixel_index) +{ +#ifdef KERNEL_STUB + STUB_ASSERT(KERNEL_ARCH, cryptomatte_postprocess); +#else + kernel_cryptomatte_post(kg, render_buffer, pixel_index); +#endif +} + +#undef KERNEL_INVOKE +#undef DEFINE_INTEGRATOR_KERNEL +#undef DEFINE_INTEGRATOR_SHADE_KERNEL +#undef DEFINE_INTEGRATOR_INIT_KERNEL + +#undef KERNEL_STUB +#undef STUB_ASSERT +#undef KERNEL_ARCH + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp b/intern/cycles/kernel/device/cpu/kernel_avx.cpp similarity index 91% rename from intern/cycles/kernel/kernels/cpu/kernel_avx.cpp rename to intern/cycles/kernel/device/cpu/kernel_avx.cpp index 5f6b6800363..cece750a255 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_avx.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_avx.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX # define KERNEL_STUB @@ -34,6 +34,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_avx2.cpp b/intern/cycles/kernel/device/cpu/kernel_avx2.cpp similarity index 91% rename from intern/cycles/kernel/kernels/cpu/kernel_avx2.cpp rename to intern/cycles/kernel/device/cpu/kernel_avx2.cpp index 97e8fc25140..fad4581236e 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_avx2.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_avx2.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 # define KERNEL_STUB @@ -35,6 +35,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_sse2.cpp b/intern/cycles/kernel/device/cpu/kernel_sse2.cpp similarity index 90% rename from intern/cycles/kernel/kernels/cpu/kernel_sse2.cpp rename to intern/cycles/kernel/device/cpu/kernel_sse2.cpp index 26d7fd4de48..5fb4849ac08 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_sse2.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse2.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 # define KERNEL_STUB @@ -29,6 +29,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_sse3.cpp b/intern/cycles/kernel/device/cpu/kernel_sse3.cpp similarity index 90% rename from intern/cycles/kernel/kernels/cpu/kernel_sse3.cpp rename to intern/cycles/kernel/device/cpu/kernel_sse3.cpp index 3f259aa4480..c9424682fd4 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_sse3.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse3.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 # define KERNEL_STUB @@ -31,6 +31,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_sse41.cpp b/intern/cycles/kernel/device/cpu/kernel_sse41.cpp similarity index 91% rename from intern/cycles/kernel/kernels/cpu/kernel_sse41.cpp rename to intern/cycles/kernel/device/cpu/kernel_sse41.cpp index 68bae8c07c6..849ebf51989 100644 --- a/intern/cycles/kernel/kernels/cpu/kernel_sse41.cpp +++ b/intern/cycles/kernel/device/cpu/kernel_sse41.cpp @@ -18,7 +18,7 @@ * optimization flags and nearly all functions inlined, while kernel.cpp * is compiled without for other CPU's. */ -#include "util/util_optimization.h" +#include "util/optimization.h" #ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 # define KERNEL_STUB @@ -32,6 +32,6 @@ # endif #endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 */ -#include "kernel/kernel.h" +#include "kernel/device/cpu/kernel.h" #define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" +#include "kernel/device/cpu/kernel_arch_impl.h" diff --git a/intern/cycles/kernel/kernel_compat_cuda.h b/intern/cycles/kernel/device/cuda/compat.h similarity index 55% rename from intern/cycles/kernel/kernel_compat_cuda.h rename to intern/cycles/kernel/device/cuda/compat.h index ea3b78b7cef..1ee82e6eb7c 100644 --- a/intern/cycles/kernel/kernel_compat_cuda.h +++ b/intern/cycles/kernel/device/cuda/compat.h @@ -14,20 +14,15 @@ * limitations under the License. */ -#ifndef __KERNEL_COMPAT_CUDA_H__ -#define __KERNEL_COMPAT_CUDA_H__ +#pragma once #define __KERNEL_GPU__ #define __KERNEL_CUDA__ #define CCL_NAMESPACE_BEGIN #define CCL_NAMESPACE_END -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL +#ifndef ATTR_FALLTHROUGH +# define ATTR_FALLTHROUGH #endif /* Manual definitions so we can compile without CUDA toolkit. */ @@ -38,8 +33,6 @@ typedef unsigned long long uint64_t; #else # include #endif -typedef unsigned short half; -typedef unsigned long long CUtexObject; #ifdef CYCLES_CUBIN_CC # define FLT_MIN 1.175494350822287507969e-38f @@ -47,14 +40,7 @@ typedef unsigned long long CUtexObject; # define FLT_EPSILON 1.192092896e-07F #endif -__device__ half __float2half(const float f) -{ - half val; - asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); - return val; -} - -/* Qualifier wrappers for different names on different devices */ +/* Qualifiers */ #define ccl_device __device__ __inline__ #if __CUDA_ARCH__ < 500 @@ -68,104 +54,60 @@ __device__ half __float2half(const float f) #define ccl_device_noinline_cpu ccl_device #define ccl_global #define ccl_static_constant __constant__ +#define ccl_device_constant __constant__ __device__ #define ccl_constant const -#define ccl_local __shared__ -#define ccl_local_param +#define ccl_gpu_shared __shared__ #define ccl_private #define ccl_may_alias -#define ccl_addr_space #define ccl_restrict __restrict__ #define ccl_loop_no_unroll -/* TODO(sergey): In theory we might use references with CUDA, however - * performance impact yet to be investigated. - */ -#define ccl_ref #define ccl_align(n) __align__(n) #define ccl_optional_struct_init -#define ATTR_FALLTHROUGH - -#define CCL_MAX_LOCAL_SIZE (CUDA_THREADS_BLOCK_WIDTH * CUDA_THREADS_BLOCK_WIDTH) - /* No assert supported for CUDA */ #define kernel_assert(cond) -/* Types */ +/* GPU thread, block, grid size and index */ -#include "util/util_half.h" -#include "util/util_types.h" +#define ccl_gpu_thread_idx_x (threadIdx.x) +#define ccl_gpu_block_dim_x (blockDim.x) +#define ccl_gpu_block_idx_x (blockIdx.x) +#define ccl_gpu_grid_dim_x (gridDim.x) +#define ccl_gpu_warp_size (warpSize) -/* Work item functions */ +#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) +#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) -ccl_device_inline uint ccl_local_id(uint d) +/* GPU warp synchronization. */ + +#define ccl_gpu_syncthreads() __syncthreads() +#define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) +#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down_sync(mask, var, detla) +#define ccl_gpu_popc(x) __popc(x) + +/* GPU texture objects */ + +typedef unsigned long long CUtexObject; +typedef CUtexObject ccl_gpu_tex_object; + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj, + const float x, + const float y) { - switch (d) { - case 0: - return threadIdx.x; - case 1: - return threadIdx.y; - case 2: - return threadIdx.z; - default: - return 0; - } + return tex2D(texobj, x, y); } -#define ccl_global_id(d) (ccl_group_id(d) * ccl_local_size(d) + ccl_local_id(d)) - -ccl_device_inline uint ccl_local_size(uint d) +template +ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj, + const float x, + const float y, + const float z) { - switch (d) { - case 0: - return blockDim.x; - case 1: - return blockDim.y; - case 2: - return blockDim.z; - default: - return 0; - } + return tex3D(texobj, x, y, z); } -#define ccl_global_size(d) (ccl_num_groups(d) * ccl_local_size(d)) - -ccl_device_inline uint ccl_group_id(uint d) -{ - switch (d) { - case 0: - return blockIdx.x; - case 1: - return blockIdx.y; - case 2: - return blockIdx.z; - default: - return 0; - } -} - -ccl_device_inline uint ccl_num_groups(uint d) -{ - switch (d) { - case 0: - return gridDim.x; - case 1: - return gridDim.y; - case 2: - return gridDim.z; - default: - return 0; - } -} - -/* Textures */ - -/* Use arrays for regular data. */ -#define kernel_tex_fetch(t, index) t[(index)] -#define kernel_tex_array(t) (t) - -#define kernel_data __data - /* Use fast math functions */ #define cosf(x) __cosf(((float)(x))) @@ -175,4 +117,25 @@ ccl_device_inline uint ccl_num_groups(uint d) #define logf(x) __logf(((float)(x))) #define expf(x) __expf(((float)(x))) -#endif /* __KERNEL_COMPAT_CUDA_H__ */ +/* Half */ + +typedef unsigned short half; + +__device__ half __float2half(const float f) +{ + half val; + asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); + return val; +} + +__device__ float __half2float(const half h) +{ + float val; + asm("{ cvt.f32.f16 %0, %1;}\n" : "=f"(val) : "h"(h)); + return val; +} + +/* Types */ + +#include "util/half.h" +#include "util/types.h" diff --git a/intern/cycles/kernel/device/cuda/config.h b/intern/cycles/kernel/device/cuda/config.h new file mode 100644 index 00000000000..46196dcdb51 --- /dev/null +++ b/intern/cycles/kernel/device/cuda/config.h @@ -0,0 +1,114 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Device data taken from CUDA occupancy calculator. + * + * Terminology + * - CUDA GPUs have multiple streaming multiprocessors + * - Each multiprocessor executes multiple thread blocks + * - Each thread block contains a number of threads, also known as the block size + * - Multiprocessors have a fixed number of registers, and the amount of registers + * used by each threads limits the number of threads per block. + */ + +/* 3.0 and 3.5 */ +#if __CUDA_ARCH__ == 300 || __CUDA_ARCH__ == 350 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 16 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 63 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +# define GPU_KERNEL_MAX_REGISTERS 63 + +/* 3.2 */ +#elif __CUDA_ARCH__ == 320 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 32768 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 16 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 63 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +# define GPU_KERNEL_MAX_REGISTERS 63 + +/* 3.7 */ +#elif __CUDA_ARCH__ == 370 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 16 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 255 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +# define GPU_KERNEL_MAX_REGISTERS 63 + +/* 5.x, 6.x */ +#elif __CUDA_ARCH__ <= 699 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 32 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 255 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 256 +/* CUDA 9.0 seems to cause slowdowns on high-end Pascal cards unless we increase the number of + * registers */ +# if __CUDACC_VER_MAJOR__ >= 9 && __CUDA_ARCH__ >= 600 +# define GPU_KERNEL_MAX_REGISTERS 64 +# else +# define GPU_KERNEL_MAX_REGISTERS 48 +# endif + +/* 7.x, 8.x */ +#elif __CUDA_ARCH__ <= 899 +# define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +# define GPU_MULTIPROCESSOR_MAX_BLOCKS 32 +# define GPU_BLOCK_MAX_THREADS 1024 +# define GPU_THREAD_MAX_REGISTERS 255 + +/* tunable parameters */ +# define GPU_KERNEL_BLOCK_NUM_THREADS 512 +# define GPU_KERNEL_MAX_REGISTERS 96 + +/* unknown architecture */ +#else +# error "Unknown or unsupported CUDA architecture, can't determine launch bounds" +#endif + +/* Compute number of threads per block and minimum blocks per multiprocessor + * given the maximum number of registers per thread. */ + +#define ccl_gpu_kernel(block_num_threads, thread_num_registers) \ + extern "C" __global__ void __launch_bounds__(block_num_threads, \ + GPU_MULTIPRESSOR_MAX_REGISTERS / \ + (block_num_threads * thread_num_registers)) + +/* sanity checks */ + +#if GPU_KERNEL_BLOCK_NUM_THREADS > GPU_BLOCK_MAX_THREADS +# error "Maximum number of threads per block exceeded" +#endif + +#if GPU_MULTIPRESSOR_MAX_REGISTERS / (GPU_KERNEL_BLOCK_NUM_THREADS * GPU_KERNEL_MAX_REGISTERS) > \ + GPU_MULTIPROCESSOR_MAX_BLOCKS +# error "Maximum number of blocks per multiprocessor exceeded" +#endif + +#if GPU_KERNEL_MAX_REGISTERS > GPU_THREAD_MAX_REGISTERS +# error "Maximum number of registers per thread exceeded" +#endif diff --git a/intern/cycles/kernel/device/cuda/globals.h b/intern/cycles/kernel/device/cuda/globals.h new file mode 100644 index 00000000000..e5023fad40c --- /dev/null +++ b/intern/cycles/kernel/device/cuda/globals.h @@ -0,0 +1,50 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/types.h" + +#include "kernel/integrator/state.h" + +#include "kernel/util/profiling.h" + +CCL_NAMESPACE_BEGIN + +/* Not actually used, just a NULL pointer that gets passed everywhere, which we + * hope gets optimized out by the compiler. */ +struct KernelGlobalsGPU { + int unused[1]; +}; +typedef ccl_global const KernelGlobalsGPU *ccl_restrict KernelGlobals; + +/* Global scene data and textures */ +__constant__ KernelData __data; +#define KERNEL_TEX(type, name) const __constant__ __device__ type *name; +#include "kernel/textures.h" + +/* Integrator state */ +__constant__ IntegratorStateGPU __integrator_state; + +/* Abstraction macros */ +#define kernel_data __data +#define kernel_tex_fetch(t, index) t[(index)] +#define kernel_tex_array(t) (t) +#define kernel_integrator_state __integrator_state + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_math.h b/intern/cycles/kernel/device/cuda/kernel.cu similarity index 66% rename from intern/cycles/kernel/kernel_math.h rename to intern/cycles/kernel/device/cuda/kernel.cu index 96391db7649..e26fe243642 100644 --- a/intern/cycles/kernel/kernel_math.h +++ b/intern/cycles/kernel/device/cuda/kernel.cu @@ -14,15 +14,15 @@ * limitations under the License. */ -#ifndef __KERNEL_MATH_H__ -#define __KERNEL_MATH_H__ +/* CUDA kernel entry points */ -#include "util/util_color.h" -#include "util/util_math.h" -#include "util/util_math_fast.h" -#include "util/util_math_intersect.h" -#include "util/util_projection.h" -#include "util/util_texture.h" -#include "util/util_transform.h" +#ifdef __CUDA_ARCH__ -#endif /* __KERNEL_MATH_H__ */ +# include "kernel/device/cuda/compat.h" +# include "kernel/device/cuda/config.h" +# include "kernel/device/cuda/globals.h" + +# include "kernel/device/gpu/image.h" +# include "kernel/device/gpu/kernel.h" + +#endif diff --git a/intern/cycles/kernel/kernels/cuda/kernel_cuda_image.h b/intern/cycles/kernel/device/gpu/image.h similarity index 78% rename from intern/cycles/kernel/kernels/cuda/kernel_cuda_image.h rename to intern/cycles/kernel/device/gpu/image.h index 132653fa7ca..95a37c693ae 100644 --- a/intern/cycles/kernel/kernels/cuda/kernel_cuda_image.h +++ b/intern/cycles/kernel/device/gpu/image.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +CCL_NAMESPACE_BEGIN + #ifdef WITH_NANOVDB # define NDEBUG /* Disable "assert" in device code */ # define NANOVDB_USE_INTRINSICS @@ -61,9 +65,9 @@ ccl_device float cubic_h1(float a) /* Fast bicubic texture lookup using 4 bilinear lookups, adapted from CUDA samples. */ template -ccl_device T kernel_tex_image_interp_bicubic(const TextureInfo &info, float x, float y) +ccl_device_noinline T kernel_tex_image_interp_bicubic(const TextureInfo &info, float x, float y) { - CUtexObject tex = (CUtexObject)info.data; + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; x = (x * info.width) - 0.5f; y = (y * info.height) - 0.5f; @@ -81,15 +85,18 @@ ccl_device T kernel_tex_image_interp_bicubic(const TextureInfo &info, float x, f float y0 = (py + cubic_h0(fy) + 0.5f) / info.height; float y1 = (py + cubic_h1(fy) + 0.5f) / info.height; - return cubic_g0(fy) * (g0x * tex2D(tex, x0, y0) + g1x * tex2D(tex, x1, y0)) + - cubic_g1(fy) * (g0x * tex2D(tex, x0, y1) + g1x * tex2D(tex, x1, y1)); + return cubic_g0(fy) * (g0x * ccl_gpu_tex_object_read_2D(tex, x0, y0) + + g1x * ccl_gpu_tex_object_read_2D(tex, x1, y0)) + + cubic_g1(fy) * (g0x * ccl_gpu_tex_object_read_2D(tex, x0, y1) + + g1x * ccl_gpu_tex_object_read_2D(tex, x1, y1)); } /* Fast tricubic texture lookup using 8 trilinear lookups. */ template -ccl_device T kernel_tex_image_interp_tricubic(const TextureInfo &info, float x, float y, float z) +ccl_device_noinline T +kernel_tex_image_interp_tricubic(const TextureInfo &info, float x, float y, float z) { - CUtexObject tex = (CUtexObject)info.data; + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; x = (x * info.width) - 0.5f; y = (y * info.height) - 0.5f; @@ -117,10 +124,14 @@ ccl_device T kernel_tex_image_interp_tricubic(const TextureInfo &info, float x, float z0 = (pz + cubic_h0(fz) + 0.5f) / info.depth; float z1 = (pz + cubic_h1(fz) + 0.5f) / info.depth; - return g0z * (g0y * (g0x * tex3D(tex, x0, y0, z0) + g1x * tex3D(tex, x1, y0, z0)) + - g1y * (g0x * tex3D(tex, x0, y1, z0) + g1x * tex3D(tex, x1, y1, z0))) + - g1z * (g0y * (g0x * tex3D(tex, x0, y0, z1) + g1x * tex3D(tex, x1, y0, z1)) + - g1y * (g0x * tex3D(tex, x0, y1, z1) + g1x * tex3D(tex, x1, y1, z1))); + return g0z * (g0y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y0, z0) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y0, z0)) + + g1y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y1, z0) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y1, z0))) + + g1z * (g0y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y0, z1) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y0, z1)) + + g1y * (g0x * ccl_gpu_tex_object_read_3D(tex, x0, y1, z1) + + g1x * ccl_gpu_tex_object_read_3D(tex, x1, y1, z1))); } #ifdef WITH_NANOVDB @@ -157,7 +168,7 @@ ccl_device T kernel_tex_image_interp_tricubic_nanovdb(S &s, float x, float y, fl } template -ccl_device_inline T kernel_tex_image_interp_nanovdb( +ccl_device_noinline T kernel_tex_image_interp_nanovdb( const TextureInfo &info, float x, float y, float z, uint interpolation) { using namespace nanovdb; @@ -178,7 +189,7 @@ ccl_device_inline T kernel_tex_image_interp_nanovdb( } #endif -ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y) +ccl_device float4 kernel_tex_image_interp(KernelGlobals kg, int id, float x, float y) { const TextureInfo &info = kernel_tex_fetch(__texture_info, id); @@ -190,8 +201,8 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, fl return kernel_tex_image_interp_bicubic(info, x, y); } else { - CUtexObject tex = (CUtexObject)info.data; - return tex2D(tex, x, y); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + return ccl_gpu_tex_object_read_2D(tex, x, y); } } /* float, byte and half */ @@ -202,15 +213,15 @@ ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, fl f = kernel_tex_image_interp_bicubic(info, x, y); } else { - CUtexObject tex = (CUtexObject)info.data; - f = tex2D(tex, x, y); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + f = ccl_gpu_tex_object_read_2D(tex, x, y); } return make_float4(f, f, f, 1.0f); } } -ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, +ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals kg, int id, float3 P, InterpolationType interp) @@ -245,8 +256,8 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, return kernel_tex_image_interp_tricubic(info, x, y, z); } else { - CUtexObject tex = (CUtexObject)info.data; - return tex3D(tex, x, y, z); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + return ccl_gpu_tex_object_read_3D(tex, x, y, z); } } else { @@ -256,10 +267,12 @@ ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, f = kernel_tex_image_interp_tricubic(info, x, y, z); } else { - CUtexObject tex = (CUtexObject)info.data; - f = tex3D(tex, x, y, z); + ccl_gpu_tex_object tex = (ccl_gpu_tex_object)info.data; + f = ccl_gpu_tex_object_read_3D(tex, x, y, z); } return make_float4(f, f, f, 1.0f); } } + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/kernel.h b/intern/cycles/kernel/device/gpu/kernel.h new file mode 100644 index 00000000000..f86a8c692aa --- /dev/null +++ b/intern/cycles/kernel/device/gpu/kernel.h @@ -0,0 +1,911 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Common GPU kernels. */ + +#include "kernel/device/gpu/parallel_active_index.h" +#include "kernel/device/gpu/parallel_prefix_sum.h" +#include "kernel/device/gpu/parallel_sorted_index.h" +#include "kernel/device/gpu/work_stealing.h" + +#include "kernel/integrator/state.h" +#include "kernel/integrator/state_flow.h" +#include "kernel/integrator/state_util.h" + +#include "kernel/integrator/init_from_bake.h" +#include "kernel/integrator/init_from_camera.h" +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/intersect_shadow.h" +#include "kernel/integrator/intersect_subsurface.h" +#include "kernel/integrator/intersect_volume_stack.h" +#include "kernel/integrator/shade_background.h" +#include "kernel/integrator/shade_light.h" +#include "kernel/integrator/shade_shadow.h" +#include "kernel/integrator/shade_surface.h" +#include "kernel/integrator/shade_volume.h" + +#include "kernel/bake/bake.h" + +#include "kernel/film/adaptive_sampling.h" +#include "kernel/film/read.h" + +/* -------------------------------------------------------------------- + * Integrator. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_reset(int num_states) +{ + const int state = ccl_gpu_global_id_x(); + + if (state < num_states) { + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_init_from_camera(KernelWorkTile *tiles, + const int num_tiles, + float *render_buffer, + const int max_tile_work_size) +{ + const int work_index = ccl_gpu_global_id_x(); + + if (work_index >= max_tile_work_size * num_tiles) { + return; + } + + const int tile_index = work_index / max_tile_work_size; + const int tile_work_index = work_index - tile_index * max_tile_work_size; + + const KernelWorkTile *tile = &tiles[tile_index]; + + if (tile_work_index >= tile->work_size) { + return; + } + + const int state = tile->path_index_offset + tile_work_index; + + uint x, y, sample; + get_work_pixel(tile, tile_work_index, &x, &y, &sample); + + integrator_init_from_camera(nullptr, state, tile, render_buffer, x, y, sample); +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_init_from_bake(KernelWorkTile *tiles, + const int num_tiles, + float *render_buffer, + const int max_tile_work_size) +{ + const int work_index = ccl_gpu_global_id_x(); + + if (work_index >= max_tile_work_size * num_tiles) { + return; + } + + const int tile_index = work_index / max_tile_work_size; + const int tile_work_index = work_index - tile_index * max_tile_work_size; + + const KernelWorkTile *tile = &tiles[tile_index]; + + if (tile_work_index >= tile->work_size) { + return; + } + + const int state = tile->path_index_offset + tile_work_index; + + uint x, y, sample; + get_work_pixel(tile, tile_work_index, &x, &y, &sample); + + integrator_init_from_bake(nullptr, state, tile, render_buffer, x, y, sample); +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_closest(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_closest(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_shadow(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_shadow(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_subsurface(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_subsurface(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_intersect_volume_stack(const int *path_index_array, const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_intersect_volume_stack(NULL, state); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_background(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_background(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_light(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_light(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_shadow(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_shadow(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_surface(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_surface(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_surface_raytrace(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_surface_raytrace(NULL, state, render_buffer); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shade_volume(const int *path_index_array, + float *render_buffer, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int state = (path_index_array) ? path_index_array[global_index] : global_index; + integrator_shade_volume(NULL, state, render_buffer); + } +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_queued_paths_array(int num_states, + int *indices, + int *num_indices, + int kernel) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [kernel](const int state) { + return (INTEGRATOR_STATE(state, path, queued_kernel) == kernel); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_queued_shadow_paths_array(int num_states, + int *indices, + int *num_indices, + int kernel) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [kernel](const int state) { + return (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == kernel); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_active_paths_array(int num_states, int *indices, int *num_indices) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [](const int state) { + return (INTEGRATOR_STATE(state, path, queued_kernel) != 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_terminated_paths_array(int num_states, + int *indices, + int *num_indices, + int indices_offset) +{ + gpu_parallel_active_index_array( + num_states, indices + indices_offset, num_indices, [](const int state) { + return (INTEGRATOR_STATE(state, path, queued_kernel) == 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_terminated_shadow_paths_array(int num_states, + int *indices, + int *num_indices, + int indices_offset) +{ + gpu_parallel_active_index_array( + num_states, indices + indices_offset, num_indices, [](const int state) { + return (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_sorted_paths_array(int num_states, + int num_states_limit, + int *indices, + int *num_indices, + int *key_counter, + int *key_prefix_sum, + int kernel) +{ + gpu_parallel_sorted_index_array( + num_states, + num_states_limit, + indices, + num_indices, + key_counter, + key_prefix_sum, + [kernel](const int state) { + return (INTEGRATOR_STATE(state, path, queued_kernel) == kernel) ? + INTEGRATOR_STATE(state, path, shader_sort_key) : + GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY; + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_paths_array(int num_states, + int *indices, + int *num_indices, + int num_active_paths) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [num_active_paths](const int state) { + return (state >= num_active_paths) && (INTEGRATOR_STATE(state, path, queued_kernel) != 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_states(const int *active_terminated_states, + const int active_states_offset, + const int terminated_states_offset, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int from_state = active_terminated_states[active_states_offset + global_index]; + const int to_state = active_terminated_states[terminated_states_offset + global_index]; + + integrator_state_move(NULL, to_state, from_state); + } +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_shadow_paths_array(int num_states, + int *indices, + int *num_indices, + int num_active_paths) +{ + gpu_parallel_active_index_array( + num_states, indices, num_indices, [num_active_paths](const int state) { + return (state >= num_active_paths) && + (INTEGRATOR_STATE(state, shadow_path, queued_kernel) != 0); + }); +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE) + kernel_gpu_integrator_compact_shadow_states(const int *active_terminated_states, + const int active_states_offset, + const int terminated_states_offset, + const int work_size) +{ + const int global_index = ccl_gpu_global_id_x(); + + if (global_index < work_size) { + const int from_state = active_terminated_states[active_states_offset + global_index]; + const int to_state = active_terminated_states[terminated_states_offset + global_index]; + + integrator_shadow_state_move(NULL, to_state, from_state); + } +} + +extern "C" __global__ void __launch_bounds__(GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE) + kernel_gpu_prefix_sum(int *counter, int *prefix_sum, int num_values) +{ + gpu_parallel_prefix_sum( + counter, prefix_sum, num_values); +} + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_adaptive_sampling_convergence_check(float *render_buffer, + int sx, + int sy, + int sw, + int sh, + float threshold, + bool reset, + int offset, + int stride, + uint *num_active_pixels) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / sw; + const int x = work_index - y * sw; + + bool converged = true; + + if (x < sw && y < sh) { + converged = kernel_adaptive_sampling_convergence_check( + nullptr, render_buffer, sx + x, sy + y, threshold, reset, offset, stride); + } + + /* NOTE: All threads specified in the mask must execute the intrinsic. */ + const uint num_active_pixels_mask = ccl_gpu_ballot(!converged); + const int lane_id = ccl_gpu_thread_idx_x % ccl_gpu_warp_size; + if (lane_id == 0) { + atomic_fetch_and_add_uint32(num_active_pixels, __popc(num_active_pixels_mask)); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_adaptive_sampling_filter_x( + float *render_buffer, int sx, int sy, int sw, int sh, int offset, int stride) +{ + const int y = ccl_gpu_global_id_x(); + + if (y < sh) { + kernel_adaptive_sampling_filter_x(NULL, render_buffer, sy + y, sx, sw, offset, stride); + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_adaptive_sampling_filter_y( + float *render_buffer, int sx, int sy, int sw, int sh, int offset, int stride) +{ + const int x = ccl_gpu_global_id_x(); + + if (x < sw) { + kernel_adaptive_sampling_filter_y(NULL, render_buffer, sx + x, sy, sh, offset, stride); + } +} + +/* -------------------------------------------------------------------- + * Cryptomatte. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_cryptomatte_postprocess(float *render_buffer, int num_pixels) +{ + const int pixel_index = ccl_gpu_global_id_x(); + + if (pixel_index < num_pixels) { + kernel_cryptomatte_post(nullptr, render_buffer, pixel_index); + } +} + +/* -------------------------------------------------------------------- + * Film. + */ + +/* Common implementation for float destination. */ +template +ccl_device_inline void kernel_gpu_film_convert_common(const KernelFilmConvert *kfilm_convert, + float *pixels, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int dst_offset, + int dst_stride, + const Processor &processor) +{ + const int render_pixel_index = ccl_gpu_global_id_x(); + if (render_pixel_index >= num_pixels) { + return; + } + + const int x = render_pixel_index % width; + const int y = render_pixel_index / width; + + ccl_global const float *buffer = render_buffer + offset + x * kfilm_convert->pass_stride + + y * stride * kfilm_convert->pass_stride; + + ccl_global float *pixel = pixels + + (render_pixel_index + dst_offset) * kfilm_convert->pixel_stride; + + processor(kfilm_convert, buffer, pixel); +} + +/* Common implementation for half4 destination and 4-channel input pass. */ +template +ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_rgba( + const KernelFilmConvert *kfilm_convert, + uchar4 *rgba, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int rgba_offset, + int rgba_stride, + const Processor &processor) +{ + const int render_pixel_index = ccl_gpu_global_id_x(); + if (render_pixel_index >= num_pixels) { + return; + } + + const int x = render_pixel_index % width; + const int y = render_pixel_index / width; + + ccl_global const float *buffer = render_buffer + offset + x * kfilm_convert->pass_stride + + y * stride * kfilm_convert->pass_stride; + + float pixel[4]; + processor(kfilm_convert, buffer, pixel); + + film_apply_pass_pixel_overlays_rgba(kfilm_convert, buffer, pixel); + + ccl_global half4 *out = ((ccl_global half4 *)rgba) + rgba_offset + y * rgba_stride + x; + *out = float4_to_half4_display(make_float4(pixel[0], pixel[1], pixel[2], pixel[3])); +} + +/* Common implementation for half4 destination and 3-channel input pass. */ +template +ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_rgb( + const KernelFilmConvert *kfilm_convert, + uchar4 *rgba, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int rgba_offset, + int rgba_stride, + const Processor &processor) +{ + kernel_gpu_film_convert_half_rgba_common_rgba( + kfilm_convert, + rgba, + render_buffer, + num_pixels, + width, + offset, + stride, + rgba_offset, + rgba_stride, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + processor(kfilm_convert, buffer, pixel_rgba); + pixel_rgba[3] = 1.0f; + }); +} + +/* Common implementation for half4 destination and single channel input pass. */ +template +ccl_device_inline void kernel_gpu_film_convert_half_rgba_common_value( + const KernelFilmConvert *kfilm_convert, + uchar4 *rgba, + float *render_buffer, + int num_pixels, + int width, + int offset, + int stride, + int rgba_offset, + int rgba_stride, + const Processor &processor) +{ + kernel_gpu_film_convert_half_rgba_common_rgba( + kfilm_convert, + rgba, + render_buffer, + num_pixels, + width, + offset, + stride, + rgba_offset, + rgba_stride, + [&processor](const KernelFilmConvert *kfilm_convert, + ccl_global const float *buffer, + float *pixel_rgba) { + float value; + processor(kfilm_convert, buffer, &value); + + pixel_rgba[0] = value; + pixel_rgba[1] = value; + pixel_rgba[2] = value; + pixel_rgba[3] = 1.0f; + }); +} + +#define KERNEL_FILM_CONVERT_PROC(name) \ + ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) name + +#define KERNEL_FILM_CONVERT_DEFINE(variant, channels) \ + KERNEL_FILM_CONVERT_PROC(kernel_gpu_film_convert_##variant) \ + (const KernelFilmConvert kfilm_convert, \ + float *pixels, \ + float *render_buffer, \ + int num_pixels, \ + int width, \ + int offset, \ + int stride, \ + int rgba_offset, \ + int rgba_stride) \ + { \ + kernel_gpu_film_convert_common(&kfilm_convert, \ + pixels, \ + render_buffer, \ + num_pixels, \ + width, \ + offset, \ + stride, \ + rgba_offset, \ + rgba_stride, \ + film_get_pass_pixel_##variant); \ + } \ + KERNEL_FILM_CONVERT_PROC(kernel_gpu_film_convert_##variant##_half_rgba) \ + (const KernelFilmConvert kfilm_convert, \ + uchar4 *rgba, \ + float *render_buffer, \ + int num_pixels, \ + int width, \ + int offset, \ + int stride, \ + int rgba_offset, \ + int rgba_stride) \ + { \ + kernel_gpu_film_convert_half_rgba_common_##channels(&kfilm_convert, \ + rgba, \ + render_buffer, \ + num_pixels, \ + width, \ + offset, \ + stride, \ + rgba_offset, \ + rgba_stride, \ + film_get_pass_pixel_##variant); \ + } + +KERNEL_FILM_CONVERT_DEFINE(depth, value) +KERNEL_FILM_CONVERT_DEFINE(mist, value) +KERNEL_FILM_CONVERT_DEFINE(sample_count, value) +KERNEL_FILM_CONVERT_DEFINE(float, value) + +KERNEL_FILM_CONVERT_DEFINE(light_path, rgb) +KERNEL_FILM_CONVERT_DEFINE(float3, rgb) + +KERNEL_FILM_CONVERT_DEFINE(motion, rgba) +KERNEL_FILM_CONVERT_DEFINE(cryptomatte, rgba) +KERNEL_FILM_CONVERT_DEFINE(shadow_catcher, rgba) +KERNEL_FILM_CONVERT_DEFINE(shadow_catcher_matte_with_shadow, rgba) +KERNEL_FILM_CONVERT_DEFINE(combined, rgba) +KERNEL_FILM_CONVERT_DEFINE(float4, rgba) + +#undef KERNEL_FILM_CONVERT_DEFINE +#undef KERNEL_FILM_CONVERT_HALF_RGBA_DEFINE +#undef KERNEL_FILM_CONVERT_PROC + +/* -------------------------------------------------------------------- + * Shader evaluation. + */ + +/* Displacement */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_shader_eval_displace(KernelShaderEvalInput *input, + float *output, + const int offset, + const int work_size) +{ + int i = ccl_gpu_global_id_x(); + if (i < work_size) { + kernel_displace_evaluate(NULL, input, output, offset + i); + } +} + +/* Background */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_shader_eval_background(KernelShaderEvalInput *input, + float *output, + const int offset, + const int work_size) +{ + int i = ccl_gpu_global_id_x(); + if (i < work_size) { + kernel_background_evaluate(NULL, input, output, offset + i); + } +} + +/* Curve Shadow Transparency */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_shader_eval_curve_shadow_transparency(KernelShaderEvalInput *input, + float *output, + const int offset, + const int work_size) +{ + int i = ccl_gpu_global_id_x(); + if (i < work_size) { + kernel_curve_shadow_transparency_evaluate(NULL, input, output, offset + i); + } +} + +/* -------------------------------------------------------------------- + * Denoising. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_color_preprocess(float *render_buffer, + int full_x, + int full_y, + int width, + int height, + int offset, + int stride, + int pass_stride, + int pass_denoised) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t render_pixel_index = offset + (x + full_x) + (y + full_y) * stride; + float *buffer = render_buffer + render_pixel_index * pass_stride; + + float *color_out = buffer + pass_denoised; + color_out[0] = clamp(color_out[0], 0.0f, 10000.0f); + color_out[1] = clamp(color_out[1], 0.0f, 10000.0f); + color_out[2] = clamp(color_out[2], 0.0f, 10000.0f); +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_guiding_preprocess(float *guiding_buffer, + int guiding_pass_stride, + int guiding_pass_albedo, + int guiding_pass_normal, + const float *render_buffer, + int render_offset, + int render_stride, + int render_pass_stride, + int render_pass_sample_count, + int render_pass_denoising_albedo, + int render_pass_denoising_normal, + int full_x, + int full_y, + int width, + int height, + int num_samples) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t guiding_pixel_index = x + y * width; + float *guiding_pixel = guiding_buffer + guiding_pixel_index * guiding_pass_stride; + + const uint64_t render_pixel_index = render_offset + (x + full_x) + (y + full_y) * render_stride; + const float *buffer = render_buffer + render_pixel_index * render_pass_stride; + + float pixel_scale; + if (render_pass_sample_count == PASS_UNUSED) { + pixel_scale = 1.0f / num_samples; + } + else { + pixel_scale = 1.0f / __float_as_uint(buffer[render_pass_sample_count]); + } + + /* Albedo pass. */ + if (guiding_pass_albedo != PASS_UNUSED) { + kernel_assert(render_pass_denoising_albedo != PASS_UNUSED); + + const float *aledo_in = buffer + render_pass_denoising_albedo; + float *albedo_out = guiding_pixel + guiding_pass_albedo; + + albedo_out[0] = aledo_in[0] * pixel_scale; + albedo_out[1] = aledo_in[1] * pixel_scale; + albedo_out[2] = aledo_in[2] * pixel_scale; + } + + /* Normal pass. */ + if (render_pass_denoising_normal != PASS_UNUSED) { + kernel_assert(render_pass_denoising_normal != PASS_UNUSED); + + const float *normal_in = buffer + render_pass_denoising_normal; + float *normal_out = guiding_pixel + guiding_pass_normal; + + normal_out[0] = normal_in[0] * pixel_scale; + normal_out[1] = normal_in[1] * pixel_scale; + normal_out[2] = normal_in[2] * pixel_scale; + } +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_guiding_set_fake_albedo(float *guiding_buffer, + int guiding_pass_stride, + int guiding_pass_albedo, + int width, + int height) +{ + kernel_assert(guiding_pass_albedo != PASS_UNUSED); + + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t guiding_pixel_index = x + y * width; + float *guiding_pixel = guiding_buffer + guiding_pixel_index * guiding_pass_stride; + + float *albedo_out = guiding_pixel + guiding_pass_albedo; + + albedo_out[0] = 0.5f; + albedo_out[1] = 0.5f; + albedo_out[2] = 0.5f; +} + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_filter_color_postprocess(float *render_buffer, + int full_x, + int full_y, + int width, + int height, + int offset, + int stride, + int pass_stride, + int num_samples, + int pass_noisy, + int pass_denoised, + int pass_sample_count, + int num_components, + bool use_compositing) +{ + const int work_index = ccl_gpu_global_id_x(); + const int y = work_index / width; + const int x = work_index - y * width; + + if (x >= width || y >= height) { + return; + } + + const uint64_t render_pixel_index = offset + (x + full_x) + (y + full_y) * stride; + float *buffer = render_buffer + render_pixel_index * pass_stride; + + float pixel_scale; + if (pass_sample_count == PASS_UNUSED) { + pixel_scale = num_samples; + } + else { + pixel_scale = __float_as_uint(buffer[pass_sample_count]); + } + + float *denoised_pixel = buffer + pass_denoised; + + denoised_pixel[0] *= pixel_scale; + denoised_pixel[1] *= pixel_scale; + denoised_pixel[2] *= pixel_scale; + + if (num_components == 3) { + /* Pass without alpha channel. */ + } + else if (!use_compositing) { + /* Currently compositing passes are either 3-component (derived by dividing light passes) + * or do not have transparency (shadow catcher). Implicitly rely on this logic, as it + * simplifies logic and avoids extra memory allocation. */ + const float *noisy_pixel = buffer + pass_noisy; + denoised_pixel[3] = noisy_pixel[3]; + } + else { + /* Assigning to zero since this is a default alpha value for 3-component passes, and it + * is an opaque pixel for 4 component passes. */ + + denoised_pixel[3] = 0; + } +} + +/* -------------------------------------------------------------------- + * Shadow catcher. + */ + +ccl_gpu_kernel(GPU_KERNEL_BLOCK_NUM_THREADS, GPU_KERNEL_MAX_REGISTERS) + kernel_gpu_integrator_shadow_catcher_count_possible_splits(int num_states, + uint *num_possible_splits) +{ + const int state = ccl_gpu_global_id_x(); + + bool can_split = false; + + if (state < num_states) { + can_split = kernel_shadow_catcher_path_can_split(nullptr, state); + } + + /* NOTE: All threads specified in the mask must execute the intrinsic. */ + const uint can_split_mask = ccl_gpu_ballot(can_split); + const int lane_id = ccl_gpu_thread_idx_x % ccl_gpu_warp_size; + if (lane_id == 0) { + atomic_fetch_and_add_uint32(num_possible_splits, __popc(can_split_mask)); + } +} diff --git a/intern/cycles/kernel/device/gpu/parallel_active_index.h b/intern/cycles/kernel/device/gpu/parallel_active_index.h new file mode 100644 index 00000000000..d7416beb783 --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_active_index.h @@ -0,0 +1,87 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Given an array of states, build an array of indices for which the states + * are active. + * + * Shared memory requirement is `sizeof(int) * (number_of_warps + 1)`. */ + +#include "util/atomic.h" + +#ifdef __HIP__ +# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_ACTIVE_INDEX_DEFAULT_BLOCK_SIZE 512 +#endif + +template +__device__ void gpu_parallel_active_index_array(const uint num_states, + int *indices, + int *num_indices, + IsActiveOp is_active_op) +{ + extern ccl_gpu_shared int warp_offset[]; + + const uint thread_index = ccl_gpu_thread_idx_x; + const uint thread_warp = thread_index % ccl_gpu_warp_size; + + const uint warp_index = thread_index / ccl_gpu_warp_size; + const uint num_warps = blocksize / ccl_gpu_warp_size; + + /* Test if state corresponding to this thread is active. */ + const uint state_index = ccl_gpu_block_idx_x * blocksize + thread_index; + const uint is_active = (state_index < num_states) ? is_active_op(state_index) : 0; + + /* For each thread within a warp compute how many other active states precede it. */ + const uint thread_mask = 0xFFFFFFFF >> (ccl_gpu_warp_size - thread_warp); + const uint thread_offset = ccl_gpu_popc(ccl_gpu_ballot(is_active) & thread_mask); + + /* Last thread in warp stores number of active states for each warp. */ + if (thread_warp == ccl_gpu_warp_size - 1) { + warp_offset[warp_index] = thread_offset + is_active; + } + + ccl_gpu_syncthreads(); + + /* Last thread in block converts per-warp sizes to offsets, increments global size of + * index array and gets offset to write to. */ + if (thread_index == blocksize - 1) { + /* TODO: parallelize this. */ + int offset = 0; + for (int i = 0; i < num_warps; i++) { + int num_active = warp_offset[i]; + warp_offset[i] = offset; + offset += num_active; + } + + const uint block_num_active = warp_offset[warp_index] + thread_offset + is_active; + warp_offset[num_warps] = atomic_fetch_and_add_uint32(num_indices, block_num_active); + } + + ccl_gpu_syncthreads(); + + /* Write to index array. */ + if (is_active) { + const uint block_offset = warp_offset[num_warps]; + indices[block_offset + warp_offset[warp_index] + thread_offset] = state_index; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h new file mode 100644 index 00000000000..6de3a022569 --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_prefix_sum.h @@ -0,0 +1,52 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Parallel prefix sum. + * + * TODO: actually make this work in parallel. + * + * This is used for an array the size of the number of shaders in the scene + * which is not usually huge, so might not be a significant bottleneck. */ + +#include "util/atomic.h" + +#ifdef __HIP__ +# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_PREFIX_SUM_DEFAULT_BLOCK_SIZE 512 +#endif + +template +__device__ void gpu_parallel_prefix_sum(int *counter, int *prefix_sum, const int num_values) +{ + if (!(ccl_gpu_block_idx_x == 0 && ccl_gpu_thread_idx_x == 0)) { + return; + } + + int offset = 0; + for (int i = 0; i < num_values; i++) { + const int new_offset = offset + counter[i]; + prefix_sum[i] = offset; + counter[i] = 0; + offset = new_offset; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/parallel_reduce.h b/intern/cycles/kernel/device/gpu/parallel_reduce.h new file mode 100644 index 00000000000..b60dceb2ed0 --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_reduce.h @@ -0,0 +1,87 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Parallel sum of array input_data with size n into output_sum. + * + * Adapted from "Optimizing Parallel Reduction in GPU", Mark Harris. + * + * This version adds multiple elements per thread sequentially. This reduces + * the overall cost of the algorithm while keeping the work complexity O(n) and + * the step complexity O(log n). (Brent's Theorem optimization) */ + +#ifdef __HIP__ +# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_SUM_DEFAULT_BLOCK_SIZE 512 +#endif + +template +__device__ void gpu_parallel_sum( + const InputT *input_data, const uint n, OutputT *output_sum, OutputT zero, ConvertOp convert) +{ + extern ccl_gpu_shared OutputT shared_data[]; + + const uint tid = ccl_gpu_thread_idx_x; + const uint gridsize = blocksize * ccl_gpu_grid_dim_x(); + + OutputT sum = zero; + for (uint i = ccl_gpu_block_idx_x * blocksize + tid; i < n; i += gridsize) { + sum += convert(input_data[i]); + } + shared_data[tid] = sum; + + ccl_gpu_syncthreads(); + + if (blocksize >= 512 && tid < 256) { + shared_data[tid] = sum = sum + shared_data[tid + 256]; + } + + ccl_gpu_syncthreads(); + + if (blocksize >= 256 && tid < 128) { + shared_data[tid] = sum = sum + shared_data[tid + 128]; + } + + ccl_gpu_syncthreads(); + + if (blocksize >= 128 && tid < 64) { + shared_data[tid] = sum = sum + shared_data[tid + 64]; + } + + ccl_gpu_syncthreads(); + + if (blocksize >= 64 && tid < 32) { + shared_data[tid] = sum = sum + shared_data[tid + 32]; + } + + ccl_gpu_syncthreads(); + + if (tid < 32) { + for (int offset = ccl_gpu_warp_size / 2; offset > 0; offset /= 2) { + sum += ccl_shfl_down_sync(0xFFFFFFFF, sum, offset); + } + } + + if (tid == 0) { + output_sum[ccl_gpu_block_idx_x] = sum; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/parallel_sorted_index.h b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h new file mode 100644 index 00000000000..c06d7be444f --- /dev/null +++ b/intern/cycles/kernel/device/gpu/parallel_sorted_index.h @@ -0,0 +1,63 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Given an array of states, build an array of indices for which the states + * are active and sorted by a given key. The prefix sum of the number of active + * states per key must have already been computed. + * + * TODO: there may be ways to optimize this to avoid this many atomic ops? */ + +#include "util/atomic.h" + +#ifdef __HIP__ +# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 1024 +#else +# define GPU_PARALLEL_SORTED_INDEX_DEFAULT_BLOCK_SIZE 512 +#endif +#define GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY (~0) + +template +__device__ void gpu_parallel_sorted_index_array(const uint num_states, + const int num_states_limit, + int *indices, + int *num_indices, + int *key_counter, + int *key_prefix_sum, + GetKeyOp get_key_op) +{ + const uint state_index = ccl_gpu_block_idx_x * blocksize + ccl_gpu_thread_idx_x; + const int key = (state_index < num_states) ? get_key_op(state_index) : + GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY; + + if (key != GPU_PARALLEL_SORTED_INDEX_INACTIVE_KEY) { + const uint index = atomic_fetch_and_add_uint32(&key_prefix_sum[key], 1); + if (index < num_states_limit) { + /* Assign state index. */ + indices[index] = state_index; + } + else { + /* Can't process this state now, increase the counter again so that + * it will be handled in another iteration. */ + atomic_fetch_and_add_uint32(&key_counter[key], 1); + } + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/gpu/work_stealing.h b/intern/cycles/kernel/device/gpu/work_stealing.h new file mode 100644 index 00000000000..fab0915c38e --- /dev/null +++ b/intern/cycles/kernel/device/gpu/work_stealing.h @@ -0,0 +1,52 @@ +/* + * Copyright 2011-2015 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* + * Utility functions for work stealing + */ + +/* Map global work index to tile, pixel X/Y and sample. */ +ccl_device_inline void get_work_pixel(ccl_global const KernelWorkTile *tile, + uint global_work_index, + ccl_private uint *x, + ccl_private uint *y, + ccl_private uint *sample) +{ +#if 0 + /* Keep threads for the same sample together. */ + uint tile_pixels = tile->w * tile->h; + uint sample_offset = global_work_index / tile_pixels; + uint pixel_offset = global_work_index - sample_offset * tile_pixels; +#else + /* Keeping threads for the same pixel together. + * Appears to improve performance by a few % on CUDA and OptiX. */ + uint sample_offset = global_work_index % tile->num_samples; + uint pixel_offset = global_work_index / tile->num_samples; +#endif + + uint y_offset = pixel_offset / tile->w; + uint x_offset = pixel_offset - y_offset * tile->w; + + *x = tile->x + x_offset; + *y = tile->y + y_offset; + *sample = tile->start_sample + sample_offset; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/hip/compat.h b/intern/cycles/kernel/device/hip/compat.h new file mode 100644 index 00000000000..282c3eca641 --- /dev/null +++ b/intern/cycles/kernel/device/hip/compat.h @@ -0,0 +1,120 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#define __KERNEL_GPU__ +#define __KERNEL_HIP__ +#define CCL_NAMESPACE_BEGIN +#define CCL_NAMESPACE_END + +#ifndef ATTR_FALLTHROUGH +# define ATTR_FALLTHROUGH +#endif + +#ifdef __HIPCC_RTC__ +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; +#else +# include +#endif + +#ifdef CYCLES_HIPBIN_CC +# define FLT_MIN 1.175494350822287507969e-38f +# define FLT_MAX 340282346638528859811704183484516925440.0f +# define FLT_EPSILON 1.192092896e-07F +#endif + +/* Qualifiers */ + +#define ccl_device __device__ __inline__ +#define ccl_device_inline __device__ __inline__ +#define ccl_device_forceinline __device__ __forceinline__ +#define ccl_device_noinline __device__ __noinline__ +#define ccl_device_noinline_cpu ccl_device +#define ccl_global +#define ccl_static_constant __constant__ +#define ccl_device_constant __constant__ __device__ +#define ccl_constant const +#define ccl_gpu_shared __shared__ +#define ccl_private +#define ccl_may_alias +#define ccl_restrict __restrict__ +#define ccl_loop_no_unroll +#define ccl_align(n) __align__(n) +#define ccl_optional_struct_init + +#define kernel_assert(cond) + +/* Types */ +#ifdef __HIP__ +# include "hip/hip_fp16.h" +# include "hip/hip_runtime.h" +#endif + +#ifdef _MSC_VER +# include +#endif + +#define ccl_gpu_thread_idx_x (threadIdx.x) +#define ccl_gpu_block_dim_x (blockDim.x) +#define ccl_gpu_block_idx_x (blockIdx.x) +#define ccl_gpu_grid_dim_x (gridDim.x) +#define ccl_gpu_warp_size (warpSize) + +#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) +#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) + +/* GPU warp synchronization */ + +#define ccl_gpu_syncthreads() __syncthreads() +#define ccl_gpu_ballot(predicate) __ballot(predicate) +#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down(var, detla) +#define ccl_gpu_popc(x) __popc(x) + +/* GPU texture objects */ +typedef hipTextureObject_t ccl_gpu_tex_object; + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj, + const float x, + const float y) +{ + return tex2D(texobj, x, y); +} + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj, + const float x, + const float y, + const float z) +{ + return tex3D(texobj, x, y, z); +} + +/* Use fast math functions */ + +#define cosf(x) __cosf(((float)(x))) +#define sinf(x) __sinf(((float)(x))) +#define powf(x, y) __powf(((float)(x)), ((float)(y))) +#define tanf(x) __tanf(((float)(x))) +#define logf(x) __logf(((float)(x))) +#define expf(x) __expf(((float)(x))) + +/* Types */ + +#include "util/half.h" +#include "util/types.h" diff --git a/intern/cycles/kernel/device/hip/config.h b/intern/cycles/kernel/device/hip/config.h new file mode 100644 index 00000000000..2fde0d46015 --- /dev/null +++ b/intern/cycles/kernel/device/hip/config.h @@ -0,0 +1,57 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Device data taken from HIP occupancy calculator. + * + * Terminology + * - HIP GPUs have multiple streaming multiprocessors + * - Each multiprocessor executes multiple thread blocks + * - Each thread block contains a number of threads, also known as the block size + * - Multiprocessors have a fixed number of registers, and the amount of registers + * used by each threads limits the number of threads per block. + */ + +/* Launch Bound Definitions */ +#define GPU_MULTIPRESSOR_MAX_REGISTERS 65536 +#define GPU_MULTIPROCESSOR_MAX_BLOCKS 64 +#define GPU_BLOCK_MAX_THREADS 1024 +#define GPU_THREAD_MAX_REGISTERS 255 + +#define GPU_KERNEL_BLOCK_NUM_THREADS 1024 +#define GPU_KERNEL_MAX_REGISTERS 64 + +/* Compute number of threads per block and minimum blocks per multiprocessor + * given the maximum number of registers per thread. */ + +#define ccl_gpu_kernel(block_num_threads, thread_num_registers) \ + extern "C" __global__ void __launch_bounds__(block_num_threads, \ + GPU_MULTIPRESSOR_MAX_REGISTERS / \ + (block_num_threads * thread_num_registers)) + +/* sanity checks */ + +#if GPU_KERNEL_BLOCK_NUM_THREADS > GPU_BLOCK_MAX_THREADS +# error "Maximum number of threads per block exceeded" +#endif + +#if GPU_MULTIPRESSOR_MAX_REGISTERS / (GPU_KERNEL_BLOCK_NUM_THREADS * GPU_KERNEL_MAX_REGISTERS) > \ + GPU_MULTIPROCESSOR_MAX_BLOCKS +# error "Maximum number of blocks per multiprocessor exceeded" +#endif + +#if GPU_KERNEL_MAX_REGISTERS > GPU_THREAD_MAX_REGISTERS +# error "Maximum number of registers per thread exceeded" +#endif diff --git a/intern/cycles/kernel/device/hip/globals.h b/intern/cycles/kernel/device/hip/globals.h new file mode 100644 index 00000000000..d9a560d668b --- /dev/null +++ b/intern/cycles/kernel/device/hip/globals.h @@ -0,0 +1,50 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/types.h" + +#include "kernel/integrator/state.h" + +#include "kernel/util/profiling.h" + +CCL_NAMESPACE_BEGIN + +/* Not actually used, just a NULL pointer that gets passed everywhere, which we + * hope gets optimized out by the compiler. */ +struct KernelGlobalsGPU { + int unused[1]; +}; +typedef ccl_global const KernelGlobalsGPU *ccl_restrict KernelGlobals; + +/* Global scene data and textures */ +__constant__ KernelData __data; +#define KERNEL_TEX(type, name) __attribute__((used)) const __constant__ __device__ type *name; +#include "kernel/textures.h" + +/* Integrator state */ +__constant__ IntegratorStateGPU __integrator_state; + +/* Abstraction macros */ +#define kernel_data __data +#define kernel_tex_fetch(t, index) t[(index)] +#define kernel_tex_array(t) (t) +#define kernel_integrator_state __integrator_state + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/opencl/kernel_indirect_subsurface.cl b/intern/cycles/kernel/device/hip/kernel.cpp similarity index 64% rename from intern/cycles/kernel/kernels/opencl/kernel_indirect_subsurface.cl rename to intern/cycles/kernel/device/hip/kernel.cpp index 84938b889e5..c801320a2e1 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_indirect_subsurface.cl +++ b/intern/cycles/kernel/device/hip/kernel.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,11 +14,15 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_indirect_subsurface.h" +/* HIP kernel entry points */ -#define KERNEL_NAME indirect_subsurface -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME +#ifdef __HIP_DEVICE_COMPILE__ +# include "kernel/device/hip/compat.h" +# include "kernel/device/hip/config.h" +# include "kernel/device/hip/globals.h" + +# include "kernel/device/gpu/image.h" +# include "kernel/device/gpu/kernel.h" + +#endif diff --git a/intern/cycles/kernel/device/metal/compat.h b/intern/cycles/kernel/device/metal/compat.h new file mode 100644 index 00000000000..77cea30914c --- /dev/null +++ b/intern/cycles/kernel/device/metal/compat.h @@ -0,0 +1,126 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#define __KERNEL_GPU__ +#define __KERNEL_METAL__ +#define CCL_NAMESPACE_BEGIN +#define CCL_NAMESPACE_END + +#ifndef ATTR_FALLTHROUGH +# define ATTR_FALLTHROUGH +#endif + +#include +#include +#include +#include + +using namespace metal; + +#pragma clang diagnostic ignored "-Wunused-variable" +#pragma clang diagnostic ignored "-Wsign-compare" + +/* Qualifiers */ + +#define ccl_device +#define ccl_device_inline ccl_device +#define ccl_device_forceinline ccl_device +#define ccl_device_noinline ccl_device __attribute__((noinline)) +#define ccl_device_noinline_cpu ccl_device +#define ccl_global device +#define ccl_static_constant static constant constexpr +#define ccl_device_constant constant +#define ccl_constant const device +#define ccl_gpu_shared threadgroup +#define ccl_private thread +#define ccl_may_alias +#define ccl_restrict __restrict +#define ccl_loop_no_unroll +#define ccl_align(n) alignas(n) +#define ccl_optional_struct_init + +/* No assert supported for Metal */ + +#define kernel_assert(cond) + +/* make_type definitions with Metal style element initializers */ +#ifdef make_float2 +# undef make_float2 +#endif +#ifdef make_float3 +# undef make_float3 +#endif +#ifdef make_float4 +# undef make_float4 +#endif +#ifdef make_int2 +# undef make_int2 +#endif +#ifdef make_int3 +# undef make_int3 +#endif +#ifdef make_int4 +# undef make_int4 +#endif +#ifdef make_uchar4 +# undef make_uchar4 +#endif + +#define make_float2(x, y) float2(x, y) +#define make_float3(x, y, z) float3(x, y, z) +#define make_float4(x, y, z, w) float4(x, y, z, w) +#define make_int2(x, y) int2(x, y) +#define make_int3(x, y, z) int3(x, y, z) +#define make_int4(x, y, z, w) int4(x, y, z, w) +#define make_uchar4(x, y, z, w) uchar4(x, y, z, w) + +/* Math functions */ + +#define __uint_as_float(x) as_type(x) +#define __float_as_uint(x) as_type(x) +#define __int_as_float(x) as_type(x) +#define __float_as_int(x) as_type(x) +#define __float2half(x) half(x) +#define powf(x, y) pow(float(x), float(y)) +#define fabsf(x) fabs(float(x)) +#define copysignf(x, y) copysign(float(x), float(y)) +#define asinf(x) asin(float(x)) +#define acosf(x) acos(float(x)) +#define atanf(x) atan(float(x)) +#define floorf(x) floor(float(x)) +#define ceilf(x) ceil(float(x)) +#define hypotf(x, y) hypot(float(x), float(y)) +#define atan2f(x, y) atan2(float(x), float(y)) +#define fmaxf(x, y) fmax(float(x), float(y)) +#define fminf(x, y) fmin(float(x), float(y)) +#define fmodf(x, y) fmod(float(x), float(y)) +#define sinhf(x) sinh(float(x)) +#define coshf(x) cosh(float(x)) +#define tanhf(x) tanh(float(x)) + +/* Use native functions with possibly lower precision for performance, + * no issues found so far. */ +#define trigmode fast +#define sinf(x) trigmode::sin(float(x)) +#define cosf(x) trigmode::cos(float(x)) +#define tanf(x) trigmode::tan(float(x)) +#define expf(x) trigmode::exp(float(x)) +#define sqrtf(x) trigmode::sqrt(float(x)) +#define logf(x) trigmode::log(float(x)) + +#define NULL 0 diff --git a/intern/cycles/kernel/kernel_compat_optix.h b/intern/cycles/kernel/device/optix/compat.h similarity index 50% rename from intern/cycles/kernel/kernel_compat_optix.h rename to intern/cycles/kernel/device/optix/compat.h index 064c99ca100..835e4621d47 100644 --- a/intern/cycles/kernel/kernel_compat_optix.h +++ b/intern/cycles/kernel/device/optix/compat.h @@ -15,14 +15,13 @@ * limitations under the License. */ -#ifndef __KERNEL_COMPAT_OPTIX_H__ -#define __KERNEL_COMPAT_OPTIX_H__ +#pragma once #define OPTIX_DONT_INCLUDE_CUDA #include #define __KERNEL_GPU__ -#define __KERNEL_CUDA__ // OptiX kernels are implicitly CUDA kernels too +#define __KERNEL_CUDA__ /* OptiX kernels are implicitly CUDA kernels too */ #define __KERNEL_OPTIX__ #define CCL_NAMESPACE_BEGIN #define CCL_NAMESPACE_END @@ -31,14 +30,14 @@ # define ATTR_FALLTHROUGH #endif +/* Manual definitions so we can compile without CUDA toolkit. */ + #ifdef __CUDACC_RTC__ typedef unsigned int uint32_t; typedef unsigned long long uint64_t; #else # include #endif -typedef unsigned short half; -typedef unsigned long long CUtexObject; #ifdef CYCLES_CUBIN_CC # define FLT_MIN 1.175494350822287507969e-38f @@ -46,21 +45,6 @@ typedef unsigned long long CUtexObject; # define FLT_EPSILON 1.192092896e-07F #endif -__device__ half __float2half(const float f) -{ - half val; - asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); - return val; -} - -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL -#endif - #define ccl_device \ __device__ __forceinline__ // Function calls are bad for OptiX performance, so inline everything #define ccl_device_inline ccl_device @@ -69,29 +53,81 @@ __device__ half __float2half(const float f) #define ccl_device_noinline_cpu ccl_device #define ccl_global #define ccl_static_constant __constant__ +#define ccl_device_constant __constant__ __device__ #define ccl_constant const -#define ccl_local -#define ccl_local_param +#define ccl_gpu_shared __shared__ #define ccl_private #define ccl_may_alias -#define ccl_addr_space -#define ccl_loop_no_unroll #define ccl_restrict __restrict__ -#define ccl_ref +#define ccl_loop_no_unroll #define ccl_align(n) __align__(n) -// Zero initialize structs to help the compiler figure out scoping +/* Zero initialize structs to help the compiler figure out scoping */ #define ccl_optional_struct_init = {} -#define kernel_data __params.data // See kernel_globals.h -#define kernel_tex_array(t) __params.t -#define kernel_tex_fetch(t, index) __params.t[(index)] +/* No assert supported for CUDA */ #define kernel_assert(cond) +/* GPU thread, block, grid size and index */ + +#define ccl_gpu_thread_idx_x (threadIdx.x) +#define ccl_gpu_block_dim_x (blockDim.x) +#define ccl_gpu_block_idx_x (blockIdx.x) +#define ccl_gpu_grid_dim_x (gridDim.x) +#define ccl_gpu_warp_size (warpSize) + +#define ccl_gpu_global_id_x() (ccl_gpu_block_idx_x * ccl_gpu_block_dim_x + ccl_gpu_thread_idx_x) +#define ccl_gpu_global_size_x() (ccl_gpu_grid_dim_x * ccl_gpu_block_dim_x) + +/* GPU warp synchronization. */ + +#define ccl_gpu_syncthreads() __syncthreads() +#define ccl_gpu_ballot(predicate) __ballot_sync(0xFFFFFFFF, predicate) +#define ccl_gpu_shfl_down_sync(mask, var, detla) __shfl_down_sync(mask, var, detla) +#define ccl_gpu_popc(x) __popc(x) + +/* GPU texture objects */ + +typedef unsigned long long CUtexObject; +typedef CUtexObject ccl_gpu_tex_object; + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_2D(const ccl_gpu_tex_object texobj, + const float x, + const float y) +{ + return tex2D(texobj, x, y); +} + +template +ccl_device_forceinline T ccl_gpu_tex_object_read_3D(const ccl_gpu_tex_object texobj, + const float x, + const float y, + const float z) +{ + return tex3D(texobj, x, y, z); +} + +/* Half */ + +typedef unsigned short half; + +__device__ half __float2half(const float f) +{ + half val; + asm("{ cvt.rn.f16.f32 %0, %1;}\n" : "=h"(val) : "f"(f)); + return val; +} + +__device__ float __half2float(const half h) +{ + float val; + asm("{ cvt.f32.f16 %0, %1;}\n" : "=f"(val) : "h"(h)); + return val; +} + /* Types */ -#include "util/util_half.h" -#include "util/util_types.h" - -#endif /* __KERNEL_COMPAT_OPTIX_H__ */ +#include "util/half.h" +#include "util/types.h" diff --git a/intern/cycles/kernel/device/optix/globals.h b/intern/cycles/kernel/device/optix/globals.h new file mode 100644 index 00000000000..e9b72369cd5 --- /dev/null +++ b/intern/cycles/kernel/device/optix/globals.h @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Constant Globals */ + +#pragma once + +#include "kernel/types.h" + +#include "kernel/integrator/state.h" + +#include "kernel/util/profiling.h" + +CCL_NAMESPACE_BEGIN + +/* Not actually used, just a NULL pointer that gets passed everywhere, which we + * hope gets optimized out by the compiler. */ +struct KernelGlobalsGPU { + int unused[1]; +}; +typedef ccl_global const KernelGlobalsGPU *ccl_restrict KernelGlobals; + +/* Launch parameters */ +struct KernelParamsOptiX { + /* Kernel arguments */ + const int *path_index_array; + float *render_buffer; + + /* Global scene data and textures */ + KernelData data; +#define KERNEL_TEX(type, name) const type *name; +#include "kernel/textures.h" + + /* Integrator state */ + IntegratorStateGPU __integrator_state; +}; + +#ifdef __NVCC__ +extern "C" static __constant__ KernelParamsOptiX __params; +#endif + +/* Abstraction macros */ +#define kernel_data __params.data +#define kernel_tex_array(t) __params.t +#define kernel_tex_fetch(t, index) __params.t[(index)] +#define kernel_integrator_state __params.__integrator_state + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/device/optix/kernel.cu b/intern/cycles/kernel/device/optix/kernel.cu new file mode 100644 index 00000000000..6989219cd9f --- /dev/null +++ b/intern/cycles/kernel/device/optix/kernel.cu @@ -0,0 +1,406 @@ +/* + * Copyright 2019, NVIDIA Corporation. + * Copyright 2019, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// clang-format off +#include "kernel/device/optix/compat.h" +#include "kernel/device/optix/globals.h" + +#include "kernel/device/gpu/image.h" /* Texture lookup uses normal CUDA intrinsics. */ + +#include "kernel/integrator/state.h" +#include "kernel/integrator/state_flow.h" +#include "kernel/integrator/state_util.h" + +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/intersect_shadow.h" +#include "kernel/integrator/intersect_subsurface.h" +#include "kernel/integrator/intersect_volume_stack.h" + +// clang-format on + +template ccl_device_forceinline T *get_payload_ptr_0() +{ + return pointer_unpack_from_uint(optixGetPayload_0(), optixGetPayload_1()); +} +template ccl_device_forceinline T *get_payload_ptr_2() +{ + return pointer_unpack_from_uint(optixGetPayload_2(), optixGetPayload_3()); +} + +ccl_device_forceinline int get_object_id() +{ +#ifdef __OBJECT_MOTION__ + /* Always get the the instance ID from the TLAS + * There might be a motion transform node between TLAS and BLAS which does not have one. */ + return optixGetInstanceIdFromHandle(optixGetTransformListHandle(0)); +#else + return optixGetInstanceId(); +#endif +} + +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_closest() +{ + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_closest(nullptr, path_index); +} + +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_shadow() +{ + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_shadow(nullptr, path_index); +} + +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_subsurface() +{ + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_subsurface(nullptr, path_index); +} + +extern "C" __global__ void __raygen__kernel_optix_integrator_intersect_volume_stack() +{ + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_intersect_volume_stack(nullptr, path_index); +} + +extern "C" __global__ void __miss__kernel_optix_miss() +{ + /* 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss. */ + optixSetPayload_0(__float_as_uint(optixGetRayTmax())); + optixSetPayload_5(PRIMITIVE_NONE); +} + +extern "C" __global__ void __anyhit__kernel_optix_local_hit() +{ +#ifdef __HAIR__ + if (!optixIsTriangleHit()) { + /* Ignore curves. */ + return optixIgnoreIntersection(); + } +#endif + +#ifdef __BVH_LOCAL__ + const int object = get_object_id(); + if (object != optixGetPayload_4() /* local_object */) { + /* Only intersect with matching object. */ + return optixIgnoreIntersection(); + } + + const uint max_hits = optixGetPayload_5(); + if (max_hits == 0) { + /* Special case for when no hit information is requested, just report that something was hit */ + optixSetPayload_5(true); + return optixTerminateRay(); + } + + int hit = 0; + uint *const lcg_state = get_payload_ptr_0(); + LocalIntersection *const local_isect = get_payload_ptr_2(); + + if (lcg_state) { + for (int i = min(max_hits, local_isect->num_hits) - 1; i >= 0; --i) { + if (optixGetRayTmax() == local_isect->hits[i].t) { + return optixIgnoreIntersection(); + } + } + + hit = local_isect->num_hits++; + + if (local_isect->num_hits > max_hits) { + hit = lcg_step_uint(lcg_state) % local_isect->num_hits; + if (hit >= max_hits) { + return optixIgnoreIntersection(); + } + } + } + else { + if (local_isect->num_hits && optixGetRayTmax() > local_isect->hits[0].t) { + /* Record closest intersection only. + * Do not terminate ray here, since there is no guarantee about distance ordering in any-hit. + */ + return optixIgnoreIntersection(); + } + + local_isect->num_hits = 1; + } + + const int prim = optixGetPrimitiveIndex(); + + Intersection *isect = &local_isect->hits[hit]; + isect->t = optixGetRayTmax(); + isect->prim = prim; + isect->object = get_object_id(); + isect->type = kernel_tex_fetch(__objects, isect->object).primitive_type; + + const float2 barycentrics = optixGetTriangleBarycentrics(); + isect->u = 1.0f - barycentrics.y - barycentrics.x; + isect->v = barycentrics.x; + + /* Record geometric normal. */ + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w; + const float3 tri_a = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 0)); + const float3 tri_b = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 1)); + const float3 tri_c = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 2)); + local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a)); + + /* Continue tracing (without this the trace call would return after the first hit). */ + optixIgnoreIntersection(); +#endif +} + +extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() +{ +#ifdef __SHADOW_RECORD_ALL__ + int prim = optixGetPrimitiveIndex(); + const uint object = get_object_id(); +# ifdef __VISIBILITY_FLAG__ + const uint visibility = optixGetPayload_4(); + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { + return optixIgnoreIntersection(); + } +# endif + + float u = 0.0f, v = 0.0f; + int type = 0; + if (optixIsTriangleHit()) { + const float2 barycentrics = optixGetTriangleBarycentrics(); + u = 1.0f - barycentrics.y - barycentrics.x; + v = barycentrics.x; + type = kernel_tex_fetch(__objects, object).primitive_type; + } +# ifdef __HAIR__ + else { + u = __uint_as_float(optixGetAttribute_0()); + v = __uint_as_float(optixGetAttribute_1()); + + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, prim); + type = segment.type; + prim = segment.prim; + + /* Filter out curve endcaps. */ + if (u == 0.0f || u == 1.0f) { + return optixIgnoreIntersection(); + } + } +# endif + +# ifndef __TRANSPARENT_SHADOWS__ + /* No transparent shadows support compiled in, make opaque. */ + optixSetPayload_5(true); + return optixTerminateRay(); +# else + const uint max_hits = optixGetPayload_3(); + const uint num_hits_packed = optixGetPayload_2(); + const uint num_recorded_hits = uint16_unpack_from_uint_0(num_hits_packed); + const uint num_hits = uint16_unpack_from_uint_1(num_hits_packed); + + /* If no transparent shadows, all light is blocked and we can stop immediately. */ + if (num_hits >= max_hits || + !(intersection_get_shader_flags(NULL, prim, type) & SD_HAS_TRANSPARENT_SHADOW)) { + optixSetPayload_5(true); + return optixTerminateRay(); + } + + /* Always use baked shadow transparency for curves. */ + if (type & PRIMITIVE_ALL_CURVE) { + float throughput = __uint_as_float(optixGetPayload_1()); + throughput *= intersection_curve_shadow_transparency(nullptr, object, prim, u); + optixSetPayload_1(__float_as_uint(throughput)); + optixSetPayload_2(uint16_pack_to_uint(num_recorded_hits, num_hits + 1)); + + if (throughput < CURVE_SHADOW_TRANSPARENCY_CUTOFF) { + optixSetPayload_4(true); + return optixTerminateRay(); + } + else { + /* Continue tracing. */ + optixIgnoreIntersection(); + return; + } + } + + /* Record transparent intersection. */ + optixSetPayload_2(uint16_pack_to_uint(num_recorded_hits + 1, num_hits + 1)); + + uint record_index = num_recorded_hits; + + const IntegratorShadowState state = optixGetPayload_0(); + + const uint max_record_hits = min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + if (record_index >= max_record_hits) { + /* If maximum number of hits reached, find a hit to replace. */ + float max_recorded_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, 0, t); + uint max_recorded_hit = 0; + + for (int i = 1; i < max_record_hits; i++) { + const float isect_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, i, t); + if (isect_t > max_recorded_t) { + max_recorded_t = isect_t; + max_recorded_hit = i; + } + } + + if (optixGetRayTmax() >= max_recorded_t) { + /* Accept hit, so that OptiX won't consider any more hits beyond the distance of the + * current hit anymore. */ + return; + } + + record_index = max_recorded_hit; + } + + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, u) = u; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, v) = v; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, t) = optixGetRayTmax(); + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, prim) = prim; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, object) = object; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, record_index, type) = type; + + /* Continue tracing. */ + optixIgnoreIntersection(); +# endif /* __TRANSPARENT_SHADOWS__ */ +#endif /* __SHADOW_RECORD_ALL__ */ +} + +extern "C" __global__ void __anyhit__kernel_optix_volume_test() +{ +#ifdef __HAIR__ + if (!optixIsTriangleHit()) { + /* Ignore curves. */ + return optixIgnoreIntersection(); + } +#endif + + const uint object = get_object_id(); +#ifdef __VISIBILITY_FLAG__ + const uint visibility = optixGetPayload_4(); + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { + return optixIgnoreIntersection(); + } +#endif + + if ((kernel_tex_fetch(__object_flag, object) & SD_OBJECT_HAS_VOLUME) == 0) { + return optixIgnoreIntersection(); + } +} + +extern "C" __global__ void __anyhit__kernel_optix_visibility_test() +{ +#ifdef __HAIR__ + if (!optixIsTriangleHit()) { + /* Filter out curve endcaps. */ + const float u = __uint_as_float(optixGetAttribute_0()); + if (u == 0.0f || u == 1.0f) { + return optixIgnoreIntersection(); + } + } +#endif + +#ifdef __VISIBILITY_FLAG__ + const uint object = get_object_id(); + const uint visibility = optixGetPayload_4(); + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { + return optixIgnoreIntersection(); + } + + /* Shadow ray early termination. */ + if (visibility & PATH_RAY_SHADOW_OPAQUE) { + return optixTerminateRay(); + } +#endif +} + +extern "C" __global__ void __closesthit__kernel_optix_hit() +{ + const int object = get_object_id(); + const int prim = optixGetPrimitiveIndex(); + + optixSetPayload_0(__float_as_uint(optixGetRayTmax())); /* Intersection distance */ + optixSetPayload_4(object); + + if (optixIsTriangleHit()) { + const float2 barycentrics = optixGetTriangleBarycentrics(); + optixSetPayload_1(__float_as_uint(1.0f - barycentrics.y - barycentrics.x)); + optixSetPayload_2(__float_as_uint(barycentrics.x)); + optixSetPayload_3(prim); + optixSetPayload_5(kernel_tex_fetch(__objects, object).primitive_type); + } + else { + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, prim); + optixSetPayload_1(optixGetAttribute_0()); /* Same as 'optixGetCurveParameter()' */ + optixSetPayload_2(optixGetAttribute_1()); + optixSetPayload_3(segment.prim); + optixSetPayload_5(segment.type); + } +} + +#ifdef __HAIR__ +ccl_device_inline void optix_intersection_curve(const int prim, const int type) +{ + const int object = get_object_id(); + +# ifdef __VISIBILITY_FLAG__ + const uint visibility = optixGetPayload_4(); + if ((kernel_tex_fetch(__objects, object).visibility & visibility) == 0) { + return; + } +# endif + + float3 P = optixGetObjectRayOrigin(); + float3 dir = optixGetObjectRayDirection(); + + /* The direction is not normalized by default, but the curve intersection routine expects that */ + float len; + dir = normalize_len(dir, &len); + +# ifdef __OBJECT_MOTION__ + const float time = optixGetRayTime(); +# else + const float time = 0.0f; +# endif + + Intersection isect; + isect.t = optixGetRayTmax(); + /* Transform maximum distance into object space. */ + if (isect.t != FLT_MAX) + isect.t *= len; + + if (curve_intersect(NULL, &isect, P, dir, isect.t, object, prim, time, type)) { + optixReportIntersection(isect.t / len, + type & PRIMITIVE_ALL, + __float_as_int(isect.u), /* Attribute_0 */ + __float_as_int(isect.v)); /* Attribute_1 */ + } +} + +extern "C" __global__ void __intersection__curve_ribbon() +{ + const KernelCurveSegment segment = kernel_tex_fetch(__curve_segments, optixGetPrimitiveIndex()); + const int prim = segment.prim; + const int type = segment.type; + if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { + optix_intersection_curve(prim, type); + } +} +#endif diff --git a/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu b/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu new file mode 100644 index 00000000000..071e9deae0b --- /dev/null +++ b/intern/cycles/kernel/device/optix/kernel_shader_raytrace.cu @@ -0,0 +1,30 @@ +/* + * Copyright 2021, Blender Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Copy of the regular kernels with additional shader ray-tracing kernel that takes + * much longer to compiler. This is only loaded when needed by the scene. */ + +#include "kernel/device/optix/kernel.cu" + +#include "kernel/integrator/shade_surface.h" + +extern "C" __global__ void __raygen__kernel_optix_integrator_shade_surface_raytrace() +{ + const int global_index = optixGetLaunchIndex().x; + const int path_index = (__params.path_index_array) ? __params.path_index_array[global_index] : + global_index; + integrator_shade_surface_raytrace(nullptr, path_index, __params.render_buffer); +} diff --git a/intern/cycles/kernel/film/accumulate.h b/intern/cycles/kernel/film/accumulate.h new file mode 100644 index 00000000000..30e1afea751 --- /dev/null +++ b/intern/cycles/kernel/film/accumulate.h @@ -0,0 +1,559 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/film/adaptive_sampling.h" +#include "kernel/film/write_passes.h" + +#include "kernel/integrator/shadow_catcher.h" + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * BSDF Evaluation + * + * BSDF evaluation result, split between diffuse and glossy. This is used to + * accumulate render passes separately. Note that reflection, transmission + * and volume scattering are written to different render passes, but we assume + * that only one of those can happen at a bounce, and so do not need to accumulate + * them separately. */ + +ccl_device_inline void bsdf_eval_init(ccl_private BsdfEval *eval, + const bool is_diffuse, + float3 value) +{ + eval->diffuse = zero_float3(); + eval->glossy = zero_float3(); + + if (is_diffuse) { + eval->diffuse = value; + } + else { + eval->glossy = value; + } +} + +ccl_device_inline void bsdf_eval_accum(ccl_private BsdfEval *eval, + const bool is_diffuse, + float3 value, + float mis_weight) +{ + value *= mis_weight; + + if (is_diffuse) { + eval->diffuse += value; + } + else { + eval->glossy += value; + } +} + +ccl_device_inline bool bsdf_eval_is_zero(ccl_private BsdfEval *eval) +{ + return is_zero(eval->diffuse) && is_zero(eval->glossy); +} + +ccl_device_inline void bsdf_eval_mul(ccl_private BsdfEval *eval, float value) +{ + eval->diffuse *= value; + eval->glossy *= value; +} + +ccl_device_inline void bsdf_eval_mul3(ccl_private BsdfEval *eval, float3 value) +{ + eval->diffuse *= value; + eval->glossy *= value; +} + +ccl_device_inline float3 bsdf_eval_sum(ccl_private const BsdfEval *eval) +{ + return eval->diffuse + eval->glossy; +} + +ccl_device_inline float3 bsdf_eval_diffuse_glossy_ratio(ccl_private const BsdfEval *eval) +{ + /* Ratio of diffuse and glossy to recover proportions for writing to render pass. + * We assume reflection, transmission and volume scatter to be exclusive. */ + return safe_divide_float3_float3(eval->diffuse, eval->diffuse + eval->glossy); +} + +/* -------------------------------------------------------------------- + * Clamping + * + * Clamping is done on a per-contribution basis so that we can write directly + * to render buffers instead of using per-thread memory, and to avoid the + * impact of clamping on other contributions. */ + +ccl_device_forceinline void kernel_accum_clamp(KernelGlobals kg, ccl_private float3 *L, int bounce) +{ +#ifdef __KERNEL_DEBUG_NAN__ + if (!isfinite3_safe(*L)) { + kernel_assert(!"Cycles sample with non-finite value detected"); + } +#endif + /* Make sure all components are finite, allowing the contribution to be usable by adaptive + * sampling convergence check, but also to make it so render result never causes issues with + * post-processing. */ + *L = ensure_finite3(*L); + +#ifdef __CLAMP_SAMPLE__ + float limit = (bounce > 0) ? kernel_data.integrator.sample_clamp_indirect : + kernel_data.integrator.sample_clamp_direct; + float sum = reduce_add(fabs(*L)); + if (sum > limit) { + *L *= limit / sum; + } +#endif +} + +/* -------------------------------------------------------------------- + * Pass accumulation utilities. + */ + +/* Get pointer to pixel in render buffer. */ +ccl_device_forceinline ccl_global float *kernel_accum_pixel_render_buffer( + KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) +{ + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + return render_buffer + render_buffer_offset; +} + +/* -------------------------------------------------------------------- + * Adaptive sampling. + */ + +ccl_device_inline int kernel_accum_sample(KernelGlobals kg, + ConstIntegratorState state, + ccl_global float *ccl_restrict render_buffer, + int sample) +{ + if (kernel_data.film.pass_sample_count == PASS_UNUSED) { + return sample; + } + + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + + return atomic_fetch_and_add_uint32((uint *)(buffer) + kernel_data.film.pass_sample_count, 1); +} + +ccl_device void kernel_accum_adaptive_buffer(KernelGlobals kg, + const int sample, + const float3 contribution, + ccl_global float *ccl_restrict buffer) +{ + /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping + * criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte + * Carlo global illumination" except that here it is applied per pixel and not in hierarchical + * tiles. */ + + if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { + return; + } + + if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { + kernel_write_pass_float4( + buffer + kernel_data.film.pass_adaptive_aux_buffer, + make_float4(contribution.x * 2.0f, contribution.y * 2.0f, contribution.z * 2.0f, 0.0f)); + } +} + +/* -------------------------------------------------------------------- + * Shadow catcher. + */ + +#ifdef __SHADOW_CATCHER__ + +/* Accumulate contribution to the Shadow Catcher pass. + * + * Returns truth if the contribution is fully handled here and is not to be added to the other + * passes (like combined, adaptive sampling). */ + +ccl_device bool kernel_accum_shadow_catcher(KernelGlobals kg, + const uint32_t path_flag, + const float3 contribution, + ccl_global float *ccl_restrict buffer) +{ + if (!kernel_data.integrator.has_shadow_catcher) { + return false; + } + + kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + /* Matte pass. */ + if (kernel_shadow_catcher_is_matte_path(path_flag)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher_matte, contribution); + /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive + * sampling is based on how noisy the combined pass is as if there were no catchers in the + * scene. */ + } + + /* Shadow catcher pass. */ + if (kernel_shadow_catcher_is_object_pass(path_flag)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); + return true; + } + + return false; +} + +ccl_device bool kernel_accum_shadow_catcher_transparent(KernelGlobals kg, + const uint32_t path_flag, + const float3 contribution, + const float transparent, + ccl_global float *ccl_restrict buffer) +{ + if (!kernel_data.integrator.has_shadow_catcher) { + return false; + } + + kernel_assert(kernel_data.film.pass_shadow_catcher != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + if (path_flag & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { + return true; + } + + /* Matte pass. */ + if (kernel_shadow_catcher_is_matte_path(path_flag)) { + kernel_write_pass_float4( + buffer + kernel_data.film.pass_shadow_catcher_matte, + make_float4(contribution.x, contribution.y, contribution.z, transparent)); + /* NOTE: Accumulate the combined pass and to the samples count pass, so that the adaptive + * sampling is based on how noisy the combined pass is as if there were no catchers in the + * scene. */ + } + + /* Shadow catcher pass. */ + if (kernel_shadow_catcher_is_object_pass(path_flag)) { + /* NOTE: The transparency of the shadow catcher pass is ignored. It is not needed for the + * calculation and the alpha channel of the pass contains numbers of samples contributed to a + * pixel of the pass. */ + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow_catcher, contribution); + return true; + } + + return false; +} + +ccl_device void kernel_accum_shadow_catcher_transparent_only(KernelGlobals kg, + const uint32_t path_flag, + const float transparent, + ccl_global float *ccl_restrict buffer) +{ + if (!kernel_data.integrator.has_shadow_catcher) { + return; + } + + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + /* Matte pass. */ + if (kernel_shadow_catcher_is_matte_path(path_flag)) { + kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, transparent); + } +} + +#endif /* __SHADOW_CATCHER__ */ + +/* -------------------------------------------------------------------- + * Render passes. + */ + +/* Write combined pass. */ +ccl_device_inline void kernel_accum_combined_pass(KernelGlobals kg, + const uint32_t path_flag, + const int sample, + const float3 contribution, + ccl_global float *ccl_restrict buffer) +{ +#ifdef __SHADOW_CATCHER__ + if (kernel_accum_shadow_catcher(kg, path_flag, contribution, buffer)) { + return; + } +#endif + + if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_combined, contribution); + } + + kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); +} + +/* Write combined pass with transparency. */ +ccl_device_inline void kernel_accum_combined_transparent_pass(KernelGlobals kg, + const uint32_t path_flag, + const int sample, + const float3 contribution, + const float transparent, + ccl_global float *ccl_restrict + buffer) +{ +#ifdef __SHADOW_CATCHER__ + if (kernel_accum_shadow_catcher_transparent(kg, path_flag, contribution, transparent, buffer)) { + return; + } +#endif + + if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { + kernel_write_pass_float4( + buffer + kernel_data.film.pass_combined, + make_float4(contribution.x, contribution.y, contribution.z, transparent)); + } + + kernel_accum_adaptive_buffer(kg, sample, contribution, buffer); +} + +/* Write background or emission to appropriate pass. */ +ccl_device_inline void kernel_accum_emission_or_background_pass(KernelGlobals kg, + ConstIntegratorState state, + float3 contribution, + ccl_global float *ccl_restrict + buffer, + const int pass) +{ + if (!(kernel_data.film.light_pass_flag & PASS_ANY)) { + return; + } + +#ifdef __PASSES__ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + int pass_offset = PASS_UNUSED; + + /* Denoising albedo. */ +# ifdef __DENOISING_FEATURES__ + if (path_flag & PATH_RAY_DENOISING_FEATURES) { + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + const float3 denoising_albedo = denoising_feature_throughput * contribution; + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + } + } +# endif /* __DENOISING_FEATURES__ */ + + if (!(path_flag & PATH_RAY_ANY_PASS)) { + /* Directly visible, write to emission or background pass. */ + pass_offset = pass; + } + else if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { + /* Indirectly visible through reflection. */ + const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? + ((INTEGRATOR_STATE(state, path, bounce) == 1) ? + kernel_data.film.pass_glossy_direct : + kernel_data.film.pass_glossy_indirect) : + ((INTEGRATOR_STATE(state, path, bounce) == 1) ? + kernel_data.film.pass_transmission_direct : + kernel_data.film.pass_transmission_indirect); + + if (glossy_pass_offset != PASS_UNUSED) { + /* Glossy is a subset of the throughput, reconstruct it here using the + * diffuse-glossy ratio. */ + const float3 ratio = INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); + const float3 glossy_contribution = (one_float3() - ratio) * contribution; + kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); + } + + /* Reconstruct diffuse subset of throughput. */ + pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ? + kernel_data.film.pass_diffuse_direct : + kernel_data.film.pass_diffuse_indirect; + if (pass_offset != PASS_UNUSED) { + contribution *= INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); + } + } + else if (path_flag & PATH_RAY_VOLUME_PASS) { + /* Indirectly visible through volume. */ + pass_offset = (INTEGRATOR_STATE(state, path, bounce) == 1) ? + kernel_data.film.pass_volume_direct : + kernel_data.film.pass_volume_indirect; + } + + /* Single write call for GPU coherence. */ + if (pass_offset != PASS_UNUSED) { + kernel_write_pass_float3(buffer + pass_offset, contribution); + } +#endif /* __PASSES__ */ +} + +/* Write light contribution to render buffer. */ +ccl_device_inline void kernel_accum_light(KernelGlobals kg, + ConstIntegratorShadowState state, + ccl_global float *ccl_restrict render_buffer) +{ + /* The throughput for shadow paths already contains the light shader evaluation. */ + float3 contribution = INTEGRATOR_STATE(state, shadow_path, throughput); + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, shadow_path, bounce)); + + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, shadow_path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset; + + const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); + const int sample = INTEGRATOR_STATE(state, shadow_path, sample); + + /* Ambient occlusion. */ + if (path_flag & PATH_RAY_SHADOW_FOR_AO) { + if ((kernel_data.kernel_features & KERNEL_FEATURE_AO_PASS) && (path_flag & PATH_RAY_CAMERA)) { + kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, contribution); + } + if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) { + const float3 ao_weight = INTEGRATOR_STATE(state, shadow_path, unshadowed_throughput); + kernel_accum_combined_pass(kg, path_flag, sample, contribution * ao_weight, buffer); + } + return; + } + + /* Direct light shadow. */ + kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); + +#ifdef __PASSES__ + if (kernel_data.film.light_pass_flag & PASS_ANY) { + const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); + int pass_offset = PASS_UNUSED; + + if (path_flag & (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS)) { + /* Indirectly visible through reflection. */ + const int glossy_pass_offset = (path_flag & PATH_RAY_REFLECT_PASS) ? + ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? + kernel_data.film.pass_glossy_direct : + kernel_data.film.pass_glossy_indirect) : + ((INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? + kernel_data.film.pass_transmission_direct : + kernel_data.film.pass_transmission_indirect); + + if (glossy_pass_offset != PASS_UNUSED) { + /* Glossy is a subset of the throughput, reconstruct it here using the + * diffuse-glossy ratio. */ + const float3 ratio = INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio); + const float3 glossy_contribution = (one_float3() - ratio) * contribution; + kernel_write_pass_float3(buffer + glossy_pass_offset, glossy_contribution); + } + + /* Reconstruct diffuse subset of throughput. */ + pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? + kernel_data.film.pass_diffuse_direct : + kernel_data.film.pass_diffuse_indirect; + if (pass_offset != PASS_UNUSED) { + contribution *= INTEGRATOR_STATE(state, shadow_path, diffuse_glossy_ratio); + } + } + else if (path_flag & PATH_RAY_VOLUME_PASS) { + /* Indirectly visible through volume. */ + pass_offset = (INTEGRATOR_STATE(state, shadow_path, bounce) == 0) ? + kernel_data.film.pass_volume_direct : + kernel_data.film.pass_volume_indirect; + } + + /* Single write call for GPU coherence. */ + if (pass_offset != PASS_UNUSED) { + kernel_write_pass_float3(buffer + pass_offset, contribution); + } + + /* Write shadow pass. */ + if (kernel_data.film.pass_shadow != PASS_UNUSED && (path_flag & PATH_RAY_SHADOW_FOR_LIGHT) && + (path_flag & PATH_RAY_CAMERA)) { + const float3 unshadowed_throughput = INTEGRATOR_STATE( + state, shadow_path, unshadowed_throughput); + const float3 shadowed_throughput = INTEGRATOR_STATE(state, shadow_path, throughput); + const float3 shadow = safe_divide_float3_float3(shadowed_throughput, unshadowed_throughput) * + kernel_data.film.pass_shadow_scale; + kernel_write_pass_float3(buffer + kernel_data.film.pass_shadow, shadow); + } + } +#endif +} + +/* Write transparency to render buffer. + * + * Note that we accumulate transparency = 1 - alpha in the render buffer. + * Otherwise we'd have to write alpha on path termination, which happens + * in many places. */ +ccl_device_inline void kernel_accum_transparent(KernelGlobals kg, + ConstIntegratorState state, + const uint32_t path_flag, + const float transparent, + ccl_global float *ccl_restrict buffer) +{ + if (kernel_data.film.light_pass_flag & PASSMASK(COMBINED)) { + kernel_write_pass_float(buffer + kernel_data.film.pass_combined + 3, transparent); + } + + kernel_accum_shadow_catcher_transparent_only(kg, path_flag, transparent, buffer); +} + +/* Write holdout to render buffer. */ +ccl_device_inline void kernel_accum_holdout(KernelGlobals kg, + ConstIntegratorState state, + const uint32_t path_flag, + const float transparent, + ccl_global float *ccl_restrict render_buffer) +{ + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + kernel_accum_transparent(kg, state, path_flag, transparent, buffer); +} + +/* Write background contribution to render buffer. + * + * Includes transparency, matching kernel_accum_transparent. */ +ccl_device_inline void kernel_accum_background(KernelGlobals kg, + ConstIntegratorState state, + const float3 L, + const float transparent, + const bool is_transparent_background_ray, + ccl_global float *ccl_restrict render_buffer) +{ + float3 contribution = INTEGRATOR_STATE(state, path, throughput) * L; + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); + + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if (is_transparent_background_ray) { + kernel_accum_transparent(kg, state, path_flag, transparent, buffer); + } + else { + const int sample = INTEGRATOR_STATE(state, path, sample); + kernel_accum_combined_transparent_pass( + kg, path_flag, sample, contribution, transparent, buffer); + } + kernel_accum_emission_or_background_pass( + kg, state, contribution, buffer, kernel_data.film.pass_background); +} + +/* Write emission to render buffer. */ +ccl_device_inline void kernel_accum_emission(KernelGlobals kg, + ConstIntegratorState state, + const float3 throughput, + const float3 L, + ccl_global float *ccl_restrict render_buffer) +{ + float3 contribution = throughput * L; + kernel_accum_clamp(kg, &contribution, INTEGRATOR_STATE(state, path, bounce) - 1); + + ccl_global float *buffer = kernel_accum_pixel_render_buffer(kg, state, render_buffer); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const int sample = INTEGRATOR_STATE(state, path, sample); + + kernel_accum_combined_pass(kg, path_flag, sample, contribution, buffer); + kernel_accum_emission_or_background_pass( + kg, state, contribution, buffer, kernel_data.film.pass_emission); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/adaptive_sampling.h b/intern/cycles/kernel/film/adaptive_sampling.h new file mode 100644 index 00000000000..468c5d4486e --- /dev/null +++ b/intern/cycles/kernel/film/adaptive_sampling.h @@ -0,0 +1,160 @@ +/* + * Copyright 2019 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/film/write_passes.h" + +CCL_NAMESPACE_BEGIN + +/* Check whether the pixel has converged and should not be sampled anymore. */ + +ccl_device_forceinline bool kernel_need_sample_pixel(KernelGlobals kg, + ConstIntegratorState state, + ccl_global float *render_buffer) +{ + if (kernel_data.film.pass_adaptive_aux_buffer == PASS_UNUSED) { + return true; + } + + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset; + + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + return buffer[aux_w_offset] == 0.0f; +} + +/* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */ + +ccl_device bool kernel_adaptive_sampling_convergence_check(KernelGlobals kg, + ccl_global float *render_buffer, + int x, + int y, + float threshold, + bool reset, + int offset, + int stride) +{ + kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_sample_count != PASS_UNUSED); + + const int render_pixel_index = offset + x + y * stride; + ccl_global float *buffer = render_buffer + + (uint64_t)render_pixel_index * kernel_data.film.pass_stride; + + /* TODO(Stefan): Is this better in linear, sRGB or something else? */ + + const float4 A = kernel_read_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer); + if (!reset && A.w != 0.0f) { + /* If the pixel was considered converged, its state will not change in this kernel. Early + * output before doing any math. + * + * TODO(sergey): On a GPU it might be better to keep thread alive for better coherency? */ + return true; + } + + const float4 I = kernel_read_pass_float4(buffer + kernel_data.film.pass_combined); + + const float sample = __float_as_uint(buffer[kernel_data.film.pass_sample_count]); + const float inv_sample = 1.0f / sample; + + /* The per pixel error as seen in section 2.1 of + * "A hierarchical automatic stopping condition for Monte Carlo global illumination" */ + const float error_difference = (fabsf(I.x - A.x) + fabsf(I.y - A.y) + fabsf(I.z - A.z)) * + inv_sample; + const float error_normalize = sqrtf((I.x + I.y + I.z) * inv_sample); + /* A small epsilon is added to the divisor to prevent division by zero. */ + const float error = error_difference / (0.0001f + error_normalize); + const bool did_converge = (error < threshold); + + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + buffer[aux_w_offset] = did_converge; + + return did_converge; +} + +/* This is a simple box filter in two passes. + * When a pixel demands more adaptive samples, let its neighboring pixels draw more samples too. */ + +ccl_device void kernel_adaptive_sampling_filter_x(KernelGlobals kg, + ccl_global float *render_buffer, + int y, + int start_x, + int width, + int offset, + int stride) +{ + kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); + + bool prev = false; + for (int x = start_x; x < start_x + width; ++x) { + int index = offset + x + y * stride; + ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + + if (buffer[aux_w_offset] == 0.0f) { + if (x > start_x && !prev) { + index = index - 1; + buffer = render_buffer + index * kernel_data.film.pass_stride; + buffer[aux_w_offset] = 0.0f; + } + prev = true; + } + else { + if (prev) { + buffer[aux_w_offset] = 0.0f; + } + prev = false; + } + } +} + +ccl_device void kernel_adaptive_sampling_filter_y(KernelGlobals kg, + ccl_global float *render_buffer, + int x, + int start_y, + int height, + int offset, + int stride) +{ + kernel_assert(kernel_data.film.pass_adaptive_aux_buffer != PASS_UNUSED); + + bool prev = false; + for (int y = start_y; y < start_y + height; ++y) { + int index = offset + x + y * stride; + ccl_global float *buffer = render_buffer + index * kernel_data.film.pass_stride; + const uint aux_w_offset = kernel_data.film.pass_adaptive_aux_buffer + 3; + + if (buffer[aux_w_offset] == 0.0f) { + if (y > start_y && !prev) { + index = index - stride; + buffer = render_buffer + index * kernel_data.film.pass_stride; + buffer[aux_w_offset] = 0.0f; + } + prev = true; + } + else { + if (prev) { + buffer[aux_w_offset] = 0.0f; + } + prev = false; + } + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_id_passes.h b/intern/cycles/kernel/film/id_passes.h similarity index 71% rename from intern/cycles/kernel/kernel_id_passes.h rename to intern/cycles/kernel/film/id_passes.h index 1ca42e933d1..d5b8c90a828 100644 --- a/intern/cycles/kernel/kernel_id_passes.h +++ b/intern/cycles/kernel/film/id_passes.h @@ -14,8 +14,18 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN +/* Element of ID pass stored in the render buffers. + * It is `float2` semantically, but it must be unaligned since the offset of ID passes in the + * render buffers might not meet expected by compiler alignment. */ +typedef struct IDPassBufferElement { + float x; + float y; +} IDPassBufferElement; + ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, int num_slots, float id, @@ -27,7 +37,7 @@ ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, } for (int slot = 0; slot < num_slots; slot++) { - ccl_global float2 *id_buffer = (ccl_global float2 *)buffer; + ccl_global IDPassBufferElement *id_buffer = (ccl_global IDPassBufferElement *)buffer; #ifdef __ATOMIC_PASS_WRITE__ /* If the loop reaches an empty slot, the ID isn't in any slot yet - so add it! */ if (id_buffer[slot].x == ID_NONE) { @@ -65,7 +75,7 @@ ccl_device_inline void kernel_write_id_slots(ccl_global float *buffer, ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_slots) { - ccl_global float2 *id_buffer = (ccl_global float2 *)buffer; + ccl_global IDPassBufferElement *id_buffer = (ccl_global IDPassBufferElement *)buffer; for (int slot = 1; slot < num_slots; ++slot) { if (id_buffer[slot].x == ID_NONE) { return; @@ -73,7 +83,7 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl /* Since we're dealing with a tiny number of elements, insertion sort should be fine. */ int i = slot; while (i > 0 && id_buffer[i].y > id_buffer[i - 1].y) { - float2 swap = id_buffer[i]; + const IDPassBufferElement swap = id_buffer[i]; id_buffer[i] = id_buffer[i - 1]; id_buffer[i - 1] = swap; --i; @@ -81,19 +91,16 @@ ccl_device_inline void kernel_sort_id_slots(ccl_global float *buffer, int num_sl } } -#ifdef __KERNEL_GPU__ /* post-sorting for Cryptomatte */ -ccl_device void kernel_cryptomatte_post( - KernelGlobals *kg, ccl_global float *buffer, uint sample, int x, int y, int offset, int stride) +ccl_device_inline void kernel_cryptomatte_post(KernelGlobals kg, + ccl_global float *render_buffer, + int pixel_index) { - if (sample - 1 == kernel_data.integrator.aa_samples) { - int index = offset + x + y * stride; - int pass_stride = kernel_data.film.pass_stride; - ccl_global float *cryptomatte_buffer = buffer + index * pass_stride + - kernel_data.film.pass_cryptomatte; - kernel_sort_id_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); - } + const int pass_stride = kernel_data.film.pass_stride; + const uint64_t render_buffer_offset = (uint64_t)pixel_index * pass_stride; + ccl_global float *cryptomatte_buffer = render_buffer + render_buffer_offset + + kernel_data.film.pass_cryptomatte; + kernel_sort_id_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); } -#endif CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/passes.h b/intern/cycles/kernel/film/passes.h new file mode 100644 index 00000000000..22b4b779a17 --- /dev/null +++ b/intern/cycles/kernel/film/passes.h @@ -0,0 +1,342 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/geom/geom.h" + +#include "kernel/film/id_passes.h" +#include "kernel/film/write_passes.h" + +CCL_NAMESPACE_BEGIN + +/* Get pointer to pixel in render buffer. */ +ccl_device_forceinline ccl_global float *kernel_pass_pixel_render_buffer( + KernelGlobals kg, ConstIntegratorState state, ccl_global float *ccl_restrict render_buffer) +{ + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + return render_buffer + render_buffer_offset; +} + +#ifdef __DENOISING_FEATURES__ + +ccl_device_forceinline void kernel_write_denoising_features_surface( + KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) +{ + if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DENOISING_FEATURES)) { + return; + } + + /* Skip implicitly transparent surfaces. */ + if (sd->flag & SD_HAS_ONLY_VOLUME) { + return; + } + + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); + + if (kernel_data.film.pass_denoising_depth != PASS_UNUSED) { + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + const float denoising_depth = ensure_finite(average(denoising_feature_throughput) * + sd->ray_length); + kernel_write_pass_float(buffer + kernel_data.film.pass_denoising_depth, denoising_depth); + } + + float3 normal = zero_float3(); + float3 diffuse_albedo = zero_float3(); + float3 specular_albedo = zero_float3(); + float sum_weight = 0.0f, sum_nonspecular_weight = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + continue; + } + + /* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */ + normal += sc->N * sc->sample_weight; + sum_weight += sc->sample_weight; + + float3 closure_albedo = sc->weight; + /* Closures that include a Fresnel term typically have weights close to 1 even though their + * actual contribution is significantly lower. + * To account for this, we scale their weight by the average fresnel factor (the same is also + * done for the sample weight in the BSDF setup, so we don't need to scale that here). */ + if (CLOSURE_IS_BSDF_MICROFACET_FRESNEL(sc->type)) { + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)sc; + closure_albedo *= bsdf->extra->fresnel_color; + } + else if (sc->type == CLOSURE_BSDF_PRINCIPLED_SHEEN_ID) { + ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)sc; + closure_albedo *= bsdf->avg_value; + } + else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { + closure_albedo *= bsdf_principled_hair_albedo(sc); + } + + if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) { + diffuse_albedo += closure_albedo; + sum_nonspecular_weight += sc->sample_weight; + } + else { + specular_albedo += closure_albedo; + } + } + + /* Wait for next bounce if 75% or more sample weight belongs to specular-like closures. */ + if ((sum_weight == 0.0f) || (sum_nonspecular_weight * 4.0f > sum_weight)) { + if (sum_weight != 0.0f) { + normal /= sum_weight; + } + + if (kernel_data.film.pass_denoising_normal != PASS_UNUSED) { + /* Transform normal into camera space. */ + const Transform worldtocamera = kernel_data.cam.worldtocamera; + normal = transform_direction(&worldtocamera, normal); + + const float3 denoising_normal = ensure_finite3(normal); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); + } + + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + const float3 denoising_albedo = ensure_finite3(denoising_feature_throughput * + diffuse_albedo); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + } + + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; + } + else { + INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) *= specular_albedo; + } +} + +ccl_device_forceinline void kernel_write_denoising_features_volume(KernelGlobals kg, + IntegratorState state, + const float3 albedo, + const bool scatter, + ccl_global float *ccl_restrict + render_buffer) +{ + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); + const float3 denoising_feature_throughput = INTEGRATOR_STATE( + state, path, denoising_feature_throughput); + + if (scatter && kernel_data.film.pass_denoising_normal != PASS_UNUSED) { + /* Assume scatter is sufficiently diffuse to stop writing denoising features. */ + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_DENOISING_FEATURES; + + /* Write view direction as normal. */ + const float3 denoising_normal = make_float3(0.0f, 0.0f, -1.0f); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_normal, denoising_normal); + } + + if (kernel_data.film.pass_denoising_albedo != PASS_UNUSED) { + /* Write albedo. */ + const float3 denoising_albedo = ensure_finite3(denoising_feature_throughput * albedo); + kernel_write_pass_float3(buffer + kernel_data.film.pass_denoising_albedo, denoising_albedo); + } +} +#endif /* __DENOISING_FEATURES__ */ + +#ifdef __SHADOW_CATCHER__ + +/* Write shadow catcher passes on a bounce from the shadow catcher object. */ +ccl_device_forceinline void kernel_write_shadow_catcher_bounce_data( + KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) +{ + if (!kernel_data.integrator.has_shadow_catcher) { + return; + } + + kernel_assert(kernel_data.film.pass_shadow_catcher_sample_count != PASS_UNUSED); + kernel_assert(kernel_data.film.pass_shadow_catcher_matte != PASS_UNUSED); + + if (!kernel_shadow_catcher_is_path_split_bounce(kg, state, sd->object_flag)) { + return; + } + + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); + + /* Count sample for the shadow catcher object. */ + kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_sample_count, 1.0f); + + /* Since the split is done, the sample does not contribute to the matte, so accumulate it as + * transparency to the matte. */ + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_write_pass_float(buffer + kernel_data.film.pass_shadow_catcher_matte + 3, + average(throughput)); +} + +#endif /* __SHADOW_CATCHER__ */ + +ccl_device_inline size_t kernel_write_id_pass(ccl_global float *ccl_restrict buffer, + size_t depth, + float id, + float matte_weight) +{ + kernel_write_id_slots(buffer, depth * 2, id, matte_weight); + return depth * 4; +} + +ccl_device_inline void kernel_write_data_passes(KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) +{ +#ifdef __PASSES__ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if (!(path_flag & PATH_RAY_CAMERA)) { + return; + } + + const int flag = kernel_data.film.pass_flag; + + if (!(flag & PASS_ANY)) { + return; + } + + ccl_global float *buffer = kernel_pass_pixel_render_buffer(kg, state, render_buffer); + + if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) { + if (!(sd->flag & SD_TRANSPARENT) || kernel_data.film.pass_alpha_threshold == 0.0f || + average(shader_bsdf_alpha(kg, sd)) >= kernel_data.film.pass_alpha_threshold) { + if (INTEGRATOR_STATE(state, path, sample) == 0) { + if (flag & PASSMASK(DEPTH)) { + const float depth = camera_z_depth(kg, sd->P); + kernel_write_pass_float(buffer + kernel_data.film.pass_depth, depth); + } + if (flag & PASSMASK(OBJECT_ID)) { + const float id = object_pass_id(kg, sd->object); + kernel_write_pass_float(buffer + kernel_data.film.pass_object_id, id); + } + if (flag & PASSMASK(MATERIAL_ID)) { + const float id = shader_pass_id(kg, sd); + kernel_write_pass_float(buffer + kernel_data.film.pass_material_id, id); + } + if (flag & PASSMASK(POSITION)) { + const float3 position = sd->P; + kernel_write_pass_float3(buffer + kernel_data.film.pass_position, position); + } + } + + if (flag & PASSMASK(NORMAL)) { + const float3 normal = shader_bsdf_average_normal(kg, sd); + kernel_write_pass_float3(buffer + kernel_data.film.pass_normal, normal); + } + if (flag & PASSMASK(ROUGHNESS)) { + const float roughness = shader_bsdf_average_roughness(sd); + kernel_write_pass_float(buffer + kernel_data.film.pass_roughness, roughness); + } + if (flag & PASSMASK(UV)) { + const float3 uv = primitive_uv(kg, sd); + kernel_write_pass_float3(buffer + kernel_data.film.pass_uv, uv); + } + if (flag & PASSMASK(MOTION)) { + const float4 speed = primitive_motion_vector(kg, sd); + kernel_write_pass_float4(buffer + kernel_data.film.pass_motion, speed); + kernel_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f); + } + + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SINGLE_PASS_DONE; + } + } + + if (kernel_data.film.cryptomatte_passes) { + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const float matte_weight = average(throughput) * + (1.0f - average(shader_bsdf_transparency(kg, sd))); + if (matte_weight > 0.0f) { + ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte; + if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { + const float id = object_cryptomatte_id(kg, sd->object); + cryptomatte_buffer += kernel_write_id_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); + } + if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { + const float id = shader_cryptomatte_id(kg, sd->shader); + cryptomatte_buffer += kernel_write_id_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); + } + if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { + const float id = object_cryptomatte_asset_id(kg, sd->object); + cryptomatte_buffer += kernel_write_id_pass( + cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight); + } + } + } + + if (flag & PASSMASK(DIFFUSE_COLOR)) { + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_color, + shader_bsdf_diffuse(kg, sd) * throughput); + } + if (flag & PASSMASK(GLOSSY_COLOR)) { + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_color, + shader_bsdf_glossy(kg, sd) * throughput); + } + if (flag & PASSMASK(TRANSMISSION_COLOR)) { + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_color, + shader_bsdf_transmission(kg, sd) * throughput); + } + if (flag & PASSMASK(MIST)) { + /* Bring depth into 0..1 range. */ + const float mist_start = kernel_data.film.mist_start; + const float mist_inv_depth = kernel_data.film.mist_inv_depth; + + const float depth = camera_distance(kg, sd->P); + float mist = saturatef((depth - mist_start) * mist_inv_depth); + + /* Falloff */ + const float mist_falloff = kernel_data.film.mist_falloff; + + if (mist_falloff == 1.0f) + ; + else if (mist_falloff == 2.0f) + mist = mist * mist; + else if (mist_falloff == 0.5f) + mist = sqrtf(mist); + else + mist = powf(mist, mist_falloff); + + /* Modulate by transparency */ + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const float3 alpha = shader_bsdf_alpha(kg, sd); + const float mist_output = (1.0f - mist) * average(throughput * alpha); + + /* Note that the final value in the render buffer we want is 1 - mist_output, + * to avoid having to tracking this in the Integrator state we do the negation + * after rendering. */ + kernel_write_pass_float(buffer + kernel_data.film.pass_mist, mist_output); + } +#endif +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/film/read.h b/intern/cycles/kernel/film/read.h new file mode 100644 index 00000000000..d308a9818e2 --- /dev/null +++ b/intern/cycles/kernel/film/read.h @@ -0,0 +1,532 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Common utilities. + */ + +/* The input buffer contains transparency = 1 - alpha, this converts it to + * alpha. Also clamp since alpha might end up outside of 0..1 due to Russian + * roulette. */ +ccl_device_forceinline float film_transparency_to_alpha(float transparency) +{ + return saturatef(1.0f - transparency); +} + +ccl_device_inline float film_get_scale(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + if (kfilm_convert->pass_sample_count == PASS_UNUSED) { + return kfilm_convert->scale; + } + + if (kfilm_convert->pass_use_filter) { + const uint sample_count = *( + (ccl_global const uint *)(buffer + kfilm_convert->pass_sample_count)); + return 1.0f / sample_count; + } + + return 1.0f; +} + +ccl_device_inline float film_get_scale_exposure(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + if (kfilm_convert->pass_sample_count == PASS_UNUSED) { + return kfilm_convert->scale_exposure; + } + + const float scale = film_get_scale(kfilm_convert, buffer); + + if (kfilm_convert->pass_use_exposure) { + return scale * kfilm_convert->exposure; + } + + return scale; +} + +ccl_device_inline bool film_get_scale_and_scale_exposure( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict scale, + ccl_private float *ccl_restrict scale_exposure) +{ + if (kfilm_convert->pass_sample_count == PASS_UNUSED) { + *scale = kfilm_convert->scale; + *scale_exposure = kfilm_convert->scale_exposure; + return true; + } + + const uint sample_count = *( + (ccl_global const uint *)(buffer + kfilm_convert->pass_sample_count)); + if (!sample_count) { + *scale = 0.0f; + *scale_exposure = 0.0f; + return false; + } + + if (kfilm_convert->pass_use_filter) { + *scale = 1.0f / sample_count; + } + else { + *scale = 1.0f; + } + + if (kfilm_convert->pass_use_exposure) { + *scale_exposure = *scale * kfilm_convert->exposure; + } + else { + *scale_exposure = *scale; + } + + return true; +} + +/* -------------------------------------------------------------------- + * Float (scalar) passes. + */ + +ccl_device_inline void film_get_pass_pixel_depth(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; + + pixel[0] = (f == 0.0f) ? 1e10f : f * scale_exposure; +} + +ccl_device_inline void film_get_pass_pixel_mist(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; + + /* Note that we accumulate 1 - mist in the kernel to avoid having to + * track the mist values in the integrator state. */ + pixel[0] = saturatef(1.0f - f * scale_exposure); +} + +ccl_device_inline void film_get_pass_pixel_sample_count( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + /* TODO(sergey): Consider normalizing into the [0..1] range, so that it is possible to see + * meaningful value when adaptive sampler stopped rendering image way before the maximum + * number of samples was reached (for examples when number of samples is set to 0 in + * viewport). */ + + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; + + pixel[0] = __float_as_uint(f) * kfilm_convert->scale; +} + +ccl_device_inline void film_get_pass_pixel_float(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 1); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + const float f = *in; + + pixel[0] = f * scale_exposure; +} + +/* -------------------------------------------------------------------- + * Float 3 passes. + */ + +ccl_device_inline void film_get_pass_pixel_light_path( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 3); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + /* Read light pass. */ + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + float3 f = make_float3(in[0], in[1], in[2]); + + /* Optionally add indirect light pass. */ + if (kfilm_convert->pass_indirect != PASS_UNUSED) { + ccl_global const float *in_indirect = buffer + kfilm_convert->pass_indirect; + const float3 f_indirect = make_float3(in_indirect[0], in_indirect[1], in_indirect[2]); + f += f_indirect; + } + + /* Optionally divide out color. */ + if (kfilm_convert->pass_divide != PASS_UNUSED) { + ccl_global const float *in_divide = buffer + kfilm_convert->pass_divide; + const float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]); + f = safe_divide_even_color(f, f_divide); + + /* Exposure only, sample scale cancels out. */ + f *= kfilm_convert->exposure; + } + else { + /* Sample scale and exposure. */ + f *= film_get_scale_exposure(kfilm_convert, buffer); + } + + pixel[0] = f.x; + pixel[1] = f.y; + pixel[2] = f.z; +} + +ccl_device_inline void film_get_pass_pixel_float3(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 3); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale_exposure = film_get_scale_exposure(kfilm_convert, buffer); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + + const float3 f = make_float3(in[0], in[1], in[2]) * scale_exposure; + + pixel[0] = f.x; + pixel[1] = f.y; + pixel[2] = f.z; +} + +/* -------------------------------------------------------------------- + * Float4 passes. + */ + +ccl_device_inline void film_get_pass_pixel_motion(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_motion_weight != PASS_UNUSED); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + ccl_global const float *in_weight = buffer + kfilm_convert->pass_motion_weight; + + const float weight = in_weight[0]; + const float weight_inv = (weight > 0.0f) ? 1.0f / weight : 0.0f; + + const float4 motion = make_float4(in[0], in[1], in[2], in[3]) * weight_inv; + + pixel[0] = motion.x; + pixel[1] = motion.y; + pixel[2] = motion.z; + pixel[3] = motion.w; +} + +ccl_device_inline void film_get_pass_pixel_cryptomatte( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + const float scale = film_get_scale(kfilm_convert, buffer); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + + const float4 f = make_float4(in[0], in[1], in[2], in[3]); + + /* x and z contain integer IDs, don't rescale them. + * y and w contain matte weights, they get scaled. */ + pixel[0] = f.x; + pixel[1] = f.y * scale; + pixel[2] = f.z; + pixel[3] = f.w * scale; +} + +ccl_device_inline void film_get_pass_pixel_float4(ccl_global const KernelFilmConvert *ccl_restrict + kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + float scale, scale_exposure; + film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure); + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + + const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure; + const float alpha = in[3] * scale; + + pixel[0] = color.x; + pixel[1] = color.y; + pixel[2] = color.z; + pixel[3] = alpha; +} + +ccl_device_inline void film_get_pass_pixel_combined( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 4); + + /* 3rd channel contains transparency = 1 - alpha for the combined pass. */ + + kernel_assert(kfilm_convert->num_components == 4); + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + + float scale, scale_exposure; + if (!film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure)) { + pixel[0] = 0.0f; + pixel[1] = 0.0f; + pixel[2] = 0.0f; + pixel[3] = 0.0f; + return; + } + + ccl_global const float *in = buffer + kfilm_convert->pass_offset; + + const float3 color = make_float3(in[0], in[1], in[2]) * scale_exposure; + const float alpha = in[3] * scale; + + pixel[0] = color.x; + pixel[1] = color.y; + pixel[2] = color.z; + pixel[3] = film_transparency_to_alpha(alpha); +} + +/* -------------------------------------------------------------------- + * Shadow catcher. + */ + +ccl_device_inline float3 film_calculate_shadow_catcher_denoised( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED); + + float scale, scale_exposure; + film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure); + + ccl_global const float *in_catcher = buffer + kfilm_convert->pass_shadow_catcher; + + const float3 pixel = make_float3(in_catcher[0], in_catcher[1], in_catcher[2]) * scale_exposure; + + return pixel; +} + +ccl_device_inline float3 safe_divide_shadow_catcher(float3 a, float3 b) +{ + float x, y, z; + + x = (b.x != 0.0f) ? a.x / b.x : 1.0f; + y = (b.y != 0.0f) ? a.y / b.y : 1.0f; + z = (b.z != 0.0f) ? a.z / b.z : 1.0f; + + return make_float3(x, y, z); +} + +ccl_device_inline float3 +film_calculate_shadow_catcher(ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + /* For the shadow catcher pass we divide combined pass by the shadow catcher. + * Note that denoised shadow catcher pass contains value which only needs ot be scaled (but not + * to be calculated as division). */ + + if (kfilm_convert->is_denoised) { + return film_calculate_shadow_catcher_denoised(kfilm_convert, buffer); + } + + kernel_assert(kfilm_convert->pass_shadow_catcher_sample_count != PASS_UNUSED); + + /* If there is no shadow catcher object in this pixel, there is no modification of the light + * needed, so return one. */ + ccl_global const float *in_catcher_sample_count = + buffer + kfilm_convert->pass_shadow_catcher_sample_count; + const float num_samples = in_catcher_sample_count[0]; + if (num_samples == 0.0f) { + return one_float3(); + } + + kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED); + ccl_global const float *in_catcher = buffer + kfilm_convert->pass_shadow_catcher; + + /* NOTE: It is possible that the Shadow Catcher pass is requested as an output without actual + * shadow catcher objects in the scene. In this case there will be no auxiliary passes required + * for the decision (to save up memory). So delay the asserts to this point so that the number of + * samples check handles such configuration. */ + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_combined != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_shadow_catcher_matte != PASS_UNUSED); + + ccl_global const float *in_combined = buffer + kfilm_convert->pass_combined; + ccl_global const float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte; + + /* No scaling needed. The integration works in way that number of samples in the combined and + * shadow catcher passes are the same, and exposure is canceled during the division. */ + const float3 color_catcher = make_float3(in_catcher[0], in_catcher[1], in_catcher[2]); + const float3 color_combined = make_float3(in_combined[0], in_combined[1], in_combined[2]); + const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]); + + /* Need to ignore contribution of the matte object when doing division (otherwise there will be + * artifacts caused by anti-aliasing). Since combined pass is used for adaptive sampling and need + * to contain matte objects, we subtract matte objects contribution here. This is the same as if + * the matte objects were not accumulated to the combined pass. */ + const float3 combined_no_matte = color_combined - color_matte; + + const float3 shadow_catcher = safe_divide_shadow_catcher(combined_no_matte, color_catcher); + + const float scale = film_get_scale(kfilm_convert, buffer); + const float transparency = in_combined[3] * scale; + const float alpha = film_transparency_to_alpha(transparency); + + /* Alpha-over on white using transparency of the combined pass. This allows to eliminate + * artifacts which are happening on an edge of a shadow catcher when using transparent film. + * Note that we treat shadow catcher as straight alpha here because alpha got canceled out + * during the division. */ + const float3 pixel = (1.0f - alpha) * one_float3() + alpha * shadow_catcher; + + return pixel; +} + +ccl_device_inline float4 film_calculate_shadow_catcher_matte_with_shadow( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer) +{ + /* The approximation of the shadow is 1 - average(shadow_catcher_pass). A better approximation + * is possible. + * + * The matte is alpha-overed onto the shadow (which is kind of alpha-overing shadow onto footage, + * and then alpha-overing synthetic objects on top). */ + + kernel_assert(kfilm_convert->pass_offset != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_shadow_catcher != PASS_UNUSED); + kernel_assert(kfilm_convert->pass_shadow_catcher_matte != PASS_UNUSED); + + float scale, scale_exposure; + if (!film_get_scale_and_scale_exposure(kfilm_convert, buffer, &scale, &scale_exposure)) { + return make_float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + ccl_global const float *in_matte = buffer + kfilm_convert->pass_shadow_catcher_matte; + + const float3 shadow_catcher = film_calculate_shadow_catcher(kfilm_convert, buffer); + const float3 color_matte = make_float3(in_matte[0], in_matte[1], in_matte[2]) * scale_exposure; + + const float transparency = in_matte[3] * scale; + const float alpha = saturatef(1.0f - transparency); + + const float alpha_matte = (1.0f - alpha) * (1.0f - average(shadow_catcher)) + alpha; + + if (kfilm_convert->use_approximate_shadow_catcher_background) { + kernel_assert(kfilm_convert->pass_background != PASS_UNUSED); + + ccl_global const float *in_background = buffer + kfilm_convert->pass_background; + const float3 color_background = make_float3( + in_background[0], in_background[1], in_background[2]) * + scale_exposure; + const float3 alpha_over = color_matte + color_background * (1.0f - alpha_matte); + return make_float4(alpha_over.x, alpha_over.y, alpha_over.z, 1.0f); + } + + return make_float4(color_matte.x, color_matte.y, color_matte.z, alpha_matte); +} + +ccl_device_inline void film_get_pass_pixel_shadow_catcher( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components >= 3); + + const float3 pixel_value = film_calculate_shadow_catcher(kfilm_convert, buffer); + + pixel[0] = pixel_value.x; + pixel[1] = pixel_value.y; + pixel[2] = pixel_value.z; +} + +ccl_device_inline void film_get_pass_pixel_shadow_catcher_matte_with_shadow( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + kernel_assert(kfilm_convert->num_components == 3 || kfilm_convert->num_components == 4); + + const float4 pixel_value = film_calculate_shadow_catcher_matte_with_shadow(kfilm_convert, + buffer); + + pixel[0] = pixel_value.x; + pixel[1] = pixel_value.y; + pixel[2] = pixel_value.z; + if (kfilm_convert->num_components == 4) { + pixel[3] = pixel_value.w; + } +} + +/* -------------------------------------------------------------------- + * Compositing and overlays. + */ + +ccl_device_inline void film_apply_pass_pixel_overlays_rgba( + ccl_global const KernelFilmConvert *ccl_restrict kfilm_convert, + ccl_global const float *ccl_restrict buffer, + ccl_private float *ccl_restrict pixel) +{ + if (kfilm_convert->show_active_pixels && + kfilm_convert->pass_adaptive_aux_buffer != PASS_UNUSED) { + if (buffer[kfilm_convert->pass_adaptive_aux_buffer + 3] == 0.0f) { + const float3 active_rgb = make_float3(1.0f, 0.0f, 0.0f); + const float3 mix_rgb = interp(make_float3(pixel[0], pixel[1], pixel[2]), active_rgb, 0.5f); + pixel[0] = mix_rgb.x; + pixel[1] = mix_rgb.y; + pixel[2] = mix_rgb.z; + } + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_write_passes.h b/intern/cycles/kernel/film/write_passes.h similarity index 57% rename from intern/cycles/kernel/kernel_write_passes.h rename to intern/cycles/kernel/film/write_passes.h index 410218d91d4..9d379495629 100644 --- a/intern/cycles/kernel/kernel_write_passes.h +++ b/intern/cycles/kernel/film/write_passes.h @@ -14,23 +14,25 @@ * limitations under the License. */ -#if defined(__SPLIT_KERNEL__) || defined(__KERNEL_CUDA__) +#pragma once + +#ifdef __KERNEL_GPU__ # define __ATOMIC_PASS_WRITE__ #endif CCL_NAMESPACE_BEGIN -ccl_device_inline void kernel_write_pass_float(ccl_global float *buffer, float value) +ccl_device_inline void kernel_write_pass_float(ccl_global float *ccl_restrict buffer, float value) { - ccl_global float *buf = buffer; #ifdef __ATOMIC_PASS_WRITE__ - atomic_add_and_fetch_float(buf, value); + atomic_add_and_fetch_float(buffer, value); #else - *buf += value; + *buffer += value; #endif } -ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 value) +ccl_device_inline void kernel_write_pass_float3(ccl_global float *ccl_restrict buffer, + float3 value) { #ifdef __ATOMIC_PASS_WRITE__ ccl_global float *buf_x = buffer + 0; @@ -41,12 +43,14 @@ ccl_device_inline void kernel_write_pass_float3(ccl_global float *buffer, float3 atomic_add_and_fetch_float(buf_y, value.y); atomic_add_and_fetch_float(buf_z, value.z); #else - ccl_global float3 *buf = (ccl_global float3 *)buffer; - *buf += value; + buffer[0] += value.x; + buffer[1] += value.y; + buffer[2] += value.z; #endif } -ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 value) +ccl_device_inline void kernel_write_pass_float4(ccl_global float *ccl_restrict buffer, + float4 value) { #ifdef __ATOMIC_PASS_WRITE__ ccl_global float *buf_x = buffer + 0; @@ -59,37 +63,26 @@ ccl_device_inline void kernel_write_pass_float4(ccl_global float *buffer, float4 atomic_add_and_fetch_float(buf_z, value.z); atomic_add_and_fetch_float(buf_w, value.w); #else - ccl_global float4 *buf = (ccl_global float4 *)buffer; - *buf += value; -#endif -} - -#ifdef __DENOISING_FEATURES__ -ccl_device_inline void kernel_write_pass_float_variance(ccl_global float *buffer, float value) -{ - kernel_write_pass_float(buffer, value); - - /* The online one-pass variance update that's used for the megakernel can't easily be implemented - * with atomics, so for the split kernel the E[x^2] - 1/N * (E[x])^2 fallback is used. */ - kernel_write_pass_float(buffer + 1, value * value); -} - -# ifdef __ATOMIC_PASS_WRITE__ -# define kernel_write_pass_float3_unaligned kernel_write_pass_float3 -# else -ccl_device_inline void kernel_write_pass_float3_unaligned(ccl_global float *buffer, float3 value) -{ buffer[0] += value.x; buffer[1] += value.y; buffer[2] += value.z; + buffer[3] += value.w; +#endif } -# endif -ccl_device_inline void kernel_write_pass_float3_variance(ccl_global float *buffer, float3 value) +ccl_device_inline float kernel_read_pass_float(ccl_global float *ccl_restrict buffer) { - kernel_write_pass_float3_unaligned(buffer, value); - kernel_write_pass_float3_unaligned(buffer + 3, value * value); + return *buffer; +} + +ccl_device_inline float3 kernel_read_pass_float3(ccl_global float *ccl_restrict buffer) +{ + return make_float3(buffer[0], buffer[1], buffer[2]); +} + +ccl_device_inline float4 kernel_read_pass_float4(ccl_global float *ccl_restrict buffer) +{ + return make_float4(buffer[0], buffer[1], buffer[2], buffer[3]); } -#endif /* __DENOISING_FEATURES__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter.h b/intern/cycles/kernel/filter/filter.h deleted file mode 100644 index b067e53a8bf..00000000000 --- a/intern/cycles/kernel/filter/filter.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __FILTER_H__ -#define __FILTER_H__ - -/* CPU Filter Kernel Interface */ - -#include "util/util_types.h" - -#include "kernel/filter/filter_defines.h" - -CCL_NAMESPACE_BEGIN - -#define KERNEL_NAME_JOIN(x, y, z) x##_##y##_##z -#define KERNEL_NAME_EVAL(arch, name) KERNEL_NAME_JOIN(kernel, arch, name) -#define KERNEL_FUNCTION_FULL_NAME(name) KERNEL_NAME_EVAL(KERNEL_ARCH, name) - -#define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/filter_cpu.h" - -#define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/filter_cpu.h" - -CCL_NAMESPACE_END - -#endif /* __FILTER_H__ */ diff --git a/intern/cycles/kernel/filter/filter_defines.h b/intern/cycles/kernel/filter/filter_defines.h deleted file mode 100644 index 1c0ac5e2cb7..00000000000 --- a/intern/cycles/kernel/filter/filter_defines.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __FILTER_DEFINES_H__ -#define __FILTER_DEFINES_H__ - -#define DENOISE_FEATURES 11 -#define TRANSFORM_SIZE (DENOISE_FEATURES * DENOISE_FEATURES) -#define XTWX_SIZE (((DENOISE_FEATURES + 1) * (DENOISE_FEATURES + 2)) / 2) -#define XTWY_SIZE (DENOISE_FEATURES + 1) - -#define DENOISE_MAX_FRAMES 16 - -typedef struct TileInfo { - int offsets[9]; - int strides[9]; - int x[4]; - int y[4]; - int from_render; - int frames[DENOISE_MAX_FRAMES]; - int num_frames; - /* TODO(lukas): CUDA doesn't have uint64_t... */ -#ifdef __KERNEL_OPENCL__ - ccl_global float *buffers[9]; -#else - long long int buffers[9]; -#endif -} TileInfo; - -#ifdef __KERNEL_OPENCL__ -# define CCL_FILTER_TILE_INFO \ - ccl_global TileInfo *tile_info, ccl_global float *tile_buffer_1, \ - ccl_global float *tile_buffer_2, ccl_global float *tile_buffer_3, \ - ccl_global float *tile_buffer_4, ccl_global float *tile_buffer_5, \ - ccl_global float *tile_buffer_6, ccl_global float *tile_buffer_7, \ - ccl_global float *tile_buffer_8, ccl_global float *tile_buffer_9 -# define CCL_FILTER_TILE_INFO_ARG \ - tile_info, tile_buffer_1, tile_buffer_2, tile_buffer_3, tile_buffer_4, tile_buffer_5, \ - tile_buffer_6, tile_buffer_7, tile_buffer_8, tile_buffer_9 -# define ccl_get_tile_buffer(id) \ - (id == 0 ? tile_buffer_1 : \ - id == 1 ? tile_buffer_2 : \ - id == 2 ? tile_buffer_3 : \ - id == 3 ? tile_buffer_4 : \ - id == 4 ? tile_buffer_5 : \ - id == 5 ? tile_buffer_6 : \ - id == 6 ? tile_buffer_7 : \ - id == 7 ? tile_buffer_8 : \ - tile_buffer_9) -#else -# ifdef __KERNEL_CUDA__ -# define CCL_FILTER_TILE_INFO ccl_global TileInfo *tile_info -# else -# define CCL_FILTER_TILE_INFO TileInfo *tile_info -# endif -# define ccl_get_tile_buffer(id) (tile_info->buffers[id]) -#endif - -#endif /* __FILTER_DEFINES_H__*/ diff --git a/intern/cycles/kernel/filter/filter_features.h b/intern/cycles/kernel/filter/filter_features.h deleted file mode 100644 index 8a2af957146..00000000000 --- a/intern/cycles/kernel/filter/filter_features.h +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#define ccl_get_feature(buffer, pass) (buffer)[(pass)*pass_stride] - -/* Loop over the pixels in the range [low.x, high.x) x [low.y, high.y).+ * pixel_buffer always - * points to the current pixel in the first pass. Repeat the loop for every secondary frame if - * there are any. */ -#define FOR_PIXEL_WINDOW \ - for (int frame = 0; frame < tile_info->num_frames; frame++) { \ - pixel.z = tile_info->frames[frame]; \ - pixel_buffer = buffer + (low.y - rect.y) * buffer_w + (low.x - rect.x) + \ - frame * frame_stride; \ - for (pixel.y = low.y; pixel.y < high.y; pixel.y++) { \ - for (pixel.x = low.x; pixel.x < high.x; pixel.x++, pixel_buffer++) { - -#define END_FOR_PIXEL_WINDOW \ - } \ - pixel_buffer += buffer_w - (high.x - low.x); \ - } \ - } - -ccl_device_inline void filter_get_features(int3 pixel, - const ccl_global float *ccl_restrict buffer, - float *features, - bool use_time, - const float *ccl_restrict mean, - int pass_stride) -{ - features[0] = pixel.x; - features[1] = pixel.y; - features[2] = fabsf(ccl_get_feature(buffer, 0)); - features[3] = ccl_get_feature(buffer, 1); - features[4] = ccl_get_feature(buffer, 2); - features[5] = ccl_get_feature(buffer, 3); - features[6] = ccl_get_feature(buffer, 4); - features[7] = ccl_get_feature(buffer, 5); - features[8] = ccl_get_feature(buffer, 6); - features[9] = ccl_get_feature(buffer, 7); - if (use_time) { - features[10] = pixel.z; - } - if (mean) { - for (int i = 0; i < (use_time ? 11 : 10); i++) { - features[i] -= mean[i]; - } - } -} - -ccl_device_inline void filter_get_feature_scales(int3 pixel, - const ccl_global float *ccl_restrict buffer, - float *scales, - bool use_time, - const float *ccl_restrict mean, - int pass_stride) -{ - scales[0] = fabsf(pixel.x - mean[0]); - scales[1] = fabsf(pixel.y - mean[1]); - scales[2] = fabsf(fabsf(ccl_get_feature(buffer, 0)) - mean[2]); - scales[3] = len_squared(make_float3(ccl_get_feature(buffer, 1) - mean[3], - ccl_get_feature(buffer, 2) - mean[4], - ccl_get_feature(buffer, 3) - mean[5])); - scales[4] = fabsf(ccl_get_feature(buffer, 4) - mean[6]); - scales[5] = len_squared(make_float3(ccl_get_feature(buffer, 5) - mean[7], - ccl_get_feature(buffer, 6) - mean[8], - ccl_get_feature(buffer, 7) - mean[9])); - if (use_time) { - scales[6] = fabsf(pixel.z - mean[10]); - } -} - -ccl_device_inline void filter_calculate_scale(float *scale, bool use_time) -{ - scale[0] = 1.0f / max(scale[0], 0.01f); - scale[1] = 1.0f / max(scale[1], 0.01f); - scale[2] = 1.0f / max(scale[2], 0.01f); - if (use_time) { - scale[10] = 1.0f / max(scale[6], 0.01f); - } - scale[6] = 1.0f / max(scale[4], 0.01f); - scale[7] = scale[8] = scale[9] = 1.0f / max(sqrtf(scale[5]), 0.01f); - scale[3] = scale[4] = scale[5] = 1.0f / max(sqrtf(scale[3]), 0.01f); -} - -ccl_device_inline float3 filter_get_color(const ccl_global float *ccl_restrict buffer, - int pass_stride) -{ - return make_float3( - ccl_get_feature(buffer, 8), ccl_get_feature(buffer, 9), ccl_get_feature(buffer, 10)); -} - -ccl_device_inline void design_row_add(float *design_row, - int rank, - const ccl_global float *ccl_restrict transform, - int stride, - int row, - float feature, - int transform_row_stride) -{ - for (int i = 0; i < rank; i++) { - design_row[1 + i] += transform[(row * transform_row_stride + i) * stride] * feature; - } -} - -/* Fill the design row. */ -ccl_device_inline void filter_get_design_row_transform( - int3 p_pixel, - const ccl_global float *ccl_restrict p_buffer, - int3 q_pixel, - const ccl_global float *ccl_restrict q_buffer, - int pass_stride, - int rank, - float *design_row, - const ccl_global float *ccl_restrict transform, - int stride, - bool use_time) -{ - int num_features = use_time ? 11 : 10; - - design_row[0] = 1.0f; - math_vector_zero(design_row + 1, rank); - -#define DESIGN_ROW_ADD(I, F) \ - design_row_add(design_row, rank, transform, stride, I, F, num_features); - DESIGN_ROW_ADD(0, q_pixel.x - p_pixel.x); - DESIGN_ROW_ADD(1, q_pixel.y - p_pixel.y); - DESIGN_ROW_ADD(2, fabsf(ccl_get_feature(q_buffer, 0)) - fabsf(ccl_get_feature(p_buffer, 0))); - DESIGN_ROW_ADD(3, ccl_get_feature(q_buffer, 1) - ccl_get_feature(p_buffer, 1)); - DESIGN_ROW_ADD(4, ccl_get_feature(q_buffer, 2) - ccl_get_feature(p_buffer, 2)); - DESIGN_ROW_ADD(5, ccl_get_feature(q_buffer, 3) - ccl_get_feature(p_buffer, 3)); - DESIGN_ROW_ADD(6, ccl_get_feature(q_buffer, 4) - ccl_get_feature(p_buffer, 4)); - DESIGN_ROW_ADD(7, ccl_get_feature(q_buffer, 5) - ccl_get_feature(p_buffer, 5)); - DESIGN_ROW_ADD(8, ccl_get_feature(q_buffer, 6) - ccl_get_feature(p_buffer, 6)); - DESIGN_ROW_ADD(9, ccl_get_feature(q_buffer, 7) - ccl_get_feature(p_buffer, 7)); - if (use_time) { - DESIGN_ROW_ADD(10, q_pixel.z - p_pixel.z) - } -#undef DESIGN_ROW_ADD -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_features_sse.h b/intern/cycles/kernel/filter/filter_features_sse.h deleted file mode 100644 index 59d4ace2bef..00000000000 --- a/intern/cycles/kernel/filter/filter_features_sse.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#define ccl_get_feature_sse(pass) load_float4(buffer + (pass)*pass_stride) - -/* Loop over the pixels in the range [low.x, high.x) x [low.y, high.y), 4 at a time. - * pixel_buffer always points to the first of the 4 current pixel in the first pass. - * x4 and y4 contain the coordinates of the four pixels, active_pixels contains a mask that's set - * for all pixels within the window. Repeat the loop for every secondary frame if there are any. */ -#define FOR_PIXEL_WINDOW_SSE \ - for (int frame = 0; frame < tile_info->num_frames; frame++) { \ - pixel.z = tile_info->frames[frame]; \ - pixel_buffer = buffer + (low.y - rect.y) * buffer_w + (low.x - rect.x) + \ - frame * frame_stride; \ - float4 t4 = make_float4(pixel.z); \ - for (pixel.y = low.y; pixel.y < high.y; pixel.y++) { \ - float4 y4 = make_float4(pixel.y); \ - for (pixel.x = low.x; pixel.x < high.x; pixel.x += 4, pixel_buffer += 4) { \ - float4 x4 = make_float4(pixel.x) + make_float4(0.0f, 1.0f, 2.0f, 3.0f); \ - int4 active_pixels = x4 < make_float4(high.x); - -#define END_FOR_PIXEL_WINDOW_SSE \ - } \ - pixel_buffer += buffer_w - (high.x - low.x); \ - } \ - } - -ccl_device_inline void filter_get_features_sse(float4 x, - float4 y, - float4 t, - int4 active_pixels, - const float *ccl_restrict buffer, - float4 *features, - bool use_time, - const float4 *ccl_restrict mean, - int pass_stride) -{ - int num_features = use_time ? 11 : 10; - - features[0] = x; - features[1] = y; - features[2] = fabs(ccl_get_feature_sse(0)); - features[3] = ccl_get_feature_sse(1); - features[4] = ccl_get_feature_sse(2); - features[5] = ccl_get_feature_sse(3); - features[6] = ccl_get_feature_sse(4); - features[7] = ccl_get_feature_sse(5); - features[8] = ccl_get_feature_sse(6); - features[9] = ccl_get_feature_sse(7); - if (use_time) { - features[10] = t; - } - - if (mean) { - for (int i = 0; i < num_features; i++) { - features[i] = features[i] - mean[i]; - } - } - for (int i = 0; i < num_features; i++) { - features[i] = mask(active_pixels, features[i]); - } -} - -ccl_device_inline void filter_get_feature_scales_sse(float4 x, - float4 y, - float4 t, - int4 active_pixels, - const float *ccl_restrict buffer, - float4 *scales, - bool use_time, - const float4 *ccl_restrict mean, - int pass_stride) -{ - scales[0] = fabs(x - mean[0]); - scales[1] = fabs(y - mean[1]); - scales[2] = fabs(fabs(ccl_get_feature_sse(0)) - mean[2]); - scales[3] = sqr(ccl_get_feature_sse(1) - mean[3]) + sqr(ccl_get_feature_sse(2) - mean[4]) + - sqr(ccl_get_feature_sse(3) - mean[5]); - scales[4] = fabs(ccl_get_feature_sse(4) - mean[6]); - scales[5] = sqr(ccl_get_feature_sse(5) - mean[7]) + sqr(ccl_get_feature_sse(6) - mean[8]) + - sqr(ccl_get_feature_sse(7) - mean[9]); - if (use_time) { - scales[6] = fabs(t - mean[10]); - } - - for (int i = 0; i < (use_time ? 7 : 6); i++) - scales[i] = mask(active_pixels, scales[i]); -} - -ccl_device_inline void filter_calculate_scale_sse(float4 *scale, bool use_time) -{ - scale[0] = rcp(max(reduce_max(scale[0]), make_float4(0.01f))); - scale[1] = rcp(max(reduce_max(scale[1]), make_float4(0.01f))); - scale[2] = rcp(max(reduce_max(scale[2]), make_float4(0.01f))); - if (use_time) { - scale[10] = rcp(max(reduce_max(scale[6]), make_float4(0.01f))); - } - scale[6] = rcp(max(reduce_max(scale[4]), make_float4(0.01f))); - scale[7] = scale[8] = scale[9] = rcp(max(reduce_max(sqrt(scale[5])), make_float4(0.01f))); - scale[3] = scale[4] = scale[5] = rcp(max(reduce_max(sqrt(scale[3])), make_float4(0.01f))); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_kernel.h b/intern/cycles/kernel/filter/filter_kernel.h deleted file mode 100644 index 2ef03dc0a02..00000000000 --- a/intern/cycles/kernel/filter/filter_kernel.h +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/util_color.h" -#include "util/util_math.h" -#include "util/util_math_fast.h" -#include "util/util_texture.h" - -#include "util/util_atomic.h" -#include "util/util_math_matrix.h" - -#include "kernel/filter/filter_defines.h" - -#include "kernel/filter/filter_features.h" -#ifdef __KERNEL_SSE3__ -# include "kernel/filter/filter_features_sse.h" -#endif - -#include "kernel/filter/filter_prefilter.h" - -#ifdef __KERNEL_GPU__ -# include "kernel/filter/filter_transform_gpu.h" -#else -# ifdef __KERNEL_SSE3__ -# include "kernel/filter/filter_transform_sse.h" -# else -# include "kernel/filter/filter_transform.h" -# endif -#endif - -#include "kernel/filter/filter_reconstruction.h" - -#ifdef __KERNEL_CPU__ -# include "kernel/filter/filter_nlm_cpu.h" -#else -# include "kernel/filter/filter_nlm_gpu.h" -#endif diff --git a/intern/cycles/kernel/filter/filter_nlm_cpu.h b/intern/cycles/kernel/filter/filter_nlm_cpu.h deleted file mode 100644 index 24200c29203..00000000000 --- a/intern/cycles/kernel/filter/filter_nlm_cpu.h +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#define load4_a(buf, ofs) (*((float4 *)((buf) + (ofs)))) -#define load4_u(buf, ofs) load_float4((buf) + (ofs)) - -ccl_device_inline void kernel_filter_nlm_calc_difference(int dx, - int dy, - const float *ccl_restrict weight_image, - const float *ccl_restrict variance_image, - const float *ccl_restrict scale_image, - float *difference_image, - int4 rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - /* Strides need to be aligned to 16 bytes. */ - kernel_assert((stride % 4) == 0 && (channel_offset % 4) == 0); - - int aligned_lowx = rect.x & (~3); - const int numChannels = (channel_offset > 0) ? 3 : 1; - const float4 channel_fac = make_float4(1.0f / numChannels); - - for (int y = rect.y; y < rect.w; y++) { - int idx_p = y * stride + aligned_lowx; - int idx_q = (y + dy) * stride + aligned_lowx + dx + frame_offset; - for (int x = aligned_lowx; x < rect.z; x += 4, idx_p += 4, idx_q += 4) { - float4 diff = make_float4(0.0f); - float4 scale_fac; - if (scale_image) { - scale_fac = clamp(load4_a(scale_image, idx_p) / load4_u(scale_image, idx_q), - make_float4(0.25f), - make_float4(4.0f)); - } - else { - scale_fac = make_float4(1.0f); - } - for (int c = 0, chan_ofs = 0; c < numChannels; c++, chan_ofs += channel_offset) { - /* idx_p is guaranteed to be aligned, but idx_q isn't. */ - float4 color_p = load4_a(weight_image, idx_p + chan_ofs); - float4 color_q = scale_fac * load4_u(weight_image, idx_q + chan_ofs); - float4 cdiff = color_p - color_q; - float4 var_p = load4_a(variance_image, idx_p + chan_ofs); - float4 var_q = sqr(scale_fac) * load4_u(variance_image, idx_q + chan_ofs); - diff += (cdiff * cdiff - a * (var_p + min(var_p, var_q))) / - (make_float4(1e-8f) + k_2 * (var_p + var_q)); - } - load4_a(difference_image, idx_p) = diff * channel_fac; - } - } -} - -ccl_device_inline void kernel_filter_nlm_blur( - const float *ccl_restrict difference_image, float *out_image, int4 rect, int stride, int f) -{ - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - const int low = max(rect.y, y - f); - const int high = min(rect.w, y + f + 1); - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) = make_float4(0.0f); - } - for (int y1 = low; y1 < high; y1++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) += load4_a(difference_image, y1 * stride + x); - } - } - float fac = 1.0f / (high - low); - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) *= fac; - } - } -} - -ccl_device_inline void nlm_blur_horizontal( - const float *ccl_restrict difference_image, float *out_image, int4 rect, int stride, int f) -{ - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) = make_float4(0.0f); - } - } - - for (int dx = -f; dx <= f; dx++) { - aligned_lowx = round_down(rect.x - min(0, dx), 4); - int highx = rect.z - max(0, dx); - int4 lowx4 = make_int4(rect.x - min(0, dx)); - int4 highx4 = make_int4(rect.z - max(0, dx)); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < highx; x += 4) { - int4 x4 = make_int4(x) + make_int4(0, 1, 2, 3); - int4 active = (x4 >= lowx4) & (x4 < highx4); - - float4 diff = load4_u(difference_image, y * stride + x + dx); - load4_a(out_image, y * stride + x) += mask(active, diff); - } - } - } - - aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - float4 x4 = make_float4(x) + make_float4(0.0f, 1.0f, 2.0f, 3.0f); - float4 low = max(make_float4(rect.x), x4 - make_float4(f)); - float4 high = min(make_float4(rect.z), x4 + make_float4(f + 1)); - load4_a(out_image, y * stride + x) *= rcp(high - low); - } - } -} - -ccl_device_inline void kernel_filter_nlm_calc_weight( - const float *ccl_restrict difference_image, float *out_image, int4 rect, int stride, int f) -{ - nlm_blur_horizontal(difference_image, out_image, rect, stride, f); - - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - load4_a(out_image, y * stride + x) = fast_expf4( - -max(load4_a(out_image, y * stride + x), make_float4(0.0f))); - } - } -} - -ccl_device_inline void kernel_filter_nlm_update_output(int dx, - int dy, - const float *ccl_restrict difference_image, - const float *ccl_restrict image, - float *temp_image, - float *out_image, - float *accum_image, - int4 rect, - int channel_offset, - int stride, - int f) -{ - nlm_blur_horizontal(difference_image, temp_image, rect, stride, f); - - int aligned_lowx = round_down(rect.x, 4); - for (int y = rect.y; y < rect.w; y++) { - for (int x = aligned_lowx; x < rect.z; x += 4) { - int4 x4 = make_int4(x) + make_int4(0, 1, 2, 3); - int4 active = (x4 >= make_int4(rect.x)) & (x4 < make_int4(rect.z)); - - int idx_p = y * stride + x, idx_q = (y + dy) * stride + (x + dx); - - float4 weight = load4_a(temp_image, idx_p); - load4_a(accum_image, idx_p) += mask(active, weight); - - float4 val = load4_u(image, idx_q); - if (channel_offset) { - val += load4_u(image, idx_q + channel_offset); - val += load4_u(image, idx_q + 2 * channel_offset); - val *= 1.0f / 3.0f; - } - - load4_a(out_image, idx_p) += mask(active, weight * val); - } - } -} - -ccl_device_inline void kernel_filter_nlm_construct_gramian(int dx, - int dy, - int t, - const float *ccl_restrict - difference_image, - const float *ccl_restrict buffer, - float *transform, - int *rank, - float *XtWX, - float3 *XtWY, - int4 rect, - int4 filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time) -{ - int4 clip_area = rect_clip(rect, filter_window); - /* fy and fy are in filter-window-relative coordinates, - * while x and y are in feature-window-relative coordinates. */ - for (int y = clip_area.y; y < clip_area.w; y++) { - for (int x = clip_area.x; x < clip_area.z; x++) { - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - float sum = 0.0f; - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - float weight = sum * (1.0f / (high - low)); - - int storage_ofs = coord_to_local_index(filter_window, x, y); - float *l_transform = transform + storage_ofs * TRANSFORM_SIZE; - float *l_XtWX = XtWX + storage_ofs * XTWX_SIZE; - float3 *l_XtWY = XtWY + storage_ofs * XTWY_SIZE; - int *l_rank = rank + storage_ofs; - - kernel_filter_construct_gramian(x, - y, - 1, - dx, - dy, - t, - stride, - pass_stride, - frame_offset, - use_time, - buffer, - l_transform, - l_rank, - weight, - l_XtWX, - l_XtWY, - 0); - } - } -} - -ccl_device_inline void kernel_filter_nlm_normalize(float *out_image, - const float *ccl_restrict accum_image, - int4 rect, - int w) -{ - for (int y = rect.y; y < rect.w; y++) { - for (int x = rect.x; x < rect.z; x++) { - out_image[y * w + x] /= accum_image[y * w + x]; - } - } -} - -#undef load4_a -#undef load4_u - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_nlm_gpu.h b/intern/cycles/kernel/filter/filter_nlm_gpu.h deleted file mode 100644 index 650c743f34f..00000000000 --- a/intern/cycles/kernel/filter/filter_nlm_gpu.h +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Determines pixel coordinates and offset for the current thread. - * Returns whether the thread should do any work. - * - * All coordinates are relative to the denoising buffer! - * - * Window is the rect that should be processed. - * co is filled with (x, y, dx, dy). - */ -ccl_device_inline bool get_nlm_coords_window( - int w, int h, int r, int stride, int4 *rect, int4 *co, int *ofs, int4 window) -{ - /* Determine the pixel offset that this thread should apply. */ - int s = 2 * r + 1; - int si = ccl_global_id(1); - int sx = si % s; - int sy = si / s; - if (sy >= s) { - return false; - } - - /* Pixels still need to lie inside the denoising buffer after applying the offset, - * so determine the area for which this is the case. */ - int dx = sx - r; - int dy = sy - r; - - *rect = make_int4(max(0, -dx), max(0, -dy), w - max(0, dx), h - max(0, dy)); - - /* Find the intersection of the area that we want to process (window) and the area - * that can be processed (rect) to get the final area for this offset. */ - int4 clip_area = rect_clip(window, *rect); - - /* If the radius is larger than one of the sides of the window, - * there will be shifts for which there is no usable pixel at all. */ - if (!rect_is_valid(clip_area)) { - return false; - } - - /* Map the linear thread index to pixels inside the clip area. */ - int x, y; - if (!local_index_to_coord(clip_area, ccl_global_id(0), &x, &y)) { - return false; - } - - *co = make_int4(x, y, dx, dy); - - *ofs = (sy * s + sx) * stride; - - return true; -} - -ccl_device_inline bool get_nlm_coords( - int w, int h, int r, int stride, int4 *rect, int4 *co, int *ofs) -{ - return get_nlm_coords_window(w, h, r, stride, rect, co, ofs, make_int4(0, 0, w, h)); -} - -ccl_device_inline void kernel_filter_nlm_calc_difference( - int x, - int y, - int dx, - int dy, - const ccl_global float *ccl_restrict weight_image, - const ccl_global float *ccl_restrict variance_image, - const ccl_global float *ccl_restrict scale_image, - ccl_global float *difference_image, - int4 rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - int idx_p = y * stride + x, idx_q = (y + dy) * stride + (x + dx) + frame_offset; - int numChannels = channel_offset ? 3 : 1; - - float diff = 0.0f; - float scale_fac = 1.0f; - if (scale_image) { - scale_fac = clamp(scale_image[idx_p] / scale_image[idx_q], 0.25f, 4.0f); - } - - for (int c = 0; c < numChannels; c++, idx_p += channel_offset, idx_q += channel_offset) { - float cdiff = weight_image[idx_p] - scale_fac * weight_image[idx_q]; - float pvar = variance_image[idx_p]; - float qvar = sqr(scale_fac) * variance_image[idx_q]; - diff += (cdiff * cdiff - a * (pvar + min(pvar, qvar))) / (1e-8f + k_2 * (pvar + qvar)); - } - if (numChannels > 1) { - diff *= 1.0f / numChannels; - } - difference_image[y * stride + x] = diff; -} - -ccl_device_inline void kernel_filter_nlm_blur(int x, - int y, - const ccl_global float *ccl_restrict - difference_image, - ccl_global float *out_image, - int4 rect, - int stride, - int f) -{ - float sum = 0.0f; - const int low = max(rect.y, y - f); - const int high = min(rect.w, y + f + 1); - for (int y1 = low; y1 < high; y1++) { - sum += difference_image[y1 * stride + x]; - } - sum *= 1.0f / (high - low); - out_image[y * stride + x] = sum; -} - -ccl_device_inline void kernel_filter_nlm_calc_weight(int x, - int y, - const ccl_global float *ccl_restrict - difference_image, - ccl_global float *out_image, - int4 rect, - int stride, - int f) -{ - float sum = 0.0f; - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - sum *= 1.0f / (high - low); - out_image[y * stride + x] = fast_expf(-max(sum, 0.0f)); -} - -ccl_device_inline void kernel_filter_nlm_update_output(int x, - int y, - int dx, - int dy, - const ccl_global float *ccl_restrict - difference_image, - const ccl_global float *ccl_restrict image, - ccl_global float *out_image, - ccl_global float *accum_image, - int4 rect, - int channel_offset, - int stride, - int f) -{ - float sum = 0.0f; - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - sum *= 1.0f / (high - low); - - int idx_p = y * stride + x, idx_q = (y + dy) * stride + (x + dx); - if (out_image) { - atomic_add_and_fetch_float(accum_image + idx_p, sum); - - float val = image[idx_q]; - if (channel_offset) { - val += image[idx_q + channel_offset]; - val += image[idx_q + 2 * channel_offset]; - val *= 1.0f / 3.0f; - } - atomic_add_and_fetch_float(out_image + idx_p, sum * val); - } - else { - accum_image[idx_p] = sum; - } -} - -ccl_device_inline void kernel_filter_nlm_construct_gramian( - int x, - int y, - int dx, - int dy, - int t, - const ccl_global float *ccl_restrict difference_image, - const ccl_global float *ccl_restrict buffer, - const ccl_global float *ccl_restrict transform, - ccl_global int *rank, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 rect, - int4 filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time, - int localIdx) -{ - const int low = max(rect.x, x - f); - const int high = min(rect.z, x + f + 1); - float sum = 0.0f; - for (int x1 = low; x1 < high; x1++) { - sum += difference_image[y * stride + x1]; - } - float weight = sum * (1.0f / (high - low)); - - /* Reconstruction data is only stored for pixels inside the filter window, - * so compute the pixels's index in there. */ - int storage_ofs = coord_to_local_index(filter_window, x, y); - transform += storage_ofs; - rank += storage_ofs; - XtWX += storage_ofs; - XtWY += storage_ofs; - - kernel_filter_construct_gramian(x, - y, - rect_size(filter_window), - dx, - dy, - t, - stride, - pass_stride, - frame_offset, - use_time, - buffer, - transform, - rank, - weight, - XtWX, - XtWY, - localIdx); -} - -ccl_device_inline void kernel_filter_nlm_normalize(int x, - int y, - ccl_global float *out_image, - const ccl_global float *ccl_restrict - accum_image, - int stride) -{ - out_image[y * stride + x] /= accum_image[y * stride + x]; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_prefilter.h b/intern/cycles/kernel/filter/filter_prefilter.h deleted file mode 100644 index 97cecba190e..00000000000 --- a/intern/cycles/kernel/filter/filter_prefilter.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/** - * First step of the shadow prefiltering, performs the shadow division and stores all data - * in a nice and easy rectangular array that can be passed to the NLM filter. - * - * Calculates: - * \param unfiltered: Contains the two half images of the shadow feature pass - * \param sampleVariance: The sample-based variance calculated in the kernel. - * Note: This calculation is biased in general, - * and especially here since the variance of the ratio can only be approximated. - * \param sampleVarianceV: Variance of the sample variance estimation, quite noisy - * (since it's essentially the buffer variance of the two variance halves) - * \param bufferVariance: The buffer-based variance of the shadow feature. - * Unbiased, but quite noisy. - */ -ccl_device void kernel_filter_divide_shadow(int sample, - CCL_FILTER_TILE_INFO, - int x, - int y, - ccl_global float *unfilteredA, - ccl_global float *unfilteredB, - ccl_global float *sampleVariance, - ccl_global float *sampleVarianceV, - ccl_global float *bufferVariance, - int4 rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int xtile = (x < tile_info->x[1]) ? 0 : ((x < tile_info->x[2]) ? 1 : 2); - int ytile = (y < tile_info->y[1]) ? 0 : ((y < tile_info->y[2]) ? 1 : 2); - int tile = ytile * 3 + xtile; - - int offset = tile_info->offsets[tile]; - int stride = tile_info->strides[tile]; - const ccl_global float *ccl_restrict center_buffer = (ccl_global float *)ccl_get_tile_buffer( - tile); - center_buffer += (y * stride + x + offset) * buffer_pass_stride; - center_buffer += buffer_denoising_offset + 14; - - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - unfilteredA[idx] = center_buffer[1] / max(center_buffer[0], 1e-7f); - unfilteredB[idx] = center_buffer[4] / max(center_buffer[3], 1e-7f); - - float varA = center_buffer[2]; - float varB = center_buffer[5]; - int odd_sample = (sample + 1) / 2; - int even_sample = sample / 2; - - /* Approximate variance as E[x^2] - 1/N * (E[x])^2, since online variance - * update does not work efficiently with atomics in the kernel. */ - varA = max(0.0f, varA - unfilteredA[idx] * unfilteredA[idx] * odd_sample); - varB = max(0.0f, varB - unfilteredB[idx] * unfilteredB[idx] * even_sample); - - varA /= max(odd_sample - 1, 1); - varB /= max(even_sample - 1, 1); - - sampleVariance[idx] = 0.5f * (varA + varB) / sample; - sampleVarianceV[idx] = 0.5f * (varA - varB) * (varA - varB) / (sample * sample); - bufferVariance[idx] = 0.5f * (unfilteredA[idx] - unfilteredB[idx]) * - (unfilteredA[idx] - unfilteredB[idx]); -} - -/* Load a regular feature from the render buffers into the denoise buffer. - * Parameters: - * - sample: The sample amount in the buffer, used to normalize the buffer. - * - m_offset, v_offset: Render Buffer Pass offsets of mean and variance of the feature. - * - x, y: Current pixel - * - mean, variance: Target denoise buffers. - * - rect: The prefilter area (lower pixels inclusive, upper pixels exclusive). - */ -ccl_device void kernel_filter_get_feature(int sample, - CCL_FILTER_TILE_INFO, - int m_offset, - int v_offset, - int x, - int y, - ccl_global float *mean, - ccl_global float *variance, - float scale, - int4 rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int xtile = (x < tile_info->x[1]) ? 0 : ((x < tile_info->x[2]) ? 1 : 2); - int ytile = (y < tile_info->y[1]) ? 0 : ((y < tile_info->y[2]) ? 1 : 2); - int tile = ytile * 3 + xtile; - ccl_global float *center_buffer = ((ccl_global float *)ccl_get_tile_buffer(tile)) + - (tile_info->offsets[tile] + y * tile_info->strides[tile] + x) * - buffer_pass_stride + - buffer_denoising_offset; - - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - float val = scale * center_buffer[m_offset]; - mean[idx] = val; - - if (v_offset >= 0) { - if (sample > 1) { - /* Approximate variance as E[x^2] - 1/N * (E[x])^2, since online variance - * update does not work efficiently with atomics in the kernel. */ - variance[idx] = max( - 0.0f, (center_buffer[v_offset] - val * val * sample) / (sample * (sample - 1))); - } - else { - /* Can't compute variance with single sample, just set it very high. */ - variance[idx] = 1e10f; - } - } -} - -ccl_device void kernel_filter_write_feature(int sample, - int x, - int y, - int4 buffer_params, - ccl_global float *from, - ccl_global float *buffer, - int out_offset, - int4 rect) -{ - ccl_global float *combined_buffer = buffer + (y * buffer_params.y + x + buffer_params.x) * - buffer_params.z; - - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - combined_buffer[out_offset] = from[idx]; -} - -#define GET_COLOR(image) \ - make_float3(image[idx], image[idx + pass_stride], image[idx + 2 * pass_stride]) -#define SET_COLOR(image, color) \ - image[idx] = color.x; \ - image[idx + pass_stride] = color.y; \ - image[idx + 2 * pass_stride] = color.z - -ccl_device void kernel_filter_detect_outliers(int x, - int y, - ccl_global float *in, - ccl_global float *variance_out, - ccl_global float *depth, - ccl_global float *image_out, - int4 rect, - int pass_stride) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - - ccl_global float *image_in = in; - ccl_global float *variance_in = in + 3 * pass_stride; - - int n = 0; - float values[25]; - float pixel_variance, max_variance = 0.0f; - for (int y1 = max(y - 2, rect.y); y1 < min(y + 3, rect.w); y1++) { - for (int x1 = max(x - 2, rect.x); x1 < min(x + 3, rect.z); x1++) { - int idx = (y1 - rect.y) * buffer_w + (x1 - rect.x); - float3 color = GET_COLOR(image_in); - color = max(color, make_float3(0.0f, 0.0f, 0.0f)); - float L = average(color); - - /* Find the position of L. */ - int i; - for (i = 0; i < n; i++) { - if (values[i] > L) - break; - } - /* Make space for L by shifting all following values to the right. */ - for (int j = n; j > i; j--) { - values[j] = values[j - 1]; - } - /* Insert L. */ - values[i] = L; - n++; - - float3 pixel_var = GET_COLOR(variance_in); - float var = average(pixel_var); - if ((x1 == x) && (y1 == y)) { - pixel_variance = (pixel_var.x < 0.0f || pixel_var.y < 0.0f || pixel_var.z < 0.0f) ? -1.0f : - var; - } - else { - max_variance = max(max_variance, var); - } - } - } - - max_variance += 1e-4f; - - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - float3 color = GET_COLOR(image_in); - float3 variance = GET_COLOR(variance_in); - color = max(color, make_float3(0.0f, 0.0f, 0.0f)); - variance = max(variance, make_float3(0.0f, 0.0f, 0.0f)); - - float L = average(color); - - float ref = 2.0f * values[(int)(n * 0.75f)]; - - /* Slightly offset values to avoid false positives in (almost) black areas. */ - max_variance += 1e-5f; - ref -= 1e-5f; - - if (L > ref) { - /* The pixel appears to be an outlier. - * However, it may just be a legitimate highlight. Therefore, it is checked how likely it is - * that the pixel should actually be at the reference value: If the reference is within the - * 3-sigma interval, the pixel is assumed to be a statistical outlier. Otherwise, it is very - * unlikely that the pixel should be darker, which indicates a legitimate highlight. - */ - - if (pixel_variance < 0.0f || pixel_variance > 9.0f * max_variance) { - depth[idx] = -depth[idx]; - color *= ref / L; - variance = make_float3(max_variance, max_variance, max_variance); - } - else { - float stddev = sqrtf(pixel_variance); - if (L - 3 * stddev < ref) { - /* The pixel is an outlier, so negate the depth value to mark it as one. - * Also, scale its brightness down to the outlier threshold to avoid trouble with the NLM - * weights. */ - depth[idx] = -depth[idx]; - float fac = ref / L; - color *= fac; - variance *= sqr(fac); - } - } - } - - /* Apply log(1+x) transform to compress highlights and avoid halos in the denoised results. - * Variance is transformed accordingly - the derivative of the transform is 1/(1+x), so we - * scale by the square of that (since we have variance instead of standard deviation). */ - color = color_highlight_compress(color, &variance); - - SET_COLOR(image_out, color); - SET_COLOR(variance_out, variance); -} - -#undef GET_COLOR -#undef SET_COLOR - -/* Combine A/B buffers. - * Calculates the combined mean and the buffer variance. */ -ccl_device void kernel_filter_combine_halves(int x, - int y, - ccl_global float *mean, - ccl_global float *variance, - ccl_global float *a, - ccl_global float *b, - int4 rect, - int r) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - int idx = (y - rect.y) * buffer_w + (x - rect.x); - - if (mean) - mean[idx] = 0.5f * (a[idx] + b[idx]); - if (variance) { - if (r == 0) - variance[idx] = 0.25f * (a[idx] - b[idx]) * (a[idx] - b[idx]); - else { - variance[idx] = 0.0f; - float values[25]; - int numValues = 0; - for (int py = max(y - r, rect.y); py < min(y + r + 1, rect.w); py++) { - for (int px = max(x - r, rect.x); px < min(x + r + 1, rect.z); px++) { - int pidx = (py - rect.y) * buffer_w + (px - rect.x); - values[numValues++] = 0.25f * (a[pidx] - b[pidx]) * (a[pidx] - b[pidx]); - } - } - /* Insertion-sort the variances (fast enough for 25 elements). */ - for (int i = 1; i < numValues; i++) { - float v = values[i]; - int j; - for (j = i - 1; j >= 0 && values[j] > v; j--) - values[j + 1] = values[j]; - values[j + 1] = v; - } - variance[idx] = values[(7 * numValues) / 8]; - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_reconstruction.h b/intern/cycles/kernel/filter/filter_reconstruction.h deleted file mode 100644 index 17941689ad5..00000000000 --- a/intern/cycles/kernel/filter/filter_reconstruction.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device_inline void kernel_filter_construct_gramian(int x, - int y, - int storage_stride, - int dx, - int dy, - int t, - int buffer_stride, - int pass_stride, - int frame_offset, - bool use_time, - const ccl_global float *ccl_restrict buffer, - const ccl_global float *ccl_restrict - transform, - ccl_global int *rank, - float weight, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int localIdx) -{ - if (weight < 1e-3f) { - return; - } - - int p_offset = y * buffer_stride + x; - int q_offset = (y + dy) * buffer_stride + (x + dx) + frame_offset; - -#ifdef __KERNEL_GPU__ - const int stride = storage_stride; -#else - const int stride = 1; - (void)storage_stride; -#endif - -#ifdef __KERNEL_CUDA__ - ccl_local float shared_design_row[(DENOISE_FEATURES + 1) * CCL_MAX_LOCAL_SIZE]; - ccl_local_param float *design_row = shared_design_row + localIdx * (DENOISE_FEATURES + 1); -#else - float design_row[DENOISE_FEATURES + 1]; -#endif - - float3 q_color = filter_get_color(buffer + q_offset, pass_stride); - - /* If the pixel was flagged as an outlier during prefiltering, skip it. */ - if (ccl_get_feature(buffer + q_offset, 0) < 0.0f) { - return; - } - - filter_get_design_row_transform(make_int3(x, y, t), - buffer + p_offset, - make_int3(x + dx, y + dy, t), - buffer + q_offset, - pass_stride, - *rank, - design_row, - transform, - stride, - use_time); - -#ifdef __KERNEL_GPU__ - math_trimatrix_add_gramian_strided(XtWX, (*rank) + 1, design_row, weight, stride); - math_vec3_add_strided(XtWY, (*rank) + 1, design_row, weight * q_color, stride); -#else - math_trimatrix_add_gramian(XtWX, (*rank) + 1, design_row, weight); - math_vec3_add(XtWY, (*rank) + 1, design_row, weight * q_color); -#endif -} - -ccl_device_inline void kernel_filter_finalize(int x, - int y, - ccl_global float *buffer, - ccl_global int *rank, - int storage_stride, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 buffer_params, - int sample) -{ -#ifdef __KERNEL_GPU__ - const int stride = storage_stride; -#else - const int stride = 1; - (void)storage_stride; -#endif - - if (XtWX[0] < 1e-3f) { - /* There is not enough information to determine a denoised result. - * As a fallback, keep the original value of the pixel. */ - return; - } - - /* The weighted average of pixel colors (essentially, the NLM-filtered image). - * In case the solution of the linear model fails due to numerical issues or - * returns nonsensical negative values, fall back to this value. */ - float3 mean_color = XtWY[0] / XtWX[0]; - - math_trimatrix_vec3_solve(XtWX, XtWY, (*rank) + 1, stride); - - float3 final_color = XtWY[0]; - if (!isfinite3_safe(final_color) || - (final_color.x < -0.01f || final_color.y < -0.01f || final_color.z < -0.01f)) { - final_color = mean_color; - } - - /* Clamp pixel value to positive values and reverse the highlight compression transform. */ - final_color = color_highlight_uncompress(max(final_color, make_float3(0.0f, 0.0f, 0.0f))); - - ccl_global float *combined_buffer = buffer + (y * buffer_params.y + x + buffer_params.x) * - buffer_params.z; - if (buffer_params.w >= 0) { - final_color *= sample; - if (buffer_params.w > 0) { - final_color.x += combined_buffer[buffer_params.w + 0]; - final_color.y += combined_buffer[buffer_params.w + 1]; - final_color.z += combined_buffer[buffer_params.w + 2]; - } - } - combined_buffer[0] = final_color.x; - combined_buffer[1] = final_color.y; - combined_buffer[2] = final_color.z; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_transform.h b/intern/cycles/kernel/filter/filter_transform.h deleted file mode 100644 index 880a661214e..00000000000 --- a/intern/cycles/kernel/filter/filter_transform.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_filter_construct_transform(const float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - int x, - int y, - int4 rect, - int pass_stride, - int frame_stride, - bool use_time, - float *transform, - int *rank, - int radius, - float pca_threshold) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - - float features[DENOISE_FEATURES]; - - const float *ccl_restrict pixel_buffer; - int3 pixel; - - int num_features = use_time ? 11 : 10; - - /* === Calculate denoising window. === */ - int2 low = make_int2(max(rect.x, x - radius), max(rect.y, y - radius)); - int2 high = make_int2(min(rect.z, x + radius + 1), min(rect.w, y + radius + 1)); - int num_pixels = (high.y - low.y) * (high.x - low.x) * tile_info->num_frames; - - /* === Shift feature passes to have mean 0. === */ - float feature_means[DENOISE_FEATURES]; - math_vector_zero(feature_means, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, NULL, pass_stride); - math_vector_add(feature_means, features, num_features); - } - END_FOR_PIXEL_WINDOW - - math_vector_scale(feature_means, 1.0f / num_pixels, num_features); - - /* === Scale the shifted feature passes to a range of [-1; 1] === - * Will be baked into the transform later. */ - float feature_scale[DENOISE_FEATURES]; - math_vector_zero(feature_scale, num_features); - - FOR_PIXEL_WINDOW - { - filter_get_feature_scales(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_max(feature_scale, features, num_features); - } - END_FOR_PIXEL_WINDOW - - filter_calculate_scale(feature_scale, use_time); - - /* === Generate the feature transformation. === - * This transformation maps the num_features-dimensional feature space to a reduced feature - * (r-feature) space which generally has fewer dimensions. - * This mainly helps to prevent over-fitting. */ - float feature_matrix[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_zero(feature_matrix, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_mul(features, feature_scale, num_features); - math_matrix_add_gramian(feature_matrix, num_features, features, 1.0f); - } - END_FOR_PIXEL_WINDOW - - math_matrix_jacobi_eigendecomposition(feature_matrix, transform, num_features, 1); - *rank = 0; - /* Prevent over-fitting when a small window is used. */ - int max_rank = min(num_features, num_pixels / 3); - if (pca_threshold < 0.0f) { - float threshold_energy = 0.0f; - for (int i = 0; i < num_features; i++) { - threshold_energy += feature_matrix[i * num_features + i]; - } - threshold_energy *= 1.0f - (-pca_threshold); - - float reduced_energy = 0.0f; - for (int i = 0; i < max_rank; i++, (*rank)++) { - if (i >= 2 && reduced_energy >= threshold_energy) - break; - float s = feature_matrix[i * num_features + i]; - reduced_energy += s; - } - } - else { - for (int i = 0; i < max_rank; i++, (*rank)++) { - float s = feature_matrix[i * num_features + i]; - if (i >= 2 && sqrtf(s) < pca_threshold) - break; - } - } - - /* Bake the feature scaling into the transformation matrix. */ - for (int i = 0; i < (*rank); i++) { - math_vector_mul(transform + i * num_features, feature_scale, num_features); - } - math_matrix_transpose(transform, num_features, 1); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_transform_gpu.h b/intern/cycles/kernel/filter/filter_transform_gpu.h deleted file mode 100644 index ec258a5212a..00000000000 --- a/intern/cycles/kernel/filter/filter_transform_gpu.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_filter_construct_transform(const ccl_global float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - int x, - int y, - int4 rect, - int pass_stride, - int frame_stride, - bool use_time, - ccl_global float *transform, - ccl_global int *rank, - int radius, - float pca_threshold, - int transform_stride, - int localIdx) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - -#ifdef __KERNEL_CUDA__ - ccl_local float shared_features[DENOISE_FEATURES * CCL_MAX_LOCAL_SIZE]; - ccl_local_param float *features = shared_features + localIdx * DENOISE_FEATURES; -#else - float features[DENOISE_FEATURES]; -#endif - - int num_features = use_time ? 11 : 10; - - /* === Calculate denoising window. === */ - int2 low = make_int2(max(rect.x, x - radius), max(rect.y, y - radius)); - int2 high = make_int2(min(rect.z, x + radius + 1), min(rect.w, y + radius + 1)); - int num_pixels = (high.y - low.y) * (high.x - low.x) * tile_info->num_frames; - const ccl_global float *ccl_restrict pixel_buffer; - int3 pixel; - - /* === Shift feature passes to have mean 0. === */ - float feature_means[DENOISE_FEATURES]; - math_vector_zero(feature_means, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, NULL, pass_stride); - math_vector_add(feature_means, features, num_features); - } - END_FOR_PIXEL_WINDOW - - math_vector_scale(feature_means, 1.0f / num_pixels, num_features); - - /* === Scale the shifted feature passes to a range of [-1; 1] === - * Will be baked into the transform later. */ - float feature_scale[DENOISE_FEATURES]; - math_vector_zero(feature_scale, num_features); - - FOR_PIXEL_WINDOW - { - filter_get_feature_scales(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_max(feature_scale, features, num_features); - } - END_FOR_PIXEL_WINDOW - - filter_calculate_scale(feature_scale, use_time); - - /* === Generate the feature transformation. === - * This transformation maps the num_features-dimensional feature space to a reduced feature - * (r-feature) space which generally has fewer dimensions. - * This mainly helps to prevent over-fitting. */ - float feature_matrix[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_zero(feature_matrix, num_features); - FOR_PIXEL_WINDOW - { - filter_get_features(pixel, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_mul(features, feature_scale, num_features); - math_matrix_add_gramian(feature_matrix, num_features, features, 1.0f); - } - END_FOR_PIXEL_WINDOW - - math_matrix_jacobi_eigendecomposition(feature_matrix, transform, num_features, transform_stride); - *rank = 0; - /* Prevent over-fitting when a small window is used. */ - int max_rank = min(num_features, num_pixels / 3); - if (pca_threshold < 0.0f) { - float threshold_energy = 0.0f; - for (int i = 0; i < num_features; i++) { - threshold_energy += feature_matrix[i * num_features + i]; - } - threshold_energy *= 1.0f - (-pca_threshold); - - float reduced_energy = 0.0f; - for (int i = 0; i < max_rank; i++, (*rank)++) { - if (i >= 2 && reduced_energy >= threshold_energy) - break; - float s = feature_matrix[i * num_features + i]; - reduced_energy += s; - } - } - else { - for (int i = 0; i < max_rank; i++, (*rank)++) { - float s = feature_matrix[i * num_features + i]; - if (i >= 2 && sqrtf(s) < pca_threshold) - break; - } - } - - math_matrix_transpose(transform, num_features, transform_stride); - - /* Bake the feature scaling into the transformation matrix. */ - for (int i = 0; i < num_features; i++) { - for (int j = 0; j < (*rank); j++) { - transform[(i * num_features + j) * transform_stride] *= feature_scale[i]; - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/filter/filter_transform_sse.h b/intern/cycles/kernel/filter/filter_transform_sse.h deleted file mode 100644 index 0304d990f9f..00000000000 --- a/intern/cycles/kernel/filter/filter_transform_sse.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_filter_construct_transform(const float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - int x, - int y, - int4 rect, - int pass_stride, - int frame_stride, - bool use_time, - float *transform, - int *rank, - int radius, - float pca_threshold) -{ - int buffer_w = align_up(rect.z - rect.x, 4); - - float4 features[DENOISE_FEATURES]; - const float *ccl_restrict pixel_buffer; - int3 pixel; - - int num_features = use_time ? 11 : 10; - - /* === Calculate denoising window. === */ - int2 low = make_int2(max(rect.x, x - radius), max(rect.y, y - radius)); - int2 high = make_int2(min(rect.z, x + radius + 1), min(rect.w, y + radius + 1)); - int num_pixels = (high.y - low.y) * (high.x - low.x) * tile_info->num_frames; - - /* === Shift feature passes to have mean 0. === */ - float4 feature_means[DENOISE_FEATURES]; - math_vector_zero_sse(feature_means, num_features); - FOR_PIXEL_WINDOW_SSE - { - filter_get_features_sse( - x4, y4, t4, active_pixels, pixel_buffer, features, use_time, NULL, pass_stride); - math_vector_add_sse(feature_means, num_features, features); - } - END_FOR_PIXEL_WINDOW_SSE - - float4 pixel_scale = make_float4(1.0f / num_pixels); - for (int i = 0; i < num_features; i++) { - feature_means[i] = reduce_add(feature_means[i]) * pixel_scale; - } - - /* === Scale the shifted feature passes to a range of [-1; 1] === - * Will be baked into the transform later. */ - float4 feature_scale[DENOISE_FEATURES]; - math_vector_zero_sse(feature_scale, num_features); - FOR_PIXEL_WINDOW_SSE - { - filter_get_feature_scales_sse( - x4, y4, t4, active_pixels, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_max_sse(feature_scale, features, num_features); - } - END_FOR_PIXEL_WINDOW_SSE - - filter_calculate_scale_sse(feature_scale, use_time); - - /* === Generate the feature transformation. === - * This transformation maps the num_features-dimensional feature space to a reduced feature - * (r-feature) space which generally has fewer dimensions. - * This mainly helps to prevent over-fitting. */ - float4 feature_matrix_sse[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_zero_sse(feature_matrix_sse, num_features); - FOR_PIXEL_WINDOW_SSE - { - filter_get_features_sse( - x4, y4, t4, active_pixels, pixel_buffer, features, use_time, feature_means, pass_stride); - math_vector_mul_sse(features, num_features, feature_scale); - math_matrix_add_gramian_sse(feature_matrix_sse, num_features, features, make_float4(1.0f)); - } - END_FOR_PIXEL_WINDOW_SSE - - float feature_matrix[DENOISE_FEATURES * DENOISE_FEATURES]; - math_matrix_hsum(feature_matrix, num_features, feature_matrix_sse); - - math_matrix_jacobi_eigendecomposition(feature_matrix, transform, num_features, 1); - - *rank = 0; - /* Prevent over-fitting when a small window is used. */ - int max_rank = min(num_features, num_pixels / 3); - if (pca_threshold < 0.0f) { - float threshold_energy = 0.0f; - for (int i = 0; i < num_features; i++) { - threshold_energy += feature_matrix[i * num_features + i]; - } - threshold_energy *= 1.0f - (-pca_threshold); - - float reduced_energy = 0.0f; - for (int i = 0; i < max_rank; i++, (*rank)++) { - if (i >= 2 && reduced_energy >= threshold_energy) - break; - float s = feature_matrix[i * num_features + i]; - reduced_energy += s; - } - } - else { - for (int i = 0; i < max_rank; i++, (*rank)++) { - float s = feature_matrix[i * num_features + i]; - if (i >= 2 && sqrtf(s) < pca_threshold) - break; - } - } - - math_matrix_transpose(transform, num_features, 1); - - /* Bake the feature scaling into the transformation matrix. */ - for (int i = 0; i < num_features; i++) { - math_vector_scale(transform + i * num_features, feature_scale[i][0], *rank); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom_attribute.h b/intern/cycles/kernel/geom/attribute.h similarity index 87% rename from intern/cycles/kernel/geom/geom_attribute.h rename to intern/cycles/kernel/geom/attribute.h index b37797ac21b..848e0430caa 100644 --- a/intern/cycles/kernel/geom/geom_attribute.h +++ b/intern/cycles/kernel/geom/attribute.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Attributes @@ -25,9 +27,9 @@ CCL_NAMESPACE_BEGIN * Lookup of attributes is different between OSL and SVM, as OSL is ustring * based while for SVM we use integer ids. */ -ccl_device_inline uint subd_triangle_patch(KernelGlobals *kg, const ShaderData *sd); +ccl_device_inline uint subd_triangle_patch(KernelGlobals kg, ccl_private const ShaderData *sd); -ccl_device_inline uint attribute_primitive_type(KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline uint attribute_primitive_type(KernelGlobals kg, ccl_private const ShaderData *sd) { if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && subd_triangle_patch(kg, sd) != ~0) { return ATTR_PRIM_SUBD; @@ -46,13 +48,13 @@ ccl_device_inline AttributeDescriptor attribute_not_found() /* Find attribute based on ID */ -ccl_device_inline uint object_attribute_map_offset(KernelGlobals *kg, int object) +ccl_device_inline uint object_attribute_map_offset(KernelGlobals kg, int object) { return kernel_tex_fetch(__objects, object).attribute_map_offset; } -ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals kg, + ccl_private const ShaderData *sd, uint id) { if (sd->object == OBJECT_NONE) { @@ -98,8 +100,8 @@ ccl_device_inline AttributeDescriptor find_attribute(KernelGlobals *kg, /* Transform matrix attribute on meshes */ -ccl_device Transform primitive_attribute_matrix(KernelGlobals *kg, - const ShaderData *sd, +ccl_device Transform primitive_attribute_matrix(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { Transform tfm; diff --git a/intern/cycles/kernel/geom/geom_curve.h b/intern/cycles/kernel/geom/curve.h similarity index 75% rename from intern/cycles/kernel/geom/geom_curve.h rename to intern/cycles/kernel/geom/curve.h index b5a62a31ca9..7271193eef8 100644 --- a/intern/cycles/kernel/geom/geom_curve.h +++ b/intern/cycles/kernel/geom/curve.h @@ -12,6 +12,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Curve Primitive @@ -25,12 +27,15 @@ CCL_NAMESPACE_BEGIN /* Reading attributes on various curve elements */ -ccl_device float curve_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device float curve_attribute_float(KernelGlobals kg, + ccl_private const ShaderData *sd, + const AttributeDescriptor desc, + ccl_private float *dx, + ccl_private float *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float f0 = kernel_tex_fetch(__attributes_float, desc.offset + k0); @@ -64,15 +69,15 @@ ccl_device float curve_attribute_float( } } -ccl_device float2 curve_attribute_float2(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float2 curve_attribute_float2(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float2 f0 = kernel_tex_fetch(__attributes_float2, desc.offset + k0); @@ -110,15 +115,15 @@ ccl_device float2 curve_attribute_float2(KernelGlobals *kg, } } -ccl_device float3 curve_attribute_float3(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float3 curve_attribute_float3(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float3 f0 = float4_to_float3(kernel_tex_fetch(__attributes_float3, desc.offset + k0)); @@ -152,15 +157,15 @@ ccl_device float3 curve_attribute_float3(KernelGlobals *kg, } } -ccl_device float4 curve_attribute_float4(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 curve_attribute_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float4 *dx, - float4 *dy) + ccl_private float4 *dx, + ccl_private float4 *dy) { if (desc.element & (ATTR_ELEMENT_CURVE_KEY | ATTR_ELEMENT_CURVE_KEY_MOTION)) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 f0 = kernel_tex_fetch(__attributes_float3, desc.offset + k0); @@ -196,13 +201,13 @@ ccl_device float4 curve_attribute_float4(KernelGlobals *kg, /* Curve thickness */ -ccl_device float curve_thickness(KernelGlobals *kg, ShaderData *sd) +ccl_device float curve_thickness(KernelGlobals kg, ccl_private const ShaderData *sd) { float r = 0.0f; if (sd->type & PRIMITIVE_ALL_CURVE) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 P_curve[2]; @@ -224,10 +229,10 @@ ccl_device float curve_thickness(KernelGlobals *kg, ShaderData *sd) /* Curve location for motion pass, linear interpolation between keys and * ignoring radius because we do the same for the motion keys */ -ccl_device float3 curve_motion_center_location(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 curve_motion_center_location(KernelGlobals kg, ccl_private const ShaderData *sd) { - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - int k0 = __float_as_int(curvedata.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + KernelCurve curve = kernel_tex_fetch(__curves, sd->prim); + int k0 = curve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; float4 P_curve[2]; @@ -240,7 +245,7 @@ ccl_device float3 curve_motion_center_location(KernelGlobals *kg, ShaderData *sd /* Curve tangent normal */ -ccl_device float3 curve_tangent_normal(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 curve_tangent_normal(KernelGlobals kg, ccl_private const ShaderData *sd) { float3 tgN = make_float3(0.0f, 0.0f, 0.0f); @@ -260,12 +265,12 @@ ccl_device float3 curve_tangent_normal(KernelGlobals *kg, ShaderData *sd) /* Curve bounds utility function */ -ccl_device_inline void curvebounds(float *lower, - float *upper, - float *extremta, - float *extrema, - float *extremtb, - float *extremb, +ccl_device_inline void curvebounds(ccl_private float *lower, + ccl_private float *upper, + ccl_private float *extremta, + ccl_private float *extrema, + ccl_private float *extremtb, + ccl_private float *extremb, float p0, float p1, float p2, diff --git a/intern/cycles/kernel/geom/geom_curve_intersect.h b/intern/cycles/kernel/geom/curve_intersect.h similarity index 85% rename from intern/cycles/kernel/geom/geom_curve_intersect.h rename to intern/cycles/kernel/geom/curve_intersect.h index e25bf5b4660..fb0b80b281f 100644 --- a/intern/cycles/kernel/geom/geom_curve_intersect.h +++ b/intern/cycles/kernel/geom/curve_intersect.h @@ -15,6 +15,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Curve primitive intersection functions. @@ -84,11 +86,11 @@ ccl_device_inline bool cylinder_intersect(const float3 cylinder_start, const float3 cylinder_end, const float cylinder_radius, const float3 ray_dir, - float2 *t_o, - float *u0_o, - float3 *Ng0_o, - float *u1_o, - float3 *Ng1_o) + ccl_private float2 *t_o, + ccl_private float *u0_o, + ccl_private float3 *Ng0_o, + ccl_private float *u1_o, + ccl_private float3 *Ng1_o) { /* Calculate quadratic equation to solve. */ const float rl = 1.0f / len(cylinder_end - cylinder_start); @@ -167,12 +169,13 @@ ccl_device_inline float2 half_plane_intersect(const float3 P, const float3 N, co } ccl_device bool curve_intersect_iterative(const float3 ray_dir, + ccl_private float *ray_tfar, const float dt, const float4 curve[4], float u, float t, const bool use_backfacing, - Intersection *isect) + ccl_private Intersection *isect) { const float length_ray_dir = len(ray_dir); @@ -230,7 +233,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, if (fabsf(f) < f_err && fabsf(g) < g_err) { t += dt; - if (!(0.0f <= t && t <= isect->t)) { + if (!(0.0f <= t && t <= *ray_tfar)) { return false; /* Rejects NaNs */ } if (!(u >= 0.0f && u <= 1.0f)) { @@ -247,6 +250,7 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, } /* Record intersection. */ + *ray_tfar = t; isect->t = t; isect->u = u; isect->v = 0.0f; @@ -259,8 +263,9 @@ ccl_device bool curve_intersect_iterative(const float3 ray_dir, ccl_device bool curve_intersect_recursive(const float3 ray_orig, const float3 ray_dir, + float ray_tfar, float4 curve[4], - Intersection *isect) + ccl_private Intersection *isect) { /* Move ray closer to make intersection stable. */ const float3 center = float4_to_float3(0.25f * (curve[0] + curve[1] + curve[2] + curve[3])); @@ -339,7 +344,7 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, } /* Intersect with cap-planes. */ - float2 tp = make_float2(-dt, isect->t - dt); + float2 tp = make_float2(-dt, ray_tfar - dt); tp = make_float2(max(tp.x, tc_outer.x), min(tp.y, tc_outer.y)); const float2 h0 = half_plane_intersect( float4_to_float3(P0), float4_to_float3(dP0du), ray_dir); @@ -402,19 +407,19 @@ ccl_device bool curve_intersect_recursive(const float3 ray_orig, CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, dt, curve, u_outer0, tp0.x, use_backfacing, isect); + ray_dir, &ray_tfar, dt, curve, u_outer0, tp0.x, use_backfacing, isect); } else { recurse = true; } } - if (valid1 && (tp1.x + dt <= isect->t)) { + if (valid1 && (tp1.x + dt <= ray_tfar)) { const int termDepth = unstable1 ? CURVE_NUM_BEZIER_SUBDIVISIONS_UNSTABLE : CURVE_NUM_BEZIER_SUBDIVISIONS; if (depth >= termDepth) { found |= curve_intersect_iterative( - ray_dir, dt, curve, u_outer1, tp1.y, use_backfacing, isect); + ray_dir, &ray_tfar, dt, curve, u_outer1, tp1.y, use_backfacing, isect); } else { recurse = true; @@ -469,9 +474,9 @@ ccl_device_inline bool ribbon_intersect_quad(const float ray_tfar, const float3 quad_v1, const float3 quad_v2, const float3 quad_v3, - float *u_o, - float *v_o, - float *t_o) + ccl_private float *u_o, + ccl_private float *v_o, + ccl_private float *t_o) { /* Calculate vertices relative to ray origin? */ const float3 O = make_float3(0.0f, 0.0f, 0.0f); @@ -542,10 +547,10 @@ ccl_device_inline float4 ribbon_to_ray_space(const float3 ray_space[3], ccl_device_inline bool ribbon_intersect(const float3 ray_org, const float3 ray_dir, - const float ray_tfar, + float ray_tfar, const int N, float4 curve[4], - Intersection *isect) + ccl_private Intersection *isect) { /* Transform control points into ray space. */ float3 ray_space[3]; @@ -590,7 +595,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, /* Intersect quad. */ float vu, vv, vt; - bool valid0 = ribbon_intersect_quad(isect->t, lp0, lp1, up1, up0, &vu, &vv, &vt); + bool valid0 = ribbon_intersect_quad(ray_tfar, lp0, lp1, up1, up0, &vu, &vv, &vt); if (valid0) { /* ignore self intersections */ @@ -604,6 +609,7 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, vv = 2.0f * vv - 1.0f; /* Record intersection. */ + ray_tfar = vt; isect->t = vt; isect->u = u + vu * step_size; isect->v = vv; @@ -619,37 +625,24 @@ ccl_device_inline bool ribbon_intersect(const float3 ray_org, return false; } -ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, - Intersection *isect, +ccl_device_forceinline bool curve_intersect(KernelGlobals kg, + ccl_private Intersection *isect, const float3 P, const float3 dir, - uint visibility, + const float tmax, int object, - int curveAddr, + int prim, float time, int type) { const bool is_motion = (type & PRIMITIVE_ALL_MOTION); -# ifndef __KERNEL_OPTIX__ /* See OptiX motion flag OPTIX_MOTION_FLAG_[START|END]_VANISH */ - if (is_motion && kernel_data.bvh.use_bvh_steps) { - const float2 prim_time = kernel_tex_fetch(__prim_time, curveAddr); - if (time < prim_time.x || time > prim_time.y) { - return false; - } - } -# endif + KernelCurve kcurve = kernel_tex_fetch(__curves, prim); - int segment = PRIMITIVE_UNPACK_SEGMENT(type); - int prim = kernel_tex_fetch(__prim_index, curveAddr); - - float4 v00 = kernel_tex_fetch(__curves, prim); - - int k0 = __float_as_int(v00.x) + segment; + int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(type); int k1 = k0 + 1; - - int ka = max(k0 - 1, __float_as_int(v00.x)); - int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1); + int ka = max(k0 - 1, kcurve.first_key); + int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1); float4 curve[4]; if (!is_motion) { @@ -659,21 +652,14 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, curve[3] = kernel_tex_fetch(__curve_keys, kb); } else { - int fobject = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, curveAddr) : object; - motion_curve_keys(kg, fobject, prim, time, ka, k0, k1, kb, curve); + motion_curve_keys(kg, object, prim, time, ka, k0, k1, kb, curve); } -# ifdef __VISIBILITY_FLAG__ - if (!(kernel_tex_fetch(__prim_visibility, curveAddr) & visibility)) { - return false; - } -# endif - if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { /* todo: adaptive number of subdivisions could help performance here. */ const int subdivisions = kernel_data.bvh.curve_subdivisions; - if (ribbon_intersect(P, dir, isect->t, subdivisions, curve, isect)) { - isect->prim = curveAddr; + if (ribbon_intersect(P, dir, tmax, subdivisions, curve, isect)) { + isect->prim = prim; isect->object = object; isect->type = type; return true; @@ -682,8 +668,8 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, return false; } else { - if (curve_intersect_recursive(P, dir, curve, isect)) { - isect->prim = curveAddr; + if (curve_intersect_recursive(P, dir, tmax, curve, isect)) { + isect->prim = prim; isect->object = object; isect->type = type; return true; @@ -693,35 +679,28 @@ ccl_device_forceinline bool curve_intersect(KernelGlobals *kg, } } -ccl_device_inline void curve_shader_setup(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray) +ccl_device_inline void curve_shader_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim) { - float t = isect->t; - float3 P = ray->P; - float3 D = ray->D; - - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D * t); - D = normalize_len(D, &t); + D = safe_normalize_len(D, &t); } - int prim = kernel_tex_fetch(__prim_index, isect->prim); - float4 v00 = kernel_tex_fetch(__curves, prim); + KernelCurve kcurve = kernel_tex_fetch(__curves, isect_prim); - int k0 = __float_as_int(v00.x) + PRIMITIVE_UNPACK_SEGMENT(sd->type); + int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); int k1 = k0 + 1; - - int ka = max(k0 - 1, __float_as_int(v00.x)); - int kb = min(k1 + 1, __float_as_int(v00.x) + __float_as_int(v00.y) - 1); + int ka = max(k0 - 1, kcurve.first_key); + int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1); float4 P_curve[4]; @@ -735,23 +714,20 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, motion_curve_keys(kg, sd->object, sd->prim, sd->time, ka, k0, k1, kb, P_curve); } - sd->u = isect->u; - P = P + D * t; - const float4 dPdu4 = catmull_rom_basis_derivative(P_curve, isect->u); + const float4 dPdu4 = catmull_rom_basis_derivative(P_curve, sd->u); const float3 dPdu = float4_to_float3(dPdu4); if (sd->type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { /* Rounded smooth normals for ribbons, to approximate thick curve shape. */ const float3 tangent = normalize(dPdu); const float3 bitangent = normalize(cross(tangent, -D)); - const float sine = isect->v; + const float sine = sd->v; const float cosine = safe_sqrtf(1.0f - sine * sine); sd->N = normalize(sine * bitangent - cosine * normalize(cross(tangent, bitangent))); sd->Ng = -D; - sd->v = isect->v; # if 0 /* This approximates the position and geometric normal of a thick curve too, @@ -765,8 +741,10 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, /* Thick curves, compute normal using direction from inside the curve. * This could be optimized by recording the normal in the intersection, * however for Optix this would go beyond the size of the payload. */ - const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, isect->u)); - const float3 Ng = normalize(P - P_inside); + /* NOTE: It is possible that P will be the same as P_inside (precision issues, or very small + * radius). In this case use the view direction to approximate the normal. */ + const float3 P_inside = float4_to_float3(catmull_rom_basis_eval(P_curve, sd->u)); + const float3 Ng = (!isequal_float3(P, P_inside)) ? normalize(P - P_inside) : -sd->I; sd->N = Ng; sd->Ng = Ng; @@ -779,20 +757,13 @@ ccl_device_inline void curve_shader_setup(KernelGlobals *kg, sd->dPdv = cross(dPdu, sd->Ng); # endif - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } sd->P = P; - - float4 curvedata = kernel_tex_fetch(__curves, sd->prim); - sd->shader = __float_as_int(curvedata.z); + sd->shader = kernel_tex_fetch(__curves, sd->prim).shader_id; } #endif diff --git a/intern/cycles/kernel/geom/geom.h b/intern/cycles/kernel/geom/geom.h index 5ff4d5f7053..9d023375a35 100644 --- a/intern/cycles/kernel/geom/geom.h +++ b/intern/cycles/kernel/geom/geom.h @@ -14,21 +14,24 @@ * limitations under the License. */ +#pragma once + // clang-format off -#include "kernel/geom/geom_attribute.h" -#include "kernel/geom/geom_object.h" +#include "kernel/geom/attribute.h" +#include "kernel/geom/object.h" #ifdef __PATCH_EVAL__ -# include "kernel/geom/geom_patch.h" +# include "kernel/geom/patch.h" #endif -#include "kernel/geom/geom_triangle.h" -#include "kernel/geom/geom_subd_triangle.h" -#include "kernel/geom/geom_triangle_intersect.h" -#include "kernel/geom/geom_motion_triangle.h" -#include "kernel/geom/geom_motion_triangle_intersect.h" -#include "kernel/geom/geom_motion_triangle_shader.h" -#include "kernel/geom/geom_motion_curve.h" -#include "kernel/geom/geom_curve.h" -#include "kernel/geom/geom_curve_intersect.h" -#include "kernel/geom/geom_volume.h" -#include "kernel/geom/geom_primitive.h" +#include "kernel/geom/triangle.h" +#include "kernel/geom/subd_triangle.h" +#include "kernel/geom/triangle_intersect.h" +#include "kernel/geom/motion_triangle.h" +#include "kernel/geom/motion_triangle_intersect.h" +#include "kernel/geom/motion_triangle_shader.h" +#include "kernel/geom/motion_curve.h" +#include "kernel/geom/curve.h" +#include "kernel/geom/curve_intersect.h" +#include "kernel/geom/volume.h" +#include "kernel/geom/primitive.h" +#include "kernel/geom/shader_data.h" // clang-format on diff --git a/intern/cycles/kernel/geom/geom_motion_curve.h b/intern/cycles/kernel/geom/motion_curve.h similarity index 75% rename from intern/cycles/kernel/geom/geom_motion_curve.h rename to intern/cycles/kernel/geom/motion_curve.h index 0f66f4af755..2dd213d43f6 100644 --- a/intern/cycles/kernel/geom/geom_motion_curve.h +++ b/intern/cycles/kernel/geom/motion_curve.h @@ -12,6 +12,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Motion Curve Primitive @@ -25,32 +27,7 @@ CCL_NAMESPACE_BEGIN #ifdef __HAIR__ -ccl_device_inline int find_attribute_curve_motion(KernelGlobals *kg, - int object, - uint id, - AttributeElement *elem) -{ - /* todo: find a better (faster) solution for this, maybe store offset per object. - * - * NOTE: currently it's not a bottleneck because in test scenes the loop below runs - * zero iterations and rendering is really slow with motion curves. For until other - * areas are speed up it's probably not so crucial to optimize this out. - */ - uint attr_offset = object_attribute_map_offset(kg, object) + ATTR_PRIM_GEOMETRY; - uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - - while (attr_map.x != id) { - attr_offset += ATTR_PRIM_TYPES; - attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - } - - *elem = (AttributeElement)attr_map.y; - - /* return result */ - return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; -} - -ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals kg, int offset, int numkeys, int numsteps, @@ -78,20 +55,19 @@ ccl_device_inline void motion_curve_keys_for_step_linear(KernelGlobals *kg, /* return 2 curve key locations */ ccl_device_inline void motion_curve_keys_linear( - KernelGlobals *kg, int object, int prim, float time, int k0, int k1, float4 keys[2]) + KernelGlobals kg, int object, int prim, float time, int k0, int k1, float4 keys[2]) { /* get motion info */ int numsteps, numkeys; object_motion_info(kg, object, &numsteps, NULL, &numkeys); /* figure out which steps we need to fetch and their interpolation factor */ - int maxstep = numsteps * 2; - int step = min((int)(time * maxstep), maxstep - 1); - float t = time * maxstep - step; + const int maxstep = numsteps * 2; + const int step = min((int)(time * maxstep), maxstep - 1); + const float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_curve_motion(kg, object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + const int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch key coordinates */ @@ -105,7 +81,7 @@ ccl_device_inline void motion_curve_keys_linear( keys[1] = (1.0f - t) * keys[1] + t * next_keys[1]; } -ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg, +ccl_device_inline void motion_curve_keys_for_step(KernelGlobals kg, int offset, int numkeys, int numsteps, @@ -138,7 +114,7 @@ ccl_device_inline void motion_curve_keys_for_step(KernelGlobals *kg, } /* return 2 curve key locations */ -ccl_device_inline void motion_curve_keys(KernelGlobals *kg, +ccl_device_inline void motion_curve_keys(KernelGlobals kg, int object, int prim, float time, @@ -153,13 +129,12 @@ ccl_device_inline void motion_curve_keys(KernelGlobals *kg, object_motion_info(kg, object, &numsteps, NULL, &numkeys); /* figure out which steps we need to fetch and their interpolation factor */ - int maxstep = numsteps * 2; - int step = min((int)(time * maxstep), maxstep - 1); - float t = time * maxstep - step; + const int maxstep = numsteps * 2; + const int step = min((int)(time * maxstep), maxstep - 1); + const float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_curve_motion(kg, object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + const int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch key coordinates */ diff --git a/intern/cycles/kernel/geom/geom_motion_triangle.h b/intern/cycles/kernel/geom/motion_triangle.h similarity index 79% rename from intern/cycles/kernel/geom/geom_motion_triangle.h rename to intern/cycles/kernel/geom/motion_triangle.h index 53d6b92dd7e..43f894938e0 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle.h +++ b/intern/cycles/kernel/geom/motion_triangle.h @@ -25,31 +25,15 @@ * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. */ +#pragma once + +#include "kernel/bvh/util.h" + CCL_NAMESPACE_BEGIN /* Time interpolation of vertex positions and normals */ -ccl_device_inline int find_attribute_motion(KernelGlobals *kg, - int object, - uint id, - AttributeElement *elem) -{ - /* todo: find a better (faster) solution for this, maybe store offset per object */ - uint attr_offset = object_attribute_map_offset(kg, object); - uint4 attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - - while (attr_map.x != id) { - attr_offset += ATTR_PRIM_TYPES; - attr_map = kernel_tex_fetch(__attributes_map, attr_offset); - } - - *elem = (AttributeElement)attr_map.y; - - /* return result */ - return (attr_map.y == ATTR_ELEMENT_NONE) ? (int)ATTR_STD_NOT_FOUND : (int)attr_map.z; -} - -ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals *kg, +ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals kg, uint4 tri_vindex, int offset, int numverts, @@ -59,9 +43,9 @@ ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals *kg, { if (step == numsteps) { /* center step: regular vertex location */ - verts[0] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - verts[1] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - verts[2] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + verts[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + verts[1] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + verts[2] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); } else { /* center step not store in this array */ @@ -76,7 +60,7 @@ ccl_device_inline void motion_triangle_verts_for_step(KernelGlobals *kg, } } -ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals *kg, +ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals kg, uint4 tri_vindex, int offset, int numverts, @@ -104,7 +88,7 @@ ccl_device_inline void motion_triangle_normals_for_step(KernelGlobals *kg, } ccl_device_inline void motion_triangle_vertices( - KernelGlobals *kg, int object, int prim, float time, float3 verts[3]) + KernelGlobals kg, int object, int prim, float time, float3 verts[3]) { /* get motion info */ int numsteps, numverts; @@ -116,8 +100,7 @@ ccl_device_inline void motion_triangle_vertices( float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_motion(kg, object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch vertex coordinates */ @@ -134,7 +117,7 @@ ccl_device_inline void motion_triangle_vertices( } ccl_device_inline float3 motion_triangle_smooth_normal( - KernelGlobals *kg, float3 Ng, int object, int prim, float u, float v, float time) + KernelGlobals kg, float3 Ng, int object, int prim, float u, float v, float time) { /* get motion info */ int numsteps, numverts; @@ -146,8 +129,7 @@ ccl_device_inline float3 motion_triangle_smooth_normal( float t = time * maxstep - step; /* find attribute */ - AttributeElement elem; - int offset = find_attribute_motion(kg, object, ATTR_STD_MOTION_VERTEX_NORMAL, &elem); + int offset = intersection_find_attribute(kg, object, ATTR_STD_MOTION_VERTEX_NORMAL); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* fetch normals */ diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h b/intern/cycles/kernel/geom/motion_triangle_intersect.h similarity index 77% rename from intern/cycles/kernel/geom/geom_motion_triangle_intersect.h rename to intern/cycles/kernel/geom/motion_triangle_intersect.h index 859d919f0bb..256e7add21e 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_intersect.h +++ b/intern/cycles/kernel/geom/motion_triangle_intersect.h @@ -25,6 +25,8 @@ * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Refine triangle intersection to more precise hit point. For rays that travel @@ -32,23 +34,21 @@ CCL_NAMESPACE_BEGIN * a closer distance. */ -ccl_device_inline float3 motion_triangle_refine( - KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, float3 verts[3]) +ccl_device_inline float3 motion_triangle_refine(KernelGlobals kg, + ccl_private ShaderData *sd, + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim, + float3 verts[3]) { - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - #ifdef __INTERSECTION_REFINE__ - if (isect->object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { if (UNLIKELY(t == 0.0f)) { return P; } -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D * t); @@ -70,13 +70,8 @@ ccl_device_inline float3 motion_triangle_refine( /* Compute refined position. */ P = P + D * rt; - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -86,7 +81,7 @@ ccl_device_inline float3 motion_triangle_refine( #endif } -/* Same as above, except that isect->t is assumed to be in object space +/* Same as above, except that t is assumed to be in object space * for instancing. */ @@ -97,27 +92,22 @@ ccl_device_noinline ccl_device_inline # endif float3 - motion_triangle_refine_local(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray, + motion_triangle_refine_local(KernelGlobals kg, + ccl_private ShaderData *sd, + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim, float3 verts[3]) { # ifdef __KERNEL_OPTIX__ - /* isect->t is always in world space with OptiX. */ - return motion_triangle_refine(kg, sd, isect, ray, verts); + /* t is always in world space with OptiX. */ + return motion_triangle_refine(kg, sd, P, D, t, isect_object, isect_prim, verts); # else - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - # ifdef __INTERSECTION_REFINE__ - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D); @@ -138,13 +128,8 @@ ccl_device_inline P = P + D * rt; - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -160,10 +145,11 @@ ccl_device_inline * time and do a ray intersection with the resulting triangle. */ -ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, - Intersection *isect, +ccl_device_inline bool motion_triangle_intersect(KernelGlobals kg, + ccl_private Intersection *isect, float3 P, float3 dir, + float tmax, float time, uint visibility, int object, @@ -179,7 +165,7 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, float t, u, v; if (ray_triangle_intersect(P, dir, - isect->t, + tmax, #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) (ssef *)verts, #else @@ -200,8 +186,9 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, isect->t = t; isect->u = u; isect->v = v; - isect->prim = prim_addr; - isect->object = object; + isect->prim = prim; + isect->object = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, prim_addr) : + object; isect->type = PRIMITIVE_MOTION_TRIANGLE; return true; } @@ -215,8 +202,8 @@ ccl_device_inline bool motion_triangle_intersect(KernelGlobals *kg, * Returns whether traversal should be stopped. */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals *kg, - LocalIntersection *local_isect, +ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals kg, + ccl_private LocalIntersection *local_isect, float3 P, float3 dir, float time, @@ -224,7 +211,7 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals *kg, int local_object, int prim_addr, float tmax, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { /* Only intersect with matching object, for instanced objects we @@ -298,12 +285,12 @@ ccl_device_inline bool motion_triangle_intersect_local(KernelGlobals *kg, } /* Record intersection. */ - Intersection *isect = &local_isect->hits[hit]; + ccl_private Intersection *isect = &local_isect->hits[hit]; isect->t = t; isect->u = u; isect->v = v; - isect->prim = prim_addr; - isect->object = object; + isect->prim = prim; + isect->object = local_object; isect->type = PRIMITIVE_MOTION_TRIANGLE; /* Record geometric normal. */ diff --git a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h b/intern/cycles/kernel/geom/motion_triangle_shader.h similarity index 79% rename from intern/cycles/kernel/geom/geom_motion_triangle_shader.h rename to intern/cycles/kernel/geom/motion_triangle_shader.h index 7a91f8041f7..fc7c181882e 100644 --- a/intern/cycles/kernel/geom/geom_motion_triangle_shader.h +++ b/intern/cycles/kernel/geom/motion_triangle_shader.h @@ -25,6 +25,8 @@ * and ATTR_STD_MOTION_VERTEX_NORMAL mesh attributes. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Setup of motion triangle specific parts of ShaderData, moved into this one @@ -32,8 +34,14 @@ CCL_NAMESPACE_BEGIN * normals */ /* return 3 triangle vertex normals */ -ccl_device_noinline void motion_triangle_shader_setup( - KernelGlobals *kg, ShaderData *sd, const Intersection *isect, const Ray *ray, bool is_local) +ccl_device_noinline void motion_triangle_shader_setup(KernelGlobals kg, + ccl_private ShaderData *sd, + const float3 P, + const float3 D, + const float ray_t, + const int isect_object, + const int isect_prim, + bool is_local) { /* Get shader. */ sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); @@ -48,8 +56,7 @@ ccl_device_noinline void motion_triangle_shader_setup( int step = min((int)(sd->time * maxstep), maxstep - 1); float t = sd->time * maxstep - step; /* Find attribute. */ - AttributeElement elem; - int offset = find_attribute_motion(kg, sd->object, ATTR_STD_MOTION_VERTEX_POSITION, &elem); + int offset = intersection_find_attribute(kg, sd->object, ATTR_STD_MOTION_VERTEX_POSITION); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* Fetch vertex coordinates. */ float3 verts[3], next_verts[3]; @@ -63,12 +70,12 @@ ccl_device_noinline void motion_triangle_shader_setup( /* Compute refined position. */ #ifdef __BVH_LOCAL__ if (is_local) { - sd->P = motion_triangle_refine_local(kg, sd, isect, ray, verts); + sd->P = motion_triangle_refine_local(kg, sd, P, D, ray_t, isect_object, isect_prim, verts); } else #endif /* __BVH_LOCAL__*/ { - sd->P = motion_triangle_refine(kg, sd, isect, ray, verts); + sd->P = motion_triangle_refine(kg, sd, P, D, ray_t, isect_object, isect_prim, verts); } /* Compute face normal. */ float3 Ng; @@ -88,8 +95,7 @@ ccl_device_noinline void motion_triangle_shader_setup( /* Compute smooth normal. */ if (sd->shader & SHADER_SMOOTH_NORMAL) { /* Find attribute. */ - AttributeElement elem; - int offset = find_attribute_motion(kg, sd->object, ATTR_STD_MOTION_VERTEX_NORMAL, &elem); + int offset = intersection_find_attribute(kg, sd->object, ATTR_STD_MOTION_VERTEX_NORMAL); kernel_assert(offset != ATTR_STD_NOT_FOUND); /* Fetch vertex coordinates. */ float3 normals[3], next_normals[3]; diff --git a/intern/cycles/kernel/geom/geom_object.h b/intern/cycles/kernel/geom/object.h similarity index 57% rename from intern/cycles/kernel/geom/geom_object.h rename to intern/cycles/kernel/geom/object.h index fe73335a335..34a9d639d9d 100644 --- a/intern/cycles/kernel/geom/geom_object.h +++ b/intern/cycles/kernel/geom/object.h @@ -22,6 +22,8 @@ * directly primitives in the BVH with world space locations applied, and the object * ID is looked up afterwards. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Object attributes, for now a fixed size and contents */ @@ -35,7 +37,7 @@ enum ObjectVectorTransform { OBJECT_PASS_MOTION_PRE = 0, OBJECT_PASS_MOTION_POST /* Object to world space transformation */ -ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform(KernelGlobals kg, int object, enum ObjectTransform type) { @@ -49,7 +51,7 @@ ccl_device_inline Transform object_fetch_transform(KernelGlobals *kg, /* Lamp to world space transformation */ -ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bool inverse) +ccl_device_inline Transform lamp_fetch_transform(KernelGlobals kg, int lamp, bool inverse) { if (inverse) { return kernel_tex_fetch(__lights, lamp).itfm; @@ -61,7 +63,7 @@ ccl_device_inline Transform lamp_fetch_transform(KernelGlobals *kg, int lamp, bo /* Object to world space transformation for motion vectors */ -ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals *kg, +ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals kg, int object, enum ObjectVectorTransform type) { @@ -72,12 +74,10 @@ ccl_device_inline Transform object_fetch_motion_pass_transform(KernelGlobals *kg /* Motion blurred object transformations */ #ifdef __OBJECT_MOTION__ -ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, - int object, - float time) +ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals kg, int object, float time) { const uint motion_offset = kernel_tex_fetch(__objects, object).motion_offset; - const ccl_global DecomposedTransform *motion = &kernel_tex_fetch(__object_motion, motion_offset); + ccl_global const DecomposedTransform *motion = &kernel_tex_fetch(__object_motion, motion_offset); const uint num_steps = kernel_tex_fetch(__objects, object).numsteps * 2 + 1; Transform tfm; @@ -86,10 +86,10 @@ ccl_device_inline Transform object_fetch_transform_motion(KernelGlobals *kg, return tfm; } -ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals *kg, +ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals kg, int object, float time, - Transform *itfm) + ccl_private Transform *itfm) { int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_MOTION) { @@ -111,45 +111,80 @@ ccl_device_inline Transform object_fetch_transform_motion_test(KernelGlobals *kg } #endif -/* Transform position from object to world space */ +/* Get transform matrix for shading point. */ -ccl_device_inline void object_position_transform(KernelGlobals *kg, - const ShaderData *sd, - float3 *P) +ccl_device_inline Transform object_get_transform(KernelGlobals kg, + ccl_private const ShaderData *sd) { #ifdef __OBJECT_MOTION__ - *P = transform_point_auto(&sd->ob_tfm, *P); + return (sd->object_flag & SD_OBJECT_MOTION) ? + sd->ob_tfm_motion : + object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); #else + return object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); +#endif +} + +ccl_device_inline Transform object_get_inverse_transform(KernelGlobals kg, + ccl_private const ShaderData *sd) +{ +#ifdef __OBJECT_MOTION__ + return (sd->object_flag & SD_OBJECT_MOTION) ? + sd->ob_itfm_motion : + object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); +#else + return object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); +#endif +} +/* Transform position from object to world space */ + +ccl_device_inline void object_position_transform(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private float3 *P) +{ +#ifdef __OBJECT_MOTION__ + if (sd->object_flag & SD_OBJECT_MOTION) { + *P = transform_point_auto(&sd->ob_tfm_motion, *P); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); *P = transform_point(&tfm, *P); -#endif } /* Transform position from world to object space */ -ccl_device_inline void object_inverse_position_transform(KernelGlobals *kg, - const ShaderData *sd, - float3 *P) +ccl_device_inline void object_inverse_position_transform(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private float3 *P) { #ifdef __OBJECT_MOTION__ - *P = transform_point_auto(&sd->ob_itfm, *P); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + *P = transform_point_auto(&sd->ob_itfm_motion, *P); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); *P = transform_point(&tfm, *P); -#endif } /* Transform normal from world to object space */ -ccl_device_inline void object_inverse_normal_transform(KernelGlobals *kg, - const ShaderData *sd, - float3 *N) +ccl_device_inline void object_inverse_normal_transform(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private float3 *N) { #ifdef __OBJECT_MOTION__ - if ((sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP)) { - *N = normalize(transform_direction_transposed_auto(&sd->ob_tfm, *N)); + if (sd->object_flag & SD_OBJECT_MOTION) { + if ((sd->object != OBJECT_NONE) || (sd->type == PRIMITIVE_LAMP)) { + *N = normalize(transform_direction_transposed_auto(&sd->ob_tfm_motion, *N)); + } + return; } -#else +#endif + if (sd->object != OBJECT_NONE) { Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); *N = normalize(transform_direction_transposed(&tfm, *N)); @@ -158,76 +193,90 @@ ccl_device_inline void object_inverse_normal_transform(KernelGlobals *kg, Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); *N = normalize(transform_direction_transposed(&tfm, *N)); } -#endif } /* Transform normal from object to world space */ -ccl_device_inline void object_normal_transform(KernelGlobals *kg, const ShaderData *sd, float3 *N) +ccl_device_inline void object_normal_transform(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private float3 *N) { #ifdef __OBJECT_MOTION__ - *N = normalize(transform_direction_transposed_auto(&sd->ob_itfm, *N)); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + *N = normalize(transform_direction_transposed_auto(&sd->ob_itfm_motion, *N)); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); *N = normalize(transform_direction_transposed(&tfm, *N)); -#endif } /* Transform direction vector from object to world space */ -ccl_device_inline void object_dir_transform(KernelGlobals *kg, const ShaderData *sd, float3 *D) +ccl_device_inline void object_dir_transform(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private float3 *D) { #ifdef __OBJECT_MOTION__ - *D = transform_direction_auto(&sd->ob_tfm, *D); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + *D = transform_direction_auto(&sd->ob_tfm_motion, *D); + return; + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); *D = transform_direction(&tfm, *D); -#endif } /* Transform direction vector from world to object space */ -ccl_device_inline void object_inverse_dir_transform(KernelGlobals *kg, - const ShaderData *sd, - float3 *D) +ccl_device_inline void object_inverse_dir_transform(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private float3 *D) { #ifdef __OBJECT_MOTION__ - *D = transform_direction_auto(&sd->ob_itfm, *D); -#else - Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); - *D = transform_direction(&tfm, *D); + if (sd->object_flag & SD_OBJECT_MOTION) { + *D = transform_direction_auto(&sd->ob_itfm_motion, *D); + return; + } #endif + + const Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); + *D = transform_direction(&tfm, *D); } /* Object center position */ -ccl_device_inline float3 object_location(KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline float3 object_location(KernelGlobals kg, ccl_private const ShaderData *sd) { if (sd->object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); #ifdef __OBJECT_MOTION__ - return make_float3(sd->ob_tfm.x.w, sd->ob_tfm.y.w, sd->ob_tfm.z.w); -#else + if (sd->object_flag & SD_OBJECT_MOTION) { + return make_float3(sd->ob_tfm_motion.x.w, sd->ob_tfm_motion.y.w, sd->ob_tfm_motion.z.w); + } +#endif + Transform tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); return make_float3(tfm.x.w, tfm.y.w, tfm.z.w); -#endif } /* Color of the object */ -ccl_device_inline float3 object_color(KernelGlobals *kg, int object) +ccl_device_inline float3 object_color(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + ccl_global const KernelObject *kobject = &kernel_tex_fetch(__objects, object); return make_float3(kobject->color[0], kobject->color[1], kobject->color[2]); } /* Pass ID number of object */ -ccl_device_inline float object_pass_id(KernelGlobals *kg, int object) +ccl_device_inline float object_pass_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -237,7 +286,7 @@ ccl_device_inline float object_pass_id(KernelGlobals *kg, int object) /* Per lamp random number for shader variation */ -ccl_device_inline float lamp_random_number(KernelGlobals *kg, int lamp) +ccl_device_inline float lamp_random_number(KernelGlobals kg, int lamp) { if (lamp == LAMP_NONE) return 0.0f; @@ -247,7 +296,7 @@ ccl_device_inline float lamp_random_number(KernelGlobals *kg, int lamp) /* Per object random number for shader variation */ -ccl_device_inline float object_random_number(KernelGlobals *kg, int object) +ccl_device_inline float object_random_number(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -257,7 +306,7 @@ ccl_device_inline float object_random_number(KernelGlobals *kg, int object) /* Particle ID from which this object was generated */ -ccl_device_inline int object_particle_id(KernelGlobals *kg, int object) +ccl_device_inline int object_particle_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0; @@ -267,31 +316,34 @@ ccl_device_inline int object_particle_id(KernelGlobals *kg, int object) /* Generated texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_generated(KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_generated(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + ccl_global const KernelObject *kobject = &kernel_tex_fetch(__objects, object); return make_float3( kobject->dupli_generated[0], kobject->dupli_generated[1], kobject->dupli_generated[2]); } /* UV texture coordinate on surface from where object was instanced */ -ccl_device_inline float3 object_dupli_uv(KernelGlobals *kg, int object) +ccl_device_inline float3 object_dupli_uv(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return make_float3(0.0f, 0.0f, 0.0f); - const ccl_global KernelObject *kobject = &kernel_tex_fetch(__objects, object); + ccl_global const KernelObject *kobject = &kernel_tex_fetch(__objects, object); return make_float3(kobject->dupli_uv[0], kobject->dupli_uv[1], 0.0f); } /* Information about mesh for motion blurred triangles and curves */ -ccl_device_inline void object_motion_info( - KernelGlobals *kg, int object, int *numsteps, int *numverts, int *numkeys) +ccl_device_inline void object_motion_info(KernelGlobals kg, + int object, + ccl_private int *numsteps, + ccl_private int *numverts, + ccl_private int *numkeys) { if (numkeys) { *numkeys = kernel_tex_fetch(__objects, object).numkeys; @@ -305,7 +357,7 @@ ccl_device_inline void object_motion_info( /* Offset to an objects patch map */ -ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object) +ccl_device_inline uint object_patch_map_offset(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0; @@ -315,7 +367,7 @@ ccl_device_inline uint object_patch_map_offset(KernelGlobals *kg, int object) /* Volume step size */ -ccl_device_inline float object_volume_density(KernelGlobals *kg, int object) +ccl_device_inline float object_volume_density(KernelGlobals kg, int object) { if (object == OBJECT_NONE) { return 1.0f; @@ -324,7 +376,7 @@ ccl_device_inline float object_volume_density(KernelGlobals *kg, int object) return kernel_tex_fetch(__objects, object).volume_density; } -ccl_device_inline float object_volume_step_size(KernelGlobals *kg, int object) +ccl_device_inline float object_volume_step_size(KernelGlobals kg, int object) { if (object == OBJECT_NONE) { return kernel_data.background.volume_step_size; @@ -335,14 +387,14 @@ ccl_device_inline float object_volume_step_size(KernelGlobals *kg, int object) /* Pass ID for shader */ -ccl_device int shader_pass_id(KernelGlobals *kg, const ShaderData *sd) +ccl_device int shader_pass_id(KernelGlobals kg, ccl_private const ShaderData *sd) { return kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).pass_id; } /* Cryptomatte ID */ -ccl_device_inline float object_cryptomatte_id(KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0.0f; @@ -350,7 +402,7 @@ ccl_device_inline float object_cryptomatte_id(KernelGlobals *kg, int object) return kernel_tex_fetch(__objects, object).cryptomatte_object; } -ccl_device_inline float object_cryptomatte_asset_id(KernelGlobals *kg, int object) +ccl_device_inline float object_cryptomatte_asset_id(KernelGlobals kg, int object) { if (object == OBJECT_NONE) return 0; @@ -360,42 +412,42 @@ ccl_device_inline float object_cryptomatte_asset_id(KernelGlobals *kg, int objec /* Particle data from which object was instanced */ -ccl_device_inline uint particle_index(KernelGlobals *kg, int particle) +ccl_device_inline uint particle_index(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).index; } -ccl_device float particle_age(KernelGlobals *kg, int particle) +ccl_device float particle_age(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).age; } -ccl_device float particle_lifetime(KernelGlobals *kg, int particle) +ccl_device float particle_lifetime(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).lifetime; } -ccl_device float particle_size(KernelGlobals *kg, int particle) +ccl_device float particle_size(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).size; } -ccl_device float4 particle_rotation(KernelGlobals *kg, int particle) +ccl_device float4 particle_rotation(KernelGlobals kg, int particle) { return kernel_tex_fetch(__particles, particle).rotation; } -ccl_device float3 particle_location(KernelGlobals *kg, int particle) +ccl_device float3 particle_location(KernelGlobals kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).location); } -ccl_device float3 particle_velocity(KernelGlobals *kg, int particle) +ccl_device float3 particle_velocity(KernelGlobals kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).velocity); } -ccl_device float3 particle_angular_velocity(KernelGlobals *kg, int particle) +ccl_device float3 particle_angular_velocity(KernelGlobals kg, int particle) { return float4_to_float3(kernel_tex_fetch(__particles, particle).angular_velocity); } @@ -417,8 +469,12 @@ ccl_device_inline float3 bvh_inverse_direction(float3 dir) /* Transform ray into object space to enter static object in BVH */ -ccl_device_inline float bvh_instance_push( - KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, float t) +ccl_device_inline float bvh_instance_push(KernelGlobals kg, + int object, + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir) { Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); @@ -428,17 +484,18 @@ ccl_device_inline float bvh_instance_push( *dir = bvh_clamp_direction(normalize_len(transform_direction(&tfm, ray->D), &len)); *idir = bvh_inverse_direction(*dir); - if (t != FLT_MAX) { - t *= len; - } - - return t; + return len; } /* Transform ray to exit static object in BVH. */ -ccl_device_inline float bvh_instance_pop( - KernelGlobals *kg, int object, const Ray *ray, float3 *P, float3 *dir, float3 *idir, float t) +ccl_device_inline float bvh_instance_pop(KernelGlobals kg, + int object, + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, + float t) { if (t != FLT_MAX) { Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); @@ -454,13 +511,13 @@ ccl_device_inline float bvh_instance_pop( /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_pop_factor(KernelGlobals *kg, +ccl_device_inline void bvh_instance_pop_factor(KernelGlobals kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, - float *t_fac) + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, + ccl_private float *t_fac) { Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); *t_fac = 1.0f / len(transform_direction(&tfm, ray->D)); @@ -473,14 +530,13 @@ ccl_device_inline void bvh_instance_pop_factor(KernelGlobals *kg, #ifdef __OBJECT_MOTION__ /* Transform ray into object space to enter motion blurred object in BVH */ -ccl_device_inline float bvh_instance_motion_push(KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_push(KernelGlobals kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, - float t, - Transform *itfm) + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, + ccl_private Transform *itfm) { object_fetch_transform_motion_test(kg, object, ray->time, itfm); @@ -490,23 +546,19 @@ ccl_device_inline float bvh_instance_motion_push(KernelGlobals *kg, *dir = bvh_clamp_direction(normalize_len(transform_direction(itfm, ray->D), &len)); *idir = bvh_inverse_direction(*dir); - if (t != FLT_MAX) { - t *= len; - } - - return t; + return len; } /* Transform ray to exit motion blurred object in BVH. */ -ccl_device_inline float bvh_instance_motion_pop(KernelGlobals *kg, +ccl_device_inline float bvh_instance_motion_pop(KernelGlobals kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, float t, - Transform *itfm) + ccl_private Transform *itfm) { if (t != FLT_MAX) { t /= len(transform_direction(itfm, ray->D)); @@ -521,14 +573,14 @@ ccl_device_inline float bvh_instance_motion_pop(KernelGlobals *kg, /* Same as above, but returns scale factor to apply to multiple intersection distances */ -ccl_device_inline void bvh_instance_motion_pop_factor(KernelGlobals *kg, +ccl_device_inline void bvh_instance_motion_pop_factor(KernelGlobals kg, int object, - const Ray *ray, - float3 *P, - float3 *dir, - float3 *idir, - float *t_fac, - Transform *itfm) + ccl_private const Ray *ray, + ccl_private float3 *P, + ccl_private float3 *dir, + ccl_private float3 *idir, + ccl_private float *t_fac, + ccl_private Transform *itfm) { *t_fac = 1.0f / len(transform_direction(itfm, ray->D)); *P = ray->P; @@ -538,48 +590,11 @@ ccl_device_inline void bvh_instance_motion_pop_factor(KernelGlobals *kg, #endif -/* TODO(sergey): This is only for until we've got OpenCL 2.0 - * on all devices we consider supported. It'll be replaced with - * generic address space. - */ +/* TODO: This can be removed when we know if no devices will require explicit + * address space qualifiers for this case. */ -#ifdef __KERNEL_OPENCL__ -ccl_device_inline void object_position_transform_addrspace(KernelGlobals *kg, - const ShaderData *sd, - ccl_addr_space float3 *P) -{ - float3 private_P = *P; - object_position_transform(kg, sd, &private_P); - *P = private_P; -} - -ccl_device_inline void object_dir_transform_addrspace(KernelGlobals *kg, - const ShaderData *sd, - ccl_addr_space float3 *D) -{ - float3 private_D = *D; - object_dir_transform(kg, sd, &private_D); - *D = private_D; -} - -ccl_device_inline void object_normal_transform_addrspace(KernelGlobals *kg, - const ShaderData *sd, - ccl_addr_space float3 *N) -{ - float3 private_N = *N; - object_normal_transform(kg, sd, &private_N); - *N = private_N; -} -#endif - -#ifndef __KERNEL_OPENCL__ -# define object_position_transform_auto object_position_transform -# define object_dir_transform_auto object_dir_transform -# define object_normal_transform_auto object_normal_transform -#else -# define object_position_transform_auto object_position_transform_addrspace -# define object_dir_transform_auto object_dir_transform_addrspace -# define object_normal_transform_auto object_normal_transform_addrspace -#endif +#define object_position_transform_auto object_position_transform +#define object_dir_transform_auto object_dir_transform +#define object_normal_transform_auto object_normal_transform CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom_patch.h b/intern/cycles/kernel/geom/patch.h similarity index 83% rename from intern/cycles/kernel/geom/geom_patch.h rename to intern/cycles/kernel/geom/patch.h index 9c1768f05db..7d24937a41e 100644 --- a/intern/cycles/kernel/geom/geom_patch.h +++ b/intern/cycles/kernel/geom/patch.h @@ -24,13 +24,19 @@ * language governing permissions and limitations under the Apache License. */ +#pragma once + +#include "util/color.h" + CCL_NAMESPACE_BEGIN typedef struct PatchHandle { int array_index, patch_index, vert_index; } PatchHandle; -ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float *v) +ccl_device_inline int patch_map_resolve_quadrant(float median, + ccl_private float *u, + ccl_private float *v) { int quadrant = -1; @@ -60,7 +66,7 @@ ccl_device_inline int patch_map_resolve_quadrant(float median, float *u, float * /* retrieve PatchHandle from patch coords */ ccl_device_inline PatchHandle -patch_map_find_patch(KernelGlobals *kg, int object, int patch, float u, float v) +patch_map_find_patch(KernelGlobals kg, int object, int patch, float u, float v) { PatchHandle handle; @@ -106,7 +112,9 @@ patch_map_find_patch(KernelGlobals *kg, int object, int patch, float u, float v) return handle; } -ccl_device_inline void patch_eval_bspline_weights(float t, float *point, float *deriv) +ccl_device_inline void patch_eval_bspline_weights(float t, + ccl_private float *point, + ccl_private float *deriv) { /* The four uniform cubic B-Spline basis functions evaluated at t */ float inv_6 = 1.0f / 6.0f; @@ -126,7 +134,9 @@ ccl_device_inline void patch_eval_bspline_weights(float t, float *point, float * deriv[3] = 0.5f * t2; } -ccl_device_inline void patch_eval_adjust_boundary_weights(uint bits, float *s, float *t) +ccl_device_inline void patch_eval_adjust_boundary_weights(uint bits, + ccl_private float *s, + ccl_private float *t) { int boundary = ((bits >> 8) & 0xf); @@ -173,7 +183,9 @@ ccl_device_inline float patch_eval_param_fraction(uint patch_bits) } } -ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, float *v) +ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, + ccl_private float *u, + ccl_private float *v) { float frac = patch_eval_param_fraction(patch_bits); @@ -191,8 +203,8 @@ ccl_device_inline void patch_eval_normalize_coords(uint patch_bits, float *u, fl /* retrieve patch control indices */ -ccl_device_inline int patch_eval_indices(KernelGlobals *kg, - const PatchHandle *handle, +ccl_device_inline int patch_eval_indices(KernelGlobals kg, + ccl_private const PatchHandle *handle, int channel, int indices[PATCH_MAX_CONTROL_VERTS]) { @@ -208,8 +220,8 @@ ccl_device_inline int patch_eval_indices(KernelGlobals *kg, /* evaluate patch basis functions */ -ccl_device_inline void patch_eval_basis(KernelGlobals *kg, - const PatchHandle *handle, +ccl_device_inline void patch_eval_basis(KernelGlobals kg, + ccl_private const PatchHandle *handle, float u, float v, float weights[PATCH_MAX_CONTROL_VERTS], @@ -247,7 +259,7 @@ ccl_device_inline void patch_eval_basis(KernelGlobals *kg, /* generic function for evaluating indices and weights from patch coords */ -ccl_device_inline int patch_eval_control_verts(KernelGlobals *kg, +ccl_device_inline int patch_eval_control_verts(KernelGlobals kg, int object, int patch, float u, @@ -269,15 +281,15 @@ ccl_device_inline int patch_eval_control_verts(KernelGlobals *kg, /* functions for evaluating attributes on patches */ -ccl_device float patch_eval_float(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float patch_eval_float(KernelGlobals kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float *du, - float *dv) + ccl_private float *du, + ccl_private float *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -306,15 +318,15 @@ ccl_device float patch_eval_float(KernelGlobals *kg, return val; } -ccl_device float2 patch_eval_float2(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float2 patch_eval_float2(KernelGlobals kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float2 *du, - float2 *dv) + ccl_private float2 *du, + ccl_private float2 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -343,15 +355,15 @@ ccl_device float2 patch_eval_float2(KernelGlobals *kg, return val; } -ccl_device float3 patch_eval_float3(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float3 patch_eval_float3(KernelGlobals kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float3 *du, - float3 *dv) + ccl_private float3 *du, + ccl_private float3 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -380,15 +392,15 @@ ccl_device float3 patch_eval_float3(KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_float4(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 patch_eval_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float4 *du, - float4 *dv) + ccl_private float4 *du, + ccl_private float4 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; @@ -417,15 +429,15 @@ ccl_device float4 patch_eval_float4(KernelGlobals *kg, return val; } -ccl_device float4 patch_eval_uchar4(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 patch_eval_uchar4(KernelGlobals kg, + ccl_private const ShaderData *sd, int offset, int patch, float u, float v, int channel, - float4 *du, - float4 *dv) + ccl_private float4 *du, + ccl_private float4 *dv) { int indices[PATCH_MAX_CONTROL_VERTS]; float weights[PATCH_MAX_CONTROL_VERTS]; diff --git a/intern/cycles/kernel/geom/geom_primitive.h b/intern/cycles/kernel/geom/primitive.h similarity index 80% rename from intern/cycles/kernel/geom/geom_primitive.h rename to intern/cycles/kernel/geom/primitive.h index aeb044c9ad3..7a8921b6d6e 100644 --- a/intern/cycles/kernel/geom/geom_primitive.h +++ b/intern/cycles/kernel/geom/primitive.h @@ -19,6 +19,10 @@ * Generic functions to look up mesh, curve and volume primitive attributes for * shading and render passes. */ +#pragma once + +#include "kernel/camera/projection.h" + CCL_NAMESPACE_BEGIN /* Surface Attributes @@ -27,8 +31,11 @@ CCL_NAMESPACE_BEGIN * attributes for performance, mainly for GPU performance to avoid bringing in * heavy volume interpolation code. */ -ccl_device_inline float primitive_surface_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device_inline float primitive_surface_attribute_float(KernelGlobals kg, + ccl_private const ShaderData *sd, + const AttributeDescriptor desc, + ccl_private float *dx, + ccl_private float *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -50,11 +57,11 @@ ccl_device_inline float primitive_surface_attribute_float( } } -ccl_device_inline float2 primitive_surface_attribute_float2(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float2 primitive_surface_attribute_float2(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -76,11 +83,11 @@ ccl_device_inline float2 primitive_surface_attribute_float2(KernelGlobals *kg, } } -ccl_device_inline float3 primitive_surface_attribute_float3(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float3 primitive_surface_attribute_float3(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -102,11 +109,11 @@ ccl_device_inline float3 primitive_surface_attribute_float3(KernelGlobals *kg, } } -ccl_device_inline float4 primitive_surface_attribute_float4(KernelGlobals *kg, - const ShaderData *sd, - const AttributeDescriptor desc, - float4 *dx, - float4 *dy) +ccl_device_forceinline float4 primitive_surface_attribute_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, + const AttributeDescriptor desc, + ccl_private float4 *dx, + ccl_private float4 *dy) { if (sd->type & PRIMITIVE_ALL_TRIANGLE) { if (subd_triangle_patch(kg, sd) == ~0) @@ -135,14 +142,14 @@ ccl_device_inline float4 primitive_surface_attribute_float4(KernelGlobals *kg, * attributes for performance, mainly for GPU performance to avoid bringing in * heavy volume interpolation code. */ -ccl_device_inline bool primitive_is_volume_attribute(const ShaderData *sd, +ccl_device_inline bool primitive_is_volume_attribute(ccl_private const ShaderData *sd, const AttributeDescriptor desc) { return sd->type == PRIMITIVE_VOLUME; } -ccl_device_inline float primitive_volume_attribute_float(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float primitive_volume_attribute_float(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (primitive_is_volume_attribute(sd, desc)) { @@ -153,8 +160,8 @@ ccl_device_inline float primitive_volume_attribute_float(KernelGlobals *kg, } } -ccl_device_inline float3 primitive_volume_attribute_float3(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float3 primitive_volume_attribute_float3(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (primitive_is_volume_attribute(sd, desc)) { @@ -165,8 +172,8 @@ ccl_device_inline float3 primitive_volume_attribute_float3(KernelGlobals *kg, } } -ccl_device_inline float4 primitive_volume_attribute_float4(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float4 primitive_volume_attribute_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (primitive_is_volume_attribute(sd, desc)) { @@ -180,7 +187,7 @@ ccl_device_inline float4 primitive_volume_attribute_float4(KernelGlobals *kg, /* Default UV coordinate */ -ccl_device_inline float3 primitive_uv(KernelGlobals *kg, ShaderData *sd) +ccl_device_inline float3 primitive_uv(KernelGlobals kg, ccl_private const ShaderData *sd) { const AttributeDescriptor desc = find_attribute(kg, sd, ATTR_STD_UV); @@ -193,7 +200,10 @@ ccl_device_inline float3 primitive_uv(KernelGlobals *kg, ShaderData *sd) /* Ptex coordinates */ -ccl_device bool primitive_ptex(KernelGlobals *kg, ShaderData *sd, float2 *uv, int *face_id) +ccl_device bool primitive_ptex(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float2 *uv, + ccl_private int *face_id) { /* storing ptex data as attributes is not memory efficient but simple for tests */ const AttributeDescriptor desc_face_id = find_attribute(kg, sd, ATTR_STD_PTEX_FACE_ID); @@ -213,7 +223,7 @@ ccl_device bool primitive_ptex(KernelGlobals *kg, ShaderData *sd, float2 *uv, in /* Surface tangent */ -ccl_device float3 primitive_tangent(KernelGlobals *kg, ShaderData *sd) +ccl_device float3 primitive_tangent(KernelGlobals kg, ccl_private ShaderData *sd) { #ifdef __HAIR__ if (sd->type & PRIMITIVE_ALL_CURVE) @@ -245,7 +255,8 @@ ccl_device float3 primitive_tangent(KernelGlobals *kg, ShaderData *sd) /* Motion vector for motion pass */ -ccl_device_inline float4 primitive_motion_vector(KernelGlobals *kg, ShaderData *sd) +ccl_device_inline float4 primitive_motion_vector(KernelGlobals kg, + ccl_private const ShaderData *sd) { /* center position */ float3 center; diff --git a/intern/cycles/kernel/geom/shader_data.h b/intern/cycles/kernel/geom/shader_data.h new file mode 100644 index 00000000000..46bda2b656c --- /dev/null +++ b/intern/cycles/kernel/geom/shader_data.h @@ -0,0 +1,447 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Functions to initialize ShaderData given. + * + * Could be from an incoming ray, intersection or sampled position. */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* ShaderData setup from incoming ray */ + +#ifdef __OBJECT_MOTION__ +ccl_device void shader_setup_object_transforms(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + float time) +{ + if (sd->object_flag & SD_OBJECT_MOTION) { + sd->ob_tfm_motion = object_fetch_transform_motion(kg, sd->object, time); + sd->ob_itfm_motion = transform_quick_inverse(sd->ob_tfm_motion); + } +} +#endif + +/* TODO: break this up if it helps reduce register pressure to load data from + * global memory as we write it to shader-data. */ +ccl_device_inline void shader_setup_from_ray(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + ccl_private const Ray *ccl_restrict ray, + ccl_private const Intersection *ccl_restrict isect) +{ + /* Read intersection data into shader globals. + * + * TODO: this is redundant, could potentially remove some of this from + * ShaderData but would need to ensure that it also works for shadow + * shader evaluation. */ + sd->u = isect->u; + sd->v = isect->v; + sd->ray_length = isect->t; + sd->type = isect->type; + sd->object = isect->object; + sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); + sd->prim = isect->prim; + sd->lamp = LAMP_NONE; + sd->flag = 0; + + /* Read matrices and time. */ + sd->time = ray->time; + +#ifdef __OBJECT_MOTION__ + shader_setup_object_transforms(kg, sd, ray->time); +#endif + + /* Read ray data into shader globals. */ + sd->I = -ray->D; + +#ifdef __HAIR__ + if (sd->type & PRIMITIVE_ALL_CURVE) { + /* curve */ + curve_shader_setup(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim); + } + else +#endif + if (sd->type & PRIMITIVE_TRIANGLE) { + /* static triangle */ + float3 Ng = triangle_normal(kg, sd); + sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); + + /* vectors */ + sd->P = triangle_refine(kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim); + sd->Ng = Ng; + sd->N = Ng; + + /* smooth normal */ + if (sd->shader & SHADER_SMOOTH_NORMAL) + sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); + +#ifdef __DPDU__ + /* dPdu/dPdv */ + triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); +#endif + } + else { + /* motion triangle */ + motion_triangle_shader_setup( + kg, sd, ray->P, ray->D, isect->t, isect->object, isect->prim, false); + } + + sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + /* instance transform */ + object_normal_transform_auto(kg, sd, &sd->N); + object_normal_transform_auto(kg, sd, &sd->Ng); +#ifdef __DPDU__ + object_dir_transform_auto(kg, sd, &sd->dPdu); + object_dir_transform_auto(kg, sd, &sd->dPdv); +#endif + } + + /* backfacing test */ + bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); + + if (backfacing) { + sd->flag |= SD_BACKFACING; + sd->Ng = -sd->Ng; + sd->N = -sd->N; +#ifdef __DPDU__ + sd->dPdu = -sd->dPdu; + sd->dPdv = -sd->dPdv; +#endif + } + +#ifdef __RAY_DIFFERENTIALS__ + /* differentials */ + differential_transfer_compact(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, sd->ray_length); + differential_incoming_compact(&sd->dI, ray->D, ray->dD); + differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); +#endif +} + +/* ShaderData setup from position sampled on mesh */ + +ccl_device_inline void shader_setup_from_sample(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + const float3 P, + const float3 Ng, + const float3 I, + int shader, + int object, + int prim, + float u, + float v, + float t, + float time, + bool object_space, + int lamp) +{ + /* vectors */ + sd->P = P; + sd->N = Ng; + sd->Ng = Ng; + sd->I = I; + sd->shader = shader; + if (prim != PRIM_NONE) + sd->type = PRIMITIVE_TRIANGLE; + else if (lamp != LAMP_NONE) + sd->type = PRIMITIVE_LAMP; + else + sd->type = PRIMITIVE_NONE; + + /* primitive */ + sd->object = object; + sd->lamp = LAMP_NONE; + /* Currently no access to bvh prim index for strand sd->prim. */ + sd->prim = prim; + sd->u = u; + sd->v = v; + sd->time = time; + sd->ray_length = t; + + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + sd->object_flag = 0; + if (sd->object != OBJECT_NONE) { + sd->object_flag |= kernel_tex_fetch(__object_flag, sd->object); + +#ifdef __OBJECT_MOTION__ + shader_setup_object_transforms(kg, sd, time); +#endif + } + else if (lamp != LAMP_NONE) { + sd->lamp = lamp; + } + + /* transform into world space */ + if (object_space) { + object_position_transform_auto(kg, sd, &sd->P); + object_normal_transform_auto(kg, sd, &sd->Ng); + sd->N = sd->Ng; + object_dir_transform_auto(kg, sd, &sd->I); + } + + if (sd->type & PRIMITIVE_TRIANGLE) { + /* smooth normal */ + if (sd->shader & SHADER_SMOOTH_NORMAL) { + sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); + + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_normal_transform_auto(kg, sd, &sd->N); + } + } + + /* dPdu/dPdv */ +#ifdef __DPDU__ + triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); + + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_dir_transform_auto(kg, sd, &sd->dPdu); + object_dir_transform_auto(kg, sd, &sd->dPdv); + } +#endif + } + else { +#ifdef __DPDU__ + sd->dPdu = zero_float3(); + sd->dPdv = zero_float3(); +#endif + } + + /* backfacing test */ + if (sd->prim != PRIM_NONE) { + bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); + + if (backfacing) { + sd->flag |= SD_BACKFACING; + sd->Ng = -sd->Ng; + sd->N = -sd->N; +#ifdef __DPDU__ + sd->dPdu = -sd->dPdu; + sd->dPdv = -sd->dPdv; +#endif + } + } + +#ifdef __RAY_DIFFERENTIALS__ + /* no ray differentials here yet */ + sd->dP = differential3_zero(); + sd->dI = differential3_zero(); + sd->du = differential_zero(); + sd->dv = differential_zero(); +#endif +} + +/* ShaderData setup for displacement */ + +ccl_device void shader_setup_from_displace(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + int object, + int prim, + float u, + float v) +{ + float3 P, Ng, I = zero_float3(); + int shader; + + triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); + + /* force smooth shading for displacement */ + shader |= SHADER_SMOOTH_NORMAL; + + shader_setup_from_sample( + kg, + sd, + P, + Ng, + I, + shader, + object, + prim, + u, + v, + 0.0f, + 0.5f, + !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), + LAMP_NONE); +} + +/* ShaderData setup for point on curve. */ + +ccl_device void shader_setup_from_curve(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + int object, + int prim, + int segment, + float u) +{ + /* Primitive */ + sd->type = PRIMITIVE_PACK_SEGMENT(PRIMITIVE_CURVE_THICK, segment); + sd->lamp = LAMP_NONE; + sd->prim = prim; + sd->u = u; + sd->v = 0.0f; + sd->time = 0.5f; + sd->ray_length = 0.0f; + + /* Shader */ + sd->shader = kernel_tex_fetch(__curves, prim).shader_id; + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + + /* Object */ + sd->object = object; + sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); +#ifdef __OBJECT_MOTION__ + shader_setup_object_transforms(kg, sd, sd->time); +#endif + + /* Get control points. */ + KernelCurve kcurve = kernel_tex_fetch(__curves, prim); + + int k0 = kcurve.first_key + PRIMITIVE_UNPACK_SEGMENT(sd->type); + int k1 = k0 + 1; + int ka = max(k0 - 1, kcurve.first_key); + int kb = min(k1 + 1, kcurve.first_key + kcurve.num_keys - 1); + + float4 P_curve[4]; + + P_curve[0] = kernel_tex_fetch(__curve_keys, ka); + P_curve[1] = kernel_tex_fetch(__curve_keys, k0); + P_curve[2] = kernel_tex_fetch(__curve_keys, k1); + P_curve[3] = kernel_tex_fetch(__curve_keys, kb); + + /* Interpolate position and tangent. */ + sd->P = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u)); +#ifdef __DPDU__ + sd->dPdu = float4_to_float3(catmull_rom_basis_derivative(P_curve, sd->u)); +#endif + + /* Transform into world space */ + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + object_position_transform_auto(kg, sd, &sd->P); +#ifdef __DPDU__ + object_dir_transform_auto(kg, sd, &sd->dPdu); +#endif + } + + /* No view direction, normals or bitangent. */ + sd->I = zero_float3(); + sd->N = zero_float3(); + sd->Ng = zero_float3(); +#ifdef __DPDU__ + sd->dPdv = zero_float3(); +#endif + + /* No ray differentials currently. */ +#ifdef __RAY_DIFFERENTIALS__ + sd->dP = differential3_zero(); + sd->dI = differential3_zero(); + sd->du = differential_zero(); + sd->dv = differential_zero(); +#endif +} + +/* ShaderData setup from ray into background */ + +ccl_device_inline void shader_setup_from_background(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + const float3 ray_P, + const float3 ray_D, + const float ray_time) +{ + /* for NDC coordinates */ + sd->ray_P = ray_P; + + /* vectors */ + sd->P = ray_D; + sd->N = -ray_D; + sd->Ng = -ray_D; + sd->I = -ray_D; + sd->shader = kernel_data.background.surface_shader; + sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + sd->object_flag = 0; + sd->time = ray_time; + sd->ray_length = 0.0f; + + sd->object = OBJECT_NONE; + sd->lamp = LAMP_NONE; + sd->prim = PRIM_NONE; + sd->u = 0.0f; + sd->v = 0.0f; + +#ifdef __DPDU__ + /* dPdu/dPdv */ + sd->dPdu = zero_float3(); + sd->dPdv = zero_float3(); +#endif + +#ifdef __RAY_DIFFERENTIALS__ + /* differentials */ + sd->dP = differential3_zero(); /* TODO: ray->dP */ + differential_incoming(&sd->dI, sd->dP); + sd->du = differential_zero(); + sd->dv = differential_zero(); +#endif +} + +/* ShaderData setup from point inside volume */ + +#ifdef __VOLUME__ +ccl_device_inline void shader_setup_from_volume(KernelGlobals kg, + ccl_private ShaderData *ccl_restrict sd, + ccl_private const Ray *ccl_restrict ray) +{ + + /* vectors */ + sd->P = ray->P; + sd->N = -ray->D; + sd->Ng = -ray->D; + sd->I = -ray->D; + sd->shader = SHADER_NONE; + sd->flag = 0; + sd->object_flag = 0; + sd->time = ray->time; + sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */ + + sd->object = OBJECT_NONE; /* todo: fill this for texture coordinates */ + sd->lamp = LAMP_NONE; + sd->prim = PRIM_NONE; + sd->type = PRIMITIVE_VOLUME; + + sd->u = 0.0f; + sd->v = 0.0f; + +# ifdef __DPDU__ + /* dPdu/dPdv */ + sd->dPdu = zero_float3(); + sd->dPdv = zero_float3(); +# endif + +# ifdef __RAY_DIFFERENTIALS__ + /* differentials */ + sd->dP = differential3_zero(); /* TODO ray->dD */ + differential_incoming(&sd->dI, sd->dP); + sd->du = differential_zero(); + sd->dv = differential_zero(); +# endif + + /* for NDC coordinates */ + sd->ray_P = ray->P; + sd->ray_dP = ray->dP; +} +#endif /* __VOLUME__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/geom/geom_subd_triangle.h b/intern/cycles/kernel/geom/subd_triangle.h similarity index 92% rename from intern/cycles/kernel/geom/geom_subd_triangle.h rename to intern/cycles/kernel/geom/subd_triangle.h index 9eceb996926..8a9a3f71231 100644 --- a/intern/cycles/kernel/geom/geom_subd_triangle.h +++ b/intern/cycles/kernel/geom/subd_triangle.h @@ -16,19 +16,21 @@ /* Functions for retrieving attributes on triangles produced from subdivision meshes */ +#pragma once + CCL_NAMESPACE_BEGIN /* Patch index for triangle, -1 if not subdivision triangle */ -ccl_device_inline uint subd_triangle_patch(KernelGlobals *kg, const ShaderData *sd) +ccl_device_inline uint subd_triangle_patch(KernelGlobals kg, ccl_private const ShaderData *sd) { return (sd->prim != PRIM_NONE) ? kernel_tex_fetch(__tri_patch, sd->prim) : ~0; } /* UV coords of triangle within patch */ -ccl_device_inline void subd_triangle_patch_uv(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline void subd_triangle_patch_uv(KernelGlobals kg, + ccl_private const ShaderData *sd, float2 uv[3]) { uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim); @@ -40,7 +42,7 @@ ccl_device_inline void subd_triangle_patch_uv(KernelGlobals *kg, /* Vertex indices of patch */ -ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals *kg, int patch) +ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals kg, int patch) { uint4 indices; @@ -54,21 +56,21 @@ ccl_device_inline uint4 subd_triangle_patch_indices(KernelGlobals *kg, int patch /* Originating face for patch */ -ccl_device_inline uint subd_triangle_patch_face(KernelGlobals *kg, int patch) +ccl_device_inline uint subd_triangle_patch_face(KernelGlobals kg, int patch) { return kernel_tex_fetch(__patches, patch + 4); } /* Number of corners on originating face */ -ccl_device_inline uint subd_triangle_patch_num_corners(KernelGlobals *kg, int patch) +ccl_device_inline uint subd_triangle_patch_num_corners(KernelGlobals kg, int patch) { return kernel_tex_fetch(__patches, patch + 5) & 0xffff; } /* Indices of the four corners that are used by the patch */ -ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch, int corners[4]) +ccl_device_inline void subd_triangle_patch_corners(KernelGlobals kg, int patch, int corners[4]) { uint4 data; @@ -99,8 +101,11 @@ ccl_device_inline void subd_triangle_patch_corners(KernelGlobals *kg, int patch, /* Reading attributes on various subdivision triangle elements */ -ccl_device_noinline float subd_triangle_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device_noinline float subd_triangle_attribute_float(KernelGlobals kg, + ccl_private const ShaderData *sd, + const AttributeDescriptor desc, + ccl_private float *dx, + ccl_private float *dy) { int patch = subd_triangle_patch(kg, sd); @@ -235,11 +240,11 @@ ccl_device_noinline float subd_triangle_attribute_float( } } -ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { int patch = subd_triangle_patch(kg, sd); @@ -378,11 +383,11 @@ ccl_device_noinline float2 subd_triangle_attribute_float2(KernelGlobals *kg, } } -ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { int patch = subd_triangle_patch(kg, sd); @@ -520,11 +525,11 @@ ccl_device_noinline float3 subd_triangle_attribute_float3(KernelGlobals *kg, } } -ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_noinline float4 subd_triangle_attribute_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float4 *dx, - float4 *dy) + ccl_private float4 *dx, + ccl_private float4 *dy) { int patch = subd_triangle_patch(kg, sd); diff --git a/intern/cycles/kernel/geom/geom_triangle.h b/intern/cycles/kernel/geom/triangle.h similarity index 76% rename from intern/cycles/kernel/geom/geom_triangle.h rename to intern/cycles/kernel/geom/triangle.h index ff7909ca425..233e901c7ca 100644 --- a/intern/cycles/kernel/geom/geom_triangle.h +++ b/intern/cycles/kernel/geom/triangle.h @@ -20,16 +20,18 @@ * ray intersection we use a precomputed triangle storage to accelerate * intersection at the cost of more memory usage */ +#pragma once + CCL_NAMESPACE_BEGIN /* Normal on triangle. */ -ccl_device_inline float3 triangle_normal(KernelGlobals *kg, ShaderData *sd) +ccl_device_inline float3 triangle_normal(KernelGlobals kg, ccl_private ShaderData *sd) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, sd->prim); - const float3 v0 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - const float3 v1 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - const float3 v2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + const float3 v0 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + const float3 v1 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + const float3 v2 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); /* return normal */ if (sd->object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { @@ -41,14 +43,20 @@ ccl_device_inline float3 triangle_normal(KernelGlobals *kg, ShaderData *sd) } /* Point and normal on triangle. */ -ccl_device_inline void triangle_point_normal( - KernelGlobals *kg, int object, int prim, float u, float v, float3 *P, float3 *Ng, int *shader) +ccl_device_inline void triangle_point_normal(KernelGlobals kg, + int object, + int prim, + float u, + float v, + ccl_private float3 *P, + ccl_private float3 *Ng, + ccl_private int *shader) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - float3 v0 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - float3 v1 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - float3 v2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + float3 v0 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + float3 v1 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + float3 v2 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); /* compute point */ float t = 1.0f - u - v; *P = (u * v0 + v * v1 + t * v2); @@ -67,25 +75,25 @@ ccl_device_inline void triangle_point_normal( /* Triangle vertex locations */ -ccl_device_inline void triangle_vertices(KernelGlobals *kg, int prim, float3 P[3]) +ccl_device_inline void triangle_vertices(KernelGlobals kg, int prim, float3 P[3]) { const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - P[0] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - P[1] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - P[2] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + P[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + P[1] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + P[2] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); } /* Triangle vertex locations and vertex normals */ -ccl_device_inline void triangle_vertices_and_normals(KernelGlobals *kg, +ccl_device_inline void triangle_vertices_and_normals(KernelGlobals kg, int prim, float3 P[3], float3 N[3]) { const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - P[0] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - P[1] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - P[2] = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + P[0] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + P[1] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + P[2] = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); N[0] = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.x)); N[1] = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.y)); N[2] = float4_to_float3(kernel_tex_fetch(__tri_vnormal, tri_vindex.z)); @@ -94,7 +102,7 @@ ccl_device_inline void triangle_vertices_and_normals(KernelGlobals *kg, /* Interpolate smooth vertex normal from vertices */ ccl_device_inline float3 -triangle_smooth_normal(KernelGlobals *kg, float3 Ng, int prim, float u, float v) +triangle_smooth_normal(KernelGlobals kg, float3 Ng, int prim, float u, float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -108,7 +116,7 @@ triangle_smooth_normal(KernelGlobals *kg, float3 Ng, int prim, float u, float v) } ccl_device_inline float3 triangle_smooth_normal_unnormalized( - KernelGlobals *kg, ShaderData *sd, float3 Ng, int prim, float u, float v) + KernelGlobals kg, ccl_private const ShaderData *sd, float3 Ng, int prim, float u, float v) { /* load triangle vertices */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); @@ -130,16 +138,16 @@ ccl_device_inline float3 triangle_smooth_normal_unnormalized( /* Ray differentials on triangle */ -ccl_device_inline void triangle_dPdudv(KernelGlobals *kg, +ccl_device_inline void triangle_dPdudv(KernelGlobals kg, int prim, - ccl_addr_space float3 *dPdu, - ccl_addr_space float3 *dPdv) + ccl_private float3 *dPdu, + ccl_private float3 *dPdv) { /* fetch triangle vertex coordinates */ const uint4 tri_vindex = kernel_tex_fetch(__tri_vindex, prim); - const float3 p0 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 0)); - const float3 p1 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 1)); - const float3 p2 = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex.w + 2)); + const float3 p0 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 0)); + const float3 p1 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 1)); + const float3 p2 = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex.w + 2)); /* compute derivatives of P w.r.t. uv */ *dPdu = (p0 - p2); @@ -148,8 +156,11 @@ ccl_device_inline void triangle_dPdudv(KernelGlobals *kg, /* Reading attributes on various triangle elements */ -ccl_device float triangle_attribute_float( - KernelGlobals *kg, const ShaderData *sd, const AttributeDescriptor desc, float *dx, float *dy) +ccl_device float triangle_attribute_float(KernelGlobals kg, + ccl_private const ShaderData *sd, + const AttributeDescriptor desc, + ccl_private float *dx, + ccl_private float *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER)) { float f0, f1, f2; @@ -195,11 +206,11 @@ ccl_device float triangle_attribute_float( } } -ccl_device float2 triangle_attribute_float2(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float2 triangle_attribute_float2(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float2 *dx, - float2 *dy) + ccl_private float2 *dx, + ccl_private float2 *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER)) { float2 f0, f1, f2; @@ -245,11 +256,11 @@ ccl_device float2 triangle_attribute_float2(KernelGlobals *kg, } } -ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float3 triangle_attribute_float3(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float3 *dx, - float3 *dy) + ccl_private float3 *dx, + ccl_private float3 *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER)) { float3 f0, f1, f2; @@ -295,11 +306,11 @@ ccl_device float3 triangle_attribute_float3(KernelGlobals *kg, } } -ccl_device float4 triangle_attribute_float4(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 triangle_attribute_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc, - float4 *dx, - float4 *dy) + ccl_private float4 *dx, + ccl_private float4 *dy) { if (desc.element & (ATTR_ELEMENT_VERTEX | ATTR_ELEMENT_VERTEX_MOTION | ATTR_ELEMENT_CORNER | ATTR_ELEMENT_CORNER_BYTE)) { diff --git a/intern/cycles/kernel/geom/geom_triangle_intersect.h b/intern/cycles/kernel/geom/triangle_intersect.h similarity index 64% rename from intern/cycles/kernel/geom/geom_triangle_intersect.h rename to intern/cycles/kernel/geom/triangle_intersect.h index b0cce274b94..faff8a85a93 100644 --- a/intern/cycles/kernel/geom/geom_triangle_intersect.h +++ b/intern/cycles/kernel/geom/triangle_intersect.h @@ -20,28 +20,34 @@ * intersection at the cost of more memory usage. */ +#pragma once + +#include "kernel/sample/lcg.h" + CCL_NAMESPACE_BEGIN -ccl_device_inline bool triangle_intersect(KernelGlobals *kg, - Intersection *isect, +ccl_device_inline bool triangle_intersect(KernelGlobals kg, + ccl_private Intersection *isect, float3 P, float3 dir, + float tmax, uint visibility, int object, int prim_addr) { - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, prim_addr); + const int prim = kernel_tex_fetch(__prim_index, prim_addr); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w; #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) - const ssef *ssef_verts = (ssef *)&kg->__prim_tri_verts.data[tri_vindex]; + const ssef *ssef_verts = (ssef *)&kg->__tri_verts.data[tri_vindex]; #else - const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), - tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), - tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); + const float4 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), + tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), + tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); #endif float t, u, v; if (ray_triangle_intersect(P, dir, - isect->t, + tmax, #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) ssef_verts, #else @@ -59,8 +65,9 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg, if (kernel_tex_fetch(__prim_visibility, prim_addr) & visibility) #endif { - isect->prim = prim_addr; - isect->object = object; + isect->object = (object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, prim_addr) : + object; + isect->prim = prim; isect->type = PRIMITIVE_TRIANGLE; isect->u = u; isect->v = v; @@ -78,15 +85,15 @@ ccl_device_inline bool triangle_intersect(KernelGlobals *kg, */ #ifdef __BVH_LOCAL__ -ccl_device_inline bool triangle_intersect_local(KernelGlobals *kg, - LocalIntersection *local_isect, +ccl_device_inline bool triangle_intersect_local(KernelGlobals kg, + ccl_private LocalIntersection *local_isect, float3 P, float3 dir, int object, int local_object, int prim_addr, float tmax, - uint *lcg_state, + ccl_private uint *lcg_state, int max_hits) { /* Only intersect with matching object, for instanced objects we @@ -97,13 +104,14 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals *kg, } } - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, prim_addr); + const int prim = kernel_tex_fetch(__prim_index, prim_addr); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, prim).w; # if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) - const ssef *ssef_verts = (ssef *)&kg->__prim_tri_verts.data[tri_vindex]; + const ssef *ssef_verts = (ssef *)&kg->__tri_verts.data[tri_vindex]; # else - const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0)), - tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1)), - tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2)); + const float3 tri_a = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 0)), + tri_b = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 1)), + tri_c = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 2)); # endif float t, u, v; if (!ray_triangle_intersect(P, @@ -161,9 +169,9 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals *kg, } /* Record intersection. */ - Intersection *isect = &local_isect->hits[hit]; - isect->prim = prim_addr; - isect->object = object; + ccl_private Intersection *isect = &local_isect->hits[hit]; + isect->prim = prim; + isect->object = local_object; isect->type = PRIMITIVE_TRIANGLE; isect->u = u; isect->v = v; @@ -171,9 +179,9 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals *kg, /* Record geometric normal. */ # if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) - const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0)), - tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1)), - tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2)); + const float3 tri_a = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 0)), + tri_b = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 1)), + tri_c = float4_to_float3(kernel_tex_fetch(__tri_verts, tri_vindex + 2)); # endif local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a)); @@ -192,25 +200,20 @@ ccl_device_inline bool triangle_intersect_local(KernelGlobals *kg, * http://www.cs.virginia.edu/~gfx/Courses/2003/ImageSynthesis/papers/Acceleration/Fast%20MinimumStorage%20RayTriangle%20Intersection.pdf */ -ccl_device_inline float3 triangle_refine(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray) +ccl_device_inline float3 triangle_refine(KernelGlobals kg, + ccl_private ShaderData *sd, + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim) { - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - #ifdef __INTERSECTION_REFINE__ - if (isect->object != OBJECT_NONE) { + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { if (UNLIKELY(t == 0.0f)) { return P; } -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D * t); @@ -219,10 +222,10 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg, P = P + D * t; - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); - const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), - tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), - tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w; + const float4 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), + tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), + tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); @@ -239,13 +242,8 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg, P = P + D * rt; } - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } @@ -255,28 +253,23 @@ ccl_device_inline float3 triangle_refine(KernelGlobals *kg, #endif } -/* Same as above, except that isect->t is assumed to be in object space for +/* Same as above, except that t is assumed to be in object space for * instancing. */ -ccl_device_inline float3 triangle_refine_local(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray) +ccl_device_inline float3 triangle_refine_local(KernelGlobals kg, + ccl_private ShaderData *sd, + float3 P, + float3 D, + float t, + const int isect_object, + const int isect_prim) { #ifdef __KERNEL_OPTIX__ - /* isect->t is always in world space with OptiX. */ - return triangle_refine(kg, sd, isect, ray); + /* t is always in world space with OptiX. */ + return triangle_refine(kg, sd, P, D, t, isect_object, isect_prim); #else - float3 P = ray->P; - float3 D = ray->D; - float t = isect->t; - - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_INVERSE_TRANSFORM); -# endif + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_inverse_transform(kg, sd); P = transform_point(&tfm, P); D = transform_direction(&tfm, D); @@ -286,10 +279,10 @@ ccl_device_inline float3 triangle_refine_local(KernelGlobals *kg, P = P + D * t; # ifdef __INTERSECTION_REFINE__ - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); - const float4 tri_a = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0), - tri_b = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1), - tri_c = kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2); + const uint tri_vindex = kernel_tex_fetch(__tri_vindex, isect_prim).w; + const float4 tri_a = kernel_tex_fetch(__tri_verts, tri_vindex + 0), + tri_b = kernel_tex_fetch(__tri_verts, tri_vindex + 1), + tri_c = kernel_tex_fetch(__tri_verts, tri_vindex + 2); float3 edge1 = make_float3(tri_a.x - tri_c.x, tri_a.y - tri_c.y, tri_a.z - tri_c.z); float3 edge2 = make_float3(tri_b.x - tri_c.x, tri_b.y - tri_c.y, tri_b.z - tri_c.z); float3 tvec = make_float3(P.x - tri_c.x, P.y - tri_c.y, P.z - tri_c.z); @@ -307,13 +300,8 @@ ccl_device_inline float3 triangle_refine_local(KernelGlobals *kg, } # endif /* __INTERSECTION_REFINE__ */ - if (isect->object != OBJECT_NONE) { -# ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -# else - Transform tfm = object_fetch_transform(kg, isect->object, OBJECT_TRANSFORM); -# endif - + if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + const Transform tfm = object_get_transform(kg, sd); P = transform_point(&tfm, P); } diff --git a/intern/cycles/kernel/geom/geom_volume.h b/intern/cycles/kernel/geom/volume.h similarity index 92% rename from intern/cycles/kernel/geom/geom_volume.h rename to intern/cycles/kernel/geom/volume.h index 809b76245ba..4e83ad6acb3 100644 --- a/intern/cycles/kernel/geom/geom_volume.h +++ b/intern/cycles/kernel/geom/volume.h @@ -23,14 +23,16 @@ * 3D voxel textures can be assigned as attributes per mesh, which means the * same shader can be used for volume objects with different densities, etc. */ +#pragma once + CCL_NAMESPACE_BEGIN #ifdef __VOLUME__ /* Return position normalized to 0..1 in mesh bounds */ -ccl_device_inline float3 volume_normalized_position(KernelGlobals *kg, - const ShaderData *sd, +ccl_device_inline float3 volume_normalized_position(KernelGlobals kg, + ccl_private const ShaderData *sd, float3 P) { /* todo: optimize this so it's just a single matrix multiplication when @@ -68,8 +70,8 @@ ccl_device float3 volume_attribute_value_to_float3(const float4 value) } } -ccl_device float4 volume_attribute_float4(KernelGlobals *kg, - const ShaderData *sd, +ccl_device float4 volume_attribute_float4(KernelGlobals kg, + ccl_private const ShaderData *sd, const AttributeDescriptor desc) { if (desc.element & (ATTR_ELEMENT_OBJECT | ATTR_ELEMENT_MESH)) { diff --git a/intern/cycles/kernel/integrator/init_from_bake.h b/intern/cycles/kernel/integrator/init_from_bake.h new file mode 100644 index 00000000000..4e30563e21b --- /dev/null +++ b/intern/cycles/kernel/integrator/init_from_bake.h @@ -0,0 +1,202 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/camera/camera.h" + +#include "kernel/film/accumulate.h" +#include "kernel/film/adaptive_sampling.h" + +#include "kernel/integrator/path_state.h" + +#include "kernel/sample/pattern.h" + +#include "kernel/geom/geom.h" + +CCL_NAMESPACE_BEGIN + +/* This helps with AA but it's not the real solution as it does not AA the geometry + * but it's better than nothing, thus committed. */ +ccl_device_inline float bake_clamp_mirror_repeat(float u, float max) +{ + /* use mirror repeat (like opengl texture) so that if the barycentric + * coordinate goes past the end of the triangle it is not always clamped + * to the same value, gives ugly patterns */ + u /= max; + float fu = floorf(u); + u = u - fu; + + return ((((int)fu) & 1) ? 1.0f - u : u) * max; +} + +/* Return false to indicate that this pixel is finished. + * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known + * that the pixel did converge. */ +ccl_device bool integrator_init_from_bake(KernelGlobals kg, + IntegratorState state, + ccl_global const KernelWorkTile *ccl_restrict tile, + ccl_global float *render_buffer, + const int x, + const int y, + const int scheduled_sample) +{ + PROFILING_INIT(kg, PROFILING_RAY_SETUP); + + /* Initialize path state to give basic buffer access and allow early outputs. */ + path_state_init(state, tile, x, y); + + /* Check whether the pixel has converged and should not be sampled anymore. */ + if (!kernel_need_sample_pixel(kg, state, render_buffer)) { + return false; + } + + /* Always count the sample, even if the camera sample will reject the ray. */ + const int sample = kernel_accum_sample(kg, state, render_buffer, scheduled_sample); + + /* Setup render buffers. */ + const int index = INTEGRATOR_STATE(state, path, render_pixel_index); + const int pass_stride = kernel_data.film.pass_stride; + render_buffer += index * pass_stride; + + ccl_global float *primitive = render_buffer + kernel_data.film.pass_bake_primitive; + ccl_global float *differential = render_buffer + kernel_data.film.pass_bake_differential; + + const int seed = __float_as_uint(primitive[0]); + int prim = __float_as_uint(primitive[1]); + if (prim == -1) { + return false; + } + + prim += kernel_data.bake.tri_offset; + + /* Random number generator. */ + const uint rng_hash = hash_uint(seed) ^ kernel_data.integrator.seed; + + float filter_x, filter_y; + if (sample == 0) { + filter_x = filter_y = 0.5f; + } + else { + path_rng_2D(kg, rng_hash, sample, PRNG_FILTER_U, &filter_x, &filter_y); + } + + /* Initialize path state for path integration. */ + path_state_init_integrator(kg, state, sample, rng_hash); + + /* Barycentric UV with sub-pixel offset. */ + float u = primitive[2]; + float v = primitive[3]; + + float dudx = differential[0]; + float dudy = differential[1]; + float dvdx = differential[2]; + float dvdy = differential[3]; + + if (sample > 0) { + u = bake_clamp_mirror_repeat(u + dudx * (filter_x - 0.5f) + dudy * (filter_y - 0.5f), 1.0f); + v = bake_clamp_mirror_repeat(v + dvdx * (filter_x - 0.5f) + dvdy * (filter_y - 0.5f), + 1.0f - u); + } + + /* Position and normal on triangle. */ + const int object = kernel_data.bake.object_index; + float3 P, Ng; + int shader; + triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); + + const int object_flag = kernel_tex_fetch(__object_flag, object); + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); + P = transform_point_auto(&tfm, P); + } + + if (kernel_data.film.pass_background != PASS_UNUSED) { + /* Environment baking. */ + + /* Setup and write ray. */ + Ray ray ccl_optional_struct_init; + ray.P = zero_float3(); + ray.D = normalize(P); + ray.t = FLT_MAX; + ray.time = 0.5f; + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); + integrator_state_write_ray(kg, state, &ray); + + /* Setup next kernel to execute. */ + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + } + else { + /* Surface baking. */ + float3 N = (shader & SHADER_SMOOTH_NORMAL) ? triangle_smooth_normal(kg, Ng, prim, u, v) : Ng; + + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); + N = normalize(transform_direction_transposed(&itfm, N)); + Ng = normalize(transform_direction_transposed(&itfm, Ng)); + } + + /* Setup ray. */ + Ray ray ccl_optional_struct_init; + ray.P = P + N; + ray.D = -N; + ray.t = FLT_MAX; + ray.time = 0.5f; + + /* Setup differentials. */ + float3 dPdu, dPdv; + triangle_dPdudv(kg, prim, &dPdu, &dPdv); + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); + dPdu = transform_direction(&tfm, dPdu); + dPdv = transform_direction(&tfm, dPdv); + } + + differential3 dP; + dP.dx = dPdu * dudx + dPdv * dvdx; + dP.dy = dPdu * dudy + dPdv * dvdy; + ray.dP = differential_make_compact(dP); + ray.dD = differential_zero_compact(); + + /* Write ray. */ + integrator_state_write_ray(kg, state, &ray); + + /* Setup and write intersection. */ + Intersection isect ccl_optional_struct_init; + isect.object = kernel_data.bake.object_index; + isect.prim = prim; + isect.u = u; + isect.v = v; + isect.t = 1.0f; + isect.type = PRIMITIVE_TRIANGLE; + integrator_state_write_isect(kg, state, &isect); + + /* Setup next kernel to execute. */ + const int shader_index = shader & SHADER_MASK; + const int shader_flags = kernel_tex_fetch(__shaders, shader_index).flags; + if (shader_flags & SD_HAS_RAYTRACE) { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader_index); + } + else { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader_index); + } + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/init_from_camera.h b/intern/cycles/kernel/integrator/init_from_camera.h new file mode 100644 index 00000000000..f0ba77bd9a6 --- /dev/null +++ b/intern/cycles/kernel/integrator/init_from_camera.h @@ -0,0 +1,124 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/camera/camera.h" + +#include "kernel/film/accumulate.h" +#include "kernel/film/adaptive_sampling.h" + +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shadow_catcher.h" + +#include "kernel/sample/pattern.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline void integrate_camera_sample(KernelGlobals kg, + const int sample, + const int x, + const int y, + const uint rng_hash, + ccl_private Ray *ray) +{ + /* Filter sampling. */ + float filter_u, filter_v; + + if (sample == 0) { + filter_u = 0.5f; + filter_v = 0.5f; + } + else { + path_rng_2D(kg, rng_hash, sample, PRNG_FILTER_U, &filter_u, &filter_v); + } + + /* Depth of field sampling. */ + float lens_u = 0.0f, lens_v = 0.0f; + if (kernel_data.cam.aperturesize > 0.0f) { + path_rng_2D(kg, rng_hash, sample, PRNG_LENS_U, &lens_u, &lens_v); + } + + /* Motion blur time sampling. */ + float time = 0.0f; +#ifdef __CAMERA_MOTION__ + if (kernel_data.cam.shuttertime != -1.0f) + time = path_rng_1D(kg, rng_hash, sample, PRNG_TIME); +#endif + + /* Generate camera ray. */ + camera_sample(kg, x, y, filter_u, filter_v, lens_u, lens_v, time, ray); +} + +/* Return false to indicate that this pixel is finished. + * Used by CPU implementation to not attempt to sample pixel for multiple samples once its known + * that the pixel did converge. */ +ccl_device bool integrator_init_from_camera(KernelGlobals kg, + IntegratorState state, + ccl_global const KernelWorkTile *ccl_restrict tile, + ccl_global float *render_buffer, + const int x, + const int y, + const int scheduled_sample) +{ + PROFILING_INIT(kg, PROFILING_RAY_SETUP); + + /* Initialize path state to give basic buffer access and allow early outputs. */ + path_state_init(state, tile, x, y); + + /* Check whether the pixel has converged and should not be sampled anymore. */ + if (!kernel_need_sample_pixel(kg, state, render_buffer)) { + return false; + } + + /* Count the sample and get an effective sample for this pixel. + * + * This logic allows to both count actual number of samples per pixel, and to add samples to this + * pixel after it was converged and samples were added somewhere else (in which case the + * `scheduled_sample` will be different from actual number of samples in this pixel). */ + const int sample = kernel_accum_sample(kg, state, render_buffer, scheduled_sample); + + /* Initialize random number seed for path. */ + const uint rng_hash = path_rng_hash_init(kg, sample, x, y); + + { + /* Generate camera ray. */ + Ray ray; + integrate_camera_sample(kg, sample, x, y, rng_hash, &ray); + if (ray.t == 0.0f) { + return true; + } + + /* Write camera ray to state. */ + integrator_state_write_ray(kg, state, &ray); + } + + /* Initialize path state for path integration. */ + path_state_init_integrator(kg, state, sample, rng_hash); + + /* Continue with intersect_closest kernel, optionally initializing volume + * stack before that if the camera may be inside a volume. */ + if (kernel_data.cam.is_inside_volume) { + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK); + } + else { + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_closest.h b/intern/cycles/kernel/integrator/intersect_closest.h new file mode 100644 index 00000000000..7fb88fc2804 --- /dev/null +++ b/intern/cycles/kernel/integrator/intersect_closest.h @@ -0,0 +1,246 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/camera/projection.h" + +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shadow_catcher.h" + +#include "kernel/light/light.h" + +#include "kernel/util/differential.h" + +#include "kernel/geom/geom.h" + +#include "kernel/bvh/bvh.h" + +CCL_NAMESPACE_BEGIN + +template +ccl_device_forceinline bool integrator_intersect_terminate(KernelGlobals kg, + IntegratorState state, + const int shader_flags) +{ + + /* Optional AO bounce termination. + * We continue evaluating emissive/transparent surfaces and volumes, similar + * to direct lighting. Only if we know there are none can we terminate the + * path immediately. */ + if (path_state_ao_bounce(kg, state)) { + if (shader_flags & (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + else if (!integrator_state_volume_stack_is_empty(kg, state)) { + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_AFTER_VOLUME; + } + else { + return true; + } + } + + /* Load random number state. */ + RNGState rng_state; + path_state_rng_load(state, &rng_state); + + /* We perform path termination in this kernel to avoid launching shade_surface + * and evaluating the shader when not needed. Only for emission and transparent + * surfaces in front of emission do we need to evaluate the shader, since we + * perform MIS as part of indirect rays. */ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const float probability = path_state_continuation_probability(kg, state, path_flag); + + if (probability != 1.0f) { + const float terminate = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE); + + if (probability == 0.0f || terminate >= probability) { + if (shader_flags & SD_HAS_EMISSION) { + /* Mark path to be terminated right after shader evaluation on the surface. */ + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE; + } + else if (!integrator_state_volume_stack_is_empty(kg, state)) { + /* TODO: only do this for emissive volumes. */ + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_TERMINATE_IN_NEXT_VOLUME; + } + else { + return true; + } + } + } + + return false; +} + +/* Note that current_kernel is a template value since making this a variable + * leads to poor performance with CUDA atomics. */ +template +ccl_device_forceinline void integrator_intersect_shader_next_kernel( + KernelGlobals kg, + IntegratorState state, + ccl_private const Intersection *ccl_restrict isect, + const int shader, + const int shader_flags) +{ + /* Note on scheduling. + * + * When there is no shadow catcher split the scheduling is simple: schedule surface shading with + * or without raytrace support, depending on the shader used. + * + * When there is a shadow catcher split the general idea is to have the following configuration: + * + * - Schedule surface shading kernel (with corresponding raytrace support) for the ray which + * will trace shadow catcher object. + * + * - When no alpha-over of approximate shadow catcher is needed, schedule surface shading for + * the matte ray. + * + * - Otherwise schedule background shading kernel, so that we have a background to alpha-over + * on. The background kernel will then schedule surface shading for the matte ray. + * + * Note that the splitting leaves kernel and sorting counters as-is, so use INIT semantic for + * the matte path. */ + + const bool use_raytrace_kernel = (shader_flags & SD_HAS_RAYTRACE); + + if (use_raytrace_kernel) { + INTEGRATOR_PATH_NEXT_SORTED( + current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + } + else { + INTEGRATOR_PATH_NEXT_SORTED(current_kernel, DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + } + +#ifdef __SHADOW_CATCHER__ + const int object_flags = intersection_get_object_flags(kg, isect); + if (kernel_shadow_catcher_split(kg, state, object_flags)) { + if (kernel_data.film.pass_background != PASS_UNUSED && !kernel_data.background.transparent) { + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SHADOW_CATCHER_BACKGROUND; + + INTEGRATOR_PATH_INIT(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + } + else if (use_raytrace_kernel) { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, shader); + } + else { + INTEGRATOR_PATH_INIT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, shader); + } + } +#endif +} + +ccl_device void integrator_intersect_closest(KernelGlobals kg, IntegratorState state) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_CLOSEST); + + /* Read ray from integrator state into local memory. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_ray(kg, state, &ray); + kernel_assert(ray.t != 0.0f); + + const uint visibility = path_state_ray_visibility(state); + const int last_isect_prim = INTEGRATOR_STATE(state, isect, prim); + const int last_isect_object = INTEGRATOR_STATE(state, isect, object); + + /* Trick to use short AO rays to approximate indirect light at the end of the path. */ + if (path_state_ao_bounce(kg, state)) { + ray.t = kernel_data.integrator.ao_bounces_distance; + + if (last_isect_object != OBJECT_NONE) { + const float object_ao_distance = kernel_tex_fetch(__objects, last_isect_object).ao_distance; + if (object_ao_distance != 0.0f) { + ray.t = object_ao_distance; + } + } + } + + /* Scene Intersection. */ + Intersection isect ccl_optional_struct_init; + bool hit = scene_intersect(kg, &ray, visibility, &isect); + + /* TODO: remove this and do it in the various intersection functions instead. */ + if (!hit) { + isect.prim = PRIM_NONE; + } + + /* Light intersection for MIS. */ + if (kernel_data.integrator.use_lamp_mis) { + /* NOTE: if we make lights visible to camera rays, we'll need to initialize + * these in the path_state_init. */ + const int last_type = INTEGRATOR_STATE(state, isect, type); + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + hit = lights_intersect( + kg, &ray, &isect, last_isect_prim, last_isect_object, last_type, path_flag) || + hit; + } + + /* Write intersection result into global integrator state memory. */ + integrator_state_write_isect(kg, state, &isect); + +#ifdef __VOLUME__ + if (!integrator_state_volume_stack_is_empty(kg, state)) { + const bool hit_surface = hit && !(isect.type & PRIMITIVE_LAMP); + const int shader = (hit_surface) ? intersection_get_shader(kg, &isect) : SHADER_NONE; + const int flags = (hit_surface) ? kernel_tex_fetch(__shaders, shader).flags : 0; + + if (!integrator_intersect_terminate( + kg, state, flags)) { + /* Continue with volume kernel if we are inside a volume, regardless + * if we hit anything. */ + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + } + else { + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + } + return; + } +#endif + + if (hit) { + /* Hit a surface, continue with light or surface kernel. */ + if (isect.type & PRIMITIVE_LAMP) { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + return; + } + else { + /* Hit a surface, continue with surface kernel unless terminated. */ + const int shader = intersection_get_shader(kg, &isect); + const int flags = kernel_tex_fetch(__shaders, shader).flags; + + if (!integrator_intersect_terminate( + kg, state, flags)) { + integrator_intersect_shader_next_kernel( + kg, state, &isect, shader, flags); + return; + } + else { + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + return; + } + } + } + else { + /* Nothing hit, continue with background kernel. */ + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + return; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_shadow.h b/intern/cycles/kernel/integrator/intersect_shadow.h new file mode 100644 index 00000000000..90422445fad --- /dev/null +++ b/intern/cycles/kernel/integrator/intersect_shadow.h @@ -0,0 +1,190 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Visibility for the shadow ray. */ +ccl_device_forceinline uint integrate_intersect_shadow_visibility(KernelGlobals kg, + ConstIntegratorShadowState state) +{ + uint visibility = PATH_RAY_SHADOW; + +#ifdef __SHADOW_CATCHER__ + const uint32_t path_flag = INTEGRATOR_STATE(state, shadow_path, flag); + visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility); +#endif + + return visibility; +} + +ccl_device bool integrate_intersect_shadow_opaque(KernelGlobals kg, + IntegratorShadowState state, + ccl_private const Ray *ray, + const uint visibility) +{ + /* Mask which will pick only opaque visibility bits from the `visibility`. + * Calculate the mask at compile time: the visibility will either be a high bits for the shadow + * catcher objects, or lower bits for the regular objects (there is no need to check the path + * state here again). */ + constexpr const uint opaque_mask = SHADOW_CATCHER_VISIBILITY_SHIFT(PATH_RAY_SHADOW_OPAQUE) | + PATH_RAY_SHADOW_OPAQUE; + + Intersection isect; + const bool opaque_hit = scene_intersect(kg, ray, visibility & opaque_mask, &isect); + + if (!opaque_hit) { + INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = 0; + } + + return opaque_hit; +} + +ccl_device_forceinline int integrate_shadow_max_transparent_hits(KernelGlobals kg, + ConstIntegratorShadowState state) +{ + const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; + const int transparent_bounce = INTEGRATOR_STATE(state, shadow_path, transparent_bounce); + + return max(transparent_max_bounce - transparent_bounce - 1, 0); +} + +#ifdef __TRANSPARENT_SHADOWS__ +# if defined(__KERNEL_CPU__) +ccl_device int shadow_intersections_compare(const void *a, const void *b) +{ + const Intersection *isect_a = (const Intersection *)a; + const Intersection *isect_b = (const Intersection *)b; + + if (isect_a->t < isect_b->t) + return -1; + else if (isect_a->t > isect_b->t) + return 1; + else + return 0; +} +# endif + +ccl_device_inline void sort_shadow_intersections(IntegratorShadowState state, uint num_hits) +{ + kernel_assert(num_hits > 0); + +# ifdef __KERNEL_GPU__ + /* Use bubble sort which has more friendly memory pattern on GPU. */ + bool swapped; + do { + swapped = false; + for (int j = 0; j < num_hits - 1; ++j) { + if (INTEGRATOR_STATE_ARRAY(state, shadow_isect, j, t) > + INTEGRATOR_STATE_ARRAY(state, shadow_isect, j + 1, t)) { + struct Intersection tmp_j ccl_optional_struct_init; + struct Intersection tmp_j_1 ccl_optional_struct_init; + integrator_state_read_shadow_isect(state, &tmp_j, j); + integrator_state_read_shadow_isect(state, &tmp_j_1, j + 1); + integrator_state_write_shadow_isect(state, &tmp_j_1, j); + integrator_state_write_shadow_isect(state, &tmp_j, j + 1); + swapped = true; + } + } + --num_hits; + } while (swapped); +# else + Intersection *isect_array = (Intersection *)state->shadow_isect; + qsort(isect_array, num_hits, sizeof(Intersection), shadow_intersections_compare); +# endif +} + +ccl_device bool integrate_intersect_shadow_transparent(KernelGlobals kg, + IntegratorShadowState state, + ccl_private const Ray *ray, + const uint visibility) +{ + /* Limit the number hits to the max transparent bounces allowed and the size that we + * have available in the integrator state. */ + const uint max_hits = integrate_shadow_max_transparent_hits(kg, state); + uint num_hits = 0; + float throughput = 1.0f; + bool opaque_hit = scene_intersect_shadow_all( + kg, state, ray, visibility, max_hits, &num_hits, &throughput); + + /* Computed throughput from baked shadow transparency, where we can bypass recording + * intersections and shader evaluation. */ + if (throughput != 1.0f) { + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) *= throughput; + } + + /* If number of hits exceed the transparent bounces limit, make opaque. */ + if (num_hits > max_hits) { + opaque_hit = true; + } + + if (!opaque_hit) { + const uint num_recorded_hits = min(num_hits, min(max_hits, INTEGRATOR_SHADOW_ISECT_SIZE)); + + if (num_recorded_hits > 0) { + sort_shadow_intersections(state, num_recorded_hits); + } + + INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = num_hits; + } + else { + INTEGRATOR_STATE_WRITE(state, shadow_path, num_hits) = 0; + } + + return opaque_hit; +} +#endif + +ccl_device void integrator_intersect_shadow(KernelGlobals kg, IntegratorShadowState state) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_SHADOW); + + /* Read ray from integrator state into local memory. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_shadow_ray(kg, state, &ray); + + /* Compute visibility. */ + const uint visibility = integrate_intersect_shadow_visibility(kg, state); + +#ifdef __TRANSPARENT_SHADOWS__ + /* TODO: compile different kernels depending on this? Especially for OptiX + * conditional trace calls are bad. */ + const bool opaque_hit = (kernel_data.integrator.transparent_shadows) ? + integrate_intersect_shadow_transparent(kg, state, &ray, visibility) : + integrate_intersect_shadow_opaque(kg, state, &ray, visibility); +#else + const bool opaque_hit = integrate_intersect_shadow_opaque(kg, state, &ray, visibility); +#endif + + if (opaque_hit) { + /* Hit an opaque surface, shadow path ends here. */ + INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + return; + } + else { + /* Hit nothing or transparent surfaces, continue to shadow kernel + * for shading and render buffer output. + * + * TODO: could also write to render buffer directly if no transparent shadows? + * Could save a kernel execution for the common case. */ + INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, + DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_subsurface.h b/intern/cycles/kernel/integrator/intersect_subsurface.h new file mode 100644 index 00000000000..27b8e1e5f5a --- /dev/null +++ b/intern/cycles/kernel/integrator/intersect_subsurface.h @@ -0,0 +1,36 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/subsurface.h" + +CCL_NAMESPACE_BEGIN + +ccl_device void integrator_intersect_subsurface(KernelGlobals kg, IntegratorState state) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_SUBSURFACE); + +#ifdef __SUBSURFACE__ + if (subsurface_scatter(kg, state)) { + return; + } +#endif + + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/intersect_volume_stack.h b/intern/cycles/kernel/integrator/intersect_volume_stack.h new file mode 100644 index 00000000000..1c91318ff9c --- /dev/null +++ b/intern/cycles/kernel/integrator/intersect_volume_stack.h @@ -0,0 +1,206 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/bvh/bvh.h" +#include "kernel/geom/geom.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/volume_stack.h" + +CCL_NAMESPACE_BEGIN + +ccl_device void integrator_volume_stack_update_for_subsurface(KernelGlobals kg, + IntegratorState state, + const float3 from_P, + const float3 to_P) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK); + + ShaderDataTinyStorage stack_sd_storage; + ccl_private ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); + + kernel_assert(kernel_data.integrator.use_volumes); + + Ray volume_ray ccl_optional_struct_init; + volume_ray.P = from_P; + volume_ray.D = normalize_len(to_P - from_P, &volume_ray.t); + + /* Store to avoid global fetches on every intersection step. */ + const uint volume_stack_size = kernel_data.volume_stack_size; + +#ifdef __VOLUME_RECORD_ALL__ + Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1]; + uint num_hits = scene_intersect_volume_all( + kg, &volume_ray, hits, 2 * volume_stack_size, PATH_RAY_ALL_VISIBILITY); + if (num_hits > 0) { + Intersection *isect = hits; + + qsort(hits, num_hits, sizeof(Intersection), intersections_compare); + + for (uint hit = 0; hit < num_hits; ++hit, ++isect) { + shader_setup_from_ray(kg, stack_sd, &volume_ray, isect); + volume_stack_enter_exit(kg, state, stack_sd); + } + } +#else + Intersection isect; + int step = 0; + while (step < 2 * volume_stack_size && + scene_intersect_volume(kg, &volume_ray, &isect, PATH_RAY_ALL_VISIBILITY)) { + shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect); + volume_stack_enter_exit(kg, state, stack_sd); + + /* Move ray forward. */ + volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); + if (volume_ray.t != FLT_MAX) { + volume_ray.D = normalize_len(to_P - volume_ray.P, &volume_ray.t); + } + ++step; + } +#endif +} + +ccl_device void integrator_intersect_volume_stack(KernelGlobals kg, IntegratorState state) +{ + PROFILING_INIT(kg, PROFILING_INTERSECT_VOLUME_STACK); + + ShaderDataTinyStorage stack_sd_storage; + ccl_private ShaderData *stack_sd = AS_SHADER_DATA(&stack_sd_storage); + + Ray volume_ray ccl_optional_struct_init; + integrator_state_read_ray(kg, state, &volume_ray); + volume_ray.t = FLT_MAX; + + const uint visibility = (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_ALL_VISIBILITY); + int stack_index = 0, enclosed_index = 0; + + /* Write background shader. */ + if (kernel_data.background.volume_shader != SHADER_NONE) { + const VolumeStack new_entry = {OBJECT_NONE, kernel_data.background.volume_shader}; + integrator_state_write_volume_stack(state, stack_index, new_entry); + stack_index++; + } + + /* Store to avoid global fetches on every intersection step. */ + const uint volume_stack_size = kernel_data.volume_stack_size; + +#ifdef __VOLUME_RECORD_ALL__ + Intersection hits[2 * MAX_VOLUME_STACK_SIZE + 1]; + uint num_hits = scene_intersect_volume_all( + kg, &volume_ray, hits, 2 * volume_stack_size, visibility); + if (num_hits > 0) { + int enclosed_volumes[MAX_VOLUME_STACK_SIZE]; + Intersection *isect = hits; + + qsort(hits, num_hits, sizeof(Intersection), intersections_compare); + + for (uint hit = 0; hit < num_hits; ++hit, ++isect) { + shader_setup_from_ray(kg, stack_sd, &volume_ray, isect); + if (stack_sd->flag & SD_BACKFACING) { + bool need_add = true; + for (int i = 0; i < enclosed_index && need_add; ++i) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ + if (enclosed_volumes[i] == stack_sd->object) { + need_add = false; + } + } + for (int i = 0; i < stack_index && need_add; ++i) { + /* Don't add intersections twice. */ + VolumeStack entry = integrator_state_read_volume_stack(state, i); + if (entry.object == stack_sd->object) { + need_add = false; + break; + } + } + if (need_add && stack_index < volume_stack_size - 1) { + const VolumeStack new_entry = {stack_sd->object, stack_sd->shader}; + integrator_state_write_volume_stack(state, stack_index, new_entry); + ++stack_index; + } + } + else { + /* If ray from camera enters the volume, this volume shouldn't + * be added to the stack on exit. + */ + enclosed_volumes[enclosed_index++] = stack_sd->object; + } + } + } +#else + /* CUDA does not support definition of a variable size arrays, so use the maximum possible. */ + int enclosed_volumes[MAX_VOLUME_STACK_SIZE]; + int step = 0; + + while (stack_index < volume_stack_size - 1 && enclosed_index < volume_stack_size - 1 && + step < 2 * volume_stack_size) { + Intersection isect; + if (!scene_intersect_volume(kg, &volume_ray, &isect, visibility)) { + break; + } + + shader_setup_from_ray(kg, stack_sd, &volume_ray, &isect); + if (stack_sd->flag & SD_BACKFACING) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ + bool need_add = true; + for (int i = 0; i < enclosed_index && need_add; ++i) { + /* If ray exited the volume and never entered to that volume + * it means that camera is inside such a volume. + */ + if (enclosed_volumes[i] == stack_sd->object) { + need_add = false; + } + } + for (int i = 0; i < stack_index && need_add; ++i) { + /* Don't add intersections twice. */ + VolumeStack entry = integrator_state_read_volume_stack(state, i); + if (entry.object == stack_sd->object) { + need_add = false; + break; + } + } + if (need_add) { + const VolumeStack new_entry = {stack_sd->object, stack_sd->shader}; + integrator_state_write_volume_stack(state, stack_index, new_entry); + ++stack_index; + } + } + else { + /* If ray from camera enters the volume, this volume shouldn't + * be added to the stack on exit. + */ + enclosed_volumes[enclosed_index++] = stack_sd->object; + } + + /* Move ray forward. */ + volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); + ++step; + } +#endif + + /* Write terminator. */ + const VolumeStack new_entry = {OBJECT_NONE, SHADER_NONE}; + integrator_state_write_volume_stack(state, stack_index, new_entry); + + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/megakernel.h b/intern/cycles/kernel/integrator/megakernel.h new file mode 100644 index 00000000000..d8cc794dc7a --- /dev/null +++ b/intern/cycles/kernel/integrator/megakernel.h @@ -0,0 +1,113 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/init_from_camera.h" +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/intersect_shadow.h" +#include "kernel/integrator/intersect_subsurface.h" +#include "kernel/integrator/intersect_volume_stack.h" +#include "kernel/integrator/shade_background.h" +#include "kernel/integrator/shade_light.h" +#include "kernel/integrator/shade_shadow.h" +#include "kernel/integrator/shade_surface.h" +#include "kernel/integrator/shade_volume.h" + +CCL_NAMESPACE_BEGIN + +ccl_device void integrator_megakernel(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + /* Each kernel indicates the next kernel to execute, so here we simply + * have to check what that kernel is and execute it. */ + while (true) { + /* Handle any shadow paths before we potentially create more shadow paths. */ + const uint32_t shadow_queued_kernel = INTEGRATOR_STATE( + &state->shadow, shadow_path, queued_kernel); + if (shadow_queued_kernel) { + switch (shadow_queued_kernel) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + integrator_intersect_shadow(kg, &state->shadow); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + integrator_shade_shadow(kg, &state->shadow, render_buffer); + break; + default: + kernel_assert(0); + break; + } + continue; + } + + /* Handle any AO paths before we potentially create more AO paths. */ + const uint32_t ao_queued_kernel = INTEGRATOR_STATE(&state->ao, shadow_path, queued_kernel); + if (ao_queued_kernel) { + switch (ao_queued_kernel) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW: + integrator_intersect_shadow(kg, &state->ao); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW: + integrator_shade_shadow(kg, &state->ao, render_buffer); + break; + default: + kernel_assert(0); + break; + } + continue; + } + + /* Then handle regular path kernels. */ + const uint32_t queued_kernel = INTEGRATOR_STATE(state, path, queued_kernel); + if (queued_kernel) { + switch (queued_kernel) { + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST: + integrator_intersect_closest(kg, state); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND: + integrator_shade_background(kg, state, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE: + integrator_shade_surface(kg, state, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME: + integrator_shade_volume(kg, state, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE: + integrator_shade_surface_raytrace(kg, state, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT: + integrator_shade_light(kg, state, render_buffer); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE: + integrator_intersect_subsurface(kg, state); + break; + case DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK: + integrator_intersect_volume_stack(kg, state); + break; + default: + kernel_assert(0); + break; + } + continue; + } + + break; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/path_state.h b/intern/cycles/kernel/integrator/path_state.h new file mode 100644 index 00000000000..8311b97dedb --- /dev/null +++ b/intern/cycles/kernel/integrator/path_state.h @@ -0,0 +1,376 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/sample/pattern.h" + +CCL_NAMESPACE_BEGIN + +/* Initialize queues, so that the this path is considered terminated. + * Used for early outputs in the camera ray initialization, as well as initialization of split + * states for shadow catcher. */ +ccl_device_inline void path_state_init_queues(IntegratorState state) +{ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; +#ifdef __KERNEL_CPU__ + INTEGRATOR_STATE_WRITE(&state->shadow, shadow_path, queued_kernel) = 0; + INTEGRATOR_STATE_WRITE(&state->ao, shadow_path, queued_kernel) = 0; +#endif +} + +/* Minimalistic initialization of the path state, which is needed for early outputs in the + * integrator initialization to work. */ +ccl_device_inline void path_state_init(IntegratorState state, + ccl_global const KernelWorkTile *ccl_restrict tile, + const int x, + const int y) +{ + const uint render_pixel_index = (uint)tile->offset + x + y * tile->stride; + + INTEGRATOR_STATE_WRITE(state, path, render_pixel_index) = render_pixel_index; + + path_state_init_queues(state); +} + +/* Initialize the rest of the path state needed to continue the path integration. */ +ccl_device_inline void path_state_init_integrator(KernelGlobals kg, + IntegratorState state, + const int sample, + const uint rng_hash) +{ + INTEGRATOR_STATE_WRITE(state, path, sample) = sample; + INTEGRATOR_STATE_WRITE(state, path, bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, diffuse_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, glossy_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, transmission_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, volume_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, volume_bounds_bounce) = 0; + INTEGRATOR_STATE_WRITE(state, path, rng_hash) = rng_hash; + INTEGRATOR_STATE_WRITE(state, path, rng_offset) = PRNG_BASE_NUM; + INTEGRATOR_STATE_WRITE(state, path, flag) = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | + PATH_RAY_TRANSPARENT_BACKGROUND; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, path, throughput) = make_float3(1.0f, 1.0f, 1.0f); + + if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, object) = OBJECT_NONE; + INTEGRATOR_STATE_ARRAY_WRITE( + state, volume_stack, 0, shader) = kernel_data.background.volume_shader; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, object) = OBJECT_NONE; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, shader) = SHADER_NONE; + } + +#ifdef __DENOISING_FEATURES__ + if (kernel_data.kernel_features & KERNEL_FEATURE_DENOISING) { + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_DENOISING_FEATURES; + INTEGRATOR_STATE_WRITE(state, path, denoising_feature_throughput) = one_float3(); + } +#endif +} + +ccl_device_inline void path_state_next(KernelGlobals kg, IntegratorState state, int label) +{ + uint32_t flag = INTEGRATOR_STATE(state, path, flag); + + /* ray through transparent keeps same flags from previous ray and is + * not counted as a regular bounce, transparent has separate max */ + if (label & LABEL_TRANSPARENT) { + uint32_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce) + 1; + + flag |= PATH_RAY_TRANSPARENT; + if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { + flag |= PATH_RAY_TERMINATE_ON_NEXT_SURFACE; + } + + if (!kernel_data.integrator.transparent_shadows) + flag |= PATH_RAY_MIS_SKIP; + + INTEGRATOR_STATE_WRITE(state, path, flag) = flag; + INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = transparent_bounce; + /* Random number generator next bounce. */ + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; + return; + } + + uint32_t bounce = INTEGRATOR_STATE(state, path, bounce) + 1; + if (bounce >= kernel_data.integrator.max_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + + flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP); + +#ifdef __VOLUME__ + if (label & LABEL_VOLUME_SCATTER) { + /* volume scatter */ + flag |= PATH_RAY_VOLUME_SCATTER; + flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; + if (bounce == 1) { + flag |= PATH_RAY_VOLUME_PASS; + } + + const int volume_bounce = INTEGRATOR_STATE(state, path, volume_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, volume_bounce) = volume_bounce; + if (volume_bounce >= kernel_data.integrator.max_volume_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + } + else +#endif + { + /* surface reflection/transmission */ + if (label & LABEL_REFLECT) { + flag |= PATH_RAY_REFLECT; + flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; + + if (label & LABEL_DIFFUSE) { + const int diffuse_bounce = INTEGRATOR_STATE(state, path, diffuse_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, diffuse_bounce) = diffuse_bounce; + if (diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + } + else { + const int glossy_bounce = INTEGRATOR_STATE(state, path, glossy_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, glossy_bounce) = glossy_bounce; + if (glossy_bounce >= kernel_data.integrator.max_glossy_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + } + } + else { + kernel_assert(label & LABEL_TRANSMIT); + + flag |= PATH_RAY_TRANSMIT; + + if (!(label & LABEL_TRANSMIT_TRANSPARENT)) { + flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; + } + + const int transmission_bounce = INTEGRATOR_STATE(state, path, transmission_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, transmission_bounce) = transmission_bounce; + if (transmission_bounce >= kernel_data.integrator.max_transmission_bounce) { + flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; + } + } + + /* diffuse/glossy/singular */ + if (label & LABEL_DIFFUSE) { + flag |= PATH_RAY_DIFFUSE | PATH_RAY_DIFFUSE_ANCESTOR; + } + else if (label & LABEL_GLOSSY) { + flag |= PATH_RAY_GLOSSY; + } + else { + kernel_assert(label & LABEL_SINGULAR); + flag |= PATH_RAY_GLOSSY | PATH_RAY_SINGULAR | PATH_RAY_MIS_SKIP; + } + + /* Render pass categories. */ + if (bounce == 1) { + flag |= (label & LABEL_TRANSMIT) ? PATH_RAY_TRANSMISSION_PASS : PATH_RAY_REFLECT_PASS; + } + } + + INTEGRATOR_STATE_WRITE(state, path, flag) = flag; + INTEGRATOR_STATE_WRITE(state, path, bounce) = bounce; + + /* Random number generator next bounce. */ + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; +} + +#ifdef __VOLUME__ +ccl_device_inline bool path_state_volume_next(IntegratorState state) +{ + /* For volume bounding meshes we pass through without counting transparent + * bounces, only sanity check in case self intersection gets us stuck. */ + uint32_t volume_bounds_bounce = INTEGRATOR_STATE(state, path, volume_bounds_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, volume_bounds_bounce) = volume_bounds_bounce; + if (volume_bounds_bounce > VOLUME_BOUNDS_MAX) { + return false; + } + + /* Random number generator next bounce. */ + if (volume_bounds_bounce > 1) { + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; + } + + return true; +} +#endif + +ccl_device_inline uint path_state_ray_visibility(ConstIntegratorState state) +{ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + uint32_t visibility = path_flag & PATH_RAY_ALL_VISIBILITY; + + /* For visibility, diffuse/glossy are for reflection only. */ + if (visibility & PATH_RAY_TRANSMIT) { + visibility &= ~(PATH_RAY_DIFFUSE | PATH_RAY_GLOSSY); + } + + /* todo: this is not supported as its own ray visibility yet. */ + if (path_flag & PATH_RAY_VOLUME_SCATTER) { + visibility |= PATH_RAY_DIFFUSE; + } + + visibility = SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility); + + return visibility; +} + +ccl_device_inline float path_state_continuation_probability(KernelGlobals kg, + ConstIntegratorState state, + const uint32_t path_flag) +{ + if (path_flag & PATH_RAY_TRANSPARENT) { + const uint32_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); + /* Do at least specified number of bounces without RR. */ + if (transparent_bounce <= kernel_data.integrator.transparent_min_bounce) { + return 1.0f; + } + } + else { + const uint32_t bounce = INTEGRATOR_STATE(state, path, bounce); + /* Do at least specified number of bounces without RR. */ + if (bounce <= kernel_data.integrator.min_bounce) { + return 1.0f; + } + } + + /* Probabilistic termination: use sqrt() to roughly match typical view + * transform and do path termination a bit later on average. */ + return min(sqrtf(max3(fabs(INTEGRATOR_STATE(state, path, throughput)))), 1.0f); +} + +ccl_device_inline bool path_state_ao_bounce(KernelGlobals kg, ConstIntegratorState state) +{ + if (!kernel_data.integrator.ao_bounces) { + return false; + } + + const int bounce = INTEGRATOR_STATE(state, path, bounce) - + INTEGRATOR_STATE(state, path, transmission_bounce) - + (INTEGRATOR_STATE(state, path, glossy_bounce) > 0) + 1; + return (bounce > kernel_data.integrator.ao_bounces); +} + +/* Random Number Sampling Utility Functions + * + * For each random number in each step of the path we must have a unique + * dimension to avoid using the same sequence twice. + * + * For branches in the path we must be careful not to reuse the same number + * in a sequence and offset accordingly. + */ + +/* RNG State loaded onto stack. */ +typedef struct RNGState { + uint rng_hash; + uint rng_offset; + int sample; +} RNGState; + +ccl_device_inline void path_state_rng_load(ConstIntegratorState state, + ccl_private RNGState *rng_state) +{ + rng_state->rng_hash = INTEGRATOR_STATE(state, path, rng_hash); + rng_state->rng_offset = INTEGRATOR_STATE(state, path, rng_offset); + rng_state->sample = INTEGRATOR_STATE(state, path, sample); +} + +ccl_device_inline void shadow_path_state_rng_load(ConstIntegratorShadowState state, + ccl_private RNGState *rng_state) +{ + rng_state->rng_hash = INTEGRATOR_STATE(state, shadow_path, rng_hash); + rng_state->rng_offset = INTEGRATOR_STATE(state, shadow_path, rng_offset); + rng_state->sample = INTEGRATOR_STATE(state, shadow_path, sample); +} + +ccl_device_inline float path_state_rng_1D(KernelGlobals kg, + ccl_private const RNGState *rng_state, + int dimension) +{ + return path_rng_1D( + kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension); +} + +ccl_device_inline void path_state_rng_2D(KernelGlobals kg, + ccl_private const RNGState *rng_state, + int dimension, + ccl_private float *fx, + ccl_private float *fy) +{ + path_rng_2D( + kg, rng_state->rng_hash, rng_state->sample, rng_state->rng_offset + dimension, fx, fy); +} + +ccl_device_inline float path_state_rng_1D_hash(KernelGlobals kg, + ccl_private const RNGState *rng_state, + uint hash) +{ + /* Use a hash instead of dimension, this is not great but avoids adding + * more dimensions to each bounce which reduces quality of dimensions we + * are already using. */ + return path_rng_1D( + kg, cmj_hash_simple(rng_state->rng_hash, hash), rng_state->sample, rng_state->rng_offset); +} + +ccl_device_inline float path_branched_rng_1D(KernelGlobals kg, + ccl_private const RNGState *rng_state, + int branch, + int num_branches, + int dimension) +{ + return path_rng_1D(kg, + rng_state->rng_hash, + rng_state->sample * num_branches + branch, + rng_state->rng_offset + dimension); +} + +ccl_device_inline void path_branched_rng_2D(KernelGlobals kg, + ccl_private const RNGState *rng_state, + int branch, + int num_branches, + int dimension, + ccl_private float *fx, + ccl_private float *fy) +{ + path_rng_2D(kg, + rng_state->rng_hash, + rng_state->sample * num_branches + branch, + rng_state->rng_offset + dimension, + fx, + fy); +} + +/* Utility functions to get light termination value, + * since it might not be needed in many cases. + */ +ccl_device_inline float path_state_rng_light_termination(KernelGlobals kg, + ccl_private const RNGState *state) +{ + if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { + return path_state_rng_1D(kg, state, PRNG_LIGHT_TERMINATE); + } + return 0.0f; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shade_background.h b/intern/cycles/kernel/integrator/shade_background.h new file mode 100644 index 00000000000..71a590749bd --- /dev/null +++ b/intern/cycles/kernel/integrator/shade_background.h @@ -0,0 +1,219 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/film/accumulate.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/light/light.h" +#include "kernel/light/sample.h" +#include "kernel/sample/mis.h" + +CCL_NAMESPACE_BEGIN + +ccl_device float3 integrator_eval_background_shader(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ +#ifdef __BACKGROUND__ + const int shader = kernel_data.background.surface_shader; + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + /* Use visibility flag to skip lights. */ + if (shader & SHADER_EXCLUDE_ANY) { + if (((shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || + ((shader & SHADER_EXCLUDE_GLOSSY) && ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == + (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || + ((shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || + ((shader & SHADER_EXCLUDE_CAMERA) && (path_flag & PATH_RAY_CAMERA)) || + ((shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) + return zero_float3(); + } + + /* Use fast constant background color if available. */ + float3 L = zero_float3(); + if (!shader_constant_emission_eval(kg, shader, &L)) { + /* Evaluate background shader. */ + + /* TODO: does aliasing like this break automatic SoA in CUDA? + * Should we instead store closures separate from ShaderData? */ + ShaderDataTinyStorage emission_sd_storage; + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + + PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); + shader_setup_from_background(kg, + emission_sd, + INTEGRATOR_STATE(state, ray, P), + INTEGRATOR_STATE(state, ray, D), + INTEGRATOR_STATE(state, ray, time)); + + PROFILING_SHADER(emission_sd->object, emission_sd->shader); + PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); + shader_eval_surface( + kg, state, emission_sd, render_buffer, path_flag | PATH_RAY_EMISSION); + + L = shader_background_eval(emission_sd); + } + + /* Background MIS weights. */ +# ifdef __BACKGROUND_MIS__ + /* Check if background light exists or if we should skip pdf. */ + if (!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_MIS_SKIP) && + kernel_data.background.use_mis) { + const float3 ray_P = INTEGRATOR_STATE(state, ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); + + /* multiple importance sampling, get background light pdf for ray + * direction, and compute weight with respect to BSDF pdf */ + const float pdf = background_light_pdf(kg, ray_P - ray_D * mis_ray_t, ray_D); + const float mis_weight = power_heuristic(mis_ray_pdf, pdf); + + L *= mis_weight; + } +# endif + + return L; +#else + return make_float3(0.8f, 0.8f, 0.8f); +#endif +} + +ccl_device_inline void integrate_background(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + /* Accumulate transparency for transparent background. We can skip background + * shader evaluation unless a background pass is used. */ + bool eval_background = true; + float transparent = 0.0f; + + const bool is_transparent_background_ray = kernel_data.background.transparent && + (INTEGRATOR_STATE(state, path, flag) & + PATH_RAY_TRANSPARENT_BACKGROUND); + + if (is_transparent_background_ray) { + transparent = average(INTEGRATOR_STATE(state, path, throughput)); + +#ifdef __PASSES__ + eval_background = (kernel_data.film.light_pass_flag & PASSMASK(BACKGROUND)); +#else + eval_background = false; +#endif + } + + /* Evaluate background shader. */ + float3 L = (eval_background) ? integrator_eval_background_shader(kg, state, render_buffer) : + zero_float3(); + + /* When using the ao bounces approximation, adjust background + * shader intensity with ao factor. */ + if (path_state_ao_bounce(kg, state)) { + L *= kernel_data.integrator.ao_bounces_factor; + } + + /* Write to render buffer. */ + kernel_accum_background(kg, state, L, transparent, is_transparent_background_ray, render_buffer); +} + +ccl_device_inline void integrate_distant_lights(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float ray_time = INTEGRATOR_STATE(state, ray, time); + LightSample ls ccl_optional_struct_init; + for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { + if (light_sample_from_distant_ray(kg, ray_D, lamp, &ls)) { + /* Use visibility flag to skip lights. */ +#ifdef __PASSES__ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if (ls.shader & SHADER_EXCLUDE_ANY) { + if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || + ((ls.shader & SHADER_EXCLUDE_GLOSSY) && + ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == + (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || + ((ls.shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || + ((ls.shader & SHADER_EXCLUDE_CAMERA) && (path_flag & PATH_RAY_CAMERA)) || + ((ls.shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) + return; + } +#endif + + /* Evaluate light shader. */ + /* TODO: does aliasing like this break automatic SoA in CUDA? */ + ShaderDataTinyStorage emission_sd_storage; + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); + if (is_zero(light_eval)) { + return; + } + + /* MIS weighting. */ + if (!(path_flag & PATH_RAY_MIS_SKIP)) { + /* multiple importance sampling, get regular light pdf, + * and compute weight with respect to BSDF pdf */ + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + const float mis_weight = power_heuristic(mis_ray_pdf, ls.pdf); + light_eval *= mis_weight; + } + + /* Write to render buffer. */ + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_accum_emission(kg, state, throughput, light_eval, render_buffer); + } + } +} + +ccl_device void integrator_shade_background(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_LIGHT_SETUP); + + /* TODO: unify these in a single loop to only have a single shader evaluation call. */ + integrate_distant_lights(kg, state, render_buffer); + integrate_background(kg, state, render_buffer); + +#ifdef __SHADOW_CATCHER__ + if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SHADOW_CATCHER_BACKGROUND) { + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SHADOW_CATCHER_BACKGROUND; + + const int isect_prim = INTEGRATOR_STATE(state, isect, prim); + const int isect_type = INTEGRATOR_STATE(state, isect, type); + const int shader = intersection_get_shader_from_isect_prim(kg, isect_prim, isect_type); + const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; + + if (shader_flags & SD_HAS_RAYTRACE) { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, + shader); + } + else { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, + shader); + } + return; + } +#endif + + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shade_light.h b/intern/cycles/kernel/integrator/shade_light.h new file mode 100644 index 00000000000..7dad3b4e49d --- /dev/null +++ b/intern/cycles/kernel/integrator/shade_light.h @@ -0,0 +1,128 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/film/accumulate.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/light/light.h" +#include "kernel/light/sample.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline void integrate_light(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + /* Setup light sample. */ + Intersection isect ccl_optional_struct_init; + integrator_state_read_isect(kg, state, &isect); + + float3 ray_P = INTEGRATOR_STATE(state, ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, ray, D); + const float ray_time = INTEGRATOR_STATE(state, ray, time); + + /* Advance ray beyond light. */ + /* TODO: can we make this more numerically robust to avoid reintersecting the + * same light in some cases? */ + const float3 new_ray_P = ray_offset(ray_P + ray_D * isect.t, ray_D); + INTEGRATOR_STATE_WRITE(state, ray, P) = new_ray_P; + INTEGRATOR_STATE_WRITE(state, ray, t) -= isect.t; + + /* Set position to where the BSDF was sampled, for correct MIS PDF. */ + const float mis_ray_t = INTEGRATOR_STATE(state, path, mis_ray_t); + ray_P -= ray_D * mis_ray_t; + isect.t += mis_ray_t; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = mis_ray_t + isect.t; + + LightSample ls ccl_optional_struct_init; + const bool use_light_sample = light_sample_from_intersection(kg, &isect, ray_P, ray_D, &ls); + + if (!use_light_sample) { + return; + } + + /* Use visibility flag to skip lights. */ +#ifdef __PASSES__ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if (ls.shader & SHADER_EXCLUDE_ANY) { + if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (path_flag & PATH_RAY_DIFFUSE)) || + ((ls.shader & SHADER_EXCLUDE_GLOSSY) && + ((path_flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == + (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || + ((ls.shader & SHADER_EXCLUDE_TRANSMIT) && (path_flag & PATH_RAY_TRANSMIT)) || + ((ls.shader & SHADER_EXCLUDE_SCATTER) && (path_flag & PATH_RAY_VOLUME_SCATTER))) + return; + } +#endif + + /* Evaluate light shader. */ + /* TODO: does aliasing like this break automatic SoA in CUDA? */ + ShaderDataTinyStorage emission_sd_storage; + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, ray_time); + if (is_zero(light_eval)) { + return; + } + + /* MIS weighting. */ + if (!(path_flag & PATH_RAY_MIS_SKIP)) { + /* multiple importance sampling, get regular light pdf, + * and compute weight with respect to BSDF pdf */ + const float mis_ray_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + const float mis_weight = power_heuristic(mis_ray_pdf, ls.pdf); + light_eval *= mis_weight; + } + + /* Write to render buffer. */ + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_accum_emission(kg, state, throughput, light_eval, render_buffer); +} + +ccl_device void integrator_shade_light(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_LIGHT_SETUP); + + integrate_light(kg, state, render_buffer); + + /* TODO: we could get stuck in an infinite loop if there are precision issues + * and the same light is hit again. + * + * As a workaround count this as a transparent bounce. It makes some sense + * to interpret lights as transparent surfaces (and support making them opaque), + * but this needs to be revisited. */ + uint32_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce) + 1; + INTEGRATOR_STATE_WRITE(state, path, transparent_bounce) = transparent_bounce; + + if (transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + return; + } + else { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + return; + } + + /* TODO: in some cases we could continue directly to SHADE_BACKGROUND, but + * probably that optimization is probably not practical if we add lights to + * scene geometry. */ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shade_shadow.h b/intern/cycles/kernel/integrator/shade_shadow.h new file mode 100644 index 00000000000..1de890aae29 --- /dev/null +++ b/intern/cycles/kernel/integrator/shade_shadow.h @@ -0,0 +1,189 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/shade_volume.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/volume_stack.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline bool shadow_intersections_has_remaining(const uint num_hits) +{ + return num_hits >= INTEGRATOR_SHADOW_ISECT_SIZE; +} + +#ifdef __TRANSPARENT_SHADOWS__ +ccl_device_inline float3 integrate_transparent_surface_shadow(KernelGlobals kg, + IntegratorShadowState state, + const int hit) +{ + PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SURFACE); + + /* TODO: does aliasing like this break automatic SoA in CUDA? + * Should we instead store closures separate from ShaderData? + * + * TODO: is it better to declare this outside the loop or keep it local + * so the compiler can see there is no dependency between iterations? */ + ShaderDataTinyStorage shadow_sd_storage; + ccl_private ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); + + /* Setup shader data at surface. */ + Intersection isect ccl_optional_struct_init; + integrator_state_read_shadow_isect(state, &isect, hit); + + Ray ray ccl_optional_struct_init; + integrator_state_read_shadow_ray(kg, state, &ray); + + shader_setup_from_ray(kg, shadow_sd, &ray, &isect); + + /* Evaluate shader. */ + if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) { + shader_eval_surface( + kg, state, shadow_sd, NULL, PATH_RAY_SHADOW); + } + +# ifdef __VOLUME__ + /* Exit/enter volume. */ + shadow_volume_stack_enter_exit(kg, state, shadow_sd); +# endif + + /* Compute transparency from closures. */ + return shader_bsdf_transparency(kg, shadow_sd); +} + +# ifdef __VOLUME__ +ccl_device_inline void integrate_transparent_volume_shadow(KernelGlobals kg, + IntegratorShadowState state, + const int hit, + const int num_recorded_hits, + ccl_private float3 *ccl_restrict + throughput) +{ + PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_VOLUME); + + /* TODO: deduplicate with surface, or does it not matter for memory usage? */ + ShaderDataTinyStorage shadow_sd_storage; + ccl_private ShaderData *shadow_sd = AS_SHADER_DATA(&shadow_sd_storage); + + /* Setup shader data. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_shadow_ray(kg, state, &ray); + + /* Modify ray position and length to match current segment. */ + const float start_t = (hit == 0) ? 0.0f : + INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit - 1, t); + const float end_t = (hit < num_recorded_hits) ? + INTEGRATOR_STATE_ARRAY(state, shadow_isect, hit, t) : + ray.t; + ray.P += start_t * ray.D; + ray.t = end_t - start_t; + + shader_setup_from_volume(kg, shadow_sd, &ray); + + const float step_size = volume_stack_step_size( + kg, [=](const int i) { return integrator_state_read_shadow_volume_stack(state, i); }); + + volume_shadow_heterogeneous(kg, state, &ray, shadow_sd, throughput, step_size); +} +# endif + +ccl_device_inline bool integrate_transparent_shadow(KernelGlobals kg, + IntegratorShadowState state, + const uint num_hits) +{ + /* Accumulate shadow for transparent surfaces. */ + const uint num_recorded_hits = min(num_hits, INTEGRATOR_SHADOW_ISECT_SIZE); + + for (uint hit = 0; hit < num_recorded_hits + 1; hit++) { + /* Volume shaders. */ + if (hit < num_recorded_hits || !shadow_intersections_has_remaining(num_hits)) { +# ifdef __VOLUME__ + if (!integrator_state_shadow_volume_stack_is_empty(kg, state)) { + float3 throughput = INTEGRATOR_STATE(state, shadow_path, throughput); + integrate_transparent_volume_shadow(kg, state, hit, num_recorded_hits, &throughput); + if (is_zero(throughput)) { + return true; + } + + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput; + } +# endif + } + + /* Surface shaders. */ + if (hit < num_recorded_hits) { + const float3 shadow = integrate_transparent_surface_shadow(kg, state, hit); + const float3 throughput = INTEGRATOR_STATE(state, shadow_path, throughput) * shadow; + if (is_zero(throughput)) { + return true; + } + + INTEGRATOR_STATE_WRITE(state, shadow_path, throughput) = throughput; + INTEGRATOR_STATE_WRITE(state, shadow_path, transparent_bounce) += 1; + INTEGRATOR_STATE_WRITE(state, shadow_path, rng_offset) += PRNG_BOUNCE_NUM; + } + + /* Note we do not need to check max_transparent_bounce here, the number + * of intersections is already limited and made opaque in the + * INTERSECT_SHADOW kernel. */ + } + + if (shadow_intersections_has_remaining(num_hits)) { + /* There are more hits that we could not recorded due to memory usage, + * adjust ray to intersect again from the last hit. */ + const float last_hit_t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, num_recorded_hits - 1, t); + const float3 ray_P = INTEGRATOR_STATE(state, shadow_ray, P); + const float3 ray_D = INTEGRATOR_STATE(state, shadow_ray, D); + INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray_offset(ray_P + last_hit_t * ray_D, ray_D); + INTEGRATOR_STATE_WRITE(state, shadow_ray, t) -= last_hit_t; + } + + return false; +} +#endif /* __TRANSPARENT_SHADOWS__ */ + +ccl_device void integrator_shade_shadow(KernelGlobals kg, + IntegratorShadowState state, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_SHADOW_SETUP); + const uint num_hits = INTEGRATOR_STATE(state, shadow_path, num_hits); + +#ifdef __TRANSPARENT_SHADOWS__ + /* Evaluate transparent shadows. */ + const bool opaque = integrate_transparent_shadow(kg, state, num_hits); + if (opaque) { + INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return; + } +#endif + + if (shadow_intersections_has_remaining(num_hits)) { + /* More intersections to find, continue shadow ray. */ + INTEGRATOR_SHADOW_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW); + return; + } + else { + kernel_accum_light(kg, state, render_buffer); + INTEGRATOR_SHADOW_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW); + return; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shade_surface.h b/intern/cycles/kernel/integrator/shade_surface.h new file mode 100644 index 00000000000..cce591eb219 --- /dev/null +++ b/intern/cycles/kernel/integrator/shade_surface.h @@ -0,0 +1,557 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/film/accumulate.h" +#include "kernel/film/passes.h" + +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/subsurface.h" +#include "kernel/integrator/volume_stack.h" + +#include "kernel/light/light.h" +#include "kernel/light/sample.h" + +#include "kernel/sample/mis.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_forceinline void integrate_surface_shader_setup(KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd) +{ + Intersection isect ccl_optional_struct_init; + integrator_state_read_isect(kg, state, &isect); + + Ray ray ccl_optional_struct_init; + integrator_state_read_ray(kg, state, &ray); + + shader_setup_from_ray(kg, sd, &ray, &isect); +} + +#ifdef __HOLDOUT__ +ccl_device_forceinline bool integrate_surface_holdout(KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd, + ccl_global float *ccl_restrict render_buffer) +{ + /* Write holdout transparency to render buffer and stop if fully holdout. */ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && + (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { + const float3 holdout_weight = shader_holdout_apply(kg, sd); + if (kernel_data.background.transparent) { + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const float transparent = average(holdout_weight * throughput); + kernel_accum_holdout(kg, state, path_flag, transparent, render_buffer); + } + if (isequal_float3(holdout_weight, one_float3())) { + return false; + } + } + + return true; +} +#endif /* __HOLDOUT__ */ + +#ifdef __EMISSION__ +ccl_device_forceinline void integrate_surface_emission(KernelGlobals kg, + ConstIntegratorState state, + ccl_private const ShaderData *sd, + ccl_global float *ccl_restrict + render_buffer) +{ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + /* Evaluate emissive closure. */ + float3 L = shader_emissive_eval(sd); + +# ifdef __HAIR__ + if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) && + (sd->type & PRIMITIVE_ALL_TRIANGLE)) +# else + if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) +# endif + { + const float bsdf_pdf = INTEGRATOR_STATE(state, path, mis_ray_pdf); + const float t = sd->ray_length + INTEGRATOR_STATE(state, path, mis_ray_t); + + /* Multiple importance sampling, get triangle light pdf, + * and compute weight with respect to BSDF pdf. */ + float pdf = triangle_light_pdf(kg, sd, t); + float mis_weight = power_heuristic(bsdf_pdf, pdf); + + L *= mis_weight; + } + + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + kernel_accum_emission(kg, state, throughput, L, render_buffer); +} +#endif /* __EMISSION__ */ + +#ifdef __EMISSION__ +/* Path tracing: sample point on light and evaluate light shader, then + * queue shadow ray to be traced. */ +ccl_device_forceinline void integrate_surface_direct_light(KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *sd, + ccl_private const RNGState *rng_state) +{ + /* Test if there is a light or BSDF that needs direct light. */ + if (!(kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL))) { + return; + } + + /* Sample position on a light. */ + LightSample ls ccl_optional_struct_init; + { + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const uint bounce = INTEGRATOR_STATE(state, path, bounce); + float light_u, light_v; + path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); + + if (!light_distribution_sample_from_position( + kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, &ls)) { + return; + } + } + + kernel_assert(ls.pdf != 0.0f); + + /* Evaluate light shader. + * + * TODO: can we reuse sd memory? In theory we can move this after + * integrate_surface_bounce, evaluate the BSDF, and only then evaluate + * the light shader. This could also move to its own kernel, for + * non-constant light sources. */ + ShaderDataTinyStorage emission_sd_storage; + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + const float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, &ls, sd->time); + if (is_zero(light_eval)) { + return; + } + + /* Evaluate BSDF. */ + const bool is_transmission = shader_bsdf_is_transmission(sd, ls.D); + + BsdfEval bsdf_eval ccl_optional_struct_init; + const float bsdf_pdf = shader_bsdf_eval(kg, sd, ls.D, is_transmission, &bsdf_eval, ls.shader); + bsdf_eval_mul3(&bsdf_eval, light_eval / ls.pdf); + + if (ls.shader & SHADER_USE_MIS) { + const float mis_weight = power_heuristic(ls.pdf, bsdf_pdf); + bsdf_eval_mul(&bsdf_eval, mis_weight); + } + + /* Path termination. */ + const float terminate = path_state_rng_light_termination(kg, rng_state); + if (light_sample_terminate(kg, &ls, &bsdf_eval, terminate)) { + return; + } + + /* Create shadow ray. */ + Ray ray ccl_optional_struct_init; + light_sample_to_surface_shadow_ray(kg, sd, &ls, &ray); + const bool is_light = light_sample_is_light(&ls); + + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT( + shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); + + /* Copy volume stack and enter/exit volume. */ + integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); + + if (is_transmission) { +# ifdef __VOLUME__ + shadow_volume_stack_enter_exit(kg, shadow_state, sd); +# endif + } + + /* Write shadow ray and associated state to global memory. */ + integrator_state_write_shadow_ray(kg, shadow_state, &ray); + + /* Copy state from main path to shadow path. */ + const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); + shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; + shadow_flag |= (is_transmission) ? PATH_RAY_TRANSMISSION_PASS : PATH_RAY_REFLECT_PASS; + const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * bsdf_eval_sum(&bsdf_eval); + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + const float3 diffuse_glossy_ratio = (bounce == 0) ? + bsdf_eval_diffuse_glossy_ratio(&bsdf_eval) : + INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + } + + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( + state, path, render_pixel_index); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE( + state, path, rng_offset); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( + state, path, rng_hash); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( + state, path, sample); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_bounce) = INTEGRATOR_STATE( + state, path, diffuse_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, glossy_bounce) = INTEGRATOR_STATE( + state, path, glossy_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE( + state, path, transmission_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput; + + if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = throughput; + } +} +#endif + +/* Path tracing: bounce off or through surface with new direction. */ +ccl_device_forceinline int integrate_surface_bsdf_bssrdf_bounce( + KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *sd, + ccl_private const RNGState *rng_state) +{ + /* Sample BSDF or BSSRDF. */ + if (!(sd->flag & (SD_BSDF | SD_BSSRDF))) { + return LABEL_NONE; + } + + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + ccl_private const ShaderClosure *sc = shader_bsdf_bssrdf_pick(sd, &bsdf_u); + +#ifdef __SUBSURFACE__ + /* BSSRDF closure, we schedule subsurface intersection kernel. */ + if (CLOSURE_IS_BSSRDF(sc->type)) { + return subsurface_bounce(kg, state, sd, sc); + } +#endif + + /* BSDF closure, sample direction. */ + float bsdf_pdf; + BsdfEval bsdf_eval ccl_optional_struct_init; + float3 bsdf_omega_in ccl_optional_struct_init; + differential3 bsdf_domega_in ccl_optional_struct_init; + int label; + + label = shader_bsdf_sample_closure( + kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf); + + if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) { + return LABEL_NONE; + } + + /* Setup ray. Note that clipping works through transparent bounces. */ + INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, + (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); + INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(bsdf_omega_in); + INTEGRATOR_STATE_WRITE(state, ray, t) = (label & LABEL_TRANSPARENT) ? + INTEGRATOR_STATE(state, ray, t) - sd->ray_length : + FLT_MAX; + +#ifdef __RAY_DIFFERENTIALS__ + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(bsdf_domega_in); +#endif + + /* Update throughput. */ + float3 throughput = INTEGRATOR_STATE(state, path, throughput); + throughput *= bsdf_eval_sum(&bsdf_eval) / bsdf_pdf; + INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput; + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + if (INTEGRATOR_STATE(state, path, bounce) == 0) { + INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = bsdf_eval_diffuse_glossy_ratio( + &bsdf_eval); + } + } + + /* Update path state */ + if (label & LABEL_TRANSPARENT) { + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; + } + else { + INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = bsdf_pdf; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( + bsdf_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); + } + + path_state_next(kg, state, label); + return label; +} + +#ifdef __VOLUME__ +ccl_device_forceinline bool integrate_surface_volume_only_bounce(IntegratorState state, + ccl_private ShaderData *sd) +{ + if (!path_state_volume_next(state)) { + return LABEL_NONE; + } + + /* Setup ray position, direction stays unchanged. */ + INTEGRATOR_STATE_WRITE(state, ray, P) = ray_offset(sd->P, -sd->Ng); + + /* Clipping works through transparent. */ + INTEGRATOR_STATE_WRITE(state, ray, t) -= sd->ray_length; + +# ifdef __RAY_DIFFERENTIALS__ + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); +# endif + + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) += sd->ray_length; + + return LABEL_TRANSMIT | LABEL_TRANSPARENT; +} +#endif + +#if defined(__AO__) +ccl_device_forceinline void integrate_surface_ao(KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict + rng_state, + ccl_global float *ccl_restrict render_buffer) +{ + if (!(kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) && + !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_CAMERA)) { + return; + } + + float bsdf_u, bsdf_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); + + float3 ao_N; + const float3 ao_weight = shader_bsdf_ao( + kg, sd, kernel_data.integrator.ao_additive_factor, &ao_N); + + float3 ao_D; + float ao_pdf; + sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); + + if (!(dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f)) { + return; + } + + Ray ray ccl_optional_struct_init; + ray.P = ray_offset(sd->P, sd->Ng); + ray.D = ao_D; + ray.t = kernel_data.integrator.ao_bounces_distance; + ray.time = sd->time; + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); + + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, ao); + + /* Copy volume stack and enter/exit volume. */ + integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); + + /* Write shadow ray and associated state to global memory. */ + integrator_state_write_shadow_ray(kg, shadow_state, &ray); + + /* Copy state from main path to shadow path. */ + const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag) | PATH_RAY_SHADOW_FOR_AO; + const float3 throughput = INTEGRATOR_STATE(state, path, throughput) * shader_bsdf_alpha(kg, sd); + + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( + state, path, render_pixel_index); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE( + state, path, rng_offset); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( + state, path, rng_hash); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( + state, path, sample); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput; + + if (kernel_data.kernel_features & KERNEL_FEATURE_AO_ADDITIVE) { + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = ao_weight; + } +} +#endif /* defined(__AO__) */ + +template +ccl_device bool integrate_surface(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) + +{ + PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_SURFACE_SETUP); + + /* Setup shader data. */ + ShaderData sd; + integrate_surface_shader_setup(kg, state, &sd); + PROFILING_SHADER(sd.object, sd.shader); + + int continue_path_label = 0; + + /* Skip most work for volume bounding surface. */ +#ifdef __VOLUME__ + if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { +#endif + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + +#ifdef __SUBSURFACE__ + /* Can skip shader evaluation for BSSRDF exit point without bump mapping. */ + if (!(path_flag & PATH_RAY_SUBSURFACE) || ((sd.flag & SD_HAS_BSSRDF_BUMP))) +#endif + { + /* Evaluate shader. */ + PROFILING_EVENT(PROFILING_SHADE_SURFACE_EVAL); + shader_eval_surface(kg, state, &sd, render_buffer, path_flag); + + /* Initialize additional RNG for BSDFs. */ + if (sd.flag & SD_BSDF_NEEDS_LCG) { + sd.lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash), + INTEGRATOR_STATE(state, path, rng_offset), + INTEGRATOR_STATE(state, path, sample), + 0xb4bc3953); + } + } + +#ifdef __SUBSURFACE__ + if (path_flag & PATH_RAY_SUBSURFACE) { + /* When coming from inside subsurface scattering, setup a diffuse + * closure to perform lighting at the exit point. */ + subsurface_shader_data_setup(kg, state, &sd, path_flag); + INTEGRATOR_STATE_WRITE(state, path, flag) &= ~PATH_RAY_SUBSURFACE; + } +#endif + + shader_prepare_surface_closures(kg, state, &sd); + +#ifdef __HOLDOUT__ + /* Evaluate holdout. */ + if (!integrate_surface_holdout(kg, state, &sd, render_buffer)) { + return false; + } +#endif + +#ifdef __EMISSION__ + /* Write emission. */ + if (sd.flag & SD_EMISSION) { + integrate_surface_emission(kg, state, &sd, render_buffer); + } +#endif + +#ifdef __PASSES__ + /* Write render passes. */ + PROFILING_EVENT(PROFILING_SHADE_SURFACE_PASSES); + kernel_write_data_passes(kg, state, &sd, render_buffer); +#endif + + /* Load random number state. */ + RNGState rng_state; + path_state_rng_load(state, &rng_state); + + /* Perform path termination. Most paths have already been terminated in + * the intersect_closest kernel, this is just for emission and for dividing + * throughput by the probability at the right moment. + * + * Also ensure we don't do it twice for SSS at both the entry and exit point. */ + if (!(path_flag & PATH_RAY_SUBSURFACE)) { + const float probability = (path_flag & PATH_RAY_TERMINATE_ON_NEXT_SURFACE) ? + 0.0f : + path_state_continuation_probability(kg, state, path_flag); + if (probability == 0.0f) { + return false; + } + else if (probability != 1.0f) { + INTEGRATOR_STATE_WRITE(state, path, throughput) /= probability; + } + } + +#ifdef __DENOISING_FEATURES__ + kernel_write_denoising_features_surface(kg, state, &sd, render_buffer); +#endif + +#ifdef __SHADOW_CATCHER__ + kernel_write_shadow_catcher_bounce_data(kg, state, &sd, render_buffer); +#endif + + /* Direct light. */ + PROFILING_EVENT(PROFILING_SHADE_SURFACE_DIRECT_LIGHT); + integrate_surface_direct_light(kg, state, &sd, &rng_state); + +#if defined(__AO__) + /* Ambient occlusion pass. */ + if (kernel_data.kernel_features & KERNEL_FEATURE_AO) { + PROFILING_EVENT(PROFILING_SHADE_SURFACE_AO); + integrate_surface_ao(kg, state, &sd, &rng_state, render_buffer); + } +#endif + + PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT); + continue_path_label = integrate_surface_bsdf_bssrdf_bounce(kg, state, &sd, &rng_state); +#ifdef __VOLUME__ + } + else { + PROFILING_EVENT(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT); + continue_path_label = integrate_surface_volume_only_bounce(state, &sd); + } + + if (continue_path_label & LABEL_TRANSMIT) { + /* Enter/Exit volume. */ + volume_stack_enter_exit(kg, state, &sd); + } +#endif + + return continue_path_label != 0; +} + +template +ccl_device_forceinline void integrator_shade_surface(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + if (integrate_surface(kg, state, render_buffer)) { + if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SUBSURFACE) { + INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE); + } + else { + kernel_assert(INTEGRATOR_STATE(state, ray, t) != 0.0f); + INTEGRATOR_PATH_NEXT(current_kernel, DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + } + } + else { + INTEGRATOR_PATH_TERMINATE(current_kernel); + } +} + +ccl_device_forceinline void integrator_shade_surface_raytrace( + KernelGlobals kg, IntegratorState state, ccl_global float *ccl_restrict render_buffer) +{ + integrator_shade_surface( + kg, state, render_buffer); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shade_volume.h b/intern/cycles/kernel/integrator/shade_volume.h new file mode 100644 index 00000000000..f455152dcf9 --- /dev/null +++ b/intern/cycles/kernel/integrator/shade_volume.h @@ -0,0 +1,1049 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/film/accumulate.h" +#include "kernel/film/passes.h" + +#include "kernel/integrator/intersect_closest.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/volume_stack.h" + +#include "kernel/light/light.h" +#include "kernel/light/sample.h" + +#include "kernel/sample/mis.h" + +CCL_NAMESPACE_BEGIN + +#ifdef __VOLUME__ + +/* Events for probabilistic scattering. */ + +typedef enum VolumeIntegrateEvent { + VOLUME_PATH_SCATTERED = 0, + VOLUME_PATH_ATTENUATED = 1, + VOLUME_PATH_MISSED = 2 +} VolumeIntegrateEvent; + +typedef struct VolumeIntegrateResult { + /* Throughput and offset for direct light scattering. */ + bool direct_scatter; + float3 direct_throughput; + float direct_t; + ShaderVolumePhases direct_phases; + + /* Throughput and offset for indirect light scattering. */ + bool indirect_scatter; + float3 indirect_throughput; + float indirect_t; + ShaderVolumePhases indirect_phases; +} VolumeIntegrateResult; + +/* Ignore paths that have volume throughput below this value, to avoid unnecessary work + * and precision issues. + * todo: this value could be tweaked or turned into a probability to avoid unnecessary + * work in volumes and subsurface scattering. */ +# define VOLUME_THROUGHPUT_EPSILON 1e-6f + +/* Volume shader properties + * + * extinction coefficient = absorption coefficient + scattering coefficient + * sigma_t = sigma_a + sigma_s */ + +typedef struct VolumeShaderCoefficients { + float3 sigma_t; + float3 sigma_s; + float3 emission; +} VolumeShaderCoefficients; + +/* Evaluate shader to get extinction coefficient at P. */ +ccl_device_inline bool shadow_volume_shader_sample(KernelGlobals kg, + IntegratorShadowState state, + ccl_private ShaderData *ccl_restrict sd, + ccl_private float3 *ccl_restrict extinction) +{ + shader_eval_volume(kg, state, sd, PATH_RAY_SHADOW, [=](const int i) { + return integrator_state_read_shadow_volume_stack(state, i); + }); + + if (!(sd->flag & SD_EXTINCTION)) { + return false; + } + + const float density = object_volume_density(kg, sd->object); + *extinction = sd->closure_transparent_extinction * density; + return true; +} + +/* Evaluate shader to get absorption, scattering and emission at P. */ +ccl_device_inline bool volume_shader_sample(KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *ccl_restrict sd, + ccl_private VolumeShaderCoefficients *coeff) +{ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + shader_eval_volume(kg, state, sd, path_flag, [=](const int i) { + return integrator_state_read_volume_stack(state, i); + }); + + if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) { + return false; + } + + coeff->sigma_s = zero_float3(); + coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_float3(); + coeff->emission = (sd->flag & SD_EMISSION) ? sd->closure_emission_background : zero_float3(); + + if (sd->flag & SD_SCATTER) { + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_VOLUME(sc->type)) { + coeff->sigma_s += sc->weight; + } + } + } + + const float density = object_volume_density(kg, sd->object); + coeff->sigma_s *= density; + coeff->sigma_t *= density; + coeff->emission *= density; + + return true; +} + +ccl_device_forceinline void volume_step_init(KernelGlobals kg, + ccl_private const RNGState *rng_state, + const float object_step_size, + float t, + ccl_private float *step_size, + ccl_private float *step_shade_offset, + ccl_private float *steps_offset, + ccl_private int *max_steps) +{ + if (object_step_size == FLT_MAX) { + /* Homogeneous volume. */ + *step_size = t; + *step_shade_offset = 0.0f; + *steps_offset = 1.0f; + *max_steps = 1; + } + else { + /* Heterogeneous volume. */ + *max_steps = kernel_data.integrator.volume_max_steps; + float step = min(object_step_size, t); + + /* compute exact steps in advance for malloc */ + if (t > *max_steps * step) { + step = t / (float)*max_steps; + } + + *step_size = step; + + /* Perform shading at this offset within a step, to integrate over + * over the entire step segment. */ + *step_shade_offset = path_state_rng_1D_hash(kg, rng_state, 0x1e31d8a4); + + /* Shift starting point of all segment by this random amount to avoid + * banding artifacts from the volume bounding shape. */ + *steps_offset = path_state_rng_1D_hash(kg, rng_state, 0x3d22c7b3); + } +} + +/* Volume Shadows + * + * These functions are used to attenuate shadow rays to lights. Both absorption + * and scattering will block light, represented by the extinction coefficient. */ + +# if 0 +/* homogeneous volume: assume shader evaluation at the starts gives + * the extinction coefficient for the entire line segment */ +ccl_device void volume_shadow_homogeneous(KernelGlobals kg, IntegratorState state, + ccl_private Ray *ccl_restrict ray, + ccl_private ShaderData *ccl_restrict sd, + ccl_global float3 *ccl_restrict throughput) +{ + float3 sigma_t = zero_float3(); + + if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { + *throughput *= volume_color_transmittance(sigma_t, ray->t); + } +} +# endif + +/* heterogeneous volume: integrate stepping through the volume until we + * reach the end, get absorbed entirely, or run out of iterations */ +ccl_device void volume_shadow_heterogeneous(KernelGlobals kg, + IntegratorShadowState state, + ccl_private Ray *ccl_restrict ray, + ccl_private ShaderData *ccl_restrict sd, + ccl_private float3 *ccl_restrict throughput, + const float object_step_size) +{ + /* Load random number state. */ + RNGState rng_state; + shadow_path_state_rng_load(state, &rng_state); + + float3 tp = *throughput; + + /* Prepare for stepping. + * For shadows we do not offset all segments, since the starting point is + * already a random distance inside the volume. It also appears to create + * banding artifacts for unknown reasons. */ + int max_steps; + float step_size, step_shade_offset, unused; + volume_step_init(kg, + &rng_state, + object_step_size, + ray->t, + &step_size, + &step_shade_offset, + &unused, + &max_steps); + const float steps_offset = 1.0f; + + /* compute extinction at the start */ + float t = 0.0f; + + float3 sum = zero_float3(); + + for (int i = 0; i < max_steps; i++) { + /* advance to new position */ + float new_t = min(ray->t, (i + steps_offset) * step_size); + float dt = new_t - t; + + float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); + float3 sigma_t = zero_float3(); + + /* compute attenuation over segment */ + sd->P = new_P; + if (shadow_volume_shader_sample(kg, state, sd, &sigma_t)) { + /* Compute `expf()` only for every Nth step, to save some calculations + * because `exp(a)*exp(b) = exp(a+b)`, also do a quick #VOLUME_THROUGHPUT_EPSILON + * check then. */ + sum += (-sigma_t * dt); + if ((i & 0x07) == 0) { /* TODO: Other interval? */ + tp = *throughput * exp3(sum); + + /* stop if nearly all light is blocked */ + if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON && + tp.z < VOLUME_THROUGHPUT_EPSILON) + break; + } + } + + /* stop if at the end of the volume */ + t = new_t; + if (t == ray->t) { + /* Update throughput in case we haven't done it above */ + tp = *throughput * exp3(sum); + break; + } + } + + *throughput = tp; +} + +/* Equi-angular sampling as in: + * "Importance Sampling Techniques for Path Tracing in Participating Media" */ + +ccl_device float volume_equiangular_sample(ccl_private const Ray *ccl_restrict ray, + const float3 light_P, + const float xi, + ccl_private float *pdf) +{ + const float t = ray->t; + const float delta = dot((light_P - ray->P), ray->D); + const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); + if (UNLIKELY(D == 0.0f)) { + *pdf = 0.0f; + return 0.0f; + } + const float theta_a = -atan2f(delta, D); + const float theta_b = atan2f(t - delta, D); + const float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a); + if (UNLIKELY(theta_b == theta_a)) { + *pdf = 0.0f; + return 0.0f; + } + *pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); + + return min(t, delta + t_); /* min is only for float precision errors */ +} + +ccl_device float volume_equiangular_pdf(ccl_private const Ray *ccl_restrict ray, + const float3 light_P, + const float sample_t) +{ + const float delta = dot((light_P - ray->P), ray->D); + const float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); + if (UNLIKELY(D == 0.0f)) { + return 0.0f; + } + + const float t = ray->t; + const float t_ = sample_t - delta; + + const float theta_a = -atan2f(delta, D); + const float theta_b = atan2f(t - delta, D); + if (UNLIKELY(theta_b == theta_a)) { + return 0.0f; + } + + const float pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); + + return pdf; +} + +ccl_device float volume_equiangular_cdf(ccl_private const Ray *ccl_restrict ray, + const float3 light_P, + const float sample_t) +{ + float delta = dot((light_P - ray->P), ray->D); + float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); + if (UNLIKELY(D == 0.0f)) { + return 0.0f; + } + + const float t = ray->t; + const float t_ = sample_t - delta; + + const float theta_a = -atan2f(delta, D); + const float theta_b = atan2f(t - delta, D); + if (UNLIKELY(theta_b == theta_a)) { + return 0.0f; + } + + const float theta_sample = atan2f(t_, D); + const float cdf = (theta_sample - theta_a) / (theta_b - theta_a); + + return cdf; +} + +/* Distance sampling */ + +ccl_device float volume_distance_sample(float max_t, + float3 sigma_t, + int channel, + float xi, + ccl_private float3 *transmittance, + ccl_private float3 *pdf) +{ + /* xi is [0, 1[ so log(0) should never happen, division by zero is + * avoided because sample_sigma_t > 0 when SD_SCATTER is set */ + float sample_sigma_t = volume_channel_get(sigma_t, channel); + float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); + float sample_transmittance = volume_channel_get(full_transmittance, channel); + + float sample_t = min(max_t, -logf(1.0f - xi * (1.0f - sample_transmittance)) / sample_sigma_t); + + *transmittance = volume_color_transmittance(sigma_t, sample_t); + *pdf = safe_divide_color(sigma_t * *transmittance, one_float3() - full_transmittance); + + /* todo: optimization: when taken together with hit/miss decision, + * the full_transmittance cancels out drops out and xi does not + * need to be remapped */ + + return sample_t; +} + +ccl_device float3 volume_distance_pdf(float max_t, float3 sigma_t, float sample_t) +{ + float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); + float3 transmittance = volume_color_transmittance(sigma_t, sample_t); + + return safe_divide_color(sigma_t * transmittance, one_float3() - full_transmittance); +} + +/* Emission */ + +ccl_device float3 volume_emission_integrate(ccl_private VolumeShaderCoefficients *coeff, + int closure_flag, + float3 transmittance, + float t) +{ + /* integral E * exp(-sigma_t * t) from 0 to t = E * (1 - exp(-sigma_t * t))/sigma_t + * this goes to E * t as sigma_t goes to zero + * + * todo: we should use an epsilon to avoid precision issues near zero sigma_t */ + float3 emission = coeff->emission; + + if (closure_flag & SD_EXTINCTION) { + float3 sigma_t = coeff->sigma_t; + + emission.x *= (sigma_t.x > 0.0f) ? (1.0f - transmittance.x) / sigma_t.x : t; + emission.y *= (sigma_t.y > 0.0f) ? (1.0f - transmittance.y) / sigma_t.y : t; + emission.z *= (sigma_t.z > 0.0f) ? (1.0f - transmittance.z) / sigma_t.z : t; + } + else + emission *= t; + + return emission; +} + +/* Volume Integration */ + +typedef struct VolumeIntegrateState { + /* Volume segment extents. */ + float start_t; + float end_t; + + /* If volume is absorption-only up to this point, and no probabilistic + * scattering or termination has been used yet. */ + bool absorption_only; + + /* Random numbers for scattering. */ + float rscatter; + float rphase; + + /* Multiple importance sampling. */ + VolumeSampleMethod direct_sample_method; + bool use_mis; + float distance_pdf; + float equiangular_pdf; +} VolumeIntegrateState; + +ccl_device_forceinline void volume_integrate_step_scattering( + ccl_private const ShaderData *sd, + ccl_private const Ray *ray, + const float3 equiangular_light_P, + ccl_private const VolumeShaderCoefficients &ccl_restrict coeff, + const float3 transmittance, + ccl_private VolumeIntegrateState &ccl_restrict vstate, + ccl_private VolumeIntegrateResult &ccl_restrict result) +{ + /* Pick random color channel, we use the Veach one-sample + * model with balance heuristic for the channels. */ + const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); + float3 channel_pdf; + const int channel = volume_sample_channel( + albedo, result.indirect_throughput, vstate.rphase, &channel_pdf); + + /* Equiangular sampling for direct lighting. */ + if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR && !result.direct_scatter) { + if (result.direct_t >= vstate.start_t && result.direct_t <= vstate.end_t) { + const float new_dt = result.direct_t - vstate.start_t; + const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); + + result.direct_scatter = true; + result.direct_throughput *= coeff.sigma_s * new_transmittance / vstate.equiangular_pdf; + shader_copy_volume_phases(&result.direct_phases, sd); + + /* Multiple importance sampling. */ + if (vstate.use_mis) { + const float distance_pdf = vstate.distance_pdf * + dot(channel_pdf, coeff.sigma_t * new_transmittance); + const float mis_weight = 2.0f * power_heuristic(vstate.equiangular_pdf, distance_pdf); + result.direct_throughput *= mis_weight; + } + } + else { + result.direct_throughput *= transmittance; + vstate.distance_pdf *= dot(channel_pdf, transmittance); + } + } + + /* Distance sampling for indirect and optional direct lighting. */ + if (!result.indirect_scatter) { + /* decide if we will scatter or continue */ + const float sample_transmittance = volume_channel_get(transmittance, channel); + + if (1.0f - vstate.rscatter >= sample_transmittance) { + /* compute sampling distance */ + const float sample_sigma_t = volume_channel_get(coeff.sigma_t, channel); + const float new_dt = -logf(1.0f - vstate.rscatter) / sample_sigma_t; + const float new_t = vstate.start_t + new_dt; + + /* transmittance and pdf */ + const float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); + const float distance_pdf = dot(channel_pdf, coeff.sigma_t * new_transmittance); + + /* throughput */ + result.indirect_scatter = true; + result.indirect_t = new_t; + result.indirect_throughput *= coeff.sigma_s * new_transmittance / distance_pdf; + shader_copy_volume_phases(&result.indirect_phases, sd); + + if (vstate.direct_sample_method != VOLUME_SAMPLE_EQUIANGULAR) { + /* If using distance sampling for direct light, just copy parameters + * of indirect light since we scatter at the same point then. */ + result.direct_scatter = true; + result.direct_t = result.indirect_t; + result.direct_throughput = result.indirect_throughput; + shader_copy_volume_phases(&result.direct_phases, sd); + + /* Multiple importance sampling. */ + if (vstate.use_mis) { + const float equiangular_pdf = volume_equiangular_pdf(ray, equiangular_light_P, new_t); + const float mis_weight = power_heuristic(vstate.distance_pdf * distance_pdf, + equiangular_pdf); + result.direct_throughput *= 2.0f * mis_weight; + } + } + } + else { + /* throughput */ + const float pdf = dot(channel_pdf, transmittance); + result.indirect_throughput *= transmittance / pdf; + if (vstate.direct_sample_method != VOLUME_SAMPLE_EQUIANGULAR) { + vstate.distance_pdf *= pdf; + } + + /* remap rscatter so we can reuse it and keep thing stratified */ + vstate.rscatter = 1.0f - (1.0f - vstate.rscatter) / sample_transmittance; + } + } +} + +/* heterogeneous volume distance sampling: integrate stepping through the + * volume until we reach the end, get absorbed entirely, or run out of + * iterations. this does probabilistically scatter or get transmitted through + * for path tracing where we don't want to branch. */ +ccl_device_forceinline void volume_integrate_heterogeneous( + KernelGlobals kg, + IntegratorState state, + ccl_private Ray *ccl_restrict ray, + ccl_private ShaderData *ccl_restrict sd, + ccl_private const RNGState *rng_state, + ccl_global float *ccl_restrict render_buffer, + const float object_step_size, + const VolumeSampleMethod direct_sample_method, + const float3 equiangular_light_P, + ccl_private VolumeIntegrateResult &result) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INTEGRATE); + + /* Prepare for stepping. + * Using a different step offset for the first step avoids banding artifacts. */ + int max_steps; + float step_size, step_shade_offset, steps_offset; + volume_step_init(kg, + rng_state, + object_step_size, + ray->t, + &step_size, + &step_shade_offset, + &steps_offset, + &max_steps); + + /* Initialize volume integration state. */ + VolumeIntegrateState vstate ccl_optional_struct_init; + vstate.start_t = 0.0f; + vstate.end_t = 0.0f; + vstate.absorption_only = true; + vstate.rscatter = path_state_rng_1D(kg, rng_state, PRNG_SCATTER_DISTANCE); + vstate.rphase = path_state_rng_1D(kg, rng_state, PRNG_PHASE_CHANNEL); + + /* Multiple importance sampling: pick between equiangular and distance sampling strategy. */ + vstate.direct_sample_method = direct_sample_method; + vstate.use_mis = (direct_sample_method == VOLUME_SAMPLE_MIS); + if (vstate.use_mis) { + if (vstate.rscatter < 0.5f) { + vstate.rscatter *= 2.0f; + vstate.direct_sample_method = VOLUME_SAMPLE_DISTANCE; + } + else { + vstate.rscatter = (vstate.rscatter - 0.5f) * 2.0f; + vstate.direct_sample_method = VOLUME_SAMPLE_EQUIANGULAR; + } + } + vstate.equiangular_pdf = 0.0f; + vstate.distance_pdf = 1.0f; + + /* Initialize volume integration result. */ + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + result.direct_throughput = throughput; + result.indirect_throughput = throughput; + + /* Equiangular sampling: compute distance and PDF in advance. */ + if (vstate.direct_sample_method == VOLUME_SAMPLE_EQUIANGULAR) { + result.direct_t = volume_equiangular_sample( + ray, equiangular_light_P, vstate.rscatter, &vstate.equiangular_pdf); + } + +# ifdef __DENOISING_FEATURES__ + const bool write_denoising_features = (INTEGRATOR_STATE(state, path, flag) & + PATH_RAY_DENOISING_FEATURES); + float3 accum_albedo = zero_float3(); +# endif + float3 accum_emission = zero_float3(); + + for (int i = 0; i < max_steps; i++) { + /* Advance to new position */ + vstate.end_t = min(ray->t, (i + steps_offset) * step_size); + const float shade_t = vstate.start_t + (vstate.end_t - vstate.start_t) * step_shade_offset; + sd->P = ray->P + ray->D * shade_t; + + /* compute segment */ + VolumeShaderCoefficients coeff ccl_optional_struct_init; + if (volume_shader_sample(kg, state, sd, &coeff)) { + const int closure_flag = sd->flag; + + /* Evaluate transmittance over segment. */ + const float dt = (vstate.end_t - vstate.start_t); + const float3 transmittance = (closure_flag & SD_EXTINCTION) ? + volume_color_transmittance(coeff.sigma_t, dt) : + one_float3(); + + /* Emission. */ + if (closure_flag & SD_EMISSION) { + /* Only write emission before indirect light scatter position, since we terminate + * stepping at that point if we have already found a direct light scatter position. */ + if (!result.indirect_scatter) { + const float3 emission = volume_emission_integrate( + &coeff, closure_flag, transmittance, dt); + accum_emission += emission; + } + } + + if (closure_flag & SD_EXTINCTION) { + if ((closure_flag & SD_SCATTER) || !vstate.absorption_only) { +# ifdef __DENOISING_FEATURES__ + /* Accumulate albedo for denoising features. */ + if (write_denoising_features && (closure_flag & SD_SCATTER)) { + const float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); + accum_albedo += result.indirect_throughput * albedo * (one_float3() - transmittance); + } +# endif + + /* Scattering and absorption. */ + volume_integrate_step_scattering( + sd, ray, equiangular_light_P, coeff, transmittance, vstate, result); + } + else { + /* Absorption only. */ + result.indirect_throughput *= transmittance; + result.direct_throughput *= transmittance; + } + + /* Stop if nearly all light blocked. */ + if (!result.indirect_scatter) { + if (max3(result.indirect_throughput) < VOLUME_THROUGHPUT_EPSILON) { + result.indirect_throughput = zero_float3(); + break; + } + } + else if (!result.direct_scatter) { + if (max3(result.direct_throughput) < VOLUME_THROUGHPUT_EPSILON) { + break; + } + } + } + + /* If we have scattering data for both direct and indirect, we're done. */ + if (result.direct_scatter && result.indirect_scatter) { + break; + } + } + + /* Stop if at the end of the volume. */ + vstate.start_t = vstate.end_t; + if (vstate.start_t == ray->t) { + break; + } + } + + /* Write accumulated emission. */ + if (!is_zero(accum_emission)) { + kernel_accum_emission(kg, state, result.indirect_throughput, accum_emission, render_buffer); + } + +# ifdef __DENOISING_FEATURES__ + /* Write denoising features. */ + if (write_denoising_features) { + kernel_write_denoising_features_volume( + kg, state, accum_albedo, result.indirect_scatter, render_buffer); + } +# endif /* __DENOISING_FEATURES__ */ +} + +# ifdef __EMISSION__ +/* Path tracing: sample point on light and evaluate light shader, then + * queue shadow ray to be traced. */ +ccl_device_forceinline bool integrate_volume_sample_light( + KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict rng_state, + ccl_private LightSample *ccl_restrict ls) +{ + /* Test if there is a light or BSDF that needs direct light. */ + if (!kernel_data.integrator.use_direct_light) { + return false; + } + + /* Sample position on a light. */ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const uint bounce = INTEGRATOR_STATE(state, path, bounce); + float light_u, light_v; + path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); + + light_distribution_sample_from_volume_segment( + kg, light_u, light_v, sd->time, sd->P, bounce, path_flag, ls); + + if (ls->shader & SHADER_EXCLUDE_SCATTER) { + return false; + } + + return true; +} + +/* Path tracing: sample point on light and evaluate light shader, then + * queue shadow ray to be traced. */ +ccl_device_forceinline void integrate_volume_direct_light( + KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const RNGState *ccl_restrict rng_state, + const float3 P, + ccl_private const ShaderVolumePhases *ccl_restrict phases, + ccl_private const float3 throughput, + ccl_private LightSample *ccl_restrict ls) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_DIRECT_LIGHT); + + if (!kernel_data.integrator.use_direct_light) { + return; + } + + /* Sample position on the same light again, now from the shading + * point where we scattered. + * + * TODO: decorrelate random numbers and use light_sample_new_position to + * avoid resampling the CDF. */ + { + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const uint bounce = INTEGRATOR_STATE(state, path, bounce); + float light_u, light_v; + path_state_rng_2D(kg, rng_state, PRNG_LIGHT_U, &light_u, &light_v); + + if (!light_distribution_sample_from_position( + kg, light_u, light_v, sd->time, P, bounce, path_flag, ls)) { + return; + } + } + + if (ls->shader & SHADER_EXCLUDE_SCATTER) { + return; + } + + /* Evaluate light shader. + * + * TODO: can we reuse sd memory? In theory we can move this after + * integrate_surface_bounce, evaluate the BSDF, and only then evaluate + * the light shader. This could also move to its own kernel, for + * non-constant light sources. */ + ShaderDataTinyStorage emission_sd_storage; + ccl_private ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); + const float3 light_eval = light_sample_shader_eval(kg, state, emission_sd, ls, sd->time); + if (is_zero(light_eval)) { + return; + } + + /* Evaluate BSDF. */ + BsdfEval phase_eval ccl_optional_struct_init; + const float phase_pdf = shader_volume_phase_eval(kg, sd, phases, ls->D, &phase_eval); + + if (ls->shader & SHADER_USE_MIS) { + float mis_weight = power_heuristic(ls->pdf, phase_pdf); + bsdf_eval_mul(&phase_eval, mis_weight); + } + + bsdf_eval_mul3(&phase_eval, light_eval / ls->pdf); + + /* Path termination. */ + const float terminate = path_state_rng_light_termination(kg, rng_state); + if (light_sample_terminate(kg, ls, &phase_eval, terminate)) { + return; + } + + /* Create shadow ray. */ + Ray ray ccl_optional_struct_init; + light_sample_to_volume_shadow_ray(kg, sd, ls, P, &ray); + const bool is_light = light_sample_is_light(ls); + + /* Branch off shadow kernel. */ + INTEGRATOR_SHADOW_PATH_INIT( + shadow_state, state, DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, shadow); + + /* Write shadow ray and associated state to global memory. */ + integrator_state_write_shadow_ray(kg, shadow_state, &ray); + + /* Copy state from main path to shadow path. */ + const uint16_t bounce = INTEGRATOR_STATE(state, path, bounce); + const uint16_t transparent_bounce = INTEGRATOR_STATE(state, path, transparent_bounce); + uint32_t shadow_flag = INTEGRATOR_STATE(state, path, flag); + shadow_flag |= (is_light) ? PATH_RAY_SHADOW_FOR_LIGHT : 0; + shadow_flag |= PATH_RAY_VOLUME_PASS; + const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval); + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + const float3 diffuse_glossy_ratio = (bounce == 0) ? + one_float3() : + INTEGRATOR_STATE(state, path, diffuse_glossy_ratio); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_glossy_ratio) = diffuse_glossy_ratio; + } + + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, render_pixel_index) = INTEGRATOR_STATE( + state, path, render_pixel_index); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_offset) = INTEGRATOR_STATE( + state, path, rng_offset); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, rng_hash) = INTEGRATOR_STATE( + state, path, rng_hash); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, sample) = INTEGRATOR_STATE( + state, path, sample); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, flag) = shadow_flag; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, bounce) = bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transparent_bounce) = transparent_bounce; + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, diffuse_bounce) = INTEGRATOR_STATE( + state, path, diffuse_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, glossy_bounce) = INTEGRATOR_STATE( + state, path, glossy_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, transmission_bounce) = INTEGRATOR_STATE( + state, path, transmission_bounce); + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, throughput) = throughput_phase; + + if (kernel_data.kernel_features & KERNEL_FEATURE_SHADOW_PASS) { + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, unshadowed_throughput) = throughput; + } + + integrator_state_copy_volume_stack_to_shadow(kg, shadow_state, state); +} +# endif + +/* Path tracing: scatter in new direction using phase function */ +ccl_device_forceinline bool integrate_volume_phase_scatter( + KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *sd, + ccl_private const RNGState *rng_state, + ccl_private const ShaderVolumePhases *phases) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_INDIRECT_LIGHT); + + float phase_u, phase_v; + path_state_rng_2D(kg, rng_state, PRNG_BSDF_U, &phase_u, &phase_v); + + /* Phase closure, sample direction. */ + float phase_pdf; + BsdfEval phase_eval ccl_optional_struct_init; + float3 phase_omega_in ccl_optional_struct_init; + differential3 phase_domega_in ccl_optional_struct_init; + + const int label = shader_volume_phase_sample(kg, + sd, + phases, + phase_u, + phase_v, + &phase_eval, + &phase_omega_in, + &phase_domega_in, + &phase_pdf); + + if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) { + return false; + } + + /* Setup ray. */ + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = normalize(phase_omega_in); + INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; + +# ifdef __RAY_DIFFERENTIALS__ + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_make_compact(phase_domega_in); +# endif + + /* Update throughput. */ + const float3 throughput = INTEGRATOR_STATE(state, path, throughput); + const float3 throughput_phase = throughput * bsdf_eval_sum(&phase_eval) / phase_pdf; + INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput_phase; + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = one_float3(); + } + + /* Update path state */ + INTEGRATOR_STATE_WRITE(state, path, mis_ray_pdf) = phase_pdf; + INTEGRATOR_STATE_WRITE(state, path, mis_ray_t) = 0.0f; + INTEGRATOR_STATE_WRITE(state, path, min_ray_pdf) = fminf( + phase_pdf, INTEGRATOR_STATE(state, path, min_ray_pdf)); + + path_state_next(kg, state, label); + return true; +} + +/* get the volume attenuation and emission over line segment defined by + * ray, with the assumption that there are no surfaces blocking light + * between the endpoints. distance sampling is used to decide if we will + * scatter or not. */ +ccl_device VolumeIntegrateEvent volume_integrate(KernelGlobals kg, + IntegratorState state, + ccl_private Ray *ccl_restrict ray, + ccl_global float *ccl_restrict render_buffer) +{ + ShaderData sd; + shader_setup_from_volume(kg, &sd, ray); + + /* Load random number state. */ + RNGState rng_state; + path_state_rng_load(state, &rng_state); + + /* Sample light ahead of volume stepping, for equiangular sampling. */ + /* TODO: distant lights are ignored now, but could instead use even distribution. */ + LightSample ls ccl_optional_struct_init; + const bool need_light_sample = !(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_TERMINATE); + const bool have_equiangular_sample = need_light_sample && + integrate_volume_sample_light( + kg, state, &sd, &rng_state, &ls) && + (ls.t != FLT_MAX); + + VolumeSampleMethod direct_sample_method = (have_equiangular_sample) ? + volume_stack_sample_method(kg, state) : + VOLUME_SAMPLE_DISTANCE; + + /* Step through volume. */ + const float step_size = volume_stack_step_size( + kg, [=](const int i) { return integrator_state_read_volume_stack(state, i); }); + + /* TODO: expensive to zero closures? */ + VolumeIntegrateResult result = {}; + volume_integrate_heterogeneous(kg, + state, + ray, + &sd, + &rng_state, + render_buffer, + step_size, + direct_sample_method, + ls.P, + result); + + /* Perform path termination. The intersect_closest will have already marked this path + * to be terminated. That will shading evaluating to leave out any scattering closures, + * but emission and absorption are still handled for multiple importance sampling. */ + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + const float probability = (path_flag & PATH_RAY_TERMINATE_IN_NEXT_VOLUME) ? + 0.0f : + path_state_continuation_probability(kg, state, path_flag); + if (probability == 0.0f) { + return VOLUME_PATH_MISSED; + } + + /* Direct light. */ + if (result.direct_scatter) { + const float3 direct_P = ray->P + result.direct_t * ray->D; + result.direct_throughput /= probability; + integrate_volume_direct_light(kg, + state, + &sd, + &rng_state, + direct_P, + &result.direct_phases, + result.direct_throughput, + &ls); + } + + /* Indirect light. + * + * Only divide throughput by probability if we scatter. For the attenuation + * case the next surface will already do this division. */ + if (result.indirect_scatter) { + result.indirect_throughput /= probability; + } + INTEGRATOR_STATE_WRITE(state, path, throughput) = result.indirect_throughput; + + if (result.indirect_scatter) { + sd.P = ray->P + result.indirect_t * ray->D; + + if (integrate_volume_phase_scatter(kg, state, &sd, &rng_state, &result.indirect_phases)) { + return VOLUME_PATH_SCATTERED; + } + else { + return VOLUME_PATH_MISSED; + } + } + else { + return VOLUME_PATH_ATTENUATED; + } +} + +#endif + +ccl_device void integrator_shade_volume(KernelGlobals kg, + IntegratorState state, + ccl_global float *ccl_restrict render_buffer) +{ + PROFILING_INIT(kg, PROFILING_SHADE_VOLUME_SETUP); + +#ifdef __VOLUME__ + /* Setup shader data. */ + Ray ray ccl_optional_struct_init; + integrator_state_read_ray(kg, state, &ray); + + Intersection isect ccl_optional_struct_init; + integrator_state_read_isect(kg, state, &isect); + + /* Set ray length to current segment. */ + ray.t = (isect.prim != PRIM_NONE) ? isect.t : FLT_MAX; + + /* Clean volume stack for background rays. */ + if (isect.prim == PRIM_NONE) { + volume_stack_clean(kg, state); + } + + VolumeIntegrateEvent event = volume_integrate(kg, state, &ray, render_buffer); + + if (event == VOLUME_PATH_SCATTERED) { + /* Queue intersect_closest kernel. */ + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST); + return; + } + else if (event == VOLUME_PATH_MISSED) { + /* End path. */ + INTEGRATOR_PATH_TERMINATE(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME); + return; + } + else { + /* Continue to background, light or surface. */ + if (isect.prim == PRIM_NONE) { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND); + return; + } + else if (isect.type & PRIMITIVE_LAMP) { + INTEGRATOR_PATH_NEXT(DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT); + return; + } + else { + /* Hit a surface, continue with surface kernel unless terminated. */ + const int shader = intersection_get_shader(kg, &isect); + const int flags = kernel_tex_fetch(__shaders, shader).flags; + + integrator_intersect_shader_next_kernel( + kg, state, &isect, shader, flags); + return; + } + } +#endif /* __VOLUME__ */ +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shader_eval.h b/intern/cycles/kernel/integrator/shader_eval.h new file mode 100644 index 00000000000..68f1ef8c118 --- /dev/null +++ b/intern/cycles/kernel/integrator/shader_eval.h @@ -0,0 +1,869 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Functions to evaluate shaders and use the resulting shader closures. */ + +#pragma once + +#include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf.h" +#include "kernel/closure/bsdf_util.h" +#include "kernel/closure/emissive.h" + +#include "kernel/film/accumulate.h" + +#include "kernel/svm/svm.h" + +#ifdef __OSL__ +# include "kernel/osl/shader.h" +#endif + +CCL_NAMESPACE_BEGIN + +/* Merging */ + +#if defined(__VOLUME__) +ccl_device_inline void shader_merge_volume_closures(ccl_private ShaderData *sd) +{ + /* Merge identical closures to save closure space with stacked volumes. */ + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sci = &sd->closure[i]; + + if (sci->type != CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { + continue; + } + + for (int j = i + 1; j < sd->num_closure; j++) { + ccl_private ShaderClosure *scj = &sd->closure[j]; + if (sci->type != scj->type) { + continue; + } + + ccl_private const HenyeyGreensteinVolume *hgi = (ccl_private const HenyeyGreensteinVolume *) + sci; + ccl_private const HenyeyGreensteinVolume *hgj = (ccl_private const HenyeyGreensteinVolume *) + scj; + if (!(hgi->g == hgj->g)) { + continue; + } + + sci->weight += scj->weight; + sci->sample_weight += scj->sample_weight; + + int size = sd->num_closure - (j + 1); + if (size > 0) { + for (int k = 0; k < size; k++) { + scj[k] = scj[k + 1]; + } + } + + sd->num_closure--; + kernel_assert(sd->num_closure >= 0); + j--; + } + } +} + +ccl_device_inline void shader_copy_volume_phases(ccl_private ShaderVolumePhases *ccl_restrict + phases, + ccl_private const ShaderData *ccl_restrict sd) +{ + phases->num_closure = 0; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *from_sc = &sd->closure[i]; + ccl_private const HenyeyGreensteinVolume *from_hg = + (ccl_private const HenyeyGreensteinVolume *)from_sc; + + if (from_sc->type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { + ccl_private ShaderVolumeClosure *to_sc = &phases->closure[phases->num_closure]; + + to_sc->weight = from_sc->weight; + to_sc->sample_weight = from_sc->sample_weight; + to_sc->g = from_hg->g; + phases->num_closure++; + if (phases->num_closure >= MAX_VOLUME_CLOSURE) { + break; + } + } + } +} +#endif /* __VOLUME__ */ + +ccl_device_inline void shader_prepare_surface_closures(KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd) +{ + /* Defensive sampling. + * + * We can likely also do defensive sampling at deeper bounces, particularly + * for cases like a perfect mirror but possibly also others. This will need + * a good heuristic. */ + if (INTEGRATOR_STATE(state, path, bounce) + INTEGRATOR_STATE(state, path, transparent_bounce) == + 0 && + sd->num_closure > 1) { + float sum = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sum += sc->sample_weight; + } + } + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sc->sample_weight = max(sc->sample_weight, 0.125f * sum); + } + } + } + + /* Filter glossy. + * + * Blurring of bsdf after bounces, for rays that have a small likelihood + * of following this particular path (diffuse, rough glossy) */ + if (kernel_data.integrator.filter_glossy != FLT_MAX) { + float blur_pdf = kernel_data.integrator.filter_glossy * + INTEGRATOR_STATE(state, path, min_ray_pdf); + + if (blur_pdf < 1.0f) { + float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF(sc->type)) { + bsdf_blur(kg, sc, blur_roughness); + } + } + } + } +} + +/* BSDF */ + +ccl_device_inline bool shader_bsdf_is_transmission(ccl_private const ShaderData *sd, + const float3 omega_in) +{ + return dot(sd->N, omega_in) < 0.0f; +} + +ccl_device_forceinline bool _shader_bsdf_exclude(ClosureType type, uint light_shader_flags) +{ + if (!(light_shader_flags & SHADER_EXCLUDE_ANY)) { + return false; + } + if (light_shader_flags & SHADER_EXCLUDE_DIFFUSE) { + if (CLOSURE_IS_BSDF_DIFFUSE(type)) { + return true; + } + } + if (light_shader_flags & SHADER_EXCLUDE_GLOSSY) { + if (CLOSURE_IS_BSDF_GLOSSY(type)) { + return true; + } + } + if (light_shader_flags & SHADER_EXCLUDE_TRANSMIT) { + if (CLOSURE_IS_BSDF_TRANSMISSION(type)) { + return true; + } + } + return false; +} + +ccl_device_inline float _shader_bsdf_multi_eval(KernelGlobals kg, + ccl_private ShaderData *sd, + const float3 omega_in, + const bool is_transmission, + ccl_private const ShaderClosure *skip_sc, + ccl_private BsdfEval *result_eval, + float sum_pdf, + float sum_sample_weight, + const uint light_shader_flags) +{ + /* This is the veach one-sample model with balance heuristic, + * some PDF factors drop out when using balance heuristic weighting. */ + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (sc == skip_sc) { + continue; + } + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + if (CLOSURE_IS_BSDF(sc->type) && !_shader_bsdf_exclude(sc->type, light_shader_flags)) { + float bsdf_pdf = 0.0f; + float3 eval = bsdf_eval(kg, sd, sc, omega_in, is_transmission, &bsdf_pdf); + + if (bsdf_pdf != 0.0f) { + const bool is_diffuse = CLOSURE_IS_BSDF_DIFFUSE(sc->type); + bsdf_eval_accum(result_eval, is_diffuse, eval * sc->weight, 1.0f); + sum_pdf += bsdf_pdf * sc->sample_weight; + } + } + + sum_sample_weight += sc->sample_weight; + } + } + + return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; +} + +#ifndef __KERNEL_CUDA__ +ccl_device +#else +ccl_device_inline +#endif + float + shader_bsdf_eval(KernelGlobals kg, + ccl_private ShaderData *sd, + const float3 omega_in, + const bool is_transmission, + ccl_private BsdfEval *bsdf_eval, + const uint light_shader_flags) +{ + bsdf_eval_init(bsdf_eval, false, zero_float3()); + + return _shader_bsdf_multi_eval( + kg, sd, omega_in, is_transmission, NULL, bsdf_eval, 0.0f, 0.0f, light_shader_flags); +} + +/* Randomly sample a BSSRDF or BSDF proportional to ShaderClosure.sample_weight. */ +ccl_device_inline ccl_private const ShaderClosure *shader_bsdf_bssrdf_pick( + ccl_private const ShaderData *ccl_restrict sd, ccl_private float *randu) +{ + int sampled = 0; + + if (sd->num_closure > 1) { + /* Pick a BSDF or based on sample weights. */ + float sum = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sum += sc->sample_weight; + } + } + + float r = (*randu) * sum; + float partial_sum = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + float next_sum = partial_sum + sc->sample_weight; + + if (r < next_sum) { + sampled = i; + + /* Rescale to reuse for direction sample, to better preserve stratification. */ + *randu = (r - partial_sum) / sc->sample_weight; + break; + } + + partial_sum = next_sum; + } + } + } + + return &sd->closure[sampled]; +} + +/* Return weight for picked BSSRDF. */ +ccl_device_inline float3 +shader_bssrdf_sample_weight(ccl_private const ShaderData *ccl_restrict sd, + ccl_private const ShaderClosure *ccl_restrict bssrdf_sc) +{ + float3 weight = bssrdf_sc->weight; + + if (sd->num_closure > 1) { + float sum = 0.0f; + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { + sum += sc->sample_weight; + } + } + weight *= sum / bssrdf_sc->sample_weight; + } + + return weight; +} + +/* Sample direction for picked BSDF, and return evaluation and pdf for all + * BSDFs combined using MIS. */ +ccl_device int shader_bsdf_sample_closure(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc, + float randu, + float randv, + ccl_private BsdfEval *bsdf_eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) +{ + /* BSSRDF should already have been handled elsewhere. */ + kernel_assert(CLOSURE_IS_BSDF(sc->type)); + + int label; + float3 eval = zero_float3(); + + *pdf = 0.0f; + label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); + + if (*pdf != 0.0f) { + const bool is_diffuse = CLOSURE_IS_BSDF_DIFFUSE(sc->type); + bsdf_eval_init(bsdf_eval, is_diffuse, eval * sc->weight); + + if (sd->num_closure > 1) { + const bool is_transmission = shader_bsdf_is_transmission(sd, *omega_in); + float sweight = sc->sample_weight; + *pdf = _shader_bsdf_multi_eval( + kg, sd, *omega_in, is_transmission, sc, bsdf_eval, *pdf * sweight, sweight, 0); + } + } + + return label; +} + +ccl_device float shader_bsdf_average_roughness(ccl_private const ShaderData *sd) +{ + float roughness = 0.0f; + float sum_weight = 0.0f; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF(sc->type)) { + /* sqrt once to undo the squaring from multiplying roughness on the + * two axes, and once for the squared roughness convention. */ + float weight = fabsf(average(sc->weight)); + roughness += weight * sqrtf(safe_sqrtf(bsdf_get_roughness_squared(sc))); + sum_weight += weight; + } + } + + return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; +} + +ccl_device float3 shader_bsdf_transparency(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + if (sd->flag & SD_HAS_ONLY_VOLUME) { + return one_float3(); + } + else if (sd->flag & SD_TRANSPARENT) { + return sd->closure_transparent_extinction; + } + else { + return zero_float3(); + } +} + +ccl_device void shader_bsdf_disable_transparency(KernelGlobals kg, ccl_private ShaderData *sd) +{ + if (sd->flag & SD_TRANSPARENT) { + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + + if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { + sc->sample_weight = 0.0f; + sc->weight = zero_float3(); + } + } + + sd->flag &= ~SD_TRANSPARENT; + } +} + +ccl_device float3 shader_bsdf_alpha(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + float3 alpha = one_float3() - shader_bsdf_transparency(kg, sd); + + alpha = max(alpha, zero_float3()); + alpha = min(alpha, one_float3()); + + return alpha; +} + +ccl_device float3 shader_bsdf_diffuse(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + float3 eval = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type)) + eval += sc->weight; + } + + return eval; +} + +ccl_device float3 shader_bsdf_glossy(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + float3 eval = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) + eval += sc->weight; + } + + return eval; +} + +ccl_device float3 shader_bsdf_transmission(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + float3 eval = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) + eval += sc->weight; + } + + return eval; +} + +ccl_device float3 shader_bsdf_average_normal(KernelGlobals kg, ccl_private const ShaderData *sd) +{ + float3 N = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) + N += sc->N * fabsf(average(sc->weight)); + } + + return (is_zero(N)) ? sd->N : normalize(N); +} + +ccl_device float3 shader_bsdf_ao(KernelGlobals kg, + ccl_private const ShaderData *sd, + const float ao_factor, + ccl_private float3 *N_) +{ + float3 eval = zero_float3(); + float3 N = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { + ccl_private const DiffuseBsdf *bsdf = (ccl_private const DiffuseBsdf *)sc; + eval += sc->weight * ao_factor; + N += bsdf->N * fabsf(average(sc->weight)); + } + } + + *N_ = (is_zero(N)) ? sd->N : normalize(N); + return eval; +} + +#ifdef __SUBSURFACE__ +ccl_device float3 shader_bssrdf_normal(ccl_private const ShaderData *sd) +{ + float3 N = zero_float3(); + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + + if (CLOSURE_IS_BSSRDF(sc->type)) { + ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc; + float avg_weight = fabsf(average(sc->weight)); + + N += bssrdf->N * avg_weight; + } + } + + return (is_zero(N)) ? sd->N : normalize(N); +} +#endif /* __SUBSURFACE__ */ + +/* Constant emission optimization */ + +ccl_device bool shader_constant_emission_eval(KernelGlobals kg, + int shader, + ccl_private float3 *eval) +{ + int shader_index = shader & SHADER_MASK; + int shader_flag = kernel_tex_fetch(__shaders, shader_index).flags; + + if (shader_flag & SD_HAS_CONSTANT_EMISSION) { + *eval = make_float3(kernel_tex_fetch(__shaders, shader_index).constant_emission[0], + kernel_tex_fetch(__shaders, shader_index).constant_emission[1], + kernel_tex_fetch(__shaders, shader_index).constant_emission[2]); + + return true; + } + + return false; +} + +/* Background */ + +ccl_device float3 shader_background_eval(ccl_private const ShaderData *sd) +{ + if (sd->flag & SD_EMISSION) { + return sd->closure_emission_background; + } + else { + return zero_float3(); + } +} + +/* Emission */ + +ccl_device float3 shader_emissive_eval(ccl_private const ShaderData *sd) +{ + if (sd->flag & SD_EMISSION) { + return emissive_simple_eval(sd->Ng, sd->I) * sd->closure_emission_background; + } + else { + return zero_float3(); + } +} + +/* Holdout */ + +ccl_device float3 shader_holdout_apply(KernelGlobals kg, ccl_private ShaderData *sd) +{ + float3 weight = zero_float3(); + + /* For objects marked as holdout, preserve transparency and remove all other + * closures, replacing them with a holdout weight. */ + if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { + if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) { + weight = one_float3() - sd->closure_transparent_extinction; + + for (int i = 0; i < sd->num_closure; i++) { + ccl_private ShaderClosure *sc = &sd->closure[i]; + if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { + sc->type = NBUILTIN_CLOSURES; + } + } + + sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF)); + } + else { + weight = one_float3(); + } + } + else { + for (int i = 0; i < sd->num_closure; i++) { + ccl_private const ShaderClosure *sc = &sd->closure[i]; + if (CLOSURE_IS_HOLDOUT(sc->type)) { + weight += sc->weight; + } + } + } + + return weight; +} + +/* Surface Evaluation */ + +template +ccl_device void shader_eval_surface(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *ccl_restrict sd, + ccl_global float *ccl_restrict buffer, + uint32_t path_flag) +{ + /* If path is being terminated, we are tracing a shadow ray or evaluating + * emission, then we don't need to store closures. The emission and shadow + * shader data also do not have a closure array to save GPU memory. */ + int max_closures; + if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { + max_closures = 0; + } + else { + max_closures = kernel_data.max_closures; + } + + sd->num_closure = 0; + sd->num_closure_left = max_closures; + +#ifdef __OSL__ + if (kg->osl) { + if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { + OSLShader::eval_background(kg, state, sd, path_flag); + } + else { + OSLShader::eval_surface(kg, state, sd, path_flag); + } + } + else +#endif + { +#ifdef __SVM__ + svm_eval_nodes(kg, state, sd, buffer, path_flag); +#else + if (sd->object == OBJECT_NONE) { + sd->closure_emission_background = make_float3(0.8f, 0.8f, 0.8f); + sd->flag |= SD_EMISSION; + } + else { + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), make_float3(0.8f, 0.8f, 0.8f)); + if (bsdf != NULL) { + bsdf->N = sd->N; + sd->flag |= bsdf_diffuse_setup(bsdf); + } + } +#endif + } +} + +/* Volume */ + +#ifdef __VOLUME__ + +ccl_device_inline float _shader_volume_phase_multi_eval( + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, + const float3 omega_in, + int skip_phase, + ccl_private BsdfEval *result_eval, + float sum_pdf, + float sum_sample_weight) +{ + for (int i = 0; i < phases->num_closure; i++) { + if (i == skip_phase) + continue; + + ccl_private const ShaderVolumeClosure *svc = &phases->closure[i]; + float phase_pdf = 0.0f; + float3 eval = volume_phase_eval(sd, svc, omega_in, &phase_pdf); + + if (phase_pdf != 0.0f) { + bsdf_eval_accum(result_eval, false, eval, 1.0f); + sum_pdf += phase_pdf * svc->sample_weight; + } + + sum_sample_weight += svc->sample_weight; + } + + return (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; +} + +ccl_device float shader_volume_phase_eval(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, + const float3 omega_in, + ccl_private BsdfEval *phase_eval) +{ + bsdf_eval_init(phase_eval, false, zero_float3()); + + return _shader_volume_phase_multi_eval(sd, phases, omega_in, -1, phase_eval, 0.0f, 0.0f); +} + +ccl_device int shader_volume_phase_sample(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumePhases *phases, + float randu, + float randv, + ccl_private BsdfEval *phase_eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) +{ + int sampled = 0; + + if (phases->num_closure > 1) { + /* pick a phase closure based on sample weights */ + float sum = 0.0f; + + for (sampled = 0; sampled < phases->num_closure; sampled++) { + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; + sum += svc->sample_weight; + } + + float r = randu * sum; + float partial_sum = 0.0f; + + for (sampled = 0; sampled < phases->num_closure; sampled++) { + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; + float next_sum = partial_sum + svc->sample_weight; + + if (r <= next_sum) { + /* Rescale to reuse for BSDF direction sample. */ + randu = (r - partial_sum) / svc->sample_weight; + break; + } + + partial_sum = next_sum; + } + + if (sampled == phases->num_closure) { + *pdf = 0.0f; + return LABEL_NONE; + } + } + + /* todo: this isn't quite correct, we don't weight anisotropy properly + * depending on color channels, even if this is perhaps not a common case */ + ccl_private const ShaderVolumeClosure *svc = &phases->closure[sampled]; + int label; + float3 eval = zero_float3(); + + *pdf = 0.0f; + label = volume_phase_sample(sd, svc, randu, randv, &eval, omega_in, domega_in, pdf); + + if (*pdf != 0.0f) { + bsdf_eval_init(phase_eval, false, eval); + } + + return label; +} + +ccl_device int shader_phase_sample_closure(KernelGlobals kg, + ccl_private const ShaderData *sd, + ccl_private const ShaderVolumeClosure *sc, + float randu, + float randv, + ccl_private BsdfEval *phase_eval, + ccl_private float3 *omega_in, + ccl_private differential3 *domega_in, + ccl_private float *pdf) +{ + int label; + float3 eval = zero_float3(); + + *pdf = 0.0f; + label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); + + if (*pdf != 0.0f) + bsdf_eval_init(phase_eval, false, eval); + + return label; +} + +/* Volume Evaluation */ + +template +ccl_device_inline void shader_eval_volume(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *ccl_restrict sd, + const uint32_t path_flag, + StackReadOp stack_read) +{ + /* If path is being terminated, we are tracing a shadow ray or evaluating + * emission, then we don't need to store closures. The emission and shadow + * shader data also do not have a closure array to save GPU memory. */ + int max_closures; + if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { + max_closures = 0; + } + else { + max_closures = kernel_data.max_closures; + } + + /* reset closures once at the start, we will be accumulating the closures + * for all volumes in the stack into a single array of closures */ + sd->num_closure = 0; + sd->num_closure_left = max_closures; + sd->flag = 0; + sd->object_flag = 0; + + for (int i = 0;; i++) { + const VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + /* Setup shader-data from stack. it's mostly setup already in + * shader_setup_from_volume, this switching should be quick. */ + sd->object = entry.object; + sd->lamp = LAMP_NONE; + sd->shader = entry.shader; + + sd->flag &= ~SD_SHADER_FLAGS; + sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; + sd->object_flag &= ~SD_OBJECT_FLAGS; + + if (sd->object != OBJECT_NONE) { + sd->object_flag |= kernel_tex_fetch(__object_flag, sd->object); + +# ifdef __OBJECT_MOTION__ + /* todo: this is inefficient for motion blur, we should be + * caching matrices instead of recomputing them each step */ + shader_setup_object_transforms(kg, sd, sd->time); +# endif + } + + /* evaluate shader */ +# ifdef __SVM__ +# ifdef __OSL__ + if (kg->osl) { + OSLShader::eval_volume(kg, state, sd, path_flag); + } + else +# endif + { + svm_eval_nodes( + kg, state, sd, NULL, path_flag); + } +# endif + + /* Merge closures to avoid exceeding number of closures limit. */ + if (!shadow) { + if (i > 0) { + shader_merge_volume_closures(sd); + } + } + } +} + +#endif /* __VOLUME__ */ + +/* Displacement Evaluation */ + +template +ccl_device void shader_eval_displacement(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *sd) +{ + sd->num_closure = 0; + sd->num_closure_left = 0; + + /* this will modify sd->P */ +#ifdef __SVM__ +# ifdef __OSL__ + if (kg->osl) + OSLShader::eval_displacement(kg, state, sd); + else +# endif + { + svm_eval_nodes( + kg, state, sd, NULL, 0); + } +#endif +} + +/* Cryptomatte */ + +ccl_device float shader_cryptomatte_id(KernelGlobals kg, int shader) +{ + return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).cryptomatte_id; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shadow_catcher.h b/intern/cycles/kernel/integrator/shadow_catcher.h new file mode 100644 index 00000000000..7beae235dbc --- /dev/null +++ b/intern/cycles/kernel/integrator/shadow_catcher.h @@ -0,0 +1,120 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/state_util.h" + +CCL_NAMESPACE_BEGIN + +/* Check whether current surface bounce is where path is to be split for the shadow catcher. */ +ccl_device_inline bool kernel_shadow_catcher_is_path_split_bounce(KernelGlobals kg, + IntegratorState state, + const int object_flag) +{ +#ifdef __SHADOW_CATCHER__ + if (!kernel_data.integrator.has_shadow_catcher) { + return false; + } + + /* Check the flag first, avoiding fetches form global memory. */ + if ((object_flag & SD_OBJECT_SHADOW_CATCHER) == 0) { + return false; + } + if (object_flag & SD_OBJECT_HOLDOUT_MASK) { + return false; + } + + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if ((path_flag & PATH_RAY_TRANSPARENT_BACKGROUND) == 0) { + /* Split only on primary rays, secondary bounces are to treat shadow catcher as a regular + * object. */ + return false; + } + + if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) { + return false; + } + + return true; +#else + (void)object_flag; + return false; +#endif +} + +/* Check whether the current path can still split. */ +ccl_device_inline bool kernel_shadow_catcher_path_can_split(KernelGlobals kg, + ConstIntegratorState state) +{ + if (INTEGRATOR_PATH_IS_TERMINATED) { + return false; + } + + const uint32_t path_flag = INTEGRATOR_STATE(state, path, flag); + + if (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) { + /* Shadow catcher was already hit and the state was split. No further split is allowed. */ + return false; + } + + return (path_flag & PATH_RAY_TRANSPARENT_BACKGROUND) != 0; +} + +/* NOTE: Leaves kernel scheduling information untouched. Use INIT semantic for one of the paths + * after this function. */ +ccl_device_inline bool kernel_shadow_catcher_split(KernelGlobals kg, + IntegratorState state, + const int object_flags) +{ +#ifdef __SHADOW_CATCHER__ + + if (!kernel_shadow_catcher_is_path_split_bounce(kg, state, object_flags)) { + return false; + } + + /* The split is to be done. Mark the current state as such, so that it stops contributing to the + * shadow catcher matte pass, but keeps contributing to the combined pass. */ + INTEGRATOR_STATE_WRITE(state, path, flag) |= PATH_RAY_SHADOW_CATCHER_HIT; + + /* Split new state from the current one. This new state will only track contribution of shadow + * catcher objects ignoring non-catcher objects. */ + integrator_state_shadow_catcher_split(kg, state); + + return true; +#else + (void)object_flags; + return false; +#endif +} + +#ifdef __SHADOW_CATCHER__ + +ccl_device_forceinline bool kernel_shadow_catcher_is_matte_path(const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW_CATCHER_HIT) == 0; +} + +ccl_device_forceinline bool kernel_shadow_catcher_is_object_pass(const uint32_t path_flag) +{ + return path_flag & PATH_RAY_SHADOW_CATCHER_PASS; +} + +#endif /* __SHADOW_CATCHER__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/shadow_state_template.h b/intern/cycles/kernel/integrator/shadow_state_template.h new file mode 100644 index 00000000000..da25e58ff1b --- /dev/null +++ b/intern/cycles/kernel/integrator/shadow_state_template.h @@ -0,0 +1,86 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/********************************* Shadow Path State **************************/ + +KERNEL_STRUCT_BEGIN(shadow_path) +/* Index of a pixel within the device render buffer. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, render_pixel_index, KERNEL_FEATURE_PATH_TRACING) +/* Current sample number. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, sample, KERNEL_FEATURE_PATH_TRACING) +/* Random number generator seed. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, rng_hash, KERNEL_FEATURE_PATH_TRACING) +/* Random number dimension offset. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, rng_offset, KERNEL_FEATURE_PATH_TRACING) +/* Current ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transparent ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current diffuse ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, diffuse_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current glossy ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, glossy_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transmission ray bounce depth. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, transmission_bounce, KERNEL_FEATURE_PATH_TRACING) +/* DeviceKernel bit indicating queued kernels. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) +/* enum PathRayFlag */ +KERNEL_STRUCT_MEMBER(shadow_path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) +/* Throughput. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, throughput, KERNEL_FEATURE_PATH_TRACING) +/* Throughput for shadow pass. */ +KERNEL_STRUCT_MEMBER(shadow_path, + float3, + unshadowed_throughput, + KERNEL_FEATURE_SHADOW_PASS | KERNEL_FEATURE_AO_ADDITIVE) +/* Ratio of throughput to distinguish diffuse and glossy render passes. */ +KERNEL_STRUCT_MEMBER(shadow_path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES) +/* Number of intersections found by ray-tracing. */ +KERNEL_STRUCT_MEMBER(shadow_path, uint16_t, num_hits, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(shadow_path) + +/********************************** Shadow Ray *******************************/ + +KERNEL_STRUCT_BEGIN(shadow_ray) +KERNEL_STRUCT_MEMBER(shadow_ray, float3, P, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float3, D, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, time, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(shadow_ray, float, dP, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(shadow_ray) + +/*********************** Shadow Intersection result **************************/ + +/* Result from scene intersection. */ +KERNEL_STRUCT_BEGIN(shadow_isect) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, u, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, float, v, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, prim, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, object, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_isect, int, type, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END_ARRAY(shadow_isect, + INTEGRATOR_SHADOW_ISECT_SIZE_CPU, + INTEGRATOR_SHADOW_ISECT_SIZE_GPU) + +/**************************** Shadow Volume Stack *****************************/ + +KERNEL_STRUCT_BEGIN(shadow_volume_stack) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, object, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_ARRAY_MEMBER(shadow_volume_stack, int, shader, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_END_ARRAY(shadow_volume_stack, + KERNEL_STRUCT_VOLUME_STACK_SIZE, + KERNEL_STRUCT_VOLUME_STACK_SIZE) diff --git a/intern/cycles/kernel/integrator/state.h b/intern/cycles/kernel/integrator/state.h new file mode 100644 index 00000000000..86dac0a65cf --- /dev/null +++ b/intern/cycles/kernel/integrator/state.h @@ -0,0 +1,195 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/* Integrator State + * + * This file defines the data structures that define the state of a path. Any state that is + * preserved and passed between kernel executions is part of this. + * + * The size of this state must be kept as small as possible, to reduce cache misses and keep memory + * usage under control on GPUs that may execute millions of kernels. + * + * Memory may be allocated and passed along in different ways depending on the device. There may + * be a scalar layout, or AoS or SoA layout for batches. The state may be passed along as a pointer + * to every kernel, or the pointer may exist at program scope or in constant memory. To abstract + * these differences between devices and experiment with different layouts, macros are used. + * + * Use IntegratorState to pass a reference to the integrator state for the current path. These are + * defined differently on the CPU and GPU. Use ConstIntegratorState instead of const + * IntegratorState for passing state as read-only, to avoid oddities in typedef behavior. + * + * INTEGRATOR_STATE(state, x, y): read nested struct member x.y of IntegratorState + * INTEGRATOR_STATE_WRITE(state, x, y): write to nested struct member x.y of IntegratorState + * + * INTEGRATOR_STATE_ARRAY(state, x, index, y): read x[index].y + * INTEGRATOR_STATE_ARRAY_WRITE(state, x, index, y): write x[index].y + * + * INTEGRATOR_STATE_NULL: use to pass empty state to other functions. + */ + +#include "kernel/types.h" + +#include "util/types.h" + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Data structures */ + +/* Integrator State + * + * CPU rendering path state with AoS layout. */ +typedef struct IntegratorShadowStateCPU { +#define KERNEL_STRUCT_BEGIN(name) struct { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) type name; +#define KERNEL_STRUCT_ARRAY_MEMBER KERNEL_STRUCT_MEMBER +#define KERNEL_STRUCT_END(name) \ + } \ + name; +#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ + } \ + name[cpu_size]; +#define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE +#include "kernel/integrator/shadow_state_template.h" +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +} IntegratorShadowStateCPU; + +typedef struct IntegratorStateCPU { +#define KERNEL_STRUCT_BEGIN(name) struct { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) type name; +#define KERNEL_STRUCT_ARRAY_MEMBER KERNEL_STRUCT_MEMBER +#define KERNEL_STRUCT_END(name) \ + } \ + name; +#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ + } \ + name[cpu_size]; +#define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE +#include "kernel/integrator/state_template.h" +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE + + IntegratorShadowStateCPU shadow; + IntegratorShadowStateCPU ao; +} IntegratorStateCPU; + +/* Path Queue + * + * Keep track of which kernels are queued to be executed next in the path + * for GPU rendering. */ +typedef struct IntegratorQueueCounter { + int num_queued[DEVICE_KERNEL_INTEGRATOR_NUM]; +} IntegratorQueueCounter; + +/* Integrator State GPU + * + * GPU rendering path state with SoA layout. */ +typedef struct IntegratorStateGPU { +#define KERNEL_STRUCT_BEGIN(name) struct { +#define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) ccl_global type *name; +#define KERNEL_STRUCT_ARRAY_MEMBER KERNEL_STRUCT_MEMBER +#define KERNEL_STRUCT_END(name) \ + } \ + name; +#define KERNEL_STRUCT_END_ARRAY(name, cpu_size, gpu_size) \ + } \ + name[gpu_size]; +#define KERNEL_STRUCT_VOLUME_STACK_SIZE MAX_VOLUME_STACK_SIZE + +#include "kernel/integrator/state_template.h" + +#include "kernel/integrator/shadow_state_template.h" + +#undef KERNEL_STRUCT_BEGIN +#undef KERNEL_STRUCT_MEMBER +#undef KERNEL_STRUCT_ARRAY_MEMBER +#undef KERNEL_STRUCT_END +#undef KERNEL_STRUCT_END_ARRAY +#undef KERNEL_STRUCT_VOLUME_STACK_SIZE + + /* Count number of queued kernels. */ + ccl_global IntegratorQueueCounter *queue_counter; + + /* Count number of kernels queued for specific shaders. */ + ccl_global int *sort_key_counter[DEVICE_KERNEL_INTEGRATOR_NUM]; + + /* Index of shadow path which will be used by a next shadow path. */ + ccl_global int *next_shadow_path_index; + + /* Index of main path which will be used by a next shadow catcher split. */ + ccl_global int *next_main_path_index; +} IntegratorStateGPU; + +/* Abstraction + * + * Macros to access data structures on different devices. + * + * Note that there is a special access function for the shadow catcher state. This access is to + * happen from a kernel which operates on a "main" path. Attempt to use shadow catcher accessors + * from a kernel which operates on a shadow catcher state will cause bad memory access. */ + +#ifdef __KERNEL_CPU__ + +/* Scalar access on CPU. */ + +typedef IntegratorStateCPU *ccl_restrict IntegratorState; +typedef const IntegratorStateCPU *ccl_restrict ConstIntegratorState; +typedef IntegratorShadowStateCPU *ccl_restrict IntegratorShadowState; +typedef const IntegratorShadowStateCPU *ccl_restrict ConstIntegratorShadowState; + +# define INTEGRATOR_STATE_NULL nullptr + +# define INTEGRATOR_STATE(state, nested_struct, member) ((state)->nested_struct.member) +# define INTEGRATOR_STATE_WRITE(state, nested_struct, member) ((state)->nested_struct.member) + +# define INTEGRATOR_STATE_ARRAY(state, nested_struct, array_index, member) \ + ((state)->nested_struct[array_index].member) +# define INTEGRATOR_STATE_ARRAY_WRITE(state, nested_struct, array_index, member) \ + ((state)->nested_struct[array_index].member) + +#else /* __KERNEL_CPU__ */ + +/* Array access on GPU with Structure-of-Arrays. */ + +typedef const int IntegratorState; +typedef const int ConstIntegratorState; +typedef const int IntegratorShadowState; +typedef const int ConstIntegratorShadowState; + +# define INTEGRATOR_STATE_NULL -1 + +# define INTEGRATOR_STATE(state, nested_struct, member) \ + kernel_integrator_state.nested_struct.member[state] +# define INTEGRATOR_STATE_WRITE(state, nested_struct, member) \ + INTEGRATOR_STATE(state, nested_struct, member) + +# define INTEGRATOR_STATE_ARRAY(state, nested_struct, array_index, member) \ + kernel_integrator_state.nested_struct[array_index].member[state] +# define INTEGRATOR_STATE_ARRAY_WRITE(state, nested_struct, array_index, member) \ + INTEGRATOR_STATE_ARRAY(state, nested_struct, array_index, member) + +#endif /* __KERNEL_CPU__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/state_flow.h b/intern/cycles/kernel/integrator/state_flow.h new file mode 100644 index 00000000000..38a2b396847 --- /dev/null +++ b/intern/cycles/kernel/integrator/state_flow.h @@ -0,0 +1,148 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/types.h" +#include "util/atomic.h" + +CCL_NAMESPACE_BEGIN + +/* Control Flow + * + * Utilities for control flow between kernels. The implementation may differ per device + * or even be handled on the host side. To abstract such differences, experiment with + * different implementations and for debugging, this is abstracted using macros. + * + * There is a main path for regular path tracing camera for path tracing. Shadows for next + * event estimation branch off from this into their own path, that may be computed in + * parallel while the main path continues. + * + * Each kernel on the main path must call one of these functions. These may not be called + * multiple times from the same kernel. + * + * INTEGRATOR_PATH_INIT(next_kernel) + * INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) + * INTEGRATOR_PATH_TERMINATE(current_kernel) + * + * For the shadow path similar functions are used, and again each shadow kernel must call + * one of them, and only once. + */ + +#define INTEGRATOR_PATH_IS_TERMINATED (INTEGRATOR_STATE(state, path, queued_kernel) == 0) +#define INTEGRATOR_SHADOW_PATH_IS_TERMINATED \ + (INTEGRATOR_STATE(state, shadow_path, queued_kernel) == 0) + +#ifdef __KERNEL_GPU__ + +# define INTEGRATOR_PATH_INIT(next_kernel) \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; + +# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ + IntegratorShadowState shadow_state = atomic_fetch_and_add_uint32( \ + &kernel_integrator_state.next_shadow_path_index[0], 1); \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; +# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.queue_counter->num_queued[next_kernel], \ + 1); \ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; +# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; + +# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ + { \ + const int key_ = key; \ + atomic_fetch_and_add_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ + 1); \ + } +# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ + { \ + const int key_ = key; \ + atomic_fetch_and_sub_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[current_kernel], 1); \ + atomic_fetch_and_add_uint32( \ + &kernel_integrator_state.queue_counter->num_queued[next_kernel], 1); \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ + INTEGRATOR_STATE_WRITE(state, path, shader_sort_key) = key_; \ + atomic_fetch_and_add_uint32(&kernel_integrator_state.sort_key_counter[next_kernel][key_], \ + 1); \ + } + +#else + +# define INTEGRATOR_PATH_INIT(next_kernel) \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; +# define INTEGRATOR_PATH_INIT_SORTED(next_kernel, key) \ + { \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ + (void)key; \ + } +# define INTEGRATOR_PATH_NEXT(current_kernel, next_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ + (void)current_kernel; \ + } +# define INTEGRATOR_PATH_TERMINATE(current_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; \ + (void)current_kernel; \ + } +# define INTEGRATOR_PATH_NEXT_SORTED(current_kernel, next_kernel, key) \ + { \ + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = next_kernel; \ + (void)key; \ + (void)current_kernel; \ + } + +# define INTEGRATOR_SHADOW_PATH_INIT(shadow_state, state, next_kernel, shadow_type) \ + IntegratorShadowState shadow_state = &state->shadow_type; \ + INTEGRATOR_STATE_WRITE(shadow_state, shadow_path, queued_kernel) = next_kernel; +# define INTEGRATOR_SHADOW_PATH_NEXT(current_kernel, next_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = next_kernel; \ + (void)current_kernel; \ + } +# define INTEGRATOR_SHADOW_PATH_TERMINATE(current_kernel) \ + { \ + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; \ + (void)current_kernel; \ + } + +#endif + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/state_template.h b/intern/cycles/kernel/integrator/state_template.h new file mode 100644 index 00000000000..e082424fabd --- /dev/null +++ b/intern/cycles/kernel/integrator/state_template.h @@ -0,0 +1,109 @@ + +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/************************************ Path State *****************************/ + +KERNEL_STRUCT_BEGIN(path) +/* Index of a pixel within the device render buffer where this path will write its result. + * To get an actual offset within the buffer the value needs to be multiplied by the + * `kernel_data.film.pass_stride`. + * + * The multiplication is delayed for later, so that state can use 32bit integer. */ +KERNEL_STRUCT_MEMBER(path, uint32_t, render_pixel_index, KERNEL_FEATURE_PATH_TRACING) +/* Current sample number. */ +KERNEL_STRUCT_MEMBER(path, uint32_t, sample, KERNEL_FEATURE_PATH_TRACING) +/* Current ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transparent ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, transparent_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current diffuse ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, diffuse_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current glossy ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, glossy_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current transmission ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, transmission_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current volume ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounce, KERNEL_FEATURE_PATH_TRACING) +/* Current volume bounds ray bounce depth. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, volume_bounds_bounce, KERNEL_FEATURE_PATH_TRACING) +/* DeviceKernel bit indicating queued kernels. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, queued_kernel, KERNEL_FEATURE_PATH_TRACING) +/* Random number generator seed. */ +KERNEL_STRUCT_MEMBER(path, uint32_t, rng_hash, KERNEL_FEATURE_PATH_TRACING) +/* Random number dimension offset. */ +KERNEL_STRUCT_MEMBER(path, uint16_t, rng_offset, KERNEL_FEATURE_PATH_TRACING) +/* enum PathRayFlag */ +KERNEL_STRUCT_MEMBER(path, uint32_t, flag, KERNEL_FEATURE_PATH_TRACING) +/* Multiple importance sampling + * The PDF of BSDF sampling at the last scatter point, and distance to the + * last scatter point minus the last ray segment. This distance lets us + * compute the complete distance through transparent surfaces and volumes. */ +KERNEL_STRUCT_MEMBER(path, float, mis_ray_pdf, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(path, float, mis_ray_t, KERNEL_FEATURE_PATH_TRACING) +/* Filter glossy. */ +KERNEL_STRUCT_MEMBER(path, float, min_ray_pdf, KERNEL_FEATURE_PATH_TRACING) +/* Throughput. */ +KERNEL_STRUCT_MEMBER(path, float3, throughput, KERNEL_FEATURE_PATH_TRACING) +/* Ratio of throughput to distinguish diffuse and glossy render passes. */ +KERNEL_STRUCT_MEMBER(path, float3, diffuse_glossy_ratio, KERNEL_FEATURE_LIGHT_PASSES) +/* Denoising. */ +KERNEL_STRUCT_MEMBER(path, float3, denoising_feature_throughput, KERNEL_FEATURE_DENOISING) +/* Shader sorting. */ +/* TODO: compress as uint16? or leave out entirely and recompute key in sorting code? */ +KERNEL_STRUCT_MEMBER(path, uint32_t, shader_sort_key, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(path) + +/************************************** Ray ***********************************/ + +KERNEL_STRUCT_BEGIN(ray) +KERNEL_STRUCT_MEMBER(ray, float3, P, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float3, D, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, time, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, dP, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(ray, float, dD, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(ray) + +/*************************** Intersection result ******************************/ + +/* Result from scene intersection. */ +KERNEL_STRUCT_BEGIN(isect) +KERNEL_STRUCT_MEMBER(isect, float, t, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, float, u, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, float, v, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, int, prim, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, int, object, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_MEMBER(isect, int, type, KERNEL_FEATURE_PATH_TRACING) +KERNEL_STRUCT_END(isect) + +/*************** Subsurface closure state for subsurface kernel ***************/ + +KERNEL_STRUCT_BEGIN(subsurface) +KERNEL_STRUCT_MEMBER(subsurface, float3, albedo, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, float3, radius, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, float, anisotropy, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_MEMBER(subsurface, float3, Ng, KERNEL_FEATURE_SUBSURFACE) +KERNEL_STRUCT_END(subsurface) + +/********************************** Volume Stack ******************************/ + +KERNEL_STRUCT_BEGIN(volume_stack) +KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, object, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_ARRAY_MEMBER(volume_stack, int, shader, KERNEL_FEATURE_VOLUME) +KERNEL_STRUCT_END_ARRAY(volume_stack, + KERNEL_STRUCT_VOLUME_STACK_SIZE, + KERNEL_STRUCT_VOLUME_STACK_SIZE) diff --git a/intern/cycles/kernel/integrator/state_util.h b/intern/cycles/kernel/integrator/state_util.h new file mode 100644 index 00000000000..dafe06e7009 --- /dev/null +++ b/intern/cycles/kernel/integrator/state_util.h @@ -0,0 +1,440 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/state.h" + +#include "kernel/util/differential.h" + +CCL_NAMESPACE_BEGIN + +/* Ray */ + +ccl_device_forceinline void integrator_state_write_ray(KernelGlobals kg, + IntegratorState state, + ccl_private const Ray *ccl_restrict ray) +{ + INTEGRATOR_STATE_WRITE(state, ray, P) = ray->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = ray->D; + INTEGRATOR_STATE_WRITE(state, ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, ray, time) = ray->time; + INTEGRATOR_STATE_WRITE(state, ray, dP) = ray->dP; + INTEGRATOR_STATE_WRITE(state, ray, dD) = ray->dD; +} + +ccl_device_forceinline void integrator_state_read_ray(KernelGlobals kg, + ConstIntegratorState state, + ccl_private Ray *ccl_restrict ray) +{ + ray->P = INTEGRATOR_STATE(state, ray, P); + ray->D = INTEGRATOR_STATE(state, ray, D); + ray->t = INTEGRATOR_STATE(state, ray, t); + ray->time = INTEGRATOR_STATE(state, ray, time); + ray->dP = INTEGRATOR_STATE(state, ray, dP); + ray->dD = INTEGRATOR_STATE(state, ray, dD); +} + +/* Shadow Ray */ + +ccl_device_forceinline void integrator_state_write_shadow_ray( + KernelGlobals kg, IntegratorShadowState state, ccl_private const Ray *ccl_restrict ray) +{ + INTEGRATOR_STATE_WRITE(state, shadow_ray, P) = ray->P; + INTEGRATOR_STATE_WRITE(state, shadow_ray, D) = ray->D; + INTEGRATOR_STATE_WRITE(state, shadow_ray, t) = ray->t; + INTEGRATOR_STATE_WRITE(state, shadow_ray, time) = ray->time; + INTEGRATOR_STATE_WRITE(state, shadow_ray, dP) = ray->dP; +} + +ccl_device_forceinline void integrator_state_read_shadow_ray(KernelGlobals kg, + ConstIntegratorShadowState state, + ccl_private Ray *ccl_restrict ray) +{ + ray->P = INTEGRATOR_STATE(state, shadow_ray, P); + ray->D = INTEGRATOR_STATE(state, shadow_ray, D); + ray->t = INTEGRATOR_STATE(state, shadow_ray, t); + ray->time = INTEGRATOR_STATE(state, shadow_ray, time); + ray->dP = INTEGRATOR_STATE(state, shadow_ray, dP); + ray->dD = differential_zero_compact(); +} + +/* Intersection */ + +ccl_device_forceinline void integrator_state_write_isect( + KernelGlobals kg, IntegratorState state, ccl_private const Intersection *ccl_restrict isect) +{ + INTEGRATOR_STATE_WRITE(state, isect, t) = isect->t; + INTEGRATOR_STATE_WRITE(state, isect, u) = isect->u; + INTEGRATOR_STATE_WRITE(state, isect, v) = isect->v; + INTEGRATOR_STATE_WRITE(state, isect, object) = isect->object; + INTEGRATOR_STATE_WRITE(state, isect, prim) = isect->prim; + INTEGRATOR_STATE_WRITE(state, isect, type) = isect->type; +} + +ccl_device_forceinline void integrator_state_read_isect( + KernelGlobals kg, ConstIntegratorState state, ccl_private Intersection *ccl_restrict isect) +{ + isect->prim = INTEGRATOR_STATE(state, isect, prim); + isect->object = INTEGRATOR_STATE(state, isect, object); + isect->type = INTEGRATOR_STATE(state, isect, type); + isect->u = INTEGRATOR_STATE(state, isect, u); + isect->v = INTEGRATOR_STATE(state, isect, v); + isect->t = INTEGRATOR_STATE(state, isect, t); +} + +ccl_device_forceinline VolumeStack integrator_state_read_volume_stack(ConstIntegratorState state, + int i) +{ + VolumeStack entry = {INTEGRATOR_STATE_ARRAY(state, volume_stack, i, object), + INTEGRATOR_STATE_ARRAY(state, volume_stack, i, shader)}; + return entry; +} + +ccl_device_forceinline void integrator_state_write_volume_stack(IntegratorState state, + int i, + VolumeStack entry) +{ + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, i, object) = entry.object; + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, i, shader) = entry.shader; +} + +ccl_device_forceinline bool integrator_state_volume_stack_is_empty(KernelGlobals kg, + ConstIntegratorState state) +{ + return (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) ? + INTEGRATOR_STATE_ARRAY(state, volume_stack, 0, shader) == SHADER_NONE : + true; +} + +/* Shadow Intersection */ + +ccl_device_forceinline void integrator_state_write_shadow_isect( + IntegratorShadowState state, + ccl_private const Intersection *ccl_restrict isect, + const int index) +{ + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, t) = isect->t; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, u) = isect->u; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, v) = isect->v; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, object) = isect->object; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, prim) = isect->prim; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_isect, index, type) = isect->type; +} + +ccl_device_forceinline void integrator_state_read_shadow_isect( + ConstIntegratorShadowState state, + ccl_private Intersection *ccl_restrict isect, + const int index) +{ + isect->prim = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, prim); + isect->object = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, object); + isect->type = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, type); + isect->u = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, u); + isect->v = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, v); + isect->t = INTEGRATOR_STATE_ARRAY(state, shadow_isect, index, t); +} + +ccl_device_forceinline void integrator_state_copy_volume_stack_to_shadow( + KernelGlobals kg, IntegratorShadowState shadow_state, ConstIntegratorState state) +{ + if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { + int index = 0; + int shader; + do { + shader = INTEGRATOR_STATE_ARRAY(state, volume_stack, index, shader); + + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_volume_stack, index, object) = + INTEGRATOR_STATE_ARRAY(state, volume_stack, index, object); + INTEGRATOR_STATE_ARRAY_WRITE(shadow_state, shadow_volume_stack, index, shader) = shader; + + ++index; + } while (shader != OBJECT_NONE); + } +} + +ccl_device_forceinline void integrator_state_copy_volume_stack(KernelGlobals kg, + IntegratorState to_state, + ConstIntegratorState state) +{ + if (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) { + int index = 0; + int shader; + do { + shader = INTEGRATOR_STATE_ARRAY(state, volume_stack, index, shader); + + INTEGRATOR_STATE_ARRAY_WRITE(to_state, volume_stack, index, object) = INTEGRATOR_STATE_ARRAY( + state, volume_stack, index, object); + INTEGRATOR_STATE_ARRAY_WRITE(to_state, volume_stack, index, shader) = shader; + + ++index; + } while (shader != OBJECT_NONE); + } +} + +ccl_device_forceinline VolumeStack +integrator_state_read_shadow_volume_stack(ConstIntegratorShadowState state, int i) +{ + VolumeStack entry = {INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, object), + INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, i, shader)}; + return entry; +} + +ccl_device_forceinline bool integrator_state_shadow_volume_stack_is_empty( + KernelGlobals kg, ConstIntegratorShadowState state) +{ + return (kernel_data.kernel_features & KERNEL_FEATURE_VOLUME) ? + INTEGRATOR_STATE_ARRAY(state, shadow_volume_stack, 0, shader) == SHADER_NONE : + true; +} + +ccl_device_forceinline void integrator_state_write_shadow_volume_stack(IntegratorShadowState state, + int i, + VolumeStack entry) +{ + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, i, object) = entry.object; + INTEGRATOR_STATE_ARRAY_WRITE(state, shadow_volume_stack, i, shader) = entry.shader; +} + +#if defined(__KERNEL_GPU__) +ccl_device_inline void integrator_state_copy_only(KernelGlobals kg, + ConstIntegratorState to_state, + ConstIntegratorState state) +{ + int index; + + /* Rely on the compiler to optimize out unused assignments and `while(false)`'s. */ + +# define KERNEL_STRUCT_BEGIN(name) \ + index = 0; \ + do { + +# define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct.name != nullptr) { \ + kernel_integrator_state.parent_struct.name[to_state] = \ + kernel_integrator_state.parent_struct.name[state]; \ + } + +# define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct[index].name != nullptr) { \ + kernel_integrator_state.parent_struct[index].name[to_state] = \ + kernel_integrator_state.parent_struct[index].name[state]; \ + } + +# define KERNEL_STRUCT_END(name) \ + } \ + while (false) \ + ; + +# define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ + ++index; \ + } \ + while (index < gpu_array_size) \ + ; + +# define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size + +# include "kernel/integrator/state_template.h" + +# undef KERNEL_STRUCT_BEGIN +# undef KERNEL_STRUCT_MEMBER +# undef KERNEL_STRUCT_ARRAY_MEMBER +# undef KERNEL_STRUCT_END +# undef KERNEL_STRUCT_END_ARRAY +# undef KERNEL_STRUCT_VOLUME_STACK_SIZE +} + +ccl_device_inline void integrator_state_move(KernelGlobals kg, + ConstIntegratorState to_state, + ConstIntegratorState state) +{ + integrator_state_copy_only(kg, to_state, state); + + INTEGRATOR_STATE_WRITE(state, path, queued_kernel) = 0; +} + +ccl_device_inline void integrator_shadow_state_copy_only(KernelGlobals kg, + ConstIntegratorShadowState to_state, + ConstIntegratorShadowState state) +{ + int index; + + /* Rely on the compiler to optimize out unused assignments and `while(false)`'s. */ + +# define KERNEL_STRUCT_BEGIN(name) \ + index = 0; \ + do { + +# define KERNEL_STRUCT_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct.name != nullptr) { \ + kernel_integrator_state.parent_struct.name[to_state] = \ + kernel_integrator_state.parent_struct.name[state]; \ + } + +# define KERNEL_STRUCT_ARRAY_MEMBER(parent_struct, type, name, feature) \ + if (kernel_integrator_state.parent_struct[index].name != nullptr) { \ + kernel_integrator_state.parent_struct[index].name[to_state] = \ + kernel_integrator_state.parent_struct[index].name[state]; \ + } + +# define KERNEL_STRUCT_END(name) \ + } \ + while (false) \ + ; + +# define KERNEL_STRUCT_END_ARRAY(name, cpu_array_size, gpu_array_size) \ + ++index; \ + } \ + while (index < gpu_array_size) \ + ; + +# define KERNEL_STRUCT_VOLUME_STACK_SIZE kernel_data.volume_stack_size + +# include "kernel/integrator/shadow_state_template.h" + +# undef KERNEL_STRUCT_BEGIN +# undef KERNEL_STRUCT_MEMBER +# undef KERNEL_STRUCT_ARRAY_MEMBER +# undef KERNEL_STRUCT_END +# undef KERNEL_STRUCT_END_ARRAY +# undef KERNEL_STRUCT_VOLUME_STACK_SIZE +} + +ccl_device_inline void integrator_shadow_state_move(KernelGlobals kg, + ConstIntegratorState to_state, + ConstIntegratorState state) +{ + integrator_shadow_state_copy_only(kg, to_state, state); + + INTEGRATOR_STATE_WRITE(state, shadow_path, queued_kernel) = 0; +} + +#endif + +/* NOTE: Leaves kernel scheduling information untouched. Use INIT semantic for one of the paths + * after this function. */ +ccl_device_inline void integrator_state_shadow_catcher_split(KernelGlobals kg, + IntegratorState state) +{ +#if defined(__KERNEL_GPU__) + ConstIntegratorState to_state = atomic_fetch_and_add_uint32( + &kernel_integrator_state.next_main_path_index[0], 1); + + integrator_state_copy_only(kg, to_state, state); +#else + IntegratorStateCPU *ccl_restrict to_state = state + 1; + + /* Only copy the required subset, since shadow intersections are big and irrelevant here. */ + to_state->path = state->path; + to_state->ray = state->ray; + to_state->isect = state->isect; + integrator_state_copy_volume_stack(kg, to_state, state); +#endif + + INTEGRATOR_STATE_WRITE(to_state, path, flag) |= PATH_RAY_SHADOW_CATCHER_PASS; +} + +#ifdef __KERNEL_CPU__ +ccl_device_inline int integrator_state_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, bounce); +} + +ccl_device_inline int integrator_state_bounce(ConstIntegratorShadowState state, const int) +{ + return INTEGRATOR_STATE(state, shadow_path, bounce); +} + +ccl_device_inline int integrator_state_diffuse_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, diffuse_bounce); +} + +ccl_device_inline int integrator_state_diffuse_bounce(ConstIntegratorShadowState state, const int) +{ + return INTEGRATOR_STATE(state, shadow_path, diffuse_bounce); +} + +ccl_device_inline int integrator_state_glossy_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, glossy_bounce); +} + +ccl_device_inline int integrator_state_glossy_bounce(ConstIntegratorShadowState state, const int) +{ + return INTEGRATOR_STATE(state, shadow_path, glossy_bounce); +} + +ccl_device_inline int integrator_state_transmission_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, transmission_bounce); +} + +ccl_device_inline int integrator_state_transmission_bounce(ConstIntegratorShadowState state, + const int) +{ + return INTEGRATOR_STATE(state, shadow_path, transmission_bounce); +} + +ccl_device_inline int integrator_state_transparent_bounce(ConstIntegratorState state, const int) +{ + return INTEGRATOR_STATE(state, path, transparent_bounce); +} + +ccl_device_inline int integrator_state_transparent_bounce(ConstIntegratorShadowState state, + const int) +{ + return INTEGRATOR_STATE(state, shadow_path, transparent_bounce); +} +#else +ccl_device_inline int integrator_state_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, bounce) : + INTEGRATOR_STATE(state, path, bounce); +} + +ccl_device_inline int integrator_state_diffuse_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, diffuse_bounce) : + INTEGRATOR_STATE(state, path, diffuse_bounce); +} + +ccl_device_inline int integrator_state_glossy_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, glossy_bounce) : + INTEGRATOR_STATE(state, path, glossy_bounce); +} + +ccl_device_inline int integrator_state_transmission_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? + INTEGRATOR_STATE(state, shadow_path, transmission_bounce) : + INTEGRATOR_STATE(state, path, transmission_bounce); +} + +ccl_device_inline int integrator_state_transparent_bounce(ConstIntegratorShadowState state, + const uint32_t path_flag) +{ + return (path_flag & PATH_RAY_SHADOW) ? INTEGRATOR_STATE(state, shadow_path, transparent_bounce) : + INTEGRATOR_STATE(state, path, transparent_bounce); +} +#endif + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/subsurface.h b/intern/cycles/kernel/integrator/subsurface.h new file mode 100644 index 00000000000..49466112387 --- /dev/null +++ b/intern/cycles/kernel/integrator/subsurface.h @@ -0,0 +1,201 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/camera/projection.h" + +#include "kernel/bvh/bvh.h" + +#include "kernel/closure/alloc.h" +#include "kernel/closure/bsdf_diffuse.h" +#include "kernel/closure/bsdf_principled_diffuse.h" +#include "kernel/closure/bssrdf.h" +#include "kernel/closure/volume.h" + +#include "kernel/integrator/intersect_volume_stack.h" +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" +#include "kernel/integrator/subsurface_disk.h" +#include "kernel/integrator/subsurface_random_walk.h" + +CCL_NAMESPACE_BEGIN + +#ifdef __SUBSURFACE__ + +ccl_device int subsurface_bounce(KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *sd, + ccl_private const ShaderClosure *sc) +{ + /* We should never have two consecutive BSSRDF bounces, the second one should + * be converted to a diffuse BSDF to avoid this. */ + kernel_assert(!(INTEGRATOR_STATE(state, path, flag) & PATH_RAY_DIFFUSE_ANCESTOR)); + + /* Setup path state for intersect_subsurface kernel. */ + ccl_private const Bssrdf *bssrdf = (ccl_private const Bssrdf *)sc; + + /* Setup ray into surface. */ + INTEGRATOR_STATE_WRITE(state, ray, P) = sd->P; + INTEGRATOR_STATE_WRITE(state, ray, D) = bssrdf->N; + INTEGRATOR_STATE_WRITE(state, ray, t) = FLT_MAX; + INTEGRATOR_STATE_WRITE(state, ray, dP) = differential_make_compact(sd->dP); + INTEGRATOR_STATE_WRITE(state, ray, dD) = differential_zero_compact(); + + /* Pass along object info, reusing isect to save memory. */ + INTEGRATOR_STATE_WRITE(state, subsurface, Ng) = sd->Ng; + INTEGRATOR_STATE_WRITE(state, isect, object) = sd->object; + + uint32_t path_flag = (INTEGRATOR_STATE(state, path, flag) & ~PATH_RAY_CAMERA) | + ((sc->type == CLOSURE_BSSRDF_BURLEY_ID) ? PATH_RAY_SUBSURFACE_DISK : + PATH_RAY_SUBSURFACE_RANDOM_WALK); + + /* Compute weight, optionally including Fresnel from entry point. */ + float3 weight = shader_bssrdf_sample_weight(sd, sc); +# ifdef __PRINCIPLED__ + if (bssrdf->roughness != FLT_MAX) { + path_flag |= PATH_RAY_SUBSURFACE_USE_FRESNEL; + } +# endif + + INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight; + INTEGRATOR_STATE_WRITE(state, path, flag) = path_flag; + + /* Advance random number offset for bounce. */ + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; + + if (kernel_data.kernel_features & KERNEL_FEATURE_LIGHT_PASSES) { + if (INTEGRATOR_STATE(state, path, bounce) == 0) { + INTEGRATOR_STATE_WRITE(state, path, diffuse_glossy_ratio) = one_float3(); + } + } + + /* Pass BSSRDF parameters. */ + INTEGRATOR_STATE_WRITE(state, subsurface, albedo) = bssrdf->albedo; + INTEGRATOR_STATE_WRITE(state, subsurface, radius) = bssrdf->radius; + INTEGRATOR_STATE_WRITE(state, subsurface, anisotropy) = bssrdf->anisotropy; + + return LABEL_SUBSURFACE_SCATTER; +} + +ccl_device void subsurface_shader_data_setup(KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *sd, + const uint32_t path_flag) +{ + /* Get bump mapped normal from shader evaluation at exit point. */ + float3 N = sd->N; + if (sd->flag & SD_HAS_BSSRDF_BUMP) { + N = shader_bssrdf_normal(sd); + } + + /* Setup diffuse BSDF at the exit point. This replaces shader_eval_surface. */ + sd->flag &= ~SD_CLOSURE_FLAGS; + sd->num_closure = 0; + sd->num_closure_left = kernel_data.max_closures; + + const float3 weight = one_float3(); + +# ifdef __PRINCIPLED__ + if (path_flag & PATH_RAY_SUBSURFACE_USE_FRESNEL) { + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( + sd, sizeof(PrincipledDiffuseBsdf), weight); + + if (bsdf) { + bsdf->N = N; + bsdf->roughness = FLT_MAX; + sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_LAMBERT_EXIT); + } + } + else +# endif /* __PRINCIPLED__ */ + { + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), weight); + + if (bsdf) { + bsdf->N = N; + sd->flag |= bsdf_diffuse_setup(bsdf); + } + } +} + +ccl_device_inline bool subsurface_scatter(KernelGlobals kg, IntegratorState state) +{ + RNGState rng_state; + path_state_rng_load(state, &rng_state); + + Ray ray ccl_optional_struct_init; + LocalIntersection ss_isect ccl_optional_struct_init; + + if (INTEGRATOR_STATE(state, path, flag) & PATH_RAY_SUBSURFACE_RANDOM_WALK) { + if (!subsurface_random_walk(kg, state, rng_state, ray, ss_isect)) { + return false; + } + } + else { + if (!subsurface_disk(kg, state, rng_state, ray, ss_isect)) { + return false; + } + } + +# ifdef __VOLUME__ + /* Update volume stack if needed. */ + if (kernel_data.integrator.use_volumes) { + const int object = ss_isect.hits[0].object; + const int object_flag = kernel_tex_fetch(__object_flag, object); + + if (object_flag & SD_OBJECT_INTERSECTS_VOLUME) { + float3 P = INTEGRATOR_STATE(state, ray, P); + const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); + const float3 offset_P = ray_offset(P, -Ng); + + integrator_volume_stack_update_for_subsurface(kg, state, offset_P, ray.P); + } + } +# endif /* __VOLUME__ */ + + /* Pretend ray is coming from the outside towards the exit point. This ensures + * correct front/back facing normals. + * TODO: find a more elegant solution? */ + ray.P += ray.D * ray.t * 2.0f; + ray.D = -ray.D; + + integrator_state_write_isect(kg, state, &ss_isect.hits[0]); + integrator_state_write_ray(kg, state, &ray); + + /* Advance random number offset for bounce. */ + INTEGRATOR_STATE_WRITE(state, path, rng_offset) += PRNG_BOUNCE_NUM; + + const int shader = intersection_get_shader(kg, &ss_isect.hits[0]); + const int shader_flags = kernel_tex_fetch(__shaders, shader).flags; + if (shader_flags & SD_HAS_RAYTRACE) { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, + shader); + } + else { + INTEGRATOR_PATH_NEXT_SORTED(DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, + shader); + } + + return true; +} + +#endif /* __SUBSURFACE__ */ + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/subsurface_disk.h b/intern/cycles/kernel/integrator/subsurface_disk.h new file mode 100644 index 00000000000..6146b8c41fc --- /dev/null +++ b/intern/cycles/kernel/integrator/subsurface_disk.h @@ -0,0 +1,206 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CCL_NAMESPACE_BEGIN + +/* BSSRDF using disk based importance sampling. + * + * BSSRDF Importance Sampling, SIGGRAPH 2013 + * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf + */ + +ccl_device_inline float3 subsurface_disk_eval(const float3 radius, float disk_r, float r) +{ + const float3 eval = bssrdf_eval(radius, r); + const float pdf = bssrdf_pdf(radius, disk_r); + return (pdf > 0.0f) ? eval / pdf : zero_float3(); +} + +/* Subsurface scattering step, from a point on the surface to other + * nearby points on the same object. */ +ccl_device_inline bool subsurface_disk(KernelGlobals kg, + IntegratorState state, + RNGState rng_state, + ccl_private Ray &ray, + ccl_private LocalIntersection &ss_isect) + +{ + float disk_u, disk_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &disk_u, &disk_v); + + /* Read shading point info from integrator state. */ + const float3 P = INTEGRATOR_STATE(state, ray, P); + const float ray_dP = INTEGRATOR_STATE(state, ray, dP); + const float time = INTEGRATOR_STATE(state, ray, time); + const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); + const int object = INTEGRATOR_STATE(state, isect, object); + + /* Read subsurface scattering parameters. */ + const float3 radius = INTEGRATOR_STATE(state, subsurface, radius); + + /* Pick random axis in local frame and point on disk. */ + float3 disk_N, disk_T, disk_B; + float pick_pdf_N, pick_pdf_T, pick_pdf_B; + + disk_N = Ng; + make_orthonormals(disk_N, &disk_T, &disk_B); + + if (disk_v < 0.5f) { + pick_pdf_N = 0.5f; + pick_pdf_T = 0.25f; + pick_pdf_B = 0.25f; + disk_v *= 2.0f; + } + else if (disk_v < 0.75f) { + float3 tmp = disk_N; + disk_N = disk_T; + disk_T = tmp; + pick_pdf_N = 0.25f; + pick_pdf_T = 0.5f; + pick_pdf_B = 0.25f; + disk_v = (disk_v - 0.5f) * 4.0f; + } + else { + float3 tmp = disk_N; + disk_N = disk_B; + disk_B = tmp; + pick_pdf_N = 0.25f; + pick_pdf_T = 0.25f; + pick_pdf_B = 0.5f; + disk_v = (disk_v - 0.75f) * 4.0f; + } + + /* Sample point on disk. */ + float phi = M_2PI_F * disk_v; + float disk_height, disk_r; + + bssrdf_sample(radius, disk_u, &disk_r, &disk_height); + + float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B; + + /* Create ray. */ + ray.P = P + disk_N * disk_height + disk_P; + ray.D = -disk_N; + ray.t = 2.0f * disk_height; + ray.dP = ray_dP; + ray.dD = differential_zero_compact(); + ray.time = time; + + /* Intersect with the same object. if multiple intersections are found it + * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits. */ + uint lcg_state = lcg_state_init( + rng_state.rng_hash, rng_state.rng_offset, rng_state.sample, 0x68bc21eb); + const int max_hits = BSSRDF_MAX_HITS; + + scene_intersect_local(kg, &ray, &ss_isect, object, &lcg_state, max_hits); + const int num_eval_hits = min(ss_isect.num_hits, max_hits); + if (num_eval_hits == 0) { + return false; + } + + /* Sort for consistent renders between CPU and GPU, independent of the BVH + * traversal algorithm. */ + sort_intersections_and_normals(ss_isect.hits, ss_isect.Ng, num_eval_hits); + + float3 weights[BSSRDF_MAX_HITS]; /* TODO: zero? */ + float sum_weights = 0.0f; + + for (int hit = 0; hit < num_eval_hits; hit++) { + /* Get geometric normal. */ + const int object = ss_isect.hits[hit].object; + const int object_flag = kernel_tex_fetch(__object_flag, object); + float3 hit_Ng = ss_isect.Ng[hit]; + if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { + hit_Ng = -hit_Ng; + } + + if (!(object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { + /* Transform normal to world space. */ + Transform itfm; + Transform tfm = object_fetch_transform_motion_test(kg, object, time, &itfm); + hit_Ng = normalize(transform_direction_transposed(&itfm, hit_Ng)); + + /* Transform t to world space, except for OptiX where it already is. */ +#ifdef __KERNEL_OPTIX__ + (void)tfm; +#else + float3 D = transform_direction(&itfm, ray.D); + D = normalize(D) * ss_isect.hits[hit].t; + ss_isect.hits[hit].t = len(transform_direction(&tfm, D)); +#endif + } + + /* Quickly retrieve P and Ng without setting up ShaderData. */ + const float3 hit_P = ray.P + ray.D * ss_isect.hits[hit].t; + + /* Probability densities for local frame axes. */ + const float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng)); + const float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng)); + const float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng)); + + /* Multiple importance sample between 3 axes, power heuristic + * found to be slightly better than balance heuristic. pdf_N + * in the MIS weight and denominator cancelled out. */ + float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B)); + if (ss_isect.num_hits > max_hits) { + w *= ss_isect.num_hits / (float)max_hits; + } + + /* Real distance to sampled point. */ + const float r = len(hit_P - P); + + /* Evaluate profiles. */ + const float3 weight = subsurface_disk_eval(radius, disk_r, r) * w; + + /* Store result. */ + ss_isect.Ng[hit] = hit_Ng; + weights[hit] = weight; + sum_weights += average(fabs(weight)); + } + + if (sum_weights == 0.0f) { + return false; + } + + /* Use importance resampling, sampling one of the hits proportional to weight. */ + const float r = lcg_step_float(&lcg_state) * sum_weights; + float partial_sum = 0.0f; + + for (int hit = 0; hit < num_eval_hits; hit++) { + const float3 weight = weights[hit]; + const float sample_weight = average(fabs(weight)); + float next_sum = partial_sum + sample_weight; + + if (r < next_sum) { + /* Return exit point. */ + INTEGRATOR_STATE_WRITE(state, path, throughput) *= weight * sum_weights / sample_weight; + + ss_isect.hits[0] = ss_isect.hits[hit]; + ss_isect.Ng[0] = ss_isect.Ng[hit]; + + ray.P = ray.P + ray.D * ss_isect.hits[hit].t; + ray.D = ss_isect.Ng[hit]; + ray.t = 1.0f; + return true; + } + + partial_sum = next_sum; + } + + return false; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/subsurface_random_walk.h b/intern/cycles/kernel/integrator/subsurface_random_walk.h new file mode 100644 index 00000000000..f0712758174 --- /dev/null +++ b/intern/cycles/kernel/integrator/subsurface_random_walk.h @@ -0,0 +1,469 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "kernel/camera/projection.h" + +#include "kernel/bvh/bvh.h" + +CCL_NAMESPACE_BEGIN + +/* Random walk subsurface scattering. + * + * "Practical and Controllable Subsurface Scattering for Production Path + * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ + +/* Support for anisotropy from: + * "Path Traced Subsurface Scattering using Anisotropic Phase Functions + * and Non-Exponential Free Flights". + * Magnus Wrenninge, Ryusuke Villemin, Christophe Hery. + * https://graphics.pixar.com/library/PathTracedSubsurface/ */ + +ccl_device void subsurface_random_walk_remap(const float albedo, + const float d, + float g, + ccl_private float *sigma_t, + ccl_private float *alpha) +{ + /* Compute attenuation and scattering coefficients from albedo. */ + const float g2 = g * g; + const float g3 = g2 * g; + const float g4 = g3 * g; + const float g5 = g4 * g; + const float g6 = g5 * g; + const float g7 = g6 * g; + + const float A = 1.8260523782f + -1.28451056436f * g + -1.79904629312f * g2 + + 9.19393289202f * g3 + -22.8215585862f * g4 + 32.0234874259f * g5 + + -23.6264803333f * g6 + 7.21067002658f * g7; + const float B = 4.98511194385f + + 0.127355959438f * + expf(31.1491581433f * g + -201.847017512f * g2 + 841.576016723f * g3 + + -2018.09288505f * g4 + 2731.71560286f * g5 + -1935.41424244f * g6 + + 559.009054474f * g7); + const float C = 1.09686102424f + -0.394704063468f * g + 1.05258115941f * g2 + + -8.83963712726f * g3 + 28.8643230661f * g4 + -46.8802913581f * g5 + + 38.5402837518f * g6 + -12.7181042538f * g7; + const float D = 0.496310210422f + 0.360146581622f * g + -2.15139309747f * g2 + + 17.8896899217f * g3 + -55.2984010333f * g4 + 82.065982243f * g5 + + -58.5106008578f * g6 + 15.8478295021f * g7; + const float E = 4.23190299701f + + 0.00310603949088f * + expf(76.7316253952f * g + -594.356773233f * g2 + 2448.8834203f * g3 + + -5576.68528998f * g4 + 7116.60171912f * g5 + -4763.54467887f * g6 + + 1303.5318055f * g7); + const float F = 2.40602999408f + -2.51814844609f * g + 9.18494908356f * g2 + + -79.2191708682f * g3 + 259.082868209f * g4 + -403.613804597f * g5 + + 302.85712436f * g6 + -87.4370473567f * g7; + + const float blend = powf(albedo, 0.25f); + + *alpha = (1.0f - blend) * A * powf(atanf(B * albedo), C) + + blend * D * powf(atanf(E * albedo), F); + *alpha = clamp(*alpha, 0.0f, 0.999999f); // because of numerical precision + + float sigma_t_prime = 1.0f / fmaxf(d, 1e-16f); + *sigma_t = sigma_t_prime / (1.0f - g); +} + +ccl_device void subsurface_random_walk_coefficients(const float3 albedo, + const float3 radius, + const float anisotropy, + ccl_private float3 *sigma_t, + ccl_private float3 *alpha, + ccl_private float3 *throughput) +{ + float sigma_t_x, sigma_t_y, sigma_t_z; + float alpha_x, alpha_y, alpha_z; + + subsurface_random_walk_remap(albedo.x, radius.x, anisotropy, &sigma_t_x, &alpha_x); + subsurface_random_walk_remap(albedo.y, radius.y, anisotropy, &sigma_t_y, &alpha_y); + subsurface_random_walk_remap(albedo.z, radius.z, anisotropy, &sigma_t_z, &alpha_z); + + /* Throughput already contains closure weight at this point, which includes the + * albedo, as well as closure mixing and Fresnel weights. Divide out the albedo + * which will be added through scattering. */ + *throughput = safe_divide_color(*throughput, albedo); + + /* With low albedo values (like 0.025) we get diffusion_length 1.0 and + * infinite phase functions. To avoid a sharp discontinuity as we go from + * such values to 0.0, increase alpha and reduce the throughput to compensate. */ + const float min_alpha = 0.2f; + if (alpha_x < min_alpha) { + (*throughput).x *= alpha_x / min_alpha; + alpha_x = min_alpha; + } + if (alpha_y < min_alpha) { + (*throughput).y *= alpha_y / min_alpha; + alpha_y = min_alpha; + } + if (alpha_z < min_alpha) { + (*throughput).z *= alpha_z / min_alpha; + alpha_z = min_alpha; + } + + *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z); + *alpha = make_float3(alpha_x, alpha_y, alpha_z); +} + +/* References for Dwivedi sampling: + * + * [1] "A Zero-variance-based Sampling Scheme for Monte Carlo Subsurface Scattering" + * by Jaroslav Křivánek and Eugene d'Eon (SIGGRAPH 2014) + * https://cgg.mff.cuni.cz/~jaroslav/papers/2014-zerovar/ + * + * [2] "Improving the Dwivedi Sampling Scheme" + * by Johannes Meng, Johannes Hanika, and Carsten Dachsbacher (EGSR 2016) + * https://cg.ivd.kit.edu/1951.php + * + * [3] "Zero-Variance Theory for Efficient Subsurface Scattering" + * by Eugene d'Eon and Jaroslav Křivánek (SIGGRAPH 2020) + * https://iliyan.com/publications/RenderingCourse2020 + */ + +ccl_device_forceinline float eval_phase_dwivedi(float v, float phase_log, float cos_theta) +{ + /* Eq. 9 from [2] using precomputed log((v + 1) / (v - 1)) */ + return 1.0f / ((v - cos_theta) * phase_log); +} + +ccl_device_forceinline float sample_phase_dwivedi(float v, float phase_log, float rand) +{ + /* Based on Eq. 10 from [2]: `v - (v + 1) * pow((v - 1) / (v + 1), rand)` + * Since we're already pre-computing `phase_log = log((v + 1) / (v - 1))` for the evaluation, + * we can implement the power function like this. */ + return v - (v + 1.0f) * expf(-rand * phase_log); +} + +ccl_device_forceinline float diffusion_length_dwivedi(float alpha) +{ + /* Eq. 67 from [3] */ + return 1.0f / sqrtf(1.0f - powf(alpha, 2.44294f - 0.0215813f * alpha + 0.578637f / alpha)); +} + +ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, float randv) +{ + float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta); + float phi = M_2PI_F * randv; + float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta); + + float3 T, B; + make_orthonormals(D, &T, &B); + return dir.x * T + dir.y * B + dir.z * D; +} + +ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, + float t, + bool hit, + ccl_private float3 *transmittance) +{ + float3 T = volume_color_transmittance(sigma_t, t); + if (transmittance) { + *transmittance = T; + } + return hit ? T : sigma_t * T; +} + +/* Define the below variable to get the similarity code active, + * and the value represents the cutoff level */ +#define SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL 9 + +ccl_device_inline bool subsurface_random_walk(KernelGlobals kg, + IntegratorState state, + RNGState rng_state, + ccl_private Ray &ray, + ccl_private LocalIntersection &ss_isect) +{ + float bssrdf_u, bssrdf_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); + + const float3 P = INTEGRATOR_STATE(state, ray, P); + const float3 N = INTEGRATOR_STATE(state, ray, D); + const float ray_dP = INTEGRATOR_STATE(state, ray, dP); + const float time = INTEGRATOR_STATE(state, ray, time); + const float3 Ng = INTEGRATOR_STATE(state, subsurface, Ng); + const int object = INTEGRATOR_STATE(state, isect, object); + + /* Sample diffuse surface scatter into the object. */ + float3 D; + float pdf; + sample_cos_hemisphere(-N, bssrdf_u, bssrdf_v, &D, &pdf); + if (dot(-Ng, D) <= 0.0f) { + return false; + } + + /* Setup ray. */ + ray.P = ray_offset(P, -Ng); + ray.D = D; + ray.t = FLT_MAX; + ray.time = time; + ray.dP = ray_dP; + ray.dD = differential_zero_compact(); + +#ifndef __KERNEL_OPTIX__ + /* Compute or fetch object transforms. */ + Transform ob_itfm ccl_optional_struct_init; + Transform ob_tfm = object_fetch_transform_motion_test(kg, object, time, &ob_itfm); +#endif + + /* Convert subsurface to volume coefficients. + * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ + const float3 albedo = INTEGRATOR_STATE(state, subsurface, albedo); + const float3 radius = INTEGRATOR_STATE(state, subsurface, radius); + const float anisotropy = INTEGRATOR_STATE(state, subsurface, anisotropy); + + float3 sigma_t, alpha; + float3 throughput = INTEGRATOR_STATE_WRITE(state, path, throughput); + subsurface_random_walk_coefficients(albedo, radius, anisotropy, &sigma_t, &alpha, &throughput); + float3 sigma_s = sigma_t * alpha; + + /* Theoretically it should be better to use the exact alpha for the channel we're sampling at + * each bounce, but in practice there doesn't seem to be a noticeable difference in exchange + * for making the code significantly more complex and slower (if direction sampling depends on + * the sampled channel, we need to compute its PDF per-channel and consider it for MIS later on). + * + * Since the strength of the guided sampling increases as alpha gets lower, using a value that + * is too low results in fireflies while one that's too high just gives a bit more noise. + * Therefore, the code here uses the highest of the three albedos to be safe. */ + const float diffusion_length = diffusion_length_dwivedi(max3(alpha)); + + if (diffusion_length == 1.0f) { + /* With specific values of alpha the length might become 1, which in asymptotic makes phase to + * be infinite. After first bounce it will cause throughput to be 0. Do early output, avoiding + * numerical issues and extra unneeded work. */ + return false; + } + + /* Precompute term for phase sampling. */ + const float phase_log = logf((diffusion_length + 1.0f) / (diffusion_length - 1.0f)); + + /* Modify state for RNGs, decorrelated from other paths. */ + rng_state.rng_hash = cmj_hash(rng_state.rng_hash + rng_state.rng_offset, 0xdeadbeef); + + /* Random walk until we hit the surface again. */ + bool hit = false; + bool have_opposite_interface = false; + float opposite_distance = 0.0f; + + /* TODO: Disable for `alpha > 0.999` or so? */ + /* Our heuristic, a compromise between guiding and classic. */ + const float guided_fraction = 1.0f - fmaxf(0.5f, powf(fabsf(anisotropy), 0.125f)); + +#ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL + float3 sigma_s_star = sigma_s * (1.0f - anisotropy); + float3 sigma_t_star = sigma_t - sigma_s + sigma_s_star; + float3 sigma_t_org = sigma_t; + float3 sigma_s_org = sigma_s; + const float anisotropy_org = anisotropy; + const float guided_fraction_org = guided_fraction; +#endif + + for (int bounce = 0; bounce < BSSRDF_MAX_BOUNCES; bounce++) { + /* Advance random number offset. */ + rng_state.rng_offset += PRNG_BOUNCE_NUM; + +#ifdef SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL + // shadow with local variables according to depth + float anisotropy, guided_fraction; + float3 sigma_s, sigma_t; + if (bounce <= SUBSURFACE_RANDOM_WALK_SIMILARITY_LEVEL) { + anisotropy = anisotropy_org; + guided_fraction = guided_fraction_org; + sigma_t = sigma_t_org; + sigma_s = sigma_s_org; + } + else { + anisotropy = 0.0f; + guided_fraction = 0.75f; // back to isotropic heuristic from Blender + sigma_t = sigma_t_star; + sigma_s = sigma_s_star; + } +#endif + + /* Sample color channel, use MIS with balance heuristic. */ + float rphase = path_state_rng_1D(kg, &rng_state, PRNG_PHASE_CHANNEL); + float3 channel_pdf; + int channel = volume_sample_channel(alpha, throughput, rphase, &channel_pdf); + float sample_sigma_t = volume_channel_get(sigma_t, channel); + float randt = path_state_rng_1D(kg, &rng_state, PRNG_SCATTER_DISTANCE); + + /* We need the result of the ray-cast to compute the full guided PDF, so just remember the + * relevant terms to avoid recomputing them later. */ + float backward_fraction = 0.0f; + float forward_pdf_factor = 0.0f; + float forward_stretching = 1.0f; + float backward_pdf_factor = 0.0f; + float backward_stretching = 1.0f; + + /* For the initial ray, we already know the direction, so just do classic distance sampling. */ + if (bounce > 0) { + /* Decide whether we should use guided or classic sampling. */ + bool guided = (path_state_rng_1D(kg, &rng_state, PRNG_LIGHT_TERMINATE) < guided_fraction); + + /* Determine if we want to sample away from the incoming interface. + * This only happens if we found a nearby opposite interface, and the probability for it + * depends on how close we are to it already. + * This probability term comes from the recorded presentation of [3]. */ + bool guide_backward = false; + if (have_opposite_interface) { + /* Compute distance of the random walk between the tangent plane at the starting point + * and the assumed opposite interface (the parallel plane that contains the point we + * found in our ray query for the opposite side). */ + float x = clamp(dot(ray.P - P, -N), 0.0f, opposite_distance); + backward_fraction = 1.0f / + (1.0f + expf((opposite_distance - 2.0f * x) / diffusion_length)); + guide_backward = path_state_rng_1D(kg, &rng_state, PRNG_TERMINATE) < backward_fraction; + } + + /* Sample scattering direction. */ + float scatter_u, scatter_v; + path_state_rng_2D(kg, &rng_state, PRNG_BSDF_U, &scatter_u, &scatter_v); + float cos_theta; + float hg_pdf; + if (guided) { + cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u); + /* The backwards guiding distribution is just mirrored along `sd->N`, so swapping the + * sign here is enough to sample from that instead. */ + if (guide_backward) { + cos_theta = -cos_theta; + } + float3 newD = direction_from_cosine(N, cos_theta, scatter_v); + hg_pdf = single_peaked_henyey_greenstein(dot(ray.D, newD), anisotropy); + ray.D = newD; + } + else { + float3 newD = henyey_greenstrein_sample(ray.D, anisotropy, scatter_u, scatter_v, &hg_pdf); + cos_theta = dot(newD, N); + ray.D = newD; + } + + /* Compute PDF factor caused by phase sampling (as the ratio of guided / classic). + * Since phase sampling is channel-independent, we can get away with applying a factor + * to the guided PDF, which implicitly means pulling out the classic PDF term and letting + * it cancel with an equivalent term in the numerator of the full estimator. + * For the backward PDF, we again reuse the same probability distribution with a sign swap. + */ + forward_pdf_factor = M_1_2PI_F * eval_phase_dwivedi(diffusion_length, phase_log, cos_theta) / + hg_pdf; + backward_pdf_factor = M_1_2PI_F * + eval_phase_dwivedi(diffusion_length, phase_log, -cos_theta) / hg_pdf; + + /* Prepare distance sampling. + * For the backwards case, this also needs the sign swapped since now directions against + * `sd->N` (and therefore with negative cos_theta) are preferred. */ + forward_stretching = (1.0f - cos_theta / diffusion_length); + backward_stretching = (1.0f + cos_theta / diffusion_length); + if (guided) { + sample_sigma_t *= guide_backward ? backward_stretching : forward_stretching; + } + } + + /* Sample direction along ray. */ + float t = -logf(1.0f - randt) / sample_sigma_t; + + /* On the first bounce, we use the ray-cast to check if the opposite side is nearby. + * If yes, we will later use backwards guided sampling in order to have a decent + * chance of connecting to it. + * TODO: Maybe use less than 10 times the mean free path? */ + ray.t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; + scene_intersect_local(kg, &ray, &ss_isect, object, NULL, 1); + hit = (ss_isect.num_hits > 0); + + if (hit) { +#ifdef __KERNEL_OPTIX__ + /* t is always in world space with OptiX. */ + ray.t = ss_isect.hits[0].t; +#else + /* Compute world space distance to surface hit. */ + float3 D = transform_direction(&ob_itfm, ray.D); + D = normalize(D) * ss_isect.hits[0].t; + ray.t = len(transform_direction(&ob_tfm, D)); +#endif + } + + if (bounce == 0) { + /* Check if we hit the opposite side. */ + if (hit) { + have_opposite_interface = true; + opposite_distance = dot(ray.P + ray.t * ray.D - P, -N); + } + /* Apart from the opposite side check, we were supposed to only trace up to distance t, + * so check if there would have been a hit in that case. */ + hit = ray.t < t; + } + + /* Use the distance to the exit point for the throughput update if we found one. */ + if (hit) { + t = ray.t; + } + else if (bounce == 0) { + /* Restore original position if nothing was hit after the first bounce, + * without the ray_offset() that was added to avoid self-intersection. + * Otherwise if that offset is relatively large compared to the scattering + * radius, we never go back up high enough to exit the surface. */ + ray.P = P; + } + + /* Advance to new scatter location. */ + ray.P += t * ray.D; + + float3 transmittance; + float3 pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); + if (bounce > 0) { + /* Compute PDF just like we do for classic sampling, but with the stretched sigma_t. */ + float3 guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); + + if (have_opposite_interface) { + /* First step of MIS: Depending on geometry we might have two methods for guided + * sampling, so perform MIS between them. */ + float3 back_pdf = subsurface_random_walk_pdf(backward_stretching * sigma_t, t, hit, NULL); + guided_pdf = mix( + guided_pdf * forward_pdf_factor, back_pdf * backward_pdf_factor, backward_fraction); + } + else { + /* Just include phase sampling factor otherwise. */ + guided_pdf *= forward_pdf_factor; + } + + /* Now we apply the MIS balance heuristic between the classic and guided sampling. */ + pdf = mix(pdf, guided_pdf, guided_fraction); + } + + /* Finally, we're applying MIS again to combine the three color channels. + * Altogether, the MIS computation combines up to nine different estimators: + * {classic, guided, backward_guided} x {r, g, b} */ + throughput *= (hit ? transmittance : sigma_s * transmittance) / dot(channel_pdf, pdf); + + if (hit) { + /* If we hit the surface, we are done. */ + break; + } + else if (throughput.x < VOLUME_THROUGHPUT_EPSILON && + throughput.y < VOLUME_THROUGHPUT_EPSILON && + throughput.z < VOLUME_THROUGHPUT_EPSILON) { + /* Avoid unnecessary work and precision issue when throughput gets really small. */ + break; + } + } + + if (hit) { + kernel_assert(isfinite3_safe(throughput)); + INTEGRATOR_STATE_WRITE(state, path, throughput) = throughput; + } + + return hit; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/integrator/volume_stack.h b/intern/cycles/kernel/integrator/volume_stack.h new file mode 100644 index 00000000000..cf69826ffff --- /dev/null +++ b/intern/cycles/kernel/integrator/volume_stack.h @@ -0,0 +1,225 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Volume Stack + * + * This is an array of object/shared ID's that the current segment of the path + * is inside of. */ + +template +ccl_device void volume_stack_enter_exit(KernelGlobals kg, + ccl_private const ShaderData *sd, + StackReadOp stack_read, + StackWriteOp stack_write) +{ + /* todo: we should have some way for objects to indicate if they want the + * world shader to work inside them. excluding it by default is problematic + * because non-volume objects can't be assumed to be closed manifolds */ + if (!(sd->flag & SD_HAS_VOLUME)) { + return; + } + + if (sd->flag & SD_BACKFACING) { + /* Exit volume object: remove from stack. */ + for (int i = 0;; i++) { + VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + if (entry.object == sd->object) { + /* Shift back next stack entries. */ + do { + entry = stack_read(i + 1); + stack_write(i, entry); + i++; + } while (entry.shader != SHADER_NONE); + + return; + } + } + } + else { + /* Enter volume object: add to stack. */ + int i; + for (i = 0;; i++) { + VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + /* Already in the stack? then we have nothing to do. */ + if (entry.object == sd->object) { + return; + } + } + + /* If we exceed the stack limit, ignore. */ + if (i >= kernel_data.volume_stack_size - 1) { + return; + } + + /* Add to the end of the stack. */ + const VolumeStack new_entry = {sd->object, sd->shader}; + const VolumeStack empty_entry = {OBJECT_NONE, SHADER_NONE}; + stack_write(i, new_entry); + stack_write(i + 1, empty_entry); + } +} + +ccl_device void volume_stack_enter_exit(KernelGlobals kg, + IntegratorState state, + ccl_private const ShaderData *sd) +{ + volume_stack_enter_exit( + kg, + sd, + [=](const int i) { return integrator_state_read_volume_stack(state, i); }, + [=](const int i, const VolumeStack entry) { + integrator_state_write_volume_stack(state, i, entry); + }); +} + +ccl_device void shadow_volume_stack_enter_exit(KernelGlobals kg, + IntegratorShadowState state, + ccl_private const ShaderData *sd) +{ + volume_stack_enter_exit( + kg, + sd, + [=](const int i) { return integrator_state_read_shadow_volume_stack(state, i); }, + [=](const int i, const VolumeStack entry) { + integrator_state_write_shadow_volume_stack(state, i, entry); + }); +} + +/* Clean stack after the last bounce. + * + * It is expected that all volumes are closed manifolds, so at the time when ray + * hits nothing (for example, it is a last bounce which goes to environment) the + * only expected volume in the stack is the world's one. All the rest volume + * entries should have been exited already. + * + * This isn't always true because of ray intersection precision issues, which + * could lead us to an infinite non-world volume in the stack, causing render + * artifacts. + * + * Use this function after the last bounce to get rid of all volumes apart from + * the world's one after the last bounce to avoid render artifacts. + */ +ccl_device_inline void volume_stack_clean(KernelGlobals kg, IntegratorState state) +{ + if (kernel_data.background.volume_shader != SHADER_NONE) { + /* Keep the world's volume in stack. */ + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 1, shader) = SHADER_NONE; + } + else { + INTEGRATOR_STATE_ARRAY_WRITE(state, volume_stack, 0, shader) = SHADER_NONE; + } +} + +template +ccl_device float volume_stack_step_size(KernelGlobals kg, StackReadOp stack_read) +{ + float step_size = FLT_MAX; + + for (int i = 0;; i++) { + VolumeStack entry = stack_read(i); + if (entry.shader == SHADER_NONE) { + break; + } + + int shader_flag = kernel_tex_fetch(__shaders, (entry.shader & SHADER_MASK)).flags; + + bool heterogeneous = false; + + if (shader_flag & SD_HETEROGENEOUS_VOLUME) { + heterogeneous = true; + } + else if (shader_flag & SD_NEED_VOLUME_ATTRIBUTES) { + /* We want to render world or objects without any volume grids + * as homogeneous, but can only verify this at run-time since other + * heterogeneous volume objects may be using the same shader. */ + int object = entry.object; + if (object != OBJECT_NONE) { + int object_flag = kernel_tex_fetch(__object_flag, object); + if (object_flag & SD_OBJECT_HAS_VOLUME_ATTRIBUTES) { + heterogeneous = true; + } + } + } + + if (heterogeneous) { + float object_step_size = object_volume_step_size(kg, entry.object); + object_step_size *= kernel_data.integrator.volume_step_rate; + step_size = fminf(object_step_size, step_size); + } + } + + return step_size; +} + +typedef enum VolumeSampleMethod { + VOLUME_SAMPLE_NONE = 0, + VOLUME_SAMPLE_DISTANCE = (1 << 0), + VOLUME_SAMPLE_EQUIANGULAR = (1 << 1), + VOLUME_SAMPLE_MIS = (VOLUME_SAMPLE_DISTANCE | VOLUME_SAMPLE_EQUIANGULAR), +} VolumeSampleMethod; + +ccl_device VolumeSampleMethod volume_stack_sample_method(KernelGlobals kg, IntegratorState state) +{ + VolumeSampleMethod method = VOLUME_SAMPLE_NONE; + + for (int i = 0;; i++) { + VolumeStack entry = integrator_state_read_volume_stack(state, i); + if (entry.shader == SHADER_NONE) { + break; + } + + int shader_flag = kernel_tex_fetch(__shaders, (entry.shader & SHADER_MASK)).flags; + + if (shader_flag & SD_VOLUME_MIS) { + /* Multiple importance sampling. */ + return VOLUME_SAMPLE_MIS; + } + else if (shader_flag & SD_VOLUME_EQUIANGULAR) { + /* Distance + equiangular sampling -> multiple importance sampling. */ + if (method == VOLUME_SAMPLE_DISTANCE) { + return VOLUME_SAMPLE_MIS; + } + + /* Only equiangular sampling. */ + method = VOLUME_SAMPLE_EQUIANGULAR; + } + else { + /* Distance + equiangular sampling -> multiple importance sampling. */ + if (method == VOLUME_SAMPLE_EQUIANGULAR) { + return VOLUME_SAMPLE_MIS; + } + + /* Distance sampling only. */ + method = VOLUME_SAMPLE_DISTANCE; + } + } + + return method; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_accumulate.h b/intern/cycles/kernel/kernel_accumulate.h deleted file mode 100644 index 61653d328f1..00000000000 --- a/intern/cycles/kernel/kernel_accumulate.h +++ /dev/null @@ -1,764 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* BSDF Eval - * - * BSDF evaluation result, split per BSDF type. This is used to accumulate - * render passes separately. */ - -ccl_device float3 shader_bsdf_transparency(KernelGlobals *kg, const ShaderData *sd); - -ccl_device_inline void bsdf_eval_init(BsdfEval *eval, - ClosureType type, - float3 value, - int use_light_pass) -{ -#ifdef __PASSES__ - eval->use_light_pass = use_light_pass; - - if (eval->use_light_pass) { - eval->diffuse = zero_float3(); - eval->glossy = zero_float3(); - eval->transmission = zero_float3(); - eval->transparent = zero_float3(); - eval->volume = zero_float3(); - - if (type == CLOSURE_BSDF_TRANSPARENT_ID) - eval->transparent = value; - else if (CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_BSDF_BSSRDF(type)) - eval->diffuse = value; - else if (CLOSURE_IS_BSDF_GLOSSY(type)) - eval->glossy = value; - else if (CLOSURE_IS_BSDF_TRANSMISSION(type)) - eval->transmission = value; - else if (CLOSURE_IS_PHASE(type)) - eval->volume = value; - } - else -#endif - { - eval->diffuse = value; - } -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis = zero_float3(); -#endif -} - -ccl_device_inline void bsdf_eval_accum(BsdfEval *eval, - ClosureType type, - float3 value, - float mis_weight) -{ -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis += value; -#endif - value *= mis_weight; -#ifdef __PASSES__ - if (eval->use_light_pass) { - if (CLOSURE_IS_BSDF_DIFFUSE(type) || CLOSURE_IS_BSDF_BSSRDF(type)) - eval->diffuse += value; - else if (CLOSURE_IS_BSDF_GLOSSY(type)) - eval->glossy += value; - else if (CLOSURE_IS_BSDF_TRANSMISSION(type)) - eval->transmission += value; - else if (CLOSURE_IS_PHASE(type)) - eval->volume += value; - - /* skipping transparent, this function is used by for eval(), will be zero then */ - } - else -#endif - { - eval->diffuse += value; - } -} - -ccl_device_inline bool bsdf_eval_is_zero(BsdfEval *eval) -{ -#ifdef __PASSES__ - if (eval->use_light_pass) { - return is_zero(eval->diffuse) && is_zero(eval->glossy) && is_zero(eval->transmission) && - is_zero(eval->transparent) && is_zero(eval->volume); - } - else -#endif - { - return is_zero(eval->diffuse); - } -} - -ccl_device_inline void bsdf_eval_mis(BsdfEval *eval, float value) -{ -#ifdef __PASSES__ - if (eval->use_light_pass) { - eval->diffuse *= value; - eval->glossy *= value; - eval->transmission *= value; - eval->volume *= value; - - /* skipping transparent, this function is used by for eval(), will be zero then */ - } - else -#endif - { - eval->diffuse *= value; - } -} - -ccl_device_inline void bsdf_eval_mul(BsdfEval *eval, float value) -{ -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis *= value; -#endif - bsdf_eval_mis(eval, value); -} - -ccl_device_inline void bsdf_eval_mul3(BsdfEval *eval, float3 value) -{ -#ifdef __SHADOW_TRICKS__ - eval->sum_no_mis *= value; -#endif -#ifdef __PASSES__ - if (eval->use_light_pass) { - eval->diffuse *= value; - eval->glossy *= value; - eval->transmission *= value; - eval->volume *= value; - - /* skipping transparent, this function is used by for eval(), will be zero then */ - } - else - eval->diffuse *= value; -#else - eval->diffuse *= value; -#endif -} - -ccl_device_inline float3 bsdf_eval_sum(const BsdfEval *eval) -{ -#ifdef __PASSES__ - if (eval->use_light_pass) { - return eval->diffuse + eval->glossy + eval->transmission + eval->volume; - } - else -#endif - return eval->diffuse; -} - -/* Path Radiance - * - * We accumulate different render passes separately. After summing at the end - * to get the combined result, it should be identical. We definite directly - * visible as the first non-transparent hit, while indirectly visible are the - * bounces after that. */ - -ccl_device_inline void path_radiance_init(KernelGlobals *kg, PathRadiance *L) -{ - /* clear all */ -#ifdef __PASSES__ - L->use_light_pass = kernel_data.film.use_light_pass; - - if (kernel_data.film.use_light_pass) { - L->indirect = zero_float3(); - L->direct_emission = zero_float3(); - - L->color_diffuse = zero_float3(); - L->color_glossy = zero_float3(); - L->color_transmission = zero_float3(); - - L->direct_diffuse = zero_float3(); - L->direct_glossy = zero_float3(); - L->direct_transmission = zero_float3(); - L->direct_volume = zero_float3(); - - L->indirect_diffuse = zero_float3(); - L->indirect_glossy = zero_float3(); - L->indirect_transmission = zero_float3(); - L->indirect_volume = zero_float3(); - - L->transparent = 0.0f; - L->emission = zero_float3(); - L->background = zero_float3(); - L->ao = zero_float3(); - L->shadow = zero_float3(); - L->mist = 0.0f; - - L->state.diffuse = zero_float3(); - L->state.glossy = zero_float3(); - L->state.transmission = zero_float3(); - L->state.volume = zero_float3(); - L->state.direct = zero_float3(); - } - else -#endif - { - L->transparent = 0.0f; - L->emission = zero_float3(); - } - -#ifdef __SHADOW_TRICKS__ - L->path_total = zero_float3(); - L->path_total_shaded = zero_float3(); - L->shadow_background_color = zero_float3(); - L->shadow_throughput = 0.0f; - L->shadow_transparency = 1.0f; - L->has_shadow_catcher = 0; -#endif - -#ifdef __DENOISING_FEATURES__ - L->denoising_normal = zero_float3(); - L->denoising_albedo = zero_float3(); - L->denoising_depth = 0.0f; -#endif -} - -ccl_device_inline void path_radiance_bsdf_bounce(KernelGlobals *kg, - PathRadianceState *L_state, - ccl_addr_space float3 *throughput, - BsdfEval *bsdf_eval, - float bsdf_pdf, - int bounce, - int bsdf_label) -{ - float inverse_pdf = 1.0f / bsdf_pdf; - -#ifdef __PASSES__ - if (kernel_data.film.use_light_pass) { - if (bounce == 0 && !(bsdf_label & LABEL_TRANSPARENT)) { - /* first on directly visible surface */ - float3 value = *throughput * inverse_pdf; - - L_state->diffuse = bsdf_eval->diffuse * value; - L_state->glossy = bsdf_eval->glossy * value; - L_state->transmission = bsdf_eval->transmission * value; - L_state->volume = bsdf_eval->volume * value; - - *throughput = L_state->diffuse + L_state->glossy + L_state->transmission + L_state->volume; - - L_state->direct = *throughput; - } - else { - /* transparent bounce before first hit, or indirectly visible through BSDF */ - float3 sum = (bsdf_eval_sum(bsdf_eval) + bsdf_eval->transparent) * inverse_pdf; - *throughput *= sum; - } - } - else -#endif - { - *throughput *= bsdf_eval->diffuse * inverse_pdf; - } -} - -#ifdef __CLAMP_SAMPLE__ -ccl_device_forceinline void path_radiance_clamp(KernelGlobals *kg, float3 *L, int bounce) -{ - float limit = (bounce > 0) ? kernel_data.integrator.sample_clamp_indirect : - kernel_data.integrator.sample_clamp_direct; - float sum = reduce_add(fabs(*L)); - if (sum > limit) { - *L *= limit / sum; - } -} - -ccl_device_forceinline void path_radiance_clamp_throughput(KernelGlobals *kg, - float3 *L, - float3 *throughput, - int bounce) -{ - float limit = (bounce > 0) ? kernel_data.integrator.sample_clamp_indirect : - kernel_data.integrator.sample_clamp_direct; - - float sum = reduce_add(fabs(*L)); - if (sum > limit) { - float clamp_factor = limit / sum; - *L *= clamp_factor; - *throughput *= clamp_factor; - } -} - -#endif - -ccl_device_inline void path_radiance_accum_emission(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 value) -{ -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - return; - } -#endif - - float3 contribution = throughput * value; -#ifdef __CLAMP_SAMPLE__ - path_radiance_clamp(kg, &contribution, state->bounce - 1); -#endif - -#ifdef __PASSES__ - if (L->use_light_pass) { - if (state->bounce == 0) - L->emission += contribution; - else if (state->bounce == 1) - L->direct_emission += contribution; - else - L->indirect += contribution; - } - else -#endif - { - L->emission += contribution; - } -} - -ccl_device_inline void path_radiance_accum_ao(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 alpha, - float3 bsdf, - float3 ao) -{ -#ifdef __PASSES__ - /* Store AO pass. */ - if (L->use_light_pass && state->bounce == 0) { - L->ao += alpha * throughput * ao; - } -#endif - -#ifdef __SHADOW_TRICKS__ - /* For shadow catcher, accumulate ratio. */ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - float3 light = throughput * bsdf; - L->path_total += light; - L->path_total_shaded += ao * light; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - return; - } - } -#endif - - float3 contribution = throughput * bsdf * ao; - -#ifdef __PASSES__ - if (L->use_light_pass) { - if (state->bounce == 0) { - /* Directly visible lighting. */ - L->direct_diffuse += contribution; - } - else { - /* Indirectly visible lighting after BSDF bounce. */ - L->indirect += contribution; - } - } - else -#endif - { - L->emission += contribution; - } -} - -ccl_device_inline void path_radiance_accum_total_ao(PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 bsdf) -{ -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - L->path_total += throughput * bsdf; - } -#else - (void)L; - (void)state; - (void)throughput; - (void)bsdf; -#endif -} - -ccl_device_inline void path_radiance_accum_light(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - BsdfEval *bsdf_eval, - float3 shadow, - float shadow_fac, - bool is_lamp) -{ -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - float3 light = throughput * bsdf_eval->sum_no_mis; - L->path_total += light; - L->path_total_shaded += shadow * light; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - return; - } - } -#endif - - float3 shaded_throughput = throughput * shadow; - -#ifdef __PASSES__ - if (L->use_light_pass) { - /* Compute the clamping based on the total contribution. - * The resulting scale is then be applied to all individual components. */ - float3 full_contribution = shaded_throughput * bsdf_eval_sum(bsdf_eval); -# ifdef __CLAMP_SAMPLE__ - path_radiance_clamp_throughput(kg, &full_contribution, &shaded_throughput, state->bounce); -# endif - - if (state->bounce == 0) { - /* directly visible lighting */ - L->direct_diffuse += shaded_throughput * bsdf_eval->diffuse; - L->direct_glossy += shaded_throughput * bsdf_eval->glossy; - L->direct_transmission += shaded_throughput * bsdf_eval->transmission; - L->direct_volume += shaded_throughput * bsdf_eval->volume; - - if (is_lamp) { - L->shadow += shadow * shadow_fac; - } - } - else { - /* indirectly visible lighting after BSDF bounce */ - L->indirect += full_contribution; - } - } - else -#endif - { - float3 contribution = shaded_throughput * bsdf_eval->diffuse; - path_radiance_clamp(kg, &contribution, state->bounce); - L->emission += contribution; - } -} - -ccl_device_inline void path_radiance_accum_total_light(PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - const BsdfEval *bsdf_eval) -{ -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - L->path_total += throughput * bsdf_eval->sum_no_mis; - } -#else - (void)L; - (void)state; - (void)throughput; - (void)bsdf_eval; -#endif -} - -ccl_device_inline void path_radiance_accum_background(KernelGlobals *kg, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 value) -{ - -#ifdef __SHADOW_TRICKS__ - if (state->flag & PATH_RAY_STORE_SHADOW_INFO) { - L->path_total += throughput * value; - L->path_total_shaded += throughput * value * L->shadow_transparency; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - return; - } - } -#endif - - float3 contribution = throughput * value; -#ifdef __CLAMP_SAMPLE__ - path_radiance_clamp(kg, &contribution, state->bounce - 1); -#endif - -#ifdef __PASSES__ - if (L->use_light_pass) { - if (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND) - L->background += contribution; - else if (state->bounce == 1) - L->direct_emission += contribution; - else - L->indirect += contribution; - } - else -#endif - { - L->emission += contribution; - } - -#ifdef __DENOISING_FEATURES__ - L->denoising_albedo += state->denoising_feature_weight * state->denoising_feature_throughput * - value; -#endif /* __DENOISING_FEATURES__ */ -} - -ccl_device_inline void path_radiance_accum_transparent(PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput) -{ - L->transparent += average(throughput); -} - -#ifdef __SHADOW_TRICKS__ -ccl_device_inline void path_radiance_accum_shadowcatcher(PathRadiance *L, - float3 throughput, - float3 background) -{ - L->shadow_throughput += average(throughput); - L->shadow_background_color += throughput * background; - L->has_shadow_catcher = 1; -} -#endif - -ccl_device_inline void path_radiance_sum_indirect(PathRadiance *L) -{ -#ifdef __PASSES__ - /* this division is a bit ugly, but means we only have to keep track of - * only a single throughput further along the path, here we recover just - * the indirect path that is not influenced by any particular BSDF type */ - if (L->use_light_pass) { - L->direct_emission = safe_divide_color(L->direct_emission, L->state.direct); - L->direct_diffuse += L->state.diffuse * L->direct_emission; - L->direct_glossy += L->state.glossy * L->direct_emission; - L->direct_transmission += L->state.transmission * L->direct_emission; - L->direct_volume += L->state.volume * L->direct_emission; - - L->indirect = safe_divide_color(L->indirect, L->state.direct); - L->indirect_diffuse += L->state.diffuse * L->indirect; - L->indirect_glossy += L->state.glossy * L->indirect; - L->indirect_transmission += L->state.transmission * L->indirect; - L->indirect_volume += L->state.volume * L->indirect; - } -#endif -} - -ccl_device_inline void path_radiance_reset_indirect(PathRadiance *L) -{ -#ifdef __PASSES__ - if (L->use_light_pass) { - L->state.diffuse = zero_float3(); - L->state.glossy = zero_float3(); - L->state.transmission = zero_float3(); - L->state.volume = zero_float3(); - - L->direct_emission = zero_float3(); - L->indirect = zero_float3(); - } -#endif -} - -ccl_device_inline void path_radiance_copy_indirect(PathRadiance *L, const PathRadiance *L_src) -{ -#ifdef __PASSES__ - if (L->use_light_pass) { - L->state = L_src->state; - - L->direct_emission = L_src->direct_emission; - L->indirect = L_src->indirect; - } -#endif -} - -#ifdef __SHADOW_TRICKS__ -ccl_device_inline void path_radiance_sum_shadowcatcher(KernelGlobals *kg, - PathRadiance *L, - float3 *L_sum, - float *alpha) -{ - /* Calculate current shadow of the path. */ - float path_total = average(L->path_total); - float shadow; - - if (UNLIKELY(!isfinite_safe(path_total))) { -# ifdef __KERNEL_DEBUG_NAN__ - kernel_assert(!"Non-finite total radiance along the path"); -# endif - shadow = 0.0f; - } - else if (path_total == 0.0f) { - shadow = L->shadow_transparency; - } - else { - float path_total_shaded = average(L->path_total_shaded); - shadow = path_total_shaded / path_total; - } - - /* Calculate final light sum and transparency for shadow catcher object. */ - if (kernel_data.background.transparent) { - *alpha -= L->shadow_throughput * shadow; - } - else { - L->shadow_background_color *= shadow; - *L_sum += L->shadow_background_color; - } -} -#endif - -ccl_device_inline float3 path_radiance_clamp_and_sum(KernelGlobals *kg, - PathRadiance *L, - float *alpha) -{ - float3 L_sum; - /* Light Passes are used */ -#ifdef __PASSES__ - float3 L_direct, L_indirect; - if (L->use_light_pass) { - path_radiance_sum_indirect(L); - - L_direct = L->direct_diffuse + L->direct_glossy + L->direct_transmission + L->direct_volume + - L->emission; - L_indirect = L->indirect_diffuse + L->indirect_glossy + L->indirect_transmission + - L->indirect_volume; - - if (!kernel_data.background.transparent) - L_direct += L->background; - - L_sum = L_direct + L_indirect; - float sum = fabsf((L_sum).x) + fabsf((L_sum).y) + fabsf((L_sum).z); - - /* Reject invalid value */ - if (!isfinite_safe(sum)) { -# ifdef __KERNEL_DEBUG_NAN__ - kernel_assert(!"Non-finite sum in path_radiance_clamp_and_sum!"); -# endif - L_sum = zero_float3(); - - L->direct_diffuse = zero_float3(); - L->direct_glossy = zero_float3(); - L->direct_transmission = zero_float3(); - L->direct_volume = zero_float3(); - - L->indirect_diffuse = zero_float3(); - L->indirect_glossy = zero_float3(); - L->indirect_transmission = zero_float3(); - L->indirect_volume = zero_float3(); - - L->emission = zero_float3(); - } - } - - /* No Light Passes */ - else -#endif - { - L_sum = L->emission; - - /* Reject invalid value */ - float sum = fabsf((L_sum).x) + fabsf((L_sum).y) + fabsf((L_sum).z); - if (!isfinite_safe(sum)) { -#ifdef __KERNEL_DEBUG_NAN__ - kernel_assert(!"Non-finite final sum in path_radiance_clamp_and_sum!"); -#endif - L_sum = zero_float3(); - } - } - - /* Compute alpha. */ - *alpha = 1.0f - L->transparent; - - /* Add shadow catcher contributions. */ -#ifdef __SHADOW_TRICKS__ - if (L->has_shadow_catcher) { - path_radiance_sum_shadowcatcher(kg, L, &L_sum, alpha); - } -#endif /* __SHADOW_TRICKS__ */ - - return L_sum; -} - -ccl_device_inline void path_radiance_split_denoising(KernelGlobals *kg, - PathRadiance *L, - float3 *noisy, - float3 *clean) -{ -#ifdef __PASSES__ - kernel_assert(L->use_light_pass); - - *clean = L->emission + L->background; - *noisy = L->direct_volume + L->indirect_volume; - -# define ADD_COMPONENT(flag, component) \ - if (kernel_data.film.denoising_flags & flag) \ - *clean += component; \ - else \ - *noisy += component; - - ADD_COMPONENT(DENOISING_CLEAN_DIFFUSE_DIR, L->direct_diffuse); - ADD_COMPONENT(DENOISING_CLEAN_DIFFUSE_IND, L->indirect_diffuse); - ADD_COMPONENT(DENOISING_CLEAN_GLOSSY_DIR, L->direct_glossy); - ADD_COMPONENT(DENOISING_CLEAN_GLOSSY_IND, L->indirect_glossy); - ADD_COMPONENT(DENOISING_CLEAN_TRANSMISSION_DIR, L->direct_transmission); - ADD_COMPONENT(DENOISING_CLEAN_TRANSMISSION_IND, L->indirect_transmission); -# undef ADD_COMPONENT -#else - *noisy = L->emission; - *clean = zero_float3(); -#endif - -#ifdef __SHADOW_TRICKS__ - if (L->has_shadow_catcher) { - *noisy += L->shadow_background_color; - } -#endif - - *noisy = ensure_finite3(*noisy); - *clean = ensure_finite3(*clean); -} - -ccl_device_inline void path_radiance_accum_sample(PathRadiance *L, PathRadiance *L_sample) -{ -#ifdef __SPLIT_KERNEL__ -# define safe_float3_add(f, v) \ - do { \ - ccl_global float *p = (ccl_global float *)(&(f)); \ - atomic_add_and_fetch_float(p + 0, (v).x); \ - atomic_add_and_fetch_float(p + 1, (v).y); \ - atomic_add_and_fetch_float(p + 2, (v).z); \ - } while (0) -# define safe_float_add(f, v) atomic_add_and_fetch_float(&(f), (v)) -#else -# define safe_float3_add(f, v) (f) += (v) -# define safe_float_add(f, v) (f) += (v) -#endif /* __SPLIT_KERNEL__ */ - -#ifdef __PASSES__ - safe_float3_add(L->direct_diffuse, L_sample->direct_diffuse); - safe_float3_add(L->direct_glossy, L_sample->direct_glossy); - safe_float3_add(L->direct_transmission, L_sample->direct_transmission); - safe_float3_add(L->direct_volume, L_sample->direct_volume); - - safe_float3_add(L->indirect_diffuse, L_sample->indirect_diffuse); - safe_float3_add(L->indirect_glossy, L_sample->indirect_glossy); - safe_float3_add(L->indirect_transmission, L_sample->indirect_transmission); - safe_float3_add(L->indirect_volume, L_sample->indirect_volume); - - safe_float3_add(L->background, L_sample->background); - safe_float3_add(L->ao, L_sample->ao); - safe_float3_add(L->shadow, L_sample->shadow); - safe_float_add(L->mist, L_sample->mist); -#endif /* __PASSES__ */ - safe_float3_add(L->emission, L_sample->emission); - -#undef safe_float_add -#undef safe_float3_add -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_adaptive_sampling.h b/intern/cycles/kernel/kernel_adaptive_sampling.h deleted file mode 100644 index 98b7bf7e7dc..00000000000 --- a/intern/cycles/kernel/kernel_adaptive_sampling.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_ADAPTIVE_SAMPLING_H__ -#define __KERNEL_ADAPTIVE_SAMPLING_H__ - -CCL_NAMESPACE_BEGIN - -/* Determines whether to continue sampling a given pixel or if it has sufficiently converged. */ - -ccl_device void kernel_do_adaptive_stopping(KernelGlobals *kg, - ccl_global float *buffer, - int sample) -{ - /* TODO Stefan: Is this better in linear, sRGB or something else? */ - float4 I = *((ccl_global float4 *)buffer); - float4 A = *(ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer); - /* The per pixel error as seen in section 2.1 of - * "A hierarchical automatic stopping condition for Monte Carlo global illumination" - * A small epsilon is added to the divisor to prevent division by zero. */ - float error = (fabsf(I.x - A.x) + fabsf(I.y - A.y) + fabsf(I.z - A.z)) / - (sample * 0.0001f + sqrtf(I.x + I.y + I.z)); - if (error < kernel_data.integrator.adaptive_threshold * (float)sample) { - /* Set the fourth component to non-zero value to indicate that this pixel has converged. */ - buffer[kernel_data.film.pass_adaptive_aux_buffer + 3] += 1.0f; - } -} - -/* Adjust the values of an adaptively sampled pixel. */ - -ccl_device void kernel_adaptive_post_adjust(KernelGlobals *kg, - ccl_global float *buffer, - float sample_multiplier) -{ - *(ccl_global float4 *)(buffer) *= sample_multiplier; - - /* Scale the aux pass too, this is necessary for progressive rendering to work properly. */ - kernel_assert(kernel_data.film.pass_adaptive_aux_buffer); - *(ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer) *= sample_multiplier; - -#ifdef __PASSES__ - int flag = kernel_data.film.pass_flag; - - if (flag & PASSMASK(NORMAL)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_normal) *= sample_multiplier; - - if (flag & PASSMASK(UV)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_uv) *= sample_multiplier; - - if (flag & PASSMASK(MOTION)) { - *(ccl_global float4 *)(buffer + kernel_data.film.pass_motion) *= sample_multiplier; - *(ccl_global float *)(buffer + kernel_data.film.pass_motion_weight) *= sample_multiplier; - } - - if (kernel_data.film.use_light_pass) { - int light_flag = kernel_data.film.light_pass_flag; - - if (light_flag & PASSMASK(MIST)) - *(ccl_global float *)(buffer + kernel_data.film.pass_mist) *= sample_multiplier; - - /* Shadow pass omitted on purpose. It has its own scale parameter. */ - - if (light_flag & PASSMASK(DIFFUSE_INDIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_diffuse_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(GLOSSY_INDIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_glossy_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(TRANSMISSION_INDIRECT)) - *(ccl_global float3 *)(buffer + - kernel_data.film.pass_transmission_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(VOLUME_INDIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_volume_indirect) *= sample_multiplier; - if (light_flag & PASSMASK(DIFFUSE_DIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_diffuse_direct) *= sample_multiplier; - if (light_flag & PASSMASK(GLOSSY_DIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_glossy_direct) *= sample_multiplier; - if (light_flag & PASSMASK(TRANSMISSION_DIRECT)) - *(ccl_global float3 *)(buffer + - kernel_data.film.pass_transmission_direct) *= sample_multiplier; - if (light_flag & PASSMASK(VOLUME_DIRECT)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_volume_direct) *= sample_multiplier; - - if (light_flag & PASSMASK(EMISSION)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_emission) *= sample_multiplier; - if (light_flag & PASSMASK(BACKGROUND)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_background) *= sample_multiplier; - if (light_flag & PASSMASK(AO)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_ao) *= sample_multiplier; - - if (light_flag & PASSMASK(DIFFUSE_COLOR)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_diffuse_color) *= sample_multiplier; - if (light_flag & PASSMASK(GLOSSY_COLOR)) - *(ccl_global float3 *)(buffer + kernel_data.film.pass_glossy_color) *= sample_multiplier; - if (light_flag & PASSMASK(TRANSMISSION_COLOR)) - *(ccl_global float3 *)(buffer + - kernel_data.film.pass_transmission_color) *= sample_multiplier; - } -#endif - -#ifdef __DENOISING_FEATURES__ - -# define scale_float3_variance(buffer, offset, scale) \ - *(buffer + offset) *= scale; \ - *(buffer + offset + 1) *= scale; \ - *(buffer + offset + 2) *= scale; \ - *(buffer + offset + 3) *= scale * scale; \ - *(buffer + offset + 4) *= scale * scale; \ - *(buffer + offset + 5) *= scale * scale; - -# define scale_shadow_variance(buffer, offset, scale) \ - *(buffer + offset) *= scale; \ - *(buffer + offset + 1) *= scale; \ - *(buffer + offset + 2) *= scale * scale; - - if (kernel_data.film.pass_denoising_data) { - scale_shadow_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_SHADOW_A, sample_multiplier); - scale_shadow_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_SHADOW_B, sample_multiplier); - if (kernel_data.film.pass_denoising_clean) { - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_COLOR, sample_multiplier); - *(buffer + kernel_data.film.pass_denoising_clean) *= sample_multiplier; - *(buffer + kernel_data.film.pass_denoising_clean + 1) *= sample_multiplier; - *(buffer + kernel_data.film.pass_denoising_clean + 2) *= sample_multiplier; - } - else { - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_COLOR, sample_multiplier); - } - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_NORMAL, sample_multiplier); - scale_float3_variance( - buffer, kernel_data.film.pass_denoising_data + DENOISING_PASS_ALBEDO, sample_multiplier); - *(buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_DEPTH) *= sample_multiplier; - *(buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_DEPTH + - 1) *= sample_multiplier * sample_multiplier; - } -#endif /* __DENOISING_FEATURES__ */ - - /* Cryptomatte. */ - if (kernel_data.film.cryptomatte_passes) { - int num_slots = 0; - num_slots += (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) ? 1 : 0; - num_slots += (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) ? 1 : 0; - num_slots += (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) ? 1 : 0; - num_slots = num_slots * 2 * kernel_data.film.cryptomatte_depth; - ccl_global float2 *id_buffer = (ccl_global float2 *)(buffer + - kernel_data.film.pass_cryptomatte); - for (int slot = 0; slot < num_slots; slot++) { - id_buffer[slot].y *= sample_multiplier; - } - } - - /* AOVs. */ - for (int i = 0; i < kernel_data.film.pass_aov_value_num; i++) { - *(buffer + kernel_data.film.pass_aov_value + i) *= sample_multiplier; - } - for (int i = 0; i < kernel_data.film.pass_aov_color_num; i++) { - *((ccl_global float4 *)(buffer + kernel_data.film.pass_aov_color) + i) *= sample_multiplier; - } -} - -/* This is a simple box filter in two passes. - * When a pixel demands more adaptive samples, let its neighboring pixels draw more samples too. */ - -ccl_device bool kernel_do_adaptive_filter_x(KernelGlobals *kg, int y, ccl_global WorkTile *tile) -{ - bool any = false; - bool prev = false; - for (int x = tile->x; x < tile->x + tile->w; ++x) { - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w == 0.0f) { - any = true; - if (x > tile->x && !prev) { - index = index - 1; - buffer = tile->buffer + index * kernel_data.film.pass_stride; - aux = (ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer); - (*aux).w = 0.0f; - } - prev = true; - } - else { - if (prev) { - (*aux).w = 0.0f; - } - prev = false; - } - } - return any; -} - -ccl_device bool kernel_do_adaptive_filter_y(KernelGlobals *kg, int x, ccl_global WorkTile *tile) -{ - bool prev = false; - bool any = false; - for (int y = tile->y; y < tile->y + tile->h; ++y) { - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w == 0.0f) { - any = true; - if (y > tile->y && !prev) { - index = index - tile->stride; - buffer = tile->buffer + index * kernel_data.film.pass_stride; - aux = (ccl_global float4 *)(buffer + kernel_data.film.pass_adaptive_aux_buffer); - (*aux).w = 0.0f; - } - prev = true; - } - else { - if (prev) { - (*aux).w = 0.0f; - } - prev = false; - } - } - return any; -} - -CCL_NAMESPACE_END - -#endif /* __KERNEL_ADAPTIVE_SAMPLING_H__ */ diff --git a/intern/cycles/kernel/kernel_bake.h b/intern/cycles/kernel/kernel_bake.h deleted file mode 100644 index 7da890b908d..00000000000 --- a/intern/cycles/kernel/kernel_bake.h +++ /dev/null @@ -1,515 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __BAKING__ - -ccl_device_noinline void compute_light_pass( - KernelGlobals *kg, ShaderData *sd, PathRadiance *L, uint rng_hash, int pass_filter, int sample) -{ - kernel_assert(kernel_data.film.use_light_pass); - - float3 throughput = one_float3(); - - /* Emission and indirect shader data memory used by various functions. */ - ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - ShaderData indirect_sd; - - /* Init radiance. */ - path_radiance_init(kg, L); - - /* Init path state. */ - PathState state; - path_state_init(kg, emission_sd, &state, rng_hash, sample, NULL); - - /* Evaluate surface shader. */ - shader_eval_surface(kg, sd, &state, NULL, state.flag); - - /* TODO: disable more closures we don't need besides transparent. */ - shader_bsdf_disable_transparency(kg, sd); - - /* Init ray. */ - Ray ray; - ray.P = sd->P + sd->Ng; - ray.D = -sd->Ng; - ray.t = FLT_MAX; -# ifdef __CAMERA_MOTION__ - ray.time = 0.5f; -# endif - -# ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched) { - /* regular path tracer */ -# endif - - /* sample ambient occlusion */ - if (pass_filter & BAKE_FILTER_AO) { - kernel_path_ao(kg, sd, emission_sd, L, &state, throughput, shader_bsdf_alpha(kg, sd)); - } - - /* sample emission */ - if ((pass_filter & BAKE_FILTER_EMISSION) && (sd->flag & SD_EMISSION)) { - float3 emission = indirect_primitive_emission(kg, sd, 0.0f, state.flag, state.ray_pdf); - path_radiance_accum_emission(kg, L, &state, throughput, emission); - } - - bool is_sss_sample = false; - -# ifdef __SUBSURFACE__ - /* sample subsurface scattering */ - if ((pass_filter & BAKE_FILTER_DIFFUSE) && (sd->flag & SD_BSSRDF)) { - /* When mixing BSSRDF and BSDF closures we should skip BSDF lighting - * if scattering was successful. */ - SubsurfaceIndirectRays ss_indirect; - kernel_path_subsurface_init_indirect(&ss_indirect); - if (kernel_path_subsurface_scatter( - kg, sd, emission_sd, L, &state, &ray, &throughput, &ss_indirect)) { - while (ss_indirect.num_rays) { - kernel_path_subsurface_setup_indirect(kg, &ss_indirect, &state, &ray, L, &throughput); - kernel_path_indirect( - kg, &indirect_sd, emission_sd, &ray, throughput, &state, L, sd->object); - } - is_sss_sample = true; - } - } -# endif - - /* sample light and BSDF */ - if (!is_sss_sample && (pass_filter & (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT))) { - kernel_path_surface_connect_light(kg, sd, emission_sd, throughput, &state, L); - - if (kernel_path_surface_bounce(kg, sd, &throughput, &state, &L->state, &ray)) { -# ifdef __LAMP_MIS__ - state.ray_t = 0.0f; -# endif - /* compute indirect light */ - kernel_path_indirect( - kg, &indirect_sd, emission_sd, &ray, throughput, &state, L, sd->object); - - /* sum and reset indirect light pass variables for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - } - } -# ifdef __BRANCHED_PATH__ - } - else { - /* branched path tracer */ - - /* sample ambient occlusion */ - if (pass_filter & BAKE_FILTER_AO) { - kernel_branched_path_ao(kg, sd, emission_sd, L, &state, throughput); - } - - /* sample emission */ - if ((pass_filter & BAKE_FILTER_EMISSION) && (sd->flag & SD_EMISSION)) { - float3 emission = indirect_primitive_emission(kg, sd, 0.0f, state.flag, state.ray_pdf); - path_radiance_accum_emission(kg, L, &state, throughput, emission); - } - -# ifdef __SUBSURFACE__ - /* sample subsurface scattering */ - if ((pass_filter & BAKE_FILTER_DIFFUSE) && (sd->flag & SD_BSSRDF)) { - /* When mixing BSSRDF and BSDF closures we should skip BSDF lighting - * if scattering was successful. */ - kernel_branched_path_subsurface_scatter( - kg, sd, &indirect_sd, emission_sd, L, &state, &ray, throughput); - } -# endif - - /* sample light and BSDF */ - if (pass_filter & (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT)) { -# if defined(__EMISSION__) - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = kernel_data.integrator.sample_all_lights_direct; - kernel_branched_path_surface_connect_light( - kg, sd, emission_sd, &state, throughput, 1.0f, L, all); - } -# endif - - /* indirect light */ - kernel_branched_path_surface_indirect_light( - kg, sd, &indirect_sd, emission_sd, throughput, 1.0f, &state, L); - } - } -# endif -} - -/* this helps with AA but it's not the real solution as it does not AA the geometry - * but it's better than nothing, thus committed */ -ccl_device_inline float bake_clamp_mirror_repeat(float u, float max) -{ - /* use mirror repeat (like opengl texture) so that if the barycentric - * coordinate goes past the end of the triangle it is not always clamped - * to the same value, gives ugly patterns */ - u /= max; - float fu = floorf(u); - u = u - fu; - - return ((((int)fu) & 1) ? 1.0f - u : u) * max; -} - -ccl_device_inline float3 kernel_bake_shader_bsdf(KernelGlobals *kg, - ShaderData *sd, - const ShaderEvalType type) -{ - switch (type) { - case SHADER_EVAL_DIFFUSE: - return shader_bsdf_diffuse(kg, sd); - case SHADER_EVAL_GLOSSY: - return shader_bsdf_glossy(kg, sd); - case SHADER_EVAL_TRANSMISSION: - return shader_bsdf_transmission(kg, sd); - default: - kernel_assert(!"Unknown bake type passed to BSDF evaluate"); - return zero_float3(); - } -} - -ccl_device float3 kernel_bake_evaluate_direct_indirect(KernelGlobals *kg, - ShaderData *sd, - PathState *state, - float3 direct, - float3 indirect, - const ShaderEvalType type, - const int pass_filter) -{ - float3 color; - const bool is_color = (pass_filter & BAKE_FILTER_COLOR) != 0; - const bool is_direct = (pass_filter & BAKE_FILTER_DIRECT) != 0; - const bool is_indirect = (pass_filter & BAKE_FILTER_INDIRECT) != 0; - float3 out = zero_float3(); - - if (is_color) { - if (is_direct || is_indirect) { - /* Leave direct and diffuse channel colored. */ - color = one_float3(); - } - else { - /* surface color of the pass only */ - shader_eval_surface(kg, sd, state, NULL, 0); - return kernel_bake_shader_bsdf(kg, sd, type); - } - } - else { - shader_eval_surface(kg, sd, state, NULL, 0); - color = kernel_bake_shader_bsdf(kg, sd, type); - } - - if (is_direct) { - out += safe_divide_even_color(direct, color); - } - - if (is_indirect) { - out += safe_divide_even_color(indirect, color); - } - - return out; -} - -ccl_device void kernel_bake_evaluate( - KernelGlobals *kg, ccl_global float *buffer, int sample, int x, int y, int offset, int stride) -{ - /* Setup render buffers. */ - const int index = offset + x + y * stride; - const int pass_stride = kernel_data.film.pass_stride; - buffer += index * pass_stride; - - ccl_global float *primitive = buffer + kernel_data.film.pass_bake_primitive; - ccl_global float *differential = buffer + kernel_data.film.pass_bake_differential; - ccl_global float *output = buffer + kernel_data.film.pass_combined; - - int seed = __float_as_uint(primitive[0]); - int prim = __float_as_uint(primitive[1]); - if (prim == -1) - return; - - prim += kernel_data.bake.tri_offset; - - /* Random number generator. */ - uint rng_hash = hash_uint(seed) ^ kernel_data.integrator.seed; - int num_samples = kernel_data.integrator.aa_samples; - - float filter_x, filter_y; - if (sample == 0) { - filter_x = filter_y = 0.5f; - } - else { - path_rng_2D(kg, rng_hash, sample, num_samples, PRNG_FILTER_U, &filter_x, &filter_y); - } - - /* Barycentric UV with sub-pixel offset. */ - float u = primitive[2]; - float v = primitive[3]; - - float dudx = differential[0]; - float dudy = differential[1]; - float dvdx = differential[2]; - float dvdy = differential[3]; - - if (sample > 0) { - u = bake_clamp_mirror_repeat(u + dudx * (filter_x - 0.5f) + dudy * (filter_y - 0.5f), 1.0f); - v = bake_clamp_mirror_repeat(v + dvdx * (filter_x - 0.5f) + dvdy * (filter_y - 0.5f), - 1.0f - u); - } - - /* Shader data setup. */ - int object = kernel_data.bake.object_index; - int shader; - float3 P, Ng; - - triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); - - ShaderData sd; - shader_setup_from_sample( - kg, - &sd, - P, - Ng, - Ng, - shader, - object, - prim, - u, - v, - 1.0f, - 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), - LAMP_NONE); - sd.I = sd.N; - - /* Setup differentials. */ - sd.dP.dx = sd.dPdu * dudx + sd.dPdv * dvdx; - sd.dP.dy = sd.dPdu * dudy + sd.dPdv * dvdy; - sd.du.dx = dudx; - sd.du.dy = dudy; - sd.dv.dx = dvdx; - sd.dv.dy = dvdy; - - /* Set RNG state for shaders that use sampling. */ - PathState state = {0}; - state.rng_hash = rng_hash; - state.rng_offset = 0; - state.sample = sample; - state.num_samples = num_samples; - state.min_ray_pdf = FLT_MAX; - - /* Light passes if we need more than color. */ - PathRadiance L; - int pass_filter = kernel_data.bake.pass_filter; - - if (kernel_data.bake.pass_filter & ~BAKE_FILTER_COLOR) - compute_light_pass(kg, &sd, &L, rng_hash, pass_filter, sample); - - float3 out = zero_float3(); - - ShaderEvalType type = (ShaderEvalType)kernel_data.bake.type; - switch (type) { - /* data passes */ - case SHADER_EVAL_NORMAL: - case SHADER_EVAL_ROUGHNESS: - case SHADER_EVAL_EMISSION: { - if (type != SHADER_EVAL_NORMAL || (sd.flag & SD_HAS_BUMP)) { - int path_flag = (type == SHADER_EVAL_EMISSION) ? PATH_RAY_EMISSION : 0; - shader_eval_surface(kg, &sd, &state, NULL, path_flag); - } - - if (type == SHADER_EVAL_NORMAL) { - float3 N = sd.N; - if (sd.flag & SD_HAS_BUMP) { - N = shader_bsdf_average_normal(kg, &sd); - } - - /* encoding: normal = (2 * color) - 1 */ - out = N * 0.5f + make_float3(0.5f, 0.5f, 0.5f); - } - else if (type == SHADER_EVAL_ROUGHNESS) { - float roughness = shader_bsdf_average_roughness(&sd); - out = make_float3(roughness, roughness, roughness); - } - else { - out = shader_emissive_eval(&sd); - } - break; - } - case SHADER_EVAL_UV: { - out = primitive_uv(kg, &sd); - break; - } -# ifdef __PASSES__ - /* light passes */ - case SHADER_EVAL_AO: { - out = L.ao; - break; - } - case SHADER_EVAL_COMBINED: { - if ((pass_filter & BAKE_FILTER_COMBINED) == BAKE_FILTER_COMBINED) { - float alpha; - out = path_radiance_clamp_and_sum(kg, &L, &alpha); - break; - } - - if ((pass_filter & BAKE_FILTER_DIFFUSE_DIRECT) == BAKE_FILTER_DIFFUSE_DIRECT) - out += L.direct_diffuse; - if ((pass_filter & BAKE_FILTER_DIFFUSE_INDIRECT) == BAKE_FILTER_DIFFUSE_INDIRECT) - out += L.indirect_diffuse; - - if ((pass_filter & BAKE_FILTER_GLOSSY_DIRECT) == BAKE_FILTER_GLOSSY_DIRECT) - out += L.direct_glossy; - if ((pass_filter & BAKE_FILTER_GLOSSY_INDIRECT) == BAKE_FILTER_GLOSSY_INDIRECT) - out += L.indirect_glossy; - - if ((pass_filter & BAKE_FILTER_TRANSMISSION_DIRECT) == BAKE_FILTER_TRANSMISSION_DIRECT) - out += L.direct_transmission; - if ((pass_filter & BAKE_FILTER_TRANSMISSION_INDIRECT) == BAKE_FILTER_TRANSMISSION_INDIRECT) - out += L.indirect_transmission; - - if ((pass_filter & BAKE_FILTER_EMISSION) != 0) - out += L.emission; - - break; - } - case SHADER_EVAL_SHADOW: { - out = L.shadow; - break; - } - case SHADER_EVAL_DIFFUSE: { - out = kernel_bake_evaluate_direct_indirect( - kg, &sd, &state, L.direct_diffuse, L.indirect_diffuse, type, pass_filter); - break; - } - case SHADER_EVAL_GLOSSY: { - out = kernel_bake_evaluate_direct_indirect( - kg, &sd, &state, L.direct_glossy, L.indirect_glossy, type, pass_filter); - break; - } - case SHADER_EVAL_TRANSMISSION: { - out = kernel_bake_evaluate_direct_indirect( - kg, &sd, &state, L.direct_transmission, L.indirect_transmission, type, pass_filter); - break; - } -# endif - - /* extra */ - case SHADER_EVAL_ENVIRONMENT: { - /* setup ray */ - Ray ray; - - ray.P = zero_float3(); - ray.D = normalize(P); - ray.t = 0.0f; -# ifdef __CAMERA_MOTION__ - ray.time = 0.5f; -# endif - -# ifdef __RAY_DIFFERENTIALS__ - ray.dD = differential3_zero(); - ray.dP = differential3_zero(); -# endif - - /* setup shader data */ - shader_setup_from_background(kg, &sd, &ray); - - /* evaluate */ - int path_flag = 0; /* we can't know which type of BSDF this is for */ - shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION); - out = shader_background_eval(&sd); - break; - } - default: { - /* no real shader, returning the position of the verts for debugging */ - out = normalize(P); - break; - } - } - - /* write output */ - const float4 result = make_float4(out.x, out.y, out.z, 1.0f); - kernel_write_pass_float4(output, result); -} - -#endif /* __BAKING__ */ - -ccl_device void kernel_displace_evaluate(KernelGlobals *kg, - ccl_global uint4 *input, - ccl_global float4 *output, - int i) -{ - ShaderData sd; - PathState state = {0}; - uint4 in = input[i]; - - /* setup shader data */ - int object = in.x; - int prim = in.y; - float u = __uint_as_float(in.z); - float v = __uint_as_float(in.w); - - shader_setup_from_displace(kg, &sd, object, prim, u, v); - - /* evaluate */ - float3 P = sd.P; - shader_eval_displacement(kg, &sd, &state); - float3 D = sd.P - P; - - object_inverse_dir_transform(kg, &sd, &D); - - /* write output */ - output[i] += make_float4(D.x, D.y, D.z, 0.0f); -} - -ccl_device void kernel_background_evaluate(KernelGlobals *kg, - ccl_global uint4 *input, - ccl_global float4 *output, - int i) -{ - ShaderData sd; - PathState state = {0}; - uint4 in = input[i]; - - /* setup ray */ - Ray ray; - float u = __uint_as_float(in.x); - float v = __uint_as_float(in.y); - - ray.P = zero_float3(); - ray.D = equirectangular_to_direction(u, v); - ray.t = 0.0f; -#ifdef __CAMERA_MOTION__ - ray.time = 0.5f; -#endif - -#ifdef __RAY_DIFFERENTIALS__ - ray.dD = differential3_zero(); - ray.dP = differential3_zero(); -#endif - - /* setup shader data */ - shader_setup_from_background(kg, &sd, &ray); - - /* evaluate */ - int path_flag = 0; /* we can't know which type of BSDF this is for */ - shader_eval_surface(kg, &sd, &state, NULL, path_flag | PATH_RAY_EMISSION); - float3 color = shader_background_eval(&sd); - - /* write output */ - output[i] += make_float4(color.x, color.y, color.z, 0.0f); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_compat_opencl.h b/intern/cycles/kernel/kernel_compat_opencl.h deleted file mode 100644 index 4a9304a134c..00000000000 --- a/intern/cycles/kernel/kernel_compat_opencl.h +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_COMPAT_OPENCL_H__ -#define __KERNEL_COMPAT_OPENCL_H__ - -#define __KERNEL_GPU__ -#define __KERNEL_OPENCL__ - -/* no namespaces in opencl */ -#define CCL_NAMESPACE_BEGIN -#define CCL_NAMESPACE_END - -#ifdef __CL_NOINLINE__ -# define ccl_noinline __attribute__((noinline)) -#else -# define ccl_noinline -#endif - -/* in opencl all functions are device functions, so leave this empty */ -#define ccl_device -#define ccl_device_inline ccl_device -#define ccl_device_forceinline ccl_device -#define ccl_device_noinline ccl_device ccl_noinline -#define ccl_device_noinline_cpu ccl_device -#define ccl_may_alias -#define ccl_static_constant static __constant -#define ccl_constant __constant -#define ccl_global __global -#define ccl_local __local -#define ccl_local_param __local -#define ccl_private __private -#define ccl_restrict restrict -#define ccl_ref -#define ccl_align(n) __attribute__((aligned(n))) -#define ccl_optional_struct_init - -#if __OPENCL_VERSION__ >= 200 && !defined(__NV_CL_C_VERSION) -# define ccl_loop_no_unroll __attribute__((opencl_unroll_hint(1))) -#else -# define ccl_loop_no_unroll -#endif - -#ifdef __SPLIT_KERNEL__ -# define ccl_addr_space __global -#else -# define ccl_addr_space -#endif - -#define ATTR_FALLTHROUGH - -#define ccl_local_id(d) get_local_id(d) -#define ccl_global_id(d) get_global_id(d) - -#define ccl_local_size(d) get_local_size(d) -#define ccl_global_size(d) get_global_size(d) - -#define ccl_group_id(d) get_group_id(d) -#define ccl_num_groups(d) get_num_groups(d) - -/* Selective nodes compilation. */ -#ifndef __NODES_MAX_GROUP__ -# define __NODES_MAX_GROUP__ NODE_GROUP_LEVEL_MAX -#endif -#ifndef __NODES_FEATURES__ -# define __NODES_FEATURES__ NODE_FEATURE_ALL -#endif - -/* no assert in opencl */ -#define kernel_assert(cond) - -/* make_type definitions with opencl style element initializers */ -#ifdef make_float2 -# undef make_float2 -#endif -#ifdef make_float3 -# undef make_float3 -#endif -#ifdef make_float4 -# undef make_float4 -#endif -#ifdef make_int2 -# undef make_int2 -#endif -#ifdef make_int3 -# undef make_int3 -#endif -#ifdef make_int4 -# undef make_int4 -#endif -#ifdef make_uchar4 -# undef make_uchar4 -#endif - -#define make_float2(x, y) ((float2)(x, y)) -#define make_float3(x, y, z) ((float3)(x, y, z)) -#define make_float4(x, y, z, w) ((float4)(x, y, z, w)) -#define make_int2(x, y) ((int2)(x, y)) -#define make_int3(x, y, z) ((int3)(x, y, z)) -#define make_int4(x, y, z, w) ((int4)(x, y, z, w)) -#define make_uchar4(x, y, z, w) ((uchar4)(x, y, z, w)) - -/* math functions */ -#define __uint_as_float(x) as_float(x) -#define __float_as_uint(x) as_uint(x) -#define __int_as_float(x) as_float(x) -#define __float_as_int(x) as_int(x) -#define powf(x, y) pow(((float)(x)), ((float)(y))) -#define fabsf(x) fabs(((float)(x))) -#define copysignf(x, y) copysign(((float)(x)), ((float)(y))) -#define asinf(x) asin(((float)(x))) -#define acosf(x) acos(((float)(x))) -#define atanf(x) atan(((float)(x))) -#define floorf(x) floor(((float)(x))) -#define ceilf(x) ceil(((float)(x))) -#define hypotf(x, y) hypot(((float)(x)), ((float)(y))) -#define atan2f(x, y) atan2(((float)(x)), ((float)(y))) -#define fmaxf(x, y) fmax(((float)(x)), ((float)(y))) -#define fminf(x, y) fmin(((float)(x)), ((float)(y))) -#define fmodf(x, y) fmod((float)(x), (float)(y)) -#define sinhf(x) sinh(((float)(x))) -#define coshf(x) cosh(((float)(x))) -#define tanhf(x) tanh(((float)(x))) - -/* Use native functions with possibly lower precision for performance, - * no issues found so far. */ -#if 1 -# define sinf(x) native_sin(((float)(x))) -# define cosf(x) native_cos(((float)(x))) -# define tanf(x) native_tan(((float)(x))) -# define expf(x) native_exp(((float)(x))) -# define sqrtf(x) native_sqrt(((float)(x))) -# define logf(x) native_log(((float)(x))) -# define rcp(x) native_recip(x) -#else -# define sinf(x) sin(((float)(x))) -# define cosf(x) cos(((float)(x))) -# define tanf(x) tan(((float)(x))) -# define expf(x) exp(((float)(x))) -# define sqrtf(x) sqrt(((float)(x))) -# define logf(x) log(((float)(x))) -# define rcp(x) recip(x) -#endif - -/* data lookup defines */ -#define kernel_data (*kg->data) -#define kernel_tex_array(tex) \ - ((const ccl_global tex##_t *)(kg->buffers[kg->tex.cl_buffer] + kg->tex.data)) -#define kernel_tex_fetch(tex, index) kernel_tex_array(tex)[(index)] - -/* define NULL */ -#ifndef NULL -# define NULL ((void *)0) -#endif - -/* enable extensions */ -#ifdef __KERNEL_CL_KHR_FP16__ -# pragma OPENCL EXTENSION cl_khr_fp16 : enable -#endif - -#include "util/util_half.h" -#include "util/util_types.h" - -#endif /* __KERNEL_COMPAT_OPENCL_H__ */ diff --git a/intern/cycles/kernel/kernel_emission.h b/intern/cycles/kernel/kernel_emission.h deleted file mode 100644 index aebf2ec8e28..00000000000 --- a/intern/cycles/kernel/kernel_emission.h +++ /dev/null @@ -1,344 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Direction Emission */ -ccl_device_noinline_cpu float3 direct_emissive_eval(KernelGlobals *kg, - ShaderData *emission_sd, - LightSample *ls, - ccl_addr_space PathState *state, - float3 I, - differential3 dI, - float t, - float time) -{ - /* setup shading at emitter */ - float3 eval = zero_float3(); - - if (shader_constant_emission_eval(kg, ls->shader, &eval)) { - if ((ls->prim != PRIM_NONE) && dot(ls->Ng, I) < 0.0f) { - ls->Ng = -ls->Ng; - } - } - else { - /* Setup shader data and call shader_eval_surface once, better - * for GPU coherence and compile times. */ -#ifdef __BACKGROUND_MIS__ - if (ls->type == LIGHT_BACKGROUND) { - Ray ray; - ray.D = ls->D; - ray.P = ls->P; - ray.t = 1.0f; - ray.time = time; - ray.dP = differential3_zero(); - ray.dD = dI; - - shader_setup_from_background(kg, emission_sd, &ray); - } - else -#endif - { - shader_setup_from_sample(kg, - emission_sd, - ls->P, - ls->Ng, - I, - ls->shader, - ls->object, - ls->prim, - ls->u, - ls->v, - t, - time, - false, - ls->lamp); - - ls->Ng = emission_sd->Ng; - } - - /* No proper path flag, we're evaluating this for all closures. that's - * weak but we'd have to do multiple evaluations otherwise. */ - path_state_modify_bounce(state, true); - shader_eval_surface(kg, emission_sd, state, NULL, PATH_RAY_EMISSION); - path_state_modify_bounce(state, false); - - /* Evaluate closures. */ -#ifdef __BACKGROUND_MIS__ - if (ls->type == LIGHT_BACKGROUND) { - eval = shader_background_eval(emission_sd); - } - else -#endif - { - eval = shader_emissive_eval(emission_sd); - } - } - - eval *= ls->eval_fac; - - if (ls->lamp != LAMP_NONE) { - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, ls->lamp); - eval *= make_float3(klight->strength[0], klight->strength[1], klight->strength[2]); - } - - return eval; -} - -ccl_device_noinline_cpu bool direct_emission(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - LightSample *ls, - ccl_addr_space PathState *state, - Ray *ray, - BsdfEval *eval, - bool *is_lamp, - float rand_terminate) -{ - if (ls->pdf == 0.0f) - return false; - - /* todo: implement */ - differential3 dD = differential3_zero(); - - /* evaluate closure */ - - float3 light_eval = direct_emissive_eval( - kg, emission_sd, ls, state, -ls->D, dD, ls->t, sd->time); - - if (is_zero(light_eval)) - return false; - - /* evaluate BSDF at shading point */ - -#ifdef __VOLUME__ - if (sd->prim != PRIM_NONE) - shader_bsdf_eval(kg, sd, ls->D, eval, ls->pdf, ls->shader & SHADER_USE_MIS); - else { - float bsdf_pdf; - shader_volume_phase_eval(kg, sd, ls->D, eval, &bsdf_pdf); - if (ls->shader & SHADER_USE_MIS) { - /* Multiple importance sampling. */ - float mis_weight = power_heuristic(ls->pdf, bsdf_pdf); - light_eval *= mis_weight; - } - } -#else - shader_bsdf_eval(kg, sd, ls->D, eval, ls->pdf, ls->shader & SHADER_USE_MIS); -#endif - - bsdf_eval_mul3(eval, light_eval / ls->pdf); - -#ifdef __PASSES__ - /* use visibility flag to skip lights */ - if (ls->shader & SHADER_EXCLUDE_ANY) { - if (ls->shader & SHADER_EXCLUDE_DIFFUSE) - eval->diffuse = zero_float3(); - if (ls->shader & SHADER_EXCLUDE_GLOSSY) - eval->glossy = zero_float3(); - if (ls->shader & SHADER_EXCLUDE_TRANSMIT) - eval->transmission = zero_float3(); - if (ls->shader & SHADER_EXCLUDE_SCATTER) - eval->volume = zero_float3(); - } -#endif - - if (bsdf_eval_is_zero(eval)) - return false; - - if (kernel_data.integrator.light_inv_rr_threshold > 0.0f -#ifdef __SHADOW_TRICKS__ - && (state->flag & PATH_RAY_SHADOW_CATCHER) == 0 -#endif - ) { - float probability = max3(fabs(bsdf_eval_sum(eval))) * - kernel_data.integrator.light_inv_rr_threshold; - if (probability < 1.0f) { - if (rand_terminate >= probability) { - return false; - } - bsdf_eval_mul(eval, 1.0f / probability); - } - } - - if (ls->shader & SHADER_CAST_SHADOW) { - /* setup ray */ - ray->P = ray_offset_shadow(kg, sd, ls->D); - - if (ls->t == FLT_MAX) { - /* distant light */ - ray->D = ls->D; - ray->t = ls->t; - } - else { - /* other lights, avoid self-intersection */ - ray->D = ray_offset(ls->P, ls->Ng) - ray->P; - ray->D = normalize_len(ray->D, &ray->t); - } - - ray->dP = sd->dP; - ray->dD = differential3_zero(); - } - else { - /* signal to not cast shadow ray */ - ray->t = 0.0f; - } - - /* return if it's a lamp for shadow pass */ - *is_lamp = (ls->prim == PRIM_NONE && ls->type != LIGHT_BACKGROUND); - - return true; -} - -/* Indirect Primitive Emission */ - -ccl_device_noinline_cpu float3 indirect_primitive_emission( - KernelGlobals *kg, ShaderData *sd, float t, int path_flag, float bsdf_pdf) -{ - /* evaluate emissive closure */ - float3 L = shader_emissive_eval(sd); - -#ifdef __HAIR__ - if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS) && - (sd->type & PRIMITIVE_ALL_TRIANGLE)) -#else - if (!(path_flag & PATH_RAY_MIS_SKIP) && (sd->flag & SD_USE_MIS)) -#endif - { - /* multiple importance sampling, get triangle light pdf, - * and compute weight with respect to BSDF pdf */ - float pdf = triangle_light_pdf(kg, sd, t); - float mis_weight = power_heuristic(bsdf_pdf, pdf); - - return L * mis_weight; - } - - return L; -} - -/* Indirect Lamp Emission */ - -ccl_device_noinline_cpu void indirect_lamp_emission(KernelGlobals *kg, - ShaderData *emission_sd, - ccl_addr_space PathState *state, - PathRadiance *L, - Ray *ray, - float3 throughput) -{ - for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { - LightSample ls ccl_optional_struct_init; - - if (!lamp_light_eval(kg, lamp, ray->P, ray->D, ray->t, &ls)) - continue; - -#ifdef __PASSES__ - /* use visibility flag to skip lights */ - if (ls.shader & SHADER_EXCLUDE_ANY) { - if (((ls.shader & SHADER_EXCLUDE_DIFFUSE) && (state->flag & PATH_RAY_DIFFUSE)) || - ((ls.shader & SHADER_EXCLUDE_GLOSSY) && - ((state->flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == - (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || - ((ls.shader & SHADER_EXCLUDE_TRANSMIT) && (state->flag & PATH_RAY_TRANSMIT)) || - ((ls.shader & SHADER_EXCLUDE_SCATTER) && (state->flag & PATH_RAY_VOLUME_SCATTER))) - continue; - } -#endif - - float3 lamp_L = direct_emissive_eval( - kg, emission_sd, &ls, state, -ray->D, ray->dD, ls.t, ray->time); - -#ifdef __VOLUME__ - if (state->volume_stack[0].shader != SHADER_NONE) { - /* shadow attenuation */ - Ray volume_ray = *ray; - volume_ray.t = ls.t; - float3 volume_tp = one_float3(); - kernel_volume_shadow(kg, emission_sd, state, &volume_ray, &volume_tp); - lamp_L *= volume_tp; - } -#endif - - if (!(state->flag & PATH_RAY_MIS_SKIP)) { - /* multiple importance sampling, get regular light pdf, - * and compute weight with respect to BSDF pdf */ - float mis_weight = power_heuristic(state->ray_pdf, ls.pdf); - lamp_L *= mis_weight; - } - - path_radiance_accum_emission(kg, L, state, throughput, lamp_L); - } -} - -/* Indirect Background */ - -ccl_device_noinline_cpu float3 indirect_background(KernelGlobals *kg, - ShaderData *emission_sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, - ccl_addr_space Ray *ray) -{ -#ifdef __BACKGROUND__ - int shader = kernel_data.background.surface_shader; - - /* Use visibility flag to skip lights. */ - if (shader & SHADER_EXCLUDE_ANY) { - if (((shader & SHADER_EXCLUDE_DIFFUSE) && (state->flag & PATH_RAY_DIFFUSE)) || - ((shader & SHADER_EXCLUDE_GLOSSY) && - ((state->flag & (PATH_RAY_GLOSSY | PATH_RAY_REFLECT)) == - (PATH_RAY_GLOSSY | PATH_RAY_REFLECT))) || - ((shader & SHADER_EXCLUDE_TRANSMIT) && (state->flag & PATH_RAY_TRANSMIT)) || - ((shader & SHADER_EXCLUDE_CAMERA) && (state->flag & PATH_RAY_CAMERA)) || - ((shader & SHADER_EXCLUDE_SCATTER) && (state->flag & PATH_RAY_VOLUME_SCATTER))) - return zero_float3(); - } - - /* Evaluate background shader. */ - float3 L = zero_float3(); - if (!shader_constant_emission_eval(kg, shader, &L)) { -# ifdef __SPLIT_KERNEL__ - Ray priv_ray = *ray; - shader_setup_from_background(kg, emission_sd, &priv_ray); -# else - shader_setup_from_background(kg, emission_sd, ray); -# endif - - path_state_modify_bounce(state, true); - shader_eval_surface(kg, emission_sd, state, buffer, state->flag | PATH_RAY_EMISSION); - path_state_modify_bounce(state, false); - - L = shader_background_eval(emission_sd); - } - - /* Background MIS weights. */ -# ifdef __BACKGROUND_MIS__ - /* Check if background light exists or if we should skip pdf. */ - if (!(state->flag & PATH_RAY_MIS_SKIP) && kernel_data.background.use_mis) { - /* multiple importance sampling, get background light pdf for ray - * direction, and compute weight with respect to BSDF pdf */ - float pdf = background_light_pdf(kg, ray->P, ray->D); - float mis_weight = power_heuristic(state->ray_pdf, pdf); - - return L * mis_weight; - } -# endif - - return L; -#else - return make_float3(0.8f, 0.8f, 0.8f); -#endif -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_film.h b/intern/cycles/kernel/kernel_film.h deleted file mode 100644 index a6fd4f1dc7e..00000000000 --- a/intern/cycles/kernel/kernel_film.h +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device float4 film_get_pass_result(KernelGlobals *kg, - ccl_global float *buffer, - float sample_scale, - int index, - bool use_display_sample_scale) -{ - float4 pass_result; - - int display_pass_stride = kernel_data.film.display_pass_stride; - int display_pass_components = kernel_data.film.display_pass_components; - - if (display_pass_components == 4) { - float4 in = *(ccl_global float4 *)(buffer + display_pass_stride + - index * kernel_data.film.pass_stride); - float alpha = use_display_sample_scale ? - (kernel_data.film.use_display_pass_alpha ? in.w : 1.0f / sample_scale) : - 1.0f; - - pass_result = make_float4(in.x, in.y, in.z, alpha); - - int display_divide_pass_stride = kernel_data.film.display_divide_pass_stride; - if (display_divide_pass_stride != -1) { - ccl_global float4 *divide_in = (ccl_global float4 *)(buffer + display_divide_pass_stride + - index * kernel_data.film.pass_stride); - float3 divided = safe_divide_even_color(float4_to_float3(pass_result), - float4_to_float3(*divide_in)); - pass_result = make_float4(divided.x, divided.y, divided.z, pass_result.w); - } - - if (kernel_data.film.use_display_exposure) { - float exposure = kernel_data.film.exposure; - pass_result *= make_float4(exposure, exposure, exposure, 1.0f); - } - } - else if (display_pass_components == 1) { - ccl_global float *in = (ccl_global float *)(buffer + display_pass_stride + - index * kernel_data.film.pass_stride); - pass_result = make_float4(*in, *in, *in, 1.0f / sample_scale); - } - - return pass_result; -} - -ccl_device float4 film_map(KernelGlobals *kg, float4 rgba_in, float scale) -{ - float4 result; - - /* Conversion to SRGB. */ - result.x = color_linear_to_srgb(rgba_in.x * scale); - result.y = color_linear_to_srgb(rgba_in.y * scale); - result.z = color_linear_to_srgb(rgba_in.z * scale); - - /* Clamp since alpha might be > 1.0 due to Russian roulette. */ - result.w = saturate(rgba_in.w * scale); - - return result; -} - -ccl_device uchar4 film_float_to_byte(float4 color) -{ - uchar4 result; - - /* simple float to byte conversion */ - result.x = (uchar)(saturate(color.x) * 255.0f); - result.y = (uchar)(saturate(color.y) * 255.0f); - result.z = (uchar)(saturate(color.z) * 255.0f); - result.w = (uchar)(saturate(color.w) * 255.0f); - - return result; -} - -ccl_device void kernel_film_convert_to_byte(KernelGlobals *kg, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) -{ - /* buffer offset */ - int index = offset + x + y * stride; - - bool use_display_sample_scale = (kernel_data.film.display_divide_pass_stride == -1); - float4 rgba_in = film_get_pass_result(kg, buffer, sample_scale, index, use_display_sample_scale); - - /* map colors */ - float4 float_result = film_map(kg, rgba_in, use_display_sample_scale ? sample_scale : 1.0f); - uchar4 uchar_result = film_float_to_byte(float_result); - - rgba += index; - *rgba = uchar_result; -} - -ccl_device void kernel_film_convert_to_half_float(KernelGlobals *kg, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) -{ - /* buffer offset */ - int index = offset + x + y * stride; - - bool use_display_sample_scale = (kernel_data.film.display_divide_pass_stride == -1); - float4 rgba_in = film_get_pass_result(kg, buffer, sample_scale, index, use_display_sample_scale); - - ccl_global half *out = (ccl_global half *)rgba + index * 4; - float4_store_half(out, rgba_in, use_display_sample_scale ? sample_scale : 1.0f); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_globals.h b/intern/cycles/kernel/kernel_globals.h deleted file mode 100644 index 70aed6d54ed..00000000000 --- a/intern/cycles/kernel/kernel_globals.h +++ /dev/null @@ -1,248 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Constant Globals */ - -#ifndef __KERNEL_GLOBALS_H__ -#define __KERNEL_GLOBALS_H__ - -#include "kernel/kernel_profiling.h" - -#ifdef __KERNEL_CPU__ -# include "util/util_map.h" -# include "util/util_vector.h" -#endif - -#ifdef __KERNEL_OPENCL__ -# include "util/util_atomic.h" -#endif - -CCL_NAMESPACE_BEGIN - -/* On the CPU, we pass along the struct KernelGlobals to nearly everywhere in - * the kernel, to access constant data. These are all stored as "textures", but - * these are really just standard arrays. We can't use actually globals because - * multiple renders may be running inside the same process. */ - -#ifdef __KERNEL_CPU__ - -# ifdef __OSL__ -struct OSLGlobals; -struct OSLThreadData; -struct OSLShadingSystem; -# endif - -typedef unordered_map CoverageMap; - -struct Intersection; -struct VolumeStep; - -typedef struct KernelGlobals { -# define KERNEL_TEX(type, name) texture name; -# include "kernel/kernel_textures.h" - - KernelData __data; - -# ifdef __OSL__ - /* On the CPU, we also have the OSL globals here. Most data structures are shared - * with SVM, the difference is in the shaders and object/mesh attributes. */ - OSLGlobals *osl; - OSLShadingSystem *osl_ss; - OSLThreadData *osl_tdata; -# endif - - /* **** Run-time data **** */ - - /* Heap-allocated storage for transparent shadows intersections. */ - Intersection *transparent_shadow_intersections; - - /* Storage for decoupled volume steps. */ - VolumeStep *decoupled_volume_steps[2]; - int decoupled_volume_steps_index; - - /* A buffer for storing per-pixel coverage for Cryptomatte. */ - CoverageMap *coverage_object; - CoverageMap *coverage_material; - CoverageMap *coverage_asset; - - /* split kernel */ - SplitData split_data; - SplitParams split_param_data; - - int2 global_size; - int2 global_id; - - ProfilingState profiler; -} KernelGlobals; - -#endif /* __KERNEL_CPU__ */ - -#ifdef __KERNEL_OPTIX__ - -typedef struct ShaderParams { - uint4 *input; - float4 *output; - int type; - int filter; - int sx; - int offset; - int sample; -} ShaderParams; - -typedef struct KernelParams { - WorkTile tile; - KernelData data; - ShaderParams shader; -# define KERNEL_TEX(type, name) const type *name; -# include "kernel/kernel_textures.h" -} KernelParams; - -typedef struct KernelGlobals { -# ifdef __VOLUME__ - VolumeState volume_state; -# endif - Intersection hits_stack[64]; -} KernelGlobals; - -extern "C" __constant__ KernelParams __params; - -#else /* __KERNEL_OPTIX__ */ - -/* For CUDA, constant memory textures must be globals, so we can't put them - * into a struct. As a result we don't actually use this struct and use actual - * globals and simply pass along a NULL pointer everywhere, which we hope gets - * optimized out. */ - -# ifdef __KERNEL_CUDA__ - -__constant__ KernelData __data; -typedef struct KernelGlobals { - /* NOTE: Keep the size in sync with SHADOW_STACK_MAX_HITS. */ - Intersection hits_stack[64]; -} KernelGlobals; - -# define KERNEL_TEX(type, name) const __constant__ __device__ type *name; -# include "kernel/kernel_textures.h" - -# endif /* __KERNEL_CUDA__ */ - -#endif /* __KERNEL_OPTIX__ */ - -/* OpenCL */ - -#ifdef __KERNEL_OPENCL__ - -# define KERNEL_TEX(type, name) typedef type name##_t; -# include "kernel/kernel_textures.h" - -typedef ccl_addr_space struct KernelGlobals { - ccl_constant KernelData *data; - ccl_global char *buffers[8]; - -# define KERNEL_TEX(type, name) TextureInfo name; -# include "kernel/kernel_textures.h" - -# ifdef __SPLIT_KERNEL__ - SplitData split_data; - SplitParams split_param_data; -# endif -} KernelGlobals; - -# define KERNEL_BUFFER_PARAMS \ - ccl_global char *buffer0, ccl_global char *buffer1, ccl_global char *buffer2, \ - ccl_global char *buffer3, ccl_global char *buffer4, ccl_global char *buffer5, \ - ccl_global char *buffer6, ccl_global char *buffer7 - -# define KERNEL_BUFFER_ARGS buffer0, buffer1, buffer2, buffer3, buffer4, buffer5, buffer6, buffer7 - -ccl_device_inline void kernel_set_buffer_pointers(KernelGlobals *kg, KERNEL_BUFFER_PARAMS) -{ -# ifdef __SPLIT_KERNEL__ - if (ccl_local_id(0) + ccl_local_id(1) == 0) -# endif - { - kg->buffers[0] = buffer0; - kg->buffers[1] = buffer1; - kg->buffers[2] = buffer2; - kg->buffers[3] = buffer3; - kg->buffers[4] = buffer4; - kg->buffers[5] = buffer5; - kg->buffers[6] = buffer6; - kg->buffers[7] = buffer7; - } - -# ifdef __SPLIT_KERNEL__ - ccl_barrier(CCL_LOCAL_MEM_FENCE); -# endif -} - -ccl_device_inline void kernel_set_buffer_info(KernelGlobals *kg) -{ -# ifdef __SPLIT_KERNEL__ - if (ccl_local_id(0) + ccl_local_id(1) == 0) -# endif - { - ccl_global TextureInfo *info = (ccl_global TextureInfo *)kg->buffers[0]; - -# define KERNEL_TEX(type, name) kg->name = *(info++); -# include "kernel/kernel_textures.h" - } - -# ifdef __SPLIT_KERNEL__ - ccl_barrier(CCL_LOCAL_MEM_FENCE); -# endif -} - -#endif /* __KERNEL_OPENCL__ */ - -/* Interpolated lookup table access */ - -ccl_device float lookup_table_read(KernelGlobals *kg, float x, int offset, int size) -{ - x = saturate(x) * (size - 1); - - int index = min(float_to_int(x), size - 1); - int nindex = min(index + 1, size - 1); - float t = x - index; - - float data0 = kernel_tex_fetch(__lookup_table, index + offset); - if (t == 0.0f) - return data0; - - float data1 = kernel_tex_fetch(__lookup_table, nindex + offset); - return (1.0f - t) * data0 + t * data1; -} - -ccl_device float lookup_table_read_2D( - KernelGlobals *kg, float x, float y, int offset, int xsize, int ysize) -{ - y = saturate(y) * (ysize - 1); - - int index = min(float_to_int(y), ysize - 1); - int nindex = min(index + 1, ysize - 1); - float t = y - index; - - float data0 = lookup_table_read(kg, x, offset + xsize * index, xsize); - if (t == 0.0f) - return data0; - - float data1 = lookup_table_read(kg, x, offset + xsize * nindex, xsize); - return (1.0f - t) * data0 + t * data1; -} - -CCL_NAMESPACE_END - -#endif /* __KERNEL_GLOBALS_H__ */ diff --git a/intern/cycles/kernel/kernel_jitter.h b/intern/cycles/kernel/kernel_jitter.h deleted file mode 100644 index f4e60a807f7..00000000000 --- a/intern/cycles/kernel/kernel_jitter.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* TODO(sergey): Consider moving portable ctz/clz stuff to util. */ - -CCL_NAMESPACE_BEGIN - -/* "Correlated Multi-Jittered Sampling" - * Andrew Kensler, Pixar Technical Memo 13-01, 2013 */ - -/* TODO: find good value, suggested 64 gives pattern on cornell box ceiling. */ -#define CMJ_RANDOM_OFFSET_LIMIT 4096 - -ccl_device_inline bool cmj_is_pow2(int i) -{ - return (i > 1) && ((i & (i - 1)) == 0); -} - -ccl_device_inline int cmj_fast_mod_pow2(int a, int b) -{ - return (a & (b - 1)); -} - -/* b must be > 1 */ -ccl_device_inline int cmj_fast_div_pow2(int a, int b) -{ - kernel_assert(b > 1); - return a >> count_trailing_zeros(b); -} - -ccl_device_inline uint cmj_w_mask(uint w) -{ - kernel_assert(w > 1); - return ((1 << (32 - count_leading_zeros(w))) - 1); -} - -ccl_device_inline uint cmj_permute(uint i, uint l, uint p) -{ - uint w = l - 1; - - if ((l & w) == 0) { - /* l is a power of two (fast) */ - i ^= p; - i *= 0xe170893d; - i ^= p >> 16; - i ^= (i & w) >> 4; - i ^= p >> 8; - i *= 0x0929eb3f; - i ^= p >> 23; - i ^= (i & w) >> 1; - i *= 1 | p >> 27; - i *= 0x6935fa69; - i ^= (i & w) >> 11; - i *= 0x74dcb303; - i ^= (i & w) >> 2; - i *= 0x9e501cc3; - i ^= (i & w) >> 2; - i *= 0xc860a3df; - i &= w; - i ^= i >> 5; - - return (i + p) & w; - } - else { - /* l is not a power of two (slow) */ - w = cmj_w_mask(w); - - do { - i ^= p; - i *= 0xe170893d; - i ^= p >> 16; - i ^= (i & w) >> 4; - i ^= p >> 8; - i *= 0x0929eb3f; - i ^= p >> 23; - i ^= (i & w) >> 1; - i *= 1 | p >> 27; - i *= 0x6935fa69; - i ^= (i & w) >> 11; - i *= 0x74dcb303; - i ^= (i & w) >> 2; - i *= 0x9e501cc3; - i ^= (i & w) >> 2; - i *= 0xc860a3df; - i &= w; - i ^= i >> 5; - } while (i >= l); - - return (i + p) % l; - } -} - -ccl_device_inline uint cmj_hash(uint i, uint p) -{ - i ^= p; - i ^= i >> 17; - i ^= i >> 10; - i *= 0xb36534e5; - i ^= i >> 12; - i ^= i >> 21; - i *= 0x93fc4795; - i ^= 0xdf6e307f; - i ^= i >> 17; - i *= 1 | p >> 18; - - return i; -} - -ccl_device_inline uint cmj_hash_simple(uint i, uint p) -{ - i = (i ^ 61) ^ p; - i += i << 3; - i ^= i >> 4; - i *= 0x27d4eb2d; - return i; -} - -ccl_device_inline float cmj_randfloat(uint i, uint p) -{ - return cmj_hash(i, p) * (1.0f / 4294967808.0f); -} - -#ifdef __CMJ__ -ccl_device float cmj_sample_1D(int s, int N, int p) -{ - kernel_assert(s < N); - - uint x = cmj_permute(s, N, p * 0x68bc21eb); - float jx = cmj_randfloat(s, p * 0x967a889b); - - float invN = 1.0f / N; - return (x + jx) * invN; -} - -/* TODO(sergey): Do some extra tests and consider moving to util_math.h. */ -ccl_device_inline int cmj_isqrt(int value) -{ -# if defined(__KERNEL_CUDA__) - return float_to_int(__fsqrt_ru(value)); -# elif defined(__KERNEL_GPU__) - return float_to_int(sqrtf(value)); -# else - /* This is a work around for fast-math on CPU which might replace sqrtf() - * with am approximated version. - */ - return float_to_int(sqrtf(value) + 1e-6f); -# endif -} - -ccl_device void cmj_sample_2D(int s, int N, int p, float *fx, float *fy) -{ - kernel_assert(s < N); - - int m = cmj_isqrt(N); - int n = (N - 1) / m + 1; - float invN = 1.0f / N; - float invm = 1.0f / m; - float invn = 1.0f / n; - - s = cmj_permute(s, N, p * 0x51633e2d); - - int sdivm, smodm; - - if (cmj_is_pow2(m)) { - sdivm = cmj_fast_div_pow2(s, m); - smodm = cmj_fast_mod_pow2(s, m); - } - else { - /* Doing `s * inmv` gives precision issues here. */ - sdivm = s / m; - smodm = s - sdivm * m; - } - - uint sx = cmj_permute(smodm, m, p * 0x68bc21eb); - uint sy = cmj_permute(sdivm, n, p * 0x02e5be93); - - float jx = cmj_randfloat(s, p * 0x967a889b); - float jy = cmj_randfloat(s, p * 0x368cc8b7); - - *fx = (sx + (sy + jx) * invn) * invm; - *fy = (s + jy) * invN; -} -#endif - -ccl_device float pmj_sample_1D(KernelGlobals *kg, int sample, int rng_hash, int dimension) -{ - /* Fallback to random */ - if (sample >= NUM_PMJ_SAMPLES) { - const int p = rng_hash + dimension; - return cmj_randfloat(sample, p); - } - else { - const uint mask = cmj_hash_simple(dimension, rng_hash) & 0x007fffff; - const int index = ((dimension % NUM_PMJ_PATTERNS) * NUM_PMJ_SAMPLES + sample) * 2; - return __uint_as_float(kernel_tex_fetch(__sample_pattern_lut, index) ^ mask) - 1.0f; - } -} - -ccl_device float2 pmj_sample_2D(KernelGlobals *kg, int sample, int rng_hash, int dimension) -{ - if (sample >= NUM_PMJ_SAMPLES) { - const int p = rng_hash + dimension; - const float fx = cmj_randfloat(sample, p); - const float fy = cmj_randfloat(sample, p + 1); - return make_float2(fx, fy); - } - else { - const int index = ((dimension % NUM_PMJ_PATTERNS) * NUM_PMJ_SAMPLES + sample) * 2; - const uint maskx = cmj_hash_simple(dimension, rng_hash) & 0x007fffff; - const uint masky = cmj_hash_simple(dimension + 1, rng_hash) & 0x007fffff; - const float fx = __uint_as_float(kernel_tex_fetch(__sample_pattern_lut, index) ^ maskx) - 1.0f; - const float fy = __uint_as_float(kernel_tex_fetch(__sample_pattern_lut, index + 1) ^ masky) - - 1.0f; - return make_float2(fx, fy); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_passes.h b/intern/cycles/kernel/kernel_passes.h deleted file mode 100644 index 8f58b8c3079..00000000000 --- a/intern/cycles/kernel/kernel_passes.h +++ /dev/null @@ -1,407 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_id_passes.h" - -CCL_NAMESPACE_BEGIN - -#ifdef __DENOISING_FEATURES__ - -ccl_device_inline void kernel_write_denoising_shadow(KernelGlobals *kg, - ccl_global float *buffer, - int sample, - float path_total, - float path_total_shaded) -{ - if (kernel_data.film.pass_denoising_data == 0) - return; - - buffer += sample_is_even(kernel_data.integrator.sampling_pattern, sample) ? - DENOISING_PASS_SHADOW_B : - DENOISING_PASS_SHADOW_A; - - path_total = ensure_finite(path_total); - path_total_shaded = ensure_finite(path_total_shaded); - - kernel_write_pass_float(buffer, path_total); - kernel_write_pass_float(buffer + 1, path_total_shaded); - - float value = path_total_shaded / max(path_total, 1e-7f); - kernel_write_pass_float(buffer + 2, value * value); -} - -ccl_device_inline void kernel_update_denoising_features(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - PathRadiance *L) -{ - if (state->denoising_feature_weight == 0.0f) { - return; - } - - L->denoising_depth += ensure_finite(state->denoising_feature_weight * sd->ray_length); - - /* Skip implicitly transparent surfaces. */ - if (sd->flag & SD_HAS_ONLY_VOLUME) { - return; - } - - float3 normal = zero_float3(); - float3 diffuse_albedo = zero_float3(); - float3 specular_albedo = zero_float3(); - float sum_weight = 0.0f, sum_nonspecular_weight = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) - continue; - - /* All closures contribute to the normal feature, but only diffuse-like ones to the albedo. */ - normal += sc->N * sc->sample_weight; - sum_weight += sc->sample_weight; - - float3 closure_albedo = sc->weight; - /* Closures that include a Fresnel term typically have weights close to 1 even though their - * actual contribution is significantly lower. - * To account for this, we scale their weight by the average fresnel factor (the same is also - * done for the sample weight in the BSDF setup, so we don't need to scale that here). */ - if (CLOSURE_IS_BSDF_MICROFACET_FRESNEL(sc->type)) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)sc; - closure_albedo *= bsdf->extra->fresnel_color; - } - else if (sc->type == CLOSURE_BSDF_PRINCIPLED_SHEEN_ID) { - PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)sc; - closure_albedo *= bsdf->avg_value; - } - else if (sc->type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { - closure_albedo *= bsdf_principled_hair_albedo(sc); - } - - if (bsdf_get_specular_roughness_squared(sc) > sqr(0.075f)) { - diffuse_albedo += closure_albedo; - sum_nonspecular_weight += sc->sample_weight; - } - else { - specular_albedo += closure_albedo; - } - } - - /* Wait for next bounce if 75% or more sample weight belongs to specular-like closures. */ - if ((sum_weight == 0.0f) || (sum_nonspecular_weight * 4.0f > sum_weight)) { - if (sum_weight != 0.0f) { - normal /= sum_weight; - } - - /* Transform normal into camera space. */ - const Transform worldtocamera = kernel_data.cam.worldtocamera; - normal = transform_direction(&worldtocamera, normal); - - L->denoising_normal += ensure_finite3(state->denoising_feature_weight * normal); - L->denoising_albedo += ensure_finite3(state->denoising_feature_weight * - state->denoising_feature_throughput * diffuse_albedo); - - state->denoising_feature_weight = 0.0f; - } - else { - state->denoising_feature_throughput *= specular_albedo; - } -} -#endif /* __DENOISING_FEATURES__ */ - -#ifdef __KERNEL_CPU__ -# define WRITE_ID_SLOT(buffer, depth, id, matte_weight, name) \ - kernel_write_id_pass_cpu(buffer, depth * 2, id, matte_weight, kg->coverage_##name) -ccl_device_inline size_t kernel_write_id_pass_cpu( - float *buffer, size_t depth, float id, float matte_weight, CoverageMap *map) -{ - if (map) { - (*map)[id] += matte_weight; - return 0; - } -#else /* __KERNEL_CPU__ */ -# define WRITE_ID_SLOT(buffer, depth, id, matte_weight, name) \ - kernel_write_id_slots_gpu(buffer, depth * 2, id, matte_weight) -ccl_device_inline size_t kernel_write_id_slots_gpu(ccl_global float *buffer, - size_t depth, - float id, - float matte_weight) -{ -#endif /* __KERNEL_CPU__ */ - kernel_write_id_slots(buffer, depth, id, matte_weight); - return depth * 2; -} - -ccl_device_inline void kernel_write_data_passes(KernelGlobals *kg, - ccl_global float *buffer, - PathRadiance *L, - ShaderData *sd, - ccl_addr_space PathState *state, - float3 throughput) -{ -#ifdef __PASSES__ - int path_flag = state->flag; - - if (!(path_flag & PATH_RAY_CAMERA)) - return; - - int flag = kernel_data.film.pass_flag; - int light_flag = kernel_data.film.light_pass_flag; - - if (!((flag | light_flag) & PASS_ANY)) - return; - - if (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)) { - if (!(sd->flag & SD_TRANSPARENT) || kernel_data.film.pass_alpha_threshold == 0.0f || - average(shader_bsdf_alpha(kg, sd)) >= kernel_data.film.pass_alpha_threshold) { - if (state->sample == 0) { - if (flag & PASSMASK(DEPTH)) { - float depth = camera_z_depth(kg, sd->P); - kernel_write_pass_float(buffer + kernel_data.film.pass_depth, depth); - } - if (flag & PASSMASK(OBJECT_ID)) { - float id = object_pass_id(kg, sd->object); - kernel_write_pass_float(buffer + kernel_data.film.pass_object_id, id); - } - if (flag & PASSMASK(MATERIAL_ID)) { - float id = shader_pass_id(kg, sd); - kernel_write_pass_float(buffer + kernel_data.film.pass_material_id, id); - } - } - - if (flag & PASSMASK(NORMAL)) { - float3 normal = shader_bsdf_average_normal(kg, sd); - kernel_write_pass_float3(buffer + kernel_data.film.pass_normal, normal); - } - if (flag & PASSMASK(UV)) { - float3 uv = primitive_uv(kg, sd); - kernel_write_pass_float3(buffer + kernel_data.film.pass_uv, uv); - } - if (flag & PASSMASK(MOTION)) { - float4 speed = primitive_motion_vector(kg, sd); - kernel_write_pass_float4(buffer + kernel_data.film.pass_motion, speed); - kernel_write_pass_float(buffer + kernel_data.film.pass_motion_weight, 1.0f); - } - - state->flag |= PATH_RAY_SINGLE_PASS_DONE; - } - } - - if (kernel_data.film.cryptomatte_passes) { - const float matte_weight = average(throughput) * - (1.0f - average(shader_bsdf_transparency(kg, sd))); - if (matte_weight > 0.0f) { - ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte; - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - float id = object_cryptomatte_id(kg, sd->object); - cryptomatte_buffer += WRITE_ID_SLOT( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight, object); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - float id = shader_cryptomatte_id(kg, sd->shader); - cryptomatte_buffer += WRITE_ID_SLOT( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight, material); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - float id = object_cryptomatte_asset_id(kg, sd->object); - cryptomatte_buffer += WRITE_ID_SLOT( - cryptomatte_buffer, kernel_data.film.cryptomatte_depth, id, matte_weight, asset); - } - } - } - - if (light_flag & PASSMASK_COMPONENT(DIFFUSE)) - L->color_diffuse += shader_bsdf_diffuse(kg, sd) * throughput; - if (light_flag & PASSMASK_COMPONENT(GLOSSY)) - L->color_glossy += shader_bsdf_glossy(kg, sd) * throughput; - if (light_flag & PASSMASK_COMPONENT(TRANSMISSION)) - L->color_transmission += shader_bsdf_transmission(kg, sd) * throughput; - - if (light_flag & PASSMASK(MIST)) { - /* bring depth into 0..1 range */ - float mist_start = kernel_data.film.mist_start; - float mist_inv_depth = kernel_data.film.mist_inv_depth; - - float depth = camera_distance(kg, sd->P); - float mist = saturate((depth - mist_start) * mist_inv_depth); - - /* falloff */ - float mist_falloff = kernel_data.film.mist_falloff; - - if (mist_falloff == 1.0f) - ; - else if (mist_falloff == 2.0f) - mist = mist * mist; - else if (mist_falloff == 0.5f) - mist = sqrtf(mist); - else - mist = powf(mist, mist_falloff); - - /* modulate by transparency */ - float3 alpha = shader_bsdf_alpha(kg, sd); - L->mist += (1.0f - mist) * average(throughput * alpha); - } -#endif -} - -ccl_device_inline void kernel_write_light_passes(KernelGlobals *kg, - ccl_global float *buffer, - PathRadiance *L) -{ -#ifdef __PASSES__ - int light_flag = kernel_data.film.light_pass_flag; - - if (!kernel_data.film.use_light_pass) - return; - - if (light_flag & PASSMASK(DIFFUSE_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_indirect, L->indirect_diffuse); - if (light_flag & PASSMASK(GLOSSY_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_indirect, L->indirect_glossy); - if (light_flag & PASSMASK(TRANSMISSION_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_indirect, - L->indirect_transmission); - if (light_flag & PASSMASK(VOLUME_INDIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_volume_indirect, L->indirect_volume); - if (light_flag & PASSMASK(DIFFUSE_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_direct, L->direct_diffuse); - if (light_flag & PASSMASK(GLOSSY_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_direct, L->direct_glossy); - if (light_flag & PASSMASK(TRANSMISSION_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_direct, - L->direct_transmission); - if (light_flag & PASSMASK(VOLUME_DIRECT)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_volume_direct, L->direct_volume); - - if (light_flag & PASSMASK(EMISSION)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_emission, L->emission); - if (light_flag & PASSMASK(BACKGROUND)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_background, L->background); - if (light_flag & PASSMASK(AO)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_ao, L->ao); - - if (light_flag & PASSMASK(DIFFUSE_COLOR)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_diffuse_color, L->color_diffuse); - if (light_flag & PASSMASK(GLOSSY_COLOR)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_glossy_color, L->color_glossy); - if (light_flag & PASSMASK(TRANSMISSION_COLOR)) - kernel_write_pass_float3(buffer + kernel_data.film.pass_transmission_color, - L->color_transmission); - if (light_flag & PASSMASK(SHADOW)) { - float3 shadow = L->shadow; - kernel_write_pass_float4( - buffer + kernel_data.film.pass_shadow, - make_float4(shadow.x, shadow.y, shadow.z, kernel_data.film.pass_shadow_scale)); - } - if (light_flag & PASSMASK(MIST)) - kernel_write_pass_float(buffer + kernel_data.film.pass_mist, 1.0f - L->mist); -#endif -} - -ccl_device_inline void kernel_write_result(KernelGlobals *kg, - ccl_global float *buffer, - int sample, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_WRITE_RESULT); - PROFILING_OBJECT(PRIM_NONE); - - float alpha; - float3 L_sum = path_radiance_clamp_and_sum(kg, L, &alpha); - - if (kernel_data.film.pass_flag & PASSMASK(COMBINED)) { - kernel_write_pass_float4(buffer, make_float4(L_sum.x, L_sum.y, L_sum.z, alpha)); - } - - kernel_write_light_passes(kg, buffer, L); - -#ifdef __DENOISING_FEATURES__ - if (kernel_data.film.pass_denoising_data) { -# ifdef __SHADOW_TRICKS__ - kernel_write_denoising_shadow(kg, - buffer + kernel_data.film.pass_denoising_data, - sample, - average(L->path_total), - average(L->path_total_shaded)); -# else - kernel_write_denoising_shadow( - kg, buffer + kernel_data.film.pass_denoising_data, sample, 0.0f, 0.0f); -# endif - if (kernel_data.film.pass_denoising_clean) { - float3 noisy, clean; - path_radiance_split_denoising(kg, L, &noisy, &clean); - kernel_write_pass_float3_variance( - buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_COLOR, noisy); - kernel_write_pass_float3_unaligned(buffer + kernel_data.film.pass_denoising_clean, clean); - } - else { - kernel_write_pass_float3_variance(buffer + kernel_data.film.pass_denoising_data + - DENOISING_PASS_COLOR, - ensure_finite3(L_sum)); - } - - kernel_write_pass_float3_variance(buffer + kernel_data.film.pass_denoising_data + - DENOISING_PASS_NORMAL, - L->denoising_normal); - kernel_write_pass_float3_variance(buffer + kernel_data.film.pass_denoising_data + - DENOISING_PASS_ALBEDO, - L->denoising_albedo); - kernel_write_pass_float_variance( - buffer + kernel_data.film.pass_denoising_data + DENOISING_PASS_DEPTH, L->denoising_depth); - } -#endif /* __DENOISING_FEATURES__ */ - - /* Adaptive Sampling. Fill the additional buffer with the odd samples and calculate our stopping - criteria. This is the heuristic from "A hierarchical automatic stopping condition for Monte - Carlo global illumination" except that here it is applied per pixel and not in hierarchical - tiles. */ - if (kernel_data.film.pass_adaptive_aux_buffer && - kernel_data.integrator.adaptive_threshold > 0.0f) { - if (sample_is_even(kernel_data.integrator.sampling_pattern, sample)) { - kernel_write_pass_float4(buffer + kernel_data.film.pass_adaptive_aux_buffer, - make_float4(L_sum.x * 2.0f, L_sum.y * 2.0f, L_sum.z * 2.0f, 0.0f)); - } -#ifdef __KERNEL_CPU__ - if ((sample > kernel_data.integrator.adaptive_min_samples) && - kernel_data.integrator.adaptive_stop_per_sample) { - const int step = kernel_data.integrator.adaptive_step; - - if ((sample & (step - 1)) == (step - 1)) { - kernel_do_adaptive_stopping(kg, buffer, sample); - } - } -#endif - } - - /* Write the sample count as negative numbers initially to mark the samples as in progress. - * Once the tile has finished rendering, the sign gets flipped and all the pixel values - * are scaled as if they were taken at a uniform sample count. */ - if (kernel_data.film.pass_sample_count) { - /* Make sure it's a negative number. In progressive refine mode, this bit gets flipped between - * passes. */ -#ifdef __ATOMIC_PASS_WRITE__ - atomic_fetch_and_or_uint32((ccl_global uint *)(buffer + kernel_data.film.pass_sample_count), - 0x80000000); -#else - if (buffer[kernel_data.film.pass_sample_count] > 0) { - buffer[kernel_data.film.pass_sample_count] *= -1.0f; - } -#endif - kernel_write_pass_float(buffer + kernel_data.film.pass_sample_count, -1.0f); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path.h b/intern/cycles/kernel/kernel_path.h deleted file mode 100644 index 92a097de9e1..00000000000 --- a/intern/cycles/kernel/kernel_path.h +++ /dev/null @@ -1,709 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef __OSL__ -# include "kernel/osl/osl_shader.h" -#endif - -// clang-format off -#include "kernel/kernel_random.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_differential.h" -#include "kernel/kernel_camera.h" - -#include "kernel/geom/geom.h" -#include "kernel/bvh/bvh.h" - -#include "kernel/kernel_write_passes.h" -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_shader.h" -#include "kernel/kernel_light.h" -#include "kernel/kernel_adaptive_sampling.h" -#include "kernel/kernel_passes.h" - -#if defined(__VOLUME__) || defined(__SUBSURFACE__) -# include "kernel/kernel_volume.h" -#endif - -#ifdef __SUBSURFACE__ -# include "kernel/kernel_subsurface.h" -#endif - -#include "kernel/kernel_path_state.h" -#include "kernel/kernel_shadow.h" -#include "kernel/kernel_emission.h" -#include "kernel/kernel_path_common.h" -#include "kernel/kernel_path_surface.h" -#include "kernel/kernel_path_volume.h" -#include "kernel/kernel_path_subsurface.h" -// clang-format on - -CCL_NAMESPACE_BEGIN - -ccl_device_forceinline bool kernel_path_scene_intersect(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - Intersection *isect, - PathRadiance *L, - const int last_object) -{ - PROFILING_INIT(kg, PROFILING_SCENE_INTERSECT); - - uint visibility = path_state_ray_visibility(kg, state); - - if (path_state_ao_bounce(kg, state)) { - ray->t = kernel_data.background.ao_distance; - if (last_object != OBJECT_NONE) { - const float object_ao_distance = kernel_tex_fetch(__objects, last_object).ao_distance; - if (object_ao_distance != 0.0f) { - ray->t = object_ao_distance; - } - } - } - - bool hit = scene_intersect(kg, ray, visibility, isect); - - return hit; -} - -ccl_device_forceinline void kernel_path_lamp_emission(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - float3 throughput, - ccl_addr_space Intersection *isect, - ShaderData *emission_sd, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_INDIRECT_EMISSION); - -#ifdef __LAMP_MIS__ - if (kernel_data.integrator.use_lamp_mis && !(state->flag & PATH_RAY_CAMERA)) { - /* ray starting from previous non-transparent bounce */ - Ray light_ray ccl_optional_struct_init; - - light_ray.P = ray->P - state->ray_t * ray->D; - state->ray_t += isect->t; - light_ray.D = ray->D; - light_ray.t = state->ray_t; - light_ray.time = ray->time; - light_ray.dD = ray->dD; - light_ray.dP = ray->dP; - - /* intersect with lamp */ - indirect_lamp_emission(kg, emission_sd, state, L, &light_ray, throughput); - } -#endif /* __LAMP_MIS__ */ -} - -ccl_device_forceinline void kernel_path_background(KernelGlobals *kg, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - float3 throughput, - ShaderData *sd, - ccl_global float *buffer, - PathRadiance *L) -{ - /* eval background shader if nothing hit */ - if (kernel_data.background.transparent && (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - L->transparent += average(throughput); - -#ifdef __PASSES__ - if (!(kernel_data.film.light_pass_flag & PASSMASK(BACKGROUND))) -#endif /* __PASSES__ */ - return; - } - - /* When using the ao bounces approximation, adjust background - * shader intensity with ao factor. */ - if (path_state_ao_bounce(kg, state)) { - throughput *= kernel_data.background.ao_bounces_factor; - } - -#ifdef __BACKGROUND__ - /* sample background shader */ - float3 L_background = indirect_background(kg, sd, state, buffer, ray); - path_radiance_accum_background(kg, L, state, throughput, L_background); -#endif /* __BACKGROUND__ */ -} - -#ifndef __SPLIT_KERNEL__ - -# ifdef __VOLUME__ -ccl_device_forceinline VolumeIntegrateResult kernel_path_volume(KernelGlobals *kg, - ShaderData *sd, - PathState *state, - Ray *ray, - float3 *throughput, - ccl_addr_space Intersection *isect, - bool hit, - ShaderData *emission_sd, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_VOLUME); - - /* Sanitize volume stack. */ - if (!hit) { - kernel_volume_clean_stack(kg, state->volume_stack); - } - - if (state->volume_stack[0].shader == SHADER_NONE) { - return VOLUME_PATH_ATTENUATED; - } - - /* volume attenuation, emission, scatter */ - Ray volume_ray = *ray; - volume_ray.t = (hit) ? isect->t : FLT_MAX; - - float step_size = volume_stack_step_size(kg, state->volume_stack); - -# ifdef __VOLUME_DECOUPLED__ - int sampling_method = volume_stack_sampling_method(kg, state->volume_stack); - bool direct = (state->flag & PATH_RAY_CAMERA) != 0; - bool decoupled = kernel_volume_use_decoupled(kg, step_size, direct, sampling_method); - - if (decoupled) { - /* cache steps along volume for repeated sampling */ - VolumeSegment volume_segment; - - shader_setup_from_volume(kg, sd, &volume_ray); - kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size); - - volume_segment.sampling_method = sampling_method; - - /* emission */ - if (volume_segment.closure_flag & SD_EMISSION) - path_radiance_accum_emission(kg, L, state, *throughput, volume_segment.accum_emission); - - /* scattering */ - VolumeIntegrateResult result = VOLUME_PATH_ATTENUATED; - - if (volume_segment.closure_flag & SD_SCATTER) { - int all = kernel_data.integrator.sample_all_lights_indirect; - - /* direct light sampling */ - kernel_branched_path_volume_connect_light( - kg, sd, emission_sd, *throughput, state, L, all, &volume_ray, &volume_segment); - - /* indirect sample. if we use distance sampling and take just - * one sample for direct and indirect light, we could share - * this computation, but makes code a bit complex */ - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - float rscatter = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - - result = kernel_volume_decoupled_scatter( - kg, state, &volume_ray, sd, throughput, rphase, rscatter, &volume_segment, NULL, true); - } - - /* free cached steps */ - kernel_volume_decoupled_free(kg, &volume_segment); - - if (result == VOLUME_PATH_SCATTERED) { - if (kernel_path_volume_bounce(kg, sd, throughput, state, &L->state, ray)) - return VOLUME_PATH_SCATTERED; - else - return VOLUME_PATH_MISSED; - } - else { - *throughput *= volume_segment.accum_transmittance; - } - } - else -# endif /* __VOLUME_DECOUPLED__ */ - { - /* integrate along volume segment with distance sampling */ - VolumeIntegrateResult result = kernel_volume_integrate( - kg, state, sd, &volume_ray, L, throughput, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* direct lighting */ - kernel_path_volume_connect_light(kg, sd, emission_sd, *throughput, state, L); - - /* indirect light bounce */ - if (kernel_path_volume_bounce(kg, sd, throughput, state, &L->state, ray)) - return VOLUME_PATH_SCATTERED; - else - return VOLUME_PATH_MISSED; - } -# endif /* __VOLUME_SCATTER__ */ - } - - return VOLUME_PATH_ATTENUATED; -} -# endif /* __VOLUME__ */ - -#endif /* __SPLIT_KERNEL__ */ - -ccl_device_forceinline bool kernel_path_shader_apply(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - float3 throughput, - ShaderData *emission_sd, - PathRadiance *L, - ccl_global float *buffer) -{ - PROFILING_INIT(kg, PROFILING_SHADER_APPLY); - -#ifdef __SHADOW_TRICKS__ - if (sd->object_flag & SD_OBJECT_SHADOW_CATCHER) { - if (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND) { - state->flag |= (PATH_RAY_SHADOW_CATCHER | PATH_RAY_STORE_SHADOW_INFO); - - float3 bg = zero_float3(); - if (!kernel_data.background.transparent) { - bg = indirect_background(kg, emission_sd, state, NULL, ray); - } - path_radiance_accum_shadowcatcher(L, throughput, bg); - } - } - else if (state->flag & PATH_RAY_SHADOW_CATCHER) { - /* Only update transparency after shadow catcher bounce. */ - L->shadow_transparency *= average(shader_bsdf_transparency(kg, sd)); - } -#endif /* __SHADOW_TRICKS__ */ - - /* holdout */ -#ifdef __HOLDOUT__ - if (((sd->flag & SD_HOLDOUT) || (sd->object_flag & SD_OBJECT_HOLDOUT_MASK)) && - (state->flag & PATH_RAY_TRANSPARENT_BACKGROUND)) { - const float3 holdout_weight = shader_holdout_apply(kg, sd); - if (kernel_data.background.transparent) { - L->transparent += average(holdout_weight * throughput); - } - if (isequal_float3(holdout_weight, one_float3())) { - return false; - } - } -#endif /* __HOLDOUT__ */ - - /* holdout mask objects do not write data passes */ - kernel_write_data_passes(kg, buffer, L, sd, state, throughput); - - /* blurring of bsdf after bounces, for rays that have a small likelihood - * of following this particular path (diffuse, rough glossy) */ - if (kernel_data.integrator.filter_glossy != FLT_MAX) { - float blur_pdf = kernel_data.integrator.filter_glossy * state->min_ray_pdf; - - if (blur_pdf < 1.0f) { - float blur_roughness = sqrtf(1.0f - blur_pdf) * 0.5f; - shader_bsdf_blur(kg, sd, blur_roughness); - } - } - -#ifdef __EMISSION__ - /* emission */ - if (sd->flag & SD_EMISSION) { - float3 emission = indirect_primitive_emission( - kg, sd, sd->ray_length, state->flag, state->ray_pdf); - path_radiance_accum_emission(kg, L, state, throughput, emission); - } -#endif /* __EMISSION__ */ - - return true; -} - -#ifdef __KERNEL_OPTIX__ -ccl_device_inline /* inline trace calls */ -#else -ccl_device_noinline -#endif - void - kernel_path_ao(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput, - float3 ao_alpha) -{ - PROFILING_INIT(kg, PROFILING_AO); - - /* todo: solve correlation */ - float bsdf_u, bsdf_v; - - path_state_rng_2D(kg, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - - float ao_factor = kernel_data.background.ao_factor; - float3 ao_N; - float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); - float3 ao_D; - float ao_pdf; - - sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - - if (dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { - Ray light_ray; - float3 ao_shadow; - - light_ray.P = ray_offset(sd->P, sd->Ng); - light_ray.D = ao_D; - light_ray.t = kernel_data.background.ao_distance; - light_ray.time = sd->time; - light_ray.dP = sd->dP; - light_ray.dD = differential3_zero(); - - if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &ao_shadow)) { - path_radiance_accum_ao(kg, L, state, throughput, ao_alpha, ao_bsdf, ao_shadow); - } - else { - path_radiance_accum_total_ao(L, state, throughput, ao_bsdf); - } - } -} - -#ifndef __SPLIT_KERNEL__ - -# if defined(__BRANCHED_PATH__) || defined(__BAKING__) - -ccl_device void kernel_path_indirect(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - Ray *ray, - float3 throughput, - PathState *state, - PathRadiance *L, - const int last_object) -{ -# ifdef __SUBSURFACE__ - SubsurfaceIndirectRays ss_indirect; - kernel_path_subsurface_init_indirect(&ss_indirect); - - for (;;) { -# endif /* __SUBSURFACE__ */ - - /* path iteration */ - for (;;) { - /* Find intersection with objects in scene. */ - Intersection isect; - bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L, last_object); - - /* Find intersection with lamps and compute emission for MIS. */ - kernel_path_lamp_emission(kg, state, ray, throughput, &isect, sd, L); - -# ifdef __VOLUME__ - /* Volume integration. */ - VolumeIntegrateResult result = kernel_path_volume( - kg, sd, state, ray, &throughput, &isect, hit, emission_sd, L); - - if (result == VOLUME_PATH_SCATTERED) { - continue; - } - else if (result == VOLUME_PATH_MISSED) { - break; - } -# endif /* __VOLUME__*/ - - /* Shade background. */ - if (!hit) { - kernel_path_background(kg, state, ray, throughput, sd, NULL, L); - break; - } - else if (path_state_ao_bounce(kg, state)) { - if (intersection_get_shader_flags(kg, &isect) & - (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - else { - break; - } - } - - /* Setup shader data. */ - shader_setup_from_ray(kg, sd, &isect, ray); - - /* Skip most work for volume bounding surface. */ -# ifdef __VOLUME__ - if (!(sd->flag & SD_HAS_ONLY_VOLUME)) { -# endif - - /* Evaluate shader. */ - shader_eval_surface(kg, sd, state, NULL, state->flag); - shader_prepare_closures(sd, state); - - /* Apply shadow catcher, holdout, emission. */ - if (!kernel_path_shader_apply(kg, sd, state, ray, throughput, emission_sd, L, NULL)) { - break; - } - - /* path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate */ - float probability = path_state_continuation_probability(kg, state, throughput); - - if (probability == 0.0f) { - break; - } - else if (probability != 1.0f) { - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE); - - if (terminate >= probability) - break; - - throughput /= probability; - } - -# ifdef __DENOISING_FEATURES__ - kernel_update_denoising_features(kg, sd, state, L); -# endif - -# ifdef __AO__ - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - kernel_path_ao(kg, sd, emission_sd, L, state, throughput, zero_float3()); - } -# endif /* __AO__ */ - -# ifdef __SUBSURFACE__ - /* bssrdf scatter to a different location on the same object, replacing - * the closures with a diffuse BSDF */ - if (sd->flag & SD_BSSRDF) { - if (kernel_path_subsurface_scatter( - kg, sd, emission_sd, L, state, ray, &throughput, &ss_indirect)) { - break; - } - } -# endif /* __SUBSURFACE__ */ - -# if defined(__EMISSION__) - int all = (kernel_data.integrator.sample_all_lights_indirect) || - (state->flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light( - kg, sd, emission_sd, state, throughput, 1.0f, L, all); -# endif /* defined(__EMISSION__) */ - -# ifdef __VOLUME__ - } -# endif - - if (!kernel_path_surface_bounce(kg, sd, &throughput, state, &L->state, ray)) - break; - } - -# ifdef __SUBSURFACE__ - /* Trace indirect subsurface rays by restarting the loop. this uses less - * stack memory than invoking kernel_path_indirect. - */ - if (ss_indirect.num_rays) { - kernel_path_subsurface_setup_indirect(kg, &ss_indirect, state, ray, L, &throughput); - } - else { - break; - } - } -# endif /* __SUBSURFACE__ */ -} - -# endif /* defined(__BRANCHED_PATH__) || defined(__BAKING__) */ - -ccl_device_forceinline void kernel_path_integrate(KernelGlobals *kg, - PathState *state, - float3 throughput, - Ray *ray, - PathRadiance *L, - ccl_global float *buffer, - ShaderData *emission_sd) -{ - PROFILING_INIT(kg, PROFILING_PATH_INTEGRATE); - - /* Shader data memory used for both volumes and surfaces, saves stack space. */ - ShaderData sd; - -# ifdef __SUBSURFACE__ - SubsurfaceIndirectRays ss_indirect; - kernel_path_subsurface_init_indirect(&ss_indirect); - - for (;;) { -# endif /* __SUBSURFACE__ */ - - /* path iteration */ - for (;;) { - /* Find intersection with objects in scene. */ - Intersection isect; - bool hit = kernel_path_scene_intersect(kg, state, ray, &isect, L, sd.object); - - /* Find intersection with lamps and compute emission for MIS. */ - kernel_path_lamp_emission(kg, state, ray, throughput, &isect, &sd, L); - -# ifdef __VOLUME__ - /* Volume integration. */ - VolumeIntegrateResult result = kernel_path_volume( - kg, &sd, state, ray, &throughput, &isect, hit, emission_sd, L); - - if (result == VOLUME_PATH_SCATTERED) { - continue; - } - else if (result == VOLUME_PATH_MISSED) { - break; - } -# endif /* __VOLUME__*/ - - /* Shade background. */ - if (!hit) { - kernel_path_background(kg, state, ray, throughput, &sd, buffer, L); - break; - } - else if (path_state_ao_bounce(kg, state)) { - if (intersection_get_shader_flags(kg, &isect) & - (SD_HAS_TRANSPARENT_SHADOW | SD_HAS_EMISSION)) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - else { - break; - } - } - - /* Setup shader data. */ - shader_setup_from_ray(kg, &sd, &isect, ray); - - /* Skip most work for volume bounding surface. */ -# ifdef __VOLUME__ - if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { -# endif - - /* Evaluate shader. */ - shader_eval_surface(kg, &sd, state, buffer, state->flag); - shader_prepare_closures(&sd, state); - - /* Apply shadow catcher, holdout, emission. */ - if (!kernel_path_shader_apply(kg, &sd, state, ray, throughput, emission_sd, L, buffer)) { - break; - } - - /* path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate */ - float probability = path_state_continuation_probability(kg, state, throughput); - - if (probability == 0.0f) { - break; - } - else if (probability != 1.0f) { - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE); - if (terminate >= probability) - break; - - throughput /= probability; - } - -# ifdef __DENOISING_FEATURES__ - kernel_update_denoising_features(kg, &sd, state, L); -# endif - -# ifdef __AO__ - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - kernel_path_ao(kg, &sd, emission_sd, L, state, throughput, shader_bsdf_alpha(kg, &sd)); - } -# endif /* __AO__ */ - -# ifdef __SUBSURFACE__ - /* bssrdf scatter to a different location on the same object, replacing - * the closures with a diffuse BSDF */ - if (sd.flag & SD_BSSRDF) { - if (kernel_path_subsurface_scatter( - kg, &sd, emission_sd, L, state, ray, &throughput, &ss_indirect)) { - break; - } - } -# endif /* __SUBSURFACE__ */ - -# ifdef __EMISSION__ - /* direct lighting */ - kernel_path_surface_connect_light(kg, &sd, emission_sd, throughput, state, L); -# endif /* __EMISSION__ */ - -# ifdef __VOLUME__ - } -# endif - - /* compute direct lighting and next bounce */ - if (!kernel_path_surface_bounce(kg, &sd, &throughput, state, &L->state, ray)) - break; - } - -# ifdef __SUBSURFACE__ - /* Trace indirect subsurface rays by restarting the loop. this uses less - * stack memory than invoking kernel_path_indirect. - */ - if (ss_indirect.num_rays) { - kernel_path_subsurface_setup_indirect(kg, &ss_indirect, state, ray, L, &throughput); - } - else { - break; - } - } -# endif /* __SUBSURFACE__ */ -} - -ccl_device void kernel_path_trace( - KernelGlobals *kg, ccl_global float *buffer, int sample, int x, int y, int offset, int stride) -{ - PROFILING_INIT(kg, PROFILING_RAY_SETUP); - - /* buffer offset */ - int index = offset + x + y * stride; - int pass_stride = kernel_data.film.pass_stride; - - buffer += index * pass_stride; - - if (kernel_data.film.pass_adaptive_aux_buffer) { - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w > 0.0f) { - return; - } - } - - /* Initialize random numbers and sample ray. */ - uint rng_hash; - Ray ray; - - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &ray); - - if (ray.t == 0.0f) { - return; - } - - /* Initialize state. */ - float3 throughput = one_float3(); - - PathRadiance L; - path_radiance_init(kg, &L); - - ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - - PathState state; - path_state_init(kg, emission_sd, &state, rng_hash, sample, &ray); - -# ifdef __KERNEL_OPTIX__ - /* Force struct into local memory to avoid costly spilling on trace calls. */ - if (pass_stride < 0) /* This is never executed and just prevents the compiler from doing SROA. */ - for (int i = 0; i < sizeof(L); ++i) - reinterpret_cast(&L)[-pass_stride + i] = 0; -# endif - - /* Integrate. */ - kernel_path_integrate(kg, &state, throughput, &ray, &L, buffer, emission_sd); - - kernel_write_result(kg, buffer, sample, &L); -} - -#endif /* __SPLIT_KERNEL__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_branched.h b/intern/cycles/kernel/kernel_path_branched.h deleted file mode 100644 index a1ee1bc107e..00000000000 --- a/intern/cycles/kernel/kernel_path_branched.h +++ /dev/null @@ -1,556 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __BRANCHED_PATH__ - -ccl_device_inline void kernel_branched_path_ao(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - PathRadiance *L, - ccl_addr_space PathState *state, - float3 throughput) -{ - int num_samples = kernel_data.integrator.ao_samples; - float num_samples_inv = 1.0f / num_samples; - float ao_factor = kernel_data.background.ao_factor; - float3 ao_N; - float3 ao_bsdf = shader_bsdf_ao(kg, sd, ao_factor, &ao_N); - float3 ao_alpha = shader_bsdf_alpha(kg, sd); - - for (int j = 0; j < num_samples; j++) { - float bsdf_u, bsdf_v; - path_branched_rng_2D( - kg, state->rng_hash, state, j, num_samples, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - - float3 ao_D; - float ao_pdf; - - sample_cos_hemisphere(ao_N, bsdf_u, bsdf_v, &ao_D, &ao_pdf); - - if (dot(sd->Ng, ao_D) > 0.0f && ao_pdf != 0.0f) { - Ray light_ray; - float3 ao_shadow; - - light_ray.P = ray_offset(sd->P, sd->Ng); - light_ray.D = ao_D; - light_ray.t = kernel_data.background.ao_distance; - light_ray.time = sd->time; - light_ray.dP = sd->dP; - light_ray.dD = differential3_zero(); - - if (!shadow_blocked(kg, sd, emission_sd, state, &light_ray, &ao_shadow)) { - path_radiance_accum_ao( - kg, L, state, throughput * num_samples_inv, ao_alpha, ao_bsdf, ao_shadow); - } - else { - path_radiance_accum_total_ao(L, state, throughput * num_samples_inv, ao_bsdf); - } - } - } -} - -# ifndef __SPLIT_KERNEL__ - -# ifdef __VOLUME__ -ccl_device_forceinline void kernel_branched_path_volume(KernelGlobals *kg, - ShaderData *sd, - PathState *state, - Ray *ray, - float3 *throughput, - ccl_addr_space Intersection *isect, - bool hit, - ShaderData *indirect_sd, - ShaderData *emission_sd, - PathRadiance *L) -{ - /* Sanitize volume stack. */ - if (!hit) { - kernel_volume_clean_stack(kg, state->volume_stack); - } - - if (state->volume_stack[0].shader == SHADER_NONE) { - return; - } - - /* volume attenuation, emission, scatter */ - Ray volume_ray = *ray; - volume_ray.t = (hit) ? isect->t : FLT_MAX; - - float step_size = volume_stack_step_size(kg, state->volume_stack); - const int object = sd->object; - -# ifdef __VOLUME_DECOUPLED__ - /* decoupled ray marching only supported on CPU */ - if (kernel_data.integrator.volume_decoupled) { - /* cache steps along volume for repeated sampling */ - VolumeSegment volume_segment; - - shader_setup_from_volume(kg, sd, &volume_ray); - kernel_volume_decoupled_record(kg, state, &volume_ray, sd, &volume_segment, step_size); - - /* direct light sampling */ - if (volume_segment.closure_flag & SD_SCATTER) { - volume_segment.sampling_method = volume_stack_sampling_method(kg, state->volume_stack); - - int all = kernel_data.integrator.sample_all_lights_direct; - - kernel_branched_path_volume_connect_light( - kg, sd, emission_sd, *throughput, state, L, all, &volume_ray, &volume_segment); - - /* indirect light sampling */ - int num_samples = kernel_data.integrator.volume_samples; - float num_samples_inv = 1.0f / num_samples; - - for (int j = 0; j < num_samples; j++) { - PathState ps = *state; - Ray pray = *ray; - float3 tp = *throughput; - - /* branch RNG state */ - path_state_branch(&ps, j, num_samples); - - /* scatter sample. if we use distance sampling and take just one - * sample for direct and indirect light, we could share this - * computation, but makes code a bit complex */ - float rphase = path_state_rng_1D(kg, &ps, PRNG_PHASE_CHANNEL); - float rscatter = path_state_rng_1D(kg, &ps, PRNG_SCATTER_DISTANCE); - - VolumeIntegrateResult result = kernel_volume_decoupled_scatter( - kg, &ps, &pray, sd, &tp, rphase, rscatter, &volume_segment, NULL, false); - - if (result == VOLUME_PATH_SCATTERED && - kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) { - kernel_path_indirect( - kg, indirect_sd, emission_sd, &pray, tp * num_samples_inv, &ps, L, object); - - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - } - } - } - - /* emission and transmittance */ - if (volume_segment.closure_flag & SD_EMISSION) - path_radiance_accum_emission(kg, L, state, *throughput, volume_segment.accum_emission); - *throughput *= volume_segment.accum_transmittance; - - /* free cached steps */ - kernel_volume_decoupled_free(kg, &volume_segment); - } - else -# endif /* __VOLUME_DECOUPLED__ */ - { - /* GPU: no decoupled ray marching, scatter probabilistically. */ - int num_samples = kernel_data.integrator.volume_samples; - float num_samples_inv = 1.0f / num_samples; - - /* todo: we should cache the shader evaluations from stepping - * through the volume, for now we redo them multiple times */ - - for (int j = 0; j < num_samples; j++) { - PathState ps = *state; - Ray pray = *ray; - float3 tp = (*throughput) * num_samples_inv; - - /* branch RNG state */ - path_state_branch(&ps, j, num_samples); - - VolumeIntegrateResult result = kernel_volume_integrate( - kg, &ps, sd, &volume_ray, L, &tp, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* todo: support equiangular, MIS and all light sampling. - * alternatively get decoupled ray marching working on the GPU */ - kernel_path_volume_connect_light(kg, sd, emission_sd, tp, state, L); - - if (kernel_path_volume_bounce(kg, sd, &tp, &ps, &L->state, &pray)) { - kernel_path_indirect(kg, indirect_sd, emission_sd, &pray, tp, &ps, L, object); - - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - } - } -# endif /* __VOLUME_SCATTER__ */ - } - - /* todo: avoid this calculation using decoupled ray marching */ - kernel_volume_shadow(kg, emission_sd, state, &volume_ray, throughput); - } -} -# endif /* __VOLUME__ */ - -/* bounce off surface and integrate indirect light */ -ccl_device_noinline_cpu void kernel_branched_path_surface_indirect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *indirect_sd, - ShaderData *emission_sd, - float3 throughput, - float num_samples_adjust, - PathState *state, - PathRadiance *L) -{ - float sum_sample_weight = 0.0f; -# ifdef __DENOISING_FEATURES__ - if (state->denoising_feature_weight > 0.0f) { - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - /* transparency is not handled here, but in outer loop */ - if (!CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - continue; - } - - sum_sample_weight += sc->sample_weight; - } - } - else { - sum_sample_weight = 1.0f; - } -# endif /* __DENOISING_FEATURES__ */ - - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - /* transparency is not handled here, but in outer loop */ - if (!CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - continue; - } - - int num_samples; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) - num_samples = kernel_data.integrator.diffuse_samples; - else if (CLOSURE_IS_BSDF_BSSRDF(sc->type)) - num_samples = 1; - else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) - num_samples = kernel_data.integrator.glossy_samples; - else - num_samples = kernel_data.integrator.transmission_samples; - - num_samples = ceil_to_int(num_samples_adjust * num_samples); - - float num_samples_inv = num_samples_adjust / num_samples; - - for (int j = 0; j < num_samples; j++) { - PathState ps = *state; - float3 tp = throughput; - Ray bsdf_ray; -# ifdef __SHADOW_TRICKS__ - float shadow_transparency = L->shadow_transparency; -# endif - - ps.rng_hash = cmj_hash(state->rng_hash, i); - - if (!kernel_branched_path_surface_bounce( - kg, sd, sc, j, num_samples, &tp, &ps, &L->state, &bsdf_ray, sum_sample_weight)) { - continue; - } - - ps.rng_hash = state->rng_hash; - - kernel_path_indirect( - kg, indirect_sd, emission_sd, &bsdf_ray, tp * num_samples_inv, &ps, L, sd->object); - - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - -# ifdef __SHADOW_TRICKS__ - L->shadow_transparency = shadow_transparency; -# endif - } - } -} - -# ifdef __SUBSURFACE__ -ccl_device void kernel_branched_path_subsurface_scatter(KernelGlobals *kg, - ShaderData *sd, - ShaderData *indirect_sd, - ShaderData *emission_sd, - PathRadiance *L, - PathState *state, - Ray *ray, - float3 throughput) -{ - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSSRDF(sc->type)) - continue; - - /* set up random number generator */ - uint lcg_state = lcg_state_init(state, 0x68bc21eb); - int num_samples = kernel_data.integrator.subsurface_samples * 3; - float num_samples_inv = 1.0f / num_samples; - uint bssrdf_rng_hash = cmj_hash(state->rng_hash, i); - - /* do subsurface scatter step with copy of shader data, this will - * replace the BSSRDF with a diffuse BSDF closure */ - for (int j = 0; j < num_samples; j++) { - PathState hit_state = *state; - path_state_branch(&hit_state, j, num_samples); - hit_state.rng_hash = bssrdf_rng_hash; - - LocalIntersection ss_isect; - float bssrdf_u, bssrdf_v; - path_state_rng_2D(kg, &hit_state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - int num_hits = subsurface_scatter_multi_intersect( - kg, &ss_isect, sd, &hit_state, sc, &lcg_state, bssrdf_u, bssrdf_v, true); - - hit_state.rng_offset += PRNG_BOUNCE_NUM; - -# ifdef __VOLUME__ - Ray volume_ray = *ray; - bool need_update_volume_stack = kernel_data.integrator.use_volumes && - sd->object_flag & SD_OBJECT_INTERSECTS_VOLUME; -# endif /* __VOLUME__ */ - - /* compute lighting with the BSDF closure */ - for (int hit = 0; hit < num_hits; hit++) { - ShaderData bssrdf_sd = *sd; - Bssrdf *bssrdf = (Bssrdf *)sc; - ClosureType bssrdf_type = sc->type; - float bssrdf_roughness = bssrdf->roughness; - subsurface_scatter_multi_setup( - kg, &ss_isect, hit, &bssrdf_sd, &hit_state, bssrdf_type, bssrdf_roughness); - -# ifdef __VOLUME__ - if (need_update_volume_stack) { - /* Setup ray from previous surface point to the new one. */ - float3 P = ray_offset(bssrdf_sd.P, -bssrdf_sd.Ng); - volume_ray.D = normalize_len(P - volume_ray.P, &volume_ray.t); - - for (int k = 0; k < VOLUME_STACK_SIZE; k++) { - hit_state.volume_stack[k] = state->volume_stack[k]; - } - - kernel_volume_stack_update_for_subsurface( - kg, emission_sd, &volume_ray, hit_state.volume_stack); - } -# endif /* __VOLUME__ */ - -# ifdef __EMISSION__ - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = (kernel_data.integrator.sample_all_lights_direct) || - (hit_state.flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light( - kg, &bssrdf_sd, emission_sd, &hit_state, throughput, num_samples_inv, L, all); - } -# endif /* __EMISSION__ */ - - /* indirect light */ - kernel_branched_path_surface_indirect_light( - kg, &bssrdf_sd, indirect_sd, emission_sd, throughput, num_samples_inv, &hit_state, L); - } - } - } -} -# endif /* __SUBSURFACE__ */ - -ccl_device void kernel_branched_path_integrate(KernelGlobals *kg, - uint rng_hash, - int sample, - Ray ray, - ccl_global float *buffer, - PathRadiance *L) -{ - /* initialize */ - float3 throughput = one_float3(); - - path_radiance_init(kg, L); - - /* shader data memory used for both volumes and surfaces, saves stack space */ - ShaderData sd; - /* shader data used by emission, shadows, volume stacks, indirect path */ - ShaderDataTinyStorage emission_sd_storage; - ShaderData *emission_sd = AS_SHADER_DATA(&emission_sd_storage); - ShaderData indirect_sd; - - PathState state; - path_state_init(kg, emission_sd, &state, rng_hash, sample, &ray); - - /* Main Loop - * Here we only handle transparency intersections from the camera ray. - * Indirect bounces are handled in kernel_branched_path_surface_indirect_light(). - */ - for (;;) { - /* Find intersection with objects in scene. */ - Intersection isect; - bool hit = kernel_path_scene_intersect(kg, &state, &ray, &isect, L, sd.object); - -# ifdef __VOLUME__ - /* Volume integration. */ - kernel_branched_path_volume( - kg, &sd, &state, &ray, &throughput, &isect, hit, &indirect_sd, emission_sd, L); -# endif /* __VOLUME__ */ - - /* Shade background. */ - if (!hit) { - kernel_path_background(kg, &state, &ray, throughput, &sd, buffer, L); - break; - } - - /* Setup and evaluate shader. */ - shader_setup_from_ray(kg, &sd, &isect, &ray); - - /* Skip most work for volume bounding surface. */ -# ifdef __VOLUME__ - if (!(sd.flag & SD_HAS_ONLY_VOLUME)) { -# endif - - shader_eval_surface(kg, &sd, &state, buffer, state.flag); - shader_merge_closures(&sd); - - /* Apply shadow catcher, holdout, emission. */ - if (!kernel_path_shader_apply(kg, &sd, &state, &ray, throughput, emission_sd, L, buffer)) { - break; - } - - /* transparency termination */ - if (state.flag & PATH_RAY_TRANSPARENT) { - /* path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate */ - float probability = path_state_continuation_probability(kg, &state, throughput); - - if (probability == 0.0f) { - break; - } - else if (probability != 1.0f) { - float terminate = path_state_rng_1D(kg, &state, PRNG_TERMINATE); - - if (terminate >= probability) - break; - - throughput /= probability; - } - } - -# ifdef __DENOISING_FEATURES__ - kernel_update_denoising_features(kg, &sd, &state, L); -# endif - -# ifdef __AO__ - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - kernel_branched_path_ao(kg, &sd, emission_sd, L, &state, throughput); - } -# endif /* __AO__ */ - -# ifdef __SUBSURFACE__ - /* bssrdf scatter to a different location on the same object */ - if (sd.flag & SD_BSSRDF) { - kernel_branched_path_subsurface_scatter( - kg, &sd, &indirect_sd, emission_sd, L, &state, &ray, throughput); - } -# endif /* __SUBSURFACE__ */ - - PathState hit_state = state; - -# ifdef __EMISSION__ - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = (kernel_data.integrator.sample_all_lights_direct) || - (state.flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light( - kg, &sd, emission_sd, &hit_state, throughput, 1.0f, L, all); - } -# endif /* __EMISSION__ */ - - /* indirect light */ - kernel_branched_path_surface_indirect_light( - kg, &sd, &indirect_sd, emission_sd, throughput, 1.0f, &hit_state, L); - - /* continue in case of transparency */ - throughput *= shader_bsdf_transparency(kg, &sd); - - if (is_zero(throughput)) - break; - - /* Update Path State */ - path_state_next(kg, &state, LABEL_TRANSPARENT); - -# ifdef __VOLUME__ - } - else { - if (!path_state_volume_next(kg, &state)) { - break; - } - } -# endif - - ray.P = ray_offset(sd.P, -sd.Ng); - ray.t -= sd.ray_length; /* clipping works through transparent */ - -# ifdef __RAY_DIFFERENTIALS__ - ray.dP = sd.dP; - ray.dD.dx = -sd.dI.dx; - ray.dD.dy = -sd.dI.dy; -# endif /* __RAY_DIFFERENTIALS__ */ - -# ifdef __VOLUME__ - /* enter/exit volume */ - kernel_volume_stack_enter_exit(kg, &sd, state.volume_stack); -# endif /* __VOLUME__ */ - } -} - -ccl_device void kernel_branched_path_trace( - KernelGlobals *kg, ccl_global float *buffer, int sample, int x, int y, int offset, int stride) -{ - /* buffer offset */ - int index = offset + x + y * stride; - int pass_stride = kernel_data.film.pass_stride; - - buffer += index * pass_stride; - - if (kernel_data.film.pass_adaptive_aux_buffer) { - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w > 0.0f) { - return; - } - } - - /* initialize random numbers and ray */ - uint rng_hash; - Ray ray; - - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &ray); - - /* integrate */ - PathRadiance L; - - if (ray.t != 0.0f) { - kernel_branched_path_integrate(kg, rng_hash, sample, ray, buffer, &L); - kernel_write_result(kg, buffer, sample, &L); - } -} - -# endif /* __SPLIT_KERNEL__ */ - -#endif /* __BRANCHED_PATH__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_common.h b/intern/cycles/kernel/kernel_path_common.h deleted file mode 100644 index 815767595a9..00000000000 --- a/intern/cycles/kernel/kernel_path_common.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/util_hash.h" - -CCL_NAMESPACE_BEGIN - -ccl_device_inline void kernel_path_trace_setup( - KernelGlobals *kg, int sample, int x, int y, uint *rng_hash, ccl_addr_space Ray *ray) -{ - float filter_u; - float filter_v; - - int num_samples = kernel_data.integrator.aa_samples; - - path_rng_init(kg, sample, num_samples, rng_hash, x, y, &filter_u, &filter_v); - - /* sample camera ray */ - - float lens_u = 0.0f, lens_v = 0.0f; - - if (kernel_data.cam.aperturesize > 0.0f) - path_rng_2D(kg, *rng_hash, sample, num_samples, PRNG_LENS_U, &lens_u, &lens_v); - - float time = 0.0f; - -#ifdef __CAMERA_MOTION__ - if (kernel_data.cam.shuttertime != -1.0f) - time = path_rng_1D(kg, *rng_hash, sample, num_samples, PRNG_TIME); -#endif - - camera_sample(kg, x, y, filter_u, filter_v, lens_u, lens_v, time, ray); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_state.h b/intern/cycles/kernel/kernel_path_state.h deleted file mode 100644 index bf601580cd0..00000000000 --- a/intern/cycles/kernel/kernel_path_state.h +++ /dev/null @@ -1,276 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device_inline void path_state_init(KernelGlobals *kg, - ShaderData *stack_sd, - ccl_addr_space PathState *state, - uint rng_hash, - int sample, - ccl_addr_space Ray *ray) -{ - state->flag = PATH_RAY_CAMERA | PATH_RAY_MIS_SKIP | PATH_RAY_TRANSPARENT_BACKGROUND; - - state->rng_hash = rng_hash; - state->rng_offset = PRNG_BASE_NUM; - state->sample = sample; - state->num_samples = kernel_data.integrator.aa_samples; - state->branch_factor = 1.0f; - - state->bounce = 0; - state->diffuse_bounce = 0; - state->glossy_bounce = 0; - state->transmission_bounce = 0; - state->transparent_bounce = 0; - -#ifdef __DENOISING_FEATURES__ - if (kernel_data.film.pass_denoising_data) { - state->flag |= PATH_RAY_STORE_SHADOW_INFO; - state->denoising_feature_weight = 1.0f; - state->denoising_feature_throughput = one_float3(); - } - else { - state->denoising_feature_weight = 0.0f; - state->denoising_feature_throughput = zero_float3(); - } -#endif /* __DENOISING_FEATURES__ */ - - state->min_ray_pdf = FLT_MAX; - state->ray_pdf = 0.0f; -#ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -#endif - -#ifdef __VOLUME__ - state->volume_bounce = 0; - state->volume_bounds_bounce = 0; - - if (kernel_data.integrator.use_volumes) { - /* Initialize volume stack with volume we are inside of. */ - kernel_volume_stack_init(kg, stack_sd, state, ray, state->volume_stack); - } - else { - state->volume_stack[0].shader = SHADER_NONE; - } -#endif -} - -ccl_device_inline void path_state_next(KernelGlobals *kg, - ccl_addr_space PathState *state, - int label) -{ - /* ray through transparent keeps same flags from previous ray and is - * not counted as a regular bounce, transparent has separate max */ - if (label & LABEL_TRANSPARENT) { - state->flag |= PATH_RAY_TRANSPARENT; - state->transparent_bounce++; - if (state->transparent_bounce >= kernel_data.integrator.transparent_max_bounce) { - state->flag |= PATH_RAY_TERMINATE_IMMEDIATE; - } - - if (!kernel_data.integrator.transparent_shadows) - state->flag |= PATH_RAY_MIS_SKIP; - - /* random number generator next bounce */ - state->rng_offset += PRNG_BOUNCE_NUM; - - return; - } - - state->bounce++; - if (state->bounce >= kernel_data.integrator.max_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - - state->flag &= ~(PATH_RAY_ALL_VISIBILITY | PATH_RAY_MIS_SKIP); - -#ifdef __VOLUME__ - if (label & LABEL_VOLUME_SCATTER) { - /* volume scatter */ - state->flag |= PATH_RAY_VOLUME_SCATTER; - state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; - - state->volume_bounce++; - if (state->volume_bounce >= kernel_data.integrator.max_volume_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - } - else -#endif - { - /* surface reflection/transmission */ - if (label & LABEL_REFLECT) { - state->flag |= PATH_RAY_REFLECT; - state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; - - if (label & LABEL_DIFFUSE) { - state->diffuse_bounce++; - if (state->diffuse_bounce >= kernel_data.integrator.max_diffuse_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - } - else { - state->glossy_bounce++; - if (state->glossy_bounce >= kernel_data.integrator.max_glossy_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - } - } - else { - kernel_assert(label & LABEL_TRANSMIT); - - state->flag |= PATH_RAY_TRANSMIT; - - if (!(label & LABEL_TRANSMIT_TRANSPARENT)) { - state->flag &= ~PATH_RAY_TRANSPARENT_BACKGROUND; - } - - state->transmission_bounce++; - if (state->transmission_bounce >= kernel_data.integrator.max_transmission_bounce) { - state->flag |= PATH_RAY_TERMINATE_AFTER_TRANSPARENT; - } - } - - /* diffuse/glossy/singular */ - if (label & LABEL_DIFFUSE) { - state->flag |= PATH_RAY_DIFFUSE | PATH_RAY_DIFFUSE_ANCESTOR; - } - else if (label & LABEL_GLOSSY) { - state->flag |= PATH_RAY_GLOSSY; - } - else { - kernel_assert(label & LABEL_SINGULAR); - state->flag |= PATH_RAY_GLOSSY | PATH_RAY_SINGULAR | PATH_RAY_MIS_SKIP; - } - } - - /* random number generator next bounce */ - state->rng_offset += PRNG_BOUNCE_NUM; - -#ifdef __DENOISING_FEATURES__ - if ((state->denoising_feature_weight == 0.0f) && !(state->flag & PATH_RAY_SHADOW_CATCHER)) { - state->flag &= ~PATH_RAY_STORE_SHADOW_INFO; - } -#endif -} - -#ifdef __VOLUME__ -ccl_device_inline bool path_state_volume_next(KernelGlobals *kg, ccl_addr_space PathState *state) -{ - /* For volume bounding meshes we pass through without counting transparent - * bounces, only sanity check in case self intersection gets us stuck. */ - state->volume_bounds_bounce++; - if (state->volume_bounds_bounce > VOLUME_BOUNDS_MAX) { - return false; - } - - /* Random number generator next bounce. */ - if (state->volume_bounds_bounce > 1) { - state->rng_offset += PRNG_BOUNCE_NUM; - } - - return true; -} -#endif - -ccl_device_inline uint path_state_ray_visibility(KernelGlobals *kg, - ccl_addr_space PathState *state) -{ - uint flag = state->flag & PATH_RAY_ALL_VISIBILITY; - - /* for visibility, diffuse/glossy are for reflection only */ - if (flag & PATH_RAY_TRANSMIT) - flag &= ~(PATH_RAY_DIFFUSE | PATH_RAY_GLOSSY); - /* todo: this is not supported as its own ray visibility yet */ - if (state->flag & PATH_RAY_VOLUME_SCATTER) - flag |= PATH_RAY_DIFFUSE; - - return flag; -} - -ccl_device_inline float path_state_continuation_probability(KernelGlobals *kg, - ccl_addr_space PathState *state, - const float3 throughput) -{ - if (state->flag & PATH_RAY_TERMINATE_IMMEDIATE) { - /* Ray is to be terminated immediately. */ - return 0.0f; - } - else if (state->flag & PATH_RAY_TRANSPARENT) { - /* Do at least specified number of bounces without RR. */ - if (state->transparent_bounce <= kernel_data.integrator.transparent_min_bounce) { - return 1.0f; - } -#ifdef __SHADOW_TRICKS__ - /* Exception for shadow catcher not working correctly with RR. */ - else if ((state->flag & PATH_RAY_SHADOW_CATCHER) && (state->transparent_bounce <= 8)) { - return 1.0f; - } -#endif - } - else { - /* Do at least specified number of bounces without RR. */ - if (state->bounce <= kernel_data.integrator.min_bounce) { - return 1.0f; - } -#ifdef __SHADOW_TRICKS__ - /* Exception for shadow catcher not working correctly with RR. */ - else if ((state->flag & PATH_RAY_SHADOW_CATCHER) && (state->bounce <= 3)) { - return 1.0f; - } -#endif - } - - /* Probabilistic termination: use sqrt() to roughly match typical view - * transform and do path termination a bit later on average. */ - return min(sqrtf(max3(fabs(throughput)) * state->branch_factor), 1.0f); -} - -/* TODO(DingTo): Find more meaningful name for this */ -ccl_device_inline void path_state_modify_bounce(ccl_addr_space PathState *state, bool increase) -{ - /* Modify bounce temporarily for shader eval */ - if (increase) - state->bounce += 1; - else - state->bounce -= 1; -} - -ccl_device_inline bool path_state_ao_bounce(KernelGlobals *kg, ccl_addr_space PathState *state) -{ - if (state->bounce <= kernel_data.integrator.ao_bounces) { - return false; - } - - int bounce = state->bounce - state->transmission_bounce - (state->glossy_bounce > 0); - return (bounce > kernel_data.integrator.ao_bounces); -} - -ccl_device_inline void path_state_branch(ccl_addr_space PathState *state, - int branch, - int num_branches) -{ - if (num_branches > 1) { - /* Path is splitting into a branch, adjust so that each branch - * still gets a unique sample from the same sequence. */ - state->sample = state->sample * num_branches + branch; - state->num_samples = state->num_samples * num_branches; - state->branch_factor *= num_branches; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_subsurface.h b/intern/cycles/kernel/kernel_path_subsurface.h deleted file mode 100644 index 97d3f292ca3..00000000000 --- a/intern/cycles/kernel/kernel_path_subsurface.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright 2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __SUBSURFACE__ -# ifndef __KERNEL_CUDA__ -ccl_device -# else -ccl_device_inline -# endif - bool - kernel_path_subsurface_scatter(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - PathRadiance *L, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - ccl_addr_space float3 *throughput, - ccl_addr_space SubsurfaceIndirectRays *ss_indirect) -{ - PROFILING_INIT(kg, PROFILING_SUBSURFACE); - - float bssrdf_u, bssrdf_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - - const ShaderClosure *sc = shader_bssrdf_pick(sd, throughput, &bssrdf_u); - - /* do bssrdf scatter step if we picked a bssrdf closure */ - if (sc) { - /* We should never have two consecutive BSSRDF bounces, - * the second one should be converted to a diffuse BSDF to - * avoid this. - */ - kernel_assert(!(state->flag & PATH_RAY_DIFFUSE_ANCESTOR)); - - uint lcg_state = lcg_state_init_addrspace(state, 0x68bc21eb); - - LocalIntersection ss_isect; - int num_hits = subsurface_scatter_multi_intersect( - kg, &ss_isect, sd, state, sc, &lcg_state, bssrdf_u, bssrdf_v, false); -# ifdef __VOLUME__ - bool need_update_volume_stack = kernel_data.integrator.use_volumes && - sd->object_flag & SD_OBJECT_INTERSECTS_VOLUME; -# endif /* __VOLUME__ */ - - /* Closure memory will be overwritten, so read required variables now. */ - Bssrdf *bssrdf = (Bssrdf *)sc; - ClosureType bssrdf_type = sc->type; - float bssrdf_roughness = bssrdf->roughness; - - /* compute lighting with the BSDF closure */ - for (int hit = 0; hit < num_hits; hit++) { - /* NOTE: We reuse the existing ShaderData, we assume the path - * integration loop stops when this function returns true. - */ - subsurface_scatter_multi_setup(kg, &ss_isect, hit, sd, state, bssrdf_type, bssrdf_roughness); - - kernel_path_surface_connect_light(kg, sd, emission_sd, *throughput, state, L); - - ccl_addr_space PathState *hit_state = &ss_indirect->state[ss_indirect->num_rays]; - ccl_addr_space Ray *hit_ray = &ss_indirect->rays[ss_indirect->num_rays]; - ccl_addr_space float3 *hit_tp = &ss_indirect->throughputs[ss_indirect->num_rays]; - PathRadianceState *hit_L_state = &ss_indirect->L_state[ss_indirect->num_rays]; - - *hit_state = *state; - *hit_ray = *ray; - *hit_tp = *throughput; - *hit_L_state = L->state; - - hit_state->rng_offset += PRNG_BOUNCE_NUM; - - if (kernel_path_surface_bounce(kg, sd, hit_tp, hit_state, hit_L_state, hit_ray)) { -# ifdef __LAMP_MIS__ - hit_state->ray_t = 0.0f; -# endif /* __LAMP_MIS__ */ - -# ifdef __VOLUME__ - if (need_update_volume_stack) { - Ray volume_ray = *ray; - /* Setup ray from previous surface point to the new one. */ - volume_ray.D = normalize_len(hit_ray->P - volume_ray.P, &volume_ray.t); - - kernel_volume_stack_update_for_subsurface( - kg, emission_sd, &volume_ray, hit_state->volume_stack); - } -# endif /* __VOLUME__ */ - ss_indirect->num_rays++; - } - } - return true; - } - return false; -} - -ccl_device_inline void kernel_path_subsurface_init_indirect( - ccl_addr_space SubsurfaceIndirectRays *ss_indirect) -{ - ss_indirect->num_rays = 0; -} - -ccl_device void kernel_path_subsurface_setup_indirect( - KernelGlobals *kg, - ccl_addr_space SubsurfaceIndirectRays *ss_indirect, - ccl_addr_space PathState *state, - ccl_addr_space Ray *ray, - PathRadiance *L, - ccl_addr_space float3 *throughput) -{ - /* Setup state, ray and throughput for indirect SSS rays. */ - ss_indirect->num_rays--; - - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - - *state = ss_indirect->state[ss_indirect->num_rays]; - *ray = ss_indirect->rays[ss_indirect->num_rays]; - L->state = ss_indirect->L_state[ss_indirect->num_rays]; - *throughput = ss_indirect->throughputs[ss_indirect->num_rays]; - - state->rng_offset += ss_indirect->num_rays * PRNG_BOUNCE_NUM; -} - -#endif /* __SUBSURFACE__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_surface.h b/intern/cycles/kernel/kernel_path_surface.h deleted file mode 100644 index ba48c0bdfc4..00000000000 --- a/intern/cycles/kernel/kernel_path_surface.h +++ /dev/null @@ -1,360 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#if defined(__BRANCHED_PATH__) || defined(__SUBSURFACE__) || defined(__SHADOW_TRICKS__) || \ - defined(__BAKING__) -/* branched path tracing: connect path directly to position on one or more lights and add it to L - */ -ccl_device_noinline_cpu void kernel_branched_path_surface_connect_light( - KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - ccl_addr_space PathState *state, - float3 throughput, - float num_samples_adjust, - PathRadiance *L, - int sample_all_lights) -{ -# ifdef __EMISSION__ - /* sample illumination from lights to find path contribution */ - BsdfEval L_light ccl_optional_struct_init; - - int num_lights = 0; - if (kernel_data.integrator.use_direct_light) { - if (sample_all_lights) { - num_lights = kernel_data.integrator.num_all_lights; - if (kernel_data.integrator.pdf_triangles != 0.0f) { - num_lights += 1; - } - } - else { - num_lights = 1; - } - } - - for (int i = 0; i < num_lights; i++) { - /* sample one light at random */ - int num_samples = 1; - int num_all_lights = 1; - uint lamp_rng_hash = state->rng_hash; - bool double_pdf = false; - bool is_mesh_light = false; - bool is_lamp = false; - - if (sample_all_lights) { - /* lamp sampling */ - is_lamp = i < kernel_data.integrator.num_all_lights; - if (is_lamp) { - if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) { - continue; - } - num_samples = ceil_to_int(num_samples_adjust * light_select_num_samples(kg, i)); - num_all_lights = kernel_data.integrator.num_all_lights; - lamp_rng_hash = cmj_hash(state->rng_hash, i); - double_pdf = kernel_data.integrator.pdf_triangles != 0.0f; - } - /* mesh light sampling */ - else { - num_samples = ceil_to_int(num_samples_adjust * kernel_data.integrator.mesh_light_samples); - double_pdf = kernel_data.integrator.num_all_lights != 0; - is_mesh_light = true; - } - } - - float num_samples_inv = num_samples_adjust / (num_samples * num_all_lights); - - for (int j = 0; j < num_samples; j++) { - Ray light_ray ccl_optional_struct_init; - light_ray.t = 0.0f; /* reset ray */ -# ifdef __OBJECT_MOTION__ - light_ray.time = sd->time; -# endif - bool has_emission = false; - - if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) { - float light_u, light_v; - path_branched_rng_2D( - kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); - float terminate = path_branched_rng_light_termination( - kg, lamp_rng_hash, state, j, num_samples); - - /* only sample triangle lights */ - if (is_mesh_light && double_pdf) { - light_u = 0.5f * light_u; - } - - LightSample ls ccl_optional_struct_init; - const int lamp = is_lamp ? i : -1; - if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - /* The sampling probability returned by lamp_light_sample assumes that all lights were - * sampled. However, this code only samples lamps, so if the scene also had mesh lights, - * the real probability is twice as high. */ - if (double_pdf) { - ls.pdf *= 2.0f; - } - - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission) { - if (!blocked) { - /* accumulate */ - path_radiance_accum_light(kg, - L, - state, - throughput * num_samples_inv, - &L_light, - shadow, - num_samples_inv, - is_lamp); - } - else { - path_radiance_accum_total_light(L, state, throughput * num_samples_inv, &L_light); - } - } - } - } -# endif -} - -/* branched path tracing: bounce off or through surface to with new direction stored in ray */ -ccl_device bool kernel_branched_path_surface_bounce(KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, - int sample, - int num_samples, - ccl_addr_space float3 *throughput, - ccl_addr_space PathState *state, - PathRadianceState *L_state, - ccl_addr_space Ray *ray, - float sum_sample_weight) -{ - /* sample BSDF */ - float bsdf_pdf; - BsdfEval bsdf_eval ccl_optional_struct_init; - float3 bsdf_omega_in ccl_optional_struct_init; - differential3 bsdf_domega_in ccl_optional_struct_init; - float bsdf_u, bsdf_v; - path_branched_rng_2D( - kg, state->rng_hash, state, sample, num_samples, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - int label; - - label = shader_bsdf_sample_closure( - kg, sd, sc, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf); - - if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) - return false; - - /* modify throughput */ - path_radiance_bsdf_bounce(kg, L_state, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label); - -# ifdef __DENOISING_FEATURES__ - state->denoising_feature_weight *= sc->sample_weight / (sum_sample_weight * num_samples); -# endif - - /* modify path state */ - path_state_next(kg, state, label); - - /* setup ray */ - ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); - ray->D = normalize(bsdf_omega_in); - ray->t = FLT_MAX; -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD = bsdf_domega_in; -# endif -# ifdef __OBJECT_MOTION__ - ray->time = sd->time; -# endif - -# ifdef __VOLUME__ - /* enter/exit volume */ - if (label & LABEL_TRANSMIT) - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); -# endif - - /* branch RNG state */ - path_state_branch(state, sample, num_samples); - - /* set MIS state */ - state->min_ray_pdf = fminf(bsdf_pdf, FLT_MAX); - state->ray_pdf = bsdf_pdf; -# ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -# endif - - return true; -} - -#endif - -/* path tracing: connect path directly to position on a light and add it to L */ -ccl_device_inline void kernel_path_surface_connect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - float3 throughput, - ccl_addr_space PathState *state, - PathRadiance *L) -{ - PROFILING_INIT(kg, PROFILING_CONNECT_LIGHT); - -#ifdef __EMISSION__ -# ifdef __SHADOW_TRICKS__ - int all = (state->flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light(kg, sd, emission_sd, state, throughput, 1.0f, L, all); -# else - /* sample illumination from lights to find path contribution */ - Ray light_ray ccl_optional_struct_init; - BsdfEval L_light ccl_optional_struct_init; - bool is_lamp = false; - bool has_emission = false; - - light_ray.t = 0.0f; -# ifdef __OBJECT_MOTION__ - light_ray.time = sd->time; -# endif - - if (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)) { - float light_u, light_v; - path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); - - LightSample ls ccl_optional_struct_init; - if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - float terminate = path_state_rng_light_termination(kg, state); - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission) { - if (!blocked) { - /* accumulate */ - path_radiance_accum_light(kg, L, state, throughput, &L_light, shadow, 1.0f, is_lamp); - } - else { - path_radiance_accum_total_light(L, state, throughput, &L_light); - } - } -# endif -#endif -} - -/* path tracing: bounce off or through surface to with new direction stored in ray */ -ccl_device bool kernel_path_surface_bounce(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space float3 *throughput, - ccl_addr_space PathState *state, - PathRadianceState *L_state, - ccl_addr_space Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SURFACE_BOUNCE); - - /* no BSDF? we can stop here */ - if (sd->flag & SD_BSDF) { - /* sample BSDF */ - float bsdf_pdf; - BsdfEval bsdf_eval ccl_optional_struct_init; - float3 bsdf_omega_in ccl_optional_struct_init; - differential3 bsdf_domega_in ccl_optional_struct_init; - float bsdf_u, bsdf_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &bsdf_u, &bsdf_v); - int label; - - label = shader_bsdf_sample( - kg, sd, bsdf_u, bsdf_v, &bsdf_eval, &bsdf_omega_in, &bsdf_domega_in, &bsdf_pdf); - - if (bsdf_pdf == 0.0f || bsdf_eval_is_zero(&bsdf_eval)) - return false; - - /* modify throughput */ - path_radiance_bsdf_bounce(kg, L_state, throughput, &bsdf_eval, bsdf_pdf, state->bounce, label); - - /* set labels */ - if (!(label & LABEL_TRANSPARENT)) { - state->ray_pdf = bsdf_pdf; -#ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -#endif - state->min_ray_pdf = fminf(bsdf_pdf, state->min_ray_pdf); - } - - /* update path state */ - path_state_next(kg, state, label); - - /* setup ray */ - ray->P = ray_offset(sd->P, (label & LABEL_TRANSMIT) ? -sd->Ng : sd->Ng); - ray->D = normalize(bsdf_omega_in); - - if (state->bounce == 0) - ray->t -= sd->ray_length; /* clipping works through transparent */ - else - ray->t = FLT_MAX; - -#ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD = bsdf_domega_in; -#endif - -#ifdef __VOLUME__ - /* enter/exit volume */ - if (label & LABEL_TRANSMIT) - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); -#endif - return true; - } -#ifdef __VOLUME__ - else if (sd->flag & SD_HAS_ONLY_VOLUME) { - if (!path_state_volume_next(kg, state)) { - return false; - } - - if (state->bounce == 0) - ray->t -= sd->ray_length; /* clipping works through transparent */ - else - ray->t = FLT_MAX; - - /* setup ray position, direction stays unchanged */ - ray->P = ray_offset(sd->P, -sd->Ng); -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; -# endif - - /* enter/exit volume */ - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); - return true; - } -#endif - else { - /* no bsdf or volume? */ - return false; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_path_volume.h b/intern/cycles/kernel/kernel_path_volume.h deleted file mode 100644 index a787910e65c..00000000000 --- a/intern/cycles/kernel/kernel_path_volume.h +++ /dev/null @@ -1,260 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __VOLUME_SCATTER__ - -ccl_device_inline void kernel_path_volume_connect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - float3 throughput, - ccl_addr_space PathState *state, - PathRadiance *L) -{ -# ifdef __EMISSION__ - /* sample illumination from lights to find path contribution */ - Ray light_ray ccl_optional_struct_init; - BsdfEval L_light ccl_optional_struct_init; - bool is_lamp = false; - bool has_emission = false; - - light_ray.t = 0.0f; -# ifdef __OBJECT_MOTION__ - /* connect to light from given point where shader has been evaluated */ - light_ray.time = sd->time; -# endif - - if (kernel_data.integrator.use_direct_light) { - float light_u, light_v; - path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); - - LightSample ls ccl_optional_struct_init; - if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - float terminate = path_state_rng_light_termination(kg, state); - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission && !blocked) { - /* accumulate */ - path_radiance_accum_light(kg, L, state, throughput, &L_light, shadow, 1.0f, is_lamp); - } -# endif /* __EMISSION__ */ -} - -ccl_device_noinline_cpu bool kernel_path_volume_bounce(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space float3 *throughput, - ccl_addr_space PathState *state, - PathRadianceState *L_state, - ccl_addr_space Ray *ray) -{ - /* sample phase function */ - float phase_pdf; - BsdfEval phase_eval ccl_optional_struct_init; - float3 phase_omega_in ccl_optional_struct_init; - differential3 phase_domega_in ccl_optional_struct_init; - float phase_u, phase_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &phase_u, &phase_v); - int label; - - label = shader_volume_phase_sample( - kg, sd, phase_u, phase_v, &phase_eval, &phase_omega_in, &phase_domega_in, &phase_pdf); - - if (phase_pdf == 0.0f || bsdf_eval_is_zero(&phase_eval)) - return false; - - /* modify throughput */ - path_radiance_bsdf_bounce(kg, L_state, throughput, &phase_eval, phase_pdf, state->bounce, label); - - /* set labels */ - state->ray_pdf = phase_pdf; -# ifdef __LAMP_MIS__ - state->ray_t = 0.0f; -# endif - state->min_ray_pdf = fminf(phase_pdf, state->min_ray_pdf); - - /* update path state */ - path_state_next(kg, state, label); - - /* Russian roulette termination of volume ray scattering. */ - float probability = path_state_continuation_probability(kg, state, *throughput); - - if (probability == 0.0f) { - return false; - } - else if (probability != 1.0f) { - /* Use dimension from the previous bounce, has not been used yet. */ - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE - PRNG_BOUNCE_NUM); - - if (terminate >= probability) { - return false; - } - - *throughput /= probability; - } - - /* setup ray */ - ray->P = sd->P; - ray->D = phase_omega_in; - ray->t = FLT_MAX; - -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD = phase_domega_in; -# endif - - return true; -} - -# if !defined(__SPLIT_KERNEL__) && (defined(__BRANCHED_PATH__) || defined(__VOLUME_DECOUPLED__)) -ccl_device void kernel_branched_path_volume_connect_light(KernelGlobals *kg, - ShaderData *sd, - ShaderData *emission_sd, - float3 throughput, - ccl_addr_space PathState *state, - PathRadiance *L, - bool sample_all_lights, - Ray *ray, - const VolumeSegment *segment) -{ -# ifdef __EMISSION__ - BsdfEval L_light ccl_optional_struct_init; - - int num_lights = 1; - if (sample_all_lights) { - num_lights = kernel_data.integrator.num_all_lights; - if (kernel_data.integrator.pdf_triangles != 0.0f) { - num_lights += 1; - } - } - - for (int i = 0; i < num_lights; ++i) { - /* sample one light at random */ - int num_samples = 1; - int num_all_lights = 1; - uint lamp_rng_hash = state->rng_hash; - bool double_pdf = false; - bool is_mesh_light = false; - bool is_lamp = false; - - if (sample_all_lights) { - /* lamp sampling */ - is_lamp = i < kernel_data.integrator.num_all_lights; - if (is_lamp) { - if (UNLIKELY(light_select_reached_max_bounces(kg, i, state->bounce))) { - continue; - } - num_samples = light_select_num_samples(kg, i); - num_all_lights = kernel_data.integrator.num_all_lights; - lamp_rng_hash = cmj_hash(state->rng_hash, i); - double_pdf = kernel_data.integrator.pdf_triangles != 0.0f; - } - /* mesh light sampling */ - else { - num_samples = kernel_data.integrator.mesh_light_samples; - double_pdf = kernel_data.integrator.num_all_lights != 0; - is_mesh_light = true; - } - } - - float num_samples_inv = 1.0f / (num_samples * num_all_lights); - - for (int j = 0; j < num_samples; j++) { - Ray light_ray ccl_optional_struct_init; - light_ray.t = 0.0f; /* reset ray */ -# ifdef __OBJECT_MOTION__ - light_ray.time = sd->time; -# endif - bool has_emission = false; - - float3 tp = throughput; - - if (kernel_data.integrator.use_direct_light) { - /* sample random position on random light/triangle */ - float light_u, light_v; - path_branched_rng_2D( - kg, lamp_rng_hash, state, j, num_samples, PRNG_LIGHT_U, &light_u, &light_v); - - /* only sample triangle lights */ - if (is_mesh_light && double_pdf) { - light_u = 0.5f * light_u; - } - - LightSample ls ccl_optional_struct_init; - const int lamp = is_lamp ? i : -1; - light_sample(kg, lamp, light_u, light_v, sd->time, ray->P, state->bounce, &ls); - - /* sample position on volume segment */ - float rphase = path_branched_rng_1D( - kg, state->rng_hash, state, j, num_samples, PRNG_PHASE_CHANNEL); - float rscatter = path_branched_rng_1D( - kg, state->rng_hash, state, j, num_samples, PRNG_SCATTER_DISTANCE); - - VolumeIntegrateResult result = kernel_volume_decoupled_scatter(kg, - state, - ray, - sd, - &tp, - rphase, - rscatter, - segment, - (ls.t != FLT_MAX) ? &ls.P : - NULL, - false); - - if (result == VOLUME_PATH_SCATTERED) { - /* todo: split up light_sample so we don't have to call it again with new position */ - if (light_sample(kg, lamp, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - if (double_pdf) { - ls.pdf *= 2.0f; - } - - /* sample random light */ - float terminate = path_branched_rng_light_termination( - kg, state->rng_hash, state, j, num_samples); - has_emission = direct_emission( - kg, sd, emission_sd, &ls, state, &light_ray, &L_light, &is_lamp, terminate); - } - } - } - - /* trace shadow ray */ - float3 shadow; - - const bool blocked = shadow_blocked(kg, sd, emission_sd, state, &light_ray, &shadow); - - if (has_emission && !blocked) { - /* accumulate */ - path_radiance_accum_light( - kg, L, state, tp * num_samples_inv, &L_light, shadow, num_samples_inv, is_lamp); - } - } - } -# endif /* __EMISSION__ */ -} -# endif /* __SPLIT_KERNEL__ */ - -#endif /* __VOLUME_SCATTER__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_queues.h b/intern/cycles/kernel/kernel_queues.h deleted file mode 100644 index d8cc08b3e85..00000000000 --- a/intern/cycles/kernel/kernel_queues.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_QUEUE_H__ -#define __KERNEL_QUEUE_H__ - -CCL_NAMESPACE_BEGIN - -/* - * Queue utility functions for split kernel - */ -#ifdef __KERNEL_OPENCL__ -# pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable -# pragma OPENCL EXTENSION cl_khr_local_int32_base_atomics : enable -#endif - -/* - * Enqueue ray index into the queue - */ -ccl_device void enqueue_ray_index( - int ray_index, /* Ray index to be enqueued. */ - int queue_number, /* Queue in which the ray index should be enqueued. */ - ccl_global int *queues, /* Buffer of all queues. */ - int queue_size, /* Size of each queue. */ - ccl_global int *queue_index) /* Array of size num_queues; Used for atomic increment. */ -{ - /* This thread's queue index. */ - int my_queue_index = atomic_fetch_and_inc_uint32((ccl_global uint *)&queue_index[queue_number]) + - (queue_number * queue_size); - queues[my_queue_index] = ray_index; -} - -/* - * Get the ray index for this thread - * Returns a positive ray_index for threads that have to do some work; - * Returns 'QUEUE_EMPTY_SLOT' for threads that don't have any work - * i.e All ray's in the queue has been successfully allocated and there - * is no more ray to allocate to other threads. - */ -ccl_device int get_ray_index( - KernelGlobals *kg, - int thread_index, /* Global thread index. */ - int queue_number, /* Queue to operate on. */ - ccl_global int *queues, /* Buffer of all queues. */ - int queuesize, /* Size of a queue. */ - int empty_queue) /* Empty the queue slot as soon as we fetch the ray index. */ -{ - int ray_index = queues[queue_number * queuesize + thread_index]; - if (empty_queue && ray_index != QUEUE_EMPTY_SLOT) { - queues[queue_number * queuesize + thread_index] = QUEUE_EMPTY_SLOT; - } - return ray_index; -} - -/* The following functions are to realize Local memory variant of enqueue ray index function. */ - -/* All threads should call this function. */ -ccl_device void enqueue_ray_index_local( - int ray_index, /* Ray index to enqueue. */ - int queue_number, /* Queue in which to enqueue ray index. */ - char enqueue_flag, /* True for threads whose ray index has to be enqueued. */ - int queuesize, /* queue size. */ - ccl_local_param unsigned int *local_queue_atomics, /* To do local queue atomics. */ - ccl_global int *Queue_data, /* Queues. */ - ccl_global int *Queue_index) /* To do global queue atomics. */ -{ - int lidx = ccl_local_id(1) * ccl_local_size(0) + ccl_local_id(0); - - /* Get local queue id. */ - unsigned int lqidx; - if (enqueue_flag) { - lqidx = atomic_fetch_and_inc_uint32(local_queue_atomics); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - /* Get global queue offset. */ - if (lidx == 0) { - *local_queue_atomics = atomic_fetch_and_add_uint32( - (ccl_global uint *)&Queue_index[queue_number], *local_queue_atomics); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - /* Get global queue index and enqueue ray. */ - if (enqueue_flag) { - unsigned int my_gqidx = queue_number * queuesize + (*local_queue_atomics) + lqidx; - Queue_data[my_gqidx] = ray_index; - } -} - -ccl_device unsigned int get_local_queue_index( - int queue_number, /* Queue in which to enqueue the ray; -1 if no queue */ - ccl_local_param unsigned int *local_queue_atomics) -{ - int my_lqidx = atomic_fetch_and_inc_uint32(&local_queue_atomics[queue_number]); - return my_lqidx; -} - -ccl_device unsigned int get_global_per_queue_offset( - int queue_number, - ccl_local_param unsigned int *local_queue_atomics, - ccl_global int *global_queue_atomics) -{ - unsigned int queue_offset = atomic_fetch_and_add_uint32( - (ccl_global uint *)&global_queue_atomics[queue_number], local_queue_atomics[queue_number]); - return queue_offset; -} - -ccl_device unsigned int get_global_queue_index( - int queue_number, - int queuesize, - unsigned int lqidx, - ccl_local_param unsigned int *global_per_queue_offset) -{ - int my_gqidx = queuesize * queue_number + lqidx + global_per_queue_offset[queue_number]; - return my_gqidx; -} - -ccl_device int dequeue_ray_index(int queue_number, - ccl_global int *queues, - int queue_size, - ccl_global int *queue_index) -{ - int index = atomic_fetch_and_dec_uint32((ccl_global uint *)&queue_index[queue_number]) - 1; - - if (index < 0) { - return QUEUE_EMPTY_SLOT; - } - - return queues[index + queue_number * queue_size]; -} - -CCL_NAMESPACE_END - -#endif // __KERNEL_QUEUE_H__ diff --git a/intern/cycles/kernel/kernel_random.h b/intern/cycles/kernel/kernel_random.h deleted file mode 100644 index 49e5e25c2e0..00000000000 --- a/intern/cycles/kernel/kernel_random.h +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_jitter.h" -#include "util/util_hash.h" - -CCL_NAMESPACE_BEGIN - -/* Pseudo random numbers, uncomment this for debugging correlations. Only run - * this single threaded on a CPU for repeatable results. */ -//#define __DEBUG_CORRELATION__ - -/* High Dimensional Sobol. - * - * Multidimensional sobol with generator matrices. Dimension 0 and 1 are equal - * to classic Van der Corput and Sobol sequences. */ - -#ifdef __SOBOL__ - -/* Skip initial numbers that for some dimensions have clear patterns that - * don't cover the entire sample space. Ideally we would have a better - * progressive pattern that doesn't suffer from this problem, because even - * with this offset some dimensions are quite poor. - */ -# define SOBOL_SKIP 64 - -ccl_device uint sobol_dimension(KernelGlobals *kg, int index, int dimension) -{ - uint result = 0; - uint i = index + SOBOL_SKIP; - for (int j = 0, x; (x = find_first_set(i)); i >>= x) { - j += x; - result ^= kernel_tex_fetch(__sample_pattern_lut, 32 * dimension + j - 1); - } - return result; -} - -#endif /* __SOBOL__ */ - -ccl_device_forceinline float path_rng_1D( - KernelGlobals *kg, uint rng_hash, int sample, int num_samples, int dimension) -{ -#ifdef __DEBUG_CORRELATION__ - return (float)drand48(); -#endif - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) { - return pmj_sample_1D(kg, sample, rng_hash, dimension); - } -#ifdef __CMJ__ -# ifdef __SOBOL__ - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_CMJ) -# endif - { - /* Correlated multi-jitter. */ - int p = rng_hash + dimension; - return cmj_sample_1D(sample, num_samples, p); - } -#endif - -#ifdef __SOBOL__ - /* Sobol sequence value using direction vectors. */ - uint result = sobol_dimension(kg, sample, dimension); - float r = (float)result * (1.0f / (float)0xFFFFFFFF); - - /* Cranly-Patterson rotation using rng seed */ - float shift; - - /* Hash rng with dimension to solve correlation issues. - * See T38710, T50116. - */ - uint tmp_rng = cmj_hash_simple(dimension, rng_hash); - shift = tmp_rng * (1.0f / (float)0xFFFFFFFF); - - return r + shift - floorf(r + shift); -#endif -} - -ccl_device_forceinline void path_rng_2D(KernelGlobals *kg, - uint rng_hash, - int sample, - int num_samples, - int dimension, - float *fx, - float *fy) -{ -#ifdef __DEBUG_CORRELATION__ - *fx = (float)drand48(); - *fy = (float)drand48(); - return; -#endif - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) { - const float2 f = pmj_sample_2D(kg, sample, rng_hash, dimension); - *fx = f.x; - *fy = f.y; - return; - } -#ifdef __CMJ__ -# ifdef __SOBOL__ - if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_CMJ) -# endif - { - /* Correlated multi-jitter. */ - int p = rng_hash + dimension; - cmj_sample_2D(sample, num_samples, p, fx, fy); - return; - } -#endif - -#ifdef __SOBOL__ - /* Sobol. */ - *fx = path_rng_1D(kg, rng_hash, sample, num_samples, dimension); - *fy = path_rng_1D(kg, rng_hash, sample, num_samples, dimension + 1); -#endif -} - -ccl_device_inline void path_rng_init(KernelGlobals *kg, - int sample, - int num_samples, - uint *rng_hash, - int x, - int y, - float *fx, - float *fy) -{ - /* load state */ - *rng_hash = hash_uint2(x, y); - *rng_hash ^= kernel_data.integrator.seed; - -#ifdef __DEBUG_CORRELATION__ - srand48(*rng_hash + sample); -#endif - - if (sample == 0) { - *fx = 0.5f; - *fy = 0.5f; - } - else { - path_rng_2D(kg, *rng_hash, sample, num_samples, PRNG_FILTER_U, fx, fy); - } -} - -/* Linear Congruential Generator */ - -ccl_device uint lcg_step_uint(uint *rng) -{ - /* implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return *rng; -} - -ccl_device float lcg_step_float(uint *rng) -{ - /* implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return (float)*rng * (1.0f / (float)0xFFFFFFFF); -} - -ccl_device uint lcg_init(uint seed) -{ - uint rng = seed; - lcg_step_uint(&rng); - return rng; -} - -/* Path Tracing Utility Functions - * - * For each random number in each step of the path we must have a unique - * dimension to avoid using the same sequence twice. - * - * For branches in the path we must be careful not to reuse the same number - * in a sequence and offset accordingly. - */ - -ccl_device_inline float path_state_rng_1D(KernelGlobals *kg, - const ccl_addr_space PathState *state, - int dimension) -{ - return path_rng_1D( - kg, state->rng_hash, state->sample, state->num_samples, state->rng_offset + dimension); -} - -ccl_device_inline void path_state_rng_2D( - KernelGlobals *kg, const ccl_addr_space PathState *state, int dimension, float *fx, float *fy) -{ - path_rng_2D(kg, - state->rng_hash, - state->sample, - state->num_samples, - state->rng_offset + dimension, - fx, - fy); -} - -ccl_device_inline float path_state_rng_1D_hash(KernelGlobals *kg, - const ccl_addr_space PathState *state, - uint hash) -{ - /* Use a hash instead of dimension, this is not great but avoids adding - * more dimensions to each bounce which reduces quality of dimensions we - * are already using. */ - return path_rng_1D(kg, - cmj_hash_simple(state->rng_hash, hash), - state->sample, - state->num_samples, - state->rng_offset); -} - -ccl_device_inline float path_branched_rng_1D(KernelGlobals *kg, - uint rng_hash, - const ccl_addr_space PathState *state, - int branch, - int num_branches, - int dimension) -{ - return path_rng_1D(kg, - rng_hash, - state->sample * num_branches + branch, - state->num_samples * num_branches, - state->rng_offset + dimension); -} - -ccl_device_inline void path_branched_rng_2D(KernelGlobals *kg, - uint rng_hash, - const ccl_addr_space PathState *state, - int branch, - int num_branches, - int dimension, - float *fx, - float *fy) -{ - path_rng_2D(kg, - rng_hash, - state->sample * num_branches + branch, - state->num_samples * num_branches, - state->rng_offset + dimension, - fx, - fy); -} - -/* Utility functions to get light termination value, - * since it might not be needed in many cases. - */ -ccl_device_inline float path_state_rng_light_termination(KernelGlobals *kg, - const ccl_addr_space PathState *state) -{ - if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { - return path_state_rng_1D(kg, state, PRNG_LIGHT_TERMINATE); - } - return 0.0f; -} - -ccl_device_inline float path_branched_rng_light_termination(KernelGlobals *kg, - uint rng_hash, - const ccl_addr_space PathState *state, - int branch, - int num_branches) -{ - if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { - return path_branched_rng_1D(kg, rng_hash, state, branch, num_branches, PRNG_LIGHT_TERMINATE); - } - return 0.0f; -} - -ccl_device_inline uint lcg_state_init(PathState *state, uint scramble) -{ - return lcg_init(state->rng_hash + state->rng_offset + state->sample * scramble); -} - -ccl_device_inline uint lcg_state_init_addrspace(ccl_addr_space PathState *state, uint scramble) -{ - return lcg_init(state->rng_hash + state->rng_offset + state->sample * scramble); -} - -ccl_device float lcg_step_float_addrspace(ccl_addr_space uint *rng) -{ - /* Implicit mod 2^32 */ - *rng = (1103515245 * (*rng) + 12345); - return (float)*rng * (1.0f / (float)0xFFFFFFFF); -} - -ccl_device_inline bool sample_is_even(int pattern, int sample) -{ - if (pattern == SAMPLING_PATTERN_PMJ) { - /* See Section 10.2.1, "Progressive Multi-Jittered Sample Sequences", Christensen et al. - * We can use this to get divide sample sequence into two classes for easier variance - * estimation. */ -#if defined(__GNUC__) && !defined(__KERNEL_GPU__) - return __builtin_popcount(sample & 0xaaaaaaaa) & 1; -#elif defined(__NVCC__) - return __popc(sample & 0xaaaaaaaa) & 1; -#elif defined(__KERNEL_OPENCL__) - return popcount(sample & 0xaaaaaaaa) & 1; -#else - /* TODO(Stefan): pop-count intrinsic for Windows with fallback for older CPUs. */ - int i = sample & 0xaaaaaaaa; - i = i - ((i >> 1) & 0x55555555); - i = (i & 0x33333333) + ((i >> 2) & 0x33333333); - i = (((i + (i >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; - return i & 1; -#endif - } - else { - /* TODO(Stefan): Are there reliable ways of dividing CMJ and Sobol into two classes? */ - return sample & 0x1; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_shader.h b/intern/cycles/kernel/kernel_shader.h deleted file mode 100644 index 7f02e6fc7b3..00000000000 --- a/intern/cycles/kernel/kernel_shader.h +++ /dev/null @@ -1,1367 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * ShaderData, used in four steps: - * - * Setup from incoming ray, sampled position and background. - * Execute for surface, volume or displacement. - * Evaluate one or more closures. - * Release. - */ - -// clang-format off -#include "kernel/closure/alloc.h" -#include "kernel/closure/bsdf_util.h" -#include "kernel/closure/bsdf.h" -#include "kernel/closure/emissive.h" -// clang-format on - -#include "kernel/svm/svm.h" - -CCL_NAMESPACE_BEGIN - -/* ShaderData setup from incoming ray */ - -#ifdef __OBJECT_MOTION__ -ccl_device void shader_setup_object_transforms(KernelGlobals *kg, ShaderData *sd, float time) -{ - if (sd->object_flag & SD_OBJECT_MOTION) { - sd->ob_tfm = object_fetch_transform_motion(kg, sd->object, time); - sd->ob_itfm = transform_quick_inverse(sd->ob_tfm); - } - else { - sd->ob_tfm = object_fetch_transform(kg, sd->object, OBJECT_TRANSFORM); - sd->ob_itfm = object_fetch_transform(kg, sd->object, OBJECT_INVERSE_TRANSFORM); - } -} -#endif - -#ifdef __KERNEL_OPTIX__ -ccl_device_inline -#else -ccl_device_noinline -#endif - void - shader_setup_from_ray(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - sd->object = (isect->object == OBJECT_NONE) ? kernel_tex_fetch(__prim_object, isect->prim) : - isect->object; - sd->lamp = LAMP_NONE; - - sd->type = isect->type; - sd->flag = 0; - sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); - - /* matrices and time */ -#ifdef __OBJECT_MOTION__ - shader_setup_object_transforms(kg, sd, ray->time); -#endif - sd->time = ray->time; - - sd->prim = kernel_tex_fetch(__prim_index, isect->prim); - sd->ray_length = isect->t; - - sd->u = isect->u; - sd->v = isect->v; - -#ifdef __HAIR__ - if (sd->type & PRIMITIVE_ALL_CURVE) { - /* curve */ - curve_shader_setup(kg, sd, isect, ray); - } - else -#endif - if (sd->type & PRIMITIVE_TRIANGLE) { - /* static triangle */ - float3 Ng = triangle_normal(kg, sd); - sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); - - /* vectors */ - sd->P = triangle_refine(kg, sd, isect, ray); - sd->Ng = Ng; - sd->N = Ng; - - /* smooth normal */ - if (sd->shader & SHADER_SMOOTH_NORMAL) - sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); - -#ifdef __DPDU__ - /* dPdu/dPdv */ - triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); -#endif - } - else { - /* motion triangle */ - motion_triangle_shader_setup(kg, sd, isect, ray, false); - } - - sd->I = -ray->D; - - sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - - if (isect->object != OBJECT_NONE) { - /* instance transform */ - object_normal_transform_auto(kg, sd, &sd->N); - object_normal_transform_auto(kg, sd, &sd->Ng); -#ifdef __DPDU__ - object_dir_transform_auto(kg, sd, &sd->dPdu); - object_dir_transform_auto(kg, sd, &sd->dPdv); -#endif - } - - /* backfacing test */ - bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); - - if (backfacing) { - sd->flag |= SD_BACKFACING; - sd->Ng = -sd->Ng; - sd->N = -sd->N; -#ifdef __DPDU__ - sd->dPdu = -sd->dPdu; - sd->dPdv = -sd->dPdv; -#endif - } - -#ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - differential_transfer(&sd->dP, ray->dP, ray->D, ray->dD, sd->Ng, isect->t); - differential_incoming(&sd->dI, ray->dD); - differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); -#endif - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} - -/* ShaderData setup from BSSRDF scatter */ - -#ifdef __SUBSURFACE__ -# ifndef __KERNEL_CUDA__ -ccl_device -# else -ccl_device_inline -# endif - void - shader_setup_from_subsurface(KernelGlobals *kg, - ShaderData *sd, - const Intersection *isect, - const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - const bool backfacing = sd->flag & SD_BACKFACING; - - /* object, matrices, time, ray_length stay the same */ - sd->flag = 0; - sd->object_flag = kernel_tex_fetch(__object_flag, sd->object); - sd->prim = kernel_tex_fetch(__prim_index, isect->prim); - sd->type = isect->type; - - sd->u = isect->u; - sd->v = isect->v; - - /* fetch triangle data */ - if (sd->type == PRIMITIVE_TRIANGLE) { - float3 Ng = triangle_normal(kg, sd); - sd->shader = kernel_tex_fetch(__tri_shader, sd->prim); - - /* static triangle */ - sd->P = triangle_refine_local(kg, sd, isect, ray); - sd->Ng = Ng; - sd->N = Ng; - - if (sd->shader & SHADER_SMOOTH_NORMAL) - sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); - -# ifdef __DPDU__ - /* dPdu/dPdv */ - triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); -# endif - } - else { - /* motion triangle */ - motion_triangle_shader_setup(kg, sd, isect, ray, true); - } - - sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - - if (isect->object != OBJECT_NONE) { - /* instance transform */ - object_normal_transform_auto(kg, sd, &sd->N); - object_normal_transform_auto(kg, sd, &sd->Ng); -# ifdef __DPDU__ - object_dir_transform_auto(kg, sd, &sd->dPdu); - object_dir_transform_auto(kg, sd, &sd->dPdv); -# endif - } - - /* backfacing test */ - if (backfacing) { - sd->flag |= SD_BACKFACING; - sd->Ng = -sd->Ng; - sd->N = -sd->N; -# ifdef __DPDU__ - sd->dPdu = -sd->dPdu; - sd->dPdv = -sd->dPdv; -# endif - } - - /* should not get used in principle as the shading will only use a diffuse - * BSDF, but the shader might still access it */ - sd->I = sd->N; - -# ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - differential_dudv(&sd->du, &sd->dv, sd->dPdu, sd->dPdv, sd->dP, sd->Ng); - /* don't modify dP and dI */ -# endif - - PROFILING_SHADER(sd->shader); -} -#endif - -/* ShaderData setup from position sampled on mesh */ - -ccl_device_inline void shader_setup_from_sample(KernelGlobals *kg, - ShaderData *sd, - const float3 P, - const float3 Ng, - const float3 I, - int shader, - int object, - int prim, - float u, - float v, - float t, - float time, - bool object_space, - int lamp) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - /* vectors */ - sd->P = P; - sd->N = Ng; - sd->Ng = Ng; - sd->I = I; - sd->shader = shader; - if (prim != PRIM_NONE) - sd->type = PRIMITIVE_TRIANGLE; - else if (lamp != LAMP_NONE) - sd->type = PRIMITIVE_LAMP; - else - sd->type = PRIMITIVE_NONE; - - /* primitive */ - sd->object = object; - sd->lamp = LAMP_NONE; - /* Currently no access to bvh prim index for strand sd->prim. */ - sd->prim = prim; - sd->u = u; - sd->v = v; - sd->time = time; - sd->ray_length = t; - - sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - sd->object_flag = 0; - if (sd->object != OBJECT_NONE) { - sd->object_flag |= kernel_tex_fetch(__object_flag, sd->object); - -#ifdef __OBJECT_MOTION__ - shader_setup_object_transforms(kg, sd, time); - } - else if (lamp != LAMP_NONE) { - sd->ob_tfm = lamp_fetch_transform(kg, lamp, false); - sd->ob_itfm = lamp_fetch_transform(kg, lamp, true); - sd->lamp = lamp; -#else - } - else if (lamp != LAMP_NONE) { - sd->lamp = lamp; -#endif - } - - /* transform into world space */ - if (object_space) { - object_position_transform_auto(kg, sd, &sd->P); - object_normal_transform_auto(kg, sd, &sd->Ng); - sd->N = sd->Ng; - object_dir_transform_auto(kg, sd, &sd->I); - } - - if (sd->type & PRIMITIVE_TRIANGLE) { - /* smooth normal */ - if (sd->shader & SHADER_SMOOTH_NORMAL) { - sd->N = triangle_smooth_normal(kg, Ng, sd->prim, sd->u, sd->v); - - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_normal_transform_auto(kg, sd, &sd->N); - } - } - - /* dPdu/dPdv */ -#ifdef __DPDU__ - triangle_dPdudv(kg, sd->prim, &sd->dPdu, &sd->dPdv); - - if (!(sd->object_flag & SD_OBJECT_TRANSFORM_APPLIED)) { - object_dir_transform_auto(kg, sd, &sd->dPdu); - object_dir_transform_auto(kg, sd, &sd->dPdv); - } -#endif - } - else { -#ifdef __DPDU__ - sd->dPdu = zero_float3(); - sd->dPdv = zero_float3(); -#endif - } - - /* backfacing test */ - if (sd->prim != PRIM_NONE) { - bool backfacing = (dot(sd->Ng, sd->I) < 0.0f); - - if (backfacing) { - sd->flag |= SD_BACKFACING; - sd->Ng = -sd->Ng; - sd->N = -sd->N; -#ifdef __DPDU__ - sd->dPdu = -sd->dPdu; - sd->dPdv = -sd->dPdv; -#endif - } - } - -#ifdef __RAY_DIFFERENTIALS__ - /* no ray differentials here yet */ - sd->dP = differential3_zero(); - sd->dI = differential3_zero(); - sd->du = differential_zero(); - sd->dv = differential_zero(); -#endif - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} - -/* ShaderData setup for displacement */ - -ccl_device void shader_setup_from_displace( - KernelGlobals *kg, ShaderData *sd, int object, int prim, float u, float v) -{ - float3 P, Ng, I = zero_float3(); - int shader; - - triangle_point_normal(kg, object, prim, u, v, &P, &Ng, &shader); - - /* force smooth shading for displacement */ - shader |= SHADER_SMOOTH_NORMAL; - - shader_setup_from_sample( - kg, - sd, - P, - Ng, - I, - shader, - object, - prim, - u, - v, - 0.0f, - 0.5f, - !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_TRANSFORM_APPLIED), - LAMP_NONE); -} - -/* ShaderData setup from ray into background */ - -ccl_device_inline void shader_setup_from_background(KernelGlobals *kg, - ShaderData *sd, - const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - /* vectors */ - sd->P = ray->D; - sd->N = -ray->D; - sd->Ng = -ray->D; - sd->I = -ray->D; - sd->shader = kernel_data.background.surface_shader; - sd->flag = kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - sd->object_flag = 0; - sd->time = ray->time; - sd->ray_length = 0.0f; - - sd->object = OBJECT_NONE; - sd->lamp = LAMP_NONE; - sd->prim = PRIM_NONE; - sd->u = 0.0f; - sd->v = 0.0f; - -#ifdef __DPDU__ - /* dPdu/dPdv */ - sd->dPdu = zero_float3(); - sd->dPdv = zero_float3(); -#endif - -#ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - sd->dP = ray->dD; - differential_incoming(&sd->dI, sd->dP); - sd->du = differential_zero(); - sd->dv = differential_zero(); -#endif - - /* for NDC coordinates */ - sd->ray_P = ray->P; - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} - -/* ShaderData setup from point inside volume */ - -#ifdef __VOLUME__ -ccl_device_inline void shader_setup_from_volume(KernelGlobals *kg, ShaderData *sd, const Ray *ray) -{ - PROFILING_INIT(kg, PROFILING_SHADER_SETUP); - - /* vectors */ - sd->P = ray->P; - sd->N = -ray->D; - sd->Ng = -ray->D; - sd->I = -ray->D; - sd->shader = SHADER_NONE; - sd->flag = 0; - sd->object_flag = 0; - sd->time = ray->time; - sd->ray_length = 0.0f; /* todo: can we set this to some useful value? */ - - sd->object = OBJECT_NONE; /* todo: fill this for texture coordinates */ - sd->lamp = LAMP_NONE; - sd->prim = PRIM_NONE; - sd->type = PRIMITIVE_NONE; - - sd->u = 0.0f; - sd->v = 0.0f; - -# ifdef __DPDU__ - /* dPdu/dPdv */ - sd->dPdu = zero_float3(); - sd->dPdv = zero_float3(); -# endif - -# ifdef __RAY_DIFFERENTIALS__ - /* differentials */ - sd->dP = ray->dD; - differential_incoming(&sd->dI, sd->dP); - sd->du = differential_zero(); - sd->dv = differential_zero(); -# endif - - /* for NDC coordinates */ - sd->ray_P = ray->P; - sd->ray_dP = ray->dP; - - PROFILING_SHADER(sd->shader); - PROFILING_OBJECT(sd->object); -} -#endif /* __VOLUME__ */ - -/* Merging */ - -#if defined(__BRANCHED_PATH__) || defined(__VOLUME__) -ccl_device_inline void shader_merge_closures(ShaderData *sd) -{ - /* merge identical closures, better when we sample a single closure at a time */ - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sci = &sd->closure[i]; - - for (int j = i + 1; j < sd->num_closure; j++) { - ShaderClosure *scj = &sd->closure[j]; - - if (sci->type != scj->type) - continue; - if (!bsdf_merge(sci, scj)) - continue; - - sci->weight += scj->weight; - sci->sample_weight += scj->sample_weight; - - int size = sd->num_closure - (j + 1); - if (size > 0) { - for (int k = 0; k < size; k++) { - scj[k] = scj[k + 1]; - } - } - - sd->num_closure--; - kernel_assert(sd->num_closure >= 0); - j--; - } - } -} -#endif /* __BRANCHED_PATH__ || __VOLUME__ */ - -/* Defensive sampling. */ - -ccl_device_inline void shader_prepare_closures(ShaderData *sd, ccl_addr_space PathState *state) -{ - /* We can likely also do defensive sampling at deeper bounces, particularly - * for cases like a perfect mirror but possibly also others. This will need - * a good heuristic. */ - if (state->bounce + state->transparent_bounce == 0 && sd->num_closure > 1) { - float sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - sum += sc->sample_weight; - } - } - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - sc->sample_weight = max(sc->sample_weight, 0.125f * sum); - } - } - } -} - -/* BSDF */ - -ccl_device_inline void _shader_bsdf_multi_eval(KernelGlobals *kg, - ShaderData *sd, - const float3 omega_in, - float *pdf, - const ShaderClosure *skip_sc, - BsdfEval *result_eval, - float sum_pdf, - float sum_sample_weight) -{ - /* this is the veach one-sample model with balance heuristic, some pdf - * factors drop out when using balance heuristic weighting */ - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (sc != skip_sc && CLOSURE_IS_BSDF(sc->type)) { - float bsdf_pdf = 0.0f; - float3 eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf); - - if (bsdf_pdf != 0.0f) { - bsdf_eval_accum(result_eval, sc->type, eval * sc->weight, 1.0f); - sum_pdf += bsdf_pdf * sc->sample_weight; - } - - sum_sample_weight += sc->sample_weight; - } - } - - *pdf = (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; -} - -#ifdef __BRANCHED_PATH__ -ccl_device_inline void _shader_bsdf_multi_eval_branched(KernelGlobals *kg, - ShaderData *sd, - const float3 omega_in, - BsdfEval *result_eval, - float light_pdf, - bool use_mis) -{ - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF(sc->type)) { - float bsdf_pdf = 0.0f; - float3 eval = bsdf_eval(kg, sd, sc, omega_in, &bsdf_pdf); - if (bsdf_pdf != 0.0f) { - float mis_weight = use_mis ? power_heuristic(light_pdf, bsdf_pdf) : 1.0f; - bsdf_eval_accum(result_eval, sc->type, eval * sc->weight, mis_weight); - } - } - } -} -#endif /* __BRANCHED_PATH__ */ - -#ifndef __KERNEL_CUDA__ -ccl_device -#else -ccl_device_inline -#endif - void - shader_bsdf_eval(KernelGlobals *kg, - ShaderData *sd, - const float3 omega_in, - BsdfEval *eval, - float light_pdf, - bool use_mis) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_EVAL); - - bsdf_eval_init(eval, NBUILTIN_CLOSURES, zero_float3(), kernel_data.film.use_light_pass); - -#ifdef __BRANCHED_PATH__ - if (kernel_data.integrator.branched) - _shader_bsdf_multi_eval_branched(kg, sd, omega_in, eval, light_pdf, use_mis); - else -#endif - { - float pdf; - _shader_bsdf_multi_eval(kg, sd, omega_in, &pdf, NULL, eval, 0.0f, 0.0f); - if (use_mis) { - float weight = power_heuristic(light_pdf, pdf); - bsdf_eval_mis(eval, weight); - } - } -} - -ccl_device_inline const ShaderClosure *shader_bsdf_pick(ShaderData *sd, float *randu) -{ - /* Note the sampling here must match shader_bssrdf_pick, - * since we reuse the same random number. */ - int sampled = 0; - - if (sd->num_closure > 1) { - /* Pick a BSDF or based on sample weights. */ - float sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - sum += sc->sample_weight; - } - } - - float r = (*randu) * sum; - float partial_sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - float next_sum = partial_sum + sc->sample_weight; - - if (r < next_sum) { - sampled = i; - - /* Rescale to reuse for direction sample, to better preserve stratification. */ - *randu = (r - partial_sum) / sc->sample_weight; - break; - } - - partial_sum = next_sum; - } - } - } - - const ShaderClosure *sc = &sd->closure[sampled]; - return CLOSURE_IS_BSDF(sc->type) ? sc : NULL; -} - -ccl_device_inline const ShaderClosure *shader_bssrdf_pick(ShaderData *sd, - ccl_addr_space float3 *throughput, - float *randu) -{ - /* Note the sampling here must match shader_bsdf_pick, - * since we reuse the same random number. */ - int sampled = 0; - - if (sd->num_closure > 1) { - /* Pick a BSDF or BSSRDF or based on sample weights. */ - float sum_bsdf = 0.0f; - float sum_bssrdf = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF(sc->type)) { - sum_bsdf += sc->sample_weight; - } - else if (CLOSURE_IS_BSSRDF(sc->type)) { - sum_bssrdf += sc->sample_weight; - } - } - - float r = (*randu) * (sum_bsdf + sum_bssrdf); - float partial_sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) { - float next_sum = partial_sum + sc->sample_weight; - - if (r < next_sum) { - if (CLOSURE_IS_BSDF(sc->type)) { - *throughput *= (sum_bsdf + sum_bssrdf) / sum_bsdf; - return NULL; - } - else { - *throughput *= (sum_bsdf + sum_bssrdf) / sum_bssrdf; - sampled = i; - - /* Rescale to reuse for direction sample, to better preserve stratification. */ - *randu = (r - partial_sum) / sc->sample_weight; - break; - } - } - - partial_sum = next_sum; - } - } - } - - const ShaderClosure *sc = &sd->closure[sampled]; - return CLOSURE_IS_BSSRDF(sc->type) ? sc : NULL; -} - -ccl_device_inline int shader_bsdf_sample(KernelGlobals *kg, - ShaderData *sd, - float randu, - float randv, - BsdfEval *bsdf_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_SAMPLE); - - const ShaderClosure *sc = shader_bsdf_pick(sd, &randu); - if (sc == NULL) { - *pdf = 0.0f; - return LABEL_NONE; - } - - /* BSSRDF should already have been handled elsewhere. */ - kernel_assert(CLOSURE_IS_BSDF(sc->type)); - - int label; - float3 eval = zero_float3(); - - *pdf = 0.0f; - label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); - - if (*pdf != 0.0f) { - bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight, kernel_data.film.use_light_pass); - - if (sd->num_closure > 1) { - float sweight = sc->sample_weight; - _shader_bsdf_multi_eval(kg, sd, *omega_in, pdf, sc, bsdf_eval, *pdf * sweight, sweight); - } - } - - return label; -} - -ccl_device int shader_bsdf_sample_closure(KernelGlobals *kg, - ShaderData *sd, - const ShaderClosure *sc, - float randu, - float randv, - BsdfEval *bsdf_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_SAMPLE); - - int label; - float3 eval = zero_float3(); - - *pdf = 0.0f; - label = bsdf_sample(kg, sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); - - if (*pdf != 0.0f) - bsdf_eval_init(bsdf_eval, sc->type, eval * sc->weight, kernel_data.film.use_light_pass); - - return label; -} - -ccl_device float shader_bsdf_average_roughness(ShaderData *sd) -{ - float roughness = 0.0f; - float sum_weight = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF(sc->type)) { - /* sqrt once to undo the squaring from multiplying roughness on the - * two axes, and once for the squared roughness convention. */ - float weight = fabsf(average(sc->weight)); - roughness += weight * sqrtf(safe_sqrtf(bsdf_get_roughness_squared(sc))); - sum_weight += weight; - } - } - - return (sum_weight > 0.0f) ? roughness / sum_weight : 0.0f; -} - -ccl_device void shader_bsdf_blur(KernelGlobals *kg, ShaderData *sd, float roughness) -{ - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF(sc->type)) - bsdf_blur(kg, sc, roughness); - } -} - -ccl_device float3 shader_bsdf_transparency(KernelGlobals *kg, const ShaderData *sd) -{ - if (sd->flag & SD_HAS_ONLY_VOLUME) { - return one_float3(); - } - else if (sd->flag & SD_TRANSPARENT) { - return sd->closure_transparent_extinction; - } - else { - return zero_float3(); - } -} - -ccl_device void shader_bsdf_disable_transparency(KernelGlobals *kg, ShaderData *sd) -{ - if (sd->flag & SD_TRANSPARENT) { - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) { - sc->sample_weight = 0.0f; - sc->weight = zero_float3(); - } - } - - sd->flag &= ~SD_TRANSPARENT; - } -} - -ccl_device float3 shader_bsdf_alpha(KernelGlobals *kg, ShaderData *sd) -{ - float3 alpha = one_float3() - shader_bsdf_transparency(kg, sd); - - alpha = max(alpha, zero_float3()); - alpha = min(alpha, one_float3()); - - return alpha; -} - -ccl_device float3 shader_bsdf_diffuse(KernelGlobals *kg, ShaderData *sd) -{ - float3 eval = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type) || CLOSURE_IS_BSSRDF(sc->type) || - CLOSURE_IS_BSDF_BSSRDF(sc->type)) - eval += sc->weight; - } - - return eval; -} - -ccl_device float3 shader_bsdf_glossy(KernelGlobals *kg, ShaderData *sd) -{ - float3 eval = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) - eval += sc->weight; - } - - return eval; -} - -ccl_device float3 shader_bsdf_transmission(KernelGlobals *kg, ShaderData *sd) -{ - float3 eval = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_TRANSMISSION(sc->type)) - eval += sc->weight; - } - - return eval; -} - -ccl_device float3 shader_bsdf_average_normal(KernelGlobals *kg, ShaderData *sd) -{ - float3 N = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_BSDF_OR_BSSRDF(sc->type)) - N += sc->N * fabsf(average(sc->weight)); - } - - return (is_zero(N)) ? sd->N : normalize(N); -} - -ccl_device float3 shader_bsdf_ao(KernelGlobals *kg, ShaderData *sd, float ao_factor, float3 *N_) -{ - float3 eval = zero_float3(); - float3 N = zero_float3(); - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) { - const DiffuseBsdf *bsdf = (const DiffuseBsdf *)sc; - eval += sc->weight * ao_factor; - N += bsdf->N * fabsf(average(sc->weight)); - } - } - - *N_ = (is_zero(N)) ? sd->N : normalize(N); - return eval; -} - -#ifdef __SUBSURFACE__ -ccl_device float3 shader_bssrdf_sum(ShaderData *sd, float3 *N_, float *texture_blur_) -{ - float3 eval = zero_float3(); - float3 N = zero_float3(); - float texture_blur = 0.0f, weight_sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_BSSRDF(sc->type)) { - const Bssrdf *bssrdf = (const Bssrdf *)sc; - float avg_weight = fabsf(average(sc->weight)); - - N += bssrdf->N * avg_weight; - eval += sc->weight; - texture_blur += bssrdf->texture_blur * avg_weight; - weight_sum += avg_weight; - } - } - - if (N_) - *N_ = (is_zero(N)) ? sd->N : normalize(N); - - if (texture_blur_) - *texture_blur_ = safe_divide(texture_blur, weight_sum); - - return eval; -} -#endif /* __SUBSURFACE__ */ - -/* Constant emission optimization */ - -ccl_device bool shader_constant_emission_eval(KernelGlobals *kg, int shader, float3 *eval) -{ - int shader_index = shader & SHADER_MASK; - int shader_flag = kernel_tex_fetch(__shaders, shader_index).flags; - - if (shader_flag & SD_HAS_CONSTANT_EMISSION) { - *eval = make_float3(kernel_tex_fetch(__shaders, shader_index).constant_emission[0], - kernel_tex_fetch(__shaders, shader_index).constant_emission[1], - kernel_tex_fetch(__shaders, shader_index).constant_emission[2]); - - return true; - } - - return false; -} - -/* Background */ - -ccl_device float3 shader_background_eval(ShaderData *sd) -{ - if (sd->flag & SD_EMISSION) { - return sd->closure_emission_background; - } - else { - return zero_float3(); - } -} - -/* Emission */ - -ccl_device float3 shader_emissive_eval(ShaderData *sd) -{ - if (sd->flag & SD_EMISSION) { - return emissive_simple_eval(sd->Ng, sd->I) * sd->closure_emission_background; - } - else { - return zero_float3(); - } -} - -/* Holdout */ - -ccl_device float3 shader_holdout_apply(KernelGlobals *kg, ShaderData *sd) -{ - float3 weight = zero_float3(); - - /* For objects marked as holdout, preserve transparency and remove all other - * closures, replacing them with a holdout weight. */ - if (sd->object_flag & SD_OBJECT_HOLDOUT_MASK) { - if ((sd->flag & SD_TRANSPARENT) && !(sd->flag & SD_HAS_ONLY_VOLUME)) { - weight = one_float3() - sd->closure_transparent_extinction; - - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - if (!CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - sc->type = NBUILTIN_CLOSURES; - } - } - - sd->flag &= ~(SD_CLOSURE_FLAGS - (SD_TRANSPARENT | SD_BSDF)); - } - else { - weight = one_float3(); - } - } - else { - for (int i = 0; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - if (CLOSURE_IS_HOLDOUT(sc->type)) { - weight += sc->weight; - } - } - } - - return weight; -} - -/* Surface Evaluation */ - -ccl_device void shader_eval_surface(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, - int path_flag) -{ - PROFILING_INIT(kg, PROFILING_SHADER_EVAL); - - /* If path is being terminated, we are tracing a shadow ray or evaluating - * emission, then we don't need to store closures. The emission and shadow - * shader data also do not have a closure array to save GPU memory. */ - int max_closures; - if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { - max_closures = 0; - } - else { - max_closures = kernel_data.integrator.max_closures; - } - - sd->num_closure = 0; - sd->num_closure_left = max_closures; - -#ifdef __OSL__ - if (kg->osl) { - if (sd->object == OBJECT_NONE && sd->lamp == LAMP_NONE) { - OSLShader::eval_background(kg, sd, state, path_flag); - } - else { - OSLShader::eval_surface(kg, sd, state, path_flag); - } - } - else -#endif - { -#ifdef __SVM__ - svm_eval_nodes(kg, sd, state, buffer, SHADER_TYPE_SURFACE, path_flag); -#else - if (sd->object == OBJECT_NONE) { - sd->closure_emission_background = make_float3(0.8f, 0.8f, 0.8f); - sd->flag |= SD_EMISSION; - } - else { - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc( - sd, sizeof(DiffuseBsdf), make_float3(0.8f, 0.8f, 0.8f)); - if (bsdf != NULL) { - bsdf->N = sd->N; - sd->flag |= bsdf_diffuse_setup(bsdf); - } - } -#endif - } - - if (sd->flag & SD_BSDF_NEEDS_LCG) { - sd->lcg_state = lcg_state_init_addrspace(state, 0xb4bc3953); - } -} - -/* Volume */ - -#ifdef __VOLUME__ - -ccl_device_inline void _shader_volume_phase_multi_eval(const ShaderData *sd, - const float3 omega_in, - float *pdf, - int skip_phase, - BsdfEval *result_eval, - float sum_pdf, - float sum_sample_weight) -{ - for (int i = 0; i < sd->num_closure; i++) { - if (i == skip_phase) - continue; - - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_PHASE(sc->type)) { - float phase_pdf = 0.0f; - float3 eval = volume_phase_eval(sd, sc, omega_in, &phase_pdf); - - if (phase_pdf != 0.0f) { - bsdf_eval_accum(result_eval, sc->type, eval, 1.0f); - sum_pdf += phase_pdf * sc->sample_weight; - } - - sum_sample_weight += sc->sample_weight; - } - } - - *pdf = (sum_sample_weight > 0.0f) ? sum_pdf / sum_sample_weight : 0.0f; -} - -ccl_device void shader_volume_phase_eval( - KernelGlobals *kg, const ShaderData *sd, const float3 omega_in, BsdfEval *eval, float *pdf) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_VOLUME_EVAL); - - bsdf_eval_init(eval, NBUILTIN_CLOSURES, zero_float3(), kernel_data.film.use_light_pass); - - _shader_volume_phase_multi_eval(sd, omega_in, pdf, -1, eval, 0.0f, 0.0f); -} - -ccl_device int shader_volume_phase_sample(KernelGlobals *kg, - const ShaderData *sd, - float randu, - float randv, - BsdfEval *phase_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_VOLUME_SAMPLE); - - int sampled = 0; - - if (sd->num_closure > 1) { - /* pick a phase closure based on sample weights */ - float sum = 0.0f; - - for (sampled = 0; sampled < sd->num_closure; sampled++) { - const ShaderClosure *sc = &sd->closure[sampled]; - - if (CLOSURE_IS_PHASE(sc->type)) - sum += sc->sample_weight; - } - - float r = randu * sum; - float partial_sum = 0.0f; - - for (sampled = 0; sampled < sd->num_closure; sampled++) { - const ShaderClosure *sc = &sd->closure[sampled]; - - if (CLOSURE_IS_PHASE(sc->type)) { - float next_sum = partial_sum + sc->sample_weight; - - if (r <= next_sum) { - /* Rescale to reuse for BSDF direction sample. */ - randu = (r - partial_sum) / sc->sample_weight; - break; - } - - partial_sum = next_sum; - } - } - - if (sampled == sd->num_closure) { - *pdf = 0.0f; - return LABEL_NONE; - } - } - - /* todo: this isn't quite correct, we don't weight anisotropy properly - * depending on color channels, even if this is perhaps not a common case */ - const ShaderClosure *sc = &sd->closure[sampled]; - int label; - float3 eval = zero_float3(); - - *pdf = 0.0f; - label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); - - if (*pdf != 0.0f) { - bsdf_eval_init(phase_eval, sc->type, eval, kernel_data.film.use_light_pass); - } - - return label; -} - -ccl_device int shader_phase_sample_closure(KernelGlobals *kg, - const ShaderData *sd, - const ShaderClosure *sc, - float randu, - float randv, - BsdfEval *phase_eval, - float3 *omega_in, - differential3 *domega_in, - float *pdf) -{ - PROFILING_INIT(kg, PROFILING_CLOSURE_VOLUME_SAMPLE); - - int label; - float3 eval = zero_float3(); - - *pdf = 0.0f; - label = volume_phase_sample(sd, sc, randu, randv, &eval, omega_in, domega_in, pdf); - - if (*pdf != 0.0f) - bsdf_eval_init(phase_eval, sc->type, eval, kernel_data.film.use_light_pass); - - return label; -} - -/* Volume Evaluation */ - -ccl_device_inline void shader_eval_volume(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_addr_space VolumeStack *stack, - int path_flag) -{ - /* If path is being terminated, we are tracing a shadow ray or evaluating - * emission, then we don't need to store closures. The emission and shadow - * shader data also do not have a closure array to save GPU memory. */ - int max_closures; - if (path_flag & (PATH_RAY_TERMINATE | PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { - max_closures = 0; - } - else { - max_closures = kernel_data.integrator.max_closures; - } - - /* reset closures once at the start, we will be accumulating the closures - * for all volumes in the stack into a single array of closures */ - sd->num_closure = 0; - sd->num_closure_left = max_closures; - sd->flag = 0; - sd->object_flag = 0; - sd->type = PRIMITIVE_VOLUME; - - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { - /* setup shaderdata from stack. it's mostly setup already in - * shader_setup_from_volume, this switching should be quick */ - sd->object = stack[i].object; - sd->lamp = LAMP_NONE; - sd->shader = stack[i].shader; - - sd->flag &= ~SD_SHADER_FLAGS; - sd->flag |= kernel_tex_fetch(__shaders, (sd->shader & SHADER_MASK)).flags; - sd->object_flag &= ~SD_OBJECT_FLAGS; - - if (sd->object != OBJECT_NONE) { - sd->object_flag |= kernel_tex_fetch(__object_flag, sd->object); - -# ifdef __OBJECT_MOTION__ - /* todo: this is inefficient for motion blur, we should be - * caching matrices instead of recomputing them each step */ - shader_setup_object_transforms(kg, sd, sd->time); -# endif - } - - /* evaluate shader */ -# ifdef __SVM__ -# ifdef __OSL__ - if (kg->osl) { - OSLShader::eval_volume(kg, sd, state, path_flag); - } - else -# endif - { - svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_VOLUME, path_flag); - } -# endif - - /* merge closures to avoid exceeding number of closures limit */ - if (i > 0) - shader_merge_closures(sd); - } -} - -#endif /* __VOLUME__ */ - -/* Displacement Evaluation */ - -ccl_device void shader_eval_displacement(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state) -{ - sd->num_closure = 0; - sd->num_closure_left = 0; - - /* this will modify sd->P */ -#ifdef __SVM__ -# ifdef __OSL__ - if (kg->osl) - OSLShader::eval_displacement(kg, sd, state); - else -# endif - { - svm_eval_nodes(kg, sd, state, NULL, SHADER_TYPE_DISPLACEMENT, 0); - } -#endif -} - -/* Transparent Shadows */ - -#ifdef __TRANSPARENT_SHADOWS__ -ccl_device bool shader_transparent_shadow(KernelGlobals *kg, Intersection *isect) -{ - int prim = kernel_tex_fetch(__prim_index, isect->prim); - int shader = 0; - -# ifdef __HAIR__ - if (isect->type & PRIMITIVE_ALL_TRIANGLE) { -# endif - shader = kernel_tex_fetch(__tri_shader, prim); -# ifdef __HAIR__ - } - else { - float4 str = kernel_tex_fetch(__curves, prim); - shader = __float_as_int(str.z); - } -# endif - int flag = kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).flags; - - return (flag & SD_HAS_TRANSPARENT_SHADOW) != 0; -} -#endif /* __TRANSPARENT_SHADOWS__ */ - -ccl_device float shader_cryptomatte_id(KernelGlobals *kg, int shader) -{ - return kernel_tex_fetch(__shaders, (shader & SHADER_MASK)).cryptomatte_id; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_shadow.h b/intern/cycles/kernel/kernel_shadow.h deleted file mode 100644 index 3b124122fba..00000000000 --- a/intern/cycles/kernel/kernel_shadow.h +++ /dev/null @@ -1,466 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __VOLUME__ -/* Get PathState ready for use for volume stack evaluation. */ -# ifdef __SPLIT_KERNEL__ -ccl_addr_space -# endif - ccl_device_inline PathState * - shadow_blocked_volume_path_state(KernelGlobals *kg, - VolumeState *volume_state, - ccl_addr_space PathState *state, - ShaderData *sd, - Ray *ray) -{ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space PathState *ps = - &kernel_split_state.state_shadow[ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0)]; -# else - PathState *ps = &volume_state->ps; -# endif - *ps = *state; - /* We are checking for shadow on the "other" side of the surface, so need - * to discard volume we are currently at. - */ - if (dot(sd->Ng, ray->D) < 0.0f) { - kernel_volume_stack_enter_exit(kg, sd, ps->volume_stack); - } - return ps; -} -#endif /* __VOLUME__ */ - -/* Attenuate throughput accordingly to the given intersection event. - * Returns true if the throughput is zero and traversal can be aborted. - */ -ccl_device_forceinline bool shadow_handle_transparent_isect(KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, -#ifdef __VOLUME__ - ccl_addr_space PathState *volume_state, -#endif - Intersection *isect, - Ray *ray, - float3 *throughput) -{ -#ifdef __VOLUME__ - /* Attenuation between last surface and next surface. */ - if (volume_state->volume_stack[0].shader != SHADER_NONE) { - Ray segment_ray = *ray; - segment_ray.t = isect->t; - kernel_volume_shadow(kg, shadow_sd, volume_state, &segment_ray, throughput); - } -#endif - /* Setup shader data at surface. */ - shader_setup_from_ray(kg, shadow_sd, isect, ray); - /* Attenuation from transparent surface. */ - if (!(shadow_sd->flag & SD_HAS_ONLY_VOLUME)) { - path_state_modify_bounce(state, true); - shader_eval_surface(kg, shadow_sd, state, NULL, PATH_RAY_SHADOW); - path_state_modify_bounce(state, false); - *throughput *= shader_bsdf_transparency(kg, shadow_sd); - } - /* Stop if all light is blocked. */ - if (is_zero(*throughput)) { - return true; - } -#ifdef __VOLUME__ - /* Exit/enter volume. */ - kernel_volume_stack_enter_exit(kg, shadow_sd, volume_state->volume_stack); -#endif - return false; -} - -/* Special version which only handles opaque shadows. */ -ccl_device bool shadow_blocked_opaque(KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *isect, - float3 *shadow) -{ - const bool blocked = scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_OPAQUE, isect); -#ifdef __VOLUME__ - if (!blocked && state->volume_stack[0].shader != SHADER_NONE) { - /* Apply attenuation from current volume shader. */ - kernel_volume_shadow(kg, shadow_sd, state, ray, shadow); - } -#endif - return blocked; -} - -#ifdef __TRANSPARENT_SHADOWS__ -# ifdef __SHADOW_RECORD_ALL__ -/* Shadow function to compute how much light is blocked, - * - * We trace a single ray. If it hits any opaque surface, or more than a given - * number of transparent surfaces is hit, then we consider the geometry to be - * entirely blocked. If not, all transparent surfaces will be recorded and we - * will shade them one by one to determine how much light is blocked. This all - * happens in one scene intersection function. - * - * Recording all hits works well in some cases but may be slower in others. If - * we have many semi-transparent hairs, one intersection may be faster because - * you'd be reinteresecting the same hairs a lot with each step otherwise. If - * however there is mostly binary transparency then we may be recording many - * unnecessary intersections when one of the first surfaces blocks all light. - * - * From tests in real scenes it seems the performance loss is either minimal, - * or there is a performance increase anyway due to avoiding the need to send - * two rays with transparent shadows. - * - * On CPU it'll handle all transparent bounces (by allocating storage for - * intersections when they don't fit into the stack storage). - * - * On GPU it'll only handle SHADOW_STACK_MAX_HITS-1 intersections, so this - * is something to be kept an eye on. - */ - -# define SHADOW_STACK_MAX_HITS 64 - -/* Actual logic with traversal loop implementation which is free from device - * specific tweaks. - * - * Note that hits array should be as big as max_hits+1. - */ -ccl_device bool shadow_blocked_transparent_all_loop(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *hits, - uint max_hits, - float3 *shadow) -{ - /* Intersect to find an opaque surface, or record all transparent - * surface hits. - */ - uint num_hits; - const bool blocked = scene_intersect_shadow_all(kg, ray, hits, visibility, max_hits, &num_hits); -# ifdef __VOLUME__ -# ifdef __KERNEL_OPTIX__ - VolumeState &volume_state = kg->volume_state; -# else - VolumeState volume_state; -# endif -# endif - /* If no opaque surface found but we did find transparent hits, - * shade them. - */ - if (!blocked && num_hits > 0) { - float3 throughput = one_float3(); - float3 Pend = ray->P + ray->D * ray->t; - float last_t = 0.0f; - int bounce = state->transparent_bounce; - Intersection *isect = hits; -# ifdef __VOLUME__ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); -# endif - sort_intersections(hits, num_hits); - for (int hit = 0; hit < num_hits; hit++, isect++) { - /* Adjust intersection distance for moving ray forward. */ - float new_t = isect->t; - isect->t -= last_t; - /* Skip hit if we did not move forward, step by step raytracing - * would have skipped it as well then. - */ - if (last_t == new_t) { - continue; - } - last_t = new_t; - /* Attenuate the throughput. */ - if (shadow_handle_transparent_isect(kg, - shadow_sd, - state, -# ifdef __VOLUME__ - ps, -# endif - isect, - ray, - &throughput)) { - return true; - } - /* Move ray forward. */ - ray->P = shadow_sd->P; - if (ray->t != FLT_MAX) { - ray->D = normalize_len(Pend - ray->P, &ray->t); - } - bounce++; - } -# ifdef __VOLUME__ - /* Attenuation for last line segment towards light. */ - if (ps->volume_stack[0].shader != SHADER_NONE) { - kernel_volume_shadow(kg, shadow_sd, ps, ray, &throughput); - } -# endif - *shadow = throughput; - return is_zero(throughput); - } -# ifdef __VOLUME__ - if (!blocked && state->volume_stack[0].shader != SHADER_NONE) { - /* Apply attenuation from current volume shader. */ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); - kernel_volume_shadow(kg, shadow_sd, ps, ray, shadow); - } -# endif - return blocked; -} - -/* Here we do all device specific trickery before invoking actual traversal - * loop to help readability of the actual logic. - */ -ccl_device bool shadow_blocked_transparent_all(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - uint max_hits, - float3 *shadow) -{ -# ifdef __SPLIT_KERNEL__ - Intersection hits_[SHADOW_STACK_MAX_HITS]; - Intersection *hits = &hits_[0]; -# elif defined(__KERNEL_CUDA__) - Intersection *hits = kg->hits_stack; -# else - Intersection hits_stack[SHADOW_STACK_MAX_HITS]; - Intersection *hits = hits_stack; -# endif -# ifndef __KERNEL_GPU__ - /* Prefer to use stack but use dynamic allocation if too deep max hits - * we need max_hits + 1 storage space due to the logic in - * scene_intersect_shadow_all which will first store and then check if - * the limit is exceeded. - * - * Ignore this on GPU because of slow/unavailable malloc(). - */ - if (max_hits + 1 > SHADOW_STACK_MAX_HITS) { - if (kg->transparent_shadow_intersections == NULL) { - const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; - kg->transparent_shadow_intersections = (Intersection *)malloc(sizeof(Intersection) * - (transparent_max_bounce + 1)); - } - hits = kg->transparent_shadow_intersections; - } -# endif /* __KERNEL_GPU__ */ - /* Invoke actual traversal. */ - return shadow_blocked_transparent_all_loop( - kg, sd, shadow_sd, state, visibility, ray, hits, max_hits, shadow); -} -# endif /* __SHADOW_RECORD_ALL__ */ - -# if defined(__KERNEL_GPU__) || !defined(__SHADOW_RECORD_ALL__) -/* Shadow function to compute how much light is blocked, - * - * Here we raytrace from one transparent surface to the next step by step. - * To minimize overhead in cases where we don't need transparent shadows, we - * first trace a regular shadow ray. We check if the hit primitive was - * potentially transparent, and only in that case start marching. this gives - * one extra ray cast for the cases were we do want transparency. - */ - -/* This function is only implementing device-independent traversal logic - * which requires some precalculation done. - */ -ccl_device bool shadow_blocked_transparent_stepped_loop(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *isect, - const bool blocked, - const bool is_transparent_isect, - float3 *shadow) -{ -# ifdef __VOLUME__ -# ifdef __KERNEL_OPTIX__ - VolumeState &volume_state = kg->volume_state; -# else - VolumeState volume_state; -# endif -# endif - if (blocked && is_transparent_isect) { - float3 throughput = one_float3(); - float3 Pend = ray->P + ray->D * ray->t; - int bounce = state->transparent_bounce; -# ifdef __VOLUME__ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); -# endif - for (;;) { - if (bounce >= kernel_data.integrator.transparent_max_bounce) { - return true; - } - if (!scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_TRANSPARENT, isect)) { - break; - } - if (!shader_transparent_shadow(kg, isect)) { - return true; - } - /* Attenuate the throughput. */ - if (shadow_handle_transparent_isect(kg, - shadow_sd, - state, -# ifdef __VOLUME__ - ps, -# endif - isect, - ray, - &throughput)) { - return true; - } - /* Move ray forward. */ - ray->P = ray_offset(shadow_sd->P, -shadow_sd->Ng); - if (ray->t != FLT_MAX) { - ray->D = normalize_len(Pend - ray->P, &ray->t); - } - bounce++; - } -# ifdef __VOLUME__ - /* Attenuation for last line segment towards light. */ - if (ps->volume_stack[0].shader != SHADER_NONE) { - kernel_volume_shadow(kg, shadow_sd, ps, ray, &throughput); - } -# endif - *shadow *= throughput; - return is_zero(throughput); - } -# ifdef __VOLUME__ - if (!blocked && state->volume_stack[0].shader != SHADER_NONE) { - /* Apply attenuation from current volume shader. */ -# ifdef __SPLIT_KERNEL__ - ccl_addr_space -# endif - PathState *ps = shadow_blocked_volume_path_state(kg, &volume_state, state, sd, ray); - kernel_volume_shadow(kg, shadow_sd, ps, ray, shadow); - } -# endif - return blocked; -} - -ccl_device bool shadow_blocked_transparent_stepped(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - const uint visibility, - Ray *ray, - Intersection *isect, - float3 *shadow) -{ - bool blocked = scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_OPAQUE, isect); - bool is_transparent_isect = blocked ? shader_transparent_shadow(kg, isect) : false; - return shadow_blocked_transparent_stepped_loop( - kg, sd, shadow_sd, state, visibility, ray, isect, blocked, is_transparent_isect, shadow); -} - -# endif /* __KERNEL_GPU__ || !__SHADOW_RECORD_ALL__ */ -#endif /* __TRANSPARENT_SHADOWS__ */ - -ccl_device_inline bool shadow_blocked(KernelGlobals *kg, - ShaderData *sd, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - Ray *ray, - float3 *shadow) -{ - *shadow = one_float3(); -#if !defined(__KERNEL_OPTIX__) - /* Some common early checks. - * Avoid conditional trace call in OptiX though, since those hurt performance there. - */ - if (ray->t == 0.0f) { - return false; - } -#endif -#ifdef __SHADOW_TRICKS__ - const uint visibility = (state->flag & PATH_RAY_SHADOW_CATCHER) ? PATH_RAY_SHADOW_NON_CATCHER : - PATH_RAY_SHADOW; -#else - const uint visibility = PATH_RAY_SHADOW; -#endif - /* Do actual shadow shading. - * First of all, we check if integrator requires transparent shadows. - * if not, we use simplest and fastest ever way to calculate occlusion. - * Do not do this in OptiX to avoid the additional trace call. - */ -#if !defined(__KERNEL_OPTIX__) || !defined(__TRANSPARENT_SHADOWS__) - Intersection isect; -# ifdef __TRANSPARENT_SHADOWS__ - if (!kernel_data.integrator.transparent_shadows) -# endif - { - return shadow_blocked_opaque(kg, shadow_sd, state, visibility, ray, &isect, shadow); - } -#endif -#ifdef __TRANSPARENT_SHADOWS__ -# ifdef __SHADOW_RECORD_ALL__ - /* For the transparent shadows we try to use record-all logic on the - * devices which supports this. - */ - const int transparent_max_bounce = kernel_data.integrator.transparent_max_bounce; - /* Check transparent bounces here, for volume scatter which can do - * lighting before surface path termination is checked. - */ - if (state->transparent_bounce >= transparent_max_bounce) { - return true; - } - uint max_hits = transparent_max_bounce - state->transparent_bounce - 1; -# if defined(__KERNEL_OPTIX__) - /* Always use record-all behavior in OptiX, but ensure there are no out of bounds - * accesses to the hit stack. - */ - max_hits = min(max_hits, SHADOW_STACK_MAX_HITS - 1); -# elif defined(__KERNEL_GPU__) - /* On GPU we do tricky with tracing opaque ray first, this avoids speed - * regressions in some files. - * - * TODO(sergey): Check why using record-all behavior causes slowdown in such - * cases. Could that be caused by a higher spill pressure? - */ - const bool blocked = scene_intersect(kg, ray, visibility & PATH_RAY_SHADOW_OPAQUE, &isect); - const bool is_transparent_isect = blocked ? shader_transparent_shadow(kg, &isect) : false; - if (!blocked || !is_transparent_isect || max_hits + 1 >= SHADOW_STACK_MAX_HITS) { - return shadow_blocked_transparent_stepped_loop( - kg, sd, shadow_sd, state, visibility, ray, &isect, blocked, is_transparent_isect, shadow); - } -# endif /* __KERNEL_GPU__ */ - return shadow_blocked_transparent_all( - kg, sd, shadow_sd, state, visibility, ray, max_hits, shadow); -# else /* __SHADOW_RECORD_ALL__ */ - /* Fallback to a slowest version which works on all devices. */ - return shadow_blocked_transparent_stepped( - kg, sd, shadow_sd, state, visibility, ray, &isect, shadow); -# endif /* __SHADOW_RECORD_ALL__ */ -#endif /* __TRANSPARENT_SHADOWS__ */ -} - -#undef SHADOW_STACK_MAX_HITS - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_subsurface.h b/intern/cycles/kernel/kernel_subsurface.h deleted file mode 100644 index 677504a4045..00000000000 --- a/intern/cycles/kernel/kernel_subsurface.h +++ /dev/null @@ -1,724 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* BSSRDF using disk based importance sampling. - * - * BSSRDF Importance Sampling, SIGGRAPH 2013 - * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf - */ - -ccl_device_inline float3 -subsurface_scatter_eval(ShaderData *sd, const ShaderClosure *sc, float disk_r, float r, bool all) -{ - /* This is the Veach one-sample model with balance heuristic, some pdf - * factors drop out when using balance heuristic weighting. For branched - * path tracing (all) we sample all closure and don't use MIS. */ - float3 eval_sum = zero_float3(); - float pdf_sum = 0.0f; - float sample_weight_inv = 0.0f; - - if (!all) { - float sample_weight_sum = 0.0f; - - for (int i = 0; i < sd->num_closure; i++) { - sc = &sd->closure[i]; - - if (CLOSURE_IS_DISK_BSSRDF(sc->type)) { - sample_weight_sum += sc->sample_weight; - } - } - - sample_weight_inv = 1.0f / sample_weight_sum; - } - - for (int i = 0; i < sd->num_closure; i++) { - sc = &sd->closure[i]; - - if (CLOSURE_IS_DISK_BSSRDF(sc->type)) { - /* in case of branched path integrate we sample all bssrdf's once, - * for path trace we pick one, so adjust pdf for that */ - float sample_weight = (all) ? 1.0f : sc->sample_weight * sample_weight_inv; - - /* compute pdf */ - float3 eval = bssrdf_eval(sc, r); - float pdf = bssrdf_pdf(sc, disk_r); - - eval_sum += sc->weight * eval; - pdf_sum += sample_weight * pdf; - } - } - - return (pdf_sum > 0.0f) ? eval_sum / pdf_sum : zero_float3(); -} - -ccl_device_inline float3 subsurface_scatter_walk_eval(ShaderData *sd, - const ShaderClosure *sc, - float3 throughput, - bool all) -{ - /* This is the Veach one-sample model with balance heuristic, some pdf - * factors drop out when using balance heuristic weighting. For branched - * path tracing (all) we sample all closure and don't use MIS. */ - if (!all) { - float bssrdf_weight = 0.0f; - float weight = sc->sample_weight; - - for (int i = 0; i < sd->num_closure; i++) { - sc = &sd->closure[i]; - - if (CLOSURE_IS_BSSRDF(sc->type)) { - bssrdf_weight += sc->sample_weight; - } - } - throughput *= bssrdf_weight / weight; - } - return throughput; -} - -/* replace closures with a single diffuse bsdf closure after scatter step */ -ccl_device void subsurface_scatter_setup_diffuse_bsdf( - KernelGlobals *kg, ShaderData *sd, ClosureType type, float roughness, float3 weight, float3 N) -{ - sd->flag &= ~SD_CLOSURE_FLAGS; - sd->num_closure = 0; - sd->num_closure_left = kernel_data.integrator.max_closures; - -#ifdef __PRINCIPLED__ - if (type == CLOSURE_BSSRDF_PRINCIPLED_ID || type == CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) { - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( - sd, sizeof(PrincipledDiffuseBsdf), weight); - - if (bsdf) { - bsdf->N = N; - bsdf->roughness = roughness; - sd->flag |= bsdf_principled_diffuse_setup(bsdf); - - /* replace CLOSURE_BSDF_PRINCIPLED_DIFFUSE_ID with this special ID so render passes - * can recognize it as not being a regular Disney principled diffuse closure */ - bsdf->type = CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID; - } - } - else if (CLOSURE_IS_BSDF_BSSRDF(type) || CLOSURE_IS_BSSRDF(type)) -#endif /* __PRINCIPLED__ */ - { - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); - - if (bsdf) { - bsdf->N = N; - sd->flag |= bsdf_diffuse_setup(bsdf); - - /* replace CLOSURE_BSDF_DIFFUSE_ID with this special ID so render passes - * can recognize it as not being a regular diffuse closure */ - bsdf->type = CLOSURE_BSDF_BSSRDF_ID; - } - } -} - -/* optionally do blurring of color and/or bump mapping, at the cost of a shader evaluation */ -ccl_device float3 subsurface_color_pow(float3 color, float exponent) -{ - color = max(color, zero_float3()); - - if (exponent == 1.0f) { - /* nothing to do */ - } - else if (exponent == 0.5f) { - color.x = sqrtf(color.x); - color.y = sqrtf(color.y); - color.z = sqrtf(color.z); - } - else { - color.x = powf(color.x, exponent); - color.y = powf(color.y, exponent); - color.z = powf(color.z, exponent); - } - - return color; -} - -ccl_device void subsurface_color_bump_blur( - KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, float3 *eval, float3 *N) -{ - /* average color and texture blur at outgoing point */ - float texture_blur; - float3 out_color = shader_bssrdf_sum(sd, NULL, &texture_blur); - - /* do we have bump mapping? */ - bool bump = (sd->flag & SD_HAS_BSSRDF_BUMP) != 0; - - if (bump || texture_blur > 0.0f) { - /* average color and normal at incoming point */ - shader_eval_surface(kg, sd, state, NULL, state->flag); - float3 in_color = shader_bssrdf_sum(sd, (bump) ? N : NULL, NULL); - - /* we simply divide out the average color and multiply with the average - * of the other one. we could try to do this per closure but it's quite - * tricky to match closures between shader evaluations, their number and - * order may change, this is simpler */ - if (texture_blur > 0.0f) { - out_color = subsurface_color_pow(out_color, texture_blur); - in_color = subsurface_color_pow(in_color, texture_blur); - - *eval *= safe_divide_color(in_color, out_color); - } - } -} - -/* Subsurface scattering step, from a point on the surface to other - * nearby points on the same object. - */ -ccl_device_inline int subsurface_scatter_disk(KernelGlobals *kg, - LocalIntersection *ss_isect, - ShaderData *sd, - const ShaderClosure *sc, - uint *lcg_state, - float disk_u, - float disk_v, - bool all) -{ - /* pick random axis in local frame and point on disk */ - float3 disk_N, disk_T, disk_B; - float pick_pdf_N, pick_pdf_T, pick_pdf_B; - - disk_N = sd->Ng; - make_orthonormals(disk_N, &disk_T, &disk_B); - - if (disk_v < 0.5f) { - pick_pdf_N = 0.5f; - pick_pdf_T = 0.25f; - pick_pdf_B = 0.25f; - disk_v *= 2.0f; - } - else if (disk_v < 0.75f) { - float3 tmp = disk_N; - disk_N = disk_T; - disk_T = tmp; - pick_pdf_N = 0.25f; - pick_pdf_T = 0.5f; - pick_pdf_B = 0.25f; - disk_v = (disk_v - 0.5f) * 4.0f; - } - else { - float3 tmp = disk_N; - disk_N = disk_B; - disk_B = tmp; - pick_pdf_N = 0.25f; - pick_pdf_T = 0.25f; - pick_pdf_B = 0.5f; - disk_v = (disk_v - 0.75f) * 4.0f; - } - - /* sample point on disk */ - float phi = M_2PI_F * disk_v; - float disk_height, disk_r; - - bssrdf_sample(sc, disk_u, &disk_r, &disk_height); - - float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B; - - /* create ray */ -#ifdef __SPLIT_KERNEL__ - Ray ray_object = ss_isect->ray; - Ray *ray = &ray_object; -#else - Ray *ray = &ss_isect->ray; -#endif - ray->P = sd->P + disk_N * disk_height + disk_P; - ray->D = -disk_N; - ray->t = 2.0f * disk_height; - ray->dP = sd->dP; - ray->dD = differential3_zero(); - ray->time = sd->time; - - /* intersect with the same object. if multiple intersections are found it - * will use at most BSSRDF_MAX_HITS hits, a random subset of all hits */ - scene_intersect_local(kg, ray, ss_isect, sd->object, lcg_state, BSSRDF_MAX_HITS); - int num_eval_hits = min(ss_isect->num_hits, BSSRDF_MAX_HITS); - - for (int hit = 0; hit < num_eval_hits; hit++) { - /* Quickly retrieve P and Ng without setting up ShaderData. */ - float3 hit_P; - if (sd->type & PRIMITIVE_TRIANGLE) { - hit_P = triangle_refine_local(kg, sd, &ss_isect->hits[hit], ray); - } -#ifdef __OBJECT_MOTION__ - else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) { - float3 verts[3]; - motion_triangle_vertices(kg, - sd->object, - kernel_tex_fetch(__prim_index, ss_isect->hits[hit].prim), - sd->time, - verts); - hit_P = motion_triangle_refine_local(kg, sd, &ss_isect->hits[hit], ray, verts); - } -#endif /* __OBJECT_MOTION__ */ - else { - ss_isect->weight[hit] = zero_float3(); - continue; - } - - float3 hit_Ng = ss_isect->Ng[hit]; - if (ss_isect->hits[hit].object != OBJECT_NONE) { - object_normal_transform(kg, sd, &hit_Ng); - } - - /* Probability densities for local frame axes. */ - float pdf_N = pick_pdf_N * fabsf(dot(disk_N, hit_Ng)); - float pdf_T = pick_pdf_T * fabsf(dot(disk_T, hit_Ng)); - float pdf_B = pick_pdf_B * fabsf(dot(disk_B, hit_Ng)); - - /* Multiple importance sample between 3 axes, power heuristic - * found to be slightly better than balance heuristic. pdf_N - * in the MIS weight and denominator cancelled out. */ - float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B)); - if (ss_isect->num_hits > BSSRDF_MAX_HITS) { - w *= ss_isect->num_hits / (float)BSSRDF_MAX_HITS; - } - - /* Real distance to sampled point. */ - float r = len(hit_P - sd->P); - - /* Evaluate profiles. */ - float3 eval = subsurface_scatter_eval(sd, sc, disk_r, r, all) * w; - - ss_isect->weight[hit] = eval; - } - -#ifdef __SPLIT_KERNEL__ - ss_isect->ray = *ray; -#endif - - return num_eval_hits; -} - -#if defined(__KERNEL_OPTIX__) && defined(__SHADER_RAYTRACE__) -ccl_device_inline void subsurface_scatter_multi_setup(KernelGlobals *kg, - LocalIntersection *ss_isect, - int hit, - ShaderData *sd, - ccl_addr_space PathState *state, - ClosureType type, - float roughness) -{ - optixDirectCall(2, kg, ss_isect, hit, sd, state, type, roughness); -} -extern "C" __device__ void __direct_callable__subsurface_scatter_multi_setup( -#else -ccl_device_noinline void subsurface_scatter_multi_setup( -#endif - KernelGlobals *kg, - LocalIntersection *ss_isect, - int hit, - ShaderData *sd, - ccl_addr_space PathState *state, - ClosureType type, - float roughness) -{ -#ifdef __SPLIT_KERNEL__ - Ray ray_object = ss_isect->ray; - Ray *ray = &ray_object; -#else - Ray *ray = &ss_isect->ray; -#endif - - /* Workaround for AMD GPU OpenCL compiler. Most probably cache bypass issue. */ -#if defined(__SPLIT_KERNEL__) && defined(__KERNEL_OPENCL_AMD__) && defined(__KERNEL_GPU__) - kernel_split_params.dummy_sd_flag = sd->flag; -#endif - - /* Setup new shading point. */ - shader_setup_from_subsurface(kg, sd, &ss_isect->hits[hit], ray); - - /* Optionally blur colors and bump mapping. */ - float3 weight = ss_isect->weight[hit]; - float3 N = sd->N; - subsurface_color_bump_blur(kg, sd, state, &weight, &N); - - /* Setup diffuse BSDF. */ - subsurface_scatter_setup_diffuse_bsdf(kg, sd, type, roughness, weight, N); -} - -/* Random walk subsurface scattering. - * - * "Practical and Controllable Subsurface Scattering for Production Path - * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ - -ccl_device void subsurface_random_walk_remap(const float A, - const float d, - float *sigma_t, - float *alpha) -{ - /* Compute attenuation and scattering coefficients from albedo. */ - *alpha = 1.0f - expf(A * (-5.09406f + A * (2.61188f - A * 4.31805f))); - const float s = 1.9f - A + 3.5f * sqr(A - 0.8f); - - *sigma_t = 1.0f / fmaxf(d * s, 1e-16f); -} - -ccl_device void subsurface_random_walk_coefficients(const ShaderClosure *sc, - float3 *sigma_t, - float3 *alpha, - float3 *weight) -{ - const Bssrdf *bssrdf = (const Bssrdf *)sc; - const float3 A = bssrdf->albedo; - const float3 d = bssrdf->radius; - float sigma_t_x, sigma_t_y, sigma_t_z; - float alpha_x, alpha_y, alpha_z; - - subsurface_random_walk_remap(A.x, d.x, &sigma_t_x, &alpha_x); - subsurface_random_walk_remap(A.y, d.y, &sigma_t_y, &alpha_y); - subsurface_random_walk_remap(A.z, d.z, &sigma_t_z, &alpha_z); - - *sigma_t = make_float3(sigma_t_x, sigma_t_y, sigma_t_z); - *alpha = make_float3(alpha_x, alpha_y, alpha_z); - - /* Closure mixing and Fresnel weights separate from albedo. */ - *weight = safe_divide_color(bssrdf->weight, A); -} - -/* References for Dwivedi sampling: - * - * [1] "A Zero-variance-based Sampling Scheme for Monte Carlo Subsurface Scattering" - * by Jaroslav Křivánek and Eugene d'Eon (SIGGRAPH 2014) - * https://cgg.mff.cuni.cz/~jaroslav/papers/2014-zerovar/ - * - * [2] "Improving the Dwivedi Sampling Scheme" - * by Johannes Meng, Johannes Hanika, and Carsten Dachsbacher (EGSR 2016) - * https://cg.ivd.kit.edu/1951.php - * - * [3] "Zero-Variance Theory for Efficient Subsurface Scattering" - * by Eugene d'Eon and Jaroslav Křivánek (SIGGRAPH 2020) - * https://iliyan.com/publications/RenderingCourse2020 - */ - -ccl_device_forceinline float eval_phase_dwivedi(float v, float phase_log, float cos_theta) -{ - /* Eq. 9 from [2] using precomputed log((v + 1) / (v - 1)) */ - return 1.0f / ((v - cos_theta) * phase_log); -} - -ccl_device_forceinline float sample_phase_dwivedi(float v, float phase_log, float rand) -{ - /* Based on Eq. 10 from [2]: `v - (v + 1) * pow((v - 1) / (v + 1), rand)` - * Since we're already pre-computing `phase_log = log((v + 1) / (v - 1))` for the evaluation, - * we can implement the power function like this. */ - return v - (v + 1) * expf(-rand * phase_log); -} - -ccl_device_forceinline float diffusion_length_dwivedi(float alpha) -{ - /* Eq. 67 from [3] */ - return 1.0f / sqrtf(1.0f - powf(alpha, 2.44294f - 0.0215813f * alpha + 0.578637f / alpha)); -} - -ccl_device_forceinline float3 direction_from_cosine(float3 D, float cos_theta, float randv) -{ - float sin_theta = safe_sqrtf(1.0f - cos_theta * cos_theta); - float phi = M_2PI_F * randv; - float3 dir = make_float3(sin_theta * cosf(phi), sin_theta * sinf(phi), cos_theta); - - float3 T, B; - make_orthonormals(D, &T, &B); - return dir.x * T + dir.y * B + dir.z * D; -} - -ccl_device_forceinline float3 subsurface_random_walk_pdf(float3 sigma_t, - float t, - bool hit, - float3 *transmittance) -{ - float3 T = volume_color_transmittance(sigma_t, t); - if (transmittance) { - *transmittance = T; - } - return hit ? T : sigma_t * T; -} - -#ifdef __KERNEL_OPTIX__ -ccl_device_inline /* inline trace calls */ -#else -ccl_device_noinline -#endif - bool - subsurface_random_walk(KernelGlobals *kg, - LocalIntersection *ss_isect, - ShaderData *sd, - ccl_addr_space PathState *state, - const ShaderClosure *sc, - const float bssrdf_u, - const float bssrdf_v, - bool all) -{ - /* Sample diffuse surface scatter into the object. */ - float3 D; - float pdf; - sample_cos_hemisphere(-sd->N, bssrdf_u, bssrdf_v, &D, &pdf); - if (dot(-sd->Ng, D) <= 0.0f) { - return 0; - } - - /* Convert subsurface to volume coefficients. - * The single-scattering albedo is named alpha to avoid confusion with the surface albedo. */ - float3 sigma_t, alpha; - float3 throughput = one_float3(); - subsurface_random_walk_coefficients(sc, &sigma_t, &alpha, &throughput); - float3 sigma_s = sigma_t * alpha; - - /* Theoretically it should be better to use the exact alpha for the channel we're sampling at - * each bounce, but in practice there doesn't seem to be a noticeable difference in exchange - * for making the code significantly more complex and slower (if direction sampling depends on - * the sampled channel, we need to compute its PDF per-channel and consider it for MIS later on). - * - * Since the strength of the guided sampling increases as alpha gets lower, using a value that - * is too low results in fireflies while one that's too high just gives a bit more noise. - * Therefore, the code here uses the highest of the three albedos to be safe. */ - float diffusion_length = diffusion_length_dwivedi(max3(alpha)); - /* Precompute term for phase sampling. */ - float phase_log = logf((diffusion_length + 1) / (diffusion_length - 1)); - - /* Setup ray. */ -#ifdef __SPLIT_KERNEL__ - Ray ray_object = ss_isect->ray; - Ray *ray = &ray_object; -#else - Ray *ray = &ss_isect->ray; -#endif - ray->P = ray_offset(sd->P, -sd->Ng); - ray->D = D; - ray->t = FLT_MAX; - ray->time = sd->time; - - /* Modify state for RNGs, decorrelated from other paths. */ - uint prev_rng_offset = state->rng_offset; - uint prev_rng_hash = state->rng_hash; - state->rng_hash = cmj_hash(state->rng_hash + state->rng_offset, 0xdeadbeef); - - /* Random walk until we hit the surface again. */ - bool hit = false; - bool have_opposite_interface = false; - float opposite_distance = 0.0f; - - /* Todo: Disable for alpha>0.999 or so? */ - const float guided_fraction = 0.75f; - - for (int bounce = 0; bounce < BSSRDF_MAX_BOUNCES; bounce++) { - /* Advance random number offset. */ - state->rng_offset += PRNG_BOUNCE_NUM; - - /* Sample color channel, use MIS with balance heuristic. */ - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - float3 channel_pdf; - int channel = kernel_volume_sample_channel(alpha, throughput, rphase, &channel_pdf); - float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); - float randt = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - - /* We need the result of the raycast to compute the full guided PDF, so just remember the - * relevant terms to avoid recomputing them later. */ - float backward_fraction = 0.0f; - float forward_pdf_factor = 0.0f; - float forward_stretching = 1.0f; - float backward_pdf_factor = 0.0f; - float backward_stretching = 1.0f; - - /* For the initial ray, we already know the direction, so just do classic distance sampling. */ - if (bounce > 0) { - /* Decide whether we should use guided or classic sampling. */ - bool guided = (path_state_rng_1D(kg, state, PRNG_LIGHT_TERMINATE) < guided_fraction); - - /* Determine if we want to sample away from the incoming interface. - * This only happens if we found a nearby opposite interface, and the probability for it - * depends on how close we are to it already. - * This probability term comes from the recorded presentation of [3]. */ - bool guide_backward = false; - if (have_opposite_interface) { - /* Compute distance of the random walk between the tangent plane at the starting point - * and the assumed opposite interface (the parallel plane that contains the point we - * found in our ray query for the opposite side). */ - float x = clamp(dot(ray->P - sd->P, -sd->N), 0.0f, opposite_distance); - backward_fraction = 1.0f / (1.0f + expf((opposite_distance - 2 * x) / diffusion_length)); - guide_backward = path_state_rng_1D(kg, state, PRNG_TERMINATE) < backward_fraction; - } - - /* Sample scattering direction. */ - float scatter_u, scatter_v; - path_state_rng_2D(kg, state, PRNG_BSDF_U, &scatter_u, &scatter_v); - float cos_theta; - if (guided) { - cos_theta = sample_phase_dwivedi(diffusion_length, phase_log, scatter_u); - /* The backwards guiding distribution is just mirrored along sd->N, so swapping the - * sign here is enough to sample from that instead. */ - if (guide_backward) { - cos_theta = -cos_theta; - } - } - else { - cos_theta = 2.0f * scatter_u - 1.0f; - } - ray->D = direction_from_cosine(sd->N, cos_theta, scatter_v); - - /* Compute PDF factor caused by phase sampling (as the ratio of guided / classic). - * Since phase sampling is channel-independent, we can get away with applying a factor - * to the guided PDF, which implicitly means pulling out the classic PDF term and letting - * it cancel with an equivalent term in the numerator of the full estimator. - * For the backward PDF, we again reuse the same probability distribution with a sign swap. - */ - forward_pdf_factor = 2.0f * eval_phase_dwivedi(diffusion_length, phase_log, cos_theta); - backward_pdf_factor = 2.0f * eval_phase_dwivedi(diffusion_length, phase_log, -cos_theta); - - /* Prepare distance sampling. - * For the backwards case, this also needs the sign swapped since now directions against - * sd->N (and therefore with negative cos_theta) are preferred. */ - forward_stretching = (1.0f - cos_theta / diffusion_length); - backward_stretching = (1.0f + cos_theta / diffusion_length); - if (guided) { - sample_sigma_t *= guide_backward ? backward_stretching : forward_stretching; - } - } - - /* Sample direction along ray. */ - float t = -logf(1.0f - randt) / sample_sigma_t; - - /* On the first bounce, we use the raycast to check if the opposite side is nearby. - * If yes, we will later use backwards guided sampling in order to have a decent - * chance of connecting to it. - * Todo: Maybe use less than 10 times the mean free path? */ - ray->t = (bounce == 0) ? max(t, 10.0f / (min3(sigma_t))) : t; - scene_intersect_local(kg, ray, ss_isect, sd->object, NULL, 1); - hit = (ss_isect->num_hits > 0); - - if (hit) { -#ifdef __KERNEL_OPTIX__ - /* t is always in world space with OptiX. */ - ray->t = ss_isect->hits[0].t; -#else - /* Compute world space distance to surface hit. */ - float3 D = ray->D; - object_inverse_dir_transform(kg, sd, &D); - D = normalize(D) * ss_isect->hits[0].t; - object_dir_transform(kg, sd, &D); - ray->t = len(D); -#endif - } - - if (bounce == 0) { - /* Check if we hit the opposite side. */ - if (hit) { - have_opposite_interface = true; - opposite_distance = dot(ray->P + ray->t * ray->D - sd->P, -sd->N); - } - /* Apart from the opposite side check, we were supposed to only trace up to distance t, - * so check if there would have been a hit in that case. */ - hit = ray->t < t; - } - - /* Use the distance to the exit point for the throughput update if we found one. */ - if (hit) { - t = ray->t; - } - else if (bounce == 0) { - /* Restore original position if nothing was hit after the first bounce, - * without the ray_offset() that was added to avoid self-intersection. - * Otherwise if that offset is relatively large compared to the scattering - * radius, we never go back up high enough to exit the surface. */ - ray->P = sd->P; - } - - /* Advance to new scatter location. */ - ray->P += t * ray->D; - - float3 transmittance; - float3 pdf = subsurface_random_walk_pdf(sigma_t, t, hit, &transmittance); - if (bounce > 0) { - /* Compute PDF just like we do for classic sampling, but with the stretched sigma_t. */ - float3 guided_pdf = subsurface_random_walk_pdf(forward_stretching * sigma_t, t, hit, NULL); - - if (have_opposite_interface) { - /* First step of MIS: Depending on geometry we might have two methods for guided - * sampling, so perform MIS between them. */ - float3 back_pdf = subsurface_random_walk_pdf(backward_stretching * sigma_t, t, hit, NULL); - guided_pdf = mix( - guided_pdf * forward_pdf_factor, back_pdf * backward_pdf_factor, backward_fraction); - } - else { - /* Just include phase sampling factor otherwise. */ - guided_pdf *= forward_pdf_factor; - } - - /* Now we apply the MIS balance heuristic between the classic and guided sampling. */ - pdf = mix(pdf, guided_pdf, guided_fraction); - } - - /* Finally, we're applying MIS again to combine the three color channels. - * Altogether, the MIS computation combines up to nine different estimators: - * {classic, guided, backward_guided} x {r, g, b} */ - throughput *= (hit ? transmittance : sigma_s * transmittance) / dot(channel_pdf, pdf); - - if (hit) { - /* If we hit the surface, we are done. */ - break; - } - else if (throughput.x < VOLUME_THROUGHPUT_EPSILON && - throughput.y < VOLUME_THROUGHPUT_EPSILON && - throughput.z < VOLUME_THROUGHPUT_EPSILON) { - /* Avoid unnecessary work and precision issue when throughput gets really small. */ - break; - } - } - - kernel_assert(isfinite_safe(throughput.x) && isfinite_safe(throughput.y) && - isfinite_safe(throughput.z)); - - state->rng_offset = prev_rng_offset; - state->rng_hash = prev_rng_hash; - - /* Return number of hits in ss_isect. */ - if (!hit) { - return 0; - } - - /* TODO: gain back performance lost from merging with disk BSSRDF. We - * only need to return on hit so this indirect ray push/pop overhead - * is not actually needed, but it does keep the code simpler. */ - ss_isect->weight[0] = subsurface_scatter_walk_eval(sd, sc, throughput, all); -#ifdef __SPLIT_KERNEL__ - ss_isect->ray = *ray; -#endif - - return 1; -} - -ccl_device_inline int subsurface_scatter_multi_intersect(KernelGlobals *kg, - LocalIntersection *ss_isect, - ShaderData *sd, - ccl_addr_space PathState *state, - const ShaderClosure *sc, - uint *lcg_state, - float bssrdf_u, - float bssrdf_v, - bool all) -{ - if (CLOSURE_IS_DISK_BSSRDF(sc->type)) { - return subsurface_scatter_disk(kg, ss_isect, sd, sc, lcg_state, bssrdf_u, bssrdf_v, all); - } - else { - return subsurface_random_walk(kg, ss_isect, sd, state, sc, bssrdf_u, bssrdf_v, all); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_volume.h b/intern/cycles/kernel/kernel_volume.h deleted file mode 100644 index f6b34be040e..00000000000 --- a/intern/cycles/kernel/kernel_volume.h +++ /dev/null @@ -1,1440 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Ignore paths that have volume throughput below this value, to avoid unnecessary work - * and precision issues. - * todo: this value could be tweaked or turned into a probability to avoid unnecessary - * work in volumes and subsurface scattering. */ -#define VOLUME_THROUGHPUT_EPSILON 1e-6f - -/* Events for probalistic scattering */ - -typedef enum VolumeIntegrateResult { - VOLUME_PATH_SCATTERED = 0, - VOLUME_PATH_ATTENUATED = 1, - VOLUME_PATH_MISSED = 2 -} VolumeIntegrateResult; - -/* Volume shader properties - * - * extinction coefficient = absorption coefficient + scattering coefficient - * sigma_t = sigma_a + sigma_s */ - -typedef struct VolumeShaderCoefficients { - float3 sigma_t; - float3 sigma_s; - float3 emission; -} VolumeShaderCoefficients; - -#ifdef __VOLUME__ - -/* evaluate shader to get extinction coefficient at P */ -ccl_device_inline bool volume_shader_extinction_sample(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - float3 P, - float3 *extinction) -{ - sd->P = P; - shader_eval_volume(kg, sd, state, state->volume_stack, PATH_RAY_SHADOW); - - if (sd->flag & SD_EXTINCTION) { - const float density = object_volume_density(kg, sd->object); - *extinction = sd->closure_transparent_extinction * density; - return true; - } - else { - return false; - } -} - -/* evaluate shader to get absorption, scattering and emission at P */ -ccl_device_inline bool volume_shader_sample(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - float3 P, - VolumeShaderCoefficients *coeff) -{ - sd->P = P; - shader_eval_volume(kg, sd, state, state->volume_stack, state->flag); - - if (!(sd->flag & (SD_EXTINCTION | SD_SCATTER | SD_EMISSION))) - return false; - - coeff->sigma_s = zero_float3(); - coeff->sigma_t = (sd->flag & SD_EXTINCTION) ? sd->closure_transparent_extinction : zero_float3(); - coeff->emission = (sd->flag & SD_EMISSION) ? sd->closure_emission_background : zero_float3(); - - if (sd->flag & SD_SCATTER) { - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (CLOSURE_IS_VOLUME(sc->type)) - coeff->sigma_s += sc->weight; - } - } - - const float density = object_volume_density(kg, sd->object); - coeff->sigma_s *= density; - coeff->sigma_t *= density; - coeff->emission *= density; - - return true; -} - -#endif /* __VOLUME__ */ - -ccl_device float3 volume_color_transmittance(float3 sigma, float t) -{ - return exp3(-sigma * t); -} - -ccl_device float kernel_volume_channel_get(float3 value, int channel) -{ - return (channel == 0) ? value.x : ((channel == 1) ? value.y : value.z); -} - -#ifdef __VOLUME__ - -ccl_device float volume_stack_step_size(KernelGlobals *kg, ccl_addr_space VolumeStack *stack) -{ - float step_size = FLT_MAX; - - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { - int shader_flag = kernel_tex_fetch(__shaders, (stack[i].shader & SHADER_MASK)).flags; - - bool heterogeneous = false; - - if (shader_flag & SD_HETEROGENEOUS_VOLUME) { - heterogeneous = true; - } - else if (shader_flag & SD_NEED_VOLUME_ATTRIBUTES) { - /* We want to render world or objects without any volume grids - * as homogeneous, but can only verify this at run-time since other - * heterogeneous volume objects may be using the same shader. */ - int object = stack[i].object; - if (object != OBJECT_NONE) { - int object_flag = kernel_tex_fetch(__object_flag, object); - if (object_flag & SD_OBJECT_HAS_VOLUME_ATTRIBUTES) { - heterogeneous = true; - } - } - } - - if (heterogeneous) { - float object_step_size = object_volume_step_size(kg, stack[i].object); - object_step_size *= kernel_data.integrator.volume_step_rate; - step_size = fminf(object_step_size, step_size); - } - } - - return step_size; -} - -ccl_device int volume_stack_sampling_method(KernelGlobals *kg, VolumeStack *stack) -{ - if (kernel_data.integrator.num_all_lights == 0) - return 0; - - int method = -1; - - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { - int shader_flag = kernel_tex_fetch(__shaders, (stack[i].shader & SHADER_MASK)).flags; - - if (shader_flag & SD_VOLUME_MIS) { - return SD_VOLUME_MIS; - } - else if (shader_flag & SD_VOLUME_EQUIANGULAR) { - if (method == 0) - return SD_VOLUME_MIS; - - method = SD_VOLUME_EQUIANGULAR; - } - else { - if (method == SD_VOLUME_EQUIANGULAR) - return SD_VOLUME_MIS; - - method = 0; - } - } - - return method; -} - -ccl_device_inline void kernel_volume_step_init(KernelGlobals *kg, - ccl_addr_space PathState *state, - const float object_step_size, - float t, - float *step_size, - float *step_shade_offset, - float *steps_offset) -{ - const int max_steps = kernel_data.integrator.volume_max_steps; - float step = min(object_step_size, t); - - /* compute exact steps in advance for malloc */ - if (t > max_steps * step) { - step = t / (float)max_steps; - } - - *step_size = step; - - /* Perform shading at this offset within a step, to integrate over - * over the entire step segment. */ - *step_shade_offset = path_state_rng_1D_hash(kg, state, 0x1e31d8a4); - - /* Shift starting point of all segment by this random amount to avoid - * banding artifacts from the volume bounding shape. */ - *steps_offset = path_state_rng_1D_hash(kg, state, 0x3d22c7b3); -} - -/* Volume Shadows - * - * These functions are used to attenuate shadow rays to lights. Both absorption - * and scattering will block light, represented by the extinction coefficient. */ - -/* homogeneous volume: assume shader evaluation at the starts gives - * the extinction coefficient for the entire line segment */ -ccl_device void kernel_volume_shadow_homogeneous(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - float3 *throughput) -{ - float3 sigma_t = zero_float3(); - - if (volume_shader_extinction_sample(kg, sd, state, ray->P, &sigma_t)) - *throughput *= volume_color_transmittance(sigma_t, ray->t); -} - -/* heterogeneous volume: integrate stepping through the volume until we - * reach the end, get absorbed entirely, or run out of iterations */ -ccl_device void kernel_volume_shadow_heterogeneous(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - float3 *throughput, - const float object_step_size) -{ - float3 tp = *throughput; - - /* Prepare for stepping. - * For shadows we do not offset all segments, since the starting point is - * already a random distance inside the volume. It also appears to create - * banding artifacts for unknown reasons. */ - int max_steps = kernel_data.integrator.volume_max_steps; - float step_size, step_shade_offset, unused; - kernel_volume_step_init( - kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &unused); - const float steps_offset = 1.0f; - - /* compute extinction at the start */ - float t = 0.0f; - - float3 sum = zero_float3(); - - for (int i = 0; i < max_steps; i++) { - /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); - float dt = new_t - t; - - float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - float3 sigma_t = zero_float3(); - - /* compute attenuation over segment */ - if (volume_shader_extinction_sample(kg, sd, state, new_P, &sigma_t)) { - /* Compute expf() only for every Nth step, to save some calculations - * because exp(a)*exp(b) = exp(a+b), also do a quick VOLUME_THROUGHPUT_EPSILON - * check then. */ - sum += (-sigma_t * dt); - if ((i & 0x07) == 0) { /* ToDo: Other interval? */ - tp = *throughput * exp3(sum); - - /* stop if nearly all light is blocked */ - if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON && - tp.z < VOLUME_THROUGHPUT_EPSILON) - break; - } - } - - /* stop if at the end of the volume */ - t = new_t; - if (t == ray->t) { - /* Update throughput in case we haven't done it above */ - tp = *throughput * exp3(sum); - break; - } - } - - *throughput = tp; -} - -/* get the volume attenuation over line segment defined by ray, with the - * assumption that there are no surfaces blocking light between the endpoints */ -# if defined(__KERNEL_OPTIX__) && defined(__SHADER_RAYTRACE__) -ccl_device_inline void kernel_volume_shadow(KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - Ray *ray, - float3 *throughput) -{ - optixDirectCall(1, kg, shadow_sd, state, ray, throughput); -} -extern "C" __device__ void __direct_callable__kernel_volume_shadow( -# else -ccl_device_noinline void kernel_volume_shadow( -# endif - KernelGlobals *kg, - ShaderData *shadow_sd, - ccl_addr_space PathState *state, - Ray *ray, - float3 *throughput) -{ - shader_setup_from_volume(kg, shadow_sd, ray); - - float step_size = volume_stack_step_size(kg, state->volume_stack); - if (step_size != FLT_MAX) - kernel_volume_shadow_heterogeneous(kg, state, ray, shadow_sd, throughput, step_size); - else - kernel_volume_shadow_homogeneous(kg, state, ray, shadow_sd, throughput); -} - -#endif /* __VOLUME__ */ - -/* Equi-angular sampling as in: - * "Importance Sampling Techniques for Path Tracing in Participating Media" */ - -ccl_device float kernel_volume_equiangular_sample(Ray *ray, float3 light_P, float xi, float *pdf) -{ - float t = ray->t; - - float delta = dot((light_P - ray->P), ray->D); - float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); - if (UNLIKELY(D == 0.0f)) { - *pdf = 0.0f; - return 0.0f; - } - float theta_a = -atan2f(delta, D); - float theta_b = atan2f(t - delta, D); - float t_ = D * tanf((xi * theta_b) + (1 - xi) * theta_a); - if (UNLIKELY(theta_b == theta_a)) { - *pdf = 0.0f; - return 0.0f; - } - *pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); - - return min(t, delta + t_); /* min is only for float precision errors */ -} - -ccl_device float kernel_volume_equiangular_pdf(Ray *ray, float3 light_P, float sample_t) -{ - float delta = dot((light_P - ray->P), ray->D); - float D = safe_sqrtf(len_squared(light_P - ray->P) - delta * delta); - if (UNLIKELY(D == 0.0f)) { - return 0.0f; - } - - float t = ray->t; - float t_ = sample_t - delta; - - float theta_a = -atan2f(delta, D); - float theta_b = atan2f(t - delta, D); - if (UNLIKELY(theta_b == theta_a)) { - return 0.0f; - } - - float pdf = D / ((theta_b - theta_a) * (D * D + t_ * t_)); - - return pdf; -} - -/* Distance sampling */ - -ccl_device float kernel_volume_distance_sample( - float max_t, float3 sigma_t, int channel, float xi, float3 *transmittance, float3 *pdf) -{ - /* xi is [0, 1[ so log(0) should never happen, division by zero is - * avoided because sample_sigma_t > 0 when SD_SCATTER is set */ - float sample_sigma_t = kernel_volume_channel_get(sigma_t, channel); - float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); - float sample_transmittance = kernel_volume_channel_get(full_transmittance, channel); - - float sample_t = min(max_t, -logf(1.0f - xi * (1.0f - sample_transmittance)) / sample_sigma_t); - - *transmittance = volume_color_transmittance(sigma_t, sample_t); - *pdf = safe_divide_color(sigma_t * *transmittance, one_float3() - full_transmittance); - - /* todo: optimization: when taken together with hit/miss decision, - * the full_transmittance cancels out drops out and xi does not - * need to be remapped */ - - return sample_t; -} - -ccl_device float3 kernel_volume_distance_pdf(float max_t, float3 sigma_t, float sample_t) -{ - float3 full_transmittance = volume_color_transmittance(sigma_t, max_t); - float3 transmittance = volume_color_transmittance(sigma_t, sample_t); - - return safe_divide_color(sigma_t * transmittance, one_float3() - full_transmittance); -} - -/* Emission */ - -ccl_device float3 kernel_volume_emission_integrate(VolumeShaderCoefficients *coeff, - int closure_flag, - float3 transmittance, - float t) -{ - /* integral E * exp(-sigma_t * t) from 0 to t = E * (1 - exp(-sigma_t * t))/sigma_t - * this goes to E * t as sigma_t goes to zero - * - * todo: we should use an epsilon to avoid precision issues near zero sigma_t */ - float3 emission = coeff->emission; - - if (closure_flag & SD_EXTINCTION) { - float3 sigma_t = coeff->sigma_t; - - emission.x *= (sigma_t.x > 0.0f) ? (1.0f - transmittance.x) / sigma_t.x : t; - emission.y *= (sigma_t.y > 0.0f) ? (1.0f - transmittance.y) / sigma_t.y : t; - emission.z *= (sigma_t.z > 0.0f) ? (1.0f - transmittance.z) / sigma_t.z : t; - } - else - emission *= t; - - return emission; -} - -/* Volume Path */ - -ccl_device int kernel_volume_sample_channel(float3 albedo, - float3 throughput, - float rand, - float3 *pdf) -{ - /* Sample color channel proportional to throughput and single scattering - * albedo, to significantly reduce noise with many bounce, following: - * - * "Practical and Controllable Subsurface Scattering for Production Path - * Tracing". Matt Jen-Yuan Chiang, Peter Kutz, Brent Burley. SIGGRAPH 2016. */ - float3 weights = fabs(throughput * albedo); - float sum_weights = weights.x + weights.y + weights.z; - float3 weights_pdf; - - if (sum_weights > 0.0f) { - weights_pdf = weights / sum_weights; - } - else { - weights_pdf = make_float3(1.0f / 3.0f, 1.0f / 3.0f, 1.0f / 3.0f); - } - - *pdf = weights_pdf; - - /* OpenCL does not support -> on float3, so don't use pdf->x. */ - if (rand < weights_pdf.x) { - return 0; - } - else if (rand < weights_pdf.x + weights_pdf.y) { - return 1; - } - else { - return 2; - } -} - -#ifdef __VOLUME__ - -/* homogeneous volume: assume shader evaluation at the start gives - * the volume shading coefficient for the entire line segment */ -ccl_device VolumeIntegrateResult -kernel_volume_integrate_homogeneous(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - PathRadiance *L, - ccl_addr_space float3 *throughput, - bool probalistic_scatter) -{ - VolumeShaderCoefficients coeff ccl_optional_struct_init; - - if (!volume_shader_sample(kg, sd, state, ray->P, &coeff)) - return VOLUME_PATH_MISSED; - - int closure_flag = sd->flag; - float t = ray->t; - float3 new_tp; - -# ifdef __VOLUME_SCATTER__ - /* randomly scatter, and if we do t is shortened */ - if (closure_flag & SD_SCATTER) { - /* Sample channel, use MIS with balance heuristic. */ - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); - float3 channel_pdf; - int channel = kernel_volume_sample_channel(albedo, *throughput, rphase, &channel_pdf); - - /* decide if we will hit or miss */ - bool scatter = true; - float xi = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - - if (probalistic_scatter) { - float sample_sigma_t = kernel_volume_channel_get(coeff.sigma_t, channel); - float sample_transmittance = expf(-sample_sigma_t * t); - - if (1.0f - xi >= sample_transmittance) { - scatter = true; - - /* rescale random number so we can reuse it */ - xi = 1.0f - (1.0f - xi - sample_transmittance) / (1.0f - sample_transmittance); - } - else - scatter = false; - } - - if (scatter) { - /* scattering */ - float3 pdf; - float3 transmittance; - float sample_t; - - /* distance sampling */ - sample_t = kernel_volume_distance_sample( - ray->t, coeff.sigma_t, channel, xi, &transmittance, &pdf); - - /* modify pdf for hit/miss decision */ - if (probalistic_scatter) - pdf *= one_float3() - volume_color_transmittance(coeff.sigma_t, t); - - new_tp = *throughput * coeff.sigma_s * transmittance / dot(channel_pdf, pdf); - t = sample_t; - } - else { - /* no scattering */ - float3 transmittance = volume_color_transmittance(coeff.sigma_t, t); - float pdf = dot(channel_pdf, transmittance); - new_tp = *throughput * transmittance / pdf; - } - } - else -# endif - if (closure_flag & SD_EXTINCTION) { - /* absorption only, no sampling needed */ - float3 transmittance = volume_color_transmittance(coeff.sigma_t, t); - new_tp = *throughput * transmittance; - } - else { - new_tp = *throughput; - } - - /* integrate emission attenuated by extinction */ - if (L && (closure_flag & SD_EMISSION)) { - float3 transmittance = volume_color_transmittance(coeff.sigma_t, ray->t); - float3 emission = kernel_volume_emission_integrate( - &coeff, closure_flag, transmittance, ray->t); - path_radiance_accum_emission(kg, L, state, *throughput, emission); - } - - /* modify throughput */ - if (closure_flag & SD_EXTINCTION) { - *throughput = new_tp; - - /* prepare to scatter to new direction */ - if (t < ray->t) { - /* adjust throughput and move to new location */ - sd->P = ray->P + t * ray->D; - - return VOLUME_PATH_SCATTERED; - } - } - - return VOLUME_PATH_ATTENUATED; -} - -/* heterogeneous volume distance sampling: integrate stepping through the - * volume until we reach the end, get absorbed entirely, or run out of - * iterations. this does probabilistically scatter or get transmitted through - * for path tracing where we don't want to branch. */ -ccl_device VolumeIntegrateResult -kernel_volume_integrate_heterogeneous_distance(KernelGlobals *kg, - ccl_addr_space PathState *state, - Ray *ray, - ShaderData *sd, - PathRadiance *L, - ccl_addr_space float3 *throughput, - const float object_step_size) -{ - float3 tp = *throughput; - - /* Prepare for stepping. - * Using a different step offset for the first step avoids banding artifacts. */ - int max_steps = kernel_data.integrator.volume_max_steps; - float step_size, step_shade_offset, steps_offset; - kernel_volume_step_init( - kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &steps_offset); - - /* compute coefficients at the start */ - float t = 0.0f; - float3 accum_transmittance = one_float3(); - - /* pick random color channel, we use the Veach one-sample - * model with balance heuristic for the channels */ - float xi = path_state_rng_1D(kg, state, PRNG_SCATTER_DISTANCE); - float rphase = path_state_rng_1D(kg, state, PRNG_PHASE_CHANNEL); - bool has_scatter = false; - - for (int i = 0; i < max_steps; i++) { - /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); - float dt = new_t - t; - - float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - VolumeShaderCoefficients coeff ccl_optional_struct_init; - - /* compute segment */ - if (volume_shader_sample(kg, sd, state, new_P, &coeff)) { - int closure_flag = sd->flag; - float3 new_tp; - float3 transmittance; - bool scatter = false; - - /* distance sampling */ -# ifdef __VOLUME_SCATTER__ - if ((closure_flag & SD_SCATTER) || (has_scatter && (closure_flag & SD_EXTINCTION))) { - has_scatter = true; - - /* Sample channel, use MIS with balance heuristic. */ - float3 albedo = safe_divide_color(coeff.sigma_s, coeff.sigma_t); - float3 channel_pdf; - int channel = kernel_volume_sample_channel(albedo, tp, rphase, &channel_pdf); - - /* compute transmittance over full step */ - transmittance = volume_color_transmittance(coeff.sigma_t, dt); - - /* decide if we will scatter or continue */ - float sample_transmittance = kernel_volume_channel_get(transmittance, channel); - - if (1.0f - xi >= sample_transmittance) { - /* compute sampling distance */ - float sample_sigma_t = kernel_volume_channel_get(coeff.sigma_t, channel); - float new_dt = -logf(1.0f - xi) / sample_sigma_t; - new_t = t + new_dt; - - /* transmittance and pdf */ - float3 new_transmittance = volume_color_transmittance(coeff.sigma_t, new_dt); - float3 pdf = coeff.sigma_t * new_transmittance; - - /* throughput */ - new_tp = tp * coeff.sigma_s * new_transmittance / dot(channel_pdf, pdf); - scatter = true; - } - else { - /* throughput */ - float pdf = dot(channel_pdf, transmittance); - new_tp = tp * transmittance / pdf; - - /* remap xi so we can reuse it and keep thing stratified */ - xi = 1.0f - (1.0f - xi) / sample_transmittance; - } - } - else -# endif - if (closure_flag & SD_EXTINCTION) { - /* absorption only, no sampling needed */ - transmittance = volume_color_transmittance(coeff.sigma_t, dt); - new_tp = tp * transmittance; - } - else { - transmittance = zero_float3(); - new_tp = tp; - } - - /* integrate emission attenuated by absorption */ - if (L && (closure_flag & SD_EMISSION)) { - float3 emission = kernel_volume_emission_integrate( - &coeff, closure_flag, transmittance, dt); - path_radiance_accum_emission(kg, L, state, tp, emission); - } - - /* modify throughput */ - if (closure_flag & SD_EXTINCTION) { - tp = new_tp; - - /* stop if nearly all light blocked */ - if (tp.x < VOLUME_THROUGHPUT_EPSILON && tp.y < VOLUME_THROUGHPUT_EPSILON && - tp.z < VOLUME_THROUGHPUT_EPSILON) { - tp = zero_float3(); - break; - } - } - - /* prepare to scatter to new direction */ - if (scatter) { - /* adjust throughput and move to new location */ - sd->P = ray->P + new_t * ray->D; - *throughput = tp; - - return VOLUME_PATH_SCATTERED; - } - else { - /* accumulate transmittance */ - accum_transmittance *= transmittance; - } - } - - /* stop if at the end of the volume */ - t = new_t; - if (t == ray->t) - break; - } - - *throughput = tp; - - return VOLUME_PATH_ATTENUATED; -} - -/* get the volume attenuation and emission over line segment defined by - * ray, with the assumption that there are no surfaces blocking light - * between the endpoints. distance sampling is used to decide if we will - * scatter or not. */ -ccl_device_noinline_cpu VolumeIntegrateResult -kernel_volume_integrate(KernelGlobals *kg, - ccl_addr_space PathState *state, - ShaderData *sd, - Ray *ray, - PathRadiance *L, - ccl_addr_space float3 *throughput, - float step_size) -{ - shader_setup_from_volume(kg, sd, ray); - - if (step_size != FLT_MAX) - return kernel_volume_integrate_heterogeneous_distance( - kg, state, ray, sd, L, throughput, step_size); - else - return kernel_volume_integrate_homogeneous(kg, state, ray, sd, L, throughput, true); -} - -# ifndef __SPLIT_KERNEL__ -/* Decoupled Volume Sampling - * - * VolumeSegment is list of coefficients and transmittance stored at all steps - * through a volume. This can then later be used for decoupled sampling as in: - * "Importance Sampling Techniques for Path Tracing in Participating Media" - * - * On the GPU this is only supported (but currently not enabled) - * for homogeneous volumes (1 step), due to - * no support for malloc/free and too much stack usage with a fix size array. */ - -typedef struct VolumeStep { - float3 sigma_s; /* scatter coefficient */ - float3 sigma_t; /* extinction coefficient */ - float3 accum_transmittance; /* accumulated transmittance including this step */ - float3 cdf_distance; /* cumulative density function for distance sampling */ - float t; /* distance at end of this step */ - float shade_t; /* jittered distance where shading was done in step */ - int closure_flag; /* shader evaluation closure flags */ -} VolumeStep; - -typedef struct VolumeSegment { - VolumeStep stack_step; /* stack storage for homogeneous step, to avoid malloc */ - VolumeStep *steps; /* recorded steps */ - int numsteps; /* number of steps */ - int closure_flag; /* accumulated closure flags from all steps */ - - float3 accum_emission; /* accumulated emission at end of segment */ - float3 accum_transmittance; /* accumulated transmittance at end of segment */ - float3 accum_albedo; /* accumulated average albedo over segment */ - - int sampling_method; /* volume sampling method */ -} VolumeSegment; - -/* record volume steps to the end of the volume. - * - * it would be nice if we could only record up to the point that we need to scatter, - * but the entire segment is needed to do always scattering, rather than probabilistically - * hitting or missing the volume. if we don't know the transmittance at the end of the - * volume we can't generate stratified distance samples up to that transmittance */ -# ifdef __VOLUME_DECOUPLED__ -ccl_device void kernel_volume_decoupled_record(KernelGlobals *kg, - PathState *state, - Ray *ray, - ShaderData *sd, - VolumeSegment *segment, - const float object_step_size) -{ - /* prepare for volume stepping */ - int max_steps; - float step_size, step_shade_offset, steps_offset; - - if (object_step_size != FLT_MAX) { - max_steps = kernel_data.integrator.volume_max_steps; - kernel_volume_step_init( - kg, state, object_step_size, ray->t, &step_size, &step_shade_offset, &steps_offset); - -# ifdef __KERNEL_CPU__ - /* NOTE: For the branched path tracing it's possible to have direct - * and indirect light integration both having volume segments allocated. - * We detect this using index in the pre-allocated memory. Currently we - * only support two segments allocated at a time, if more needed some - * modifications to the KernelGlobals will be needed. - * - * This gives us restrictions that decoupled record should only happen - * in the stack manner, meaning if there's subsequent call of decoupled - * record it'll need to free memory before its caller frees memory. - */ - const int index = kg->decoupled_volume_steps_index; - assert(index < sizeof(kg->decoupled_volume_steps) / sizeof(*kg->decoupled_volume_steps)); - if (kg->decoupled_volume_steps[index] == NULL) { - kg->decoupled_volume_steps[index] = (VolumeStep *)malloc(sizeof(VolumeStep) * max_steps); - } - segment->steps = kg->decoupled_volume_steps[index]; - ++kg->decoupled_volume_steps_index; -# else - segment->steps = (VolumeStep *)malloc(sizeof(VolumeStep) * max_steps); -# endif - } - else { - max_steps = 1; - step_size = ray->t; - step_shade_offset = 0.0f; - steps_offset = 1.0f; - segment->steps = &segment->stack_step; - } - - /* init accumulation variables */ - float3 accum_emission = zero_float3(); - float3 accum_transmittance = one_float3(); - float3 accum_albedo = zero_float3(); - float3 cdf_distance = zero_float3(); - float t = 0.0f; - - segment->numsteps = 0; - segment->closure_flag = 0; - bool is_last_step_empty = false; - - VolumeStep *step = segment->steps; - - for (int i = 0; i < max_steps; i++, step++) { - /* advance to new position */ - float new_t = min(ray->t, (i + steps_offset) * step_size); - float dt = new_t - t; - - float3 new_P = ray->P + ray->D * (t + dt * step_shade_offset); - VolumeShaderCoefficients coeff ccl_optional_struct_init; - - /* compute segment */ - if (volume_shader_sample(kg, sd, state, new_P, &coeff)) { - int closure_flag = sd->flag; - float3 sigma_t = coeff.sigma_t; - - /* compute average albedo for channel sampling */ - if (closure_flag & SD_SCATTER) { - accum_albedo += (dt / ray->t) * safe_divide_color(coeff.sigma_s, sigma_t); - } - - /* compute accumulated transmittance */ - float3 transmittance = volume_color_transmittance(sigma_t, dt); - - /* compute emission attenuated by absorption */ - if (closure_flag & SD_EMISSION) { - float3 emission = kernel_volume_emission_integrate( - &coeff, closure_flag, transmittance, dt); - accum_emission += accum_transmittance * emission; - } - - accum_transmittance *= transmittance; - - /* compute pdf for distance sampling */ - float3 pdf_distance = dt * accum_transmittance * coeff.sigma_s; - cdf_distance = cdf_distance + pdf_distance; - - /* write step data */ - step->sigma_t = sigma_t; - step->sigma_s = coeff.sigma_s; - step->closure_flag = closure_flag; - - segment->closure_flag |= closure_flag; - - is_last_step_empty = false; - segment->numsteps++; - } - else { - if (is_last_step_empty) { - /* consecutive empty step, merge */ - step--; - } - else { - /* store empty step */ - step->sigma_t = zero_float3(); - step->sigma_s = zero_float3(); - step->closure_flag = 0; - - segment->numsteps++; - is_last_step_empty = true; - } - } - - step->accum_transmittance = accum_transmittance; - step->cdf_distance = cdf_distance; - step->t = new_t; - step->shade_t = t + dt * step_shade_offset; - - /* stop if at the end of the volume */ - t = new_t; - if (t == ray->t) - break; - - /* stop if nearly all light blocked */ - if (accum_transmittance.x < VOLUME_THROUGHPUT_EPSILON && - accum_transmittance.y < VOLUME_THROUGHPUT_EPSILON && - accum_transmittance.z < VOLUME_THROUGHPUT_EPSILON) - break; - } - - /* store total emission and transmittance */ - segment->accum_emission = accum_emission; - segment->accum_transmittance = accum_transmittance; - segment->accum_albedo = accum_albedo; - - /* normalize cumulative density function for distance sampling */ - VolumeStep *last_step = segment->steps + segment->numsteps - 1; - - if (!is_zero(last_step->cdf_distance)) { - VolumeStep *step = &segment->steps[0]; - int numsteps = segment->numsteps; - float3 inv_cdf_distance_sum = safe_invert_color(last_step->cdf_distance); - - for (int i = 0; i < numsteps; i++, step++) - step->cdf_distance *= inv_cdf_distance_sum; - } -} - -ccl_device void kernel_volume_decoupled_free(KernelGlobals *kg, VolumeSegment *segment) -{ - if (segment->steps != &segment->stack_step) { -# ifdef __KERNEL_CPU__ - /* NOTE: We only allow free last allocated segment. - * No random order of alloc/free is supported. - */ - assert(kg->decoupled_volume_steps_index > 0); - assert(segment->steps == kg->decoupled_volume_steps[kg->decoupled_volume_steps_index - 1]); - --kg->decoupled_volume_steps_index; -# else - free(segment->steps); -# endif - } -} -# endif /* __VOLUME_DECOUPLED__ */ - -/* scattering for homogeneous and heterogeneous volumes, using decoupled ray - * marching. - * - * function is expected to return VOLUME_PATH_SCATTERED when probalistic_scatter is false */ -ccl_device VolumeIntegrateResult kernel_volume_decoupled_scatter(KernelGlobals *kg, - PathState *state, - Ray *ray, - ShaderData *sd, - float3 *throughput, - float rphase, - float rscatter, - const VolumeSegment *segment, - const float3 *light_P, - bool probalistic_scatter) -{ - kernel_assert(segment->closure_flag & SD_SCATTER); - - /* Sample color channel, use MIS with balance heuristic. */ - float3 channel_pdf; - int channel = kernel_volume_sample_channel( - segment->accum_albedo, *throughput, rphase, &channel_pdf); - - float xi = rscatter; - - /* probabilistic scattering decision based on transmittance */ - if (probalistic_scatter) { - float sample_transmittance = kernel_volume_channel_get(segment->accum_transmittance, channel); - - if (1.0f - xi >= sample_transmittance) { - /* rescale random number so we can reuse it */ - xi = 1.0f - (1.0f - xi - sample_transmittance) / (1.0f - sample_transmittance); - } - else { - *throughput /= sample_transmittance; - return VOLUME_PATH_MISSED; - } - } - - VolumeStep *step; - float3 transmittance; - float pdf, sample_t; - float mis_weight = 1.0f; - bool distance_sample = true; - bool use_mis = false; - - if (segment->sampling_method && light_P) { - if (segment->sampling_method == SD_VOLUME_MIS) { - /* multiple importance sample: randomly pick between - * equiangular and distance sampling strategy */ - if (xi < 0.5f) { - xi *= 2.0f; - } - else { - xi = (xi - 0.5f) * 2.0f; - distance_sample = false; - } - - use_mis = true; - } - else { - /* only equiangular sampling */ - distance_sample = false; - } - } - - /* distance sampling */ - if (distance_sample) { - /* find step in cdf */ - step = segment->steps; - - float prev_t = 0.0f; - float3 step_pdf_distance = one_float3(); - - if (segment->numsteps > 1) { - float prev_cdf = 0.0f; - float step_cdf = 1.0f; - float3 prev_cdf_distance = zero_float3(); - - for (int i = 0;; i++, step++) { - /* todo: optimize using binary search */ - step_cdf = kernel_volume_channel_get(step->cdf_distance, channel); - - if (xi < step_cdf || i == segment->numsteps - 1) - break; - - prev_cdf = step_cdf; - prev_t = step->t; - prev_cdf_distance = step->cdf_distance; - } - - /* remap xi so we can reuse it */ - xi = (xi - prev_cdf) / (step_cdf - prev_cdf); - - /* pdf for picking step */ - step_pdf_distance = step->cdf_distance - prev_cdf_distance; - } - - /* determine range in which we will sample */ - float step_t = step->t - prev_t; - - /* sample distance and compute transmittance */ - float3 distance_pdf; - sample_t = prev_t + kernel_volume_distance_sample( - step_t, step->sigma_t, channel, xi, &transmittance, &distance_pdf); - - /* modify pdf for hit/miss decision */ - if (probalistic_scatter) - distance_pdf *= one_float3() - segment->accum_transmittance; - - pdf = dot(channel_pdf, distance_pdf * step_pdf_distance); - - /* multiple importance sampling */ - if (use_mis) { - float equi_pdf = kernel_volume_equiangular_pdf(ray, *light_P, sample_t); - mis_weight = 2.0f * power_heuristic(pdf, equi_pdf); - } - } - /* equi-angular sampling */ - else { - /* sample distance */ - sample_t = kernel_volume_equiangular_sample(ray, *light_P, xi, &pdf); - - /* find step in which sampled distance is located */ - step = segment->steps; - - float prev_t = 0.0f; - float3 step_pdf_distance = one_float3(); - - if (segment->numsteps > 1) { - float3 prev_cdf_distance = zero_float3(); - - int numsteps = segment->numsteps; - int high = numsteps - 1; - int low = 0; - int mid; - - while (low < high) { - mid = (low + high) >> 1; - - if (sample_t < step[mid].t) - high = mid; - else if (sample_t >= step[mid + 1].t) - low = mid + 1; - else { - /* found our interval in step[mid] .. step[mid+1] */ - prev_t = step[mid].t; - prev_cdf_distance = step[mid].cdf_distance; - step += mid + 1; - break; - } - } - - if (low >= numsteps - 1) { - prev_t = step[numsteps - 1].t; - prev_cdf_distance = step[numsteps - 1].cdf_distance; - step += numsteps - 1; - } - - /* pdf for picking step with distance sampling */ - step_pdf_distance = step->cdf_distance - prev_cdf_distance; - } - - /* determine range in which we will sample */ - float step_t = step->t - prev_t; - float step_sample_t = sample_t - prev_t; - - /* compute transmittance */ - transmittance = volume_color_transmittance(step->sigma_t, step_sample_t); - - /* multiple importance sampling */ - if (use_mis) { - float3 distance_pdf3 = kernel_volume_distance_pdf(step_t, step->sigma_t, step_sample_t); - float distance_pdf = dot(channel_pdf, distance_pdf3 * step_pdf_distance); - mis_weight = 2.0f * power_heuristic(pdf, distance_pdf); - } - } - if (sample_t < 0.0f || pdf == 0.0f) { - return VOLUME_PATH_MISSED; - } - - /* compute transmittance up to this step */ - if (step != segment->steps) - transmittance *= (step - 1)->accum_transmittance; - - /* modify throughput */ - *throughput *= step->sigma_s * transmittance * (mis_weight / pdf); - - /* evaluate shader to create closures at shading point */ - if (segment->numsteps > 1) { - sd->P = ray->P + step->shade_t * ray->D; - - VolumeShaderCoefficients coeff; - volume_shader_sample(kg, sd, state, sd->P, &coeff); - } - - /* move to new position */ - sd->P = ray->P + sample_t * ray->D; - - return VOLUME_PATH_SCATTERED; -} -# endif /* __SPLIT_KERNEL */ - -/* decide if we need to use decoupled or not */ -ccl_device bool kernel_volume_use_decoupled(KernelGlobals *kg, - bool heterogeneous, - bool direct, - int sampling_method) -{ - /* decoupled ray marching for heterogeneous volumes not supported on the GPU, - * which also means equiangular and multiple importance sampling is not - * support for that case */ - if (!kernel_data.integrator.volume_decoupled) - return false; - -# ifdef __KERNEL_GPU__ - if (heterogeneous) - return false; -# endif - - /* equiangular and multiple importance sampling only implemented for decoupled */ - if (sampling_method != 0) - return true; - - /* for all light sampling use decoupled, reusing shader evaluations is - * typically faster in that case */ - if (direct) - return kernel_data.integrator.sample_all_lights_direct; - else - return kernel_data.integrator.sample_all_lights_indirect; -} - -/* Volume Stack - * - * This is an array of object/shared ID's that the current segment of the path - * is inside of. */ - -ccl_device void kernel_volume_stack_init(KernelGlobals *kg, - ShaderData *stack_sd, - ccl_addr_space const PathState *state, - ccl_addr_space const Ray *ray, - ccl_addr_space VolumeStack *stack) -{ - /* NULL ray happens in the baker, does it need proper initialization of - * camera in volume? - */ - if (!kernel_data.cam.is_inside_volume || ray == NULL) { - /* Camera is guaranteed to be in the air, only take background volume - * into account in this case. - */ - if (kernel_data.background.volume_shader != SHADER_NONE) { - stack[0].shader = kernel_data.background.volume_shader; - stack[0].object = PRIM_NONE; - stack[1].shader = SHADER_NONE; - } - else { - stack[0].shader = SHADER_NONE; - } - return; - } - - kernel_assert(state->flag & PATH_RAY_CAMERA); - - Ray volume_ray = *ray; - volume_ray.t = FLT_MAX; - - const uint visibility = (state->flag & PATH_RAY_ALL_VISIBILITY); - int stack_index = 0, enclosed_index = 0; - -# ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * VOLUME_STACK_SIZE + 1]; - uint num_hits = scene_intersect_volume_all( - kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, visibility); - if (num_hits > 0) { - int enclosed_volumes[VOLUME_STACK_SIZE]; - Intersection *isect = hits; - - qsort(hits, num_hits, sizeof(Intersection), intersections_compare); - - for (uint hit = 0; hit < num_hits; ++hit, ++isect) { - shader_setup_from_ray(kg, stack_sd, isect, &volume_ray); - if (stack_sd->flag & SD_BACKFACING) { - bool need_add = true; - for (int i = 0; i < enclosed_index && need_add; ++i) { - /* If ray exited the volume and never entered to that volume - * it means that camera is inside such a volume. - */ - if (enclosed_volumes[i] == stack_sd->object) { - need_add = false; - } - } - for (int i = 0; i < stack_index && need_add; ++i) { - /* Don't add intersections twice. */ - if (stack[i].object == stack_sd->object) { - need_add = false; - break; - } - } - if (need_add && stack_index < VOLUME_STACK_SIZE - 1) { - stack[stack_index].object = stack_sd->object; - stack[stack_index].shader = stack_sd->shader; - ++stack_index; - } - } - else { - /* If ray from camera enters the volume, this volume shouldn't - * be added to the stack on exit. - */ - enclosed_volumes[enclosed_index++] = stack_sd->object; - } - } - } -# else - int enclosed_volumes[VOLUME_STACK_SIZE]; - int step = 0; - - while (stack_index < VOLUME_STACK_SIZE - 1 && enclosed_index < VOLUME_STACK_SIZE - 1 && - step < 2 * VOLUME_STACK_SIZE) { - Intersection isect; - if (!scene_intersect_volume(kg, &volume_ray, &isect, visibility)) { - break; - } - - shader_setup_from_ray(kg, stack_sd, &isect, &volume_ray); - if (stack_sd->flag & SD_BACKFACING) { - /* If ray exited the volume and never entered to that volume - * it means that camera is inside such a volume. - */ - bool need_add = true; - for (int i = 0; i < enclosed_index && need_add; ++i) { - /* If ray exited the volume and never entered to that volume - * it means that camera is inside such a volume. - */ - if (enclosed_volumes[i] == stack_sd->object) { - need_add = false; - } - } - for (int i = 0; i < stack_index && need_add; ++i) { - /* Don't add intersections twice. */ - if (stack[i].object == stack_sd->object) { - need_add = false; - break; - } - } - if (need_add) { - stack[stack_index].object = stack_sd->object; - stack[stack_index].shader = stack_sd->shader; - ++stack_index; - } - } - else { - /* If ray from camera enters the volume, this volume shouldn't - * be added to the stack on exit. - */ - enclosed_volumes[enclosed_index++] = stack_sd->object; - } - - /* Move ray forward. */ - volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); - ++step; - } -# endif - /* stack_index of 0 means quick checks outside of the kernel gave false - * positive, nothing to worry about, just we've wasted quite a few of - * ticks just to come into conclusion that camera is in the air. - * - * In this case we're doing the same above -- check whether background has - * volume. - */ - if (stack_index == 0 && kernel_data.background.volume_shader == SHADER_NONE) { - stack[0].shader = kernel_data.background.volume_shader; - stack[0].object = OBJECT_NONE; - stack[1].shader = SHADER_NONE; - } - else { - stack[stack_index].shader = SHADER_NONE; - } -} - -ccl_device void kernel_volume_stack_enter_exit(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space VolumeStack *stack) -{ - /* todo: we should have some way for objects to indicate if they want the - * world shader to work inside them. excluding it by default is problematic - * because non-volume objects can't be assumed to be closed manifolds */ - - if (!(sd->flag & SD_HAS_VOLUME)) - return; - - if (sd->flag & SD_BACKFACING) { - /* exit volume object: remove from stack */ - for (int i = 0; stack[i].shader != SHADER_NONE; i++) { - if (stack[i].object == sd->object) { - /* shift back next stack entries */ - do { - stack[i] = stack[i + 1]; - i++; - } while (stack[i].shader != SHADER_NONE); - - return; - } - } - } - else { - /* enter volume object: add to stack */ - int i; - - for (i = 0; stack[i].shader != SHADER_NONE; i++) { - /* already in the stack? then we have nothing to do */ - if (stack[i].object == sd->object) - return; - } - - /* if we exceed the stack limit, ignore */ - if (i >= VOLUME_STACK_SIZE - 1) - return; - - /* add to the end of the stack */ - stack[i].shader = sd->shader; - stack[i].object = sd->object; - stack[i + 1].shader = SHADER_NONE; - } -} - -# ifdef __SUBSURFACE__ -ccl_device void kernel_volume_stack_update_for_subsurface(KernelGlobals *kg, - ShaderData *stack_sd, - Ray *ray, - ccl_addr_space VolumeStack *stack) -{ - kernel_assert(kernel_data.integrator.use_volumes); - - Ray volume_ray = *ray; - -# ifdef __VOLUME_RECORD_ALL__ - Intersection hits[2 * VOLUME_STACK_SIZE + 1]; - uint num_hits = scene_intersect_volume_all( - kg, &volume_ray, hits, 2 * VOLUME_STACK_SIZE, PATH_RAY_ALL_VISIBILITY); - if (num_hits > 0) { - Intersection *isect = hits; - - qsort(hits, num_hits, sizeof(Intersection), intersections_compare); - - for (uint hit = 0; hit < num_hits; ++hit, ++isect) { - shader_setup_from_ray(kg, stack_sd, isect, &volume_ray); - kernel_volume_stack_enter_exit(kg, stack_sd, stack); - } - } -# else - Intersection isect; - int step = 0; - float3 Pend = ray->P + ray->D * ray->t; - while (step < 2 * VOLUME_STACK_SIZE && - scene_intersect_volume(kg, &volume_ray, &isect, PATH_RAY_ALL_VISIBILITY)) { - shader_setup_from_ray(kg, stack_sd, &isect, &volume_ray); - kernel_volume_stack_enter_exit(kg, stack_sd, stack); - - /* Move ray forward. */ - volume_ray.P = ray_offset(stack_sd->P, -stack_sd->Ng); - if (volume_ray.t != FLT_MAX) { - volume_ray.D = normalize_len(Pend - volume_ray.P, &volume_ray.t); - } - ++step; - } -# endif -} -# endif - -/* Clean stack after the last bounce. - * - * It is expected that all volumes are closed manifolds, so at the time when ray - * hits nothing (for example, it is a last bounce which goes to environment) the - * only expected volume in the stack is the world's one. All the rest volume - * entries should have been exited already. - * - * This isn't always true because of ray intersection precision issues, which - * could lead us to an infinite non-world volume in the stack, causing render - * artifacts. - * - * Use this function after the last bounce to get rid of all volumes apart from - * the world's one after the last bounce to avoid render artifacts. - */ -ccl_device_inline void kernel_volume_clean_stack(KernelGlobals *kg, - ccl_addr_space VolumeStack *volume_stack) -{ - if (kernel_data.background.volume_shader != SHADER_NONE) { - /* Keep the world's volume in stack. */ - volume_stack[1].shader = SHADER_NONE; - } - else { - volume_stack[0].shader = SHADER_NONE; - } -} - -#endif /* __VOLUME__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_work_stealing.h b/intern/cycles/kernel/kernel_work_stealing.h deleted file mode 100644 index d1602744f1d..00000000000 --- a/intern/cycles/kernel/kernel_work_stealing.h +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_WORK_STEALING_H__ -#define __KERNEL_WORK_STEALING_H__ - -CCL_NAMESPACE_BEGIN - -/* - * Utility functions for work stealing - */ - -/* Map global work index to tile, pixel X/Y and sample. */ -ccl_device_inline void get_work_pixel(ccl_global const WorkTile *tile, - uint global_work_index, - ccl_private uint *x, - ccl_private uint *y, - ccl_private uint *sample) -{ -#ifdef __KERNEL_CUDA__ - /* Keeping threads for the same pixel together improves performance on CUDA. */ - uint sample_offset = global_work_index % tile->num_samples; - uint pixel_offset = global_work_index / tile->num_samples; -#else /* __KERNEL_CUDA__ */ - uint tile_pixels = tile->w * tile->h; - uint sample_offset = global_work_index / tile_pixels; - uint pixel_offset = global_work_index - sample_offset * tile_pixels; -#endif /* __KERNEL_CUDA__ */ - uint y_offset = pixel_offset / tile->w; - uint x_offset = pixel_offset - y_offset * tile->w; - - *x = tile->x + x_offset; - *y = tile->y + y_offset; - *sample = tile->start_sample + sample_offset; -} - -#ifdef __KERNEL_OPENCL__ -# pragma OPENCL EXTENSION cl_khr_global_int32_base_atomics : enable -#endif - -#ifdef __SPLIT_KERNEL__ -/* Returns true if there is work */ -ccl_device bool get_next_work_item(KernelGlobals *kg, - ccl_global uint *work_pools, - uint total_work_size, - uint ray_index, - ccl_private uint *global_work_index) -{ - /* With a small amount of work there may be more threads than work due to - * rounding up of global size, stop such threads immediately. */ - if (ray_index >= total_work_size) { - return false; - } - - /* Increase atomic work index counter in pool. */ - uint pool = ray_index / WORK_POOL_SIZE; - uint work_index = atomic_fetch_and_inc_uint32(&work_pools[pool]); - - /* Map per-pool work index to a global work index. */ - uint global_size = ccl_global_size(0) * ccl_global_size(1); - kernel_assert(global_size % WORK_POOL_SIZE == 0); - kernel_assert(ray_index < global_size); - - *global_work_index = (work_index / WORK_POOL_SIZE) * global_size + (pool * WORK_POOL_SIZE) + - (work_index % WORK_POOL_SIZE); - - /* Test if all work for this pool is done. */ - return (*global_work_index < total_work_size); -} - -ccl_device bool get_next_work(KernelGlobals *kg, - ccl_global uint *work_pools, - uint total_work_size, - uint ray_index, - ccl_private uint *global_work_index) -{ - bool got_work = false; - if (kernel_data.film.pass_adaptive_aux_buffer) { - do { - got_work = get_next_work_item(kg, work_pools, total_work_size, ray_index, global_work_index); - if (got_work) { - ccl_global WorkTile *tile = &kernel_split_params.tile; - uint x, y, sample; - get_work_pixel(tile, *global_work_index, &x, &y, &sample); - uint buffer_offset = (tile->offset + x + y * tile->stride) * kernel_data.film.pass_stride; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - ccl_global float4 *aux = (ccl_global float4 *)(buffer + - kernel_data.film.pass_adaptive_aux_buffer); - if ((*aux).w == 0.0f) { - break; - } - } - } while (got_work); - } - else { - got_work = get_next_work_item(kg, work_pools, total_work_size, ray_index, global_work_index); - } - return got_work; -} -#endif - -CCL_NAMESPACE_END - -#endif /* __KERNEL_WORK_STEALING_H__ */ diff --git a/intern/cycles/kernel/kernels/cpu/filter.cpp b/intern/cycles/kernel/kernels/cpu/filter.cpp deleted file mode 100644 index 145a6b6ac40..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CPU kernel entry points */ - -/* On x86-64, we can assume SSE2, so avoid the extra kernel and compile this - * one with SSE2 intrinsics. - */ -#if defined(__x86_64__) || defined(_M_X64) -# define __KERNEL_SSE2__ -#endif - -/* When building kernel for native machine detect kernel features from the flags - * set by compiler. - */ -#ifdef WITH_KERNEL_NATIVE -# ifdef __SSE2__ -# ifndef __KERNEL_SSE2__ -# define __KERNEL_SSE2__ -# endif -# endif -# ifdef __SSE3__ -# define __KERNEL_SSE3__ -# endif -# ifdef __SSSE3__ -# define __KERNEL_SSSE3__ -# endif -# ifdef __SSE4_1__ -# define __KERNEL_SSE41__ -# endif -# ifdef __AVX__ -# define __KERNEL_SSE__ -# define __KERNEL_AVX__ -# endif -# ifdef __AVX2__ -# define __KERNEL_SSE__ -# define __KERNEL_AVX2__ -# endif -#endif - -/* quiet unused define warnings */ -#if defined(__KERNEL_SSE2__) -/* do nothing */ -#endif - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_avx.cpp b/intern/cycles/kernel/kernels/cpu/filter_avx.cpp deleted file mode 100644 index 012daba62d8..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_avx.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_avx2.cpp b/intern/cycles/kernel/kernels/cpu/filter_avx2.cpp deleted file mode 100644 index 16351a7f949..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_avx2.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# define __KERNEL_AVX2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_cpu.h b/intern/cycles/kernel/kernels/cpu/filter_cpu.h deleted file mode 100644 index 1423b182ab8..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_cpu.h +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common declaration part of all CPU kernels. */ - -void KERNEL_FUNCTION_FULL_NAME(filter_divide_shadow)(int sample, - TileInfo *tile_info, - int x, - int y, - float *unfilteredA, - float *unfilteredB, - float *sampleV, - float *sampleVV, - float *bufferV, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset); - -void KERNEL_FUNCTION_FULL_NAME(filter_get_feature)(int sample, - TileInfo *tile_info, - int m_offset, - int v_offset, - int x, - int y, - float *mean, - float *variance, - float scale, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset); - -void KERNEL_FUNCTION_FULL_NAME(filter_write_feature)(int sample, - int x, - int y, - int *buffer_params, - float *from, - float *buffer, - int out_offset, - int *prefilter_rect); - -void KERNEL_FUNCTION_FULL_NAME(filter_detect_outliers)(int x, - int y, - ccl_global float *image, - ccl_global float *variance, - ccl_global float *depth, - ccl_global float *output, - int *rect, - int pass_stride); - -void KERNEL_FUNCTION_FULL_NAME(filter_combine_halves)( - int x, int y, float *mean, float *variance, float *a, float *b, int *prefilter_rect, int r); - -void KERNEL_FUNCTION_FULL_NAME(filter_construct_transform)(float *buffer, - TileInfo *tiles, - int x, - int y, - int storage_ofs, - float *transform, - int *rank, - int *rect, - int pass_stride, - int frame_stride, - bool use_time, - int radius, - float pca_threshold); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_difference)(int dx, - int dy, - float *weight_image, - float *variance_image, - float *scale_image, - float *difference_image, - int *rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_blur)( - float *difference_image, float *out_image, int *rect, int stride, int f); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_weight)( - float *difference_image, float *out_image, int *rect, int stride, int f); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_update_output)(int dx, - int dy, - float *difference_image, - float *image, - float *temp_image, - float *out_image, - float *accum_image, - int *rect, - int channel_offset, - int stride, - int f); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_construct_gramian)(int dx, - int dy, - int t, - float *difference_image, - float *buffer, - float *transform, - int *rank, - float *XtWX, - float3 *XtWY, - int *rect, - int *filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time); - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_normalize)(float *out_image, - float *accum_image, - int *rect, - int stride); - -void KERNEL_FUNCTION_FULL_NAME(filter_finalize)(int x, - int y, - int storage_ofs, - float *buffer, - int *rank, - float *XtWX, - float3 *XtWY, - int *buffer_params, - int sample); - -#undef KERNEL_ARCH diff --git a/intern/cycles/kernel/kernels/cpu/filter_cpu_impl.h b/intern/cycles/kernel/kernels/cpu/filter_cpu_impl.h deleted file mode 100644 index 3d4cb87e104..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_cpu_impl.h +++ /dev/null @@ -1,331 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common implementation part of all CPU kernels. - * - * The idea is that particular .cpp files sets needed optimization flags and - * simply includes this file without worry of copying actual implementation over. - */ - -#include "kernel/kernel_compat_cpu.h" - -#include "kernel/filter/filter_kernel.h" - -#ifdef KERNEL_STUB -# define STUB_ASSERT(arch, name) \ - assert(!(#name " kernel stub for architecture " #arch " was called!")) -#endif - -CCL_NAMESPACE_BEGIN - -/* Denoise filter */ - -void KERNEL_FUNCTION_FULL_NAME(filter_divide_shadow)(int sample, - TileInfo *tile_info, - int x, - int y, - float *unfilteredA, - float *unfilteredB, - float *sampleVariance, - float *sampleVarianceV, - float *bufferVariance, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_divide_shadow); -#else - kernel_filter_divide_shadow(sample, - tile_info, - x, - y, - unfilteredA, - unfilteredB, - sampleVariance, - sampleVarianceV, - bufferVariance, - load_int4(prefilter_rect), - buffer_pass_stride, - buffer_denoising_offset); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_get_feature)(int sample, - TileInfo *tile_info, - int m_offset, - int v_offset, - int x, - int y, - float *mean, - float *variance, - float scale, - int *prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_get_feature); -#else - kernel_filter_get_feature(sample, - tile_info, - m_offset, - v_offset, - x, - y, - mean, - variance, - scale, - load_int4(prefilter_rect), - buffer_pass_stride, - buffer_denoising_offset); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_write_feature)(int sample, - int x, - int y, - int *buffer_params, - float *from, - float *buffer, - int out_offset, - int *prefilter_rect) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_write_feature); -#else - kernel_filter_write_feature( - sample, x, y, load_int4(buffer_params), from, buffer, out_offset, load_int4(prefilter_rect)); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_detect_outliers)(int x, - int y, - ccl_global float *image, - ccl_global float *variance, - ccl_global float *depth, - ccl_global float *output, - int *rect, - int pass_stride) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_detect_outliers); -#else - kernel_filter_detect_outliers( - x, y, image, variance, depth, output, load_int4(rect), pass_stride); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_combine_halves)( - int x, int y, float *mean, float *variance, float *a, float *b, int *prefilter_rect, int r) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_combine_halves); -#else - kernel_filter_combine_halves(x, y, mean, variance, a, b, load_int4(prefilter_rect), r); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_construct_transform)(float *buffer, - TileInfo *tile_info, - int x, - int y, - int storage_ofs, - float *transform, - int *rank, - int *prefilter_rect, - int pass_stride, - int frame_stride, - bool use_time, - int radius, - float pca_threshold) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_construct_transform); -#else - rank += storage_ofs; - transform += storage_ofs * TRANSFORM_SIZE; - kernel_filter_construct_transform(buffer, - tile_info, - x, - y, - load_int4(prefilter_rect), - pass_stride, - frame_stride, - use_time, - transform, - rank, - radius, - pca_threshold); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_difference)(int dx, - int dy, - float *weight_image, - float *variance_image, - float *scale_image, - float *difference_image, - int *rect, - int stride, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_calc_difference); -#else - kernel_filter_nlm_calc_difference(dx, - dy, - weight_image, - variance_image, - scale_image, - difference_image, - load_int4(rect), - stride, - channel_offset, - frame_offset, - a, - k_2); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_blur)( - float *difference_image, float *out_image, int *rect, int stride, int f) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_blur); -#else - kernel_filter_nlm_blur(difference_image, out_image, load_int4(rect), stride, f); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_calc_weight)( - float *difference_image, float *out_image, int *rect, int stride, int f) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_calc_weight); -#else - kernel_filter_nlm_calc_weight(difference_image, out_image, load_int4(rect), stride, f); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_update_output)(int dx, - int dy, - float *difference_image, - float *image, - float *temp_image, - float *out_image, - float *accum_image, - int *rect, - int channel_offset, - int stride, - int f) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_update_output); -#else - kernel_filter_nlm_update_output(dx, - dy, - difference_image, - image, - temp_image, - out_image, - accum_image, - load_int4(rect), - channel_offset, - stride, - f); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_construct_gramian)(int dx, - int dy, - int t, - float *difference_image, - float *buffer, - float *transform, - int *rank, - float *XtWX, - float3 *XtWY, - int *rect, - int *filter_window, - int stride, - int f, - int pass_stride, - int frame_offset, - bool use_time) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_construct_gramian); -#else - kernel_filter_nlm_construct_gramian(dx, - dy, - t, - difference_image, - buffer, - transform, - rank, - XtWX, - XtWY, - load_int4(rect), - load_int4(filter_window), - stride, - f, - pass_stride, - frame_offset, - use_time); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_nlm_normalize)(float *out_image, - float *accum_image, - int *rect, - int stride) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_nlm_normalize); -#else - kernel_filter_nlm_normalize(out_image, accum_image, load_int4(rect), stride); -#endif -} - -void KERNEL_FUNCTION_FULL_NAME(filter_finalize)(int x, - int y, - int storage_ofs, - float *buffer, - int *rank, - float *XtWX, - float3 *XtWY, - int *buffer_params, - int sample) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, filter_finalize); -#else - XtWX += storage_ofs * XTWX_SIZE; - XtWY += storage_ofs * XTWY_SIZE; - rank += storage_ofs; - kernel_filter_finalize(x, y, buffer, rank, 1, XtWX, XtWY, load_int4(buffer_params), sample); -#endif -} - -#undef KERNEL_STUB -#undef STUB_ASSERT -#undef KERNEL_ARCH - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/filter_sse2.cpp b/intern/cycles/kernel/kernels/cpu/filter_sse2.cpp deleted file mode 100644 index 75833d83648..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_sse2.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_sse3.cpp b/intern/cycles/kernel/kernels/cpu/filter_sse3.cpp deleted file mode 100644 index c998cd54d3a..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_sse3.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/filter_sse41.cpp b/intern/cycles/kernel/kernels/cpu/filter_sse41.cpp deleted file mode 100644 index fc4ef1fca5b..00000000000 --- a/intern/cycles/kernel/kernels/cpu/filter_sse41.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 */ - -#include "kernel/filter/filter.h" -#define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/filter_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu.h deleted file mode 100644 index ea3103f12c3..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common declaration part of all CPU kernels. */ - -void KERNEL_FUNCTION_FULL_NAME(path_trace)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride); - -void KERNEL_FUNCTION_FULL_NAME(convert_to_byte)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride); - -void KERNEL_FUNCTION_FULL_NAME(convert_to_half_float)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride); - -void KERNEL_FUNCTION_FULL_NAME(shader)(KernelGlobals *kg, - uint4 *input, - float4 *output, - int type, - int filter, - int i, - int offset, - int sample); - -void KERNEL_FUNCTION_FULL_NAME(bake)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride); - -/* Split kernels */ - -void KERNEL_FUNCTION_FULL_NAME(data_init)(KernelGlobals *kg, - ccl_constant KernelData *data, - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - int start_sample, - int end_sample, - int sx, - int sy, - int sw, - int sh, - int offset, - int stride, - ccl_global int *Queue_index, - int queuesize, - ccl_global char *use_queues_flag, - ccl_global unsigned int *work_pool_wgs, - unsigned int num_samples, - ccl_global float *buffer); - -#define DECLARE_SPLIT_KERNEL_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * data); - -DECLARE_SPLIT_KERNEL_FUNCTION(path_init) -DECLARE_SPLIT_KERNEL_FUNCTION(scene_intersect) -DECLARE_SPLIT_KERNEL_FUNCTION(lamp_emission) -DECLARE_SPLIT_KERNEL_FUNCTION(do_volume) -DECLARE_SPLIT_KERNEL_FUNCTION(queue_enqueue) -DECLARE_SPLIT_KERNEL_FUNCTION(indirect_background) -DECLARE_SPLIT_KERNEL_FUNCTION(shader_setup) -DECLARE_SPLIT_KERNEL_FUNCTION(shader_sort) -DECLARE_SPLIT_KERNEL_FUNCTION(shader_eval) -DECLARE_SPLIT_KERNEL_FUNCTION(holdout_emission_blurring_pathtermination_ao) -DECLARE_SPLIT_KERNEL_FUNCTION(subsurface_scatter) -DECLARE_SPLIT_KERNEL_FUNCTION(direct_lighting) -DECLARE_SPLIT_KERNEL_FUNCTION(shadow_blocked_ao) -DECLARE_SPLIT_KERNEL_FUNCTION(shadow_blocked_dl) -DECLARE_SPLIT_KERNEL_FUNCTION(enqueue_inactive) -DECLARE_SPLIT_KERNEL_FUNCTION(next_iteration_setup) -DECLARE_SPLIT_KERNEL_FUNCTION(indirect_subsurface) -DECLARE_SPLIT_KERNEL_FUNCTION(buffer_update) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_stopping) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_filter_x) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_filter_y) -DECLARE_SPLIT_KERNEL_FUNCTION(adaptive_adjust_samples) - -#undef KERNEL_ARCH diff --git a/intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h b/intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h deleted file mode 100644 index 51d6c23f72f..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_cpu_impl.h +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Templated common implementation part of all CPU kernels. - * - * The idea is that particular .cpp files sets needed optimization flags and - * simply includes this file without worry of copying actual implementation over. - */ - -// clang-format off -#include "kernel/kernel_compat_cpu.h" - -#ifndef KERNEL_STUB -# ifndef __SPLIT_KERNEL__ -# include "kernel/kernel_math.h" -# include "kernel/kernel_types.h" - -# include "kernel/split/kernel_split_data.h" -# include "kernel/kernel_globals.h" - -# include "kernel/kernel_color.h" -# include "kernel/kernels/cpu/kernel_cpu_image.h" -# include "kernel/kernel_film.h" -# include "kernel/kernel_path.h" -# include "kernel/kernel_path_branched.h" -# include "kernel/kernel_bake.h" -# else -# include "kernel/split/kernel_split_common.h" - -# include "kernel/split/kernel_data_init.h" -# include "kernel/split/kernel_path_init.h" -# include "kernel/split/kernel_scene_intersect.h" -# include "kernel/split/kernel_lamp_emission.h" -# include "kernel/split/kernel_do_volume.h" -# include "kernel/split/kernel_queue_enqueue.h" -# include "kernel/split/kernel_indirect_background.h" -# include "kernel/split/kernel_shader_setup.h" -# include "kernel/split/kernel_shader_sort.h" -# include "kernel/split/kernel_shader_eval.h" -# include "kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h" -# include "kernel/split/kernel_subsurface_scatter.h" -# include "kernel/split/kernel_direct_lighting.h" -# include "kernel/split/kernel_shadow_blocked_ao.h" -# include "kernel/split/kernel_shadow_blocked_dl.h" -# include "kernel/split/kernel_enqueue_inactive.h" -# include "kernel/split/kernel_next_iteration_setup.h" -# include "kernel/split/kernel_indirect_subsurface.h" -# include "kernel/split/kernel_buffer_update.h" -# include "kernel/split/kernel_adaptive_stopping.h" -# include "kernel/split/kernel_adaptive_filter_x.h" -# include "kernel/split/kernel_adaptive_filter_y.h" -# include "kernel/split/kernel_adaptive_adjust_samples.h" -# endif /* __SPLIT_KERNEL__ */ -#else -# define STUB_ASSERT(arch, name) \ - assert(!(#name " kernel stub for architecture " #arch " was called!")) - -# ifdef __SPLIT_KERNEL__ -# include "kernel/split/kernel_data_init.h" -# endif /* __SPLIT_KERNEL__ */ -#endif /* KERNEL_STUB */ -// clang-format on - -CCL_NAMESPACE_BEGIN - -#ifndef __SPLIT_KERNEL__ - -/* Path Tracing */ - -void KERNEL_FUNCTION_FULL_NAME(path_trace)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, path_trace); -# else -# ifdef __BRANCHED_PATH__ - if (kernel_data.integrator.branched) { - kernel_branched_path_trace(kg, buffer, sample, x, y, offset, stride); - } - else -# endif - { - kernel_path_trace(kg, buffer, sample, x, y, offset, stride); - } -# endif /* KERNEL_STUB */ -} - -/* Film */ - -void KERNEL_FUNCTION_FULL_NAME(convert_to_byte)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, convert_to_byte); -# else - kernel_film_convert_to_byte(kg, rgba, buffer, sample_scale, x, y, offset, stride); -# endif /* KERNEL_STUB */ -} - -void KERNEL_FUNCTION_FULL_NAME(convert_to_half_float)(KernelGlobals *kg, - uchar4 *rgba, - float *buffer, - float sample_scale, - int x, - int y, - int offset, - int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, convert_to_half_float); -# else - kernel_film_convert_to_half_float(kg, rgba, buffer, sample_scale, x, y, offset, stride); -# endif /* KERNEL_STUB */ -} - -/* Bake */ - -void KERNEL_FUNCTION_FULL_NAME(bake)( - KernelGlobals *kg, float *buffer, int sample, int x, int y, int offset, int stride) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, bake); -# else -# ifdef __BAKING__ - kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); -# endif -# endif /* KERNEL_STUB */ -} - -/* Shader Evaluate */ - -void KERNEL_FUNCTION_FULL_NAME(shader)(KernelGlobals *kg, - uint4 *input, - float4 *output, - int type, - int filter, - int i, - int offset, - int sample) -{ -# ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, shader); -# else - if (type == SHADER_EVAL_DISPLACE) { - kernel_displace_evaluate(kg, input, output, i); - } - else { - kernel_background_evaluate(kg, input, output, i); - } -# endif /* KERNEL_STUB */ -} - -#else /* __SPLIT_KERNEL__ */ - -/* Split Kernel Path Tracing */ - -# ifdef KERNEL_STUB -# define DEFINE_SPLIT_KERNEL_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - STUB_ASSERT(KERNEL_ARCH, name); \ - } - -# define DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(name, type) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - STUB_ASSERT(KERNEL_ARCH, name); \ - } -# else -# define DEFINE_SPLIT_KERNEL_FUNCTION(name) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - kernel_##name(kg); \ - } - -# define DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(name, type) \ - void KERNEL_FUNCTION_FULL_NAME(name)(KernelGlobals * kg, KernelData * /*data*/) \ - { \ - ccl_local type locals; \ - kernel_##name(kg, &locals); \ - } -# endif /* KERNEL_STUB */ - -DEFINE_SPLIT_KERNEL_FUNCTION(path_init) -DEFINE_SPLIT_KERNEL_FUNCTION(scene_intersect) -DEFINE_SPLIT_KERNEL_FUNCTION(lamp_emission) -DEFINE_SPLIT_KERNEL_FUNCTION(do_volume) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(queue_enqueue, QueueEnqueueLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_background) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_sort, ShaderSortLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(shader_eval) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(holdout_emission_blurring_pathtermination_ao, - BackgroundAOLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(subsurface_scatter) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(direct_lighting, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_ao) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_dl) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(enqueue_inactive, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(next_iteration_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_subsurface) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(buffer_update, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_stopping) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_x) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_y) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_adjust_samples) -#endif /* __SPLIT_KERNEL__ */ - -#undef KERNEL_STUB -#undef STUB_ASSERT -#undef KERNEL_ARCH - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split.cpp deleted file mode 100644 index 989f5e5aaa8..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CPU kernel entry points */ - -/* On x86-64, we can assume SSE2, so avoid the extra kernel and compile this - * one with SSE2 intrinsics. - */ -#if defined(__x86_64__) || defined(_M_X64) -# define __KERNEL_SSE2__ -#endif - -#define __SPLIT_KERNEL__ - -/* When building kernel for native machine detect kernel features from the flags - * set by compiler. - */ -#ifdef WITH_KERNEL_NATIVE -# ifdef __SSE2__ -# ifndef __KERNEL_SSE2__ -# define __KERNEL_SSE2__ -# endif -# endif -# ifdef __SSE3__ -# define __KERNEL_SSE3__ -# endif -# ifdef __SSSE3__ -# define __KERNEL_SSSE3__ -# endif -# ifdef __SSE4_1__ -# define __KERNEL_SSE41__ -# endif -# ifdef __AVX__ -# define __KERNEL_AVX__ -# endif -# ifdef __AVX2__ -# define __KERNEL_SSE__ -# define __KERNEL_AVX2__ -# endif -#endif - -/* quiet unused define warnings */ -#if defined(__KERNEL_SSE2__) -/* do nothing */ -#endif - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_avx.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_avx.cpp deleted file mode 100644 index 40e485d27c0..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_avx.cpp +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_avx -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_avx2.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_avx2.cpp deleted file mode 100644 index 8c44238470e..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_avx2.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2011-2014 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with AVX2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE__ -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# define __KERNEL_AVX__ -# define __KERNEL_AVX2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_AVX2 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_avx2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_sse2.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_sse2.cpp deleted file mode 100644 index 7a3f218d5fc..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_sse2.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE2 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE2 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_sse2 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_sse3.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_sse3.cpp deleted file mode 100644 index 1cab59e0ea0..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_sse3.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE3 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_sse3 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cpu/kernel_split_sse41.cpp b/intern/cycles/kernel/kernels/cpu/kernel_split_sse41.cpp deleted file mode 100644 index 637126d9d4c..00000000000 --- a/intern/cycles/kernel/kernels/cpu/kernel_split_sse41.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* Optimized CPU kernel entry points. This file is compiled with SSE3/SSSE3 - * optimization flags and nearly all functions inlined, while kernel.cpp - * is compiled without for other CPU's. */ - -#define __SPLIT_KERNEL__ - -#include "util/util_optimization.h" - -#ifndef WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 -# define KERNEL_STUB -#else -/* SSE optimization disabled for now on 32 bit, see bug T36316. */ -# if !(defined(__GNUC__) && (defined(i386) || defined(_M_IX86))) -# define __KERNEL_SSE2__ -# define __KERNEL_SSE3__ -# define __KERNEL_SSSE3__ -# define __KERNEL_SSE41__ -# endif -#endif /* WITH_CYCLES_OPTIMIZED_KERNEL_SSE41 */ - -#include "kernel/kernel.h" -#define KERNEL_ARCH cpu_sse41 -#include "kernel/kernels/cpu/kernel_cpu_impl.h" diff --git a/intern/cycles/kernel/kernels/cuda/filter.cu b/intern/cycles/kernel/kernels/cuda/filter.cu deleted file mode 100644 index 6c9642d1f03..00000000000 --- a/intern/cycles/kernel/kernels/cuda/filter.cu +++ /dev/null @@ -1,413 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CUDA kernel entry points */ - -#ifdef __CUDA_ARCH__ - -#include "kernel_config.h" - -#include "kernel/kernel_compat_cuda.h" - -#include "kernel/filter/filter_kernel.h" - -/* kernels */ - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_copy_input(float *buffer, - CCL_FILTER_TILE_INFO, - int4 prefilter_rect, - int buffer_pass_stride) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - int xtile = (x < tile_info->x[1]) ? 0 : ((x < tile_info->x[2]) ? 1 : 2); - int ytile = (y < tile_info->y[1]) ? 0 : ((y < tile_info->y[2]) ? 1 : 2); - int itile = ytile * 3 + xtile; - float *const in = ((float *)ccl_get_tile_buffer(itile)) + - (tile_info->offsets[itile] + y * tile_info->strides[itile] + x) * buffer_pass_stride; - buffer += ((y - prefilter_rect.y) * (prefilter_rect.z - prefilter_rect.x) + (x - prefilter_rect.x)) * buffer_pass_stride; - for (int i = 0; i < buffer_pass_stride; ++i) - buffer[i] = in[i]; - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_convert_to_rgb(float *rgb, float *buf, int sw, int sh, int stride, int pass_stride, int3 pass_offset, int num_inputs, int num_samples) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < sw && y < sh) { - if (num_inputs > 0) { - float *in = buf + x * pass_stride + (y * stride + pass_offset.x) / sizeof(float); - float *out = rgb + (x + y * sw) * 3; - out[0] = clamp(in[0] / num_samples, 0.0f, 10000.0f); - out[1] = clamp(in[1] / num_samples, 0.0f, 10000.0f); - out[2] = clamp(in[2] / num_samples, 0.0f, 10000.0f); - } - if (num_inputs > 1) { - float *in = buf + x * pass_stride + (y * stride + pass_offset.y) / sizeof(float); - float *out = rgb + (x + y * sw) * 3 + (sw * sh) * 3; - out[0] = in[0] / num_samples; - out[1] = in[1] / num_samples; - out[2] = in[2] / num_samples; - } - if (num_inputs > 2) { - float *in = buf + x * pass_stride + (y * stride + pass_offset.z) / sizeof(float); - float *out = rgb + (x + y * sw) * 3 + (sw * sh * 2) * 3; - out[0] = in[0] / num_samples; - out[1] = in[1] / num_samples; - out[2] = in[2] / num_samples; - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_convert_from_rgb(float *rgb, float *buf, int ix, int iy, int iw, int ih, int sx, int sy, int sw, int sh, int offset, int stride, int pass_stride, int num_samples) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < sw && y < sh) { - float *in = rgb + ((ix + x) + (iy + y) * iw) * 3; - float *out = buf + (offset + (sx + x) + (sy + y) * stride) * pass_stride; - out[0] = in[0] * num_samples; - out[1] = in[1] * num_samples; - out[2] = in[2] * num_samples; - } -} - - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_divide_shadow(int sample, - CCL_FILTER_TILE_INFO, - float *unfilteredA, - float *unfilteredB, - float *sampleVariance, - float *sampleVarianceV, - float *bufferVariance, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_divide_shadow(sample, - tile_info, - x, y, - unfilteredA, - unfilteredB, - sampleVariance, - sampleVarianceV, - bufferVariance, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_get_feature(int sample, - CCL_FILTER_TILE_INFO, - int m_offset, - int v_offset, - float *mean, - float *variance, - float scale, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_get_feature(sample, - tile_info, - m_offset, v_offset, - x, y, - mean, variance, - scale, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_write_feature(int sample, - int4 buffer_params, - int4 filter_area, - float *from, - float *buffer, - int out_offset, - int4 prefilter_rect) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < filter_area.z && y < filter_area.w) { - kernel_filter_write_feature(sample, - x + filter_area.x, - y + filter_area.y, - buffer_params, - from, - buffer, - out_offset, - prefilter_rect); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_detect_outliers(float *image, - float *variance, - float *depth, - float *output, - int4 prefilter_rect, - int pass_stride) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_detect_outliers(x, y, image, variance, depth, output, prefilter_rect, pass_stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_combine_halves(float *mean, float *variance, float *a, float *b, int4 prefilter_rect, int r) -{ - int x = prefilter_rect.x + blockDim.x*blockIdx.x + threadIdx.x; - int y = prefilter_rect.y + blockDim.y*blockIdx.y + threadIdx.y; - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_combine_halves(x, y, mean, variance, a, b, prefilter_rect, r); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_construct_transform(float const* __restrict__ buffer, - CCL_FILTER_TILE_INFO, - float *transform, int *rank, - int4 filter_area, int4 rect, - int radius, float pca_threshold, - int pass_stride, int frame_stride, - bool use_time) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < filter_area.z && y < filter_area.w) { - int *l_rank = rank + y*filter_area.z + x; - float *l_transform = transform + y*filter_area.z + x; - kernel_filter_construct_transform(buffer, - tile_info, - x + filter_area.x, y + filter_area.y, - rect, - pass_stride, frame_stride, - use_time, - l_transform, l_rank, - radius, pca_threshold, - filter_area.z*filter_area.w, - threadIdx.y*blockDim.x + threadIdx.x); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_calc_difference(const float *ccl_restrict weight_image, - const float *ccl_restrict variance_image, - const float *ccl_restrict scale_image, - float *difference_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_difference(co.x, co.y, co.z, co.w, - weight_image, - variance_image, - scale_image, - difference_image + ofs, - rect, stride, - channel_offset, - frame_offset, - a, k_2); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_blur(const float *ccl_restrict difference_image, - float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_blur(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_calc_weight(const float *ccl_restrict difference_image, - float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_weight(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_update_output(const float *ccl_restrict difference_image, - const float *ccl_restrict image, - float *out_image, - float *accum_image, - int w, - int h, - int stride, - int pass_stride, - int channel_offset, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_update_output(co.x, co.y, co.z, co.w, - difference_image + ofs, - image, - out_image, - accum_image, - rect, - channel_offset, - stride, f); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_normalize(float *out_image, - const float *ccl_restrict accum_image, - int w, - int h, - int stride) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < w && y < h) { - kernel_filter_nlm_normalize(x, y, out_image, accum_image, stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_nlm_construct_gramian(int t, - const float *ccl_restrict difference_image, - const float *ccl_restrict buffer, - float const* __restrict__ transform, - int *rank, - float *XtWX, - float3 *XtWY, - int4 filter_window, - int w, - int h, - int stride, - int pass_stride, - int r, - int f, - int frame_offset, - bool use_time) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords_window(w, h, r, pass_stride, &rect, &co, &ofs, filter_window)) { - kernel_filter_nlm_construct_gramian(co.x, co.y, - co.z, co.w, - t, - difference_image + ofs, - buffer, - transform, rank, - XtWX, XtWY, - rect, filter_window, - stride, f, - pass_stride, - frame_offset, - use_time, - threadIdx.y*blockDim.x + threadIdx.x); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_filter_finalize(float *buffer, - int *rank, - float *XtWX, - float3 *XtWY, - int4 filter_area, - int4 buffer_params, - int sample) -{ - int x = blockDim.x*blockIdx.x + threadIdx.x; - int y = blockDim.y*blockIdx.y + threadIdx.y; - if(x < filter_area.z && y < filter_area.w) { - int storage_ofs = y*filter_area.z+x; - rank += storage_ofs; - XtWX += storage_ofs; - XtWY += storage_ofs; - kernel_filter_finalize(x, y, buffer, rank, - filter_area.z*filter_area.w, - XtWX, XtWY, - buffer_params, sample); - } -} - -#endif - diff --git a/intern/cycles/kernel/kernels/cuda/kernel.cu b/intern/cycles/kernel/kernels/cuda/kernel.cu deleted file mode 100644 index cf62b6e781e..00000000000 --- a/intern/cycles/kernel/kernels/cuda/kernel.cu +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CUDA kernel entry points */ - -#ifdef __CUDA_ARCH__ - -#include "kernel/kernel_compat_cuda.h" -#include "kernel_config.h" - -#include "util/util_atomic.h" - -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/cuda/kernel_cuda_image.h" -#include "kernel/kernel_film.h" -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" -#include "kernel/kernel_bake.h" -#include "kernel/kernel_work_stealing.h" -#include "kernel/kernel_adaptive_sampling.h" - -/* kernels */ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_path_trace(WorkTile *tile, uint total_work_size) -{ - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - uint x, y, sample; - KernelGlobals kg; - if(thread_is_active) { - get_work_pixel(tile, work_index, &x, &y, &sample); - - kernel_path_trace(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - - if(kernel_data.film.cryptomatte_passes) { - __syncthreads(); - if(thread_is_active) { - kernel_cryptomatte_post(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - } -} - -#ifdef __BRANCHED_PATH__ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_BRANCHED_MAX_REGISTERS) -kernel_cuda_branched_path_trace(WorkTile *tile, uint total_work_size) -{ - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - uint x, y, sample; - KernelGlobals kg; - if(thread_is_active) { - get_work_pixel(tile, work_index, &x, &y, &sample); - - kernel_branched_path_trace(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - - if(kernel_data.film.cryptomatte_passes) { - __syncthreads(); - if(thread_is_active) { - kernel_cryptomatte_post(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } - } -} -#endif - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_stopping(WorkTile *tile, int sample, uint total_work_size) -{ - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - KernelGlobals kg; - if(thread_is_active && kernel_data.film.pass_adaptive_aux_buffer) { - uint x = tile->x + work_index % tile->w; - uint y = tile->y + work_index / tile->w; - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - kernel_do_adaptive_stopping(&kg, buffer, sample); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_filter_x(WorkTile *tile, int sample, uint) -{ - KernelGlobals kg; - if(kernel_data.film.pass_adaptive_aux_buffer && sample > kernel_data.integrator.adaptive_min_samples) { - if(ccl_global_id(0) < tile->h) { - int y = tile->y + ccl_global_id(0); - kernel_do_adaptive_filter_x(&kg, y, tile); - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_filter_y(WorkTile *tile, int sample, uint) -{ - KernelGlobals kg; - if(kernel_data.film.pass_adaptive_aux_buffer && sample > kernel_data.integrator.adaptive_min_samples) { - if(ccl_global_id(0) < tile->w) { - int x = tile->x + ccl_global_id(0); - kernel_do_adaptive_filter_y(&kg, x, tile); - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_adaptive_scale_samples(WorkTile *tile, int start_sample, int sample, uint total_work_size) -{ - if(kernel_data.film.pass_adaptive_aux_buffer) { - int work_index = ccl_global_id(0); - bool thread_is_active = work_index < total_work_size; - KernelGlobals kg; - if(thread_is_active) { - uint x = tile->x + work_index % tile->w; - uint y = tile->y + work_index / tile->w; - int index = tile->offset + x + y * tile->stride; - ccl_global float *buffer = tile->buffer + index * kernel_data.film.pass_stride; - if(buffer[kernel_data.film.pass_sample_count] < 0.0f) { - buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count]; - float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count]; - if(sample_multiplier != 1.0f) { - kernel_adaptive_post_adjust(&kg, buffer, sample_multiplier); - } - } - else { - kernel_adaptive_post_adjust(&kg, buffer, sample / (sample - 1.0f)); - } - } - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_byte(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) { - kernel_film_convert_to_byte(NULL, rgba, buffer, sample_scale, x, y, offset, stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_half_float(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) { - kernel_film_convert_to_half_float(NULL, rgba, buffer, sample_scale, x, y, offset, stride); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_displace(uint4 *input, - float4 *output, - int type, - int sx, - int sw, - int offset, - int sample) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - - if(x < sx + sw) { - KernelGlobals kg; - kernel_displace_evaluate(&kg, input, output, x); - } -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_background(uint4 *input, - float4 *output, - int type, - int sx, - int sw, - int offset, - int sample) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - - if(x < sx + sw) { - KernelGlobals kg; - kernel_background_evaluate(&kg, input, output, x); - } -} - -#ifdef __BAKING__ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_bake(WorkTile *tile, uint total_work_size) -{ - int work_index = ccl_global_id(0); - - if(work_index < total_work_size) { - uint x, y, sample; - get_work_pixel(tile, work_index, &x, &y, &sample); - - KernelGlobals kg; - kernel_bake_evaluate(&kg, tile->buffer, sample, x, y, tile->offset, tile->stride); - } -} -#endif - -#endif - diff --git a/intern/cycles/kernel/kernels/cuda/kernel_config.h b/intern/cycles/kernel/kernels/cuda/kernel_config.h deleted file mode 100644 index 2e47ce2de6c..00000000000 --- a/intern/cycles/kernel/kernels/cuda/kernel_config.h +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* device data taken from CUDA occupancy calculator */ - -/* 3.0 and 3.5 */ -#if __CUDA_ARCH__ == 300 || __CUDA_ARCH__ == 350 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 16 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 63 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 63 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 3.2 */ -#elif __CUDA_ARCH__ == 320 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 32768 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 16 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 63 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 63 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 3.7 */ -#elif __CUDA_ARCH__ == 370 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 16 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 255 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 63 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 5.x, 6.x */ -#elif __CUDA_ARCH__ <= 699 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 32 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 255 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -/* CUDA 9.0 seems to cause slowdowns on high-end Pascal cards unless we increase the number of - * registers */ -# if __CUDACC_VER_MAJOR__ >= 9 && __CUDA_ARCH__ >= 600 -# define CUDA_KERNEL_MAX_REGISTERS 64 -# else -# define CUDA_KERNEL_MAX_REGISTERS 48 -# endif -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 63 - -/* 7.x, 8.x */ -#elif __CUDA_ARCH__ <= 899 -# define CUDA_MULTIPRESSOR_MAX_REGISTERS 65536 -# define CUDA_MULTIPROCESSOR_MAX_BLOCKS 32 -# define CUDA_BLOCK_MAX_THREADS 1024 -# define CUDA_THREAD_MAX_REGISTERS 255 - -/* tunable parameters */ -# define CUDA_THREADS_BLOCK_WIDTH 16 -# define CUDA_KERNEL_MAX_REGISTERS 64 -# define CUDA_KERNEL_BRANCHED_MAX_REGISTERS 72 - -/* unknown architecture */ -#else -# error "Unknown or unsupported CUDA architecture, can't determine launch bounds" -#endif - -/* For split kernel using all registers seems fastest for now, but this - * is unlikely to be optimal once we resolve other bottlenecks. */ - -#define CUDA_KERNEL_SPLIT_MAX_REGISTERS CUDA_THREAD_MAX_REGISTERS - -/* Compute number of threads per block and minimum blocks per multiprocessor - * given the maximum number of registers per thread. */ - -#define CUDA_LAUNCH_BOUNDS(threads_block_width, thread_num_registers) \ - __launch_bounds__(threads_block_width *threads_block_width, \ - CUDA_MULTIPRESSOR_MAX_REGISTERS / \ - (threads_block_width * threads_block_width * thread_num_registers)) - -/* sanity checks */ - -#if CUDA_THREADS_BLOCK_WIDTH * CUDA_THREADS_BLOCK_WIDTH > CUDA_BLOCK_MAX_THREADS -# error "Maximum number of threads per block exceeded" -#endif - -#if CUDA_MULTIPRESSOR_MAX_REGISTERS / \ - (CUDA_THREADS_BLOCK_WIDTH * CUDA_THREADS_BLOCK_WIDTH * CUDA_KERNEL_MAX_REGISTERS) > \ - CUDA_MULTIPROCESSOR_MAX_BLOCKS -# error "Maximum number of blocks per multiprocessor exceeded" -#endif - -#if CUDA_KERNEL_MAX_REGISTERS > CUDA_THREAD_MAX_REGISTERS -# error "Maximum number of registers per thread exceeded" -#endif - -#if CUDA_KERNEL_BRANCHED_MAX_REGISTERS > CUDA_THREAD_MAX_REGISTERS -# error "Maximum number of registers per thread exceeded" -#endif diff --git a/intern/cycles/kernel/kernels/cuda/kernel_split.cu b/intern/cycles/kernel/kernels/cuda/kernel_split.cu deleted file mode 100644 index 95ad7599cf1..00000000000 --- a/intern/cycles/kernel/kernels/cuda/kernel_split.cu +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* CUDA split kernel entry points */ - -#ifdef __CUDA_ARCH__ - -#define __SPLIT_KERNEL__ - -#include "kernel/kernel_compat_cuda.h" -#include "kernel_config.h" - -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_data_init.h" -#include "kernel/split/kernel_path_init.h" -#include "kernel/split/kernel_scene_intersect.h" -#include "kernel/split/kernel_lamp_emission.h" -#include "kernel/split/kernel_do_volume.h" -#include "kernel/split/kernel_queue_enqueue.h" -#include "kernel/split/kernel_indirect_background.h" -#include "kernel/split/kernel_shader_setup.h" -#include "kernel/split/kernel_shader_sort.h" -#include "kernel/split/kernel_shader_eval.h" -#include "kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h" -#include "kernel/split/kernel_subsurface_scatter.h" -#include "kernel/split/kernel_direct_lighting.h" -#include "kernel/split/kernel_shadow_blocked_ao.h" -#include "kernel/split/kernel_shadow_blocked_dl.h" -#include "kernel/split/kernel_enqueue_inactive.h" -#include "kernel/split/kernel_next_iteration_setup.h" -#include "kernel/split/kernel_indirect_subsurface.h" -#include "kernel/split/kernel_buffer_update.h" -#include "kernel/split/kernel_adaptive_stopping.h" -#include "kernel/split/kernel_adaptive_filter_x.h" -#include "kernel/split/kernel_adaptive_filter_y.h" -#include "kernel/split/kernel_adaptive_adjust_samples.h" - -#include "kernel/kernel_film.h" - -/* kernels */ -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_state_buffer_size(uint num_threads, uint64_t *size) -{ - *size = split_data_buffer_size(NULL, num_threads); -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_path_trace_data_init( - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - int start_sample, - int end_sample, - int sx, int sy, int sw, int sh, int offset, int stride, - ccl_global int *Queue_index, - int queuesize, - ccl_global char *use_queues_flag, - ccl_global unsigned int *work_pool_wgs, - unsigned int num_samples, - ccl_global float *buffer) -{ - kernel_data_init(NULL, - NULL, - split_data_buffer, - num_elements, - ray_state, - start_sample, - end_sample, - sx, sy, sw, sh, offset, stride, - Queue_index, - queuesize, - use_queues_flag, - work_pool_wgs, - num_samples, - buffer); -} - -#define DEFINE_SPLIT_KERNEL_FUNCTION(name) \ - extern "C" __global__ void \ - CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_SPLIT_MAX_REGISTERS) \ - kernel_cuda_##name() \ - { \ - kernel_##name(NULL); \ - } - -#define DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(name, type) \ - extern "C" __global__ void \ - CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_SPLIT_MAX_REGISTERS) \ - kernel_cuda_##name() \ - { \ - ccl_local type locals; \ - kernel_##name(NULL, &locals); \ - } - -DEFINE_SPLIT_KERNEL_FUNCTION(path_init) -DEFINE_SPLIT_KERNEL_FUNCTION(scene_intersect) -DEFINE_SPLIT_KERNEL_FUNCTION(lamp_emission) -DEFINE_SPLIT_KERNEL_FUNCTION(do_volume) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(queue_enqueue, QueueEnqueueLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_background) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(shader_sort, ShaderSortLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(shader_eval) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(holdout_emission_blurring_pathtermination_ao, BackgroundAOLocals) -DEFINE_SPLIT_KERNEL_FUNCTION(subsurface_scatter) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(direct_lighting, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_ao) -DEFINE_SPLIT_KERNEL_FUNCTION(shadow_blocked_dl) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(enqueue_inactive, uint) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(next_iteration_setup, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(indirect_subsurface) -DEFINE_SPLIT_KERNEL_FUNCTION_LOCALS(buffer_update, uint) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_stopping) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_x) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_filter_y) -DEFINE_SPLIT_KERNEL_FUNCTION(adaptive_adjust_samples) - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_byte(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_byte(NULL, rgba, buffer, sample_scale, x, y, offset, stride); -} - -extern "C" __global__ void -CUDA_LAUNCH_BOUNDS(CUDA_THREADS_BLOCK_WIDTH, CUDA_KERNEL_MAX_REGISTERS) -kernel_cuda_convert_to_half_float(uchar4 *rgba, float *buffer, float sample_scale, int sx, int sy, int sw, int sh, int offset, int stride) -{ - int x = sx + blockDim.x*blockIdx.x + threadIdx.x; - int y = sy + blockDim.y*blockIdx.y + threadIdx.y; - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_half_float(NULL, rgba, buffer, sample_scale, x, y, offset, stride); -} - -#endif - diff --git a/intern/cycles/kernel/kernels/opencl/filter.cl b/intern/cycles/kernel/kernels/opencl/filter.cl deleted file mode 100644 index 996bc27f71b..00000000000 --- a/intern/cycles/kernel/kernels/opencl/filter.cl +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* OpenCL kernel entry points */ - -#include "kernel/kernel_compat_opencl.h" - -#include "kernel/filter/filter_kernel.h" - -/* kernels */ - -__kernel void kernel_ocl_filter_divide_shadow(int sample, - CCL_FILTER_TILE_INFO, - ccl_global float *unfilteredA, - ccl_global float *unfilteredB, - ccl_global float *sampleVariance, - ccl_global float *sampleVarianceV, - ccl_global float *bufferVariance, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_divide_shadow(sample, - CCL_FILTER_TILE_INFO_ARG, - x, y, - unfilteredA, - unfilteredB, - sampleVariance, - sampleVarianceV, - bufferVariance, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -__kernel void kernel_ocl_filter_get_feature(int sample, - CCL_FILTER_TILE_INFO, - int m_offset, - int v_offset, - ccl_global float *mean, - ccl_global float *variance, - float scale, - int4 prefilter_rect, - int buffer_pass_stride, - int buffer_denoising_offset) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_get_feature(sample, - CCL_FILTER_TILE_INFO_ARG, - m_offset, v_offset, - x, y, - mean, variance, - scale, - prefilter_rect, - buffer_pass_stride, - buffer_denoising_offset); - } -} - -__kernel void kernel_ocl_filter_write_feature(int sample, - int4 buffer_params, - int4 filter_area, - ccl_global float *from, - ccl_global float *buffer, - int out_offset, - int4 prefilter_rect) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < filter_area.z && y < filter_area.w) { - kernel_filter_write_feature(sample, - x + filter_area.x, - y + filter_area.y, - buffer_params, - from, - buffer, - out_offset, - prefilter_rect); - } -} - -__kernel void kernel_ocl_filter_detect_outliers(ccl_global float *image, - ccl_global float *variance, - ccl_global float *depth, - ccl_global float *output, - int4 prefilter_rect, - int pass_stride) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_detect_outliers(x, y, image, variance, depth, output, prefilter_rect, pass_stride); - } -} - -__kernel void kernel_ocl_filter_combine_halves(ccl_global float *mean, - ccl_global float *variance, - ccl_global float *a, - ccl_global float *b, - int4 prefilter_rect, - int r) -{ - int x = prefilter_rect.x + get_global_id(0); - int y = prefilter_rect.y + get_global_id(1); - if(x < prefilter_rect.z && y < prefilter_rect.w) { - kernel_filter_combine_halves(x, y, mean, variance, a, b, prefilter_rect, r); - } -} - -__kernel void kernel_ocl_filter_construct_transform(const ccl_global float *ccl_restrict buffer, - CCL_FILTER_TILE_INFO, - ccl_global float *transform, - ccl_global int *rank, - int4 filter_area, - int4 rect, - int pass_stride, - int frame_stride, - char use_time, - int radius, - float pca_threshold) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < filter_area.z && y < filter_area.w) { - ccl_global int *l_rank = rank + y*filter_area.z + x; - ccl_global float *l_transform = transform + y*filter_area.z + x; - kernel_filter_construct_transform(buffer, - CCL_FILTER_TILE_INFO_ARG, - x + filter_area.x, y + filter_area.y, - rect, - pass_stride, frame_stride, - use_time, - l_transform, l_rank, - radius, pca_threshold, - filter_area.z*filter_area.w, - get_local_id(1)*get_local_size(0) + get_local_id(0)); - } -} - -__kernel void kernel_ocl_filter_nlm_calc_difference(const ccl_global float *ccl_restrict weight_image, - const ccl_global float *ccl_restrict variance_image, - const ccl_global float *ccl_restrict scale_image, - ccl_global float *difference_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int channel_offset, - int frame_offset, - float a, - float k_2) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_difference(co.x, co.y, co.z, co.w, - weight_image, - variance_image, - scale_image, - difference_image + ofs, - rect, stride, - channel_offset, - frame_offset, - a, k_2); - } -} - -__kernel void kernel_ocl_filter_nlm_blur(const ccl_global float *ccl_restrict difference_image, - ccl_global float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_blur(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -__kernel void kernel_ocl_filter_nlm_calc_weight(const ccl_global float *ccl_restrict difference_image, - ccl_global float *out_image, - int w, - int h, - int stride, - int pass_stride, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_calc_weight(co.x, co.y, - difference_image + ofs, - out_image + ofs, - rect, stride, f); - } -} - -__kernel void kernel_ocl_filter_nlm_update_output(const ccl_global float *ccl_restrict difference_image, - const ccl_global float *ccl_restrict image, - ccl_global float *out_image, - ccl_global float *accum_image, - int w, - int h, - int stride, - int pass_stride, - int channel_offset, - int r, - int f) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords(w, h, r, pass_stride, &rect, &co, &ofs)) { - kernel_filter_nlm_update_output(co.x, co.y, co.z, co.w, - difference_image + ofs, - image, - out_image, - accum_image, - rect, - channel_offset, - stride, f); - } -} - -__kernel void kernel_ocl_filter_nlm_normalize(ccl_global float *out_image, - const ccl_global float *ccl_restrict accum_image, - int w, - int h, - int stride) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < w && y < h) { - kernel_filter_nlm_normalize(x, y, out_image, accum_image, stride); - } -} - -__kernel void kernel_ocl_filter_nlm_construct_gramian(int t, - const ccl_global float *ccl_restrict difference_image, - const ccl_global float *ccl_restrict buffer, - const ccl_global float *ccl_restrict transform, - ccl_global int *rank, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 filter_window, - int w, - int h, - int stride, - int pass_stride, - int r, - int f, - int frame_offset, - char use_time) -{ - int4 co, rect; - int ofs; - if(get_nlm_coords_window(w, h, r, pass_stride, &rect, &co, &ofs, filter_window)) { - kernel_filter_nlm_construct_gramian(co.x, co.y, - co.z, co.w, - t, - difference_image + ofs, - buffer, - transform, rank, - XtWX, XtWY, - rect, filter_window, - stride, f, - pass_stride, - frame_offset, - use_time, - get_local_id(1)*get_local_size(0) + get_local_id(0)); - } -} - -__kernel void kernel_ocl_filter_finalize(ccl_global float *buffer, - ccl_global int *rank, - ccl_global float *XtWX, - ccl_global float3 *XtWY, - int4 filter_area, - int4 buffer_params, - int sample) -{ - int x = get_global_id(0); - int y = get_global_id(1); - if(x < filter_area.z && y < filter_area.w) { - int storage_ofs = y*filter_area.z+x; - rank += storage_ofs; - XtWX += storage_ofs; - XtWY += storage_ofs; - kernel_filter_finalize(x, y, buffer, rank, - filter_area.z*filter_area.w, - XtWX, XtWY, - buffer_params, sample); - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl deleted file mode 100644 index ebdb99d4730..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_adjust_samples.h" - -#define KERNEL_NAME adaptive_adjust_samples -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_x.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_x.cl deleted file mode 100644 index 76d82d4184e..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_x.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_filter_x.h" - -#define KERNEL_NAME adaptive_filter_x -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_y.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_y.cl deleted file mode 100644 index 1e6d15ba0f2..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_filter_y.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_filter_y.h" - -#define KERNEL_NAME adaptive_filter_y -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_stopping.cl b/intern/cycles/kernel/kernels/opencl/kernel_adaptive_stopping.cl deleted file mode 100644 index 51de0059667..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_adaptive_stopping.cl +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_adaptive_stopping.h" - -#define KERNEL_NAME adaptive_stopping -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME diff --git a/intern/cycles/kernel/kernels/opencl/kernel_background.cl b/intern/cycles/kernel/kernels/opencl/kernel_background.cl deleted file mode 100644 index 0e600676e82..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_background.cl +++ /dev/null @@ -1,35 +0,0 @@ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/opencl/kernel_opencl_image.h" - -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" - -#include "kernel/kernel_bake.h" - -__kernel void kernel_ocl_background( - ccl_constant KernelData *data, - ccl_global uint4 *input, - ccl_global float4 *output, - - KERNEL_BUFFER_PARAMS, - - int type, int sx, int sw, int offset, int sample) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - - if(x < sx + sw) { - kernel_background_evaluate(kg, input, output, x); - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl b/intern/cycles/kernel/kernels/opencl/kernel_bake.cl deleted file mode 100644 index 7b81e387467..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_bake.cl +++ /dev/null @@ -1,36 +0,0 @@ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/opencl/kernel_opencl_image.h" - -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" - -#include "kernel/kernel_bake.h" - -__kernel void kernel_ocl_bake( - ccl_constant KernelData *data, - ccl_global float *buffer, - - KERNEL_BUFFER_PARAMS, - - int sx, int sy, int sw, int sh, int offset, int stride, int sample) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - int y = sy + ccl_global_id(1); - - if(x < sx + sw && y < sy + sh) { -#ifndef __NO_BAKING__ - kernel_bake_evaluate(kg, buffer, sample, x, y, offset, stride); -#endif - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_base.cl b/intern/cycles/kernel/kernels/opencl/kernel_base.cl deleted file mode 100644 index 1c2d89e8a92..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_base.cl +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* OpenCL base kernels entry points */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" - -#include "kernel/kernel_film.h" - - -__kernel void kernel_ocl_convert_to_byte( - ccl_constant KernelData *data, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - - KERNEL_BUFFER_PARAMS, - - float sample_scale, - int sx, int sy, int sw, int sh, int offset, int stride) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - int y = sy + ccl_global_id(1); - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_byte(kg, rgba, buffer, sample_scale, x, y, offset, stride); -} - -__kernel void kernel_ocl_convert_to_half_float( - ccl_constant KernelData *data, - ccl_global uchar4 *rgba, - ccl_global float *buffer, - - KERNEL_BUFFER_PARAMS, - - float sample_scale, - int sx, int sy, int sw, int sh, int offset, int stride) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - int y = sy + ccl_global_id(1); - - if(x < sx + sw && y < sy + sh) - kernel_film_convert_to_half_float(kg, rgba, buffer, sample_scale, x, y, offset, stride); -} - -__kernel void kernel_ocl_zero_buffer(ccl_global float4 *buffer, uint64_t size, uint64_t offset) -{ - size_t i = ccl_global_id(0) + ccl_global_id(1) * ccl_global_size(0); - - if(i < size / sizeof(float4)) { - buffer[i+offset/sizeof(float4)] = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - else if(i == size / sizeof(float4)) { - ccl_global uchar *b = (ccl_global uchar*)&buffer[i+offset/sizeof(float4)]; - - for(i = 0; i < size % sizeof(float4); i++) { - *(b++) = 0; - } - } -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_buffer_update.cl b/intern/cycles/kernel/kernels/opencl/kernel_buffer_update.cl deleted file mode 100644 index dcea2630aef..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_buffer_update.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_buffer_update.h" - -#define KERNEL_NAME buffer_update -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_data_init.cl b/intern/cycles/kernel/kernels/opencl/kernel_data_init.cl deleted file mode 100644 index 7125348a49f..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_data_init.cl +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_data_init.h" - -__kernel void kernel_ocl_path_trace_data_init( - ccl_global char *kg, - ccl_constant KernelData *data, - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - KERNEL_BUFFER_PARAMS, - int start_sample, - int end_sample, - int sx, int sy, int sw, int sh, int offset, int stride, - ccl_global int *Queue_index, /* Tracks the number of elements in queues */ - int queuesize, /* size (capacity) of the queue */ - ccl_global char *use_queues_flag, /* flag to decide if scene-intersect kernel should use queues to fetch ray index */ - ccl_global unsigned int *work_pool_wgs, /* Work pool for each work group */ - unsigned int num_samples, /* Total number of samples per pixel */ - ccl_global float *buffer) -{ - kernel_data_init((KernelGlobals*)kg, - data, - split_data_buffer, - num_elements, - ray_state, - KERNEL_BUFFER_ARGS, - start_sample, - end_sample, - sx, sy, sw, sh, offset, stride, - Queue_index, - queuesize, - use_queues_flag, - work_pool_wgs, - num_samples, - buffer); -} diff --git a/intern/cycles/kernel/kernels/opencl/kernel_direct_lighting.cl b/intern/cycles/kernel/kernels/opencl/kernel_direct_lighting.cl deleted file mode 100644 index ed64ae01aae..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_direct_lighting.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_direct_lighting.h" - -#define KERNEL_NAME direct_lighting -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_displace.cl b/intern/cycles/kernel/kernels/opencl/kernel_displace.cl deleted file mode 100644 index 76cc36971f5..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_displace.cl +++ /dev/null @@ -1,36 +0,0 @@ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernels/opencl/kernel_opencl_image.h" - -#include "kernel/kernel_path.h" -#include "kernel/kernel_path_branched.h" - -#include "kernel/kernel_bake.h" - -__kernel void kernel_ocl_displace( - ccl_constant KernelData *data, - ccl_global uint4 *input, - ccl_global float4 *output, - - KERNEL_BUFFER_PARAMS, - - int type, int sx, int sw, int offset, int sample) -{ - KernelGlobals kglobals, *kg = &kglobals; - - kg->data = data; - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); - - int x = sx + ccl_global_id(0); - - if(x < sx + sw) { - kernel_displace_evaluate(kg, input, output, x); - } -} - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_do_volume.cl b/intern/cycles/kernel/kernels/opencl/kernel_do_volume.cl deleted file mode 100644 index 8afaa686e28..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_do_volume.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_do_volume.h" - -#define KERNEL_NAME do_volume -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_enqueue_inactive.cl b/intern/cycles/kernel/kernels/opencl/kernel_enqueue_inactive.cl deleted file mode 100644 index e68d4104a91..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_enqueue_inactive.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_enqueue_inactive.h" - -#define KERNEL_NAME enqueue_inactive -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl b/intern/cycles/kernel/kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl deleted file mode 100644 index 9e1e57beba6..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_holdout_emission_blurring_pathtermination_ao.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h" - -#define KERNEL_NAME holdout_emission_blurring_pathtermination_ao -#define LOCALS_TYPE BackgroundAOLocals -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_indirect_background.cl b/intern/cycles/kernel/kernels/opencl/kernel_indirect_background.cl deleted file mode 100644 index 192d01444ba..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_indirect_background.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_indirect_background.h" - -#define KERNEL_NAME indirect_background -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_lamp_emission.cl b/intern/cycles/kernel/kernels/opencl/kernel_lamp_emission.cl deleted file mode 100644 index c314dc96c33..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_lamp_emission.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_lamp_emission.h" - -#define KERNEL_NAME lamp_emission -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_next_iteration_setup.cl b/intern/cycles/kernel/kernels/opencl/kernel_next_iteration_setup.cl deleted file mode 100644 index 8b1332bf013..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_next_iteration_setup.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_next_iteration_setup.h" - -#define KERNEL_NAME next_iteration_setup -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h b/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h deleted file mode 100644 index bb6b8a40e8e..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_opencl_image.h +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright 2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifdef WITH_NANOVDB -/* Data type to replace `double` used in the NanoVDB headers. Cycles don't need doubles, and is - * safer and more portable to never use double datatype on GPU. - * Use a special structure, so that the following is true: - * - No unnoticed implicit cast or mathematical operations used on scalar 64bit type - * (which rules out trick like using `uint64_t` as a drop-in replacement for double). - * - Padding rules are matching exactly `double` - * (which rules out array of `uint8_t`). */ -typedef struct ccl_vdb_double_t { - uint64_t i; -} ccl_vdb_double_t; - -# define double ccl_vdb_double_t -# include "nanovdb/CNanoVDB.h" -# undef double -#endif - -/* For OpenCL we do manual lookup and interpolation. */ - -ccl_device_inline ccl_global TextureInfo *kernel_tex_info(KernelGlobals *kg, uint id) -{ - const uint tex_offset = id -#define KERNEL_TEX(type, name) +1 -#include "kernel/kernel_textures.h" - ; - - return &((ccl_global TextureInfo *)kg->buffers[0])[tex_offset]; -} - -#define tex_fetch(type, info, index) \ - ((ccl_global type *)(kg->buffers[info->cl_buffer] + info->data))[(index)] - -ccl_device_inline int svm_image_texture_wrap_periodic(int x, int width) -{ - x %= width; - if (x < 0) - x += width; - return x; -} - -ccl_device_inline int svm_image_texture_wrap_clamp(int x, int width) -{ - return clamp(x, 0, width - 1); -} - -ccl_device_inline float4 svm_image_texture_read( - KernelGlobals *kg, const ccl_global TextureInfo *info, void *acc, int x, int y, int z) -{ - const int data_offset = x + info->width * y + info->width * info->height * z; - const int texture_type = info->data_type; - - /* Float4 */ - if (texture_type == IMAGE_DATA_TYPE_FLOAT4) { - return tex_fetch(float4, info, data_offset); - } - /* Byte4 */ - else if (texture_type == IMAGE_DATA_TYPE_BYTE4) { - uchar4 r = tex_fetch(uchar4, info, data_offset); - float f = 1.0f / 255.0f; - return make_float4(r.x * f, r.y * f, r.z * f, r.w * f); - } - /* Ushort4 */ - else if (texture_type == IMAGE_DATA_TYPE_USHORT4) { - ushort4 r = tex_fetch(ushort4, info, data_offset); - float f = 1.0f / 65535.f; - return make_float4(r.x * f, r.y * f, r.z * f, r.w * f); - } - /* Float */ - else if (texture_type == IMAGE_DATA_TYPE_FLOAT) { - float f = tex_fetch(float, info, data_offset); - return make_float4(f, f, f, 1.0f); - } - /* UShort */ - else if (texture_type == IMAGE_DATA_TYPE_USHORT) { - ushort r = tex_fetch(ushort, info, data_offset); - float f = r * (1.0f / 65535.0f); - return make_float4(f, f, f, 1.0f); - } -#ifdef WITH_NANOVDB - /* NanoVDB Float */ - else if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT) { - cnanovdb_coord coord; - coord.mVec[0] = x; - coord.mVec[1] = y; - coord.mVec[2] = z; - float f = cnanovdb_readaccessor_getValueF((cnanovdb_readaccessor *)acc, &coord); - return make_float4(f, f, f, 1.0f); - } - /* NanoVDB Float3 */ - else if (texture_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { - cnanovdb_coord coord; - coord.mVec[0] = x; - coord.mVec[1] = y; - coord.mVec[2] = z; - cnanovdb_Vec3F f = cnanovdb_readaccessor_getValueF3((cnanovdb_readaccessor *)acc, &coord); - return make_float4(f.mVec[0], f.mVec[1], f.mVec[2], 1.0f); - } -#endif -#ifdef __KERNEL_CL_KHR_FP16__ - /* Half and Half4 are optional in OpenCL */ - else if (texture_type == IMAGE_DATA_TYPE_HALF) { - float f = tex_fetch(half, info, data_offset); - return make_float4(f, f, f, 1.0f); - } - else if (texture_type == IMAGE_DATA_TYPE_HALF4) { - half4 r = tex_fetch(half4, info, data_offset); - return make_float4(r.x, r.y, r.z, r.w); - } -#endif - /* Byte */ - else { - uchar r = tex_fetch(uchar, info, data_offset); - float f = r * (1.0f / 255.0f); - return make_float4(f, f, f, 1.0f); - } -} - -ccl_device_inline float4 -svm_image_texture_read_2d(KernelGlobals *kg, int id, void *acc, int x, int y) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - -#ifdef WITH_NANOVDB - if (info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { -#endif - /* Wrap */ - if (info->extension == EXTENSION_REPEAT) { - x = svm_image_texture_wrap_periodic(x, info->width); - y = svm_image_texture_wrap_periodic(y, info->height); - } - else { - x = svm_image_texture_wrap_clamp(x, info->width); - y = svm_image_texture_wrap_clamp(y, info->height); - } -#ifdef WITH_NANOVDB - } -#endif - - return svm_image_texture_read(kg, info, acc, x, y, 0); -} - -ccl_device_inline float4 -svm_image_texture_read_3d(KernelGlobals *kg, int id, void *acc, int x, int y, int z) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - -#ifdef WITH_NANOVDB - if (info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT && - info->data_type != IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { -#endif - /* Wrap */ - if (info->extension == EXTENSION_REPEAT) { - x = svm_image_texture_wrap_periodic(x, info->width); - y = svm_image_texture_wrap_periodic(y, info->height); - z = svm_image_texture_wrap_periodic(z, info->depth); - } - else { - x = svm_image_texture_wrap_clamp(x, info->width); - y = svm_image_texture_wrap_clamp(y, info->height); - z = svm_image_texture_wrap_clamp(z, info->depth); - } -#ifdef WITH_NANOVDB - } -#endif - - return svm_image_texture_read(kg, info, acc, x, y, z); -} - -ccl_device_inline float svm_image_texture_frac(float x, int *ix) -{ - int i = float_to_int(x) - ((x < 0.0f) ? 1 : 0); - *ix = i; - return x - (float)i; -} - -#define SET_CUBIC_SPLINE_WEIGHTS(u, t) \ - { \ - u[0] = (((-1.0f / 6.0f) * t + 0.5f) * t - 0.5f) * t + (1.0f / 6.0f); \ - u[1] = ((0.5f * t - 1.0f) * t) * t + (2.0f / 3.0f); \ - u[2] = ((-0.5f * t + 0.5f) * t + 0.5f) * t + (1.0f / 6.0f); \ - u[3] = (1.0f / 6.0f) * t * t * t; \ - } \ - (void)0 - -ccl_device float4 kernel_tex_image_interp(KernelGlobals *kg, int id, float x, float y) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - - if (info->extension == EXTENSION_CLIP) { - if (x < 0.0f || y < 0.0f || x > 1.0f || y > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - } - - if (info->interpolation == INTERPOLATION_CLOSEST) { - /* Closest interpolation. */ - int ix, iy; - svm_image_texture_frac(x * info->width, &ix); - svm_image_texture_frac(y * info->height, &iy); - - return svm_image_texture_read_2d(kg, id, NULL, ix, iy); - } - else if (info->interpolation == INTERPOLATION_LINEAR) { - /* Bilinear interpolation. */ - int ix, iy; - float tx = svm_image_texture_frac(x * info->width - 0.5f, &ix); - float ty = svm_image_texture_frac(y * info->height - 0.5f, &iy); - - float4 r; - r = (1.0f - ty) * (1.0f - tx) * svm_image_texture_read_2d(kg, id, NULL, ix, iy); - r += (1.0f - ty) * tx * svm_image_texture_read_2d(kg, id, NULL, ix + 1, iy); - r += ty * (1.0f - tx) * svm_image_texture_read_2d(kg, id, NULL, ix, iy + 1); - r += ty * tx * svm_image_texture_read_2d(kg, id, NULL, ix + 1, iy + 1); - return r; - } - else { - /* Bicubic interpolation. */ - int ix, iy; - float tx = svm_image_texture_frac(x * info->width - 0.5f, &ix); - float ty = svm_image_texture_frac(y * info->height - 0.5f, &iy); - - float u[4], v[4]; - SET_CUBIC_SPLINE_WEIGHTS(u, tx); - SET_CUBIC_SPLINE_WEIGHTS(v, ty); - - float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - float weight = u[x] * v[y]; - r += weight * svm_image_texture_read_2d(kg, id, NULL, ix + x - 1, iy + y - 1); - } - } - return r; - } -} - -ccl_device float4 kernel_tex_image_interp_3d(KernelGlobals *kg, int id, float3 P, int interp) -{ - const ccl_global TextureInfo *info = kernel_tex_info(kg, id); - - if (info->use_transform_3d) { - Transform tfm = info->transform_3d; - P = transform_point(&tfm, P); - } - - float x = P.x; - float y = P.y; - float z = P.z; - - uint interpolation = (interp == INTERPOLATION_NONE) ? info->interpolation : interp; - -#ifdef WITH_NANOVDB - cnanovdb_readaccessor acc; - if (info->data_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT || - info->data_type == IMAGE_DATA_TYPE_NANOVDB_FLOAT3) { - ccl_global cnanovdb_griddata *grid = - (ccl_global cnanovdb_griddata *)(kg->buffers[info->cl_buffer] + info->data); - cnanovdb_readaccessor_init(&acc, cnanovdb_treedata_rootF(cnanovdb_griddata_tree(grid))); - } - else { - if (info->extension == EXTENSION_CLIP) { - if (x < 0.0f || y < 0.0f || z < 0.0f || x > 1.0f || y > 1.0f || z > 1.0f) { - return make_float4(0.0f, 0.0f, 0.0f, 0.0f); - } - } - - x *= info->width; - y *= info->height; - z *= info->depth; - } -# define NANOVDB_ACCESS_POINTER &acc -#else -# define NANOVDB_ACCESS_POINTER NULL -#endif - - if (interpolation == INTERPOLATION_CLOSEST) { - /* Closest interpolation. */ - int ix, iy, iz; - svm_image_texture_frac(x, &ix); - svm_image_texture_frac(y, &iy); - svm_image_texture_frac(z, &iz); - - return svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy, iz); - } - else if (interpolation == INTERPOLATION_LINEAR) { - /* Trilinear interpolation. */ - int ix, iy, iz; - float tx = svm_image_texture_frac(x - 0.5f, &ix); - float ty = svm_image_texture_frac(y - 0.5f, &iy); - float tz = svm_image_texture_frac(z - 0.5f, &iz); - - float4 r; - r = (1.0f - tz) * (1.0f - ty) * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy, iz); - r += (1.0f - tz) * (1.0f - ty) * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy, iz); - r += (1.0f - tz) * ty * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy + 1, iz); - r += (1.0f - tz) * ty * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy + 1, iz); - - r += tz * (1.0f - ty) * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy, iz + 1); - r += tz * (1.0f - ty) * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy, iz + 1); - r += tz * ty * (1.0f - tx) * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix, iy + 1, iz + 1); - r += tz * ty * tx * - svm_image_texture_read_3d(kg, id, NANOVDB_ACCESS_POINTER, ix + 1, iy + 1, iz + 1); - return r; - } - else { - /* Tricubic interpolation. */ - int ix, iy, iz; - float tx = svm_image_texture_frac(x - 0.5f, &ix); - float ty = svm_image_texture_frac(y - 0.5f, &iy); - float tz = svm_image_texture_frac(z - 0.5f, &iz); - - float u[4], v[4], w[4]; - SET_CUBIC_SPLINE_WEIGHTS(u, tx); - SET_CUBIC_SPLINE_WEIGHTS(v, ty); - SET_CUBIC_SPLINE_WEIGHTS(w, tz); - - float4 r = make_float4(0.0f, 0.0f, 0.0f, 0.0f); - - for (int z = 0; z < 4; z++) { - for (int y = 0; y < 4; y++) { - for (int x = 0; x < 4; x++) { - float weight = u[x] * v[y] * w[z]; - r += weight * svm_image_texture_read_3d( - kg, id, NANOVDB_ACCESS_POINTER, ix + x - 1, iy + y - 1, iz + z - 1); - } - } - } - return r; - } -#undef NANOVDB_ACCESS_POINTER -} - -#undef SET_CUBIC_SPLINE_WEIGHTS diff --git a/intern/cycles/kernel/kernels/opencl/kernel_queue_enqueue.cl b/intern/cycles/kernel/kernels/opencl/kernel_queue_enqueue.cl deleted file mode 100644 index 68ee6f1d536..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_queue_enqueue.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_queue_enqueue.h" - -#define KERNEL_NAME queue_enqueue -#define LOCALS_TYPE QueueEnqueueLocals -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shader_eval.cl b/intern/cycles/kernel/kernels/opencl/kernel_shader_eval.cl deleted file mode 100644 index 40eaa561863..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shader_eval.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shader_eval.h" - -#define KERNEL_NAME shader_eval -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shader_setup.cl b/intern/cycles/kernel/kernels/opencl/kernel_shader_setup.cl deleted file mode 100644 index 8c36100f762..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shader_setup.cl +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shader_setup.h" - -#define KERNEL_NAME shader_setup -#define LOCALS_TYPE unsigned int -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shader_sort.cl b/intern/cycles/kernel/kernels/opencl/kernel_shader_sort.cl deleted file mode 100644 index bcacaa4a054..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shader_sort.cl +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shader_sort.h" - -__attribute__((reqd_work_group_size(64, 1, 1))) -#define KERNEL_NAME shader_sort -#define LOCALS_TYPE ShaderSortLocals -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME -#undef LOCALS_TYPE - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_ao.cl b/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_ao.cl deleted file mode 100644 index 8de250a375c..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_ao.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shadow_blocked_ao.h" - -#define KERNEL_NAME shadow_blocked_ao -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_dl.cl b/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_dl.cl deleted file mode 100644 index 29da77022ed..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_shadow_blocked_dl.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_shadow_blocked_dl.h" - -#define KERNEL_NAME shadow_blocked_dl -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl b/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl deleted file mode 100644 index c3b7b09460a..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_split_bundle.cl +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" // PRECOMPILED -#include "kernel/split/kernel_split_common.h" // PRECOMPILED - -#include "kernel/kernels/opencl/kernel_data_init.cl" -#include "kernel/kernels/opencl/kernel_path_init.cl" -#include "kernel/kernels/opencl/kernel_state_buffer_size.cl" -#include "kernel/kernels/opencl/kernel_scene_intersect.cl" -#include "kernel/kernels/opencl/kernel_queue_enqueue.cl" -#include "kernel/kernels/opencl/kernel_shader_setup.cl" -#include "kernel/kernels/opencl/kernel_shader_sort.cl" -#include "kernel/kernels/opencl/kernel_enqueue_inactive.cl" -#include "kernel/kernels/opencl/kernel_next_iteration_setup.cl" -#include "kernel/kernels/opencl/kernel_indirect_subsurface.cl" -#include "kernel/kernels/opencl/kernel_buffer_update.cl" -#include "kernel/kernels/opencl/kernel_adaptive_stopping.cl" -#include "kernel/kernels/opencl/kernel_adaptive_filter_x.cl" -#include "kernel/kernels/opencl/kernel_adaptive_filter_y.cl" -#include "kernel/kernels/opencl/kernel_adaptive_adjust_samples.cl" diff --git a/intern/cycles/kernel/kernels/opencl/kernel_split_function.h b/intern/cycles/kernel/kernels/opencl/kernel_split_function.h deleted file mode 100644 index e123b4cd6ec..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_split_function.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define KERNEL_NAME_JOIN(a, b) a##_##b -#define KERNEL_NAME_EVAL(a, b) KERNEL_NAME_JOIN(a, b) - -__kernel void KERNEL_NAME_EVAL(kernel_ocl_path_trace, - KERNEL_NAME)(ccl_global char *kg_global, - ccl_constant KernelData *data, - - ccl_global void *split_data_buffer, - ccl_global char *ray_state, - - KERNEL_BUFFER_PARAMS, - - ccl_global int *queue_index, - ccl_global char *use_queues_flag, - ccl_global unsigned int *work_pools, - ccl_global float *buffer) -{ -#ifdef LOCALS_TYPE - ccl_local LOCALS_TYPE locals; -#endif - - KernelGlobals *kg = (KernelGlobals *)kg_global; - - if (ccl_local_id(0) + ccl_local_id(1) == 0) { - kg->data = data; - - kernel_split_params.queue_index = queue_index; - kernel_split_params.use_queues_flag = use_queues_flag; - kernel_split_params.work_pools = work_pools; - kernel_split_params.tile.buffer = buffer; - - split_data_init(kg, - &kernel_split_state, - ccl_global_size(0) * ccl_global_size(1), - split_data_buffer, - ray_state); - } - - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - - KERNEL_NAME_EVAL(kernel, KERNEL_NAME) - (kg -#ifdef LOCALS_TYPE - , - &locals -#endif - ); -} - -#undef KERNEL_NAME_JOIN -#undef KERNEL_NAME_EVAL diff --git a/intern/cycles/kernel/kernels/opencl/kernel_subsurface_scatter.cl b/intern/cycles/kernel/kernels/opencl/kernel_subsurface_scatter.cl deleted file mode 100644 index 2b3be38df84..00000000000 --- a/intern/cycles/kernel/kernels/opencl/kernel_subsurface_scatter.cl +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" -#include "kernel/split/kernel_subsurface_scatter.h" - -#define KERNEL_NAME subsurface_scatter -#include "kernel/kernels/opencl/kernel_split_function.h" -#undef KERNEL_NAME - diff --git a/intern/cycles/kernel/kernels/optix/kernel_optix.cu b/intern/cycles/kernel/kernels/optix/kernel_optix.cu deleted file mode 100644 index 7f609eab474..00000000000 --- a/intern/cycles/kernel/kernels/optix/kernel_optix.cu +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright 2019, NVIDIA Corporation. - * Copyright 2019, Blender Foundation. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// clang-format off -#include "kernel/kernel_compat_optix.h" -#include "util/util_atomic.h" -#include "kernel/kernel_types.h" -#include "kernel/kernel_globals.h" -#include "../cuda/kernel_cuda_image.h" // Texture lookup uses normal CUDA intrinsics - -#include "kernel/kernel_path.h" -#include "kernel/kernel_bake.h" -// clang-format on - -template ccl_device_forceinline T *get_payload_ptr_0() -{ - return (T *)(((uint64_t)optixGetPayload_1() << 32) | optixGetPayload_0()); -} -template ccl_device_forceinline T *get_payload_ptr_2() -{ - return (T *)(((uint64_t)optixGetPayload_3() << 32) | optixGetPayload_2()); -} - -template ccl_device_forceinline uint get_object_id() -{ -#ifdef __OBJECT_MOTION__ - // Always get the the instance ID from the TLAS - // There might be a motion transform node between TLAS and BLAS which does not have one - uint object = optixGetInstanceIdFromHandle(optixGetTransformListHandle(0)); -#else - uint object = optixGetInstanceId(); -#endif - // Choose between always returning object ID or only for instances - if (always || (object & 1) == 0) - // Can just remove the low bit since instance always contains object ID - return object >> 1; - else - // Set to OBJECT_NONE if this is not an instanced object - return OBJECT_NONE; -} - -extern "C" __global__ void __raygen__kernel_optix_path_trace() -{ - KernelGlobals kg; // Allocate stack storage for common data - - const uint3 launch_index = optixGetLaunchIndex(); - // Keep threads for same pixel together to improve occupancy of warps - uint pixel_offset = launch_index.x / __params.tile.num_samples; - uint sample_offset = launch_index.x % __params.tile.num_samples; - - kernel_path_trace(&kg, - __params.tile.buffer, - __params.tile.start_sample + sample_offset, - __params.tile.x + pixel_offset, - __params.tile.y + launch_index.y, - __params.tile.offset, - __params.tile.stride); -} - -#ifdef __BAKING__ -extern "C" __global__ void __raygen__kernel_optix_bake() -{ - KernelGlobals kg; - const ShaderParams &p = __params.shader; - kernel_bake_evaluate(&kg, - p.input, - p.output, - (ShaderEvalType)p.type, - p.filter, - p.sx + optixGetLaunchIndex().x, - p.offset, - p.sample); -} -#endif - -extern "C" __global__ void __raygen__kernel_optix_displace() -{ - KernelGlobals kg; - const ShaderParams &p = __params.shader; - kernel_displace_evaluate(&kg, p.input, p.output, p.sx + optixGetLaunchIndex().x); -} - -extern "C" __global__ void __raygen__kernel_optix_background() -{ - KernelGlobals kg; - const ShaderParams &p = __params.shader; - kernel_background_evaluate(&kg, p.input, p.output, p.sx + optixGetLaunchIndex().x); -} - -extern "C" __global__ void __miss__kernel_optix_miss() -{ - // 'kernel_path_lamp_emission' checks intersection distance, so need to set it even on a miss - optixSetPayload_0(__float_as_uint(optixGetRayTmax())); - optixSetPayload_5(PRIMITIVE_NONE); -} - -extern "C" __global__ void __anyhit__kernel_optix_local_hit() -{ -#ifdef __BVH_LOCAL__ - const uint object = get_object_id(); - if (object != optixGetPayload_4() /* local_object */) { - // Only intersect with matching object - return optixIgnoreIntersection(); - } - - const uint max_hits = optixGetPayload_5(); - if (max_hits == 0) { - // Special case for when no hit information is requested, just report that something was hit - optixSetPayload_5(true); - return optixTerminateRay(); - } - - int hit = 0; - uint *const lcg_state = get_payload_ptr_0(); - LocalIntersection *const local_isect = get_payload_ptr_2(); - - if (lcg_state) { - for (int i = min(max_hits, local_isect->num_hits) - 1; i >= 0; --i) { - if (optixGetRayTmax() == local_isect->hits[i].t) { - return optixIgnoreIntersection(); - } - } - - hit = local_isect->num_hits++; - - if (local_isect->num_hits > max_hits) { - hit = lcg_step_uint(lcg_state) % local_isect->num_hits; - if (hit >= max_hits) { - return optixIgnoreIntersection(); - } - } - } - else { - if (local_isect->num_hits && optixGetRayTmax() > local_isect->hits[0].t) { - // Record closest intersection only - // Do not terminate ray here, since there is no guarantee about distance ordering in any-hit - return optixIgnoreIntersection(); - } - - local_isect->num_hits = 1; - } - - Intersection *isect = &local_isect->hits[hit]; - isect->t = optixGetRayTmax(); - isect->prim = optixGetPrimitiveIndex(); - isect->object = get_object_id(); - isect->type = kernel_tex_fetch(__prim_type, isect->prim); - - const float2 barycentrics = optixGetTriangleBarycentrics(); - isect->u = 1.0f - barycentrics.y - barycentrics.x; - isect->v = barycentrics.x; - - // Record geometric normal - const uint tri_vindex = kernel_tex_fetch(__prim_tri_index, isect->prim); - const float3 tri_a = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 0)); - const float3 tri_b = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 1)); - const float3 tri_c = float4_to_float3(kernel_tex_fetch(__prim_tri_verts, tri_vindex + 2)); - local_isect->Ng[hit] = normalize(cross(tri_b - tri_a, tri_c - tri_a)); - - // Continue tracing (without this the trace call would return after the first hit) - optixIgnoreIntersection(); -#endif -} - -extern "C" __global__ void __anyhit__kernel_optix_shadow_all_hit() -{ -#ifdef __SHADOW_RECORD_ALL__ - const uint prim = optixGetPrimitiveIndex(); -# ifdef __VISIBILITY_FLAG__ - const uint visibility = optixGetPayload_4(); - if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { - return optixIgnoreIntersection(); - } -# endif - - // Offset into array with num_hits - Intersection *const isect = get_payload_ptr_0() + optixGetPayload_2(); - isect->t = optixGetRayTmax(); - isect->prim = prim; - isect->object = get_object_id(); - isect->type = kernel_tex_fetch(__prim_type, prim); - - if (optixIsTriangleHit()) { - const float2 barycentrics = optixGetTriangleBarycentrics(); - isect->u = 1.0f - barycentrics.y - barycentrics.x; - isect->v = barycentrics.x; - } -# ifdef __HAIR__ - else { - const float u = __uint_as_float(optixGetAttribute_0()); - isect->u = u; - isect->v = __uint_as_float(optixGetAttribute_1()); - - // Filter out curve endcaps - if (u == 0.0f || u == 1.0f) { - return optixIgnoreIntersection(); - } - } -# endif - -# ifdef __TRANSPARENT_SHADOWS__ - // Detect if this surface has a shader with transparent shadows - if (!shader_transparent_shadow(NULL, isect) || optixGetPayload_2() >= optixGetPayload_3()) { -# endif - // This is an opaque hit or the hit limit has been reached, abort traversal - optixSetPayload_5(true); - return optixTerminateRay(); -# ifdef __TRANSPARENT_SHADOWS__ - } - - optixSetPayload_2(optixGetPayload_2() + 1); // num_hits++ - - // Continue tracing - optixIgnoreIntersection(); -# endif -#endif -} - -extern "C" __global__ void __anyhit__kernel_optix_visibility_test() -{ - uint visibility = optixGetPayload_4(); -#ifdef __VISIBILITY_FLAG__ - const uint prim = optixGetPrimitiveIndex(); - if ((kernel_tex_fetch(__prim_visibility, prim) & visibility) == 0) { - return optixIgnoreIntersection(); - } -#endif - -#ifdef __HAIR__ - if (!optixIsTriangleHit()) { - // Filter out curve endcaps - const float u = __uint_as_float(optixGetAttribute_0()); - if (u == 0.0f || u == 1.0f) { - return optixIgnoreIntersection(); - } - } -#endif - - // Shadow ray early termination - if (visibility & PATH_RAY_SHADOW_OPAQUE) { - return optixTerminateRay(); - } -} - -extern "C" __global__ void __closesthit__kernel_optix_hit() -{ - optixSetPayload_0(__float_as_uint(optixGetRayTmax())); // Intersection distance - optixSetPayload_3(optixGetPrimitiveIndex()); - optixSetPayload_4(get_object_id()); - // Can be PRIMITIVE_TRIANGLE and PRIMITIVE_MOTION_TRIANGLE or curve type and segment index - optixSetPayload_5(kernel_tex_fetch(__prim_type, optixGetPrimitiveIndex())); - - if (optixIsTriangleHit()) { - const float2 barycentrics = optixGetTriangleBarycentrics(); - optixSetPayload_1(__float_as_uint(1.0f - barycentrics.y - barycentrics.x)); - optixSetPayload_2(__float_as_uint(barycentrics.x)); - } - else { - optixSetPayload_1(optixGetAttribute_0()); // Same as 'optixGetCurveParameter()' - optixSetPayload_2(optixGetAttribute_1()); - } -} - -#ifdef __HAIR__ -ccl_device_inline void optix_intersection_curve(const uint prim, const uint type) -{ - const uint object = get_object_id(); - const uint visibility = optixGetPayload_4(); - - float3 P = optixGetObjectRayOrigin(); - float3 dir = optixGetObjectRayDirection(); - - // The direction is not normalized by default, but the curve intersection routine expects that - float len; - dir = normalize_len(dir, &len); - -# ifdef __OBJECT_MOTION__ - const float time = optixGetRayTime(); -# else - const float time = 0.0f; -# endif - - Intersection isect; - isect.t = optixGetRayTmax(); - // Transform maximum distance into object space - if (isect.t != FLT_MAX) - isect.t *= len; - - if (curve_intersect(NULL, &isect, P, dir, visibility, object, prim, time, type)) { - optixReportIntersection(isect.t / len, - type & PRIMITIVE_ALL, - __float_as_int(isect.u), // Attribute_0 - __float_as_int(isect.v)); // Attribute_1 - } -} - -extern "C" __global__ void __intersection__curve_ribbon() -{ - const uint prim = optixGetPrimitiveIndex(); - const uint type = kernel_tex_fetch(__prim_type, prim); - - if (type & (PRIMITIVE_CURVE_RIBBON | PRIMITIVE_MOTION_CURVE_RIBBON)) { - optix_intersection_curve(prim, type); - } -} - -extern "C" __global__ void __intersection__curve_all() -{ - const uint prim = optixGetPrimitiveIndex(); - const uint type = kernel_tex_fetch(__prim_type, prim); - optix_intersection_curve(prim, type); -} -#endif diff --git a/intern/cycles/kernel/kernel_light_background.h b/intern/cycles/kernel/light/background.h similarity index 91% rename from intern/cycles/kernel/kernel_light_background.h rename to intern/cycles/kernel/light/background.h index f0f64ce8704..d801cc94393 100644 --- a/intern/cycles/kernel/kernel_light_background.h +++ b/intern/cycles/kernel/light/background.h @@ -14,7 +14,9 @@ * limitations under the License. */ -#include "kernel_light_common.h" +#pragma once + +#include "kernel/light/common.h" CCL_NAMESPACE_BEGIN @@ -22,7 +24,10 @@ CCL_NAMESPACE_BEGIN #ifdef __BACKGROUND_MIS__ -ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float randv, float *pdf) +ccl_device float3 background_map_sample(KernelGlobals kg, + float randu, + float randv, + ccl_private float *pdf) { /* for the following, the CDF values are actually a pair of floats, with the * function value as X and the actual CDF as Y. The last entry's function @@ -104,7 +109,7 @@ ccl_device float3 background_map_sample(KernelGlobals *kg, float randu, float ra /* TODO(sergey): Same as above, after the release we should consider using * 'noinline' for all devices. */ -ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction) +ccl_device float background_map_pdf(KernelGlobals kg, float3 direction) { float2 uv = direction_to_equirectangular(direction); int res_x = kernel_data.background.map_res_x; @@ -138,7 +143,7 @@ ccl_device float background_map_pdf(KernelGlobals *kg, float3 direction) } ccl_device_inline bool background_portal_data_fetch_and_check_side( - KernelGlobals *kg, float3 P, int index, float3 *lightpos, float3 *dir) + KernelGlobals kg, float3 P, int index, ccl_private float3 *lightpos, ccl_private float3 *dir) { int portal = kernel_data.background.portal_offset + index; const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, portal); @@ -154,7 +159,7 @@ ccl_device_inline bool background_portal_data_fetch_and_check_side( } ccl_device_inline float background_portal_pdf( - KernelGlobals *kg, float3 P, float3 direction, int ignore_portal, bool *is_possible) + KernelGlobals kg, float3 P, float3 direction, int ignore_portal, ccl_private bool *is_possible) { float portal_pdf = 0.0f; @@ -214,7 +219,7 @@ ccl_device_inline float background_portal_pdf( return (num_possible > 0) ? portal_pdf / num_possible : 0.0f; } -ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P) +ccl_device int background_num_possible_portals(KernelGlobals kg, float3 P) { int num_possible_portals = 0; for (int p = 0; p < kernel_data.background.num_portals; p++) { @@ -225,13 +230,13 @@ ccl_device int background_num_possible_portals(KernelGlobals *kg, float3 P) return num_possible_portals; } -ccl_device float3 background_portal_sample(KernelGlobals *kg, +ccl_device float3 background_portal_sample(KernelGlobals kg, float3 P, float randu, float randv, int num_possible, - int *sampled_portal, - float *pdf) + ccl_private int *sampled_portal, + ccl_private float *pdf) { /* Pick a portal, then re-normalize randv. */ randv *= num_possible; @@ -280,10 +285,10 @@ ccl_device float3 background_portal_sample(KernelGlobals *kg, return zero_float3(); } -ccl_device_inline float3 background_sun_sample(KernelGlobals *kg, +ccl_device_inline float3 background_sun_sample(KernelGlobals kg, float randu, float randv, - float *pdf) + ccl_private float *pdf) { float3 D; const float3 N = float4_to_float3(kernel_data.background.sun); @@ -292,15 +297,15 @@ ccl_device_inline float3 background_sun_sample(KernelGlobals *kg, return D; } -ccl_device_inline float background_sun_pdf(KernelGlobals *kg, float3 D) +ccl_device_inline float background_sun_pdf(KernelGlobals kg, float3 D) { const float3 N = float4_to_float3(kernel_data.background.sun); const float angle = kernel_data.background.sun.w; return pdf_uniform_cone(N, D, angle); } -ccl_device_inline float3 -background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, float *pdf) +ccl_device_inline float3 background_light_sample( + KernelGlobals kg, float3 P, float randu, float randv, ccl_private float *pdf) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; @@ -400,7 +405,7 @@ background_light_sample(KernelGlobals *kg, float3 P, float randu, float randv, f return D; } -ccl_device float background_light_pdf(KernelGlobals *kg, float3 P, float3 direction) +ccl_device float background_light_pdf(KernelGlobals kg, float3 P, float3 direction) { float portal_method_pdf = kernel_data.background.portal_weight; float sun_method_pdf = kernel_data.background.sun_weight; diff --git a/intern/cycles/kernel/kernel_light_common.h b/intern/cycles/kernel/light/common.h similarity index 94% rename from intern/cycles/kernel/kernel_light_common.h rename to intern/cycles/kernel/light/common.h index 4a683d36226..75331d32d44 100644 --- a/intern/cycles/kernel/kernel_light_common.h +++ b/intern/cycles/kernel/light/common.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/sample/mapping.h" + CCL_NAMESPACE_BEGIN /* Area light sampling */ @@ -28,7 +32,7 @@ CCL_NAMESPACE_BEGIN * Note: light_p is modified when sample_coord is true. */ ccl_device_inline float rect_light_sample(float3 P, - float3 *light_p, + ccl_private float3 *light_p, float3 axisu, float3 axisv, float randu, @@ -163,9 +167,9 @@ ccl_device float light_spread_attenuation(const float3 D, * reduce noise with low spread. */ ccl_device bool light_spread_clamp_area_light(const float3 P, const float3 lightNg, - float3 *lightP, - float3 *axisu, - float3 *axisv, + ccl_private float3 *lightP, + ccl_private float3 *axisu, + ccl_private float3 *axisv, const float tan_spread) { /* Closest point in area light plane and distance to that plane. */ @@ -210,7 +214,7 @@ ccl_device bool light_spread_clamp_area_light(const float3 P, return true; } -ccl_device float lamp_light_pdf(KernelGlobals *kg, const float3 Ng, const float3 I, float t) +ccl_device float lamp_light_pdf(KernelGlobals kg, const float3 Ng, const float3 I, float t) { float cos_pi = dot(Ng, I); diff --git a/intern/cycles/kernel/kernel_light.h b/intern/cycles/kernel/light/light.h similarity index 61% rename from intern/cycles/kernel/kernel_light.h rename to intern/cycles/kernel/light/light.h index 42a834d2ce3..746c7747569 100644 --- a/intern/cycles/kernel/kernel_light.h +++ b/intern/cycles/kernel/light/light.h @@ -14,7 +14,11 @@ * limitations under the License. */ -#include "kernel_light_background.h" +#pragma once + +#include "kernel/geom/geom.h" +#include "kernel/light/background.h" +#include "kernel/sample/mapping.h" CCL_NAMESPACE_BEGIN @@ -37,10 +41,22 @@ typedef struct LightSample { /* Regular Light */ -ccl_device_inline bool lamp_light_sample( - KernelGlobals *kg, int lamp, float randu, float randv, float3 P, LightSample *ls) +template +ccl_device_inline bool light_sample(KernelGlobals kg, + const int lamp, + const float randu, + const float randv, + const float3 P, + const uint32_t path_flag, + ccl_private LightSample *ls) { const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) { + if (klight->shader_id & SHADER_EXCLUDE_SHADOW_CATCHER) { + return false; + } + } + LightType type = (LightType)klight->type; ls->type = type; ls->shader = klight->shader_id; @@ -50,6 +66,18 @@ ccl_device_inline bool lamp_light_sample( ls->u = randu; ls->v = randv; + if (in_volume_segment && (type == LIGHT_DISTANT || type == LIGHT_BACKGROUND)) { + /* Distant lights in a volume get a dummy sample, position will not actually + * be used in that case. Only when sampling from a specific scatter position + * do we actually need to evaluate these. */ + ls->P = zero_float3(); + ls->Ng = zero_float3(); + ls->D = zero_float3(); + ls->pdf = true; + ls->t = FLT_MAX; + return true; + } + if (type == LIGHT_DISTANT) { /* distant light */ float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); @@ -123,13 +151,15 @@ ccl_device_inline bool lamp_light_sample( float invarea = fabsf(klight->area.invarea); bool is_round = (klight->area.invarea < 0.0f); - if (dot(ls->P - P, Ng) > 0.0f) { - return false; + if (!in_volume_segment) { + if (dot(ls->P - P, Ng) > 0.0f) { + return false; + } } float3 inplane; - if (is_round) { + if (is_round || in_volume_segment) { inplane = ellipse_sample(axisu * 0.5f, axisv * 0.5f, randu, randv); ls->P += inplane; ls->pdf = invarea; @@ -176,11 +206,137 @@ ccl_device_inline bool lamp_light_sample( return (ls->pdf > 0.0f); } -ccl_device bool lamp_light_eval( - KernelGlobals *kg, int lamp, float3 P, float3 D, float t, LightSample *ls) +ccl_device bool lights_intersect(KernelGlobals kg, + ccl_private const Ray *ccl_restrict ray, + ccl_private Intersection *ccl_restrict isect, + const int last_prim, + const int last_object, + const int last_type, + const uint32_t path_flag) { - const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); - LightType type = (LightType)klight->type; + for (int lamp = 0; lamp < kernel_data.integrator.num_all_lights; lamp++) { + const ccl_global KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + + if (path_flag & PATH_RAY_CAMERA) { + if (klight->shader_id & SHADER_EXCLUDE_CAMERA) { + continue; + } + } + else { + if (!(klight->shader_id & SHADER_USE_MIS)) { + continue; + } + } + + if (path_flag & PATH_RAY_SHADOW_CATCHER_PASS) { + if (klight->shader_id & SHADER_EXCLUDE_SHADOW_CATCHER) { + continue; + } + } + + LightType type = (LightType)klight->type; + float t = 0.0f, u = 0.0f, v = 0.0f; + + if (type == LIGHT_POINT || type == LIGHT_SPOT) { + /* Sphere light. */ + const float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); + const float radius = klight->spot.radius; + if (radius == 0.0f) { + continue; + } + + float3 P; + if (!ray_aligned_disk_intersect(ray->P, ray->D, ray->t, lightP, radius, &P, &t)) { + continue; + } + } + else if (type == LIGHT_AREA) { + /* Area light. */ + const float invarea = fabsf(klight->area.invarea); + const bool is_round = (klight->area.invarea < 0.0f); + if (invarea == 0.0f) { + continue; + } + + const float3 axisu = make_float3( + klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); + const float3 axisv = make_float3( + klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); + const float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); + + /* One sided. */ + if (dot(ray->D, Ng) >= 0.0f) { + continue; + } + + const float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); + + float3 P; + if (!ray_quad_intersect( + ray->P, ray->D, 0.0f, ray->t, light_P, axisu, axisv, Ng, &P, &t, &u, &v, is_round)) { + continue; + } + } + else { + continue; + } + + if (t < isect->t && + !(last_prim == lamp && last_object == OBJECT_NONE && last_type == PRIMITIVE_LAMP)) { + isect->t = t; + isect->u = u; + isect->v = v; + isect->type = PRIMITIVE_LAMP; + isect->prim = lamp; + isect->object = OBJECT_NONE; + } + } + + return isect->prim != PRIM_NONE; +} + +ccl_device bool light_sample_from_distant_ray(KernelGlobals kg, + const float3 ray_D, + const int lamp, + ccl_private LightSample *ccl_restrict ls) +{ + ccl_global const KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + const int shader = klight->shader_id; + const float radius = klight->distant.radius; + const LightType type = (LightType)klight->type; + + if (type != LIGHT_DISTANT) { + return false; + } + if (!(shader & SHADER_USE_MIS)) { + return false; + } + if (radius == 0.0f) { + return false; + } + + /* a distant light is infinitely far away, but equivalent to a disk + * shaped light exactly 1 unit away from the current shading point. + * + * radius t^2/cos(theta) + * <----------> t = sqrt(1^2 + tan(theta)^2) + * tan(th) area = radius*radius*pi + * <-----> + * \ | (1 + tan(theta)^2)/cos(theta) + * \ | (1 + tan(acos(cos(theta)))^2)/cos(theta) + * t \th| 1 simplifies to + * \-| 1/(cos(theta)^3) + * \| magic! + * P + */ + + float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); + float costheta = dot(-lightD, ray_D); + float cosangle = klight->distant.cosangle; + + if (costheta < cosangle) + return false; + ls->type = type; ls->shader = klight->shader_id; ls->object = PRIM_NONE; @@ -189,66 +345,41 @@ ccl_device bool lamp_light_eval( /* todo: missing texture coordinates */ ls->u = 0.0f; ls->v = 0.0f; + ls->t = FLT_MAX; + ls->P = -ray_D; + ls->Ng = -ray_D; + ls->D = ray_D; - if (!(ls->shader & SHADER_USE_MIS)) - return false; + /* compute pdf */ + float invarea = klight->distant.invarea; + ls->pdf = invarea / (costheta * costheta * costheta); + ls->pdf *= kernel_data.integrator.pdf_lights; + ls->eval_fac = ls->pdf; - if (type == LIGHT_DISTANT) { - /* distant light */ - float radius = klight->distant.radius; + return true; +} - if (radius == 0.0f) - return false; - if (t != FLT_MAX) - return false; +ccl_device bool light_sample_from_intersection(KernelGlobals kg, + ccl_private const Intersection *ccl_restrict isect, + const float3 ray_P, + const float3 ray_D, + ccl_private LightSample *ccl_restrict ls) +{ + const int lamp = isect->prim; + ccl_global const KernelLight *klight = &kernel_tex_fetch(__lights, lamp); + LightType type = (LightType)klight->type; + ls->type = type; + ls->shader = klight->shader_id; + ls->object = PRIM_NONE; + ls->prim = PRIM_NONE; + ls->lamp = lamp; + /* todo: missing texture coordinates */ + ls->t = isect->t; + ls->P = ray_P + ray_D * ls->t; + ls->D = ray_D; - /* a distant light is infinitely far away, but equivalent to a disk - * shaped light exactly 1 unit away from the current shading point. - * - * radius t^2/cos(theta) - * <----------> t = sqrt(1^2 + tan(theta)^2) - * tan(th) area = radius*radius*pi - * <-----> - * \ | (1 + tan(theta)^2)/cos(theta) - * \ | (1 + tan(acos(cos(theta)))^2)/cos(theta) - * t \th| 1 simplifies to - * \-| 1/(cos(theta)^3) - * \| magic! - * P - */ - - float3 lightD = make_float3(klight->co[0], klight->co[1], klight->co[2]); - float costheta = dot(-lightD, D); - float cosangle = klight->distant.cosangle; - - if (costheta < cosangle) - return false; - - ls->P = -D; - ls->Ng = -D; - ls->D = D; - ls->t = FLT_MAX; - - /* compute pdf */ - float invarea = klight->distant.invarea; - ls->pdf = invarea / (costheta * costheta * costheta); - ls->eval_fac = ls->pdf; - } - else if (type == LIGHT_POINT || type == LIGHT_SPOT) { - float3 lightP = make_float3(klight->co[0], klight->co[1], klight->co[2]); - - float radius = klight->spot.radius; - - /* sphere light */ - if (radius == 0.0f) - return false; - - if (!ray_aligned_disk_intersect(P, D, t, lightP, radius, &ls->P, &ls->t)) { - return false; - } - - ls->Ng = -D; - ls->D = D; + if (type == LIGHT_POINT || type == LIGHT_SPOT) { + ls->Ng = -ray_D; float invarea = klight->spot.invarea; ls->eval_fac = (0.25f * M_1_PI_F) * invarea; @@ -260,8 +391,9 @@ ccl_device bool lamp_light_eval( ls->eval_fac *= spot_light_attenuation( dir, klight->spot.spot_angle, klight->spot.spot_smooth, ls->Ng); - if (ls->eval_fac == 0.0f) + if (ls->eval_fac == 0.0f) { return false; + } } float2 uv = map_to_sphere(ls->Ng); ls->u = uv.x; @@ -274,31 +406,22 @@ ccl_device bool lamp_light_eval( else if (type == LIGHT_AREA) { /* area light */ float invarea = fabsf(klight->area.invarea); - bool is_round = (klight->area.invarea < 0.0f); - if (invarea == 0.0f) - return false; float3 axisu = make_float3( klight->area.axisu[0], klight->area.axisu[1], klight->area.axisu[2]); float3 axisv = make_float3( klight->area.axisv[0], klight->area.axisv[1], klight->area.axisv[2]); float3 Ng = make_float3(klight->area.dir[0], klight->area.dir[1], klight->area.dir[2]); - - /* one sided */ - if (dot(D, Ng) >= 0.0f) - return false; - float3 light_P = make_float3(klight->co[0], klight->co[1], klight->co[2]); - if (!ray_quad_intersect( - P, D, 0.0f, t, light_P, axisu, axisv, Ng, &ls->P, &ls->t, &ls->u, &ls->v, is_round)) { - return false; - } - - ls->D = D; + ls->u = isect->u; + ls->v = isect->v; + ls->D = ray_D; ls->Ng = Ng; + + const bool is_round = (klight->area.invarea < 0.0f); if (is_round) { - ls->pdf = invarea * lamp_light_pdf(kg, Ng, -D, ls->t); + ls->pdf = invarea * lamp_light_pdf(kg, Ng, -ray_D, ls->t); } else { float3 sample_axisu = axisu; @@ -306,12 +429,12 @@ ccl_device bool lamp_light_eval( if (klight->area.tan_spread > 0.0f) { if (!light_spread_clamp_area_light( - P, Ng, &light_P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) { + ray_P, Ng, &light_P, &sample_axisu, &sample_axisv, klight->area.tan_spread)) { return false; } } - ls->pdf = rect_light_sample(P, &light_P, sample_axisu, sample_axisv, 0, 0, false); + ls->pdf = rect_light_sample(ray_P, &light_P, sample_axisu, sample_axisv, 0, 0, false); } ls->eval_fac = 0.25f * invarea; @@ -325,6 +448,7 @@ ccl_device bool lamp_light_eval( } } else { + kernel_assert(!"Invalid lamp type in light_sample_from_intersection"); return false; } @@ -337,7 +461,7 @@ ccl_device bool lamp_light_eval( /* returns true if the triangle is has motion blur or an instancing transform applied */ ccl_device_inline bool triangle_world_space_vertices( - KernelGlobals *kg, int object, int prim, float time, float3 V[3]) + KernelGlobals kg, int object, int prim, float time, float3 V[3]) { bool has_motion = false; const int object_flag = kernel_tex_fetch(__object_flag, object); @@ -365,7 +489,7 @@ ccl_device_inline bool triangle_world_space_vertices( return has_motion; } -ccl_device_inline float triangle_light_pdf_area(KernelGlobals *kg, +ccl_device_inline float triangle_light_pdf_area(KernelGlobals kg, const float3 Ng, const float3 I, float t) @@ -379,7 +503,9 @@ ccl_device_inline float triangle_light_pdf_area(KernelGlobals *kg, return t * t * pdf / cos_pi; } -ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *sd, float t) +ccl_device_forceinline float triangle_light_pdf(KernelGlobals kg, + ccl_private const ShaderData *sd, + float t) { /* A naive heuristic to decide between costly solid angle sampling * and simple area sampling, comparing the distance to the triangle plane @@ -448,13 +574,14 @@ ccl_device_forceinline float triangle_light_pdf(KernelGlobals *kg, ShaderData *s } } -ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, +template +ccl_device_forceinline void triangle_light_sample(KernelGlobals kg, int prim, int object, float randu, float randv, float time, - LightSample *ls, + ccl_private LightSample *ls, const float3 P) { /* A naive heuristic to decide between costly solid angle sampling @@ -488,7 +615,7 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, float distance_to_plane = fabsf(dot(N0, V[0] - P) / dot(N0, N0)); - if (longest_edge_squared > distance_to_plane * distance_to_plane) { + if (!in_volume_segment && (longest_edge_squared > distance_to_plane * distance_to_plane)) { /* see James Arvo, "Stratified Sampling of Spherical Triangles" * http://www.graphics.cornell.edu/pubs/1995/Arv95c.pdf */ @@ -617,7 +744,7 @@ ccl_device_forceinline void triangle_light_sample(KernelGlobals *kg, /* Light Distribution */ -ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) +ccl_device int light_distribution_sample(KernelGlobals kg, ccl_private float *randu) { /* This is basically std::upper_bound as used by PBRT, to find a point light or * triangle to emit from, proportional to area. a good improvement would be to @@ -655,51 +782,91 @@ ccl_device int light_distribution_sample(KernelGlobals *kg, float *randu) /* Generic Light */ -ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals *kg, int index, int bounce) +ccl_device_inline bool light_select_reached_max_bounces(KernelGlobals kg, int index, int bounce) { return (bounce > kernel_tex_fetch(__lights, index).max_bounces); } -ccl_device_noinline bool light_sample(KernelGlobals *kg, - int lamp, - float randu, - float randv, - float time, - float3 P, - int bounce, - LightSample *ls) +template +ccl_device_noinline bool light_distribution_sample(KernelGlobals kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const uint32_t path_flag, + ccl_private LightSample *ls) { - if (lamp < 0) { - /* sample index */ - int index = light_distribution_sample(kg, &randu); + /* Sample light index from distribution. */ + const int index = light_distribution_sample(kg, &randu); + ccl_global const KernelLightDistribution *kdistribution = &kernel_tex_fetch(__light_distribution, + index); + const int prim = kdistribution->prim; - /* fetch light data */ - const ccl_global KernelLightDistribution *kdistribution = &kernel_tex_fetch( - __light_distribution, index); - int prim = kdistribution->prim; + if (prim >= 0) { + /* Mesh light. */ + const int object = kdistribution->mesh_light.object_id; - if (prim >= 0) { - int object = kdistribution->mesh_light.object_id; - int shader_flag = kdistribution->mesh_light.shader_flag; - - triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); - ls->shader |= shader_flag; - return (ls->pdf > 0.0f); + /* Exclude synthetic meshes from shadow catcher pass. */ + if ((path_flag & PATH_RAY_SHADOW_CATCHER_PASS) && + !(kernel_tex_fetch(__object_flag, object) & SD_OBJECT_SHADOW_CATCHER)) { + return false; } - lamp = -prim - 1; + const int shader_flag = kdistribution->mesh_light.shader_flag; + triangle_light_sample(kg, prim, object, randu, randv, time, ls, P); + ls->shader |= shader_flag; + return (ls->pdf > 0.0f); } + const int lamp = -prim - 1; + if (UNLIKELY(light_select_reached_max_bounces(kg, lamp, bounce))) { return false; } - return lamp_light_sample(kg, lamp, randu, randv, P, ls); + return light_sample(kg, lamp, randu, randv, P, path_flag, ls); } -ccl_device_inline int light_select_num_samples(KernelGlobals *kg, int index) +ccl_device_inline bool light_distribution_sample_from_volume_segment(KernelGlobals kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const uint32_t path_flag, + ccl_private LightSample *ls) { - return kernel_tex_fetch(__lights, index).samples; + return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); +} + +ccl_device_inline bool light_distribution_sample_from_position(KernelGlobals kg, + float randu, + const float randv, + const float time, + const float3 P, + const int bounce, + const uint32_t path_flag, + ccl_private LightSample *ls) +{ + return light_distribution_sample(kg, randu, randv, time, P, bounce, path_flag, ls); +} + +ccl_device_inline bool light_distribution_sample_new_position(KernelGlobals kg, + const float randu, + const float randv, + const float time, + const float3 P, + ccl_private LightSample *ls) +{ + /* Sample a new position on the same light, for volume sampling. */ + if (ls->type == LIGHT_TRIANGLE) { + triangle_light_sample(kg, ls->prim, ls->object, randu, randv, time, ls, P); + return (ls->pdf > 0.0f); + } + else { + return light_sample(kg, ls->lamp, randu, randv, P, 0, ls); + } } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/light/sample.h b/intern/cycles/kernel/light/sample.h new file mode 100644 index 00000000000..6b643a95250 --- /dev/null +++ b/intern/cycles/kernel/light/sample.h @@ -0,0 +1,271 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" + +#include "kernel/light/light.h" + +#include "kernel/sample/mapping.h" + +CCL_NAMESPACE_BEGIN + +/* Evaluate shader on light. */ +ccl_device_noinline_cpu float3 +light_sample_shader_eval(KernelGlobals kg, + IntegratorState state, + ccl_private ShaderData *ccl_restrict emission_sd, + ccl_private LightSample *ccl_restrict ls, + float time) +{ + /* setup shading at emitter */ + float3 eval = zero_float3(); + + if (shader_constant_emission_eval(kg, ls->shader, &eval)) { + if ((ls->prim != PRIM_NONE) && dot(ls->Ng, ls->D) > 0.0f) { + ls->Ng = -ls->Ng; + } + } + else { + /* Setup shader data and call shader_eval_surface once, better + * for GPU coherence and compile times. */ + PROFILING_INIT_FOR_SHADER(kg, PROFILING_SHADE_LIGHT_SETUP); +#ifdef __BACKGROUND_MIS__ + if (ls->type == LIGHT_BACKGROUND) { + shader_setup_from_background(kg, emission_sd, ls->P, ls->D, time); + } + else +#endif + { + shader_setup_from_sample(kg, + emission_sd, + ls->P, + ls->Ng, + -ls->D, + ls->shader, + ls->object, + ls->prim, + ls->u, + ls->v, + ls->t, + time, + false, + ls->lamp); + + ls->Ng = emission_sd->Ng; + } + + PROFILING_SHADER(emission_sd->object, emission_sd->shader); + PROFILING_EVENT(PROFILING_SHADE_LIGHT_EVAL); + + /* No proper path flag, we're evaluating this for all closures. that's + * weak but we'd have to do multiple evaluations otherwise. */ + shader_eval_surface( + kg, state, emission_sd, NULL, PATH_RAY_EMISSION); + + /* Evaluate closures. */ +#ifdef __BACKGROUND_MIS__ + if (ls->type == LIGHT_BACKGROUND) { + eval = shader_background_eval(emission_sd); + } + else +#endif + { + eval = shader_emissive_eval(emission_sd); + } + } + + eval *= ls->eval_fac; + + if (ls->lamp != LAMP_NONE) { + ccl_global const KernelLight *klight = &kernel_tex_fetch(__lights, ls->lamp); + eval *= make_float3(klight->strength[0], klight->strength[1], klight->strength[2]); + } + + return eval; +} + +/* Test if light sample is from a light or emission from geometry. */ +ccl_device_inline bool light_sample_is_light(ccl_private const LightSample *ccl_restrict ls) +{ + /* return if it's a lamp for shadow pass */ + return (ls->prim == PRIM_NONE && ls->type != LIGHT_BACKGROUND); +} + +/* Early path termination of shadow rays. */ +ccl_device_inline bool light_sample_terminate(KernelGlobals kg, + ccl_private const LightSample *ccl_restrict ls, + ccl_private BsdfEval *ccl_restrict eval, + const float rand_terminate) +{ + if (bsdf_eval_is_zero(eval)) { + return true; + } + + if (kernel_data.integrator.light_inv_rr_threshold > 0.0f) { + float probability = max3(fabs(bsdf_eval_sum(eval))) * + kernel_data.integrator.light_inv_rr_threshold; + if (probability < 1.0f) { + if (rand_terminate >= probability) { + return true; + } + bsdf_eval_mul(eval, 1.0f / probability); + } + } + + return false; +} + +/* This function should be used to compute a modified ray start position for + * rays leaving from a surface. The algorithm slightly distorts flat surface + * of a triangle. Surface is lifted by amount h along normal n in the incident + * point. */ + +ccl_device_inline float3 shadow_ray_smooth_surface_offset( + KernelGlobals kg, ccl_private const ShaderData *ccl_restrict sd, float3 Ng) +{ + float3 V[3], N[3]; + triangle_vertices_and_normals(kg, sd->prim, V, N); + + const float u = sd->u, v = sd->v; + const float w = 1 - u - v; + float3 P = V[0] * u + V[1] * v + V[2] * w; /* Local space */ + float3 n = N[0] * u + N[1] * v + N[2] * w; /* We get away without normalization */ + + object_normal_transform(kg, sd, &n); /* Normal x scale, world space */ + + /* Parabolic approximation */ + float a = dot(N[2] - N[0], V[0] - V[2]); + float b = dot(N[2] - N[1], V[1] - V[2]); + float c = dot(N[1] - N[0], V[1] - V[0]); + float h = a * u * (u - 1) + (a + b + c) * u * v + b * v * (v - 1); + + /* Check flipped normals */ + if (dot(n, Ng) > 0) { + /* Local linear envelope */ + float h0 = max(max(dot(V[1] - V[0], N[0]), dot(V[2] - V[0], N[0])), 0.0f); + float h1 = max(max(dot(V[0] - V[1], N[1]), dot(V[2] - V[1], N[1])), 0.0f); + float h2 = max(max(dot(V[0] - V[2], N[2]), dot(V[1] - V[2], N[2])), 0.0f); + h0 = max(dot(V[0] - P, N[0]) + h0, 0.0f); + h1 = max(dot(V[1] - P, N[1]) + h1, 0.0f); + h2 = max(dot(V[2] - P, N[2]) + h2, 0.0f); + h = max(min(min(h0, h1), h2), h * 0.5f); + } + else { + float h0 = max(max(dot(V[0] - V[1], N[0]), dot(V[0] - V[2], N[0])), 0.0f); + float h1 = max(max(dot(V[1] - V[0], N[1]), dot(V[1] - V[2], N[1])), 0.0f); + float h2 = max(max(dot(V[2] - V[0], N[2]), dot(V[2] - V[1], N[2])), 0.0f); + h0 = max(dot(P - V[0], N[0]) + h0, 0.0f); + h1 = max(dot(P - V[1], N[1]) + h1, 0.0f); + h2 = max(dot(P - V[2], N[2]) + h2, 0.0f); + h = min(-min(min(h0, h1), h2), h * 0.5f); + } + + return n * h; +} + +/* Ray offset to avoid shadow terminator artifact. */ + +ccl_device_inline float3 shadow_ray_offset(KernelGlobals kg, + ccl_private const ShaderData *ccl_restrict sd, + float3 L) +{ + float NL = dot(sd->N, L); + bool transmit = (NL < 0.0f); + float3 Ng = (transmit ? -sd->Ng : sd->Ng); + float3 P = ray_offset(sd->P, Ng); + + if ((sd->type & PRIMITIVE_ALL_TRIANGLE) && (sd->shader & SHADER_SMOOTH_NORMAL)) { + const float offset_cutoff = + kernel_tex_fetch(__objects, sd->object).shadow_terminator_geometry_offset; + /* Do ray offset (heavy stuff) only for close to be terminated triangles: + * offset_cutoff = 0.1f means that 10-20% of rays will be affected. Also + * make a smooth transition near the threshold. */ + if (offset_cutoff > 0.0f) { + float NgL = dot(Ng, L); + float offset_amount = 0.0f; + if (NL < offset_cutoff) { + offset_amount = clamp(2.0f - (NgL + NL) / offset_cutoff, 0.0f, 1.0f); + } + else { + offset_amount = clamp(1.0f - NgL / offset_cutoff, 0.0f, 1.0f); + } + if (offset_amount > 0.0f) { + P += shadow_ray_smooth_surface_offset(kg, sd, Ng) * offset_amount; + } + } + } + + return P; +} + +ccl_device_inline void shadow_ray_setup(ccl_private const ShaderData *ccl_restrict sd, + ccl_private const LightSample *ccl_restrict ls, + const float3 P, + ccl_private Ray *ray) +{ + if (ls->shader & SHADER_CAST_SHADOW) { + /* setup ray */ + ray->P = P; + + if (ls->t == FLT_MAX) { + /* distant light */ + ray->D = ls->D; + ray->t = ls->t; + } + else { + /* other lights, avoid self-intersection */ + ray->D = ray_offset(ls->P, ls->Ng) - P; + ray->D = normalize_len(ray->D, &ray->t); + } + } + else { + /* signal to not cast shadow ray */ + ray->P = zero_float3(); + ray->D = zero_float3(); + ray->t = 0.0f; + } + + ray->dP = differential_make_compact(sd->dP); + ray->dD = differential_zero_compact(); + ray->time = sd->time; +} + +/* Create shadow ray towards light sample. */ +ccl_device_inline void light_sample_to_surface_shadow_ray( + KernelGlobals kg, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const LightSample *ccl_restrict ls, + ccl_private Ray *ray) +{ + const float3 P = shadow_ray_offset(kg, sd, ls->D); + shadow_ray_setup(sd, ls, P, ray); +} + +/* Create shadow ray towards light sample. */ +ccl_device_inline void light_sample_to_volume_shadow_ray( + KernelGlobals kg, + ccl_private const ShaderData *ccl_restrict sd, + ccl_private const LightSample *ccl_restrict ls, + const float3 P, + ccl_private Ray *ray) +{ + shadow_ray_setup(sd, ls, P, ray); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/osl/CMakeLists.txt b/intern/cycles/kernel/osl/CMakeLists.txt index 6cdc7367fbb..f226c95766f 100644 --- a/intern/cycles/kernel/osl/CMakeLists.txt +++ b/intern/cycles/kernel/osl/CMakeLists.txt @@ -25,21 +25,21 @@ set(SRC bsdf_diffuse_ramp.cpp bsdf_phong_ramp.cpp emissive.cpp - osl_bssrdf.cpp - osl_closures.cpp - osl_services.cpp - osl_shader.cpp + bssrdf.cpp + closures.cpp + services.cpp + shader.cpp ) set(HEADER_SRC - osl_closures.h - osl_globals.h - osl_services.h - osl_shader.h + closures.h + globals.h + services.h + shader.h ) set(LIB - cycles_render + cycles_scene ${OSL_LIBRARIES} ${OPENIMAGEIO_LIBRARIES} diff --git a/intern/cycles/kernel/osl/background.cpp b/intern/cycles/kernel/osl/background.cpp index 3f9de5ab33d..540180f99e8 100644 --- a/intern/cycles/kernel/osl/background.cpp +++ b/intern/cycles/kernel/osl/background.cpp @@ -34,10 +34,10 @@ #include -#include "kernel/osl/osl_closures.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_compat_cpu.h" +#include "kernel/device/cpu/compat.h" #include "kernel/closure/alloc.h" #include "kernel/closure/emissive.h" // clang-format on @@ -54,7 +54,7 @@ using namespace OSL; /// class GenericBackgroundClosure : public CClosurePrimitive { public: - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { background_setup(sd, weight); } @@ -69,7 +69,7 @@ class GenericBackgroundClosure : public CClosurePrimitive { /// class HoldoutClosure : CClosurePrimitive { public: - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { closure_alloc(sd, sizeof(ShaderClosure), CLOSURE_HOLDOUT_ID, weight); sd->flag |= SD_HOLDOUT; diff --git a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp index 76a2e41abfa..768531a0bf9 100644 --- a/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_diffuse_ramp.cpp @@ -34,12 +34,11 @@ #include -#include "kernel/kernel_compat_cpu.h" -#include "kernel/osl/osl_closures.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_types.h" -#include "kernel/kernel_montecarlo.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_diffuse_ramp.h" // clang-format on @@ -53,7 +52,7 @@ class DiffuseRampClosure : public CBSDFClosure { DiffuseRampBsdf params; Color3 colors[8]; - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { DiffuseRampBsdf *bsdf = (DiffuseRampBsdf *)bsdf_alloc_osl( sd, sizeof(DiffuseRampBsdf), weight, ¶ms); diff --git a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp index b78dc8a3a67..d34a33216a0 100644 --- a/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp +++ b/intern/cycles/kernel/osl/bsdf_phong_ramp.cpp @@ -34,11 +34,11 @@ #include -#include "kernel/kernel_compat_cpu.h" -#include "kernel/osl/osl_closures.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_phong_ramp.h" // clang-format on @@ -52,7 +52,7 @@ class PhongRampClosure : public CBSDFClosure { PhongRampBsdf params; Color3 colors[8]; - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { PhongRampBsdf *bsdf = (PhongRampBsdf *)bsdf_alloc_osl( sd, sizeof(PhongRampBsdf), weight, ¶ms); diff --git a/intern/cycles/kernel/osl/osl_bssrdf.cpp b/intern/cycles/kernel/osl/bssrdf.cpp similarity index 72% rename from intern/cycles/kernel/osl/osl_bssrdf.cpp rename to intern/cycles/kernel/osl/bssrdf.cpp index c5ca8616fbd..7c7f1ce157f 100644 --- a/intern/cycles/kernel/osl/osl_bssrdf.cpp +++ b/intern/cycles/kernel/osl/bssrdf.cpp @@ -32,12 +32,11 @@ #include -#include "kernel/kernel_compat_cpu.h" -#include "kernel/osl/osl_closures.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_types.h" -#include "kernel/kernel_montecarlo.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_util.h" @@ -50,48 +49,37 @@ CCL_NAMESPACE_BEGIN using namespace OSL; -static ustring u_cubic("cubic"); -static ustring u_gaussian("gaussian"); static ustring u_burley("burley"); -static ustring u_principled("principled"); +static ustring u_random_walk_fixed_radius("random_walk_fixed_radius"); static ustring u_random_walk("random_walk"); -static ustring u_principled_random_walk("principled_random_walk"); class CBSSRDFClosure : public CClosurePrimitive { public: Bssrdf params; + float ior; ustring method; CBSSRDFClosure() { - params.texture_blur = 0.0f; - params.sharpness = 0.0f; - params.roughness = 0.0f; + params.roughness = FLT_MAX; + params.anisotropy = 1.0f; + ior = 1.4f; } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { - if (method == u_cubic) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_CUBIC_ID); - } - else if (method == u_gaussian) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_GAUSSIAN_ID); - } - else if (method == u_burley) { + if (method == u_burley) { alloc(sd, path_flag, weight, CLOSURE_BSSRDF_BURLEY_ID); } - else if (method == u_principled) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_PRINCIPLED_ID); + else if (method == u_random_walk_fixed_radius) { + alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); } else if (method == u_random_walk) { alloc(sd, path_flag, weight, CLOSURE_BSSRDF_RANDOM_WALK_ID); } - else if (method == u_principled_random_walk) { - alloc(sd, path_flag, weight, CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID); - } } - void alloc(ShaderData *sd, int path_flag, float3 weight, ClosureType type) + void alloc(ShaderData *sd, uint32_t path_flag, float3 weight, ClosureType type) { Bssrdf *bssrdf = bssrdf_alloc(sd, weight); @@ -106,11 +94,10 @@ class CBSSRDFClosure : public CClosurePrimitive { /* create one closure per color channel */ bssrdf->radius = params.radius; bssrdf->albedo = params.albedo; - bssrdf->texture_blur = params.texture_blur; - bssrdf->sharpness = params.sharpness; bssrdf->N = params.N; bssrdf->roughness = params.roughness; - sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type); + bssrdf->anisotropy = clamp(params.anisotropy, 0.0f, 0.9f); + sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type, clamp(ior, 1.01f, 3.8f)); } } }; @@ -122,9 +109,9 @@ ClosureParam *closure_bssrdf_params() CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.N), CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.radius), CLOSURE_FLOAT3_PARAM(CBSSRDFClosure, params.albedo), - CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.texture_blur, "texture_blur"), - CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.sharpness, "sharpness"), CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.roughness, "roughness"), + CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, ior, "ior"), + CLOSURE_FLOAT_KEYPARAM(CBSSRDFClosure, params.anisotropy, "anisotropy"), CLOSURE_STRING_KEYPARAM(CBSSRDFClosure, label, "label"), CLOSURE_FINISH_PARAM(CBSSRDFClosure)}; return params; diff --git a/intern/cycles/kernel/osl/osl_closures.cpp b/intern/cycles/kernel/osl/closures.cpp similarity index 77% rename from intern/cycles/kernel/osl/osl_closures.cpp rename to intern/cycles/kernel/osl/closures.cpp index 7ee467a46dd..adc0f50aefb 100644 --- a/intern/cycles/kernel/osl/osl_closures.cpp +++ b/intern/cycles/kernel/osl/closures.cpp @@ -33,19 +33,17 @@ #include #include -#include "kernel/osl/osl_closures.h" -#include "kernel/osl/osl_shader.h" +#include "kernel/osl/closures.h" +#include "kernel/osl/shader.h" -#include "util/util_math.h" -#include "util/util_param.h" +#include "util/math.h" +#include "util/param.h" // clang-format off -#include "kernel/kernel_types.h" -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_random.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/bsdf_util.h" @@ -73,137 +71,137 @@ using namespace OSL; /* BSDF class definitions */ BSDF_CLOSURE_CLASS_BEGIN(Diffuse, diffuse, DiffuseBsdf, LABEL_DIFFUSE) -CLOSURE_FLOAT3_PARAM(DiffuseClosure, params.N), - BSDF_CLOSURE_CLASS_END(Diffuse, diffuse) + BSDF_CLOSURE_FLOAT3_PARAM(DiffuseClosure, params.N) +BSDF_CLOSURE_CLASS_END(Diffuse, diffuse) - BSDF_CLOSURE_CLASS_BEGIN(Translucent, translucent, DiffuseBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(TranslucentClosure, params.N), - BSDF_CLOSURE_CLASS_END(Translucent, translucent) +BSDF_CLOSURE_CLASS_BEGIN(Translucent, translucent, DiffuseBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(TranslucentClosure, params.N) +BSDF_CLOSURE_CLASS_END(Translucent, translucent) - BSDF_CLOSURE_CLASS_BEGIN(OrenNayar, oren_nayar, OrenNayarBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(OrenNayarClosure, params.N), - CLOSURE_FLOAT_PARAM(OrenNayarClosure, params.roughness), - BSDF_CLOSURE_CLASS_END(OrenNayar, oren_nayar) +BSDF_CLOSURE_CLASS_BEGIN(OrenNayar, oren_nayar, OrenNayarBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(OrenNayarClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(OrenNayarClosure, params.roughness) +BSDF_CLOSURE_CLASS_END(OrenNayar, oren_nayar) - BSDF_CLOSURE_CLASS_BEGIN(Reflection, reflection, MicrofacetBsdf, LABEL_SINGULAR) - CLOSURE_FLOAT3_PARAM(ReflectionClosure, params.N), - BSDF_CLOSURE_CLASS_END(Reflection, reflection) +BSDF_CLOSURE_CLASS_BEGIN(Reflection, reflection, MicrofacetBsdf, LABEL_SINGULAR) + BSDF_CLOSURE_FLOAT3_PARAM(ReflectionClosure, params.N) +BSDF_CLOSURE_CLASS_END(Reflection, reflection) - BSDF_CLOSURE_CLASS_BEGIN(Refraction, refraction, MicrofacetBsdf, LABEL_SINGULAR) - CLOSURE_FLOAT3_PARAM(RefractionClosure, params.N), - CLOSURE_FLOAT_PARAM(RefractionClosure, params.ior), - BSDF_CLOSURE_CLASS_END(Refraction, refraction) +BSDF_CLOSURE_CLASS_BEGIN(Refraction, refraction, MicrofacetBsdf, LABEL_SINGULAR) + BSDF_CLOSURE_FLOAT3_PARAM(RefractionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(RefractionClosure, params.ior) +BSDF_CLOSURE_CLASS_END(Refraction, refraction) - BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, VelvetBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(AshikhminVelvetClosure, params.N), - CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, params.sigma), - BSDF_CLOSURE_CLASS_END(AshikhminVelvet, ashikhmin_velvet) +BSDF_CLOSURE_CLASS_BEGIN(AshikhminVelvet, ashikhmin_velvet, VelvetBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(AshikhminVelvetClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(AshikhminVelvetClosure, params.sigma) +BSDF_CLOSURE_CLASS_END(AshikhminVelvet, ashikhmin_velvet) - BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, - ashikhmin_shirley, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.N), - CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.T), - CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_y), - BSDF_CLOSURE_CLASS_END(AshikhminShirley, ashikhmin_shirley) +BSDF_CLOSURE_CLASS_BEGIN(AshikhminShirley, + ashikhmin_shirley, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.N) + BSDF_CLOSURE_FLOAT3_PARAM(AshikhminShirleyClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(AshikhminShirleyClosure, params.alpha_y) +BSDF_CLOSURE_CLASS_END(AshikhminShirley, ashikhmin_shirley) - BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, ToonBsdf, LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, params.N), - CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.size), - CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.smooth), - BSDF_CLOSURE_CLASS_END(DiffuseToon, diffuse_toon) +BSDF_CLOSURE_CLASS_BEGIN(DiffuseToon, diffuse_toon, ToonBsdf, LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(DiffuseToonClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.size) + BSDF_CLOSURE_FLOAT_PARAM(DiffuseToonClosure, params.smooth) +BSDF_CLOSURE_CLASS_END(DiffuseToon, diffuse_toon) - BSDF_CLOSURE_CLASS_BEGIN(GlossyToon, glossy_toon, ToonBsdf, LABEL_GLOSSY) - CLOSURE_FLOAT3_PARAM(GlossyToonClosure, params.N), - CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.size), - CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.smooth), - BSDF_CLOSURE_CLASS_END(GlossyToon, glossy_toon) +BSDF_CLOSURE_CLASS_BEGIN(GlossyToon, glossy_toon, ToonBsdf, LABEL_GLOSSY) + BSDF_CLOSURE_FLOAT3_PARAM(GlossyToonClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.size) + BSDF_CLOSURE_FLOAT_PARAM(GlossyToonClosure, params.smooth) +BSDF_CLOSURE_CLASS_END(GlossyToon, glossy_toon) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXIsotropic, - microfacet_ggx_isotropic, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetGGXIsotropicClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetGGXIsotropicClosure, params.alpha_x), - BSDF_CLOSURE_CLASS_END(MicrofacetGGXIsotropic, microfacet_ggx_isotropic) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXIsotropic, + microfacet_ggx_isotropic, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXIsotropicClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXIsotropicClosure, params.alpha_x) +BSDF_CLOSURE_CLASS_END(MicrofacetGGXIsotropic, microfacet_ggx_isotropic) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, - microfacet_ggx, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_y), - BSDF_CLOSURE_CLASS_END(MicrofacetGGX, microfacet_ggx) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGX, + microfacet_ggx, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.N) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXClosure, params.alpha_y) +BSDF_CLOSURE_CLASS_END(MicrofacetGGX, microfacet_ggx) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannIsotropic, - microfacet_beckmann_isotropic, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannIsotropicClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannIsotropicClosure, params.alpha_x), - BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannIsotropic, microfacet_beckmann_isotropic) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannIsotropic, + microfacet_beckmann_isotropic, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannIsotropicClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannIsotropicClosure, params.alpha_x) +BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannIsotropic, microfacet_beckmann_isotropic) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, - microfacet_beckmann, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_REFLECT) - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.N), - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.T), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_y), - BSDF_CLOSURE_CLASS_END(MicrofacetBeckmann, microfacet_beckmann) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmann, + microfacet_beckmann, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_REFLECT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.N) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannClosure, params.alpha_y) +BSDF_CLOSURE_CLASS_END(MicrofacetBeckmann, microfacet_beckmann) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, - microfacet_ggx_refraction, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_TRANSMIT) - CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.ior), - BSDF_CLOSURE_CLASS_END(MicrofacetGGXRefraction, microfacet_ggx_refraction) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetGGXRefraction, + microfacet_ggx_refraction, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_TRANSMIT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetGGXRefractionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetGGXRefractionClosure, params.ior) +BSDF_CLOSURE_CLASS_END(MicrofacetGGXRefraction, microfacet_ggx_refraction) - BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannRefraction, - microfacet_beckmann_refraction, - MicrofacetBsdf, - LABEL_GLOSSY | LABEL_TRANSMIT) - CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannRefractionClosure, params.N), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.alpha_x), - CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.ior), - BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction) +BSDF_CLOSURE_CLASS_BEGIN(MicrofacetBeckmannRefraction, + microfacet_beckmann_refraction, + MicrofacetBsdf, + LABEL_GLOSSY | LABEL_TRANSMIT) + BSDF_CLOSURE_FLOAT3_PARAM(MicrofacetBeckmannRefractionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.alpha_x) + BSDF_CLOSURE_FLOAT_PARAM(MicrofacetBeckmannRefractionClosure, params.ior) +BSDF_CLOSURE_CLASS_END(MicrofacetBeckmannRefraction, microfacet_beckmann_refraction) - BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, HairBsdf, LABEL_GLOSSY) - CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.N), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness1), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness2), - CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset), - BSDF_CLOSURE_CLASS_END(HairReflection, hair_reflection) +BSDF_CLOSURE_CLASS_BEGIN(HairReflection, hair_reflection, HairBsdf, LABEL_GLOSSY) + BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness1) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.roughness2) + BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset) +BSDF_CLOSURE_CLASS_END(HairReflection, hair_reflection) - BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, HairBsdf, LABEL_GLOSSY) - CLOSURE_FLOAT3_PARAM(HairTransmissionClosure, params.N), - CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness1), - CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness2), - CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T), - CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset), - BSDF_CLOSURE_CLASS_END(HairTransmission, hair_transmission) +BSDF_CLOSURE_CLASS_BEGIN(HairTransmission, hair_transmission, HairBsdf, LABEL_GLOSSY) + BSDF_CLOSURE_FLOAT3_PARAM(HairTransmissionClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness1) + BSDF_CLOSURE_FLOAT_PARAM(HairTransmissionClosure, params.roughness2) + BSDF_CLOSURE_FLOAT3_PARAM(HairReflectionClosure, params.T) + BSDF_CLOSURE_FLOAT_PARAM(HairReflectionClosure, params.offset) +BSDF_CLOSURE_CLASS_END(HairTransmission, hair_transmission) - BSDF_CLOSURE_CLASS_BEGIN(PrincipledDiffuse, - principled_diffuse, - PrincipledDiffuseBsdf, - LABEL_DIFFUSE) - CLOSURE_FLOAT3_PARAM(PrincipledDiffuseClosure, params.N), - CLOSURE_FLOAT_PARAM(PrincipledDiffuseClosure, params.roughness), - BSDF_CLOSURE_CLASS_END(PrincipledDiffuse, principled_diffuse) +BSDF_CLOSURE_CLASS_BEGIN(PrincipledDiffuse, + principled_diffuse, + PrincipledDiffuseBsdf, + LABEL_DIFFUSE) + BSDF_CLOSURE_FLOAT3_PARAM(PrincipledDiffuseClosure, params.N) + BSDF_CLOSURE_FLOAT_PARAM(PrincipledDiffuseClosure, params.roughness) +BSDF_CLOSURE_CLASS_END(PrincipledDiffuse, principled_diffuse) - class PrincipledSheenClosure : public CBSDFClosure { +class PrincipledSheenClosure : public CBSDFClosure { public: PrincipledSheenBsdf params; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { if (!skip(sd, path_flag, LABEL_DIFFUSE)) { PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)bsdf_alloc_osl( @@ -228,7 +226,7 @@ class PrincipledHairClosure : public CBSDFClosure { public: PrincipledHairBSDF params; - PrincipledHairBSDF *alloc(ShaderData *sd, int path_flag, float3 weight) + PrincipledHairBSDF *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)bsdf_alloc_osl( sd, sizeof(PrincipledHairBSDF), weight, ¶ms); @@ -246,7 +244,7 @@ class PrincipledHairClosure : public CBSDFClosure { return bsdf; } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { if (!skip(sd, path_flag, LABEL_GLOSSY)) { PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)alloc(sd, path_flag, weight); @@ -282,7 +280,7 @@ class PrincipledClearcoatClosure : public CBSDFClosure { MicrofacetBsdf params; float clearcoat, clearcoat_roughness; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc_osl( sd, sizeof(MicrofacetBsdf), weight, ¶ms); @@ -306,7 +304,7 @@ class PrincipledClearcoatClosure : public CBSDFClosure { return bsdf; } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -496,11 +494,11 @@ void OSLShader::register_closures(OSLShadingSystem *ss_) /* BSDF Closure */ -bool CBSDFClosure::skip(const ShaderData *sd, int path_flag, int scattering) +bool CBSDFClosure::skip(const ShaderData *sd, uint32_t path_flag, int scattering) { /* caustic options */ if ((scattering & LABEL_GLOSSY) && (path_flag & PATH_RAY_DIFFUSE)) { - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if ((!kernel_data.integrator.caustics_reflective && (scattering & LABEL_REFLECT)) || (!kernel_data.integrator.caustics_refractive && (scattering & LABEL_TRANSMIT))) { @@ -519,7 +517,7 @@ class MicrofacetClosure : public CBSDFClosure { ustring distribution; int refract; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { static ustring u_ggx("ggx"); static ustring u_default("default"); @@ -595,7 +593,7 @@ class MicrofacetFresnelClosure : public CBSDFClosure { float3 color; float3 cspec0; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { /* Technically, the MultiGGX Glass closure may also transmit. However, * since this is set statically and only used for caustic flags, this @@ -625,7 +623,7 @@ class MicrofacetFresnelClosure : public CBSDFClosure { class MicrofacetGGXFresnelClosure : public MicrofacetFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -654,7 +652,7 @@ CCLOSURE_PREPARE(closure_bsdf_microfacet_ggx_fresnel_prepare, MicrofacetGGXFresn class MicrofacetGGXAnisoFresnelClosure : public MicrofacetFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -689,7 +687,7 @@ class MicrofacetMultiClosure : public CBSDFClosure { MicrofacetBsdf params; float3 color; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { /* Technically, the MultiGGX closure may also transmit. However, * since this is set statically and only used for caustic flags, this @@ -719,7 +717,7 @@ class MicrofacetMultiClosure : public CBSDFClosure { class MicrofacetMultiGGXClosure : public MicrofacetMultiClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -747,7 +745,7 @@ CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_prepare, MicrofacetMultiGGXCl class MicrofacetMultiGGXAnisoClosure : public MicrofacetMultiClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -779,7 +777,7 @@ class MicrofacetMultiGGXGlassClosure : public MicrofacetMultiClosure { { } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -813,7 +811,7 @@ class MicrofacetMultiFresnelClosure : public CBSDFClosure { float3 color; float3 cspec0; - MicrofacetBsdf *alloc(ShaderData *sd, int path_flag, float3 weight) + MicrofacetBsdf *alloc(ShaderData *sd, uint32_t path_flag, float3 weight) { /* Technically, the MultiGGX closure may also transmit. However, * since this is set statically and only used for caustic flags, this @@ -843,7 +841,7 @@ class MicrofacetMultiFresnelClosure : public CBSDFClosure { class MicrofacetMultiGGXFresnelClosure : public MicrofacetMultiFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -873,7 +871,7 @@ CCLOSURE_PREPARE(closure_bsdf_microfacet_multi_ggx_fresnel_prepare, class MicrofacetMultiGGXAnisoFresnelClosure : public MicrofacetMultiFresnelClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -907,7 +905,7 @@ class MicrofacetMultiGGXGlassFresnelClosure : public MicrofacetMultiFresnelClosu { } - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { MicrofacetBsdf *bsdf = alloc(sd, path_flag, weight); if (!bsdf) { @@ -942,7 +940,7 @@ class TransparentClosure : public CBSDFClosure { ShaderClosure params; float3 unused; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { bsdf_transparent_setup(sd, weight, path_flag); } @@ -961,7 +959,7 @@ CCLOSURE_PREPARE(closure_bsdf_transparent_prepare, TransparentClosure) class VolumeAbsorptionClosure : public CBSDFClosure { public: - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { volume_extinction_setup(sd, weight); } @@ -980,7 +978,7 @@ class VolumeHenyeyGreensteinClosure : public CBSDFClosure { public: HenyeyGreensteinVolume params; - void setup(ShaderData *sd, int path_flag, float3 weight) + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) { volume_extinction_setup(sd, weight); diff --git a/intern/cycles/kernel/osl/osl_closures.h b/intern/cycles/kernel/osl/closures.h similarity index 94% rename from intern/cycles/kernel/osl/osl_closures.h rename to intern/cycles/kernel/osl/closures.h index e4058e3a746..8f573e89734 100644 --- a/intern/cycles/kernel/osl/osl_closures.h +++ b/intern/cycles/kernel/osl/closures.h @@ -33,8 +33,8 @@ #ifndef __OSL_CLOSURES_H__ #define __OSL_CLOSURES_H__ -#include "kernel/kernel_types.h" -#include "util/util_types.h" +#include "kernel/types.h" +#include "util/types.h" #include #include @@ -105,6 +105,9 @@ void closure_bsdf_principled_hair_prepare(OSL::RendererServices *, int id, void TypeDesc::TypeVector, (int)reckless_offsetof(st, fld), NULL, sizeof(OSL::Vec3) \ } +#define BSDF_CLOSURE_FLOAT_PARAM(st, fld) CLOSURE_FLOAT_PARAM(st, fld), +#define BSDF_CLOSURE_FLOAT3_PARAM(st, fld) CLOSURE_FLOAT3_PARAM(st, fld), + #define TO_VEC3(v) OSL::Vec3(v.x, v.y, v.z) #define TO_COLOR3(v) OSL::Color3(v.x, v.y, v.z) #define TO_FLOAT3(v) make_float3(v[0], v[1], v[2]) @@ -113,7 +116,7 @@ void closure_bsdf_principled_hair_prepare(OSL::RendererServices *, int id, void class CClosurePrimitive { public: - virtual void setup(ShaderData *sd, int path_flag, float3 weight) = 0; + virtual void setup(ShaderData *sd, uint32_t path_flag, float3 weight) = 0; OSL::ustring label; }; @@ -122,7 +125,7 @@ class CClosurePrimitive { class CBSDFClosure : public CClosurePrimitive { public: - bool skip(const ShaderData *sd, int path_flag, int scattering); + bool skip(const ShaderData *sd, uint32_t path_flag, int scattering); }; #define BSDF_CLOSURE_CLASS_BEGIN(Upper, lower, structname, TYPE) \ @@ -132,7 +135,7 @@ class CBSDFClosure : public CClosurePrimitive { structname params; \ float3 unused; \ \ - void setup(ShaderData *sd, int path_flag, float3 weight) \ + void setup(ShaderData *sd, uint32_t path_flag, float3 weight) \ { \ if (!skip(sd, path_flag, TYPE)) { \ structname *bsdf = (structname *)bsdf_alloc_osl(sd, sizeof(structname), weight, ¶ms); \ diff --git a/intern/cycles/kernel/osl/emissive.cpp b/intern/cycles/kernel/osl/emissive.cpp index d656723bac2..2615e300a92 100644 --- a/intern/cycles/kernel/osl/emissive.cpp +++ b/intern/cycles/kernel/osl/emissive.cpp @@ -34,11 +34,11 @@ #include -#include "kernel/osl/osl_closures.h" +#include "kernel/osl/closures.h" // clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/kernel_types.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/types.h" #include "kernel/closure/alloc.h" #include "kernel/closure/emissive.h" // clang-format on @@ -56,7 +56,7 @@ using namespace OSL; /// class GenericEmissiveClosure : public CClosurePrimitive { public: - void setup(ShaderData *sd, int /* path_flag */, float3 weight) + void setup(ShaderData *sd, uint32_t /* path_flag */, float3 weight) { emission_setup(sd, weight); } diff --git a/intern/cycles/kernel/osl/osl_globals.h b/intern/cycles/kernel/osl/globals.h similarity index 93% rename from intern/cycles/kernel/osl/osl_globals.h rename to intern/cycles/kernel/osl/globals.h index f1789f0d7eb..126ace0086e 100644 --- a/intern/cycles/kernel/osl/osl_globals.h +++ b/intern/cycles/kernel/osl/globals.h @@ -24,11 +24,11 @@ # include # include -# include "util/util_map.h" -# include "util/util_param.h" -# include "util/util_thread.h" -# include "util/util_unique_ptr.h" -# include "util/util_vector.h" +# include "util/map.h" +# include "util/param.h" +# include "util/thread.h" +# include "util/unique_ptr.h" +# include "util/vector.h" # ifndef WIN32 using std::isfinite; diff --git a/intern/cycles/kernel/osl/osl_services.cpp b/intern/cycles/kernel/osl/services.cpp similarity index 88% rename from intern/cycles/kernel/osl/osl_services.cpp rename to intern/cycles/kernel/osl/services.cpp index 2b7c21d0bc4..ca0a5a068b3 100644 --- a/intern/cycles/kernel/osl/osl_services.cpp +++ b/intern/cycles/kernel/osl/services.cpp @@ -25,38 +25,41 @@ #include -#include "render/colorspace.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/colorspace.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" -#include "kernel/osl/osl_closures.h" -#include "kernel/osl/osl_globals.h" -#include "kernel/osl/osl_services.h" -#include "kernel/osl/osl_shader.h" +#include "kernel/osl/closures.h" +#include "kernel/osl/globals.h" +#include "kernel/osl/services.h" +#include "kernel/osl/shader.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_string.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/string.h" // clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" -#include "kernel/kernel_random.h" -#include "kernel/kernel_write_passes.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_differential.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_camera.h" -#include "kernel/kernels/cpu/kernel_cpu_image.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" +#include "kernel/device/cpu/image.h" + +#include "kernel/util/differential.h" + +#include "kernel/integrator/state.h" +#include "kernel/integrator/state_flow.h" + #include "kernel/geom/geom.h" + #include "kernel/bvh/bvh.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_accumulate.h" -#include "kernel/kernel_shader.h" +#include "kernel/camera/camera.h" +#include "kernel/camera/projection.h" + +#include "kernel/integrator/path_state.h" +#include "kernel/integrator/shader_eval.h" + +#include "kernel/util/color.h" // clang-format on CCL_NAMESPACE_BEGIN @@ -107,8 +110,10 @@ ustring OSLRenderServices::u_geom_undisplaced("geom:undisplaced"); ustring OSLRenderServices::u_is_smooth("geom:is_smooth"); ustring OSLRenderServices::u_is_curve("geom:is_curve"); ustring OSLRenderServices::u_curve_thickness("geom:curve_thickness"); +ustring OSLRenderServices::u_curve_length("geom:curve_length"); ustring OSLRenderServices::u_curve_tangent_normal("geom:curve_tangent_normal"); ustring OSLRenderServices::u_curve_random("geom:curve_random"); +ustring OSLRenderServices::u_normal_map_normal("geom:normal_map_normal"); ustring OSLRenderServices::u_path_ray_length("path:ray_length"); ustring OSLRenderServices::u_path_ray_depth("path:ray_depth"); ustring OSLRenderServices::u_path_diffuse_depth("path:diffuse_depth"); @@ -147,7 +152,7 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -155,18 +160,19 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, Transform tfm; if (time == sd->time) - tfm = sd->ob_tfm; + tfm = object_get_transform(kg, sd); else tfm = object_fetch_transform_motion_test(kg, object, time, NULL); #else - Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); + const Transform tfm = object_get_transform(kg, sd); #endif copy_matrix(result, tfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_tfm); + const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); + copy_matrix(result, tfm); return true; } @@ -184,7 +190,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { @@ -192,18 +198,19 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, Transform itfm; if (time == sd->time) - itfm = sd->ob_itfm; + itfm = object_get_inverse_transform(kg, sd); else object_fetch_transform_motion_test(kg, object, time, &itfm); #else - Transform itfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); + const Transform itfm = object_get_inverse_transform(kg, sd); #endif copy_matrix(result, itfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_itfm); + const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true); + copy_matrix(result, itfm); return true; } @@ -218,7 +225,7 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, float time) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -250,7 +257,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, float time) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -284,21 +291,18 @@ bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { -#ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_tfm; -#else - KernelGlobals *kg = sd->osl_globals; - Transform tfm = object_fetch_transform(kg, object, OBJECT_TRANSFORM); -#endif + const Transform tfm = object_get_transform(kg, sd); copy_matrix(result, tfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_tfm); + const Transform tfm = lamp_fetch_transform(kg, sd->lamp, false); + copy_matrix(result, tfm); return true; } @@ -315,21 +319,18 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, * a concept of shader space, so we just use object space for both. */ if (xform) { const ShaderData *sd = (const ShaderData *)xform; + const KernelGlobalsCPU *kg = sd->osl_globals; int object = sd->object; if (object != OBJECT_NONE) { -#ifdef __OBJECT_MOTION__ - Transform tfm = sd->ob_itfm; -#else - KernelGlobals *kg = sd->osl_globals; - Transform tfm = object_fetch_transform(kg, object, OBJECT_INVERSE_TRANSFORM); -#endif + const Transform tfm = object_get_inverse_transform(kg, sd); copy_matrix(result, tfm); return true; } else if (sd->type == PRIMITIVE_LAMP) { - copy_matrix(result, sd->ob_itfm); + const Transform itfm = lamp_fetch_transform(kg, sd->lamp, true); + copy_matrix(result, itfm); return true; } @@ -341,7 +342,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, bool OSLRenderServices::get_matrix(OSL::ShaderGlobals *sg, OSL::Matrix44 &result, ustring from) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (from == u_ndc) { copy_matrix(result, kernel_data.cam.ndctoworld); @@ -368,7 +369,7 @@ bool OSLRenderServices::get_inverse_matrix(OSL::ShaderGlobals *sg, ustring to) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (to == u_ndc) { copy_matrix(result, kernel_data.cam.worldtondc); @@ -747,7 +748,7 @@ static bool set_attribute_matrix(const Transform &tfm, TypeDesc type, void *val) return false; } -static bool get_primitive_attribute(KernelGlobals *kg, +static bool get_primitive_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, const OSLGlobals::Attribute &attr, const TypeDesc &type, @@ -808,7 +809,7 @@ static bool get_primitive_attribute(KernelGlobals *kg, } } -static bool get_mesh_attribute(KernelGlobals *kg, +static bool get_mesh_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, const OSLGlobals::Attribute &attr, const TypeDesc &type, @@ -857,8 +858,12 @@ static bool get_object_attribute(const OSLGlobals::Attribute &attr, } } -bool OSLRenderServices::get_object_standard_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val) +bool OSLRenderServices::get_object_standard_attribute(const KernelGlobalsCPU *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val) { /* todo: turn this into hash table? */ @@ -984,12 +989,26 @@ bool OSLRenderServices::get_object_standard_attribute( float3 f = curve_tangent_normal(kg, sd); return set_attribute_float3(f, type, derivatives, val); } - else + else if (name == u_normal_map_normal) { + if (sd->type & PRIMITIVE_ALL_TRIANGLE) { + float3 f = triangle_smooth_normal_unnormalized(kg, sd, sd->Ng, sd->prim, sd->u, sd->v); + return set_attribute_float3(f, type, derivatives, val); + } + else { + return false; + } + } + else { return false; + } } -bool OSLRenderServices::get_background_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val) +bool OSLRenderServices::get_background_attribute(const KernelGlobalsCPU *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val) { if (name == u_path_ray_length) { /* Ray Length */ @@ -998,38 +1017,45 @@ bool OSLRenderServices::get_background_attribute( } else if (name == u_path_ray_depth) { /* Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.bounce : (shadow_state) ? shadow_state->shadow_path.bounce : 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_diffuse_depth) { /* Diffuse Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->diffuse_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.diffuse_bounce : + (shadow_state) ? shadow_state->shadow_path.diffuse_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_glossy_depth) { /* Glossy Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->glossy_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.glossy_bounce : + (shadow_state) ? shadow_state->shadow_path.glossy_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_transmission_depth) { /* Transmission Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->transmission_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.transmission_bounce : + (shadow_state) ? shadow_state->shadow_path.transmission_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_path_transparent_depth) { /* Transparent Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->transparent_bounce; - return set_attribute_int(f, type, derivatives, val); - } - else if (name == u_path_transmission_depth) { - /* Transmission Ray Depth */ - PathState *state = sd->osl_path_state; - int f = state->transmission_bounce; + const IntegratorStateCPU *state = sd->osl_path_state; + const IntegratorShadowStateCPU *shadow_state = sd->osl_shadow_path_state; + int f = (state) ? state->path.transparent_bounce : + (shadow_state) ? shadow_state->shadow_path.transparent_bounce : + 0; return set_attribute_int(f, type, derivatives, val); } else if (name == u_ndc) { @@ -1043,8 +1069,10 @@ bool OSLRenderServices::get_background_attribute( ndc[0] = camera_world_to_ndc(kg, sd, sd->ray_P); if (derivatives) { - ndc[1] = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dx) - ndc[0]; - ndc[2] = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dy) - ndc[0]; + ndc[1] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)) - + ndc[0]; + ndc[2] = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)) - + ndc[0]; } } else { @@ -1079,7 +1107,7 @@ bool OSLRenderServices::get_attribute(OSL::ShaderGlobals *sg, bool OSLRenderServices::get_attribute( ShaderData *sd, bool derivatives, ustring object_name, TypeDesc type, ustring name, void *val) { - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; int prim_type = 0; int object; @@ -1208,42 +1236,46 @@ bool OSLRenderServices::texture(ustring filename, OSLTextureHandle *handle = (OSLTextureHandle *)texture_handle; OSLTextureHandle::Type texture_type = (handle) ? handle->type : OSLTextureHandle::OIIO; ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; bool status = false; switch (texture_type) { case OSLTextureHandle::BEVEL: { /* Bevel shader hack. */ if (nchannels >= 3) { - PathState *state = sd->osl_path_state; - int num_samples = (int)s; - float radius = t; - float3 N = svm_bevel(kernel_globals, sd, state, radius, num_samples); - result[0] = N.x; - result[1] = N.y; - result[2] = N.z; - status = true; + const IntegratorStateCPU *state = sd->osl_path_state; + if (state) { + int num_samples = (int)s; + float radius = t; + float3 N = svm_bevel(kernel_globals, state, sd, radius, num_samples); + result[0] = N.x; + result[1] = N.y; + result[2] = N.z; + status = true; + } } break; } case OSLTextureHandle::AO: { /* AO shader hack. */ - PathState *state = sd->osl_path_state; - int num_samples = (int)s; - float radius = t; - float3 N = make_float3(dsdx, dtdx, dsdy); - int flags = 0; - if ((int)dtdy) { - flags |= NODE_AO_INSIDE; + const IntegratorStateCPU *state = sd->osl_path_state; + if (state) { + int num_samples = (int)s; + float radius = t; + float3 N = make_float3(dsdx, dtdx, dsdy); + int flags = 0; + if ((int)dtdy) { + flags |= NODE_AO_INSIDE; + } + if ((int)options.sblur) { + flags |= NODE_AO_ONLY_LOCAL; + } + if ((int)options.tblur) { + flags |= NODE_AO_GLOBAL_RADIUS; + } + result[0] = svm_ao(kernel_globals, state, sd, N, radius, num_samples, flags); + status = true; } - if ((int)options.sblur) { - flags |= NODE_AO_ONLY_LOCAL; - } - if ((int)options.tblur) { - flags |= NODE_AO_GLOBAL_RADIUS; - } - result[0] = svm_ao(kernel_globals, sd, N, state, radius, num_samples, flags); - status = true; break; } case OSLTextureHandle::SVM: { @@ -1355,7 +1387,7 @@ bool OSLRenderServices::texture3d(ustring filename, case OSLTextureHandle::SVM: { /* Packed texture. */ ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; int slot = handle->svm_slot; float3 P_float3 = make_float3(P.x, P.y, P.z); float4 rgba = kernel_tex_image_interp_3d(kernel_globals, slot, P_float3, INTERPOLATION_NONE); @@ -1377,7 +1409,7 @@ bool OSLRenderServices::texture3d(ustring filename, if (handle && handle->oiio_handle) { if (texture_thread_info == NULL) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; OSLThreadData *tdata = kernel_globals->osl_tdata; texture_thread_info = tdata->oiio_thread_info; } @@ -1462,7 +1494,7 @@ bool OSLRenderServices::environment(ustring filename, if (handle && handle->oiio_handle) { if (thread_info == NULL) { ShaderData *sd = (ShaderData *)(sg->renderstate); - KernelGlobals *kernel_globals = sd->osl_globals; + KernelGlobals kernel_globals = sd->osl_globals; OSLThreadData *tdata = kernel_globals->osl_tdata; thread_info = tdata->oiio_thread_info; } @@ -1600,10 +1632,14 @@ bool OSLRenderServices::trace(TraceOpt &options, } /* ray differentials */ - ray.dP.dx = TO_FLOAT3(dPdx); - ray.dP.dy = TO_FLOAT3(dPdy); - ray.dD.dx = TO_FLOAT3(dRdx); - ray.dD.dy = TO_FLOAT3(dRdy); + differential3 dP; + dP.dx = TO_FLOAT3(dPdx); + dP.dy = TO_FLOAT3(dPdy); + ray.dP = differential_make_compact(dP); + differential3 dD; + dD.dx = TO_FLOAT3(dRdx); + dD.dy = TO_FLOAT3(dRdy); + ray.dD = differential_make_compact(dD); /* allocate trace data */ OSLTraceData *tracedata = (OSLTraceData *)sg->tracedata; @@ -1613,7 +1649,7 @@ bool OSLRenderServices::trace(TraceOpt &options, tracedata->hit = false; tracedata->sd.osl_globals = sd->osl_globals; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; /* Can't raytrace from shaders like displacement, before BVH exists. */ if (kernel_data.bvh.bvh_layout == BVH_LAYOUT_NONE) { @@ -1646,11 +1682,11 @@ bool OSLRenderServices::getmessage(OSL::ShaderGlobals *sg, } else { ShaderData *sd = &tracedata->sd; - KernelGlobals *kg = sd->osl_globals; + const KernelGlobalsCPU *kg = sd->osl_globals; if (!tracedata->setup) { /* lazy shader data setup */ - shader_setup_from_ray(kg, sd, &tracedata->isect, &tracedata->ray); + shader_setup_from_ray(kg, sd, &tracedata->ray, &tracedata->isect); tracedata->setup = true; } diff --git a/intern/cycles/kernel/osl/osl_services.h b/intern/cycles/kernel/osl/services.h similarity index 93% rename from intern/cycles/kernel/osl/osl_services.h rename to intern/cycles/kernel/osl/services.h index 891b9172dd4..d9f57c642ad 100644 --- a/intern/cycles/kernel/osl/osl_services.h +++ b/intern/cycles/kernel/osl/services.h @@ -40,7 +40,7 @@ class Scene; class Shader; struct ShaderData; struct float3; -struct KernelGlobals; +struct KernelGlobalsCPU; /* OSL Texture Handle * @@ -250,10 +250,18 @@ class OSLRenderServices : public OSL::RendererServices { void *data) override; #endif - static bool get_background_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val); - static bool get_object_standard_attribute( - KernelGlobals *kg, ShaderData *sd, ustring name, TypeDesc type, bool derivatives, void *val); + static bool get_background_attribute(const KernelGlobalsCPU *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val); + static bool get_object_standard_attribute(const KernelGlobalsCPU *kg, + ShaderData *sd, + ustring name, + TypeDesc type, + bool derivatives, + void *val); static ustring u_distance; static ustring u_index; @@ -286,8 +294,10 @@ class OSLRenderServices : public OSL::RendererServices { static ustring u_is_smooth; static ustring u_is_curve; static ustring u_curve_thickness; + static ustring u_curve_length; static ustring u_curve_tangent_normal; static ustring u_curve_random; + static ustring u_normal_map_normal; static ustring u_path_ray_length; static ustring u_path_ray_depth; static ustring u_path_diffuse_depth; diff --git a/intern/cycles/kernel/osl/osl_shader.cpp b/intern/cycles/kernel/osl/shader.cpp similarity index 86% rename from intern/cycles/kernel/osl/osl_shader.cpp rename to intern/cycles/kernel/osl/shader.cpp index 389c854c495..33633c69e29 100644 --- a/intern/cycles/kernel/osl/osl_shader.cpp +++ b/intern/cycles/kernel/osl/shader.cpp @@ -17,31 +17,28 @@ #include // clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data_types.h" -#include "kernel/kernel_globals.h" +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" -#include "kernel/geom/geom_object.h" +#include "kernel/types.h" -#include "kernel/osl/osl_closures.h" -#include "kernel/osl/osl_globals.h" -#include "kernel/osl/osl_services.h" -#include "kernel/osl/osl_shader.h" +#include "kernel/geom/object.h" + +#include "kernel/integrator/state.h" + +#include "kernel/osl/closures.h" +#include "kernel/osl/globals.h" +#include "kernel/osl/services.h" +#include "kernel/osl/shader.h" // clang-format on -#include "util/util_foreach.h" - -#include "render/attribute.h" +#include "scene/attribute.h" CCL_NAMESPACE_BEGIN /* Threads */ -void OSLShader::thread_init(KernelGlobals *kg, - KernelGlobals *kernel_globals, - OSLGlobals *osl_globals) +void OSLShader::thread_init(KernelGlobalsCPU *kg, OSLGlobals *osl_globals) { /* no osl used? */ if (!osl_globals->use) { @@ -67,7 +64,7 @@ void OSLShader::thread_init(KernelGlobals *kg, kg->osl_tdata = tdata; } -void OSLShader::thread_free(KernelGlobals *kg) +void OSLShader::thread_free(KernelGlobalsCPU *kg) { if (!kg->osl) return; @@ -87,8 +84,11 @@ void OSLShader::thread_free(KernelGlobals *kg) /* Globals */ -static void shaderdata_to_shaderglobals( - KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag, OSLThreadData *tdata) +static void shaderdata_to_shaderglobals(const KernelGlobalsCPU *kg, + ShaderData *sd, + const void *state, + uint32_t path_flag, + OSLThreadData *tdata) { OSL::ShaderGlobals *globals = &tdata->globals; @@ -131,13 +131,18 @@ static void shaderdata_to_shaderglobals( /* Used by render-services. */ sd->osl_globals = kg; - sd->osl_path_state = state; + if (path_flag & PATH_RAY_SHADOW) { + sd->osl_shadow_path_state = (const IntegratorShadowStateCPU *)state; + } + else { + sd->osl_path_state = (const IntegratorStateCPU *)state; + } } /* Surface */ static void flatten_surface_closure_tree(ShaderData *sd, - int path_flag, + uint32_t path_flag, const OSL::ClosureColor *closure, float3 weight = make_float3(1.0f, 1.0f, 1.0f)) { @@ -171,7 +176,10 @@ static void flatten_surface_closure_tree(ShaderData *sd, } } -void OSLShader::eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag) +void OSLShader::eval_surface(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -276,7 +284,10 @@ static void flatten_background_closure_tree(ShaderData *sd, } } -void OSLShader::eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag) +void OSLShader::eval_background(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -331,7 +342,10 @@ static void flatten_volume_closure_tree(ShaderData *sd, } } -void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag) +void OSLShader::eval_volume(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -354,7 +368,7 @@ void OSLShader::eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, /* Displacement */ -void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, PathState *state) +void OSLShader::eval_displacement(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd) { /* setup shader globals from shader data */ OSLThreadData *tdata = kg->osl_tdata; @@ -377,7 +391,7 @@ void OSLShader::eval_displacement(KernelGlobals *kg, ShaderData *sd, PathState * /* Attributes */ -int OSLShader::find_attribute(KernelGlobals *kg, +int OSLShader::find_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc) diff --git a/intern/cycles/kernel/osl/osl_shader.h b/intern/cycles/kernel/osl/shader.h similarity index 62% rename from intern/cycles/kernel/osl/osl_shader.h rename to intern/cycles/kernel/osl/shader.h index a4fa24d0a90..7d68d4eae7f 100644 --- a/intern/cycles/kernel/osl/osl_shader.h +++ b/intern/cycles/kernel/osl/shader.h @@ -29,7 +29,7 @@ * This means no thread state must be passed along in the kernel itself. */ -# include "kernel/kernel_types.h" +# include "kernel/types.h" CCL_NAMESPACE_BEGIN @@ -37,8 +37,9 @@ class Scene; struct ShaderClosure; struct ShaderData; +struct IntegratorStateCPU; struct differential3; -struct KernelGlobals; +struct KernelGlobalsCPU; struct OSLGlobals; struct OSLShadingSystem; @@ -49,19 +50,26 @@ class OSLShader { static void register_closures(OSLShadingSystem *ss); /* per thread data */ - static void thread_init(KernelGlobals *kg, - KernelGlobals *kernel_globals, - OSLGlobals *osl_globals); - static void thread_free(KernelGlobals *kg); + static void thread_init(KernelGlobalsCPU *kg, OSLGlobals *osl_globals); + static void thread_free(KernelGlobalsCPU *kg); /* eval */ - static void eval_surface(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag); - static void eval_background(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag); - static void eval_volume(KernelGlobals *kg, ShaderData *sd, PathState *state, int path_flag); - static void eval_displacement(KernelGlobals *kg, ShaderData *sd, PathState *state); + static void eval_surface(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag); + static void eval_background(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag); + static void eval_volume(const KernelGlobalsCPU *kg, + const void *state, + ShaderData *sd, + uint32_t path_flag); + static void eval_displacement(const KernelGlobalsCPU *kg, const void *state, ShaderData *sd); /* attributes */ - static int find_attribute(KernelGlobals *kg, + static int find_attribute(const KernelGlobalsCPU *kg, const ShaderData *sd, uint id, AttributeDescriptor *desc); diff --git a/intern/cycles/kernel/shaders/CMakeLists.txt b/intern/cycles/kernel/osl/shaders/CMakeLists.txt similarity index 99% rename from intern/cycles/kernel/shaders/CMakeLists.txt rename to intern/cycles/kernel/osl/shaders/CMakeLists.txt index 02be7813369..6b62e7bb52f 100644 --- a/intern/cycles/kernel/shaders/CMakeLists.txt +++ b/intern/cycles/kernel/osl/shaders/CMakeLists.txt @@ -41,6 +41,7 @@ set(SRC_OSL node_vector_displacement.osl node_emission.osl node_environment_texture.osl + node_float_curve.osl node_fresnel.osl node_gamma.osl node_geometry.osl diff --git a/intern/cycles/kernel/shaders/node_absorption_volume.osl b/intern/cycles/kernel/osl/shaders/node_absorption_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_absorption_volume.osl rename to intern/cycles/kernel/osl/shaders/node_absorption_volume.osl diff --git a/intern/cycles/kernel/shaders/node_add_closure.osl b/intern/cycles/kernel/osl/shaders/node_add_closure.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_add_closure.osl rename to intern/cycles/kernel/osl/shaders/node_add_closure.osl diff --git a/intern/cycles/kernel/shaders/node_ambient_occlusion.osl b/intern/cycles/kernel/osl/shaders/node_ambient_occlusion.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_ambient_occlusion.osl rename to intern/cycles/kernel/osl/shaders/node_ambient_occlusion.osl diff --git a/intern/cycles/kernel/shaders/node_anisotropic_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_anisotropic_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_anisotropic_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_anisotropic_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_attribute.osl b/intern/cycles/kernel/osl/shaders/node_attribute.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_attribute.osl rename to intern/cycles/kernel/osl/shaders/node_attribute.osl diff --git a/intern/cycles/kernel/shaders/node_background.osl b/intern/cycles/kernel/osl/shaders/node_background.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_background.osl rename to intern/cycles/kernel/osl/shaders/node_background.osl diff --git a/intern/cycles/kernel/shaders/node_bevel.osl b/intern/cycles/kernel/osl/shaders/node_bevel.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_bevel.osl rename to intern/cycles/kernel/osl/shaders/node_bevel.osl diff --git a/intern/cycles/kernel/shaders/node_blackbody.osl b/intern/cycles/kernel/osl/shaders/node_blackbody.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_blackbody.osl rename to intern/cycles/kernel/osl/shaders/node_blackbody.osl diff --git a/intern/cycles/kernel/shaders/node_brick_texture.osl b/intern/cycles/kernel/osl/shaders/node_brick_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_brick_texture.osl rename to intern/cycles/kernel/osl/shaders/node_brick_texture.osl diff --git a/intern/cycles/kernel/shaders/node_brightness.osl b/intern/cycles/kernel/osl/shaders/node_brightness.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_brightness.osl rename to intern/cycles/kernel/osl/shaders/node_brightness.osl diff --git a/intern/cycles/kernel/shaders/node_bump.osl b/intern/cycles/kernel/osl/shaders/node_bump.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_bump.osl rename to intern/cycles/kernel/osl/shaders/node_bump.osl diff --git a/intern/cycles/kernel/shaders/node_camera.osl b/intern/cycles/kernel/osl/shaders/node_camera.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_camera.osl rename to intern/cycles/kernel/osl/shaders/node_camera.osl diff --git a/intern/cycles/kernel/shaders/node_checker_texture.osl b/intern/cycles/kernel/osl/shaders/node_checker_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_checker_texture.osl rename to intern/cycles/kernel/osl/shaders/node_checker_texture.osl diff --git a/intern/cycles/kernel/shaders/node_clamp.osl b/intern/cycles/kernel/osl/shaders/node_clamp.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_clamp.osl rename to intern/cycles/kernel/osl/shaders/node_clamp.osl diff --git a/intern/cycles/kernel/shaders/node_color.h b/intern/cycles/kernel/osl/shaders/node_color.h similarity index 100% rename from intern/cycles/kernel/shaders/node_color.h rename to intern/cycles/kernel/osl/shaders/node_color.h diff --git a/intern/cycles/kernel/shaders/node_combine_hsv.osl b/intern/cycles/kernel/osl/shaders/node_combine_hsv.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_combine_hsv.osl rename to intern/cycles/kernel/osl/shaders/node_combine_hsv.osl diff --git a/intern/cycles/kernel/shaders/node_combine_rgb.osl b/intern/cycles/kernel/osl/shaders/node_combine_rgb.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_combine_rgb.osl rename to intern/cycles/kernel/osl/shaders/node_combine_rgb.osl diff --git a/intern/cycles/kernel/shaders/node_combine_xyz.osl b/intern/cycles/kernel/osl/shaders/node_combine_xyz.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_combine_xyz.osl rename to intern/cycles/kernel/osl/shaders/node_combine_xyz.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_color.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_color.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_color.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_color.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_float.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_float.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_float.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_float.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_int.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_int.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_int.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_int.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_normal.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_normal.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_normal.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_normal.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_point.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_point.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_point.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_point.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_string.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_string.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_string.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_string.osl diff --git a/intern/cycles/kernel/shaders/node_convert_from_vector.osl b/intern/cycles/kernel/osl/shaders/node_convert_from_vector.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_convert_from_vector.osl rename to intern/cycles/kernel/osl/shaders/node_convert_from_vector.osl diff --git a/intern/cycles/kernel/shaders/node_diffuse_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_diffuse_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_diffuse_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_diffuse_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_displacement.osl b/intern/cycles/kernel/osl/shaders/node_displacement.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_displacement.osl rename to intern/cycles/kernel/osl/shaders/node_displacement.osl diff --git a/intern/cycles/kernel/shaders/node_emission.osl b/intern/cycles/kernel/osl/shaders/node_emission.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_emission.osl rename to intern/cycles/kernel/osl/shaders/node_emission.osl diff --git a/intern/cycles/kernel/shaders/node_environment_texture.osl b/intern/cycles/kernel/osl/shaders/node_environment_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_environment_texture.osl rename to intern/cycles/kernel/osl/shaders/node_environment_texture.osl diff --git a/intern/cycles/kernel/kernels/opencl/kernel_state_buffer_size.cl b/intern/cycles/kernel/osl/shaders/node_float_curve.osl similarity index 52% rename from intern/cycles/kernel/kernels/opencl/kernel_state_buffer_size.cl rename to intern/cycles/kernel/osl/shaders/node_float_curve.osl index c10ecc426c6..f1f05fd88a9 100644 --- a/intern/cycles/kernel/kernels/opencl/kernel_state_buffer_size.cl +++ b/intern/cycles/kernel/osl/shaders/node_float_curve.osl @@ -1,5 +1,5 @@ /* - * Copyright 2011-2017 Blender Foundation + * Copyright 2011-2021 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,16 +14,19 @@ * limitations under the License. */ -#include "kernel/kernel_compat_opencl.h" -#include "kernel/split/kernel_split_common.h" +#include "node_ramp_util.h" +#include "stdcycles.h" -__kernel void kernel_ocl_path_trace_state_buffer_size( - ccl_global char *kg, - ccl_constant KernelData *data, - uint num_threads, - ccl_global uint64_t *size) +shader node_float_curve(float ramp[] = {0.0}, + float min_x = 0.0, + float max_x = 1.0, + float ValueIn = 0.0, + float Factor = 0.0, + output float ValueOut = 0.0) { - ((KernelGlobals*)kg)->data = data; - *size = split_data_buffer_size((KernelGlobals*)kg, num_threads); -} + float c = (ValueIn - min_x) / (max_x - min_x); + ValueOut = rgb_ramp_lookup(ramp, c, 1, 1); + + ValueOut = mix(ValueIn, ValueOut, Factor); +} diff --git a/intern/cycles/kernel/shaders/node_fresnel.h b/intern/cycles/kernel/osl/shaders/node_fresnel.h similarity index 100% rename from intern/cycles/kernel/shaders/node_fresnel.h rename to intern/cycles/kernel/osl/shaders/node_fresnel.h diff --git a/intern/cycles/kernel/shaders/node_fresnel.osl b/intern/cycles/kernel/osl/shaders/node_fresnel.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_fresnel.osl rename to intern/cycles/kernel/osl/shaders/node_fresnel.osl diff --git a/intern/cycles/kernel/shaders/node_gamma.osl b/intern/cycles/kernel/osl/shaders/node_gamma.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_gamma.osl rename to intern/cycles/kernel/osl/shaders/node_gamma.osl diff --git a/intern/cycles/kernel/shaders/node_geometry.osl b/intern/cycles/kernel/osl/shaders/node_geometry.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_geometry.osl rename to intern/cycles/kernel/osl/shaders/node_geometry.osl diff --git a/intern/cycles/kernel/shaders/node_glass_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_glass_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_glass_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_glossy_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_glossy_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_glossy_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_glossy_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_gradient_texture.osl b/intern/cycles/kernel/osl/shaders/node_gradient_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_gradient_texture.osl rename to intern/cycles/kernel/osl/shaders/node_gradient_texture.osl diff --git a/intern/cycles/kernel/shaders/node_hair_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_hair_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_hair_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_hair_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_hair_info.osl b/intern/cycles/kernel/osl/shaders/node_hair_info.osl similarity index 92% rename from intern/cycles/kernel/shaders/node_hair_info.osl rename to intern/cycles/kernel/osl/shaders/node_hair_info.osl index ee08ea57e68..ddc2e28b83a 100644 --- a/intern/cycles/kernel/shaders/node_hair_info.osl +++ b/intern/cycles/kernel/osl/shaders/node_hair_info.osl @@ -18,12 +18,14 @@ shader node_hair_info(output float IsStrand = 0.0, output float Intercept = 0.0, + output float Length = 0.0, output float Thickness = 0.0, output normal TangentNormal = N, output float Random = 0) { getattribute("geom:is_curve", IsStrand); getattribute("geom:curve_intercept", Intercept); + getattribute("geom:curve_length", Length); getattribute("geom:curve_thickness", Thickness); getattribute("geom:curve_tangent_normal", TangentNormal); getattribute("geom:curve_random", Random); diff --git a/intern/cycles/kernel/shaders/node_hash.h b/intern/cycles/kernel/osl/shaders/node_hash.h similarity index 100% rename from intern/cycles/kernel/shaders/node_hash.h rename to intern/cycles/kernel/osl/shaders/node_hash.h diff --git a/intern/cycles/kernel/shaders/node_holdout.osl b/intern/cycles/kernel/osl/shaders/node_holdout.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_holdout.osl rename to intern/cycles/kernel/osl/shaders/node_holdout.osl diff --git a/intern/cycles/kernel/shaders/node_hsv.osl b/intern/cycles/kernel/osl/shaders/node_hsv.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_hsv.osl rename to intern/cycles/kernel/osl/shaders/node_hsv.osl diff --git a/intern/cycles/kernel/shaders/node_ies_light.osl b/intern/cycles/kernel/osl/shaders/node_ies_light.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_ies_light.osl rename to intern/cycles/kernel/osl/shaders/node_ies_light.osl diff --git a/intern/cycles/kernel/shaders/node_image_texture.osl b/intern/cycles/kernel/osl/shaders/node_image_texture.osl similarity index 98% rename from intern/cycles/kernel/shaders/node_image_texture.osl rename to intern/cycles/kernel/osl/shaders/node_image_texture.osl index 9e2ef84c872..56fcc47a011 100644 --- a/intern/cycles/kernel/shaders/node_image_texture.osl +++ b/intern/cycles/kernel/osl/shaders/node_image_texture.osl @@ -143,10 +143,10 @@ shader node_image_texture(int use_mapping = 0, * in between we blend between two textures, and in the middle we a blend * between three textures. * - * the Nxyz values are the barycentric coordinates in an equilateral + * the `Nxyz` values are the barycentric coordinates in an equilateral * triangle, which in case of blending, in the middle has a smaller * equilateral triangle where 3 textures blend. this divides things into - * 7 zones, with an if () test for each zone */ + * 7 zones, with an if () test for each zone. */ vector weight = vector(0.0, 0.0, 0.0); float blend = projection_blend; diff --git a/intern/cycles/kernel/shaders/node_invert.osl b/intern/cycles/kernel/osl/shaders/node_invert.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_invert.osl rename to intern/cycles/kernel/osl/shaders/node_invert.osl diff --git a/intern/cycles/kernel/shaders/node_layer_weight.osl b/intern/cycles/kernel/osl/shaders/node_layer_weight.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_layer_weight.osl rename to intern/cycles/kernel/osl/shaders/node_layer_weight.osl diff --git a/intern/cycles/kernel/shaders/node_light_falloff.osl b/intern/cycles/kernel/osl/shaders/node_light_falloff.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_light_falloff.osl rename to intern/cycles/kernel/osl/shaders/node_light_falloff.osl diff --git a/intern/cycles/kernel/shaders/node_light_path.osl b/intern/cycles/kernel/osl/shaders/node_light_path.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_light_path.osl rename to intern/cycles/kernel/osl/shaders/node_light_path.osl diff --git a/intern/cycles/kernel/shaders/node_magic_texture.osl b/intern/cycles/kernel/osl/shaders/node_magic_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_magic_texture.osl rename to intern/cycles/kernel/osl/shaders/node_magic_texture.osl diff --git a/intern/cycles/kernel/shaders/node_map_range.osl b/intern/cycles/kernel/osl/shaders/node_map_range.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_map_range.osl rename to intern/cycles/kernel/osl/shaders/node_map_range.osl diff --git a/intern/cycles/kernel/shaders/node_mapping.osl b/intern/cycles/kernel/osl/shaders/node_mapping.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_mapping.osl rename to intern/cycles/kernel/osl/shaders/node_mapping.osl diff --git a/intern/cycles/kernel/shaders/node_math.h b/intern/cycles/kernel/osl/shaders/node_math.h similarity index 100% rename from intern/cycles/kernel/shaders/node_math.h rename to intern/cycles/kernel/osl/shaders/node_math.h diff --git a/intern/cycles/kernel/shaders/node_math.osl b/intern/cycles/kernel/osl/shaders/node_math.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_math.osl rename to intern/cycles/kernel/osl/shaders/node_math.osl diff --git a/intern/cycles/kernel/shaders/node_mix.osl b/intern/cycles/kernel/osl/shaders/node_mix.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_mix.osl rename to intern/cycles/kernel/osl/shaders/node_mix.osl diff --git a/intern/cycles/kernel/shaders/node_mix_closure.osl b/intern/cycles/kernel/osl/shaders/node_mix_closure.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_mix_closure.osl rename to intern/cycles/kernel/osl/shaders/node_mix_closure.osl diff --git a/intern/cycles/kernel/shaders/node_musgrave_texture.osl b/intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl similarity index 99% rename from intern/cycles/kernel/shaders/node_musgrave_texture.osl rename to intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl index 0e71ce74c29..6a688d654c9 100644 --- a/intern/cycles/kernel/shaders/node_musgrave_texture.osl +++ b/intern/cycles/kernel/osl/shaders/node_musgrave_texture.osl @@ -697,7 +697,7 @@ shader node_musgrave_texture( output float Fac = 0.0) { float dimension = max(Dimension, 1e-5); - float octaves = clamp(Detail, 0.0, 16.0); + float octaves = clamp(Detail, 0.0, 15.0); float lacunarity = max(Lacunarity, 1e-5); vector3 s = Vector; diff --git a/intern/cycles/kernel/shaders/node_noise.h b/intern/cycles/kernel/osl/shaders/node_noise.h similarity index 96% rename from intern/cycles/kernel/shaders/node_noise.h rename to intern/cycles/kernel/osl/shaders/node_noise.h index ab4cd7792cc..e8a71032171 100644 --- a/intern/cycles/kernel/shaders/node_noise.h +++ b/intern/cycles/kernel/osl/shaders/node_noise.h @@ -90,7 +90,7 @@ float fractal_noise(float p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); @@ -119,7 +119,7 @@ float fractal_noise(vector2 p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); @@ -148,7 +148,7 @@ float fractal_noise(vector3 p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); @@ -177,7 +177,7 @@ float fractal_noise(vector4 p, float details, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - float octaves = clamp(details, 0.0, 16.0); + float octaves = clamp(details, 0.0, 15.0); int n = (int)octaves; for (int i = 0; i <= n; i++) { float t = safe_noise(fscale * p); diff --git a/intern/cycles/kernel/shaders/node_noise_texture.osl b/intern/cycles/kernel/osl/shaders/node_noise_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_noise_texture.osl rename to intern/cycles/kernel/osl/shaders/node_noise_texture.osl diff --git a/intern/cycles/kernel/shaders/node_normal.osl b/intern/cycles/kernel/osl/shaders/node_normal.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_normal.osl rename to intern/cycles/kernel/osl/shaders/node_normal.osl diff --git a/intern/cycles/kernel/shaders/node_normal_map.osl b/intern/cycles/kernel/osl/shaders/node_normal_map.osl similarity index 97% rename from intern/cycles/kernel/shaders/node_normal_map.osl rename to intern/cycles/kernel/osl/shaders/node_normal_map.osl index 6d4780f6dae..7a94ad8ad1a 100644 --- a/intern/cycles/kernel/shaders/node_normal_map.osl +++ b/intern/cycles/kernel/osl/shaders/node_normal_map.osl @@ -45,7 +45,7 @@ shader node_normal_map(normal NormalIn = N, // get _unnormalized_ interpolated normal and tangent if (getattribute(attr_name, tangent) && getattribute(attr_sign_name, tangent_sign) && - (!is_smooth || getattribute("geom:N", ninterp))) { + (!is_smooth || getattribute("geom:normal_map_normal", ninterp))) { // apply normal map vector B = tangent_sign * cross(ninterp, tangent); Normal = normalize(mcolor[0] * tangent + mcolor[1] * B + mcolor[2] * ninterp); diff --git a/intern/cycles/kernel/shaders/node_object_info.osl b/intern/cycles/kernel/osl/shaders/node_object_info.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_object_info.osl rename to intern/cycles/kernel/osl/shaders/node_object_info.osl diff --git a/intern/cycles/kernel/shaders/node_output_displacement.osl b/intern/cycles/kernel/osl/shaders/node_output_displacement.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_output_displacement.osl rename to intern/cycles/kernel/osl/shaders/node_output_displacement.osl diff --git a/intern/cycles/kernel/shaders/node_output_surface.osl b/intern/cycles/kernel/osl/shaders/node_output_surface.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_output_surface.osl rename to intern/cycles/kernel/osl/shaders/node_output_surface.osl diff --git a/intern/cycles/kernel/shaders/node_output_volume.osl b/intern/cycles/kernel/osl/shaders/node_output_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_output_volume.osl rename to intern/cycles/kernel/osl/shaders/node_output_volume.osl diff --git a/intern/cycles/kernel/shaders/node_particle_info.osl b/intern/cycles/kernel/osl/shaders/node_particle_info.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_particle_info.osl rename to intern/cycles/kernel/osl/shaders/node_particle_info.osl diff --git a/intern/cycles/kernel/shaders/node_principled_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl similarity index 87% rename from intern/cycles/kernel/shaders/node_principled_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl index 23949f406c7..55afb892d36 100644 --- a/intern/cycles/kernel/shaders/node_principled_bsdf.osl +++ b/intern/cycles/kernel/osl/shaders/node_principled_bsdf.osl @@ -18,11 +18,13 @@ #include "stdcycles.h" shader node_principled_bsdf(string distribution = "Multiscatter GGX", - string subsurface_method = "burley", + string subsurface_method = "random_walk", color BaseColor = color(0.8, 0.8, 0.8), float Subsurface = 0.0, vector SubsurfaceRadius = vector(1.0, 1.0, 1.0), color SubsurfaceColor = color(0.7, 0.1, 0.1), + float SubsurfaceIOR = 1.4, + float SubsurfaceAnisotropy = 0.0, float Metallic = 0.0, float Specular = 0.5, float SpecularTint = 0.0, @@ -59,22 +61,17 @@ shader node_principled_bsdf(string distribution = "Multiscatter GGX", if (diffuse_weight > 1e-5) { if (Subsurface > 1e-5) { color mixed_ss_base_color = SubsurfaceColor * Subsurface + BaseColor * (1.0 - Subsurface); - if (subsurface_method == "burley") { - BSDF = mixed_ss_base_color * bssrdf("principled", - Normal, - Subsurface * SubsurfaceRadius, - SubsurfaceColor, - "roughness", - Roughness); - } - else { - BSDF = mixed_ss_base_color * bssrdf("principled_random_walk", - Normal, - Subsurface * SubsurfaceRadius, - mixed_ss_base_color, - "roughness", - Roughness); - } + + BSDF = mixed_ss_base_color * bssrdf(subsurface_method, + Normal, + Subsurface * SubsurfaceRadius, + mixed_ss_base_color, + "roughness", + Roughness, + "ior", + SubsurfaceIOR, + "anisotropy", + SubsurfaceAnisotropy); } else { BSDF = BaseColor * principled_diffuse(Normal, Roughness); diff --git a/intern/cycles/kernel/shaders/node_principled_hair_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_principled_hair_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_principled_hair_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_principled_hair_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_principled_volume.osl b/intern/cycles/kernel/osl/shaders/node_principled_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_principled_volume.osl rename to intern/cycles/kernel/osl/shaders/node_principled_volume.osl diff --git a/intern/cycles/kernel/shaders/node_ramp_util.h b/intern/cycles/kernel/osl/shaders/node_ramp_util.h similarity index 100% rename from intern/cycles/kernel/shaders/node_ramp_util.h rename to intern/cycles/kernel/osl/shaders/node_ramp_util.h diff --git a/intern/cycles/kernel/shaders/node_refraction_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_refraction_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_refraction_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_refraction_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_rgb_curves.osl b/intern/cycles/kernel/osl/shaders/node_rgb_curves.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_rgb_curves.osl rename to intern/cycles/kernel/osl/shaders/node_rgb_curves.osl diff --git a/intern/cycles/kernel/shaders/node_rgb_ramp.osl b/intern/cycles/kernel/osl/shaders/node_rgb_ramp.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_rgb_ramp.osl rename to intern/cycles/kernel/osl/shaders/node_rgb_ramp.osl diff --git a/intern/cycles/kernel/shaders/node_rgb_to_bw.osl b/intern/cycles/kernel/osl/shaders/node_rgb_to_bw.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_rgb_to_bw.osl rename to intern/cycles/kernel/osl/shaders/node_rgb_to_bw.osl diff --git a/intern/cycles/kernel/shaders/node_scatter_volume.osl b/intern/cycles/kernel/osl/shaders/node_scatter_volume.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_scatter_volume.osl rename to intern/cycles/kernel/osl/shaders/node_scatter_volume.osl diff --git a/intern/cycles/kernel/shaders/node_separate_hsv.osl b/intern/cycles/kernel/osl/shaders/node_separate_hsv.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_separate_hsv.osl rename to intern/cycles/kernel/osl/shaders/node_separate_hsv.osl diff --git a/intern/cycles/kernel/shaders/node_separate_rgb.osl b/intern/cycles/kernel/osl/shaders/node_separate_rgb.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_separate_rgb.osl rename to intern/cycles/kernel/osl/shaders/node_separate_rgb.osl diff --git a/intern/cycles/kernel/shaders/node_separate_xyz.osl b/intern/cycles/kernel/osl/shaders/node_separate_xyz.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_separate_xyz.osl rename to intern/cycles/kernel/osl/shaders/node_separate_xyz.osl diff --git a/intern/cycles/kernel/shaders/node_set_normal.osl b/intern/cycles/kernel/osl/shaders/node_set_normal.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_set_normal.osl rename to intern/cycles/kernel/osl/shaders/node_set_normal.osl diff --git a/intern/cycles/kernel/shaders/node_sky_texture.osl b/intern/cycles/kernel/osl/shaders/node_sky_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_sky_texture.osl rename to intern/cycles/kernel/osl/shaders/node_sky_texture.osl diff --git a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl b/intern/cycles/kernel/osl/shaders/node_subsurface_scattering.osl similarity index 50% rename from intern/cycles/kernel/shaders/node_subsurface_scattering.osl rename to intern/cycles/kernel/osl/shaders/node_subsurface_scattering.osl index b1e854150ab..f55e38c54ff 100644 --- a/intern/cycles/kernel/shaders/node_subsurface_scattering.osl +++ b/intern/cycles/kernel/osl/shaders/node_subsurface_scattering.osl @@ -19,27 +19,12 @@ shader node_subsurface_scattering(color Color = 0.8, float Scale = 1.0, vector Radius = vector(0.1, 0.1, 0.1), - float TextureBlur = 0.0, - float Sharpness = 0.0, - string falloff = "cubic", + float IOR = 1.4, + float Anisotropy = 0.0, + string method = "random_walk", normal Normal = N, output closure color BSSRDF = 0) { - if (falloff == "gaussian") - BSSRDF = Color * - bssrdf("gaussian", Normal, Scale * Radius, Color, "texture_blur", TextureBlur); - else if (falloff == "cubic") - BSSRDF = Color * bssrdf("cubic", - Normal, - Scale * Radius, - Color, - "texture_blur", - TextureBlur, - "sharpness", - Sharpness); - else if (falloff == "burley") - BSSRDF = Color * bssrdf("burley", Normal, Scale * Radius, Color, "texture_blur", TextureBlur); - else - BSSRDF = Color * - bssrdf("random_walk", Normal, Scale * Radius, Color, "texture_blur", TextureBlur); + BSSRDF = Color * + bssrdf(method, Normal, Scale * Radius, Color, "ior", IOR, "anisotropy", Anisotropy); } diff --git a/intern/cycles/kernel/shaders/node_tangent.osl b/intern/cycles/kernel/osl/shaders/node_tangent.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_tangent.osl rename to intern/cycles/kernel/osl/shaders/node_tangent.osl diff --git a/intern/cycles/kernel/shaders/node_texture_coordinate.osl b/intern/cycles/kernel/osl/shaders/node_texture_coordinate.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_texture_coordinate.osl rename to intern/cycles/kernel/osl/shaders/node_texture_coordinate.osl diff --git a/intern/cycles/kernel/shaders/node_toon_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_toon_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_toon_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_toon_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_translucent_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_translucent_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_translucent_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_translucent_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_transparent_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_transparent_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_transparent_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_transparent_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_uv_map.osl b/intern/cycles/kernel/osl/shaders/node_uv_map.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_uv_map.osl rename to intern/cycles/kernel/osl/shaders/node_uv_map.osl diff --git a/intern/cycles/kernel/shaders/node_value.osl b/intern/cycles/kernel/osl/shaders/node_value.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_value.osl rename to intern/cycles/kernel/osl/shaders/node_value.osl diff --git a/intern/cycles/kernel/shaders/node_vector_curves.osl b/intern/cycles/kernel/osl/shaders/node_vector_curves.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_curves.osl rename to intern/cycles/kernel/osl/shaders/node_vector_curves.osl diff --git a/intern/cycles/kernel/shaders/node_vector_displacement.osl b/intern/cycles/kernel/osl/shaders/node_vector_displacement.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_displacement.osl rename to intern/cycles/kernel/osl/shaders/node_vector_displacement.osl diff --git a/intern/cycles/kernel/shaders/node_vector_math.osl b/intern/cycles/kernel/osl/shaders/node_vector_math.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_math.osl rename to intern/cycles/kernel/osl/shaders/node_vector_math.osl diff --git a/intern/cycles/kernel/shaders/node_vector_rotate.osl b/intern/cycles/kernel/osl/shaders/node_vector_rotate.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_rotate.osl rename to intern/cycles/kernel/osl/shaders/node_vector_rotate.osl diff --git a/intern/cycles/kernel/shaders/node_vector_transform.osl b/intern/cycles/kernel/osl/shaders/node_vector_transform.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vector_transform.osl rename to intern/cycles/kernel/osl/shaders/node_vector_transform.osl diff --git a/intern/cycles/kernel/shaders/node_velvet_bsdf.osl b/intern/cycles/kernel/osl/shaders/node_velvet_bsdf.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_velvet_bsdf.osl rename to intern/cycles/kernel/osl/shaders/node_velvet_bsdf.osl diff --git a/intern/cycles/kernel/shaders/node_vertex_color.osl b/intern/cycles/kernel/osl/shaders/node_vertex_color.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_vertex_color.osl rename to intern/cycles/kernel/osl/shaders/node_vertex_color.osl diff --git a/intern/cycles/kernel/shaders/node_voronoi_texture.osl b/intern/cycles/kernel/osl/shaders/node_voronoi_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_voronoi_texture.osl rename to intern/cycles/kernel/osl/shaders/node_voronoi_texture.osl diff --git a/intern/cycles/kernel/shaders/node_voxel_texture.osl b/intern/cycles/kernel/osl/shaders/node_voxel_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_voxel_texture.osl rename to intern/cycles/kernel/osl/shaders/node_voxel_texture.osl diff --git a/intern/cycles/kernel/shaders/node_wave_texture.osl b/intern/cycles/kernel/osl/shaders/node_wave_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_wave_texture.osl rename to intern/cycles/kernel/osl/shaders/node_wave_texture.osl diff --git a/intern/cycles/kernel/shaders/node_wavelength.osl b/intern/cycles/kernel/osl/shaders/node_wavelength.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_wavelength.osl rename to intern/cycles/kernel/osl/shaders/node_wavelength.osl diff --git a/intern/cycles/kernel/shaders/node_white_noise_texture.osl b/intern/cycles/kernel/osl/shaders/node_white_noise_texture.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_white_noise_texture.osl rename to intern/cycles/kernel/osl/shaders/node_white_noise_texture.osl diff --git a/intern/cycles/kernel/shaders/node_wireframe.osl b/intern/cycles/kernel/osl/shaders/node_wireframe.osl similarity index 100% rename from intern/cycles/kernel/shaders/node_wireframe.osl rename to intern/cycles/kernel/osl/shaders/node_wireframe.osl diff --git a/intern/cycles/kernel/shaders/stdcycles.h b/intern/cycles/kernel/osl/shaders/stdcycles.h similarity index 100% rename from intern/cycles/kernel/shaders/stdcycles.h rename to intern/cycles/kernel/osl/shaders/stdcycles.h diff --git a/intern/cycles/kernel/sample/jitter.h b/intern/cycles/kernel/sample/jitter.h new file mode 100644 index 00000000000..b76b9b8f23e --- /dev/null +++ b/intern/cycles/kernel/sample/jitter.h @@ -0,0 +1,195 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +CCL_NAMESPACE_BEGIN + +ccl_device_inline uint32_t laine_karras_permutation(uint32_t x, uint32_t seed) +{ + x += seed; + x ^= (x * 0x6c50b47cu); + x ^= x * 0xb82f1e52u; + x ^= x * 0xc7afe638u; + x ^= x * 0x8d22f6e6u; + + return x; +} + +ccl_device_inline uint32_t nested_uniform_scramble(uint32_t x, uint32_t seed) +{ + x = reverse_integer_bits(x); + x = laine_karras_permutation(x, seed); + x = reverse_integer_bits(x); + + return x; +} + +ccl_device_inline uint cmj_hash(uint i, uint p) +{ + i ^= p; + i ^= i >> 17; + i ^= i >> 10; + i *= 0xb36534e5; + i ^= i >> 12; + i ^= i >> 21; + i *= 0x93fc4795; + i ^= 0xdf6e307f; + i ^= i >> 17; + i *= 1 | p >> 18; + + return i; +} + +ccl_device_inline uint cmj_hash_simple(uint i, uint p) +{ + i = (i ^ 61) ^ p; + i += i << 3; + i ^= i >> 4; + i *= 0x27d4eb2d; + return i; +} + +ccl_device_inline float cmj_randfloat(uint i, uint p) +{ + return cmj_hash(i, p) * (1.0f / 4294967808.0f); +} + +ccl_device_inline float cmj_randfloat_simple(uint i, uint p) +{ + return cmj_hash_simple(i, p) * (1.0f / (float)0xFFFFFFFF); +} + +ccl_device_inline float cmj_randfloat_simple_dist(uint i, uint p, float d) +{ + return cmj_hash_simple(i, p) * (d / (float)0xFFFFFFFF); +} + +ccl_device float pmj_sample_1D(KernelGlobals kg, uint sample, uint rng_hash, uint dimension) +{ + uint hash = rng_hash; + float jitter_x = 0.0f; + if (kernel_data.integrator.scrambling_distance < 1.0f) { + hash = kernel_data.integrator.seed; + + jitter_x = cmj_randfloat_simple_dist( + dimension, rng_hash, kernel_data.integrator.scrambling_distance); + } + + /* Perform Owen shuffle of the sample number to reorder the samples. */ +#ifdef _SIMPLE_HASH_ + const uint rv = cmj_hash_simple(dimension, hash); +#else /* Use a _REGULAR_HASH_. */ + const uint rv = cmj_hash(dimension, hash); +#endif +#ifdef _XOR_SHUFFLE_ +# warning "Using XOR shuffle." + const uint s = sample ^ rv; +#else /* Use _OWEN_SHUFFLE_ for reordering. */ + const uint s = nested_uniform_scramble(sample, rv); +#endif + + /* Based on the sample number a sample pattern is selected and offset by the dimension. */ + const uint sample_set = s / NUM_PMJ_SAMPLES; + const uint d = (dimension + sample_set); + const uint dim = d % NUM_PMJ_PATTERNS; + + /* The PMJ sample sets contain a sample with (x,y) with NUM_PMJ_SAMPLES so for 1D + * the x part is used for even dims and the y for odd. */ + int index = 2 * ((dim >> 1) * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)) + (dim & 1); + + float fx = kernel_tex_fetch(__sample_pattern_lut, index); + +#ifndef _NO_CRANLEY_PATTERSON_ROTATION_ + /* Use Cranley-Patterson rotation to displace the sample pattern. */ +# ifdef _SIMPLE_HASH_ + float dx = cmj_randfloat_simple(d, hash); +# else + float dx = cmj_randfloat(d, hash); +# endif + /* Jitter sample locations and map back into [0 1]. */ + fx = fx + dx + jitter_x; + fx = fx - floorf(fx); +#else +# warning "Not using Cranley-Patterson Rotation." +#endif + + return fx; +} + +ccl_device void pmj_sample_2D(KernelGlobals kg, + uint sample, + uint rng_hash, + uint dimension, + ccl_private float *x, + ccl_private float *y) +{ + uint hash = rng_hash; + float jitter_x = 0.0f; + float jitter_y = 0.0f; + if (kernel_data.integrator.scrambling_distance < 1.0f) { + hash = kernel_data.integrator.seed; + + jitter_x = cmj_randfloat_simple_dist( + dimension, rng_hash, kernel_data.integrator.scrambling_distance); + jitter_y = cmj_randfloat_simple_dist( + dimension + 1, rng_hash, kernel_data.integrator.scrambling_distance); + } + + /* Perform a shuffle on the sample number to reorder the samples. */ +#ifdef _SIMPLE_HASH_ + const uint rv = cmj_hash_simple(dimension, hash); +#else /* Use a _REGULAR_HASH_. */ + const uint rv = cmj_hash(dimension, hash); +#endif +#ifdef _XOR_SHUFFLE_ +# warning "Using XOR shuffle." + const uint s = sample ^ rv; +#else /* Use _OWEN_SHUFFLE_ for reordering. */ + const uint s = nested_uniform_scramble(sample, rv); +#endif + + /* Based on the sample number a sample pattern is selected and offset by the dimension. */ + const uint sample_set = s / NUM_PMJ_SAMPLES; + const uint d = dimension + sample_set; + uint dim = d % NUM_PMJ_PATTERNS; + int index = 2 * (dim * NUM_PMJ_SAMPLES + (s % NUM_PMJ_SAMPLES)); + + float fx = kernel_tex_fetch(__sample_pattern_lut, index); + float fy = kernel_tex_fetch(__sample_pattern_lut, index + 1); + +#ifndef _NO_CRANLEY_PATTERSON_ROTATION_ + /* Use Cranley-Patterson rotation to displace the sample pattern. */ +# ifdef _SIMPLE_HASH_ + float dx = cmj_randfloat_simple(d, hash); + float dy = cmj_randfloat_simple(d + 1, hash); +# else + float dx = cmj_randfloat(d, hash); + float dy = cmj_randfloat(d + 1, hash); +# endif + /* Jitter sample locations and map back to the unit square [0 1]x[0 1]. */ + float sx = fx + dx + jitter_x; + float sy = fy + dy + jitter_y; + sx = sx - floorf(sx); + sy = sy - floorf(sy); +#else +# warning "Not using Cranley Patterson Rotation." +#endif + + (*x) = sx; + (*y) = sy; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/lcg.h b/intern/cycles/kernel/sample/lcg.h new file mode 100644 index 00000000000..92cfff639b4 --- /dev/null +++ b/intern/cycles/kernel/sample/lcg.h @@ -0,0 +1,51 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Linear Congruential Generator */ + +ccl_device uint lcg_step_uint(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return *rng; +} + +ccl_device float lcg_step_float(uint *rng) +{ + /* implicit mod 2^32 */ + *rng = (1103515245 * (*rng) + 12345); + return (float)*rng * (1.0f / (float)0xFFFFFFFF); +} + +ccl_device uint lcg_init(uint seed) +{ + uint rng = seed; + lcg_step_uint(&rng); + return rng; +} + +ccl_device_inline uint lcg_state_init(const uint rng_hash, + const uint rng_offset, + const uint sample, + const uint scramble) +{ + return lcg_init(rng_hash + rng_offset + sample * scramble); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_montecarlo.h b/intern/cycles/kernel/sample/mapping.h similarity index 50% rename from intern/cycles/kernel/kernel_montecarlo.h rename to intern/cycles/kernel/sample/mapping.h index ce37bd0b15e..3297aa2a29a 100644 --- a/intern/cycles/kernel/kernel_montecarlo.h +++ b/intern/cycles/kernel/sample/mapping.h @@ -30,13 +30,12 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef __KERNEL_MONTECARLO_CL__ -#define __KERNEL_MONTECARLO_CL__ +#pragma once CCL_NAMESPACE_BEGIN /* distribute uniform xy on [0,1] over unit disk [-1,1] */ -ccl_device void to_unit_disk(float *x, float *y) +ccl_device void to_unit_disk(ccl_private float *x, ccl_private float *y) { float phi = M_2PI_F * (*x); float r = sqrtf(*y); @@ -47,7 +46,10 @@ ccl_device void to_unit_disk(float *x, float *y) /* return an orthogonal tangent and bitangent given a normal and tangent that * may not be exactly orthogonal */ -ccl_device void make_orthonormals_tangent(const float3 N, const float3 T, float3 *a, float3 *b) +ccl_device void make_orthonormals_tangent(const float3 N, + const float3 T, + ccl_private float3 *a, + ccl_private float3 *b) { *b = normalize(cross(N, T)); *a = cross(*b, N); @@ -55,7 +57,7 @@ ccl_device void make_orthonormals_tangent(const float3 N, const float3 T, float3 /* sample direction with cosine weighted distributed in hemisphere */ ccl_device_inline void sample_cos_hemisphere( - const float3 N, float randu, float randv, float3 *omega_in, float *pdf) + const float3 N, float randu, float randv, ccl_private float3 *omega_in, ccl_private float *pdf) { to_unit_disk(&randu, &randv); float costheta = sqrtf(max(1.0f - randu * randu - randv * randv, 0.0f)); @@ -67,7 +69,7 @@ ccl_device_inline void sample_cos_hemisphere( /* sample direction uniformly distributed in hemisphere */ ccl_device_inline void sample_uniform_hemisphere( - const float3 N, float randu, float randv, float3 *omega_in, float *pdf) + const float3 N, float randu, float randv, ccl_private float3 *omega_in, ccl_private float *pdf) { float z = randu; float r = sqrtf(max(0.0f, 1.0f - z * z)); @@ -82,8 +84,12 @@ ccl_device_inline void sample_uniform_hemisphere( } /* sample direction uniformly distributed in cone */ -ccl_device_inline void sample_uniform_cone( - const float3 N, float angle, float randu, float randv, float3 *omega_in, float *pdf) +ccl_device_inline void sample_uniform_cone(const float3 N, + float angle, + float randu, + float randv, + ccl_private float3 *omega_in, + ccl_private float *pdf) { float zMin = cosf(angle); float z = zMin - zMin * randu + randu; @@ -120,31 +126,6 @@ ccl_device float3 sample_uniform_sphere(float u1, float u2) return make_float3(x, y, z); } -ccl_device float balance_heuristic(float a, float b) -{ - return (a) / (a + b); -} - -ccl_device float balance_heuristic_3(float a, float b, float c) -{ - return (a) / (a + b + c); -} - -ccl_device float power_heuristic(float a, float b) -{ - return (a * a) / (a * a + b * b); -} - -ccl_device float power_heuristic_3(float a, float b, float c) -{ - return (a * a) / (a * a + b * b + c * c); -} - -ccl_device float max_heuristic(float a, float b) -{ - return (a > b) ? 1.0f : 0.0f; -} - /* distribute uniform xy on [0,1] over unit disk [-1,1], with concentric mapping * to better preserve stratification for some RNG sequences */ ccl_device float2 concentric_sample_disk(float u1, float u2) @@ -193,112 +174,4 @@ ccl_device float2 regular_polygon_sample(float corners, float rotation, float u, return make_float2(cr * p.x - sr * p.y, sr * p.x + cr * p.y); } -ccl_device float3 ensure_valid_reflection(float3 Ng, float3 I, float3 N) -{ - float3 R = 2 * dot(N, I) * N - I; - - /* Reflection rays may always be at least as shallow as the incoming ray. */ - float threshold = min(0.9f * dot(Ng, I), 0.01f); - if (dot(Ng, R) >= threshold) { - return N; - } - - /* Form coordinate system with Ng as the Z axis and N inside the X-Z-plane. - * The X axis is found by normalizing the component of N that's orthogonal to Ng. - * The Y axis isn't actually needed. - */ - float NdotNg = dot(N, Ng); - float3 X = normalize(N - NdotNg * Ng); - - /* Keep math expressions. */ - /* clang-format off */ - /* Calculate N.z and N.x in the local coordinate system. - * - * The goal of this computation is to find a N' that is rotated towards Ng just enough - * to lift R' above the threshold (here called t), therefore dot(R', Ng) = t. - * - * According to the standard reflection equation, - * this means that we want dot(2*dot(N', I)*N' - I, Ng) = t. - * - * Since the Z axis of our local coordinate system is Ng, dot(x, Ng) is just x.z, so we get - * 2*dot(N', I)*N'.z - I.z = t. - * - * The rotation is simple to express in the coordinate system we formed - - * since N lies in the X-Z-plane, we know that N' will also lie in the X-Z-plane, - * so N'.y = 0 and therefore dot(N', I) = N'.x*I.x + N'.z*I.z . - * - * Furthermore, we want N' to be normalized, so N'.x = sqrt(1 - N'.z^2). - * - * With these simplifications, - * we get the final equation 2*(sqrt(1 - N'.z^2)*I.x + N'.z*I.z)*N'.z - I.z = t. - * - * The only unknown here is N'.z, so we can solve for that. - * - * The equation has four solutions in general: - * - * N'.z = +-sqrt(0.5*(+-sqrt(I.x^2*(I.x^2 + I.z^2 - t^2)) + t*I.z + I.x^2 + I.z^2)/(I.x^2 + I.z^2)) - * We can simplify this expression a bit by grouping terms: - * - * a = I.x^2 + I.z^2 - * b = sqrt(I.x^2 * (a - t^2)) - * c = I.z*t + a - * N'.z = +-sqrt(0.5*(+-b + c)/a) - * - * Two solutions can immediately be discarded because they're negative so N' would lie in the - * lower hemisphere. - */ - /* clang-format on */ - - float Ix = dot(I, X), Iz = dot(I, Ng); - float Ix2 = sqr(Ix), Iz2 = sqr(Iz); - float a = Ix2 + Iz2; - - float b = safe_sqrtf(Ix2 * (a - sqr(threshold))); - float c = Iz * threshold + a; - - /* Evaluate both solutions. - * In many cases one can be immediately discarded (if N'.z would be imaginary or larger than - * one), so check for that first. If no option is viable (might happen in extreme cases like N - * being in the wrong hemisphere), give up and return Ng. */ - float fac = 0.5f / a; - float N1_z2 = fac * (b + c), N2_z2 = fac * (-b + c); - bool valid1 = (N1_z2 > 1e-5f) && (N1_z2 <= (1.0f + 1e-5f)); - bool valid2 = (N2_z2 > 1e-5f) && (N2_z2 <= (1.0f + 1e-5f)); - - float2 N_new; - if (valid1 && valid2) { - /* If both are possible, do the expensive reflection-based check. */ - float2 N1 = make_float2(safe_sqrtf(1.0f - N1_z2), safe_sqrtf(N1_z2)); - float2 N2 = make_float2(safe_sqrtf(1.0f - N2_z2), safe_sqrtf(N2_z2)); - - float R1 = 2 * (N1.x * Ix + N1.y * Iz) * N1.y - Iz; - float R2 = 2 * (N2.x * Ix + N2.y * Iz) * N2.y - Iz; - - valid1 = (R1 >= 1e-5f); - valid2 = (R2 >= 1e-5f); - if (valid1 && valid2) { - /* If both solutions are valid, return the one with the shallower reflection since it will be - * closer to the input (if the original reflection wasn't shallow, we would not be in this - * part of the function). */ - N_new = (R1 < R2) ? N1 : N2; - } - else { - /* If only one reflection is valid (= positive), pick that one. */ - N_new = (R1 > R2) ? N1 : N2; - } - } - else if (valid1 || valid2) { - /* Only one solution passes the N'.z criterium, so pick that one. */ - float Nz2 = valid1 ? N1_z2 : N2_z2; - N_new = make_float2(safe_sqrtf(1.0f - Nz2), safe_sqrtf(Nz2)); - } - else { - return Ng; - } - - return N_new.x * X + N_new.y * Ng; -} - CCL_NAMESPACE_END - -#endif /* __KERNEL_MONTECARLO_CL__ */ diff --git a/intern/cycles/kernel/sample/mis.h b/intern/cycles/kernel/sample/mis.h new file mode 100644 index 00000000000..0878b3aac36 --- /dev/null +++ b/intern/cycles/kernel/sample/mis.h @@ -0,0 +1,64 @@ +/* + * Parts adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2011, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Multiple importance sampling utilities. */ + +ccl_device float balance_heuristic(float a, float b) +{ + return (a) / (a + b); +} + +ccl_device float balance_heuristic_3(float a, float b, float c) +{ + return (a) / (a + b + c); +} + +ccl_device float power_heuristic(float a, float b) +{ + return (a * a) / (a * a + b * b); +} + +ccl_device float power_heuristic_3(float a, float b, float c) +{ + return (a * a) / (a * a + b * b + c * c); +} + +ccl_device float max_heuristic(float a, float b) +{ + return (a > b) ? 1.0f : 0.0f; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/sample/pattern.h b/intern/cycles/kernel/sample/pattern.h new file mode 100644 index 00000000000..0c27992c7f6 --- /dev/null +++ b/intern/cycles/kernel/sample/pattern.h @@ -0,0 +1,185 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include "kernel/sample/jitter.h" +#include "util/hash.h" + +CCL_NAMESPACE_BEGIN + +/* Pseudo random numbers, uncomment this for debugging correlations. Only run + * this single threaded on a CPU for repeatable results. */ +//#define __DEBUG_CORRELATION__ + +/* High Dimensional Sobol. + * + * Multidimensional sobol with generator matrices. Dimension 0 and 1 are equal + * to classic Van der Corput and Sobol sequences. */ + +#ifdef __SOBOL__ + +/* Skip initial numbers that for some dimensions have clear patterns that + * don't cover the entire sample space. Ideally we would have a better + * progressive pattern that doesn't suffer from this problem, because even + * with this offset some dimensions are quite poor. + */ +# define SOBOL_SKIP 64 + +ccl_device uint sobol_dimension(KernelGlobals kg, int index, int dimension) +{ + uint result = 0; + uint i = index + SOBOL_SKIP; + for (int j = 0, x; (x = find_first_set(i)); i >>= x) { + j += x; + result ^= __float_as_uint(kernel_tex_fetch(__sample_pattern_lut, 32 * dimension + j - 1)); + } + return result; +} + +#endif /* __SOBOL__ */ + +ccl_device_forceinline float path_rng_1D(KernelGlobals kg, + uint rng_hash, + int sample, + int dimension) +{ +#ifdef __DEBUG_CORRELATION__ + return (float)drand48(); +#endif + +#ifdef __SOBOL__ + if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) +#endif + { + return pmj_sample_1D(kg, sample, rng_hash, dimension); + } + +#ifdef __SOBOL__ + /* Sobol sequence value using direction vectors. */ + uint result = sobol_dimension(kg, sample, dimension); + float r = (float)result * (1.0f / (float)0xFFFFFFFF); + + /* Cranly-Patterson rotation using rng seed */ + float shift; + + /* Hash rng with dimension to solve correlation issues. + * See T38710, T50116. + */ + uint tmp_rng = cmj_hash_simple(dimension, rng_hash); + shift = tmp_rng * (kernel_data.integrator.scrambling_distance / (float)0xFFFFFFFF); + + return r + shift - floorf(r + shift); +#endif +} + +ccl_device_forceinline void path_rng_2D(KernelGlobals kg, + uint rng_hash, + int sample, + int dimension, + ccl_private float *fx, + ccl_private float *fy) +{ +#ifdef __DEBUG_CORRELATION__ + *fx = (float)drand48(); + *fy = (float)drand48(); + return; +#endif + +#ifdef __SOBOL__ + if (kernel_data.integrator.sampling_pattern == SAMPLING_PATTERN_PMJ) +#endif + { + pmj_sample_2D(kg, sample, rng_hash, dimension, fx, fy); + + return; + } + +#ifdef __SOBOL__ + /* Sobol. */ + *fx = path_rng_1D(kg, rng_hash, sample, dimension); + *fy = path_rng_1D(kg, rng_hash, sample, dimension + 1); +#endif +} + +/** + * 1D hash recommended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh + * http://www.jcgt.org/published/0009/03/02/paper.pdf + */ +ccl_device_inline uint hash_iqint1(uint n) +{ + n = (n << 13U) ^ n; + n = n * (n * n * 15731U + 789221U) + 1376312589U; + + return n; +} + +/** + * 2D hash recommended from "Hash Functions for GPU Rendering" JCGT Vol. 9, No. 3, 2020 + * See https://www.shadertoy.com/view/4tXyWN and https://www.shadertoy.com/view/XlGcRh + * http://www.jcgt.org/published/0009/03/02/paper.pdf + */ +ccl_device_inline uint hash_iqnt2d(const uint x, const uint y) +{ + const uint qx = 1103515245U * ((x >> 1U) ^ (y)); + const uint qy = 1103515245U * ((y >> 1U) ^ (x)); + const uint n = 1103515245U * ((qx) ^ (qy >> 3U)); + + return n; +} + +ccl_device_inline uint path_rng_hash_init(KernelGlobals kg, + const int sample, + const int x, + const int y) +{ + const uint rng_hash = hash_iqnt2d(x, y) ^ kernel_data.integrator.seed; + +#ifdef __DEBUG_CORRELATION__ + srand48(rng_hash + sample); +#else + (void)sample; +#endif + + return rng_hash; +} + +ccl_device_inline bool sample_is_even(int pattern, int sample) +{ + if (pattern == SAMPLING_PATTERN_PMJ) { + /* See Section 10.2.1, "Progressive Multi-Jittered Sample Sequences", Christensen et al. + * We can use this to get divide sample sequence into two classes for easier variance + * estimation. */ +#if defined(__GNUC__) && !defined(__KERNEL_GPU__) + return __builtin_popcount(sample & 0xaaaaaaaa) & 1; +#elif defined(__NVCC__) + return __popc(sample & 0xaaaaaaaa) & 1; +#else + /* TODO(Stefan): pop-count intrinsic for Windows with fallback for older CPUs. */ + int i = sample & 0xaaaaaaaa; + i = i - ((i >> 1) & 0x55555555); + i = (i & 0x33333333) + ((i >> 2) & 0x33333333); + i = (((i + (i >> 4)) & 0xF0F0F0F) * 0x1010101) >> 24; + return i & 1; +#endif + } + else { + /* TODO(Stefan): Are there reliable ways of dividing CMJ and Sobol into two classes? */ + return sample & 0x1; + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_adaptive_adjust_samples.h b/intern/cycles/kernel/split/kernel_adaptive_adjust_samples.h deleted file mode 100644 index 437a5c9581b..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_adjust_samples.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_adjust_samples(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.w * kernel_split_params.tile.h) { - int x = kernel_split_params.tile.x + pixel_index % kernel_split_params.tile.w; - int y = kernel_split_params.tile.y + pixel_index / kernel_split_params.tile.w; - int buffer_offset = (kernel_split_params.tile.offset + x + - y * kernel_split_params.tile.stride) * - kernel_data.film.pass_stride; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - int sample = kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples; - if (buffer[kernel_data.film.pass_sample_count] < 0.0f) { - buffer[kernel_data.film.pass_sample_count] = -buffer[kernel_data.film.pass_sample_count]; - float sample_multiplier = sample / buffer[kernel_data.film.pass_sample_count]; - if (sample_multiplier != 1.0f) { - kernel_adaptive_post_adjust(kg, buffer, sample_multiplier); - } - } - else { - kernel_adaptive_post_adjust(kg, buffer, sample / (sample - 1.0f)); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_adaptive_filter_x.h b/intern/cycles/kernel/split/kernel_adaptive_filter_x.h deleted file mode 100644 index 93f41f7ced4..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_filter_x.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_filter_x(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.h && - kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples >= - kernel_data.integrator.adaptive_min_samples) { - int y = kernel_split_params.tile.y + pixel_index; - kernel_do_adaptive_filter_x(kg, y, &kernel_split_params.tile); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_adaptive_filter_y.h b/intern/cycles/kernel/split/kernel_adaptive_filter_y.h deleted file mode 100644 index eca53d079ec..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_filter_y.h +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_filter_y(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.w && - kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples >= - kernel_data.integrator.adaptive_min_samples) { - int x = kernel_split_params.tile.x + pixel_index; - kernel_do_adaptive_filter_y(kg, x, &kernel_split_params.tile); - } -} -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_adaptive_stopping.h b/intern/cycles/kernel/split/kernel_adaptive_stopping.h deleted file mode 100644 index c8eb1ebd705..00000000000 --- a/intern/cycles/kernel/split/kernel_adaptive_stopping.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_adaptive_stopping(KernelGlobals *kg) -{ - int pixel_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (pixel_index < kernel_split_params.tile.w * kernel_split_params.tile.h && - kernel_split_params.tile.start_sample + kernel_split_params.tile.num_samples >= - kernel_data.integrator.adaptive_min_samples) { - int x = kernel_split_params.tile.x + pixel_index % kernel_split_params.tile.w; - int y = kernel_split_params.tile.y + pixel_index / kernel_split_params.tile.w; - int buffer_offset = (kernel_split_params.tile.offset + x + - y * kernel_split_params.tile.stride) * - kernel_data.film.pass_stride; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - kernel_do_adaptive_stopping(kg, - buffer, - kernel_split_params.tile.start_sample + - kernel_split_params.tile.num_samples - 1); - } -} -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_branched.h b/intern/cycles/kernel/split/kernel_branched.h deleted file mode 100644 index 45f5037d321..00000000000 --- a/intern/cycles/kernel/split/kernel_branched.h +++ /dev/null @@ -1,231 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#ifdef __BRANCHED_PATH__ - -/* sets up the various state needed to do an indirect loop */ -ccl_device_inline void kernel_split_branched_path_indirect_loop_init(KernelGlobals *kg, - int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - /* save a copy of the state to restore later */ -# define BRANCHED_STORE(name) branched_state->name = kernel_split_state.name[ray_index]; - - BRANCHED_STORE(path_state); - BRANCHED_STORE(throughput); - BRANCHED_STORE(ray); - BRANCHED_STORE(isect); - BRANCHED_STORE(ray_state); - - *kernel_split_sd(branched_state_sd, ray_index) = *kernel_split_sd(sd, ray_index); - for (int i = 0; i < kernel_split_sd(branched_state_sd, ray_index)->num_closure; i++) { - kernel_split_sd(branched_state_sd, ray_index)->closure[i] = - kernel_split_sd(sd, ray_index)->closure[i]; - } - -# undef BRANCHED_STORE - - /* Set loop counters to initial position. */ - branched_state->next_closure = 0; - branched_state->next_sample = 0; -} - -/* ends an indirect loop and restores the previous state */ -ccl_device_inline void kernel_split_branched_path_indirect_loop_end(KernelGlobals *kg, - int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - /* restore state */ -# define BRANCHED_RESTORE(name) kernel_split_state.name[ray_index] = branched_state->name; - - BRANCHED_RESTORE(path_state); - BRANCHED_RESTORE(throughput); - BRANCHED_RESTORE(ray); - BRANCHED_RESTORE(isect); - BRANCHED_RESTORE(ray_state); - - *kernel_split_sd(sd, ray_index) = *kernel_split_sd(branched_state_sd, ray_index); - for (int i = 0; i < kernel_split_sd(branched_state_sd, ray_index)->num_closure; i++) { - kernel_split_sd(sd, ray_index)->closure[i] = - kernel_split_sd(branched_state_sd, ray_index)->closure[i]; - } - -# undef BRANCHED_RESTORE - - /* leave indirect loop */ - REMOVE_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_INDIRECT); -} - -ccl_device_inline bool kernel_split_branched_indirect_start_shared(KernelGlobals *kg, - int ray_index) -{ - ccl_global char *ray_state = kernel_split_state.ray_state; - - int inactive_ray = dequeue_ray_index(QUEUE_INACTIVE_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - kernel_split_params.queue_index); - - if (!IS_STATE(ray_state, inactive_ray, RAY_INACTIVE)) { - return false; - } - -# define SPLIT_DATA_ENTRY(type, name, num) \ - if (num) { \ - kernel_split_state.name[inactive_ray] = kernel_split_state.name[ray_index]; \ - } - SPLIT_DATA_ENTRIES_BRANCHED_SHARED -# undef SPLIT_DATA_ENTRY - - *kernel_split_sd(sd, inactive_ray) = *kernel_split_sd(sd, ray_index); - for (int i = 0; i < kernel_split_sd(sd, ray_index)->num_closure; i++) { - kernel_split_sd(sd, inactive_ray)->closure[i] = kernel_split_sd(sd, ray_index)->closure[i]; - } - - kernel_split_state.branched_state[inactive_ray].shared_sample_count = 0; - kernel_split_state.branched_state[inactive_ray].original_ray = ray_index; - kernel_split_state.branched_state[inactive_ray].waiting_on_shared_samples = false; - - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - PathRadiance *inactive_L = &kernel_split_state.path_radiance[inactive_ray]; - - path_radiance_init(kg, inactive_L); - path_radiance_copy_indirect(inactive_L, L); - - ray_state[inactive_ray] = RAY_REGENERATED; - ADD_RAY_FLAG(ray_state, inactive_ray, RAY_BRANCHED_INDIRECT_SHARED); - ADD_RAY_FLAG(ray_state, inactive_ray, IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)); - - atomic_fetch_and_inc_uint32( - (ccl_global uint *)&kernel_split_state.branched_state[ray_index].shared_sample_count); - - return true; -} - -/* bounce off surface and integrate indirect light */ -ccl_device_noinline bool kernel_split_branched_path_surface_indirect_light_iter( - KernelGlobals *kg, - int ray_index, - float num_samples_adjust, - ShaderData *saved_sd, - bool reset_path_state, - bool wait_for_shared) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - ShaderData *sd = saved_sd; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - float3 throughput = branched_state->throughput; - ccl_global PathState *ps = &kernel_split_state.path_state[ray_index]; - - float sum_sample_weight = 0.0f; -# ifdef __DENOISING_FEATURES__ - if (ps->denoising_feature_weight > 0.0f) { - for (int i = 0; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - /* transparency is not handled here, but in outer loop */ - if (!CLOSURE_IS_BSDF(sc->type) || CLOSURE_IS_BSDF_TRANSPARENT(sc->type)) { - continue; - } - - sum_sample_weight += sc->sample_weight; - } - } - else { - sum_sample_weight = 1.0f; - } -# endif /* __DENOISING_FEATURES__ */ - - for (int i = branched_state->next_closure; i < sd->num_closure; i++) { - const ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSDF(sc->type)) - continue; - /* transparency is not handled here, but in outer loop */ - if (sc->type == CLOSURE_BSDF_TRANSPARENT_ID) - continue; - - int num_samples; - - if (CLOSURE_IS_BSDF_DIFFUSE(sc->type)) - num_samples = kernel_data.integrator.diffuse_samples; - else if (CLOSURE_IS_BSDF_BSSRDF(sc->type)) - num_samples = 1; - else if (CLOSURE_IS_BSDF_GLOSSY(sc->type)) - num_samples = kernel_data.integrator.glossy_samples; - else - num_samples = kernel_data.integrator.transmission_samples; - - num_samples = ceil_to_int(num_samples_adjust * num_samples); - - float num_samples_inv = num_samples_adjust / num_samples; - - for (int j = branched_state->next_sample; j < num_samples; j++) { - if (reset_path_state) { - *ps = branched_state->path_state; - } - - ps->rng_hash = cmj_hash(branched_state->path_state.rng_hash, i); - - ccl_global float3 *tp = &kernel_split_state.throughput[ray_index]; - *tp = throughput; - - ccl_global Ray *bsdf_ray = &kernel_split_state.ray[ray_index]; - - if (!kernel_branched_path_surface_bounce( - kg, sd, sc, j, num_samples, tp, ps, &L->state, bsdf_ray, sum_sample_weight)) { - continue; - } - - ps->rng_hash = branched_state->path_state.rng_hash; - - /* update state for next iteration */ - branched_state->next_closure = i; - branched_state->next_sample = j + 1; - - /* start the indirect path */ - *tp *= num_samples_inv; - - if (kernel_split_branched_indirect_start_shared(kg, ray_index)) { - continue; - } - - return true; - } - - branched_state->next_sample = 0; - } - - branched_state->next_closure = sd->num_closure; - - if (wait_for_shared) { - branched_state->waiting_on_shared_samples = (branched_state->shared_sample_count > 0); - if (branched_state->waiting_on_shared_samples) { - return true; - } - } - - return false; -} - -#endif /* __BRANCHED_PATH__ */ - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_buffer_update.h b/intern/cycles/kernel/split/kernel_buffer_update.h deleted file mode 100644 index b96feca582f..00000000000 --- a/intern/cycles/kernel/split/kernel_buffer_update.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of rays that hit the background (sceneintersect - * kernel), and for the rays of state RAY_UPDATE_BUFFER it updates the ray's - * accumulated radiance in the output buffer. This kernel also takes care of - * rays that have been determined to-be-regenerated. - * - * We will empty QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue in this kernel. - * - * Typically all rays that are in state RAY_HIT_BACKGROUND, RAY_UPDATE_BUFFER - * will be eventually set to RAY_TO_REGENERATE state in this kernel. - * Finally all rays of ray_state RAY_TO_REGENERATE will be regenerated and put - * in queue QUEUE_ACTIVE_AND_REGENERATED_RAYS. - * - * State of queues when this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND, RAY_TO_REGENERATE rays. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and - * RAY_REGENERATED rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be empty. - */ -ccl_device void kernel_buffer_update(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (ray_index == 0) { - /* We will empty this queue in this kernel. */ - kernel_split_params.queue_index[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = 0; - } - char enqueue_flag = 0; - ray_index = get_ray_index(kg, - ray_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (ray_index != QUEUE_EMPTY_SLOT) { - ccl_global char *ray_state = kernel_split_state.ray_state; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - bool ray_was_updated = false; - - if (IS_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER)) { - ray_was_updated = true; - uint sample = state->sample; - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - /* accumulate result in output buffer */ - kernel_write_result(kg, buffer, sample, L); - - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_TO_REGENERATE); - } - - if (kernel_data.film.cryptomatte_passes) { - /* Make sure no thread is writing to the buffers. */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ray_was_updated && state->sample - 1 == kernel_data.integrator.aa_samples) { - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - ccl_global float *cryptomatte_buffer = buffer + kernel_data.film.pass_cryptomatte; - kernel_sort_id_slots(cryptomatte_buffer, 2 * kernel_data.film.cryptomatte_depth); - } - } - - if (IS_STATE(ray_state, ray_index, RAY_TO_REGENERATE)) { - /* We have completed current work; So get next work */ - ccl_global uint *work_pools = kernel_split_params.work_pools; - uint total_work_size = kernel_split_params.total_work_size; - uint work_index; - - if (!get_next_work(kg, work_pools, total_work_size, ray_index, &work_index)) { - /* If work is invalid, this means no more work is available and the thread may exit */ - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_INACTIVE); - } - - if (IS_STATE(ray_state, ray_index, RAY_TO_REGENERATE)) { - ccl_global WorkTile *tile = &kernel_split_params.tile; - uint x, y, sample; - get_work_pixel(tile, work_index, &x, &y, &sample); - - /* Store buffer offset for writing to passes. */ - uint buffer_offset = (tile->offset + x + y * tile->stride) * kernel_data.film.pass_stride; - kernel_split_state.buffer_offset[ray_index] = buffer_offset; - - /* Initialize random numbers and ray. */ - uint rng_hash; - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, ray); - - if (ray->t != 0.0f) { - /* Initialize throughput, path radiance, Ray, PathState; - * These rays proceed with path-iteration. - */ - *throughput = make_float3(1.0f, 1.0f, 1.0f); - path_radiance_init(kg, L); - path_state_init(kg, - AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]), - state, - rng_hash, - sample, - ray); -#ifdef __SUBSURFACE__ - kernel_path_subsurface_init_indirect(&kernel_split_state.ss_rays[ray_index]); -#endif - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - enqueue_flag = 1; - } - else { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_TO_REGENERATE); - } - } - } - } - - /* Enqueue RAY_REGENERATED rays into QUEUE_ACTIVE_AND_REGENERATED_RAYS; - * These rays will be made active during next SceneIntersectkernel. - */ - enqueue_ray_index_local(ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_data_init.h b/intern/cycles/kernel/split/kernel_data_init.h deleted file mode 100644 index 2f83a10316d..00000000000 --- a/intern/cycles/kernel/split/kernel_data_init.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel Initializes structures needed in path-iteration kernels. - * - * Note on Queues: - * All slots in queues are initialized to queue empty slot; - * The number of elements in the queues is initialized to 0; - */ - -#ifndef __KERNEL_CPU__ -ccl_device void kernel_data_init( -#else -void KERNEL_FUNCTION_FULL_NAME(data_init)( -#endif - KernelGlobals *kg, - ccl_constant KernelData *data, - ccl_global void *split_data_buffer, - int num_elements, - ccl_global char *ray_state, - -#ifdef __KERNEL_OPENCL__ - KERNEL_BUFFER_PARAMS, -#endif - - int start_sample, - int end_sample, - int sx, - int sy, - int sw, - int sh, - int offset, - int stride, - ccl_global int *Queue_index, /* Tracks the number of elements in queues */ - int queuesize, /* size (capacity) of the queue */ - ccl_global char *use_queues_flag, /* flag to decide if scene-intersect kernel should use queues - to fetch ray index */ - ccl_global unsigned int *work_pools, /* Work pool for each work group */ - unsigned int num_samples, - ccl_global float *buffer) -{ -#ifdef KERNEL_STUB - STUB_ASSERT(KERNEL_ARCH, data_init); -#else - -# ifdef __KERNEL_OPENCL__ - kg->data = data; -# endif - - kernel_split_params.tile.x = sx; - kernel_split_params.tile.y = sy; - kernel_split_params.tile.w = sw; - kernel_split_params.tile.h = sh; - - kernel_split_params.tile.start_sample = start_sample; - kernel_split_params.tile.num_samples = num_samples; - - kernel_split_params.tile.offset = offset; - kernel_split_params.tile.stride = stride; - - kernel_split_params.tile.buffer = buffer; - - kernel_split_params.total_work_size = sw * sh * num_samples; - - kernel_split_params.work_pools = work_pools; - - kernel_split_params.queue_index = Queue_index; - kernel_split_params.queue_size = queuesize; - kernel_split_params.use_queues_flag = use_queues_flag; - - split_data_init(kg, &kernel_split_state, num_elements, split_data_buffer, ray_state); - -# ifdef __KERNEL_OPENCL__ - kernel_set_buffer_pointers(kg, KERNEL_BUFFER_ARGS); - kernel_set_buffer_info(kg); -# endif - - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - /* Initialize queue data and queue index. */ - if (thread_index < queuesize) { - for (int i = 0; i < NUM_QUEUES; i++) { - kernel_split_state.queue_data[i * queuesize + thread_index] = QUEUE_EMPTY_SLOT; - } - } - - if (thread_index == 0) { - for (int i = 0; i < NUM_QUEUES; i++) { - Queue_index[i] = 0; - } - - /* The scene-intersect kernel should not use the queues very first time. - * since the queue would be empty. - */ - *use_queues_flag = 0; - } -#endif /* KERENL_STUB */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_direct_lighting.h b/intern/cycles/kernel/split/kernel_direct_lighting.h deleted file mode 100644 index 3be2b35812f..00000000000 --- a/intern/cycles/kernel/split/kernel_direct_lighting.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of direct lighting logic. - * However, the "shadow ray cast" part of direct lighting is handled - * in the next kernel. - * - * This kernels determines the rays for which a shadow_blocked() function - * associated with direct lighting should be executed. Those rays for which - * a shadow_blocked() function for direct-lighting must be executed, are - * marked with flag RAY_SHADOW_RAY_CAST_DL and enqueued into the queue - * QUEUE_SHADOW_RAY_CAST_DL_RAYS - * - * Note on Queues: - * This kernel only reads from the QUEUE_ACTIVE_AND_REGENERATED_RAYS queue - * and processes only the rays of state RAY_ACTIVE; If a ray needs to execute - * the corresponding shadow_blocked part, after direct lighting, the ray is - * marked with RAY_SHADOW_RAY_CAST_DL flag. - * - * State of queues when this kernel is called: - * - State of queues QUEUE_ACTIVE_AND_REGENERATED_RAYS and - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be same before and after this - * kernel call. - * - QUEUE_SHADOW_RAY_CAST_DL_RAYS queue will be filled with rays for which a - * shadow_blocked function must be executed, after this kernel call - * Before this kernel call the QUEUE_SHADOW_RAY_CAST_DL_RAYS will be empty. - */ -ccl_device void kernel_direct_lighting(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - char enqueue_flag = 0; - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - /* direct lighting */ -#ifdef __EMISSION__ - bool flag = (kernel_data.integrator.use_direct_light && (sd->flag & SD_BSDF_HAS_EVAL)); - -# ifdef __BRANCHED_PATH__ - if (flag && kernel_data.integrator.branched) { - flag = false; - enqueue_flag = 1; - } -# endif /* __BRANCHED_PATH__ */ - -# ifdef __SHADOW_TRICKS__ - if (flag && state->flag & PATH_RAY_SHADOW_CATCHER) { - flag = false; - enqueue_flag = 1; - } -# endif /* __SHADOW_TRICKS__ */ - - if (flag) { - /* Sample illumination from lights to find path contribution. */ - float light_u, light_v; - path_state_rng_2D(kg, state, PRNG_LIGHT_U, &light_u, &light_v); - float terminate = path_state_rng_light_termination(kg, state); - - LightSample ls; - if (light_sample(kg, -1, light_u, light_v, sd->time, sd->P, state->bounce, &ls)) { - Ray light_ray; - light_ray.time = sd->time; - - BsdfEval L_light; - bool is_lamp; - if (direct_emission(kg, - sd, - AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]), - &ls, - state, - &light_ray, - &L_light, - &is_lamp, - terminate)) { - /* Write intermediate data to global memory to access from - * the next kernel. - */ - kernel_split_state.light_ray[ray_index] = light_ray; - kernel_split_state.bsdf_eval[ray_index] = L_light; - kernel_split_state.is_lamp[ray_index] = is_lamp; - /* Mark ray state for next shadow kernel. */ - enqueue_flag = 1; - } - } - } -#endif /* __EMISSION__ */ - } - -#ifdef __EMISSION__ - /* Enqueue RAY_SHADOW_RAY_CAST_DL rays. */ - enqueue_ray_index_local(ray_index, - QUEUE_SHADOW_RAY_CAST_DL_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -#endif - -#ifdef __BRANCHED_PATH__ - /* Enqueue RAY_LIGHT_INDIRECT_NEXT_ITER rays - * this is the last kernel before next_iteration_setup that uses local atomics so we do this here - */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - enqueue_ray_index_local( - ray_index, - QUEUE_LIGHT_INDIRECT_ITER, - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_LIGHT_INDIRECT_NEXT_ITER), - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - -#endif /* __BRANCHED_PATH__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_do_volume.h b/intern/cycles/kernel/split/kernel_do_volume.h deleted file mode 100644 index 1775e870f07..00000000000 --- a/intern/cycles/kernel/split/kernel_do_volume.h +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#if defined(__BRANCHED_PATH__) && defined(__VOLUME__) - -ccl_device_inline void kernel_split_branched_path_volume_indirect_light_init(KernelGlobals *kg, - int ray_index) -{ - kernel_split_branched_path_indirect_loop_init(kg, ray_index); - - ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_VOLUME_INDIRECT); -} - -ccl_device_noinline bool kernel_split_branched_path_volume_indirect_light_iter(KernelGlobals *kg, - int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - ShaderData *sd = kernel_split_sd(sd, ray_index); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - /* GPU: no decoupled ray marching, scatter probabilistically. */ - int num_samples = kernel_data.integrator.volume_samples; - float num_samples_inv = 1.0f / num_samples; - - Ray volume_ray = branched_state->ray; - volume_ray.t = (!IS_STATE(&branched_state->ray_state, 0, RAY_HIT_BACKGROUND)) ? - branched_state->isect.t : - FLT_MAX; - - float step_size = volume_stack_step_size(kg, branched_state->path_state.volume_stack); - - for (int j = branched_state->next_sample; j < num_samples; j++) { - ccl_global PathState *ps = &kernel_split_state.path_state[ray_index]; - *ps = branched_state->path_state; - - ccl_global Ray *pray = &kernel_split_state.ray[ray_index]; - *pray = branched_state->ray; - - ccl_global float3 *tp = &kernel_split_state.throughput[ray_index]; - *tp = branched_state->throughput * num_samples_inv; - - /* branch RNG state */ - path_state_branch(ps, j, num_samples); - - /* integrate along volume segment with distance sampling */ - VolumeIntegrateResult result = kernel_volume_integrate( - kg, ps, sd, &volume_ray, L, tp, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* direct lighting */ - kernel_path_volume_connect_light(kg, sd, emission_sd, *tp, &branched_state->path_state, L); - - /* indirect light bounce */ - if (!kernel_path_volume_bounce(kg, sd, tp, ps, &L->state, pray)) { - continue; - } - - /* start the indirect path */ - branched_state->next_closure = 0; - branched_state->next_sample = j + 1; - - /* Attempting to share too many samples is slow for volumes as it causes us to - * loop here more and have many calls to kernel_volume_integrate which evaluates - * shaders. The many expensive shader evaluations cause the work load to become - * unbalanced and many threads to become idle in this kernel. Limiting the - * number of shared samples here helps quite a lot. - */ - if (branched_state->shared_sample_count < 2) { - if (kernel_split_branched_indirect_start_shared(kg, ray_index)) { - continue; - } - } - - return true; - } -# endif - } - - branched_state->next_sample = num_samples; - - branched_state->waiting_on_shared_samples = (branched_state->shared_sample_count > 0); - if (branched_state->waiting_on_shared_samples) { - return true; - } - - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - - /* todo: avoid this calculation using decoupled ray marching */ - float3 throughput = kernel_split_state.throughput[ray_index]; - kernel_volume_shadow( - kg, emission_sd, &kernel_split_state.path_state[ray_index], &volume_ray, &throughput); - kernel_split_state.throughput[ray_index] = throughput; - - return false; -} - -#endif /* __BRANCHED_PATH__ && __VOLUME__ */ - -ccl_device void kernel_do_volume(KernelGlobals *kg) -{ -#ifdef __VOLUME__ - /* We will empty this queue in this kernel. */ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; -# ifdef __BRANCHED_PATH__ - kernel_split_params.queue_index[QUEUE_VOLUME_INDIRECT_ITER] = 0; -# endif /* __BRANCHED_PATH__ */ - } - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - if (*kernel_split_params.use_queues_flag) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - } - - ccl_global char *ray_state = kernel_split_state.ray_state; - - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE) || - IS_STATE(ray_state, ray_index, RAY_HIT_BACKGROUND)) { - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global Intersection *isect = &kernel_split_state.isect[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - bool hit = !IS_STATE(ray_state, ray_index, RAY_HIT_BACKGROUND); - - /* Sanitize volume stack. */ - if (!hit) { - kernel_volume_clean_stack(kg, state->volume_stack); - } - /* volume attenuation, emission, scatter */ - if (state->volume_stack[0].shader != SHADER_NONE) { - Ray volume_ray = *ray; - volume_ray.t = (hit) ? isect->t : FLT_MAX; - -# ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || - IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -# endif /* __BRANCHED_PATH__ */ - float step_size = volume_stack_step_size(kg, state->volume_stack); - - { - /* integrate along volume segment with distance sampling */ - VolumeIntegrateResult result = kernel_volume_integrate( - kg, state, sd, &volume_ray, L, throughput, step_size); - -# ifdef __VOLUME_SCATTER__ - if (result == VOLUME_PATH_SCATTERED) { - /* direct lighting */ - kernel_path_volume_connect_light(kg, sd, emission_sd, *throughput, state, L); - - /* indirect light bounce */ - if (kernel_path_volume_bounce(kg, sd, throughput, state, &L->state, ray)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - else { - kernel_split_path_end(kg, ray_index); - } - } -# endif /* __VOLUME_SCATTER__ */ - } - -# ifdef __BRANCHED_PATH__ - } - else { - kernel_split_branched_path_volume_indirect_light_init(kg, ray_index); - - if (kernel_split_branched_path_volume_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif /* __BRANCHED_PATH__ */ - } - } - -# ifdef __BRANCHED_PATH__ - /* iter loop */ - ray_index = get_ray_index(kg, - ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0), - QUEUE_VOLUME_INDIRECT_ITER, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (IS_STATE(ray_state, ray_index, RAY_VOLUME_INDIRECT_NEXT_ITER)) { - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(&kernel_split_state.path_radiance[ray_index]); - path_radiance_reset_indirect(&kernel_split_state.path_radiance[ray_index]); - - if (kernel_split_branched_path_volume_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif /* __BRANCHED_PATH__ */ - -#endif /* __VOLUME__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_enqueue_inactive.h b/intern/cycles/kernel/split/kernel_enqueue_inactive.h deleted file mode 100644 index 745313f89f1..00000000000 --- a/intern/cycles/kernel/split/kernel_enqueue_inactive.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_enqueue_inactive(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ -#ifdef __BRANCHED_PATH__ - /* Enqueue RAY_INACTIVE rays into QUEUE_INACTIVE_RAYS queue. */ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - char enqueue_flag = 0; - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_INACTIVE)) { - enqueue_flag = 1; - } - - enqueue_ray_index_local(ray_index, - QUEUE_INACTIVE_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -#endif /* __BRANCHED_PATH__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h b/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h deleted file mode 100644 index 61722840b0b..00000000000 --- a/intern/cycles/kernel/split/kernel_holdout_emission_blurring_pathtermination_ao.h +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of the logic to process "material of type holdout", - * indirect primitive emission, bsdf blurring, probabilistic path termination - * and AO. - * - * This kernels determines the rays for which a shadow_blocked() function - * associated with AO should be executed. Those rays for which a - * shadow_blocked() function for AO must be executed are marked with flag - * RAY_SHADOW_RAY_CAST_ao and enqueued into the queue - * QUEUE_SHADOW_RAY_CAST_AO_RAYS - * - * Ray state of rays that are terminated in this kernel are changed to RAY_UPDATE_BUFFER - * - * Note on Queues: - * This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS - * and processes only the rays of state RAY_ACTIVE. - * There are different points in this kernel where a ray may terminate and - * reach RAY_UPDATE_BUFFER state. These rays are enqueued into - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will still be present - * in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has - * been changed to RAY_UPDATE_BUFFER, there is no problem. - * - * State of queues when this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE and - * RAY_REGENERATED rays - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE rays. - * - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be empty. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, - * RAY_REGENERATED and RAY_UPDATE_BUFFER rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays. - * - QUEUE_SHADOW_RAY_CAST_AO_RAYS will be filled with rays marked with - * flag RAY_SHADOW_RAY_CAST_AO - */ - -ccl_device void kernel_holdout_emission_blurring_pathtermination_ao( - KernelGlobals *kg, ccl_local_param BackgroundAOLocals *locals) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - locals->queue_atomics_bg = 0; - locals->queue_atomics_ao = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - -#ifdef __AO__ - char enqueue_flag = 0; -#endif - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index != QUEUE_EMPTY_SLOT) { - ccl_global PathState *state = 0x0; - float3 throughput; - - ccl_global char *ray_state = kernel_split_state.ray_state; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - - throughput = kernel_split_state.throughput[ray_index]; - state = &kernel_split_state.path_state[ray_index]; - - if (!kernel_path_shader_apply(kg, sd, state, ray, throughput, emission_sd, L, buffer)) { - kernel_split_path_end(kg, ray_index); - } - } - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - /* Path termination. this is a strange place to put the termination, it's - * mainly due to the mixed in MIS that we use. gives too many unneeded - * shader evaluations, only need emission if we are going to terminate. - */ - float probability = path_state_continuation_probability(kg, state, throughput); - - if (probability == 0.0f) { - kernel_split_path_end(kg, ray_index); - } - else if (probability < 1.0f) { - float terminate = path_state_rng_1D(kg, state, PRNG_TERMINATE); - if (terminate >= probability) { - kernel_split_path_end(kg, ray_index); - } - else { - kernel_split_state.throughput[ray_index] = throughput / probability; - } - } - -#ifdef __DENOISING_FEATURES__ - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - kernel_update_denoising_features(kg, sd, state, L); - } -#endif - } - -#ifdef __AO__ - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - /* ambient occlusion */ - if (kernel_data.integrator.use_ambient_occlusion) { - enqueue_flag = 1; - } - } -#endif /* __AO__ */ - } - -#ifdef __AO__ - /* Enqueue to-shadow-ray-cast rays. */ - enqueue_ray_index_local(ray_index, - QUEUE_SHADOW_RAY_CAST_AO_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - &locals->queue_atomics_ao, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -#endif -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_indirect_background.h b/intern/cycles/kernel/split/kernel_indirect_background.h deleted file mode 100644 index 6d500650cc0..00000000000 --- a/intern/cycles/kernel/split/kernel_indirect_background.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_indirect_background(KernelGlobals *kg) -{ - ccl_global char *ray_state = kernel_split_state.ray_state; - - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - int ray_index; - - if (kernel_data.integrator.ao_bounces != INT_MAX) { - ray_index = get_ray_index(kg, - thread_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index != QUEUE_EMPTY_SLOT) { - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - if (path_state_ao_bounce(kg, state)) { - kernel_split_path_end(kg, ray_index); - } - } - } - } - - ray_index = get_ray_index(kg, - thread_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - if (IS_STATE(ray_state, ray_index, RAY_HIT_BACKGROUND)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - float3 throughput = kernel_split_state.throughput[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - kernel_path_background(kg, state, ray, throughput, sd, buffer, L); - kernel_split_path_end(kg, ray_index); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_indirect_subsurface.h b/intern/cycles/kernel/split/kernel_indirect_subsurface.h deleted file mode 100644 index 3f48f8d6f56..00000000000 --- a/intern/cycles/kernel/split/kernel_indirect_subsurface.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_indirect_subsurface(KernelGlobals *kg) -{ - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index == 0) { - /* We will empty both queues in this kernel. */ - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; - kernel_split_params.queue_index[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = 0; - } - - int ray_index; - get_ray_index(kg, - thread_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - ray_index = get_ray_index(kg, - thread_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - -#ifdef __SUBSURFACE__ - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - ccl_global char *ray_state = kernel_split_state.ray_state; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - - if (IS_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER)) { - ccl_addr_space SubsurfaceIndirectRays *ss_indirect = &kernel_split_state.ss_rays[ray_index]; - - /* Trace indirect subsurface rays by restarting the loop. this uses less - * stack memory than invoking kernel_path_indirect. - */ - if (ss_indirect->num_rays) { - kernel_path_subsurface_setup_indirect(kg, ss_indirect, state, ray, L, throughput); - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -#endif /* __SUBSURFACE__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_lamp_emission.h b/intern/cycles/kernel/split/kernel_lamp_emission.h deleted file mode 100644 index 7ecb099208d..00000000000 --- a/intern/cycles/kernel/split/kernel_lamp_emission.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel operates on QUEUE_ACTIVE_AND_REGENERATED_RAYS. - * It processes rays of state RAY_ACTIVE and RAY_HIT_BACKGROUND. - * We will empty QUEUE_ACTIVE_AND_REGENERATED_RAYS queue in this kernel. - */ -ccl_device void kernel_lamp_emission(KernelGlobals *kg) -{ -#ifndef __VOLUME__ - /* We will empty this queue in this kernel. */ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; - } -#endif - /* Fetch use_queues_flag. */ - char local_use_queues_flag = *kernel_split_params.use_queues_flag; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (local_use_queues_flag) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, -#ifndef __VOLUME__ - 1 -#else - 0 -#endif - ); - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - } - - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HIT_BACKGROUND)) { - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - - float3 throughput = kernel_split_state.throughput[ray_index]; - Ray ray = kernel_split_state.ray[ray_index]; - ccl_global Intersection *isect = &kernel_split_state.isect[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - kernel_path_lamp_emission(kg, state, &ray, throughput, isect, sd, L); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_next_iteration_setup.h b/intern/cycles/kernel/split/kernel_next_iteration_setup.h deleted file mode 100644 index 320f6a414bf..00000000000 --- a/intern/cycles/kernel/split/kernel_next_iteration_setup.h +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/*This kernel takes care of setting up ray for the next iteration of - * path-iteration and accumulating radiance corresponding to AO and - * direct-lighting - * - * Ray state of rays that are terminated in this kernel are changed - * to RAY_UPDATE_BUFFER. - * - * Note on queues: - * This kernel fetches rays from the queue QUEUE_ACTIVE_AND_REGENERATED_RAYS - * and processes only the rays of state RAY_ACTIVE. - * There are different points in this kernel where a ray may terminate and - * reach RAY_UPDATE_BUFF state. These rays are enqueued into - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. These rays will still be present - * in QUEUE_ACTIVE_AND_REGENERATED_RAYS queue, but since their ray-state has - * been changed to RAY_UPDATE_BUFF, there is no problem. - * - * State of queues when this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, - * RAY_REGENERATED, RAY_UPDATE_BUFFER rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE and RAY_UPDATE_BUFFER rays. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE, - * RAY_REGENERATED and more RAY_UPDATE_BUFFER rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE and more RAY_UPDATE_BUFFER rays. - */ - -#ifdef __BRANCHED_PATH__ -ccl_device_inline void kernel_split_branched_indirect_light_init(KernelGlobals *kg, int ray_index) -{ - kernel_split_branched_path_indirect_loop_init(kg, ray_index); - - ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_LIGHT_INDIRECT); -} - -ccl_device void kernel_split_branched_transparent_bounce(KernelGlobals *kg, int ray_index) -{ - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - -# ifdef __VOLUME__ - if (!(sd->flag & SD_HAS_ONLY_VOLUME)) { -# endif - /* continue in case of transparency */ - *throughput *= shader_bsdf_transparency(kg, sd); - - if (is_zero(*throughput)) { - kernel_split_path_end(kg, ray_index); - return; - } - - /* Update Path State */ - path_state_next(kg, state, LABEL_TRANSPARENT); -# ifdef __VOLUME__ - } - else { - if (!path_state_volume_next(kg, state)) { - kernel_split_path_end(kg, ray_index); - return; - } - } -# endif - - ray->P = ray_offset(sd->P, -sd->Ng); - ray->t -= sd->ray_length; /* clipping works through transparent */ - -# ifdef __RAY_DIFFERENTIALS__ - ray->dP = sd->dP; - ray->dD.dx = -sd->dI.dx; - ray->dD.dy = -sd->dI.dy; -# endif /* __RAY_DIFFERENTIALS__ */ - -# ifdef __VOLUME__ - /* enter/exit volume */ - kernel_volume_stack_enter_exit(kg, sd, state->volume_stack); -# endif /* __VOLUME__ */ -} -#endif /* __BRANCHED_PATH__ */ - -ccl_device void kernel_next_iteration_setup(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - /* If we are here, then it means that scene-intersect kernel - * has already been executed at least once. From the next time, - * scene-intersect kernel may operate on queues to fetch ray index - */ - *kernel_split_params.use_queues_flag = 1; - - /* Mark queue indices of QUEUE_SHADOW_RAY_CAST_AO_RAYS and - * QUEUE_SHADOW_RAY_CAST_DL_RAYS queues that were made empty during the - * previous kernel. - */ - kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_AO_RAYS] = 0; - kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_DL_RAYS] = 0; - } - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - ccl_global char *ray_state = kernel_split_state.ray_state; - -#ifdef __VOLUME__ - /* Reactivate only volume rays here, most surface work was skipped. */ - if (IS_STATE(ray_state, ray_index, RAY_HAS_ONLY_VOLUME)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_ACTIVE); - } -#endif - - bool active = IS_STATE(ray_state, ray_index, RAY_ACTIVE); - if (active) { - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - -#ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -#endif - /* Compute direct lighting and next bounce. */ - if (!kernel_path_surface_bounce(kg, sd, throughput, state, &L->state, ray)) { - kernel_split_path_end(kg, ray_index); - } -#ifdef __BRANCHED_PATH__ - } - else if (sd->flag & SD_HAS_ONLY_VOLUME) { - kernel_split_branched_transparent_bounce(kg, ray_index); - } - else { - kernel_split_branched_indirect_light_init(kg, ray_index); - - if (kernel_split_branched_path_surface_indirect_light_iter( - kg, ray_index, 1.0f, kernel_split_sd(branched_state_sd, ray_index), true, true)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - else { - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - kernel_split_branched_transparent_bounce(kg, ray_index); - } - } -#endif /* __BRANCHED_PATH__ */ - } - - /* Enqueue RAY_UPDATE_BUFFER rays. */ - enqueue_ray_index_local(ray_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - IS_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER) && active, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - -#ifdef __BRANCHED_PATH__ - /* iter loop */ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_LIGHT_INDIRECT_ITER] = 0; - } - - ray_index = get_ray_index(kg, - ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0), - QUEUE_LIGHT_INDIRECT_ITER, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (IS_STATE(ray_state, ray_index, RAY_LIGHT_INDIRECT_NEXT_ITER)) { - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - - path_radiance_sum_indirect(L); - path_radiance_reset_indirect(L); - - if (kernel_split_branched_path_surface_indirect_light_iter( - kg, ray_index, 1.0f, kernel_split_sd(branched_state_sd, ray_index), true, true)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - else { - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - kernel_split_branched_transparent_bounce(kg, ray_index); - } - } - -# ifdef __VOLUME__ - /* Enqueue RAY_VOLUME_INDIRECT_NEXT_ITER rays */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - enqueue_ray_index_local( - ray_index, - QUEUE_VOLUME_INDIRECT_ITER, - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_VOLUME_INDIRECT_NEXT_ITER), - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - -# endif /* __VOLUME__ */ - -# ifdef __SUBSURFACE__ - /* Enqueue RAY_SUBSURFACE_INDIRECT_NEXT_ITER rays */ - ccl_barrier(CCL_LOCAL_MEM_FENCE); - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - enqueue_ray_index_local( - ray_index, - QUEUE_SUBSURFACE_INDIRECT_ITER, - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_SUBSURFACE_INDIRECT_NEXT_ITER), - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); -# endif /* __SUBSURFACE__ */ -#endif /* __BRANCHED_PATH__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_path_init.h b/intern/cycles/kernel/split/kernel_path_init.h deleted file mode 100644 index c686f46a0cd..00000000000 --- a/intern/cycles/kernel/split/kernel_path_init.h +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel initializes structures needed in path-iteration kernels. - * This is the first kernel in ray-tracing logic. - * - * Ray state of rays outside the tile-boundary will be marked RAY_INACTIVE - */ -ccl_device void kernel_path_init(KernelGlobals *kg) -{ - int ray_index = ccl_global_id(0) + ccl_global_id(1) * ccl_global_size(0); - - /* This is the first assignment to ray_state; - * So we don't use ASSIGN_RAY_STATE macro. - */ - kernel_split_state.ray_state[ray_index] = RAY_ACTIVE; - - /* Get work. */ - ccl_global uint *work_pools = kernel_split_params.work_pools; - uint total_work_size = kernel_split_params.total_work_size; - uint work_index; - - if (!get_next_work(kg, work_pools, total_work_size, ray_index, &work_index)) { - /* No more work, mark ray as inactive */ - kernel_split_state.ray_state[ray_index] = RAY_INACTIVE; - - return; - } - - ccl_global WorkTile *tile = &kernel_split_params.tile; - uint x, y, sample; - get_work_pixel(tile, work_index, &x, &y, &sample); - - /* Store buffer offset for writing to passes. */ - uint buffer_offset = (tile->offset + x + y * tile->stride) * kernel_data.film.pass_stride; - kernel_split_state.buffer_offset[ray_index] = buffer_offset; - - /* Initialize random numbers and ray. */ - uint rng_hash; - kernel_path_trace_setup(kg, sample, x, y, &rng_hash, &kernel_split_state.ray[ray_index]); - - if (kernel_split_state.ray[ray_index].t != 0.0f) { - /* Initialize throughput, path radiance, Ray, PathState; - * These rays proceed with path-iteration. - */ - kernel_split_state.throughput[ray_index] = make_float3(1.0f, 1.0f, 1.0f); - path_radiance_init(kg, &kernel_split_state.path_radiance[ray_index]); - path_state_init(kg, - AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]), - &kernel_split_state.path_state[ray_index], - rng_hash, - sample, - &kernel_split_state.ray[ray_index]); -#ifdef __SUBSURFACE__ - kernel_path_subsurface_init_indirect(&kernel_split_state.ss_rays[ray_index]); -#endif - } - else { - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_TO_REGENERATE); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_queue_enqueue.h b/intern/cycles/kernel/split/kernel_queue_enqueue.h deleted file mode 100644 index 2db87f7a671..00000000000 --- a/intern/cycles/kernel/split/kernel_queue_enqueue.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel enqueues rays of different ray state into their - * appropriate queues: - * - * 1. Rays that have been determined to hit the background from the - * "kernel_scene_intersect" kernel are enqueued in - * QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS; - * 2. Rays that have been determined to be actively participating in pat - * -iteration will be enqueued into QUEUE_ACTIVE_AND_REGENERATED_RAYS. - * - * State of queue during other times this kernel is called: - * At entry, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be empty. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will contain RAY_TO_REGENERATE - * and RAY_UPDATE_BUFFER rays. - * At exit, - * - QUEUE_ACTIVE_AND_REGENERATED_RAYS will be filled with RAY_ACTIVE rays. - * - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS will be filled with - * RAY_TO_REGENERATE, RAY_UPDATE_BUFFER, RAY_HIT_BACKGROUND rays. - */ -ccl_device void kernel_queue_enqueue(KernelGlobals *kg, ccl_local_param QueueEnqueueLocals *locals) -{ - /* We have only 2 cases (Hit/Not-Hit) */ - int lidx = ccl_local_id(1) * ccl_local_size(0) + ccl_local_id(0); - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - - if (lidx == 0) { - locals->queue_atomics[0] = 0; - locals->queue_atomics[1] = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int queue_number = -1; - - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HIT_BACKGROUND) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_UPDATE_BUFFER) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_TO_REGENERATE)) { - queue_number = QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS; - } - else if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME) || - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_REGENERATED)) { - queue_number = QUEUE_ACTIVE_AND_REGENERATED_RAYS; - } - - unsigned int my_lqidx; - if (queue_number != -1) { - my_lqidx = get_local_queue_index(queue_number, locals->queue_atomics); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - if (lidx == 0) { - locals->queue_atomics[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = get_global_per_queue_offset( - QUEUE_ACTIVE_AND_REGENERATED_RAYS, locals->queue_atomics, kernel_split_params.queue_index); - locals->queue_atomics[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = get_global_per_queue_offset( - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - locals->queue_atomics, - kernel_split_params.queue_index); - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - unsigned int my_gqidx; - if (queue_number != -1) { - my_gqidx = get_global_queue_index( - queue_number, kernel_split_params.queue_size, my_lqidx, locals->queue_atomics); - kernel_split_state.queue_data[my_gqidx] = ray_index; - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_scene_intersect.h b/intern/cycles/kernel/split/kernel_scene_intersect.h deleted file mode 100644 index 9ac95aafd2f..00000000000 --- a/intern/cycles/kernel/split/kernel_scene_intersect.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel takes care of scene_intersect function. - * - * This kernel changes the ray_state of RAY_REGENERATED rays to RAY_ACTIVE. - * This kernel processes rays of ray state RAY_ACTIVE - * This kernel determines the rays that have hit the background and changes - * their ray state to RAY_HIT_BACKGROUND. - */ -ccl_device void kernel_scene_intersect(KernelGlobals *kg) -{ - /* Fetch use_queues_flag */ - char local_use_queues_flag = *kernel_split_params.use_queues_flag; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (local_use_queues_flag) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - } - - /* All regenerated rays become active here */ - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_REGENERATED)) { -#ifdef __BRANCHED_PATH__ - if (kernel_split_state.branched_state[ray_index].waiting_on_shared_samples) { - kernel_split_path_end(kg, ray_index); - } - else -#endif /* __BRANCHED_PATH__ */ - { - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE); - } - } - - if (!IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) { - return; - } - - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - Ray ray = kernel_split_state.ray[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - - Intersection isect; - const int last_object = state->bounce > 0 ? - intersection_get_object(kg, &kernel_split_state.isect[ray_index]) : - OBJECT_NONE; - bool hit = kernel_path_scene_intersect(kg, state, &ray, &isect, L, last_object); - kernel_split_state.isect[ray_index] = isect; - - if (!hit) { - /* Change the state of rays that hit the background; - * These rays undergo special processing in the - * background_bufferUpdate kernel. - */ - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_HIT_BACKGROUND); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shader_eval.h b/intern/cycles/kernel/split/kernel_shader_eval.h deleted file mode 100644 index c760a2b2049..00000000000 --- a/intern/cycles/kernel/split/kernel_shader_eval.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel evaluates ShaderData structure from the values computed - * by the previous kernels. - */ -ccl_device void kernel_shader_eval(KernelGlobals *kg) -{ - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - /* Sorting on cuda split is not implemented */ -#ifdef __KERNEL_CUDA__ - int queue_index = kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS]; -#else - int queue_index = kernel_split_params.queue_index[QUEUE_SHADER_SORTED_RAYS]; -#endif - if (ray_index >= queue_index) { - return; - } - ray_index = get_ray_index(kg, - ray_index, -#ifdef __KERNEL_CUDA__ - QUEUE_ACTIVE_AND_REGENERATED_RAYS, -#else - QUEUE_SHADER_SORTED_RAYS, -#endif - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - ccl_global char *ray_state = kernel_split_state.ray_state; - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - uint buffer_offset = kernel_split_state.buffer_offset[ray_index]; - ccl_global float *buffer = kernel_split_params.tile.buffer + buffer_offset; - - shader_eval_surface(kg, kernel_split_sd(sd, ray_index), state, buffer, state->flag); -#ifdef __BRANCHED_PATH__ - if (kernel_data.integrator.branched) { - shader_merge_closures(kernel_split_sd(sd, ray_index)); - } - else -#endif - { - shader_prepare_closures(kernel_split_sd(sd, ray_index), state); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shader_setup.h b/intern/cycles/kernel/split/kernel_shader_setup.h deleted file mode 100644 index 551836d1653..00000000000 --- a/intern/cycles/kernel/split/kernel_shader_setup.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* This kernel sets up the ShaderData structure from the values computed - * by the previous kernels. - * - * It also identifies the rays of state RAY_TO_REGENERATE and enqueues them - * in QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. - */ -ccl_device void kernel_shader_setup(KernelGlobals *kg, - ccl_local_param unsigned int *local_queue_atomics) -{ - /* Enqueue RAY_TO_REGENERATE rays into QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS queue. */ - if (ccl_local_id(0) == 0 && ccl_local_id(1) == 0) { - *local_queue_atomics = 0; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - int queue_index = kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS]; - if (ray_index < queue_index) { - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 0); - } - else { - ray_index = QUEUE_EMPTY_SLOT; - } - - char enqueue_flag = (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_TO_REGENERATE)) ? 1 : - 0; - enqueue_ray_index_local(ray_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - enqueue_flag, - kernel_split_params.queue_size, - local_queue_atomics, - kernel_split_state.queue_data, - kernel_split_params.queue_index); - - /* Continue on with shader evaluation. */ - if (IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE)) { - Intersection isect = kernel_split_state.isect[ray_index]; - Ray ray = kernel_split_state.ray[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - - shader_setup_from_ray(kg, sd, &isect, &ray); - -#ifdef __VOLUME__ - if (sd->flag & SD_HAS_ONLY_VOLUME) { - ASSIGN_RAY_STATE(kernel_split_state.ray_state, ray_index, RAY_HAS_ONLY_VOLUME); - } -#endif - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shader_sort.h b/intern/cycles/kernel/split/kernel_shader_sort.h deleted file mode 100644 index 95d33a42014..00000000000 --- a/intern/cycles/kernel/split/kernel_shader_sort.h +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device void kernel_shader_sort(KernelGlobals *kg, ccl_local_param ShaderSortLocals *locals) -{ -#ifndef __KERNEL_CUDA__ - int tid = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - uint qsize = kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS]; - if (tid == 0) { - kernel_split_params.queue_index[QUEUE_SHADER_SORTED_RAYS] = qsize; - } - - uint offset = (tid / SHADER_SORT_LOCAL_SIZE) * SHADER_SORT_BLOCK_SIZE; - if (offset >= qsize) { - return; - } - - int lid = ccl_local_id(1) * ccl_local_size(0) + ccl_local_id(0); - uint input = QUEUE_ACTIVE_AND_REGENERATED_RAYS * (kernel_split_params.queue_size); - uint output = QUEUE_SHADER_SORTED_RAYS * (kernel_split_params.queue_size); - ccl_local uint *local_value = &locals->local_value[0]; - ccl_local ushort *local_index = &locals->local_index[0]; - - /* copy to local memory */ - for (uint i = 0; i < SHADER_SORT_BLOCK_SIZE; i += SHADER_SORT_LOCAL_SIZE) { - uint idx = offset + i + lid; - uint add = input + idx; - uint value = (~0); - if (idx < qsize) { - int ray_index = kernel_split_state.queue_data[add]; - bool valid = (ray_index != QUEUE_EMPTY_SLOT) && - IS_STATE(kernel_split_state.ray_state, ray_index, RAY_ACTIVE); - if (valid) { - value = kernel_split_sd(sd, ray_index)->shader & SHADER_MASK; - } - } - local_value[i + lid] = value; - local_index[i + lid] = i + lid; - } - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - /* skip sorting for cpu split kernel */ -# ifdef __KERNEL_OPENCL__ - - /* bitonic sort */ - for (uint length = 1; length < SHADER_SORT_BLOCK_SIZE; length <<= 1) { - for (uint inc = length; inc > 0; inc >>= 1) { - for (uint ii = 0; ii < SHADER_SORT_BLOCK_SIZE; ii += SHADER_SORT_LOCAL_SIZE) { - uint i = lid + ii; - bool direction = ((i & (length << 1)) != 0); - uint j = i ^ inc; - ushort ioff = local_index[i]; - ushort joff = local_index[j]; - uint iKey = local_value[ioff]; - uint jKey = local_value[joff]; - bool smaller = (jKey < iKey) || (jKey == iKey && j < i); - bool swap = smaller ^ (j < i) ^ direction; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - local_index[i] = (swap) ? joff : ioff; - local_index[j] = (swap) ? ioff : joff; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - } - } - } -# endif /* __KERNEL_OPENCL__ */ - - /* copy to destination */ - for (uint i = 0; i < SHADER_SORT_BLOCK_SIZE; i += SHADER_SORT_LOCAL_SIZE) { - uint idx = offset + i + lid; - uint lidx = local_index[i + lid]; - uint outi = output + idx; - uint ini = input + offset + lidx; - uint value = local_value[lidx]; - if (idx < qsize) { - kernel_split_state.queue_data[outi] = (value == (~0)) ? QUEUE_EMPTY_SLOT : - kernel_split_state.queue_data[ini]; - } - } -#endif /* __KERNEL_CUDA__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shadow_blocked_ao.h b/intern/cycles/kernel/split/kernel_shadow_blocked_ao.h deleted file mode 100644 index 5d772fc597b..00000000000 --- a/intern/cycles/kernel/split/kernel_shadow_blocked_ao.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Shadow ray cast for AO. */ -ccl_device void kernel_shadow_blocked_ao(KernelGlobals *kg) -{ - unsigned int ao_queue_length = kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_AO_RAYS]; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = QUEUE_EMPTY_SLOT; - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index < ao_queue_length) { - ray_index = get_ray_index(kg, - thread_index, - QUEUE_SHADOW_RAY_CAST_AO_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - } - - if (ray_index == QUEUE_EMPTY_SLOT) { - return; - } - - ShaderData *sd = kernel_split_sd(sd, ray_index); - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - float3 throughput = kernel_split_state.throughput[ray_index]; - -#ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || - IS_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -#endif - kernel_path_ao(kg, sd, emission_sd, L, state, throughput, shader_bsdf_alpha(kg, sd)); -#ifdef __BRANCHED_PATH__ - } - else { - kernel_branched_path_ao(kg, sd, emission_sd, L, state, throughput); - } -#endif -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_shadow_blocked_dl.h b/intern/cycles/kernel/split/kernel_shadow_blocked_dl.h deleted file mode 100644 index 5e46d300bca..00000000000 --- a/intern/cycles/kernel/split/kernel_shadow_blocked_dl.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -/* Shadow ray cast for direct visible light. */ -ccl_device void kernel_shadow_blocked_dl(KernelGlobals *kg) -{ - unsigned int dl_queue_length = kernel_split_params.queue_index[QUEUE_SHADOW_RAY_CAST_DL_RAYS]; - ccl_barrier(CCL_LOCAL_MEM_FENCE); - - int ray_index = QUEUE_EMPTY_SLOT; - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index < dl_queue_length) { - ray_index = get_ray_index(kg, - thread_index, - QUEUE_SHADOW_RAY_CAST_DL_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - } - -#ifdef __BRANCHED_PATH__ - /* TODO(mai): move this somewhere else? */ - if (thread_index == 0) { - /* Clear QUEUE_INACTIVE_RAYS before next kernel. */ - kernel_split_params.queue_index[QUEUE_INACTIVE_RAYS] = 0; - } -#endif /* __BRANCHED_PATH__ */ - - if (ray_index == QUEUE_EMPTY_SLOT) - return; - - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - Ray ray = kernel_split_state.light_ray[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - float3 throughput = kernel_split_state.throughput[ray_index]; - - BsdfEval L_light = kernel_split_state.bsdf_eval[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - bool is_lamp = kernel_split_state.is_lamp[ray_index]; - -#if defined(__BRANCHED_PATH__) || defined(__SHADOW_TRICKS__) - bool use_branched = false; - int all = 0; - - if (state->flag & PATH_RAY_SHADOW_CATCHER) { - use_branched = true; - all = 1; - } -# if defined(__BRANCHED_PATH__) - else if (kernel_data.integrator.branched) { - use_branched = true; - - if (IS_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { - all = (kernel_data.integrator.sample_all_lights_indirect); - } - else { - all = (kernel_data.integrator.sample_all_lights_direct); - } - } -# endif /* __BRANCHED_PATH__ */ - - if (use_branched) { - kernel_branched_path_surface_connect_light( - kg, sd, emission_sd, state, throughput, 1.0f, L, all); - } - else -#endif /* defined(__BRANCHED_PATH__) || defined(__SHADOW_TRICKS__)*/ - { - /* trace shadow ray */ - float3 shadow; - - if (!shadow_blocked(kg, sd, emission_sd, state, &ray, &shadow)) { - /* accumulate */ - path_radiance_accum_light(kg, L, state, throughput, &L_light, shadow, 1.0f, is_lamp); - } - else { - path_radiance_accum_total_light(L, state, throughput, &L_light); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/split/kernel_split_common.h b/intern/cycles/kernel/split/kernel_split_common.h deleted file mode 100644 index 5114f2b03e5..00000000000 --- a/intern/cycles/kernel/split/kernel_split_common.h +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright 2011-2015 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_SPLIT_H__ -#define __KERNEL_SPLIT_H__ - -// clang-format off -#include "kernel/kernel_math.h" -#include "kernel/kernel_types.h" - -#include "kernel/split/kernel_split_data.h" - -#include "kernel/kernel_globals.h" -#include "kernel/kernel_color.h" - -#ifdef __OSL__ -# include "kernel/osl/osl_shader.h" -#endif - -#ifdef __KERNEL_OPENCL__ -# include "kernel/kernels/opencl/kernel_opencl_image.h" -#endif -#ifdef __KERNEL_CUDA__ -# include "kernel/kernels/cuda/kernel_cuda_image.h" -#endif -#ifdef __KERNEL_CPU__ -# include "kernel/kernels/cpu/kernel_cpu_image.h" -#endif - -#include "util/util_atomic.h" - -#include "kernel/kernel_path.h" -#ifdef __BRANCHED_PATH__ -# include "kernel/kernel_path_branched.h" -#endif - -#include "kernel/kernel_queues.h" -#include "kernel/kernel_work_stealing.h" - -#ifdef __BRANCHED_PATH__ -# include "kernel/split/kernel_branched.h" -#endif -// clang-format on - -CCL_NAMESPACE_BEGIN - -ccl_device_inline void kernel_split_path_end(KernelGlobals *kg, int ray_index) -{ - ccl_global char *ray_state = kernel_split_state.ray_state; - -#ifdef __BRANCHED_PATH__ -# ifdef __SUBSURFACE__ - ccl_addr_space SubsurfaceIndirectRays *ss_indirect = &kernel_split_state.ss_rays[ray_index]; - - if (ss_indirect->num_rays) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); - } - else -# endif /* __SUBSURFACE__ */ - if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT_SHARED)) { - int orig_ray = kernel_split_state.branched_state[ray_index].original_ray; - - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - PathRadiance *orig_ray_L = &kernel_split_state.path_radiance[orig_ray]; - - path_radiance_sum_indirect(L); - path_radiance_accum_sample(orig_ray_L, L); - - atomic_fetch_and_dec_uint32( - (ccl_global uint *)&kernel_split_state.branched_state[orig_ray].shared_sample_count); - - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_INACTIVE); - } - else if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_LIGHT_INDIRECT)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_LIGHT_INDIRECT_NEXT_ITER); - } - else if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_VOLUME_INDIRECT)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_VOLUME_INDIRECT_NEXT_ITER); - } - else if (IS_FLAG(ray_state, ray_index, RAY_BRANCHED_SUBSURFACE_INDIRECT)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_SUBSURFACE_INDIRECT_NEXT_ITER); - } - else { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); - } -#else - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_UPDATE_BUFFER); -#endif -} - -CCL_NAMESPACE_END - -#endif /* __KERNEL_SPLIT_H__ */ diff --git a/intern/cycles/kernel/split/kernel_split_data.h b/intern/cycles/kernel/split/kernel_split_data.h deleted file mode 100644 index decc537b39b..00000000000 --- a/intern/cycles/kernel/split/kernel_split_data.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_SPLIT_DATA_H__ -#define __KERNEL_SPLIT_DATA_H__ - -#include "kernel/split/kernel_split_data_types.h" - -#include "kernel/kernel_globals.h" - -CCL_NAMESPACE_BEGIN - -ccl_device_inline uint64_t split_data_buffer_size(KernelGlobals *kg, size_t num_elements) -{ - (void)kg; /* Unused on CPU. */ - - uint64_t size = 0; -#define SPLIT_DATA_ENTRY(type, name, num) +align_up(num_elements *num * sizeof(type), 16) - size = size SPLIT_DATA_ENTRIES; -#undef SPLIT_DATA_ENTRY - - uint64_t closure_size = sizeof(ShaderClosure) * (kernel_data.integrator.max_closures - 1); - -#ifdef __BRANCHED_PATH__ - size += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); -#endif - - size += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); - - return size; -} - -ccl_device_inline void split_data_init(KernelGlobals *kg, - ccl_global SplitData *split_data, - size_t num_elements, - ccl_global void *data, - ccl_global char *ray_state) -{ - (void)kg; /* Unused on CPU. */ - - ccl_global char *p = (ccl_global char *)data; - -#define SPLIT_DATA_ENTRY(type, name, num) \ - split_data->name = (type *)p; \ - p += align_up(num_elements * num * sizeof(type), 16); - SPLIT_DATA_ENTRIES; -#undef SPLIT_DATA_ENTRY - - uint64_t closure_size = sizeof(ShaderClosure) * (kernel_data.integrator.max_closures - 1); - -#ifdef __BRANCHED_PATH__ - split_data->_branched_state_sd = (ShaderData *)p; - p += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); -#endif - - split_data->_sd = (ShaderData *)p; - p += align_up(num_elements * (sizeof(ShaderData) + closure_size), 16); - - split_data->ray_state = ray_state; -} - -CCL_NAMESPACE_END - -#endif /* __KERNEL_SPLIT_DATA_H__ */ diff --git a/intern/cycles/kernel/split/kernel_split_data_types.h b/intern/cycles/kernel/split/kernel_split_data_types.h deleted file mode 100644 index 06bdce9947d..00000000000 --- a/intern/cycles/kernel/split/kernel_split_data_types.h +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __KERNEL_SPLIT_DATA_TYPES_H__ -#define __KERNEL_SPLIT_DATA_TYPES_H__ - -CCL_NAMESPACE_BEGIN - -/* parameters used by the split kernels, we use a single struct to avoid passing these to each - * kernel */ - -typedef struct SplitParams { - WorkTile tile; - uint total_work_size; - - ccl_global unsigned int *work_pools; - - ccl_global int *queue_index; - int queue_size; - ccl_global char *use_queues_flag; - - /* Place for storing sd->flag. AMD GPU OpenCL compiler workaround */ - int dummy_sd_flag; -} SplitParams; - -/* Global memory variables [porting]; These memory is used for - * co-operation between different kernels; Data written by one - * kernel will be available to another kernel via this global - * memory. - */ - -/* SPLIT_DATA_ENTRY(type, name, num) */ - -#ifdef __BRANCHED_PATH__ - -typedef ccl_global struct SplitBranchedState { - /* various state that must be kept and restored after an indirect loop */ - PathState path_state; - float3 throughput; - Ray ray; - - Intersection isect; - - char ray_state; - - /* indirect loop state */ - int next_closure; - int next_sample; - -# ifdef __SUBSURFACE__ - int ss_next_closure; - int ss_next_sample; - int next_hit; - int num_hits; - - uint lcg_state; - LocalIntersection ss_isect; -# endif /* __SUBSURFACE__ */ - - int shared_sample_count; /* number of branched samples shared with other threads */ - int original_ray; /* index of original ray when sharing branched samples */ - bool waiting_on_shared_samples; -} SplitBranchedState; - -# define SPLIT_DATA_BRANCHED_ENTRIES \ - SPLIT_DATA_ENTRY(SplitBranchedState, branched_state, 1) \ - SPLIT_DATA_ENTRY(ShaderData, _branched_state_sd, 0) -#else -# define SPLIT_DATA_BRANCHED_ENTRIES -#endif /* __BRANCHED_PATH__ */ - -#ifdef __SUBSURFACE__ -# define SPLIT_DATA_SUBSURFACE_ENTRIES \ - SPLIT_DATA_ENTRY(ccl_global SubsurfaceIndirectRays, ss_rays, 1) -#else -# define SPLIT_DATA_SUBSURFACE_ENTRIES -#endif /* __SUBSURFACE__ */ - -#ifdef __VOLUME__ -# define SPLIT_DATA_VOLUME_ENTRIES SPLIT_DATA_ENTRY(ccl_global PathState, state_shadow, 1) -#else -# define SPLIT_DATA_VOLUME_ENTRIES -#endif /* __VOLUME__ */ - -#define SPLIT_DATA_ENTRIES \ - SPLIT_DATA_ENTRY(ccl_global float3, throughput, 1) \ - SPLIT_DATA_ENTRY(PathRadiance, path_radiance, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, ray, 1) \ - SPLIT_DATA_ENTRY(ccl_global PathState, path_state, 1) \ - SPLIT_DATA_ENTRY(ccl_global Intersection, isect, 1) \ - SPLIT_DATA_ENTRY(ccl_global BsdfEval, bsdf_eval, 1) \ - SPLIT_DATA_ENTRY(ccl_global int, is_lamp, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, light_ray, 1) \ - SPLIT_DATA_ENTRY( \ - ccl_global int, queue_data, (NUM_QUEUES * 2)) /* TODO(mai): this is too large? */ \ - SPLIT_DATA_ENTRY(ccl_global uint, buffer_offset, 1) \ - SPLIT_DATA_ENTRY(ShaderDataTinyStorage, sd_DL_shadow, 1) \ - SPLIT_DATA_SUBSURFACE_ENTRIES \ - SPLIT_DATA_VOLUME_ENTRIES \ - SPLIT_DATA_BRANCHED_ENTRIES \ - SPLIT_DATA_ENTRY(ShaderData, _sd, 0) - -/* Entries to be copied to inactive rays when sharing branched samples - * (TODO: which are actually needed?) */ -#define SPLIT_DATA_ENTRIES_BRANCHED_SHARED \ - SPLIT_DATA_ENTRY(ccl_global float3, throughput, 1) \ - SPLIT_DATA_ENTRY(PathRadiance, path_radiance, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, ray, 1) \ - SPLIT_DATA_ENTRY(ccl_global PathState, path_state, 1) \ - SPLIT_DATA_ENTRY(ccl_global Intersection, isect, 1) \ - SPLIT_DATA_ENTRY(ccl_global BsdfEval, bsdf_eval, 1) \ - SPLIT_DATA_ENTRY(ccl_global int, is_lamp, 1) \ - SPLIT_DATA_ENTRY(ccl_global Ray, light_ray, 1) \ - SPLIT_DATA_ENTRY(ShaderDataTinyStorage, sd_DL_shadow, 1) \ - SPLIT_DATA_SUBSURFACE_ENTRIES \ - SPLIT_DATA_VOLUME_ENTRIES \ - SPLIT_DATA_BRANCHED_ENTRIES \ - SPLIT_DATA_ENTRY(ShaderData, _sd, 0) - -/* struct that holds pointers to data in the shared state buffer */ -typedef struct SplitData { -#define SPLIT_DATA_ENTRY(type, name, num) type *name; - SPLIT_DATA_ENTRIES -#undef SPLIT_DATA_ENTRY - - /* this is actually in a separate buffer from the rest of the split state data (so it can be read - * back from the host easily) but is still used the same as the other data so we have it here in - * this struct as well - */ - ccl_global char *ray_state; -} SplitData; - -#ifndef __KERNEL_CUDA__ -# define kernel_split_state (kg->split_data) -# define kernel_split_params (kg->split_param_data) -#else -__device__ SplitData __split_data; -# define kernel_split_state (__split_data) -__device__ SplitParams __split_param_data; -# define kernel_split_params (__split_param_data) -#endif /* __KERNEL_CUDA__ */ - -#define kernel_split_sd(sd, ray_index) \ - ((ShaderData *)(((ccl_global char *)kernel_split_state._##sd) + \ - (sizeof(ShaderData) + \ - sizeof(ShaderClosure) * (kernel_data.integrator.max_closures - 1)) * \ - (ray_index))) - -/* Local storage for queue_enqueue kernel. */ -typedef struct QueueEnqueueLocals { - uint queue_atomics[2]; -} QueueEnqueueLocals; - -/* Local storage for holdout_emission_blurring_pathtermination_ao kernel. */ -typedef struct BackgroundAOLocals { - uint queue_atomics_bg; - uint queue_atomics_ao; -} BackgroundAOLocals; - -typedef struct ShaderSortLocals { - uint local_value[SHADER_SORT_BLOCK_SIZE]; - ushort local_index[SHADER_SORT_BLOCK_SIZE]; -} ShaderSortLocals; - -CCL_NAMESPACE_END - -#endif /* __KERNEL_SPLIT_DATA_TYPES_H__ */ diff --git a/intern/cycles/kernel/split/kernel_subsurface_scatter.h b/intern/cycles/kernel/split/kernel_subsurface_scatter.h deleted file mode 100644 index ba06ae3bc53..00000000000 --- a/intern/cycles/kernel/split/kernel_subsurface_scatter.h +++ /dev/null @@ -1,264 +0,0 @@ -/* - * Copyright 2011-2017 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -#if defined(__BRANCHED_PATH__) && defined(__SUBSURFACE__) - -ccl_device_inline void kernel_split_branched_path_subsurface_indirect_light_init(KernelGlobals *kg, - int ray_index) -{ - kernel_split_branched_path_indirect_loop_init(kg, ray_index); - - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - branched_state->ss_next_closure = 0; - branched_state->ss_next_sample = 0; - - branched_state->num_hits = 0; - branched_state->next_hit = 0; - - ADD_RAY_FLAG(kernel_split_state.ray_state, ray_index, RAY_BRANCHED_SUBSURFACE_INDIRECT); -} - -ccl_device_noinline bool kernel_split_branched_path_subsurface_indirect_light_iter( - KernelGlobals *kg, int ray_index) -{ - SplitBranchedState *branched_state = &kernel_split_state.branched_state[ray_index]; - - ShaderData *sd = kernel_split_sd(branched_state_sd, ray_index); - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - for (int i = branched_state->ss_next_closure; i < sd->num_closure; i++) { - ShaderClosure *sc = &sd->closure[i]; - - if (!CLOSURE_IS_BSSRDF(sc->type)) - continue; - - /* Closure memory will be overwritten, so read required variables now. */ - Bssrdf *bssrdf = (Bssrdf *)sc; - ClosureType bssrdf_type = sc->type; - float bssrdf_roughness = bssrdf->roughness; - - /* set up random number generator */ - if (branched_state->ss_next_sample == 0 && branched_state->next_hit == 0 && - branched_state->next_closure == 0 && branched_state->next_sample == 0) { - branched_state->lcg_state = lcg_state_init_addrspace(&branched_state->path_state, - 0x68bc21eb); - } - int num_samples = kernel_data.integrator.subsurface_samples * 3; - float num_samples_inv = 1.0f / num_samples; - uint bssrdf_rng_hash = cmj_hash(branched_state->path_state.rng_hash, i); - - /* do subsurface scatter step with copy of shader data, this will - * replace the BSSRDF with a diffuse BSDF closure */ - for (int j = branched_state->ss_next_sample; j < num_samples; j++) { - ccl_global PathState *hit_state = &kernel_split_state.path_state[ray_index]; - *hit_state = branched_state->path_state; - hit_state->rng_hash = bssrdf_rng_hash; - path_state_branch(hit_state, j, num_samples); - - ccl_global LocalIntersection *ss_isect = &branched_state->ss_isect; - float bssrdf_u, bssrdf_v; - path_branched_rng_2D( - kg, bssrdf_rng_hash, hit_state, j, num_samples, PRNG_BSDF_U, &bssrdf_u, &bssrdf_v); - - /* intersection is expensive so avoid doing multiple times for the same input */ - if (branched_state->next_hit == 0 && branched_state->next_closure == 0 && - branched_state->next_sample == 0) { - uint lcg_state = branched_state->lcg_state; - LocalIntersection ss_isect_private; - - branched_state->num_hits = subsurface_scatter_multi_intersect( - kg, &ss_isect_private, sd, hit_state, sc, &lcg_state, bssrdf_u, bssrdf_v, true); - - branched_state->lcg_state = lcg_state; - *ss_isect = ss_isect_private; - } - - hit_state->rng_offset += PRNG_BOUNCE_NUM; - -# ifdef __VOLUME__ - Ray volume_ray = branched_state->ray; - bool need_update_volume_stack = kernel_data.integrator.use_volumes && - sd->object_flag & SD_OBJECT_INTERSECTS_VOLUME; -# endif /* __VOLUME__ */ - - /* compute lighting with the BSDF closure */ - for (int hit = branched_state->next_hit; hit < branched_state->num_hits; hit++) { - ShaderData *bssrdf_sd = kernel_split_sd(sd, ray_index); - *bssrdf_sd = *sd; /* note: copy happens each iteration of inner loop, this is - * important as the indirect path will write into bssrdf_sd */ - - LocalIntersection ss_isect_private = *ss_isect; - subsurface_scatter_multi_setup( - kg, &ss_isect_private, hit, bssrdf_sd, hit_state, bssrdf_type, bssrdf_roughness); - *ss_isect = ss_isect_private; - -# ifdef __VOLUME__ - if (need_update_volume_stack) { - /* Setup ray from previous surface point to the new one. */ - float3 P = ray_offset(bssrdf_sd->P, -bssrdf_sd->Ng); - volume_ray.D = normalize_len(P - volume_ray.P, &volume_ray.t); - - for (int k = 0; k < VOLUME_STACK_SIZE; k++) { - hit_state->volume_stack[k] = branched_state->path_state.volume_stack[k]; - } - - kernel_volume_stack_update_for_subsurface( - kg, emission_sd, &volume_ray, hit_state->volume_stack); - } -# endif /* __VOLUME__ */ - -# ifdef __EMISSION__ - if (branched_state->next_closure == 0 && branched_state->next_sample == 0) { - /* direct light */ - if (kernel_data.integrator.use_direct_light) { - int all = (kernel_data.integrator.sample_all_lights_direct) || - (hit_state->flag & PATH_RAY_SHADOW_CATCHER); - kernel_branched_path_surface_connect_light(kg, - bssrdf_sd, - emission_sd, - hit_state, - branched_state->throughput, - num_samples_inv, - L, - all); - } - } -# endif /* __EMISSION__ */ - - /* indirect light */ - if (kernel_split_branched_path_surface_indirect_light_iter( - kg, ray_index, num_samples_inv, bssrdf_sd, false, false)) { - branched_state->ss_next_closure = i; - branched_state->ss_next_sample = j; - branched_state->next_hit = hit; - - return true; - } - - branched_state->next_closure = 0; - } - - branched_state->next_hit = 0; - } - - branched_state->ss_next_sample = 0; - } - - branched_state->ss_next_closure = sd->num_closure; - - branched_state->waiting_on_shared_samples = (branched_state->shared_sample_count > 0); - if (branched_state->waiting_on_shared_samples) { - return true; - } - - kernel_split_branched_path_indirect_loop_end(kg, ray_index); - - return false; -} - -#endif /* __BRANCHED_PATH__ && __SUBSURFACE__ */ - -ccl_device void kernel_subsurface_scatter(KernelGlobals *kg) -{ - int thread_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - if (thread_index == 0) { - /* We will empty both queues in this kernel. */ - kernel_split_params.queue_index[QUEUE_ACTIVE_AND_REGENERATED_RAYS] = 0; - kernel_split_params.queue_index[QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS] = 0; - } - - int ray_index = ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0); - ray_index = get_ray_index(kg, - ray_index, - QUEUE_ACTIVE_AND_REGENERATED_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - get_ray_index(kg, - thread_index, - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - -#ifdef __SUBSURFACE__ - ccl_global char *ray_state = kernel_split_state.ray_state; - - if (IS_STATE(ray_state, ray_index, RAY_ACTIVE)) { - ccl_global PathState *state = &kernel_split_state.path_state[ray_index]; - PathRadiance *L = &kernel_split_state.path_radiance[ray_index]; - ccl_global Ray *ray = &kernel_split_state.ray[ray_index]; - ccl_global float3 *throughput = &kernel_split_state.throughput[ray_index]; - ccl_global SubsurfaceIndirectRays *ss_indirect = &kernel_split_state.ss_rays[ray_index]; - ShaderData *sd = kernel_split_sd(sd, ray_index); - ShaderData *emission_sd = AS_SHADER_DATA(&kernel_split_state.sd_DL_shadow[ray_index]); - - if (sd->flag & SD_BSSRDF) { - -# ifdef __BRANCHED_PATH__ - if (!kernel_data.integrator.branched || - IS_FLAG(ray_state, ray_index, RAY_BRANCHED_INDIRECT)) { -# endif - if (kernel_path_subsurface_scatter( - kg, sd, emission_sd, L, state, ray, throughput, ss_indirect)) { - kernel_split_path_end(kg, ray_index); - } -# ifdef __BRANCHED_PATH__ - } - else { - kernel_split_branched_path_subsurface_indirect_light_init(kg, ray_index); - - if (kernel_split_branched_path_subsurface_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif - } - } - -# ifdef __BRANCHED_PATH__ - if (ccl_global_id(0) == 0 && ccl_global_id(1) == 0) { - kernel_split_params.queue_index[QUEUE_SUBSURFACE_INDIRECT_ITER] = 0; - } - - /* iter loop */ - ray_index = get_ray_index(kg, - ccl_global_id(1) * ccl_global_size(0) + ccl_global_id(0), - QUEUE_SUBSURFACE_INDIRECT_ITER, - kernel_split_state.queue_data, - kernel_split_params.queue_size, - 1); - - if (IS_STATE(ray_state, ray_index, RAY_SUBSURFACE_INDIRECT_NEXT_ITER)) { - /* for render passes, sum and reset indirect light pass variables - * for the next samples */ - path_radiance_sum_indirect(&kernel_split_state.path_radiance[ray_index]); - path_radiance_reset_indirect(&kernel_split_state.path_radiance[ray_index]); - - if (kernel_split_branched_path_subsurface_indirect_light_iter(kg, ray_index)) { - ASSIGN_RAY_STATE(ray_state, ray_index, RAY_REGENERATED); - } - } -# endif /* __BRANCHED_PATH__ */ - -#endif /* __SUBSURFACE__ */ -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_ao.h b/intern/cycles/kernel/svm/ao.h similarity index 61% rename from intern/cycles/kernel/svm/svm_ao.h rename to intern/cycles/kernel/svm/ao.h index 4cb986b897a..678f49c8ccd 100644 --- a/intern/cycles/kernel/svm/svm_ao.h +++ b/intern/cycles/kernel/svm/ao.h @@ -14,20 +14,29 @@ * limitations under the License. */ +#pragma once + +#include "kernel/bvh/bvh.h" + CCL_NAMESPACE_BEGIN #ifdef __SHADER_RAYTRACE__ -ccl_device_noinline float svm_ao(KernelGlobals *kg, - ShaderData *sd, - float3 N, - ccl_addr_space PathState *state, - float max_dist, - int num_samples, - int flags) +# ifdef __KERNEL_OPTIX__ +extern "C" __device__ float __direct_callable__svm_node_ao( +# else +ccl_device float svm_ao( +# endif + KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd, + float3 N, + float max_dist, + int num_samples, + int flags) { if (flags & NODE_AO_GLOBAL_RADIUS) { - max_dist = kernel_data.background.ao_distance; + max_dist = kernel_data.integrator.ao_bounces_distance; } /* Early out if no sampling needed. */ @@ -47,11 +56,14 @@ ccl_device_noinline float svm_ao(KernelGlobals *kg, float3 T, B; make_orthonormals(N, &T, &B); + /* TODO: support ray-tracing in shadow shader evaluation? */ + RNGState rng_state; + path_state_rng_load(state, &rng_state); + int unoccluded = 0; for (int sample = 0; sample < num_samples; sample++) { float disk_u, disk_v; - path_branched_rng_2D( - kg, state->rng_hash, state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); + path_branched_rng_2D(kg, &rng_state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); float2 d = concentric_sample_disk(disk_u, disk_v); float3 D = make_float3(d.x, d.y, safe_sqrtf(1.0f - dot(d, d))); @@ -62,8 +74,8 @@ ccl_device_noinline float svm_ao(KernelGlobals *kg, ray.D = D.x * T + D.y * B + D.z * N; ray.t = max_dist; ray.time = sd->time; - ray.dP = sd->dP; - ray.dD = differential3_zero(); + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); if (flags & NODE_AO_ONLY_LOCAL) { if (!scene_intersect_local(kg, &ray, NULL, sd->object, NULL, 0)) { @@ -81,8 +93,18 @@ ccl_device_noinline float svm_ao(KernelGlobals *kg, return ((float)unoccluded) / num_samples; } -ccl_device void svm_node_ao( - KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, float *stack, uint4 node) +template +# if defined(__KERNEL_OPTIX__) +ccl_device_inline +# else +ccl_device_noinline +# endif + void + svm_node_ao(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint flags, dist_offset, normal_offset, out_ao_offset; svm_unpack_node_uchar4(node.y, &flags, &dist_offset, &normal_offset, &out_ao_offset); @@ -90,9 +112,19 @@ ccl_device void svm_node_ao( uint color_offset, out_color_offset, samples; svm_unpack_node_uchar3(node.z, &color_offset, &out_color_offset, &samples); - float dist = stack_load_float_default(stack, dist_offset, node.w); - float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; - float ao = svm_ao(kg, sd, normal, state, dist, samples, flags); + float ao = 1.0f; + + IF_KERNEL_NODES_FEATURE(RAYTRACE) + { + float dist = stack_load_float_default(stack, dist_offset, node.w); + float3 normal = stack_valid(normal_offset) ? stack_load_float3(stack, normal_offset) : sd->N; + +# ifdef __KERNEL_OPTIX__ + ao = optixDirectCall(0, kg, state, sd, normal, dist, samples, flags); +# else + ao = svm_ao(kg, state, sd, normal, dist, samples, flags); +# endif + } if (stack_valid(out_ao_offset)) { stack_store_float(stack, out_ao_offset, ao); diff --git a/intern/cycles/kernel/svm/aov.h b/intern/cycles/kernel/svm/aov.h new file mode 100644 index 00000000000..21ee7af7639 --- /dev/null +++ b/intern/cycles/kernel/svm/aov.h @@ -0,0 +1,70 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "kernel/film/write_passes.h" + +CCL_NAMESPACE_BEGIN + +ccl_device_inline bool svm_node_aov_check(const uint32_t path_flag, + ccl_global float *render_buffer) +{ + bool is_primary = (path_flag & PATH_RAY_CAMERA) && (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)); + + return ((render_buffer != NULL) && is_primary); +} + +template +ccl_device void svm_node_aov_color(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + ccl_global float *render_buffer) +{ + IF_KERNEL_NODES_FEATURE(AOV) + { + const float3 val = stack_load_float3(stack, node.y); + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset + + (kernel_data.film.pass_aov_color + node.z); + kernel_write_pass_float3(buffer, make_float3(val.x, val.y, val.z)); + } +} + +template +ccl_device void svm_node_aov_value(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + ccl_global float *render_buffer) +{ + IF_KERNEL_NODES_FEATURE(AOV) + { + const float val = stack_load_float(stack, node.y); + const uint32_t render_pixel_index = INTEGRATOR_STATE(state, path, render_pixel_index); + const uint64_t render_buffer_offset = (uint64_t)render_pixel_index * + kernel_data.film.pass_stride; + ccl_global float *buffer = render_buffer + render_buffer_offset + + (kernel_data.film.pass_aov_value + node.z); + kernel_write_pass_float(buffer, val); + } +} +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_attribute.h b/intern/cycles/kernel/svm/attribute.h similarity index 82% rename from intern/cycles/kernel/svm/svm_attribute.h rename to intern/cycles/kernel/svm/attribute.h index 62740824ad1..e9de0164c7a 100644 --- a/intern/cycles/kernel/svm/svm_attribute.h +++ b/intern/cycles/kernel/svm/attribute.h @@ -14,12 +14,17 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Attribute Node */ -ccl_device AttributeDescriptor svm_node_attr_init( - KernelGlobals *kg, ShaderData *sd, uint4 node, NodeAttributeOutputType *type, uint *out_offset) +ccl_device AttributeDescriptor svm_node_attr_init(KernelGlobals kg, + ccl_private ShaderData *sd, + uint4 node, + ccl_private NodeAttributeOutputType *type, + ccl_private uint *out_offset) { *out_offset = node.z; *type = (NodeAttributeOutputType)node.w; @@ -44,31 +49,38 @@ ccl_device AttributeDescriptor svm_node_attr_init( return desc; } -ccl_device void svm_node_attr(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +template +ccl_device_noinline void svm_node_attr(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; uint out_offset = 0; AttributeDescriptor desc = svm_node_attr_init(kg, sd, node, &type, &out_offset); #ifdef __VOLUME__ - /* Volumes - * NOTE: moving this into its own node type might help improve performance. */ - if (primitive_is_volume_attribute(sd, desc)) { - const float4 value = volume_attribute_float4(kg, sd, desc); + IF_KERNEL_NODES_FEATURE(VOLUME) + { + /* Volumes + * NOTE: moving this into its own node type might help improve performance. */ + if (primitive_is_volume_attribute(sd, desc)) { + const float4 value = volume_attribute_float4(kg, sd, desc); - if (type == NODE_ATTR_OUTPUT_FLOAT) { - const float f = volume_attribute_value_to_float(value); - stack_store_float(stack, out_offset, f); + if (type == NODE_ATTR_OUTPUT_FLOAT) { + const float f = volume_attribute_value_to_float(value); + stack_store_float(stack, out_offset, f); + } + else if (type == NODE_ATTR_OUTPUT_FLOAT3) { + const float3 f = volume_attribute_value_to_float3(value); + stack_store_float3(stack, out_offset, f); + } + else { + const float f = volume_attribute_value_to_alpha(value); + stack_store_float(stack, out_offset, f); + } + return; } - else if (type == NODE_ATTR_OUTPUT_FLOAT3) { - const float3 f = volume_attribute_value_to_float3(value); - stack_store_float3(stack, out_offset, f); - } - else { - const float f = volume_attribute_value_to_alpha(value); - stack_store_float(stack, out_offset, f); - } - return; } #endif @@ -139,7 +151,10 @@ ccl_device void svm_node_attr(KernelGlobals *kg, ShaderData *sd, float *stack, u } } -ccl_device void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_attr_bump_dx(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; uint out_offset = 0; @@ -232,7 +247,10 @@ ccl_device void svm_node_attr_bump_dx(KernelGlobals *kg, ShaderData *sd, float * } } -ccl_device void svm_node_attr_bump_dy(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_attr_bump_dy(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { NodeAttributeOutputType type = NODE_ATTR_OUTPUT_FLOAT; uint out_offset = 0; diff --git a/intern/cycles/kernel/svm/svm_bevel.h b/intern/cycles/kernel/svm/bevel.h similarity index 52% rename from intern/cycles/kernel/svm/svm_bevel.h rename to intern/cycles/kernel/svm/bevel.h index bf5957ec9e4..6799489514f 100644 --- a/intern/cycles/kernel/svm/svm_bevel.h +++ b/intern/cycles/kernel/svm/bevel.h @@ -14,21 +14,102 @@ * limitations under the License. */ +#pragma once + +#include "kernel/bvh/bvh.h" +#include "kernel/sample/mapping.h" +#include "kernel/sample/pattern.h" + CCL_NAMESPACE_BEGIN #ifdef __SHADER_RAYTRACE__ +/* Planar Cubic BSSRDF falloff, reused for bevel. + * + * This is basically (Rm - x)^3, with some factors to normalize it. For sampling + * we integrate 2*pi*x * (Rm - x)^3, which gives us a quintic equation that as + * far as I can tell has no closed form solution. So we get an iterative solution + * instead with newton-raphson. */ + +ccl_device float svm_bevel_cubic_eval(const float radius, float r) +{ + const float Rm = radius; + + if (r >= Rm) + return 0.0f; + + /* integrate (2*pi*r * 10*(R - r)^3)/(pi * R^5) from 0 to R = 1 */ + const float Rm5 = (Rm * Rm) * (Rm * Rm) * Rm; + const float f = Rm - r; + const float num = f * f * f; + + return (10.0f * num) / (Rm5 * M_PI_F); +} + +ccl_device float svm_bevel_cubic_pdf(const float radius, float r) +{ + return svm_bevel_cubic_eval(radius, r); +} + +/* solve 10x^2 - 20x^3 + 15x^4 - 4x^5 - xi == 0 */ +ccl_device_forceinline float svm_bevel_cubic_quintic_root_find(float xi) +{ + /* newton-raphson iteration, usually succeeds in 2-4 iterations, except + * outside 0.02 ... 0.98 where it can go up to 10, so overall performance + * should not be too bad */ + const float tolerance = 1e-6f; + const int max_iteration_count = 10; + float x = 0.25f; + int i; + + for (i = 0; i < max_iteration_count; i++) { + float x2 = x * x; + float x3 = x2 * x; + float nx = (1.0f - x); + + float f = 10.0f * x2 - 20.0f * x3 + 15.0f * x2 * x2 - 4.0f * x2 * x3 - xi; + float f_ = 20.0f * (x * nx) * (nx * nx); + + if (fabsf(f) < tolerance || f_ == 0.0f) + break; + + x = saturatef(x - f / f_); + } + + return x; +} + +ccl_device void svm_bevel_cubic_sample(const float radius, + float xi, + ccl_private float *r, + ccl_private float *h) +{ + float Rm = radius; + float r_ = svm_bevel_cubic_quintic_root_find(xi); + + r_ *= Rm; + *r = r_; + + /* h^2 + r^2 = Rm^2 */ + *h = safe_sqrtf(Rm * Rm - r_ * r_); +} + /* Bevel shader averaging normals from nearby surfaces. * * Sampling strategy from: BSSRDF Importance Sampling, SIGGRAPH 2013 * http://library.imageworks.com/pdfs/imageworks-library-BSSRDF-sampling.pdf */ -ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - float radius, - int num_samples) +# ifdef __KERNEL_OPTIX__ +extern "C" __device__ float3 __direct_callable__svm_node_bevel( +# else +ccl_device float3 svm_bevel( +# endif + KernelGlobals kg, + ConstIntegratorState state, + ccl_private ShaderData *sd, + float radius, + int num_samples) { /* Early out if no sampling needed. */ if (radius <= 0.0f || num_samples < 1 || sd->object == OBJECT_NONE) { @@ -41,21 +122,27 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, } /* Don't bevel for blurry indirect rays. */ - if (state->min_ray_pdf < 8.0f) { + if (INTEGRATOR_STATE(state, path, min_ray_pdf) < 8.0f) { return sd->N; } /* Setup for multi intersection. */ LocalIntersection isect; - uint lcg_state = lcg_state_init_addrspace(state, 0x64c6a40e); + uint lcg_state = lcg_state_init(INTEGRATOR_STATE(state, path, rng_hash), + INTEGRATOR_STATE(state, path, rng_offset), + INTEGRATOR_STATE(state, path, sample), + 0x64c6a40e); /* Sample normals from surrounding points on surface. */ float3 sum_N = make_float3(0.0f, 0.0f, 0.0f); + /* TODO: support ray-tracing in shadow shader evaluation? */ + RNGState rng_state; + path_state_rng_load(state, &rng_state); + for (int sample = 0; sample < num_samples; sample++) { float disk_u, disk_v; - path_branched_rng_2D( - kg, state->rng_hash, state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); + path_branched_rng_2D(kg, &rng_state, sample, num_samples, PRNG_BEVEL_U, &disk_u, &disk_v); /* Pick random axis in local frame and point on disk. */ float3 disk_N, disk_T, disk_B; @@ -97,22 +184,22 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, float disk_height; /* Perhaps find something better than Cubic BSSRDF, but happens to work well. */ - bssrdf_cubic_sample(radius, 0.0f, disk_r, &disk_r, &disk_height); + svm_bevel_cubic_sample(radius, disk_r, &disk_r, &disk_height); float3 disk_P = (disk_r * cosf(phi)) * disk_T + (disk_r * sinf(phi)) * disk_B; /* Create ray. */ - Ray *ray = &isect.ray; - ray->P = sd->P + disk_N * disk_height + disk_P; - ray->D = -disk_N; - ray->t = 2.0f * disk_height; - ray->dP = sd->dP; - ray->dD = differential3_zero(); - ray->time = sd->time; + Ray ray ccl_optional_struct_init; + ray.P = sd->P + disk_N * disk_height + disk_P; + ray.D = -disk_N; + ray.t = 2.0f * disk_height; + ray.dP = differential_zero_compact(); + ray.dD = differential_zero_compact(); + ray.time = sd->time; /* Intersect with the same object. if multiple intersections are found it * will use at most LOCAL_MAX_HITS hits, a random subset of all hits. */ - scene_intersect_local(kg, ray, &isect, sd->object, &lcg_state, LOCAL_MAX_HITS); + scene_intersect_local(kg, &ray, &isect, sd->object, &lcg_state, LOCAL_MAX_HITS); int num_eval_hits = min(isect.num_hits, LOCAL_MAX_HITS); @@ -120,22 +207,21 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, /* Quickly retrieve P and Ng without setting up ShaderData. */ float3 hit_P; if (sd->type & PRIMITIVE_TRIANGLE) { - hit_P = triangle_refine_local(kg, sd, &isect.hits[hit], ray); + hit_P = triangle_refine_local( + kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim); } # ifdef __OBJECT_MOTION__ else if (sd->type & PRIMITIVE_MOTION_TRIANGLE) { float3 verts[3]; - motion_triangle_vertices( - kg, sd->object, kernel_tex_fetch(__prim_index, isect.hits[hit].prim), sd->time, verts); - hit_P = motion_triangle_refine_local(kg, sd, &isect.hits[hit], ray, verts); + motion_triangle_vertices(kg, sd->object, isect.hits[hit].prim, sd->time, verts); + hit_P = motion_triangle_refine_local( + kg, sd, ray.P, ray.D, ray.t, isect.hits[hit].object, isect.hits[hit].prim, verts); } # endif /* __OBJECT_MOTION__ */ /* Get geometric normal. */ float3 hit_Ng = isect.Ng[hit]; - int object = (isect.hits[hit].object == OBJECT_NONE) ? - kernel_tex_fetch(__prim_object, isect.hits[hit].prim) : - isect.hits[hit].object; + int object = isect.hits[hit].object; int object_flag = kernel_tex_fetch(__object_flag, object); if (object_flag & SD_OBJECT_NEGATIVE_SCALE_APPLIED) { hit_Ng = -hit_Ng; @@ -143,7 +229,7 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, /* Compute smooth normal. */ float3 N = hit_Ng; - int prim = kernel_tex_fetch(__prim_index, isect.hits[hit].prim); + int prim = isect.hits[hit].prim; int shader = kernel_tex_fetch(__tri_shader, prim); if (shader & SHADER_SMOOTH_NORMAL) { @@ -173,7 +259,7 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, /* Multiple importance sample between 3 axes, power heuristic * found to be slightly better than balance heuristic. pdf_N - * in the MIS weight and denominator cancelled out. */ + * in the MIS weight and denominator canceled out. */ float w = pdf_N / (sqr(pdf_N) + sqr(pdf_T) + sqr(pdf_B)); if (isect.num_hits > LOCAL_MAX_HITS) { w *= isect.num_hits / (float)LOCAL_MAX_HITS; @@ -183,8 +269,8 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, float r = len(hit_P - sd->P); /* Compute weight. */ - float pdf = bssrdf_cubic_pdf(radius, 0.0f, r); - float disk_pdf = bssrdf_cubic_pdf(radius, 0.0f, disk_r); + float pdf = svm_bevel_cubic_pdf(radius, r); + float disk_pdf = svm_bevel_cubic_pdf(radius, disk_r); w *= pdf / disk_pdf; @@ -198,19 +284,39 @@ ccl_device_noinline float3 svm_bevel(KernelGlobals *kg, return is_zero(N) ? sd->N : (sd->flag & SD_BACKFACING) ? -N : N; } -ccl_device void svm_node_bevel( - KernelGlobals *kg, ShaderData *sd, ccl_addr_space PathState *state, float *stack, uint4 node) +template +# if defined(__KERNEL_OPTIX__) +ccl_device_inline +# else +ccl_device_noinline +# endif + void + svm_node_bevel(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint num_samples, radius_offset, normal_offset, out_offset; svm_unpack_node_uchar4(node.y, &num_samples, &radius_offset, &normal_offset, &out_offset); - float radius = stack_load_float(stack, radius_offset); - float3 bevel_N = svm_bevel(kg, sd, state, radius, num_samples); + float3 bevel_N = sd->N; - if (stack_valid(normal_offset)) { - /* Preserve input normal. */ - float3 ref_N = stack_load_float3(stack, normal_offset); - bevel_N = normalize(ref_N + (bevel_N - sd->N)); + IF_KERNEL_NODES_FEATURE(RAYTRACE) + { + float radius = stack_load_float(stack, radius_offset); + +# ifdef __KERNEL_OPTIX__ + bevel_N = optixDirectCall(1, kg, state, sd, radius, num_samples); +# else + bevel_N = svm_bevel(kg, state, sd, radius, num_samples); +# endif + + if (stack_valid(normal_offset)) { + /* Preserve input normal. */ + float3 ref_N = stack_load_float3(stack, normal_offset); + bevel_N = normalize(ref_N + (bevel_N - sd->N)); + } } stack_store_float3(stack, out_offset, bevel_N); diff --git a/intern/cycles/kernel/svm/svm_blackbody.h b/intern/cycles/kernel/svm/blackbody.h similarity index 83% rename from intern/cycles/kernel/svm/svm_blackbody.h rename to intern/cycles/kernel/svm/blackbody.h index adfc50d961e..da15550f918 100644 --- a/intern/cycles/kernel/svm/svm_blackbody.h +++ b/intern/cycles/kernel/svm/blackbody.h @@ -30,12 +30,19 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + +#include "kernel/svm/math_util.h" + CCL_NAMESPACE_BEGIN /* Blackbody Node */ -ccl_device void svm_node_blackbody( - KernelGlobals *kg, ShaderData *sd, float *stack, uint temperature_offset, uint col_offset) +ccl_device_noinline void svm_node_blackbody(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint temperature_offset, + uint col_offset) { /* Input */ float temperature = stack_load_float(stack, temperature_offset); diff --git a/intern/cycles/kernel/svm/svm_brick.h b/intern/cycles/kernel/svm/brick.h similarity index 92% rename from intern/cycles/kernel/svm/svm_brick.h rename to intern/cycles/kernel/svm/brick.h index 6984afa30a5..d8d01766106 100644 --- a/intern/cycles/kernel/svm/svm_brick.h +++ b/intern/cycles/kernel/svm/brick.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Brick */ @@ -54,7 +56,7 @@ ccl_device_noinline_cpu float2 svm_brick(float3 p, x = (p.x + offset) - brick_width * bricknum; y = p.y - row_height * rownum; - float tint = saturate((brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias)); + float tint = saturatef((brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias)); float min_dist = min(min(x, y), min(brick_width - x, row_height - y)); float mortar; @@ -72,12 +74,12 @@ ccl_device_noinline_cpu float2 svm_brick(float3 p, return make_float2(tint, mortar); } -ccl_device void svm_node_tex_brick( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_brick( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { - uint4 node2 = read_node(kg, offset); - uint4 node3 = read_node(kg, offset); - uint4 node4 = read_node(kg, offset); + uint4 node2 = read_node(kg, &offset); + uint4 node3 = read_node(kg, &offset); + uint4 node4 = read_node(kg, &offset); /* Input and Output Sockets */ uint co_offset, color1_offset, color2_offset, mortar_offset, scale_offset; @@ -133,6 +135,7 @@ ccl_device void svm_node_tex_brick( stack_store_float3(stack, color_offset, color1 * (1.0f - f) + mortar * f); if (stack_valid(fac_offset)) stack_store_float(stack, fac_offset, f); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_brightness.h b/intern/cycles/kernel/svm/brightness.h similarity index 84% rename from intern/cycles/kernel/svm/svm_brightness.h rename to intern/cycles/kernel/svm/brightness.h index 9554b5946fb..5c82a4347cd 100644 --- a/intern/cycles/kernel/svm/svm_brightness.h +++ b/intern/cycles/kernel/svm/brightness.h @@ -14,10 +14,14 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/color_util.h" + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_brightness( - ShaderData *sd, float *stack, uint in_color, uint out_color, uint node) +ccl_device_noinline void svm_node_brightness( + ccl_private ShaderData *sd, ccl_private float *stack, uint in_color, uint out_color, uint node) { uint bright_offset, contrast_offset; float3 color = stack_load_float3(stack, in_color); diff --git a/intern/cycles/kernel/svm/svm_bump.h b/intern/cycles/kernel/svm/bump.h similarity index 71% rename from intern/cycles/kernel/svm/svm_bump.h rename to intern/cycles/kernel/svm/bump.h index c9d430a2bba..2fae06fa54b 100644 --- a/intern/cycles/kernel/svm/svm_bump.h +++ b/intern/cycles/kernel/svm/bump.h @@ -14,14 +14,16 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Bump Eval Nodes */ -ccl_device void svm_node_enter_bump_eval(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint offset) +ccl_device_noinline void svm_node_enter_bump_eval(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint offset) { /* save state */ stack_store_float3(stack, offset + 0, sd->P); @@ -45,10 +47,10 @@ ccl_device void svm_node_enter_bump_eval(KernelGlobals *kg, } } -ccl_device void svm_node_leave_bump_eval(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint offset) +ccl_device_noinline void svm_node_leave_bump_eval(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint offset) { /* restore state */ sd->P = stack_load_float3(stack, offset + 0); diff --git a/intern/cycles/kernel/svm/svm_camera.h b/intern/cycles/kernel/svm/camera.h similarity index 74% rename from intern/cycles/kernel/svm/svm_camera.h rename to intern/cycles/kernel/svm/camera.h index 21a17acf5f1..c71c02e6b19 100644 --- a/intern/cycles/kernel/svm/svm_camera.h +++ b/intern/cycles/kernel/svm/camera.h @@ -14,14 +14,16 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_camera(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint out_vector, - uint out_zdepth, - uint out_distance) +ccl_device_noinline void svm_node_camera(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint out_vector, + uint out_zdepth, + uint out_distance) { float distance; float zdepth; diff --git a/intern/cycles/kernel/svm/svm_checker.h b/intern/cycles/kernel/svm/checker.h similarity index 86% rename from intern/cycles/kernel/svm/svm_checker.h rename to intern/cycles/kernel/svm/checker.h index d54cb73df91..a79b1651f44 100644 --- a/intern/cycles/kernel/svm/svm_checker.h +++ b/intern/cycles/kernel/svm/checker.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Checker */ @@ -32,7 +34,10 @@ ccl_device float svm_checker(float3 p) return ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f; } -ccl_device void svm_node_tex_checker(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tex_checker(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint co_offset, color1_offset, color2_offset, scale_offset; uint color_offset, fac_offset; diff --git a/intern/cycles/kernel/svm/svm_clamp.h b/intern/cycles/kernel/svm/clamp.h similarity index 70% rename from intern/cycles/kernel/svm/svm_clamp.h rename to intern/cycles/kernel/svm/clamp.h index a85fd82754e..c07c0206d29 100644 --- a/intern/cycles/kernel/svm/svm_clamp.h +++ b/intern/cycles/kernel/svm/clamp.h @@ -14,22 +14,24 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Clamp Node */ -ccl_device void svm_node_clamp(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint value_stack_offset, - uint parameters_stack_offsets, - uint result_stack_offset, - int *offset) +ccl_device_noinline int svm_node_clamp(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint value_stack_offset, + uint parameters_stack_offsets, + uint result_stack_offset, + int offset) { uint min_stack_offset, max_stack_offset, type; svm_unpack_node_uchar3(parameters_stack_offsets, &min_stack_offset, &max_stack_offset, &type); - uint4 defaults = read_node(kg, offset); + uint4 defaults = read_node(kg, &offset); float value = stack_load_float(stack, value_stack_offset); float min = stack_load_float_default(stack, min_stack_offset, defaults.x); @@ -41,6 +43,7 @@ ccl_device void svm_node_clamp(KernelGlobals *kg, else { stack_store_float(stack, result_stack_offset, clamp(value, min, max)); } + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_closure.h b/intern/cycles/kernel/svm/closure.h similarity index 78% rename from intern/cycles/kernel/svm/svm_closure.h rename to intern/cycles/kernel/svm/closure.h index bbe8d72edf0..71952e9e0f8 100644 --- a/intern/cycles/kernel/svm/svm_closure.h +++ b/intern/cycles/kernel/svm/closure.h @@ -14,12 +14,18 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Closure Nodes */ -ccl_device void svm_node_glass_setup( - ShaderData *sd, MicrofacetBsdf *bsdf, int type, float eta, float roughness, bool refract) +ccl_device void svm_node_glass_setup(ccl_private ShaderData *sd, + ccl_private MicrofacetBsdf *bsdf, + int type, + float eta, + float roughness, + bool refract) { if (type == CLOSURE_BSDF_SHARP_GLASS_ID) { if (refract) { @@ -57,13 +63,26 @@ ccl_device void svm_node_glass_setup( } } -ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node, - ShaderType shader_type, - int path_flag, - int *offset) +ccl_device_inline int svm_node_closure_bsdf_skip(KernelGlobals kg, int offset, uint type) +{ + if (type == CLOSURE_BSDF_PRINCIPLED_ID) { + /* Read all principled BSDF extra data to get the right offset. */ + read_node(kg, &offset); + read_node(kg, &offset); + read_node(kg, &offset); + read_node(kg, &offset); + } + + return offset; +} + +template +ccl_device_noinline int svm_node_closure_bsdf(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + uint32_t path_flag, + int offset) { uint type, param1_offset, param2_offset; @@ -73,22 +92,24 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, 1.0f); /* note we read this extra node before weight check, so offset is added */ - uint4 data_node = read_node(kg, offset); + uint4 data_node = read_node(kg, &offset); /* Only compute BSDF for surfaces, transparent variable is shared with volume extinction. */ - if (mix_weight == 0.0f || shader_type != SHADER_TYPE_SURFACE) { - if (type == CLOSURE_BSDF_PRINCIPLED_ID) { - /* Read all principled BSDF extra data to get the right offset. */ - read_node(kg, offset); - read_node(kg, offset); - read_node(kg, offset); - read_node(kg, offset); + IF_KERNEL_NODES_FEATURE(BSDF) + { + if ((shader_type != SHADER_TYPE_SURFACE) || mix_weight == 0.0f) { + return svm_node_closure_bsdf_skip(kg, offset, type); } - - return; + } + else + { + return svm_node_closure_bsdf_skip(kg, offset, type); } float3 N = stack_valid(data_node.x) ? stack_load_float3(stack, data_node.x) : sd->N; + if (!(sd->type & PRIMITIVE_ALL_CURVE)) { + N = ensure_valid_reflection(sd->Ng, sd->I, N); + } float param1 = (stack_valid(param1_offset)) ? stack_load_float(stack, param1_offset) : __uint_as_float(node.z); @@ -102,7 +123,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, sheen_offset, sheen_tint_offset, clearcoat_offset, clearcoat_roughness_offset, eta_offset, transmission_offset, anisotropic_rotation_offset, transmission_roughness_offset; - uint4 data_node2 = read_node(kg, offset); + uint4 data_node2 = read_node(kg, &offset); float3 T = stack_load_float3(stack, data_node.y); svm_unpack_node_uchar4(data_node.z, @@ -152,13 +173,13 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, float fresnel = fresnel_dielectric_cos(cosNO, ior); // calculate weights of the diffuse and specular part - float diffuse_weight = (1.0f - saturate(metallic)) * (1.0f - saturate(transmission)); + float diffuse_weight = (1.0f - saturatef(metallic)) * (1.0f - saturatef(transmission)); - float final_transmission = saturate(transmission) * (1.0f - saturate(metallic)); + float final_transmission = saturatef(transmission) * (1.0f - saturatef(metallic)); float specular_weight = (1.0f - final_transmission); // get the base color - uint4 data_base_color = read_node(kg, offset); + uint4 data_base_color = read_node(kg, &offset); float3 base_color = stack_valid(data_base_color.x) ? stack_load_float3(stack, data_base_color.x) : make_float3(__uint_as_float(data_base_color.y), @@ -166,16 +187,24 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, __uint_as_float(data_base_color.w)); // get the additional clearcoat normal and subsurface scattering radius - uint4 data_cn_ssr = read_node(kg, offset); + uint4 data_cn_ssr = read_node(kg, &offset); float3 clearcoat_normal = stack_valid(data_cn_ssr.x) ? stack_load_float3(stack, data_cn_ssr.x) : sd->N; + if (!(sd->type & PRIMITIVE_ALL_CURVE)) { + clearcoat_normal = ensure_valid_reflection(sd->Ng, sd->I, clearcoat_normal); + } float3 subsurface_radius = stack_valid(data_cn_ssr.y) ? stack_load_float3(stack, data_cn_ssr.y) : make_float3(1.0f, 1.0f, 1.0f); + float subsurface_ior = stack_valid(data_cn_ssr.z) ? stack_load_float(stack, data_cn_ssr.z) : + 1.4f; + float subsurface_anisotropy = stack_valid(data_cn_ssr.w) ? + stack_load_float(stack, data_cn_ssr.w) : + 0.0f; // get the subsurface color - uint4 data_subsurface_color = read_node(kg, offset); + uint4 data_subsurface_color = read_node(kg, &offset); float3 subsurface_color = stack_valid(data_subsurface_color.x) ? stack_load_float3(stack, data_subsurface_color.x) : make_float3(__uint_as_float(data_subsurface_color.y), @@ -206,32 +235,32 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (subsurface <= CLOSURE_WEIGHT_CUTOFF && diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { float3 diff_weight = weight * base_color * diffuse_weight; - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( - sd, sizeof(PrincipledDiffuseBsdf), diff_weight); + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *) + bsdf_alloc(sd, sizeof(PrincipledDiffuseBsdf), diff_weight); if (bsdf) { bsdf->N = N; bsdf->roughness = roughness; /* setup bsdf */ - sd->flag |= bsdf_principled_diffuse_setup(bsdf); + sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_FULL); } } else if (subsurface > CLOSURE_WEIGHT_CUTOFF) { - Bssrdf *bssrdf = bssrdf_alloc(sd, subsurf_weight); + ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, subsurf_weight); if (bssrdf) { bssrdf->radius = subsurface_radius * subsurface; - bssrdf->albedo = (subsurface_method == CLOSURE_BSSRDF_PRINCIPLED_ID) ? - subsurface_color : - mixed_ss_base_color; - bssrdf->texture_blur = 0.0f; - bssrdf->sharpness = 0.0f; + bssrdf->albedo = mixed_ss_base_color; bssrdf->N = N; bssrdf->roughness = roughness; + /* Clamps protecting against bad/extreme and non physical values. */ + subsurface_ior = clamp(subsurface_ior, 1.01f, 3.8f); + bssrdf->anisotropy = clamp(subsurface_anisotropy, 0.0f, 0.9f); + /* setup bsdf */ - sd->flag |= bssrdf_setup(sd, bssrdf, subsurface_method); + sd->flag |= bssrdf_setup(sd, bssrdf, subsurface_method, subsurface_ior); } } } @@ -240,7 +269,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (diffuse_weight > CLOSURE_WEIGHT_CUTOFF) { float3 diff_weight = weight * base_color * diffuse_weight; - PrincipledDiffuseBsdf *bsdf = (PrincipledDiffuseBsdf *)bsdf_alloc( + ccl_private PrincipledDiffuseBsdf *bsdf = (ccl_private PrincipledDiffuseBsdf *)bsdf_alloc( sd, sizeof(PrincipledDiffuseBsdf), diff_weight); if (bsdf) { @@ -248,7 +277,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, bsdf->roughness = roughness; /* setup bsdf */ - sd->flag |= bsdf_principled_diffuse_setup(bsdf); + sd->flag |= bsdf_principled_diffuse_setup(bsdf, PRINCIPLED_DIFFUSE_FULL); } } # endif @@ -266,7 +295,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, float3 sheen_weight = weight * sheen * sheen_color * diffuse_weight; - PrincipledSheenBsdf *bsdf = (PrincipledSheenBsdf *)bsdf_alloc( + ccl_private PrincipledSheenBsdf *bsdf = (ccl_private PrincipledSheenBsdf *)bsdf_alloc( sd, sizeof(PrincipledSheenBsdf), sheen_weight); if (bsdf) { @@ -285,11 +314,12 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, (specular > CLOSURE_WEIGHT_CUTOFF || metallic > CLOSURE_WEIGHT_CUTOFF)) { float3 spec_weight = weight * specular_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), spec_weight); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? + (ccl_private MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = N; @@ -348,11 +378,12 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) # endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), glass_weight * fresnel); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = N; @@ -377,7 +408,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) # endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), base_color * glass_weight * (1.0f - fresnel)); if (bsdf) { bsdf->N = N; @@ -400,11 +431,12 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, } } else { /* use multi-scatter GGX */ - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), glass_weight); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = N; @@ -433,10 +465,12 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) { # endif if (clearcoat > CLOSURE_WEIGHT_CUTOFF) { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); - MicrofacetExtra *extra = (bsdf != NULL) ? (MicrofacetExtra *)closure_alloc_extra( - sd, sizeof(MicrofacetExtra)) : - NULL; + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetExtra *extra = + (bsdf != NULL) ? + (ccl_private MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)) : + NULL; if (bsdf && extra) { bsdf->N = clearcoat_normal; @@ -464,7 +498,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, #endif /* __PRINCIPLED__ */ case CLOSURE_BSDF_DIFFUSE_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - OrenNayarBsdf *bsdf = (OrenNayarBsdf *)bsdf_alloc(sd, sizeof(OrenNayarBsdf), weight); + ccl_private OrenNayarBsdf *bsdf = (ccl_private OrenNayarBsdf *)bsdf_alloc( + sd, sizeof(OrenNayarBsdf), weight); if (bsdf) { bsdf->N = N; @@ -472,7 +507,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, float roughness = param1; if (roughness == 0.0f) { - sd->flag |= bsdf_diffuse_setup((DiffuseBsdf *)bsdf); + sd->flag |= bsdf_diffuse_setup((ccl_private DiffuseBsdf *)bsdf); } else { bsdf->roughness = roughness; @@ -483,7 +518,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, } case CLOSURE_BSDF_TRANSLUCENT_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - DiffuseBsdf *bsdf = (DiffuseBsdf *)bsdf_alloc(sd, sizeof(DiffuseBsdf), weight); + ccl_private DiffuseBsdf *bsdf = (ccl_private DiffuseBsdf *)bsdf_alloc( + sd, sizeof(DiffuseBsdf), weight); if (bsdf) { bsdf->N = N; @@ -506,7 +542,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, break; #endif float3 weight = sd->svm_closure_weight * mix_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); if (!bsdf) { break; @@ -552,7 +589,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, sd->flag |= bsdf_microfacet_ggx_setup(bsdf); else if (type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_ID) { kernel_assert(stack_valid(data_node.w)); - bsdf->extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + bsdf->extra = (ccl_private MicrofacetExtra *)closure_alloc_extra(sd, + sizeof(MicrofacetExtra)); if (bsdf->extra) { bsdf->extra->color = stack_load_float3(stack, data_node.w); bsdf->extra->cspec0 = make_float3(0.0f, 0.0f, 0.0f); @@ -574,7 +612,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, break; #endif float3 weight = sd->svm_closure_weight * mix_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); if (bsdf) { bsdf->N = N; @@ -632,7 +671,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (kernel_data.integrator.caustics_reflective || (path_flag & PATH_RAY_DIFFUSE) == 0) #endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight * fresnel); if (bsdf) { @@ -648,7 +687,7 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, if (kernel_data.integrator.caustics_refractive || (path_flag & PATH_RAY_DIFFUSE) == 0) #endif { - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc( + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( sd, sizeof(MicrofacetBsdf), weight * (1.0f - fresnel)); if (bsdf) { @@ -668,12 +707,14 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, break; #endif float3 weight = sd->svm_closure_weight * mix_weight; - MicrofacetBsdf *bsdf = (MicrofacetBsdf *)bsdf_alloc(sd, sizeof(MicrofacetBsdf), weight); + ccl_private MicrofacetBsdf *bsdf = (ccl_private MicrofacetBsdf *)bsdf_alloc( + sd, sizeof(MicrofacetBsdf), weight); if (!bsdf) { break; } - MicrofacetExtra *extra = (MicrofacetExtra *)closure_alloc_extra(sd, sizeof(MicrofacetExtra)); + ccl_private MicrofacetExtra *extra = (ccl_private MicrofacetExtra *)closure_alloc_extra( + sd, sizeof(MicrofacetExtra)); if (!extra) { break; } @@ -699,12 +740,13 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, } case CLOSURE_BSDF_ASHIKHMIN_VELVET_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - VelvetBsdf *bsdf = (VelvetBsdf *)bsdf_alloc(sd, sizeof(VelvetBsdf), weight); + ccl_private VelvetBsdf *bsdf = (ccl_private VelvetBsdf *)bsdf_alloc( + sd, sizeof(VelvetBsdf), weight); if (bsdf) { bsdf->N = N; - bsdf->sigma = saturate(param1); + bsdf->sigma = saturatef(param1); sd->flag |= bsdf_ashikhmin_velvet_setup(bsdf); } break; @@ -717,7 +759,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, #endif case CLOSURE_BSDF_DIFFUSE_TOON_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - ToonBsdf *bsdf = (ToonBsdf *)bsdf_alloc(sd, sizeof(ToonBsdf), weight); + ccl_private ToonBsdf *bsdf = (ccl_private ToonBsdf *)bsdf_alloc( + sd, sizeof(ToonBsdf), weight); if (bsdf) { bsdf->N = N; @@ -733,9 +776,9 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, } #ifdef __HAIR__ case CLOSURE_BSDF_HAIR_PRINCIPLED_ID: { - uint4 data_node2 = read_node(kg, offset); - uint4 data_node3 = read_node(kg, offset); - uint4 data_node4 = read_node(kg, offset); + uint4 data_node2 = read_node(kg, &offset); + uint4 data_node3 = read_node(kg, &offset); + uint4 data_node4 = read_node(kg, &offset); float3 weight = sd->svm_closure_weight * mix_weight; @@ -764,11 +807,11 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, random = stack_load_float_default(stack, random_ofs, data_node3.y); } - PrincipledHairBSDF *bsdf = (PrincipledHairBSDF *)bsdf_alloc( + ccl_private PrincipledHairBSDF *bsdf = (ccl_private PrincipledHairBSDF *)bsdf_alloc( sd, sizeof(PrincipledHairBSDF), weight); if (bsdf) { - PrincipledHairExtra *extra = (PrincipledHairExtra *)closure_alloc_extra( - sd, sizeof(PrincipledHairExtra)); + ccl_private PrincipledHairExtra *extra = (ccl_private PrincipledHairExtra *) + closure_alloc_extra(sd, sizeof(PrincipledHairExtra)); if (!extra) break; @@ -847,7 +890,8 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, case CLOSURE_BSDF_HAIR_TRANSMISSION_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - HairBsdf *bsdf = (HairBsdf *)bsdf_alloc(sd, sizeof(HairBsdf), weight); + ccl_private HairBsdf *bsdf = (ccl_private HairBsdf *)bsdf_alloc( + sd, sizeof(HairBsdf), weight); if (bsdf) { bsdf->N = N; @@ -878,12 +922,11 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, #endif /* __HAIR__ */ #ifdef __SUBSURFACE__ - case CLOSURE_BSSRDF_CUBIC_ID: - case CLOSURE_BSSRDF_GAUSSIAN_ID: case CLOSURE_BSSRDF_BURLEY_ID: - case CLOSURE_BSSRDF_RANDOM_WALK_ID: { + case CLOSURE_BSSRDF_RANDOM_WALK_ID: + case CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID: { float3 weight = sd->svm_closure_weight * mix_weight; - Bssrdf *bssrdf = bssrdf_alloc(sd, weight); + ccl_private Bssrdf *bssrdf = bssrdf_alloc(sd, weight); if (bssrdf) { /* disable in case of diffuse ancestor, can't see it well then and @@ -894,11 +937,14 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, bssrdf->radius = stack_load_float3(stack, data_node.z) * param1; bssrdf->albedo = sd->svm_closure_weight; - bssrdf->texture_blur = param2; - bssrdf->sharpness = stack_load_float(stack, data_node.w); bssrdf->N = N; - bssrdf->roughness = 0.0f; - sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type); + bssrdf->roughness = FLT_MAX; + + const float subsurface_ior = clamp(param2, 1.01f, 3.8f); + const float subsurface_anisotropy = stack_load_float(stack, data_node.w); + bssrdf->anisotropy = clamp(subsurface_anisotropy, 0.0f, 0.9f); + + sd->flag |= bssrdf_setup(sd, bssrdf, (ClosureType)type, subsurface_ior); } break; @@ -907,10 +953,15 @@ ccl_device void svm_node_closure_bsdf(KernelGlobals *kg, default: break; } + + return offset; } -ccl_device void svm_node_closure_volume( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ShaderType shader_type) +template +ccl_device_noinline void svm_node_closure_volume(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { #ifdef __VOLUME__ /* Only sum extinction for volumes, variable is shared with surface transparency. */ @@ -944,7 +995,7 @@ ccl_device void svm_node_closure_volume( /* Add closure for volume scattering. */ if (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) { - HenyeyGreensteinVolume *volume = (HenyeyGreensteinVolume *)bsdf_alloc( + ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc( sd, sizeof(HenyeyGreensteinVolume), weight); if (volume) { @@ -961,21 +1012,21 @@ ccl_device void svm_node_closure_volume( #endif } -ccl_device void svm_node_principled_volume(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node, - ShaderType shader_type, - int path_flag, - int *offset) +template +ccl_device_noinline int svm_node_principled_volume(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node, + uint32_t path_flag, + int offset) { #ifdef __VOLUME__ - uint4 value_node = read_node(kg, offset); - uint4 attr_node = read_node(kg, offset); + uint4 value_node = read_node(kg, &offset); + uint4 attr_node = read_node(kg, &offset); /* Only sum extinction for volumes, variable is shared with surface transparency. */ if (shader_type != SHADER_TYPE_VOLUME) { - return; + return offset; } uint density_offset, anisotropy_offset, absorption_color_offset, mix_weight_offset; @@ -985,7 +1036,7 @@ ccl_device void svm_node_principled_volume(KernelGlobals *kg, 1.0f); if (mix_weight == 0.0f) { - return; + return offset; } /* Compute density. */ @@ -1013,7 +1064,7 @@ ccl_device void svm_node_principled_volume(KernelGlobals *kg, } /* Add closure for volume scattering. */ - HenyeyGreensteinVolume *volume = (HenyeyGreensteinVolume *)bsdf_alloc( + ccl_private HenyeyGreensteinVolume *volume = (ccl_private HenyeyGreensteinVolume *)bsdf_alloc( sd, sizeof(HenyeyGreensteinVolume), color * density); if (volume) { float anisotropy = (stack_valid(anisotropy_offset)) ? @@ -1034,7 +1085,7 @@ ccl_device void svm_node_principled_volume(KernelGlobals *kg, /* Compute emission. */ if (path_flag & PATH_RAY_SHADOW) { /* Don't need emission for shadows. */ - return; + return offset; } uint emission_offset, emission_color_offset, blackbody_offset, temperature_offset; @@ -1074,9 +1125,12 @@ ccl_device void svm_node_principled_volume(KernelGlobals *kg, } } #endif + return offset; } -ccl_device void svm_node_closure_emission(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_emission(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint mix_weight_offset = node.y; float3 weight = sd->svm_closure_weight; @@ -1093,7 +1147,9 @@ ccl_device void svm_node_closure_emission(ShaderData *sd, float *stack, uint4 no emission_setup(sd, weight); } -ccl_device void svm_node_closure_background(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_background(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint mix_weight_offset = node.y; float3 weight = sd->svm_closure_weight; @@ -1110,7 +1166,9 @@ ccl_device void svm_node_closure_background(ShaderData *sd, float *stack, uint4 background_setup(sd, weight); } -ccl_device void svm_node_closure_holdout(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_closure_holdout(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint mix_weight_offset = node.y; @@ -1131,28 +1189,29 @@ ccl_device void svm_node_closure_holdout(ShaderData *sd, float *stack, uint4 nod /* Closure Nodes */ -ccl_device_inline void svm_node_closure_store_weight(ShaderData *sd, float3 weight) +ccl_device_inline void svm_node_closure_store_weight(ccl_private ShaderData *sd, float3 weight) { sd->svm_closure_weight = weight; } -ccl_device void svm_node_closure_set_weight(ShaderData *sd, uint r, uint g, uint b) +ccl_device void svm_node_closure_set_weight(ccl_private ShaderData *sd, uint r, uint g, uint b) { float3 weight = make_float3(__uint_as_float(r), __uint_as_float(g), __uint_as_float(b)); svm_node_closure_store_weight(sd, weight); } -ccl_device void svm_node_closure_weight(ShaderData *sd, float *stack, uint weight_offset) +ccl_device void svm_node_closure_weight(ccl_private ShaderData *sd, + ccl_private float *stack, + uint weight_offset) { float3 weight = stack_load_float3(stack, weight_offset); - svm_node_closure_store_weight(sd, weight); } -ccl_device void svm_node_emission_weight(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node) +ccl_device_noinline void svm_node_emission_weight(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint color_offset = node.y; uint strength_offset = node.z; @@ -1163,7 +1222,9 @@ ccl_device void svm_node_emission_weight(KernelGlobals *kg, svm_node_closure_store_weight(sd, weight); } -ccl_device void svm_node_mix_closure(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_mix_closure(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { /* fetch weight from blend input, previous mix closures, * and write to stack to be used by closure nodes later */ @@ -1172,7 +1233,7 @@ ccl_device void svm_node_mix_closure(ShaderData *sd, float *stack, uint4 node) node.y, &weight_offset, &in_weight_offset, &weight1_offset, &weight2_offset); float weight = stack_load_float(stack, weight_offset); - weight = saturate(weight); + weight = saturatef(weight); float in_weight = (stack_valid(in_weight_offset)) ? stack_load_float(stack, in_weight_offset) : 1.0f; @@ -1185,8 +1246,11 @@ ccl_device void svm_node_mix_closure(ShaderData *sd, float *stack, uint4 node) /* (Bump) normal */ -ccl_device void svm_node_set_normal( - KernelGlobals *kg, ShaderData *sd, float *stack, uint in_direction, uint out_normal) +ccl_device void svm_node_set_normal(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_direction, + uint out_normal) { float3 normal = stack_load_float3(stack, in_direction); sd->N = normal; diff --git a/intern/cycles/kernel/svm/svm_color_util.h b/intern/cycles/kernel/svm/color_util.h similarity index 99% rename from intern/cycles/kernel/svm/svm_color_util.h rename to intern/cycles/kernel/svm/color_util.h index 1a0fa03305e..0c1a510e655 100644 --- a/intern/cycles/kernel/svm/svm_color_util.h +++ b/intern/cycles/kernel/svm/color_util.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device float3 svm_mix_blend(float t, float3 col1, float3 col2) @@ -260,7 +262,7 @@ ccl_device float3 svm_mix_clamp(float3 col) ccl_device_noinline_cpu float3 svm_mix(NodeMix type, float fac, float3 c1, float3 c2) { - float t = saturate(fac); + float t = saturatef(fac); switch (type) { case NODE_MIX_BLEND: diff --git a/intern/cycles/kernel/svm/svm_convert.h b/intern/cycles/kernel/svm/convert.h similarity index 84% rename from intern/cycles/kernel/svm/svm_convert.h rename to intern/cycles/kernel/svm/convert.h index 5df6c9fb755..427ffd97f59 100644 --- a/intern/cycles/kernel/svm/svm_convert.h +++ b/intern/cycles/kernel/svm/convert.h @@ -14,12 +14,18 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Conversion Nodes */ -ccl_device void svm_node_convert( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint from, uint to) +ccl_device_noinline void svm_node_convert(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint from, + uint to) { switch (type) { case NODE_CONVERT_FI: { diff --git a/intern/cycles/kernel/svm/svm_displace.h b/intern/cycles/kernel/svm/displace.h similarity index 83% rename from intern/cycles/kernel/svm/svm_displace.h rename to intern/cycles/kernel/svm/displace.h index 250fac6bcb8..cea1436f36d 100644 --- a/intern/cycles/kernel/svm/svm_displace.h +++ b/intern/cycles/kernel/svm/displace.h @@ -14,11 +14,18 @@ * limitations under the License. */ +#pragma once + +#include "kernel/sample/mapping.h" + CCL_NAMESPACE_BEGIN /* Bump Node */ -ccl_device void svm_node_set_bump(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_set_bump(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { #ifdef __RAY_DIFFERENTIALS__ /* get normal input */ @@ -83,16 +90,19 @@ ccl_device void svm_node_set_bump(KernelGlobals *kg, ShaderData *sd, float *stac /* Displacement Node */ -ccl_device void svm_node_set_displacement(KernelGlobals *kg, - ShaderData *sd, - float *stack, +ccl_device void svm_node_set_displacement(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, uint fac_offset) { float3 dP = stack_load_float3(stack, fac_offset); sd->P += dP; } -ccl_device void svm_node_displacement(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_displacement(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint height_offset, midlevel_offset, scale_offset, normal_offset; svm_unpack_node_uchar4(node.y, &height_offset, &midlevel_offset, &scale_offset, &normal_offset); @@ -119,10 +129,10 @@ ccl_device void svm_node_displacement(KernelGlobals *kg, ShaderData *sd, float * stack_store_float3(stack, node.z, dP); } -ccl_device void svm_node_vector_displacement( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_vector_displacement( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { - uint4 data_node = read_node(kg, offset); + uint4 data_node = read_node(kg, &offset); uint space = data_node.x; uint vector_offset, midlevel_offset, scale_offset, displacement_offset; @@ -164,6 +174,7 @@ ccl_device void svm_node_vector_displacement( } stack_store_float3(stack, displacement_offset, dP); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_fractal_noise.h b/intern/cycles/kernel/svm/fractal_noise.h similarity index 94% rename from intern/cycles/kernel/svm/svm_fractal_noise.h rename to intern/cycles/kernel/svm/fractal_noise.h index 57fa8c690ac..8256a24c751 100644 --- a/intern/cycles/kernel/svm/svm_fractal_noise.h +++ b/intern/cycles/kernel/svm/fractal_noise.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/noise.h" + CCL_NAMESPACE_BEGIN /* The fractal_noise_[1-4] functions are all exactly the same except for the input type. */ @@ -23,7 +27,7 @@ ccl_device_noinline float fractal_noise_1d(float p, float octaves, float roughne float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_1d(fscale * p); @@ -52,7 +56,7 @@ ccl_device_noinline float fractal_noise_2d(float2 p, float octaves, float roughn float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_2d(fscale * p); @@ -81,7 +85,7 @@ ccl_device_noinline float fractal_noise_3d(float3 p, float octaves, float roughn float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_3d(fscale * p); @@ -110,7 +114,7 @@ ccl_device_noinline float fractal_noise_4d(float4 p, float octaves, float roughn float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = clamp(octaves, 0.0f, 16.0f); + octaves = clamp(octaves, 0.0f, 15.0f); int n = float_to_int(octaves); for (int i = 0; i <= n; i++) { float t = noise_4d(fscale * p); diff --git a/intern/cycles/kernel/svm/svm_fresnel.h b/intern/cycles/kernel/svm/fresnel.h similarity index 80% rename from intern/cycles/kernel/svm/svm_fresnel.h rename to intern/cycles/kernel/svm/fresnel.h index 96d602e35bf..9dd68c3e38f 100644 --- a/intern/cycles/kernel/svm/svm_fresnel.h +++ b/intern/cycles/kernel/svm/fresnel.h @@ -14,12 +14,17 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Fresnel Node */ -ccl_device void svm_node_fresnel( - ShaderData *sd, float *stack, uint ior_offset, uint ior_value, uint node) +ccl_device_noinline void svm_node_fresnel(ccl_private ShaderData *sd, + ccl_private float *stack, + uint ior_offset, + uint ior_value, + uint node) { uint normal_offset, out_offset; svm_unpack_node_uchar2(node, &normal_offset, &out_offset); @@ -37,7 +42,9 @@ ccl_device void svm_node_fresnel( /* Layer Weight Node */ -ccl_device void svm_node_layer_weight(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_layer_weight(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint blend_offset = node.y; uint blend_value = node.z; diff --git a/intern/cycles/kernel/svm/svm_gamma.h b/intern/cycles/kernel/svm/gamma.h similarity index 73% rename from intern/cycles/kernel/svm/svm_gamma.h rename to intern/cycles/kernel/svm/gamma.h index 65eb08eb0eb..9f89e780be9 100644 --- a/intern/cycles/kernel/svm/svm_gamma.h +++ b/intern/cycles/kernel/svm/gamma.h @@ -14,10 +14,15 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_gamma( - ShaderData *sd, float *stack, uint in_gamma, uint in_color, uint out_color) +ccl_device_noinline void svm_node_gamma(ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_gamma, + uint in_color, + uint out_color) { float3 color = stack_load_float3(stack, in_color); float gamma = stack_load_float(stack, in_gamma); diff --git a/intern/cycles/kernel/svm/svm_geometry.h b/intern/cycles/kernel/svm/geometry.h similarity index 72% rename from intern/cycles/kernel/svm/svm_geometry.h rename to intern/cycles/kernel/svm/geometry.h index e48e96dcfa4..772942e0c08 100644 --- a/intern/cycles/kernel/svm/svm_geometry.h +++ b/intern/cycles/kernel/svm/geometry.h @@ -14,12 +14,17 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Geometry Node */ -ccl_device_inline void svm_node_geometry( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { float3 data; @@ -51,8 +56,11 @@ ccl_device_inline void svm_node_geometry( stack_store_float3(stack, out_offset, data); } -ccl_device void svm_node_geometry_bump_dx( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry_bump_dx(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -75,8 +83,11 @@ ccl_device void svm_node_geometry_bump_dx( #endif } -ccl_device void svm_node_geometry_bump_dy( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_geometry_bump_dy(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -101,8 +112,11 @@ ccl_device void svm_node_geometry_bump_dy( /* Object Info */ -ccl_device void svm_node_object_info( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_object_info(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { float data; @@ -140,8 +154,11 @@ ccl_device void svm_node_object_info( /* Particle Info */ -ccl_device void svm_node_particle_info( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_particle_info(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { switch (type) { case NODE_INFO_PAR_INDEX: { @@ -199,8 +216,11 @@ ccl_device void svm_node_particle_info( /* Hair Info */ -ccl_device void svm_node_hair_info( - KernelGlobals *kg, ShaderData *sd, float *stack, uint type, uint out_offset) +ccl_device_noinline void svm_node_hair_info(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset) { float data; float3 data3; @@ -213,6 +233,8 @@ ccl_device void svm_node_hair_info( } case NODE_INFO_CURVE_INTERCEPT: break; /* handled as attribute */ + case NODE_INFO_CURVE_LENGTH: + break; /* handled as attribute */ case NODE_INFO_CURVE_RANDOM: break; /* handled as attribute */ case NODE_INFO_CURVE_THICKNESS: { diff --git a/intern/cycles/kernel/svm/svm_gradient.h b/intern/cycles/kernel/svm/gradient.h similarity index 89% rename from intern/cycles/kernel/svm/svm_gradient.h rename to intern/cycles/kernel/svm/gradient.h index 08304bc47e8..42d8dbef792 100644 --- a/intern/cycles/kernel/svm/svm_gradient.h +++ b/intern/cycles/kernel/svm/gradient.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Gradient */ @@ -60,7 +62,9 @@ ccl_device float svm_gradient(float3 p, NodeGradientType type) return 0.0f; } -ccl_device void svm_node_tex_gradient(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tex_gradient(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint type, co_offset, color_offset, fac_offset; @@ -69,7 +73,7 @@ ccl_device void svm_node_tex_gradient(ShaderData *sd, float *stack, uint4 node) float3 co = stack_load_float3(stack, co_offset); float f = svm_gradient(co, (NodeGradientType)type); - f = saturate(f); + f = saturatef(f); if (stack_valid(fac_offset)) stack_store_float(stack, fac_offset, f); diff --git a/intern/cycles/kernel/svm/svm_hsv.h b/intern/cycles/kernel/svm/hsv.h similarity index 86% rename from intern/cycles/kernel/svm/svm_hsv.h rename to intern/cycles/kernel/svm/hsv.h index c299cf58c7f..fdb266883fa 100644 --- a/intern/cycles/kernel/svm/svm_hsv.h +++ b/intern/cycles/kernel/svm/hsv.h @@ -14,13 +14,14 @@ * limitations under the License. */ -#ifndef __SVM_HSV_H__ -#define __SVM_HSV_H__ +#pragma once CCL_NAMESPACE_BEGIN -ccl_device void svm_node_hsv( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline void svm_node_hsv(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint in_color_offset, fac_offset, out_color_offset; uint hue_offset, sat_offset, val_offset; @@ -39,7 +40,7 @@ ccl_device void svm_node_hsv( /* Remember: `fmodf` doesn't work for negative numbers here. */ color.x = fmodf(color.x + hue + 0.5f, 1.0f); - color.y = saturate(color.y * sat); + color.y = saturatef(color.y * sat); color.z *= val; color = hsv_to_rgb(color); @@ -58,5 +59,3 @@ ccl_device void svm_node_hsv( } CCL_NAMESPACE_END - -#endif /* __SVM_HSV_H__ */ diff --git a/intern/cycles/kernel/svm/svm_ies.h b/intern/cycles/kernel/svm/ies.h similarity index 91% rename from intern/cycles/kernel/svm/svm_ies.h rename to intern/cycles/kernel/svm/ies.h index 56c804b44d0..f0923720878 100644 --- a/intern/cycles/kernel/svm/svm_ies.h +++ b/intern/cycles/kernel/svm/ies.h @@ -14,12 +14,14 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* IES Light */ ccl_device_inline float interpolate_ies_vertical( - KernelGlobals *kg, int ofs, int v, int v_num, float v_frac, int h) + KernelGlobals kg, int ofs, int v, int v_num, float v_frac, int h) { /* Since lookups are performed in spherical coordinates, clamping the coordinates at the low end * of v (corresponding to the north pole) would result in artifacts. The proper way of dealing @@ -39,10 +41,7 @@ ccl_device_inline float interpolate_ies_vertical( return cubic_interp(a, b, c, d, v_frac); } -ccl_device_inline float kernel_ies_interp(KernelGlobals *kg, - int slot, - float h_angle, - float v_angle) +ccl_device_inline float kernel_ies_interp(KernelGlobals kg, int slot, float h_angle, float v_angle) { /* Find offset of the IES data in the table. */ int ofs = __float_as_int(kernel_tex_fetch(__ies, slot)); @@ -98,8 +97,10 @@ ccl_device_inline float kernel_ies_interp(KernelGlobals *kg, return max(cubic_interp(a, b, c, d, h_frac), 0.0f); } -ccl_device void svm_node_ies( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline void svm_node_ies(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint vector_offset, strength_offset, fac_offset, slot = node.z; svm_unpack_node_uchar3(node.y, &strength_offset, &vector_offset, &fac_offset); diff --git a/intern/cycles/kernel/svm/svm_image.h b/intern/cycles/kernel/svm/image.h similarity index 84% rename from intern/cycles/kernel/svm/svm_image.h rename to intern/cycles/kernel/svm/image.h index 9348ddabde5..2ebd3d4eb87 100644 --- a/intern/cycles/kernel/svm/svm_image.h +++ b/intern/cycles/kernel/svm/image.h @@ -14,9 +14,11 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device float4 svm_image_texture(KernelGlobals *kg, int id, float x, float y, uint flags) +ccl_device float4 svm_image_texture(KernelGlobals kg, int id, float x, float y, uint flags) { if (id == -1) { return make_float4( @@ -44,8 +46,8 @@ ccl_device_inline float3 texco_remap_square(float3 co) return (co - make_float3(0.5f, 0.5f, 0.5f)) * 2.0f; } -ccl_device void svm_node_tex_image( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_image( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint co_offset, out_offset, alpha_offset, flags; @@ -71,7 +73,7 @@ ccl_device void svm_node_tex_image( int num_nodes = (int)node.y; if (num_nodes > 0) { /* Remember the offset of the node following the tile nodes. */ - int next_offset = (*offset) + num_nodes; + int next_offset = offset + num_nodes; /* Find the tile that the UV lies in. */ int tx = (int)tex_co.x; @@ -83,7 +85,7 @@ ccl_device void svm_node_tex_image( /* Find the index of the tile. */ for (int i = 0; i < num_nodes; i++) { - uint4 tile_node = read_node(kg, offset); + uint4 tile_node = read_node(kg, &offset); if (tile_node.x == tile) { id = tile_node.y; break; @@ -102,7 +104,7 @@ ccl_device void svm_node_tex_image( } /* Skip over the remaining nodes. */ - *offset = next_offset; + offset = next_offset; } else { id = -num_nodes; @@ -114,9 +116,13 @@ ccl_device void svm_node_tex_image( stack_store_float3(stack, out_offset, make_float3(f.x, f.y, f.z)); if (stack_valid(alpha_offset)) stack_store_float(stack, alpha_offset, f.w); + return offset; } -ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tex_image_box(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { /* get object space normal */ float3 N = sd->N; @@ -138,10 +144,10 @@ ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float * in between we blend between two textures, and in the middle we a blend * between three textures. * - * the Nxyz values are the barycentric coordinates in an equilateral + * The `Nxyz` values are the barycentric coordinates in an equilateral * triangle, which in case of blending, in the middle has a smaller * equilateral triangle where 3 textures blend. this divides things into - * 7 zones, with an if() test for each zone */ + * 7 zones, with an if() test for each zone. */ float3 weight = make_float3(0.0f, 0.0f, 0.0f); float blend = __int_as_float(node.w); @@ -161,17 +167,17 @@ ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float /* in case of blending, test for mixes between two textures */ if (N.z < (1.0f - limit) * (N.y + N.x)) { weight.x = N.x / (N.x + N.y); - weight.x = saturate((weight.x - 0.5f * (1.0f - blend)) / blend); + weight.x = saturatef((weight.x - 0.5f * (1.0f - blend)) / blend); weight.y = 1.0f - weight.x; } else if (N.x < (1.0f - limit) * (N.y + N.z)) { weight.y = N.y / (N.y + N.z); - weight.y = saturate((weight.y - 0.5f * (1.0f - blend)) / blend); + weight.y = saturatef((weight.y - 0.5f * (1.0f - blend)) / blend); weight.z = 1.0f - weight.y; } else if (N.y < (1.0f - limit) * (N.x + N.z)) { weight.x = N.x / (N.x + N.z); - weight.x = saturate((weight.x - 0.5f * (1.0f - blend)) / blend); + weight.x = saturatef((weight.x - 0.5f * (1.0f - blend)) / blend); weight.z = 1.0f - weight.x; } else { @@ -215,10 +221,10 @@ ccl_device void svm_node_tex_image_box(KernelGlobals *kg, ShaderData *sd, float stack_store_float(stack, alpha_offset, f.w); } -ccl_device void svm_node_tex_environment(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node) +ccl_device_noinline void svm_node_tex_environment(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint id = node.y; uint co_offset, out_offset, alpha_offset, flags; diff --git a/intern/cycles/kernel/svm/svm_invert.h b/intern/cycles/kernel/svm/invert.h similarity index 76% rename from intern/cycles/kernel/svm/svm_invert.h rename to intern/cycles/kernel/svm/invert.h index 02024742b13..5a88e9df2c9 100644 --- a/intern/cycles/kernel/svm/svm_invert.h +++ b/intern/cycles/kernel/svm/invert.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device float invert(float color, float factor) @@ -21,8 +23,11 @@ ccl_device float invert(float color, float factor) return factor * (1.0f - color) + (1.0f - factor) * color; } -ccl_device void svm_node_invert( - ShaderData *sd, float *stack, uint in_fac, uint in_color, uint out_color) +ccl_device_noinline void svm_node_invert(ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_fac, + uint in_color, + uint out_color) { float factor = stack_load_float(stack, in_fac); float3 color = stack_load_float3(stack, in_color); diff --git a/intern/cycles/kernel/svm/svm_light_path.h b/intern/cycles/kernel/svm/light_path.h similarity index 58% rename from intern/cycles/kernel/svm/svm_light_path.h rename to intern/cycles/kernel/svm/light_path.h index 768c65918cd..44a35b568fa 100644 --- a/intern/cycles/kernel/svm/svm_light_path.h +++ b/intern/cycles/kernel/svm/light_path.h @@ -14,16 +14,20 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Light Path Node */ -ccl_device void svm_node_light_path(ShaderData *sd, - ccl_addr_space PathState *state, - float *stack, - uint type, - uint out_offset, - int path_flag) +template +ccl_device_noinline void svm_node_light_path(KernelGlobals kg, + ConstIntegratorGenericState state, + ccl_private const ShaderData *sd, + ccl_private float *stack, + uint type, + uint out_offset, + uint32_t path_flag) { float info = 0.0f; @@ -58,20 +62,46 @@ ccl_device void svm_node_light_path(ShaderData *sd, case NODE_LP_ray_length: info = sd->ray_length; break; - case NODE_LP_ray_depth: - info = (float)state->bounce; + case NODE_LP_ray_depth: { + /* Read bounce from difference location depending if this is a shadow + * path. It's a bit dubious to have integrate state details leak into + * this function but hard to avoid currently. */ + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_bounce(state, path_flag); + } + + /* For background, light emission and shadow evaluation we from a + * surface or volume we are effective one bounce further. */ + if (path_flag & (PATH_RAY_SHADOW | PATH_RAY_EMISSION)) { + info += 1.0f; + } break; + } + case NODE_LP_ray_transparent: { + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_transparent_bounce(state, path_flag); + } + break; + } case NODE_LP_ray_diffuse: - info = (float)state->diffuse_bounce; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_diffuse_bounce(state, path_flag); + } break; case NODE_LP_ray_glossy: - info = (float)state->glossy_bounce; - break; - case NODE_LP_ray_transparent: - info = (float)state->transparent_bounce; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_glossy_bounce(state, path_flag); + } break; case NODE_LP_ray_transmission: - info = (float)state->transmission_bounce; + IF_KERNEL_NODES_FEATURE(LIGHT_PATH) + { + info = (float)integrator_state_transmission_bounce(state, path_flag); + } break; } @@ -80,7 +110,9 @@ ccl_device void svm_node_light_path(ShaderData *sd, /* Light Falloff Node */ -ccl_device void svm_node_light_falloff(ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_light_falloff(ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint strength_offset, out_offset, smooth_offset; diff --git a/intern/cycles/kernel/svm/svm_magic.h b/intern/cycles/kernel/svm/magic.h similarity index 92% rename from intern/cycles/kernel/svm/svm_magic.h rename to intern/cycles/kernel/svm/magic.h index 9c160e6d8cc..f103a8eebcc 100644 --- a/intern/cycles/kernel/svm/svm_magic.h +++ b/intern/cycles/kernel/svm/magic.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Magic */ @@ -87,8 +89,8 @@ ccl_device_noinline_cpu float3 svm_magic(float3 p, int n, float distortion) return make_float3(0.5f - x, 0.5f - y, 0.5f - z); } -ccl_device void svm_node_tex_magic( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_magic( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint depth; uint scale_offset, distortion_offset, co_offset, fac_offset, color_offset; @@ -96,7 +98,7 @@ ccl_device void svm_node_tex_magic( svm_unpack_node_uchar3(node.y, &depth, &color_offset, &fac_offset); svm_unpack_node_uchar3(node.z, &co_offset, &scale_offset, &distortion_offset); - uint4 node2 = read_node(kg, offset); + uint4 node2 = read_node(kg, &offset); float3 co = stack_load_float3(stack, co_offset); float scale = stack_load_float_default(stack, scale_offset, node2.x); float distortion = stack_load_float_default(stack, distortion_offset, node2.y); @@ -107,6 +109,7 @@ ccl_device void svm_node_tex_magic( stack_store_float(stack, fac_offset, average(color)); if (stack_valid(color_offset)) stack_store_float3(stack, color_offset, color); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_map_range.h b/intern/cycles/kernel/svm/map_range.h similarity index 83% rename from intern/cycles/kernel/svm/svm_map_range.h rename to intern/cycles/kernel/svm/map_range.h index 533a631c837..fdbfc6531c4 100644 --- a/intern/cycles/kernel/svm/svm_map_range.h +++ b/intern/cycles/kernel/svm/map_range.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Map Range Node */ @@ -24,13 +26,13 @@ ccl_device_inline float smootherstep(float edge0, float edge1, float x) return x * x * x * (x * (x * 6.0f - 15.0f) + 10.0f); } -ccl_device void svm_node_map_range(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint value_stack_offset, - uint parameters_stack_offsets, - uint results_stack_offsets, - int *offset) +ccl_device_noinline int svm_node_map_range(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint value_stack_offset, + uint parameters_stack_offsets, + uint results_stack_offsets, + int offset) { uint from_min_stack_offset, from_max_stack_offset, to_min_stack_offset, to_max_stack_offset; uint type_stack_offset, steps_stack_offset, result_stack_offset; @@ -42,8 +44,8 @@ ccl_device void svm_node_map_range(KernelGlobals *kg, svm_unpack_node_uchar3( results_stack_offsets, &type_stack_offset, &steps_stack_offset, &result_stack_offset); - uint4 defaults = read_node(kg, offset); - uint4 defaults2 = read_node(kg, offset); + uint4 defaults = read_node(kg, &offset); + uint4 defaults2 = read_node(kg, &offset); float value = stack_load_float(stack, value_stack_offset); float from_min = stack_load_float_default(stack, from_min_stack_offset, defaults.x); @@ -83,6 +85,7 @@ ccl_device void svm_node_map_range(KernelGlobals *kg, result = 0.0f; } stack_store_float(stack, result_stack_offset, result); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_mapping.h b/intern/cycles/kernel/svm/mapping.h similarity index 53% rename from intern/cycles/kernel/svm/svm_mapping.h rename to intern/cycles/kernel/svm/mapping.h index 6e19c859e19..19f79471ad2 100644 --- a/intern/cycles/kernel/svm/svm_mapping.h +++ b/intern/cycles/kernel/svm/mapping.h @@ -14,17 +14,20 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/mapping_util.h" + CCL_NAMESPACE_BEGIN /* Mapping Node */ -ccl_device void svm_node_mapping(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint type, - uint inputs_stack_offsets, - uint result_stack_offset, - int *offset) +ccl_device_noinline void svm_node_mapping(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint inputs_stack_offsets, + uint result_stack_offset) { uint vector_stack_offset, location_stack_offset, rotation_stack_offset, scale_stack_offset; svm_unpack_node_uchar4(inputs_stack_offsets, @@ -44,30 +47,40 @@ ccl_device void svm_node_mapping(KernelGlobals *kg, /* Texture Mapping */ -ccl_device void svm_node_texture_mapping( - KernelGlobals *kg, ShaderData *sd, float *stack, uint vec_offset, uint out_offset, int *offset) +ccl_device_noinline int svm_node_texture_mapping(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint vec_offset, + uint out_offset, + int offset) { float3 v = stack_load_float3(stack, vec_offset); Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); float3 r = transform_point(&tfm, v); stack_store_float3(stack, out_offset, r); + return offset; } -ccl_device void svm_node_min_max( - KernelGlobals *kg, ShaderData *sd, float *stack, uint vec_offset, uint out_offset, int *offset) +ccl_device_noinline int svm_node_min_max(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint vec_offset, + uint out_offset, + int offset) { float3 v = stack_load_float3(stack, vec_offset); - float3 mn = float4_to_float3(read_node_float(kg, offset)); - float3 mx = float4_to_float3(read_node_float(kg, offset)); + float3 mn = float4_to_float3(read_node_float(kg, &offset)); + float3 mx = float4_to_float3(read_node_float(kg, &offset)); float3 r = min(max(mn, v), mx); stack_store_float3(stack, out_offset, r); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_mapping_util.h b/intern/cycles/kernel/svm/mapping_util.h similarity index 99% rename from intern/cycles/kernel/svm/svm_mapping_util.h rename to intern/cycles/kernel/svm/mapping_util.h index ec2c84e0791..51b13c0c264 100644 --- a/intern/cycles/kernel/svm/svm_mapping_util.h +++ b/intern/cycles/kernel/svm/mapping_util.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN ccl_device float3 diff --git a/intern/cycles/kernel/svm/svm_math.h b/intern/cycles/kernel/svm/math.h similarity index 69% rename from intern/cycles/kernel/svm/svm_math.h rename to intern/cycles/kernel/svm/math.h index 733ea28f9e5..ff0f3683ea3 100644 --- a/intern/cycles/kernel/svm/svm_math.h +++ b/intern/cycles/kernel/svm/math.h @@ -14,15 +14,16 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_math(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint type, - uint inputs_stack_offsets, - uint result_stack_offset, - int *offset) +ccl_device_noinline void svm_node_math(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint inputs_stack_offsets, + uint result_stack_offset) { uint a_stack_offset, b_stack_offset, c_stack_offset; svm_unpack_node_uchar3(inputs_stack_offsets, &a_stack_offset, &b_stack_offset, &c_stack_offset); @@ -35,13 +36,13 @@ ccl_device void svm_node_math(KernelGlobals *kg, stack_store_float(stack, result_stack_offset, result); } -ccl_device void svm_node_vector_math(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint type, - uint inputs_stack_offsets, - uint outputs_stack_offsets, - int *offset) +ccl_device_noinline int svm_node_vector_math(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint type, + uint inputs_stack_offsets, + uint outputs_stack_offsets, + int offset) { uint value_stack_offset, vector_stack_offset; uint a_stack_offset, b_stack_offset, param1_stack_offset; @@ -60,7 +61,7 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg, /* 3 Vector Operators */ if (type == NODE_VECTOR_MATH_WRAP || type == NODE_VECTOR_MATH_FACEFORWARD || type == NODE_VECTOR_MATH_MULTIPLY_ADD) { - uint4 extra_node = read_node(kg, offset); + uint4 extra_node = read_node(kg, &offset); c = stack_load_float3(stack, extra_node.x); } @@ -70,6 +71,7 @@ ccl_device void svm_node_vector_math(KernelGlobals *kg, stack_store_float(stack, value_stack_offset, value); if (stack_valid(vector_stack_offset)) stack_store_float3(stack, vector_stack_offset, vector); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_math_util.h b/intern/cycles/kernel/svm/math_util.h similarity index 81% rename from intern/cycles/kernel/svm/svm_math_util.h rename to intern/cycles/kernel/svm/math_util.h index 9e654f2247f..b2e539cdd1f 100644 --- a/intern/cycles/kernel/svm/svm_math_util.h +++ b/intern/cycles/kernel/svm/math_util.h @@ -14,10 +14,12 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_vector_math(float *value, - float3 *vector, +ccl_device void svm_vector_math(ccl_private float *value, + ccl_private float3 *vector, NodeVectorMathType type, float3 a, float3 b, @@ -200,43 +202,43 @@ ccl_device float svm_math(NodeMathType type, float a, float b, float c) } } -/* Calculate color in range 800..12000 using an approximation - * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B - * Max absolute error for RGB is (0.00095, 0.00077, 0.00057), - * which is enough to get the same 8 bit/channel color. - */ - -ccl_static_constant float blackbody_table_r[6][3] = { - {2.52432244e+03f, -1.06185848e-03f, 3.11067539e+00f}, - {3.37763626e+03f, -4.34581697e-04f, 1.64843306e+00f}, - {4.10671449e+03f, -8.61949938e-05f, 6.41423749e-01f}, - {4.66849800e+03f, 2.85655028e-05f, 1.29075375e-01f}, - {4.60124770e+03f, 2.89727618e-05f, 1.48001316e-01f}, - {3.78765709e+03f, 9.36026367e-06f, 3.98995841e-01f}, -}; - -ccl_static_constant float blackbody_table_g[6][3] = { - {-7.50343014e+02f, 3.15679613e-04f, 4.73464526e-01f}, - {-1.00402363e+03f, 1.29189794e-04f, 9.08181524e-01f}, - {-1.22075471e+03f, 2.56245413e-05f, 1.20753416e+00f}, - {-1.42546105e+03f, -4.01730887e-05f, 1.44002695e+00f}, - {-1.18134453e+03f, -2.18913373e-05f, 1.30656109e+00f}, - {-5.00279505e+02f, -4.59745390e-06f, 1.09090465e+00f}, -}; - -ccl_static_constant float blackbody_table_b[6][4] = { - {0.0f, 0.0f, 0.0f, 0.0f}, /* zeros should be optimized by compiler */ - {0.0f, 0.0f, 0.0f, 0.0f}, - {0.0f, 0.0f, 0.0f, 0.0f}, - {-2.02524603e-11f, 1.79435860e-07f, -2.60561875e-04f, -1.41761141e-02f}, - {-2.22463426e-13f, -1.55078698e-08f, 3.81675160e-04f, -7.30646033e-01f}, - {6.72595954e-13f, -2.73059993e-08f, 4.24068546e-04f, -7.52204323e-01f}, -}; - ccl_device float3 svm_math_blackbody_color(float t) { /* TODO(lukas): Reimplement in XYZ. */ + /* Calculate color in range 800..12000 using an approximation + * a/x+bx+c for R and G and ((at + b)t + c)t + d) for B + * Max absolute error for RGB is (0.00095, 0.00077, 0.00057), + * which is enough to get the same 8 bit/channel color. + */ + + const float blackbody_table_r[6][3] = { + {2.52432244e+03f, -1.06185848e-03f, 3.11067539e+00f}, + {3.37763626e+03f, -4.34581697e-04f, 1.64843306e+00f}, + {4.10671449e+03f, -8.61949938e-05f, 6.41423749e-01f}, + {4.66849800e+03f, 2.85655028e-05f, 1.29075375e-01f}, + {4.60124770e+03f, 2.89727618e-05f, 1.48001316e-01f}, + {3.78765709e+03f, 9.36026367e-06f, 3.98995841e-01f}, + }; + + const float blackbody_table_g[6][3] = { + {-7.50343014e+02f, 3.15679613e-04f, 4.73464526e-01f}, + {-1.00402363e+03f, 1.29189794e-04f, 9.08181524e-01f}, + {-1.22075471e+03f, 2.56245413e-05f, 1.20753416e+00f}, + {-1.42546105e+03f, -4.01730887e-05f, 1.44002695e+00f}, + {-1.18134453e+03f, -2.18913373e-05f, 1.30656109e+00f}, + {-5.00279505e+02f, -4.59745390e-06f, 1.09090465e+00f}, + }; + + const float blackbody_table_b[6][4] = { + {0.0f, 0.0f, 0.0f, 0.0f}, /* zeros should be optimized by compiler */ + {0.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 0.0f}, + {-2.02524603e-11f, 1.79435860e-07f, -2.60561875e-04f, -1.41761141e-02f}, + {-2.22463426e-13f, -1.55078698e-08f, 3.81675160e-04f, -7.30646033e-01f}, + {6.72595954e-13f, -2.73059993e-08f, 4.24068546e-04f, -7.52204323e-01f}, + }; + if (t >= 12000.0f) { return make_float3(0.826270103f, 0.994478524f, 1.56626022f); } diff --git a/intern/cycles/kernel/svm/svm_mix.h b/intern/cycles/kernel/svm/mix.h similarity index 67% rename from intern/cycles/kernel/svm/svm_mix.h rename to intern/cycles/kernel/svm/mix.h index 15114bfd5e4..96e5b3f5b5e 100644 --- a/intern/cycles/kernel/svm/svm_mix.h +++ b/intern/cycles/kernel/svm/mix.h @@ -14,20 +14,22 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Node */ -ccl_device void svm_node_mix(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint fac_offset, - uint c1_offset, - uint c2_offset, - int *offset) +ccl_device_noinline int svm_node_mix(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint fac_offset, + uint c1_offset, + uint c2_offset, + int offset) { /* read extra data */ - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); float fac = stack_load_float(stack, fac_offset); float3 c1 = stack_load_float3(stack, c1_offset); @@ -35,6 +37,7 @@ ccl_device void svm_node_mix(KernelGlobals *kg, float3 result = svm_mix((NodeMix)node1.y, fac, c1, c2); stack_store_float3(stack, node1.z, result); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_musgrave.h b/intern/cycles/kernel/svm/musgrave.h similarity index 96% rename from intern/cycles/kernel/svm/svm_musgrave.h rename to intern/cycles/kernel/svm/musgrave.h index 571f62fe27f..a37ca9eb8eb 100644 --- a/intern/cycles/kernel/svm/svm_musgrave.h +++ b/intern/cycles/kernel/svm/musgrave.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/noise.h" + CCL_NAMESPACE_BEGIN /* 1D Musgrave fBm @@ -176,7 +180,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_1d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_1d(p)); signal *= signal; signal *= weight; @@ -347,7 +351,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_2d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_2d(p)); signal *= signal; signal *= weight; @@ -518,7 +522,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_3d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_3d(p)); signal *= signal; signal *= weight; @@ -689,7 +693,7 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_4d( for (int i = 1; i < float_to_int(octaves); i++) { p *= lacunarity; - weight = saturate(signal * gain); + weight = saturatef(signal * gain); signal = offset - fabsf(snoise_4d(p)); signal *= signal; signal *= weight; @@ -700,13 +704,13 @@ ccl_device_noinline_cpu float noise_musgrave_ridged_multi_fractal_4d( return value; } -ccl_device void svm_node_tex_musgrave(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint offsets1, - uint offsets2, - uint offsets3, - int *offset) +ccl_device_noinline int svm_node_tex_musgrave(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint offsets1, + uint offsets2, + uint offsets3, + int offset) { uint type, dimensions, co_stack_offset, w_stack_offset; uint scale_stack_offset, detail_stack_offset, dimension_stack_offset, lacunarity_stack_offset; @@ -720,8 +724,8 @@ ccl_device void svm_node_tex_musgrave(KernelGlobals *kg, &lacunarity_stack_offset); svm_unpack_node_uchar3(offsets3, &offset_stack_offset, &gain_stack_offset, &fac_stack_offset); - uint4 defaults1 = read_node(kg, offset); - uint4 defaults2 = read_node(kg, offset); + uint4 defaults1 = read_node(kg, &offset); + uint4 defaults2 = read_node(kg, &offset); float3 co = stack_load_float3(stack, co_stack_offset); float w = stack_load_float_default(stack, w_stack_offset, defaults1.x); @@ -733,7 +737,7 @@ ccl_device void svm_node_tex_musgrave(KernelGlobals *kg, float gain = stack_load_float_default(stack, gain_stack_offset, defaults2.z); dimension = fmaxf(dimension, 1e-5f); - detail = clamp(detail, 0.0f, 16.0f); + detail = clamp(detail, 0.0f, 15.0f); lacunarity = fmaxf(lacunarity, 1e-5f); float fac; @@ -844,6 +848,7 @@ ccl_device void svm_node_tex_musgrave(KernelGlobals *kg, } stack_store_float(stack, fac_stack_offset, fac); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_noise.h b/intern/cycles/kernel/svm/noise.h similarity index 98% rename from intern/cycles/kernel/svm/svm_noise.h rename to intern/cycles/kernel/svm/noise.h index 94d8bfde555..0a1616226db 100644 --- a/intern/cycles/kernel/svm/svm_noise.h +++ b/intern/cycles/kernel/svm/noise.h @@ -30,6 +30,8 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + CCL_NAMESPACE_BEGIN /* **** Perlin Noise **** */ @@ -330,7 +332,7 @@ ccl_device_inline ssef grad(const ssei &hash, const ssef &x, const ssef &y) * |__________________________| * */ -ccl_device_noinline float perlin_2d(float x, float y) +ccl_device_noinline_cpu float perlin_2d(float x, float y) { ssei XY; ssef fxy = floorfrac(ssef(x, y, 0.0f, 0.0f), &XY); @@ -447,7 +449,7 @@ ccl_device_inline ssef quad_mix(ssef p, ssef q, ssef r, ssef s, ssef f) * v7 (1, 1, 1) * */ -ccl_device_noinline float perlin_3d(float x, float y, float z) +ccl_device_noinline_cpu float perlin_3d(float x, float y, float z) { ssei XYZ; ssef fxyz = floorfrac(ssef(x, y, z, 0.0f), &XYZ); @@ -501,7 +503,7 @@ ccl_device_noinline float perlin_3d(float x, float y, float z) * v15 (1, 1, 1, 1) * */ -ccl_device_noinline float perlin_4d(float x, float y, float z, float w) +ccl_device_noinline_cpu float perlin_4d(float x, float y, float z, float w) { ssei XYZW; ssef fxyzw = floorfrac(ssef(x, y, z, w), &XYZW); @@ -585,7 +587,7 @@ ccl_device_inline ssef quad_mix(avxf p, avxf q, ssef f) * |__________________________| * */ -ccl_device_noinline float perlin_3d(float x, float y, float z) +ccl_device_noinline_cpu float perlin_3d(float x, float y, float z) { ssei XYZ; ssef fxyz = floorfrac(ssef(x, y, z, 0.0f), &XYZ); @@ -637,7 +639,7 @@ ccl_device_noinline float perlin_3d(float x, float y, float z) * v15 (1, 1, 1, 1) * */ -ccl_device_noinline float perlin_4d(float x, float y, float z, float w) +ccl_device_noinline_cpu float perlin_4d(float x, float y, float z, float w) { ssei XYZW; ssef fxyzw = floorfrac(ssef(x, y, z, w), &XYZW); diff --git a/intern/cycles/kernel/svm/svm_noisetex.h b/intern/cycles/kernel/svm/noisetex.h similarity index 87% rename from intern/cycles/kernel/svm/svm_noisetex.h rename to intern/cycles/kernel/svm/noisetex.h index 61fd9553802..c43c3b9f9d2 100644 --- a/intern/cycles/kernel/svm/svm_noisetex.h +++ b/intern/cycles/kernel/svm/noisetex.h @@ -14,6 +14,10 @@ * limitations under the License. */ +#pragma once + +#include "kernel/svm/fractal_noise.h" + CCL_NAMESPACE_BEGIN /* The following offset functions generate random offsets to be added to texture @@ -55,8 +59,8 @@ ccl_device void noise_texture_1d(float co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float p = co; if (distortion != 0.0f) { @@ -76,8 +80,8 @@ ccl_device void noise_texture_2d(float2 co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float2 p = co; if (distortion != 0.0f) { @@ -98,8 +102,8 @@ ccl_device void noise_texture_3d(float3 co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float3 p = co; if (distortion != 0.0f) { @@ -121,8 +125,8 @@ ccl_device void noise_texture_4d(float4 co, float roughness, float distortion, bool color_is_needed, - float *value, - float3 *color) + ccl_private float *value, + ccl_private float3 *color) { float4 p = co; if (distortion != 0.0f) { @@ -140,13 +144,13 @@ ccl_device void noise_texture_4d(float4 co, } } -ccl_device void svm_node_tex_noise(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint dimensions, - uint offsets1, - uint offsets2, - int *offset) +ccl_device_noinline int svm_node_tex_noise(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint dimensions, + uint offsets1, + uint offsets2, + int offset) { uint vector_stack_offset, w_stack_offset, scale_stack_offset; uint detail_stack_offset, roughness_stack_offset, distortion_stack_offset; @@ -160,8 +164,8 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg, &value_stack_offset, &color_stack_offset); - uint4 defaults1 = read_node(kg, offset); - uint4 defaults2 = read_node(kg, offset); + uint4 defaults1 = read_node(kg, &offset); + uint4 defaults2 = read_node(kg, &offset); float3 vector = stack_load_float3(stack, vector_stack_offset); float w = stack_load_float_default(stack, w_stack_offset, defaults1.x); @@ -212,6 +216,7 @@ ccl_device void svm_node_tex_noise(KernelGlobals *kg, if (stack_valid(color_stack_offset)) { stack_store_float3(stack, color_stack_offset, color); } + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_normal.h b/intern/cycles/kernel/svm/normal.h similarity index 69% rename from intern/cycles/kernel/svm/svm_normal.h rename to intern/cycles/kernel/svm/normal.h index 4cd3eab0ed2..6a2d88b68a6 100644 --- a/intern/cycles/kernel/svm/svm_normal.h +++ b/intern/cycles/kernel/svm/normal.h @@ -14,18 +14,20 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_normal(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint in_normal_offset, - uint out_normal_offset, - uint out_dot_offset, - int *offset) +ccl_device_noinline int svm_node_normal(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_normal_offset, + uint out_normal_offset, + uint out_dot_offset, + int offset) { /* read extra data */ - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); float3 normal = stack_load_float3(stack, in_normal_offset); float3 direction; @@ -39,6 +41,7 @@ ccl_device void svm_node_normal(KernelGlobals *kg, if (stack_valid(out_dot_offset)) stack_store_float(stack, out_dot_offset, dot(direction, normalize(normal))); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/ramp.h b/intern/cycles/kernel/svm/ramp.h new file mode 100644 index 00000000000..61093e0bd82 --- /dev/null +++ b/intern/cycles/kernel/svm/ramp.h @@ -0,0 +1,165 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */ + +ccl_device_inline float fetch_float(KernelGlobals kg, int offset) +{ + uint4 node = kernel_tex_fetch(__svm_nodes, offset); + return __uint_as_float(node.x); +} + +ccl_device_inline float float_ramp_lookup( + KernelGlobals kg, int offset, float f, bool interpolate, bool extrapolate, int table_size) +{ + if ((f < 0.0f || f > 1.0f) && extrapolate) { + float t0, dy; + if (f < 0.0f) { + t0 = fetch_float(kg, offset); + dy = t0 - fetch_float(kg, offset + 1); + f = -f; + } + else { + t0 = fetch_float(kg, offset + table_size - 1); + dy = t0 - fetch_float(kg, offset + table_size - 2); + f = f - 1.0f; + } + return t0 + dy * f * (table_size - 1); + } + + f = saturatef(f) * (table_size - 1); + + /* clamp int as well in case of NaN */ + int i = clamp(float_to_int(f), 0, table_size - 1); + float t = f - (float)i; + + float a = fetch_float(kg, offset + i); + + if (interpolate && t > 0.0f) + a = (1.0f - t) * a + t * fetch_float(kg, offset + i + 1); + + return a; +} + +ccl_device_inline float4 rgb_ramp_lookup( + KernelGlobals kg, int offset, float f, bool interpolate, bool extrapolate, int table_size) +{ + if ((f < 0.0f || f > 1.0f) && extrapolate) { + float4 t0, dy; + if (f < 0.0f) { + t0 = fetch_node_float(kg, offset); + dy = t0 - fetch_node_float(kg, offset + 1); + f = -f; + } + else { + t0 = fetch_node_float(kg, offset + table_size - 1); + dy = t0 - fetch_node_float(kg, offset + table_size - 2); + f = f - 1.0f; + } + return t0 + dy * f * (table_size - 1); + } + + f = saturatef(f) * (table_size - 1); + + /* clamp int as well in case of NaN */ + int i = clamp(float_to_int(f), 0, table_size - 1); + float t = f - (float)i; + + float4 a = fetch_node_float(kg, offset + i); + + if (interpolate && t > 0.0f) + a = (1.0f - t) * a + t * fetch_node_float(kg, offset + i + 1); + + return a; +} + +ccl_device_noinline int svm_node_rgb_ramp( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) +{ + uint fac_offset, color_offset, alpha_offset; + uint interpolate = node.z; + + svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &alpha_offset); + + uint table_size = read_node(kg, &offset).x; + + float fac = stack_load_float(stack, fac_offset); + float4 color = rgb_ramp_lookup(kg, offset, fac, interpolate, false, table_size); + + if (stack_valid(color_offset)) + stack_store_float3(stack, color_offset, float4_to_float3(color)); + if (stack_valid(alpha_offset)) + stack_store_float(stack, alpha_offset, color.w); + + offset += table_size; + return offset; +} + +ccl_device_noinline int svm_node_curves( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) +{ + uint fac_offset, color_offset, out_offset; + svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &out_offset); + + uint table_size = read_node(kg, &offset).x; + + float fac = stack_load_float(stack, fac_offset); + float3 color = stack_load_float3(stack, color_offset); + + const float min_x = __int_as_float(node.z), max_x = __int_as_float(node.w); + const float range_x = max_x - min_x; + const float3 relpos = (color - make_float3(min_x, min_x, min_x)) / range_x; + + float r = rgb_ramp_lookup(kg, offset, relpos.x, true, true, table_size).x; + float g = rgb_ramp_lookup(kg, offset, relpos.y, true, true, table_size).y; + float b = rgb_ramp_lookup(kg, offset, relpos.z, true, true, table_size).z; + + color = (1.0f - fac) * color + fac * make_float3(r, g, b); + stack_store_float3(stack, out_offset, color); + + offset += table_size; + return offset; +} + +ccl_device_noinline int svm_node_curve( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) +{ + uint fac_offset, value_in_offset, out_offset; + svm_unpack_node_uchar3(node.y, &fac_offset, &value_in_offset, &out_offset); + + uint table_size = read_node(kg, &offset).x; + + float fac = stack_load_float(stack, fac_offset); + float in = stack_load_float(stack, value_in_offset); + + const float min = __int_as_float(node.z), max = __int_as_float(node.w); + const float range = max - min; + const float relpos = (in - min) / range; + + float v = float_ramp_lookup(kg, offset, relpos, true, true, table_size); + + in = (1.0f - fac) * in + fac * v; + stack_store_float(stack, out_offset, in); + + offset += table_size; + return offset; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_ramp_util.h b/intern/cycles/kernel/svm/ramp_util.h similarity index 96% rename from intern/cycles/kernel/svm/svm_ramp_util.h rename to intern/cycles/kernel/svm/ramp_util.h index 202596c1fe3..f5951f7e283 100644 --- a/intern/cycles/kernel/svm/svm_ramp_util.h +++ b/intern/cycles/kernel/svm/ramp_util.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_RAMP_UTIL_H__ -#define __SVM_RAMP_UTIL_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -86,5 +85,3 @@ ccl_device float float_ramp_lookup( } CCL_NAMESPACE_END - -#endif /* __SVM_RAMP_UTIL_H__ */ diff --git a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h b/intern/cycles/kernel/svm/sepcomb_hsv.h similarity index 57% rename from intern/cycles/kernel/svm/svm_sepcomb_hsv.h rename to intern/cycles/kernel/svm/sepcomb_hsv.h index f501252062e..941a83e85b3 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_hsv.h +++ b/intern/cycles/kernel/svm/sepcomb_hsv.h @@ -14,17 +14,19 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_combine_hsv(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint hue_in, - uint saturation_in, - uint value_in, - int *offset) +ccl_device_noinline int svm_node_combine_hsv(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint hue_in, + uint saturation_in, + uint value_in, + int offset) { - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); uint color_out = node1.y; float hue = stack_load_float(stack, hue_in); @@ -36,17 +38,18 @@ ccl_device void svm_node_combine_hsv(KernelGlobals *kg, if (stack_valid(color_out)) stack_store_float3(stack, color_out, color); + return offset; } -ccl_device void svm_node_separate_hsv(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint color_in, - uint hue_out, - uint saturation_out, - int *offset) +ccl_device_noinline int svm_node_separate_hsv(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint color_in, + uint hue_out, + uint saturation_out, + int offset) { - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); uint value_out = node1.y; float3 color = stack_load_float3(stack, color_in); @@ -60,6 +63,7 @@ ccl_device void svm_node_separate_hsv(KernelGlobals *kg, stack_store_float(stack, saturation_out, color.y); if (stack_valid(value_out)) stack_store_float(stack, value_out, color.z); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_sepcomb_vector.h b/intern/cycles/kernel/svm/sepcomb_vector.h similarity index 65% rename from intern/cycles/kernel/svm/svm_sepcomb_vector.h rename to intern/cycles/kernel/svm/sepcomb_vector.h index cbf77f1e640..acdea741aed 100644 --- a/intern/cycles/kernel/svm/svm_sepcomb_vector.h +++ b/intern/cycles/kernel/svm/sepcomb_vector.h @@ -14,12 +14,17 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Vector combine / separate, used for the RGB and XYZ nodes */ -ccl_device void svm_node_combine_vector( - ShaderData *sd, float *stack, uint in_offset, uint vector_index, uint out_offset) +ccl_device void svm_node_combine_vector(ccl_private ShaderData *sd, + ccl_private float *stack, + uint in_offset, + uint vector_index, + uint out_offset) { float vector = stack_load_float(stack, in_offset); @@ -27,8 +32,11 @@ ccl_device void svm_node_combine_vector( stack_store_float(stack, out_offset + vector_index, vector); } -ccl_device void svm_node_separate_vector( - ShaderData *sd, float *stack, uint ivector_offset, uint vector_index, uint out_offset) +ccl_device void svm_node_separate_vector(ccl_private ShaderData *sd, + ccl_private float *stack, + uint ivector_offset, + uint vector_index, + uint out_offset) { float3 vector = stack_load_float3(stack, ivector_offset); diff --git a/intern/cycles/kernel/svm/svm_sky.h b/intern/cycles/kernel/svm/sky.h similarity index 86% rename from intern/cycles/kernel/svm/svm_sky.h rename to intern/cycles/kernel/svm/sky.h index b908732f026..867fdfc2a3f 100644 --- a/intern/cycles/kernel/svm/svm_sky.h +++ b/intern/cycles/kernel/svm/sky.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Sky texture */ @@ -28,7 +30,7 @@ ccl_device float sky_angle_between(float thetav, float phiv, float theta, float * "A Practical Analytic Model for Daylight" * A. J. Preetham, Peter Shirley, Brian Smits */ -ccl_device float sky_perez_function(float *lam, float theta, float gamma) +ccl_device float sky_perez_function(ccl_private float *lam, float theta, float gamma) { float ctheta = cosf(theta); float cgamma = cosf(gamma); @@ -37,16 +39,16 @@ ccl_device float sky_perez_function(float *lam, float theta, float gamma) (1.0f + lam[2] * expf(lam[3] * gamma) + lam[4] * cgamma * cgamma); } -ccl_device float3 sky_radiance_preetham(KernelGlobals *kg, +ccl_device float3 sky_radiance_preetham(KernelGlobals kg, float3 dir, float sunphi, float suntheta, float radiance_x, float radiance_y, float radiance_z, - float *config_x, - float *config_y, - float *config_z) + ccl_private float *config_x, + ccl_private float *config_y, + ccl_private float *config_z) { /* convert vector to spherical coordinates */ float2 spherical = direction_to_spherical(dir); @@ -73,7 +75,7 @@ ccl_device float3 sky_radiance_preetham(KernelGlobals *kg, * "An Analytic Model for Full Spectral Sky-Dome Radiance" * Lukas Hosek, Alexander Wilkie */ -ccl_device float sky_radiance_internal(float *configuration, float theta, float gamma) +ccl_device float sky_radiance_internal(ccl_private float *configuration, float theta, float gamma) { float ctheta = cosf(theta); float cgamma = cosf(gamma); @@ -90,16 +92,16 @@ ccl_device float sky_radiance_internal(float *configuration, float theta, float configuration[6] * mieM + configuration[7] * zenith); } -ccl_device float3 sky_radiance_hosek(KernelGlobals *kg, +ccl_device float3 sky_radiance_hosek(KernelGlobals kg, float3 dir, float sunphi, float suntheta, float radiance_x, float radiance_y, float radiance_z, - float *config_x, - float *config_y, - float *config_z) + ccl_private float *config_x, + ccl_private float *config_y, + ccl_private float *config_z) { /* convert vector to spherical coordinates */ float2 spherical = direction_to_spherical(dir); @@ -127,9 +129,9 @@ ccl_device float3 geographical_to_direction(float lat, float lon) return make_float3(cos(lat) * cos(lon), cos(lat) * sin(lon), sin(lat)); } -ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, +ccl_device float3 sky_radiance_nishita(KernelGlobals kg, float3 dir, - float *nishita_data, + ccl_private float *nishita_data, uint texture_id) { /* definitions */ @@ -209,8 +211,8 @@ ccl_device float3 sky_radiance_nishita(KernelGlobals *kg, return xyz_to_rgb(kg, xyz); } -ccl_device void svm_node_tex_sky( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_sky( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { /* Load data */ uint dir_offset = node.y; @@ -226,49 +228,49 @@ ccl_device void svm_node_tex_sky( float sunphi, suntheta, radiance_x, radiance_y, radiance_z; float config_x[9], config_y[9], config_z[9]; - float4 data = read_node_float(kg, offset); + float4 data = read_node_float(kg, &offset); sunphi = data.x; suntheta = data.y; radiance_x = data.z; radiance_y = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); radiance_z = data.x; config_x[0] = data.y; config_x[1] = data.z; config_x[2] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_x[3] = data.x; config_x[4] = data.y; config_x[5] = data.z; config_x[6] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_x[7] = data.x; config_x[8] = data.y; config_y[0] = data.z; config_y[1] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_y[2] = data.x; config_y[3] = data.y; config_y[4] = data.z; config_y[5] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_y[6] = data.x; config_y[7] = data.y; config_y[8] = data.z; config_z[0] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_z[1] = data.x; config_z[2] = data.y; config_z[3] = data.z; config_z[4] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); config_z[5] = data.x; config_z[6] = data.y; config_z[7] = data.z; @@ -305,19 +307,19 @@ ccl_device void svm_node_tex_sky( /* Define variables */ float nishita_data[10]; - float4 data = read_node_float(kg, offset); + float4 data = read_node_float(kg, &offset); nishita_data[0] = data.x; nishita_data[1] = data.y; nishita_data[2] = data.z; nishita_data[3] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); nishita_data[4] = data.x; nishita_data[5] = data.y; nishita_data[6] = data.z; nishita_data[7] = data.w; - data = read_node_float(kg, offset); + data = read_node_float(kg, &offset); nishita_data[8] = data.x; nishita_data[9] = data.y; uint texture_id = __float_as_uint(data.z); @@ -327,6 +329,7 @@ ccl_device void svm_node_tex_sky( } stack_store_float3(stack, out_offset, f); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm.h b/intern/cycles/kernel/svm/svm.h index 000da1fa615..62ba5bf04e3 100644 --- a/intern/cycles/kernel/svm/svm.h +++ b/intern/cycles/kernel/svm/svm.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_H__ -#define __SVM_H__ +#pragma once /* Shader Virtual Machine * @@ -38,60 +37,62 @@ * mostly taken care of in the SVM compiler. */ -#include "kernel/svm/svm_types.h" +#include "kernel/svm/types.h" CCL_NAMESPACE_BEGIN /* Stack */ -ccl_device_inline float3 stack_load_float3(float *stack, uint a) +ccl_device_inline float3 stack_load_float3(ccl_private float *stack, uint a) { kernel_assert(a + 2 < SVM_STACK_SIZE); - return make_float3(stack[a + 0], stack[a + 1], stack[a + 2]); + ccl_private float *stack_a = stack + a; + return make_float3(stack_a[0], stack_a[1], stack_a[2]); } -ccl_device_inline void stack_store_float3(float *stack, uint a, float3 f) +ccl_device_inline void stack_store_float3(ccl_private float *stack, uint a, float3 f) { kernel_assert(a + 2 < SVM_STACK_SIZE); - stack[a + 0] = f.x; - stack[a + 1] = f.y; - stack[a + 2] = f.z; + ccl_private float *stack_a = stack + a; + stack_a[0] = f.x; + stack_a[1] = f.y; + stack_a[2] = f.z; } -ccl_device_inline float stack_load_float(float *stack, uint a) +ccl_device_inline float stack_load_float(ccl_private float *stack, uint a) { kernel_assert(a < SVM_STACK_SIZE); return stack[a]; } -ccl_device_inline float stack_load_float_default(float *stack, uint a, uint value) +ccl_device_inline float stack_load_float_default(ccl_private float *stack, uint a, uint value) { return (a == (uint)SVM_STACK_INVALID) ? __uint_as_float(value) : stack_load_float(stack, a); } -ccl_device_inline void stack_store_float(float *stack, uint a, float f) +ccl_device_inline void stack_store_float(ccl_private float *stack, uint a, float f) { kernel_assert(a < SVM_STACK_SIZE); stack[a] = f; } -ccl_device_inline int stack_load_int(float *stack, uint a) +ccl_device_inline int stack_load_int(ccl_private float *stack, uint a) { kernel_assert(a < SVM_STACK_SIZE); return __float_as_int(stack[a]); } -ccl_device_inline int stack_load_int_default(float *stack, uint a, uint value) +ccl_device_inline int stack_load_int_default(ccl_private float *stack, uint a, uint value) { return (a == (uint)SVM_STACK_INVALID) ? (int)value : stack_load_int(stack, a); } -ccl_device_inline void stack_store_int(float *stack, uint a, int i) +ccl_device_inline void stack_store_int(ccl_private float *stack, uint a, int i) { kernel_assert(a < SVM_STACK_SIZE); @@ -105,14 +106,14 @@ ccl_device_inline bool stack_valid(uint a) /* Reading Nodes */ -ccl_device_inline uint4 read_node(KernelGlobals *kg, int *offset) +ccl_device_inline uint4 read_node(KernelGlobals kg, ccl_private int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); (*offset)++; return node; } -ccl_device_inline float4 read_node_float(KernelGlobals *kg, int *offset) +ccl_device_inline float4 read_node_float(KernelGlobals kg, ccl_private int *offset) { uint4 node = kernel_tex_fetch(__svm_nodes, *offset); float4 f = make_float4(__uint_as_float(node.x), @@ -123,7 +124,7 @@ ccl_device_inline float4 read_node_float(KernelGlobals *kg, int *offset) return f; } -ccl_device_inline float4 fetch_node_float(KernelGlobals *kg, int offset) +ccl_device_inline float4 fetch_node_float(KernelGlobals kg, int offset) { uint4 node = kernel_tex_fetch(__svm_nodes, offset); return make_float4(__uint_as_float(node.x), @@ -132,20 +133,26 @@ ccl_device_inline float4 fetch_node_float(KernelGlobals *kg, int offset) __uint_as_float(node.w)); } -ccl_device_forceinline void svm_unpack_node_uchar2(uint i, uint *x, uint *y) +ccl_device_forceinline void svm_unpack_node_uchar2(uint i, + ccl_private uint *x, + ccl_private uint *y) { *x = (i & 0xFF); *y = ((i >> 8) & 0xFF); } -ccl_device_forceinline void svm_unpack_node_uchar3(uint i, uint *x, uint *y, uint *z) +ccl_device_forceinline void svm_unpack_node_uchar3(uint i, + ccl_private uint *x, + ccl_private uint *y, + ccl_private uint *z) { *x = (i & 0xFF); *y = ((i >> 8) & 0xFF); *z = ((i >> 16) & 0xFF); } -ccl_device_forceinline void svm_unpack_node_uchar4(uint i, uint *x, uint *y, uint *z, uint *w) +ccl_device_forceinline void svm_unpack_node_uchar4( + uint i, ccl_private uint *x, ccl_private uint *y, ccl_private uint *z, ccl_private uint *w) { *x = (i & 0xFF); *y = ((i >> 8) & 0xFF); @@ -157,86 +164,65 @@ CCL_NAMESPACE_END /* Nodes */ -#include "kernel/svm/svm_noise.h" -#include "svm_fractal_noise.h" - -#include "kernel/svm/svm_color_util.h" -#include "kernel/svm/svm_mapping_util.h" -#include "kernel/svm/svm_math_util.h" - -#include "kernel/svm/svm_aov.h" -#include "kernel/svm/svm_attribute.h" -#include "kernel/svm/svm_blackbody.h" -#include "kernel/svm/svm_brick.h" -#include "kernel/svm/svm_brightness.h" -#include "kernel/svm/svm_bump.h" -#include "kernel/svm/svm_camera.h" -#include "kernel/svm/svm_checker.h" -#include "kernel/svm/svm_clamp.h" -#include "kernel/svm/svm_closure.h" -#include "kernel/svm/svm_convert.h" -#include "kernel/svm/svm_displace.h" -#include "kernel/svm/svm_fresnel.h" -#include "kernel/svm/svm_gamma.h" -#include "kernel/svm/svm_geometry.h" -#include "kernel/svm/svm_gradient.h" -#include "kernel/svm/svm_hsv.h" -#include "kernel/svm/svm_ies.h" -#include "kernel/svm/svm_image.h" -#include "kernel/svm/svm_invert.h" -#include "kernel/svm/svm_light_path.h" -#include "kernel/svm/svm_magic.h" -#include "kernel/svm/svm_map_range.h" -#include "kernel/svm/svm_mapping.h" -#include "kernel/svm/svm_math.h" -#include "kernel/svm/svm_mix.h" -#include "kernel/svm/svm_musgrave.h" -#include "kernel/svm/svm_noisetex.h" -#include "kernel/svm/svm_normal.h" -#include "kernel/svm/svm_ramp.h" -#include "kernel/svm/svm_sepcomb_hsv.h" -#include "kernel/svm/svm_sepcomb_vector.h" -#include "kernel/svm/svm_sky.h" -#include "kernel/svm/svm_tex_coord.h" -#include "kernel/svm/svm_value.h" -#include "kernel/svm/svm_vector_rotate.h" -#include "kernel/svm/svm_vector_transform.h" -#include "kernel/svm/svm_vertex_color.h" -#include "kernel/svm/svm_voronoi.h" -#include "kernel/svm/svm_voxel.h" -#include "kernel/svm/svm_wave.h" -#include "kernel/svm/svm_wavelength.h" -#include "kernel/svm/svm_white_noise.h" -#include "kernel/svm/svm_wireframe.h" +#include "kernel/svm/aov.h" +#include "kernel/svm/attribute.h" +#include "kernel/svm/blackbody.h" +#include "kernel/svm/brick.h" +#include "kernel/svm/brightness.h" +#include "kernel/svm/bump.h" +#include "kernel/svm/camera.h" +#include "kernel/svm/checker.h" +#include "kernel/svm/clamp.h" +#include "kernel/svm/closure.h" +#include "kernel/svm/convert.h" +#include "kernel/svm/displace.h" +#include "kernel/svm/fresnel.h" +#include "kernel/svm/gamma.h" +#include "kernel/svm/geometry.h" +#include "kernel/svm/gradient.h" +#include "kernel/svm/hsv.h" +#include "kernel/svm/ies.h" +#include "kernel/svm/image.h" +#include "kernel/svm/invert.h" +#include "kernel/svm/light_path.h" +#include "kernel/svm/magic.h" +#include "kernel/svm/map_range.h" +#include "kernel/svm/mapping.h" +#include "kernel/svm/math.h" +#include "kernel/svm/mix.h" +#include "kernel/svm/musgrave.h" +#include "kernel/svm/noisetex.h" +#include "kernel/svm/normal.h" +#include "kernel/svm/ramp.h" +#include "kernel/svm/sepcomb_hsv.h" +#include "kernel/svm/sepcomb_vector.h" +#include "kernel/svm/sky.h" +#include "kernel/svm/tex_coord.h" +#include "kernel/svm/value.h" +#include "kernel/svm/vector_rotate.h" +#include "kernel/svm/vector_transform.h" +#include "kernel/svm/vertex_color.h" +#include "kernel/svm/voronoi.h" +#include "kernel/svm/voxel.h" +#include "kernel/svm/wave.h" +#include "kernel/svm/wavelength.h" +#include "kernel/svm/white_noise.h" +#include "kernel/svm/wireframe.h" #ifdef __SHADER_RAYTRACE__ -# include "kernel/svm/svm_ao.h" -# include "kernel/svm/svm_bevel.h" +# include "kernel/svm/ao.h" +# include "kernel/svm/bevel.h" #endif CCL_NAMESPACE_BEGIN /* Main Interpreter Loop */ -#if defined(__KERNEL_OPTIX__) && defined(__SHADER_RAYTRACE__) -ccl_device_inline void svm_eval_nodes(KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, - ShaderType type, - int path_flag) -{ - optixDirectCall(0, kg, sd, state, buffer, type, path_flag); -} -extern "C" __device__ void __direct_callable__svm_eval_nodes( -#else -ccl_device_noinline void svm_eval_nodes( -#endif - KernelGlobals *kg, - ShaderData *sd, - ccl_addr_space PathState *state, - ccl_global float *buffer, - ShaderType type, - int path_flag) +template +ccl_device void svm_eval_nodes(KernelGlobals kg, + ConstIntegratorGenericState state, + ShaderData *sd, + ccl_global float *render_buffer, + uint32_t path_flag) { float stack[SVM_STACK_SIZE]; int offset = sd->shader & SHADER_MASK; @@ -247,7 +233,6 @@ ccl_device_noinline void svm_eval_nodes( switch (node.x) { case NODE_END: return; -#if NODES_GROUP(NODE_GROUP_LEVEL_0) case NODE_SHADER_JUMP: { if (type == SHADER_TYPE_SURFACE) offset = node.y; @@ -260,13 +245,20 @@ ccl_device_noinline void svm_eval_nodes( break; } case NODE_CLOSURE_BSDF: - svm_node_closure_bsdf(kg, sd, stack, node, type, path_flag, &offset); + offset = svm_node_closure_bsdf( + kg, sd, stack, node, path_flag, offset); break; case NODE_CLOSURE_EMISSION: - svm_node_closure_emission(sd, stack, node); + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_closure_emission(sd, stack, node); + } break; case NODE_CLOSURE_BACKGROUND: - svm_node_closure_background(sd, stack, node); + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_closure_background(sd, stack, node); + } break; case NODE_CLOSURE_SET_WEIGHT: svm_node_closure_set_weight(sd, node.y, node.z, node.w); @@ -275,7 +267,10 @@ ccl_device_noinline void svm_eval_nodes( svm_node_closure_weight(sd, stack, node.y); break; case NODE_EMISSION_WEIGHT: - svm_node_emission_weight(kg, sd, stack, node); + IF_KERNEL_NODES_FEATURE(EMISSION) + { + svm_node_emission_weight(kg, sd, stack, node); + } break; case NODE_MIX_CLOSURE: svm_node_mix_closure(sd, stack, node); @@ -295,86 +290,123 @@ ccl_device_noinline void svm_eval_nodes( svm_node_convert(kg, sd, stack, node.y, node.z, node.w); break; case NODE_TEX_COORD: - svm_node_tex_coord(kg, sd, path_flag, stack, node, &offset); + offset = svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); break; case NODE_VALUE_F: svm_node_value_f(kg, sd, stack, node.y, node.z); break; case NODE_VALUE_V: - svm_node_value_v(kg, sd, stack, node.y, &offset); + offset = svm_node_value_v(kg, sd, stack, node.y, offset); break; case NODE_ATTR: - svm_node_attr(kg, sd, stack, node); + svm_node_attr(kg, sd, stack, node); break; case NODE_VERTEX_COLOR: svm_node_vertex_color(kg, sd, stack, node.y, node.z, node.w); break; -# if NODES_FEATURE(NODE_FEATURE_BUMP) case NODE_GEOMETRY_BUMP_DX: - svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_geometry_bump_dx(kg, sd, stack, node.y, node.z); + } break; case NODE_GEOMETRY_BUMP_DY: - svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_geometry_bump_dy(kg, sd, stack, node.y, node.z); + } break; case NODE_SET_DISPLACEMENT: - svm_node_set_displacement(kg, sd, stack, node.y); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_set_displacement(kg, sd, stack, node.y); + } break; case NODE_DISPLACEMENT: - svm_node_displacement(kg, sd, stack, node); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_displacement(kg, sd, stack, node); + } break; case NODE_VECTOR_DISPLACEMENT: - svm_node_vector_displacement(kg, sd, stack, node, &offset); + IF_KERNEL_NODES_FEATURE(BUMP) + { + offset = svm_node_vector_displacement(kg, sd, stack, node, offset); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */ case NODE_TEX_IMAGE: - svm_node_tex_image(kg, sd, stack, node, &offset); + offset = svm_node_tex_image(kg, sd, stack, node, offset); break; case NODE_TEX_IMAGE_BOX: svm_node_tex_image_box(kg, sd, stack, node); break; case NODE_TEX_NOISE: - svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_tex_noise(kg, sd, stack, node.y, node.z, node.w, offset); break; -# if NODES_FEATURE(NODE_FEATURE_BUMP) case NODE_SET_BUMP: - svm_node_set_bump(kg, sd, stack, node); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_set_bump(kg, sd, stack, node); + } break; case NODE_ATTR_BUMP_DX: - svm_node_attr_bump_dx(kg, sd, stack, node); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_attr_bump_dx(kg, sd, stack, node); + } break; case NODE_ATTR_BUMP_DY: - svm_node_attr_bump_dy(kg, sd, stack, node); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_attr_bump_dy(kg, sd, stack, node); + } break; case NODE_VERTEX_COLOR_BUMP_DX: - svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_vertex_color_bump_dx(kg, sd, stack, node.y, node.z, node.w); + } break; case NODE_VERTEX_COLOR_BUMP_DY: - svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_vertex_color_bump_dy(kg, sd, stack, node.y, node.z, node.w); + } break; case NODE_TEX_COORD_BUMP_DX: - svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, &offset); + IF_KERNEL_NODES_FEATURE(BUMP) + { + offset = svm_node_tex_coord_bump_dx(kg, sd, path_flag, stack, node, offset); + } break; case NODE_TEX_COORD_BUMP_DY: - svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, &offset); + IF_KERNEL_NODES_FEATURE(BUMP) + { + offset = svm_node_tex_coord_bump_dy(kg, sd, path_flag, stack, node, offset); + } break; case NODE_CLOSURE_SET_NORMAL: - svm_node_set_normal(kg, sd, stack, node.y, node.z); + IF_KERNEL_NODES_FEATURE(BUMP) + { + svm_node_set_normal(kg, sd, stack, node.y, node.z); + } break; -# if NODES_FEATURE(NODE_FEATURE_BUMP_STATE) case NODE_ENTER_BUMP_EVAL: - svm_node_enter_bump_eval(kg, sd, stack, node.y); + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { + svm_node_enter_bump_eval(kg, sd, stack, node.y); + } break; case NODE_LEAVE_BUMP_EVAL: - svm_node_leave_bump_eval(kg, sd, stack, node.y); + IF_KERNEL_NODES_FEATURE(BUMP_STATE) + { + svm_node_leave_bump_eval(kg, sd, stack, node.y); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_BUMP_STATE) */ -# endif /* NODES_FEATURE(NODE_FEATURE_BUMP) */ case NODE_HSV: - svm_node_hsv(kg, sd, stack, node, &offset); + svm_node_hsv(kg, sd, stack, node); break; -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_0) */ -#if NODES_GROUP(NODE_GROUP_LEVEL_1) case NODE_CLOSURE_HOLDOUT: svm_node_closure_holdout(sd, stack, node); break; @@ -384,22 +416,26 @@ ccl_device_noinline void svm_eval_nodes( case NODE_LAYER_WEIGHT: svm_node_layer_weight(sd, stack, node); break; -# if NODES_FEATURE(NODE_FEATURE_VOLUME) case NODE_CLOSURE_VOLUME: - svm_node_closure_volume(kg, sd, stack, node, type); + IF_KERNEL_NODES_FEATURE(VOLUME) + { + svm_node_closure_volume(kg, sd, stack, node); + } break; case NODE_PRINCIPLED_VOLUME: - svm_node_principled_volume(kg, sd, stack, node, type, path_flag, &offset); + IF_KERNEL_NODES_FEATURE(VOLUME) + { + offset = svm_node_principled_volume(kg, sd, stack, node, path_flag, offset); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_VOLUME) */ case NODE_MATH: - svm_node_math(kg, sd, stack, node.y, node.z, node.w, &offset); + svm_node_math(kg, sd, stack, node.y, node.z, node.w); break; case NODE_VECTOR_MATH: - svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_vector_math(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_RGB_RAMP: - svm_node_rgb_ramp(kg, sd, stack, node, &offset); + offset = svm_node_rgb_ramp(kg, sd, stack, node, offset); break; case NODE_GAMMA: svm_node_gamma(sd, stack, node.y, node.z, node.w); @@ -408,7 +444,7 @@ ccl_device_noinline void svm_eval_nodes( svm_node_brightness(sd, stack, node.y, node.z, node.w); break; case NODE_LIGHT_PATH: - svm_node_light_path(sd, state, stack, node.y, node.z, path_flag); + svm_node_light_path(kg, state, sd, stack, node.y, node.z, path_flag); break; case NODE_OBJECT_INFO: svm_node_object_info(kg, sd, stack, node.y, node.z); @@ -416,22 +452,23 @@ ccl_device_noinline void svm_eval_nodes( case NODE_PARTICLE_INFO: svm_node_particle_info(kg, sd, stack, node.y, node.z); break; -# if defined(__HAIR__) && NODES_FEATURE(NODE_FEATURE_HAIR) +#if defined(__HAIR__) case NODE_HAIR_INFO: - svm_node_hair_info(kg, sd, stack, node.y, node.z); + IF_KERNEL_NODES_FEATURE(HAIR) + { + svm_node_hair_info(kg, sd, stack, node.y, node.z); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_HAIR) */ -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_1) */ +#endif -#if NODES_GROUP(NODE_GROUP_LEVEL_2) case NODE_TEXTURE_MAPPING: - svm_node_texture_mapping(kg, sd, stack, node.y, node.z, &offset); + offset = svm_node_texture_mapping(kg, sd, stack, node.y, node.z, offset); break; case NODE_MAPPING: - svm_node_mapping(kg, sd, stack, node.y, node.z, node.w, &offset); + svm_node_mapping(kg, sd, stack, node.y, node.z, node.w); break; case NODE_MIN_MAX: - svm_node_min_max(kg, sd, stack, node.y, node.z, &offset); + offset = svm_node_min_max(kg, sd, stack, node.y, node.z, offset); break; case NODE_CAMERA: svm_node_camera(kg, sd, stack, node.y, node.z, node.w); @@ -440,47 +477,48 @@ ccl_device_noinline void svm_eval_nodes( svm_node_tex_environment(kg, sd, stack, node); break; case NODE_TEX_SKY: - svm_node_tex_sky(kg, sd, stack, node, &offset); + offset = svm_node_tex_sky(kg, sd, stack, node, offset); break; case NODE_TEX_GRADIENT: svm_node_tex_gradient(sd, stack, node); break; case NODE_TEX_VORONOI: - svm_node_tex_voronoi(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_tex_voronoi( + kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_TEX_MUSGRAVE: - svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_tex_musgrave(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_TEX_WAVE: - svm_node_tex_wave(kg, sd, stack, node, &offset); + offset = svm_node_tex_wave(kg, sd, stack, node, offset); break; case NODE_TEX_MAGIC: - svm_node_tex_magic(kg, sd, stack, node, &offset); + offset = svm_node_tex_magic(kg, sd, stack, node, offset); break; case NODE_TEX_CHECKER: svm_node_tex_checker(kg, sd, stack, node); break; case NODE_TEX_BRICK: - svm_node_tex_brick(kg, sd, stack, node, &offset); + offset = svm_node_tex_brick(kg, sd, stack, node, offset); break; case NODE_TEX_WHITE_NOISE: - svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w, &offset); + svm_node_tex_white_noise(kg, sd, stack, node.y, node.z, node.w); break; case NODE_NORMAL: - svm_node_normal(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_normal(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_LIGHT_FALLOFF: svm_node_light_falloff(sd, stack, node); break; case NODE_IES: - svm_node_ies(kg, sd, stack, node, &offset); + svm_node_ies(kg, sd, stack, node); break; -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_2) */ - -#if NODES_GROUP(NODE_GROUP_LEVEL_3) case NODE_RGB_CURVES: case NODE_VECTOR_CURVES: - svm_node_curves(kg, sd, stack, node, &offset); + offset = svm_node_curves(kg, sd, stack, node, offset); + break; + case NODE_FLOAT_CURVE: + offset = svm_node_curve(kg, sd, stack, node, offset); break; case NODE_TANGENT: svm_node_tangent(kg, sd, stack, node); @@ -492,7 +530,7 @@ ccl_device_noinline void svm_eval_nodes( svm_node_invert(sd, stack, node.y, node.z, node.w); break; case NODE_MIX: - svm_node_mix(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_mix(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_SEPARATE_VECTOR: svm_node_separate_vector(sd, stack, node.y, node.z, node.w); @@ -501,10 +539,10 @@ ccl_device_noinline void svm_eval_nodes( svm_node_combine_vector(sd, stack, node.y, node.z, node.w); break; case NODE_SEPARATE_HSV: - svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_separate_hsv(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_COMBINE_HSV: - svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_combine_hsv(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_VECTOR_ROTATE: svm_node_vector_rotate(sd, stack, node.y, node.z, node.w); @@ -522,39 +560,37 @@ ccl_device_noinline void svm_eval_nodes( svm_node_blackbody(kg, sd, stack, node.y, node.z); break; case NODE_MAP_RANGE: - svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_map_range(kg, sd, stack, node.y, node.z, node.w, offset); break; case NODE_CLAMP: - svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, &offset); + offset = svm_node_clamp(kg, sd, stack, node.y, node.z, node.w, offset); break; -# ifdef __SHADER_RAYTRACE__ +#ifdef __SHADER_RAYTRACE__ case NODE_BEVEL: - svm_node_bevel(kg, sd, state, stack, node); + svm_node_bevel(kg, state, sd, stack, node); break; case NODE_AMBIENT_OCCLUSION: - svm_node_ao(kg, sd, state, stack, node); + svm_node_ao(kg, state, sd, stack, node); break; -# endif /* __SHADER_RAYTRACE__ */ -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_3) */ +#endif -#if NODES_GROUP(NODE_GROUP_LEVEL_4) -# if NODES_FEATURE(NODE_FEATURE_VOLUME) case NODE_TEX_VOXEL: - svm_node_tex_voxel(kg, sd, stack, node, &offset); + IF_KERNEL_NODES_FEATURE(VOLUME) + { + offset = svm_node_tex_voxel(kg, sd, stack, node, offset); + } break; -# endif /* NODES_FEATURE(NODE_FEATURE_VOLUME) */ case NODE_AOV_START: - if (!svm_node_aov_check(state, buffer)) { + if (!svm_node_aov_check(path_flag, render_buffer)) { return; } break; case NODE_AOV_COLOR: - svm_node_aov_color(kg, sd, stack, node, buffer); + svm_node_aov_color(kg, state, sd, stack, node, render_buffer); break; case NODE_AOV_VALUE: - svm_node_aov_value(kg, sd, stack, node, buffer); + svm_node_aov_value(kg, state, sd, stack, node, render_buffer); break; -#endif /* NODES_GROUP(NODE_GROUP_LEVEL_4) */ default: kernel_assert(!"Unknown node type was passed to the SVM machine"); return; @@ -563,5 +599,3 @@ ccl_device_noinline void svm_eval_nodes( } CCL_NAMESPACE_END - -#endif /* __SVM_H__ */ diff --git a/intern/cycles/kernel/svm/svm_aov.h b/intern/cycles/kernel/svm/svm_aov.h deleted file mode 100644 index 899e466d099..00000000000 --- a/intern/cycles/kernel/svm/svm_aov.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -CCL_NAMESPACE_BEGIN - -ccl_device_inline bool svm_node_aov_check(ccl_addr_space PathState *state, - ccl_global float *buffer) -{ - int path_flag = state->flag; - - bool is_primary = (path_flag & PATH_RAY_CAMERA) && (!(path_flag & PATH_RAY_SINGLE_PASS_DONE)); - - return ((buffer != NULL) && is_primary); -} - -ccl_device void svm_node_aov_color( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer) -{ - float3 val = stack_load_float3(stack, node.y); - - if (buffer) { - kernel_write_pass_float4(buffer + kernel_data.film.pass_aov_color + 4 * node.z, - make_float4(val.x, val.y, val.z, 1.0f)); - } -} - -ccl_device void svm_node_aov_value( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, ccl_global float *buffer) -{ - float val = stack_load_float(stack, node.y); - - if (buffer) { - kernel_write_pass_float(buffer + kernel_data.film.pass_aov_value + node.z, val); - } -} -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_ramp.h b/intern/cycles/kernel/svm/svm_ramp.h deleted file mode 100644 index 85ccf39144b..00000000000 --- a/intern/cycles/kernel/svm/svm_ramp.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __SVM_RAMP_H__ -#define __SVM_RAMP_H__ - -CCL_NAMESPACE_BEGIN - -/* NOTE: svm_ramp.h, svm_ramp_util.h and node_ramp_util.h must stay consistent */ - -ccl_device_inline float4 rgb_ramp_lookup( - KernelGlobals *kg, int offset, float f, bool interpolate, bool extrapolate, int table_size) -{ - if ((f < 0.0f || f > 1.0f) && extrapolate) { - float4 t0, dy; - if (f < 0.0f) { - t0 = fetch_node_float(kg, offset); - dy = t0 - fetch_node_float(kg, offset + 1); - f = -f; - } - else { - t0 = fetch_node_float(kg, offset + table_size - 1); - dy = t0 - fetch_node_float(kg, offset + table_size - 2); - f = f - 1.0f; - } - return t0 + dy * f * (table_size - 1); - } - - f = saturate(f) * (table_size - 1); - - /* clamp int as well in case of NaN */ - int i = clamp(float_to_int(f), 0, table_size - 1); - float t = f - (float)i; - - float4 a = fetch_node_float(kg, offset + i); - - if (interpolate && t > 0.0f) - a = (1.0f - t) * a + t * fetch_node_float(kg, offset + i + 1); - - return a; -} - -ccl_device void svm_node_rgb_ramp( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) -{ - uint fac_offset, color_offset, alpha_offset; - uint interpolate = node.z; - - svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &alpha_offset); - - uint table_size = read_node(kg, offset).x; - - float fac = stack_load_float(stack, fac_offset); - float4 color = rgb_ramp_lookup(kg, *offset, fac, interpolate, false, table_size); - - if (stack_valid(color_offset)) - stack_store_float3(stack, color_offset, float4_to_float3(color)); - if (stack_valid(alpha_offset)) - stack_store_float(stack, alpha_offset, color.w); - - *offset += table_size; -} - -ccl_device void svm_node_curves( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) -{ - uint fac_offset, color_offset, out_offset; - svm_unpack_node_uchar3(node.y, &fac_offset, &color_offset, &out_offset); - - uint table_size = read_node(kg, offset).x; - - float fac = stack_load_float(stack, fac_offset); - float3 color = stack_load_float3(stack, color_offset); - - const float min_x = __int_as_float(node.z), max_x = __int_as_float(node.w); - const float range_x = max_x - min_x; - const float3 relpos = (color - make_float3(min_x, min_x, min_x)) / range_x; - - float r = rgb_ramp_lookup(kg, *offset, relpos.x, true, true, table_size).x; - float g = rgb_ramp_lookup(kg, *offset, relpos.y, true, true, table_size).y; - float b = rgb_ramp_lookup(kg, *offset, relpos.z, true, true, table_size).z; - - color = (1.0f - fac) * color + fac * make_float3(r, g, b); - stack_store_float3(stack, out_offset, color); - - *offset += table_size; -} - -CCL_NAMESPACE_END - -#endif /* __SVM_RAMP_H__ */ diff --git a/intern/cycles/kernel/svm/svm_wavelength.h b/intern/cycles/kernel/svm/svm_wavelength.h deleted file mode 100644 index d6144802559..00000000000 --- a/intern/cycles/kernel/svm/svm_wavelength.h +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Adapted from Open Shading Language with this license: - * - * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. - * All Rights Reserved. - * - * Modifications Copyright 2013, Blender Foundation. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are - * met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the name of Sony Pictures Imageworks nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -CCL_NAMESPACE_BEGIN - -/* Wavelength to RGB */ - -// CIE colour matching functions xBar, yBar, and zBar for -// wavelengths from 380 through 780 nanometers, every 5 -// nanometers. For a wavelength lambda in this range: -// cie_colour_match[(lambda - 380) / 5][0] = xBar -// cie_colour_match[(lambda - 380) / 5][1] = yBar -// cie_colour_match[(lambda - 380) / 5][2] = zBar -ccl_static_constant float cie_colour_match[81][3] = { - {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, - {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, - {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, - {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, - {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, - {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, - {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, - {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, - {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, - {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, - {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, - {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, - {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, - {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, - {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, - {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, - {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, - {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, - {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, - {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, - {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, - {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, - {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, - {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, - {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, - {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, - {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; - -ccl_device void svm_node_wavelength( - KernelGlobals *kg, ShaderData *sd, float *stack, uint wavelength, uint color_out) -{ - float lambda_nm = stack_load_float(stack, wavelength); - float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); // scaled 0..80 - int i = float_to_int(ii); - float3 color; - - if (i < 0 || i >= 80) { - color = make_float3(0.0f, 0.0f, 0.0f); - } - else { - ii -= i; - ccl_constant float *c = cie_colour_match[i]; - color = interp(make_float3(c[0], c[1], c[2]), make_float3(c[3], c[4], c[5]), ii); - } - - color = xyz_to_rgb(kg, color); - color *= 1.0f / 2.52f; // Empirical scale from lg to make all comps <= 1 - - /* Clamp to zero if values are smaller */ - color = max(color, make_float3(0.0f, 0.0f, 0.0f)); - - stack_store_float3(stack, color_out, color); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_tex_coord.h b/intern/cycles/kernel/svm/tex_coord.h similarity index 79% rename from intern/cycles/kernel/svm/svm_tex_coord.h rename to intern/cycles/kernel/svm/tex_coord.h index 46600551cc4..5e0debc968a 100644 --- a/intern/cycles/kernel/svm/svm_tex_coord.h +++ b/intern/cycles/kernel/svm/tex_coord.h @@ -14,12 +14,22 @@ * limitations under the License. */ +#pragma once + +#include "kernel/camera/camera.h" +#include "kernel/geom/geom.h" +#include "kernel/sample/mapping.h" + CCL_NAMESPACE_BEGIN /* Texture Coordinate Node */ -ccl_device void svm_node_tex_coord( - KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_coord(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + ccl_private float *stack, + uint4 node, + int offset) { float3 data; uint type = node.y; @@ -35,9 +45,9 @@ ccl_device void svm_node_tex_coord( } else { Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); data = transform_point(&tfm, data); } break; @@ -92,10 +102,15 @@ ccl_device void svm_node_tex_coord( } stack_store_float3(stack, out_offset, data); + return offset; } -ccl_device void svm_node_tex_coord_bump_dx( - KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_coord_bump_dx(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + ccl_private float *stack, + uint4 node, + int offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -112,9 +127,9 @@ ccl_device void svm_node_tex_coord_bump_dx( } else { Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); data = transform_point(&tfm, data); } break; @@ -136,7 +151,7 @@ ccl_device void svm_node_tex_coord_bump_dx( case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dx); + data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(sd->ray_dP, 0.0f, 0.0f)); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dx); data.z = 0.0f; @@ -169,13 +184,18 @@ ccl_device void svm_node_tex_coord_bump_dx( } stack_store_float3(stack, out_offset, data); + return offset; #else - svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); + return svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); #endif } -ccl_device void svm_node_tex_coord_bump_dy( - KernelGlobals *kg, ShaderData *sd, int path_flag, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_coord_bump_dy(KernelGlobals kg, + ccl_private ShaderData *sd, + uint32_t path_flag, + ccl_private float *stack, + uint4 node, + int offset) { #ifdef __RAY_DIFFERENTIALS__ float3 data; @@ -192,9 +212,9 @@ ccl_device void svm_node_tex_coord_bump_dy( } else { Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); data = transform_point(&tfm, data); } break; @@ -216,7 +236,7 @@ ccl_device void svm_node_tex_coord_bump_dy( case NODE_TEXCO_WINDOW: { if ((path_flag & PATH_RAY_CAMERA) && sd->object == OBJECT_NONE && kernel_data.cam.type == CAMERA_ORTHOGRAPHIC) - data = camera_world_to_ndc(kg, sd, sd->ray_P + sd->ray_dP.dy); + data = camera_world_to_ndc(kg, sd, sd->ray_P + make_float3(0.0f, sd->ray_dP, 0.0f)); else data = camera_world_to_ndc(kg, sd, sd->P + sd->dP.dy); data.z = 0.0f; @@ -249,12 +269,16 @@ ccl_device void svm_node_tex_coord_bump_dy( } stack_store_float3(stack, out_offset, data); + return offset; #else - svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); + return svm_node_tex_coord(kg, sd, path_flag, stack, node, offset); #endif } -ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_normal_map(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint color_offset, strength_offset, normal_offset, space; svm_unpack_node_uchar4(node.y, &color_offset, &strength_offset, &normal_offset, &space); @@ -337,8 +361,6 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st N = safe_normalize(sd->N + (N - sd->N) * strength); } - N = ensure_valid_reflection(sd->Ng, sd->I, N); - if (is_zero(N)) { N = sd->N; } @@ -346,7 +368,10 @@ ccl_device void svm_node_normal_map(KernelGlobals *kg, ShaderData *sd, float *st stack_store_float3(stack, normal_offset, N); } -ccl_device void svm_node_tangent(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_tangent(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint tangent_offset, direction_type, axis; svm_unpack_node_uchar3(node.y, &tangent_offset, &direction_type, &axis); diff --git a/intern/cycles/kernel/svm/svm_types.h b/intern/cycles/kernel/svm/types.h similarity index 89% rename from intern/cycles/kernel/svm/svm_types.h rename to intern/cycles/kernel/svm/types.h index 062afcfa5ac..8c95c571815 100644 --- a/intern/cycles/kernel/svm/svm_types.h +++ b/intern/cycles/kernel/svm/types.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __SVM_TYPES_H__ -#define __SVM_TYPES_H__ +#pragma once CCL_NAMESPACE_BEGIN @@ -30,37 +29,6 @@ CCL_NAMESPACE_BEGIN /* Nodes */ -/* Known frequencies of used nodes, used for selective nodes compilation - * in the kernel. Currently only affects split OpenCL kernel. - * - * Keep as defines so it's easy to check which nodes are to be compiled - * from preprocessor. - * - * Lower the number of group more often the node is used. - */ -#define NODE_GROUP_LEVEL_0 0 -#define NODE_GROUP_LEVEL_1 1 -#define NODE_GROUP_LEVEL_2 2 -#define NODE_GROUP_LEVEL_3 3 -#define NODE_GROUP_LEVEL_4 4 -#define NODE_GROUP_LEVEL_MAX NODE_GROUP_LEVEL_4 - -#define NODE_FEATURE_VOLUME (1 << 0) -#define NODE_FEATURE_HAIR (1 << 1) -#define NODE_FEATURE_BUMP (1 << 2) -#define NODE_FEATURE_BUMP_STATE (1 << 3) -#define NODE_FEATURE_VORONOI_EXTRA (1 << 4) -/* TODO(sergey): Consider using something like ((uint)(-1)). - * Need to check carefully operand types around usage of this - * define first. - */ -#define NODE_FEATURE_ALL \ - (NODE_FEATURE_VOLUME | NODE_FEATURE_HAIR | NODE_FEATURE_BUMP | NODE_FEATURE_BUMP_STATE | \ - NODE_FEATURE_VORONOI_EXTRA) - -#define NODES_GROUP(group) ((group) <= __NODES_MAX_GROUP__) -#define NODES_FEATURE(feature) ((__NODES_FEATURES__ & (feature)) != 0) - typedef enum ShaderNodeType { NODE_END = 0, NODE_SHADER_JUMP, @@ -153,6 +121,7 @@ typedef enum ShaderNodeType { NODE_AOV_START, NODE_AOV_COLOR, NODE_AOV_VALUE, + NODE_FLOAT_CURVE, /* NOTE: for best OpenCL performance, item definition in the enum must * match the switch case order in svm.h. */ } ShaderNodeType; @@ -204,6 +173,7 @@ typedef enum NodeParticleInfo { typedef enum NodeHairInfo { NODE_INFO_CURVE_IS_STRAND, NODE_INFO_CURVE_INTERCEPT, + NODE_INFO_CURVE_LENGTH, NODE_INFO_CURVE_THICKNESS, /* Fade for minimum hair width transiency. */ // NODE_INFO_CURVE_FADE, @@ -567,17 +537,12 @@ typedef enum ClosureType { CLOSURE_BSDF_HAIR_TRANSMISSION_ID, /* Special cases */ - CLOSURE_BSDF_BSSRDF_ID, - CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID, CLOSURE_BSDF_TRANSPARENT_ID, /* BSSRDF */ - CLOSURE_BSSRDF_CUBIC_ID, - CLOSURE_BSSRDF_GAUSSIAN_ID, - CLOSURE_BSSRDF_PRINCIPLED_ID, CLOSURE_BSSRDF_BURLEY_ID, CLOSURE_BSSRDF_RANDOM_WALK_ID, - CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID, + CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID, /* Other */ CLOSURE_HOLDOUT_ID, @@ -601,8 +566,6 @@ typedef enum ClosureType { (type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID)) #define CLOSURE_IS_BSDF_TRANSMISSION(type) \ (type >= CLOSURE_BSDF_REFRACTION_ID && type <= CLOSURE_BSDF_HAIR_TRANSMISSION_ID) -#define CLOSURE_IS_BSDF_BSSRDF(type) \ - (type == CLOSURE_BSDF_BSSRDF_ID || type == CLOSURE_BSDF_BSSRDF_PRINCIPLED_ID) #define CLOSURE_IS_BSDF_SINGULAR(type) \ (type == CLOSURE_BSDF_REFLECTION_ID || type == CLOSURE_BSDF_REFRACTION_ID || \ type == CLOSURE_BSDF_TRANSPARENT_ID) @@ -620,11 +583,9 @@ typedef enum ClosureType { type == CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_FRESNEL_ID || \ type == CLOSURE_BSDF_MICROFACET_GGX_FRESNEL_ID || \ type == CLOSURE_BSDF_MICROFACET_GGX_CLEARCOAT_ID) -#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) +#define CLOSURE_IS_BSDF_OR_BSSRDF(type) (type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) #define CLOSURE_IS_BSSRDF(type) \ - (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID) -#define CLOSURE_IS_DISK_BSSRDF(type) \ - (type >= CLOSURE_BSSRDF_CUBIC_ID && type <= CLOSURE_BSSRDF_BURLEY_ID) + (type >= CLOSURE_BSSRDF_BURLEY_ID && type <= CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID) #define CLOSURE_IS_VOLUME(type) \ (type >= CLOSURE_VOLUME_ID && type <= CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) #define CLOSURE_IS_VOLUME_SCATTER(type) (type == CLOSURE_VOLUME_HENYEY_GREENSTEIN_ID) @@ -638,5 +599,3 @@ typedef enum ClosureType { #define CLOSURE_WEIGHT_CUTOFF 1e-5f CCL_NAMESPACE_END - -#endif /* __SVM_TYPES_H__ */ diff --git a/intern/cycles/kernel/svm/svm_value.h b/intern/cycles/kernel/svm/value.h similarity index 60% rename from intern/cycles/kernel/svm/svm_value.h rename to intern/cycles/kernel/svm/value.h index 5b76f2c8832..cc62f1e2a82 100644 --- a/intern/cycles/kernel/svm/svm_value.h +++ b/intern/cycles/kernel/svm/value.h @@ -14,25 +14,34 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Value Nodes */ -ccl_device void svm_node_value_f( - KernelGlobals *kg, ShaderData *sd, float *stack, uint ivalue, uint out_offset) +ccl_device void svm_node_value_f(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint ivalue, + uint out_offset) { stack_store_float(stack, out_offset, __uint_as_float(ivalue)); } -ccl_device void svm_node_value_v( - KernelGlobals *kg, ShaderData *sd, float *stack, uint out_offset, int *offset) +ccl_device int svm_node_value_v(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint out_offset, + int offset) { /* read extra data */ - uint4 node1 = read_node(kg, offset); + uint4 node1 = read_node(kg, &offset); float3 p = make_float3( __uint_as_float(node1.y), __uint_as_float(node1.z), __uint_as_float(node1.w)); stack_store_float3(stack, out_offset, p); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_vector_rotate.h b/intern/cycles/kernel/svm/vector_rotate.h similarity index 87% rename from intern/cycles/kernel/svm/svm_vector_rotate.h rename to intern/cycles/kernel/svm/vector_rotate.h index 50045752484..2a0d331734c 100644 --- a/intern/cycles/kernel/svm/svm_vector_rotate.h +++ b/intern/cycles/kernel/svm/vector_rotate.h @@ -14,15 +14,17 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Vector Rotate */ -ccl_device void svm_node_vector_rotate(ShaderData *sd, - float *stack, - uint input_stack_offsets, - uint axis_stack_offsets, - uint result_stack_offset) +ccl_device_noinline void svm_node_vector_rotate(ccl_private ShaderData *sd, + ccl_private float *stack, + uint input_stack_offsets, + uint axis_stack_offsets, + uint result_stack_offset) { uint type, vector_stack_offset, rotation_stack_offset, center_stack_offset, axis_stack_offset, angle_stack_offset, invert; diff --git a/intern/cycles/kernel/svm/svm_vector_transform.h b/intern/cycles/kernel/svm/vector_transform.h similarity index 91% rename from intern/cycles/kernel/svm/svm_vector_transform.h rename to intern/cycles/kernel/svm/vector_transform.h index 1e95492cf1b..d7a51078cea 100644 --- a/intern/cycles/kernel/svm/svm_vector_transform.h +++ b/intern/cycles/kernel/svm/vector_transform.h @@ -14,14 +14,16 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Vector Transform */ -ccl_device void svm_node_vector_transform(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint4 node) +ccl_device_noinline void svm_node_vector_transform(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint itype, ifrom, ito; uint vector_in, vector_out; diff --git a/intern/cycles/kernel/svm/svm_vertex_color.h b/intern/cycles/kernel/svm/vertex_color.h similarity index 62% rename from intern/cycles/kernel/svm/svm_vertex_color.h rename to intern/cycles/kernel/svm/vertex_color.h index 0aa45835522..b676a28c0e3 100644 --- a/intern/cycles/kernel/svm/svm_vertex_color.h +++ b/intern/cycles/kernel/svm/vertex_color.h @@ -14,14 +14,16 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_vertex_color(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint layer_id, - uint color_offset, - uint alpha_offset) +ccl_device_noinline void svm_node_vertex_color(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint layer_id, + uint color_offset, + uint alpha_offset) { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { @@ -35,18 +37,12 @@ ccl_device void svm_node_vertex_color(KernelGlobals *kg, } } -#ifndef __KERNEL_CUDA__ -ccl_device -#else -ccl_device_noinline -#endif - void - svm_node_vertex_color_bump_dx(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint layer_id, - uint color_offset, - uint alpha_offset) +ccl_device_noinline void svm_node_vertex_color_bump_dx(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint layer_id, + uint color_offset, + uint alpha_offset) { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { @@ -62,18 +58,12 @@ ccl_device_noinline } } -#ifndef __KERNEL_CUDA__ -ccl_device -#else -ccl_device_noinline -#endif - void - svm_node_vertex_color_bump_dy(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint layer_id, - uint color_offset, - uint alpha_offset) +ccl_device_noinline void svm_node_vertex_color_bump_dy(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint layer_id, + uint color_offset, + uint alpha_offset) { AttributeDescriptor descriptor = find_attribute(kg, sd, layer_id); if (descriptor.offset != ATTR_STD_NOT_FOUND) { diff --git a/intern/cycles/kernel/svm/svm_voronoi.h b/intern/cycles/kernel/svm/voronoi.h similarity index 84% rename from intern/cycles/kernel/svm/svm_voronoi.h rename to intern/cycles/kernel/svm/voronoi.h index d0e7db35fab..730965b6aed 100644 --- a/intern/cycles/kernel/svm/svm_voronoi.h +++ b/intern/cycles/kernel/svm/voronoi.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* @@ -46,9 +48,9 @@ ccl_device void voronoi_f1_1d(float w, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float *outW) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float *outW) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -76,9 +78,9 @@ ccl_device void voronoi_smooth_f1_1d(float w, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float *outW) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float *outW) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -108,9 +110,9 @@ ccl_device void voronoi_f2_1d(float w, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float *outW) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float *outW) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -144,7 +146,9 @@ ccl_device void voronoi_f2_1d(float w, *outW = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_1d(float w, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_1d(float w, + float randomness, + ccl_private float *outDistance) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -158,7 +162,7 @@ ccl_device void voronoi_distance_to_edge_1d(float w, float randomness, float *ou *outDistance = min(distanceToMidLeft, distanceToMidRight); } -ccl_device void voronoi_n_sphere_radius_1d(float w, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_1d(float w, float randomness, ccl_private float *outRadius) { float cellPosition = floorf(w); float localPosition = w - cellPosition; @@ -223,9 +227,9 @@ ccl_device void voronoi_f1_2d(float2 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float2 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float2 *outPosition) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -256,9 +260,9 @@ ccl_device void voronoi_smooth_f1_2d(float2 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float2 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float2 *outPosition) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -291,9 +295,9 @@ ccl_device void voronoi_f2_2d(float2 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float2 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float2 *outPosition) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -330,7 +334,9 @@ ccl_device void voronoi_f2_2d(float2 coord, *outPosition = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_2d(float2 coord, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_2d(float2 coord, + float randomness, + ccl_private float *outDistance) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -369,7 +375,9 @@ ccl_device void voronoi_distance_to_edge_2d(float2 coord, float randomness, floa *outDistance = minDistance; } -ccl_device void voronoi_n_sphere_radius_2d(float2 coord, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_2d(float2 coord, + float randomness, + ccl_private float *outRadius) { float2 cellPosition = floor(coord); float2 localPosition = coord - cellPosition; @@ -441,9 +449,9 @@ ccl_device void voronoi_f1_3d(float3 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float3 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float3 *outPosition) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -477,9 +485,9 @@ ccl_device void voronoi_smooth_f1_3d(float3 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float3 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float3 *outPosition) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -515,9 +523,9 @@ ccl_device void voronoi_f2_3d(float3 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float3 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float3 *outPosition) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -557,7 +565,9 @@ ccl_device void voronoi_f2_3d(float3 coord, *outPosition = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_3d(float3 coord, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_3d(float3 coord, + float randomness, + ccl_private float *outDistance) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -600,7 +610,9 @@ ccl_device void voronoi_distance_to_edge_3d(float3 coord, float randomness, floa *outDistance = minDistance; } -ccl_device void voronoi_n_sphere_radius_3d(float3 coord, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_3d(float3 coord, + float randomness, + ccl_private float *outRadius) { float3 cellPosition = floor(coord); float3 localPosition = coord - cellPosition; @@ -676,9 +688,9 @@ ccl_device void voronoi_f1_4d(float4 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float4 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float4 *outPosition) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -715,9 +727,9 @@ ccl_device void voronoi_smooth_f1_4d(float4 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float4 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float4 *outPosition) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -756,9 +768,9 @@ ccl_device void voronoi_f2_4d(float4 coord, float exponent, float randomness, NodeVoronoiDistanceMetric metric, - float *outDistance, - float3 *outColor, - float4 *outPosition) + ccl_private float *outDistance, + ccl_private float3 *outColor, + ccl_private float4 *outPosition) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -801,7 +813,9 @@ ccl_device void voronoi_f2_4d(float4 coord, *outPosition = positionF2 + cellPosition; } -ccl_device void voronoi_distance_to_edge_4d(float4 coord, float randomness, float *outDistance) +ccl_device void voronoi_distance_to_edge_4d(float4 coord, + float randomness, + ccl_private float *outDistance) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -850,7 +864,9 @@ ccl_device void voronoi_distance_to_edge_4d(float4 coord, float randomness, floa *outDistance = minDistance; } -ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float *outRadius) +ccl_device void voronoi_n_sphere_radius_4d(float4 coord, + float randomness, + ccl_private float *outRadius) { float4 cellPosition = floor(coord); float4 localPosition = coord - cellPosition; @@ -902,16 +918,17 @@ ccl_device void voronoi_n_sphere_radius_4d(float4 coord, float randomness, float *outRadius = distance(closestPointToClosestPoint, closestPoint) / 2.0f; } -ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint dimensions, - uint feature, - uint metric, - int *offset) +template +ccl_device_noinline int svm_node_tex_voronoi(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint dimensions, + uint feature, + uint metric, + int offset) { - uint4 stack_offsets = read_node(kg, offset); - uint4 defaults = read_node(kg, offset); + uint4 stack_offsets = read_node(kg, &offset); + uint4 defaults = read_node(kg, &offset); uint coord_stack_offset, w_stack_offset, scale_stack_offset, smoothness_stack_offset; uint exponent_stack_offset, randomness_stack_offset, distance_out_stack_offset, @@ -986,7 +1003,7 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, } case 2: { float2 coord_2d = make_float2(coord.x, coord.y); - float2 position_out_2d; + float2 position_out_2d = zero_float2(); switch (voronoi_feature) { case NODE_VORONOI_F1: voronoi_f1_2d(coord_2d, @@ -997,18 +1014,19 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, &color_out, &position_out_2d); break; -#if NODES_FEATURE(NODE_FEATURE_VORONOI_EXTRA) case NODE_VORONOI_SMOOTH_F1: - voronoi_smooth_f1_2d(coord_2d, - smoothness, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_2d); + IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA) + { + voronoi_smooth_f1_2d(coord_2d, + smoothness, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_2d); + } break; -#endif case NODE_VORONOI_F2: voronoi_f2_2d(coord_2d, exponent, @@ -1042,18 +1060,19 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, &color_out, &position_out); break; -#if NODES_FEATURE(NODE_FEATURE_VORONOI_EXTRA) case NODE_VORONOI_SMOOTH_F1: - voronoi_smooth_f1_3d(coord, - smoothness, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out); + IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA) + { + voronoi_smooth_f1_3d(coord, + smoothness, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out); + } break; -#endif case NODE_VORONOI_F2: voronoi_f2_3d(coord, exponent, @@ -1076,54 +1095,55 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, break; } -#if NODES_FEATURE(NODE_FEATURE_VORONOI_EXTRA) case 4: { - float4 coord_4d = make_float4(coord.x, coord.y, coord.z, w); - float4 position_out_4d; - switch (voronoi_feature) { - case NODE_VORONOI_F1: - voronoi_f1_4d(coord_4d, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_4d); - break; - case NODE_VORONOI_SMOOTH_F1: - voronoi_smooth_f1_4d(coord_4d, - smoothness, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_4d); - break; - case NODE_VORONOI_F2: - voronoi_f2_4d(coord_4d, - exponent, - randomness, - voronoi_metric, - &distance_out, - &color_out, - &position_out_4d); - break; - case NODE_VORONOI_DISTANCE_TO_EDGE: - voronoi_distance_to_edge_4d(coord_4d, randomness, &distance_out); - break; - case NODE_VORONOI_N_SPHERE_RADIUS: - voronoi_n_sphere_radius_4d(coord_4d, randomness, &radius_out); - break; - default: - kernel_assert(0); + IF_KERNEL_NODES_FEATURE(VORONOI_EXTRA) + { + float4 coord_4d = make_float4(coord.x, coord.y, coord.z, w); + float4 position_out_4d; + switch (voronoi_feature) { + case NODE_VORONOI_F1: + voronoi_f1_4d(coord_4d, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_4d); + break; + case NODE_VORONOI_SMOOTH_F1: + voronoi_smooth_f1_4d(coord_4d, + smoothness, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_4d); + break; + case NODE_VORONOI_F2: + voronoi_f2_4d(coord_4d, + exponent, + randomness, + voronoi_metric, + &distance_out, + &color_out, + &position_out_4d); + break; + case NODE_VORONOI_DISTANCE_TO_EDGE: + voronoi_distance_to_edge_4d(coord_4d, randomness, &distance_out); + break; + case NODE_VORONOI_N_SPHERE_RADIUS: + voronoi_n_sphere_radius_4d(coord_4d, randomness, &radius_out); + break; + default: + kernel_assert(0); + } + position_out_4d = safe_divide_float4_float(position_out_4d, scale); + position_out = make_float3(position_out_4d.x, position_out_4d.y, position_out_4d.z); + w_out = position_out_4d.w; } - position_out_4d = safe_divide_float4_float(position_out_4d, scale); - position_out = make_float3(position_out_4d.x, position_out_4d.y, position_out_4d.z); - w_out = position_out_4d.w; break; } -#endif default: kernel_assert(0); } @@ -1138,6 +1158,7 @@ ccl_device void svm_node_tex_voronoi(KernelGlobals *kg, stack_store_float(stack, w_out_stack_offset, w_out); if (stack_valid(radius_out_stack_offset)) stack_store_float(stack, radius_out_stack_offset, radius_out); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_voxel.h b/intern/cycles/kernel/svm/voxel.h similarity index 83% rename from intern/cycles/kernel/svm/svm_voxel.h rename to intern/cycles/kernel/svm/voxel.h index 4bc14f82382..43947fbc54f 100644 --- a/intern/cycles/kernel/svm/svm_voxel.h +++ b/intern/cycles/kernel/svm/voxel.h @@ -14,13 +14,15 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* TODO(sergey): Think of making it more generic volume-type attribute * sampler. */ -ccl_device void svm_node_tex_voxel( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_voxel( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { uint co_offset, density_out_offset, color_out_offset, space; svm_unpack_node_uchar4(node.z, &co_offset, &density_out_offset, &color_out_offset, &space); @@ -33,9 +35,9 @@ ccl_device void svm_node_tex_voxel( else { kernel_assert(space == NODE_TEX_VOXEL_SPACE_WORLD); Transform tfm; - tfm.x = read_node_float(kg, offset); - tfm.y = read_node_float(kg, offset); - tfm.z = read_node_float(kg, offset); + tfm.x = read_node_float(kg, &offset); + tfm.y = read_node_float(kg, &offset); + tfm.z = read_node_float(kg, &offset); co = transform_point(&tfm, co); } @@ -47,6 +49,7 @@ ccl_device void svm_node_tex_voxel( stack_store_float(stack, density_out_offset, r.w); if (stack_valid(color_out_offset)) stack_store_float3(stack, color_out_offset, make_float3(r.x, r.y, r.z)); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_wave.h b/intern/cycles/kernel/svm/wave.h similarity index 94% rename from intern/cycles/kernel/svm/svm_wave.h rename to intern/cycles/kernel/svm/wave.h index c4763475b47..40e71b9d5df 100644 --- a/intern/cycles/kernel/svm/svm_wave.h +++ b/intern/cycles/kernel/svm/wave.h @@ -14,6 +14,8 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Wave */ @@ -82,11 +84,11 @@ ccl_device_noinline_cpu float svm_wave(NodeWaveType type, } } -ccl_device void svm_node_tex_wave( - KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node, int *offset) +ccl_device_noinline int svm_node_tex_wave( + KernelGlobals kg, ccl_private ShaderData *sd, ccl_private float *stack, uint4 node, int offset) { - uint4 node2 = read_node(kg, offset); - uint4 node3 = read_node(kg, offset); + uint4 node2 = read_node(kg, &offset); + uint4 node3 = read_node(kg, &offset); /* RNA properties */ uint type_offset, bands_dir_offset, rings_dir_offset, profile_offset; @@ -125,6 +127,7 @@ ccl_device void svm_node_tex_wave( stack_store_float(stack, fac_offset, f); if (stack_valid(color_offset)) stack_store_float3(stack, color_offset, make_float3(f, f, f)); + return offset; } CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/wavelength.h b/intern/cycles/kernel/svm/wavelength.h new file mode 100644 index 00000000000..28fd172abc7 --- /dev/null +++ b/intern/cycles/kernel/svm/wavelength.h @@ -0,0 +1,103 @@ +/* + * Adapted from Open Shading Language with this license: + * + * Copyright (c) 2009-2010 Sony Pictures Imageworks Inc., et al. + * All Rights Reserved. + * + * Modifications Copyright 2013, Blender Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Sony Pictures Imageworks nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Wavelength to RGB */ + +ccl_device_noinline void svm_node_wavelength(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint wavelength, + uint color_out) +{ + // CIE colour matching functions xBar, yBar, and zBar for + // wavelengths from 380 through 780 nanometers, every 5 + // nanometers. For a wavelength lambda in this range: + // cie_colour_match[(lambda - 380) / 5][0] = xBar + // cie_colour_match[(lambda - 380) / 5][1] = yBar + // cie_colour_match[(lambda - 380) / 5][2] = zBar + const float cie_colour_match[81][3] = { + {0.0014f, 0.0000f, 0.0065f}, {0.0022f, 0.0001f, 0.0105f}, {0.0042f, 0.0001f, 0.0201f}, + {0.0076f, 0.0002f, 0.0362f}, {0.0143f, 0.0004f, 0.0679f}, {0.0232f, 0.0006f, 0.1102f}, + {0.0435f, 0.0012f, 0.2074f}, {0.0776f, 0.0022f, 0.3713f}, {0.1344f, 0.0040f, 0.6456f}, + {0.2148f, 0.0073f, 1.0391f}, {0.2839f, 0.0116f, 1.3856f}, {0.3285f, 0.0168f, 1.6230f}, + {0.3483f, 0.0230f, 1.7471f}, {0.3481f, 0.0298f, 1.7826f}, {0.3362f, 0.0380f, 1.7721f}, + {0.3187f, 0.0480f, 1.7441f}, {0.2908f, 0.0600f, 1.6692f}, {0.2511f, 0.0739f, 1.5281f}, + {0.1954f, 0.0910f, 1.2876f}, {0.1421f, 0.1126f, 1.0419f}, {0.0956f, 0.1390f, 0.8130f}, + {0.0580f, 0.1693f, 0.6162f}, {0.0320f, 0.2080f, 0.4652f}, {0.0147f, 0.2586f, 0.3533f}, + {0.0049f, 0.3230f, 0.2720f}, {0.0024f, 0.4073f, 0.2123f}, {0.0093f, 0.5030f, 0.1582f}, + {0.0291f, 0.6082f, 0.1117f}, {0.0633f, 0.7100f, 0.0782f}, {0.1096f, 0.7932f, 0.0573f}, + {0.1655f, 0.8620f, 0.0422f}, {0.2257f, 0.9149f, 0.0298f}, {0.2904f, 0.9540f, 0.0203f}, + {0.3597f, 0.9803f, 0.0134f}, {0.4334f, 0.9950f, 0.0087f}, {0.5121f, 1.0000f, 0.0057f}, + {0.5945f, 0.9950f, 0.0039f}, {0.6784f, 0.9786f, 0.0027f}, {0.7621f, 0.9520f, 0.0021f}, + {0.8425f, 0.9154f, 0.0018f}, {0.9163f, 0.8700f, 0.0017f}, {0.9786f, 0.8163f, 0.0014f}, + {1.0263f, 0.7570f, 0.0011f}, {1.0567f, 0.6949f, 0.0010f}, {1.0622f, 0.6310f, 0.0008f}, + {1.0456f, 0.5668f, 0.0006f}, {1.0026f, 0.5030f, 0.0003f}, {0.9384f, 0.4412f, 0.0002f}, + {0.8544f, 0.3810f, 0.0002f}, {0.7514f, 0.3210f, 0.0001f}, {0.6424f, 0.2650f, 0.0000f}, + {0.5419f, 0.2170f, 0.0000f}, {0.4479f, 0.1750f, 0.0000f}, {0.3608f, 0.1382f, 0.0000f}, + {0.2835f, 0.1070f, 0.0000f}, {0.2187f, 0.0816f, 0.0000f}, {0.1649f, 0.0610f, 0.0000f}, + {0.1212f, 0.0446f, 0.0000f}, {0.0874f, 0.0320f, 0.0000f}, {0.0636f, 0.0232f, 0.0000f}, + {0.0468f, 0.0170f, 0.0000f}, {0.0329f, 0.0119f, 0.0000f}, {0.0227f, 0.0082f, 0.0000f}, + {0.0158f, 0.0057f, 0.0000f}, {0.0114f, 0.0041f, 0.0000f}, {0.0081f, 0.0029f, 0.0000f}, + {0.0058f, 0.0021f, 0.0000f}, {0.0041f, 0.0015f, 0.0000f}, {0.0029f, 0.0010f, 0.0000f}, + {0.0020f, 0.0007f, 0.0000f}, {0.0014f, 0.0005f, 0.0000f}, {0.0010f, 0.0004f, 0.0000f}, + {0.0007f, 0.0002f, 0.0000f}, {0.0005f, 0.0002f, 0.0000f}, {0.0003f, 0.0001f, 0.0000f}, + {0.0002f, 0.0001f, 0.0000f}, {0.0002f, 0.0001f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, + {0.0001f, 0.0000f, 0.0000f}, {0.0001f, 0.0000f, 0.0000f}, {0.0000f, 0.0000f, 0.0000f}}; + + float lambda_nm = stack_load_float(stack, wavelength); + float ii = (lambda_nm - 380.0f) * (1.0f / 5.0f); // scaled 0..80 + int i = float_to_int(ii); + float3 color; + + if (i < 0 || i >= 80) { + color = make_float3(0.0f, 0.0f, 0.0f); + } + else { + ii -= i; + ccl_constant float *c = cie_colour_match[i]; + color = interp(make_float3(c[0], c[1], c[2]), make_float3(c[3], c[4], c[5]), ii); + } + + color = xyz_to_rgb(kg, color); + color *= 1.0f / 2.52f; // Empirical scale from lg to make all comps <= 1 + + /* Clamp to zero if values are smaller */ + color = max(color, make_float3(0.0f, 0.0f, 0.0f)); + + stack_store_float3(stack, color_out, color); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/svm/svm_white_noise.h b/intern/cycles/kernel/svm/white_noise.h similarity index 82% rename from intern/cycles/kernel/svm/svm_white_noise.h rename to intern/cycles/kernel/svm/white_noise.h index b30d85acaec..d275a3f7068 100644 --- a/intern/cycles/kernel/svm/svm_white_noise.h +++ b/intern/cycles/kernel/svm/white_noise.h @@ -14,15 +14,16 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN -ccl_device void svm_node_tex_white_noise(KernelGlobals *kg, - ShaderData *sd, - float *stack, - uint dimensions, - uint inputs_stack_offsets, - uint ouptuts_stack_offsets, - int *offset) +ccl_device_noinline void svm_node_tex_white_noise(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint dimensions, + uint inputs_stack_offsets, + uint ouptuts_stack_offsets) { uint vector_stack_offset, w_stack_offset, value_stack_offset, color_stack_offset; svm_unpack_node_uchar2(inputs_stack_offsets, &vector_stack_offset, &w_stack_offset); diff --git a/intern/cycles/kernel/svm/svm_wireframe.h b/intern/cycles/kernel/svm/wireframe.h similarity index 87% rename from intern/cycles/kernel/svm/svm_wireframe.h rename to intern/cycles/kernel/svm/wireframe.h index 49158bd86d5..530a9601bce 100644 --- a/intern/cycles/kernel/svm/svm_wireframe.h +++ b/intern/cycles/kernel/svm/wireframe.h @@ -30,12 +30,17 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#pragma once + CCL_NAMESPACE_BEGIN /* Wireframe Node */ -ccl_device_inline float wireframe( - KernelGlobals *kg, ShaderData *sd, float size, int pixel_size, float3 *P) +ccl_device_inline float wireframe(KernelGlobals kg, + ccl_private ShaderData *sd, + float size, + int pixel_size, + ccl_private float3 *P) { #ifdef __HAIR__ if (sd->prim != PRIM_NONE && sd->type & PRIMITIVE_ALL_TRIANGLE) @@ -88,7 +93,10 @@ ccl_device_inline float wireframe( return 0.0f; } -ccl_device void svm_node_wireframe(KernelGlobals *kg, ShaderData *sd, float *stack, uint4 node) +ccl_device_noinline void svm_node_wireframe(KernelGlobals kg, + ccl_private ShaderData *sd, + ccl_private float *stack, + uint4 node) { uint in_size = node.y; uint out_fac = node.z; @@ -100,18 +108,7 @@ ccl_device void svm_node_wireframe(KernelGlobals *kg, ShaderData *sd, float *sta int pixel_size = (int)use_pixel_size; /* Calculate wireframe */ -#ifdef __SPLIT_KERNEL__ - /* TODO(sergey): This is because sd is actually a global space, - * which makes it difficult to re-use same wireframe() function. - * - * With OpenCL 2.0 it's possible to avoid this change, but for until - * then we'll be living with such an exception. - */ - float3 P = sd->P; - float f = wireframe(kg, sd, size, pixel_size, &P); -#else float f = wireframe(kg, sd, size, pixel_size, &sd->P); -#endif /* TODO(sergey): Think of faster way to calculate derivatives. */ if (bump_offset == NODE_BUMP_OFFSET_DX) { diff --git a/intern/cycles/kernel/kernel_textures.h b/intern/cycles/kernel/textures.h similarity index 91% rename from intern/cycles/kernel/kernel_textures.h rename to intern/cycles/kernel/textures.h index c8e01677d09..464ecb183cb 100644 --- a/intern/cycles/kernel/kernel_textures.h +++ b/intern/cycles/kernel/textures.h @@ -18,11 +18,9 @@ # define KERNEL_TEX(type, name) #endif -/* bvh */ +/* BVH2, not used for OptiX or Embree. */ KERNEL_TEX(float4, __bvh_nodes) KERNEL_TEX(float4, __bvh_leaf_nodes) -KERNEL_TEX(float4, __prim_tri_verts) -KERNEL_TEX(uint, __prim_tri_index) KERNEL_TEX(uint, __prim_type) KERNEL_TEX(uint, __prim_visibility) KERNEL_TEX(uint, __prim_index) @@ -46,10 +44,12 @@ KERNEL_TEX(float4, __tri_vnormal) KERNEL_TEX(uint4, __tri_vindex) KERNEL_TEX(uint, __tri_patch) KERNEL_TEX(float2, __tri_patch_uv) +KERNEL_TEX(float4, __tri_verts) /* curves */ -KERNEL_TEX(float4, __curves) +KERNEL_TEX(KernelCurve, __curves) KERNEL_TEX(float4, __curve_keys) +KERNEL_TEX(KernelCurveSegment, __curve_segments) /* patches */ KERNEL_TEX(uint, __patches) @@ -78,7 +78,7 @@ KERNEL_TEX(KernelShader, __shaders) KERNEL_TEX(float, __lookup_table) /* sobol */ -KERNEL_TEX(uint, __sample_pattern_lut) +KERNEL_TEX(float, __sample_pattern_lut) /* image textures */ KERNEL_TEX(TextureInfo, __texture_info) diff --git a/intern/cycles/kernel/kernel_types.h b/intern/cycles/kernel/types.h similarity index 60% rename from intern/cycles/kernel/kernel_types.h rename to intern/cycles/kernel/types.h index 7cbe18acf28..2827139d511 100644 --- a/intern/cycles/kernel/kernel_types.h +++ b/intern/cycles/kernel/types.h @@ -14,8 +14,7 @@ * limitations under the License. */ -#ifndef __KERNEL_TYPES_H__ -#define __KERNEL_TYPES_H__ +#pragma once #if !defined(__KERNEL_GPU__) && defined(WITH_EMBREE) # include @@ -23,9 +22,15 @@ # define __EMBREE__ #endif -#include "kernel/kernel_math.h" -#include "kernel/svm/svm_types.h" -#include "util/util_static_assert.h" +#include "util/math.h" +#include "util/math_fast.h" +#include "util/math_intersect.h" +#include "util/projection.h" +#include "util/static_assert.h" +#include "util/texture.h" +#include "util/transform.h" + +#include "kernel/svm/types.h" #ifndef __KERNEL_GPU__ # define __KERNEL_CPU__ @@ -60,26 +65,15 @@ CCL_NAMESPACE_BEGIN #define PRIM_NONE (~0) #define LAMP_NONE (~0) #define ID_NONE (0.0f) +#define PASS_UNUSED (~0) -#define VOLUME_STACK_SIZE 32 +#define INTEGRATOR_SHADOW_ISECT_SIZE_CPU 1024U +#define INTEGRATOR_SHADOW_ISECT_SIZE_GPU 4U -/* Split kernel constants */ -#define WORK_POOL_SIZE_GPU 64 -#define WORK_POOL_SIZE_CPU 1 -#ifdef __KERNEL_GPU__ -# define WORK_POOL_SIZE WORK_POOL_SIZE_GPU +#ifdef __KERNEL_CPU__ +# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_CPU #else -# define WORK_POOL_SIZE WORK_POOL_SIZE_CPU -#endif - -#define SHADER_SORT_BLOCK_SIZE 2048 - -#ifdef __KERNEL_OPENCL__ -# define SHADER_SORT_LOCAL_SIZE 64 -#elif defined(__KERNEL_CUDA__) -# define SHADER_SORT_LOCAL_SIZE 32 -#else -# define SHADER_SORT_LOCAL_SIZE 1 +# define INTEGRATOR_SHADOW_ISECT_SIZE INTEGRATOR_SHADOW_ISECT_SIZE_GPU #endif /* Kernel features */ @@ -93,7 +87,7 @@ CCL_NAMESPACE_BEGIN #define __INTERSECTION_REFINE__ #define __CLAMP_SAMPLE__ #define __PATCH_EVAL__ -#define __SHADOW_TRICKS__ +#define __SHADOW_CATCHER__ #define __DENOISING_FEATURES__ #define __SHADER_RAYTRACE__ #define __AO__ @@ -102,7 +96,6 @@ CCL_NAMESPACE_BEGIN #define __SVM__ #define __EMISSION__ #define __HOLDOUT__ -#define __MULTI_CLOSURE__ #define __TRANSPARENT_SHADOWS__ #define __BACKGROUND_MIS__ #define __LAMP_MIS__ @@ -112,7 +105,6 @@ CCL_NAMESPACE_BEGIN #define __PRINCIPLED__ #define __SUBSURFACE__ #define __VOLUME__ -#define __VOLUME_SCATTER__ #define __CMJ__ #define __SHADOW_RECORD_ALL__ #define __BRANCHED_PATH__ @@ -122,106 +114,60 @@ CCL_NAMESPACE_BEGIN # ifdef WITH_OSL # define __OSL__ # endif -# define __VOLUME_DECOUPLED__ # define __VOLUME_RECORD_ALL__ #endif /* __KERNEL_CPU__ */ -#ifdef __KERNEL_CUDA__ -# ifdef __SPLIT_KERNEL__ -# undef __BRANCHED_PATH__ -# endif -#endif /* __KERNEL_CUDA__ */ - #ifdef __KERNEL_OPTIX__ # undef __BAKING__ -# undef __BRANCHED_PATH__ #endif /* __KERNEL_OPTIX__ */ -#ifdef __KERNEL_OPENCL__ -#endif /* __KERNEL_OPENCL__ */ - /* Scene-based selective features compilation. */ -#ifdef __NO_CAMERA_MOTION__ -# undef __CAMERA_MOTION__ -#endif -#ifdef __NO_OBJECT_MOTION__ -# undef __OBJECT_MOTION__ -#endif -#ifdef __NO_HAIR__ -# undef __HAIR__ -#endif -#ifdef __NO_VOLUME__ -# undef __VOLUME__ -# undef __VOLUME_SCATTER__ -#endif -#ifdef __NO_SUBSURFACE__ -# undef __SUBSURFACE__ -#endif -#ifdef __NO_BAKING__ -# undef __BAKING__ -#endif -#ifdef __NO_BRANCHED_PATH__ -# undef __BRANCHED_PATH__ -#endif -#ifdef __NO_PATCH_EVAL__ -# undef __PATCH_EVAL__ -#endif -#ifdef __NO_TRANSPARENT__ -# undef __TRANSPARENT_SHADOWS__ -#endif -#ifdef __NO_SHADOW_TRICKS__ -# undef __SHADOW_TRICKS__ -#endif -#ifdef __NO_PRINCIPLED__ -# undef __PRINCIPLED__ -#endif -#ifdef __NO_DENOISING__ -# undef __DENOISING_FEATURES__ -#endif -#ifdef __NO_SHADER_RAYTRACE__ -# undef __SHADER_RAYTRACE__ +#ifdef __KERNEL_FEATURES__ +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_CAMERA_MOTION) +# undef __CAMERA_MOTION__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_OBJECT_MOTION) +# undef __OBJECT_MOTION__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_HAIR) +# undef __HAIR__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_VOLUME) +# undef __VOLUME__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_SUBSURFACE) +# undef __SUBSURFACE__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_BAKING) +# undef __BAKING__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_PATCH_EVALUATION) +# undef __PATCH_EVAL__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_TRANSPARENT) +# undef __TRANSPARENT_SHADOWS__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_SHADOW_CATCHER) +# undef __SHADOW_CATCHER__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_PRINCIPLED) +# undef __PRINCIPLED__ +# endif +# if !(__KERNEL_FEATURES & KERNEL_FEATURE_DENOISING) +# undef __DENOISING_FEATURES__ +# endif #endif #ifdef WITH_CYCLES_DEBUG_NAN # define __KERNEL_DEBUG_NAN__ #endif +/* Features that enable others */ + #if defined(__SUBSURFACE__) || defined(__SHADER_RAYTRACE__) # define __BVH_LOCAL__ #endif -/* Shader Evaluation */ - -typedef enum ShaderEvalType { - SHADER_EVAL_DISPLACE, - SHADER_EVAL_BACKGROUND, - /* bake types */ - SHADER_EVAL_BAKE, /* no real shade, it's used in the code to - * differentiate the type of shader eval from the above - */ - /* data passes */ - SHADER_EVAL_NORMAL, - SHADER_EVAL_UV, - SHADER_EVAL_ROUGHNESS, - SHADER_EVAL_DIFFUSE_COLOR, - SHADER_EVAL_GLOSSY_COLOR, - SHADER_EVAL_TRANSMISSION_COLOR, - SHADER_EVAL_EMISSION, - SHADER_EVAL_AOV_COLOR, - SHADER_EVAL_AOV_VALUE, - - /* light passes */ - SHADER_EVAL_AO, - SHADER_EVAL_COMBINED, - SHADER_EVAL_SHADOW, - SHADER_EVAL_DIFFUSE, - SHADER_EVAL_GLOSSY, - SHADER_EVAL_TRANSMISSION, - - /* extra */ - SHADER_EVAL_ENVIRONMENT, -} ShaderEvalType; - /* Path Tracing * note we need to keep the u/v pairs at even values */ @@ -252,74 +198,132 @@ enum PathTraceDimension { enum SamplingPattern { SAMPLING_PATTERN_SOBOL = 0, - SAMPLING_PATTERN_CMJ = 1, - SAMPLING_PATTERN_PMJ = 2, + SAMPLING_PATTERN_PMJ = 1, SAMPLING_NUM_PATTERNS, }; -/* these flags values correspond to raytypes in osl.cpp, so keep them in sync! */ +/* These flags values correspond to `raytypes` in `osl.cpp`, so keep them in sync! */ enum PathRayFlag { - /* Ray visibility. */ - PATH_RAY_CAMERA = (1 << 0), - PATH_RAY_REFLECT = (1 << 1), - PATH_RAY_TRANSMIT = (1 << 2), - PATH_RAY_DIFFUSE = (1 << 3), - PATH_RAY_GLOSSY = (1 << 4), - PATH_RAY_SINGULAR = (1 << 5), - PATH_RAY_TRANSPARENT = (1 << 6), + /* -------------------------------------------------------------------- + * Ray visibility. + * + * NOTE: Recalculated after a surface bounce. + */ + + PATH_RAY_CAMERA = (1U << 0U), + PATH_RAY_REFLECT = (1U << 1U), + PATH_RAY_TRANSMIT = (1U << 2U), + PATH_RAY_DIFFUSE = (1U << 3U), + PATH_RAY_GLOSSY = (1U << 4U), + PATH_RAY_SINGULAR = (1U << 5U), + PATH_RAY_TRANSPARENT = (1U << 6U), + PATH_RAY_VOLUME_SCATTER = (1U << 7U), /* Shadow ray visibility. */ - PATH_RAY_SHADOW_OPAQUE_NON_CATCHER = (1 << 7), - PATH_RAY_SHADOW_OPAQUE_CATCHER = (1 << 8), - PATH_RAY_SHADOW_OPAQUE = (PATH_RAY_SHADOW_OPAQUE_NON_CATCHER | PATH_RAY_SHADOW_OPAQUE_CATCHER), - PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER = (1 << 9), - PATH_RAY_SHADOW_TRANSPARENT_CATCHER = (1 << 10), - PATH_RAY_SHADOW_TRANSPARENT = (PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER | - PATH_RAY_SHADOW_TRANSPARENT_CATCHER), - PATH_RAY_SHADOW_NON_CATCHER = (PATH_RAY_SHADOW_OPAQUE_NON_CATCHER | - PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER), + PATH_RAY_SHADOW_OPAQUE = (1U << 8U), + PATH_RAY_SHADOW_TRANSPARENT = (1U << 9U), PATH_RAY_SHADOW = (PATH_RAY_SHADOW_OPAQUE | PATH_RAY_SHADOW_TRANSPARENT), - /* Unused, free to reuse. */ - PATH_RAY_UNUSED = (1 << 11), + /* Special flag to tag unaligned BVH nodes. + * Only set and used in BVH nodes to distinguish how to interpret bounding box information stored + * in the node (either it should be intersected as AABB or as OBBU). */ + PATH_RAY_NODE_UNALIGNED = (1U << 10U), - /* Ray visibility for volume scattering. */ - PATH_RAY_VOLUME_SCATTER = (1 << 12), + /* Subset of flags used for ray visibility for intersection. + * + * NOTE: SHADOW_CATCHER macros below assume there are no more than + * 16 visibility bits. */ + PATH_RAY_ALL_VISIBILITY = ((1U << 11U) - 1U), - /* Special flag to tag unaligned BVH nodes. */ - PATH_RAY_NODE_UNALIGNED = (1 << 13), - - PATH_RAY_ALL_VISIBILITY = ((1 << 14) - 1), + /* -------------------------------------------------------------------- + * Path flags. + */ /* Don't apply multiple importance sampling weights to emission from * lamp or surface hits, because they were not direct light sampled. */ - PATH_RAY_MIS_SKIP = (1 << 14), + PATH_RAY_MIS_SKIP = (1U << 11U), + /* Diffuse bounce earlier in the path, skip SSS to improve performance * and avoid branching twice with disk sampling SSS. */ - PATH_RAY_DIFFUSE_ANCESTOR = (1 << 15), + PATH_RAY_DIFFUSE_ANCESTOR = (1U << 12U), + /* Single pass has been written. */ - PATH_RAY_SINGLE_PASS_DONE = (1 << 16), - /* Ray is behind a shadow catcher. */ - PATH_RAY_SHADOW_CATCHER = (1 << 17), - /* Store shadow data for shadow catcher or denoising. */ - PATH_RAY_STORE_SHADOW_INFO = (1 << 18), + PATH_RAY_SINGLE_PASS_DONE = (1U << 13U), + /* Zero background alpha, for camera or transparent glass rays. */ - PATH_RAY_TRANSPARENT_BACKGROUND = (1 << 19), + PATH_RAY_TRANSPARENT_BACKGROUND = (1U << 14U), + /* Terminate ray immediately at next bounce. */ - PATH_RAY_TERMINATE_IMMEDIATE = (1 << 20), + PATH_RAY_TERMINATE_ON_NEXT_SURFACE = (1U << 15U), + PATH_RAY_TERMINATE_IN_NEXT_VOLUME = (1U << 16U), + /* Ray is to be terminated, but continue with transparent bounces and * emission as long as we encounter them. This is required to make the * MIS between direct and indirect light rays match, as shadow rays go * through transparent surfaces to reach emission too. */ - PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1 << 21), + PATH_RAY_TERMINATE_AFTER_TRANSPARENT = (1U << 17U), + + /* Terminate ray immediately after volume shading. */ + PATH_RAY_TERMINATE_AFTER_VOLUME = (1U << 18U), + /* Ray is to be terminated. */ - PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_IMMEDIATE | PATH_RAY_TERMINATE_AFTER_TRANSPARENT), + PATH_RAY_TERMINATE = (PATH_RAY_TERMINATE_ON_NEXT_SURFACE | PATH_RAY_TERMINATE_IN_NEXT_VOLUME | + PATH_RAY_TERMINATE_AFTER_TRANSPARENT | PATH_RAY_TERMINATE_AFTER_VOLUME), + /* Path and shader is being evaluated for direct lighting emission. */ - PATH_RAY_EMISSION = (1 << 22) + PATH_RAY_EMISSION = (1U << 19U), + + /* Perform subsurface scattering. */ + PATH_RAY_SUBSURFACE_RANDOM_WALK = (1U << 20U), + PATH_RAY_SUBSURFACE_DISK = (1U << 21U), + PATH_RAY_SUBSURFACE_USE_FRESNEL = (1U << 22U), + PATH_RAY_SUBSURFACE = (PATH_RAY_SUBSURFACE_RANDOM_WALK | PATH_RAY_SUBSURFACE_DISK | + PATH_RAY_SUBSURFACE_USE_FRESNEL), + + /* Contribute to denoising features. */ + PATH_RAY_DENOISING_FEATURES = (1U << 23U), + + /* Render pass categories. */ + PATH_RAY_REFLECT_PASS = (1U << 24U), + PATH_RAY_TRANSMISSION_PASS = (1U << 25U), + PATH_RAY_VOLUME_PASS = (1U << 26U), + PATH_RAY_ANY_PASS = (PATH_RAY_REFLECT_PASS | PATH_RAY_TRANSMISSION_PASS | PATH_RAY_VOLUME_PASS), + + /* Shadow ray is for a light or surface, or AO. */ + PATH_RAY_SHADOW_FOR_LIGHT = (1U << 27U), + PATH_RAY_SHADOW_FOR_AO = (1U << 28U), + + /* A shadow catcher object was hit and the path was split into two. */ + PATH_RAY_SHADOW_CATCHER_HIT = (1U << 29U), + + /* A shadow catcher object was hit and this path traces only shadow catchers, writing them into + * their dedicated pass for later division. + * + * NOTE: Is not covered with `PATH_RAY_ANY_PASS` because shadow catcher does special handling + * which is separate from the light passes. */ + PATH_RAY_SHADOW_CATCHER_PASS = (1U << 30U), + + /* Path is evaluating background for an approximate shadow catcher with non-transparent film. */ + PATH_RAY_SHADOW_CATCHER_BACKGROUND = (1U << 31U), }; +/* Configure ray visibility bits for rays and objects respectively, + * to make shadow catchers work. + * + * On shadow catcher paths we want to ignore any intersections with non-catchers, + * whereas on regular paths we want to intersect all objects. */ + +#define SHADOW_CATCHER_VISIBILITY_SHIFT(visibility) ((visibility) << 16) + +#define SHADOW_CATCHER_PATH_VISIBILITY(path_flag, visibility) \ + (((path_flag)&PATH_RAY_SHADOW_CATCHER_PASS) ? SHADOW_CATCHER_VISIBILITY_SHIFT(visibility) : \ + (visibility)) + +#define SHADOW_CATCHER_OBJECT_VISIBILITY(is_shadow_catcher, visibility) \ + (((is_shadow_catcher) ? SHADOW_CATCHER_VISIBILITY_SHIFT(visibility) : 0) | (visibility)) + /* Closure Label */ typedef enum ClosureLabel { @@ -332,6 +336,7 @@ typedef enum ClosureLabel { LABEL_TRANSPARENT = 32, LABEL_VOLUME_SCATTER = 64, LABEL_TRANSMIT_TRANSPARENT = 128, + LABEL_SUBSURFACE_SCATTER = 256, } ClosureLabel; /* Render Passes */ @@ -339,53 +344,75 @@ typedef enum ClosureLabel { #define PASS_NAME_JOIN(a, b) a##_##b #define PASSMASK(pass) (1 << ((PASS_NAME_JOIN(PASS, pass)) % 32)) -#define PASSMASK_COMPONENT(comp) \ - (PASSMASK(PASS_NAME_JOIN(comp, DIRECT)) | PASSMASK(PASS_NAME_JOIN(comp, INDIRECT)) | \ - PASSMASK(PASS_NAME_JOIN(comp, COLOR))) - +// NOTE: Keep in sync with `Pass::get_type_enum()`. typedef enum PassType { PASS_NONE = 0, - /* Main passes */ + /* Light Passes */ PASS_COMBINED = 1, - PASS_DEPTH, + PASS_EMISSION, + PASS_BACKGROUND, + PASS_AO, + PASS_SHADOW, + PASS_DIFFUSE, + PASS_DIFFUSE_DIRECT, + PASS_DIFFUSE_INDIRECT, + PASS_GLOSSY, + PASS_GLOSSY_DIRECT, + PASS_GLOSSY_INDIRECT, + PASS_TRANSMISSION, + PASS_TRANSMISSION_DIRECT, + PASS_TRANSMISSION_INDIRECT, + PASS_VOLUME, + PASS_VOLUME_DIRECT, + PASS_VOLUME_INDIRECT, + PASS_CATEGORY_LIGHT_END = 31, + + /* Data passes */ + PASS_DEPTH = 32, + PASS_POSITION, PASS_NORMAL, + PASS_ROUGHNESS, PASS_UV, PASS_OBJECT_ID, PASS_MATERIAL_ID, PASS_MOTION, PASS_MOTION_WEIGHT, - PASS_RENDER_TIME, PASS_CRYPTOMATTE, PASS_AOV_COLOR, PASS_AOV_VALUE, PASS_ADAPTIVE_AUX_BUFFER, PASS_SAMPLE_COUNT, - PASS_CATEGORY_MAIN_END = 31, - - PASS_MIST = 32, - PASS_EMISSION, - PASS_BACKGROUND, - PASS_AO, - PASS_SHADOW, - PASS_LIGHT, /* no real pass, used to force use_light_pass */ - PASS_DIFFUSE_DIRECT, - PASS_DIFFUSE_INDIRECT, PASS_DIFFUSE_COLOR, - PASS_GLOSSY_DIRECT, - PASS_GLOSSY_INDIRECT, PASS_GLOSSY_COLOR, - PASS_TRANSMISSION_DIRECT, - PASS_TRANSMISSION_INDIRECT, PASS_TRANSMISSION_COLOR, - PASS_VOLUME_DIRECT = 50, - PASS_VOLUME_INDIRECT, /* No Scatter color since it's tricky to define what it would even mean. */ - PASS_CATEGORY_LIGHT_END = 63, + PASS_MIST, + PASS_DENOISING_NORMAL, + PASS_DENOISING_ALBEDO, + PASS_DENOISING_DEPTH, + + /* PASS_SHADOW_CATCHER accumulates contribution of shadow catcher object which is not affected by + * any other object. The pass accessor will divide the combined pass by the shadow catcher. The + * result of this division is then to be multiplied with the backdrop. The alpha channel of this + * pass contains number of samples which contributed to the color components of the pass. + * + * PASS_SHADOW_CATCHER_SAMPLE_COUNT contains number of samples for which the path split + * happened. + * + * PASS_SHADOW_CATCHER_MATTE contains pass which contains non-catcher objects. This pass is to be + * alpha-overed onto the backdrop (after multiplication). */ + PASS_SHADOW_CATCHER, + PASS_SHADOW_CATCHER_SAMPLE_COUNT, + PASS_SHADOW_CATCHER_MATTE, + + PASS_CATEGORY_DATA_END = 63, PASS_BAKE_PRIMITIVE, PASS_BAKE_DIFFERENTIAL, - PASS_CATEGORY_BAKE_END = 95 + PASS_CATEGORY_BAKE_END = 95, + + PASS_NUM, } PassType; #define PASS_ANY (~0) @@ -398,158 +425,9 @@ typedef enum CryptomatteType { CRYPT_ACCURATE = (1 << 3), } CryptomatteType; -typedef enum DenoisingPassOffsets { - DENOISING_PASS_NORMAL = 0, - DENOISING_PASS_NORMAL_VAR = 3, - DENOISING_PASS_ALBEDO = 6, - DENOISING_PASS_ALBEDO_VAR = 9, - DENOISING_PASS_DEPTH = 12, - DENOISING_PASS_DEPTH_VAR = 13, - DENOISING_PASS_SHADOW_A = 14, - DENOISING_PASS_SHADOW_B = 17, - DENOISING_PASS_COLOR = 20, - DENOISING_PASS_COLOR_VAR = 23, - DENOISING_PASS_CLEAN = 26, - - DENOISING_PASS_PREFILTERED_DEPTH = 0, - DENOISING_PASS_PREFILTERED_NORMAL = 1, - DENOISING_PASS_PREFILTERED_SHADOWING = 4, - DENOISING_PASS_PREFILTERED_ALBEDO = 5, - DENOISING_PASS_PREFILTERED_COLOR = 8, - DENOISING_PASS_PREFILTERED_VARIANCE = 11, - DENOISING_PASS_PREFILTERED_INTENSITY = 14, - - DENOISING_PASS_SIZE_BASE = 26, - DENOISING_PASS_SIZE_CLEAN = 3, - DENOISING_PASS_SIZE_PREFILTERED = 15, -} DenoisingPassOffsets; - -typedef enum eBakePassFilter { - BAKE_FILTER_NONE = 0, - BAKE_FILTER_DIRECT = (1 << 0), - BAKE_FILTER_INDIRECT = (1 << 1), - BAKE_FILTER_COLOR = (1 << 2), - BAKE_FILTER_DIFFUSE = (1 << 3), - BAKE_FILTER_GLOSSY = (1 << 4), - BAKE_FILTER_TRANSMISSION = (1 << 5), - BAKE_FILTER_EMISSION = (1 << 6), - BAKE_FILTER_AO = (1 << 7), -} eBakePassFilter; - -typedef enum BakePassFilterCombos { - BAKE_FILTER_COMBINED = (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT | BAKE_FILTER_DIFFUSE | - BAKE_FILTER_GLOSSY | BAKE_FILTER_TRANSMISSION | BAKE_FILTER_EMISSION | - BAKE_FILTER_AO), - BAKE_FILTER_DIFFUSE_DIRECT = (BAKE_FILTER_DIRECT | BAKE_FILTER_DIFFUSE), - BAKE_FILTER_GLOSSY_DIRECT = (BAKE_FILTER_DIRECT | BAKE_FILTER_GLOSSY), - BAKE_FILTER_TRANSMISSION_DIRECT = (BAKE_FILTER_DIRECT | BAKE_FILTER_TRANSMISSION), - BAKE_FILTER_DIFFUSE_INDIRECT = (BAKE_FILTER_INDIRECT | BAKE_FILTER_DIFFUSE), - BAKE_FILTER_GLOSSY_INDIRECT = (BAKE_FILTER_INDIRECT | BAKE_FILTER_GLOSSY), - BAKE_FILTER_TRANSMISSION_INDIRECT = (BAKE_FILTER_INDIRECT | BAKE_FILTER_TRANSMISSION), -} BakePassFilterCombos; - -typedef enum DenoiseFlag { - DENOISING_CLEAN_DIFFUSE_DIR = (1 << 0), - DENOISING_CLEAN_DIFFUSE_IND = (1 << 1), - DENOISING_CLEAN_GLOSSY_DIR = (1 << 2), - DENOISING_CLEAN_GLOSSY_IND = (1 << 3), - DENOISING_CLEAN_TRANSMISSION_DIR = (1 << 4), - DENOISING_CLEAN_TRANSMISSION_IND = (1 << 5), - DENOISING_CLEAN_ALL_PASSES = (1 << 6) - 1, -} DenoiseFlag; - -typedef ccl_addr_space struct PathRadianceState { -#ifdef __PASSES__ - float3 diffuse; - float3 glossy; - float3 transmission; - float3 volume; - - float3 direct; -#endif -} PathRadianceState; - -typedef ccl_addr_space struct PathRadiance { -#ifdef __PASSES__ - int use_light_pass; -#endif - - float transparent; - float3 emission; -#ifdef __PASSES__ - float3 background; - float3 ao; - - float3 indirect; - float3 direct_emission; - - float3 color_diffuse; - float3 color_glossy; - float3 color_transmission; - - float3 direct_diffuse; - float3 direct_glossy; - float3 direct_transmission; - float3 direct_volume; - - float3 indirect_diffuse; - float3 indirect_glossy; - float3 indirect_transmission; - float3 indirect_volume; - - float3 shadow; - float mist; -#endif - - struct PathRadianceState state; - -#ifdef __SHADOW_TRICKS__ - /* Total light reachable across the path, ignoring shadow blocked queries. */ - float3 path_total; - /* Total light reachable across the path with shadow blocked queries - * applied here. - * - * Dividing this figure by path_total will give estimate of shadow pass. - */ - float3 path_total_shaded; - - /* Color of the background on which shadow is alpha-overed. */ - float3 shadow_background_color; - - /* Path radiance sum and throughput at the moment when ray hits shadow - * catcher object. - */ - float shadow_throughput; - - /* Accumulated transparency along the path after shadow catcher bounce. */ - float shadow_transparency; - - /* Indicate if any shadow catcher data is set. */ - int has_shadow_catcher; -#endif - -#ifdef __DENOISING_FEATURES__ - float3 denoising_normal; - float3 denoising_albedo; - float denoising_depth; -#endif /* __DENOISING_FEATURES__ */ -} PathRadiance; - typedef struct BsdfEval { -#ifdef __PASSES__ - int use_light_pass; -#endif - float3 diffuse; -#ifdef __PASSES__ float3 glossy; - float3 transmission; - float3 transparent; - float3 volume; -#endif -#ifdef __SHADOW_TRICKS__ - float3 sum_no_mis; -#endif } BsdfEval; /* Shader Flag */ @@ -564,8 +442,10 @@ typedef enum ShaderFlag { SHADER_EXCLUDE_TRANSMIT = (1 << 25), SHADER_EXCLUDE_CAMERA = (1 << 24), SHADER_EXCLUDE_SCATTER = (1 << 23), + SHADER_EXCLUDE_SHADOW_CATCHER = (1 << 22), SHADER_EXCLUDE_ANY = (SHADER_EXCLUDE_DIFFUSE | SHADER_EXCLUDE_GLOSSY | SHADER_EXCLUDE_TRANSMIT | - SHADER_EXCLUDE_CAMERA | SHADER_EXCLUDE_SCATTER), + SHADER_EXCLUDE_CAMERA | SHADER_EXCLUDE_SCATTER | + SHADER_EXCLUDE_SHADOW_CATCHER), SHADER_MASK = ~(SHADER_SMOOTH_NORMAL | SHADER_CAST_SHADOW | SHADER_AREA_LIGHT | SHADER_USE_MIS | SHADER_EXCLUDE_ANY) @@ -612,38 +492,20 @@ typedef struct differential { /* Ray */ typedef struct Ray { -/* TODO(sergey): This is only needed because current AMD - * compiler has hard time building the kernel with this - * reshuffle. And at the same time reshuffle will cause - * less optimal CPU code in certain places. - * - * We'll get rid of this nasty exception once AMD compiler - * is fixed. - */ -#ifndef __KERNEL_OPENCL_AMD__ float3 P; /* origin */ float3 D; /* direction */ float t; /* length of the ray */ float time; /* time (for motion blur) */ -#else - float t; /* length of the ray */ - float time; /* time (for motion blur) */ - float3 P; /* origin */ - float3 D; /* direction */ -#endif #ifdef __RAY_DIFFERENTIALS__ - differential3 dP; - differential3 dD; + float dP; + float dD; #endif } Ray; /* Intersection */ typedef struct Intersection { -#ifdef __EMBREE__ - float3 Ng; -#endif float t, u, v; int prim; int object; @@ -661,9 +523,6 @@ typedef enum PrimitiveType { PRIMITIVE_CURVE_RIBBON = (1 << 4), PRIMITIVE_MOTION_CURVE_RIBBON = (1 << 5), PRIMITIVE_VOLUME = (1 << 6), - /* Lamp primitive is not included below on purpose, - * since it is no real traceable primitive. - */ PRIMITIVE_LAMP = (1 << 7), PRIMITIVE_ALL_TRIANGLE = (PRIMITIVE_TRIANGLE | PRIMITIVE_MOTION_TRIANGLE), @@ -672,16 +531,14 @@ typedef enum PrimitiveType { PRIMITIVE_ALL_VOLUME = (PRIMITIVE_VOLUME), PRIMITIVE_ALL_MOTION = (PRIMITIVE_MOTION_TRIANGLE | PRIMITIVE_MOTION_CURVE_THICK | PRIMITIVE_MOTION_CURVE_RIBBON), - PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE | PRIMITIVE_ALL_CURVE | PRIMITIVE_ALL_VOLUME), + PRIMITIVE_ALL = (PRIMITIVE_ALL_TRIANGLE | PRIMITIVE_ALL_CURVE | PRIMITIVE_ALL_VOLUME | + PRIMITIVE_LAMP), - /* Total number of different traceable primitives. - * NOTE: This is an actual value, not a bitflag. - */ - PRIMITIVE_NUM_TOTAL = 7, + PRIMITIVE_NUM = 8, } PrimitiveType; -#define PRIMITIVE_PACK_SEGMENT(type, segment) ((segment << PRIMITIVE_NUM_TOTAL) | (type)) -#define PRIMITIVE_UNPACK_SEGMENT(type) (type >> PRIMITIVE_NUM_TOTAL) +#define PRIMITIVE_PACK_SEGMENT(type, segment) ((segment << PRIMITIVE_NUM) | (type)) +#define PRIMITIVE_UNPACK_SEGMENT(type) (type >> PRIMITIVE_NUM) typedef enum CurveShapeType { CURVE_RIBBON = 0, @@ -730,6 +587,7 @@ typedef enum AttributeStandard { ATTR_STD_MOTION_VERTEX_NORMAL, ATTR_STD_PARTICLE, ATTR_STD_CURVE_INTERCEPT, + ATTR_STD_CURVE_LENGTH, ATTR_STD_CURVE_RANDOM, ATTR_STD_PTEX_FACE_ID, ATTR_STD_PTEX_UV, @@ -741,6 +599,7 @@ typedef enum AttributeStandard { ATTR_STD_VOLUME_VELOCITY, ATTR_STD_POINTINESS, ATTR_STD_RANDOM_PER_ISLAND, + ATTR_STD_SHADOW_TRANSPARENCY, ATTR_STD_NUM, ATTR_STD_NOT_FOUND = ~0 @@ -760,20 +619,20 @@ typedef struct AttributeDescriptor { /* Closure data */ -#ifdef __MULTI_CLOSURE__ -# ifdef __SPLIT_KERNEL__ -# define MAX_CLOSURE 1 -# else -# ifndef __MAX_CLOSURE__ -# define MAX_CLOSURE 64 -# else -# define MAX_CLOSURE __MAX_CLOSURE__ -# endif -# endif +#ifndef __MAX_CLOSURE__ +# define MAX_CLOSURE 64 #else -# define MAX_CLOSURE 1 +# define MAX_CLOSURE __MAX_CLOSURE__ #endif +#ifndef __MAX_VOLUME_STACK_SIZE__ +# define MAX_VOLUME_STACK_SIZE 32 +#else +# define MAX_VOLUME_STACK_SIZE __MAX_VOLUME_STACK_SIZE__ +#endif + +#define MAX_VOLUME_CLOSURE 8 + /* This struct is the base class for all closures. The common members are * duplicated in all derived classes since we don't have C++ in the kernel * yet, and because it lets us lay out the members to minimize padding. The @@ -792,7 +651,7 @@ typedef struct AttributeDescriptor { float sample_weight; \ float3 N -typedef ccl_addr_space struct ccl_align(16) ShaderClosure +typedef struct ccl_align(16) ShaderClosure { SHADER_CLOSURE_BASE; @@ -866,11 +725,14 @@ enum ShaderDataFlag { SD_NEED_VOLUME_ATTRIBUTES = (1 << 28), /* Shader has emission */ SD_HAS_EMISSION = (1 << 29), + /* Shader has raytracing */ + SD_HAS_RAYTRACE = (1 << 30), SD_SHADER_FLAGS = (SD_USE_MIS | SD_HAS_TRANSPARENT_SHADOW | SD_HAS_VOLUME | SD_HAS_ONLY_VOLUME | SD_HETEROGENEOUS_VOLUME | SD_HAS_BSSRDF_BUMP | SD_VOLUME_EQUIANGULAR | SD_VOLUME_MIS | SD_VOLUME_CUBIC | SD_HAS_BUMP | SD_HAS_DISPLACEMENT | - SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES) + SD_HAS_CONSTANT_EMISSION | SD_NEED_VOLUME_ATTRIBUTES | SD_HAS_EMISSION | + SD_HAS_RAYTRACE) }; /* Object flags. */ @@ -900,7 +762,7 @@ enum ShaderDataObjectFlag { SD_OBJECT_HAS_VOLUME_ATTRIBUTES) }; -typedef ccl_addr_space struct ccl_align(16) ShaderData +typedef struct ccl_align(16) ShaderData { /* position */ float3 P; @@ -955,19 +817,20 @@ typedef ccl_addr_space struct ccl_align(16) ShaderData #endif #ifdef __OBJECT_MOTION__ - /* object <-> world space transformations, cached to avoid - * re-interpolating them constantly for shading */ - Transform ob_tfm; - Transform ob_itfm; + /* Object <-> world space transformations for motion blur, cached to avoid + * re-interpolating them constantly for shading. */ + Transform ob_tfm_motion; + Transform ob_itfm_motion; #endif /* ray start position, only set for backgrounds */ float3 ray_P; - differential3 ray_dP; + float ray_dP; #ifdef __OSL__ - struct KernelGlobals *osl_globals; - struct PathState *osl_path_state; + const struct KernelGlobalsCPU *osl_globals; + const struct IntegratorStateCPU *osl_path_state; + const struct IntegratorShadowStateCPU *osl_shadow_path_state; #endif /* LCG state for closures that require additional random numbers. */ @@ -976,7 +839,6 @@ typedef ccl_addr_space struct ccl_align(16) ShaderData /* Closure data, we store a fixed array of closures */ int num_closure; int num_closure_left; - float randb_closure; float3 svm_closure_weight; /* Closure weights summed directly, so we can evaluate @@ -991,14 +853,30 @@ ShaderData; /* ShaderDataTinyStorage needs the same alignment as ShaderData, or else * the pointer cast in AS_SHADER_DATA invokes undefined behavior. */ -typedef ccl_addr_space struct ccl_align(16) ShaderDataTinyStorage +typedef struct ccl_align(16) ShaderDataTinyStorage { char pad[sizeof(ShaderData) - sizeof(ShaderClosure) * MAX_CLOSURE]; } ShaderDataTinyStorage; -#define AS_SHADER_DATA(shader_data_tiny_storage) ((ShaderData *)shader_data_tiny_storage) +#define AS_SHADER_DATA(shader_data_tiny_storage) \ + ((ccl_private ShaderData *)shader_data_tiny_storage) -/* Path State */ +/* Compact volume closures storage. + * + * Used for decoupled direct/indirect light closure storage. */ + +typedef struct ShaderVolumeClosure { + float3 weight; + float sample_weight; + float g; +} ShaderVolumeClosure; + +typedef struct ShaderVolumePhases { + ShaderVolumeClosure closure[MAX_VOLUME_CLOSURE]; + int num_closure; +} ShaderVolumePhases; + +/* Volume Stack */ #ifdef __VOLUME__ typedef struct VolumeStack { @@ -1007,77 +885,13 @@ typedef struct VolumeStack { } VolumeStack; #endif -typedef struct PathState { - /* see enum PathRayFlag */ - int flag; - - /* random number generator state */ - uint rng_hash; /* per pixel hash */ - int rng_offset; /* dimension offset */ - int sample; /* path sample number */ - int num_samples; /* total number of times this path will be sampled */ - float branch_factor; /* number of branches in indirect paths */ - - /* bounce counting */ - int bounce; - int diffuse_bounce; - int glossy_bounce; - int transmission_bounce; - int transparent_bounce; - -#ifdef __DENOISING_FEATURES__ - float denoising_feature_weight; - float3 denoising_feature_throughput; -#endif /* __DENOISING_FEATURES__ */ - - /* multiple importance sampling */ - float min_ray_pdf; /* smallest bounce pdf over entire path up to now */ - float ray_pdf; /* last bounce pdf */ -#ifdef __LAMP_MIS__ - float ray_t; /* accumulated distance through transparent surfaces */ -#endif - - /* volume rendering */ -#ifdef __VOLUME__ - int volume_bounce; - int volume_bounds_bounce; - VolumeStack volume_stack[VOLUME_STACK_SIZE]; -#endif -} PathState; - -#ifdef __VOLUME__ -typedef struct VolumeState { -# ifdef __SPLIT_KERNEL__ -# else - PathState ps; -# endif -} VolumeState; -#endif - /* Struct to gather multiple nearby intersections. */ typedef struct LocalIntersection { - Ray ray; - float3 weight[LOCAL_MAX_HITS]; - int num_hits; struct Intersection hits[LOCAL_MAX_HITS]; float3 Ng[LOCAL_MAX_HITS]; } LocalIntersection; -/* Subsurface */ - -/* Struct to gather SSS indirect rays and delay tracing them. */ -typedef struct SubsurfaceIndirectRays { - PathState state[BSSRDF_MAX_HITS]; - - int num_rays; - - struct Ray rays[BSSRDF_MAX_HITS]; - float3 throughputs[BSSRDF_MAX_HITS]; - struct PathRadianceState L_state[BSSRDF_MAX_HITS]; -} SubsurfaceIndirectRays; -static_assert(BSSRDF_MAX_HITS <= LOCAL_MAX_HITS, "BSSRDF hits too high."); - /* Constant Kernel Data * * These structs are passed from CPU to various devices, and the struct layout @@ -1128,7 +942,7 @@ typedef struct KernelCamera { /* render size */ float width, height; - int resolution; + int pad1; /* anamorphic lens bokeh */ float inv_aperture_ratio; @@ -1169,11 +983,12 @@ typedef struct KernelFilm { int light_pass_flag; int pass_stride; - int use_light_pass; int pass_combined; int pass_depth; + int pass_position; int pass_normal; + int pass_roughness; int pass_motion; int pass_motion_weight; @@ -1202,7 +1017,13 @@ typedef struct KernelFilm { int pass_shadow; float pass_shadow_scale; + + int pass_shadow_catcher; + int pass_shadow_catcher_sample_count; + int pass_shadow_catcher_matte; + int filter_table_offset; + int cryptomatte_passes; int cryptomatte_depth; int pass_cryptomatte; @@ -1215,15 +1036,12 @@ typedef struct KernelFilm { float mist_inv_depth; float mist_falloff; - int pass_denoising_data; - int pass_denoising_clean; - int denoising_flags; + int pass_denoising_normal; + int pass_denoising_albedo; + int pass_denoising_depth; int pass_aov_color; int pass_aov_value; - int pass_aov_color_num; - int pass_aov_value_num; - int pad1, pad2, pad3; /* XYZ to rendering color space transform. float4 instead of float3 to * ensure consistent padding/alignment across devices. */ @@ -1234,19 +1052,54 @@ typedef struct KernelFilm { int pass_bake_primitive; int pass_bake_differential; - int pad; - /* viewport rendering options */ - int display_pass_stride; - int display_pass_components; - int display_divide_pass_stride; - int use_display_exposure; - int use_display_pass_alpha; + int use_approximate_shadow_catcher; - int pad4, pad5, pad6; + int pad1, pad2; } KernelFilm; static_assert_align(KernelFilm, 16); +typedef struct KernelFilmConvert { + int pass_offset; + int pass_stride; + + int pass_use_exposure; + int pass_use_filter; + + int pass_divide; + int pass_indirect; + + int pass_combined; + int pass_sample_count; + int pass_adaptive_aux_buffer; + int pass_motion_weight; + int pass_shadow_catcher; + int pass_shadow_catcher_sample_count; + int pass_shadow_catcher_matte; + int pass_background; + + float scale; + float exposure; + float scale_exposure; + + int use_approximate_shadow_catcher; + int use_approximate_shadow_catcher_background; + int show_active_pixels; + + /* Number of components to write to. */ + int num_components; + + /* Number of floats per pixel. When zero is the same as `num_components`. + * NOTE: Is ignored for half4 destination. */ + int pixel_stride; + + int is_denoised; + + /* Padding. */ + int pad1; +} KernelFilmConvert; +static_assert_align(KernelFilmConvert, 16); + typedef struct KernelBackground { /* only shader index */ int surface_shader; @@ -1255,11 +1108,6 @@ typedef struct KernelBackground { int transparent; float transparent_roughness_squared_threshold; - /* ambient occlusion */ - float ao_factor; - float ao_distance; - float ao_bounces_factor; - /* portal sampling */ float portal_weight; int num_portals; @@ -1277,13 +1125,15 @@ typedef struct KernelBackground { int map_res_y; int use_mis; + + /* Padding */ + int pad1, pad2, pad3; } KernelBackground; static_assert_align(KernelBackground, 16); typedef struct KernelIntegrator { /* emission */ int use_direct_light; - int use_ambient_occlusion; int num_distribution; int num_all_lights; float pdf_triangles; @@ -1299,7 +1149,11 @@ typedef struct KernelIntegrator { int max_transmission_bounce; int max_volume_bounce; + /* AO bounces */ int ao_bounces; + float ao_bounces_distance; + float ao_bounces_factor; + float ao_additive_factor; /* transparent */ int transparent_min_bounce; @@ -1318,40 +1172,21 @@ typedef struct KernelIntegrator { float sample_clamp_direct; float sample_clamp_indirect; - /* branched path */ - int branched; - int volume_decoupled; - int diffuse_samples; - int glossy_samples; - int transmission_samples; - int ao_samples; - int mesh_light_samples; - int subsurface_samples; - int sample_all_lights_direct; - int sample_all_lights_indirect; - /* mis */ int use_lamp_mis; /* sampler */ int sampling_pattern; - int aa_samples; - int adaptive_min_samples; - int adaptive_step; - int adaptive_stop_per_sample; - float adaptive_threshold; /* volume render */ int use_volumes; int volume_max_steps; float volume_step_rate; - int volume_samples; - int start_sample; + int has_shadow_catcher; + float scrambling_distance; - int max_closures; - - int pad1, pad2; + /* padding */ } KernelIntegrator; static_assert_align(KernelIntegrator, 16); @@ -1401,14 +1236,19 @@ typedef struct KernelTables { static_assert_align(KernelTables, 16); typedef struct KernelBake { + int use; int object_index; int tri_offset; - int type; - int pass_filter; + int pad1; } KernelBake; static_assert_align(KernelBake, 16); typedef struct KernelData { + uint kernel_features; + uint max_closures; + uint max_shaders; + uint volume_stack_size; + KernelCamera cam; KernelFilm film; KernelBackground background; @@ -1450,10 +1290,25 @@ typedef struct KernelObject { float ao_distance; - float pad1, pad2; + uint visibility; + int primitive_type; } KernelObject; static_assert_align(KernelObject, 16); +typedef struct KernelCurve { + int shader_id; + int first_key; + int num_keys; + int type; +} KernelCurve; +static_assert_align(KernelCurve, 16); + +typedef struct KernelCurveSegment { + int prim; + int type; +} KernelCurveSegment; +static_assert_align(KernelCurveSegment, 8); + typedef struct KernelSpotLight { float radius; float invarea; @@ -1485,11 +1340,10 @@ typedef struct KernelLight { int type; float co[3]; int shader_id; - int samples; float max_bounces; float random; float strength[3]; - float pad1; + float pad1, pad2; Transform tfm; Transform itfm; union { @@ -1539,110 +1393,6 @@ typedef struct KernelShader { } KernelShader; static_assert_align(KernelShader, 16); -/* Declarations required for split kernel */ - -/* Macro for queues */ -/* Value marking queue's empty slot */ -#define QUEUE_EMPTY_SLOT -1 - -/* - * Queue 1 - Active rays - * Queue 2 - Background queue - * Queue 3 - Shadow ray cast kernel - AO - * Queue 4 - Shadow ray cast kernel - direct lighting - */ - -/* Queue names */ -enum QueueNumber { - /* All active rays and regenerated rays are enqueued here. */ - QUEUE_ACTIVE_AND_REGENERATED_RAYS = 0, - - /* All - * 1. Background-hit rays, - * 2. Rays that has exited path-iteration but needs to update output buffer - * 3. Rays to be regenerated - * are enqueued here. - */ - QUEUE_HITBG_BUFF_UPDATE_TOREGEN_RAYS, - - /* All rays for which a shadow ray should be cast to determine radiance - * contribution for AO are enqueued here. - */ - QUEUE_SHADOW_RAY_CAST_AO_RAYS, - - /* All rays for which a shadow ray should be cast to determine radiance - * contributing for direct lighting are enqueued here. - */ - QUEUE_SHADOW_RAY_CAST_DL_RAYS, - - /* Rays sorted according to shader->id */ - QUEUE_SHADER_SORTED_RAYS, - -#ifdef __BRANCHED_PATH__ - /* All rays moving to next iteration of the indirect loop for light */ - QUEUE_LIGHT_INDIRECT_ITER, - /* Queue of all inactive rays. These are candidates for sharing work of indirect loops */ - QUEUE_INACTIVE_RAYS, -# ifdef __VOLUME__ - /* All rays moving to next iteration of the indirect loop for volumes */ - QUEUE_VOLUME_INDIRECT_ITER, -# endif -# ifdef __SUBSURFACE__ - /* All rays moving to next iteration of the indirect loop for subsurface */ - QUEUE_SUBSURFACE_INDIRECT_ITER, -# endif -#endif /* __BRANCHED_PATH__ */ - - NUM_QUEUES -}; - -/* We use RAY_STATE_MASK to get ray_state */ -#define RAY_STATE_MASK 0x0F -#define RAY_FLAG_MASK 0xF0 -enum RayState { - RAY_INVALID = 0, - /* Denotes ray is actively involved in path-iteration. */ - RAY_ACTIVE, - /* Denotes ray has completed processing all samples and is inactive. */ - RAY_INACTIVE, - /* Denotes ray has exited path-iteration and needs to update output buffer. */ - RAY_UPDATE_BUFFER, - /* Denotes ray needs to skip most surface shader work. */ - RAY_HAS_ONLY_VOLUME, - /* Denotes ray has hit background */ - RAY_HIT_BACKGROUND, - /* Denotes ray has to be regenerated */ - RAY_TO_REGENERATE, - /* Denotes ray has been regenerated */ - RAY_REGENERATED, - /* Denotes ray is moving to next iteration of the branched indirect loop */ - RAY_LIGHT_INDIRECT_NEXT_ITER, - RAY_VOLUME_INDIRECT_NEXT_ITER, - RAY_SUBSURFACE_INDIRECT_NEXT_ITER, - - /* Ray flags */ - - /* Flags to denote that the ray is currently evaluating the branched indirect loop */ - RAY_BRANCHED_LIGHT_INDIRECT = (1 << 4), - RAY_BRANCHED_VOLUME_INDIRECT = (1 << 5), - RAY_BRANCHED_SUBSURFACE_INDIRECT = (1 << 6), - RAY_BRANCHED_INDIRECT = (RAY_BRANCHED_LIGHT_INDIRECT | RAY_BRANCHED_VOLUME_INDIRECT | - RAY_BRANCHED_SUBSURFACE_INDIRECT), - - /* Ray is evaluating an iteration of an indirect loop for another thread */ - RAY_BRANCHED_INDIRECT_SHARED = (1 << 7), -}; - -#define ASSIGN_RAY_STATE(ray_state, ray_index, state) \ - (ray_state[ray_index] = ((ray_state[ray_index] & RAY_FLAG_MASK) | state)) -#define IS_STATE(ray_state, ray_index, state) \ - ((ray_index) != QUEUE_EMPTY_SLOT && ((ray_state)[(ray_index)] & RAY_STATE_MASK) == (state)) -#define ADD_RAY_FLAG(ray_state, ray_index, flag) \ - (ray_state[ray_index] = (ray_state[ray_index] | flag)) -#define REMOVE_RAY_FLAG(ray_state, ray_index, flag) \ - (ray_state[ray_index] = (ray_state[ray_index] & (~flag))) -#define IS_FLAG(ray_state, ray_index, flag) (ray_state[ray_index] & flag) - /* Patches */ #define PATCH_MAX_CONTROL_VERTS 16 @@ -1655,7 +1405,7 @@ enum RayState { /* Work Tiles */ -typedef struct WorkTile { +typedef struct KernelWorkTile { uint x, y, w, h; uint start_sample; @@ -1664,13 +1414,195 @@ typedef struct WorkTile { int offset; uint stride; - ccl_global float *buffer; -} WorkTile; + /* Precalculated parameters used by init_from_camera kernel on GPU. */ + int path_index_offset; + int work_size; +} KernelWorkTile; + +/* Shader Evaluation. + * + * Position on a primitive on an object at which we want to evaluate the + * shader for e.g. mesh displacement or light importance map. */ + +typedef struct KernelShaderEvalInput { + int object; + int prim; + float u, v; +} KernelShaderEvalInput; +static_assert_align(KernelShaderEvalInput, 16); /* Pre-computed sample table sizes for PMJ02 sampler. */ -#define NUM_PMJ_SAMPLES (64 * 64) -#define NUM_PMJ_PATTERNS 48 +#define NUM_PMJ_DIVISIONS 32 +#define NUM_PMJ_SAMPLES ((NUM_PMJ_DIVISIONS) * (NUM_PMJ_DIVISIONS)) +#define NUM_PMJ_PATTERNS 1 + +/* Device kernels. + * + * Identifier for kernels that can be executed in device queues. + * + * Some implementation details. + * + * If the kernel uses shared CUDA memory, `CUDADeviceQueue::enqueue` is to be modified. + * The path iteration kernels are handled in `PathTraceWorkGPU::enqueue_path_iteration`. */ + +typedef enum DeviceKernel { + DEVICE_KERNEL_INTEGRATOR_INIT_FROM_CAMERA = 0, + DEVICE_KERNEL_INTEGRATOR_INIT_FROM_BAKE, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_CLOSEST, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SHADOW, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_SUBSURFACE, + DEVICE_KERNEL_INTEGRATOR_INTERSECT_VOLUME_STACK, + DEVICE_KERNEL_INTEGRATOR_SHADE_BACKGROUND, + DEVICE_KERNEL_INTEGRATOR_SHADE_LIGHT, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_SURFACE_RAYTRACE, + DEVICE_KERNEL_INTEGRATOR_SHADE_VOLUME, + DEVICE_KERNEL_INTEGRATOR_SHADE_SHADOW, + DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL, + + DEVICE_KERNEL_INTEGRATOR_QUEUED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_QUEUED_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_ACTIVE_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_TERMINATED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_SORTED_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_STATES, + DEVICE_KERNEL_INTEGRATOR_TERMINATED_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_PATHS_ARRAY, + DEVICE_KERNEL_INTEGRATOR_COMPACT_SHADOW_STATES, + DEVICE_KERNEL_INTEGRATOR_RESET, + DEVICE_KERNEL_INTEGRATOR_SHADOW_CATCHER_COUNT_POSSIBLE_SPLITS, + + DEVICE_KERNEL_SHADER_EVAL_DISPLACE, + DEVICE_KERNEL_SHADER_EVAL_BACKGROUND, + DEVICE_KERNEL_SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY, + +#define DECLARE_FILM_CONVERT_KERNEL(variant) \ + DEVICE_KERNEL_FILM_CONVERT_##variant, DEVICE_KERNEL_FILM_CONVERT_##variant##_HALF_RGBA + + DECLARE_FILM_CONVERT_KERNEL(DEPTH), + DECLARE_FILM_CONVERT_KERNEL(MIST), + DECLARE_FILM_CONVERT_KERNEL(SAMPLE_COUNT), + DECLARE_FILM_CONVERT_KERNEL(FLOAT), + DECLARE_FILM_CONVERT_KERNEL(LIGHT_PATH), + DECLARE_FILM_CONVERT_KERNEL(FLOAT3), + DECLARE_FILM_CONVERT_KERNEL(MOTION), + DECLARE_FILM_CONVERT_KERNEL(CRYPTOMATTE), + DECLARE_FILM_CONVERT_KERNEL(SHADOW_CATCHER), + DECLARE_FILM_CONVERT_KERNEL(SHADOW_CATCHER_MATTE_WITH_SHADOW), + DECLARE_FILM_CONVERT_KERNEL(COMBINED), + DECLARE_FILM_CONVERT_KERNEL(FLOAT4), + +#undef DECLARE_FILM_CONVERT_KERNEL + + DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_CHECK, + DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_X, + DEVICE_KERNEL_ADAPTIVE_SAMPLING_CONVERGENCE_FILTER_Y, + + DEVICE_KERNEL_FILTER_GUIDING_PREPROCESS, + DEVICE_KERNEL_FILTER_GUIDING_SET_FAKE_ALBEDO, + DEVICE_KERNEL_FILTER_COLOR_PREPROCESS, + DEVICE_KERNEL_FILTER_COLOR_POSTPROCESS, + + DEVICE_KERNEL_CRYPTOMATTE_POSTPROCESS, + + DEVICE_KERNEL_PREFIX_SUM, + + DEVICE_KERNEL_NUM, +} DeviceKernel; + +enum { + DEVICE_KERNEL_INTEGRATOR_NUM = DEVICE_KERNEL_INTEGRATOR_MEGAKERNEL + 1, +}; + +/* Kernel Features */ + +enum KernelFeatureFlag : unsigned int { + /* Shader nodes. */ + KERNEL_FEATURE_NODE_BSDF = (1U << 0U), + KERNEL_FEATURE_NODE_EMISSION = (1U << 1U), + KERNEL_FEATURE_NODE_VOLUME = (1U << 2U), + KERNEL_FEATURE_NODE_HAIR = (1U << 3U), + KERNEL_FEATURE_NODE_BUMP = (1U << 4U), + KERNEL_FEATURE_NODE_BUMP_STATE = (1U << 5U), + KERNEL_FEATURE_NODE_VORONOI_EXTRA = (1U << 6U), + KERNEL_FEATURE_NODE_RAYTRACE = (1U << 7U), + KERNEL_FEATURE_NODE_AOV = (1U << 8U), + KERNEL_FEATURE_NODE_LIGHT_PATH = (1U << 9U), + + /* Use denoising kernels and output denoising passes. */ + KERNEL_FEATURE_DENOISING = (1U << 10U), + + /* Use path tracing kernels. */ + KERNEL_FEATURE_PATH_TRACING = (1U << 11U), + + /* BVH/sampling kernel features. */ + KERNEL_FEATURE_HAIR = (1U << 12U), + KERNEL_FEATURE_HAIR_THICK = (1U << 13U), + KERNEL_FEATURE_OBJECT_MOTION = (1U << 14U), + KERNEL_FEATURE_CAMERA_MOTION = (1U << 15U), + + /* Denotes whether baking functionality is needed. */ + KERNEL_FEATURE_BAKING = (1U << 16U), + + /* Use subsurface scattering materials. */ + KERNEL_FEATURE_SUBSURFACE = (1U << 17U), + + /* Use volume materials. */ + KERNEL_FEATURE_VOLUME = (1U << 18U), + + /* Use OpenSubdiv patch evaluation */ + KERNEL_FEATURE_PATCH_EVALUATION = (1U << 19U), + + /* Use Transparent shadows */ + KERNEL_FEATURE_TRANSPARENT = (1U << 20U), + + /* Use shadow catcher. */ + KERNEL_FEATURE_SHADOW_CATCHER = (1U << 21U), + + /* Per-uber shader usage flags. */ + KERNEL_FEATURE_PRINCIPLED = (1U << 22U), + + /* Light render passes. */ + KERNEL_FEATURE_LIGHT_PASSES = (1U << 23U), + + /* Shadow render pass. */ + KERNEL_FEATURE_SHADOW_PASS = (1U << 24U), + + /* AO. */ + KERNEL_FEATURE_AO_PASS = (1U << 25U), + KERNEL_FEATURE_AO_ADDITIVE = (1U << 26U), + KERNEL_FEATURE_AO = (KERNEL_FEATURE_AO_PASS | KERNEL_FEATURE_AO_ADDITIVE), +}; + +/* Shader node feature mask, to specialize shader evaluation for kernels. */ + +#define KERNEL_FEATURE_NODE_MASK_SURFACE_LIGHT \ + (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VORONOI_EXTRA | \ + KERNEL_FEATURE_NODE_LIGHT_PATH) +#define KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW \ + (KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \ + KERNEL_FEATURE_NODE_HAIR | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE | \ + KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH) +#define KERNEL_FEATURE_NODE_MASK_SURFACE \ + (KERNEL_FEATURE_NODE_MASK_SURFACE_SHADOW | KERNEL_FEATURE_NODE_RAYTRACE | \ + KERNEL_FEATURE_NODE_AOV | KERNEL_FEATURE_NODE_LIGHT_PATH) +#define KERNEL_FEATURE_NODE_MASK_VOLUME \ + (KERNEL_FEATURE_NODE_EMISSION | KERNEL_FEATURE_NODE_VOLUME | \ + KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_LIGHT_PATH) +#define KERNEL_FEATURE_NODE_MASK_DISPLACEMENT \ + (KERNEL_FEATURE_NODE_VORONOI_EXTRA | KERNEL_FEATURE_NODE_BUMP | KERNEL_FEATURE_NODE_BUMP_STATE) +#define KERNEL_FEATURE_NODE_MASK_BUMP KERNEL_FEATURE_NODE_MASK_DISPLACEMENT + +/* Must be constexpr on the CPU to avoid compile errors because the state types + * are different depending on the main, shadow or null path. For GPU we don't have + * C++17 everywhere so can't use it. */ +#ifdef __KERNEL_CPU__ +# define IF_KERNEL_NODES_FEATURE(feature) \ + if constexpr ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U) +#else +# define IF_KERNEL_NODES_FEATURE(feature) \ + if ((node_feature_mask & (KERNEL_FEATURE_NODE_##feature)) != 0U) +#endif CCL_NAMESPACE_END - -#endif /* __KERNEL_TYPES_H__ */ diff --git a/intern/cycles/kernel/kernel_color.h b/intern/cycles/kernel/util/color.h similarity index 79% rename from intern/cycles/kernel/kernel_color.h rename to intern/cycles/kernel/util/color.h index 5eb1bdad02e..6d17647c9f8 100644 --- a/intern/cycles/kernel/kernel_color.h +++ b/intern/cycles/kernel/util/color.h @@ -14,25 +14,22 @@ * limitations under the License. */ -#ifndef __KERNEL_COLOR_H__ -#define __KERNEL_COLOR_H__ +#pragma once -#include "util/util_color.h" +#include "util/color.h" CCL_NAMESPACE_BEGIN -ccl_device float3 xyz_to_rgb(KernelGlobals *kg, float3 xyz) +ccl_device float3 xyz_to_rgb(KernelGlobals kg, float3 xyz) { return make_float3(dot(float4_to_float3(kernel_data.film.xyz_to_r), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_g), xyz), dot(float4_to_float3(kernel_data.film.xyz_to_b), xyz)); } -ccl_device float linear_rgb_to_gray(KernelGlobals *kg, float3 c) +ccl_device float linear_rgb_to_gray(KernelGlobals kg, float3 c) { return dot(c, float4_to_float3(kernel_data.film.rgb_to_y)); } CCL_NAMESPACE_END - -#endif /* __KERNEL_COLOR_H__ */ diff --git a/intern/cycles/kernel/kernel_differential.h b/intern/cycles/kernel/util/differential.h similarity index 50% rename from intern/cycles/kernel/kernel_differential.h rename to intern/cycles/kernel/util/differential.h index 3ec0cdbaccc..17187083019 100644 --- a/intern/cycles/kernel/kernel_differential.h +++ b/intern/cycles/kernel/util/differential.h @@ -14,29 +14,31 @@ * limitations under the License. */ +#pragma once + CCL_NAMESPACE_BEGIN /* See "Tracing Ray Differentials", Homan Igehy, 1999. */ -ccl_device void differential_transfer(ccl_addr_space differential3 *dP_, - const differential3 dP, - float3 D, - const differential3 dD, - float3 Ng, - float t) +ccl_device void differential_transfer(ccl_private differential3 *surface_dP, + const differential3 ray_dP, + float3 ray_D, + const differential3 ray_dD, + float3 surface_Ng, + float ray_t) { /* ray differential transfer through homogeneous medium, to * compute dPdx/dy at a shading point from the incoming ray */ - float3 tmp = D / dot(D, Ng); - float3 tmpx = dP.dx + t * dD.dx; - float3 tmpy = dP.dy + t * dD.dy; + float3 tmp = ray_D / dot(ray_D, surface_Ng); + float3 tmpx = ray_dP.dx + ray_t * ray_dD.dx; + float3 tmpy = ray_dP.dy + ray_t * ray_dD.dy; - dP_->dx = tmpx - dot(tmpx, Ng) * tmp; - dP_->dy = tmpy - dot(tmpy, Ng) * tmp; + surface_dP->dx = tmpx - dot(tmpx, surface_Ng) * tmp; + surface_dP->dy = tmpy - dot(tmpy, surface_Ng) * tmp; } -ccl_device void differential_incoming(ccl_addr_space differential3 *dI, const differential3 dD) +ccl_device void differential_incoming(ccl_private differential3 *dI, const differential3 dD) { /* compute dIdx/dy at a shading point, we just need to negate the * differential of the ray direction */ @@ -45,8 +47,8 @@ ccl_device void differential_incoming(ccl_addr_space differential3 *dI, const di dI->dy = -dD.dy; } -ccl_device void differential_dudv(ccl_addr_space differential *du, - ccl_addr_space differential *dv, +ccl_device void differential_dudv(ccl_private differential *du, + ccl_private differential *dv, float3 dPdu, float3 dPdv, differential3 dP, @@ -112,4 +114,53 @@ ccl_device differential3 differential3_zero() return d; } +/* Compact ray differentials that are just a scale to reduce memory usage and + * access cost in GPU. + * + * See above for more accurate reference implementations. + * + * TODO: also store the more compact version in ShaderData and recompute where + * needed? */ + +ccl_device_forceinline float differential_zero_compact() +{ + return 0.0f; +} + +ccl_device_forceinline float differential_make_compact(const differential3 D) +{ + return 0.5f * (len(D.dx) + len(D.dy)); +} + +ccl_device_forceinline void differential_transfer_compact(ccl_private differential3 *surface_dP, + const float ray_dP, + const float3 /* ray_D */, + const float ray_dD, + const float3 surface_Ng, + const float ray_t) +{ + /* ray differential transfer through homogeneous medium, to + * compute dPdx/dy at a shading point from the incoming ray */ + float scale = ray_dP + ray_t * ray_dD; + + float3 dx, dy; + make_orthonormals(surface_Ng, &dx, &dy); + surface_dP->dx = dx * scale; + surface_dP->dy = dy * scale; +} + +ccl_device_forceinline void differential_incoming_compact(ccl_private differential3 *dI, + const float3 D, + const float dD) +{ + /* compute dIdx/dy at a shading point, we just need to negate the + * differential of the ray direction */ + + float3 dx, dy; + make_orthonormals(D, &dx, &dy); + + dI->dx = dD * dx; + dI->dy = dD * dy; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/util/lookup_table.h b/intern/cycles/kernel/util/lookup_table.h new file mode 100644 index 00000000000..3ffbb4856da --- /dev/null +++ b/intern/cycles/kernel/util/lookup_table.h @@ -0,0 +1,56 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +CCL_NAMESPACE_BEGIN + +/* Interpolated lookup table access */ + +ccl_device float lookup_table_read(KernelGlobals kg, float x, int offset, int size) +{ + x = saturatef(x) * (size - 1); + + int index = min(float_to_int(x), size - 1); + int nindex = min(index + 1, size - 1); + float t = x - index; + + float data0 = kernel_tex_fetch(__lookup_table, index + offset); + if (t == 0.0f) + return data0; + + float data1 = kernel_tex_fetch(__lookup_table, nindex + offset); + return (1.0f - t) * data0 + t * data1; +} + +ccl_device float lookup_table_read_2D( + KernelGlobals kg, float x, float y, int offset, int xsize, int ysize) +{ + y = saturatef(y) * (ysize - 1); + + int index = min(float_to_int(y), ysize - 1); + int nindex = min(index + 1, ysize - 1); + float t = y - index; + + float data0 = lookup_table_read(kg, x, offset + xsize * index, xsize); + if (t == 0.0f) + return data0; + + float data1 = lookup_table_read(kg, x, offset + xsize * nindex, xsize); + return (1.0f - t) * data0 + t * data1; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/kernel/kernel_profiling.h b/intern/cycles/kernel/util/profiling.h similarity index 60% rename from intern/cycles/kernel/kernel_profiling.h rename to intern/cycles/kernel/util/profiling.h index 780830879d8..12ce441ccbf 100644 --- a/intern/cycles/kernel/kernel_profiling.h +++ b/intern/cycles/kernel/util/profiling.h @@ -14,33 +14,27 @@ * limitations under the License. */ -#ifndef __KERNEL_PROFILING_H__ -#define __KERNEL_PROFILING_H__ +#pragma once #ifdef __KERNEL_CPU__ -# include "util/util_profiling.h" +# include "util/profiling.h" #endif CCL_NAMESPACE_BEGIN #ifdef __KERNEL_CPU__ -# define PROFILING_INIT(kg, event) ProfilingHelper profiling_helper(&kg->profiler, event) +# define PROFILING_INIT(kg, event) \ + ProfilingHelper profiling_helper((ProfilingState *)&kg->profiler, event) # define PROFILING_EVENT(event) profiling_helper.set_event(event) -# define PROFILING_SHADER(shader) \ - if ((shader) != SHADER_NONE) { \ - profiling_helper.set_shader((shader)&SHADER_MASK); \ - } -# define PROFILING_OBJECT(object) \ - if ((object) != PRIM_NONE) { \ - profiling_helper.set_object(object); \ - } +# define PROFILING_INIT_FOR_SHADER(kg, event) \ + ProfilingWithShaderHelper profiling_helper((ProfilingState *)&kg->profiler, event) +# define PROFILING_SHADER(object, shader) \ + profiling_helper.set_shader(object, (shader)&SHADER_MASK); #else # define PROFILING_INIT(kg, event) # define PROFILING_EVENT(event) -# define PROFILING_SHADER(shader) -# define PROFILING_OBJECT(object) +# define PROFILING_INIT_FOR_SHADER(kg, event) +# define PROFILING_SHADER(object, shader) #endif /* __KERNEL_CPU__ */ CCL_NAMESPACE_END - -#endif /* __KERNEL_PROFILING_H__ */ diff --git a/intern/cycles/render/bake.cpp b/intern/cycles/render/bake.cpp deleted file mode 100644 index 317a3937cab..00000000000 --- a/intern/cycles/render/bake.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2011-2014 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "render/bake.h" -#include "render/buffers.h" -#include "render/integrator.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/shader.h" -#include "render/stats.h" - -#include "util/util_foreach.h" - -CCL_NAMESPACE_BEGIN - -static int aa_samples(Scene *scene, Object *object, ShaderEvalType type) -{ - if (type == SHADER_EVAL_UV || type == SHADER_EVAL_ROUGHNESS) { - return 1; - } - else if (type == SHADER_EVAL_NORMAL) { - /* Only antialias normal if mesh has bump mapping. */ - if (object->get_geometry()) { - foreach (Node *node, object->get_geometry()->get_used_shaders()) { - Shader *shader = static_cast(node); - if (shader->has_bump) { - return scene->integrator->get_aa_samples(); - } - } - } - - return 1; - } - else { - return scene->integrator->get_aa_samples(); - } -} - -/* Keep it synced with kernel_bake.h logic */ -static int shader_type_to_pass_filter(ShaderEvalType type, int pass_filter) -{ - const int component_flags = pass_filter & - (BAKE_FILTER_DIRECT | BAKE_FILTER_INDIRECT | BAKE_FILTER_COLOR); - - switch (type) { - case SHADER_EVAL_AO: - return BAKE_FILTER_AO; - case SHADER_EVAL_SHADOW: - return BAKE_FILTER_DIRECT; - case SHADER_EVAL_DIFFUSE: - return BAKE_FILTER_DIFFUSE | component_flags; - case SHADER_EVAL_GLOSSY: - return BAKE_FILTER_GLOSSY | component_flags; - case SHADER_EVAL_TRANSMISSION: - return BAKE_FILTER_TRANSMISSION | component_flags; - case SHADER_EVAL_COMBINED: - return pass_filter; - default: - return 0; - } -} - -BakeManager::BakeManager() -{ - type = SHADER_EVAL_BAKE; - pass_filter = 0; - - need_update_ = true; -} - -BakeManager::~BakeManager() -{ -} - -bool BakeManager::get_baking() -{ - return !object_name.empty(); -} - -void BakeManager::set(Scene *scene, - const std::string &object_name_, - ShaderEvalType type_, - int pass_filter_) -{ - object_name = object_name_; - type = type_; - pass_filter = shader_type_to_pass_filter(type_, pass_filter_); - - Pass::add(PASS_BAKE_PRIMITIVE, scene->passes); - Pass::add(PASS_BAKE_DIFFERENTIAL, scene->passes); - - if (type == SHADER_EVAL_UV) { - /* force UV to be available */ - Pass::add(PASS_UV, scene->passes); - } - - /* force use_light_pass to be true if we bake more than just colors */ - if (pass_filter & ~BAKE_FILTER_COLOR) { - Pass::add(PASS_LIGHT, scene->passes); - } - - /* create device and update scene */ - scene->film->tag_modified(); - scene->integrator->tag_update(scene, Integrator::UPDATE_ALL); - - need_update_ = true; -} - -void BakeManager::device_update(Device * /*device*/, - DeviceScene *dscene, - Scene *scene, - Progress & /* progress */) -{ - if (!need_update()) - return; - - scoped_callback_timer timer([scene](double time) { - if (scene->update_stats) { - scene->update_stats->bake.times.add_entry({"device_update", time}); - } - }); - - KernelIntegrator *kintegrator = &dscene->data.integrator; - KernelBake *kbake = &dscene->data.bake; - - kbake->type = type; - kbake->pass_filter = pass_filter; - - int object_index = 0; - foreach (Object *object, scene->objects) { - const Geometry *geom = object->get_geometry(); - if (object->name == object_name && geom->geometry_type == Geometry::MESH) { - kbake->object_index = object_index; - kbake->tri_offset = geom->prim_offset; - kintegrator->aa_samples = aa_samples(scene, object, type); - break; - } - - object_index++; - } - - need_update_ = false; -} - -void BakeManager::device_free(Device * /*device*/, DeviceScene * /*dscene*/) -{ -} - -void BakeManager::tag_update() -{ - need_update_ = true; -} - -bool BakeManager::need_update() const -{ - return need_update_; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/buffers.cpp b/intern/cycles/render/buffers.cpp deleted file mode 100644 index fcfad58995e..00000000000 --- a/intern/cycles/render/buffers.cpp +++ /dev/null @@ -1,564 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#include "device/device.h" -#include "render/buffers.h" - -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_math.h" -#include "util/util_opengl.h" -#include "util/util_time.h" -#include "util/util_types.h" - -CCL_NAMESPACE_BEGIN - -/* Buffer Params */ - -BufferParams::BufferParams() -{ - width = 0; - height = 0; - - full_x = 0; - full_y = 0; - full_width = 0; - full_height = 0; - - denoising_data_pass = false; - denoising_clean_pass = false; - denoising_prefiltered_pass = false; - - Pass::add(PASS_COMBINED, passes); -} - -void BufferParams::get_offset_stride(int &offset, int &stride) -{ - offset = -(full_x + full_y * width); - stride = width; -} - -bool BufferParams::modified(const BufferParams ¶ms) -{ - return !(full_x == params.full_x && full_y == params.full_y && width == params.width && - height == params.height && full_width == params.full_width && - full_height == params.full_height && Pass::equals(passes, params.passes) && - denoising_data_pass == params.denoising_data_pass && - denoising_clean_pass == params.denoising_clean_pass && - denoising_prefiltered_pass == params.denoising_prefiltered_pass); -} - -int BufferParams::get_passes_size() -{ - int size = 0; - - for (size_t i = 0; i < passes.size(); i++) - size += passes[i].components; - - if (denoising_data_pass) { - size += DENOISING_PASS_SIZE_BASE; - if (denoising_clean_pass) - size += DENOISING_PASS_SIZE_CLEAN; - if (denoising_prefiltered_pass) - size += DENOISING_PASS_SIZE_PREFILTERED; - } - - return align_up(size, 4); -} - -int BufferParams::get_denoising_offset() -{ - int offset = 0; - - for (size_t i = 0; i < passes.size(); i++) - offset += passes[i].components; - - return offset; -} - -int BufferParams::get_denoising_prefiltered_offset() -{ - assert(denoising_prefiltered_pass); - - int offset = get_denoising_offset(); - - offset += DENOISING_PASS_SIZE_BASE; - if (denoising_clean_pass) { - offset += DENOISING_PASS_SIZE_CLEAN; - } - - return offset; -} - -/* Render Buffer Task */ - -RenderTile::RenderTile() -{ - x = 0; - y = 0; - w = 0; - h = 0; - - sample = 0; - start_sample = 0; - num_samples = 0; - resolution = 0; - - offset = 0; - stride = 0; - - buffer = 0; - - buffers = NULL; - stealing_state = NO_STEALING; -} - -/* Render Buffers */ - -RenderBuffers::RenderBuffers(Device *device) - : buffer(device, "RenderBuffers", MEM_READ_WRITE), - map_neighbor_copied(false), - render_time(0.0f) -{ -} - -RenderBuffers::~RenderBuffers() -{ - buffer.free(); -} - -void RenderBuffers::reset(BufferParams ¶ms_) -{ - params = params_; - - /* re-allocate buffer */ - buffer.alloc(params.width * params.get_passes_size(), params.height); - buffer.zero_to_device(); -} - -void RenderBuffers::zero() -{ - buffer.zero_to_device(); -} - -bool RenderBuffers::copy_from_device() -{ - if (!buffer.device_pointer) - return false; - - buffer.copy_from_device(0, params.width * params.get_passes_size(), params.height); - - return true; -} - -bool RenderBuffers::get_denoising_pass_rect( - int type, float exposure, int sample, int components, float *pixels) -{ - if (buffer.data() == NULL) { - return false; - } - - float scale = 1.0f; - float alpha_scale = 1.0f / sample; - if (type == DENOISING_PASS_PREFILTERED_COLOR || type == DENOISING_PASS_CLEAN || - type == DENOISING_PASS_PREFILTERED_INTENSITY) { - scale *= exposure; - } - else if (type == DENOISING_PASS_PREFILTERED_VARIANCE) { - scale *= exposure * exposure * (sample - 1); - } - - int offset; - if (type == DENOISING_PASS_CLEAN) { - /* The clean pass isn't changed by prefiltering, so we use the original one there. */ - offset = type + params.get_denoising_offset(); - scale /= sample; - } - else if (params.denoising_prefiltered_pass) { - offset = type + params.get_denoising_prefiltered_offset(); - } - else { - switch (type) { - case DENOISING_PASS_PREFILTERED_DEPTH: - offset = params.get_denoising_offset() + DENOISING_PASS_DEPTH; - break; - case DENOISING_PASS_PREFILTERED_NORMAL: - offset = params.get_denoising_offset() + DENOISING_PASS_NORMAL; - break; - case DENOISING_PASS_PREFILTERED_ALBEDO: - offset = params.get_denoising_offset() + DENOISING_PASS_ALBEDO; - break; - case DENOISING_PASS_PREFILTERED_COLOR: - /* If we're not saving the prefiltering result, return the original noisy pass. */ - offset = params.get_denoising_offset() + DENOISING_PASS_COLOR; - break; - default: - return false; - } - scale /= sample; - } - - int pass_stride = params.get_passes_size(); - int size = params.width * params.height; - - float *in = buffer.data() + offset; - - if (components == 1) { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - pixels[0] = in[0] * scale; - } - } - else if (components == 3) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 3) { - pixels[0] = in[0] * scale; - pixels[1] = in[1] * scale; - pixels[2] = in[2] * scale; - } - } - else if (components == 4) { - /* Since the alpha channel is not involved in denoising, output the Combined alpha channel. */ - assert(params.passes[0].type == PASS_COMBINED); - float *in_combined = buffer.data(); - - for (int i = 0; i < size; i++, in += pass_stride, in_combined += pass_stride, pixels += 4) { - float3 val = make_float3(in[0], in[1], in[2]); - if (type == DENOISING_PASS_PREFILTERED_COLOR && params.denoising_prefiltered_pass) { - /* Remove highlight compression from the image. */ - val = color_highlight_uncompress(val); - } - pixels[0] = val.x * scale; - pixels[1] = val.y * scale; - pixels[2] = val.z * scale; - pixels[3] = saturate(in_combined[3] * alpha_scale); - } - } - else { - return false; - } - - return true; -} - -bool RenderBuffers::get_pass_rect( - const string &name, float exposure, int sample, int components, float *pixels) -{ - if (buffer.data() == NULL) { - return false; - } - - float *sample_count = NULL; - if (name == "Combined") { - int sample_offset = 0; - for (size_t j = 0; j < params.passes.size(); j++) { - Pass &pass = params.passes[j]; - if (pass.type != PASS_SAMPLE_COUNT) { - sample_offset += pass.components; - continue; - } - else { - sample_count = buffer.data() + sample_offset; - break; - } - } - } - - int pass_offset = 0; - - for (size_t j = 0; j < params.passes.size(); j++) { - Pass &pass = params.passes[j]; - - /* Pass is identified by both type and name, multiple of the same type - * may exist with a different name. */ - if (pass.name != name) { - pass_offset += pass.components; - continue; - } - - PassType type = pass.type; - - float *in = buffer.data() + pass_offset; - int pass_stride = params.get_passes_size(); - - float scale = (pass.filter) ? 1.0f / (float)sample : 1.0f; - float scale_exposure = (pass.exposure) ? scale * exposure : scale; - - int size = params.width * params.height; - - if (components == 1 && type == PASS_RENDER_TIME) { - /* Render time is not stored by kernel, but measured per tile. */ - float val = (float)(1000.0 * render_time / (params.width * params.height * sample)); - for (int i = 0; i < size; i++, pixels++) { - pixels[0] = val; - } - } - else if (components == 1) { - assert(pass.components == components); - - /* Scalar */ - if (type == PASS_DEPTH) { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - float f = *in; - pixels[0] = (f == 0.0f) ? 1e10f : f * scale_exposure; - } - } - else if (type == PASS_MIST) { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - float f = *in; - pixels[0] = saturate(f * scale_exposure); - } - } - else { - for (int i = 0; i < size; i++, in += pass_stride, pixels++) { - float f = *in; - pixels[0] = f * scale_exposure; - } - } - } - else if (components == 3) { - assert(pass.components == 4); - - /* RGBA */ - if (type == PASS_SHADOW) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 3) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - float invw = (f.w > 0.0f) ? 1.0f / f.w : 1.0f; - - pixels[0] = f.x * invw; - pixels[1] = f.y * invw; - pixels[2] = f.z * invw; - } - } - else if (pass.divide_type != PASS_NONE) { - /* RGB lighting passes that need to divide out color */ - pass_offset = 0; - for (size_t k = 0; k < params.passes.size(); k++) { - Pass &color_pass = params.passes[k]; - if (color_pass.type == pass.divide_type) - break; - pass_offset += color_pass.components; - } - - float *in_divide = buffer.data() + pass_offset; - - for (int i = 0; i < size; i++, in += pass_stride, in_divide += pass_stride, pixels += 3) { - float3 f = make_float3(in[0], in[1], in[2]); - float3 f_divide = make_float3(in_divide[0], in_divide[1], in_divide[2]); - - f = safe_divide_even_color(f * exposure, f_divide); - - pixels[0] = f.x; - pixels[1] = f.y; - pixels[2] = f.z; - } - } - else { - /* RGB/vector */ - for (int i = 0; i < size; i++, in += pass_stride, pixels += 3) { - float3 f = make_float3(in[0], in[1], in[2]); - - pixels[0] = f.x * scale_exposure; - pixels[1] = f.y * scale_exposure; - pixels[2] = f.z * scale_exposure; - } - } - } - else if (components == 4) { - assert(pass.components == components); - - /* RGBA */ - if (type == PASS_SHADOW) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - float invw = (f.w > 0.0f) ? 1.0f / f.w : 1.0f; - - pixels[0] = f.x * invw; - pixels[1] = f.y * invw; - pixels[2] = f.z * invw; - pixels[3] = 1.0f; - } - } - else if (type == PASS_MOTION) { - /* need to normalize by number of samples accumulated for motion */ - pass_offset = 0; - for (size_t k = 0; k < params.passes.size(); k++) { - Pass &color_pass = params.passes[k]; - if (color_pass.type == PASS_MOTION_WEIGHT) - break; - pass_offset += color_pass.components; - } - - float *in_weight = buffer.data() + pass_offset; - - for (int i = 0; i < size; i++, in += pass_stride, in_weight += pass_stride, pixels += 4) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - float w = in_weight[0]; - float invw = (w > 0.0f) ? 1.0f / w : 0.0f; - - pixels[0] = f.x * invw; - pixels[1] = f.y * invw; - pixels[2] = f.z * invw; - pixels[3] = f.w * invw; - } - } - else if (type == PASS_CRYPTOMATTE) { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) { - float4 f = make_float4(in[0], in[1], in[2], in[3]); - /* x and z contain integer IDs, don't rescale them. - y and w contain matte weights, they get scaled. */ - pixels[0] = f.x; - pixels[1] = f.y * scale; - pixels[2] = f.z; - pixels[3] = f.w * scale; - } - } - else { - for (int i = 0; i < size; i++, in += pass_stride, pixels += 4) { - if (sample_count && sample_count[i * pass_stride] < 0.0f) { - scale = (pass.filter) ? -1.0f / (sample_count[i * pass_stride]) : 1.0f; - scale_exposure = (pass.exposure) ? scale * exposure : scale; - } - - float4 f = make_float4(in[0], in[1], in[2], in[3]); - - pixels[0] = f.x * scale_exposure; - pixels[1] = f.y * scale_exposure; - pixels[2] = f.z * scale_exposure; - - /* Clamp since alpha might be > 1.0 due to Russian roulette. */ - pixels[3] = saturate(f.w * scale); - } - } - } - - return true; - } - - return false; -} - -bool RenderBuffers::set_pass_rect(PassType type, int components, float *pixels, int samples) -{ - if (buffer.data() == NULL) { - return false; - } - - int pass_offset = 0; - - for (size_t j = 0; j < params.passes.size(); j++) { - Pass &pass = params.passes[j]; - - if (pass.type != type) { - pass_offset += pass.components; - continue; - } - - float *out = buffer.data() + pass_offset; - int pass_stride = params.get_passes_size(); - int size = params.width * params.height; - - assert(pass.components == components); - - for (int i = 0; i < size; i++, out += pass_stride, pixels += components) { - if (pass.filter) { - /* Scale by the number of samples, inverse of what we do in get_pass_rect. - * A better solution would be to remove the need for set_pass_rect entirely, - * and change baking to bake multiple objects in a tile at once. */ - for (int j = 0; j < components; j++) { - out[j] = pixels[j] * samples; - } - } - else { - /* For non-filtered passes just straight copy, these may contain non-float data. */ - memcpy(out, pixels, sizeof(float) * components); - } - } - - return true; - } - - return false; -} - -/* Display Buffer */ - -DisplayBuffer::DisplayBuffer(Device *device, bool linear) - : draw_width(0), - draw_height(0), - transparent(true), /* todo: determine from background */ - half_float(linear), - rgba_byte(device, "display buffer byte"), - rgba_half(device, "display buffer half") -{ -} - -DisplayBuffer::~DisplayBuffer() -{ - rgba_byte.free(); - rgba_half.free(); -} - -void DisplayBuffer::reset(BufferParams ¶ms_) -{ - draw_width = 0; - draw_height = 0; - - params = params_; - - /* allocate display pixels */ - if (half_float) { - rgba_half.alloc_to_device(params.width, params.height); - } - else { - rgba_byte.alloc_to_device(params.width, params.height); - } -} - -void DisplayBuffer::draw_set(int width, int height) -{ - assert(width <= params.width && height <= params.height); - - draw_width = width; - draw_height = height; -} - -void DisplayBuffer::draw(Device *device, const DeviceDrawParams &draw_params) -{ - if (draw_width != 0 && draw_height != 0) { - device_memory &rgba = (half_float) ? (device_memory &)rgba_half : (device_memory &)rgba_byte; - - device->draw_pixels(rgba, - 0, - draw_width, - draw_height, - params.width, - params.height, - params.full_x, - params.full_y, - params.full_width, - params.full_height, - transparent, - draw_params); - } -} - -bool DisplayBuffer::draw_ready() -{ - return (draw_width != 0 && draw_height != 0); -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/buffers.h b/intern/cycles/render/buffers.h deleted file mode 100644 index 4ffc628bb52..00000000000 --- a/intern/cycles/render/buffers.h +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __BUFFERS_H__ -#define __BUFFERS_H__ - -#include "device/device_memory.h" - -#include "render/film.h" - -#include "kernel/kernel_types.h" - -#include "util/util_half.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_types.h" - -CCL_NAMESPACE_BEGIN - -class Device; -struct DeviceDrawParams; -struct float4; - -/* Buffer Parameters - * Size of render buffer and how it fits in the full image (border render). */ - -class BufferParams { - public: - /* width/height of the physical buffer */ - int width; - int height; - - /* offset into and width/height of the full buffer */ - int full_x; - int full_y; - int full_width; - int full_height; - - /* passes */ - vector passes; - bool denoising_data_pass; - /* If only some light path types should be target, an additional pass is needed. */ - bool denoising_clean_pass; - /* When we're prefiltering the passes during rendering, we need to keep both the - * original and the prefiltered data around because neighboring tiles might still - * need the original data. */ - bool denoising_prefiltered_pass; - - /* functions */ - BufferParams(); - - void get_offset_stride(int &offset, int &stride); - bool modified(const BufferParams ¶ms); - int get_passes_size(); - int get_denoising_offset(); - int get_denoising_prefiltered_offset(); -}; - -/* Render Buffers */ - -class RenderBuffers { - public: - /* buffer parameters */ - BufferParams params; - - /* float buffer */ - device_vector buffer; - bool map_neighbor_copied; - double render_time; - - explicit RenderBuffers(Device *device); - ~RenderBuffers(); - - void reset(BufferParams ¶ms); - void zero(); - - bool copy_from_device(); - bool get_pass_rect( - const string &name, float exposure, int sample, int components, float *pixels); - bool get_denoising_pass_rect( - int offset, float exposure, int sample, int components, float *pixels); - bool set_pass_rect(PassType type, int components, float *pixels, int samples); -}; - -/* Display Buffer - * - * The buffer used for drawing during render, filled by converting the render - * buffers to byte of half float storage */ - -class DisplayBuffer { - public: - /* buffer parameters */ - BufferParams params; - /* dimensions for how much of the buffer is actually ready for display. - * with progressive render we can be using only a subset of the buffer. - * if these are zero, it means nothing can be drawn yet */ - int draw_width, draw_height; - /* draw alpha channel? */ - bool transparent; - /* use half float? */ - bool half_float; - /* byte buffer for converted result */ - device_pixels rgba_byte; - device_pixels rgba_half; - - DisplayBuffer(Device *device, bool linear = false); - ~DisplayBuffer(); - - void reset(BufferParams ¶ms); - - void draw_set(int width, int height); - void draw(Device *device, const DeviceDrawParams &draw_params); - bool draw_ready(); -}; - -/* Render Tile - * Rendering task on a buffer */ - -class RenderTile { - public: - typedef enum { PATH_TRACE = (1 << 0), BAKE = (1 << 1), DENOISE = (1 << 2) } Task; - - Task task; - int x, y, w, h; - int start_sample; - int num_samples; - int sample; - int resolution; - int offset; - int stride; - int tile_index; - - device_ptr buffer; - int device_size; - - typedef enum { NO_STEALING = 0, CAN_BE_STOLEN = 1, WAS_STOLEN = 2 } StealingState; - StealingState stealing_state; - - RenderBuffers *buffers; - - RenderTile(); - - int4 bounds() const - { - return make_int4(x, /* xmin */ - y, /* ymin */ - x + w, /* xmax */ - y + h); /* ymax */ - } -}; - -/* Render Tile Neighbors - * Set of neighboring tiles used for denoising. Tile order: - * 0 1 2 - * 3 4 5 - * 6 7 8 */ - -class RenderTileNeighbors { - public: - static const int SIZE = 9; - static const int CENTER = 4; - - RenderTile tiles[SIZE]; - RenderTile target; - - RenderTileNeighbors(const RenderTile ¢er) - { - tiles[CENTER] = center; - } - - int4 bounds() const - { - return make_int4(tiles[3].x, /* xmin */ - tiles[1].y, /* ymin */ - tiles[5].x + tiles[5].w, /* xmax */ - tiles[7].y + tiles[7].h); /* ymax */ - } - - void set_bounds_from_center() - { - tiles[3].x = tiles[CENTER].x; - tiles[1].y = tiles[CENTER].y; - tiles[5].x = tiles[CENTER].x + tiles[CENTER].w; - tiles[7].y = tiles[CENTER].y + tiles[CENTER].h; - } -}; - -CCL_NAMESPACE_END - -#endif /* __BUFFERS_H__ */ diff --git a/intern/cycles/render/coverage.cpp b/intern/cycles/render/coverage.cpp deleted file mode 100644 index 99d4daa6961..00000000000 --- a/intern/cycles/render/coverage.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright 2018 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "render/coverage.h" -#include "render/buffers.h" - -#include "kernel/kernel_compat_cpu.h" -#include "kernel/kernel_types.h" -#include "kernel/split/kernel_split_data.h" - -#include "kernel/kernel_globals.h" -#include "kernel/kernel_id_passes.h" - -#include "util/util_map.h" - -CCL_NAMESPACE_BEGIN - -static bool crypomatte_comp(const pair &i, const pair j) -{ - return i.first > j.first; -} - -void Coverage::finalize() -{ - int pass_offset = 0; - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - finalize_buffer(coverage_object, pass_offset); - pass_offset += kernel_data.film.cryptomatte_depth * 4; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - finalize_buffer(coverage_material, pass_offset); - pass_offset += kernel_data.film.cryptomatte_depth * 4; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - finalize_buffer(coverage_asset, pass_offset); - } -} - -void Coverage::init_path_trace() -{ - kg->coverage_object = kg->coverage_material = kg->coverage_asset = NULL; - - if (kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE) { - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - coverage_object.clear(); - coverage_object.resize(tile.w * tile.h); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - coverage_material.clear(); - coverage_material.resize(tile.w * tile.h); - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - coverage_asset.clear(); - coverage_asset.resize(tile.w * tile.h); - } - } -} - -void Coverage::init_pixel(int x, int y) -{ - if (kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE) { - const int pixel_index = tile.w * (y - tile.y) + x - tile.x; - if (kernel_data.film.cryptomatte_passes & CRYPT_OBJECT) { - kg->coverage_object = &coverage_object[pixel_index]; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_MATERIAL) { - kg->coverage_material = &coverage_material[pixel_index]; - } - if (kernel_data.film.cryptomatte_passes & CRYPT_ASSET) { - kg->coverage_asset = &coverage_asset[pixel_index]; - } - } -} - -void Coverage::finalize_buffer(vector &coverage, const int pass_offset) -{ - if (kernel_data.film.cryptomatte_passes & CRYPT_ACCURATE) { - flatten_buffer(coverage, pass_offset); - } - else { - sort_buffer(pass_offset); - } -} - -void Coverage::flatten_buffer(vector &coverage, const int pass_offset) -{ - /* Sort the coverage map and write it to the output */ - int pixel_index = 0; - int pass_stride = tile.buffers->params.get_passes_size(); - for (int y = 0; y < tile.h; ++y) { - for (int x = 0; x < tile.w; ++x) { - const CoverageMap &pixel = coverage[pixel_index]; - if (!pixel.empty()) { - /* buffer offset */ - int index = x + y * tile.stride; - float *buffer = (float *)tile.buffer + index * pass_stride; - - /* sort the cryptomatte pixel */ - vector> sorted_pixel; - for (CoverageMap::const_iterator it = pixel.begin(); it != pixel.end(); ++it) { - sorted_pixel.push_back(std::make_pair(it->second, it->first)); - } - sort(sorted_pixel.begin(), sorted_pixel.end(), crypomatte_comp); - int num_slots = 2 * (kernel_data.film.cryptomatte_depth); - if (sorted_pixel.size() > num_slots) { - float leftover = 0.0f; - for (vector>::iterator it = sorted_pixel.begin() + num_slots; - it != sorted_pixel.end(); - ++it) { - leftover += it->first; - } - sorted_pixel[num_slots - 1].first += leftover; - } - int limit = min(num_slots, sorted_pixel.size()); - for (int i = 0; i < limit; ++i) { - kernel_write_id_slots(buffer + kernel_data.film.pass_cryptomatte + pass_offset, - 2 * (kernel_data.film.cryptomatte_depth), - sorted_pixel[i].second, - sorted_pixel[i].first); - } - } - ++pixel_index; - } - } -} - -void Coverage::sort_buffer(const int pass_offset) -{ - /* Sort the coverage map and write it to the output */ - int pass_stride = tile.buffers->params.get_passes_size(); - for (int y = 0; y < tile.h; ++y) { - for (int x = 0; x < tile.w; ++x) { - /* buffer offset */ - int index = x + y * tile.stride; - float *buffer = (float *)tile.buffer + index * pass_stride; - kernel_sort_id_slots(buffer + kernel_data.film.pass_cryptomatte + pass_offset, - 2 * (kernel_data.film.cryptomatte_depth)); - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/coverage.h b/intern/cycles/render/coverage.h deleted file mode 100644 index 12182c614da..00000000000 --- a/intern/cycles/render/coverage.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __COVERAGE_H__ -#define __COVERAGE_H__ - -#include "util/util_map.h" -#include "util/util_vector.h" - -CCL_NAMESPACE_BEGIN - -struct KernelGlobals; -class RenderTile; - -typedef unordered_map CoverageMap; - -class Coverage { - public: - Coverage(KernelGlobals *kg_, RenderTile &tile_) : kg(kg_), tile(tile_) - { - } - void init_path_trace(); - void init_pixel(int x, int y); - void finalize(); - - private: - vector coverage_object; - vector coverage_material; - vector coverage_asset; - KernelGlobals *kg; - RenderTile &tile; - void finalize_buffer(vector &coverage, const int pass_offset); - void flatten_buffer(vector &coverage, const int pass_offset); - void sort_buffer(const int pass_offset); -}; - -CCL_NAMESPACE_END - -#endif /* __COVERAGE_H__ */ diff --git a/intern/cycles/render/film.cpp b/intern/cycles/render/film.cpp deleted file mode 100644 index 5df396394c4..00000000000 --- a/intern/cycles/render/film.cpp +++ /dev/null @@ -1,722 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "render/film.h" -#include "device/device.h" -#include "render/camera.h" -#include "render/integrator.h" -#include "render/mesh.h" -#include "render/scene.h" -#include "render/stats.h" -#include "render/tables.h" - -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_math.h" -#include "util/util_math_cdf.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -/* Pass */ - -static bool compare_pass_order(const Pass &a, const Pass &b) -{ - if (a.components == b.components) - return (a.type < b.type); - return (a.components > b.components); -} - -static NodeEnum *get_pass_type_enum() -{ - static NodeEnum pass_type_enum; - pass_type_enum.insert("combined", PASS_COMBINED); - pass_type_enum.insert("depth", PASS_DEPTH); - pass_type_enum.insert("normal", PASS_NORMAL); - pass_type_enum.insert("uv", PASS_UV); - pass_type_enum.insert("object_id", PASS_OBJECT_ID); - pass_type_enum.insert("material_id", PASS_MATERIAL_ID); - pass_type_enum.insert("motion", PASS_MOTION); - pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT); - pass_type_enum.insert("render_time", PASS_RENDER_TIME); - pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE); - pass_type_enum.insert("aov_color", PASS_AOV_COLOR); - pass_type_enum.insert("aov_value", PASS_AOV_VALUE); - pass_type_enum.insert("adaptive_aux_buffer", PASS_ADAPTIVE_AUX_BUFFER); - pass_type_enum.insert("sample_count", PASS_SAMPLE_COUNT); - pass_type_enum.insert("mist", PASS_MIST); - pass_type_enum.insert("emission", PASS_EMISSION); - pass_type_enum.insert("background", PASS_BACKGROUND); - pass_type_enum.insert("ambient_occlusion", PASS_AO); - pass_type_enum.insert("shadow", PASS_SHADOW); - pass_type_enum.insert("diffuse_direct", PASS_DIFFUSE_DIRECT); - pass_type_enum.insert("diffuse_indirect", PASS_DIFFUSE_INDIRECT); - pass_type_enum.insert("diffuse_color", PASS_DIFFUSE_COLOR); - pass_type_enum.insert("glossy_direct", PASS_GLOSSY_DIRECT); - pass_type_enum.insert("glossy_indirect", PASS_GLOSSY_INDIRECT); - pass_type_enum.insert("glossy_color", PASS_GLOSSY_COLOR); - pass_type_enum.insert("transmission_direct", PASS_TRANSMISSION_DIRECT); - pass_type_enum.insert("transmission_indirect", PASS_TRANSMISSION_INDIRECT); - pass_type_enum.insert("transmission_color", PASS_TRANSMISSION_COLOR); - pass_type_enum.insert("volume_direct", PASS_VOLUME_DIRECT); - pass_type_enum.insert("volume_indirect", PASS_VOLUME_INDIRECT); - pass_type_enum.insert("bake_primitive", PASS_BAKE_PRIMITIVE); - pass_type_enum.insert("bake_differential", PASS_BAKE_DIFFERENTIAL); - - return &pass_type_enum; -} - -NODE_DEFINE(Pass) -{ - NodeType *type = NodeType::add("pass", create); - - NodeEnum *pass_type_enum = get_pass_type_enum(); - SOCKET_ENUM(type, "Type", *pass_type_enum, PASS_COMBINED); - SOCKET_STRING(name, "Name", ustring()); - - return type; -} - -Pass::Pass() : Node(get_node_type()) -{ -} - -void Pass::add(PassType type, vector &passes, const char *name) -{ - for (size_t i = 0; i < passes.size(); i++) { - if (passes[i].type != type) { - continue; - } - - /* An empty name is used as a placeholder to signal that any pass of - * that type is fine (because the content always is the same). - * This is important to support divide_type: If the pass that has a - * divide_type is added first, a pass for divide_type with an empty - * name will be added. Then, if a matching pass with a name is later - * requested, the existing placeholder will be renamed to that. - * If the divide_type is explicitly allocated with a name first and - * then again as part of another pass, the second one will just be - * skipped because that type already exists. */ - - /* If no name is specified, any pass of the correct type will match. */ - if (name == NULL) { - return; - } - - /* If we already have a placeholder pass, rename that one. */ - if (passes[i].name.empty()) { - passes[i].name = name; - return; - } - - /* If neither existing nor requested pass have placeholder name, they - * must match. */ - if (name == passes[i].name) { - return; - } - } - - Pass pass; - - pass.type = type; - pass.filter = true; - pass.exposure = false; - pass.divide_type = PASS_NONE; - if (name) { - pass.name = name; - } - - switch (type) { - case PASS_NONE: - pass.components = 0; - break; - case PASS_COMBINED: - pass.components = 4; - pass.exposure = true; - break; - case PASS_DEPTH: - pass.components = 1; - pass.filter = false; - break; - case PASS_MIST: - pass.components = 1; - break; - case PASS_NORMAL: - pass.components = 4; - break; - case PASS_UV: - pass.components = 4; - break; - case PASS_MOTION: - pass.components = 4; - pass.divide_type = PASS_MOTION_WEIGHT; - break; - case PASS_MOTION_WEIGHT: - pass.components = 1; - break; - case PASS_OBJECT_ID: - case PASS_MATERIAL_ID: - pass.components = 1; - pass.filter = false; - break; - - case PASS_EMISSION: - case PASS_BACKGROUND: - pass.components = 4; - pass.exposure = true; - break; - case PASS_AO: - pass.components = 4; - break; - case PASS_SHADOW: - pass.components = 4; - pass.exposure = false; - break; - case PASS_LIGHT: - /* This isn't a real pass, used by baking to see whether - * light data is needed or not. - * - * Set components to 0 so pass sort below happens in a - * determined way. - */ - pass.components = 0; - break; - case PASS_RENDER_TIME: - /* This pass is handled entirely on the host side. */ - pass.components = 0; - break; - - case PASS_DIFFUSE_COLOR: - case PASS_GLOSSY_COLOR: - case PASS_TRANSMISSION_COLOR: - pass.components = 4; - break; - case PASS_DIFFUSE_DIRECT: - case PASS_DIFFUSE_INDIRECT: - pass.components = 4; - pass.exposure = true; - pass.divide_type = PASS_DIFFUSE_COLOR; - break; - case PASS_GLOSSY_DIRECT: - case PASS_GLOSSY_INDIRECT: - pass.components = 4; - pass.exposure = true; - pass.divide_type = PASS_GLOSSY_COLOR; - break; - case PASS_TRANSMISSION_DIRECT: - case PASS_TRANSMISSION_INDIRECT: - pass.components = 4; - pass.exposure = true; - pass.divide_type = PASS_TRANSMISSION_COLOR; - break; - case PASS_VOLUME_DIRECT: - case PASS_VOLUME_INDIRECT: - pass.components = 4; - pass.exposure = true; - break; - case PASS_CRYPTOMATTE: - pass.components = 4; - break; - case PASS_ADAPTIVE_AUX_BUFFER: - pass.components = 4; - break; - case PASS_SAMPLE_COUNT: - pass.components = 1; - pass.exposure = false; - break; - case PASS_AOV_COLOR: - pass.components = 4; - break; - case PASS_AOV_VALUE: - pass.components = 1; - break; - case PASS_BAKE_PRIMITIVE: - case PASS_BAKE_DIFFERENTIAL: - pass.components = 4; - pass.exposure = false; - pass.filter = false; - break; - default: - assert(false); - break; - } - - passes.push_back(pass); - - /* Order from by components, to ensure alignment so passes with size 4 - * come first and then passes with size 1. Note this must use stable sort - * so cryptomatte passes remain in the right order. */ - stable_sort(&passes[0], &passes[0] + passes.size(), compare_pass_order); - - if (pass.divide_type != PASS_NONE) - Pass::add(pass.divide_type, passes); -} - -bool Pass::equals(const vector &A, const vector &B) -{ - if (A.size() != B.size()) - return false; - - for (int i = 0; i < A.size(); i++) - if (A[i].type != B[i].type || A[i].name != B[i].name) - return false; - - return true; -} - -bool Pass::contains(const vector &passes, PassType type) -{ - for (size_t i = 0; i < passes.size(); i++) - if (passes[i].type == type) - return true; - - return false; -} - -/* Pixel Filter */ - -static float filter_func_box(float /*v*/, float /*width*/) -{ - return 1.0f; -} - -static float filter_func_gaussian(float v, float width) -{ - v *= 6.0f / width; - return expf(-2.0f * v * v); -} - -static float filter_func_blackman_harris(float v, float width) -{ - v = M_2PI_F * (v / width + 0.5f); - return 0.35875f - 0.48829f * cosf(v) + 0.14128f * cosf(2.0f * v) - 0.01168f * cosf(3.0f * v); -} - -static vector filter_table(FilterType type, float width) -{ - vector filter_table(FILTER_TABLE_SIZE); - float (*filter_func)(float, float) = NULL; - - switch (type) { - case FILTER_BOX: - filter_func = filter_func_box; - break; - case FILTER_GAUSSIAN: - filter_func = filter_func_gaussian; - width *= 3.0f; - break; - case FILTER_BLACKMAN_HARRIS: - filter_func = filter_func_blackman_harris; - width *= 2.0f; - break; - default: - assert(0); - } - - /* Create importance sampling table. */ - - /* TODO(sergey): With the even filter table size resolution we can not - * really make it nice symmetric importance map without sampling full range - * (meaning, we would need to sample full filter range and not use the - * make_symmetric argument). - * - * Current code matches exactly initial filter table code, but we should - * consider either making FILTER_TABLE_SIZE odd value or sample full filter. - */ - - util_cdf_inverted(FILTER_TABLE_SIZE, - 0.0f, - width * 0.5f, - function_bind(filter_func, _1, width), - true, - filter_table); - - return filter_table; -} - -/* Film */ - -NODE_DEFINE(Film) -{ - NodeType *type = NodeType::add("film", create); - - SOCKET_FLOAT(exposure, "Exposure", 0.8f); - SOCKET_FLOAT(pass_alpha_threshold, "Pass Alpha Threshold", 0.0f); - - static NodeEnum filter_enum; - filter_enum.insert("box", FILTER_BOX); - filter_enum.insert("gaussian", FILTER_GAUSSIAN); - filter_enum.insert("blackman_harris", FILTER_BLACKMAN_HARRIS); - - SOCKET_ENUM(filter_type, "Filter Type", filter_enum, FILTER_BOX); - SOCKET_FLOAT(filter_width, "Filter Width", 1.0f); - - SOCKET_FLOAT(mist_start, "Mist Start", 0.0f); - SOCKET_FLOAT(mist_depth, "Mist Depth", 100.0f); - SOCKET_FLOAT(mist_falloff, "Mist Falloff", 1.0f); - - SOCKET_BOOLEAN(denoising_data_pass, "Generate Denoising Data Pass", false); - SOCKET_BOOLEAN(denoising_clean_pass, "Generate Denoising Clean Pass", false); - SOCKET_BOOLEAN(denoising_prefiltered_pass, "Generate Denoising Prefiltered Pass", false); - SOCKET_INT(denoising_flags, "Denoising Flags", 0); - SOCKET_BOOLEAN(use_adaptive_sampling, "Use Adaptive Sampling", false); - - SOCKET_BOOLEAN(use_light_visibility, "Use Light Visibility", false); - - NodeEnum *pass_type_enum = get_pass_type_enum(); - SOCKET_ENUM(display_pass, "Display Pass", *pass_type_enum, PASS_COMBINED); - - static NodeEnum cryptomatte_passes_enum; - cryptomatte_passes_enum.insert("none", CRYPT_NONE); - cryptomatte_passes_enum.insert("object", CRYPT_OBJECT); - cryptomatte_passes_enum.insert("material", CRYPT_MATERIAL); - cryptomatte_passes_enum.insert("asset", CRYPT_ASSET); - cryptomatte_passes_enum.insert("accurate", CRYPT_ACCURATE); - SOCKET_ENUM(cryptomatte_passes, "Cryptomatte Passes", cryptomatte_passes_enum, CRYPT_NONE); - - SOCKET_INT(cryptomatte_depth, "Cryptomatte Depth", 0); - - return type; -} - -Film::Film() : Node(get_node_type()) -{ - use_light_visibility = false; - filter_table_offset = TABLE_OFFSET_INVALID; - cryptomatte_passes = CRYPT_NONE; - display_pass = PASS_COMBINED; -} - -Film::~Film() -{ -} - -void Film::add_default(Scene *scene) -{ - Pass::add(PASS_COMBINED, scene->passes); -} - -void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) -{ - if (!is_modified()) - return; - - scoped_callback_timer timer([scene](double time) { - if (scene->update_stats) { - scene->update_stats->film.times.add_entry({"update", time}); - } - }); - - device_free(device, dscene, scene); - - KernelFilm *kfilm = &dscene->data.film; - - /* update __data */ - kfilm->exposure = exposure; - kfilm->pass_flag = 0; - - kfilm->display_pass_stride = -1; - kfilm->display_pass_components = 0; - kfilm->display_divide_pass_stride = -1; - kfilm->use_display_exposure = false; - kfilm->use_display_pass_alpha = (display_pass == PASS_COMBINED); - - kfilm->light_pass_flag = 0; - kfilm->pass_stride = 0; - kfilm->use_light_pass = use_light_visibility; - kfilm->pass_aov_value_num = 0; - kfilm->pass_aov_color_num = 0; - - bool have_cryptomatte = false; - - for (size_t i = 0; i < scene->passes.size(); i++) { - Pass &pass = scene->passes[i]; - - if (pass.type == PASS_NONE) { - continue; - } - - /* Can't do motion pass if no motion vectors are available. */ - if (pass.type == PASS_MOTION || pass.type == PASS_MOTION_WEIGHT) { - if (scene->need_motion() != Scene::MOTION_PASS) { - kfilm->pass_stride += pass.components; - continue; - } - } - - int pass_flag = (1 << (pass.type % 32)); - if (pass.type <= PASS_CATEGORY_MAIN_END) { - kfilm->pass_flag |= pass_flag; - } - else if (pass.type <= PASS_CATEGORY_LIGHT_END) { - kfilm->use_light_pass = 1; - kfilm->light_pass_flag |= pass_flag; - } - else { - assert(pass.type <= PASS_CATEGORY_BAKE_END); - } - - switch (pass.type) { - case PASS_COMBINED: - kfilm->pass_combined = kfilm->pass_stride; - break; - case PASS_DEPTH: - kfilm->pass_depth = kfilm->pass_stride; - break; - case PASS_NORMAL: - kfilm->pass_normal = kfilm->pass_stride; - break; - case PASS_UV: - kfilm->pass_uv = kfilm->pass_stride; - break; - case PASS_MOTION: - kfilm->pass_motion = kfilm->pass_stride; - break; - case PASS_MOTION_WEIGHT: - kfilm->pass_motion_weight = kfilm->pass_stride; - break; - case PASS_OBJECT_ID: - kfilm->pass_object_id = kfilm->pass_stride; - break; - case PASS_MATERIAL_ID: - kfilm->pass_material_id = kfilm->pass_stride; - break; - - case PASS_MIST: - kfilm->pass_mist = kfilm->pass_stride; - break; - case PASS_EMISSION: - kfilm->pass_emission = kfilm->pass_stride; - break; - case PASS_BACKGROUND: - kfilm->pass_background = kfilm->pass_stride; - break; - case PASS_AO: - kfilm->pass_ao = kfilm->pass_stride; - break; - case PASS_SHADOW: - kfilm->pass_shadow = kfilm->pass_stride; - break; - - case PASS_LIGHT: - break; - - case PASS_DIFFUSE_COLOR: - kfilm->pass_diffuse_color = kfilm->pass_stride; - break; - case PASS_GLOSSY_COLOR: - kfilm->pass_glossy_color = kfilm->pass_stride; - break; - case PASS_TRANSMISSION_COLOR: - kfilm->pass_transmission_color = kfilm->pass_stride; - break; - case PASS_DIFFUSE_INDIRECT: - kfilm->pass_diffuse_indirect = kfilm->pass_stride; - break; - case PASS_GLOSSY_INDIRECT: - kfilm->pass_glossy_indirect = kfilm->pass_stride; - break; - case PASS_TRANSMISSION_INDIRECT: - kfilm->pass_transmission_indirect = kfilm->pass_stride; - break; - case PASS_VOLUME_INDIRECT: - kfilm->pass_volume_indirect = kfilm->pass_stride; - break; - case PASS_DIFFUSE_DIRECT: - kfilm->pass_diffuse_direct = kfilm->pass_stride; - break; - case PASS_GLOSSY_DIRECT: - kfilm->pass_glossy_direct = kfilm->pass_stride; - break; - case PASS_TRANSMISSION_DIRECT: - kfilm->pass_transmission_direct = kfilm->pass_stride; - break; - case PASS_VOLUME_DIRECT: - kfilm->pass_volume_direct = kfilm->pass_stride; - break; - - case PASS_BAKE_PRIMITIVE: - kfilm->pass_bake_primitive = kfilm->pass_stride; - break; - case PASS_BAKE_DIFFERENTIAL: - kfilm->pass_bake_differential = kfilm->pass_stride; - break; - - case PASS_RENDER_TIME: - break; - case PASS_CRYPTOMATTE: - kfilm->pass_cryptomatte = have_cryptomatte ? - min(kfilm->pass_cryptomatte, kfilm->pass_stride) : - kfilm->pass_stride; - have_cryptomatte = true; - break; - case PASS_ADAPTIVE_AUX_BUFFER: - kfilm->pass_adaptive_aux_buffer = kfilm->pass_stride; - break; - case PASS_SAMPLE_COUNT: - kfilm->pass_sample_count = kfilm->pass_stride; - break; - case PASS_AOV_COLOR: - if (kfilm->pass_aov_color_num == 0) { - kfilm->pass_aov_color = kfilm->pass_stride; - } - kfilm->pass_aov_color_num++; - break; - case PASS_AOV_VALUE: - if (kfilm->pass_aov_value_num == 0) { - kfilm->pass_aov_value = kfilm->pass_stride; - } - kfilm->pass_aov_value_num++; - break; - default: - assert(false); - break; - } - - if (pass.type == display_pass) { - kfilm->display_pass_stride = kfilm->pass_stride; - kfilm->display_pass_components = pass.components; - kfilm->use_display_exposure = pass.exposure && (kfilm->exposure != 1.0f); - } - else if (pass.type == PASS_DIFFUSE_COLOR || pass.type == PASS_TRANSMISSION_COLOR || - pass.type == PASS_GLOSSY_COLOR) { - kfilm->display_divide_pass_stride = kfilm->pass_stride; - } - - kfilm->pass_stride += pass.components; - } - - kfilm->pass_denoising_data = 0; - kfilm->pass_denoising_clean = 0; - kfilm->denoising_flags = 0; - if (denoising_data_pass) { - kfilm->pass_denoising_data = kfilm->pass_stride; - kfilm->pass_stride += DENOISING_PASS_SIZE_BASE; - kfilm->denoising_flags = denoising_flags; - if (denoising_clean_pass) { - kfilm->pass_denoising_clean = kfilm->pass_stride; - kfilm->pass_stride += DENOISING_PASS_SIZE_CLEAN; - kfilm->use_light_pass = 1; - } - if (denoising_prefiltered_pass) { - kfilm->pass_stride += DENOISING_PASS_SIZE_PREFILTERED; - } - } - - kfilm->pass_stride = align_up(kfilm->pass_stride, 4); - - /* When displaying the normal/uv pass in the viewport we need to disable - * transparency. - * - * We also don't need to perform light accumulations. Later we want to optimize this to suppress - * light calculations. */ - if (display_pass == PASS_NORMAL || display_pass == PASS_UV) { - kfilm->use_light_pass = 0; - } - else { - kfilm->pass_alpha_threshold = pass_alpha_threshold; - } - - /* update filter table */ - vector table = filter_table(filter_type, filter_width); - scene->lookup_tables->remove_table(&filter_table_offset); - filter_table_offset = scene->lookup_tables->add_table(dscene, table); - kfilm->filter_table_offset = (int)filter_table_offset; - - /* mist pass parameters */ - kfilm->mist_start = mist_start; - kfilm->mist_inv_depth = (mist_depth > 0.0f) ? 1.0f / mist_depth : 0.0f; - kfilm->mist_falloff = mist_falloff; - - kfilm->cryptomatte_passes = cryptomatte_passes; - kfilm->cryptomatte_depth = cryptomatte_depth; - - pass_stride = kfilm->pass_stride; - denoising_data_offset = kfilm->pass_denoising_data; - denoising_clean_offset = kfilm->pass_denoising_clean; - - clear_modified(); -} - -void Film::device_free(Device * /*device*/, DeviceScene * /*dscene*/, Scene *scene) -{ - scene->lookup_tables->remove_table(&filter_table_offset); -} - -void Film::tag_passes_update(Scene *scene, const vector &passes_, bool update_passes) -{ - if (Pass::contains(scene->passes, PASS_UV) != Pass::contains(passes_, PASS_UV)) { - scene->geometry_manager->tag_update(scene, GeometryManager::UV_PASS_NEEDED); - - foreach (Shader *shader, scene->shaders) - shader->need_update_uvs = true; - } - else if (Pass::contains(scene->passes, PASS_MOTION) != Pass::contains(passes_, PASS_MOTION)) { - scene->geometry_manager->tag_update(scene, GeometryManager::MOTION_PASS_NEEDED); - } - else if (Pass::contains(scene->passes, PASS_AO) != Pass::contains(passes_, PASS_AO)) { - scene->integrator->tag_update(scene, Integrator::AO_PASS_MODIFIED); - } - - if (update_passes) { - scene->passes = passes_; - } -} - -int Film::get_aov_offset(Scene *scene, string name, bool &is_color) -{ - int num_color = 0, num_value = 0; - foreach (const Pass &pass, scene->passes) { - if (pass.type == PASS_AOV_COLOR) { - num_color++; - } - else if (pass.type == PASS_AOV_VALUE) { - num_value++; - } - else { - continue; - } - - if (pass.name == name) { - is_color = (pass.type == PASS_AOV_COLOR); - return (is_color ? num_color : num_value) - 1; - } - } - - return -1; -} - -int Film::get_pass_stride() const -{ - return pass_stride; -} - -int Film::get_denoising_data_offset() const -{ - return denoising_data_offset; -} - -int Film::get_denoising_clean_offset() const -{ - return denoising_clean_offset; -} - -size_t Film::get_filter_table_offset() const -{ - return filter_table_offset; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/session.cpp b/intern/cycles/render/session.cpp deleted file mode 100644 index 1b91c49f0ea..00000000000 --- a/intern/cycles/render/session.cpp +++ /dev/null @@ -1,1289 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include "device/device.h" -#include "render/bake.h" -#include "render/buffers.h" -#include "render/camera.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/session.h" - -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_logging.h" -#include "util/util_math.h" -#include "util/util_opengl.h" -#include "util/util_task.h" -#include "util/util_time.h" - -CCL_NAMESPACE_BEGIN - -/* Note about preserve_tile_device option for tile manager: - * progressive refine and viewport rendering does requires tiles to - * always be allocated for the same device - */ -Session::Session(const SessionParams ¶ms_) - : params(params_), - tile_manager(params.progressive, - params.samples, - params.tile_size, - params.start_resolution, - params.background == false || params.progressive_refine, - params.background, - params.tile_order, - max(params.device.multi_devices.size(), 1), - params.pixel_size), - stats(), - profiler() -{ - device_use_gl_ = ((params.device.type != DEVICE_CPU) && !params.background); - - TaskScheduler::init(params.threads); - - session_thread_ = NULL; - scene = NULL; - - reset_time_ = 0.0; - last_update_time_ = 0.0; - - delayed_reset_.do_reset = false; - delayed_reset_.samples = 0; - - display_outdated_ = false; - gpu_draw_ready_ = false; - gpu_need_display_buffer_update_ = false; - - pause_ = false; - cancel_ = false; - new_work_added_ = false; - - buffers = NULL; - display = NULL; - - /* Validate denoising parameters. */ - set_denoising(params.denoising); - - /* Create CPU/GPU devices. */ - device = Device::create(params.device, stats, profiler, params.background); - - if (!device->error_message().empty()) { - progress.set_error(device->error_message()); - return; - } - - /* Create buffers for interactive rendering. */ - if (!(params.background && !params.write_render_cb)) { - buffers = new RenderBuffers(device); - display = new DisplayBuffer(device, params.display_buffer_linear); - } -} - -Session::~Session() -{ - cancel(); - - if (buffers && params.write_render_cb) { - /* Copy to display buffer and write out image if requested */ - delete display; - - display = new DisplayBuffer(device, false); - display->reset(buffers->params); - copy_to_display_buffer(params.samples); - - int w = display->draw_width; - int h = display->draw_height; - uchar4 *pixels = display->rgba_byte.copy_from_device(0, w, h); - params.write_render_cb((uchar *)pixels, w, h, 4); - } - - /* clean up */ - tile_manager.device_free(); - - delete buffers; - delete display; - delete scene; - delete device; - - TaskScheduler::exit(); -} - -void Session::start() -{ - if (!session_thread_) { - session_thread_ = new thread(function_bind(&Session::run, this)); - } -} - -void Session::cancel() -{ - if (session_thread_) { - /* wait for session thread to end */ - progress.set_cancel("Exiting"); - - gpu_need_display_buffer_update_ = false; - gpu_need_display_buffer_update_cond_.notify_all(); - - { - thread_scoped_lock pause_lock(pause_mutex_); - pause_ = false; - cancel_ = true; - } - pause_cond_.notify_all(); - - wait(); - } -} - -bool Session::ready_to_reset() -{ - double dt = time_dt() - reset_time_; - - if (!display_outdated_) - return (dt > params.reset_timeout); - else - return (dt > params.cancel_timeout); -} - -/* GPU Session */ - -void Session::reset_gpu(BufferParams &buffer_params, int samples) -{ - thread_scoped_lock pause_lock(pause_mutex_); - - /* block for buffer access and reset immediately. we can't do this - * in the thread, because we need to allocate an OpenGL buffer, and - * that only works in the main thread */ - thread_scoped_lock display_lock(display_mutex_); - thread_scoped_lock buffers_lock(buffers_mutex_); - - display_outdated_ = true; - reset_time_ = time_dt(); - - reset_(buffer_params, samples); - - gpu_need_display_buffer_update_ = false; - gpu_need_display_buffer_update_cond_.notify_all(); - - new_work_added_ = true; - - pause_cond_.notify_all(); -} - -bool Session::draw_gpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) -{ - /* block for buffer access */ - thread_scoped_lock display_lock(display_mutex_); - - /* first check we already rendered something */ - if (gpu_draw_ready_) { - /* then verify the buffers have the expected size, so we don't - * draw previous results in a resized window */ - if (buffer_params.width == display->params.width && - buffer_params.height == display->params.height) { - /* for CUDA we need to do tone-mapping still, since we can - * only access GL buffers from the main thread. */ - if (gpu_need_display_buffer_update_) { - thread_scoped_lock buffers_lock(buffers_mutex_); - copy_to_display_buffer(tile_manager.state.sample); - gpu_need_display_buffer_update_ = false; - gpu_need_display_buffer_update_cond_.notify_all(); - } - - display->draw(device, draw_params); - - if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout) - return false; - - return true; - } - } - - return false; -} - -void Session::run_gpu() -{ - bool tiles_written = false; - - reset_time_ = time_dt(); - last_update_time_ = time_dt(); - last_display_time_ = last_update_time_; - - progress.set_render_start_time(); - - while (!progress.get_cancel()) { - const bool no_tiles = !run_update_for_next_iteration(); - - if (no_tiles) { - if (params.background) { - /* if no work left and in background mode, we can stop immediately */ - progress.set_status("Finished"); - break; - } - } - - if (run_wait_for_work(no_tiles)) { - continue; - } - - if (progress.get_cancel()) { - break; - } - - if (!no_tiles) { - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - if (progress.get_cancel()) - break; - - /* buffers mutex is locked entirely while rendering each - * sample, and released/reacquired on each iteration to allow - * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex_); - - /* update status and timing */ - update_status_time(); - - /* render */ - bool delayed_denoise = false; - const bool need_denoise = render_need_denoise(delayed_denoise); - render(need_denoise); - - device->task_wait(); - - if (!device->error_message().empty()) - progress.set_cancel(device->error_message()); - - /* update status and timing */ - update_status_time(); - - gpu_need_display_buffer_update_ = !delayed_denoise; - gpu_draw_ready_ = true; - progress.set_update(); - - /* wait for until display buffer is updated */ - if (!params.background) { - while (gpu_need_display_buffer_update_) { - if (progress.get_cancel()) - break; - - gpu_need_display_buffer_update_cond_.wait(buffers_lock); - } - } - - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - tiles_written = update_progressive_refine(progress.get_cancel()); - - if (progress.get_cancel()) - break; - } - } - - if (!tiles_written) - update_progressive_refine(true); -} - -/* CPU Session */ - -void Session::reset_cpu(BufferParams &buffer_params, int samples) -{ - thread_scoped_lock reset_lock(delayed_reset_.mutex); - thread_scoped_lock pause_lock(pause_mutex_); - - display_outdated_ = true; - reset_time_ = time_dt(); - - delayed_reset_.params = buffer_params; - delayed_reset_.samples = samples; - delayed_reset_.do_reset = true; - device->task_cancel(); - - pause_cond_.notify_all(); -} - -bool Session::draw_cpu(BufferParams &buffer_params, DeviceDrawParams &draw_params) -{ - thread_scoped_lock display_lock(display_mutex_); - - /* first check we already rendered something */ - if (display->draw_ready()) { - /* then verify the buffers have the expected size, so we don't - * draw previous results in a resized window */ - if (buffer_params.width == display->params.width && - buffer_params.height == display->params.height) { - display->draw(device, draw_params); - - if (display_outdated_ && (time_dt() - reset_time_) > params.text_timeout) - return false; - - return true; - } - } - - return false; -} - -bool Session::steal_tile(RenderTile &rtile, Device *tile_device, thread_scoped_lock &tile_lock) -{ - /* Devices that can get their tiles stolen don't steal tiles themselves. - * Additionally, if there are no stealable tiles in flight, give up here. */ - if (tile_device->info.type == DEVICE_CPU || stealable_tiles_ == 0) { - return false; - } - - /* Wait until no other thread is trying to steal a tile. */ - while (tile_stealing_state_ != NOT_STEALING && stealable_tiles_ > 0) { - /* Someone else is currently trying to get a tile. - * Wait on the condition variable and try later. */ - tile_steal_cond_.wait(tile_lock); - } - /* If another thread stole the last stealable tile in the meantime, give up. */ - if (stealable_tiles_ == 0) { - return false; - } - - /* There are stealable tiles in flight, so signal that one should be released. */ - tile_stealing_state_ = WAITING_FOR_TILE; - - /* Wait until a device notices the signal and releases its tile. */ - while (tile_stealing_state_ != GOT_TILE && stealable_tiles_ > 0) { - tile_steal_cond_.wait(tile_lock); - } - /* If the last stealable tile finished on its own, give up. */ - if (tile_stealing_state_ != GOT_TILE) { - tile_stealing_state_ = NOT_STEALING; - return false; - } - - /* Successfully stole a tile, now move it to the new device. */ - rtile = stolen_tile_; - rtile.buffers->buffer.move_device(tile_device); - rtile.buffer = rtile.buffers->buffer.device_pointer; - rtile.stealing_state = RenderTile::NO_STEALING; - rtile.num_samples -= (rtile.sample - rtile.start_sample); - rtile.start_sample = rtile.sample; - - tile_stealing_state_ = NOT_STEALING; - - /* Poke any threads which might be waiting for NOT_STEALING above. */ - tile_steal_cond_.notify_one(); - - return true; -} - -bool Session::get_tile_stolen() -{ - /* If tile_stealing_state is WAITING_FOR_TILE, atomically set it to RELEASING_TILE - * and return true. */ - TileStealingState expected = WAITING_FOR_TILE; - return tile_stealing_state_.compare_exchange_weak(expected, RELEASING_TILE); -} - -bool Session::acquire_tile(RenderTile &rtile, Device *tile_device, uint tile_types) -{ - if (progress.get_cancel()) { - if (params.progressive_refine == false) { - /* for progressive refine current sample should be finished for all tiles */ - return false; - } - } - - thread_scoped_lock tile_lock(tile_mutex_); - - /* get next tile from manager */ - Tile *tile; - int device_num = device->device_number(tile_device); - - while (!tile_manager.next_tile(tile, device_num, tile_types)) { - /* Can only steal tiles on devices that support rendering - * This is because denoising tiles cannot be stolen (see below) - */ - if ((tile_types & (RenderTile::PATH_TRACE | RenderTile::BAKE)) && - steal_tile(rtile, tile_device, tile_lock)) { - return true; - } - - /* Wait for denoising tiles to become available */ - if ((tile_types & RenderTile::DENOISE) && !progress.get_cancel() && tile_manager.has_tiles()) { - denoising_cond_.wait(tile_lock); - continue; - } - - return false; - } - - /* fill render tile */ - rtile.x = tile_manager.state.buffer.full_x + tile->x; - rtile.y = tile_manager.state.buffer.full_y + tile->y; - rtile.w = tile->w; - rtile.h = tile->h; - rtile.start_sample = tile_manager.state.sample; - rtile.num_samples = tile_manager.state.num_samples; - rtile.resolution = tile_manager.state.resolution_divider; - rtile.tile_index = tile->index; - rtile.stealing_state = RenderTile::NO_STEALING; - - if (tile->state == Tile::DENOISE) { - rtile.task = RenderTile::DENOISE; - } - else { - if (tile_device->info.type == DEVICE_CPU) { - stealable_tiles_++; - rtile.stealing_state = RenderTile::CAN_BE_STOLEN; - } - - if (read_bake_tile_cb) { - rtile.task = RenderTile::BAKE; - } - else { - rtile.task = RenderTile::PATH_TRACE; - } - } - - tile_lock.unlock(); - - /* in case of a permanent buffer, return it, otherwise we will allocate - * a new temporary buffer */ - if (buffers) { - tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = buffers->buffer.device_pointer; - rtile.buffers = buffers; - - device->map_tile(tile_device, rtile); - - /* Reset copy state, since buffer contents change after the tile was acquired */ - buffers->map_neighbor_copied = false; - - /* This hack ensures that the copy in 'MultiDevice::map_neighbor_tiles' accounts - * for the buffer resolution divider. */ - buffers->buffer.data_width = (buffers->params.width * buffers->params.get_passes_size()) / - tile_manager.state.resolution_divider; - buffers->buffer.data_height = buffers->params.height / tile_manager.state.resolution_divider; - - return true; - } - - if (tile->buffers == NULL) { - /* fill buffer parameters */ - BufferParams buffer_params = tile_manager.params; - buffer_params.full_x = rtile.x; - buffer_params.full_y = rtile.y; - buffer_params.width = rtile.w; - buffer_params.height = rtile.h; - - /* allocate buffers */ - tile->buffers = new RenderBuffers(tile_device); - tile->buffers->reset(buffer_params); - } - else if (tile->buffers->buffer.device != tile_device) { - /* Move buffer to current tile device again in case it was stolen before. - * Not needed for denoising since that already handles mapping of tiles and - * neighbors to its own device. */ - if (rtile.task != RenderTile::DENOISE) { - tile->buffers->buffer.move_device(tile_device); - } - } - - tile->buffers->map_neighbor_copied = false; - - tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = tile->buffers->buffer.device_pointer; - rtile.buffers = tile->buffers; - rtile.sample = tile_manager.state.sample; - - if (read_bake_tile_cb) { - /* This will read any passes needed as input for baking. */ - if (tile_manager.state.sample == tile_manager.range_start_sample) { - { - thread_scoped_lock tile_lock(tile_mutex_); - read_bake_tile_cb(rtile); - } - rtile.buffers->buffer.copy_to_device(); - } - } - else { - /* This will tag tile as IN PROGRESS in blender-side render pipeline, - * which is needed to highlight currently rendering tile before first - * sample was processed for it. */ - update_tile_sample(rtile); - } - - return true; -} - -void Session::update_tile_sample(RenderTile &rtile) -{ - thread_scoped_lock tile_lock(tile_mutex_); - - if (update_render_tile_cb) { - if (params.progressive_refine == false) { - /* todo: optimize this by making it thread safe and removing lock */ - - update_render_tile_cb(rtile, true); - } - } - - update_status_time(); -} - -void Session::release_tile(RenderTile &rtile, const bool need_denoise) -{ - thread_scoped_lock tile_lock(tile_mutex_); - - if (rtile.stealing_state != RenderTile::NO_STEALING) { - stealable_tiles_--; - if (rtile.stealing_state == RenderTile::WAS_STOLEN) { - /* If the tile is being stolen, don't release it here - the new device will pick up where - * the old one left off. */ - - assert(tile_stealing_state_ == RELEASING_TILE); - assert(rtile.sample < rtile.start_sample + rtile.num_samples); - - tile_stealing_state_ = GOT_TILE; - stolen_tile_ = rtile; - tile_steal_cond_.notify_all(); - return; - } - else if (stealable_tiles_ == 0) { - /* If this was the last stealable tile, wake up any threads still waiting for one. */ - tile_steal_cond_.notify_all(); - } - } - - progress.add_finished_tile(rtile.task == RenderTile::DENOISE); - - bool delete_tile; - - if (tile_manager.finish_tile(rtile.tile_index, need_denoise, delete_tile)) { - /* Finished tile pixels write. */ - if (write_render_tile_cb && params.progressive_refine == false) { - write_render_tile_cb(rtile); - } - - if (delete_tile) { - delete rtile.buffers; - tile_manager.state.tiles[rtile.tile_index].buffers = NULL; - } - } - else { - /* In progress tile pixels update. */ - if (update_render_tile_cb && params.progressive_refine == false) { - update_render_tile_cb(rtile, false); - } - } - - update_status_time(); - - /* Notify denoising thread that a tile was finished. */ - denoising_cond_.notify_all(); -} - -void Session::map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) -{ - thread_scoped_lock tile_lock(tile_mutex_); - - const int4 image_region = make_int4( - tile_manager.state.buffer.full_x, - tile_manager.state.buffer.full_y, - tile_manager.state.buffer.full_x + tile_manager.state.buffer.width, - tile_manager.state.buffer.full_y + tile_manager.state.buffer.height); - - RenderTile ¢er_tile = neighbors.tiles[RenderTileNeighbors::CENTER]; - - if (!tile_manager.schedule_denoising) { - /* Fix up tile slices with overlap. */ - if (tile_manager.slice_overlap != 0) { - int y = max(center_tile.y - tile_manager.slice_overlap, image_region.y); - center_tile.h = min(center_tile.y + center_tile.h + tile_manager.slice_overlap, - image_region.w) - - y; - center_tile.y = y; - } - - /* Tiles are not being denoised individually, which means the entire image is processed. */ - neighbors.set_bounds_from_center(); - } - else { - int center_idx = center_tile.tile_index; - assert(tile_manager.state.tiles[center_idx].state == Tile::DENOISE); - - for (int dy = -1, i = 0; dy <= 1; dy++) { - for (int dx = -1; dx <= 1; dx++, i++) { - RenderTile &rtile = neighbors.tiles[i]; - int nindex = tile_manager.get_neighbor_index(center_idx, i); - if (nindex >= 0) { - Tile *tile = &tile_manager.state.tiles[nindex]; - - rtile.x = image_region.x + tile->x; - rtile.y = image_region.y + tile->y; - rtile.w = tile->w; - rtile.h = tile->h; - - if (buffers) { - tile_manager.state.buffer.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = buffers->buffer.device_pointer; - rtile.buffers = buffers; - } - else { - assert(tile->buffers); - tile->buffers->params.get_offset_stride(rtile.offset, rtile.stride); - - rtile.buffer = tile->buffers->buffer.device_pointer; - rtile.buffers = tile->buffers; - } - } - else { - int px = center_tile.x + dx * params.tile_size.x; - int py = center_tile.y + dy * params.tile_size.y; - - rtile.x = clamp(px, image_region.x, image_region.z); - rtile.y = clamp(py, image_region.y, image_region.w); - rtile.w = rtile.h = 0; - - rtile.buffer = (device_ptr)NULL; - rtile.buffers = NULL; - } - } - } - } - - assert(center_tile.buffers); - device->map_neighbor_tiles(tile_device, neighbors); - - /* The denoised result is written back to the original tile. */ - neighbors.target = center_tile; -} - -void Session::unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device) -{ - thread_scoped_lock tile_lock(tile_mutex_); - device->unmap_neighbor_tiles(tile_device, neighbors); -} - -void Session::run_cpu() -{ - bool tiles_written = false; - - last_update_time_ = time_dt(); - last_display_time_ = last_update_time_; - - while (!progress.get_cancel()) { - const bool no_tiles = !run_update_for_next_iteration(); - bool need_copy_to_display_buffer = false; - - if (no_tiles) { - if (params.background) { - /* if no work left and in background mode, we can stop immediately */ - progress.set_status("Finished"); - break; - } - } - - if (run_wait_for_work(no_tiles)) { - continue; - } - - if (progress.get_cancel()) { - break; - } - - if (!no_tiles) { - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - if (progress.get_cancel()) - break; - - /* buffers mutex is locked entirely while rendering each - * sample, and released/reacquired on each iteration to allow - * reset and draw in between */ - thread_scoped_lock buffers_lock(buffers_mutex_); - - /* update status and timing */ - update_status_time(); - - /* render */ - bool delayed_denoise = false; - const bool need_denoise = render_need_denoise(delayed_denoise); - render(need_denoise); - - /* update status and timing */ - update_status_time(); - - if (!params.background) - need_copy_to_display_buffer = !delayed_denoise; - - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - } - - device->task_wait(); - - { - thread_scoped_lock reset_lock(delayed_reset_.mutex); - thread_scoped_lock buffers_lock(buffers_mutex_); - thread_scoped_lock display_lock(display_mutex_); - - if (delayed_reset_.do_reset) { - /* reset rendering if request from main thread */ - delayed_reset_.do_reset = false; - reset_(delayed_reset_.params, delayed_reset_.samples); - } - else if (need_copy_to_display_buffer) { - /* Only copy to display_buffer if we do not reset, we don't - * want to show the result of an incomplete sample */ - copy_to_display_buffer(tile_manager.state.sample); - } - - if (!device->error_message().empty()) - progress.set_error(device->error_message()); - - tiles_written = update_progressive_refine(progress.get_cancel()); - } - - progress.set_update(); - } - - if (!tiles_written) - update_progressive_refine(true); -} - -void Session::run() -{ - if (params.use_profiling && (params.device.type == DEVICE_CPU)) { - profiler.start(); - } - - /* session thread loop */ - progress.set_status("Waiting for render to start"); - - /* run */ - if (!progress.get_cancel()) { - /* reset number of rendered samples */ - progress.reset_sample(); - - if (device_use_gl_) - run_gpu(); - else - run_cpu(); - } - - profiler.stop(); - - /* progress update */ - if (progress.get_cancel()) - progress.set_status(progress.get_cancel_message()); - else - progress.set_update(); -} - -bool Session::run_update_for_next_iteration() -{ - thread_scoped_lock scene_lock(scene->mutex); - thread_scoped_lock reset_lock(delayed_reset_.mutex); - - if (delayed_reset_.do_reset) { - thread_scoped_lock buffers_lock(buffers_mutex_); - reset_(delayed_reset_.params, delayed_reset_.samples); - delayed_reset_.do_reset = false; - } - - const bool have_tiles = tile_manager.next(); - - if (have_tiles) { - scoped_timer update_timer; - if (update_scene()) { - profiler.reset(scene->shaders.size(), scene->objects.size()); - } - progress.add_skip_time(update_timer, params.background); - } - - return have_tiles; -} - -bool Session::run_wait_for_work(bool no_tiles) -{ - /* In an offline rendering there is no pause, and no tiles will mean the job is fully done. */ - if (params.background) { - return false; - } - - thread_scoped_lock pause_lock(pause_mutex_); - - if (!pause_ && !no_tiles) { - /* Rendering is not paused and there is work to be done. No need to wait for anything. */ - return false; - } - - update_status_time(pause_, no_tiles); - - /* Only leave the loop when rendering is not paused. But even if the current render is un-paused - * but there is nothing to render keep waiting until new work is added. */ - while (!cancel_) { - scoped_timer pause_timer; - - if (!pause_ && (!no_tiles || new_work_added_ || delayed_reset_.do_reset)) { - break; - } - - /* Wait for either pause state changed, or extra samples added to render. */ - pause_cond_.wait(pause_lock); - - if (pause_) { - progress.add_skip_time(pause_timer, params.background); - } - - update_status_time(pause_, no_tiles); - progress.set_update(); - } - - new_work_added_ = false; - - return no_tiles; -} - -bool Session::draw(BufferParams &buffer_params, DeviceDrawParams &draw_params) -{ - if (device_use_gl_) - return draw_gpu(buffer_params, draw_params); - else - return draw_cpu(buffer_params, draw_params); -} - -void Session::reset_(BufferParams &buffer_params, int samples) -{ - if (buffers && buffer_params.modified(tile_manager.params)) { - gpu_draw_ready_ = false; - buffers->reset(buffer_params); - if (display) { - display->reset(buffer_params); - } - } - - tile_manager.reset(buffer_params, samples); - stealable_tiles_ = 0; - tile_stealing_state_ = NOT_STEALING; - progress.reset_sample(); - - bool show_progress = params.background || tile_manager.get_num_effective_samples() != INT_MAX; - progress.set_total_pixel_samples(show_progress ? tile_manager.state.total_pixel_samples : 0); - - if (!params.background) - progress.set_start_time(); - progress.set_render_start_time(); -} - -void Session::reset(BufferParams &buffer_params, int samples) -{ - if (device_use_gl_) - reset_gpu(buffer_params, samples); - else - reset_cpu(buffer_params, samples); -} - -void Session::set_samples(int samples) -{ - if (samples == params.samples) { - return; - } - - params.samples = samples; - tile_manager.set_samples(samples); - - { - thread_scoped_lock pause_lock(pause_mutex_); - new_work_added_ = true; - } - - pause_cond_.notify_all(); -} - -void Session::set_pause(bool pause) -{ - bool notify = false; - - { - thread_scoped_lock pause_lock(pause_mutex_); - - if (pause != pause_) { - pause_ = pause; - notify = true; - } - } - - if (session_thread_) { - if (notify) { - pause_cond_.notify_all(); - } - } - else if (pause_) { - update_status_time(pause_); - } -} - -void Session::set_denoising(const DenoiseParams &denoising) -{ - bool need_denoise = denoising.need_denoising_task(); - - /* Lock buffers so no denoising operation is triggered while the settings are changed here. */ - thread_scoped_lock buffers_lock(buffers_mutex_); - params.denoising = denoising; - - if (!(params.device.denoisers & denoising.type)) { - if (need_denoise) { - progress.set_error("Denoiser type not supported by compute device"); - } - - params.denoising.use = false; - need_denoise = false; - } - - // TODO(pmours): Query the required overlap value for denoising from the device? - tile_manager.slice_overlap = need_denoise && !params.background ? 64 : 0; - - /* Schedule per tile denoising for final renders if we are either denoising or - * need prefiltered passes for the native denoiser. */ - tile_manager.schedule_denoising = need_denoise && !buffers; -} - -void Session::set_denoising_start_sample(int sample) -{ - if (sample != params.denoising.start_sample) { - params.denoising.start_sample = sample; - - pause_cond_.notify_all(); - } -} - -void Session::wait() -{ - if (session_thread_) { - session_thread_->join(); - delete session_thread_; - } - - session_thread_ = NULL; -} - -bool Session::update_scene() -{ - /* update camera if dimensions changed for progressive render. the camera - * knows nothing about progressive or cropped rendering, it just gets the - * image dimensions passed in */ - Camera *cam = scene->camera; - int width = tile_manager.state.buffer.full_width; - int height = tile_manager.state.buffer.full_height; - int resolution = tile_manager.state.resolution_divider; - - cam->set_screen_size_and_resolution(width, height, resolution); - - /* number of samples is needed by multi jittered - * sampling pattern and by baking */ - Integrator *integrator = scene->integrator; - BakeManager *bake_manager = scene->bake_manager; - - if (integrator->get_sampling_pattern() != SAMPLING_PATTERN_SOBOL || bake_manager->get_baking()) { - integrator->set_aa_samples(tile_manager.num_samples); - } - - bool kernel_switch_needed = false; - if (scene->update(progress, kernel_switch_needed)) { - if (kernel_switch_needed) { - reset(tile_manager.params, params.samples); - } - return true; - } - return false; -} - -void Session::update_status_time(bool show_pause, bool show_done) -{ - int progressive_sample = tile_manager.state.sample; - int num_samples = tile_manager.get_num_effective_samples(); - - int tile = progress.get_rendered_tiles(); - int num_tiles = tile_manager.state.num_tiles; - - /* update status */ - string status, substatus; - - if (!params.progressive) { - const bool is_cpu = params.device.type == DEVICE_CPU; - const bool rendering_finished = (tile == num_tiles); - const bool is_last_tile = (tile + 1) == num_tiles; - - substatus = string_printf("Rendered %d/%d Tiles", tile, num_tiles); - - if (!rendering_finished && (device->show_samples() || (is_cpu && is_last_tile))) { - /* Some devices automatically support showing the sample number: - * - CUDADevice - * - OpenCLDevice when using the megakernel (the split kernel renders multiple - * samples at the same time, so the current sample isn't really defined) - * - CPUDevice when using one thread - * For these devices, the current sample is always shown. - * - * The other option is when the last tile is currently being rendered by the CPU. - */ - substatus += string_printf(", Sample %d/%d", progress.get_current_sample(), num_samples); - } - if (params.denoising.use && params.denoising.type != DENOISER_OPENIMAGEDENOISE) { - substatus += string_printf(", Denoised %d tiles", progress.get_denoised_tiles()); - } - else if (params.denoising.store_passes && params.denoising.type == DENOISER_NLM) { - substatus += string_printf(", Prefiltered %d tiles", progress.get_denoised_tiles()); - } - } - else if (tile_manager.num_samples == Integrator::MAX_SAMPLES) - substatus = string_printf("Path Tracing Sample %d", progressive_sample + 1); - else - substatus = string_printf("Path Tracing Sample %d/%d", progressive_sample + 1, num_samples); - - if (show_pause) { - status = "Rendering Paused"; - } - else if (show_done) { - status = "Rendering Done"; - progress.set_end_time(); /* Save end time so that further calls to get_time are accurate. */ - } - else { - status = substatus; - substatus.clear(); - } - - progress.set_status(status, substatus); -} - -bool Session::render_need_denoise(bool &delayed) -{ - delayed = false; - - /* Not supported yet for baking. */ - if (read_bake_tile_cb) { - return false; - } - - /* Denoising enabled? */ - if (!params.denoising.need_denoising_task()) { - return false; - } - - if (params.background) { - /* Background render, only denoise when rendering the last sample. */ - return tile_manager.done(); - } - - /* Viewport render. */ - - /* It can happen that denoising was already enabled, but the scene still needs an update. */ - if (scene->film->is_modified() || !scene->film->get_denoising_data_offset()) { - return false; - } - - /* Immediately denoise when we reach the start sample or last sample. */ - const int num_samples_finished = tile_manager.state.sample + 1; - if (num_samples_finished == params.denoising.start_sample || - num_samples_finished == params.samples) { - return true; - } - - /* Do not denoise until the sample at which denoising should start is reached. */ - if (num_samples_finished < params.denoising.start_sample) { - return false; - } - - /* Avoid excessive denoising in viewport after reaching a certain amount of samples. */ - delayed = (tile_manager.state.sample >= 20 && - (time_dt() - last_display_time_) < params.progressive_update_timeout); - return !delayed; -} - -void Session::render(bool need_denoise) -{ - if (buffers && tile_manager.state.sample == tile_manager.range_start_sample) { - /* Clear buffers. */ - buffers->zero(); - } - - if (tile_manager.state.buffer.width == 0 || tile_manager.state.buffer.height == 0) { - return; /* Avoid empty launches. */ - } - - /* Add path trace task. */ - DeviceTask task(DeviceTask::RENDER); - - task.acquire_tile = function_bind(&Session::acquire_tile, this, _2, _1, _3); - task.release_tile = function_bind(&Session::release_tile, this, _1, need_denoise); - task.map_neighbor_tiles = function_bind(&Session::map_neighbor_tiles, this, _1, _2); - task.unmap_neighbor_tiles = function_bind(&Session::unmap_neighbor_tiles, this, _1, _2); - task.get_cancel = function_bind(&Progress::get_cancel, &this->progress); - task.update_tile_sample = function_bind(&Session::update_tile_sample, this, _1); - task.update_progress_sample = function_bind(&Progress::add_samples, &this->progress, _1, _2); - task.get_tile_stolen = function_bind(&Session::get_tile_stolen, this); - task.need_finish_queue = params.progressive_refine; - task.integrator_branched = scene->integrator->get_method() == Integrator::BRANCHED_PATH; - - task.adaptive_sampling.use = (scene->integrator->get_sampling_pattern() == - SAMPLING_PATTERN_PMJ) && - scene->dscene.data.film.pass_adaptive_aux_buffer; - task.adaptive_sampling.min_samples = scene->dscene.data.integrator.adaptive_min_samples; - task.adaptive_sampling.adaptive_step = scene->dscene.data.integrator.adaptive_step; - - /* Acquire render tiles by default. */ - task.tile_types = RenderTile::PATH_TRACE; - - if (need_denoise) { - task.denoising = params.denoising; - - task.pass_stride = scene->film->get_pass_stride(); - task.target_pass_stride = task.pass_stride; - task.pass_denoising_data = scene->film->get_denoising_data_offset(); - task.pass_denoising_clean = scene->film->get_denoising_clean_offset(); - - task.denoising_from_render = true; - - if (tile_manager.schedule_denoising) { - /* Acquire denoising tiles during rendering. */ - task.tile_types |= RenderTile::DENOISE; - } - else { - assert(buffers); - - /* Schedule rendering and wait for it to finish. */ - device->task_add(task); - device->task_wait(); - - /* Then run denoising on the whole image at once. */ - task.type = DeviceTask::DENOISE_BUFFER; - task.x = tile_manager.state.buffer.full_x; - task.y = tile_manager.state.buffer.full_y; - task.w = tile_manager.state.buffer.width; - task.h = tile_manager.state.buffer.height; - task.buffer = buffers->buffer.device_pointer; - task.sample = tile_manager.state.sample; - task.num_samples = tile_manager.state.num_samples; - tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); - task.buffers = buffers; - } - } - - device->task_add(task); -} - -void Session::copy_to_display_buffer(int sample) -{ - /* add film conversion task */ - DeviceTask task(DeviceTask::FILM_CONVERT); - - task.x = tile_manager.state.buffer.full_x; - task.y = tile_manager.state.buffer.full_y; - task.w = tile_manager.state.buffer.width; - task.h = tile_manager.state.buffer.height; - task.rgba_byte = display->rgba_byte.device_pointer; - task.rgba_half = display->rgba_half.device_pointer; - task.buffer = buffers->buffer.device_pointer; - task.sample = sample; - tile_manager.state.buffer.get_offset_stride(task.offset, task.stride); - - if (task.w > 0 && task.h > 0) { - device->task_add(task); - device->task_wait(); - - /* set display to new size */ - display->draw_set(task.w, task.h); - - last_display_time_ = time_dt(); - } - - display_outdated_ = false; -} - -bool Session::update_progressive_refine(bool cancel) -{ - int sample = tile_manager.state.sample + 1; - bool write = sample == tile_manager.num_samples || cancel; - - double current_time = time_dt(); - - if (current_time - last_update_time_ < params.progressive_update_timeout) { - /* If last sample was processed, we need to write buffers anyway. */ - if (!write && sample != 1) - return false; - } - - if (params.progressive_refine) { - foreach (Tile &tile, tile_manager.state.tiles) { - if (!tile.buffers) { - continue; - } - - RenderTile rtile; - rtile.x = tile_manager.state.buffer.full_x + tile.x; - rtile.y = tile_manager.state.buffer.full_y + tile.y; - rtile.w = tile.w; - rtile.h = tile.h; - rtile.sample = sample; - rtile.buffers = tile.buffers; - - if (write) { - if (write_render_tile_cb) - write_render_tile_cb(rtile); - } - else { - if (update_render_tile_cb) - update_render_tile_cb(rtile, true); - } - } - } - - last_update_time_ = current_time; - - return write; -} - -void Session::device_free() -{ - scene->device_free(); - - tile_manager.device_free(); - - /* used from background render only, so no need to - * re-create render/display buffers here - */ -} - -void Session::collect_statistics(RenderStats *render_stats) -{ - scene->collect_statistics(render_stats); - if (params.use_profiling && (params.device.type == DEVICE_CPU)) { - render_stats->collect_profiling(scene, profiler); - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/session.h b/intern/cycles/render/session.h deleted file mode 100644 index 05025c10f9c..00000000000 --- a/intern/cycles/render/session.h +++ /dev/null @@ -1,252 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __SESSION_H__ -#define __SESSION_H__ - -#include "device/device.h" -#include "render/buffers.h" -#include "render/shader.h" -#include "render/stats.h" -#include "render/tile.h" - -#include "util/util_progress.h" -#include "util/util_stats.h" -#include "util/util_thread.h" -#include "util/util_vector.h" - -CCL_NAMESPACE_BEGIN - -class BufferParams; -class Device; -class DeviceScene; -class DeviceRequestedFeatures; -class DisplayBuffer; -class Progress; -class RenderBuffers; -class Scene; - -/* Session Parameters */ - -class SessionParams { - public: - DeviceInfo device; - bool background; - bool progressive_refine; - - bool progressive; - bool experimental; - int samples; - int2 tile_size; - TileOrder tile_order; - int start_resolution; - int denoising_start_sample; - int pixel_size; - int threads; - bool adaptive_sampling; - - bool use_profiling; - - bool display_buffer_linear; - - DenoiseParams denoising; - - double cancel_timeout; - double reset_timeout; - double text_timeout; - double progressive_update_timeout; - - ShadingSystem shadingsystem; - - function write_render_cb; - - SessionParams() - { - background = false; - progressive_refine = false; - - progressive = false; - experimental = false; - samples = 1024; - tile_size = make_int2(64, 64); - start_resolution = INT_MAX; - denoising_start_sample = 0; - pixel_size = 1; - threads = 0; - adaptive_sampling = false; - - use_profiling = false; - - display_buffer_linear = false; - - cancel_timeout = 0.1; - reset_timeout = 0.1; - text_timeout = 1.0; - progressive_update_timeout = 1.0; - - shadingsystem = SHADINGSYSTEM_SVM; - tile_order = TILE_CENTER; - } - - bool modified(const SessionParams ¶ms) - { - /* Modified means we have to recreate the session, any parameter changes - * that can be handled by an existing Session are omitted. */ - return !(device == params.device && background == params.background && - progressive_refine == params.progressive_refine && - progressive == params.progressive && experimental == params.experimental && - tile_size == params.tile_size && start_resolution == params.start_resolution && - pixel_size == params.pixel_size && threads == params.threads && - adaptive_sampling == params.adaptive_sampling && - use_profiling == params.use_profiling && - display_buffer_linear == params.display_buffer_linear && - cancel_timeout == params.cancel_timeout && reset_timeout == params.reset_timeout && - text_timeout == params.text_timeout && - progressive_update_timeout == params.progressive_update_timeout && - tile_order == params.tile_order && shadingsystem == params.shadingsystem && - denoising.type == params.denoising.type && - (denoising.use == params.denoising.use || (device.denoisers & denoising.type))); - } -}; - -/* Session - * - * This is the class that contains the session thread, running the render - * control loop and dispatching tasks. */ - -class Session { - public: - Device *device; - Scene *scene; - RenderBuffers *buffers; - DisplayBuffer *display; - Progress progress; - SessionParams params; - TileManager tile_manager; - Stats stats; - Profiler profiler; - - function write_render_tile_cb; - function update_render_tile_cb; - function read_bake_tile_cb; - - explicit Session(const SessionParams ¶ms); - ~Session(); - - void start(); - void cancel(); - bool draw(BufferParams ¶ms, DeviceDrawParams &draw_params); - void wait(); - - bool ready_to_reset(); - void reset(BufferParams ¶ms, int samples); - void set_pause(bool pause); - void set_samples(int samples); - void set_denoising(const DenoiseParams &denoising); - void set_denoising_start_sample(int sample); - - bool update_scene(); - - void device_free(); - - /* Returns the rendering progress or 0 if no progress can be determined - * (for example, when rendering with unlimited samples). */ - float get_progress(); - - void collect_statistics(RenderStats *stats); - - protected: - struct DelayedReset { - thread_mutex mutex; - bool do_reset; - BufferParams params; - int samples; - } delayed_reset_; - - void run(); - - bool run_update_for_next_iteration(); - bool run_wait_for_work(bool no_tiles); - - void update_status_time(bool show_pause = false, bool show_done = false); - - void render(bool use_denoise); - void copy_to_display_buffer(int sample); - - void reset_(BufferParams ¶ms, int samples); - - void run_cpu(); - bool draw_cpu(BufferParams ¶ms, DeviceDrawParams &draw_params); - void reset_cpu(BufferParams ¶ms, int samples); - - void run_gpu(); - bool draw_gpu(BufferParams ¶ms, DeviceDrawParams &draw_params); - void reset_gpu(BufferParams ¶ms, int samples); - - bool render_need_denoise(bool &delayed); - - bool steal_tile(RenderTile &tile, Device *tile_device, thread_scoped_lock &tile_lock); - bool get_tile_stolen(); - bool acquire_tile(RenderTile &tile, Device *tile_device, uint tile_types); - void update_tile_sample(RenderTile &tile); - void release_tile(RenderTile &tile, const bool need_denoise); - - void map_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); - void unmap_neighbor_tiles(RenderTileNeighbors &neighbors, Device *tile_device); - - bool device_use_gl_; - - thread *session_thread_; - - volatile bool display_outdated_; - - volatile bool gpu_draw_ready_; - volatile bool gpu_need_display_buffer_update_; - thread_condition_variable gpu_need_display_buffer_update_cond_; - - bool pause_; - bool cancel_; - bool new_work_added_; - - thread_condition_variable pause_cond_; - thread_mutex pause_mutex_; - thread_mutex tile_mutex_; - thread_mutex buffers_mutex_; - thread_mutex display_mutex_; - thread_condition_variable denoising_cond_; - thread_condition_variable tile_steal_cond_; - - double reset_time_; - double last_update_time_; - double last_display_time_; - - RenderTile stolen_tile_; - typedef enum { - NOT_STEALING, /* There currently is no tile stealing in progress. */ - WAITING_FOR_TILE, /* A device is waiting for another device to release a tile. */ - RELEASING_TILE, /* A device has releasing a stealable tile. */ - GOT_TILE /* A device has released a stealable tile, which is now stored in stolen_tile. */ - } TileStealingState; - std::atomic tile_stealing_state_; - int stealable_tiles_; - - /* progressive refine */ - bool update_progressive_refine(bool cancel); -}; - -CCL_NAMESPACE_END - -#endif /* __SESSION_H__ */ diff --git a/intern/cycles/render/tile.cpp b/intern/cycles/render/tile.cpp deleted file mode 100644 index 375c9fd8e09..00000000000 --- a/intern/cycles/render/tile.cpp +++ /dev/null @@ -1,616 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "render/tile.h" - -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_types.h" - -CCL_NAMESPACE_BEGIN - -namespace { - -class TileComparator { - public: - TileComparator(TileOrder order_, int2 center_, Tile *tiles_) - : order(order_), center(center_), tiles(tiles_) - { - } - - bool operator()(int a, int b) - { - switch (order) { - case TILE_CENTER: { - float2 dist_a = make_float2(center.x - (tiles[a].x + tiles[a].w / 2), - center.y - (tiles[a].y + tiles[a].h / 2)); - float2 dist_b = make_float2(center.x - (tiles[b].x + tiles[b].w / 2), - center.y - (tiles[b].y + tiles[b].h / 2)); - return dot(dist_a, dist_a) < dot(dist_b, dist_b); - } - case TILE_LEFT_TO_RIGHT: - return (tiles[a].x == tiles[b].x) ? (tiles[a].y < tiles[b].y) : (tiles[a].x < tiles[b].x); - case TILE_RIGHT_TO_LEFT: - return (tiles[a].x == tiles[b].x) ? (tiles[a].y < tiles[b].y) : (tiles[a].x > tiles[b].x); - case TILE_TOP_TO_BOTTOM: - return (tiles[a].y == tiles[b].y) ? (tiles[a].x < tiles[b].x) : (tiles[a].y > tiles[b].y); - case TILE_BOTTOM_TO_TOP: - default: - return (tiles[a].y == tiles[b].y) ? (tiles[a].x < tiles[b].x) : (tiles[a].y < tiles[b].y); - } - } - - protected: - TileOrder order; - int2 center; - Tile *tiles; -}; - -inline int2 hilbert_index_to_pos(int n, int d) -{ - int2 r, xy = make_int2(0, 0); - for (int s = 1; s < n; s *= 2) { - r.x = (d >> 1) & 1; - r.y = (d ^ r.x) & 1; - if (!r.y) { - if (r.x) { - xy = make_int2(s - 1, s - 1) - xy; - } - swap(xy.x, xy.y); - } - xy += r * make_int2(s, s); - d >>= 2; - } - return xy; -} - -enum SpiralDirection { - DIRECTION_UP, - DIRECTION_LEFT, - DIRECTION_DOWN, - DIRECTION_RIGHT, -}; - -} /* namespace */ - -TileManager::TileManager(bool progressive_, - int num_samples_, - int2 tile_size_, - int start_resolution_, - bool preserve_tile_device_, - bool background_, - TileOrder tile_order_, - int num_devices_, - int pixel_size_) -{ - progressive = progressive_; - tile_size = tile_size_; - tile_order = tile_order_; - start_resolution = start_resolution_; - pixel_size = pixel_size_; - slice_overlap = 0; - num_samples = num_samples_; - num_devices = num_devices_; - preserve_tile_device = preserve_tile_device_; - background = background_; - schedule_denoising = false; - - range_start_sample = 0; - range_num_samples = -1; - - BufferParams buffer_params; - reset(buffer_params, 0); -} - -TileManager::~TileManager() -{ -} - -void TileManager::device_free() -{ - if (schedule_denoising || progressive) { - for (int i = 0; i < state.tiles.size(); i++) { - delete state.tiles[i].buffers; - state.tiles[i].buffers = NULL; - } - } - - state.tiles.clear(); -} - -static int get_divider(int w, int h, int start_resolution) -{ - int divider = 1; - if (start_resolution != INT_MAX) { - while (w * h > start_resolution * start_resolution) { - w = max(1, w / 2); - h = max(1, h / 2); - - divider <<= 1; - } - } - return divider; -} - -void TileManager::reset(BufferParams ¶ms_, int num_samples_) -{ - params = params_; - - set_samples(num_samples_); - - state.buffer = BufferParams(); - state.sample = range_start_sample - 1; - state.num_tiles = 0; - state.num_samples = 0; - state.resolution_divider = get_divider(params.width, params.height, start_resolution); - state.render_tiles.clear(); - state.denoising_tiles.clear(); - device_free(); -} - -void TileManager::set_samples(int num_samples_) -{ - num_samples = num_samples_; - - /* No real progress indication is possible when using unlimited samples. */ - if (num_samples == INT_MAX) { - state.total_pixel_samples = 0; - } - else { - uint64_t pixel_samples = 0; - /* While rendering in the viewport, the initial preview resolution is increased to the native - * resolution before the actual rendering begins. Therefore, additional pixel samples will be - * rendered. */ - int divider = max(get_divider(params.width, params.height, start_resolution) / 2, pixel_size); - while (divider > pixel_size) { - int image_w = max(1, params.width / divider); - int image_h = max(1, params.height / divider); - pixel_samples += image_w * image_h; - divider >>= 1; - } - - int image_w = max(1, params.width / divider); - int image_h = max(1, params.height / divider); - state.total_pixel_samples = pixel_samples + - (uint64_t)get_num_effective_samples() * image_w * image_h; - if (schedule_denoising) { - state.total_pixel_samples += params.width * params.height; - } - } -} - -/* If sliced is false, splits image into tiles and assigns equal amount of tiles to every render - * device. If sliced is true, slice image into as much pieces as how many devices are rendering - * this image. */ -int TileManager::gen_tiles(bool sliced) -{ - int resolution = state.resolution_divider; - int image_w = max(1, params.width / resolution); - int image_h = max(1, params.height / resolution); - int2 center = make_int2(image_w / 2, image_h / 2); - - int num = preserve_tile_device || sliced ? min(image_h, num_devices) : 1; - int slice_num = sliced ? num : 1; - int tile_w = (tile_size.x >= image_w) ? 1 : divide_up(image_w, tile_size.x); - - device_free(); - state.render_tiles.clear(); - state.denoising_tiles.clear(); - state.render_tiles.resize(num); - state.denoising_tiles.resize(num); - state.tile_stride = tile_w; - vector>::iterator tile_list; - tile_list = state.render_tiles.begin(); - - if (tile_order == TILE_HILBERT_SPIRAL) { - assert(!sliced && slice_overlap == 0); - - int tile_h = (tile_size.y >= image_h) ? 1 : divide_up(image_h, tile_size.y); - state.tiles.resize(tile_w * tile_h); - - /* Size of blocks in tiles, must be a power of 2 */ - const int hilbert_size = (max(tile_size.x, tile_size.y) <= 12) ? 8 : 4; - - int tiles_per_device = divide_up(tile_w * tile_h, num); - int cur_device = 0, cur_tiles = 0; - - int2 block_size = tile_size * make_int2(hilbert_size, hilbert_size); - /* Number of blocks to fill the image */ - int blocks_x = (block_size.x >= image_w) ? 1 : divide_up(image_w, block_size.x); - int blocks_y = (block_size.y >= image_h) ? 1 : divide_up(image_h, block_size.y); - int n = max(blocks_x, blocks_y) | 0x1; /* Side length of the spiral (must be odd) */ - /* Offset of spiral (to keep it centered) */ - int2 offset = make_int2((image_w - n * block_size.x) / 2, (image_h - n * block_size.y) / 2); - offset = (offset / tile_size) * tile_size; /* Round to tile border. */ - - int2 block = make_int2(0, 0); /* Current block */ - SpiralDirection prev_dir = DIRECTION_UP, dir = DIRECTION_UP; - for (int i = 0;;) { - /* Generate the tiles in the current block. */ - for (int hilbert_index = 0; hilbert_index < hilbert_size * hilbert_size; hilbert_index++) { - int2 tile, hilbert_pos = hilbert_index_to_pos(hilbert_size, hilbert_index); - /* Rotate block according to spiral direction. */ - if (prev_dir == DIRECTION_UP && dir == DIRECTION_UP) { - tile = make_int2(hilbert_pos.y, hilbert_pos.x); - } - else if (dir == DIRECTION_LEFT || prev_dir == DIRECTION_LEFT) { - tile = hilbert_pos; - } - else if (dir == DIRECTION_DOWN) { - tile = make_int2(hilbert_size - 1 - hilbert_pos.y, hilbert_size - 1 - hilbert_pos.x); - } - else { - tile = make_int2(hilbert_size - 1 - hilbert_pos.x, hilbert_size - 1 - hilbert_pos.y); - } - - int2 pos = block * block_size + tile * tile_size + offset; - /* Only add tiles which are in the image (tiles outside of the image can be generated since - * the spiral is always square). */ - if (pos.x >= 0 && pos.y >= 0 && pos.x < image_w && pos.y < image_h) { - int w = min(tile_size.x, image_w - pos.x); - int h = min(tile_size.y, image_h - pos.y); - int2 ipos = pos / tile_size; - int idx = ipos.y * tile_w + ipos.x; - state.tiles[idx] = Tile(idx, pos.x, pos.y, w, h, cur_device, Tile::RENDER); - tile_list->push_front(idx); - cur_tiles++; - - if (cur_tiles == tiles_per_device) { - tile_list++; - cur_tiles = 0; - cur_device++; - } - } - } - - /* Stop as soon as the spiral has reached the center block. */ - if (block.x == (n - 1) / 2 && block.y == (n - 1) / 2) - break; - - /* Advance to next block. */ - prev_dir = dir; - switch (dir) { - case DIRECTION_UP: - block.y++; - if (block.y == (n - i - 1)) { - dir = DIRECTION_LEFT; - } - break; - case DIRECTION_LEFT: - block.x++; - if (block.x == (n - i - 1)) { - dir = DIRECTION_DOWN; - } - break; - case DIRECTION_DOWN: - block.y--; - if (block.y == i) { - dir = DIRECTION_RIGHT; - } - break; - case DIRECTION_RIGHT: - block.x--; - if (block.x == i + 1) { - dir = DIRECTION_UP; - i++; - } - break; - } - } - return tile_w * tile_h; - } - - int idx = 0; - for (int slice = 0; slice < slice_num; slice++) { - int slice_y = (image_h / slice_num) * slice; - int slice_h = (slice == slice_num - 1) ? image_h - slice * (image_h / slice_num) : - image_h / slice_num; - - if (slice_overlap != 0) { - int slice_y_offset = max(slice_y - slice_overlap, 0); - slice_h = min(slice_y + slice_h + slice_overlap, image_h) - slice_y_offset; - slice_y = slice_y_offset; - } - - int tile_h = (tile_size.y >= slice_h) ? 1 : divide_up(slice_h, tile_size.y); - - int tiles_per_device = divide_up(tile_w * tile_h, num); - int cur_device = 0, cur_tiles = 0; - - for (int tile_y = 0; tile_y < tile_h; tile_y++) { - for (int tile_x = 0; tile_x < tile_w; tile_x++, idx++) { - int x = tile_x * tile_size.x; - int y = tile_y * tile_size.y; - int w = (tile_x == tile_w - 1) ? image_w - x : tile_size.x; - int h = (tile_y == tile_h - 1) ? slice_h - y : tile_size.y; - - state.tiles.push_back( - Tile(idx, x, y + slice_y, w, h, sliced ? slice : cur_device, Tile::RENDER)); - tile_list->push_back(idx); - - if (!sliced) { - cur_tiles++; - - if (cur_tiles == tiles_per_device) { - /* Tiles are already generated in Bottom-to-Top order, so no sort is necessary in that - * case. */ - if (tile_order != TILE_BOTTOM_TO_TOP) { - tile_list->sort(TileComparator(tile_order, center, &state.tiles[0])); - } - tile_list++; - cur_tiles = 0; - cur_device++; - } - } - } - } - if (sliced) { - tile_list++; - } - } - - return idx; -} - -void TileManager::gen_render_tiles() -{ - /* Regenerate just the render tiles for progressive render. */ - foreach (Tile &tile, state.tiles) { - tile.state = Tile::RENDER; - state.render_tiles[tile.device].push_back(tile.index); - } -} - -void TileManager::set_tiles() -{ - int resolution = state.resolution_divider; - int image_w = max(1, params.width / resolution); - int image_h = max(1, params.height / resolution); - - state.num_tiles = gen_tiles(!background); - - state.buffer.width = image_w; - state.buffer.height = image_h; - - state.buffer.full_x = params.full_x / resolution; - state.buffer.full_y = params.full_y / resolution; - state.buffer.full_width = max(1, params.full_width / resolution); - state.buffer.full_height = max(1, params.full_height / resolution); -} - -int TileManager::get_neighbor_index(int index, int neighbor) -{ - /* Neighbor indices: - * 0 1 2 - * 3 4 5 - * 6 7 8 - */ - static const int dx[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1}; - static const int dy[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1}; - - int resolution = state.resolution_divider; - int image_w = max(1, params.width / resolution); - int image_h = max(1, params.height / resolution); - - int num = min(image_h, num_devices); - int slice_num = !background ? num : 1; - int slice_h = image_h / slice_num; - - int tile_w = (tile_size.x >= image_w) ? 1 : divide_up(image_w, tile_size.x); - int tile_h = (tile_size.y >= slice_h) ? 1 : divide_up(slice_h, tile_size.y); - - /* Tiles in the state tile list are always indexed from left to right, top to bottom. */ - int nx = (index % tile_w) + dx[neighbor]; - int ny = (index / tile_w) + dy[neighbor]; - if (nx < 0 || ny < 0 || nx >= tile_w || ny >= tile_h * slice_num) - return -1; - - return ny * state.tile_stride + nx; -} - -/* Checks whether all neighbors of a tile (as well as the tile itself) are at least at state - * min_state. */ -bool TileManager::check_neighbor_state(int index, Tile::State min_state) -{ - if (index < 0 || state.tiles[index].state < min_state) { - return false; - } - for (int neighbor = 0; neighbor < 9; neighbor++) { - int nindex = get_neighbor_index(index, neighbor); - /* Out-of-bounds tiles don't matter. */ - if (nindex >= 0 && state.tiles[nindex].state < min_state) { - return false; - } - } - - return true; -} - -/* Returns whether the tile should be written (and freed if no denoising is used) instead of - * updating. */ -bool TileManager::finish_tile(const int index, const bool need_denoise, bool &delete_tile) -{ - delete_tile = false; - - switch (state.tiles[index].state) { - case Tile::RENDER: { - if (!(schedule_denoising && need_denoise)) { - state.tiles[index].state = Tile::DONE; - delete_tile = !progressive; - return true; - } - state.tiles[index].state = Tile::RENDERED; - /* For each neighbor and the tile itself, check whether all of its neighbors have been - * rendered. If yes, it can be denoised. */ - for (int neighbor = 0; neighbor < 9; neighbor++) { - int nindex = get_neighbor_index(index, neighbor); - if (check_neighbor_state(nindex, Tile::RENDERED)) { - state.tiles[nindex].state = Tile::DENOISE; - state.denoising_tiles[state.tiles[nindex].device].push_back(nindex); - } - } - return false; - } - case Tile::DENOISE: { - state.tiles[index].state = Tile::DENOISED; - /* For each neighbor and the tile itself, check whether all of its neighbors have been - * denoised. If yes, it can be freed. */ - for (int neighbor = 0; neighbor < 9; neighbor++) { - int nindex = get_neighbor_index(index, neighbor); - if (check_neighbor_state(nindex, Tile::DENOISED)) { - state.tiles[nindex].state = Tile::DONE; - /* Do not delete finished tiles in progressive mode. */ - if (!progressive) { - /* It can happen that the tile just finished denoising and already can be freed here. - * However, in that case it still has to be written before deleting, so we can't delete - * it yet. */ - if (neighbor == 4) { - delete_tile = true; - } - else { - delete state.tiles[nindex].buffers; - state.tiles[nindex].buffers = NULL; - } - } - } - } - return true; - } - default: - assert(false); - return true; - } -} - -bool TileManager::next_tile(Tile *&tile, int device, uint tile_types) -{ - /* Preserve device if requested, unless this is a separate denoising device that just wants to - * grab any available tile. */ - const bool preserve_device = preserve_tile_device && device < num_devices; - - if (tile_types & RenderTile::DENOISE) { - int tile_index = -1; - int logical_device = preserve_device ? device : 0; - - while (logical_device < state.denoising_tiles.size()) { - if (state.denoising_tiles[logical_device].empty()) { - if (preserve_device) { - break; - } - else { - logical_device++; - continue; - } - } - - tile_index = state.denoising_tiles[logical_device].front(); - state.denoising_tiles[logical_device].pop_front(); - break; - } - - if (tile_index >= 0) { - tile = &state.tiles[tile_index]; - return true; - } - } - - if (tile_types & RenderTile::PATH_TRACE) { - int tile_index = -1; - int logical_device = preserve_device ? device : 0; - - while (logical_device < state.render_tiles.size()) { - if (state.render_tiles[logical_device].empty()) { - if (preserve_device) { - break; - } - else { - logical_device++; - continue; - } - } - - tile_index = state.render_tiles[logical_device].front(); - state.render_tiles[logical_device].pop_front(); - break; - } - - if (tile_index >= 0) { - tile = &state.tiles[tile_index]; - return true; - } - } - - return false; -} - -bool TileManager::done() -{ - int end_sample = (range_num_samples == -1) ? num_samples : - range_start_sample + range_num_samples; - return (state.resolution_divider == pixel_size) && - (state.sample + state.num_samples >= end_sample); -} - -bool TileManager::has_tiles() -{ - foreach (Tile &tile, state.tiles) { - if (tile.state != Tile::DONE) { - return true; - } - } - return false; -} - -bool TileManager::next() -{ - if (done()) - return false; - - if (progressive && state.resolution_divider > pixel_size) { - state.sample = 0; - state.resolution_divider = max(state.resolution_divider / 2, pixel_size); - state.num_samples = 1; - set_tiles(); - } - else { - state.sample++; - - if (progressive) - state.num_samples = 1; - else if (range_num_samples == -1) - state.num_samples = num_samples; - else - state.num_samples = range_num_samples; - - state.resolution_divider = pixel_size; - - if (state.sample == range_start_sample) { - set_tiles(); - } - else { - gen_render_tiles(); - } - } - - return true; -} - -int TileManager::get_num_effective_samples() -{ - return (range_num_samples == -1) ? num_samples : range_num_samples; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/tile.h b/intern/cycles/render/tile.h deleted file mode 100644 index 790a56f9445..00000000000 --- a/intern/cycles/render/tile.h +++ /dev/null @@ -1,172 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __TILE_H__ -#define __TILE_H__ - -#include - -#include "render/buffers.h" -#include "util/util_list.h" - -CCL_NAMESPACE_BEGIN - -/* Tile */ - -class Tile { - public: - int index; - int x, y, w, h; - int device; - /* RENDER: The tile has to be rendered. - * RENDERED: The tile has been rendered, but can't be denoised yet (waiting for neighbors). - * DENOISE: The tile can be denoised now. - * DENOISED: The tile has been denoised, but can't be freed yet (waiting for neighbors). - * DONE: The tile is finished and has been freed. */ - typedef enum { RENDER = 0, RENDERED, DENOISE, DENOISED, DONE } State; - State state; - RenderBuffers *buffers; - - Tile() - { - } - - Tile(int index_, int x_, int y_, int w_, int h_, int device_, State state_ = RENDER) - : index(index_), x(x_), y(y_), w(w_), h(h_), device(device_), state(state_), buffers(NULL) - { - } -}; - -/* Tile order */ - -/* Note: this should match enum_tile_order in properties.py */ -enum TileOrder { - TILE_CENTER = 0, - TILE_RIGHT_TO_LEFT = 1, - TILE_LEFT_TO_RIGHT = 2, - TILE_TOP_TO_BOTTOM = 3, - TILE_BOTTOM_TO_TOP = 4, - TILE_HILBERT_SPIRAL = 5, -}; - -/* Tile Manager */ - -class TileManager { - public: - BufferParams params; - - struct State { - vector tiles; - int tile_stride; - BufferParams buffer; - int sample; - int num_samples; - int resolution_divider; - int num_tiles; - - /* Total samples over all pixels: Generally num_samples*num_pixels, - * but can be higher due to the initial resolution division for previews. */ - uint64_t total_pixel_samples; - - /* These lists contain the indices of the tiles to be rendered/denoised and are used - * when acquiring a new tile for the device. - * Each list in each vector is for one logical device. */ - vector> render_tiles; - vector> denoising_tiles; - } state; - - int num_samples; - int slice_overlap; - - TileManager(bool progressive, - int num_samples, - int2 tile_size, - int start_resolution, - bool preserve_tile_device, - bool background, - TileOrder tile_order, - int num_devices = 1, - int pixel_size = 1); - ~TileManager(); - - void device_free(); - void reset(BufferParams ¶ms, int num_samples); - void set_samples(int num_samples); - bool next(); - bool next_tile(Tile *&tile, int device, uint tile_types); - bool finish_tile(const int index, const bool need_denoise, bool &delete_tile); - bool done(); - bool has_tiles(); - - void set_tile_order(TileOrder tile_order_) - { - tile_order = tile_order_; - } - - int get_neighbor_index(int index, int neighbor); - bool check_neighbor_state(int index, Tile::State state); - - /* ** Sample range rendering. ** */ - - /* Start sample in the range. */ - int range_start_sample; - - /* Number to samples in the rendering range. */ - int range_num_samples; - - /* Get number of actual samples to render. */ - int get_num_effective_samples(); - - /* Schedule tiles for denoising after they've been rendered. */ - bool schedule_denoising; - - protected: - void set_tiles(); - - bool progressive; - int2 tile_size; - TileOrder tile_order; - int start_resolution; - int pixel_size; - int num_devices; - - /* in some cases it is important that the same tile will be returned for the same - * device it was originally generated for (i.e. viewport rendering when buffer is - * allocating once for tile and then always used by it) - * - * in other cases any tile could be handled by any device (i.e. final rendering - * without progressive refine) - */ - bool preserve_tile_device; - - /* for background render tiles should exactly match render parts generated from - * blender side, which means image first gets split into tiles and then tiles are - * assigning to render devices - * - * however viewport rendering expects tiles to be allocated in a special way, - * meaning image is being sliced horizontally first and every device handles - * its own slice - */ - bool background; - - /* Generate tile list, return number of tiles. */ - int gen_tiles(bool sliced); - void gen_render_tiles(); -}; - -CCL_NAMESPACE_END - -#endif /* __TILE_H__ */ diff --git a/intern/cycles/render/CMakeLists.txt b/intern/cycles/scene/CMakeLists.txt similarity index 87% rename from intern/cycles/render/CMakeLists.txt rename to intern/cycles/scene/CMakeLists.txt index feead27c5ca..a3fde99306b 100644 --- a/intern/cycles/render/CMakeLists.txt +++ b/intern/cycles/scene/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2011-2020 Blender Foundation +# Copyright 2011-2021 Blender Foundation # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,29 +14,20 @@ set(INC .. - ../../glew-mx ../../sky/include ) -set(INC_SYS - ${GLEW_INCLUDE_DIR} -) - set(SRC alembic.cpp alembic_read.cpp attribute.cpp background.cpp bake.cpp - buffers.cpp camera.cpp colorspace.cpp constant_fold.cpp - coverage.cpp - denoising.cpp film.cpp geometry.cpp - graph.cpp hair.cpp image.cpp image_oiio.cpp @@ -45,24 +36,23 @@ set(SRC integrator.cpp jitter.cpp light.cpp - merge.cpp mesh.cpp mesh_displace.cpp mesh_subdivision.cpp - nodes.cpp procedural.cpp object.cpp osl.cpp particles.cpp + pass.cpp curves.cpp scene.cpp - session.cpp shader.cpp + shader_graph.cpp + shader_nodes.cpp sobol.cpp stats.cpp svm.cpp tables.cpp - tile.cpp volume.cpp ) @@ -72,15 +62,11 @@ set(SRC_HEADERS attribute.h bake.h background.h - buffers.h camera.h colorspace.h constant_fold.h - coverage.h - denoising.h film.h geometry.h - graph.h hair.h image.h image_oiio.h @@ -89,28 +75,28 @@ set(SRC_HEADERS integrator.h light.h jitter.h - merge.h mesh.h - nodes.h object.h osl.h particles.h + pass.h procedural.h curves.h scene.h - session.h shader.h + shader_graph.h + shader_nodes.h sobol.h stats.h svm.h tables.h - tile.h volume.h ) set(LIB cycles_bvh cycles_device + cycles_integrator cycles_subd cycles_util ) @@ -174,4 +160,4 @@ include_directories(SYSTEM ${INC_SYS}) add_definitions(${GL_DEFINITIONS}) -cycles_add_library(cycles_render "${LIB}" ${SRC} ${SRC_HEADERS}) +cycles_add_library(cycles_scene "${LIB}" ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/render/alembic.cpp b/intern/cycles/scene/alembic.cpp similarity index 99% rename from intern/cycles/render/alembic.cpp rename to intern/cycles/scene/alembic.cpp index 69bc0712674..39b5f467736 100644 --- a/intern/cycles/render/alembic.cpp +++ b/intern/cycles/scene/alembic.cpp @@ -14,21 +14,21 @@ * limitations under the License. */ -#include "render/alembic.h" +#include "scene/alembic.h" -#include "render/alembic_read.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/alembic_read.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/transform.h" +#include "util/vector.h" #ifdef WITH_ALEMBIC diff --git a/intern/cycles/render/alembic.h b/intern/cycles/scene/alembic.h similarity index 99% rename from intern/cycles/render/alembic.h rename to intern/cycles/scene/alembic.h index 8e166a5ab04..77aafd0ab32 100644 --- a/intern/cycles/render/alembic.h +++ b/intern/cycles/scene/alembic.h @@ -17,11 +17,11 @@ #pragma once #include "graph/node.h" -#include "render/attribute.h" -#include "render/procedural.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "scene/attribute.h" +#include "scene/procedural.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/vector.h" #ifdef WITH_ALEMBIC diff --git a/intern/cycles/render/alembic_read.cpp b/intern/cycles/scene/alembic_read.cpp similarity index 99% rename from intern/cycles/render/alembic_read.cpp rename to intern/cycles/scene/alembic_read.cpp index b105af63b44..35f4854127a 100644 --- a/intern/cycles/render/alembic_read.cpp +++ b/intern/cycles/scene/alembic_read.cpp @@ -14,12 +14,12 @@ * limitations under the License. */ -#include "render/alembic_read.h" +#include "scene/alembic_read.h" +#include "scene/alembic.h" +#include "scene/mesh.h" -#include "render/alembic.h" -#include "render/mesh.h" - -#include "util/util_progress.h" +#include "util/color.h" +#include "util/progress.h" #ifdef WITH_ALEMBIC diff --git a/intern/cycles/render/alembic_read.h b/intern/cycles/scene/alembic_read.h similarity index 99% rename from intern/cycles/render/alembic_read.h rename to intern/cycles/scene/alembic_read.h index 9cc8622a1ba..6b656b59481 100644 --- a/intern/cycles/render/alembic_read.h +++ b/intern/cycles/scene/alembic_read.h @@ -21,7 +21,7 @@ # include # include -# include "util/util_vector.h" +# include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/attribute.cpp b/intern/cycles/scene/attribute.cpp similarity index 97% rename from intern/cycles/render/attribute.cpp rename to intern/cycles/scene/attribute.cpp index ea5a5f50f2d..3401eea307f 100644 --- a/intern/cycles/render/attribute.cpp +++ b/intern/cycles/scene/attribute.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "render/attribute.h" -#include "render/hair.h" -#include "render/image.h" -#include "render/mesh.h" +#include "scene/attribute.h" +#include "scene/hair.h" +#include "scene/image.h" +#include "scene/mesh.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN @@ -342,6 +342,8 @@ const char *Attribute::standard_name(AttributeStandard std) return "particle"; case ATTR_STD_CURVE_INTERCEPT: return "curve_intercept"; + case ATTR_STD_CURVE_LENGTH: + return "curve_length"; case ATTR_STD_CURVE_RANDOM: return "curve_random"; case ATTR_STD_PTEX_FACE_ID: @@ -364,6 +366,8 @@ const char *Attribute::standard_name(AttributeStandard std) return "pointiness"; case ATTR_STD_RANDOM_PER_ISLAND: return "random_per_island"; + case ATTR_STD_SHADOW_TRANSPARENCY: + return "shadow_transparency"; case ATTR_STD_NOT_FOUND: case ATTR_STD_NONE: case ATTR_STD_NUM: @@ -586,6 +590,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name) case ATTR_STD_CURVE_INTERCEPT: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY); break; + case ATTR_STD_CURVE_LENGTH: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE); + break; case ATTR_STD_CURVE_RANDOM: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE); break; @@ -598,6 +605,9 @@ Attribute *AttributeSet::add(AttributeStandard std, ustring name) case ATTR_STD_RANDOM_PER_ISLAND: attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_FACE); break; + case ATTR_STD_SHADOW_TRANSPARENCY: + attr = add(name, TypeDesc::TypeFloat, ATTR_ELEMENT_CURVE_KEY); + break; default: assert(0); break; diff --git a/intern/cycles/render/attribute.h b/intern/cycles/scene/attribute.h similarity index 97% rename from intern/cycles/render/attribute.h rename to intern/cycles/scene/attribute.h index 004c267cabc..4a25a900c14 100644 --- a/intern/cycles/render/attribute.h +++ b/intern/cycles/scene/attribute.h @@ -17,15 +17,15 @@ #ifndef __ATTRIBUTE_H__ #define __ATTRIBUTE_H__ -#include "render/image.h" +#include "scene/image.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_list.h" -#include "util/util_param.h" -#include "util/util_set.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/param.h" +#include "util/set.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/background.cpp b/intern/cycles/scene/background.cpp similarity index 84% rename from intern/cycles/render/background.cpp rename to intern/cycles/scene/background.cpp index b925e755434..72dccc6f9a8 100644 --- a/intern/cycles/render/background.cpp +++ b/intern/cycles/scene/background.cpp @@ -14,19 +14,19 @@ * limitations under the License. */ -#include "render/background.h" +#include "scene/background.h" #include "device/device.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/nodes.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/integrator.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" -#include "util/util_foreach.h" -#include "util/util_math.h" -#include "util/util_time.h" -#include "util/util_types.h" +#include "util/foreach.h" +#include "util/math.h" +#include "util/time.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN @@ -34,11 +34,7 @@ NODE_DEFINE(Background) { NodeType *type = NodeType::add("background", create); - SOCKET_FLOAT(ao_factor, "AO Factor", 0.0f); - SOCKET_FLOAT(ao_distance, "AO Distance", FLT_MAX); - SOCKET_BOOLEAN(use_shader, "Use Shader", true); - SOCKET_BOOLEAN(use_ao, "Use AO", false); SOCKET_UINT(visibility, "Visibility", PATH_RAY_ALL_VISIBILITY); SOCKET_BOOLEAN(transparent, "Transparent", false); @@ -80,10 +76,6 @@ void Background::device_update(Device *device, DeviceScene *dscene, Scene *scene /* set shader index and transparent option */ KernelBackground *kbackground = &dscene->data.background; - kbackground->ao_factor = (use_ao) ? ao_factor : 0.0f; - kbackground->ao_bounces_factor = ao_factor; - kbackground->ao_distance = ao_distance; - kbackground->transparent = transparent; kbackground->surface_shader = scene->shader_manager->get_shader_id(bg_shader); @@ -138,10 +130,6 @@ void Background::tag_update(Scene *scene) * 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); - } } Shader *Background::get_shader(const Scene *scene) diff --git a/intern/cycles/render/background.h b/intern/cycles/scene/background.h similarity index 91% rename from intern/cycles/render/background.h rename to intern/cycles/scene/background.h index e89ffbc2445..31f15d09749 100644 --- a/intern/cycles/render/background.h +++ b/intern/cycles/scene/background.h @@ -19,7 +19,7 @@ #include "graph/node.h" -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN @@ -32,11 +32,7 @@ class Background : public Node { public: NODE_DECLARE - NODE_SOCKET_API(float, ao_factor) - NODE_SOCKET_API(float, ao_distance) - NODE_SOCKET_API(bool, use_shader) - NODE_SOCKET_API(bool, use_ao) NODE_SOCKET_API(uint, visibility) NODE_SOCKET_API(Shader *, shader) diff --git a/intern/cycles/scene/bake.cpp b/intern/cycles/scene/bake.cpp new file mode 100644 index 00000000000..90c9e0e4ae8 --- /dev/null +++ b/intern/cycles/scene/bake.cpp @@ -0,0 +1,104 @@ +/* + * Copyright 2011-2014 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "scene/bake.h" +#include "scene/integrator.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/shader.h" +#include "scene/stats.h" +#include "session/buffers.h" + +#include "util/foreach.h" + +CCL_NAMESPACE_BEGIN + +BakeManager::BakeManager() +{ + need_update_ = true; +} + +BakeManager::~BakeManager() +{ +} + +bool BakeManager::get_baking() const +{ + return !object_name.empty(); +} + +void BakeManager::set(Scene *scene, const std::string &object_name_) +{ + object_name = object_name_; + + /* create device and update scene */ + scene->film->tag_modified(); + scene->integrator->tag_update(scene, Integrator::UPDATE_ALL); + + need_update_ = true; +} + +void BakeManager::device_update(Device * /*device*/, + DeviceScene *dscene, + Scene *scene, + Progress & /* progress */) +{ + if (!need_update()) + return; + + KernelBake *kbake = &dscene->data.bake; + memset(kbake, 0, sizeof(*kbake)); + + if (!object_name.empty()) { + scoped_callback_timer timer([scene](double time) { + if (scene->update_stats) { + scene->update_stats->bake.times.add_entry({"device_update", time}); + } + }); + + kbake->use = true; + + int object_index = 0; + foreach (Object *object, scene->objects) { + const Geometry *geom = object->get_geometry(); + if (object->name == object_name && geom->geometry_type == Geometry::MESH) { + kbake->object_index = object_index; + kbake->tri_offset = geom->prim_offset; + break; + } + + object_index++; + } + } + + need_update_ = false; +} + +void BakeManager::device_free(Device * /*device*/, DeviceScene * /*dscene*/) +{ +} + +void BakeManager::tag_update() +{ + need_update_ = true; +} + +bool BakeManager::need_update() const +{ + return need_update_; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/bake.h b/intern/cycles/scene/bake.h similarity index 81% rename from intern/cycles/render/bake.h rename to intern/cycles/scene/bake.h index 655b9b1cf7e..370cc20ae4f 100644 --- a/intern/cycles/render/bake.h +++ b/intern/cycles/scene/bake.h @@ -18,10 +18,10 @@ #define __BAKE_H__ #include "device/device.h" -#include "render/scene.h" +#include "scene/scene.h" -#include "util/util_progress.h" -#include "util/util_vector.h" +#include "util/progress.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -30,8 +30,8 @@ class BakeManager { BakeManager(); ~BakeManager(); - void set(Scene *scene, const std::string &object_name, ShaderEvalType type, int pass_filter); - bool get_baking(); + void set(Scene *scene, const std::string &object_name); + bool get_baking() const; void device_update(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); void device_free(Device *device, DeviceScene *dscene); @@ -42,8 +42,6 @@ class BakeManager { private: bool need_update_; - ShaderEvalType type; - int pass_filter; std::string object_name; }; diff --git a/intern/cycles/render/camera.cpp b/intern/cycles/scene/camera.cpp similarity index 94% rename from intern/cycles/render/camera.cpp rename to intern/cycles/scene/camera.cpp index 327f166f9d8..5bafe736fb5 100644 --- a/intern/cycles/render/camera.cpp +++ b/intern/cycles/scene/camera.cpp @@ -14,33 +14,28 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/stats.h" -#include "render/tables.h" +#include "scene/camera.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/stats.h" +#include "scene/tables.h" #include "device/device.h" -#include "util/util_foreach.h" -#include "util/util_function.h" -#include "util/util_logging.h" -#include "util/util_math_cdf.h" -#include "util/util_task.h" -#include "util/util_time.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/function.h" +#include "util/log.h" +#include "util/math_cdf.h" +#include "util/task.h" +#include "util/time.h" +#include "util/vector.h" /* needed for calculating differentials */ -// clang-format off -#include "kernel/kernel_compat_cpu.h" -#include "kernel/split/kernel_split_data.h" -#include "kernel/kernel_globals.h" -#include "kernel/kernel_projection.h" -#include "kernel/kernel_differential.h" -#include "kernel/kernel_montecarlo.h" -#include "kernel/kernel_camera.h" -// clang-format on +#include "kernel/device/cpu/compat.h" +#include "kernel/device/cpu/globals.h" + +#include "kernel/camera/camera.h" CCL_NAMESPACE_BEGIN @@ -169,7 +164,6 @@ Camera::Camera() : Node(get_node_type()) width = 1024; height = 512; - resolution = 1; use_perspective_motion = false; @@ -455,7 +449,6 @@ void Camera::update(Scene *scene) /* render size */ kcam->width = width; kcam->height = height; - kcam->resolution = resolution; /* store differentials */ kcam->dx = float3_to_float4(dx); @@ -599,22 +592,26 @@ BoundBox Camera::viewplane_bounds_get() if (camera_type == CAMERA_PANORAMA) { if (use_spherical_stereo == false) { - bounds.grow(make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w)); + bounds.grow(make_float3(cameratoworld.x.w, cameratoworld.y.w, cameratoworld.z.w), nearclip); } else { float half_eye_distance = interocular_distance * 0.5f; - bounds.grow(make_float3( - cameratoworld.x.w + half_eye_distance, cameratoworld.y.w, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.x.w + half_eye_distance, cameratoworld.y.w, cameratoworld.z.w), + nearclip); - bounds.grow(make_float3( - cameratoworld.z.w, cameratoworld.y.w + half_eye_distance, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.z.w, cameratoworld.y.w + half_eye_distance, cameratoworld.z.w), + nearclip); - bounds.grow(make_float3( - cameratoworld.x.w - half_eye_distance, cameratoworld.y.w, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.x.w - half_eye_distance, cameratoworld.y.w, cameratoworld.z.w), + nearclip); - bounds.grow(make_float3( - cameratoworld.x.w, cameratoworld.y.w - half_eye_distance, cameratoworld.z.w)); + bounds.grow( + make_float3(cameratoworld.x.w, cameratoworld.y.w - half_eye_distance, cameratoworld.z.w), + nearclip); } } else { @@ -776,9 +773,11 @@ float Camera::world_to_raster_size(float3 P) &ray); #endif - differential_transfer(&ray.dP, ray.dP, ray.D, ray.dD, ray.D, dist); + /* TODO: would it help to use more accurate differentials here? */ + differential3 dP; + differential_transfer_compact(&dP, ray.dP, ray.D, ray.dD, ray.D, dist); - return max(len(ray.dP.dx), len(ray.dP.dy)); + return max(len(dP.dx), len(dP.dy)); } return res; @@ -789,12 +788,11 @@ bool Camera::use_motion() const return motion.size() > 1; } -void Camera::set_screen_size_and_resolution(int width_, int height_, int resolution_) +void Camera::set_screen_size(int width_, int height_) { - if (width_ != width || height_ != height || resolution_ != resolution) { + if (width_ != width || height_ != height) { width = width_; height = height_; - resolution = resolution_; tag_modified(); } } diff --git a/intern/cycles/render/camera.h b/intern/cycles/scene/camera.h similarity index 95% rename from intern/cycles/render/camera.h rename to intern/cycles/scene/camera.h index 5abb4750764..58e39599267 100644 --- a/intern/cycles/render/camera.h +++ b/intern/cycles/scene/camera.h @@ -17,15 +17,15 @@ #ifndef __CAMERA_H__ #define __CAMERA_H__ -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "graph/node.h" -#include "util/util_array.h" -#include "util/util_boundbox.h" -#include "util/util_projection.h" -#include "util/util_transform.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/boundbox.h" +#include "util/projection.h" +#include "util/transform.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN @@ -199,7 +199,6 @@ class Camera : public Node { private: int width; int height; - int resolution; public: /* functions */ @@ -225,7 +224,7 @@ class Camera : public Node { int motion_step(float time) const; bool use_motion() const; - void set_screen_size_and_resolution(int width_, int height_, int resolution_); + void set_screen_size(int width_, int height_); private: /* Private utility functions. */ diff --git a/intern/cycles/render/colorspace.cpp b/intern/cycles/scene/colorspace.cpp similarity index 98% rename from intern/cycles/render/colorspace.cpp rename to intern/cycles/scene/colorspace.cpp index 3842f8e4726..c1a308fcbaa 100644 --- a/intern/cycles/render/colorspace.cpp +++ b/intern/cycles/scene/colorspace.cpp @@ -14,15 +14,15 @@ * limitations under the License. */ -#include "render/colorspace.h" +#include "scene/colorspace.h" -#include "util/util_color.h" -#include "util/util_half.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_math.h" -#include "util/util_thread.h" -#include "util/util_vector.h" +#include "util/color.h" +#include "util/half.h" +#include "util/image.h" +#include "util/log.h" +#include "util/math.h" +#include "util/thread.h" +#include "util/vector.h" #ifdef WITH_OCIO # include diff --git a/intern/cycles/render/colorspace.h b/intern/cycles/scene/colorspace.h similarity index 97% rename from intern/cycles/render/colorspace.h rename to intern/cycles/scene/colorspace.h index 51d0b121cc0..7f7bc604f07 100644 --- a/intern/cycles/render/colorspace.h +++ b/intern/cycles/scene/colorspace.h @@ -17,8 +17,8 @@ #ifndef __COLORSPACE_H__ #define __COLORSPACE_H__ -#include "util/util_map.h" -#include "util/util_param.h" +#include "util/map.h" +#include "util/param.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/constant_fold.cpp b/intern/cycles/scene/constant_fold.cpp similarity index 95% rename from intern/cycles/render/constant_fold.cpp rename to intern/cycles/scene/constant_fold.cpp index a4d40ae8183..a5fb68bf229 100644 --- a/intern/cycles/render/constant_fold.cpp +++ b/intern/cycles/scene/constant_fold.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/constant_fold.h" -#include "render/graph.h" +#include "scene/constant_fold.h" +#include "scene/shader_graph.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" +#include "util/foreach.h" +#include "util/log.h" CCL_NAMESPACE_BEGIN @@ -43,7 +43,7 @@ bool ConstantFolder::all_inputs_constant() const void ConstantFolder::make_constant(float value) const { - VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant (" << value + VLOG(3) << "Folding " << node->name << "::" << output->name() << " to constant (" << value << ")."; foreach (ShaderInput *sock, output->links) { @@ -56,7 +56,7 @@ void ConstantFolder::make_constant(float value) const void ConstantFolder::make_constant(float3 value) const { - VLOG(1) << "Folding " << node->name << "::" << output->name() << " to constant " << value << "."; + VLOG(3) << "Folding " << node->name << "::" << output->name() << " to constant " << value << "."; foreach (ShaderInput *sock, output->links) { sock->set(value); @@ -68,15 +68,15 @@ void ConstantFolder::make_constant(float3 value) const void ConstantFolder::make_constant_clamp(float value, bool clamp) const { - make_constant(clamp ? saturate(value) : value); + make_constant(clamp ? saturatef(value) : value); } void ConstantFolder::make_constant_clamp(float3 value, bool clamp) const { if (clamp) { - value.x = saturate(value.x); - value.y = saturate(value.y); - value.z = saturate(value.z); + value.x = saturatef(value.x); + value.y = saturatef(value.y); + value.z = saturatef(value.z); } make_constant(value); @@ -112,7 +112,7 @@ void ConstantFolder::bypass(ShaderOutput *new_output) const { assert(new_output); - VLOG(1) << "Folding " << node->name << "::" << output->name() << " to socket " + VLOG(3) << "Folding " << node->name << "::" << output->name() << " to socket " << new_output->parent->name << "::" << new_output->name() << "."; /* Remove all outgoing links from socket and connect them to new_output instead. @@ -131,7 +131,7 @@ void ConstantFolder::discard() const { assert(output->type() == SocketType::CLOSURE); - VLOG(1) << "Discarding closure " << node->name << "."; + VLOG(3) << "Discarding closure " << node->name << "."; graph->disconnect(output); } @@ -215,7 +215,7 @@ void ConstantFolder::fold_mix(NodeMix type, bool clamp) const ShaderInput *color1_in = node->input("Color1"); ShaderInput *color2_in = node->input("Color2"); - float fac = saturate(node->get_float(fac_in->socket_type)); + float fac = saturatef(node->get_float(fac_in->socket_type)); bool fac_is_zero = !fac_in->link && fac == 0.0f; bool fac_is_one = !fac_in->link && fac == 1.0f; diff --git a/intern/cycles/render/constant_fold.h b/intern/cycles/scene/constant_fold.h similarity index 97% rename from intern/cycles/render/constant_fold.h rename to intern/cycles/scene/constant_fold.h index fec4123c361..36b249920d0 100644 --- a/intern/cycles/render/constant_fold.h +++ b/intern/cycles/scene/constant_fold.h @@ -17,8 +17,8 @@ #ifndef __CONSTANT_FOLD_H__ #define __CONSTANT_FOLD_H__ -#include "kernel/svm/svm_types.h" -#include "util/util_types.h" +#include "kernel/svm/types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/curves.cpp b/intern/cycles/scene/curves.cpp similarity index 90% rename from intern/cycles/render/curves.cpp rename to intern/cycles/scene/curves.cpp index db48d8b6430..7863ce6c666 100644 --- a/intern/cycles/render/curves.cpp +++ b/intern/cycles/scene/curves.cpp @@ -14,16 +14,16 @@ * limitations under the License. */ -#include "render/curves.h" +#include "scene/curves.h" #include "device/device.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" -#include "util/util_foreach.h" -#include "util/util_map.h" -#include "util/util_progress.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/map.h" +#include "util/progress.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/curves.h b/intern/cycles/scene/curves.h similarity index 94% rename from intern/cycles/render/curves.h rename to intern/cycles/scene/curves.h index c52fcb9c882..3076f4291f5 100644 --- a/intern/cycles/render/curves.h +++ b/intern/cycles/scene/curves.h @@ -17,10 +17,10 @@ #ifndef __CURVES_H__ #define __CURVES_H__ -#include "util/util_array.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/types.h" -#include "render/hair.h" +#include "scene/hair.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/scene/film.cpp b/intern/cycles/scene/film.cpp new file mode 100644 index 00000000000..591f309384e --- /dev/null +++ b/intern/cycles/scene/film.cpp @@ -0,0 +1,693 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "scene/film.h" +#include "device/device.h" +#include "scene/background.h" +#include "scene/bake.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/stats.h" +#include "scene/tables.h" + +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/math.h" +#include "util/math_cdf.h" +#include "util/time.h" + +CCL_NAMESPACE_BEGIN + +/* Pixel Filter */ + +static float filter_func_box(float /*v*/, float /*width*/) +{ + return 1.0f; +} + +static float filter_func_gaussian(float v, float width) +{ + v *= 6.0f / width; + return expf(-2.0f * v * v); +} + +static float filter_func_blackman_harris(float v, float width) +{ + v = M_2PI_F * (v / width + 0.5f); + return 0.35875f - 0.48829f * cosf(v) + 0.14128f * cosf(2.0f * v) - 0.01168f * cosf(3.0f * v); +} + +static vector filter_table(FilterType type, float width) +{ + vector filter_table(FILTER_TABLE_SIZE); + float (*filter_func)(float, float) = NULL; + + switch (type) { + case FILTER_BOX: + filter_func = filter_func_box; + break; + case FILTER_GAUSSIAN: + filter_func = filter_func_gaussian; + width *= 3.0f; + break; + case FILTER_BLACKMAN_HARRIS: + filter_func = filter_func_blackman_harris; + width *= 2.0f; + break; + default: + assert(0); + } + + /* Create importance sampling table. */ + + /* TODO(sergey): With the even filter table size resolution we can not + * really make it nice symmetric importance map without sampling full range + * (meaning, we would need to sample full filter range and not use the + * make_symmetric argument). + * + * Current code matches exactly initial filter table code, but we should + * consider either making FILTER_TABLE_SIZE odd value or sample full filter. + */ + + util_cdf_inverted(FILTER_TABLE_SIZE, + 0.0f, + width * 0.5f, + function_bind(filter_func, _1, width), + true, + filter_table); + + return filter_table; +} + +/* Film */ + +NODE_DEFINE(Film) +{ + NodeType *type = NodeType::add("film", create); + + SOCKET_FLOAT(exposure, "Exposure", 0.8f); + SOCKET_FLOAT(pass_alpha_threshold, "Pass Alpha Threshold", 0.0f); + + static NodeEnum filter_enum; + filter_enum.insert("box", FILTER_BOX); + filter_enum.insert("gaussian", FILTER_GAUSSIAN); + filter_enum.insert("blackman_harris", FILTER_BLACKMAN_HARRIS); + + SOCKET_ENUM(filter_type, "Filter Type", filter_enum, FILTER_BOX); + SOCKET_FLOAT(filter_width, "Filter Width", 1.0f); + + SOCKET_FLOAT(mist_start, "Mist Start", 0.0f); + SOCKET_FLOAT(mist_depth, "Mist Depth", 100.0f); + SOCKET_FLOAT(mist_falloff, "Mist Falloff", 1.0f); + + const NodeEnum *pass_type_enum = Pass::get_type_enum(); + SOCKET_ENUM(display_pass, "Display Pass", *pass_type_enum, PASS_COMBINED); + + SOCKET_BOOLEAN(show_active_pixels, "Show Active Pixels", false); + + static NodeEnum cryptomatte_passes_enum; + cryptomatte_passes_enum.insert("none", CRYPT_NONE); + cryptomatte_passes_enum.insert("object", CRYPT_OBJECT); + cryptomatte_passes_enum.insert("material", CRYPT_MATERIAL); + cryptomatte_passes_enum.insert("asset", CRYPT_ASSET); + cryptomatte_passes_enum.insert("accurate", CRYPT_ACCURATE); + SOCKET_ENUM(cryptomatte_passes, "Cryptomatte Passes", cryptomatte_passes_enum, CRYPT_NONE); + + SOCKET_INT(cryptomatte_depth, "Cryptomatte Depth", 0); + + SOCKET_BOOLEAN(use_approximate_shadow_catcher, "Use Approximate Shadow Catcher", false); + + return type; +} + +Film::Film() : Node(get_node_type()), filter_table_offset_(TABLE_OFFSET_INVALID) +{ +} + +Film::~Film() +{ +} + +void Film::add_default(Scene *scene) +{ + Pass *pass = scene->create_node(); + pass->set_type(PASS_COMBINED); +} + +void Film::device_update(Device *device, DeviceScene *dscene, Scene *scene) +{ + if (!is_modified()) + return; + + scoped_callback_timer timer([scene](double time) { + if (scene->update_stats) { + scene->update_stats->film.times.add_entry({"update", time}); + } + }); + + device_free(device, dscene, scene); + + KernelFilm *kfilm = &dscene->data.film; + + /* update __data */ + kfilm->exposure = exposure; + kfilm->pass_alpha_threshold = pass_alpha_threshold; + kfilm->pass_flag = 0; + + kfilm->use_approximate_shadow_catcher = get_use_approximate_shadow_catcher(); + + kfilm->light_pass_flag = 0; + kfilm->pass_stride = 0; + + /* Mark with PASS_UNUSED to avoid mask test in the kernel. */ + kfilm->pass_background = PASS_UNUSED; + kfilm->pass_emission = PASS_UNUSED; + kfilm->pass_ao = PASS_UNUSED; + kfilm->pass_diffuse_direct = PASS_UNUSED; + kfilm->pass_diffuse_indirect = PASS_UNUSED; + kfilm->pass_glossy_direct = PASS_UNUSED; + kfilm->pass_glossy_indirect = PASS_UNUSED; + kfilm->pass_transmission_direct = PASS_UNUSED; + kfilm->pass_transmission_indirect = PASS_UNUSED; + kfilm->pass_volume_direct = PASS_UNUSED; + kfilm->pass_volume_indirect = PASS_UNUSED; + kfilm->pass_volume_direct = PASS_UNUSED; + kfilm->pass_volume_indirect = PASS_UNUSED; + kfilm->pass_shadow = PASS_UNUSED; + + /* Mark passes as unused so that the kernel knows the pass is inaccessible. */ + kfilm->pass_denoising_normal = PASS_UNUSED; + kfilm->pass_denoising_albedo = PASS_UNUSED; + kfilm->pass_denoising_depth = PASS_UNUSED; + kfilm->pass_sample_count = PASS_UNUSED; + kfilm->pass_adaptive_aux_buffer = PASS_UNUSED; + kfilm->pass_shadow_catcher = PASS_UNUSED; + kfilm->pass_shadow_catcher_sample_count = PASS_UNUSED; + kfilm->pass_shadow_catcher_matte = PASS_UNUSED; + + bool have_cryptomatte = false; + bool have_aov_color = false; + bool have_aov_value = false; + + for (size_t i = 0; i < scene->passes.size(); i++) { + const Pass *pass = scene->passes[i]; + + if (pass->get_type() == PASS_NONE || !pass->is_written()) { + continue; + } + + if (pass->get_mode() == PassMode::DENOISED) { + /* Generally we only storing offsets of the noisy passes. The display pass is an exception + * since it is a read operation and not a write. */ + kfilm->pass_stride += pass->get_info().num_components; + continue; + } + + /* Can't do motion pass if no motion vectors are available. */ + if (pass->get_type() == PASS_MOTION || pass->get_type() == PASS_MOTION_WEIGHT) { + if (scene->need_motion() != Scene::MOTION_PASS) { + kfilm->pass_stride += pass->get_info().num_components; + continue; + } + } + + const int pass_flag = (1 << (pass->get_type() % 32)); + if (pass->get_type() <= PASS_CATEGORY_LIGHT_END) { + kfilm->light_pass_flag |= pass_flag; + } + else if (pass->get_type() <= PASS_CATEGORY_DATA_END) { + kfilm->pass_flag |= pass_flag; + } + else { + assert(pass->get_type() <= PASS_CATEGORY_BAKE_END); + } + + switch (pass->get_type()) { + case PASS_COMBINED: + kfilm->pass_combined = kfilm->pass_stride; + break; + case PASS_DEPTH: + kfilm->pass_depth = kfilm->pass_stride; + break; + case PASS_NORMAL: + kfilm->pass_normal = kfilm->pass_stride; + break; + case PASS_POSITION: + kfilm->pass_position = kfilm->pass_stride; + break; + case PASS_ROUGHNESS: + kfilm->pass_roughness = kfilm->pass_stride; + break; + case PASS_UV: + kfilm->pass_uv = kfilm->pass_stride; + break; + case PASS_MOTION: + kfilm->pass_motion = kfilm->pass_stride; + break; + case PASS_MOTION_WEIGHT: + kfilm->pass_motion_weight = kfilm->pass_stride; + break; + case PASS_OBJECT_ID: + kfilm->pass_object_id = kfilm->pass_stride; + break; + case PASS_MATERIAL_ID: + kfilm->pass_material_id = kfilm->pass_stride; + break; + + case PASS_MIST: + kfilm->pass_mist = kfilm->pass_stride; + break; + case PASS_EMISSION: + kfilm->pass_emission = kfilm->pass_stride; + break; + case PASS_BACKGROUND: + kfilm->pass_background = kfilm->pass_stride; + break; + case PASS_AO: + kfilm->pass_ao = kfilm->pass_stride; + break; + case PASS_SHADOW: + kfilm->pass_shadow = kfilm->pass_stride; + break; + + case PASS_DIFFUSE_COLOR: + kfilm->pass_diffuse_color = kfilm->pass_stride; + break; + case PASS_GLOSSY_COLOR: + kfilm->pass_glossy_color = kfilm->pass_stride; + break; + case PASS_TRANSMISSION_COLOR: + kfilm->pass_transmission_color = kfilm->pass_stride; + break; + case PASS_DIFFUSE_INDIRECT: + kfilm->pass_diffuse_indirect = kfilm->pass_stride; + break; + case PASS_GLOSSY_INDIRECT: + kfilm->pass_glossy_indirect = kfilm->pass_stride; + break; + case PASS_TRANSMISSION_INDIRECT: + kfilm->pass_transmission_indirect = kfilm->pass_stride; + break; + case PASS_VOLUME_INDIRECT: + kfilm->pass_volume_indirect = kfilm->pass_stride; + break; + case PASS_DIFFUSE_DIRECT: + kfilm->pass_diffuse_direct = kfilm->pass_stride; + break; + case PASS_GLOSSY_DIRECT: + kfilm->pass_glossy_direct = kfilm->pass_stride; + break; + case PASS_TRANSMISSION_DIRECT: + kfilm->pass_transmission_direct = kfilm->pass_stride; + break; + case PASS_VOLUME_DIRECT: + kfilm->pass_volume_direct = kfilm->pass_stride; + break; + + case PASS_BAKE_PRIMITIVE: + kfilm->pass_bake_primitive = kfilm->pass_stride; + break; + case PASS_BAKE_DIFFERENTIAL: + kfilm->pass_bake_differential = kfilm->pass_stride; + break; + + case PASS_CRYPTOMATTE: + kfilm->pass_cryptomatte = have_cryptomatte ? + min(kfilm->pass_cryptomatte, kfilm->pass_stride) : + kfilm->pass_stride; + have_cryptomatte = true; + break; + + case PASS_DENOISING_NORMAL: + kfilm->pass_denoising_normal = kfilm->pass_stride; + break; + case PASS_DENOISING_ALBEDO: + kfilm->pass_denoising_albedo = kfilm->pass_stride; + break; + case PASS_DENOISING_DEPTH: + kfilm->pass_denoising_depth = kfilm->pass_stride; + break; + + case PASS_SHADOW_CATCHER: + kfilm->pass_shadow_catcher = kfilm->pass_stride; + break; + case PASS_SHADOW_CATCHER_SAMPLE_COUNT: + kfilm->pass_shadow_catcher_sample_count = kfilm->pass_stride; + break; + case PASS_SHADOW_CATCHER_MATTE: + kfilm->pass_shadow_catcher_matte = kfilm->pass_stride; + break; + + case PASS_ADAPTIVE_AUX_BUFFER: + kfilm->pass_adaptive_aux_buffer = kfilm->pass_stride; + break; + case PASS_SAMPLE_COUNT: + kfilm->pass_sample_count = kfilm->pass_stride; + break; + + case PASS_AOV_COLOR: + if (!have_aov_color) { + kfilm->pass_aov_color = kfilm->pass_stride; + have_aov_color = true; + } + break; + case PASS_AOV_VALUE: + if (!have_aov_value) { + kfilm->pass_aov_value = kfilm->pass_stride; + have_aov_value = true; + } + break; + default: + assert(false); + break; + } + + kfilm->pass_stride += pass->get_info().num_components; + } + + /* update filter table */ + vector table = filter_table(filter_type, filter_width); + scene->lookup_tables->remove_table(&filter_table_offset_); + filter_table_offset_ = scene->lookup_tables->add_table(dscene, table); + kfilm->filter_table_offset = (int)filter_table_offset_; + + /* mist pass parameters */ + kfilm->mist_start = mist_start; + kfilm->mist_inv_depth = (mist_depth > 0.0f) ? 1.0f / mist_depth : 0.0f; + kfilm->mist_falloff = mist_falloff; + + kfilm->cryptomatte_passes = cryptomatte_passes; + kfilm->cryptomatte_depth = cryptomatte_depth; + + clear_modified(); +} + +void Film::device_free(Device * /*device*/, DeviceScene * /*dscene*/, Scene *scene) +{ + scene->lookup_tables->remove_table(&filter_table_offset_); +} + +int Film::get_aov_offset(Scene *scene, string name, bool &is_color) +{ + int offset_color = 0, offset_value = 0; + foreach (const Pass *pass, scene->passes) { + if (pass->get_name() == name) { + if (pass->get_type() == PASS_AOV_VALUE) { + is_color = false; + return offset_value; + } + else if (pass->get_type() == PASS_AOV_COLOR) { + is_color = true; + return offset_color; + } + } + + if (pass->get_type() == PASS_AOV_VALUE) { + offset_value += pass->get_info().num_components; + } + else if (pass->get_type() == PASS_AOV_COLOR) { + offset_color += pass->get_info().num_components; + } + } + + return -1; +} + +void Film::update_passes(Scene *scene, bool add_sample_count_pass) +{ + const Background *background = scene->background; + const BakeManager *bake_manager = scene->bake_manager; + const ObjectManager *object_manager = scene->object_manager; + Integrator *integrator = scene->integrator; + + if (!is_modified() && !object_manager->need_update() && !integrator->is_modified() && + !background->is_modified()) { + return; + } + + /* Remove auto generated passes and recreate them. */ + remove_auto_passes(scene); + + /* Display pass for viewport. */ + const PassType display_pass = get_display_pass(); + add_auto_pass(scene, display_pass); + + /* Assumption is that a combined pass always exists for now, for example + * adaptive sampling is always based on a combined pass. But we should + * try to lift this limitation in the future for faster rendering of + * individual passes. */ + if (display_pass != PASS_COMBINED) { + add_auto_pass(scene, PASS_COMBINED); + } + + /* Create passes needed for adaptive sampling. */ + const AdaptiveSampling adaptive_sampling = integrator->get_adaptive_sampling(); + if (adaptive_sampling.use) { + add_auto_pass(scene, PASS_SAMPLE_COUNT); + add_auto_pass(scene, PASS_ADAPTIVE_AUX_BUFFER); + } + + /* Create passes needed for denoising. */ + const bool use_denoise = integrator->get_use_denoise(); + if (use_denoise) { + if (integrator->get_use_denoise_pass_normal()) { + add_auto_pass(scene, PASS_DENOISING_NORMAL); + } + if (integrator->get_use_denoise_pass_albedo()) { + add_auto_pass(scene, PASS_DENOISING_ALBEDO); + } + } + + /* Create passes for shadow catcher. */ + if (scene->has_shadow_catcher()) { + const bool need_background = get_use_approximate_shadow_catcher() && + !background->get_transparent(); + + add_auto_pass(scene, PASS_SHADOW_CATCHER); + add_auto_pass(scene, PASS_SHADOW_CATCHER_SAMPLE_COUNT); + add_auto_pass(scene, PASS_SHADOW_CATCHER_MATTE); + + if (need_background) { + add_auto_pass(scene, PASS_BACKGROUND); + } + } + else if (Pass::contains(scene->passes, PASS_SHADOW_CATCHER)) { + add_auto_pass(scene, PASS_SHADOW_CATCHER); + add_auto_pass(scene, PASS_SHADOW_CATCHER_SAMPLE_COUNT); + } + + const vector passes_immutable = scene->passes; + for (const Pass *pass : passes_immutable) { + const PassInfo info = pass->get_info(); + /* Add utility passes needed to generate some light passes. */ + if (info.divide_type != PASS_NONE) { + add_auto_pass(scene, info.divide_type); + } + if (info.direct_type != PASS_NONE) { + add_auto_pass(scene, info.direct_type); + } + if (info.indirect_type != PASS_NONE) { + add_auto_pass(scene, info.indirect_type); + } + + /* NOTE: Enable all denoised passes when storage is requested. + * This way it is possible to tweak denoiser parameters later on. */ + if (info.support_denoise && use_denoise) { + add_auto_pass(scene, pass->get_type(), PassMode::DENOISED); + } + } + + if (bake_manager->get_baking()) { + add_auto_pass(scene, PASS_BAKE_PRIMITIVE, "BakePrimitive"); + add_auto_pass(scene, PASS_BAKE_DIFFERENTIAL, "BakeDifferential"); + } + + if (add_sample_count_pass) { + if (!Pass::contains(scene->passes, PASS_SAMPLE_COUNT)) { + add_auto_pass(scene, PASS_SAMPLE_COUNT); + } + } + + /* Remove duplicates and initialize internal pass info. */ + finalize_passes(scene, use_denoise); + + /* Flush scene updates. */ + const bool have_uv_pass = Pass::contains(scene->passes, PASS_UV); + const bool have_motion_pass = Pass::contains(scene->passes, PASS_MOTION); + const bool have_ao_pass = Pass::contains(scene->passes, PASS_AO); + + if (have_uv_pass != prev_have_uv_pass) { + scene->geometry_manager->tag_update(scene, GeometryManager::UV_PASS_NEEDED); + foreach (Shader *shader, scene->shaders) + shader->need_update_uvs = true; + } + if (have_motion_pass != prev_have_motion_pass) { + scene->geometry_manager->tag_update(scene, GeometryManager::MOTION_PASS_NEEDED); + } + if (have_ao_pass != prev_have_ao_pass) { + scene->integrator->tag_update(scene, Integrator::AO_PASS_MODIFIED); + } + + prev_have_uv_pass = have_uv_pass; + prev_have_motion_pass = have_motion_pass; + prev_have_ao_pass = have_ao_pass; + + tag_modified(); + + /* Debug logging. */ + if (VLOG_IS_ON(2)) { + VLOG(2) << "Effective scene passes:"; + for (const Pass *pass : scene->passes) { + VLOG(2) << "- " << *pass; + } + } +} + +void Film::add_auto_pass(Scene *scene, PassType type, const char *name) +{ + add_auto_pass(scene, type, PassMode::NOISY, name); +} + +void Film::add_auto_pass(Scene *scene, PassType type, PassMode mode, const char *name) +{ + Pass *pass = new Pass(); + pass->set_type(type); + pass->set_mode(mode); + pass->set_name(ustring((name) ? name : "")); + pass->is_auto_ = true; + + pass->set_owner(scene); + scene->passes.push_back(pass); +} + +void Film::remove_auto_passes(Scene *scene) +{ + /* Remove all passes which were automatically created. */ + vector new_passes; + + for (Pass *pass : scene->passes) { + if (!pass->is_auto_) { + new_passes.push_back(pass); + } + else { + delete pass; + } + } + + scene->passes = new_passes; +} + +static bool compare_pass_order(const Pass *a, const Pass *b) +{ + const int num_components_a = a->get_info().num_components; + const int num_components_b = b->get_info().num_components; + + if (num_components_a == num_components_b) { + return (a->get_type() < b->get_type()); + } + + return num_components_a > num_components_b; +} + +void Film::finalize_passes(Scene *scene, const bool use_denoise) +{ + /* Remove duplicate passes. */ + vector new_passes; + + for (Pass *pass : scene->passes) { + /* Disable denoising on passes if denoising is disabled, or if the + * pass does not support it. */ + pass->set_mode((use_denoise && pass->get_info().support_denoise) ? pass->get_mode() : + PassMode::NOISY); + + /* Merge duplicate passes. */ + bool duplicate_found = false; + for (Pass *new_pass : new_passes) { + /* If different type or denoising, don't merge. */ + if (new_pass->get_type() != pass->get_type() || new_pass->get_mode() != pass->get_mode()) { + continue; + } + + /* If both passes have a name and the names are different, don't merge. + * If either pass has a name, we'll use that name. */ + if (!pass->get_name().empty() && !new_pass->get_name().empty() && + pass->get_name() != new_pass->get_name()) { + continue; + } + + if (!pass->get_name().empty() && new_pass->get_name().empty()) { + new_pass->set_name(pass->get_name()); + } + + new_pass->is_auto_ &= pass->is_auto_; + duplicate_found = true; + break; + } + + if (!duplicate_found) { + new_passes.push_back(pass); + } + else { + delete pass; + } + } + + /* Order from by components and type, This is required to for AOVs and cryptomatte passes, + * which the kernel assumes to be in order. Note this must use stable sort so cryptomatte + * passes remain in the right order. */ + stable_sort(new_passes.begin(), new_passes.end(), compare_pass_order); + + scene->passes = new_passes; +} + +uint Film::get_kernel_features(const Scene *scene) const +{ + uint kernel_features = 0; + + for (const Pass *pass : scene->passes) { + if (!pass->is_written()) { + continue; + } + + const PassType pass_type = pass->get_type(); + const PassMode pass_mode = pass->get_mode(); + + if (pass_mode == PassMode::DENOISED || pass_type == PASS_DENOISING_NORMAL || + pass_type == PASS_DENOISING_ALBEDO || pass_type == PASS_DENOISING_DEPTH) { + kernel_features |= KERNEL_FEATURE_DENOISING; + } + + if (pass_type != PASS_NONE && pass_type != PASS_COMBINED && + pass_type <= PASS_CATEGORY_LIGHT_END) { + kernel_features |= KERNEL_FEATURE_LIGHT_PASSES; + + if (pass_type == PASS_SHADOW) { + kernel_features |= KERNEL_FEATURE_SHADOW_PASS; + } + } + + if (pass_type == PASS_AO) { + kernel_features |= KERNEL_FEATURE_AO_PASS; + } + } + + return kernel_features; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/film.h b/intern/cycles/scene/film.h similarity index 60% rename from intern/cycles/render/film.h rename to intern/cycles/scene/film.h index 462a7275491..5207c5e62b5 100644 --- a/intern/cycles/render/film.h +++ b/intern/cycles/scene/film.h @@ -17,10 +17,11 @@ #ifndef __FILM_H__ #define __FILM_H__ -#include "util/util_string.h" -#include "util/util_vector.h" +#include "scene/pass.h" +#include "util/string.h" +#include "util/vector.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "graph/node.h" @@ -38,36 +39,15 @@ typedef enum FilterType { FILTER_NUM_TYPES, } FilterType; -class Pass : public Node { - public: - NODE_DECLARE - - Pass(); - - PassType type; - int components; - bool filter; - bool exposure; - PassType divide_type; - ustring name; - - static void add(PassType type, vector &passes, const char *name = NULL); - static bool equals(const vector &A, const vector &B); - static bool contains(const vector &passes, PassType); -}; - class Film : public Node { public: NODE_DECLARE NODE_SOCKET_API(float, exposure) - NODE_SOCKET_API(bool, denoising_data_pass) - NODE_SOCKET_API(bool, denoising_clean_pass) - NODE_SOCKET_API(bool, denoising_prefiltered_pass) - NODE_SOCKET_API(int, denoising_flags) NODE_SOCKET_API(float, pass_alpha_threshold) NODE_SOCKET_API(PassType, display_pass) + NODE_SOCKET_API(bool, show_active_pixels) NODE_SOCKET_API(FilterType, filter_type) NODE_SOCKET_API(float, filter_width) @@ -76,17 +56,18 @@ class Film : public Node { NODE_SOCKET_API(float, mist_depth) NODE_SOCKET_API(float, mist_falloff) - NODE_SOCKET_API(bool, use_light_visibility) NODE_SOCKET_API(CryptomatteType, cryptomatte_passes) NODE_SOCKET_API(int, cryptomatte_depth) - NODE_SOCKET_API(bool, use_adaptive_sampling) + /* Approximate shadow catcher pass into its matte pass, so that both artificial objects and + * shadows can be alpha-overed onto a backdrop. */ + NODE_SOCKET_API(bool, use_approximate_shadow_catcher) private: - int pass_stride; - int denoising_data_offset; - int denoising_clean_offset; - size_t filter_table_offset; + size_t filter_table_offset_; + bool prev_have_uv_pass = false; + bool prev_have_motion_pass = false; + bool prev_have_ao_pass = false; public: Film(); @@ -98,14 +79,20 @@ class Film : public Node { void device_update(Device *device, DeviceScene *dscene, Scene *scene); void device_free(Device *device, DeviceScene *dscene, Scene *scene); - void tag_passes_update(Scene *scene, const vector &passes_, bool update_passes = true); - int get_aov_offset(Scene *scene, string name, bool &is_color); - int get_pass_stride() const; - int get_denoising_data_offset() const; - int get_denoising_clean_offset() const; - size_t get_filter_table_offset() const; + /* Update passes so that they contain all passes required for the configured functionality. + * + * If `add_sample_count_pass` is true then the SAMPLE_COUNT pass is ensured to be added. */ + void update_passes(Scene *scene, bool add_sample_count_pass); + + uint get_kernel_features(const Scene *scene) const; + + private: + void add_auto_pass(Scene *scene, PassType type, const char *name = nullptr); + void add_auto_pass(Scene *scene, PassType type, PassMode mode, const char *name = nullptr); + void remove_auto_passes(Scene *scene); + void finalize_passes(Scene *scene, const bool use_denoise); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/geometry.cpp b/intern/cycles/scene/geometry.cpp similarity index 86% rename from intern/cycles/render/geometry.cpp rename to intern/cycles/scene/geometry.cpp index 7ec1d2d9abb..8a3fc522d22 100644 --- a/intern/cycles/render/geometry.cpp +++ b/intern/cycles/scene/geometry.cpp @@ -19,39 +19,33 @@ #include "device/device.h" -#include "render/attribute.h" -#include "render/camera.h" -#include "render/geometry.h" -#include "render/hair.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" -#include "render/volume.h" +#include "scene/attribute.h" +#include "scene/camera.h" +#include "scene/geometry.h" +#include "scene/hair.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" +#include "scene/volume.h" -#include "subd/subd_patch_table.h" -#include "subd/subd_split.h" +#include "subd/patch_table.h" +#include "subd/split.h" -#include "kernel/osl/osl_globals.h" +#include "kernel/osl/globals.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN /* Geometry */ -PackFlags operator|=(PackFlags &pack_flags, uint32_t value) -{ - pack_flags = (PackFlags)((uint32_t)pack_flags | value); - return pack_flags; -} - NODE_ABSTRACT_DEFINE(Geometry) { NodeType *type = NodeType::add("geometry_base", NULL); @@ -79,7 +73,6 @@ Geometry::Geometry(const NodeType *node_type, const Type type) bvh = NULL; attr_map_offset = 0; - optix_prim_offset = 0; prim_offset = 0; } @@ -215,6 +208,12 @@ void Geometry::compute_bvh( msg += string_printf("%s %u/%u", name.c_str(), (uint)(n + 1), (uint)total); Object object; + + /* Ensure all visibility bits are set at the geometry level BVH. In + * the object level BVH is where actual visibility is tested. */ + object.set_is_shadow_catcher(true); + object.set_visibility(~0); + object.set_geometry(this); vector geometry; @@ -315,7 +314,7 @@ void GeometryManager::update_osl_attributes(Device *device, { #ifdef WITH_OSL /* for OSL, a hash map is used to lookup the attribute by name. */ - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); og->object_name_map.clear(); og->attribute_map.clear(); @@ -701,9 +700,9 @@ void GeometryManager::update_attribute_element_offset(Geometry *geom, if (element == ATTR_ELEMENT_CURVE) offset -= hair->prim_offset; else if (element == ATTR_ELEMENT_CURVE_KEY) - offset -= hair->curvekey_offset; + offset -= hair->curve_key_offset; else if (element == ATTR_ELEMENT_CURVE_KEY_MOTION) - offset -= hair->curvekey_offset; + offset -= hair->curve_key_offset; } } else { @@ -735,6 +734,10 @@ void GeometryManager::device_update_attributes(Device *device, Shader *shader = static_cast(node); geom_attributes[i].add(shader->attributes); } + + if (geom->is_hair() && static_cast(geom)->need_shadow_transparency()) { + geom_attributes[i].add(ATTR_STD_SHADOW_TRANSPARENCY); + } } /* convert object attributes to use the same data structures as geometry ones */ @@ -788,11 +791,6 @@ void GeometryManager::device_update_attributes(Device *device, foreach (AttributeRequest &req, attributes.requests) { Attribute *attr = geom->attributes.find(req); - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (attr && attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - update_attribute_element_size(geom, attr, ATTR_PRIM_GEOMETRY, @@ -805,11 +803,6 @@ void GeometryManager::device_update_attributes(Device *device, Mesh *mesh = static_cast(geom); Attribute *subd_attr = mesh->subd_attributes.find(req); - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (subd_attr && subd_attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - update_attribute_element_size(mesh, subd_attr, ATTR_PRIM_SUBD, @@ -864,11 +857,6 @@ void GeometryManager::device_update_attributes(Device *device, Attribute *attr = geom->attributes.find(req); if (attr) { - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - /* force a copy if we need to reallocate all the data */ attr->modified |= attributes_need_realloc[Attribute::kernel_type(*attr)]; } @@ -892,11 +880,6 @@ void GeometryManager::device_update_attributes(Device *device, Attribute *subd_attr = mesh->subd_attributes.find(req); if (subd_attr) { - /* Vertex normals are stored in DeviceScene.tri_vnormal. */ - if (subd_attr->std == ATTR_STD_VERTEX_NORMAL) { - continue; - } - /* force a copy if we need to reallocate all the data */ subd_attr->modified |= attributes_need_realloc[Attribute::kernel_type(*subd_attr)]; } @@ -986,28 +969,22 @@ void GeometryManager::mesh_calc_offset(Scene *scene, BVHLayout bvh_layout) size_t vert_size = 0; size_t tri_size = 0; - size_t curve_key_size = 0; size_t curve_size = 0; + size_t curve_key_size = 0; + size_t curve_segment_size = 0; size_t patch_size = 0; size_t face_size = 0; size_t corner_size = 0; - size_t optix_prim_size = 0; - foreach (Geometry *geom, scene->geometry) { - if (geom->optix_prim_offset != optix_prim_size) { - /* Need to rebuild BVH in OptiX, since refit only allows modified mesh data there */ - const bool has_optix_bvh = bvh_layout == BVH_LAYOUT_OPTIX || - bvh_layout == BVH_LAYOUT_MULTI_OPTIX || - bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE; - geom->need_update_rebuild |= has_optix_bvh; - geom->need_update_bvh_for_offset = true; - } + bool prim_offset_changed = false; if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { Mesh *mesh = static_cast(geom); + prim_offset_changed = (mesh->prim_offset != tri_size); + mesh->vert_offset = vert_size; mesh->prim_offset = tri_size; @@ -1031,27 +1008,35 @@ void GeometryManager::mesh_calc_offset(Scene *scene, BVHLayout bvh_layout) face_size += mesh->get_num_subd_faces(); corner_size += mesh->subd_face_corners.size(); - - mesh->optix_prim_offset = optix_prim_size; - optix_prim_size += mesh->num_triangles(); } else if (geom->is_hair()) { Hair *hair = static_cast(geom); - hair->curvekey_offset = curve_key_size; + prim_offset_changed = (hair->curve_segment_offset != curve_segment_size); + hair->curve_key_offset = curve_key_size; + hair->curve_segment_offset = curve_segment_size; hair->prim_offset = curve_size; - curve_key_size += hair->get_curve_keys().size(); curve_size += hair->num_curves(); + curve_key_size += hair->get_curve_keys().size(); + curve_segment_size += hair->num_segments(); + } - hair->optix_prim_offset = optix_prim_size; - optix_prim_size += hair->num_segments(); + if (prim_offset_changed) { + /* Need to rebuild BVH in OptiX, since refit only allows modified mesh data there */ + const bool has_optix_bvh = bvh_layout == BVH_LAYOUT_OPTIX || + bvh_layout == BVH_LAYOUT_MULTI_OPTIX || + bvh_layout == BVH_LAYOUT_MULTI_OPTIX_EMBREE; + geom->need_update_rebuild |= has_optix_bvh; + geom->need_update_bvh_for_offset = true; } } } -void GeometryManager::device_update_mesh( - Device *, DeviceScene *dscene, Scene *scene, bool for_displacement, Progress &progress) +void GeometryManager::device_update_mesh(Device *, + DeviceScene *dscene, + Scene *scene, + Progress &progress) { /* Count. */ size_t vert_size = 0; @@ -1059,6 +1044,7 @@ void GeometryManager::device_update_mesh( size_t curve_key_size = 0; size_t curve_size = 0; + size_t curve_segment_size = 0; size_t patch_size = 0; @@ -1085,31 +1071,7 @@ void GeometryManager::device_update_mesh( curve_key_size += hair->get_curve_keys().size(); curve_size += hair->num_curves(); - } - } - - /* Create mapping from triangle to primitive triangle array. */ - vector tri_prim_index(tri_size); - if (for_displacement) { - /* For displacement kernels we do some trickery to make them believe - * we've got all required data ready. However, that data is different - * from final render kernels since we don't have BVH yet, so can't - * really use same semantic of arrays. - */ - foreach (Geometry *geom, scene->geometry) { - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast(geom); - for (size_t i = 0; i < mesh->num_triangles(); ++i) { - tri_prim_index[i + mesh->prim_offset] = 3 * (i + mesh->prim_offset); - } - } - } - } - else { - for (size_t i = 0; i < dscene->prim_index.size(); ++i) { - if ((dscene->prim_type[i] & PRIMITIVE_ALL_TRIANGLE) != 0) { - tri_prim_index[dscene->prim_index[i]] = dscene->prim_tri_index[i]; - } + curve_segment_size += hair->num_segments(); } } @@ -1118,6 +1080,7 @@ void GeometryManager::device_update_mesh( /* normals */ progress.set_status("Updating Mesh", "Computing normals"); + float4 *tri_verts = dscene->tri_verts.alloc(tri_size * 3); uint *tri_shader = dscene->tri_shader.alloc(tri_size); float4 *vnormal = dscene->tri_vnormal.alloc(vert_size); uint4 *tri_vindex = dscene->tri_vindex.alloc(tri_size); @@ -1143,13 +1106,12 @@ void GeometryManager::device_update_mesh( mesh->pack_normals(&vnormal[mesh->vert_offset]); } - if (mesh->triangles_is_modified() || mesh->vert_patch_uv_is_modified() || copy_all_data) { - mesh->pack_verts(tri_prim_index, + if (mesh->verts_is_modified() || mesh->triangles_is_modified() || + mesh->vert_patch_uv_is_modified() || copy_all_data) { + mesh->pack_verts(&tri_verts[mesh->prim_offset * 3], &tri_vindex[mesh->prim_offset], &tri_patch[mesh->prim_offset], - &tri_patch_uv[mesh->vert_offset], - mesh->vert_offset, - mesh->prim_offset); + &tri_patch_uv[mesh->vert_offset]); } if (progress.get_cancel()) @@ -1160,6 +1122,7 @@ void GeometryManager::device_update_mesh( /* vertex coordinates */ progress.set_status("Updating Mesh", "Copying Mesh to device"); + dscene->tri_verts.copy_to_device_if_modified(); dscene->tri_shader.copy_to_device_if_modified(); dscene->tri_vnormal.copy_to_device_if_modified(); dscene->tri_vindex.copy_to_device_if_modified(); @@ -1167,13 +1130,16 @@ void GeometryManager::device_update_mesh( dscene->tri_patch_uv.copy_to_device_if_modified(); } - if (curve_size != 0) { - progress.set_status("Updating Mesh", "Copying Strands to device"); + if (curve_segment_size != 0) { + progress.set_status("Updating Mesh", "Copying Curves to device"); float4 *curve_keys = dscene->curve_keys.alloc(curve_key_size); - float4 *curves = dscene->curves.alloc(curve_size); + KernelCurve *curves = dscene->curves.alloc(curve_size); + KernelCurveSegment *curve_segments = dscene->curve_segments.alloc(curve_segment_size); - const bool copy_all_data = dscene->curve_keys.need_realloc() || dscene->curves.need_realloc(); + const bool copy_all_data = dscene->curve_keys.need_realloc() || + dscene->curves.need_realloc() || + dscene->curve_segments.need_realloc(); foreach (Geometry *geom, scene->geometry) { if (geom->is_hair()) { @@ -1189,9 +1155,9 @@ void GeometryManager::device_update_mesh( } hair->pack_curves(scene, - &curve_keys[hair->curvekey_offset], + &curve_keys[hair->curve_key_offset], &curves[hair->prim_offset], - hair->curvekey_offset); + &curve_segments[hair->curve_segment_offset]); if (progress.get_cancel()) return; } @@ -1199,6 +1165,7 @@ void GeometryManager::device_update_mesh( dscene->curve_keys.copy_to_device_if_modified(); dscene->curves.copy_to_device_if_modified(); + dscene->curve_segments.copy_to_device_if_modified(); } if (patch_size != 0 && dscene->patches.need_realloc()) { @@ -1209,10 +1176,7 @@ void GeometryManager::device_update_mesh( foreach (Geometry *geom, scene->geometry) { if (geom->is_mesh()) { Mesh *mesh = static_cast(geom); - mesh->pack_patches(&patch_data[mesh->patch_offset], - mesh->vert_offset, - mesh->face_offset, - mesh->corner_offset); + mesh->pack_patches(&patch_data[mesh->patch_offset]); if (mesh->patch_table) { mesh->patch_table->copy_adjusting_offsets(&patch_data[mesh->patch_table_offset], @@ -1226,23 +1190,6 @@ void GeometryManager::device_update_mesh( dscene->patches.copy_to_device(); } - - if (for_displacement) { - float4 *prim_tri_verts = dscene->prim_tri_verts.alloc(tri_size * 3); - foreach (Geometry *geom, scene->geometry) { - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast(geom); - for (size_t i = 0; i < mesh->num_triangles(); ++i) { - Mesh::Triangle t = mesh->get_triangle(i); - size_t offset = 3 * (i + mesh->prim_offset); - prim_tri_verts[offset + 0] = float3_to_float4(mesh->verts[t.v[0]]); - prim_tri_verts[offset + 1] = float3_to_float4(mesh->verts[t.v[1]]); - prim_tri_verts[offset + 2] = float3_to_float4(mesh->verts[t.v[2]]); - } - } - } - dscene->prim_tri_verts.copy_to_device(); - } } void GeometryManager::device_update_bvh(Device *device, @@ -1270,16 +1217,6 @@ void GeometryManager::device_update_bvh(Device *device, const bool can_refit = scene->bvh != nullptr && (bparams.bvh_layout == BVHLayout::BVH_LAYOUT_OPTIX); - PackFlags pack_flags = PackFlags::PACK_NONE; - - if (scene->bvh == nullptr) { - pack_flags |= PackFlags::PACK_ALL; - } - - if (dscene->prim_visibility.is_modified()) { - pack_flags |= PackFlags::PACK_VISIBILITY; - } - BVH *bvh = scene->bvh; if (!scene->bvh) { bvh = scene->bvh = BVH::create(bparams, scene->geometry, scene->objects, device); @@ -1298,77 +1235,7 @@ void GeometryManager::device_update_bvh(Device *device, pack = std::move(static_cast(bvh)->pack); } else { - progress.set_status("Updating Scene BVH", "Packing BVH primitives"); - - size_t num_prims = 0; - size_t num_tri_verts = 0; - foreach (Geometry *geom, scene->geometry) { - if (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME) { - Mesh *mesh = static_cast(geom); - num_prims += mesh->num_triangles(); - num_tri_verts += 3 * mesh->num_triangles(); - } - else if (geom->is_hair()) { - Hair *hair = static_cast(geom); - num_prims += hair->num_segments(); - } - } - pack.root_index = -1; - - if (pack_flags != PackFlags::PACK_ALL) { - /* if we do not need to recreate the BVH, then only the vertices are updated, so we can - * safely retake the memory */ - dscene->prim_tri_verts.give_data(pack.prim_tri_verts); - - if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) { - dscene->prim_visibility.give_data(pack.prim_visibility); - } - } - else { - /* It is not strictly necessary to skip those resizes we if do not have to repack, as the OS - * will not allocate pages if we do not touch them, however it does help catching bugs. */ - pack.prim_tri_index.resize(num_prims); - pack.prim_tri_verts.resize(num_tri_verts); - pack.prim_type.resize(num_prims); - pack.prim_index.resize(num_prims); - pack.prim_object.resize(num_prims); - pack.prim_visibility.resize(num_prims); - } - - // Merge visibility flags of all objects and find object index for non-instanced geometry - unordered_map> geometry_to_object_info; - geometry_to_object_info.reserve(scene->geometry.size()); - foreach (Object *ob, scene->objects) { - const Geometry *const geom = ob->get_geometry(); - pair &info = geometry_to_object_info[geom]; - info.second |= ob->visibility_for_tracing(); - if (!geom->is_instanced()) { - info.first = ob->get_device_index(); - } - } - - TaskPool pool; - // Iterate over scene mesh list instead of objects, since 'optix_prim_offset' was calculated - // based on that list, which may be ordered differently from the object list. - foreach (Geometry *geom, scene->geometry) { - /* Make a copy of the pack_flags so the current geometry's flags do not pollute the others'. - */ - PackFlags geom_pack_flags = pack_flags; - - if (geom->is_modified()) { - geom_pack_flags |= PackFlags::PACK_VERTICES; - } - - if (geom_pack_flags == PACK_NONE) { - continue; - } - - const pair &info = geometry_to_object_info[geom]; - pool.push(function_bind( - &Geometry::pack_primitives, geom, &pack, info.first, info.second, geom_pack_flags)); - } - pool.wait_work(); } /* copy to device */ @@ -1389,31 +1256,23 @@ void GeometryManager::device_update_bvh(Device *device, dscene->object_node.steal_data(pack.object_node); dscene->object_node.copy_to_device(); } - if (pack.prim_tri_index.size() && (dscene->prim_tri_index.need_realloc() || has_bvh2_layout)) { - dscene->prim_tri_index.steal_data(pack.prim_tri_index); - dscene->prim_tri_index.copy_to_device(); - } - if (pack.prim_tri_verts.size()) { - dscene->prim_tri_verts.steal_data(pack.prim_tri_verts); - dscene->prim_tri_verts.copy_to_device(); - } - if (pack.prim_type.size() && (dscene->prim_type.need_realloc() || has_bvh2_layout)) { + if (pack.prim_type.size()) { dscene->prim_type.steal_data(pack.prim_type); dscene->prim_type.copy_to_device(); } - if (pack.prim_visibility.size() && (dscene->prim_visibility.is_modified() || has_bvh2_layout)) { + if (pack.prim_visibility.size()) { dscene->prim_visibility.steal_data(pack.prim_visibility); dscene->prim_visibility.copy_to_device(); } - if (pack.prim_index.size() && (dscene->prim_index.need_realloc() || has_bvh2_layout)) { + if (pack.prim_index.size()) { dscene->prim_index.steal_data(pack.prim_index); dscene->prim_index.copy_to_device(); } - if (pack.prim_object.size() && (dscene->prim_object.need_realloc() || has_bvh2_layout)) { + if (pack.prim_object.size()) { dscene->prim_object.steal_data(pack.prim_object); dscene->prim_object.copy_to_device(); } - if (pack.prim_time.size() && (dscene->prim_time.need_realloc() || has_bvh2_layout)) { + if (pack.prim_time.size()) { dscene->prim_time.steal_data(pack.prim_time); dscene->prim_time.copy_to_device(); } @@ -1643,8 +1502,6 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro dscene->bvh_nodes.tag_realloc(); dscene->bvh_leaf_nodes.tag_realloc(); dscene->object_node.tag_realloc(); - dscene->prim_tri_verts.tag_realloc(); - dscene->prim_tri_index.tag_realloc(); dscene->prim_type.tag_realloc(); dscene->prim_visibility.tag_realloc(); dscene->prim_index.tag_realloc(); @@ -1652,6 +1509,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro dscene->prim_time.tag_realloc(); if (device_update_flags & DEVICE_MESH_DATA_NEEDS_REALLOC) { + dscene->tri_verts.tag_realloc(); dscene->tri_vnormal.tag_realloc(); dscene->tri_vindex.tag_realloc(); dscene->tri_patch.tag_realloc(); @@ -1663,6 +1521,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro if (device_update_flags & DEVICE_CURVE_DATA_NEEDS_REALLOC) { dscene->curves.tag_realloc(); dscene->curve_keys.tag_realloc(); + dscene->curve_segments.tag_realloc(); } } @@ -1705,6 +1564,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro if (device_update_flags & DEVICE_MESH_DATA_MODIFIED) { /* if anything else than vertices or shaders are modified, we would need to reallocate, so * these are the only arrays that can be updated */ + dscene->tri_verts.tag_modified(); dscene->tri_vnormal.tag_modified(); dscene->tri_shader.tag_modified(); } @@ -1712,6 +1572,7 @@ void GeometryManager::device_update_preprocess(Device *device, Scene *scene, Pro if (device_update_flags & DEVICE_CURVE_DATA_MODIFIED) { dscene->curve_keys.tag_modified(); dscene->curves.tag_modified(); + dscene->curve_segments.tag_modified(); } need_flags_update = false; @@ -1727,9 +1588,20 @@ void GeometryManager::device_update_displacement_images(Device *device, set bump_images; foreach (Geometry *geom, scene->geometry) { if (geom->is_modified()) { + /* Geometry-level check for hair shadow transparency. + * This matches the logic in the `Hair::update_shadow_transparency()`, avoiding access to + * possible non-loaded images. */ + bool need_shadow_transparency = false; + if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast(geom); + need_shadow_transparency = hair->need_shadow_transparency(); + } + foreach (Node *node, geom->get_used_shaders()) { Shader *shader = static_cast(node); - if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) { + const bool is_true_displacement = (shader->has_displacement && + shader->get_displacement_method() != DISPLACE_BUMP); + if (!is_true_displacement && !need_shadow_transparency) { continue; } foreach (ShaderNode *node, shader->graph->nodes) { @@ -1802,6 +1674,7 @@ void GeometryManager::device_update(Device *device, VLOG(1) << "Total " << scene->geometry.size() << " meshes."; bool true_displacement_used = false; + bool curve_shadow_transparency_used = false; size_t total_tess_needed = 0; { @@ -1812,26 +1685,33 @@ void GeometryManager::device_update(Device *device, }); foreach (Geometry *geom, scene->geometry) { - if (geom->is_modified() && - (geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) { - Mesh *mesh = static_cast(geom); + if (geom->is_modified()) { + if ((geom->geometry_type == Geometry::MESH || geom->geometry_type == Geometry::VOLUME)) { + Mesh *mesh = static_cast(geom); - /* Update normals. */ - mesh->add_face_normals(); - mesh->add_vertex_normals(); + /* Update normals. */ + mesh->add_face_normals(); + mesh->add_vertex_normals(); - if (mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) { - mesh->add_undisplaced(); + if (mesh->need_attribute(scene, ATTR_STD_POSITION_UNDISPLACED)) { + mesh->add_undisplaced(); + } + + /* Test if we need tessellation. */ + if (mesh->need_tesselation()) { + total_tess_needed++; + } + + /* Test if we need displacement. */ + if (mesh->has_true_displacement()) { + true_displacement_used = true; + } } - - /* Test if we need tessellation. */ - if (mesh->need_tesselation()) { - total_tess_needed++; - } - - /* Test if we need displacement. */ - if (mesh->has_true_displacement()) { - true_displacement_used = true; + else if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast(geom); + if (hair->need_shadow_transparency()) { + curve_shadow_transparency_used = true; + } } if (progress.get_cancel()) { @@ -1855,8 +1735,8 @@ void GeometryManager::device_update(Device *device, }); Camera *dicing_camera = scene->dicing_camera; - dicing_camera->set_screen_size_and_resolution( - dicing_camera->get_full_width(), dicing_camera->get_full_height(), 1); + dicing_camera->set_screen_size(dicing_camera->get_full_width(), + dicing_camera->get_full_height()); dicing_camera->update(scene); size_t i = 0; @@ -1895,7 +1775,7 @@ void GeometryManager::device_update(Device *device, /* Update images needed for true displacement. */ bool old_need_object_flags_update = false; - if (true_displacement_used) { + if (true_displacement_used || curve_shadow_transparency_used) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry( @@ -1913,14 +1793,14 @@ void GeometryManager::device_update(Device *device, const BVHLayout bvh_layout = BVHParams::best_bvh_layout(scene->params.bvh_layout, device->get_bvh_layout_mask()); mesh_calc_offset(scene, bvh_layout); - if (true_displacement_used) { + if (true_displacement_used || curve_shadow_transparency_used) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry( {"device_update (displacement: copy meshes to device)", time}); } }); - device_update_mesh(device, dscene, scene, true, progress); + device_update_mesh(device, dscene, scene, progress); } if (progress.get_cancel()) { return; @@ -1938,11 +1818,15 @@ void GeometryManager::device_update(Device *device, } } - /* Update displacement. */ + /* Update displacement and hair shadow transparency. */ bool displacement_done = false; + bool curve_shadow_transparency_done = false; size_t num_bvh = 0; { + /* Copy constant data needed by shader evaluation. */ + device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); + scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry({"device_update (displacement)", time}); @@ -1953,10 +1837,16 @@ void GeometryManager::device_update(Device *device, if (geom->is_modified()) { if (geom->is_mesh()) { Mesh *mesh = static_cast(geom); - if (displace(device, dscene, scene, mesh, progress)) { + if (displace(device, scene, mesh, progress)) { displacement_done = true; } } + else if (geom->geometry_type == Geometry::HAIR) { + Hair *hair = static_cast(geom); + if (hair->update_shadow_transparency(device, scene, progress)) { + curve_shadow_transparency_done = true; + } + } } if (geom->is_modified() || geom->need_update_bvh_for_offset) { @@ -1976,7 +1866,7 @@ void GeometryManager::device_update(Device *device, } /* Device re-update after displacement. */ - if (displacement_done) { + if (displacement_done || curve_shadow_transparency_done) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->geometry.times.add_entry( @@ -2072,7 +1962,7 @@ void GeometryManager::device_update(Device *device, {"device_update (copy meshes to device)", time}); } }); - device_update_mesh(device, dscene, scene, false, progress); + device_update_mesh(device, dscene, scene, progress); if (progress.get_cancel()) { return; } @@ -2105,13 +1995,12 @@ void GeometryManager::device_update(Device *device, dscene->bvh_nodes.clear_modified(); dscene->bvh_leaf_nodes.clear_modified(); dscene->object_node.clear_modified(); - dscene->prim_tri_verts.clear_modified(); - dscene->prim_tri_index.clear_modified(); dscene->prim_type.clear_modified(); dscene->prim_visibility.clear_modified(); dscene->prim_index.clear_modified(); dscene->prim_object.clear_modified(); dscene->prim_time.clear_modified(); + dscene->tri_verts.clear_modified(); dscene->tri_shader.clear_modified(); dscene->tri_vindex.clear_modified(); dscene->tri_patch.clear_modified(); @@ -2119,6 +2008,7 @@ void GeometryManager::device_update(Device *device, dscene->tri_patch_uv.clear_modified(); dscene->curves.clear_modified(); dscene->curve_keys.clear_modified(); + dscene->curve_segments.clear_modified(); dscene->patches.clear_modified(); dscene->attributes_map.clear_modified(); dscene->attributes_float.clear_modified(); @@ -2132,13 +2022,12 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc dscene->bvh_nodes.free_if_need_realloc(force_free); dscene->bvh_leaf_nodes.free_if_need_realloc(force_free); dscene->object_node.free_if_need_realloc(force_free); - dscene->prim_tri_verts.free_if_need_realloc(force_free); - dscene->prim_tri_index.free_if_need_realloc(force_free); dscene->prim_type.free_if_need_realloc(force_free); dscene->prim_visibility.free_if_need_realloc(force_free); dscene->prim_index.free_if_need_realloc(force_free); dscene->prim_object.free_if_need_realloc(force_free); dscene->prim_time.free_if_need_realloc(force_free); + dscene->tri_verts.free_if_need_realloc(force_free); dscene->tri_shader.free_if_need_realloc(force_free); dscene->tri_vnormal.free_if_need_realloc(force_free); dscene->tri_vindex.free_if_need_realloc(force_free); @@ -2146,6 +2035,7 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc dscene->tri_patch_uv.free_if_need_realloc(force_free); dscene->curves.free_if_need_realloc(force_free); dscene->curve_keys.free_if_need_realloc(force_free); + dscene->curve_segments.free_if_need_realloc(force_free); dscene->patches.free_if_need_realloc(force_free); dscene->attributes_map.free_if_need_realloc(force_free); dscene->attributes_float.free_if_need_realloc(force_free); @@ -2157,7 +2047,7 @@ void GeometryManager::device_free(Device *device, DeviceScene *dscene, bool forc dscene->data.bvh.bvh_layout = BVH_LAYOUT_NONE; #ifdef WITH_OSL - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); if (og) { og->object_name_map.clear(); diff --git a/intern/cycles/render/geometry.h b/intern/cycles/scene/geometry.h similarity index 85% rename from intern/cycles/render/geometry.h rename to intern/cycles/scene/geometry.h index 7db122f69cb..335bcdcd0b7 100644 --- a/intern/cycles/render/geometry.h +++ b/intern/cycles/scene/geometry.h @@ -19,15 +19,15 @@ #include "graph/node.h" -#include "bvh/bvh_params.h" +#include "bvh/params.h" -#include "render/attribute.h" +#include "scene/attribute.h" -#include "util/util_boundbox.h" -#include "util/util_set.h" -#include "util/util_transform.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/boundbox.h" +#include "util/set.h" +#include "util/transform.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -43,24 +43,6 @@ class Shader; class Volume; struct PackedBVH; -/* Flags used to determine which geometry data need to be packed. */ -enum PackFlags : uint32_t { - PACK_NONE = 0u, - - /* Pack the geometry information (e.g. triangle or curve keys indices). */ - PACK_GEOMETRY = (1u << 0), - - /* Pack the vertices, for Meshes and Volumes' bounding meshes. */ - PACK_VERTICES = (1u << 1), - - /* Pack the visibility flags for each triangle or curve. */ - PACK_VISIBILITY = (1u << 2), - - PACK_ALL = (PACK_GEOMETRY | PACK_VERTICES | PACK_VISIBILITY), -}; - -PackFlags operator|=(PackFlags &pack_flags, uint32_t value); - /* Geometry * * Base class for geometric types like Mesh and Hair. */ @@ -100,7 +82,6 @@ class Geometry : public Node { BVH *bvh; size_t attr_map_offset; size_t prim_offset; - size_t optix_prim_offset; /* Shader Properties */ bool has_volume; /* Set in the device_update_flags(). */ @@ -144,10 +125,7 @@ class Geometry : public Node { int n, int total); - virtual void pack_primitives(PackedBVH *pack, - int object, - uint visibility, - PackFlags pack_flags) = 0; + virtual PrimitiveType primitive_type() const = 0; /* Check whether the geometry should have own BVH built separately. Briefly, * own BVH is needed for geometry, if: @@ -241,7 +219,7 @@ class GeometryManager { void collect_statistics(const Scene *scene, RenderStats *stats); protected: - bool displace(Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress); + bool displace(Device *device, Scene *scene, Mesh *mesh, Progress &progress); void create_volume_mesh(Volume *volume, Progress &progress); @@ -260,11 +238,7 @@ class GeometryManager { void device_update_object(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); - void device_update_mesh(Device *device, - DeviceScene *dscene, - Scene *scene, - bool for_displacement, - Progress &progress); + void device_update_mesh(Device *device, DeviceScene *dscene, Scene *scene, Progress &progress); void device_update_attributes(Device *device, DeviceScene *dscene, diff --git a/intern/cycles/render/hair.cpp b/intern/cycles/scene/hair.cpp similarity index 78% rename from intern/cycles/render/hair.cpp rename to intern/cycles/scene/hair.cpp index 72fc612c0c0..2951a609ae9 100644 --- a/intern/cycles/render/hair.cpp +++ b/intern/cycles/scene/hair.cpp @@ -16,9 +16,14 @@ #include "bvh/bvh.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/scene.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/object.h" +#include "scene/scene.h" + +#include "integrator/shader_eval.h" + +#include "util/progress.h" CCL_NAMESPACE_BEGIN @@ -295,7 +300,8 @@ NODE_DEFINE(Hair) Hair::Hair() : Geometry(get_node_type(), Geometry::HAIR) { - curvekey_offset = 0; + curve_key_offset = 0; + curve_segment_offset = 0; curve_shape = CURVE_RIBBON; } @@ -440,6 +446,9 @@ void Hair::apply_transform(const Transform &tfm, const bool apply_to_motion) curve_radius[i] = radius; } + tag_curve_keys_modified(); + tag_curve_radius_modified(); + if (apply_to_motion) { Attribute *curve_attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); @@ -462,8 +471,8 @@ void Hair::apply_transform(const Transform &tfm, const bool apply_to_motion) void Hair::pack_curves(Scene *scene, float4 *curve_key_co, - float4 *curve_data, - size_t curvekey_offset) + KernelCurve *curves, + KernelCurveSegment *curve_segments) { size_t curve_keys_size = curve_keys.size(); @@ -477,7 +486,10 @@ void Hair::pack_curves(Scene *scene, } /* pack curve segments */ + const PrimitiveType type = primitive_type(); + size_t curve_num = num_curves(); + size_t index = 0; for (size_t i = 0; i < curve_num; i++) { Curve curve = get_curve(i); @@ -487,56 +499,134 @@ void Hair::pack_curves(Scene *scene, scene->default_surface; shader_id = scene->shader_manager->get_shader_id(shader, false); - curve_data[i] = make_float4(__int_as_float(curve.first_key + curvekey_offset), - __int_as_float(curve.num_keys), - __int_as_float(shader_id), - 0.0f); + curves[i].shader_id = shader_id; + curves[i].first_key = curve_key_offset + curve.first_key; + curves[i].num_keys = curve.num_keys; + curves[i].type = type; + + for (int k = 0; k < curve.num_segments(); ++k, ++index) { + curve_segments[index].prim = prim_offset + i; + curve_segments[index].type = PRIMITIVE_PACK_SEGMENT(type, k); + } } } -void Hair::pack_primitives(PackedBVH *pack, int object, uint visibility, PackFlags pack_flags) +PrimitiveType Hair::primitive_type() const { - if (curve_first_key.empty()) - return; + return has_motion_blur() ? + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : + PRIMITIVE_MOTION_CURVE_THICK) : + ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : PRIMITIVE_CURVE_THICK); +} - /* Separate loop as other arrays are not initialized if their packing is not required. */ - if ((pack_flags & PACK_VISIBILITY) != 0) { - unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset]; +/* Fill in coordinates for curve transparency shader evaluation on device. */ +static int fill_shader_input(const Hair *hair, + const int object_index, + device_vector &d_input) +{ + int d_input_size = 0; + KernelShaderEvalInput *d_input_data = d_input.data(); - size_t index = 0; - for (size_t j = 0; j < num_curves(); ++j) { - Curve curve = get_curve(j); - for (size_t k = 0; k < curve.num_segments(); ++k, ++index) { - prim_visibility[index] = visibility; - } + const int num_curves = hair->num_curves(); + for (int i = 0; i < num_curves; i++) { + const Hair::Curve curve = hair->get_curve(i); + const int num_segments = curve.num_segments(); + + for (int j = 0; j < num_segments + 1; j++) { + KernelShaderEvalInput in; + in.object = object_index; + in.prim = hair->prim_offset + i; + in.u = (j < num_segments) ? 0.0f : 1.0f; + in.v = (j < num_segments) ? __int_as_float(j) : __int_as_float(j - 1); + d_input_data[d_input_size++] = in; } } - if ((pack_flags & PACK_GEOMETRY) != 0) { - unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset]; - int *prim_type = &pack->prim_type[optix_prim_offset]; - int *prim_index = &pack->prim_index[optix_prim_offset]; - int *prim_object = &pack->prim_object[optix_prim_offset]; - // 'pack->prim_time' is unused by Embree and OptiX + return d_input_size; +} - uint type = has_motion_blur() ? - ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_MOTION_CURVE_RIBBON : - PRIMITIVE_MOTION_CURVE_THICK) : - ((curve_shape == CURVE_RIBBON) ? PRIMITIVE_CURVE_RIBBON : - PRIMITIVE_CURVE_THICK); +/* Read back curve transparency shader output. */ +static void read_shader_output(float *shadow_transparency, + bool &is_fully_opaque, + const device_vector &d_output) +{ + const int num_keys = d_output.size(); + const float *output_data = d_output.data(); + bool is_opaque = true; - size_t index = 0; - for (size_t j = 0; j < num_curves(); ++j) { - Curve curve = get_curve(j); - for (size_t k = 0; k < curve.num_segments(); ++k, ++index) { - prim_tri_index[index] = -1; - prim_type[index] = PRIMITIVE_PACK_SEGMENT(type, k); - // Each curve segment points back to its curve index - prim_index[index] = j + prim_offset; - prim_object[index] = object; - } + for (int i = 0; i < num_keys; i++) { + shadow_transparency[i] = output_data[i]; + if (shadow_transparency[i] > 0.0f) { + is_opaque = false; } } + + is_fully_opaque = is_opaque; +} + +bool Hair::need_shadow_transparency() +{ + for (const Node *node : used_shaders) { + const Shader *shader = static_cast(node); + if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) { + return true; + } + } + + return false; +} + +bool Hair::update_shadow_transparency(Device *device, Scene *scene, Progress &progress) +{ + if (!need_shadow_transparency()) { + /* If no shaders with shadow transparency, remove attribute. */ + Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY); + if (attr) { + attributes.remove(attr); + return true; + } + else { + return false; + } + } + + string msg = string_printf("Computing Shadow Transparency %s", name.c_str()); + progress.set_status("Updating Hair", msg); + + /* Create shadow transparency attribute. */ + Attribute *attr = attributes.find(ATTR_STD_SHADOW_TRANSPARENCY); + const bool attribute_exists = (attr != nullptr); + if (!attribute_exists) { + attr = attributes.add(ATTR_STD_SHADOW_TRANSPARENCY); + } + + float *attr_data = attr->data_float(); + + /* Find object index. */ + size_t object_index = OBJECT_NONE; + + for (size_t i = 0; i < scene->objects.size(); i++) { + if (scene->objects[i]->get_geometry() == this) { + object_index = i; + break; + } + } + + /* Evaluate shader on device. */ + ShaderEval shader_eval(device, progress); + bool is_fully_opaque = false; + shader_eval.eval(SHADER_EVAL_CURVE_SHADOW_TRANSPARENCY, + num_keys(), + 1, + function_bind(&fill_shader_input, this, object_index, _1), + function_bind(&read_shader_output, attr_data, is_fully_opaque, _1)); + + if (is_fully_opaque) { + attributes.remove(attr); + return attribute_exists; + } + + return true; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/hair.h b/intern/cycles/scene/hair.h similarity index 90% rename from intern/cycles/render/hair.h rename to intern/cycles/scene/hair.h index e4451d70767..832015058fe 100644 --- a/intern/cycles/render/hair.h +++ b/intern/cycles/scene/hair.h @@ -17,10 +17,12 @@ #ifndef __HAIR_H__ #define __HAIR_H__ -#include "render/geometry.h" +#include "scene/geometry.h" CCL_NAMESPACE_BEGIN +struct KernelCurveSegment; + class Hair : public Geometry { public: NODE_DECLARE @@ -95,7 +97,8 @@ class Hair : public Geometry { NODE_SOCKET_API_ARRAY(array, curve_shader) /* BVH */ - size_t curvekey_offset; + size_t curve_key_offset; + size_t curve_segment_offset; CurveShapeType curve_shape; /* Constructor/Destructor */ @@ -144,12 +147,16 @@ class Hair : public Geometry { void get_uv_tiles(ustring map, unordered_set &tiles) override; /* BVH */ - void pack_curves(Scene *scene, float4 *curve_key_co, float4 *curve_data, size_t curvekey_offset); + void pack_curves(Scene *scene, + float4 *curve_key_co, + KernelCurve *curve, + KernelCurveSegment *curve_segments); - void pack_primitives(PackedBVH *pack, - int object, - uint visibility, - PackFlags pack_flags) override; + PrimitiveType primitive_type() const override; + + /* Attributes */ + bool need_shadow_transparency(); + bool update_shadow_transparency(Device *device, Scene *scene, Progress &progress); }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/image.cpp b/intern/cycles/scene/image.cpp similarity index 97% rename from intern/cycles/render/image.cpp rename to intern/cycles/scene/image.cpp index 27f9b7df1dd..8bb2d87fd1e 100644 --- a/intern/cycles/render/image.cpp +++ b/intern/cycles/scene/image.cpp @@ -14,23 +14,23 @@ * limitations under the License. */ -#include "render/image.h" +#include "scene/image.h" #include "device/device.h" -#include "render/colorspace.h" -#include "render/image_oiio.h" -#include "render/image_vdb.h" -#include "render/scene.h" -#include "render/stats.h" +#include "scene/colorspace.h" +#include "scene/image_oiio.h" +#include "scene/image_vdb.h" +#include "scene/scene.h" +#include "scene/stats.h" -#include "util/util_foreach.h" -#include "util/util_image.h" -#include "util/util_image_impl.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_progress.h" -#include "util/util_task.h" -#include "util/util_texture.h" -#include "util/util_unique_ptr.h" +#include "util/foreach.h" +#include "util/image.h" +#include "util/image_impl.h" +#include "util/log.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/task.h" +#include "util/texture.h" +#include "util/unique_ptr.h" #ifdef WITH_OSL # include @@ -303,7 +303,6 @@ ImageManager::ImageManager(const DeviceInfo &info) animation_frame = 0; /* Set image limits */ - features.has_half_float = info.has_half_images; features.has_nanovdb = info.has_nanovdb; } @@ -357,8 +356,6 @@ void ImageManager::load_image_metadata(Image *img) metadata.detect_colorspace(); - 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)); diff --git a/intern/cycles/render/image.h b/intern/cycles/scene/image.h similarity index 96% rename from intern/cycles/render/image.h rename to intern/cycles/scene/image.h index dede9513d5f..7cf09dd6d8f 100644 --- a/intern/cycles/render/image.h +++ b/intern/cycles/scene/image.h @@ -17,15 +17,15 @@ #ifndef __IMAGE_H__ #define __IMAGE_H__ -#include "device/device_memory.h" +#include "device/memory.h" -#include "render/colorspace.h" +#include "scene/colorspace.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_transform.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/transform.h" +#include "util/unique_ptr.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -100,7 +100,6 @@ class ImageMetaData { /* Information about supported features that Image loaders can use. */ class ImageDeviceFeatures { public: - bool has_half_float; bool has_nanovdb; }; diff --git a/intern/cycles/render/image_oiio.cpp b/intern/cycles/scene/image_oiio.cpp similarity index 96% rename from intern/cycles/render/image_oiio.cpp rename to intern/cycles/scene/image_oiio.cpp index 4867efe6ac0..4cea7fbfb01 100644 --- a/intern/cycles/render/image_oiio.cpp +++ b/intern/cycles/scene/image_oiio.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/image_oiio.h" +#include "scene/image_oiio.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_path.h" +#include "util/image.h" +#include "util/log.h" +#include "util/path.h" CCL_NAMESPACE_BEGIN @@ -30,7 +30,8 @@ OIIOImageLoader::~OIIOImageLoader() { } -bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMetaData &metadata) +bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures & /*features*/, + ImageMetaData &metadata) { /* Perform preliminary checks, with meaningful logging. */ if (!path_exists(filepath.string())) { @@ -76,7 +77,7 @@ bool OIIOImageLoader::load_metadata(const ImageDeviceFeatures &features, ImageMe } /* check if it's half float */ - if (spec.format == TypeDesc::HALF && features.has_half_float) { + if (spec.format == TypeDesc::HALF) { is_half = true; } diff --git a/intern/cycles/render/image_oiio.h b/intern/cycles/scene/image_oiio.h similarity index 98% rename from intern/cycles/render/image_oiio.h rename to intern/cycles/scene/image_oiio.h index a6dbb168b65..87f8b20f254 100644 --- a/intern/cycles/render/image_oiio.h +++ b/intern/cycles/scene/image_oiio.h @@ -17,7 +17,7 @@ #ifndef __IMAGE_OIIO__ #define __IMAGE_OIIO__ -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/image_sky.cpp b/intern/cycles/scene/image_sky.cpp similarity index 95% rename from intern/cycles/render/image_sky.cpp rename to intern/cycles/scene/image_sky.cpp index 7f9b85836f8..4f0877aeb99 100644 --- a/intern/cycles/render/image_sky.cpp +++ b/intern/cycles/scene/image_sky.cpp @@ -14,14 +14,14 @@ * limitations under the License. */ -#include "render/image_sky.h" +#include "scene/image_sky.h" #include "sky_model.h" -#include "util/util_image.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_task.h" +#include "util/image.h" +#include "util/log.h" +#include "util/path.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/image_sky.h b/intern/cycles/scene/image_sky.h similarity index 98% rename from intern/cycles/render/image_sky.h rename to intern/cycles/scene/image_sky.h index 89ff586e7fd..d57e7bbee07 100644 --- a/intern/cycles/render/image_sky.h +++ b/intern/cycles/scene/image_sky.h @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/image_vdb.cpp b/intern/cycles/scene/image_vdb.cpp similarity index 98% rename from intern/cycles/render/image_vdb.cpp rename to intern/cycles/scene/image_vdb.cpp index 4da11c8a140..d3315670390 100644 --- a/intern/cycles/render/image_vdb.cpp +++ b/intern/cycles/scene/image_vdb.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "render/image_vdb.h" +#include "scene/image_vdb.h" -#include "util/util_logging.h" -#include "util/util_openvdb.h" +#include "util/log.h" +#include "util/openvdb.h" #ifdef WITH_OPENVDB # include diff --git a/intern/cycles/render/image_vdb.h b/intern/cycles/scene/image_vdb.h similarity index 98% rename from intern/cycles/render/image_vdb.h rename to intern/cycles/scene/image_vdb.h index 763196f2a15..0a43d83a24f 100644 --- a/intern/cycles/render/image_vdb.h +++ b/intern/cycles/scene/image_vdb.h @@ -24,7 +24,7 @@ # include #endif -#include "render/image.h" +#include "scene/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/integrator.cpp b/intern/cycles/scene/integrator.cpp similarity index 56% rename from intern/cycles/render/integrator.cpp rename to intern/cycles/scene/integrator.cpp index d8749cec9fa..e9ff868c3fc 100644 --- a/intern/cycles/render/integrator.cpp +++ b/intern/cycles/scene/integrator.cpp @@ -14,26 +14,26 @@ * limitations under the License. */ -#include "render/integrator.h" +#include "scene/integrator.h" #include "device/device.h" -#include "render/background.h" -#include "render/camera.h" -#include "render/film.h" -#include "render/jitter.h" -#include "render/light.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/sobol.h" -#include "render/stats.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/film.h" +#include "scene/jitter.h" +#include "scene/light.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/sobol.h" +#include "scene/stats.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_task.h" -#include "util/util_time.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/task.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN @@ -53,6 +53,9 @@ NODE_DEFINE(Integrator) SOCKET_INT(transparent_max_bounce, "Transparent Max Bounce", 7); SOCKET_INT(ao_bounces, "AO Bounces", 0); + SOCKET_FLOAT(ao_factor, "AO Factor", 0.0f); + SOCKET_FLOAT(ao_distance, "AO Distance", FLT_MAX); + SOCKET_FLOAT(ao_additive_factor, "AO Additive Factor", 0.0f); SOCKET_INT(volume_max_steps, "Volume Max Steps", 1024); SOCKET_FLOAT(volume_step_rate, "Volume Step Rate", 1.0f); @@ -66,32 +69,39 @@ NODE_DEFINE(Integrator) SOCKET_BOOLEAN(motion_blur, "Motion Blur", false); SOCKET_INT(aa_samples, "AA Samples", 0); - SOCKET_INT(diffuse_samples, "Diffuse Samples", 1); - SOCKET_INT(glossy_samples, "Glossy Samples", 1); - SOCKET_INT(transmission_samples, "Transmission Samples", 1); - SOCKET_INT(ao_samples, "AO Samples", 1); - SOCKET_INT(mesh_light_samples, "Mesh Light Samples", 1); - SOCKET_INT(subsurface_samples, "Subsurface Samples", 1); - SOCKET_INT(volume_samples, "Volume Samples", 1); SOCKET_INT(start_sample, "Start Sample", 0); + SOCKET_BOOLEAN(use_adaptive_sampling, "Use Adaptive Sampling", false); SOCKET_FLOAT(adaptive_threshold, "Adaptive Threshold", 0.0f); SOCKET_INT(adaptive_min_samples, "Adaptive Min Samples", 0); - SOCKET_BOOLEAN(sample_all_lights_direct, "Sample All Lights Direct", true); - SOCKET_BOOLEAN(sample_all_lights_indirect, "Sample All Lights Indirect", true); SOCKET_FLOAT(light_sampling_threshold, "Light Sampling Threshold", 0.05f); - static NodeEnum method_enum; - method_enum.insert("path", PATH); - method_enum.insert("branched_path", BRANCHED_PATH); - SOCKET_ENUM(method, "Method", method_enum, PATH); - static NodeEnum sampling_pattern_enum; sampling_pattern_enum.insert("sobol", SAMPLING_PATTERN_SOBOL); - sampling_pattern_enum.insert("cmj", SAMPLING_PATTERN_CMJ); sampling_pattern_enum.insert("pmj", SAMPLING_PATTERN_PMJ); SOCKET_ENUM(sampling_pattern, "Sampling Pattern", sampling_pattern_enum, SAMPLING_PATTERN_SOBOL); + SOCKET_FLOAT(scrambling_distance, "Scrambling Distance", 1.0f); + + static NodeEnum denoiser_type_enum; + denoiser_type_enum.insert("optix", DENOISER_OPTIX); + denoiser_type_enum.insert("openimagedenoise", DENOISER_OPENIMAGEDENOISE); + + static NodeEnum denoiser_prefilter_enum; + denoiser_prefilter_enum.insert("none", DENOISER_PREFILTER_NONE); + denoiser_prefilter_enum.insert("fast", DENOISER_PREFILTER_FAST); + denoiser_prefilter_enum.insert("accurate", DENOISER_PREFILTER_ACCURATE); + + /* Default to accurate denoising with OpenImageDenoise. For interactive viewport + * it's best use OptiX and disable the normal pass since it does not always have + * the desired effect for that denoiser. */ + SOCKET_BOOLEAN(use_denoise, "Use Denoiser", false); + SOCKET_ENUM(denoiser_type, "Denoiser Type", denoiser_type_enum, DENOISER_OPENIMAGEDENOISE); + SOCKET_INT(denoise_start_sample, "Start Sample to Denoise", 0); + SOCKET_BOOLEAN(use_denoise_pass_albedo, "Use Albedo Pass for Denoiser", true); + SOCKET_BOOLEAN(use_denoise_pass_normal, "Use Normal Pass for Denoiser", true); + SOCKET_ENUM( + denoiser_prefilter, "Denoiser Type", denoiser_prefilter_enum, DENOISER_PREFILTER_ACCURATE); return type; } @@ -115,13 +125,20 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene } }); - const bool need_update_lut = ao_samples_is_modified() || diffuse_samples_is_modified() || - glossy_samples_is_modified() || max_bounce_is_modified() || - max_transmission_bounce_is_modified() || - mesh_light_samples_is_modified() || method_is_modified() || - sampling_pattern_is_modified() || - subsurface_samples_is_modified() || - transmission_samples_is_modified() || volume_samples_is_modified(); + KernelIntegrator *kintegrator = &dscene->data.integrator; + + /* Adaptive sampling requires PMJ samples. + * + * This also makes detection of sampling pattern a bit more involved: can not rely on the changed + * state of socket, since its value might be different from the effective value used here. So + * instead compare with previous value in the KernelIntegrator. Only do it if the device was + * updated once (in which case the `sample_pattern_lut` will be allocated to a non-zero size). */ + const SamplingPattern new_sampling_pattern = (use_adaptive_sampling) ? SAMPLING_PATTERN_PMJ : + sampling_pattern; + + const bool need_update_lut = max_bounce_is_modified() || max_transmission_bounce_is_modified() || + dscene->sample_pattern_lut.size() == 0 || + kintegrator->sampling_pattern != new_sampling_pattern; if (need_update_lut) { dscene->sample_pattern_lut.tag_realloc(); @@ -129,8 +146,6 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene device_free(device, dscene); - KernelIntegrator *kintegrator = &dscene->data.integrator; - /* integrator parameters */ kintegrator->min_bounce = min_bounce + 1; kintegrator->max_bounce = max_bounce + 1; @@ -143,12 +158,10 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->transparent_min_bounce = transparent_min_bounce + 1; kintegrator->transparent_max_bounce = transparent_max_bounce + 1; - if (ao_bounces == 0) { - kintegrator->ao_bounces = INT_MAX; - } - else { - kintegrator->ao_bounces = ao_bounces - 1; - } + kintegrator->ao_bounces = ao_bounces; + kintegrator->ao_bounces_distance = ao_distance; + kintegrator->ao_bounces_factor = ao_factor; + kintegrator->ao_additive_factor = ao_additive_factor; /* Transparent Shadows * We only need to enable transparent shadows, if we actually have @@ -171,10 +184,7 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene kintegrator->caustics_refractive = caustics_refractive; kintegrator->filter_glossy = (filter_glossy == 0.0f) ? FLT_MAX : 1.0f / filter_glossy; - kintegrator->seed = hash_uint2(seed, 0); - - kintegrator->use_ambient_occlusion = ((Pass::contains(scene->passes, PASS_AO)) || - dscene->data.background.ao_factor != 0.0f); + kintegrator->seed = seed; kintegrator->sample_clamp_direct = (sample_clamp_direct == 0.0f) ? FLT_MAX : sample_clamp_direct * 3.0f; @@ -182,51 +192,8 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene FLT_MAX : sample_clamp_indirect * 3.0f; - kintegrator->branched = (method == BRANCHED_PATH) && device->info.has_branched_path; - kintegrator->volume_decoupled = device->info.has_volume_decoupled; - kintegrator->diffuse_samples = diffuse_samples; - kintegrator->glossy_samples = glossy_samples; - kintegrator->transmission_samples = transmission_samples; - kintegrator->ao_samples = ao_samples; - kintegrator->mesh_light_samples = mesh_light_samples; - kintegrator->subsurface_samples = subsurface_samples; - kintegrator->volume_samples = volume_samples; - kintegrator->start_sample = start_sample; - - if (kintegrator->branched) { - kintegrator->sample_all_lights_direct = sample_all_lights_direct; - kintegrator->sample_all_lights_indirect = sample_all_lights_indirect; - } - else { - kintegrator->sample_all_lights_direct = false; - kintegrator->sample_all_lights_indirect = false; - } - - kintegrator->sampling_pattern = sampling_pattern; - kintegrator->aa_samples = aa_samples; - if (aa_samples > 0 && adaptive_min_samples == 0) { - kintegrator->adaptive_min_samples = max(4, (int)sqrtf(aa_samples)); - VLOG(1) << "Cycles adaptive sampling: automatic min samples = " - << kintegrator->adaptive_min_samples; - } - else { - kintegrator->adaptive_min_samples = max(4, adaptive_min_samples); - } - - kintegrator->adaptive_step = 4; - kintegrator->adaptive_stop_per_sample = device->info.has_adaptive_stop_per_sample; - - /* Adaptive step must be a power of two for bitwise operations to work. */ - assert((kintegrator->adaptive_step & (kintegrator->adaptive_step - 1)) == 0); - - if (aa_samples > 0 && adaptive_threshold == 0.0f) { - kintegrator->adaptive_threshold = max(0.001f, 1.0f / (float)aa_samples); - VLOG(1) << "Cycles adaptive sampling: automatic threshold = " - << kintegrator->adaptive_threshold; - } - else { - kintegrator->adaptive_threshold = adaptive_threshold; - } + kintegrator->sampling_pattern = new_sampling_pattern; + kintegrator->scrambling_distance = scrambling_distance; if (light_sampling_threshold > 0.0f) { kintegrator->light_inv_rr_threshold = 1.0f / light_sampling_threshold; @@ -236,29 +203,15 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene } /* sobol directions table */ - int max_samples = 1; - - if (kintegrator->branched) { - foreach (Light *light, scene->lights) - max_samples = max(max_samples, light->get_samples()); - - max_samples = max(max_samples, - max(diffuse_samples, max(glossy_samples, transmission_samples))); - max_samples = max(max_samples, max(ao_samples, max(mesh_light_samples, subsurface_samples))); - max_samples = max(max_samples, volume_samples); - } - - uint total_bounces = max_bounce + transparent_max_bounce + 3 + VOLUME_BOUNDS_MAX + - max(BSSRDF_MAX_HITS, BSSRDF_MAX_BOUNCES); - - max_samples *= total_bounces; + int max_samples = max_bounce + transparent_max_bounce + 3 + VOLUME_BOUNDS_MAX + + max(BSSRDF_MAX_HITS, BSSRDF_MAX_BOUNCES); int dimensions = PRNG_BASE_NUM + max_samples * PRNG_BOUNCE_NUM; dimensions = min(dimensions, SOBOL_MAX_DIMENSIONS); if (need_update_lut) { - if (sampling_pattern == SAMPLING_PATTERN_SOBOL) { - uint *directions = dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions); + if (kintegrator->sampling_pattern == SAMPLING_PATTERN_SOBOL) { + uint *directions = (uint *)dscene->sample_pattern_lut.alloc(SOBOL_BITS * dimensions); sobol_generate_direction_vectors((uint(*)[SOBOL_BITS])directions, dimensions); @@ -276,10 +229,13 @@ void Integrator::device_update(Device *device, DeviceScene *dscene, Scene *scene function_bind(&progressive_multi_jitter_02_generate_2D, sequence, sequence_size, j)); } pool.wait_work(); + dscene->sample_pattern_lut.copy_to_device(); } } + kintegrator->has_shadow_catcher = scene->has_shadow_catcher(); + dscene->sample_pattern_lut.clear_modified(); clear_modified(); } @@ -295,17 +251,12 @@ void Integrator::tag_update(Scene *scene, uint32_t flag) tag_modified(); } - if (flag & (AO_PASS_MODIFIED | BACKGROUND_AO_MODIFIED)) { + if (flag & AO_PASS_MODIFIED) { /* tag only the ao_bounces socket as modified so we avoid updating sample_pattern_lut * unnecessarily */ tag_ao_bounces_modified(); } - if ((flag & LIGHT_SAMPLES_MODIFIED) && (method == BRANCHED_PATH)) { - /* the number of light samples may affect the size of the sample_pattern_lut */ - tag_sampling_pattern_modified(); - } - if (filter_glossy_is_modified()) { foreach (Shader *shader, scene->shaders) { if (shader->has_integrator_dependency) { @@ -321,4 +272,76 @@ void Integrator::tag_update(Scene *scene, uint32_t flag) } } +uint Integrator::get_kernel_features() const +{ + uint kernel_features = 0; + + if (ao_additive_factor != 0.0f) { + kernel_features |= KERNEL_FEATURE_AO_ADDITIVE; + } + + return kernel_features; +} + +AdaptiveSampling Integrator::get_adaptive_sampling() const +{ + AdaptiveSampling adaptive_sampling; + + adaptive_sampling.use = use_adaptive_sampling; + + if (!adaptive_sampling.use) { + return adaptive_sampling; + } + + if (aa_samples > 0 && adaptive_threshold == 0.0f) { + adaptive_sampling.threshold = max(0.001f, 1.0f / (float)aa_samples); + VLOG(1) << "Cycles adaptive sampling: automatic threshold = " << adaptive_sampling.threshold; + } + else { + adaptive_sampling.threshold = adaptive_threshold; + } + + if (adaptive_sampling.threshold > 0 && adaptive_min_samples == 0) { + /* Threshold 0.1 -> 32, 0.01 -> 64, 0.001 -> 128. + * This is highly scene dependent, we make a guess that seemed to work well + * in various test scenes. */ + const int min_samples = (int)ceilf(16.0f / powf(adaptive_sampling.threshold, 0.3f)); + adaptive_sampling.min_samples = max(4, min_samples); + VLOG(1) << "Cycles adaptive sampling: automatic min samples = " + << adaptive_sampling.min_samples; + } + else { + adaptive_sampling.min_samples = max(4, adaptive_min_samples); + } + + /* Arbitrary factor that makes the threshold more similar to what is was before, + * and gives arguably more intuitive values. */ + adaptive_sampling.threshold *= 5.0f; + + adaptive_sampling.adaptive_step = 16; + + DCHECK(is_power_of_two(adaptive_sampling.adaptive_step)) + << "Adaptive step must be a power of two for bitwise operations to work"; + + return adaptive_sampling; +} + +DenoiseParams Integrator::get_denoise_params() const +{ + DenoiseParams denoise_params; + + denoise_params.use = use_denoise; + + denoise_params.type = denoiser_type; + + denoise_params.start_sample = denoise_start_sample; + + denoise_params.use_pass_albedo = use_denoise_pass_albedo; + denoise_params.use_pass_normal = use_denoise_pass_normal; + + denoise_params.prefilter = denoiser_prefilter; + + return denoise_params; +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/integrator.h b/intern/cycles/scene/integrator.h similarity index 75% rename from intern/cycles/render/integrator.h rename to intern/cycles/scene/integrator.h index 4eeeda92d41..75764bcdedc 100644 --- a/intern/cycles/render/integrator.h +++ b/intern/cycles/scene/integrator.h @@ -17,9 +17,11 @@ #ifndef __INTEGRATOR_H__ #define __INTEGRATOR_H__ -#include "kernel/kernel_types.h" +#include "kernel/types.h" +#include "device/denoise.h" /* For the parameters and type enum. */ #include "graph/node.h" +#include "integrator/adaptive_sampling.h" CCL_NAMESPACE_BEGIN @@ -43,6 +45,9 @@ class Integrator : public Node { NODE_SOCKET_API(int, transparent_max_bounce) NODE_SOCKET_API(int, ao_bounces) + NODE_SOCKET_API(float, ao_factor) + NODE_SOCKET_API(float, ao_distance) + NODE_SOCKET_API(float, ao_additive_factor) NODE_SOCKET_API(int, volume_max_steps) NODE_SOCKET_API(float, volume_step_rate) @@ -62,37 +67,27 @@ class Integrator : public Node { static const int MAX_SAMPLES = (1 << 24); NODE_SOCKET_API(int, aa_samples) - NODE_SOCKET_API(int, diffuse_samples) - NODE_SOCKET_API(int, glossy_samples) - NODE_SOCKET_API(int, transmission_samples) - NODE_SOCKET_API(int, ao_samples) - NODE_SOCKET_API(int, mesh_light_samples) - NODE_SOCKET_API(int, subsurface_samples) - NODE_SOCKET_API(int, volume_samples) NODE_SOCKET_API(int, start_sample) - NODE_SOCKET_API(bool, sample_all_lights_direct) - NODE_SOCKET_API(bool, sample_all_lights_indirect) NODE_SOCKET_API(float, light_sampling_threshold) + NODE_SOCKET_API(bool, use_adaptive_sampling) NODE_SOCKET_API(int, adaptive_min_samples) NODE_SOCKET_API(float, adaptive_threshold) - enum Method { - BRANCHED_PATH = 0, - PATH = 1, - - NUM_METHODS, - }; - - NODE_SOCKET_API(Method, method) - NODE_SOCKET_API(SamplingPattern, sampling_pattern) + NODE_SOCKET_API(float, scrambling_distance) + + NODE_SOCKET_API(bool, use_denoise); + NODE_SOCKET_API(DenoiserType, denoiser_type); + NODE_SOCKET_API(int, denoise_start_sample); + NODE_SOCKET_API(bool, use_denoise_pass_albedo); + NODE_SOCKET_API(bool, use_denoise_pass_normal); + NODE_SOCKET_API(DenoiserPrefilter, denoiser_prefilter); enum : uint32_t { AO_PASS_MODIFIED = (1 << 0), - BACKGROUND_AO_MODIFIED = (1 << 1), - LIGHT_SAMPLES_MODIFIED = (1 << 2), + OBJECT_MANAGER = (1 << 1), /* tag everything in the manager for an update */ UPDATE_ALL = ~0u, @@ -107,6 +102,11 @@ class Integrator : public Node { void device_free(Device *device, DeviceScene *dscene, bool force_free = false); void tag_update(Scene *scene, uint32_t flag); + + uint get_kernel_features() const; + + AdaptiveSampling get_adaptive_sampling() const; + DenoiseParams get_denoise_params() const; }; CCL_NAMESPACE_END diff --git a/intern/cycles/render/jitter.cpp b/intern/cycles/scene/jitter.cpp similarity index 97% rename from intern/cycles/render/jitter.cpp rename to intern/cycles/scene/jitter.cpp index fc47b0e8f0a..e2162195ad8 100644 --- a/intern/cycles/render/jitter.cpp +++ b/intern/cycles/scene/jitter.cpp @@ -23,7 +23,7 @@ * "Efficient Generation of Points that Satisfy Two-Dimensional Elementary Intervals" */ -#include "render/jitter.h" +#include "scene/jitter.h" #include #include @@ -242,12 +242,6 @@ class PMJ02_Generator : public PMJ_Generator { static void shuffle(float2 points[], int size, int rng_seed) { - /* Offset samples by 1.0 for faster scrambling in kernel_random.h */ - for (int i = 0; i < size; ++i) { - points[i].x += 1.0f; - points[i].y += 1.0f; - } - if (rng_seed == 0) { return; } diff --git a/intern/cycles/render/jitter.h b/intern/cycles/scene/jitter.h similarity index 96% rename from intern/cycles/render/jitter.h rename to intern/cycles/scene/jitter.h index ed34c7a4f4d..756e4a1de78 100644 --- a/intern/cycles/render/jitter.h +++ b/intern/cycles/scene/jitter.h @@ -17,7 +17,7 @@ #ifndef __JITTER_H__ #define __JITTER_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/light.cpp b/intern/cycles/scene/light.cpp similarity index 91% rename from intern/cycles/render/light.cpp rename to intern/cycles/scene/light.cpp index 15aa4e047b5..83e531f42ef 100644 --- a/intern/cycles/render/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -14,25 +14,28 @@ * limitations under the License. */ -#include "render/light.h" #include "device/device.h" -#include "render/background.h" -#include "render/film.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_path.h" -#include "util/util_progress.h" -#include "util/util_task.h" +#include "scene/background.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" + +#include "integrator/shader_eval.h" + +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/path.h" +#include "util/progress.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN @@ -43,63 +46,51 @@ static void shade_background_pixels(Device *device, vector &pixels, Progress &progress) { - /* create input */ - device_vector d_input(device, "background_input", MEM_READ_ONLY); - device_vector d_output(device, "background_output", MEM_READ_WRITE); - - uint4 *d_input_data = d_input.alloc(width * height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - float u = (x + 0.5f) / width; - float v = (y + 0.5f) / height; - - uint4 in = make_uint4(__float_as_int(u), __float_as_int(v), 0, 0); - d_input_data[x + y * width] = in; - } - } - - /* compute on device */ - d_output.alloc(width * height); - d_output.zero_to_device(); - d_input.copy_to_device(); - + /* Needs to be up to data for attribute access. */ device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); - DeviceTask main_task(DeviceTask::SHADER); - main_task.shader_input = d_input.device_pointer; - main_task.shader_output = d_output.device_pointer; - main_task.shader_eval_type = SHADER_EVAL_BACKGROUND; - main_task.shader_x = 0; - main_task.shader_w = width * height; - main_task.num_samples = 1; - main_task.get_cancel = function_bind(&Progress::get_cancel, &progress); + const int size = width * height; + const int num_channels = 3; + pixels.resize(size); - /* disabled splitting for now, there's an issue with multi-GPU mem_copy_from */ - list split_tasks; - main_task.split(split_tasks, 1, 128 * 128); + /* Evaluate shader on device. */ + ShaderEval shader_eval(device, progress); + shader_eval.eval( + SHADER_EVAL_BACKGROUND, + size, + num_channels, + [&](device_vector &d_input) { + /* Fill coordinates for shading. */ + KernelShaderEvalInput *d_input_data = d_input.data(); - foreach (DeviceTask &task, split_tasks) { - device->task_add(task); - device->task_wait(); - d_output.copy_from_device(task.shader_x, 1, task.shader_w); - } + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + float u = (x + 0.5f) / width; + float v = (y + 0.5f) / height; - d_input.free(); + KernelShaderEvalInput in; + in.object = OBJECT_NONE; + in.prim = PRIM_NONE; + in.u = u; + in.v = v; + d_input_data[x + y * width] = in; + } + } - float4 *d_output_data = d_output.data(); + return size; + }, + [&](device_vector &d_output) { + /* Copy output to pixel buffer. */ + float *d_output_data = d_output.data(); - pixels.resize(width * height); - - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - pixels[y * width + x].x = d_output_data[y * width + x].x; - pixels[y * width + x].y = d_output_data[y * width + x].y; - pixels[y * width + x].z = d_output_data[y * width + x].z; - } - } - - d_output.free(); + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + pixels[y * width + x].x = d_output_data[(y * width + x) * num_channels + 0]; + pixels[y * width + x].y = d_output_data[(y * width + x) * num_channels + 1]; + pixels[y * width + x].z = d_output_data[(y * width + x) * num_channels + 2]; + } + } + }); } /* Light */ @@ -140,15 +131,16 @@ NODE_DEFINE(Light) SOCKET_BOOLEAN(cast_shadow, "Cast Shadow", true); SOCKET_BOOLEAN(use_mis, "Use Mis", false); + SOCKET_BOOLEAN(use_camera, "Use Camera", true); SOCKET_BOOLEAN(use_diffuse, "Use Diffuse", true); SOCKET_BOOLEAN(use_glossy, "Use Glossy", true); SOCKET_BOOLEAN(use_transmission, "Use Transmission", true); SOCKET_BOOLEAN(use_scatter, "Use Scatter", true); - SOCKET_INT(samples, "Samples", 1); SOCKET_INT(max_bounces, "Max Bounces", 1024); SOCKET_UINT(random_id, "Random ID", 0); + SOCKET_BOOLEAN(is_shadow_catcher, "Shadow Catcher", true); SOCKET_BOOLEAN(is_portal, "Is Portal", false); SOCKET_BOOLEAN(is_enabled, "Is Enabled", true); @@ -166,10 +158,6 @@ void Light::tag_update(Scene *scene) { if (is_modified()) { scene->light_manager->tag_update(scene, LightManager::LIGHT_MODIFIED); - - if (samples_is_modified()) { - scene->integrator->tag_update(scene, Integrator::LIGHT_SAMPLES_MODIFIED); - } } } @@ -193,7 +181,6 @@ LightManager::LightManager() { update_flags = UPDATE_ALL; need_update_background = true; - use_light_visibility = false; last_background_enabled = false; last_background_resolution = 0; } @@ -357,21 +344,23 @@ void LightManager::device_update_distribution(Device *, int object_id = j; int shader_flag = 0; + if (!(object->get_visibility() & PATH_RAY_CAMERA)) { + shader_flag |= SHADER_EXCLUDE_CAMERA; + } if (!(object->get_visibility() & PATH_RAY_DIFFUSE)) { shader_flag |= SHADER_EXCLUDE_DIFFUSE; - use_light_visibility = true; } if (!(object->get_visibility() & PATH_RAY_GLOSSY)) { shader_flag |= SHADER_EXCLUDE_GLOSSY; - use_light_visibility = true; } if (!(object->get_visibility() & PATH_RAY_TRANSMIT)) { shader_flag |= SHADER_EXCLUDE_TRANSMIT; - use_light_visibility = true; } if (!(object->get_visibility() & PATH_RAY_VOLUME_SCATTER)) { shader_flag |= SHADER_EXCLUDE_SCATTER; - use_light_visibility = true; + } + if (!(object->get_is_shadow_catcher())) { + shader_flag |= SHADER_EXCLUDE_SHADOW_CATCHER; } size_t mesh_num_triangles = mesh->num_triangles(); @@ -496,10 +485,10 @@ void LightManager::device_update_distribution(Device *, kfilm->pass_shadow_scale = 1.0f; if (kintegrator->pdf_triangles != 0.0f) - kfilm->pass_shadow_scale *= 0.5f; + kfilm->pass_shadow_scale /= 0.5f; if (num_background_lights < num_lights) - kfilm->pass_shadow_scale *= (float)(num_lights - num_background_lights) / (float)num_lights; + kfilm->pass_shadow_scale /= (float)(num_lights - num_background_lights) / (float)num_lights; /* CDF */ dscene->light_distribution.copy_to_device(); @@ -766,25 +755,26 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (!light->cast_shadow) shader_id &= ~SHADER_CAST_SHADOW; + if (!light->use_camera) { + shader_id |= SHADER_EXCLUDE_CAMERA; + } if (!light->use_diffuse) { shader_id |= SHADER_EXCLUDE_DIFFUSE; - use_light_visibility = true; } if (!light->use_glossy) { shader_id |= SHADER_EXCLUDE_GLOSSY; - use_light_visibility = true; } if (!light->use_transmission) { shader_id |= SHADER_EXCLUDE_TRANSMIT; - use_light_visibility = true; } if (!light->use_scatter) { shader_id |= SHADER_EXCLUDE_SCATTER; - use_light_visibility = true; + } + if (!light->is_shadow_catcher) { + shader_id |= SHADER_EXCLUDE_SHADOW_CATCHER; } klights[light_index].type = light->light_type; - klights[light_index].samples = light->samples; klights[light_index].strength[0] = light->strength.x; klights[light_index].strength[1] = light->strength.y; klights[light_index].strength[2] = light->strength.z; @@ -836,19 +826,15 @@ void LightManager::device_update_points(Device *, DeviceScene *dscene, Scene *sc if (!(visibility & PATH_RAY_DIFFUSE)) { shader_id |= SHADER_EXCLUDE_DIFFUSE; - use_light_visibility = true; } if (!(visibility & PATH_RAY_GLOSSY)) { shader_id |= SHADER_EXCLUDE_GLOSSY; - use_light_visibility = true; } if (!(visibility & PATH_RAY_TRANSMIT)) { shader_id |= SHADER_EXCLUDE_TRANSMIT; - use_light_visibility = true; } if (!(visibility & PATH_RAY_VOLUME_SCATTER)) { shader_id |= SHADER_EXCLUDE_SCATTER; - use_light_visibility = true; } } else if (light->light_type == LIGHT_AREA) { @@ -998,8 +984,6 @@ void LightManager::device_update(Device *device, device_free(device, dscene, need_update_background); - use_light_visibility = false; - device_update_points(device, dscene, scene); if (progress.get_cancel()) return; @@ -1018,8 +1002,6 @@ void LightManager::device_update(Device *device, if (progress.get_cancel()) return; - scene->film->set_use_light_visibility(use_light_visibility); - update_flags = UPDATE_NONE; need_update_background = false; } diff --git a/intern/cycles/render/light.h b/intern/cycles/scene/light.h similarity index 94% rename from intern/cycles/render/light.h rename to intern/cycles/scene/light.h index fbd709125ff..97ec9792860 100644 --- a/intern/cycles/render/light.h +++ b/intern/cycles/scene/light.h @@ -17,18 +17,18 @@ #ifndef __LIGHT_H__ #define __LIGHT_H__ -#include "kernel/kernel_types.h" +#include "kernel/types.h" #include "graph/node.h" /* included as Light::set_shader defined through NODE_SOCKET_API does not select * the right Node::set overload as it does not know that Shader is a Node */ -#include "render/shader.h" +#include "scene/shader.h" -#include "util/util_ies.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/ies.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -69,16 +69,17 @@ class Light : public Node { NODE_SOCKET_API(bool, cast_shadow) NODE_SOCKET_API(bool, use_mis) + NODE_SOCKET_API(bool, use_camera) NODE_SOCKET_API(bool, use_diffuse) NODE_SOCKET_API(bool, use_glossy) NODE_SOCKET_API(bool, use_transmission) NODE_SOCKET_API(bool, use_scatter) + NODE_SOCKET_API(bool, is_shadow_catcher) NODE_SOCKET_API(bool, is_portal) NODE_SOCKET_API(bool, is_enabled) NODE_SOCKET_API(Shader *, shader) - NODE_SOCKET_API(int, samples) NODE_SOCKET_API(int, max_bounces) NODE_SOCKET_API(uint, random_id) @@ -108,8 +109,6 @@ class LightManager { UPDATE_NONE = 0u, }; - bool use_light_visibility; - /* Need to update background (including multiple importance map) */ bool need_update_background; diff --git a/intern/cycles/render/mesh.cpp b/intern/cycles/scene/mesh.cpp similarity index 88% rename from intern/cycles/render/mesh.cpp rename to intern/cycles/scene/mesh.cpp index fd9879dd5dd..f47dab30869 100644 --- a/intern/cycles/render/mesh.cpp +++ b/intern/cycles/scene/mesh.cpp @@ -14,24 +14,24 @@ * limitations under the License. */ +#include "bvh/build.h" #include "bvh/bvh.h" -#include "bvh/bvh_build.h" #include "device/device.h" -#include "render/graph.h" -#include "render/hair.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" +#include "scene/hair.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader_graph.h" -#include "subd/subd_patch_table.h" -#include "subd/subd_split.h" +#include "subd/patch_table.h" +#include "subd/split.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_set.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/set.h" CCL_NAMESPACE_BEGIN @@ -508,6 +508,8 @@ void Mesh::apply_transform(const Transform &tfm, const bool apply_to_motion) for (size_t i = 0; i < verts.size(); i++) verts[i] = transform_point(&tfm, verts[i]); + tag_verts_modified(); + if (apply_to_motion) { Attribute *attr = attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); @@ -729,12 +731,7 @@ void Mesh::pack_normals(float4 *vnormal) } } -void Mesh::pack_verts(const vector &tri_prim_index, - uint4 *tri_vindex, - uint *tri_patch, - float2 *tri_patch_uv, - size_t vert_offset, - size_t tri_offset) +void Mesh::pack_verts(float4 *tri_verts, uint4 *tri_vindex, uint *tri_patch, float2 *tri_patch_uv) { size_t verts_size = verts.size(); @@ -749,17 +746,19 @@ void Mesh::pack_verts(const vector &tri_prim_index, size_t triangles_size = num_triangles(); for (size_t i = 0; i < triangles_size; i++) { - Triangle t = get_triangle(i); - tri_vindex[i] = make_uint4(t.v[0] + vert_offset, - t.v[1] + vert_offset, - t.v[2] + vert_offset, - tri_prim_index[i + tri_offset]); + const Triangle t = get_triangle(i); + tri_vindex[i] = make_uint4( + t.v[0] + vert_offset, t.v[1] + vert_offset, t.v[2] + vert_offset, 3 * (prim_offset + i)); tri_patch[i] = (!get_num_subd_faces()) ? -1 : (triangle_patch[i] * 8 + patch_offset); + + tri_verts[i * 3] = float3_to_float4(verts[t.v[0]]); + tri_verts[i * 3 + 1] = float3_to_float4(verts[t.v[1]]); + tri_verts[i * 3 + 2] = float3_to_float4(verts[t.v[2]]); } } -void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset) +void Mesh::pack_patches(uint *patch_data) { size_t num_faces = get_num_subd_faces(); int ngons = 0; @@ -805,53 +804,9 @@ void Mesh::pack_patches(uint *patch_data, uint vert_offset, uint face_offset, ui } } -void Mesh::pack_primitives(ccl::PackedBVH *pack, int object, uint visibility, PackFlags pack_flags) +PrimitiveType Mesh::primitive_type() const { - if (triangles.empty()) - return; - - const size_t num_prims = num_triangles(); - - /* Use prim_offset for indexing as it is computed per geometry type, and prim_tri_verts does not - * contain data for Hair geometries. */ - float4 *prim_tri_verts = &pack->prim_tri_verts[prim_offset * 3]; - // 'pack->prim_time' is unused by Embree and OptiX - - uint type = has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE; - - /* Separate loop as other arrays are not initialized if their packing is not required. */ - if ((pack_flags & PackFlags::PACK_VISIBILITY) != 0) { - unsigned int *prim_visibility = &pack->prim_visibility[optix_prim_offset]; - for (size_t k = 0; k < num_prims; ++k) { - prim_visibility[k] = visibility; - } - } - - if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) { - /* Use optix_prim_offset for indexing as those arrays also contain data for Hair geometries. */ - unsigned int *prim_tri_index = &pack->prim_tri_index[optix_prim_offset]; - int *prim_type = &pack->prim_type[optix_prim_offset]; - int *prim_index = &pack->prim_index[optix_prim_offset]; - int *prim_object = &pack->prim_object[optix_prim_offset]; - - for (size_t k = 0; k < num_prims; ++k) { - if ((pack_flags & PackFlags::PACK_GEOMETRY) != 0) { - prim_tri_index[k] = (prim_offset + k) * 3; - prim_type[k] = type; - prim_index[k] = prim_offset + k; - prim_object[k] = object; - } - } - } - - if ((pack_flags & PackFlags::PACK_VERTICES) != 0) { - for (size_t k = 0; k < num_prims; ++k) { - const Mesh::Triangle t = get_triangle(k); - prim_tri_verts[k * 3] = float3_to_float4(verts[t.v[0]]); - prim_tri_verts[k * 3 + 1] = float3_to_float4(verts[t.v[1]]); - prim_tri_verts[k * 3 + 2] = float3_to_float4(verts[t.v[2]]); - } - } + return has_motion_blur() ? PRIMITIVE_MOTION_TRIANGLE : PRIMITIVE_TRIANGLE; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/mesh.h b/intern/cycles/scene/mesh.h similarity index 87% rename from intern/cycles/render/mesh.h rename to intern/cycles/scene/mesh.h index e9e79f7f20d..d13b3003164 100644 --- a/intern/cycles/render/mesh.h +++ b/intern/cycles/scene/mesh.h @@ -19,19 +19,19 @@ #include "graph/node.h" -#include "bvh/bvh_params.h" -#include "render/attribute.h" -#include "render/geometry.h" -#include "render/shader.h" +#include "bvh/params.h" +#include "scene/attribute.h" +#include "scene/geometry.h" +#include "scene/shader.h" -#include "util/util_array.h" -#include "util/util_boundbox.h" -#include "util/util_list.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_set.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/boundbox.h" +#include "util/list.h" +#include "util/map.h" +#include "util/param.h" +#include "util/set.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -224,18 +224,10 @@ class Mesh : public Geometry { void pack_shaders(Scene *scene, uint *shader); void pack_normals(float4 *vnormal); - void pack_verts(const vector &tri_prim_index, - uint4 *tri_vindex, - uint *tri_patch, - float2 *tri_patch_uv, - size_t vert_offset, - size_t tri_offset); - void pack_patches(uint *patch_data, uint vert_offset, uint face_offset, uint corner_offset); + void pack_verts(float4 *tri_verts, uint4 *tri_vindex, uint *tri_patch, float2 *tri_patch_uv); + void pack_patches(uint *patch_data); - void pack_primitives(PackedBVH *pack, - int object, - uint visibility, - PackFlags pack_flags) override; + PrimitiveType primitive_type() const override; void tessellate(DiagSplit *split); diff --git a/intern/cycles/render/mesh_displace.cpp b/intern/cycles/scene/mesh_displace.cpp similarity index 74% rename from intern/cycles/render/mesh_displace.cpp rename to intern/cycles/scene/mesh_displace.cpp index b39d81023d9..e69c2d1c3be 100644 --- a/intern/cycles/render/mesh_displace.cpp +++ b/intern/cycles/scene/mesh_displace.cpp @@ -16,15 +16,17 @@ #include "device/device.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/scene.h" -#include "render/shader.h" +#include "integrator/shader_eval.h" -#include "util/util_foreach.h" -#include "util/util_map.h" -#include "util/util_progress.h" -#include "util/util_set.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader.h" + +#include "util/foreach.h" +#include "util/map.h" +#include "util/progress.h" +#include "util/set.h" CCL_NAMESPACE_BEGIN @@ -43,40 +45,28 @@ static float3 compute_face_normal(const Mesh::Triangle &t, float3 *verts) return norm / normlen; } -bool GeometryManager::displace( - Device *device, DeviceScene *dscene, Scene *scene, Mesh *mesh, Progress &progress) +/* Fill in coordinates for mesh displacement shader evaluation on device. */ +static int fill_shader_input(const Scene *scene, + const Mesh *mesh, + const int object_index, + device_vector &d_input) { - /* verify if we have a displacement shader */ - if (!mesh->has_true_displacement()) { - return false; - } + int d_input_size = 0; + KernelShaderEvalInput *d_input_data = d_input.data(); - string msg = string_printf("Computing Displacement %s", mesh->name.c_str()); - progress.set_status("Updating Mesh", msg); + const array &mesh_shaders = mesh->get_shader(); + const array &mesh_used_shaders = mesh->get_used_shaders(); + const array &mesh_verts = mesh->get_verts(); - /* find object index. todo: is arbitrary */ - size_t object_index = OBJECT_NONE; - - for (size_t i = 0; i < scene->objects.size(); i++) { - if (scene->objects[i]->get_geometry() == mesh) { - object_index = i; - break; - } - } - - /* setup input for device task */ - const size_t num_verts = mesh->verts.size(); + const int num_verts = mesh_verts.size(); vector done(num_verts, false); - device_vector d_input(device, "displace_input", MEM_READ_ONLY); - uint4 *d_input_data = d_input.alloc(num_verts); - size_t d_input_size = 0; - size_t num_triangles = mesh->num_triangles(); - for (size_t i = 0; i < num_triangles; i++) { + int num_triangles = mesh->num_triangles(); + for (int i = 0; i < num_triangles; i++) { Mesh::Triangle t = mesh->get_triangle(i); - int shader_index = mesh->shader[i]; - Shader *shader = (shader_index < mesh->used_shaders.size()) ? - static_cast(mesh->used_shaders[shader_index]) : + int shader_index = mesh_shaders[i]; + Shader *shader = (shader_index < mesh_used_shaders.size()) ? + static_cast(mesh_used_shaders[shader_index]) : scene->default_surface; if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) { @@ -110,57 +100,41 @@ bool GeometryManager::displace( } /* back */ - uint4 in = make_uint4(object, prim, __float_as_int(u), __float_as_int(v)); + KernelShaderEvalInput in; + in.object = object; + in.prim = prim; + in.u = u; + in.v = v; d_input_data[d_input_size++] = in; } } - if (d_input_size == 0) - return false; + return d_input_size; +} - /* run device task */ - device_vector d_output(device, "displace_output", MEM_READ_WRITE); - d_output.alloc(d_input_size); - d_output.zero_to_device(); - d_input.copy_to_device(); +/* Read back mesh displacement shader output. */ +static void read_shader_output(const Scene *scene, + Mesh *mesh, + const device_vector &d_output) +{ + const array &mesh_shaders = mesh->get_shader(); + const array &mesh_used_shaders = mesh->get_used_shaders(); + array &mesh_verts = mesh->get_verts(); - /* needs to be up to data for attribute access */ - device->const_copy_to("__data", &dscene->data, sizeof(dscene->data)); + const int num_verts = mesh_verts.size(); + const int num_motion_steps = mesh->get_motion_steps(); + vector done(num_verts, false); - DeviceTask task(DeviceTask::SHADER); - task.shader_input = d_input.device_pointer; - task.shader_output = d_output.device_pointer; - task.shader_eval_type = SHADER_EVAL_DISPLACE; - task.shader_x = 0; - task.shader_w = d_output.size(); - task.num_samples = 1; - task.get_cancel = function_bind(&Progress::get_cancel, &progress); - - device->task_add(task); - device->task_wait(); - - if (progress.get_cancel()) { - d_input.free(); - d_output.free(); - return false; - } - - d_output.copy_from_device(0, 1, d_output.size()); - d_input.free(); - - /* read result */ - done.clear(); - done.resize(num_verts, false); - int k = 0; - - float4 *offset = d_output.data(); + const float *d_output_data = d_output.data(); + int d_output_index = 0; Attribute *attr_mP = mesh->attributes.find(ATTR_STD_MOTION_VERTEX_POSITION); - for (size_t i = 0; i < num_triangles; i++) { + int num_triangles = mesh->num_triangles(); + for (int i = 0; i < num_triangles; i++) { Mesh::Triangle t = mesh->get_triangle(i); - int shader_index = mesh->shader[i]; - Shader *shader = (shader_index < mesh->used_shaders.size()) ? - static_cast(mesh->used_shaders[shader_index]) : + int shader_index = mesh_shaders[i]; + Shader *shader = (shader_index < mesh_used_shaders.size()) ? + static_cast(mesh_used_shaders[shader_index]) : scene->default_surface; if (!shader->has_displacement || shader->get_displacement_method() == DISPLACE_BUMP) { @@ -170,12 +144,16 @@ bool GeometryManager::displace( for (int j = 0; j < 3; j++) { if (!done[t.v[j]]) { done[t.v[j]] = true; - float3 off = float4_to_float3(offset[k++]); + float3 off = make_float3(d_output_data[d_output_index + 0], + d_output_data[d_output_index + 1], + d_output_data[d_output_index + 2]); + d_output_index += 3; + /* Avoid illegal vertex coordinates. */ off = ensure_finite3(off); - mesh->verts[t.v[j]] += off; + mesh_verts[t.v[j]] += off; if (attr_mP != NULL) { - for (int step = 0; step < mesh->motion_steps - 1; step++) { + for (int step = 0; step < num_motion_steps - 1; step++) { float3 *mP = attr_mP->data_float3() + step * num_verts; mP[t.v[j]] += off; } @@ -183,8 +161,44 @@ bool GeometryManager::displace( } } } +} - d_output.free(); +bool GeometryManager::displace(Device *device, Scene *scene, Mesh *mesh, Progress &progress) +{ + /* verify if we have a displacement shader */ + if (!mesh->has_true_displacement()) { + return false; + } + + const size_t num_verts = mesh->verts.size(); + const size_t num_triangles = mesh->num_triangles(); + + if (num_triangles == 0) { + return false; + } + + string msg = string_printf("Computing Displacement %s", mesh->name.c_str()); + progress.set_status("Updating Mesh", msg); + + /* find object index. todo: is arbitrary */ + size_t object_index = OBJECT_NONE; + + for (size_t i = 0; i < scene->objects.size(); i++) { + if (scene->objects[i]->get_geometry() == mesh) { + object_index = i; + break; + } + } + + /* Evaluate shader on device. */ + ShaderEval shader_eval(device, progress); + if (!shader_eval.eval(SHADER_EVAL_DISPLACE, + num_verts, + 3, + function_bind(&fill_shader_input, scene, mesh, object_index, _1), + function_bind(&read_shader_output, scene, mesh, _1))) { + return false; + } /* stitch */ unordered_set stitch_keys; @@ -297,8 +311,7 @@ bool GeometryManager::displace( } /* normalize vertex normals */ - done.clear(); - done.resize(num_verts, false); + vector done(num_verts, false); for (size_t i = 0; i < num_triangles; i++) { if (tri_has_true_disp[i]) { @@ -368,8 +381,7 @@ bool GeometryManager::displace( } /* normalize vertex normals */ - done.clear(); - done.resize(num_verts, false); + vector done(num_verts, false); for (size_t i = 0; i < num_triangles; i++) { if (tri_has_true_disp[i]) { diff --git a/intern/cycles/render/mesh_subdivision.cpp b/intern/cycles/scene/mesh_subdivision.cpp similarity index 98% rename from intern/cycles/render/mesh_subdivision.cpp rename to intern/cycles/scene/mesh_subdivision.cpp index 575dbef8ec2..a0c0bc68f8b 100644 --- a/intern/cycles/render/mesh_subdivision.cpp +++ b/intern/cycles/scene/mesh_subdivision.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#include "render/attribute.h" -#include "render/camera.h" -#include "render/mesh.h" +#include "scene/attribute.h" +#include "scene/camera.h" +#include "scene/mesh.h" -#include "subd/subd_patch.h" -#include "subd/subd_patch_table.h" -#include "subd/subd_split.h" +#include "subd/patch.h" +#include "subd/patch_table.h" +#include "subd/split.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/hash.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/object.cpp b/intern/cycles/scene/object.cpp similarity index 95% rename from intern/cycles/render/object.cpp rename to intern/cycles/scene/object.cpp index c88d94fe4c2..69a2365f17c 100644 --- a/intern/cycles/render/object.cpp +++ b/intern/cycles/scene/object.cpp @@ -14,29 +14,29 @@ * limitations under the License. */ -#include "render/object.h" +#include "scene/object.h" #include "device/device.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/hair.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/particles.h" -#include "render/scene.h" -#include "render/stats.h" -#include "render/volume.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/hair.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/particles.h" +#include "scene/scene.h" +#include "scene/stats.h" +#include "scene/volume.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_murmurhash.h" -#include "util/util_progress.h" -#include "util/util_set.h" -#include "util/util_task.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/map.h" +#include "util/murmurhash.h" +#include "util/progress.h" +#include "util/set.h" +#include "util/task.h" +#include "util/vector.h" -#include "subd/subd_patch_table.h" +#include "subd/patch_table.h" CCL_NAMESPACE_BEGIN @@ -60,6 +60,7 @@ struct UpdateObjectTransformState { /* Packed object arrays. Those will be filled in. */ uint *object_flag; + uint *object_visibility; KernelObject *objects; Transform *object_motion_pass; DecomposedTransform *object_motion; @@ -113,6 +114,7 @@ Object::Object() : Node(get_node_type()) particle_index = 0; attr_map_offset = 0; bounds = BoundBox::empty; + intersects_volume = false; } Object::~Object() @@ -216,6 +218,10 @@ void Object::tag_update(Scene *scene) if (use_holdout_is_modified()) { flag |= ObjectManager::HOLDOUT_MODIFIED; } + + if (is_shadow_catcher_is_modified()) { + scene->tag_shadow_catcher_modified(); + } } if (geometry) { @@ -273,14 +279,7 @@ bool Object::is_traceable() const uint Object::visibility_for_tracing() const { - uint trace_visibility = visibility; - if (is_shadow_catcher) { - trace_visibility &= ~PATH_RAY_SHADOW_NON_CATCHER; - } - else { - trace_visibility &= ~PATH_RAY_SHADOW_CATCHER; - } - return trace_visibility; + return SHADOW_CATCHER_OBJECT_VISIBILITY(is_shadow_catcher, visibility & PATH_RAY_ALL_VISIBILITY); } float Object::compute_volume_step_size() const @@ -515,6 +514,9 @@ void ObjectManager::device_update_object_transform(UpdateObjectTransformState *s (1.0f - 0.5f * ob->shadow_terminator_shading_offset); kobject.shadow_terminator_geometry_offset = ob->shadow_terminator_geometry_offset; + kobject.visibility = ob->visibility_for_tracing(); + kobject.primitive_type = geom->primitive_type(); + /* Object flag. */ if (ob->use_holdout) { flag |= SD_OBJECT_HOLDOUT_MASK; @@ -680,7 +682,7 @@ void ObjectManager::device_update(Device *device, /* prepare for static BVH building */ /* todo: do before to support getting object level coords? */ - if (scene->params.bvh_type == SceneParams::BVH_STATIC) { + if (scene->params.bvh_type == BVH_TYPE_STATIC) { scoped_callback_timer timer([scene](double time) { if (scene->update_stats) { scene->update_stats->object.times.add_entry( @@ -758,12 +760,14 @@ void ObjectManager::device_update_flags( } if (bounds_valid) { + object->intersects_volume = false; foreach (Object *volume_object, volume_objects) { if (object == volume_object) { continue; } if (object->bounds.intersects(volume_object->bounds)) { object_flag[object->index] |= SD_OBJECT_INTERSECTS_VOLUME; + object->intersects_volume = true; break; } } @@ -932,6 +936,11 @@ void ObjectManager::tag_update(Scene *scene, uint32_t flag) } scene->light_manager->tag_update(scene, LightManager::OBJECT_MANAGER); + + /* Integrator's shadow catcher settings depends on object visibility settings. */ + if (flag & (OBJECT_ADDED | OBJECT_REMOVED | OBJECT_MODIFIED)) { + scene->integrator->tag_update(scene, Integrator::OBJECT_MANAGER); + } } bool ObjectManager::need_update() const diff --git a/intern/cycles/render/object.h b/intern/cycles/scene/object.h similarity index 94% rename from intern/cycles/render/object.h rename to intern/cycles/scene/object.h index c52ddce48da..f6dc57ee8b9 100644 --- a/intern/cycles/render/object.h +++ b/intern/cycles/scene/object.h @@ -22,16 +22,16 @@ /* included as Object::set_particle_system defined through NODE_SOCKET_API does * not select the right Node::set overload as it does not know that ParticleSystem * is a Node */ -#include "render/particles.h" -#include "render/scene.h" +#include "scene/particles.h" +#include "scene/scene.h" -#include "util/util_array.h" -#include "util/util_boundbox.h" -#include "util/util_param.h" -#include "util/util_thread.h" -#include "util/util_transform.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/boundbox.h" +#include "util/param.h" +#include "util/thread.h" +#include "util/transform.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -75,6 +75,9 @@ class Object : public Node { NODE_SOCKET_API(float, ao_distance) + /* Set during device update. */ + bool intersects_volume; + Object(); ~Object(); diff --git a/intern/cycles/render/osl.cpp b/intern/cycles/scene/osl.cpp similarity index 93% rename from intern/cycles/render/osl.cpp rename to intern/cycles/scene/osl.cpp index 7dc79f48145..c5f38d4f270 100644 --- a/intern/cycles/render/osl.cpp +++ b/intern/cycles/scene/osl.cpp @@ -16,29 +16,29 @@ #include "device/device.h" -#include "render/background.h" -#include "render/colorspace.h" -#include "render/graph.h" -#include "render/light.h" -#include "render/nodes.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" +#include "scene/background.h" +#include "scene/colorspace.h" +#include "scene/light.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" #ifdef WITH_OSL -# include "kernel/osl/osl_globals.h" -# include "kernel/osl/osl_services.h" -# include "kernel/osl/osl_shader.h" +# include "kernel/osl/globals.h" +# include "kernel/osl/services.h" +# include "kernel/osl/shader.h" -# include "util/util_aligned_malloc.h" -# include "util/util_foreach.h" -# include "util/util_logging.h" -# include "util/util_md5.h" -# include "util/util_path.h" -# include "util/util_progress.h" -# include "util/util_projection.h" +# include "util/aligned_malloc.h" +# include "util/foreach.h" +# include "util/log.h" +# include "util/md5.h" +# include "util/path.h" +# include "util/progress.h" +# include "util/projection.h" #endif @@ -113,7 +113,7 @@ void OSLShaderManager::device_update_specific(Device *device, scene->image_manager->set_osl_texture_system((void *)ts); /* create shaders */ - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); Shader *background_shader = scene->background->get_shader(scene); foreach (Shader *shader, scene->shaders) { @@ -174,7 +174,7 @@ void OSLShaderManager::device_update_specific(Device *device, void OSLShaderManager::device_free(Device *device, DeviceScene *dscene, Scene *scene) { - OSLGlobals *og = (OSLGlobals *)device->osl_memory(); + OSLGlobals *og = (OSLGlobals *)device->get_cpu_osl_memory(); device_free_common(device, dscene, scene); @@ -257,25 +257,36 @@ void OSLShaderManager::shading_system_init() /* our own ray types */ static const char *raytypes[] = { - "camera", /* PATH_RAY_CAMERA */ - "reflection", /* PATH_RAY_REFLECT */ - "refraction", /* PATH_RAY_TRANSMIT */ - "diffuse", /* PATH_RAY_DIFFUSE */ - "glossy", /* PATH_RAY_GLOSSY */ - "singular", /* PATH_RAY_SINGULAR */ - "transparent", /* PATH_RAY_TRANSPARENT */ + "camera", /* PATH_RAY_CAMERA */ + "reflection", /* PATH_RAY_REFLECT */ + "refraction", /* PATH_RAY_TRANSMIT */ + "diffuse", /* PATH_RAY_DIFFUSE */ + "glossy", /* PATH_RAY_GLOSSY */ + "singular", /* PATH_RAY_SINGULAR */ + "transparent", /* PATH_RAY_TRANSPARENT */ + "volume_scatter", /* PATH_RAY_VOLUME_SCATTER */ - "shadow", /* PATH_RAY_SHADOW_OPAQUE_NON_CATCHER */ - "shadow", /* PATH_RAY_SHADOW_OPAQUE_CATCHER */ - "shadow", /* PATH_RAY_SHADOW_TRANSPARENT_NON_CATCHER */ - "shadow", /* PATH_RAY_SHADOW_TRANSPARENT_CATCHER */ + "shadow", /* PATH_RAY_SHADOW_OPAQUE */ + "shadow", /* PATH_RAY_SHADOW_TRANSPARENT */ - "__unused__", "volume_scatter", /* PATH_RAY_VOLUME_SCATTER */ - "__unused__", + "__unused__", /* PATH_RAY_NODE_UNALIGNED */ + "__unused__", /* PATH_RAY_MIS_SKIP */ - "__unused__", "diffuse_ancestor", /* PATH_RAY_DIFFUSE_ANCESTOR */ - "__unused__", "__unused__", "__unused__", "__unused__", - "__unused__", "__unused__", "__unused__", + "diffuse_ancestor", /* PATH_RAY_DIFFUSE_ANCESTOR */ + + "__unused__", /* PATH_RAY_SINGLE_PASS_DONE */ + "__unused__", /* PATH_RAY_TRANSPARENT_BACKGROUND */ + "__unused__", /* PATH_RAY_TERMINATE_IMMEDIATE */ + "__unused__", /* PATH_RAY_TERMINATE_AFTER_TRANSPARENT */ + "__unused__", /* PATH_RAY_EMISSION */ + "__unused__", /* PATH_RAY_SUBSURFACE */ + "__unused__", /* PATH_RAY_DENOISING_FEATURES */ + "__unused__", /* PATH_RAY_REFLECT_PASS */ + "__unused__", /* PATH_RAY_TRANSMISSION_PASS */ + "__unused__", /* PATH_RAY_VOLUME_PASS */ + "__unused__", /* PATH_RAY_SHADOW_FOR_LIGHT */ + "__unused__", /* PATH_RAY_SHADOW_CATCHER_HIT */ + "__unused__", /* PATH_RAY_SHADOW_CATCHER_PASS */ }; const int nraytypes = sizeof(raytypes) / sizeof(raytypes[0]); @@ -315,17 +326,22 @@ bool OSLShaderManager::osl_compile(const string &inputfile, const string &output string stdosl_path; string shader_path = path_get("shader"); - /* specify output file name */ + /* Specify output file name. */ options.push_back("-o"); options.push_back(outputfile); - /* specify standard include path */ + /* Specify standard include path. */ string include_path_arg = string("-I") + shader_path; options.push_back(include_path_arg); stdosl_path = path_join(shader_path, "stdcycles.h"); - /* compile */ + /* Compile. + * + * Mutex protected because the OSL compiler does not appear to be thread safe, see T92503. */ + static thread_mutex osl_compiler_mutex; + thread_scoped_lock lock(osl_compiler_mutex); + OSL::OSLCompiler *compiler = new OSL::OSLCompiler(&OSL::ErrorHandler::default_handler()); bool ok = compiler->compile(string_view(inputfile), options, string_view(stdosl_path)); delete compiler; @@ -603,7 +619,7 @@ string OSLCompiler::compatible_name(ShaderNode *node, ShaderInput *input) string sname(input->name().string()); size_t i; - /* strip whitespace */ + /* Strip white-space. */ while ((i = sname.find(" ")) != string::npos) sname.replace(i, 1, ""); @@ -623,7 +639,7 @@ string OSLCompiler::compatible_name(ShaderNode *node, ShaderOutput *output) string sname(output->name().string()); size_t i; - /* strip whitespace */ + /* Strip white-space. */ while ((i = sname.find(" ")) != string::npos) sname.replace(i, 1, ""); @@ -716,8 +732,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) } } - /* create shader of the appropriate type. OSL only distinguishes between "surface" - * and "displacement" atm */ + /* Create shader of the appropriate type. OSL only distinguishes between "surface" + * and "displacement" at the moment. */ if (current_type == SHADER_TYPE_SURFACE) ss->Shader("surface", name, id(node).c_str()); else if (current_type == SHADER_TYPE_VOLUME) @@ -758,7 +774,8 @@ void OSLCompiler::add(ShaderNode *node, const char *name, bool isfilepath) current_shader->has_surface_bssrdf = true; current_shader->has_bssrdf_bump = true; /* can't detect yet */ } - current_shader->has_bump = true; /* can't detect yet */ + current_shader->has_bump = true; /* can't detect yet */ + current_shader->has_surface_raytrace = true; /* can't detect yet */ } if (node->has_spatial_varying()) { @@ -1054,6 +1071,8 @@ void OSLCompiler::generate_nodes(const ShaderNodeSet &nodes) current_shader->has_surface_emission = true; if (node->has_surface_transparent()) current_shader->has_surface_transparent = true; + if (node->get_feature() & KERNEL_FEATURE_NODE_RAYTRACE) + current_shader->has_surface_raytrace = true; if (node->has_spatial_varying()) current_shader->has_surface_spatial_varying = true; if (node->has_surface_bssrdf()) { diff --git a/intern/cycles/render/osl.h b/intern/cycles/scene/osl.h similarity index 96% rename from intern/cycles/render/osl.h rename to intern/cycles/scene/osl.h index dfeec54d915..d54040e1047 100644 --- a/intern/cycles/render/osl.h +++ b/intern/cycles/scene/osl.h @@ -17,14 +17,14 @@ #ifndef __OSL_H__ #define __OSL_H__ -#include "util/util_array.h" -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_thread.h" +#include "util/array.h" +#include "util/set.h" +#include "util/string.h" +#include "util/thread.h" -#include "render/graph.h" -#include "render/nodes.h" -#include "render/shader.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" #ifdef WITH_OSL # include diff --git a/intern/cycles/render/particles.cpp b/intern/cycles/scene/particles.cpp similarity index 93% rename from intern/cycles/render/particles.cpp rename to intern/cycles/scene/particles.cpp index 02dd1359b18..92381171082 100644 --- a/intern/cycles/render/particles.cpp +++ b/intern/cycles/scene/particles.cpp @@ -14,17 +14,17 @@ * limitations under the License. */ -#include "render/particles.h" +#include "scene/particles.h" #include "device/device.h" -#include "render/scene.h" -#include "render/stats.h" +#include "scene/scene.h" +#include "scene/stats.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_map.h" -#include "util/util_progress.h" -#include "util/util_vector.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/map.h" +#include "util/progress.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/particles.h b/intern/cycles/scene/particles.h similarity index 96% rename from intern/cycles/render/particles.h rename to intern/cycles/scene/particles.h index 8b59756f148..b958d12e4e3 100644 --- a/intern/cycles/render/particles.h +++ b/intern/cycles/scene/particles.h @@ -17,8 +17,8 @@ #ifndef __PARTICLES_H__ #define __PARTICLES_H__ -#include "util/util_array.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/types.h" #include "graph/node.h" diff --git a/intern/cycles/scene/pass.cpp b/intern/cycles/scene/pass.cpp new file mode 100644 index 00000000000..a885ede50a4 --- /dev/null +++ b/intern/cycles/scene/pass.cpp @@ -0,0 +1,427 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "scene/pass.h" + +#include "util/algorithm.h" +#include "util/log.h" + +CCL_NAMESPACE_BEGIN + +const char *pass_type_as_string(const PassType type) +{ + const int type_int = static_cast(type); + + const NodeEnum *type_enum = Pass::get_type_enum(); + + if (!type_enum->exists(type_int)) { + LOG(DFATAL) << "Unhandled pass type " << static_cast(type) << ", not supposed to happen."; + return "UNKNOWN"; + } + + return (*type_enum)[type_int].c_str(); +} + +const char *pass_mode_as_string(PassMode mode) +{ + switch (mode) { + case PassMode::NOISY: + return "NOISY"; + case PassMode::DENOISED: + return "DENOISED"; + } + + LOG(DFATAL) << "Unhandled pass mode " << static_cast(mode) << ", should never happen."; + return "UNKNOWN"; +} + +std::ostream &operator<<(std::ostream &os, PassMode mode) +{ + os << pass_mode_as_string(mode); + return os; +} + +const NodeEnum *Pass::get_type_enum() +{ + static NodeEnum pass_type_enum; + + if (pass_type_enum.empty()) { + + /* Light Passes. */ + pass_type_enum.insert("combined", PASS_COMBINED); + pass_type_enum.insert("emission", PASS_EMISSION); + pass_type_enum.insert("background", PASS_BACKGROUND); + pass_type_enum.insert("ao", PASS_AO); + pass_type_enum.insert("shadow", PASS_SHADOW); + pass_type_enum.insert("diffuse", PASS_DIFFUSE); + pass_type_enum.insert("diffuse_direct", PASS_DIFFUSE_DIRECT); + pass_type_enum.insert("diffuse_indirect", PASS_DIFFUSE_INDIRECT); + pass_type_enum.insert("glossy", PASS_GLOSSY); + pass_type_enum.insert("glossy_direct", PASS_GLOSSY_DIRECT); + pass_type_enum.insert("glossy_indirect", PASS_GLOSSY_INDIRECT); + pass_type_enum.insert("transmission", PASS_TRANSMISSION); + pass_type_enum.insert("transmission_direct", PASS_TRANSMISSION_DIRECT); + pass_type_enum.insert("transmission_indirect", PASS_TRANSMISSION_INDIRECT); + pass_type_enum.insert("volume", PASS_VOLUME); + pass_type_enum.insert("volume_direct", PASS_VOLUME_DIRECT); + pass_type_enum.insert("volume_indirect", PASS_VOLUME_INDIRECT); + + /* Data passes. */ + pass_type_enum.insert("depth", PASS_DEPTH); + pass_type_enum.insert("position", PASS_POSITION); + pass_type_enum.insert("normal", PASS_NORMAL); + pass_type_enum.insert("roughness", PASS_ROUGHNESS); + pass_type_enum.insert("uv", PASS_UV); + pass_type_enum.insert("object_id", PASS_OBJECT_ID); + pass_type_enum.insert("material_id", PASS_MATERIAL_ID); + pass_type_enum.insert("motion", PASS_MOTION); + pass_type_enum.insert("motion_weight", PASS_MOTION_WEIGHT); + pass_type_enum.insert("cryptomatte", PASS_CRYPTOMATTE); + pass_type_enum.insert("aov_color", PASS_AOV_COLOR); + pass_type_enum.insert("aov_value", PASS_AOV_VALUE); + pass_type_enum.insert("adaptive_aux_buffer", PASS_ADAPTIVE_AUX_BUFFER); + pass_type_enum.insert("sample_count", PASS_SAMPLE_COUNT); + pass_type_enum.insert("diffuse_color", PASS_DIFFUSE_COLOR); + pass_type_enum.insert("glossy_color", PASS_GLOSSY_COLOR); + pass_type_enum.insert("transmission_color", PASS_TRANSMISSION_COLOR); + pass_type_enum.insert("mist", PASS_MIST); + pass_type_enum.insert("denoising_normal", PASS_DENOISING_NORMAL); + pass_type_enum.insert("denoising_albedo", PASS_DENOISING_ALBEDO); + pass_type_enum.insert("denoising_depth", PASS_DENOISING_DEPTH); + + pass_type_enum.insert("shadow_catcher", PASS_SHADOW_CATCHER); + pass_type_enum.insert("shadow_catcher_sample_count", PASS_SHADOW_CATCHER_SAMPLE_COUNT); + pass_type_enum.insert("shadow_catcher_matte", PASS_SHADOW_CATCHER_MATTE); + + pass_type_enum.insert("bake_primitive", PASS_BAKE_PRIMITIVE); + pass_type_enum.insert("bake_differential", PASS_BAKE_DIFFERENTIAL); + } + + return &pass_type_enum; +} + +const NodeEnum *Pass::get_mode_enum() +{ + static NodeEnum pass_mode_enum; + + if (pass_mode_enum.empty()) { + pass_mode_enum.insert("noisy", static_cast(PassMode::NOISY)); + pass_mode_enum.insert("denoised", static_cast(PassMode::DENOISED)); + } + + return &pass_mode_enum; +} + +NODE_DEFINE(Pass) +{ + NodeType *type = NodeType::add("pass", create); + + const NodeEnum *pass_type_enum = get_type_enum(); + const NodeEnum *pass_mode_enum = get_mode_enum(); + + SOCKET_ENUM(type, "Type", *pass_type_enum, PASS_COMBINED); + SOCKET_ENUM(mode, "Mode", *pass_mode_enum, static_cast(PassMode::DENOISED)); + SOCKET_STRING(name, "Name", ustring()); + SOCKET_BOOLEAN(include_albedo, "Include Albedo", false); + + return type; +} + +Pass::Pass() : Node(get_node_type()), is_auto_(false) +{ +} + +PassInfo Pass::get_info() const +{ + return get_info(type, include_albedo); +} + +bool Pass::is_written() const +{ + return get_info().is_written; +} + +PassInfo Pass::get_info(const PassType type, const bool include_albedo) +{ + PassInfo pass_info; + + pass_info.use_filter = true; + pass_info.use_exposure = false; + pass_info.divide_type = PASS_NONE; + pass_info.use_compositing = false; + pass_info.use_denoising_albedo = true; + + switch (type) { + case PASS_NONE: + pass_info.num_components = 0; + break; + case PASS_COMBINED: + pass_info.num_components = 4; + pass_info.use_exposure = true; + pass_info.support_denoise = true; + break; + case PASS_DEPTH: + pass_info.num_components = 1; + pass_info.use_filter = false; + break; + case PASS_MIST: + pass_info.num_components = 1; + break; + case PASS_POSITION: + pass_info.num_components = 3; + pass_info.use_filter = false; + break; + case PASS_NORMAL: + pass_info.num_components = 3; + break; + case PASS_ROUGHNESS: + pass_info.num_components = 1; + break; + case PASS_UV: + pass_info.num_components = 3; + break; + case PASS_MOTION: + pass_info.num_components = 4; + pass_info.divide_type = PASS_MOTION_WEIGHT; + break; + case PASS_MOTION_WEIGHT: + pass_info.num_components = 1; + break; + case PASS_OBJECT_ID: + case PASS_MATERIAL_ID: + pass_info.num_components = 1; + pass_info.use_filter = false; + break; + + case PASS_EMISSION: + case PASS_BACKGROUND: + pass_info.num_components = 3; + pass_info.use_exposure = true; + break; + case PASS_AO: + pass_info.num_components = 3; + break; + case PASS_SHADOW: + pass_info.num_components = 3; + pass_info.use_exposure = false; + break; + + case PASS_DIFFUSE_COLOR: + case PASS_GLOSSY_COLOR: + case PASS_TRANSMISSION_COLOR: + pass_info.num_components = 3; + break; + case PASS_DIFFUSE: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_DIFFUSE_DIRECT; + pass_info.indirect_type = PASS_DIFFUSE_INDIRECT; + pass_info.divide_type = (!include_albedo) ? PASS_DIFFUSE_COLOR : PASS_NONE; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_DIFFUSE_DIRECT: + case PASS_DIFFUSE_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.divide_type = (!include_albedo) ? PASS_DIFFUSE_COLOR : PASS_NONE; + pass_info.use_compositing = true; + break; + case PASS_GLOSSY: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_GLOSSY_DIRECT; + pass_info.indirect_type = PASS_GLOSSY_INDIRECT; + pass_info.divide_type = (!include_albedo) ? PASS_GLOSSY_COLOR : PASS_NONE; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_GLOSSY_DIRECT: + case PASS_GLOSSY_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.divide_type = (!include_albedo) ? PASS_GLOSSY_COLOR : PASS_NONE; + pass_info.use_compositing = true; + break; + case PASS_TRANSMISSION: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_TRANSMISSION_DIRECT; + pass_info.indirect_type = PASS_TRANSMISSION_INDIRECT; + pass_info.divide_type = (!include_albedo) ? PASS_TRANSMISSION_COLOR : PASS_NONE; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_TRANSMISSION_DIRECT: + case PASS_TRANSMISSION_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.divide_type = (!include_albedo) ? PASS_TRANSMISSION_COLOR : PASS_NONE; + pass_info.use_compositing = true; + break; + case PASS_VOLUME: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.direct_type = PASS_VOLUME_DIRECT; + pass_info.indirect_type = PASS_VOLUME_INDIRECT; + pass_info.use_compositing = true; + pass_info.is_written = false; + break; + case PASS_VOLUME_DIRECT: + case PASS_VOLUME_INDIRECT: + pass_info.num_components = 3; + pass_info.use_exposure = true; + break; + + case PASS_CRYPTOMATTE: + pass_info.num_components = 4; + break; + + case PASS_DENOISING_NORMAL: + pass_info.num_components = 3; + break; + case PASS_DENOISING_ALBEDO: + pass_info.num_components = 3; + break; + case PASS_DENOISING_DEPTH: + pass_info.num_components = 1; + break; + + case PASS_SHADOW_CATCHER: + pass_info.num_components = 3; + pass_info.use_exposure = true; + pass_info.use_compositing = true; + pass_info.use_denoising_albedo = false; + pass_info.support_denoise = true; + break; + case PASS_SHADOW_CATCHER_SAMPLE_COUNT: + pass_info.num_components = 1; + break; + case PASS_SHADOW_CATCHER_MATTE: + pass_info.num_components = 4; + pass_info.use_exposure = true; + pass_info.support_denoise = true; + /* Without shadow catcher approximation compositing is not needed. + * Since we don't know here whether approximation is used or not, leave the decision up to + * the caller which will know that. */ + break; + + case PASS_ADAPTIVE_AUX_BUFFER: + pass_info.num_components = 4; + break; + case PASS_SAMPLE_COUNT: + pass_info.num_components = 1; + pass_info.use_exposure = false; + break; + + case PASS_AOV_COLOR: + pass_info.num_components = 3; + break; + case PASS_AOV_VALUE: + pass_info.num_components = 1; + break; + + case PASS_BAKE_PRIMITIVE: + case PASS_BAKE_DIFFERENTIAL: + pass_info.num_components = 4; + pass_info.use_exposure = false; + pass_info.use_filter = false; + break; + + case PASS_CATEGORY_LIGHT_END: + case PASS_CATEGORY_DATA_END: + case PASS_CATEGORY_BAKE_END: + case PASS_NUM: + LOG(DFATAL) << "Unexpected pass type is used " << type; + pass_info.num_components = 0; + break; + } + + return pass_info; +} + +bool Pass::contains(const vector &passes, PassType type) +{ + for (const Pass *pass : passes) { + if (pass->get_type() != type) { + continue; + } + + return true; + } + + return false; +} + +const Pass *Pass::find(const vector &passes, const string &name) +{ + for (const Pass *pass : passes) { + if (pass->get_name() == name) { + return pass; + } + } + + return nullptr; +} + +const Pass *Pass::find(const vector &passes, PassType type, PassMode mode) +{ + for (const Pass *pass : passes) { + if (pass->get_type() != type || pass->get_mode() != mode) { + continue; + } + + return pass; + } + + return nullptr; +} + +int Pass::get_offset(const vector &passes, const Pass *pass) +{ + int pass_offset = 0; + + for (const Pass *current_pass : passes) { + /* Note that pass name is allowed to be empty. This is why we check for type and mode. */ + if (current_pass->get_type() == pass->get_type() && + current_pass->get_mode() == pass->get_mode() && + current_pass->get_name() == pass->get_name()) { + if (current_pass->is_written()) { + return pass_offset; + } + else { + return PASS_UNUSED; + } + } + if (current_pass->is_written()) { + pass_offset += current_pass->get_info().num_components; + } + } + + return PASS_UNUSED; +} + +std::ostream &operator<<(std::ostream &os, const Pass &pass) +{ + os << "type: " << pass_type_as_string(pass.get_type()); + os << ", name: \"" << pass.get_name() << "\""; + os << ", mode: " << pass.get_mode(); + os << ", is_written: " << string_from_bool(pass.is_written()); + + return os; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/scene/pass.h b/intern/cycles/scene/pass.h new file mode 100644 index 00000000000..7da07cfa562 --- /dev/null +++ b/intern/cycles/scene/pass.h @@ -0,0 +1,106 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include // NOLINT + +#include "util/string.h" +#include "util/vector.h" + +#include "kernel/types.h" + +#include "graph/node.h" + +CCL_NAMESPACE_BEGIN + +const char *pass_type_as_string(const PassType type); + +enum class PassMode { + NOISY, + DENOISED, +}; +const char *pass_mode_as_string(PassMode mode); +std::ostream &operator<<(std::ostream &os, PassMode mode); + +struct PassInfo { + int num_components = -1; + bool use_filter = false; + bool use_exposure = false; + bool is_written = true; + PassType divide_type = PASS_NONE; + PassType direct_type = PASS_NONE; + PassType indirect_type = PASS_NONE; + + /* Pass access for read can not happen directly and needs some sort of compositing (for example, + * light passes due to divide_type, or shadow catcher pass. */ + bool use_compositing = false; + + /* Used to disable albedo pass for denoising. + * Light and shadow catcher passes should not have discontinuity in the denoised result based on + * the underlying albedo. */ + bool use_denoising_albedo = true; + + /* Pass supports denoising. */ + bool support_denoise = false; +}; + +class Pass : public Node { + public: + NODE_DECLARE + + NODE_SOCKET_API(PassType, type) + NODE_SOCKET_API(PassMode, mode) + NODE_SOCKET_API(ustring, name) + NODE_SOCKET_API(bool, include_albedo) + + Pass(); + + PassInfo get_info() const; + + /* The pass is written by the render pipeline (kernel or denoiser). If the pass is written it + * will have pixels allocated in a RenderBuffer. Passes which are not written do not have their + * pixels allocated to save memory. */ + bool is_written() const; + + protected: + /* The has been created automatically as a requirement to various rendering functionality (such + * as adaptive sampling). */ + bool is_auto_; + + public: + static const NodeEnum *get_type_enum(); + static const NodeEnum *get_mode_enum(); + + static PassInfo get_info(PassType type, const bool include_albedo = false); + + static bool contains(const vector &passes, PassType type); + + /* Returns nullptr if there is no pass with the given name or type+mode. */ + static const Pass *find(const vector &passes, const string &name); + static const Pass *find(const vector &passes, + PassType type, + PassMode mode = PassMode::NOISY); + + /* Returns PASS_UNUSED if there is no corresponding pass. */ + static int get_offset(const vector &passes, const Pass *pass); + + friend class Film; +}; + +std::ostream &operator<<(std::ostream &os, const Pass &pass); + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/procedural.cpp b/intern/cycles/scene/procedural.cpp similarity index 92% rename from intern/cycles/render/procedural.cpp rename to intern/cycles/scene/procedural.cpp index 1307a35dcf2..f038c8b1023 100644 --- a/intern/cycles/render/procedural.cpp +++ b/intern/cycles/scene/procedural.cpp @@ -14,13 +14,12 @@ * limitations under the License. */ -#include "procedural.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/stats.h" -#include "render/scene.h" -#include "render/stats.h" - -#include "util/util_foreach.h" -#include "util/util_progress.h" +#include "util/foreach.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/procedural.h b/intern/cycles/scene/procedural.h similarity index 100% rename from intern/cycles/render/procedural.h rename to intern/cycles/scene/procedural.h diff --git a/intern/cycles/render/scene.cpp b/intern/cycles/scene/scene.cpp similarity index 74% rename from intern/cycles/render/scene.cpp rename to intern/cycles/scene/scene.cpp index c4e7d2c79d6..ef0ee0c6625 100644 --- a/intern/cycles/render/scene.cpp +++ b/intern/cycles/scene/scene.cpp @@ -18,30 +18,30 @@ #include "bvh/bvh.h" #include "device/device.h" -#include "render/alembic.h" -#include "render/background.h" -#include "render/bake.h" -#include "render/camera.h" -#include "render/curves.h" -#include "render/film.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/object.h" -#include "render/osl.h" -#include "render/particles.h" -#include "render/procedural.h" -#include "render/scene.h" -#include "render/session.h" -#include "render/shader.h" -#include "render/svm.h" -#include "render/tables.h" -#include "render/volume.h" +#include "scene/alembic.h" +#include "scene/background.h" +#include "scene/bake.h" +#include "scene/camera.h" +#include "scene/curves.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/particles.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/svm.h" +#include "scene/tables.h" +#include "scene/volume.h" +#include "session/session.h" -#include "util/util_foreach.h" -#include "util/util_guarded_allocator.h" -#include "util/util_logging.h" -#include "util/util_progress.h" +#include "util/foreach.h" +#include "util/guarded_allocator.h" +#include "util/log.h" +#include "util/progress.h" CCL_NAMESPACE_BEGIN @@ -49,13 +49,12 @@ DeviceScene::DeviceScene(Device *device) : bvh_nodes(device, "__bvh_nodes", MEM_GLOBAL), bvh_leaf_nodes(device, "__bvh_leaf_nodes", MEM_GLOBAL), object_node(device, "__object_node", MEM_GLOBAL), - prim_tri_index(device, "__prim_tri_index", MEM_GLOBAL), - prim_tri_verts(device, "__prim_tri_verts", MEM_GLOBAL), prim_type(device, "__prim_type", MEM_GLOBAL), prim_visibility(device, "__prim_visibility", MEM_GLOBAL), prim_index(device, "__prim_index", MEM_GLOBAL), prim_object(device, "__prim_object", MEM_GLOBAL), prim_time(device, "__prim_time", MEM_GLOBAL), + tri_verts(device, "__tri_verts", MEM_GLOBAL), tri_shader(device, "__tri_shader", MEM_GLOBAL), tri_vnormal(device, "__tri_vnormal", MEM_GLOBAL), tri_vindex(device, "__tri_vindex", MEM_GLOBAL), @@ -63,6 +62,7 @@ DeviceScene::DeviceScene(Device *device) tri_patch_uv(device, "__tri_patch_uv", MEM_GLOBAL), curves(device, "__curves", MEM_GLOBAL), curve_keys(device, "__curve_keys", MEM_GLOBAL), + curve_segments(device, "__curve_segments", MEM_GLOBAL), patches(device, "__patches", MEM_GLOBAL), objects(device, "__objects", MEM_GLOBAL), object_motion_pass(device, "__object_motion_pass", MEM_GLOBAL), @@ -163,12 +163,15 @@ void Scene::free_memory(bool final) delete p; foreach (Light *l, lights) delete l; + foreach (Pass *p, passes) + delete p; geometry.clear(); objects.clear(); lights.clear(); particle_systems.clear(); procedurals.clear(); + passes.clear(); if (device) { camera->device_free(device, &dscene, this); @@ -253,7 +256,6 @@ void Scene::device_update(Device *device_, Progress &progress) * - Camera may be used for adaptive subdivision. * - Displacement shader must have all shader data available. * - Light manager needs lookup tables and final mesh data to compute emission CDF. - * - Film needs light manager to run for use_light_visibility * - Lookup tables are done a second time to handle film tables */ @@ -358,6 +360,8 @@ void Scene::device_update(Device *device_, Progress &progress) return; if (device->have_error() == false) { + dscene.data.volume_stack_size = get_volume_stack_size(); + progress.set_status("Updating Device", "Writing constant memory"); device->const_copy_to("__data", &dscene.data, sizeof(dscene.data)); } @@ -469,88 +473,111 @@ void Scene::enable_update_stats() } } -DeviceRequestedFeatures Scene::get_requested_device_features() +void Scene::update_kernel_features() { - DeviceRequestedFeatures requested_features; + if (!need_update()) { + return; + } - shader_manager->get_requested_features(this, &requested_features); + /* These features are not being tweaked as often as shaders, + * so could be done selective magic for the viewport as well. */ + uint kernel_features = shader_manager->get_kernel_features(this); - /* This features are not being tweaked as often as shaders, - * so could be done selective magic for the viewport as well. - */ bool use_motion = need_motion() == Scene::MotionType::MOTION_BLUR; - requested_features.use_hair = false; - requested_features.use_hair_thick = (params.hair_shape == CURVE_THICK); - requested_features.use_object_motion = false; - requested_features.use_camera_motion = use_motion && camera->use_motion(); + kernel_features |= KERNEL_FEATURE_PATH_TRACING; + if (params.hair_shape == CURVE_THICK) { + kernel_features |= KERNEL_FEATURE_HAIR_THICK; + } + if (use_motion && camera->use_motion()) { + kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; + } foreach (Object *object, objects) { Geometry *geom = object->get_geometry(); if (use_motion) { - requested_features.use_object_motion |= object->use_motion() | geom->get_use_motion_blur(); - requested_features.use_camera_motion |= geom->get_use_motion_blur(); + if (object->use_motion() || geom->get_use_motion_blur()) { + kernel_features |= KERNEL_FEATURE_OBJECT_MOTION; + } + if (geom->get_use_motion_blur()) { + kernel_features |= KERNEL_FEATURE_CAMERA_MOTION; + } } if (object->get_is_shadow_catcher()) { - requested_features.use_shadow_tricks = true; + kernel_features |= KERNEL_FEATURE_SHADOW_CATCHER; } if (geom->is_mesh()) { - Mesh *mesh = static_cast(geom); #ifdef WITH_OPENSUBDIV + Mesh *mesh = static_cast(geom); if (mesh->get_subdivision_type() != Mesh::SUBDIVISION_NONE) { - requested_features.use_patch_evaluation = true; + kernel_features |= KERNEL_FEATURE_PATCH_EVALUATION; } #endif - requested_features.use_true_displacement |= mesh->has_true_displacement(); } else if (geom->is_hair()) { - requested_features.use_hair = true; + kernel_features |= KERNEL_FEATURE_HAIR; } } - requested_features.use_background_light = light_manager->has_background_light(this); - - requested_features.use_baking = bake_manager->get_baking(); - requested_features.use_integrator_branched = (integrator->get_method() == - Integrator::BRANCHED_PATH); - if (film->get_denoising_data_pass()) { - requested_features.use_denoising = true; - requested_features.use_shadow_tricks = true; + if (bake_manager->get_baking()) { + kernel_features |= KERNEL_FEATURE_BAKING; } - return requested_features; + kernel_features |= film->get_kernel_features(this); + kernel_features |= integrator->get_kernel_features(); + + dscene.data.kernel_features = kernel_features; + + /* Currently viewport render is faster with higher max_closures, needs investigating. */ + const uint max_closures = (params.background) ? get_max_closure_count() : MAX_CLOSURE; + dscene.data.max_closures = max_closures; + dscene.data.max_shaders = shaders.size(); } -bool Scene::update(Progress &progress, bool &kernel_switch_needed) +bool Scene::update(Progress &progress) { - /* update scene */ - if (need_update()) { - /* Update max_closures. */ - KernelIntegrator *kintegrator = &dscene.data.integrator; - if (params.background) { - kintegrator->max_closures = get_max_closure_count(); - } - else { - /* Currently viewport render is faster with higher max_closures, needs investigating. */ - kintegrator->max_closures = MAX_CLOSURE; - } - - /* Load render kernels, before device update where we upload data to the GPU. */ - bool new_kernels_needed = load_kernels(progress, false); - - progress.set_status("Updating Scene"); - MEM_GUARDED_CALL(&progress, device_update, device, progress); - - DeviceKernelStatus kernel_switch_status = device->get_active_kernel_switch_state(); - kernel_switch_needed = kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_AVAILABLE || - kernel_switch_status == DEVICE_KERNEL_FEATURE_KERNEL_INVALID; - if (new_kernels_needed || kernel_switch_needed) { - progress.set_kernel_status("Compiling render kernels"); - device->wait_for_availability(loaded_kernel_features); - progress.set_kernel_status(""); - } - - return true; + if (!need_update()) { + return false; } - return false; + + /* Load render kernels, before device update where we upload data to the GPU. */ + load_kernels(progress, false); + + /* Upload scene data to the GPU. */ + progress.set_status("Updating Scene"); + MEM_GUARDED_CALL(&progress, device_update, device, progress); + + return true; +} + +static void log_kernel_features(const uint features) +{ + VLOG(2) << "Requested features:\n"; + VLOG(2) << "Use BSDF " << string_from_bool(features & KERNEL_FEATURE_NODE_BSDF) << "\n"; + VLOG(2) << "Use Principled BSDF " << string_from_bool(features & KERNEL_FEATURE_PRINCIPLED) + << "\n"; + VLOG(2) << "Use Emission " << string_from_bool(features & KERNEL_FEATURE_NODE_EMISSION) << "\n"; + VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_NODE_VOLUME) << "\n"; + VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_NODE_HAIR) << "\n"; + VLOG(2) << "Use Bump " << string_from_bool(features & KERNEL_FEATURE_NODE_BUMP) << "\n"; + VLOG(2) << "Use Voronoi " << string_from_bool(features & KERNEL_FEATURE_NODE_VORONOI_EXTRA) + << "\n"; + VLOG(2) << "Use Shader Raytrace " << string_from_bool(features & KERNEL_FEATURE_NODE_RAYTRACE) + << "\n"; + VLOG(2) << "Use Transparent " << string_from_bool(features & KERNEL_FEATURE_TRANSPARENT) << "\n"; + VLOG(2) << "Use Denoising " << string_from_bool(features & KERNEL_FEATURE_DENOISING) << "\n"; + VLOG(2) << "Use Path Tracing " << string_from_bool(features & KERNEL_FEATURE_PATH_TRACING) + << "\n"; + VLOG(2) << "Use Hair " << string_from_bool(features & KERNEL_FEATURE_HAIR) << "\n"; + VLOG(2) << "Use Object Motion " << string_from_bool(features & KERNEL_FEATURE_OBJECT_MOTION) + << "\n"; + VLOG(2) << "Use Camera Motion " << string_from_bool(features & KERNEL_FEATURE_CAMERA_MOTION) + << "\n"; + VLOG(2) << "Use Baking " << string_from_bool(features & KERNEL_FEATURE_BAKING) << "\n"; + VLOG(2) << "Use Subsurface " << string_from_bool(features & KERNEL_FEATURE_SUBSURFACE) << "\n"; + VLOG(2) << "Use Volume " << string_from_bool(features & KERNEL_FEATURE_VOLUME) << "\n"; + VLOG(2) << "Use Patch Evaluation " + << string_from_bool(features & KERNEL_FEATURE_PATCH_EVALUATION) << "\n"; + VLOG(2) << "Use Shadow Catcher " << string_from_bool(features & KERNEL_FEATURE_SHADOW_CATCHER) + << "\n"; } bool Scene::load_kernels(Progress &progress, bool lock_scene) @@ -560,15 +587,17 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene) scene_lock = thread_scoped_lock(mutex); } - DeviceRequestedFeatures requested_features = get_requested_device_features(); + update_kernel_features(); - if (!kernels_loaded || loaded_kernel_features.modified(requested_features)) { + const uint kernel_features = dscene.data.kernel_features; + + if (!kernels_loaded || loaded_kernel_features != kernel_features) { progress.set_status("Loading render kernels (may take a few minutes the first time)"); scoped_timer timer; - VLOG(2) << "Requested features:\n" << requested_features; - if (!device->load_kernels(requested_features)) { + log_kernel_features(kernel_features); + if (!device->load_kernels(kernel_features)) { string message = device->error_message(); if (message.empty()) message = "Failed loading render kernel, see console for errors"; @@ -580,7 +609,7 @@ bool Scene::load_kernels(Progress &progress, bool lock_scene) } kernels_loaded = true; - loaded_kernel_features = requested_features; + loaded_kernel_features = kernel_features; return true; } return false; @@ -618,6 +647,72 @@ int Scene::get_max_closure_count() return max_closure_global; } +int Scene::get_volume_stack_size() const +{ + int volume_stack_size = 0; + + /* Space for background volume and terminator. + * Don't do optional here because camera ray initialization expects that there is space for + * at least those elements (avoiding extra condition to check if there is actual volume or not). + */ + volume_stack_size += 2; + + /* Quick non-expensive check. Can over-estimate maximum possible nested level, but does not + * require expensive calculation during pre-processing. */ + bool has_volume_object = false; + for (const Object *object : objects) { + if (!object->get_geometry()->has_volume) { + continue; + } + + if (object->intersects_volume) { + /* Object intersects another volume, assume it's possible to go deeper in the stack. */ + /* TODO(sergey): This might count nesting twice (A intersects B and B intersects A), but + * can't think of a computationally cheap algorithm. Dividing my 2 doesn't work because of + * Venn diagram example with 3 circles. */ + ++volume_stack_size; + } + else if (!has_volume_object) { + /* Allocate space for at least one volume object. */ + ++volume_stack_size; + } + + has_volume_object = true; + + if (volume_stack_size == MAX_VOLUME_STACK_SIZE) { + break; + } + } + + volume_stack_size = min(volume_stack_size, MAX_VOLUME_STACK_SIZE); + + VLOG(3) << "Detected required volume stack size " << volume_stack_size; + + return volume_stack_size; +} + +bool Scene::has_shadow_catcher() +{ + if (shadow_catcher_modified_) { + has_shadow_catcher_ = false; + for (Object *object : objects) { + if (object->get_is_shadow_catcher()) { + has_shadow_catcher_ = true; + break; + } + } + + shadow_catcher_modified_ = false; + } + + return has_shadow_catcher_; +} + +void Scene::tag_shadow_catcher_modified() +{ + shadow_catcher_modified_ = true; +} + template<> Light *Scene::create_node() { Light *node = new Light(); @@ -694,6 +789,15 @@ template<> AlembicProcedural *Scene::create_node() #endif } +template<> Pass *Scene::create_node() +{ + Pass *node = new Pass(); + node->set_owner(this); + passes.push_back(node); + film->tag_modified(); + return node; +} + template void delete_node_from_array(vector &nodes, T node) { for (size_t i = 0; i < nodes.size(); ++i) { @@ -779,6 +883,12 @@ template<> void Scene::delete_node_impl(AlembicProcedural *node) #endif } +template<> void Scene::delete_node_impl(Pass *node) +{ + delete_node_from_array(passes, node); + film->tag_modified(); +} + template static void remove_nodes_in_set(const set &nodes_set, vector &nodes_array, @@ -842,4 +952,10 @@ template<> void Scene::delete_nodes(const set &nodes, const NodeOw procedural_manager->tag_update(); } +template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner) +{ + remove_nodes_in_set(nodes, passes, owner); + film->tag_modified(); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/render/scene.h b/intern/cycles/scene/scene.h similarity index 87% rename from intern/cycles/render/scene.h rename to intern/cycles/scene/scene.h index 7d8a6774381..fa7fc54602a 100644 --- a/intern/cycles/render/scene.h +++ b/intern/cycles/scene/scene.h @@ -17,22 +17,22 @@ #ifndef __SCENE_H__ #define __SCENE_H__ -#include "bvh/bvh_params.h" +#include "bvh/params.h" -#include "render/film.h" -#include "render/image.h" -#include "render/shader.h" +#include "scene/film.h" +#include "scene/image.h" +#include "scene/shader.h" #include "device/device.h" -#include "device/device_memory.h" +#include "device/memory.h" -#include "util/util_param.h" -#include "util/util_string.h" -#include "util/util_system.h" -#include "util/util_texture.h" -#include "util/util_thread.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/param.h" +#include "util/string.h" +#include "util/system.h" +#include "util/texture.h" +#include "util/thread.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -74,8 +74,6 @@ class DeviceScene { device_vector bvh_nodes; device_vector bvh_leaf_nodes; device_vector object_node; - device_vector prim_tri_index; - device_vector prim_tri_verts; device_vector prim_type; device_vector prim_visibility; device_vector prim_index; @@ -83,14 +81,16 @@ class DeviceScene { device_vector prim_time; /* mesh */ + device_vector tri_verts; device_vector tri_shader; device_vector tri_vnormal; device_vector tri_vindex; device_vector tri_patch; device_vector tri_patch_uv; - device_vector curves; + device_vector curves; device_vector curve_keys; + device_vector curve_segments; device_vector patches; @@ -128,7 +128,7 @@ class DeviceScene { device_vector lookup_table; /* integrator */ - device_vector sample_pattern_lut; + device_vector sample_pattern_lut; /* ies lights */ device_vector ies_lights; @@ -142,27 +142,6 @@ class DeviceScene { class SceneParams { public: - /* Type of BVH, in terms whether it is supported dynamic updates of meshes - * or whether modifying geometry requires full BVH rebuild. - */ - enum BVHType { - /* BVH supports dynamic updates of geometry. - * - * Faster for updating BVH tree when doing modifications in viewport, - * but slower for rendering. - */ - BVH_DYNAMIC = 0, - /* BVH tree is calculated for specific scene, updates in geometry - * requires full tree rebuild. - * - * Slower to update BVH tree when modifying objects in viewport, also - * slower to build final BVH tree but gives best possible render speed. - */ - BVH_STATIC = 1, - - BVH_NUM_TYPES, - }; - ShadingSystem shadingsystem; /* Requested BVH layout. @@ -186,7 +165,7 @@ class SceneParams { { shadingsystem = SHADINGSYSTEM_SVM; bvh_layout = BVH_LAYOUT_BVH2; - bvh_type = BVH_DYNAMIC; + bvh_type = BVH_TYPE_DYNAMIC; use_bvh_spatial_split = false; use_bvh_unaligned_nodes = true; num_bvh_time_steps = 0; @@ -196,7 +175,7 @@ class SceneParams { background = true; } - bool modified(const SceneParams ¶ms) + bool modified(const SceneParams ¶ms) const { return !(shadingsystem == params.shadingsystem && bvh_layout == params.bvh_layout && bvh_type == params.bvh_type && @@ -236,7 +215,7 @@ class Scene : public NodeOwner { vector shaders; vector lights; vector particle_systems; - vector passes; + vector passes; vector procedurals; /* data managers */ @@ -291,7 +270,10 @@ class Scene : public NodeOwner { void enable_update_stats(); - bool update(Progress &progress, bool &kernel_switch_needed); + bool update(Progress &progress); + + bool has_shadow_catcher(); + void tag_shadow_catcher_modified(); /* This function is used to create a node of a specified type instead of * calling 'new', and sets the scene as the owner of the node. @@ -348,13 +330,13 @@ class Scene : public NodeOwner { void free_memory(bool final); bool kernels_loaded; - DeviceRequestedFeatures loaded_kernel_features; + uint loaded_kernel_features; + void update_kernel_features(); bool load_kernels(Progress &progress, bool lock_scene = true); - /* ** Split kernel routines ** */ - - DeviceRequestedFeatures get_requested_device_features(); + bool has_shadow_catcher_ = false; + bool shadow_catcher_modified_ = true; /* Maximum number of closure during session lifetime. */ int max_closure_global; @@ -362,6 +344,9 @@ class Scene : public NodeOwner { /* Get maximum number of closures to be used in kernel. */ int get_max_closure_count(); + /* Get size of a volume stack needed to render this scene. */ + int get_volume_stack_size() const; + template void delete_node_impl(T *node) { delete node; @@ -384,6 +369,8 @@ template<> Shader *Scene::create_node(); template<> AlembicProcedural *Scene::create_node(); +template<> Pass *Scene::create_node(); + template<> void Scene::delete_node_impl(Light *node); template<> void Scene::delete_node_impl(Mesh *node); @@ -404,6 +391,8 @@ template<> void Scene::delete_node_impl(Procedural *node); template<> void Scene::delete_node_impl(AlembicProcedural *node); +template<> void Scene::delete_node_impl(Pass *node); + template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); @@ -416,6 +405,8 @@ template<> void Scene::delete_nodes(const set &nodes, const NodeOwner template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); +template<> void Scene::delete_nodes(const set &nodes, const NodeOwner *owner); + CCL_NAMESPACE_END #endif /* __SCENE_H__ */ diff --git a/intern/cycles/render/shader.cpp b/intern/cycles/scene/shader.cpp similarity index 91% rename from intern/cycles/render/shader.cpp rename to intern/cycles/scene/shader.cpp index 59b60904746..0b286aba9cf 100644 --- a/intern/cycles/render/shader.cpp +++ b/intern/cycles/scene/shader.cpp @@ -16,26 +16,26 @@ #include "device/device.h" -#include "render/background.h" -#include "render/camera.h" -#include "render/colorspace.h" -#include "render/graph.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/object.h" -#include "render/osl.h" -#include "render/procedural.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/svm.h" -#include "render/tables.h" +#include "scene/background.h" +#include "scene/camera.h" +#include "scene/colorspace.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/osl.h" +#include "scene/procedural.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/svm.h" +#include "scene/tables.h" -#include "util/util_foreach.h" -#include "util/util_murmurhash.h" -#include "util/util_task.h" -#include "util/util_transform.h" +#include "util/foreach.h" +#include "util/murmurhash.h" +#include "util/task.h" +#include "util/transform.h" #ifdef WITH_OCIO # include @@ -203,6 +203,7 @@ Shader::Shader() : Node(get_node_type()) has_surface = false; has_surface_transparent = false; has_surface_emission = false; + has_surface_raytrace = false; has_surface_bssrdf = false; has_volume = false; has_displacement = false; @@ -485,7 +486,7 @@ void ShaderManager::device_update(Device *device, device_update_specific(device, dscene, scene, progress); } -void ShaderManager::device_update_common(Device *device, +void ShaderManager::device_update_common(Device * /*device*/, DeviceScene *dscene, Scene *scene, Progress & /*progress*/) @@ -508,6 +509,8 @@ void ShaderManager::device_update_common(Device *device, flag |= SD_HAS_EMISSION; if (shader->has_surface_transparent && shader->get_use_transparent_shadow()) flag |= SD_HAS_TRANSPARENT_SHADOW; + if (shader->has_surface_raytrace) + flag |= SD_HAS_RAYTRACE; if (shader->has_volume) { flag |= SD_HAS_VOLUME; has_volumes = true; @@ -528,12 +531,10 @@ void ShaderManager::device_update_common(Device *device, flag |= SD_NEED_VOLUME_ATTRIBUTES; if (shader->has_bssrdf_bump) flag |= SD_HAS_BSSRDF_BUMP; - if (device->info.has_volume_decoupled) { - if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_EQUIANGULAR) - flag |= SD_VOLUME_EQUIANGULAR; - if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_MULTIPLE_IMPORTANCE) - flag |= SD_VOLUME_MIS; - } + if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_EQUIANGULAR) + flag |= SD_VOLUME_EQUIANGULAR; + if (shader->get_volume_sampling_method() == VOLUME_SAMPLING_MULTIPLE_IMPORTANCE) + flag |= SD_VOLUME_MIS; if (shader->get_volume_interpolation_method() == VOLUME_INTERPOLATION_CUBIC) flag |= SD_VOLUME_CUBIC; if (shader->has_bump) @@ -682,39 +683,35 @@ void ShaderManager::add_default(Scene *scene) } } -void ShaderManager::get_requested_graph_features(ShaderGraph *graph, - DeviceRequestedFeatures *requested_features) +uint ShaderManager::get_graph_kernel_features(ShaderGraph *graph) { + uint kernel_features = 0; + foreach (ShaderNode *node, graph->nodes) { - requested_features->max_nodes_group = max(requested_features->max_nodes_group, - node->get_group()); - requested_features->nodes_features |= node->get_feature(); + kernel_features |= node->get_feature(); if (node->special_type == SHADER_SPECIAL_TYPE_CLOSURE) { BsdfBaseNode *bsdf_node = static_cast(node); if (CLOSURE_IS_VOLUME(bsdf_node->get_closure_type())) { - requested_features->nodes_features |= NODE_FEATURE_VOLUME; + kernel_features |= KERNEL_FEATURE_NODE_VOLUME; } else if (CLOSURE_IS_PRINCIPLED(bsdf_node->get_closure_type())) { - requested_features->use_principled = true; + kernel_features |= KERNEL_FEATURE_PRINCIPLED; } } if (node->has_surface_bssrdf()) { - requested_features->use_subsurface = true; + kernel_features |= KERNEL_FEATURE_SUBSURFACE; } if (node->has_surface_transparent()) { - requested_features->use_transparent = true; - } - if (node->has_raytrace()) { - requested_features->use_shader_raytrace = true; + kernel_features |= KERNEL_FEATURE_TRANSPARENT; } } + + return kernel_features; } -void ShaderManager::get_requested_features(Scene *scene, - DeviceRequestedFeatures *requested_features) +uint ShaderManager::get_kernel_features(Scene *scene) { - requested_features->max_nodes_group = NODE_GROUP_LEVEL_0; - requested_features->nodes_features = 0; + uint kernel_features = KERNEL_FEATURE_NODE_BSDF | KERNEL_FEATURE_NODE_EMISSION; for (int i = 0; i < scene->shaders.size(); i++) { Shader *shader = scene->shaders[i]; if (!shader->reference_count()) { @@ -722,21 +719,22 @@ void ShaderManager::get_requested_features(Scene *scene, } /* Gather requested features from all the nodes from the graph nodes. */ - get_requested_graph_features(shader->graph, requested_features); + kernel_features |= get_graph_kernel_features(shader->graph); ShaderNode *output_node = shader->graph->output(); if (output_node->input("Displacement")->link != NULL) { - requested_features->nodes_features |= NODE_FEATURE_BUMP; + kernel_features |= KERNEL_FEATURE_NODE_BUMP; if (shader->get_displacement_method() == DISPLACE_BOTH) { - requested_features->nodes_features |= NODE_FEATURE_BUMP_STATE; - requested_features->max_nodes_group = max(requested_features->max_nodes_group, - NODE_GROUP_LEVEL_1); + kernel_features |= KERNEL_FEATURE_NODE_BUMP_STATE; } } /* On top of volume nodes, also check if we need volume sampling because - * e.g. an Emission node would slip through the NODE_FEATURE_VOLUME check */ - if (shader->has_volume) - requested_features->use_volume |= true; + * e.g. an Emission node would slip through the KERNEL_FEATURE_NODE_VOLUME check */ + if (shader->has_volume_connected) { + kernel_features |= KERNEL_FEATURE_VOLUME; + } } + + return kernel_features; } void ShaderManager::free_memory() diff --git a/intern/cycles/render/shader.h b/intern/cycles/scene/shader.h similarity index 93% rename from intern/cycles/render/shader.h rename to intern/cycles/scene/shader.h index c65cac351a4..e9d26412ae8 100644 --- a/intern/cycles/render/shader.h +++ b/intern/cycles/scene/shader.h @@ -19,26 +19,25 @@ #ifdef WITH_OSL /* So no context pollution happens from indirectly included windows.h */ -# include "util/util_windows.h" +# include "util/windows.h" # include #endif -#include "kernel/kernel_types.h" -#include "render/attribute.h" +#include "kernel/types.h" +#include "scene/attribute.h" #include "graph/node.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_types.h" +#include "util/map.h" +#include "util/param.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN class Device; class DeviceScene; -class DeviceRequestedFeatures; class Mesh; class Progress; class Scene; @@ -117,6 +116,7 @@ class Shader : public Node { bool has_surface; bool has_surface_emission; bool has_surface_transparent; + bool has_surface_raytrace; bool has_volume; bool has_displacement; bool has_surface_bssrdf; @@ -216,7 +216,7 @@ class ShaderManager { static void add_default(Scene *scene); /* Selective nodes compilation. */ - void get_requested_features(Scene *scene, DeviceRequestedFeatures *requested_features); + uint get_kernel_features(Scene *scene); static void free_memory(); @@ -244,8 +244,7 @@ class ShaderManager { size_t beckmann_table_offset; - void get_requested_graph_features(ShaderGraph *graph, - DeviceRequestedFeatures *requested_features); + uint get_graph_kernel_features(ShaderGraph *graph); thread_spin_lock attribute_lock_; diff --git a/intern/cycles/render/graph.cpp b/intern/cycles/scene/shader_graph.cpp similarity index 98% rename from intern/cycles/render/graph.cpp rename to intern/cycles/scene/shader_graph.cpp index e9da48b624d..f99dfa141f6 100644 --- a/intern/cycles/render/graph.cpp +++ b/intern/cycles/scene/shader_graph.cpp @@ -14,18 +14,18 @@ * limitations under the License. */ -#include "render/graph.h" -#include "render/attribute.h" -#include "render/constant_fold.h" -#include "render/nodes.h" -#include "render/scene.h" -#include "render/shader.h" +#include "scene/shader_graph.h" +#include "scene/attribute.h" +#include "scene/constant_fold.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_nodes.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_md5.h" -#include "util/util_queue.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/md5.h" +#include "util/queue.h" CCL_NAMESPACE_BEGIN @@ -1149,7 +1149,9 @@ int ShaderGraph::get_num_closures() num_closures += 8; } else if (CLOSURE_IS_VOLUME(closure_type)) { - num_closures += VOLUME_STACK_SIZE; + /* TODO(sergey): Verify this is still needed, since we have special minimized volume storage + * for the volume steps. */ + num_closures += MAX_VOLUME_STACK_SIZE; } else if (closure_type == CLOSURE_BSDF_HAIR_PRINCIPLED_ID) { num_closures += 4; diff --git a/intern/cycles/render/graph.h b/intern/cycles/scene/shader_graph.h similarity index 94% rename from intern/cycles/render/graph.h rename to intern/cycles/scene/shader_graph.h index 5102b182593..8b525a7ec0b 100644 --- a/intern/cycles/render/graph.h +++ b/intern/cycles/scene/shader_graph.h @@ -20,14 +20,14 @@ #include "graph/node.h" #include "graph/node_type.h" -#include "kernel/kernel_types.h" +#include "kernel/types.h" -#include "util/util_list.h" -#include "util/util_map.h" -#include "util/util_param.h" -#include "util/util_set.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/map.h" +#include "util/param.h" +#include "util/set.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -224,10 +224,6 @@ class ShaderNode : public Node { { return false; } - virtual bool has_raytrace() - { - return false; - } vector inputs; vector outputs; @@ -242,22 +238,13 @@ class ShaderNode : public Node { * that those functions are for selective compilation only? */ - /* Nodes are split into several groups, group of level 0 contains - * nodes which are most commonly used, further levels are extension - * of previous one and includes less commonly used nodes. - */ - virtual int get_group() - { - return NODE_GROUP_LEVEL_0; - } - /* Node feature are used to disable huge nodes inside the group, * so it's possible to disable huge nodes inside of the required * nodes group. */ virtual int get_feature() { - return bump == SHADER_BUMP_NONE ? 0 : NODE_FEATURE_BUMP; + return bump == SHADER_BUMP_NONE ? 0 : KERNEL_FEATURE_NODE_BUMP; } /* Get closure ID to which the node compiles into. */ diff --git a/intern/cycles/render/nodes.cpp b/intern/cycles/scene/shader_nodes.cpp similarity index 97% rename from intern/cycles/render/nodes.cpp rename to intern/cycles/scene/shader_nodes.cpp index 795166bcf4c..94c96d473b6 100644 --- a/intern/cycles/render/nodes.cpp +++ b/intern/cycles/scene/shader_nodes.cpp @@ -14,29 +14,30 @@ * limitations under the License. */ -#include "render/nodes.h" -#include "render/colorspace.h" -#include "render/constant_fold.h" -#include "render/film.h" -#include "render/image.h" -#include "render/image_sky.h" -#include "render/integrator.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/osl.h" -#include "render/scene.h" -#include "render/svm.h" +#include "scene/shader_nodes.h" +#include "scene/colorspace.h" +#include "scene/constant_fold.h" +#include "scene/film.h" +#include "scene/image.h" +#include "scene/image_sky.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/osl.h" +#include "scene/scene.h" +#include "scene/svm.h" #include "sky_model.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_transform.h" +#include "util/color.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/transform.h" -#include "kernel/svm/svm_color_util.h" -#include "kernel/svm/svm_mapping_util.h" -#include "kernel/svm/svm_math_util.h" -#include "kernel/svm/svm_ramp_util.h" +#include "kernel/svm/color_util.h" +#include "kernel/svm/mapping_util.h" +#include "kernel/svm/math_util.h" +#include "kernel/svm/ramp_util.h" CCL_NAMESPACE_BEGIN @@ -2396,7 +2397,7 @@ void GlossyBsdfNode::simplify_settings(Scene *scene) * Note: Keep the epsilon in sync with kernel! */ if (!roughness_input->link && roughness <= 1e-4f) { - VLOG(1) << "Using sharp glossy BSDF."; + VLOG(3) << "Using sharp glossy BSDF."; distribution = CLOSURE_BSDF_REFLECTION_ID; } } @@ -2405,7 +2406,7 @@ void GlossyBsdfNode::simplify_settings(Scene *scene) * benefit from closure blur to remove unwanted noise. */ if (roughness_input->link == NULL && distribution == CLOSURE_BSDF_REFLECTION_ID) { - VLOG(1) << "Using GGX glossy with filter glossy."; + VLOG(3) << "Using GGX glossy with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_ID; roughness = 0.0f; } @@ -2489,7 +2490,7 @@ void GlassBsdfNode::simplify_settings(Scene *scene) * Note: Keep the epsilon in sync with kernel! */ if (!roughness_input->link && roughness <= 1e-4f) { - VLOG(1) << "Using sharp glass BSDF."; + VLOG(3) << "Using sharp glass BSDF."; distribution = CLOSURE_BSDF_SHARP_GLASS_ID; } } @@ -2498,7 +2499,7 @@ void GlassBsdfNode::simplify_settings(Scene *scene) * benefit from closure blur to remove unwanted noise. */ if (roughness_input->link == NULL && distribution == CLOSURE_BSDF_SHARP_GLASS_ID) { - VLOG(1) << "Using GGX glass with filter glossy."; + VLOG(3) << "Using GGX glass with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID; roughness = 0.0f; } @@ -2582,7 +2583,7 @@ void RefractionBsdfNode::simplify_settings(Scene *scene) * Note: Keep the epsilon in sync with kernel! */ if (!roughness_input->link && roughness <= 1e-4f) { - VLOG(1) << "Using sharp refraction BSDF."; + VLOG(3) << "Using sharp refraction BSDF."; distribution = CLOSURE_BSDF_REFRACTION_ID; } } @@ -2591,7 +2592,7 @@ void RefractionBsdfNode::simplify_settings(Scene *scene) * benefit from closure blur to remove unwanted noise. */ if (roughness_input->link == NULL && distribution == CLOSURE_BSDF_REFRACTION_ID) { - VLOG(1) << "Using GGX refraction with filter glossy."; + VLOG(3) << "Using GGX refraction with filter glossy."; distribution = CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID; roughness = 0.0f; } @@ -2736,18 +2737,22 @@ NODE_DEFINE(PrincipledBsdfNode) distribution, "Distribution", distribution_enum, CLOSURE_BSDF_MICROFACET_MULTI_GGX_GLASS_ID); static NodeEnum subsurface_method_enum; - subsurface_method_enum.insert("burley", CLOSURE_BSSRDF_PRINCIPLED_ID); - subsurface_method_enum.insert("random_walk", CLOSURE_BSSRDF_PRINCIPLED_RANDOM_WALK_ID); + subsurface_method_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); + subsurface_method_enum.insert("random_walk_fixed_radius", + CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); + subsurface_method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); SOCKET_ENUM(subsurface_method, "Subsurface Method", subsurface_method_enum, - CLOSURE_BSSRDF_PRINCIPLED_ID); + CLOSURE_BSSRDF_RANDOM_WALK_ID); SOCKET_IN_COLOR(base_color, "Base Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_COLOR(subsurface_color, "Subsurface Color", make_float3(0.8f, 0.8f, 0.8f)); SOCKET_IN_FLOAT(metallic, "Metallic", 0.0f); SOCKET_IN_FLOAT(subsurface, "Subsurface", 0.0f); SOCKET_IN_VECTOR(subsurface_radius, "Subsurface Radius", make_float3(0.1f, 0.1f, 0.1f)); + SOCKET_IN_FLOAT(subsurface_ior, "Subsurface IOR", 1.4f); + SOCKET_IN_FLOAT(subsurface_anisotropy, "Subsurface Anisotropy", 0.0f); SOCKET_IN_FLOAT(specular, "Specular", 0.0f); SOCKET_IN_FLOAT(roughness, "Roughness", 0.5f); SOCKET_IN_FLOAT(specular_tint, "Specular Tint", 0.0f); @@ -2857,6 +2862,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler, ShaderInput *p_metallic, ShaderInput *p_subsurface, ShaderInput *p_subsurface_radius, + ShaderInput *p_subsurface_ior, + ShaderInput *p_subsurface_anisotropy, ShaderInput *p_specular, ShaderInput *p_roughness, ShaderInput *p_specular_tint, @@ -2896,6 +2903,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler, int transmission_roughness_offset = compiler.stack_assign(p_transmission_roughness); int anisotropic_rotation_offset = compiler.stack_assign(p_anisotropic_rotation); int subsurface_radius_offset = compiler.stack_assign(p_subsurface_radius); + int subsurface_ior_offset = compiler.stack_assign(p_subsurface_ior); + int subsurface_anisotropy_offset = compiler.stack_assign(p_subsurface_anisotropy); compiler.add_node(NODE_CLOSURE_BSDF, compiler.encode_uchar4(closure, @@ -2929,8 +2938,10 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler, __float_as_int(bc_default.y), __float_as_int(bc_default.z)); - compiler.add_node( - clearcoat_normal_offset, subsurface_radius_offset, SVM_STACK_INVALID, SVM_STACK_INVALID); + compiler.add_node(clearcoat_normal_offset, + subsurface_radius_offset, + subsurface_ior_offset, + subsurface_anisotropy_offset); float3 ss_default = get_float3(subsurface_color_in->socket_type); @@ -2953,6 +2964,8 @@ void PrincipledBsdfNode::compile(SVMCompiler &compiler) input("Metallic"), input("Subsurface"), input("Subsurface Radius"), + input("Subsurface IOR"), + input("Subsurface Anisotropy"), input("Specular"), input("Roughness"), input("Specular Tint"), @@ -3048,16 +3061,17 @@ NODE_DEFINE(SubsurfaceScatteringNode) SOCKET_IN_NORMAL(normal, "Normal", zero_float3(), SocketType::LINK_NORMAL); SOCKET_IN_FLOAT(surface_mix_weight, "SurfaceMixWeight", 0.0f, SocketType::SVM_INTERNAL); - static NodeEnum falloff_enum; - falloff_enum.insert("cubic", CLOSURE_BSSRDF_CUBIC_ID); - falloff_enum.insert("gaussian", CLOSURE_BSSRDF_GAUSSIAN_ID); - falloff_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); - falloff_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); - SOCKET_ENUM(falloff, "Falloff", falloff_enum, CLOSURE_BSSRDF_BURLEY_ID); + static NodeEnum method_enum; + method_enum.insert("burley", CLOSURE_BSSRDF_BURLEY_ID); + method_enum.insert("random_walk_fixed_radius", CLOSURE_BSSRDF_RANDOM_WALK_FIXED_RADIUS_ID); + method_enum.insert("random_walk", CLOSURE_BSSRDF_RANDOM_WALK_ID); + SOCKET_ENUM(method, "Method", method_enum, CLOSURE_BSSRDF_RANDOM_WALK_ID); + SOCKET_IN_FLOAT(scale, "Scale", 0.01f); SOCKET_IN_VECTOR(radius, "Radius", make_float3(0.1f, 0.1f, 0.1f)); - SOCKET_IN_FLOAT(sharpness, "Sharpness", 0.0f); - SOCKET_IN_FLOAT(texture_blur, "Texture Blur", 1.0f); + + SOCKET_IN_FLOAT(subsurface_ior, "IOR", 1.4f); + SOCKET_IN_FLOAT(subsurface_anisotropy, "Anisotropy", 0.0f); SOCKET_OUT_CLOSURE(BSSRDF, "BSSRDF"); @@ -3066,20 +3080,19 @@ NODE_DEFINE(SubsurfaceScatteringNode) SubsurfaceScatteringNode::SubsurfaceScatteringNode() : BsdfNode(get_node_type()) { - closure = falloff; + closure = method; } void SubsurfaceScatteringNode::compile(SVMCompiler &compiler) { - closure = falloff; - BsdfNode::compile( - compiler, input("Scale"), input("Texture Blur"), input("Radius"), input("Sharpness")); + closure = method; + BsdfNode::compile(compiler, input("Scale"), input("IOR"), input("Radius"), input("Anisotropy")); } void SubsurfaceScatteringNode::compile(OSLCompiler &compiler) { - closure = falloff; - compiler.parameter(this, "falloff"); + closure = method; + compiler.parameter(this, "method"); compiler.add(this, "node_subsurface_scattering"); } @@ -3786,20 +3799,6 @@ void GeometryNode::compile(OSLCompiler &compiler) compiler.add(this, "node_geometry"); } -int GeometryNode::get_group() -{ - ShaderOutput *out; - int result = ShaderNode::get_group(); - - /* Backfacing uses NODE_LIGHT_PATH */ - out = output("Backfacing"); - if (!out->links.empty()) { - result = max(result, NODE_GROUP_LEVEL_1); - } - - return result; -} - /* TextureCoordinate */ NODE_DEFINE(TextureCoordinateNode) @@ -4372,6 +4371,7 @@ NODE_DEFINE(HairInfoNode) SOCKET_OUT_FLOAT(is_strand, "Is Strand"); SOCKET_OUT_FLOAT(intercept, "Intercept"); + SOCKET_OUT_FLOAT(size, "Length"); SOCKET_OUT_FLOAT(thickness, "Thickness"); SOCKET_OUT_NORMAL(tangent_normal, "Tangent Normal"); #if 0 /* Output for minimum hair width transparency - deactivated. */ @@ -4394,6 +4394,9 @@ void HairInfoNode::attributes(Shader *shader, AttributeRequestSet *attributes) if (!intercept_out->links.empty()) attributes->add(ATTR_STD_CURVE_INTERCEPT); + if (!output("Length")->links.empty()) + attributes->add(ATTR_STD_CURVE_LENGTH); + if (!output("Random")->links.empty()) attributes->add(ATTR_STD_CURVE_RANDOM); } @@ -4416,6 +4419,12 @@ void HairInfoNode::compile(SVMCompiler &compiler) compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT); } + out = output("Length"); + if (!out->links.empty()) { + int attr = compiler.attribute(ATTR_STD_CURVE_LENGTH); + compiler.add_node(NODE_ATTR, attr, compiler.stack_assign(out), NODE_ATTR_OUTPUT_FLOAT); + } + out = output("Thickness"); if (!out->links.empty()) { compiler.add_node(NODE_HAIR_INFO, NODE_INFO_CURVE_THICKNESS, compiler.stack_assign(out)); @@ -5926,33 +5935,33 @@ NODE_DEFINE(OutputAOVNode) OutputAOVNode::OutputAOVNode() : ShaderNode(get_node_type()) { special_type = SHADER_SPECIAL_TYPE_OUTPUT_AOV; - slot = -1; + offset = -1; } void OutputAOVNode::simplify_settings(Scene *scene) { - slot = scene->film->get_aov_offset(scene, name.string(), is_color); - if (slot == -1) { - slot = scene->film->get_aov_offset(scene, name.string(), is_color); + offset = scene->film->get_aov_offset(scene, name.string(), is_color); + if (offset == -1) { + offset = scene->film->get_aov_offset(scene, name.string(), is_color); } - if (slot == -1 || is_color) { + if (offset == -1 || is_color) { input("Value")->disconnect(); } - if (slot == -1 || !is_color) { + if (offset == -1 || !is_color) { input("Color")->disconnect(); } } void OutputAOVNode::compile(SVMCompiler &compiler) { - assert(slot >= 0); + assert(offset >= 0); if (is_color) { - compiler.add_node(NODE_AOV_COLOR, compiler.stack_assign(input("Color")), slot); + compiler.add_node(NODE_AOV_COLOR, compiler.stack_assign(input("Color")), offset); } else { - compiler.add_node(NODE_AOV_VALUE, compiler.stack_assign(input("Value")), slot); + compiler.add_node(NODE_AOV_VALUE, compiler.stack_assign(input("Value")), offset); } } @@ -6376,7 +6385,7 @@ void BumpNode::constant_fold(const ConstantFolder &folder) /* TODO(sergey): Ignore bump with zero strength. */ } -/* Curve node */ +/* Curves node */ CurvesNode::CurvesNode(const NodeType *node_type) : ShaderNode(node_type) { @@ -6525,6 +6534,83 @@ void VectorCurvesNode::compile(OSLCompiler &compiler) CurvesNode::compile(compiler, "node_vector_curves"); } +/* FloatCurveNode */ + +NODE_DEFINE(FloatCurveNode) +{ + NodeType *type = NodeType::add("float_curve", create, NodeType::SHADER); + + SOCKET_FLOAT_ARRAY(curve, "Curve", array()); + SOCKET_FLOAT(min_x, "Min X", 0.0f); + SOCKET_FLOAT(max_x, "Max X", 1.0f); + + SOCKET_IN_FLOAT(fac, "Factor", 0.0f); + SOCKET_IN_FLOAT(value, "Value", 0.0f); + + SOCKET_OUT_FLOAT(value, "Value"); + + return type; +} + +FloatCurveNode::FloatCurveNode() : ShaderNode(get_node_type()) +{ +} + +void FloatCurveNode::constant_fold(const ConstantFolder &folder) +{ + ShaderInput *value_in = input("Value"); + ShaderInput *fac_in = input("Factor"); + + /* evaluate fully constant node */ + if (folder.all_inputs_constant()) { + if (curve.size() == 0) { + return; + } + + float pos = (value - min_x) / (max_x - min_x); + float result = float_ramp_lookup(curve.data(), pos, true, true, curve.size()); + + folder.make_constant(value + fac * (result - value)); + } + /* remove no-op node */ + else if (!fac_in->link && fac == 0.0f) { + /* link is not null because otherwise all inputs are constant */ + folder.bypass(value_in->link); + } +} + +void FloatCurveNode::compile(SVMCompiler &compiler) +{ + if (curve.size() == 0) + return; + + ShaderInput *value_in = input("Value"); + ShaderInput *fac_in = input("Factor"); + ShaderOutput *value_out = output("Value"); + + compiler.add_node(NODE_FLOAT_CURVE, + compiler.encode_uchar4(compiler.stack_assign(fac_in), + compiler.stack_assign(value_in), + compiler.stack_assign(value_out)), + __float_as_int(min_x), + __float_as_int(max_x)); + + compiler.add_node(curve.size()); + for (int i = 0; i < curve.size(); i++) + compiler.add_node(make_float4(curve[i])); +} + +void FloatCurveNode::compile(OSLCompiler &compiler) +{ + if (curve.size() == 0) + return; + + compiler.parameter_array("ramp", curve.data(), curve.size()); + compiler.parameter(this, "min_x"); + compiler.parameter(this, "max_x"); + compiler.add(this, "node_float_curve"); +} + /* RGBRampNode */ NODE_DEFINE(RGBRampNode) @@ -6753,8 +6839,6 @@ void NormalMapNode::attributes(Shader *shader, AttributeRequestSet *attributes) attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } - - attributes->add(ATTR_STD_VERTEX_NORMAL); } ShaderNode::attributes(shader, attributes); @@ -7030,8 +7114,6 @@ void VectorDisplacementNode::attributes(Shader *shader, AttributeRequestSet *att attributes->add(ustring((string(attribute.c_str()) + ".tangent").c_str())); attributes->add(ustring((string(attribute.c_str()) + ".tangent_sign").c_str())); } - - attributes->add(ATTR_STD_VERTEX_NORMAL); } ShaderNode::attributes(shader, attributes); diff --git a/intern/cycles/render/nodes.h b/intern/cycles/scene/shader_nodes.h similarity index 90% rename from intern/cycles/render/nodes.h rename to intern/cycles/scene/shader_nodes.h index 3013e9b1866..64a2b1c7843 100644 --- a/intern/cycles/render/nodes.h +++ b/intern/cycles/scene/shader_nodes.h @@ -18,11 +18,11 @@ #define __NODES_H__ #include "graph/node.h" -#include "render/graph.h" -#include "render/image.h" +#include "scene/image.h" +#include "scene/shader_graph.h" -#include "util/util_array.h" -#include "util/util_string.h" +#include "util/array.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN @@ -143,10 +143,6 @@ class EnvironmentTextureNode : public ImageSlotTextureNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } virtual bool equals(const ShaderNode &other) { @@ -170,11 +166,6 @@ class SkyTextureNode : public TextureNode { public: SHADER_NODE_CLASS(SkyTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(NodeSkyType, sky_type) NODE_SOCKET_API(float3, sun_direction) NODE_SOCKET_API(float, turbidity) @@ -224,18 +215,13 @@ class OutputAOVNode : public ShaderNode { NODE_SOCKET_API(ustring, name) - virtual int get_group() - { - return NODE_GROUP_LEVEL_4; - } - /* Don't allow output node de-duplication. */ virtual bool equals(const ShaderNode & /*other*/) { return false; } - int slot; + int offset; bool is_color; }; @@ -243,11 +229,6 @@ class GradientTextureNode : public TextureNode { public: SHADER_NODE_CLASS(GradientTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(NodeGradientType, gradient_type) NODE_SOCKET_API(float3, vector) }; @@ -269,19 +250,14 @@ class VoronoiTextureNode : public TextureNode { public: SHADER_NODE_CLASS(VoronoiTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - virtual int get_feature() { int result = ShaderNode::get_feature(); if (dimensions == 4) { - result |= NODE_FEATURE_VORONOI_EXTRA; + result |= KERNEL_FEATURE_NODE_VORONOI_EXTRA; } else if (dimensions >= 2 && feature == NODE_VORONOI_SMOOTH_F1) { - result |= NODE_FEATURE_VORONOI_EXTRA; + result |= KERNEL_FEATURE_NODE_VORONOI_EXTRA; } return result; } @@ -301,11 +277,6 @@ class MusgraveTextureNode : public TextureNode { public: SHADER_NODE_CLASS(MusgraveTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(int, dimensions) NODE_SOCKET_API(NodeMusgraveType, musgrave_type) NODE_SOCKET_API(float, w) @@ -322,11 +293,6 @@ class WaveTextureNode : public TextureNode { public: SHADER_NODE_CLASS(WaveTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(NodeWaveType, wave_type) NODE_SOCKET_API(NodeWaveBandsDirection, bands_direction) NODE_SOCKET_API(NodeWaveRingsDirection, rings_direction) @@ -345,11 +311,6 @@ class MagicTextureNode : public TextureNode { public: SHADER_NODE_CLASS(MagicTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } - NODE_SOCKET_API(int, depth) NODE_SOCKET_API(float3, vector) NODE_SOCKET_API(float, scale) @@ -364,11 +325,6 @@ class CheckerTextureNode : public TextureNode { NODE_SOCKET_API(float3, color1) NODE_SOCKET_API(float3, color2) NODE_SOCKET_API(float, scale) - - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } }; class BrickTextureNode : public TextureNode { @@ -390,20 +346,11 @@ class BrickTextureNode : public TextureNode { NODE_SOCKET_API(float, brick_width) NODE_SOCKET_API(float, row_height) NODE_SOCKET_API(float3, vector) - - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } }; class PointDensityTextureNode : public ShaderNode { public: SHADER_NODE_NO_CLONE_CLASS(PointDensityTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_4; - } ~PointDensityTextureNode(); ShaderNode *clone(ShaderGraph *graph) const; @@ -443,10 +390,6 @@ class IESLightNode : public TextureNode { ~IESLightNode(); ShaderNode *clone(ShaderGraph *graph) const; - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(ustring, filename) NODE_SOCKET_API(ustring, ies) @@ -464,10 +407,6 @@ class IESLightNode : public TextureNode { class WhiteNoiseTextureNode : public ShaderNode { public: SHADER_NODE_CLASS(WhiteNoiseTextureNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(int, dimensions) NODE_SOCKET_API(float3, vector) @@ -477,10 +416,6 @@ class WhiteNoiseTextureNode : public ShaderNode { class MappingNode : public ShaderNode { public: SHADER_NODE_CLASS(MappingNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } void constant_fold(const ConstantFolder &folder); NODE_SOCKET_API(float3, vector) @@ -546,6 +481,11 @@ class BsdfBaseNode : public ShaderNode { return false; } + virtual int get_feature() + { + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_BSDF; + } + protected: ClosureType closure; }; @@ -606,6 +546,8 @@ class PrincipledBsdfNode : public BsdfBaseNode { ShaderInput *metallic, ShaderInput *subsurface, ShaderInput *subsurface_radius, + ShaderInput *subsurface_ior, + ShaderInput *subsurface_anisotropy, ShaderInput *specular, ShaderInput *roughness, ShaderInput *specular_tint, @@ -622,6 +564,8 @@ class PrincipledBsdfNode : public BsdfBaseNode { NODE_SOCKET_API(float3, base_color) NODE_SOCKET_API(float3, subsurface_color) NODE_SOCKET_API(float3, subsurface_radius) + NODE_SOCKET_API(float, subsurface_ior) + NODE_SOCKET_API(float, subsurface_anisotropy) NODE_SOCKET_API(float, metallic) NODE_SOCKET_API(float, subsurface) NODE_SOCKET_API(float, specular) @@ -758,14 +702,14 @@ class SubsurfaceScatteringNode : public BsdfNode { bool has_bssrdf_bump(); ClosureType get_closure_type() { - return falloff; + return method; } NODE_SOCKET_API(float, scale) NODE_SOCKET_API(float3, radius) - NODE_SOCKET_API(float, sharpness) - NODE_SOCKET_API(float, texture_blur) - NODE_SOCKET_API(ClosureType, falloff) + NODE_SOCKET_API(float, subsurface_ior) + NODE_SOCKET_API(float, subsurface_anisotropy) + NODE_SOCKET_API(ClosureType, method) }; class EmissionNode : public ShaderNode { @@ -782,6 +726,11 @@ class EmissionNode : public ShaderNode { return true; } + virtual int get_feature() + { + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_EMISSION; + } + NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, strength) NODE_SOCKET_API(float, surface_mix_weight) @@ -792,6 +741,11 @@ class BackgroundNode : public ShaderNode { SHADER_NODE_CLASS(BackgroundNode) void constant_fold(const ConstantFolder &folder); + virtual int get_feature() + { + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_EMISSION; + } + NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, strength) NODE_SOCKET_API(float, surface_mix_weight) @@ -800,10 +754,6 @@ class BackgroundNode : public ShaderNode { class HoldoutNode : public ShaderNode { public: SHADER_NODE_CLASS(HoldoutNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } virtual ClosureType get_closure_type() { return CLOSURE_HOLDOUT_ID; @@ -821,13 +771,9 @@ class AmbientOcclusionNode : public ShaderNode { { return true; } - virtual int get_group() + virtual int get_feature() { - return NODE_GROUP_LEVEL_3; - } - virtual bool has_raytrace() - { - return true; + return KERNEL_FEATURE_NODE_RAYTRACE; } NODE_SOCKET_API(float3, color) @@ -845,13 +791,9 @@ class VolumeNode : public ShaderNode { SHADER_NODE_BASE_CLASS(VolumeNode) void compile(SVMCompiler &compiler, ShaderInput *param1, ShaderInput *param2); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } virtual int get_feature() { - return ShaderNode::get_feature() | NODE_FEATURE_VOLUME; + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_VOLUME; } virtual ClosureType get_closure_type() { @@ -1013,10 +955,6 @@ class UVMapNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(ustring, attribute) NODE_SOCKET_API(bool, from_dupli) @@ -1025,10 +963,6 @@ class UVMapNode : public ShaderNode { class LightPathNode : public ShaderNode { public: SHADER_NODE_CLASS(LightPathNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } }; class LightFalloffNode : public ShaderNode { @@ -1038,10 +972,6 @@ class LightFalloffNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(float, strength) NODE_SOCKET_API(float, smooth) @@ -1050,10 +980,6 @@ class LightFalloffNode : public ShaderNode { class ObjectInfoNode : public ShaderNode { public: SHADER_NODE_CLASS(ObjectInfoNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } }; class ParticleInfoNode : public ShaderNode { @@ -1064,10 +990,6 @@ class ParticleInfoNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } }; class HairInfoNode : public ShaderNode { @@ -1083,13 +1005,9 @@ class HairInfoNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } virtual int get_feature() { - return ShaderNode::get_feature() | NODE_FEATURE_HAIR; + return ShaderNode::get_feature() | KERNEL_FEATURE_NODE_HAIR; } }; @@ -1168,10 +1086,6 @@ class InvertNode : public ShaderNode { public: SHADER_NODE_CLASS(InvertNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, fac) NODE_SOCKET_API(float3, color) @@ -1182,11 +1096,6 @@ class MixNode : public ShaderNode { SHADER_NODE_CLASS(MixNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } - NODE_SOCKET_API(NodeMix, mix_type) NODE_SOCKET_API(bool, use_clamp) NODE_SOCKET_API(float3, color1) @@ -1198,10 +1107,6 @@ class CombineRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineRGBNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, r) NODE_SOCKET_API(float, g) @@ -1212,10 +1117,6 @@ class CombineHSVNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineHSVNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, h) NODE_SOCKET_API(float, s) @@ -1226,10 +1127,6 @@ class CombineXYZNode : public ShaderNode { public: SHADER_NODE_CLASS(CombineXYZNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, x) NODE_SOCKET_API(float, y) @@ -1240,10 +1137,6 @@ class GammaNode : public ShaderNode { public: SHADER_NODE_CLASS(GammaNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, gamma) @@ -1253,10 +1146,6 @@ class BrightContrastNode : public ShaderNode { public: SHADER_NODE_CLASS(BrightContrastNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, color) NODE_SOCKET_API(float, bright) @@ -1267,10 +1156,6 @@ class SeparateRGBNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateRGBNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float3, color) }; @@ -1279,10 +1164,6 @@ class SeparateHSVNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateHSVNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float3, color) }; @@ -1291,10 +1172,6 @@ class SeparateXYZNode : public ShaderNode { public: SHADER_NODE_CLASS(SeparateXYZNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float3, vector) }; @@ -1333,10 +1210,6 @@ class CameraNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } }; class FresnelNode : public ShaderNode { @@ -1346,10 +1219,6 @@ class FresnelNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, normal) NODE_SOCKET_API(float, IOR) @@ -1362,10 +1231,6 @@ class LayerWeightNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API(float3, normal) NODE_SOCKET_API(float, blend) @@ -1378,10 +1243,6 @@ class WireframeNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, size) NODE_SOCKET_API(bool, use_pixel_size) @@ -1390,10 +1251,6 @@ class WireframeNode : public ShaderNode { class WavelengthNode : public ShaderNode { public: SHADER_NODE_CLASS(WavelengthNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, wavelength) }; @@ -1402,10 +1259,6 @@ class BlackbodyNode : public ShaderNode { public: SHADER_NODE_CLASS(BlackbodyNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, temperature) }; @@ -1413,10 +1266,6 @@ class BlackbodyNode : public ShaderNode { class MapRangeNode : public ShaderNode { public: SHADER_NODE_CLASS(MapRangeNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } void expand(ShaderGraph *graph); NODE_SOCKET_API(float, value) @@ -1433,10 +1282,6 @@ class ClampNode : public ShaderNode { public: SHADER_NODE_CLASS(ClampNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(float, value) NODE_SOCKET_API(float, min) NODE_SOCKET_API(float, max) @@ -1446,10 +1291,6 @@ class ClampNode : public ShaderNode { class MathNode : public ShaderNode { public: SHADER_NODE_CLASS(MathNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } void expand(ShaderGraph *graph); void constant_fold(const ConstantFolder &folder); @@ -1463,10 +1304,6 @@ class MathNode : public ShaderNode { class NormalNode : public ShaderNode { public: SHADER_NODE_CLASS(NormalNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_2; - } NODE_SOCKET_API(float3, direction) NODE_SOCKET_API(float3, normal) @@ -1475,10 +1312,6 @@ class NormalNode : public ShaderNode { class VectorMathNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorMathNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } void constant_fold(const ConstantFolder &folder); NODE_SOCKET_API(float3, vector1) @@ -1492,10 +1325,6 @@ class VectorRotateNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorRotateNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(NodeVectorRotateType, rotate_type) NODE_SOCKET_API(bool, invert) NODE_SOCKET_API(float3, vector) @@ -1509,11 +1338,6 @@ class VectorTransformNode : public ShaderNode { public: SHADER_NODE_CLASS(VectorTransformNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } - NODE_SOCKET_API(NodeVectorTransformType, transform_type) NODE_SOCKET_API(NodeVectorTransformConvertSpace, convert_from) NODE_SOCKET_API(NodeVectorTransformConvertSpace, convert_to) @@ -1530,7 +1354,7 @@ class BumpNode : public ShaderNode { } virtual int get_feature() { - return NODE_FEATURE_BUMP; + return KERNEL_FEATURE_NODE_BUMP; } NODE_SOCKET_API(bool, invert) @@ -1549,11 +1373,6 @@ class CurvesNode : public ShaderNode { explicit CurvesNode(const NodeType *node_type); SHADER_NODE_BASE_CLASS(CurvesNode) - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } - NODE_SOCKET_API_ARRAY(array, curves) NODE_SOCKET_API(float, min_x) NODE_SOCKET_API(float, max_x) @@ -1579,14 +1398,22 @@ class VectorCurvesNode : public CurvesNode { void constant_fold(const ConstantFolder &folder); }; +class FloatCurveNode : public ShaderNode { + public: + SHADER_NODE_CLASS(FloatCurveNode) + void constant_fold(const ConstantFolder &folder); + + NODE_SOCKET_API_ARRAY(array, curve) + NODE_SOCKET_API(float, min_x) + NODE_SOCKET_API(float, max_x) + NODE_SOCKET_API(float, fac) + NODE_SOCKET_API(float, value) +}; + class RGBRampNode : public ShaderNode { public: SHADER_NODE_CLASS(RGBRampNode) void constant_fold(const ConstantFolder &folder); - virtual int get_group() - { - return NODE_GROUP_LEVEL_1; - } NODE_SOCKET_API_ARRAY(array, ramp) NODE_SOCKET_API_ARRAY(array, ramp_alpha) @@ -1656,10 +1483,6 @@ class NormalMapNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(NodeNormalMapSpace, space) NODE_SOCKET_API(ustring, attribute) @@ -1680,10 +1503,6 @@ class TangentNode : public ShaderNode { { return true; } - virtual int get_group() - { - return NODE_GROUP_LEVEL_3; - } NODE_SOCKET_API(NodeTangentDirectionType, direction_type) NODE_SOCKET_API(NodeTangentAxis, axis) @@ -1698,13 +1517,9 @@ class BevelNode : public ShaderNode { { return true; } - virtual int get_group() + virtual int get_feature() { - return NODE_GROUP_LEVEL_3; - } - virtual bool has_raytrace() - { - return true; + return KERNEL_FEATURE_NODE_RAYTRACE; } NODE_SOCKET_API(float, radius) @@ -1718,7 +1533,7 @@ class DisplacementNode : public ShaderNode { void constant_fold(const ConstantFolder &folder); virtual int get_feature() { - return NODE_FEATURE_BUMP; + return KERNEL_FEATURE_NODE_BUMP; } NODE_SOCKET_API(NodeNormalMapSpace, space) @@ -1739,7 +1554,7 @@ class VectorDisplacementNode : public ShaderNode { void constant_fold(const ConstantFolder &folder); virtual int get_feature() { - return NODE_FEATURE_BUMP; + return KERNEL_FEATURE_NODE_BUMP; } NODE_SOCKET_API(NodeNormalMapSpace, space) diff --git a/intern/cycles/scene/sobol.cpp b/intern/cycles/scene/sobol.cpp new file mode 100644 index 00000000000..09d10c3660e --- /dev/null +++ b/intern/cycles/scene/sobol.cpp @@ -0,0 +1,94 @@ +/* + * Sobol sequence direction vectors. + * + * This file contains code to create direction vectors for generating sobol + * sequences in high dimensions. It is adapted from code on this webpage: + * + * http://web.maths.unsw.edu.au/~fkuo/sobol/ + * + * From these papers: + * + * S. Joe and F. Y. Kuo, Remark on Algorithm 659: Implementing Sobol's quasirandom + * sequence generator, ACM Trans. Math. Softw. 29, 49-57 (2003) + * + * S. Joe and F. Y. Kuo, Constructing Sobol sequences with better two-dimensional + * projections, SIAM J. Sci. Comput. 30, 2635-2654 (2008) + */ + +/* Copyright (c) 2008, Frances Y. Kuo and Stephen Joe + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the names of the copyright holders nor the names of the + * University of New South Wales and the University of Waikato + * and its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "util/types.h" + +#include "scene/sobol.h" + +CCL_NAMESPACE_BEGIN + +#include "scene/sobol.tables" + +void sobol_generate_direction_vectors(uint vectors[][SOBOL_BITS], int dimensions) +{ + assert(dimensions <= SOBOL_MAX_DIMENSIONS); + + const uint L = SOBOL_BITS; + + /* first dimension is exception */ + uint *v = vectors[0]; + + for (uint i = 0; i < L; i++) + v[i] = 1 << (31 - i); // all m's = 1 + + for (int dim = 1; dim < dimensions; dim++) { + const SobolDirectionNumbers *numbers = &SOBOL_NUMBERS[dim - 1]; + const uint s = numbers->s; + const uint a = numbers->a; + const uint *m = numbers->m; + + v = vectors[dim]; + + if (L <= s) { + for (uint i = 0; i < L; i++) + v[i] = m[i] << (31 - i); + } + else { + for (uint i = 0; i < s; i++) + v[i] = m[i] << (31 - i); + + for (uint i = s; i < L; i++) { + v[i] = v[i - s] ^ (v[i - s] >> s); + + for (uint k = 1; k < s; k++) + v[i] ^= (((a >> (s - 1 - k)) & 1) * v[i - k]); + } + } + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/sobol.h b/intern/cycles/scene/sobol.h similarity index 96% rename from intern/cycles/render/sobol.h rename to intern/cycles/scene/sobol.h index d38857d2b35..86b2a1616b8 100644 --- a/intern/cycles/render/sobol.h +++ b/intern/cycles/scene/sobol.h @@ -17,7 +17,7 @@ #ifndef __SOBOL_H__ #define __SOBOL_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/sobol.cpp b/intern/cycles/scene/sobol.tables similarity index 99% rename from intern/cycles/render/sobol.cpp rename to intern/cycles/scene/sobol.tables index c821249b239..24f174dfae2 100644 --- a/intern/cycles/render/sobol.cpp +++ b/intern/cycles/scene/sobol.tables @@ -45,11 +45,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "util/util_types.h" - -#include "render/sobol.h" - -CCL_NAMESPACE_BEGIN +/* Note: this file has a non-standard extension so it is skipped by clang-format. */ #define SOBOL_MAX_NUMBER 32 @@ -58,10 +54,6 @@ typedef struct SobolDirectionNumbers { uint m[SOBOL_MAX_NUMBER]; } SobolDirectionNumbers; -/* Note: this file is skipped by clang-format. */ - -/* Keep simple alignment. */ -/* clang-format off */ static const SobolDirectionNumbers SOBOL_NUMBERS[SOBOL_MAX_DIMENSIONS - 1] = { {2, 1, 0, {1}}, {3, 2, 1, {1, 3}}, @@ -21264,44 +21256,3 @@ static const SobolDirectionNumbers SOBOL_NUMBERS[SOBOL_MAX_DIMENSIONS - 1] = { {21200, 18, 131020, {1, 1, 5, 1, 19, 1, 83, 3, 425, 873, 1943, 3935, 4257, 14587, 11829, 55217, 21963, 39683}}, {21201, 18, 131059, {1, 1, 7, 11, 15, 7, 37, 239, 337, 245, 1557, 3681, 7357, 9639, 27367, 26869, 114603, 86317}} }; -/* clang-format on */ - -void sobol_generate_direction_vectors(uint vectors[][SOBOL_BITS], int dimensions) -{ - assert(dimensions <= SOBOL_MAX_DIMENSIONS); - - const uint L = SOBOL_BITS; - - /* first dimension is exception */ - uint *v = vectors[0]; - - for (uint i = 0; i < L; i++) - v[i] = 1 << (31 - i); // all m's = 1 - - for (int dim = 1; dim < dimensions; dim++) { - const SobolDirectionNumbers *numbers = &SOBOL_NUMBERS[dim - 1]; - const uint s = numbers->s; - const uint a = numbers->a; - const uint *m = numbers->m; - - v = vectors[dim]; - - if (L <= s) { - for (uint i = 0; i < L; i++) - v[i] = m[i] << (31 - i); - } - else { - for (uint i = 0; i < s; i++) - v[i] = m[i] << (31 - i); - - for (uint i = s; i < L; i++) { - v[i] = v[i - s] ^ (v[i - s] >> s); - - for (uint k = 1; k < s; k++) - v[i] ^= (((a >> (s - 1 - k)) & 1) * v[i - k]); - } - } - } -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/render/stats.cpp b/intern/cycles/scene/stats.cpp similarity index 75% rename from intern/cycles/render/stats.cpp rename to intern/cycles/scene/stats.cpp index 2c6273842e2..e2b00d16593 100644 --- a/intern/cycles/render/stats.cpp +++ b/intern/cycles/scene/stats.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/stats.h" -#include "render/object.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_string.h" +#include "scene/stats.h" +#include "scene/object.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN @@ -264,53 +264,34 @@ void RenderStats::collect_profiling(Scene *scene, Profiler &prof) has_profiling = true; kernel = NamedNestedSampleStats("Total render time", prof.get_event(PROFILING_UNKNOWN)); - kernel.add_entry("Ray setup", prof.get_event(PROFILING_RAY_SETUP)); - kernel.add_entry("Result writing", prof.get_event(PROFILING_WRITE_RESULT)); + kernel.add_entry("Intersect Closest", prof.get_event(PROFILING_INTERSECT_CLOSEST)); + kernel.add_entry("Intersect Shadow", prof.get_event(PROFILING_INTERSECT_SHADOW)); + kernel.add_entry("Intersect Subsurface", prof.get_event(PROFILING_INTERSECT_SUBSURFACE)); + kernel.add_entry("Intersect Volume Stack", prof.get_event(PROFILING_INTERSECT_VOLUME_STACK)); - NamedNestedSampleStats &integrator = kernel.add_entry("Path integration", - prof.get_event(PROFILING_PATH_INTEGRATE)); - integrator.add_entry("Scene intersection", prof.get_event(PROFILING_SCENE_INTERSECT)); - integrator.add_entry("Indirect emission", prof.get_event(PROFILING_INDIRECT_EMISSION)); - integrator.add_entry("Volumes", prof.get_event(PROFILING_VOLUME)); + NamedNestedSampleStats &surface = kernel.add_entry("Shade Surface", 0); + surface.add_entry("Setup", prof.get_event(PROFILING_SHADE_SURFACE_SETUP)); + surface.add_entry("Shader Evaluation", prof.get_event(PROFILING_SHADE_SURFACE_EVAL)); + surface.add_entry("Render Passes", prof.get_event(PROFILING_SHADE_SURFACE_PASSES)); + surface.add_entry("Direct Light", prof.get_event(PROFILING_SHADE_SURFACE_DIRECT_LIGHT)); + surface.add_entry("Indirect Light", prof.get_event(PROFILING_SHADE_SURFACE_INDIRECT_LIGHT)); + surface.add_entry("Ambient Occlusion", prof.get_event(PROFILING_SHADE_SURFACE_AO)); - NamedNestedSampleStats &shading = integrator.add_entry("Shading", 0); - shading.add_entry("Shader Setup", prof.get_event(PROFILING_SHADER_SETUP)); - shading.add_entry("Shader Eval", prof.get_event(PROFILING_SHADER_EVAL)); - shading.add_entry("Shader Apply", prof.get_event(PROFILING_SHADER_APPLY)); - shading.add_entry("Ambient Occlusion", prof.get_event(PROFILING_AO)); - shading.add_entry("Subsurface", prof.get_event(PROFILING_SUBSURFACE)); + NamedNestedSampleStats &volume = kernel.add_entry("Shade Volume", 0); + volume.add_entry("Setup", prof.get_event(PROFILING_SHADE_VOLUME_SETUP)); + volume.add_entry("Integrate", prof.get_event(PROFILING_SHADE_VOLUME_INTEGRATE)); + volume.add_entry("Direct Light", prof.get_event(PROFILING_SHADE_VOLUME_DIRECT_LIGHT)); + volume.add_entry("Indirect Light", prof.get_event(PROFILING_SHADE_VOLUME_INDIRECT_LIGHT)); - integrator.add_entry("Connect Light", prof.get_event(PROFILING_CONNECT_LIGHT)); - integrator.add_entry("Surface Bounce", prof.get_event(PROFILING_SURFACE_BOUNCE)); + NamedNestedSampleStats &shadow = kernel.add_entry("Shade Shadow", 0); + shadow.add_entry("Setup", prof.get_event(PROFILING_SHADE_SHADOW_SETUP)); + shadow.add_entry("Surface", prof.get_event(PROFILING_SHADE_SHADOW_SURFACE)); + shadow.add_entry("Volume", prof.get_event(PROFILING_SHADE_SHADOW_VOLUME)); - NamedNestedSampleStats &intersection = kernel.add_entry("Intersection", 0); - intersection.add_entry("Full Intersection", prof.get_event(PROFILING_INTERSECT)); - intersection.add_entry("Local Intersection", prof.get_event(PROFILING_INTERSECT_LOCAL)); - intersection.add_entry("Shadow All Intersection", - prof.get_event(PROFILING_INTERSECT_SHADOW_ALL)); - intersection.add_entry("Volume Intersection", prof.get_event(PROFILING_INTERSECT_VOLUME)); - intersection.add_entry("Volume All Intersection", - prof.get_event(PROFILING_INTERSECT_VOLUME_ALL)); - - NamedNestedSampleStats &closure = kernel.add_entry("Closures", 0); - closure.add_entry("Surface Closure Evaluation", prof.get_event(PROFILING_CLOSURE_EVAL)); - closure.add_entry("Surface Closure Sampling", prof.get_event(PROFILING_CLOSURE_SAMPLE)); - closure.add_entry("Volume Closure Evaluation", prof.get_event(PROFILING_CLOSURE_VOLUME_EVAL)); - closure.add_entry("Volume Closure Sampling", prof.get_event(PROFILING_CLOSURE_VOLUME_SAMPLE)); - - NamedNestedSampleStats &denoising = kernel.add_entry("Denoising", - prof.get_event(PROFILING_DENOISING)); - denoising.add_entry("Construct Transform", - prof.get_event(PROFILING_DENOISING_CONSTRUCT_TRANSFORM)); - denoising.add_entry("Reconstruct", prof.get_event(PROFILING_DENOISING_RECONSTRUCT)); - - NamedNestedSampleStats &prefilter = denoising.add_entry("Prefiltering", 0); - prefilter.add_entry("Divide Shadow", prof.get_event(PROFILING_DENOISING_DIVIDE_SHADOW)); - prefilter.add_entry("Non-Local means", prof.get_event(PROFILING_DENOISING_NON_LOCAL_MEANS)); - prefilter.add_entry("Get Feature", prof.get_event(PROFILING_DENOISING_GET_FEATURE)); - prefilter.add_entry("Detect Outliers", prof.get_event(PROFILING_DENOISING_DETECT_OUTLIERS)); - prefilter.add_entry("Combine Halves", prof.get_event(PROFILING_DENOISING_COMBINE_HALVES)); + NamedNestedSampleStats &light = kernel.add_entry("Shade Light", 0); + light.add_entry("Setup", prof.get_event(PROFILING_SHADE_LIGHT_SETUP)); + light.add_entry("Shader Evaluation", prof.get_event(PROFILING_SHADE_LIGHT_EVAL)); shaders.entries.clear(); foreach (Shader *shader, scene->shaders) { diff --git a/intern/cycles/render/stats.h b/intern/cycles/scene/stats.h similarity index 97% rename from intern/cycles/render/stats.h rename to intern/cycles/scene/stats.h index 93d029bba61..d9095acc4c9 100644 --- a/intern/cycles/render/stats.h +++ b/intern/cycles/scene/stats.h @@ -17,11 +17,11 @@ #ifndef __RENDER_STATS_H__ #define __RENDER_STATS_H__ -#include "render/scene.h" +#include "scene/scene.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/svm.cpp b/intern/cycles/scene/svm.cpp similarity index 96% rename from intern/cycles/render/svm.cpp rename to intern/cycles/scene/svm.cpp index dcb3976e15c..d121765bbdd 100644 --- a/intern/cycles/render/svm.cpp +++ b/intern/cycles/scene/svm.cpp @@ -16,20 +16,20 @@ #include "device/device.h" -#include "render/background.h" -#include "render/graph.h" -#include "render/light.h" -#include "render/mesh.h" -#include "render/nodes.h" -#include "render/scene.h" -#include "render/shader.h" -#include "render/stats.h" -#include "render/svm.h" +#include "scene/background.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/scene.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" +#include "scene/stats.h" +#include "scene/svm.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_progress.h" -#include "util/util_task.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/progress.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN @@ -64,7 +64,7 @@ void SVMShaderManager::device_update_shader(Scene *scene, compiler.background = (shader == scene->background->get_shader(scene)); compiler.compile(shader, *svm_nodes, 0, &summary); - VLOG(2) << "Compilation summary:\n" + VLOG(3) << "Compilation summary:\n" << "Shader name: " << shader->name << "\n" << summary.full_report(); } @@ -446,6 +446,8 @@ void SVMCompiler::generate_node(ShaderNode *node, ShaderNodeSet &done) if (current_type == SHADER_TYPE_SURFACE) { if (node->has_spatial_varying()) current_shader->has_surface_spatial_varying = true; + if (node->get_feature() & KERNEL_FEATURE_NODE_RAYTRACE) + current_shader->has_surface_raytrace = true; } else if (current_type == SHADER_TYPE_VOLUME) { if (node->has_spatial_varying()) @@ -492,6 +494,13 @@ void SVMCompiler::generate_svm_nodes(const ShaderNodeSet &nodes, CompilerState * void SVMCompiler::generate_closure_node(ShaderNode *node, CompilerState *state) { + /* Skip generating closure that are not supported or needed for a particular + * type of shader. For example a BSDF in a volume shader. */ + const int node_feature = node->get_feature(); + if ((state->node_feature_mask & node_feature) != node_feature) { + return; + } + /* execute dependencies for closure */ foreach (ShaderInput *in, node->inputs) { if (in->link != NULL) { @@ -555,7 +564,7 @@ void SVMCompiler::find_aov_nodes_and_dependencies(ShaderNodeSet &aov_nodes, foreach (ShaderNode *node, graph->nodes) { if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) { OutputAOVNode *aov_node = static_cast(node); - if (aov_node->slot >= 0) { + if (aov_node->offset >= 0) { aov_nodes.insert(aov_node); foreach (ShaderInput *in, node->inputs) { if (in->link != NULL) { @@ -785,17 +794,21 @@ void SVMCompiler::compile_type(Shader *shader, ShaderGraph *graph, ShaderType ty case SHADER_TYPE_SURFACE: /* generate surface shader */ generate = true; shader->has_surface = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_SURFACE; break; case SHADER_TYPE_VOLUME: /* generate volume shader */ generate = true; shader->has_volume = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_VOLUME; break; case SHADER_TYPE_DISPLACEMENT: /* generate displacement shader */ generate = true; shader->has_displacement = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_DISPLACEMENT; break; case SHADER_TYPE_BUMP: /* generate bump shader */ generate = true; + state.node_feature_mask = KERNEL_FEATURE_NODE_MASK_BUMP; break; default: break; @@ -867,6 +880,7 @@ void SVMCompiler::compile(Shader *shader, array &svm_nodes, int index, Sum shader->has_surface = false; shader->has_surface_emission = false; shader->has_surface_transparent = false; + shader->has_surface_raytrace = false; shader->has_surface_bssrdf = false; shader->has_bump = has_bump; shader->has_bssrdf_bump = has_bump; @@ -964,6 +978,7 @@ SVMCompiler::CompilerState::CompilerState(ShaderGraph *graph) max_id = max(node->id, max_id); } nodes_done_flag.resize(max_id + 1, false); + node_feature_mask = 0; } CCL_NAMESPACE_END diff --git a/intern/cycles/render/svm.h b/intern/cycles/scene/svm.h similarity index 96% rename from intern/cycles/render/svm.h rename to intern/cycles/scene/svm.h index d23ff3e2a47..edfd71040e4 100644 --- a/intern/cycles/render/svm.h +++ b/intern/cycles/scene/svm.h @@ -17,14 +17,14 @@ #ifndef __SVM_H__ #define __SVM_H__ -#include "render/attribute.h" -#include "render/graph.h" -#include "render/shader.h" +#include "scene/attribute.h" +#include "scene/shader.h" +#include "scene/shader_graph.h" -#include "util/util_array.h" -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_thread.h" +#include "util/array.h" +#include "util/set.h" +#include "util/string.h" +#include "util/thread.h" CCL_NAMESPACE_BEGIN @@ -192,6 +192,9 @@ class SVMCompiler { * all areas to use this flags array. */ vector nodes_done_flag; + + /* Node features that can be compiled. */ + uint node_feature_mask; }; void stack_clear_temporary(ShaderNode *node); diff --git a/intern/cycles/render/tables.cpp b/intern/cycles/scene/tables.cpp similarity index 95% rename from intern/cycles/render/tables.cpp rename to intern/cycles/scene/tables.cpp index a0813400b1c..3544fea67d6 100644 --- a/intern/cycles/render/tables.cpp +++ b/intern/cycles/scene/tables.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ -#include "render/tables.h" +#include "scene/tables.h" #include "device/device.h" -#include "render/scene.h" -#include "render/stats.h" +#include "scene/scene.h" +#include "scene/stats.h" -#include "util/util_logging.h" -#include "util/util_time.h" +#include "util/log.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/tables.h b/intern/cycles/scene/tables.h similarity index 95% rename from intern/cycles/render/tables.h rename to intern/cycles/scene/tables.h index de538e2af78..3e52544d1fb 100644 --- a/intern/cycles/render/tables.h +++ b/intern/cycles/scene/tables.h @@ -17,8 +17,8 @@ #ifndef __TABLES_H__ #define __TABLES_H__ -#include "util/util_list.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/volume.cpp b/intern/cycles/scene/volume.cpp similarity index 98% rename from intern/cycles/render/volume.cpp rename to intern/cycles/scene/volume.cpp index 358ef71d501..509d0ecedf7 100644 --- a/intern/cycles/render/volume.cpp +++ b/intern/cycles/scene/volume.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "render/volume.h" -#include "render/attribute.h" -#include "render/image_vdb.h" -#include "render/scene.h" +#include "scene/volume.h" +#include "scene/attribute.h" +#include "scene/image_vdb.h" +#include "scene/scene.h" #ifdef WITH_OPENVDB # include @@ -25,12 +25,12 @@ # include #endif -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_logging.h" -#include "util/util_openvdb.h" -#include "util/util_progress.h" -#include "util/util_types.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/log.h" +#include "util/openvdb.h" +#include "util/progress.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/render/volume.h b/intern/cycles/scene/volume.h similarity index 97% rename from intern/cycles/render/volume.h rename to intern/cycles/scene/volume.h index 2e9703bf7ed..eae48f78b8c 100644 --- a/intern/cycles/render/volume.h +++ b/intern/cycles/scene/volume.h @@ -18,7 +18,7 @@ #include "graph/node.h" -#include "render/mesh.h" +#include "scene/mesh.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/CMakeLists.txt b/intern/cycles/session/CMakeLists.txt new file mode 100644 index 00000000000..f441def128e --- /dev/null +++ b/intern/cycles/session/CMakeLists.txt @@ -0,0 +1,48 @@ +# Copyright 2011-2021 Blender Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set(INC + .. +) + +set(SRC + buffers.cpp + denoising.cpp + merge.cpp + session.cpp + tile.cpp +) + +set(SRC_HEADERS + buffers.h + display_driver.h + denoising.h + merge.h + output_driver.h + session.h + tile.h +) + +set(LIB + cycles_device + cycles_integrator + cycles_util +) + +include_directories(${INC}) +include_directories(SYSTEM ${INC_SYS}) + +add_definitions(${GL_DEFINITIONS}) + +cycles_add_library(cycles_session "${LIB}" ${SRC} ${SRC_HEADERS}) diff --git a/intern/cycles/session/buffers.cpp b/intern/cycles/session/buffers.cpp new file mode 100644 index 00000000000..5c9e097e5b1 --- /dev/null +++ b/intern/cycles/session/buffers.cpp @@ -0,0 +1,384 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "device/device.h" +#include "session/buffers.h" + +#include "util/foreach.h" +#include "util/hash.h" +#include "util/math.h" +#include "util/time.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Convert part information to an index of `BufferParams::pass_offset_`. + */ + +static int pass_type_mode_to_index(PassType pass_type, PassMode mode) +{ + int index = static_cast(pass_type) * 2; + + if (mode == PassMode::DENOISED) { + ++index; + } + + return index; +} + +static int pass_to_index(const BufferPass &pass) +{ + return pass_type_mode_to_index(pass.type, pass.mode); +} + +/* -------------------------------------------------------------------- + * Buffer pass. + */ + +NODE_DEFINE(BufferPass) +{ + NodeType *type = NodeType::add("buffer_pass", create); + + const NodeEnum *pass_type_enum = Pass::get_type_enum(); + const NodeEnum *pass_mode_enum = Pass::get_mode_enum(); + + SOCKET_ENUM(type, "Type", *pass_type_enum, PASS_COMBINED); + SOCKET_ENUM(mode, "Mode", *pass_mode_enum, static_cast(PassMode::DENOISED)); + SOCKET_STRING(name, "Name", ustring()); + SOCKET_BOOLEAN(include_albedo, "Include Albedo", false); + + SOCKET_INT(offset, "Offset", -1); + + return type; +} + +BufferPass::BufferPass() : Node(get_node_type()) +{ +} + +BufferPass::BufferPass(const Pass *scene_pass) + : Node(get_node_type()), + type(scene_pass->get_type()), + mode(scene_pass->get_mode()), + name(scene_pass->get_name()), + include_albedo(scene_pass->get_include_albedo()) +{ +} + +PassInfo BufferPass::get_info() const +{ + return Pass::get_info(type, include_albedo); +} + +/* -------------------------------------------------------------------- + * Buffer Params. + */ + +NODE_DEFINE(BufferParams) +{ + NodeType *type = NodeType::add("buffer_params", create); + + SOCKET_INT(width, "Width", 0); + SOCKET_INT(height, "Height", 0); + + SOCKET_INT(window_x, "Window X", 0); + SOCKET_INT(window_y, "Window Y", 0); + SOCKET_INT(window_width, "Window Width", 0); + SOCKET_INT(window_height, "Window Height", 0); + + SOCKET_INT(full_x, "Full X", 0); + SOCKET_INT(full_y, "Full Y", 0); + SOCKET_INT(full_width, "Full Width", 0); + SOCKET_INT(full_height, "Full Height", 0); + + SOCKET_STRING(layer, "Layer", ustring()); + SOCKET_STRING(view, "View", ustring()); + SOCKET_INT(samples, "Samples", 0); + SOCKET_FLOAT(exposure, "Exposure", 1.0f); + SOCKET_BOOLEAN(use_approximate_shadow_catcher, "Use Approximate Shadow Catcher", false); + SOCKET_BOOLEAN(use_transparent_background, "Transparent Background", false); + + /* Notes: + * - Skip passes since they do not follow typical container socket definition. + * Might look into covering those as a socket in the future. + * + * - Skip offset, stride, and pass stride since those can be delivered from the passes and + * rest of the sockets. */ + + return type; +} + +BufferParams::BufferParams() : Node(get_node_type()) +{ + reset_pass_offset(); +} + +void BufferParams::update_passes() +{ + update_offset_stride(); + reset_pass_offset(); + + pass_stride = 0; + for (const BufferPass &pass : passes) { + if (pass.offset != PASS_UNUSED) { + const int index = pass_to_index(pass); + if (pass_offset_[index] == PASS_UNUSED) { + pass_offset_[index] = pass_stride; + } + + pass_stride += pass.get_info().num_components; + } + } +} + +void BufferParams::update_passes(const vector &scene_passes) +{ + passes.clear(); + + pass_stride = 0; + for (const Pass *scene_pass : scene_passes) { + BufferPass buffer_pass(scene_pass); + + if (scene_pass->is_written()) { + buffer_pass.offset = pass_stride; + pass_stride += scene_pass->get_info().num_components; + } + else { + buffer_pass.offset = PASS_UNUSED; + } + + passes.emplace_back(std::move(buffer_pass)); + } + + update_passes(); +} + +void BufferParams::reset_pass_offset() +{ + for (int i = 0; i < kNumPassOffsets; ++i) { + pass_offset_[i] = PASS_UNUSED; + } +} + +int BufferParams::get_pass_offset(PassType pass_type, PassMode mode) const +{ + if (pass_type == PASS_NONE || pass_type == PASS_UNUSED) { + return PASS_UNUSED; + } + + const int index = pass_type_mode_to_index(pass_type, mode); + return pass_offset_[index]; +} + +const BufferPass *BufferParams::find_pass(string_view name) const +{ + for (const BufferPass &pass : passes) { + if (pass.name == name) { + return &pass; + } + } + + return nullptr; +} + +const BufferPass *BufferParams::find_pass(PassType type, PassMode mode) const +{ + for (const BufferPass &pass : passes) { + if (pass.type == type && pass.mode == mode) { + return &pass; + } + } + + return nullptr; +} + +const BufferPass *BufferParams::get_actual_display_pass(PassType type, PassMode mode) const +{ + const BufferPass *pass = find_pass(type, mode); + return get_actual_display_pass(pass); +} + +const BufferPass *BufferParams::get_actual_display_pass(const BufferPass *pass) const +{ + if (!pass) { + return nullptr; + } + + if (pass->type == PASS_COMBINED) { + const BufferPass *shadow_catcher_matte_pass = find_pass(PASS_SHADOW_CATCHER_MATTE, pass->mode); + if (shadow_catcher_matte_pass) { + pass = shadow_catcher_matte_pass; + } + } + + return pass; +} + +void BufferParams::update_offset_stride() +{ + offset = -(full_x + full_y * width); + stride = width; +} + +bool BufferParams::modified(const BufferParams &other) const +{ + if (width != other.width || height != other.height) { + return true; + } + + if (full_x != other.full_x || full_y != other.full_y || full_width != other.full_width || + full_height != other.full_height) { + return true; + } + + if (window_x != other.window_x || window_y != other.window_y || + window_width != other.window_width || window_height != other.window_height) { + return true; + } + + if (offset != other.offset || stride != other.stride || pass_stride != other.pass_stride) { + return true; + } + + if (layer != other.layer || view != other.view) { + return true; + } + + if (exposure != other.exposure || + use_approximate_shadow_catcher != other.use_approximate_shadow_catcher || + use_transparent_background != other.use_transparent_background) { + return true; + } + + return !(passes == other.passes); +} + +/* -------------------------------------------------------------------- + * Render Buffers. + */ + +RenderBuffers::RenderBuffers(Device *device) : buffer(device, "RenderBuffers", MEM_READ_WRITE) +{ +} + +RenderBuffers::~RenderBuffers() +{ + buffer.free(); +} + +void RenderBuffers::reset(const BufferParams ¶ms_) +{ + DCHECK(params_.pass_stride != -1); + + params = params_; + + /* re-allocate buffer */ + buffer.alloc(params.width * params.pass_stride, params.height); +} + +void RenderBuffers::zero() +{ + buffer.zero_to_device(); +} + +bool RenderBuffers::copy_from_device() +{ + DCHECK(params.pass_stride != -1); + + if (!buffer.device_pointer) + return false; + + buffer.copy_from_device(0, params.width * params.pass_stride, params.height); + + return true; +} + +void RenderBuffers::copy_to_device() +{ + buffer.copy_to_device(); +} + +void render_buffers_host_copy_denoised(RenderBuffers *dst, + const BufferParams &dst_params, + const RenderBuffers *src, + const BufferParams &src_params, + const size_t src_offset) +{ + DCHECK_EQ(dst_params.width, src_params.width); + /* TODO(sergey): More sanity checks to avoid buffer overrun. */ + + /* Create a map of pass offsets to be copied. + * Assume offsets are different to allow copying passes between buffers with different set of + * passes. */ + + struct { + int dst_offset; + int src_offset; + } pass_offsets[PASS_NUM]; + + int num_passes = 0; + + for (int i = 0; i < PASS_NUM; ++i) { + const PassType pass_type = static_cast(i); + + const int dst_pass_offset = dst_params.get_pass_offset(pass_type, PassMode::DENOISED); + if (dst_pass_offset == PASS_UNUSED) { + continue; + } + + const int src_pass_offset = src_params.get_pass_offset(pass_type, PassMode::DENOISED); + if (src_pass_offset == PASS_UNUSED) { + continue; + } + + pass_offsets[num_passes].dst_offset = dst_pass_offset; + pass_offsets[num_passes].src_offset = src_pass_offset; + ++num_passes; + } + + /* Copy passes. */ + /* TODO(sergey): Make it more reusable, allowing implement copy of noisy passes. */ + + const int64_t dst_width = dst_params.width; + const int64_t dst_height = dst_params.height; + const int64_t dst_pass_stride = dst_params.pass_stride; + const int64_t dst_num_pixels = dst_width * dst_height; + + const int64_t src_pass_stride = src_params.pass_stride; + const int64_t src_offset_in_floats = src_offset * src_pass_stride; + + const float *src_pixel = src->buffer.data() + src_offset_in_floats; + float *dst_pixel = dst->buffer.data(); + + for (int i = 0; i < dst_num_pixels; + ++i, src_pixel += src_pass_stride, dst_pixel += dst_pass_stride) { + for (int pass_offset_idx = 0; pass_offset_idx < num_passes; ++pass_offset_idx) { + const int dst_pass_offset = pass_offsets[pass_offset_idx].dst_offset; + const int src_pass_offset = pass_offsets[pass_offset_idx].src_offset; + + /* TODO(sergey): Support non-RGBA passes. */ + dst_pixel[dst_pass_offset + 0] = src_pixel[src_pass_offset + 0]; + dst_pixel[dst_pass_offset + 1] = src_pixel[src_pass_offset + 1]; + dst_pixel[dst_pass_offset + 2] = src_pixel[src_pass_offset + 2]; + dst_pixel[dst_pass_offset + 3] = src_pixel[src_pass_offset + 3]; + } + } +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/session/buffers.h b/intern/cycles/session/buffers.h new file mode 100644 index 00000000000..67022bb5b6b --- /dev/null +++ b/intern/cycles/session/buffers.h @@ -0,0 +1,199 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __BUFFERS_H__ +#define __BUFFERS_H__ + +#include "device/memory.h" +#include "graph/node.h" +#include "scene/pass.h" + +#include "kernel/types.h" + +#include "util/half.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +class Device; +struct DeviceDrawParams; +struct float4; + +/* NOTE: Is not a real scene node. Using Node API for ease of (de)serialization. */ +class BufferPass : public Node { + public: + NODE_DECLARE + + PassType type = PASS_NONE; + PassMode mode = PassMode::NOISY; + ustring name; + bool include_albedo = false; + + int offset = -1; + + BufferPass(); + explicit BufferPass(const Pass *scene_pass); + + BufferPass(BufferPass &&other) noexcept = default; + BufferPass(const BufferPass &other) = default; + + BufferPass &operator=(BufferPass &&other) = default; + BufferPass &operator=(const BufferPass &other) = default; + + ~BufferPass() = default; + + PassInfo get_info() const; + + inline bool operator==(const BufferPass &other) const + { + return type == other.type && mode == other.mode && name == other.name && + include_albedo == other.include_albedo && offset == other.offset; + } + inline bool operator!=(const BufferPass &other) const + { + return !(*this == other); + } +}; + +/* Buffer Parameters + * Size of render buffer and how it fits in the full image (border render). */ + +/* NOTE: Is not a real scene node. Using Node API for ease of (de)serialization. */ +class BufferParams : public Node { + public: + NODE_DECLARE + + /* Width/height of the physical buffer. */ + int width = 0; + int height = 0; + + /* Windows defines which part of the buffers is visible. The part outside of the window is + * considered an "overscan". + * + * Window X and Y are relative to the position of the buffer in the full buffer. */ + int window_x = 0; + int window_y = 0; + int window_width = 0; + int window_height = 0; + + /* Offset into and width/height of the full buffer. */ + int full_x = 0; + int full_y = 0; + int full_width = 0; + int full_height = 0; + + /* Runtime fields, only valid after `update_passes()` or `update_offset_stride()`. */ + int offset = -1, stride = -1; + + /* Runtime fields, only valid after `update_passes()`. */ + int pass_stride = -1; + + /* Properties which are used for accessing buffer pixels outside of scene graph. */ + vector passes; + ustring layer; + ustring view; + int samples = 0; + float exposure = 1.0f; + bool use_approximate_shadow_catcher = false; + bool use_transparent_background = false; + + BufferParams(); + + BufferParams(BufferParams &&other) noexcept = default; + BufferParams(const BufferParams &other) = default; + + BufferParams &operator=(BufferParams &&other) = default; + BufferParams &operator=(const BufferParams &other) = default; + + ~BufferParams() = default; + + /* Pre-calculate all fields which depends on the passes. + * + * When the scene passes are given, the buffer passes will be created from them and stored in + * this params, and then params are updated for those passes. + * The `update_passes()` without parameters updates offsets and strides which are stored outside + * of the passes. */ + void update_passes(); + void update_passes(const vector &scene_passes); + + /* Returns PASS_UNUSED if there is no such pass in the buffer. */ + int get_pass_offset(PassType type, PassMode mode = PassMode::NOISY) const; + + /* Returns nullptr if pass with given name does not exist. */ + const BufferPass *find_pass(string_view name) const; + const BufferPass *find_pass(PassType type, PassMode mode = PassMode::NOISY) const; + + /* Get display pass from its name. + * Will do special logic to replace combined pass with shadow catcher matte. */ + const BufferPass *get_actual_display_pass(PassType type, PassMode mode = PassMode::NOISY) const; + const BufferPass *get_actual_display_pass(const BufferPass *pass) const; + + void update_offset_stride(); + + bool modified(const BufferParams &other) const; + + protected: + void reset_pass_offset(); + + /* Multiplied by 2 to be able to store noisy and denoised pass types. */ + static constexpr int kNumPassOffsets = PASS_NUM * 2; + + /* Indexed by an index derived from pass type and mode, indicates offset of the corresponding + * pass in the buffer. + * If there are multiple passes with same type and mode contains lowest offset of all of them. */ + int pass_offset_[kNumPassOffsets]; +}; + +/* Render Buffers */ + +class RenderBuffers { + public: + /* buffer parameters */ + BufferParams params; + + /* float buffer */ + device_vector buffer; + + explicit RenderBuffers(Device *device); + ~RenderBuffers(); + + void reset(const BufferParams ¶ms); + void zero(); + + bool copy_from_device(); + void copy_to_device(); +}; + +/* Copy denoised passes form source to destination. + * + * Buffer parameters are provided explicitly, allowing to copy pixels between render buffers which + * content corresponds to a render result at a non-unit resolution divider. + * + * `src_offset` allows to offset source pixel index which is used when a fraction of the source + * buffer is to be copied. + * + * Copy happens of the number of pixels in the destination. */ +void render_buffers_host_copy_denoised(RenderBuffers *dst, + const BufferParams &dst_params, + const RenderBuffers *src, + const BufferParams &src_params, + const size_t src_offset = 0); + +CCL_NAMESPACE_END + +#endif /* __BUFFERS_H__ */ diff --git a/intern/cycles/render/denoising.cpp b/intern/cycles/session/denoising.cpp similarity index 98% rename from intern/cycles/render/denoising.cpp rename to intern/cycles/session/denoising.cpp index ddbe7484800..21df068092a 100644 --- a/intern/cycles/render/denoising.cpp +++ b/intern/cycles/session/denoising.cpp @@ -14,17 +14,19 @@ * limitations under the License. */ -#include "render/denoising.h" +#include "session/denoising.h" -#include "kernel/filter/filter_defines.h" +#if 0 -#include "util/util_foreach.h" -#include "util/util_map.h" -#include "util/util_system.h" -#include "util/util_task.h" -#include "util/util_time.h" +# include "kernel/filter/filter_defines.h" -#include +# include "util/util_foreach.h" +# include "util/util_map.h" +# include "util/util_system.h" +# include "util/util_task.h" +# include "util/util_time.h" + +# include CCL_NAMESPACE_BEGIN @@ -225,7 +227,7 @@ bool DenoiseImageLayer::match_channels(int neighbor, /* Denoise Task */ DenoiseTask::DenoiseTask(Device *device, - Denoiser *denoiser, + DenoiserPipeline *denoiser, int frame, const vector &neighbor_frames) : denoiser(denoiser), @@ -386,7 +388,6 @@ void DenoiseTask::create_task(DeviceTask &task) task.denoising = denoiser->params; task.denoising.type = DENOISER_NLM; task.denoising.use = true; - task.denoising.store_passes = false; task.denoising_from_render = false; task.denoising_frames.resize(neighbor_frames.size()); @@ -863,7 +864,7 @@ bool DenoiseImage::save_output(const string &out_filepath, string &error) /* File pattern handling and outer loop over frames */ -Denoiser::Denoiser(DeviceInfo &device_info) +DenoiserPipeline::DenoiserPipeline(DeviceInfo &device_info) { samples_override = 0; tile_size = make_int2(64, 64); @@ -876,18 +877,16 @@ Denoiser::Denoiser(DeviceInfo &device_info) /* Initialize device. */ device = Device::create(device_info, stats, profiler, true); - DeviceRequestedFeatures req; - req.use_denoising = true; - device->load_kernels(req); + device->load_kernels(KERNEL_FEATURE_DENOISING); } -Denoiser::~Denoiser() +DenoiserPipeline::~DenoiserPipeline() { delete device; TaskScheduler::exit(); } -bool Denoiser::run() +bool DenoiserPipeline::run() { assert(input.size() == output.size()); @@ -931,3 +930,5 @@ bool Denoiser::run() } CCL_NAMESPACE_END + +#endif diff --git a/intern/cycles/render/denoising.h b/intern/cycles/session/denoising.h similarity index 88% rename from intern/cycles/render/denoising.h rename to intern/cycles/session/denoising.h index c1b4d0a5596..097cc570d06 100644 --- a/intern/cycles/render/denoising.h +++ b/intern/cycles/session/denoising.h @@ -17,27 +17,31 @@ #ifndef __DENOISING_H__ #define __DENOISING_H__ -#include "device/device.h" -#include "device/device_denoising.h" +#if 0 -#include "render/buffers.h" +/* TODO(sergey): Make it explicit and clear when something is a denoiser, its pipeline or + * parameters. Currently it is an annoying mixture of terms used interchangeably. */ -#include "util/util_string.h" -#include "util/util_unique_ptr.h" -#include "util/util_vector.h" +# include "device/device.h" -#include +# include "render/buffers.h" + +# include "util/util_string.h" +# include "util/util_unique_ptr.h" +# include "util/util_vector.h" + +# include OIIO_NAMESPACE_USING CCL_NAMESPACE_BEGIN -/* Denoiser */ +/* Denoiser pipeline */ -class Denoiser { +class DenoiserPipeline { public: - Denoiser(DeviceInfo &device_info); - ~Denoiser(); + DenoiserPipeline(DeviceInfo &device_info); + ~DenoiserPipeline(); bool run(); @@ -155,7 +159,10 @@ class DenoiseImage { class DenoiseTask { public: - DenoiseTask(Device *device, Denoiser *denoiser, int frame, const vector &neighbor_frames); + DenoiseTask(Device *device, + DenoiserPipeline *denoiser, + int frame, + const vector &neighbor_frames); ~DenoiseTask(); /* Task stages */ @@ -168,7 +175,7 @@ class DenoiseTask { protected: /* Denoiser parameters and device */ - Denoiser *denoiser; + DenoiserPipeline *denoiser; Device *device; /* Frame number to be denoised */ @@ -204,4 +211,6 @@ class DenoiseTask { CCL_NAMESPACE_END +#endif + #endif /* __DENOISING_H__ */ diff --git a/intern/cycles/session/display_driver.h b/intern/cycles/session/display_driver.h new file mode 100644 index 00000000000..77f89326fd0 --- /dev/null +++ b/intern/cycles/session/display_driver.h @@ -0,0 +1,131 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/half.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +/* Display driver for efficient interactive display of renders. + * + * Host applications implement this interface for viewport rendering. For best performance, we + * recommend: + * - Allocating a texture on the GPU to be interactively updated + * - Using the graphics interop mechanism to avoid CPU-GPU copying overhead + * - Using a dedicated or thread-safe graphics API context for updates, to avoid + * blocking the host application. + */ +class DisplayDriver { + public: + DisplayDriver() = default; + virtual ~DisplayDriver() = default; + + /* Render buffer parameters. */ + struct Params { + public: + /* Render resolution, ignoring progressive resolution changes. + * The texture buffer should be allocated with this size. */ + int2 size = make_int2(0, 0); + + /* For border rendering, the full resolution of the render, and the offset within that larger + * render. */ + int2 full_size = make_int2(0, 0); + int2 full_offset = make_int2(0, 0); + + bool modified(const Params &other) const + { + return !(full_offset == other.full_offset && full_size == other.full_size && + size == other.size); + } + }; + + /* Update the render from the rendering thread. + * + * Cycles periodically updates the render to be displayed. For multithreaded updates with + * potentially multiple rendering devices, it will call these methods as follows. + * + * if (driver.update_begin(params, width, height)) { + * parallel_for_each(rendering_device) { + * buffer = driver.map_texture_buffer(); + * if (buffer) { + * fill(buffer); + * driver.unmap_texture_buffer(); + * } + * } + * driver.update_end(); + * } + * + * The parameters may dynamically change due to camera changes in the scene, and resources should + * be re-allocated accordingly. + * + * The width and height passed to update_begin() are the effective render resolution taking into + * account progressive resolution changes, which may be equal to or smaller than the params.size. + * For efficiency, changes in this resolution should be handled without re-allocating resources, + * but rather by using a subset of the full resolution buffer. */ + virtual bool update_begin(const Params ¶ms, int width, int height) = 0; + virtual void update_end() = 0; + + virtual half4 *map_texture_buffer() = 0; + virtual void unmap_texture_buffer() = 0; + + /* Optionally return a handle to a native graphics API texture buffer. If supported, + * the rendering device may write directly to this buffer instead of calling + * map_texture_buffer() and unmap_texture_buffer(). */ + class GraphicsInterop { + public: + /* Dimensions of the buffer, in pixels. */ + int buffer_width = 0; + int buffer_height = 0; + + /* OpenGL pixel buffer object. */ + int opengl_pbo_id = 0; + + /* Clear the entire buffer before doing partial write to it. */ + bool need_clear = false; + }; + + virtual GraphicsInterop graphics_interop_get() + { + return GraphicsInterop(); + } + + /* (De)activate graphics context required for editing or deleting the graphics interop + * object. + * + * For example, destruction of the CUDA object associated with an OpenGL requires the + * OpenGL context to be active. */ + virtual void graphics_interop_activate(){}; + virtual void graphics_interop_deactivate(){}; + + /* Clear the display buffer by filling it with zeros. */ + virtual void clear() = 0; + + /* Draw the render using the native graphics API. + * + * Note that this may be called in parallel to updates. The implementation is responsible for + * mutex locking or other mechanisms to avoid conflicts. + * + * The parameters may have changed since the last update. The implementation is responsible for + * deciding to skip or adjust render display for such changes. + * + * Host application drawing the render buffer should use Session.draw(), which will + * call this method. */ + virtual void draw(const Params ¶ms) = 0; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/render/merge.cpp b/intern/cycles/session/merge.cpp similarity index 93% rename from intern/cycles/render/merge.cpp rename to intern/cycles/session/merge.cpp index 8a58c827e82..9044a8c4809 100644 --- a/intern/cycles/render/merge.cpp +++ b/intern/cycles/session/merge.cpp @@ -14,13 +14,13 @@ * limitations under the License. */ -#include "render/merge.h" +#include "session/merge.h" -#include "util/util_array.h" -#include "util/util_map.h" -#include "util/util_system.h" -#include "util/util_time.h" -#include "util/util_unique_ptr.h" +#include "util/array.h" +#include "util/map.h" +#include "util/system.h" +#include "util/time.h" +#include "util/unique_ptr.h" #include #include @@ -163,8 +163,26 @@ static bool parse_channels(const ImageSpec &in_spec, file_layers[layername].passes.push_back(pass); } - /* Loop over all detected render-layers, check whether they contain a full set of input channels. - * Any channels that won't be processed internally are also passed through. */ + /* If file contains a single unnamed layer, name it after the first layer metadata we find. */ + if (file_layers.size() == 1 && file_layers.find("") != file_layers.end()) { + for (const ParamValue &attrib : in_spec.extra_attribs) { + const string attrib_name = attrib.name().string(); + if (string_startswith(attrib_name, "cycles.") && string_endswith(attrib_name, ".samples")) { + /* Extract layer name. */ + const size_t start = strlen("cycles."); + const size_t end = attrib_name.size() - strlen(".samples"); + const string layername = attrib_name.substr(start, end - start); + + /* Reinsert as named instead of unnamed layer. */ + const MergeImageLayer layer = file_layers[""]; + file_layers.clear(); + file_layers[layername] = layer; + } + } + } + + /* Loop over all detected render-layers, check whether they contain a full set of input + * channels. Any channels that won't be processed internally are also passed through. */ for (auto &i : file_layers) { const string &name = i.first; MergeImageLayer &layer = i.second; diff --git a/intern/cycles/render/merge.h b/intern/cycles/session/merge.h similarity index 94% rename from intern/cycles/render/merge.h rename to intern/cycles/session/merge.h index 87e5d2d4723..be03a69b27a 100644 --- a/intern/cycles/render/merge.h +++ b/intern/cycles/session/merge.h @@ -17,8 +17,8 @@ #ifndef __MERGE_H__ #define __MERGE_H__ -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/session/output_driver.h b/intern/cycles/session/output_driver.h new file mode 100644 index 00000000000..95e15ed875b --- /dev/null +++ b/intern/cycles/session/output_driver.h @@ -0,0 +1,82 @@ +/* + * Copyright 2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "util/math.h" +#include "util/string.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +/* Output driver for reading render buffers. + * + * Host applications implement this interface for outputting render buffers for offline rendering. + * Drivers can be used to copy the buffers into the host application or write them directly to + * disk. This interface may also be used for interactive display, however the DisplayDriver is more + * efficient for that purpose. + */ +class OutputDriver { + public: + OutputDriver() = default; + virtual ~OutputDriver() = default; + + class Tile { + public: + Tile(const int2 offset, + const int2 size, + const int2 full_size, + const string_view layer, + const string_view view) + : offset(offset), size(size), full_size(full_size), layer(layer), view(view) + { + } + virtual ~Tile() = default; + + const int2 offset; + const int2 size; + const int2 full_size; + const string layer; + const string view; + + virtual bool get_pass_pixels(const string_view pass_name, + const int num_channels, + float *pixels) const = 0; + virtual bool set_pass_pixels(const string_view pass_name, + const int num_channels, + const float *pixels) const = 0; + }; + + /* Write tile once it has finished rendering. */ + virtual void write_render_tile(const Tile &tile) = 0; + + /* Update tile while rendering is in progress. Return true if any update + * was performed. */ + virtual bool update_render_tile(const Tile & /* tile */) + { + return false; + } + + /* For baking, read render pass PASS_BAKE_PRIMITIVE and PASS_BAKE_DIFFERENTIAL + * to determine which shading points to use for baking at each pixel. Return + * true if any data was read. */ + virtual bool read_render_tile(const Tile & /* tile */) + { + return false; + } +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/session/session.cpp b/intern/cycles/session/session.cpp new file mode 100644 index 00000000000..b228939689c --- /dev/null +++ b/intern/cycles/session/session.cpp @@ -0,0 +1,624 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "device/cpu/device.h" +#include "device/device.h" +#include "integrator/pass_accessor_cpu.h" +#include "integrator/path_trace.h" +#include "scene/background.h" +#include "scene/bake.h" +#include "scene/camera.h" +#include "scene/integrator.h" +#include "scene/light.h" +#include "scene/mesh.h" +#include "scene/object.h" +#include "scene/scene.h" +#include "scene/shader_graph.h" +#include "session/buffers.h" +#include "session/display_driver.h" +#include "session/output_driver.h" +#include "session/session.h" + +#include "util/foreach.h" +#include "util/function.h" +#include "util/log.h" +#include "util/math.h" +#include "util/task.h" +#include "util/time.h" + +CCL_NAMESPACE_BEGIN + +Session::Session(const SessionParams ¶ms_, const SceneParams &scene_params) + : params(params_), render_scheduler_(tile_manager_, params) +{ + TaskScheduler::init(params.threads); + + session_thread_ = nullptr; + + delayed_reset_.do_reset = false; + + pause_ = false; + cancel_ = false; + new_work_added_ = false; + + device = Device::create(params.device, stats, profiler); + + scene = new Scene(scene_params, device); + + /* Configure path tracer. */ + path_trace_ = make_unique( + device, scene->film, &scene->dscene, render_scheduler_, tile_manager_); + path_trace_->set_progress(&progress); + path_trace_->progress_update_cb = [&]() { update_status_time(); }; + + tile_manager_.full_buffer_written_cb = [&](string_view filename) { + if (!full_buffer_written_cb) { + return; + } + full_buffer_written_cb(filename); + }; +} + +Session::~Session() +{ + cancel(); + + /* Make sure path tracer is destroyed before the device. This is needed because destruction might + * need to access device for device memory free. */ + /* TODO(sergey): Convert device to be unique_ptr, and rely on C++ to destruct objects in the + * pre-defined order. */ + path_trace_.reset(); + + delete scene; + delete device; + + TaskScheduler::exit(); +} + +void Session::start() +{ + if (!session_thread_) { + session_thread_ = new thread(function_bind(&Session::run, this)); + } +} + +void Session::cancel(bool quick) +{ + if (quick && path_trace_) { + path_trace_->cancel(); + } + + if (session_thread_) { + /* wait for session thread to end */ + progress.set_cancel("Exiting"); + + { + thread_scoped_lock pause_lock(pause_mutex_); + pause_ = false; + cancel_ = true; + } + pause_cond_.notify_all(); + + wait(); + } +} + +bool Session::ready_to_reset() +{ + return path_trace_->ready_to_reset(); +} + +void Session::run_main_render_loop() +{ + path_trace_->clear_display(); + + while (true) { + RenderWork render_work = run_update_for_next_iteration(); + + if (!render_work) { + if (VLOG_IS_ON(2)) { + double total_time, render_time; + progress.get_time(total_time, render_time); + VLOG(2) << "Rendering in main loop is done in " << render_time << " seconds."; + VLOG(2) << path_trace_->full_report(); + } + + if (params.background) { + /* if no work left and in background mode, we can stop immediately. */ + progress.set_status("Finished"); + break; + } + } + + const bool did_cancel = progress.get_cancel(); + if (did_cancel) { + render_scheduler_.render_work_reschedule_on_cancel(render_work); + if (!render_work) { + break; + } + } + else if (run_wait_for_work(render_work)) { + continue; + } + + /* Stop rendering if error happened during scene update or other step of preparing scene + * for render. */ + if (device->have_error()) { + progress.set_error(device->error_message()); + break; + } + + { + /* buffers mutex is locked entirely while rendering each + * sample, and released/reacquired on each iteration to allow + * reset and draw in between */ + thread_scoped_lock buffers_lock(buffers_mutex_); + + /* update status and timing */ + update_status_time(); + + /* render */ + path_trace_->render(render_work); + + /* update status and timing */ + update_status_time(); + + /* Stop rendering if error happened during path tracing. */ + if (device->have_error()) { + progress.set_error(device->error_message()); + break; + } + } + + progress.set_update(); + + if (did_cancel) { + break; + } + } +} + +void Session::run() +{ + if (params.use_profiling && (params.device.type == DEVICE_CPU)) { + profiler.start(); + } + + /* session thread loop */ + progress.set_status("Waiting for render to start"); + + /* run */ + if (!progress.get_cancel()) { + /* reset number of rendered samples */ + progress.reset_sample(); + + run_main_render_loop(); + } + + profiler.stop(); + + /* progress update */ + if (progress.get_cancel()) + progress.set_status(progress.get_cancel_message()); + else + progress.set_update(); +} + +RenderWork Session::run_update_for_next_iteration() +{ + RenderWork render_work; + + thread_scoped_lock scene_lock(scene->mutex); + thread_scoped_lock reset_lock(delayed_reset_.mutex); + + bool have_tiles = true; + bool switched_to_new_tile = false; + + const bool did_reset = delayed_reset_.do_reset; + if (delayed_reset_.do_reset) { + thread_scoped_lock buffers_lock(buffers_mutex_); + do_delayed_reset(); + + /* After reset make sure the tile manager is at the first big tile. */ + have_tiles = tile_manager_.next(); + switched_to_new_tile = true; + } + + /* Update number of samples in the integrator. + * Ideally this would need to happen once in `Session::set_samples()`, but the issue there is + * the initial configuration when Session is created where the `set_samples()` is not used. + * + * NOTE: Unless reset was requested only allow increasing number of samples. */ + if (did_reset || scene->integrator->get_aa_samples() < params.samples) { + scene->integrator->set_aa_samples(params.samples); + } + + /* Update denoiser settings. */ + { + const DenoiseParams denoise_params = scene->integrator->get_denoise_params(); + path_trace_->set_denoiser_params(denoise_params); + } + + /* Update adaptive sampling. */ + { + const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling(); + path_trace_->set_adaptive_sampling(adaptive_sampling); + } + + render_scheduler_.set_num_samples(params.samples); + render_scheduler_.set_time_limit(params.time_limit); + + while (have_tiles) { + render_work = render_scheduler_.get_render_work(); + if (render_work) { + break; + } + + progress.add_finished_tile(false); + + have_tiles = tile_manager_.next(); + if (have_tiles) { + render_scheduler_.reset_for_next_tile(); + switched_to_new_tile = true; + } + } + + if (render_work) { + scoped_timer update_timer; + + if (switched_to_new_tile) { + BufferParams tile_params = buffer_params_; + + const Tile &tile = tile_manager_.get_current_tile(); + + tile_params.width = tile.width; + tile_params.height = tile.height; + + tile_params.window_x = tile.window_x; + tile_params.window_y = tile.window_y; + tile_params.window_width = tile.window_width; + tile_params.window_height = tile.window_height; + + tile_params.full_x = tile.x + buffer_params_.full_x; + tile_params.full_y = tile.y + buffer_params_.full_y; + tile_params.full_width = buffer_params_.full_width; + tile_params.full_height = buffer_params_.full_height; + + tile_params.update_offset_stride(); + + path_trace_->reset(buffer_params_, tile_params); + } + + const int resolution = render_work.resolution_divider; + const int width = max(1, buffer_params_.full_width / resolution); + const int height = max(1, buffer_params_.full_height / resolution); + + if (update_scene(width, height)) { + profiler.reset(scene->shaders.size(), scene->objects.size()); + } + progress.add_skip_time(update_timer, params.background); + } + + return render_work; +} + +bool Session::run_wait_for_work(const RenderWork &render_work) +{ + /* In an offline rendering there is no pause, and no tiles will mean the job is fully done. */ + if (params.background) { + return false; + } + + thread_scoped_lock pause_lock(pause_mutex_); + + if (!pause_ && render_work) { + /* Rendering is not paused and there is work to be done. No need to wait for anything. */ + return false; + } + + const bool no_work = !render_work; + update_status_time(pause_, no_work); + + /* Only leave the loop when rendering is not paused. But even if the current render is un-paused + * but there is nothing to render keep waiting until new work is added. */ + while (!cancel_) { + scoped_timer pause_timer; + + if (!pause_ && (render_work || new_work_added_ || delayed_reset_.do_reset)) { + break; + } + + /* Wait for either pause state changed, or extra samples added to render. */ + pause_cond_.wait(pause_lock); + + if (pause_) { + progress.add_skip_time(pause_timer, params.background); + } + + update_status_time(pause_, no_work); + progress.set_update(); + } + + new_work_added_ = false; + + return no_work; +} + +void Session::draw() +{ + path_trace_->draw(); +} + +int2 Session::get_effective_tile_size() const +{ + /* No support yet for baking with tiles. */ + if (!params.use_auto_tile || scene->bake_manager->get_baking()) { + return make_int2(buffer_params_.width, buffer_params_.height); + } + + /* TODO(sergey): Take available memory into account, and if there is enough memory do not tile + * and prefer optimal performance. */ + const int tile_size = tile_manager_.compute_render_tile_size(params.tile_size); + return make_int2(tile_size, tile_size); +} + +void Session::do_delayed_reset() +{ + if (!delayed_reset_.do_reset) { + return; + } + delayed_reset_.do_reset = false; + + params = delayed_reset_.session_params; + buffer_params_ = delayed_reset_.buffer_params; + + /* Store parameters used for buffers access outside of scene graph. */ + buffer_params_.samples = params.samples; + buffer_params_.exposure = scene->film->get_exposure(); + buffer_params_.use_approximate_shadow_catcher = + scene->film->get_use_approximate_shadow_catcher(); + buffer_params_.use_transparent_background = scene->background->get_transparent(); + + /* Tile and work scheduling. */ + tile_manager_.reset_scheduling(buffer_params_, get_effective_tile_size()); + render_scheduler_.reset(buffer_params_, params.samples); + + /* Passes. */ + /* When multiple tiles are used SAMPLE_COUNT pass is used to keep track of possible partial + * tile results. It is safe to use generic update function here which checks for changes since + * changes in tile settings re-creates session, which ensures film is fully updated on tile + * changes. */ + scene->film->update_passes(scene, tile_manager_.has_multiple_tiles()); + + /* Update for new state of scene and passes. */ + buffer_params_.update_passes(scene->passes); + tile_manager_.update(buffer_params_, scene); + + /* Progress. */ + progress.reset_sample(); + progress.set_total_pixel_samples(static_cast(buffer_params_.width) * + buffer_params_.height * params.samples); + + if (!params.background) { + progress.set_start_time(); + } + progress.set_render_start_time(); +} + +void Session::reset(const SessionParams &session_params, const BufferParams &buffer_params) +{ + { + thread_scoped_lock reset_lock(delayed_reset_.mutex); + thread_scoped_lock pause_lock(pause_mutex_); + + delayed_reset_.do_reset = true; + delayed_reset_.session_params = session_params; + delayed_reset_.buffer_params = buffer_params; + + path_trace_->cancel(); + } + + pause_cond_.notify_all(); +} + +void Session::set_samples(int samples) +{ + if (samples == params.samples) { + return; + } + + params.samples = samples; + + { + thread_scoped_lock pause_lock(pause_mutex_); + new_work_added_ = true; + } + + pause_cond_.notify_all(); +} + +void Session::set_time_limit(double time_limit) +{ + if (time_limit == params.time_limit) { + return; + } + + params.time_limit = time_limit; + + { + thread_scoped_lock pause_lock(pause_mutex_); + new_work_added_ = true; + } + + pause_cond_.notify_all(); +} + +void Session::set_pause(bool pause) +{ + bool notify = false; + + { + thread_scoped_lock pause_lock(pause_mutex_); + + if (pause != pause_) { + pause_ = pause; + notify = true; + } + } + + if (session_thread_) { + if (notify) { + pause_cond_.notify_all(); + } + } + else if (pause_) { + update_status_time(pause_); + } +} + +void Session::set_output_driver(unique_ptr driver) +{ + path_trace_->set_output_driver(move(driver)); +} + +void Session::set_display_driver(unique_ptr driver) +{ + path_trace_->set_display_driver(move(driver)); +} + +double Session::get_estimated_remaining_time() const +{ + const float completed = progress.get_progress(); + if (completed == 0.0f) { + return 0.0; + } + + double total_time, render_time; + progress.get_time(total_time, render_time); + double remaining = (1.0 - (double)completed) * (render_time / (double)completed); + + const double time_limit = render_scheduler_.get_time_limit(); + if (time_limit != 0.0) { + remaining = min(remaining, max(time_limit - render_time, 0.0)); + } + + return remaining; +} + +void Session::wait() +{ + if (session_thread_) { + session_thread_->join(); + delete session_thread_; + } + + session_thread_ = nullptr; +} + +bool Session::update_scene(int width, int height) +{ + /* Update camera if dimensions changed for progressive render. the camera + * knows nothing about progressive or cropped rendering, it just gets the + * image dimensions passed in. */ + Camera *cam = scene->camera; + cam->set_screen_size(width, height); + + const bool scene_update_result = scene->update(progress); + + path_trace_->load_kernels(); + path_trace_->alloc_work_memory(); + + return scene_update_result; +} + +static string status_append(const string &status, const string &suffix) +{ + string prefix = status; + if (!prefix.empty()) { + prefix += ", "; + } + return prefix + suffix; +} + +void Session::update_status_time(bool show_pause, bool show_done) +{ + string status, substatus; + + const int current_tile = progress.get_rendered_tiles(); + const int num_tiles = tile_manager_.get_num_tiles(); + + const int current_sample = progress.get_current_sample(); + const int num_samples = render_scheduler_.get_num_samples(); + + /* TIle. */ + if (tile_manager_.has_multiple_tiles()) { + substatus = status_append(substatus, + string_printf("Rendered %d/%d Tiles", current_tile, num_tiles)); + } + + /* Sample. */ + if (num_samples == Integrator::MAX_SAMPLES) { + substatus = status_append(substatus, string_printf("Sample %d", current_sample)); + } + else { + substatus = status_append(substatus, + string_printf("Sample %d/%d", current_sample, num_samples)); + } + + /* TODO(sergey): Denoising status from the path trace. */ + + if (show_pause) { + status = "Rendering Paused"; + } + else if (show_done) { + status = "Rendering Done"; + progress.set_end_time(); /* Save end time so that further calls to get_time are accurate. */ + } + else { + status = substatus; + substatus.clear(); + } + + progress.set_status(status, substatus); +} + +void Session::device_free() +{ + scene->device_free(); + path_trace_->device_free(); +} + +void Session::collect_statistics(RenderStats *render_stats) +{ + scene->collect_statistics(render_stats); + if (params.use_profiling && (params.device.type == DEVICE_CPU)) { + render_stats->collect_profiling(scene, profiler); + } +} + +/* -------------------------------------------------------------------- + * Full-frame on-disk storage. + */ + +void Session::process_full_buffer_from_disk(string_view filename) +{ + path_trace_->process_full_buffer_from_disk(filename); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/session/session.h b/intern/cycles/session/session.h new file mode 100644 index 00000000000..1ec0c6e9bb1 --- /dev/null +++ b/intern/cycles/session/session.h @@ -0,0 +1,229 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __SESSION_H__ +#define __SESSION_H__ + +#include "device/device.h" +#include "integrator/render_scheduler.h" +#include "scene/shader.h" +#include "scene/stats.h" +#include "session/buffers.h" +#include "session/tile.h" + +#include "util/progress.h" +#include "util/stats.h" +#include "util/thread.h" +#include "util/unique_ptr.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +class BufferParams; +class Device; +class DeviceScene; +class DisplayDriver; +class OutputDriver; +class PathTrace; +class Progress; +class RenderBuffers; +class Scene; +class SceneParams; + +/* Session Parameters */ + +class SessionParams { + public: + DeviceInfo device; + + bool headless; + bool background; + + bool experimental; + int samples; + int pixel_size; + int threads; + + /* Limit in seconds for how long path tracing is allowed to happen. + * Zero means no limit is applied. */ + double time_limit; + + bool use_profiling; + + bool use_auto_tile; + int tile_size; + + ShadingSystem shadingsystem; + + SessionParams() + { + headless = false; + background = false; + + experimental = false; + samples = 1024; + pixel_size = 1; + threads = 0; + time_limit = 0.0; + + use_profiling = false; + + use_auto_tile = true; + tile_size = 2048; + + shadingsystem = SHADINGSYSTEM_SVM; + } + + bool modified(const SessionParams ¶ms) const + { + /* Modified means we have to recreate the session, any parameter changes + * that can be handled by an existing Session are omitted. */ + return !(device == params.device && headless == params.headless && + background == params.background && experimental == params.experimental && + pixel_size == params.pixel_size && threads == params.threads && + use_profiling == params.use_profiling && shadingsystem == params.shadingsystem && + use_auto_tile == params.use_auto_tile && tile_size == params.tile_size); + } +}; + +/* Session + * + * This is the class that contains the session thread, running the render + * control loop and dispatching tasks. */ + +class Session { + public: + Device *device; + Scene *scene; + Progress progress; + SessionParams params; + Stats stats; + Profiler profiler; + + /* Callback is invoked by tile manager whenever on-dist tiles storage file is closed after + * writing. Allows an engine integration to keep track of those files without worry about + * transferring the information when it needs to re-create session during rendering. */ + function full_buffer_written_cb; + + explicit Session(const SessionParams ¶ms, const SceneParams &scene_params); + ~Session(); + + void start(); + + /* When quick cancel is requested path tracing is cancels as soon as possible, without waiting + * for the buffer to be uniformly sampled. */ + void cancel(bool quick = false); + + void draw(); + void wait(); + + bool ready_to_reset(); + void reset(const SessionParams &session_params, const BufferParams &buffer_params); + + void set_pause(bool pause); + + void set_samples(int samples); + void set_time_limit(double time_limit); + + void set_output_driver(unique_ptr driver); + void set_display_driver(unique_ptr driver); + + double get_estimated_remaining_time() const; + + void device_free(); + + /* Returns the rendering progress or 0 if no progress can be determined + * (for example, when rendering with unlimited samples). */ + float get_progress(); + + void collect_statistics(RenderStats *stats); + + /* -------------------------------------------------------------------- + * Full-frame on-disk storage. + */ + + /* Read given full-frame file from disk, perform needed processing and write it to the software + * via the write callback. */ + void process_full_buffer_from_disk(string_view filename); + + protected: + struct DelayedReset { + thread_mutex mutex; + bool do_reset; + SessionParams session_params; + BufferParams buffer_params; + } delayed_reset_; + + void run(); + + /* Update for the new iteration of the main loop in run implementation (run_cpu and run_gpu). + * + * Will take care of the following things: + * - Delayed reset + * - Scene update + * - Tile manager advance + * - Render scheduler work request + * + * The updates are done in a proper order with proper locking around them, which guarantees + * that the device side of scene and render buffers are always in a consistent state. + * + * Returns render work which is to be rendered next. */ + RenderWork run_update_for_next_iteration(); + + /* Wait for rendering to be unpaused, or for new tiles for render to arrive. + * Returns true if new main render loop iteration is required after this function call. + * + * The `render_work` is the work which was scheduled by the render scheduler right before + * checking the pause. */ + bool run_wait_for_work(const RenderWork &render_work); + + void run_main_render_loop(); + + bool update_scene(int width, int height); + + void update_status_time(bool show_pause = false, bool show_done = false); + + void do_delayed_reset(); + + int2 get_effective_tile_size() const; + + thread *session_thread_; + + bool pause_ = false; + bool cancel_ = false; + bool new_work_added_ = false; + + thread_condition_variable pause_cond_; + thread_mutex pause_mutex_; + thread_mutex tile_mutex_; + thread_mutex buffers_mutex_; + + TileManager tile_manager_; + BufferParams buffer_params_; + + /* Render scheduler is used to get work to be rendered with the current big tile. */ + RenderScheduler render_scheduler_; + + /* Path tracer object. + * + * Is a single full-frame path tracer for interactive viewport rendering. + * A path tracer for the current big-tile for an offline rendering. */ + unique_ptr path_trace_; +}; + +CCL_NAMESPACE_END + +#endif /* __SESSION_H__ */ diff --git a/intern/cycles/session/tile.cpp b/intern/cycles/session/tile.cpp new file mode 100644 index 00000000000..56bc519378f --- /dev/null +++ b/intern/cycles/session/tile.cpp @@ -0,0 +1,635 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "session/tile.h" + +#include + +#include "graph/node.h" +#include "scene/background.h" +#include "scene/film.h" +#include "scene/integrator.h" +#include "scene/scene.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/path.h" +#include "util/string.h" +#include "util/system.h" +#include "util/types.h" + +CCL_NAMESPACE_BEGIN + +/* -------------------------------------------------------------------- + * Internal functions. + */ + +static const char *ATTR_PASSES_COUNT = "cycles.passes.count"; +static const char *ATTR_PASS_SOCKET_PREFIX_FORMAT = "cycles.passes.%d."; +static const char *ATTR_BUFFER_SOCKET_PREFIX = "cycles.buffer."; +static const char *ATTR_DENOISE_SOCKET_PREFIX = "cycles.denoise."; + +/* Global counter of ToleManager object instances. */ +static std::atomic g_instance_index = 0; + +/* Construct names of EXR channels which will ensure order of all channels to match exact offsets + * in render buffers corresponding to the given passes. + * + * Returns `std` datatypes so that it can be assigned directly to the OIIO's `ImageSpec`. */ +static std::vector exr_channel_names_for_passes(const BufferParams &buffer_params) +{ + static const char *component_suffixes[] = {"R", "G", "B", "A"}; + + int pass_index = 0; + int num_channels = 0; + std::vector channel_names; + for (const BufferPass &pass : buffer_params.passes) { + if (pass.offset == PASS_UNUSED) { + continue; + } + + const PassInfo pass_info = pass.get_info(); + num_channels += pass_info.num_components; + + /* EXR canonically expects first part of channel names to be sorted alphabetically, which is + * not guaranteed to be the case with passes names. Assign a prefix based on the pass index + * with a fixed width to ensure ordering. This makes it possible to dump existing render + * buffers memory to disk and read it back without doing extra mapping. */ + const string prefix = string_printf("%08d", pass_index); + + const string channel_name_prefix = prefix + string(pass.name) + "."; + + for (int i = 0; i < pass_info.num_components; ++i) { + channel_names.push_back(channel_name_prefix + component_suffixes[i]); + } + + ++pass_index; + } + + return channel_names; +} + +inline string node_socket_attribute_name(const SocketType &socket, const string &attr_name_prefix) +{ + return attr_name_prefix + string(socket.name); +} + +template +static bool node_socket_generic_to_image_spec_atttributes( + ImageSpec *image_spec, + const Node *node, + const SocketType &socket, + const string &attr_name_prefix, + const ValidateValueFunc &validate_value_func, + const GetValueFunc &get_value_func) +{ + if (!validate_value_func(node, socket)) { + return false; + } + + image_spec->attribute(node_socket_attribute_name(socket, attr_name_prefix), + get_value_func(node, socket)); + + return true; +} + +static bool node_socket_to_image_spec_atttributes(ImageSpec *image_spec, + const Node *node, + const SocketType &socket, + const string &attr_name_prefix) +{ + const string attr_name = node_socket_attribute_name(socket, attr_name_prefix); + + switch (socket.type) { + case SocketType::ENUM: { + const ustring value = node->get_string(socket); + + /* Validate that the node is consistent with the node type definition. */ + const NodeEnum &enum_values = *socket.enum_values; + if (!enum_values.exists(value)) { + LOG(DFATAL) << "Node enum contains invalid value " << value; + return false; + } + + image_spec->attribute(attr_name, value); + + return true; + } + + case SocketType::STRING: + image_spec->attribute(attr_name, node->get_string(socket)); + return true; + + case SocketType::INT: + image_spec->attribute(attr_name, node->get_int(socket)); + return true; + + case SocketType::FLOAT: + image_spec->attribute(attr_name, node->get_float(socket)); + return true; + + case SocketType::BOOLEAN: + image_spec->attribute(attr_name, node->get_bool(socket)); + return true; + + default: + LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen."; + return false; + } +} + +static bool node_socket_from_image_spec_atttributes(Node *node, + const SocketType &socket, + const ImageSpec &image_spec, + const string &attr_name_prefix) +{ + const string attr_name = node_socket_attribute_name(socket, attr_name_prefix); + + switch (socket.type) { + case SocketType::ENUM: { + /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */ + const ustring value(image_spec.get_string_attribute(attr_name, "")); + + /* Validate that the node is consistent with the node type definition. */ + const NodeEnum &enum_values = *socket.enum_values; + if (!enum_values.exists(value)) { + LOG(ERROR) << "Invalid enumerator value " << value; + return false; + } + + node->set(socket, enum_values[value]); + + return true; + } + + case SocketType::STRING: + /* TODO(sergey): Avoid construction of `ustring` by using `string_view` in the Node API. */ + node->set(socket, ustring(image_spec.get_string_attribute(attr_name, ""))); + return true; + + case SocketType::INT: + node->set(socket, image_spec.get_int_attribute(attr_name, 0)); + return true; + + case SocketType::FLOAT: + node->set(socket, image_spec.get_float_attribute(attr_name, 0)); + return true; + + case SocketType::BOOLEAN: + node->set(socket, static_cast(image_spec.get_int_attribute(attr_name, 0))); + return true; + + default: + LOG(DFATAL) << "Unhandled socket type " << socket.type << ", should never happen."; + return false; + } +} + +static bool node_to_image_spec_atttributes(ImageSpec *image_spec, + const Node *node, + const string &attr_name_prefix) +{ + for (const SocketType &socket : node->type->inputs) { + if (!node_socket_to_image_spec_atttributes(image_spec, node, socket, attr_name_prefix)) { + return false; + } + } + + return true; +} + +static bool node_from_image_spec_atttributes(Node *node, + const ImageSpec &image_spec, + const string &attr_name_prefix) +{ + for (const SocketType &socket : node->type->inputs) { + if (!node_socket_from_image_spec_atttributes(node, socket, image_spec, attr_name_prefix)) { + return false; + } + } + + return true; +} + +static bool buffer_params_to_image_spec_atttributes(ImageSpec *image_spec, + const BufferParams &buffer_params) +{ + if (!node_to_image_spec_atttributes(image_spec, &buffer_params, ATTR_BUFFER_SOCKET_PREFIX)) { + return false; + } + + /* Passes storage is not covered by the node socket. so "expand" the loop manually. */ + + const int num_passes = buffer_params.passes.size(); + image_spec->attribute(ATTR_PASSES_COUNT, num_passes); + + for (int pass_index = 0; pass_index < num_passes; ++pass_index) { + const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index); + + const BufferPass *pass = &buffer_params.passes[pass_index]; + if (!node_to_image_spec_atttributes(image_spec, pass, attr_name_prefix)) { + return false; + } + } + + return true; +} + +static bool buffer_params_from_image_spec_atttributes(BufferParams *buffer_params, + const ImageSpec &image_spec) +{ + if (!node_from_image_spec_atttributes(buffer_params, image_spec, ATTR_BUFFER_SOCKET_PREFIX)) { + return false; + } + + /* Passes storage is not covered by the node socket. so "expand" the loop manually. */ + + const int num_passes = image_spec.get_int_attribute(ATTR_PASSES_COUNT, 0); + if (num_passes == 0) { + LOG(ERROR) << "Missing passes count attribute."; + return false; + } + + for (int pass_index = 0; pass_index < num_passes; ++pass_index) { + const string attr_name_prefix = string_printf(ATTR_PASS_SOCKET_PREFIX_FORMAT, pass_index); + + BufferPass pass; + + if (!node_from_image_spec_atttributes(&pass, image_spec, attr_name_prefix)) { + return false; + } + + buffer_params->passes.emplace_back(std::move(pass)); + } + + buffer_params->update_passes(); + + return true; +} + +/* Configure image specification for the given buffer parameters and passes. + * + * Image channels will be strictly ordered to match content of corresponding buffer, and the + * metadata will be set so that the render buffers and passes can be reconstructed from it. + * + * If the tile size different from (0, 0) the image specification will be configured to use the + * given tile size for tiled IO. */ +static bool configure_image_spec_from_buffer(ImageSpec *image_spec, + const BufferParams &buffer_params, + const int2 tile_size = make_int2(0, 0)) +{ + const std::vector channel_names = exr_channel_names_for_passes(buffer_params); + const int num_channels = channel_names.size(); + + *image_spec = ImageSpec( + buffer_params.width, buffer_params.height, num_channels, TypeDesc::FLOAT); + + image_spec->channelnames = move(channel_names); + + if (!buffer_params_to_image_spec_atttributes(image_spec, buffer_params)) { + return false; + } + + if (tile_size.x != 0 || tile_size.y != 0) { + DCHECK_GT(tile_size.x, 0); + DCHECK_GT(tile_size.y, 0); + + image_spec->tile_width = min(TileManager::IMAGE_TILE_SIZE, tile_size.x); + image_spec->tile_height = min(TileManager::IMAGE_TILE_SIZE, tile_size.y); + } + + return true; +} + +/* -------------------------------------------------------------------- + * Tile Manager. + */ + +TileManager::TileManager() +{ + /* Use process ID to separate different processes. + * To ensure uniqueness from within a process use combination of object address and instance + * index. This solves problem of possible object re-allocation at the same time, and solves + * possible conflict when the counter overflows while there are still active instances of the + * class. */ + const int tile_manager_id = g_instance_index.fetch_add(1, std::memory_order_relaxed); + tile_file_unique_part_ = to_string(system_self_process_id()) + "-" + + to_string(reinterpret_cast(this)) + "-" + + to_string(tile_manager_id); +} + +TileManager::~TileManager() +{ +} + +int TileManager::compute_render_tile_size(const int suggested_tile_size) const +{ + /* Must be a multiple of IMAGE_TILE_SIZE so that we can write render tiles into the image file + * aligned on image tile boundaries. We can't set IMAGE_TILE_SIZE equal to the render tile size + * because too big tile size leads to integer overflow inside OpenEXR. */ + return (suggested_tile_size <= IMAGE_TILE_SIZE) ? suggested_tile_size : + align_up(suggested_tile_size, IMAGE_TILE_SIZE); +} + +void TileManager::reset_scheduling(const BufferParams ¶ms, int2 tile_size) +{ + VLOG(3) << "Using tile size of " << tile_size; + + close_tile_output(); + + tile_size_ = tile_size; + + tile_state_.num_tiles_x = divide_up(params.width, tile_size_.x); + tile_state_.num_tiles_y = divide_up(params.height, tile_size_.y); + tile_state_.num_tiles = tile_state_.num_tiles_x * tile_state_.num_tiles_y; + + tile_state_.next_tile_index = 0; + + tile_state_.current_tile = Tile(); +} + +void TileManager::update(const BufferParams ¶ms, const Scene *scene) +{ + DCHECK_NE(params.pass_stride, -1); + + buffer_params_ = params; + + if (has_multiple_tiles()) { + /* TODO(sergey): Proper Error handling, so that if configuration has failed we don't attempt to + * write to a partially configured file. */ + configure_image_spec_from_buffer(&write_state_.image_spec, buffer_params_, tile_size_); + + const DenoiseParams denoise_params = scene->integrator->get_denoise_params(); + const AdaptiveSampling adaptive_sampling = scene->integrator->get_adaptive_sampling(); + + node_to_image_spec_atttributes( + &write_state_.image_spec, &denoise_params, ATTR_DENOISE_SOCKET_PREFIX); + + if (adaptive_sampling.use) { + overscan_ = 4; + } + else { + overscan_ = 0; + } + } + else { + write_state_.image_spec = ImageSpec(); + overscan_ = 0; + } +} + +bool TileManager::done() +{ + return tile_state_.next_tile_index == tile_state_.num_tiles; +} + +bool TileManager::next() +{ + if (done()) { + return false; + } + + tile_state_.current_tile = get_tile_for_index(tile_state_.next_tile_index); + + ++tile_state_.next_tile_index; + + return true; +} + +Tile TileManager::get_tile_for_index(int index) const +{ + /* TODO(sergey): Consider using hilbert spiral, or. maybe, even configurable. Not sure this + * brings a lot of value since this is only applicable to BIG tiles. */ + + const int tile_index_y = index / tile_state_.num_tiles_x; + const int tile_index_x = index - tile_index_y * tile_state_.num_tiles_x; + + const int tile_window_x = tile_index_x * tile_size_.x; + const int tile_window_y = tile_index_y * tile_size_.y; + + Tile tile; + + tile.x = max(0, tile_window_x - overscan_); + tile.y = max(0, tile_window_y - overscan_); + + tile.window_x = tile_window_x - tile.x; + tile.window_y = tile_window_y - tile.y; + tile.window_width = min(tile_size_.x, buffer_params_.width - tile_window_x); + tile.window_height = min(tile_size_.y, buffer_params_.height - tile_window_y); + + tile.width = min(buffer_params_.width - tile.x, tile.window_x + tile.window_width + overscan_); + tile.height = min(buffer_params_.height - tile.y, + tile.window_y + tile.window_height + overscan_); + + return tile; +} + +const Tile &TileManager::get_current_tile() const +{ + return tile_state_.current_tile; +} + +const int2 TileManager::get_size() const +{ + return make_int2(buffer_params_.width, buffer_params_.height); +} + +bool TileManager::open_tile_output() +{ + write_state_.filename = path_temp_get("cycles-tile-buffer-" + tile_file_unique_part_ + "-" + + to_string(write_state_.tile_file_index) + ".exr"); + + write_state_.tile_out = ImageOutput::create(write_state_.filename); + if (!write_state_.tile_out) { + LOG(ERROR) << "Error creating image output for " << write_state_.filename; + return false; + } + + if (!write_state_.tile_out->supports("tiles")) { + LOG(ERROR) << "Progress tile file format does not support tiling."; + return false; + } + + if (!write_state_.tile_out->open(write_state_.filename, write_state_.image_spec)) { + LOG(ERROR) << "Error opening tile file: " << write_state_.tile_out->geterror(); + write_state_.tile_out = nullptr; + return false; + } + + write_state_.num_tiles_written = 0; + + VLOG(3) << "Opened tile file " << write_state_.filename; + + return true; +} + +bool TileManager::close_tile_output() +{ + if (!write_state_.tile_out) { + return true; + } + + const bool success = write_state_.tile_out->close(); + write_state_.tile_out = nullptr; + + if (!success) { + LOG(ERROR) << "Error closing tile file."; + return false; + } + + VLOG(3) << "Tile output is closed."; + + return true; +} + +bool TileManager::write_tile(const RenderBuffers &tile_buffers) +{ + if (!write_state_.tile_out) { + if (!open_tile_output()) { + return false; + } + } + + DCHECK_EQ(tile_buffers.params.pass_stride, buffer_params_.pass_stride); + + vector pixel_storage; + + const BufferParams &tile_params = tile_buffers.params; + + const int tile_x = tile_params.full_x - buffer_params_.full_x + tile_params.window_x; + const int tile_y = tile_params.full_y - buffer_params_.full_y + tile_params.window_y; + + const int64_t pass_stride = tile_params.pass_stride; + const int64_t tile_row_stride = tile_params.width * pass_stride; + + const int64_t xstride = pass_stride * sizeof(float); + const int64_t ystride = xstride * tile_params.width; + const int64_t zstride = ystride * tile_params.height; + + const float *pixels = tile_buffers.buffer.data() + tile_params.window_x * pass_stride + + tile_params.window_y * tile_row_stride; + + VLOG(3) << "Write tile at " << tile_x << ", " << tile_y; + + /* The image tile sizes in the OpenEXR file are different from the size of our big tiles. The + * write_tiles() method expects a contiguous image region that will be split into tiles + * internally. OpenEXR expects the size of this region to be a multiple of the tile size, + * however OpenImageIO automatically adds the required padding. + * + * The only thing we have to ensure is that the tile_x and tile_y are a multiple of the + * image tile size, which happens in compute_render_tile_size. */ + if (!write_state_.tile_out->write_tiles(tile_x, + tile_x + tile_params.window_width, + tile_y, + tile_y + tile_params.window_height, + 0, + 1, + TypeDesc::FLOAT, + pixels, + xstride, + ystride, + zstride)) { + LOG(ERROR) << "Error writing tile " << write_state_.tile_out->geterror(); + return false; + } + + ++write_state_.num_tiles_written; + + return true; +} + +void TileManager::finish_write_tiles() +{ + if (!write_state_.tile_out) { + /* None of the tiles were written hence the file was not created. + * Avoid creation of fully empty file since it is redundant. */ + return; + } + + /* EXR expects all tiles to present in file. So explicitly write missing tiles as all-zero. */ + if (write_state_.num_tiles_written < tile_state_.num_tiles) { + vector pixel_storage(tile_size_.x * tile_size_.y * buffer_params_.pass_stride); + + for (int tile_index = write_state_.num_tiles_written; tile_index < tile_state_.num_tiles; + ++tile_index) { + const Tile tile = get_tile_for_index(tile_index); + + const int tile_x = tile.x + tile.window_x; + const int tile_y = tile.y + tile.window_y; + + VLOG(3) << "Write dummy tile at " << tile_x << ", " << tile_y; + + write_state_.tile_out->write_tiles(tile_x, + tile_x + tile.window_width, + tile_y, + tile_y + tile.window_height, + 0, + 1, + TypeDesc::FLOAT, + pixel_storage.data()); + } + } + + close_tile_output(); + + if (full_buffer_written_cb) { + full_buffer_written_cb(write_state_.filename); + } + + /* Advance the counter upon explicit finish of the file. + * Makes it possible to re-use tile manager for another scene, and avoids unnecessary increments + * of the tile-file-within-session index. */ + ++write_state_.tile_file_index; + + write_state_.filename = ""; +} + +bool TileManager::read_full_buffer_from_disk(const string_view filename, + RenderBuffers *buffers, + DenoiseParams *denoise_params) +{ + unique_ptr in(ImageInput::open(filename)); + if (!in) { + LOG(ERROR) << "Error opening tile file " << filename; + return false; + } + + const ImageSpec &image_spec = in->spec(); + + BufferParams buffer_params; + if (!buffer_params_from_image_spec_atttributes(&buffer_params, image_spec)) { + return false; + } + buffers->reset(buffer_params); + + if (!node_from_image_spec_atttributes(denoise_params, image_spec, ATTR_DENOISE_SOCKET_PREFIX)) { + return false; + } + + if (!in->read_image(TypeDesc::FLOAT, buffers->buffer.data())) { + LOG(ERROR) << "Error reading pixels from the tile file " << in->geterror(); + return false; + } + + if (!in->close()) { + LOG(ERROR) << "Error closing tile file " << in->geterror(); + return false; + } + + return true; +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/session/tile.h b/intern/cycles/session/tile.h new file mode 100644 index 00000000000..eace148eb0a --- /dev/null +++ b/intern/cycles/session/tile.h @@ -0,0 +1,182 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "session/buffers.h" +#include "util/image.h" +#include "util/string.h" +#include "util/unique_ptr.h" + +CCL_NAMESPACE_BEGIN + +class DenoiseParams; +class Scene; + +/* -------------------------------------------------------------------- + * Tile. + */ + +class Tile { + public: + int x = 0, y = 0; + int width = 0, height = 0; + + int window_x = 0, window_y = 0; + int window_width = 0, window_height = 0; + + Tile() + { + } +}; + +/* -------------------------------------------------------------------- + * Tile Manager. + */ + +class TileManager { + public: + /* This callback is invoked by whenever on-dist tiles storage file is closed after writing. */ + function full_buffer_written_cb; + + TileManager(); + ~TileManager(); + + TileManager(const TileManager &other) = delete; + TileManager(TileManager &&other) noexcept = delete; + TileManager &operator=(const TileManager &other) = delete; + TileManager &operator=(TileManager &&other) = delete; + + /* Reset current progress and start new rendering of the full-frame parameters in tiles of the + * given size. + * Only touches scheduling-related state of the tile manager. */ + /* TODO(sergey): Consider using tile area instead of exact size to help dealing with extreme + * cases of stretched renders. */ + void reset_scheduling(const BufferParams ¶ms, int2 tile_size); + + /* Update for the known buffer passes and scene parameters. + * Will store all parameters needed for buffers access outside of the scene graph. */ + void update(const BufferParams ¶ms, const Scene *scene); + + inline int get_num_tiles() const + { + return tile_state_.num_tiles; + } + + inline bool has_multiple_tiles() const + { + return tile_state_.num_tiles > 1; + } + + inline int get_tile_overscan() const + { + return overscan_; + } + + bool next(); + bool done(); + + const Tile &get_current_tile() const; + const int2 get_size() const; + + /* Write render buffer of a tile to a file on disk. + * + * Opens file for write when first tile is written. + * + * Returns true on success. */ + bool write_tile(const RenderBuffers &tile_buffers); + + /* Inform the tile manager that no more tiles will be written to disk. + * The file will be considered final, all handles to it will be closed. */ + void finish_write_tiles(); + + /* Check whether any tile has been written to disk. */ + inline bool has_written_tiles() const + { + return write_state_.num_tiles_written != 0; + } + + /* Read full frame render buffer from tiles file on disk. + * + * Returns true on success. */ + bool read_full_buffer_from_disk(string_view filename, + RenderBuffers *buffers, + DenoiseParams *denoise_params); + + /* Compute valid tile size compatible with image saving. */ + int compute_render_tile_size(const int suggested_tile_size) const; + + /* Tile size in the image file. */ + static const int IMAGE_TILE_SIZE = 128; + + protected: + /* Get tile configuration for its index. + * The tile index must be within [0, state_.tile_state_). */ + Tile get_tile_for_index(int index) const; + + bool open_tile_output(); + bool close_tile_output(); + + /* Part of an on-disk tile file name which avoids conflicts between several Cycles instances or + * several sessions. */ + string tile_file_unique_part_; + + int2 tile_size_ = make_int2(0, 0); + + /* Number of extra pixels around the actual tile to render. */ + int overscan_ = 0; + + BufferParams buffer_params_; + + /* Tile scheduling state. */ + struct { + int num_tiles_x = 0; + int num_tiles_y = 0; + int num_tiles = 0; + + int next_tile_index; + + Tile current_tile; + } tile_state_; + + /* State of tiles writing to a file on disk. */ + struct { + /* Index of a tile file used during the current session. + * This number is used for the file name construction, making it possible to render several + * scenes throughout duration of the session and keep all results available for later read + * access. */ + int tile_file_index = 0; + + string filename; + + /* Specification of the tile image which corresponds to the buffer parameters. + * Contains channels configured according to the passes configuration in the path traces. + * + * Output images are saved using this specification, input images are expected to have matched + * specification. */ + ImageSpec image_spec; + + /* Output handle for the tile file. + * + * This file can not be closed until all tiles has been provided, so the handle is stored in + * the state and is created whenever writing is requested. */ + unique_ptr tile_out; + + int num_tiles_written = 0; + } write_state_; +}; + +CCL_NAMESPACE_END diff --git a/intern/cycles/subd/CMakeLists.txt b/intern/cycles/subd/CMakeLists.txt index c697ddb9891..4bf5503dc4b 100644 --- a/intern/cycles/subd/CMakeLists.txt +++ b/intern/cycles/subd/CMakeLists.txt @@ -21,18 +21,18 @@ set(INC_SYS ) set(SRC - subd_dice.cpp - subd_patch.cpp - subd_split.cpp - subd_patch_table.cpp + dice.cpp + patch.cpp + split.cpp + patch_table.cpp ) set(SRC_HEADERS - subd_dice.h - subd_patch.h - subd_patch_table.h - subd_split.h - subd_subpatch.h + dice.h + patch.h + patch_table.h + split.h + subpatch.h ) set(LIB diff --git a/intern/cycles/subd/subd_dice.cpp b/intern/cycles/subd/dice.cpp similarity index 98% rename from intern/cycles/subd/subd_dice.cpp rename to intern/cycles/subd/dice.cpp index 4efdb98aa0f..461fa0bcd9c 100644 --- a/intern/cycles/subd/subd_dice.cpp +++ b/intern/cycles/subd/dice.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/mesh.h" +#include "scene/camera.h" +#include "scene/mesh.h" -#include "subd/subd_dice.h" -#include "subd/subd_patch.h" +#include "subd/dice.h" +#include "subd/patch.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_dice.h b/intern/cycles/subd/dice.h similarity index 96% rename from intern/cycles/subd/subd_dice.h rename to intern/cycles/subd/dice.h index ee63403d40c..7510aae775c 100644 --- a/intern/cycles/subd/subd_dice.h +++ b/intern/cycles/subd/dice.h @@ -22,10 +22,10 @@ * DiagSplit. For more algorithm details, see the DiagSplit paper or the * ARB_tessellation_shader OpenGL extension, Section 2.X.2. */ -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/types.h" +#include "util/vector.h" -#include "subd/subd_subpatch.h" +#include "subd/subpatch.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_patch.cpp b/intern/cycles/subd/patch.cpp similarity index 96% rename from intern/cycles/subd/subd_patch.cpp rename to intern/cycles/subd/patch.cpp index a975b7b6342..4d73f334c1b 100644 --- a/intern/cycles/subd/subd_patch.cpp +++ b/intern/cycles/subd/patch.cpp @@ -16,12 +16,12 @@ /* Parts adapted from code in the public domain in NVidia Mesh Tools. */ -#include "render/mesh.h" +#include "scene/mesh.h" -#include "subd/subd_patch.h" +#include "subd/patch.h" -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_patch.h b/intern/cycles/subd/patch.h similarity index 95% rename from intern/cycles/subd/subd_patch.h rename to intern/cycles/subd/patch.h index 8fe423bc94d..ad4dc1bd8e9 100644 --- a/intern/cycles/subd/subd_patch.h +++ b/intern/cycles/subd/patch.h @@ -17,8 +17,8 @@ #ifndef __SUBD_PATCH_H__ #define __SUBD_PATCH_H__ -#include "util/util_boundbox.h" -#include "util/util_types.h" +#include "util/boundbox.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_patch_table.cpp b/intern/cycles/subd/patch_table.cpp similarity index 98% rename from intern/cycles/subd/subd_patch_table.cpp rename to intern/cycles/subd/patch_table.cpp index 4e873375725..d215dfaa1dd 100644 --- a/intern/cycles/subd/subd_patch_table.cpp +++ b/intern/cycles/subd/patch_table.cpp @@ -24,10 +24,10 @@ * language governing permissions and limitations under the Apache License. */ -#include "subd/subd_patch_table.h" -#include "kernel/kernel_types.h" +#include "subd/patch_table.h" +#include "kernel/types.h" -#include "util/util_math.h" +#include "util/math.h" #ifdef WITH_OPENSUBDIV # include diff --git a/intern/cycles/subd/subd_patch_table.h b/intern/cycles/subd/patch_table.h similarity index 96% rename from intern/cycles/subd/subd_patch_table.h rename to intern/cycles/subd/patch_table.h index 118d410f8f0..b5fd5923f31 100644 --- a/intern/cycles/subd/subd_patch_table.h +++ b/intern/cycles/subd/patch_table.h @@ -17,8 +17,8 @@ #ifndef __SUBD_PATCH_TABLE_H__ #define __SUBD_PATCH_TABLE_H__ -#include "util/util_array.h" -#include "util/util_types.h" +#include "util/array.h" +#include "util/types.h" #ifdef WITH_OPENSUBDIV # ifdef _MSC_VER diff --git a/intern/cycles/subd/subd_split.cpp b/intern/cycles/subd/split.cpp similarity index 98% rename from intern/cycles/subd/subd_split.cpp rename to intern/cycles/subd/split.cpp index 4d648eb1f77..2b29f3a5a78 100644 --- a/intern/cycles/subd/subd_split.cpp +++ b/intern/cycles/subd/split.cpp @@ -14,18 +14,18 @@ * limitations under the License. */ -#include "render/camera.h" -#include "render/mesh.h" +#include "scene/camera.h" +#include "scene/mesh.h" -#include "subd/subd_dice.h" -#include "subd/subd_patch.h" -#include "subd/subd_split.h" +#include "subd/dice.h" +#include "subd/patch.h" +#include "subd/split.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_hash.h" -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/hash.h" +#include "util/math.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/subd/subd_split.h b/intern/cycles/subd/split.h similarity index 93% rename from intern/cycles/subd/subd_split.h rename to intern/cycles/subd/split.h index 7416b2fbbf8..e876f34c419 100644 --- a/intern/cycles/subd/subd_split.h +++ b/intern/cycles/subd/split.h @@ -22,12 +22,12 @@ * evaluation at arbitrary points is required for this to work. See the paper * for more details. */ -#include "subd/subd_dice.h" -#include "subd/subd_subpatch.h" +#include "subd/dice.h" +#include "subd/subpatch.h" -#include "util/util_deque.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/deque.h" +#include "util/types.h" +#include "util/vector.h" #include diff --git a/intern/cycles/subd/subd_subpatch.h b/intern/cycles/subd/subpatch.h similarity index 98% rename from intern/cycles/subd/subd_subpatch.h rename to intern/cycles/subd/subpatch.h index cdaa310916a..0ba8ed88aa8 100644 --- a/intern/cycles/subd/subd_subpatch.h +++ b/intern/cycles/subd/subpatch.h @@ -17,8 +17,8 @@ #ifndef __SUBD_SUBPATCH_H__ #define __SUBD_SUBPATCH_H__ -#include "util/util_map.h" -#include "util/util_types.h" +#include "util/map.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/CMakeLists.txt b/intern/cycles/test/CMakeLists.txt index 65a692acd03..86a830b2b65 100644 --- a/intern/cycles/test/CMakeLists.txt +++ b/intern/cycles/test/CMakeLists.txt @@ -20,22 +20,18 @@ if(WITH_GTESTS) endif() set(INC - . .. - ../device - ../graph - ../kernel - ../render - ../util ) set(ALL_CYCLES_LIBRARIES - cycles_device cycles_kernel - cycles_render + cycles_integrator + cycles_scene + cycles_session cycles_bvh cycles_graph cycles_subd + cycles_device cycles_util extern_clew ${CYCLES_GL_LIBRARIES} @@ -45,8 +41,12 @@ include_directories(${INC}) cycles_link_directories() set(SRC + integrator_adaptive_sampling_test.cpp + integrator_render_scheduler_test.cpp + integrator_tile_test.cpp render_graph_finalize_test.cpp util_aligned_malloc_test.cpp + util_math_test.cpp util_path_test.cpp util_string_test.cpp util_task_test.cpp diff --git a/intern/cycles/test/integrator_adaptive_sampling_test.cpp b/intern/cycles/test/integrator_adaptive_sampling_test.cpp new file mode 100644 index 00000000000..30688605e44 --- /dev/null +++ b/intern/cycles/test/integrator_adaptive_sampling_test.cpp @@ -0,0 +1,116 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "integrator/adaptive_sampling.h" +#include "util/vector.h" + +CCL_NAMESPACE_BEGIN + +TEST(AdaptiveSampling, schedule_samples) +{ + AdaptiveSampling adaptive_sampling; + adaptive_sampling.use = true; + adaptive_sampling.min_samples = 0; + adaptive_sampling.adaptive_step = 4; + + for (int sample = 2; sample < 32; ++sample) { + for (int num_samples = 8; num_samples < 32; ++num_samples) { + const int num_samples_aligned = adaptive_sampling.align_samples(sample, num_samples); + /* NOTE: `sample + num_samples_aligned` is the number of samples after rendering, so need + * to convert this to the 0-based index of the last sample. */ + EXPECT_TRUE(adaptive_sampling.need_filter(sample + num_samples_aligned - 1)); + } + } +} + +TEST(AdaptiveSampling, align_samples) +{ + AdaptiveSampling adaptive_sampling; + adaptive_sampling.use = true; + adaptive_sampling.min_samples = 11 /* rounded of sqrt(128) */; + adaptive_sampling.adaptive_step = 4; + + /* Filtering will happen at the following samples: + * 15, 19, 23, 27, 31, 35, 39, 43 */ + + /* Requested sample and number of samples will result in number of samples lower than + * `min_samples`. */ + EXPECT_EQ(adaptive_sampling.align_samples(0, 4), 4); + EXPECT_EQ(adaptive_sampling.align_samples(0, 7), 7); + + /* Request number of samples higher than the minimum samples before filter, but prior to the + * first sample at which filtering will happen. */ + EXPECT_EQ(adaptive_sampling.align_samples(0, 15), 15); + + /* When rendering many samples from the very beginning, limit number of samples by the first + * sample at which filtering is to happen. */ + EXPECT_EQ(adaptive_sampling.align_samples(0, 16), 16); + EXPECT_EQ(adaptive_sampling.align_samples(0, 17), 16); + EXPECT_EQ(adaptive_sampling.align_samples(0, 20), 16); + EXPECT_EQ(adaptive_sampling.align_samples(0, 60), 16); + + /* Similar to above, but start sample is not 0. */ + EXPECT_EQ(adaptive_sampling.align_samples(9, 8), 7); + EXPECT_EQ(adaptive_sampling.align_samples(9, 20), 7); + EXPECT_EQ(adaptive_sampling.align_samples(9, 60), 7); + + /* Start sample is past the minimum required samples, but prior to the first filter sample. */ + EXPECT_EQ(adaptive_sampling.align_samples(12, 6), 4); + EXPECT_EQ(adaptive_sampling.align_samples(12, 20), 4); + EXPECT_EQ(adaptive_sampling.align_samples(12, 60), 4); + + /* Start sample is the sample which is to be filtered. */ + EXPECT_EQ(adaptive_sampling.align_samples(15, 4), 1); + EXPECT_EQ(adaptive_sampling.align_samples(15, 6), 1); + EXPECT_EQ(adaptive_sampling.align_samples(15, 10), 1); + EXPECT_EQ(adaptive_sampling.align_samples(58, 2), 2); + + /* Start sample is past the sample which is to be filtered. */ + EXPECT_EQ(adaptive_sampling.align_samples(16, 3), 3); + EXPECT_EQ(adaptive_sampling.align_samples(16, 4), 4); + EXPECT_EQ(adaptive_sampling.align_samples(16, 5), 4); + EXPECT_EQ(adaptive_sampling.align_samples(16, 10), 4); + + /* Should never exceed requested number of samples. */ + EXPECT_EQ(adaptive_sampling.align_samples(15, 2), 1); + EXPECT_EQ(adaptive_sampling.align_samples(16, 2), 2); + EXPECT_EQ(adaptive_sampling.align_samples(17, 2), 2); + EXPECT_EQ(adaptive_sampling.align_samples(18, 2), 2); +} + +TEST(AdaptiveSampling, need_filter) +{ + AdaptiveSampling adaptive_sampling; + adaptive_sampling.use = true; + adaptive_sampling.min_samples = 11 /* rounded of sqrt(128) */; + adaptive_sampling.adaptive_step = 4; + + const vector expected_samples_to_filter = { + {15, 19, 23, 27, 31, 35, 39, 43, 47, 51, 55, 59}}; + + vector actual_samples_to_filter; + for (int sample = 0; sample < 60; ++sample) { + if (adaptive_sampling.need_filter(sample)) { + actual_samples_to_filter.push_back(sample); + } + } + + EXPECT_EQ(actual_samples_to_filter, expected_samples_to_filter); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/integrator_render_scheduler_test.cpp b/intern/cycles/test/integrator_render_scheduler_test.cpp new file mode 100644 index 00000000000..b4efbc2d1a7 --- /dev/null +++ b/intern/cycles/test/integrator_render_scheduler_test.cpp @@ -0,0 +1,37 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "integrator/render_scheduler.h" + +CCL_NAMESPACE_BEGIN + +TEST(IntegratorRenderScheduler, calculate_resolution_divider_for_resolution) +{ + EXPECT_EQ(calculate_resolution_divider_for_resolution(1920, 1080, 1920), 1); + EXPECT_EQ(calculate_resolution_divider_for_resolution(1920, 1080, 960), 2); + EXPECT_EQ(calculate_resolution_divider_for_resolution(1920, 1080, 480), 4); +} + +TEST(IntegratorRenderScheduler, calculate_resolution_for_divider) +{ + EXPECT_EQ(calculate_resolution_for_divider(1920, 1080, 1), 1440); + EXPECT_EQ(calculate_resolution_for_divider(1920, 1080, 2), 720); + EXPECT_EQ(calculate_resolution_for_divider(1920, 1080, 4), 360); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/integrator_tile_test.cpp b/intern/cycles/test/integrator_tile_test.cpp new file mode 100644 index 00000000000..8bb0856d6a9 --- /dev/null +++ b/intern/cycles/test/integrator_tile_test.cpp @@ -0,0 +1,50 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "integrator/tile.h" +#include "util/math.h" + +CCL_NAMESPACE_BEGIN + +TEST(tile_calculate_best_size, Basic) +{ + /* Make sure CPU-like case is handled properly. */ + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1, 1.0f), TileSize(1, 1, 1)); + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1, 1.0f), TileSize(1, 1, 1)); + + /* Enough path states to fit an entire image with all samples. */ + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 1, 1920 * 1080, 1.0f), + TileSize(1920, 1080, 1)); + EXPECT_EQ(tile_calculate_best_size(make_int2(1920, 1080), 100, 1920 * 1080 * 100, 1.0f), + TileSize(1920, 1080, 100)); +} + +TEST(tile_calculate_best_size, Extreme) +{ + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 262144, 131072, 1.0f), + TileSize(1, 1, 512)); + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 1048576, 131072, 1.0f), + TileSize(1, 1, 1024)); + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 10485760, 131072, 1.0f), + TileSize(1, 1, 4096)); + + EXPECT_EQ(tile_calculate_best_size(make_int2(32, 32), 8192 * 8192 * 2, 1024, 1.0f), + TileSize(1, 1, 1024)); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/render_graph_finalize_test.cpp b/intern/cycles/test/render_graph_finalize_test.cpp index da9b29314a7..390d3a2d5f8 100644 --- a/intern/cycles/test/render_graph_finalize_test.cpp +++ b/intern/cycles/test/render_graph_finalize_test.cpp @@ -19,15 +19,15 @@ #include "device/device.h" -#include "render/graph.h" -#include "render/nodes.h" -#include "render/scene.h" +#include "scene/scene.h" +#include "scene/shader_graph.h" +#include "scene/shader_nodes.h" -#include "util/util_array.h" -#include "util/util_logging.h" -#include "util/util_stats.h" -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/array.h" +#include "util/log.h" +#include "util/stats.h" +#include "util/string.h" +#include "util/vector.h" using testing::_; using testing::AnyNumber; @@ -179,9 +179,9 @@ class RenderGraph : public testing::Test { virtual void SetUp() { util_logging_start(); - util_logging_verbosity_set(1); + util_logging_verbosity_set(3); - device_cpu = Device::create(device_info, stats, profiler, true); + device_cpu = Device::create(device_info, stats, profiler); scene = new Scene(scene_params, device_cpu); } diff --git a/intern/cycles/test/util_aligned_malloc_test.cpp b/intern/cycles/test/util_aligned_malloc_test.cpp index 8829c422a0f..2748db520eb 100644 --- a/intern/cycles/test/util_aligned_malloc_test.cpp +++ b/intern/cycles/test/util_aligned_malloc_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_aligned_malloc.h" +#include "util/aligned_malloc.h" #define CHECK_ALIGNMENT(ptr, align) EXPECT_EQ((size_t)ptr % align, 0) diff --git a/intern/cycles/test/util_avxf_test.h b/intern/cycles/test/util_avxf_test.h index 64825200c9e..b178a0450d0 100644 --- a/intern/cycles/test/util_avxf_test.h +++ b/intern/cycles/test/util_avxf_test.h @@ -15,8 +15,8 @@ */ #include "testing/testing.h" -#include "util/util_system.h" -#include "util/util_types.h" +#include "util/system.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_math_test.cpp b/intern/cycles/test/util_math_test.cpp new file mode 100644 index 00000000000..adbedf7adbe --- /dev/null +++ b/intern/cycles/test/util_math_test.cpp @@ -0,0 +1,61 @@ +/* + * Copyright 2011-2021 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "testing/testing.h" + +#include "util/math.h" + +CCL_NAMESPACE_BEGIN + +TEST(math, next_power_of_two) +{ + EXPECT_EQ(next_power_of_two(0), 1); + EXPECT_EQ(next_power_of_two(1), 2); + EXPECT_EQ(next_power_of_two(2), 4); + EXPECT_EQ(next_power_of_two(3), 4); + EXPECT_EQ(next_power_of_two(4), 8); +} + +TEST(math, prev_power_of_two) +{ + EXPECT_EQ(prev_power_of_two(0), 0); + + EXPECT_EQ(prev_power_of_two(1), 1); + EXPECT_EQ(prev_power_of_two(2), 1); + + EXPECT_EQ(prev_power_of_two(3), 2); + EXPECT_EQ(prev_power_of_two(4), 2); + + EXPECT_EQ(prev_power_of_two(5), 4); + EXPECT_EQ(prev_power_of_two(6), 4); + EXPECT_EQ(prev_power_of_two(7), 4); + EXPECT_EQ(prev_power_of_two(8), 4); +} + +TEST(math, reverse_integer_bits) +{ + EXPECT_EQ(reverse_integer_bits(0xFFFFFFFF), 0xFFFFFFFF); + EXPECT_EQ(reverse_integer_bits(0x00000000), 0x00000000); + EXPECT_EQ(reverse_integer_bits(0x1), 0x80000000); + EXPECT_EQ(reverse_integer_bits(0x80000000), 0x1); + EXPECT_EQ(reverse_integer_bits(0xFFFF0000), 0x0000FFFF); + EXPECT_EQ(reverse_integer_bits(0x0000FFFF), 0xFFFF0000); + EXPECT_EQ(reverse_integer_bits(0x00FF0000), 0x0000FF00); + EXPECT_EQ(reverse_integer_bits(0x0000FF00), 0x00FF0000); + EXPECT_EQ(reverse_integer_bits(0xAAAAAAAA), 0x55555555); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/test/util_path_test.cpp b/intern/cycles/test/util_path_test.cpp index 76d48dc241d..7afdd1150a4 100644 --- a/intern/cycles/test/util_path_test.cpp +++ b/intern/cycles/test/util_path_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_path.h" +#include "util/path.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_string_test.cpp b/intern/cycles/test/util_string_test.cpp index 97f8daa65de..f558dda9e47 100644 --- a/intern/cycles/test/util_string_test.cpp +++ b/intern/cycles/test/util_string_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_string.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN @@ -281,4 +281,40 @@ TEST(util_string_remove_trademark, r_space_middle) EXPECT_EQ(str, "foo bar baz"); } +/* ******** Tests for string_startswith() ******** */ + +TEST(string_startswith, basic) +{ + EXPECT_TRUE(string_startswith("", "")); + + EXPECT_FALSE(string_startswith("", "World")); + EXPECT_TRUE(string_startswith("Hello", "")); + + EXPECT_FALSE(string_startswith("Hello", "World")); + + EXPECT_TRUE(string_startswith("Hello", "Hello")); + EXPECT_TRUE(string_startswith("Hello", "He")); + EXPECT_TRUE(string_startswith("Hello", "H")); + + EXPECT_FALSE(string_startswith("Hello", "e")); + EXPECT_FALSE(string_startswith("Hello", "HelloWorld")); +} + +TEST(string_endswith, basic) +{ + EXPECT_TRUE(string_endswith("", "")); + + EXPECT_FALSE(string_endswith("", "World")); + EXPECT_TRUE(string_endswith("Hello", "")); + + EXPECT_FALSE(string_endswith("Hello", "World")); + + EXPECT_TRUE(string_endswith("Hello", "Hello")); + EXPECT_TRUE(string_endswith("Hello", "lo")); + EXPECT_TRUE(string_endswith("Hello", "o")); + + EXPECT_FALSE(string_endswith("Hello", "e")); + EXPECT_FALSE(string_endswith("Hello", "WorldHello")); +} + CCL_NAMESPACE_END diff --git a/intern/cycles/test/util_task_test.cpp b/intern/cycles/test/util_task_test.cpp index a8b4dfc3a37..17cfe4ff9b2 100644 --- a/intern/cycles/test/util_task_test.cpp +++ b/intern/cycles/test/util_task_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_task.h" +#include "util/task.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_time_test.cpp b/intern/cycles/test/util_time_test.cpp index ab5ead2c7b1..97a0134df67 100644 --- a/intern/cycles/test/util_time_test.cpp +++ b/intern/cycles/test/util_time_test.cpp @@ -16,7 +16,7 @@ #include "testing/testing.h" -#include "util/util_time.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/test/util_transform_test.cpp b/intern/cycles/test/util_transform_test.cpp index a5267df9fb7..11dd71ea0c2 100644 --- a/intern/cycles/test/util_transform_test.cpp +++ b/intern/cycles/test/util_transform_test.cpp @@ -16,8 +16,8 @@ #include "testing/testing.h" -#include "util/util_transform.h" -#include "util/util_vector.h" +#include "util/transform.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/CMakeLists.txt b/intern/cycles/util/CMakeLists.txt index 18e14913884..b68646a44d5 100644 --- a/intern/cycles/util/CMakeLists.txt +++ b/intern/cycles/util/CMakeLists.txt @@ -22,23 +22,23 @@ set(INC_SYS ) set(SRC - util_aligned_malloc.cpp - util_debug.cpp - util_ies.cpp - util_logging.cpp - util_math_cdf.cpp - util_md5.cpp - util_murmurhash.cpp - util_path.cpp - util_profiling.cpp - util_string.cpp - util_simd.cpp - util_system.cpp - util_task.cpp - util_thread.cpp - util_time.cpp - util_transform.cpp - util_windows.cpp + aligned_malloc.cpp + debug.cpp + ies.cpp + log.cpp + math_cdf.cpp + md5.cpp + murmurhash.cpp + path.cpp + profiling.cpp + string.cpp + simd.cpp + system.cpp + task.cpp + thread.cpp + time.cpp + transform.cpp + windows.cpp ) set(LIB @@ -48,7 +48,7 @@ set(LIB if(WITH_CYCLES_STANDALONE) if(WITH_CYCLES_STANDALONE_GUI) list(APPEND SRC - util_view.cpp + view.cpp ) endif() endif() @@ -64,108 +64,108 @@ else() endif() set(SRC_HEADERS - util_algorithm.h - util_aligned_malloc.h - util_args.h - util_array.h - util_atomic.h - util_boundbox.h - util_debug.h - util_defines.h - util_deque.h - util_disjoint_set.h - util_guarded_allocator.cpp - util_foreach.h - util_function.h - util_guarded_allocator.h - util_half.h - util_hash.h - util_ies.h - util_image.h - util_image_impl.h - util_list.h - util_logging.h - util_map.h - util_math.h - util_math_cdf.h - util_math_fast.h - util_math_intersect.h - util_math_float2.h - util_math_float3.h - util_math_float4.h - util_math_int2.h - util_math_int3.h - util_math_int4.h - util_math_matrix.h - util_md5.h - util_murmurhash.h - util_openimagedenoise.h - util_opengl.h - util_openvdb.h - util_optimization.h - util_param.h - util_path.h - util_profiling.h - util_progress.h - util_projection.h - util_queue.h - util_rect.h - util_set.h - util_simd.h - util_avxf.h - util_avxb.h - util_avxi.h - util_semaphore.h - util_sseb.h - util_ssef.h - util_ssei.h - util_stack_allocator.h - util_static_assert.h - util_stats.h - util_string.h - util_system.h - util_task.h - util_tbb.h - util_texture.h - util_thread.h - util_time.h - util_transform.h - util_types.h - util_types_float2.h - util_types_float2_impl.h - util_types_float3.h - util_types_float3_impl.h - util_types_float4.h - util_types_float4_impl.h - util_types_float8.h - util_types_float8_impl.h - util_types_int2.h - util_types_int2_impl.h - util_types_int3.h - util_types_int3_impl.h - util_types_int4.h - util_types_int4_impl.h - util_types_uchar2.h - util_types_uchar2_impl.h - util_types_uchar3.h - util_types_uchar3_impl.h - util_types_uchar4.h - util_types_uchar4_impl.h - util_types_uint2.h - util_types_uint2_impl.h - util_types_uint3.h - util_types_uint3_impl.h - util_types_uint4.h - util_types_uint4_impl.h - util_types_ushort4.h - util_types_vector3.h - util_types_vector3_impl.h - util_unique_ptr.h - util_vector.h - util_version.h - util_view.h - util_windows.h - util_xml.h + algorithm.h + aligned_malloc.h + args.h + array.h + atomic.h + boundbox.h + debug.h + defines.h + deque.h + disjoint_set.h + guarded_allocator.cpp + foreach.h + function.h + guarded_allocator.h + half.h + hash.h + ies.h + image.h + image_impl.h + list.h + log.h + map.h + math.h + math_cdf.h + math_fast.h + math_intersect.h + math_float2.h + math_float3.h + math_float4.h + math_int2.h + math_int3.h + math_int4.h + math_matrix.h + md5.h + murmurhash.h + openimagedenoise.h + opengl.h + openvdb.h + optimization.h + param.h + path.h + profiling.h + progress.h + projection.h + queue.h + rect.h + set.h + simd.h + avxf.h + avxb.h + avxi.h + semaphore.h + sseb.h + ssef.h + ssei.h + stack_allocator.h + static_assert.h + stats.h + string.h + system.h + task.h + tbb.h + texture.h + thread.h + time.h + transform.h + types.h + types_float2.h + types_float2_impl.h + types_float3.h + types_float3_impl.h + types_float4.h + types_float4_impl.h + types_float8.h + types_float8_impl.h + types_int2.h + types_int2_impl.h + types_int3.h + types_int3_impl.h + types_int4.h + types_int4_impl.h + types_uchar2.h + types_uchar2_impl.h + types_uchar3.h + types_uchar3_impl.h + types_uchar4.h + types_uchar4_impl.h + types_uint2.h + types_uint2_impl.h + types_uint3.h + types_uint3_impl.h + types_uint4.h + types_uint4_impl.h + types_ushort4.h + types_vector3.h + types_vector3_impl.h + unique_ptr.h + vector.h + version.h + view.h + windows.h + xml.h ) include_directories(${INC}) diff --git a/intern/cycles/util/util_algorithm.h b/intern/cycles/util/algorithm.h similarity index 100% rename from intern/cycles/util/util_algorithm.h rename to intern/cycles/util/algorithm.h diff --git a/intern/cycles/util/util_aligned_malloc.cpp b/intern/cycles/util/aligned_malloc.cpp similarity index 96% rename from intern/cycles/util/util_aligned_malloc.cpp rename to intern/cycles/util/aligned_malloc.cpp index 9b729cd4fc4..2b05559b55f 100644 --- a/intern/cycles/util/util_aligned_malloc.cpp +++ b/intern/cycles/util/aligned_malloc.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "util/util_aligned_malloc.h" -#include "util/util_guarded_allocator.h" +#include "util/aligned_malloc.h" +#include "util/guarded_allocator.h" #include diff --git a/intern/cycles/util/util_aligned_malloc.h b/intern/cycles/util/aligned_malloc.h similarity index 97% rename from intern/cycles/util/util_aligned_malloc.h rename to intern/cycles/util/aligned_malloc.h index df7d93c056d..66c2ac1c593 100644 --- a/intern/cycles/util/util_aligned_malloc.h +++ b/intern/cycles/util/aligned_malloc.h @@ -17,7 +17,7 @@ #ifndef __UTIL_ALIGNED_MALLOC_H__ #define __UTIL_ALIGNED_MALLOC_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_args.h b/intern/cycles/util/args.h similarity index 100% rename from intern/cycles/util/util_args.h rename to intern/cycles/util/args.h diff --git a/intern/cycles/util/util_array.h b/intern/cycles/util/array.h similarity index 97% rename from intern/cycles/util/util_array.h rename to intern/cycles/util/array.h index 73f7d6cf7f8..4c905b09138 100644 --- a/intern/cycles/util/util_array.h +++ b/intern/cycles/util/array.h @@ -20,10 +20,10 @@ #include #include -#include "util/util_aligned_malloc.h" -#include "util/util_guarded_allocator.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/aligned_malloc.h" +#include "util/guarded_allocator.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_atomic.h b/intern/cycles/util/atomic.h similarity index 57% rename from intern/cycles/util/util_atomic.h rename to intern/cycles/util/atomic.h index 13d177d2b25..faba411c769 100644 --- a/intern/cycles/util/util_atomic.h +++ b/intern/cycles/util/atomic.h @@ -34,57 +34,7 @@ #else /* __KERNEL_GPU__ */ -# ifdef __KERNEL_OPENCL__ - -/* Float atomics implementation credits: - * http://suhorukov.blogspot.in/2011/12/opencl-11-atomic-operations-on-floating.html - */ -ccl_device_inline float atomic_add_and_fetch_float(volatile ccl_global float *source, - const float operand) -{ - union { - unsigned int int_value; - float float_value; - } new_value; - union { - unsigned int int_value; - float float_value; - } prev_value; - do { - prev_value.float_value = *source; - new_value.float_value = prev_value.float_value + operand; - } while (atomic_cmpxchg((volatile ccl_global unsigned int *)source, - prev_value.int_value, - new_value.int_value) != prev_value.int_value); - return new_value.float_value; -} - -ccl_device_inline float atomic_compare_and_swap_float(volatile ccl_global float *dest, - const float old_val, - const float new_val) -{ - union { - unsigned int int_value; - float float_value; - } new_value, prev_value, result; - prev_value.float_value = old_val; - new_value.float_value = new_val; - result.int_value = atomic_cmpxchg( - (volatile ccl_global unsigned int *)dest, prev_value.int_value, new_value.int_value); - return result.float_value; -} - -# define atomic_fetch_and_add_uint32(p, x) atomic_add((p), (x)) -# define atomic_fetch_and_inc_uint32(p) atomic_inc((p)) -# define atomic_fetch_and_dec_uint32(p) atomic_dec((p)) -# define atomic_fetch_and_or_uint32(p, x) atomic_or((p), (x)) - -# define CCL_LOCAL_MEM_FENCE CLK_LOCAL_MEM_FENCE -# define ccl_barrier(flags) barrier(flags) - -# endif /* __KERNEL_OPENCL__ */ - -# ifdef __KERNEL_CUDA__ +# if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) # define atomic_add_and_fetch_float(p, x) (atomicAdd((float *)(p), (float)(x)) + (float)(x)) diff --git a/intern/cycles/util/util_avxb.h b/intern/cycles/util/avxb.h similarity index 100% rename from intern/cycles/util/util_avxb.h rename to intern/cycles/util/avxb.h diff --git a/intern/cycles/util/util_avxf.h b/intern/cycles/util/avxf.h similarity index 100% rename from intern/cycles/util/util_avxf.h rename to intern/cycles/util/avxf.h diff --git a/intern/cycles/util/util_avxi.h b/intern/cycles/util/avxi.h similarity index 100% rename from intern/cycles/util/util_avxi.h rename to intern/cycles/util/avxi.h diff --git a/intern/cycles/util/util_boundbox.h b/intern/cycles/util/boundbox.h similarity index 98% rename from intern/cycles/util/util_boundbox.h rename to intern/cycles/util/boundbox.h index 7fab7bd5a15..ed81e4cf8c3 100644 --- a/intern/cycles/util/util_boundbox.h +++ b/intern/cycles/util/boundbox.h @@ -20,10 +20,10 @@ #include #include -#include "util/util_math.h" -#include "util/util_string.h" -#include "util/util_transform.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/string.h" +#include "util/transform.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_color.h b/intern/cycles/util/color.h similarity index 97% rename from intern/cycles/util/util_color.h rename to intern/cycles/util/color.h index 7b67b90e44d..e2a5c5b9c4a 100644 --- a/intern/cycles/util/util_color.h +++ b/intern/cycles/util/color.h @@ -17,11 +17,11 @@ #ifndef __UTIL_COLOR_H__ #define __UTIL_COLOR_H__ -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/types.h" #if !defined(__KERNEL_GPU__) && defined(__KERNEL_SSE2__) -# include "util/util_simd.h" +# include "util/simd.h" #endif CCL_NAMESPACE_BEGIN @@ -277,7 +277,7 @@ ccl_device float4 color_srgb_to_linear_v4(float4 c) #endif } -ccl_device float3 color_highlight_compress(float3 color, float3 *variance) +ccl_device float3 color_highlight_compress(float3 color, ccl_private float3 *variance) { color += one_float3(); if (variance) { diff --git a/intern/cycles/util/debug.cpp b/intern/cycles/util/debug.cpp new file mode 100644 index 00000000000..7d5b6d4e54e --- /dev/null +++ b/intern/cycles/util/debug.cpp @@ -0,0 +1,102 @@ +/* + * Copyright 2011-2016 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "util/debug.h" + +#include + +#include "bvh/params.h" + +#include "util/log.h" +#include "util/string.h" + +CCL_NAMESPACE_BEGIN + +DebugFlags::CPU::CPU() + : avx2(true), avx(true), sse41(true), sse3(true), sse2(true), bvh_layout(BVH_LAYOUT_AUTO) +{ + reset(); +} + +void DebugFlags::CPU::reset() +{ +#define STRINGIFY(x) #x +#define CHECK_CPU_FLAGS(flag, env) \ + do { \ + flag = (getenv(env) == NULL); \ + if (!flag) { \ + VLOG(1) << "Disabling " << STRINGIFY(flag) << " instruction set."; \ + } \ + } while (0) + + CHECK_CPU_FLAGS(avx2, "CYCLES_CPU_NO_AVX2"); + CHECK_CPU_FLAGS(avx, "CYCLES_CPU_NO_AVX"); + CHECK_CPU_FLAGS(sse41, "CYCLES_CPU_NO_SSE41"); + CHECK_CPU_FLAGS(sse3, "CYCLES_CPU_NO_SSE3"); + CHECK_CPU_FLAGS(sse2, "CYCLES_CPU_NO_SSE2"); + +#undef STRINGIFY +#undef CHECK_CPU_FLAGS + + bvh_layout = BVH_LAYOUT_AUTO; +} + +DebugFlags::CUDA::CUDA() : adaptive_compile(false) +{ + reset(); +} + +DebugFlags::HIP::HIP() : adaptive_compile(false) +{ + reset(); +} + +void DebugFlags::CUDA::reset() +{ + if (getenv("CYCLES_CUDA_ADAPTIVE_COMPILE") != NULL) + adaptive_compile = true; +} + +void DebugFlags::HIP::reset() +{ + if (getenv("CYCLES_HIP_ADAPTIVE_COMPILE") != NULL) + adaptive_compile = true; +} + +DebugFlags::OptiX::OptiX() +{ + reset(); +} + +void DebugFlags::OptiX::reset() +{ + use_debug = false; +} + +DebugFlags::DebugFlags() : viewport_static_bvh(false), running_inside_blender(false) +{ + /* Nothing for now. */ +} + +void DebugFlags::reset() +{ + viewport_static_bvh = false; + cpu.reset(); + cuda.reset(); + optix.reset(); +} + +CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_debug.h b/intern/cycles/util/debug.h similarity index 63% rename from intern/cycles/util/util_debug.h rename to intern/cycles/util/debug.h index f7e53f90f74..548c67600e5 100644 --- a/intern/cycles/util/util_debug.h +++ b/intern/cycles/util/debug.h @@ -20,7 +20,7 @@ #include #include -#include "bvh/bvh_params.h" +#include "bvh/params.h" CCL_NAMESPACE_BEGIN @@ -79,9 +79,6 @@ class DebugFlags { * CPUs and GPUs can be selected here instead. */ BVHLayout bvh_layout; - - /* Whether split kernel is used */ - bool split_kernel; }; /* Descriptor of CUDA feature-set to be used. */ @@ -92,11 +89,19 @@ class DebugFlags { void reset(); /* Whether adaptive feature based runtime compile is enabled or not. - * Requires the CUDA Toolkit and only works on Linux atm. */ + * Requires the CUDA Toolkit and only works on Linux at the moment. */ bool adaptive_compile; + }; - /* Whether split kernel is used */ - bool split_kernel; + /* Descriptor of HIP feature-set to be used. */ + struct HIP { + HIP(); + + /* Reset flags to their defaults. */ + void reset(); + + /* Whether adaptive feature based runtime compile is enabled or not.*/ + bool adaptive_compile; }; /* Descriptor of OptiX feature-set to be used. */ @@ -106,61 +111,9 @@ class DebugFlags { /* Reset flags to their defaults. */ void reset(); - /* Number of CUDA streams to launch kernels concurrently from. */ - int cuda_streams; - - /* Use OptiX curves API for hair instead of custom implementation. */ - bool curves_api; - }; - - /* Descriptor of OpenCL feature-set to be used. */ - struct OpenCL { - OpenCL(); - - /* Reset flags to their defaults. */ - void reset(); - - /* Available device types. - * Only gives a hint which devices to let user to choose from, does not - * try to use any sort of optimal device or so. - */ - enum DeviceType { - /* None of OpenCL devices will be used. */ - DEVICE_NONE, - /* All OpenCL devices will be used. */ - DEVICE_ALL, - /* Default system OpenCL device will be used. */ - DEVICE_DEFAULT, - /* Host processor will be used. */ - DEVICE_CPU, - /* GPU devices will be used. */ - DEVICE_GPU, - /* Dedicated OpenCL accelerator device will be used. */ - DEVICE_ACCELERATOR, - }; - - /* Available kernel types. */ - enum KernelType { - /* Do automated guess which kernel to use, based on the officially - * supported GPUs and such. - */ - KERNEL_DEFAULT, - /* Force mega kernel to be used. */ - KERNEL_MEGA, - /* Force split kernel to be used. */ - KERNEL_SPLIT, - }; - - /* Requested device type. */ - DeviceType device_type; - - /* Use debug version of the kernel. */ - bool debug; - - /* TODO(mai): Currently this is only for OpenCL, but we should have it implemented for all - * devices. */ - /* Artificial memory limit in bytes (0 if disabled). */ - size_t mem_limit; + /* Load OptiX module with debug capabilities. Will lower logging verbosity level, enable + * validations, and lower optimization level. */ + bool use_debug; }; /* Get instance of debug flags registry. */ @@ -182,8 +135,8 @@ class DebugFlags { /* Requested OptiX flags. */ OptiX optix; - /* Requested OpenCL flags. */ - OpenCL opencl; + /* Requested HIP flags. */ + HIP hip; private: DebugFlags(); @@ -207,8 +160,6 @@ inline DebugFlags &DebugFlags() return DebugFlags::get(); } -std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags); - CCL_NAMESPACE_END #endif /* __UTIL_DEBUG_H__ */ diff --git a/intern/cycles/util/util_defines.h b/intern/cycles/util/defines.h similarity index 93% rename from intern/cycles/util/util_defines.h rename to intern/cycles/util/defines.h index 0a239a944a5..a778bef52b2 100644 --- a/intern/cycles/util/util_defines.h +++ b/intern/cycles/util/defines.h @@ -33,21 +33,14 @@ /* Qualifiers for kernel code shared by CPU and GPU */ #ifndef __KERNEL_GPU__ -# define ccl_device static inline -# define ccl_device_noinline static -# define ccl_device_noinline_cpu ccl_device_noinline -# define ccl_global -# define ccl_static_constant static const -# define ccl_constant const -# define ccl_local -# define ccl_local_param -# define ccl_private -# define ccl_restrict __restrict -# define ccl_ref & -# define ccl_optional_struct_init -# define ccl_loop_no_unroll -# define __KERNEL_WITH_SSE_ALIGN__ +/* Leave inlining decisions to compiler for these, the inline keyword here + * is not about performance but including function definitions in headers. */ +# define ccl_device static inline +# define ccl_device_noinline static inline +# define ccl_device_noinline_cpu ccl_device_noinline + +/* Forced inlining. */ # if defined(_WIN32) && !defined(FREE_WINDOWS) # define ccl_device_inline static __forceinline # define ccl_device_forceinline static __forceinline @@ -62,7 +55,6 @@ # define ccl_may_alias # define ccl_always_inline __forceinline # define ccl_never_inline __declspec(noinline) -# define ccl_maybe_unused # else /* _WIN32 && !FREE_WINDOWS */ # define ccl_device_inline static inline __attribute__((always_inline)) # define ccl_device_forceinline static inline __attribute__((always_inline)) @@ -74,9 +66,20 @@ # define ccl_may_alias __attribute__((__may_alias__)) # define ccl_always_inline __attribute__((always_inline)) # define ccl_never_inline __attribute__((noinline)) -# define ccl_maybe_unused __attribute__((used)) # endif /* _WIN32 && !FREE_WINDOWS */ +/* Address spaces for GPU. */ +# define ccl_global +# define ccl_static_constant static const +# define ccl_constant const +# define ccl_private + +# define ccl_restrict __restrict +# define ccl_optional_struct_init +# define ccl_loop_no_unroll +# define ccl_attr_maybe_unused [[maybe_unused]] +# define __KERNEL_WITH_SSE_ALIGN__ + /* Use to suppress '-Wimplicit-fallthrough' (in place of 'break'). */ # ifndef ATTR_FALLTHROUGH # if defined(__GNUC__) && (__GNUC__ >= 7) /* gcc7.0+ only */ diff --git a/intern/cycles/util/util_deque.h b/intern/cycles/util/deque.h similarity index 100% rename from intern/cycles/util/util_deque.h rename to intern/cycles/util/deque.h diff --git a/intern/cycles/util/util_disjoint_set.h b/intern/cycles/util/disjoint_set.h similarity index 98% rename from intern/cycles/util/util_disjoint_set.h rename to intern/cycles/util/disjoint_set.h index 946632371d2..5226423d7cd 100644 --- a/intern/cycles/util/util_disjoint_set.h +++ b/intern/cycles/util/disjoint_set.h @@ -17,7 +17,7 @@ #ifndef __UTIL_DISJOINT_SET_H__ #define __UTIL_DISJOINT_SET_H__ -#include "util_array.h" +#include "util/array.h" #include CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_foreach.h b/intern/cycles/util/foreach.h similarity index 100% rename from intern/cycles/util/util_foreach.h rename to intern/cycles/util/foreach.h diff --git a/intern/cycles/util/util_function.h b/intern/cycles/util/function.h similarity index 100% rename from intern/cycles/util/util_function.h rename to intern/cycles/util/function.h diff --git a/intern/cycles/util/util_guarded_allocator.cpp b/intern/cycles/util/guarded_allocator.cpp similarity index 93% rename from intern/cycles/util/util_guarded_allocator.cpp rename to intern/cycles/util/guarded_allocator.cpp index 1cb466a1ffa..4063b301331 100644 --- a/intern/cycles/util/util_guarded_allocator.cpp +++ b/intern/cycles/util/guarded_allocator.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include "util/util_guarded_allocator.h" -#include "util/util_stats.h" +#include "util/guarded_allocator.h" +#include "util/stats.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_guarded_allocator.h b/intern/cycles/util/guarded_allocator.h similarity index 100% rename from intern/cycles/util/util_guarded_allocator.h rename to intern/cycles/util/guarded_allocator.h diff --git a/intern/cycles/util/half.h b/intern/cycles/util/half.h new file mode 100644 index 00000000000..016975e3c25 --- /dev/null +++ b/intern/cycles/util/half.h @@ -0,0 +1,169 @@ +/* + * Copyright 2011-2013 Blender Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __UTIL_HALF_H__ +#define __UTIL_HALF_H__ + +#include "util/math.h" +#include "util/types.h" + +#if !defined(__KERNEL_GPU__) && defined(__KERNEL_SSE2__) +# include "util/simd.h" +#endif + +CCL_NAMESPACE_BEGIN + +/* Half Floats */ + +/* CUDA has its own half data type, no need to define then */ +#if !defined(__KERNEL_CUDA__) && !defined(__KERNEL_HIP__) +/* Implementing this as a class rather than a typedef so that the compiler can tell it apart from + * unsigned shorts. */ +class half { + public: + half() : v(0) + { + } + half(const unsigned short &i) : v(i) + { + } + operator unsigned short() + { + return v; + } + half &operator=(const unsigned short &i) + { + v = i; + return *this; + } + + private: + unsigned short v; +}; +#endif + +struct half4 { + half x, y, z, w; +}; + +/* Conversion to/from half float for image textures + * + * Simplified float to half for fast sampling on processor without a native + * instruction, and eliminating any NaN and inf values. */ + +ccl_device_inline half float_to_half_image(float f) +{ +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) + return __float2half(f); +#else + const uint u = __float_as_uint(f); + /* Sign bit, shifted to its position. */ + uint sign_bit = u & 0x80000000; + sign_bit >>= 16; + /* Exponent. */ + uint exponent_bits = u & 0x7f800000; + /* Non-sign bits. */ + uint value_bits = u & 0x7fffffff; + value_bits >>= 13; /* Align mantissa on MSB. */ + value_bits -= 0x1c000; /* Adjust bias. */ + /* Flush-to-zero. */ + value_bits = (exponent_bits < 0x38800000) ? 0 : value_bits; + /* Clamp-to-max. */ + value_bits = (exponent_bits > 0x47000000) ? 0x7bff : value_bits; + /* Denormals-as-zero. */ + value_bits = (exponent_bits == 0 ? 0 : value_bits); + /* Re-insert sign bit and return. */ + return (value_bits | sign_bit); +#endif +} + +ccl_device_inline float half_to_float_image(half h) +{ +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) + return __half2float(h); +#else + const int x = ((h & 0x8000) << 16) | (((h & 0x7c00) + 0x1C000) << 13) | ((h & 0x03FF) << 13); + return __int_as_float(x); +#endif +} + +ccl_device_inline float4 half4_to_float4_image(const half4 h) +{ + /* Unable to use because it gives different results half_to_float_image, can we + * modify float_to_half_image so the conversion results are identical? */ +#if 0 /* defined(__KERNEL_AVX2__) */ + /* CPU: AVX. */ + __m128i x = _mm_castpd_si128(_mm_load_sd((const double *)&h)); + return float4(_mm_cvtph_ps(x)); +#endif + + const float4 f = make_float4(half_to_float_image(h.x), + half_to_float_image(h.y), + half_to_float_image(h.z), + half_to_float_image(h.w)); + return f; +} + +/* Conversion to half float texture for display. + * + * Simplified float to half for fast display texture conversion on processors + * without a native instruction. Assumes no negative, no NaN, no inf, and sets + * denormal to 0. */ + +ccl_device_inline half float_to_half_display(const float f) +{ +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_HIP__) + return __float2half(f); +#else + const int x = __float_as_int((f > 0.0f) ? ((f < 65504.0f) ? f : 65504.0f) : 0.0f); + const int absolute = x & 0x7FFFFFFF; + const int Z = absolute + 0xC8000000; + const int result = (absolute < 0x38800000) ? 0 : Z; + const int rshift = (result >> 13); + return (rshift & 0x7FFF); +#endif +} + +ccl_device_inline half4 float4_to_half4_display(const float4 f) +{ +#ifdef __KERNEL_SSE2__ + /* CPU: SSE and AVX. */ + ssef x = min(max(load4f(f), 0.0f), 65504.0f); +# ifdef __KERNEL_AVX2__ + ssei rpack = _mm_cvtps_ph(x, 0); +# else + ssei absolute = cast(x) & 0x7FFFFFFF; + ssei Z = absolute + 0xC8000000; + ssei result = andnot(absolute < 0x38800000, Z); + ssei rshift = (result >> 13) & 0x7FFF; + ssei rpack = _mm_packs_epi32(rshift, rshift); +# endif + half4 h; + _mm_storel_pi((__m64 *)&h, _mm_castsi128_ps(rpack)); + return h; +#else + /* GPU and scalar fallback. */ + const half4 h = {float_to_half_display(f.x), + float_to_half_display(f.y), + float_to_half_display(f.z), + float_to_half_display(f.w)}; + return h; +#endif +} + +CCL_NAMESPACE_END + +#endif /* __UTIL_HALF_H__ */ diff --git a/intern/cycles/util/util_hash.h b/intern/cycles/util/hash.h similarity index 99% rename from intern/cycles/util/util_hash.h rename to intern/cycles/util/hash.h index 0021eec169b..013a0f90a27 100644 --- a/intern/cycles/util/util_hash.h +++ b/intern/cycles/util/hash.h @@ -17,7 +17,7 @@ #ifndef __UTIL_HASH_H__ #define __UTIL_HASH_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_ies.cpp b/intern/cycles/util/ies.cpp similarity index 99% rename from intern/cycles/util/util_ies.cpp rename to intern/cycles/util/ies.cpp index 62d3d42186d..5e879478df5 100644 --- a/intern/cycles/util/util_ies.cpp +++ b/intern/cycles/util/ies.cpp @@ -16,10 +16,10 @@ #include -#include "util/util_foreach.h" -#include "util/util_ies.h" -#include "util/util_math.h" -#include "util/util_string.h" +#include "util/foreach.h" +#include "util/ies.h" +#include "util/math.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_ies.h b/intern/cycles/util/ies.h similarity index 96% rename from intern/cycles/util/util_ies.h rename to intern/cycles/util/ies.h index 95473103614..7be072dd5f5 100644 --- a/intern/cycles/util/util_ies.h +++ b/intern/cycles/util/ies.h @@ -17,8 +17,8 @@ #ifndef __UTIL_IES_H__ #define __UTIL_IES_H__ -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_image.h b/intern/cycles/util/image.h similarity index 94% rename from intern/cycles/util/util_image.h rename to intern/cycles/util/image.h index 27ec7ffb423..69fc3a50c1d 100644 --- a/intern/cycles/util/util_image.h +++ b/intern/cycles/util/image.h @@ -21,8 +21,8 @@ # include -# include "util/util_half.h" -# include "util/util_vector.h" +# include "util/half.h" +# include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -56,7 +56,7 @@ template<> inline float util_image_cast_to_float(uint16_t value) } template<> inline float util_image_cast_to_float(half value) { - return half_to_float(value); + return half_to_float_image(value); } /* Cast float value to output pixel type. */ @@ -88,11 +88,11 @@ template<> inline uint16_t util_image_cast_from_float(float value) } template<> inline half util_image_cast_from_float(float value) { - return float_to_half(value); + return float_to_half_image(value); } CCL_NAMESPACE_END #endif /* __UTIL_IMAGE_H__ */ -#include "util/util_image_impl.h" +#include "util/image_impl.h" diff --git a/intern/cycles/util/util_image_impl.h b/intern/cycles/util/image_impl.h similarity index 98% rename from intern/cycles/util/util_image_impl.h rename to intern/cycles/util/image_impl.h index 3eb30d070ea..3d8eed80775 100644 --- a/intern/cycles/util/util_image_impl.h +++ b/intern/cycles/util/image_impl.h @@ -17,9 +17,9 @@ #ifndef __UTIL_IMAGE_IMPL_H__ #define __UTIL_IMAGE_IMPL_H__ -#include "util/util_algorithm.h" -#include "util/util_half.h" -#include "util/util_image.h" +#include "util/algorithm.h" +#include "util/half.h" +#include "util/image.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_list.h b/intern/cycles/util/list.h similarity index 100% rename from intern/cycles/util/util_list.h rename to intern/cycles/util/list.h diff --git a/intern/cycles/util/util_logging.cpp b/intern/cycles/util/log.cpp similarity index 96% rename from intern/cycles/util/util_logging.cpp rename to intern/cycles/util/log.cpp index 8272728a7a0..68a5a3f576f 100644 --- a/intern/cycles/util/util_logging.cpp +++ b/intern/cycles/util/log.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_logging.h" +#include "util/log.h" -#include "util/util_math.h" -#include "util/util_string.h" +#include "util/math.h" +#include "util/string.h" #include #ifdef _MSC_VER diff --git a/intern/cycles/util/util_logging.h b/intern/cycles/util/log.h similarity index 98% rename from intern/cycles/util/util_logging.h rename to intern/cycles/util/log.h index c161299acd0..35c2d436d09 100644 --- a/intern/cycles/util/util_logging.h +++ b/intern/cycles/util/log.h @@ -49,6 +49,7 @@ class LogMessageVoidify { # define LOG(severity) LOG_SUPPRESS() # define VLOG(severity) LOG_SUPPRESS() # define VLOG_IF(severity, condition) LOG_SUPPRESS() +# define VLOG_IS_ON(severity) false # define CHECK(expression) LOG_SUPPRESS() diff --git a/intern/cycles/util/util_map.h b/intern/cycles/util/map.h similarity index 100% rename from intern/cycles/util/util_map.h rename to intern/cycles/util/map.h diff --git a/intern/cycles/util/util_math.h b/intern/cycles/util/math.h similarity index 81% rename from intern/cycles/util/util_math.h rename to intern/cycles/util/math.h index c5996ebfcb6..e4c7df6e44a 100644 --- a/intern/cycles/util/util_math.h +++ b/intern/cycles/util/math.h @@ -26,13 +26,15 @@ # include #endif -#ifndef __KERNEL_OPENCL__ -# include -# include -# include -#endif /* __KERNEL_OPENCL__ */ +#ifdef __HIP__ +# include +#endif -#include "util/util_types.h" +#include +#include +#include + +#include "util/types.h" CCL_NAMESPACE_BEGIN @@ -85,8 +87,8 @@ CCL_NAMESPACE_BEGIN /* Scalar */ -#ifdef _WIN32 -# ifndef __KERNEL_OPENCL__ +#ifndef __HIP__ +# ifdef _WIN32 ccl_device_inline float fmaxf(float a, float b) { return (a > b) ? a : b; @@ -96,8 +98,9 @@ ccl_device_inline float fminf(float a, float b) { return (a < b) ? a : b; } -# endif /* !__KERNEL_OPENCL__ */ -#endif /* _WIN32 */ + +# endif /* _WIN32 */ +#endif /* __HIP__ */ #ifndef __KERNEL_GPU__ using std::isfinite; @@ -119,6 +122,11 @@ ccl_device_inline int min(int a, int b) return (a < b) ? a : b; } +ccl_device_inline uint min(uint a, uint b) +{ + return (a < b) ? a : b; +} + ccl_device_inline float max(float a, float b) { return (a > b) ? a : b; @@ -166,7 +174,6 @@ ccl_device_inline float max4(float a, float b, float c, float d) return max(max(a, b), max(c, d)); } -#ifndef __KERNEL_OPENCL__ /* Int/Float conversion */ ccl_device_inline int as_int(uint i) @@ -199,6 +206,7 @@ ccl_device_inline uint as_uint(float f) return u.i; } +#ifndef __HIP__ ccl_device_inline int __float_as_int(float f) { union { @@ -238,27 +246,57 @@ ccl_device_inline float __uint_as_float(uint i) u.i = i; return u.f; } +#endif ccl_device_inline int4 __float4_as_int4(float4 f) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(f.m128)); -# else +#else return make_int4( __float_as_int(f.x), __float_as_int(f.y), __float_as_int(f.z), __float_as_int(f.w)); -# endif +#endif } ccl_device_inline float4 __int4_as_float4(int4 i) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_castsi128_ps(i.m128)); -# else +#else return make_float4( __int_as_float(i.x), __int_as_float(i.y), __int_as_float(i.z), __int_as_float(i.w)); -# endif +#endif +} + +template ccl_device_inline uint pointer_pack_to_uint_0(T *ptr) +{ + return ((uint64_t)ptr) & 0xFFFFFFFF; +} + +template ccl_device_inline uint pointer_pack_to_uint_1(T *ptr) +{ + return (((uint64_t)ptr) >> 32) & 0xFFFFFFFF; +} + +template ccl_device_inline T *pointer_unpack_from_uint(const uint a, const uint b) +{ + return (T *)(((uint64_t)b << 32) | a); +} + +ccl_device_inline uint uint16_pack_to_uint(const uint a, const uint b) +{ + return (a << 16) | b; +} + +ccl_device_inline uint uint16_unpack_from_uint_0(const uint i) +{ + return i >> 16; +} + +ccl_device_inline uint uint16_unpack_from_uint_1(const uint i) +{ + return i & 0xFFFF; } -#endif /* __KERNEL_OPENCL__ */ /* Versions of functions which are safe for fast math. */ ccl_device_inline bool isnan_safe(float f) @@ -279,7 +317,6 @@ ccl_device_inline float ensure_finite(float v) return isfinite_safe(v) ? v : 0.0f; } -#ifndef __KERNEL_OPENCL__ ccl_device_inline int clamp(int a, int mn, int mx) { return min(max(a, mn), mx); @@ -309,13 +346,16 @@ ccl_device_inline float smoothstep(float edge0, float edge1, float x) return result; } -#endif /* __KERNEL_OPENCL__ */ - #ifndef __KERNEL_CUDA__ -ccl_device_inline float saturate(float a) +ccl_device_inline float saturatef(float a) { return clamp(a, 0.0f, 1.0f); } +#else +ccl_device_inline float saturatef(float a) +{ + return __saturatef(a); +} #endif /* __KERNEL_CUDA__ */ ccl_device_inline int float_to_int(float f) @@ -333,7 +373,7 @@ ccl_device_inline int quick_floor_to_int(float x) return float_to_int(x) - ((x < 0) ? 1 : 0); } -ccl_device_inline float floorfrac(float x, int *i) +ccl_device_inline float floorfrac(float x, ccl_private int *i) { *i = quick_floor_to_int(x); return x - *i; @@ -439,19 +479,18 @@ ccl_device_inline float cubic_interp(float a, float b, float c, float d, float x CCL_NAMESPACE_END -#include "util/util_math_int2.h" -#include "util/util_math_int3.h" -#include "util/util_math_int4.h" +#include "util/math_int2.h" +#include "util/math_int3.h" +#include "util/math_int4.h" -#include "util/util_math_float2.h" -#include "util/util_math_float3.h" -#include "util/util_math_float4.h" +#include "util/math_float2.h" +#include "util/math_float3.h" +#include "util/math_float4.h" -#include "util/util_rect.h" +#include "util/rect.h" CCL_NAMESPACE_BEGIN -#ifndef __KERNEL_OPENCL__ /* Interpolation */ template A lerp(const A &a, const A &b, const B &t) @@ -459,22 +498,20 @@ template A lerp(const A &a, const A &b, const B &t) return (A)(a * ((B)1 - t) + b * t); } -#endif /* __KERNEL_OPENCL__ */ - /* Triangle */ -#ifndef __KERNEL_OPENCL__ -ccl_device_inline float triangle_area(const float3 &v1, const float3 &v2, const float3 &v3) -#else -ccl_device_inline float triangle_area(const float3 v1, const float3 v2, const float3 v3) -#endif +ccl_device_inline float triangle_area(ccl_private const float3 &v1, + ccl_private const float3 &v2, + ccl_private const float3 &v3) { return len(cross(v3 - v2, v1 - v2)) * 0.5f; } /* Orthonormal vectors */ -ccl_device_inline void make_orthonormals(const float3 N, float3 *a, float3 *b) +ccl_device_inline void make_orthonormals(const float3 N, + ccl_private float3 *a, + ccl_private float3 *b) { #if 0 if (fabsf(N.y) >= 0.999f) { @@ -665,11 +702,7 @@ ccl_device_inline float pow22(float a) ccl_device_inline float beta(float x, float y) { -#ifndef __KERNEL_OPENCL__ return expf(lgammaf(x) + lgammaf(y) - lgammaf(x + y)); -#else - return expf(lgamma(x) + lgamma(y) - lgamma(x + y)); -#endif } ccl_device_inline float xor_signmask(float x, int y) @@ -684,10 +717,8 @@ ccl_device float bits_to_01(uint bits) ccl_device_inline uint count_leading_zeros(uint x) { -#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__) return __clz(x); -#elif defined(__KERNEL_OPENCL__) - return clz(x); #else assert(x != 0); # ifdef _MSC_VER @@ -702,10 +733,8 @@ ccl_device_inline uint count_leading_zeros(uint x) ccl_device_inline uint count_trailing_zeros(uint x) { -#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__) return (__ffs(x) - 1); -#elif defined(__KERNEL_OPENCL__) - return (31 - count_leading_zeros(x & -x)); #else assert(x != 0); # ifdef _MSC_VER @@ -720,10 +749,8 @@ ccl_device_inline uint count_trailing_zeros(uint x) ccl_device_inline uint find_first_set(uint x) { -#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) +#if defined(__KERNEL_CUDA__) || defined(__KERNEL_OPTIX__) || defined(__KERNEL_HIP__) return __ffs(x); -#elif defined(__KERNEL_OPENCL__) - return (x != 0) ? (32 - count_leading_zeros(x & (-x))) : 0; #else # ifdef _MSC_VER return (x != 0) ? (32 - count_leading_zeros(x & (-x))) : 0; @@ -797,6 +824,52 @@ ccl_device_inline float precise_angle(float3 a, float3 b) return 2.0f * atan2f(len(a - b), len(a + b)); } +/* Return value which is greater than the given one and is a power of two. */ +ccl_device_inline uint next_power_of_two(uint x) +{ + return x == 0 ? 1 : 1 << (32 - count_leading_zeros(x)); +} + +/* Return value which is lower than the given one and is a power of two. */ +ccl_device_inline uint prev_power_of_two(uint x) +{ + return x < 2 ? x : 1 << (31 - count_leading_zeros(x - 1)); +} + +#ifndef __has_builtin +# define __has_builtin(v) 0 +#endif + +/* Reverses the bits of a 32 bit integer. */ +ccl_device_inline uint32_t reverse_integer_bits(uint32_t x) +{ + /* Use a native instruction if it exists. */ +#if defined(__arm__) || defined(__aarch64__) + __asm__("rbit %w0, %w1" : "=r"(x) : "r"(x)); + return x; +#elif defined(__KERNEL_CUDA__) + return __brev(x); +#elif __has_builtin(__builtin_bitreverse32) + return __builtin_bitreverse32(x); +#else + /* Flip pairwise. */ + x = ((x & 0x55555555) << 1) | ((x & 0xAAAAAAAA) >> 1); + /* Flip pairs. */ + x = ((x & 0x33333333) << 2) | ((x & 0xCCCCCCCC) >> 2); + /* Flip nibbles. */ + x = ((x & 0x0F0F0F0F) << 4) | ((x & 0xF0F0F0F0) >> 4); + /* Flip bytes. CPUs have an instruction for that, pretty fast one. */ +# ifdef _MSC_VER + return _byteswap_ulong(x); +# elif defined(__INTEL_COMPILER) + return (uint32_t)_bswap((int)x); +# else + /* Assuming gcc or clang. */ + return __builtin_bswap32(x); +# endif +#endif +} + CCL_NAMESPACE_END #endif /* __UTIL_MATH_H__ */ diff --git a/intern/cycles/util/util_math_cdf.cpp b/intern/cycles/util/math_cdf.cpp similarity index 95% rename from intern/cycles/util/util_math_cdf.cpp rename to intern/cycles/util/math_cdf.cpp index a58bab188ef..02c6646f824 100644 --- a/intern/cycles/util/util_math_cdf.cpp +++ b/intern/cycles/util/math_cdf.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_math_cdf.h" +#include "util/math_cdf.h" -#include "util/util_algorithm.h" -#include "util/util_math.h" +#include "util/algorithm.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_cdf.h b/intern/cycles/util/math_cdf.h similarity index 96% rename from intern/cycles/util/util_math_cdf.h rename to intern/cycles/util/math_cdf.h index 43995204263..4c57dac4bbe 100644 --- a/intern/cycles/util/util_math_cdf.h +++ b/intern/cycles/util/math_cdf.h @@ -17,9 +17,9 @@ #ifndef __UTIL_MATH_CDF_H__ #define __UTIL_MATH_CDF_H__ -#include "util/util_algorithm.h" -#include "util/util_math.h" -#include "util/util_vector.h" +#include "util/algorithm.h" +#include "util/math.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_fast.h b/intern/cycles/util/math_fast.h similarity index 99% rename from intern/cycles/util/util_math_fast.h rename to intern/cycles/util/math_fast.h index 38afa163db5..cc924f36a71 100644 --- a/intern/cycles/util/util_math_fast.h +++ b/intern/cycles/util/math_fast.h @@ -156,7 +156,7 @@ ccl_device float fast_cosf(float x) return u; } -ccl_device void fast_sincosf(float x, float *sine, float *cosine) +ccl_device void fast_sincosf(float x, ccl_private float *sine, ccl_private float *cosine) { /* Same argument reduction as fast_sin. */ int q = fast_rint(x * M_1_PI_F); diff --git a/intern/cycles/util/util_math_float2.h b/intern/cycles/util/math_float2.h similarity index 96% rename from intern/cycles/util/util_math_float2.h rename to intern/cycles/util/math_float2.h index 17f6f3c9382..87141d5bc37 100644 --- a/intern/cycles/util/util_math_float2.h +++ b/intern/cycles/util/math_float2.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_FLOAT2_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN @@ -27,7 +27,6 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline float2 operator-(const float2 &a); ccl_device_inline float2 operator*(const float2 &a, const float2 &b); ccl_device_inline float2 operator*(const float2 &a, float f); @@ -64,7 +63,6 @@ ccl_device_inline float2 fabs(const float2 &a); ccl_device_inline float2 as_float2(const float4 &a); ccl_device_inline float2 interp(const float2 &a, const float2 &b, float t); ccl_device_inline float2 floor(const float2 &a); -#endif /* !__KERNEL_OPENCL__ */ ccl_device_inline float2 safe_divide_float2_float(const float2 a, const float b); @@ -82,7 +80,6 @@ ccl_device_inline float2 one_float2() return make_float2(1.0f, 1.0f); } -#ifndef __KERNEL_OPENCL__ ccl_device_inline float2 operator-(const float2 &a) { return make_float2(-a.x, -a.y); @@ -210,7 +207,7 @@ ccl_device_inline float2 normalize(const float2 &a) return a / len(a); } -ccl_device_inline float2 normalize_len(const float2 &a, float *t) +ccl_device_inline float2 normalize_len(const float2 &a, ccl_private float *t) { *t = len(a); return a / (*t); @@ -262,8 +259,6 @@ ccl_device_inline float2 floor(const float2 &a) return make_float2(floorf(a.x), floorf(a.y)); } -#endif /* !__KERNEL_OPENCL__ */ - ccl_device_inline float2 safe_divide_float2_float(const float2 a, const float b) { return (b != 0.0f) ? a / b : zero_float2(); diff --git a/intern/cycles/util/util_math_float3.h b/intern/cycles/util/math_float3.h similarity index 90% rename from intern/cycles/util/util_math_float3.h rename to intern/cycles/util/math_float3.h index 9673c043189..81550c5d03c 100644 --- a/intern/cycles/util/util_math_float3.h +++ b/intern/cycles/util/math_float3.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_FLOAT3_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN @@ -27,7 +27,6 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline float3 operator-(const float3 &a); ccl_device_inline float3 operator*(const float3 &a, const float3 &b); ccl_device_inline float3 operator*(const float3 &a, const float f); @@ -63,7 +62,6 @@ ccl_device_inline float3 rcp(const float3 &a); ccl_device_inline float3 sqrt(const float3 &a); ccl_device_inline float3 floor(const float3 &a); ccl_device_inline float3 ceil(const float3 &a); -#endif /* !__KERNEL_OPENCL__ */ ccl_device_inline float min3(float3 a); ccl_device_inline float max3(float3 a); @@ -105,50 +103,49 @@ ccl_device_inline float3 one_float3() return make_float3(1.0f, 1.0f, 1.0f); } -#ifndef __KERNEL_OPENCL__ ccl_device_inline float3 operator-(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_xor_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x80000000)))); -# else +#else return make_float3(-a.x, -a.y, -a.z); -# endif +#endif } ccl_device_inline float3 operator*(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_mul_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x * b.x, a.y * b.y, a.z * b.z); -# endif +#endif } ccl_device_inline float3 operator*(const float3 &a, const float f) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_mul_ps(a.m128, _mm_set1_ps(f))); -# else +#else return make_float3(a.x * f, a.y * f, a.z * f); -# endif +#endif } ccl_device_inline float3 operator*(const float f, const float3 &a) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return float3(_mm_mul_ps(_mm_set1_ps(f), a.m128)); -# else +#else return make_float3(a.x * f, a.y * f, a.z * f); -# endif +#endif } ccl_device_inline float3 operator/(const float f, const float3 &a) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return float3(_mm_div_ps(_mm_set1_ps(f), a.m128)); -# else +#else return make_float3(f / a.x, f / a.y, f / a.z); -# endif +#endif } ccl_device_inline float3 operator/(const float3 &a, const float f) @@ -159,11 +156,11 @@ ccl_device_inline float3 operator/(const float3 &a, const float f) ccl_device_inline float3 operator/(const float3 &a, const float3 &b) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return float3(_mm_div_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x / b.x, a.y / b.y, a.z / b.z); -# endif +#endif } ccl_device_inline float3 operator+(const float3 &a, const float f) @@ -173,11 +170,11 @@ ccl_device_inline float3 operator+(const float3 &a, const float f) ccl_device_inline float3 operator+(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_add_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x + b.x, a.y + b.y, a.z + b.z); -# endif +#endif } ccl_device_inline float3 operator-(const float3 &a, const float f) @@ -187,11 +184,11 @@ ccl_device_inline float3 operator-(const float3 &a, const float f) ccl_device_inline float3 operator-(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_sub_ps(a.m128, b.m128)); -# else +#else return make_float3(a.x - b.x, a.y - b.y, a.z - b.z); -# endif +#endif } ccl_device_inline float3 operator+=(float3 &a, const float3 &b) @@ -227,11 +224,11 @@ ccl_device_inline float3 operator/=(float3 &a, float f) ccl_device_inline bool operator==(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return (_mm_movemask_ps(_mm_cmpeq_ps(a.m128, b.m128)) & 7) == 7; -# else +#else return (a.x == b.x && a.y == b.y && a.z == b.z); -# endif +#endif } ccl_device_inline bool operator!=(const float3 &a, const float3 &b) @@ -246,20 +243,20 @@ ccl_device_inline float distance(const float3 &a, const float3 &b) ccl_device_inline float dot(const float3 &a, const float3 &b) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) return _mm_cvtss_f32(_mm_dp_ps(a, b, 0x7F)); -# else +#else return a.x * b.x + a.y * b.y + a.z * b.z; -# endif +#endif } ccl_device_inline float dot_xy(const float3 &a, const float3 &b) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) return _mm_cvtss_f32(_mm_hadd_ps(_mm_mul_ps(a, b), b)); -# else +#else return a.x * b.x + a.y * b.y; -# endif +#endif } ccl_device_inline float3 cross(const float3 &a, const float3 &b) @@ -270,30 +267,30 @@ ccl_device_inline float3 cross(const float3 &a, const float3 &b) ccl_device_inline float3 normalize(const float3 &a) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) __m128 norm = _mm_sqrt_ps(_mm_dp_ps(a.m128, a.m128, 0x7F)); return float3(_mm_div_ps(a.m128, norm)); -# else +#else return a / len(a); -# endif +#endif } ccl_device_inline float3 min(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_min_ps(a.m128, b.m128)); -# else +#else return make_float3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); -# endif +#endif } ccl_device_inline float3 max(const float3 &a, const float3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_max_ps(a.m128, b.m128)); -# else +#else return make_float3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); -# endif +#endif } ccl_device_inline float3 clamp(const float3 &a, const float3 &mn, const float3 &mx) @@ -303,43 +300,43 @@ ccl_device_inline float3 clamp(const float3 &a, const float3 &mn, const float3 & ccl_device_inline float3 fabs(const float3 &a) { -# ifdef __KERNEL_SSE__ -# ifdef __KERNEL_NEON__ +#ifdef __KERNEL_SSE__ +# ifdef __KERNEL_NEON__ return float3(vabsq_f32(a.m128)); -# else +# else __m128 mask = _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)); return float3(_mm_and_ps(a.m128, mask)); -# endif -# else - return make_float3(fabsf(a.x), fabsf(a.y), fabsf(a.z)); # endif +#else + return make_float3(fabsf(a.x), fabsf(a.y), fabsf(a.z)); +#endif } ccl_device_inline float3 sqrt(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_sqrt_ps(a)); -# else +#else return make_float3(sqrtf(a.x), sqrtf(a.y), sqrtf(a.z)); -# endif +#endif } ccl_device_inline float3 floor(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_floor_ps(a)); -# else +#else return make_float3(floorf(a.x), floorf(a.y), floorf(a.z)); -# endif +#endif } ccl_device_inline float3 ceil(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float3(_mm_ceil_ps(a)); -# else +#else return make_float3(ceilf(a.x), ceilf(a.y), ceilf(a.z)); -# endif +#endif } ccl_device_inline float3 mix(const float3 &a, const float3 &b, float t) @@ -349,14 +346,13 @@ ccl_device_inline float3 mix(const float3 &a, const float3 &b, float t) ccl_device_inline float3 rcp(const float3 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ /* Don't use _mm_rcp_ps due to poor precision. */ return float3(_mm_div_ps(_mm_set_ps1(1.0f), a.m128)); -# else +#else return make_float3(1.0f / a.x, 1.0f / a.y, 1.0f / a.z); -# endif +#endif } -#endif /* !__KERNEL_OPENCL__ */ ccl_device_inline float min3(float3 a) { @@ -412,10 +408,10 @@ ccl_device_inline float3 project(const float3 v, const float3 v_proj) ccl_device_inline float3 saturate3(float3 a) { - return make_float3(saturate(a.x), saturate(a.y), saturate(a.z)); + return make_float3(saturatef(a.x), saturatef(a.y), saturatef(a.z)); } -ccl_device_inline float3 normalize_len(const float3 a, float *t) +ccl_device_inline float3 normalize_len(const float3 a, ccl_private float *t) { *t = len(a); float x = 1.0f / *t; @@ -428,7 +424,7 @@ ccl_device_inline float3 safe_normalize(const float3 a) return (t != 0.0f) ? a * (1.0f / t) : a; } -ccl_device_inline float3 safe_normalize_len(const float3 a, float *t) +ccl_device_inline float3 safe_normalize_len(const float3 a, ccl_private float *t) { *t = len(a); return (*t != 0.0f) ? a / (*t) : a; @@ -483,11 +479,7 @@ ccl_device_inline float average(const float3 a) ccl_device_inline bool isequal_float3(const float3 a, const float3 b) { -#ifdef __KERNEL_OPENCL__ - return all(a == b); -#else return a == b; -#endif } ccl_device_inline float3 pow3(float3 v, float e) diff --git a/intern/cycles/util/util_math_float4.h b/intern/cycles/util/math_float4.h similarity index 91% rename from intern/cycles/util/util_math_float4.h rename to intern/cycles/util/math_float4.h index 0ba2bafa2f0..c76959ee7ff 100644 --- a/intern/cycles/util/util_math_float4.h +++ b/intern/cycles/util/math_float4.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_FLOAT4_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN @@ -27,7 +27,6 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline float4 operator-(const float4 &a); ccl_device_inline float4 operator*(const float4 &a, const float4 &b); ccl_device_inline float4 operator*(const float4 &a, float f); @@ -66,7 +65,6 @@ ccl_device_inline float4 clamp(const float4 &a, const float4 &mn, const float4 & ccl_device_inline float4 fabs(const float4 &a); ccl_device_inline float4 floor(const float4 &a); ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t); -#endif /* !__KERNEL_OPENCL__*/ ccl_device_inline float4 safe_divide_float4_float(const float4 a, const float b); @@ -112,33 +110,32 @@ ccl_device_inline float4 one_float4() return make_float4(1.0f, 1.0f, 1.0f, 1.0f); } -#ifndef __KERNEL_OPENCL__ ccl_device_inline float4 operator-(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ __m128 mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000)); return float4(_mm_xor_ps(a.m128, mask)); -# else +#else return make_float4(-a.x, -a.y, -a.z, -a.w); -# endif +#endif } ccl_device_inline float4 operator*(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_mul_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x * b.x, a.y * b.y, a.z * b.z, a.w * b.w); -# endif +#endif } ccl_device_inline float4 operator*(const float4 &a, float f) { -# if defined(__KERNEL_SSE__) +#if defined(__KERNEL_SSE__) return a * make_float4(f); -# else +#else return make_float4(a.x * f, a.y * f, a.z * f, a.w * f); -# endif +#endif } ccl_device_inline float4 operator*(float f, const float4 &a) @@ -153,11 +150,11 @@ ccl_device_inline float4 operator/(const float4 &a, float f) ccl_device_inline float4 operator/(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_div_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x / b.x, a.y / b.y, a.z / b.z, a.w / b.w); -# endif +#endif } ccl_device_inline float4 operator+(const float4 &a, const float f) @@ -167,11 +164,11 @@ ccl_device_inline float4 operator+(const float4 &a, const float f) ccl_device_inline float4 operator+(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_add_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w); -# endif +#endif } ccl_device_inline float4 operator-(const float4 &a, const float f) @@ -181,11 +178,11 @@ ccl_device_inline float4 operator-(const float4 &a, const float f) ccl_device_inline float4 operator-(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_sub_ps(a.m128, b.m128)); -# else +#else return make_float4(a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w); -# endif +#endif } ccl_device_inline float4 operator+=(float4 &a, const float4 &b) @@ -215,38 +212,38 @@ ccl_device_inline float4 operator/=(float4 &a, float f) ccl_device_inline int4 operator<(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(_mm_cmplt_ps(a.m128, b.m128))); -# else +#else return make_int4(a.x < b.x, a.y < b.y, a.z < b.z, a.w < b.w); -# endif +#endif } ccl_device_inline int4 operator>=(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(_mm_cmpge_ps(a.m128, b.m128))); -# else +#else return make_int4(a.x >= b.x, a.y >= b.y, a.z >= b.z, a.w >= b.w); -# endif +#endif } ccl_device_inline int4 operator<=(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int4(_mm_castps_si128(_mm_cmple_ps(a.m128, b.m128))); -# else +#else return make_int4(a.x <= b.x, a.y <= b.y, a.z <= b.z, a.w <= b.w); -# endif +#endif } ccl_device_inline bool operator==(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return (_mm_movemask_ps(_mm_cmpeq_ps(a.m128, b.m128)) & 15) == 15; -# else +#else return (a.x == b.x && a.y == b.y && a.z == b.z && a.w == b.w); -# endif +#endif } ccl_device_inline float distance(const float4 &a, const float4 &b) @@ -256,16 +253,16 @@ ccl_device_inline float distance(const float4 &a, const float4 &b) ccl_device_inline float dot(const float4 &a, const float4 &b) { -# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) -# if defined(__KERNEL_NEON__) +#if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) +# if defined(__KERNEL_NEON__) __m128 t = vmulq_f32(a, b); return vaddvq_f32(t); -# else - return _mm_cvtss_f32(_mm_dp_ps(a, b, 0xFF)); -# endif # else - return (a.x * b.x + a.y * b.y) + (a.z * b.z + a.w * b.w); + return _mm_cvtss_f32(_mm_dp_ps(a, b, 0xFF)); # endif +#else + return (a.x * b.x + a.y * b.y) + (a.z * b.z + a.w * b.w); +#endif } ccl_device_inline float len_squared(const float4 &a) @@ -275,21 +272,21 @@ ccl_device_inline float len_squared(const float4 &a) ccl_device_inline float4 rcp(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ /* Don't use _mm_rcp_ps due to poor precision. */ return float4(_mm_div_ps(_mm_set_ps1(1.0f), a.m128)); -# else +#else return make_float4(1.0f / a.x, 1.0f / a.y, 1.0f / a.z, 1.0f / a.w); -# endif +#endif } ccl_device_inline float4 sqrt(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_sqrt_ps(a.m128)); -# else +#else return make_float4(sqrtf(a.x), sqrtf(a.y), sqrtf(a.z), sqrtf(a.w)); -# endif +#endif } ccl_device_inline float4 sqr(const float4 &a) @@ -299,39 +296,39 @@ ccl_device_inline float4 sqr(const float4 &a) ccl_device_inline float4 cross(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return (shuffle<1, 2, 0, 0>(a) * shuffle<2, 0, 1, 0>(b)) - (shuffle<2, 0, 1, 0>(a) * shuffle<1, 2, 0, 0>(b)); -# else +#else return make_float4(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x, 0.0f); -# endif +#endif } ccl_device_inline bool is_zero(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return a == make_float4(0.0f); -# else +#else return (a.x == 0.0f && a.y == 0.0f && a.z == 0.0f && a.w == 0.0f); -# endif +#endif } ccl_device_inline float4 reduce_add(const float4 &a) { -# if defined(__KERNEL_SSE__) -# if defined(__KERNEL_NEON__) +#if defined(__KERNEL_SSE__) +# if defined(__KERNEL_NEON__) return float4(vdupq_n_f32(vaddvq_f32(a))); -# elif defined(__KERNEL_SSE3__) +# elif defined(__KERNEL_SSE3__) float4 h(_mm_hadd_ps(a.m128, a.m128)); return float4(_mm_hadd_ps(h.m128, h.m128)); -# else +# else float4 h(shuffle<1, 0, 3, 2>(a) + a); return shuffle<2, 3, 0, 1>(h) + h; -# endif -# else +# endif +#else float sum = (a.x + a.y) + (a.z + a.w); return make_float4(sum, sum, sum, sum); -# endif +#endif } ccl_device_inline float average(const float4 &a) @@ -357,20 +354,20 @@ ccl_device_inline float4 safe_normalize(const float4 &a) ccl_device_inline float4 min(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_min_ps(a.m128, b.m128)); -# else +#else return make_float4(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z), min(a.w, b.w)); -# endif +#endif } ccl_device_inline float4 max(const float4 &a, const float4 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_max_ps(a.m128, b.m128)); -# else +#else return make_float4(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z), max(a.w, b.w)); -# endif +#endif } ccl_device_inline float4 clamp(const float4 &a, const float4 &mn, const float4 &mx) @@ -380,24 +377,24 @@ ccl_device_inline float4 clamp(const float4 &a, const float4 &mn, const float4 & ccl_device_inline float4 fabs(const float4 &a) { -# if defined(__KERNEL_SSE__) -# if defined(__KERNEL_NEON__) +#if defined(__KERNEL_SSE__) +# if defined(__KERNEL_NEON__) return float4(vabsq_f32(a)); -# else - return float4(_mm_and_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); -# endif # else - return make_float4(fabsf(a.x), fabsf(a.y), fabsf(a.z), fabsf(a.w)); + return float4(_mm_and_ps(a.m128, _mm_castsi128_ps(_mm_set1_epi32(0x7fffffff)))); # endif +#else + return make_float4(fabsf(a.x), fabsf(a.y), fabsf(a.z), fabsf(a.w)); +#endif } ccl_device_inline float4 floor(const float4 &a) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return float4(_mm_floor_ps(a)); -# else +#else return make_float4(floorf(a.x), floorf(a.y), floorf(a.z), floorf(a.w)); -# endif +#endif } ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t) @@ -405,8 +402,6 @@ ccl_device_inline float4 mix(const float4 &a, const float4 &b, float t) return a + t * (b - a); } -#endif /* !__KERNEL_OPENCL__*/ - #ifdef __KERNEL_SSE__ template __forceinline const float4 shuffle(const float4 &b) @@ -502,7 +497,7 @@ ccl_device_inline float4 reduce_max(const float4 &a) # endif } -ccl_device_inline float4 load_float4(const float *v) +ccl_device_inline float4 load_float4(ccl_private const float *v) { # ifdef __KERNEL_SSE__ return float4(_mm_loadu_ps(v)); diff --git a/intern/cycles/util/util_math_int2.h b/intern/cycles/util/math_int2.h similarity index 91% rename from intern/cycles/util/util_math_int2.h rename to intern/cycles/util/math_int2.h index 0295cd51f7e..5b04be92152 100644 --- a/intern/cycles/util/util_math_int2.h +++ b/intern/cycles/util/math_int2.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_INT2_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN @@ -27,20 +27,17 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline bool operator==(const int2 a, const int2 b); ccl_device_inline int2 operator+(const int2 &a, const int2 &b); ccl_device_inline int2 operator+=(int2 &a, const int2 &b); ccl_device_inline int2 operator-(const int2 &a, const int2 &b); ccl_device_inline int2 operator*(const int2 &a, const int2 &b); ccl_device_inline int2 operator/(const int2 &a, const int2 &b); -#endif /* !__KERNEL_OPENCL__ */ /******************************************************************************* * Definition. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline bool operator==(const int2 a, const int2 b) { return (a.x == b.x && a.y == b.y); @@ -70,7 +67,6 @@ ccl_device_inline int2 operator/(const int2 &a, const int2 &b) { return make_int2(a.x / b.x, a.y / b.y); } -#endif /* !__KERNEL_OPENCL__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_math_int3.h b/intern/cycles/util/math_int3.h similarity index 83% rename from intern/cycles/util/util_math_int3.h rename to intern/cycles/util/math_int3.h index d92ed895dc2..128f2cb53b8 100644 --- a/intern/cycles/util/util_math_int3.h +++ b/intern/cycles/util/math_int3.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_INT3_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN @@ -27,52 +27,49 @@ CCL_NAMESPACE_BEGIN * Declaration. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline int3 min(int3 a, int3 b); ccl_device_inline int3 max(int3 a, int3 b); ccl_device_inline int3 clamp(const int3 &a, int mn, int mx); ccl_device_inline int3 clamp(const int3 &a, int3 &mn, int mx); -#endif /* !__KERNEL_OPENCL__ */ /******************************************************************************* * Definition. */ -#ifndef __KERNEL_OPENCL__ ccl_device_inline int3 min(int3 a, int3 b) { -# if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) +#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) return int3(_mm_min_epi32(a.m128, b.m128)); -# else +#else return make_int3(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z)); -# endif +#endif } ccl_device_inline int3 max(int3 a, int3 b) { -# if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) +#if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE41__) return int3(_mm_max_epi32(a.m128, b.m128)); -# else +#else return make_int3(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z)); -# endif +#endif } ccl_device_inline int3 clamp(const int3 &a, int mn, int mx) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return min(max(a, make_int3(mn)), make_int3(mx)); -# else +#else return make_int3(clamp(a.x, mn, mx), clamp(a.y, mn, mx), clamp(a.z, mn, mx)); -# endif +#endif } ccl_device_inline int3 clamp(const int3 &a, int3 &mn, int mx) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return min(max(a, mn), make_int3(mx)); -# else +#else return make_int3(clamp(a.x, mn.x, mx), clamp(a.y, mn.y, mx), clamp(a.z, mn.z, mx)); -# endif +#endif } ccl_device_inline bool operator==(const int3 &a, const int3 &b) @@ -92,22 +89,21 @@ ccl_device_inline bool operator<(const int3 &a, const int3 &b) ccl_device_inline int3 operator+(const int3 &a, const int3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int3(_mm_add_epi32(a.m128, b.m128)); -# else +#else return make_int3(a.x + b.x, a.y + b.y, a.z + b.z); -# endif +#endif } ccl_device_inline int3 operator-(const int3 &a, const int3 &b) { -# ifdef __KERNEL_SSE__ +#ifdef __KERNEL_SSE__ return int3(_mm_sub_epi32(a.m128, b.m128)); -# else +#else return make_int3(a.x - b.x, a.y - b.y, a.z - b.z); -# endif +#endif } -#endif /* !__KERNEL_OPENCL__ */ CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_math_int4.h b/intern/cycles/util/math_int4.h similarity index 98% rename from intern/cycles/util/util_math_int4.h rename to intern/cycles/util/math_int4.h index 186cc58489b..9e3f001efc2 100644 --- a/intern/cycles/util/util_math_int4.h +++ b/intern/cycles/util/math_int4.h @@ -18,7 +18,7 @@ #define __UTIL_MATH_INT4_H__ #ifndef __UTIL_MATH_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_math_intersect.h b/intern/cycles/util/math_intersect.h similarity index 89% rename from intern/cycles/util/util_math_intersect.h rename to intern/cycles/util/math_intersect.h index fa3a541eea9..0c431a36afb 100644 --- a/intern/cycles/util/util_math_intersect.h +++ b/intern/cycles/util/math_intersect.h @@ -26,8 +26,8 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, float ray_t, float3 sphere_P, float sphere_radius, - float3 *isect_P, - float *isect_t) + ccl_private float3 *isect_P, + ccl_private float *isect_t) { const float3 d = sphere_P - ray_P; const float radiussq = sphere_radius * sphere_radius; @@ -40,7 +40,7 @@ ccl_device bool ray_sphere_intersect(float3 ray_P, /* Ray points away from sphere. */ return false; } - const float dsq = tsq - tp * tp; /* pythagoras */ + const float dsq = tsq - tp * tp; /* Pythagoras. */ if (dsq > radiussq) { /* Closest point on ray outside sphere. */ return false; @@ -60,8 +60,8 @@ ccl_device bool ray_aligned_disk_intersect(float3 ray_P, float ray_t, float3 disk_P, float disk_radius, - float3 *isect_P, - float *isect_t) + ccl_private float3 *isect_P, + ccl_private float *isect_t) { /* Aligned disk normal. */ float disk_t; @@ -95,9 +95,9 @@ ccl_device_forceinline bool ray_triangle_intersect(float3 ray_P, const float3 tri_b, const float3 tri_c, #endif - float *isect_u, - float *isect_v, - float *isect_t) + ccl_private float *isect_u, + ccl_private float *isect_v, + ccl_private float *isect_t) { #if defined(__KERNEL_SSE2__) && defined(__KERNEL_SSE__) typedef ssef float3; @@ -207,10 +207,10 @@ ccl_device bool ray_quad_intersect(float3 ray_P, float3 quad_u, float3 quad_v, float3 quad_n, - float3 *isect_P, - float *isect_t, - float *isect_u, - float *isect_v, + ccl_private float3 *isect_P, + ccl_private float *isect_t, + ccl_private float *isect_u, + ccl_private float *isect_v, bool ellipse) { /* Perform intersection test. */ diff --git a/intern/cycles/util/util_math_matrix.h b/intern/cycles/util/math_matrix.h similarity index 90% rename from intern/cycles/util/util_math_matrix.h rename to intern/cycles/util/math_matrix.h index 123736f75a6..bff7ddb4cee 100644 --- a/intern/cycles/util/util_math_matrix.h +++ b/intern/cycles/util/math_matrix.h @@ -35,14 +35,14 @@ CCL_NAMESPACE_BEGIN /* Zeroing helpers. */ -ccl_device_inline void math_vector_zero(float *v, int n) +ccl_device_inline void math_vector_zero(ccl_private float *v, int n) { for (int i = 0; i < n; i++) { v[i] = 0.0f; } } -ccl_device_inline void math_matrix_zero(float *A, int n) +ccl_device_inline void math_matrix_zero(ccl_private float *A, int n) { for (int row = 0; row < n; row++) { for (int col = 0; col <= row; col++) { @@ -53,14 +53,18 @@ ccl_device_inline void math_matrix_zero(float *A, int n) /* Elementary vector operations. */ -ccl_device_inline void math_vector_add(float *a, const float *ccl_restrict b, int n) +ccl_device_inline void math_vector_add(ccl_private float *a, + ccl_private const float *ccl_restrict b, + int n) { for (int i = 0; i < n; i++) { a[i] += b[i]; } } -ccl_device_inline void math_vector_mul(float *a, const float *ccl_restrict b, int n) +ccl_device_inline void math_vector_mul(ccl_private float *a, + ccl_private const float *ccl_restrict b, + int n) { for (int i = 0; i < n; i++) { a[i] *= b[i]; @@ -68,7 +72,7 @@ ccl_device_inline void math_vector_mul(float *a, const float *ccl_restrict b, in } ccl_device_inline void math_vector_mul_strided(ccl_global float *a, - const float *ccl_restrict b, + ccl_private const float *ccl_restrict b, int astride, int n) { @@ -77,21 +81,23 @@ ccl_device_inline void math_vector_mul_strided(ccl_global float *a, } } -ccl_device_inline void math_vector_scale(float *a, float b, int n) +ccl_device_inline void math_vector_scale(ccl_private float *a, float b, int n) { for (int i = 0; i < n; i++) { a[i] *= b; } } -ccl_device_inline void math_vector_max(float *a, const float *ccl_restrict b, int n) +ccl_device_inline void math_vector_max(ccl_private float *a, + ccl_private const float *ccl_restrict b, + int n) { for (int i = 0; i < n; i++) { a[i] = max(a[i], b[i]); } } -ccl_device_inline void math_vec3_add(float3 *v, int n, float *x, float3 w) +ccl_device_inline void math_vec3_add(ccl_private float3 *v, int n, ccl_private float *x, float3 w) { for (int i = 0; i < n; i++) { v[i] += w * x[i]; @@ -99,7 +105,7 @@ ccl_device_inline void math_vec3_add(float3 *v, int n, float *x, float3 w) } ccl_device_inline void math_vec3_add_strided( - ccl_global float3 *v, int n, float *x, float3 w, int stride) + ccl_global float3 *v, int n, ccl_private float *x, float3 w, int stride) { for (int i = 0; i < n; i++) { ccl_global float *elem = (ccl_global float *)(v + i * stride); @@ -125,9 +131,9 @@ ccl_device_inline void math_trimatrix_add_diagonal(ccl_global float *A, /* Add Gramian matrix of v to A. * The Gramian matrix of v is vt*v, so element (i,j) is v[i]*v[j]. */ -ccl_device_inline void math_matrix_add_gramian(float *A, +ccl_device_inline void math_matrix_add_gramian(ccl_private float *A, int n, - const float *ccl_restrict v, + ccl_private const float *ccl_restrict v, float weight) { for (int row = 0; row < n; row++) { @@ -140,7 +146,7 @@ ccl_device_inline void math_matrix_add_gramian(float *A, /* Add Gramian matrix of v to A. * The Gramian matrix of v is vt*v, so element (i,j) is v[i]*v[j]. */ ccl_device_inline void math_trimatrix_add_gramian_strided( - ccl_global float *A, int n, const float *ccl_restrict v, float weight, int stride) + ccl_global float *A, int n, ccl_private const float *ccl_restrict v, float weight, int stride) { for (int row = 0; row < n; row++) { for (int col = 0; col <= row; col++) { @@ -151,7 +157,7 @@ ccl_device_inline void math_trimatrix_add_gramian_strided( ccl_device_inline void math_trimatrix_add_gramian(ccl_global float *A, int n, - const float *ccl_restrict v, + ccl_private const float *ccl_restrict v, float weight) { for (int row = 0; row < n; row++) { @@ -244,7 +250,7 @@ ccl_device_inline void math_trimatrix_vec3_solve(ccl_global float *A, * and V will contain the eigenvectors of the original A in its rows (!), * so that A = V^T*D*V. Therefore, the diagonal elements of D are the (sorted) eigenvalues of A. */ -ccl_device void math_matrix_jacobi_eigendecomposition(float *A, +ccl_device void math_matrix_jacobi_eigendecomposition(ccl_private float *A, ccl_global float *V, int n, int v_stride) diff --git a/intern/cycles/util/util_md5.cpp b/intern/cycles/util/md5.cpp similarity index 99% rename from intern/cycles/util/util_md5.cpp rename to intern/cycles/util/md5.cpp index 0df521c2b58..47e489b1aed 100644 --- a/intern/cycles/util/util_md5.cpp +++ b/intern/cycles/util/md5.cpp @@ -23,8 +23,8 @@ /* Minor modifications done to remove some code and change style. */ -#include "util_md5.h" -#include "util_path.h" +#include "util/md5.h" +#include "util/path.h" #include #include diff --git a/intern/cycles/util/util_md5.h b/intern/cycles/util/md5.h similarity index 96% rename from intern/cycles/util/util_md5.h rename to intern/cycles/util/md5.h index 3102a0f4bad..cc7cbef6a49 100644 --- a/intern/cycles/util/util_md5.h +++ b/intern/cycles/util/md5.h @@ -30,8 +30,8 @@ #ifndef __UTIL_MD5_H__ #define __UTIL_MD5_H__ -#include "util/util_string.h" -#include "util/util_types.h" +#include "util/string.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_murmurhash.cpp b/intern/cycles/util/murmurhash.cpp similarity index 97% rename from intern/cycles/util/util_murmurhash.cpp rename to intern/cycles/util/murmurhash.cpp index 5d728769fe9..9ba0a282cc2 100644 --- a/intern/cycles/util/util_murmurhash.cpp +++ b/intern/cycles/util/murmurhash.cpp @@ -23,8 +23,8 @@ #include #include -#include "util/util_algorithm.h" -#include "util/util_murmurhash.h" +#include "util/algorithm.h" +#include "util/murmurhash.h" #if defined(_MSC_VER) # define ROTL32(x, y) _rotl(x, y) diff --git a/intern/cycles/util/util_murmurhash.h b/intern/cycles/util/murmurhash.h similarity index 96% rename from intern/cycles/util/util_murmurhash.h rename to intern/cycles/util/murmurhash.h index 2ec87efd87a..7c303db6ffa 100644 --- a/intern/cycles/util/util_murmurhash.h +++ b/intern/cycles/util/murmurhash.h @@ -17,7 +17,7 @@ #ifndef __UTIL_MURMURHASH_H__ #define __UTIL_MURMURHASH_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_opengl.h b/intern/cycles/util/opengl.h similarity index 100% rename from intern/cycles/util/util_opengl.h rename to intern/cycles/util/opengl.h diff --git a/intern/cycles/util/util_openimagedenoise.h b/intern/cycles/util/openimagedenoise.h similarity index 97% rename from intern/cycles/util/util_openimagedenoise.h rename to intern/cycles/util/openimagedenoise.h index 898c634141e..cc7b14ae18f 100644 --- a/intern/cycles/util/util_openimagedenoise.h +++ b/intern/cycles/util/openimagedenoise.h @@ -21,7 +21,7 @@ # include #endif -#include "util_system.h" +#include "util/system.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_openvdb.h b/intern/cycles/util/openvdb.h similarity index 100% rename from intern/cycles/util/util_openvdb.h rename to intern/cycles/util/openvdb.h diff --git a/intern/cycles/util/util_optimization.h b/intern/cycles/util/optimization.h similarity index 100% rename from intern/cycles/util/util_optimization.h rename to intern/cycles/util/optimization.h diff --git a/intern/cycles/util/util_param.h b/intern/cycles/util/param.h similarity index 100% rename from intern/cycles/util/util_param.h rename to intern/cycles/util/param.h diff --git a/intern/cycles/util/util_path.cpp b/intern/cycles/util/path.cpp similarity index 71% rename from intern/cycles/util/util_path.cpp rename to intern/cycles/util/path.cpp index 8905c8bc7f0..5704c4ef8ef 100644 --- a/intern/cycles/util/util_path.cpp +++ b/intern/cycles/util/path.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include "util/util_path.h" -#include "util/util_md5.h" -#include "util/util_string.h" +#include "util/path.h" +#include "util/md5.h" +#include "util/string.h" #include #include @@ -44,8 +44,8 @@ OIIO_NAMESPACE_USING # include #endif -#include "util/util_map.h" -#include "util/util_windows.h" +#include "util/map.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN @@ -66,6 +66,7 @@ typedef struct stat path_stat_t; static string cached_path = ""; static string cached_user_path = ""; +static string cached_temp_path = ""; static string cached_xdg_cache_path = ""; namespace { @@ -335,10 +336,11 @@ static string path_xdg_cache_get() } #endif -void path_init(const string &path, const string &user_path) +void path_init(const string &path, const string &user_path, const string &temp_path) { cached_path = path; cached_user_path = user_path; + cached_temp_path = temp_path; #ifdef _MSC_VER // workaround for https://svn.boost.org/trac/boost/ticket/6320 @@ -382,6 +384,15 @@ string path_cache_get(const string &sub) #endif } +string path_temp_get(const string &sub) +{ + if (cached_temp_path == "") { + cached_temp_path = Filesystem::temp_directory_path(); + } + + return path_join(cached_temp_path, sub); +} + #if defined(__linux__) || defined(__APPLE__) string path_xdg_home_get(const string &sub = ""); #endif @@ -739,177 +750,6 @@ bool path_remove(const string &path) return remove(path.c_str()) == 0; } -struct SourceReplaceState { - typedef map ProcessedMapping; - /* Base director for all relative include headers. */ - string base; - /* Result of processed files. */ - ProcessedMapping processed_files; - /* Set of files which are considered "precompiled" and which are replaced - * with and empty string on a subsequent occurrence in include statement. - */ - set precompiled_headers; -}; - -static string path_source_replace_includes_recursive(const string &source, - const string &source_filepath, - SourceReplaceState *state); - -static string line_directive(const SourceReplaceState &state, const string &path, const int line) -{ - string unescaped_path = path; - /* First we make path relative. */ - if (string_startswith(unescaped_path, state.base.c_str())) { - const string base_file = path_filename(state.base); - const size_t base_len = state.base.length(); - unescaped_path = base_file + - unescaped_path.substr(base_len, unescaped_path.length() - base_len); - } - /* Second, we replace all unsafe characters. */ - const size_t length = unescaped_path.length(); - string escaped_path = ""; - for (size_t i = 0; i < length; ++i) { - const char ch = unescaped_path[i]; - if (strchr("\"\'\?\\", ch) != NULL) { - escaped_path += "\\"; - } - escaped_path += ch; - } - /* TODO(sergey): Check whether using std::to_string combined with several - * concatenation operations is any faster. - */ - return string_printf("#line %d \"%s\"", line, escaped_path.c_str()); -} - -static string path_source_handle_preprocessor(const string &preprocessor_line, - const string &source_filepath, - const size_t line_number, - SourceReplaceState *state) -{ - string result = preprocessor_line; - string token = string_strip(preprocessor_line.substr(1, preprocessor_line.size() - 1)); - if (string_startswith(token, "include")) { - token = string_strip(token.substr(7, token.size() - 7)); - if (token[0] == '"') { - const size_t n_start = 1; - const size_t n_end = token.find("\"", n_start); - const string filename = token.substr(n_start, n_end - n_start); - const bool is_precompiled = string_endswith(token, "// PRECOMPILED"); - string filepath = path_join(state->base, filename); - if (!path_exists(filepath)) { - filepath = path_join(path_dirname(source_filepath), filename); - } - if (is_precompiled) { - state->precompiled_headers.insert(filepath); - } - string text; - if (path_read_text(filepath, text)) { - text = path_source_replace_includes_recursive(text, filepath, state); - /* Use line directives for better error messages. */ - result = line_directive(*state, filepath, 1) + "\n" + text + "\n" + - line_directive(*state, source_filepath, line_number + 1); - } - } - } - return result; -} - -/* Our own little c preprocessor that replaces #includes with the file - * contents, to work around issue of OpenCL drivers not supporting - * include paths with spaces in them. - */ -static string path_source_replace_includes_recursive(const string &source, - const string &source_filepath, - SourceReplaceState *state) -{ - /* Try to re-use processed file without spending time on replacing all - * include directives again. - */ - SourceReplaceState::ProcessedMapping::iterator replaced_file = state->processed_files.find( - source_filepath); - if (replaced_file != state->processed_files.end()) { - if (state->precompiled_headers.find(source_filepath) != state->precompiled_headers.end()) { - return ""; - } - return replaced_file->second; - } - /* Perform full file processing. */ - string result = ""; - const size_t source_length = source.length(); - size_t index = 0; - /* Information about where we are in the source. */ - size_t line_number = 0, column_number = 1; - /* Currently gathered non-preprocessor token. - * Store as start/length rather than token itself to avoid overhead of - * memory re-allocations on each character concatenation. - */ - size_t token_start = 0, token_length = 0; - /* Denotes whether we're inside of preprocessor line, together with - * preprocessor line itself. - * - * TODO(sergey): Investigate whether using token start/end position - * gives measurable speedup. - */ - bool inside_preprocessor = false; - string preprocessor_line = ""; - /* Actual loop over the whole source. */ - while (index < source_length) { - const char ch = source[index]; - if (ch == '\n') { - if (inside_preprocessor) { - result += path_source_handle_preprocessor( - preprocessor_line, source_filepath, line_number, state); - /* Start gathering net part of the token. */ - token_start = index; - token_length = 0; - } - inside_preprocessor = false; - preprocessor_line = ""; - column_number = 0; - ++line_number; - } - else if (ch == '#' && column_number == 1 && !inside_preprocessor) { - /* Append all possible non-preprocessor token to the result. */ - if (token_length != 0) { - result.append(source, token_start, token_length); - token_start = index; - token_length = 0; - } - inside_preprocessor = true; - } - if (inside_preprocessor) { - preprocessor_line += ch; - } - else { - ++token_length; - } - ++index; - ++column_number; - } - /* Append possible tokens which happened before special events handled - * above. - */ - if (token_length != 0) { - result.append(source, token_start, token_length); - } - if (inside_preprocessor) { - result += path_source_handle_preprocessor( - preprocessor_line, source_filepath, line_number, state); - } - /* Store result for further reuse. */ - state->processed_files[source_filepath] = result; - return result; -} - -string path_source_replace_includes(const string &source, - const string &path, - const string &source_filename) -{ - SourceReplaceState state; - state.base = path; - return path_source_replace_includes_recursive(source, path_join(path, source_filename), &state); -} - FILE *path_fopen(const string &path, const string &mode) { #ifdef _WIN32 diff --git a/intern/cycles/util/util_path.h b/intern/cycles/util/path.h similarity index 87% rename from intern/cycles/util/util_path.h rename to intern/cycles/util/path.h index 7a83c2135a4..a1394555302 100644 --- a/intern/cycles/util/util_path.h +++ b/intern/cycles/util/path.h @@ -24,17 +24,18 @@ #include -#include "util/util_set.h" -#include "util/util_string.h" -#include "util/util_types.h" -#include "util/util_vector.h" +#include "util/set.h" +#include "util/string.h" +#include "util/types.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN /* program paths */ -void path_init(const string &path = "", const string &user_path = ""); +void path_init(const string &path = "", const string &user_path = "", const string &tmp_path = ""); string path_get(const string &sub = ""); string path_user_get(const string &sub = ""); +string path_temp_get(const string &sub = ""); string path_cache_get(const string &sub = ""); /* path string manipulation */ @@ -65,11 +66,6 @@ bool path_read_text(const string &path, string &text); /* File manipulation. */ bool path_remove(const string &path); -/* source code utility */ -string path_source_replace_includes(const string &source, - const string &path, - const string &source_filename = ""); - /* cache utility */ void path_cache_clear_except(const string &name, const set &except); diff --git a/intern/cycles/util/util_profiling.cpp b/intern/cycles/util/profiling.cpp similarity index 89% rename from intern/cycles/util/util_profiling.cpp rename to intern/cycles/util/profiling.cpp index 073b09f719f..55b35b7320f 100644 --- a/intern/cycles/util/util_profiling.cpp +++ b/intern/cycles/util/profiling.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_profiling.h" -#include "util/util_algorithm.h" -#include "util/util_foreach.h" -#include "util/util_set.h" +#include "util/profiling.h" +#include "util/algorithm.h" +#include "util/foreach.h" +#include "util/set.h" CCL_NAMESPACE_BEGIN @@ -48,13 +48,7 @@ void Profiler::run() } if (cur_shader >= 0 && cur_shader < shader_samples.size()) { - /* Only consider the active shader during events whose runtime significantly depends on it. - */ - if (((cur_event >= PROFILING_SHADER_EVAL) && (cur_event <= PROFILING_SUBSURFACE)) || - ((cur_event >= PROFILING_CLOSURE_EVAL) && - (cur_event <= PROFILING_CLOSURE_VOLUME_SAMPLE))) { - shader_samples[cur_shader]++; - } + shader_samples[cur_shader]++; } if (cur_object >= 0 && cur_object < object_samples.size()) { diff --git a/intern/cycles/util/util_profiling.h b/intern/cycles/util/profiling.h similarity index 70% rename from intern/cycles/util/util_profiling.h rename to intern/cycles/util/profiling.h index ceec08ed894..b30aac90879 100644 --- a/intern/cycles/util/util_profiling.h +++ b/intern/cycles/util/profiling.h @@ -19,47 +19,39 @@ #include -#include "util/util_map.h" -#include "util/util_thread.h" -#include "util/util_vector.h" +#include "util/map.h" +#include "util/thread.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN enum ProfilingEvent : uint32_t { PROFILING_UNKNOWN, PROFILING_RAY_SETUP, - PROFILING_PATH_INTEGRATE, - PROFILING_SCENE_INTERSECT, - PROFILING_INDIRECT_EMISSION, - PROFILING_VOLUME, - PROFILING_SHADER_SETUP, - PROFILING_SHADER_EVAL, - PROFILING_SHADER_APPLY, - PROFILING_AO, - PROFILING_SUBSURFACE, - PROFILING_CONNECT_LIGHT, - PROFILING_SURFACE_BOUNCE, - PROFILING_WRITE_RESULT, - PROFILING_INTERSECT, - PROFILING_INTERSECT_LOCAL, - PROFILING_INTERSECT_SHADOW_ALL, - PROFILING_INTERSECT_VOLUME, - PROFILING_INTERSECT_VOLUME_ALL, + PROFILING_INTERSECT_CLOSEST, + PROFILING_INTERSECT_SUBSURFACE, + PROFILING_INTERSECT_SHADOW, + PROFILING_INTERSECT_VOLUME_STACK, - PROFILING_CLOSURE_EVAL, - PROFILING_CLOSURE_SAMPLE, - PROFILING_CLOSURE_VOLUME_EVAL, - PROFILING_CLOSURE_VOLUME_SAMPLE, + PROFILING_SHADE_SURFACE_SETUP, + PROFILING_SHADE_SURFACE_EVAL, + PROFILING_SHADE_SURFACE_DIRECT_LIGHT, + PROFILING_SHADE_SURFACE_INDIRECT_LIGHT, + PROFILING_SHADE_SURFACE_AO, + PROFILING_SHADE_SURFACE_PASSES, - PROFILING_DENOISING, - PROFILING_DENOISING_CONSTRUCT_TRANSFORM, - PROFILING_DENOISING_RECONSTRUCT, - PROFILING_DENOISING_DIVIDE_SHADOW, - PROFILING_DENOISING_NON_LOCAL_MEANS, - PROFILING_DENOISING_COMBINE_HALVES, - PROFILING_DENOISING_GET_FEATURE, - PROFILING_DENOISING_DETECT_OUTLIERS, + PROFILING_SHADE_VOLUME_SETUP, + PROFILING_SHADE_VOLUME_INTEGRATE, + PROFILING_SHADE_VOLUME_DIRECT_LIGHT, + PROFILING_SHADE_VOLUME_INDIRECT_LIGHT, + + PROFILING_SHADE_SHADOW_SETUP, + PROFILING_SHADE_SHADOW_SURFACE, + PROFILING_SHADE_SHADOW_VOLUME, + + PROFILING_SHADE_LIGHT_SETUP, + PROFILING_SHADE_LIGHT_EVAL, PROFILING_NUM_EVENTS, }; @@ -136,39 +128,53 @@ class ProfilingHelper { state->event = event; } - inline void set_event(ProfilingEvent event) - { - state->event = event; - } - - inline void set_shader(int shader) - { - state->shader = shader; - if (state->active) { - assert(shader < state->shader_hits.size()); - state->shader_hits[shader]++; - } - } - - inline void set_object(int object) - { - state->object = object; - if (state->active) { - assert(object < state->object_hits.size()); - state->object_hits[object]++; - } - } - ~ProfilingHelper() { state->event = previous_event; } - private: + inline void set_event(ProfilingEvent event) + { + state->event = event; + } + + protected: ProfilingState *state; uint32_t previous_event; }; +class ProfilingWithShaderHelper : public ProfilingHelper { + public: + ProfilingWithShaderHelper(ProfilingState *state, ProfilingEvent event) + : ProfilingHelper(state, event) + { + } + + ~ProfilingWithShaderHelper() + { + state->object = -1; + state->shader = -1; + } + + inline void set_shader(int object, int shader) + { + if (state->active) { + state->shader = shader; + state->object = object; + + if (shader >= 0) { + assert(shader < state->shader_hits.size()); + state->shader_hits[shader]++; + } + + if (object >= 0) { + assert(object < state->object_hits.size()); + state->object_hits[object]++; + } + } + } +}; + CCL_NAMESPACE_END #endif /* __UTIL_PROFILING_H__ */ diff --git a/intern/cycles/util/util_progress.h b/intern/cycles/util/progress.h similarity index 89% rename from intern/cycles/util/util_progress.h rename to intern/cycles/util/progress.h index 26534a29dfe..4b0ff08aa7e 100644 --- a/intern/cycles/util/util_progress.h +++ b/intern/cycles/util/progress.h @@ -23,10 +23,10 @@ * update notifications from a job running in another thread. All methods * except for the constructor/destructor are thread safe. */ -#include "util/util_function.h" -#include "util/util_string.h" -#include "util/util_thread.h" -#include "util/util_time.h" +#include "util/function.h" +#include "util/string.h" +#include "util/thread.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN @@ -46,7 +46,6 @@ class Progress { substatus = ""; sync_status = ""; sync_substatus = ""; - kernel_status = ""; update_cb = function_null; cancel = false; cancel_message = ""; @@ -87,7 +86,6 @@ class Progress { substatus = ""; sync_status = ""; sync_substatus = ""; - kernel_status = ""; cancel = false; cancel_message = ""; error = false; @@ -102,7 +100,7 @@ class Progress { cancel = true; } - bool get_cancel() + bool get_cancel() const { if (!cancel && cancel_cb) cancel_cb(); @@ -110,7 +108,7 @@ class Progress { return cancel; } - string get_cancel_message() + string get_cancel_message() const { thread_scoped_lock lock(progress_mutex); return cancel_message; @@ -132,12 +130,12 @@ class Progress { cancel = true; } - bool get_error() + bool get_error() const { return error; } - string get_error_message() + string get_error_message() const { thread_scoped_lock lock(progress_mutex); return error_message; @@ -170,7 +168,7 @@ class Progress { } } - void get_time(double &total_time_, double &render_time_) + void get_time(double &total_time_, double &render_time_) const { thread_scoped_lock lock(progress_mutex); @@ -202,7 +200,7 @@ class Progress { total_pixel_samples = total_pixel_samples_; } - float get_progress() + float get_progress() const { thread_scoped_lock lock(progress_mutex); @@ -238,7 +236,7 @@ class Progress { } } - int get_current_sample() + int get_current_sample() const { thread_scoped_lock lock(progress_mutex); /* Note that the value here always belongs to the last tile that updated, @@ -246,13 +244,13 @@ class Progress { return current_tile_sample; } - int get_rendered_tiles() + int get_rendered_tiles() const { thread_scoped_lock lock(progress_mutex); return rendered_tiles; } - int get_denoised_tiles() + int get_denoised_tiles() const { thread_scoped_lock lock(progress_mutex); return denoised_tiles; @@ -302,7 +300,7 @@ class Progress { set_update(); } - void get_status(string &status_, string &substatus_) + void get_status(string &status_, string &substatus_) const { thread_scoped_lock lock(progress_mutex); @@ -316,24 +314,6 @@ class Progress { } } - /* kernel status */ - - void set_kernel_status(const string &kernel_status_) - { - { - thread_scoped_lock lock(progress_mutex); - kernel_status = kernel_status_; - } - - set_update(); - } - - void get_kernel_status(string &kernel_status_) - { - thread_scoped_lock lock(progress_mutex); - kernel_status_ = kernel_status; - } - /* callback */ void set_update() @@ -350,8 +330,8 @@ class Progress { } protected: - thread_mutex progress_mutex; - thread_mutex update_mutex; + mutable thread_mutex progress_mutex; + mutable thread_mutex update_mutex; function update_cb; function cancel_cb; @@ -378,8 +358,6 @@ class Progress { string sync_status; string sync_substatus; - string kernel_status; - volatile bool cancel; string cancel_message; diff --git a/intern/cycles/util/util_projection.h b/intern/cycles/util/projection.h similarity index 95% rename from intern/cycles/util/util_projection.h rename to intern/cycles/util/projection.h index 9c7e0061c82..8d822a3777d 100644 --- a/intern/cycles/util/util_projection.h +++ b/intern/cycles/util/projection.h @@ -17,7 +17,7 @@ #ifndef __UTIL_PROJECTION_H__ #define __UTIL_PROJECTION_H__ -#include "util/util_transform.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN @@ -45,7 +45,8 @@ typedef struct PerspectiveMotionTransform { /* Functions */ -ccl_device_inline float3 transform_perspective(const ProjectionTransform *t, const float3 a) +ccl_device_inline float3 transform_perspective(ccl_private const ProjectionTransform *t, + const float3 a) { float4 b = make_float4(a.x, a.y, a.z, 1.0f); float3 c = make_float3(dot(t->x, b), dot(t->y, b), dot(t->z, b)); @@ -54,7 +55,7 @@ ccl_device_inline float3 transform_perspective(const ProjectionTransform *t, con return (w != 0.0f) ? c / w : zero_float3(); } -ccl_device_inline float3 transform_perspective_direction(const ProjectionTransform *t, +ccl_device_inline float3 transform_perspective_direction(ccl_private const ProjectionTransform *t, const float3 a) { float3 c = make_float3(a.x * t->x.x + a.y * t->x.y + a.z * t->x.z, diff --git a/intern/cycles/util/util_queue.h b/intern/cycles/util/queue.h similarity index 100% rename from intern/cycles/util/util_queue.h rename to intern/cycles/util/queue.h diff --git a/intern/cycles/util/util_rect.h b/intern/cycles/util/rect.h similarity index 88% rename from intern/cycles/util/util_rect.h rename to intern/cycles/util/rect.h index 36f02a01f7b..79d64b917b7 100644 --- a/intern/cycles/util/util_rect.h +++ b/intern/cycles/util/rect.h @@ -17,7 +17,7 @@ #ifndef __UTIL_RECT_H__ #define __UTIL_RECT_H__ -#include "util/util_types.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN @@ -54,7 +54,10 @@ ccl_device_inline int coord_to_local_index(int4 rect, int x, int y) /* Finds the coordinates of a pixel given by its row-major index in the rect, * and returns whether the pixel is inside it. */ -ccl_device_inline bool local_index_to_coord(int4 rect, int idx, int *x, int *y) +ccl_device_inline bool local_index_to_coord(int4 rect, + int idx, + ccl_private int *x, + ccl_private int *y) { int w = rect.z - rect.x; *x = (idx % w) + rect.x; diff --git a/intern/cycles/util/util_semaphore.h b/intern/cycles/util/semaphore.h similarity index 97% rename from intern/cycles/util/util_semaphore.h rename to intern/cycles/util/semaphore.h index d995b0732b8..8da8a232ba2 100644 --- a/intern/cycles/util/util_semaphore.h +++ b/intern/cycles/util/semaphore.h @@ -17,7 +17,7 @@ #ifndef __UTIL_SEMAPHORE_H__ #define __UTIL_SEMAPHORE_H__ -#include "util/util_thread.h" +#include "util/thread.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_set.h b/intern/cycles/util/set.h similarity index 100% rename from intern/cycles/util/util_set.h rename to intern/cycles/util/set.h diff --git a/intern/cycles/util/util_simd.cpp b/intern/cycles/util/simd.cpp similarity index 98% rename from intern/cycles/util/util_simd.cpp rename to intern/cycles/util/simd.cpp index 861dcf1fe36..089444bb6cc 100644 --- a/intern/cycles/util/util_simd.cpp +++ b/intern/cycles/util/simd.cpp @@ -18,7 +18,7 @@ #if (defined(WITH_KERNEL_SSE2)) || (defined(WITH_KERNEL_NATIVE) && defined(__SSE2__)) # define __KERNEL_SSE2__ -# include "util/util_simd.h" +# include "util/simd.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_simd.h b/intern/cycles/util/simd.h similarity index 98% rename from intern/cycles/util/util_simd.h rename to intern/cycles/util/simd.h index 8e8caa98a1b..cc4950891d0 100644 --- a/intern/cycles/util/util_simd.h +++ b/intern/cycles/util/simd.h @@ -21,7 +21,7 @@ #include #include -#include "util/util_defines.h" +#include "util/defines.h" /* SSE Intrinsics includes * @@ -30,7 +30,7 @@ * MinGW64 has conflicting declarations for these SSE headers in . * Since we can't avoid including , better only include that */ #if defined(FREE_WINDOWS64) -# include "util/util_windows.h" +# include "util/windows.h" #elif defined(_MSC_VER) # include #elif (defined(__x86_64__) || defined(__i386__)) @@ -61,14 +61,14 @@ static struct TrueTy { { return true; } -} True ccl_maybe_unused; +} True ccl_attr_maybe_unused; static struct FalseTy { __forceinline operator bool() const { return false; } -} False ccl_maybe_unused; +} False ccl_attr_maybe_unused; static struct ZeroTy { __forceinline operator float() const @@ -79,7 +79,7 @@ static struct ZeroTy { { return 0; } -} zero ccl_maybe_unused; +} zero ccl_attr_maybe_unused; static struct OneTy { __forceinline operator float() const @@ -90,7 +90,7 @@ static struct OneTy { { return 1; } -} one ccl_maybe_unused; +} one ccl_attr_maybe_unused; static struct NegInfTy { __forceinline operator float() const @@ -101,7 +101,7 @@ static struct NegInfTy { { return std::numeric_limits::min(); } -} neg_inf ccl_maybe_unused; +} neg_inf ccl_attr_maybe_unused; static struct PosInfTy { __forceinline operator float() const @@ -112,10 +112,10 @@ static struct PosInfTy { { return std::numeric_limits::max(); } -} inf ccl_maybe_unused, pos_inf ccl_maybe_unused; +} inf ccl_attr_maybe_unused, pos_inf ccl_attr_maybe_unused; static struct StepTy { -} step ccl_maybe_unused; +} step ccl_attr_maybe_unused; #endif diff --git a/intern/cycles/util/util_sseb.h b/intern/cycles/util/sseb.h similarity index 100% rename from intern/cycles/util/util_sseb.h rename to intern/cycles/util/sseb.h diff --git a/intern/cycles/util/util_ssef.h b/intern/cycles/util/ssef.h similarity index 99% rename from intern/cycles/util/util_ssef.h rename to intern/cycles/util/ssef.h index 0c81ed87553..ea5e78b54d2 100644 --- a/intern/cycles/util/util_ssef.h +++ b/intern/cycles/util/ssef.h @@ -18,7 +18,7 @@ #ifndef __UTIL_SSEF_H__ #define __UTIL_SSEF_H__ -#include "util_ssei.h" +#include "util/ssei.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/ssei.h similarity index 100% rename from intern/cycles/util/util_ssei.h rename to intern/cycles/util/ssei.h diff --git a/intern/cycles/util/util_stack_allocator.h b/intern/cycles/util/stack_allocator.h similarity index 100% rename from intern/cycles/util/util_stack_allocator.h rename to intern/cycles/util/stack_allocator.h diff --git a/intern/cycles/util/util_static_assert.h b/intern/cycles/util/static_assert.h similarity index 92% rename from intern/cycles/util/util_static_assert.h rename to intern/cycles/util/static_assert.h index d809f2e06d7..7df52d462b7 100644 --- a/intern/cycles/util/util_static_assert.h +++ b/intern/cycles/util/static_assert.h @@ -24,9 +24,9 @@ CCL_NAMESPACE_BEGIN -#if defined(__KERNEL_OPENCL__) || defined(CYCLES_CUBIN_CC) +#if defined(CYCLES_CUBIN_CC) # define static_assert(statement, message) -#endif /* __KERNEL_OPENCL__ */ +#endif #define static_assert_align(st, align) \ static_assert((sizeof(st) % (align) == 0), "Structure must be strictly aligned") // NOLINT diff --git a/intern/cycles/util/util_stats.h b/intern/cycles/util/stats.h similarity index 94% rename from intern/cycles/util/util_stats.h rename to intern/cycles/util/stats.h index 15cf836de3c..590973f1cbc 100644 --- a/intern/cycles/util/util_stats.h +++ b/intern/cycles/util/stats.h @@ -17,8 +17,8 @@ #ifndef __UTIL_STATS_H__ #define __UTIL_STATS_H__ -#include "util/util_atomic.h" -#include "util/util_profiling.h" +#include "util/atomic.h" +#include "util/profiling.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_string.cpp b/intern/cycles/util/string.cpp similarity index 87% rename from intern/cycles/util/util_string.cpp rename to intern/cycles/util/string.cpp index 4dfebf14923..b98272f7759 100644 --- a/intern/cycles/util/util_string.cpp +++ b/intern/cycles/util/string.cpp @@ -17,9 +17,12 @@ #include #include -#include "util/util_foreach.h" -#include "util/util_string.h" -#include "util/util_windows.h" +#include +#include + +#include "util/foreach.h" +#include "util/string.h" +#include "util/windows.h" #ifdef _WIN32 # ifndef vsnprintf @@ -107,24 +110,26 @@ void string_split(vector &tokens, } } -bool string_startswith(const string &s, const char *start) +bool string_startswith(const string_view s, const string_view start) { - size_t len = strlen(start); + const size_t len = start.size(); - if (len > s.size()) - return 0; - else - return strncmp(s.c_str(), start, len) == 0; + if (len > s.size()) { + return false; + } + + return strncmp(s.c_str(), start.data(), len) == 0; } -bool string_endswith(const string &s, const string &end) +bool string_endswith(const string_view s, const string_view end) { - size_t len = end.length(); + const size_t len = end.size(); - if (len > s.size()) - return 0; - else - return s.compare(s.length() - len, len, end) == 0; + if (len > s.size()) { + return false; + } + + return strncmp(s.c_str() + s.size() - len, end.data(), len) == 0; } string string_strip(const string &s) @@ -148,7 +153,7 @@ string string_remove_trademark(const string &s) { string result = s; - /* Special case, so we don;t leave sequential spaces behind. */ + /* Special case, so we don't leave sequential spaces behind. */ /* TODO(sergey): Consider using regex perhaps? */ string_replace(result, " (TM)", ""); string_replace(result, " (R)", ""); @@ -172,6 +177,13 @@ string to_string(const char *str) return string(str); } +string string_to_lower(const string &s) +{ + string r = s; + std::transform(r.begin(), r.end(), r.begin(), [](char c) { return std::tolower(c); }); + return r; +} + /* Wide char strings helpers for Windows. */ #ifdef _WIN32 diff --git a/intern/cycles/util/util_string.h b/intern/cycles/util/string.h similarity index 83% rename from intern/cycles/util/util_string.h rename to intern/cycles/util/string.h index f2272819b2f..cc20a6df120 100644 --- a/intern/cycles/util/util_string.h +++ b/intern/cycles/util/string.h @@ -14,14 +14,18 @@ * limitations under the License. */ -#ifndef __UTIL_STRING_H__ -#define __UTIL_STRING_H__ +#pragma once #include #include #include -#include "util/util_vector.h" +/* Use string view implementation from OIIO. + * Ideally, need to switch to `std::string_view`, but this first requires getting rid of using + * namespace OIIO as it causes symbol collision. */ +#include + +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -31,6 +35,8 @@ using std::string; using std::stringstream; using std::to_string; +using OIIO::string_view; + #ifdef __GNUC__ # define PRINTF_ATTRIBUTE __attribute__((format(printf, 1, 2))) #else @@ -45,12 +51,13 @@ void string_split(vector &tokens, const string &separators = "\t ", bool skip_empty_tokens = true); void string_replace(string &haystack, const string &needle, const string &other); -bool string_startswith(const string &s, const char *start); -bool string_endswith(const string &s, const string &end); +bool string_startswith(string_view s, string_view start); +bool string_endswith(string_view s, string_view end); string string_strip(const string &s); string string_remove_trademark(const string &s); string string_from_bool(const bool var); string to_string(const char *str); +string string_to_lower(const string &s); /* Wide char strings are only used on Windows to deal with non-ASCII * characters in file names and such. No reason to use such strings @@ -72,5 +79,3 @@ string string_human_readable_size(size_t size); string string_human_readable_number(size_t num); CCL_NAMESPACE_END - -#endif /* __UTIL_STRING_H__ */ diff --git a/intern/cycles/util/util_system.cpp b/intern/cycles/util/system.cpp similarity index 97% rename from intern/cycles/util/util_system.cpp rename to intern/cycles/util/system.cpp index b010881058b..f12e15e756f 100644 --- a/intern/cycles/util/util_system.cpp +++ b/intern/cycles/util/system.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "util/util_system.h" +#include "util/system.h" -#include "util/util_logging.h" -#include "util/util_string.h" -#include "util/util_types.h" +#include "util/log.h" +#include "util/string.h" +#include "util/types.h" #include @@ -29,7 +29,7 @@ OIIO_NAMESPACE_USING # if (!defined(FREE_WINDOWS)) # include # endif -# include "util_windows.h" +# include "util/windows.h" #elif defined(__APPLE__) # include # include @@ -403,4 +403,13 @@ size_t system_physical_ram() #endif } +uint64_t system_self_process_id() +{ +#ifdef _WIN32 + return GetCurrentProcessId(); +#else + return getpid(); +#endif +} + CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_system.h b/intern/cycles/util/system.h similarity index 93% rename from intern/cycles/util/util_system.h rename to intern/cycles/util/system.h index c4db8b74339..425c7255cbe 100644 --- a/intern/cycles/util/util_system.h +++ b/intern/cycles/util/system.h @@ -17,8 +17,8 @@ #ifndef __UTIL_SYSTEM_H__ #define __UTIL_SYSTEM_H__ -#include "util/util_string.h" -#include "util/util_vector.h" +#include "util/string.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN @@ -65,6 +65,9 @@ size_t system_physical_ram(); /* Start a new process of the current application with the given arguments. */ bool system_call_self(const vector &args); +/* Get identifier of the currently running process. */ +uint64_t system_self_process_id(); + CCL_NAMESPACE_END #endif /* __UTIL_SYSTEM_H__ */ diff --git a/intern/cycles/util/util_task.cpp b/intern/cycles/util/task.cpp similarity index 96% rename from intern/cycles/util/util_task.cpp rename to intern/cycles/util/task.cpp index 949ba0a7b4d..ce61bf8d6c4 100644 --- a/intern/cycles/util/util_task.cpp +++ b/intern/cycles/util/task.cpp @@ -14,11 +14,11 @@ * limitations under the License. */ -#include "util/util_task.h" -#include "util/util_foreach.h" -#include "util/util_logging.h" -#include "util/util_system.h" -#include "util/util_time.h" +#include "util/task.h" +#include "util/foreach.h" +#include "util/log.h" +#include "util/system.h" +#include "util/time.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_task.h b/intern/cycles/util/task.h similarity index 96% rename from intern/cycles/util/util_task.h rename to intern/cycles/util/task.h index ec45dfa8040..1a8f512b83a 100644 --- a/intern/cycles/util/util_task.h +++ b/intern/cycles/util/task.h @@ -17,11 +17,11 @@ #ifndef __UTIL_TASK_H__ #define __UTIL_TASK_H__ -#include "util/util_list.h" -#include "util/util_string.h" -#include "util/util_tbb.h" -#include "util/util_thread.h" -#include "util/util_vector.h" +#include "util/list.h" +#include "util/string.h" +#include "util/tbb.h" +#include "util/thread.h" +#include "util/vector.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_tbb.h b/intern/cycles/util/tbb.h similarity index 95% rename from intern/cycles/util/util_tbb.h rename to intern/cycles/util/tbb.h index 73e0f92d19c..6fc3b8daad3 100644 --- a/intern/cycles/util/util_tbb.h +++ b/intern/cycles/util/tbb.h @@ -19,10 +19,11 @@ /* TBB includes , do it ourselves first so we are sure * WIN32_LEAN_AND_MEAN and similar are defined beforehand. */ -#include "util_windows.h" +#include "util/windows.h" #include #include +#include #include #include diff --git a/intern/cycles/util/util_texture.h b/intern/cycles/util/texture.h similarity index 97% rename from intern/cycles/util/util_texture.h rename to intern/cycles/util/texture.h index 71bf9c65911..5e37b79e340 100644 --- a/intern/cycles/util/util_texture.h +++ b/intern/cycles/util/texture.h @@ -17,7 +17,7 @@ #ifndef __UTIL_TEXTURE_H__ #define __UTIL_TEXTURE_H__ -#include "util_transform.h" +#include "util/transform.h" CCL_NAMESPACE_BEGIN @@ -85,8 +85,6 @@ typedef struct TextureInfo { uint64_t data; /* Data Type */ uint data_type; - /* Buffer number for OpenCL. */ - uint cl_buffer; /* Interpolation and extension type. */ uint interpolation, extension; /* Dimensions. */ diff --git a/intern/cycles/util/util_thread.cpp b/intern/cycles/util/thread.cpp similarity index 94% rename from intern/cycles/util/util_thread.cpp rename to intern/cycles/util/thread.cpp index cccde5ae7d5..24a0600425d 100644 --- a/intern/cycles/util/util_thread.cpp +++ b/intern/cycles/util/thread.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ -#include "util/util_thread.h" +#include "util/thread.h" -#include "util/util_system.h" -#include "util/util_windows.h" +#include "util/system.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_thread.h b/intern/cycles/util/thread.h similarity index 97% rename from intern/cycles/util/util_thread.h rename to intern/cycles/util/thread.h index 29f9becbefe..09686e4b23f 100644 --- a/intern/cycles/util/util_thread.h +++ b/intern/cycles/util/thread.h @@ -24,7 +24,7 @@ #include #ifdef _WIN32 -# include "util_windows.h" +# include "util/windows.h" #else # include #endif @@ -33,7 +33,7 @@ * functionality requires RTTI, which is disabled for OSL kernel. */ #include -#include "util/util_function.h" +#include "util/function.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_time.cpp b/intern/cycles/util/time.cpp similarity index 96% rename from intern/cycles/util/util_time.cpp rename to intern/cycles/util/time.cpp index 1641395d07e..62d14b063be 100644 --- a/intern/cycles/util/util_time.cpp +++ b/intern/cycles/util/time.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "util/util_time.h" +#include "util/time.h" #include @@ -23,9 +23,9 @@ # include #endif -#include "util/util_math.h" -#include "util/util_string.h" -#include "util/util_windows.h" +#include "util/math.h" +#include "util/string.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_time.h b/intern/cycles/util/time.h similarity index 96% rename from intern/cycles/util/util_time.h rename to intern/cycles/util/time.h index a82d400a0d7..380921664e8 100644 --- a/intern/cycles/util/util_time.h +++ b/intern/cycles/util/time.h @@ -17,8 +17,8 @@ #ifndef __UTIL_TIME_H__ #define __UTIL_TIME_H__ -#include "util/util_function.h" -#include "util/util_string.h" +#include "util/function.h" +#include "util/string.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_transform.cpp b/intern/cycles/util/transform.cpp similarity index 98% rename from intern/cycles/util/util_transform.cpp rename to intern/cycles/util/transform.cpp index e8233b7fe6d..bd990cb0f79 100644 --- a/intern/cycles/util/util_transform.cpp +++ b/intern/cycles/util/transform.cpp @@ -46,11 +46,11 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "util/util_transform.h" -#include "util/util_projection.h" +#include "util/transform.h" +#include "util/projection.h" -#include "util/util_boundbox.h" -#include "util/util_math.h" +#include "util/boundbox.h" +#include "util/math.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_transform.h b/intern/cycles/util/transform.h similarity index 88% rename from intern/cycles/util/util_transform.h rename to intern/cycles/util/transform.h index f79eac4cbcf..7bfe747fcfb 100644 --- a/intern/cycles/util/util_transform.h +++ b/intern/cycles/util/transform.h @@ -21,8 +21,8 @@ # include #endif -#include "util/util_math.h" -#include "util/util_types.h" +#include "util/math.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN @@ -53,7 +53,7 @@ typedef struct DecomposedTransform { /* Functions */ -ccl_device_inline float3 transform_point(const Transform *t, const float3 a) +ccl_device_inline float3 transform_point(ccl_private const Transform *t, const float3 a) { /* TODO(sergey): Disabled for now, causes crashes in certain cases. */ #if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__) @@ -82,7 +82,7 @@ ccl_device_inline float3 transform_point(const Transform *t, const float3 a) #endif } -ccl_device_inline float3 transform_direction(const Transform *t, const float3 a) +ccl_device_inline float3 transform_direction(ccl_private const Transform *t, const float3 a) { #if defined(__KERNEL_SSE__) && defined(__KERNEL_SSE2__) ssef x, y, z, w, aa; @@ -108,7 +108,8 @@ ccl_device_inline float3 transform_direction(const Transform *t, const float3 a) #endif } -ccl_device_inline float3 transform_direction_transposed(const Transform *t, const float3 a) +ccl_device_inline float3 transform_direction_transposed(ccl_private const Transform *t, + const float3 a) { float3 x = make_float3(t->x.x, t->y.x, t->z.x); float3 y = make_float3(t->x.y, t->y.y, t->z.y); @@ -409,7 +410,8 @@ ccl_device_inline Transform transform_quick_inverse(Transform M) return R; } -ccl_device_inline void transform_compose(Transform *tfm, const DecomposedTransform *decomp) +ccl_device_inline void transform_compose(ccl_private Transform *tfm, + ccl_private const DecomposedTransform *decomp) { /* rotation */ float q0, q1, q2, q3, qda, qdb, qdc, qaa, qab, qac, qbb, qbc, qcc; @@ -449,7 +451,7 @@ ccl_device_inline void transform_compose(Transform *tfm, const DecomposedTransfo /* Interpolate from array of decomposed transforms. */ ccl_device void transform_motion_array_interpolate(Transform *tfm, - const ccl_global DecomposedTransform *motion, + const DecomposedTransform *motion, uint numsteps, float time) { @@ -458,8 +460,8 @@ ccl_device void transform_motion_array_interpolate(Transform *tfm, int step = min((int)(time * maxstep), maxstep - 1); float t = time * maxstep - step; - const ccl_global DecomposedTransform *a = motion + step; - const ccl_global DecomposedTransform *b = motion + step + 1; + const DecomposedTransform *a = motion + step; + const DecomposedTransform *b = motion + step + 1; /* Interpolate rotation, translation and scale. */ DecomposedTransform decomp; @@ -472,12 +474,12 @@ ccl_device void transform_motion_array_interpolate(Transform *tfm, transform_compose(tfm, &decomp); } -ccl_device_inline bool transform_isfinite_safe(Transform *tfm) +ccl_device_inline bool transform_isfinite_safe(ccl_private Transform *tfm) { return isfinite4_safe(tfm->x) && isfinite4_safe(tfm->y) && isfinite4_safe(tfm->z); } -ccl_device_inline bool transform_decomposed_isfinite_safe(DecomposedTransform *decomp) +ccl_device_inline bool transform_decomposed_isfinite_safe(ccl_private DecomposedTransform *decomp) { return isfinite4_safe(decomp->x) && isfinite4_safe(decomp->y) && isfinite4_safe(decomp->z) && isfinite4_safe(decomp->w); @@ -498,36 +500,12 @@ Transform transform_from_viewplane(BoundBox2D &viewplane); #endif -/* TODO(sergey): This is only for until we've got OpenCL 2.0 - * on all devices we consider supported. It'll be replaced with - * generic address space. - */ +/* TODO: This can be removed when we know if no devices will require explicit + * address space qualifiers for this case. */ -#ifdef __KERNEL_OPENCL__ - -# define OPENCL_TRANSFORM_ADDRSPACE_GLUE(a, b) a##b -# define OPENCL_TRANSFORM_ADDRSPACE_DECLARE(function) \ - ccl_device_inline float3 OPENCL_TRANSFORM_ADDRSPACE_GLUE(function, _addrspace)( \ - ccl_addr_space const Transform *t, const float3 a) \ - { \ - Transform private_tfm = *t; \ - return function(&private_tfm, a); \ - } - -OPENCL_TRANSFORM_ADDRSPACE_DECLARE(transform_point) -OPENCL_TRANSFORM_ADDRSPACE_DECLARE(transform_direction) -OPENCL_TRANSFORM_ADDRSPACE_DECLARE(transform_direction_transposed) - -# undef OPENCL_TRANSFORM_ADDRSPACE_DECLARE -# undef OPENCL_TRANSFORM_ADDRSPACE_GLUE -# define transform_point_auto transform_point_addrspace -# define transform_direction_auto transform_direction_addrspace -# define transform_direction_transposed_auto transform_direction_transposed_addrspace -#else -# define transform_point_auto transform_point -# define transform_direction_auto transform_direction -# define transform_direction_transposed_auto transform_direction_transposed -#endif +#define transform_point_auto transform_point +#define transform_direction_auto transform_direction +#define transform_direction_transposed_auto transform_direction_transposed CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_types.h b/intern/cycles/util/types.h similarity index 56% rename from intern/cycles/util/util_types.h rename to intern/cycles/util/types.h index 87358877e3c..697dc2b44ea 100644 --- a/intern/cycles/util/util_types.h +++ b/intern/cycles/util/types.h @@ -17,9 +17,7 @@ #ifndef __UTIL_TYPES_H__ #define __UTIL_TYPES_H__ -#ifndef __KERNEL_OPENCL__ -# include -#endif +#include /* Standard Integer Types */ @@ -27,11 +25,11 @@ # include #endif -#include "util/util_defines.h" +#include "util/defines.h" #ifndef __KERNEL_GPU__ -# include "util/util_optimization.h" -# include "util/util_simd.h" +# include "util/optimization.h" +# include "util/simd.h" #endif CCL_NAMESPACE_BEGIN @@ -44,18 +42,12 @@ CCL_NAMESPACE_BEGIN /* Shorter Unsigned Names */ -#ifndef __KERNEL_OPENCL__ typedef unsigned char uchar; typedef unsigned int uint; typedef unsigned short ushort; -#endif /* Fixed Bits Types */ -#ifdef __KERNEL_OPENCL__ -typedef unsigned long uint64_t; -#endif - #ifndef __KERNEL_GPU__ /* Generic Memory Pointer */ @@ -90,56 +82,56 @@ ccl_device_inline bool is_power_of_two(size_t x) CCL_NAMESPACE_END /* Vectorized types declaration. */ -#include "util/util_types_uchar2.h" -#include "util/util_types_uchar3.h" -#include "util/util_types_uchar4.h" +#include "util/types_uchar2.h" +#include "util/types_uchar3.h" +#include "util/types_uchar4.h" -#include "util/util_types_int2.h" -#include "util/util_types_int3.h" -#include "util/util_types_int4.h" +#include "util/types_int2.h" +#include "util/types_int3.h" +#include "util/types_int4.h" -#include "util/util_types_uint2.h" -#include "util/util_types_uint3.h" -#include "util/util_types_uint4.h" +#include "util/types_uint2.h" +#include "util/types_uint3.h" +#include "util/types_uint4.h" -#include "util/util_types_ushort4.h" +#include "util/types_ushort4.h" -#include "util/util_types_float2.h" -#include "util/util_types_float3.h" -#include "util/util_types_float4.h" -#include "util/util_types_float8.h" +#include "util/types_float2.h" +#include "util/types_float3.h" +#include "util/types_float4.h" +#include "util/types_float8.h" -#include "util/util_types_vector3.h" +#include "util/types_vector3.h" /* Vectorized types implementation. */ -#include "util/util_types_uchar2_impl.h" -#include "util/util_types_uchar3_impl.h" -#include "util/util_types_uchar4_impl.h" +#include "util/types_uchar2_impl.h" +#include "util/types_uchar3_impl.h" +#include "util/types_uchar4_impl.h" -#include "util/util_types_int2_impl.h" -#include "util/util_types_int3_impl.h" -#include "util/util_types_int4_impl.h" +#include "util/types_int2_impl.h" +#include "util/types_int3_impl.h" +#include "util/types_int4_impl.h" -#include "util/util_types_uint2_impl.h" -#include "util/util_types_uint3_impl.h" -#include "util/util_types_uint4_impl.h" +#include "util/types_uint2_impl.h" +#include "util/types_uint3_impl.h" +#include "util/types_uint4_impl.h" -#include "util/util_types_float2_impl.h" -#include "util/util_types_float3_impl.h" -#include "util/util_types_float4_impl.h" -#include "util/util_types_float8_impl.h" +#include "util/types_float2_impl.h" +#include "util/types_float3_impl.h" +#include "util/types_float4_impl.h" +#include "util/types_float8_impl.h" -#include "util/util_types_vector3_impl.h" +#include "util/types_vector3_impl.h" /* SSE types. */ #ifndef __KERNEL_GPU__ -# include "util/util_sseb.h" -# include "util/util_ssef.h" -# include "util/util_ssei.h" +# include "util/sseb.h" +# include "util/ssef.h" +# include "util/ssei.h" # if defined(__KERNEL_AVX__) || defined(__KERNEL_AVX2__) -# include "util/util_avxb.h" -# include "util/util_avxf.h" -# include "util/util_avxi.h" +# include "util/avxb.h" +# include "util/avxf.h" +# include "util/avxi.h" # endif #endif diff --git a/intern/cycles/util/util_types_float2.h b/intern/cycles/util/types_float2.h similarity index 94% rename from intern/cycles/util/util_types_float2.h rename to intern/cycles/util/types_float2.h index 3760bf579b6..e71204bef5b 100644 --- a/intern/cycles/util/util_types_float2.h +++ b/intern/cycles/util/types_float2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float2_impl.h b/intern/cycles/util/types_float2_impl.h similarity index 95% rename from intern/cycles/util/util_types_float2_impl.h rename to intern/cycles/util/types_float2_impl.h index 7810d2a8781..c02c13f8c47 100644 --- a/intern/cycles/util/util_types_float2_impl.h +++ b/intern/cycles/util/types_float2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_float3.h b/intern/cycles/util/types_float3.h similarity index 96% rename from intern/cycles/util/util_types_float3.h rename to intern/cycles/util/types_float3.h index 694a600bf5c..f990367e7b8 100644 --- a/intern/cycles/util/util_types_float3.h +++ b/intern/cycles/util/types_float3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float3_impl.h b/intern/cycles/util/types_float3_impl.h similarity index 97% rename from intern/cycles/util/util_types_float3_impl.h rename to intern/cycles/util/types_float3_impl.h index ab25fb4c975..76a9067acc7 100644 --- a/intern/cycles/util/util_types_float3_impl.h +++ b/intern/cycles/util/types_float3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_float4.h b/intern/cycles/util/types_float4.h similarity index 96% rename from intern/cycles/util/util_types_float4.h rename to intern/cycles/util/types_float4.h index c29e6e15bc3..8d4e07e7e4d 100644 --- a/intern/cycles/util/util_types_float4.h +++ b/intern/cycles/util/types_float4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float4_impl.h b/intern/cycles/util/types_float4_impl.h similarity index 97% rename from intern/cycles/util/util_types_float4_impl.h rename to intern/cycles/util/types_float4_impl.h index 05a1feee5b2..d75715332e5 100644 --- a/intern/cycles/util/util_types_float4_impl.h +++ b/intern/cycles/util/types_float4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_FLOAT4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_float8.h b/intern/cycles/util/types_float8.h similarity index 97% rename from intern/cycles/util/util_types_float8.h rename to intern/cycles/util/types_float8.h index 27da120a4ba..cf1f66b7622 100644 --- a/intern/cycles/util/util_types_float8.h +++ b/intern/cycles/util/types_float8.h @@ -30,7 +30,7 @@ #define __UTIL_TYPES_FLOAT8_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_float8_impl.h b/intern/cycles/util/types_float8_impl.h similarity index 97% rename from intern/cycles/util/util_types_float8_impl.h rename to intern/cycles/util/types_float8_impl.h index 4e4ea28c6a4..a795666adc7 100644 --- a/intern/cycles/util/util_types_float8_impl.h +++ b/intern/cycles/util/types_float8_impl.h @@ -30,7 +30,7 @@ #define __UTIL_TYPES_FLOAT8_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_int2.h b/intern/cycles/util/types_int2.h similarity index 93% rename from intern/cycles/util/util_types_int2.h rename to intern/cycles/util/types_int2.h index 8811e5ec7c2..75970577d77 100644 --- a/intern/cycles/util/util_types_int2.h +++ b/intern/cycles/util/types_int2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int2_impl.h b/intern/cycles/util/types_int2_impl.h similarity index 94% rename from intern/cycles/util/util_types_int2_impl.h rename to intern/cycles/util/types_int2_impl.h index ce95d4f14e5..efa63cdfd2a 100644 --- a/intern/cycles/util/util_types_int2_impl.h +++ b/intern/cycles/util/types_int2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int3.h b/intern/cycles/util/types_int3.h similarity index 96% rename from intern/cycles/util/util_types_int3.h rename to intern/cycles/util/types_int3.h index 09edc09dff3..071a886136e 100644 --- a/intern/cycles/util/util_types_int3.h +++ b/intern/cycles/util/types_int3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int3_impl.h b/intern/cycles/util/types_int3_impl.h similarity index 96% rename from intern/cycles/util/util_types_int3_impl.h rename to intern/cycles/util/types_int3_impl.h index 080c892640b..c91c64b804e 100644 --- a/intern/cycles/util/util_types_int3_impl.h +++ b/intern/cycles/util/types_int3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_int4.h b/intern/cycles/util/types_int4.h similarity index 96% rename from intern/cycles/util/util_types_int4.h rename to intern/cycles/util/types_int4.h index 5c7917cf5d6..cb497d70035 100644 --- a/intern/cycles/util/util_types_int4.h +++ b/intern/cycles/util/types_int4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_int4_impl.h b/intern/cycles/util/types_int4_impl.h similarity index 97% rename from intern/cycles/util/util_types_int4_impl.h rename to intern/cycles/util/types_int4_impl.h index c6f6ff23a17..258b42c029e 100644 --- a/intern/cycles/util/util_types_int4_impl.h +++ b/intern/cycles/util/types_int4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_INT4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif #ifndef __KERNEL_GPU__ diff --git a/intern/cycles/util/util_types_uchar2.h b/intern/cycles/util/types_uchar2.h similarity index 94% rename from intern/cycles/util/util_types_uchar2.h rename to intern/cycles/util/types_uchar2.h index 8cc486e3e48..0dc1d46bf29 100644 --- a/intern/cycles/util/util_types_uchar2.h +++ b/intern/cycles/util/types_uchar2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar2_impl.h b/intern/cycles/util/types_uchar2_impl.h similarity index 94% rename from intern/cycles/util/util_types_uchar2_impl.h rename to intern/cycles/util/types_uchar2_impl.h index 16968c32dd9..234a71a2247 100644 --- a/intern/cycles/util/util_types_uchar2_impl.h +++ b/intern/cycles/util/types_uchar2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar3.h b/intern/cycles/util/types_uchar3.h similarity index 94% rename from intern/cycles/util/util_types_uchar3.h rename to intern/cycles/util/types_uchar3.h index 5838c437c70..d3913afb3a2 100644 --- a/intern/cycles/util/util_types_uchar3.h +++ b/intern/cycles/util/types_uchar3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar3_impl.h b/intern/cycles/util/types_uchar3_impl.h similarity index 94% rename from intern/cycles/util/util_types_uchar3_impl.h rename to intern/cycles/util/types_uchar3_impl.h index aa31b725731..90f510e3b28 100644 --- a/intern/cycles/util/util_types_uchar3_impl.h +++ b/intern/cycles/util/types_uchar3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar4.h b/intern/cycles/util/types_uchar4.h similarity index 94% rename from intern/cycles/util/util_types_uchar4.h rename to intern/cycles/util/types_uchar4.h index 22b6a1ac705..bfe1c06acd8 100644 --- a/intern/cycles/util/util_types_uchar4.h +++ b/intern/cycles/util/types_uchar4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uchar4_impl.h b/intern/cycles/util/types_uchar4_impl.h similarity index 94% rename from intern/cycles/util/util_types_uchar4_impl.h rename to intern/cycles/util/types_uchar4_impl.h index 79879f176a6..d15c74bed03 100644 --- a/intern/cycles/util/util_types_uchar4_impl.h +++ b/intern/cycles/util/types_uchar4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UCHAR4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint2.h b/intern/cycles/util/types_uint2.h similarity index 94% rename from intern/cycles/util/util_types_uint2.h rename to intern/cycles/util/types_uint2.h index abcb8ee5346..7419977040b 100644 --- a/intern/cycles/util/util_types_uint2.h +++ b/intern/cycles/util/types_uint2.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT2_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint2_impl.h b/intern/cycles/util/types_uint2_impl.h similarity index 94% rename from intern/cycles/util/util_types_uint2_impl.h rename to intern/cycles/util/types_uint2_impl.h index db62bd99b89..8427f9694b5 100644 --- a/intern/cycles/util/util_types_uint2_impl.h +++ b/intern/cycles/util/types_uint2_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT2_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint3.h b/intern/cycles/util/types_uint3.h similarity index 94% rename from intern/cycles/util/util_types_uint3.h rename to intern/cycles/util/types_uint3.h index 436d870b621..1e97e7f2d36 100644 --- a/intern/cycles/util/util_types_uint3.h +++ b/intern/cycles/util/types_uint3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint3_impl.h b/intern/cycles/util/types_uint3_impl.h similarity index 94% rename from intern/cycles/util/util_types_uint3_impl.h rename to intern/cycles/util/types_uint3_impl.h index d188fa06e2a..ba83cffe9a8 100644 --- a/intern/cycles/util/util_types_uint3_impl.h +++ b/intern/cycles/util/types_uint3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint4.h b/intern/cycles/util/types_uint4.h similarity index 94% rename from intern/cycles/util/util_types_uint4.h rename to intern/cycles/util/types_uint4.h index 57f2859fedf..b135877b890 100644 --- a/intern/cycles/util/util_types_uint4.h +++ b/intern/cycles/util/types_uint4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_uint4_impl.h b/intern/cycles/util/types_uint4_impl.h similarity index 94% rename from intern/cycles/util/util_types_uint4_impl.h rename to intern/cycles/util/types_uint4_impl.h index bac8d23030d..b860fbfc49a 100644 --- a/intern/cycles/util/util_types_uint4_impl.h +++ b/intern/cycles/util/types_uint4_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_UINT4_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_ushort4.h b/intern/cycles/util/types_ushort4.h similarity index 93% rename from intern/cycles/util/util_types_ushort4.h rename to intern/cycles/util/types_ushort4.h index 476ceec622c..8d080bcc1b9 100644 --- a/intern/cycles/util/util_types_ushort4.h +++ b/intern/cycles/util/types_ushort4.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_USHORT4_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_vector3.h b/intern/cycles/util/types_vector3.h similarity index 94% rename from intern/cycles/util/util_types_vector3.h rename to intern/cycles/util/types_vector3.h index 728c7ca62a1..d46a0266855 100644 --- a/intern/cycles/util/util_types_vector3.h +++ b/intern/cycles/util/types_vector3.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_VECTOR3_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_types_vector3_impl.h b/intern/cycles/util/types_vector3_impl.h similarity index 94% rename from intern/cycles/util/util_types_vector3_impl.h rename to intern/cycles/util/types_vector3_impl.h index 33ba53e20b2..ff6dcd85b12 100644 --- a/intern/cycles/util/util_types_vector3_impl.h +++ b/intern/cycles/util/types_vector3_impl.h @@ -18,7 +18,7 @@ #define __UTIL_TYPES_VECTOR3_IMPL_H__ #ifndef __UTIL_TYPES_H__ -# error "Do not include this file directly, include util_types.h instead." +# error "Do not include this file directly, include util/types.h instead." #endif CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_unique_ptr.h b/intern/cycles/util/unique_ptr.h similarity index 97% rename from intern/cycles/util/util_unique_ptr.h rename to intern/cycles/util/unique_ptr.h index 3aaaf083eff..3181eafd43d 100644 --- a/intern/cycles/util/util_unique_ptr.h +++ b/intern/cycles/util/unique_ptr.h @@ -21,6 +21,7 @@ CCL_NAMESPACE_BEGIN +using std::make_unique; using std::unique_ptr; CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_debug.cpp b/intern/cycles/util/util_debug.cpp deleted file mode 100644 index 74ecefa1917..00000000000 --- a/intern/cycles/util/util_debug.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright 2011-2016 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "util/util_debug.h" - -#include - -#include "bvh/bvh_params.h" - -#include "util/util_logging.h" -#include "util/util_string.h" - -CCL_NAMESPACE_BEGIN - -DebugFlags::CPU::CPU() - : avx2(true), - avx(true), - sse41(true), - sse3(true), - sse2(true), - bvh_layout(BVH_LAYOUT_AUTO), - split_kernel(false) -{ - reset(); -} - -void DebugFlags::CPU::reset() -{ -#define STRINGIFY(x) #x -#define CHECK_CPU_FLAGS(flag, env) \ - do { \ - flag = (getenv(env) == NULL); \ - if (!flag) { \ - VLOG(1) << "Disabling " << STRINGIFY(flag) << " instruction set."; \ - } \ - } while (0) - - CHECK_CPU_FLAGS(avx2, "CYCLES_CPU_NO_AVX2"); - CHECK_CPU_FLAGS(avx, "CYCLES_CPU_NO_AVX"); - CHECK_CPU_FLAGS(sse41, "CYCLES_CPU_NO_SSE41"); - CHECK_CPU_FLAGS(sse3, "CYCLES_CPU_NO_SSE3"); - CHECK_CPU_FLAGS(sse2, "CYCLES_CPU_NO_SSE2"); - -#undef STRINGIFY -#undef CHECK_CPU_FLAGS - - bvh_layout = BVH_LAYOUT_AUTO; - - split_kernel = false; -} - -DebugFlags::CUDA::CUDA() : adaptive_compile(false), split_kernel(false) -{ - reset(); -} - -void DebugFlags::CUDA::reset() -{ - if (getenv("CYCLES_CUDA_ADAPTIVE_COMPILE") != NULL) - adaptive_compile = true; - - split_kernel = false; -} - -DebugFlags::OptiX::OptiX() -{ - reset(); -} - -void DebugFlags::OptiX::reset() -{ - cuda_streams = 1; - curves_api = false; -} - -DebugFlags::OpenCL::OpenCL() : device_type(DebugFlags::OpenCL::DEVICE_ALL), debug(false) -{ - reset(); -} - -void DebugFlags::OpenCL::reset() -{ - /* Initialize device type from environment variables. */ - device_type = DebugFlags::OpenCL::DEVICE_ALL; - char *device = getenv("CYCLES_OPENCL_TEST"); - if (device) { - if (strcmp(device, "NONE") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_NONE; - } - else if (strcmp(device, "ALL") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_ALL; - } - else if (strcmp(device, "DEFAULT") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_DEFAULT; - } - else if (strcmp(device, "CPU") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_CPU; - } - else if (strcmp(device, "GPU") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_GPU; - } - else if (strcmp(device, "ACCELERATOR") == 0) { - device_type = DebugFlags::OpenCL::DEVICE_ACCELERATOR; - } - } - /* Initialize other flags from environment variables. */ - debug = (getenv("CYCLES_OPENCL_DEBUG") != NULL); -} - -DebugFlags::DebugFlags() : viewport_static_bvh(false), running_inside_blender(false) -{ - /* Nothing for now. */ -} - -void DebugFlags::reset() -{ - viewport_static_bvh = false; - cpu.reset(); - cuda.reset(); - optix.reset(); - opencl.reset(); -} - -std::ostream &operator<<(std::ostream &os, DebugFlagsConstRef debug_flags) -{ - os << "CPU flags:\n" - << " AVX2 : " << string_from_bool(debug_flags.cpu.avx2) << "\n" - << " AVX : " << string_from_bool(debug_flags.cpu.avx) << "\n" - << " SSE4.1 : " << string_from_bool(debug_flags.cpu.sse41) << "\n" - << " SSE3 : " << string_from_bool(debug_flags.cpu.sse3) << "\n" - << " SSE2 : " << string_from_bool(debug_flags.cpu.sse2) << "\n" - << " BVH layout : " << bvh_layout_name(debug_flags.cpu.bvh_layout) << "\n" - << " Split : " << string_from_bool(debug_flags.cpu.split_kernel) << "\n"; - - os << "CUDA flags:\n" - << " Adaptive Compile : " << string_from_bool(debug_flags.cuda.adaptive_compile) << "\n"; - - os << "OptiX flags:\n" - << " CUDA streams : " << debug_flags.optix.cuda_streams << "\n"; - - const char *opencl_device_type; - switch (debug_flags.opencl.device_type) { - case DebugFlags::OpenCL::DEVICE_NONE: - opencl_device_type = "NONE"; - break; - case DebugFlags::OpenCL::DEVICE_ALL: - opencl_device_type = "ALL"; - break; - case DebugFlags::OpenCL::DEVICE_DEFAULT: - opencl_device_type = "DEFAULT"; - break; - case DebugFlags::OpenCL::DEVICE_CPU: - opencl_device_type = "CPU"; - break; - case DebugFlags::OpenCL::DEVICE_GPU: - opencl_device_type = "GPU"; - break; - case DebugFlags::OpenCL::DEVICE_ACCELERATOR: - opencl_device_type = "ACCELERATOR"; - break; - } - os << "OpenCL flags:\n" - << " Device type : " << opencl_device_type << "\n" - << " Debug : " << string_from_bool(debug_flags.opencl.debug) << "\n" - << " Memory limit : " << string_human_readable_size(debug_flags.opencl.mem_limit) << "\n"; - return os; -} - -CCL_NAMESPACE_END diff --git a/intern/cycles/util/util_half.h b/intern/cycles/util/util_half.h deleted file mode 100644 index a8d4ee75e20..00000000000 --- a/intern/cycles/util/util_half.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * Copyright 2011-2013 Blender Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef __UTIL_HALF_H__ -#define __UTIL_HALF_H__ - -#include "util/util_math.h" -#include "util/util_types.h" - -#if !defined(__KERNEL_GPU__) && defined(__KERNEL_SSE2__) -# include "util/util_simd.h" -#endif - -CCL_NAMESPACE_BEGIN - -/* Half Floats */ - -#ifdef __KERNEL_OPENCL__ - -# define float4_store_half(h, f, scale) vstore_half4(f *(scale), 0, h); - -#else - -/* CUDA has its own half data type, no need to define then */ -# ifndef __KERNEL_CUDA__ -/* Implementing this as a class rather than a typedef so that the compiler can tell it apart from - * unsigned shorts. */ -class half { - public: - half() : v(0) - { - } - half(const unsigned short &i) : v(i) - { - } - operator unsigned short() - { - return v; - } - half &operator=(const unsigned short &i) - { - v = i; - return *this; - } - - private: - unsigned short v; -}; -# endif - -struct half4 { - half x, y, z, w; -}; - -# ifdef __KERNEL_CUDA__ - -ccl_device_inline void float4_store_half(half *h, float4 f, float scale) -{ - h[0] = __float2half(f.x * scale); - h[1] = __float2half(f.y * scale); - h[2] = __float2half(f.z * scale); - h[3] = __float2half(f.w * scale); -} - -# else - -ccl_device_inline void float4_store_half(half *h, float4 f, float scale) -{ -# ifndef __KERNEL_SSE2__ - for (int i = 0; i < 4; i++) { - /* optimized float to half for pixels: - * assumes no negative, no nan, no inf, and sets denormal to 0 */ - union { - uint i; - float f; - } in; - float fscale = f[i] * scale; - in.f = (fscale > 0.0f) ? ((fscale < 65504.0f) ? fscale : 65504.0f) : 0.0f; - int x = in.i; - - int absolute = x & 0x7FFFFFFF; - int Z = absolute + 0xC8000000; - int result = (absolute < 0x38800000) ? 0 : Z; - int rshift = (result >> 13); - - h[i] = (rshift & 0x7FFF); - } -# else - /* same as above with SSE */ - ssef fscale = load4f(f) * scale; - ssef x = min(max(fscale, 0.0f), 65504.0f); - -# ifdef __KERNEL_AVX2__ - ssei rpack = _mm_cvtps_ph(x, 0); -# else - ssei absolute = cast(x) & 0x7FFFFFFF; - ssei Z = absolute + 0xC8000000; - ssei result = andnot(absolute < 0x38800000, Z); - ssei rshift = (result >> 13) & 0x7FFF; - ssei rpack = _mm_packs_epi32(rshift, rshift); -# endif - - _mm_storel_pi((__m64 *)h, _mm_castsi128_ps(rpack)); -# endif -} - -ccl_device_inline float half_to_float(half h) -{ - float f; - - *((int *)&f) = ((h & 0x8000) << 16) | (((h & 0x7c00) + 0x1C000) << 13) | ((h & 0x03FF) << 13); - - return f; -} - -ccl_device_inline float4 half4_to_float4(half4 h) -{ - float4 f; - - f.x = half_to_float(h.x); - f.y = half_to_float(h.y); - f.z = half_to_float(h.z); - f.w = half_to_float(h.w); - - return f; -} - -ccl_device_inline half float_to_half(float f) -{ - const uint u = __float_as_uint(f); - /* Sign bit, shifted to its position. */ - uint sign_bit = u & 0x80000000; - sign_bit >>= 16; - /* Exponent. */ - uint exponent_bits = u & 0x7f800000; - /* Non-sign bits. */ - uint value_bits = u & 0x7fffffff; - value_bits >>= 13; /* Align mantissa on MSB. */ - value_bits -= 0x1c000; /* Adjust bias. */ - /* Flush-to-zero. */ - value_bits = (exponent_bits < 0x38800000) ? 0 : value_bits; - /* Clamp-to-max. */ - value_bits = (exponent_bits > 0x47000000) ? 0x7bff : value_bits; - /* Denormals-as-zero. */ - value_bits = (exponent_bits == 0 ? 0 : value_bits); - /* Re-insert sign bit and return. */ - return (value_bits | sign_bit); -} - -# endif - -#endif - -CCL_NAMESPACE_END - -#endif /* __UTIL_HALF_H__ */ diff --git a/intern/cycles/util/util_vector.h b/intern/cycles/util/vector.h similarity index 93% rename from intern/cycles/util/util_vector.h rename to intern/cycles/util/vector.h index 87cd4de8438..db35f198dc1 100644 --- a/intern/cycles/util/util_vector.h +++ b/intern/cycles/util/vector.h @@ -21,9 +21,9 @@ #include #include -#include "util/util_aligned_malloc.h" -#include "util/util_guarded_allocator.h" -#include "util/util_types.h" +#include "util/aligned_malloc.h" +#include "util/guarded_allocator.h" +#include "util/types.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_version.h b/intern/cycles/util/version.h similarity index 100% rename from intern/cycles/util/util_version.h rename to intern/cycles/util/version.h diff --git a/intern/cycles/util/util_view.cpp b/intern/cycles/util/view.cpp similarity index 97% rename from intern/cycles/util/util_view.cpp rename to intern/cycles/util/view.cpp index 9d9ff451b3b..1c70cea1a8b 100644 --- a/intern/cycles/util/util_view.cpp +++ b/intern/cycles/util/view.cpp @@ -17,11 +17,11 @@ #include #include -#include "util/util_opengl.h" -#include "util/util_string.h" -#include "util/util_time.h" -#include "util/util_version.h" -#include "util/util_view.h" +#include "util/opengl.h" +#include "util/string.h" +#include "util/time.h" +#include "util/version.h" +#include "util/view.h" #ifdef __APPLE__ # include diff --git a/intern/cycles/util/util_view.h b/intern/cycles/util/view.h similarity index 100% rename from intern/cycles/util/util_view.h rename to intern/cycles/util/view.h diff --git a/intern/cycles/util/util_windows.cpp b/intern/cycles/util/windows.cpp similarity index 98% rename from intern/cycles/util/util_windows.cpp rename to intern/cycles/util/windows.cpp index 807a5adc84a..96944d07390 100644 --- a/intern/cycles/util/util_windows.cpp +++ b/intern/cycles/util/windows.cpp @@ -18,7 +18,7 @@ # include #endif -#include "util_windows.h" +#include "util/windows.h" CCL_NAMESPACE_BEGIN diff --git a/intern/cycles/util/util_windows.h b/intern/cycles/util/windows.h similarity index 100% rename from intern/cycles/util/util_windows.h rename to intern/cycles/util/windows.h diff --git a/intern/cycles/util/util_xml.h b/intern/cycles/util/xml.h similarity index 100% rename from intern/cycles/util/util_xml.h rename to intern/cycles/util/xml.h diff --git a/intern/ghost/CMakeLists.txt b/intern/ghost/CMakeLists.txt index 76cac1049fb..05423209c71 100644 --- a/intern/ghost/CMakeLists.txt +++ b/intern/ghost/CMakeLists.txt @@ -473,6 +473,7 @@ if(WITH_XR_OPENXR) intern/GHOST_Xr.cpp intern/GHOST_XrAction.cpp intern/GHOST_XrContext.cpp + intern/GHOST_XrControllerModel.cpp intern/GHOST_XrEvent.cpp intern/GHOST_XrGraphicsBinding.cpp intern/GHOST_XrSession.cpp @@ -482,13 +483,19 @@ if(WITH_XR_OPENXR) intern/GHOST_IXrGraphicsBinding.h intern/GHOST_XrAction.h intern/GHOST_XrContext.h + intern/GHOST_XrControllerModel.h intern/GHOST_XrException.h intern/GHOST_XrSession.h intern/GHOST_XrSwapchain.h intern/GHOST_Xr_intern.h intern/GHOST_Xr_openxr_includes.h ) + list(APPEND INC + ../../extern/json/include + ../../extern/tinygltf + ) list(APPEND INC_SYS + ${EIGEN3_INCLUDE_DIRS} ${XR_OPENXR_SDK_INCLUDE_DIR} ) list(APPEND LIB diff --git a/intern/ghost/GHOST_C-api.h b/intern/ghost/GHOST_C-api.h index b78aac6f5eb..784febe8581 100644 --- a/intern/ghost/GHOST_C-api.h +++ b/intern/ghost/GHOST_C-api.h @@ -1140,6 +1140,30 @@ void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_context, const char *action_set_name, void **r_customdata_array); +/* controller model */ +/** + * Load the OpenXR controller model. + */ +int GHOST_XrLoadControllerModel(GHOST_XrContextHandle xr_context, const char *subaction_path); + +/** + * Unload the OpenXR controller model. + */ +void GHOST_XrUnloadControllerModel(GHOST_XrContextHandle xr_context, const char *subaction_path); + +/** + * Update component transforms for the OpenXR controller model. + */ +int GHOST_XrUpdateControllerModelComponents(GHOST_XrContextHandle xr_context, + const char *subaction_path); + +/** + * Get vertex data for the OpenXR controller model. + */ +int GHOST_XrGetControllerModelData(GHOST_XrContextHandle xr_context, + const char *subaction_path, + GHOST_XrControllerModelData *r_data); + #endif /* WITH_XR_OPENXR */ #ifdef __cplusplus diff --git a/intern/ghost/GHOST_IWindow.h b/intern/ghost/GHOST_IWindow.h index 5f9bd808c8c..91f576ca304 100644 --- a/intern/ghost/GHOST_IWindow.h +++ b/intern/ghost/GHOST_IWindow.h @@ -40,7 +40,7 @@ * There are two coordinate systems: * * - The screen coordinate system. The origin of the screen is located in the - * upper left corner of the screen. + * upper left corner of the screen. * - The client rectangle coordinate system. The client rectangle of a window * is the area that is drawable by the application (excluding title bars etc.). */ diff --git a/intern/ghost/GHOST_Types.h b/intern/ghost/GHOST_Types.h index 221fa140f70..2c8014a08cc 100644 --- a/intern/ghost/GHOST_Types.h +++ b/intern/ghost/GHOST_Types.h @@ -569,6 +569,7 @@ typedef enum { GHOST_kUserSpecialDirMusic, GHOST_kUserSpecialDirPictures, GHOST_kUserSpecialDirVideos, + GHOST_kUserSpecialDirCaches, /* Can be extended as needed. */ } GHOST_TUserSpecialDirTypes; @@ -753,8 +754,31 @@ typedef struct GHOST_XrActionProfileInfo { const char *profile_path; uint32_t count_subaction_paths; const char **subaction_paths; - /* Bindings for each subaction path. */ + /** Bindings for each subaction path. */ const GHOST_XrActionBindingInfo *bindings; } GHOST_XrActionProfileInfo; +typedef struct GHOST_XrControllerModelVertex { + float position[3]; + float normal[3]; +} GHOST_XrControllerModelVertex; + +typedef struct GHOST_XrControllerModelComponent { + /** World space transform. */ + float transform[4][4]; + uint32_t vertex_offset; + uint32_t vertex_count; + uint32_t index_offset; + uint32_t index_count; +} GHOST_XrControllerModelComponent; + +typedef struct GHOST_XrControllerModelData { + uint32_t count_vertices; + const GHOST_XrControllerModelVertex *vertices; + uint32_t count_indices; + const uint32_t *indices; + uint32_t count_components; + const GHOST_XrControllerModelComponent *components; +} GHOST_XrControllerModelData; + #endif /* WITH_XR_OPENXR */ diff --git a/intern/ghost/intern/GHOST_C-api.cpp b/intern/ghost/intern/GHOST_C-api.cpp index a2871b46222..a21c3a90c06 100644 --- a/intern/ghost/intern/GHOST_C-api.cpp +++ b/intern/ghost/intern/GHOST_C-api.cpp @@ -1069,4 +1069,39 @@ void GHOST_XrGetActionCustomdataArray(GHOST_XrContextHandle xr_contexthandle, xr_context); } +int GHOST_XrLoadControllerModel(GHOST_XrContextHandle xr_contexthandle, const char *subaction_path) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->loadControllerModel(subaction_path), xr_context); + return 0; +} + +void GHOST_XrUnloadControllerModel(GHOST_XrContextHandle xr_contexthandle, + const char *subaction_path) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL(xr_session->unloadControllerModel(subaction_path), xr_context); +} + +int GHOST_XrUpdateControllerModelComponents(GHOST_XrContextHandle xr_contexthandle, + const char *subaction_path) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->updateControllerModelComponents(subaction_path), xr_context); + return 0; +} + +int GHOST_XrGetControllerModelData(GHOST_XrContextHandle xr_contexthandle, + const char *subaction_path, + GHOST_XrControllerModelData *r_data) +{ + GHOST_IXrContext *xr_context = (GHOST_IXrContext *)xr_contexthandle; + GHOST_XrSession *xr_session = xr_context->getSession(); + GHOST_XR_CAPI_CALL_RET(xr_session->getControllerModelData(subaction_path, *r_data), xr_context); + return 0; +} + #endif /* WITH_XR_OPENXR */ diff --git a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp index 18adf948e3b..09b2e4dfe2b 100644 --- a/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp +++ b/intern/ghost/intern/GHOST_DisplayManagerSDL.cpp @@ -101,12 +101,11 @@ GHOST_TSuccess GHOST_DisplayManagerSDL::setCurrentDisplaySetting( uint8_t display, const GHOST_DisplaySetting &setting) { /* - * Mode switching code ported from Quake 2 version 3.21 and bzflag version - * 2.4.0: + * Mode switching code ported from Quake 2 version 3.21 and BZFLAG version 2.4.0: * ftp://ftp.idsoftware.com/idstuff/source/q2source-3.21.zip * See linux/gl_glx.c:GLimp_SetMode * http://wiki.bzflag.org/BZFlag_Source - * See src/platform/SDLDisplay.cxx:SDLDisplay and createWindow + * See: `src/platform/SDLDisplay.cxx:SDLDisplay` and `createWindow`. */ SDL_DisplayMode mode; const int num_modes = SDL_GetNumDisplayModes(display); diff --git a/intern/ghost/intern/GHOST_SystemCocoa.mm b/intern/ghost/intern/GHOST_SystemCocoa.mm index 189e663f91a..204bbdaec50 100644 --- a/intern/ghost/intern/GHOST_SystemCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemCocoa.mm @@ -116,8 +116,6 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) case kVK_ANSI_Z: return GHOST_kKeyZ; #endif /* Numbers keys: mapped to handle some int'l keyboard (e.g. French). */ - case kVK_ISO_Section: - return GHOST_kKeyUnknown; case kVK_ANSI_1: return GHOST_kKey1; case kVK_ANSI_2: @@ -257,6 +255,7 @@ static GHOST_TKey convertKey(int rawCode, unichar recvChar, UInt16 keyAction) case kVK_ANSI_LeftBracket: return GHOST_kKeyLeftBracket; case kVK_ANSI_RightBracket: return GHOST_kKeyRightBracket; case kVK_ANSI_Grave: return GHOST_kKeyAccentGrave; + case kVK_ISO_Section: return GHOST_kKeyUnknown; #endif case kVK_VolumeUp: case kVK_VolumeDown: diff --git a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm index 3b29d5106f6..43ce0bb0533 100644 --- a/intern/ghost/intern/GHOST_SystemPathsCocoa.mm +++ b/intern/ghost/intern/GHOST_SystemPathsCocoa.mm @@ -117,6 +117,9 @@ const char *GHOST_SystemPathsCocoa::getUserSpecialDir(GHOST_TUserSpecialDirTypes case GHOST_kUserSpecialDirVideos: ns_directory = NSMoviesDirectory; break; + case GHOST_kUserSpecialDirCaches: + ns_directory = NSCachesDirectory; + break; default: GHOST_ASSERT( false, diff --git a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp index b58799e9c2a..72396782752 100644 --- a/intern/ghost/intern/GHOST_SystemPathsUnix.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsUnix.cpp @@ -114,6 +114,7 @@ const char *GHOST_SystemPathsUnix::getUserDir(int version, const char *versionst const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes type) const { const char *type_str; + std::string add_path = ""; switch (type) { case GHOST_kUserSpecialDirDesktop: @@ -134,6 +135,18 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes case GHOST_kUserSpecialDirVideos: type_str = "VIDEOS"; break; + case GHOST_kUserSpecialDirCaches: { + const char *cache_dir = getenv("XDG_CACHE_HOME"); + if (cache_dir) { + return cache_dir; + } + /* Fallback to ~home/.cache/. + * When invoking `xdg-user-dir` without parameters the user folder + * will be read. `.cache` will be appended. */ + type_str = ""; + add_path = ".cache"; + break; + } default: GHOST_ASSERT( false, @@ -142,7 +155,7 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes } static string path = ""; - /* Pipe stderr to /dev/null to avoid error prints. We will fail gracefully still. */ + /* Pipe `stderr` to `/dev/null` to avoid error prints. We will fail gracefully still. */ string command = string("xdg-user-dir ") + type_str + " 2> /dev/null"; FILE *fstream = popen(command.c_str(), "r"); @@ -152,7 +165,7 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes std::stringstream path_stream; while (!feof(fstream)) { char c = fgetc(fstream); - /* xdg-user-dir ends the path with '\n'. */ + /* `xdg-user-dir` ends the path with '\n'. */ if (c == '\n') { break; } @@ -163,6 +176,10 @@ const char *GHOST_SystemPathsUnix::getUserSpecialDir(GHOST_TUserSpecialDirTypes return NULL; } + if (!add_path.empty()) { + path_stream << '/' << add_path; + } + path = path_stream.str(); return path[0] ? path.c_str() : NULL; } diff --git a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp index 580cfcac7ba..bced552921f 100644 --- a/intern/ghost/intern/GHOST_SystemPathsWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemPathsWin32.cpp @@ -100,6 +100,9 @@ const char *GHOST_SystemPathsWin32::getUserSpecialDir(GHOST_TUserSpecialDirTypes case GHOST_kUserSpecialDirVideos: folderid = FOLDERID_Videos; break; + case GHOST_kUserSpecialDirCaches: + folderid = FOLDERID_LocalAppData; + break; default: GHOST_ASSERT( false, diff --git a/intern/ghost/intern/GHOST_SystemSDL.cpp b/intern/ghost/intern/GHOST_SystemSDL.cpp index 35c7a7ef463..5370d4df857 100644 --- a/intern/ghost/intern/GHOST_SystemSDL.cpp +++ b/intern/ghost/intern/GHOST_SystemSDL.cpp @@ -374,8 +374,8 @@ void GHOST_SystemSDL::processEvent(SDL_Event *sdl_event) if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) window->getClientBounds(bounds); - /* Could also clamp to screen bounds wrap with a window outside the view will fail atm. - * Use offset of 8 in case the window is at screen bounds. */ + /* Could also clamp to screen bounds wrap with a window outside the view will + * fail at the moment. Use offset of 8 in case the window is at screen bounds. */ bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); diff --git a/intern/ghost/intern/GHOST_SystemWin32.cpp b/intern/ghost/intern/GHOST_SystemWin32.cpp index f44107ee000..482f20f5cd1 100644 --- a/intern/ghost/intern/GHOST_SystemWin32.cpp +++ b/intern/ghost/intern/GHOST_SystemWin32.cpp @@ -1100,8 +1100,8 @@ GHOST_EventCursor *GHOST_SystemWin32::processCursorEvent(GHOST_WindowWin32 *wind window->getClientBounds(bounds); } - /* Could also clamp to screen bounds wrap with a window outside the view will fail atm. - * Use inset in case the window is at screen bounds. */ + /* Could also clamp to screen bounds wrap with a window outside the view will + * fail at the moment. Use inset in case the window is at screen bounds. */ bounds.wrapPoint(x_new, y_new, 2, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); diff --git a/intern/ghost/intern/GHOST_SystemX11.cpp b/intern/ghost/intern/GHOST_SystemX11.cpp index 10ccb00cc15..ab8039ea95d 100644 --- a/intern/ghost/intern/GHOST_SystemX11.cpp +++ b/intern/ghost/intern/GHOST_SystemX11.cpp @@ -973,8 +973,8 @@ void GHOST_SystemX11::processEvent(XEvent *xe) if (window->getCursorGrabBounds(bounds) == GHOST_kFailure) window->getClientBounds(bounds); - /* Could also clamp to screen bounds wrap with a window outside the view will fail atm. - * Use offset of 8 in case the window is at screen bounds. */ + /* Could also clamp to screen bounds wrap with a window outside the view will + * fail at the moment. Use offset of 8 in case the window is at screen bounds. */ bounds.wrapPoint(x_new, y_new, 8, window->getCursorGrabAxis()); window->getCursorGrabAccum(x_accum, y_accum); @@ -1044,7 +1044,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) #ifdef USE_NON_LATIN_KB_WORKAROUND /* XXX: Code below is kinda awfully convoluted... Issues are: - * - In keyboards like latin ones, numbers need a 'Shift' to be accessed but key_sym + * - In keyboards like Latin ones, numbers need a 'Shift' to be accessed but key_sym * is unmodified (or anyone swapping the keys with `xmodmap`). * - #XLookupKeysym seems to always use first defined key-map (see T47228), which generates * key-codes unusable by ghost_key_from_keysym for non-Latin-compatible key-maps. @@ -1131,7 +1131,7 @@ void GHOST_SystemX11::processEvent(XEvent *xe) } } #else - /* In keyboards like latin ones, + /* In keyboards like Latin ones, * numbers needs a 'Shift' to be accessed but key_sym * is unmodified (or anyone swapping the keys with xmodmap). * @@ -1528,13 +1528,13 @@ void GHOST_SystemX11::processEvent(XEvent *xe) window->GetTabletData().Pressure = axis_value / ((float)xtablet.PressureLevels); } - /* the (short) cast and the & 0xffff is bizarre and unexplained anywhere, - * but I got garbage data without it. Found it in the xidump.c source --matt + /* NOTE(@broken): the (short) cast and the & 0xffff is bizarre and unexplained anywhere, + * but I got garbage data without it. Found it in the `xidump.c` source. * - * The '& 0xffff' just truncates the value to its two lowest bytes, this probably means - * some drivers do not properly set the whole int value? Since we convert to float - * afterward, I don't think we need to cast to short here, but do not have a device to - * check this. --mont29 + * NOTE(@mont29): The '& 0xffff' just truncates the value to its two lowest bytes, + * this probably means some drivers do not properly set the whole int value? + * Since we convert to float afterward, + * I don't think we need to cast to short here, but do not have a device to check this. */ if (AXIS_VALUE_GET(3, axis_value)) { window->GetTabletData().Xtilt = (short)(axis_value & 0xffff) / diff --git a/intern/ghost/intern/GHOST_WindowX11.cpp b/intern/ghost/intern/GHOST_WindowX11.cpp index de389951613..8b44403c598 100644 --- a/intern/ghost/intern/GHOST_WindowX11.cpp +++ b/intern/ghost/intern/GHOST_WindowX11.cpp @@ -1092,9 +1092,9 @@ GHOST_TSuccess GHOST_WindowX11::setOrder(GHOST_TWindowOrder order) XWindowAttributes attr; Atom atom; - /* We use both XRaiseWindow and _NET_ACTIVE_WINDOW, since some - * window managers ignore the former (e.g. kwin from kde) and others - * don't implement the latter (e.g. fluxbox pre 0.9.9) */ + /* We use both #XRaiseWindow and #_NET_ACTIVE_WINDOW, since some + * window managers ignore the former (e.g. KWIN from KDE) and others + * don't implement the latter (e.g. FLUXBOX before 0.9.9). */ XRaiseWindow(m_display, m_window); diff --git a/intern/ghost/intern/GHOST_XrContext.cpp b/intern/ghost/intern/GHOST_XrContext.cpp index fe8fec052fe..15b40690d83 100644 --- a/intern/ghost/intern/GHOST_XrContext.cpp +++ b/intern/ghost/intern/GHOST_XrContext.cpp @@ -412,11 +412,14 @@ void GHOST_XrContext::getExtensionsToEnable( try_ext.push_back(XR_EXT_DEBUG_UTILS_EXTENSION_NAME); } - /* Try enabling interaction profile extensions. */ + /* Interaction profile extensions. */ try_ext.push_back(XR_EXT_HP_MIXED_REALITY_CONTROLLER_EXTENSION_NAME); try_ext.push_back(XR_HTC_VIVE_COSMOS_CONTROLLER_INTERACTION_EXTENSION_NAME); try_ext.push_back(XR_HUAWEI_CONTROLLER_INTERACTION_EXTENSION_NAME); + /* Controller model extension. */ + try_ext.push_back(XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME); + /* Varjo quad view extension. */ try_ext.push_back(XR_VARJO_QUAD_VIEWS_EXTENSION_NAME); diff --git a/intern/ghost/intern/GHOST_XrControllerModel.cpp b/intern/ghost/intern/GHOST_XrControllerModel.cpp new file mode 100644 index 00000000000..ae15bf11aa0 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrControllerModel.cpp @@ -0,0 +1,629 @@ +/* + * 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 GHOST + */ + +#include + +#include +#include + +#include "GHOST_Types.h" +#include "GHOST_XrException.h" +#include "GHOST_Xr_intern.h" + +#include "GHOST_XrControllerModel.h" + +#define TINYGLTF_IMPLEMENTATION +#define TINYGLTF_NO_STB_IMAGE +#define TINYGLTF_NO_STB_IMAGE_WRITE +#define STBIWDEF static inline +#include "tiny_gltf.h" + +struct GHOST_XrControllerModelNode { + int32_t parent_idx = -1; + int32_t component_idx = -1; + float local_transform[4][4]; +}; + +/* -------------------------------------------------------------------- */ +/** \name glTF Utilities + * + * Adapted from Microsoft OpenXR-Mixed Reality Samples (MIT License): + * https://github.com/microsoft/OpenXR-MixedReality + * \{ */ + +struct GHOST_XrPrimitive { + std::vector vertices; + std::vector indices; +}; + +/** + * Validate that an accessor does not go out of bounds of the buffer view that it references and + * that the buffer view does not exceed the bounds of the buffer that it references + */ +static void validate_accessor(const tinygltf::Accessor &accessor, + const tinygltf::BufferView &buffer_view, + const tinygltf::Buffer &buffer, + size_t byte_stride, + size_t element_size) +{ + /* Make sure the accessor does not go out of range of the buffer view. */ + if (accessor.byteOffset + (accessor.count - 1) * byte_stride + element_size > + buffer_view.byteLength) { + throw GHOST_XrException("glTF: Accessor goes out of range of bufferview."); + } + + /* Make sure the buffer view does not go out of range of the buffer. */ + if (buffer_view.byteOffset + buffer_view.byteLength > buffer.data.size()) { + throw GHOST_XrException("glTF: BufferView goes out of range of buffer."); + } +} + +template +static void read_vertices(const tinygltf::Accessor &accessor, + const tinygltf::BufferView &buffer_view, + const tinygltf::Buffer &buffer, + GHOST_XrPrimitive &primitive) +{ + if (accessor.type != TINYGLTF_TYPE_VEC3) { + throw GHOST_XrException( + "glTF: Accessor for primitive attribute has incorrect type (VEC3 expected)."); + } + + if (accessor.componentType != TINYGLTF_COMPONENT_TYPE_FLOAT) { + throw GHOST_XrException( + "glTF: Accessor for primitive attribute has incorrect component type (FLOAT expected)."); + } + + /* If stride is not specified, it is tightly packed. */ + constexpr size_t packed_size = sizeof(float) * 3; + const size_t stride = buffer_view.byteStride == 0 ? packed_size : buffer_view.byteStride; + validate_accessor(accessor, buffer_view, buffer, stride, packed_size); + + /* Resize the vertices vector, if necessary, to include room for the attribute data. + If there are multiple attributes for a primitive, the first one will resize, and the + subsequent will not need to. */ + primitive.vertices.resize(accessor.count); + + /* Copy the attribute value over from the glTF buffer into the appropriate vertex field. */ + const uint8_t *buffer_ptr = buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset; + for (size_t i = 0; i < accessor.count; i++, buffer_ptr += stride) { + memcpy(primitive.vertices[i].*field, buffer_ptr, stride); + } +} + +static void load_attribute_accessor(const tinygltf::Model &gltf_model, + const std::string &attribute_name, + int accessor_id, + GHOST_XrPrimitive &primitive) +{ + const auto &accessor = gltf_model.accessors.at(accessor_id); + + if (accessor.bufferView == -1) { + throw GHOST_XrException("glTF: Accessor for primitive attribute specifies no bufferview."); + } + + const tinygltf::BufferView &buffer_view = gltf_model.bufferViews.at(accessor.bufferView); + if (buffer_view.target != TINYGLTF_TARGET_ARRAY_BUFFER && buffer_view.target != 0) { + throw GHOST_XrException( + "glTF: Accessor for primitive attribute uses bufferview with invalid 'target' type."); + } + + const tinygltf::Buffer &buffer = gltf_model.buffers.at(buffer_view.buffer); + + if (attribute_name.compare("POSITION") == 0) { + read_vertices<&GHOST_XrControllerModelVertex::position>( + accessor, buffer_view, buffer, primitive); + } + else if (attribute_name.compare("NORMAL") == 0) { + read_vertices<&GHOST_XrControllerModelVertex::normal>( + accessor, buffer_view, buffer, primitive); + } +} + +/** + * Reads index data from a glTF primitive into a GHOST_XrPrimitive. glTF indices may be 8bit, 16bit + * or 32bit integers. This will coalesce indices from the source type(s) into a 32bit integer. + */ +template +static void read_indices(const tinygltf::Accessor &accessor, + const tinygltf::BufferView &buffer_view, + const tinygltf::Buffer &buffer, + GHOST_XrPrimitive &primitive) +{ + if (buffer_view.target != TINYGLTF_TARGET_ELEMENT_ARRAY_BUFFER && + buffer_view.target != 0) { /* Allow 0 (not specified) even though spec doesn't seem to allow + this (BoomBox GLB fails). */ + throw GHOST_XrException( + "glTF: Accessor for indices uses bufferview with invalid 'target' type."); + } + + constexpr size_t component_size_bytes = sizeof(TSrcIndex); + if (buffer_view.byteStride != 0 && + buffer_view.byteStride != + component_size_bytes) { /* Index buffer must be packed per glTF spec. */ + throw GHOST_XrException( + "glTF: Accessor for indices uses bufferview with invalid 'byteStride'."); + } + + validate_accessor(accessor, buffer_view, buffer, component_size_bytes, component_size_bytes); + + if ((accessor.count % 3) != 0) { /* Since only triangles are supported, enforce that the number + of indices is divisible by 3. */ + throw GHOST_XrException("glTF: Unexpected number of indices for triangle primitive"); + } + + const TSrcIndex *index_buffer = reinterpret_cast( + buffer.data.data() + buffer_view.byteOffset + accessor.byteOffset); + for (uint32_t i = 0; i < accessor.count; i++) { + primitive.indices.push_back(*(index_buffer + i)); + } +} + +/** + * Reads index data from a glTF primitive into a GHOST_XrPrimitive. + */ +static void load_index_accessor(const tinygltf::Model &gltf_model, + const tinygltf::Accessor &accessor, + GHOST_XrPrimitive &primitive) +{ + if (accessor.type != TINYGLTF_TYPE_SCALAR) { + throw GHOST_XrException("glTF: Accessor for indices specifies invalid 'type'."); + } + + if (accessor.bufferView == -1) { + throw GHOST_XrException("glTF: Index accessor without bufferView is currently not supported."); + } + + const tinygltf::BufferView &buffer_view = gltf_model.bufferViews.at(accessor.bufferView); + const tinygltf::Buffer &buffer = gltf_model.buffers.at(buffer_view.buffer); + + if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_BYTE) { + read_indices(accessor, buffer_view, buffer, primitive); + } + else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_SHORT) { + read_indices(accessor, buffer_view, buffer, primitive); + } + else if (accessor.componentType == TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT) { + read_indices(accessor, buffer_view, buffer, primitive); + } + else { + throw GHOST_XrException("glTF: Accessor for indices specifies invalid 'componentType'."); + } +} + +static GHOST_XrPrimitive read_primitive(const tinygltf::Model &gltf_model, + const tinygltf::Primitive &gltf_primitive) +{ + if (gltf_primitive.mode != TINYGLTF_MODE_TRIANGLES) { + throw GHOST_XrException( + "glTF: Unsupported primitive mode. Only TINYGLTF_MODE_TRIANGLES is supported."); + } + + GHOST_XrPrimitive primitive; + + /* glTF vertex data is stored in an attribute dictionary.Loop through each attribute and insert + * it into the GHOST_XrPrimitive. */ + for (const auto &[attr_name, accessor_idx] : gltf_primitive.attributes) { + load_attribute_accessor(gltf_model, attr_name, accessor_idx, primitive); + } + + if (gltf_primitive.indices != -1) { + /* If indices are specified for the glTF primitive, read them into the GHOST_XrPrimitive. */ + load_index_accessor(gltf_model, gltf_model.accessors.at(gltf_primitive.indices), primitive); + } + + return primitive; +} + +/** + * Calculate node local and world transforms. + */ +static void calc_node_transforms(const tinygltf::Node &gltf_node, + const float parent_transform[4][4], + float r_local_transform[4][4], + float r_world_transform[4][4]) +{ + /* A node may specify either a 4x4 matrix or TRS (Translation - Rotation - Scale) values, but not + * both. */ + if (gltf_node.matrix.size() == 16) { + const std::vector &dm = gltf_node.matrix; + float m[4][4] = {(float)dm[0], + (float)dm[1], + (float)dm[2], + (float)dm[3], + (float)dm[4], + (float)dm[5], + (float)dm[6], + (float)dm[7], + (float)dm[8], + (float)dm[9], + (float)dm[10], + (float)dm[11], + (float)dm[12], + (float)dm[13], + (float)dm[14], + (float)dm[15]}; + memcpy(r_local_transform, m, sizeof(float) * 16); + } + else { + /* No matrix is present, so construct a matrix from the TRS values (each one is optional). */ + std::vector translation = gltf_node.translation; + std::vector rotation = gltf_node.rotation; + std::vector scale = gltf_node.scale; + Eigen::Matrix4f &m = *(Eigen::Matrix4f *)r_local_transform; + Eigen::Quaternionf q; + Eigen::Matrix3f scalemat; + + if (translation.size() != 3) { + translation.resize(3); + translation[0] = translation[1] = translation[2] = 0.0; + } + if (rotation.size() != 4) { + rotation.resize(4); + rotation[0] = rotation[1] = rotation[2] = 0.0; + rotation[3] = 1.0; + } + if (scale.size() != 3) { + scale.resize(3); + scale[0] = scale[1] = scale[2] = 1.0; + } + + q.w() = (float)rotation[3]; + q.x() = (float)rotation[0]; + q.y() = (float)rotation[1]; + q.z() = (float)rotation[2]; + q.normalize(); + + scalemat.setIdentity(); + scalemat(0, 0) = (float)scale[0]; + scalemat(1, 1) = (float)scale[1]; + scalemat(2, 2) = (float)scale[2]; + + m.setIdentity(); + m.block<3, 3>(0, 0) = q.toRotationMatrix() * scalemat; + m.block<3, 1>(0, 3) = Eigen::Vector3f( + (float)translation[0], (float)translation[1], (float)translation[2]); + } + + *(Eigen::Matrix4f *)r_world_transform = *(Eigen::Matrix4f *)parent_transform * + *(Eigen::Matrix4f *)r_local_transform; +} + +static void load_node(const tinygltf::Model &gltf_model, + int gltf_node_id, + int32_t parent_idx, + const float parent_transform[4][4], + const std::string &parent_name, + const std::vector &node_properties, + std::vector &vertices, + std::vector &indices, + std::vector &components, + std::vector &nodes, + std::vector &node_state_indices) +{ + const tinygltf::Node &gltf_node = gltf_model.nodes.at(gltf_node_id); + float world_transform[4][4]; + + GHOST_XrControllerModelNode &node = nodes.emplace_back(); + const int32_t node_idx = (int32_t)(nodes.size() - 1); + node.parent_idx = parent_idx; + calc_node_transforms(gltf_node, parent_transform, node.local_transform, world_transform); + + for (size_t i = 0; i < node_properties.size(); ++i) { + if ((node_state_indices[i] < 0) && (parent_name == node_properties[i].parentNodeName) && + (gltf_node.name == node_properties[i].nodeName)) { + node_state_indices[i] = node_idx; + break; + } + } + + if (gltf_node.mesh != -1) { + const tinygltf::Mesh &gltf_mesh = gltf_model.meshes.at(gltf_node.mesh); + + GHOST_XrControllerModelComponent &component = components.emplace_back(); + node.component_idx = components.size() - 1; + memcpy(component.transform, world_transform, sizeof(component.transform)); + component.vertex_offset = vertices.size(); + component.index_offset = indices.size(); + + for (const tinygltf::Primitive &gltf_primitive : gltf_mesh.primitives) { + /* Read the primitive data from the glTF buffers. */ + const GHOST_XrPrimitive primitive = read_primitive(gltf_model, gltf_primitive); + + const size_t start_vertex = vertices.size(); + size_t offset = start_vertex; + size_t count = primitive.vertices.size(); + vertices.resize(offset + count); + memcpy(vertices.data() + offset, + primitive.vertices.data(), + count * sizeof(decltype(primitive.vertices)::value_type)); + + offset = indices.size(); + count = primitive.indices.size(); + indices.resize(offset + count); + for (size_t i = 0; i < count; i += 3) { + indices[offset + i + 0] = start_vertex + primitive.indices[i + 0]; + indices[offset + i + 1] = start_vertex + primitive.indices[i + 2]; + indices[offset + i + 2] = start_vertex + primitive.indices[i + 1]; + } + } + + component.vertex_count = vertices.size() - component.vertex_offset; + component.index_count = indices.size() - component.index_offset; + } + + /* Recursively load all children. */ + for (const int child_node_id : gltf_node.children) { + load_node(gltf_model, + child_node_id, + node_idx, + world_transform, + gltf_node.name, + node_properties, + vertices, + indices, + components, + nodes, + node_state_indices); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name OpenXR Extension Functions + * + * \{ */ + +static PFN_xrGetControllerModelKeyMSFT g_xrGetControllerModelKeyMSFT = nullptr; +static PFN_xrLoadControllerModelMSFT g_xrLoadControllerModelMSFT = nullptr; +static PFN_xrGetControllerModelPropertiesMSFT g_xrGetControllerModelPropertiesMSFT = nullptr; +static PFN_xrGetControllerModelStateMSFT g_xrGetControllerModelStateMSFT = nullptr; +static XrInstance g_instance = XR_NULL_HANDLE; + +#define INIT_EXTENSION_FUNCTION(name) \ + CHECK_XR( \ + xrGetInstanceProcAddr(instance, #name, reinterpret_cast(&g_##name)), \ + "Failed to get pointer to extension function: " #name); + +static void init_controller_model_extension_functions(XrInstance instance) +{ + if (instance != g_instance) { + g_instance = instance; + g_xrGetControllerModelKeyMSFT = nullptr; + g_xrLoadControllerModelMSFT = nullptr; + g_xrGetControllerModelPropertiesMSFT = nullptr; + g_xrGetControllerModelStateMSFT = nullptr; + } + + if (g_xrGetControllerModelKeyMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrGetControllerModelKeyMSFT); + } + if (g_xrLoadControllerModelMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrLoadControllerModelMSFT); + } + if (g_xrGetControllerModelPropertiesMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrGetControllerModelPropertiesMSFT); + } + if (g_xrGetControllerModelStateMSFT == nullptr) { + INIT_EXTENSION_FUNCTION(xrGetControllerModelStateMSFT); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name GHOST_XrControllerModel + * + * \{ */ + +GHOST_XrControllerModel::GHOST_XrControllerModel(XrInstance instance, + const char *subaction_path_str) +{ + init_controller_model_extension_functions(instance); + + CHECK_XR(xrStringToPath(instance, subaction_path_str, &m_subaction_path), + (std::string("Failed to get user path \"") + subaction_path_str + "\".").data()); +} + +GHOST_XrControllerModel::~GHOST_XrControllerModel() +{ + if (m_load_task.valid()) { + m_load_task.wait(); + } +} + +void GHOST_XrControllerModel::load(XrSession session) +{ + if (m_data_loaded || m_load_task.valid()) { + return; + } + + /* Get model key. */ + XrControllerModelKeyStateMSFT key_state{XR_TYPE_CONTROLLER_MODEL_KEY_STATE_MSFT}; + CHECK_XR(g_xrGetControllerModelKeyMSFT(session, m_subaction_path, &key_state), + "Failed to get controller model key state."); + + if (key_state.modelKey != XR_NULL_CONTROLLER_MODEL_KEY_MSFT) { + m_model_key = key_state.modelKey; + /* Load asynchronously. */ + m_load_task = std::async(std::launch::async, + [&, session = session]() { return loadControllerModel(session); }); + } +} + +void GHOST_XrControllerModel::loadControllerModel(XrSession session) +{ + /* Load binary buffers. */ + uint32_t buf_size = 0; + CHECK_XR(g_xrLoadControllerModelMSFT(session, m_model_key, 0, &buf_size, nullptr), + "Failed to get controller model buffer size."); + + std::vector buf((size_t)buf_size); + CHECK_XR(g_xrLoadControllerModelMSFT(session, m_model_key, buf_size, &buf_size, buf.data()), + "Failed to load controller model binary buffers."); + + /* Convert to glTF model. */ + tinygltf::TinyGLTF gltf_loader; + tinygltf::Model gltf_model; + std::string err_msg; + { + /* Workaround for TINYGLTF_NO_STB_IMAGE define. Set custom image loader to prevent failure when + * parsing image data. */ + auto load_img_func = [](tinygltf::Image *img, + const int p0, + std::string *p1, + std::string *p2, + int p3, + int p4, + const unsigned char *p5, + int p6, + void *user_pointer) -> bool { + (void)img; + (void)p0; + (void)p1; + (void)p2; + (void)p3; + (void)p4; + (void)p5; + (void)p6; + (void)user_pointer; + return true; + }; + gltf_loader.SetImageLoader(load_img_func, nullptr); + } + + if (!gltf_loader.LoadBinaryFromMemory(&gltf_model, &err_msg, nullptr, buf.data(), buf_size)) { + throw GHOST_XrException(("Failed to load glTF controller model: " + err_msg).c_str()); + } + + /* Get node properties. */ + XrControllerModelPropertiesMSFT model_properties{XR_TYPE_CONTROLLER_MODEL_PROPERTIES_MSFT}; + model_properties.nodeCapacityInput = 0; + CHECK_XR(g_xrGetControllerModelPropertiesMSFT(session, m_model_key, &model_properties), + "Failed to get controller model node properties count."); + + std::vector node_properties( + model_properties.nodeCountOutput, {XR_TYPE_CONTROLLER_MODEL_NODE_PROPERTIES_MSFT}); + model_properties.nodeCapacityInput = (uint32_t)node_properties.size(); + model_properties.nodeProperties = node_properties.data(); + CHECK_XR(g_xrGetControllerModelPropertiesMSFT(session, m_model_key, &model_properties), + "Failed to get controller model node properties."); + + m_node_state_indices.resize(node_properties.size(), -1); + + /* Get mesh vertex data. */ + const tinygltf::Scene &default_scene = gltf_model.scenes.at( + (gltf_model.defaultScene == -1) ? 0 : gltf_model.defaultScene); + const int32_t root_idx = -1; + const std::string root_name = ""; + float root_transform[4][4] = {0}; + root_transform[0][0] = root_transform[1][1] = root_transform[2][2] = root_transform[3][3] = 1.0f; + + for (const int node_id : default_scene.nodes) { + load_node(gltf_model, + node_id, + root_idx, + root_transform, + root_name, + node_properties, + m_vertices, + m_indices, + m_components, + m_nodes, + m_node_state_indices); + } + + m_data_loaded = true; +} + +void GHOST_XrControllerModel::updateComponents(XrSession session) +{ + if (!m_data_loaded) { + return; + } + + /* Get node states. */ + XrControllerModelStateMSFT model_state{XR_TYPE_CONTROLLER_MODEL_STATE_MSFT}; + model_state.nodeCapacityInput = 0; + CHECK_XR(g_xrGetControllerModelStateMSFT(session, m_model_key, &model_state), + "Failed to get controller model node state count."); + + const uint32_t count = model_state.nodeCountOutput; + std::vector node_states( + count, {XR_TYPE_CONTROLLER_MODEL_NODE_STATE_MSFT}); + model_state.nodeCapacityInput = count; + model_state.nodeStates = node_states.data(); + CHECK_XR(g_xrGetControllerModelStateMSFT(session, m_model_key, &model_state), + "Failed to get controller model node states."); + + /* Update node local transforms. */ + assert(m_node_state_indices.size() == count); + + for (uint32_t state_idx = 0; state_idx < count; ++state_idx) { + const int32_t &node_idx = m_node_state_indices[state_idx]; + if (node_idx >= 0) { + const XrPosef &pose = node_states[state_idx].nodePose; + Eigen::Matrix4f &m = *(Eigen::Matrix4f *)m_nodes[node_idx].local_transform; + Eigen::Quaternionf q( + pose.orientation.w, pose.orientation.x, pose.orientation.y, pose.orientation.z); + m.setIdentity(); + m.block<3, 3>(0, 0) = q.toRotationMatrix(); + m.block<3, 1>(0, 3) = Eigen::Vector3f(pose.position.x, pose.position.y, pose.position.z); + } + } + + /* Calculate component transforms (in world space). */ + std::vector world_transforms(m_nodes.size()); + uint32_t i = 0; + for (const GHOST_XrControllerModelNode &node : m_nodes) { + world_transforms[i] = (node.parent_idx >= 0) ? world_transforms[node.parent_idx] * + *(Eigen::Matrix4f *)node.local_transform : + *(Eigen::Matrix4f *)node.local_transform; + if (node.component_idx >= 0) { + memcpy(m_components[node.component_idx].transform, + world_transforms[i].data(), + sizeof(m_components[node.component_idx].transform)); + } + ++i; + } +} + +void GHOST_XrControllerModel::getData(GHOST_XrControllerModelData &r_data) +{ + if (m_data_loaded) { + r_data.count_vertices = (uint32_t)m_vertices.size(); + r_data.vertices = m_vertices.data(); + r_data.count_indices = (uint32_t)m_indices.size(); + r_data.indices = m_indices.data(); + r_data.count_components = (uint32_t)m_components.size(); + r_data.components = m_components.data(); + } + else { + r_data.count_vertices = 0; + r_data.vertices = nullptr; + r_data.count_indices = 0; + r_data.indices = nullptr; + r_data.count_components = 0; + r_data.components = nullptr; + } +} + +/** \} */ diff --git a/intern/ghost/intern/GHOST_XrControllerModel.h b/intern/ghost/intern/GHOST_XrControllerModel.h new file mode 100644 index 00000000000..5ff72957b24 --- /dev/null +++ b/intern/ghost/intern/GHOST_XrControllerModel.h @@ -0,0 +1,59 @@ +/* + * 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 GHOST + */ + +/* Note: Requires OpenXR headers to be included before this one for OpenXR types (XrInstance, + * XrSession, etc.). */ + +#pragma once + +#include +#include +#include + +struct GHOST_XrControllerModelNode; + +/** + * OpenXR glTF controller model. + */ +class GHOST_XrControllerModel { + public: + GHOST_XrControllerModel(XrInstance instance, const char *subaction_path); + ~GHOST_XrControllerModel(); + + void load(XrSession session); + void updateComponents(XrSession session); + void getData(GHOST_XrControllerModelData &r_data); + + private: + XrPath m_subaction_path = XR_NULL_PATH; + XrControllerModelKeyMSFT m_model_key = XR_NULL_CONTROLLER_MODEL_KEY_MSFT; + + std::future m_load_task; + std::atomic m_data_loaded = false; + + std::vector m_vertices; + std::vector m_indices; + std::vector m_components; + std::vector m_nodes; + /** Maps node states to nodes. */ + std::vector m_node_state_indices; + + void loadControllerModel(XrSession session); +}; diff --git a/intern/ghost/intern/GHOST_XrSession.cpp b/intern/ghost/intern/GHOST_XrSession.cpp index cd930c8328b..64aa2f515c4 100644 --- a/intern/ghost/intern/GHOST_XrSession.cpp +++ b/intern/ghost/intern/GHOST_XrSession.cpp @@ -30,6 +30,7 @@ #include "GHOST_IXrGraphicsBinding.h" #include "GHOST_XrAction.h" #include "GHOST_XrContext.h" +#include "GHOST_XrControllerModel.h" #include "GHOST_XrException.h" #include "GHOST_XrSwapchain.h" #include "GHOST_Xr_intern.h" @@ -52,6 +53,8 @@ struct OpenXRSessionData { std::vector swapchains; std::map action_sets; + /* Controller models identified by subaction path. */ + std::map controller_models; }; struct GHOST_XrDrawInfo { @@ -124,7 +127,9 @@ void GHOST_XrSession::initSystem() /** \name State Management * \{ */ -static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose &base_pose) +static void create_reference_spaces(OpenXRSessionData &oxr, + const GHOST_XrPose &base_pose, + bool isDebugMode) { XrReferenceSpaceCreateInfo create_info = {XR_TYPE_REFERENCE_SPACE_CREATE_INFO}; create_info.poseInReferenceSpace.orientation.w = 1.0f; @@ -160,10 +165,11 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose & * since runtimes are not required to support the stage reference space. If the runtime * doesn't support it then just fall back to the local space. */ if (result == XR_ERROR_REFERENCE_SPACE_UNSUPPORTED) { - printf( - "Warning: XR runtime does not support stage reference space, falling back to local " - "reference space.\n"); - + if (isDebugMode) { + printf( + "Warning: XR runtime does not support stage reference space, falling back to local " + "reference space.\n"); + } create_info.referenceSpaceType = XR_REFERENCE_SPACE_TYPE_LOCAL; CHECK_XR(xrCreateReferenceSpace(oxr.session, &create_info, &oxr.reference_space), "Failed to create local reference space."); @@ -179,11 +185,12 @@ static void create_reference_spaces(OpenXRSessionData &oxr, const GHOST_XrPose & CHECK_XR(xrGetReferenceSpaceBoundsRect(oxr.session, XR_REFERENCE_SPACE_TYPE_STAGE, &extents), "Failed to get stage reference space bounds."); if (extents.width == 0.0f || extents.height == 0.0f) { - printf( - "Warning: Invalid stage reference space bounds, falling back to local reference space. " - "To use the stage reference space, please define a tracking space via the XR " - "runtime.\n"); - + if (isDebugMode) { + printf( + "Warning: Invalid stage reference space bounds, falling back to local reference " + "space. To use the stage reference space, please define a tracking space via the XR " + "runtime.\n"); + } /* Fallback to local space. */ if (oxr.reference_space != XR_NULL_HANDLE) { CHECK_XR(xrDestroySpace(oxr.reference_space), "Failed to destroy stage reference space."); @@ -252,7 +259,7 @@ void GHOST_XrSession::start(const GHOST_XrSessionBeginInfo *begin_info) "detailed error information to the command line."); prepareDrawing(); - create_reference_spaces(*m_oxr, begin_info->base_pose); + create_reference_spaces(*m_oxr, begin_info->base_pose, m_context->isDebugMode()); /* Create and bind actions here. */ m_context->getCustomFuncs().session_create_fn(); @@ -916,3 +923,71 @@ void GHOST_XrSession::getActionCustomdataArray(const char *action_set_name, } /** \} */ /* Actions */ + +/* -------------------------------------------------------------------- */ +/** \name Controller Model + * + * \{ */ + +bool GHOST_XrSession::loadControllerModel(const char *subaction_path) +{ + if (!m_context->isExtensionEnabled(XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME)) { + return false; + } + + XrSession session = m_oxr->session; + std::map &controller_models = m_oxr->controller_models; + std::map::iterator it = controller_models.find( + subaction_path); + + if (it == controller_models.end()) { + XrInstance instance = m_context->getInstance(); + it = controller_models + .emplace(std::piecewise_construct, + std::make_tuple(subaction_path), + std::make_tuple(instance, subaction_path)) + .first; + } + + it->second.load(session); + + return true; +} + +void GHOST_XrSession::unloadControllerModel(const char *subaction_path) +{ + std::map &controller_models = m_oxr->controller_models; + if (controller_models.find(subaction_path) != controller_models.end()) { + controller_models.erase(subaction_path); + } +} + +bool GHOST_XrSession::updateControllerModelComponents(const char *subaction_path) +{ + XrSession session = m_oxr->session; + std::map::iterator it = m_oxr->controller_models.find( + subaction_path); + if (it == m_oxr->controller_models.end()) { + return false; + } + + it->second.updateComponents(session); + + return true; +} + +bool GHOST_XrSession::getControllerModelData(const char *subaction_path, + GHOST_XrControllerModelData &r_data) +{ + std::map::iterator it = m_oxr->controller_models.find( + subaction_path); + if (it == m_oxr->controller_models.end()) { + return false; + } + + it->second.getData(r_data); + + return true; +} + +/** \} */ /* Controller Model */ diff --git a/intern/ghost/intern/GHOST_XrSession.h b/intern/ghost/intern/GHOST_XrSession.h index a76e11aede1..83de44c8d8e 100644 --- a/intern/ghost/intern/GHOST_XrSession.h +++ b/intern/ghost/intern/GHOST_XrSession.h @@ -90,6 +90,12 @@ class GHOST_XrSession { uint32_t getActionCount(const char *action_set_name); void getActionCustomdataArray(const char *action_set_name, void **r_customdata_array); + /** Controller model functions. */ + bool loadControllerModel(const char *subaction_path); + void unloadControllerModel(const char *subaction_path); + bool updateControllerModelComponents(const char *subaction_path); + bool getControllerModelData(const char *subaction_path, GHOST_XrControllerModelData &r_data); + private: /** Pointer back to context managing this session. Would be nice to avoid, but needed to access * custom callbacks set before session start. */ diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c index 98a8553a3eb..bba72c907eb 100644 --- a/intern/guardedalloc/intern/mallocn_guarded_impl.c +++ b/intern/guardedalloc/intern/mallocn_guarded_impl.c @@ -89,7 +89,7 @@ typedef struct localListBase { void *first, *last; } localListBase; -/* note: keep this struct aligned (e.g., irix/gcc) - Hos */ +/* NOTE(@hos): keep this struct aligned (e.g., IRIX/GCC). */ typedef struct MemHead { int tag1; size_t len; @@ -98,9 +98,8 @@ typedef struct MemHead { const char *nextname; int tag2; short pad1; - short alignment; /* if non-zero aligned alloc was used - * and alignment is stored here. - */ + /* if non-zero aligned allocation was used and alignment is stored here. */ + short alignment; #ifdef DEBUG_MEMCOUNTER int _count; #endif diff --git a/intern/libmv/CMakeLists.txt b/intern/libmv/CMakeLists.txt index 91ef1f4d038..596e8efa082 100644 --- a/intern/libmv/CMakeLists.txt +++ b/intern/libmv/CMakeLists.txt @@ -156,6 +156,7 @@ if(WITH_LIBMV) libmv/base/scoped_ptr.h libmv/base/vector.h libmv/base/vector_utils.h + libmv/build/build_config.h libmv/image/array_nd.h libmv/image/convolve.h libmv/image/correlation.h @@ -199,6 +200,7 @@ if(WITH_LIBMV) libmv/simple_pipeline/reconstruction_scale.h libmv/simple_pipeline/resect.h libmv/simple_pipeline/tracks.h + libmv/threading/threading.h libmv/tracking/brute_region_tracker.h libmv/tracking/hybrid_region_tracker.h libmv/tracking/kalman_filter.h diff --git a/intern/libmv/ChangeLog b/intern/libmv/ChangeLog index 81096dd90c9..80e69878c99 100644 --- a/intern/libmv/ChangeLog +++ b/intern/libmv/ChangeLog @@ -1,520 +1,556 @@ -commit efd7a93317e0278b99e66785f667823e451daef1 -Author: Sergey Sharybin -Date: Tue May 9 10:16:42 2017 +0200 +commit f701b2b9fbc641b3252b3513239eeb14477ed5e1 +Author: Lazydodo +Date: Wed Aug 21 10:38:33 2019 -0600 - Fix strict compiler warnings, unused variables - -commit 8efd47e13dfdd3f7209bc96f26d0b13127dd6376 -Author: Sergey Sharybin -Date: Wed Dec 14 10:44:57 2016 +0100 - - Fix T50243: libmv_panography_test is broken + Cleanup: Fix build error with MSVC - There was fully wrong logic in comparison: was actually accessing memory - past the array boundary. Run test manually and the figure seems correct - to me now. + Previously eigens internal include order somehow implicitly provided + M_PI and friends. The recent eigen version bump broke this implicit + behaviour, better to be explicit that we need the math defines for MSVC. + +commit 5b2892f5a3cb6a7a2300f1c32e93d90e22302286 +Author: Sergey Sharybin +Date: Mon Mar 15 14:58:13 2021 +0100 + + Cleanup: Spelling in Libmv comments + +commit 54cdd2fe5cad29df83cb4d3c3ef1e02e06b022a7 +Author: Sergey Sharybin +Date: Thu Oct 21 12:09:30 2021 +0200 + + Ensure clang-format is disabled for 3rd party libraries + +commit 517d51654d6a4cf790a340b657cc0f7dc00f3158 +Author: Campbell Barton +Date: Tue Jul 13 21:58:45 2021 +1000 + + Fix x/y mismatch in retract region tracker - Spotted by @LazyDodo, thanks! - -commit 6dfb9cd1bd14669d84be789000ce234747fb00ff -Author: Sergey Sharybin -Date: Thu Jul 14 11:49:38 2016 +0200 - - Fix some strict compiler warnings + Correct X/Y mismatch in RetrackRegionTracker. - One of them was a real bug! - -commit f61adaecf7b29ebe6677be0e1c825f0a8d475e4b -Author: Sergey Sharybin -Date: Wed May 31 11:22:34 2017 +0200 - - Enable explicit schur complement for BA step - - This is something we do in Blender and only reason it was not - enabled for standalone Libmv is because we did not have fresh - enough version of Ceres bundled. - -commit fc5d3a1d4880c6658aff693c1c1e8c10c96ce1a7 -Author: Sergey Sharybin -Date: Wed Nov 2 15:32:11 2016 +0100 - - Update tests to make tests pass after recent Ceres update - - Just a precision issue, difference is around 1e-7. Should be fine to - simply update expected value. - -commit e1ac9f6124110c1a90d8e417bea47acfcbdcca42 -Author: Sergey Sharybin -Date: Wed May 31 10:54:48 2017 +0200 - - Update Ceres to latest release 1.12.0 - -commit ac1571352b4962f110929b963f8616d7310ceea5 -Author: Sergey Sharybin -Date: Fri Apr 7 17:10:44 2017 +0200 - - Fix crash of keyframe selection on 32bit linux - -commit 5f8df3da965686df39a6ae5c9f17482075017bf4 -Author: Sergey Sharybin -Date: Tue Jan 19 14:00:53 2016 +0500 - - Solve some strict warnings in tests - -commit 8ea3a5d752a9ce3337ab7643897472a4d33747f1 -Author: Brecht Van Lommel -Date: Sat Feb 18 23:52:31 2017 +0100 - - Fix a few compiler warnings with macOS / clang. - -commit ffbe81461770e70736e80b8cab8e6eb1f8b27160 -Author: Mike Erwin -Date: Wed May 31 10:43:08 2017 +0200 - - Fix comparison of identicals - - Some of these check that dimensions match before running code that - assumes they do match. - - Found with PVS-Studio T48917. - -commit 206c01999cde16c1c6c43a8e13ffa86020821d98 -Author: Sergey Sharybin -Date: Wed May 31 10:39:16 2017 +0200 - - Add basic track masking API in place - - This brings back ability to mask non-interesting parts of - specific track (the feature got lost with new auto-track API). - - Added it back by extending frame accessor class. This isn't really - a frame thing, but we don't have other type of accessor here. - - Surely, we can use old-style API here and pass mask via region - tracker options for this particular case, but then it becomes much - less obvious how real auto-tracker will access this mask with old - style API. - - So seems we do need an accessor for such data, just matter of - finding better place than frame accessor. - -commit faa069cb826892780356477cc10602390fecf06b -Author: Sergey Sharybin -Date: Wed May 31 10:36:26 2017 +0200 - - Tests: Tweak epsilon to avoid what looks a false-positive failure - -commit 7c84e45c1d330871477ba3516f57178e5b9d101f -Author: Sergey Sharybin -Date: Wed May 31 10:15:43 2017 +0200 - - CMake: Fix mistake in closing branch - -commit cb769a0d319a8c95948153d78a4c3378a0142ece -Author: Sergey Sharybin -Date: Thu Jul 21 12:52:33 2016 +0200 - - Set of fixes for MSVC215 - - - Move GLOG/GFLAGS defines to a more global scope, - this way ANY of our own libraries will use proper - declspec. - - - Compile png/zlib/openexif on Windows as well since - those are required for a correct linking. - -commit bb95c8654fd2cea72d66ed04cd825cc3712ea804 -Author: Sergey Sharybin -Date: Wed Jul 20 18:14:46 2016 +0200 - - Disable unexisting Ceres option - - Explicit Schur complement requires having - newer Ceres than we currently have bundled. - -commit a2e12c959ef32cc9382244d1581992c2f7aa9c09 -Author: Sergey Sharybin -Date: Wed Jul 20 18:04:57 2016 +0200 - - Various fixes for MSVC - - - Update Eigen to 3.2.7 since this brings crucial - fixes for MSVC 2015. - - - Switch to STATIC build by default. - - There are issues building current sources as dynamic - libraries with MSVC2015 and additionally building - dynamic Ceres is not recommended anyway, so let's - not do this for the time being. - - If anyone finds a way to make this all working -- - it'llsurely be a welcome addition. - -commit 7a676106720fb126a27ff010abdd8bb65d7e0d9a -Author: Sergey Sharybin -Date: Mon Jan 4 18:30:12 2016 +0500 - - Fix strict compiler warnings - -commit d3537e3709fe11f42312e82cb1c9837c9e742385 -Author: Sergey Sharybin -Date: Sun Jan 3 14:17:10 2016 +0500 - - GLog/GFlags: Reduce difference between upstream and bundled versions - - Several things here: - - - Re-bundled sources using own fork with pull-requests applied on the sources. - - - Got rid of changes around include "config.h", it was needed by Blender to - make it's include directories configuration to work. This could be addressed - differently from Blender side. - - - Moved some customization to defines set by CMakeLists. - -commit 1ec37bba2cfbbf0d6568429fa3035ee2164c23e6 -Author: Sergey Sharybin -Date: Sat Jan 2 12:42:55 2016 +0500 - - GFlags linking errors fix for MSVC - -commit df7642b270e8e43685e9ffb404b59d7b226a9f60 -Author: Sergey Sharybin -Date: Thu Dec 31 17:56:12 2015 +0500 - - Alternative fix for missing prototype for a couple of functions - -commit 08f685797b7d776cdaa579136c82b15ddc6ffb30 -Author: Sergey Sharybin -Date: Thu Dec 31 17:33:05 2015 +0500 - - Update GFlags to the latest upstream version - - Makes it easier to synchronize some compiler/warning fixes. - -commit e0ef5b09203e3906a555e6c2010f25cb667da9cd -Author: Sergey Sharybin -Date: Thu Dec 31 16:15:59 2015 +0500 - - GLog: Solve some compilation warnings - - Those are actually sent to a pull-request, see - - https://github.com/google/glog/pull/81 - -commit 2072b213d4d3a55d099a063ed1e7331cc773454e -Author: Sergey Sharybin -Date: Thu Dec 31 16:13:53 2015 +0500 - - Add Martijn Berger to the AUTHORS file - -commit 4dd0770d98d67896e4f936356e281f63d927410e -Author: Martijn Berger -Date: Thu Dec 31 16:13:08 2015 +0500 - - Fix compilation error of Glog and Gflags with MSVC2015 - -commit 2712f42be2ad79e7d3a6c9905f6d8d1e3b7133ac -Author: Brecht Van Lommel -Date: Thu Dec 31 14:00:58 2015 +0500 - - Fix OS X (with 10.11 SDK) glog build errors due to using deprecated code. - - Some values are now restored to the ones from before the upgrade today. - -commit d249280fdf7c937fd6ebbc465508843a70aafd4c -Author: Sergey Sharybin -Date: Wed Dec 30 16:59:28 2015 +0500 - - Tweaks to Glog to support building on all platforms - - This makes it possible to compile Libmv on all platforms, - amount of hacks is lower, which could bring some warnings - up, but those are better be addressed via upstream which - is now rather active. - -commit 86c57750ddb857643fb5dd2c83b4953da83dd57d -Author: Sergey Sharybin -Date: Wed Dec 30 16:15:47 2015 +0500 - - Enable explicit Schur complement matrix by default - - Gives up to 2x speed up of camera solving process in average scene. - In the really huge one it might be slower, but that we need to investigate. - -commit d6c52a70b5a0664b7c74bda68f59a895fe8aa235 -Author: Sergey Sharybin -Date: Wed Dec 30 16:13:03 2015 +0500 - - Fix one frame memory leak when tracking last frame - -commit 6e2ac41d25d5923b2a62c96d27d919a36eff9b48 -Author: Brecht Van Lommel -Date: Wed Dec 30 16:11:24 2015 +0500 - - Motion tracking not workig with Xcode 7 on OS X. - - Caused by use of the uninitialized shape_ variable in Resize(). - -commit fc72ae06fb4ae559ac37d14d1b34d6669505cc86 -Author: Sergey Sharybin -Date: Wed Dec 30 15:56:40 2015 +0500 - - Update GLog to latest upstream - - Should fix issues building with MSVC2015. - -commit d4b2d15bd3d195074b074331354de96a1b51042f -Author: Sergey Sharybin -Date: Wed Dec 30 16:01:10 2015 +0500 - - Fix wrong README file reference - -commit 2b4aa0b7720cae9a408284834559bea9960157ee -Author: Keir Mierle -Date: Mon May 11 02:16:53 2015 -0700 - - Make README more informative for GitHub viewers - - Reviewers: sergey + NOTE: This isn't used at the moment. Reviewed By: sergey - Differential Revision: https://developer.blender.org/D1295 + Ref D11895 -commit 514e4491aea655d20be047ed87f002fb7854d5c9 -Author: Keir Mierle -Date: Mon May 11 01:54:09 2015 -0700 +commit cfcfc803cf599c3bd7bbfa3bac7d5e9c8bca284b +Author: Jesse Yurkovich +Date: Mon Jul 12 21:01:18 2021 -0700 - Simplify the modal solver Ceres cost function + Cleanup: Use correct _WIN32/64 defines for MSVC - Fix test by flipping the quaternion. + Docs: https://docs.microsoft.com/en-us/cpp/preprocessor/predefined-macros - Reviewers: sergey + Differential Revision: https://developer.blender.org/D11460 + +commit ad8dfd41958b0e4cb90c7c4e5cc20f93c0832e73 +Author: Campbell Barton +Date: Thu Jun 24 15:56:58 2021 +1000 + + Cleanup: comment blocks, trailing space in comments + +commit 9a6cc5daa6c38f33ce2385fe489c07d97586d452 +Author: Campbell Barton +Date: Sun Jun 13 15:11:40 2021 +1000 + + Fix new[]/delete[] mismatch + +commit 5591d12928df74ed3517b5e61eeda0b64e6ade4f +Author: Sergey Sharybin +Date: Mon Mar 22 15:16:07 2021 +0100 + + Fix T86591: Tracking backwards is slower - Reviewed By: sergey + The root of the issue was caused by the PredictMarkerPosition() + always returning false when tracking backwards. This was making + it so tracker always had to run brute initialization, which is + an expensive operation. - Projects: #libmv + From own timing here: - Differential Revision: https://developer.blender.org/D756 - -commit e55fafd31f7d53d42af7c6b7df2eebe3c2568da9 -Author: Sergey Sharybin -Date: Wed Dec 31 19:05:51 2014 +0500 - - Synchronize MSVC compilation fixes from Blender - -commit 7d6020d2ec42c6cb2749bc891186b4880d26d40b -Author: Sergey Sharybin -Date: Wed Dec 31 15:32:07 2014 +0500 - - Update GLog to latest upstream revision 143 + - Tracking forward takes 0.667637 seconds + - Tracking backward used to take 2.591856 seconds + - Tracking backward now takes 0.827724 seconds - Mainly to solve compilation error with demangle.cc. + This is a very nice speedup, although the tracking backwards is + still somewhat slower. Will be investigated further as part of + a regular development. -commit 5dc746700eaf85cb674f0fb73ff3c1b49a7f6315 -Author: Sergey Sharybin -Date: Fri Dec 12 14:59:55 2014 +0500 +commit ad9546d2319b0db3e1ccc41c4f84899729d1ad1e +Author: Sergey Sharybin +Date: Mon Mar 15 15:48:15 2021 +0100 - Update GFlags to latest release 2.1.1 + Fix T86262: Tracking backwards fails after gap in track - Main purpose of this (andsome of upcoming) update is to check if the - upstream sources are useable without any modifications for us. And if - not, then we'll need to consider moving some changes into upstream. - - This commit contains an one-to-one copy of the upstream GFlags library - and also changes namespace usage since it's changed in the upstream. + The issue was caused by a prediction algorithm detecting tracking the + wrong way. Solved by passing tracking direction explicitly, so that + prediction will always happen correctly regardless of the state of the + Tracks context. -commit 6fe6d75f7e90e161b44643b953f058a3829a5247 -Author: Sergey Sharybin -Date: Sat Nov 1 02:53:36 2014 +0500 +commit 0773a8d6dfe1bf997b8e42d61d136a65b67fde88 +Author: Sergey Sharybin +Date: Thu Oct 21 11:18:10 2021 +0200 - Libmv: Code cleanup, mixed class/struct in declaration/definition + Cleanup: clang-format + + Is based on Google style which was used in the Libmv project before, + but is now consistently applied for the sources of the library itself + and to C-API. With some time C-API will likely be removed, and it + makes it easier to make it follow Libmv style, hence the diversion + from Blender's style. + + There are quite some exceptions (clang-format off) in the code around + Eigen matrix initialization. It is rather annoying, and there could be + some neat way to make initialization readable without such exception. + + Could be some places where loss of readability in matrix initialization + got lost as the change is quite big. If this has happened it is easier + to address readability once actually working on the code. + + This change allowed to spot some missing header guards, so that's nice. + + Doing it in bundled version, as the upstream library needs to have some + of the recent development ported over from bundle to upstream. + + There should be no functional changes. -commit d2a5f7953812d2d09765431b59c6c4ac72faf35b -Author: Sergey Sharybin -Date: Thu Oct 30 23:13:53 2014 +0500 +commit c4de0ccd5aede3bb90ac6ad1039a83c2260fbefd +Author: Sergey Sharybin +Date: Tue Feb 23 16:43:01 2021 +0100 - Libmv: Support disabled color channels in tracking settings + Avoid use of LOG(INFO) in solver - This was never ported to a new tracking pipeline and now it's done using - FrameAccessor::Transform routines. Quite striaghtforward, but i've changed - order of grayscale conversion in blender side with call of transform callback. - - This way it's much easier to perform rescaling in libmv side. + Usage of LOG(INFO) actually went against own guidelines in the + logging.h: the INFO is for messages which are to be printed + regardless of debug/verbosity settings. -commit d976e034cdf74b34860e0632d7b29713f47c5756 -Author: Keir Mierle -Date: Sat Aug 23 00:38:01 2014 -0700 +commit 94d925131b08e23cea7fdf735923e24b78a8c7fd +Author: Campbell Barton +Date: Fri Feb 5 16:23:34 2021 +1100 - Minor keyframe selection cleanups - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D757 + Cleanup: correct spelling in comments -commit bc99ca55dadfca89fde0f93764397c2fe028943d -Author: Sergey Sharybin -Date: Sat Aug 23 01:55:32 2014 +0600 +commit db0c8dbea19d684899dffcb46a2e79c12dc91e6b +Author: Sergey Sharybin +Date: Tue Dec 1 14:52:08 2020 +0100 - implement backward prediction + Tweak default logging verbosity level - The title actually says it all, just extend current implementation - of PredictMarkerPosition() to cases when tracking happens in the reverse - order (from the end frame to start). + Log to verbosity level 1 rather than INFO severity. - it's still doesn't solve all the ambiguity happening in the function - in cases when one tracks the feature and then re-tracks it in order - to refine the sliding. This is considered a separate TODO for now and - will likely be solved by passing tracking direction to the prediction + Avoids a lot of overhead coming from construction of the INFO stream + and improves performance and threadability of code which uses logging. + + This makes tracking of 250 frames of a track of default settings to + drop down from 0.6sec to 0.4sec. + +commit 8f2c13edf0d3b0c8a2ad90d6ae13f11aed457709 +Author: Sergey Sharybin +Date: Tue Dec 1 14:46:35 2020 +0100 + + Cleanup, remove unused logging macros + + Unused and was not entirely happy with such short abbreviations. + +commit 1dc2ab9847ce6cc84ab6c8e3e8613687480d735d +Author: Sergey Sharybin +Date: Fri Nov 27 15:58:55 2020 +0100 + + Add threading primitives + + Allows to use mutex, scoped_lock, and conditional_variable from within + the libmv namespace. + + Implementation is coming from C++11. Other configurations are easy to + implement, but currently C++11 is the way to go. + +commit 6531b66fd3bf4b5ee112b904b390d164a03628c5 +Author: Sergey Sharybin +Date: Fri Nov 27 15:43:44 2020 +0100 + + Add build configuration header + + Allows to easily access build platform information, such as bitness, + compiler, supported C++ version and so on. + +commit 632c356375e4ac07da43b346384fdf67e7916426 +Author: Campbell Barton +Date: Fri Nov 6 14:35:38 2020 +1100 + + Cleanup: doxygen comments + +commit c9479472ce8d63b1d6e4c9c1b072b975479cd4c0 +Author: Campbell Barton +Date: Fri Nov 6 11:25:27 2020 +1100 + + Cleanup: follow our code style for float literals + +commit 6455055b265cddbf604523653b01c9d7adbc23d5 +Author: Ivan Perevala +Date: Thu Oct 29 10:19:06 2020 +0100 + + Fix clang inconsistent-missing-override warnings. + + Reviewed By: sergey, ankitm + + Differential Revision: https://developer.blender.org/D9377 + +commit 020fc13a5336c872c3251e45d28cc02584094f33 +Author: Sergey Sharybin +Date: Wed Oct 21 10:53:13 2020 +0200 + + Simplify configuration of intrinsics to refine + + Previously, only predefined and limited set of intrinsics combinations + could have been refined. This was caused by a bundle adjustment library + used in the early days of the solver. + + Now it is possible to fully customize which intrinsics are to be refined + during camera solving. Internally solver supports per-parameter settings + but in the interface they are grouped as following: + + * Focal length + * Optical center + * Radial distortion coefficients (which includes k1, k2, k3, k4) + * Tangential distortion coefficients (which includes p1, p2) + + Differential Revision: https://developer.blender.org/D9294 + +commit 5ef3c2c41854a2e634ba692e9b63194aa74b928d +Author: Sergey Sharybin +Date: Wed Oct 21 10:48:13 2020 +0200 + + Fix typo in packed intrinsics + + Was using doing an implicit cast of floating point value to boolean. + Was not noticed before because the boolean value was never never used. + +commit 648388878525aa585bdbbed2e457186b6483bd5e +Author: Sergey Sharybin +Date: Tue Oct 13 11:34:05 2020 +0200 + + Refactor camera intrinsics parameter block + + Use the newly introduced packed intrinsics, which allows to remove + code which was initializing parameters block based on distortion + model type. + + Now such initialization is done by a particular implementation of + a distortion model. + + Differential Revision: https://developer.blender.org/D9192 + +commit 60780e30a8aec37f53d4e952375ffc8d720341c6 +Author: Sergey Sharybin +Date: Tue Oct 13 11:32:35 2020 +0200 + + Add generic class for packed intrinsics + + This is a common class which can be used in all sort of minimization + problems which needs camera intrinsics as a parameter block. + + Currently unused, but will replace a lot of hard-coded logic in the + bundle adjustment code. + +commit 409924c76be27fec3b619d79379df76cb8de6a9a +Author: Sergey Sharybin +Date: Tue Oct 13 11:25:34 2020 +0200 + + Add array to libmv namespace + +commit 6411c7fed3c30dce05b9b2df239b7f4cf91cdecd +Author: Sebastian Parborg +Date: Tue Oct 20 14:45:54 2020 +0200 + + Fix test on windows + + There is no point in testing std::vector capacity as it can differ + between std implementations. + +commit 242d31ff4ed78a6f36ac29f139b03abc4f32f4f5 +Author: Harley Acheson +Date: Mon Oct 19 08:51:50 2020 -0700 + + Spelling: Miscellaneous + + Corrects 34 miscellaneous misspelled words. + + Differential Revision: https://developer.blender.org/D9248 + + Reviewed by Campbell Barton + +commit 91cd0310d6762fdcc7253303bec7f0453cf1f18f +Author: Harley Acheson +Date: Mon Oct 19 08:12:33 2020 -0700 + + Spelling: It's Versus Its + + Corrects incorrect usage of contraction for 'it is', when possessive 'its' was required. + + Differential Revision: https://developer.blender.org/D9250 + + Reviewed by Campbell Barton + +commit 788ed5fa8ace0df951fe8f03e6510187ed694044 +Author: Sebastian Parborg +Date: Mon Oct 19 13:03:06 2020 +0200 + + Fix alignment issues when compiling with AVX support + + There would be eigen alignment issues with the custom libmv vector + class when compiling with AVX optimizations. This would lead to + segfaults. + + Simply use the std::vector base class as suggested by the old TODO in + the class header. + + Reviewed By: Sergey + + Differential Revision: http://developer.blender.org/D8968 + +commit 21a114476eaafec4b25c0ea19eaccdd9e870160d +Author: Sergey Sharybin +Date: Mon Oct 12 14:59:18 2020 +0200 + + Remove array access from camera intrinsics + + That was a suboptimal decision from back in the days, which ended up + being problematic. It is no longer used, so remove it from API making + it so new code does not depend on this weak concept. + +commit b6a579fd88de5e13c812310a0416e698648578c4 +Author: Sergey Sharybin +Date: Mon Oct 12 12:17:55 2020 +0200 + + Fix wrong packing order of intrinsics for BA step + + The order got broken when Brown distortion model has been added. + Made it so the indexing of parameters is strictly defined in the + parameter block, matching how parameters are used in the cost function. - Reviewers: keir + There is some duplication going on accessing parameters. This can + be refactored in the future, by either moving common parts packing + and cost function to an utility function in bundle.cc. + Alternatively, can introduce a public PackedIntrinsics class which + will contain a continuous block of parameters, and each of the + camera models will have API to be initialized from packed form and + to create this packed form. - Reviewed By: keir + The benefit of this approach over alternative solutions previously + made in the master branch or suggested in D9116 is that the specific + implementation of BA does not dictate the way how public classes need + to be organized. It is API which needs to define how implementation + goes, not the other way around. - Differential Revision: https://developer.blender.org/D663 + Thanks Bastien and Ivan for the investigation! -commit 5b87682d98df65ade02638bc6482d824cf0dd0b3 -Author: Keir Mierle -Date: Thu Aug 21 22:45:22 2014 -0700 +commit 595d4a45db2f1d68bc6091b6fad3580411a10e40 +Author: Sergey Sharybin +Date: Mon Oct 12 10:46:31 2020 +0200 - Make libmv compile on Ubuntu 14.04 + Fix memory leak in modal solver - Reviewers: fsiddi + The leak was happening when problem did not have any parameters blocks + defined. This happens, for example, if there are no 3D points at all, + or when all markers are set to 0 weight. - Reviewed By: fsiddi - - Subscribers: sergey - - Differential Revision: https://developer.blender.org/D755 + Was noticeable in libmv_modal_solver_test when building with LSAN + enabled. -commit 0a81db623c458e0384b4f7060d1bcff8993fb469 +commit b6ecdc497c00e5f9a0a6dffe1c27051c8df8482a +Author: Sergey Sharybin +Date: Mon Oct 12 10:44:57 2020 +0200 + + Cleanup, spelling in function name + + Is a local function, not affecting API. + +commit 2a712777076c076fcf8aae9b9657089d81fd129f +Author: Ivan Perevala +Date: Wed Sep 30 15:12:14 2020 +0200 + + Implement Brown-Conrady distortion model + + Implemented Brown-Conrady lens distortion model with 4 radial and + 2 tangential coefficients to improve compatibility with other software, + such as Agisoft Photoscan/Metashapes, 3DF Zephir, RealityCapture, + Bentley ContextCapture, Alisevision Meshroom(opensource). + + Also older programs: Bundler, CPMVS. + In general terms, most photogrammetric software. + + The new model is available under the distortion model menu in Lens + settings. + + For tests and demos check the original patch. + + Reviewed By: sergey + + Differential Revision: https://developer.blender.org/D9037 + +commit e4e2b8c382f9bb2490318cd621a438ccb4dd7327 +Author: Ivan +Date: Mon Sep 28 09:57:03 2020 +0200 + + Fix NukeCameraIntrinsics copy constructor + + Copy the appropriate parameter + + Reviewed By: sergey + + Differential Revision: https://developer.blender.org/D9014 + +commit 1e5deb138ad1ef79204b118f4dc741f96bd0e650 +Author: Johan Walles +Date: Tue Jul 7 11:09:31 2020 +0200 + + Add units to motion tracking solve errors + + The unit being "pixels". + + Before this change the solve errors were unitless in the UI. + + With this change in place, the UI is now clear on that the unit of the + reprojection errors is pixels (px). + + Differential Revision: https://developer.blender.org/D8000 + +commit a8a5a701f2839a97af51825d98726e7eb7e6eb1d +Author: Campbell Barton +Date: Wed Jul 1 13:12:24 2020 +1000 + + Cleanup: spelling + +commit fd83866975d5d8163d9bb71d645239562cb329b5 Author: Sergey Sharybin -Date: Wed Jul 23 00:42:00 2014 +0600 +Date: Thu Jun 18 10:12:01 2020 +0200 - Fix wrong residual blocks counter + Update Ceres to the latest upstream version - This happened in cases when having zero-weighted tracks - and could lead to some assert failures on marking parameter - block constant. + Using latest master because of various compilation error fixes. + + Brings a lot of recent development. From most interesting parts: + + - New threading model. + - Tiny solver. + - Compatibility with C++17. -commit 2824dbac54cacf74828678be7a5c9fd960ce83e2 +commit 307e9a945a320a1b7d9af210c2da1cfbb6439ec5 Author: Sergey Sharybin -Date: Fri Jul 18 12:52:03 2014 +0600 +Date: Fri May 15 14:54:30 2020 +0200 - Fix search area sliding issue + Fix crash solving when having negative frames - The only way to do this is to store search region in floats - and round when we need to sample it. Otherwise you'll always - have sliding effect caused by rounding the issues, especially - when doing incremental offset (thing which happens in the - prediction code). + Don't use linear array with frame as an index since it has the + following disadvantages: - Pretty much straightforward change apart from stuff to be kept - in mind: offset calculation int should happen relative to the - rounded search region. This is because tracker works in the space - of the search window image which get's rounded on the frame access, + - Requires every application to take care of frame remapping, which + could be way more annoying than it sounds. - This makes API a bit creepy because frame accessor uses the same - Region struct as the search window in Marker and ideally we would - need to have either IntRegion or Region in order to make - Libmv fully track on what's getting rounded and when. + - Inefficient from memory point of view when solving part of a footage + which is closer to the end of frame range. - Reviewers: keir + Using map technically is slower from performance point of view, but + could not feel any difference as the actual computation is way more + complex than access of camera on individual frames. - Reviewed By: keir - - Differential Revision: https://developer.blender.org/D616 + Solves crash aspect of T72009 -commit 04862c479332308be47a0f27361402444ace8880 -Author: Keir Mierle -Date: Fri May 9 23:00:03 2014 +0200 +commit 730eec3e0a382b23648d9000e07218519e1223d1 +Author: Sergey Sharybin +Date: Fri May 15 11:08:18 2020 +0200 - Start the automatic 2D tracking code - - This starts the 2D automatic tracking code. It is totally unfinished. - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D523 + Add map utility -commit be679f67d807a2139c1f7d7e2ca45141940b30d5 -Author: Keir Mierle -Date: Fri May 9 14:36:04 2014 +0200 +commit 0d3f5d94474553c51a1e5d830521fca4ee82aa54 +Author: Sergey Sharybin +Date: Fri May 15 11:05:07 2020 +0200 - Also shift the search window - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D520 + Cleanup, spelling -commit 66b8f5eef2633ebcde32a388fc14c60171011821 -Author: Keir Mierle -Date: Fri May 9 13:06:28 2014 +0200 +commit d26503ab5a9d2faef0f388695d6bb1c46ffff4eb +Author: Aaron Carlisle +Date: Thu May 7 23:42:22 2020 -0400 - Change the search region to absolute frame coordinates - - Smarter Eigen usage - - Better error logging - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D519 + Cleanup: Doxygen: fix markup warnings for links -commit a08193319ae409fad8f08887eae1f79f02e91eaa -Author: Keir Mierle -Date: Fri May 9 12:02:47 2014 +0200 +commit 9e8e94e8281abb6edb5e81afe4365743994800d2 +Author: Sergey Sharybin +Date: Mon Apr 20 17:33:03 2020 +0200 - First cut at predictive tracing + Implement Nuke/Natron distortion model - This adds a Kalman filter-based approach to predict where a marker - will go in the next frame to track. Hopefully this will make the - tracker work faster by avoiding lengthy searches. This code - compiles, but is otherwise untested, and likely does not work. + Neither Nuke nor Natron support OpenCV's radial distortion model + which makes it impossible to have any kind of interoperability. - Fix else branch + The new model is available under the distortion model menu in Lens + settings. - Add some tests - - Update patch coordinates as well (and test) - - Reviewers: sergey - - Reviewed By: sergey - - Differential Revision: https://developer.blender.org/D518 + Differential Revision: https://developer.blender.org/D7484 -commit 607ffb2f62b56e34a841abbb952d83e19cd1e23c -Author: Keir Mierle -Date: Thu May 8 16:05:28 2014 +0200 +commit 1dc83e7545fc832180322f492245933fb343f158 +Author: Sergey Sharybin +Date: Tue Apr 21 16:41:23 2020 +0200 - Add constructor to AutoTrack - -commit c39e20a0c27da3733804c3848454b5d4c4f0e66b -Author: Keir Mierle -Date: Thu May 8 16:04:20 2014 +0200 - - Fix GetMarker compilation issue - -commit 8dd93e431b6e44439c803bfd26ec2669b656177e -Author: Keir Mierle -Date: Thu May 8 15:50:26 2014 +0200 - - Expose GetMarker() in AutoTrack + Cleanup, naming - Reviewers: sergey + Initial bundle adjustment only supported OpenCV's radial distortion + model, so the cost functor was called after it. - Reviewed By: sergey + Nowadays it supports more than this single model, so naming was a bit + wrong and misleading. + +commit b6df71dd5e5a49b3459c522c10ae98e8795e69b5 +Author: Sergey Sharybin +Date: Tue Apr 21 12:25:45 2020 +0200 + + Cleanup, spelling and naming in bundle adjustment - Differential Revision: https://developer.blender.org/D516 + Just more things which were discovered to be annoying on unclear when + adding more features to this code. + +commit a4bbe3a10a5b6b23acd261b1e6c772a6f6672c00 +Author: Sergey Sharybin +Date: Mon Apr 20 17:26:45 2020 +0200 + + Cleanup, spelling in comment + +commit 4219e9d22bad012e9e64b83a5e26d1d4eff3fcc6 +Author: Sergey Sharybin +Date: Mon Apr 20 12:44:07 2020 +0200 + + De-duplicate creation of residual block + + Allows to centralize logic which is needed to check which cost functor + to use for the specific intrinsics. + +commit 9637ebc18b2bac794c8f11b1c2c092a3a9c3e6d2 +Author: Sergey Sharybin +Date: Mon Apr 20 11:41:01 2020 +0200 + + Cleanup reprojection cost function + + Make it smaller and more clear how and what it operates on. + +commit 1efc975a5457cfee6baf41df67afb3e43834d57f +Author: Sergey Sharybin +Date: Mon Apr 20 11:19:47 2020 +0200 + + Pass entire camera intrinsics to reprojection error functor + + Currently no functional changes, but allows to have access to some + invariant settings of camera intrinsics such as image dimensions. + +commit 0f54f2b305f59fc99764b9c85fe28f35f10faa86 +Author: Sergey Sharybin +Date: Fri Apr 17 17:34:19 2020 +0200 + + Cleanup, rephrase comment + +commit 229912b0e1746145c4ab710f8609ce90f690a8e2 +Author: Sergey Sharybin +Date: Fri Apr 17 17:29:04 2020 +0200 + + Cleanup, fix indentation diff --git a/intern/libmv/bundle.sh b/intern/libmv/bundle.sh index db8f88845a8..b728d038c5b 100755 --- a/intern/libmv/bundle.sh +++ b/intern/libmv/bundle.sh @@ -9,7 +9,8 @@ fi BRANCH="master" -repo="git://git.blender.org/libmv.git" +# repo="git://git.blender.org/libmv.git" +repo="/home/sergey/Developer/libmv" tmp=`mktemp -d` git clone -b $BRANCH $repo $tmp/libmv @@ -26,16 +27,16 @@ done rm -rf $tmp -sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v _test.cc | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d` -headers=`find ./libmv -type f -iname '*.h' | grep -v test_data_sets | sed -r 's/^\.\//\t\t/' | sort -d` +sources=`find ./libmv -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | grep -v _test.cc | grep -v test_data_sets | sed -r 's/^\.\// /' | sort -d` +headers=`find ./libmv -type f -iname '*.h' | grep -v test_data_sets | sed -r 's/^\.\// /' | sort -d` -third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\//\t\t/' | sort -d` -third_headers=`find ./third_party -type f -iname '*.h' | sed -r 's/^\.\//\t\t/' | sort -d` +third_sources=`find ./third_party -type f -iname '*.cc' -or -iname '*.cpp' -or -iname '*.c' | sed -r 's/^\.\// /' | sort -d` +third_headers=`find ./third_party -type f -iname '*.h' | sed -r 's/^\.\// /' | sort -d` -tests=`find ./libmv -type f -iname '*_test.cc' | sort -d | awk ' { name=gensub(".*/([A-Za-z_]+)_test.cc", "\\\\1", $1); printf("\t\tBLENDER_SRC_GTEST(\"libmv_%s\" \"%s\" \"libmv_test_dataset;bf_intern_libmv;extern_ceres\")\n", name, $1) } '` +tests=`find ./libmv -type f -iname '*_test.cc' | sort -d | awk ' { name=gensub(".*/([A-Za-z_]+)_test.cc", "\\\\1", "g", $1); printf(" blender_add_test_executable(\"libmv_%s\" \"%s\" \"\${INC}\" \"\${INC_SYS}\" \"libmv_test_dataset;bf_intern_libmv;extern_ceres\")\n", name, $1) } '` -src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t\t/' | sort -d | uniq` -src_third_dir=`find ./third_party -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\//\t\t/' | sort -d | uniq` +src_dir=`find ./libmv -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\// /' | sort -d | uniq` +src_third_dir=`find ./third_party -type f -iname '*.cc' -exec dirname {} \; -or -iname '*.cpp' -exec dirname {} \; -or -iname '*.c' -exec dirname {} \; | sed -r 's/^\.\// /' | sort -d | uniq` src="" win_src="" for x in $src_dir $src_third_dir; do @@ -119,6 +120,9 @@ set(LIB if(WITH_LIBMV) setup_libdirs() + if(WIN32) + add_definitions(-D_USE_MATH_DEFINES) + endif() add_definitions(\${GFLAGS_DEFINES}) add_definitions(\${GLOG_DEFINES}) add_definitions(\${CERES_DEFINES}) @@ -186,7 +190,9 @@ ${third_headers} if(WITH_GTESTS) - blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "${INC}" "${INC_SYS}" "") + include(GTestTesting) + + blender_add_lib(libmv_test_dataset "./libmv/multiview/test_data_sets.cc" "\${INC}" "\${INC_SYS}" "") ${tests} endif() diff --git a/intern/libmv/files.txt b/intern/libmv/files.txt index 985074bfe50..1a177a84c7a 100644 --- a/intern/libmv/files.txt +++ b/intern/libmv/files.txt @@ -119,6 +119,7 @@ libmv/simple_pipeline/resect.h libmv/simple_pipeline/resect_test.cc libmv/simple_pipeline/tracks.cc libmv/simple_pipeline/tracks.h +libmv/threading/threading.h libmv/tracking/brute_region_tracker.cc libmv/tracking/brute_region_tracker.h libmv/tracking/brute_region_tracker_test.cc @@ -138,6 +139,7 @@ libmv/tracking/track_region.cc libmv/tracking/track_region.h libmv/tracking/trklt_region_tracker.cc libmv/tracking/trklt_region_tracker.h +third_party/.clang-format third_party/msinttypes/inttypes.h third_party/msinttypes/README.libmv third_party/msinttypes/stdint.h diff --git a/intern/libmv/mkfiles.sh b/intern/libmv/mkfiles.sh index 618070f0a81..165b328f9aa 100755 --- a/intern/libmv/mkfiles.sh +++ b/intern/libmv/mkfiles.sh @@ -1,6 +1,6 @@ #!/bin/sh find ./libmv/ -type f | sed -r 's/^\.\///' | sort > files.txt -find ./third_party/ -mindepth 2 -type f | \ +find ./third_party/ -type f | \ grep -v third_party/ceres | \ sed -r 's/^\.\///' | sort >> files.txt diff --git a/intern/libmv/third_party/msinttypes/inttypes.h b/intern/libmv/third_party/msinttypes/inttypes.h index 71f5693def6..0e8af69cb07 100644 --- a/intern/libmv/third_party/msinttypes/inttypes.h +++ b/intern/libmv/third_party/msinttypes/inttypes.h @@ -1,35 +1,32 @@ -/* No need to format 3rd-party compatibility headers. */ -/* clang-format off */ - // ISO C9x compliant inttypes.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// // Copyright (c) 2006 Alexander Chemeris -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. -// +// // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ diff --git a/intern/libmv/third_party/msinttypes/stdint.h b/intern/libmv/third_party/msinttypes/stdint.h index 8dfd65f591d..189ee34571c 100644 --- a/intern/libmv/third_party/msinttypes/stdint.h +++ b/intern/libmv/third_party/msinttypes/stdint.h @@ -1,35 +1,32 @@ -/* No need to format 3rd-party compatibility headers. */ -/* clang-format off */ - // ISO C9x compliant stdint.h for Microsoft Visual Studio -// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 -// +// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124 +// // Copyright (c) 2006-2008 Alexander Chemeris -// +// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are met: -// +// // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. -// +// // 2. Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in the // documentation and/or other materials provided with the distribution. -// +// // 3. The name of the author may be used to endorse or promote products // derived from this software without specific prior written permission. -// +// // THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO // EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; -// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR // OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF // ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// +// /////////////////////////////////////////////////////////////////////////////// #ifndef _MSC_VER // [ diff --git a/intern/locale/boost_locale_wrapper.cpp b/intern/locale/boost_locale_wrapper.cpp index 73433fe7c5e..444b51b5e04 100644 --- a/intern/locale/boost_locale_wrapper.cpp +++ b/intern/locale/boost_locale_wrapper.cpp @@ -26,8 +26,8 @@ static std::string messages_path; static std::string default_domain; static std::string locale_str; -/* Note: We cannot use short stuff like boost::locale::gettext, because those return - * std::basic_string objects, which c_ptr()-returned char* is no more valid +/* NOTE: We cannot use short stuff like `boost::locale::gettext`, because those return + * `std::basic_string` objects, which c_ptr()-returned char* is no more valid * once deleted (which happens as soons they are out of scope of this func). */ typedef boost::locale::message_format char_message_facet; static std::locale locale_global; @@ -63,7 +63,7 @@ static void bl_locale_global_cache() void bl_locale_init(const char *_messages_path, const char *_default_domain) { - // Avoid using ICU backend, we do not need its power and it's rather heavy! + /* Avoid using ICU backend, we do not need its power and it's rather heavy! */ boost::locale::localization_backend_manager lman = boost::locale::localization_backend_manager::global(); #if defined(_WIN32) @@ -81,7 +81,7 @@ void bl_locale_set(const char *locale) { boost::locale::generator gen; std::locale _locale; - // Specify location of dictionaries. + /* Specify location of dictionaries. */ gen.add_messages_path(messages_path); gen.add_messages_domain(default_domain); // gen.set_default_messages_domain(default_domain); @@ -99,12 +99,12 @@ void bl_locale_set(const char *locale) #endif } std::locale::global(_locale); - // Note: boost always uses "C" LC_NUMERIC by default! + /* NOTE: boost always uses "C" LC_NUMERIC by default! */ bl_locale_global_cache(); - // Generate the locale string - // (useful to know which locale we are actually using in case of "default" one). + /* Generate the locale string + * (useful to know which locale we are actually using in case of "default" one). */ #define LOCALE_INFO std::use_facet(_locale) locale_str = LOCALE_INFO.language(); @@ -117,6 +117,12 @@ void bl_locale_set(const char *locale) #undef LOCALE_INFO } + /* Extra catch on `std::runtime_error` is needed for macOS/Clang as it seems that exceptions + * like `boost::locale::conv::conversion_error` (which inherit from `std::runtime_error`) are + * not caught by their ancestor `std::exception`. See T88877#1177108 */ + catch (std::runtime_error const &e) { + std::cout << "bl_locale_set(" << locale << "): " << e.what() << " \n"; + } catch (std::exception const &e) { std::cout << "bl_locale_set(" << locale << "): " << e.what() << " \n"; } diff --git a/intern/locale/osx_user_locale.mm b/intern/locale/osx_user_locale.mm index e2f65d39df9..ce694b5fc1e 100644 --- a/intern/locale/osx_user_locale.mm +++ b/intern/locale/osx_user_locale.mm @@ -14,7 +14,17 @@ const char *osx_user_locale() CFLocaleRef myCFLocale = CFLocaleCopyCurrent(); NSLocale *myNSLocale = (NSLocale *)myCFLocale; [myNSLocale autorelease]; - NSString *nsIdentifier = [myNSLocale localeIdentifier]; + + // This produces gettext-invalid locale in recent macOS versions (11.4), + // like `ko-Kore_KR` instead of `ko_KR`. See T88877. + // NSString *nsIdentifier = [myNSLocale localeIdentifier]; + + const NSString *nsIdentifier = [myNSLocale languageCode]; + const NSString *const nsIdentifier_country = [myNSLocale countryCode]; + if ([nsIdentifier length] != 0 && [nsIdentifier_country length] != 0) { + nsIdentifier = [NSString stringWithFormat:@"%@_%@", nsIdentifier, nsIdentifier_country]; + } + user_locale = ::strdup([nsIdentifier UTF8String]); [pool drain]; diff --git a/intern/mikktspace/mikktspace.c b/intern/mikktspace/mikktspace.c index 96e8d433e30..b4596dc35ad 100644 --- a/intern/mikktspace/mikktspace.c +++ b/intern/mikktspace/mikktspace.c @@ -1112,7 +1112,7 @@ static tbool AssignRecur(const int piTriListIn[], static tbool CompareSubGroups(const SSubGroup *pg1, const SSubGroup *pg2); static void QuickSort(int *pSortBuffer, int iLeft, int iRight, unsigned int uSeed); -static STSpace EvalTspace(int face_indices[], +static STSpace EvalTspace(const int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], @@ -1292,7 +1292,7 @@ static tbool GenerateTSpaces(STSpace psTspace[], return TTRUE; } -static STSpace EvalTspace(int face_indices[], +static STSpace EvalTspace(const int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], diff --git a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc index b3fc021e1ee..4f4f332ff15 100644 --- a/intern/opensubdiv/internal/evaluator/evaluator_impl.cc +++ b/intern/opensubdiv/internal/evaluator/evaluator_impl.cc @@ -553,7 +553,7 @@ void convertPatchCoordsToArray(const OpenSubdiv_PatchCoord *patch_coords, } // namespace -// Note: Define as a class instead of typedcef to make it possible +// Note: Define as a class instead of typedef to make it possible // to have anonymous class in opensubdiv_evaluator_internal.h class CpuEvalOutput : public VolatileEvalOutput - -""" -Thumbnailer runs with python 2.7 and 3.x. -To run automatically with a file manager such as Nautilus, save this file -in a directory that is listed in PATH environment variable, and create -blender.thumbnailer file in ${HOME}/.local/share/thumbnailers/ directory -with the following contents: - -[Thumbnailer Entry] -TryExec=blender-thumbnailer.py -Exec=blender-thumbnailer.py %u %o -MimeType=application/x-blender; -""" - -import struct - - -def open_wrapper_get(): - """ wrap OS specific read functionality here, fallback to 'open()' - """ - - class GFileWrapper: - __slots__ = ("mode", "g_file") - - def __init__(self, url, mode='r'): - self.mode = mode # used in gzip module - self.g_file = Gio.File.parse_name(url).read(None) - - def read(self, size): - return self.g_file.read_bytes(size, None).get_data() - - def seek(self, offset, whence=0): - self.g_file.seek(offset, [1, 0, 2][whence], None) - return self.g_file.tell() - - def tell(self): - return self.g_file.tell() - - def close(self): - self.g_file.close(None) - - def open_local_url(url, mode='r'): - o = urlparse(url) - if o.scheme == '': - path = o.path - elif o.scheme == 'file': - path = unquote(o.path) - else: - raise(IOError('URL scheme "%s" needs gi.repository.Gio module' % o.scheme)) - return open(path, mode) - - try: - from gi.repository import Gio - return GFileWrapper - except ImportError: - try: - # Python 3 - from urllib.parse import urlparse, unquote - except ImportError: - # Python 2 - from urlparse import urlparse - from urllib import unquote - return open_local_url - - -def blend_extract_thumb(path): - import os - open_wrapper = open_wrapper_get() - - REND = b'REND' - TEST = b'TEST' - - blendfile = open_wrapper(path, 'rb') - - head = blendfile.read(12) - - if head[0:2] == b'\x1f\x8b': # gzip magic - import gzip - blendfile.close() - blendfile = gzip.GzipFile('', 'rb', 0, open_wrapper(path, 'rb')) - head = blendfile.read(12) - - if not head.startswith(b'BLENDER'): - blendfile.close() - return None, 0, 0 - - is_64_bit = (head[7] == b'-'[0]) - - # true for PPC, false for X86 - is_big_endian = (head[8] == b'V'[0]) - - # blender pre 2.5 had no thumbs - if head[9:11] <= b'24': - return None, 0, 0 - - sizeof_bhead = 24 if is_64_bit else 20 - int_endian = '>i' if is_big_endian else ' ") - else: - file_in = sys.argv[-2] - - buf, width, height = blend_extract_thumb(file_in) - - if buf: - file_out = sys.argv[-1] - - f = open(file_out, "wb") - f.write(write_png(buf, width, height)) - f.close() - - -if __name__ == '__main__': - main() diff --git a/release/datafiles/blender_icons.svg b/release/datafiles/blender_icons.svg index 70bd7dc8085..c87ad40ac0f 100644 --- a/release/datafiles/blender_icons.svg +++ b/release/datafiles/blender_icons.svg @@ -7719,7 +7719,7 @@ + transform="translate(-20)"> @@ -7741,7 +7741,7 @@ @@ -8843,14 +8843,14 @@ @@ -10425,13 +10425,13 @@ sodipodi:nodetypes="sssss" /> @@ -11015,7 +11015,7 @@ inkscape:connector-curvature="0" /> + transform="translate(-626.99987,42.000005)"> + transform="translate(-87.000002,-170)"> @@ -17302,11 +17302,11 @@ inkscape:connector-curvature="0" /> - + diff --git a/release/datafiles/blender_icons16/icon16_editmode_hlt.dat b/release/datafiles/blender_icons16/icon16_editmode_hlt.dat index 69d4e3bc4a9..a29abf4e4c8 100644 Binary files a/release/datafiles/blender_icons16/icon16_editmode_hlt.dat and b/release/datafiles/blender_icons16/icon16_editmode_hlt.dat differ diff --git a/release/datafiles/blender_icons16/icon16_fake_user_off.dat b/release/datafiles/blender_icons16/icon16_fake_user_off.dat index 1807949b579..a40ac7a148e 100644 Binary files a/release/datafiles/blender_icons16/icon16_fake_user_off.dat and b/release/datafiles/blender_icons16/icon16_fake_user_off.dat differ diff --git a/release/datafiles/blender_icons16/icon16_fake_user_on.dat b/release/datafiles/blender_icons16/icon16_fake_user_on.dat index d7f5a678d40..ea1176dacc3 100644 Binary files a/release/datafiles/blender_icons16/icon16_fake_user_on.dat and b/release/datafiles/blender_icons16/icon16_fake_user_on.dat differ diff --git a/release/datafiles/blender_icons16/icon16_force_boid.dat b/release/datafiles/blender_icons16/icon16_force_boid.dat index f719054d84a..71f89bd7c04 100644 Binary files a/release/datafiles/blender_icons16/icon16_force_boid.dat and b/release/datafiles/blender_icons16/icon16_force_boid.dat differ diff --git a/release/datafiles/blender_icons16/icon16_object_datamode.dat b/release/datafiles/blender_icons16/icon16_object_datamode.dat index 2d2545d372a..28293424510 100644 Binary files a/release/datafiles/blender_icons16/icon16_object_datamode.dat and b/release/datafiles/blender_icons16/icon16_object_datamode.dat differ diff --git a/release/datafiles/blender_icons16/icon16_particlemode.dat b/release/datafiles/blender_icons16/icon16_particlemode.dat index dccb70f4bbe..1a32156a7b8 100644 Binary files a/release/datafiles/blender_icons16/icon16_particlemode.dat and b/release/datafiles/blender_icons16/icon16_particlemode.dat differ diff --git a/release/datafiles/blender_icons16/icon16_pose_hlt.dat b/release/datafiles/blender_icons16/icon16_pose_hlt.dat index fd3fd905d21..3a59a1c9274 100644 Binary files a/release/datafiles/blender_icons16/icon16_pose_hlt.dat and b/release/datafiles/blender_icons16/icon16_pose_hlt.dat differ diff --git a/release/datafiles/blender_icons16/icon16_restrict_render_off.dat b/release/datafiles/blender_icons16/icon16_restrict_render_off.dat index db720670903..5eab5eaa538 100644 Binary files a/release/datafiles/blender_icons16/icon16_restrict_render_off.dat and b/release/datafiles/blender_icons16/icon16_restrict_render_off.dat differ diff --git a/release/datafiles/blender_icons16/icon16_restrict_render_on.dat b/release/datafiles/blender_icons16/icon16_restrict_render_on.dat index 3db7aa2bb86..29bebb2ee75 100644 Binary files a/release/datafiles/blender_icons16/icon16_restrict_render_on.dat and b/release/datafiles/blender_icons16/icon16_restrict_render_on.dat differ diff --git a/release/datafiles/blender_icons16/icon16_restrict_select_off.dat b/release/datafiles/blender_icons16/icon16_restrict_select_off.dat index cd3910bdce8..fa8bbcb3a04 100644 Binary files a/release/datafiles/blender_icons16/icon16_restrict_select_off.dat and b/release/datafiles/blender_icons16/icon16_restrict_select_off.dat differ diff --git a/release/datafiles/blender_icons16/icon16_restrict_select_on.dat b/release/datafiles/blender_icons16/icon16_restrict_select_on.dat index 9f52ef4af6b..8fd1bcc97ab 100644 Binary files a/release/datafiles/blender_icons16/icon16_restrict_select_on.dat and b/release/datafiles/blender_icons16/icon16_restrict_select_on.dat differ diff --git a/release/datafiles/blender_icons16/icon16_restrict_view_off.dat b/release/datafiles/blender_icons16/icon16_restrict_view_off.dat index 717d8d04f32..7b116a56356 100644 Binary files a/release/datafiles/blender_icons16/icon16_restrict_view_off.dat and b/release/datafiles/blender_icons16/icon16_restrict_view_off.dat differ diff --git a/release/datafiles/blender_icons16/icon16_restrict_view_on.dat b/release/datafiles/blender_icons16/icon16_restrict_view_on.dat index 54fda79d24e..60ed6d0ef42 100644 Binary files a/release/datafiles/blender_icons16/icon16_restrict_view_on.dat and b/release/datafiles/blender_icons16/icon16_restrict_view_on.dat differ diff --git a/release/datafiles/blender_icons16/icon16_sculptmode_hlt.dat b/release/datafiles/blender_icons16/icon16_sculptmode_hlt.dat index 30bb1538032..ab8fbd88fe1 100644 Binary files a/release/datafiles/blender_icons16/icon16_sculptmode_hlt.dat and b/release/datafiles/blender_icons16/icon16_sculptmode_hlt.dat differ diff --git a/release/datafiles/blender_icons16/icon16_tpaint_hlt.dat b/release/datafiles/blender_icons16/icon16_tpaint_hlt.dat index 3537aa62099..9a56ff6177f 100644 Binary files a/release/datafiles/blender_icons16/icon16_tpaint_hlt.dat and b/release/datafiles/blender_icons16/icon16_tpaint_hlt.dat differ diff --git a/release/datafiles/blender_icons16/icon16_uv_data.dat b/release/datafiles/blender_icons16/icon16_uv_data.dat index b843c28ee26..bea802a3bcb 100644 Binary files a/release/datafiles/blender_icons16/icon16_uv_data.dat and b/release/datafiles/blender_icons16/icon16_uv_data.dat differ diff --git a/release/datafiles/blender_icons16/icon16_vpaint_hlt.dat b/release/datafiles/blender_icons16/icon16_vpaint_hlt.dat index 7b4b42af987..0ad483ac862 100644 Binary files a/release/datafiles/blender_icons16/icon16_vpaint_hlt.dat and b/release/datafiles/blender_icons16/icon16_vpaint_hlt.dat differ diff --git a/release/datafiles/blender_icons16/icon16_wpaint_hlt.dat b/release/datafiles/blender_icons16/icon16_wpaint_hlt.dat index f5eaf8ff566..199a859bb5d 100644 Binary files a/release/datafiles/blender_icons16/icon16_wpaint_hlt.dat and b/release/datafiles/blender_icons16/icon16_wpaint_hlt.dat differ diff --git a/release/datafiles/blender_icons32/icon32_editmode_hlt.dat b/release/datafiles/blender_icons32/icon32_editmode_hlt.dat index 1a2f4ad096a..6bfcaafe875 100644 Binary files a/release/datafiles/blender_icons32/icon32_editmode_hlt.dat and b/release/datafiles/blender_icons32/icon32_editmode_hlt.dat differ diff --git a/release/datafiles/blender_icons32/icon32_fake_user_off.dat b/release/datafiles/blender_icons32/icon32_fake_user_off.dat index 53a555f8ac5..2e111fa7ba6 100644 Binary files a/release/datafiles/blender_icons32/icon32_fake_user_off.dat and b/release/datafiles/blender_icons32/icon32_fake_user_off.dat differ diff --git a/release/datafiles/blender_icons32/icon32_fake_user_on.dat b/release/datafiles/blender_icons32/icon32_fake_user_on.dat index 81ad3792699..ce276de53d6 100644 Binary files a/release/datafiles/blender_icons32/icon32_fake_user_on.dat and b/release/datafiles/blender_icons32/icon32_fake_user_on.dat differ diff --git a/release/datafiles/blender_icons32/icon32_force_boid.dat b/release/datafiles/blender_icons32/icon32_force_boid.dat index 9043989024b..7fc7cb5ee8c 100644 Binary files a/release/datafiles/blender_icons32/icon32_force_boid.dat and b/release/datafiles/blender_icons32/icon32_force_boid.dat differ diff --git a/release/datafiles/blender_icons32/icon32_object_datamode.dat b/release/datafiles/blender_icons32/icon32_object_datamode.dat index 32173788c7e..d4086196780 100644 Binary files a/release/datafiles/blender_icons32/icon32_object_datamode.dat and b/release/datafiles/blender_icons32/icon32_object_datamode.dat differ diff --git a/release/datafiles/blender_icons32/icon32_particlemode.dat b/release/datafiles/blender_icons32/icon32_particlemode.dat index 0863a19a5f4..343c568ec73 100644 Binary files a/release/datafiles/blender_icons32/icon32_particlemode.dat and b/release/datafiles/blender_icons32/icon32_particlemode.dat differ diff --git a/release/datafiles/blender_icons32/icon32_pose_hlt.dat b/release/datafiles/blender_icons32/icon32_pose_hlt.dat index d5f05a2e730..af5b10ad63a 100644 Binary files a/release/datafiles/blender_icons32/icon32_pose_hlt.dat and b/release/datafiles/blender_icons32/icon32_pose_hlt.dat differ diff --git a/release/datafiles/blender_icons32/icon32_restrict_render_off.dat b/release/datafiles/blender_icons32/icon32_restrict_render_off.dat index d1cc5a755c4..7f6cf73fea0 100644 Binary files a/release/datafiles/blender_icons32/icon32_restrict_render_off.dat and b/release/datafiles/blender_icons32/icon32_restrict_render_off.dat differ diff --git a/release/datafiles/blender_icons32/icon32_restrict_render_on.dat b/release/datafiles/blender_icons32/icon32_restrict_render_on.dat index 0d80bea7add..18184f7fe5f 100644 Binary files a/release/datafiles/blender_icons32/icon32_restrict_render_on.dat and b/release/datafiles/blender_icons32/icon32_restrict_render_on.dat differ diff --git a/release/datafiles/blender_icons32/icon32_restrict_select_off.dat b/release/datafiles/blender_icons32/icon32_restrict_select_off.dat index d3e43410075..b7163da17a0 100644 Binary files a/release/datafiles/blender_icons32/icon32_restrict_select_off.dat and b/release/datafiles/blender_icons32/icon32_restrict_select_off.dat differ diff --git a/release/datafiles/blender_icons32/icon32_restrict_select_on.dat b/release/datafiles/blender_icons32/icon32_restrict_select_on.dat index ff7ba6ceef0..a4149dc650d 100644 Binary files a/release/datafiles/blender_icons32/icon32_restrict_select_on.dat and b/release/datafiles/blender_icons32/icon32_restrict_select_on.dat differ diff --git a/release/datafiles/blender_icons32/icon32_restrict_view_off.dat b/release/datafiles/blender_icons32/icon32_restrict_view_off.dat index df6268b17c7..df30b2e873f 100644 Binary files a/release/datafiles/blender_icons32/icon32_restrict_view_off.dat and b/release/datafiles/blender_icons32/icon32_restrict_view_off.dat differ diff --git a/release/datafiles/blender_icons32/icon32_restrict_view_on.dat b/release/datafiles/blender_icons32/icon32_restrict_view_on.dat index d85859c3aed..02c08a102d2 100644 Binary files a/release/datafiles/blender_icons32/icon32_restrict_view_on.dat and b/release/datafiles/blender_icons32/icon32_restrict_view_on.dat differ diff --git a/release/datafiles/blender_icons32/icon32_sculptmode_hlt.dat b/release/datafiles/blender_icons32/icon32_sculptmode_hlt.dat index e5dfa527311..8590aa141c5 100644 Binary files a/release/datafiles/blender_icons32/icon32_sculptmode_hlt.dat and b/release/datafiles/blender_icons32/icon32_sculptmode_hlt.dat differ diff --git a/release/datafiles/blender_icons32/icon32_tpaint_hlt.dat b/release/datafiles/blender_icons32/icon32_tpaint_hlt.dat index cd21f05419c..b92dec6ef42 100644 Binary files a/release/datafiles/blender_icons32/icon32_tpaint_hlt.dat and b/release/datafiles/blender_icons32/icon32_tpaint_hlt.dat differ diff --git a/release/datafiles/blender_icons32/icon32_uv_data.dat b/release/datafiles/blender_icons32/icon32_uv_data.dat index 9ef062e760e..d7bf1314c0c 100644 Binary files a/release/datafiles/blender_icons32/icon32_uv_data.dat and b/release/datafiles/blender_icons32/icon32_uv_data.dat differ diff --git a/release/datafiles/blender_icons32/icon32_vpaint_hlt.dat b/release/datafiles/blender_icons32/icon32_vpaint_hlt.dat index cee921a6ba8..71d263c5bc9 100644 Binary files a/release/datafiles/blender_icons32/icon32_vpaint_hlt.dat and b/release/datafiles/blender_icons32/icon32_vpaint_hlt.dat differ diff --git a/release/datafiles/blender_icons32/icon32_wpaint_hlt.dat b/release/datafiles/blender_icons32/icon32_wpaint_hlt.dat index 0d12a956ef7..9f3bb8421af 100644 Binary files a/release/datafiles/blender_icons32/icon32_wpaint_hlt.dat and b/release/datafiles/blender_icons32/icon32_wpaint_hlt.dat differ diff --git a/release/datafiles/userdef/userdef_default.c b/release/datafiles/userdef/userdef_default.c index b82d78b927e..3cbc6b26b4a 100644 --- a/release/datafiles/userdef/userdef_default.c +++ b/release/datafiles/userdef/userdef_default.c @@ -35,9 +35,9 @@ const UserDef U_default = { .subversionfile = BLENDER_FILE_SUBVERSION, .flag = (USER_AUTOSAVE | USER_TOOLTIPS | USER_RELPATHS | USER_RELEASECONFIRM | USER_SCRIPT_AUTOEXEC_DISABLE | USER_NONEGFRAMES), - .dupflag = USER_DUP_MESH | USER_DUP_CURVE | USER_DUP_SURF | USER_DUP_FONT | USER_DUP_MBALL | - USER_DUP_LAMP | USER_DUP_ARM | USER_DUP_ACT | USER_DUP_LIGHTPROBE | - USER_DUP_GPENCIL, + .dupflag = USER_DUP_MESH | USER_DUP_CURVE | USER_DUP_SURF | USER_DUP_LATTICE | USER_DUP_FONT | + USER_DUP_MBALL | USER_DUP_LAMP | USER_DUP_ARM | USER_DUP_CAMERA | USER_DUP_SPEAKER | + USER_DUP_ACT | USER_DUP_LIGHTPROBE | USER_DUP_GPENCIL, .pref_flag = USER_PREF_FLAG_SAVE, .savetime = 2, .tempdir = "", diff --git a/release/datafiles/userdef/userdef_default_theme.c b/release/datafiles/userdef/userdef_default_theme.c index 85532d01d5c..f33ecea0eed 100644 --- a/release/datafiles/userdef/userdef_default_theme.c +++ b/release/datafiles/userdef/userdef_default_theme.c @@ -22,181 +22,163 @@ const bTheme U_theme_default = { .name = "Default", .tui = { .wcol_regular = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x585858ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0x3e3e3eff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0xffffff80), .text = RGBA(0xd9d9d9ff), .text_sel = RGBA(0xffffffff), - .shadedown = -5, .roundness = 0.2f, }, .wcol_tool = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x585858ff), - .inner_sel = RGBA(0x5680c2ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffffff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadedown = -5, .roundness = 0.2f, }, .wcol_toolbar_item = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x313131ff), - .inner_sel = RGBA(0x5680c2ff), - .item = RGBA(0xe6e6e6cc), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0xffffff80), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_text = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x1f1f1fff), - .inner_sel = RGBA(0x505050ff), - .item = RGBA(0x191919ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x1d1d1dff), + .inner_sel = RGBA(0x181818ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shaded = 1, - .shadetop = -3, .roundness = 0.2f, }, .wcol_radio = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0xffffffff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0x252525ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadetop = 5, - .shadedown = -5, .roundness = 0.2f, }, .wcol_option = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x666666ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0xffffffff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x71aaffff), + .item = RGBA(0x111111ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadedown = -15, .roundness = 0.2f, }, .wcol_toggle = { - .outline = RGBA(0x373737ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0x191919ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0x252525ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_num = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x505050ff), - .item = RGBA(0x191919ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x222222ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_numslider = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x595959ff), - .inner_sel = RGBA(0x505050ff), - .item = RGBA(0x5680c2e6), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x545454ff), + .inner_sel = RGBA(0x222222ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shaded = 1, - .shadetop = -4, .roundness = 0.2f, }, .wcol_tab = { - .outline = RGBA(0x202020ff), - .inner = RGBA(0x2b2b2bff), - .inner_sel = RGBA(0x424242ff), - .item = RGBA(0x2d2d2dff), + .outline = RGBA(0x1d1d1dff), + .inner = RGBA(0x1d1d1dff), + .inner_sel = RGBA(0x303030ff), + .item = RGBA(0x1d1d1dff), .text = RGBA(0x989898ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_menu = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x2c2c2cff), - .inner_sel = RGBA(0x696e76ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x282828ff), + .inner_sel = RGBA(0x4772b3b3), .item = RGBA(0xd9d9d9ff), - .text = RGBA(0xd9d9d9ff), + .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadetop = 10, - .shadedown = -10, .roundness = 0.2f, }, .wcol_pulldown = { - .outline = RGBA(0x4d4d4dff), - .inner = RGBA(0x2e2e2ecc), - .inner_sel = RGBA(0x5680c2e6), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x22222266), + .inner_sel = RGBA(0x4772b3b3), .item = RGBA(0x727272ff), .text = RGBA(0xd9d9d9ff), .text_sel = RGBA(0xffffffff), - .shadetop = 25, - .shadedown = -20, .roundness = 0.2f, }, .wcol_menu_back = { - .outline = RGBA(0x19191aff), - .inner = RGBA(0x1f1f1fef), - .inner_sel = RGBA(0x585858ff), - .item = RGBA(0x727272ff), - .text = RGBA(0xa5a5a5ff), + .outline = RGBA(0x242424ff), + .inner = RGBA(0x181818ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0xd9d9d9ff), + .text = RGBA(0x999999ff), .text_sel = RGBA(0xffffffff), - .shadetop = 25, - .shadedown = -20, .roundness = 0.2f, }, .wcol_menu_item = { - .inner_sel = RGBA(0x5680c2e6), + .outline = RGBA(0x3d3d3d00), + .inner = RGBA(0x18181800), + .inner_sel = RGBA(0x4772b3ff), .item = RGBA(0xffffff8f), - .text = RGBA(0xe6e6e6ff), + .text = RGBA(0xddddddff), .text_sel = RGBA(0xffffffff), - .shadetop = 38, .roundness = 0.2f, }, .wcol_tooltip = { .outline = RGBA(0x19191aff), - .inner = RGBA(0x19191aef), - .inner_sel = RGBA(0x19191aef), - .item = RGBA(0x19191aef), - .text = RGBA(0xe6e6e6ff), + .inner = RGBA(0x181818ff), + .inner_sel = RGBA(0x181818ff), + .item = RGBA(0x181818ff), + .text = RGBA(0xccccccff), .text_sel = RGBA(0xffffffff), - .shadetop = 25, - .shadedown = -20, .roundness = 0.2f, }, .wcol_box = { - .outline = RGBA(0x444444ff), - .inner = RGBA(0x00000033), - .inner_sel = RGBA(0x696e76ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x1d1d1d80), + .inner_sel = RGBA(0x545454ff), .item = RGBA(0x191919ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_scroll = { - .outline = RGBA(0x424242ff), - .inner = RGBA(0x67676700), - .inner_sel = RGBA(0xb3b3b3ff), - .item = RGBA(0x676767ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x22222200), + .inner_sel = RGBA(0xffffffff), + .item = RGBA(0x545454ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), - .shadetop = 5, - .shadedown = -5, .roundness = 0.5f, }, .wcol_progress = { - .outline = RGBA(0x585858ff), - .inner = RGBA(0x2c2c2cff), - .inner_sel = RGBA(0x5680c2ff), - .item = RGBA(0x5680c2ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x222222ff), + .inner_sel = RGBA(0x4772b3ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xe6e6e6ff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, @@ -204,21 +186,19 @@ const bTheme U_theme_default = { .wcol_list_item = { .outline = RGBA(0x2d2d2dff), .inner = RGBA(0x2d2d2d00), - .inner_sel = RGBA(0x696e76ff), + .inner_sel = RGBA(0x484a4fff), .item = RGBA(0xb3b3b3ff), - .text = RGBA(0xe6e6e6ff), + .text = RGBA(0xccccccff), .text_sel = RGBA(0xffffffff), .roundness = 0.2f, }, .wcol_pie_menu = { - .outline = RGBA(0x333333ff), - .inner = RGBA(0x212121ef), - .inner_sel = RGBA(0x5680c2e6), - .item = RGBA(0x585858ff), + .outline = RGBA(0x3d3d3dff), + .inner = RGBA(0x181818ff), + .inner_sel = RGBA(0x5680c2ff), + .item = RGBA(0x4772b3ff), .text = RGBA(0xd9d9d9ff), .text_sel = RGBA(0xffffffff), - .shadetop = 10, - .shadedown = -10, .roundness = 0.2f, }, .wcol_state = { @@ -235,15 +215,15 @@ const bTheme U_theme_default = { .blend = 0.5f, }, .widget_emboss = RGBA(0x00000026), - .menu_shadow_fac = 0.3f, - .menu_shadow_width = 4, - .editor_outline = RGBA(0x1f1f1fff), + .menu_shadow_fac = 0.4f, + .menu_shadow_width = 2, + .editor_outline = RGBA(0x161616ff), .transparent_checker_primary = RGBA(0x333333ff), .transparent_checker_secondary = RGBA(0x262626ff), .transparent_checker_size = 8, .icon_alpha = 1.0f, .icon_saturation = 0.5f, - .widget_text_cursor = RGBA(0x3399e6ff), + .widget_text_cursor = RGBA(0x71a8ffff), .xaxis = RGBA(0xff3352ff), .yaxis = RGBA(0x8bdc00ff), .zaxis = RGBA(0x2890ffff), @@ -253,64 +233,70 @@ const bTheme U_theme_default = { .gizmo_view_align = RGBA(0xffffffff), .gizmo_a = RGBA(0x4da84dff), .gizmo_b = RGBA(0xa33535ff), - .icon_scene = RGBA(0xe6e6e6ff), - .icon_collection = RGBA(0xf4f4f4ff), - .icon_object = RGBA(0xee9e5dff), + .icon_scene = RGBA(0xccccccff), + .icon_collection = RGBA(0xffffffff), + .icon_object = RGBA(0xe19658ff), .icon_object_data = RGBA(0x00d4a3ff), - .icon_modifier = RGBA(0x84b8ffff), - .icon_shading = RGBA(0xea7581ff), - .icon_folder = RGBA(0xe3c16eff), + .icon_modifier = RGBA(0x74a2ffff), + .icon_shading = RGBA(0xcc6670ff), + .icon_folder = RGBA(0xccad63ff), + .panel_roundness = 0.4f, }, .space_properties = { - .back = RGBA(0x42424200), - .title = RGBA(0xd4d4d4ff), + .back = RGBA(0x30303000), + .title = RGBA(0xe6e6e6ff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), - .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), + .button_title = RGBA(0xccccccff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .navigation_bar = RGBA(0x232323ff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x1d1d1dff), .panelcolors = { - .header = RGBA(0x424242ff), - .back = RGBA(0x383838ff), - .sub_back = RGBA(0x00000024), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, + .active = RGBA(0x4772b3ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .match = RGBA(0x5680c2ff), - .active = RGBA(0x5680c2ff), + .match = RGBA(0x4772b3ff), }, .space_view3d = { - .back = RGBA(0x393939ff), + .back = RGBA(0x3d3d3dff), + .back_grad = RGBA(0x30303000), + .background_type = 2, .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x42424200), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x35353500), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242e6), - .back = RGBA(0x333333f0), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .grid = RGBA(0x6666664d), + .grid = RGBA(0x54545480), .wire = RGBA(0x000000ff), .wire_edit = RGBA(0x000000ff), .select = RGBA(0xed5700ff), @@ -346,7 +332,7 @@ const bTheme U_theme_default = { .bone_pose = RGBA(0x50c8ff50), .bone_pose_active = RGBA(0x8cffff50), .bone_locked_weight = RGBA(0xff000080), - .cframe = RGBA(0x60c040ff), + .cframe = RGBA(0x4772b3ff), .time_keyframe = RGBA(0xddd700ff), .time_gp_keyframe = RGBA(0xb5e61dff), .freestyle_edge_mark = RGBA(0x7fff7fff), @@ -370,7 +356,7 @@ const bTheme U_theme_default = { .obcenter_dia = 6, .facedot_size = 3, .editmesh_active = RGBA(0xffffff33), - .clipping_border_3d = RGBA(0x313131ff), + .clipping_border_3d = RGBA(0x3f3f3fff), .bundle_solid = RGBA(0xc8c8c8ff), .camera_path = RGBA(0x000000ff), .gp_vertex_size = 3, @@ -385,66 +371,69 @@ const bTheme U_theme_default = { .title = RGBA(0xffffffff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x2e2e2eff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x4b4b4bff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .execution_buts = RGBA(0x444444ff), + .navigation_bar = RGBA(0x303030ff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x4b4b4bff), - .back = RGBA(0x404040ff), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .hilite = RGBA(0x4f76b3ff), + .hilite = RGBA(0x4772b3ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .row_alternate = RGBA(0xffffff07), + .row_alternate = RGBA(0xffffff04), }, .space_graph = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xffffffff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x2e2e2eff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x1d1d1dff), .list_title = RGBA(0xffffffff), .list_text = RGBA(0xb8b8b8ff), - .list_text_hi = RGBA(0xffaf29ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0x96969600), - .shade2 = RGBA(0x33333364), - .grid = RGBA(0x2a2a2aff), - .group = RGBA(0x16330fff), - .group_active = RGBA(0x368024ff), + .shade2 = RGBA(0x12121264), + .grid = RGBA(0x1a1a1aff), + .group = RGBA(0x1a332dff), + .group_active = RGBA(0x216d5bff), .vertex = RGBA(0x000000ff), .vertex_select = RGBA(0xff8500ff), .vertex_active = RGBA(0xffffffff), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x161616ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), .lastsel_point = RGBA(0xffffffff), .handle_auto = RGBA(0x909000ff), .handle_vect = RGBA(0x409030ff), @@ -454,8 +443,8 @@ const bTheme U_theme_default = { .handle_sel_vect = RGBA(0x40c030ff), .handle_sel_align = RGBA(0xf090a0ff), .handle_sel_auto_clamped = RGBA(0xf0af90ff), - .ds_channel = RGBA(0x0f2c4dff), - .ds_subchannel = RGBA(0x143e66ff), + .ds_channel = RGBA(0x194e80ff), + .ds_subchannel = RGBA(0x0f2c4dff), .vertex_size = 6, .outline_width = 1, .facedot_size = 4, @@ -464,25 +453,27 @@ const bTheme U_theme_default = { .anim_preview_range = RGBA(0xa14d0066), }, .space_info = { - .back = RGBA(0x28282800), + .back = RGBA(0x1d1d1d00), .title = RGBA(0xffffffff), .text = RGBA(0xc3c3c3ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x454545ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, @@ -497,56 +488,58 @@ const bTheme U_theme_default = { .info_info_text = RGBA(0xffffffff), .info_debug = RGBA(0x6b3293ff), .info_debug_text = RGBA(0xffffffff), - .info_property = RGBA(0x329364ff), + .info_property = RGBA(0x236666ff), .info_property_text = RGBA(0xffffffff), - .info_operator = RGBA(0x329364ff), + .info_operator = RGBA(0x235266ff), .info_operator_text = RGBA(0xffffffff), }, .space_action = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xa6a6a6ff), - .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .text_hi = RGBA(0x143e66ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x282828ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x22222200), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x1d1d1dff), .list_title = RGBA(0xffffffff), .list_text = RGBA(0xb8b8b8ff), - .list_text_hi = RGBA(0xffaf29ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0xc0c0c000), - .shade2 = RGBA(0x333333ff), + .shade2 = RGBA(0x1d1d1d99), .hilite = RGBA(0x60c04044), - .grid = RGBA(0x2a2a2aff), - .group = RGBA(0x16330f37), - .group_active = RGBA(0x36802455), - .strip = RGBA(0x1a151580), - .strip_select = RGBA(0xff8c00cc), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), - .ds_channel = RGBA(0x0f2c4d24), - .ds_subchannel = RGBA(0x143e6624), + .grid = RGBA(0x161616ff), + .group = RGBA(0x1a332d37), + .group_active = RGBA(0x216d5b67), + .strip = RGBA(0xffffff1f), + .strip_select = RGBA(0xff8c0099), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x1d1d1dff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), + .ds_channel = RGBA(0x194e8080), + .ds_subchannel = RGBA(0x0f2c4d80), .ds_ipoline = RGBA(0x94e575cc), - .keytype_keyframe = RGBA(0xe8e8e8ff), + .keytype_keyframe = RGBA(0xbfbfbfff), .keytype_extreme = RGBA(0xe8b3ccff), .keytype_breakdown = RGBA(0xb3dbe8ff), .keytype_jitter = RGBA(0x94e575ff), - .keytype_movehold = RGBA(0x5c5656ff), + .keytype_movehold = RGBA(0x808080ff), .keytype_keyframe_select = RGBA(0xffbe33ff), .keytype_extreme_select = RGBA(0xf28080ff), .keytype_breakdown_select = RGBA(0x54bfedff), @@ -559,43 +552,45 @@ const bTheme U_theme_default = { .facedot_size = 4, .keyframe_scale_fac = 1.0f, .handle_vertex_size = 4, - .anim_active = RGBA(0x4d250066), + .anim_active = RGBA(0x4d272766), .anim_preview_range = RGBA(0xa14d0066), }, .space_nla = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xffffffff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x1d1d1dff), .list_title = RGBA(0xffffffff), - .list_text = RGBA(0xb8b8b8ff), - .list_text_hi = RGBA(0xffaf29ff), + .list_text = RGBA(0xe5e5e5ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0x96969600), .grid = RGBA(0x2a2a2aff), - .strip = RGBA(0x0c0a0a80), + .strip = RGBA(0x0d0d0d80), .strip_select = RGBA(0xff8c00ff), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), - .ds_channel = RGBA(0x5a85b2ff), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x161616ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), + .ds_channel = RGBA(0x0f2c4dff), .ds_subchannel = RGBA(0x7d98b3ff), .keyborder = RGBA(0x000000ff), .keyborder_select = RGBA(0x000000ff), @@ -603,12 +598,12 @@ const bTheme U_theme_default = { .outline_width = 1, .facedot_size = 4, .handle_vertex_size = 4, - .anim_active = RGBA(0xcc701a66), - .anim_non_active = RGBA(0x9987614d), + .anim_active = RGBA(0x99541366), + .anim_non_active = RGBA(0x4d3b174d), .anim_preview_range = RGBA(0xa14d0066), .nla_tweaking = RGBA(0x4df31a4d), .nla_tweakdupli = RGBA(0xd90000ff), - .nla_track = RGBA(0x424242ff), + .nla_track = RGBA(0x303030ff), .nla_transition = RGBA(0x1c2630ff), .nla_transition_sel = RGBA(0x2e75dbff), .nla_meta = RGBA(0x332642ff), @@ -617,34 +612,36 @@ const bTheme U_theme_default = { .nla_sound_sel = RGBA(0x1f7a7aff), }, .space_sequencer = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .shade1 = RGBA(0xa0a0a000), - .grid = RGBA(0x212121ff), + .grid = RGBA(0x181818ff), .vertex_select = RGBA(0xff8500ff), .bone_pose = RGBA(0x50c8ff50), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x121212ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, @@ -652,8 +649,8 @@ const bTheme U_theme_default = { .movieclip = RGBA(0x8f4c4cff), .mask = RGBA(0x666666ff), .image = RGBA(0x8f744bff), - .scene = RGBA(0x828f50ff), - .audio = RGBA(0x4c8f8fff), + .scene = RGBA(0x808033ff), + .audio = RGBA(0x448080ff), .effect = RGBA(0x514a73ff), .transition = RGBA(0x8f4571ff), .meta = RGBA(0x5b4d91ff), @@ -663,39 +660,41 @@ const bTheme U_theme_default = { .selected_strip = RGBA(0xff8f0dff), .gp_vertex_size = 3, .gp_vertex_select = RGBA(0xff8500ff), + .row_alternate = RGBA(0xffffff05), .anim_preview_range = RGBA(0xa14d0066), .metadatatext = RGBA(0xffffffff), - .row_alternate = RGBA(0xffffff0d), }, .space_image = { - .back = RGBA(0x44444400), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x35353500), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .grid = RGBA(0x505050ff), + .grid = RGBA(0x303030ff), .wire_edit = RGBA(0xc0c0c0ff), .vertex_select = RGBA(0xff8500ff), .edge_select = RGBA(0xff8500ff), .face = RGBA(0xffffff0a), .face_select = RGBA(0xff85003c), .face_dot = RGBA(0xff8500ff), - .cframe = RGBA(0x60c040ff), + .cframe = RGBA(0x4772b3ff), .freestyle_face_mark = RGBA(0x7fff7f33), .handle_auto = RGBA(0x909000ff), .handle_align = RGBA(0x803060ff), @@ -722,181 +721,190 @@ const bTheme U_theme_default = { .metadatatext = RGBA(0xffffffff), }, .space_text = { - .back = RGBA(0x30303000), + .back = RGBA(0x23232300), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x42424200), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x42424200), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .shade2 = RGBA(0x5680c2e6), + .shade2 = RGBA(0x2d4366e6), .hilite = RGBA(0xff0000ff), - .grid = RGBA(0x202020ff), + .grid = RGBA(0x1d1d1dff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .syntaxl = RGBA(0xf6e162ff), + .syntaxl = RGBA(0xe6d573ff), .syntaxs = RGBA(0xff734dff), - .syntaxb = RGBA(0xff1961ff), - .syntaxn = RGBA(0x50dbffff), - .syntaxv = RGBA(0x95d600ff), + .syntaxb = RGBA(0xe62e67ff), + .syntaxn = RGBA(0x48c5e6ff), + .syntaxv = RGBA(0x689d06ff), .syntaxc = RGBA(0x939393ff), - .syntaxd = RGBA(0xad80ffff), + .syntaxd = RGBA(0x9c73e6ff), .syntaxr = RGBA(0xc4753bff), - .line_numbers = RGBA(0xd0d0d0ff), + .line_numbers = RGBA(0x777777ff), }, .space_outliner = { .back = RGBA(0x28282800), .title = RGBA(0xffffffff), .text = RGBA(0xc3c3c3ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x454545ff), + .header = RGBA(0x282828b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .active = RGBA(0x3b5689ff), + .active = RGBA(0x334d80ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, .match = RGBA(0x337f334c), - .selected_highlight = RGBA(0x223a5bff), + .selected_highlight = RGBA(0x1d314dff), .selected_object = RGBA(0xe96a00ff), .active_object = RGBA(0xffaf29ff), .edited_object = RGBA(0x00806266), - .row_alternate = RGBA(0xffffff07), + .row_alternate = RGBA(0xffffff04), }, .space_node = { - .back = RGBA(0x23232300), + .back = RGBA(0x1d1d1d00), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x1d1d1db3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x42424200), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), + .list = RGBA(0x303030ff), .list_title = RGBA(0xffffffff), .list_text = RGBA(0xccccccff), .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .shade2 = RGBA(0x7f707064), - .grid = RGBA(0x23232300), - .wire = RGBA(0x808080ff), + .shade2 = RGBA(0x7f7f7f64), + .grid = RGBA(0x28282800), + .wire = RGBA(0x1a1a1aff), .select = RGBA(0xed5700ff), .active = RGBA(0xffffffff), - .edge_select = RGBA(0xffffffff), - .console_output = RGBA(0x1a1a1aff), + .edge_select = RGBA(0xffffffb3), + .console_output = RGBA(0x000000ff), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, .noodle_curving = 4, - .grid_levels = 2, - .syntaxl = RGBA(0x565656ff), - .syntaxs = RGBA(0x975b5bff), - .syntaxb = RGBA(0xccb83dff), - .syntaxn = RGBA(0xe64555ff), - .syntaxv = RGBA(0x66c4ffff), - .syntaxc = RGBA(0x426628b9), - .syntaxd = RGBA(0x749797ff), - .syntaxr = RGBA(0x808080ff), - .nodeclass_output = RGBA(0xb33641ff), - .nodeclass_filter = RGBA(0x584d80ff), - .nodeclass_vector = RGBA(0x9b80ffff), - .nodeclass_texture = RGBA(0xe68745ff), - .nodeclass_shader = RGBA(0x63c763ff), + .grid_levels = 7, + .dash_alpha = 0.5f, + .syntaxl = RGBA(0x303030ff), + .syntaxs = RGBA(0x973c3cff), + .syntaxb = RGBA(0xcccc00ff), + .syntaxn = RGBA(0xff3371ff), + .syntaxv = RGBA(0x12adffff), + .syntaxc = RGBA(0x3b660aff), + .syntaxd = RGBA(0x4c9797ff), + .syntaxr = RGBA(0x8d8d8dff), + .nodeclass_output = RGBA(0x4d0017ff), + .nodeclass_filter = RGBA(0x551a80ff), + .nodeclass_vector = RGBA(0x4d4dffff), + .nodeclass_texture = RGBA(0xe66800ff), + .nodeclass_shader = RGBA(0x24b524ff), .nodeclass_script = RGBA(0x084d4dff), .nodeclass_pattern = RGBA(0x6c696fff), .nodeclass_layout = RGBA(0x6c696fff), - .nodeclass_geometry = RGBA(0x00d7a4ff), - .nodeclass_attribute = RGBA(0x3f5980ff), - .movie = RGBA(0x1a1a1acc), + .nodeclass_geometry = RGBA(0x00d6a3ff), + .nodeclass_attribute = RGBA(0x001566ff), + .movie = RGBA(0x0f0f0fcc), .gp_vertex_size = 3, .gp_vertex = RGBA(0x97979700), .gp_vertex_select = RGBA(0xff8500ff), }, .space_preferences = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x1d1d1dff), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .navigation_bar = RGBA(0x4b4b4bff), - .execution_buts = RGBA(0x4b4b4bff), + .navigation_bar = RGBA(0x303030ff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x42424200), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, .facedot_size = 4, }, .space_console = { - .back = RGBA(0x30303000), + .back = RGBA(0x1d1d1d00), .title = RGBA(0xeeeeeeff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .console_output = RGBA(0x71a8ffff), .console_input = RGBA(0xf2f2f2ff), @@ -909,46 +917,51 @@ const bTheme U_theme_default = { .facedot_size = 4, }, .space_clip = { - .back = RGBA(0x42424200), + .back = RGBA(0x30303000), .title = RGBA(0xeeeeeeff), .text = RGBA(0xa6a6a6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x424242ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), - .list = RGBA(0x282828ff), - .list_text = RGBA(0x000000ff), + .list = RGBA(0x303030ff), + .list_title = RGBA(0xffffff00), + .list_text = RGBA(0xb8b8b8ff), .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, - .grid = RGBA(0x424242ff), - .strip = RGBA(0x0c0a0a80), + .grid = RGBA(0x181818ff), + .strip = RGBA(0xffffff80), .strip_select = RGBA(0xff8c00ff), - .cframe = RGBA(0x5680c2ff), - .time_scrub_background = RGBA(0x292929e6), - .time_marker_line = RGBA(0x00000060), - .time_marker_line_selected = RGBA(0xffffff60), + .cframe = RGBA(0x4772b3ff), + .time_scrub_background = RGBA(0x181818ff), + .time_marker_line = RGBA(0xffffff4d), + .time_marker_line_selected = RGBA(0xffffffb3), .handle_auto = RGBA(0x909000ff), .handle_align = RGBA(0x803060ff), + .handle_auto_clamped = RGBA(0x99403000), .handle_sel_auto = RGBA(0xf0ff40ff), .handle_sel_align = RGBA(0xf090a0ff), + .handle_sel_auto_clamped = RGBA(0xf0af9000), .vertex_size = 3, .outline_width = 1, .facedot_size = 4, - .handle_vertex_select = RGBA(0xffff00ff), + .handle_vertex_select = RGBA(0xff8500ff), .handle_vertex_size = 5, - .marker = RGBA(0x7f7f00ff), + .marker = RGBA(0x808000ff), .act_marker = RGBA(0xffffffff), .sel_marker = RGBA(0xffff00ff), .dis_marker = RGBA(0x7f0000ff), @@ -961,25 +974,27 @@ const bTheme U_theme_default = { .metadatatext = RGBA(0xffffffff), }, .space_topbar = { - .back = RGBA(0x42424200), + .back = RGBA(0x18181800), .title = RGBA(0xffffffff), .text = RGBA(0xe6e6e6ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x232323ff), + .header = RGBA(0x181818b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, @@ -987,23 +1002,26 @@ const bTheme U_theme_default = { .gp_vertex_size = 3, }, .space_statusbar = { - .back = RGBA(0x2e2e2e00), + .back = RGBA(0x30303000), .title = RGBA(0xffffffff), .text = RGBA(0x838383ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x303030ff), - .header_text = RGBA(0xaaaaaaff), + .header = RGBA(0x181818b3), + .header_text = RGBA(0x888888ff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x353535ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), + .button_text = RGBA(0xcccccc00), .button_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, .vertex_size = 3, .outline_width = 1, @@ -1015,22 +1033,29 @@ const bTheme U_theme_default = { .title = RGBA(0xffffffff), .text = RGBA(0xc3c3c3ff), .text_hi = RGBA(0xffffffff), - .header = RGBA(0x454545ff), + .header = RGBA(0x303030b3), .header_text = RGBA(0xeeeeeeff), .header_text_hi = RGBA(0xffffffff), - .tab_active = RGBA(0x4b4b4bff), - .tab_inactive = RGBA(0x2b2b2bff), - .tab_back = RGBA(0x232323ff), - .tab_outline = RGBA(0x232323ff), - .button = RGBA(0x424242ff), + .tab_active = RGBA(0x303030ff), + .tab_inactive = RGBA(0x1d1d1dff), + .tab_back = RGBA(0x181818ff), + .tab_outline = RGBA(0x3d3d3dff), + .button = RGBA(0x30303000), .button_title = RGBA(0xffffffff), - .button_text = RGBA(0xe5e5e5ff), + .button_text = RGBA(0xccccccff), .button_text_hi = RGBA(0xffffffff), + .list = RGBA(0x303030ff), + .list_title = RGBA(0xc3c3c3ff), + .list_text = RGBA(0xc3c3c3ff), + .list_text_hi = RGBA(0xffffffff), + .navigation_bar = RGBA(0x1d1d1dff), + .execution_buts = RGBA(0x303030ff), .panelcolors = { - .header = RGBA(0x424242cc), - .back = RGBA(0x333333b3), - .sub_back = RGBA(0x0000003e), + .header = RGBA(0x3d3d3dff), + .back = RGBA(0x3d3d3dff), + .sub_back = RGBA(0x0000001f), }, + .hilite = RGBA(0x80808080), .active = RGBA(0x3b5689ff), .vertex_size = 3, .outline_width = 1, @@ -1040,12 +1065,7 @@ const bTheme U_theme_default = { .selected_object = RGBA(0xe96a00ff), .active_object = RGBA(0xffaf29ff), .edited_object = RGBA(0x00806266), - .row_alternate = RGBA(0xffffff07), - .list = RGBA(0x424242ff), - .list_title = RGBA(0xc3c3c3ff), - .list_text = RGBA(0xc3c3c3ff), - .list_text_hi = RGBA(0xffffff), - .hilite = RGBA(0x80808080), + .row_alternate = RGBA(0xffffff04), }, .tarm = { { @@ -1175,6 +1195,35 @@ const bTheme U_theme_default = { .color = RGBA(0x7a5441ff), }, }, + .strip_color = { + { + .color = RGBA(0xe2605bff), + }, + { + .color = RGBA(0xf1a355ff), + }, + { + .color = RGBA(0xf1dc55ff), + }, + { + .color = RGBA(0x7bcc7bff), + }, + { + .color = RGBA(0x5db6eaff), + }, + { + .color = RGBA(0x8d59daff), + }, + { + .color = RGBA(0xc673b8ff), + }, + { + .color = RGBA(0x7a5441ff), + }, + { + .color = RGBA(0x5f5f5fff), + }, + }, }; /* clang-format on */ diff --git a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py index 3355e9075a0..00edd7d523d 100644 --- a/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py +++ b/release/scripts/modules/bl_i18n_utils/bl_extract_messages.py @@ -252,27 +252,50 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): # Function definitions def walk_properties(cls): + # This handles properties whose name is the same as their identifier. + # Usually, it means that those are internal properties not exposed in the UI, however there are some cases + # where the UI label is actually defined and same as the identifier (color spaces e.g., `RGB` etc.). + # So we only exclude those properties in case they belong to an operator for now. + def prop_name_validate(cls, prop_name, prop_identifier): + if prop_name != prop_identifier: + return True + # Heuristic: A lot of operator's HIDDEN properties have no UI label/description. + # While this is not ideal (for API doc purposes, description should always be provided), + # for now skip those properties. + # NOTE: keep in sync with C code in ui_searchbox_region_draw_cb__operator(). + if issubclass(cls, bpy.types.OperatorProperties) and "_OT_" in cls.__name__: + return False + # Heuristic: If UI label is not capitalized, it is likely a private (undocumented) property, + # that can be skipped. + if prop_name and not prop_name[0].isupper(): + return False + return True + bl_rna = cls.bl_rna # Get our parents' properties, to not export them multiple times. bl_rna_base = bl_rna.base + bl_rna_base_props = set() if bl_rna_base: - bl_rna_base_props = set(bl_rna_base.properties.values()) - else: - bl_rna_base_props = set() + bl_rna_base_props |= set(bl_rna_base.properties.values()) + for cls_base in cls.__bases__: + bl_rna_base = getattr(cls_base, "bl_rna", None) + if not bl_rna_base: + continue + bl_rna_base_props |= set(bl_rna_base.properties.values()) props = sorted(bl_rna.properties, key=lambda p: p.identifier) for prop in props: # Only write this property if our parent hasn't got it. if prop in bl_rna_base_props: continue - if prop.identifier == "rna_type": + if prop.identifier in {"rna_type", "bl_icon", "icon"}: continue reports["rna_props"].append((cls, prop)) msgsrc = "bpy.types.{}.{}".format(bl_rna.identifier, prop.identifier) msgctxt = prop.translation_context or default_context - if prop.name and (prop.name != prop.identifier or msgctxt != default_context): + if prop.name and prop_name_validate(cls, prop.name, prop.identifier): process_msg(msgs, msgctxt, prop.name, msgsrc, reports, check_ctxt_rna, settings) if prop.description: process_msg(msgs, default_context, prop.description, msgsrc, reports, check_ctxt_rna_tip, settings) @@ -282,7 +305,7 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): for item in prop.enum_items: msgsrc = "bpy.types.{}.{}:'{}'".format(bl_rna.identifier, prop.identifier, item.identifier) done_items.add(item.identifier) - if item.name and item.name != item.identifier: + if item.name and prop_name_validate(cls, item.name, item.identifier): process_msg(msgs, msgctxt, item.name, msgsrc, reports, check_ctxt_rna, settings) if item.description: process_msg(msgs, default_context, item.description, msgsrc, reports, check_ctxt_rna_tip, @@ -292,7 +315,7 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): continue msgsrc = "bpy.types.{}.{}:'{}'".format(bl_rna.identifier, prop.identifier, item.identifier) done_items.add(item.identifier) - if item.name and item.name != item.identifier: + if item.name and prop_name_validate(cls, item.name, item.identifier): process_msg(msgs, msgctxt, item.name, msgsrc, reports, check_ctxt_rna, settings) if item.description: process_msg(msgs, default_context, item.description, msgsrc, reports, check_ctxt_rna_tip, @@ -378,7 +401,15 @@ def dump_rna_messages(msgs, reports, settings, verbose=False): if cls in blacklist_rna_class: return cls.__name__ cls_id = "" - bl_rna = cls.bl_rna + bl_rna = getattr(cls, "bl_rna", None) + # It seems that py-defined 'wrappers' RNA classes (like `MeshEdge` in `bpy_types.py`) need to be accessed + # once from `bpy.types` before they have a valid `bl_rna` member. + # Weirdly enough, this is only triggered on release builds, debug builds somehow do not have that issue. + if bl_rna is None: + if getattr(bpy.types, cls.__name__, None) is not None: + bl_rna = getattr(cls, "bl_rna", None) + if bl_rna is None: + raise TypeError("Unknown RNA class") while bl_rna: cls_id = bl_rna.identifier + "." + cls_id bl_rna = bl_rna.base @@ -735,7 +766,9 @@ def dump_src_messages(msgs, reports, settings): def clean_str(s): # The encode/decode to/from 'raw_unicode_escape' allows to transform the C-type unicode hexadecimal escapes # (like '\u2715' for the '×' symbol) back into a proper unicode character. - return "".join(m.group("clean") for m in _clean_str(s)).encode('raw_unicode_escape').decode('raw_unicode_escape') + return "".join( + m.group("clean") for m in _clean_str(s) + ).encode('raw_unicode_escape').decode('raw_unicode_escape') def dump_src_file(path, rel_path, msgs, reports, settings): def process_entry(_msgctxt, _msgid): @@ -862,7 +895,10 @@ def dump_messages(do_messages, do_checks, settings): dump_src_messages(msgs, reports, settings) # Get strings from addons' categories. - for uid, label, tip in bpy.types.WindowManager.addon_filter.keywords['items'](bpy.context.window_manager, bpy.context): + for uid, label, tip in bpy.types.WindowManager.addon_filter.keywords['items']( + bpy.context.window_manager, + bpy.context, + ): process_msg(msgs, settings.DEFAULT_CONTEXT, label, "Add-ons' categories", reports, None, settings) if tip: process_msg(msgs, settings.DEFAULT_CONTEXT, tip, "Add-ons' categories", reports, None, settings) diff --git a/release/scripts/modules/bl_i18n_utils/settings.py b/release/scripts/modules/bl_i18n_utils/settings.py index 51b326fb338..57aeef895b9 100644 --- a/release/scripts/modules/bl_i18n_utils/settings.py +++ b/release/scripts/modules/bl_i18n_utils/settings.py @@ -195,7 +195,7 @@ DOMAIN = "blender" # Our own "gettext" stuff. # File type (ext) to parse. -PYGETTEXT_ALLOWED_EXTS = {".c", ".cpp", ".cxx", ".hpp", ".hxx", ".h"} +PYGETTEXT_ALLOWED_EXTS = {".c", ".cc", ".cpp", ".cxx", ".hh", ".hpp", ".hxx", ".h"} # Max number of contexts into a BLT_I18N_MSGID_MULTI_CTXT macro... PYGETTEXT_MAX_MULTI_CTXT = 16 @@ -400,6 +400,8 @@ WARN_MSGID_NOT_CAPITALIZED_ALLOWED = { "verts only", "view", "virtual parents", + "and NVIDIA driver version 470 or newer", + "and AMD driver version ??? or newer", } WARN_MSGID_NOT_CAPITALIZED_ALLOWED |= set(lng[2] for lng in LANGUAGES) diff --git a/release/scripts/modules/bl_i18n_utils/utils.py b/release/scripts/modules/bl_i18n_utils/utils.py index 95184853f73..13fb87d386a 100644 --- a/release/scripts/modules/bl_i18n_utils/utils.py +++ b/release/scripts/modules/bl_i18n_utils/utils.py @@ -219,7 +219,7 @@ def enable_addons(addons=None, support=None, disable=False, check_only=False): try: import bpy except ModuleNotFoundError: - print("Could not import bpy, enable_addons must be run from whithin Blender.") + print("Could not import bpy, enable_addons must be run from within Blender.") return if addons is None: diff --git a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py index 0293d7143ec..62186655326 100644 --- a/release/scripts/modules/bl_i18n_utils/utils_spell_check.py +++ b/release/scripts/modules/bl_i18n_utils/utils_spell_check.py @@ -66,6 +66,7 @@ class SpellChecker: "ons", # add-ons "pong", # ping pong "resumable", + "runtimes", "scalable", "shadeless", "shouldn", # shouldn't @@ -76,6 +77,12 @@ class SpellChecker: "vertices", "wasn", # wasn't + # Brands etc. + "htc", + "huawei", + "vive", + "xbox", + # Merged words "antialiasing", "antialias", "arcsine", "arccosine", "arctangent", @@ -131,6 +138,7 @@ class SpellChecker: "forcefield", "forcefields", "fulldome", "fulldomes", "fullscreen", + "gamepad", "gridline", "gridlines", "hardlight", "hemi", @@ -176,6 +184,7 @@ class SpellChecker: "precalculate", "precomputing", "prefetch", + "prefilter", "prefiltering", "preload", "premultiply", "premultiplied", "prepass", @@ -225,6 +234,7 @@ class SpellChecker: "subpath", "subsize", "substep", "substeps", + "substring", "targetless", "textbox", "textboxes", "tilemode", @@ -679,11 +689,13 @@ class SpellChecker: "ctrl", "cw", "ccw", "dev", + "dls", "djv", "dpi", "dvar", "dx", "eo", + "ewa", "fh", "fk", "fov", @@ -723,6 +735,7 @@ class SpellChecker: "rhs", "rv", "sdl", + "sdls", "sl", "smpte", "ssao", @@ -731,6 +744,7 @@ class SpellChecker: "tma", "ui", "unix", + "uuid", "vbo", "vbos", "vr", "wxyz", @@ -756,6 +770,7 @@ class SpellChecker: "svbvh", # Files types/formats + "aac", "avi", "attrac", "autocad", @@ -778,6 +793,7 @@ class SpellChecker: "ico", "jpg", "jpeg", "jpegs", "json", + "lzw", "matroska", "mdd", "mkv", @@ -787,6 +803,7 @@ class SpellChecker: "openjpeg", "osl", "oso", + "pcm", "piz", "png", "pngs", "po", diff --git a/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py b/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py index 0784a91d174..c3bc0f429c1 100644 --- a/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py +++ b/release/scripts/modules/bl_keymap_utils/keymap_hierarchy.py @@ -168,9 +168,13 @@ _km_hierarchy = [ ('Node Editor', 'NODE_EDITOR', 'WINDOW', [ ('Node Generic', 'NODE_EDITOR', 'WINDOW', []), ]), - ('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', [ - ('SequencerCommon', 'SEQUENCE_EDITOR', 'WINDOW', []), - ('SequencerPreview', 'SEQUENCE_EDITOR', 'WINDOW', []), + ('SequencerCommon', 'SEQUENCE_EDITOR', 'WINDOW', [ + ('Sequencer', 'SEQUENCE_EDITOR', 'WINDOW', [ + _km_expand_from_toolsystem('SEQUENCE_EDITOR', 'SEQUENCER'), + ]), + ('SequencerPreview', 'SEQUENCE_EDITOR', 'WINDOW', [ + _km_expand_from_toolsystem('SEQUENCE_EDITOR', 'PREVIEW'), + ]), ]), ('File Browser', 'FILE_BROWSER', 'WINDOW', [ diff --git a/release/scripts/modules/bl_previews_utils/bl_previews_render.py b/release/scripts/modules/bl_previews_utils/bl_previews_render.py index 17577a9a9f2..0b3b3a4e406 100644 --- a/release/scripts/modules/bl_previews_utils/bl_previews_render.py +++ b/release/scripts/modules/bl_previews_utils/bl_previews_render.py @@ -140,8 +140,6 @@ def do_previews(do_objects, do_collections, do_scenes, do_data_intern): scene.render.use_overwrite = True scene.render.use_stamp = False scene.render.threads_mode = 'AUTO' - scene.render.tile_x = RENDER_PREVIEW_SIZE // 4 - scene.render.tile_y = RENDER_PREVIEW_SIZE // 4 image = bpy.data.images.new("TEMP_render_image", RENDER_PREVIEW_SIZE, RENDER_PREVIEW_SIZE, alpha=True) image.source = 'FILE' @@ -485,19 +483,44 @@ def main(): argv = sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else [] parser = argparse.ArgumentParser( - description="Use Blender to generate previews for currently open Blender file's items.") - parser.add_argument('--clear', default=False, action="store_true", - help="Clear previews instead of generating them.") - parser.add_argument('--no_backups', default=False, action="store_true", - help="Do not generate a backup .blend1 file when saving processed ones.") - parser.add_argument('--no_scenes', default=True, action="store_false", - help="Do not generate/clear previews for scene IDs.") - parser.add_argument('--no_collections', default=True, action="store_false", - help="Do not generate/clear previews for collection IDs.") - parser.add_argument('--no_objects', default=True, action="store_false", - help="Do not generate/clear previews for object IDs.") - parser.add_argument('--no_data_intern', default=True, action="store_false", - help="Do not generate/clear previews for mat/tex/image/etc. IDs (those handled by core Blender code).") + description="Use Blender to generate previews for currently open Blender file's items.", + ) + parser.add_argument( + '--clear', + default=False, + action="store_true", + help="Clear previews instead of generating them.", + ) + parser.add_argument( + '--no_backups', + default=False, + action="store_true", + help="Do not generate a backup .blend1 file when saving processed ones.", + ) + parser.add_argument( + '--no_scenes', + default=True, + action="store_false", + help="Do not generate/clear previews for scene IDs.", + ) + parser.add_argument( + '--no_collections', + default=True, + action="store_false", + help="Do not generate/clear previews for collection IDs.", + ) + parser.add_argument( + '--no_objects', + default=True, + action="store_false", + help="Do not generate/clear previews for object IDs.", + ) + parser.add_argument( + '--no_data_intern', + default=True, + action="store_false", + help="Do not generate/clear previews for mat/tex/image/etc. IDs (those handled by core Blender code).", + ) args = parser.parse_args(argv) orig_save_version = bpy.context.preferences.filepaths.save_version diff --git a/release/scripts/modules/bpy/path.py b/release/scripts/modules/bpy/path.py index 30f93d051e5..37bdb939c07 100644 --- a/release/scripts/modules/bpy/path.py +++ b/release/scripts/modules/bpy/path.py @@ -67,6 +67,8 @@ def abspath(path, *, start=None, library=None): :arg library: The library this path is from. This is only included for convenience, when the library is not None its path replaces *start*. :type library: :class:`bpy.types.Library` + :return: The absolute path. + :rtype: string """ if isinstance(path, bytes): if path.startswith(b"//"): @@ -101,6 +103,8 @@ def relpath(path, *, start=None): :arg start: Relative to this path, when not set the current filename is used. :type start: string or bytes + :return: The relative path. + :rtype: string """ if isinstance(path, bytes): if not path.startswith(b"//"): @@ -123,6 +127,8 @@ def is_subdir(path, directory): :arg path: An absolute path. :type path: string or bytes + :return: Whether or not the path is a subdirectory. + :rtype: boolean """ from os.path import normpath, normcase, sep path = normpath(normcase(path)) @@ -141,6 +147,12 @@ def clean_name(name, *, replace="_"): such as writing to a file. All characters besides A-Z/a-z, 0-9 are replaced with "_" or the *replace* argument if defined. + :arg name: The path name. + :type name: string or bytes + :arg replace: The replacement for non-valid characters. + :type replace: string + :return: The cleaned name. + :rtype: string """ if replace != "_": @@ -207,8 +219,14 @@ def display_name(name, *, has_ext=True, title_case=True): Creates a display string from name to be used menus and the user interface. Intended for use with filenames and module names. - :arg has_ext: Remove file extension from name - :arg title_case: Convert lowercase names to title case + :arg name: The name to be used for displaying the user interface. + :type name: string + :arg has_ext: Remove file extension from name. + :type has_ext: boolean + :arg title_case: Convert lowercase names to title case. + :type title_case: boolean + :return: The display string. + :rtype: string """ if has_ext: @@ -233,6 +251,10 @@ def display_name_to_filepath(name): """ Performs the reverse of display_name using literal versions of characters which aren't supported in a filepath. + :arg name: The display name to convert. + :type name: string + :return: The file path. + :rtype: string """ for disp_value, file_value in _display_name_literals.items(): name = name.replace(disp_value, file_value) @@ -243,6 +265,10 @@ def display_name_from_filepath(name): """ Returns the path stripped of directory and extension, ensured to be utf8 compatible. + :arg name: The file path to convert. + :type name: string + :return: The display name. + :rtype: string """ name = _os.path.splitext(basename(name))[0] @@ -254,6 +280,10 @@ def resolve_ncase(path): """ Resolve a case insensitive path on a case sensitive system, returning a string with the path if found else return the original path. + :arg path: The path name to resolve. + :type path: string + :return: The resolved path. + :rtype: string """ def _ncase_path_found(path): @@ -315,11 +345,15 @@ def ensure_ext(filepath, ext, *, case_sensitive=False): """ Return the path with the extension added if it is not already set. + :arg filepath: The file path. + :type filepath: string :arg ext: The extension to check for, can be a compound extension. Should start with a dot, such as '.blend' or '.tar.gz'. :type ext: string :arg case_sensitive: Check for matching case when comparing extensions. - :type case_sensitive: bool + :type case_sensitive: boolean + :return: The file path with the given extension. + :rtype: string """ if case_sensitive: @@ -341,7 +375,7 @@ def module_names(path, *, recursive=False): :arg recursive: Also return submodule names for packages. :type recursive: bool :return: a list of string pairs (module_name, module_file). - :rtype: list + :rtype: list of strings """ from os.path import join, isfile @@ -374,6 +408,8 @@ def basename(path): Equivalent to ``os.path.basename``, but skips a "//" prefix. Use for Windows compatibility. + :return: The base name of the given path. + :rtype: string """ return _os.path.basename(path[2:] if path[:2] in {"//", b"//"} else path) @@ -381,6 +417,10 @@ def basename(path): def native_pathsep(path): """ Replace the path separator with the systems native ``os.sep``. + :arg path: The path to replace. + :type path: string + :return: The path with system native separators. + :rtype: string """ if type(path) is str: if _os.sep == "/": @@ -407,9 +447,9 @@ def reduce_dirs(dirs): (Useful for recursive path searching). :arg dirs: Sequence of directory paths. - :type dirs: sequence + :type dirs: sequence of strings :return: A unique list of paths. - :rtype: list + :rtype: list of strings """ dirs = list({_os.path.normpath(_os.path.abspath(d)) for d in dirs}) dirs.sort(key=lambda d: len(d)) diff --git a/release/scripts/modules/bpy/utils/__init__.py b/release/scripts/modules/bpy/utils/__init__.py index afa04a18ef6..3f0248970c6 100644 --- a/release/scripts/modules/bpy/utils/__init__.py +++ b/release/scripts/modules/bpy/utils/__init__.py @@ -858,6 +858,7 @@ def register_tool(tool_cls, *, after=None, separator=False, group=False): "description": getattr(tool_cls, "bl_description", tool_cls.__doc__), "icon": getattr(tool_cls, "bl_icon", None), "cursor": getattr(tool_cls, "bl_cursor", None), + "options": getattr(tool_cls, "bl_options", None), "widget": getattr(tool_cls, "bl_widget", None), "widget_properties": getattr(tool_cls, "bl_widget_properties", None), "keymap": getattr(tool_cls, "bl_keymap", None), diff --git a/release/scripts/modules/bpy_extras/asset_utils.py b/release/scripts/modules/bpy_extras/asset_utils.py index 2cd5dddefbc..c7ebdc1d5e1 100644 --- a/release/scripts/modules/bpy_extras/asset_utils.py +++ b/release/scripts/modules/bpy_extras/asset_utils.py @@ -52,19 +52,12 @@ class AssetBrowserPanel: bl_space_type = 'FILE_BROWSER' @classmethod - def poll(cls, context): + def asset_browser_panel_poll(cls, context): return SpaceAssetInfo.is_asset_browser_poll(context) - -class AssetBrowserSpecificCategoryPanel(AssetBrowserPanel): - asset_categories = set() # Set of strings like 'ANIMATIONS', see `asset_category_items` in rna_space.c - @classmethod def poll(cls, context): - return ( - SpaceAssetInfo.is_asset_browser_poll(context) - and context.space_data.params.asset_category in cls.asset_categories - ) + return cls.asset_browser_panel_poll(context) class AssetMetaDataPanel: diff --git a/release/scripts/modules/bpy_extras/io_utils.py b/release/scripts/modules/bpy_extras/io_utils.py index 9e3c5bb64e0..a3b39853b3a 100644 --- a/release/scripts/modules/bpy_extras/io_utils.py +++ b/release/scripts/modules/bpy_extras/io_utils.py @@ -25,7 +25,6 @@ __all__ = ( "axis_conversion", "axis_conversion_ensure", "create_derived_objects", - "free_derived_objects", "unpack_list", "unpack_face_list", "path_reference", @@ -348,21 +347,40 @@ def axis_conversion_ensure(operator, forward_attr, up_attr): return False -# return a tuple (free, object list), free is True if memory should be freed -# later with free_derived_objects() -def create_derived_objects(scene, ob): - if ob.parent and ob.parent.instance_type in {'VERTS', 'FACES'}: - return False, None +def create_derived_objects(depsgraph, objects): + """ + This function takes a sequence of objects, returning their instances. - if ob.instance_type != 'NONE': - ob.dupli_list_create(scene) - return True, [(dob.object, dob.matrix) for dob in ob.dupli_list] - else: - return False, [(ob, ob.matrix_world)] + :arg depsgraph: The evaluated depsgraph. + :type depsgraph: :class:`bpy.types.Depsgraph` + :arg objects: A sequencer of objects. + :type objects: sequence of :class:`bpy.types.Object` + :return: A dictionary where each key is an object from `objects`, + values are lists of (:class:`bpy.types.Object`, :class:`mathutils.Matrix`) tuples representing instances. + :rtype: dict + """ + result = {} + has_instancer = False + for ob in objects: + ob_parent = ob.parent + if ob_parent and ob_parent.instance_type in {'VERTS', 'FACES'}: + continue + result[ob] = [] if ob.is_instancer else [(ob, ob.matrix_world.copy())] - -def free_derived_objects(ob): - ob.dupli_list_clear() + if result: + for dup in depsgraph.object_instances: + dup_parent = dup.parent + if dup_parent is None: + continue + dup_parent_original = dup_parent.original + if not dup_parent_original.is_instancer: + # The instance has already been added (on assignment). + continue + instance_list = result.get(dup_parent_original) + if instance_list is None: + continue + instance_list.append((dup.instance_object.original, dup.matrix_world.copy())) + return result def unpack_list(list_of_tuples): diff --git a/release/scripts/modules/bpy_types.py b/release/scripts/modules/bpy_types.py index 8a1615ad99f..26efb6e3307 100644 --- a/release/scripts/modules/bpy_types.py +++ b/release/scripts/modules/bpy_types.py @@ -378,10 +378,9 @@ class PoseBone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup): def children(self): obj = self.id_data pbones = obj.pose.bones - self_bone = self.bone - return tuple(pbones[bone.name] for bone in obj.data.bones - if bone.parent == self_bone) + # Use Bone.children, which is a native RNA property. + return tuple(pbones[bone.name] for bone in self.bone.children) class Bone(StructRNA, _GenericBone, metaclass=StructMetaPropGroup): diff --git a/release/scripts/modules/nodeitems_utils.py b/release/scripts/modules/nodeitems_utils.py index a5c18cee463..f6b6aaf03cf 100644 --- a/release/scripts/modules/nodeitems_utils.py +++ b/release/scripts/modules/nodeitems_utils.py @@ -108,7 +108,7 @@ def register_node_categories(identifier, cat_list): # works as draw function for menus def draw_node_item(self, context): layout = self.layout - col = layout.column() + col = layout.column(align=True) for item in self.category.items(context): item.draw(item, col, context) diff --git a/release/scripts/modules/rna_manual_reference.py b/release/scripts/modules/rna_manual_reference.py index 24b5bb6b685..797eb2627b3 100644 --- a/release/scripts/modules/rna_manual_reference.py +++ b/release/scripts/modules/rna_manual_reference.py @@ -40,12 +40,14 @@ if LANG is not None: url_manual_mapping = ( ("bpy.types.movietrackingsettings.refine_intrinsics_tangential_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-tangential-distortion"), + ("bpy.types.spacesequesequencertimelineoverlaynceeditor.show_strip_offset*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequesequencertimelineoverlaynceeditor-show-strip-offset"), ("bpy.types.movietrackingsettings.refine_intrinsics_radial_distortion*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-radial-distortion"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-wavecrest"), ("bpy.types.fluiddomainsettings.sndparticle_potential_min_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-min-wavecrest"), ("bpy.types.movietrackingsettings.refine_intrinsics_principal_point*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-principal-point"), + ("bpy.types.cyclesobjectsettings.shadow_terminator_geometry_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-geometry-offset"), ("bpy.types.cyclesrenderlayersettings.denoising_optix_input_passes*", "render/layers/denoising.html#bpy-types-cyclesrenderlayersettings-denoising-optix-input-passes"), ("bpy.types.sequencertoolsettings.use_snap_current_frame_to_strips*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-use-snap-current-frame-to-strips"), ("bpy.types.fluiddomainsettings.sndparticle_potential_max_energy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-max-energy"), @@ -53,13 +55,18 @@ url_manual_mapping = ( ("bpy.types.movietrackingsettings.refine_intrinsics_focal_length*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-refine-intrinsics-focal-length"), ("bpy.types.rigidbodyconstraint.rigidbodyconstraint.use_breaking*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-rigidbodyconstraint-use-breaking"), ("bpy.types.cyclesrendersettings.preview_denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-input-passes"), + ("bpy.types.cyclesrendersettings.preview_denoising_start_sample*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-start-sample"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_trappedair*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-trappedair"), ("bpy.types.fluiddomainsettings.sndparticle_sampling_wavecrest*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-sampling-wavecrest"), ("bpy.types.rigidbodyconstraint.use_override_solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-override-solver-iterations"), ("bpy.types.toolsettings.use_transform_correct_face_attributes*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-face-attributes"), + ("bpy.types.cyclesrendersettings.preview_adaptive_min_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-min-samples"), ("bpy.types.rendersettings.use_sequencer_override_scene_strip*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-use-sequencer-override-scene-strip"), ("bpy.types.toolsettings.use_transform_correct_keep_connected*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-transform-correct-keep-connected"), + ("bpy.types.cyclesrendersettings.preview_denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoising-prefilter"), ("bpy.types.fluiddomainsettings.sndparticle_potential_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-potential-radius"), + ("bpy.types.cyclesrendersettings.film_transparent_roughness*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-roughness"), + ("bpy.types.cyclesrendersettings.preview_adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-adaptive-threshold"), ("bpy.types.fluiddomainsettings.openvdb_cache_compress_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-openvdb-cache-compress-type"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_buoyancy*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-buoyancy"), ("bpy.types.fluiddomainsettings.sndparticle_combined_export*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-combined-export"), @@ -71,66 +78,98 @@ url_manual_mapping = ( ("bpy.types.toolsettings.annotation_stroke_placement_view3d*", "interface/annotate_tool.html#bpy-types-toolsettings-annotation-stroke-placement-view3d"), ("bpy.types.fluiddomainsettings.use_collision_border_front*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-front"), ("bpy.types.fluiddomainsettings.use_collision_border_right*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-right"), + ("bpy.types.sequencertimelineoverlay.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-waveform-display-type"), + ("bpy.types.cyclesmaterialsettings.use_transparent_shadow*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-use-transparent-shadow"), + ("bpy.types.cyclesobjectsettings.shadow_terminator_offset*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-shadow-terminator-offset"), ("bpy.types.cyclesobjectsettings.use_adaptive_subdivision*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-use-adaptive-subdivision"), + ("bpy.types.cyclesrendersettings.debug_use_spatial_splits*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-spatial-splits"), + ("bpy.types.cyclesrendersettings.light_sampling_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-light-sampling-threshold"), + ("bpy.types.cyclesrendersettings.rolling_shutter_duration*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-duration"), + ("bpy.types.cyclesrendersettings.volume_preview_step_rate*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-preview-step-rate"), ("bpy.types.fluiddomainsettings.sndparticle_update_radius*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-update-radius"), ("bpy.types.fluiddomainsettings.use_collision_border_back*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-back"), ("bpy.types.fluiddomainsettings.use_collision_border_left*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-left"), ("bpy.types.rendersettings_simplify_gpencil_view_modifier*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-modifier"), ("bpy.types.brushgpencilsettings.use_settings_stabilizer*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-stabilizer"), ("bpy.types.colormanagedsequencercolorspacesettings.name*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings-name"), + ("bpy.types.cyclesrendersettings.max_transparent_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-max-transparent-bounces"), + ("bpy.types.cyclesrendersettings.min_transparent_bounces*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-min-transparent-bounces"), ("bpy.types.fluiddomainsettings.use_collision_border_top*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-collision-border-top"), ("bpy.types.gpencilsculptsettings.use_multiframe_falloff*", "grease_pencil/multiframe.html#bpy-types-gpencilsculptsettings-use-multiframe-falloff"), ("bpy.types.movietrackingsettings.use_keyframe_selection*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-keyframe-selection"), ("bpy.types.rendersettings.simplify_gpencil_antialiasing*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-antialiasing"), + ("bpy.types.sequencertimelineoverlay.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-duration"), ("bpy.types.spaceoutliner.use_filter_lib_override_system*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override-system"), ("bpy.types.toolsettings.use_transform_pivot_point_align*", "scene_layout/object/tools/tool_settings.html#bpy-types-toolsettings-use-transform-pivot-point-align"), ("bpy.types.brush.show_multiplane_scrape_planes_preview*", "sculpt_paint/sculpting/tools/multiplane_scrape.html#bpy-types-brush-show-multiplane-scrape-planes-preview"), + ("bpy.types.cyclesmaterialsettings.volume_interpolation*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-interpolation"), + ("bpy.types.cyclesrendersettings.debug_optix_curves_api*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-optix-curves-api"), + ("bpy.types.cyclesrendersettings.denoising_input_passes*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-input-passes"), + ("bpy.types.cyclesrendersettings.film_transparent_glass*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-transparent-glass"), ("bpy.types.cyclesrendersettings.offscreen_dicing_scale*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-offscreen-dicing-scale"), ("bpy.types.fluiddomainsettings.sndparticle_bubble_drag*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-bubble-drag"), - ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), - ("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"), + ("bpy.types.linestylegeometrymodifier_backbonestretcher*", "render/freestyle/view_layer/line_style/modifiers/geometry/backbone_stretcher.html#bpy-types-linestylegeometrymodifier-backbonestretcher"), + ("bpy.types.linestylegeometrymodifier_sinusdisplacement*", "render/freestyle/view_layer/line_style/modifiers/geometry/sinus_displacement.html#bpy-types-linestylegeometrymodifier-sinusdisplacement"), ("bpy.types.sequencertoolsettings.snap_to_current_frame*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-current-frame"), ("bpy.types.colormanageddisplaysettings.display_device*", "render/color_management.html#bpy-types-colormanageddisplaysettings-display-device"), ("bpy.types.colormanagedviewsettings.use_curve_mapping*", "render/color_management.html#bpy-types-colormanagedviewsettings-use-curve-mapping"), + ("bpy.types.cyclesrendersettings.sample_clamp_indirect*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-sample-clamp-indirect"), + ("bpy.types.cyclesrendersettings.use_adaptive_sampling*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-adaptive-sampling"), + ("bpy.types.cyclesrendersettings.use_preview_denoising*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-preview-denoising"), ("bpy.types.fluiddomainsettings.color_ramp_field_scale*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field-scale"), ("bpy.types.fluiddomainsettings.use_adaptive_timesteps*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-adaptive-timesteps"), ("bpy.types.fluiddomainsettings.use_dissolve_smoke_log*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-use-dissolve-smoke-log"), + ("bpy.types.freestylelineset.select_suggestive_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-suggestive-contour"), ("bpy.types.gpencillayer.annotation_onion_before_color*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-before-color"), ("bpy.types.gpencillayer.annotation_onion_before_range*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-before-range"), ("bpy.types.gpencillayer.use_annotation_onion_skinning*", "interface/annotate_tool.html#bpy-types-gpencillayer-use-annotation-onion-skinning"), ("bpy.types.greasepencil.use_adaptive_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-adaptive-curve-resolution"), - ("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"), + ("bpy.types.linestylegeometrymodifier_polygonalization*", "render/freestyle/view_layer/line_style/modifiers/geometry/polygonization.html#bpy-types-linestylegeometrymodifier-polygonalization"), + ("bpy.types.sequencertimelineoverlay.show_strip_source*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-source"), ("bpy.types.toolsettings.use_gpencil_automerge_strokes*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-automerge-strokes"), ("bpy.types.toolsettings.use_proportional_edit_objects*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-edit-objects"), ("bpy.ops.view3d.edit_mesh_extrude_move_shrink_fatten*", "modeling/meshes/editing/face/extrude_faces_normal.html#bpy-ops-view3d-edit-mesh-extrude-move-shrink-fatten"), + ("bpy.types.cyclesmaterialsettings.homogeneous_volume*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-homogeneous-volume"), + ("bpy.types.cyclesrendersettings.adaptive_min_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-min-samples"), + ("bpy.types.cyclesrendersettings.debug_bvh_time_steps*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-bvh-time-steps"), ("bpy.types.cyclesrendersettings.distance_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-distance-cull-margin"), + ("bpy.types.cyclesrendersettings.motion_blur_position*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-motion-blur-position"), + ("bpy.types.cyclesrendersettings.rolling_shutter_type*", "render/cycles/render_settings/motion_blur.html#bpy-types-cyclesrendersettings-rolling-shutter-type"), + ("bpy.types.cyclesrendersettings.transmission_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-transmission-bounces"), + ("bpy.types.cyclesworldsettings.sample_map_resolution*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sample-map-resolution"), ("bpy.types.fluiddomainsettings.display_interpolation*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-display-interpolation"), ("bpy.types.fluiddomainsettings.gridlines_cell_filter*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-cell-filter"), ("bpy.types.fluiddomainsettings.gridlines_color_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-color-field"), ("bpy.types.fluiddomainsettings.gridlines_lower_bound*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-lower-bound"), ("bpy.types.fluiddomainsettings.gridlines_range_color*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-range-color"), ("bpy.types.fluiddomainsettings.gridlines_upper_bound*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-gridlines-upper-bound"), + ("bpy.types.freestylelineset.select_material_boundary*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-material-boundary"), ("bpy.types.gpencillayer.annotation_onion_after_color*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-color"), ("bpy.types.gpencillayer.annotation_onion_after_range*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-onion-after-range"), ("bpy.types.materialgpencilstyle.use_fill_texture_mix*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-texture-mix"), ("bpy.types.rendersettings_simplify_gpencil_shader_fx*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-shader-fx"), ("bpy.types.rendersettings_simplify_gpencil_view_fill*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-view-fill"), ("bpy.types.sequencertoolsettings.snap_to_hold_offset*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-to-hold-offset"), - ("bpy.types.spacesequenceeditor.waveform_display_type*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-waveform-display-type"), ("bpy.types.toolsettings.use_mesh_automerge_and_split*", "modeling/meshes/tools/tool_settings.html#bpy-types-toolsettings-use-mesh-automerge-and-split"), ("bpy.types.brush.cloth_constraint_softbody_strength*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-constraint-softbody-strength"), ("bpy.types.brush.elastic_deform_volume_preservation*", "sculpt_paint/sculpting/tools/elastic_deform.html#bpy-types-brush-elastic-deform-volume-preservation"), ("bpy.types.brushgpencilsettings.fill_simplify_level*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-simplify-level"), ("bpy.types.brushgpencilsettings.use_jitter_pressure*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-jitter-pressure"), ("bpy.types.brushgpencilsettings.use_settings_random*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-use-settings-random"), + ("bpy.types.cyclesrendersettings.denoising_prefilter*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoising-prefilter"), + ("bpy.types.cyclesrendersettings.preview_dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-preview-dicing-rate"), + ("bpy.types.cyclesrendersettings.sample_clamp_direct*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-sample-clamp-direct"), + ("bpy.types.cyclesworldsettings.volume_interpolation*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-interpolation"), ("bpy.types.fluiddomainsettings.mesh_particle_radius*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-mesh-particle-radius"), ("bpy.types.fluiddomainsettings.sndparticle_boundary*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-boundary"), ("bpy.types.fluiddomainsettings.sndparticle_life_max*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-max"), ("bpy.types.fluiddomainsettings.sndparticle_life_min*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-sndparticle-life-min"), ("bpy.types.fluiddomainsettings.sys_particle_maximum*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-sys-particle-maximum"), ("bpy.types.fluiddomainsettings.use_bubble_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-bubble-particles"), - ("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"), + ("bpy.types.freestylelineset.select_external_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-external-contour"), + ("bpy.types.linestylegeometrymodifier_simplification*", "render/freestyle/view_layer/line_style/modifiers/geometry/simplification.html#bpy-types-linestylegeometrymodifier-simplification"), ("bpy.types.materialgpencilstyle.use_overlap_strokes*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-overlap-strokes"), + ("bpy.types.sequencertimelineoverlay.show_strip_name*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-strip-name"), ("bpy.types.spacespreadsheet.geometry_component_type*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-geometry-component-type"), ("bpy.types.toolsettings.use_gpencil_weight_data_add*", "grease_pencil/modes/draw/introduction.html#bpy-types-toolsettings-use-gpencil-weight-data-add"), ("bpy.types.view3doverlay.texture_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-texture-paint-mode-opacity"), @@ -141,7 +180,10 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.use_default_eraser*", "grease_pencil/modes/draw/tools/erase.html#bpy-types-brushgpencilsettings-use-default-eraser"), ("bpy.types.colormanagedsequencercolorspacesettings*", "render/color_management.html#bpy-types-colormanagedsequencercolorspacesettings"), ("bpy.types.colormanagedviewsettings.view_transform*", "render/color_management.html#bpy-types-colormanagedviewsettings-view-transform"), + ("bpy.types.cyclesmaterialsettings.volume_step_rate*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-step-rate"), + ("bpy.types.cyclesrendersettings.adaptive_threshold*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-adaptive-threshold"), ("bpy.types.cyclesrendersettings.camera_cull_margin*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-camera-cull-margin"), + ("bpy.types.cyclesrendersettings.debug_use_hair_bvh*", "render/cycles/render_settings/performance.html#bpy-types-cyclesrendersettings-debug-use-hair-bvh"), ("bpy.types.fluiddomainsettings.export_manta_script*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-export-manta-script"), ("bpy.types.fluiddomainsettings.fractions_threshold*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-threshold"), ("bpy.types.fluiddomainsettings.particle_band_width*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-particle-band-width"), @@ -150,28 +192,40 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.use_resumable_cache*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-use-resumable-cache"), ("bpy.types.fluiddomainsettings.use_spray_particles*", "physics/fluid/type/domain/liquid/particles.html#bpy-types-fluiddomainsettings-use-spray-particles"), ("bpy.types.fluiddomainsettings.vector_display_type*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-display-type"), + ("bpy.types.freestylelineset.select_by_image_border*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-image-border"), + ("bpy.types.freestylesettings.kr_derivative_epsilon*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-kr-derivative-epsilon"), ("bpy.types.geometrynodecurveprimitivebeziersegment*", "modeling/geometry_nodes/curve_primitives/bezier_segment.html#bpy-types-geometrynodecurveprimitivebeziersegment"), - ("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"), - ("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"), + ("bpy.types.linestylegeometrymodifier_perlinnoise1d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_1d.html#bpy-types-linestylegeometrymodifier-perlinnoise1d"), + ("bpy.types.linestylegeometrymodifier_perlinnoise2d*", "render/freestyle/view_layer/line_style/modifiers/geometry/perlin_noise_2d.html#bpy-types-linestylegeometrymodifier-perlinnoise2d"), ("bpy.types.materialgpencilstyle.use_stroke_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-stroke-holdout"), ("bpy.types.movietrackingsettings.use_tripod_solver*", "movie_clip/tracking/clip/toolbar/solve.html#bpy-types-movietrackingsettings-use-tripod-solver"), + ("bpy.types.rendersettings.simplify_child_particles*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-child-particles"), ("bpy.types.rendersettings.use_high_quality_normals*", "render/eevee/render_settings/performance.html#bpy-types-rendersettings-use-high-quality-normals"), + ("bpy.types.sequencerpreviewoverlay.show_annotation*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-annotation"), + ("bpy.types.sequencerpreviewoverlay.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-safe-areas"), ("bpy.types.sequencertoolsettings.snap_ignore_muted*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-muted"), ("bpy.types.sequencertoolsettings.snap_ignore_sound*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-snap-ignore-sound"), ("bpy.types.spaceoutliner.use_filter_case_sensitive*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-case-sensitive"), ("bpy.types.spaceoutliner.use_filter_object_content*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-content"), - ("bpy.types.spacesequenceeditor.show_strip_duration*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-duration"), ("bpy.types.toolsettings.use_proportional_connected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-connected"), ("bpy.types.toolsettings.use_proportional_projected*", "editors/3dview/controls/proportional_editing.html#bpy-types-toolsettings-use-proportional-projected"), ("bpy.types.view3doverlay.vertex_paint_mode_opacity*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-vertex-paint-mode-opacity"), - ("bpy.types.viewlayer.use_pass_cryptomatte_accurate*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-accurate"), ("bpy.types.viewlayer.use_pass_cryptomatte_material*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-material"), ("bpy.ops.gpencil.vertex_color_brightness_contrast*", "grease_pencil/modes/vertex_paint/editing.html#bpy-ops-gpencil-vertex-color-brightness-contrast"), ("bpy.ops.view3d.edit_mesh_extrude_individual_move*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-individual-move"), ("bpy.ops.view3d.edit_mesh_extrude_manifold_normal*", "modeling/meshes/tools/extrude_manifold.html#bpy-ops-view3d-edit-mesh-extrude-manifold-normal"), + ("bpy.types.cyclescurverendersettings.subdivisions*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-subdivisions"), + ("bpy.types.cyclesmaterialsettings.sample_as_light*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-sample-as-light"), + ("bpy.types.cyclesmaterialsettings.volume_sampling*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-volume-sampling"), + ("bpy.types.cyclesobjectsettings.use_deform_motion*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-deform-motion"), ("bpy.types.cyclesobjectsettings.use_distance_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-distance-cull"), ("bpy.types.cyclesrendersettings.ao_bounces_render*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces-render"), + ("bpy.types.cyclesrendersettings.min_light_bounces*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-min-light-bounces"), + ("bpy.types.cyclesrendersettings.pixel_filter_type*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-pixel-filter-type"), + ("bpy.types.cyclesrendersettings.use_animated_seed*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-animated-seed"), ("bpy.types.cyclesrendersettings.use_distance_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-distance-cull"), + ("bpy.types.cyclesrendersettings.use_layer_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-layer-samples"), + ("bpy.types.cyclesworldsettings.homogeneous_volume*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-homogeneous-volume"), ("bpy.types.fluiddomainsettings.cache_frame_offset*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-offset"), ("bpy.types.fluiddomainsettings.delete_in_obstacle*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-delete-in-obstacle"), ("bpy.types.fluiddomainsettings.fractions_distance*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-fractions-distance"), @@ -185,9 +239,9 @@ url_manual_mapping = ( ("bpy.types.fluideffectorsettings.surface_distance*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-surface-distance"), ("bpy.types.fluidflowsettings.density_vertex_group*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density-vertex-group"), ("bpy.types.fluidflowsettings.use_initial_velocity*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-initial-velocity"), - ("bpy.types.linestylegeometrymodifier_guidinglines*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/guiding_lines.html#bpy-types-linestylegeometrymodifier-guidinglines"), - ("bpy.types.linestylegeometrymodifier_spatialnoise*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/spatial_noise.html#bpy-types-linestylegeometrymodifier-spatialnoise"), - ("bpy.types.linestylethicknessmodifier_calligraphy*", "render/freestyle/parameter_editor/line_style/modifiers/thickness/calligraphy.html#bpy-types-linestylethicknessmodifier-calligraphy"), + ("bpy.types.linestylegeometrymodifier_guidinglines*", "render/freestyle/view_layer/line_style/modifiers/geometry/guiding_lines.html#bpy-types-linestylegeometrymodifier-guidinglines"), + ("bpy.types.linestylegeometrymodifier_spatialnoise*", "render/freestyle/view_layer/line_style/modifiers/geometry/spatial_noise.html#bpy-types-linestylegeometrymodifier-spatialnoise"), + ("bpy.types.linestylethicknessmodifier_calligraphy*", "render/freestyle/view_layer/line_style/modifiers/thickness/calligraphy.html#bpy-types-linestylethicknessmodifier-calligraphy"), ("bpy.types.rendersettings_simplify_gpencil_onplay*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil-onplay"), ("bpy.types.rigidbodyconstraint.breaking_threshold*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-breaking-threshold"), ("bpy.types.spaceclipeditor.use_manual_calibration*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-manual-calibration"), @@ -203,6 +257,10 @@ url_manual_mapping = ( ("bpy.types.view3doverlay.sculpt_mode_mask_opacity*", "sculpt_paint/sculpting/editing/mask.html#bpy-types-view3doverlay-sculpt-mode-mask-opacity"), ("bpy.ops.outliner.collection_indirect_only_clear*", "render/layers/introduction.html#bpy-ops-outliner-collection-indirect-only-clear"), ("bpy.types.cyclesrendersettings.max_subdivisions*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-max-subdivisions"), + ("bpy.types.cyclesrendersettings.preview_denoiser*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-denoiser"), + ("bpy.types.cyclesrendersettings.sampling_pattern*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-sampling-pattern"), + ("bpy.types.cyclesrendersettings.volume_max_steps*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-max-steps"), + ("bpy.types.cyclesrendersettings.volume_step_rate*", "render/cycles/render_settings/volumes.html#bpy-types-cyclesrendersettings-volume-step-rate"), ("bpy.types.editbone.bbone_handle_use_scale_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-start"), ("bpy.types.fluiddomainsettings.cache_data_format*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-data-format"), ("bpy.types.fluiddomainsettings.cache_frame_start*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-frame-start"), @@ -217,20 +275,23 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.vector_show_mac_y*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-show-mac-y"), ("bpy.types.fluiddomainsettings.vector_show_mac_z*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-vector-show-mac-z"), ("bpy.types.fluideffectorsettings.velocity_factor*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-velocity-factor"), - ("bpy.types.linestyle*modifier_distancefromcamera*", "render/freestyle/parameter_editor/line_style/modifiers/color/distance_from_camera.html#bpy-types-linestyle-modifier-distancefromcamera"), - ("bpy.types.linestyle*modifier_distancefromobject*", "render/freestyle/parameter_editor/line_style/modifiers/color/distance_from_object.html#bpy-types-linestyle-modifier-distancefromobject"), - ("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/2d_transform.html#bpy-types-linestylegeometrymodifier-2dtransform"), - ("bpy.types.linestylegeometrymodifier_beziercurve*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/bezier_curve.html#bpy-types-linestylegeometrymodifier-beziercurve"), + ("bpy.types.freestylelineset.select_by_collection*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-collection"), + ("bpy.types.freestylelineset.select_by_edge_types*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-edge-types"), + ("bpy.types.freestylelineset.select_by_face_marks*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-by-face-marks"), + ("bpy.types.linestyle*modifier_distancefromcamera*", "render/freestyle/view_layer/line_style/modifiers/color/distance_from_camera.html#bpy-types-linestyle-modifier-distancefromcamera"), + ("bpy.types.linestyle*modifier_distancefromobject*", "render/freestyle/view_layer/line_style/modifiers/color/distance_from_object.html#bpy-types-linestyle-modifier-distancefromobject"), + ("bpy.types.linestylegeometrymodifier_2dtransform*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_transform.html#bpy-types-linestylegeometrymodifier-2dtransform"), + ("bpy.types.linestylegeometrymodifier_beziercurve*", "render/freestyle/view_layer/line_style/modifiers/geometry/bezier_curve.html#bpy-types-linestylegeometrymodifier-beziercurve"), ("bpy.types.materialgpencilstyle.use_fill_holdout*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-use-fill-holdout"), ("bpy.types.particlesettings.use_parent_particles*", "physics/particles/emitter/render.html#bpy-types-particlesettings-use-parent-particles"), ("bpy.types.rigidbodyconstraint.solver_iterations*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-solver-iterations"), + ("bpy.types.sequencerpreviewoverlay.show_metadata*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay-show-metadata"), + ("bpy.types.sequencertimelineoverlay.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay-show-fcurves"), ("bpy.types.spaceclipeditor.use_grayscale_preview*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-grayscale-preview"), ("bpy.types.spaceoutliner.use_filter_lib_override*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-lib-override"), ("bpy.types.spaceoutliner.use_filter_object_empty*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-empty"), ("bpy.types.spaceoutliner.use_filter_object_light*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object-light"), ("bpy.types.spacesequenceeditor.proxy_render_size*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-proxy-render-size"), - ("bpy.types.spacesequenceeditor.show_strip_offset*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-offset"), - ("bpy.types.spacesequenceeditor.show_strip_source*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-source"), ("bpy.types.spacespreadsheetrowfilter.column_name*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-column-name"), ("bpy.types.toolsettings.gpencil_stroke_placement*", "grease_pencil/modes/draw/stroke_placement.html#bpy-types-toolsettings-gpencil-stroke-placement"), ("bpy.types.toolsettings.use_keyframe_cycle_aware*", "editors/timeline.html#bpy-types-toolsettings-use-keyframe-cycle-aware"), @@ -239,13 +300,23 @@ url_manual_mapping = ( ("bpy.ops.armature.rigify_apply_selection_colors*", "addons/rigging/rigify/metarigs.html#bpy-ops-armature-rigify-apply-selection-colors"), ("bpy.types.brushgpencilsettings.fill_layer_mode*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-layer-mode"), ("bpy.types.cyclesobjectsettings.use_camera_cull*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-camera-cull"), + ("bpy.types.cyclesobjectsettings.use_motion_blur*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-use-motion-blur"), + ("bpy.types.cyclesrendersettings.diffuse_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-diffuse-bounces"), + ("bpy.types.cyclesrendersettings.preview_samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-preview-samples"), ("bpy.types.cyclesrendersettings.use_camera_cull*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-use-camera-cull"), + ("bpy.types.cyclesworldsettings.volume_step_size*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-step-size"), ("bpy.types.editbone.bbone_handle_use_ease_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-ease-start"), ("bpy.types.fluiddomainsettings.color_ramp_field*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-color-ramp-field"), ("bpy.types.fluiddomainsettings.guide_vel_factor*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-vel-factor"), ("bpy.types.fluideffectorsettings.use_plane_init*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-use-plane-init"), + ("bpy.types.freestylelineset.collection_negation*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-collection-negation"), + ("bpy.types.freestylelineset.face_mark_condition*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-face-mark-condition"), + ("bpy.types.freestylelineset.select_ridge_valley*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-ridge-valley"), + ("bpy.types.freestylelinestyle.material_boundary*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-material-boundary"), + ("bpy.types.freestylelinestyle.use_split_pattern*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-pattern"), + ("bpy.types.freestylesettings.use_view_map_cache*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-view-map-cache"), ("bpy.types.greasepencil.curve_edit_corner_angle*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-corner-angle"), - ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), + ("bpy.types.linestylegeometrymodifier_tipremover*", "render/freestyle/view_layer/line_style/modifiers/geometry/tip_remover.html#bpy-types-linestylegeometrymodifier-tipremover"), ("bpy.types.movieclipuser.use_render_undistorted*", "editors/clip/display/clip_display.html#bpy-types-movieclipuser-use-render-undistorted"), ("bpy.types.movietrackingcamera.distortion_model*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-distortion-model"), ("bpy.types.rendersettings.resolution_percentage*", "render/output/properties/format.html#bpy-types-rendersettings-resolution-percentage"), @@ -257,6 +328,7 @@ url_manual_mapping = ( ("bpy.types.toolsettings.use_snap_align_rotation*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap-align-rotation"), ("bpy.types.viewlayer.use_pass_cryptomatte_asset*", "render/layers/passes.html#bpy-types-viewlayer-use-pass-cryptomatte-asset"), ("bpy.ops.outliner.collection_indirect_only_set*", "render/layers/introduction.html#bpy-ops-outliner-collection-indirect-only-set"), + ("bpy.ops.scene.freestyle_geometry_modifier_add*", "render/freestyle/view_layer/line_style/geometry.html#bpy-ops-scene-freestyle-geometry-modifier-add"), ("bpy.ops.sequencer.deinterlace_selected_movies*", "video_editing/sequencer/editing.html#bpy-ops-sequencer-deinterlace-selected-movies"), ("bpy.types.bakesettings.use_selected_to_active*", "render/cycles/baking.html#bpy-types-bakesettings-use-selected-to-active"), ("bpy.types.brush.surface_smooth_current_vertex*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-current-vertex"), @@ -266,6 +338,11 @@ url_manual_mapping = ( ("bpy.types.brushgpencilsettings.fill_threshold*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-threshold"), ("bpy.types.clothsettings.vertex_group_pressure*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-pressure"), ("bpy.types.cyclesmaterialsettings.displacement*", "render/cycles/material_settings.html#bpy-types-cyclesmaterialsettings-displacement"), + ("bpy.types.cyclesrendersettings.debug_bvh_type*", "render/cycles/render_settings/debug.html#bpy-types-cyclesrendersettings-debug-bvh-type"), + ("bpy.types.cyclesrendersettings.glossy_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-glossy-bounces"), + ("bpy.types.cyclesrendersettings.volume_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-volume-bounces"), + ("bpy.types.cyclesworldsettings.sampling_method*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-sampling-method"), + ("bpy.types.cyclesworldsettings.volume_sampling*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-volume-sampling"), ("bpy.types.editbone.bbone_handle_use_scale_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-scale-end"), ("bpy.types.fluiddomainsettings.adapt_threshold*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-threshold"), ("bpy.types.fluiddomainsettings.cache_directory*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-directory"), @@ -280,7 +357,10 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.viscosity_value*", "physics/fluid/type/domain/liquid/viscosity.html#bpy-types-fluiddomainsettings-viscosity-value"), ("bpy.types.fluideffectorsettings.effector_type*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-effector-type"), ("bpy.types.fluidflowsettings.use_particle_size*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-particle-size"), - ("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/blueprint.html#bpy-types-linestylegeometrymodifier-blueprint"), + ("bpy.types.freestylelineset.face_mark_negation*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-face-mark-negation"), + ("bpy.types.freestylelinestyle.integration_type*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-integration-type"), + ("bpy.types.freestylelinestyle.use_split_length*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-split-length"), + ("bpy.types.linestylegeometrymodifier_blueprint*", "render/freestyle/view_layer/line_style/modifiers/geometry/blueprint.html#bpy-types-linestylegeometrymodifier-blueprint"), ("bpy.types.materialgpencilstyle.alignment_mode*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-alignment-mode"), ("bpy.types.particlesettings.use_modifier_stack*", "physics/particles/emitter/emission.html#bpy-types-particlesettings-use-modifier-stack"), ("bpy.types.rendersettings.sequencer_gl_preview*", "video_editing/preview/sidebar.html#bpy-types-rendersettings-sequencer-gl-preview"), @@ -288,10 +368,7 @@ url_manual_mapping = ( ("bpy.types.spacegrapheditor.show_extrapolation*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-show-extrapolation"), ("bpy.types.spaceoutliner.use_filter_collection*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-collection"), ("bpy.types.spacesequenceeditor.display_channel*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-display-channel"), - ("bpy.types.spacesequenceeditor.show_annotation*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-annotation"), ("bpy.types.spacesequenceeditor.show_region_hud*", "video_editing/sequencer/navigating.html#bpy-types-spacesequenceeditor-show-region-hud"), - ("bpy.types.spacesequenceeditor.show_safe_areas*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-safe-areas"), - ("bpy.types.spacesequenceeditor.show_strip_name*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-strip-name"), ("bpy.types.spacespreadsheet.show_only_selected*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-show-only-selected"), ("bpy.types.spacespreadsheetrowfilter.operation*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-operation"), ("bpy.types.spacespreadsheetrowfilter.threshold*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-threshold"), @@ -304,8 +381,11 @@ url_manual_mapping = ( ("bpy.ops.view3d.edit_mesh_extrude_move_normal*", "modeling/meshes/editing/face/extrude_faces.html#bpy-ops-view3d-edit-mesh-extrude-move-normal"), ("bpy.types.bakesettings.use_pass_transmission*", "render/cycles/baking.html#bpy-types-bakesettings-use-pass-transmission"), ("bpy.types.clothsettings.internal_compression*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-internal-compression"), + ("bpy.types.cyclescamerasettings.panorama_type*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-panorama-type"), ("bpy.types.cyclesrendersettings.dicing_camera*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-camera"), + ("bpy.types.cyclesrendersettings.film_exposure*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-film-exposure"), ("bpy.types.cyclesrendersettings.texture_limit*", "render/cycles/render_settings/simplify.html#bpy-types-cyclesrendersettings-texture-limit"), + ("bpy.types.cyclesrendersettings.use_denoising*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-use-denoising"), ("bpy.types.editbone.bbone_custom_handle_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-start"), ("bpy.types.editbone.bbone_handle_use_ease_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-use-ease-end"), ("bpy.types.fluiddomainsettings.additional_res*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-additional-res"), @@ -323,12 +403,19 @@ url_manual_mapping = ( ("bpy.types.fluideffectorsettings.use_effector*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-use-effector"), ("bpy.types.fluidflowsettings.surface_distance*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-surface-distance"), ("bpy.types.fluidflowsettings.texture_map_type*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-texture-map-type"), + ("bpy.types.freestylelineset.select_silhouette*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-silhouette"), + ("bpy.types.freestylelinestyle.texture_spacing*", "render/freestyle/view_layer/line_style/texture.html#bpy-types-freestylelinestyle-texture-spacing"), + ("bpy.types.freestylelinestyle.use_chain_count*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-chain-count"), + ("bpy.types.freestylelinestyle.use_dashed_line*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-dashed-line"), + ("bpy.types.freestylelinestyle.use_same_object*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-same-object"), ("bpy.types.geometrynodeattributesampletexture*", "modeling/geometry_nodes/attribute/attribute_sample_texture.html#bpy-types-geometrynodeattributesampletexture"), ("bpy.types.gpencilsculptguide.reference_point*", "grease_pencil/modes/draw/guides.html#bpy-types-gpencilsculptguide-reference-point"), ("bpy.types.greasepencil.edit_curve_resolution*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-edit-curve-resolution"), - ("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/2d_offset.html#bpy-types-linestylegeometrymodifier-2doffset"), - ("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/parameter_editor/line_style/modifiers/geometry/sampling.html#bpy-types-linestylegeometrymodifier-sampling"), + ("bpy.types.linestylegeometrymodifier_2doffset*", "render/freestyle/view_layer/line_style/modifiers/geometry/2d_offset.html#bpy-types-linestylegeometrymodifier-2doffset"), + ("bpy.types.linestylegeometrymodifier_sampling*", "render/freestyle/view_layer/line_style/modifiers/geometry/sampling.html#bpy-types-linestylegeometrymodifier-sampling"), ("bpy.types.nodesocketinterface*.default_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-default-value"), + ("bpy.types.rendersettings.line_thickness_mode*", "render/freestyle/render.html#bpy-types-rendersettings-line-thickness-mode"), + ("bpy.types.rendersettings.motion_blur_shutter*", "render/cycles/render_settings/motion_blur.html#bpy-types-rendersettings-motion-blur-shutter"), ("bpy.types.rendersettings.use_persistent_data*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-persistent-data"), ("bpy.types.sequencertoolsettings.overlap_mode*", "video_editing/sequencer/editing.html#bpy-types-sequencertoolsettings-overlap-mode"), ("bpy.types.spaceclipeditor.show_green_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-green-channel"), @@ -340,7 +427,9 @@ url_manual_mapping = ( ("bpy.types.clothsettings.use_pressure_volume*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-use-pressure-volume"), ("bpy.types.clothsettings.vertex_group_intern*", "physics/cloth/settings/physical_properties.html#bpy-types-clothsettings-vertex-group-intern"), ("bpy.types.colormanagedviewsettings.exposure*", "render/color_management.html#bpy-types-colormanagedviewsettings-exposure"), - ("bpy.types.cyclesrendersettings.*dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"), + ("bpy.types.cyclescamerasettings.fisheye_lens*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-fisheye-lens"), + ("bpy.types.cyclesobjectsettings.motion_steps*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-motion-steps"), + ("bpy.types.cyclesrendersettings.filter_width*", "render/cycles/render_settings/film.html#bpy-types-cyclesrendersettings-filter-width"), ("bpy.types.fluiddomainsettings.cfl_condition*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-cfl-condition"), ("bpy.types.fluiddomainsettings.show_velocity*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-show-velocity"), ("bpy.types.fluiddomainsettings.timesteps_max*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-timesteps-max"), @@ -351,6 +440,9 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.particle_system*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-particle-system"), ("bpy.types.fluidflowsettings.velocity_factor*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-factor"), ("bpy.types.fluidflowsettings.velocity_normal*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-normal"), + ("bpy.types.freestylelineset.select_edge_mark*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-edge-mark"), + ("bpy.types.freestylelinestyle.use_length_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-max"), + ("bpy.types.freestylelinestyle.use_length_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-length-min"), ("bpy.types.geometrynodealignrotationtovector*", "modeling/geometry_nodes/point/align_rotation_to_vector.html#bpy-types-geometrynodealignrotationtovector"), ("bpy.types.geometrynodeattributevectorrotate*", "modeling/geometry_nodes/attribute/attribute_vector_rotate.html#bpy-types-geometrynodeattributevectorrotate"), ("bpy.types.greasepencil.curve_edit_threshold*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-curve-edit-threshold"), @@ -362,9 +454,9 @@ url_manual_mapping = ( ("bpy.types.sculpt.constant_detail_resolution*", "sculpt_paint/sculpting/tool_settings/dyntopo.html#bpy-types-sculpt-constant-detail-resolution"), ("bpy.types.spaceclipeditor.annotation_source*", "movie_clip/tracking/clip/sidebar/view.html#bpy-types-spaceclipeditor-annotation-source"), ("bpy.types.spaceclipeditor.show_blue_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-blue-channel"), + ("bpy.types.spacefilebrowser.system_bookmarks*", "editors/file_browser.html#bpy-types-spacefilebrowser-system-bookmarks"), ("bpy.types.spaceoutliner.use_filter_children*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-children"), ("bpy.types.spaceoutliner.use_filter_complete*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-complete"), - ("bpy.types.spacesequenceeditor.show_metadata*", "video_editing/preview/introduction.html#bpy-types-spacesequenceeditor-show-metadata"), ("bpy.types.spacespreadsheet.attribute_domain*", "editors/spreadsheet.html#bpy-types-spacespreadsheet-attribute-domain"), ("bpy.types.spacespreadsheetrowfilter.enabled*", "editors/spreadsheet.html#bpy-types-spacespreadsheetrowfilter-enabled"), ("bpy.types.spaceview3d.transform_orientation*", "editors/3dview/controls/orientation.html#bpy-types-spaceview3d-transform-orientation"), @@ -375,13 +467,21 @@ url_manual_mapping = ( ("bpy.ops.object.constraint_add_with_targets*", "animation/constraints/interface/adding_removing.html#bpy-ops-object-constraint-add-with-targets"), ("bpy.ops.object.material_slot_remove_unused*", "scene_layout/object/editing/cleanup.html#bpy-ops-object-material-slot-remove-unused"), ("bpy.ops.outliner.collection_disable_render*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable-render"), + ("bpy.ops.scene.freestyle_alpha_modifier_add*", "render/freestyle/view_layer/line_style/alpha.html#bpy-ops-scene-freestyle-alpha-modifier-add"), + ("bpy.ops.scene.freestyle_color_modifier_add*", "render/freestyle/view_layer/line_style/color.html#bpy-ops-scene-freestyle-color-modifier-add"), ("bpy.types.brush.cloth_simulation_area_type*", "sculpt_paint/sculpting/tools/cloth.html#bpy-types-brush-cloth-simulation-area-type"), ("bpy.types.brushgpencilsettings.fill_factor*", "grease_pencil/modes/draw/tools/fill.html#bpy-types-brushgpencilsettings-fill-factor"), ("bpy.types.curve.bevel_factor_mapping_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-start"), + ("bpy.types.cyclescamerasettings.fisheye_fov*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-fisheye-fov"), + ("bpy.types.cyclesobjectsettings.ao_distance*", "render/cycles/object_settings/object_data.html#bpy-types-cyclesobjectsettings-ao-distance"), ("bpy.types.cyclesobjectsettings.dicing_rate*", "render/cycles/object_settings/adaptive_subdiv.html#bpy-types-cyclesobjectsettings-dicing-rate"), + ("bpy.types.cyclesrendersettings.blur_glossy*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-blur-glossy"), + ("bpy.types.cyclesrendersettings.dicing_rate*", "render/cycles/render_settings/subdivision.html#bpy-types-cyclesrendersettings-dicing-rate"), + ("bpy.types.cyclesrendersettings.max_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-max-bounces"), ("bpy.types.cyclesrendersettings.use_fast_gi*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-use-fast-gi"), ("bpy.types.editbone.bbone_custom_handle_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-custom-handle-end"), ("bpy.types.editbone.bbone_handle_type_start*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-start"), + ("bpy.types.fileselectparams.recursion_level*", "editors/file_browser.html#bpy-types-fileselectparams-recursion-level"), ("bpy.types.fluiddomainsettings.adapt_margin*", "physics/fluid/type/domain/gas/adaptive_domain.html#bpy-types-fluiddomainsettings-adapt-margin"), ("bpy.types.fluiddomainsettings.burning_rate*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-burning-rate"), ("bpy.types.fluiddomainsettings.guide_parent*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-parent"), @@ -395,6 +495,10 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.use_plane_init*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-plane-init"), ("bpy.types.fluidflowsettings.velocity_coord*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-velocity-coord"), ("bpy.types.fluidflowsettings.volume_density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-volume-density"), + ("bpy.types.freestylelinestyle.use_angle_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-angle-max"), + ("bpy.types.freestylelinestyle.use_angle_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-angle-min"), + ("bpy.types.freestylesettings.as_render_pass*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-as-render-pass"), + ("bpy.types.freestylesettings.use_smoothness*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-smoothness"), ("bpy.types.geometrynodecurvequadraticbezier*", "modeling/geometry_nodes/curve_primitives/quadratic_bezier.html#bpy-types-geometrynodecurvequadraticbezier"), ("bpy.types.materialgpencilstyle.show_stroke*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-stroke"), ("bpy.types.movietrackingcamera.focal_length*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-focal-length"), @@ -406,7 +510,6 @@ url_manual_mapping = ( ("bpy.types.spaceclipeditor.show_red_channel*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-show-red-channel"), ("bpy.types.spaceclipeditor.use_mute_footage*", "editors/clip/display/clip_display.html#bpy-types-spaceclipeditor-use-mute-footage"), ("bpy.types.spacesequenceeditor.overlay_type*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-overlay-type"), - ("bpy.types.spacesequenceeditor.show_fcurves*", "editors/video_sequencer/sequencer/display.html#bpy-types-spacesequenceeditor-show-fcurves"), ("bpy.types.spaceuveditor.sticky_select_mode*", "editors/uv/selecting.html#bpy-types-spaceuveditor-sticky-select-mode"), ("bpy.types.spaceview3d.show_object_viewport*", "editors/3dview/display/visibility.html#bpy-types-spaceview3d-show-object-viewport"), ("bpy.types.view3doverlay.show_fade_inactive*", "editors/3dview/display/overlays.html#bpy-types-view3doverlay-show-fade-inactive"), @@ -425,7 +528,11 @@ url_manual_mapping = ( ("bpy.types.brush.disconnected_distance_max*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-disconnected-distance-max"), ("bpy.types.brush.surface_smooth_iterations*", "sculpt_paint/sculpting/tools/smooth.html#bpy-types-brush-surface-smooth-iterations"), ("bpy.types.brushgpencilsettings.pen_jitter*", "grease_pencil/modes/draw/tools/draw.html#bpy-types-brushgpencilsettings-pen-jitter"), + ("bpy.types.cyclescurverendersettings.shape*", "render/cycles/render_settings/hair.html#bpy-types-cyclescurverendersettings-shape"), ("bpy.types.cyclesrendersettings.ao_bounces*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-ao-bounces"), + ("bpy.types.cyclesrendersettings.time_limit*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-time-limit"), + ("bpy.types.cyclesvisibilitysettings.camera*", "render/cycles/world_settings.html#bpy-types-cyclesvisibilitysettings-camera"), + ("bpy.types.cyclesworldsettings.max_bounces*", "render/cycles/world_settings.html#bpy-types-cyclesworldsettings-max-bounces"), ("bpy.types.fluiddomainsettings.domain_type*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-domain-type"), ("bpy.types.fluiddomainsettings.flame_smoke*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flame-smoke"), ("bpy.types.fluiddomainsettings.fluid_group*", "physics/fluid/type/domain/collections.html#bpy-types-fluiddomainsettings-fluid-group"), @@ -435,6 +542,10 @@ url_manual_mapping = ( ("bpy.types.fluideffectorsettings.subframes*", "physics/fluid/type/effector.html#bpy-types-fluideffectorsettings-subframes"), ("bpy.types.fluidflowsettings.flow_behavior*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-behavior"), ("bpy.types.fluidflowsettings.noise_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-noise-texture"), + ("bpy.types.freestylelineset.select_contour*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-contour"), + ("bpy.types.freestylelinestyle.split_length*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-length"), + ("bpy.types.freestylelinestyle.use_chaining*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-chaining"), + ("bpy.types.freestylesettings.sphere_radius*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-sphere-radius"), ("bpy.types.geometrynodeattributevectormath*", "modeling/geometry_nodes/attribute/attribute_vector_math.html#bpy-types-geometrynodeattributevectormath"), ("bpy.types.gpencillayer.annotation_opacity*", "interface/annotate_tool.html#bpy-types-gpencillayer-annotation-opacity"), ("bpy.types.gpencillayer.use_onion_skinning*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-onion-skinning"), @@ -442,20 +553,21 @@ url_manual_mapping = ( ("bpy.types.gpencilsculptsettings.lock_axis*", "grease_pencil/modes/draw/drawing_planes.html#bpy-types-gpencilsculptsettings-lock-axis"), ("bpy.types.imageformatsettings.file_format*", "render/output/properties/output.html#bpy-types-imageformatsettings-file-format"), ("bpy.types.imagepaint.use_backface_culling*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-use-backface-culling"), - ("bpy.types.linestyle*modifier_curvature_3d*", "render/freestyle/parameter_editor/line_style/modifiers/color/curvature_3d.html#bpy-types-linestyle-modifier-curvature-3d"), + ("bpy.types.linestyle*modifier_curvature_3d*", "render/freestyle/view_layer/line_style/modifiers/color/curvature_3d.html#bpy-types-linestyle-modifier-curvature-3d"), ("bpy.types.materialgpencilstyle.fill_color*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-fill-color"), ("bpy.types.materialgpencilstyle.fill_style*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-fill-style"), ("bpy.types.materialgpencilstyle.mix_factor*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mix-factor"), ("bpy.types.materialgpencilstyle.pass_index*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-pass-index"), ("bpy.types.nodesocketinterface.description*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-description"), ("bpy.types.rendersettings.dither_intensity*", "render/output/properties/post_processing.html#bpy-types-rendersettings-dither-intensity"), + ("bpy.types.rendersettings.film_transparent*", "render/cycles/render_settings/film.html#bpy-types-rendersettings-film-transparent"), ("bpy.types.rendersettings.simplify_volumes*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-volumes"), ("bpy.types.rendersettings.use_render_cache*", "render/output/properties/output.html#bpy-types-rendersettings-use-render-cache"), - ("bpy.types.rendersettings.use_save_buffers*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-save-buffers"), ("bpy.types.rendersettings.use_single_layer*", "render/layers/view_layer.html#bpy-types-rendersettings-use-single-layer"), - ("bpy.types.rendersettings_simplify_gpencil*", "render/cycles/render_settings/simplify.html#bpy-types-rendersettings-simplify-gpencil"), ("bpy.types.sceneeevee.use_taa_reprojection*", "render/eevee/render_settings/sampling.html#bpy-types-sceneeevee-use-taa-reprojection"), ("bpy.types.sequenceeditor.use_overlay_lock*", "video_editing/preview/sidebar.html#bpy-types-sequenceeditor-use-overlay-lock"), + ("bpy.types.spacefilebrowser.recent_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-recent-folders"), + ("bpy.types.spacefilebrowser.system_folders*", "editors/file_browser.html#bpy-types-spacefilebrowser-system-folders"), ("bpy.types.spaceoutliner.use_filter_object*", "editors/outliner/interface.html#bpy-types-spaceoutliner-use-filter-object"), ("bpy.types.spacesequenceeditor.use_proxies*", "video_editing/preview/sidebar.html#bpy-types-spacesequenceeditor-use-proxies"), ("bpy.types.spaceuveditor.show_pixel_coords*", "editors/uv/sidebar.html#bpy-types-spaceuveditor-show-pixel-coords"), @@ -484,8 +596,10 @@ url_manual_mapping = ( ("bpy.types.colormanagedviewsettings.gamma*", "render/color_management.html#bpy-types-colormanagedviewsettings-gamma"), ("bpy.types.compositornodeplanetrackdeform*", "compositing/types/distort/plane_track_deform.html#bpy-types-compositornodeplanetrackdeform"), ("bpy.types.curve.bevel_factor_mapping_end*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-mapping-end"), + ("bpy.types.cyclescamerasettings.longitude*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-longitude"), ("bpy.types.editbone.bbone_handle_type_end*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-handle-type-end"), ("bpy.types.editbone.use_endroll_as_inroll*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-endroll-as-inroll"), + ("bpy.types.fileselectparams.filter_search*", "editors/file_browser.html#bpy-types-fileselectparams-filter-search"), ("bpy.types.fluiddomainsettings.cache_type*", "physics/fluid/type/domain/cache.html#bpy-types-fluiddomainsettings-cache-type"), ("bpy.types.fluiddomainsettings.flip_ratio*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-flip-ratio"), ("bpy.types.fluiddomainsettings.guide_beta*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-guide-beta"), @@ -494,6 +608,11 @@ url_manual_mapping = ( ("bpy.types.fluiddomainsettings.time_scale*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-time-scale"), ("bpy.types.fluidflowsettings.texture_size*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-texture-size"), ("bpy.types.fluidflowsettings.use_absolute*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-absolute"), + ("bpy.types.freestylelineset.select_border*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-border"), + ("bpy.types.freestylelineset.select_crease*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-select-crease"), + ("bpy.types.freestylelinestyle.chain_count*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-chain-count"), + ("bpy.types.freestylelinestyle.use_sorting*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-use-sorting"), + ("bpy.types.freestylesettings.crease_angle*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-crease-angle"), ("bpy.types.geometrynodeattributecolorramp*", "modeling/geometry_nodes/attribute/attribute_color_ramp.html#bpy-types-geometrynodeattributecolorramp"), ("bpy.types.geometrynodeattributeproximity*", "modeling/geometry_nodes/attribute/attribute_proximity.html#bpy-types-geometrynodeattributeproximity"), ("bpy.types.geometrynodeattributerandomize*", "modeling/geometry_nodes/attribute/attribute_randomize.html#bpy-types-geometrynodeattributerandomize"), @@ -501,9 +620,9 @@ url_manual_mapping = ( ("bpy.types.geometrynodeseparatecomponents*", "modeling/geometry_nodes/geometry/separate_components.html#bpy-types-geometrynodeseparatecomponents"), ("bpy.types.geometrynodesubdivisionsurface*", "modeling/geometry_nodes/mesh/subdivision_surface.html#bpy-types-geometrynodesubdivisionsurface"), ("bpy.types.imageformatsettings.color_mode*", "render/output/properties/output.html#bpy-types-imageformatsettings-color-mode"), - ("bpy.types.linestyle*modifier_alongstroke*", "render/freestyle/parameter_editor/line_style/modifiers/color/along_stroke.html#bpy-types-linestyle-modifier-alongstroke"), - ("bpy.types.linestyle*modifier_creaseangle*", "render/freestyle/parameter_editor/line_style/modifiers/color/crease_angle.html#bpy-types-linestyle-modifier-creaseangle"), - ("bpy.types.linestylecolormodifier_tangent*", "render/freestyle/parameter_editor/line_style/modifiers/color/tangent.html#bpy-types-linestylecolormodifier-tangent"), + ("bpy.types.linestyle*modifier_alongstroke*", "render/freestyle/view_layer/line_style/modifiers/color/along_stroke.html#bpy-types-linestyle-modifier-alongstroke"), + ("bpy.types.linestyle*modifier_creaseangle*", "render/freestyle/view_layer/line_style/modifiers/color/crease_angle.html#bpy-types-linestyle-modifier-creaseangle"), + ("bpy.types.linestylecolormodifier_tangent*", "render/freestyle/view_layer/line_style/modifiers/color/tangent.html#bpy-types-linestylecolormodifier-tangent"), ("bpy.types.materialgpencilstyle.mix_color*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-mix-color"), ("bpy.types.materialgpencilstyle.show_fill*", "grease_pencil/materials/properties.html#bpy-types-materialgpencilstyle-show-fill"), ("bpy.types.mesh.use_mirror_vertex_group_x*", "sculpt_paint/weight_paint/tool_settings/symmetry.html#bpy-types-mesh-use-mirror-vertex-group-x"), @@ -515,6 +634,7 @@ url_manual_mapping = ( ("bpy.types.nodesocketinterface.hide_value*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-hide-value"), ("bpy.types.objectlineart.crease_threshold*", "scene_layout/object/properties/line_art.html#bpy-types-objectlineart-crease-threshold"), ("bpy.types.rendersettings.use_compositing*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-compositing"), + ("bpy.types.rendersettings.use_motion_blur*", "render/cycles/render_settings/motion_blur.html#bpy-types-rendersettings-use-motion-blur"), ("bpy.types.rendersettings.use_placeholder*", "render/output/properties/output.html#bpy-types-rendersettings-use-placeholder"), ("bpy.types.shadernodesubsurfacescattering*", "render/shader_nodes/shader/sss.html#bpy-types-shadernodesubsurfacescattering"), ("bpy.types.spaceclipeditor.lock_selection*", "editors/clip/introduction.html#bpy-types-spaceclipeditor-lock-selection"), @@ -544,7 +664,12 @@ url_manual_mapping = ( ("bpy.types.colormanagedviewsettings.look*", "render/color_management.html#bpy-types-colormanagedviewsettings-look"), ("bpy.types.compositornodecolorcorrection*", "compositing/types/color/color_correction.html#bpy-types-compositornodecolorcorrection"), ("bpy.types.compositornodemoviedistortion*", "compositing/types/distort/movie_distortion.html#bpy-types-compositornodemoviedistortion"), + ("bpy.types.cyclescamerasettings.latitude*", "render/cycles/object_settings/cameras.html#bpy-types-cyclescamerasettings-latitude"), + ("bpy.types.cyclesrendersettings.caustics*", "render/cycles/render_settings/light_paths.html#bpy-types-cyclesrendersettings-caustics"), + ("bpy.types.cyclesrendersettings.denoiser*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-denoiser"), ("bpy.types.editbone.use_inherit_rotation*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-inherit-rotation"), + ("bpy.types.fileselectparams.display_size*", "editors/file_browser.html#bpy-types-fileselectparams-display-size"), + ("bpy.types.fileselectparams.display_type*", "editors/file_browser.html#bpy-types-fileselectparams-display-type"), ("bpy.types.fluiddomainsettings.use_guide*", "physics/fluid/type/domain/guides.html#bpy-types-fluiddomainsettings-use-guide"), ("bpy.types.fluiddomainsettings.use_noise*", "physics/fluid/type/domain/gas/noise.html#bpy-types-fluiddomainsettings-use-noise"), ("bpy.types.fluiddomainsettings.use_slice*", "physics/fluid/type/domain/gas/viewport_display.html#bpy-types-fluiddomainsettings-use-slice"), @@ -555,6 +680,11 @@ url_manual_mapping = ( ("bpy.types.fluidflowsettings.temperature*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-temperature"), ("bpy.types.fluidflowsettings.use_texture*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-texture"), ("bpy.types.fmodifierenvelopecontrolpoint*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelopecontrolpoint"), + ("bpy.types.freestylelinestyle.length_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-length-max"), + ("bpy.types.freestylelinestyle.length_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-length-min"), + ("bpy.types.freestylelinestyle.sort_order*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-sort-order"), + ("bpy.types.freestylelinestyle.split_dash*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-dash"), + ("bpy.types.freestylesettings.use_culling*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-use-culling"), ("bpy.types.geometrynodeattributecurvemap*", "modeling/geometry_nodes/attribute/attribute_curve_map.html#bpy-types-geometrynodeattributecurvemap"), ("bpy.types.geometrynodeattributemaprange*", "modeling/geometry_nodes/attribute/attribute_map_range.html#bpy-types-geometrynodeattributemaprange"), ("bpy.types.geometrynodeattributetransfer*", "modeling/geometry_nodes/attribute/attribute_transfer.html#bpy-types-geometrynodeattributetransfer"), @@ -564,6 +694,7 @@ url_manual_mapping = ( ("bpy.types.movietrackingcamera.principal*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-principal"), ("bpy.types.object.use_camera_lock_parent*", "scene_layout/object/properties/relations.html#bpy-types-object-use-camera-lock-parent"), ("bpy.types.object.visible_volume_scatter*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-volume-scatter"), + ("bpy.types.rendersettings.line_thickness*", "render/freestyle/render.html#bpy-types-rendersettings-line-thickness"), ("bpy.types.rendersettings.pixel_aspect_x*", "render/output/properties/format.html#bpy-types-rendersettings-pixel-aspect-x"), ("bpy.types.rendersettings.pixel_aspect_y*", "render/output/properties/format.html#bpy-types-rendersettings-pixel-aspect-y"), ("bpy.types.rigidbodyconstraint.use_limit*", "physics/rigid_body/constraints/introduction.html#bpy-types-rigidbodyconstraint-use-limit"), @@ -600,8 +731,15 @@ url_manual_mapping = ( ("bpy.types.brush.use_pose_lock_rotation*", "sculpt_paint/sculpting/tools/pose.html#bpy-types-brush-use-pose-lock-rotation"), ("bpy.types.compositornodebrightcontrast*", "compositing/types/color/bright_contrast.html#bpy-types-compositornodebrightcontrast"), ("bpy.types.compositornodedoubleedgemask*", "compositing/types/matte/double_edge_mask.html#bpy-types-compositornodedoubleedgemask"), + ("bpy.types.cyclesrendersettings.samples*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-samples"), + ("bpy.types.fileselectparams.show_hidden*", "editors/file_browser.html#bpy-types-fileselectparams-show-hidden"), + ("bpy.types.fileselectparams.sort_method*", "editors/file_browser.html#bpy-types-fileselectparams-sort-method"), ("bpy.types.fluiddomainsettings.clipping*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-clipping"), ("bpy.types.fluiddomainsettings.use_mesh*", "physics/fluid/type/domain/liquid/mesh.html#bpy-types-fluiddomainsettings-use-mesh"), + ("bpy.types.freestylelinestyle.angle_max*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-angle-max"), + ("bpy.types.freestylelinestyle.angle_min*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-angle-min"), + ("bpy.types.freestylelinestyle.split_gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-split-gap"), + ("bpy.types.freestylelinestyle.use_nodes*", "render/freestyle/view_layer/line_style/texture.html#bpy-types-freestylelinestyle-use-nodes"), ("bpy.types.geometrynodeattributecompare*", "modeling/geometry_nodes/attribute/attribute_compare.html#bpy-types-geometrynodeattributecompare"), ("bpy.types.geometrynodeattributeconvert*", "modeling/geometry_nodes/attribute/attribute_convert.html#bpy-types-geometrynodeattributeconvert"), ("bpy.types.geometrynodeselectbymaterial*", "modeling/geometry_nodes/material/select_by_material.html#bpy-types-geometrynodeselectbymaterial"), @@ -612,6 +750,7 @@ url_manual_mapping = ( ("bpy.types.object.use_empty_image_alpha*", "modeling/empties.html#bpy-types-object-use-empty-image-alpha"), ("bpy.types.rendersettings.frame_map_new*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-new"), ("bpy.types.rendersettings.frame_map_old*", "render/output/properties/frame_range.html#bpy-types-rendersettings-frame-map-old"), + ("bpy.types.rendersettings.use_auto_tile*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-use-auto-tile"), ("bpy.types.rendersettings.use_overwrite*", "render/output/properties/output.html#bpy-types-rendersettings-use-overwrite"), ("bpy.types.rendersettings.use_sequencer*", "render/output/properties/post_processing.html#bpy-types-rendersettings-use-sequencer"), ("bpy.types.sceneeevee.volumetric_shadow*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric-shadow"), @@ -657,16 +796,21 @@ url_manual_mapping = ( ("bpy.types.compositornodesetalpha.mode*", "compositing/types/converter/set_alpha.html#bpy-types-compositornodesetalpha-mode"), ("bpy.types.dopesheet.use_filter_invert*", "editors/graph_editor/channels.html#bpy-types-dopesheet-use-filter-invert"), ("bpy.types.editbone.use_local_location*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-use-local-location"), + ("bpy.types.fileselectparams.use_filter*", "editors/file_browser.html#bpy-types-fileselectparams-use-filter"), ("bpy.types.fluiddomainsettings.gravity*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-gravity"), ("bpy.types.fluidflowsettings.flow_type*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-flow-type"), ("bpy.types.fluidflowsettings.subframes*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-subframes"), + ("bpy.types.freestylelineset.collection*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-collection"), + ("bpy.types.freestylelineset.visibility*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-visibility"), + ("bpy.types.freestylelinestyle.chaining*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-chaining"), + ("bpy.types.freestylelinestyle.sort_key*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-sort-key"), ("bpy.types.geometrynodeattributeremove*", "modeling/geometry_nodes/attribute/attribute_remove.html#bpy-types-geometrynodeattributeremove"), ("bpy.types.geometrynodematerialreplace*", "modeling/geometry_nodes/material/replace.html#bpy-types-geometrynodematerialreplace"), ("bpy.types.geometrynodepointdistribute*", "modeling/geometry_nodes/point/point_distribute.html#bpy-types-geometrynodepointdistribute"), ("bpy.types.gpencillayer.use_mask_layer*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-use-mask-layer"), ("bpy.types.greasepencil.use_curve_edit*", "grease_pencil/modes/edit/curve_editing.html#bpy-types-greasepencil-use-curve-edit"), ("bpy.types.imagepaint.screen_grab_size*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-types-imagepaint-screen-grab-size"), - ("bpy.types.linestyle*modifier_material*", "render/freestyle/parameter_editor/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"), + ("bpy.types.linestyle*modifier_material*", "render/freestyle/view_layer/line_style/modifiers/color/material.html#bpy-types-linestyle-modifier-material"), ("bpy.types.movietrackingcamera.brown_k*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-brown-k"), ("bpy.types.movietrackingcamera.brown_p*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera-brown-p"), ("bpy.types.object.visible_transmission*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-transmission"), @@ -724,6 +868,7 @@ url_manual_mapping = ( ("bpy.types.compositornodecolorbalance*", "compositing/types/color/color_balance.html#bpy-types-compositornodecolorbalance"), ("bpy.types.compositornodekeyingscreen*", "compositing/types/matte/keying_screen.html#bpy-types-compositornodekeyingscreen"), ("bpy.types.dynamicpaintcanvassettings*", "physics/dynamic_paint/canvas.html#bpy-types-dynamicpaintcanvassettings"), + ("bpy.types.fileselectparams.directory*", "editors/file_browser.html#bpy-types-fileselectparams-directory"), ("bpy.types.fluidflowsettings.use_flow*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-use-flow"), ("bpy.types.fmodifierfunctiongenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierfunctiongenerator"), ("bpy.types.geometrynodeattributeclamp*", "modeling/geometry_nodes/attribute/attribute_clamp.html#bpy-types-geometrynodeattributeclamp"), @@ -747,6 +892,7 @@ url_manual_mapping = ( ("bpy.types.shadernodeambientocclusion*", "render/shader_nodes/input/ao.html#bpy-types-shadernodeambientocclusion"), ("bpy.types.shadernodevolumeabsorption*", "render/shader_nodes/shader/volume_absorption.html#bpy-types-shadernodevolumeabsorption"), ("bpy.types.shadernodevolumeprincipled*", "render/shader_nodes/shader/volume_principled.html#bpy-types-shadernodevolumeprincipled"), + ("bpy.types.spacefilebrowser.bookmarks*", "editors/file_browser.html#bpy-types-spacefilebrowser-bookmarks"), ("bpy.types.spaceoutliner.display_mode*", "editors/outliner/interface.html#bpy-types-spaceoutliner-display-mode"), ("bpy.types.spaceoutliner.filter_state*", "editors/outliner/interface.html#bpy-types-spaceoutliner-filter-state"), ("bpy.types.toolsettings.keyframe_type*", "editors/timeline.html#bpy-types-toolsettings-keyframe-type"), @@ -774,6 +920,7 @@ url_manual_mapping = ( ("bpy.ops.outliner.collection_disable*", "editors/outliner/editing.html#bpy-ops-outliner-collection-disable"), ("bpy.ops.outliner.collection_isolate*", "editors/outliner/editing.html#bpy-ops-outliner-collection-isolate"), ("bpy.ops.pose.visual_transform_apply*", "animation/armatures/posing/editing/apply.html#bpy-ops-pose-visual-transform-apply"), + ("bpy.ops.render.shutter_curve_preset*", "render/cycles/render_settings/motion_blur.html#bpy-ops-render-shutter-curve-preset"), ("bpy.ops.sequencer.view_ghost_border*", "video_editing/preview/sidebar.html#bpy-ops-sequencer-view-ghost-border"), ("bpy.ops.ui.override_type_set_button*", "files/linked_libraries/library_overrides.html#bpy-ops-ui-override-type-set-button"), ("bpy.ops.view3d.blenderkit_asset_bar*", "addons/3d_view/blenderkit.html#bpy-ops-view3d-blenderkit-asset-bar"), @@ -792,10 +939,14 @@ url_manual_mapping = ( ("bpy.types.compositornodeellipsemask*", "compositing/types/matte/ellipse_mask.html#bpy-types-compositornodeellipsemask"), ("bpy.types.compositornodesplitviewer*", "compositing/types/output/split_viewer.html#bpy-types-compositornodesplitviewer"), ("bpy.types.curve.render_resolution_u*", "modeling/curves/properties/shape.html#bpy-types-curve-render-resolution-u"), + ("bpy.types.cyclesrendersettings.seed*", "render/cycles/render_settings/sampling.html#bpy-types-cyclesrendersettings-seed"), ("bpy.types.dynamicpaintbrushsettings*", "physics/dynamic_paint/brush.html#bpy-types-dynamicpaintbrushsettings"), ("bpy.types.editbone.use_scale_easing*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-use-scale-easing"), + ("bpy.types.fileselectparams.filename*", "editors/file_browser.html#bpy-types-fileselectparams-filename"), ("bpy.types.fluiddomainsettings.alpha*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-alpha"), ("bpy.types.fluidflowsettings.density*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings-density"), + ("bpy.types.freestylelineset.qi_start*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-start"), + ("bpy.types.freestylelinestyle.rounds*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-rounds"), ("bpy.types.geometrynodeattributefill*", "modeling/geometry_nodes/attribute/attribute_fill.html#bpy-types-geometrynodeattributefill"), ("bpy.types.geometrynodeattributemath*", "modeling/geometry_nodes/attribute/attribute_math.html#bpy-types-geometrynodeattributemath"), ("bpy.types.geometrynodecurvetopoints*", "modeling/geometry_nodes/curve/curve_to_points.html#bpy-types-geometrynodecurvetopoints"), @@ -833,7 +984,9 @@ url_manual_mapping = ( ("bpy.types.spaceuveditor.lock_bounds*", "modeling/meshes/uv/editing.html#bpy-types-spaceuveditor-lock-bounds"), ("bpy.types.spline.tilt_interpolation*", "modeling/curves/properties/active_spline.html#bpy-types-spline-tilt-interpolation"), ("bpy.types.transformorientation.name*", "editors/3dview/controls/orientation.html#bpy-types-transformorientation-name"), + ("bpy.types.viewlayer.use_motion_blur*", "render/layers/introduction.html#bpy-types-viewlayer-use-motion-blur"), ("bpy.types.volumedisplay.slice_depth*", "modeling/volumes/properties.html#bpy-types-volumedisplay-slice-depth"), + ("bpy.types.worldmistsettings.falloff*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-falloff"), ("bpy.ops.clip.lock_selection_toggle*", "editors/clip/introduction.html#bpy-ops-clip-lock-selection-toggle"), ("bpy.ops.mesh.customdata_mask_clear*", "sculpt_paint/sculpting/editing/mask.html#bpy-ops-mesh-customdata-mask-clear"), ("bpy.ops.mesh.extrude_vertices_move*", "modeling/meshes/editing/vertex/extrude_vertices.html#bpy-ops-mesh-extrude-vertices-move"), @@ -879,6 +1032,9 @@ url_manual_mapping = ( ("bpy.types.curve.bevel_factor_start*", "modeling/curves/properties/geometry.html#bpy-types-curve-bevel-factor-start"), ("bpy.types.fluiddomainsettings.beta*", "physics/fluid/type/domain/settings.html#bpy-types-fluiddomainsettings-beta"), ("bpy.types.fluidmodifier.fluid_type*", "physics/fluid/type/index.html#bpy-types-fluidmodifier-fluid-type"), + ("bpy.types.freestylelineset.exclude*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-exclude"), + ("bpy.types.freestylelinestyle.alpha*", "render/freestyle/view_layer/line_style/alpha.html#bpy-types-freestylelinestyle-alpha"), + ("bpy.types.freestylelinestyle.color*", "render/freestyle/view_layer/line_style/color.html#bpy-types-freestylelinestyle-color"), ("bpy.types.functionnodefloatcompare*", "modeling/geometry_nodes/utilities/float_compare.html#bpy-types-functionnodefloatcompare"), ("bpy.types.geometrynodeattributemix*", "modeling/geometry_nodes/attribute/attribute_mix.html#bpy-types-geometrynodeattributemix"), ("bpy.types.geometrynodecurvereverse*", "modeling/geometry_nodes/curve/curve_reverse.html#bpy-types-geometrynodecurvereverse"), @@ -888,13 +1044,15 @@ url_manual_mapping = ( ("bpy.types.geometrynodevolumetomesh*", "modeling/geometry_nodes/volume/volume_to_mesh.html#bpy-types-geometrynodevolumetomesh"), ("bpy.types.image.use_half_precision*", "editors/image/image_settings.html#bpy-types-image-use-half-precision"), ("bpy.types.imagepaint.interpolation*", "sculpt_paint/texture_paint/tool_settings/texture_slots.html#bpy-types-imagepaint-interpolation"), - ("bpy.types.linestyle*modifier_noise*", "render/freestyle/parameter_editor/line_style/modifiers/color/noise.html#bpy-types-linestyle-modifier-noise"), + ("bpy.types.linestyle*modifier_noise*", "render/freestyle/view_layer/line_style/modifiers/color/noise.html#bpy-types-linestyle-modifier-noise"), ("bpy.types.maintainvolumeconstraint*", "animation/constraints/transform/maintain_volume.html#bpy-types-maintainvolumeconstraint"), ("bpy.types.mesh.use_mirror_topology*", "modeling/meshes/tools/tool_settings.html#bpy-types-mesh-use-mirror-topology"), ("bpy.types.movieclip.display_aspect*", "editors/clip/display/clip_display.html#bpy-types-movieclip-display-aspect"), ("bpy.types.nodesocketinterface.name*", "interface/controls/nodes/groups.html#bpy-types-nodesocketinterface-name"), ("bpy.types.object.is_shadow_catcher*", "render/cycles/object_settings/object_data.html#bpy-types-object-is-shadow-catcher"), ("bpy.types.particleinstancemodifier*", "modeling/modifiers/physics/particle_instance.html#bpy-types-particleinstancemodifier"), + ("bpy.types.rendersettings.tile_size*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-size"), + ("bpy.types.sequencertimelineoverlay*", "editors/video_sequencer/sequencer/display.html#bpy-types-sequencertimelineoverlay"), ("bpy.types.sequencetransform.offset*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-offset"), ("bpy.types.shadernodebrightcontrast*", "render/shader_nodes/color/bright_contrast.html#bpy-types-shadernodebrightcontrast"), ("bpy.types.shadernodebsdfprincipled*", "render/shader_nodes/shader/principled.html#bpy-types-shadernodebsdfprincipled"), @@ -960,6 +1118,9 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_curveinz*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-curveinz"), ("bpy.types.editbone.bbone_scaleout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scaleout"), ("bpy.types.editbone.bbone_segments*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-segments"), + ("bpy.types.freestylelineset.qi_end*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset-qi-end"), + ("bpy.types.freestylelinestyle.caps*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-caps"), + ("bpy.types.freestylelinestyle.dash*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-dash"), ("bpy.types.freestylemodulesettings*", "render/freestyle/python.html#bpy-types-freestylemodulesettings"), ("bpy.types.functionnodebooleanmath*", "modeling/geometry_nodes/utilities/boolean_math.html#bpy-types-functionnodebooleanmath"), ("bpy.types.functionnodeinputstring*", "modeling/geometry_nodes/input/string.html#bpy-types-functionnodeinputstring"), @@ -987,6 +1148,7 @@ url_manual_mapping = ( ("bpy.types.rendersettings.fps_base*", "render/output/properties/format.html#bpy-types-rendersettings-fps-base"), ("bpy.types.rigidbodyobject.enabled*", "physics/rigid_body/properties/settings.html#bpy-types-rigidbodyobject-enabled"), ("bpy.types.sceneeevee.use_overscan*", "render/eevee/render_settings/film.html#bpy-types-sceneeevee-use-overscan"), + ("bpy.types.sequencerpreviewoverlay*", "video_editing/preview/introduction.html#bpy-types-sequencerpreviewoverlay"), ("bpy.types.sequencetransform.scale*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sequencetransform-scale"), ("bpy.types.shadernodeeeveespecular*", "render/shader_nodes/shader/specular_bsdf.html#bpy-types-shadernodeeeveespecular"), ("bpy.types.shadernodehuesaturation*", "render/shader_nodes/color/hue_saturation.html#bpy-types-shadernodehuesaturation"), @@ -995,8 +1157,11 @@ url_manual_mapping = ( ("bpy.types.simplifygpencilmodifier*", "grease_pencil/modifiers/generate/simplify.html#bpy-types-simplifygpencilmodifier"), ("bpy.types.spacegrapheditor.cursor*", "editors/graph_editor/introduction.html#bpy-types-spacegrapheditor-cursor"), ("bpy.types.vertexweightmixmodifier*", "modeling/modifiers/modify/weight_mix.html#bpy-types-vertexweightmixmodifier"), - ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer.html#bpy-types-viewlayer-use-freestyle"), + ("bpy.types.viewlayer.use_freestyle*", "render/freestyle/view_layer/freestyle.html#bpy-types-viewlayer-use-freestyle"), ("bpy.types.volumedisplay.use_slice*", "modeling/volumes/properties.html#bpy-types-volumedisplay-use-slice"), + ("bpy.types.worldlighting.ao_factor*", "render/cycles/render_settings/light_paths.html#bpy-types-worldlighting-ao-factor"), + ("bpy.types.worldmistsettings.depth*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-depth"), + ("bpy.types.worldmistsettings.start*", "render/cycles/world_settings.html#bpy-types-worldmistsettings-start"), ("bpy.ops.armature.armature_layers*", "animation/armatures/bones/editing/change_layers.html#bpy-ops-armature-armature-layers"), ("bpy.ops.armature.select_linked()*", "animation/armatures/bones/selecting.html#bpy-ops-armature-select-linked"), ("bpy.ops.clip.stabilize_2d_select*", "movie_clip/tracking/clip/selecting.html#bpy-ops-clip-stabilize-2d-select"), @@ -1066,6 +1231,8 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_rollout*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-rollout"), ("bpy.types.editbone.bbone_scalein*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-scalein"), ("bpy.types.editbone.inherit_scale*", "animation/armatures/bones/properties/relations.html#bpy-types-editbone-inherit-scale"), + ("bpy.types.freestylelinestyle.gap*", "render/freestyle/view_layer/line_style/strokes.html#bpy-types-freestylelinestyle-gap"), + ("bpy.types.freestylesettings.mode*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings-mode"), ("bpy.types.geometrynodeconvexhull*", "modeling/geometry_nodes/geometry/convex_hull.html#bpy-types-geometrynodeconvexhull"), ("bpy.types.geometrynodefloattoint*", "modeling/geometry_nodes/utilities/float_to_int.html#bpy-types-geometrynodefloattoint"), ("bpy.types.geometrynodeisviewport*", "modeling/geometry_nodes/input/is_viewport.html#bpy-types-geometrynodeisviewport"), @@ -1077,6 +1244,7 @@ url_manual_mapping = ( ("bpy.types.keyframe.interpolation*", "editors/graph_editor/fcurves/properties.html#bpy-types-keyframe-interpolation"), ("bpy.types.latticegpencilmodifier*", "grease_pencil/modifiers/deform/lattice.html#bpy-types-latticegpencilmodifier"), ("bpy.types.lineartgpencilmodifier*", "grease_pencil/modifiers/generate/line_art.html#bpy-types-lineartgpencilmodifier"), + ("bpy.types.material.line_priority*", "render/freestyle/material.html#bpy-types-material-line-priority"), ("bpy.types.mesh.auto_smooth_angle*", "modeling/meshes/structure.html#bpy-types-mesh-auto-smooth-angle"), ("bpy.types.modifier.show_viewport*", "modeling/modifiers/introduction.html#bpy-types-modifier-show-viewport"), ("bpy.types.object.visible_diffuse*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-diffuse"), @@ -1093,10 +1261,11 @@ url_manual_mapping = ( ("bpy.types.shadernodevectorrotate*", "render/shader_nodes/vector/vector_rotate.html#bpy-types-shadernodevectorrotate"), ("bpy.types.sound.use_memory_cache*", "video_editing/sequencer/sidebar/strip.html#bpy-types-sound-use-memory-cache"), ("bpy.types.spaceview3d.show_gizmo*", "editors/3dview/display/gizmo.html#bpy-types-spaceview3d-show-gizmo"), - ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/color/texture_mapping.html#bpy-types-texturegpencilmodifier"), + ("bpy.types.texturegpencilmodifier*", "grease_pencil/modifiers/modify/texture_mapping.html#bpy-types-texturegpencilmodifier"), ("bpy.types.volumedisplacemodifier*", "modeling/modifiers/deform/volume_displace.html#bpy-types-volumedisplacemodifier"), ("bpy.types.volumerender.step_size*", "modeling/volumes/properties.html#bpy-types-volumerender-step-size"), ("bpy.types.weightednormalmodifier*", "modeling/modifiers/modify/weighted_normal.html#bpy-types-weightednormalmodifier"), + ("bpy.types.worldlighting.distance*", "render/cycles/render_settings/light_paths.html#bpy-types-worldlighting-distance"), ("bpy.ops.armature.autoside_names*", "animation/armatures/bones/editing/naming.html#bpy-ops-armature-autoside-names"), ("bpy.ops.armature.calculate_roll*", "animation/armatures/bones/editing/bone_roll.html#bpy-ops-armature-calculate-roll"), ("bpy.ops.armature.duplicate_move*", "animation/armatures/bones/editing/duplicate.html#bpy-ops-armature-duplicate-move"), @@ -1197,8 +1366,6 @@ url_manual_mapping = ( ("bpy.types.object.visible_shadow*", "render/cycles/object_settings/object_data.html#bpy-types-object-visible-shadow"), ("bpy.types.offsetgpencilmodifier*", "grease_pencil/modifiers/deform/offset.html#bpy-types-offsetgpencilmodifier"), ("bpy.types.posebone.custom_shape*", "animation/armatures/bones/properties/display.html#bpy-types-posebone-custom-shape"), - ("bpy.types.rendersettings.tile_x*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-x"), - ("bpy.types.rendersettings.tile_y*", "render/cycles/render_settings/performance.html#bpy-types-rendersettings-tile-y"), ("bpy.types.rigifyselectioncolors*", "addons/rigging/rigify/metarigs.html#bpy-types-rigifyselectioncolors"), ("bpy.types.sceneeevee.volumetric*", "render/eevee/render_settings/volumetrics.html#bpy-types-sceneeevee-volumetric"), ("bpy.types.screen.show_statusbar*", "interface/window_system/topbar.html#bpy-types-screen-show-statusbar"), @@ -1215,6 +1382,7 @@ url_manual_mapping = ( ("bpy.types.spline.use_endpoint_u*", "modeling/curves/properties/active_spline.html#bpy-types-spline-use-endpoint-u"), ("bpy.types.surfacedeformmodifier*", "modeling/modifiers/deform/surface_deform.html#bpy-types-surfacedeformmodifier"), ("bpy.types.toolsettings.use_snap*", "editors/3dview/controls/snapping.html#bpy-types-toolsettings-use-snap"), + ("bpy.types.viewlayer.use_volumes*", "render/layers/introduction.html#bpy-types-viewlayer-use-volumes"), ("bpy.types.volume.frame_duration*", "modeling/volumes/properties.html#bpy-types-volume-frame-duration"), ("bpy.types.volumedisplay.density*", "modeling/volumes/properties.html#bpy-types-volumedisplay-density"), ("bpy.types.volumerender.clipping*", "modeling/volumes/properties.html#bpy-types-volumerender-clipping"), @@ -1383,6 +1551,7 @@ url_manual_mapping = ( ("bpy.types.hookgpencilmodifier*", "grease_pencil/modifiers/deform/hook.html#bpy-types-hookgpencilmodifier"), ("bpy.types.imageformatsettings*", "files/media/image_formats.html#bpy-types-imageformatsettings"), ("bpy.types.kinematicconstraint*", "animation/constraints/tracking/ik_solver.html#bpy-types-kinematicconstraint"), + ("bpy.types.material.line_color*", "render/freestyle/material.html#bpy-types-material-line-color"), ("bpy.types.mesh.use_paint_mask*", "sculpt_paint/brush/introduction.html#bpy-types-mesh-use-paint-mask"), ("bpy.types.movietrackingcamera*", "movie_clip/tracking/clip/sidebar/track/camera.html#bpy-types-movietrackingcamera"), ("bpy.types.object.display_type*", "scene_layout/object/properties/display.html#bpy-types-object-display-type"), @@ -1408,7 +1577,7 @@ url_manual_mapping = ( ("bpy.types.stretchtoconstraint*", "animation/constraints/tracking/stretch_to.html#bpy-types-stretchtoconstraint"), ("bpy.types.texturenodecurvergb*", "editors/texture_node/types/color/rgb_curves.html#bpy-types-texturenodecurvergb"), ("bpy.types.texturenodevaltorgb*", "editors/texture_node/types/converter/rgb_to_bw.html#bpy-types-texturenodevaltorgb"), - ("bpy.types.timegpencilmodifier*", "grease_pencil/modifiers/deform/time_offset.html#bpy-types-timegpencilmodifier"), + ("bpy.types.timegpencilmodifier*", "grease_pencil/modifiers/modify/time_offset.html#bpy-types-timegpencilmodifier"), ("bpy.types.tintgpencilmodifier*", "grease_pencil/modifiers/color/tint.html#bpy-types-tintgpencilmodifier"), ("bpy.types.transformconstraint*", "animation/constraints/transform/transformation.html#bpy-types-transformconstraint"), ("bpy.types.triangulatemodifier*", "modeling/modifiers/generate/triangulate.html#bpy-types-triangulatemodifier"), @@ -1487,8 +1656,9 @@ url_manual_mapping = ( ("bpy.types.curve.twist_smooth*", "modeling/curves/properties/shape.html#bpy-types-curve-twist-smooth"), ("bpy.types.curvepaintsettings*", "modeling/curves/tools/draw.html#bpy-types-curvepaintsettings"), ("bpy.types.fcurve.array_index*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-array-index"), + ("bpy.types.fileselectidfilter*", "editors/file_browser.html#bpy-types-fileselectidfilter"), ("bpy.types.fmodifiergenerator*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifiergenerator"), - ("bpy.types.freestylelinestyle*", "render/freestyle/parameter_editor/line_style/index.html#bpy-types-freestylelinestyle"), + ("bpy.types.freestylelinestyle*", "render/freestyle/view_layer/line_style/index.html#bpy-types-freestylelinestyle"), ("bpy.types.gammacrosssequence*", "video_editing/sequencer/strips/transitions/gamma_cross.html#bpy-types-gammacrosssequence"), ("bpy.types.geometrynodeswitch*", "modeling/geometry_nodes/utilities/switch.html#bpy-types-geometrynodeswitch"), ("bpy.types.geometrynodeviewer*", "modeling/geometry_nodes/output/viewer.html#bpy-types-geometrynodeviewer"), @@ -1606,7 +1776,7 @@ url_manual_mapping = ( ("bpy.types.fcurve.color_mode*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-color-mode"), ("bpy.types.fluidflowsettings*", "physics/fluid/type/flow.html#bpy-types-fluidflowsettings"), ("bpy.types.fmodifierenvelope*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierenvelope"), - ("bpy.types.freestylesettings*", "render/freestyle/view_layer.html#bpy-types-freestylesettings"), + ("bpy.types.freestylesettings*", "render/freestyle/view_layer/freestyle.html#bpy-types-freestylesettings"), ("bpy.types.geometrynodegroup*", "modeling/geometry_nodes/group.html#bpy-types-geometrynodegroup"), ("bpy.types.gpencillayer.hide*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-hide"), ("bpy.types.gpencillayer.lock*", "grease_pencil/properties/layers.html#bpy-types-gpencillayer-lock"), @@ -1655,6 +1825,7 @@ url_manual_mapping = ( ("bpy.ops.clip.track_markers*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-track-markers"), ("bpy.ops.curve.extrude_move*", "modeling/curves/editing/control_points.html#bpy-ops-curve-extrude-move"), ("bpy.ops.curve.make_segment*", "modeling/curves/editing/control_points.html#bpy-ops-curve-make-segment"), + ("bpy.ops.file.directory_new*", "editors/file_browser.html#bpy-ops-file-directory-new"), ("bpy.ops.graph.euler_filter*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-euler-filter"), ("bpy.ops.marker.camera_bind*", "animation/markers.html#bpy-ops-marker-camera-bind"), ("bpy.ops.mask.select_circle*", "movie_clip/masking/selecting.html#bpy-ops-mask-select-circle"), @@ -1709,8 +1880,9 @@ url_manual_mapping = ( ("bpy.types.editbone.bbone_x*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-x"), ("bpy.types.editbone.bbone_z*", "animation/armatures/bones/properties/bendy_bones.html#bpy-types-editbone-bbone-z"), ("bpy.types.fcurve.data_path*", "editors/graph_editor/fcurves/properties.html#bpy-types-fcurve-data-path"), + ("bpy.types.fileselectparams*", "editors/file_browser.html#bpy-types-fileselectparams"), ("bpy.types.fmodifierstepped*", "editors/graph_editor/fcurves/modifiers.html#bpy-types-fmodifierstepped"), - ("bpy.types.freestylelineset*", "render/freestyle/parameter_editor/line_set.html#bpy-types-freestylelineset"), + ("bpy.types.freestylelineset*", "render/freestyle/view_layer/line_set.html#bpy-types-freestylelineset"), ("bpy.types.mask.frame_start*", "movie_clip/masking/sidebar.html#bpy-types-mask-frame-start"), ("bpy.types.mesh.*customdata*", "modeling/meshes/properties/custom_data.html#bpy-types-mesh-customdata"), ("bpy.types.multicamsequence*", "video_editing/sequencer/strips/effects/multicam.html#bpy-types-multicamsequence"), @@ -1739,6 +1911,7 @@ url_manual_mapping = ( ("bpy.types.softbodymodifier*", "physics/soft_body/index.html#bpy-types-softbodymodifier"), ("bpy.types.softbodysettings*", "physics/soft_body/settings/index.html#bpy-types-softbodysettings"), ("bpy.types.solidifymodifier*", "modeling/modifiers/generate/solidify.html#bpy-types-solidifymodifier"), + ("bpy.types.spacefilebrowser*", "editors/file_browser.html#bpy-types-spacefilebrowser"), ("bpy.types.spacegrapheditor*", "editors/graph_editor/index.html#bpy-types-spacegrapheditor"), ("bpy.types.spacepreferences*", "editors/preferences/index.html#bpy-types-spacepreferences"), ("bpy.types.spacespreadsheet*", "editors/spreadsheet.html#bpy-types-spacespreadsheet"), @@ -1758,6 +1931,7 @@ url_manual_mapping = ( ("bpy.ops.clip.solve_camera*", "movie_clip/tracking/clip/editing/track.html#bpy-ops-clip-solve-camera"), ("bpy.ops.constraint.delete*", "animation/constraints/interface/header.html#bpy-ops-constraint-delete"), ("bpy.ops.curve.smooth_tilt*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth-tilt"), + ("bpy.ops.file.reset_recent*", "editors/file_browser.html#bpy-ops-file-reset-recent"), ("bpy.ops.fluid.bake_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-bake-guides"), ("bpy.ops.fluid.free_guides*", "physics/fluid/type/domain/guides.html#bpy-ops-fluid-free-guides"), ("bpy.ops.font.style_toggle*", "modeling/texts/editing.html#bpy-ops-font-style-toggle"), @@ -2005,6 +2179,7 @@ url_manual_mapping = ( ("bpy.types.viewlayer.use*", "render/layers/view_layer.html#bpy-types-viewlayer-use"), ("bpy.types.volumedisplay*", "modeling/volumes/properties.html#bpy-types-volumedisplay"), ("bpy.types.windowmanager*", "interface/index.html#bpy-types-windowmanager"), + ("bpy.types.worldlighting*", "render/cycles/world_settings.html#bpy-types-worldlighting"), ("bpy.ops.*.select_lasso*", "interface/selecting.html#bpy-ops-select-lasso"), ("bpy.ops.armature.align*", "animation/armatures/bones/editing/transform.html#bpy-ops-armature-align"), ("bpy.ops.armature.split*", "animation/armatures/bones/editing/split.html#bpy-ops-armature-split"), @@ -2086,6 +2261,7 @@ url_manual_mapping = ( ("bpy.ops.clip.prefetch*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-prefetch"), ("bpy.ops.clip.set_axis*", "movie_clip/tracking/clip/editing/reconstruction.html#bpy-ops-clip-set-axis"), ("bpy.ops.file.pack_all*", "files/blend/packed_data.html#bpy-ops-file-pack-all"), + ("bpy.ops.file.previous*", "editors/file_browser.html#bpy-ops-file-previous"), ("bpy.ops.gpencil.paste*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-paste"), ("bpy.ops.image.project*", "sculpt_paint/texture_paint/tool_settings/options.html#bpy-ops-image-project"), ("bpy.ops.image.replace*", "editors/image/editing.html#bpy-ops-image-replace"), @@ -2134,6 +2310,8 @@ url_manual_mapping = ( ("bpy.ops.curve.delete*", "modeling/curves/editing/curve.html#bpy-ops-curve-delete"), ("bpy.ops.curve.reveal*", "modeling/curves/editing/curve.html#bpy-ops-curve-reveal"), ("bpy.ops.curve.smooth*", "modeling/curves/editing/control_points.html#bpy-ops-curve-smooth"), + ("bpy.ops.file.execute*", "editors/file_browser.html#bpy-ops-file-execute"), + ("bpy.ops.file.refresh*", "editors/file_browser.html#bpy-ops-file-refresh"), ("bpy.ops.fluid.preset*", "physics/fluid/type/domain/liquid/diffusion.html#bpy-ops-fluid-preset"), ("bpy.ops.gpencil.copy*", "grease_pencil/modes/edit/grease_pencil_menu.html#bpy-ops-gpencil-copy"), ("bpy.ops.graph.delete*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-delete"), @@ -2175,6 +2353,8 @@ url_manual_mapping = ( ("bpy.types.vectorfont*", "modeling/texts/index.html#bpy-types-vectorfont"), ("bpy.ops.clip.reload*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-reload"), ("bpy.ops.curve.split*", "modeling/curves/editing/curve.html#bpy-ops-curve-split"), + ("bpy.ops.file.cancel*", "editors/file_browser.html#bpy-ops-file-cancel"), + ("bpy.ops.file.parent*", "editors/file_browser.html#bpy-ops-file-parent"), ("bpy.ops.graph.clean*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-clean"), ("bpy.ops.graph.paste*", "editors/graph_editor/fcurves/editing.html#bpy-ops-graph-paste"), ("bpy.ops.mesh.bisect*", "modeling/meshes/editing/mesh/bisect.html#bpy-ops-mesh-bisect"), @@ -2240,7 +2420,7 @@ url_manual_mapping = ( ("bpy.types.editbone*", "animation/armatures/bones/editing/index.html#bpy-types-editbone"), ("bpy.types.facemaps*", "modeling/meshes/properties/object_data.html#bpy-types-facemaps"), ("bpy.types.keyframe*", "animation/keyframes/index.html#bpy-types-keyframe"), - ("bpy.types.linesets*", "render/freestyle/parameter_editor/line_set.html#bpy-types-linesets"), + ("bpy.types.linesets*", "render/freestyle/view_layer/line_set.html#bpy-types-linesets"), ("bpy.types.metaball*", "modeling/metas/index.html#bpy-types-metaball"), ("bpy.types.modifier*", "modeling/modifiers/index.html#bpy-types-modifier"), ("bpy.types.nlastrip*", "editors/nla/strips.html#bpy-types-nlastrip"), @@ -2254,6 +2434,7 @@ url_manual_mapping = ( ("bpy.types.spacenla*", "editors/nla/index.html#bpy-types-spacenla"), ("bpy.types.sunlight*", "render/lights/light_object.html#bpy-types-sunlight"), ("bpy.ops.clip.open*", "movie_clip/tracking/clip/editing/clip.html#bpy-ops-clip-open"), + ("bpy.ops.file.next*", "editors/file_browser.html#bpy-ops-file-next"), ("bpy.ops.image.new*", "editors/image/editing.html#bpy-ops-image-new"), ("bpy.ops.mesh.fill*", "modeling/meshes/editing/face/fill.html#bpy-ops-mesh-fill"), ("bpy.ops.mesh.poke*", "modeling/meshes/editing/face/poke_faces.html#bpy-ops-mesh-poke"), @@ -2364,6 +2545,7 @@ url_manual_mapping = ( ("bpy.ops.anim*", "animation/index.html#bpy-ops-anim"), ("bpy.ops.boid*", "physics/particles/emitter/physics/boids.html#bpy-ops-boid"), ("bpy.ops.clip*", "movie_clip/index.html#bpy-ops-clip"), + ("bpy.ops.file*", "editors/file_browser.html#bpy-ops-file"), ("bpy.ops.font*", "modeling/texts/index.html#bpy-ops-font"), ("bpy.ops.mask*", "movie_clip/masking/index.html#bpy-ops-mask"), ("bpy.ops.mesh*", "modeling/meshes/index.html#bpy-ops-mesh"), diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index 26a2f9ad89b..fce59a26c38 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -21,13 +21,14 @@ import bpy from mathutils import Vector +from bpy.types import bpy_prop_array from idprop.types import IDPropertyArray, IDPropertyGroup -ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector) +ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector, bpy_prop_array) # Maximum length of an array property for which a multi-line # edit field will be displayed in the Custom Properties panel. -MAX_DISPLAY_ROWS = 4 +MAX_DISPLAY_ROWS = 8 def rna_idprop_quote_path(prop): @@ -133,18 +134,7 @@ def rna_idprop_ui_create( def draw(layout, context, context_member, property_type, *, use_edit=True): - - def assign_props(prop, val, key): - prop.data_path = context_member - prop.property = key - - try: - prop.value = str(val) - except: - pass - rna_item, context_member = rna_idprop_context_value(context, context_member, property_type) - # poll should really get this... if not rna_item: return @@ -163,82 +153,72 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): # TODO: Allow/support adding new custom props to overrides. if use_edit and not is_lib_override: row = layout.row() - props = row.operator("wm.properties_add", text="Add") + props = row.operator("wm.properties_add", text="New", icon='ADD') props.data_path = context_member del row + layout.separator() show_developer_ui = context.preferences.view.show_developer_ui rna_properties = {prop.identifier for prop in rna_item.bl_rna.properties if prop.is_runtime} if items else None - layout.use_property_split = True - layout.use_property_decorate = False # No animation. + layout.use_property_decorate = False - flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) - - for key, val in items: + for key, value in items: is_rna = (key in rna_properties) - # only show API defined for developers + # Only show API defined properties to developers. if is_rna and not show_developer_ui: continue - to_dict = getattr(val, "to_dict", None) - to_list = getattr(val, "to_list", None) + to_dict = getattr(value, "to_dict", None) + to_list = getattr(value, "to_list", None) - # val_orig = val # UNUSED if to_dict: - val = to_dict() - val_draw = str(val) + value = to_dict() elif to_list: - val = to_list() - val_draw = str(val) - else: - val_draw = val + value = to_list() - row = layout.row(align=True) - box = row.box() + split = layout.split(factor=0.4, align=True) + label_row = split.row() + label_row.alignment = 'RIGHT' + label_row.label(text=key, translate=False) + + value_row = split.row(align=True) + value_column = value_row.column(align=True) + + is_long_array = to_list and len(value) >= MAX_DISPLAY_ROWS + + if is_rna: + value_column.prop(rna_item, key, text="") + elif to_dict or is_long_array: + props = value_column.operator("wm.properties_edit_value", text="Edit Value") + props.data_path = context_member + props.property_name = key + else: + value_column.prop(rna_item, '["%s"]' % escape_identifier(key), text="") + + operator_row = value_row.row() + + # Do not allow editing of overridden properties (we cannot use a poll function + # of the operators here since they's have no access to the specific property). + operator_row.enabled = not(is_lib_override and key in rna_item.id_data.override_library.reference) if use_edit: - split = box.split(factor=0.75) - row = split.row() - else: - split = box.split(factor=1.00) - row = split.row() - - row.alignment = 'RIGHT' - - row.label(text=key, translate=False) - - # explicit exception for arrays. - show_array_ui = to_list and not is_rna and 0 < len(val) <= MAX_DISPLAY_ROWS - - if show_array_ui and isinstance(val[0], (int, float)): - row.prop(rna_item, '["%s"]' % escape_identifier(key), text="") - elif to_dict or to_list: - row.label(text=val_draw, translate=False) - else: if is_rna: - row.prop(rna_item, key, text="") - else: - row.prop(rna_item, '["%s"]' % escape_identifier(key), text="") - - if use_edit: - row = split.row(align=True) - # Do not allow editing of overridden properties (we cannot use a poll function of the operators here - # since they's have no access to the specific property...). - row.enabled = not(is_lib_override and key in rna_item.id_data.override_library.reference) - if is_rna: - row.label(text="API Defined") + operator_row.label(text="API Defined") elif is_lib_override: - row.label(text="Library Override") + operator_row.active = False + operator_row.label(text="", icon='DECORATE_LIBRARY_OVERRIDE') else: - props = row.operator("wm.properties_edit", text="Edit") - assign_props(props, val_draw, key) - props = row.operator("wm.properties_remove", text="", icon='REMOVE') - assign_props(props, val_draw, key) - - del flow - + props = operator_row.operator("wm.properties_edit", text="", icon='PREFERENCES', emboss=False) + props.data_path = context_member + props.property_name = key + props = operator_row.operator("wm.properties_remove", text="", icon='X', emboss=False) + props.data_path = context_member + props.property_name = key + else: + # Add some spacing, so the right side of the buttons line up with layouts with decorators. + operator_row.label(text="", icon='BLANK1') class PropertyPanel: """ diff --git a/release/scripts/presets/cycles/sampling/Final.py b/release/scripts/presets/cycles/sampling/Final.py index f1222d927c1..f3626c4b778 100644 --- a/release/scripts/presets/cycles/sampling/Final.py +++ b/release/scripts/presets/cycles/sampling/Final.py @@ -1,18 +1,12 @@ import bpy cycles = bpy.context.scene.cycles -# Path Trace -cycles.samples = 512 -cycles.preview_samples = 128 - -# Branched Path Trace -cycles.aa_samples = 128 -cycles.preview_aa_samples = 32 - -cycles.diffuse_samples = 4 -cycles.glossy_samples = 4 -cycles.transmission_samples = 4 -cycles.ao_samples = 1 -cycles.mesh_light_samples = 4 -cycles.subsurface_samples = 4 -cycles.volume_samples = 4 +cycles.use_adaptive_sampling = True +cycles.adaptive_threshold = 0.01 +cycles.samples = 4096 +cycles.adaptive_min_samples = 0 +cycles.time_limit = 0.0 +cycles.use_denoising = True +cycles.denoiser = 'OPENIMAGEDENOISE' +cycles.denoising_input_passes = 'RGB_ALBEDO_NORMAL' +cycles.denoising_prefilter = 'ACCURATE' diff --git a/release/scripts/presets/cycles/sampling/Preview.py b/release/scripts/presets/cycles/sampling/Preview.py index c16449e2c8f..66aa9339063 100644 --- a/release/scripts/presets/cycles/sampling/Preview.py +++ b/release/scripts/presets/cycles/sampling/Preview.py @@ -1,18 +1,12 @@ import bpy cycles = bpy.context.scene.cycles -# Path Trace -cycles.samples = 128 -cycles.preview_samples = 32 - -# Branched Path Trace -cycles.aa_samples = 32 -cycles.preview_aa_samples = 4 - -cycles.diffuse_samples = 4 -cycles.glossy_samples = 4 -cycles.transmission_samples = 4 -cycles.ao_samples = 1 -cycles.mesh_light_samples = 4 -cycles.subsurface_samples = 4 -cycles.volume_samples = 4 +cycles.use_adaptive_sampling = True +cycles.adaptive_threshold = 0.1 +cycles.samples = 1024 +cycles.adaptive_min_samples = 0 +cycles.time_limit = 0.0 +cycles.use_denoising = True +cycles.denoiser = 'OPENIMAGEDENOISE' +cycles.denoising_input_passes = 'RGB_ALBEDO_NORMAL' +cycles.denoising_prefilter = 'ACCURATE' diff --git a/release/scripts/presets/cycles/viewport_sampling/Final.py b/release/scripts/presets/cycles/viewport_sampling/Final.py new file mode 100644 index 00000000000..b2cb6bfe90a --- /dev/null +++ b/release/scripts/presets/cycles/viewport_sampling/Final.py @@ -0,0 +1,11 @@ +import bpy +cycles = bpy.context.scene.cycles + +cycles.use_preview_adaptive_sampling = True +cycles.preview_adaptive_threshold = 0.01 +cycles.preview_samples = 4096 +cycles.preview_adaptive_min_samples = 0 +cycles.use_preview_denoising = True +cycles.preview_denoiser = 'OPENIMAGEDENOISE' +cycles.preview_denoising_input_passes = 'RGB_ALBEDO_NORMAL' +cycles.preview_denoising_prefilter = 'ACCURATE' diff --git a/release/scripts/presets/cycles/viewport_sampling/Preview.py b/release/scripts/presets/cycles/viewport_sampling/Preview.py new file mode 100644 index 00000000000..f8319b70d4a --- /dev/null +++ b/release/scripts/presets/cycles/viewport_sampling/Preview.py @@ -0,0 +1,11 @@ +import bpy +cycles = bpy.context.scene.cycles + +cycles.use_preview_adaptive_sampling = True +cycles.preview_adaptive_threshold = 0.1 +cycles.preview_samples = 1024 +cycles.preview_adaptive_min_samples = 0 +cycles.use_preview_denoising = False +cycles.preview_denoiser = 'AUTO' +cycles.preview_denoising_input_passes = 'RGB_ALBEDO' +cycles.preview_denoising_prefilter = 'FAST' diff --git a/release/scripts/presets/interface_theme/Blender_Light.xml b/release/scripts/presets/interface_theme/Blender_Light.xml index e75d4334582..e3ac77b008d 100644 --- a/release/scripts/presets/interface_theme/Blender_Light.xml +++ b/release/scripts/presets/interface_theme/Blender_Light.xml @@ -9,6 +9,7 @@ widget_emboss="#00000026" editor_outline="#1f1f1f" widget_text_cursor="#3399e6" + panel_roundness="0.4" transparent_checker_primary="#333333" transparent_checker_secondary="#262626" transparent_checker_size="8" @@ -433,7 +434,7 @@ button="#99999900" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#b3b3b3" @@ -508,7 +509,7 @@ button="#999999e6" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -554,7 +555,7 @@ button="#999999e6" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#999999e6" tab_active="#6697e6" @@ -612,7 +613,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -687,7 +688,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -772,7 +773,7 @@ button="#99999900" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#b3b3b3" @@ -832,7 +833,7 @@ button="#99999900" button_title="#1a1a1a" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#b3b3b3" @@ -869,7 +870,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#656565ff" execution_buts="#00000000" tab_active="#6697e6" @@ -916,7 +917,7 @@ button="#7272727f" button_title="#000000" button_text="#000000" - button_text_hi="#ffffff" + button_text_hi="#000000" navigation_bar="#00000000" execution_buts="#00000000" tab_active="#6697e6" @@ -938,10 +939,10 @@ 3.0. - if version_new == 300: - version_new = 294 - version_old = 293 - else: - print("TODO: remove exception!") - # End special case. - # Ensure we only try to copy files from a point release. # The check below ensures the second numbers match. while (version_new % 100) // 10 == (version_old % 100) // 10: @@ -268,7 +260,7 @@ class PREFERENCES_OT_keyconfig_export(Operator): ) filepath: StringProperty( subtype='FILE_PATH', - default="keymap.py", + default="", ) filter_folder: BoolProperty( name="Filter folders", @@ -307,7 +299,13 @@ class PREFERENCES_OT_keyconfig_export(Operator): return {'FINISHED'} def invoke(self, context, _event): + import os wm = context.window_manager + if not self.filepath: + self.filepath = os.path.join( + os.path.expanduser("~"), + bpy.path.display_name_to_filepath(wm.keyconfigs.active.name) + ".py", + ) wm.fileselect_add(self) return {'RUNNING_MODAL'} diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 6af86e75b8a..28bb0a58c02 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -23,6 +23,7 @@ import bpy from bpy.types import ( Menu, Operator, + bpy_prop_array, ) from bpy.props import ( BoolProperty, @@ -31,6 +32,8 @@ from bpy.props import ( FloatProperty, IntProperty, StringProperty, + IntVectorProperty, + FloatVectorProperty, ) from bpy.app.translations import pgettext_iface as iface_ @@ -976,6 +979,12 @@ class WM_OT_url_open(Operator): return {'FINISHED'} +# NOTE: needed for Python 3.10 since there are name-space issues with annotations. +# This can be moved into the class as a static-method once Python 3.9x is dropped. +def _wm_url_open_preset_type_items(_self, _context): + return [item for (item, _) in WM_OT_url_open_preset.preset_items] + + class WM_OT_url_open_preset(Operator): """Open a preset website in the web browser""" bl_idname = "wm.url_open_preset" @@ -984,9 +993,7 @@ class WM_OT_url_open_preset(Operator): type: EnumProperty( name="Site", - items=lambda self, _context: ( - item for (item, _) in WM_OT_url_open_preset.preset_items - ), + items=_wm_url_open_preset_type_items, ) id: StringProperty( @@ -1266,48 +1273,20 @@ rna_path = StringProperty( options={'HIDDEN'}, ) -rna_value = StringProperty( - name="Property Value", - description="Property value edit", - maxlen=1024, -) - -rna_default = StringProperty( - name="Default Value", - description="Default value of the property. Important for NLA mixing", - maxlen=1024, -) - -rna_custom_property = StringProperty( +rna_custom_property_name = StringProperty( name="Property Name", description="Property name edit", # Match `MAX_IDPROP_NAME - 1` in Blender's source. maxlen=63, ) -rna_min = FloatProperty( - name="Min", - description="Minimum value of the property", - default=-10000.0, - precision=3, -) - -rna_max = FloatProperty( - name="Max", - description="Maximum value of the property", - default=10000.0, - precision=3, -) - -rna_use_soft_limits = BoolProperty( - name="Use Soft Limits", - description="Limits the Property Value slider to a range, values outside the range must be inputted numerically", -) - -rna_is_overridable_library = BoolProperty( - name="Is Library Overridable", - description="Allow the property to be overridden when the data-block is linked", - default=False, +rna_custom_property_type_items = ( + ('FLOAT', "Float", "A single floating-point value"), + ('FLOAT_ARRAY', "Float Array", "An array of floating-point values"), + ('INT', "Integer", "A single integer"), + ('INT_ARRAY', "Integer Array", "An array of integers"), + ('STRING', "String", "A string value"), + ('PYTHON', "Python", "Edit a python value directly, for unsupported property types"), ) # Most useful entries of rna_enum_property_subtype_items for number arrays: @@ -1320,34 +1299,200 @@ rna_vector_subtype_items = ( ) +# NOTE: needed for Python 3.10 since there are name-space issues with annotations. +# This can be moved into the class as a static-method once Python 3.9x is dropped. +def _wm_properties_edit_subtype_items(_self, _context): + return WM_OT_properties_edit.subtype_items + + class WM_OT_properties_edit(Operator): - """Edit the attributes of the property""" + """Change a custom property's type, or adjust how it is displayed in the interface""" bl_idname = "wm.properties_edit" bl_label = "Edit Property" # register only because invoke_props_popup requires. bl_options = {'REGISTER', 'INTERNAL'} + # Common settings used for all property types. Generally, separate properties are used for each + # type to improve the experience when choosing UI data values. + data_path: rna_path - property: rna_custom_property - value: rna_value - default: rna_default - min: rna_min - max: rna_max - use_soft_limits: rna_use_soft_limits - is_overridable_library: rna_is_overridable_library - soft_min: rna_min - soft_max: rna_max + property_name: rna_custom_property_name + property_type: EnumProperty( + name="Type", + items=rna_custom_property_type_items, + ) + is_overridable_library: BoolProperty( + name="Is Library Overridable", + description="Allow the property to be overridden when the data-block is linked", + default=False, + ) description: StringProperty( - name="Tooltip", + name="Description", + ) + + # Shared for integer and string properties. + + use_soft_limits: BoolProperty( + name="Use Soft Limits", + description=( + "Limits the Property Value slider to a range, " + "values outside the range must be inputted numerically" + ), + ) + array_length: IntProperty( + name="Array Length", + default=3, + min=1, + max=32, # 32 is the maximum size for RNA array properties. + ) + + # Integer properties. + + # This property stores values for both array and non-array properties. + default_int: IntVectorProperty( + name="Default Value", + size=32, + ) + min_int: IntProperty( + name="Min", + default=-10000, + ) + max_int: IntProperty( + name="Max", + default=10000, + ) + soft_min_int: IntProperty( + name="Soft Min", + default=-10000, + ) + soft_max_int: IntProperty( + name="Soft Max", + default=10000, + ) + step_int: IntProperty( + name="Step", + min=1, + default=1, + ) + + # Float properties. + + # This property stores values for both array and non-array properties. + default_float: FloatVectorProperty( + name="Default Value", + size=32, + ) + min_float: FloatProperty( + name="Min", + default=-10000.0, + ) + max_float: FloatProperty( + name="Max", + default=-10000.0, + ) + soft_min_float: FloatProperty( + name="Soft Min", + default=-10000.0, + ) + soft_max_float: FloatProperty( + name="Soft Max", + default=-10000.0, + ) + precision: IntProperty( + name="Precision", + default=3, + min=0, + max=8, + ) + step_float: FloatProperty( + name="Step", + default=0.1, + min=0.001, ) subtype: EnumProperty( name="Subtype", - items=lambda self, _context: WM_OT_properties_edit.subtype_items, + items=_wm_properties_edit_subtype_items, ) + # String properties. + + default_string: StringProperty( + name="Default Value", + maxlen=1024, + ) + + # Store the value converted to a string as a fallback for otherwise unsupported types. + eval_string: StringProperty( + name="Value", + description="Python value for unsupported custom property types" + ) + + type_items = rna_custom_property_type_items subtype_items = rna_vector_subtype_items - def _init_subtype(self, prop_type, is_array, subtype): + # Helper method to avoid repetative code to retrieve a single value from sequences and non-sequences. + @staticmethod + def _convert_new_value_single(old_value, new_type): + if hasattr(old_value, "__len__") and len(old_value) > 0: + return new_type(old_value[0]) + return new_type(old_value) + + # Helper method to create a list of a given value and type, using a sequence or non-sequence old value. + @staticmethod + def _convert_new_value_array(old_value, new_type, new_len): + if hasattr(old_value, "__len__"): + new_array = [new_type()] * new_len + for i in range(min(len(old_value), new_len)): + new_array[i] = new_type(old_value[i]) + return new_array + return [new_type(old_value)] * new_len + + # Convert an old property for a string, avoiding unhelpful string representations for custom list types. + @staticmethod + def convert_custom_property_to_string(item, name): + # The IDProperty group view API currently doesn't have a "lookup" method. + for key, value in item.items(): + if key == name: + old_value = value + break + + # In order to get a better string conversion, convert the property to a builtin sequence type first. + to_dict = getattr(old_value, "to_dict", None) + to_list = getattr(old_value, "to_list", None) + if to_dict: + old_value = to_dict() + elif to_list: + old_value = to_list() + + return str(old_value) + + # Retrieve the current type of the custom property on the RNA struct. Some properties like group properties + # can be created in the UI, but editing their meta-data isn't supported. In that case, return 'PYTHON'. + @staticmethod + def get_property_type(item, property_name): + from rna_prop_ui import ( + rna_idprop_value_item_type, + ) + + prop_value = item[property_name] + + prop_type, is_array = rna_idprop_value_item_type(prop_value) + if prop_type == int: + if is_array: + return 'INT_ARRAY' + return 'INT' + elif prop_type == float: + if is_array: + return 'FLOAT_ARRAY' + return 'FLOAT' + elif prop_type == str: + if is_array: + return 'PYTHON' + return 'STRING' + + return 'PYTHON' + + def _init_subtype(self, subtype): subtype = subtype or 'NONE' subtype_items = rna_vector_subtype_items @@ -1358,121 +1503,139 @@ class WM_OT_properties_edit(Operator): WM_OT_properties_edit.subtype_items = subtype_items self.subtype = subtype - def _cmp_props_get(self): - # Changing these properties will refresh the UI - return { - "use_soft_limits": self.use_soft_limits, - "soft_range": (self.soft_min, self.soft_max), - "hard_range": (self.min, self.max), - } + # Fill the operator's properties with the UI data properties from the existing custom property. + # Note that if the UI data doesn't exist yet, the access will create it and use those default values. + def _fill_old_ui_data(self, item, name): + ui_data = item.id_properties_ui(name) + rna_data = ui_data.as_dict() - def get_value_eval(self): - failed = False - try: - value_eval = eval(self.value) - # assert else None -> None, not "None", see T33431. - assert(type(value_eval) in {str, float, int, bool, tuple, list}) - except: - failed = True - value_eval = self.value + if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}: + self.min_float = rna_data["min"] + self.max_float = rna_data["max"] + self.soft_min_float = rna_data["soft_min"] + self.soft_max_float = rna_data["soft_max"] + self.precision = rna_data["precision"] + self.step_float = rna_data["step"] + self.subtype = rna_data["subtype"] + self.use_soft_limits = ( + self.min_float != self.soft_min_float or + self.max_float != self.soft_max_float + ) + default = self._convert_new_value_array(rna_data["default"], float, 32) + self.default_float = default if isinstance(default, list) else [default] * 32 + elif self.property_type in {'INT', 'INT_ARRAY'}: + self.min_int = rna_data["min"] + self.max_int = rna_data["max"] + self.soft_min_int = rna_data["soft_min"] + self.soft_max_int = rna_data["soft_max"] + self.step_int = rna_data["step"] + self.use_soft_limits = ( + self.min_int != self.soft_min_int or + self.max_int != self.soft_max_int + ) + self.default_int = self._convert_new_value_array(rna_data["default"], int, 32) + elif self.property_type == 'STRING': + self.default_string = rna_data["default"] - return value_eval, failed + if self.property_type in { 'FLOAT_ARRAY', 'INT_ARRAY'}: + self.array_length = len(item[name]) - def get_default_eval(self): - failed = False - try: - default_eval = eval(self.default) - # assert else None -> None, not "None", see T33431. - assert(type(default_eval) in {str, float, int, bool, tuple, list}) - except: - failed = True - default_eval = self.default + # The dictionary does not contain the description if it was empty. + self.description = rna_data.get("description", "") - return default_eval, failed + self._init_subtype(self.subtype) + escaped_name = bpy.utils.escape_identifier(name) + self.is_overridable_library = bool(item.is_property_overridable_library('["%s"]' % escaped_name)) - def execute(self, context): + # When the operator chooses a different type than the original property, + # attempt to convert the old value to the new type for continuity and speed. + def _get_converted_value(self, item, name_old, prop_type_new): + if prop_type_new == 'INT': + return self._convert_new_value_single(item[name_old], int) + + if prop_type_new == 'FLOAT': + return self._convert_new_value_single(item[name_old], float) + + if prop_type_new == 'INT_ARRAY': + prop_type_old = self.get_property_type(item, name_old) + if prop_type_old in {'INT', 'FLOAT', 'INT_ARRAY', 'FLOAT_ARRAY'}: + return self._convert_new_value_array(item[name_old], int, self.array_length) + + if prop_type_new == 'FLOAT_ARRAY': + prop_type_old = self.get_property_type(item, name_old) + if prop_type_old in {'INT', 'FLOAT', 'FLOAT_ARRAY', 'INT_ARRAY'}: + return self._convert_new_value_array(item[name_old], float, self.array_length) + + if prop_type_new == 'STRING': + return self.convert_custom_property_to_string(item, name_old) + + # If all else fails, create an empty string property. That should avoid errors later on anyway. + return "" + + # Any time the target type is changed in the dialog, it's helpful to convert the UI data values + # to the new type as well, when possible, currently this only applies for floats and ints. + def _convert_old_ui_data_to_new_type(self, prop_type_old, prop_type_new): + if prop_type_new in {'INT', 'INT_ARRAY'} and prop_type_old in {'FLOAT', 'FLOAT_ARRAY'}: + self.min_int = int(self.min_float) + self.max_int = int(self.max_float) + self.soft_min_int = int(self.soft_min_float) + self.soft_max_int = int(self.soft_max_float) + self.default_int = self._convert_new_value_array(self.default_float, int, 32) + elif prop_type_new in {'FLOAT', 'FLOAT_ARRAY'} and prop_type_old in {'INT', 'INT_ARRAY'}: + self.min_float = float(self.min_int) + self.max_float = float(self.max_int) + self.soft_min_float = float(self.soft_min_int) + self.soft_max_float = float(self.soft_max_int) + self.default_float = self._convert_new_value_array(self.default_int, float, 32) + # Don't convert between string and float/int defaults here, it's not expected like the other conversions. + + # Fill the property's UI data with the values chosen in the operator. + def _create_ui_data_for_new_prop(self, item, name, prop_type_new): + if prop_type_new in {'INT', 'INT_ARRAY'}: + ui_data = item.id_properties_ui(name) + ui_data.update( + min=self.min_int, + max=self.max_int, + soft_min=self.soft_min_int if self.use_soft_limits else self.min_int, + soft_max=self.soft_max_int if self.use_soft_limits else self.max_int, + step=self.step_int, + default=self.default_int[0] if prop_type_new == 'INT' else self.default_int[:self.array_length], + description=self.description, + ) + elif prop_type_new in {'FLOAT', 'FLOAT_ARRAY'}: + ui_data = item.id_properties_ui(name) + ui_data.update( + min=self.min_float, + max=self.max_float, + soft_min=self.soft_min_float if self.use_soft_limits else self.min_float, + soft_max=self.soft_max_float if self.use_soft_limits else self.max_float, + step=self.step_float, + precision=self.precision, + default=self.default_float[0] if prop_type_new == 'FLOAT' else self.default_float[:self.array_length], + description=self.description, + subtype=self.subtype, + ) + elif prop_type_new == 'STRING': + ui_data = item.id_properties_ui(name) + ui_data.update( + default=self.default_string, + description=self.description, + ) + + escaped_name = bpy.utils.escape_identifier(name) + item.property_overridable_library_set('["%s"]' % escaped_name, self.is_overridable_library) + + def _update_blender_for_prop_change(self, context, item, name, prop_type_old, prop_type_new): from rna_prop_ui import ( rna_idprop_ui_prop_update, - rna_idprop_value_item_type, ) - data_path = self.data_path - prop = self.property - prop_escape = bpy.utils.escape_identifier(prop) - - prop_old = getattr(self, "_last_prop", [None])[0] - - if prop_old is None: - self.report({'ERROR'}, "Direct execution not supported") - return {'CANCELLED'} - - value_eval, value_failed = self.get_value_eval() - default_eval, default_failed = self.get_default_eval() - - # First remove - item = eval("context.%s" % data_path) - - if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference): - self.report({'ERROR'}, "Cannot edit properties from override data") - return {'CANCELLED'} - - prop_type_old = type(item[prop_old]) - - # Deleting the property will also remove the UI data. - del item[prop_old] - - # Reassign - item[prop] = value_eval - item.property_overridable_library_set('["%s"]' % prop_escape, self.is_overridable_library) - rna_idprop_ui_prop_update(item, prop) - - self._last_prop[:] = [prop] - - prop_value = item[prop] - prop_type_new = type(prop_value) - prop_type, is_array = rna_idprop_value_item_type(prop_value) - - if prop_type == int: - ui_data = item.id_properties_ui(prop) - if type(default_eval) == str: - self.report({'WARNING'}, "Could not evaluate number from default value") - default_eval = None - elif hasattr(default_eval, "__len__"): - default_eval = [int(round(value)) for value in default_eval] - ui_data.update( - min=int(round(self.min)), - max=int(round(self.max)), - soft_min=int(round(self.soft_min)), - soft_max=int(round(self.soft_max)), - default=default_eval, - subtype=self.subtype, - description=self.description - ) - elif prop_type == float: - ui_data = item.id_properties_ui(prop) - if type(default_eval) == str: - self.report({'WARNING'}, "Could not evaluate number from default value") - default_eval = None - ui_data.update( - min=self.min, - max=self.max, - soft_min=self.soft_min, - soft_max=self.soft_max, - default=default_eval, - subtype=self.subtype, - description=self.description - ) - elif prop_type == str and not is_array and not default_failed: # String arrays do not support UI data. - ui_data = item.id_properties_ui(prop) - ui_data.update( - default=self.default, - subtype=self.subtype, - description=self.description - ) + rna_idprop_ui_prop_update(item, name) # If we have changed the type of the property, update its potential anim curves! if prop_type_old != prop_type_new: - data_path = '["%s"]' % prop_escape + escaped_name = bpy.utils.escape_identifier(name) + data_path = '["%s"]' % escaped_name done = set() def _update(fcurves): @@ -1498,142 +1661,252 @@ class WM_OT_properties_edit(Operator): for nt in adt.nla_tracks: _update_strips(nt.strips) - # Otherwise existing buttons which reference freed - # memory may crash Blender T26510. - # context.area.tag_redraw() + # Otherwise existing buttons which reference freed memory may crash Blender (T26510). for win in context.window_manager.windows: for area in win.screen.areas: area.tag_redraw() - return {'FINISHED'} - - def invoke(self, context, _event): - from rna_prop_ui import ( - rna_idprop_value_to_python, - rna_idprop_value_item_type - ) - - prop = self.property - prop_escape = bpy.utils.escape_identifier(prop) - - data_path = self.data_path - - if not data_path: - self.report({'ERROR'}, "Data path not set") + def execute(self, context): + name_old = getattr(self, "_old_prop_name", [None])[0] + if name_old is None: + self.report({'ERROR'}, "Direct execution not supported") return {'CANCELLED'} - self._last_prop = [prop] + data_path = self.data_path + name = self.property_name item = eval("context.%s" % data_path) - if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference): self.report({'ERROR'}, "Cannot edit properties from override data") return {'CANCELLED'} - # retrieve overridable static - is_overridable = item.is_property_overridable_library('["%s"]' % prop_escape) - self.is_overridable_library = bool(is_overridable) + prop_type_old = self.get_property_type(item, name_old) + prop_type_new = self.property_type + self._old_prop_name[:] = [name] - # default default value - value, value_failed = self.get_value_eval() - prop_type, is_array = rna_idprop_value_item_type(value) - if prop_type in {int, float}: - self.default = str(prop_type(0)) + if prop_type_new == 'PYTHON': + try: + new_value = eval(self.eval_string) + except Exception as ex: + self.report({'WARNING'}, "Python evaluation failed: " + str(ex)) + return {'CANCELLED'} + try: + item[name] = new_value + except Exception as ex: + self.report({'ERROR'}, "Failed to assign value: " + str(ex)) + return {'CANCELLED'} + if name_old != name: + del item[name_old] else: - self.default = "" + new_value = self._get_converted_value(item, name_old, prop_type_new) + del item[name_old] + item[name] = new_value - # setup defaults - if prop_type in {int, float}: - ui_data = item.id_properties_ui(prop) - rna_data = ui_data.as_dict() - self.subtype = rna_data["subtype"] - self.min = rna_data["min"] - self.max = rna_data["max"] - self.soft_min = rna_data["soft_min"] - self.soft_max = rna_data["soft_max"] - self.use_soft_limits = ( - self.min != self.soft_min or - self.max != self.soft_max - ) - self.default = str(rna_data["default"]) - if prop_type == str and not is_array and not value_failed: # String arrays do not support UI data. - ui_data = item.id_properties_ui(prop) - rna_data = ui_data.as_dict() - self.subtype = rna_data["subtype"] - self.default = str(rna_data["default"]) + self._create_ui_data_for_new_prop(item, name, prop_type_new) - self._init_subtype(prop_type, is_array, self.subtype) + self._update_blender_for_prop_change(context, item, name, prop_type_old, prop_type_new) - # store for comparison - self._cmp_props = self._cmp_props_get() + return {'FINISHED'} + + def invoke(self, context, _event): + data_path = self.data_path + if not data_path: + self.report({'ERROR'}, "Data path not set") + return {'CANCELLED'} + + name = self.property_name + + self._old_prop_name = [name] + + item = eval("context.%s" % data_path) + if (item.id_data and item.id_data.override_library and item.id_data.override_library.reference): + self.report({'ERROR'}, "Properties from override data can not be edited") + return {'CANCELLED'} + + # Set operator's property type with the type of the existing property, to display the right settings. + old_type = self.get_property_type(item, name) + self.property_type = old_type + self.last_property_type = old_type + + # So that the operator can do something for unsupported properties, change the property into + # a string, just for editing in the dialog. When the operator executes, it will be converted back + # into a python value. Always do this conversion, in case the Python property edit type is selected. + self.eval_string = self.convert_custom_property_to_string(item, name) + + if old_type != 'PYTHON': + self._fill_old_ui_data(item, name) wm = context.window_manager return wm.invoke_props_dialog(self) - def check(self, _context): - cmp_props = self._cmp_props_get() + def check(self, context): changed = False - if self._cmp_props != cmp_props: - if cmp_props["use_soft_limits"]: - if cmp_props["soft_range"] != self._cmp_props["soft_range"]: - self.min = min(self.min, self.soft_min) - self.max = max(self.max, self.soft_max) + + # In order to convert UI data between types for type changes before the operator has actually executed, + # compare against the type the last time the check method was called (the last time a value was edited). + if self.property_type != self.last_property_type: + self._convert_old_ui_data_to_new_type(self.last_property_type, self.property_type) + changed = True + + # Make sure that min is less than max, soft range is inside hard range, etc. + if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}: + if self.min_float > self.max_float: + self.min_float, self.max_float = self.max_float, self.min_float + changed = True + if self.use_soft_limits: + if self.soft_min_float > self.soft_max_float: + self.soft_min_float, self.soft_max_float = self.soft_max_float, self.soft_min_float changed = True - if cmp_props["hard_range"] != self._cmp_props["hard_range"]: - self.soft_min = max(self.min, self.soft_min) - self.soft_max = min(self.max, self.soft_max) + if self.soft_max_float > self.max_float: + self.soft_max_float = self.max_float changed = True - else: - if cmp_props["soft_range"] != cmp_props["hard_range"]: - self.soft_min = self.min - self.soft_max = self.max + if self.soft_min_float < self.min_float: + self.soft_min_float = self.min_float + changed = True + elif self.property_type in {'INT', 'INT_ARRAY'}: + if self.min_int > self.max_int: + self.min_int, self.max_int = self.max_int, self.min_int + changed = True + if self.use_soft_limits: + if self.soft_min_int > self.soft_max_int: + self.soft_min_int, self.soft_max_int = self.soft_max_int, self.soft_min_int + changed = True + if self.soft_max_int > self.max_int: + self.soft_max_int = self.max_int + changed = True + if self.soft_min_int < self.min_int: + self.soft_min_int = self.min_int changed = True - changed |= (cmp_props["use_soft_limits"] != self._cmp_props["use_soft_limits"]) - - if changed: - cmp_props = self._cmp_props_get() - - self._cmp_props = cmp_props + self.last_property_type = self.property_type return changed def draw(self, _context): - from rna_prop_ui import ( - rna_idprop_value_item_type, - ) - layout = self.layout layout.use_property_split = True layout.use_property_decorate = False - layout.prop(self, "property") - layout.prop(self, "value") + layout.prop(self, "property_type") + layout.prop(self, "property_name") - value = self.get_value_eval() - proptype, is_array = rna_idprop_value_item_type(value) + if self.property_type in {'FLOAT', 'FLOAT_ARRAY'}: + if self.property_type == 'FLOAT_ARRAY': + layout.prop(self, "array_length") + col = layout.column(align=True) + col.prop(self, "default_float", index=0, text="Default") + for i in range(1, self.array_length): + col.prop(self, "default_float", index=i, text=" ") + else: + layout.prop(self, "default_float", index=0) - row = layout.row() - row.enabled = proptype in {int, float, str} - row.prop(self, "default") + col = layout.column(align=True) + col.prop(self, "min_float") + col.prop(self, "max_float") - col = layout.column(align=True) - col.prop(self, "min") - col.prop(self, "max") + col = layout.column() + col.prop(self, "is_overridable_library") + col.prop(self, "use_soft_limits") - col = layout.column() - col.prop(self, "is_overridable_library") - col.prop(self, "use_soft_limits") + col = layout.column(align=True) + col.enabled = self.use_soft_limits + col.prop(self, "soft_min_float", text="Soft Min") + col.prop(self, "soft_max_float", text="Max") - col = layout.column(align=True) - col.enabled = self.use_soft_limits - col.prop(self, "soft_min", text="Soft Min") - col.prop(self, "soft_max", text="Max") - layout.prop(self, "description") + layout.prop(self, "step_float") + layout.prop(self, "precision") - if is_array and proptype == float: - layout.prop(self, "subtype") + # Subtype is only supported for float properties currently. + if self.property_type != 'FLOAT': + layout.prop(self, "subtype") + elif self.property_type in {'INT', 'INT_ARRAY'}: + if self.property_type == 'INT_ARRAY': + layout.prop(self, "array_length") + col = layout.column(align=True) + col.prop(self, "default_int", index=0, text="Default") + for i in range(1, self.array_length): + col.prop(self, "default_int", index=i, text=" ") + else: + layout.prop(self, "default_int", index=0) + + col = layout.column(align=True) + col.prop(self, "min_int") + col.prop(self, "max_int") + + col = layout.column() + col.prop(self, "is_overridable_library") + col.prop(self, "use_soft_limits") + + col = layout.column(align=True) + col.enabled = self.use_soft_limits + col.prop(self, "soft_min_int", text="Soft Min") + col.prop(self, "soft_max_int", text="Max") + + layout.prop(self, "step_int") + elif self.property_type == 'STRING': + layout.prop(self, "default_string") + + if self.property_type == 'PYTHON': + layout.prop(self, "eval_string") + else: + layout.prop(self, "description") + + +# Edit the value of a custom property with the given name on the RNA struct at the given data path. +# For supported types, this simply acts as a convenient way to create a popup for a specific property +# and draws the custom property value directly in the popup. For types like groups which can't be edited +# directly with buttons, instead convert the value to a string, evaluate the changed string when executing. +class WM_OT_properties_edit_value(Operator): + """Edit the value of a custom property""" + bl_idname = "wm.properties_edit_value" + bl_label = "Edit Property Value" + # register only because invoke_props_popup requires. + bl_options = {'REGISTER', 'INTERNAL'} + + data_path: rna_path + property_name: rna_custom_property_name + + # Store the value converted to a string as a fallback for otherwise unsupported types. + eval_string: StringProperty( + name="Value", + description="Value for custom property types that can only be edited as a Python expression" + ) + + def execute(self, context): + if self.eval_string: + rna_item = eval("context.%s" % self.data_path) + try: + new_value = eval(self.eval_string) + except Exception as ex: + self.report({'WARNING'}, "Python evaluation failed: " + str(ex)) + return {'CANCELLED'} + rna_item[self.property_name] = new_value + return {'FINISHED'} + + def invoke(self, context, _event): + rna_item = eval("context.%s" % self.data_path) + + if WM_OT_properties_edit.get_property_type(rna_item, self.property_name) == 'PYTHON': + self.eval_string = WM_OT_properties_edit.convert_custom_property_to_string(rna_item, + self.property_name) + else: + self.eval_string = "" + + wm = context.window_manager + return wm.invoke_props_dialog(self) + + def draw(self, context): + from bpy.utils import escape_identifier + + rna_item = eval("context.%s" % self.data_path) + + layout = self.layout + if WM_OT_properties_edit.get_property_type(rna_item, self.property_name) == 'PYTHON': + layout.prop(self, "eval_string") + else: + col = layout.column(align=True) + col.prop(rna_item, '["%s"]' % escape_identifier(self.property_name), text="") class WM_OT_properties_add(Operator): @@ -1699,7 +1972,7 @@ class WM_OT_properties_remove(Operator): bl_options = {'UNDO', 'INTERNAL'} data_path: rna_path - property: rna_custom_property + property_name: rna_custom_property_name def execute(self, context): from rna_prop_ui import ( @@ -1712,9 +1985,9 @@ class WM_OT_properties_remove(Operator): self.report({'ERROR'}, "Cannot remove properties from override data") return {'CANCELLED'} - prop = self.property - rna_idprop_ui_prop_update(item, prop) - del item[prop] + name = self.property_name + rna_idprop_ui_prop_update(item, name) + del item[name] return {'FINISHED'} @@ -2187,6 +2460,7 @@ class WM_OT_batch_rename(Operator): bl_idname = "wm.batch_rename" bl_label = "Batch Rename" + bl_description = "Rename multiple items at once" bl_options = {'UNDO'} data_type: EnumProperty( @@ -2633,12 +2907,13 @@ class WM_MT_splash_quick_setup(Menu): col = split.column() - sub = col.split(factor=0.35) - row = sub.row() - row.alignment = 'RIGHT' - row.label(text="Language") - prefs = context.preferences - sub.prop(prefs.view, "language", text="") + if bpy.app.build_options.international: + sub = col.split(factor=0.35) + row = sub.row() + row.alignment = 'RIGHT' + row.label(text="Language") + prefs = context.preferences + sub.prop(prefs.view, "language", text="") col.separator() @@ -2845,6 +3120,7 @@ classes = ( WM_OT_properties_add, WM_OT_properties_context_change, WM_OT_properties_edit, + WM_OT_properties_edit_value, WM_OT_properties_remove, WM_OT_sysinfo, WM_OT_owner_disable, diff --git a/release/scripts/startup/bl_ui/__init__.py b/release/scripts/startup/bl_ui/__init__.py index 25484e905c3..1fb40ad8bc8 100644 --- a/release/scripts/startup/bl_ui/__init__.py +++ b/release/scripts/startup/bl_ui/__init__.py @@ -117,6 +117,8 @@ def register(): for cls in mod.classes: register_class(cls) + space_filebrowser.register_props() + from bpy.props import ( EnumProperty, StringProperty, diff --git a/release/scripts/startup/bl_ui/properties_animviz.py b/release/scripts/startup/bl_ui/properties_animviz.py index 6c3c1fd1721..44965a60489 100644 --- a/release/scripts/startup/bl_ui/properties_animviz.py +++ b/release/scripts/startup/bl_ui/properties_animviz.py @@ -68,20 +68,25 @@ class MotionPathButtonsPanel: col.prop(mpath, "frame_start", text="Cache From") col.prop(mpath, "frame_end", text="To") - row = layout.row(align=True) + col = layout.column(align=True) + + row = col.row(align=True) if bones: row.operator("pose.paths_update", text="Update Paths", icon='BONE_DATA') row.operator("pose.paths_clear", text="", icon='X') else: row.operator("object.paths_update", text="Update Paths", icon='OBJECT_DATA') row.operator("object.paths_clear", text="", icon='X') + col.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') else: col = layout.column(align=True) col.label(text="Nothing to show yet...", icon='ERROR') + if bones: col.operator("pose.paths_calculate", text="Calculate...", icon='BONE_DATA') else: col.operator("object.paths_calculate", text="Calculate...", icon='OBJECT_DATA') + col.operator("object.paths_update_visible", text="Update All Paths", icon='WORLD') class MotionPathButtonsPanel_display: diff --git a/release/scripts/startup/bl_ui/properties_constraint.py b/release/scripts/startup/bl_ui/properties_constraint.py index 2a0cf56534c..85381f5ee3c 100644 --- a/release/scripts/startup/bl_ui/properties_constraint.py +++ b/release/scripts/startup/bl_ui/properties_constraint.py @@ -70,7 +70,7 @@ class ConstraintButtonsPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_label = "" - bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND', 'DRAW_BOX'} + bl_options = {'INSTANCED', 'HEADER_LAYOUT_EXPAND'} @staticmethod def draw_influence(layout, con): @@ -976,7 +976,6 @@ class ConstraintButtonsSubPanel: bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_label = "" - bl_options = {'DRAW_BOX'} def get_constraint(self, _context): con = self.custom_data diff --git a/release/scripts/startup/bl_ui/properties_data_bone.py b/release/scripts/startup/bl_ui/properties_data_bone.py index daf64642f68..1a00150d6d2 100644 --- a/release/scripts/startup/bl_ui/properties_data_bone.py +++ b/release/scripts/startup/bl_ui/properties_data_bone.py @@ -196,7 +196,7 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): row2.prop(bone, "bbone_handle_use_scale_start", index=1, text="Y", toggle=True) row2.prop(bone, "bbone_handle_use_scale_start", index=2, text="Z", toggle=True) split2.prop(bone, "bbone_handle_use_ease_start", text="Ease", toggle=True) - row.label(icon="BLANK1") + row.label(icon='BLANK1') col = topcol.column(align=True) col.prop(bone, "bbone_handle_type_end", text="End Handle") @@ -216,7 +216,7 @@ class BONE_PT_curved(BoneButtonsPanel, Panel): row2.prop(bone, "bbone_handle_use_scale_end", index=1, text="Y", toggle=True) row2.prop(bone, "bbone_handle_use_scale_end", index=2, text="Z", toggle=True) split2.prop(bone, "bbone_handle_use_ease_end", text="Ease", toggle=True) - row.label(icon="BLANK1") + row.label(icon='BLANK1') class BONE_PT_relations(BoneButtonsPanel, Panel): diff --git a/release/scripts/startup/bl_ui/properties_data_mesh.py b/release/scripts/startup/bl_ui/properties_data_mesh.py index 74d9c595986..e0a9248515d 100644 --- a/release/scripts/startup/bl_ui/properties_data_mesh.py +++ b/release/scripts/startup/bl_ui/properties_data_mesh.py @@ -606,7 +606,6 @@ class DATA_PT_mesh_attributes(MeshButtonsPanel, Panel): add_attributes(mesh.attributes) add_attributes(mesh.uv_layers) - add_attributes(mesh.vertex_colors) add_attributes(ob.vertex_groups) colliding_names = [name for name, layers in attributes_by_name.items() if len(layers) >= 2] diff --git a/release/scripts/startup/bl_ui/properties_freestyle.py b/release/scripts/startup/bl_ui/properties_freestyle.py index 3c765c10154..4519390f953 100644 --- a/release/scripts/startup/bl_ui/properties_freestyle.py +++ b/release/scripts/startup/bl_ui/properties_freestyle.py @@ -280,7 +280,7 @@ class VIEWLAYER_PT_freestyle_lineset(ViewLayerFreestyleEditorButtonsPanel, Panel col.separator() - col.menu("RENDER_MT_lineset_context_menu", icon="DOWNARROW_HLT", text="") + col.menu("RENDER_MT_lineset_context_menu", icon='DOWNARROW_HLT', text="") if is_sortable: col.separator() diff --git a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py index f01e75dbab8..6ca13674234 100644 --- a/release/scripts/startup/bl_ui/properties_grease_pencil_common.py +++ b/release/scripts/startup/bl_ui/properties_grease_pencil_common.py @@ -525,7 +525,7 @@ class GreasePencilMaterialsPanel: is_view3d = (self.bl_space_type == 'VIEW_3D') tool_settings = context.scene.tool_settings gpencil_paint = tool_settings.gpencil_paint - brush = gpencil_paint.brush + brush = gpencil_paint.brush if gpencil_paint else None ob = context.object row = layout.row() @@ -587,8 +587,11 @@ class GreasePencilMaterialsPanel: ma = ob.material_slots[ob.active_material_index].material else: ma = gp_settings.material + else: + if len(ob.material_slots) > 0 and ob.active_material_index >= 0: + ma = ob.material_slots[ob.active_material_index].material - if ma is not None and ma.grease_pencil is not None: + if is_view3d and ma is not None and ma.grease_pencil is not None: gpcolor = ma.grease_pencil if gpcolor.stroke_style == 'SOLID': row = layout.row() diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index c038f5f906a..14aacf3a47a 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -1151,7 +1151,8 @@ def brush_basic__draw_color_selector(context, layout, brush, gp_settings, props) if len(txt_ma) > maxw: txt_ma = txt_ma[:maxw - 5] + '..' + txt_ma[-3:] - sub = row.row() + sub = row.row(align=True) + sub.enabled = not gp_settings.use_material_pin sub.ui_units_x = 8 sub.popover( panel="TOPBAR_PT_gpencil_materials", diff --git a/release/scripts/startup/bl_ui/properties_physics_fluid.py b/release/scripts/startup/bl_ui/properties_physics_fluid.py index 6408d4096fe..47f0b399b66 100644 --- a/release/scripts/startup/bl_ui/properties_physics_fluid.py +++ b/release/scripts/startup/bl_ui/properties_physics_fluid.py @@ -227,7 +227,12 @@ class PHYSICS_PT_settings(PhysicButtonsPanel, Panel): split.enabled = note_flag and ob.mode == 'OBJECT' bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) - if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete: + if ( + domain.cache_resumable and + domain.has_cache_baked_data and + not domain.is_cache_baking_data and + bake_incomplete + ): col = split.column() col.operator("fluid.bake_data", text="Resume") col = split.column() @@ -1249,7 +1254,12 @@ class PHYSICS_PT_cache(PhysicButtonsPanel, Panel): split.enabled = ob.mode == 'OBJECT' bake_incomplete = (domain.cache_frame_pause_data < domain.cache_frame_end) - if domain.cache_resumable and domain.has_cache_baked_data and not domain.is_cache_baking_data and bake_incomplete: + if ( + domain.cache_resumable and + domain.has_cache_baked_data and + not domain.is_cache_baking_data and + bake_incomplete + ): col = split.column() col.operator("fluid.bake_all", text="Resume") col = split.column() diff --git a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py index a55bd89ca18..26fe215b17d 100644 --- a/release/scripts/startup/bl_ui/properties_physics_rigidbody.py +++ b/release/scripts/startup/bl_ui/properties_physics_rigidbody.py @@ -109,7 +109,11 @@ class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel): @classmethod def poll(cls, context): obj = context.object - if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND': + if ( + (obj.parent is not None) and + (obj.parent.rigid_body is not None) and + (not obj.parent.rigid_body.collision_shape == 'COMPOUND') + ): return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) @@ -124,7 +128,11 @@ class PHYSICS_PT_rigid_body_collisions(PHYSICS_PT_rigidbody_panel, Panel): layout.prop(rbo, "collision_shape", text="Shape") if rbo.collision_shape == 'COMPOUND': - if parent is not None and parent.rigid_body is not None and parent.rigid_body.collision_shape == 'COMPOUND': + if ( + (parent is not None) and + (parent.rigid_body is not None) and + (parent.rigid_body.collision_shape == 'COMPOUND') + ): rigid_body_warning(layout, "Sub compound shapes are not allowed") else: found = False @@ -179,7 +187,11 @@ class PHYSICS_PT_rigid_body_collisions_sensitivity(PHYSICS_PT_rigidbody_panel, P @classmethod def poll(cls, context): obj = context.object - if obj.parent is not None and obj.parent.rigid_body is not None and not obj.parent.rigid_body.collision_shape == 'COMPOUND': + if ( + (obj.parent is not None) and + (obj.parent.rigid_body is not None) and + (not obj.parent.rigid_body.collision_shape == 'COMPOUND') + ): return False return (obj and obj.rigid_body and (context.engine in cls.COMPAT_ENGINES)) diff --git a/release/scripts/startup/bl_ui/properties_view_layer.py b/release/scripts/startup/bl_ui/properties_view_layer.py index ad7d6008238..6b130d7353d 100644 --- a/release/scripts/startup/bl_ui/properties_view_layer.py +++ b/release/scripts/startup/bl_ui/properties_view_layer.py @@ -192,8 +192,6 @@ class ViewLayerCryptomattePanel(ViewLayerButtonsPanel, Panel): view_layer.use_pass_cryptomatte_material, view_layer.use_pass_cryptomatte_asset)) col.prop(view_layer, "pass_cryptomatte_depth", text="Levels") - col.prop(view_layer, "use_pass_cryptomatte_accurate", - text="Accurate Mode") class VIEWLAYER_PT_layer_passes_cryptomatte(ViewLayerCryptomattePanel, Panel): diff --git a/release/scripts/startup/bl_ui/space_filebrowser.py b/release/scripts/startup/bl_ui/space_filebrowser.py index 8ba82a7d407..05f505c518d 100644 --- a/release/scripts/startup/bl_ui/space_filebrowser.py +++ b/release/scripts/startup/bl_ui/space_filebrowser.py @@ -36,15 +36,10 @@ class FILEBROWSER_HT_header(Header): space_data = context.space_data params = space_data.params - row = layout.row(align=True) - row.prop(params, "asset_library_ref", text="") - # External libraries don't auto-refresh, add refresh button. - if params.asset_library_ref != 'LOCAL': - row.operator("file.refresh", text="", icon='FILE_REFRESH') - layout.separator_spacer() - layout.prop(params, "import_type", text="") + if params.asset_library_ref != 'LOCAL': + layout.prop(params, "import_type", text="") layout.separator_spacer() @@ -57,7 +52,15 @@ class FILEBROWSER_HT_header(Header): icon_only=True, ) - layout.prop(params, "filter_search", text="", icon='VIEWZOOM') + sub = layout.row() + sub.ui_units_x = 8 + sub.prop(params, "filter_search", text="", icon='VIEWZOOM') + + layout.popover( + panel="ASSETBROWSER_PT_filter", + text="", + icon='FILTER' + ) layout.operator( "screen.region_toggle", @@ -132,7 +135,7 @@ class FILEBROWSER_PT_display(FileBrowserPanel, Panel): class FILEBROWSER_PT_filter(FileBrowserPanel, Panel): bl_region_type = 'HEADER' bl_label = "Filter Settings" # Shows as tooltip in popover - bl_ui_units_x = 8 + bl_ui_units_x = 10 def draw(self, context): layout = self.layout @@ -195,13 +198,14 @@ class FILEBROWSER_PT_filter(FileBrowserPanel, Panel): sub = row.column(align=True) - if context.preferences.experimental.use_extended_asset_browser: - sub.prop(params, "use_filter_asset_only") + sub.prop(params, "use_filter_asset_only") filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("category_"): - sub.prop(filter_id, identifier, toggle=True) + subrow = sub.row() + subrow.label(icon=filter_id.bl_rna.properties[identifier].icon) + subrow.prop(filter_id, identifier, toggle=False) col.separator() @@ -300,7 +304,7 @@ class FILEBROWSER_PT_bookmarks_favorites(FileBrowserPanel, Panel): bl_space_type = 'FILE_BROWSER' bl_region_type = 'TOOLS' bl_category = "Bookmarks" - bl_label = "Favorites" + bl_label = "Bookmarks" @classmethod def poll(cls, context): @@ -394,7 +398,9 @@ class FILEBROWSER_PT_advanced_filter(Panel): filter_id = params.filter_id for identifier in dir(filter_id): if identifier.startswith("filter_"): - col.prop(filter_id, identifier, toggle=True) + row = col.row() + row.label(icon=filter_id.bl_rna.properties[identifier].icon) + row.prop(filter_id, identifier, toggle=False) def is_option_region_visible(context, space): @@ -594,6 +600,31 @@ class ASSETBROWSER_PT_display(asset_utils.AssetBrowserPanel, Panel): col.prop(params, "show_details_datetime", text="Date") +class ASSETBROWSER_PT_filter(asset_utils.AssetBrowserPanel, Panel): + bl_region_type = 'HEADER' + bl_category = "Filter" + bl_label = "Filter" + + def draw(self, context): + layout = self.layout + space = context.space_data + params = space.params + use_extended_browser = context.preferences.experimental.use_extended_asset_browser + + if params.use_filter_blendid: + col = layout.column(align=True) + + filter_id = params.filter_asset_id + for identifier in dir(filter_id): + if ( + identifier.startswith("filter_") or + (identifier.startswith("experimental_filter_") and use_extended_browser) + ): + row = col.row() + row.label(icon=filter_id.bl_rna.properties[identifier].icon) + row.prop(filter_id, identifier, toggle=False) + + class AssetBrowserMenu: @classmethod def poll(cls, context): @@ -610,6 +641,7 @@ class ASSETBROWSER_MT_editor_menus(AssetBrowserMenu, Menu): layout.menu("ASSETBROWSER_MT_view") layout.menu("ASSETBROWSER_MT_select") + layout.menu("ASSETBROWSER_MT_edit") class ASSETBROWSER_MT_view(AssetBrowserMenu, Menu): @@ -648,28 +680,14 @@ class ASSETBROWSER_MT_select(AssetBrowserMenu, Menu): layout.operator("file.select_box") -class ASSETBROWSER_PT_navigation_bar(asset_utils.AssetBrowserPanel, Panel): - bl_label = "Asset Navigation" - bl_region_type = 'TOOLS' - bl_options = {'HIDE_HEADER'} +class ASSETBROWSER_MT_edit(AssetBrowserMenu, Menu): + bl_label = "Edit" - @classmethod - def poll(cls, context): - return ( - asset_utils.AssetBrowserPanel.poll(context) and - context.preferences.experimental.use_extended_asset_browser - ) - - def draw(self, context): + def draw(self, _context): layout = self.layout - space_file = context.space_data - - col = layout.column() - - col.scale_x = 1.3 - col.scale_y = 1.3 - col.prop(space_file.params, "asset_category", expand=True) + layout.operator("asset.catalog_undo", text="Undo") + layout.operator("asset.catalog_redo", text="Redo") class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): @@ -679,6 +697,7 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): def draw(self, context): layout = self.layout + wm = context.window_manager asset_file_handle = context.asset_file_handle if asset_file_handle is None: @@ -688,20 +707,37 @@ class ASSETBROWSER_PT_metadata(asset_utils.AssetBrowserPanel, Panel): asset_library_ref = context.asset_library_ref asset_lib_path = bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref) + show_developer_ui = context.preferences.view.show_developer_ui + + layout.use_property_split = True + layout.use_property_decorate = False # No animation. + if asset_file_handle.local_id: # If the active file is an ID, use its name directly so renaming is possible from right here. - layout.prop(asset_file_handle.local_id, "name", text="") - row = layout.row() - row.label(text="Source: Current File") - else: - layout.prop(asset_file_handle, "name", text="") - col = layout.column(align=True) # Just to reduce margin. - col.label(text="Source:") - row = col.row() - row.label(text=asset_lib_path) + layout.prop(asset_file_handle.local_id, "name") + if show_developer_ui: + col = layout.column(align=True) + col.label(text="Asset Catalog:") + col.prop(asset_file_handle.local_id.asset_data, "catalog_id", text="UUID") + col.prop(asset_file_handle.local_id.asset_data, "catalog_simple_name", text="Simple Name") + else: + layout.prop(asset_file_handle, "name") + + if show_developer_ui: + col = layout.column(align=True) + col.enabled = False + col.label(text="Asset Catalog:") + col.prop(asset_file_handle.asset_data, "catalog_id", text="UUID") + col.prop(asset_file_handle.asset_data, "catalog_simple_name", text="Simple Name") + + row = layout.row(align=True) + row.prop(wm, "asset_path_dummy", text="Source") row.operator("asset.open_containing_blend_file", text="", icon='TOOL_SETTINGS') + layout.prop(asset_file_handle.asset_data, "description") + layout.prop(asset_file_handle.asset_data, "author") + class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): bl_label = "Preview" @@ -713,24 +749,11 @@ class ASSETBROWSER_PT_metadata_preview(asset_utils.AssetMetaDataPanel, Panel): row = layout.row() box = row.box() box.template_icon(icon_value=active_file.preview_icon_id, scale=5.0) - if bpy.ops.ed.lib_id_load_custom_preview.poll(): - col = row.column(align=True) - col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="") - col.separator() - col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="") - -class ASSETBROWSER_PT_metadata_details(asset_utils.AssetMetaDataPanel, Panel): - bl_label = "Details" - - def draw(self, context): - layout = self.layout - active_asset = asset_utils.SpaceAssetInfo.get_active_asset(context) - - layout.use_property_split = True - - if active_asset: - layout.prop(active_asset, "description") + col = row.column(align=True) + col.operator("ed.lib_id_load_custom_preview", icon='FILEBROWSER', text="") + col.separator() + col.operator("ed.lib_id_generate_preview", icon='FILE_REFRESH', text="") class ASSETBROWSER_PT_metadata_tags(asset_utils.AssetMetaDataPanel, Panel): @@ -769,13 +792,14 @@ class ASSETBROWSER_MT_context_menu(AssetBrowserMenu, Menu): st = context.space_data params = st.params - layout.operator("file.refresh", text="Refresh") + layout.operator("file.asset_library_refresh") layout.separator() - sub = layout.row() + sub = layout.column() sub.operator_context = 'EXEC_DEFAULT' - sub.operator("asset.clear", text="Clear Asset") + sub.operator("asset.clear", text="Clear Asset").set_fake_user = False + sub.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True layout.separator() @@ -804,18 +828,47 @@ classes = ( FILEBROWSER_MT_select, FILEBROWSER_MT_context_menu, ASSETBROWSER_PT_display, + ASSETBROWSER_PT_filter, ASSETBROWSER_MT_editor_menus, ASSETBROWSER_MT_view, ASSETBROWSER_MT_select, - ASSETBROWSER_PT_navigation_bar, + ASSETBROWSER_MT_edit, ASSETBROWSER_PT_metadata, ASSETBROWSER_PT_metadata_preview, - ASSETBROWSER_PT_metadata_details, ASSETBROWSER_PT_metadata_tags, ASSETBROWSER_UL_metadata_tags, ASSETBROWSER_MT_context_menu, ) +def asset_path_str_get(self): + asset_file_handle = bpy.context.asset_file_handle + if asset_file_handle is None: + return None + + if asset_file_handle.local_id: + return "Current File" + + asset_library_ref = bpy.context.asset_library_ref + return bpy.types.AssetHandle.get_full_library_path(asset_file_handle, asset_library_ref) + + +def register_props(): + from bpy.props import ( + StringProperty, + ) + from bpy.types import ( + WindowManager, + ) + + # Just a dummy property to be able to show a string in a label button via + # UILayout.prop(). + WindowManager.asset_path_dummy = StringProperty( + name="Asset Blend Path", + description="Full path to the Blender file containing the active asset", + get=asset_path_str_get, + ) + + if __name__ == "__main__": # only for live edit. from bpy.utils import register_class diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 3ee668888f3..6a769b1aecc 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -598,16 +598,10 @@ class IMAGE_HT_tool_header(Header): def draw(self, context): layout = self.layout - layout.template_header() - self.draw_tool_settings(context) layout.separator_spacer() - IMAGE_HT_header.draw_xform_template(layout, context) - - layout.separator_spacer() - self.draw_mode_settings(context) def draw_tool_settings(self, context): @@ -762,8 +756,7 @@ class IMAGE_HT_header(Header): show_uvedit = sima.show_uvedit show_maskedit = sima.show_maskedit - if not show_region_tool_header: - layout.template_header() + layout.template_header() if sima.mode != 'UV': layout.prop(sima, "ui_mode", text="") @@ -784,8 +777,7 @@ class IMAGE_HT_header(Header): layout.separator_spacer() - if not show_region_tool_header: - IMAGE_HT_header.draw_xform_template(layout, context) + IMAGE_HT_header.draw_xform_template(layout, context) layout.template_ID(sima, "image", new="image.new", open="image.open") @@ -934,6 +926,10 @@ class IMAGE_PT_snapping(Panel): row = col.row(align=True) row.prop(tool_settings, "snap_target", expand=True) + col.separator() + if 'INCREMENT' in tool_settings.snap_uv_element: + col.prop(tool_settings, "use_snap_uv_grid_absolute") + col.label(text="Affect") row = col.row(align=True) row.prop(tool_settings, "use_snap_translate", text="Move", toggle=True) @@ -1467,6 +1463,33 @@ class IMAGE_PT_udim_grid(Panel): col = layout.column() col.prop(uvedit, "tile_grid_shape", text="Grid Shape") +class IMAGE_PT_custom_grid(Panel): + bl_space_type = 'IMAGE_EDITOR' + bl_region_type = 'UI' + bl_category = "View" + bl_label = "Custom Grid" + + @classmethod + def poll(cls, context): + sima = context.space_data + return sima.show_uvedit + + def draw_header(self, context): + sima = context.space_data + uvedit = sima.uv_editor + self.layout.prop(uvedit, "use_custom_grid", text="") + + def draw(self, context): + layout = self.layout + + sima = context.space_data + uvedit = sima.uv_editor + + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column() + col.prop(uvedit, "custom_grid_subdivisions", text="Subdivisions") class IMAGE_PT_overlay(Panel): bl_space_type = 'IMAGE_EDITOR' @@ -1652,6 +1675,7 @@ classes = ( IMAGE_PT_uv_cursor, IMAGE_PT_annotation, IMAGE_PT_udim_grid, + IMAGE_PT_custom_grid, IMAGE_PT_overlay, IMAGE_PT_overlay_uv_edit, IMAGE_PT_overlay_uv_edit_geometry, diff --git a/release/scripts/startup/bl_ui/space_node.py b/release/scripts/startup/bl_ui/space_node.py index 5f36009901a..2fda13184da 100644 --- a/release/scripts/startup/bl_ui/space_node.py +++ b/release/scripts/startup/bl_ui/space_node.py @@ -49,6 +49,7 @@ class NODE_HT_header(Header): scene = context.scene snode = context.space_data + overlay = snode.overlay snode_id = snode.id id_from = snode.id_from tool_settings = context.tool_settings @@ -205,6 +206,13 @@ class NODE_HT_header(Header): if tool_settings.snap_node_element != 'GRID': row.prop(tool_settings, "snap_target", text="") + # Overlay toggle & popover + row = layout.row(align=True) + row.prop(overlay, "show_overlays", icon='OVERLAY', text="") + sub = row.row(align=True) + sub.active = overlay.show_overlays + sub.popover(panel="NODE_PT_overlay", text="") + class NODE_MT_editor_menus(Menu): bl_idname = "NODE_MT_editor_menus" @@ -680,6 +688,29 @@ class NODE_PT_quality(bpy.types.Panel): col.prop(snode, "use_auto_render") +class NODE_PT_overlay(Panel): + bl_space_type = 'NODE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Overlays" + bl_ui_units_x = 7 + + def draw(self, context): + layout = self.layout + layout.label(text="Node Editor Overlays") + + snode = context.space_data + overlay = snode.overlay + + layout.active = overlay.show_overlays + + col = layout.column() + col.prop(overlay, "show_wire_color", text="Wire Colors") + + col.separator() + + col.prop(snode, "show_annotation", text="Annotations") + + class NODE_UL_interface_sockets(bpy.types.UIList): def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index): socket = item @@ -754,6 +785,19 @@ class NodeTreeInterfacePanel: # Display descriptions only for Geometry Nodes, since it's only used in the modifier panel. if tree.type == 'GEOMETRY': layout.prop(active_socket, "description") + field_socket_prefixes = { + "NodeSocketInt", + "NodeSocketColor", + "NodeSocketVector", + "NodeSocketBool", + "NodeSocketFloat", + } + is_field_type = any( + active_socket.bl_socket_idname.startswith(prefix) + for prefix in field_socket_prefixes + ) + if in_out == 'OUT' and is_field_type: + layout.prop(active_socket, "attribute_domain") active_socket.draw(context, layout) @@ -843,6 +887,7 @@ classes = ( NODE_PT_backdrop, NODE_PT_quality, NODE_PT_annotation, + NODE_PT_overlay, NODE_UL_interface_sockets, NODE_PT_node_tree_interface_inputs, NODE_PT_node_tree_interface_outputs, diff --git a/release/scripts/startup/bl_ui/space_outliner.py b/release/scripts/startup/bl_ui/space_outliner.py index febd064147f..07fa2220915 100644 --- a/release/scripts/startup/bl_ui/space_outliner.py +++ b/release/scripts/startup/bl_ui/space_outliner.py @@ -313,17 +313,14 @@ class OUTLINER_MT_object(Menu): class OUTLINER_MT_asset(Menu): bl_label = "Assets" - @classmethod - def poll(cls, context): - return context.preferences.experimental.use_extended_asset_browser - def draw(self, context): layout = self.layout space = context.space_data layout.operator("asset.mark") - layout.operator("asset.clear") + layout.operator("asset.clear", text="Clear Asset").set_fake_user = False + layout.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True class OUTLINER_PT_filter(Panel): diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 258797c18da..6a4babfff8a 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -37,6 +37,14 @@ from bl_ui.space_toolsystem_common import ( from rna_prop_ui import PropertyPanel +def _space_view_types(st): + view_type = st.view_type + return ( + view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}, + view_type in {'PREVIEW', 'SEQUENCER_PREVIEW'}, + ) + + def selected_sequences_len(context): selected_sequences = getattr(context, "selected_sequences", None) if selected_sequences is None: @@ -46,44 +54,85 @@ def selected_sequences_len(context): def draw_color_balance(layout, color_balance): + layout.prop(color_balance, "correction_method") + layout.use_property_split = False flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False) - col = flow.column() - box = col.box() - split = box.split(factor=0.35) - col = split.column(align=True) - col.label(text="Lift:") - col.separator() - col.separator() - col.prop(color_balance, "lift", text="") - col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT') - split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True) + if color_balance.correction_method == 'LIFT_GAMMA_GAIN': + col = flow.column() - col = flow.column() + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Lift:") + col.separator() + col.separator() + col.prop(color_balance, "lift", text="") + col.prop(color_balance, "invert_lift", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "lift", value_slider=True, cubic=True) - box = col.box() - split = box.split(factor=0.35) - col = split.column(align=True) - col.label(text="Gamma:") - col.separator() - col.separator() - col.prop(color_balance, "gamma", text="") - col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT') - split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True) + col = flow.column() - col = flow.column() + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Gamma:") + col.separator() + col.separator() + col.prop(color_balance, "gamma", text="") + col.prop(color_balance, "invert_gamma", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "gamma", value_slider=True, lock_luminosity=True, cubic=True) - box = col.box() - split = box.split(factor=0.35) - col = split.column(align=True) - col.label(text="Gain:") - col.separator() - col.separator() - col.prop(color_balance, "gain", text="") - col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT') - split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True) + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Gain:") + col.separator() + col.separator() + col.prop(color_balance, "gain", text="") + col.prop(color_balance, "invert_gain", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "gain", value_slider=True, lock_luminosity=True, cubic=True) + + elif color_balance.correction_method == 'OFFSET_POWER_SLOPE': + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Offset:") + col.separator() + col.separator() + col.prop(color_balance, "offset", text="") + col.prop(color_balance, "invert_offset", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "offset", value_slider=True, cubic=True) + + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Power:") + col.separator() + col.separator() + col.prop(color_balance, "power", text="") + col.prop(color_balance, "invert_power", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "power", value_slider=True, cubic=True) + + col = flow.column() + + box = col.box() + split = box.split(factor=0.35) + col = split.column(align=True) + col.label(text="Slope:") + col.separator() + col.separator() + col.prop(color_balance, "slope", text="") + col.prop(color_balance, "invert_slope", text="Invert", icon='ARROW_LEFTRIGHT') + split.template_color_picker(color_balance, "slope", value_slider=True, cubic=True) class SEQUENCER_PT_active_tool(ToolActivePanelHelper, Panel): @@ -99,8 +148,6 @@ class SEQUENCER_HT_tool_header(Header): def draw(self, context): layout = self.layout - layout.template_header() - self.draw_tool_settings(context) # TODO: options popover. @@ -130,10 +177,7 @@ class SEQUENCER_HT_header(Header): st = context.space_data - show_region_tool_header = st.show_region_tool_header - - if not show_region_tool_header: - layout.template_header() + layout.template_header() layout.prop(st, "view_type", text="") @@ -141,11 +185,22 @@ class SEQUENCER_HT_header(Header): layout.separator_spacer() + tool_settings = context.tool_settings + sequencer_tool_settings = tool_settings.sequencer_tool_settings + + if st.view_type == 'PREVIEW': + layout.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True) + layout.separator_spacer() + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: - tool_settings = context.tool_settings - sequencer_tool_settings = tool_settings.sequencer_tool_settings row = layout.row(align=True) row.prop(sequencer_tool_settings, "overlap_mode", text="") + + if st.view_type == 'SEQUENCER_PREVIEW': + row = layout.row(align=True) + row.prop(sequencer_tool_settings, "pivot_point", text="", icon_only=True) + + if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: row = layout.row(align=True) row.prop(tool_settings, "use_snap_sequencer", text="") sub = row.row(align=True) @@ -156,11 +211,22 @@ class SEQUENCER_HT_header(Header): layout.prop(st, "display_mode", text="", icon_only=True) layout.prop(st, "preview_channels", text="", icon_only=True) + # Gizmo toggle & popover. + row = layout.row(align=True) + # FIXME: place-holder icon. + row.prop(st, "show_gizmo", text="", toggle=True, icon='GIZMO') + sub = row.row(align=True) + sub.active = st.show_gizmo + sub.popover( + panel="SEQUENCER_PT_gizmo_display", + text="", + ) + row = layout.row(align=True) - row.prop(st, "show_strip_overlay", text="", icon='OVERLAY') + row.prop(st, "show_overlays", text="", icon='OVERLAY') sub = row.row(align=True) sub.popover(panel="SEQUENCER_PT_overlay", text="") - sub.active = st.show_strip_overlay + sub.active = st.show_overlays class SEQUENCER_MT_editor_menus(Menu): @@ -170,15 +236,42 @@ class SEQUENCER_MT_editor_menus(Menu): def draw(self, context): layout = self.layout st = context.space_data + has_sequencer, _has_preview = _space_view_types(st) layout.menu("SEQUENCER_MT_view") + layout.menu("SEQUENCER_MT_select") - if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: - layout.menu("SEQUENCER_MT_select") + if has_sequencer: if st.show_markers: layout.menu("SEQUENCER_MT_marker") layout.menu("SEQUENCER_MT_add") - layout.menu("SEQUENCER_MT_strip") + + layout.menu("SEQUENCER_MT_strip") + + layout.menu("SEQUENCER_MT_image") + + +class SEQUENCER_PT_gizmo_display(Panel): + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'HEADER' + bl_label = "Gizmo" + bl_ui_units_x = 8 + + def draw(self, context): + layout = self.layout + + scene = context.scene + st = context.space_data + + col = layout.column() + col.label(text="Viewport Gizmos") + col.separator() + + col.active = st.show_gizmo + colsub = col.column() + colsub.prop(st, "show_gizmo_navigate", text="Navigate") + colsub.prop(st, "show_gizmo_tool", text="Active Tools") + # colsub.prop(st, "show_gizmo_context", text="Active Object") # Currently unused. class SEQUENCER_PT_overlay(Panel): @@ -205,13 +298,16 @@ class SEQUENCER_PT_preview_overlay(Panel): def draw(self, context): ed = context.scene.sequence_editor st = context.space_data + overlay_settings = st.preview_overlay layout = self.layout - layout.active = st.show_strip_overlay - layout.prop(ed, "show_overlay", text="Frame Overlay") - layout.prop(st, "show_safe_areas", text="Safe Areas") - layout.prop(st, "show_metadata", text="Metadata") - layout.prop(st, "show_annotation", text="Annotations") + layout.active = st.show_overlays + layout.prop(overlay_settings, "show_image_outline") + layout.prop(overlay_settings, "show_cursor") + layout.prop(ed, "show_overlay_frame", text="Frame Overlay") + layout.prop(overlay_settings, "show_safe_areas", text="Safe Areas") + layout.prop(overlay_settings, "show_metadata", text="Metadata") + layout.prop(overlay_settings, "show_annotation", text="Annotations") class SEQUENCER_PT_sequencer_overlay(Panel): @@ -227,23 +323,26 @@ class SEQUENCER_PT_sequencer_overlay(Panel): def draw(self, context): st = context.space_data + overlay_settings = st.timeline_overlay layout = self.layout - layout.active = st.show_strip_overlay + layout.active = st.show_overlays - layout.prop(st, "show_strip_name", text="Name") - layout.prop(st, "show_strip_source", text="Source") - layout.prop(st, "show_strip_duration", text="Duration") + layout.prop(overlay_settings, "show_strip_name", text="Name") + layout.prop(overlay_settings, "show_strip_source", text="Source") + layout.prop(overlay_settings, "show_strip_duration", text="Duration") + layout.prop(overlay_settings, "show_strip_tag_color", text="Color Tags") layout.separator() - layout.prop(st, "show_strip_offset", text="Offsets") - layout.prop(st, "show_fcurves", text="F-Curves") - layout.prop(st, "show_grid", text="Grid") + layout.prop(overlay_settings, "show_strip_offset", text="Offsets") + layout.prop(overlay_settings, "show_fcurves", text="F-Curves") + layout.prop(overlay_settings, "show_thumbnails", text="Thumbnails") + layout.prop(overlay_settings, "show_grid", text="Grid") layout.separator() - layout.prop_menu_enum(st, "waveform_display_type") + layout.prop_menu_enum(overlay_settings, "waveform_display_type") class SEQUENCER_MT_view_cache(Menu): @@ -333,6 +432,7 @@ class SEQUENCER_MT_view(Menu): # wm_keymap_item_find_props() (see T32595). layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.prop(st, "show_region_ui") + layout.prop(st, "show_region_tool_header") layout.prop(st, "show_region_toolbar") layout.operator_context = 'INVOKE_DEFAULT' @@ -366,9 +466,9 @@ class SEQUENCER_MT_view(Menu): layout.operator("view2d.zoom_border", text="Zoom") layout.menu("SEQUENCER_MT_preview_zoom") - if st.display_mode == 'IMAGE': - layout.prop(st, "use_zoom_to_fit") - elif st.display_mode == 'WAVEFORM': + layout.prop(st, "use_zoom_to_fit") + + if st.display_mode == 'WAVEFORM': layout.separator() layout.prop(st, "show_separate_color", text="Show Separate Color Channels") @@ -471,8 +571,14 @@ class SEQUENCER_MT_select_linked(Menu): class SEQUENCER_MT_select(Menu): bl_label = "Select" - def draw(self, _context): + def draw(self, context): layout = self.layout + st = context.space_data + has_sequencer, has_preview = _space_view_types(st) + + # FIXME: this doesn't work for both preview + window region. + if has_preview: + layout.operator_context = 'INVOKE_REGION_PREVIEW' layout.operator("sequencer.select_all", text="All").action = 'SELECT' layout.operator("sequencer.select_all", text="None").action = 'DESELECT' @@ -481,17 +587,20 @@ class SEQUENCER_MT_select(Menu): layout.separator() layout.operator("sequencer.select_box", text="Box Select") - props = layout.operator("sequencer.select_box", text="Box Select (Include Handles)") - props.include_handles = True + if has_sequencer: + props = layout.operator("sequencer.select_box", text="Box Select (Include Handles)") + props.include_handles = True layout.separator() - layout.operator_menu_enum("sequencer.select_side_of_frame", "side", text="Side of Frame...") - layout.menu("SEQUENCER_MT_select_handle", text="Handle") - layout.menu("SEQUENCER_MT_select_channel", text="Channel") - layout.menu("SEQUENCER_MT_select_linked", text="Linked") + if has_sequencer: + layout.operator_menu_enum("sequencer.select_side_of_frame", "side", text="Side of Frame...") + layout.menu("SEQUENCER_MT_select_handle", text="Handle") + layout.menu("SEQUENCER_MT_select_channel", text="Channel") + layout.menu("SEQUENCER_MT_select_linked", text="Linked") + + layout.separator() - layout.separator() layout.operator_menu_enum("sequencer.select_grouped", "type", text="Grouped") @@ -699,43 +808,43 @@ class SEQUENCER_MT_add_effect(Menu): col.enabled = selected_sequences_len(context) != 0 -class SEQUENCER_MT_strip_image_transform(Menu): - bl_label = "Image Transform" - - def draw(self, _context): - layout = self.layout - - layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT' - layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL' - layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH' - layout.separator() - - layout.operator("sequencer.strip_transform_clear", text="Clear Position").property = 'POSITION' - layout.operator("sequencer.strip_transform_clear", text="Clear Scale").property = 'SCALE' - layout.operator("sequencer.strip_transform_clear", text="Clear Rotation").property = 'ROTATION' - layout.operator("sequencer.strip_transform_clear", text="Clear All").property = 'ALL' - - class SEQUENCER_MT_strip_transform(Menu): bl_label = "Transform" - def draw(self, _context): + def draw(self, context): layout = self.layout + st = context.space_data + has_sequencer, has_preview = _space_view_types(st) - layout.operator("transform.seq_slide", text="Move") - layout.operator("transform.transform", text="Move/Extend from Current Frame").mode = 'TIME_EXTEND' - layout.operator("sequencer.slip", text="Slip Strip Contents") + if has_preview: + layout.operator_context = 'INVOKE_REGION_PREVIEW' + else: + layout.operator_context = 'INVOKE_REGION_WIN' - layout.separator() - layout.operator("sequencer.snap") - layout.operator("sequencer.offset_clear") + # FIXME: mixed preview/sequencer views. + if has_preview: + layout.operator("transform.translate", text="Move") + layout.operator("transform.rotate", text="Rotate") + layout.operator("transform.resize", text="Scale") + else: + layout.operator("transform.seq_slide", text="Move") + layout.operator("transform.transform", text="Move/Extend from Current Frame").mode = 'TIME_EXTEND' + layout.operator("sequencer.slip", text="Slip Strip Contents") - layout.separator() - layout.operator_menu_enum("sequencer.swap", "side") + # TODO (for preview) + if has_sequencer: + layout.separator() + layout.operator("sequencer.snap") + layout.operator("sequencer.offset_clear") - layout.separator() - layout.operator("sequencer.gap_remove").all = False - layout.operator("sequencer.gap_insert") + layout.separator() + + if has_sequencer: + layout.operator_menu_enum("sequencer.swap", "side") + + layout.separator() + layout.operator("sequencer.gap_remove").all = False + layout.operator("sequencer.gap_insert") class SEQUENCER_MT_strip_input(Menu): @@ -805,66 +914,129 @@ class SEQUENCER_MT_strip(Menu): def draw(self, context): layout = self.layout + st = context.space_data + has_sequencer, has_preview = _space_view_types(st) - layout.operator_context = 'INVOKE_REGION_WIN' + # FIXME: this doesn't work for both preview + window region. + if has_preview: + layout.operator_context = 'INVOKE_REGION_PREVIEW' + else: + layout.operator_context = 'INVOKE_REGION_WIN' - layout.separator() layout.menu("SEQUENCER_MT_strip_transform") - layout.menu("SEQUENCER_MT_strip_image_transform") - layout.separator() - layout.operator("sequencer.split", text="Split").type = 'SOFT' - layout.operator("sequencer.split", text="Hold Split").type = 'HARD' - layout.separator() - layout.operator("sequencer.copy", text="Copy") - layout.operator("sequencer.paste", text="Paste") - layout.operator("sequencer.duplicate_move") + if has_sequencer: + + layout.operator("sequencer.split", text="Split").type = 'SOFT' + layout.operator("sequencer.split", text="Hold Split").type = 'HARD' + layout.separator() + + if has_sequencer: + layout.operator("sequencer.copy", text="Copy") + layout.operator("sequencer.paste", text="Paste") + layout.operator("sequencer.duplicate_move") + layout.operator("sequencer.delete", text="Delete") strip = context.active_sequence_strip - if strip: - strip_type = strip.type + if has_sequencer: + if strip: + strip_type = strip.type - if strip_type != 'SOUND': - layout.separator() - layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier") - layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") + if strip_type != 'SOUND': + layout.separator() + layout.operator_menu_enum("sequencer.strip_modifier_add", "type", text="Add Modifier") + layout.operator("sequencer.strip_modifier_copy", text="Copy Modifiers to Selection") - if strip_type in { - 'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', - 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW', - 'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT', - 'GAUSSIAN_BLUR', - }: - layout.separator() - layout.menu("SEQUENCER_MT_strip_effect") - elif strip_type == 'MOVIE': - layout.separator() - layout.menu("SEQUENCER_MT_strip_movie") - elif strip_type == 'IMAGE': - layout.separator() - layout.operator("sequencer.rendersize") - layout.operator("sequencer.images_separate") - elif strip_type == 'TEXT': - layout.separator() - layout.menu("SEQUENCER_MT_strip_effect") - elif strip_type == 'META': - layout.separator() - layout.operator("sequencer.meta_make") - layout.operator("sequencer.meta_separate") - layout.operator("sequencer.meta_toggle", text="Toggle Meta") - if strip_type != 'META': - layout.separator() - layout.operator("sequencer.meta_make") - layout.operator("sequencer.meta_toggle", text="Toggle Meta") + if strip_type in { + 'CROSS', 'ADD', 'SUBTRACT', 'ALPHA_OVER', 'ALPHA_UNDER', + 'GAMMA_CROSS', 'MULTIPLY', 'OVER_DROP', 'WIPE', 'GLOW', + 'TRANSFORM', 'COLOR', 'SPEED', 'MULTICAM', 'ADJUSTMENT', + 'GAUSSIAN_BLUR', + }: + layout.separator() + layout.menu("SEQUENCER_MT_strip_effect") + elif strip_type == 'MOVIE': + layout.separator() + layout.menu("SEQUENCER_MT_strip_movie") + elif strip_type == 'IMAGE': + layout.separator() + layout.operator("sequencer.rendersize") + layout.operator("sequencer.images_separate") + elif strip_type == 'TEXT': + layout.separator() + layout.menu("SEQUENCER_MT_strip_effect") + elif strip_type == 'META': + layout.separator() + layout.operator("sequencer.meta_make") + layout.operator("sequencer.meta_separate") + layout.operator("sequencer.meta_toggle", text="Toggle Meta") + if strip_type != 'META': + layout.separator() + layout.operator("sequencer.meta_make") + layout.operator("sequencer.meta_toggle", text="Toggle Meta") - layout.separator() - layout.menu("SEQUENCER_MT_strip_lock_mute") + if has_sequencer: + layout.separator() + layout.menu("SEQUENCER_MT_color_tag_picker") - layout.separator() - layout.menu("SEQUENCER_MT_strip_input") + layout.separator() + layout.menu("SEQUENCER_MT_strip_lock_mute") + + layout.separator() + layout.menu("SEQUENCER_MT_strip_input") + + +class SEQUENCER_MT_image(Menu): + bl_label = "Image" + + def draw(self, context): + layout = self.layout + st = context.space_data + + if st.view_type == {'PREVIEW', 'SEQUENCER_PREVIEW'}: + layout.menu("SEQUENCER_MT_image_transform") + + layout.menu("SEQUENCER_MT_image_clear") + layout.menu("SEQUENCER_MT_image_apply") + + +class SEQUENCER_MT_image_transform(Menu): + bl_label = "Transform" + + def draw(self, _context): + layout = self.layout + + layout.operator_context = 'INVOKE_REGION_PREVIEW' + + layout.operator("transform.translate") + layout.operator("transform.rotate") + layout.operator("transform.resize", text="Scale") + + +class SEQUENCER_MT_image_clear(Menu): + bl_label = "Clear" + + def draw(self, _context): + layout = self.layout + + layout.operator("sequencer.strip_transform_clear", text="Position").property = 'POSITION' + layout.operator("sequencer.strip_transform_clear", text="Scale").property = 'SCALE' + layout.operator("sequencer.strip_transform_clear", text="Rotation").property = 'ROTATION' + layout.operator("sequencer.strip_transform_clear", text="All Transforms").property = 'ALL' + + +class SEQUENCER_MT_image_apply(Menu): + bl_label = "Apply" + + def draw(self, _context): + layout = self.layout + + layout.operator("sequencer.strip_transform_fit", text="Scale To Fit").fit_method = 'FIT' + layout.operator("sequencer.strip_transform_fit", text="Scale to Fill").fit_method = 'FILL' + layout.operator("sequencer.strip_transform_fit", text="Stretch To Fill").fit_method = 'STRETCH' class SEQUENCER_MT_context_menu(Menu): @@ -956,11 +1128,51 @@ class SEQUENCER_MT_context_menu(Menu): layout.operator("sequencer.meta_make") layout.operator("sequencer.meta_toggle", text="Toggle Meta") + layout.separator() + layout.menu("SEQUENCER_MT_color_tag_picker") + layout.separator() layout.menu("SEQUENCER_MT_strip_lock_mute") +class SEQUENCER_MT_preview_context_menu(Menu): + bl_label = "Sequencer Preview Context Menu" + + def draw(self, context): + layout = self.layout + + layout.operator_context = 'INVOKE_REGION_WIN' + + props = layout.operator("wm.call_panel", text="Rename...") + props.name = "TOPBAR_PT_name" + props.keep_open = False + + # TODO: support in preview. + # layout.operator("sequencer.delete", text="Delete") + + strip = context.active_sequence_strip + + if strip: + pass + + +class SEQUENCER_MT_pivot_pie(Menu): + bl_label = "Pivot Point" + + def draw(self, context): + layout = self.layout + pie = layout.menu_pie() + + tool_settings = context.tool_settings + sequencer_tool_settings = context.tool_settings.sequencer_tool_settings + + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CENTER') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='CURSOR') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='INDIVIDUAL_ORIGINS') + pie.prop_enum(sequencer_tool_settings, "pivot_point", value='MEDIAN') + + class SequencerButtonsPanel: bl_space_type = 'SEQUENCE_EDITOR' bl_region_type = 'UI' @@ -988,6 +1200,44 @@ class SequencerButtonsPanel_Output: return cls.has_preview(context) +class SequencerColorTagPicker: + bl_space_type = 'SEQUENCE_EDITOR' + bl_region_type = 'UI' + + @staticmethod + def has_sequencer(context): + return (context.space_data.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}) + + @classmethod + def poll(cls, context): + return cls.has_sequencer(context) and context.active_sequence_strip is not None + + +class SEQUENCER_PT_color_tag_picker(SequencerColorTagPicker, Panel): + bl_label = "Color Tag" + bl_category = "Strip" + bl_options = {'HIDE_HEADER', 'INSTANCED'} + + def draw(self, context): + layout = self.layout + + row = layout.row(align=True) + row.operator("sequencer.strip_color_tag_set", icon='X').color = 'NONE' + for i in range(1, 10): + icon = 'SEQUENCE_COLOR_%02d' % i + row.operator("sequencer.strip_color_tag_set", icon=icon).color = 'COLOR_%02d' % i + + +class SEQUENCER_MT_color_tag_picker(SequencerColorTagPicker, Menu): + bl_label = "Set Color Tag" + + def draw(self, context): + layout = self.layout + + row = layout.row(align=True) + row.operator_enum("sequencer.strip_color_tag_set", "color", icon_only=True) + + class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel): bl_label = "" bl_options = {'HIDE_HEADER'} @@ -1031,9 +1281,20 @@ class SEQUENCER_PT_strip(SequencerButtonsPanel, Panel): else: icon_header = 'SEQ_SEQUENCER' - row = layout.row() + row = layout.row(align=True) + row.use_property_decorate = False row.label(text="", icon=icon_header) + row.separator() row.prop(strip, "name", text="") + + sub = row.row(align=True) + if strip.color_tag == 'NONE': + sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon='COLOR') + else: + icon = 'SEQUENCE_' + strip.color_tag + sub.popover(panel="SEQUENCER_PT_color_tag_picker", text="", icon=icon) + + row.separator() row.prop(strip, "mute", toggle=True, icon_only=True, emboss=False) @@ -1134,15 +1395,15 @@ class SEQUENCER_PT_effect(SequencerButtonsPanel, Panel): elif strip_type == 'SPEED': col = layout.column(align=True) col.prop(strip, "speed_control", text="Speed Control") - if strip.speed_control == "MULTIPLY": + if strip.speed_control == 'MULTIPLY': col.prop(strip, "speed_factor", text=" ") - elif strip.speed_control == "LENGTH": + elif strip.speed_control == 'LENGTH': col.prop(strip, "speed_length", text=" ") - elif strip.speed_control == "FRAME_NUMBER": + elif strip.speed_control == 'FRAME_NUMBER': col.prop(strip, "speed_frame_number", text=" ") row = layout.row(align=True) - row.enabled = strip.speed_control != "STRETCH" + row.enabled = strip.speed_control != 'STRETCH' row = layout.row(align=True, heading="Interpolation") row.prop(strip, "use_frame_interpolate", text="") @@ -1649,25 +1910,16 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): def draw(self, context): layout = self.layout - layout.use_property_split = False st = context.space_data + overlay_settings = st.timeline_overlay strip = context.active_sequence_strip sound = strip.sound layout.active = not strip.mute if sound is not None: - col = layout.column() - - split = col.split(factor=0.4) - split.label(text="") - split.prop(sound, "use_mono") - if st.waveform_display_type == 'DEFAULT_WAVEFORMS': - split = col.split(factor=0.4) - split.label(text="") - split.prop(strip, "show_waveform") - + layout.use_property_split = True col = layout.column() split = col.split(factor=0.4) @@ -1680,15 +1932,37 @@ class SEQUENCER_PT_adjust_sound(SequencerButtonsPanel, Panel): split.label(text="Pitch") split.prop(strip, "pitch", text="") + audio_channels = context.scene.render.ffmpeg.audio_channels + pan_enabled = sound.use_mono and audio_channels != 'MONO' + pan_text = "%.2f°" % (strip.pan * 90) + split = col.split(factor=0.4) split.alignment = 'RIGHT' split.label(text="Pan") - audio_channels = context.scene.render.ffmpeg.audio_channels - pan_text = "" + split.prop(strip, "pan", text="") + split.enabled = pan_enabled + if audio_channels != 'MONO' and audio_channels != 'STEREO': - pan_text = "%.2f°" % (strip.pan * 90) - split.prop(strip, "pan", text=pan_text) - split.enabled = sound.use_mono and audio_channels != 'MONO' + split = col.split(factor=0.4) + split.alignment = 'RIGHT' + split.label(text="Pan Angle") + split.enabled = pan_enabled + subsplit = split.row() + subsplit.alignment = 'CENTER' + subsplit.label(text=pan_text) + subsplit.label(text=" ") # Compensate for no decorate. + subsplit.enabled = pan_enabled + + layout.use_property_split = False + col = layout.column() + + split = col.split(factor=0.4) + split.label(text="") + split.prop(sound, "use_mono") + if overlay_settings.waveform_display_type == 'DEFAULT_WAVEFORMS': + split = col.split(factor=0.4) + split.label(text="") + split.prop(strip, "show_waveform") @@ -1753,6 +2027,9 @@ class SEQUENCER_PT_adjust_transform(SequencerButtonsPanel, Panel): col = layout.column(align=True) col.prop(strip.transform, "rotation", text="Rotation") + col = layout.column(align=True) + col.prop(strip.transform, "origin") + row = layout.row(heading="Mirror") sub = row.row(align=True) sub.prop(strip, "use_flip_x", text="X", toggle=True) @@ -2039,6 +2316,22 @@ class SEQUENCER_PT_view(SequencerButtonsPanel_Output, Panel): col.prop(st, "show_separate_color") +class SEQUENCER_PT_view_cursor(SequencerButtonsPanel_Output, Panel): + bl_category = "View" + bl_label = "2D Cursor" + + def draw(self, context): + layout = self.layout + + st = context.space_data + + layout.use_property_split = True + layout.use_property_decorate = False + + col = layout.column() + col.prop(st, "cursor_location", text="Location") + + class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): bl_label = "Frame Overlay" bl_category = "View" @@ -2054,7 +2347,7 @@ class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): scene = context.scene ed = scene.sequence_editor - self.layout.prop(ed, "show_overlay", text="") + self.layout.prop(ed, "show_overlay_frame", text="") def draw(self, context): layout = self.layout @@ -2070,12 +2363,12 @@ class SEQUENCER_PT_frame_overlay(SequencerButtonsPanel_Output, Panel): scene = context.scene ed = scene.sequence_editor - layout.active = ed.show_overlay + layout.active = ed.show_overlay_frame col = layout.column() col.prop(ed, "overlay_frame", text="Frame Offset") - col.prop(st, "overlay_type") - col.prop(ed, "use_overlay_lock") + col.prop(st, "overlay_frame_type") + col.prop(ed, "use_overlay_frame_lock") class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel): @@ -2090,17 +2383,16 @@ class SEQUENCER_PT_view_safe_areas(SequencerButtonsPanel_Output, Panel): return is_preview and (st.display_mode == 'IMAGE') def draw_header(self, context): - st = context.space_data - - self.layout.prop(st, "show_safe_areas", text="") + overlay_settings = context.space_data.preview_overlay + self.layout.prop(overlay_settings, "show_safe_areas", text="") def draw(self, context): layout = self.layout layout.use_property_split = True - st = context.space_data + overlay_settings = context.space_data.preview_overlay safe_data = context.scene.safe_areas - layout.active = st.show_safe_areas + layout.active = overlay_settings.show_safe_areas col = layout.column() @@ -2116,19 +2408,18 @@ class SEQUENCER_PT_view_safe_areas_center_cut(SequencerButtonsPanel_Output, Pane bl_category = "View" def draw_header(self, context): - st = context.space_data - layout = self.layout - layout.active = st.show_safe_areas - layout.prop(st, "show_safe_center", text="") + overlay_settings = context.space_data.preview_overlay + layout.active = overlay_settings.show_safe_areas + layout.prop(overlay_settings, "show_safe_center", text="") def draw(self, context): layout = self.layout layout.use_property_split = True safe_data = context.scene.safe_areas - st = context.space_data + overlay_settings = context.space_data.preview_overlay - layout.active = st.show_safe_areas and st.show_safe_center + layout.active = overlay_settings.show_safe_areas and overlay_settings.show_safe_center col = layout.column() col.prop(safe_data, "title_center", slider=True) @@ -2313,15 +2604,24 @@ classes = ( SEQUENCER_MT_strip_effect, SEQUENCER_MT_strip_movie, SEQUENCER_MT_strip, - SEQUENCER_MT_strip_image_transform, SEQUENCER_MT_strip_transform, SEQUENCER_MT_strip_input, SEQUENCER_MT_strip_lock_mute, + SEQUENCER_MT_image, + SEQUENCER_MT_image_transform, + SEQUENCER_MT_image_clear, + SEQUENCER_MT_image_apply, + SEQUENCER_MT_color_tag_picker, SEQUENCER_MT_context_menu, + SEQUENCER_MT_preview_context_menu, + SEQUENCER_MT_pivot_pie, + + SEQUENCER_PT_color_tag_picker, SEQUENCER_PT_active_tool, SEQUENCER_PT_strip, + SEQUENCER_PT_gizmo_display, SEQUENCER_PT_overlay, SEQUENCER_PT_preview_overlay, SEQUENCER_PT_sequencer_overlay, @@ -2352,6 +2652,7 @@ classes = ( SEQUENCER_PT_custom_props, SEQUENCER_PT_view, + SEQUENCER_PT_view_cursor, SEQUENCER_PT_frame_overlay, SEQUENCER_PT_view_safe_areas, SEQUENCER_PT_view_safe_areas_center_cut, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index 28549098e51..4305476cb0f 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -118,6 +118,8 @@ ToolDef = namedtuple( "draw_settings", # Optional draw cursor. "draw_cursor", + # Various options, see: `bpy.types.WorkSpaceTool.setup` options argument. + "options", ) ) del namedtuple @@ -133,6 +135,7 @@ def from_dict(kw_args): "description": None, "icon": None, "cursor": None, + "options": None, "widget": None, "widget_properties": None, "keymap": None, @@ -187,7 +190,7 @@ class ToolActivePanelHelper: ToolSelectPanelHelper.draw_active_tool_header( context, layout.column(), - show_tool_name=True, + show_tool_icon_always=True, tool_key=ToolSelectPanelHelper._tool_key_from_context(context, space_type=self.bl_space_type), ) @@ -536,6 +539,9 @@ class ToolSelectPanelHelper: visited.add(km_name) yield (km_name, cls.bl_space_type, 'WINDOW', []) + # Callable types don't use fall-backs. + if isinstance(km_name, str): + yield (km_name + " (fallback)", cls.bl_space_type, 'WINDOW', []) # ------------------------------------------------------------------------- # Layout Generators @@ -760,7 +766,7 @@ class ToolSelectPanelHelper: def draw_active_tool_header( context, layout, *, - show_tool_name=False, + show_tool_icon_always=False, tool_key=None, ): if tool_key is None: @@ -777,9 +783,16 @@ class ToolSelectPanelHelper: return None # Note: we could show 'item.text' here but it makes the layout jitter when switching tools. # Add some spacing since the icon is currently assuming regular small icon size. - layout.label(text=" " + item.label if show_tool_name else " ", icon_value=icon_value) - if show_tool_name: + if show_tool_icon_always: + layout.label(text=" " + item.label, icon_value=icon_value) layout.separator() + else: + if context.space_data.show_region_toolbar: + layout.template_icon(icon_value=0, scale=0.5) + else: + layout.template_icon(icon_value=icon_value, scale=0.5) + layout.separator() + draw_settings = item.draw_settings if draw_settings is not None: draw_settings(context, layout, tool) @@ -988,16 +1001,22 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False): gizmo_group = item.widget or "" + idname_fallback = (item_fallback and item_fallback.idname) or "" + keymap_fallback = (item_fallback and item_fallback.keymap and item_fallback.keymap[0]) or "" + if keymap_fallback: + keymap_fallback = keymap_fallback + " (fallback)" + tool.setup( idname=item.idname, keymap=item.keymap[0] if item.keymap is not None else "", cursor=item.cursor or 'DEFAULT', + options=item.options or set(), gizmo_group=gizmo_group, data_block=item.data_block or "", operator=item.operator or "", index=index, - idname_fallback=(item_fallback and item_fallback.idname) or "", - keymap_fallback=(item_fallback and item_fallback.keymap and item_fallback.keymap[0]) or "", + idname_fallback=idname_fallback, + keymap_fallback=keymap_fallback, ) if ( diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index be9c3b023a6..31d75923ad2 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -105,6 +105,7 @@ class _defs_view3d_generic: icon="ops.generic.cursor", keymap="3D View Tool: Cursor", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -143,6 +144,7 @@ class _defs_view3d_generic: icon="ops.view3d.ruler", widget="VIEW3D_GGT_ruler", keymap="3D View Tool: Measure", + options={'KEYMAP_FALLBACK'}, ) @@ -183,23 +185,18 @@ class _defs_annotate: tool_settings = context.tool_settings if space_type == 'VIEW_3D': - layout.separator() - row = layout.row(align=True) row.prop(tool_settings, "annotation_stroke_placement_view3d", text="Placement") if tool_settings.gpencil_stroke_placement_view3d == 'CURSOR': row.prop(tool_settings.gpencil_sculpt, "lockaxis") elif tool_settings.gpencil_stroke_placement_view3d in {'SURFACE', 'STROKE'}: row.prop(tool_settings, "use_gpencil_stroke_endpoints") - elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}: - layout.separator() + elif space_type in {'IMAGE_EDITOR', 'NODE_EDITOR', 'SEQUENCE_EDITOR', 'CLIP_EDITOR'}: row = layout.row(align=True) row.prop(tool_settings, "annotation_stroke_placement_view2d", text="Placement") if tool.idname == "builtin.annotate_line": - layout.separator() - props = tool.operator_properties("gpencil.annotate") if region_type == 'TOOL_HEADER': row = layout.row() @@ -221,7 +218,6 @@ class _defs_annotate: subrow.prop(props, "stabilizer_radius", text="Radius", slider=True) subrow.prop(props, "stabilizer_factor", text="Factor", slider=True) else: - layout.separator() layout.prop(props, "use_stabilizer", text="Stabilize Stroke") col = layout.column(align=False) col.active = props.use_stabilizer @@ -237,6 +233,7 @@ class _defs_annotate: cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -248,6 +245,7 @@ class _defs_annotate: cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate Line", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn.with_args(draw_settings=draw_settings_common) @@ -259,6 +257,7 @@ class _defs_annotate: cursor='PAINT_BRUSH', keymap="Generic Tool: Annotate Polygon", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -274,6 +273,7 @@ class _defs_annotate: cursor='ERASER', keymap="Generic Tool: Annotate Eraser", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @@ -491,18 +491,14 @@ class _defs_view3d_add: props = tool.operator_properties("view3d.interactive_add") if not extra: row = layout.row() - row.scale_x = 0.8 row.label(text="Depth:") row = layout.row() - row.scale_x = 0.9 row.prop(props, "plane_depth", text="") row = layout.row() - row.scale_x = 0.8 row.label(text="Orientation:") row = layout.row() row.prop(props, "plane_orientation", text="") row = layout.row() - row.scale_x = 0.8 row.prop(props, "snap_target") region_is_header = bpy.context.region.type == 'TOOL_HEADER' @@ -543,6 +539,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -569,6 +566,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -594,6 +592,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -619,6 +618,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -643,6 +643,7 @@ class _defs_view3d_add: widget="VIEW3D_GGT_placement", keymap="3D View Tool: Object, Add Primitive", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @@ -1076,11 +1077,29 @@ class _defs_edit_mesh: @ToolDef.from_fn def knife(): - def draw_settings(_context, layout, tool): + def draw_settings(_context, layout, tool, *, extra=False): + show_extra = False props = tool.operator_properties("mesh.knife_tool") - layout.prop(props, "use_occlude_geometry") - layout.prop(props, "only_selected") - + if not extra: + row = layout.row() + layout.prop(props, "use_occlude_geometry") + row = layout.row() + layout.prop(props, "only_selected") + row = layout.row() + layout.prop(props, "xray") + region_is_header = bpy.context.region.type == 'TOOL_HEADER' + if region_is_header: + show_extra = True + else: + extra = True + if extra: + layout.use_property_split = True + layout.prop(props, "visible_measurements") + layout.prop(props, "angle_snapping") + layout.label(text="Angle Snapping Increment") + layout.row().prop(props, "angle_snapping_increment", text="", expand=True) + if show_extra: + layout.popover("TOPBAR_PT_tool_settings_extra", text="...") return dict( idname="builtin.knife", label="Knife", @@ -1089,6 +1108,7 @@ class _defs_edit_mesh: widget=None, keymap=(), draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -1659,6 +1679,7 @@ class _defs_weight_paint: props = tool.operator_properties("paint.weight_gradient") layout.prop(props, "type", expand=True) + layout.popover("VIEW3D_PT_tools_weight_gradient") return dict( idname="builtin.gradient", @@ -1693,6 +1714,7 @@ class _defs_image_generic: ), icon="ops.generic.cursor", keymap=(), + options={'KEYMAP_FALLBACK'}, ) # Currently a place holder so we can switch away from the annotation tool. @@ -1844,6 +1866,7 @@ class _defs_image_uv_edit: # TODO: generic operator (UV version of `VIEW3D_GGT_tool_generic_handle_free`). widget=None, keymap=(), + options={'KEYMAP_FALLBACK'}, ) @@ -1877,6 +1900,7 @@ class _defs_image_uv_sculpt: operator="sculpt.uv_sculpt_stroke", keymap="Image Editor Tool: Uv, Sculpt Stroke", draw_cursor=draw_cursor, + options={'KEYMAP_FALLBACK'}, ), ) @@ -2403,11 +2427,25 @@ class _defs_node_edit: icon="ops.node.links_cut", widget=None, keymap="Node Tool: Links Cut", + options={'KEYMAP_FALLBACK'}, ) class _defs_sequencer_generic: + @ToolDef.from_fn + def cursor(): + return dict( + idname="builtin.cursor", + label="Cursor", + description=( + "Set the cursor location, drag to transform" + ), + icon="ops.generic.cursor", + keymap="Sequencer Tool: Cursor", + options={'KEYMAP_FALLBACK'}, + ) + @ToolDef.from_fn def blade(): def draw_settings(_context, layout, tool): @@ -2423,6 +2461,7 @@ class _defs_sequencer_generic: widget=None, keymap="Sequencer Tool: Blade", draw_settings=draw_settings, + options={'KEYMAP_FALLBACK'}, ) @ToolDef.from_fn @@ -2437,16 +2476,61 @@ class _defs_sequencer_generic: keymap="Sequencer Tool: Sample", ) + @ToolDef.from_fn + def translate(): + return dict( + idname="builtin.move", + label="Move", + icon="ops.transform.translate", + widget="SEQUENCER_GGT_gizmo2d_translate", + operator="transform.translate", + keymap="Sequencer Tool: Move", + ) + + @ToolDef.from_fn + def rotate(): + return dict( + idname="builtin.rotate", + label="Rotate", + icon="ops.transform.rotate", + widget="SEQUENCER_GGT_gizmo2d_rotate", + operator="transform.rotate", + keymap="Sequencer Tool: Rotate", + ) + + @ToolDef.from_fn + def scale(): + return dict( + idname="builtin.scale", + label="Scale", + icon="ops.transform.resize", + widget="SEQUENCER_GGT_gizmo2d_resize", + operator="transform.resize", + keymap="Sequencer Tool: Scale", + ) + + @ToolDef.from_fn + def transform(): + return dict( + idname="builtin.transform", + label="Transform", + description=( + "Supports any combination of grab, rotate, and scale at once" + ), + icon="ops.transform.transform", + widget="SEQUENCER_GGT_gizmo2d", + # No keymap default action, only for gizmo! + ) class _defs_sequencer_select: @ToolDef.from_fn def select(): return dict( idname="builtin.select", - label="Select", + label="Tweak", icon="ops.generic.select", widget=None, - keymap="Sequencer Tool: Select", + keymap="Sequencer Tool: Tweak", ) @ToolDef.from_fn @@ -3024,6 +3108,14 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): None: [ ], 'PREVIEW': [ + *_tools_select, + _defs_sequencer_generic.cursor, + None, + _defs_sequencer_generic.translate, + _defs_sequencer_generic.rotate, + _defs_sequencer_generic.scale, + _defs_sequencer_generic.transform, + None, _defs_sequencer_generic.sample, *_tools_annotate, ], @@ -3033,9 +3125,17 @@ class SEQUENCER_PT_tools_active(ToolSelectPanelHelper, Panel): ], 'SEQUENCER_PREVIEW': [ *_tools_select, - _defs_sequencer_generic.blade, + _defs_sequencer_generic.cursor, + None, + _defs_sequencer_generic.translate, + _defs_sequencer_generic.rotate, + _defs_sequencer_generic.scale, + _defs_sequencer_generic.transform, + None, _defs_sequencer_generic.sample, *_tools_annotate, + None, + _defs_sequencer_generic.blade, ], } diff --git a/release/scripts/startup/bl_ui/space_topbar.py b/release/scripts/startup/bl_ui/space_topbar.py index 1d75ad8ff0a..518979a5ef3 100644 --- a/release/scripts/startup/bl_ui/space_topbar.py +++ b/release/scripts/startup/bl_ui/space_topbar.py @@ -634,6 +634,8 @@ class TOPBAR_MT_window(Menu): layout = self.layout + operator_context_default = layout.operator_context + layout.operator("wm.window_new") layout.operator("wm.window_new_main") @@ -655,7 +657,14 @@ class TOPBAR_MT_window(Menu): layout.separator() layout.operator("screen.screenshot") + + # Showing the status in the area doesn't work well in this case. + # - From the top-bar, the text replaces the file-menu (not so bad but strange). + # - From menu-search it replaces the area that the user may want to screen-shot. + # Setting the context to screen causes the status to show in the global status-bar. + layout.operator_context = 'INVOKE_SCREEN' layout.operator("screen.screenshot_area") + layout.operator_context = operator_context_default if sys.platform[:3] == "win": layout.separator() diff --git a/release/scripts/startup/bl_ui/space_userpref.py b/release/scripts/startup/bl_ui/space_userpref.py index 57bafe60613..ee27dc4fd63 100644 --- a/release/scripts/startup/bl_ui/space_userpref.py +++ b/release/scripts/startup/bl_ui/space_userpref.py @@ -398,21 +398,26 @@ class USERPREF_PT_edit_objects_duplicate_data(EditingPanel, CenterAlignMixIn, Pa col = flow.column() col.prop(edit, "use_duplicate_action", text="Action") col.prop(edit, "use_duplicate_armature", text="Armature") + col.prop(edit, "use_duplicate_camera", text="Camera") col.prop(edit, "use_duplicate_curve", text="Curve") # col.prop(edit, "use_duplicate_fcurve", text="F-Curve") # Not implemented. col.prop(edit, "use_duplicate_grease_pencil", text="Grease Pencil") if hasattr(edit, "use_duplicate_hair"): col.prop(edit, "use_duplicate_hair", text="Hair") - col.prop(edit, "use_duplicate_light", text="Light") + col = flow.column() + col.prop(edit, "use_duplicate_lattice", text="Lattice") + col.prop(edit, "use_duplicate_light", text="Light") col.prop(edit, "use_duplicate_lightprobe", text="Light Probe") col.prop(edit, "use_duplicate_material", text="Material") col.prop(edit, "use_duplicate_mesh", text="Mesh") col.prop(edit, "use_duplicate_metaball", text="Metaball") - col.prop(edit, "use_duplicate_particle", text="Particle") + col = flow.column() + col.prop(edit, "use_duplicate_particle", text="Particle") if hasattr(edit, "use_duplicate_pointcloud"): col.prop(edit, "use_duplicate_pointcloud", text="Point Cloud") + col.prop(edit, "use_duplicate_speaker", text="Speaker") col.prop(edit, "use_duplicate_surface", text="Surface") col.prop(edit, "use_duplicate_text", text="Text") # col.prop(edit, "use_duplicate_texture", text="Texture") # Not implemented. @@ -920,6 +925,7 @@ class USERPREF_PT_theme_interface_styles(ThemePanel, CenterAlignMixIn, Panel): flow.prop(ui, "editor_outline") flow.prop(ui, "widget_text_cursor") flow.prop(ui, "widget_emboss") + flow.prop(ui, "panel_roundness") class USERPREF_PT_theme_interface_transparent_checker(ThemePanel, CenterAlignMixIn, Panel): @@ -1073,6 +1079,25 @@ class USERPREF_PT_theme_collection_colors(ThemePanel, CenterAlignMixIn, Panel): flow.prop(ui, "color", text=iface_("Color %d") % i, translate=False) +class USERPREF_PT_theme_strip_colors(ThemePanel, CenterAlignMixIn, Panel): + bl_label = "Strip Colors" + bl_options = {'DEFAULT_CLOSED'} + + def draw_header(self, _context): + layout = self.layout + + layout.label(icon='SEQ_STRIP_DUPLICATE') + + def draw_centered(self, context, layout): + theme = context.preferences.themes[0] + + layout.use_property_split = True + + flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=False) + for i, ui in enumerate(theme.strip_color, 1): + flow.prop(ui, "color", text=iface_("Color %d") % i, translate=False) + + # Base class for dynamically defined theme-space panels. # This is not registered. class PreferenceThemeSpacePanel: @@ -1385,12 +1410,18 @@ class USERPREF_PT_file_paths_asset_libraries(FilePathsPanel, Panel): row.label(text="Path") for i, library in enumerate(paths.asset_libraries): - name_col.prop(library, "name", text="") + row = name_col.row() + row.alert = not library.name + row.prop(library, "name", text="") + row = path_col.row() - row.prop(library, "path", text="") + subrow = row.row() + subrow.alert = not library.path + subrow.prop(library, "path", text="") row.operator("preferences.asset_library_remove", text="", icon='X', emboss=False).index = i + row = box.row() - row.alignment = 'LEFT' + row.alignment = 'RIGHT' row.operator("preferences.asset_library_add", text="", icon='ADD', emboss=False) @@ -2250,7 +2281,6 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel): ({"property": "use_sculpt_tools_tilt"}, "T82877"), ({"property": "use_extended_asset_browser"}, ("project/view/130/", "Project Page")), ({"property": "use_override_templates"}, ("T73318", "Milestone 4")), - ({"property": "use_geometry_nodes_fields"}, "T91274"), ), ) @@ -2282,7 +2312,9 @@ class USERPREF_PT_experimental_debugging(ExperimentalPanel, Panel): context, ( ({"property": "use_undo_legacy"}, "T60695"), ({"property": "override_auto_resync"}, "T83811"), + ({"property": "proxy_to_override_auto_conversion"}, "T91671"), ({"property": "use_cycles_debug"}, None), + ({"property": "use_geometry_nodes_legacy"}, "T91274"), ), ) @@ -2346,6 +2378,7 @@ classes = ( USERPREF_PT_theme_text_style, USERPREF_PT_theme_bone_color_sets, USERPREF_PT_theme_collection_colors, + USERPREF_PT_theme_strip_colors, USERPREF_PT_file_paths_data, USERPREF_PT_file_paths_render, diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py index a332295715c..0172fa0655f 100644 --- a/release/scripts/startup/bl_ui/space_view3d.py +++ b/release/scripts/startup/bl_ui/space_view3d.py @@ -46,16 +46,10 @@ class VIEW3D_HT_tool_header(Header): def draw(self, context): layout = self.layout - layout.row(align=True).template_header() - self.draw_tool_settings(context) layout.separator_spacer() - VIEW3D_HT_header.draw_xform_template(layout, context) - - layout.separator_spacer() - self.draw_mode_settings(context) def draw_tool_settings(self, context): @@ -604,10 +598,8 @@ class VIEW3D_HT_header(Header): tool_settings = context.tool_settings view = context.space_data shading = view.shading - show_region_tool_header = view.show_region_tool_header - if not show_region_tool_header: - layout.row(align=True).template_header() + layout.row(align=True).template_header() row = layout.row(align=True) obj = context.active_object @@ -754,7 +746,7 @@ class VIEW3D_HT_header(Header): ) layout.separator_spacer() - elif not show_region_tool_header: + else: # Transform settings depending on tool header visibility VIEW3D_HT_header.draw_xform_template(layout, context) @@ -2234,8 +2226,6 @@ class VIEW3D_MT_object_relations(Menu): def draw(self, _context): layout = self.layout - layout.operator("object.proxy_make", text="Make Proxy...") - layout.operator("object.make_override_library", text="Make Library Override...") layout.operator("object.convert_proxy_to_override") @@ -2275,6 +2265,7 @@ class VIEW3D_MT_object(Menu): layout.separator() + layout.menu("VIEW3D_MT_object_asset") layout.menu("VIEW3D_MT_object_parent") layout.menu("VIEW3D_MT_object_collection") layout.menu("VIEW3D_MT_object_relations") @@ -2768,6 +2759,16 @@ class VIEW3D_MT_object_cleanup(Menu): layout.operator("object.material_slot_remove_unused", text="Remove Unused Material Slots") +class VIEW3D_MT_object_asset(Menu): + bl_label = "Asset" + + def draw(self, _context): + layout = self.layout + + layout.operator("asset.mark") + layout.operator("asset.clear", text="Clear Asset").set_fake_user = False + layout.operator("asset.clear", text="Clear Asset (Set Fake User)").set_fake_user = True + class VIEW3D_MT_make_single_user(Menu): bl_label = "Make Single User" @@ -3059,8 +3060,7 @@ class VIEW3D_MT_sculpt(Menu): layout.separator() - props = layout.operator("object.transfer_mode", text="Transfer Sculpt Mode") - props.use_eyedropper = True + layout.operator("object.transfer_mode", text="Transfer Sculpt Mode") class VIEW3D_MT_mask(Menu): @@ -3453,6 +3453,7 @@ class VIEW3D_MT_pose_slide(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") + layout.operator("pose.blend_to_neighbor") class VIEW3D_MT_pose_propagate(Menu): @@ -3605,11 +3606,14 @@ class VIEW3D_MT_pose_context_menu(Menu): layout.operator("pose.push") layout.operator("pose.relax") layout.operator("pose.breakdown") + layout.operator("pose.blend_to_neighbor") layout.separator() layout.operator("pose.paths_calculate", text="Calculate Motion Paths") layout.operator("pose.paths_clear", text="Clear Motion Paths") + layout.operator("pose.paths_update", text="Update Armature Motion Paths") + layout.operator("object.paths_update_visible", text="Update All Motion Paths") layout.separator() @@ -7548,6 +7552,7 @@ classes = ( VIEW3D_MT_image_add, VIEW3D_MT_object, VIEW3D_MT_object_animation, + VIEW3D_MT_object_asset, VIEW3D_MT_object_rigid_body, VIEW3D_MT_object_clear, VIEW3D_MT_object_context_menu, diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 5c4bd3f3eb4..e8ca4ff9788 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -688,6 +688,39 @@ class VIEW3D_PT_tools_brush_stroke_smooth_stroke(Panel, View3DPaintPanel, Smooth bl_options = {'DEFAULT_CLOSED'} +class VIEW3D_PT_tools_weight_gradient(Panel, View3DPaintPanel): + bl_context = ".weightpaint" # dot on purpose (access from topbar) + bl_label = "Falloff" + bl_options = {'DEFAULT_CLOSED'} + + @classmethod + def poll(cls, context): + settings = context.tool_settings.weight_paint + brush = settings.brush + return brush is not None + + def draw(self, context): + layout = self.layout + settings = context.tool_settings.weight_paint + brush = settings.brush + + col = layout.column(align=True) + row = col.row(align=True) + row.prop(brush, "curve_preset", text="") + + if brush.curve_preset == 'CUSTOM': + layout.template_curve_mapping(brush, "curve", brush=True) + + col = layout.column(align=True) + row = col.row(align=True) + row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' + + # TODO, move to space_view3d.py class VIEW3D_PT_tools_brush_falloff(Panel, View3DPaintPanel, FalloffPanel): bl_context = ".paint_common" # dot on purpose (access from topbar) @@ -2218,6 +2251,7 @@ classes = ( VIEW3D_PT_tools_brush_falloff_frontface, VIEW3D_PT_tools_brush_falloff_normal, VIEW3D_PT_tools_brush_display, + VIEW3D_PT_tools_weight_gradient, VIEW3D_PT_sculpt_dyntopo, VIEW3D_PT_sculpt_voxel_remesh, diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py index ecf07edb9f3..34f447a7108 100644 --- a/release/scripts/startup/nodeitems_builtins.py +++ b/release/scripts/startup/nodeitems_builtins.py @@ -80,6 +80,179 @@ node_tree_group_type = { 'GeometryNodeTree': 'GeometryNodeGroup', } +# Custom Menu for Geometry Node Curves +def curve_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyCurveEndpoints") + yield NodeItem("GeometryNodeLegacyCurveReverse") + yield NodeItem("GeometryNodeLegacyCurveSubdivide") + yield NodeItem("GeometryNodeLegacyCurveToPoints") + yield NodeItem("GeometryNodeLegacyMeshToCurve") + yield NodeItem("GeometryNodeLegacyCurveSelectHandles") + yield NodeItem("GeometryNodeLegacyCurveSetHandles") + yield NodeItem("GeometryNodeLegacyCurveSplineType") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeCurveLength") + yield NodeItem("GeometryNodeCurveToMesh") + yield NodeItem("GeometryNodeCurveToPoints") + yield NodeItem("GeometryNodeFillCurve") + yield NodeItem("GeometryNodeFilletCurve") + yield NodeItem("GeometryNodeResampleCurve") + yield NodeItem("GeometryNodeReverseCurve") + yield NodeItem("GeometryNodeSampleCurve") + yield NodeItem("GeometryNodeSubdivideCurve") + yield NodeItem("GeometryNodeTrimCurve") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputCurveHandlePositions") + yield NodeItem("GeometryNodeCurveParameter") + yield NodeItem("GeometryNodeInputTangent") + yield NodeItem("GeometryNodeInputCurveTilt") + yield NodeItem("GeometryNodeCurveEndpointSelection") + yield NodeItem("GeometryNodeCurveHandleTypeSelection") + yield NodeItem("GeometryNodeInputSplineCyclic") + yield NodeItem("GeometryNodeSplineLength") + yield NodeItem("GeometryNodeInputSplineResolution") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetCurveRadius") + yield NodeItem("GeometryNodeSetCurveTilt") + yield NodeItem("GeometryNodeSetCurveHandlePositions") + yield NodeItem("GeometryNodeCurveSetHandles") + yield NodeItem("GeometryNodeSetSplineCyclic") + yield NodeItem("GeometryNodeSetSplineResolution") + yield NodeItem("GeometryNodeCurveSplineType") + +# Custom Menu for Geometry Node Mesh +def mesh_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyEdgeSplit", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacySubdivisionSurface", poll=geometry_nodes_legacy_poll) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeMeshBoolean") + yield NodeItem("GeometryNodeMeshToCurve") + yield NodeItem("GeometryNodeMeshToPoints") + yield NodeItem("GeometryNodeSplitEdges") + yield NodeItem("GeometryNodeSubdivideMesh") + yield NodeItem("GeometryNodeSubdivisionSurface") + yield NodeItem("GeometryNodeTriangulate") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputShadeSmooth") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetShadeSmooth") + +# Custom Menu for Geometry Nodes "Geometry" category +def geometry_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_legacy_poll) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeBoundBox") + yield NodeItem("GeometryNodeConvexHull") + yield NodeItem("GeometryNodeDeleteGeometry") + yield NodeItem("GeometryNodeProximity") + yield NodeItem("GeometryNodeJoinGeometry") + yield NodeItem("GeometryNodeRaycast") + yield NodeItem("GeometryNodeSeparateComponents") + yield NodeItem("GeometryNodeSeparateGeometry") + yield NodeItem("GeometryNodeTransform") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetID") + yield NodeItem("GeometryNodeSetPosition") + +# Custom Menu for Geometry Node Input Nodes +def geometry_input_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("FunctionNodeLegacyRandomFloat") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("FunctionNodeInputBool") + yield NodeItem("GeometryNodeCollectionInfo") + yield NodeItem("FunctionNodeInputColor") + yield NodeItem("FunctionNodeInputInt") + yield NodeItem("GeometryNodeIsViewport") + yield NodeItem("GeometryNodeInputMaterial") + yield NodeItem("GeometryNodeObjectInfo") + yield NodeItem("FunctionNodeInputString") + yield NodeItem("ShaderNodeValue") + yield NodeItem("FunctionNodeInputVector") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputID") + yield NodeItem("GeometryNodeInputIndex") + yield NodeItem("GeometryNodeInputNormal") + yield NodeItem("GeometryNodeInputPosition") + yield NodeItem("GeometryNodeInputRadius") + +# Custom Menu for Material Nodes +def geometry_material_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyMaterialAssign") + yield NodeItem("GeometryNodeLegacySelectByMaterial") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeReplaceMaterial") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeInputMaterialIndex") + yield NodeItem("GeometryNodeMaterialSelection") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetMaterial") + yield NodeItem("GeometryNodeSetMaterialIndex") + +# Custom Menu for Geometry Node Points +def point_node_items(context): + if context is None: + return + space = context.space_data + if not space: + return + + if geometry_nodes_legacy_poll(context): + yield NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_legacy_poll) + yield NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_legacy_poll) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + + yield NodeItem("GeometryNodeDistributePointsOnFaces") + yield NodeItem("GeometryNodePointsToVertices") + yield NodeItem("GeometryNodePointsToVolume") + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) + yield NodeItem("GeometryNodeSetPointRadius") # generic node group items generator for shader, compositor, geometry and texture node groups def node_group_items(context): @@ -88,15 +261,16 @@ def node_group_items(context): space = context.space_data if not space: return - ntree = space.edit_tree - if not ntree: - return yield NodeItemCustom(draw=group_tools_draw) yield NodeItem("NodeGroupInput", poll=group_input_output_item_poll) yield NodeItem("NodeGroupOutput", poll=group_input_output_item_poll) + ntree = space.edit_tree + if not ntree: + return + yield NodeItemCustom(draw=lambda self, layout, context: layout.separator()) def contains_group(nodetree, group): @@ -180,11 +354,8 @@ def object_eevee_cycles_shader_nodes_poll(context): eevee_cycles_shader_nodes_poll(context)) -def geometry_nodes_fields_poll(context): - return context.preferences.experimental.use_geometry_nodes_fields - -def geometry_nodes_fields_legacy_poll(context): - return not context.preferences.experimental.use_geometry_nodes_fields +def geometry_nodes_legacy_poll(context): + return context.preferences.experimental.use_geometry_nodes_legacy # All standard node categories currently used in nodes. @@ -282,6 +453,7 @@ shader_node_categories = [ ]), ShaderNodeCategory("SH_NEW_CONVERTOR", "Converter", items=[ NodeItem("ShaderNodeMapRange"), + NodeItem("ShaderNodeFloatCurve"), NodeItem("ShaderNodeClamp"), NodeItem("ShaderNodeMath"), NodeItem("ShaderNodeValToRGB"), @@ -483,26 +655,28 @@ texture_node_categories = [ geometry_node_categories = [ # Geometry Nodes GeometryNodeCategory("GEO_ATTRIBUTE", "Attribute", items=[ - NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeRandomize", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMath", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeClamp", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCompare", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeConvert", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCurveMap", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeFill", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMix", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeProximity", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeColorRamp", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeVectorMath", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeVectorRotate", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeSampleTexture", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeCombineXYZ", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeSeparateXYZ", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeMapRange", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyAttributeTransfer", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeAttributeRemove", poll=geometry_nodes_legacy_poll), - NodeItem("GeometryNodeAttributeCapture", poll=geometry_nodes_fields_poll), + NodeItem("GeometryNodeCaptureAttribute"), + NodeItem("GeometryNodeAttributeStatistic"), + NodeItem("GeometryNodeAttributeTransfer"), ]), GeometryNodeCategory("GEO_COLOR", "Color", items=[ NodeItem("ShaderNodeMixRGB"), @@ -511,22 +685,7 @@ geometry_node_categories = [ NodeItem("ShaderNodeSeparateRGB"), NodeItem("ShaderNodeCombineRGB"), ]), - GeometryNodeCategory("GEO_CURVE", "Curve", items=[ - NodeItem("GeometryNodeLegacyCurveSubdivide", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveReverse", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSplineType", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSetHandles", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyCurveSelectHandles", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyMeshToCurve", poll=geometry_nodes_fields_legacy_poll), - - NodeItem("GeometryNodeCurveToMesh"), - NodeItem("GeometryNodeCurveResample"), - NodeItem("GeometryNodeCurveToPoints"), - NodeItem("GeometryNodeCurveEndpoints"), - NodeItem("GeometryNodeCurveFill"), - NodeItem("GeometryNodeCurveTrim"), - NodeItem("GeometryNodeCurveLength"), - ]), + GeometryNodeCategory("GEO_CURVE", "Curve", items=curve_node_items), GeometryNodeCategory("GEO_PRIMITIVES_CURVE", "Curve Primitives", items=[ NodeItem("GeometryNodeCurvePrimitiveLine"), NodeItem("GeometryNodeCurvePrimitiveCircle"), @@ -536,45 +695,18 @@ geometry_node_categories = [ NodeItem("GeometryNodeCurvePrimitiveQuadrilateral"), NodeItem("GeometryNodeCurvePrimitiveBezierSegment"), ]), - GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=[ - NodeItem("GeometryNodeLegacyDeleteGeometry", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyRaycast", poll=geometry_nodes_fields_legacy_poll), - - NodeItem("GeometryNodeBoundBox"), - NodeItem("GeometryNodeConvexHull"), - NodeItem("GeometryNodeTransform"), - NodeItem("GeometryNodeJoinGeometry"), - NodeItem("GeometryNodeSeparateComponents"), - NodeItem("GeometryNodeSetPosition", poll=geometry_nodes_fields_poll), - ]), - GeometryNodeCategory("GEO_INPUT", "Input", items=[ - NodeItem("GeometryNodeObjectInfo"), - NodeItem("GeometryNodeCollectionInfo"), - NodeItem("FunctionNodeRandomFloat"), - NodeItem("ShaderNodeValue"), - NodeItem("FunctionNodeInputString"), - NodeItem("FunctionNodeInputVector"), - NodeItem("GeometryNodeInputMaterial"), - NodeItem("GeometryNodeIsViewport"), - NodeItem("GeometryNodeInputPosition", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeInputIndex", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeInputNormal", poll=geometry_nodes_fields_poll), - ]), - GeometryNodeCategory("GEO_MATERIAL", "Material", items=[ - NodeItem("GeometryNodeLegacyMaterialAssign", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacySelectByMaterial", poll=geometry_nodes_fields_legacy_poll), - - NodeItem("GeometryNodeMaterialAssign", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeMaterialSelection", poll=geometry_nodes_fields_poll), - NodeItem("GeometryNodeMaterialReplace"), - ]), - GeometryNodeCategory("GEO_MESH", "Mesh", items=[ - NodeItem("GeometryNodeBoolean"), - NodeItem("GeometryNodeTriangulate"), - NodeItem("GeometryNodeEdgeSplit"), - NodeItem("GeometryNodeSubdivisionSurface"), - NodeItem("GeometryNodeMeshSubdivide"), + GeometryNodeCategory("GEO_GEOMETRY", "Geometry", items=geometry_node_items), + GeometryNodeCategory("GEO_INPUT", "Input", items=geometry_input_node_items), + GeometryNodeCategory("GEO_INSTANCE", "Instances", items=[ + NodeItem("GeometryNodeInstanceOnPoints"), + NodeItem("GeometryNodeInstancesToPoints"), + NodeItem("GeometryNodeRealizeInstances"), + NodeItem("GeometryNodeRotateInstances"), + NodeItem("GeometryNodeScaleInstances"), + NodeItem("GeometryNodeTranslateInstances"), ]), + GeometryNodeCategory("GEO_MATERIAL", "Material", items=geometry_material_node_items), + GeometryNodeCategory("GEO_MESH", "Mesh", items=mesh_node_items), GeometryNodeCategory("GEO_PRIMITIVES_MESH", "Mesh Primitives", items=[ NodeItem("GeometryNodeMeshCircle"), NodeItem("GeometryNodeMeshCone"), @@ -585,23 +717,43 @@ geometry_node_categories = [ NodeItem("GeometryNodeMeshLine"), NodeItem("GeometryNodeMeshUVSphere"), ]), - GeometryNodeCategory("GEO_POINT", "Point", items=[ - NodeItem("GeometryNodeLegacyPointDistribute", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointInstance", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointSeparate", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointScale", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyPointTranslate", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyRotatePoints", poll=geometry_nodes_fields_legacy_poll), - NodeItem("GeometryNodeLegacyAlignRotationToVector", poll=geometry_nodes_fields_legacy_poll), + GeometryNodeCategory("GEO_OUTPUT", "Output", items=[ + NodeItem("GeometryNodeViewer"), + ]), + GeometryNodeCategory("GEO_POINT", "Point", items=point_node_items), + GeometryNodeCategory("GEO_TEXT", "Text", items=[ + NodeItem("FunctionNodeStringLength"), + NodeItem("FunctionNodeSliceString"), + NodeItem("FunctionNodeValueToString"), + NodeItem("GeometryNodeStringJoin"), + NodeItem("FunctionNodeInputSpecialCharacters"), + NodeItem("GeometryNodeStringToCurves"), + NodeItem("FunctionNodeReplaceString"), + ]), + GeometryNodeCategory("GEO_TEXTURE", "Texture", items=[ + NodeItem("ShaderNodeTexBrick"), + NodeItem("ShaderNodeTexChecker"), + NodeItem("ShaderNodeTexGradient"), + NodeItem("ShaderNodeTexMagic"), + NodeItem("ShaderNodeTexMusgrave"), + NodeItem("ShaderNodeTexNoise"), + NodeItem("ShaderNodeTexVoronoi"), + NodeItem("ShaderNodeTexWave"), + NodeItem("ShaderNodeTexWhiteNoise"), + NodeItem("GeometryNodeImageTexture"), ]), GeometryNodeCategory("GEO_UTILITIES", "Utilities", items=[ NodeItem("ShaderNodeMapRange"), + NodeItem("ShaderNodeFloatCurve"), NodeItem("ShaderNodeClamp"), NodeItem("ShaderNodeMath"), NodeItem("FunctionNodeBooleanMath"), - NodeItem("FunctionNodeFloatCompare"), + NodeItem("FunctionNodeRotateEuler"), + NodeItem("FunctionNodeCompareFloats"), NodeItem("FunctionNodeFloatToInt"), NodeItem("GeometryNodeSwitch"), + NodeItem("FunctionNodeRandomValue"), + NodeItem("FunctionNodeAlignEulerToVector"), ]), GeometryNodeCategory("GEO_VECTOR", "Vector", items=[ NodeItem("ShaderNodeVectorCurve"), @@ -610,11 +762,9 @@ geometry_node_categories = [ NodeItem("ShaderNodeVectorMath"), NodeItem("ShaderNodeVectorRotate"), ]), - GeometryNodeCategory("GEO_OUTPUT", "Output", items=[ - NodeItem("GeometryNodeViewer"), - ]), GeometryNodeCategory("GEO_VOLUME", "Volume", items=[ - NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_fields_legacy_poll), + NodeItem("GeometryNodeLegacyPointsToVolume", poll=geometry_nodes_legacy_poll), + NodeItem("GeometryNodeLegacyVolumeToMesh", poll=geometry_nodes_legacy_poll), NodeItem("GeometryNodeVolumeToMesh"), ]), diff --git a/source/blender/CMakeLists.txt b/source/blender/CMakeLists.txt index fbc0ec440cf..c6112344208 100644 --- a/source/blender/CMakeLists.txt +++ b/source/blender/CMakeLists.txt @@ -119,6 +119,7 @@ add_subdirectory(blenloader) add_subdirectory(depsgraph) add_subdirectory(ikplugin) add_subdirectory(simulation) +add_subdirectory(geometry) add_subdirectory(gpu) add_subdirectory(imbuf) add_subdirectory(nodes) @@ -131,6 +132,10 @@ add_subdirectory(functions) add_subdirectory(makesdna) add_subdirectory(makesrna) +if(WITH_BLENDER_THUMBNAILER) + add_subdirectory(blendthumb) +endif() + if(WITH_COMPOSITOR) add_subdirectory(compositor) endif() @@ -158,7 +163,3 @@ endif() if(WITH_FREESTYLE) add_subdirectory(freestyle) endif() - -if(WIN32) - add_subdirectory(blendthumb) -endif() diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt index b42ca284ecb..4c2e72418a0 100644 --- a/source/blender/blendthumb/CMakeLists.txt +++ b/source/blender/blendthumb/CMakeLists.txt @@ -19,23 +19,48 @@ # ***** END GPL LICENSE BLOCK ***** #----------------------------------------------------------------------------- -include_directories(${ZLIB_INCLUDE_DIRS}) +# Shared Thumbnail Extraction Logic + +include_directories( + ../blenlib + ../makesdna + ../../../intern/guardedalloc +) + +include_directories( + SYSTEM + ${ZLIB_INCLUDE_DIRS} +) set(SRC - src/BlenderThumb.cpp - src/BlendThumb.def - src/BlendThumb.rc - src/Dll.cpp + src/blendthumb.hh + src/blendthumb_extract.cc + src/blendthumb_png.cc ) -string(APPEND CMAKE_SHARED_LINKER_FLAGS_DEBUG " /nodefaultlib:MSVCRT.lib") +if(WIN32) + # ----------------------------------------------------------------------------- + # Build `BlendThumb.dll` -add_library(BlendThumb SHARED ${SRC}) -setup_platform_linker_flags(BlendThumb) -target_link_libraries(BlendThumb ${ZLIB_LIBRARIES}) + set(SRC_WIN32 + src/blendthumb_win32.cc + src/blendthumb_win32.def + src/blendthumb_win32.rc + src/blendthumb_win32_dll.cc + ) -install( - FILES $ - COMPONENT Blender - DESTINATION "." -) + add_definitions(-DNOMINMAX) + + add_library(BlendThumb SHARED ${SRC} ${SRC_WIN32}) + + target_link_libraries(BlendThumb bf_blenlib dbghelp.lib Version.lib) + set_target_properties(BlendThumb PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:msvcrt") + +else() + # ----------------------------------------------------------------------------- + # Build `blender-thumbnailer` executable + + add_executable(blender-thumbnailer ${SRC} src/blender_thumbnailer.cc) + target_link_libraries(blender-thumbnailer bf_blenlib) + target_link_libraries(blender-thumbnailer ${PTHREADS_LIBRARIES}) +endif() diff --git a/source/blender/blendthumb/src/BlenderThumb.cpp b/source/blender/blendthumb/src/BlenderThumb.cpp deleted file mode 100644 index 939e7bbf67c..00000000000 --- a/source/blender/blendthumb/src/BlenderThumb.cpp +++ /dev/null @@ -1,321 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include // For IThumbnailProvider. - -#pragma comment(lib, "shlwapi.lib") - -// this thumbnail provider implements IInitializeWithStream to enable being hosted -// in an isolated process for robustness - -class CBlendThumb : public IInitializeWithStream, public IThumbnailProvider { - public: - CBlendThumb() : _cRef(1), _pStream(NULL) - { - } - - virtual ~CBlendThumb() - { - if (_pStream) { - _pStream->Release(); - } - } - - // IUnknown - IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv) - { - static const QITAB qit[] = { - QITABENT(CBlendThumb, IInitializeWithStream), - QITABENT(CBlendThumb, IThumbnailProvider), - {0}, - }; - return QISearch(this, qit, riid, ppv); - } - - IFACEMETHODIMP_(ULONG) AddRef() - { - return InterlockedIncrement(&_cRef); - } - - IFACEMETHODIMP_(ULONG) Release() - { - ULONG cRef = InterlockedDecrement(&_cRef); - if (!cRef) { - delete this; - } - return cRef; - } - - // IInitializeWithStream - IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode); - - // IThumbnailProvider - IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha); - - private: - long _cRef; - IStream *_pStream; // provided during initialization. -}; - -HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv) -{ - CBlendThumb *pNew = new (std::nothrow) CBlendThumb(); - HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY; - if (SUCCEEDED(hr)) { - hr = pNew->QueryInterface(riid, ppv); - pNew->Release(); - } - return hr; -} - -// IInitializeWithStream -IFACEMETHODIMP CBlendThumb::Initialize(IStream *pStream, DWORD) -{ - HRESULT hr = E_UNEXPECTED; // can only be inited once - if (_pStream == NULL) { - // take a reference to the stream if we have not been inited yet - hr = pStream->QueryInterface(&_pStream); - } - return hr; -} - -#include "Wincodec.h" -#include -#include -const unsigned char gzip_magic[3] = {0x1f, 0x8b, 0x08}; - -// IThumbnailProvider -IFACEMETHODIMP CBlendThumb::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha) -{ - ULONG BytesRead; - HRESULT hr = S_FALSE; - LARGE_INTEGER SeekPos; - - // Compressed? - unsigned char in_magic[3]; - _pStream->Read(&in_magic, 3, &BytesRead); - bool gzipped = true; - for (int i = 0; i < 3; i++) - if (in_magic[i] != gzip_magic[i]) { - gzipped = false; - break; - } - - if (gzipped) { - // Zlib inflate - z_stream stream; - stream.zalloc = Z_NULL; - stream.zfree = Z_NULL; - stream.opaque = Z_NULL; - - // Get compressed file length - SeekPos.QuadPart = 0; - _pStream->Seek(SeekPos, STREAM_SEEK_END, NULL); - - // Get compressed and uncompressed size - uLong source_size; - uLongf dest_size; - // SeekPos.QuadPart = -4; // last 4 bytes define size of uncompressed file - // ULARGE_INTEGER Tell; - //_pStream->Seek(SeekPos,STREAM_SEEK_END,&Tell); - // source_size = (uLong)Tell.QuadPart + 4; // src - //_pStream->Read(&dest_size,4,&BytesRead); // dest - dest_size = 1024 * 70; // thumbnail is currently always inside the first 65KB...if it moves or - // enlargens this line will have to change or go! - source_size = (uLong)max(SeekPos.QuadPart, dest_size); // for safety, assume no compression - - // Input - Bytef *src = new Bytef[source_size]; - stream.next_in = (Bytef *)src; - stream.avail_in = (uInt)source_size; - - // Output - Bytef *dest = new Bytef[dest_size]; - stream.next_out = (Bytef *)dest; - stream.avail_out = dest_size; - - // IStream to src - SeekPos.QuadPart = 0; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - _pStream->Read(src, source_size, &BytesRead); - - // Do the inflation - int err; - err = inflateInit2(&stream, 16); // 16 means "gzip"...nice! - err = inflate(&stream, Z_FINISH); - err = inflateEnd(&stream); - - // Replace the IStream, which is read-only - _pStream->Release(); - _pStream = SHCreateMemStream(dest, dest_size); - - delete[] src; - delete[] dest; - } - - // Blender version, early out if sub 2.5 - SeekPos.QuadPart = 9; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - char version[4]; - version[3] = '\0'; - _pStream->Read(&version, 3, &BytesRead); - if (BytesRead != 3) { - return E_UNEXPECTED; - } - int iVersion = atoi(version); - if (iVersion < 250) { - return S_FALSE; - } - - // 32 or 64 bit blend? - SeekPos.QuadPart = 7; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - - char _PointerSize; - _pStream->Read(&_PointerSize, 1, &BytesRead); - - int PointerSize = _PointerSize == '_' ? 4 : 8; - int HeaderSize = 16 + PointerSize; - - // Find and read thumbnail ("TEST") block - SeekPos.QuadPart = 12; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - int BlockOffset = 12; - while (_pStream) { - // Scan current block - char BlockName[5]; - BlockName[4] = '\0'; - int BlockSize = 0; - - if (_pStream->Read(BlockName, 4, &BytesRead) == S_OK && - _pStream->Read((void *)&BlockSize, 4, &BytesRead) == S_OK) { - if (strcmp(BlockName, "TEST") != 0) { - SeekPos.QuadPart = BlockOffset += HeaderSize + BlockSize; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - continue; - } - } - else { - break; // eof - } - - // Found the block - SeekPos.QuadPart = BlockOffset + HeaderSize; - _pStream->Seek(SeekPos, STREAM_SEEK_SET, NULL); - - int width, height; - _pStream->Read((char *)&width, 4, &BytesRead); - _pStream->Read((char *)&height, 4, &BytesRead); - BlockSize -= 8; - - // Isolate RGBA data - char *pRGBA = new char[BlockSize]; - _pStream->Read(pRGBA, BlockSize, &BytesRead); - - if (BytesRead != (ULONG)BlockSize) { - return E_UNEXPECTED; - } - - // Convert to BGRA for Windows - for (int i = 0; i < BlockSize; i += 4) { -#define RED_BYTE pRGBA[i] -#define BLUE_BYTE pRGBA[i + 2] - - char red = RED_BYTE; - RED_BYTE = BLUE_BYTE; - BLUE_BYTE = red; - } - - // Flip vertically (Blender stores it upside-down) - unsigned int LineSize = width * 4; - char *FlippedImage = new char[BlockSize]; - for (int i = 0; i < height; i++) { - if (0 != memcpy_s(&FlippedImage[(height - i - 1) * LineSize], - LineSize, - &pRGBA[i * LineSize], - LineSize)) { - return E_UNEXPECTED; - } - } - delete[] pRGBA; - pRGBA = FlippedImage; - - // Create image - *phbmp = CreateBitmap(width, height, 1, 32, pRGBA); - if (!*phbmp) { - return E_FAIL; - } - *pdwAlpha = WTSAT_ARGB; // it's actually BGRA, not sure why this works - - // Scale down if required - if ((unsigned)width > cx || (unsigned)height > cx) { - float scale = 1.0f / (max(width, height) / (float)cx); - LONG NewWidth = (LONG)(width * scale); - LONG NewHeight = (LONG)(height * scale); - -#ifdef _DEBUG -# if 0 - MessageBox(0, "Attach now", "Debugging", MB_OK); -# endif -#endif - IWICImagingFactory *pImgFac; - hr = CoCreateInstance( - CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac)); - - IWICBitmap *WICBmp; - hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp, 0, WICBitmapUseAlpha, &WICBmp); - - BITMAPINFO bmi = {}; - bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); - bmi.bmiHeader.biWidth = NewWidth; - bmi.bmiHeader.biHeight = -NewHeight; - bmi.bmiHeader.biPlanes = 1; - bmi.bmiHeader.biBitCount = 32; - bmi.bmiHeader.biCompression = BI_RGB; - - BYTE *pBits; - HBITMAP ResizedHBmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0); - hr = ResizedHBmp ? S_OK : E_OUTOFMEMORY; - if (SUCCEEDED(hr)) { - IWICBitmapScaler *pIScaler; - hr = pImgFac->CreateBitmapScaler(&pIScaler); - hr = pIScaler->Initialize(WICBmp, NewWidth, NewHeight, WICBitmapInterpolationModeFant); - - WICRect rect = {0, 0, NewWidth, NewHeight}; - hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits); - - if (SUCCEEDED(hr)) { - DeleteObject(*phbmp); - *phbmp = ResizedHBmp; - } - else { - DeleteObject(ResizedHBmp); - } - - pIScaler->Release(); - } - WICBmp->Release(); - pImgFac->Release(); - } - else { - hr = S_OK; - } - break; - } - return hr; -} diff --git a/source/blender/blendthumb/src/blender_thumbnailer.cc b/source/blender/blendthumb/src/blender_thumbnailer.cc new file mode 100644 index 00000000000..8dd9d5c0c0a --- /dev/null +++ b/source/blender/blendthumb/src/blender_thumbnailer.cc @@ -0,0 +1,113 @@ +/* + * 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 blendthumb + * + * This file defines the thumbnail generation command (typically used on UNIX). + * + * To run automatically with a file manager such as Nautilus, save this file + * in a directory that is listed in PATH environment variable, and create + * `blender.thumbnailer` file in `${HOME}/.local/share/thumbnailers/` directory + * with the following contents: + * + * \code{.txt} + * [Thumbnailer Entry] + * TryExec=blender-thumbnailer + * Exec=blender-thumbnailer %u %o + * MimeType=application/x-blender; + * \endcode + */ + +#include +#include + +#include +#ifndef WIN32 +# include /* For read close. */ +#else +# include "BLI_winstuff.h" +# include "winsock2.h" +# include /* For open close read. */ +#endif + +#include "BLI_fileops.h" +#include "BLI_filereader.h" +#include "BLI_vector.hh" + +#include "blendthumb.hh" + +/** + * This function opens .blend file from src_blend, extracts thumbnail from file if there is one, + * and writes `.png` image into `dst_png`. + * Returns exit code (0 if successful). + */ +static eThumbStatus extract_png_from_blend_file(const char *src_blend, const char *dst_png) +{ + eThumbStatus err; + + /* Open source file `src_blend`. */ + const int src_file = BLI_open(src_blend, O_BINARY | O_RDONLY, 0); + if (src_file == -1) { + return BT_FILE_ERR; + } + + /* Thumbnail reading is responsible for freeing `file` and closing `src_file`. */ + FileReader *file = BLI_filereader_new_file(src_file); + if (file == nullptr) { + close(src_file); + return BT_FILE_ERR; + } + + /* Extract thumbnail from file. */ + Thumbnail thumb; + err = blendthumb_create_thumb_from_file(file, &thumb); + if (err != BT_OK) { + return err; + } + + /* Write thumbnail to `dst_png`. */ + const int dst_file = BLI_open(dst_png, O_BINARY | O_WRONLY | O_CREAT | O_TRUNC, 0666); + if (dst_file == -1) { + return BT_FILE_ERR; + } + + std::optional> png_buf_opt = blendthumb_create_png_data_from_thumb( + &thumb); + if (png_buf_opt == std::nullopt) { + err = BT_ERROR; + } + else { + blender::Vector png_buf = *png_buf_opt; + err = (write(dst_file, png_buf.data(), png_buf.size()) == png_buf.size()) ? BT_OK : + BT_FILE_ERR; + } + close(dst_file); + + return err; +} + +int main(int argc, char *argv[]) +{ + if (argc < 3) { + std::cerr << "Usage: blender-thumbnailer " << std::endl; + return -1; + } + + eThumbStatus ret = extract_png_from_blend_file(argv[1], argv[2]); + + return (int)ret; +} diff --git a/source/blender/blendthumb/src/blendthumb.hh b/source/blender/blendthumb/src/blendthumb.hh new file mode 100644 index 00000000000..c029a1766d6 --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb.hh @@ -0,0 +1,65 @@ +/* + * 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) 2008-2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup blendthumb + * + * Shared thumbnail extraction logic. + * + * Used for both MS-Windows DLL and Unix command line. + */ + +#pragma once + +#include + +#include "BLI_array.hh" +#include "BLI_vector.hh" + +struct FileReader; + +struct Thumbnail { + blender::Array data; + int width; + int height; +}; + +enum eThumbStatus { + BT_OK = 0, + BT_FILE_ERR = 1, + BT_COMPRES_ERR = 2, + BT_DECOMPRESS_ERR = 3, + BT_INVALID_FILE = 4, + BT_EARLY_VERSION = 5, + BT_INVALID_THUMB = 6, + BT_ERROR = 9 +}; + +std::optional> blendthumb_create_png_data_from_thumb( + const Thumbnail *thumb); +eThumbStatus blendthumb_create_thumb_from_file(struct FileReader *rawfile, Thumbnail *thumb); + +/* INTEGER CODES */ +#ifdef __BIG_ENDIAN__ +/* Big Endian */ +# define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d)) +#else +/* Little Endian */ +# define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a)) +#endif diff --git a/source/blender/blendthumb/src/blendthumb_extract.cc b/source/blender/blendthumb/src/blendthumb_extract.cc new file mode 100644 index 00000000000..f1c5567bab5 --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb_extract.cc @@ -0,0 +1,257 @@ +/* + * 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) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup blendthumb + * + * Expose #blendthumb_create_thumb_from_file that creates the PNG data + * but does not write it to a file. + */ + +#include + +#include "BLI_alloca.h" +#include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_fileops.h" +#include "BLI_filereader.h" +#include "BLI_string.h" + +#include "blendthumb.hh" + +static bool blend_header_check_magic(const char header[12]) +{ + /* Check magic string at start of file. */ + if (!BLI_str_startswith(header, "BLENDER")) { + return false; + } + /* Check pointer size and endianness indicators. */ + if (!ELEM(header[7], '_', '-') || !ELEM(header[8], 'v', 'V')) { + return false; + } + /* Check version number. */ + if (!isdigit(header[9]) || !isdigit(header[10]) || !isdigit(header[11])) { + return false; + } + return true; +} + +static bool blend_header_is_version_valid(const char header[12]) +{ + /* Thumbnails are only in files with version >= 2.50 */ + char num[4]; + memcpy(num, header + 9, 3); + num[3] = 0; + return atoi(num) >= 250; +} + +static int blend_header_pointer_size(const char header[12]) +{ + return header[7] == '_' ? 4 : 8; +} + +static bool blend_header_is_endian_switch_needed(const char header[12]) +{ + return (((header[8] == 'v') ? L_ENDIAN : B_ENDIAN) != ENDIAN_ORDER); +} + +static void thumb_data_vertical_flip(Thumbnail *thumb) +{ + uint32_t *rect = (uint32_t *)thumb->data.data(); + int x = thumb->width, y = thumb->height; + uint32_t *top = rect; + uint32_t *bottom = top + ((y - 1) * x); + uint32_t *line = (uint32_t *)malloc(x * sizeof(uint32_t)); + + y >>= 1; + for (; y > 0; y--) { + memcpy(line, top, x * sizeof(uint32_t)); + memcpy(top, bottom, x * sizeof(uint32_t)); + memcpy(bottom, line, x * sizeof(uint32_t)); + bottom -= x; + top += x; + } + free(line); +} + +static int32_t bytes_to_native_i32(const uint8_t bytes[4], bool endian_switch) +{ + int32_t data; + memcpy(&data, bytes, 4); + if (endian_switch) { + BLI_endian_switch_int32(&data); + } + return data; +} + +static bool file_read(FileReader *file, uint8_t *buf, size_t buf_len) +{ + return (file->read(file, buf, buf_len) == buf_len); +} + +static bool file_seek(FileReader *file, size_t len) +{ + if (file->seek != nullptr) { + if (file->seek(file, len, SEEK_CUR) == -1) { + return false; + } + return true; + } + + /* File doesn't support seeking (e.g. gzip), so read and discard in chunks. */ + constexpr size_t dummy_data_size = 4096; + blender::Array dummy_data(dummy_data_size); + while (len > 0) { + const size_t len_chunk = std::min(len, dummy_data_size); + if ((size_t)file->read(file, dummy_data.data(), len_chunk) != len_chunk) { + return false; + } + len -= len_chunk; + } + return true; +} + +static eThumbStatus blendthumb_extract_from_file_impl(FileReader *file, + Thumbnail *thumb, + const size_t bhead_size, + const bool endian_switch) +{ + /* Iterate over file blocks until we find the thumbnail or run out of data. */ + uint8_t *bhead_data = (uint8_t *)BLI_array_alloca(bhead_data, bhead_size); + while (file_read(file, bhead_data, bhead_size)) { + /* Parse type and size from `BHead`. */ + const int32_t block_size = bytes_to_native_i32(&bhead_data[4], endian_switch); + + /* We're looking for the thumbnail, so skip any other block. */ + switch (*((int32_t *)bhead_data)) { + case MAKE_ID('T', 'E', 'S', 'T'): { + uint8_t shape[8]; + if (!file_read(file, shape, sizeof(shape))) { + return BT_INVALID_THUMB; + } + thumb->width = bytes_to_native_i32(&shape[0], endian_switch); + thumb->height = bytes_to_native_i32(&shape[4], endian_switch); + + /* Verify that image dimensions and data size make sense. */ + size_t data_size = block_size - 8; + const size_t expected_size = thumb->width * thumb->height * 4; + if (thumb->width < 0 || thumb->height < 0 || data_size != expected_size) { + return BT_INVALID_THUMB; + } + + thumb->data = blender::Array(data_size); + if (!file_read(file, thumb->data.data(), data_size)) { + return BT_INVALID_THUMB; + } + return BT_OK; + } + case MAKE_ID('R', 'E', 'N', 'D'): { + if (!file_seek(file, block_size)) { + return BT_INVALID_THUMB; + } + /* Check the next block. */ + break; + } + default: { + /* Early exit if there are no `TEST` or `REND` blocks. + * This saves scanning the entire blend file which could be slow. */ + return BT_INVALID_THUMB; + } + } + } + + return BT_INVALID_THUMB; +} + +/** + * This function extracts the thumbnail from the .blend file into thumb. + * Returns #BT_OK for success and the relevant error code otherwise. + */ +eThumbStatus blendthumb_create_thumb_from_file(FileReader *rawfile, Thumbnail *thumb) +{ + /* Read header in order to identify file type. */ + char header[12]; + if (rawfile->read(rawfile, header, sizeof(header)) != sizeof(header)) { + rawfile->close(rawfile); + return BT_ERROR; + } + + /* Rewind the file after reading the header. */ + rawfile->seek(rawfile, 0, SEEK_SET); + + /* Try to identify the file type from the header. */ + FileReader *file = nullptr; + if (BLI_str_startswith(header, "BLENDER")) { + file = rawfile; + rawfile = nullptr; + } + else if (BLI_file_magic_is_gzip(header)) { + file = BLI_filereader_new_gzip(rawfile); + if (file != nullptr) { + rawfile = nullptr; /* The Gzip #FileReader takes ownership of raw-file. */ + } + } + else if (BLI_file_magic_is_zstd(header)) { + file = BLI_filereader_new_zstd(rawfile); + if (file != nullptr) { + rawfile = nullptr; /* The Zstd #FileReader takes ownership of raw-file. */ + } + } + + /* Clean up rawfile if it wasn't taken over. */ + if (rawfile != nullptr) { + rawfile->close(rawfile); + } + + if (file == nullptr) { + return BT_ERROR; + } + + /* Re-read header in case we had compression. */ + if (file->read(file, header, sizeof(header)) != sizeof(header)) { + file->close(file); + return BT_ERROR; + } + + /* Check if the header format is valid for a .blend file. */ + if (!blend_header_check_magic(header)) { + file->close(file); + return BT_INVALID_FILE; + } + + /* Check if the file is new enough to contain a thumbnail. */ + if (!blend_header_is_version_valid(header)) { + file->close(file); + return BT_EARLY_VERSION; + } + + /* Depending on where it was saved, the file can use different pointer size or endianness. */ + int bhead_size = 16 + blend_header_pointer_size(header); + const bool endian_switch = blend_header_is_endian_switch_needed(header); + + /* Read the thumbnail. */ + eThumbStatus err = blendthumb_extract_from_file_impl(file, thumb, bhead_size, endian_switch); + file->close(file); + if (err != BT_OK) { + return err; + } + + thumb_data_vertical_flip(thumb); + return BT_OK; +} diff --git a/source/blender/blendthumb/src/blendthumb_png.cc b/source/blender/blendthumb/src/blendthumb_png.cc new file mode 100644 index 00000000000..d8156150078 --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb_png.cc @@ -0,0 +1,158 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup blendthumb + * + * Expose #blendthumb_create_png_data_from_thumb that creates the PNG data + * but does not write it to a file. + */ + +#include +#include +#include + +#include "blendthumb.hh" + +#include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_vector.hh" + +static void png_extend_native_int32(blender::Vector &output, int32_t data) +{ + if (ENDIAN_ORDER == L_ENDIAN) { + BLI_endian_switch_int32(&data); + } + output.extend_unchecked(blender::Span((uint8_t *)&data, 4)); +} + +/** The number of bytes each chunk uses on top of the data that's written. */ +#define PNG_CHUNK_EXTRA 12 + +static void png_chunk_create(blender::Vector &output, + const uint32_t tag, + const blender::Vector &data) +{ + uint32_t crc = crc32(0, nullptr, 0); + crc = crc32(crc, (uint8_t *)&tag, sizeof(tag)); + crc = crc32(crc, (uint8_t *)data.data(), data.size()); + + png_extend_native_int32(output, data.size()); + output.extend_unchecked(blender::Span((uint8_t *)&tag, sizeof(tag))); + output.extend_unchecked(data); + png_extend_native_int32(output, crc); +} + +static blender::Vector filtered_rows_from_thumb(const Thumbnail *thumb) +{ + /* In the image data sent to the compression step, each scan-line is preceded by a filter type + * byte containing the numeric code of the filter algorithm used for that scan-line. */ + const size_t line_size = thumb->width * 4; + blender::Vector filtered{}; + size_t final_size = thumb->height * (line_size + 1); + filtered.reserve(final_size); + for (int i = 0; i < thumb->height; i++) { + filtered.append_unchecked(0x00); + filtered.extend_unchecked(blender::Span(&thumb->data[i * line_size], line_size)); + } + BLI_assert(final_size == filtered.size()); + return filtered; +} + +static std::optional> zlib_compress(const blender::Vector &data) +{ + unsigned long uncompressed_size = data.size(); + uLongf compressed_size = compressBound(uncompressed_size); + + blender::Vector compressed(compressed_size, 0x00); + + int return_value = compress2((uchar *)compressed.data(), + &compressed_size, + (uchar *)data.data(), + uncompressed_size, + Z_NO_COMPRESSION); + if (return_value != Z_OK) { + /* Something went wrong with compression of data. */ + return std::nullopt; + } + compressed.resize(compressed_size); + return compressed; +} + +std::optional> blendthumb_create_png_data_from_thumb( + const Thumbnail *thumb) +{ + if (thumb->data.is_empty()) { + return std::nullopt; + } + + /* Create `IDAT` chunk data. */ + blender::Vector image_data{}; + { + auto image_data_opt = zlib_compress(filtered_rows_from_thumb(thumb)); + if (image_data_opt == std::nullopt) { + return std::nullopt; + } + image_data = *image_data_opt; + } + + /* Create the IHDR chunk data. */ + blender::Vector ihdr_data{}; + { + const size_t ihdr_data_final_size = 4 + 4 + 5; + ihdr_data.reserve(ihdr_data_final_size); + png_extend_native_int32(ihdr_data, thumb->width); + png_extend_native_int32(ihdr_data, thumb->height); + ihdr_data.extend_unchecked({ + 0x08, /* Bit Depth. */ + 0x06, /* Color Type. */ + 0x00, /* Compression method. */ + 0x00, /* Filter method. */ + 0x00, /* Interlace method. */ + }); + BLI_assert((size_t)ihdr_data.size() == ihdr_data_final_size); + } + + /* Join it all together to create a PNG image. */ + blender::Vector png_buf{}; + { + const size_t png_buf_final_size = ( + /* Header. */ + 8 + + /* `IHDR` chunk. */ + (ihdr_data.size() + PNG_CHUNK_EXTRA) + + /* `IDAT` chunk. */ + (image_data.size() + PNG_CHUNK_EXTRA) + + /* `IEND` chunk. */ + PNG_CHUNK_EXTRA); + + png_buf.reserve(png_buf_final_size); + + /* This is the standard PNG file header. Every PNG file starts with it. */ + png_buf.extend_unchecked({0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}); + + png_chunk_create(png_buf, MAKE_ID('I', 'H', 'D', 'R'), ihdr_data); + png_chunk_create(png_buf, MAKE_ID('I', 'D', 'A', 'T'), image_data); + png_chunk_create(png_buf, MAKE_ID('I', 'E', 'N', 'D'), {}); + + BLI_assert((size_t)png_buf.size() == png_buf_final_size); + } + + return png_buf; +} diff --git a/source/blender/blendthumb/src/blendthumb_win32.cc b/source/blender/blendthumb/src/blendthumb_win32.cc new file mode 100644 index 00000000000..d757bb1c97e --- /dev/null +++ b/source/blender/blendthumb/src/blendthumb_win32.cc @@ -0,0 +1,237 @@ +/* + * 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 blendthumb + * + * Thumbnail from Blend file extraction for MS-Windows. + */ + +#include +#include +#include +#include +#include /* for #IThumbnailProvider */ + +#include "Wincodec.h" + +#include "blendthumb.hh" + +#include "BLI_filereader.h" + +#pragma comment(lib, "shlwapi.lib") + +/** + * This thumbnail provider implements #IInitializeWithStream to enable being hosted + * in an isolated process for robustness. + */ +class CBlendThumb : public IInitializeWithStream, public IThumbnailProvider { + public: + CBlendThumb() : _cRef(1), _pStream(NULL) + { + } + + virtual ~CBlendThumb() + { + if (_pStream) { + _pStream->Release(); + } + } + + IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv) + { + static const QITAB qit[] = { + QITABENT(CBlendThumb, IInitializeWithStream), + QITABENT(CBlendThumb, IThumbnailProvider), + {0}, + }; + return QISearch(this, qit, riid, ppv); + } + + IFACEMETHODIMP_(ULONG) AddRef() + { + return InterlockedIncrement(&_cRef); + } + + IFACEMETHODIMP_(ULONG) Release() + { + ULONG cRef = InterlockedDecrement(&_cRef); + if (!cRef) { + delete this; + } + return cRef; + } + + /** IInitializeWithStream */ + IFACEMETHODIMP Initialize(IStream *pStream, DWORD grfMode); + + /** IThumbnailProvider */ + IFACEMETHODIMP GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha); + + private: + long _cRef; + IStream *_pStream; /* provided in Initialize(). */ +}; + +HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv) +{ + CBlendThumb *pNew = new (std::nothrow) CBlendThumb(); + HRESULT hr = pNew ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) { + hr = pNew->QueryInterface(riid, ppv); + pNew->Release(); + } + return hr; +} + +IFACEMETHODIMP CBlendThumb::Initialize(IStream *pStream, DWORD) +{ + if (_pStream != NULL) { + /* Can only be initialized once. */ + return E_UNEXPECTED; + } + /* Take a reference to the stream. */ + return pStream->QueryInterface(&_pStream); +} + +/** + * #FileReader compatible wrapper around the Windows stream that gives access to the .blend file. + */ +typedef struct { + FileReader reader; + + IStream *_pStream; +} StreamReader; + +static ssize_t stream_read(FileReader *reader, void *buffer, size_t size) +{ + StreamReader *stream = (StreamReader *)reader; + + ULONG readsize; + stream->_pStream->Read(buffer, size, &readsize); + stream->reader.offset += readsize; + + return (ssize_t)readsize; +} + +static off64_t stream_seek(FileReader *reader, off64_t offset, int whence) +{ + StreamReader *stream = (StreamReader *)reader; + + DWORD origin = STREAM_SEEK_SET; + switch (whence) { + case SEEK_CUR: + origin = STREAM_SEEK_CUR; + break; + case SEEK_END: + origin = STREAM_SEEK_END; + break; + } + LARGE_INTEGER offsetI; + offsetI.QuadPart = offset; + ULARGE_INTEGER newPos; + stream->_pStream->Seek(offsetI, origin, &newPos); + stream->reader.offset = newPos.QuadPart; + + return stream->reader.offset; +} + +static void stream_close(FileReader *reader) +{ + StreamReader *stream = (StreamReader *)reader; + delete stream; +} + +IFACEMETHODIMP CBlendThumb::GetThumbnail(UINT cx, HBITMAP *phbmp, WTS_ALPHATYPE *pdwAlpha) +{ + HRESULT hr = S_FALSE; + + StreamReader *file = new StreamReader; + file->reader.read = stream_read; + file->reader.seek = stream_seek; + file->reader.close = stream_close; + file->reader.offset = 0; + file->_pStream = _pStream; + + file->reader.seek(&file->reader, 0, SEEK_SET); + + /* Extract thumbnail from stream. */ + Thumbnail thumb; + if (blendthumb_create_thumb_from_file(&file->reader, &thumb) != BT_OK) { + return S_FALSE; + } + + /* Convert to BGRA for Windows. */ + for (int i = 0; i < thumb.width * thumb.height; i++) { + std::swap(thumb.data[4 * i], thumb.data[4 * i + 2]); + } + + *phbmp = CreateBitmap(thumb.width, thumb.height, 1, 32, thumb.data.data()); + if (!*phbmp) { + return E_FAIL; + } + *pdwAlpha = WTSAT_ARGB; + + /* Scale down the thumbnail if required. */ + if ((unsigned)thumb.width > cx || (unsigned)thumb.height > cx) { + float scale = 1.0f / (std::max(thumb.width, thumb.height) / (float)cx); + LONG NewWidth = (LONG)(thumb.width * scale); + LONG NewHeight = (LONG)(thumb.height * scale); + + IWICImagingFactory *pImgFac; + hr = CoCreateInstance( + CLSID_WICImagingFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pImgFac)); + + IWICBitmap *WICBmp; + hr = pImgFac->CreateBitmapFromHBITMAP(*phbmp, 0, WICBitmapUseAlpha, &WICBmp); + + BITMAPINFO bmi = {}; + bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader); + bmi.bmiHeader.biWidth = NewWidth; + bmi.bmiHeader.biHeight = -NewHeight; + bmi.bmiHeader.biPlanes = 1; + bmi.bmiHeader.biBitCount = 32; + bmi.bmiHeader.biCompression = BI_RGB; + + BYTE *pBits; + HBITMAP ResizedHBmp = CreateDIBSection(NULL, &bmi, DIB_RGB_COLORS, (void **)&pBits, NULL, 0); + hr = ResizedHBmp ? S_OK : E_OUTOFMEMORY; + if (SUCCEEDED(hr)) { + IWICBitmapScaler *pIScaler; + hr = pImgFac->CreateBitmapScaler(&pIScaler); + hr = pIScaler->Initialize(WICBmp, NewWidth, NewHeight, WICBitmapInterpolationModeFant); + + WICRect rect = {0, 0, NewWidth, NewHeight}; + hr = pIScaler->CopyPixels(&rect, NewWidth * 4, NewWidth * NewHeight * 4, pBits); + + if (SUCCEEDED(hr)) { + DeleteObject(*phbmp); + *phbmp = ResizedHBmp; + } + else { + DeleteObject(ResizedHBmp); + } + + pIScaler->Release(); + } + WICBmp->Release(); + pImgFac->Release(); + } + else { + hr = S_OK; + } + return hr; +} diff --git a/source/blender/blendthumb/src/BlendThumb.def b/source/blender/blendthumb/src/blendthumb_win32.def similarity index 100% rename from source/blender/blendthumb/src/BlendThumb.def rename to source/blender/blendthumb/src/blendthumb_win32.def diff --git a/source/blender/blendthumb/src/BlendThumb.rc b/source/blender/blendthumb/src/blendthumb_win32.rc similarity index 100% rename from source/blender/blendthumb/src/BlendThumb.rc rename to source/blender/blendthumb/src/blendthumb_win32.rc diff --git a/source/blender/blendthumb/src/Dll.cpp b/source/blender/blendthumb/src/blendthumb_win32_dll.cc similarity index 79% rename from source/blender/blendthumb/src/Dll.cpp rename to source/blender/blendthumb/src/blendthumb_win32_dll.cc index 6516540034e..7f10777f884 100644 --- a/source/blender/blendthumb/src/Dll.cpp +++ b/source/blender/blendthumb/src/blendthumb_win32_dll.cc @@ -14,11 +14,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +/** \file + * \ingroup blendthumb + * + * Thumbnail from Blend file extraction for MS-Windows (DLL). + */ + #include #include -#include // For SHChangeNotify +#include /* For #SHChangeNotify */ #include -#include // For IThumbnailProvider. +#include /* For IThumbnailProvider */ extern HRESULT CBlendThumb_CreateInstance(REFIID riid, void **ppv); @@ -33,16 +39,16 @@ struct CLASS_OBJECT_INIT { PFNCREATEINSTANCE pfnCreate; }; -// add classes supported by this module here +/* Add classes supported by this module here. */ const CLASS_OBJECT_INIT c_rgClassObjectInit[] = { {&CLSID_BlendThumbHandler, CBlendThumb_CreateInstance}}; long g_cRefModule = 0; -// Handle the DLL's module -HINSTANCE g_hInst = NULL; +/** Handle the DLL's module */ +HINSTANCE g_hInst = nullptr; -// Standard DLL functions +/** Standard DLL functions. */ STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *) { if (dwReason == DLL_PROCESS_ATTACH) { @@ -54,7 +60,7 @@ STDAPI_(BOOL) DllMain(HINSTANCE hInstance, DWORD dwReason, void *) STDAPI DllCanUnloadNow() { - // Only allow the DLL to be unloaded after all outstanding references have been released + /* Only allow the DLL to be unloaded after all outstanding references have been released. */ return (g_cRefModule == 0) ? S_OK : S_FALSE; } @@ -76,7 +82,7 @@ class CClassFactory : public IClassFactory { REFIID riid, void **ppv) { - *ppv = NULL; + *ppv = nullptr; HRESULT hr = CLASS_E_CLASSNOTAVAILABLE; for (size_t i = 0; i < cClassObjectInits; i++) { if (clsid == *pClassObjectInits[i].pClsid) { @@ -87,7 +93,8 @@ class CClassFactory : public IClassFactory { hr = pClassFactory->QueryInterface(riid, ppv); pClassFactory->Release(); } - break; // match found + /* Match found. */ + break; } } return hr; @@ -98,7 +105,7 @@ class CClassFactory : public IClassFactory { DllAddRef(); } - // IUnknown + /** #IUnknown */ IFACEMETHODIMP QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = {QITABENT(CClassFactory, IClassFactory), {0}}; @@ -119,7 +126,7 @@ class CClassFactory : public IClassFactory { return cRef; } - // IClassFactory + /** #IClassFactory */ IFACEMETHODIMP CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { return punkOuter ? CLASS_E_NOAGGREGATION : _pfnCreate(riid, ppv); @@ -152,33 +159,37 @@ STDAPI DllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv) clsid, c_rgClassObjectInit, ARRAYSIZE(c_rgClassObjectInit), riid, ppv); } -// A struct to hold the information required for a registry entry - +/** + * A struct to hold the information required for a registry entry. + */ struct REGISTRY_ENTRY { HKEY hkeyRoot; PCWSTR pszKeyName; PCWSTR pszValueName; DWORD dwValueType; - PCWSTR pszData; // These two fields could/should have been a union, but C++ - DWORD dwData; // only lets you initialize the first field in a union. + /** These two fields could/should have been a union, but C++ */ + PCWSTR pszData; + /** Only lets you initialize the first field in a union. */ + DWORD dwData; }; -// Creates a registry key (if needed) and sets the default value of the key - +/** + * Creates a registry key (if needed) and sets the default value of the key. + */ HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry) { HKEY hKey; HRESULT hr = HRESULT_FROM_WIN32(RegCreateKeyExW(pRegistryEntry->hkeyRoot, pRegistryEntry->pszKeyName, 0, - NULL, + nullptr, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, - NULL, + nullptr, &hKey, - NULL)); + nullptr)); if (SUCCEEDED(hr)) { - // All this just to support REG_DWORD... + /* All this just to support #REG_DWORD. */ DWORD size; DWORD data; BYTE *lpData = (LPBYTE)pRegistryEntry->pszData; @@ -202,9 +213,9 @@ HRESULT CreateRegKeyAndSetValue(const REGISTRY_ENTRY *pRegistryEntry) return hr; } -// -// Registers this COM server -// +/** + * Registers this COM server. + */ STDAPI DllRegisterServer() { HRESULT hr; @@ -216,15 +227,15 @@ STDAPI DllRegisterServer() } else { const REGISTRY_ENTRY rgRegistryEntries[] = { - // RootKey KeyName ValueName ValueType Data + /* `RootKey KeyName ValueName ValueType Data` */ {HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER, - NULL, + nullptr, REG_SZ, SZ_BLENDTHUMBHANDLER}, {HKEY_CURRENT_USER, L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER L"\\InProcServer32", - NULL, + nullptr, REG_SZ, szModuleName}, {HKEY_CURRENT_USER, @@ -237,10 +248,10 @@ STDAPI DllRegisterServer() L"Treatment", REG_DWORD, 0, - 0}, // doesn't appear to do anything... + 0}, /* This doesn't appear to do anything. */ {HKEY_CURRENT_USER, L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}", - NULL, + nullptr, REG_SZ, SZ_CLSID_BLENDTHUMBHANDLER}, }; @@ -251,17 +262,17 @@ STDAPI DllRegisterServer() } } if (SUCCEEDED(hr)) { - // This tells the shell to invalidate the thumbnail cache. This is important because any - // .blend files viewed before registering this handler would otherwise show cached blank - // thumbnails. - SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL); + /* This tells the shell to invalidate the thumbnail cache. + * This is important because any `.blend` files viewed before registering this handler + * would otherwise show cached blank thumbnails. */ + SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, nullptr, nullptr); } return hr; } -// -// Unregisters this COM server -// +/** + * Unregisters this COM server + */ STDAPI DllUnregisterServer() { HRESULT hr = S_OK; @@ -270,11 +281,11 @@ STDAPI DllUnregisterServer() L"Software\\Classes\\CLSID\\" SZ_CLSID_BLENDTHUMBHANDLER, L"Software\\Classes\\.blend\\ShellEx\\{e357fccd-a995-4576-b01f-234630154e96}"}; - // Delete the registry entries + /* Delete the registry entries. */ for (int i = 0; i < ARRAYSIZE(rgpszKeys) && SUCCEEDED(hr); i++) { hr = HRESULT_FROM_WIN32(RegDeleteTreeW(HKEY_CURRENT_USER, rgpszKeys[i])); if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) { - // If the registry entry has already been deleted, say S_OK. + /* If the registry entry has already been deleted, say S_OK. */ hr = S_OK; } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 27478bd7f8e..a2c778fcf16 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -34,6 +34,7 @@ #include FT_FREETYPE_H #include FT_GLYPH_H +#include FT_ADVANCES_H /* For FT_Get_Advance */ #include "MEM_guardedalloc.h" @@ -297,44 +298,27 @@ static void blf_batch_draw_end(void) * characters. */ -BLI_INLINE GlyphBLF *blf_utf8_next_fast( - FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p, uint *r_c) +BLI_INLINE GlyphBLF *blf_glyph_from_utf8_and_step( + FontBLF *font, GlyphCacheBLF *gc, const char *str, size_t str_len, size_t *i_p) { - GlyphBLF *g; - if ((*r_c = str[*i_p]) < GLYPH_ASCII_TABLE_SIZE) { - g = (gc->glyph_ascii_table)[*r_c]; - if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); - gc->glyph_ascii_table[*r_c] = g; - } - (*i_p)++; - } - else { - *r_c = BLI_str_utf8_as_unicode_step(str, str_len, i_p); - g = blf_glyph_search(gc, *r_c); - if (UNLIKELY(g == NULL)) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, *r_c), *r_c); - } - } - return g; + uint charcode = BLI_str_utf8_as_unicode_step(str, str_len, i_p); + /* Invalid unicode sequences return the byte value, stepping forward one. + * This allows `latin1` to display (which is sometimes used for file-paths). */ + BLI_assert(charcode != BLI_UTF8_ERR); + return blf_glyph_ensure(font, gc, charcode); } -BLI_INLINE void blf_kerning_step_fast(FontBLF *font, - const GlyphBLF *g_prev, - const GlyphBLF *g, - const uint c_prev, - const uint c, - int *pen_x_p) +BLI_INLINE int blf_kerning(FontBLF *font, const GlyphBLF *g_prev, const GlyphBLF *g) { if (!FT_HAS_KERNING(font->face) || g_prev == NULL) { - return; + return 0; } FT_Vector delta = {KERNING_ENTRY_UNSET}; /* Get unscaled kerning value from our cache if ASCII. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - delta.x = font->kerning_cache->ascii_table[c][c_prev]; + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + delta.x = font->kerning_cache->ascii_table[g->c][g_prev->c]; } /* If not ASCII or not found in cache, ask FreeType for kerning. */ @@ -344,14 +328,16 @@ BLI_INLINE void blf_kerning_step_fast(FontBLF *font, } /* If ASCII we save this value to our cache for quicker access next time. */ - if ((c_prev < KERNING_CACHE_TABLE_SIZE) && (c < GLYPH_ASCII_TABLE_SIZE)) { - font->kerning_cache->ascii_table[c][c_prev] = (int)delta.x; + if ((g_prev->c < KERNING_CACHE_TABLE_SIZE) && (g->c < GLYPH_ASCII_TABLE_SIZE)) { + font->kerning_cache->ascii_table[g->c][g_prev->c] = (int)delta.x; } if (delta.x != 0) { /* Convert unscaled design units to pixels and move pen. */ - *pen_x_p += blf_unscaled_F26Dot6_to_pixels(font, delta.x); + return blf_unscaled_F26Dot6_to_pixels(font, delta.x); } + + return 0; } /** \} */ @@ -367,7 +353,6 @@ static void blf_font_draw_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; @@ -380,22 +365,18 @@ static void blf_font_draw_ex(FontBLF *font, blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); /* do not return this loop if clipped, we want every character tested */ - blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); pen_x += g->advance_i; g_prev = g; - c_prev = c; } blf_batch_draw_end(); @@ -415,7 +396,6 @@ void blf_font_draw(FontBLF *font, const char *str, const size_t str_len, struct /* use fixed column width, but an utf8 character may occupy multiple columns */ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int cwidth) { - unsigned int c; GlyphBLF *g; int col, columns = 0; int pen_x = 0, pen_y = 0; @@ -426,19 +406,15 @@ int blf_font_draw_mono(FontBLF *font, const char *str, const size_t str_len, int blf_batch_draw_begin(font); while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - /* do not return this loop if clipped, we want every character tested */ - blf_glyph_render(font, gc, g, (float)pen_x, (float)pen_y); + blf_glyph_draw(font, gc, g, (float)pen_x, (float)pen_y); - col = BLI_wcwidth((char32_t)c); + col = BLI_wcwidth((char32_t)g->c); if (col < 0) { col = 1; } @@ -467,7 +443,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = (int)font->pos[0]; int pen_y_basis = (int)font->pos[1] + pen_y; @@ -483,15 +458,12 @@ static void blf_font_draw_buffer_ex(FontBLF *font, /* another buffer specific call for color conversion */ while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); chx = pen_x + ((int)g->pos[0]); chy = pen_y_basis + g->dims[1]; @@ -588,7 +560,6 @@ static void blf_font_draw_buffer_ex(FontBLF *font, pen_x += g->advance_i; g_prev = g; - c_prev = c; } if (r_info) { @@ -617,31 +588,22 @@ void blf_font_draw_buffer(FontBLF *font, * - #BLF_width_to_rstrlen * \{ */ -static bool blf_font_width_to_strlen_glyph_process(FontBLF *font, - const uint c_prev, - const uint c, - GlyphBLF *g_prev, - GlyphBLF *g, - int *pen_x, - const int width_i) +static bool blf_font_width_to_strlen_glyph_process( + FontBLF *font, GlyphBLF *g_prev, GlyphBLF *g, int *pen_x, const int width_i) { - if (UNLIKELY(c == BLI_UTF8_ERR)) { - return true; /* break the calling loop. */ - } if (UNLIKELY(g == NULL)) { return false; /* continue the calling loop. */ } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, pen_x); - + *pen_x += blf_kerning(font, g_prev, g); *pen_x += g->advance_i; + /* When true, break the calling loop. */ return (*pen_x >= width_i); } size_t blf_font_width_to_strlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; int pen_x, width_new; size_t i, i_prev; @@ -649,11 +611,11 @@ size_t blf_font_width_to_strlen( GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; - for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL, c_prev = 0; (i < str_len) && str[i]; - i_prev = i, width_new = pen_x, c_prev = c, g_prev = g) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + for (i_prev = i = 0, width_new = pen_x = 0, g_prev = NULL; (i < str_len) && str[i]; + i_prev = i, width_new = pen_x, g_prev = g) { + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { break; } } @@ -669,7 +631,6 @@ size_t blf_font_width_to_strlen( size_t blf_font_width_to_rstrlen( FontBLF *font, const char *str, const size_t str_len, float width, float *r_width) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev; int pen_x, width_new; size_t i, i_prev, i_tmp; @@ -685,19 +646,19 @@ size_t blf_font_width_to_rstrlen( i_prev = (size_t)(s_prev - str); i_tmp = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp); for (width_new = pen_x = 0; (s != NULL); - i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { + i = i_prev, s = s_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { s_prev = BLI_str_find_prev_char_utf8(s, str); i_prev = (size_t)(s_prev - str); if (s_prev != NULL) { i_tmp = i_prev; - g_prev = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c_prev); + g_prev = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i_tmp); BLI_assert(i_tmp == i); } - if (blf_font_width_to_strlen_glyph_process(font, c_prev, c, g_prev, g, &pen_x, width_i)) { + if (blf_font_width_to_strlen_glyph_process(font, g_prev, g, &pen_x, width_i)) { break; } } @@ -724,7 +685,6 @@ static void blf_font_boundbox_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0; @@ -736,15 +696,12 @@ static void blf_font_boundbox_ex(FontBLF *font, box->ymax = -32000.0f; while ((i < str_len) && str[i]) { - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); gbox.xmin = (float)pen_x; gbox.xmax = (float)pen_x + g->advance; @@ -767,7 +724,6 @@ static void blf_font_boundbox_ex(FontBLF *font, pen_x += g->advance_i; g_prev = g; - c_prev = c; } if (box->xmin > box->xmax) { @@ -869,22 +825,7 @@ float blf_font_height(FontBLF *font, float blf_font_fixed_width(FontBLF *font) { - const unsigned int c = ' '; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - GlyphBLF *g = blf_glyph_search(gc, c); - if (!g) { - g = blf_glyph_add(font, gc, FT_Get_Char_Index(font->face, c), c); - - /* if we don't find the glyph. */ - if (!g) { - blf_glyph_cache_release(font); - return 0.0f; - } - } - - blf_glyph_cache_release(font); - return g->advance; + return (float)font->fixed_width; } static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, @@ -896,7 +837,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, struct ResultBLF *r_info, int pen_y) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0; size_t i = 0, i_curr; @@ -909,15 +849,12 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, while ((i < str_len) && str[i]) { i_curr = i; - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); gbox.xmin = pen_x; gbox.xmax = gbox.xmin + MIN2(g->advance_i, g->dims[0]); @@ -931,7 +868,6 @@ static void blf_font_boundbox_foreach_glyph_ex(FontBLF *font, } g_prev = g; - c_prev = c; } if (r_info) { @@ -978,7 +914,6 @@ static void blf_font_wrap_apply(FontBLF *font, void *userdata), void *userdata) { - unsigned int c, c_prev = BLI_UTF8_ERR; GlyphBLF *g, *g_prev = NULL; int pen_x = 0, pen_y = 0; size_t i = 0; @@ -999,15 +934,12 @@ static void blf_font_wrap_apply(FontBLF *font, size_t i_curr = i; bool do_draw = false; - g = blf_utf8_next_fast(font, gc, str, str_len, &i, &c); + g = blf_glyph_from_utf8_and_step(font, gc, str, str_len, &i); - if (UNLIKELY(c == BLI_UTF8_ERR)) { - break; - } if (UNLIKELY(g == NULL)) { continue; } - blf_kerning_step_fast(font, g_prev, g, c_prev, c, &pen_x); + pen_x += blf_kerning(font, g_prev, g); /** * Implementation Detail (utf8). @@ -1045,16 +977,14 @@ static void blf_font_wrap_apply(FontBLF *font, wrap.start = wrap.last[0]; i = wrap.last[1]; pen_x = 0; - pen_y -= gc->glyph_height_max; + pen_y -= blf_font_height_max(font); g_prev = NULL; - c_prev = BLI_UTF8_ERR; lines += 1; continue; } pen_x = pen_x_next; g_prev = g; - c_prev = c; } // printf("done! lines: %d, width, %d\n", lines, pen_x_next); @@ -1170,45 +1100,41 @@ int blf_font_count_missing_chars(FontBLF *font, int blf_font_height_max(FontBLF *font) { int height_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - height_max = gc->glyph_height_max; - - blf_glyph_cache_release(font); - return height_max; + if (FT_IS_SCALABLE(font->face)) { + height_max = (int)((float)(font->face->ascender - font->face->descender) * + (((float)font->face->size->metrics.y_ppem) / + ((float)font->face->units_per_EM))); + } + else { + height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); + } + /* can happen with size 1 fonts */ + return MAX2(height_max, 1); } int blf_font_width_max(FontBLF *font) { int width_max; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - width_max = gc->glyph_width_max; - - blf_glyph_cache_release(font); - return width_max; + if (FT_IS_SCALABLE(font->face)) { + width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) * + (((float)font->face->size->metrics.x_ppem) / + ((float)font->face->units_per_EM))); + } + else { + width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); + } + /* can happen with size 1 fonts */ + return MAX2(width_max, 1); } float blf_font_descender(FontBLF *font) { - float descender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - descender = gc->descender; - - blf_glyph_cache_release(font); - return descender; + return ((float)font->face->size->metrics.descender) / 64.0f; } float blf_font_ascender(FontBLF *font) { - float ascender; - - GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); - ascender = gc->ascender; - - blf_glyph_cache_release(font); - return ascender; + return ((float)font->face->size->metrics.ascender) / 64.0f; } char *blf_display_name(FontBLF *font) @@ -1439,6 +1365,22 @@ void blf_font_size(FontBLF *font, unsigned int size, unsigned int dpi) } blf_glyph_cache_release(font); + + /* Set fixed-width size for monospaced output. */ + FT_UInt gindex = FT_Get_Char_Index(font->face, U'0'); + if (gindex) { + FT_Fixed advance = 0; + FT_Get_Advance(font->face, gindex, FT_LOAD_NO_HINTING, &advance); + /* Use CSS 'ch unit' width, advance of zero character. */ + font->fixed_width = (int)(advance >> 16); + } + else { + /* Font does not contain "0" so use CSS fallback of 1/2 of em. */ + font->fixed_width = (int)((font->face->size->metrics.height / 2) >> 6); + } + if (font->fixed_width < 1) { + font->fixed_width = 1; + } } /** \} */ diff --git a/source/blender/blenfont/intern/blf_glyph.c b/source/blender/blenfont/intern/blf_glyph.c index 6cdf5fc5996..c4ffb3f87f1 100644 --- a/source/blender/blenfont/intern/blf_glyph.c +++ b/source/blender/blenfont/intern/blf_glyph.c @@ -86,27 +86,6 @@ GlyphCacheBLF *blf_glyph_cache_new(FontBLF *font) memset(gc->glyph_ascii_table, 0, sizeof(gc->glyph_ascii_table)); memset(gc->bucket, 0, sizeof(gc->bucket)); - gc->ascender = ((float)font->face->size->metrics.ascender) / 64.0f; - gc->descender = ((float)font->face->size->metrics.descender) / 64.0f; - - if (FT_IS_SCALABLE(font->face)) { - gc->glyph_width_max = (int)((float)(font->face->bbox.xMax - font->face->bbox.xMin) * - (((float)font->face->size->metrics.x_ppem) / - ((float)font->face->units_per_EM))); - - gc->glyph_height_max = (int)((float)(font->face->bbox.yMax - font->face->bbox.yMin) * - (((float)font->face->size->metrics.y_ppem) / - ((float)font->face->units_per_EM))); - } - else { - gc->glyph_width_max = (int)(((float)font->face->size->metrics.max_advance) / 64.0f); - gc->glyph_height_max = (int)(((float)font->face->size->metrics.height) / 64.0f); - } - - /* can happen with size 1 fonts */ - CLAMP_MIN(gc->glyph_width_max, 1); - CLAMP_MIN(gc->glyph_height_max, 1); - BLI_addhead(&font->cache, gc); return gc; } @@ -159,7 +138,7 @@ void blf_glyph_cache_free(GlyphCacheBLF *gc) MEM_freeN(gc); } -GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) +static GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) { GlyphBLF *p; unsigned int key; @@ -175,33 +154,8 @@ GlyphBLF *blf_glyph_search(GlyphCacheBLF *gc, unsigned int c) return NULL; } -GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, unsigned int c) +static bool blf_glyph_render(FontBLF *font, FT_UInt glyph_index) { - FT_GlyphSlot slot; - GlyphBLF *g; - FT_Error err; - FT_Bitmap bitmap, tempbitmap; - FT_BBox bbox; - unsigned int key; - - g = blf_glyph_search(gc, c); - if (g) { - return g; - } - - /* glyphs are dynamically created as needed by font rendering. this means that - * to make font rendering thread safe we have to do locking here. note that this - * must be a lock for the whole library and not just per font, because the font - * renderer uses a shared buffer internally */ - BLI_spin_lock(font->ft_lib_mutex); - - /* search again after locking */ - g = blf_glyph_search(gc, c); - if (g) { - BLI_spin_unlock(font->ft_lib_mutex); - return g; - } - int load_flags; int render_mode; @@ -228,7 +182,10 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } } - err = FT_Load_Glyph(font->face, (FT_UInt)index, load_flags); + FT_Error err = FT_Load_Glyph(font->face, glyph_index, load_flags); + if (err != 0) { + return false; + } /* Do not oblique a font that is designed to be italic! */ if (((font->flags & BLF_ITALIC) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_ITALIC) && @@ -243,9 +200,8 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } /* Do not embolden an already bold font! */ - if (((font->flags & BLF_BOLD) != 0) && - !(font->face->style_flags & FT_STYLE_FLAG_BOLD) & - (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) { + if (((font->flags & BLF_BOLD) != 0) && !(font->face->style_flags & FT_STYLE_FLAG_BOLD) && + (font->face->glyph->format == FT_GLYPH_FORMAT_OUTLINE)) { /* Strengthen the width more than the height. */ const FT_Pos extra_x = FT_MulFix(font->face->units_per_EM, font->face->size->metrics.x_scale) / 14; @@ -263,15 +219,12 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } } - if (err) { - BLI_spin_unlock(font->ft_lib_mutex); - return NULL; - } - /* get the glyph. */ - slot = font->face->glyph; + FT_GlyphSlot slot = font->face->glyph; err = FT_Render_Glyph(slot, render_mode); + FT_Bitmap tempbitmap; + if (font->flags & BLF_MONOCHROME) { /* Convert result from 1 bit per pixel to 8 bit per pixel */ /* Accum errors for later, fine if not interested beyond "ok vs any error" */ @@ -284,45 +237,69 @@ GlyphBLF *blf_glyph_add(FontBLF *font, GlyphCacheBLF *gc, unsigned int index, un } if (err || slot->format != FT_GLYPH_FORMAT_BITMAP) { - BLI_spin_unlock(font->ft_lib_mutex); + return false; + } + + return true; +} + +GlyphBLF *blf_glyph_ensure(FontBLF *font, GlyphCacheBLF *gc, uint charcode) +{ + GlyphBLF *g = (charcode < GLYPH_ASCII_TABLE_SIZE) ? (gc->glyph_ascii_table)[charcode] : + blf_glyph_search(gc, charcode); + if (g) { + return g; + } + + FT_UInt glyph_index = FT_Get_Char_Index(font->face, charcode); + + if (!blf_glyph_render(font, glyph_index)) { return NULL; } - g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_add"); - g->c = c; - g->idx = (FT_UInt)index; - bitmap = slot->bitmap; - g->dims[0] = (int)bitmap.width; - g->dims[1] = (int)bitmap.rows; + FT_GlyphSlot slot = font->face->glyph; - const int buffer_size = g->dims[0] * g->dims[1]; - - if (buffer_size != 0) { - if (font->flags & BLF_MONOCHROME) { - /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ - for (int i = 0; i < buffer_size; i++) { - bitmap.buffer[i] = bitmap.buffer[i] ? 255 : 0; - } - } - - g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); - memcpy(g->bitmap, bitmap.buffer, (size_t)buffer_size); - } + /* glyphs are dynamically created as needed by font rendering. this means that + * to make font rendering thread safe we have to do locking here. note that this + * must be a lock for the whole library and not just per font, because the font + * renderer uses a shared buffer internally */ + BLI_spin_lock(font->ft_lib_mutex); + g = (GlyphBLF *)MEM_callocN(sizeof(GlyphBLF), "blf_glyph_get"); + g->c = charcode; + g->idx = glyph_index; g->advance = ((float)slot->advance.x) / 64.0f; g->advance_i = (int)g->advance; g->pos[0] = slot->bitmap_left; g->pos[1] = slot->bitmap_top; + g->dims[0] = (int)slot->bitmap.width; + g->dims[1] = (int)slot->bitmap.rows; g->pitch = slot->bitmap.pitch; + FT_BBox bbox; FT_Outline_Get_CBox(&(slot->outline), &bbox); g->box.xmin = ((float)bbox.xMin) / 64.0f; g->box.xmax = ((float)bbox.xMax) / 64.0f; g->box.ymin = ((float)bbox.yMin) / 64.0f; g->box.ymax = ((float)bbox.yMax) / 64.0f; - key = blf_hash(g->c); + const int buffer_size = (int)(slot->bitmap.width * slot->bitmap.rows); + if (buffer_size != 0) { + if (font->flags & BLF_MONOCHROME) { + /* Font buffer uses only 0 or 1 values, Blender expects full 0..255 range */ + for (int i = 0; i < buffer_size; i++) { + slot->bitmap.buffer[i] = slot->bitmap.buffer[i] ? 255 : 0; + } + } + g->bitmap = MEM_mallocN((size_t)buffer_size, "glyph bitmap"); + memcpy(g->bitmap, slot->bitmap.buffer, (size_t)buffer_size); + } + + unsigned int key = blf_hash(g->c); BLI_addhead(&(gc->bucket[key]), g); + if (charcode < GLYPH_ASCII_TABLE_SIZE) { + gc->glyph_ascii_table[charcode] = g; + } BLI_spin_unlock(font->ft_lib_mutex); @@ -419,7 +396,7 @@ static void blf_glyph_calc_rect_shadow(rctf *rect, GlyphBLF *g, float x, float y blf_glyph_calc_rect(rect, g, x + (float)font->shadow_x, y + (float)font->shadow_y); } -void blf_glyph_render(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) +void blf_glyph_draw(FontBLF *font, GlyphCacheBLF *gc, GlyphBLF *g, float x, float y) { if ((!g->dims[0]) || (!g->dims[1])) { return; diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 6fd5e8b7503..a715d5df692 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -139,14 +139,10 @@ void blf_glyph_cache_release(struct FontBLF *font); void blf_glyph_cache_clear(struct FontBLF *font); void blf_glyph_cache_free(struct GlyphCacheBLF *gc); -struct GlyphBLF *blf_glyph_search(struct GlyphCacheBLF *gc, unsigned int c); -struct GlyphBLF *blf_glyph_add(struct FontBLF *font, - struct GlyphCacheBLF *gc, - unsigned int index, - unsigned int c); +struct GlyphBLF *blf_glyph_ensure(struct FontBLF *font, struct GlyphCacheBLF *gc, uint charcode); void blf_glyph_free(struct GlyphBLF *g); -void blf_glyph_render( +void blf_glyph_draw( struct FontBLF *font, struct GlyphCacheBLF *gc, struct GlyphBLF *g, float x, float y); #ifdef WIN32 diff --git a/source/blender/blenfont/intern/blf_internal_types.h b/source/blender/blenfont/intern/blf_internal_types.h index 38d7d7b6e21..aae666fa182 100644 --- a/source/blender/blenfont/intern/blf_internal_types.h +++ b/source/blender/blenfont/intern/blf_internal_types.h @@ -86,13 +86,6 @@ typedef struct GlyphCacheBLF { int bitmap_len_landed; int bitmap_len_alloc; - /* and the bigger glyph in the font. */ - int glyph_width_max; - int glyph_height_max; - - /* ascender and descender value. */ - float ascender; - float descender; } GlyphCacheBLF; typedef struct GlyphBLF { @@ -152,7 +145,7 @@ typedef struct FontBufInfoBLF { struct ColorManagedDisplay *display; /* and the color, the alphas is get from the glyph! - * color is srgb space */ + * color is sRGB space */ float col_init[4]; /* cached conversion from 'col_init' */ unsigned char col_char[4]; @@ -214,6 +207,9 @@ typedef struct FontBLF { /* font size. */ unsigned int size; + /* Column width when printing monospaced. */ + int fixed_width; + /* max texture size. */ int tex_size_max; diff --git a/source/blender/blenfont/intern/blf_thumbs.c b/source/blender/blenfont/intern/blf_thumbs.c index 3153a55b697..12a83f7634e 100644 --- a/source/blender/blenfont/intern/blf_thumbs.c +++ b/source/blender/blenfont/intern/blf_thumbs.c @@ -106,7 +106,7 @@ void BLF_thumb_preview(const char *filename, font_size_curr -= (font_size_curr / font_shrink); font_shrink += 1; - font->pos[1] -= gc->ascender * 1.1f; + font->pos[1] -= blf_font_ascender(font) * 1.1f; /* We fallback to default english strings in case not enough chars are available in current * font for given translated string (useful in non-latin i18n context, like Chinese, diff --git a/source/blender/blenkernel/BKE_DerivedMesh.h b/source/blender/blenkernel/BKE_DerivedMesh.h index e3954e134da..c95190d2c83 100644 --- a/source/blender/blenkernel/BKE_DerivedMesh.h +++ b/source/blender/blenkernel/BKE_DerivedMesh.h @@ -157,15 +157,6 @@ struct DerivedMesh { int (*getNumLoops)(DerivedMesh *dm); int (*getNumPolys)(DerivedMesh *dm); - /** Copy a single vert/edge/tessellated face from the derived mesh into - * `*r_{vert/edge/face}`. note that the current implementation - * of this function can be quite slow, iterating over all - * elements (editmesh) - */ - void (*getVert)(DerivedMesh *dm, int index, struct MVert *r_vert); - void (*getEdge)(DerivedMesh *dm, int index, struct MEdge *r_edge); - void (*getTessFace)(DerivedMesh *dm, int index, struct MFace *r_face); - /** Return a pointer to the entire array of verts/edges/face from the * derived mesh. if such an array does not exist yet, it will be created, * and freed on the next ->release(). consider using getVert/Edge/Face if diff --git a/source/blender/blenkernel/BKE_appdir.h b/source/blender/blenkernel/BKE_appdir.h index fee52479cd0..65485058dd7 100644 --- a/source/blender/blenkernel/BKE_appdir.h +++ b/source/blender/blenkernel/BKE_appdir.h @@ -16,11 +16,13 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke */ #include +#include "BLI_compiler_attrs.h" + #ifdef __cplusplus extern "C" { #endif @@ -32,9 +34,12 @@ void BKE_appdir_exit(void); /* note on naming: typical _get() suffix is omitted here, * since its the main purpose of the API. */ -const char *BKE_appdir_folder_default(void); +const char *BKE_appdir_folder_default(void) ATTR_WARN_UNUSED_RESULT; +const char *BKE_appdir_folder_root(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; +const char *BKE_appdir_folder_default_or_root(void) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL; const char *BKE_appdir_folder_home(void); bool BKE_appdir_folder_documents(char *dir); +bool BKE_appdir_folder_caches(char *r_path, size_t path_len); bool BKE_appdir_folder_id_ex(const int folder_id, const char *subfolder, char *path, diff --git a/source/blender/blenkernel/BKE_asset.h b/source/blender/blenkernel/BKE_asset.h index 50eb2859279..722d142b56c 100644 --- a/source/blender/blenkernel/BKE_asset.h +++ b/source/blender/blenkernel/BKE_asset.h @@ -20,18 +20,33 @@ #pragma once +#include "BLI_compiler_attrs.h" #include "BLI_utildefines.h" +#include "DNA_asset_types.h" + #ifdef __cplusplus extern "C" { #endif struct AssetLibraryReference; +struct AssetMetaData; struct BlendDataReader; struct BlendWriter; struct ID; +struct IDProperty; struct PreviewImage; +typedef void (*PreSaveFn)(void *asset_ptr, struct AssetMetaData *asset_data); + +typedef struct AssetTypeInfo { + /** + * For local assets (assets in the current .blend file), a callback to execute before the file is + * saved. + */ + PreSaveFn pre_save_fn; +} AssetTypeInfo; + struct AssetMetaData *BKE_asset_metadata_create(void); void BKE_asset_metadata_free(struct AssetMetaData **asset_data); @@ -46,8 +61,18 @@ struct AssetTagEnsureResult BKE_asset_metadata_tag_ensure(struct AssetMetaData * const char *name); void BKE_asset_metadata_tag_remove(struct AssetMetaData *asset_data, struct AssetTag *tag); +/** Clean up the catalog ID (white-spaces removed, length reduced, etc.) and assign it. */ +void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data); +void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, + bUUID catalog_id, + const char *catalog_simple_name); + void BKE_asset_library_reference_init_default(struct AssetLibraryReference *library_ref); +void BKE_asset_metadata_idprop_ensure(struct AssetMetaData *asset_data, struct IDProperty *prop); +struct IDProperty *BKE_asset_metadata_idprop_find(const struct AssetMetaData *asset_data, + const char *name) ATTR_WARN_UNUSED_RESULT; + struct PreviewImage *BKE_asset_metadata_preview_get_from_id(const struct AssetMetaData *asset_data, const struct ID *owner_id); diff --git a/source/blender/blenkernel/BKE_asset_catalog.hh b/source/blender/blenkernel/BKE_asset_catalog.hh new file mode 100644 index 00000000000..766a3f34a66 --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_catalog.hh @@ -0,0 +1,514 @@ +/* + * 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 + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++ header. The C interface is yet to be implemented/designed. +#endif + +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_string_ref.hh" +#include "BLI_uuid.h" +#include "BLI_vector.hh" + +#include "BKE_asset_catalog_path.hh" + +#include +#include +#include +#include + +namespace blender::bke { + +class AssetCatalog; +class AssetCatalogCollection; +class AssetCatalogDefinitionFile; +class AssetCatalogFilter; +class AssetCatalogTree; + +using CatalogID = bUUID; +using CatalogPathComponent = std::string; +/* Would be nice to be able to use `std::filesystem::path` for this, but it's currently not + * available on the minimum macOS target version. */ +using CatalogFilePath = std::string; +using OwningAssetCatalogMap = Map>; + +/* Manages the asset catalogs of a single asset library (i.e. of catalogs defined in a single + * directory hierarchy). */ +class AssetCatalogService { + public: + static const CatalogFilePath DEFAULT_CATALOG_FILENAME; + + public: + AssetCatalogService(); + explicit AssetCatalogService(const CatalogFilePath &asset_library_root); + + /** + * Set tag indicating that some catalog modifications are unsaved, which could + * get lost on exit. This tag is not set by internal catalog code, the catalog + * service user is responsible for it. It is cleared by #write_to_disk(). + * + * This "dirty" state is tracked per catalog, so that it's possible to gracefully load changes + * from disk. Any catalog with unsaved changes will not be overwritten by on-disk changes. */ + void tag_has_unsaved_changes(AssetCatalog *edited_catalog); + bool has_unsaved_changes() const; + + /** Load asset catalog definitions from the files found in the asset library. */ + void load_from_disk(); + /** Load asset catalog definitions from the given file or directory. */ + void load_from_disk(const CatalogFilePath &file_or_directory_path); + + /** + * Write the catalog definitions to disk. + * + * The location where the catalogs are saved is variable, and depends on the location of the + * blend file. The first matching rule wins: + * + * - Already loaded a CDF from disk? + * -> Always write to that file. + * - The directory containing the blend file has a blender_assets.cats.txt file? + * -> Merge with & write to that file. + * - The directory containing the blend file is part of an asset library, as per + * the user's preferences? + * -> Merge with & write to ${ASSET_LIBRARY_ROOT}/blender_assets.cats.txt + * - Create a new file blender_assets.cats.txt next to the blend file. + * + * Return true on success, which either means there were no in-memory categories to save, + * or the save was successful. */ + bool write_to_disk(const CatalogFilePath &blend_file_path); + + /** + * Merge on-disk changes into the in-memory asset catalogs. + * This should be called before writing the asset catalogs to disk. + * + * - New on-disk catalogs are loaded into memory. + * - Already-known on-disk catalogs are ignored (so will be overwritten with our in-memory + * data). This includes in-memory marked-as-deleted catalogs. + */ + void reload_catalogs(); + + /** Return catalog with the given ID. Return nullptr if not found. */ + AssetCatalog *find_catalog(CatalogID catalog_id) const; + + /** + * Return first catalog with the given path. Return nullptr if not found. This is not an + * efficient call as it's just a linear search over the catalogs. + * + * If there are multiple catalogs with the same path, return the first-loaded one. If there is + * none marked as "first loaded", return the one with the lowest UUID. */ + AssetCatalog *find_catalog_by_path(const AssetCatalogPath &path) const; + + /** + * Return true only if this catalog is known. + * This treats deleted catalogs as "unknown". */ + bool is_catalog_known(CatalogID catalog_id) const; + + /** + * Create a filter object that can be used to determine whether an asset belongs to the given + * catalog, or any of the catalogs in the sub-tree rooted at the given catalog. + * + * \see #AssetCatalogFilter + */ + AssetCatalogFilter create_catalog_filter(CatalogID active_catalog_id) const; + + /** Create a catalog with some sensible auto-generated catalog ID. + * The catalog will be saved to the default catalog file.*/ + AssetCatalog *create_catalog(const AssetCatalogPath &catalog_path); + + /** + * Delete all catalogs with the given path, and their children. + */ + void prune_catalogs_by_path(const AssetCatalogPath &path); + + /** + * Delete all catalogs with the same path as the identified catalog, and their children. + * This call is the same as calling `prune_catalogs_by_path(find_catalog(catalog_id)->path)`. + */ + void prune_catalogs_by_id(CatalogID catalog_id); + + /** + * Update the catalog path, also updating the catalog path of all sub-catalogs. + */ + void update_catalog_path(CatalogID catalog_id, const AssetCatalogPath &new_catalog_path); + + AssetCatalogTree *get_catalog_tree(); + + /** Return true only if there are no catalogs known. */ + bool is_empty() const; + + /** + * Store the current catalogs in the undo stack. + * This snapshots everything in the #AssetCatalogCollection. */ + void undo_push(); + /** + * Restore the last-saved undo snapshot, pushing the current state onto the redo stack. + * The caller is responsible for first checking that undoing is possible. + */ + void undo(); + bool is_undo_possbile() const; + /** + * Restore the last-saved redo snapshot, pushing the current state onto the undo stack. + * The caller is responsible for first checking that undoing is possible. */ + void redo(); + bool is_redo_possbile() const; + + protected: + std::unique_ptr catalog_collection_; + std::unique_ptr catalog_tree_ = std::make_unique(); + CatalogFilePath asset_library_root_; + + Vector> undo_snapshots_; + Vector> redo_snapshots_; + + void load_directory_recursive(const CatalogFilePath &directory_path); + void load_single_file(const CatalogFilePath &catalog_definition_file_path); + + /** Implementation of #write_to_disk() that doesn't clear the "has unsaved changes" tag. */ + bool write_to_disk_ex(const CatalogFilePath &blend_file_path); + void untag_has_unsaved_changes(); + bool is_catalog_known_with_unsaved_changes(CatalogID catalog_id) const; + + /** + * Delete catalogs, only keeping them when they are either listed in + * \a catalogs_to_keep or have unsaved changes. + * + * \note Deleted catalogs are hard-deleted, i.e. they just vanish instead of + * remembering them as "deleted". + */ + void purge_catalogs_not_listed(const Set &catalogs_to_keep); + + /** + * Delete a catalog, without deleting any of its children and without rebuilding the catalog + * tree. The deletion in "Soft", in the sense that the catalog pointer is moved from `catalogs_` + * to `deleted_catalogs_`; the AssetCatalog instance itself is kept in memory. As a result, it + * will be removed from a CDF when saved to disk. + * + * This is a lower-level function than #prune_catalogs_by_path. + */ + void delete_catalog_by_id_soft(CatalogID catalog_id); + + /** + * Hard delete a catalog. This simply removes the catalog from existence. The deletion will not + * be remembered, and reloading the CDF will bring it back. */ + void delete_catalog_by_id_hard(CatalogID catalog_id); + + std::unique_ptr parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path); + + /** + * Construct an in-memory catalog definition file (CDF) from the currently known catalogs. + * This object can then be processed further before saving to disk. */ + std::unique_ptr construct_cdf_in_memory( + const CatalogFilePath &file_path); + + /** + * Find a suitable path to write a CDF to. + * + * This depends on the location of the blend file, and on whether a CDF already exists next to it + * or whether the blend file is saved inside an asset library. + */ + static CatalogFilePath find_suitable_cdf_path_for_writing( + const CatalogFilePath &blend_file_path); + + std::unique_ptr read_into_tree(); + void rebuild_tree(); + + /** + * For every catalog, ensure that its parent path also has a known catalog. + */ + void create_missing_catalogs(); + + /* For access by subclasses, as those will not be marked as friend by #AssetCatalogCollection. */ + AssetCatalogDefinitionFile *get_catalog_definition_file(); + OwningAssetCatalogMap &get_catalogs(); + OwningAssetCatalogMap &get_deleted_catalogs(); +}; + +/** + * All catalogs that are owned by a single asset library, and managed by a single instance of + * #AssetCatalogService. The undo system for asset catalog edits contains historical copies of this + * struct. + */ +class AssetCatalogCollection { + friend AssetCatalogService; + + public: + AssetCatalogCollection() = default; + AssetCatalogCollection(const AssetCatalogCollection &other) = delete; + AssetCatalogCollection(AssetCatalogCollection &&other) noexcept = default; + + std::unique_ptr deep_copy() const; + + protected: + /** All catalogs known, except the known-but-deleted ones. */ + OwningAssetCatalogMap catalogs_; + + /** Catalogs that have been deleted. They are kept around so that the load-merge-save of catalog + * definition files can actually delete them if they already existed on disk (instead of the + * merge operation resurrecting them). */ + OwningAssetCatalogMap deleted_catalogs_; + + /* For now only a single catalog definition file is supported. + * The aim is to support an arbitrary number of such files per asset library in the future. */ + std::unique_ptr catalog_definition_file_; + + /** Whether any of the catalogs have unsaved changes. */ + bool has_unsaved_changes_ = false; + + static OwningAssetCatalogMap copy_catalog_map(const OwningAssetCatalogMap &orig); +}; + +/** + * Representation of a catalog path in the #AssetCatalogTree. + */ +class AssetCatalogTreeItem { + friend class AssetCatalogTree; + + public: + /** Container for child items. Uses a #std::map to keep items ordered by their name (i.e. their + * last catalog component). */ + using ChildMap = std::map; + using ItemIterFn = FunctionRef; + + AssetCatalogTreeItem(StringRef name, + CatalogID catalog_id, + StringRef simple_name, + const AssetCatalogTreeItem *parent = nullptr); + + CatalogID get_catalog_id() const; + StringRefNull get_simple_name() const; + StringRefNull get_name() const; + bool has_unsaved_changes() const; + /** Return the full catalog path, defined as the name of this catalog prefixed by the full + * catalog path of its parent and a separator. */ + AssetCatalogPath catalog_path() const; + int count_parents() const; + bool has_children() const; + + /** Iterate over children calling \a callback for each of them, but do not recurse into their + * children. */ + void foreach_child(const ItemIterFn callback); + + protected: + /** Child tree items, ordered by their names. */ + ChildMap children_; + /** The user visible name of this component. */ + CatalogPathComponent name_; + CatalogID catalog_id_; + /** Copy of #AssetCatalog::simple_name. */ + std::string simple_name_; + /** Copy of #AssetCatalog::flags.has_unsaved_changes. */ + bool has_unsaved_changes_ = false; + + /** Pointer back to the parent item. Used to reconstruct the hierarchy from an item (e.g. to + * build a path). */ + const AssetCatalogTreeItem *parent_ = nullptr; + + private: + static void foreach_item_recursive(ChildMap &children_, ItemIterFn callback); +}; + +/** + * A representation of the catalog paths as tree structure. Each component of the catalog tree is + * represented by an #AssetCatalogTreeItem. The last path component of an item is used as its name, + * which may also be shown to the user. + * An item can not have multiple children with the same name. That means the name uniquely + * identifies an item within its parent. + * + * There is no single root tree element, the #AssetCatalogTree instance itself represents the root. + */ +class AssetCatalogTree { + using ChildMap = AssetCatalogTreeItem::ChildMap; + using ItemIterFn = AssetCatalogTreeItem::ItemIterFn; + + public: + /** Ensure an item representing \a path is in the tree, adding it if necessary. */ + void insert_item(const AssetCatalog &catalog); + + void foreach_item(const AssetCatalogTreeItem::ItemIterFn callback); + /** Iterate over root items calling \a callback for each of them, but do not recurse into their + * children. */ + void foreach_root_item(const ItemIterFn callback); + + protected: + /** Child tree items, ordered by their names. */ + ChildMap root_items_; +}; + +/** Keeps track of which catalogs are defined in a certain file on disk. + * Only contains non-owning pointers to the #AssetCatalog instances, so ensure the lifetime of this + * class is shorter than that of the #`AssetCatalog`s themselves. */ +class AssetCatalogDefinitionFile { + public: + /* For now this is the only version of the catalog definition files that is supported. + * Later versioning code may be added to handle older files. */ + const static int SUPPORTED_VERSION; + const static std::string VERSION_MARKER; + const static std::string HEADER; + + CatalogFilePath file_path; + + AssetCatalogDefinitionFile() = default; + + /** + * Write the catalog definitions to the same file they were read from. + * Return true when the file was written correctly, false when there was a problem. + */ + bool write_to_disk() const; + /** + * Write the catalog definitions to an arbitrary file path. + * + * Any existing file is backed up to "filename~". Any previously existing backup is overwritten. + * + * Return true when the file was written correctly, false when there was a problem. + */ + bool write_to_disk(const CatalogFilePath &dest_file_path) const; + + bool contains(CatalogID catalog_id) const; + /** Add a catalog, overwriting the one with the same catalog ID. */ + void add_overwrite(AssetCatalog *catalog); + /** Add a new catalog. Undefined behavior if a catalog with the same ID was already added. */ + void add_new(AssetCatalog *catalog); + + /** Remove the catalog from the collection of catalogs stored in this file. */ + void forget(CatalogID catalog_id); + + using AssetCatalogParsedFn = FunctionRef)>; + void parse_catalog_file(const CatalogFilePath &catalog_definition_file_path, + AssetCatalogParsedFn callback); + + std::unique_ptr copy_and_remap( + const OwningAssetCatalogMap &catalogs, const OwningAssetCatalogMap &deleted_catalogs) const; + + protected: + /* Catalogs stored in this file. They are mapped by ID to make it possible to query whether a + * catalog is already known, without having to find the corresponding `AssetCatalog*`. */ + Map catalogs_; + + bool parse_version_line(StringRef line); + std::unique_ptr parse_catalog_line(StringRef line); + + /** + * Write the catalog definitions to the given file path. + * Return true when the file was written correctly, false when there was a problem. + */ + bool write_to_disk_unsafe(const CatalogFilePath &dest_file_path) const; + bool ensure_directory_exists(const CatalogFilePath directory_path) const; +}; + +/** Asset Catalog definition, containing a symbolic ID and a path that points to a node in the + * catalog hierarchy. */ +class AssetCatalog { + public: + AssetCatalog() = default; + AssetCatalog(CatalogID catalog_id, const AssetCatalogPath &path, const std::string &simple_name); + + CatalogID catalog_id; + AssetCatalogPath path; + /** + * Simple, human-readable name for the asset catalog. This is stored on assets alongside the + * catalog ID; the catalog ID is a UUID that is not human-readable, + * so to avoid complete data-loss when the catalog definition file gets lost, + * we also store a human-readable simple name for the catalog. */ + std::string simple_name; + + struct Flags { + /* Treat this catalog as deleted. Keeping deleted catalogs around is necessary to support + * merging of on-disk changes with in-memory changes. */ + bool is_deleted = false; + + /* Sort this catalog first when there are multiple catalogs with the same catalog path. This + * ensures that in a situation where missing catalogs were auto-created, and then + * load-and-merged with a file that also has these catalogs, the first one in that file is + * always sorted first, regardless of the sort order of its UUID. */ + bool is_first_loaded = false; + + /* Merging on-disk changes into memory will not overwrite this catalog. + * For example, when a catalog was renamed (i.e. changed path) in this Blender session, + * reloading the catalog definition file should not overwrite that change. + * + * Note that this flag is ignored when is_deleted=true; deleted catalogs that are still in + * memory are considered "unsaved" by definition. */ + bool has_unsaved_changes = false; + } flags; + + /** + * Create a new Catalog with the given path, auto-generating a sensible catalog simple-name. + * + * NOTE: the given path will be cleaned up (trailing spaces removed, etc.), so the returned + * `AssetCatalog`'s path differ from the given one. + */ + static std::unique_ptr from_path(const AssetCatalogPath &path); + + /** Make a new simple name for the catalog, based on its path. */ + void simple_name_refresh(); + + protected: + /** Generate a sensible catalog ID for the given path. */ + static std::string sensible_simple_name_for_path(const AssetCatalogPath &path); +}; + +/** Comparator for asset catalogs, ordering by (path, first_seen, UUID). */ +struct AssetCatalogLessThan { + bool operator()(const AssetCatalog *lhs, const AssetCatalog *rhs) const + { + if (lhs->path != rhs->path) { + return lhs->path < rhs->path; + } + + if (lhs->flags.is_first_loaded != rhs->flags.is_first_loaded) { + return lhs->flags.is_first_loaded; + } + + return lhs->catalog_id < rhs->catalog_id; + } +}; + +/** + * Set that stores catalogs ordered by (path, UUID). + * Being a set, duplicates are removed. The catalog's simple name is ignored in this. */ +using AssetCatalogOrderedSet = std::set; +using MutableAssetCatalogOrderedSet = std::set; + +/** + * Filter that can determine whether an asset should be visible or not, based on its catalog ID. + * + * \see AssetCatalogService::create_catalog_filter() + */ +class AssetCatalogFilter { + public: + bool contains(CatalogID asset_catalog_id) const; + + /* So that all unknown catalogs can be shown under "Unassigned". */ + bool is_known(CatalogID asset_catalog_id) const; + + protected: + friend AssetCatalogService; + const Set matching_catalog_ids; + const Set known_catalog_ids; + + explicit AssetCatalogFilter(Set &&matching_catalog_ids, + Set &&known_catalog_ids); +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_asset_catalog_path.hh b/source/blender/blenkernel/BKE_asset_catalog_path.hh new file mode 100644 index 00000000000..f51232334f2 --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_catalog_path.hh @@ -0,0 +1,146 @@ +/* + * 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 + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++ header. +#endif + +#include "BLI_function_ref.hh" +#include "BLI_string_ref.hh" +#include "BLI_sys_types.h" + +#include + +namespace blender::bke { + +/** + * Location of an Asset Catalog in the catalog tree, denoted by slash-separated path components. + * + * Each path component is a string that is not allowed to have slashes or colons. The latter is to + * make things easy to save in the colon-delimited Catalog Definition File format. + * + * The path of a catalog determines where in the catalog hierarchy the catalog is shown. Examples + * are "Characters/Ellie/Poses/Hand" or "Kit_bash/City/Skyscrapers". The path looks like a + * file-system path, with a few differences: + * + * - Only slashes are used as path component separators. + * - All paths are absolute, so there is no need for a leading slash. + * + * See https://wiki.blender.org/wiki/Source/Architecture/Asset_System/Catalogs + * + * Paths are stored as byte sequences, and assumed to be UTF-8. + */ +class AssetCatalogPath { + friend std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append); + + private: + /** + * The path itself, such as "Agents/Secret/327". + */ + std::string path_ = ""; + + public: + static const char SEPARATOR; + + AssetCatalogPath() = default; + AssetCatalogPath(StringRef path); + AssetCatalogPath(const std::string &path); + AssetCatalogPath(const char *path); + AssetCatalogPath(const AssetCatalogPath &other_path) = default; + AssetCatalogPath(AssetCatalogPath &&other_path) noexcept; + ~AssetCatalogPath() = default; + + uint64_t hash() const; + uint64_t length() const; /* Length of the path in bytes. */ + + /** C-string representation of the path. */ + const char *c_str() const; + const std::string &str() const; + + /* The last path component, used as label in the tree view. */ + StringRefNull name() const; + + /* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor. + * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */ + bool operator==(const AssetCatalogPath &other_path) const; + bool operator!=(const AssetCatalogPath &other_path) const; + bool operator<(const AssetCatalogPath &other_path) const; + AssetCatalogPath &operator=(const AssetCatalogPath &other_path) = default; + AssetCatalogPath &operator=(AssetCatalogPath &&other_path) = default; + + /** Concatenate two paths, returning the new path. */ + AssetCatalogPath operator/(const AssetCatalogPath &path_to_append) const; + + /* False when the path is empty, true otherwise. */ + operator bool() const; + + /** + * Clean up the path. This ensures: + * - Every path component is stripped of its leading/trailing spaces. + * - Empty components (caused by double slashes or leading/trailing slashes) are removed. + * - Invalid characters are replaced with valid ones. + */ + [[nodiscard]] AssetCatalogPath cleanup() const; + + /** + * \return true only if the given path is a parent of this catalog's path. + * When this catalog's path is equal to the given path, return true as well. + * In other words, this defines a weak subset. + * + * True: "some/path/there" is contained in "some/path" and "some". + * False: "path/there" is not contained in "some/path/there". + * + * Note that non-cleaned-up paths (so for example starting or ending with a + * slash) are not supported, and result in undefined behavior. + */ + bool is_contained_in(const AssetCatalogPath &other_path) const; + + /** + * \return the parent path, or an empty path if there is no parent. + */ + AssetCatalogPath parent() const; + + /** + * Change the initial part of the path from `from_path` to `to_path`. + * If this path does not start with `from_path`, return an empty path as result. + * + * Example: + * + * AssetCatalogPath path("some/path/to/some/catalog"); + * path.rebase("some/path", "new/base") -> "new/base/to/some/catalog" + */ + AssetCatalogPath rebase(const AssetCatalogPath &from_path, + const AssetCatalogPath &to_path) const; + + /** Call the callback function for each path component, in left-to-right order. */ + using ComponentIteratorFn = FunctionRef; + void iterate_components(ComponentIteratorFn callback) const; + + protected: + /** Strip leading/trailing spaces and replace disallowed characters. */ + static std::string cleanup_component(StringRef component_name); +}; + +/** Output the path as string. */ +std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_asset_library.h b/source/blender/blenkernel/BKE_asset_library.h new file mode 100644 index 00000000000..ca12fd6f4fb --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_library.h @@ -0,0 +1,85 @@ +/* + * 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 + */ + +#pragma once + +struct Main; + +#ifdef __cplusplus +extern "C" { +#endif + +/** Forward declaration, defined in intern/asset_library.hh */ +typedef struct AssetLibrary AssetLibrary; + +/** + * Return the #AssetLibrary rooted at the given directory path. + * + * Will return the same pointer for repeated calls, until another blend file is loaded. + * + * To get the in-memory-only "current file" asset library, pass an empty path. + */ +struct AssetLibrary *BKE_asset_library_load(const char *library_path); + +/** + * Try to find an appropriate location for an asset library root from a file or directory path. + * Does not check if \a input_path exists. + * + * The design is made to find an appropriate asset library path from a .blend file path, but + * technically works with any file or directory as \a input_path. + * Design is: + * * If \a input_path lies within a known asset library path (i.e. an asset library registered in + * the Preferences), return the asset library path. + * * Otherwise, if \a input_path has a parent path, return the parent path (e.g. to use the + * directory a .blend file is in as asset library root). + * * If \a input_path is empty or doesn't have a parent path (e.g. because a .blend wasn't saved + * yet), there is no suitable path. The caller has to decide how to handle this case. + * + * \param r_library_path: The returned asset library path with a trailing slash, or an empty string + * if no suitable path is found. Assumed to be a buffer of at least + * #FILE_MAXDIR bytes. + * + * \return True if the function could find a valid, that is, a non-empty path to return in \a + * r_library_path. + */ +bool BKE_asset_library_find_suitable_root_path_from_path( + const char *input_path, char r_library_path[768 /* FILE_MAXDIR */]); +/** + * Uses the current location on disk of the file represented by \a bmain as input to + * #BKE_asset_library_find_suitable_root_path_from_path(). Refer to it for a design + * description. + * + * \return True if the function could find a valid, that is, a non-empty path to return in \a + * r_library_path. If \a bmain wasn't saved into a file yet, the return value will be + * false. + */ +bool BKE_asset_library_find_suitable_root_path_from_main( + const struct Main *bmain, char r_library_path[768 /* FILE_MAXDIR */]); + +/** Look up the asset's catalog and copy its simple name into #asset_data. */ +void BKE_asset_library_refresh_catalog_simplename(struct AssetLibrary *asset_library, + struct AssetMetaData *asset_data); + +/** Return whether any loaded AssetLibrary has unsaved changes to its catalogs. */ +bool BKE_asset_library_has_any_unsaved_catalogs(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/BKE_asset_library.hh b/source/blender/blenkernel/BKE_asset_library.hh new file mode 100644 index 00000000000..15f7991e75e --- /dev/null +++ b/source/blender/blenkernel/BKE_asset_library.hh @@ -0,0 +1,76 @@ +/* + * 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 + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++-only header file. Use BKE_asset_library.h instead. +#endif + +#include "BKE_asset_library.h" + +#include "BKE_asset_catalog.hh" +#include "BKE_callbacks.h" + +#include + +namespace blender::bke { + +/** + * AssetLibrary provides access to an asset library's data. + * For now this is only for catalogs, later this can be expanded to indexes/caches/more. + */ +struct AssetLibrary { + /* Controlled by #ED_asset_catalogs_set_save_catalogs_when_file_is_saved, + * for managing the "Save Catalog Changes" in the quit-confirmation dialog box. */ + static bool save_catalogs_when_file_is_saved; + + std::unique_ptr catalog_service; + + AssetLibrary(); + ~AssetLibrary(); + + void load(StringRefNull library_root_directory); + + /** Load catalogs that have changed on disk. */ + void refresh(); + + /** + * Update `catalog_simple_name` by looking up the asset's catalog by its ID. + * + * No-op if the catalog cannot be found. This could be the kind of "the + * catalog definition file is corrupt/lost" scenario that the simple name is + * meant to help recover from. */ + void refresh_catalog_simplename(struct AssetMetaData *asset_data); + + void on_blend_save_handler_register(); + void on_blend_save_handler_unregister(); + + void on_blend_save_post(struct Main *, struct PointerRNA **pointers, const int num_pointers); + + private: + bCallbackFuncStore on_save_callback_store_{}; +}; + +} // namespace blender::bke + +blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service( + const ::AssetLibrary *library); +blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library); diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index cf54e7efa0d..6a87375e5e2 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -42,65 +42,23 @@ class AttributeIDRef { const AnonymousAttributeID *anonymous_id_ = nullptr; public: - AttributeIDRef() = default; + AttributeIDRef(); + AttributeIDRef(StringRef name); + AttributeIDRef(StringRefNull name); + AttributeIDRef(const char *name); + AttributeIDRef(const std::string &name); + AttributeIDRef(const AnonymousAttributeID *anonymous_id); - AttributeIDRef(StringRef name) : name_(name) - { - } + operator bool() const; + uint64_t hash() const; + bool is_named() const; + bool is_anonymous() const; + StringRef name() const; + const AnonymousAttributeID &anonymous_id() const; + bool should_be_kept() const; - AttributeIDRef(StringRefNull name) : name_(name) - { - } - - AttributeIDRef(const char *name) : name_(name) - { - } - - AttributeIDRef(const std::string &name) : name_(name) - { - } - - /* The anonymous id is only borrowed, the caller has to keep a reference to it. */ - AttributeIDRef(const AnonymousAttributeID *anonymous_id) : anonymous_id_(anonymous_id) - { - } - - operator bool() const - { - return this->is_named() || this->is_anonymous(); - } - - friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) - { - return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; - } - - uint64_t hash() const - { - return get_default_hash_2(name_, anonymous_id_); - } - - bool is_named() const - { - return !name_.is_empty(); - } - - bool is_anonymous() const - { - return anonymous_id_ != nullptr; - } - - StringRef name() const - { - BLI_assert(this->is_named()); - return name_; - } - - const AnonymousAttributeID &anonymous_id() const - { - BLI_assert(this->is_anonymous()); - return *anonymous_id_; - } + friend bool operator==(const AttributeIDRef &a, const AttributeIDRef &b); + friend std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id); }; } // namespace blender::bke @@ -120,6 +78,11 @@ struct AttributeMetaData { } }; +struct AttributeKind { + AttributeDomain domain; + CustomDataType data_type; +}; + /** * Base class for the attribute initializer types described below. */ @@ -220,6 +183,8 @@ struct WriteAttributeLookup { GVMutableArrayPtr varray; /* Domain the attributes lives on in the geometry. */ AttributeDomain domain; + /* Call this after changing the attribute to invalidate caches that depend on this attribute. */ + std::function tag_modified_fn; /* Convenience function to check if the attribute has been found. */ operator bool() const @@ -245,80 +210,33 @@ class OutputAttribute { private: GVMutableArrayPtr varray_; - AttributeDomain domain_; + AttributeDomain domain_ = ATTR_DOMAIN_AUTO; SaveFn save_; std::unique_ptr optional_span_varray_; bool ignore_old_values_ = false; bool save_has_been_called_ = false; public: - OutputAttribute() = default; - + OutputAttribute(); + OutputAttribute(OutputAttribute &&other); OutputAttribute(GVMutableArrayPtr varray, AttributeDomain domain, SaveFn save, - const bool ignore_old_values) - : varray_(std::move(varray)), - domain_(domain), - save_(std::move(save)), - ignore_old_values_(ignore_old_values) - { - } - - OutputAttribute(OutputAttribute &&other) = default; + const bool ignore_old_values); ~OutputAttribute(); - operator bool() const - { - return varray_.get() != nullptr; - } + operator bool() const; - GVMutableArray &operator*() - { - return *varray_; - } + GVMutableArray &operator*(); + GVMutableArray *operator->(); + GVMutableArray &varray(); + AttributeDomain domain() const; + const CPPType &cpp_type() const; + CustomDataType custom_data_type() const; - GVMutableArray *operator->() - { - return varray_.get(); - } - - GVMutableArray &varray() - { - return *varray_; - } - - AttributeDomain domain() const - { - return domain_; - } - - const CPPType &cpp_type() const - { - return varray_->type(); - } - - CustomDataType custom_data_type() const - { - return cpp_type_to_custom_data_type(this->cpp_type()); - } - - fn::GMutableSpan as_span() - { - if (!optional_span_varray_) { - const bool materialize_old_values = !ignore_old_values_; - optional_span_varray_ = std::make_unique(*varray_, - materialize_old_values); - } - fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; - return span_varray; - } - - template MutableSpan as_span() - { - return this->as_span().typed(); - } + fn::GMutableSpan as_span(); + template MutableSpan as_span(); void save(); }; @@ -329,18 +247,32 @@ class OutputAttribute { template class OutputAttribute_Typed { private: OutputAttribute attribute_; - std::optional> optional_varray_; + std::unique_ptr> optional_varray_; VMutableArray *varray_ = nullptr; public: + OutputAttribute_Typed(); OutputAttribute_Typed(OutputAttribute attribute) : attribute_(std::move(attribute)) { if (attribute_) { - optional_varray_.emplace(attribute_.varray()); + optional_varray_ = std::make_unique>(attribute_.varray()); varray_ = &**optional_varray_; } } + OutputAttribute_Typed(OutputAttribute_Typed &&other); + ~OutputAttribute_Typed(); + + OutputAttribute_Typed &operator=(OutputAttribute_Typed &&other) + { + if (this == &other) { + return *this; + } + this->~OutputAttribute_Typed(); + new (this) OutputAttribute_Typed(std::move(other)); + return *this; + } + operator bool() const { return varray_ != nullptr; @@ -387,6 +319,13 @@ template class OutputAttribute_Typed { } }; +/* These are not defined in the class directly, because when defining them there, the external + * template instantiation does not work, resulting in longer compile times. */ +template inline OutputAttribute_Typed::OutputAttribute_Typed() = default; +template +inline OutputAttribute_Typed::OutputAttribute_Typed(OutputAttribute_Typed &&other) = default; +template inline OutputAttribute_Typed::~OutputAttribute_Typed() = default; + /** * A basic container around DNA CustomData so that its users * don't have to implement special copy and move constructors. @@ -433,8 +372,166 @@ class CustomDataAttributes { void *buffer); bool remove(const AttributeIDRef &attribute_id); + /** + * Change the order of the attributes to match the order of IDs in the argument. + */ + void reorder(Span new_order); + bool foreach_attribute(const AttributeForeachCallback callback, const AttributeDomain domain) const; }; +/* -------------------------------------------------------------------- */ +/** \name #AttributeIDRef Inline Methods + * \{ */ + +inline AttributeIDRef::AttributeIDRef() = default; + +inline AttributeIDRef::AttributeIDRef(StringRef name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(StringRefNull name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(const char *name) : name_(name) +{ +} + +inline AttributeIDRef::AttributeIDRef(const std::string &name) : name_(name) +{ +} + +/* The anonymous id is only borrowed, the caller has to keep a reference to it. */ +inline AttributeIDRef::AttributeIDRef(const AnonymousAttributeID *anonymous_id) + : anonymous_id_(anonymous_id) +{ +} + +inline bool operator==(const AttributeIDRef &a, const AttributeIDRef &b) +{ + return a.anonymous_id_ == b.anonymous_id_ && a.name_ == b.name_; +} + +inline AttributeIDRef::operator bool() const +{ + return this->is_named() || this->is_anonymous(); +} + +inline uint64_t AttributeIDRef::hash() const +{ + return get_default_hash_2(name_, anonymous_id_); +} + +inline bool AttributeIDRef::is_named() const +{ + return !name_.is_empty(); +} + +inline bool AttributeIDRef::is_anonymous() const +{ + return anonymous_id_ != nullptr; +} + +inline StringRef AttributeIDRef::name() const +{ + BLI_assert(this->is_named()); + return name_; +} + +inline const AnonymousAttributeID &AttributeIDRef::anonymous_id() const +{ + BLI_assert(this->is_anonymous()); + return *anonymous_id_; +} + +/** + * \return True if the attribute should not be removed automatically as an optimization during + * processing or copying. Anonymous attributes can be removed when they no longer have any + * references. + */ +inline bool AttributeIDRef::should_be_kept() const +{ + return this->is_named() || BKE_anonymous_attribute_id_has_strong_references(anonymous_id_); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #OutputAttribute Inline Methods + * \{ */ + +inline OutputAttribute::OutputAttribute() = default; +inline OutputAttribute::OutputAttribute(OutputAttribute &&other) = default; + +inline OutputAttribute::OutputAttribute(GVMutableArrayPtr varray, + AttributeDomain domain, + SaveFn save, + const bool ignore_old_values) + : varray_(std::move(varray)), + domain_(domain), + save_(std::move(save)), + ignore_old_values_(ignore_old_values) +{ +} + +inline OutputAttribute::operator bool() const +{ + return varray_.get() != nullptr; +} + +inline GVMutableArray &OutputAttribute::operator*() +{ + return *varray_; +} + +inline GVMutableArray *OutputAttribute::operator->() +{ + return varray_.get(); +} + +inline GVMutableArray &OutputAttribute::varray() +{ + return *varray_; +} + +inline AttributeDomain OutputAttribute::domain() const +{ + return domain_; +} + +inline const CPPType &OutputAttribute::cpp_type() const +{ + return varray_->type(); +} + +inline CustomDataType OutputAttribute::custom_data_type() const +{ + return cpp_type_to_custom_data_type(this->cpp_type()); +} + +template inline MutableSpan OutputAttribute::as_span() +{ + return this->as_span().typed(); +} + +/** \} */ + } // namespace blender::bke + +/* -------------------------------------------------------------------- */ +/** \name External Template Instantiations + * + * Defined in `intern/extern_implementations.cc`. + * \{ */ + +namespace blender::bke { +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +extern template class OutputAttribute_Typed; +} // namespace blender::bke + +/** \} */ diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 4a34a7ec2fc..6fc2fa37d9f 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -31,7 +31,7 @@ extern "C" { */ /* Blender major and minor version. */ -#define BLENDER_VERSION 300 +#define BLENDER_VERSION 301 /* Blender patch version for bugfix releases. */ #define BLENDER_VERSION_PATCH 0 /** Blender release cycle stage: alpha/beta/rc/release. */ @@ -39,13 +39,13 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 26 +#define BLENDER_FILE_SUBVERSION 0 /* 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 * was written with too new a version. */ #define BLENDER_FILE_MIN_VERSION 300 -#define BLENDER_FILE_MIN_SUBVERSION 11 +#define BLENDER_FILE_MIN_SUBVERSION 36 /** User readable version string. */ const char *BKE_blender_version_string(void); diff --git a/source/blender/blenkernel/BKE_bvhutils.h b/source/blender/blenkernel/BKE_bvhutils.h index 06be8ec80fc..bb95985ef4c 100644 --- a/source/blender/blenkernel/BKE_bvhutils.h +++ b/source/blender/blenkernel/BKE_bvhutils.h @@ -48,7 +48,7 @@ struct BVHCache; typedef struct BVHTreeFromEditMesh { struct BVHTree *tree; - /* default callbacks to bvh nearest and raycast */ + /** Default callbacks to bvh nearest and ray-cast. */ BVHTree_NearestPointCallback nearest_callback; BVHTree_RayCastCallback raycast_callback; @@ -60,18 +60,18 @@ typedef struct BVHTreeFromEditMesh { } BVHTreeFromEditMesh; /** - * Struct that stores basic information about a BVHTree built from a mesh. + * Struct that stores basic information about a #BVHTree built from a mesh. */ typedef struct BVHTreeFromMesh { struct BVHTree *tree; - /* default callbacks to bvh nearest and raycast */ + /** Default callbacks to bvh nearest and ray-cast. */ BVHTree_NearestPointCallback nearest_callback; BVHTree_RayCastCallback raycast_callback; /* Vertex array, so that callbacks have instant access to data. */ const struct MVert *vert; - const struct MEdge *edge; /* only used for BVHTreeFromMeshEdges */ + const struct MEdge *edge; /* only used for #BVHTreeFromMeshEdges */ const struct MFace *face; const struct MLoop *loop; const struct MLoopTri *looptri; diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h index ef2a0ed34a0..7c518f33c89 100644 --- a/source/blender/blenkernel/BKE_callbacks.h +++ b/source/blender/blenkernel/BKE_callbacks.h @@ -130,6 +130,7 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain, struct Depsgraph *depsgraph, eCbEvent evt); void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt); +void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt); void BKE_callback_global_init(void); void BKE_callback_global_finalize(void); diff --git a/source/blender/blenkernel/BKE_colortools.h b/source/blender/blenkernel/BKE_colortools.h index ec2262d4f60..109947cece4 100644 --- a/source/blender/blenkernel/BKE_colortools.h +++ b/source/blender/blenkernel/BKE_colortools.h @@ -97,6 +97,7 @@ void BKE_curvemapping_evaluate_premulRGBF(const struct CurveMapping *cumap, float vecout[3], const float vecin[3]); bool BKE_curvemapping_RGBA_does_something(const struct CurveMapping *cumap); +void BKE_curvemapping_table_F(const struct CurveMapping *cumap, float **array, int *size); void BKE_curvemapping_table_RGBA(const struct CurveMapping *cumap, float **array, int *size); /* non-const, these modify the curve */ diff --git a/source/blender/render/intern/initrender.h b/source/blender/blenkernel/BKE_curve_to_mesh.hh similarity index 72% rename from source/blender/render/intern/initrender.h rename to source/blender/blenkernel/BKE_curve_to_mesh.hh index f5ac352752f..fb077425336 100644 --- a/source/blender/render/intern/initrender.h +++ b/source/blender/blenkernel/BKE_curve_to_mesh.hh @@ -12,27 +12,20 @@ * 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) 2001-2002 by NaN Holding BV. - * All rights reserved. - */ - -/** \file - * \ingroup render */ #pragma once -#ifdef __cplusplus -extern "C" { -#endif +struct Mesh; +struct CurveEval; -/* Functions */ +/** \file + * \ingroup bke + */ -void RE_parts_init(Render *re); -void RE_parts_free(Render *re); -void RE_parts_clamp(Render *re); +namespace blender::bke { -#ifdef __cplusplus -} -#endif +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, bool fill_caps); +Mesh *curve_to_wire_mesh(const CurveEval &curve); + +} // namespace blender::bke diff --git a/source/blender/blenkernel/BKE_curveprofile.h b/source/blender/blenkernel/BKE_curveprofile.h index 501ae70ecdb..5a948f0d844 100644 --- a/source/blender/blenkernel/BKE_curveprofile.h +++ b/source/blender/blenkernel/BKE_curveprofile.h @@ -70,10 +70,7 @@ void BKE_curveprofile_reset_view(struct CurveProfile *profile); void BKE_curveprofile_reset(struct CurveProfile *profile); -void BKE_curveprofile_create_samples(struct CurveProfile *profile, - int n_segments, - bool sample_straight_edges, - struct CurveProfilePoint *r_samples); +int BKE_curveprofile_table_size(const struct CurveProfile *profile); void BKE_curveprofile_init(struct CurveProfile *profile, short segments_len); @@ -85,13 +82,6 @@ enum { }; void BKE_curveprofile_update(struct CurveProfile *profile, const int update_flags); -/* Need to find the total length of the curve to sample a portion of it */ -float BKE_curveprofile_total_length(const struct CurveProfile *profile); - -void BKE_curveprofile_create_samples_even_spacing(struct CurveProfile *profile, - int n_segments, - struct CurveProfilePoint *r_samples); - /* Length portion is the fraction of the total path length where we want the location */ void BKE_curveprofile_evaluate_length_portion(const struct CurveProfile *profile, float length_portion, diff --git a/source/blender/blenkernel/BKE_displist.h b/source/blender/blenkernel/BKE_displist.h index db5663fcc94..8fb596a8096 100644 --- a/source/blender/blenkernel/BKE_displist.h +++ b/source/blender/blenkernel/BKE_displist.h @@ -87,11 +87,6 @@ void BKE_displist_make_curveTypes(struct Depsgraph *depsgraph, const struct Scene *scene, struct Object *ob, const bool for_render); -void BKE_displist_make_curveTypes_forRender(struct Depsgraph *depsgraph, - const struct Scene *scene, - struct Object *ob, - struct ListBase *dispbase, - struct Mesh **r_final); void BKE_displist_make_mball(struct Depsgraph *depsgraph, struct Scene *scene, struct Object *ob); void BKE_curve_calc_modifiers_pre(struct Depsgraph *depsgraph, diff --git a/source/blender/blenkernel/BKE_geometry_set.hh b/source/blender/blenkernel/BKE_geometry_set.hh index bf38294257a..58a89d0207a 100644 --- a/source/blender/blenkernel/BKE_geometry_set.hh +++ b/source/blender/blenkernel/BKE_geometry_set.hh @@ -25,6 +25,7 @@ #include "BLI_float3.hh" #include "BLI_float4x4.hh" +#include "BLI_function_ref.hh" #include "BLI_hash.hh" #include "BLI_map.hh" #include "BLI_set.hh" @@ -252,6 +253,13 @@ struct GeometrySet { blender::Map components_; public: + GeometrySet(); + GeometrySet(const GeometrySet &other); + GeometrySet(GeometrySet &&other); + ~GeometrySet(); + GeometrySet &operator=(const GeometrySet &other); + GeometrySet &operator=(GeometrySet &&other); + GeometryComponent &get_component_for_write(GeometryComponentType component_type); template Component &get_component_for_write() { @@ -280,6 +288,8 @@ struct GeometrySet { return this->remove(Component::static_type); } + void keep_only(const blender::Span component_types); + void add(const GeometryComponent &component); blender::Vector get_components_for_read() const; @@ -293,6 +303,28 @@ struct GeometrySet { bool owns_direct_data() const; void ensure_owns_direct_data(); + using AttributeForeachCallback = + blender::FunctionRef; + + void attribute_foreach(blender::Span component_types, + bool include_instances, + AttributeForeachCallback callback) const; + + void gather_attributes_for_propagation( + blender::Span component_types, + GeometryComponentType dst_component_type, + bool include_instances, + blender::Map &r_attributes) const; + + blender::Vector gather_component_types(bool include_instances, + bool ignore_empty) const; + + using ForeachSubGeometryCallback = blender::FunctionRef; + + void modify_geometry_sets(ForeachSubGeometryCallback callback); + /* Utility methods for creation. */ static GeometrySet create_with_mesh( Mesh *mesh, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); @@ -307,6 +339,8 @@ struct GeometrySet { bool has_instances() const; bool has_volume() const; bool has_curve() const; + bool has_realized_data() const; + bool is_empty() const; const Mesh *get_mesh_for_read() const; const PointCloud *get_pointcloud_for_read() const; @@ -326,6 +360,15 @@ struct GeometrySet { GeometryOwnershipType ownership = GeometryOwnershipType::Owned); void replace_curve(CurveEval *curve, GeometryOwnershipType ownership = GeometryOwnershipType::Owned); + + private: + /* Utility to retrieve a mutable component without creating it. */ + GeometryComponent *get_component_ptr(GeometryComponentType type); + template Component *get_component_ptr() + { + BLI_STATIC_ASSERT(is_geometry_component_v, ""); + return static_cast(get_component_ptr(Component::static_type)); + } }; /** A geometry component that can store a mesh. */ @@ -481,13 +524,40 @@ class InstanceReference { { } - InstanceReference(const InstanceReference &src) : type_(src.type_), data_(src.data_) + InstanceReference(const InstanceReference &other) : type_(other.type_), data_(other.data_) { - if (src.type_ == Type::GeometrySet) { - geometry_set_ = std::make_unique(*src.geometry_set_); + if (other.geometry_set_) { + geometry_set_ = std::make_unique(*other.geometry_set_); } } + InstanceReference(InstanceReference &&other) + : type_(other.type_), data_(other.data_), geometry_set_(std::move(other.geometry_set_)) + { + other.type_ = Type::None; + other.data_ = nullptr; + } + + InstanceReference &operator=(const InstanceReference &other) + { + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(other); + return *this; + } + + InstanceReference &operator=(InstanceReference &&other) + { + if (this == &other) { + return *this; + } + this->~InstanceReference(); + new (this) InstanceReference(std::move(other)); + return *this; + } + Type type() const { return type_; @@ -554,7 +624,9 @@ class InstancesComponent : public GeometryComponent { blender::Vector instance_transforms_; /** * IDs of the instances. They are used for consistency over multiple frames for things like - * motion blur. + * motion blur. Proper stable ID data that actually helps when rendering can only be generated + * in some situations, so this vector is allowed to be empty, in which case the index of each + * instance will be used for the final ID. */ blender::Vector instance_ids_; @@ -576,9 +648,13 @@ class InstancesComponent : public GeometryComponent { void resize(int capacity); int add_reference(const InstanceReference &reference); - void add_instance(int instance_handle, const blender::float4x4 &transform, const int id = -1); + void add_instance(int instance_handle, const blender::float4x4 &transform); blender::Span references() const; + void remove_unused_references(); + + void ensure_geometry_instances(); + GeometrySet &geometry_set_from_reference(const int reference_index); blender::Span instance_reference_handles() const; blender::MutableSpan instance_reference_handles(); @@ -587,16 +663,28 @@ class InstancesComponent : public GeometryComponent { blender::MutableSpan instance_ids(); blender::Span instance_ids() const; + blender::MutableSpan instance_ids_ensure(); + void instance_ids_clear(); + int instances_amount() const; + int references_amount() const; blender::Span almost_unique_ids() const; + int attribute_domain_size(const AttributeDomain domain) const final; + + void foreach_referenced_geometry( + blender::FunctionRef callback) const; + bool is_empty() const final; bool owns_direct_data() const override; void ensure_owns_direct_data() override; static constexpr inline GeometryComponentType static_type = GEO_COMPONENT_TYPE_INSTANCES; + + private: + const blender::bke::ComponentAttributeProviders *get_attribute_providers() const final; }; /** A geometry component that stores volume grids. */ @@ -656,6 +744,14 @@ class AttributeFieldInput : public fn::FieldInput { AttributeFieldInput(std::string name, const CPPType &type) : fn::FieldInput(type, name), name_(std::move(name)) { + category_ = Category::NamedAttribute; + } + + template static fn::Field Create(std::string name) + { + const CPPType &type = CPPType::get(); + auto field_input = std::make_shared(std::move(name), type); + return fn::Field{field_input}; } StringRefNull attribute_name() const @@ -673,6 +769,23 @@ class AttributeFieldInput : public fn::FieldInput { bool is_equal_to(const fn::FieldNode &other) const override; }; +class IDAttributeFieldInput : public fn::FieldInput { + public: + IDAttributeFieldInput() : fn::FieldInput(CPPType::get()) + { + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const override; + + std::string socket_inspection_name() const override; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + class AnonymousAttributeFieldInput : public fn::FieldInput { private: /** @@ -680,11 +793,26 @@ class AnonymousAttributeFieldInput : public fn::FieldInput { * automatically. */ StrongAnonymousAttributeID anonymous_id_; + std::string producer_name_; public: - AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, const CPPType &type) - : fn::FieldInput(type, anonymous_id.debug_name()), anonymous_id_(std::move(anonymous_id)) + AnonymousAttributeFieldInput(StrongAnonymousAttributeID anonymous_id, + const CPPType &type, + std::string producer_name) + : fn::FieldInput(type, anonymous_id.debug_name()), + anonymous_id_(std::move(anonymous_id)), + producer_name_(producer_name) { + category_ = Category::AnonymousAttribute; + } + + template + static fn::Field Create(StrongAnonymousAttributeID anonymous_id, std::string producer_name) + { + const CPPType &type = CPPType::get(); + auto field_input = std::make_shared( + std::move(anonymous_id), type, std::move(producer_name)); + return fn::Field{field_input}; } const GVArray *get_varray_for_context(const fn::FieldContext &context, diff --git a/source/blender/blenkernel/BKE_geometry_set_instances.hh b/source/blender/blenkernel/BKE_geometry_set_instances.hh index 44a0ee30c4c..e5b28e4fbab 100644 --- a/source/blender/blenkernel/BKE_geometry_set_instances.hh +++ b/source/blender/blenkernel/BKE_geometry_set_instances.hh @@ -20,6 +20,8 @@ namespace blender::bke { +GeometrySet object_get_evaluated_geometry_set(const Object &object); + /** * Used to keep track of a group of instances using the same geometry data. */ @@ -39,21 +41,11 @@ struct GeometryInstanceGroup { Vector transforms; }; -void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit); - void geometry_set_gather_instances(const GeometrySet &geometry_set, Vector &r_instance_groups); -GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set); GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set); -struct AttributeKind { - CustomDataType data_type; - AttributeDomain domain; -}; - /** * Add information about all the attributes on every component of the type. The resulting info * will contain the highest complexity data type and the highest priority domain among every diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h index 7696b5c0189..bacdd4adc76 100644 --- a/source/blender/blenkernel/BKE_global.h +++ b/source/blender/blenkernel/BKE_global.h @@ -211,6 +211,12 @@ enum { G_TRANSFORM_SEQ = (1 << 2), G_TRANSFORM_FCURVES = (1 << 3), G_TRANSFORM_WM = (1 << 4), + /** + * Set when transforming the cursor it's self. + * Used as a hint to draw the cursor (even when hidden). + * Otherwise it's not possible to see whats being transformed. + */ + G_TRANSFORM_CURSOR = (1 << 5), }; /** Defined in blender.c */ diff --git a/source/blender/blenkernel/BKE_gpencil_geom.h b/source/blender/blenkernel/BKE_gpencil_geom.h index d472fd6f02b..a9cd553a8fe 100644 --- a/source/blender/blenkernel/BKE_gpencil_geom.h +++ b/source/blender/blenkernel/BKE_gpencil_geom.h @@ -114,7 +114,12 @@ void BKE_gpencil_dissolve_points(struct bGPdata *gpd, bool BKE_gpencil_stroke_stretch(struct bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode); + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature); bool BKE_gpencil_stroke_trim_points(struct bGPDstroke *gps, const int index_from, const int index_to); diff --git a/source/blender/blenkernel/BKE_idtype.h b/source/blender/blenkernel/BKE_idtype.h index 7136a3fd7af..d33c24f2c75 100644 --- a/source/blender/blenkernel/BKE_idtype.h +++ b/source/blender/blenkernel/BKE_idtype.h @@ -49,8 +49,11 @@ enum { * appended. * NOTE: Mutually exclusive with `IDTYPE_FLAGS_NO_LIBLINKING`. */ IDTYPE_FLAGS_ONLY_APPEND = 1 << 2, + /** Allow to re-use an existing local ID with matching weak library reference instead of creating + * a new copy of it, when appending. See also #LibraryWeakReference in `DNA_ID.h`. */ + IDTYPE_FLAGS_APPEND_IS_REUSABLE = 1 << 3, /** Indicates that the given IDType does not have animation data. */ - IDTYPE_FLAGS_NO_ANIMDATA = 1 << 3, + IDTYPE_FLAGS_NO_ANIMDATA = 1 << 4, }; typedef struct IDCacheKey { @@ -225,6 +228,11 @@ typedef struct IDTypeInfo { * \note Currently needed for some update operation on point caches. */ IDTypeLibOverrideApplyPost lib_override_apply_post; + + /** + * Callbacks for assets, based on the type of asset. + */ + struct AssetTypeInfo *asset_type_info; } IDTypeInfo; /* ********** Declaration of each IDTypeInfo. ********** */ @@ -290,6 +298,7 @@ bool BKE_idtype_idcode_is_valid(const short idcode); bool BKE_idtype_idcode_is_linkable(const short idcode); bool BKE_idtype_idcode_is_only_appendable(const short idcode); +bool BKE_idtype_idcode_append_is_reusable(const short idcode); /* Macro currently, since any linkable IDtype should be localizable. */ #define BKE_idtype_idcode_is_localizable BKE_idtype_idcode_is_linkable diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index b62ad3ad24a..77f1d197844 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -47,9 +47,6 @@ struct anim; #define IMA_MAX_SPACE 64 #define IMA_UDIM_MAX 2000 -void BKE_images_init(void); -void BKE_images_exit(void); - void BKE_image_free_packedfiles(struct Image *image); void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); @@ -153,10 +150,6 @@ struct RenderData; struct RenderPass; struct RenderResult; -/* ima->ok */ -#define IMA_OK 1 -#define IMA_OK_LOADED 2 - /* signals */ /* reload only frees, doesn't read until image_get_ibuf() called */ #define IMA_SIGNAL_RELOAD 0 @@ -253,13 +246,13 @@ bool BKE_image_is_stereo(struct Image *ima); struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima); void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima); -/* for multilayer images as well as for singlelayer */ +/* For multi-layer images as well as for single-layer. */ bool BKE_image_is_openexr(struct Image *ima); -/* for multiple slot render, call this before render */ +/* For multiple slot render, call this before render. */ void BKE_image_backup_render(struct Scene *scene, struct Image *ima, bool free_current_slot); -/* for singlelayer openexr saving */ +/* For single-layer OpenEXR saving */ bool BKE_image_save_openexr_multiview(struct Image *ima, struct ImBuf *ibuf, const char *filepath, @@ -285,22 +278,22 @@ void BKE_image_packfiles_from_mem(struct ReportList *reports, char *data, const size_t data_len); -/* prints memory statistics for images */ +/* Prints memory statistics for images. */ void BKE_image_print_memlist(struct Main *bmain); -/* merge source into dest, and free source */ +/* Merge source into dest, and free source. */ void BKE_image_merge(struct Main *bmain, struct Image *dest, struct Image *source); -/* scale the image */ +/* Scale the image. */ bool BKE_image_scale(struct Image *image, int width, int height); -/* check if texture has alpha (depth=32) */ +/* Check if texture has alpha (depth=32). */ bool BKE_image_has_alpha(struct Image *image); -/* check if texture has gpu texture code */ +/* Check if texture has GPU texture code. */ bool BKE_image_has_opengl_texture(struct Image *ima); -/* get tile index for tiled images */ +/* Get tile index for tiled images. */ void BKE_image_get_tile_label(struct Image *ima, struct ImageTile *tile, char *label, @@ -355,7 +348,7 @@ bool BKE_image_is_dirty_writable(struct Image *image, bool *is_format_writable); /* Guess offset for the first frame in the sequence */ int BKE_image_sequence_guess_offset(struct Image *image); bool BKE_image_has_anim(struct Image *image); -bool BKE_image_has_packedfile(struct Image *image); +bool BKE_image_has_packedfile(const struct Image *image); bool BKE_image_has_filepath(struct Image *ima); bool BKE_image_is_animated(struct Image *image); bool BKE_image_has_multiple_ibufs(struct Image *image); @@ -369,10 +362,10 @@ struct ImBuf *BKE_image_get_first_ibuf(struct Image *image); /* Not to be use directly. */ struct GPUTexture *BKE_image_create_gpu_texture_from_ibuf(struct Image *image, struct ImBuf *ibuf); -/* Get the GPUTexture for a given `Image`. +/* Get the #GPUTexture for a given `Image`. * * `iuser` and `ibuf` are mutual exclusive parameters. The caller can pass the `ibuf` when already - * available. It is also required when requesting the GPUTexture for a render result. */ + * available. It is also required when requesting the #GPUTexture for a render result. */ struct GPUTexture *BKE_image_get_gpu_texture(struct Image *image, struct ImageUser *iuser, struct ImBuf *ibuf); diff --git a/source/blender/blenkernel/BKE_lib_id.h b/source/blender/blenkernel/BKE_lib_id.h index 36f57209e33..d79df4b2216 100644 --- a/source/blender/blenkernel/BKE_lib_id.h +++ b/source/blender/blenkernel/BKE_lib_id.h @@ -133,6 +133,9 @@ enum { LIB_ID_COPY_SHAPEKEY = 1 << 26, /** EXCEPTION! Specific deep-copy of node trees used e.g. for rendering purposes. */ LIB_ID_COPY_NODETREE_LOCALIZE = 1 << 27, + /** EXCEPTION! Specific handling of RB objects regarding collections differs depending whether we + duplicate scene/collections, or objects. */ + LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING = 1 << 28, /* *** Helper 'defines' gathering most common flag sets. *** */ /** Shapekeys are not real ID's, more like local data to geometry IDs... */ @@ -242,6 +245,10 @@ enum { /** In case caller code already knows this ID should be made local using copying. */ LIB_ID_MAKELOCAL_FORCE_COPY = 1 << 2, + /** Clear asset data (in case the ID can actually be made local, in copy case asset data is never + * copied over). */ + LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR = 1 << 3, + /* Special type-specific options. */ /** For Objects, do not clear the proxy pointers while making the data-block local. */ LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING = 1 << 16, @@ -261,19 +268,20 @@ struct ID *BKE_id_copy_ex(struct Main *bmain, const int flag); struct ID *BKE_id_copy_for_duplicate(struct Main *bmain, struct ID *id, - const uint duplicate_flags); + const uint duplicate_flags, + const int copy_flags); void BKE_lib_id_swap(struct Main *bmain, struct ID *id_a, struct ID *id_b); void BKE_lib_id_swap_full(struct Main *bmain, struct ID *id_a, struct ID *id_b); void id_sort_by_name(struct ListBase *lb, struct ID *id, struct ID *id_sorting_hint); -void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id); +void BKE_lib_id_expand_local(struct Main *bmain, struct ID *id, const int flags); bool BKE_id_new_name_validate(struct ListBase *lb, struct ID *id, const char *name, const bool do_linked_data) ATTR_NONNULL(1, 2); -void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id); +void BKE_lib_id_clear_library_data(struct Main *bmain, struct ID *id, const int flags); /* Affect whole Main database. */ void BKE_main_id_tag_idcode(struct Main *mainvar, diff --git a/source/blender/blenkernel/BKE_lib_override.h b/source/blender/blenkernel/BKE_lib_override.h index c6658ff424a..b94a1b33606 100644 --- a/source/blender/blenkernel/BKE_lib_override.h +++ b/source/blender/blenkernel/BKE_lib_override.h @@ -84,6 +84,8 @@ bool BKE_lib_override_library_proxy_convert(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Object *ob_proxy); +void BKE_lib_override_library_main_proxy_convert(struct Main *bmain, + struct BlendFileReadReport *reports); bool BKE_lib_override_library_resync(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, @@ -171,6 +173,8 @@ void BKE_lib_override_library_main_unused_cleanup(struct Main *bmain); void BKE_lib_override_library_update(struct Main *bmain, struct ID *local); void BKE_lib_override_library_main_update(struct Main *bmain); +bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id); + /* Storage (.blend file writing) part. */ /* For now, we just use a temp main list. */ diff --git a/source/blender/blenkernel/BKE_lib_query.h b/source/blender/blenkernel/BKE_lib_query.h index 9c49514e7b8..30c742e3af6 100644 --- a/source/blender/blenkernel/BKE_lib_query.h +++ b/source/blender/blenkernel/BKE_lib_query.h @@ -143,7 +143,8 @@ enum { typedef struct LibraryForeachIDData LibraryForeachIDData; -bool BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data, +bool BKE_lib_query_foreachid_iter_stop(struct LibraryForeachIDData *data); +void BKE_lib_query_foreachid_process(struct LibraryForeachIDData *data, struct ID **id_pp, int cb_flag); int BKE_lib_query_foreachid_process_flags_get(struct LibraryForeachIDData *data); @@ -154,22 +155,33 @@ int BKE_lib_query_foreachid_process_callback_flag_override(struct LibraryForeach #define BKE_LIB_FOREACHID_PROCESS_ID(_data, _id, _cb_flag) \ { \ CHECK_TYPE_ANY((_id), ID *, void *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return; \ } \ } \ ((void)0) -#define BKE_LIB_FOREACHID_PROCESS(_data, _id_super, _cb_flag) \ +#define BKE_LIB_FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return; \ } \ } \ ((void)0) -bool BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp); +#define BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(_data, _func_call) \ + { \ + _func_call; \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ + return; \ + } \ + } \ + ((void)0) + +void BKE_library_foreach_ID_embedded(struct LibraryForeachIDData *data, struct ID **id_pp); void BKE_lib_query_idpropertiesForeachIDLink_callback(struct IDProperty *id_prop, void *user_data); /* Loop over all of the ID's this datablock links to. */ diff --git a/source/blender/blenkernel/BKE_lib_remap.h b/source/blender/blenkernel/BKE_lib_remap.h index c70521f9593..5e154459a6c 100644 --- a/source/blender/blenkernel/BKE_lib_remap.h +++ b/source/blender/blenkernel/BKE_lib_remap.h @@ -89,6 +89,12 @@ enum { * dealing with IDs temporarily out of Main, but which will be put in it ultimately). */ ID_REMAP_FORCE_USER_REFCOUNT = 1 << 8, + /** + * Force obdata pointers to also be processed, even when object (`id_owner`) is in Edit mode. + * This is required by some tools creating/deleting IDs while operating in Edit mode, like e.g. + * the 'separate' mesh operator. + */ + ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 9, }; /* NOTE: Requiring new_id to be non-null, this *may* not be the case ultimately, @@ -111,8 +117,8 @@ void BKE_libblock_relink_ex(struct Main *bmain, void *new_idv, const short remap_flags) ATTR_NONNULL(1, 2); -void BKE_libblock_relink_to_newid(struct ID *id) ATTR_NONNULL(); -void BKE_libblock_relink_to_newid_new(struct Main *bmain, struct ID *id) ATTR_NONNULL(); +void BKE_libblock_relink_to_newid(struct Main *bmain, struct ID *id, const int remap_flag) + ATTR_NONNULL(); typedef void (*BKE_library_free_notifier_reference_cb)(const void *); typedef void (*BKE_library_remap_editor_id_reference_cb)(struct ID *, struct ID *); diff --git a/source/blender/blenkernel/BKE_main.h b/source/blender/blenkernel/BKE_main.h index ae60a5563b5..9ded97e0003 100644 --- a/source/blender/blenkernel/BKE_main.h +++ b/source/blender/blenkernel/BKE_main.h @@ -90,12 +90,12 @@ typedef struct MainIDRelationsEntry { } MainIDRelationsEntry; /* MainIDRelationsEntry.tags */ -typedef enum MainIDRelationsEntryTags { +typedef enum eMainIDRelationsEntryTags { /* Generic tag marking the entry as to be processed. */ MAINIDRELATIONS_ENTRY_TAGS_DOIT = 1 << 0, /* Generic tag marking the entry as processed. */ MAINIDRELATIONS_ENTRY_TAGS_PROCESSED = 1 << 1, -} MainIDRelationsEntryTags; +} eMainIDRelationsEntryTags; typedef struct MainIDRelations { /* Mapping from an ID pointer to all of its parents (IDs using it) and children (IDs it uses). @@ -201,24 +201,52 @@ typedef struct Main { struct Main *BKE_main_new(void); void BKE_main_free(struct Main *mainvar); +bool BKE_main_is_empty(struct Main *bmain); + void BKE_main_lock(struct Main *bmain); void BKE_main_unlock(struct Main *bmain); void BKE_main_relations_create(struct Main *bmain, const short flag); void BKE_main_relations_free(struct Main *bmain); void BKE_main_relations_tag_set(struct Main *bmain, - const MainIDRelationsEntryTags tag, + const eMainIDRelationsEntryTags tag, const bool value); struct GSet *BKE_main_gset_create(struct Main *bmain, struct GSet *gset); +/* + * Temporary runtime API to allow re-using local (already appended) IDs instead of appending a new + * copy again. + */ + +struct GHash *BKE_main_library_weak_reference_create(struct Main *bmain) ATTR_NONNULL(); +void BKE_main_library_weak_reference_destroy(struct GHash *library_weak_reference_mapping) + ATTR_NONNULL(); +struct ID *BKE_main_library_weak_reference_search_item( + struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name) ATTR_NONNULL(); +void BKE_main_library_weak_reference_add_item(struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + struct ID *new_id) ATTR_NONNULL(); +void BKE_main_library_weak_reference_update_item(struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + struct ID *old_id, + struct ID *new_id) ATTR_NONNULL(); +void BKE_main_library_weak_reference_remove_item(struct GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + struct ID *old_id) ATTR_NONNULL(); + /* *** Generic utils to loop over whole Main database. *** */ #define FOREACH_MAIN_LISTBASE_ID_BEGIN(_lb, _id) \ { \ - ID *_id_next = (_lb)->first; \ + ID *_id_next = (ID *)(_lb)->first; \ for ((_id) = _id_next; (_id) != NULL; (_id) = _id_next) { \ - _id_next = (_id)->next; + _id_next = (ID *)(_id)->next; #define FOREACH_MAIN_LISTBASE_ID_END \ } \ diff --git a/source/blender/blenkernel/BKE_material.h b/source/blender/blenkernel/BKE_material.h index 69e2d52e1dd..b1eaf7207fa 100644 --- a/source/blender/blenkernel/BKE_material.h +++ b/source/blender/blenkernel/BKE_material.h @@ -90,7 +90,7 @@ void BKE_object_material_array_assign(struct Main *bmain, short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma); bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob); bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob); -bool BKE_object_material_slot_used(struct ID *id, short actcol); +bool BKE_object_material_slot_used(struct Object *object, short actcol); struct Material *BKE_gpencil_material(struct Object *ob, short act); struct MaterialGPencilStyle *BKE_gpencil_material_settings(struct Object *ob, short act); diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index b0a8fee1178..f44d35c62a3 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -651,9 +651,8 @@ extern void (*BKE_mesh_batch_cache_free_cb)(struct Mesh *me); /* Inlines */ -/* Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h, - * but I don't want to force every user of BKE_mesh.h to also include that file. - * ~~ Sybren */ +/* NOTE(@sybren): Instead of -1 that function uses ORIGINDEX_NONE as defined in BKE_customdata.h, + * but I don't want to force every user of BKE_mesh.h to also include that file. */ BLI_INLINE int BKE_mesh_origindex_mface_mpoly(const int *index_mf_to_mpoly, const int *index_mp_to_orig, const int i) diff --git a/source/blender/blenkernel/BKE_mesh_sample.hh b/source/blender/blenkernel/BKE_mesh_sample.hh index 2fbf7372a09..17f8e766724 100644 --- a/source/blender/blenkernel/BKE_mesh_sample.hh +++ b/source/blender/blenkernel/BKE_mesh_sample.hh @@ -44,17 +44,20 @@ void sample_point_attribute(const Mesh &mesh, Span looptri_indices, Span bary_coords, const GVArray &data_in, + const IndexMask mask, GMutableSpan data_out); void sample_corner_attribute(const Mesh &mesh, Span looptri_indices, Span bary_coords, const GVArray &data_in, + const IndexMask mask, GMutableSpan data_out); void sample_face_attribute(const Mesh &mesh, Span looptri_indices, const GVArray &data_in, + const IndexMask mask, GMutableSpan data_out); enum class eAttributeMapMode { @@ -72,6 +75,7 @@ enum class eAttributeMapMode { class MeshAttributeInterpolator { private: const Mesh *mesh_; + const IndexMask mask_; const Span positions_; const Span looptri_indices_; @@ -80,9 +84,15 @@ class MeshAttributeInterpolator { public: MeshAttributeInterpolator(const Mesh *mesh, + const IndexMask mask, const Span positions, const Span looptri_indices); + void sample_data(const GVArray &src, + const AttributeDomain domain, + const eAttributeMapMode mode, + const GMutableSpan dst); + void sample_attribute(const ReadAttributeLookup &src_attribute, OutputAttribute &dst_attribute, eAttributeMapMode mode); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index a57281e4478..58fea6d462c 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -339,6 +339,10 @@ typedef struct bNodeType { /* Declares which sockets the node has. */ NodeDeclareFunction declare; + /* Different nodes of this type can have different declarations. */ + bool declaration_is_dynamic; + /* Declaration to be used when it is not dynamic. */ + NodeDeclarationHandle *fixed_declaration; /* RNA integration */ ExtensionRNA rna_ext; @@ -417,7 +421,7 @@ typedef struct bNodeTreeType { void (*local_sync)(struct bNodeTree *localtree, struct bNodeTree *ntree); void (*local_merge)(struct Main *bmain, struct bNodeTree *localtree, struct bNodeTree *ntree); - /* Tree update. Overrides nodetype->updatetreefunc! */ + /* Tree update. Overrides `nodetype->updatetreefunc` ! */ void (*update)(struct bNodeTree *ntree); bool (*validate_link)(struct bNodeTree *ntree, struct bNodeLink *link); @@ -443,7 +447,7 @@ void ntreeTypeFreeLink(const struct bNodeTreeType *nt); bool ntreeIsRegistered(struct bNodeTree *ntree); struct GHashIterator *ntreeTypeGetIterator(void); -/* helper macros for iterating over tree types */ +/* Helper macros for iterating over tree types. */ #define NODE_TREE_TYPES_BEGIN(ntype) \ { \ GHashIterator *__node_tree_type_iter__ = ntreeTypeGetIterator(); \ @@ -480,7 +484,7 @@ bool ntreeHasType(const struct bNodeTree *ntree, int type); bool ntreeHasTree(const struct bNodeTree *ntree, const struct bNodeTree *lookup); void ntreeUpdateTree(struct Main *main, struct bNodeTree *ntree); void ntreeUpdateAllNew(struct Main *main); -void ntreeUpdateAllUsers(struct Main *main, struct ID *id); +void ntreeUpdateAllUsers(struct Main *main, struct ID *id, int tree_update_flag); void ntreeGetDependencyList(struct bNodeTree *ntree, struct bNode ***r_deplist, @@ -548,7 +552,7 @@ void nodeUnregisterType(struct bNodeType *ntype); bool nodeTypeUndefined(struct bNode *node); struct GHashIterator *nodeTypeGetIterator(void); -/* helper macros for iterating over node types */ +/* Helper macros for iterating over node types. */ #define NODE_TYPES_BEGIN(ntype) \ { \ GHashIterator *__node_type_iter__ = nodeTypeGetIterator(); \ @@ -574,7 +578,7 @@ const char *nodeStaticSocketType(int type, int subtype); const char *nodeStaticSocketInterfaceType(int type, int subtype); const char *nodeStaticSocketLabel(int type, int subtype); -/* helper macros for iterating over node types */ +/* Helper macros for iterating over node types. */ #define NODE_SOCKET_TYPES_BEGIN(stype) \ { \ GHashIterator *__node_socket_type_iter__ = nodeSocketTypeGetIterator(); \ @@ -746,7 +750,8 @@ int BKE_node_clipboard_get_type(void); /* Node Instance Hash */ typedef struct bNodeInstanceHash { - GHash *ghash; /* XXX should be made a direct member, GHash allocation needs to support it */ + /** XXX should be made a direct member, #GHash allocation needs to support it */ + GHash *ghash; } bNodeInstanceHash; typedef void (*bNodeInstanceValueFP)(void *value); @@ -1102,6 +1107,7 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, #define SH_NODE_VERTEX_COLOR 706 #define SH_NODE_OUTPUT_AOV 707 #define SH_NODE_VECTOR_ROTATE 708 +#define SH_NODE_CURVE_FLOAT 709 /* custom defines options for Material node */ // #define SH_NODE_MAT_DIFF 1 @@ -1346,7 +1352,7 @@ void ntreeCompositCryptomatteLayerPrefix(const Scene *scene, const bNode *node, char *r_prefix, size_t prefix_len); -/* Update the runtime layer names with the cryptomatte layer names of the references +/* Update the runtime layer names with the crypto-matte layer names of the references * render layer or image. */ void ntreeCompositCryptomatteUpdateLayerNames(const Scene *scene, bNode *node); struct CryptomatteSession *ntreeCompositCryptomatteSession(const Scene *scene, bNode *node); @@ -1411,12 +1417,12 @@ int ntreeTexExecTree(struct bNodeTree *ntree, * \{ */ #define GEO_NODE_TRIANGULATE 1000 -#define GEO_NODE_EDGE_SPLIT 1001 +#define GEO_NODE_LEGACY_EDGE_SPLIT 1001 #define GEO_NODE_TRANSFORM 1002 -#define GEO_NODE_BOOLEAN 1003 +#define GEO_NODE_MESH_BOOLEAN 1003 #define GEO_NODE_LEGACY_POINT_DISTRIBUTE 1004 #define GEO_NODE_LEGACY_POINT_INSTANCE 1005 -#define GEO_NODE_SUBDIVISION_SURFACE 1006 +#define GEO_NODE_LEGACY_SUBDIVISION_SURFACE 1006 #define GEO_NODE_OBJECT_INFO 1007 #define GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE 1008 #define GEO_NODE_LEGACY_ATTRIBUTE_MATH 1009 @@ -1436,10 +1442,10 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_COLLECTION_INFO 1023 #define GEO_NODE_IS_VIEWPORT 1024 #define GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY 1025 -#define GEO_NODE_VOLUME_TO_MESH 1026 +#define GEO_NODE_LEGACY_VOLUME_TO_MESH 1026 #define GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ 1027 #define GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ 1028 -#define GEO_NODE_MESH_SUBDIVIDE 1029 +#define GEO_NODE_SUBDIVIDE_MESH 1029 #define GEO_NODE_ATTRIBUTE_REMOVE 1030 #define GEO_NODE_LEGACY_ATTRIBUTE_CONVERT 1031 #define GEO_NODE_MESH_PRIMITIVE_CUBE 1032 @@ -1451,23 +1457,23 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_MESH_PRIMITIVE_LINE 1038 #define GEO_NODE_MESH_PRIMITIVE_GRID 1039 #define GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE 1040 -#define GEO_NODE_LECAGY_ATTRIBUTE_CLAMP 1041 +#define GEO_NODE_LEGACY_ATTRIBUTE_CLAMP 1041 #define GEO_NODE_BOUNDING_BOX 1042 #define GEO_NODE_SWITCH 1043 #define GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER 1044 #define GEO_NODE_CURVE_TO_MESH 1045 #define GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP 1046 -#define GEO_NODE_CURVE_RESAMPLE 1047 -#define GEO_NODE_ATTRIBUTE_VECTOR_ROTATE 1048 +#define GEO_NODE_RESAMPLE_CURVE 1047 +#define GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE 1048 #define GEO_NODE_LEGACY_MATERIAL_ASSIGN 1049 #define GEO_NODE_INPUT_MATERIAL 1050 -#define GEO_NODE_MATERIAL_REPLACE 1051 +#define GEO_NODE_REPLACE_MATERIAL 1051 #define GEO_NODE_LEGACY_MESH_TO_CURVE 1052 #define GEO_NODE_LEGACY_DELETE_GEOMETRY 1053 #define GEO_NODE_CURVE_LENGTH 1054 #define GEO_NODE_LEGACY_SELECT_BY_MATERIAL 1055 #define GEO_NODE_CONVEX_HULL 1056 -#define GEO_NODE_CURVE_TO_POINTS 1057 +#define GEO_NODE_LEGACY_CURVE_TO_POINTS 1057 #define GEO_NODE_LEGACY_CURVE_REVERSE 1058 #define GEO_NODE_SEPARATE_COMPONENTS 1059 #define GEO_NODE_LEGACY_CURVE_SUBDIVIDE 1060 @@ -1479,20 +1485,72 @@ int ntreeTexExecTree(struct bNodeTree *ntree, #define GEO_NODE_CURVE_PRIMITIVE_CIRCLE 1066 #define GEO_NODE_VIEWER 1067 #define GEO_NODE_CURVE_PRIMITIVE_LINE 1068 -#define GEO_NODE_CURVE_ENDPOINTS 1069 +#define GEO_NODE_LEGACY_CURVE_ENDPOINTS 1069 #define GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL 1070 -#define GEO_NODE_CURVE_TRIM 1071 +#define GEO_NODE_TRIM_CURVE 1071 #define GEO_NODE_LEGACY_CURVE_SET_HANDLES 1072 #define GEO_NODE_LEGACY_CURVE_SPLINE_TYPE 1073 #define GEO_NODE_LEGACY_CURVE_SELECT_HANDLES 1074 -#define GEO_NODE_CURVE_FILL 1075 +#define GEO_NODE_FILL_CURVE 1075 #define GEO_NODE_INPUT_POSITION 1076 #define GEO_NODE_SET_POSITION 1077 #define GEO_NODE_INPUT_INDEX 1078 #define GEO_NODE_INPUT_NORMAL 1079 -#define GEO_NODE_ATTRIBUTE_CAPTURE 1080 +#define GEO_NODE_CAPTURE_ATTRIBUTE 1080 #define GEO_NODE_MATERIAL_SELECTION 1081 -#define GEO_NODE_MATERIAL_ASSIGN 1082 +#define GEO_NODE_SET_MATERIAL 1082 +#define GEO_NODE_REALIZE_INSTANCES 1083 +#define GEO_NODE_ATTRIBUTE_STATISTIC 1084 +#define GEO_NODE_SAMPLE_CURVE 1085 +#define GEO_NODE_INPUT_TANGENT 1086 +#define GEO_NODE_STRING_JOIN 1087 +#define GEO_NODE_CURVE_PARAMETER 1088 +#define GEO_NODE_FILLET_CURVE 1089 +#define GEO_NODE_DISTRIBUTE_POINTS_ON_FACES 1090 +#define GEO_NODE_STRING_TO_CURVES 1091 +#define GEO_NODE_INSTANCE_ON_POINTS 1092 +#define GEO_NODE_MESH_TO_POINTS 1093 +#define GEO_NODE_POINTS_TO_VERTICES 1094 +#define GEO_NODE_REVERSE_CURVE 1095 +#define GEO_NODE_PROXIMITY 1096 +#define GEO_NODE_SUBDIVIDE_CURVE 1097 +#define GEO_NODE_INPUT_SPLINE_LENGTH 1098 +#define GEO_NODE_CURVE_SPLINE_TYPE 1099 +#define GEO_NODE_CURVE_SET_HANDLES 1100 +#define GEO_NODE_POINTS_TO_VOLUME 1101 +#define GEO_NODE_CURVE_HANDLE_TYPE_SELECTION 1102 +#define GEO_NODE_DELETE_GEOMETRY 1103 +#define GEO_NODE_SEPARATE_GEOMETRY 1104 +#define GEO_NODE_INPUT_RADIUS 1105 +#define GEO_NODE_INPUT_CURVE_TILT 1106 +#define GEO_NODE_INPUT_CURVE_HANDLES 1107 +#define GEO_NODE_INPUT_SHADE_SMOOTH 1108 +#define GEO_NODE_INPUT_SPLINE_RESOLUTION 1109 +#define GEO_NODE_INPUT_SPLINE_CYCLIC 1110 +#define GEO_NODE_SET_CURVE_RADIUS 1111 +#define GEO_NODE_SET_CURVE_TILT 1112 +#define GEO_NODE_SET_CURVE_HANDLES 1113 +#define GEO_NODE_SET_SHADE_SMOOTH 1114 +#define GEO_NODE_SET_SPLINE_RESOLUTION 1115 +#define GEO_NODE_SET_SPLINE_CYCLIC 1116 +#define GEO_NODE_SET_POINT_RADIUS 1117 +#define GEO_NODE_INPUT_MATERIAL_INDEX 1118 +#define GEO_NODE_SET_MATERIAL_INDEX 1119 +#define GEO_NODE_TRANSLATE_INSTANCES 1120 +#define GEO_NODE_SCALE_INSTANCES 1121 +#define GEO_NODE_ROTATE_INSTANCES 1122 +#define GEO_NODE_SPLIT_EDGES 1123 +#define GEO_NODE_MESH_TO_CURVE 1124 +#define GEO_NODE_TRANSFER_ATTRIBUTE 1125 +#define GEO_NODE_SUBDIVISION_SURFACE 1126 +#define GEO_NODE_CURVE_ENDPOINT_SELECTION 1127 +#define GEO_NODE_RAYCAST 1128 +#define GEO_NODE_CURVE_TO_POINTS 1130 +#define GEO_NODE_INSTANCES_TO_POINTS 1131 +#define GEO_NODE_IMAGE_TEXTURE 1132 +#define GEO_NODE_VOLUME_TO_MESH 1133 +#define GEO_NODE_INPUT_ID 1134 +#define GEO_NODE_SET_ID 1135 /** \} */ @@ -1501,11 +1559,22 @@ int ntreeTexExecTree(struct bNodeTree *ntree, * \{ */ #define FN_NODE_BOOLEAN_MATH 1200 -#define FN_NODE_FLOAT_COMPARE 1202 -#define FN_NODE_RANDOM_FLOAT 1206 +#define FN_NODE_COMPARE_FLOATS 1202 +#define FN_NODE_LEGACY_RANDOM_FLOAT 1206 #define FN_NODE_INPUT_VECTOR 1207 #define FN_NODE_INPUT_STRING 1208 #define FN_NODE_FLOAT_TO_INT 1209 +#define FN_NODE_VALUE_TO_STRING 1210 +#define FN_NODE_STRING_LENGTH 1211 +#define FN_NODE_SLICE_STRING 1212 +#define FN_NODE_INPUT_SPECIAL_CHARACTERS 1213 +#define FN_NODE_RANDOM_VALUE 1214 +#define FN_NODE_ROTATE_EULER 1215 +#define FN_NODE_ALIGN_EULER_TO_VECTOR 1216 +#define FN_NODE_INPUT_COLOR 1217 +#define FN_NODE_REPLACE_STRING 1218 +#define FN_NODE_INPUT_BOOL 1219 +#define FN_NODE_INPUT_INT 1220 /** \} */ diff --git a/source/blender/blenkernel/BKE_ocean.h b/source/blender/blenkernel/BKE_ocean.h index 380f9045520..186e0ec174b 100644 --- a/source/blender/blenkernel/BKE_ocean.h +++ b/source/blender/blenkernel/BKE_ocean.h @@ -19,7 +19,7 @@ #include /** \file - * \ingroup bli + * \ingroup bke */ #ifdef __cplusplus diff --git a/source/blender/blenkernel/BKE_packedFile.h b/source/blender/blenkernel/BKE_packedFile.h index 8cb0c78d9aa..8ddf77e3d49 100644 --- a/source/blender/blenkernel/BKE_packedFile.h +++ b/source/blender/blenkernel/BKE_packedFile.h @@ -121,8 +121,8 @@ int BKE_packedfile_seek(struct PackedFile *pf, int offset, int whence); void BKE_packedfile_rewind(struct PackedFile *pf); int BKE_packedfile_read(struct PackedFile *pf, void *data, int size); -/* ID should be not NULL, return 1 if there's a packed file */ -bool BKE_packedfile_id_check(struct ID *id); +/* ID should be not NULL, return true if there's a packed file */ +bool BKE_packedfile_id_check(const struct ID *id); /* ID should be not NULL, throws error when ID is Library */ void BKE_packedfile_id_unpack(struct Main *bmain, struct ID *id, diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index 2d2406fb17b..3c691df15c3 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -544,7 +544,7 @@ typedef struct SculptSession { float cursor_sampled_normal[3]; float cursor_view_normal[3]; - /* For Sculpt trimming gesture tools, initial raycast data from the position of the mouse when + /* For Sculpt trimming gesture tools, initial ray-cast data from the position of the mouse when * the gesture starts (intersection with the surface and if they ray hit the surface or not). */ float gesture_initial_location[3]; float gesture_initial_normal[3]; diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 929a2d03b97..78eebec16e7 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -129,8 +129,8 @@ void BKE_pbvh_build_bmesh(PBVH *pbvh, void BKE_pbvh_free(PBVH *pbvh); /* Hierarchical Search in the BVH, two methods: - * - for each hit calling a callback - * - gather nodes in an array (easy to multithread) */ + * - For each hit calling a callback. + * - Gather nodes in an array (easy to multi-thread). */ void BKE_pbvh_search_callback(PBVH *pbvh, BKE_pbvh_SearchCallback scb, @@ -141,7 +141,7 @@ void BKE_pbvh_search_callback(PBVH *pbvh, void BKE_pbvh_search_gather( PBVH *pbvh, BKE_pbvh_SearchCallback scb, void *search_data, PBVHNode ***array, int *tot); -/* Raycast +/* Ray-cast * the hit callback is called for all leaf nodes intersecting the ray; * it's up to the callback to find the primitive within the leaves that is * hit first */ diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index 04a41d425bb..fd4d13f4125 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -29,22 +29,35 @@ extern "C" { struct UserDef; struct bUserAssetLibrary; -void BKE_preferences_asset_library_free(struct bUserAssetLibrary *library) ATTR_NONNULL(); +/** Name of the asset library added by default. Needs translation with `DATA_()` still. */ +#define BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME N_("User Library") struct bUserAssetLibrary *BKE_preferences_asset_library_add(struct UserDef *userdef, const char *name, const char *path) ATTR_NONNULL(1); +void BKE_preferences_asset_library_remove(struct UserDef *userdef, + struct bUserAssetLibrary *library) ATTR_NONNULL(); + void BKE_preferences_asset_library_name_set(struct UserDef *userdef, struct bUserAssetLibrary *library, const char *name) ATTR_NONNULL(); -void BKE_preferences_asset_library_remove(struct UserDef *userdef, - struct bUserAssetLibrary *library) ATTR_NONNULL(); - struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_index( const struct UserDef *userdef, int index) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; struct bUserAssetLibrary *BKE_preferences_asset_library_find_from_name( const struct UserDef *userdef, const char *name) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; + +/** + * Return the bUserAssetLibrary that contains the given file/directory path. The given path can be + * the library's top-level directory, or any path inside that directory. + * + * When more than one asset libraries match, the first matching one is returned (no smartness when + * there nested asset libraries). + * + * Return NULL when no such asset library is found. */ +struct bUserAssetLibrary *BKE_preferences_asset_library_containing_path( + const struct UserDef *userdef, const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; + int BKE_preferences_asset_library_get_index(const struct UserDef *userdef, const struct bUserAssetLibrary *library) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenkernel/BKE_report.h b/source/blender/blenkernel/BKE_report.h index 5b22918e84c..ec2e8d0f875 100644 --- a/source/blender/blenkernel/BKE_report.h +++ b/source/blender/blenkernel/BKE_report.h @@ -40,27 +40,27 @@ extern "C" { void BKE_reports_init(ReportList *reports, int flag); void BKE_reports_clear(ReportList *reports); -void BKE_report(ReportList *reports, ReportType type, const char *message); -void BKE_reportf(ReportList *reports, ReportType type, const char *format, ...) +void BKE_report(ReportList *reports, eReportType type, const char *message); +void BKE_reportf(ReportList *reports, eReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(3, 4); void BKE_reports_prepend(ReportList *reports, const char *prepend); void BKE_reports_prependf(ReportList *reports, const char *prepend, ...) ATTR_PRINTF_FORMAT(2, 3); -ReportType BKE_report_print_level(ReportList *reports); -void BKE_report_print_level_set(ReportList *reports, ReportType level); +eReportType BKE_report_print_level(ReportList *reports); +void BKE_report_print_level_set(ReportList *reports, eReportType level); -ReportType BKE_report_store_level(ReportList *reports); -void BKE_report_store_level_set(ReportList *reports, ReportType level); +eReportType BKE_report_store_level(ReportList *reports); +void BKE_report_store_level_set(ReportList *reports, eReportType level); -char *BKE_reports_string(ReportList *reports, ReportType level); -void BKE_reports_print(ReportList *reports, ReportType level); +char *BKE_reports_string(ReportList *reports, eReportType level); +void BKE_reports_print(ReportList *reports, eReportType level); Report *BKE_reports_last_displayable(ReportList *reports); -bool BKE_reports_contain(ReportList *reports, ReportType level); +bool BKE_reports_contain(ReportList *reports, eReportType level); -const char *BKE_report_type_str(ReportType type); +const char *BKE_report_type_str(eReportType type); bool BKE_report_write_file_fp(FILE *fp, ReportList *reports, const char *header); bool BKE_report_write_file(const char *filepath, ReportList *reports, const char *header); diff --git a/source/blender/blenkernel/BKE_rigidbody.h b/source/blender/blenkernel/BKE_rigidbody.h index ae1e437cd60..e28f668d189 100644 --- a/source/blender/blenkernel/BKE_rigidbody.h +++ b/source/blender/blenkernel/BKE_rigidbody.h @@ -18,7 +18,7 @@ */ /** \file - * \ingroup blenkernel + * \ingroup bke * \brief API for Blender-side Rigid Body stuff */ diff --git a/source/blender/blenkernel/BKE_screen.h b/source/blender/blenkernel/BKE_screen.h index 6f341a12b82..5c913ed851f 100644 --- a/source/blender/blenkernel/BKE_screen.h +++ b/source/blender/blenkernel/BKE_screen.h @@ -301,8 +301,6 @@ enum { PANEL_TYPE_LAYOUT_VERT_BAR = (1 << 3), /** This panel type represents data external to the UI. */ PANEL_TYPE_INSTANCED = (1 << 4), - /** Draw panel like a box widget. */ - PANEL_TYPE_DRAW_BOX = (1 << 6), /** Don't search panels with this type during property search. */ PANEL_TYPE_NO_SEARCH = (1 << 7), }; diff --git a/source/blender/blenkernel/BKE_sound.h b/source/blender/blenkernel/BKE_sound.h index fa58813c5f8..8796e2c18f3 100644 --- a/source/blender/blenkernel/BKE_sound.h +++ b/source/blender/blenkernel/BKE_sound.h @@ -96,13 +96,24 @@ typedef struct SoundInfo { eSoundChannels channels; } specs; float length; - double start_offset; } SoundInfo; +typedef struct SoundStreamInfo { + double duration; + double start; +} SoundStreamInfo; + /* Get information about given sound. Returns truth on success., false if sound can not be loaded * or if the codes is not supported. */ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *sound_info); +/* Get information about given sound. Returns truth on success., false if sound can not be loaded + * or if the codes is not supported. */ +bool BKE_sound_stream_info_get(struct Main *main, + const char *filepath, + int stream, + SoundStreamInfo *sound_info); + #if defined(WITH_AUDASPACE) AUD_Device *BKE_sound_mixdown(const struct Scene *scene, AUD_DeviceSpecs specs, diff --git a/source/blender/blenkernel/BKE_spline.hh b/source/blender/blenkernel/BKE_spline.hh index 0fbf39a52fa..8509b730709 100644 --- a/source/blender/blenkernel/BKE_spline.hh +++ b/source/blender/blenkernel/BKE_spline.hh @@ -316,6 +316,9 @@ class BezierSpline final : public Spline { void translate(const blender::float3 &translation) override; void transform(const blender::float4x4 &matrix) override; + void set_handle_position_right(const int index, const blender::float3 &value); + void set_handle_position_left(const int index, const blender::float3 &value); + bool point_is_sharp(const int index) const; void mark_cache_invalid() final; @@ -565,6 +568,9 @@ struct CurveEval { blender::Array control_point_offsets() const; blender::Array evaluated_point_offsets() const; + blender::Array accumulated_spline_lengths() const; + + void mark_cache_invalid(); void assert_valid_point_attributes() const; }; diff --git a/source/blender/blenkernel/BKE_subdiv.h b/source/blender/blenkernel/BKE_subdiv.h index 3d47c8d3bc5..2fb27fad30d 100644 --- a/source/blender/blenkernel/BKE_subdiv.h +++ b/source/blender/blenkernel/BKE_subdiv.h @@ -56,7 +56,7 @@ typedef enum eSubdivFVarLinearInterpolation { } eSubdivFVarLinearInterpolation; typedef struct SubdivSettings { - /* Simple subdivision corresponds to "Simple" option in the interface. When its enabled the + /* Simple subdivision corresponds to "Simple" option in the interface. When it's enabled the * subdivided mesh is not "smoothed": new vertices are added uniformly on the existing surface. * * On an OpenSubdiv implementation level this translates to a subdivision scheme: diff --git a/source/blender/blenkernel/BKE_subdiv_foreach.h b/source/blender/blenkernel/BKE_subdiv_foreach.h index a351b9a3d11..3f74299455d 100644 --- a/source/blender/blenkernel/BKE_subdiv_foreach.h +++ b/source/blender/blenkernel/BKE_subdiv_foreach.h @@ -127,7 +127,7 @@ typedef struct SubdivForeachContext { SubdivForeachVertexFromEdgeCb vertex_edge; /* Called exactly once, always corresponds to a single ptex face. */ SubdivForeachVertexInnerCb vertex_inner; - /* Called once for each loose vertex. One loose coarse vertexcorresponds + /* Called once for each loose vertex. One loose coarse vertex corresponds * to a single subdivision vertex. */ SubdivForeachLooseCb vertex_loose; @@ -144,7 +144,7 @@ typedef struct SubdivForeachContext { SubdivForeachPolygonCb poly; /* User-defined pointer, to allow callbacks know something about context the - * traversal is happening for, + * traversal is happening for. */ void *user_data; @@ -163,7 +163,7 @@ typedef struct SubdivForeachContext { * indices (for vertices, edges, loops, polygons) in the same way as subdivision * modifier will do for a dense mesh. * - * Returns truth if the whole topology was traversed, without any early exits. + * Returns true if the whole topology was traversed, without any early exits. * * TODO(sergey): Need to either get rid of subdiv or of coarse_mesh. * The main point here is to be able to get base level topology, which can be diff --git a/source/blender/blenkernel/BKE_undo_system.h b/source/blender/blenkernel/BKE_undo_system.h index 2973a432723..2a90211f8e0 100644 --- a/source/blender/blenkernel/BKE_undo_system.h +++ b/source/blender/blenkernel/BKE_undo_system.h @@ -102,11 +102,11 @@ typedef enum eUndoStepDir { STEP_INVALID = 0, } eUndoStepDir; -typedef enum UndoPushReturn { +typedef enum eUndoPushReturn { UNDO_PUSH_RET_FAILURE = 0, UNDO_PUSH_RET_SUCCESS = (1 << 0), UNDO_PUSH_RET_OVERRIDE_CHANGED = (1 << 1), -} UndoPushReturn; +} eUndoPushReturn; typedef void (*UndoTypeForEachIDRefFn)(void *user_data, struct UndoRefID *id_ref); @@ -156,7 +156,7 @@ typedef struct UndoType { } UndoType; /** #UndoType.flag bitflags. */ -typedef enum UndoTypeFlags { +typedef enum eUndoTypeFlags { /** * This undo type `encode` callback needs a valid context, it will fail otherwise. * \note Callback is still supposed to properly deal with a NULL context pointer. @@ -169,7 +169,7 @@ typedef enum UndoTypeFlags { * This is typically used for undo systems that store both before/after states. */ UNDOTYPE_FLAG_DECODE_ACTIVE_STEP = 1 << 1, -} UndoTypeFlags; +} eUndoTypeFlags; /* Expose since we need to perform operations on specific undo types (rarely). */ extern const UndoType *BKE_UNDOSYS_TYPE_IMAGE; @@ -204,11 +204,11 @@ UndoStep *BKE_undosys_step_push_init_with_type(UndoStack *ustack, const UndoType *ut); UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, struct bContext *C, const char *name); -UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, - struct bContext *C, - const char *name, - const UndoType *ut); -UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name); +eUndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, + struct bContext *C, + const char *name, + const UndoType *ut); +eUndoPushReturn BKE_undosys_step_push(UndoStack *ustack, struct bContext *C, const char *name); UndoStep *BKE_undosys_step_find_by_name_with_type(UndoStack *ustack, const char *name, diff --git a/source/blender/blenkernel/BKE_font.h b/source/blender/blenkernel/BKE_vfont.h similarity index 89% rename from source/blender/blenkernel/BKE_font.h rename to source/blender/blenkernel/BKE_vfont.h index 522d3843bb2..827ae1b6a0f 100644 --- a/source/blender/blenkernel/BKE_font.h +++ b/source/blender/blenkernel/BKE_vfont.h @@ -85,6 +85,15 @@ bool BKE_vfont_to_curve_ex(struct Object *ob, struct CharTrans **r_chartransdata); bool BKE_vfont_to_curve_nubase(struct Object *ob, int mode, struct ListBase *r_nubase); bool BKE_vfont_to_curve(struct Object *ob, int mode); +void BKE_vfont_build_char(struct Curve *cu, + struct ListBase *nubase, + unsigned int character, + struct CharInfo *info, + float ofsx, + float ofsy, + float rot, + int charidx, + const float fsize); int BKE_vfont_select_get(struct Object *ob, int *r_start, int *r_end); void BKE_vfont_select_clamp(struct Object *ob); diff --git a/source/blender/blenlib/BLI_vfontdata.h b/source/blender/blenkernel/BKE_vfontdata.h similarity index 82% rename from source/blender/blenlib/BLI_vfontdata.h rename to source/blender/blenkernel/BKE_vfontdata.h index 0bb32ca24b7..b6e57dad934 100644 --- a/source/blender/blenlib/BLI_vfontdata.h +++ b/source/blender/blenkernel/BKE_vfontdata.h @@ -20,7 +20,7 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke * \brief A structure to represent vector fonts, * and to load them from PostScript fonts. */ @@ -49,11 +49,11 @@ typedef struct VChar { float width; } VChar; -VFontData *BLI_vfontdata_from_freetypefont(struct PackedFile *pf); -VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int flag); +VFontData *BKE_vfontdata_from_freetypefont(struct PackedFile *pf); +VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int flag); -VChar *BLI_vfontchar_from_freetypefont(struct VFont *vfont, unsigned long character); -VChar *BLI_vfontchar_copy(const VChar *vchar_src, const int flag); +VChar *BKE_vfontdata_char_from_freetypefont(struct VFont *vfont, unsigned long character); +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_volume.h b/source/blender/blenkernel/BKE_volume.h index d9333996632..601e0cf26a9 100644 --- a/source/blender/blenkernel/BKE_volume.h +++ b/source/blender/blenkernel/BKE_volume.h @@ -18,7 +18,7 @@ /** \file * \ingroup bke - * \brief Volume datablock. + * \brief Volume data-block. */ #ifdef __cplusplus extern "C" { @@ -37,7 +37,7 @@ struct VolumeGridVector; void BKE_volumes_init(void); -/* Datablock Management */ +/* Data-block Management */ void BKE_volume_init_grids(struct Volume *volume); void *BKE_volume_add(struct Main *bmain, const char *name); @@ -122,13 +122,13 @@ void BKE_volume_grid_transform_matrix(const struct VolumeGrid *grid, float mat[4 /* Volume Editing * - * These are intended for modifiers to use on evaluated datablocks. + * These are intended for modifiers to use on evaluated data-blocks. * - * new_for_eval creates a volume datablock with no grids or file path, but + * new_for_eval creates a volume data-block with no grids or file path, but * preserves other settings such as viewport display options. * - * copy_for_eval creates a volume datablock preserving everything except the - * file path. Grids are shared with the source datablock, not copied. */ + * copy_for_eval creates a volume data-block preserving everything except the + * file path. Grids are shared with the source data-block, not copied. */ struct Volume *BKE_volume_new_for_eval(const struct Volume *volume_src); struct Volume *BKE_volume_copy_for_eval(struct Volume *volume_src, bool reference); @@ -160,6 +160,7 @@ bool BKE_volume_save(const struct Volume *volume, #ifdef __cplusplus # include "BLI_float3.hh" # include "BLI_float4x4.hh" +# include "BLI_string_ref.hh" bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::float3 &r_max); @@ -167,6 +168,10 @@ bool BKE_volume_min_max(const Volume *volume, blender::float3 &r_min, blender::f # include # include +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + blender::StringRef name, + openvdb::GridBase::Ptr vdb_grid); + bool BKE_volume_grid_bounds(openvdb::GridBase::ConstPtr grid, blender::float3 &r_min, blender::float3 &r_max); diff --git a/source/blender/blenkernel/BKE_volume_to_mesh.hh b/source/blender/blenkernel/BKE_volume_to_mesh.hh index 1f6e89636c4..9532da8c23c 100644 --- a/source/blender/blenkernel/BKE_volume_to_mesh.hh +++ b/source/blender/blenkernel/BKE_volume_to_mesh.hh @@ -14,6 +14,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_span.hh" + #include "DNA_modifier_types.h" #ifdef WITH_OPENVDB @@ -33,10 +35,40 @@ struct VolumeToMeshResolution { }; #ifdef WITH_OPENVDB + +/** + * The result of converting a volume grid to mesh data, in the format used by the OpenVDB API. + */ +struct OpenVDBMeshData { + std::vector verts; + std::vector tris; + std::vector quads; + bool is_empty() const + { + return verts.empty(); + } +}; + struct Mesh *volume_to_mesh(const openvdb::GridBase &grid, const VolumeToMeshResolution &resolution, const float threshold, const float adaptivity); + +struct OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity); + +void fill_mesh_from_openvdb_data(const Span vdb_verts, + const Span vdb_tris, + const Span vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan verts, + MutableSpan polys, + MutableSpan loops); + #endif } // namespace blender::bke diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 0b082bf1c5a..ef95d747948 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -60,6 +60,9 @@ set(INC set(INC_SYS ${ZLIB_INCLUDE_DIRS} + + # For `vfontdata_freetype.c`. + ${FREETYPE_INCLUDE_DIRS} ) set(SRC @@ -84,6 +87,10 @@ set(SRC intern/armature_selection.cc intern/armature_update.c intern/asset.cc + intern/asset_catalog.cc + intern/asset_catalog_path.cc + intern/asset_library.cc + intern/asset_library_service.cc intern/attribute.c intern/attribute_access.cc intern/attribute_math.cc @@ -116,7 +123,8 @@ set(SRC intern/curve_decimate.c intern/curve_deform.c intern/curve_eval.cc - intern/curveprofile.c + intern/curve_to_mesh_convert.cc + intern/curveprofile.cc intern/customdata.c intern/customdata_file.c intern/data_transfer.c @@ -130,12 +138,12 @@ set(SRC intern/editmesh_cache.c intern/editmesh_tangent.c intern/effect.c + intern/extern_implementations.cc intern/fcurve.c intern/fcurve_cache.c intern/fcurve_driver.c intern/fluid.c intern/fmodifier.c - intern/font.c intern/freestyle.c intern/geometry_component_curve.cc intern/geometry_component_instances.cc @@ -281,6 +289,8 @@ set(SRC intern/tracking_util.c intern/undo_system.c intern/unit.c + intern/vfont.c + intern/vfontdata_freetype.c intern/volume.cc intern/volume_render.cc intern/volume_to_mesh.cc @@ -302,6 +312,10 @@ set(SRC BKE_armature.h BKE_armature.hh BKE_asset.h + BKE_asset_catalog.hh + BKE_asset_catalog_path.hh + BKE_asset_library.h + BKE_asset_library.hh BKE_attribute.h BKE_attribute_access.hh BKE_attribute_math.hh @@ -332,6 +346,7 @@ set(SRC BKE_cryptomatte.h BKE_cryptomatte.hh BKE_curve.h + BKE_curve_to_mesh.hh BKE_curveprofile.h BKE_customdata.h BKE_customdata_file.h @@ -350,7 +365,6 @@ set(SRC BKE_fcurve.h BKE_fcurve_driver.h BKE_fluid.h - BKE_font.h BKE_freestyle.h BKE_geometry_set.h BKE_geometry_set.hh @@ -443,6 +457,8 @@ set(SRC BKE_tracking.h BKE_undo_system.h BKE_unit.h + BKE_vfont.h + BKE_vfontdata.h BKE_volume.h BKE_volume_render.h BKE_volume_to_mesh.hh @@ -456,6 +472,7 @@ set(SRC intern/CCGSubSurf.h intern/CCGSubSurf_inline.h intern/CCGSubSurf_intern.h + intern/asset_library_service.hh intern/attribute_access_intern.hh intern/data_transfer_intern.h intern/lib_intern.h @@ -492,6 +509,9 @@ set(LIB bf_rna bf_shader_fx bf_simulation + + # For `vfontdata_freetype.c`. + ${FREETYPE_LIBRARY} ) if(WITH_BINRELOC) @@ -781,6 +801,11 @@ if(WITH_GTESTS) set(TEST_SRC intern/action_test.cc intern/armature_test.cc + intern/asset_catalog_path_test.cc + intern/asset_catalog_test.cc + intern/asset_library_service_test.cc + intern/asset_library_test.cc + intern/asset_test.cc intern/cryptomatte_test.cc intern/fcurve_test.cc intern/lattice_deform_test.cc diff --git a/source/blender/blenkernel/intern/DerivedMesh.cc b/source/blender/blenkernel/intern/DerivedMesh.cc index 59e81938e79..4b1332d5b84 100644 --- a/source/blender/blenkernel/intern/DerivedMesh.cc +++ b/source/blender/blenkernel/intern/DerivedMesh.cc @@ -885,33 +885,6 @@ void BKE_mesh_wrapper_deferred_finalize(Mesh *me_eval, BLI_assert(me_eval->runtime.wrapper_type_finalize == 0); } -/** - * Some modifiers don't work on geometry sets directly, but expect a single mesh as input. - * Therefore, we convert data from the geometry set into a single mesh, so that those - * modifiers can work on it as well. - */ -static Mesh *prepare_geometry_set_for_mesh_modifier(Mesh *mesh, GeometrySet &r_geometry_set) -{ - if (!r_geometry_set.has_instances() && !r_geometry_set.has_pointcloud()) { - return mesh; - } - - { - /* Add the mesh to the geometry set. */ - MeshComponent &mesh_component = r_geometry_set.get_component_for_write(); - mesh_component.replace(mesh, GeometryOwnershipType::Editable); - } - { - /* Combine mesh and all instances into a single mesh that can be passed to the modifier. */ - GeometrySet new_geometry_set = blender::bke::geometry_set_realize_mesh_for_modifier( - r_geometry_set); - MeshComponent &mesh_component = new_geometry_set.get_component_for_write(); - Mesh *new_mesh = mesh_component.release(); - r_geometry_set = new_geometry_set; - return new_mesh; - } -} - /** * Modifies the given mesh and geometry set. The mesh is not passed as part of the mesh component * in the \a geometry_set input, it is only passed in \a input_mesh and returned in the return @@ -928,14 +901,7 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md, Mesh *mesh_output = nullptr; const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type); if (mti->modifyGeometrySet == nullptr) { - Mesh *new_input_mesh = prepare_geometry_set_for_mesh_modifier(input_mesh, geometry_set); - mesh_output = BKE_modifier_modify_mesh(md, &mectx, new_input_mesh); - - /* The caller is responsible for freeing `input_mesh` and `mesh_output`. The intermediate - * `new_input_mesh` has to be freed here. */ - if (!ELEM(new_input_mesh, input_mesh, mesh_output)) { - BKE_id_free(nullptr, new_input_mesh); - } + mesh_output = BKE_modifier_modify_mesh(md, &mectx, input_mesh); } else { /* For performance reasons, this should be called by the modifier and/or nodes themselves at @@ -955,6 +921,10 @@ static Mesh *modifier_modify_mesh_and_geometry_set(ModifierData *md, /* Release the mesh from the geometry set again. */ if (geometry_set.has()) { MeshComponent &mesh_component = geometry_set.get_component_for_write(); + if (mesh_component.get_for_read() != input_mesh) { + /* Make sure the mesh component actually owns the mesh before taking over ownership. */ + mesh_component.ensure_owns_direct_data(); + } mesh_output = mesh_component.release(); } @@ -1177,14 +1147,6 @@ static void mesh_calc_modifiers(struct Depsgraph *depsgraph, /* No existing verts to deform, need to build them. */ if (!deformed_verts) { if (mesh_final) { - Mesh *mesh_final_new = prepare_geometry_set_for_mesh_modifier(mesh_final, - geometry_set_final); - if (mesh_final_new != mesh_final) { - BLI_assert(mesh_final != mesh_input); - BKE_id_free(nullptr, mesh_final); - mesh_final = mesh_final_new; - } - /* Deforming a mesh, read the vertex locations * out of the mesh and deform them. Once done with this * run of deformers verts will be written back. */ diff --git a/source/blender/blenkernel/intern/action.c b/source/blender/blenkernel/intern/action.c index 16d269f9e26..cae72ddf68c 100644 --- a/source/blender/blenkernel/intern/action.c +++ b/source/blender/blenkernel/intern/action.c @@ -175,11 +175,11 @@ static void action_foreach_id(ID *id, LibraryForeachIDData *data) bAction *act = (bAction *)id; LISTBASE_FOREACH (FCurve *, fcu, &act->curves) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } LISTBASE_FOREACH (TimeMarker *, marker, &act->markers) { - BKE_LIB_FOREACHID_PROCESS(data, marker->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); } } @@ -1211,7 +1211,7 @@ void BKE_pose_channel_copy_data(bPoseChannel *pchan, const bPoseChannel *pchan_f /* copy bone group */ pchan->agrp_index = pchan_from->agrp_index; - /* ik (dof) settings */ + /* IK (DOF) settings. */ pchan->ikflag = pchan_from->ikflag; copy_v3_v3(pchan->limitmin, pchan_from->limitmin); copy_v3_v3(pchan->limitmax, pchan_from->limitmax); diff --git a/source/blender/blenkernel/intern/action_mirror.c b/source/blender/blenkernel/intern/action_mirror.c index 48472dfc9b3..cc3a15aa546 100644 --- a/source/blender/blenkernel/intern/action_mirror.c +++ b/source/blender/blenkernel/intern/action_mirror.c @@ -327,10 +327,10 @@ static void action_flip_pchan(Object *ob_arm, * the X-axis, it turns into a 180 degree rotation over the Y-axis. * This has only been observed with bones that can't be flipped, * hence the check for `pchan_flip`. */ - const float unit_x[4] = {1.0f, 0.0f, 0.0f, 0.0f}; - const bool is_problematic = pchan_flip == NULL && - fabsf(dot_v4v4(pchan->bone->arm_mat[0], unit_x)) <= 1e-6; - if (is_problematic) { + const float unit_x[3] = {1.0f, 0.0f, 0.0f}; + const bool is_x_axis_orthogonal = (pchan_flip == NULL) && + (fabsf(dot_v3v3(pchan->bone->arm_mat[0], unit_x)) <= 1e-6f); + if (is_x_axis_orthogonal) { /* Matrix needs to flip both the X and Z axes to come out right. */ float extra_mat[4][4] = { {-1.0f, 0.0f, 0.0f, 0.0f}, diff --git a/source/blender/blenkernel/intern/anim_data.c b/source/blender/blenkernel/intern/anim_data.c index 7e4ab754500..21887d514d9 100644 --- a/source/blender/blenkernel/intern/anim_data.c +++ b/source/blender/blenkernel/intern/anim_data.c @@ -294,11 +294,11 @@ bool BKE_animdata_id_is_animated(const struct ID *id) void BKE_animdata_foreach_id(AnimData *adt, LibraryForeachIDData *data) { LISTBASE_FOREACH (FCurve *, fcu, &adt->drivers) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } - BKE_LIB_FOREACHID_PROCESS(data, adt->action, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, adt->tmpact, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->action, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, adt->tmpact, IDWALK_CB_USER); LISTBASE_FOREACH (NlaTrack *, nla_track, &adt->nla_tracks) { LISTBASE_FOREACH (NlaStrip *, nla_strip, &nla_track->strips) { diff --git a/source/blender/blenkernel/intern/anim_sys.c b/source/blender/blenkernel/intern/anim_sys.c index 92b0db5b214..cbdcf43c039 100644 --- a/source/blender/blenkernel/intern/anim_sys.c +++ b/source/blender/blenkernel/intern/anim_sys.c @@ -1383,7 +1383,7 @@ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values) switch (RNA_property_type(prop)) { case PROP_BOOLEAN: - tmp_bool = MEM_malloc_arrayN(sizeof(*tmp_bool), length, __func__); + tmp_bool = MEM_malloc_arrayN(length, sizeof(*tmp_bool), __func__); RNA_property_boolean_get_default_array(ptr, prop, tmp_bool); for (int i = 0; i < length; i++) { r_values[i] = (float)tmp_bool[i]; @@ -1391,7 +1391,7 @@ static void nlaevalchan_get_default_values(NlaEvalChannel *nec, float *r_values) MEM_freeN(tmp_bool); break; case PROP_INT: - tmp_int = MEM_malloc_arrayN(sizeof(*tmp_int), length, __func__); + tmp_int = MEM_malloc_arrayN(length, sizeof(*tmp_int), __func__); RNA_property_int_get_default_array(ptr, prop, tmp_int); for (int i = 0; i < length; i++) { r_values[i] = (float)tmp_int[i]; diff --git a/source/blender/blenkernel/intern/anonymous_attribute.cc b/source/blender/blenkernel/intern/anonymous_attribute.cc index 67611053d83..22c2f83e8be 100644 --- a/source/blender/blenkernel/intern/anonymous_attribute.cc +++ b/source/blender/blenkernel/intern/anonymous_attribute.cc @@ -97,6 +97,7 @@ void BKE_anonymous_attribute_id_decrement_weak(const AnonymousAttributeID *anony { const int new_refcount = anonymous_id->refcount_tot.fetch_sub(1) - 1; if (new_refcount == 0) { + BLI_assert(anonymous_id->refcount_strong == 0); delete anonymous_id; } } diff --git a/source/blender/blenkernel/intern/appdir.c b/source/blender/blenkernel/intern/appdir.c index eae331fc7d1..d872dc67dcb 100644 --- a/source/blender/blenkernel/intern/appdir.c +++ b/source/blender/blenkernel/intern/appdir.c @@ -148,11 +148,12 @@ static char *blender_version_decimal(const int version) * \{ */ /** - * Get the folder that's the "natural" starting point for browsing files on an OS. On Unix that is - * $HOME, on Windows it is %userprofile%/Documents. + * Get the folder that's the "natural" starting point for browsing files on an OS. + * - Unix: `$HOME` + * - Windows: `%userprofile%/Documents` * * \note On Windows `Users/{MyUserName}/Documents` is used as it's the default location to save - * documents. + * documents. */ const char *BKE_appdir_folder_default(void) { @@ -169,8 +170,30 @@ const char *BKE_appdir_folder_default(void) #endif /* WIN32 */ } +const char *BKE_appdir_folder_root(void) +{ +#ifndef WIN32 + return "/"; +#else + static char root[4]; + BLI_windows_get_default_root_dir(root); + return root; +#endif +} + +const char *BKE_appdir_folder_default_or_root(void) +{ + const char *path = BKE_appdir_folder_default(); + if (path == NULL) { + path = BKE_appdir_folder_root(); + } + return path; +} + /** - * Get the user's home directory, i.e. $HOME on UNIX, %userprofile% on Windows. + * Get the user's home directory, i.e. + * - Unix: `$HOME` + * - Windows: `%userprofile%` */ const char *BKE_appdir_folder_home(void) { @@ -182,8 +205,11 @@ const char *BKE_appdir_folder_home(void) } /** - * Get the user's document directory, i.e. $HOME/Documents on Linux, %userprofile%/Documents on - * Windows. If this can't be found using OS queries (via Ghost), try manually finding it. + * Get the user's document directory, i.e. + * - Linux: `$HOME/Documents` + * - Windows: `%userprofile%/Documents` + * + * If this can't be found using OS queries (via Ghost), try manually finding it. * * \returns True if the path is valid and points to an existing directory. */ @@ -217,26 +243,67 @@ bool BKE_appdir_folder_documents(char *dir) return true; } +/** + * Get the user's cache directory, i.e. + * - Linux: `$HOME/.cache/blender/` + * - Windows: `%USERPROFILE%\AppData\Local\Blender Foundation\Blender\` + * - MacOS: `/Library/Caches/Blender` + * + * \returns True if the path is valid. It doesn't create or checks format + * if the `blender` folder exists. It does check if the parent of the path exists. + */ +bool BKE_appdir_folder_caches(char *r_path, const size_t path_len) +{ + r_path[0] = '\0'; + + const char *caches_root_path = GHOST_getUserSpecialDir(GHOST_kUserSpecialDirCaches); + if (caches_root_path == NULL || !BLI_is_dir(caches_root_path)) { + caches_root_path = BKE_tempdir_base(); + } + if (caches_root_path == NULL || !BLI_is_dir(caches_root_path)) { + return false; + } + +#ifdef WIN32 + BLI_path_join( + r_path, path_len, caches_root_path, "Blender Foundation", "Blender", "Cache", SEP_STR, NULL); +#elif defined(__APPLE__) + BLI_path_join(r_path, path_len, caches_root_path, "Blender", SEP_STR, NULL); +#else /* __linux__ */ + BLI_path_join(r_path, path_len, caches_root_path, "blender", SEP_STR, NULL); +#endif + + return true; +} + /** * Gets a good default directory for fonts. */ -bool BKE_appdir_font_folder_default( - /* This parameter can only be `const` on non-windows platforms. - * NOLINTNEXTLINE: readability-non-const-parameter. */ - char *dir) +bool BKE_appdir_font_folder_default(char *dir) { - bool success = false; + char test_dir[FILE_MAXDIR]; + test_dir[0] = '\0'; + #ifdef WIN32 wchar_t wpath[FILE_MAXDIR]; - success = SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0); - if (success) { + if (SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0)) { wcscat(wpath, L"\\"); - BLI_strncpy_wchar_as_utf8(dir, wpath, FILE_MAXDIR); + BLI_strncpy_wchar_as_utf8(test_dir, wpath, sizeof(test_dir)); } +#elif defined(__APPLE__) + const char *home = BLI_getenv("HOME"); + if (home) { + BLI_path_join(test_dir, sizeof(test_dir), home, "Library", "Fonts", NULL); + } +#else + STRNCPY(test_dir, "/usr/share/fonts"); #endif - /* TODO: Values for other platforms. */ - UNUSED_VARS(dir); - return success; + + if (test_dir[0] && BLI_exists(test_dir)) { + BLI_strncpy(dir, test_dir, FILE_MAXDIR); + return true; + } + return false; } /** \} */ diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index 87320c88b1b..b830c9de5f5 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -161,30 +161,36 @@ static void armature_free_data(struct ID *id) static void armature_foreach_id_bone(Bone *bone, LibraryForeachIDData *data) { - IDP_foreach_property( - bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property( + bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data)); LISTBASE_FOREACH (Bone *, curbone, &bone->childbase) { - armature_foreach_id_bone(curbone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(curbone, data)); } } static void armature_foreach_id_editbone(EditBone *edit_bone, LibraryForeachIDData *data) { - IDP_foreach_property( - edit_bone->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(edit_bone->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); } static void armature_foreach_id(ID *id, LibraryForeachIDData *data) { bArmature *arm = (bArmature *)id; LISTBASE_FOREACH (Bone *, bone, &arm->bonebase) { - armature_foreach_id_bone(bone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_bone(bone, data)); } if (arm->edbo != NULL) { LISTBASE_FOREACH (EditBone *, edit_bone, arm->edbo) { - armature_foreach_id_editbone(edit_bone, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, armature_foreach_id_editbone(edit_bone, data)); } } } @@ -315,7 +321,7 @@ IDTypeInfo IDType_ID_AR = { .name = "Armature", .name_plural = "armatures", .translation_context = BLT_I18NCONTEXT_ID_ARMATURE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = armature_init_data, .copy_data = armature_copy_data, @@ -1495,13 +1501,13 @@ static void allocate_bbone_cache(bPoseChannel *pchan, int segments) runtime->bbone_segments = segments; runtime->bbone_rest_mats = MEM_malloc_arrayN( - sizeof(Mat4), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_rest_mats"); + 1 + (uint)segments, sizeof(Mat4), "bPoseChannel_Runtime::bbone_rest_mats"); runtime->bbone_pose_mats = MEM_malloc_arrayN( - sizeof(Mat4), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_pose_mats"); + 1 + (uint)segments, sizeof(Mat4), "bPoseChannel_Runtime::bbone_pose_mats"); runtime->bbone_deform_mats = MEM_malloc_arrayN( - sizeof(Mat4), 2 + (uint)segments, "bPoseChannel_Runtime::bbone_deform_mats"); + 2 + (uint)segments, sizeof(Mat4), "bPoseChannel_Runtime::bbone_deform_mats"); runtime->bbone_dual_quats = MEM_malloc_arrayN( - sizeof(DualQuat), 1 + (uint)segments, "bPoseChannel_Runtime::bbone_dual_quats"); + 1 + (uint)segments, sizeof(DualQuat), "bPoseChannel_Runtime::bbone_dual_quats"); } } @@ -2227,41 +2233,48 @@ void mat3_vec_to_roll(const float mat[3][3], const float vec[3], float *r_roll) * * * When y is close to -1, computing 1 / (1 + y) will cause severe numerical instability, - * so we ignore it and normalize M instead. + * so we use a different approach based on x and z as inputs. * We know `y^2 = 1 - (x^2 + z^2)`, and `y < 0`, hence `y = -sqrt(1 - (x^2 + z^2))`. * - * Since x and z are both close to 0, we apply the binomial expansion to the first order: - * `y = -sqrt(1 - (x^2 + z^2)) = -1 + (x^2 + z^2) / 2`. Which gives: + * Since x and z are both close to 0, we apply the binomial expansion to the second order: + * `y = -sqrt(1 - (x^2 + z^2)) = -1 + (x^2 + z^2) / 2 + (x^2 + z^2)^2 / 8`, which allows + * eliminating the problematic `1` constant. + * + * A first order expansion allows simplifying to this, but second order is more precise: *
  *                        ┌  z^2 - x^2,  -2 * x * z ┐
  * M* = 1 / (x^2 + z^2) * │                         │
  *                        └ -2 * x * z,   x^2 - z^2 ┘
  * 
+ * + * P.S. In the end, this basically is a heavily optimized version of Damped Track +Y. */ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_mat[3][3]) { - const float THETA_SAFE = 1.0e-5f; /* theta above this value are always safe to use. */ - const float THETA_CRITICAL = 1.0e-9f; /* above this is safe under certain conditions. */ + const float SAFE_THRESHOLD = 6.1e-3f; /* Theta above this value has good enough precision. */ + const float CRITICAL_THRESHOLD = 2.5e-4f; /* True singularity if XZ distance is below this. */ + const float THRESHOLD_SQUARED = CRITICAL_THRESHOLD * CRITICAL_THRESHOLD; const float x = nor[0]; const float y = nor[1]; const float z = nor[2]; - const float theta = 1.0f + y; - const float theta_alt = x * x + z * z; + float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */ + const float theta_alt = x * x + z * z; /* squared distance from origin in x,z plane. */ float rMatrix[3][3], bMatrix[3][3]; BLI_ASSERT_UNIT_V3(nor); - /* When theta is close to zero (nor is aligned close to negative Y Axis), + /* Determine if the input is far enough from the true singularity of this type of + * transformation at (0,-1,0), where roll becomes 0/0 undefined without a limit. + * + * When theta is close to zero (nor is aligned close to negative Y Axis), * we have to check we do have non-null X/Z components as well. * Also, due to float precision errors, nor can be (0.0, -0.99999994, 0.0) which results * in theta being close to zero. This will cause problems when theta is used as divisor. */ - if (theta > THETA_SAFE || ((x || z) && theta > THETA_CRITICAL)) { - /* nor is *not* aligned to negative Y-axis (0,-1,0). - * We got these values for free... so be happy with it... ;) - */ + if (theta > SAFE_THRESHOLD || theta_alt > THRESHOLD_SQUARED) { + /* nor is *not* aligned to negative Y-axis (0,-1,0). */ bMatrix[0][1] = -x; bMatrix[1][0] = x; @@ -2269,18 +2282,15 @@ void vec_roll_to_mat3_normalized(const float nor[3], const float roll, float r_m bMatrix[1][2] = z; bMatrix[2][1] = -z; - if (theta > THETA_SAFE) { - /* nor differs significantly from negative Y axis (0,-1,0): apply the general case. */ - bMatrix[0][0] = 1 - x * x / theta; - bMatrix[2][2] = 1 - z * z / theta; - bMatrix[2][0] = bMatrix[0][2] = -x * z / theta; - } - else { - /* nor is close to negative Y axis (0,-1,0): apply the special case. */ - bMatrix[0][0] = (x + z) * (x - z) / -theta_alt; - bMatrix[2][2] = -bMatrix[0][0]; - bMatrix[2][0] = bMatrix[0][2] = 2.0f * x * z / theta_alt; + if (theta <= SAFE_THRESHOLD) { + /* When nor is close to negative Y axis (0,-1,0) the theta precision is very bad, + * so recompute it from x and z instead, using the series expansion for sqrt. */ + theta = theta_alt * 0.5f + theta_alt * theta_alt * 0.125f; } + + bMatrix[0][0] = 1 - x * x / theta; + bMatrix[2][2] = 1 - z * z / theta; + bMatrix[2][0] = bMatrix[0][2] = -x * z / theta; } else { /* nor is very close to negative Y axis (0,-1,0): use simple symmetry by Z axis. */ @@ -2763,7 +2773,7 @@ void BKE_pose_where_is_bone(struct Depsgraph *depsgraph, float ctime, bool do_extra) { - /* This gives a chan_mat with actions (ipos) results. */ + /* This gives a chan_mat with actions (F-curve) results. */ if (do_extra) { BKE_pchan_calc_mat(pchan); } @@ -2966,10 +2976,16 @@ bool BKE_pose_minmax(Object *ob, float r_min[3], float r_max[3], bool use_hidden BKE_object_boundbox_get(pchan->custom) : NULL; if (bb_custom) { - float mat[4][4], smat[4][4]; + float mat[4][4], smat[4][4], rmat[4][4], tmp[4][4]; scale_m4_fl(smat, PCHAN_CUSTOM_BONE_LENGTH(pchan)); rescale_m4(smat, pchan->custom_scale_xyz); - mul_m4_series(mat, ob->obmat, pchan_tx->pose_mat, smat); + eulO_to_mat4(rmat, pchan->custom_rotation_euler, ROT_MODE_XYZ); + copy_m4_m4(tmp, pchan_tx->pose_mat); + translate_m4(tmp, + pchan->custom_translation[0], + pchan->custom_translation[1], + pchan->custom_translation[2]); + mul_m4_series(mat, ob->obmat, tmp, rmat, smat); BKE_boundbox_minmax(bb_custom, mat, r_min, r_max); } else { diff --git a/source/blender/blenkernel/intern/armature_test.cc b/source/blender/blenkernel/intern/armature_test.cc index 2994563175f..a6d9a1f41e9 100644 --- a/source/blender/blenkernel/intern/armature_test.cc +++ b/source/blender/blenkernel/intern/armature_test.cc @@ -30,6 +30,36 @@ namespace blender::bke::tests { static const float FLOAT_EPSILON = 1.2e-7; +static const float SCALE_EPSILON = 3.71e-5; +static const float ORTHO_EPSILON = 5e-5; + +/** Test that the matrix is orthogonal, i.e. has no scale or shear within acceptable precision. */ +static double EXPECT_M3_ORTHOGONAL(const float mat[3][3], + double epsilon_scale, + double epsilon_ortho) +{ + /* Do the checks in double precision to avoid precision issues in the checks themselves. */ + double dmat[3][3]; + copy_m3d_m3(dmat, mat); + + /* Check individual axis scaling. */ + EXPECT_NEAR(len_v3_db(dmat[0]), 1.0, epsilon_scale); + EXPECT_NEAR(len_v3_db(dmat[1]), 1.0, epsilon_scale); + EXPECT_NEAR(len_v3_db(dmat[2]), 1.0, epsilon_scale); + + /* Check orthogonality. */ + EXPECT_NEAR(dot_v3v3_db(dmat[0], dmat[1]), 0.0, epsilon_ortho); + EXPECT_NEAR(dot_v3v3_db(dmat[0], dmat[2]), 0.0, epsilon_ortho); + EXPECT_NEAR(dot_v3v3_db(dmat[1], dmat[2]), 0.0, epsilon_ortho); + + /* Check determinant to detect flipping and as a secondary volume change check. */ + double determinant = determinant_m3_array_db(dmat); + + EXPECT_NEAR(determinant, 1.0, epsilon_ortho); + + return determinant; +} + TEST(mat3_vec_to_roll, UnitMatrix) { float unit_matrix[3][3]; @@ -93,73 +123,246 @@ TEST(mat3_vec_to_roll, Rotationmatrix) } } -TEST(vec_roll_to_mat3_normalized, Rotationmatrix) +/** Generic function to test vec_roll_to_mat3_normalized. */ +static double test_vec_roll_to_mat3_normalized(const float input[3], + float roll, + const float expected_roll_mat[3][3], + bool normalize = true) { - float negative_y_axis[3][3]; - unit_m3(negative_y_axis); - negative_y_axis[0][0] = negative_y_axis[1][1] = -1.0f; - - const float roll = 0.0f; + float input_normalized[3]; float roll_mat[3][3]; - /* If normalized_vector is -Y, simple symmetry by Z axis. */ - { - const float normalized_vector[3] = {0.0f, -1.0f, 0.0f}; - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, negative_y_axis, FLT_EPSILON); + if (normalize) { + /* The vector is re-normalized to replicate the actual usage. */ + normalize_v3_v3(input_normalized, input); + } + else { + copy_v3_v3(input_normalized, input); } - /* If normalized_vector is far enough from -Y, apply the general case. */ - { - const float expected_roll_mat[3][3] = {{1.000000f, 0.000000f, 0.000000f}, - {0.000000f, -0.999989986f, -0.000000f}, - {0.000000f, 0.000000f, 1.000000f}}; + vec_roll_to_mat3_normalized(input_normalized, roll, roll_mat); - const float normalized_vector[3] = {0.0f, -1.0f + 1e-5f, 0.0f}; - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); + EXPECT_V3_NEAR(roll_mat[1], input_normalized, FLT_EPSILON); + + if (expected_roll_mat) { EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); } -#if 0 - /* TODO: This test will pass after fixing T82455) */ - /* If normalized_vector is close to -Y and - * it has X and Z values above a threshold, - * apply the special case. */ - { - const float expected_roll_mat[3][3] = {{0.000000f, -9.99999975e-06f, 1.000000f}, - {9.99999975e-06f, -0.999999881f, 9.99999975e-06f}, - {1.000000f, -9.99999975e-06, 0.000000f}}; - const float normalized_vector[3] = {1e-24, -0.999999881, 0}; - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); - } -#endif - - /* If normalized_vector is in a critical range close to -Y, apply the special case. */ - { - const float expected_roll_mat[3][3] = {{0.000000f, -9.99999975e-06f, 1.000000f}, - {9.99999975e-06f, -0.999999881f, 9.99999975e-06f}, - {1.000000f, -9.99999975e-06f, 0.000000f}}; - - const float normalized_vector[3] = {1e-5f, -0.999999881f, 1e-5f}; /* Corner Case. */ - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); - } - - /* If normalized_vector is far enough from -Y, apply the general case. */ - { - const float expected_roll_mat[3][3] = {{0.788675129f, -0.577350259f, -0.211324856f}, - {0.577350259f, 0.577350259f, 0.577350259f}, - {-0.211324856f, -0.577350259f, 0.788675129f}}; - - const float vector[3] = {1.0f, 1.0f, 1.0f}; /* Arbitrary Value. */ - float normalized_vector[3]; - normalize_v3_v3(normalized_vector, vector); - vec_roll_to_mat3_normalized(normalized_vector, roll, roll_mat); - EXPECT_M3_NEAR(roll_mat, expected_roll_mat, FLT_EPSILON); - } + return EXPECT_M3_ORTHOGONAL(roll_mat, SCALE_EPSILON, ORTHO_EPSILON); } +/** Binary search to test where the code switches to the most degenerate special case. */ +static double find_flip_boundary(double x, double z) +{ + /* Irrational scale factor to ensure values aren't 'nice', have a lot of rounding errors, + * and can't accidentally produce the exact result returned by the special case. */ + const double scale = M_1_PI / 10; + double theta = x * x + z * z; + double minv = 0, maxv = 1e-2; + + while (maxv - minv > FLT_EPSILON * 1e-3) { + double mid = (minv + maxv) / 2; + + float roll_mat[3][3]; + float input[3] = {float(x * mid * scale), + -float(sqrt(1 - theta * mid * mid) * scale), + float(z * mid * scale)}; + + normalize_v3(input); + vec_roll_to_mat3_normalized(input, 0, roll_mat); + + /* The special case assigns exact constants rather than computing. */ + if (roll_mat[0][0] == -1 && roll_mat[0][1] == 0 && roll_mat[2][1] == 0) { + minv = mid; + } + else { + maxv = mid; + } + } + + return sqrt(theta) * (minv + maxv) * 0.5; +} + +TEST(vec_roll_to_mat3_normalized, FlippedBoundary1) +{ + EXPECT_NEAR(find_flip_boundary(0, 1), 2.50e-4, 0.01e-4); +} + +TEST(vec_roll_to_mat3_normalized, FlippedBoundary2) +{ + EXPECT_NEAR(find_flip_boundary(1, 1), 2.50e-4, 0.01e-4); +} + +/* Test cases close to the -Y axis. */ +TEST(vec_roll_to_mat3_normalized, Flipped1) +{ + /* If normalized_vector is -Y, simple symmetry by Z axis. */ + const float input[3] = {0.0f, -1.0f, 0.0f}; + const float expected_roll_mat[3][3] = { + {-1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat, false); +} + +TEST(vec_roll_to_mat3_normalized, Flipped2) +{ + /* If normalized_vector is close to -Y and + * it has X and Z values below a threshold, + * simple symmetry by Z axis. */ + const float input[3] = {1e-24, -0.999999881, 0}; + const float expected_roll_mat[3][3] = { + {-1.0f, 0.0f, 0.0f}, {0.0f, -1.0f, 0.0f}, {0.0f, 0.0f, 1.0f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat, false); +} + +TEST(vec_roll_to_mat3_normalized, Flipped3) +{ + /* If normalized_vector is in a critical range close to -Y, apply the special case. */ + const float input[3] = {2.5e-4f, -0.999999881f, 2.5e-4f}; /* Corner Case. */ + const float expected_roll_mat[3][3] = {{0.000000f, -2.5e-4f, -1.000000f}, + {2.5e-4f, -0.999999881f, 2.5e-4f}, + {-1.000000f, -2.5e-4f, 0.000000f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat, false); +} + +/* Test 90 degree rotations. */ +TEST(vec_roll_to_mat3_normalized, Rotate90_Z_CW) +{ + /* Rotate 90 around Z. */ + const float input[3] = {1, 0, 0}; + const float expected_roll_mat[3][3] = {{0, -1, 0}, {1, 0, 0}, {0, 0, 1}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Rotate90_Z_CCW) +{ + /* Rotate 90 around Z. */ + const float input[3] = {-1, 0, 0}; + const float expected_roll_mat[3][3] = {{0, 1, 0}, {-1, 0, 0}, {0, 0, 1}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Rotate90_X_CW) +{ + /* Rotate 90 around X. */ + const float input[3] = {0, 0, -1}; + const float expected_roll_mat[3][3] = {{1, 0, 0}, {0, 0, -1}, {0, 1, 0}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Rotate90_X_CCW) +{ + /* Rotate 90 around X. */ + const float input[3] = {0, 0, 1}; + const float expected_roll_mat[3][3] = {{1, 0, 0}, {0, 0, 1}, {0, -1, 0}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +/* Test the general case when the vector is far enough from -Y. */ +TEST(vec_roll_to_mat3_normalized, Generic1) +{ + const float input[3] = {1.0f, 1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.788675129f, -0.577350259f, -0.211324856f}, + {0.577350259f, 0.577350259f, 0.577350259f}, + {-0.211324856f, -0.577350259f, 0.788675129f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Generic2) +{ + const float input[3] = {1.0f, -1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, -0.577350259f, -0.788675129f}, + {0.577350259f, -0.577350259f, 0.577350259f}, + {-0.788675129f, -0.577350259f, 0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Generic3) +{ + const float input[3] = {-1.0f, -1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, 0.577350259f, 0.788675129f}, + {-0.577350259f, -0.577350259f, 0.577350259f}, + {0.788675129f, -0.577350259f, 0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +TEST(vec_roll_to_mat3_normalized, Generic4) +{ + const float input[3] = {-1.0f, -1.0f, -1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, 0.577350259f, -0.788675129f}, + {-0.577350259f, -0.577350259f, -0.577350259f}, + {-0.788675129f, 0.577350259f, 0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, 0.0f, expected_roll_mat); +} + +/* Test roll. */ +TEST(vec_roll_to_mat3_normalized, Roll1) +{ + const float input[3] = {1.0f, 1.0f, 1.0f}; /* Arbitrary Value. */ + const float expected_roll_mat[3][3] = {{0.211324856f, 0.577350259f, -0.788675129f}, + {0.577350259f, 0.577350259f, 0.577350259f}, + {0.788675129f, -0.577350259f, -0.211324856f}}; + test_vec_roll_to_mat3_normalized(input, float(M_PI * 0.5), expected_roll_mat); +} + +/** Test that the matrix is orthogonal for an input close to -Y. */ +static double test_vec_roll_to_mat3_orthogonal(double s, double x, double z) +{ + const float input[3] = {float(x), float(s * sqrt(1 - x * x - z * z)), float(z)}; + + return test_vec_roll_to_mat3_normalized(input, 0.0f, nullptr); +} + +/** Test that the matrix is orthogonal for a range of inputs close to -Y. */ +static void test_vec_roll_to_mat3_orthogonal(double s, double x1, double x2, double y1, double y2) +{ + const int count = 5000; + double delta = 0; + double tmax = 0; + + for (int i = 0; i <= count; i++) { + double t = double(i) / count; + double det = test_vec_roll_to_mat3_orthogonal(s, interpd(x2, x1, t), interpd(y2, y1, t)); + + /* Find and report maximum error in the matrix determinant. */ + double curdelta = abs(det - 1); + if (curdelta > delta) { + delta = curdelta; + tmax = t; + } + } + + printf(" Max determinant deviation %.10f at %f.\n", delta, tmax); +} + +#define TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(name, s, x1, x2, y1, y2) \ + TEST(vec_roll_to_mat3_normalized, name) \ + { \ + test_vec_roll_to_mat3_orthogonal(s, x1, x2, y1, y2); \ + } + +/* Moving from -Y towards X. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_005, -1, 0, 0, 3e-4, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_010, -1, 0, 0, 0.005, 0.010) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_050, -1, 0, 0, 0.010, 0.050) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_100, -1, 0, 0, 0.050, 0.100) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_200, -1, 0, 0, 0.100, 0.200) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_000_300, -1, 0, 0, 0.200, 0.300) + +/* Moving from -Y towards X and Y. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_005_005, -1, 3e-4, 0.005, 3e-4, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_010_010, -1, 0.005, 0.010, 0.005, 0.010) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_050_050, -1, 0.010, 0.050, 0.010, 0.050) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_100_100, -1, 0.050, 0.100, 0.050, 0.100) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoN_200_200, -1, 0.100, 0.200, 0.100, 0.200) + +/* Moving from +Y towards X. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_000_005, 1, 0, 0, 0, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_000_100, 1, 0, 0, 0.005, 0.100) + +/* Moving from +Y towards X and Y. */ +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_005_005, 1, 0, 0.005, 0, 0.005) +TEST_VEC_ROLL_TO_MAT3_ORTHOGONAL(OrthoP_100_100, 1, 0.005, 0.100, 0.005, 0.100) + class BKE_armature_find_selected_bones_test : public testing::Test { protected: bArmature arm; diff --git a/source/blender/blenkernel/intern/asset.cc b/source/blender/blenkernel/intern/asset.cc index f74018b20c5..59e402b6680 100644 --- a/source/blender/blenkernel/intern/asset.cc +++ b/source/blender/blenkernel/intern/asset.cc @@ -21,13 +21,13 @@ #include #include "DNA_ID.h" -#include "DNA_asset_types.h" #include "DNA_defaults.h" #include "BLI_listbase.h" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BLI_string_utils.h" -#include "BLI_utildefines.h" +#include "BLI_uuid.h" #include "BKE_asset.h" #include "BKE_icons.h" @@ -37,6 +37,8 @@ #include "MEM_guardedalloc.h" +using namespace blender; + AssetMetaData *BKE_asset_metadata_create(void) { AssetMetaData *asset_data = (AssetMetaData *)MEM_callocN(sizeof(*asset_data), __func__); @@ -49,6 +51,7 @@ void BKE_asset_metadata_free(AssetMetaData **asset_data) if ((*asset_data)->properties) { IDP_FreeProperty((*asset_data)->properties); } + MEM_SAFE_FREE((*asset_data)->author); MEM_SAFE_FREE((*asset_data)->description); BLI_freelistN(&(*asset_data)->tags); @@ -115,6 +118,46 @@ void BKE_asset_library_reference_init_default(AssetLibraryReference *library_ref memcpy(library_ref, DNA_struct_default_get(AssetLibraryReference), sizeof(*library_ref)); } +void BKE_asset_metadata_catalog_id_clear(struct AssetMetaData *asset_data) +{ + asset_data->catalog_id = BLI_uuid_nil(); + asset_data->catalog_simple_name[0] = '\0'; +} + +void BKE_asset_metadata_catalog_id_set(struct AssetMetaData *asset_data, + const ::bUUID catalog_id, + const char *catalog_simple_name) +{ + asset_data->catalog_id = catalog_id; + + constexpr size_t max_simple_name_length = sizeof(asset_data->catalog_simple_name); + + /* The substr() call is necessary to make copy() copy the first N characters (instead of refusing + * to copy and producing an empty string). */ + StringRef trimmed_id = + StringRef(catalog_simple_name).trim().substr(0, max_simple_name_length - 1); + trimmed_id.copy(asset_data->catalog_simple_name, max_simple_name_length); +} + +void BKE_asset_metadata_idprop_ensure(AssetMetaData *asset_data, IDProperty *prop) +{ + if (!asset_data->properties) { + IDPropertyTemplate val = {0}; + asset_data->properties = IDP_New(IDP_GROUP, &val, "AssetMetaData.properties"); + } + /* Important: The property may already exist. For now just allow always allow a newly allocated + * property, and replace the existing one as a way of updating. */ + IDP_ReplaceInGroup(asset_data->properties, prop); +} + +IDProperty *BKE_asset_metadata_idprop_find(const AssetMetaData *asset_data, const char *name) +{ + if (!asset_data->properties) { + return nullptr; + } + return IDP_GetPropertyFromGroup(asset_data->properties, name); +} + /* Queries -------------------------------------------- */ PreviewImage *BKE_asset_metadata_preview_get_from_id(const AssetMetaData *UNUSED(asset_data), @@ -133,6 +176,9 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data) IDP_BlendWrite(writer, asset_data->properties); } + if (asset_data->author) { + BLO_write_string(writer, asset_data->author); + } if (asset_data->description) { BLO_write_string(writer, asset_data->description); } @@ -144,12 +190,14 @@ void BKE_asset_metadata_write(BlendWriter *writer, AssetMetaData *asset_data) void BKE_asset_metadata_read(BlendDataReader *reader, AssetMetaData *asset_data) { /* asset_data itself has been read already. */ + asset_data->local_type_info = nullptr; if (asset_data->properties) { BLO_read_data_address(reader, &asset_data->properties); IDP_BlendDataRead(reader, &asset_data->properties); } + BLO_read_data_address(reader, &asset_data->author); BLO_read_data_address(reader, &asset_data->description); BLO_read_list(reader, &asset_data->tags); BLI_assert(BLI_listbase_count(&asset_data->tags) == asset_data->tot_tags); diff --git a/source/blender/blenkernel/intern/asset_catalog.cc b/source/blender/blenkernel/intern/asset_catalog.cc new file mode 100644 index 00000000000..03043f3b784 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog.cc @@ -0,0 +1,1084 @@ +/* + * 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 + */ + +#include +#include + +#include "BKE_asset_catalog.hh" +#include "BKE_asset_library.h" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" + +/* For S_ISREG() and S_ISDIR() on Windows. */ +#ifdef WIN32 +# include "BLI_winstuff.h" +#endif + +namespace blender::bke { + +const CatalogFilePath AssetCatalogService::DEFAULT_CATALOG_FILENAME = "blender_assets.cats.txt"; + +/* For now this is the only version of the catalog definition files that is supported. + * Later versioning code may be added to handle older files. */ +const int AssetCatalogDefinitionFile::SUPPORTED_VERSION = 1; +/* String that's matched in the catalog definition file to know that the line is the version + * declaration. It has to start with a space to ensure it won't match any hypothetical future field + * that starts with "VERSION". */ +const std::string AssetCatalogDefinitionFile::VERSION_MARKER = "VERSION "; + +const std::string AssetCatalogDefinitionFile::HEADER = + "# This is an Asset Catalog Definition file for Blender.\n" + "#\n" + "# Empty lines and lines starting with `#` will be ignored.\n" + "# The first non-ignored line should be the version indicator.\n" + "# Other lines are of the format \"UUID:catalog/path/for/assets:simple catalog name\"\n"; + +AssetCatalogService::AssetCatalogService() + : catalog_collection_(std::make_unique()) +{ +} + +AssetCatalogService::AssetCatalogService(const CatalogFilePath &asset_library_root) + : catalog_collection_(std::make_unique()), + asset_library_root_(asset_library_root) +{ +} + +void AssetCatalogService::tag_has_unsaved_changes(AssetCatalog *edited_catalog) +{ + if (edited_catalog) { + edited_catalog->flags.has_unsaved_changes = true; + } + BLI_assert(catalog_collection_); + catalog_collection_->has_unsaved_changes_ = true; +} + +void AssetCatalogService::untag_has_unsaved_changes() +{ + BLI_assert(catalog_collection_); + catalog_collection_->has_unsaved_changes_ = false; + + /* TODO(Sybren): refactor; this is more like "post-write cleanup" than "remove a tag" code. */ + + /* Forget about any deleted catalogs. */ + if (catalog_collection_->catalog_definition_file_) { + for (CatalogID catalog_id : catalog_collection_->deleted_catalogs_.keys()) { + catalog_collection_->catalog_definition_file_->forget(catalog_id); + } + } + catalog_collection_->deleted_catalogs_.clear(); + + /* Mark all remaining catalogs as "without unsaved changes". */ + for (auto &catalog_uptr : catalog_collection_->catalogs_.values()) { + catalog_uptr->flags.has_unsaved_changes = false; + } +} + +bool AssetCatalogService::has_unsaved_changes() const +{ + BLI_assert(catalog_collection_); + return catalog_collection_->has_unsaved_changes_; +} + +bool AssetCatalogService::is_empty() const +{ + BLI_assert(catalog_collection_); + return catalog_collection_->catalogs_.is_empty(); +} + +OwningAssetCatalogMap &AssetCatalogService::get_catalogs() +{ + return catalog_collection_->catalogs_; +} +OwningAssetCatalogMap &AssetCatalogService::get_deleted_catalogs() +{ + return catalog_collection_->deleted_catalogs_; +} + +AssetCatalogDefinitionFile *AssetCatalogService::get_catalog_definition_file() +{ + return catalog_collection_->catalog_definition_file_.get(); +} + +AssetCatalog *AssetCatalogService::find_catalog(CatalogID catalog_id) const +{ + const std::unique_ptr *catalog_uptr_ptr = + catalog_collection_->catalogs_.lookup_ptr(catalog_id); + if (catalog_uptr_ptr == nullptr) { + return nullptr; + } + return catalog_uptr_ptr->get(); +} + +AssetCatalog *AssetCatalogService::find_catalog_by_path(const AssetCatalogPath &path) const +{ + /* Use an AssetCatalogOrderedSet to find the 'best' catalog for this path. This will be the first + * one loaded from disk, or if that does not exist the one with the lowest UUID. This ensures + * stable, predictable results. */ + MutableAssetCatalogOrderedSet ordered_catalogs; + + for (const auto &catalog : catalog_collection_->catalogs_.values()) { + if (catalog->path == path) { + ordered_catalogs.insert(catalog.get()); + } + } + + if (ordered_catalogs.empty()) { + return nullptr; + } + + MutableAssetCatalogOrderedSet::iterator best_choice_it = ordered_catalogs.begin(); + return *best_choice_it; +} + +bool AssetCatalogService::is_catalog_known(CatalogID catalog_id) const +{ + BLI_assert(catalog_collection_); + return catalog_collection_->catalogs_.contains(catalog_id); +} + +AssetCatalogFilter AssetCatalogService::create_catalog_filter( + const CatalogID active_catalog_id) const +{ + Set matching_catalog_ids; + Set known_catalog_ids; + matching_catalog_ids.add(active_catalog_id); + + const AssetCatalog *active_catalog = find_catalog(active_catalog_id); + + /* This cannot just iterate over tree items to get all the required data, because tree items only + * represent single UUIDs. It could be used to get the main UUIDs of the children, though, and + * then only do an exact match on the path (instead of the more complex `is_contained_in()` + * call). Without an extra indexed-by-path acceleration structure, this is still going to require + * a linear search, though. */ + for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) { + if (active_catalog && catalog_uptr->path.is_contained_in(active_catalog->path)) { + matching_catalog_ids.add(catalog_uptr->catalog_id); + } + known_catalog_ids.add(catalog_uptr->catalog_id); + } + + return AssetCatalogFilter(std::move(matching_catalog_ids), std::move(known_catalog_ids)); +} + +void AssetCatalogService::delete_catalog_by_id_soft(const CatalogID catalog_id) +{ + std::unique_ptr *catalog_uptr_ptr = catalog_collection_->catalogs_.lookup_ptr( + catalog_id); + if (catalog_uptr_ptr == nullptr) { + /* Catalog cannot be found, which is fine. */ + return; + } + + /* Mark the catalog as deleted. */ + AssetCatalog *catalog = catalog_uptr_ptr->get(); + catalog->flags.is_deleted = true; + + /* Move ownership from catalog_collection_->catalogs_ to catalog_collection_->deleted_catalogs_. + */ + catalog_collection_->deleted_catalogs_.add(catalog_id, std::move(*catalog_uptr_ptr)); + + /* The catalog can now be removed from the map without freeing the actual AssetCatalog. */ + catalog_collection_->catalogs_.remove(catalog_id); +} + +void AssetCatalogService::delete_catalog_by_id_hard(CatalogID catalog_id) +{ + catalog_collection_->catalogs_.remove(catalog_id); + catalog_collection_->deleted_catalogs_.remove(catalog_id); + + /* TODO(@sybren): adjust this when supporting multiple CDFs. */ + catalog_collection_->catalog_definition_file_->forget(catalog_id); +} + +void AssetCatalogService::prune_catalogs_by_path(const AssetCatalogPath &path) +{ + /* Build a collection of catalog IDs to delete. */ + Set catalogs_to_delete; + for (const auto &catalog_uptr : catalog_collection_->catalogs_.values()) { + const AssetCatalog *cat = catalog_uptr.get(); + if (cat->path.is_contained_in(path)) { + catalogs_to_delete.add(cat->catalog_id); + } + } + + /* Delete the catalogs. */ + for (const CatalogID cat_id : catalogs_to_delete) { + this->delete_catalog_by_id_soft(cat_id); + } + + this->rebuild_tree(); +} + +void AssetCatalogService::prune_catalogs_by_id(const CatalogID catalog_id) +{ + const AssetCatalog *catalog = find_catalog(catalog_id); + BLI_assert_msg(catalog, "trying to prune asset catalogs by the path of a non-existent catalog"); + if (!catalog) { + return; + } + this->prune_catalogs_by_path(catalog->path); +} + +void AssetCatalogService::update_catalog_path(const CatalogID catalog_id, + const AssetCatalogPath &new_catalog_path) +{ + AssetCatalog *renamed_cat = this->find_catalog(catalog_id); + const AssetCatalogPath old_cat_path = renamed_cat->path; + + for (auto &catalog_uptr : catalog_collection_->catalogs_.values()) { + AssetCatalog *cat = catalog_uptr.get(); + + const AssetCatalogPath new_path = cat->path.rebase(old_cat_path, new_catalog_path); + if (!new_path) { + continue; + } + cat->path = new_path; + cat->simple_name_refresh(); + + /* TODO(Sybren): go over all assets that are assigned to this catalog, defined in the current + * blend file, and update the catalog simple name stored there. */ + } + + this->rebuild_tree(); +} + +AssetCatalog *AssetCatalogService::create_catalog(const AssetCatalogPath &catalog_path) +{ + std::unique_ptr catalog = AssetCatalog::from_path(catalog_path); + catalog->flags.has_unsaved_changes = true; + + /* So we can std::move(catalog) and still use the non-owning pointer: */ + AssetCatalog *const catalog_ptr = catalog.get(); + + /* TODO(@sybren): move the `AssetCatalog::from_path()` function to another place, that can reuse + * catalogs when a catalog with the given path is already known, and avoid duplicate catalog IDs. + */ + BLI_assert_msg(!catalog_collection_->catalogs_.contains(catalog->catalog_id), + "duplicate catalog ID not supported"); + catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog)); + + if (catalog_collection_->catalog_definition_file_) { + /* Ensure the new catalog gets written to disk at some point. If there is no CDF in memory yet, + * it's enough to have the catalog known to the service as it'll be saved to a new file. */ + catalog_collection_->catalog_definition_file_->add_new(catalog_ptr); + } + + BLI_assert_msg(catalog_tree_, "An Asset Catalog tree should always exist."); + catalog_tree_->insert_item(*catalog_ptr); + + return catalog_ptr; +} + +static std::string asset_definition_default_file_path_from_dir(StringRef asset_library_root) +{ + char file_path[PATH_MAX]; + BLI_join_dirfile(file_path, + sizeof(file_path), + asset_library_root.data(), + AssetCatalogService::DEFAULT_CATALOG_FILENAME.data()); + return file_path; +} + +void AssetCatalogService::load_from_disk() +{ + load_from_disk(asset_library_root_); +} + +void AssetCatalogService::load_from_disk(const CatalogFilePath &file_or_directory_path) +{ + BLI_stat_t status; + if (BLI_stat(file_or_directory_path.data(), &status) == -1) { + /* TODO(@sybren): throw an appropriate exception. */ + return; + } + + if (S_ISREG(status.st_mode)) { + load_single_file(file_or_directory_path); + } + else if (S_ISDIR(status.st_mode)) { + load_directory_recursive(file_or_directory_path); + } + else { + /* TODO(@sybren): throw an appropriate exception. */ + } + + /* TODO: Should there be a sanitize step? E.g. to remove catalogs with identical paths? */ + + rebuild_tree(); +} + +void AssetCatalogService::load_directory_recursive(const CatalogFilePath &directory_path) +{ + /* TODO(@sybren): implement proper multi-file support. For now, just load + * the default file if it is there. */ + CatalogFilePath file_path = asset_definition_default_file_path_from_dir(directory_path); + + if (!BLI_exists(file_path.data())) { + /* No file to be loaded is perfectly fine. */ + return; + } + + this->load_single_file(file_path); +} + +void AssetCatalogService::load_single_file(const CatalogFilePath &catalog_definition_file_path) +{ + /* TODO(@sybren): check that #catalog_definition_file_path is contained in #asset_library_root_, + * otherwise some assumptions may fail. */ + std::unique_ptr cdf = parse_catalog_file( + catalog_definition_file_path); + + BLI_assert_msg(!catalog_collection_->catalog_definition_file_, + "Only loading of a single catalog definition file is supported."); + catalog_collection_->catalog_definition_file_ = std::move(cdf); +} + +std::unique_ptr AssetCatalogService::parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path) +{ + auto cdf = std::make_unique(); + cdf->file_path = catalog_definition_file_path; + + /* TODO(Sybren): this might have to move to a higher level when supporting multiple CDFs. */ + Set seen_paths; + + auto catalog_parsed_callback = [this, catalog_definition_file_path, &seen_paths]( + std::unique_ptr catalog) { + if (catalog_collection_->catalogs_.contains(catalog->catalog_id)) { + /* TODO(@sybren): apparently another CDF was already loaded. This is not supported yet. */ + std::cerr << catalog_definition_file_path << ": multiple definitions of catalog " + << catalog->catalog_id << " in multiple files, ignoring this one." << std::endl; + /* Don't store 'catalog'; unique_ptr will free its memory. */ + return false; + } + + catalog->flags.is_first_loaded = seen_paths.add(catalog->path); + + /* The AssetCatalog pointer is now owned by the AssetCatalogService. */ + catalog_collection_->catalogs_.add_new(catalog->catalog_id, std::move(catalog)); + return true; + }; + + cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback); + + return cdf; +} + +void AssetCatalogService::reload_catalogs() +{ + /* TODO(Sybren): expand to support multiple CDFs. */ + AssetCatalogDefinitionFile *const cdf = catalog_collection_->catalog_definition_file_.get(); + if (!cdf || cdf->file_path.empty() || !BLI_is_file(cdf->file_path.c_str())) { + return; + } + + /* Keeps track of the catalog IDs that are seen in the CDF, so that we also know what was deleted + * from the file on disk. */ + Set cats_in_file; + + auto catalog_parsed_callback = [this, &cats_in_file](std::unique_ptr catalog) { + const CatalogID catalog_id = catalog->catalog_id; + cats_in_file.add(catalog_id); + + const bool should_skip = is_catalog_known_with_unsaved_changes(catalog_id); + if (should_skip) { + /* Do not overwrite unsaved local changes. */ + return false; + } + + /* This is either a new catalog, or we can just replace the in-memory one with the newly loaded + * one. */ + catalog_collection_->catalogs_.add_overwrite(catalog_id, std::move(catalog)); + return true; + }; + + cdf->parse_catalog_file(cdf->file_path, catalog_parsed_callback); + this->purge_catalogs_not_listed(cats_in_file); + this->rebuild_tree(); +} + +void AssetCatalogService::purge_catalogs_not_listed(const Set &catalogs_to_keep) +{ + Set cats_to_remove; + for (CatalogID cat_id : this->catalog_collection_->catalogs_.keys()) { + if (catalogs_to_keep.contains(cat_id)) { + continue; + } + if (is_catalog_known_with_unsaved_changes(cat_id)) { + continue; + } + /* This catalog is not on disk, but also not modified, so get rid of it. */ + cats_to_remove.add(cat_id); + } + + for (CatalogID cat_id : cats_to_remove) { + delete_catalog_by_id_hard(cat_id); + } +} + +bool AssetCatalogService::is_catalog_known_with_unsaved_changes(const CatalogID catalog_id) const +{ + if (catalog_collection_->deleted_catalogs_.contains(catalog_id)) { + /* Deleted catalogs are always considered modified, by definition. */ + return true; + } + + const std::unique_ptr *catalog_uptr_ptr = + catalog_collection_->catalogs_.lookup_ptr(catalog_id); + if (!catalog_uptr_ptr) { + /* Catalog is unknown. */ + return false; + } + + const bool has_unsaved_changes = (*catalog_uptr_ptr)->flags.has_unsaved_changes; + return has_unsaved_changes; +} + +bool AssetCatalogService::write_to_disk(const CatalogFilePath &blend_file_path) +{ + if (!write_to_disk_ex(blend_file_path)) { + return false; + } + + untag_has_unsaved_changes(); + rebuild_tree(); + return true; +} + +bool AssetCatalogService::write_to_disk_ex(const CatalogFilePath &blend_file_path) +{ + /* TODO(Sybren): expand to support multiple CDFs. */ + + /* - Already loaded a CDF from disk? -> Always write to that file. */ + if (catalog_collection_->catalog_definition_file_) { + reload_catalogs(); + return catalog_collection_->catalog_definition_file_->write_to_disk(); + } + + if (catalog_collection_->catalogs_.is_empty() && + catalog_collection_->deleted_catalogs_.is_empty()) { + /* Avoid saving anything, when there is nothing to save. */ + return true; /* Writing nothing when there is nothing to write is still a success. */ + } + + const CatalogFilePath cdf_path_to_write = find_suitable_cdf_path_for_writing(blend_file_path); + catalog_collection_->catalog_definition_file_ = construct_cdf_in_memory(cdf_path_to_write); + reload_catalogs(); + return catalog_collection_->catalog_definition_file_->write_to_disk(); +} + +CatalogFilePath AssetCatalogService::find_suitable_cdf_path_for_writing( + const CatalogFilePath &blend_file_path) +{ + BLI_assert_msg(!blend_file_path.empty(), + "A non-empty .blend file path is required to be able to determine where the " + "catalog definition file should be put"); + + /* Ask the asset library API for an appropriate location. */ + char suitable_root_path[PATH_MAX]; + const bool asset_lib_root_found = BKE_asset_library_find_suitable_root_path_from_path( + blend_file_path.c_str(), suitable_root_path); + if (asset_lib_root_found) { + char asset_lib_cdf_path[PATH_MAX]; + BLI_path_join(asset_lib_cdf_path, + sizeof(asset_lib_cdf_path), + suitable_root_path, + DEFAULT_CATALOG_FILENAME.c_str(), + NULL); + return asset_lib_cdf_path; + } + + /* Determine the default CDF path in the same directory of the blend file. */ + char blend_dir_path[PATH_MAX]; + BLI_split_dir_part(blend_file_path.c_str(), blend_dir_path, sizeof(blend_dir_path)); + const CatalogFilePath cdf_path_next_to_blend = asset_definition_default_file_path_from_dir( + blend_dir_path); + return cdf_path_next_to_blend; +} + +std::unique_ptr AssetCatalogService::construct_cdf_in_memory( + const CatalogFilePath &file_path) +{ + auto cdf = std::make_unique(); + cdf->file_path = file_path; + + for (auto &catalog : catalog_collection_->catalogs_.values()) { + cdf->add_new(catalog.get()); + } + + return cdf; +} + +AssetCatalogTree *AssetCatalogService::get_catalog_tree() +{ + return catalog_tree_.get(); +} + +std::unique_ptr AssetCatalogService::read_into_tree() +{ + auto tree = std::make_unique(); + + /* Go through the catalogs, insert each path component into the tree where needed. */ + for (auto &catalog : catalog_collection_->catalogs_.values()) { + tree->insert_item(*catalog); + } + + return tree; +} + +void AssetCatalogService::rebuild_tree() +{ + create_missing_catalogs(); + this->catalog_tree_ = read_into_tree(); +} + +void AssetCatalogService::create_missing_catalogs() +{ + /* Construct an ordered set of paths to check, so that parents are ordered before children. */ + std::set paths_to_check; + for (auto &catalog : catalog_collection_->catalogs_.values()) { + paths_to_check.insert(catalog->path); + } + + std::set seen_paths; + /* The empty parent should never be created, so always be considered "seen". */ + seen_paths.insert(AssetCatalogPath("")); + + /* Find and create missing direct parents (so ignoring parents-of-parents). */ + while (!paths_to_check.empty()) { + /* Pop the first path of the queue. */ + const AssetCatalogPath path = *paths_to_check.begin(); + paths_to_check.erase(paths_to_check.begin()); + + if (seen_paths.find(path) != seen_paths.end()) { + /* This path has been seen already, so it can be ignored. */ + continue; + } + seen_paths.insert(path); + + const AssetCatalogPath parent_path = path.parent(); + if (seen_paths.find(parent_path) != seen_paths.end()) { + /* The parent exists, continue to the next path. */ + continue; + } + + /* The parent doesn't exist, so create it and queue it up for checking its parent. */ + AssetCatalog *parent_catalog = create_catalog(parent_path); + parent_catalog->flags.has_unsaved_changes = true; + + paths_to_check.insert(parent_path); + } + + /* TODO(Sybren): bind the newly created catalogs to a CDF, if we know about it. */ +} + +bool AssetCatalogService::is_undo_possbile() const +{ + return !undo_snapshots_.is_empty(); +} + +bool AssetCatalogService::is_redo_possbile() const +{ + return !redo_snapshots_.is_empty(); +} + +void AssetCatalogService::undo() +{ + BLI_assert_msg(is_undo_possbile(), "Undo stack is empty"); + + redo_snapshots_.append(std::move(catalog_collection_)); + catalog_collection_ = undo_snapshots_.pop_last(); + rebuild_tree(); +} + +void AssetCatalogService::redo() +{ + BLI_assert_msg(is_redo_possbile(), "Redo stack is empty"); + + undo_snapshots_.append(std::move(catalog_collection_)); + catalog_collection_ = redo_snapshots_.pop_last(); + rebuild_tree(); +} + +void AssetCatalogService::undo_push() +{ + std::unique_ptr snapshot = catalog_collection_->deep_copy(); + undo_snapshots_.append(std::move(snapshot)); + redo_snapshots_.clear(); +} + +/* ---------------------------------------------------------------------- */ + +std::unique_ptr AssetCatalogCollection::deep_copy() const +{ + auto copy = std::make_unique(); + + copy->has_unsaved_changes_ = this->has_unsaved_changes_; + copy->catalogs_ = copy_catalog_map(this->catalogs_); + copy->deleted_catalogs_ = copy_catalog_map(this->deleted_catalogs_); + + if (catalog_definition_file_) { + copy->catalog_definition_file_ = catalog_definition_file_->copy_and_remap( + copy->catalogs_, copy->deleted_catalogs_); + } + + return copy; +} + +OwningAssetCatalogMap AssetCatalogCollection::copy_catalog_map(const OwningAssetCatalogMap &orig) +{ + OwningAssetCatalogMap copy; + + for (const auto &orig_catalog_uptr : orig.values()) { + auto copy_catalog_uptr = std::make_unique(*orig_catalog_uptr); + copy.add_new(copy_catalog_uptr->catalog_id, std::move(copy_catalog_uptr)); + } + + return copy; +} + +/* ---------------------------------------------------------------------- */ + +AssetCatalogTreeItem::AssetCatalogTreeItem(StringRef name, + CatalogID catalog_id, + StringRef simple_name, + const AssetCatalogTreeItem *parent) + : name_(name), catalog_id_(catalog_id), simple_name_(simple_name), parent_(parent) +{ +} + +CatalogID AssetCatalogTreeItem::get_catalog_id() const +{ + return catalog_id_; +} + +StringRefNull AssetCatalogTreeItem::get_name() const +{ + return name_; +} + +StringRefNull AssetCatalogTreeItem::get_simple_name() const +{ + return simple_name_; +} +bool AssetCatalogTreeItem::has_unsaved_changes() const +{ + return has_unsaved_changes_; +} + +AssetCatalogPath AssetCatalogTreeItem::catalog_path() const +{ + AssetCatalogPath current_path = name_; + for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) { + current_path = AssetCatalogPath(parent->name_) / current_path; + } + return current_path; +} + +int AssetCatalogTreeItem::count_parents() const +{ + int i = 0; + for (const AssetCatalogTreeItem *parent = parent_; parent; parent = parent->parent_) { + i++; + } + return i; +} + +bool AssetCatalogTreeItem::has_children() const +{ + return !children_.empty(); +} + +void AssetCatalogTreeItem::foreach_item_recursive(AssetCatalogTreeItem::ChildMap &children, + const ItemIterFn callback) +{ + for (auto &[key, item] : children) { + callback(item); + foreach_item_recursive(item.children_, callback); + } +} + +void AssetCatalogTreeItem::foreach_child(const ItemIterFn callback) +{ + for (auto &[key, item] : children_) { + callback(item); + } +} + +/* ---------------------------------------------------------------------- */ + +void AssetCatalogTree::insert_item(const AssetCatalog &catalog) +{ + const AssetCatalogTreeItem *parent = nullptr; + /* The children for the currently iterated component, where the following component should be + * added to (if not there yet). */ + AssetCatalogTreeItem::ChildMap *current_item_children = &root_items_; + + BLI_assert_msg(!ELEM(catalog.path.str()[0], '/', '\\'), + "Malformed catalog path; should not start with a separator"); + + const CatalogID nil_id{}; + + catalog.path.iterate_components([&](StringRef component_name, const bool is_last_component) { + /* Insert new tree element - if no matching one is there yet! */ + auto [key_and_item, was_inserted] = current_item_children->emplace( + component_name, + AssetCatalogTreeItem(component_name, + is_last_component ? catalog.catalog_id : nil_id, + is_last_component ? catalog.simple_name : "", + parent)); + AssetCatalogTreeItem &item = key_and_item->second; + + /* If full path of this catalog already exists as parent path of a previously read catalog, + * we can ensure this tree item's UUID is set here. */ + if (is_last_component) { + if (BLI_uuid_is_nil(item.catalog_id_) || catalog.flags.is_first_loaded) { + item.catalog_id_ = catalog.catalog_id; + } + item.has_unsaved_changes_ = catalog.flags.has_unsaved_changes; + } + + /* Walk further into the path (no matter if a new item was created or not). */ + parent = &item; + current_item_children = &item.children_; + }); +} + +void AssetCatalogTree::foreach_item(AssetCatalogTreeItem::ItemIterFn callback) +{ + AssetCatalogTreeItem::foreach_item_recursive(root_items_, callback); +} + +void AssetCatalogTree::foreach_root_item(const ItemIterFn callback) +{ + for (auto &[key, item] : root_items_) { + callback(item); + } +} + +/* ---------------------------------------------------------------------- */ + +/* ---------------------------------------------------------------------- */ + +bool AssetCatalogDefinitionFile::contains(const CatalogID catalog_id) const +{ + return catalogs_.contains(catalog_id); +} + +void AssetCatalogDefinitionFile::add_new(AssetCatalog *catalog) +{ + catalogs_.add_new(catalog->catalog_id, catalog); +} + +void AssetCatalogDefinitionFile::add_overwrite(AssetCatalog *catalog) +{ + catalogs_.add_overwrite(catalog->catalog_id, catalog); +} + +void AssetCatalogDefinitionFile::forget(CatalogID catalog_id) +{ + catalogs_.remove(catalog_id); +} + +void AssetCatalogDefinitionFile::parse_catalog_file( + const CatalogFilePath &catalog_definition_file_path, + AssetCatalogParsedFn catalog_loaded_callback) +{ + std::fstream infile(catalog_definition_file_path); + + bool seen_version_number = false; + std::string line; + while (std::getline(infile, line)) { + const StringRef trimmed_line = StringRef(line).trim(); + if (trimmed_line.is_empty() || trimmed_line[0] == '#') { + continue; + } + + if (!seen_version_number) { + /* The very first non-ignored line should be the version declaration. */ + const bool is_valid_version = this->parse_version_line(trimmed_line); + if (!is_valid_version) { + std::cerr << catalog_definition_file_path + << ": first line should be version declaration; ignoring file." << std::endl; + break; + } + seen_version_number = true; + continue; + } + + std::unique_ptr catalog = this->parse_catalog_line(trimmed_line); + if (!catalog) { + continue; + } + + AssetCatalog *non_owning_ptr = catalog.get(); + const bool keep_catalog = catalog_loaded_callback(std::move(catalog)); + if (!keep_catalog) { + continue; + } + + /* The AssetDefinitionFile should include this catalog when writing it back to disk. */ + this->add_overwrite(non_owning_ptr); + } +} + +bool AssetCatalogDefinitionFile::parse_version_line(const StringRef line) +{ + if (!line.startswith(VERSION_MARKER)) { + return false; + } + + const std::string version_string = line.substr(VERSION_MARKER.length()); + const int file_version = std::atoi(version_string.c_str()); + + /* No versioning, just a blunt check whether it's the right one. */ + return file_version == SUPPORTED_VERSION; +} + +std::unique_ptr AssetCatalogDefinitionFile::parse_catalog_line(const StringRef line) +{ + const char delim = ':'; + const int64_t first_delim = line.find_first_of(delim); + if (first_delim == StringRef::not_found) { + std::cerr << "Invalid catalog line in " << this->file_path << ": " << line << std::endl; + return std::unique_ptr(nullptr); + } + + /* Parse the catalog ID. */ + const std::string id_as_string = line.substr(0, first_delim).trim(); + bUUID catalog_id; + const bool uuid_parsed_ok = BLI_uuid_parse_string(&catalog_id, id_as_string.c_str()); + if (!uuid_parsed_ok) { + std::cerr << "Invalid UUID in " << this->file_path << ": " << line << std::endl; + return std::unique_ptr(nullptr); + } + + /* Parse the path and simple name. */ + const StringRef path_and_simple_name = line.substr(first_delim + 1); + const int64_t second_delim = path_and_simple_name.find_first_of(delim); + + std::string path_in_file; + std::string simple_name; + if (second_delim == 0) { + /* Delimiter as first character means there is no path. These lines are to be ignored. */ + return std::unique_ptr(nullptr); + } + + if (second_delim == StringRef::not_found) { + /* No delimiter means no simple name, just treat it as all "path". */ + path_in_file = path_and_simple_name; + simple_name = ""; + } + else { + path_in_file = path_and_simple_name.substr(0, second_delim); + simple_name = path_and_simple_name.substr(second_delim + 1).trim(); + } + + AssetCatalogPath catalog_path = path_in_file; + return std::make_unique(catalog_id, catalog_path.cleanup(), simple_name); +} + +bool AssetCatalogDefinitionFile::write_to_disk() const +{ + BLI_assert_msg(!this->file_path.empty(), "Writing to CDF requires its file path to be known"); + return this->write_to_disk(this->file_path); +} + +bool AssetCatalogDefinitionFile::write_to_disk(const CatalogFilePath &dest_file_path) const +{ + const CatalogFilePath writable_path = dest_file_path + ".writing"; + const CatalogFilePath backup_path = dest_file_path + "~"; + + if (!this->write_to_disk_unsafe(writable_path)) { + /* TODO: communicate what went wrong. */ + return false; + } + if (BLI_exists(dest_file_path.c_str())) { + if (BLI_rename(dest_file_path.c_str(), backup_path.c_str())) { + /* TODO: communicate what went wrong. */ + return false; + } + } + if (BLI_rename(writable_path.c_str(), dest_file_path.c_str())) { + /* TODO: communicate what went wrong. */ + return false; + } + + return true; +} + +bool AssetCatalogDefinitionFile::write_to_disk_unsafe(const CatalogFilePath &dest_file_path) const +{ + char directory[PATH_MAX]; + BLI_split_dir_part(dest_file_path.c_str(), directory, sizeof(directory)); + if (!ensure_directory_exists(directory)) { + /* TODO(Sybren): pass errors to the UI somehow. */ + return false; + } + + std::ofstream output(dest_file_path); + + /* TODO(@sybren): remember the line ending style that was originally read, then use that to write + * the file again. */ + + /* Write the header. */ + output << HEADER; + output << "" << std::endl; + output << VERSION_MARKER << SUPPORTED_VERSION << std::endl; + output << "" << std::endl; + + /* Write the catalogs, ordered by path (primary) and UUID (secondary). */ + AssetCatalogOrderedSet catalogs_by_path; + for (const AssetCatalog *catalog : catalogs_.values()) { + if (catalog->flags.is_deleted) { + continue; + } + catalogs_by_path.insert(catalog); + } + + for (const AssetCatalog *catalog : catalogs_by_path) { + output << catalog->catalog_id << ":" << catalog->path << ":" << catalog->simple_name + << std::endl; + } + output.close(); + return !output.bad(); +} + +bool AssetCatalogDefinitionFile::ensure_directory_exists( + const CatalogFilePath directory_path) const +{ + /* TODO(@sybren): design a way to get such errors presented to users (or ensure that they never + * occur). */ + if (directory_path.empty()) { + std::cerr + << "AssetCatalogService: no asset library root configured, unable to ensure it exists." + << std::endl; + return false; + } + + if (BLI_exists(directory_path.data())) { + if (!BLI_is_dir(directory_path.data())) { + std::cerr << "AssetCatalogService: " << directory_path + << " exists but is not a directory, this is not a supported situation." + << std::endl; + return false; + } + + /* Root directory exists, work is done. */ + return true; + } + + /* Ensure the root directory exists. */ + std::error_code err_code; + if (!BLI_dir_create_recursive(directory_path.data())) { + std::cerr << "AssetCatalogService: error creating directory " << directory_path << ": " + << err_code << std::endl; + return false; + } + + /* Root directory has been created, work is done. */ + return true; +} + +std::unique_ptr AssetCatalogDefinitionFile::copy_and_remap( + const OwningAssetCatalogMap &catalogs, const OwningAssetCatalogMap &deleted_catalogs) const +{ + auto copy = std::make_unique(*this); + copy->catalogs_.clear(); + + /* Remap pointers of the copy from the original AssetCatalogCollection to the given one. */ + for (CatalogID catalog_id : catalogs_.keys()) { + /* The catalog can be in the regular or the deleted map. */ + const std::unique_ptr *remapped_catalog_uptr_ptr = catalogs.lookup_ptr( + catalog_id); + if (remapped_catalog_uptr_ptr) { + copy->catalogs_.add_new(catalog_id, remapped_catalog_uptr_ptr->get()); + continue; + } + + remapped_catalog_uptr_ptr = deleted_catalogs.lookup_ptr(catalog_id); + if (remapped_catalog_uptr_ptr) { + copy->catalogs_.add_new(catalog_id, remapped_catalog_uptr_ptr->get()); + continue; + } + + BLI_assert(!"A CDF should only reference known catalogs."); + } + + return copy; +} + +AssetCatalog::AssetCatalog(const CatalogID catalog_id, + const AssetCatalogPath &path, + const std::string &simple_name) + : catalog_id(catalog_id), path(path), simple_name(simple_name) +{ +} + +std::unique_ptr AssetCatalog::from_path(const AssetCatalogPath &path) +{ + const AssetCatalogPath clean_path = path.cleanup(); + const CatalogID cat_id = BLI_uuid_generate_random(); + const std::string simple_name = sensible_simple_name_for_path(clean_path); + auto catalog = std::make_unique(cat_id, clean_path, simple_name); + return catalog; +} + +void AssetCatalog::simple_name_refresh() +{ + this->simple_name = sensible_simple_name_for_path(this->path); +} + +std::string AssetCatalog::sensible_simple_name_for_path(const AssetCatalogPath &path) +{ + std::string name = path.str(); + std::replace(name.begin(), name.end(), AssetCatalogPath::SEPARATOR, '-'); + if (name.length() < MAX_NAME - 1) { + return name; + } + + /* Trim off the start of the path, as that's the most generic part and thus contains the least + * information. */ + return "..." + name.substr(name.length() - 60); +} + +AssetCatalogFilter::AssetCatalogFilter(Set &&matching_catalog_ids, + Set &&known_catalog_ids) + : matching_catalog_ids(std::move(matching_catalog_ids)), + known_catalog_ids(std::move(known_catalog_ids)) +{ +} + +bool AssetCatalogFilter::contains(const CatalogID asset_catalog_id) const +{ + return matching_catalog_ids.contains(asset_catalog_id); +} + +bool AssetCatalogFilter::is_known(const CatalogID asset_catalog_id) const +{ + if (BLI_uuid_is_nil(asset_catalog_id)) { + return false; + } + return known_catalog_ids.contains(asset_catalog_id); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_path.cc b/source/blender/blenkernel/intern/asset_catalog_path.cc new file mode 100644 index 00000000000..20cac76b40b --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog_path.cc @@ -0,0 +1,240 @@ +/* + * 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 + */ + +#include "BKE_asset_catalog_path.hh" + +#include "BLI_path_util.h" + +namespace blender::bke { + +const char AssetCatalogPath::SEPARATOR = '/'; + +AssetCatalogPath::AssetCatalogPath(const std::string &path) : path_(path) +{ +} + +AssetCatalogPath::AssetCatalogPath(StringRef path) : path_(path) +{ +} + +AssetCatalogPath::AssetCatalogPath(const char *path) : path_(path) +{ +} + +AssetCatalogPath::AssetCatalogPath(AssetCatalogPath &&other_path) noexcept + : path_(std::move(other_path.path_)) +{ +} + +uint64_t AssetCatalogPath::hash() const +{ + std::hash hasher{}; + return hasher(this->path_); +} + +uint64_t AssetCatalogPath::length() const +{ + return this->path_.length(); +} + +const char *AssetCatalogPath::c_str() const +{ + return this->path_.c_str(); +} + +const std::string &AssetCatalogPath::str() const +{ + return this->path_; +} + +StringRefNull AssetCatalogPath::name() const +{ + const size_t last_sep_index = this->path_.rfind(SEPARATOR); + if (last_sep_index == std::string::npos) { + return StringRefNull(this->path_); + } + + return StringRefNull(this->path_.c_str() + last_sep_index + 1); +} + +/* In-class operators, because of the implicit `AssetCatalogPath(StringRef)` constructor. + * Otherwise `string == string` could cast both sides to `AssetCatalogPath`. */ +bool AssetCatalogPath::operator==(const AssetCatalogPath &other_path) const +{ + return this->path_ == other_path.path_; +} + +bool AssetCatalogPath::operator!=(const AssetCatalogPath &other_path) const +{ + return !(*this == other_path); +} + +bool AssetCatalogPath::operator<(const AssetCatalogPath &other_path) const +{ + return this->path_ < other_path.path_; +} + +AssetCatalogPath AssetCatalogPath::operator/(const AssetCatalogPath &path_to_append) const +{ + /* `"" / "path"` or `"path" / ""` should just result in `"path"` */ + if (!*this) { + return path_to_append; + } + if (!path_to_append) { + return *this; + } + + std::stringstream new_path; + new_path << this->path_ << SEPARATOR << path_to_append.path_; + return AssetCatalogPath(new_path.str()); +} + +AssetCatalogPath::operator bool() const +{ + return !this->path_.empty(); +} + +std::ostream &operator<<(std::ostream &stream, const AssetCatalogPath &path_to_append) +{ + stream << path_to_append.path_; + return stream; +} + +AssetCatalogPath AssetCatalogPath::cleanup() const +{ + std::stringstream clean_components; + bool first_component_seen = false; + + this->iterate_components([&clean_components, &first_component_seen](StringRef component_name, + bool /*is_last_component*/) { + const std::string clean_component = cleanup_component(component_name); + + if (clean_component.empty()) { + /* These are caused by leading, trailing, or double slashes. */ + return; + } + + /* If a previous path component has been streamed already, we need a path separator. This + * cannot use the `is_last_component` boolean, because the last component might be skipped due + * to the condition above. */ + if (first_component_seen) { + clean_components << SEPARATOR; + } + first_component_seen = true; + + clean_components << clean_component; + }); + + return AssetCatalogPath(clean_components.str()); +} + +std::string AssetCatalogPath::cleanup_component(StringRef component) +{ + std::string cleaned = component.trim(); + /* Replace colons with something else, as those are used in the CDF file as delimiter. */ + std::replace(cleaned.begin(), cleaned.end(), ':', '-'); + return cleaned; +} + +bool AssetCatalogPath::is_contained_in(const AssetCatalogPath &other_path) const +{ + if (!other_path) { + /* The empty path contains all other paths. */ + return true; + } + + if (this->path_ == other_path.path_) { + /* Weak is-in relation: equal paths contain each other. */ + return true; + } + + /* To be a child path of 'other_path', our path must be at least a separator and another + * character longer. */ + if (this->length() < other_path.length() + 2) { + return false; + } + + /* Create StringRef to be able to use .startswith(). */ + const StringRef this_path(this->path_); + const bool prefix_ok = this_path.startswith(other_path.path_); + const char next_char = this_path[other_path.length()]; + return prefix_ok && next_char == SEPARATOR; +} + +AssetCatalogPath AssetCatalogPath::parent() const +{ + if (!*this) { + return AssetCatalogPath(""); + } + std::string::size_type last_sep_index = this->path_.rfind(SEPARATOR); + if (last_sep_index == std::string::npos) { + return AssetCatalogPath(""); + } + return AssetCatalogPath(this->path_.substr(0, last_sep_index)); +} + +void AssetCatalogPath::iterate_components(ComponentIteratorFn callback) const +{ + const char *next_slash_ptr; + + for (const char *path_component = this->path_.data(); path_component && path_component[0]; + /* Jump to one after the next slash if there is any. */ + path_component = next_slash_ptr ? next_slash_ptr + 1 : nullptr) { + /* Note that this also treats backslashes as component separators, which + * helps in cleaning up backslash-separated paths. */ + next_slash_ptr = BLI_path_slash_find(path_component); + + const bool is_last_component = next_slash_ptr == nullptr; + /* Note that this won't be null terminated. */ + const StringRef component_name = is_last_component ? + path_component : + StringRef(path_component, + next_slash_ptr - path_component); + + callback(component_name, is_last_component); + } +} + +AssetCatalogPath AssetCatalogPath::rebase(const AssetCatalogPath &from_path, + const AssetCatalogPath &to_path) const +{ + if (!from_path) { + if (!to_path) { + return AssetCatalogPath(""); + } + return to_path / *this; + } + + if (!this->is_contained_in(from_path)) { + return AssetCatalogPath(""); + } + + if (*this == from_path) { + /* Early return, because otherwise the length+1 below is going to cause problems. */ + return to_path; + } + + /* When from_path = "test", we need to skip "test/" to get the rest of the path, hence the +1. */ + const StringRef suffix = StringRef(this->path_).substr(from_path.length() + 1); + const AssetCatalogPath path_suffix(suffix); + return to_path / path_suffix; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_catalog_path_test.cc b/source/blender/blenkernel/intern/asset_catalog_path_test.cc new file mode 100644 index 00000000000..f248863ce77 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog_path_test.cc @@ -0,0 +1,284 @@ +/* + * 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) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_asset_catalog_path.hh" + +#include "BLI_set.hh" +#include "BLI_vector.hh" + +#include +#include + +#include "testing/testing.h" + +namespace blender::bke::tests { + +TEST(AssetCatalogPathTest, construction) +{ + AssetCatalogPath default_constructed; + /* Use `.str()` to use `std:string`'s comparison operators here, not our own (which are tested + * later). */ + EXPECT_EQ(default_constructed.str(), ""); + + /* C++ considers this construction special, it doesn't call the default constructor but does + * recursive, member-wise value initialization. See https://stackoverflow.com/a/4982720. */ + AssetCatalogPath value_initialized = AssetCatalogPath(); + EXPECT_EQ(value_initialized.str(), ""); + + AssetCatalogPath from_char_literal("the/path"); + + const std::string str_const = "the/path"; + AssetCatalogPath from_string_constant(str_const); + + std::string str_variable = "the/path"; + AssetCatalogPath from_string_variable(str_variable); + + std::string long_string = "this is a long/string/with/a/path in the middle"; + StringRef long_string_ref(long_string); + StringRef middle_bit = long_string_ref.substr(10, 23); + AssetCatalogPath from_string_ref(middle_bit); + EXPECT_EQ(from_string_ref, "long/string/with/a/path"); +} + +TEST(AssetCatalogPathTest, length) +{ + const AssetCatalogPath one("1"); + EXPECT_EQ(1, one.length()); + + const AssetCatalogPath empty(""); + EXPECT_EQ(0, empty.length()); + + const AssetCatalogPath utf8("some/родитель"); + EXPECT_EQ(21, utf8.length()) << "13 characters should be 21 bytes."; +} + +TEST(AssetCatalogPathTest, name) +{ + EXPECT_EQ(StringRefNull(""), AssetCatalogPath("").name()); + EXPECT_EQ(StringRefNull("word"), AssetCatalogPath("word").name()); + EXPECT_EQ(StringRefNull("Пермь"), AssetCatalogPath("дорога/в/Пермь").name()); + EXPECT_EQ(StringRefNull("windows\\paths"), + AssetCatalogPath("these/are/not/windows\\paths").name()); +} + +TEST(AssetCatalogPathTest, comparison_operators) +{ + const AssetCatalogPath empty(""); + const AssetCatalogPath the_path("the/path"); + const AssetCatalogPath the_path_child("the/path/child"); + const AssetCatalogPath unrelated_path("unrelated/path"); + const AssetCatalogPath other_instance_same_path("the/path"); + + EXPECT_LT(empty, the_path); + EXPECT_LT(the_path, the_path_child); + EXPECT_LT(the_path, unrelated_path); + + EXPECT_EQ(empty, empty) << "Identical empty instances should compare equal."; + EXPECT_EQ(empty, "") << "Comparison to empty string should be possible."; + EXPECT_EQ(the_path, the_path) << "Identical non-empty instances should compare equal."; + EXPECT_EQ(the_path, "the/path") << "Comparison to string should be possible."; + EXPECT_EQ(the_path, other_instance_same_path) + << "Different instances with equal path should compare equal."; + + EXPECT_NE(the_path, the_path_child); + EXPECT_NE(the_path, unrelated_path); + EXPECT_NE(the_path, empty); + + EXPECT_FALSE(empty); + EXPECT_TRUE(the_path); +} + +TEST(AssetCatalogPathTest, move_semantics) +{ + AssetCatalogPath source_path("source/path"); + EXPECT_TRUE(source_path); + + AssetCatalogPath dest_path = std::move(source_path); + EXPECT_FALSE(source_path); /* NOLINT: bugprone-use-after-move */ + EXPECT_TRUE(dest_path); +} + +TEST(AssetCatalogPathTest, concatenation) +{ + AssetCatalogPath some_parent("some/родитель"); + AssetCatalogPath child = some_parent / "ребенок"; + + EXPECT_EQ(some_parent, "some/родитель") + << "Appending a child path should not modify the parent."; + EXPECT_EQ(child, "some/родитель/ребенок"); + + AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук"; + EXPECT_EQ(appended_compound_path, "some/родитель/ребенок/внук"); + + AssetCatalogPath empty(""); + AssetCatalogPath child_of_the_void = empty / "child"; + EXPECT_EQ(child_of_the_void, "child") + << "Appending to an empty path should not create an initial slash."; + + AssetCatalogPath parent_of_the_void = some_parent / empty; + EXPECT_EQ(parent_of_the_void, "some/родитель") + << "Prepending to an empty path should not create a trailing slash."; + + std::string subpath = "child"; + AssetCatalogPath concatenated_with_string = some_parent / subpath; + EXPECT_EQ(concatenated_with_string, "some/родитель/child"); +} + +TEST(AssetCatalogPathTest, hashable) +{ + AssetCatalogPath path("heyyyyy"); + + std::set path_std_set; + path_std_set.insert(path); + + blender::Set path_blender_set; + path_blender_set.add(path); +} + +TEST(AssetCatalogPathTest, stream_operator) +{ + AssetCatalogPath path("путь/в/Пермь"); + std::stringstream sstream; + sstream << path; + EXPECT_EQ("путь/в/Пермь", sstream.str()); +} + +TEST(AssetCatalogPathTest, is_contained_in) +{ + const AssetCatalogPath catpath("simple/path/child"); + EXPECT_FALSE(catpath.is_contained_in("unrelated")); + EXPECT_FALSE(catpath.is_contained_in("sim")); + EXPECT_FALSE(catpath.is_contained_in("simple/pathx")); + EXPECT_FALSE(catpath.is_contained_in("simple/path/c")); + EXPECT_FALSE(catpath.is_contained_in("simple/path/child/grandchild")); + EXPECT_FALSE(catpath.is_contained_in("simple/path/")) + << "Non-normalized paths are not expected to work."; + + EXPECT_TRUE(catpath.is_contained_in("")); + EXPECT_TRUE(catpath.is_contained_in("simple")); + EXPECT_TRUE(catpath.is_contained_in("simple/path")); + + /* Test with some UTF8 non-ASCII characters. */ + AssetCatalogPath some_parent("some/родитель"); + AssetCatalogPath child = some_parent / "ребенок"; + + EXPECT_TRUE(child.is_contained_in(some_parent)); + EXPECT_TRUE(child.is_contained_in("some")); + + AssetCatalogPath appended_compound_path = some_parent / "ребенок/внук"; + EXPECT_TRUE(appended_compound_path.is_contained_in(some_parent)); + EXPECT_TRUE(appended_compound_path.is_contained_in(child)); + + /* Test "going up" directory-style. */ + AssetCatalogPath child_with_dotdot = some_parent / "../../other/hierarchy/part"; + EXPECT_TRUE(child_with_dotdot.is_contained_in(some_parent)) + << "dotdot path components should have no meaning"; +} + +TEST(AssetCatalogPathTest, cleanup) +{ + { + AssetCatalogPath ugly_path("/ some / родитель / "); + AssetCatalogPath clean_path = ugly_path.cleanup(); + EXPECT_EQ(AssetCatalogPath("/ some / родитель / "), ugly_path) + << "cleanup should not modify the path instance itself"; + EXPECT_EQ(AssetCatalogPath("some/родитель"), clean_path); + } + { + AssetCatalogPath double_slashed("some//родитель"); + EXPECT_EQ(AssetCatalogPath("some/родитель"), double_slashed.cleanup()); + } + { + AssetCatalogPath with_colons("some/key:subkey=value/path"); + EXPECT_EQ(AssetCatalogPath("some/key-subkey=value/path"), with_colons.cleanup()); + } + { + const AssetCatalogPath with_backslashes("windows\\for\\life"); + EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_backslashes.cleanup()); + } + { + const AssetCatalogPath with_mixed("windows\\for/life"); + EXPECT_EQ(AssetCatalogPath("windows/for/life"), with_mixed.cleanup()); + } + { + const AssetCatalogPath with_punctuation("is!/this?/¿valid?"); + EXPECT_EQ(AssetCatalogPath("is!/this?/¿valid?"), with_punctuation.cleanup()); + } +} + +TEST(AssetCatalogPathTest, iterate_components) +{ + AssetCatalogPath path("путь/в/Пермь"); + Vector> seen_components; + + path.iterate_components([&seen_components](StringRef component_name, bool is_last_component) { + std::pair parameter_pair = std::make_pair( + component_name, bool(is_last_component)); + seen_components.append(parameter_pair); + }); + + ASSERT_EQ(3, seen_components.size()); + + EXPECT_EQ("путь", seen_components[0].first); + EXPECT_EQ("в", seen_components[1].first); + EXPECT_EQ("Пермь", seen_components[2].first); + + EXPECT_FALSE(seen_components[0].second); + EXPECT_FALSE(seen_components[1].second); + EXPECT_TRUE(seen_components[2].second); +} + +TEST(AssetCatalogPathTest, rebase) +{ + AssetCatalogPath path("some/path/to/some/catalog"); + EXPECT_EQ(path.rebase("some/path", "new/base"), "new/base/to/some/catalog"); + EXPECT_EQ(path.rebase("", "new/base"), "new/base/some/path/to/some/catalog"); + + EXPECT_EQ(path.rebase("some/path/to/some/catalog", "some/path/to/some/catalog"), + "some/path/to/some/catalog") + << "Rebasing to itself should not change the path."; + + EXPECT_EQ(path.rebase("path/to", "new/base"), "") + << "Non-matching base path should return empty string to indicate 'NO'."; + + /* Empty strings should be handled without crashing or other nasty side-effects. */ + AssetCatalogPath empty(""); + EXPECT_EQ(empty.rebase("path/to", "new/base"), ""); + EXPECT_EQ(empty.rebase("", "new/base"), "new/base"); + EXPECT_EQ(empty.rebase("", ""), ""); +} + +TEST(AssetCatalogPathTest, parent) +{ + const AssetCatalogPath ascii_path("path/with/missing/parents"); + EXPECT_EQ(ascii_path.parent(), "path/with/missing"); + + const AssetCatalogPath path("путь/в/Пермь/долог/и/далек"); + EXPECT_EQ(path.parent(), "путь/в/Пермь/долог/и"); + EXPECT_EQ(path.parent().parent(), "путь/в/Пермь/долог"); + EXPECT_EQ(path.parent().parent().parent(), "путь/в/Пермь"); + + const AssetCatalogPath one_level("one"); + EXPECT_EQ(one_level.parent(), ""); + + const AssetCatalogPath empty(""); + EXPECT_EQ(empty.parent(), ""); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_catalog_test.cc b/source/blender/blenkernel/intern/asset_catalog_test.cc new file mode 100644 index 00000000000..2cef34966f8 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_catalog_test.cc @@ -0,0 +1,1488 @@ +/* + * 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) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_appdir.h" +#include "BKE_asset_catalog.hh" +#include "BKE_preferences.h" + +#include "BLI_fileops.h" +#include "BLI_path_util.h" + +#include "DNA_userdef_types.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +/* UUIDs from lib/tests/asset_library/blender_assets.cats.txt */ +const bUUID UUID_ID_WITHOUT_PATH("e34dd2c5-5d2e-4668-9794-1db5de2a4f71"); +const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); +const bUUID UUID_POSES_ELLIE_WHITESPACE("b06132f6-5687-4751-a6dd-392740eb3c46"); +const bUUID UUID_POSES_ELLIE_TRAILING_SLASH("3376b94b-a28d-4d05-86c1-bf30b937130d"); +const bUUID UUID_POSES_ELLIE_BACKSLASHES("a51e17ae-34fc-47d5-ba0f-64c2c9b771f7"); +const bUUID UUID_POSES_RUZENA("79a4f887-ab60-4bd4-94da-d572e27d6aed"); +const bUUID UUID_POSES_RUZENA_HAND("81811c31-1a88-4bd7-bb34-c6fc2607a12e"); +const bUUID UUID_POSES_RUZENA_FACE("82162c1f-06cc-4d91-a9bf-4f72c104e348"); +const bUUID UUID_WITHOUT_SIMPLENAME("d7916a31-6ca9-4909-955f-182ca2b81fa3"); +const bUUID UUID_ANOTHER_RUZENA("00000000-d9fa-4b91-b704-e6af1f1339ef"); + +/* UUIDs from lib/tests/asset_library/modified_assets.cats.txt */ +const bUUID UUID_AGENT_47("c5744ba5-43f5-4f73-8e52-010ad4a61b34"); + +/* Subclass that adds accessors such that protected fields can be used in tests. */ +class TestableAssetCatalogService : public AssetCatalogService { + public: + TestableAssetCatalogService() = default; + + explicit TestableAssetCatalogService(const CatalogFilePath &asset_library_root) + : AssetCatalogService(asset_library_root) + { + } + + AssetCatalogDefinitionFile *get_catalog_definition_file() + { + return AssetCatalogService::get_catalog_definition_file(); + } + + OwningAssetCatalogMap &get_deleted_catalogs() + { + return AssetCatalogService::get_deleted_catalogs(); + } + + void create_missing_catalogs() + { + AssetCatalogService::create_missing_catalogs(); + } + + void delete_catalog_by_id_soft(CatalogID catalog_id) + { + AssetCatalogService::delete_catalog_by_id_soft(catalog_id); + } + + int64_t count_catalogs_with_path(const CatalogFilePath &path) + { + int64_t count = 0; + for (auto &catalog_uptr : get_catalogs().values()) { + if (catalog_uptr->path == path) { + count++; + } + } + return count; + } +}; + +class AssetCatalogTest : public testing::Test { + protected: + CatalogFilePath asset_library_root_; + CatalogFilePath temp_library_path_; + + void SetUp() override + { + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + asset_library_root_ = test_files_dir + "/" + "asset_library"; + temp_library_path_ = ""; + } + + void TearDown() override + { + if (!temp_library_path_.empty()) { + BLI_delete(temp_library_path_.c_str(), true, true); + temp_library_path_ = ""; + } + } + + /* Register a temporary path, which will be removed at the end of the test. + * The returned path ends in a slash. */ + CatalogFilePath use_temp_path() + { + BKE_tempdir_init(""); + const CatalogFilePath tempdir = BKE_tempdir_session(); + temp_library_path_ = tempdir + "test-temporary-path/"; + return temp_library_path_; + } + + CatalogFilePath create_temp_path() + { + CatalogFilePath path = use_temp_path(); + BLI_dir_create_recursive(path.c_str()); + return path; + } + + void assert_expected_item(const AssetCatalogPath &expected_path, + const AssetCatalogTreeItem &actual_item) + { + if (expected_path != actual_item.catalog_path().str()) { + /* This will fail, but with a nicer error message than just calling FAIL(). */ + EXPECT_EQ(expected_path, actual_item.catalog_path()); + return; + } + + /* Is the catalog name as expected? "character", "Ellie", ... */ + EXPECT_EQ(expected_path.name(), actual_item.get_name()); + + /* Does the computed number of parents match? */ + const std::string expected_path_str = expected_path.str(); + const size_t expected_parent_count = std::count( + expected_path_str.begin(), expected_path_str.end(), AssetCatalogPath::SEPARATOR); + EXPECT_EQ(expected_parent_count, actual_item.count_parents()); + } + + /** + * Recursively iterate over all tree items using #AssetCatalogTree::foreach_item() and check if + * the items map exactly to \a expected_paths. + */ + void assert_expected_tree_items(AssetCatalogTree *tree, + const std::vector &expected_paths) + { + int i = 0; + tree->foreach_item([&](const AssetCatalogTreeItem &actual_item) { + ASSERT_LT(i, expected_paths.size()) + << "More catalogs in tree than expected; did not expect " << actual_item.catalog_path(); + assert_expected_item(expected_paths[i], actual_item); + i++; + }); + } + + /** + * Iterate over the root items of \a tree and check if the items map exactly to \a + * expected_paths. Similar to #assert_expected_tree_items() but calls + * #AssetCatalogTree::foreach_root_item() instead of #AssetCatalogTree::foreach_item(). + */ + void assert_expected_tree_root_items(AssetCatalogTree *tree, + const std::vector &expected_paths) + { + int i = 0; + tree->foreach_root_item([&](const AssetCatalogTreeItem &actual_item) { + ASSERT_LT(i, expected_paths.size()) + << "More catalogs in tree root than expected; did not expect " + << actual_item.catalog_path(); + assert_expected_item(expected_paths[i], actual_item); + i++; + }); + } + + /** + * Iterate over the child items of \a parent_item and check if the items map exactly to \a + * expected_paths. Similar to #assert_expected_tree_items() but calls + * #AssetCatalogTreeItem::foreach_child() instead of #AssetCatalogTree::foreach_item(). + */ + void assert_expected_tree_item_child_items(AssetCatalogTreeItem *parent_item, + const std::vector &expected_paths) + { + int i = 0; + parent_item->foreach_child([&](const AssetCatalogTreeItem &actual_item) { + ASSERT_LT(i, expected_paths.size()) + << "More catalogs in tree item than expected; did not expect " + << actual_item.catalog_path(); + assert_expected_item(expected_paths[i], actual_item); + i++; + }); + } + + /* Used by on_blendfile_save__from_memory_into_existing_asset_lib* test functions. */ + void save_from_memory_into_existing_asset_lib(const bool should_top_level_cdf_exist) + { + const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath registered_asset_lib = target_dir + "my_asset_library/"; + const CatalogFilePath asset_lib_subdir = registered_asset_lib + "subdir/"; + CatalogFilePath cdf_toplevel = registered_asset_lib + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + CatalogFilePath cdf_in_subdir = asset_lib_subdir + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_path_slash_native(cdf_toplevel.data()); + BLI_path_slash_native(cdf_in_subdir.data()); + + /* Set up a temporary asset library for testing. */ + bUserAssetLibrary *asset_lib_pref = BKE_preferences_asset_library_add( + &U, "Test", registered_asset_lib.c_str()); + ASSERT_NE(nullptr, asset_lib_pref); + ASSERT_TRUE(BLI_dir_create_recursive(asset_lib_subdir.c_str())); + + if (should_top_level_cdf_exist) { + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), cdf_toplevel.c_str())); + } + + /* Create an empty CDF to add complexity. It should not save to this, but to the top-level + * one.*/ + ASSERT_TRUE(BLI_file_touch(cdf_in_subdir.c_str())); + ASSERT_EQ(0, BLI_file_size(cdf_in_subdir.c_str())); + + /* Create the catalog service without loading the already-existing CDF. */ + TestableAssetCatalogService service; + const CatalogFilePath blendfilename = asset_lib_subdir + "some_file.blend"; + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + /* Mock that the blend file is written to the directory already containing a CDF. */ + ASSERT_TRUE(service.write_to_disk(blendfilename)); + + /* Test that the CDF still exists in the expected location. */ + EXPECT_TRUE(BLI_exists(cdf_toplevel.c_str())); + const CatalogFilePath backup_filename = cdf_toplevel + "~"; + const bool backup_exists = BLI_exists(backup_filename.c_str()); + EXPECT_EQ(should_top_level_cdf_exist, backup_exists) + << "Overwritten CDF should have been backed up."; + + /* Test that the in-memory CDF has the expected file path. */ + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + BLI_path_slash_native(cdf->file_path.data()); + EXPECT_EQ(cdf_toplevel, cdf->file_path); + + /* Test that the in-memory catalogs have been merged with the on-disk one. */ + AssetCatalogService loaded_service(cdf_toplevel); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); + + /* This catalog comes from a pre-existing CDF that should have been merged. + * However, if the file doesn't exist, so does the catalog. */ + AssetCatalog *poses_ellie_catalog = loaded_service.find_catalog(UUID_POSES_ELLIE); + if (should_top_level_cdf_exist) { + EXPECT_NE(nullptr, poses_ellie_catalog); + } + else { + EXPECT_EQ(nullptr, poses_ellie_catalog); + } + + /* Test that the "red herring" CDF has not been touched. */ + EXPECT_EQ(0, BLI_file_size(cdf_in_subdir.c_str())); + + BKE_preferences_asset_library_remove(&U, asset_lib_pref); + } +}; + +TEST_F(AssetCatalogTest, load_single_file) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Test getting a non-existent catalog ID. */ + EXPECT_EQ(nullptr, service.find_catalog(BLI_uuid_generate_random())); + + /* Test getting an invalid catalog (without path definition). */ + AssetCatalog *cat_without_path = service.find_catalog(UUID_ID_WITHOUT_PATH); + ASSERT_EQ(nullptr, cat_without_path); + + /* Test getting a regular catalog. */ + AssetCatalog *poses_ellie = service.find_catalog(UUID_POSES_ELLIE); + ASSERT_NE(nullptr, poses_ellie); + EXPECT_EQ(UUID_POSES_ELLIE, poses_ellie->catalog_id); + EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str()); + EXPECT_EQ("POSES_ELLIE", poses_ellie->simple_name); + + /* Test white-space stripping and support in the path. */ + AssetCatalog *poses_whitespace = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE); + ASSERT_NE(nullptr, poses_whitespace); + EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, poses_whitespace->catalog_id); + EXPECT_EQ("character/Ellie/poselib/white space", poses_whitespace->path.str()); + EXPECT_EQ("POSES_ELLIE WHITESPACE", poses_whitespace->simple_name); + + /* Test getting a UTF-8 catalog ID. */ + AssetCatalog *poses_ruzena = service.find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, poses_ruzena); + EXPECT_EQ(UUID_POSES_RUZENA, poses_ruzena->catalog_id); + EXPECT_EQ("character/Ružena/poselib", poses_ruzena->path.str()); + EXPECT_EQ("POSES_RUŽENA", poses_ruzena->simple_name); + + /* Test getting a catalog that aliases an earlier-defined catalog. */ + AssetCatalog *another_ruzena = service.find_catalog(UUID_ANOTHER_RUZENA); + ASSERT_NE(nullptr, another_ruzena); + EXPECT_EQ(UUID_ANOTHER_RUZENA, another_ruzena->catalog_id); + EXPECT_EQ("character/Ružena/poselib", another_ruzena->path.str()); + EXPECT_EQ("Another Ružena", another_ruzena->simple_name); +} + +TEST_F(AssetCatalogTest, load_catalog_path_backslashes) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + const AssetCatalog *found_by_id = service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES); + ASSERT_NE(nullptr, found_by_id); + EXPECT_EQ(AssetCatalogPath("character/Ellie/backslashes"), found_by_id->path) + << "Backslashes should be normalised when loading from disk."; + EXPECT_EQ(StringRefNull("Windows For Life!"), found_by_id->simple_name); + + const AssetCatalog *found_by_path = service.find_catalog_by_path("character/Ellie/backslashes"); + EXPECT_EQ(found_by_id, found_by_path) + << "Catalog with backslashed path should be findable by the normalized path."; + + EXPECT_EQ(nullptr, service.find_catalog_by_path("character\\Ellie\\backslashes")) + << "Nothing should be found when searching for backslashes."; +} + +TEST_F(AssetCatalogTest, is_first_loaded_flag) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + AssetCatalog *new_cat = service.create_catalog("never/before/seen/path"); + EXPECT_FALSE(new_cat->flags.is_first_loaded) + << "Adding a catalog at runtime should never mark it as 'first loaded'; " + "only loading from disk is allowed to do that."; + + AssetCatalog *alias_cat = service.create_catalog("character/Ružena/poselib"); + EXPECT_FALSE(alias_cat->flags.is_first_loaded) + << "Adding a new catalog with an already-loaded path should not mark it as 'first loaded'"; + + EXPECT_TRUE(service.find_catalog(UUID_POSES_ELLIE)->flags.is_first_loaded); + EXPECT_TRUE(service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->flags.is_first_loaded); + EXPECT_TRUE(service.find_catalog(UUID_POSES_RUZENA)->flags.is_first_loaded); + EXPECT_FALSE(service.find_catalog(UUID_ANOTHER_RUZENA)->flags.is_first_loaded); + + AssetCatalog *ruzena = service.find_catalog_by_path("character/Ružena/poselib"); + EXPECT_EQ(UUID_POSES_RUZENA, ruzena->catalog_id) + << "The first-seen definition of a catalog should be returned"; +} + +TEST_F(AssetCatalogTest, insert_item_into_tree) +{ + { + AssetCatalogTree tree; + std::unique_ptr catalog_empty_path = AssetCatalog::from_path(""); + tree.insert_item(*catalog_empty_path); + + assert_expected_tree_items(&tree, {}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("item"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {"item"}); + + /* Insert child after parent already exists. */ + std::unique_ptr child_catalog = AssetCatalog::from_path("item/child"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {"item", "item/child"}); + + std::vector expected_paths; + + /* Test inserting multi-component sub-path. */ + std::unique_ptr grandgrandchild_catalog = AssetCatalog::from_path( + "item/child/grandchild/grandgrandchild"); + tree.insert_item(*catalog); + expected_paths = { + "item", "item/child", "item/child/grandchild", "item/child/grandchild/grandgrandchild"}; + assert_expected_tree_items(&tree, expected_paths); + + std::unique_ptr root_level_catalog = AssetCatalog::from_path("root level"); + tree.insert_item(*catalog); + expected_paths = {"item", + "item/child", + "item/child/grandchild", + "item/child/grandchild/grandgrandchild", + "root level"}; + assert_expected_tree_items(&tree, expected_paths); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("item/child"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {"item", "item/child"}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("white space"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {"white space"}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog = AssetCatalog::from_path("/item/white space"); + tree.insert_item(*catalog); + assert_expected_tree_items(&tree, {"item", "item/white space"}); + } + + { + AssetCatalogTree tree; + + std::unique_ptr catalog_unicode_path = AssetCatalog::from_path("Ružena"); + tree.insert_item(*catalog_unicode_path); + assert_expected_tree_items(&tree, {"Ružena"}); + + catalog_unicode_path = AssetCatalog::from_path("Ružena/Ružena"); + tree.insert_item(*catalog_unicode_path); + assert_expected_tree_items(&tree, {"Ružena", "Ružena/Ružena"}); + } +} + +TEST_F(AssetCatalogTest, load_single_file_into_tree) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Contains not only paths from the CDF but also the missing parents (implicitly defined + * catalogs). */ + std::vector expected_paths{ + "character", + "character/Ellie", + "character/Ellie/backslashes", + "character/Ellie/poselib", + "character/Ellie/poselib/tailslash", + "character/Ellie/poselib/white space", + "character/Ružena", + "character/Ružena/poselib", + "character/Ružena/poselib/face", + "character/Ružena/poselib/hand", + "path", /* Implicit. */ + "path/without", /* Implicit. */ + "path/without/simplename", /* From CDF. */ + }; + + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_items(tree, expected_paths); +} + +TEST_F(AssetCatalogTest, foreach_in_tree) +{ + { + AssetCatalogTree tree{}; + const std::vector no_catalogs{}; + + assert_expected_tree_items(&tree, no_catalogs); + assert_expected_tree_root_items(&tree, no_catalogs); + /* Need a root item to check child items. */ + std::unique_ptr catalog = AssetCatalog::from_path("something"); + tree.insert_item(*catalog); + tree.foreach_root_item([&no_catalogs, this](AssetCatalogTreeItem &item) { + assert_expected_tree_item_child_items(&item, no_catalogs); + }); + } + + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + std::vector expected_root_items{{"character", "path"}}; + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_root_items(tree, expected_root_items); + + /* Test if the direct children of the root item are what's expected. */ + std::vector> expected_root_child_items = { + /* Children of the "character" root item. */ + {"character/Ellie", "character/Ružena"}, + /* Children of the "path" root item. */ + {"path/without"}, + }; + int i = 0; + tree->foreach_root_item([&expected_root_child_items, &i, this](AssetCatalogTreeItem &item) { + assert_expected_tree_item_child_items(&item, expected_root_child_items[i]); + i++; + }); +} + +TEST_F(AssetCatalogTest, find_catalog_by_path) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + AssetCatalog *catalog; + + EXPECT_EQ(nullptr, service.find_catalog_by_path("")); + catalog = service.find_catalog_by_path("character/Ellie/poselib/white space"); + EXPECT_NE(nullptr, catalog); + EXPECT_EQ(UUID_POSES_ELLIE_WHITESPACE, catalog->catalog_id); + catalog = service.find_catalog_by_path("character/Ružena/poselib"); + EXPECT_NE(nullptr, catalog); + EXPECT_EQ(UUID_POSES_RUZENA, catalog->catalog_id); + + /* "character/Ellie/poselib" is used by two catalogs. Check if it's using the first one. */ + catalog = service.find_catalog_by_path("character/Ellie/poselib"); + EXPECT_NE(nullptr, catalog); + EXPECT_EQ(UUID_POSES_ELLIE, catalog->catalog_id); + EXPECT_NE(UUID_POSES_ELLIE_TRAILING_SLASH, catalog->catalog_id); +} + +TEST_F(AssetCatalogTest, write_single_file) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + const CatalogFilePath save_to_path = use_temp_path() + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + cdf->write_to_disk(save_to_path); + + AssetCatalogService loaded_service(save_to_path); + loaded_service.load_from_disk(); + + /* Test that the expected catalogs are there. */ + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); + + /* Test that the invalid catalog definition wasn't copied. */ + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_ID_WITHOUT_PATH)); + + /* TODO(@sybren): test ordering of catalogs in the file. */ +} + +TEST_F(AssetCatalogTest, no_writing_empty_files) +{ + const CatalogFilePath temp_lib_root = create_temp_path(); + AssetCatalogService service(temp_lib_root); + service.write_to_disk(temp_lib_root + "phony.blend"); + + const CatalogFilePath default_cdf_path = temp_lib_root + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + EXPECT_FALSE(BLI_exists(default_cdf_path.c_str())); +} + +/* Already loaded a CDF, saving to some unrelated directory. */ +TEST_F(AssetCatalogTest, on_blendfile_save__with_existing_cdf) +{ + const CatalogFilePath top_level_dir = create_temp_path(); /* Has trailing slash. */ + + /* Create a copy of the CDF in SVN, so we can safely write to it. */ + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath cdf_dirname = top_level_dir + "other_dir/"; + const CatalogFilePath cdf_filename = cdf_dirname + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + ASSERT_TRUE(BLI_dir_create_recursive(cdf_dirname.c_str())); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), cdf_filename.c_str())) + << "Unable to copy " << original_cdf_file << " to " << cdf_filename; + + /* Load the CDF, add a catalog, and trigger a write. This should write to the loaded CDF. */ + TestableAssetCatalogService service(cdf_filename); + service.load_from_disk(); + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + const CatalogFilePath blendfilename = top_level_dir + "subdir/some_file.blend"; + ASSERT_TRUE(service.write_to_disk(blendfilename)); + EXPECT_EQ(cdf_filename, service.get_catalog_definition_file()->file_path); + + /* Test that the CDF was created in the expected location. */ + const CatalogFilePath backup_filename = cdf_filename + "~"; + EXPECT_TRUE(BLI_exists(cdf_filename.c_str())); + EXPECT_TRUE(BLI_exists(backup_filename.c_str())) + << "Overwritten CDF should have been backed up."; + + /* Test that the on-disk CDF contains the expected catalogs. */ + AssetCatalogService loaded_service(cdf_filename); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)) + << "Expected to see the newly-created catalog."; + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)) + << "Expected to see the already-existing catalog."; +} + +/* Create some catalogs in memory, save to directory that doesn't contain anything else. */ +TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_empty_directory) +{ + const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ + + TestableAssetCatalogService service; + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + const CatalogFilePath blendfilename = target_dir + "some_file.blend"; + ASSERT_TRUE(service.write_to_disk(blendfilename)); + + /* Test that the CDF was created in the expected location. */ + const CatalogFilePath expected_cdf_path = target_dir + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + EXPECT_TRUE(BLI_exists(expected_cdf_path.c_str())); + + /* Test that the in-memory CDF has been created, and contains the expected catalog. */ + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + ASSERT_NE(nullptr, cdf); + EXPECT_TRUE(cdf->contains(cat->catalog_id)); + + /* Test that the on-disk CDF contains the expected catalog. */ + AssetCatalogService loaded_service(expected_cdf_path); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); +} + +/* Create some catalogs in memory, save to directory that contains a default CDF. */ +TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_cdf_and_merge) +{ + const CatalogFilePath target_dir = create_temp_path(); /* Has trailing slash. */ + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + CatalogFilePath writable_cdf_file = target_dir + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_path_slash_native(writable_cdf_file.data()); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); + + /* Create the catalog service without loading the already-existing CDF. */ + TestableAssetCatalogService service; + const AssetCatalog *cat = service.create_catalog("some/catalog/path"); + + /* Mock that the blend file is written to a subdirectory of the asset library. */ + const CatalogFilePath blendfilename = target_dir + "some_file.blend"; + ASSERT_TRUE(service.write_to_disk(blendfilename)); + + /* Test that the CDF still exists in the expected location. */ + const CatalogFilePath backup_filename = writable_cdf_file + "~"; + EXPECT_TRUE(BLI_exists(writable_cdf_file.c_str())); + EXPECT_TRUE(BLI_exists(backup_filename.c_str())) + << "Overwritten CDF should have been backed up."; + + /* Test that the in-memory CDF has the expected file path. */ + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + ASSERT_NE(nullptr, cdf); + EXPECT_EQ(writable_cdf_file, cdf->file_path); + + /* Test that the in-memory catalogs have been merged with the on-disk one. */ + AssetCatalogService loaded_service(writable_cdf_file); + loaded_service.load_from_disk(); + EXPECT_NE(nullptr, loaded_service.find_catalog(cat->catalog_id)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); +} + +/* Create some catalogs in memory, save to subdirectory of a registered asset library, where the + * subdirectory also contains a CDF. This should still write to the top-level dir of the asset + * library. */ +TEST_F(AssetCatalogTest, + on_blendfile_save__from_memory_into_existing_asset_lib_without_top_level_cdf) +{ + save_from_memory_into_existing_asset_lib(true); +} + +/* Create some catalogs in memory, save to subdirectory of a registered asset library, where the + * subdirectory contains a CDF, but the top-level directory does not. This should still write to + * the top-level dir of the asset library. */ +TEST_F(AssetCatalogTest, on_blendfile_save__from_memory_into_existing_asset_lib) +{ + save_from_memory_into_existing_asset_lib(false); +} + +TEST_F(AssetCatalogTest, create_first_catalog_from_scratch) +{ + /* Even from scratch a root directory should be known. */ + const CatalogFilePath temp_lib_root = use_temp_path(); + AssetCatalogService service; + + /* Just creating the service should NOT create the path. */ + EXPECT_FALSE(BLI_exists(temp_lib_root.c_str())); + + AssetCatalog *cat = service.create_catalog("some/catalog/path"); + ASSERT_NE(nullptr, cat); + EXPECT_EQ(cat->path, "some/catalog/path"); + EXPECT_EQ(cat->simple_name, "some-catalog-path"); + + /* Creating a new catalog should not save anything to disk yet. */ + EXPECT_FALSE(BLI_exists(temp_lib_root.c_str())); + + /* Writing to disk should create the directory + the default file. */ + service.write_to_disk(temp_lib_root + "phony.blend"); + EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); + + const CatalogFilePath definition_file_path = temp_lib_root + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + EXPECT_TRUE(BLI_is_file(definition_file_path.c_str())); + + AssetCatalogService loaded_service(temp_lib_root); + loaded_service.load_from_disk(); + + /* Test that the expected catalog is there. */ + AssetCatalog *written_cat = loaded_service.find_catalog(cat->catalog_id); + ASSERT_NE(nullptr, written_cat); + EXPECT_EQ(written_cat->catalog_id, cat->catalog_id); + EXPECT_EQ(written_cat->path, cat->path.str()); +} + +TEST_F(AssetCatalogTest, create_catalog_after_loading_file) +{ + const CatalogFilePath temp_lib_root = create_temp_path(); + + /* Copy the asset catalog definition files to a separate location, so that we can test without + * overwriting the test file in SVN. */ + const CatalogFilePath default_catalog_path = asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + const CatalogFilePath writable_catalog_path = temp_lib_root + + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + ASSERT_EQ(0, BLI_copy(default_catalog_path.c_str(), writable_catalog_path.c_str())); + EXPECT_TRUE(BLI_is_dir(temp_lib_root.c_str())); + EXPECT_TRUE(BLI_is_file(writable_catalog_path.c_str())); + + TestableAssetCatalogService service(temp_lib_root); + service.load_from_disk(); + EXPECT_EQ(writable_catalog_path, service.get_catalog_definition_file()->file_path); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE)) << "expected catalogs to be loaded"; + + /* This should create a new catalog but not write to disk. */ + const AssetCatalog *new_catalog = service.create_catalog("new/catalog"); + const bUUID new_catalog_id = new_catalog->catalog_id; + + /* Reload the on-disk catalog file. */ + TestableAssetCatalogService loaded_service(temp_lib_root); + loaded_service.load_from_disk(); + EXPECT_EQ(writable_catalog_path, loaded_service.get_catalog_definition_file()->file_path); + + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)) + << "expected pre-existing catalogs to be kept in the file"; + EXPECT_EQ(nullptr, loaded_service.find_catalog(new_catalog_id)) + << "expecting newly added catalog to not yet be saved to " << temp_lib_root; + + /* Write and reload the catalog file. */ + service.write_to_disk(temp_lib_root + "phony.blend"); + AssetCatalogService reloaded_service(temp_lib_root); + reloaded_service.load_from_disk(); + EXPECT_NE(nullptr, reloaded_service.find_catalog(UUID_POSES_ELLIE)) + << "expected pre-existing catalogs to be kept in the file"; + EXPECT_NE(nullptr, reloaded_service.find_catalog(new_catalog_id)) + << "expecting newly added catalog to exist in the file"; +} + +TEST_F(AssetCatalogTest, create_catalog_path_cleanup) +{ + AssetCatalogService service; + AssetCatalog *cat = service.create_catalog(" /some/path / "); + + EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id)); + EXPECT_EQ("some/path", cat->path.str()); + EXPECT_EQ("some-path", cat->simple_name); +} + +TEST_F(AssetCatalogTest, create_catalog_simple_name) +{ + AssetCatalogService service; + AssetCatalog *cat = service.create_catalog( + "production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands"); + + EXPECT_FALSE(BLI_uuid_is_nil(cat->catalog_id)); + EXPECT_EQ("production/Spite Fright/Characters/Victora/Pose Library/Approved/Body Parts/Hands", + cat->path.str()); + EXPECT_EQ("...ht-Characters-Victora-Pose Library-Approved-Body Parts-Hands", cat->simple_name); +} + +TEST_F(AssetCatalogTest, delete_catalog_leaf) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Delete a leaf catalog, i.e. one that is not a parent of another catalog. + * This keeps this particular test easy. */ + service.prune_catalogs_by_id(UUID_POSES_RUZENA_HAND); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); + + /* Contains not only paths from the CDF but also the missing parents (implicitly defined + * catalogs). This is why a leaf catalog was deleted. */ + std::vector expected_paths{ + "character", + "character/Ellie", + "character/Ellie/backslashes", + "character/Ellie/poselib", + "character/Ellie/poselib/tailslash", + "character/Ellie/poselib/white space", + "character/Ružena", + "character/Ružena/poselib", + "character/Ružena/poselib/face", + // "character/Ružena/poselib/hand", /* This is the deleted one. */ + "path", + "path/without", + "path/without/simplename", + }; + + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_items(tree, expected_paths); +} + +TEST_F(AssetCatalogTest, delete_catalog_parent_by_id) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Delete a parent catalog. */ + service.delete_catalog_by_id_soft(UUID_POSES_RUZENA); + + /* The catalog should have been deleted, but its children should still be there. */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_RUZENA_FACE)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); +} + +TEST_F(AssetCatalogTest, delete_catalog_parent_by_path) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + "blender_assets.cats.txt"); + + /* Create an extra catalog with the to-be-deleted path, and one with a child of that. + * This creates some duplicates that are bound to occur in production asset libraries as well. + */ + const bUUID cat1_uuid = service.create_catalog("character/Ružena/poselib")->catalog_id; + const bUUID cat2_uuid = service.create_catalog("character/Ružena/poselib/body")->catalog_id; + + /* Delete a parent catalog. */ + service.prune_catalogs_by_path("character/Ružena/poselib"); + + /* The catalogs and their children should have been deleted. */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_FACE)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_EQ(nullptr, service.find_catalog(cat1_uuid)); + EXPECT_EQ(nullptr, service.find_catalog(cat2_uuid)); + + /* Contains not only paths from the CDF but also the missing parents (implicitly defined + * catalogs). This is why a leaf catalog was deleted. */ + std::vector expected_paths{ + "character", + "character/Ellie", + "character/Ellie/backslashes", + "character/Ellie/poselib", + "character/Ellie/poselib/tailslash", + "character/Ellie/poselib/white space", + "character/Ružena", + "path", + "path/without", + "path/without/simplename", + }; + + AssetCatalogTree *tree = service.get_catalog_tree(); + assert_expected_tree_items(tree, expected_paths); +} + +TEST_F(AssetCatalogTest, delete_catalog_write_to_disk) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + service.delete_catalog_by_id_soft(UUID_POSES_ELLIE); + + const CatalogFilePath save_to_path = use_temp_path(); + AssetCatalogDefinitionFile *cdf = service.get_catalog_definition_file(); + cdf->write_to_disk(save_to_path + "/" + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + AssetCatalogService loaded_service(save_to_path); + loaded_service.load_from_disk(); + + /* Test that the expected catalogs are there, except the deleted one. */ + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); +} + +TEST_F(AssetCatalogTest, update_catalog_path) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); + const AssetCatalogPath orig_path = orig_cat->path; + + service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena"); + + EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path)) + << "The original (pre-rename) path should not be associated with a catalog any more."; + + const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, renamed_cat); + ASSERT_EQ(orig_cat, renamed_cat) << "Changing the path should not reallocate the catalog."; + EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) + << "Changing the path should not change the catalog ID."; + + EXPECT_EQ("charlib/Ružena", renamed_cat->path.str()) + << "Changing the path should change the path. Surprise."; + + EXPECT_EQ("charlib/Ružena/hand", service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str()) + << "Changing the path should update children."; + EXPECT_EQ("charlib/Ružena/face", service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str()) + << "Changing the path should update children."; +} + +TEST_F(AssetCatalogTest, update_catalog_path_simple_name) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + service.update_catalog_path(UUID_POSES_RUZENA, "charlib/Ružena"); + + /* This may not be valid forever; maybe at some point we'll expose the simple name to users & + * let them change it from the UI. Until then, automatically updating it is better, because + * otherwise all simple names would be "Catalog". */ + EXPECT_EQ("charlib-Ružena", service.find_catalog(UUID_POSES_RUZENA)->simple_name) + << "Changing the path should update the simplename."; + EXPECT_EQ("charlib-Ružena-face", service.find_catalog(UUID_POSES_RUZENA_FACE)->simple_name) + << "Changing the path should update the simplename of children."; +} + +TEST_F(AssetCatalogTest, update_catalog_path_add_slashes) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(asset_library_root_ + "/" + + AssetCatalogService::DEFAULT_CATALOG_FILENAME); + + const AssetCatalog *orig_cat = service.find_catalog(UUID_POSES_RUZENA); + const AssetCatalogPath orig_path = orig_cat->path; + + /* Original path is `character/Ružena/poselib`. + * This rename will also create a new catalog for `character/Ružena/poses`. */ + service.update_catalog_path(UUID_POSES_RUZENA, "character/Ružena/poses/general"); + + EXPECT_EQ(nullptr, service.find_catalog_by_path(orig_path)) + << "The original (pre-rename) path should not be associated with a catalog any more."; + + const AssetCatalog *renamed_cat = service.find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, renamed_cat); + EXPECT_EQ(orig_cat->catalog_id, renamed_cat->catalog_id) + << "Changing the path should not change the catalog ID."; + + EXPECT_EQ("character/Ružena/poses/general", renamed_cat->path.str()) + << "When creating a new catalog by renaming + adding a slash, the renamed catalog should be " + "assigned the path passed to update_catalog_path()"; + + /* Test the newly created catalog. */ + const AssetCatalog *new_cat = service.find_catalog_by_path("character/Ružena/poses"); + ASSERT_NE(nullptr, new_cat) << "Renaming to .../X/Y should cause .../X to exist as well."; + EXPECT_EQ("character/Ružena/poses", new_cat->path.str()); + EXPECT_EQ("character-Ružena-poses", new_cat->simple_name); + EXPECT_TRUE(new_cat->flags.has_unsaved_changes); + + /* Test the children. */ + EXPECT_EQ("character/Ružena/poses/general/hand", + service.find_catalog(UUID_POSES_RUZENA_HAND)->path.str()) + << "Changing the path should update children."; + EXPECT_EQ("character/Ružena/poses/general/face", + service.find_catalog(UUID_POSES_RUZENA_FACE)->path.str()) + << "Changing the path should update children."; +} + +TEST_F(AssetCatalogTest, merge_catalog_files) +{ + const CatalogFilePath cdf_dir = create_temp_path(); + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath modified_cdf_file = asset_library_root_ + "/modified_assets.cats.txt"; + const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str())); + + /* Load the unmodified, original CDF. */ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(cdf_dir); + + /* Copy a modified file, to mimic a situation where someone changed the + * CDF after we loaded it. */ + ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); + + /* Overwrite the modified file. This should merge the on-disk file with our catalogs. + * No catalog was marked as "has unsaved changes", so effectively this should not + * save anything, and reload what's on disk. */ + service.write_to_disk(cdf_dir + "phony.blend"); + + AssetCatalogService loaded_service(cdf_dir); + loaded_service.load_from_disk(); + + /* Test that the expected catalogs are there. */ + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_AGENT_47)); /* New in the modified file. */ + + /* Test that catalogs removed from modified CDF are gone. */ + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_EQ(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + + /* On-disk changed catalogs should have overridden in-memory not-changed ones. */ + const AssetCatalog *ruzena_face = loaded_service.find_catalog(UUID_POSES_RUZENA_FACE); + EXPECT_EQ("character/Ružena/poselib/gezicht", ruzena_face->path.str()); +} + +TEST_F(AssetCatalogTest, refresh_catalogs_with_modification) +{ + const CatalogFilePath cdf_dir = create_temp_path(); + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath modified_cdf_file = asset_library_root_ + "/catalog_reload_test.cats.txt"; + const CatalogFilePath temp_cdf_file = cdf_dir + "blender_assets.cats.txt"; + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), temp_cdf_file.c_str())); + + /* Load the unmodified, original CDF. */ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(cdf_dir); + + /* === Perform changes that should be handled gracefully by the reloading code: */ + + /* 1. Delete a subtree of catalogs. */ + service.prune_catalogs_by_id(UUID_POSES_RUZENA); + /* 2. Rename a catalog. */ + service.tag_has_unsaved_changes(service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + service.update_catalog_path(UUID_POSES_ELLIE_TRAILING_SLASH, "character/Ellie/test-value"); + + /* Copy a modified file, to mimic a situation where someone changed the + * CDF after we loaded it. */ + ASSERT_EQ(0, BLI_copy(modified_cdf_file.c_str(), temp_cdf_file.c_str())); + + AssetCatalog *const ellie_whitespace_before_reload = service.find_catalog( + UUID_POSES_ELLIE_WHITESPACE); + + /* This should merge the on-disk file with our catalogs. */ + service.reload_catalogs(); + + /* === Test that the expected catalogs are there. */ + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + + /* === Test changes made to the CDF: */ + + /* Removed from the file. */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_ELLIE_BACKSLASHES)); + /* Added to the file. */ + EXPECT_NE(nullptr, service.find_catalog(UUID_AGENT_47)); + /* Path modified in file. */ + AssetCatalog *ellie_whitespace_after_reload = service.find_catalog(UUID_POSES_ELLIE_WHITESPACE); + EXPECT_EQ(AssetCatalogPath("whitespace from file"), ellie_whitespace_after_reload->path); + EXPECT_NE(ellie_whitespace_after_reload, ellie_whitespace_before_reload); + /* Simple name modified in file. */ + EXPECT_EQ(std::string("Hah simple name after all"), + service.find_catalog(UUID_WITHOUT_SIMPLENAME)->simple_name); + + /* === Test persistence of in-memory changes: */ + + /* This part of the tree we deleted, but still existed in the CDF. They should remain deleted + * after reloading: */ + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_EQ(nullptr, service.find_catalog(UUID_POSES_RUZENA_FACE)); + + /* This catalog had its path changed in the test and in the CDF. The change from the test (i.e. + * the in-memory, yet-unsaved change) should persist. */ + EXPECT_EQ(AssetCatalogPath("character/Ellie/test-value"), + service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)->path); + + /* Overwrite the modified file. This should merge the on-disk file with our catalogs, and clear + * the "has_unsaved_changes" flags. */ + service.write_to_disk(cdf_dir + "phony.blend"); + + EXPECT_FALSE(service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)->flags.has_unsaved_changes) + << "The catalogs whose path we changed should now be saved"; + EXPECT_TRUE(service.get_deleted_catalogs().is_empty()) + << "Deleted catalogs should not be remembered after saving."; +} + +TEST_F(AssetCatalogTest, backups) +{ + const CatalogFilePath cdf_dir = create_temp_path(); + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + const CatalogFilePath writable_cdf_file = cdf_dir + "/blender_assets.cats.txt"; + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); + + /* Read a CDF, modify, and write it. */ + TestableAssetCatalogService service(cdf_dir); + service.load_from_disk(); + service.delete_catalog_by_id_soft(UUID_POSES_ELLIE); + service.write_to_disk(cdf_dir + "phony.blend"); + + const CatalogFilePath backup_path = writable_cdf_file + "~"; + ASSERT_TRUE(BLI_is_file(backup_path.c_str())); + + AssetCatalogService loaded_service; + loaded_service.load_from_disk(backup_path); + + /* Test that the expected catalogs are there, including the deleted one. + * This is the backup, after all. */ + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(nullptr, loaded_service.find_catalog(UUID_POSES_RUZENA_FACE)); +} + +TEST_F(AssetCatalogTest, order_by_path) +{ + const bUUID cat2_uuid("22222222-b847-44d9-bdca-ff04db1c24f5"); + const bUUID cat4_uuid("11111111-b847-44d9-bdca-ff04db1c24f5"); /* Sorts earlier than above. */ + const AssetCatalog cat1(BLI_uuid_generate_random(), "simple/path/child", ""); + const AssetCatalog cat2(cat2_uuid, "simple/path", ""); + const AssetCatalog cat3(BLI_uuid_generate_random(), "complex/path/...or/is/it?", ""); + const AssetCatalog cat4( + cat4_uuid, "simple/path", "different ID, same path"); /* should be kept */ + const AssetCatalog cat5(cat4_uuid, "simple/path", "same ID, same path"); /* disappears */ + + AssetCatalogOrderedSet by_path; + by_path.insert(&cat1); + by_path.insert(&cat2); + by_path.insert(&cat3); + by_path.insert(&cat4); + by_path.insert(&cat5); + + AssetCatalogOrderedSet::const_iterator set_iter = by_path.begin(); + + EXPECT_EQ(1, by_path.count(&cat1)); + EXPECT_EQ(1, by_path.count(&cat2)); + EXPECT_EQ(1, by_path.count(&cat3)); + EXPECT_EQ(1, by_path.count(&cat4)); + ASSERT_EQ(4, by_path.size()) << "Expecting cat5 to not be stored in the set, as it duplicates " + "an already-existing path + UUID"; + + EXPECT_EQ(cat3.catalog_id, (*(set_iter++))->catalog_id); /* complex/path */ + EXPECT_EQ(cat4.catalog_id, (*(set_iter++))->catalog_id); /* simple/path with 111.. ID */ + EXPECT_EQ(cat2.catalog_id, (*(set_iter++))->catalog_id); /* simple/path with 222.. ID */ + EXPECT_EQ(cat1.catalog_id, (*(set_iter++))->catalog_id); /* simple/path/child */ + + if (set_iter != by_path.end()) { + const AssetCatalog *next_cat = *set_iter; + FAIL() << "Did not expect more items in the set, had at least " << next_cat->catalog_id << ":" + << next_cat->path; + } +} + +TEST_F(AssetCatalogTest, order_by_path_and_first_seen) +{ + AssetCatalogService service; + service.load_from_disk(asset_library_root_); + + const bUUID first_seen_uuid("3d451c87-27d1-40fd-87fc-f4c9e829c848"); + const bUUID first_sorted_uuid("00000000-0000-0000-0000-000000000001"); + const bUUID last_sorted_uuid("ffffffff-ffff-ffff-ffff-ffffffffffff"); + + AssetCatalog first_seen_cat(first_seen_uuid, "simple/path/child", ""); + const AssetCatalog first_sorted_cat(first_sorted_uuid, "simple/path/child", ""); + const AssetCatalog last_sorted_cat(last_sorted_uuid, "simple/path/child", ""); + + /* Mimic that this catalog was first-seen when loading from disk. */ + first_seen_cat.flags.is_first_loaded = true; + + /* Just an assertion of the defaults; this is more to avoid confusing errors later on than an + * actual test of these defaults. */ + ASSERT_FALSE(first_sorted_cat.flags.is_first_loaded); + ASSERT_FALSE(last_sorted_cat.flags.is_first_loaded); + + AssetCatalogOrderedSet by_path; + by_path.insert(&first_seen_cat); + by_path.insert(&first_sorted_cat); + by_path.insert(&last_sorted_cat); + + AssetCatalogOrderedSet::const_iterator set_iter = by_path.begin(); + + EXPECT_EQ(1, by_path.count(&first_seen_cat)); + EXPECT_EQ(1, by_path.count(&first_sorted_cat)); + EXPECT_EQ(1, by_path.count(&last_sorted_cat)); + ASSERT_EQ(3, by_path.size()); + + EXPECT_EQ(first_seen_uuid, (*(set_iter++))->catalog_id); + EXPECT_EQ(first_sorted_uuid, (*(set_iter++))->catalog_id); + EXPECT_EQ(last_sorted_uuid, (*(set_iter++))->catalog_id); + + if (set_iter != by_path.end()) { + const AssetCatalog *next_cat = *set_iter; + FAIL() << "Did not expect more items in the set, had at least " << next_cat->catalog_id << ":" + << next_cat->path; + } +} + +TEST_F(AssetCatalogTest, create_missing_catalogs) +{ + TestableAssetCatalogService new_service; + new_service.create_catalog("path/with/missing/parents"); + + EXPECT_EQ(nullptr, new_service.find_catalog_by_path("path/with/missing")) + << "Missing parents should not be immediately created."; + EXPECT_EQ(nullptr, new_service.find_catalog_by_path("")) << "Empty path should never be valid"; + + new_service.create_missing_catalogs(); + + EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with/missing")); + EXPECT_NE(nullptr, new_service.find_catalog_by_path("path/with")); + EXPECT_NE(nullptr, new_service.find_catalog_by_path("path")); + EXPECT_EQ(nullptr, new_service.find_catalog_by_path("")) + << "Empty path should never be valid, even when after missing catalogs"; +} + +TEST_F(AssetCatalogTest, create_missing_catalogs_after_loading) +{ + TestableAssetCatalogService loaded_service(asset_library_root_); + loaded_service.load_from_disk(); + + const AssetCatalog *cat_char = loaded_service.find_catalog_by_path("character"); + const AssetCatalog *cat_ellie = loaded_service.find_catalog_by_path("character/Ellie"); + const AssetCatalog *cat_ruzena = loaded_service.find_catalog_by_path("character/Ružena"); + ASSERT_NE(nullptr, cat_char) << "Missing parents should be created immediately after loading."; + ASSERT_NE(nullptr, cat_ellie) << "Missing parents should be created immediately after loading."; + ASSERT_NE(nullptr, cat_ruzena) << "Missing parents should be created immediately after loading."; + + EXPECT_TRUE(cat_char->flags.has_unsaved_changes) + << "Missing parents should be marked as having changes."; + EXPECT_TRUE(cat_ellie->flags.has_unsaved_changes) + << "Missing parents should be marked as having changes."; + EXPECT_TRUE(cat_ruzena->flags.has_unsaved_changes) + << "Missing parents should be marked as having changes."; + + AssetCatalogDefinitionFile *cdf = loaded_service.get_catalog_definition_file(); + ASSERT_NE(nullptr, cdf); + EXPECT_TRUE(cdf->contains(cat_char->catalog_id)) << "Missing parents should be saved to a CDF."; + EXPECT_TRUE(cdf->contains(cat_ellie->catalog_id)) << "Missing parents should be saved to a CDF."; + EXPECT_TRUE(cdf->contains(cat_ruzena->catalog_id)) + << "Missing parents should be saved to a CDF."; + + /* Check that each missing parent is only created once. The CDF contains multiple paths that + * could trigger the creation of missing parents, so this test makes sense. */ + EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character")); + EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ellie")); + EXPECT_EQ(1, loaded_service.count_catalogs_with_path("character/Ružena")); +} + +TEST_F(AssetCatalogTest, create_catalog_filter) +{ + AssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + /* Alias for the same catalog as the main one. */ + AssetCatalog *alias_ruzena = service.create_catalog("character/Ružena/poselib"); + /* Alias for a sub-catalog. */ + AssetCatalog *alias_ruzena_hand = service.create_catalog("character/Ružena/poselib/hand"); + + AssetCatalogFilter filter = service.create_catalog_filter(UUID_POSES_RUZENA); + + /* Positive test for loaded-from-disk catalogs. */ + EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA)) + << "Main catalog should be included in the filter."; + EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_HAND)) + << "Sub-catalog should be included in the filter."; + EXPECT_TRUE(filter.contains(UUID_POSES_RUZENA_FACE)) + << "Sub-catalog should be included in the filter."; + + /* Positive test for newly-created catalogs. */ + EXPECT_TRUE(filter.contains(alias_ruzena->catalog_id)) + << "Alias of main catalog should be included in the filter."; + EXPECT_TRUE(filter.contains(alias_ruzena_hand->catalog_id)) + << "Alias of sub-catalog should be included in the filter."; + + /* Negative test for unrelated catalogs. */ + EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included."; + EXPECT_FALSE(filter.contains(UUID_ID_WITHOUT_PATH)); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_WHITESPACE)); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE_TRAILING_SLASH)); + EXPECT_FALSE(filter.contains(UUID_WITHOUT_SIMPLENAME)); +} + +TEST_F(AssetCatalogTest, create_catalog_filter_for_unknown_uuid) +{ + AssetCatalogService service; + const bUUID unknown_uuid = BLI_uuid_generate_random(); + + AssetCatalogFilter filter = service.create_catalog_filter(unknown_uuid); + EXPECT_TRUE(filter.contains(unknown_uuid)); + + EXPECT_FALSE(filter.contains(BLI_uuid_nil())) << "Nil catalog should not be included."; + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); +} + +TEST_F(AssetCatalogTest, create_catalog_filter_for_unassigned_assets) +{ + AssetCatalogService service; + + AssetCatalogFilter filter = service.create_catalog_filter(BLI_uuid_nil()); + EXPECT_TRUE(filter.contains(BLI_uuid_nil())); + EXPECT_FALSE(filter.contains(UUID_POSES_ELLIE)); +} + +TEST_F(AssetCatalogTest, cat_collection_deep_copy__empty) +{ + const AssetCatalogCollection empty; + auto copy = empty.deep_copy(); + EXPECT_NE(&empty, copy.get()); +} + +class TestableAssetCatalogCollection : public AssetCatalogCollection { + public: + OwningAssetCatalogMap &get_catalogs() + { + return catalogs_; + } + OwningAssetCatalogMap &get_deleted_catalogs() + { + return deleted_catalogs_; + } + AssetCatalogDefinitionFile *get_catalog_definition_file() + { + return catalog_definition_file_.get(); + } + AssetCatalogDefinitionFile *allocate_catalog_definition_file() + { + catalog_definition_file_ = std::make_unique(); + return get_catalog_definition_file(); + } +}; + +TEST_F(AssetCatalogTest, cat_collection_deep_copy__nonempty_nocdf) +{ + TestableAssetCatalogCollection catcoll; + auto cat1 = std::make_unique(UUID_POSES_RUZENA, "poses/Henrik", ""); + auto cat2 = std::make_unique(UUID_POSES_RUZENA_FACE, "poses/Henrik/face", ""); + auto cat3 = std::make_unique(UUID_POSES_RUZENA_HAND, "poses/Henrik/hands", ""); + cat3->flags.is_deleted = true; + + AssetCatalog *cat1_ptr = cat1.get(); + AssetCatalog *cat3_ptr = cat3.get(); + + catcoll.get_catalogs().add_new(cat1->catalog_id, std::move(cat1)); + catcoll.get_catalogs().add_new(cat2->catalog_id, std::move(cat2)); + catcoll.get_deleted_catalogs().add_new(cat3->catalog_id, std::move(cat3)); + + auto copy = catcoll.deep_copy(); + EXPECT_NE(&catcoll, copy.get()); + + TestableAssetCatalogCollection *testcopy = reinterpret_cast( + copy.get()); + + /* Test catalogs & deleted catalogs. */ + EXPECT_EQ(2, testcopy->get_catalogs().size()); + EXPECT_EQ(1, testcopy->get_deleted_catalogs().size()); + + ASSERT_TRUE(testcopy->get_catalogs().contains(UUID_POSES_RUZENA)); + ASSERT_TRUE(testcopy->get_catalogs().contains(UUID_POSES_RUZENA_FACE)); + ASSERT_TRUE(testcopy->get_deleted_catalogs().contains(UUID_POSES_RUZENA_HAND)); + + EXPECT_NE(nullptr, testcopy->get_catalogs().lookup(UUID_POSES_RUZENA)); + EXPECT_NE(cat1_ptr, testcopy->get_catalogs().lookup(UUID_POSES_RUZENA).get()) + << "AssetCatalogs should be actual copies."; + + EXPECT_NE(nullptr, testcopy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND)); + EXPECT_NE(cat3_ptr, testcopy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND).get()) + << "AssetCatalogs should be actual copies."; +} + +class TestableAssetCatalogDefinitionFile : public AssetCatalogDefinitionFile { + public: + Map get_catalogs() + { + return catalogs_; + } +}; + +TEST_F(AssetCatalogTest, cat_collection_deep_copy__nonempty_cdf) +{ + TestableAssetCatalogCollection catcoll; + auto cat1 = std::make_unique(UUID_POSES_RUZENA, "poses/Henrik", ""); + auto cat2 = std::make_unique(UUID_POSES_RUZENA_FACE, "poses/Henrik/face", ""); + auto cat3 = std::make_unique(UUID_POSES_RUZENA_HAND, "poses/Henrik/hands", ""); + cat3->flags.is_deleted = true; + + AssetCatalog *cat1_ptr = cat1.get(); + AssetCatalog *cat2_ptr = cat2.get(); + AssetCatalog *cat3_ptr = cat3.get(); + + catcoll.get_catalogs().add_new(cat1->catalog_id, std::move(cat1)); + catcoll.get_catalogs().add_new(cat2->catalog_id, std::move(cat2)); + catcoll.get_deleted_catalogs().add_new(cat3->catalog_id, std::move(cat3)); + + AssetCatalogDefinitionFile *cdf = catcoll.allocate_catalog_definition_file(); + cdf->file_path = "path/to/somewhere.cats.txt"; + cdf->add_new(cat1_ptr); + cdf->add_new(cat2_ptr); + cdf->add_new(cat3_ptr); + + /* Test CDF remapping. */ + auto copy = catcoll.deep_copy(); + TestableAssetCatalogCollection *testable_copy = static_cast( + copy.get()); + + TestableAssetCatalogDefinitionFile *cdf_copy = static_cast( + testable_copy->get_catalog_definition_file()); + EXPECT_EQ(testable_copy->get_catalogs().lookup(UUID_POSES_RUZENA).get(), + cdf_copy->get_catalogs().lookup(UUID_POSES_RUZENA)) + << "AssetCatalog pointers should have been remapped to the copy."; + + EXPECT_EQ(testable_copy->get_deleted_catalogs().lookup(UUID_POSES_RUZENA_HAND).get(), + cdf_copy->get_catalogs().lookup(UUID_POSES_RUZENA_HAND)) + << "Deleted AssetCatalog pointers should have been remapped to the copy."; +} + +TEST_F(AssetCatalogTest, undo_redo_one_step) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + EXPECT_FALSE(service.is_undo_possbile()); + EXPECT_FALSE(service.is_redo_possbile()); + + service.create_catalog("some/catalog/path"); + EXPECT_FALSE(service.is_undo_possbile()) + << "Undo steps should be created explicitly, and not after creating any catalog."; + + service.undo_push(); + const bUUID other_catalog_id = service.create_catalog("other/catalog/path")->catalog_id; + EXPECT_TRUE(service.is_undo_possbile()) + << "Undo should be possible after creating an undo snapshot."; + + /* Undo the creation of the catalog. */ + service.undo(); + EXPECT_FALSE(service.is_undo_possbile()) + << "Undoing the only stored step should make it impossible to undo further."; + EXPECT_TRUE(service.is_redo_possbile()) << "Undoing a step should make redo possible."; + EXPECT_EQ(nullptr, service.find_catalog_by_path("other/catalog/path")) + << "Undone catalog should not exist after undo."; + EXPECT_NE(nullptr, service.find_catalog_by_path("some/catalog/path")) + << "First catalog should still exist after undo."; + EXPECT_FALSE(service.get_catalog_definition_file()->contains(other_catalog_id)) + << "The CDF should also not contain the undone catalog."; + + /* Redo the creation of the catalog. */ + service.redo(); + EXPECT_TRUE(service.is_undo_possbile()) + << "Undoing and then redoing a step should make it possible to undo again."; + EXPECT_FALSE(service.is_redo_possbile()) + << "Undoing and then redoing a step should make redo impossible."; + EXPECT_NE(nullptr, service.find_catalog_by_path("other/catalog/path")) + << "Redone catalog should exist after redo."; + EXPECT_NE(nullptr, service.find_catalog_by_path("some/catalog/path")) + << "First catalog should still exist after redo."; + EXPECT_TRUE(service.get_catalog_definition_file()->contains(other_catalog_id)) + << "The CDF should contain the redone catalog."; +} + +TEST_F(AssetCatalogTest, undo_redo_more_complex) +{ + TestableAssetCatalogService service(asset_library_root_); + service.load_from_disk(); + + service.undo_push(); + service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->simple_name = "Edited simple name"; + + service.undo_push(); + service.find_catalog(UUID_POSES_ELLIE)->path = "poselib/EllieWithEditedPath"; + + service.undo(); + service.undo(); + + service.undo_push(); + service.find_catalog(UUID_POSES_ELLIE)->simple_name = "Ellie Simple"; + + EXPECT_FALSE(service.is_redo_possbile()) + << "After storing an undo snapshot, the redo buffer should be empty."; + EXPECT_TRUE(service.is_undo_possbile()) + << "After storing an undo snapshot, undoing should be possible"; + + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE)->simple_name, "Ellie Simple"); /* Not undone. */ + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE_WHITESPACE)->simple_name, + "POSES_ELLIE WHITESPACE"); /* Undone. */ + EXPECT_EQ(service.find_catalog(UUID_POSES_ELLIE)->path, "character/Ellie/poselib"); /* Undone. */ +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_library.cc b/source/blender/blenkernel/intern/asset_library.cc new file mode 100644 index 00000000000..68e43852a21 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library.cc @@ -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 + */ + +#include + +#include "BKE_asset_library.hh" +#include "BKE_main.h" +#include "BKE_preferences.h" + +#include "BLI_path_util.h" + +#include "DNA_asset_types.h" +#include "DNA_userdef_types.h" + +#include "asset_library_service.hh" + +bool blender::bke::AssetLibrary::save_catalogs_when_file_is_saved = true; + +/** + * Loading an asset library at this point only means loading the catalogs. Later on this should + * invoke reading of asset representations too. + */ +struct AssetLibrary *BKE_asset_library_load(const char *library_path) +{ + blender::bke::AssetLibraryService *service = blender::bke::AssetLibraryService::get(); + blender::bke::AssetLibrary *lib; + if (library_path == nullptr || library_path[0] == '\0') { + lib = service->get_asset_library_current_file(); + } + else { + lib = service->get_asset_library_on_disk(library_path); + } + return reinterpret_cast(lib); +} + +bool BKE_asset_library_has_any_unsaved_catalogs() +{ + blender::bke::AssetLibraryService *service = blender::bke::AssetLibraryService::get(); + return service->has_any_unsaved_catalogs(); +} + +bool BKE_asset_library_find_suitable_root_path_from_path(const char *input_path, + char *r_library_path) +{ + if (bUserAssetLibrary *preferences_lib = BKE_preferences_asset_library_containing_path( + &U, input_path)) { + BLI_strncpy(r_library_path, preferences_lib->path, FILE_MAXDIR); + return true; + } + + BLI_split_dir_part(input_path, r_library_path, FILE_MAXDIR); + return r_library_path[0] != '\0'; +} + +bool BKE_asset_library_find_suitable_root_path_from_main(const Main *bmain, char *r_library_path) +{ + return BKE_asset_library_find_suitable_root_path_from_path(bmain->name, r_library_path); +} + +blender::bke::AssetCatalogService *BKE_asset_library_get_catalog_service( + const ::AssetLibrary *library_c) +{ + if (library_c == nullptr) { + return nullptr; + } + + const blender::bke::AssetLibrary &library = reinterpret_cast( + *library_c); + return library.catalog_service.get(); +} + +blender::bke::AssetCatalogTree *BKE_asset_library_get_catalog_tree(const ::AssetLibrary *library) +{ + blender::bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service( + library); + if (catalog_service == nullptr) { + return nullptr; + } + + return catalog_service->get_catalog_tree(); +} + +void BKE_asset_library_refresh_catalog_simplename(struct AssetLibrary *asset_library, + struct AssetMetaData *asset_data) +{ + blender::bke::AssetLibrary *lib = reinterpret_cast(asset_library); + lib->refresh_catalog_simplename(asset_data); +} + +namespace blender::bke { + +AssetLibrary::AssetLibrary() : catalog_service(std::make_unique()) +{ +} + +AssetLibrary::~AssetLibrary() +{ + if (on_save_callback_store_.func) { + on_blend_save_handler_unregister(); + } +} + +void AssetLibrary::load(StringRefNull library_root_directory) +{ + auto catalog_service = std::make_unique(library_root_directory); + catalog_service->load_from_disk(); + this->catalog_service = std::move(catalog_service); +} + +void AssetLibrary::refresh() +{ + this->catalog_service->reload_catalogs(); +} + +namespace { +void asset_library_on_save_post(struct Main *main, + struct PointerRNA **pointers, + const int num_pointers, + void *arg) +{ + AssetLibrary *asset_lib = static_cast(arg); + asset_lib->on_blend_save_post(main, pointers, num_pointers); +} + +} // namespace + +void AssetLibrary::on_blend_save_handler_register() +{ + /* The callback system doesn't own `on_save_callback_store_`. */ + on_save_callback_store_.alloc = false; + + on_save_callback_store_.func = asset_library_on_save_post; + on_save_callback_store_.arg = this; + + BKE_callback_add(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST); +} + +void AssetLibrary::on_blend_save_handler_unregister() +{ + BKE_callback_remove(&on_save_callback_store_, BKE_CB_EVT_SAVE_POST); + on_save_callback_store_.func = nullptr; + on_save_callback_store_.arg = nullptr; +} + +void AssetLibrary::on_blend_save_post(struct Main *main, + struct PointerRNA ** /*pointers*/, + const int /*num_pointers*/) +{ + if (this->catalog_service == nullptr) { + return; + } + + if (save_catalogs_when_file_is_saved) { + this->catalog_service->write_to_disk(main->name); + } +} + +void AssetLibrary::refresh_catalog_simplename(struct AssetMetaData *asset_data) +{ + if (BLI_uuid_is_nil(asset_data->catalog_id)) { + asset_data->catalog_simple_name[0] = '\0'; + return; + } + const AssetCatalog *catalog = this->catalog_service->find_catalog(asset_data->catalog_id); + if (catalog == nullptr) { + /* No-op if the catalog cannot be found. This could be the kind of "the catalog definition file + * is corrupt/lost" scenario that the simple name is meant to help recover from. */ + return; + } + STRNCPY(asset_data->catalog_simple_name, catalog->simple_name.c_str()); +} +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_service.cc b/source/blender/blenkernel/intern/asset_library_service.cc new file mode 100644 index 00000000000..d202d6462cf --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_service.cc @@ -0,0 +1,164 @@ +/* + * 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 + */ + +#include "asset_library_service.hh" + +#include "BKE_blender.h" + +#include "BLI_fileops.h" /* For PATH_MAX (at least on Windows). */ +#include "BLI_path_util.h" +#include "BLI_string_ref.hh" + +#include "CLG_log.h" + +static CLG_LogRef LOG = {"bke.asset_service"}; + +namespace blender::bke { + +std::unique_ptr AssetLibraryService::instance_; +bool AssetLibraryService::atexit_handler_registered_ = false; + +AssetLibraryService *AssetLibraryService::get() +{ + if (!instance_) { + allocate_service_instance(); + } + return instance_.get(); +} + +void AssetLibraryService::destroy() +{ + if (!instance_) { + return; + } + instance_->app_handler_unregister(); + instance_.reset(); +} + +namespace { +std::string normalize_directory_path(StringRefNull directory) +{ + + char dir_normalized[PATH_MAX]; + STRNCPY(dir_normalized, directory.c_str()); + BLI_path_normalize_dir(nullptr, dir_normalized); + return std::string(dir_normalized); +} +} // namespace + +AssetLibrary *AssetLibraryService::get_asset_library_on_disk(StringRefNull top_level_directory) +{ + BLI_assert_msg(!top_level_directory.is_empty(), + "top level directory must be given for on-disk asset library"); + + std::string top_dir_trailing_slash = normalize_directory_path(top_level_directory); + + AssetLibraryPtr *lib_uptr_ptr = on_disk_libraries_.lookup_ptr(top_dir_trailing_slash); + if (lib_uptr_ptr != nullptr) { + CLOG_INFO(&LOG, 2, "get \"%s\" (cached)", top_dir_trailing_slash.c_str()); + AssetLibrary *lib = lib_uptr_ptr->get(); + lib->refresh(); + return lib; + } + + AssetLibraryPtr lib_uptr = std::make_unique(); + AssetLibrary *lib = lib_uptr.get(); + + lib->on_blend_save_handler_register(); + lib->load(top_dir_trailing_slash); + + on_disk_libraries_.add_new(top_dir_trailing_slash, std::move(lib_uptr)); + CLOG_INFO(&LOG, 2, "get \"%s\" (loaded)", top_dir_trailing_slash.c_str()); + return lib; +} + +AssetLibrary *AssetLibraryService::get_asset_library_current_file() +{ + if (current_file_library_) { + CLOG_INFO(&LOG, 2, "get current file lib (cached)"); + } + else { + CLOG_INFO(&LOG, 2, "get current file lib (loaded)"); + current_file_library_ = std::make_unique(); + current_file_library_->on_blend_save_handler_register(); + } + + AssetLibrary *lib = current_file_library_.get(); + return lib; +} + +void AssetLibraryService::allocate_service_instance() +{ + instance_ = std::make_unique(); + instance_->app_handler_register(); + + if (!atexit_handler_registered_) { + /* Ensure the instance gets freed before Blender's memory leak detector runs. */ + BKE_blender_atexit_register([](void * /*user_data*/) { AssetLibraryService::destroy(); }, + nullptr); + atexit_handler_registered_ = true; + } +} + +static void on_blendfile_load(struct Main * /*bMain*/, + struct PointerRNA ** /*pointers*/, + const int /*num_pointers*/, + void * /*arg*/) +{ + AssetLibraryService::destroy(); +} + +/** + * Ensure the AssetLibraryService instance is destroyed before a new blend file is loaded. + * This makes memory management simple, and ensures a fresh start for every blend file. */ +void AssetLibraryService::app_handler_register() +{ + /* The callback system doesn't own `on_load_callback_store_`. */ + on_load_callback_store_.alloc = false; + + on_load_callback_store_.func = &on_blendfile_load; + on_load_callback_store_.arg = this; + + BKE_callback_add(&on_load_callback_store_, BKE_CB_EVT_LOAD_PRE); +} + +void AssetLibraryService::app_handler_unregister() +{ + BKE_callback_remove(&on_load_callback_store_, BKE_CB_EVT_LOAD_PRE); + on_load_callback_store_.func = nullptr; + on_load_callback_store_.arg = nullptr; +} + +bool AssetLibraryService::has_any_unsaved_catalogs() const +{ + if (current_file_library_ && current_file_library_->catalog_service->has_unsaved_changes()) { + return true; + } + + for (const auto &asset_lib_uptr : on_disk_libraries_.values()) { + if (asset_lib_uptr->catalog_service->has_unsaved_changes()) { + return true; + } + } + + return false; +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_service.hh b/source/blender/blenkernel/intern/asset_library_service.hh new file mode 100644 index 00000000000..03df706bc42 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_service.hh @@ -0,0 +1,93 @@ +/* + * 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 + */ + +#pragma once + +#ifndef __cplusplus +# error This is a C++-only header file. +#endif + +#include "BKE_asset_library.hh" + +#include "BLI_map.hh" + +#include + +namespace blender::bke { + +/** + * Global singleton-ish that provides access to individual #AssetLibrary instances. + * + * Whenever a blend file is loaded, the existing instance of AssetLibraryService is destructed, and + * a new one is created -- hence the "singleton-ish". This ensures only information about relevant + * asset libraries is loaded. + * + * \note How Asset libraries are identified may change in the future. + * For now they are assumed to be: + * - on disk (identified by the absolute directory), or + * - the "current file" library (which is in memory but could have catalogs + * loaded from a file on disk). + */ +class AssetLibraryService { + public: + using AssetLibraryPtr = std::unique_ptr; + + AssetLibraryService() = default; + ~AssetLibraryService() = default; + + /** Return the AssetLibraryService singleton, allocating it if necessary. */ + static AssetLibraryService *get(); + + /** Destroy the AssetLibraryService singleton. It will be reallocated by #get() if necessary. */ + static void destroy(); + + /** + * Get the given asset library. Opens it (i.e. creates a new AssetLibrary instance) if necessary. + */ + AssetLibrary *get_asset_library_on_disk(StringRefNull top_level_directory); + + /** Get the "Current File" asset library. */ + AssetLibrary *get_asset_library_current_file(); + + /** Returns whether there are any known asset libraries with unsaved catalog edits. */ + bool has_any_unsaved_catalogs() const; + + protected: + static std::unique_ptr instance_; + + /* Mapping absolute path of the library's top-level directory to the AssetLibrary instance. */ + Map on_disk_libraries_; + AssetLibraryPtr current_file_library_; + + /* Handlers for managing the life cycle of the AssetLibraryService instance. */ + bCallbackFuncStore on_load_callback_store_; + static bool atexit_handler_registered_; + + /** Allocate a new instance of the service and assign it to `instance_`. */ + static void allocate_service_instance(); + + /** + * Ensure the AssetLibraryService instance is destroyed before a new blend file is loaded. + * This makes memory management simple, and ensures a fresh start for every blend file. */ + void app_handler_register(); + void app_handler_unregister(); +}; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/asset_library_service_test.cc b/source/blender/blenkernel/intern/asset_library_service_test.cc new file mode 100644 index 00000000000..ee910cab945 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_service_test.cc @@ -0,0 +1,214 @@ +/* + * 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) 2020 Blender Foundation + * All rights reserved. + */ + +#include "asset_library_service.hh" + +#include "BLI_fileops.h" /* For PATH_MAX (at least on Windows). */ +#include "BLI_path_util.h" + +#include "BKE_appdir.h" +#include "BKE_callbacks.h" + +#include "CLG_log.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + +class AssetLibraryServiceTest : public testing::Test { + public: + CatalogFilePath asset_library_root_; + CatalogFilePath temp_library_path_; + + static void SetUpTestSuite() + { + CLG_init(); + BKE_callback_global_init(); + } + static void TearDownTestSuite() + { + CLG_exit(); + BKE_callback_global_finalize(); + } + + void SetUp() override + { + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + asset_library_root_ = test_files_dir + "/" + "asset_library"; + temp_library_path_ = ""; + } + + void TearDown() override + { + AssetLibraryService::destroy(); + + if (!temp_library_path_.empty()) { + BLI_delete(temp_library_path_.c_str(), true, true); + temp_library_path_ = ""; + } + } + + /* Register a temporary path, which will be removed at the end of the test. + * The returned path ends in a slash. */ + CatalogFilePath use_temp_path() + { + BKE_tempdir_init(""); + const CatalogFilePath tempdir = BKE_tempdir_session(); + temp_library_path_ = tempdir + "test-temporary-path/"; + return temp_library_path_; + } + + CatalogFilePath create_temp_path() + { + CatalogFilePath path = use_temp_path(); + BLI_dir_create_recursive(path.c_str()); + return path; + } +}; + +TEST_F(AssetLibraryServiceTest, get_destroy) +{ + AssetLibraryService *const service = AssetLibraryService::get(); + EXPECT_EQ(service, AssetLibraryService::get()) + << "Calling twice without destroying in between should return the same instance."; + + /* This should not crash. */ + AssetLibraryService::destroy(); + AssetLibraryService::destroy(); + + /* Note: there used to be a test for the opposite here, that after a call to + * AssetLibraryService::destroy() the above calls should return freshly allocated objects. This + * cannot be reliably tested by just pointer comparison, though. */ +} + +TEST_F(AssetLibraryServiceTest, library_pointers) +{ + AssetLibraryService *service = AssetLibraryService::get(); + AssetLibrary *const lib = service->get_asset_library_on_disk(asset_library_root_); + AssetLibrary *const curfile_lib = service->get_asset_library_current_file(); + + EXPECT_EQ(lib, service->get_asset_library_on_disk(asset_library_root_)) + << "Calling twice without destroying in between should return the same instance."; + EXPECT_EQ(curfile_lib, service->get_asset_library_current_file()) + << "Calling twice without destroying in between should return the same instance."; + + /* Note: there used to be a test for the opposite here, that after a call to + * AssetLibraryService::destroy() the above calls should return freshly allocated objects. This + * cannot be reliably tested by just pointer comparison, though. */ +} + +TEST_F(AssetLibraryServiceTest, library_path_trailing_slashes) +{ + AssetLibraryService *service = AssetLibraryService::get(); + + char asset_lib_no_slash[PATH_MAX]; + char asset_lib_with_slash[PATH_MAX]; + STRNCPY(asset_lib_no_slash, asset_library_root_.c_str()); + STRNCPY(asset_lib_with_slash, asset_library_root_.c_str()); + + /* Ensure #asset_lib_no_slash has no trailing slash, regardless of what was passed on the CLI to + * the unit test. */ + while (strlen(asset_lib_no_slash) && + ELEM(asset_lib_no_slash[strlen(asset_lib_no_slash) - 1], SEP, ALTSEP)) { + asset_lib_no_slash[strlen(asset_lib_no_slash) - 1] = '\0'; + } + + BLI_path_slash_ensure(asset_lib_with_slash); + + AssetLibrary *const lib_no_slash = service->get_asset_library_on_disk(asset_lib_no_slash); + + EXPECT_EQ(lib_no_slash, service->get_asset_library_on_disk(asset_lib_with_slash)) + << "With or without trailing slash shouldn't matter."; +} + +TEST_F(AssetLibraryServiceTest, catalogs_loaded) +{ + AssetLibraryService *const service = AssetLibraryService::get(); + AssetLibrary *const lib = service->get_asset_library_on_disk(asset_library_root_); + AssetCatalogService *const cat_service = lib->catalog_service.get(); + + const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + EXPECT_NE(nullptr, cat_service->find_catalog(UUID_POSES_ELLIE)) + << "Catalogs should be loaded after getting an asset library from disk."; +} + +TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs) +{ + AssetLibraryService *const service = AssetLibraryService::get(); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Empty AssetLibraryService should have no unsaved catalogs"; + + AssetLibrary *const lib = service->get_asset_library_on_disk(asset_library_root_); + AssetCatalogService *const cat_service = lib->catalog_service.get(); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Unchanged AssetLibrary should have no unsaved catalogs"; + + const bUUID UUID_POSES_ELLIE("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + cat_service->prune_catalogs_by_id(UUID_POSES_ELLIE); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Deletion of catalogs via AssetCatalogService should not automatically tag as 'unsaved " + "changes'."; + + const bUUID UUID_POSES_RUZENA("79a4f887-ab60-4bd4-94da-d572e27d6aed"); + AssetCatalog *cat = cat_service->find_catalog(UUID_POSES_RUZENA); + ASSERT_NE(nullptr, cat) << "Catalog " << UUID_POSES_RUZENA << " should be known"; + + cat_service->tag_has_unsaved_changes(cat); + EXPECT_TRUE(service->has_any_unsaved_catalogs()) + << "Tagging as having unsaved changes of a single catalog service should result in unsaved " + "changes being reported."; + EXPECT_TRUE(cat->flags.has_unsaved_changes); +} + +TEST_F(AssetLibraryServiceTest, has_any_unsaved_catalogs_after_write) +{ + const CatalogFilePath writable_dir = create_temp_path(); /* Has trailing slash. */ + const CatalogFilePath original_cdf_file = asset_library_root_ + "/blender_assets.cats.txt"; + CatalogFilePath writable_cdf_file = writable_dir + AssetCatalogService::DEFAULT_CATALOG_FILENAME; + BLI_path_slash_native(writable_cdf_file.data()); + ASSERT_EQ(0, BLI_copy(original_cdf_file.c_str(), writable_cdf_file.c_str())); + + AssetLibraryService *const service = AssetLibraryService::get(); + AssetLibrary *const lib = service->get_asset_library_on_disk(writable_dir); + + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Unchanged AssetLibrary should have no unsaved catalogs"; + + AssetCatalogService *const cat_service = lib->catalog_service.get(); + AssetCatalog *cat = cat_service->find_catalog(UUID_POSES_ELLIE); + + cat_service->tag_has_unsaved_changes(cat); + + EXPECT_TRUE(service->has_any_unsaved_catalogs()) + << "Tagging as having unsaved changes of a single catalog service should result in unsaved " + "changes being reported."; + EXPECT_TRUE(cat->flags.has_unsaved_changes); + + cat_service->write_to_disk(writable_dir + "dummy_path.blend"); + EXPECT_FALSE(service->has_any_unsaved_catalogs()) + << "Written AssetCatalogService should have no unsaved catalogs"; + EXPECT_FALSE(cat->flags.has_unsaved_changes); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_library_test.cc b/source/blender/blenkernel/intern/asset_library_test.cc new file mode 100644 index 00000000000..702008fed96 --- /dev/null +++ b/source/blender/blenkernel/intern/asset_library_test.cc @@ -0,0 +1,102 @@ +/* + * 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) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_appdir.h" +#include "BKE_asset_catalog.hh" +#include "BKE_asset_library.hh" +#include "BKE_callbacks.h" + +#include "asset_library_service.hh" + +#include "CLG_log.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +class AssetLibraryTest : public testing::Test { + public: + static void SetUpTestSuite() + { + CLG_init(); + BKE_callback_global_init(); + } + static void TearDownTestSuite() + { + CLG_exit(); + BKE_callback_global_finalize(); + } + + void TearDown() override + { + AssetLibraryService::destroy(); + } +}; + +TEST_F(AssetLibraryTest, bke_asset_library_load) +{ + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + /* Load the asset library. */ + const std::string library_path = test_files_dir + "/" + "asset_library"; + ::AssetLibrary *library_c_ptr = BKE_asset_library_load(library_path.data()); + ASSERT_NE(nullptr, library_c_ptr); + + /* Check that it can be cast to the C++ type and has a Catalog Service. */ + blender::bke::AssetLibrary *library_cpp_ptr = reinterpret_cast( + library_c_ptr); + AssetCatalogService *service = library_cpp_ptr->catalog_service.get(); + ASSERT_NE(nullptr, service); + + /* Check that the catalogs defined in the library are actually loaded. This just tests one single + * catalog, as that indicates the file has been loaded. Testing that that loading went OK is for + * the asset catalog service tests. */ + const bUUID uuid_poses_ellie("df60e1f6-2259-475b-93d9-69a1b4a8db78"); + AssetCatalog *poses_ellie = service->find_catalog(uuid_poses_ellie); + ASSERT_NE(nullptr, poses_ellie) << "unable to find POSES_ELLIE catalog"; + EXPECT_EQ("character/Ellie/poselib", poses_ellie->path.str()); +} + +TEST_F(AssetLibraryTest, load_nonexistent_directory) +{ + const std::string test_files_dir = blender::tests::flags_test_asset_dir(); + if (test_files_dir.empty()) { + FAIL(); + } + + /* Load the asset library. */ + const std::string library_path = test_files_dir + "/" + + "asset_library/this/subdir/does/not/exist"; + ::AssetLibrary *library_c_ptr = BKE_asset_library_load(library_path.data()); + ASSERT_NE(nullptr, library_c_ptr); + + /* Check that it can be cast to the C++ type and has a Catalog Service. */ + blender::bke::AssetLibrary *library_cpp_ptr = reinterpret_cast( + library_c_ptr); + AssetCatalogService *service = library_cpp_ptr->catalog_service.get(); + ASSERT_NE(nullptr, service); + + /* Check that the catalog service doesn't have any catalogs. */ + EXPECT_TRUE(service->is_empty()); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/asset_test.cc b/source/blender/blenkernel/intern/asset_test.cc new file mode 100644 index 00000000000..77b98a8ac0a --- /dev/null +++ b/source/blender/blenkernel/intern/asset_test.cc @@ -0,0 +1,70 @@ +/* + * 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) 2020 Blender Foundation + * All rights reserved. + */ + +#include "BKE_asset.h" + +#include "BLI_uuid.h" + +#include "DNA_asset_types.h" + +#include "testing/testing.h" + +namespace blender::bke::tests { + +TEST(AssetMetadataTest, set_catalog_id) +{ + AssetMetaData meta; + const bUUID uuid = BLI_uuid_generate_random(); + + /* Test trivial values. */ + BKE_asset_metadata_catalog_id_clear(&meta); + EXPECT_TRUE(BLI_uuid_is_nil(meta.catalog_id)); + EXPECT_STREQ("", meta.catalog_simple_name); + + /* Test simple situation where the given short name is used as-is. */ + BKE_asset_metadata_catalog_id_set(&meta, uuid, "simple"); + EXPECT_TRUE(BLI_uuid_equal(uuid, meta.catalog_id)); + EXPECT_STREQ("simple", meta.catalog_simple_name); + + /* Test white-space trimming. */ + BKE_asset_metadata_catalog_id_set(&meta, uuid, " Govoriš angleško? "); + EXPECT_STREQ("Govoriš angleško?", meta.catalog_simple_name); + + /* Test length trimming to 63 chars + terminating zero. */ + constexpr char len66[] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; + constexpr char len63[] = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1"; + BKE_asset_metadata_catalog_id_set(&meta, uuid, len66); + EXPECT_STREQ(len63, meta.catalog_simple_name); + + /* Test length trimming happens after white-space trimming. */ + constexpr char len68[] = + " \ + 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 "; + BKE_asset_metadata_catalog_id_set(&meta, uuid, len68); + EXPECT_STREQ(len63, meta.catalog_simple_name); + + /* Test length trimming to 63 bytes, and not 63 characters. ✓ in UTF-8 is three bytes long. */ + constexpr char with_utf8[] = + "00010203040506✓0708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20"; + BKE_asset_metadata_catalog_id_set(&meta, uuid, with_utf8); + EXPECT_STREQ("00010203040506✓0708090a0b0c0d0e0f101112131415161718191a1b1c1d", + meta.catalog_simple_name); +} + +} // namespace blender::bke::tests diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 8c4f87be91f..cd394a4ca42 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -52,9 +52,25 @@ using blender::fn::GMutableSpan; using blender::fn::GSpan; using blender::fn::GVArray_For_GSpan; using blender::fn::GVArray_For_SingleValue; +using blender::fn::GVMutableArray_For_GMutableSpan; namespace blender::bke { +std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_id) +{ + if (attribute_id.is_named()) { + stream << attribute_id.name(); + } + else if (attribute_id.is_anonymous()) { + const AnonymousAttributeID &anonymous_id = attribute_id.anonymous_id(); + stream << "<" << BKE_anonymous_attribute_id_debug_name(&anonymous_id) << ">"; + } + else { + stream << ""; + } + return stream; +} + const blender::fn::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) { switch (type) { @@ -187,6 +203,17 @@ AttributeDomain attribute_domain_highest_priority(Span domains) return highest_priority_domain; } +fn::GMutableSpan OutputAttribute::as_span() +{ + if (!optional_span_varray_) { + const bool materialize_old_values = !ignore_old_values_; + optional_span_varray_ = std::make_unique(*varray_, + materialize_old_values); + } + fn::GVMutableArray_GSpan &span_varray = *optional_span_varray_; + return span_varray; +} + void OutputAttribute::save() { save_has_been_called_ = true; @@ -207,72 +234,18 @@ OutputAttribute::~OutputAttribute() } } -GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( - const GeometryComponent &component) const +static AttributeIDRef attribute_id_from_custom_data_layer(const CustomDataLayer &layer) { - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); - if (custom_data == nullptr) { - return {}; + if (layer.anonymous_id != nullptr) { + return layer.anonymous_id; } - - const int domain_size = component.attribute_domain_size(domain_); - const void *data = CustomData_get_layer(custom_data, stored_type_); - if (data == nullptr) { - return {}; - } - return as_read_attribute_(data, domain_size); + return layer.name; } -GVMutableArrayPtr BuiltinCustomDataLayerProvider::try_get_for_write( - GeometryComponent &component) const -{ - if (writable_ != Writable) { - return {}; - } - CustomData *custom_data = custom_data_access_.get_custom_data(component); - if (custom_data == nullptr) { - return {}; - } - const int domain_size = component.attribute_domain_size(domain_); - void *data = CustomData_get_layer(custom_data, stored_type_); - if (data == nullptr) { - return {}; - } - void *new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); - if (data != new_data) { - custom_data_access_.update_custom_data_pointers(component); - data = new_data; - } - if (update_on_write_ != nullptr) { - update_on_write_(component); - } - return as_write_attribute_(data, domain_size); -} - -bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const -{ - if (deletable_ != Deletable) { - return false; - } - CustomData *custom_data = custom_data_access_.get_custom_data(component); - if (custom_data == nullptr) { - return {}; - } - - const int domain_size = component.attribute_domain_size(domain_); - const int layer_index = CustomData_get_layer_index(custom_data, stored_type_); - const bool delete_success = CustomData_free_layer( - custom_data, stored_type_, domain_size, layer_index); - if (delete_success) { - custom_data_access_.update_custom_data_pointers(component); - } - return delete_success; -} - -static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data, - const CustomDataType data_type, - const int domain_size, - const AttributeInit &initializer) +static bool add_builtin_type_custom_data_layer_from_init(CustomData &custom_data, + const CustomDataType data_type, + const int domain_size, + const AttributeInit &initializer) { switch (initializer.type) { case AttributeInit::Type::Default: { @@ -304,145 +277,6 @@ static bool add_custom_data_layer_from_attribute_init(CustomData &custom_data, return false; } -bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, - const AttributeInit &initializer) const -{ - if (createable_ != Creatable) { - return false; - } - CustomData *custom_data = custom_data_access_.get_custom_data(component); - if (custom_data == nullptr) { - return false; - } - if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { - /* Exists already. */ - return false; - } - - const int domain_size = component.attribute_domain_size(domain_); - const bool success = add_custom_data_layer_from_attribute_init( - *custom_data, stored_type_, domain_size, initializer); - if (success) { - custom_data_access_.update_custom_data_pointers(component); - } - return success; -} - -bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const -{ - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); - if (custom_data == nullptr) { - return false; - } - const void *data = CustomData_get_layer(custom_data, stored_type_); - return data != nullptr; -} - -static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, - const AttributeIDRef &attribute_id) -{ - if (!attribute_id) { - return false; - } - if (attribute_id.is_anonymous()) { - return layer.anonymous_id == &attribute_id.anonymous_id(); - } - return layer.name == attribute_id.name(); -} - -ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( - const GeometryComponent &component, const AttributeIDRef &attribute_id) const -{ - const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); - if (custom_data == nullptr) { - return {}; - } - const int domain_size = component.attribute_domain_size(domain_); - for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { - if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { - continue; - } - const CustomDataType data_type = (CustomDataType)layer.type; - switch (data_type) { - case CD_PROP_FLOAT: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_FLOAT2: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_FLOAT3: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_INT32: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_COLOR: - return this->layer_to_read_attribute(layer, domain_size); - case CD_PROP_BOOL: - return this->layer_to_read_attribute(layer, domain_size); - default: - break; - } - } - return {}; -} - -WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( - GeometryComponent &component, const AttributeIDRef &attribute_id) const -{ - CustomData *custom_data = custom_data_access_.get_custom_data(component); - if (custom_data == nullptr) { - return {}; - } - const int domain_size = component.attribute_domain_size(domain_); - for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { - if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { - continue; - } - if (attribute_id.is_named()) { - CustomData_duplicate_referenced_layer_named( - custom_data, layer.type, layer.name, domain_size); - } - else { - CustomData_duplicate_referenced_layer_anonymous( - custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); - } - const CustomDataType data_type = (CustomDataType)layer.type; - switch (data_type) { - case CD_PROP_FLOAT: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_FLOAT2: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_FLOAT3: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_INT32: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_COLOR: - return this->layer_to_write_attribute(layer, domain_size); - case CD_PROP_BOOL: - return this->layer_to_write_attribute(layer, domain_size); - default: - break; - } - } - return {}; -} - -bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, - const AttributeIDRef &attribute_id) const -{ - CustomData *custom_data = custom_data_access_.get_custom_data(component); - if (custom_data == nullptr) { - return false; - } - const int domain_size = component.attribute_domain_size(domain_); - for (const int i : IndexRange(custom_data->totlayer)) { - const CustomDataLayer &layer = custom_data->layers[i]; - if (this->type_is_supported((CustomDataType)layer.type) && - custom_data_layer_matches_attribute_id(layer, attribute_id)) { - CustomData_free_layer(custom_data, layer.type, domain_size, i); - return true; - } - } - return false; -} - static void *add_generic_custom_data_layer(CustomData &custom_data, const CustomDataType data_type, const eCDAllocType alloctype, @@ -499,6 +333,238 @@ static bool add_custom_data_layer_from_attribute_init(const AttributeIDRef &attr return false; } +static bool custom_data_layer_matches_attribute_id(const CustomDataLayer &layer, + const AttributeIDRef &attribute_id) +{ + if (!attribute_id) { + return false; + } + if (attribute_id.is_anonymous()) { + return layer.anonymous_id == &attribute_id.anonymous_id(); + } + return layer.name == attribute_id.name(); +} + +GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( + const GeometryComponent &component) const +{ + const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + if (custom_data == nullptr) { + return {}; + } + + const void *data; + if (stored_as_named_attribute_) { + data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()); + } + else { + data = CustomData_get_layer(custom_data, stored_type_); + } + if (data == nullptr) { + return {}; + } + + const int domain_size = component.attribute_domain_size(domain_); + return as_read_attribute_(data, domain_size); +} + +WriteAttributeLookup BuiltinCustomDataLayerProvider::try_get_for_write( + GeometryComponent &component) const +{ + if (writable_ != Writable) { + return {}; + } + CustomData *custom_data = custom_data_access_.get_custom_data(component); + if (custom_data == nullptr) { + return {}; + } + const int domain_size = component.attribute_domain_size(domain_); + + void *data; + if (stored_as_named_attribute_) { + data = CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()); + } + else { + data = CustomData_get_layer(custom_data, stored_type_); + } + if (data == nullptr) { + return {}; + } + + void *new_data; + if (stored_as_named_attribute_) { + new_data = CustomData_duplicate_referenced_layer_named( + custom_data, stored_type_, name_.c_str(), domain_size); + } + else { + new_data = CustomData_duplicate_referenced_layer(custom_data, stored_type_, domain_size); + } + + if (data != new_data) { + custom_data_access_.update_custom_data_pointers(component); + data = new_data; + } + + std::function tag_modified_fn; + if (update_on_write_ != nullptr) { + tag_modified_fn = [component = &component, update = update_on_write_]() { + update(*component); + }; + } + + return {as_write_attribute_(data, domain_size), domain_, std::move(tag_modified_fn)}; +} + +bool BuiltinCustomDataLayerProvider::try_delete(GeometryComponent &component) const +{ + if (deletable_ != Deletable) { + return false; + } + CustomData *custom_data = custom_data_access_.get_custom_data(component); + if (custom_data == nullptr) { + return {}; + } + + const int domain_size = component.attribute_domain_size(domain_); + int layer_index; + if (stored_as_named_attribute_) { + for (const int i : IndexRange(custom_data->totlayer)) { + if (custom_data_layer_matches_attribute_id(custom_data->layers[i], name_)) { + layer_index = i; + break; + } + } + } + else { + layer_index = CustomData_get_layer_index(custom_data, stored_type_); + } + + const bool delete_success = CustomData_free_layer( + custom_data, stored_type_, domain_size, layer_index); + if (delete_success) { + custom_data_access_.update_custom_data_pointers(component); + } + return delete_success; +} + +bool BuiltinCustomDataLayerProvider::try_create(GeometryComponent &component, + const AttributeInit &initializer) const +{ + if (createable_ != Creatable) { + return false; + } + CustomData *custom_data = custom_data_access_.get_custom_data(component); + if (custom_data == nullptr) { + return false; + } + + const int domain_size = component.attribute_domain_size(domain_); + bool success; + if (stored_as_named_attribute_) { + if (CustomData_get_layer_named(custom_data, data_type_, name_.c_str())) { + /* Exists already. */ + return false; + } + success = add_custom_data_layer_from_attribute_init( + name_, *custom_data, stored_type_, domain_size, initializer); + } + else { + if (CustomData_get_layer(custom_data, stored_type_) != nullptr) { + /* Exists already. */ + return false; + } + success = add_builtin_type_custom_data_layer_from_init( + *custom_data, stored_type_, domain_size, initializer); + } + if (success) { + custom_data_access_.update_custom_data_pointers(component); + } + return success; +} + +bool BuiltinCustomDataLayerProvider::exists(const GeometryComponent &component) const +{ + const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + if (custom_data == nullptr) { + return false; + } + if (stored_as_named_attribute_) { + return CustomData_get_layer_named(custom_data, stored_type_, name_.c_str()) != nullptr; + } + return CustomData_get_layer(custom_data, stored_type_) != nullptr; +} + +ReadAttributeLookup CustomDataAttributeProvider::try_get_for_read( + const GeometryComponent &component, const AttributeIDRef &attribute_id) const +{ + const CustomData *custom_data = custom_data_access_.get_const_custom_data(component); + if (custom_data == nullptr) { + return {}; + } + const int domain_size = component.attribute_domain_size(domain_); + for (const CustomDataLayer &layer : Span(custom_data->layers, custom_data->totlayer)) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { + continue; + } + const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); + if (type == nullptr) { + continue; + } + GSpan data{*type, layer.data, domain_size}; + return {std::make_unique(data), domain_}; + } + return {}; +} + +WriteAttributeLookup CustomDataAttributeProvider::try_get_for_write( + GeometryComponent &component, const AttributeIDRef &attribute_id) const +{ + CustomData *custom_data = custom_data_access_.get_custom_data(component); + if (custom_data == nullptr) { + return {}; + } + const int domain_size = component.attribute_domain_size(domain_); + for (CustomDataLayer &layer : MutableSpan(custom_data->layers, custom_data->totlayer)) { + if (!custom_data_layer_matches_attribute_id(layer, attribute_id)) { + continue; + } + if (attribute_id.is_named()) { + CustomData_duplicate_referenced_layer_named( + custom_data, layer.type, layer.name, domain_size); + } + else { + CustomData_duplicate_referenced_layer_anonymous( + custom_data, layer.type, &attribute_id.anonymous_id(), domain_size); + } + const CPPType *type = custom_data_type_to_cpp_type((CustomDataType)layer.type); + if (type == nullptr) { + continue; + } + GMutableSpan data{*type, layer.data, domain_size}; + return {std::make_unique(data), domain_}; + } + return {}; +} + +bool CustomDataAttributeProvider::try_delete(GeometryComponent &component, + const AttributeIDRef &attribute_id) const +{ + CustomData *custom_data = custom_data_access_.get_custom_data(component); + if (custom_data == nullptr) { + return false; + } + const int domain_size = component.attribute_domain_size(domain_); + for (const int i : IndexRange(custom_data->totlayer)) { + const CustomDataLayer &layer = custom_data->layers[i]; + if (this->type_is_supported((CustomDataType)layer.type) && + custom_data_layer_matches_attribute_id(layer, attribute_id)) { + CustomData_free_layer(custom_data, layer.type, domain_size, i); + return true; + } + } + return false; +} + bool CustomDataAttributeProvider::try_create(GeometryComponent &component, const AttributeIDRef &attribute_id, const AttributeDomain domain, @@ -537,13 +603,7 @@ bool CustomDataAttributeProvider::foreach_attribute(const GeometryComponent &com const CustomDataType data_type = (CustomDataType)layer.type; if (this->type_is_supported(data_type)) { AttributeMetaData meta_data{domain_, data_type}; - AttributeIDRef attribute_id; - if (layer.anonymous_id != nullptr) { - attribute_id = layer.anonymous_id; - } - else { - attribute_id = layer.name; - } + const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer); if (!callback(attribute_id, meta_data)) { return false; } @@ -767,13 +827,7 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call { for (const CustomDataLayer &layer : Span(data.layers, data.totlayer)) { AttributeMetaData meta_data{domain, (CustomDataType)layer.type}; - AttributeIDRef attribute_id; - if (layer.anonymous_id != nullptr) { - attribute_id = layer.anonymous_id; - } - else { - attribute_id = layer.name; - } + const AttributeIDRef attribute_id = attribute_id_from_custom_data_layer(layer); if (!callback(attribute_id, meta_data)) { return false; } @@ -781,6 +835,26 @@ bool CustomDataAttributes::foreach_attribute(const AttributeForeachCallback call return true; } +void CustomDataAttributes::reorder(Span new_order) +{ + BLI_assert(new_order.size() == data.totlayer); + + Map old_order; + old_order.reserve(data.totlayer); + Array old_layers(Span(data.layers, data.totlayer)); + for (const int i : old_layers.index_range()) { + old_order.add_new(attribute_id_from_custom_data_layer(old_layers[i]), i); + } + + MutableSpan layers(data.layers, data.totlayer); + for (const int i : layers.index_range()) { + const int old_index = old_order.lookup(new_order[i]); + layers[i] = old_layers[old_index]; + } + + CustomData_update_typemap(&data); +} + } // namespace blender::bke /* -------------------------------------------------------------------- */ @@ -871,7 +945,7 @@ blender::bke::WriteAttributeLookup GeometryComponent::attribute_try_get_for_writ const BuiltinAttributeProvider *builtin_provider = providers->builtin_attribute_providers().lookup_default_as(attribute_id.name(), nullptr); if (builtin_provider != nullptr) { - return {builtin_provider->try_get_for_write(*this), builtin_provider->domain()}; + return builtin_provider->try_get_for_write(*this); } } for (const DynamicAttributesProvider *dynamic_provider : @@ -1055,7 +1129,7 @@ std::unique_ptr GeometryComponent::attribute_try_get_for_r } std::unique_ptr varray = std::move(attribute.varray); - if (domain != ATTR_DOMAIN_AUTO && attribute.domain != domain) { + if (!ELEM(domain, ATTR_DOMAIN_AUTO, attribute.domain)) { varray = this->attribute_try_adapt_domain(std::move(varray), attribute.domain, domain); if (!varray) { return {}; @@ -1195,6 +1269,20 @@ static void save_output_attribute(OutputAttribute &output_attribute) varray.get(i, buffer); write_attribute.varray->set_by_relocate(i, buffer); } + if (write_attribute.tag_modified_fn) { + write_attribute.tag_modified_fn(); + } +} + +static std::function get_simple_output_attribute_save_method( + const blender::bke::WriteAttributeLookup &attribute) +{ + if (!attribute.tag_modified_fn) { + return {}; + } + return [tag_modified_fn = attribute.tag_modified_fn](OutputAttribute &UNUSED(attribute)) { + tag_modified_fn(); + }; } static OutputAttribute create_output_attribute(GeometryComponent &component, @@ -1239,14 +1327,21 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, /* Builtin attribute is on different domain. */ return {}; } + GVMutableArrayPtr varray = std::move(attribute.varray); if (varray->type() == *cpp_type) { /* Builtin attribute matches exactly. */ - return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); + return OutputAttribute(std::move(varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } /* Builtin attribute is on the same domain but has a different data type. */ varray = conversions.try_convert(std::move(varray), *cpp_type); - return OutputAttribute(std::move(varray), domain, {}, ignore_old_values); + return OutputAttribute(std::move(varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } const int domain_size = component.attribute_domain_size(domain); @@ -1270,7 +1365,11 @@ static OutputAttribute create_output_attribute(GeometryComponent &component, } if (attribute.domain == domain && attribute.varray->type() == *cpp_type) { /* Existing generic attribute matches exactly. */ - return OutputAttribute(std::move(attribute.varray), domain, {}, ignore_old_values); + + return OutputAttribute(std::move(attribute.varray), + domain, + get_simple_output_attribute_save_method(attribute), + ignore_old_values); } /* Allocate a new array that lives next to the existing attribute. It will overwrite the existing @@ -1331,7 +1430,7 @@ const GVArray *AttributeFieldInput::get_varray_for_context(const fn::FieldContex std::string AttributeFieldInput::socket_inspection_name() const { std::stringstream ss; - ss << TIP_("Attribute: ") << name_; + ss << '"' << name_ << '"' << TIP_(" attribute from geometry"); return ss.str(); } @@ -1348,6 +1447,54 @@ bool AttributeFieldInput::is_equal_to(const fn::FieldNode &other) const return false; } +static StringRef get_random_id_attribute_name(const AttributeDomain domain) +{ + switch (domain) { + case ATTR_DOMAIN_POINT: + return "id"; + default: + return ""; + } +} + +const GVArray *IDAttributeFieldInput::get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const +{ + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + const StringRef name = get_random_id_attribute_name(domain); + GVArrayPtr attribute = component.attribute_try_get_for_read(name, domain, CD_PROP_INT32); + if (attribute) { + BLI_assert(attribute->size() == component.attribute_domain_size(domain)); + return scope.add(std::move(attribute)); + } + + /* Use the index as the fallback if no random ID attribute exists. */ + return fn::IndexFieldInput::get_index_varray(mask, scope); + } + return nullptr; +} + +std::string IDAttributeFieldInput::socket_inspection_name() const +{ + return TIP_("ID / Index"); +} + +uint64_t IDAttributeFieldInput::hash() const +{ + /* All random ID attribute inputs are the same within the same evaluation context. */ + return 92386459827; +} + +bool IDAttributeFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + /* All random ID attribute inputs are the same within the same evaluation context. */ + return dynamic_cast(&other) != nullptr; +} + const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( const fn::FieldContext &context, IndexMask UNUSED(mask), ResourceScope &scope) const { @@ -1366,7 +1513,7 @@ const GVArray *AnonymousAttributeFieldInput::get_varray_for_context( std::string AnonymousAttributeFieldInput::socket_inspection_name() const { std::stringstream ss; - ss << TIP_("Anonymous Attribute: ") << debug_name_; + ss << '"' << debug_name_ << '"' << TIP_(" from ") << producer_name_; return ss.str(); } diff --git a/source/blender/blenkernel/intern/attribute_access_intern.hh b/source/blender/blenkernel/intern/attribute_access_intern.hh index 261cb26d4e5..140498bdb01 100644 --- a/source/blender/blenkernel/intern/attribute_access_intern.hh +++ b/source/blender/blenkernel/intern/attribute_access_intern.hh @@ -87,7 +87,7 @@ class BuiltinAttributeProvider { } virtual GVArrayPtr try_get_for_read(const GeometryComponent &component) const = 0; - virtual GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const = 0; + virtual WriteAttributeLookup try_get_for_write(GeometryComponent &component) const = 0; virtual bool try_delete(GeometryComponent &component) const = 0; virtual bool try_create(GeometryComponent &UNUSED(component), const AttributeInit &UNUSED(initializer)) const = 0; @@ -177,24 +177,6 @@ class CustomDataAttributeProvider final : public DynamicAttributesProvider { } private: - template - ReadAttributeLookup layer_to_read_attribute(const CustomDataLayer &layer, - const int domain_size) const - { - return {std::make_unique>( - Span(static_cast(layer.data), domain_size)), - domain_}; - } - - template - WriteAttributeLookup layer_to_write_attribute(CustomDataLayer &layer, - const int domain_size) const - { - return {std::make_unique>( - MutableSpan(static_cast(layer.data), domain_size)), - domain_}; - } - bool type_is_supported(CustomDataType data_type) const { return ((1ULL << data_type) & supported_types_mask) != 0; @@ -245,6 +227,9 @@ class NamedLegacyCustomDataProvider final : public DynamicAttributesProvider { * This provider is used to provide access to builtin attributes. It supports making internal types * available as different types. For example, the vertex position attribute is stored as part of * the #MVert struct, but is exposed as float3 attribute. + * + * It also supports named builtin attributes, and will look up attributes in #CustomData by name + * if the stored type is the same as the attribute type. */ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { using AsReadAttribute = GVArrayPtr (*)(const void *data, const int domain_size); @@ -256,6 +241,7 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { const AsReadAttribute as_read_attribute_; const AsWriteAttribute as_write_attribute_; const UpdateOnWrite update_on_write_; + bool stored_as_named_attribute_; public: BuiltinCustomDataLayerProvider(std::string attribute_name, @@ -275,12 +261,13 @@ class BuiltinCustomDataLayerProvider final : public BuiltinAttributeProvider { custom_data_access_(custom_data_access), as_read_attribute_(as_read_attribute), as_write_attribute_(as_write_attribute), - update_on_write_(update_on_write) + update_on_write_(update_on_write), + stored_as_named_attribute_(data_type_ == stored_type_) { } GVArrayPtr try_get_for_read(const GeometryComponent &component) const final; - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final; + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final; bool try_delete(GeometryComponent &component) const final; bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final; bool exists(const GeometryComponent &component) const final; diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c index 97f8bddc043..fb65a9bec7e 100644 --- a/source/blender/blenkernel/intern/blender.c +++ b/source/blender/blenkernel/intern/blender.c @@ -90,7 +90,6 @@ void BKE_blender_free(void) IMB_exit(); BKE_cachefiles_exit(); - BKE_images_exit(); DEG_free_node_types(); BKE_brush_system_exit(); diff --git a/source/blender/blenkernel/intern/blendfile.c b/source/blender/blenkernel/intern/blendfile.c index 1c5d8804280..fc535fc2ad1 100644 --- a/source/blender/blenkernel/intern/blendfile.c +++ b/source/blender/blenkernel/intern/blendfile.c @@ -251,7 +251,7 @@ static void setup_app_data(bContext *C, * replace it with 'curscene' if its needed */ } /* and we enforce curscene to be in current screen */ - else if (win) { /* can run in bgmode */ + else if (win) { /* The window may be NULL in background-mode. */ win->scene = curscene; } @@ -344,6 +344,13 @@ static void setup_app_data(bContext *C, do_versions_ipos_to_animato(bmain); } + /* FIXME: Same as above, readfile's `do_version` do not allow to create new IDs. */ + /* TODO: Once this is definitively validated for 3.0 and option to not do it is removed, add a + * version bump and check here. */ + if (mode != LOAD_UNDO && !USER_EXPERIMENTAL_TEST(&U, no_proxy_to_override_conversion)) { + BKE_lib_override_library_main_proxy_convert(bmain, reports); + } + bmain->recovered = 0; /* startup.blend or recovered startup */ diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index 1684e22dece..9ce58d8129b 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -15,7 +15,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ /* TODO: @@ -66,13 +66,13 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BKE_font.h" #include "BKE_image.h" #include "BKE_lib_id.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_report.h" +#include "BKE_vfont.h" #include "BKE_bpath.h" /* own include */ @@ -586,6 +586,11 @@ void BKE_bpath_traverse_id( return; } + if (id->library_weak_reference != NULL) { + rewrite_path_fixed( + id->library_weak_reference->library_filepath, visit_cb, absbase, bpath_user_data); + } + switch (GS(id->name)) { case ID_IM: { Image *ima; diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 69773b43173..602fd098d1a 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -183,8 +183,8 @@ static void brush_make_local(Main *bmain, ID *id, const int flags) } if (force_local) { - BKE_lib_id_clear_library_data(bmain, &brush->id); - BKE_lib_id_expand_local(bmain, &brush->id); + BKE_lib_id_clear_library_data(bmain, &brush->id, flags); + BKE_lib_id_expand_local(bmain, &brush->id, flags); /* enable fake user by default */ id_fake_user_set(&brush->id); @@ -207,14 +207,15 @@ static void brush_foreach_id(ID *id, LibraryForeachIDData *data) { Brush *brush = (Brush *)id; - BKE_LIB_FOREACHID_PROCESS(data, brush->toggle_brush, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, brush->clone.image, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, brush->paint_curve, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->toggle_brush, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->clone.image, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->paint_curve, IDWALK_CB_USER); if (brush->gpencil_settings) { - BKE_LIB_FOREACHID_PROCESS(data, brush->gpencil_settings->material, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER); } - BKE_texture_mtex_foreach_id(data, &brush->mtex); - BKE_texture_mtex_foreach_id(data, &brush->mask_mtex); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_texture_mtex_foreach_id(data, &brush->mtex)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_texture_mtex_foreach_id(data, &brush->mask_mtex)); } static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address) diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index 707201207d9..1f92f834972 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -161,9 +161,11 @@ void bvhcache_free(BVHCache *bvh_cache) MEM_freeN(bvh_cache); } -/* BVH tree balancing inside a mutex lock must be run in isolation. Balancing +/** + * BVH-tree balancing inside a mutex lock must be run in isolation. Balancing * is multithreaded, and we do not want the current thread to start another task - * that may involve acquiring the same mutex lock that it is waiting for. */ + * that may involve acquiring the same mutex lock that it is waiting for. + */ static void bvhtree_balance_isolated(void *userdata) { BLI_bvhtree_balance((BVHTree *)userdata); @@ -233,8 +235,12 @@ float bvhtree_sphereray_tri_intersection(const BVHTreeRay *ray, * BVH from meshes callbacks */ -/* Callback to bvh tree nearest point. The tree must have been built using bvhtree_from_mesh_faces. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree nearest point. + * The tree must have been built using #bvhtree_from_mesh_faces. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_faces_nearest_point(void *userdata, int index, const float co[3], @@ -325,8 +331,12 @@ static void editmesh_looptri_nearest_point(void *userdata, } } -/* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_faces. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree ray-cast. + * The tree must have been built using bvhtree_from_mesh_faces. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_faces_spherecast(void *userdata, int index, const BVHTreeRay *ray, @@ -430,8 +440,12 @@ static void editmesh_looptri_spherecast(void *userdata, } } -/* Callback to bvh tree nearest point. The tree must have been built using bvhtree_from_mesh_edges. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree nearest point. + * The tree must have been built using #bvhtree_from_mesh_edges. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_edges_nearest_point(void *userdata, int index, const float co[3], @@ -491,8 +505,12 @@ static void editmesh_verts_spherecast(void *userdata, mesh_verts_spherecast_do(index, eve->co, ray, hit); } -/* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_verts. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree ray-cast. + * The tree must have been built using bvhtree_from_mesh_verts. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_verts_spherecast(void *userdata, int index, const BVHTreeRay *ray, @@ -504,8 +522,12 @@ static void mesh_verts_spherecast(void *userdata, mesh_verts_spherecast_do(index, v, ray, hit); } -/* Callback to bvh tree raycast. The tree must have been built using bvhtree_from_mesh_edges. - * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. */ +/** + * Callback to BVH-tree ray-cast. + * The tree must have been built using bvhtree_from_mesh_edges. + * + * \param userdata: Must be a #BVHMeshCallbackUserdata built from the same mesh as the tree. + */ static void mesh_edges_spherecast(void *userdata, int index, const BVHTreeRay *ray, @@ -647,7 +669,9 @@ static void bvhtree_from_mesh_verts_setup_data(BVHTreeFromMesh *data, data->vert_allocated = vert_allocated; } -/* Builds a bvh tree where nodes are the vertices of the given em */ +/** + * Builds a BVH-tree where nodes are the vertices of the given `em`. + */ BVHTree *bvhtree_from_editmesh_verts_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, const BLI_bitmap *verts_mask, @@ -704,10 +728,10 @@ BVHTree *bvhtree_from_editmesh_verts( } /** - * Builds a bvh tree where nodes are the given vertices (NOTE: does not copy given `vert`!). + * Builds a BVH-tree where nodes are the given vertices (NOTE: does not copy given `vert`!). * \param vert_allocated: if true, vert freeing will be done when freeing data. - * \param verts_mask: if not null, true elements give which vert to add to BVH tree. - * \param verts_num_active: if >= 0, number of active verts to add to BVH tree + * \param verts_mask: if not null, true elements give which vert to add to BVH-tree. + * \param verts_num_active: if >= 0, number of active verts to add to BVH-tree * (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_verts_ex(BVHTreeFromMesh *data, @@ -860,7 +884,9 @@ static void bvhtree_from_mesh_edges_setup_data(BVHTreeFromMesh *data, data->edge_allocated = edge_allocated; } -/* Builds a bvh tree where nodes are the edges of the given em */ +/** + * Builds a BVH-tree where nodes are the edges of the given `em`. + */ BVHTree *bvhtree_from_editmesh_edges_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, const BLI_bitmap *edges_mask, @@ -916,11 +942,11 @@ BVHTree *bvhtree_from_editmesh_edges( } /** - * Builds a bvh tree where nodes are the given edges . + * Builds a BVH-tree where nodes are the given edges. * \param vert, vert_allocated: if true, elem freeing will be done when freeing data. * \param edge, edge_allocated: if true, elem freeing will be done when freeing data. - * \param edges_mask: if not null, true elements give which vert to add to BVH tree. - * \param edges_num_active: if >= 0, number of active edges to add to BVH tree + * \param edges_mask: if not null, true elements give which vert to add to BVH-tree. + * \param edges_num_active: if >= 0, number of active edges to add to BVH-tree * (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_edges_ex(BVHTreeFromMesh *data, @@ -1050,12 +1076,12 @@ static void bvhtree_from_mesh_faces_setup_data(BVHTreeFromMesh *data, } /** - * Builds a bvh tree where nodes are the given tessellated faces + * Builds a BVH-tree where nodes are the given tessellated faces * (NOTE: does not copy given mfaces!). * \param vert_allocated: if true, vert freeing will be done when freeing data. * \param face_allocated: if true, face freeing will be done when freeing data. - * \param faces_mask: if not null, true elements give which faces to add to BVH tree. - * \param faces_num_active: if >= 0, number of active faces to add to BVH tree + * \param faces_mask: if not null, true elements give which faces to add to BVH-tree. + * \param faces_num_active: if >= 0, number of active faces to add to BVH-tree * (else will be computed from mask). */ BVHTree *bvhtree_from_mesh_faces_ex(BVHTreeFromMesh *data, @@ -1135,7 +1161,7 @@ static BVHTree *bvhtree_from_editmesh_looptri_create_tree(float epsilon, if (tree) { const BMLoop *(*looptris)[3] = (const BMLoop *(*)[3])em->looptris; - /* Insert BMesh-tessellation triangles into the bvh tree, unless they are hidden + /* Insert BMesh-tessellation triangles into the BVH-tree, unless they are hidden * and/or selected. Even if the faces themselves are not selected for the snapped * transform, having a vertex selected means the face (and thus it's tessellated * triangles) will be moving and will not be a good snap targets. */ @@ -1232,7 +1258,7 @@ static void bvhtree_from_mesh_looptri_setup_data(BVHTreeFromMesh *data, } /** - * Builds a bvh tree where nodes are the looptri faces of the given bm + * Builds a BVH-tree where nodes are the `looptri` faces of the given `bm`. */ BVHTree *bvhtree_from_editmesh_looptri_ex(BVHTreeFromEditMesh *data, BMEditMesh *em, @@ -1684,7 +1710,7 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, mesh_eval_mutex); } else { - /* Setup BVHTreeFromMesh */ + /* Setup #BVHTreeFromMesh */ data->nearest_callback = nullptr; /* TODO */ data->raycast_callback = nullptr; /* TODO */ } @@ -1704,7 +1730,7 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, mesh_eval_mutex); } else { - /* Setup BVHTreeFromMesh */ + /* Setup #BVHTreeFromMesh */ data->nearest_callback = editmesh_looptri_nearest_point; data->raycast_callback = editmesh_looptri_spherecast; } @@ -1741,7 +1767,9 @@ BVHTree *BKE_bvhtree_from_editmesh_get(BVHTreeFromEditMesh *data, /** \} */ -/* Frees data allocated by a call to bvhtree_from_editmesh_*. */ +/** + * Frees data allocated by a call to `bvhtree_from_editmesh_*`. + */ void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data) { if (data->tree) { @@ -1752,7 +1780,9 @@ void free_bvhtree_from_editmesh(struct BVHTreeFromEditMesh *data) } } -/* Frees data allocated by a call to bvhtree_from_mesh_*. */ +/** + * Frees data allocated by a call to `bvhtree_from_mesh_*`. + */ void free_bvhtree_from_mesh(struct BVHTreeFromMesh *data) { if (data->tree && !data->cached) { diff --git a/source/blender/blenkernel/intern/cachefile.c b/source/blender/blenkernel/intern/cachefile.c index 87b1584d422..e642bbc9e06 100644 --- a/source/blender/blenkernel/intern/cachefile.c +++ b/source/blender/blenkernel/intern/cachefile.c @@ -133,7 +133,7 @@ IDTypeInfo IDType_ID_CF = { .name = "CacheFile", .name_plural = "cache_files", .translation_context = BLT_I18NCONTEXT_ID_CACHEFILE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = cache_file_init_data, .copy_data = cache_file_copy_data, diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index 11ee9492b44..72dd51a940d 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -30,15 +30,23 @@ static ListBase callback_slots[BKE_CB_EVT_TOT] = {{NULL}}; +static bool callbacks_initialized = false; + +#define ASSERT_CALLBACKS_INITIALIZED() \ + BLI_assert_msg(callbacks_initialized, \ + "Callbacks should be initialized with BKE_callback_global_init() before using " \ + "the callback system.") + void BKE_callback_exec(struct Main *bmain, struct PointerRNA **pointers, const int num_pointers, eCbEvent evt) { - ListBase *lb = &callback_slots[evt]; - bCallbackFuncStore *funcstore; + ASSERT_CALLBACKS_INITIALIZED(); - for (funcstore = lb->first; funcstore; funcstore = funcstore->next) { + /* Use mutable iteration so handlers are able to remove themselves. */ + ListBase *lb = &callback_slots[evt]; + LISTBASE_FOREACH_MUTABLE (bCallbackFuncStore *, funcstore, lb) { funcstore->func(bmain, pointers, num_pointers, funcstore->arg); } } @@ -76,13 +84,35 @@ void BKE_callback_exec_id_depsgraph(struct Main *bmain, void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) { + ASSERT_CALLBACKS_INITIALIZED(); ListBase *lb = &callback_slots[evt]; BLI_addtail(lb, funcstore); } +void BKE_callback_remove(bCallbackFuncStore *funcstore, eCbEvent evt) +{ + /* The callback may have already been removed by BKE_callback_global_finalize(), for + * example when removing callbacks in response to a BKE_blender_atexit_register callback + * function. `BKE_blender_atexit()` runs after `BKE_callback_global_finalize()`. */ + if (!callbacks_initialized) { + return; + } + + ListBase *lb = &callback_slots[evt]; + + /* Be noisy about potential programming errors. */ + BLI_assert_msg(BLI_findindex(lb, funcstore) != -1, "To-be-removed callback not found"); + + BLI_remlink(lb, funcstore); + + if (funcstore->alloc) { + MEM_freeN(funcstore); + } +} + void BKE_callback_global_init(void) { - /* do nothing */ + callbacks_initialized = true; } /* call on application exit */ @@ -95,10 +125,9 @@ void BKE_callback_global_finalize(void) bCallbackFuncStore *funcstore_next; for (funcstore = lb->first; funcstore; funcstore = funcstore_next) { funcstore_next = funcstore->next; - BLI_remlink(lb, funcstore); - if (funcstore->alloc) { - MEM_freeN(funcstore); - } + BKE_callback_remove(funcstore, evt); } } + + callbacks_initialized = false; } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index b77855f8f95..d355de73170 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -103,13 +103,13 @@ static void camera_foreach_id(ID *id, LibraryForeachIDData *data) { Camera *camera = (Camera *)id; - BKE_LIB_FOREACHID_PROCESS(data, camera->dof.focus_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, camera->dof.focus_object, IDWALK_CB_NOP); LISTBASE_FOREACH (CameraBGImage *, bgpic, &camera->bg_images) { if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) { - BKE_LIB_FOREACHID_PROCESS(data, bgpic->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->ima, IDWALK_CB_USER); } else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) { - BKE_LIB_FOREACHID_PROCESS(data, bgpic->clip, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, bgpic->clip, IDWALK_CB_USER); } } } @@ -140,7 +140,6 @@ static void camera_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_list(reader, &ca->bg_images); LISTBASE_FOREACH (CameraBGImage *, bgpic, &ca->bg_images) { - bgpic->iuser.ok = 1; bgpic->iuser.scene = NULL; } } @@ -182,7 +181,7 @@ IDTypeInfo IDType_ID_CA = { .name = "Camera", .name_plural = "cameras", .translation_context = BLT_I18NCONTEXT_ID_CAMERA, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = camera_init_data, .copy_data = camera_copy_data, @@ -1128,7 +1127,6 @@ CameraBGImage *BKE_camera_background_image_new(Camera *cam) bgpic->scale = 1.0f; bgpic->alpha = 0.5f; - bgpic->iuser.ok = 1; bgpic->iuser.flag |= IMA_ANIM_ALWAYS; bgpic->flag |= CAM_BGIMG_FLAG_EXPANDED; diff --git a/source/blender/blenkernel/intern/cdderivedmesh.c b/source/blender/blenkernel/intern/cdderivedmesh.c index 039a971fe2c..c93d320787a 100644 --- a/source/blender/blenkernel/intern/cdderivedmesh.c +++ b/source/blender/blenkernel/intern/cdderivedmesh.c @@ -103,24 +103,6 @@ static int cdDM_getNumPolys(DerivedMesh *dm) return dm->numPolyData; } -static void cdDM_getVert(DerivedMesh *dm, int index, MVert *r_vert) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - *r_vert = cddm->mvert[index]; -} - -static void cdDM_getEdge(DerivedMesh *dm, int index, MEdge *r_edge) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - *r_edge = cddm->medge[index]; -} - -static void cdDM_getTessFace(DerivedMesh *dm, int index, MFace *r_face) -{ - CDDerivedMesh *cddm = (CDDerivedMesh *)dm; - *r_face = cddm->mface[index]; -} - static void cdDM_copyVertArray(DerivedMesh *dm, MVert *r_vert) { CDDerivedMesh *cddm = (CDDerivedMesh *)dm; @@ -231,10 +213,6 @@ static CDDerivedMesh *cdDM_create(const char *desc) dm->getNumLoops = cdDM_getNumLoops; dm->getNumPolys = cdDM_getNumPolys; - dm->getVert = cdDM_getVert; - dm->getEdge = cdDM_getEdge; - dm->getTessFace = cdDM_getTessFace; - dm->copyVertArray = cdDM_copyVertArray; dm->copyEdgeArray = cdDM_copyEdgeArray; dm->copyTessFaceArray = cdDM_copyTessFaceArray; diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 2d172f23428..22b939d3cf9 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -158,10 +158,11 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) Collection *collection = (Collection *)id; LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - BKE_LIB_FOREACHID_PROCESS(data, cob->ob, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, cob->ob, IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - BKE_LIB_FOREACHID_PROCESS(data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER); } LISTBASE_FOREACH (CollectionParent *, parent, &collection->parents) { /* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad @@ -170,7 +171,7 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data) (parent->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ? IDWALK_CB_EMBEDDED : IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, parent->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_LOOPBACK | cb_flag); } } @@ -597,7 +598,7 @@ static Collection *collection_duplicate_recursive(Main *bmain, } else if (collection_old->id.newid == NULL) { collection_new = (Collection *)BKE_id_copy_for_duplicate( - bmain, (ID *)collection_old, duplicate_flags); + bmain, (ID *)collection_old, duplicate_flags, LIB_ID_COPY_DEFAULT); if (collection_new == collection_old) { return collection_new; @@ -715,7 +716,7 @@ Collection *BKE_collection_duplicate(Main *bmain, collection_new->id.tag &= ~LIB_TAG_NEW; /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&collection_new->id); + BKE_libblock_relink_to_newid(bmain, &collection_new->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ diff --git a/source/blender/blenkernel/intern/collision.c b/source/blender/blenkernel/intern/collision.c index 1c24dae430c..03957d54629 100644 --- a/source/blender/blenkernel/intern/collision.c +++ b/source/blender/blenkernel/intern/collision.c @@ -577,7 +577,7 @@ static float compute_collision_point_edge_tri(const float a1[3], return dist; } -// w3 is not perfect +/* `w3` is not perfect. */ static void collision_compute_barycentric(const float pv[3], const float p1[3], const float p2[3], diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index f2c2e552a9f..62b817487fc 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -1212,6 +1212,20 @@ void BKE_curvemapping_init(CurveMapping *cumap) } } +void BKE_curvemapping_table_F(const CurveMapping *cumap, float **array, int *size) +{ + int a; + + *size = CM_TABLE + 1; + *array = MEM_callocN(sizeof(float) * (*size) * 4, "CurveMapping"); + + for (a = 0; a < *size; a++) { + if (cumap->cm[0].table) { + (*array)[a * 4 + 0] = cumap->cm[0].table[a].y; + } + } +} + void BKE_curvemapping_table_RGBA(const CurveMapping *cumap, float **array, int *size) { int a; diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index b9b15eba6a4..b2b03d28483 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -3499,7 +3499,7 @@ static void stretchto_new_data(void *cdata) bStretchToConstraint *data = (bStretchToConstraint *)cdata; data->volmode = 0; - data->plane = 0; + data->plane = SWING_Y; data->orglength = 0.0; data->bulge = 1.0; data->bulge_max = 1.0f; diff --git a/source/blender/blenkernel/intern/curve.c b/source/blender/blenkernel/intern/curve.c index f22c3b13efc..aae9ac383a4 100644 --- a/source/blender/blenkernel/intern/curve.c +++ b/source/blender/blenkernel/intern/curve.c @@ -52,13 +52,13 @@ #include "BKE_curve.h" #include "BKE_curveprofile.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_idtype.h" #include "BKE_key.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_object.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -130,17 +130,17 @@ static void curve_free_data(ID *id) static void curve_foreach_id(ID *id, LibraryForeachIDData *data) { Curve *curve = (Curve *)id; - BKE_LIB_FOREACHID_PROCESS(data, curve->bevobj, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->taperobj, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->textoncurve, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, curve->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->bevobj, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->taperobj, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->textoncurve, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->key, IDWALK_CB_USER); for (int i = 0; i < curve->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, curve->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->mat[i], IDWALK_CB_USER); } - BKE_LIB_FOREACHID_PROCESS(data, curve->vfont, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfontb, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfonti, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, curve->vfontbi, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfont, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontb, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfonti, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, curve->vfontbi, IDWALK_CB_USER); } static void curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -311,7 +311,7 @@ IDTypeInfo IDType_ID_CU = { .name = "Curve", .name_plural = "curves", .translation_context = BLT_I18NCONTEXT_ID_CURVE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = curve_init_data, .copy_data = curve_copy_data, @@ -404,6 +404,7 @@ void BKE_curve_init(Curve *cu, const short curve_type) } else if (cu->type == OB_SURF) { cu->flag |= CU_3D; + cu->resolu = 4; cu->resolv = 4; } cu->bevel_profile = NULL; @@ -3346,7 +3347,7 @@ static void calchandleNurb_intern(BezTriple *bezt, if (skip_align || /* when one handle is free, alignming makes no sense, see: T35952 */ - (ELEM(HD_FREE, bezt->h1, bezt->h2)) || + ELEM(HD_FREE, bezt->h1, bezt->h2) || /* also when no handles are aligned, skip this step */ (!ELEM(HD_ALIGN, bezt->h1, bezt->h2) && !ELEM(HD_ALIGN_DOUBLESIDE, bezt->h1, bezt->h2))) { /* handles need to be updated during animation and applying stuff like hooks, diff --git a/source/blender/blenkernel/intern/curve_convert.c b/source/blender/blenkernel/intern/curve_convert.c index 5bcce9c339e..98a9cbc2bcf 100644 --- a/source/blender/blenkernel/intern/curve_convert.c +++ b/source/blender/blenkernel/intern/curve_convert.c @@ -26,9 +26,9 @@ #include "BKE_curve.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_lib_id.h" #include "BKE_modifier.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" diff --git a/source/blender/blenkernel/intern/curve_deform.c b/source/blender/blenkernel/intern/curve_deform.c index 28b7c2dfba0..b8b8506d681 100644 --- a/source/blender/blenkernel/intern/curve_deform.c +++ b/source/blender/blenkernel/intern/curve_deform.c @@ -311,7 +311,7 @@ static void curve_deform_coords_impl(const Object *ob_curve, } \ ((void)0) - /* already in 'cd.curvespace', prev for loop */ + /* Already in 'cd.curvespace', previous for loop. */ #define DEFORM_OP_CLAMPED(dvert) \ { \ const float weight = invert_vgroup ? 1.0f - BKE_defvert_find_weight(dvert, defgrp_index) : \ @@ -369,7 +369,7 @@ static void curve_deform_coords_impl(const Object *ob_curve, } for (a = 0; a < vert_coords_len; a++) { - /* already in 'cd.curvespace', prev for loop */ + /* Already in 'cd.curvespace', previous for loop. */ calc_curve_deform(ob_curve, vert_coords[a], defaxis, &cd, NULL); mul_m4_v3(cd.objectspace, vert_coords[a]); } diff --git a/source/blender/blenkernel/intern/curve_eval.cc b/source/blender/blenkernel/intern/curve_eval.cc index 1c4f9c5a6ab..bb745d5b20d 100644 --- a/source/blender/blenkernel/intern/curve_eval.cc +++ b/source/blender/blenkernel/intern/curve_eval.cc @@ -110,7 +110,7 @@ void CurveEval::bounds_min_max(float3 &min, float3 &max, const bool use_evaluate } /** - * Return the start indices for each of the curve spline's evaluated points, as if they were part + * Return the start indices for each of the curve spline's control points, if they were part * of a flattened array. This can be used to facilitate parallelism by avoiding the need to * accumulate an offset while doing more complex calculations. * @@ -143,6 +143,30 @@ blender::Array CurveEval::evaluated_point_offsets() const return offsets; } +/** + * Return the accumulated length at the start of every spline in the curve. + * + * \note The result is one longer than the spline count; the last element is the total length. + */ +blender::Array CurveEval::accumulated_spline_lengths() const +{ + Array spline_lengths(splines_.size() + 1); + float spline_length = 0.0f; + for (const int i : splines_.index_range()) { + spline_lengths[i] = spline_length; + spline_length += splines_[i]->length(); + } + spline_lengths.last() = spline_length; + return spline_lengths; +} + +void CurveEval::mark_cache_invalid() +{ + for (SplinePtr &spline : splines_) { + spline->mark_cache_invalid(); + } +} + static BezierSpline::HandleType handle_type_from_dna_bezt(const eBezTriple_Handle dna_handle_type) { switch (dna_handle_type) { @@ -324,6 +348,7 @@ std::unique_ptr curve_eval_from_dna_curve(const Curve &dna_curve) * because attributes are stored on splines rather than in a flat array on the curve: * - The same set of attributes exists on every spline. * - Attributes with the same name have the same type on every spline. + * - Attributes are in the same order on every spline. */ void CurveEval::assert_valid_point_attributes() const { @@ -332,25 +357,40 @@ void CurveEval::assert_valid_point_attributes() const return; } const int layer_len = splines_.first()->attributes.data.totlayer; - Map map; - for (const SplinePtr &spline : splines_) { - BLI_assert(spline->attributes.data.totlayer == layer_len); - spline->attributes.foreach_attribute( + + Array ids_in_order(layer_len); + Array meta_data_in_order(layer_len); + + { + int i = 0; + splines_.first()->attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - map.add_or_modify( - attribute_id, - [&](AttributeMetaData *map_data) { - /* All unique attribute names should be added on the first spline. */ - BLI_assert(spline == splines_.first()); - *map_data = meta_data; - }, - [&](AttributeMetaData *map_data) { - /* Attributes on different splines should all have the same type. */ - BLI_assert(meta_data == *map_data); - }); + ids_in_order[i] = attribute_id; + meta_data_in_order[i] = meta_data; + i++; return true; }, ATTR_DOMAIN_POINT); } + + for (const SplinePtr &spline : splines_) { + /* All splines should have the same number of attributes. */ + BLI_assert(spline->attributes.data.totlayer == layer_len); + + int i = 0; + spline->attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + /* Attribute names and IDs should have the same order and exist on all splines. */ + BLI_assert(attribute_id == ids_in_order[i]); + + /* Attributes with the same ID different splines should all have the same type. */ + BLI_assert(meta_data == meta_data_in_order[i]); + + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } + #endif } diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc new file mode 100644 index 00000000000..ed4af2bbb08 --- /dev/null +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -0,0 +1,784 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_spline.hh" + +#include "BKE_curve_to_mesh.hh" + +using blender::fn::GMutableSpan; +using blender::fn::GSpan; +using blender::fn::GVArray_Typed; +using blender::fn::GVArrayPtr; + +namespace blender::bke { + +/** Information about the creation of one curve spline and profile spline combination. */ +struct ResultInfo { + const Spline &spline; + const Spline &profile; + int vert_offset; + int edge_offset; + int loop_offset; + int poly_offset; + int spline_vert_len; + int spline_edge_len; + int profile_vert_len; + int profile_edge_len; +}; + +static void vert_extrude_to_mesh_data(const Spline &spline, + const float3 profile_vert, + MutableSpan r_verts, + MutableSpan r_edges, + const int vert_offset, + const int edge_offset) +{ + Span positions = spline.evaluated_positions(); + + for (const int i : IndexRange(positions.size() - 1)) { + MEdge &edge = r_edges[edge_offset + i]; + edge.v1 = vert_offset + i; + edge.v2 = vert_offset + i + 1; + edge.flag = ME_LOOSEEDGE; + } + + if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { + MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1]; + edge.v1 = vert_offset; + edge.v2 = vert_offset + positions.size() - 1; + edge.flag = ME_LOOSEEDGE; + } + + for (const int i : positions.index_range()) { + MVert &vert = r_verts[vert_offset + i]; + copy_v3_v3(vert.co, positions[i] + profile_vert); + } +} + +static void mark_edges_sharp(MutableSpan edges) +{ + for (MEdge &edge : edges) { + edge.flag |= ME_SHARP; + } +} + +static void spline_extrude_to_mesh_data(const ResultInfo &info, + const bool fill_caps, + MutableSpan r_verts, + MutableSpan r_edges, + MutableSpan r_loops, + MutableSpan r_polys) +{ + const Spline &spline = info.spline; + const Spline &profile = info.profile; + if (info.profile_vert_len == 1) { + vert_extrude_to_mesh_data(spline, + profile.evaluated_positions()[0], + r_verts, + r_edges, + info.vert_offset, + info.edge_offset); + return; + } + + /* Add the edges running along the length of the curve, starting at each profile vertex. */ + const int spline_edges_start = info.edge_offset; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; + + MEdge &edge = r_edges[profile_edge_offset + i_ring]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = next_ring_vert_offset + i_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Add the edges running along each profile ring. */ + const int profile_edges_start = spline_edges_start + + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + + const int ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; + + MEdge &edge = r_edges[ring_edge_offset + i_profile]; + edge.v1 = ring_vert_offset + i_profile; + edge.v2 = ring_vert_offset + i_next_profile; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Calculate poly and corner indices. */ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; + + const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; + const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; + + const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring; + const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring; + + const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len; + const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4; + + for (const int i_profile : IndexRange(info.profile_edge_len)) { + const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4; + const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; + + const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile; + const int next_spline_edge_start = spline_edges_start + + info.spline_edge_len * i_next_profile; + + MPoly &poly = r_polys[ring_poly_offset + i_profile]; + poly.loopstart = ring_segment_loop_offset; + poly.totloop = 4; + poly.flag = ME_SMOOTH; + + MLoop &loop_a = r_loops[ring_segment_loop_offset]; + loop_a.v = ring_vert_offset + i_profile; + loop_a.e = ring_edge_start + i_profile; + MLoop &loop_b = r_loops[ring_segment_loop_offset + 1]; + loop_b.v = ring_vert_offset + i_next_profile; + loop_b.e = next_spline_edge_start + i_ring; + MLoop &loop_c = r_loops[ring_segment_loop_offset + 2]; + loop_c.v = next_ring_vert_offset + i_next_profile; + loop_c.e = next_ring_edge_offset + i_profile; + MLoop &loop_d = r_loops[ring_segment_loop_offset + 3]; + loop_d.v = next_ring_vert_offset + i_profile; + loop_d.e = spline_edge_start + i_ring; + } + } + + if (fill_caps && profile.is_cyclic()) { + const int poly_size = info.spline_edge_len * info.profile_edge_len; + const int cap_loop_offset = info.loop_offset + poly_size * 4; + const int cap_poly_offset = info.poly_offset + poly_size; + + MPoly &poly_start = r_polys[cap_poly_offset]; + poly_start.loopstart = cap_loop_offset; + poly_start.totloop = info.profile_edge_len; + MPoly &poly_end = r_polys[cap_poly_offset + 1]; + poly_end.loopstart = cap_loop_offset + info.profile_edge_len; + poly_end.totloop = info.profile_edge_len; + + const int last_ring_index = info.spline_vert_len - 1; + const int last_ring_vert_offset = info.vert_offset + info.profile_vert_len * last_ring_index; + const int last_ring_edge_offset = profile_edges_start + + info.profile_edge_len * last_ring_index; + + for (const int i : IndexRange(info.profile_edge_len)) { + const int i_inv = info.profile_edge_len - i - 1; + MLoop &loop_start = r_loops[cap_loop_offset + i]; + loop_start.v = info.vert_offset + i_inv; + loop_start.e = profile_edges_start + ((i == (info.profile_edge_len - 1)) ? + (info.profile_edge_len - 1) : + (i_inv - 1)); + MLoop &loop_end = r_loops[cap_loop_offset + info.profile_edge_len + i]; + loop_end.v = last_ring_vert_offset + i; + loop_end.e = last_ring_edge_offset + i; + } + + mark_edges_sharp(r_edges.slice(profile_edges_start, info.profile_edge_len)); + mark_edges_sharp(r_edges.slice(last_ring_edge_offset, info.profile_edge_len)); + } + + /* Calculate the positions of each profile ring profile along the spline. */ + Span positions = spline.evaluated_positions(); + Span tangents = spline.evaluated_tangents(); + Span normals = spline.evaluated_normals(); + Span profile_positions = profile.evaluated_positions(); + + GVArray_Typed radii = spline.interpolate_to_evaluated(spline.radii()); + for (const int i_ring : IndexRange(info.spline_vert_len)) { + float4x4 point_matrix = float4x4::from_normalized_axis_data( + positions[i_ring], normals[i_ring], tangents[i_ring]); + point_matrix.apply_scale(radii[i_ring]); + + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + MVert &vert = r_verts[ring_vert_start + i_profile]; + copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); + } + } + + /* Mark edge loops from sharp vector control points sharp. */ + if (profile.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast(profile); + Span control_point_offsets = bezier_spline.control_point_offsets(); + for (const int i : IndexRange(bezier_spline.size())) { + if (bezier_spline.point_is_sharp(i)) { + mark_edges_sharp( + r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i], + info.spline_edge_len)); + } + } + } +} + +static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile) +{ + return curve.evaluated_points_size() * profile.evaluated_points_size(); +} + +static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile) +{ + /* Add the ring edges, with one ring for every curve vertex, and the edge loops + * that run along the length of the curve, starting on the first profile. */ + return curve.evaluated_points_size() * profile.evaluated_edges_size() + + curve.evaluated_edges_size() * profile.evaluated_points_size(); +} + +static inline int spline_extrude_loop_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) +{ + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; + const int caps = (fill_caps && profile.is_cyclic()) ? profile.evaluated_edges_size() * 2 : 0; + return tube + caps; +} + +static inline int spline_extrude_poly_size(const Spline &curve, + const Spline &profile, + const bool fill_caps) +{ + const int tube = curve.evaluated_edges_size() * profile.evaluated_edges_size(); + const int caps = (fill_caps && profile.is_cyclic()) ? 2 : 0; + return tube + caps; +} + +struct ResultOffsets { + Array vert; + Array edge; + Array loop; + Array poly; +}; +static ResultOffsets calculate_result_offsets(Span profiles, + Span curves, + const bool fill_caps) +{ + const int total = profiles.size() * curves.size(); + Array vert(total + 1); + Array edge(total + 1); + Array loop(total + 1); + Array poly(total + 1); + + int mesh_index = 0; + int vert_offset = 0; + int edge_offset = 0; + int loop_offset = 0; + int poly_offset = 0; + for (const int i_spline : curves.index_range()) { + for (const int i_profile : profiles.index_range()) { + vert[mesh_index] = vert_offset; + edge[mesh_index] = edge_offset; + loop[mesh_index] = loop_offset; + poly[mesh_index] = poly_offset; + vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]); + edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]); + loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile], fill_caps); + poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile], fill_caps); + mesh_index++; + } + } + vert.last() = vert_offset; + edge.last() = edge_offset; + loop.last() = loop_offset; + poly.last() = poly_offset; + + return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)}; +} + +static AttributeDomain get_result_attribute_domain(const MeshComponent &component, + const AttributeIDRef &attribute_id) +{ + /* Only use a different domain if it is builtin and must only exist on one domain. */ + if (!component.attribute_is_builtin(attribute_id)) { + return ATTR_DOMAIN_POINT; + } + + std::optional meta_data = component.attribute_get_meta_data(attribute_id); + if (!meta_data) { + /* This function has to return something in this case, but it shouldn't be used, + * so return an output that will assert later if the code attempts to handle it. */ + return ATTR_DOMAIN_AUTO; + } + + return meta_data->domain; +} + +/** + * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling + * `as_span()` for every single profile and curve spline combination, and for readability. + */ +struct ResultAttributeData { + GMutableSpan data; + AttributeDomain domain; +}; + +static std::optional create_attribute_and_get_span( + MeshComponent &component, + const AttributeIDRef &attribute_id, + AttributeMetaData meta_data, + Vector &r_attributes) +{ + const AttributeDomain domain = get_result_attribute_domain(component, attribute_id); + OutputAttribute attribute = component.attribute_try_get_for_output_only( + attribute_id, domain, meta_data.data_type); + if (!attribute) { + return std::nullopt; + } + + GMutableSpan span = attribute.as_span(); + r_attributes.append(std::move(attribute)); + return std::make_optional({span, domain}); +} + +/** + * Store the references to the attribute data from the curve and profile inputs. Here we rely on + * the invariants of the storage of curve attributes, that the order will be consistent between + * splines, and all splines will have the same attributes. + */ +struct ResultAttributes { + /** + * Result attributes on the mesh corresponding to each attribute on the curve input, in the same + * order. The data is optional only in case the attribute does not exist on the mesh for some + * reason, like "shade_smooth" when the result has no faces. + */ + Vector> curve_point_attributes; + Vector> curve_spline_attributes; + + /** + * Result attributes corresponding the attributes on the profile input, in the same order. The + * attributes are optional in case the attribute names correspond to a names used by the curve + * input, in which case the curve input attributes take precedence. + */ + Vector> profile_point_attributes; + Vector> profile_spline_attributes; + + /** + * Because some builtin attributes are not stored contiguously, and the curve inputs might have + * attributes with those names, it's necessary to keep OutputAttributes around to give access to + * the result data in a contiguous array. + */ + Vector attributes; +}; +static ResultAttributes create_result_attributes(const CurveEval &curve, + const CurveEval &profile, + Mesh &mesh) +{ + MeshComponent mesh_component; + mesh_component.replace(&mesh, GeometryOwnershipType::Editable); + Set curve_attributes; + + /* In order to prefer attributes on the main curve input when there are name collisions, first + * check the attributes on the curve, then add attributes on the profile that are not also on the + * main curve input. */ + ResultAttributes result; + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_POINT); + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + curve_attributes.add_new(id); + result.curve_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + return true; + }, + ATTR_DOMAIN_CURVE); + profile.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_point_attributes.append({}); + } + else { + result.profile_point_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_POINT); + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (curve_attributes.contains(id)) { + result.profile_spline_attributes.append({}); + } + else { + result.profile_spline_attributes.append( + create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); + } + return true; + }, + ATTR_DOMAIN_CURVE); + + return result; +} + +template +static void copy_curve_point_data_to_mesh_verts(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]); + } +} + +template +static void copy_curve_point_data_to_mesh_edges(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len; + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int ring_edge_start = edges_start + info.profile_edge_len * i_ring; + dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +template +static void copy_curve_point_data_to_mesh_faces(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring; + dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]); + } +} + +static void copy_curve_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_curve_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_curve_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_curve_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +template +static void copy_profile_point_data_to_mesh_verts(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_vert_len)) { + const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len; + for (const int i_profile : IndexRange(info.profile_vert_len)) { + dst[profile_vert_start + i_profile] = src[i_profile]; + } + } +} + +template +static void copy_profile_point_data_to_mesh_edges(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_profile : IndexRange(info.profile_vert_len)) { + const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len; + dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]); + } +} + +template +static void copy_profile_point_data_to_mesh_faces(const Span src, + const ResultInfo &info, + MutableSpan dst) +{ + for (const int i_ring : IndexRange(info.spline_edge_len)) { + const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len; + for (const int i_profile : IndexRange(info.profile_edge_len)) { + dst[profile_face_start + i_profile] = src[i_profile]; + } + } +} + +static void copy_profile_point_attribute_to_mesh(const GSpan src, + const ResultInfo &info, + ResultAttributeData &dst) +{ + GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src); + GSpan interpolated = interpolated_gvarray->get_internal_span(); + + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst.domain) { + case ATTR_DOMAIN_POINT: + copy_profile_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_profile_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_profile_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + /* Unsupported for now, since there are no builtin attributes to convert into. */ + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_point_domain_attributes_to_mesh(const ResultInfo &info, + ResultAttributes &attributes) +{ + if (!attributes.curve_point_attributes.is_empty()) { + int i = 0; + info.spline.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_point_attributes[i]) { + copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id), + info, + *attributes.curve_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } + if (!attributes.profile_point_attributes.is_empty()) { + int i = 0; + info.profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_point_attributes[i]) { + copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id), + info, + *attributes.profile_point_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_POINT); + } +} + +template +static void copy_spline_data_to_mesh(Span src, Span offsets, MutableSpan dst) +{ + for (const int i : IndexRange(src.size())) { + dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); + } +} + +/** + * Since the offsets for each combination of curve and profile spline are stored for every mesh + * domain, and this just needs to fill the chunks corresponding to each combination, we can use + * the same function for all mesh domains. + */ +static void copy_spline_attribute_to_mesh(const GSpan src, + const ResultOffsets &offsets, + ResultAttributeData &dst_attribute) +{ + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + switch (dst_attribute.domain) { + case ATTR_DOMAIN_POINT: + copy_spline_data_to_mesh(src.typed(), offsets.vert, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_EDGE: + copy_spline_data_to_mesh(src.typed(), offsets.edge, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_FACE: + copy_spline_data_to_mesh(src.typed(), offsets.poly, dst_attribute.data.typed()); + break; + case ATTR_DOMAIN_CORNER: + copy_spline_data_to_mesh(src.typed(), offsets.loop, dst_attribute.data.typed()); + break; + default: + BLI_assert_unreachable(); + break; + } + }); +} + +static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve, + const CurveEval &profile, + const ResultOffsets &offsets, + ResultAttributes &attributes) +{ + if (!attributes.curve_spline_attributes.is_empty()) { + int i = 0; + curve.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.curve_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id), + offsets, + *attributes.curve_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } + if (!attributes.profile_spline_attributes.is_empty()) { + int i = 0; + profile.attributes.foreach_attribute( + [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { + if (attributes.profile_spline_attributes[i]) { + copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id), + offsets, + *attributes.profile_spline_attributes[i]); + } + i++; + return true; + }, + ATTR_DOMAIN_CURVE); + } +} + +/** + * Extrude all splines in the profile curve along the path of every spline in the curve input. + * Transfer curve attributes to the mesh. + * + * \note Normal calculation is by far the slowest part of calculations relating to the result mesh. + * Although it would be a sensible decision to use the better topology information available while + * generating the mesh to also generate the normals, that work may wasted if the output mesh is + * changed anyway in a way that affects the normals. So currently this code uses the safer / + * simpler solution of deferring normal calculation to the rest of Blender. + */ +Mesh *curve_to_mesh_sweep(const CurveEval &curve, const CurveEval &profile, const bool fill_caps) +{ + Span profiles = profile.splines(); + Span curves = curve.splines(); + + const ResultOffsets offsets = calculate_result_offsets(profiles, curves, fill_caps); + if (offsets.vert.last() == 0) { + return nullptr; + } + + Mesh *mesh = BKE_mesh_new_nomain( + offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last()); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + mesh->flag |= ME_AUTOSMOOTH; + mesh->smoothresh = DEG2RADF(180.0f); + BKE_mesh_normals_tag_dirty(mesh); + + ResultAttributes attributes = create_result_attributes(curve, profile, *mesh); + + threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { + for (const int i_spline : curves_range) { + const Spline &spline = *curves[i_spline]; + if (spline.evaluated_points_size() == 0) { + continue; + } + const int spline_start_index = i_spline * profiles.size(); + threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) { + for (const int i_profile : profiles_range) { + const Spline &profile = *profiles[i_profile]; + const int i_mesh = spline_start_index + i_profile; + ResultInfo info{ + spline, + profile, + offsets.vert[i_mesh], + offsets.edge[i_mesh], + offsets.loop[i_mesh], + offsets.poly[i_mesh], + spline.evaluated_points_size(), + spline.evaluated_edges_size(), + profile.evaluated_points_size(), + profile.evaluated_edges_size(), + }; + + spline_extrude_to_mesh_data(info, + fill_caps, + {mesh->mvert, mesh->totvert}, + {mesh->medge, mesh->totedge}, + {mesh->mloop, mesh->totloop}, + {mesh->mpoly, mesh->totpoly}); + + copy_point_domain_attributes_to_mesh(info, attributes); + } + }); + } + }); + + copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes); + + for (OutputAttribute &output_attribute : attributes.attributes) { + output_attribute.save(); + } + + return mesh; +} + +static CurveEval get_curve_single_vert() +{ + CurveEval curve; + std::unique_ptr spline = std::make_unique(); + spline->add_point(float3(0), 0, 0.0f); + curve.add_spline(std::move(spline)); + + return curve; +} + +/** + * Create a loose-edge mesh based on the evaluated path of the curve's splines. + * Transfer curve attributes to the mesh. + */ +Mesh *curve_to_wire_mesh(const CurveEval &curve) +{ + static const CurveEval vert_curve = get_curve_single_vert(); + return curve_to_mesh_sweep(curve, vert_curve, false); +} + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/curveprofile.c b/source/blender/blenkernel/intern/curveprofile.cc similarity index 89% rename from source/blender/blenkernel/intern/curveprofile.c rename to source/blender/blenkernel/intern/curveprofile.cc index 00cdc7b3031..7f2a2bc342d 100644 --- a/source/blender/blenkernel/intern/curveprofile.c +++ b/source/blender/blenkernel/intern/curveprofile.cc @@ -21,28 +21,44 @@ * \ingroup bke */ -#include -#include -#include -#include +#include #include "MEM_guardedalloc.h" #include "DNA_curve_types.h" #include "DNA_curveprofile_types.h" -#include "BLI_blenlib.h" -#include "BLI_math.h" -#include "BLI_task.h" -#include "BLI_threads.h" +#include "BLI_math_vector.h" +#include "BLI_rect.h" #include "BLI_utildefines.h" #include "BKE_curve.h" #include "BKE_curveprofile.h" -#include "BKE_fcurve.h" #include "BLO_read_write.h" +/** Number of points in high resolution table is dynamic up to a maximum. */ +#define PROF_TABLE_MAX 512 + +/* -------------------------------------------------------------------- */ +/** \name Data Handling + * \{ */ + +/** + * Returns a pointer to a newly allocated curve profile, using the given preset. + */ +struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset) +{ + CurveProfile *profile = (CurveProfile *)MEM_callocN(sizeof(CurveProfile), __func__); + + BKE_curveprofile_set_defaults(profile); + profile->preset = preset; + BKE_curveprofile_reset(profile); + BKE_curveprofile_update(profile, 0); + + return profile; +} + void BKE_curveprofile_free_data(CurveProfile *profile) { MEM_SAFE_FREE(profile->path); @@ -62,9 +78,9 @@ void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profil { *target = *profile; - target->path = MEM_dupallocN(profile->path); - target->table = MEM_dupallocN(profile->table); - target->segments = MEM_dupallocN(profile->segments); + target->path = (CurveProfilePoint *)MEM_dupallocN(profile->path); + target->table = (CurveProfilePoint *)MEM_dupallocN(profile->table); + target->segments = (CurveProfilePoint *)MEM_dupallocN(profile->segments); /* Update the reference the points have to the profile. */ for (int i = 0; i < target->path_len; i++) { @@ -75,13 +91,40 @@ void BKE_curveprofile_copy_data(CurveProfile *target, const CurveProfile *profil CurveProfile *BKE_curveprofile_copy(const CurveProfile *profile) { if (profile) { - CurveProfile *new_prdgt = MEM_dupallocN(profile); + CurveProfile *new_prdgt = (CurveProfile *)MEM_dupallocN(profile); BKE_curveprofile_copy_data(new_prdgt, profile); return new_prdgt; } - return NULL; + return nullptr; } +void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile) +{ + BLO_write_struct(writer, CurveProfile, profile); + BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path); +} + +/* Expects that the curve profile itself has been read already. */ +void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile) +{ + BLO_read_data_address(reader, &profile->path); + profile->table = nullptr; + profile->segments = nullptr; + + /* Reset the points' pointers to the profile. */ + for (int i = 0; i < profile->path_len; i++) { + profile->path[i].profile = profile; + } + + BKE_curveprofile_init(profile, profile->segments_len); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Editing + * \{ */ + /** * Move a point's handle, accounting for the alignment of handles with the #HD_ALIGN type. * @@ -201,8 +244,8 @@ bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *poi return false; } - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); int i_delete = (int)(point - profile->path); BLI_assert(i_delete > 0); @@ -229,8 +272,8 @@ bool BKE_curveprofile_remove_point(CurveProfile *profile, CurveProfilePoint *poi void BKE_curveprofile_remove_by_flag(CurveProfile *profile, const short flag) { /* Copy every point without the flag into the new path. */ - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); /* Build the new list without any of the points with the flag. Keep the first and last points. */ int i_new = 1; @@ -278,7 +321,7 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float /* Don't add more control points than the maximum size of the higher resolution table. */ if (profile->path_len == PROF_TABLE_MAX - 1) { - return NULL; + return nullptr; } /* Find the index at the line segment that's closest to the new position. */ @@ -297,9 +340,9 @@ CurveProfilePoint *BKE_curveprofile_insert(CurveProfile *profile, float x, float /* Insert the new point at the location we found and copy all of the old points in as well. */ profile->path_len++; - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); - CurveProfilePoint *new_pt = NULL; + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); + CurveProfilePoint *new_pt = nullptr; for (int i_new = 0, i_old = 0; i_new < profile->path_len; i_new++) { if (i_new != i_insert) { /* Insert old points. */ @@ -341,7 +384,7 @@ void BKE_curveprofile_selected_handle_set(CurveProfile *profile, int type_1, int if (type_1 == HD_ALIGN && type_2 == HD_ALIGN) { /* Align the handles. */ - BKE_curveprofile_move_handle(&profile->path[i], true, false, NULL); + BKE_curveprofile_move_handle(&profile->path[i], true, false, nullptr); } } } @@ -365,8 +408,8 @@ void BKE_curveprofile_reverse(CurveProfile *profile) if (profile->path_len == 2) { return; } - CurveProfilePoint *new_path = MEM_mallocN(sizeof(CurveProfilePoint) * profile->path_len, - "profile path"); + CurveProfilePoint *new_path = (CurveProfilePoint *)MEM_mallocN( + sizeof(CurveProfilePoint) * profile->path_len, __func__); /* Mirror the new points across the y = x line */ for (int i = 0; i < profile->path_len; i++) { int i_reversed = profile->path_len - i - 1; @@ -452,7 +495,7 @@ void BKE_curveprofile_reset(CurveProfile *profile) { MEM_SAFE_FREE(profile->path); - eCurveProfilePresets preset = profile->preset; + eCurveProfilePresets preset = static_cast(profile->preset); switch (preset) { case PROF_PRESET_LINE: profile->path_len = 2; @@ -485,7 +528,8 @@ void BKE_curveprofile_reset(CurveProfile *profile) break; } - profile->path = MEM_callocN(sizeof(CurveProfilePoint) * profile->path_len, "profile path"); + profile->path = (CurveProfilePoint *)MEM_callocN(sizeof(CurveProfilePoint) * profile->path_len, + __func__); switch (preset) { case PROF_PRESET_LINE: @@ -536,7 +580,22 @@ void BKE_curveprofile_reset(CurveProfile *profile) } MEM_SAFE_FREE(profile->table); - profile->table = NULL; + profile->table = nullptr; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sampling and Evaluation + * \{ */ + +int BKE_curveprofile_table_size(const CurveProfile *profile) +{ + /** Number of table points per control point. */ + const int resolution = 16; + + /* Make sure there is always one sample, even if there are no control points. */ + return std::clamp((profile->path_len - 1) * resolution + 1, 1, PROF_TABLE_MAX); } /** @@ -564,7 +623,7 @@ static void point_calculate_handle(CurveProfilePoint *point, float pt[2]; const float *prev_loc, *next_loc; - if (prev == NULL) { + if (prev == nullptr) { next_loc = &next->x; pt[0] = 2.0f * point_loc[0] - next_loc[0]; pt[1] = 2.0f * point_loc[1] - next_loc[1]; @@ -574,7 +633,7 @@ static void point_calculate_handle(CurveProfilePoint *point, prev_loc = &prev->x; } - if (next == NULL) { + if (next == nullptr) { prev_loc = &prev->x; pt[0] = 2.0f * point_loc[0] - prev_loc[0]; pt[1] = 2.0f * point_loc[1] - prev_loc[1]; @@ -625,15 +684,15 @@ static void point_calculate_handle(CurveProfilePoint *point, static void calculate_path_handles(CurveProfilePoint *path, int path_len) { - point_calculate_handle(&path[0], NULL, &path[1]); + point_calculate_handle(&path[0], nullptr, &path[1]); for (int i = 1; i < path_len - 1; i++) { point_calculate_handle(&path[i], &path[i - 1], &path[i + 1]); } - point_calculate_handle(&path[path_len - 1], &path[path_len - 2], NULL); + point_calculate_handle(&path[path_len - 1], &path[path_len - 2], nullptr); } /** - * Helper function for 'BKE_curveprofile_create_samples.' Calculates the angle between the + * Helper function for #create_samples. Calculates the angle between the * handles on the inside of the edge starting at index i. A larger angle means the edge is * more curved. * \param i_edge: The start index of the edge to calculate the angle for. @@ -651,15 +710,15 @@ static float bezt_edge_handle_angle(const CurveProfilePoint *path, int i_edge) } /** Struct to sort curvature of control point edges. */ -typedef struct { +struct CurvatureSortPoint { /** The index of the corresponding profile point. */ int point_index; /** The curvature of the edge with the above index. */ float point_curvature; -} CurvatureSortPoint; +}; /** - * Helper function for 'BKE_curveprofile_create_samples' for sorting edges based on curvature. + * Helper function for #create_samples for sorting edges based on curvature. */ static int sort_points_curvature(const void *in_a, const void *in_b) { @@ -686,10 +745,10 @@ static int sort_points_curvature(const void *in_a, const void *in_b) * n_segments. Fill the array with the sampled locations and if the point corresponds to a * control point, its handle type. */ -void BKE_curveprofile_create_samples(CurveProfile *profile, - int n_segments, - bool sample_straight_edges, - CurveProfilePoint *r_samples) +static void create_samples(CurveProfile *profile, + int n_segments, + bool sample_straight_edges, + CurveProfilePoint *r_samples) { CurveProfilePoint *path = profile->path; int totpoints = profile->path_len; @@ -700,8 +759,8 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, calculate_path_handles(path, totpoints); /* Create a list of edge indices with the most curved at the start, least curved at the end. */ - CurvatureSortPoint *curve_sorted = MEM_callocN(sizeof(CurvatureSortPoint) * totedges, - "curve sorted"); + CurvatureSortPoint *curve_sorted = (CurvatureSortPoint *)MEM_callocN( + sizeof(CurvatureSortPoint) * totedges, __func__); for (int i = 0; i < totedges; i++) { curve_sorted[i].point_index = i; /* Calculate the curvature of each edge once for use when sorting for curvature. */ @@ -710,7 +769,7 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, qsort(curve_sorted, totedges, sizeof(CurvatureSortPoint), sort_points_curvature); /* Assign the number of sampled points for each edge. */ - int16_t *n_samples = MEM_callocN(sizeof(int16_t) * totedges, "samples numbers"); + int16_t *n_samples = (int16_t *)MEM_callocN(sizeof(int16_t) * totedges, "samples numbers"); int n_added = 0; int n_left; if (n_segments >= totedges) { @@ -812,17 +871,146 @@ void BKE_curveprofile_create_samples(CurveProfile *profile, MEM_freeN(n_samples); } +/** + * Sets the default settings and clip range for the profile widget. + * Does not generate either table. + */ +void BKE_curveprofile_set_defaults(CurveProfile *profile) +{ + profile->flag = PROF_USE_CLIP; + + BLI_rctf_init(&profile->view_rect, 0.0f, 1.0f, 0.0f, 1.0f); + profile->clip_rect = profile->view_rect; + + profile->path_len = 2; + profile->path = (CurveProfilePoint *)MEM_callocN(2 * sizeof(CurveProfilePoint), __func__); + + profile->path[0].x = 1.0f; + profile->path[0].y = 0.0f; + profile->path[0].profile = profile; + profile->path[1].x = 1.0f; + profile->path[1].y = 1.0f; + profile->path[1].profile = profile; + + profile->changed_timestamp = 0; +} + +/** + * Refreshes the higher resolution table sampled from the input points. A call to this or + * #BKE_curveprofile_update is needed before evaluation functions that use the table. + * Also sets the number of segments used for the display preview of the locations + * of the sampled points. + */ +void BKE_curveprofile_init(CurveProfile *profile, short segments_len) +{ + if (segments_len != profile->segments_len) { + profile->flag |= PROF_DIRTY_PRESET; + } + profile->segments_len = segments_len; + + /* Calculate the higher resolution / segments tables for display and evaluation. */ + BKE_curveprofile_update(profile, PROF_UPDATE_NONE); +} + +/** + * Gives the distance to the next point in the widgets sampled table, in other words the length + * of the \a 'i' edge of the table. + * + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. + */ +static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i) +{ + BLI_assert(i < BKE_curveprofile_table_size(profile)); + + return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); +} + +/** + * Calculates the total length of the profile from the curves sampled in the table. + * + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. + */ +static float curveprofile_total_length(const CurveProfile *profile) +{ + float total_length = 0; + for (int i = 0; i < BKE_curveprofile_table_size(profile) - 1; i++) { + total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); + } + return total_length; +} + +/** + * Samples evenly spaced positions along the curve profile's table (generated from path). Fills + * an entire table at once for a speedup if all of the results are going to be used anyway. + * + * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. + * \note Working, but would conflict with "Sample Straight Edges" option, so this is unused for + * now. + */ +static void create_samples_even_spacing(CurveProfile *profile, + int n_segments, + CurveProfilePoint *r_samples) +{ + const float total_length = curveprofile_total_length(profile); + const float segment_length = total_length / n_segments; + float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0); + float distance_to_previous_table_point = 0.0f; + int i_table = 0; + + /* Set the location for the first point. */ + r_samples[0].x = profile->table[0].x; + r_samples[0].y = profile->table[0].y; + + /* Travel along the path, recording the locations of segments as we pass them. */ + float segment_left = segment_length; + for (int i = 1; i < n_segments; i++) { + /* Travel over all of the points that fit inside this segment. */ + while (distance_to_next_table_point < segment_left) { + segment_left -= distance_to_next_table_point; + i_table++; + distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table); + distance_to_previous_table_point = 0.0f; + } + /* We're at the last table point that fits inside the current segment, use interpolation. */ + float factor = (distance_to_previous_table_point + segment_left) / + (distance_to_previous_table_point + distance_to_next_table_point); + r_samples[i].x = interpf(profile->table[i_table + 1].x, profile->table[i_table].x, factor); + r_samples[i].y = interpf(profile->table[i_table + 1].y, profile->table[i_table].y, factor); + BLI_assert(factor <= 1.0f && factor >= 0.0f); +#ifdef DEBUG_CURVEPROFILE_EVALUATE + printf("segment_left: %.3f\n", segment_left); + printf("i_table: %d\n", i_table); + printf("distance_to_previous_table_point: %.3f\n", distance_to_previous_table_point); + printf("distance_to_next_table_point: %.3f\n", distance_to_next_table_point); + printf("Interpolating with factor %.3f from (%.3f, %.3f) to (%.3f, %.3f)\n\n", + factor, + profile->table[i_table].x, + profile->table[i_table].y, + profile->table[i_table + 1].x, + profile->table[i_table + 1].y); +#endif + + /* We sampled in between this table point and the next, so the next travel step is smaller. */ + distance_to_next_table_point -= segment_left; + distance_to_previous_table_point += segment_left; + segment_left = segment_length; + } +} + /** * Creates a higher resolution table by sampling the curved points. * This table is used for display and evenly spaced evaluation. */ static void curveprofile_make_table(CurveProfile *profile) { - int n_samples = PROF_TABLE_LEN(profile->path_len); - CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1), - __func__); + int n_samples = BKE_curveprofile_table_size(profile); + CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( + sizeof(CurveProfilePoint) * (n_samples + 1), __func__); + + if (n_samples > 1) { + create_samples(profile, n_samples - 1, false, new_table); + } - BKE_curveprofile_create_samples(profile, n_samples - 1, false, new_table); /* Manually add last point at the end of the profile */ new_table[n_samples - 1].x = 0.0f; new_table[n_samples - 1].y = 1.0f; @@ -841,61 +1029,21 @@ static void curveprofile_make_segments_table(CurveProfile *profile) if (n_samples <= 0) { return; } - CurveProfilePoint *new_table = MEM_callocN(sizeof(CurveProfilePoint) * (n_samples + 1), - __func__); + CurveProfilePoint *new_table = (CurveProfilePoint *)MEM_callocN( + sizeof(CurveProfilePoint) * (n_samples + 1), __func__); if (profile->flag & PROF_SAMPLE_EVEN_LENGTHS) { /* Even length sampling incompatible with only straight edge sampling for now. */ - BKE_curveprofile_create_samples_even_spacing(profile, n_samples, new_table); + create_samples_even_spacing(profile, n_samples, new_table); } else { - BKE_curveprofile_create_samples( - profile, n_samples, profile->flag & PROF_SAMPLE_STRAIGHT_EDGES, new_table); + create_samples(profile, n_samples, profile->flag & PROF_SAMPLE_STRAIGHT_EDGES, new_table); } MEM_SAFE_FREE(profile->segments); profile->segments = new_table; } -/** - * Sets the default settings and clip range for the profile widget. - * Does not generate either table. - */ -void BKE_curveprofile_set_defaults(CurveProfile *profile) -{ - profile->flag = PROF_USE_CLIP; - - BLI_rctf_init(&profile->view_rect, 0.0f, 1.0f, 0.0f, 1.0f); - profile->clip_rect = profile->view_rect; - - profile->path_len = 2; - profile->path = MEM_callocN(2 * sizeof(CurveProfilePoint), "path points"); - - profile->path[0].x = 1.0f; - profile->path[0].y = 0.0f; - profile->path[0].profile = profile; - profile->path[1].x = 1.0f; - profile->path[1].y = 1.0f; - profile->path[1].profile = profile; - - profile->changed_timestamp = 0; -} - -/** - * Returns a pointer to a newly allocated curve profile, using the given preset. - */ -struct CurveProfile *BKE_curveprofile_add(eCurveProfilePresets preset) -{ - CurveProfile *profile = MEM_callocN(sizeof(CurveProfile), "curve profile"); - - BKE_curveprofile_set_defaults(profile); - profile->preset = preset; - BKE_curveprofile_reset(profile); - curveprofile_make_table(profile); - - return profile; -} - /** * Should be called after the widget is changed. Does profile and remove double checks and more * importantly, recreates the display / evaluation and segments tables. @@ -957,108 +1105,6 @@ void BKE_curveprofile_update(CurveProfile *profile, const int update_flags) } } -/** - * Refreshes the higher resolution table sampled from the input points. A call to this or - * #BKE_curveprofile_update is needed before evaluation functions that use the table. - * Also sets the number of segments used for the display preview of the locations - * of the sampled points. - */ -void BKE_curveprofile_init(CurveProfile *profile, short segments_len) -{ - if (segments_len != profile->segments_len) { - profile->flag |= PROF_DIRTY_PRESET; - } - profile->segments_len = segments_len; - - /* Calculate the higher resolution / segments tables for display and evaluation. */ - BKE_curveprofile_update(profile, PROF_UPDATE_NONE); -} - -/** - * Gives the distance to the next point in the widgets sampled table, in other words the length - * of the \a 'i' edge of the table. - * - * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. - */ -static float curveprofile_distance_to_next_table_point(const CurveProfile *profile, int i) -{ - BLI_assert(i < PROF_TABLE_LEN(profile->path_len)); - - return len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); -} - -/** - * Calculates the total length of the profile from the curves sampled in the table. - * - * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. - */ -float BKE_curveprofile_total_length(const CurveProfile *profile) -{ - float total_length = 0; - for (int i = 0; i < PROF_TABLE_LEN(profile->path_len) - 1; i++) { - total_length += len_v2v2(&profile->table[i].x, &profile->table[i + 1].x); - } - return total_length; -} - -/** - * Samples evenly spaced positions along the curve profile's table (generated from path). Fills - * an entire table at once for a speedup if all of the results are going to be used anyway. - * - * \note Requires #BKE_curveprofile_init or #BKE_curveprofile_update call before to fill table. - * \note Working, but would conflict with "Sample Straight Edges" option, so this is unused for - * now. - */ -void BKE_curveprofile_create_samples_even_spacing(CurveProfile *profile, - int n_segments, - CurveProfilePoint *r_samples) -{ - const float total_length = BKE_curveprofile_total_length(profile); - const float segment_length = total_length / n_segments; - float distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, 0); - float distance_to_previous_table_point = 0.0f; - int i_table = 0; - - /* Set the location for the first point. */ - r_samples[0].x = profile->table[0].x; - r_samples[0].y = profile->table[0].y; - - /* Travel along the path, recording the locations of segments as we pass them. */ - float segment_left = segment_length; - for (int i = 1; i < n_segments; i++) { - /* Travel over all of the points that fit inside this segment. */ - while (distance_to_next_table_point < segment_left) { - segment_left -= distance_to_next_table_point; - i_table++; - distance_to_next_table_point = curveprofile_distance_to_next_table_point(profile, i_table); - distance_to_previous_table_point = 0.0f; - } - /* We're at the last table point that fits inside the current segment, use interpolation. */ - float factor = (distance_to_previous_table_point + segment_left) / - (distance_to_previous_table_point + distance_to_next_table_point); - r_samples[i].x = interpf(profile->table[i_table + 1].x, profile->table[i_table].x, factor); - r_samples[i].y = interpf(profile->table[i_table + 1].y, profile->table[i_table].y, factor); - BLI_assert(factor <= 1.0f && factor >= 0.0f); -#ifdef DEBUG_CURVEPROFILE_EVALUATE - printf("segment_left: %.3f\n", segment_left); - printf("i_table: %d\n", i_table); - printf("distance_to_previous_table_point: %.3f\n", distance_to_previous_table_point); - printf("distance_to_next_table_point: %.3f\n", distance_to_next_table_point); - printf("Interpolating with factor %.3f from (%.3f, %.3f) to (%.3f, %.3f)\n\n", - factor, - profile->table[i_table].x, - profile->table[i_table].y, - profile->table[i_table + 1].x, - profile->table[i_table + 1].y); -#endif - - /* We sampled in between this table point and the next, so the next travel step is smaller. */ - distance_to_next_table_point -= segment_left; - distance_to_previous_table_point += segment_left; - segment_left = segment_length; - } -} - /** * Does a single evaluation along the profile's path. * Travels down (length_portion * path) length and returns the position at that point. @@ -1071,7 +1117,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float *x_out, float *y_out) { - const float total_length = BKE_curveprofile_total_length(profile); + const float total_length = curveprofile_total_length(profile); const float requested_length = length_portion * total_length; /* Find the last point along the path with a lower length portion than the input. */ @@ -1079,7 +1125,7 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, float length_travelled = 0.0f; while (length_travelled < requested_length) { /* Check if we reached the last point before the final one. */ - if (i == PROF_TABLE_LEN(profile->path_len) - 2) { + if (i == BKE_curveprofile_table_size(profile) - 2) { break; } float new_length = curveprofile_distance_to_next_table_point(profile, i); @@ -1110,23 +1156,4 @@ void BKE_curveprofile_evaluate_length_portion(const CurveProfile *profile, *y_out = interpf(profile->table[i].y, profile->table[i + 1].y, lerp_factor); } -void BKE_curveprofile_blend_write(struct BlendWriter *writer, const struct CurveProfile *profile) -{ - BLO_write_struct(writer, CurveProfile, profile); - BLO_write_struct_array(writer, CurveProfilePoint, profile->path_len, profile->path); -} - -/* Expects that the curve profile itself has been read already. */ -void BKE_curveprofile_blend_read(struct BlendDataReader *reader, struct CurveProfile *profile) -{ - BLO_read_data_address(reader, &profile->path); - profile->table = NULL; - profile->segments = NULL; - - /* Reset the points' pointers to the profile. */ - for (int i = 0; i < profile->path_len; i++) { - profile->path[i].profile = profile; - } - - BKE_curveprofile_init(profile, profile->segments_len); -} +/** \} */ diff --git a/source/blender/blenkernel/intern/customdata.c b/source/blender/blenkernel/intern/customdata.c index ad2d5d267d5..d86b8163ebc 100644 --- a/source/blender/blenkernel/intern/customdata.c +++ b/source/blender/blenkernel/intern/customdata.c @@ -1856,6 +1856,8 @@ static const LayerTypeInfo LAYERTYPEINFO[CD_NUMTYPES] = { NULL, NULL, NULL}, + /* 51: CD_HAIRLENGTH */ + {sizeof(float), "float", 1, NULL, NULL, NULL, NULL, NULL, NULL}, }; static const char *LAYERTYPENAMES[CD_NUMTYPES] = { @@ -1912,6 +1914,7 @@ static const char *LAYERTYPENAMES[CD_NUMTYPES] = { "CDPropFloat3", "CDPropFloat2", "CDPropBoolean", + "CDHairLength", }; const CustomData_MeshMasks CD_MASK_BAREMESH = { @@ -2592,6 +2595,11 @@ static CustomDataLayer *customData_add_layer__internal(CustomData *data, data->layers[index] = data->layers[index - 1]; } + /* Clear remaining data on the layer. The original data on the layer has been moved to another + * index. Without this, it can happen that information from the previous layer at that index + * leaks into the new layer. */ + memset(data->layers + index, 0, sizeof(CustomDataLayer)); + data->layers[index].type = type; data->layers[index].flag = flag; data->layers[index].data = newlayerdata; diff --git a/source/blender/blenkernel/intern/displist.cc b/source/blender/blenkernel/intern/displist.cc index e756daa1156..ebe00531e65 100644 --- a/source/blender/blenkernel/intern/displist.cc +++ b/source/blender/blenkernel/intern/displist.cc @@ -47,7 +47,6 @@ #include "BKE_anim_path.h" #include "BKE_curve.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_geometry_set.hh" #include "BKE_key.h" #include "BKE_lattice.h" @@ -58,6 +57,7 @@ #include "BKE_modifier.h" #include "BKE_object.h" #include "BKE_spline.hh" +#include "BKE_vfont.h" #include "BLI_sys_types.h" /* For #intptr_t support. */ @@ -261,7 +261,7 @@ bool BKE_displist_surfindex_get( return true; } -/* ****************** make displists ********************* */ +/* ****************** Make #DispList ********************* */ #ifdef __INTEL_COMPILER /* ICC with the optimization -02 causes crashes. */ # pragma intel optimization_level 1 @@ -1540,23 +1540,6 @@ void BKE_displist_make_curveTypes(Depsgraph *depsgraph, boundbox_displist_object(ob); } -void BKE_displist_make_curveTypes_forRender( - Depsgraph *depsgraph, const Scene *scene, Object *ob, ListBase *r_dispbase, Mesh **r_final) -{ - if (ob->runtime.curve_cache == nullptr) { - ob->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), __func__); - } - - if (ob->type == OB_SURF) { - evaluate_surface_object(depsgraph, scene, ob, true, r_dispbase, r_final); - } - else { - GeometrySet geometry_set = evaluate_curve_type_object(depsgraph, scene, ob, true, r_dispbase); - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - *r_final = mesh_component.release(); - } -} - void BKE_displist_minmax(const ListBase *dispbase, float min[3], float max[3]) { bool doit = false; diff --git a/source/blender/blenkernel/intern/dynamicpaint.c b/source/blender/blenkernel/intern/dynamicpaint.c index d75b3259148..2ef7ef91160 100644 --- a/source/blender/blenkernel/intern/dynamicpaint.c +++ b/source/blender/blenkernel/intern/dynamicpaint.c @@ -112,7 +112,7 @@ static int neighStraightY[8] = {0, 1, 0, -1, 1, 1, -1, -1}; #define SUBFRAME_RECURSION 5 /* surface_getBrushFlags() return vals */ #define BRUSH_USES_VELOCITY (1 << 0) -/* brush mesh raycast status */ +/* Brush mesh ray-cast status. */ #define HIT_VOLUME 1 #define HIT_PROXIMITY 2 /* dynamicPaint_findNeighborPixel() return codes */ @@ -317,7 +317,7 @@ static bool setError(DynamicPaintCanvasSettings *canvas, const char *string) static int dynamicPaint_surfaceNumOfPoints(DynamicPaintSurface *surface) { if (surface->format == MOD_DPAINT_SURFACE_F_PTEX) { - return 0; /* not supported atm */ + return 0; /* Not supported at the moment. */ } if (surface->format == MOD_DPAINT_SURFACE_F_VERTEX) { const Mesh *canvas_mesh = dynamicPaint_canvas_mesh_get(surface->canvas); @@ -1231,7 +1231,7 @@ void dynamicPaint_Modifier_copy(const struct DynamicPaintModifierData *pmd, /* copy existing surfaces */ for (surface = pmd->canvas->surfaces.first; surface; surface = surface->next) { DynamicPaintSurface *t_surface = dynamicPaint_createNewSurface(tpmd->canvas, NULL); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* TODO(sergey): Consider passing some tips to the surface * creation to avoid this allocate-and-free cache behavior. */ BKE_ptcache_free_list(&t_surface->ptcaches); @@ -3436,7 +3436,7 @@ void dynamicPaint_outputSurfaceImage(DynamicPaintSurface *surface, /***************************** Ray / Nearest Point Utils ******************************/ -/* A modified callback to bvh tree raycast. +/* A modified callback to bvh tree ray-cast. * The tree must have been built using bvhtree_from_mesh_looptri. * userdata must be a BVHMeshCallbackUserdata built from the same mesh as the tree. * @@ -4107,7 +4107,7 @@ static void dynamic_paint_paint_mesh_cell_point_cb_ex( hit.index = -1; hit.dist = brush_radius; - /* Do a face normal directional raycast, and use that distance */ + /* Do a face normal directional ray-cast, and use that distance. */ BLI_bvhtree_ray_cast( treeData->tree, ray_start, proj_ray, 0.0f, &hit, mesh_tris_spherecast_dp, treeData); if (hit.index != -1) { @@ -4597,7 +4597,7 @@ static bool dynamicPaint_paintParticles(DynamicPaintSurface *surface, } /* - * Build a kd-tree to optimize distance search + * Build a KD-tree to optimize distance search */ tree = BLI_kdtree_3d_new(psys->totpart); @@ -5883,8 +5883,7 @@ static void dynamic_paint_surface_pre_step_cb(void *__restrict userdata, } /* dissolve for float types */ else if (surface->flags & MOD_DPAINT_DISSOLVE && - (surface->type == MOD_DPAINT_SURFACE_T_DISPLACE || - surface->type == MOD_DPAINT_SURFACE_T_WEIGHT)) { + ELEM(surface->type, MOD_DPAINT_SURFACE_T_DISPLACE, MOD_DPAINT_SURFACE_T_WEIGHT)) { float *point = &((float *)sData->type_data)[index]; /* log or linear */ value_dissolve( diff --git a/source/blender/blenkernel/intern/editmesh_bvh.c b/source/blender/blenkernel/intern/editmesh_bvh.c index 087481b1b5d..5058863912f 100644 --- a/source/blender/blenkernel/intern/editmesh_bvh.c +++ b/source/blender/blenkernel/intern/editmesh_bvh.c @@ -221,7 +221,7 @@ static void bmbvh_tri_from_face(const float *cos[3], } } -/* taken from bvhutils.c */ +/* Taken from `bvhutils.c`. */ /* -------------------------------------------------------------------- */ /* BKE_bmbvh_ray_cast */ diff --git a/source/blender/blenkernel/intern/extern_implementations.cc b/source/blender/blenkernel/intern/extern_implementations.cc new file mode 100644 index 00000000000..07a4b6fc455 --- /dev/null +++ b/source/blender/blenkernel/intern/extern_implementations.cc @@ -0,0 +1,27 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_attribute_access.hh" + +namespace blender::bke { + +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; +template class OutputAttribute_Typed; + +} // namespace blender::bke diff --git a/source/blender/blenkernel/intern/fcurve.c b/source/blender/blenkernel/intern/fcurve.c index 8e9c504dcbf..bbf61c51bfb 100644 --- a/source/blender/blenkernel/intern/fcurve.c +++ b/source/blender/blenkernel/intern/fcurve.c @@ -203,14 +203,18 @@ void BKE_fcurve_foreach_id(FCurve *fcu, LibraryForeachIDData *data) switch (fcm->type) { case FMODIFIER_TYPE_PYTHON: { FMod_Python *fcm_py = (FMod_Python *)fcm->data; - BKE_LIB_FOREACHID_PROCESS(data, fcm_py->script, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fcm_py->script, IDWALK_CB_NOP); - IDP_foreach_property(fcm_py->prop, - IDP_TYPE_FILTER_ID, - BKE_lib_query_idpropertiesForeachIDLink_callback, - data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(fcm_py->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); break; } + default: + break; } } } diff --git a/source/blender/blenkernel/intern/fluid.c b/source/blender/blenkernel/intern/fluid.c index 1324b37f39c..6b7594dcf36 100644 --- a/source/blender/blenkernel/intern/fluid.c +++ b/source/blender/blenkernel/intern/fluid.c @@ -2676,7 +2676,7 @@ static void update_flowsflags(FluidDomainSettings *fds, Object **flowobjs, int n } /* Activate color field if flows add smoke with varying colors. */ if (ffs->density != 0.0 && - (ffs->type == FLUID_FLOW_TYPE_SMOKE || ffs->type == FLUID_FLOW_TYPE_SMOKEFIRE)) { + ELEM(ffs->type, FLUID_FLOW_TYPE_SMOKE, FLUID_FLOW_TYPE_SMOKEFIRE)) { if (!(active_fields & FLUID_DOMAIN_ACTIVE_COLOR_SET)) { copy_v3_v3(fds->active_color, ffs->color); active_fields |= FLUID_DOMAIN_ACTIVE_COLOR_SET; @@ -5094,7 +5094,7 @@ void BKE_fluid_modifier_copy(const struct FluidModifierData *fmd, /* pointcache options */ BKE_ptcache_free_list(&(tfds->ptcaches[0])); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* Share the cache with the original object's modifier. */ tfmd->modifier.flag |= eModifierFlag_SharedCaches; tfds->point_cache[0] = fds->point_cache[0]; diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index 7d0537178ef..d3c3fcc1e67 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -433,7 +433,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { return as_read_attribute_(*curve); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final { if (writable_ != Writable) { return {}; @@ -442,7 +442,7 @@ class BuiltinSplineAttributeProvider final : public BuiltinAttributeProvider { if (curve == nullptr) { return {}; } - return as_write_attribute_(*curve); + return {as_write_attribute_(*curve), domain_}; } bool try_delete(GeometryComponent &UNUSED(component)) const final @@ -535,6 +535,9 @@ static GVMutableArrayPtr make_cyclic_write_attribute(CurveEval &curve) * array implementations try to make it workable in common situations. * \{ */ +/** + * Individual spans in \a data may be empty if that spline contains no data for the attribute. + */ template static void point_attribute_materialize(Span> data, Span offsets, @@ -546,7 +549,15 @@ static void point_attribute_materialize(Span> data, for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; - r_span.slice(offset, next_offset - offset).copy_from(data[spline_index]); + + Span src = data[spline_index]; + MutableSpan dst = r_span.slice(offset, next_offset - offset); + if (src.is_empty()) { + dst.fill(T()); + } + else { + dst.copy_from(src); + } } } else { @@ -557,11 +568,20 @@ static void point_attribute_materialize(Span> data, } const int index_in_spline = dst_index - offsets[spline_index]; - r_span[dst_index] = data[spline_index][index_in_spline]; + Span src = data[spline_index]; + if (src.is_empty()) { + r_span[dst_index] = T(); + } + else { + r_span[dst_index] = src[index_in_spline]; + } } } } +/** + * Individual spans in \a data may be empty if that spline contains no data for the attribute. + */ template static void point_attribute_materialize_to_uninitialized(Span> data, Span offsets, @@ -574,7 +594,14 @@ static void point_attribute_materialize_to_uninitialized(Span> data, for (const int spline_index : data.index_range()) { const int offset = offsets[spline_index]; const int next_offset = offsets[spline_index + 1]; - uninitialized_copy_n(data[spline_index].data(), next_offset - offset, dst + offset); + + Span src = data[spline_index]; + if (src.is_empty()) { + uninitialized_fill_n(dst + offset, next_offset - offset, T()); + } + else { + uninitialized_copy_n(src.data(), next_offset - offset, dst + offset); + } } } else { @@ -585,11 +612,114 @@ static void point_attribute_materialize_to_uninitialized(Span> data, } const int index_in_spline = dst_index - offsets[spline_index]; - new (dst + dst_index) T(data[spline_index][index_in_spline]); + Span src = data[spline_index]; + if (src.is_empty()) { + new (dst + dst_index) T(); + } + else { + new (dst + dst_index) T(src[index_in_spline]); + } } } } +static GVArrayPtr varray_from_initializer(const AttributeInit &initializer, + const CustomDataType data_type, + const Span splines) +{ + switch (initializer.type) { + case AttributeInit::Type::Default: + /* This function shouldn't be called in this case, since there + * is no need to copy anything to the new custom data array. */ + BLI_assert_unreachable(); + return {}; + case AttributeInit::Type::VArray: + return static_cast(initializer).varray->shallow_copy(); + case AttributeInit::Type::MoveArray: + int total_size = 0; + for (const SplinePtr &spline : splines) { + total_size += spline->size(); + } + return std::make_unique( + GSpan(*bke::custom_data_type_to_cpp_type(data_type), + static_cast(initializer).data, + total_size)); + } + BLI_assert_unreachable(); + return {}; +} + +static bool create_point_attribute(GeometryComponent &component, + const AttributeIDRef &attribute_id, + const AttributeInit &initializer, + const CustomDataType data_type) +{ + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr || curve->splines().size() == 0) { + return false; + } + + MutableSpan splines = curve->splines(); + + /* First check the one case that allows us to avoid copying the input data. */ + if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { + void *source_data = static_cast(initializer).data; + if (!splines.first()->attributes.create_by_move(attribute_id, data_type, source_data)) { + MEM_freeN(source_data); + return false; + } + return true; + } + + /* Otherwise just create a custom data layer on each of the splines. */ + for (const int i : splines.index_range()) { + if (!splines[i]->attributes.create(attribute_id, data_type)) { + /* If attribute creation fails on one of the splines, we cannot leave the custom data + * layers in the previous splines around, so delete them before returning. However, + * this is not an expected case. */ + BLI_assert_unreachable(); + return false; + } + } + + /* With a default initializer type, we can keep the values at their initial values. */ + if (initializer.type == AttributeInit::Type::Default) { + return true; + } + + WriteAttributeLookup write_attribute = component.attribute_try_get_for_write(attribute_id); + /* We just created the attribute, it should exist. */ + BLI_assert(write_attribute); + + GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, splines); + /* TODO: When we can call a variant of #set_all with a virtual array argument, + * this theoretically unnecessary materialize step could be removed. */ + GVArray_GSpan source_varray_span{*source_varray}; + write_attribute.varray->set_all(source_varray_span.data()); + + if (initializer.type == AttributeInit::Type::MoveArray) { + MEM_freeN(static_cast(initializer).data); + } + + return true; +} + +static bool remove_point_attribute(GeometryComponent &component, + const AttributeIDRef &attribute_id) +{ + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr) { + return false; + } + + /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ + bool layer_freed = false; + for (SplinePtr &spline : curve->splines()) { + layer_freed = spline->attributes.remove(attribute_id); + } + return layer_freed; +} + /** * Virtual array for any control point data accessed with spans and an offset array. */ @@ -769,6 +899,169 @@ class VMutableArray_For_SplinePosition final : public VMutableArray { } }; +class VArray_For_BezierHandle final : public VArray { + private: + Span splines_; + Array offsets_; + bool is_right_; + + public: + VArray_For_BezierHandle(Span splines, Array offsets, const bool is_right) + : VArray(offsets.last()), + splines_(std::move(splines)), + offsets_(std::move(offsets)), + is_right_(is_right) + { + } + + static float3 get_internal(const int64_t index, + Span splines, + Span offsets, + const bool is_right) + { + const PointIndices indices = lookup_point_indices(offsets, index); + const Spline &spline = *splines[indices.spline_index]; + if (spline.type() == Spline::Type::Bezier) { + const BezierSpline &bezier_spline = static_cast(spline); + return is_right ? bezier_spline.handle_positions_right()[indices.point_index] : + bezier_spline.handle_positions_left()[indices.point_index]; + } + return float3(0); + } + + float3 get_impl(const int64_t index) const final + { + return get_internal(index, splines_, offsets_, is_right_); + } + + /** + * Utility so we can pass handle positions to the materialize functions above. + * + * \note This relies on the ability of the materialize implementations to + * handle empty spans, since only Bezier splines have handles. + */ + static Array> get_handle_spans(Span splines, const bool is_right) + { + Array> spans(splines.size()); + for (const int i : spans.index_range()) { + if (splines[i]->type() == Spline::Type::Bezier) { + BezierSpline &bezier_spline = static_cast(*splines[i]); + spans[i] = is_right ? bezier_spline.handle_positions_right() : + bezier_spline.handle_positions_left(); + } + else { + spans[i] = {}; + } + } + return spans; + } + + static void materialize_internal(const IndexMask mask, + Span splines, + Span offsets, + const bool is_right, + MutableSpan r_span) + { + Array> spans = get_handle_spans(splines, is_right); + point_attribute_materialize(spans.as_span(), offsets, mask, r_span); + } + + static void materialize_to_uninitialized_internal(const IndexMask mask, + Span splines, + Span offsets, + const bool is_right, + MutableSpan r_span) + { + Array> spans = get_handle_spans(splines, is_right); + point_attribute_materialize_to_uninitialized(spans.as_span(), offsets, mask, r_span); + } + + void materialize_impl(const IndexMask mask, MutableSpan r_span) const final + { + materialize_internal(mask, splines_, offsets_, is_right_, r_span); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, + MutableSpan r_span) const final + { + materialize_to_uninitialized_internal(mask, splines_, offsets_, is_right_, r_span); + } +}; + +class VMutableArray_For_BezierHandles final : public VMutableArray { + private: + MutableSpan splines_; + Array offsets_; + bool is_right_; + + public: + VMutableArray_For_BezierHandles(MutableSpan splines, + Array offsets, + const bool is_right) + : VMutableArray(offsets.last()), + splines_(splines), + offsets_(std::move(offsets)), + is_right_(is_right) + { + } + + float3 get_impl(const int64_t index) const final + { + return VArray_For_BezierHandle::get_internal(index, splines_, offsets_, is_right_); + } + + void set_impl(const int64_t index, float3 value) final + { + const PointIndices indices = lookup_point_indices(offsets_, index); + Spline &spline = *splines_[indices.spline_index]; + if (spline.type() == Spline::Type::Bezier) { + BezierSpline &bezier_spline = static_cast(spline); + if (is_right_) { + bezier_spline.set_handle_position_right(indices.point_index, value); + } + else { + bezier_spline.set_handle_position_left(indices.point_index, value); + } + bezier_spline.mark_cache_invalid(); + } + } + + void set_all_impl(Span src) final + { + for (const int spline_index : splines_.index_range()) { + Spline &spline = *splines_[spline_index]; + if (spline.type() == Spline::Type::Bezier) { + const int offset = offsets_[spline_index]; + + BezierSpline &bezier_spline = static_cast(spline); + if (is_right_) { + for (const int i : IndexRange(bezier_spline.size())) { + bezier_spline.set_handle_position_right(i, src[offset + i]); + } + } + else { + for (const int i : IndexRange(bezier_spline.size())) { + bezier_spline.set_handle_position_left(i, src[offset + i]); + } + } + bezier_spline.mark_cache_invalid(); + } + } + } + + void materialize_impl(const IndexMask mask, MutableSpan r_span) const final + { + VArray_For_BezierHandle::materialize_internal(mask, splines_, offsets_, is_right_, r_span); + } + + void materialize_to_uninitialized_impl(const IndexMask mask, + MutableSpan r_span) const final + { + VArray_For_BezierHandle::materialize_to_uninitialized_internal( + mask, splines_, offsets_, is_right_, r_span); + } +}; + /** * Provider for any builtin control point attribute that doesn't need * special handling like access to other arrays in the spline. @@ -781,22 +1074,26 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu const GetSpan get_span_; const GetMutableSpan get_mutable_span_; const UpdateOnWrite update_on_write_; + bool stored_in_custom_data_; public: BuiltinPointAttributeProvider(std::string attribute_name, - const WritableEnum writable, + const CreatableEnum creatable, + const DeletableEnum deletable, const GetSpan get_span, const GetMutableSpan get_mutable_span, - const UpdateOnWrite update_on_write) + const UpdateOnWrite update_on_write, + const bool stored_in_custom_data) : BuiltinAttributeProvider(std::move(attribute_name), ATTR_DOMAIN_POINT, bke::cpp_type_to_custom_data_type(CPPType::get()), - BuiltinAttributeProvider::NonCreatable, - writable, - BuiltinAttributeProvider::NonDeletable), + creatable, + WritableEnum::Writable, + deletable), get_span_(get_span), get_mutable_span_(get_mutable_span), - update_on_write_(update_on_write) + update_on_write_(update_on_write), + stored_in_custom_data_(stored_in_custom_data) { } @@ -807,6 +1104,10 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return {}; } + if (!this->exists(component)) { + return {}; + } + Span splines = curve->splines(); if (splines.size() == 1) { return std::make_unique(get_span_(*splines.first())); @@ -821,29 +1122,193 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu return point_data_gvarray(spans, offsets); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const override + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override { CurveEval *curve = get_curve_from_component_for_write(component); if (curve == nullptr) { return {}; } + if (!this->exists(component)) { + return {}; + } + + std::function tag_modified_fn; + if (update_on_write_ != nullptr) { + tag_modified_fn = [curve, update = update_on_write_]() { + for (SplinePtr &spline : curve->splines()) { + update(*spline); + } + }; + } + MutableSpan splines = curve->splines(); if (splines.size() == 1) { - return std::make_unique( - get_mutable_span_(*splines.first())); + return {std::make_unique( + get_mutable_span_(*splines.first())), + domain_, + std::move(tag_modified_fn)}; } Array offsets = curve->control_point_offsets(); Array> spans(splines.size()); for (const int i : splines.index_range()) { spans[i] = get_mutable_span_(*splines[i]); - if (update_on_write_) { - update_on_write_(*splines[i]); + } + + return {point_data_gvarray(spans, offsets), domain_, tag_modified_fn}; + } + + bool try_delete(GeometryComponent &component) const final + { + if (deletable_ == DeletableEnum::NonDeletable) { + return false; + } + return remove_point_attribute(component, name_); + } + + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final + { + if (createable_ == CreatableEnum::NonCreatable) { + return false; + } + return create_point_attribute(component, name_, initializer, CD_PROP_INT32); + } + + bool exists(const GeometryComponent &component) const final + { + const CurveEval *curve = get_curve_from_component_for_read(component); + if (curve == nullptr) { + return false; + } + + Span splines = curve->splines(); + if (splines.size() == 0) { + return false; + } + + if (stored_in_custom_data_) { + if (!curve->splines().first()->attributes.get_for_read(name_)) { + return false; } } - return point_data_gvarray(spans, offsets); + bool has_point = false; + for (const SplinePtr &spline : curve->splines()) { + if (spline->size() != 0) { + has_point = true; + break; + } + } + + if (!has_point) { + return false; + } + + return true; + } +}; + +/** + * Special attribute provider for the position attribute. Keeping this separate means we don't + * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the + * positions is more clear. + */ +class PositionAttributeProvider final : public BuiltinPointAttributeProvider { + public: + PositionAttributeProvider() + : BuiltinPointAttributeProvider( + "position", + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::NonDeletable, + [](const Spline &spline) { return spline.positions(); }, + [](Spline &spline) { return spline.positions(); }, + [](Spline &spline) { spline.mark_cache_invalid(); }, + false) + { + } + + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + { + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr) { + return {}; + } + + /* Use the regular position virtual array when there aren't any Bezier splines + * to avoid the overhead of checking the spline type for every point. */ + if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + return BuiltinPointAttributeProvider::try_get_for_write(component); + } + + auto tag_modified_fn = [curve]() { + /* Changing the positions requires recalculation of cached evaluated data in many cases. + * This could set more specific flags in the future to avoid unnecessary recomputation. */ + curve->mark_cache_invalid(); + }; + + Array offsets = curve->control_point_offsets(); + return {std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray>( + offsets.last(), curve->splines(), std::move(offsets)), + domain_, + tag_modified_fn}; + } +}; + +class BezierHandleAttributeProvider : public BuiltinAttributeProvider { + private: + bool is_right_; + + public: + BezierHandleAttributeProvider(const bool is_right) + : BuiltinAttributeProvider(is_right ? "handle_right" : "handle_left", + ATTR_DOMAIN_POINT, + CD_PROP_FLOAT3, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonDeletable), + is_right_(is_right) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const override + { + const CurveEval *curve = get_curve_from_component_for_read(component); + if (curve == nullptr) { + return {}; + } + + if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + return {}; + } + + Array offsets = curve->control_point_offsets(); + return std::make_unique>( + offsets.last(), curve->splines(), std::move(offsets), is_right_); + } + + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const override + { + CurveEval *curve = get_curve_from_component_for_write(component); + if (curve == nullptr) { + return {}; + } + + if (!curve->has_spline_with_type(Spline::Type::Bezier)) { + return {}; + } + + auto tag_modified_fn = [curve]() { curve->mark_cache_invalid(); }; + + Array offsets = curve->control_point_offsets(); + return { + std::make_unique< + fn::GVMutableArray_For_EmbeddedVMutableArray>( + offsets.last(), curve->splines(), std::move(offsets), is_right_), + domain_, + tag_modified_fn}; } bool try_delete(GeometryComponent &UNUSED(component)) const final @@ -859,50 +1324,13 @@ template class BuiltinPointAttributeProvider : public BuiltinAttribu bool exists(const GeometryComponent &component) const final { - return component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; - } -}; - -/** - * Special attribute provider for the position attribute. Keeping this separate means we don't - * need to make #BuiltinPointAttributeProvider overly generic, and the special handling for the - * positions is more clear. - */ -class PositionAttributeProvider final : public BuiltinPointAttributeProvider { - public: - PositionAttributeProvider() - : BuiltinPointAttributeProvider( - "position", - BuiltinAttributeProvider::Writable, - [](const Spline &spline) { return spline.positions(); }, - [](Spline &spline) { return spline.positions(); }, - [](Spline &spline) { spline.mark_cache_invalid(); }) - { - } - - GVMutableArrayPtr try_get_for_write(GeometryComponent &component) const final - { - CurveEval *curve = get_curve_from_component_for_write(component); + const CurveEval *curve = get_curve_from_component_for_read(component); if (curve == nullptr) { - return {}; + return false; } - /* Use the regular position virtual array when there aren't any Bezier splines - * to avoid the overhead of checking the spline type for every point. */ - if (!curve->has_spline_with_type(Spline::Type::Bezier)) { - return BuiltinPointAttributeProvider::try_get_for_write(component); - } - - /* Changing the positions requires recalculation of cached evaluated data in many cases. - * This could set more specific flags in the future to avoid unnecessary recomputation. */ - for (SplinePtr &spline : curve->splines()) { - spline->mark_cache_invalid(); - } - - Array offsets = curve->control_point_offsets(); - return std::make_unique< - fn::GVMutableArray_For_EmbeddedVMutableArray>( - offsets.last(), curve->splines(), std::move(offsets)); + return curve->has_spline_with_type(Spline::Type::Bezier) && + component.attribute_domain_size(ATTR_DOMAIN_POINT) != 0; } }; @@ -1031,39 +1459,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { bool try_delete(GeometryComponent &component, const AttributeIDRef &attribute_id) const final { - CurveEval *curve = get_curve_from_component_for_write(component); - if (curve == nullptr) { - return false; - } - - /* Reuse the boolean for all splines; we expect all splines to have the same attributes. */ - bool layer_freed = false; - for (SplinePtr &spline : curve->splines()) { - layer_freed = spline->attributes.remove(attribute_id); - } - return layer_freed; - } - - static GVArrayPtr varray_from_initializer(const AttributeInit &initializer, - const CustomDataType data_type, - const int total_size) - { - switch (initializer.type) { - case AttributeInit::Type::Default: - /* This function shouldn't be called in this case, since there - * is no need to copy anything to the new custom data array. */ - BLI_assert_unreachable(); - return {}; - case AttributeInit::Type::VArray: - return static_cast(initializer).varray->shallow_copy(); - case AttributeInit::Type::MoveArray: - return std::make_unique( - GSpan(*bke::custom_data_type_to_cpp_type(data_type), - static_cast(initializer).data, - total_size)); - } - BLI_assert_unreachable(); - return {}; + return remove_point_attribute(component, attribute_id); } bool try_create(GeometryComponent &component, @@ -1076,55 +1472,7 @@ class DynamicPointAttributeProvider final : public DynamicAttributesProvider { if (domain != ATTR_DOMAIN_POINT) { return false; } - CurveEval *curve = get_curve_from_component_for_write(component); - if (curve == nullptr || curve->splines().size() == 0) { - return false; - } - - MutableSpan splines = curve->splines(); - - /* First check the one case that allows us to avoid copying the input data. */ - if (splines.size() == 1 && initializer.type == AttributeInit::Type::MoveArray) { - void *source_data = static_cast(initializer).data; - if (!splines[0]->attributes.create_by_move(attribute_id, data_type, source_data)) { - MEM_freeN(source_data); - return false; - } - return true; - } - - /* Otherwise just create a custom data layer on each of the splines. */ - for (const int i : splines.index_range()) { - if (!splines[i]->attributes.create(attribute_id, data_type)) { - /* If attribute creation fails on one of the splines, we cannot leave the custom data - * layers in the previous splines around, so delete them before returning. However, - * this is not an expected case. */ - BLI_assert_unreachable(); - return false; - } - } - - /* With a default initializer type, we can keep the values at their initial values. */ - if (initializer.type == AttributeInit::Type::Default) { - return true; - } - - WriteAttributeLookup write_attribute = this->try_get_for_write(component, attribute_id); - /* We just created the attribute, it should exist. */ - BLI_assert(write_attribute); - - const int total_size = curve->control_point_offsets().last(); - GVArrayPtr source_varray = varray_from_initializer(initializer, data_type, total_size); - /* TODO: When we can call a variant of #set_all with a virtual array argument, - * this theoretically unnecessary materialize step could be removed. */ - GVArray_GSpan source_varray_span{*source_varray}; - write_attribute.varray->set_all(source_varray_span.data()); - - if (initializer.type == AttributeInit::Type::MoveArray) { - MEM_freeN(static_cast(initializer).data); - } - - return true; + return create_point_attribute(component, attribute_id, initializer, data_type); } bool foreach_attribute(const GeometryComponent &component, @@ -1196,25 +1544,47 @@ static ComponentAttributeProviders create_attribute_providers_for_curve() spline_custom_data_access); static PositionAttributeProvider position; + static BezierHandleAttributeProvider handles_start(false); + static BezierHandleAttributeProvider handles_end(true); + + static BuiltinPointAttributeProvider id( + "id", + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Deletable, + [](const Spline &spline) { + std::optional span = spline.attributes.get_for_read("id"); + return span ? span->typed() : Span(); + }, + [](Spline &spline) { + std::optional span = spline.attributes.get_for_write("id"); + return span ? span->typed() : MutableSpan(); + }, + {}, + true); static BuiltinPointAttributeProvider radius( "radius", - BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::NonDeletable, [](const Spline &spline) { return spline.radii(); }, [](Spline &spline) { return spline.radii(); }, - nullptr); + nullptr, + false); static BuiltinPointAttributeProvider tilt( "tilt", - BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::NonCreatable, + BuiltinAttributeProvider::NonDeletable, [](const Spline &spline) { return spline.tilts(); }, [](Spline &spline) { return spline.tilts(); }, - [](Spline &spline) { spline.mark_cache_invalid(); }); + [](Spline &spline) { spline.mark_cache_invalid(); }, + false); static DynamicPointAttributeProvider point_custom_data; - return ComponentAttributeProviders({&position, &radius, &tilt, &resolution, &cyclic}, - {&spline_custom_data, &point_custom_data}); + return ComponentAttributeProviders( + {&position, &id, &radius, &tilt, &handles_start, &handles_end, &resolution, &cyclic}, + {&spline_custom_data, &point_custom_data}); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_component_instances.cc b/source/blender/blenkernel/intern/geometry_component_instances.cc index 26ef827d36d..5fe77000519 100644 --- a/source/blender/blenkernel/intern/geometry_component_instances.cc +++ b/source/blender/blenkernel/intern/geometry_component_instances.cc @@ -14,22 +14,29 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include + #include "BLI_float4x4.hh" #include "BLI_map.hh" #include "BLI_rand.hh" #include "BLI_set.hh" #include "BLI_span.hh" +#include "BLI_task.hh" #include "BLI_vector.hh" #include "DNA_collection_types.h" #include "BKE_geometry_set.hh" +#include "BKE_geometry_set_instances.hh" + +#include "attribute_access_intern.hh" using blender::float4x4; using blender::Map; using blender::MutableSpan; using blender::Set; using blender::Span; +using blender::VectorSet; /* -------------------------------------------------------------------- */ /** \name Geometry Component Implementation @@ -53,7 +60,9 @@ void InstancesComponent::reserve(int min_capacity) { instance_reference_handles_.reserve(min_capacity); instance_transforms_.reserve(min_capacity); - instance_ids_.reserve(min_capacity); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } /** @@ -66,7 +75,9 @@ void InstancesComponent::resize(int capacity) { instance_reference_handles_.resize(capacity); instance_transforms_.resize(capacity); - instance_ids_.resize(capacity); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } void InstancesComponent::clear() @@ -78,15 +89,15 @@ void InstancesComponent::clear() references_.clear(); } -void InstancesComponent::add_instance(const int instance_handle, - const float4x4 &transform, - const int id) +void InstancesComponent::add_instance(const int instance_handle, const float4x4 &transform) { BLI_assert(instance_handle >= 0); BLI_assert(instance_handle < references_.size()); instance_reference_handles_.append(instance_handle); instance_transforms_.append(transform); - instance_ids_.append(id); + if (!instance_ids_.is_empty()) { + this->instance_ids_ensure(); + } } blender::Span InstancesComponent::instance_reference_handles() const @@ -117,6 +128,38 @@ blender::Span InstancesComponent::instance_ids() const return instance_ids_; } +/** + * Make sure the ID storage size matches the number of instances. By directly resizing the + * component's vectors internally, it is possible to be in a situation where the IDs are not + * empty but they do not have the correct size; this function resolves that. + */ +blender::MutableSpan InstancesComponent::instance_ids_ensure() +{ + instance_ids_.append_n_times(0, this->instances_amount() - instance_ids_.size()); + return instance_ids_; +} + +void InstancesComponent::instance_ids_clear() +{ + instance_ids_.clear_and_make_inline(); +} + +/** + * With write access to the instances component, the data in the instanced geometry sets can be + * changed. This is a function on the component rather than each reference to ensure `const` + * correctness for that reason. + */ +GeometrySet &InstancesComponent::geometry_set_from_reference(const int reference_index) +{ + /* If this assert fails, it means #ensure_geometry_instances must be called first or that the + * reference can't be converted to a geometry set. */ + BLI_assert(references_[reference_index].type() == InstanceReference::Type::GeometrySet); + + /* The const cast is okay because the instance's hash in the set + * is not changed by adjusting the data inside the geometry set. */ + return const_cast(references_[reference_index].geometry_set()); +} + /** * Returns a handle for the given reference. * If the reference exists already, the handle of the existing reference is returned. @@ -132,11 +175,96 @@ blender::Span InstancesComponent::references() const return references_; } +void InstancesComponent::remove_unused_references() +{ + using namespace blender; + using namespace blender::bke; + + const int tot_instances = this->instances_amount(); + const int tot_references_before = references_.size(); + + if (tot_instances == 0) { + /* If there are no instances, no reference is needed. */ + references_.clear(); + return; + } + if (tot_references_before == 1) { + /* There is only one reference and at least one instance. So the only existing reference is + * used. Nothing to do here. */ + return; + } + + Array usage_by_handle(tot_references_before, false); + std::mutex mutex; + + /* Loop over all instances to see which references are used. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + /* Use local counter to avoid lock contention. */ + Array local_usage_by_handle(tot_references_before, false); + + for (const int i : range) { + const int handle = instance_reference_handles_[i]; + BLI_assert(handle >= 0 && handle < tot_references_before); + local_usage_by_handle[handle] = true; + } + + std::lock_guard lock{mutex}; + for (const int i : IndexRange(tot_references_before)) { + usage_by_handle[i] |= local_usage_by_handle[i]; + } + }); + + if (!usage_by_handle.as_span().contains(false)) { + /* All references are used. */ + return; + } + + /* Create new references and a mapping for the handles. */ + Vector handle_mapping; + VectorSet new_references; + int next_new_handle = 0; + bool handles_have_to_be_updated = false; + for (const int old_handle : IndexRange(tot_references_before)) { + if (!usage_by_handle[old_handle]) { + /* Add some dummy value. It won't be read again. */ + handle_mapping.append(-1); + } + else { + const InstanceReference &reference = references_[old_handle]; + handle_mapping.append(next_new_handle); + new_references.add_new(reference); + if (old_handle != next_new_handle) { + handles_have_to_be_updated = true; + } + next_new_handle++; + } + } + references_ = new_references; + + if (!handles_have_to_be_updated) { + /* All remaining handles are the same as before, so they don't have to be updated. This happens + * when unused handles are only at the end. */ + return; + } + + /* Update handles of instances. */ + threading::parallel_for(IndexRange(tot_instances), 1000, [&](IndexRange range) { + for (const int i : range) { + instance_reference_handles_[i] = handle_mapping[instance_reference_handles_[i]]; + } + }); +} + int InstancesComponent::instances_amount() const { return instance_transforms_.size(); } +int InstancesComponent::references_amount() const +{ + return references_.size(); +} + bool InstancesComponent::is_empty() const { return this->instance_reference_handles_.size() == 0; @@ -219,10 +347,172 @@ static blender::Array generate_unique_instance_ids(Span original_ids) blender::Span InstancesComponent::almost_unique_ids() const { std::lock_guard lock(almost_unique_ids_mutex_); - if (almost_unique_ids_.size() != instance_ids_.size()) { - almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); + if (instance_ids().is_empty()) { + almost_unique_ids_.reinitialize(this->instances_amount()); + for (const int i : almost_unique_ids_.index_range()) { + almost_unique_ids_[i] = i; + } + } + else { + if (almost_unique_ids_.size() != instance_ids_.size()) { + almost_unique_ids_ = generate_unique_instance_ids(instance_ids_); + } } return almost_unique_ids_; } +int InstancesComponent::attribute_domain_size(const AttributeDomain domain) const +{ + if (domain != ATTR_DOMAIN_POINT) { + return 0; + } + return this->instances_amount(); +} + +namespace blender::bke { + +static float3 get_transform_position(const float4x4 &transform) +{ + return transform.translation(); +} + +static void set_transform_position(float4x4 &transform, const float3 position) +{ + copy_v3_v3(transform.values[3], position); +} + +class InstancePositionAttributeProvider final : public BuiltinAttributeProvider { + public: + InstancePositionAttributeProvider() + : BuiltinAttributeProvider( + "position", ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, NonCreatable, Writable, NonDeletable) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final + { + const InstancesComponent &instances_component = static_cast( + component); + Span transforms = instances_component.instance_transforms(); + return std::make_unique>( + transforms); + } + + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + { + InstancesComponent &instances_component = static_cast(component); + MutableSpan transforms = instances_component.instance_transforms(); + return { + std::make_unique>(transforms), + domain_}; + } + + bool try_delete(GeometryComponent &UNUSED(component)) const final + { + return false; + } + + bool try_create(GeometryComponent &UNUSED(component), + const AttributeInit &UNUSED(initializer)) const final + { + return false; + } + + bool exists(const GeometryComponent &UNUSED(component)) const final + { + return true; + } +}; + +class InstanceIDAttributeProvider final : public BuiltinAttributeProvider { + public: + InstanceIDAttributeProvider() + : BuiltinAttributeProvider( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32, Creatable, Writable, Deletable) + { + } + + GVArrayPtr try_get_for_read(const GeometryComponent &component) const final + { + const InstancesComponent &instances = static_cast(component); + if (instances.instance_ids().is_empty()) { + return {}; + } + return std::make_unique>(instances.instance_ids()); + } + + WriteAttributeLookup try_get_for_write(GeometryComponent &component) const final + { + InstancesComponent &instances = static_cast(component); + if (instances.instance_ids().is_empty()) { + return {}; + } + return {std::make_unique>(instances.instance_ids()), + domain_}; + } + + bool try_delete(GeometryComponent &component) const final + { + InstancesComponent &instances = static_cast(component); + if (instances.instance_ids().is_empty()) { + return false; + } + instances.instance_ids_clear(); + return true; + } + + bool try_create(GeometryComponent &component, const AttributeInit &initializer) const final + { + InstancesComponent &instances = static_cast(component); + if (instances.instances_amount() == 0) { + return false; + } + MutableSpan ids = instances.instance_ids_ensure(); + switch (initializer.type) { + case AttributeInit::Type::Default: { + ids.fill(0); + break; + } + case AttributeInit::Type::VArray: { + const GVArray *varray = static_cast(initializer).varray; + varray->materialize_to_uninitialized(IndexRange(varray->size()), ids.data()); + break; + } + case AttributeInit::Type::MoveArray: { + void *source_data = static_cast(initializer).data; + ids.copy_from({static_cast(source_data), instances.instances_amount()}); + MEM_freeN(source_data); + break; + } + } + return true; + } + + bool exists(const GeometryComponent &component) const final + { + const InstancesComponent &instances = static_cast(component); + return !instances.instance_ids().is_empty(); + } +}; + +static ComponentAttributeProviders create_attribute_providers_for_instances() +{ + static InstancePositionAttributeProvider position; + static InstanceIDAttributeProvider id; + + return ComponentAttributeProviders({&position, &id}, {}); +} +} // namespace blender::bke + +const blender::bke::ComponentAttributeProviders *InstancesComponent::get_attribute_providers() + const +{ + static blender::bke::ComponentAttributeProviders providers = + blender::bke::create_attribute_providers_for_instances(); + return &providers; +} + /** \} */ diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 0c98aa5551b..c3e39c0b2cb 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -912,6 +912,19 @@ static GVMutableArrayPtr make_derived_write_attribute(void *data, const int doma MutableSpan((StructT *)data, domain_size)); } +template +static GVArrayPtr make_array_read_attribute(const void *data, const int domain_size) +{ + return std::make_unique>(Span((const T *)data, domain_size)); +} + +template +static GVMutableArrayPtr make_array_write_attribute(void *data, const int domain_size) +{ + return std::make_unique>( + MutableSpan((T *)data, domain_size)); +} + static float3 get_vertex_position(const MVert &vert) { return float3(vert.co); @@ -1197,7 +1210,7 @@ class NormalAttributeProvider final : public BuiltinAttributeProvider { return std::make_unique>>(std::move(normals)); } - GVMutableArrayPtr try_get_for_write(GeometryComponent &UNUSED(component)) const final + WriteAttributeLookup try_get_for_write(GeometryComponent &UNUSED(component)) const final { return {}; } @@ -1274,6 +1287,18 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() static NormalAttributeProvider normal; + static BuiltinCustomDataLayerProvider id("id", + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute, + make_array_write_attribute, + nullptr); + static BuiltinCustomDataLayerProvider material_index( "material_index", ATTR_DOMAIN_FACE, @@ -1335,14 +1360,15 @@ static ComponentAttributeProviders create_attribute_providers_for_mesh() static CustomDataAttributeProvider edge_custom_data(ATTR_DOMAIN_EDGE, edge_access); static CustomDataAttributeProvider face_custom_data(ATTR_DOMAIN_FACE, face_access); - return ComponentAttributeProviders({&position, &material_index, &shade_smooth, &normal, &crease}, - {&uvs, - &vertex_colors, - &corner_custom_data, - &vertex_groups, - &point_custom_data, - &edge_custom_data, - &face_custom_data}); + return ComponentAttributeProviders( + {&position, &id, &material_index, &shade_smooth, &normal, &crease}, + {&uvs, + &vertex_colors, + &corner_custom_data, + &vertex_groups, + &point_custom_data, + &edge_custom_data, + &face_custom_data}); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc index 6c4af7a6d23..dfb65a9078d 100644 --- a/source/blender/blenkernel/intern/geometry_component_pointcloud.cc +++ b/source/blender/blenkernel/intern/geometry_component_pointcloud.cc @@ -202,8 +202,19 @@ static ComponentAttributeProviders create_attribute_providers_for_point_cloud() make_array_read_attribute, make_array_write_attribute, nullptr); + static BuiltinCustomDataLayerProvider id("id", + ATTR_DOMAIN_POINT, + CD_PROP_INT32, + CD_PROP_INT32, + BuiltinAttributeProvider::Creatable, + BuiltinAttributeProvider::Writable, + BuiltinAttributeProvider::Deletable, + point_access, + make_array_read_attribute, + make_array_write_attribute, + nullptr); static CustomDataAttributeProvider point_custom_data(ATTR_DOMAIN_POINT, point_access); - return ComponentAttributeProviders({&position, &radius}, {&point_custom_data}); + return ComponentAttributeProviders({&position, &radius, &id}, {&point_custom_data}); } } // namespace blender::bke diff --git a/source/blender/blenkernel/intern/geometry_set.cc b/source/blender/blenkernel/intern/geometry_set.cc index e717d289894..c250c14f1d7 100644 --- a/source/blender/blenkernel/intern/geometry_set.cc +++ b/source/blender/blenkernel/intern/geometry_set.cc @@ -15,6 +15,7 @@ */ #include "BLI_map.hh" +#include "BLI_task.hh" #include "BKE_attribute.h" #include "BKE_attribute_access.hh" @@ -104,6 +105,14 @@ bool GeometryComponent::is_empty() const /** \name Geometry Set * \{ */ +/* The methods are defaulted here so that they are not instantiated in every translation unit. */ +GeometrySet::GeometrySet() = default; +GeometrySet::GeometrySet(const GeometrySet &other) = default; +GeometrySet::GeometrySet(GeometrySet &&other) = default; +GeometrySet::~GeometrySet() = default; +GeometrySet &GeometrySet::operator=(const GeometrySet &other) = default; +GeometrySet &GeometrySet::operator=(GeometrySet &&other) = default; + /* This method can only be used when the geometry set is mutable. It returns a mutable geometry * component of the given type. */ @@ -130,6 +139,18 @@ GeometryComponent &GeometrySet::get_component_for_write(GeometryComponentType co }); } +/** + * Retrieve the pointer to a component without creating it if it does not exist, + * unlike #get_component_for_write. + */ +GeometryComponent *GeometrySet::get_component_ptr(GeometryComponentType type) +{ + if (this->has(type)) { + return &this->get_component_for_write(type); + } + return nullptr; +} + /* Get the component of the given type. Might return null if the component does not exist yet. */ const GeometryComponent *GeometrySet::get_component_for_read( GeometryComponentType component_type) const @@ -151,6 +172,19 @@ void GeometrySet::remove(const GeometryComponentType component_type) components_.remove(component_type); } +/** + * Remove all geometry components with types that are not in the provided list. + */ +void GeometrySet::keep_only(const blender::Span component_types) +{ + for (auto it = components_.keys().begin(); it != components_.keys().end(); ++it) { + const GeometryComponentType type = *it; + if (!component_types.contains(type)) { + components_.remove(it); + } + } +} + void GeometrySet::add(const GeometryComponent &component) { BLI_assert(!components_.contains(component.type())); @@ -291,12 +325,37 @@ bool GeometrySet::has_curve() const return component != nullptr && component->has_curve(); } +/* Returns true when the geometry set has any data that is not an instance. */ +bool GeometrySet::has_realized_data() const +{ + if (components_.is_empty()) { + return false; + } + if (components_.size() > 1) { + return true; + } + /* Check if the only component is an #InstancesComponent. */ + return this->get_component_for_read() == nullptr; +} + +/* Return true if the geometry set has any component that isn't empty. */ +bool GeometrySet::is_empty() const +{ + if (components_.is_empty()) { + return true; + } + return !(this->has_mesh() || this->has_curve() || this->has_pointcloud() || + this->has_instances()); +} + /* Create a new geometry set that only contains the given mesh. */ GeometrySet GeometrySet::create_with_mesh(Mesh *mesh, GeometryOwnershipType ownership) { GeometrySet geometry_set; - MeshComponent &component = geometry_set.get_component_for_write(); - component.replace(mesh, ownership); + if (mesh != nullptr) { + MeshComponent &component = geometry_set.get_component_for_write(); + component.replace(mesh, ownership); + } return geometry_set; } @@ -305,8 +364,10 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { GeometrySet geometry_set; - PointCloudComponent &component = geometry_set.get_component_for_write(); - component.replace(pointcloud, ownership); + if (pointcloud != nullptr) { + PointCloudComponent &component = geometry_set.get_component_for_write(); + component.replace(pointcloud, ownership); + } return geometry_set; } @@ -314,65 +375,222 @@ GeometrySet GeometrySet::create_with_pointcloud(PointCloud *pointcloud, GeometrySet GeometrySet::create_with_curve(CurveEval *curve, GeometryOwnershipType ownership) { GeometrySet geometry_set; - CurveComponent &component = geometry_set.get_component_for_write(); - component.replace(curve, ownership); + if (curve != nullptr) { + CurveComponent &component = geometry_set.get_component_for_write(); + component.replace(curve, ownership); + } return geometry_set; } /* Clear the existing mesh and replace it with the given one. */ void GeometrySet::replace_mesh(Mesh *mesh, GeometryOwnershipType ownership) { - MeshComponent &component = this->get_component_for_write(); - component.replace(mesh, ownership); + if (mesh == nullptr) { + this->remove(); + } + else { + MeshComponent &component = this->get_component_for_write(); + component.replace(mesh, ownership); + } } /* Clear the existing curve and replace it with the given one. */ void GeometrySet::replace_curve(CurveEval *curve, GeometryOwnershipType ownership) { - CurveComponent &component = this->get_component_for_write(); - component.replace(curve, ownership); + if (curve == nullptr) { + this->remove(); + } + else { + CurveComponent &component = this->get_component_for_write(); + component.replace(curve, ownership); + } } /* Clear the existing point cloud and replace with the given one. */ void GeometrySet::replace_pointcloud(PointCloud *pointcloud, GeometryOwnershipType ownership) { - PointCloudComponent &pointcloud_component = this->get_component_for_write(); - pointcloud_component.replace(pointcloud, ownership); + if (pointcloud == nullptr) { + this->remove(); + } + else { + PointCloudComponent &component = this->get_component_for_write(); + component.replace(pointcloud, ownership); + } } /* Clear the existing volume and replace with the given one. */ void GeometrySet::replace_volume(Volume *volume, GeometryOwnershipType ownership) { - VolumeComponent &volume_component = this->get_component_for_write(); - volume_component.replace(volume, ownership); + if (volume == nullptr) { + this->remove(); + } + else { + VolumeComponent &component = this->get_component_for_write(); + component.replace(volume, ownership); + } } /* Returns a mutable mesh or null. No ownership is transferred. */ Mesh *GeometrySet::get_mesh_for_write() { - MeshComponent &component = this->get_component_for_write(); - return component.get_for_write(); + MeshComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); } /* Returns a mutable point cloud or null. No ownership is transferred. */ PointCloud *GeometrySet::get_pointcloud_for_write() { - PointCloudComponent &component = this->get_component_for_write(); - return component.get_for_write(); + PointCloudComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); } /* Returns a mutable volume or null. No ownership is transferred. */ Volume *GeometrySet::get_volume_for_write() { - VolumeComponent &component = this->get_component_for_write(); - return component.get_for_write(); + VolumeComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); } /* Returns a mutable curve or null. No ownership is transferred. */ CurveEval *GeometrySet::get_curve_for_write() { - CurveComponent &component = this->get_component_for_write(); - return component.get_for_write(); + CurveComponent *component = this->get_component_ptr(); + return component == nullptr ? nullptr : component->get_for_write(); +} + +void GeometrySet::attribute_foreach(const Span component_types, + const bool include_instances, + const AttributeForeachCallback callback) const +{ + using namespace blender; + using namespace blender::bke; + for (const GeometryComponentType component_type : component_types) { + if (!this->has(component_type)) { + continue; + } + const GeometryComponent &component = *this->get_component_for_read(component_type); + component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + callback(attribute_id, meta_data, component); + return true; + }); + } + if (include_instances && this->has_instances()) { + const InstancesComponent &instances = *this->get_component_for_read(); + instances.foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { + instance_geometry_set.attribute_foreach(component_types, include_instances, callback); + }); + } +} + +void GeometrySet::gather_attributes_for_propagation( + const Span component_types, + const GeometryComponentType dst_component_type, + bool include_instances, + blender::Map &r_attributes) const +{ + using namespace blender; + using namespace blender::bke; + /* Only needed right now to check if an attribute is built-in on this component type. + * TODO: Get rid of the dummy component. */ + const GeometryComponent *dummy_component = GeometryComponent::create(dst_component_type); + this->attribute_foreach( + component_types, + include_instances, + [&](const AttributeIDRef &attribute_id, + const AttributeMetaData &meta_data, + const GeometryComponent &component) { + if (component.attribute_is_builtin(attribute_id)) { + if (!dummy_component->attribute_is_builtin(attribute_id)) { + /* Don't propagate built-in attributes that are not built-in on the destination + * component. */ + return; + } + } + + if (!attribute_id.should_be_kept()) { + return; + } + + auto add_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = meta_data.domain; + attribute_kind->data_type = meta_data.data_type; + }; + auto modify_info = [&](AttributeKind *attribute_kind) { + attribute_kind->domain = bke::attribute_domain_highest_priority( + {attribute_kind->domain, meta_data.domain}); + attribute_kind->data_type = bke::attribute_data_type_highest_complexity( + {attribute_kind->data_type, meta_data.data_type}); + }; + r_attributes.add_or_modify(attribute_id, add_info, modify_info); + }); + delete dummy_component; +} + +static void gather_component_types_recursive(const GeometrySet &geometry_set, + const bool include_instances, + const bool ignore_empty, + Vector &r_types) +{ + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { + if (ignore_empty) { + if (component->is_empty()) { + continue; + } + } + r_types.append_non_duplicates(component->type()); + } + if (!include_instances) { + return; + } + const InstancesComponent *instances = geometry_set.get_component_for_read(); + if (instances == nullptr) { + return; + } + instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { + gather_component_types_recursive( + instance_geometry_set, include_instances, ignore_empty, r_types); + }); +} + +blender::Vector GeometrySet::gather_component_types( + const bool include_instances, bool ignore_empty) const +{ + Vector types; + gather_component_types_recursive(*this, include_instances, ignore_empty, types); + return types; +} + +static void gather_mutable_geometry_sets(GeometrySet &geometry_set, + Vector &r_geometry_sets) +{ + r_geometry_sets.append(&geometry_set); + if (!geometry_set.has_instances()) { + return; + } + /* In the future this can be improved by deduplicating instance references across different + * instances. */ + InstancesComponent &instances_component = + geometry_set.get_component_for_write(); + instances_component.ensure_geometry_instances(); + for (const int handle : instances_component.references().index_range()) { + if (instances_component.references()[handle].type() == InstanceReference::Type::GeometrySet) { + GeometrySet &instance_geometry = instances_component.geometry_set_from_reference(handle); + gather_mutable_geometry_sets(instance_geometry, r_geometry_sets); + } + } +} + +/** + * Modify every (recursive) instance separately. This is often more efficient than realizing all + * instances just to change the same thing on all of them. + */ +void GeometrySet::modify_geometry_sets(ForeachSubGeometryCallback callback) +{ + Vector geometry_sets; + gather_mutable_geometry_sets(*this, geometry_sets); + blender::threading::parallel_for_each( + geometry_sets, [&](GeometrySet *geometry_set) { callback(*geometry_set); }); } /** \} */ @@ -392,24 +610,32 @@ bool BKE_object_has_geometry_set_instances(const Object *ob) if (geometry_set == nullptr) { return false; } - if (geometry_set->has_instances()) { - return true; - } - const bool has_mesh = geometry_set->has_mesh(); - const bool has_pointcloud = geometry_set->has_pointcloud(); - const bool has_volume = geometry_set->has_volume(); - const bool has_curve = geometry_set->has_curve(); - if (ob->type == OB_MESH) { - return has_pointcloud || has_volume || has_curve; - } - if (ob->type == OB_POINTCLOUD) { - return has_mesh || has_volume || has_curve; - } - if (ob->type == OB_VOLUME) { - return has_mesh || has_pointcloud || has_curve; - } - if (ELEM(ob->type, OB_CURVE, OB_FONT)) { - return has_mesh || has_pointcloud || has_volume; + for (const GeometryComponent *component : geometry_set->get_components_for_read()) { + if (component->is_empty()) { + continue; + } + const GeometryComponentType type = component->type(); + bool is_instance = false; + switch (type) { + case GEO_COMPONENT_TYPE_MESH: + is_instance = ob->type != OB_MESH; + break; + case GEO_COMPONENT_TYPE_POINT_CLOUD: + is_instance = ob->type != OB_POINTCLOUD; + break; + case GEO_COMPONENT_TYPE_INSTANCES: + is_instance = true; + break; + case GEO_COMPONENT_TYPE_VOLUME: + is_instance = ob->type != OB_VOLUME; + break; + case GEO_COMPONENT_TYPE_CURVE: + is_instance = !ELEM(ob->type, OB_CURVE, OB_FONT); + break; + } + if (is_instance) { + return true; + } } return false; } diff --git a/source/blender/blenkernel/intern/geometry_set_instances.cc b/source/blender/blenkernel/intern/geometry_set_instances.cc index 9dca2c2907e..a56c7ffb295 100644 --- a/source/blender/blenkernel/intern/geometry_set_instances.cc +++ b/source/blender/blenkernel/intern/geometry_set_instances.cc @@ -14,6 +14,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BKE_collection.h" #include "BKE_geometry_set_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" @@ -23,6 +24,7 @@ #include "BKE_spline.hh" #include "DNA_collection_types.h" +#include "DNA_layer_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_object_types.h" @@ -54,7 +56,7 @@ static void add_final_mesh_as_geometry_component(const Object &object, GeometryS /** * \note This doesn't extract instances from the "dupli" system for non-geometry-nodes instances. */ -static GeometrySet object_get_geometry_set_for_read(const Object &object) +GeometrySet object_get_evaluated_geometry_set(const Object &object) { if (object.type == OB_MESH && object.mode == OB_MODE_EDIT) { GeometrySet geometry_set; @@ -98,7 +100,7 @@ static void geometry_set_collect_recursive_object(const Object &object, const float4x4 &transform, Vector &r_sets) { - GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object); + GeometrySet instance_geometry_set = object_get_evaluated_geometry_set(object); geometry_set_collect_recursive(instance_geometry_set, transform, r_sets); if (object.type == OB_EMPTY) { @@ -187,134 +189,6 @@ void geometry_set_gather_instances(const GeometrySet &geometry_set, geometry_set_collect_recursive(geometry_set, unit_transform, r_instance_groups); } -static bool collection_instance_attribute_foreach(const Collection &collection, - const AttributeForeachCallback callback, - const int limit, - int &count); - -static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit, - int &count); - -static bool object_instance_attribute_foreach(const Object &object, - const AttributeForeachCallback callback, - const int limit, - int &count) -{ - GeometrySet instance_geometry_set = object_get_geometry_set_for_read(object); - if (!instances_attribute_foreach_recursive(instance_geometry_set, callback, limit, count)) { - return false; - } - - if (object.type == OB_EMPTY) { - const Collection *collection_instance = object.instance_collection; - if (collection_instance != nullptr) { - if (!collection_instance_attribute_foreach(*collection_instance, callback, limit, count)) { - return false; - } - } - } - return true; -} - -static bool collection_instance_attribute_foreach(const Collection &collection, - const AttributeForeachCallback callback, - const int limit, - int &count) -{ - LISTBASE_FOREACH (const CollectionObject *, collection_object, &collection.gobject) { - BLI_assert(collection_object->ob != nullptr); - const Object &object = *collection_object->ob; - if (!object_instance_attribute_foreach(object, callback, limit, count)) { - return false; - } - } - LISTBASE_FOREACH (const CollectionChild *, collection_child, &collection.children) { - BLI_assert(collection_child->collection != nullptr); - const Collection &collection = *collection_child->collection; - if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { - return false; - } - } - return true; -} - -/** - * \return True if the recursive iteration should continue, false if the limit is reached or the - * callback has returned false indicating it should stop. - */ -static bool instances_attribute_foreach_recursive(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit, - int &count) -{ - for (const GeometryComponent *component : geometry_set.get_components_for_read()) { - if (!component->attribute_foreach(callback)) { - return false; - } - } - - /* Now that this geometry set is visited, increase the count and check with the limit. */ - if (limit > 0 && count++ > limit) { - return false; - } - - const InstancesComponent *instances_component = - geometry_set.get_component_for_read(); - if (instances_component == nullptr) { - return true; - } - - for (const InstanceReference &reference : instances_component->references()) { - switch (reference.type()) { - case InstanceReference::Type::Object: { - const Object &object = reference.object(); - if (!object_instance_attribute_foreach(object, callback, limit, count)) { - return false; - } - break; - } - case InstanceReference::Type::Collection: { - const Collection &collection = reference.collection(); - if (!collection_instance_attribute_foreach(collection, callback, limit, count)) { - return false; - } - break; - } - case InstanceReference::Type::GeometrySet: { - const GeometrySet &geometry_set = reference.geometry_set(); - if (!instances_attribute_foreach_recursive(geometry_set, callback, limit, count)) { - return false; - } - break; - } - case InstanceReference::Type::None: { - break; - } - } - } - - return true; -} - -/** - * Call the callback on all of this geometry set's components, including geometry sets from - * instances and recursive instances. This is necessary to access available attributes without - * making all of the set's geometry real. - * - * \param limit: The total number of geometry sets to visit before returning early. This is used - * to avoid looking through too many geometry sets recursively, as an explicit tradeoff in favor - * of performance at the cost of visiting every unique attribute. - */ -void geometry_set_instances_attribute_foreach(const GeometrySet &geometry_set, - const AttributeForeachCallback callback, - const int limit) -{ - int count = 0; - instances_attribute_foreach_recursive(geometry_set, callback, limit, count); -} - void geometry_set_gather_instances_attribute_info(Span set_groups, Span component_types, const Set &ignored_attributes, @@ -350,8 +224,7 @@ void geometry_set_gather_instances_attribute_info(Span se } } -static Mesh *join_mesh_topology_and_builtin_attributes(Span set_groups, - const bool convert_points_to_vertices) +static Mesh *join_mesh_topology_and_builtin_attributes(Span set_groups) { int totverts = 0; int totloops = 0; @@ -381,10 +254,6 @@ static Mesh *join_mesh_topology_and_builtin_attributes(Spanmvert[vert_offset + i]; - const float3 old_position = pointcloud.co[i]; - const float3 new_position = transform * old_position; - copy_v3_v3(new_vert.co, new_position); - memcpy(&new_vert.no, point_normal_short, sizeof(point_normal_short)); - } - vert_offset += pointcloud.totpoint; - } - } } /* A possible optimization is to only tag the normals dirty when there are transforms that change @@ -621,12 +472,9 @@ static CurveEval *join_curve_splines_and_builtin_attributes(Span set_groups, - bool convert_points_to_vertices, - GeometrySet &result) +static void join_instance_groups_mesh(Span set_groups, GeometrySet &result) { - Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups, - convert_points_to_vertices); + Mesh *new_mesh = join_mesh_topology_and_builtin_attributes(set_groups); if (new_mesh == nullptr) { return; } @@ -634,21 +482,17 @@ static void join_instance_groups_mesh(Span set_groups, MeshComponent &dst_component = result.get_component_for_write(); dst_component.replace(new_mesh); - Vector component_types; - component_types.append(GEO_COMPONENT_TYPE_MESH); - if (convert_points_to_vertices) { - component_types.append(GEO_COMPONENT_TYPE_POINT_CLOUD); - } - /* Don't copy attributes that are stored directly in the mesh data structs. */ Map attributes; geometry_set_gather_instances_attribute_info( set_groups, - component_types, + {GEO_COMPONENT_TYPE_MESH}, {"position", "material_index", "normal", "shade_smooth", "crease"}, attributes); - join_attributes( - set_groups, component_types, attributes, static_cast(dst_component)); + join_attributes(set_groups, + {GEO_COMPONENT_TYPE_MESH}, + attributes, + static_cast(dst_component)); } static void join_instance_groups_pointcloud(Span set_groups, @@ -686,6 +530,24 @@ static void join_instance_groups_volume(Span set_groups, } } +/** + * Curve point domain attributes must be in the same order on every spline. The order might have + * been different on separate instances, so ensure that all splines have the same order. Note that + * because #Map is used, the order is not necessarily consistent every time, but it is the same for + * every spline, and that's what matters. + */ +static void sort_curve_point_attributes(const Map &info, + MutableSpan splines) +{ + Vector new_order; + for (const AttributeIDRef attribute_id : info.keys()) { + new_order.append(attribute_id); + } + for (SplinePtr &spline : splines) { + spline->attributes.reorder(new_order); + } +} + static void join_instance_groups_curve(Span set_groups, GeometrySet &result) { CurveEval *curve = join_curve_splines_and_builtin_attributes(set_groups); @@ -700,30 +562,14 @@ static void join_instance_groups_curve(Span set_groups, G geometry_set_gather_instances_attribute_info( set_groups, {GEO_COMPONENT_TYPE_CURVE}, - {"position", "radius", "tilt", "cyclic", "resolution"}, + {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"}, attributes); join_attributes(set_groups, {GEO_COMPONENT_TYPE_CURVE}, attributes, static_cast(dst_component)); -} - -GeometrySet geometry_set_realize_mesh_for_modifier(const GeometrySet &geometry_set) -{ - if (!geometry_set.has_instances() && !geometry_set.has_pointcloud()) { - return geometry_set; - } - - GeometrySet new_geometry_set = geometry_set; - Vector 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. */ - new_geometry_set.remove(); - /* If there was a point cloud, it is now part of the mesh. */ - new_geometry_set.remove(); - return new_geometry_set; + sort_curve_point_attributes(attributes, curve->splines()); + curve->assert_valid_point_attributes(); } GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) @@ -736,7 +582,7 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) Vector set_groups; geometry_set_gather_instances(geometry_set, set_groups); - join_instance_groups_mesh(set_groups, false, new_geometry_set); + join_instance_groups_mesh(set_groups, new_geometry_set); join_instance_groups_pointcloud(set_groups, new_geometry_set); join_instance_groups_volume(set_groups, new_geometry_set); join_instance_groups_curve(set_groups, new_geometry_set); @@ -745,3 +591,91 @@ GeometrySet geometry_set_realize_instances(const GeometrySet &geometry_set) } } // namespace blender::bke + +void InstancesComponent::foreach_referenced_geometry( + blender::FunctionRef callback) const +{ + using namespace blender::bke; + for (const InstanceReference &reference : references_) { + switch (reference.type()) { + case InstanceReference::Type::Object: { + const Object &object = reference.object(); + const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object); + callback(object_geometry_set); + break; + } + case InstanceReference::Type::Collection: { + Collection &collection = reference.collection(); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { + const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(*object); + callback(object_geometry_set); + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + break; + } + case InstanceReference::Type::GeometrySet: { + const GeometrySet &instance_geometry_set = reference.geometry_set(); + callback(instance_geometry_set); + break; + } + case InstanceReference::Type::None: { + break; + } + } + } +} + +/** + * If references have a collection or object type, convert them into geometry instances + * recursively. After that, the geometry sets can be edited. There may still be instances of other + * types of they can't be converted to geometry sets. + */ +void InstancesComponent::ensure_geometry_instances() +{ + using namespace blender; + using namespace blender::bke; + VectorSet new_references; + new_references.reserve(references_.size()); + for (const InstanceReference &reference : references_) { + switch (reference.type()) { + case InstanceReference::Type::None: + case InstanceReference::Type::GeometrySet: { + /* Those references can stay as their were. */ + new_references.add_new(reference); + break; + } + case InstanceReference::Type::Object: { + /* Create a new reference that contains the geometry set of the object. We may want to + * treat e.g. lamps and similar object types separately here. */ + const Object &object = reference.object(); + GeometrySet object_geometry_set = object_get_evaluated_geometry_set(object); + if (object_geometry_set.has_instances()) { + InstancesComponent &component = + object_geometry_set.get_component_for_write(); + component.ensure_geometry_instances(); + } + new_references.add_new(std::move(object_geometry_set)); + break; + } + case InstanceReference::Type::Collection: { + /* Create a new reference that contains a geometry set that contains all objects from the + * collection as instances. */ + GeometrySet collection_geometry_set; + InstancesComponent &component = + collection_geometry_set.get_component_for_write(); + Collection &collection = reference.collection(); + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { + const int handle = component.add_reference(*object); + component.add_instance(handle, object->obmat); + float4x4 &transform = component.instance_transforms().last(); + sub_v3_v3(transform.values[3], collection.instance_offset); + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + component.ensure_geometry_instances(); + new_references.add_new(std::move(collection_geometry_set)); + break; + } + } + } + references_ = std::move(new_references); +} diff --git a/source/blender/blenkernel/intern/gpencil.c b/source/blender/blenkernel/intern/gpencil.c index 82a44afbbb1..bea65030c06 100644 --- a/source/blender/blenkernel/intern/gpencil.c +++ b/source/blender/blenkernel/intern/gpencil.c @@ -139,11 +139,11 @@ static void greasepencil_foreach_id(ID *id, LibraryForeachIDData *data) bGPdata *gpencil = (bGPdata *)id; /* materials */ for (int i = 0; i < gpencil->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, gpencil->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gpencil->mat[i], IDWALK_CB_USER); } LISTBASE_FOREACH (bGPDlayer *, gplayer, &gpencil->layers) { - BKE_LIB_FOREACHID_PROCESS(data, gplayer->parent, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gplayer->parent, IDWALK_CB_NOP); } } @@ -319,7 +319,7 @@ IDTypeInfo IDType_ID_GD = { .name = "GPencil", .name_plural = "grease_pencils", .translation_context = BLT_I18NCONTEXT_ID_GPENCIL, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = NULL, .copy_data = greasepencil_copy_data, @@ -1044,7 +1044,6 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src, const bool dup_frames, const bool dup_strokes) { - const bGPDframe *gpf_src; bGPDframe *gpf_dst; bGPDlayer *gpl_dst; @@ -1063,7 +1062,7 @@ bGPDlayer *BKE_gpencil_layer_duplicate(const bGPDlayer *gpl_src, /* copy frames */ BLI_listbase_clear(&gpl_dst->frames); if (dup_frames) { - for (gpf_src = gpl_src->frames.first; gpf_src; gpf_src = gpf_src->next) { + LISTBASE_FOREACH (bGPDframe *, gpf_src, &gpl_src->frames) { /* make a copy of source frame */ gpf_dst = BKE_gpencil_frame_duplicate(gpf_src, dup_strokes); BLI_addtail(&gpl_dst->frames, gpf_dst); diff --git a/source/blender/blenkernel/intern/gpencil_curve.c b/source/blender/blenkernel/intern/gpencil_curve.c index 0752424df71..98e481e6ea8 100644 --- a/source/blender/blenkernel/intern/gpencil_curve.c +++ b/source/blender/blenkernel/intern/gpencil_curve.c @@ -543,7 +543,7 @@ void BKE_gpencil_convert_curve(Main *bmain, int actcol = ob_gp->actcol; for (int slot = 1; slot <= ob_gp->totcol; slot++) { - while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp->data, slot)) { + while (slot <= ob_gp->totcol && !BKE_object_material_slot_used(ob_gp, slot)) { ob_gp->actcol = slot; BKE_object_material_slot_remove(bmain, ob_gp); @@ -1167,7 +1167,7 @@ void BKE_gpencil_editcurve_recalculate_handles(bGPDstroke *gps) bGPDcurve_point *gpc_pt = &gpc->curve_points[i]; bGPDcurve_point *gpc_pt_prev = &gpc->curve_points[i - 1]; bGPDcurve_point *gpc_pt_next = &gpc->curve_points[i + 1]; - /* update handle if point or neighbour is selected */ + /* update handle if point or neighbor is selected */ if (gpc_pt->flag & GP_CURVE_POINT_SELECT || gpc_pt_prev->flag & GP_CURVE_POINT_SELECT || gpc_pt_next->flag & GP_CURVE_POINT_SELECT) { BezTriple *bezt = &gpc_pt->bezt; diff --git a/source/blender/blenkernel/intern/gpencil_geom.cc b/source/blender/blenkernel/intern/gpencil_geom.cc index 8ff026231f5..debdf44b0bb 100644 --- a/source/blender/blenkernel/intern/gpencil_geom.cc +++ b/source/blender/blenkernel/intern/gpencil_geom.cc @@ -540,65 +540,242 @@ bool BKE_gpencil_stroke_sample(bGPdata *gpd, bGPDstroke *gps, const float dist, return true; } +/** + * Give extra stroke points before and after the original tip points. + * \param gps: Target stroke + * \param count_before: how many extra points to be added before a stroke + * \param count_after: how many extra points to be added after a stroke + */ +static bool BKE_gpencil_stroke_extra_points(bGPDstroke *gps, + const int count_before, + const int count_after) +{ + bGPDspoint *pts = gps->points; + + BLI_assert(count_before >= 0); + BLI_assert(count_after >= 0); + if (!count_before && !count_after) { + return false; + } + + const int new_count = count_before + count_after + gps->totpoints; + + bGPDspoint *new_pts = (bGPDspoint *)MEM_mallocN(sizeof(bGPDspoint) * new_count, __func__); + + for (int i = 0; i < count_before; i++) { + memcpy(&new_pts[i], &pts[0], sizeof(bGPDspoint)); + } + memcpy(&new_pts[count_before], pts, sizeof(bGPDspoint) * gps->totpoints); + for (int i = new_count - count_after; i < new_count; i++) { + memcpy(&new_pts[i], &pts[gps->totpoints - 1], sizeof(bGPDspoint)); + } + + if (gps->dvert) { + MDeformVert *new_dv = (MDeformVert *)MEM_mallocN(sizeof(MDeformVert) * new_count, __func__); + + for (int i = 0; i < new_count; i++) { + MDeformVert *dv = &gps->dvert[CLAMPIS(i - count_before, 0, gps->totpoints - 1)]; + int inew = i; + new_dv[inew].flag = dv->flag; + new_dv[inew].totweight = dv->totweight; + new_dv[inew].dw = (MDeformWeight *)MEM_mallocN(sizeof(MDeformWeight) * dv->totweight, + __func__); + memcpy(new_dv[inew].dw, dv->dw, sizeof(MDeformWeight) * dv->totweight); + } + BKE_gpencil_free_stroke_weights(gps); + MEM_freeN(gps->dvert); + gps->dvert = new_dv; + } + + MEM_freeN(gps->points); + gps->points = new_pts; + gps->totpoints = new_count; + + return true; +} + /** * Backbone stretch similar to Freestyle. * \param gps: Stroke to sample. - * \param dist: Distance of one segment. - * \param overshoot_fac: How exact is the follow curve algorithm. + * \param dist: Length of the added section. + * \param overshoot_fac: Relative length of the curve which is used to determine the extension. * \param mode: Affect to Start, End or Both extremes (0->Both, 1->Start, 2->End) + * \param follow_curvature: True for approximating curvature of given overshoot. + * \param extra_point_count: When follow_curvature is true, use this amount of extra points */ bool BKE_gpencil_stroke_stretch(bGPDstroke *gps, const float dist, const float overshoot_fac, - const short mode) + const short mode, + const bool follow_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature) { #define BOTH 0 #define START 1 #define END 2 - bGPDspoint *pt = gps->points, *last_pt, *second_last, *next_pt; - int i; - float threshold = (overshoot_fac == 0 ? 0.001f : overshoot_fac); + const bool do_start = ELEM(mode, BOTH, START); + const bool do_end = ELEM(mode, BOTH, END); + float used_percent_length = overshoot_fac; + CLAMP(used_percent_length, 1e-4f, 1.0f); + if (!isfinite(used_percent_length)) { + /* #used_percent_length must always be finite, otherwise a segfault occurs. + * Since this function should never segfault, set #used_percent_length to a safe fallback. */ + /* NOTE: This fallback is used if gps->totpoints == 2, see MOD_gpencillength.c */ + used_percent_length = 0.1f; + } - if (gps->totpoints < 2 || dist < FLT_EPSILON) { + if (gps->totpoints <= 1 || dist < FLT_EPSILON || extra_point_count <= 0) { return false; } - last_pt = &pt[gps->totpoints - 1]; - second_last = &pt[gps->totpoints - 2]; - next_pt = &pt[1]; + /* NOTE: When it's just a straight line, we don't need to do the curvature stuff. */ + if (!follow_curvature || gps->totpoints <= 2) { + /* Not following curvature, just straight line. */ + /* NOTE: #overshoot_point_param can not be zero. */ + float overshoot_point_param = used_percent_length * (gps->totpoints - 1); + float result[3]; - if (mode == BOTH || mode == START) { - float len1 = 0.0f; - i = 1; - while (len1 < threshold && gps->totpoints > i) { - next_pt = &pt[i]; - len1 = len_v3v3(&next_pt->x, &pt->x); - i++; - } - float extend1 = (len1 + dist) / len1; - float result1[3]; - - interp_v3_v3v3(result1, &next_pt->x, &pt->x, extend1); - copy_v3_v3(&pt->x, result1); - } - - if (mode == BOTH || mode == END) { - float len2 = 0.0f; - i = 2; - while (len2 < threshold && gps->totpoints >= i) { - second_last = &pt[gps->totpoints - i]; - len2 = len_v3v3(&last_pt->x, &second_last->x); - i++; + if (do_start) { + int index1 = floor(overshoot_point_param); + int index2 = ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[0].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3(result, &gps->points[1].x, &gps->points[0].x); + } + madd_v3_v3fl(&gps->points[0].x, result, -dist / len_v3(result)); } - float extend2 = (len2 + dist) / len2; - float result2[3]; - interp_v3_v3v3(result2, &second_last->x, &last_pt->x, extend2); - - copy_v3_v3(&last_pt->x, result2); + if (do_end) { + int index1 = gps->totpoints - 1 - floor(overshoot_point_param); + int index2 = gps->totpoints - 1 - ceil(overshoot_point_param); + interp_v3_v3v3(result, + &gps->points[index1].x, + &gps->points[index2].x, + fmodf(overshoot_point_param, 1.0f)); + sub_v3_v3(result, &gps->points[gps->totpoints - 1].x); + if (UNLIKELY(is_zero_v3(result))) { + sub_v3_v3v3( + result, &gps->points[gps->totpoints - 2].x, &gps->points[gps->totpoints - 1].x); + } + madd_v3_v3fl(&gps->points[gps->totpoints - 1].x, result, -dist / len_v3(result)); + } + return true; } + /* Curvature calculation. */ + + /* First allocate the new stroke size. */ + const int first_old_index = do_start ? extra_point_count : 0; + const int last_old_index = gps->totpoints - 1 + first_old_index; + const int orig_totpoints = gps->totpoints; + BKE_gpencil_stroke_extra_points(gps, first_old_index, do_end ? extra_point_count : 0); + + /* The fractional amount of points to query when calculating the average curvature of the + * strokes. */ + const float overshoot_parameter = used_percent_length * (orig_totpoints - 2); + int overshoot_pointcount = ceil(overshoot_parameter); + CLAMP(overshoot_pointcount, 1, orig_totpoints - 2); + + /* Do for both sides without code duplication. */ + float no[3], vec1[3], vec2[3], total_angle[3]; + for (int k = 0; k < 2; k++) { + if ((k == 0 && !do_start) || (k == 1 && !do_end)) { + continue; + } + + const int start_i = k == 0 ? first_old_index : + last_old_index; // first_old_index, last_old_index + const int dir_i = 1 - k * 2; // 1, -1 + + sub_v3_v3v3(vec1, &gps->points[start_i + dir_i].x, &gps->points[start_i].x); + zero_v3(total_angle); + float segment_length = normalize_v3(vec1); + float overshoot_length = 0.0f; + + /* Accumulate rotation angle and length. */ + int j = 0; + for (int i = start_i; j < overshoot_pointcount; i += dir_i, j++) { + /* Don't fully add last segment to get continuity in overshoot_fac. */ + float fac = fmin(overshoot_parameter - j, 1.0f); + + /* Read segments. */ + copy_v3_v3(vec2, vec1); + sub_v3_v3v3(vec1, &gps->points[i + dir_i * 2].x, &gps->points[i + dir_i].x); + const float len = normalize_v3(vec1); + float angle = angle_normalized_v3v3(vec1, vec2) * fac; + + /* Add half of both adjacent legs of the current angle. */ + const float added_len = (segment_length + len) * 0.5f * fac; + overshoot_length += added_len; + segment_length = len; + + if (angle > max_angle) { + continue; + } + if (angle > M_PI * 0.995f) { + continue; + } + + angle *= powf(added_len, segment_influence); + + cross_v3_v3v3(no, vec1, vec2); + normalize_v3_length(no, angle); + add_v3_v3(total_angle, no); + } + + if (UNLIKELY(overshoot_length == 0.0f)) { + /* Don't do a proper extension if the used points are all in the same position. */ + continue; + } + + sub_v3_v3v3(vec1, &gps->points[start_i].x, &gps->points[start_i + dir_i].x); + /* In general curvature = 1/radius. For the case without the + * weights introduced by #segment_influence, the calculation is: + * `curvature = delta angle/delta arclength = len_v3(total_angle) / overshoot_length` */ + float curvature = normalize_v3(total_angle) / overshoot_length; + /* Compensate for the weights powf(added_len, segment_influence). */ + curvature /= powf(overshoot_length / fminf(overshoot_parameter, (float)j), segment_influence); + if (invert_curvature) { + curvature = -curvature; + } + const float angle_step = curvature * dist / extra_point_count; + float step_length = dist / extra_point_count; + if (fabsf(angle_step) > FLT_EPSILON) { + /* Make a direct step length from the assigned arc step length. */ + step_length *= sin(angle_step * 0.5f) / (angle_step * 0.5f); + } + else { + zero_v3(total_angle); + } + const float prev_length = normalize_v3_length(vec1, step_length); + + /* Build rotation matrix here to get best performance. */ + float rot[3][3]; + float q[4]; + axis_angle_to_quat(q, total_angle, angle_step); + quat_to_mat3(rot, q); + + /* Rotate the starting direction to account for change in edge lengths. */ + axis_angle_to_quat(q, + total_angle, + fmaxf(0.0f, 1.0f - fabs(segment_influence)) * + (curvature * prev_length - angle_step) / 2.0f); + mul_qt_v3(q, vec1); + + /* Now iteratively accumulate the segments with a rotating added direction. */ + for (int i = start_i - dir_i, j = 0; j < extra_point_count; i -= dir_i, j++) { + mul_v3_m3v3(vec1, rot, vec1); + add_v3_v3v3(&gps->points[i].x, vec1, &gps->points[i + dir_i].x); + } + } return true; } @@ -749,6 +926,7 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo second_last = &pt[gps->totpoints - 2]; + float len; float len1, cut_len1; float len2, cut_len2; len1 = len2 = cut_len1 = cut_len2 = 0.0f; @@ -759,11 +937,13 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 0; index_end = gps->totpoints - 1; while (len1 < dist && gps->totpoints > i + 1) { - len1 += len_v3v3(&pt[i].x, &pt[i + 1].x); + len = len_v3v3(&pt[i].x, &pt[i + 1].x); + len1 += len; cut_len1 = len1 - dist; i++; } index_start = i - 1; + interp_v3_v3v3(&pt[index_start].x, &pt[index_start + 1].x, &pt[index_start].x, cut_len1 / len); } if (mode == END) { @@ -771,18 +951,20 @@ bool BKE_gpencil_stroke_shrink(bGPDstroke *gps, const float dist, const short mo i = 2; while (len2 < dist && gps->totpoints >= i) { second_last = &pt[gps->totpoints - i]; - len2 += len_v3v3(&second_last[1].x, &second_last->x); + len = len_v3v3(&second_last[1].x, &second_last->x); + len2 += len; cut_len2 = len2 - dist; i++; } index_end = gps->totpoints - i + 2; + interp_v3_v3v3(&pt[index_end].x, &pt[index_end - 1].x, &pt[index_end].x, cut_len2 / len); } if (index_end <= index_start) { index_start = index_end = 0; /* empty stroke */ } - if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < dist)) { + if ((index_end == index_start + 1) && (cut_len1 + cut_len2 < 0)) { index_start = index_end = 0; /* no length left to cut */ } diff --git a/source/blender/blenkernel/intern/gpencil_modifier.c b/source/blender/blenkernel/intern/gpencil_modifier.c index 6be03bffb3c..a6164340477 100644 --- a/source/blender/blenkernel/intern/gpencil_modifier.c +++ b/source/blender/blenkernel/intern/gpencil_modifier.c @@ -65,7 +65,7 @@ static CLG_LogRef LOG = {"bke.gpencil_modifier"}; static GpencilModifierTypeInfo *modifier_gpencil_types[NUM_GREASEPENCIL_MODIFIER_TYPES] = {NULL}; #if 0 -/* Note that GPencil actually does not support these atm, but might do in the future. */ +/* Note that GPencil actually does not support these at the moment, but might do in the future. */ static GpencilVirtualModifierData virtualModifierCommonData; #endif @@ -129,7 +129,8 @@ GpencilModifierData *BKE_gpencil_modifiers_get_virtual_modifierlist( GpencilModifierData *md = ob->greasepencil_modifiers.first; #if 0 - /* Note that GPencil actually does not support these atm, but might do in the future. */ + /* Note that GPencil actually does not support these at the moment, + * but might do in the future. */ *virtualModifierData = virtualModifierCommonData; if (ob->parent) { if (ob->parent->type == OB_ARMATURE && ob->partype == PARSKEL) { @@ -328,8 +329,9 @@ void BKE_gpencil_modifier_init(void) gpencil_modifier_type_init(modifier_gpencil_types); /* MOD_gpencil_util.c */ #if 0 - /* Note that GPencil actually does not support these atm, but might do in the future. */ - /* Initialize global cmmon storage used for virtual modifier list */ + /* Note that GPencil actually does not support these at the moment, + * but might do in the future. */ + /* Initialize global common storage used for virtual modifier list. */ GpencilModifierData *md; md = BKE_gpencil_modifier_new(eGpencilModifierType_Armature); virtualModifierCommonData.amd = *((ArmatureGpencilModifierData *)md); @@ -518,7 +520,7 @@ static void gpencil_modifier_copy_data_id_us_cb(void *UNUSED(userData), * Copy grease pencil modifier data. * \param md: Source modifier data * \param target: Target modifier data - * \parm flag: Flags + * \param flag: Flags */ void BKE_gpencil_modifier_copydata_ex(GpencilModifierData *md, GpencilModifierData *target, diff --git a/source/blender/blenkernel/intern/hair.c b/source/blender/blenkernel/intern/hair.c index af7cc0acb57..7433ee7ac29 100644 --- a/source/blender/blenkernel/intern/hair.c +++ b/source/blender/blenkernel/intern/hair.c @@ -107,7 +107,7 @@ static void hair_foreach_id(ID *id, LibraryForeachIDData *data) { Hair *hair = (Hair *)id; for (int i = 0; i < hair->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, hair->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, hair->mat[i], IDWALK_CB_USER); } } @@ -181,7 +181,7 @@ IDTypeInfo IDType_ID_HA = { .name = "Hair", .name_plural = "hairs", .translation_context = BLT_I18NCONTEXT_ID_HAIR, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = hair_init_data, .copy_data = hair_copy_data, diff --git a/source/blender/blenkernel/intern/icons.cc b/source/blender/blenkernel/intern/icons.cc index 97c742b1ec1..f820b345c59 100644 --- a/source/blender/blenkernel/intern/icons.cc +++ b/source/blender/blenkernel/intern/icons.cc @@ -359,22 +359,30 @@ void BKE_previewimg_id_copy(ID *new_id, const ID *old_id) PreviewImage **BKE_previewimg_id_get_p(const ID *id) { switch (GS(id->name)) { + case ID_OB: { + Object *ob = (Object *)id; + /* Currently, only object types with real geometry can be rendered as preview. */ + if (!OB_TYPE_IS_GEOMETRY(ob->type)) { + return nullptr; + } + return &ob->preview; + } + #define ID_PRV_CASE(id_code, id_struct) \ case id_code: { \ return &((id_struct *)id)->preview; \ } \ ((void)0) - ID_PRV_CASE(ID_MA, Material); - ID_PRV_CASE(ID_TE, Tex); - ID_PRV_CASE(ID_WO, World); - ID_PRV_CASE(ID_LA, Light); - ID_PRV_CASE(ID_IM, Image); - ID_PRV_CASE(ID_BR, Brush); - ID_PRV_CASE(ID_OB, Object); - ID_PRV_CASE(ID_GR, Collection); - ID_PRV_CASE(ID_SCE, Scene); - ID_PRV_CASE(ID_SCR, bScreen); - ID_PRV_CASE(ID_AC, bAction); + ID_PRV_CASE(ID_MA, Material); + ID_PRV_CASE(ID_TE, Tex); + ID_PRV_CASE(ID_WO, World); + ID_PRV_CASE(ID_LA, Light); + ID_PRV_CASE(ID_IM, Image); + ID_PRV_CASE(ID_BR, Brush); + ID_PRV_CASE(ID_GR, Collection); + ID_PRV_CASE(ID_SCE, Scene); + ID_PRV_CASE(ID_SCR, bScreen); + ID_PRV_CASE(ID_AC, bAction); #undef ID_PRV_CASE default: break; diff --git a/source/blender/blenkernel/intern/idtype.c b/source/blender/blenkernel/intern/idtype.c index b2efccc53c4..d9dc68b1a4f 100644 --- a/source/blender/blenkernel/intern/idtype.c +++ b/source/blender/blenkernel/intern/idtype.c @@ -254,6 +254,24 @@ bool BKE_idtype_idcode_is_only_appendable(const short idcode) return false; } +/** + * Check if an ID type can try to reuse and existing matching local one when being appended again. + * + * \param idcode: The IDType code to check. + * \return Boolean, false when it cannot be re-used, true otherwise. + */ +bool BKE_idtype_idcode_append_is_reusable(const short idcode) +{ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_idcode(idcode); + BLI_assert(id_type != NULL); + if (id_type != NULL && (id_type->flags & IDTYPE_FLAGS_APPEND_IS_REUSABLE) != 0) { + /* All appendable ID types should also always be linkable. */ + BLI_assert((id_type->flags & IDTYPE_FLAGS_NO_LIBLINKING) == 0); + return true; + } + return false; +} + /** * Convert an \a idcode into an \a idfilter (e.g. ID_OB -> FILTER_ID_OB). */ diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 33f007c6dee..8472ad5d8aa 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -112,12 +112,26 @@ #include "DNA_view3d_types.h" static CLG_LogRef LOG = {"bke.image"}; -static ThreadMutex *image_mutex; static void image_init(Image *ima, short source, short type); static void image_free_packedfiles(Image *ima); static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src); +/* Reset runtime image fields when datablock is being initialized. */ +static void image_runtime_reset(struct Image *image) +{ + memset(&image->runtime, 0, sizeof(image->runtime)); + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(image->runtime.cache_mutex); +} + +/* Reset runtime image fields when datablock is being copied. */ +static void image_runtime_reset_on_copy(struct Image *image) +{ + image->runtime.cache_mutex = MEM_mallocN(sizeof(ThreadMutex), "image runtime cache_mutex"); + BLI_mutex_init(image->runtime.cache_mutex); +} + static void image_init_data(ID *id) { Image *image = (Image *)id; @@ -167,6 +181,8 @@ static void image_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c else { image_dst->preview = NULL; } + + image_runtime_reset_on_copy(image_dst); } static void image_free_data(ID *id) @@ -194,6 +210,9 @@ static void image_free_data(ID *id) BLI_freelistN(&image->tiles); BLI_freelistN(&image->gpu_refresh_areas); + + BLI_mutex_end(image->runtime.cache_mutex); + MEM_freeN(image->runtime.cache_mutex); } static void image_foreach_cache(ID *id, @@ -211,8 +230,12 @@ static void image_foreach_cache(ID *id, for (int eye = 0; eye < 2; eye++) { for (int a = 0; a < TEXTARGET_COUNT; a++) { for (int resolution = 0; resolution < IMA_TEXTURE_RESOLUTION_LEN; resolution++) { + GPUTexture *texture = image->gputexture[a][eye][resolution]; + if (texture == NULL) { + continue; + } key.offset_in_ID = offsetof(Image, gputexture[a][eye][resolution]); - key.cache_v = image->gputexture[a][eye]; + key.cache_v = texture; function_callback(id, &key, (void **)&image->gputexture[a][eye][resolution], 0, user_data); } } @@ -317,12 +340,12 @@ static void image_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &ima->preview); BKE_previewimg_blend_read(reader, ima->preview); BLO_read_data_address(reader, &ima->stereo3d_format); - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = IMA_OK; - } + ima->lastused = 0; ima->gpuflag = 0; BLI_listbase_clear(&ima->gpu_refresh_areas); + + image_runtime_reset(ima); } static void image_blend_read_lib(BlendLibReader *UNUSED(reader), ID *id) @@ -345,7 +368,7 @@ IDTypeInfo IDType_ID_IM = { .name = "Image", .name_plural = "images", .translation_context = BLT_I18NCONTEXT_ID_IMAGE, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = image_init_data, .copy_data = image_copy_data, @@ -439,27 +462,17 @@ static void imagecache_remove(Image *image, int index) IMB_moviecache_remove(image->cache, &key); } -static struct ImBuf *imagecache_get(Image *image, int index) +static struct ImBuf *imagecache_get(Image *image, int index, bool *r_is_cached_empty) { if (image->cache) { ImageCacheKey key; key.index = index; - return IMB_moviecache_get(image->cache, &key); + return IMB_moviecache_get(image->cache, &key, r_is_cached_empty); } return NULL; } -void BKE_images_init(void) -{ - image_mutex = BLI_mutex_alloc(); -} - -void BKE_images_exit(void) -{ - BLI_mutex_free(image_mutex); -} - /* ***************** ALLOC & FREE, DATA MANAGING *************** */ static void image_free_cached_frames(Image *image) @@ -512,7 +525,7 @@ static void image_free_anims(Image *ima) void BKE_image_free_buffers_ex(Image *ima, bool do_lock) { if (do_lock) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); } image_free_cached_frames(ima); @@ -525,12 +538,8 @@ void BKE_image_free_buffers_ex(Image *ima, bool do_lock) BKE_image_free_gputextures(ima); - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = IMA_OK; - } - if (do_lock) { - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } } @@ -560,7 +569,6 @@ static void image_init(Image *ima, short source, short type) } ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tiles"); - tile->ok = IMA_OK; tile->tile_number = 1001; BLI_addtail(&ima->tiles, tile); @@ -570,6 +578,8 @@ static void image_init(Image *ima, short source, short type) } } + image_runtime_reset(ima); + BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); } @@ -593,25 +603,25 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ * call IMB_freeImBuf to de-reference the image buffer after * it's done handling it. */ -static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, int index, int entry) +static ImBuf *image_get_cached_ibuf_for_index_entry(Image *ima, + int index, + int entry, + bool *r_is_cached_empty) { if (index != IMA_NO_INDEX) { index = IMA_MAKE_INDEX(entry, index); } - return imagecache_get(ima, index); + return imagecache_get(ima, index, r_is_cached_empty); } -/* no ima->ibuf anymore, but listbase */ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int entry) { - if (ibuf) { - if (index != IMA_NO_INDEX) { - index = IMA_MAKE_INDEX(entry, index); - } - - imagecache_put(ima, index, ibuf); + if (index != IMA_NO_INDEX) { + index = IMA_MAKE_INDEX(entry, index); } + + imagecache_put(ima, index, ibuf); } static void image_remove_ibuf(Image *ima, int index, int entry) @@ -643,7 +653,9 @@ void BKE_image_merge(Main *bmain, Image *dest, Image *source) { /* sanity check */ if (dest && source && dest != source) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(source->runtime.cache_mutex); + BLI_mutex_lock(dest->runtime.cache_mutex); + if (source->cache != NULL) { struct MovieCacheIter *iter; iter = IMB_moviecacheIter_new(source->cache); @@ -655,7 +667,9 @@ void BKE_image_merge(Main *bmain, Image *dest, Image *source) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + + BLI_mutex_unlock(dest->runtime.cache_mutex); + BLI_mutex_unlock(source->runtime.cache_mutex); BKE_id_free(bmain, source); } @@ -884,11 +898,6 @@ Image *BKE_image_load_exists_ex(Main *bmain, const char *filepath, bool *r_exist if (BLI_path_cmp(strtest, str) == 0) { if ((BKE_image_has_anim(ima) == false) || (ima->id.us == 0)) { id_us_plus(&ima->id); /* officially should not, it doesn't link here! */ - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->ok == 0) { - tile->ok = IMA_OK; - } - } if (r_exists) { *r_exists = true; } @@ -1076,9 +1085,6 @@ Image *BKE_image_add_generated(Main *bmain, image_add_view(ima, names[view_id], ""); } - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; - return ima; } @@ -1101,8 +1107,6 @@ Image *BKE_image_add_from_imbuf(Main *bmain, ImBuf *ibuf, const char *name) if (ima) { STRNCPY(ima->filepath, ibuf->name); image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; } return ima; @@ -1153,7 +1157,7 @@ bool BKE_image_memorypack(Image *ima) int i; for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, 0, NULL); if (!ibuf) { ok = false; @@ -1173,7 +1177,7 @@ bool BKE_image_memorypack(Image *ima) ima->views_format = R_IMF_VIEWS_INDIVIDUAL; } else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); if (ibuf) { ok = ok && image_memorypack_imbuf(ima, ibuf, ibuf->name); @@ -1256,12 +1260,17 @@ static uintptr_t image_mem_size(Image *image) return 0; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); + if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); while (!IMB_moviecacheIter_done(iter)) { ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + IMB_moviecacheIter_step(iter); + if (ibuf == NULL) { + continue; + } ImBuf *ibufm; int level; @@ -1283,12 +1292,11 @@ static uintptr_t image_mem_size(Image *image) } } } - - IMB_moviecacheIter_step(iter); } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + + BLI_mutex_unlock(image->runtime.cache_mutex); return size; } @@ -1366,11 +1374,11 @@ static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void /* except_frame is weak, only works for seqs without offset... */ void BKE_image_free_anim_ibufs(Image *ima, int except_frame) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); if (ima->cache != NULL) { IMB_moviecache_cleanup(ima->cache, imagecache_check_free_anim, &except_frame); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } void BKE_image_all_free_anim_ibufs(Main *bmain, int cfra) @@ -1560,9 +1568,9 @@ bool BKE_imtype_requires_linear_float(const char imtype) char BKE_imtype_valid_channels(const char imtype, bool write_file) { - char chan_flag = IMA_CHAN_FLAG_RGB; /* assume all support rgb */ + char chan_flag = IMA_CHAN_FLAG_RGB; /* Assume all support RGB. */ - /* alpha */ + /* Alpha. */ switch (imtype) { case R_IMF_IMTYPE_BMP: if (write_file) { @@ -1583,7 +1591,7 @@ char BKE_imtype_valid_channels(const char imtype, bool write_file) break; } - /* bw */ + /* BW. */ switch (imtype) { case R_IMF_IMTYPE_BMP: case R_IMF_IMTYPE_PNG: @@ -3078,8 +3086,7 @@ int BKE_imbuf_write_as(ImBuf *ibuf, const char *name, ImageFormatData *imf, cons ImBuf ibuf_back = *ibuf; int ok; - /* all data is rgba anyway, - * this just controls how to save for some formats */ + /* All data is RGBA anyway, this just controls how to save for some formats. */ ibuf->planes = imf->planes; ok = BKE_imbuf_write(ibuf, name, imf); @@ -3290,7 +3297,7 @@ void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser * } if (do_reset) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); image_free_cached_frames(ima); BKE_image_free_views(ima); @@ -3298,7 +3305,7 @@ void BKE_image_ensure_viewer_views(const RenderData *rd, Image *ima, ImageUser * /* add new views */ image_viewer_create_views(rd, ima); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } BLI_thread_unlock(LOCK_DRAW_IMAGE); @@ -3484,7 +3491,6 @@ static void image_tag_frame_recalc(Image *ima, ID *iuser_id, ImageUser *iuser, v if (ima == changed_image && BKE_image_is_animated(ima)) { iuser->flag |= IMA_NEED_FRAME_RECALC; - iuser->ok = 1; if (iuser_id) { /* Must copy image user changes to CoW datablock. */ @@ -3498,7 +3504,6 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c Image *changed_image = customdata; if (ima == changed_image) { - iuser->ok = 1; if (iuser->scene) { image_update_views_format(ima, iuser); } @@ -3512,7 +3517,6 @@ static void image_tag_reload(Image *ima, ID *iuser_id, ImageUser *iuser, void *c void BKE_imageuser_default(ImageUser *iuser) { memset(iuser, 0, sizeof(ImageUser)); - iuser->ok = 1; iuser->frames = 100; iuser->sfra = 1; } @@ -3565,14 +3569,13 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) return; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); switch (signal) { case IMA_SIGNAL_FREE: BKE_image_free_buffers(ima); if (iuser) { - iuser->ok = 1; if (iuser->scene) { image_update_views_format(ima, iuser); } @@ -3587,7 +3590,7 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) if (ima->source == IMA_SRC_GENERATED) { if (ima->gen_x == 0 || ima->gen_y == 0) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); if (ibuf) { ima->gen_x = ibuf->x; ima->gen_y = ibuf->y; @@ -3627,10 +3630,6 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) */ BKE_image_free_buffers(ima); - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = 1; - } - if (iuser) { image_tag_frame_recalc(ima, NULL, iuser, ima); } @@ -3678,7 +3677,6 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) break; case IMA_SIGNAL_USER_NEW_IMAGE: if (iuser) { - iuser->ok = 1; if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_TILED)) { if (ima->type == IMA_TYPE_MULTILAYER) { BKE_image_init_imageuser(ima, iuser); @@ -3688,19 +3686,10 @@ void BKE_image_signal(Main *bmain, Image *ima, ImageUser *iuser, int signal) break; case IMA_SIGNAL_COLORMANAGE: BKE_image_free_buffers(ima); - - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - tile->ok = 1; - } - - if (iuser) { - iuser->ok = 1; - } - break; } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); /* don't use notifiers because they are not 100% sure to succeeded * this also makes sure all scenes are accounted for. */ @@ -3796,7 +3785,6 @@ ImageTile *BKE_image_add_tile(struct Image *ima, int tile_number, const char *la } ImageTile *tile = MEM_callocN(sizeof(ImageTile), "image new tile"); - tile->ok = IMA_OK; tile->tile_number = tile_number; if (next_tile) { @@ -3861,14 +3849,14 @@ void BKE_image_reassign_tile(struct Image *ima, ImageTile *tile, int new_tile_nu if (BKE_image_is_multiview(ima)) { const int totviews = BLI_listbase_count(&ima->views); for (int i = 0; i < totviews; i++) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, i, old_tile_number, NULL); image_remove_ibuf(ima, i, old_tile_number); image_assign_ibuf(ima, ibuf, i, new_tile_number); IMB_freeImBuf(ibuf); } } else { - ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number); + ImBuf *ibuf = image_get_cached_ibuf_for_index_entry(ima, 0, old_tile_number, NULL); image_remove_ibuf(ima, 0, old_tile_number); image_assign_ibuf(ima, ibuf, 0, new_tile_number); IMB_freeImBuf(ibuf); @@ -3927,7 +3915,6 @@ bool BKE_image_fill_tile(struct Image *ima, if (tile_ibuf != NULL) { image_assign_ibuf(ima, tile_ibuf, 0, tile->tile_number); BKE_image_release_ibuf(ima, tile_ibuf, NULL); - tile->ok = IMA_OK; return true; } return false; @@ -4205,9 +4192,7 @@ static void image_init_after_load(Image *ima, ImageUser *iuser, ImBuf *UNUSED(ib /* Images should never get loaded if the corresponding tile does not exist, * but we should at least not crash if it happens due to a bug elsewhere. */ BLI_assert(tile != NULL); - if (tile != NULL) { - tile->ok = IMA_OK_LOADED; - } + UNUSED_VARS_NDEBUG(tile); } static int imbuf_alpha_flags_for_image(Image *ima) @@ -4242,8 +4227,7 @@ static int image_num_files(Image *ima) return BLI_listbase_count(&ima->views); } -static ImBuf *load_sequence_single( - Image *ima, ImageUser *iuser, int frame, const int view_id, bool *r_assign) +static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, const int view_id) { struct ImBuf *ibuf; char name[FILE_MAX]; @@ -4293,19 +4277,11 @@ static ImBuf *load_sequence_single( } else { image_init_after_load(ima, iuser, ibuf); - *r_assign = true; } #else image_init_after_load(ima, iuser, ibuf); - *r_assign = true; #endif } - else { - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - if (tile != NULL) { - tile->ok = 0; - } - } return ibuf; } @@ -4315,13 +4291,10 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, struct ImBuf *ibuf = NULL; const bool is_multiview = BKE_image_is_multiview(ima); const int totfiles = image_num_files(ima); - bool assign = false; if (!is_multiview) { - ibuf = load_sequence_single(ima, iuser, frame, 0, &assign); - if (assign) { - image_assign_ibuf(ima, ibuf, 0, entry); - } + ibuf = load_sequence_single(ima, iuser, frame, 0); + image_assign_ibuf(ima, ibuf, 0, entry); } else { const int totviews = BLI_listbase_count(&ima->views); @@ -4330,7 +4303,7 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); for (int i = 0; i < totfiles; i++) { - ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, &assign); + ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i); } if (BKE_image_is_stereo(ima) && ima->views_format == R_IMF_VIEWS_STEREO_3D) { @@ -4340,10 +4313,8 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, /* return the original requested ImBuf */ ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; - if (assign) { - for (int i = 0; i < totviews; i++) { - image_assign_ibuf(ima, ibuf_arr[i], i, entry); - } + for (int i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, entry); } /* "remove" the others (decrease their refcount) */ @@ -4363,7 +4334,6 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int entry, static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int entry, int frame) { struct ImBuf *ibuf = NULL; - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); /* either we load from RenderResult, or we have to load a new one */ @@ -4405,13 +4375,6 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int e } // else printf("pass not found\n"); } - else { - tile->ok = 0; - } - - if (iuser) { - iuser->ok = tile->ok; - } return ibuf; } @@ -4423,8 +4386,6 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i ia = BLI_findlink(&ima->anims, view_id); - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (ia->anim == NULL) { char str[FILE_MAX]; int flags = IB_rect; @@ -4466,12 +4427,6 @@ static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const i if (ibuf) { image_init_after_load(ima, iuser, ibuf); } - else { - tile->ok = 0; - } - } - else { - tile->ok = 0; } return ibuf; @@ -4482,7 +4437,6 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) struct ImBuf *ibuf = NULL; const bool is_multiview = BKE_image_is_multiview(ima); const int totfiles = image_num_files(ima); - ImageTile *tile = BKE_image_get_tile(ima, 0); if (totfiles != BLI_listbase_count_at_most(&ima->anims, totfiles + 1)) { image_free_anims(ima); @@ -4513,12 +4467,7 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) } for (int i = 0; i < totviews; i++) { - if (ibuf_arr[i]) { - image_assign_ibuf(ima, ibuf_arr[i], i, frame); - } - else { - tile->ok = 0; - } + image_assign_ibuf(ima, ibuf_arr[i], i, frame); } /* return the original requested ImBuf */ @@ -4535,19 +4484,11 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) MEM_freeN(ibuf_arr); } - if (iuser) { - iuser->ok = tile->ok; - } - return ibuf; } -static ImBuf *load_image_single(Image *ima, - ImageUser *iuser, - int cfra, - const int view_id, - const bool has_packed, - bool *r_assign) +static ImBuf *load_image_single( + Image *ima, ImageUser *iuser, int cfra, const int view_id, const bool has_packed) { char filepath[FILE_MAX]; struct ImBuf *ibuf = NULL; @@ -4609,9 +4550,8 @@ static ImBuf *load_image_single(Image *ima, #endif { image_init_after_load(ima, iuser, ibuf); - *r_assign = true; - /* make packed file for autopack */ + /* Make packed file for auto-pack. */ if ((has_packed == false) && (G.fileflags & G_FILE_AUTOPACK)) { ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Pack-file"); BLI_addtail(&ima->packedfiles, imapf); @@ -4622,10 +4562,6 @@ static ImBuf *load_image_single(Image *ima, } } } - else { - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); - tile->ok = 0; - } return ibuf; } @@ -4636,7 +4572,6 @@ static ImBuf *load_image_single(Image *ima, static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) { struct ImBuf *ibuf = NULL; - bool assign = false; const bool is_multiview = BKE_image_is_multiview(ima); const int totfiles = image_num_files(ima); bool has_packed = BKE_image_has_packedfile(ima); @@ -4653,10 +4588,8 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) } if (!is_multiview) { - ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &assign); - if (assign) { - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); - } + ibuf = load_image_single(ima, iuser, cfra, 0, has_packed); + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); } else { struct ImBuf **ibuf_arr; @@ -4666,7 +4599,7 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); for (int i = 0; i < totfiles; i++) { - ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, &assign); + ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed); } /* multi-views/multi-layers OpenEXR files directly populate ima, and return NULL ibuf... */ @@ -4679,10 +4612,8 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) int i = (iuser && iuser->multi_index < totviews) ? iuser->multi_index : 0; ibuf = ibuf_arr[i]; - if (assign) { - for (i = 0; i < totviews; i++) { - image_assign_ibuf(ima, ibuf_arr[i], i, 0); - } + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, 0); } /* "remove" the others (decrease their refcount) */ @@ -4696,11 +4627,6 @@ static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) MEM_freeN(ibuf_arr); } - if (iuser) { - ImageTile *tile = BKE_image_get_tile(ima, 0); - iuser->ok = tile->ok; - } - return ibuf; } @@ -4733,14 +4659,6 @@ static ImBuf *image_get_ibuf_multilayer(Image *ima, ImageUser *iuser) } } - ImageTile *tile = BKE_image_get_tile(ima, 0); - if (ibuf == NULL) { - tile->ok = 0; - } - if (iuser) { - iuser->ok = tile->ok; - } - return ibuf; } @@ -4858,7 +4776,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc } } - ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, IMA_NO_INDEX, 0, NULL); /* make ibuf if needed, and initialize it */ if (ibuf == NULL) { @@ -4935,9 +4853,6 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **r_loc ibuf->dither = dither; - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; - return ibuf; } @@ -4994,7 +4909,8 @@ static void image_get_entry_and_index(Image *ima, ImageUser *iuser, int *r_entry * call IMB_freeImBuf to de-reference the image buffer after * it's done handling it. */ -static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, int *r_index) +static ImBuf *image_get_cached_ibuf( + Image *ima, ImageUser *iuser, int *r_entry, int *r_index, bool *r_is_cached_empty) { ImBuf *ibuf = NULL; int entry = 0, index = image_get_multiview_index(ima, iuser); @@ -5002,42 +4918,30 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); ima->lastframe = entry; } else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); ima->lastframe = entry; - - /* counter the fact that image is set as invalid when loading a frame - * that is not in the cache (through image_acquire_ibuf for instance), - * yet we have valid frames in the cache loaded */ - if (ibuf) { - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; - - if (iuser) { - iuser->ok = tile->ok; - } - } } else if (ima->type == IMA_TYPE_MULTILAYER) { entry = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); } else if (ima->type == IMA_TYPE_MULTILAYER) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); } } else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, 0, r_is_cached_empty); } else if (ima->source == IMA_SRC_VIEWER) { /* always verify entirely, not that this shouldn't happen @@ -5047,17 +4951,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_entry, else if (ima->source == IMA_SRC_TILED) { if (ELEM(ima->type, IMA_TYPE_IMAGE, IMA_TYPE_MULTILAYER)) { entry = image_get_tile_number_from_iuser(ima, iuser); - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); - - if ((ima->type == IMA_TYPE_IMAGE) && ibuf != NULL) { - ImageTile *tile = BKE_image_get_tile(ima, entry); - tile->ok = IMA_OK_LOADED; - - /* iuser->ok is useless for tiled images because iuser->tile changes all the time. */ - if (iuser != NULL) { - iuser->ok = 1; - } - } + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, r_is_cached_empty); } } @@ -5078,19 +4972,10 @@ BLI_INLINE bool image_quick_test(Image *ima, const ImageUser *iuser) return false; } - if (iuser) { - if (iuser->ok == 0) { - return false; - } - } - ImageTile *tile = BKE_image_get_tile_from_iuser(ima, iuser); if (tile == NULL) { return false; } - if (tile->ok == 0) { - return false; - } return true; } @@ -5113,7 +4998,11 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) return NULL; } - ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index); + bool is_cached_empty = false; + ibuf = image_get_cached_ibuf(ima, iuser, &entry, &index, &is_cached_empty); + if (is_cached_empty) { + return NULL; + } if (ibuf == NULL) { /* we are sure we have to load the ibuf, using source and type */ @@ -5175,8 +5064,6 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) ima->gen_color, &ima->colorspace_settings); image_assign_ibuf(ima, ibuf, index, 0); - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; } else if (ima->source == IMA_SRC_VIEWER) { if (ima->type == IMA_TYPE_R_RESULT) { @@ -5193,7 +5080,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) /* XXX anim play for viewer nodes not yet supported */ entry = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry); + ibuf = image_get_cached_ibuf_for_index_entry(ima, index, entry, NULL); if (!ibuf) { /* Composite Viewer, all handled in compositor */ @@ -5227,11 +5114,11 @@ ImBuf *BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **r_lock) { ImBuf *ibuf; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); ibuf = image_acquire_ibuf(ima, iuser, r_lock); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); return ibuf; } @@ -5250,9 +5137,9 @@ void BKE_image_release_ibuf(Image *ima, ImBuf *ibuf, void *lock) } if (ibuf) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); IMB_freeImBuf(ibuf); - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); } } @@ -5266,15 +5153,15 @@ bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser) return false; } - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(ima->runtime.cache_mutex); - ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL); + ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL, NULL); if (!ibuf) { ibuf = image_acquire_ibuf(ima, iuser, NULL); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(ima->runtime.cache_mutex); IMB_freeImBuf(ibuf); @@ -5294,6 +5181,7 @@ typedef struct ImagePoolItem { typedef struct ImagePool { ListBase image_buffers; BLI_mempool *memory_pool; + ThreadMutex mutex; } ImagePool; ImagePool *BKE_image_pool_new(void) @@ -5301,21 +5189,28 @@ ImagePool *BKE_image_pool_new(void) ImagePool *pool = MEM_callocN(sizeof(ImagePool), "Image Pool"); pool->memory_pool = BLI_mempool_create(sizeof(ImagePoolItem), 0, 128, BLI_MEMPOOL_NOP); + BLI_mutex_init(&pool->mutex); + return pool; } void BKE_image_pool_free(ImagePool *pool) { /* Use single lock to dereference all the image buffers. */ - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(&pool->mutex); for (ImagePoolItem *item = pool->image_buffers.first; item != NULL; item = item->next) { if (item->ibuf != NULL) { + BLI_mutex_lock(item->image->runtime.cache_mutex); IMB_freeImBuf(item->ibuf); + BLI_mutex_unlock(item->image->runtime.cache_mutex); } } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(&pool->mutex); BLI_mempool_destroy(pool->memory_pool); + + BLI_mutex_end(&pool->mutex); + MEM_freeN(pool); } @@ -5347,28 +5242,34 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool } if (pool == NULL) { - /* pool could be NULL, in this case use general acquire function */ + /* Pool could be NULL, in this case use general acquire function. */ return BKE_image_acquire_ibuf(ima, iuser, NULL); } image_get_entry_and_index(ima, iuser, &entry, &index); + /* Use double-checked locking, to avoid locking when the requested image buffer is already in the + * pool. */ + ibuf = image_pool_find_item(pool, ima, entry, index, &found); if (found) { return ibuf; } - BLI_mutex_lock(image_mutex); + /* Lock the pool, to allow thread-safe modification of the content of the pool. */ + BLI_mutex_lock(&pool->mutex); ibuf = image_pool_find_item(pool, ima, entry, index, &found); - /* will also create item even in cases image buffer failed to load, - * prevents trying to load the same buggy file multiple times - */ + /* Will also create item even in cases image buffer failed to load, + * prevents trying to load the same buggy file multiple times. */ if (!found) { ImagePoolItem *item; - ibuf = image_acquire_ibuf(ima, iuser, NULL); + /* Thread-safe acquisition of an image buffer from the image. + * The acquisition does not use image pools, so there is no risk of recursive or out-of-order + * mutex locking. */ + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); item = BLI_mempool_alloc(pool->memory_pool); item->image = ima; @@ -5379,7 +5280,7 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool BLI_addtail(&pool->image_buffers, item); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(&pool->mutex); return ibuf; } @@ -5489,18 +5390,6 @@ void BKE_image_user_frame_calc(Image *ima, ImageUser *iuser, int cfra) ima->gpuframenr = iuser->framenr; } - if (iuser->ok == 0) { - iuser->ok = 1; - } - - if (ima) { - LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->ok == 0) { - tile->ok = IMA_OK; - } - } - } - iuser->flag &= ~IMA_NEED_FRAME_RECALC; } } @@ -5750,7 +5639,7 @@ bool BKE_image_has_anim(Image *ima) return (BLI_listbase_is_empty(&ima->anims) == false); } -bool BKE_image_has_packedfile(Image *ima) +bool BKE_image_has_packedfile(const Image *ima) { return (BLI_listbase_is_empty(&ima->packedfiles) == false); } @@ -5780,13 +5669,13 @@ bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) bool is_dirty = false; bool is_writable = false; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); while (!IMB_moviecacheIter_done(iter)) { ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf->userflags & IB_BITMAPDIRTY) { + if (ibuf != NULL && ibuf->userflags & IB_BITMAPDIRTY) { is_writable = BKE_image_buffer_format_writable(ibuf); is_dirty = true; break; @@ -5795,7 +5684,7 @@ bool BKE_image_is_dirty_writable(Image *image, bool *r_is_writable) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); if (r_is_writable) { *r_is_writable = is_writable; @@ -5824,26 +5713,28 @@ bool BKE_image_buffer_format_writable(ImBuf *ibuf) void BKE_image_file_format_set(Image *image, int ftype, const ImbFormatOptions *options) { - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); while (!IMB_moviecacheIter_done(iter)) { ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); - ibuf->ftype = ftype; - ibuf->foptions = *options; + if (ibuf != NULL) { + ibuf->ftype = ftype; + ibuf->foptions = *options; + } IMB_moviecacheIter_step(iter); } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); } bool BKE_image_has_loaded_ibuf(Image *image) { bool has_loaded_ibuf = false; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); @@ -5853,7 +5744,7 @@ bool BKE_image_has_loaded_ibuf(Image *image) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return has_loaded_ibuf; } @@ -5866,13 +5757,13 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) { ImBuf *ibuf = NULL; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); while (!IMB_moviecacheIter_done(iter)) { ImBuf *current_ibuf = IMB_moviecacheIter_getImBuf(iter); - if (STREQ(current_ibuf->name, name)) { + if (current_ibuf != NULL && STREQ(current_ibuf->name, name)) { ibuf = current_ibuf; IMB_refImBuf(ibuf); break; @@ -5881,7 +5772,7 @@ ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return ibuf; } @@ -5899,18 +5790,20 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) { ImBuf *ibuf = NULL; - BLI_mutex_lock(image_mutex); + BLI_mutex_lock(image->runtime.cache_mutex); if (image->cache != NULL) { struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); while (!IMB_moviecacheIter_done(iter)) { ibuf = IMB_moviecacheIter_getImBuf(iter); - IMB_refImBuf(ibuf); + if (ibuf != NULL) { + IMB_refImBuf(ibuf); + } break; } IMB_moviecacheIter_free(iter); } - BLI_mutex_unlock(image_mutex); + BLI_mutex_unlock(image->runtime.cache_mutex); return ibuf; } diff --git a/source/blender/blenkernel/intern/image_gen.c b/source/blender/blenkernel/intern/image_gen.c index 1a0cc8c2924..943909cc90f 100644 --- a/source/blender/blenkernel/intern/image_gen.c +++ b/source/blender/blenkernel/intern/image_gen.c @@ -101,13 +101,12 @@ static void image_buf_fill_checker_slice( /* these two passes could be combined into one, but it's more readable and * easy to tweak like this, speed isn't really that much of an issue in this situation... */ - int checkerwidth = 32, dark = 1; + int checkerwidth = 32; int x, y; unsigned char *rect_orig = rect; float *rect_float_orig = rect_float; - float h = 0.0, hoffs = 0.0; float hsv[3] = {0.0f, 0.9f, 0.9f}; float rgb[3]; @@ -119,7 +118,7 @@ static void image_buf_fill_checker_slice( /* checkers */ for (y = offset; y < height + offset; y++) { - dark = powf(-1.0f, floorf(y / checkerwidth)); + int dark = powf(-1.0f, floorf(y / checkerwidth)); for (x = 0; x < width; x++) { if (x % checkerwidth == 0) { @@ -156,10 +155,10 @@ static void image_buf_fill_checker_slice( /* 2nd pass, colored + */ for (y = offset; y < height + offset; y++) { - hoffs = 0.125f * floorf(y / checkerwidth); + float hoffs = 0.125f * floorf(y / checkerwidth); for (x = 0; x < width; x++) { - h = 0.125f * floorf(x / checkerwidth); + float h = 0.125f * floorf(x / checkerwidth); if ((abs((x % checkerwidth) - (checkerwidth / 2)) < 4) && (abs((y % checkerwidth) - (checkerwidth / 2)) < 4)) { diff --git a/source/blender/blenkernel/intern/image_gpu.c b/source/blender/blenkernel/intern/image_gpu.c index 9712e912bed..330af1cc505 100644 --- a/source/blender/blenkernel/intern/image_gpu.c +++ b/source/blender/blenkernel/intern/image_gpu.c @@ -373,15 +373,14 @@ static GPUTexture *image_get_gpu_texture(Image *ima, /* Check if image has been updated and tagged to be updated (full or partial). */ ImageTile *tile = BKE_image_get_tile(ima, 0); if (((ima->gpuflag & IMA_GPU_REFRESH) != 0) || - ((ibuf == NULL || tile == NULL || !tile->ok) && - ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) { + ((ibuf == NULL || tile == NULL) && ((ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) != 0))) { image_free_gpu(ima, true); BLI_freelistN(&ima->gpu_refresh_areas); ima->gpuflag &= ~(IMA_GPU_REFRESH | IMA_GPU_PARTIAL_REFRESH); } else if (ima->gpuflag & IMA_GPU_PARTIAL_REFRESH) { BLI_assert(ibuf); - BLI_assert(tile && tile->ok); + BLI_assert(tile); ImagePartialRefresh *refresh_area; while ((refresh_area = BLI_pophead(&ima->gpu_refresh_areas))) { const int tile_offset_x = refresh_area->tile_x * IMA_PARTIAL_REFRESH_TILE_SIZE; @@ -417,7 +416,7 @@ static GPUTexture *image_get_gpu_texture(Image *ima, /* Check if we have a valid image. If not, we return a dummy * texture with zero bind-code so we don't keep trying. */ - if (tile == NULL || tile->ok == 0) { + if (tile == NULL) { *tex = image_gpu_texture_error_create(textarget); return *tex; } diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 9b72a2d1a72..26a1240080f 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -2013,7 +2013,8 @@ static void nlastrips_to_animdata(ID *id, ListBase *strips) } } - /* try to add this strip to the current NLA-Track (i.e. the 'last' one on the stack atm) */ + /* Try to add this strip to the current NLA-Track + * (i.e. the 'last' one on the stack at the moment). */ if (BKE_nlatrack_add_strip(nlt, strip, false) == 0) { /* trying to add to the current failed (no space), * so add a new track to the stack, and add to that... diff --git a/source/blender/blenkernel/intern/key.c b/source/blender/blenkernel/intern/key.c index 44fc86877a7..c09fcf0715e 100644 --- a/source/blender/blenkernel/intern/key.c +++ b/source/blender/blenkernel/intern/key.c @@ -1904,7 +1904,7 @@ KeyBlock *BKE_keyblock_add_ctime(Key *key, const char *name, const bool do_force return kb; } -/* only the active keyblock */ +/* Only the active key-block. */ KeyBlock *BKE_keyblock_from_object(Object *ob) { Key *key = BKE_key_from_object(ob); @@ -2247,7 +2247,7 @@ void BKE_keyblock_convert_to_mesh(KeyBlock *kb, Mesh *me) * Computes normals (vertices, polygons and/or loops ones) of given mesh for given shape key. * * \param kb: the KeyBlock to use to compute normals. - * \param mesh: the Mesh to apply keyblock to. + * \param mesh: the Mesh to apply key-block to. * \param r_vertnors: if non-NULL, an array of vectors, same length as number of vertices. * \param r_polynors: if non-NULL, an array of vectors, same length as number of polygons. * \param r_loopnors: if non-NULL, an array of vectors, same length as number of loops. @@ -2345,7 +2345,7 @@ void BKE_keyblock_update_from_vertcos(Object *ob, KeyBlock *kb, const float (*ve return; } - /* Copy coords to keyblock */ + /* Copy coords to key-block. */ if (ELEM(ob->type, OB_MESH, OB_LATTICE)) { for (a = 0; a < tot; a++, fp += 3, co++) { copy_v3_v3(fp, *co); @@ -2405,7 +2405,7 @@ void BKE_keyblock_convert_from_vertcos(Object *ob, KeyBlock *kb, const float (*v kb->data = MEM_mallocN(tot * elemsize, __func__); - /* Copy coords to keyblock */ + /* Copy coords to key-block. */ BKE_keyblock_update_from_vertcos(ob, kb, vertCos); } @@ -2594,7 +2594,7 @@ bool BKE_keyblock_move(Object *ob, int org_index, int new_index) } /** - * Check if given keyblock (as index) is used as basis by others in given key. + * Check if given key-block (as index) is used as basis by others in given key. */ bool BKE_keyblock_is_basis(Key *key, const int index) { diff --git a/source/blender/blenkernel/intern/lattice.c b/source/blender/blenkernel/intern/lattice.c index e804f32e5a6..a2da59bca58 100644 --- a/source/blender/blenkernel/intern/lattice.c +++ b/source/blender/blenkernel/intern/lattice.c @@ -131,7 +131,7 @@ static void lattice_free_data(ID *id) static void lattice_foreach_id(ID *id, LibraryForeachIDData *data) { Lattice *lattice = (Lattice *)id; - BKE_LIB_FOREACHID_PROCESS(data, lattice->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lattice->key, IDWALK_CB_USER); } static void lattice_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -196,7 +196,7 @@ IDTypeInfo IDType_ID_LT = { .name = "Lattice", .name_plural = "lattices", .translation_context = BLT_I18NCONTEXT_ID_LATTICE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = lattice_init_data, .copy_data = lattice_copy_data, diff --git a/source/blender/blenkernel/intern/lattice_deform.c b/source/blender/blenkernel/intern/lattice_deform.c index f9437eeaffa..af721412472 100644 --- a/source/blender/blenkernel/intern/lattice_deform.c +++ b/source/blender/blenkernel/intern/lattice_deform.c @@ -115,7 +115,7 @@ LatticeDeformData *BKE_lattice_deform_data_create(const Object *oblatt, const Ob defgrp_index = BKE_id_defgroup_name_index(<->id, lt->vgroup); if (defgrp_index != -1) { - lattice_weights = MEM_malloc_arrayN(sizeof(float), num_points, "lattice_weights"); + lattice_weights = MEM_malloc_arrayN(num_points, sizeof(float), "lattice_weights"); for (int index = 0; index < num_points; index++) { lattice_weights[index] = BKE_defvert_find_weight(dvert + index, defgrp_index); } diff --git a/source/blender/blenkernel/intern/lattice_deform_test.cc b/source/blender/blenkernel/intern/lattice_deform_test.cc index a7cd5c36ec2..bface94d9d4 100644 --- a/source/blender/blenkernel/intern/lattice_deform_test.cc +++ b/source/blender/blenkernel/intern/lattice_deform_test.cc @@ -44,7 +44,7 @@ static void test_lattice_deform_init(LatticeDeformTestContext *ctx, int32_t num_items) { /* Generate random input data between -5 and 5. */ - ctx->coords = (float(*)[3])MEM_malloc_arrayN(sizeof(float[3]), num_items, __func__); + ctx->coords = (float(*)[3])MEM_malloc_arrayN(num_items, sizeof(float[3]), __func__); for (uint32_t index = 0; index < num_items; index++) { ctx->coords[index][0] = (rng->get_float() - 0.5f) * 10; ctx->coords[index][1] = (rng->get_float() - 0.5f) * 10; diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index b489675cd74..e51442b705d 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -170,7 +170,7 @@ ViewLayer *BKE_view_layer_context_active_PLACEHOLDER(const Scene *scene) static ViewLayer *view_layer_add(const char *name) { if (!name) { - name = DATA_("View Layer"); + name = DATA_("ViewLayer"); } ViewLayer *view_layer = MEM_callocN(sizeof(ViewLayer), "View Layer"); @@ -183,7 +183,6 @@ static ViewLayer *view_layer_add(const char *name) view_layer->passflag = SCE_PASS_COMBINED; view_layer->pass_alpha_threshold = 0.5f; view_layer->cryptomatte_levels = 6; - view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE; BKE_freestyle_config_init(&view_layer->freestyle_config); return view_layer; @@ -249,7 +248,7 @@ ViewLayer *BKE_view_layer_add(Scene *scene, BLI_uniquename(&scene->view_layers, view_layer_new, DATA_("ViewLayer"), - '.', + '_', offsetof(ViewLayer, name), sizeof(view_layer_new->name)); @@ -2381,8 +2380,12 @@ static void viewlayer_aov_make_name_unique(ViewLayer *view_layer) if (aov == NULL) { return; } + + /* Don't allow dots, it's incompatible with OpenEXR convention to store channels + * as "layer.pass.channel". */ + BLI_str_replace_char(aov->name, '.', '_'); BLI_uniquename( - &view_layer->aovs, aov, DATA_("AOV"), '.', offsetof(ViewLayerAOV, name), sizeof(aov->name)); + &view_layer->aovs, aov, DATA_("AOV"), '_', offsetof(ViewLayerAOV, name), sizeof(aov->name)); } static void viewlayer_aov_active_set(ViewLayer *view_layer, ViewLayerAOV *aov) diff --git a/source/blender/blenkernel/intern/layer_test.cc b/source/blender/blenkernel/intern/layer_test.cc index c918c0cab67..b5f800dd181 100644 --- a/source/blender/blenkernel/intern/layer_test.cc +++ b/source/blender/blenkernel/intern/layer_test.cc @@ -69,7 +69,7 @@ TEST(view_layer, aov_unique_names) EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0); EXPECT_FALSE((aov2->flag & AOV_CONFLICT) != 0); EXPECT_TRUE(STREQ(aov1->name, "AOV")); - EXPECT_TRUE(STREQ(aov2->name, "AOV.001")); + EXPECT_TRUE(STREQ(aov2->name, "AOV_001")); /* Revert previous resolution */ BLI_strncpy(aov2->name, "AOV", MAX_NAME); @@ -78,7 +78,7 @@ TEST(view_layer, aov_unique_names) EXPECT_FALSE((aov1->flag & AOV_CONFLICT) != 0); EXPECT_FALSE((aov2->flag & AOV_CONFLICT) != 0); EXPECT_TRUE(STREQ(aov1->name, "AOV")); - EXPECT_TRUE(STREQ(aov2->name, "AOV.001")); + EXPECT_TRUE(STREQ(aov2->name, "AOV_001")); /* Resolve by removing AOV resolution */ BKE_view_layer_remove_aov(view_layer, aov2); diff --git a/source/blender/blenkernel/intern/lib_id.c b/source/blender/blenkernel/intern/lib_id.c index 60b6d7ad66d..bc0a8e2b9b8 100644 --- a/source/blender/blenkernel/intern/lib_id.c +++ b/source/blender/blenkernel/intern/lib_id.c @@ -152,8 +152,10 @@ static int lib_id_clear_library_data_users_update_cb(LibraryIDLinkCallbackData * /** * Pull an ID out of a library (make it local). Only call this for IDs that * don't have other library users. + * + * \param flags Same set of `LIB_ID_MAKELOCAL_` flags as passed to `BKE_lib_id_make_local`. */ -void BKE_lib_id_clear_library_data(Main *bmain, ID *id) +void BKE_lib_id_clear_library_data(Main *bmain, ID *id, const int flags) { const bool id_in_mainlist = (id->tag & LIB_TAG_NO_MAIN) == 0 && (id->flag & LIB_EMBEDDED_DATA) == 0; @@ -177,6 +179,10 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id) BKE_lib_libblock_session_uuid_renew(id); } + if ((flags & LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR) != 0 && id->asset_data != NULL) { + BKE_asset_metadata_free(&id->asset_data); + } + /* We need to tag this IDs and all of its users, conceptually new local ID and original linked * ones are two completely different data-blocks that were virtually remapped, even though in * reality they remain the same data. For undo this info is critical now. */ @@ -193,7 +199,7 @@ void BKE_lib_id_clear_library_data(Main *bmain, ID *id) * IDs here, this is down automatically in `lib_id_expand_local_cb()`. */ Key *key = BKE_key_from_id(id); if (key != NULL) { - BKE_lib_id_clear_library_data(bmain, &key->id); + BKE_lib_id_clear_library_data(bmain, &key->id, flags); } DEG_relations_tag_update(bmain); @@ -372,6 +378,7 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) ID *id_self = cb_data->id_self; ID **id_pointer = cb_data->id_pointer; int const cb_flag = cb_data->cb_flag; + const int flags = POINTER_AS_INT(cb_data->user_data); if (cb_flag & IDWALK_CB_LOOPBACK) { /* We should never have anything to do with loop-back pointers here. */ @@ -386,7 +393,7 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) if (*id_pointer != NULL && ID_IS_LINKED(*id_pointer)) { BLI_assert(*id_pointer != id_self); - BKE_lib_id_clear_library_data(bmain, *id_pointer); + BKE_lib_id_clear_library_data(bmain, *id_pointer, flags); } return IDWALK_RET_NOP; } @@ -407,18 +414,19 @@ static int lib_id_expand_local_cb(LibraryIDLinkCallbackData *cb_data) * Expand ID usages of given id as 'extern' (and no more indirect) linked data. * Used by ID copy/make_local functions. */ -void BKE_lib_id_expand_local(Main *bmain, ID *id) +void BKE_lib_id_expand_local(Main *bmain, ID *id, const int flags) { - BKE_library_foreach_ID_link(bmain, id, lib_id_expand_local_cb, bmain, IDWALK_READONLY); + BKE_library_foreach_ID_link( + bmain, id, lib_id_expand_local_cb, POINTER_FROM_INT(flags), IDWALK_READONLY); } /** * Ensure new (copied) ID is fully made local. */ -static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id) +static void lib_id_copy_ensure_local(Main *bmain, const ID *old_id, ID *new_id, const int flags) { if (ID_IS_LINKED(old_id)) { - BKE_lib_id_expand_local(bmain, new_id); + BKE_lib_id_expand_local(bmain, new_id, flags); lib_id_library_local_paths(bmain, old_id->lib, new_id); } } @@ -459,8 +467,8 @@ void BKE_lib_id_make_local_generic(Main *bmain, ID *id, const int flags) } if (force_local) { - BKE_lib_id_clear_library_data(bmain, id); - BKE_lib_id_expand_local(bmain, id); + BKE_lib_id_clear_library_data(bmain, id, flags); + BKE_lib_id_expand_local(bmain, id, flags); } else if (force_copy) { ID *id_new = BKE_id_copy(bmain, id); @@ -648,7 +656,7 @@ ID *BKE_id_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int flag) * XXX TODO: is this behavior OK, or should we need own flag to control that? */ if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { BLI_assert((flag & LIB_ID_COPY_KEEP_LIB) == 0); - lib_id_copy_ensure_local(bmain, id, newid); + lib_id_copy_ensure_local(bmain, id, newid, 0); } else { newid->lib = id->lib; @@ -674,7 +682,10 @@ ID *BKE_id_copy(Main *bmain, const ID *id) * Invokes the appropriate copy method for the block and returns the result in * newid, unless test. Returns true if the block can be copied. */ -ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplicate_flags) +ID *BKE_id_copy_for_duplicate(Main *bmain, + ID *id, + const eDupli_ID_Flags duplicate_flags, + const int copy_flags) { if (id == NULL) { return id; @@ -685,7 +696,7 @@ ID *BKE_id_copy_for_duplicate(Main *bmain, ID *id, const eDupli_ID_Flags duplica return id; } - ID *id_new = BKE_id_copy(bmain, id); + ID *id_new = BKE_id_copy_ex(bmain, id, NULL, copy_flags); /* Copying add one user by default, need to get rid of that one. */ id_us_min(id_new); ID_NEW_SET(id, id_new); @@ -1118,8 +1129,9 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl id->us = 1; } if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - /* Note that 2.8x versioning has tested not to cause conflicts. */ - BLI_assert(bmain->is_locked_for_linking == false || ELEM(type, ID_WS, ID_GR)); + /* Note that 2.8x versioning has tested not to cause conflicts. Node trees are + * skipped in this check to allow adding a geometry node tree for versioning. */ + BLI_assert(bmain->is_locked_for_linking == false || ELEM(type, ID_WS, ID_GR, ID_NT)); ListBase *lb = which_libbase(bmain, type); BKE_main_lock(bmain); @@ -1129,6 +1141,11 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl /* alphabetic insertion: is in new_id */ BKE_main_unlock(bmain); + /* This is important in 'readfile doversion after liblink' context mainly, but is a good + * consistency change in general: ID created for a Main should get that main's current + * library pointer. */ + id->lib = bmain->curlib; + /* TODO: to be removed from here! */ if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) { DEG_id_type_tag(bmain, type); @@ -1301,6 +1318,9 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag); } + /* This is never duplicated, only one existing ID should have a given weak ref to library/ID. */ + new_id->library_weak_reference = NULL; + if ((orig_flag & LIB_ID_COPY_NO_LIB_OVERRIDE) == 0) { if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { /* We do not want to copy existing override rules here, as they would break the proper @@ -2039,8 +2059,8 @@ void BKE_library_make_local(Main *bmain, * currently there are some indirect usages. So instead of making a copy that we'll likely * get rid of later, directly make that data block local. * Saves a tremendous amount of time with complex scenes... */ - BKE_lib_id_clear_library_data(bmain, id); - BKE_lib_id_expand_local(bmain, id); + BKE_lib_id_clear_library_data(bmain, id, 0); + BKE_lib_id_expand_local(bmain, id, 0); id->tag &= ~LIB_TAG_DOIT; if (GS(id->name) == ID_OB) { @@ -2440,6 +2460,10 @@ void BKE_id_blend_write(BlendWriter *writer, ID *id) BKE_asset_metadata_write(writer, id->asset_data); } + if (id->library_weak_reference != NULL) { + BLO_write_struct(writer, LibraryWeakReference, id->library_weak_reference); + } + /* ID_WM's id->properties are considered runtime only, and never written in .blend file. */ if (id->properties && !ELEM(GS(id->name), ID_WM)) { IDP_BlendWrite(writer, id->properties); diff --git a/source/blender/blenkernel/intern/lib_id_delete.c b/source/blender/blenkernel/intern/lib_id_delete.c index 79717fe5f48..502a1197616 100644 --- a/source/blender/blenkernel/intern/lib_id_delete.c +++ b/source/blender/blenkernel/intern/lib_id_delete.c @@ -69,6 +69,10 @@ void BKE_libblock_free_data(ID *id, const bool do_id_user) BKE_asset_metadata_free(&id->asset_data); } + if (id->library_weak_reference != NULL) { + MEM_freeN(id->library_weak_reference); + } + BKE_animdata_free(id, do_id_user); } diff --git a/source/blender/blenkernel/intern/lib_override.c b/source/blender/blenkernel/intern/lib_override.c index 3fead8b0f39..5117c8bd64c 100644 --- a/source/blender/blenkernel/intern/lib_override.c +++ b/source/blender/blenkernel/intern/lib_override.c @@ -865,7 +865,9 @@ static void lib_override_library_create_post_process(Main *bmain, Object *ob_ref = (Object *)id_ref; LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { if (BKE_collection_has_object(collection, ob_ref) && - BKE_view_layer_has_collection(view_layer, collection) && + (view_layer != NULL ? + BKE_view_layer_has_collection(view_layer, collection) : + BKE_collection_has_collection(scene->master_collection, collection)) && !ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { default_instantiating_collection = collection; } @@ -897,6 +899,8 @@ static void lib_override_library_create_post_process(Main *bmain, * \note It will override all IDs tagged with \a LIB_TAG_DOIT, and it does not clear that tag at * its beginning, so caller code can add extra data-blocks to be overridden as well. * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). * \param id_root: The root ID to create an override from. * \param id_reference: Some reference ID used to do some post-processing after overrides have been * created, may be NULL. Typically, the Empty object instantiating the linked collection we @@ -960,6 +964,8 @@ bool BKE_lib_override_library_template_create(struct ID *id) * \note This is a thin wrapper around \a BKE_lib_override_library_create, only extra work is to * actually convert the proxy itself into an override first. * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). * \return true if override was successfully created. */ bool BKE_lib_override_library_proxy_convert(Main *bmain, @@ -994,14 +1000,99 @@ bool BKE_lib_override_library_proxy_convert(Main *bmain, DEG_id_tag_update(&ob_proxy->id, ID_RECALC_COPY_ON_WRITE); + /* In case of proxy conversion, remap all local ID usages to linked IDs to their newly created + * overrides. + * While this might not be 100% the desired behavior, it is likely to be the case most of the + * time. Ref: T91711. */ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + if (!ID_IS_LINKED(id_iter)) { + id_iter->tag |= LIB_TAG_DOIT; + } + } + FOREACH_MAIN_ID_END; + return BKE_lib_override_library_create(bmain, scene, view_layer, id_root, id_reference, NULL); } +static void lib_override_library_proxy_convert_do(Main *bmain, + Scene *scene, + Object *ob_proxy, + BlendFileReadReport *reports) +{ + Object *ob_proxy_group = ob_proxy->proxy_group; + const bool is_override_instancing_object = ob_proxy_group != NULL; + + const bool success = BKE_lib_override_library_proxy_convert(bmain, scene, NULL, ob_proxy); + + if (success) { + CLOG_INFO(&LOG, + 4, + "Proxy object '%s' successfuly converted to library overrides", + ob_proxy->id.name); + /* Remove the instance empty from this scene, the items now have an overridden collection + * instead. */ + if (is_override_instancing_object) { + BKE_scene_collections_object_remove(bmain, scene, ob_proxy_group, true); + } + reports->count.proxies_to_lib_overrides_success++; + } +} + +/** + * Convert all proxy objects into library overrides. + * + * \note Only affects local proxies, linked ones are not affected. + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). + */ +void BKE_lib_override_library_main_proxy_convert(Main *bmain, BlendFileReadReport *reports) +{ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy_group == NULL) { + continue; + } + + lib_override_library_proxy_convert_do(bmain, scene, object, reports); + } + FOREACH_SCENE_OBJECT_END; + + FOREACH_SCENE_OBJECT_BEGIN (scene, object) { + if (object->proxy == NULL) { + continue; + } + + lib_override_library_proxy_convert_do(bmain, scene, object, reports); + } + FOREACH_SCENE_OBJECT_END; + } + + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (ID_IS_LINKED(object)) { + if (object->proxy != NULL) { + CLOG_WARN(&LOG, "Did not try to convert linked proxy object '%s'", object->id.name); + reports->count.linked_proxies++; + } + continue; + } + + if (object->proxy_group != NULL || object->proxy != NULL) { + CLOG_WARN( + &LOG, "Proxy object '%s' failed to be converted to library override", object->id.name); + reports->count.proxies_to_lib_overrides_failures++; + } + } +} + /** * Advanced 'smart' function to resync, re-create fully functional overrides up-to-date with linked * data, from an existing override hierarchy. * * \param id_root: The root liboverride ID to resync from. + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). * \return true if override was successfully resynced. */ bool BKE_lib_override_library_resync(Main *bmain, @@ -1723,6 +1814,9 @@ static int lib_override_libraries_index_define(Main *bmain) * * Then it will handle the resync of necessary IDs (through calls to * #BKE_lib_override_library_resync). + * + * \param view_layer: the active view layer to search instantiated collections in, can be NULL (in + * which case \a scene's master collection children hierarchy is used instead). */ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, @@ -1824,6 +1918,16 @@ void BKE_lib_override_library_delete(Main *bmain, ID *id_root) */ void BKE_lib_override_library_make_local(ID *id) { + if (!ID_IS_OVERRIDE_LIBRARY(id)) { + return; + } + if (ID_IS_OVERRIDE_LIBRARY_VIRTUAL(id)) { + /* We should never directly 'make local' virtual overrides (aka shape keys). */ + BLI_assert_unreachable(); + id->flag &= ~LIB_EMBEDDED_DATA_LIB_OVERRIDE; + return; + } + BKE_lib_override_library_free(&id->override_library, true); Key *shape_key = BKE_key_from_id(id); @@ -2878,6 +2982,31 @@ void BKE_lib_override_library_main_update(Main *bmain) G_MAIN = orig_gmain; } +/** In case an ID is used by another liboverride ID, user may not be allowed to delete it. */ +bool BKE_lib_override_library_id_is_user_deletable(struct Main *bmain, struct ID *id) +{ + if (!(ID_IS_LINKED(id) || ID_IS_OVERRIDE_LIBRARY(id))) { + return true; + } + + /* The only strong known case currently are objects used by override collections. */ + /* TODO: There are most likely other cases... This may need to be addressed in a better way at + * some point. */ + if (GS(id->name) != ID_OB) { + return true; + } + Object *ob = (Object *)id; + LISTBASE_FOREACH (Collection *, collection, &bmain->collections) { + if (!ID_IS_OVERRIDE_LIBRARY(collection)) { + continue; + } + if (BKE_collection_has_object(collection, ob)) { + return false; + } + } + return true; +} + /** * Storage (how to store overriding data into `.blend` files). * diff --git a/source/blender/blenkernel/intern/lib_query.c b/source/blender/blenkernel/intern/lib_query.c index 2ac92828cec..74750a9b61a 100644 --- a/source/blender/blenkernel/intern/lib_query.c +++ b/source/blender/blenkernel/intern/lib_query.c @@ -76,48 +76,52 @@ typedef struct LibraryForeachIDData { BLI_LINKSTACK_DECLARE(ids_todo, ID *); } LibraryForeachIDData; -bool BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag) +/** Check whether current iteration over ID usages should be stopped or not. + * \return true if the iteration should be stopped, false otherwise. */ +bool BKE_lib_query_foreachid_iter_stop(LibraryForeachIDData *data) { - if (!(data->status & IDWALK_STOP)) { - const int flag = data->flag; - ID *old_id = *id_pp; + return (data->status & IDWALK_STOP) != 0; +} - /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic - * caller code. */ - cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear); - - /* Update the callback flags with some extra information regarding overrides: all 'loopback', - * 'internal', 'embedded' etc. ID pointers are never overridable. */ - if (cb_flag & - (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { - cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; - } - - const int callback_return = data->callback( - &(struct LibraryIDLinkCallbackData){.user_data = data->user_data, - .bmain = data->bmain, - .id_owner = data->owner_id, - .id_self = data->self_id, - .id_pointer = id_pp, - .cb_flag = cb_flag}); - if (flag & IDWALK_READONLY) { - BLI_assert(*(id_pp) == old_id); - } - if (old_id && (flag & IDWALK_RECURSE)) { - if (BLI_gset_add((data)->ids_handled, old_id)) { - if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { - BLI_LINKSTACK_PUSH(data->ids_todo, old_id); - } - } - } - if (callback_return & IDWALK_RET_STOP_ITER) { - data->status |= IDWALK_STOP; - return false; - } - return true; +void BKE_lib_query_foreachid_process(LibraryForeachIDData *data, ID **id_pp, int cb_flag) +{ + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; } - return false; + const int flag = data->flag; + ID *old_id = *id_pp; + + /* Update the callback flags with the ones defined (or forbidden) in `data` by the generic + * caller code. */ + cb_flag = ((cb_flag | data->cb_flag) & ~data->cb_flag_clear); + + /* Update the callback flags with some extra information regarding overrides: all 'loopback', + * 'internal', 'embedded' etc. ID pointers are never overridable. */ + if (cb_flag & (IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { + cb_flag |= IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE; + } + + const int callback_return = data->callback( + &(struct LibraryIDLinkCallbackData){.user_data = data->user_data, + .bmain = data->bmain, + .id_owner = data->owner_id, + .id_self = data->self_id, + .id_pointer = id_pp, + .cb_flag = cb_flag}); + if (flag & IDWALK_READONLY) { + BLI_assert(*(id_pp) == old_id); + } + if (old_id && (flag & IDWALK_RECURSE)) { + if (BLI_gset_add((data)->ids_handled, old_id)) { + if (!(callback_return & IDWALK_RET_STOP_RECURSION)) { + BLI_LINKSTACK_PUSH(data->ids_todo, old_id); + } + } + } + if (callback_return & IDWALK_RET_STOP_ITER) { + data->status |= IDWALK_STOP; + } } int BKE_lib_query_foreachid_process_flags_get(LibraryForeachIDData *data) @@ -139,7 +143,7 @@ int BKE_lib_query_foreachid_process_callback_flag_override(LibraryForeachIDData return cb_flag_backup; } -static void library_foreach_ID_link(Main *bmain, +static bool library_foreach_ID_link(Main *bmain, ID *id_owner, ID *id, LibraryIDLinkCallback callback, @@ -158,19 +162,24 @@ void BKE_lib_query_idpropertiesForeachIDLink_callback(IDProperty *id_prop, void BKE_LIB_FOREACHID_PROCESS_ID(data, id_prop->data.pointer, cb_flag); } -bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) +/** Process embedded ID pointers (root nodetrees, master collections, ...). + * + * Those require specific care, since they are technically sub-data of their owner, yet in some + * cases they still behave as regular IDs. */ +void BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) { /* Needed e.g. for callbacks handling relationships. This call shall be absolutely read-only. */ ID *id = *id_pp; const int flag = data->flag; - if (!BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED)) { - return false; + BKE_lib_query_foreachid_process(data, id_pp, IDWALK_CB_EMBEDDED); + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; } BLI_assert(id == *id_pp); if (id == NULL) { - return true; + return; } if (flag & IDWALK_IGNORE_EMBEDDED_ID) { @@ -186,14 +195,24 @@ bool BKE_library_foreach_ID_embedded(LibraryForeachIDData *data, ID **id_pp) } } else { - library_foreach_ID_link( - data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data); + if (!library_foreach_ID_link( + data->bmain, data->owner_id, id, data->callback, data->user_data, data->flag, data)) { + data->status |= IDWALK_STOP; + return; + } } - - return true; } -static void library_foreach_ID_link(Main *bmain, +static void library_foreach_ID_data_cleanup(LibraryForeachIDData *data) +{ + if (data->ids_handled != NULL) { + BLI_gset_free(data->ids_handled, NULL); + BLI_LINKSTACK_FREE(data->ids_todo); + } +} + +/** \return false in case iteration over ID pointers must be stopped, true otherwise. */ +static bool library_foreach_ID_link(Main *bmain, ID *id_owner, ID *id, LibraryIDLinkCallback callback, @@ -210,6 +229,10 @@ static void library_foreach_ID_link(Main *bmain, flag |= IDWALK_READONLY; flag &= ~IDWALK_DO_INTERNAL_RUNTIME_POINTERS; + /* NOTE: This function itself should never be called recursively when IDWALK_RECURSE is set, + * see also comments in #BKE_library_foreach_ID_embedded. + * This is why we can always create this data here, and do not need to try and re-use it from + * `inherit_data`. */ data.ids_handled = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); BLI_LINKSTACK_INIT(data.ids_todo); @@ -224,10 +247,26 @@ static void library_foreach_ID_link(Main *bmain, data.user_data = user_data; #define CALLBACK_INVOKE_ID(check_id, cb_flag) \ - BKE_LIB_FOREACHID_PROCESS_ID(&data, check_id, cb_flag) + { \ + CHECK_TYPE_ANY((check_id), ID *, void *); \ + BKE_lib_query_foreachid_process(&data, (ID **)&(check_id), (cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop(&data)) { \ + library_foreach_ID_data_cleanup(&data); \ + return false; \ + } \ + } \ + ((void)0) #define CALLBACK_INVOKE(check_id_super, cb_flag) \ - BKE_LIB_FOREACHID_PROCESS(&data, check_id_super, cb_flag) + { \ + CHECK_TYPE(&((check_id_super)->id), ID *); \ + BKE_lib_query_foreachid_process(&data, (ID **)&(check_id_super), (cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop(&data)) { \ + library_foreach_ID_data_cleanup(&data); \ + return false; \ + } \ + } \ + ((void)0) for (; id != NULL; id = (flag & IDWALK_RECURSE) ? BLI_LINKSTACK_POP(data.ids_todo) : NULL) { data.self_id = id; @@ -269,6 +308,10 @@ static void library_foreach_ID_link(Main *bmain, to_id_entry = to_id_entry->next) { BKE_lib_query_foreachid_process( &data, to_id_entry->id_pointer.to, to_id_entry->usage_flag); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } } continue; } @@ -292,26 +335,33 @@ static void library_foreach_ID_link(Main *bmain, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, &data); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } AnimData *adt = BKE_animdata_from_id(id); if (adt) { BKE_animdata_foreach_id(adt, &data); + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; + } } const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); if (id_type->foreach_id != NULL) { id_type->foreach_id(id, &data); - if (data.status & IDWALK_STOP) { - break; + if (BKE_lib_query_foreachid_iter_stop(&data)) { + library_foreach_ID_data_cleanup(&data); + return false; } } } - if (data.ids_handled) { - BLI_gset_free(data.ids_handled, NULL); - BLI_LINKSTACK_FREE(data.ids_todo); - } + library_foreach_ID_data_cleanup(&data); + return true; #undef CALLBACK_INVOKE_ID #undef CALLBACK_INVOKE diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 250b8d4d515..014c923f04f 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -132,7 +132,8 @@ static int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) const bool is_obj = (GS(id_owner->name) == ID_OB); const bool is_obj_proxy = (is_obj && (((Object *)id_owner)->proxy || ((Object *)id_owner)->proxy_group)); - const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner)); + const bool is_obj_editmode = (is_obj && BKE_object_is_in_editmode((Object *)id_owner) && + (id_remap_data->flag & ID_REMAP_FORCE_OBDATA_IN_EDITMODE) == 0); const bool is_never_null = ((cb_flag & IDWALK_CB_NEVER_NULL) && (new_id == NULL) && (id_remap_data->flag & ID_REMAP_FORCE_NEVER_NULL_USAGE) == 0); const bool skip_reference = (id_remap_data->flag & ID_REMAP_SKIP_OVERRIDE_LIBRARY) != 0; @@ -345,7 +346,7 @@ static void libblock_remap_data_postprocess_obdata_relink(Main *bmain, Object *o static void libblock_remap_data_postprocess_nodetree_update(Main *bmain, ID *new_id) { /* Update all group nodes using a node group. */ - ntreeUpdateAllUsers(bmain, new_id); + ntreeUpdateAllUsers(bmain, new_id, 0); } /** @@ -669,57 +670,11 @@ void BKE_libblock_relink_ex( DEG_relations_tag_update(bmain); } +static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag); static int id_relink_to_newid_looper(LibraryIDLinkCallbackData *cb_data) { const int cb_flag = cb_data->cb_flag; - if (cb_flag & IDWALK_CB_EMBEDDED) { - return IDWALK_RET_NOP; - } - - ID **id_pointer = cb_data->id_pointer; - ID *id = *id_pointer; - if (id) { - /* See: NEW_ID macro */ - if (id->newid) { - BKE_library_update_ID_link_user(id->newid, id, cb_flag); - id = id->newid; - *id_pointer = id; - } - if (id->tag & LIB_TAG_NEW) { - id->tag &= ~LIB_TAG_NEW; - BKE_libblock_relink_to_newid(id); - } - } - return IDWALK_RET_NOP; -} - -/** - * Similar to #libblock_relink_ex, - * but is remapping IDs to their newid value if non-NULL, in given \a id. - * - * Very specific usage, not sure we'll keep it on the long run, - * currently only used in Object/Collection duplication code... - * - * WARNING: This is a deprecated version of this function, should not be used by new code. See - * #BKE_libblock_relink_to_newid_new below. - */ -void BKE_libblock_relink_to_newid(ID *id) -{ - if (ID_IS_LINKED(id)) { - return; - } - - BKE_library_foreach_ID_link(NULL, id, id_relink_to_newid_looper, NULL, 0); -} - -/* ************************ - * FIXME: Port all usages of #BKE_libblock_relink_to_newid to this - * #BKE_libblock_relink_to_newid_new new code and remove old one. - ************************** */ -static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) -{ - const int cb_flag = cb_data->cb_flag; - if (cb_flag & IDWALK_CB_EMBEDDED) { + if (cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { return IDWALK_RET_NOP; } @@ -728,19 +683,33 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) ID **id_pointer = cb_data->id_pointer; ID *id = *id_pointer; if (id) { + const int remap_flag = POINTER_AS_INT(cb_data->user_data); /* See: NEW_ID macro */ if (id->newid != NULL) { - BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, ID_REMAP_SKIP_INDIRECT_USAGE); + const int remap_flag_final = remap_flag | ID_REMAP_SKIP_INDIRECT_USAGE | + ID_REMAP_SKIP_OVERRIDE_LIBRARY; + BKE_libblock_relink_ex(bmain, id_owner, id, id->newid, (short)remap_flag_final); id = id->newid; } if (id->tag & LIB_TAG_NEW) { id->tag &= ~LIB_TAG_NEW; - BKE_libblock_relink_to_newid_new(bmain, id); + libblock_relink_to_newid(bmain, id, remap_flag); } } return IDWALK_RET_NOP; } +static void libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) +{ + if (ID_IS_LINKED(id)) { + return; + } + + id->tag &= ~LIB_TAG_NEW; + BKE_library_foreach_ID_link( + bmain, id, id_relink_to_newid_looper, POINTER_FROM_INT(remap_flag), 0); +} + /** * Remaps ID usages of given ID to their `id->newid` pointer if not None, and proceeds recursively * in the dependency tree of IDs for all data-blocks tagged with `LIB_TAG_NEW`. @@ -750,7 +719,7 @@ static int id_relink_to_newid_looper_new(LibraryIDLinkCallbackData *cb_data) * Very specific usage, not sure we'll keep it on the long run, * currently only used in Object/Collection duplication code... */ -void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) +void BKE_libblock_relink_to_newid(Main *bmain, ID *id, const int remap_flag) { if (ID_IS_LINKED(id)) { return; @@ -758,6 +727,8 @@ void BKE_libblock_relink_to_newid_new(Main *bmain, ID *id) /* We do not want to have those cached relationship data here. */ BLI_assert(bmain->relations == NULL); - id->tag &= ~LIB_TAG_NEW; - BKE_library_foreach_ID_link(bmain, id, id_relink_to_newid_looper_new, NULL, 0); + BKE_layer_collection_resync_forbid(); + libblock_relink_to_newid(bmain, id, remap_flag); + BKE_layer_collection_resync_allow(); + BKE_main_collection_sync_remap(bmain); } diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c index 36958e36004..1dba353d8ce 100644 --- a/source/blender/blenkernel/intern/library.c +++ b/source/blender/blenkernel/intern/library.c @@ -57,7 +57,7 @@ static void library_free_data(ID *id) static void library_foreach_id(ID *id, LibraryForeachIDData *data) { Library *lib = (Library *)id; - BKE_LIB_FOREACHID_PROCESS(data, lib->parent, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lib->parent, IDWALK_CB_NEVER_SELF); } IDTypeInfo IDType_ID_LI = { diff --git a/source/blender/blenkernel/intern/light.c b/source/blender/blenkernel/intern/light.c index c2b71b85973..05e8d4fe978 100644 --- a/source/blender/blenkernel/intern/light.c +++ b/source/blender/blenkernel/intern/light.c @@ -129,7 +129,8 @@ static void light_foreach_id(ID *id, LibraryForeachIDData *data) Light *lamp = (Light *)id; if (lamp->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&lamp->nodetree)); } } @@ -193,7 +194,7 @@ IDTypeInfo IDType_ID_LA = { .name = "Light", .name_plural = "lights", .translation_context = BLT_I18NCONTEXT_ID_LIGHT, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = light_init_data, .copy_data = light_copy_data, diff --git a/source/blender/blenkernel/intern/lightprobe.c b/source/blender/blenkernel/intern/lightprobe.c index 15733af8ef0..57ad6695db4 100644 --- a/source/blender/blenkernel/intern/lightprobe.c +++ b/source/blender/blenkernel/intern/lightprobe.c @@ -53,8 +53,8 @@ static void lightprobe_foreach_id(ID *id, LibraryForeachIDData *data) { LightProbe *probe = (LightProbe *)id; - BKE_LIB_FOREACHID_PROCESS(data, probe->image, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, probe->visibility_grp, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->image, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, probe->visibility_grp, IDWALK_CB_NOP); } static void lightprobe_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -91,7 +91,7 @@ IDTypeInfo IDType_ID_LP = { .name = "LightProbe", .name_plural = "lightprobes", .translation_context = BLT_I18NCONTEXT_ID_LIGHTPROBE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = lightprobe_init_data, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/linestyle.c b/source/blender/blenkernel/intern/linestyle.c index 19030fca38b..3c305d1fb3f 100644 --- a/source/blender/blenkernel/intern/linestyle.c +++ b/source/blender/blenkernel/intern/linestyle.c @@ -155,12 +155,14 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) for (int i = 0; i < MAX_MTEX; i++) { if (linestyle->mtex[i]) { - BKE_texture_mtex_foreach_id(data, linestyle->mtex[i]); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_texture_mtex_foreach_id(data, linestyle->mtex[i])); } } if (linestyle->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&linestyle->nodetree)); } LISTBASE_FOREACH (LineStyleModifier *, lsm, &linestyle->color_modifiers) { @@ -168,7 +170,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleColorModifier_DistanceFromObject *p = (LineStyleColorModifier_DistanceFromObject *) lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } @@ -177,7 +179,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleAlphaModifier_DistanceFromObject *p = (LineStyleAlphaModifier_DistanceFromObject *) lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } @@ -186,7 +188,7 @@ static void linestyle_foreach_id(ID *id, LibraryForeachIDData *data) LineStyleThicknessModifier_DistanceFromObject *p = (LineStyleThicknessModifier_DistanceFromObject *)lsm; if (p->target) { - BKE_LIB_FOREACHID_PROCESS(data, p->target, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, p->target, IDWALK_CB_NOP); } } } @@ -751,7 +753,7 @@ IDTypeInfo IDType_ID_LS = { .name = "FreestyleLineStyle", .name_plural = "linestyles", .translation_context = BLT_I18NCONTEXT_ID_FREESTYLELINESTYLE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = linestyle_init_data, .copy_data = linestyle_copy_data, diff --git a/source/blender/blenkernel/intern/main.c b/source/blender/blenkernel/intern/main.c index 981f1d4a623..a5de0bc99c8 100644 --- a/source/blender/blenkernel/intern/main.c +++ b/source/blender/blenkernel/intern/main.c @@ -35,6 +35,7 @@ #include "DNA_ID.h" #include "BKE_global.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" @@ -204,6 +205,17 @@ void BKE_main_free(Main *mainvar) MEM_freeN(mainvar); } +/* Check whether given `bmain` is empty or contains some IDs. */ +bool BKE_main_is_empty(struct Main *bmain) +{ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + return false; + } + FOREACH_MAIN_ID_END; + return true; +} + void BKE_main_lock(struct Main *bmain) { BLI_spin_lock((SpinLock *)bmain->lock); @@ -316,7 +328,7 @@ void BKE_main_relations_free(Main *bmain) /** Set or clear given `tag` in all relation entries of given `bmain`. */ void BKE_main_relations_tag_set(struct Main *bmain, - const MainIDRelationsEntryTags tag, + const eMainIDRelationsEntryTags tag, const bool value) { if (bmain->relations == NULL) { @@ -358,6 +370,208 @@ GSet *BKE_main_gset_create(Main *bmain, GSet *gset) return gset; } +/* Utils for ID's library weak reference API. */ +typedef struct LibWeakRefKey { + char filepath[FILE_MAX]; + char id_name[MAX_ID_NAME]; +} LibWeakRefKey; + +static LibWeakRefKey *lib_weak_key_create(LibWeakRefKey *key, + const char *lib_path, + const char *id_name) +{ + if (key == NULL) { + key = MEM_mallocN(sizeof(*key), __func__); + } + BLI_strncpy(key->filepath, lib_path, sizeof(key->filepath)); + BLI_strncpy(key->id_name, id_name, sizeof(key->id_name)); + return key; +} + +static uint lib_weak_key_hash(const void *ptr) +{ + const LibWeakRefKey *string_pair = ptr; + uint hash = BLI_ghashutil_strhash_p_murmur(string_pair->filepath); + return hash ^ BLI_ghashutil_strhash_p_murmur(string_pair->id_name); +} + +static bool lib_weak_key_cmp(const void *a, const void *b) +{ + const LibWeakRefKey *string_pair_a = a; + const LibWeakRefKey *string_pair_b = b; + + return !(STREQ(string_pair_a->filepath, string_pair_b->filepath) && + STREQ(string_pair_a->id_name, string_pair_b->id_name)); +} + +/** + * Generate a mapping between 'library path' of an ID (as a pair (relative blend file path, id + * name)), and a current local ID, if any. + * + * This uses the information stored in `ID.library_weak_reference`. + */ +GHash *BKE_main_library_weak_reference_create(Main *bmain) +{ + GHash *library_weak_reference_mapping = BLI_ghash_new( + lib_weak_key_hash, lib_weak_key_cmp, __func__); + + ListBase *lb; + FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { + ID *id_iter = lb->first; + if (id_iter == NULL) { + continue; + } + if (!BKE_idtype_idcode_append_is_reusable(GS(id_iter->name))) { + continue; + } + BLI_assert(BKE_idtype_idcode_is_linkable(GS(id_iter->name))); + + FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) { + if (id_iter->library_weak_reference == NULL) { + continue; + } + LibWeakRefKey *key = lib_weak_key_create(NULL, + id_iter->library_weak_reference->library_filepath, + id_iter->library_weak_reference->library_id_name); + BLI_ghash_insert(library_weak_reference_mapping, key, id_iter); + } + FOREACH_MAIN_LISTBASE_ID_END; + } + FOREACH_MAIN_LISTBASE_END; + + return library_weak_reference_mapping; +} + +/** + * Destroy the data generated by #BKE_main_library_weak_reference_create. + */ +void BKE_main_library_weak_reference_destroy(GHash *library_weak_reference_mapping) +{ + BLI_ghash_free(library_weak_reference_mapping, MEM_freeN, NULL); +} + +/** + * Search for a local ID matching the given linked ID reference. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID + * type. + */ +ID *BKE_main_library_weak_reference_search_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name) +{ + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + return (ID *)BLI_ghash_lookup(library_weak_reference_mapping, &key); +} + +/** + * Add the given ID weak library reference to given local ID and the runtime mapping. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param new_id: New local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_add_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *new_id) +{ + BLI_assert(GS(library_id_name) == GS(new_id->name)); + BLI_assert(new_id->library_weak_reference == NULL); + BLI_assert(BKE_idtype_idcode_append_is_reusable(GS(new_id->name))); + + new_id->library_weak_reference = MEM_mallocN(sizeof(*(new_id->library_weak_reference)), + __func__); + + LibWeakRefKey *key = lib_weak_key_create(NULL, library_filepath, library_id_name); + void **id_p; + const bool already_exist_in_mapping = BLI_ghash_ensure_p( + library_weak_reference_mapping, key, &id_p); + BLI_assert(!already_exist_in_mapping); + UNUSED_VARS_NDEBUG(already_exist_in_mapping); + + BLI_strncpy(new_id->library_weak_reference->library_filepath, + library_filepath, + sizeof(new_id->library_weak_reference->library_filepath)); + BLI_strncpy(new_id->library_weak_reference->library_id_name, + library_id_name, + sizeof(new_id->library_weak_reference->library_id_name)); + *id_p = new_id; +} + +/** + * Update the status of the given ID weak library reference in current local IDs and the runtime + * mapping. + * + * This effectively transfers the 'ownership' of the given weak reference from `old_id` to + * `new_id`. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param old_id: Existing local ID matching given weak reference. + * \param new_id: New local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_update_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *old_id, + ID *new_id) +{ + BLI_assert(GS(library_id_name) == GS(old_id->name)); + BLI_assert(GS(library_id_name) == GS(new_id->name)); + BLI_assert(old_id->library_weak_reference != NULL); + BLI_assert(new_id->library_weak_reference == NULL); + BLI_assert(STREQ(old_id->library_weak_reference->library_filepath, library_filepath)); + BLI_assert(STREQ(old_id->library_weak_reference->library_id_name, library_id_name)); + + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + void **id_p = BLI_ghash_lookup_p(library_weak_reference_mapping, &key); + BLI_assert(id_p != NULL && *id_p == old_id); + + new_id->library_weak_reference = old_id->library_weak_reference; + old_id->library_weak_reference = NULL; + *id_p = new_id; +} + +/** + * Remove the given ID weak library reference from the given local ID and the runtime mapping. + * + * \param library_weak_reference_mapping: the mapping data generated by + * #BKE_main_library_weak_reference_create. + * \param library_relative_path: the path of a blend file library (relative to current working + * one). + * \param library_id_name: the full ID name, including the leading two chars encoding the ID type. + * \param old_id: Existing local ID matching given weak reference. + */ +void BKE_main_library_weak_reference_remove_item(GHash *library_weak_reference_mapping, + const char *library_filepath, + const char *library_id_name, + ID *old_id) +{ + BLI_assert(GS(library_id_name) == GS(old_id->name)); + BLI_assert(old_id->library_weak_reference != NULL); + + LibWeakRefKey key; + lib_weak_key_create(&key, library_filepath, library_id_name); + + BLI_assert(BLI_ghash_lookup(library_weak_reference_mapping, &key) == old_id); + BLI_ghash_remove(library_weak_reference_mapping, &key, MEM_freeN, NULL); + + MEM_SAFE_FREE(old_id->library_weak_reference); +} + /** * Generates a raw .blend file thumbnail data from given image. * diff --git a/source/blender/blenkernel/intern/mask.c b/source/blender/blenkernel/intern/mask.c index a93fcb6e8e0..1d3ebaac303 100644 --- a/source/blender/blenkernel/intern/mask.c +++ b/source/blender/blenkernel/intern/mask.c @@ -254,7 +254,7 @@ IDTypeInfo IDType_ID_MSK = { .name = "Mask", .name_plural = "masks", .translation_context = BLT_I18NCONTEXT_ID_MASK, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = NULL, .copy_data = mask_copy_data, diff --git a/source/blender/blenkernel/intern/mask_rasterize.c b/source/blender/blenkernel/intern/mask_rasterize.c index e04e5fceec6..0c40a1b5078 100644 --- a/source/blender/blenkernel/intern/mask_rasterize.c +++ b/source/blender/blenkernel/intern/mask_rasterize.c @@ -151,7 +151,7 @@ BLI_INLINE unsigned int clampis_uint(const unsigned int v, } /* --------------------------------------------------------------------- */ -/* local structs for mask rasterizeing */ +/* local structs for mask rasterizing */ /* --------------------------------------------------------------------- */ /** diff --git a/source/blender/blenkernel/intern/material.c b/source/blender/blenkernel/intern/material.c index 13b5bca5638..3a4e39812ab 100644 --- a/source/blender/blenkernel/intern/material.c +++ b/source/blender/blenkernel/intern/material.c @@ -46,6 +46,7 @@ #include "DNA_meta_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" +#include "DNA_particle_types.h" #include "DNA_pointcloud_types.h" #include "DNA_scene_types.h" #include "DNA_volume_types.h" @@ -62,7 +63,6 @@ #include "BKE_curve.h" #include "BKE_displist.h" #include "BKE_editmesh.h" -#include "BKE_font.h" #include "BKE_gpencil.h" #include "BKE_icons.h" #include "BKE_idtype.h" @@ -73,7 +73,9 @@ #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" +#include "BKE_object.h" #include "BKE_scene.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -164,15 +166,14 @@ static void material_foreach_id(ID *id, LibraryForeachIDData *data) { Material *material = (Material *)id; /* Nodetrees **are owned by IDs**, treat them as mere sub-data and not real ID! */ - if (!BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree)) { - return; - } + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&material->nodetree)); if (material->texpaintslot != NULL) { - BKE_LIB_FOREACHID_PROCESS(data, material->texpaintslot->ima, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->texpaintslot->ima, IDWALK_CB_NOP); } if (material->gp_style != NULL) { - BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->sima, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, material->gp_style->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->sima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, material->gp_style->ima, IDWALK_CB_USER); } } @@ -259,7 +260,7 @@ IDTypeInfo IDType_ID_MA = { .name = "Material", .name_plural = "materials", .translation_context = BLT_I18NCONTEXT_ID_MATERIAL, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = material_init_data, .copy_data = material_copy_data, @@ -462,21 +463,33 @@ static void material_data_index_remove_id(ID *id, short index) } } -bool BKE_object_material_slot_used(ID *id, short actcol) +bool BKE_object_material_slot_used(Object *object, short actcol) { - /* ensure we don't try get materials from non-obdata */ - BLI_assert(OB_DATA_SUPPORT_ID(GS(id->name))); + if (!BKE_object_supports_material_slots(object)) { + return false; + } - switch (GS(id->name)) { + LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { + if (psys->part->omat == actcol) { + return true; + } + } + + ID *ob_data = object->data; + if (ob_data == NULL || !OB_DATA_SUPPORT_ID(GS(ob_data->name))) { + return false; + } + + switch (GS(ob_data->name)) { case ID_ME: - return BKE_mesh_material_index_used((Mesh *)id, actcol - 1); + return BKE_mesh_material_index_used((Mesh *)ob_data, actcol - 1); case ID_CU: - return BKE_curve_material_index_used((Curve *)id, actcol - 1); + return BKE_curve_material_index_used((Curve *)ob_data, actcol - 1); case ID_MB: - /* meta-elems don't have materials atm */ + /* Meta-elements don't support materials at the moment. */ return false; case ID_GD: - return BKE_gpencil_material_index_used((bGPdata *)id, actcol - 1); + return BKE_gpencil_material_index_used((bGPdata *)ob_data, actcol - 1); default: return false; } @@ -886,7 +899,17 @@ void BKE_object_materials_test(Main *bmain, Object *ob, ID *id) return; } - BKE_object_material_resize(bmain, ob, *totcol, false); + if ((ob->id.tag & LIB_TAG_MISSING) == 0 && (id->tag & LIB_TAG_MISSING) != 0) { + /* Exception: In case the object is a valid data, but its obdata is an empty place-holder, + * use object's material slots amount as reference. + * This avoids loosing materials in a local object when its linked obdata gets missing. + * See T92780. */ + BKE_id_material_resize(bmain, id, (short)ob->totcol, false); + } + else { + /* Normal case: the use the obdata amount of materials slots to update the object's one. */ + BKE_object_material_resize(bmain, ob, *totcol, false); + } } void BKE_objects_materials_test_all(Main *bmain, ID *id) diff --git a/source/blender/blenkernel/intern/mball.c b/source/blender/blenkernel/intern/mball.c index 45cf0f17840..48d31361eac 100644 --- a/source/blender/blenkernel/intern/mball.c +++ b/source/blender/blenkernel/intern/mball.c @@ -112,7 +112,7 @@ static void metaball_foreach_id(ID *id, LibraryForeachIDData *data) { MetaBall *metaball = (MetaBall *)id; for (int i = 0; i < metaball->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, metaball->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, metaball->mat[i], IDWALK_CB_USER); } } @@ -188,7 +188,7 @@ IDTypeInfo IDType_ID_MB = { .name = "Metaball", .name_plural = "metaballs", .translation_context = BLT_I18NCONTEXT_ID_METABALL, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = metaball_init_data, .copy_data = metaball_copy_data, diff --git a/source/blender/blenkernel/intern/mball_tessellate.c b/source/blender/blenkernel/intern/mball_tessellate.c index 9dd583b4c6b..a2590171abd 100644 --- a/source/blender/blenkernel/intern/mball_tessellate.c +++ b/source/blender/blenkernel/intern/mball_tessellate.c @@ -454,7 +454,7 @@ static void make_face(PROCESS *process, int i1, int i2, int i3, int i4) cur = process->indices[process->curindex++]; - /* displists now support array drawing, we treat tri's as fake quad */ + /* #DispList supports array drawing, treat tri's as fake quad. */ cur[0] = i1; cur[1] = i2; diff --git a/source/blender/blenkernel/intern/mesh.c b/source/blender/blenkernel/intern/mesh.c index d631993c4e8..7277f7ad209 100644 --- a/source/blender/blenkernel/intern/mesh.c +++ b/source/blender/blenkernel/intern/mesh.c @@ -176,10 +176,10 @@ static void mesh_free_data(ID *id) static void mesh_foreach_id(ID *id, LibraryForeachIDData *data) { Mesh *mesh = (Mesh *)id; - BKE_LIB_FOREACHID_PROCESS(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, mesh->key, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->texcomesh, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->key, IDWALK_CB_USER); for (int i = 0; i < mesh->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, mesh->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mesh->mat[i], IDWALK_CB_USER); } } @@ -360,7 +360,7 @@ IDTypeInfo IDType_ID_ME = { .name = "Mesh", .name_plural = "meshes", .translation_context = BLT_I18NCONTEXT_ID_MESH, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = mesh_init_data, .copy_data = mesh_copy_data, diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 07dc6db05aa..adfbe4b8c94 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -41,6 +41,7 @@ #include "BKE_deform.h" #include "BKE_displist.h" #include "BKE_editmesh.h" +#include "BKE_geometry_set.hh" #include "BKE_key.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" @@ -51,6 +52,7 @@ #include "BKE_mesh_runtime.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_spline.hh" /* these 2 are only used by conversion functions */ #include "BKE_curve.h" /* -- */ @@ -58,6 +60,8 @@ /* -- */ #include "BKE_pointcloud.h" +#include "BKE_curve_to_mesh.hh" + #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -237,7 +241,7 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, int a, b, ofs, vertcount, startvert, totvert = 0, totedge = 0, totloop = 0, totpoly = 0; int p1, p2, p3, p4, *index; const bool conv_polys = ( - /* 2d polys are filled with DL_INDEX3 displists */ + /* 2D polys are filled with #DispList.type == #DL_INDEX3. */ (CU_DO_2DFILL(cu) == false) || /* surf polys are never filled */ BKE_curve_type_get(cu) == OB_SURF); @@ -450,10 +454,10 @@ static int mesh_nurbs_displist_to_mdata(const Curve *cu, mloopuv->uv[1] = (v % dl->nr) / (float)orco_sizeu; /* cyclic correction */ - if ((i == 1 || i == 2) && mloopuv->uv[0] == 0.0f) { + if ((ELEM(i, 1, 2)) && mloopuv->uv[0] == 0.0f) { mloopuv->uv[0] = 1.0f; } - if ((i == 0 || i == 1) && mloopuv->uv[1] == 0.0f) { + if ((ELEM(i, 0, 1)) && mloopuv->uv[1] == 0.0f) { mloopuv->uv[1] = 1.0f; } } @@ -573,90 +577,6 @@ Mesh *BKE_mesh_new_nomain_from_curve(const Object *ob) return BKE_mesh_new_nomain_from_curve_displist(ob, &disp); } -static void mesh_from_nurbs_displist(Object *ob, ListBase *dispbase, const char *obdata_name) -{ - if (ob->runtime.data_eval && GS(((ID *)ob->runtime.data_eval)->name) != ID_ME) { - return; - } - - Mesh *me_eval = (Mesh *)ob->runtime.data_eval; - Mesh *me; - MVert *allvert = nullptr; - MEdge *alledge = nullptr; - MLoop *allloop = nullptr; - MLoopUV *alluv = nullptr; - MPoly *allpoly = nullptr; - int totvert, totedge, totloop, totpoly; - - Curve *cu = (Curve *)ob->data; - - if (me_eval == nullptr) { - if (mesh_nurbs_displist_to_mdata(cu, - dispbase, - &allvert, - &totvert, - &alledge, - &totedge, - &allloop, - &allpoly, - &alluv, - &totloop, - &totpoly) != 0) { - /* Error initializing */ - return; - } - - /* make mesh */ - me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); - - me->totvert = totvert; - me->totedge = totedge; - me->totloop = totloop; - me->totpoly = totpoly; - - me->mvert = (MVert *)CustomData_add_layer( - &me->vdata, CD_MVERT, CD_ASSIGN, allvert, me->totvert); - me->medge = (MEdge *)CustomData_add_layer( - &me->edata, CD_MEDGE, CD_ASSIGN, alledge, me->totedge); - me->mloop = (MLoop *)CustomData_add_layer( - &me->ldata, CD_MLOOP, CD_ASSIGN, allloop, me->totloop); - me->mpoly = (MPoly *)CustomData_add_layer( - &me->pdata, CD_MPOLY, CD_ASSIGN, allpoly, me->totpoly); - - if (alluv) { - const char *uvname = "UVMap"; - me->mloopuv = (MLoopUV *)CustomData_add_layer_named( - &me->ldata, CD_MLOOPUV, CD_ASSIGN, alluv, me->totloop, uvname); - } - - BKE_mesh_calc_normals(me); - } - else { - me = (Mesh *)BKE_id_new_nomain(ID_ME, obdata_name); - - ob->runtime.data_eval = nullptr; - BKE_mesh_nomain_to_mesh(me_eval, me, ob, &CD_MASK_MESH, true); - } - - me->totcol = cu->totcol; - me->mat = cu->mat; - - mesh_copy_texture_space_from_curve_type(cu, me); - - cu->mat = nullptr; - cu->totcol = 0; - - /* Do not decrement ob->data usercount here, - * it's done at end of func with BKE_id_free_us() call. */ - ob->data = me; - ob->type = OB_MESH; - - /* For temporary objects in BKE_mesh_new_from_object don't remap - * the entire scene with associated depsgraph updates, which are - * problematic for renderers exporting data. */ - BKE_id_free(nullptr, cu); -} - struct EdgeLink { struct EdgeLink *next, *prev; void *edge; @@ -948,54 +868,32 @@ void BKE_pointcloud_to_mesh(Main *bmain, Depsgraph *depsgraph, Scene *UNUSED(sce BKE_object_free_derived_caches(ob); } -/* Create a temporary object to be used for nurbs-to-mesh conversion. - * - * This is more complex that it should be because #mesh_from_nurbs_displist will do more than - * simply conversion and will attempt to take over ownership of evaluated result and will also - * modify the input object. */ -static Object *object_for_curve_to_mesh_create(Object *object) +/* Create a temporary object to be used for nurbs-to-mesh conversion. */ +static Object *object_for_curve_to_mesh_create(const Object *object) { - Curve *curve = (Curve *)object->data; + const Curve *curve = (const Curve *)object->data; - /* Create object itself. */ + /* Create a temporary object which can be evaluated and modified by generic + * curve evaluation (hence the #LIB_ID_COPY_SET_COPIED_ON_WRITE flag). */ Object *temp_object = (Object *)BKE_id_copy_ex( - nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE); + nullptr, &object->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE); /* Remove all modifiers, since we don't want them to be applied. */ BKE_object_free_modifiers(temp_object, LIB_ID_CREATE_NO_USER_REFCOUNT); - /* Copy relevant evaluated fields of curve cache. - * - * Note that there are extra fields in there like bevel and path, but those are not needed during - * conversion, so they are not copied to save unnecessary allocations. */ - if (temp_object->runtime.curve_cache == nullptr) { - temp_object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), - "CurveCache for curve types"); - } - - if (object->runtime.curve_cache != nullptr) { - BKE_displist_copy(&temp_object->runtime.curve_cache->disp, &object->runtime.curve_cache->disp); - } - - /* Constructive modifiers will use mesh to store result. */ - if (object->runtime.data_eval != nullptr) { - BKE_id_copy_ex( - nullptr, object->runtime.data_eval, &temp_object->runtime.data_eval, LIB_ID_COPY_LOCALIZE); - } - - /* Need to create copy of curve itself as well, it will be freed by underlying conversion - * functions. - * - * NOTE: Copies the data, but not the shapekeys. */ - BKE_id_copy_ex( - nullptr, (const ID *)object->data, (ID **)&temp_object->data, LIB_ID_COPY_LOCALIZE); + /* Need to create copy of curve itself as well, since it will be changed by the curve evaluation + * process. NOTE: Copies the data, but not the shape-keys. */ + temp_object->data = BKE_id_copy_ex(nullptr, + (const ID *)object->data, + nullptr, + LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_SET_COPIED_ON_WRITE); Curve *temp_curve = (Curve *)temp_object->data; /* Make sure texture space is calculated for a copy of curve, it will be used for the final * result. */ BKE_curve_texspace_calc(temp_curve); - /* Temporarily set edit so we get updates from edit mode, but also because for text datablocks + /* Temporarily set edit so we get updates from edit mode, but also because for text data-blocks * copying it while in edit mode gives invalid data structures. */ temp_curve->editfont = curve->editfont; temp_curve->editnurb = curve->editnurb; @@ -1006,23 +904,10 @@ static Object *object_for_curve_to_mesh_create(Object *object) /** * Populate `object->runtime.curve_cache` which is then used to create the mesh. */ -static void curve_to_mesh_eval_ensure(Object *object) +static void curve_to_mesh_eval_ensure(Object &object) { - Curve *curve = (Curve *)object->data; - Curve remapped_curve = *curve; - Object remapped_object = *object; - BKE_object_runtime_reset(&remapped_object); - - remapped_object.data = &remapped_curve; - - if (object->runtime.curve_cache == nullptr) { - object->runtime.curve_cache = (CurveCache *)MEM_callocN(sizeof(CurveCache), - "CurveCache for Curve"); - } - - /* Temporarily share the curve-cache with the temporary object, owned by `object`. */ - remapped_object.runtime.curve_cache = object->runtime.curve_cache; - + BLI_assert(GS(static_cast(object.data)->name) == ID_CU); + Curve &curve = *static_cast(object.data); /* Clear all modifiers for the bevel object. * * This is because they can not be reliably evaluated for an original object (at least because @@ -1031,83 +916,97 @@ static void curve_to_mesh_eval_ensure(Object *object) * So we create temporary copy of the object which will use same data as the original bevel, but * will have no modifiers. */ Object bevel_object = {{nullptr}}; - if (remapped_curve.bevobj != nullptr) { - bevel_object = *remapped_curve.bevobj; + if (curve.bevobj != nullptr) { + bevel_object = *curve.bevobj; BLI_listbase_clear(&bevel_object.modifiers); BKE_object_runtime_reset(&bevel_object); - remapped_curve.bevobj = &bevel_object; + curve.bevobj = &bevel_object; } /* Same thing for taper. */ Object taper_object = {{nullptr}}; - if (remapped_curve.taperobj != nullptr) { - taper_object = *remapped_curve.taperobj; + if (curve.taperobj != nullptr) { + taper_object = *curve.taperobj; BLI_listbase_clear(&taper_object.modifiers); BKE_object_runtime_reset(&taper_object); - remapped_curve.taperobj = &taper_object; + curve.taperobj = &taper_object; } /* NOTE: We don't have dependency graph or scene here, so we pass nullptr. This is all fine since * they are only used for modifier stack, which we have explicitly disabled for all objects. * * TODO(sergey): This is a very fragile logic, but proper solution requires re-writing quite a - * bit of internal functions (#mesh_from_nurbs_displist, BKE_mesh_nomain_to_mesh) and also - * Mesh From Curve operator. + * bit of internal functions (#BKE_mesh_nomain_to_mesh) and also Mesh From Curve operator. * Brecht says hold off with that. */ - Mesh *mesh_eval = nullptr; - BKE_displist_make_curveTypes_forRender( - nullptr, nullptr, &remapped_object, &remapped_object.runtime.curve_cache->disp, &mesh_eval); + BKE_displist_make_curveTypes(nullptr, nullptr, &object, true); - /* NOTE: this is to be consistent with `BKE_displist_make_curveTypes()`, however that is not a - * real issue currently, code here is broken in more than one way, fix(es) will be done - * separately. */ - if (mesh_eval != nullptr) { - BKE_object_eval_assign_data(&remapped_object, &mesh_eval->id, true); - } - - /* Owned by `object` & needed by the caller to create the mesh. */ - remapped_object.runtime.curve_cache = nullptr; - - BKE_object_runtime_free_data(&remapped_object); - BKE_object_runtime_free_data(&taper_object); + BKE_object_runtime_free_data(&bevel_object); BKE_object_runtime_free_data(&taper_object); } -static Mesh *mesh_new_from_curve_type_object(Object *object) +/* Necessary because #BKE_object_get_evaluated_mesh doesn't look in the geometry set yet. */ +static const Mesh *get_evaluated_mesh_from_object(const Object *object) { - Curve *curve = (Curve *)object->data; + const Mesh *mesh = BKE_object_get_evaluated_mesh(object); + if (mesh) { + return mesh; + } + GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; + if (geometry_set_eval) { + return geometry_set_eval->get_mesh_for_read(); + } + return nullptr; +} + +static const CurveEval *get_evaluated_curve_from_object(const Object *object) +{ + GeometrySet *geometry_set_eval = object->runtime.geometry_set_eval; + if (geometry_set_eval) { + return geometry_set_eval->get_curve_for_read(); + } + return nullptr; +} + +static Mesh *mesh_new_from_evaluated_curve_type_object(const Object *evaluated_object) +{ + const Mesh *mesh = get_evaluated_mesh_from_object(evaluated_object); + if (mesh) { + return BKE_mesh_copy_for_eval(mesh, false); + } + const CurveEval *curve = get_evaluated_curve_from_object(evaluated_object); + if (curve) { + return blender::bke::curve_to_wire_mesh(*curve); + } + return nullptr; +} + +static Mesh *mesh_new_from_curve_type_object(const Object *object) +{ + /* If the object is evaluated, it should either have an evaluated mesh or curve data already. + * The mesh can be duplicated, or the curve converted to wire mesh edges. */ + if (DEG_is_evaluated_object(object)) { + return mesh_new_from_evaluated_curve_type_object(object); + } + + /* Otherwise, create a temporary "fake" evaluated object and try again. This might have + * different results, since in order to avoid having adverse affects to other original objects, + * modifiers are cleared. An alternative would be to create a temporary depsgraph only for this + * object and its dependencies. */ Object *temp_object = object_for_curve_to_mesh_create(object); - Curve *temp_curve = (Curve *)temp_object->data; + ID *temp_data = static_cast(temp_object->data); + curve_to_mesh_eval_ensure(*temp_object); - /* When input object is an original one, we don't have evaluated curve cache yet, so need to - * create it in the temporary object. */ - if (!DEG_is_evaluated_object(object)) { - curve_to_mesh_eval_ensure(temp_object); + /* If evaluating the curve replaced object data with different data, free the original data. */ + if (temp_data != temp_object->data) { + BKE_id_free(nullptr, temp_data); } - /* Reset pointers before conversion. */ - temp_curve->editfont = nullptr; - temp_curve->editnurb = nullptr; - - /* Convert to mesh. */ - mesh_from_nurbs_displist( - temp_object, &temp_object->runtime.curve_cache->disp, curve->id.name + 2); - - /* #mesh_from_nurbs_displist changes the type to a mesh, check it worked. If it didn't - * the curve did not have any segments or otherwise would have generated an empty mesh. */ - if (temp_object->type != OB_MESH) { - BKE_id_free(nullptr, temp_object->data); - BKE_id_free(nullptr, temp_object); - return nullptr; - } - - Mesh *mesh_result = (Mesh *)temp_object->data; + Mesh *mesh = mesh_new_from_evaluated_curve_type_object(temp_object); + BKE_id_free(nullptr, temp_object->data); BKE_id_free(nullptr, temp_object); - /* NOTE: Materials are copied in #mesh_from_nurbs_displist(). */ - - return mesh_result; + return mesh; } static Mesh *mesh_new_from_mball_object(Object *object) @@ -1118,7 +1017,7 @@ static Mesh *mesh_new_from_mball_object(Object *object) * balls and all evaluated child meta balls (since polygonization is only stored in the mother * ball). * - * We create empty mesh so scripters don't run into None objects. */ + * Create empty mesh so script-authors don't run into None objects. */ if (!DEG_is_evaluated_object(object) || object->runtime.curve_cache == nullptr || BLI_listbase_is_empty(&object->runtime.curve_cache->disp)) { return (Mesh *)BKE_id_new_nomain(ID_ME, ((ID *)object->data)->name + 2); @@ -1290,7 +1189,7 @@ Mesh *BKE_mesh_new_from_object_to_bmain(Main *bmain, return mesh_in_bmain; } - /* Make sure mesh only points original datablocks, also increase users of materials and other + /* Make sure mesh only points original data-blocks, also increase users of materials and other * possibly referenced data-blocks. * * Going to original data-blocks is required to have bmain in a consistent state, where diff --git a/source/blender/blenkernel/intern/mesh_fair.cc b/source/blender/blenkernel/intern/mesh_fair.cc index 6ac1aa9b2b9..50db1bc1564 100644 --- a/source/blender/blenkernel/intern/mesh_fair.cc +++ b/source/blender/blenkernel/intern/mesh_fair.cc @@ -293,8 +293,8 @@ class BMeshFairingContext : public FairingContext { } bmloop_.reserve(bm->totloop); - vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(sizeof(MeshElemMap), bm->totvert, "bmesh loop map"); - vlmap_mem_ = (int *)MEM_malloc_arrayN(sizeof(int), bm->totloop, "bmesh loop map mempool"); + vlmap_ = (MeshElemMap *)MEM_calloc_arrayN(bm->totvert, sizeof(MeshElemMap), "bmesh loop map"); + vlmap_mem_ = (int *)MEM_malloc_arrayN(bm->totloop, sizeof(int), "bmesh loop map mempool"); BMVert *v; BMLoop *l; diff --git a/source/blender/blenkernel/intern/mesh_runtime.c b/source/blender/blenkernel/intern/mesh_runtime.c index 7ac4c29f0ee..1c8646a4bdd 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.c +++ b/source/blender/blenkernel/intern/mesh_runtime.c @@ -52,6 +52,8 @@ void BKE_mesh_runtime_reset(Mesh *mesh) memset(&mesh->runtime, 0, sizeof(mesh->runtime)); mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } /* Clear all pointers which we don't want to be shared on copying the datablock. @@ -71,6 +73,9 @@ void BKE_mesh_runtime_reset_on_copy(Mesh *mesh, const int UNUSED(flag)) mesh->runtime.eval_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime eval_mutex"); BLI_mutex_init(mesh->runtime.eval_mutex); + + mesh->runtime.render_mutex = MEM_mallocN(sizeof(ThreadMutex), "mesh runtime render_mutex"); + BLI_mutex_init(mesh->runtime.render_mutex); } void BKE_mesh_runtime_clear_cache(Mesh *mesh) @@ -80,6 +85,11 @@ void BKE_mesh_runtime_clear_cache(Mesh *mesh) MEM_freeN(mesh->runtime.eval_mutex); mesh->runtime.eval_mutex = NULL; } + if (mesh->runtime.render_mutex != NULL) { + BLI_mutex_end(mesh->runtime.render_mutex); + MEM_freeN(mesh->runtime.render_mutex); + mesh->runtime.render_mutex = NULL; + } if (mesh->runtime.mesh_eval != NULL) { mesh->runtime.mesh_eval->edit_mesh = NULL; BKE_id_free(NULL, mesh->runtime.mesh_eval); diff --git a/source/blender/blenkernel/intern/mesh_sample.cc b/source/blender/blenkernel/intern/mesh_sample.cc index 5388f6e530e..2274d34f0f1 100644 --- a/source/blender/blenkernel/intern/mesh_sample.cc +++ b/source/blender/blenkernel/intern/mesh_sample.cc @@ -29,12 +29,13 @@ BLI_NOINLINE static void sample_point_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const VArray &data_in, + const IndexMask mask, const MutableSpan data_out) { const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : bary_coords.index_range()) { + for (const int i : mask) { const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const float3 &bary_coord = bary_coords[i]; @@ -56,10 +57,9 @@ void sample_point_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const GVArray &data_in, + const IndexMask mask, const GMutableSpan data_out) { - BLI_assert(data_out.size() == looptri_indices.size()); - BLI_assert(data_out.size() == bary_coords.size()); BLI_assert(data_in.size() == mesh.totvert); BLI_assert(data_in.type() == data_out.type()); @@ -67,7 +67,7 @@ void sample_point_attribute(const Mesh &mesh, attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_point_attribute( - mesh, looptri_indices, bary_coords, data_in.typed(), data_out.typed()); + mesh, looptri_indices, bary_coords, data_in.typed(), mask, data_out.typed()); }); } @@ -76,12 +76,13 @@ BLI_NOINLINE static void sample_corner_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const VArray &data_in, + const IndexMask mask, const MutableSpan data_out) { const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : bary_coords.index_range()) { + for (const int i : mask) { const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const float3 &bary_coord = bary_coords[i]; @@ -103,10 +104,9 @@ void sample_corner_attribute(const Mesh &mesh, const Span looptri_indices, const Span bary_coords, const GVArray &data_in, + const IndexMask mask, const GMutableSpan data_out) { - BLI_assert(data_out.size() == looptri_indices.size()); - BLI_assert(data_out.size() == bary_coords.size()); BLI_assert(data_in.size() == mesh.totloop); BLI_assert(data_in.type() == data_out.type()); @@ -114,7 +114,7 @@ void sample_corner_attribute(const Mesh &mesh, attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); sample_corner_attribute( - mesh, looptri_indices, bary_coords, data_in.typed(), data_out.typed()); + mesh, looptri_indices, bary_coords, data_in.typed(), mask, data_out.typed()); }); } @@ -122,12 +122,13 @@ template void sample_face_attribute(const Mesh &mesh, const Span looptri_indices, const VArray &data_in, + const IndexMask mask, const MutableSpan data_out) { const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), BKE_mesh_runtime_looptri_len(&mesh)}; - for (const int i : data_out.index_range()) { + for (const int i : mask) { const int looptri_index = looptri_indices[i]; const MLoopTri &looptri = looptris[looptri_index]; const int poly_index = looptri.poly; @@ -138,23 +139,24 @@ void sample_face_attribute(const Mesh &mesh, void sample_face_attribute(const Mesh &mesh, const Span looptri_indices, const GVArray &data_in, + const IndexMask mask, const GMutableSpan data_out) { - BLI_assert(data_out.size() == looptri_indices.size()); BLI_assert(data_in.size() == mesh.totpoly); BLI_assert(data_in.type() == data_out.type()); const CPPType &type = data_in.type(); attribute_math::convert_to_static_type(type, [&](auto dummy) { using T = decltype(dummy); - sample_face_attribute(mesh, looptri_indices, data_in.typed(), data_out.typed()); + sample_face_attribute(mesh, looptri_indices, data_in.typed(), mask, data_out.typed()); }); } MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh, + const IndexMask mask, const Span positions, const Span looptri_indices) - : mesh_(mesh), positions_(positions), looptri_indices_(looptri_indices) + : mesh_(mesh), mask_(mask), positions_(positions), looptri_indices_(looptri_indices) { BLI_assert(positions.size() == looptri_indices.size()); } @@ -162,15 +164,15 @@ MeshAttributeInterpolator::MeshAttributeInterpolator(const Mesh *mesh, Span MeshAttributeInterpolator::ensure_barycentric_coords() { if (!bary_coords_.is_empty()) { - BLI_assert(bary_coords_.size() == positions_.size()); + BLI_assert(bary_coords_.size() >= mask_.min_array_size()); return bary_coords_; } - bary_coords_.reinitialize(positions_.size()); + bary_coords_.reinitialize(mask_.min_array_size()); const Span looptris{BKE_mesh_runtime_looptri_ensure(mesh_), BKE_mesh_runtime_looptri_len(mesh_)}; - for (const int i : bary_coords_.index_range()) { + for (const int i : mask_) { const int looptri_index = looptri_indices_[i]; const MLoopTri &looptri = looptris[looptri_index]; @@ -190,15 +192,15 @@ Span MeshAttributeInterpolator::ensure_barycentric_coords() Span MeshAttributeInterpolator::ensure_nearest_weights() { if (!nearest_weights_.is_empty()) { - BLI_assert(nearest_weights_.size() == positions_.size()); + BLI_assert(nearest_weights_.size() >= mask_.min_array_size()); return nearest_weights_; } - nearest_weights_.reinitialize(positions_.size()); + nearest_weights_.reinitialize(mask_.min_array_size()); const Span looptris{BKE_mesh_runtime_looptri_ensure(mesh_), BKE_mesh_runtime_looptri_len(mesh_)}; - for (const int i : nearest_weights_.index_range()) { + for (const int i : mask_) { const int looptri_index = looptri_indices_[i]; const MLoopTri &looptri = looptris[looptri_index]; @@ -215,22 +217,18 @@ Span MeshAttributeInterpolator::ensure_nearest_weights() return nearest_weights_; } -void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, - OutputAttribute &dst_attribute, - eAttributeMapMode mode) +void MeshAttributeInterpolator::sample_data(const GVArray &src, + const AttributeDomain domain, + const eAttributeMapMode mode, + const GMutableSpan dst) { - if (!src_attribute || !dst_attribute) { - return; - } - const GVArray &src_varray = *src_attribute.varray; - GMutableSpan dst_span = dst_attribute.as_span(); - if (src_varray.is_empty() || dst_span.is_empty()) { + if (src.is_empty() || dst.is_empty()) { return; } /* Compute barycentric coordinates only when they are needed. */ Span weights; - if (ELEM(src_attribute.domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CORNER)) { switch (mode) { case eAttributeMapMode::INTERPOLATED: weights = ensure_barycentric_coords(); @@ -242,17 +240,17 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_ } /* Interpolate the source attributes on the surface. */ - switch (src_attribute.domain) { + switch (domain) { case ATTR_DOMAIN_POINT: { - sample_point_attribute(*mesh_, looptri_indices_, weights, src_varray, dst_span); + sample_point_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; } case ATTR_DOMAIN_FACE: { - sample_face_attribute(*mesh_, looptri_indices_, src_varray, dst_span); + sample_face_attribute(*mesh_, looptri_indices_, src, mask_, dst); break; } case ATTR_DOMAIN_CORNER: { - sample_corner_attribute(*mesh_, looptri_indices_, weights, src_varray, dst_span); + sample_corner_attribute(*mesh_, looptri_indices_, weights, src, mask_, dst); break; } case ATTR_DOMAIN_EDGE: { @@ -266,4 +264,13 @@ void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_ } } +void MeshAttributeInterpolator::sample_attribute(const ReadAttributeLookup &src_attribute, + OutputAttribute &dst_attribute, + eAttributeMapMode mode) +{ + if (src_attribute && dst_attribute) { + this->sample_data(*src_attribute.varray, src_attribute.domain, mode, dst_attribute.as_span()); + } +} + } // namespace blender::bke::mesh_surface_sample diff --git a/source/blender/blenkernel/intern/modifier.c b/source/blender/blenkernel/intern/modifier.c index b55b02c7bf2..6f6cf12f023 100644 --- a/source/blender/blenkernel/intern/modifier.c +++ b/source/blender/blenkernel/intern/modifier.c @@ -100,7 +100,7 @@ void BKE_modifier_init(void) /* Initialize modifier types */ modifier_type_init(modifier_types); /* MOD_utils.c */ - /* Initialize global cmmon storage used for virtual modifier list */ + /* Initialize global common storage used for virtual modifier list. */ md = BKE_modifier_new(eModifierType_Armature); virtualModifierCommonData.amd = *((ArmatureModifierData *)md); BKE_modifier_free(md); diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index e507252307b..f4db81fffc5 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -132,19 +132,19 @@ static void movie_clip_foreach_id(ID *id, LibraryForeachIDData *data) MovieClip *movie_clip = (MovieClip *)id; MovieTracking *tracking = &movie_clip->tracking; - BKE_LIB_FOREACHID_PROCESS(data, movie_clip->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, movie_clip->gpd, IDWALK_CB_USER); LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking->tracks) { - BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER); } LISTBASE_FOREACH (MovieTrackingObject *, object, &tracking->objects) { LISTBASE_FOREACH (MovieTrackingTrack *, track, &object->tracks) { - BKE_LIB_FOREACHID_PROCESS(data, track->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, track->gpd, IDWALK_CB_USER); } } LISTBASE_FOREACH (MovieTrackingPlaneTrack *, plane_track, &tracking->plane_tracks) { - BKE_LIB_FOREACHID_PROCESS(data, plane_track->image, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, plane_track->image, IDWALK_CB_USER); } } @@ -346,7 +346,7 @@ IDTypeInfo IDType_ID_MC = { .name = "MovieClip", .name_plural = "movieclips", .translation_context = BLT_I18NCONTEXT_ID_MOVIECLIP, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = movie_clip_init_data, .copy_data = movie_clip_copy_data, @@ -842,7 +842,7 @@ static ImBuf *get_imbuf_cache(MovieClip *clip, const MovieClipUser *user, int fl key.render_flag = 0; } - return IMB_moviecache_get(clip->cache->moviecache, &key); + return IMB_moviecache_get(clip->cache->moviecache, &key, NULL); } return NULL; diff --git a/source/blender/blenkernel/intern/multires.c b/source/blender/blenkernel/intern/multires.c index eaa11a6683a..dc418545e23 100644 --- a/source/blender/blenkernel/intern/multires.c +++ b/source/blender/blenkernel/intern/multires.c @@ -1496,7 +1496,7 @@ void multires_topology_changed(Mesh *me) if (!mdisp->totdisp || !mdisp->disps) { if (grid) { mdisp->totdisp = grid; - mdisp->disps = MEM_calloc_arrayN(sizeof(float[3]), mdisp->totdisp, "mdisp topology"); + mdisp->disps = MEM_calloc_arrayN(mdisp->totdisp, sizeof(float[3]), "mdisp topology"); } continue; diff --git a/source/blender/blenkernel/intern/multires_reshape_smooth.c b/source/blender/blenkernel/intern/multires_reshape_smooth.c index 9fb158d2f84..3665d01926b 100644 --- a/source/blender/blenkernel/intern/multires_reshape_smooth.c +++ b/source/blender/blenkernel/intern/multires_reshape_smooth.c @@ -271,7 +271,7 @@ static void base_surface_grids_allocate(MultiresReshapeSmoothContext *reshape_sm for (int grid_index = 0; grid_index < num_grids; ++grid_index) { surface_grid[grid_index].points = MEM_calloc_arrayN( - sizeof(SurfacePoint), grid_area, "delta grid displacement"); + grid_area, sizeof(SurfacePoint), "delta grid displacement"); } reshape_smooth_context->base_surface_grids = surface_grid; @@ -576,19 +576,19 @@ static bool foreach_topology_info(const SubdivForeachContext *foreach_context, /* NOTE: Calloc so the counters are re-set to 0 "for free". */ reshape_smooth_context->geometry.num_vertices = num_vertices; reshape_smooth_context->geometry.vertices = MEM_calloc_arrayN( - sizeof(Vertex), num_vertices, "smooth vertices"); + num_vertices, sizeof(Vertex), "smooth vertices"); reshape_smooth_context->geometry.max_edges = max_edges; reshape_smooth_context->geometry.edges = MEM_malloc_arrayN( - sizeof(Edge), max_edges, "smooth edges"); + max_edges, sizeof(Edge), "smooth edges"); reshape_smooth_context->geometry.num_corners = num_loops; reshape_smooth_context->geometry.corners = MEM_malloc_arrayN( - sizeof(Corner), num_loops, "smooth corners"); + num_loops, sizeof(Corner), "smooth corners"); reshape_smooth_context->geometry.num_faces = num_polygons; reshape_smooth_context->geometry.faces = MEM_malloc_arrayN( - sizeof(Face), num_polygons, "smooth faces"); + num_polygons, sizeof(Face), "smooth faces"); return true; } diff --git a/source/blender/blenkernel/intern/multires_reshape_util.c b/source/blender/blenkernel/intern/multires_reshape_util.c index 8fb406e54a5..79a2b9eb002 100644 --- a/source/blender/blenkernel/intern/multires_reshape_util.c +++ b/source/blender/blenkernel/intern/multires_reshape_util.c @@ -86,7 +86,7 @@ static void context_init_lookup(MultiresReshapeContext *reshape_context) const int num_faces = base_mesh->totpoly; reshape_context->face_start_grid_index = MEM_malloc_arrayN( - sizeof(int), num_faces, "face_start_grid_index"); + num_faces, sizeof(int), "face_start_grid_index"); int num_grids = 0; int num_ptex_faces = 0; for (int face_index = 0; face_index < num_faces; ++face_index) { @@ -97,9 +97,9 @@ static void context_init_lookup(MultiresReshapeContext *reshape_context) } reshape_context->grid_to_face_index = MEM_malloc_arrayN( - sizeof(int), num_grids, "grid_to_face_index"); + num_grids, sizeof(int), "grid_to_face_index"); reshape_context->ptex_start_grid_index = MEM_malloc_arrayN( - sizeof(int), num_ptex_faces, "ptex_start_grid_index"); + num_ptex_faces, sizeof(int), "ptex_start_grid_index"); for (int face_index = 0, grid_index = 0, ptex_index = 0; face_index < num_faces; ++face_index) { const int num_corners = mpoly[face_index].totloop; const int num_face_ptex_faces = (num_corners == 4) ? 1 : num_corners; diff --git a/source/blender/blenkernel/intern/multires_unsubdivide.c b/source/blender/blenkernel/intern/multires_unsubdivide.c index 501e3f27389..643e1a50fd5 100644 --- a/source/blender/blenkernel/intern/multires_unsubdivide.c +++ b/source/blender/blenkernel/intern/multires_unsubdivide.c @@ -177,7 +177,7 @@ static bool is_vertex_diagonal(BMVert *from_v, BMVert *to_v) */ static void unsubdivide_face_center_vertex_tag(BMesh *bm, BMVert *initial_vertex) { - bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices"); + bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); GSQueue *queue; queue = BLI_gsqueue_new(sizeof(BMVert *)); @@ -368,7 +368,7 @@ static bool unsubdivide_tag_disconnected_mesh_element(BMesh *bm, int *elem_id, i */ static int unsubdivide_init_elem_ids(BMesh *bm, int *elem_id) { - bool *visited_vertices = MEM_calloc_arrayN(sizeof(bool), bm->totvert, "visited vertices"); + bool *visited_vertices = MEM_calloc_arrayN(bm->totvert, sizeof(bool), "visited vertices"); int current_id = 0; for (int i = 0; i < bm->totvert; i++) { if (!visited_vertices[i]) { @@ -475,7 +475,7 @@ static bool multires_unsubdivide_single_level(BMesh *bm) BM_mesh_elem_table_ensure(bm, BM_VERT); /* Build disconnected elements IDs. Each disconnected mesh element is evaluated separately. */ - int *elem_id = MEM_calloc_arrayN(sizeof(int), bm->totvert, " ELEM ID"); + int *elem_id = MEM_calloc_arrayN(bm->totvert, sizeof(int), " ELEM ID"); const int tot_ids = unsubdivide_init_elem_ids(bm, elem_id); bool valid_tag_found = true; @@ -961,7 +961,7 @@ static void multires_unsubdivide_prepare_original_bmesh_for_extract( } /* Create a map from loop index to poly index for the original mesh. */ - context->loop_to_face_map = MEM_calloc_arrayN(sizeof(int), original_mesh->totloop, "loop map"); + context->loop_to_face_map = MEM_calloc_arrayN(original_mesh->totloop, sizeof(int), "loop map"); for (int i = 0; i < original_mesh->totpoly; i++) { MPoly *poly = &original_mesh->mpoly[i]; @@ -1005,13 +1005,13 @@ static void multires_unsubdivide_extract_grids(MultiresUnsubdivideContext *conte context->num_grids = base_mesh->totloop; context->base_mesh_grids = MEM_calloc_arrayN( - sizeof(MultiresUnsubdivideGrid), base_mesh->totloop, "grids"); + base_mesh->totloop, sizeof(MultiresUnsubdivideGrid), "grids"); /* Based on the existing indices in the data-layers, generate two vertex indices maps. */ /* From vertex index in original to vertex index in base and from vertex index in base to vertex * index in original. */ - int *orig_to_base_vmap = MEM_calloc_arrayN(sizeof(int), bm_original_mesh->totvert, "orig vmap"); - int *base_to_orig_vmap = MEM_calloc_arrayN(sizeof(int), base_mesh->totvert, "base vmap"); + int *orig_to_base_vmap = MEM_calloc_arrayN(bm_original_mesh->totvert, sizeof(int), "orig vmap"); + int *base_to_orig_vmap = MEM_calloc_arrayN(base_mesh->totvert, sizeof(int), "base vmap"); context->base_to_orig_vmap = CustomData_get_layer_named(&base_mesh->vdata, CD_PROP_INT32, vname); for (int i = 0; i < base_mesh->totvert; i++) { diff --git a/source/blender/blenkernel/intern/nla.c b/source/blender/blenkernel/intern/nla.c index 4ce2ae3c11f..124db07298d 100644 --- a/source/blender/blenkernel/intern/nla.c +++ b/source/blender/blenkernel/intern/nla.c @@ -488,14 +488,14 @@ NlaStrip *BKE_nla_add_soundstrip(Main *bmain, Scene *scene, Speaker *speaker) */ void BKE_nla_strip_foreach_id(NlaStrip *strip, LibraryForeachIDData *data) { - BKE_LIB_FOREACHID_PROCESS(data, strip->act, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, strip->act, IDWALK_CB_USER); LISTBASE_FOREACH (FCurve *, fcu, &strip->fcurves) { - BKE_fcurve_foreach_id(fcu, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_fcurve_foreach_id(fcu, data)); } LISTBASE_FOREACH (NlaStrip *, substrip, &strip->strips) { - BKE_nla_strip_foreach_id(substrip, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_nla_strip_foreach_id(substrip, data)); } } @@ -1484,7 +1484,7 @@ void BKE_nlastrip_recalculate_bounds(NlaStrip *strip) } /* Is the given NLA-strip the first one to occur for the given AnimData block */ -// TODO: make this an api method if necessary, but need to add prefix first +/* TODO: make this an api method if necessary, but need to add prefix first */ static bool nlastrip_is_first(AnimData *adt, NlaStrip *strip) { NlaTrack *nlt; diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc index b96e98a58ec..688005454c8 100644 --- a/source/blender/blenkernel/intern/node.cc +++ b/source/blender/blenkernel/intern/node.cc @@ -52,9 +52,12 @@ #include "BLI_map.hh" #include "BLI_math.h" #include "BLI_path_util.h" +#include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_string_utils.h" #include "BLI_utildefines.h" +#include "BLI_vector_set.hh" #include "BLT_translation.h" @@ -80,6 +83,7 @@ #include "NOD_function.h" #include "NOD_geometry.h" #include "NOD_node_declaration.hh" +#include "NOD_node_tree_ref.hh" #include "NOD_shader.h" #include "NOD_socket.h" #include "NOD_texture.h" @@ -93,6 +97,21 @@ #define NODE_DEFAULT_MAX_WIDTH 700 +using blender::Array; +using blender::MutableSpan; +using blender::Set; +using blender::Span; +using blender::Stack; +using blender::Vector; +using blender::VectorSet; +using blender::nodes::FieldInferencingInterface; +using blender::nodes::InputSocketFieldType; +using blender::nodes::NodeDeclaration; +using blender::nodes::OutputFieldDependency; +using blender::nodes::OutputSocketFieldType; +using blender::nodes::SocketDeclaration; +using namespace blender::nodes::node_tree_ref_types; + /* Fallback types for undefined tree, nodes, sockets */ static bNodeTreeType NodeTreeTypeUndefined; bNodeType NodeTypeUndefined; @@ -110,6 +129,10 @@ static void node_socket_interface_free(bNodeTree *UNUSED(ntree), static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree, struct bNode *node, const bool mute); +static FieldInferencingInterface *node_field_inferencing_interface_copy( + const FieldInferencingInterface &field_inferencing_interface); +static void node_field_inferencing_interface_free( + const FieldInferencingInterface *field_inferencing_interface); static void ntree_init_data(ID *id) { @@ -220,6 +243,11 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c /* node tree will generate its own interface type */ ntree_dst->interface_type = nullptr; + + if (ntree_src->field_inferencing_interface) { + ntree_dst->field_inferencing_interface = node_field_inferencing_interface_copy( + *ntree_src->field_inferencing_interface); + } } static void ntree_free_data(ID *id) @@ -265,6 +293,8 @@ static void ntree_free_data(ID *id) MEM_freeN(sock); } + node_field_inferencing_interface_free(ntree->field_inferencing_interface); + /* free preview hash */ if (ntree->previews) { BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)BKE_node_preview_free); @@ -277,34 +307,36 @@ static void ntree_free_data(ID *id) static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock) { - IDP_foreach_property( - sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property( + sock->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data)); switch ((eNodeSocketDatatype)sock->type) { case SOCK_OBJECT: { bNodeSocketValueObject *default_value = (bNodeSocketValueObject *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_IMAGE: { bNodeSocketValueImage *default_value = (bNodeSocketValueImage *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_COLLECTION: { bNodeSocketValueCollection *default_value = (bNodeSocketValueCollection *) sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_TEXTURE: { bNodeSocketValueTexture *default_value = (bNodeSocketValueTexture *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_MATERIAL: { bNodeSocketValueMaterial *default_value = (bNodeSocketValueMaterial *)sock->default_value; - BKE_LIB_FOREACHID_PROCESS(data, default_value->value, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value->value, IDWALK_CB_USER); break; } case SOCK_FLOAT: @@ -325,26 +357,30 @@ static void node_foreach_id(ID *id, LibraryForeachIDData *data) { bNodeTree *ntree = (bNodeTree *)id; - BKE_LIB_FOREACHID_PROCESS(data, ntree->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ntree->gpd, IDWALK_CB_USER); LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { BKE_LIB_FOREACHID_PROCESS_ID(data, node->id, IDWALK_CB_USER); - IDP_foreach_property( - node->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(node->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } } LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->inputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } LISTBASE_FOREACH (bNodeSocket *, sock, &ntree->outputs) { - library_foreach_node_socket(data, sock); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, library_foreach_node_socket(data, sock)); } } @@ -508,7 +544,7 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree) if (node->storage) { /* could be handlerized at some point, now only 1 exception still */ if (ELEM(ntree->type, NTREE_SHADER, NTREE_GEOMETRY) && - ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB)) { + ELEM(node->type, SH_NODE_CURVE_VEC, SH_NODE_CURVE_RGB, SH_NODE_CURVE_FLOAT)) { BKE_curvemapping_blend_write(writer, (const CurveMapping *)node->storage); } else if ((ntree->type == NTREE_GEOMETRY) && @@ -647,6 +683,8 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) ntree->progress = nullptr; ntree->execdata = nullptr; + ntree->field_inferencing_interface = nullptr; + BLO_read_data_address(reader, &ntree->adt); BKE_animdata_blend_read_data(reader, ntree->adt); @@ -682,6 +720,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) switch (node->type) { case SH_NODE_CURVE_VEC: case SH_NODE_CURVE_RGB: + case SH_NODE_CURVE_FLOAT: case CMP_NODE_TIME: case CMP_NODE_CURVE_VEC: case CMP_NODE_CURVE_RGB: @@ -715,13 +754,11 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) } case SH_NODE_TEX_IMAGE: { NodeTexImage *tex = (NodeTexImage *)node->storage; - tex->iuser.ok = 1; tex->iuser.scene = nullptr; break; } case SH_NODE_TEX_ENVIRONMENT: { NodeTexEnvironment *tex = (NodeTexEnvironment *)node->storage; - tex->iuser.ok = 1; tex->iuser.scene = nullptr; break; } @@ -730,7 +767,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) case CMP_NODE_VIEWER: case CMP_NODE_SPLITVIEWER: { ImageUser *iuser = (ImageUser *)node->storage; - iuser->ok = 1; iuser->scene = nullptr; break; } @@ -744,7 +780,6 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) } case TEX_NODE_IMAGE: { ImageUser *iuser = (ImageUser *)node->storage; - iuser->ok = 1; iuser->scene = nullptr; break; } @@ -792,6 +827,11 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree) /* TODO: should be dealt by new generic cache handling of IDs... */ ntree->previews = nullptr; + if (ntree->type == NTREE_GEOMETRY) { + /* Update field referencing for the geometry nodes modifier. */ + ntree->update |= NTREE_UPDATE_FIELD_INFERENCING; + } + /* type verification is in lib-link */ } @@ -992,7 +1032,7 @@ IDTypeInfo IDType_ID_NT = { /* name */ "NodeTree", /* name_plural */ "node_groups", /* translation_context */ BLT_I18NCONTEXT_ID_NODETREE, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ ntree_init_data, /* copy_data */ ntree_copy_data, @@ -1092,7 +1132,7 @@ static void node_init(const struct bContext *C, bNodeTree *ntree, bNode *node) RNA_pointer_create((ID *)ntree, &RNA_Node, node, &ptr); /* XXX Warning: context can be nullptr in case nodes are added in do_versions. - * Delayed init is not supported for nodes with context-based initfunc_api atm. + * Delayed init is not supported for nodes with context-based `initfunc_api` at the moment. */ BLI_assert(C != nullptr); ntype->initfunc_api(C, &ptr); @@ -1341,6 +1381,9 @@ static void node_free_type(void *nodetype_v) free_dynamic_typeinfo(nodetype); } + delete nodetype->fixed_declaration; + nodetype->fixed_declaration = nullptr; + /* Can be null when the type is not dynamically allocated. */ if (nodetype->free_self) { nodetype->free_self(nodetype); @@ -1353,6 +1396,14 @@ void nodeRegisterType(bNodeType *nt) BLI_assert(nt->idname[0] != '\0'); BLI_assert(nt->poll != nullptr); + if (nt->declare && !nt->declaration_is_dynamic) { + if (nt->fixed_declaration == nullptr) { + nt->fixed_declaration = new blender::nodes::NodeDeclaration(); + blender::nodes::NodeDeclarationBuilder builder{*nt->fixed_declaration}; + nt->declare(builder); + } + } + BLI_ghash_insert(nodetypes_hash, nt->idname, nt); /* XXX pass Main to register function? */ /* Probably not. It is pretty much expected we want to update G_MAIN here I think - @@ -1368,7 +1419,7 @@ void nodeUnregisterType(bNodeType *nt) bool nodeTypeUndefined(bNode *node) { return (node->typeinfo == &NodeTypeUndefined) || - ((node->type == NODE_GROUP || node->type == NODE_CUSTOM_GROUP) && node->id && + ((ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) && node->id && ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING)); } @@ -2216,9 +2267,6 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, *node_dst = *node_src; - /* Reset the declaration of the new node. */ - node_dst->declaration = nullptr; - /* can be called for nodes outside a node tree (e.g. clipboard) */ if (ntree) { if (unique_name) { @@ -2289,6 +2337,10 @@ bNode *BKE_node_copy_ex(bNodeTree *ntree, ntree->update |= NTREE_UPDATE_NODES; } + /* Reset the declaration of the new node. */ + node_dst->declaration = nullptr; + nodeDeclarationEnsure(ntree, node_dst); + return node_dst; } @@ -2555,6 +2607,17 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node) bNodeLink *fromlink = link->fromsock->link->fromsock->link; /* skip the node */ if (fromlink) { + if (link->tosock->flag & SOCK_MULTI_INPUT) { + /* remove the link that would be the same as the relinked one */ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link_to_compare, &ntree->links) { + if (link_to_compare->fromsock == fromlink->fromsock && + link_to_compare->tosock == link->tosock) { + adjust_multi_input_indices_after_removed_link( + ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index); + nodeRemLink(ntree, link_to_compare); + } + } + } link->fromnode = fromlink->fromnode; link->fromsock = fromlink->fromsock; @@ -3106,7 +3169,9 @@ static void node_free_node(bNodeTree *ntree, bNode *node) MEM_freeN(node->prop); } - delete node->declaration; + if (node->typeinfo->declaration_is_dynamic) { + delete node->declaration; + } MEM_freeN(node); @@ -3944,16 +4009,22 @@ int nodeSocketLinkLimit(const bNodeSocket *sock) */ void nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node) { - if (node->typeinfo->declare == nullptr) { - return; - } if (node->declaration != nullptr) { return; } - - node->declaration = new blender::nodes::NodeDeclaration(); - blender::nodes::NodeDeclarationBuilder builder{*node->declaration}; - node->typeinfo->declare(builder); + if (node->typeinfo->declare == nullptr) { + return; + } + if (node->typeinfo->declaration_is_dynamic) { + node->declaration = new blender::nodes::NodeDeclaration(); + blender::nodes::NodeDeclarationBuilder builder{*node->declaration}; + node->typeinfo->declare(builder); + } + else { + /* Declaration should have been created in #nodeRegisterType. */ + BLI_assert(node->typeinfo->fixed_declaration != nullptr); + node->declaration = node->typeinfo->fixed_declaration; + } } /* ************** Node Clipboard *********** */ @@ -4425,7 +4496,516 @@ void ntreeUpdateAllNew(Main *main) FOREACH_NODETREE_END; } -void ntreeUpdateAllUsers(Main *main, ID *id) +static FieldInferencingInterface *node_field_inferencing_interface_copy( + const FieldInferencingInterface &field_inferencing_interface) +{ + return new FieldInferencingInterface(field_inferencing_interface); +} + +static void node_field_inferencing_interface_free( + const FieldInferencingInterface *field_inferencing_interface) +{ + delete field_inferencing_interface; +} + +namespace blender::bke::node_field_inferencing { + +static bool is_field_socket_type(eNodeSocketDatatype type) +{ + return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA); +} + +static bool is_field_socket_type(const SocketRef &socket) +{ + return is_field_socket_type((eNodeSocketDatatype)socket.typeinfo()->type); +} + +static bool update_field_inferencing(bNodeTree &btree); + +static InputSocketFieldType get_interface_input_field_type(const NodeRef &node, + const InputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + return InputSocketFieldType::None; + } + if (node.is_reroute_node()) { + return InputSocketFieldType::IsSupported; + } + if (node.is_group_output_node()) { + /* Outputs always support fields when the data type is correct. */ + return InputSocketFieldType::IsSupported; + } + if (node.is_undefined()) { + return InputSocketFieldType::None; + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + /* Get the field type from the declaration. */ + const SocketDeclaration &socket_decl = *node_decl->inputs()[socket.index()]; + const InputSocketFieldType field_type = socket_decl.input_field_type(); + if (field_type == InputSocketFieldType::Implicit) { + return field_type; + } + if (node_decl->is_function_node()) { + /* In a function node, every socket supports fields. */ + return InputSocketFieldType::IsSupported; + } + return field_type; +} + +static OutputFieldDependency get_interface_output_field_dependency(const NodeRef &node, + const OutputSocketRef &socket) +{ + if (!is_field_socket_type(socket)) { + /* Non-field sockets always output data. */ + return OutputFieldDependency::ForDataSource(); + } + if (node.is_reroute_node()) { + /* The reroute just forwards what is passed in. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_group_input_node()) { + /* Input nodes get special treatment in #determine_group_input_states. */ + return OutputFieldDependency::ForDependentField(); + } + if (node.is_undefined()) { + return OutputFieldDependency::ForDataSource(); + } + + const NodeDeclaration *node_decl = node.declaration(); + + /* Node declarations should be implemented for nodes involved here. */ + BLI_assert(node_decl != nullptr); + + if (node_decl->is_function_node()) { + /* In a generic function node, all outputs depend on all inputs. */ + return OutputFieldDependency::ForDependentField(); + } + + /* Use the socket declaration. */ + const SocketDeclaration &socket_decl = *node_decl->outputs()[socket.index()]; + return socket_decl.output_field_dependency(); +} + +/** + * Retrieves information about how the node interacts with fields. + * In the future, this information can be stored in the node declaration. This would allow this + * function to return a reference, making it more efficient. + */ +static FieldInferencingInterface get_node_field_inferencing_interface(const NodeRef &node) +{ + /* Node groups already reference all required information, so just return that. */ + if (node.is_group_node()) { + bNodeTree *group = (bNodeTree *)node.bnode()->id; + if (group == nullptr) { + return FieldInferencingInterface(); + } + if (group->field_inferencing_interface == nullptr) { + /* Update group recursively. */ + update_field_inferencing(*group); + } + return *group->field_inferencing_interface; + } + + FieldInferencingInterface inferencing_interface; + for (const InputSocketRef *input_socket : node.inputs()) { + inferencing_interface.inputs.append(get_interface_input_field_type(node, *input_socket)); + } + + for (const OutputSocketRef *output_socket : node.outputs()) { + inferencing_interface.outputs.append( + get_interface_output_field_dependency(node, *output_socket)); + } + return inferencing_interface; +} + +/** + * This struct contains information for every socket. The values are propagated through the + * network. + */ +struct SocketFieldState { + /* This socket starts a new field. */ + bool is_field_source = false; + /* This socket can never become a field, because the node itself does not support it. */ + bool is_always_single = false; + /* This socket is currently a single value. It could become a field though. */ + bool is_single = true; + /* This socket is required to be a single value. This can be because the node itself only + * supports this socket to be a single value, or because a node afterwards requires this to be a + * single value. */ + bool requires_single = false; +}; + +static Vector gather_input_socket_dependencies( + const OutputFieldDependency &field_dependency, const NodeRef &node) +{ + const OutputSocketFieldType type = field_dependency.field_type(); + Vector input_sockets; + switch (type) { + case OutputSocketFieldType::FieldSource: + case OutputSocketFieldType::None: { + break; + } + case OutputSocketFieldType::DependentField: { + /* This output depends on all inputs. */ + input_sockets.extend(node.inputs()); + break; + } + case OutputSocketFieldType::PartiallyDependent: { + /* This output depends only on a few inputs. */ + for (const int i : field_dependency.linked_input_indices()) { + input_sockets.append(&node.input(i)); + } + break; + } + } + return input_sockets; +} + +/** + * Check what the group output socket depends on. Potentially traverses the node tree + * to figure out if it is always a field or if it depends on any group inputs. + */ +static OutputFieldDependency find_group_output_dependencies( + const InputSocketRef &group_output_socket, + const Span field_state_by_socket_id) +{ + if (!is_field_socket_type(group_output_socket)) { + return OutputFieldDependency::ForDataSource(); + } + + /* Use a Set here instead of an array indexed by socket id, because we my only need to look at + * very few sockets. */ + Set handled_sockets; + Stack sockets_to_check; + + handled_sockets.add(&group_output_socket); + sockets_to_check.push(&group_output_socket); + + /* Keeps track of group input indices that are (indirectly) connected to the output. */ + Vector linked_input_indices; + + while (!sockets_to_check.is_empty()) { + const InputSocketRef *input_socket = sockets_to_check.pop(); + + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { + const NodeRef &origin_node = origin_socket->node(); + const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()]; + + if (origin_state.is_field_source) { + if (origin_node.is_group_input_node()) { + /* Found a group input that the group output depends on. */ + linked_input_indices.append_non_duplicates(origin_socket->index()); + } + else { + /* Found a field source that is not the group input. So the output is always a field. */ + return OutputFieldDependency::ForFieldSource(); + } + } + else if (!origin_state.is_single) { + const FieldInferencingInterface inferencing_interface = + get_node_field_inferencing_interface(origin_node); + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[origin_socket->index()]; + + /* Propagate search further to the left. */ + for (const InputSocketRef *origin_input_socket : + gather_input_socket_dependencies(field_dependency, origin_node)) { + if (!field_state_by_socket_id[origin_input_socket->id()].is_single) { + if (handled_sockets.add(origin_input_socket)) { + sockets_to_check.push(origin_input_socket); + } + } + } + } + } + } + return OutputFieldDependency::ForPartiallyDependentField(std::move(linked_input_indices)); +} + +static void propagate_data_requirements_from_right_to_left( + const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) +{ + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( + NodeTreeRef::ToposortDirection::RightToLeft); + + for (const NodeRef *node : toposort_result.sorted_nodes) { + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + if (field_dependency.field_type() == OutputSocketFieldType::FieldSource) { + continue; + } + if (field_dependency.field_type() == OutputSocketFieldType::None) { + state.requires_single = true; + state.is_always_single = true; + continue; + } + + /* The output is required to be a single value when it is connected to any input that does + * not support fields. */ + for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) { + state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single; + } + + if (state.requires_single) { + bool any_input_is_field_implicitly = false; + const Vector connected_inputs = gather_input_socket_dependencies( + field_dependency, *node); + for (const InputSocketRef *input_socket : connected_inputs) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + if (!input_socket->is_logically_linked()) { + any_input_is_field_implicitly = true; + break; + } + } + } + if (any_input_is_field_implicitly) { + /* This output isn't a single value actually. */ + state.requires_single = false; + } + else { + /* If the output is required to be a single value, the connected inputs in the same node + * must not be fields as well. */ + for (const InputSocketRef *input_socket : connected_inputs) { + field_state_by_socket_id[input_socket->id()].requires_single = true; + } + } + } + } + + /* Some inputs do not require fields independent of what the outputs are connected to. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (inferencing_interface.inputs[input_socket->index()] == InputSocketFieldType::None) { + state.requires_single = true; + state.is_always_single = true; + } + } + } +} + +static void determine_group_input_states( + const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const MutableSpan field_state_by_socket_id) +{ + { + /* Non-field inputs never support fields. */ + int index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, group_input, &tree.btree()->inputs, index) { + if (!is_field_socket_type((eNodeSocketDatatype)group_input->type)) { + new_inferencing_interface.inputs[index] = InputSocketFieldType::None; + } + } + } + /* Check if group inputs are required to be single values, because they are (indirectly) + * connected to some socket that does not support fields. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + if (state.requires_single) { + new_inferencing_interface.inputs[output_socket->index()] = InputSocketFieldType::None; + } + } + } + /* If an input does not support fields, this should be reflected in all Group Input nodes. */ + for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) { + for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const bool supports_field = new_inferencing_interface.inputs[output_socket->index()] != + InputSocketFieldType::None; + if (supports_field) { + state.is_single = false; + state.is_field_source = true; + } + else { + state.requires_single = true; + } + } + SocketFieldState &dummy_socket_state = field_state_by_socket_id[node->outputs().last()->id()]; + dummy_socket_state.requires_single = true; + } +} + +static void propagate_field_status_from_left_to_right( + const NodeTreeRef &tree, const MutableSpan field_state_by_socket_id) +{ + const NodeTreeRef::ToposortResult toposort_result = tree.toposort( + NodeTreeRef::ToposortDirection::LeftToRight); + + for (const NodeRef *node : toposort_result.sorted_nodes) { + if (node->is_group_input_node()) { + continue; + } + + const FieldInferencingInterface inferencing_interface = get_node_field_inferencing_interface( + *node); + + /* Update field state of input sockets, also taking into account linked origin sockets. */ + for (const InputSocketRef *input_socket : node->inputs()) { + SocketFieldState &state = field_state_by_socket_id[input_socket->id()]; + if (state.is_always_single) { + state.is_single = true; + continue; + } + state.is_single = true; + if (input_socket->directly_linked_sockets().is_empty()) { + if (inferencing_interface.inputs[input_socket->index()] == + InputSocketFieldType::Implicit) { + state.is_single = false; + } + } + else { + for (const OutputSocketRef *origin_socket : input_socket->directly_linked_sockets()) { + if (!field_state_by_socket_id[origin_socket->id()].is_single) { + state.is_single = false; + break; + } + } + } + } + + /* Update field state of output sockets, also taking into account input sockets. */ + for (const OutputSocketRef *output_socket : node->outputs()) { + SocketFieldState &state = field_state_by_socket_id[output_socket->id()]; + const OutputFieldDependency &field_dependency = + inferencing_interface.outputs[output_socket->index()]; + + switch (field_dependency.field_type()) { + case OutputSocketFieldType::None: { + state.is_single = true; + break; + } + case OutputSocketFieldType::FieldSource: { + state.is_single = false; + state.is_field_source = true; + break; + } + case OutputSocketFieldType::PartiallyDependent: + case OutputSocketFieldType::DependentField: { + for (const InputSocketRef *input_socket : + gather_input_socket_dependencies(field_dependency, *node)) { + if (!input_socket->is_available()) { + continue; + } + if (!field_state_by_socket_id[input_socket->id()].is_single) { + state.is_single = false; + break; + } + } + break; + } + } + } + } +} + +static void determine_group_output_states(const NodeTreeRef &tree, + FieldInferencingInterface &new_inferencing_interface, + const Span field_state_by_socket_id) +{ + for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) { + /* Ignore inactive group output nodes. */ + if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) { + continue; + } + /* Determine dependencies of all group outputs. */ + for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) { + OutputFieldDependency field_dependency = find_group_output_dependencies( + *group_output_socket, field_state_by_socket_id); + new_inferencing_interface.outputs[group_output_socket->index()] = std::move( + field_dependency); + } + break; + } +} + +static void update_socket_shapes(const NodeTreeRef &tree, + const Span field_state_by_socket_id) +{ + const eNodeSocketDisplayShape requires_data_shape = SOCK_DISPLAY_SHAPE_CIRCLE; + const eNodeSocketDisplayShape data_but_can_be_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT; + const eNodeSocketDisplayShape is_field_shape = SOCK_DISPLAY_SHAPE_DIAMOND; + + auto get_shape_for_state = [&](const SocketFieldState &state) { + if (state.is_always_single) { + return requires_data_shape; + } + if (!state.is_single) { + return is_field_shape; + } + if (state.requires_single) { + return requires_data_shape; + } + return data_but_can_be_field_shape; + }; + + for (const InputSocketRef *socket : tree.input_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + bsocket->display_shape = get_shape_for_state(state); + } + for (const OutputSocketRef *socket : tree.output_sockets()) { + bNodeSocket *bsocket = socket->bsocket(); + const SocketFieldState &state = field_state_by_socket_id[socket->id()]; + bsocket->display_shape = get_shape_for_state(state); + } +} + +static bool update_field_inferencing(bNodeTree &btree) +{ + using namespace blender::nodes; + if (btree.type != NTREE_GEOMETRY) { + return false; + } + + /* Create new inferencing interface for this node group. */ + FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface(); + new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs), + InputSocketFieldType::IsSupported); + new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs), + OutputFieldDependency::ForDataSource()); + + /* Create #NodeTreeRef to accelerate various queries on the node tree (e.g. linked sockets). */ + const NodeTreeRef tree{&btree}; + + /* Keep track of the state of all sockets. The index into this array is #SocketRef::id(). */ + Array field_state_by_socket_id(tree.sockets().size()); + + propagate_data_requirements_from_right_to_left(tree, field_state_by_socket_id); + determine_group_input_states(tree, *new_inferencing_interface, field_state_by_socket_id); + propagate_field_status_from_left_to_right(tree, field_state_by_socket_id); + determine_group_output_states(tree, *new_inferencing_interface, field_state_by_socket_id); + update_socket_shapes(tree, field_state_by_socket_id); + + /* Update the previous group interface. */ + const bool group_interface_changed = btree.field_inferencing_interface == nullptr || + *btree.field_inferencing_interface != + *new_inferencing_interface; + delete btree.field_inferencing_interface; + btree.field_inferencing_interface = new_inferencing_interface; + + return group_interface_changed; +} + +} // namespace blender::bke::node_field_inferencing + +/** + * \param tree_update_flag: #eNodeTreeUpdate enum. + */ +void ntreeUpdateAllUsers(Main *main, ID *id, const int tree_update_flag) { if (id == nullptr) { return; @@ -4446,7 +5026,8 @@ void ntreeUpdateAllUsers(Main *main, ID *id) } if (need_update) { - ntreeUpdateTree(nullptr, ntree); + ntree->update |= tree_update_flag; + ntreeUpdateTree(tree_update_flag ? main : nullptr, ntree); } } FOREACH_NODETREE_END; @@ -4508,8 +5089,18 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree) ntreeInterfaceTypeUpdate(ntree); } + int tree_user_update_flag = 0; + + if (ntree->update & NTREE_UPDATE) { + /* If the field interface of this node tree has changed, all node trees using + * this group will need to recalculate their interface as well. */ + if (blender::bke::node_field_inferencing::update_field_inferencing(*ntree)) { + tree_user_update_flag |= NTREE_UPDATE_FIELD_INFERENCING; + } + } + if (bmain) { - ntreeUpdateAllUsers(bmain, &ntree->id); + ntreeUpdateAllUsers(bmain, &ntree->id, tree_user_update_flag); } if (ntree->update & (NTREE_UPDATE_LINKS | NTREE_UPDATE_NODES)) { @@ -5023,6 +5614,7 @@ static void registerShaderNodes() register_node_type_sh_shadertorgb(); register_node_type_sh_normal(); register_node_type_sh_mapping(); + register_node_type_sh_curve_float(); register_node_type_sh_curve_vec(); register_node_type_sh_curve_rgb(); register_node_type_sh_map_range(); @@ -5156,10 +5748,28 @@ static void registerGeometryNodes() { register_node_type_geo_group(); + register_node_type_geo_legacy_attribute_proximity(); + register_node_type_geo_legacy_attribute_randomize(); + register_node_type_geo_legacy_attribute_transfer(); + register_node_type_geo_legacy_curve_endpoints(); + register_node_type_geo_legacy_curve_reverse(); + register_node_type_geo_legacy_curve_set_handles(); + register_node_type_geo_legacy_curve_spline_type(); + register_node_type_geo_legacy_curve_subdivide(); + register_node_type_geo_legacy_curve_to_points(); + register_node_type_geo_legacy_delete_geometry(); + register_node_type_geo_legacy_edge_split(); register_node_type_geo_legacy_material_assign(); + register_node_type_geo_legacy_mesh_to_curve(); + register_node_type_geo_legacy_points_to_volume(); + register_node_type_geo_legacy_raycast(); + register_node_type_geo_legacy_select_by_handle_type(); register_node_type_geo_legacy_select_by_material(); + register_node_type_geo_legacy_subdivision_surface(); + register_node_type_geo_legacy_volume_to_mesh(); register_node_type_geo_align_rotation_to_vector(); + register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_clamp(); register_node_type_geo_attribute_color_ramp(); register_node_type_geo_attribute_combine_xyz(); @@ -5167,24 +5777,24 @@ static void registerGeometryNodes() register_node_type_geo_attribute_convert(); register_node_type_geo_attribute_curve_map(); register_node_type_geo_attribute_fill(); - register_node_type_geo_attribute_capture(); register_node_type_geo_attribute_map_range(); register_node_type_geo_attribute_math(); register_node_type_geo_attribute_mix(); - register_node_type_geo_attribute_proximity(); - register_node_type_geo_attribute_randomize(); register_node_type_geo_attribute_remove(); register_node_type_geo_attribute_separate_xyz(); - register_node_type_geo_attribute_transfer(); + register_node_type_geo_attribute_statistic(); register_node_type_geo_attribute_vector_math(); register_node_type_geo_attribute_vector_rotate(); register_node_type_geo_boolean(); register_node_type_geo_bounding_box(); register_node_type_geo_collection_info(); register_node_type_geo_convex_hull(); - register_node_type_geo_curve_endpoints(); + register_node_type_geo_curve_endpoint_selection(); register_node_type_geo_curve_fill(); + register_node_type_geo_curve_fillet(); + register_node_type_geo_curve_handle_type_selection(); register_node_type_geo_curve_length(); + register_node_type_geo_curve_parameter(); register_node_type_geo_curve_primitive_bezier_segment(); register_node_type_geo_curve_primitive_circle(); register_node_type_geo_curve_primitive_line(); @@ -5194,6 +5804,7 @@ static void registerGeometryNodes() register_node_type_geo_curve_primitive_star(); register_node_type_geo_curve_resample(); register_node_type_geo_curve_reverse(); + register_node_type_geo_curve_sample(); register_node_type_geo_curve_set_handles(); register_node_type_geo_curve_spline_type(); register_node_type_geo_curve_subdivide(); @@ -5201,15 +5812,29 @@ static void registerGeometryNodes() register_node_type_geo_curve_to_points(); register_node_type_geo_curve_trim(); register_node_type_geo_delete_geometry(); + register_node_type_geo_distribute_points_on_faces(); register_node_type_geo_edge_split(); + register_node_type_geo_image_texture(); + register_node_type_geo_input_curve_handles(); + register_node_type_geo_input_curve_tilt(); + register_node_type_geo_input_id(); register_node_type_geo_input_index(); + register_node_type_geo_input_material_index(); register_node_type_geo_input_material(); register_node_type_geo_input_normal(); register_node_type_geo_input_position(); + register_node_type_geo_input_radius(); + register_node_type_geo_input_shade_smooth(); + register_node_type_geo_input_spline_cyclic(); + register_node_type_geo_input_spline_length(); + register_node_type_geo_input_spline_resolution(); + register_node_type_geo_input_tangent(); + register_node_type_geo_instance_on_points(); + register_node_type_geo_instances_to_points(); register_node_type_geo_is_viewport(); register_node_type_geo_join_geometry(); - register_node_type_geo_material_assign(); register_node_type_geo_material_replace(); + register_node_type_geo_material_selection(); register_node_type_geo_mesh_primitive_circle(); register_node_type_geo_mesh_primitive_cone(); register_node_type_geo_mesh_primitive_cube(); @@ -5220,6 +5845,7 @@ static void registerGeometryNodes() register_node_type_geo_mesh_primitive_uv_sphere(); register_node_type_geo_mesh_subdivide(); register_node_type_geo_mesh_to_curve(); + register_node_type_geo_mesh_to_points(); register_node_type_geo_object_info(); register_node_type_geo_point_distribute(); register_node_type_geo_point_instance(); @@ -5227,16 +5853,34 @@ static void registerGeometryNodes() register_node_type_geo_point_scale(); register_node_type_geo_point_separate(); register_node_type_geo_point_translate(); + register_node_type_geo_points_to_vertices(); register_node_type_geo_points_to_volume(); + register_node_type_geo_proximity(); register_node_type_geo_raycast(); + register_node_type_geo_realize_instances(); + register_node_type_geo_rotate_instances(); register_node_type_geo_sample_texture(); - register_node_type_geo_select_by_handle_type(); - register_node_type_geo_material_selection(); + register_node_type_geo_scale_instances(); register_node_type_geo_separate_components(); + register_node_type_geo_separate_geometry(); + register_node_type_geo_set_curve_handles(); + register_node_type_geo_set_curve_radius(); + register_node_type_geo_set_curve_tilt(); + register_node_type_geo_set_id(); + register_node_type_geo_set_material_index(); + register_node_type_geo_set_material(); + register_node_type_geo_set_point_radius(); register_node_type_geo_set_position(); + register_node_type_geo_set_shade_smooth(); + register_node_type_geo_set_spline_cyclic(); + register_node_type_geo_set_spline_resolution(); + register_node_type_geo_string_join(); + register_node_type_geo_string_to_curves(); register_node_type_geo_subdivision_surface(); register_node_type_geo_switch(); + register_node_type_geo_transfer_attribute(); register_node_type_geo_transform(); + register_node_type_geo_translate_instances(); register_node_type_geo_triangulate(); register_node_type_geo_viewer(); register_node_type_geo_volume_to_mesh(); @@ -5244,12 +5888,24 @@ static void registerGeometryNodes() static void registerFunctionNodes() { + register_node_type_fn_legacy_random_float(); + + register_node_type_fn_align_euler_to_vector(); register_node_type_fn_boolean_math(); register_node_type_fn_float_compare(); register_node_type_fn_float_to_int(); + register_node_type_fn_input_bool(); + register_node_type_fn_input_color(); + register_node_type_fn_input_int(); + register_node_type_fn_input_special_characters(); register_node_type_fn_input_string(); register_node_type_fn_input_vector(); - register_node_type_fn_random_float(); + register_node_type_fn_random_value(); + register_node_type_fn_replace_string(); + register_node_type_fn_rotate_euler(); + register_node_type_fn_slice_string(); + register_node_type_fn_string_length(); + register_node_type_fn_value_to_string(); } void BKE_node_system_init(void) diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 465ec9dc665..b5d39921f26 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -82,6 +82,7 @@ #include "BKE_anim_visualization.h" #include "BKE_animsys.h" #include "BKE_armature.h" +#include "BKE_asset.h" #include "BKE_camera.h" #include "BKE_collection.h" #include "BKE_constraint.h" @@ -94,7 +95,6 @@ #include "BKE_effect.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" -#include "BKE_font.h" #include "BKE_geometry_set.h" #include "BKE_global.h" #include "BKE_gpencil.h" @@ -136,6 +136,7 @@ #include "BKE_speaker.h" #include "BKE_subdiv_ccg.h" #include "BKE_subsurf.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "DEG_depsgraph.h" @@ -357,8 +358,8 @@ static void object_make_local(Main *bmain, ID *id, const int flags) } if (force_local) { - BKE_lib_id_clear_library_data(bmain, &ob->id); - BKE_lib_id_expand_local(bmain, &ob->id); + BKE_lib_id_clear_library_data(bmain, &ob->id, flags); + BKE_lib_id_expand_local(bmain, &ob->id, flags); if (clear_proxy) { if (ob->proxy_from != NULL) { ob->proxy_from->proxy = NULL; @@ -388,7 +389,8 @@ static void library_foreach_modifiersForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, @@ -397,7 +399,8 @@ static void library_foreach_gpencil_modifiersForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_shaderfxForeachIDLink(void *user_data, @@ -406,7 +409,8 @@ static void library_foreach_shaderfxForeachIDLink(void *user_data, int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), @@ -416,7 +420,8 @@ static void library_foreach_constraintObjectLooper(bConstraint *UNUSED(con), { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; const int cb_flag = is_reference ? IDWALK_CB_USER : IDWALK_CB_NOP; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(psys), @@ -425,7 +430,8 @@ static void library_foreach_particlesystemsObjectLooper(ParticleSystem *UNUSED(p int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } static void object_foreach_id(ID *id, LibraryForeachIDData *data) @@ -452,11 +458,11 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) } } - BKE_LIB_FOREACHID_PROCESS(data, object->parent, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->track, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF); /* object->proxy is refcounted, but not object->proxy_group... *sigh* */ - BKE_LIB_FOREACHID_PROCESS(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->proxy_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy, IDWALK_CB_USER | IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->proxy_group, IDWALK_CB_NOP); /* Special case! * Since this field is set/owned by 'user' of this ID (and not ID itself), @@ -468,22 +474,23 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) IDWALK_CB_INDIRECT_USAGE : 0, true); - BKE_LIB_FOREACHID_PROCESS(data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->proxy_from, IDWALK_CB_LOOPBACK | IDWALK_CB_NEVER_SELF); BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } - BKE_LIB_FOREACHID_PROCESS(data, object->poselib, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->poselib, IDWALK_CB_USER); for (int i = 0; i < object->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->mat[i], proxy_cb_flag | IDWALK_CB_USER); } /* Note that ob->gpd is deprecated, so no need to handle it here. */ - BKE_LIB_FOREACHID_PROCESS(data, object->instance_collection, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->instance_collection, IDWALK_CB_USER); if (object->pd) { - BKE_LIB_FOREACHID_PROCESS(data, object->pd->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, object->pd->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->pd->f_source, IDWALK_CB_NOP); } /* Note that ob->effect is deprecated, so no need to handle it here. */ @@ -491,34 +498,52 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data) const int cb_flag_orig = BKE_lib_query_foreachid_process_callback_flag_override( data, proxy_cb_flag, false); LISTBASE_FOREACH (bPoseChannel *, pchan, &object->pose->chanbase) { - IDP_foreach_property( - pchan->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); - BKE_LIB_FOREACHID_PROCESS(data, pchan->custom, IDWALK_CB_USER); - BKE_constraints_id_loop(&pchan->constraints, library_foreach_constraintObjectLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(pchan->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); + + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pchan->custom, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_constraints_id_loop( + &pchan->constraints, library_foreach_constraintObjectLooper, data)); } BKE_lib_query_foreachid_process_callback_flag_override(data, cb_flag_orig, true); } if (object->rigidbody_constraint) { - BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->rigidbody_constraint->ob1, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->rigidbody_constraint->ob2, IDWALK_CB_NEVER_SELF); } - BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data); - BKE_gpencil_modifiers_foreach_ID_link( - object, library_foreach_gpencil_modifiersForeachIDLink, data); - BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data); - BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_modifiers_foreach_ID_link(object, library_foreach_modifiersForeachIDLink, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_gpencil_modifiers_foreach_ID_link( + object, library_foreach_gpencil_modifiersForeachIDLink, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_constraints_id_loop(&object->constraints, library_foreach_constraintObjectLooper, data)); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_shaderfx_foreach_ID_link(object, library_foreach_shaderfxForeachIDLink, data)); LISTBASE_FOREACH (ParticleSystem *, psys, &object->particlesystem) { - BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_particlesystem_id_loop(psys, library_foreach_particlesystemsObjectLooper, data)); } if (object->soft) { - BKE_LIB_FOREACHID_PROCESS(data, object->soft->collision_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->soft->collision_group, IDWALK_CB_NOP); if (object->soft->effector_weights) { - BKE_LIB_FOREACHID_PROCESS(data, object->soft->effector_weights->group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER( + data, object->soft->effector_weights->group, IDWALK_CB_NOP); } } } @@ -844,6 +869,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) { Object *ob = (Object *)id; + Main *bmain = BLO_read_lib_get_main(reader); BlendFileReadReport *reports = BLO_read_lib_reports(reader); /* XXX deprecated - old animation system <<< */ @@ -940,12 +966,7 @@ static void object_blend_read_lib(BlendLibReader *reader, ID *id) /* When the object is local and the data is library its possible * the material list size gets out of sync. T22663. */ if (ob->data && ob->id.lib != ((ID *)ob->data)->lib) { - const short *totcol_data = BKE_object_material_len_p(ob); - /* Only expand so as not to lose any object materials that might be set. */ - if (totcol_data && (*totcol_data > ob->totcol)) { - // printf("'%s' %d -> %d\n", ob->id.name, ob->totcol, *totcol_data); - BKE_object_material_resize(BLO_read_lib_get_main(reader), ob, *totcol_data, false); - } + BKE_object_materials_test(bmain, ob, ob->data); } BLO_read_id_address(reader, ob->id.lib, &ob->gpd); @@ -1131,20 +1152,101 @@ static void object_blend_read_expand(BlendExpander *expander, ID *id) } } -static void object_lib_override_apply_post(ID *id_dst, ID *UNUSED(id_src)) +static void object_lib_override_apply_post(ID *id_dst, ID *id_src) { - Object *object = (Object *)id_dst; + /* id_dst is the new local override copy of the linked reference data. id_src is the old override + * data stored on disk, used as source data for override operations. */ + Object *object_dst = (Object *)id_dst; + Object *object_src = (Object *)id_src; - ListBase pidlist; - BKE_ptcache_ids_from_object(&pidlist, object, NULL, 0); - LISTBASE_FOREACH (PTCacheID *, pid, &pidlist) { - LISTBASE_FOREACH (PointCache *, point_cache, pid->ptcaches) { - point_cache->flag |= PTCACHE_FLAG_INFO_DIRTY; + ListBase pidlist_dst, pidlist_src; + BKE_ptcache_ids_from_object(&pidlist_dst, object_dst, NULL, 0); + BKE_ptcache_ids_from_object(&pidlist_src, object_src, NULL, 0); + + /* Problem with point caches is that several status flags (like OUTDATED or BAKED) are read-only + * at RNA level, and therefore not overridable per-se. + * + * This code is a workaround this to check all point-caches from both source and destination + * objects in parallel, and transfer those flags when it makes sense. + * + * This allows to keep baked caches across liboverrides applies. + * + * NOTE: This is fairly hackish and weak, but so is the point-cache system as its whole. A more + * robust solution would be e.g. to have a specific RNA entry point to deal with such cases + * (maybe a new flag to allow override code to set values of some read-only properties?). + */ + PTCacheID *pid_src, *pid_dst; + for (pid_dst = pidlist_dst.first, pid_src = pidlist_src.first; pid_dst != NULL; + pid_dst = pid_dst->next, pid_src = (pid_src != NULL) ? pid_src->next : NULL) { + /* If pid's do not match, just tag info of caches in dst as dirty and continue. */ + if (pid_src == NULL) { + continue; + } + if (pid_dst->type != pid_src->type || pid_dst->file_type != pid_src->file_type || + pid_dst->default_step != pid_src->default_step || pid_dst->max_step != pid_src->max_step || + pid_dst->data_types != pid_src->data_types || pid_dst->info_types != pid_src->info_types) { + LISTBASE_FOREACH (PointCache *, point_cache_src, pid_src->ptcaches) { + point_cache_src->flag |= PTCACHE_FLAG_INFO_DIRTY; + } + continue; + } + + PointCache *point_cache_dst, *point_cache_src; + for (point_cache_dst = pid_dst->ptcaches->first, point_cache_src = pid_src->ptcaches->first; + point_cache_dst != NULL; + point_cache_dst = point_cache_dst->next, + point_cache_src = (point_cache_src != NULL) ? point_cache_src->next : NULL) { + /* Always force updating info about caches of applied liboverrides. */ + point_cache_dst->flag |= PTCACHE_FLAG_INFO_DIRTY; + if (point_cache_src == NULL || !STREQ(point_cache_dst->name, point_cache_src->name)) { + continue; + } + if ((point_cache_src->flag & PTCACHE_BAKED) != 0) { + point_cache_dst->flag |= PTCACHE_BAKED; + } + if ((point_cache_src->flag & PTCACHE_OUTDATED) == 0) { + point_cache_dst->flag &= ~PTCACHE_OUTDATED; + } } } - BLI_freelistN(&pidlist); + BLI_freelistN(&pidlist_dst); + BLI_freelistN(&pidlist_src); } +static IDProperty *object_asset_dimensions_property(Object *ob) +{ + float dimensions[3]; + BKE_object_dimensions_get(ob, dimensions); + if (is_zero_v3(dimensions)) { + return NULL; + } + + IDPropertyTemplate idprop = {0}; + idprop.array.len = ARRAY_SIZE(dimensions); + idprop.array.type = IDP_FLOAT; + + IDProperty *property = IDP_New(IDP_ARRAY, &idprop, "dimensions"); + memcpy(IDP_Array(property), dimensions, sizeof(dimensions)); + + return property; +} + +static void object_asset_pre_save(void *asset_ptr, struct AssetMetaData *asset_data) +{ + Object *ob = asset_ptr; + BLI_assert(GS(ob->id.name) == ID_OB); + + /* Update dimensions hint for the asset. */ + IDProperty *dimensions_prop = object_asset_dimensions_property(ob); + if (dimensions_prop) { + BKE_asset_metadata_idprop_ensure(asset_data, dimensions_prop); + } +} + +AssetTypeInfo AssetType_OB = { + .pre_save_fn = object_asset_pre_save, +}; + IDTypeInfo IDType_ID_OB = { .id_code = ID_OB, .id_filter = FILTER_ID_OB, @@ -1171,6 +1273,8 @@ IDTypeInfo IDType_ID_OB = { .blend_read_undo_preserve = NULL, .lib_override_apply_post = object_lib_override_apply_post, + + .asset_type_info = &AssetType_OB, }; void BKE_object_workob_clear(Object *workob) @@ -2382,8 +2486,8 @@ ParticleSystem *BKE_object_copy_particlesystem(ParticleSystem *psys, const int f psysn->pointcache = BKE_ptcache_copy_list(&psysn->ptcaches, &psys->ptcaches, flag); } - /* XXX(campbell): from reading existing code this seems correct but intended usage of - * pointcache should /w cloth should be added in 'ParticleSystem'. */ + /* XXX(@campbellbarton): from reading existing code this seems correct but intended usage of + * point-cache should /w cloth should be added in 'ParticleSystem'. */ if (psysn->clmd) { psysn->clmd->point_cache = psysn->pointcache; } @@ -2634,10 +2738,16 @@ Object *BKE_object_duplicate(Main *bmain, { const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0; const bool is_root_id = (duplicate_options & LIB_ID_DUPLICATE_IS_ROOT_ID) != 0; + int copy_flags = LIB_ID_COPY_DEFAULT; if (!is_subprocess) { BKE_main_id_newptr_and_tag_clear(bmain); } + else { + /* In case copying object is a sub-process of collection (or scene) copying, do not try to + * re-assign RB objects to existing RBW collections. */ + copy_flags |= LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING; + } if (is_root_id) { /* In case root duplicated ID is linked, assume we want to get a local copy of it and duplicate * all expected linked data. */ @@ -2649,24 +2759,22 @@ Object *BKE_object_duplicate(Main *bmain, Material ***matarar; - Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag); + Object *obn = (Object *)BKE_id_copy_for_duplicate(bmain, &ob->id, dupflag, copy_flags); /* 0 == full linked. */ if (dupflag == 0) { return obn; } - BKE_animdata_duplicate_id_action(bmain, &obn->id, dupflag); - if (dupflag & USER_DUP_MAT) { for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)obn->mat[i], dupflag, copy_flags); } } if (dupflag & USER_DUP_PSYS) { ParticleSystem *psys; for (psys = obn->particlesystem.first; psys; psys = psys->next) { - BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)psys->part, dupflag, copy_flags); } } @@ -2677,77 +2785,77 @@ Object *BKE_object_duplicate(Main *bmain, switch (obn->type) { case OB_MESH: if (dupflag & USER_DUP_MESH) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_CURVE: if (dupflag & USER_DUP_CURVE) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_SURF: if (dupflag & USER_DUP_SURF) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_FONT: if (dupflag & USER_DUP_FONT) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_MBALL: if (dupflag & USER_DUP_MBALL) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_LAMP: if (dupflag & USER_DUP_LAMP) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_ARMATURE: if (dupflag & USER_DUP_ARM) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_LATTICE: - if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + if (dupflag & USER_DUP_LATTICE) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_CAMERA: - if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + if (dupflag & USER_DUP_CAMERA) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_LIGHTPROBE: if (dupflag & USER_DUP_LIGHTPROBE) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_SPEAKER: - if (dupflag != 0) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + if (dupflag & USER_DUP_SPEAKER) { + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_GPENCIL: if (dupflag & USER_DUP_GPENCIL) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_HAIR: if (dupflag & USER_DUP_HAIR) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_POINTCLOUD: if (dupflag & USER_DUP_POINTCLOUD) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; case OB_VOLUME: if (dupflag & USER_DUP_VOLUME) { - id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag); + id_new = BKE_id_copy_for_duplicate(bmain, id_old, dupflag, copy_flags); } break; } @@ -2758,7 +2866,7 @@ Object *BKE_object_duplicate(Main *bmain, matarar = BKE_object_material_array_p(obn); if (matarar) { for (int i = 0; i < obn->totcol; i++) { - BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag); + BKE_id_copy_for_duplicate(bmain, (ID *)(*matarar)[i], dupflag, copy_flags); } } } @@ -2766,7 +2874,7 @@ Object *BKE_object_duplicate(Main *bmain, if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&obn->id); + BKE_libblock_relink_to_newid(bmain, &obn->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */ @@ -4058,7 +4166,6 @@ void BKE_object_empty_draw_type_set(Object *ob, const int value) if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { if (!ob->iuser) { ob->iuser = MEM_callocN(sizeof(ImageUser), "image user"); - ob->iuser->ok = 1; ob->iuser->flag |= IMA_ANIM_ALWAYS; ob->iuser->frames = 100; ob->iuser->sfra = 1; @@ -5624,7 +5731,7 @@ bool BKE_object_modifier_update_subframe(Depsgraph *depsgraph, /* skip subframe if object is parented * to vertex of a dynamic paint canvas */ - if (no_update && (ob->partype == PARVERT1 || ob->partype == PARVERT3)) { + if (no_update && (ELEM(ob->partype, PARVERT1, PARVERT3))) { return false; } @@ -5768,6 +5875,14 @@ void BKE_object_replace_data_on_shallow_copy(Object *ob, ID *new_data) bool BKE_object_supports_material_slots(struct Object *ob) { - return ELEM( - ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME); + return ELEM(ob->type, + OB_MESH, + OB_CURVE, + OB_SURF, + OB_FONT, + OB_MBALL, + OB_HAIR, + OB_POINTCLOUD, + OB_VOLUME, + OB_GPENCIL); } diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 04739ec19d3..666a31a9e3f 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -50,7 +50,6 @@ #include "BKE_duplilist.h" #include "BKE_editmesh.h" #include "BKE_editmesh_cache.h" -#include "BKE_font.h" #include "BKE_geometry_set.h" #include "BKE_geometry_set.hh" #include "BKE_global.h" @@ -63,6 +62,7 @@ #include "BKE_object.h" #include "BKE_particle.h" #include "BKE_scene.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -88,7 +88,6 @@ struct DupliContext { Object *obedit; Scene *scene; - ViewLayer *view_layer; Object *object; float space_mat[4][4]; @@ -127,7 +126,6 @@ static void init_context(DupliContext *r_ctx, { r_ctx->depsgraph = depsgraph; r_ctx->scene = scene; - r_ctx->view_layer = DEG_get_evaluated_view_layer(depsgraph); r_ctx->collection = nullptr; r_ctx->object = ob; @@ -311,13 +309,18 @@ static void make_child_duplis(const DupliContext *ctx, FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_END; } else { - int baseid; - ViewLayer *view_layer = ctx->view_layer; - LISTBASE_FOREACH_INDEX (Base *, base, &view_layer->object_bases, baseid) { - Object *ob = base->object; + /* FIXME: using a mere counter to generate a 'persistent' dupli id is very weak. One possible + * better solution could be to use `session_uuid` of ID's instead? */ + int persistent_dupli_id = 0; + /* NOTE: this set of flags ensure we only iterate over objects that have a base in either the + * current scene, or the set (background) scene. */ + int deg_objects_visibility_flags = DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | + DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET; + + DEG_OBJECT_ITER_BEGIN (ctx->depsgraph, ob, deg_objects_visibility_flags) { if ((ob != ctx->obedit) && is_child(ob, parent)) { DupliContext pctx; - copy_dupli_context(&pctx, ctx, ctx->object, nullptr, baseid); + copy_dupli_context(&pctx, ctx, ctx->object, nullptr, persistent_dupli_id); /* Meta-balls have a different dupli-handling. */ if (ob->type != OB_MBALL) { @@ -326,7 +329,9 @@ static void make_child_duplis(const DupliContext *ctx, make_child_duplis_cb(&pctx, userdata, ob); } + persistent_dupli_id++; } + DEG_OBJECT_ITER_END; } } diff --git a/source/blender/blenkernel/intern/ocean_intern.h b/source/blender/blenkernel/intern/ocean_intern.h index 4ebd03789af..df9dcd7e2f5 100644 --- a/source/blender/blenkernel/intern/ocean_intern.h +++ b/source/blender/blenkernel/intern/ocean_intern.h @@ -17,7 +17,7 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke */ #ifdef __cplusplus diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index baff1bb47cc..f0f8343420d 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -43,12 +43,12 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" -#include "BKE_font.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_packedFile.h" #include "BKE_report.h" #include "BKE_sound.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "IMB_imbuf.h" @@ -805,27 +805,27 @@ void BKE_packedfile_unpack_all(Main *bmain, ReportList *reports, enum ePF_FileSt } /* ID should be not NULL, return 1 if there's a packed file */ -bool BKE_packedfile_id_check(ID *id) +bool BKE_packedfile_id_check(const ID *id) { switch (GS(id->name)) { case ID_IM: { - Image *ima = (Image *)id; + const Image *ima = (const Image *)id; return BKE_image_has_packedfile(ima); } case ID_VF: { - VFont *vf = (VFont *)id; + const VFont *vf = (const VFont *)id; return vf->packedfile != NULL; } case ID_SO: { - bSound *snd = (bSound *)id; + const bSound *snd = (const bSound *)id; return snd->packedfile != NULL; } case ID_VO: { - Volume *volume = (Volume *)id; + const Volume *volume = (const Volume *)id; return volume->packedfile != NULL; } case ID_LI: { - Library *li = (Library *)id; + const Library *li = (const Library *)id; return li->packedfile != NULL; } default: diff --git a/source/blender/blenkernel/intern/particle.c b/source/blender/blenkernel/intern/particle.c index 50b0fb1c9f5..5b62761bd91 100644 --- a/source/blender/blenkernel/intern/particle.c +++ b/source/blender/blenkernel/intern/particle.c @@ -173,28 +173,29 @@ static void particle_settings_free_data(ID *id) static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) { ParticleSettings *psett = (ParticleSettings *)id; - BKE_LIB_FOREACHID_PROCESS(data, psett->instance_collection, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->instance_object, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, psett->bb_ob, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, psett->collision_group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_collection, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->instance_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->bb_ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->collision_group, IDWALK_CB_NOP); for (int i = 0; i < MAX_MTEX; i++) { if (psett->mtex[i]) { - BKE_texture_mtex_foreach_id(data, psett->mtex[i]); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_texture_mtex_foreach_id(data, psett->mtex[i])); } } if (psett->effector_weights) { - BKE_LIB_FOREACHID_PROCESS(data, psett->effector_weights->group, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->effector_weights->group, IDWALK_CB_NOP); } if (psett->pd) { - BKE_LIB_FOREACHID_PROCESS(data, psett->pd->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->pd->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd->f_source, IDWALK_CB_NOP); } if (psett->pd2) { - BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->tex, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, psett->pd2->f_source, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, psett->pd2->f_source, IDWALK_CB_NOP); } if (psett->boids) { @@ -202,18 +203,18 @@ static void particle_settings_foreach_id(ID *id, LibraryForeachIDData *data) LISTBASE_FOREACH (BoidRule *, rule, &state->rules) { if (rule->type == eBoidRuleType_Avoid) { BoidRuleGoalAvoid *gabr = (BoidRuleGoalAvoid *)rule; - BKE_LIB_FOREACHID_PROCESS(data, gabr->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, gabr->ob, IDWALK_CB_NOP); } else if (rule->type == eBoidRuleType_FollowLeader) { BoidRuleFollowLeader *flbr = (BoidRuleFollowLeader *)rule; - BKE_LIB_FOREACHID_PROCESS(data, flbr->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, flbr->ob, IDWALK_CB_NOP); } } } } LISTBASE_FOREACH (ParticleDupliWeight *, dw, &psett->instance_weights) { - BKE_LIB_FOREACHID_PROCESS(data, dw->ob, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, dw->ob, IDWALK_CB_NOP); } } @@ -4619,11 +4620,11 @@ void psys_get_particle_on_path(ParticleSimulationData *sim, pind.cache = cached ? psys->pointcache : NULL; pind.epoint = NULL; pind.bspline = (psys->part->flag & PART_HAIR_BSPLINE); - /* pind.dm disabled in editmode means we don't get effectors taken into - * account when subdividing for instance */ + /* `pind.dm` disabled in edit-mode means we don't get effectors taken into + * account when subdividing for instance. */ pind.mesh = psys_in_edit_mode(sim->depsgraph, psys) ? NULL : - psys->hair_out_mesh; /* XXX Sybren EEK */ + psys->hair_out_mesh; /* XXX(@sybren) EEK. */ init_particle_interpolation(sim->ob, psys, pa, &pind); do_particle_interpolation(psys, p, pa, t, &pind, state); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 0b4b8fa0eed..b88210f5bbb 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -15,7 +15,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ #include "MEM_guardedalloc.h" @@ -1413,7 +1413,7 @@ static int pbvh_flush_bb(PBVH *pbvh, PBVHNode *node, int flag) { int update = 0; - /* difficult to multithread well, we just do single threaded recursive */ + /* Difficult to multi-thread well, we just do single threaded recursive. */ if (node->flag & PBVH_Leaf) { if (flag & PBVH_UpdateBB) { update |= (node->flag & PBVH_UpdateBB); @@ -2013,7 +2013,7 @@ bool BKE_pbvh_node_vert_update_check_any(PBVH *pbvh, PBVHNode *node) return false; } -/********************************* Raycast ***********************************/ +/********************************* Ray-cast ***********************************/ typedef struct { struct IsectRayAABB_Precalc ray; diff --git a/source/blender/blenkernel/intern/pbvh_bmesh.c b/source/blender/blenkernel/intern/pbvh_bmesh.c index c30f94a4cf6..679a8b378b9 100644 --- a/source/blender/blenkernel/intern/pbvh_bmesh.c +++ b/source/blender/blenkernel/intern/pbvh_bmesh.c @@ -15,7 +15,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ #include "MEM_guardedalloc.h" diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 84c4ae4dead..79b25c027ba 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -17,7 +17,7 @@ #pragma once /** \file - * \ingroup bli + * \ingroup bke */ /* Axis-aligned bounding box */ diff --git a/source/blender/blenkernel/intern/pointcloud.cc b/source/blender/blenkernel/intern/pointcloud.cc index 837a772607f..15c5a809118 100644 --- a/source/blender/blenkernel/intern/pointcloud.cc +++ b/source/blender/blenkernel/intern/pointcloud.cc @@ -105,7 +105,7 @@ static void pointcloud_foreach_id(ID *id, LibraryForeachIDData *data) { PointCloud *pointcloud = (PointCloud *)id; for (int i = 0; i < pointcloud->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, pointcloud->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, pointcloud->mat[i], IDWALK_CB_USER); } } @@ -174,7 +174,7 @@ IDTypeInfo IDType_ID_PT = { /* name */ "PointCloud", /* name_plural */ "pointclouds", /* translation_context */ BLT_I18NCONTEXT_ID_POINTCLOUD, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ pointcloud_init_data, /* copy_data */ pointcloud_copy_data, diff --git a/source/blender/blenkernel/intern/preferences.c b/source/blender/blenkernel/intern/preferences.c index 8dcf6de164a..0a10601f751 100644 --- a/source/blender/blenkernel/intern/preferences.c +++ b/source/blender/blenkernel/intern/preferences.c @@ -61,6 +61,15 @@ bUserAssetLibrary *BKE_preferences_asset_library_add(UserDef *userdef, return library; } +/** + * Unlink and free a library preference member. + * \note Free's \a library itself. + */ +void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library) +{ + BLI_freelinkN(&userdef->asset_libraries, library); +} + void BKE_preferences_asset_library_name_set(UserDef *userdef, bUserAssetLibrary *library, const char *name) @@ -74,15 +83,6 @@ void BKE_preferences_asset_library_name_set(UserDef *userdef, sizeof(library->name)); } -/** - * Unlink and free a library preference member. - * \note Free's \a library itself. - */ -void BKE_preferences_asset_library_remove(UserDef *userdef, bUserAssetLibrary *library) -{ - BLI_freelinkN(&userdef->asset_libraries, library); -} - bUserAssetLibrary *BKE_preferences_asset_library_find_from_index(const UserDef *userdef, int index) { return BLI_findlink(&userdef->asset_libraries, index); @@ -94,6 +94,17 @@ bUserAssetLibrary *BKE_preferences_asset_library_find_from_name(const UserDef *u return BLI_findstring(&userdef->asset_libraries, name, offsetof(bUserAssetLibrary, name)); } +bUserAssetLibrary *BKE_preferences_asset_library_containing_path(const UserDef *userdef, + const char *path) +{ + LISTBASE_FOREACH (bUserAssetLibrary *, asset_lib_pref, &userdef->asset_libraries) { + if (BLI_path_contains(asset_lib_pref->path, path)) { + return asset_lib_pref; + } + } + return NULL; +} + int BKE_preferences_asset_library_get_index(const UserDef *userdef, const bUserAssetLibrary *library) { @@ -109,7 +120,8 @@ void BKE_preferences_asset_library_default_add(UserDef *userdef) return; } - bUserAssetLibrary *library = BKE_preferences_asset_library_add(userdef, DATA_("Default"), NULL); + bUserAssetLibrary *library = BKE_preferences_asset_library_add( + userdef, DATA_(BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME), NULL); /* Add new "Default" library under '[doc_path]/Blender/Assets'. */ BLI_path_join( diff --git a/source/blender/blenkernel/intern/report.c b/source/blender/blenkernel/intern/report.c index c877ec6b6b0..90e7ca3f11a 100644 --- a/source/blender/blenkernel/intern/report.c +++ b/source/blender/blenkernel/intern/report.c @@ -37,7 +37,7 @@ #include "BKE_global.h" /* G.background only */ #include "BKE_report.h" -const char *BKE_report_type_str(ReportType type) +const char *BKE_report_type_str(eReportType type) { switch (type) { case RPT_DEBUG: @@ -101,7 +101,7 @@ void BKE_reports_clear(ReportList *reports) BLI_listbase_clear(&reports->list); } -void BKE_report(ReportList *reports, ReportType type, const char *_message) +void BKE_report(ReportList *reports, eReportType type, const char *_message) { Report *report; int len; @@ -129,7 +129,7 @@ void BKE_report(ReportList *reports, ReportType type, const char *_message) } } -void BKE_reportf(ReportList *reports, ReportType type, const char *_format, ...) +void BKE_reportf(ReportList *reports, eReportType type, const char *_format, ...) { DynStr *ds; Report *report; @@ -215,7 +215,7 @@ void BKE_reports_prependf(ReportList *reports, const char *_prepend, ...) } } -ReportType BKE_report_print_level(ReportList *reports) +eReportType BKE_report_print_level(ReportList *reports) { if (!reports) { return RPT_ERROR; @@ -224,7 +224,7 @@ ReportType BKE_report_print_level(ReportList *reports) return reports->printlevel; } -void BKE_report_print_level_set(ReportList *reports, ReportType level) +void BKE_report_print_level_set(ReportList *reports, eReportType level) { if (!reports) { return; @@ -233,7 +233,7 @@ void BKE_report_print_level_set(ReportList *reports, ReportType level) reports->printlevel = level; } -ReportType BKE_report_store_level(ReportList *reports) +eReportType BKE_report_store_level(ReportList *reports) { if (!reports) { return RPT_ERROR; @@ -242,7 +242,7 @@ ReportType BKE_report_store_level(ReportList *reports) return reports->storelevel; } -void BKE_report_store_level_set(ReportList *reports, ReportType level) +void BKE_report_store_level_set(ReportList *reports, eReportType level) { if (!reports) { return; @@ -251,7 +251,7 @@ void BKE_report_store_level_set(ReportList *reports, ReportType level) reports->storelevel = level; } -char *BKE_reports_string(ReportList *reports, ReportType level) +char *BKE_reports_string(ReportList *reports, eReportType level) { Report *report; DynStr *ds; @@ -279,7 +279,7 @@ char *BKE_reports_string(ReportList *reports, ReportType level) return cstring; } -void BKE_reports_print(ReportList *reports, ReportType level) +void BKE_reports_print(ReportList *reports, eReportType level) { char *cstring = BKE_reports_string(reports, level); @@ -305,7 +305,7 @@ Report *BKE_reports_last_displayable(ReportList *reports) return NULL; } -bool BKE_reports_contain(ReportList *reports, ReportType level) +bool BKE_reports_contain(ReportList *reports, eReportType level) { Report *report; if (reports != NULL) { diff --git a/source/blender/blenkernel/intern/rigidbody.c b/source/blender/blenkernel/intern/rigidbody.c index 328c54fc21b..4482285c271 100644 --- a/source/blender/blenkernel/intern/rigidbody.c +++ b/source/blender/blenkernel/intern/rigidbody.c @@ -18,7 +18,7 @@ */ /** \file - * \ingroup blenkernel + * \ingroup bke * \brief Blender-side interface and methods for dealing with Rigid Body simulations */ @@ -302,7 +302,7 @@ void BKE_rigidbody_object_copy(Main *bmain, Object *ob_dst, const Object *ob_src ob_dst->rigidbody_object = rigidbody_copy_object(ob_src, flag); ob_dst->rigidbody_constraint = rigidbody_copy_constraint(ob_src, flag); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if ((flag & (LIB_ID_CREATE_NO_MAIN | LIB_ID_COPY_RIGID_BODY_NO_COLLECTION_HANDLING)) != 0) { return; } @@ -1211,8 +1211,8 @@ RigidBodyWorld *BKE_rigidbody_world_copy(RigidBodyWorld *rbw, const int flag) id_us_plus((ID *)rbw_copy->constraints); } - if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - /* This is a regular copy, and not a CoW copy for depsgraph evaluation */ + if ((flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) == 0) { + /* This is a regular copy, and not a CoW copy for depsgraph evaluation. */ rbw_copy->shared = MEM_callocN(sizeof(*rbw_copy->shared), "RigidBodyWorld_Shared"); BKE_ptcache_copy_list(&rbw_copy->shared->ptcaches, &rbw->shared->ptcaches, LIB_ID_COPY_CACHES); rbw_copy->shared->pointcache = rbw_copy->shared->ptcaches.first; @@ -1473,16 +1473,51 @@ static bool rigidbody_add_object_to_scene(Main *bmain, Scene *scene, Object *ob) return true; } -void BKE_rigidbody_ensure_local_object(Main *bmain, Object *ob) +static bool rigidbody_add_constraint_to_scene(Main *bmain, Scene *scene, Object *ob) { - if (ob->rigidbody_object == NULL) { - return; + /* Add rigid body world and group if they don't exist for convenience */ + RigidBodyWorld *rbw = BKE_rigidbody_get_world(scene); + if (rbw == NULL) { + rbw = BKE_rigidbody_create_world(scene); + if (rbw == NULL) { + return false; + } + + BKE_rigidbody_validate_sim_world(scene, rbw, false); + scene->rigidbody_world = rbw; } - /* Add newly local object to scene. */ - for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { - if (BKE_scene_object_find(scene, ob)) { - rigidbody_add_object_to_scene(bmain, scene, ob); + if (rbw->constraints == NULL) { + rbw->constraints = BKE_collection_add(bmain, NULL, "RigidBodyConstraints"); + id_fake_user_set(&rbw->constraints->id); + } + + /* Add object to rigid body group. */ + BKE_collection_object_add(bmain, rbw->constraints, ob); + BKE_rigidbody_cache_reset(rbw); + + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&rbw->constraints->id, ID_RECALC_COPY_ON_WRITE); + + return true; +} + +void BKE_rigidbody_ensure_local_object(Main *bmain, Object *ob) +{ + if (ob->rigidbody_object != NULL) { + /* Add newly local object to scene. */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + if (BKE_scene_object_find(scene, ob)) { + rigidbody_add_object_to_scene(bmain, scene, ob); + } + } + } + if (ob->rigidbody_constraint != NULL) { + /* Add newly local object to scene. */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + if (BKE_scene_object_find(scene, ob)) { + rigidbody_add_constraint_to_scene(bmain, scene, ob); + } } } } diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6b5c94a2786..04106e6f42a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -63,6 +63,8 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLO_readfile.h" + #include "BLT_translation.h" #include "BKE_action.h" @@ -237,7 +239,7 @@ static void scene_init_data(ID *id) /* Master Collection */ scene->master_collection = BKE_collection_master_add(); - BKE_view_layer_add(scene, "View Layer", NULL, VIEWLAYER_ADD_NEW); + BKE_view_layer_add(scene, "ViewLayer", NULL, VIEWLAYER_ADD_NEW); } static void scene_copy_markers(Scene *scene_dst, const Scene *scene_src, const int flag) @@ -469,7 +471,8 @@ static void scene_foreach_rigidbodyworldSceneLooper(struct RigidBodyWorld *UNUSE int cb_flag) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; - BKE_lib_query_foreachid_process(data, id_pointer, cb_flag); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_lib_query_foreachid_process(data, id_pointer, cb_flag)); } /** @@ -520,7 +523,10 @@ static void scene_foreach_toolsettings_id_pointer_process( } } -#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS( \ +/* Special handling is needed here, as `scene_foreach_toolsettings` (and its dependency + * `scene_foreach_paint`) are also used by `scene_undo_preserve`, where `LibraryForeachIDData + * *data` is NULL. */ +#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER( \ __data, __id, __do_undo_restore, __action, __reader, __id_old, __cb_flag) \ { \ if (__do_undo_restore) { \ @@ -528,7 +534,21 @@ static void scene_foreach_toolsettings_id_pointer_process( (ID **)&(__id), __action, __reader, (ID **)&(__id_old), __cb_flag); \ } \ else { \ - BKE_LIB_FOREACHID_PROCESS(__data, __id, __cb_flag); \ + BLI_assert((__data) != NULL); \ + BKE_LIB_FOREACHID_PROCESS_IDSUPER(__data, __id, __cb_flag); \ + } \ + } \ + (void)0 + +#define BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( \ + __data, __do_undo_restore, __func_call) \ + { \ + if (__do_undo_restore) { \ + __func_call; \ + } \ + else { \ + BLI_assert((__data) != NULL); \ + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(__data, __func_call); \ } \ } \ (void)0 @@ -539,13 +559,13 @@ static void scene_foreach_paint(LibraryForeachIDData *data, BlendLibReader *reader, Paint *paint_old) { - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - paint->brush, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->brush, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + paint->brush, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->brush, + IDWALK_CB_USER); for (int i = 0; i < paint_old->tool_slots_len; i++) { /* This is a bit tricky. * - In case we do not do `undo_restore`, `paint` and `paint_old` pointers are the same, so @@ -557,21 +577,21 @@ static void scene_foreach_paint(LibraryForeachIDData *data, */ Brush *brush_tmp = NULL; Brush **brush_p = i < paint->tool_slots_len ? &paint->tool_slots[i].brush : &brush_tmp; - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - *brush_p, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->brush, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + *brush_p, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->brush, + IDWALK_CB_USER); } - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - paint->palette, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - paint_old->palette, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + paint->palette, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + paint_old->palette, + IDWALK_CB_USER); } static void scene_foreach_toolsettings(LibraryForeachIDData *data, @@ -580,110 +600,152 @@ static void scene_foreach_toolsettings(LibraryForeachIDData *data, BlendLibReader *reader, ToolSettings *toolsett_old) { - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.scene, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.scene, - IDWALK_CB_NOP); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.object, - IDWALK_CB_NOP); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->particle.shape_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->particle.shape_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.scene, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.scene, + IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.object, + IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->particle.shape_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->particle.shape_object, + IDWALK_CB_NOP); scene_foreach_paint( data, &toolsett->imapaint.paint, do_undo_restore, reader, &toolsett_old->imapaint.paint); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.stencil, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.stencil, - IDWALK_CB_USER); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.clone, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.clone, - IDWALK_CB_USER); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->imapaint.canvas, - do_undo_restore, - SCENE_FOREACH_UNDO_RESTORE, - reader, - toolsett_old->imapaint.canvas, - IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.stencil, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.stencil, + IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.clone, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.clone, + IDWALK_CB_USER); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->imapaint.canvas, + do_undo_restore, + SCENE_FOREACH_UNDO_RESTORE, + reader, + toolsett_old->imapaint.canvas, + IDWALK_CB_USER); if (toolsett->vpaint) { - scene_foreach_paint( - data, &toolsett->vpaint->paint, do_undo_restore, reader, &toolsett_old->vpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->vpaint->paint, + do_undo_restore, + reader, + &toolsett_old->vpaint->paint)); } if (toolsett->wpaint) { - scene_foreach_paint( - data, &toolsett->wpaint->paint, do_undo_restore, reader, &toolsett_old->wpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->wpaint->paint, + do_undo_restore, + reader, + &toolsett_old->wpaint->paint)); } if (toolsett->sculpt) { - scene_foreach_paint( - data, &toolsett->sculpt->paint, do_undo_restore, reader, &toolsett_old->sculpt->paint); - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->sculpt->gravity_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->sculpt->gravity_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->sculpt->paint, + do_undo_restore, + reader, + &toolsett_old->sculpt->paint)); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->sculpt->gravity_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->sculpt->gravity_object, + IDWALK_CB_NOP); } if (toolsett->uvsculpt) { - scene_foreach_paint( - data, &toolsett->uvsculpt->paint, do_undo_restore, reader, &toolsett_old->uvsculpt->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->uvsculpt->paint, + do_undo_restore, + reader, + &toolsett_old->uvsculpt->paint)); } if (toolsett->gp_paint) { - scene_foreach_paint( - data, &toolsett->gp_paint->paint, do_undo_restore, reader, &toolsett_old->gp_paint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_paint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_paint->paint)); } if (toolsett->gp_vertexpaint) { - scene_foreach_paint(data, - &toolsett->gp_vertexpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_vertexpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_vertexpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_vertexpaint->paint)); } if (toolsett->gp_sculptpaint) { - scene_foreach_paint(data, - &toolsett->gp_sculptpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_sculptpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_sculptpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_sculptpaint->paint)); } if (toolsett->gp_weightpaint) { - scene_foreach_paint(data, - &toolsett->gp_weightpaint->paint, - do_undo_restore, - reader, - &toolsett_old->gp_weightpaint->paint); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL( + data, + do_undo_restore, + scene_foreach_paint(data, + &toolsett->gp_weightpaint->paint, + do_undo_restore, + reader, + &toolsett_old->gp_weightpaint->paint)); } - BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS(data, - toolsett->gp_sculpt.guide.reference_object, - do_undo_restore, - SCENE_FOREACH_UNDO_NO_RESTORE, - reader, - toolsett_old->gp_sculpt.guide.reference_object, - IDWALK_CB_NOP); + BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER(data, + toolsett->gp_sculpt.guide.reference_object, + do_undo_restore, + SCENE_FOREACH_UNDO_NO_RESTORE, + reader, + toolsett_old->gp_sculpt.guide.reference_object, + IDWALK_CB_NOP); } +#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_IDSUPER +#undef BKE_LIB_FOREACHID_UNDO_PRESERVE_PROCESS_FUNCTION_CALL + static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase *lb) { LISTBASE_FOREACH (LayerCollection *, lc, lb) { @@ -693,7 +755,7 @@ static void scene_foreach_layer_collection(LibraryForeachIDData *data, ListBase (lc->collection->id.flag & LIB_EMBEDDED_DATA) != 0) ? IDWALK_CB_EMBEDDED : IDWALK_CB_NOP; - BKE_LIB_FOREACHID_PROCESS(data, lc->collection, cb_flag); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lc->collection, cb_flag); scene_foreach_layer_collection(data, &lc->layer_collections); } } @@ -702,32 +764,33 @@ static bool seq_foreach_member_id_cb(Sequence *seq, void *user_data) { LibraryForeachIDData *data = (LibraryForeachIDData *)user_data; -#define FOREACHID_PROCESS(_data, _id_super, _cb_flag) \ +#define FOREACHID_PROCESS_IDSUPER(_data, _id_super, _cb_flag) \ { \ CHECK_TYPE(&((_id_super)->id), ID *); \ - if (!BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag))) { \ + BKE_lib_query_foreachid_process((_data), (ID **)&(_id_super), (_cb_flag)); \ + if (BKE_lib_query_foreachid_iter_stop((_data))) { \ return false; \ } \ } \ ((void)0) - FOREACHID_PROCESS(data, seq->scene, IDWALK_CB_NEVER_SELF); - FOREACHID_PROCESS(data, seq->scene_camera, IDWALK_CB_NOP); - FOREACHID_PROCESS(data, seq->clip, IDWALK_CB_USER); - FOREACHID_PROCESS(data, seq->mask, IDWALK_CB_USER); - FOREACHID_PROCESS(data, seq->sound, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->scene, IDWALK_CB_NEVER_SELF); + FOREACHID_PROCESS_IDSUPER(data, seq->scene_camera, IDWALK_CB_NOP); + FOREACHID_PROCESS_IDSUPER(data, seq->clip, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->mask, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, seq->sound, IDWALK_CB_USER); IDP_foreach_property( seq->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) { - FOREACHID_PROCESS(data, smd->mask_id, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, smd->mask_id, IDWALK_CB_USER); } if (seq->type == SEQ_TYPE_TEXT && seq->effectdata) { TextVars *text_data = seq->effectdata; - FOREACHID_PROCESS(data, text_data->text_font, IDWALK_CB_USER); + FOREACHID_PROCESS_IDSUPER(data, text_data->text_font, IDWALK_CB_USER); } -#undef FOREACHID_PROCESS +#undef FOREACHID_PROCESS_IDSUPER return true; } @@ -736,66 +799,77 @@ static void scene_foreach_id(ID *id, LibraryForeachIDData *data) { Scene *scene = (Scene *)id; - BKE_LIB_FOREACHID_PROCESS(data, scene->camera, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, scene->world, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->set, IDWALK_CB_NEVER_SELF); - BKE_LIB_FOREACHID_PROCESS(data, scene->clip, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->gpd, IDWALK_CB_USER); - BKE_LIB_FOREACHID_PROCESS(data, scene->r.bake.cage_object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->world, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->set, IDWALK_CB_NEVER_SELF); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->clip, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scene->r.bake.cage_object, IDWALK_CB_NOP); if (scene->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->nodetree)); } if (scene->ed) { - SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, SEQ_for_each_callback(&scene->ed->seqbase, seq_foreach_member_id_cb, data)); } /* This pointer can be NULL during old files reading, better be safe than sorry. */ if (scene->master_collection != NULL) { - BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&scene->master_collection)); } LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { - BKE_LIB_FOREACHID_PROCESS(data, view_layer->mat_override, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, view_layer->mat_override, IDWALK_CB_USER); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, base->object, IDWALK_CB_NOP | IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE); } - scene_foreach_layer_collection(data, &view_layer->layer_collections); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, scene_foreach_layer_collection(data, &view_layer->layer_collections)); LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { if (fmc->script) { - BKE_LIB_FOREACHID_PROCESS(data, fmc->script, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fmc->script, IDWALK_CB_NOP); } } LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { if (fls->group) { - BKE_LIB_FOREACHID_PROCESS(data, fls->group, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->group, IDWALK_CB_USER); } if (fls->linestyle) { - BKE_LIB_FOREACHID_PROCESS(data, fls->linestyle, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, fls->linestyle, IDWALK_CB_USER); } } } LISTBASE_FOREACH (TimeMarker *, marker, &scene->markers) { - BKE_LIB_FOREACHID_PROCESS(data, marker->camera, IDWALK_CB_NOP); - IDP_foreach_property( - marker->prop, IDP_TYPE_FILTER_ID, BKE_lib_query_idpropertiesForeachIDLink_callback, data); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, marker->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + IDP_foreach_property(marker->prop, + IDP_TYPE_FILTER_ID, + BKE_lib_query_idpropertiesForeachIDLink_callback, + data)); } ToolSettings *toolsett = scene->toolsettings; if (toolsett) { - scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, scene_foreach_toolsettings(data, toolsett, false, NULL, toolsett)); } if (scene->rigidbody_world) { - BKE_rigidbody_world_id_loop( - scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, + BKE_rigidbody_world_id_loop( + scene->rigidbody_world, scene_foreach_rigidbodyworldSceneLooper, data)); } } @@ -993,8 +1067,13 @@ static void link_recurs_seq(BlendDataReader *reader, ListBase *lb) { BLO_read_list(reader, lb); - LISTBASE_FOREACH (Sequence *, seq, lb) { - if (seq->seqbase.first) { + LISTBASE_FOREACH_MUTABLE (Sequence *, seq, lb) { + /* Sanity check. */ + if (!SEQ_valid_strip_channel(seq)) { + BLI_freelinkN(lb, seq); + BLO_read_data_reports(reader)->count.vse_strips_skipped++; + } + else if (seq->seqbase.first) { link_recurs_seq(reader, &seq->seqbase); } } @@ -1794,6 +1873,7 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) /* Scene duplication is always root of duplication currently. */ const bool is_subprocess = false; const bool is_root_id = true; + const int copy_flags = LIB_ID_COPY_DEFAULT; if (!is_subprocess) { BKE_main_id_newptr_and_tag_clear(bmain); @@ -1809,24 +1889,43 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type) /* Copy Freestyle LineStyle datablocks. */ LISTBASE_FOREACH (ViewLayer *, view_layer_dst, &sce_copy->view_layers) { LISTBASE_FOREACH (FreestyleLineSet *, lineset, &view_layer_dst->freestyle_config.linesets) { - BKE_id_copy_for_duplicate(bmain, (ID *)lineset->linestyle, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)lineset->linestyle, duplicate_flags, copy_flags); } } /* Full copy of world (included animations) */ - BKE_id_copy_for_duplicate(bmain, (ID *)sce->world, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)sce->world, duplicate_flags, copy_flags); /* Full copy of GreasePencil. */ - BKE_id_copy_for_duplicate(bmain, (ID *)sce->gpd, duplicate_flags); + BKE_id_copy_for_duplicate(bmain, (ID *)sce->gpd, duplicate_flags, copy_flags); /* Deep-duplicate collections and objects (using preferences' settings for which sub-data to * duplicate along the object itself). */ BKE_collection_duplicate( bmain, NULL, sce_copy->master_collection, duplicate_flags, LIB_ID_DUPLICATE_IS_SUBPROCESS); + /* Rigid body world collections may not be instantiated as scene's collections, ensure they + * also get properly duplicated. */ + if (sce_copy->rigidbody_world != NULL) { + if (sce_copy->rigidbody_world->group != NULL) { + BKE_collection_duplicate(bmain, + NULL, + sce_copy->rigidbody_world->group, + duplicate_flags, + LIB_ID_DUPLICATE_IS_SUBPROCESS); + } + if (sce_copy->rigidbody_world->constraints != NULL) { + BKE_collection_duplicate(bmain, + NULL, + sce_copy->rigidbody_world->constraints, + duplicate_flags, + LIB_ID_DUPLICATE_IS_SUBPROCESS); + } + } + if (!is_subprocess) { /* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW. */ - BKE_libblock_relink_to_newid(&sce_copy->id); + BKE_libblock_relink_to_newid(bmain, &sce_copy->id, 0); #ifndef NDEBUG /* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those @@ -2465,7 +2564,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on // DEG_debug_graph_relations_validate(depsgraph, bmain, scene); /* Flush editing data if needed. */ prepare_mesh_for_viewport_render(bmain, view_layer); - /* Update all objects: drivers, matrices, displists, etc. flags set + /* Update all objects: drivers, matrices, #DispList, etc. flags set * by depsgraph or manual, no layer check here, gets correct flushed. */ DEG_evaluate_on_refresh(depsgraph); /* Update sound system. */ @@ -2541,7 +2640,7 @@ void BKE_scene_graph_update_for_newframe_ex(Depsgraph *depsgraph, const bool cle BKE_image_editors_update_frame(bmain, scene->r.cfra); BKE_sound_set_cfra(scene->r.cfra); DEG_graph_relations_update(depsgraph); - /* Update all objects: drivers, matrices, displists, etc. flags set + /* Update all objects: drivers, matrices, #DispList, etc. flags set * by depgraph or manual, no layer check here, gets correct flushed. * * NOTE: Only update for new frame on first iteration. Second iteration is for ensuring user @@ -2760,6 +2859,12 @@ bool BKE_scene_uses_cycles_experimental_features(Scene *scene) PointerRNA scene_ptr; RNA_id_pointer_create(&scene->id, &scene_ptr); PointerRNA cycles_ptr = RNA_pointer_get(&scene_ptr, "cycles"); + + if (RNA_pointer_is_null(&cycles_ptr)) { + /* The pointer only exists if Cycles is enabled. */ + return false; + } + return RNA_enum_get(&cycles_ptr, "feature_set") == CYCLES_FEATURES_EXPERIMENTAL; } diff --git a/source/blender/blenkernel/intern/screen.c b/source/blender/blenkernel/intern/screen.c index 73e25a22225..6352e08ec4b 100644 --- a/source/blender/blenkernel/intern/screen.c +++ b/source/blender/blenkernel/intern/screen.c @@ -93,13 +93,17 @@ static void screen_foreach_id_dopesheet(LibraryForeachIDData *data, bDopeSheet * { if (ads != NULL) { BKE_LIB_FOREACHID_PROCESS_ID(data, ads->source, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, ads->filter_grp, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, ads->filter_grp, IDWALK_CB_NOP); } } +/** + * Callback used by lib_query to walk over all ID usages (mimics `foreach_id` callback of + * `IDTypeInfo` structure). + */ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area) { - BKE_LIB_FOREACHID_PROCESS(data, area->full, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, area->full, IDWALK_CB_NOP); /* TODO: this should be moved to a callback in `SpaceType`, defined in each editor's own code. * Will be for a later round of cleanup though... */ @@ -107,24 +111,21 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area switch (sl->spacetype) { case SPACE_VIEW3D: { View3D *v3d = (View3D *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, v3d->camera, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, v3d->ob_center, IDWALK_CB_NOP); - + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->ob_center, IDWALK_CB_NOP); if (v3d->localvd) { - BKE_LIB_FOREACHID_PROCESS(data, v3d->localvd->camera, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, v3d->localvd->camera, IDWALK_CB_NOP); } break; } case SPACE_GRAPH: { SpaceGraph *sipo = (SpaceGraph *)sl; - - screen_foreach_id_dopesheet(data, sipo->ads); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + screen_foreach_id_dopesheet(data, sipo->ads)); break; } case SPACE_PROPERTIES: { SpaceProperties *sbuts = (SpaceProperties *)sl; - BKE_LIB_FOREACHID_PROCESS_ID(data, sbuts->pinid, IDWALK_CB_NOP); break; } @@ -132,48 +133,41 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area break; case SPACE_ACTION: { SpaceAction *saction = (SpaceAction *)sl; - screen_foreach_id_dopesheet(data, &saction->ads); - BKE_LIB_FOREACHID_PROCESS(data, saction->action, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, saction->action, IDWALK_CB_NOP); break; } case SPACE_IMAGE: { SpaceImage *sima = (SpaceImage *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, sima->image, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sima->mask_info.mask, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sima->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->image, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->mask_info.mask, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sima->gpd, IDWALK_CB_USER); break; } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, sseq->gpd, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sseq->gpd, IDWALK_CB_USER); break; } case SPACE_NLA: { SpaceNla *snla = (SpaceNla *)sl; - - screen_foreach_id_dopesheet(data, snla->ads); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + screen_foreach_id_dopesheet(data, snla->ads)); break; } case SPACE_TEXT: { SpaceText *st = (SpaceText *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, st->text, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, st->text, IDWALK_CB_NOP); break; } case SPACE_SCRIPT: { SpaceScript *scpt = (SpaceScript *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, scpt->script, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, scpt->script, IDWALK_CB_NOP); break; } case SPACE_OUTLINER: { SpaceOutliner *space_outliner = (SpaceOutliner *)sl; - BKE_LIB_FOREACHID_PROCESS_ID(data, space_outliner->search_tse.id, IDWALK_CB_NOP); - if (space_outliner->treestore != NULL) { TreeStoreElem *tselem; BLI_mempool_iter iter; @@ -187,26 +181,24 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } case SPACE_NODE: { SpaceNode *snode = (SpaceNode *)sl; - const bool is_private_nodetree = snode->id != NULL && ntreeFromID(snode->id) == snode->nodetree; BKE_LIB_FOREACHID_PROCESS_ID(data, snode->id, IDWALK_CB_NOP); BKE_LIB_FOREACHID_PROCESS_ID(data, snode->from, IDWALK_CB_NOP); - - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, snode->nodetree, is_private_nodetree ? IDWALK_CB_EMBEDDED : IDWALK_CB_USER_ONE); LISTBASE_FOREACH (bNodeTreePath *, path, &snode->treepath) { if (path == snode->treepath.first) { /* first nodetree in path is same as snode->nodetree */ - BKE_LIB_FOREACHID_PROCESS(data, - path->nodetree, - is_private_nodetree ? IDWALK_CB_EMBEDDED : - IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, + path->nodetree, + is_private_nodetree ? IDWALK_CB_EMBEDDED : + IDWALK_CB_USER_ONE); } else { - BKE_LIB_FOREACHID_PROCESS(data, path->nodetree, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, path->nodetree, IDWALK_CB_USER_ONE); } if (path->nodetree == NULL) { @@ -214,22 +206,20 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area } } - BKE_LIB_FOREACHID_PROCESS(data, snode->edittree, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, snode->edittree, IDWALK_CB_NOP); break; } case SPACE_CLIP: { SpaceClip *sclip = (SpaceClip *)sl; - - BKE_LIB_FOREACHID_PROCESS(data, sclip->clip, IDWALK_CB_USER_ONE); - BKE_LIB_FOREACHID_PROCESS(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->clip, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, sclip->mask_info.mask, IDWALK_CB_USER_ONE); break; } case SPACE_SPREADSHEET: { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - LISTBASE_FOREACH (SpreadsheetContext *, context, &sspreadsheet->context_path) { if (context->type == SPREADSHEET_CONTEXT_OBJECT) { - BKE_LIB_FOREACHID_PROCESS( + BKE_LIB_FOREACHID_PROCESS_IDSUPER( data, ((SpreadsheetContextObject *)context)->object, IDWALK_CB_NOP); } } @@ -243,12 +233,13 @@ void BKE_screen_foreach_id_screen_area(LibraryForeachIDData *data, ScrArea *area static void screen_foreach_id(ID *id, LibraryForeachIDData *data) { - if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { - bScreen *screen = (bScreen *)id; + if ((BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) == 0) { + return; + } + bScreen *screen = (bScreen *)id; - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - BKE_screen_foreach_id_screen_area(data, area); - } + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_screen_foreach_id_screen_area(data, area)); } } @@ -1624,7 +1615,6 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) SpaceImage *sima = (SpaceImage *)sl; sima->iuser.scene = NULL; - sima->iuser.ok = 1; sima->scopes.waveform_1 = NULL; sima->scopes.waveform_2 = NULL; sima->scopes.waveform_3 = NULL; @@ -1679,6 +1669,7 @@ static void direct_link_area(BlendDataReader *reader, ScrArea *area) sseq->scopes.sep_waveform_ibuf = NULL; sseq->scopes.vector_ibuf = NULL; sseq->scopes.histogram_ibuf = NULL; + memset(&sseq->runtime, 0x0, sizeof(sseq->runtime)); } else if (sl->spacetype == SPACE_PROPERTIES) { SpaceProperties *sbuts = (SpaceProperties *)sl; diff --git a/source/blender/blenkernel/intern/shrinkwrap.c b/source/blender/blenkernel/intern/shrinkwrap.c index 7c0c28d664e..dd863f1ce06 100644 --- a/source/blender/blenkernel/intern/shrinkwrap.c +++ b/source/blender/blenkernel/intern/shrinkwrap.c @@ -644,7 +644,7 @@ static void shrinkwrap_calc_normal_projection(ShrinkwrapCalcData *calc) /* Options about projection direction */ float proj_axis[3] = {0.0f, 0.0f, 0.0f}; - /* Raycast and tree stuff */ + /* Ray-cast and tree stuff. */ /** \note 'hit.dist' is kept in the targets space, this is only used * for finding the best hit, to get the real dist, diff --git a/source/blender/blenkernel/intern/simulation.cc b/source/blender/blenkernel/intern/simulation.cc index 4c97ccdf8b1..98e7405bde6 100644 --- a/source/blender/blenkernel/intern/simulation.cc +++ b/source/blender/blenkernel/intern/simulation.cc @@ -103,7 +103,8 @@ static void simulation_foreach_id(ID *id, LibraryForeachIDData *data) Simulation *simulation = (Simulation *)id; if (simulation->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&simulation->nodetree)); } } @@ -152,7 +153,7 @@ IDTypeInfo IDType_ID_SIM = { /* name */ "Simulation", /* name_plural */ "simulations", /* translation_context */ BLT_I18NCONTEXT_ID_SIMULATION, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ simulation_init_data, /* copy_data */ simulation_copy_data, diff --git a/source/blender/blenkernel/intern/softbody.c b/source/blender/blenkernel/intern/softbody.c index fbc781f5eb9..b7eb9d31b23 100644 --- a/source/blender/blenkernel/intern/softbody.c +++ b/source/blender/blenkernel/intern/softbody.c @@ -2295,7 +2295,7 @@ static void softbody_calc_forces( sb_sfesf_threads_run(depsgraph, scene, ob, timenow, sb->totspring, NULL); } - /* after spring scan because it uses Effoctors too */ + /* After spring scan because it uses effectors too. */ ListBase *effectors = BKE_effectors_create(depsgraph, ob, NULL, sb->effector_weights, false); if (do_deflector) { diff --git a/source/blender/blenkernel/intern/sound.c b/source/blender/blenkernel/intern/sound.c index c61fa793367..f523c5e02bd 100644 --- a/source/blender/blenkernel/intern/sound.c +++ b/source/blender/blenkernel/intern/sound.c @@ -204,7 +204,7 @@ IDTypeInfo IDType_ID_SO = { .name = "Sound", .name_plural = "sounds", .translation_context = BLT_I18NCONTEXT_ID_SOUND, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* A fuzzy case, think NULLified content is OK here... */ .init_data = NULL, @@ -702,13 +702,13 @@ void *BKE_sound_scene_add_scene_sound( Scene *scene, Sequence *sequence, int startframe, int endframe, int frameskip) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene && scene != sequence->scene && sequence->sound) { + if (sequence->scene && scene != sequence->scene) { const double fps = FPS; return AUD_Sequence_add(scene->sound_scene, sequence->scene->sound_scene, startframe / fps, endframe / fps, - frameskip / fps + sequence->sound->offset_time); + frameskip / fps); } return NULL; } @@ -774,13 +774,13 @@ void BKE_sound_move_scene_sound( void BKE_sound_move_scene_sound_defaults(Scene *scene, Sequence *sequence) { sound_verify_evaluated_id(&scene->id); - if (sequence->scene_sound && sequence->sound) { + if (sequence->scene_sound) { BKE_sound_move_scene_sound(scene, sequence->scene_sound, sequence->startdisp, sequence->enddisp, sequence->startofs + sequence->anim_startofs, - sequence->sound->offset_time); + 0.0); } } @@ -1213,7 +1213,6 @@ static bool sound_info_from_playback_handle(void *playback_handle, SoundInfo *so AUD_SoundInfo info = AUD_getInfo(playback_handle); sound_info->specs.channels = (eSoundChannels)info.specs.channels; sound_info->length = info.length; - sound_info->start_offset = info.start_offset; return true; } @@ -1231,6 +1230,47 @@ bool BKE_sound_info_get(struct Main *main, struct bSound *sound, SoundInfo *soun return result; } +bool BKE_sound_stream_info_get(struct Main *main, + const char *filepath, + int stream, + SoundStreamInfo *sound_info) +{ + const char *path; + char str[FILE_MAX]; + AUD_Sound *sound; + AUD_StreamInfo *stream_infos; + int stream_count; + + BLI_strncpy(str, filepath, sizeof(str)); + path = BKE_main_blendfile_path(main); + BLI_path_abs(str, path); + + sound = AUD_Sound_file(str); + if (!sound) { + return false; + } + + stream_count = AUD_Sound_getFileStreams(sound, &stream_infos); + + AUD_Sound_free(sound); + + if (!stream_infos) { + return false; + } + + if ((stream < 0) || (stream >= stream_count)) { + free(stream_infos); + return false; + } + + sound_info->start = stream_infos[stream].start; + sound_info->duration = stream_infos[stream].duration; + + free(stream_infos); + + return true; +} + #else /* WITH_AUDASPACE */ # include "BLI_utildefines.h" @@ -1400,6 +1440,14 @@ bool BKE_sound_info_get(struct Main *UNUSED(main), return false; } +bool BKE_sound_stream_info_get(struct Main *UNUSED(main), + const char *UNUSED(filepath), + int UNUSED(stream), + SoundStreamInfo *UNUSED(sound_info)) +{ + return false; +} + #endif /* WITH_AUDASPACE */ void BKE_sound_reset_scene_runtime(Scene *scene) diff --git a/source/blender/blenkernel/intern/speaker.c b/source/blender/blenkernel/intern/speaker.c index 4b10522c375..230ff9d6da0 100644 --- a/source/blender/blenkernel/intern/speaker.c +++ b/source/blender/blenkernel/intern/speaker.c @@ -50,7 +50,7 @@ static void speaker_foreach_id(ID *id, LibraryForeachIDData *data) { Speaker *speaker = (Speaker *)id; - BKE_LIB_FOREACHID_PROCESS(data, speaker->sound, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, speaker->sound, IDWALK_CB_USER); } static void speaker_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -98,7 +98,7 @@ IDTypeInfo IDType_ID_SPK = { .name = "Speaker", .name_plural = "speakers", .translation_context = BLT_I18NCONTEXT_ID_SPEAKER, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = speaker_init_data, .copy_data = NULL, diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc index a8871777420..c2c9d178171 100644 --- a/source/blender/blenkernel/intern/spline_base.cc +++ b/source/blender/blenkernel/intern/spline_base.cc @@ -142,7 +142,8 @@ void Spline::reverse() int Spline::evaluated_edges_size() const { const int eval_size = this->evaluated_points_size(); - if (eval_size == 1) { + if (eval_size < 2) { + /* Two points are required for an edge. */ return 0; } @@ -190,7 +191,7 @@ static void accumulate_lengths(Span positions, * Return non-owning access to the cache of accumulated lengths along the spline. Each item is the * length of the subsequent segment, i.e. the first value is the length of the first segment rather * than 0. This calculation is rather trivial, and only depends on the evaluated positions. - * However, the results are used often, so it makes sense to cache it. + * However, the results are used often, and it is necessarily single threaded, so it is cached. */ Span Spline::evaluated_lengths() const { @@ -205,9 +206,10 @@ Span Spline::evaluated_lengths() const const int total = evaluated_edges_size(); evaluated_lengths_cache_.resize(total); - - Span positions = this->evaluated_positions(); - accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_); + if (total != 0) { + Span positions = this->evaluated_positions(); + accumulate_lengths(positions, is_cyclic_, evaluated_lengths_cache_); + } length_cache_dirty_ = false; return evaluated_lengths_cache_; @@ -484,6 +486,12 @@ Array Spline::sample_uniform_index_factors(const int samples_size) const prev_length = length; } + /* Zero lengths or float inaccuracies can cause invalid values, or simply + * skip some, so set the values that weren't completed in the main loop. */ + for (const int i : IndexRange(i_sample, samples_size - i_sample)) { + samples[i] = float(samples_size); + } + if (!is_cyclic_) { /* In rare cases this can prevent overflow of the stored index. */ samples.last() = lengths.size(); diff --git a/source/blender/blenkernel/intern/spline_bezier.cc b/source/blender/blenkernel/intern/spline_bezier.cc index 79d2137ee84..e760bf3495e 100644 --- a/source/blender/blenkernel/intern/spline_bezier.cc +++ b/source/blender/blenkernel/intern/spline_bezier.cc @@ -214,6 +214,11 @@ void BezierSpline::ensure_auto_handles() const return; } + if (this->size() == 1) { + auto_handles_dirty_ = false; + return; + } + for (const int i : IndexRange(this->size())) { if (ELEM(HandleType::Auto, handle_types_left_[i], handle_types_right_[i])) { const float3 prev_diff = positions_[i] - previous_position(positions_, is_cyclic_, i); @@ -284,14 +289,70 @@ void BezierSpline::transform(const blender::float4x4 &matrix) this->mark_cache_invalid(); } +static void set_handle_position(const float3 &position, + const BezierSpline::HandleType type, + const BezierSpline::HandleType type_other, + const float3 &new_value, + float3 &handle, + float3 &handle_other) +{ + /* Don't bother when the handle positions are calculated automatically anyway. */ + if (ELEM(type, BezierSpline::HandleType::Auto, BezierSpline::HandleType::Vector)) { + return; + } + + handle = new_value; + if (type_other == BezierSpline::HandleType::Align) { + /* Keep track of the old length of the opposite handle. */ + const float length = float3::distance(handle_other, position); + /* Set the other handle to directly opposite from the current handle. */ + const float3 dir = (handle - position).normalized(); + handle_other = position - dir * length; + } +} + +/** + * Set positions for the right handle of the control point, ensuring that + * aligned handles stay aligned. Has no effect for auto and vector type handles. + */ +void BezierSpline::set_handle_position_right(const int index, const blender::float3 &value) +{ + set_handle_position(positions_[index], + handle_types_right_[index], + handle_types_left_[index], + value, + handle_positions_right_[index], + handle_positions_left_[index]); +} + +/** + * Set positions for the left handle of the control point, ensuring that + * aligned handles stay aligned. Has no effect for auto and vector type handles. + */ +void BezierSpline::set_handle_position_left(const int index, const blender::float3 &value) +{ + set_handle_position(positions_[index], + handle_types_left_[index], + handle_types_right_[index], + value, + handle_positions_left_[index], + handle_positions_right_[index]); +} + bool BezierSpline::point_is_sharp(const int index) const { return ELEM(handle_types_left_[index], HandleType::Vector, HandleType::Free) || ELEM(handle_types_right_[index], HandleType::Vector, HandleType::Free); } +/** + * \warning This functional assumes that the spline has more than one point. + */ bool BezierSpline::segment_is_vector(const int index) const { + /* Two control points are necessary to form a segment, that should be checked by the caller. */ + BLI_assert(this->size() > 1); + if (index == this->size() - 1) { if (is_cyclic_) { return handle_types_right_.last() == HandleType::Vector && @@ -452,13 +513,18 @@ Span BezierSpline::control_point_offsets() const offset_cache_.resize(size + 1); MutableSpan offsets = offset_cache_; - - int offset = 0; - for (const int i : IndexRange(size)) { - offsets[i] = offset; - offset += this->segment_is_vector(i) ? 1 : resolution_; + if (size == 1) { + offsets.first() = 0; + offsets.last() = 1; + } + else { + int offset = 0; + for (const int i : IndexRange(size)) { + offsets[i] = offset; + offset += this->segment_is_vector(i) ? 1 : resolution_; + } + offsets.last() = offset; } - offsets.last() = offset; offset_cache_dirty_ = false; return offsets; @@ -545,14 +611,22 @@ Span BezierSpline::evaluated_positions() const return evaluated_position_cache_; } - this->ensure_auto_handles(); - const int size = this->size(); const int eval_size = this->evaluated_points_size(); evaluated_position_cache_.resize(eval_size); MutableSpan positions = evaluated_position_cache_; + if (size == 1) { + /* Use a special case for single point splines to avoid checking in #evaluate_segment. */ + BLI_assert(eval_size == 1); + positions.first() = positions_.first(); + position_cache_dirty_ = false; + return positions; + } + + this->ensure_auto_handles(); + Span offsets = this->control_point_offsets(); const int grain_size = std::max(512 / resolution_, 1); diff --git a/source/blender/blenkernel/intern/subdiv_ccg.c b/source/blender/blenkernel/intern/subdiv_ccg.c index 95f51a72b70..77962ec924c 100644 --- a/source/blender/blenkernel/intern/subdiv_ccg.c +++ b/source/blender/blenkernel/intern/subdiv_ccg.c @@ -359,30 +359,29 @@ static void subdiv_ccg_init_faces(SubdivCCG *subdiv_ccg) /* TODO(sergey): Consider making it generic enough to be fit into BLI. */ typedef struct StaticOrHeapIntStorage { int static_storage[64]; - int static_storage_size; + int static_storage_len; int *heap_storage; - int heap_storage_size; + int heap_storage_len; } StaticOrHeapIntStorage; static void static_or_heap_storage_init(StaticOrHeapIntStorage *storage) { - storage->static_storage_size = sizeof(storage->static_storage) / - sizeof(*storage->static_storage); + storage->static_storage_len = sizeof(storage->static_storage) / sizeof(*storage->static_storage); storage->heap_storage = NULL; - storage->heap_storage_size = 0; + storage->heap_storage_len = 0; } -static int *static_or_heap_storage_get(StaticOrHeapIntStorage *storage, int size) +static int *static_or_heap_storage_get(StaticOrHeapIntStorage *storage, int heap_len) { /* Requested size small enough to be fit into stack allocated memory. */ - if (size <= storage->static_storage_size) { + if (heap_len <= storage->static_storage_len) { return storage->static_storage; } /* Make sure heap ius big enough. */ - if (size > storage->heap_storage_size) { + if (heap_len > storage->heap_storage_len) { MEM_SAFE_FREE(storage->heap_storage); - storage->heap_storage = MEM_malloc_arrayN(size, sizeof(int), "int storage"); - storage->heap_storage_size = size; + storage->heap_storage = MEM_malloc_arrayN(heap_len, sizeof(int), "int storage"); + storage->heap_storage_len = heap_len; } return storage->heap_storage; } @@ -1062,7 +1061,7 @@ static void subdiv_ccg_average_grids_boundary(SubdivCCG *subdiv_ccg, } if (tls->accumulators == NULL) { tls->accumulators = MEM_calloc_arrayN( - sizeof(GridElementAccumulator), grid_size2, "average accumulators"); + grid_size2, sizeof(GridElementAccumulator), "average accumulators"); } else { for (int i = 1; i < grid_size2 - 1; i++) { @@ -1797,7 +1796,7 @@ static void neighbor_coords_edge_get(const SubdivCCG *subdiv_ccg, r_neighbors->coords[i + 2] = coord_step_inside_from_boundary(subdiv_ccg, &grid_coord); if (grid_coord.grid_index == coord->grid_index) { - /* Prev and next along the edge for the current grid. */ + /* Previous and next along the edge for the current grid. */ r_neighbors->coords[0] = boundary_coords[prev_point_index]; r_neighbors->coords[1] = boundary_coords[next_point_index]; } @@ -1972,7 +1971,7 @@ const int *BKE_subdiv_ccg_start_face_grid_index_ensure(SubdivCCG *subdiv_ccg) const int num_coarse_faces = topology_refiner->getNumFaces(topology_refiner); subdiv_ccg->cache_.start_face_grid_index = MEM_malloc_arrayN( - sizeof(int), num_coarse_faces, "start_face_grid_index"); + num_coarse_faces, sizeof(int), "start_face_grid_index"); int start_grid_index = 0; for (int face_index = 0; face_index < num_coarse_faces; face_index++) { diff --git a/source/blender/blenkernel/intern/subdiv_deform.c b/source/blender/blenkernel/intern/subdiv_deform.c index 2c900fbd600..7a2d639e4e5 100644 --- a/source/blender/blenkernel/intern/subdiv_deform.c +++ b/source/blender/blenkernel/intern/subdiv_deform.c @@ -69,7 +69,7 @@ static void subdiv_mesh_prepare_accumulator(SubdivDeformContext *ctx, int num_ve return; } ctx->accumulated_counters = MEM_calloc_arrayN( - sizeof(*ctx->accumulated_counters), num_vertices, "subdiv accumulated counters"); + num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters"); } static void subdiv_mesh_context_free(SubdivDeformContext *ctx) diff --git a/source/blender/blenkernel/intern/subdiv_mesh.c b/source/blender/blenkernel/intern/subdiv_mesh.c index e9cd0b70019..29e7d0a1a3c 100644 --- a/source/blender/blenkernel/intern/subdiv_mesh.c +++ b/source/blender/blenkernel/intern/subdiv_mesh.c @@ -50,7 +50,7 @@ typedef struct SubdivMeshContext { const Mesh *coarse_mesh; Subdiv *subdiv; Mesh *subdiv_mesh; - /* Cached custom data arrays for fastter access. */ + /* Cached custom data arrays for faster access. */ int *vert_origindex; int *edge_origindex; int *loop_origindex; @@ -108,9 +108,9 @@ static void subdiv_mesh_prepare_accumulator(SubdivMeshContext *ctx, int num_vert /* TODO(sergey): Technically, this is overallocating, we don't need memory * for an inner subdivision vertices. */ ctx->accumulated_normals = MEM_calloc_arrayN( - sizeof(*ctx->accumulated_normals), num_vertices, "subdiv accumulated normals"); + num_vertices, sizeof(*ctx->accumulated_normals), "subdiv accumulated normals"); ctx->accumulated_counters = MEM_calloc_arrayN( - sizeof(*ctx->accumulated_counters), num_vertices, "subdiv accumulated counters"); + num_vertices, sizeof(*ctx->accumulated_counters), "subdiv accumulated counters"); } static void subdiv_mesh_context_free(SubdivMeshContext *ctx) diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index a1b45c2ac7d..0c58c8e8a5a 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -912,141 +912,6 @@ static void ccgDM_getFinalVertNo(DerivedMesh *dm, int vertNum, float r_no[3]) normal_short_to_float_v3(r_no, mvert.no); } -static void ccgDM_getFinalEdge(DerivedMesh *dm, int edgeNum, MEdge *med) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int i; - - memset(med, 0, sizeof(*med)); - - if (edgeNum < ccgdm->edgeMap[0].startEdge) { - /* this edge comes from face data */ - int lastface = ccgSubSurf_getNumFaces(ss) - 1; - CCGFace *f; - int x, y, grid /*, numVerts*/; - int offset; - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int gridSideEdges; - int gridInternalEdges; - - i = 0; - while (i < lastface && edgeNum >= ccgdm->faceMap[i + 1].startEdge) { - i++; - } - - f = ccgdm->faceMap[i].face; - /* numVerts = ccgSubSurf_getFaceNumVerts(f); */ /*UNUSED*/ - - gridSideEdges = gridSize - 1; - gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2; - - offset = edgeNum - ccgdm->faceMap[i].startEdge; - grid = offset / (gridSideEdges + gridInternalEdges); - offset %= (gridSideEdges + gridInternalEdges); - - if (offset < gridSideEdges) { - x = offset; - med->v1 = getFaceIndex(ss, f, grid, x, 0, edgeSize, gridSize); - med->v2 = getFaceIndex(ss, f, grid, x + 1, 0, edgeSize, gridSize); - } - else { - offset -= gridSideEdges; - x = (offset / 2) / gridSideEdges + 1; - y = (offset / 2) % gridSideEdges; - if (offset % 2 == 0) { - med->v1 = getFaceIndex(ss, f, grid, x, y, edgeSize, gridSize); - med->v2 = getFaceIndex(ss, f, grid, x, y + 1, edgeSize, gridSize); - } - else { - med->v1 = getFaceIndex(ss, f, grid, y, x, edgeSize, gridSize); - med->v2 = getFaceIndex(ss, f, grid, y + 1, x, edgeSize, gridSize); - } - } - } - else { - /* this vert comes from edge data */ - CCGEdge *e; - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int x; - short *edgeFlag; - unsigned int flags = 0; - - i = (edgeNum - ccgdm->edgeMap[0].startEdge) / (edgeSize - 1); - - e = ccgdm->edgeMap[i].edge; - - if (!ccgSubSurf_getEdgeNumFaces(e)) { - flags |= ME_LOOSEEDGE; - } - - x = edgeNum - ccgdm->edgeMap[i].startEdge; - - med->v1 = getEdgeIndex(ss, e, x, edgeSize); - med->v2 = getEdgeIndex(ss, e, x + 1, edgeSize); - - edgeFlag = (ccgdm->edgeFlags) ? &ccgdm->edgeFlags[i] : NULL; - if (edgeFlag) { - flags |= (*edgeFlag & (ME_SEAM | ME_SHARP)) | ME_EDGEDRAW | ME_EDGERENDER; - } - else { - flags |= ME_EDGEDRAW | ME_EDGERENDER; - } - - med->flag = flags; - } -} - -static void ccgDM_getFinalFace(DerivedMesh *dm, int faceNum, MFace *mf) -{ - CCGDerivedMesh *ccgdm = (CCGDerivedMesh *)dm; - CCGSubSurf *ss = ccgdm->ss; - int gridSize = ccgSubSurf_getGridSize(ss); - int edgeSize = ccgSubSurf_getEdgeSize(ss); - int gridSideEdges = gridSize - 1; - int gridFaces = gridSideEdges * gridSideEdges; - int i; - CCGFace *f; - // int numVerts; - int offset; - int grid; - int x, y; - // int lastface = ccgSubSurf_getNumFaces(ss) - 1; /* UNUSED */ - DMFlagMat *faceFlags = ccgdm->faceFlags; - - memset(mf, 0, sizeof(*mf)); - if (faceNum >= ccgdm->dm.numTessFaceData) { - return; - } - - i = ccgdm->reverseFaceMap[faceNum]; - - f = ccgdm->faceMap[i].face; - // numVerts = ccgSubSurf_getFaceNumVerts(f); /* UNUSED */ - - offset = faceNum - ccgdm->faceMap[i].startFace; - grid = offset / gridFaces; - offset %= gridFaces; - y = offset / gridSideEdges; - x = offset % gridSideEdges; - - mf->v1 = getFaceIndex(ss, f, grid, x + 0, y + 0, edgeSize, gridSize); - mf->v2 = getFaceIndex(ss, f, grid, x + 0, y + 1, edgeSize, gridSize); - mf->v3 = getFaceIndex(ss, f, grid, x + 1, y + 1, edgeSize, gridSize); - mf->v4 = getFaceIndex(ss, f, grid, x + 1, y + 0, edgeSize, gridSize); - - if (faceFlags) { - mf->flag = faceFlags[i].flag; - mf->mat_nr = faceFlags[i].mat_nr; - } - else { - mf->flag = ME_SMOOTH; - } - - mf->edcode = 0; -} - /* Translate GridHidden into the ME_HIDE flag for MVerts. Assumes * vertices are in the order output by ccgDM_copyFinalVertArray. */ void subsurf_copy_grid_hidden(DerivedMesh *dm, @@ -1920,10 +1785,6 @@ static void set_default_ccgdm_callbacks(CCGDerivedMesh *ccgdm) ccgdm->dm.getNumPolys = ccgDM_getNumPolys; ccgdm->dm.getNumTessFaces = ccgDM_getNumTessFaces; - ccgdm->dm.getVert = ccgDM_getFinalVert; - ccgdm->dm.getEdge = ccgDM_getFinalEdge; - ccgdm->dm.getTessFace = ccgDM_getFinalFace; - ccgdm->dm.getVertCo = ccgDM_getFinalVertCo; ccgdm->dm.getVertNo = ccgDM_getFinalVertNo; @@ -2032,9 +1893,7 @@ static void set_ccgdm_all_geometry(CCGDerivedMesh *ccgdm, gridSideEdges = gridSize - 1; gridInternalEdges = (gridSideEdges - 1) * gridSideEdges * 2; - /* mvert = dm->getVertArray(dm); */ /* UNUSED */ medge = dm->getEdgeArray(dm); - /* mface = dm->getTessFaceArray(dm); */ /* UNUSED */ mpoly = CustomData_get_layer(&dm->polyData, CD_MPOLY); base_polyOrigIndex = CustomData_get_layer(&dm->polyData, CD_ORIGINDEX); diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index f67bf68010d..0cb2218e7e0 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -241,7 +241,7 @@ IDTypeInfo IDType_ID_TXT = { .name = "Text", .name_plural = "texts", .translation_context = BLT_I18NCONTEXT_ID_TEXT, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = text_init_data, .copy_data = text_copy_data, @@ -480,7 +480,7 @@ Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const BLI_stat_t st; BLI_strncpy(filepath_abs, file, FILE_MAX); - if (relpath) { /* can be NULL (bg mode) */ + if (relpath) { /* Can be NULL (background mode). */ BLI_path_abs(filepath_abs, relpath); } diff --git a/source/blender/blenkernel/intern/texture.c b/source/blender/blenkernel/intern/texture.c index 228e6fffdf7..a8c8eaa6a1c 100644 --- a/source/blender/blenkernel/intern/texture.c +++ b/source/blender/blenkernel/intern/texture.c @@ -142,9 +142,10 @@ static void texture_foreach_id(ID *id, LibraryForeachIDData *data) Tex *texture = (Tex *)id; if (texture->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&texture->nodetree)); } - BKE_LIB_FOREACHID_PROCESS(data, texture->ima, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, texture->ima, IDWALK_CB_USER); } static void texture_blend_write(BlendWriter *writer, ID *id, const void *id_address) @@ -184,7 +185,6 @@ static void texture_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &tex->preview); BKE_previewimg_blend_read(reader, tex->preview); - tex->iuser.ok = 1; tex->iuser.scene = NULL; } @@ -210,7 +210,7 @@ IDTypeInfo IDType_ID_TE = { .name = "Texture", .name_plural = "textures", .translation_context = BLT_I18NCONTEXT_ID_TEXTURE, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = texture_init_data, .copy_data = texture_copy_data, @@ -233,8 +233,8 @@ IDTypeInfo IDType_ID_TE = { /* Utils for all IDs using those texture slots. */ void BKE_texture_mtex_foreach_id(LibraryForeachIDData *data, MTex *mtex) { - BKE_LIB_FOREACHID_PROCESS(data, mtex->object, IDWALK_CB_NOP); - BKE_LIB_FOREACHID_PROCESS(data, mtex->tex, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->object, IDWALK_CB_NOP); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, mtex->tex, IDWALK_CB_USER); } /* ****************** Mapping ******************* */ diff --git a/source/blender/blenkernel/intern/tracking.c b/source/blender/blenkernel/intern/tracking.c index 068d048fd08..3cdb8e927a6 100644 --- a/source/blender/blenkernel/intern/tracking.c +++ b/source/blender/blenkernel/intern/tracking.c @@ -3014,6 +3014,61 @@ static int channels_average_error_sort(const void *a, const void *b) return 0; } +static int compare_firstlast_putting_undefined_first( + bool inverse, bool a_markerless, int a_value, bool b_markerless, int b_value) +{ + if (a_markerless && b_markerless) { + /* Neither channel has not-disabled markers, return whatever. */ + return 0; + } + if (a_markerless) { + /* Put the markerless channel first. */ + return 0; + } + if (b_markerless) { + /* Put the markerless channel first. */ + return 1; + } + + /* Both channels have markers. */ + + if (inverse) { + if (a_value < b_value) { + return 1; + } + return 0; + } + + if (a_value > b_value) { + return 1; + } + return 0; +} + +static int channels_start_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(false, + channel_a->tot_segment == 0, + channel_a->first_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->first_not_disabled_marker_framenr); +} + +static int channels_end_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(false, + channel_a->tot_segment == 0, + channel_a->last_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->last_not_disabled_marker_framenr); +} + static int channels_alpha_inverse_sort(const void *a, const void *b) { if (channels_alpha_sort(a, b)) { @@ -3053,22 +3108,51 @@ static int channels_average_error_inverse_sort(const void *a, const void *b) return 0; } +static int channels_start_inverse_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(true, + channel_a->tot_segment == 0, + channel_a->first_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->first_not_disabled_marker_framenr); +} + +static int channels_end_inverse_sort(const void *a, const void *b) +{ + const MovieTrackingDopesheetChannel *channel_a = a; + const MovieTrackingDopesheetChannel *channel_b = b; + + return compare_firstlast_putting_undefined_first(true, + channel_a->tot_segment == 0, + channel_a->last_not_disabled_marker_framenr, + channel_b->tot_segment == 0, + channel_b->last_not_disabled_marker_framenr); +} + /* Calculate frames segments at which track is tracked continuously. */ static void tracking_dopesheet_channels_segments_calc(MovieTrackingDopesheetChannel *channel) { MovieTrackingTrack *track = channel->track; int i, segment; + bool first_not_disabled_marker_framenr_set; channel->tot_segment = 0; channel->max_segment = 0; channel->total_frames = 0; + channel->first_not_disabled_marker_framenr = 0; + channel->last_not_disabled_marker_framenr = 0; + /* TODO(sergey): looks a bit code-duplicated, need to look into * logic de-duplication here. */ /* count */ i = 0; + first_not_disabled_marker_framenr_set = false; while (i < track->markersnr) { MovieTrackingMarker *marker = &track->markers[i]; @@ -3086,6 +3170,12 @@ static void tracking_dopesheet_channels_segments_calc(MovieTrackingDopesheetChan break; } + if (!first_not_disabled_marker_framenr_set) { + channel->first_not_disabled_marker_framenr = marker->framenr; + first_not_disabled_marker_framenr_set = true; + } + channel->last_not_disabled_marker_framenr = marker->framenr; + prev_fra = marker->framenr; len++; i++; @@ -3203,6 +3293,12 @@ static void tracking_dopesheet_channels_sort(MovieTracking *tracking, else if (sort_method == TRACKING_DOPE_SORT_AVERAGE_ERROR) { BLI_listbase_sort(&dopesheet->channels, channels_average_error_inverse_sort); } + else if (sort_method == TRACKING_DOPE_SORT_START) { + BLI_listbase_sort(&dopesheet->channels, channels_start_inverse_sort); + } + else if (sort_method == TRACKING_DOPE_SORT_END) { + BLI_listbase_sort(&dopesheet->channels, channels_end_inverse_sort); + } } else { if (sort_method == TRACKING_DOPE_SORT_NAME) { @@ -3217,6 +3313,12 @@ static void tracking_dopesheet_channels_sort(MovieTracking *tracking, else if (sort_method == TRACKING_DOPE_SORT_AVERAGE_ERROR) { BLI_listbase_sort(&dopesheet->channels, channels_average_error_sort); } + else if (sort_method == TRACKING_DOPE_SORT_START) { + BLI_listbase_sort(&dopesheet->channels, channels_start_sort); + } + else if (sort_method == TRACKING_DOPE_SORT_END) { + BLI_listbase_sort(&dopesheet->channels, channels_end_sort); + } } } diff --git a/source/blender/blenkernel/intern/tracking_auto.c b/source/blender/blenkernel/intern/tracking_auto.c index 92ff0911cf3..e2a29d7858d 100644 --- a/source/blender/blenkernel/intern/tracking_auto.c +++ b/source/blender/blenkernel/intern/tracking_auto.c @@ -475,7 +475,7 @@ static void autotrack_context_init_autotrack(AutoTrackContext *context) /* Allocate memory for all the markers. */ libmv_Marker *libmv_markers = MEM_malloc_arrayN( - sizeof(libmv_Marker), num_trackable_markers, "libmv markers array"); + num_trackable_markers, sizeof(libmv_Marker), "libmv markers array"); /* Fill in markers array. */ int num_filled_libmv_markers = 0; @@ -516,7 +516,7 @@ static void autotrack_context_init_markers(AutoTrackContext *context) /* Allocate required memory. */ context->autotrack_markers = MEM_calloc_arrayN( - sizeof(AutoTrackMarker), context->num_autotrack_markers, "auto track options"); + context->num_autotrack_markers, sizeof(AutoTrackMarker), "auto track options"); /* Fill in all the markers. */ int autotrack_marker_index = 0; diff --git a/source/blender/blenkernel/intern/undo_system.c b/source/blender/blenkernel/intern/undo_system.c index db5184edfd2..26d37489e35 100644 --- a/source/blender/blenkernel/intern/undo_system.c +++ b/source/blender/blenkernel/intern/undo_system.c @@ -346,7 +346,7 @@ static bool undosys_stack_push_main(UndoStack *ustack, const char *name, struct CLOG_INFO(&LOG, 1, "'%s'", name); bContext *C_temp = CTX_create(); CTX_data_main_set(C_temp, bmain); - UndoPushReturn ret = BKE_undosys_step_push_with_type( + eUndoPushReturn ret = BKE_undosys_step_push_with_type( ustack, C_temp, name, BKE_UNDOSYS_TYPE_MEMFILE); CTX_free(C_temp); return (ret & UNDO_PUSH_RET_SUCCESS); @@ -500,17 +500,17 @@ UndoStep *BKE_undosys_step_push_init(UndoStack *ustack, bContext *C, const char /** * \param C: Can be NULL from some callers if their encoding function doesn't need it */ -UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, - bContext *C, - const char *name, - const UndoType *ut) +eUndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, + bContext *C, + const char *name, + const UndoType *ut) { BLI_assert((ut->flags & UNDOTYPE_FLAG_NEED_CONTEXT_FOR_ENCODE) == 0 || C != NULL); UNDO_NESTED_ASSERT(false); undosys_stack_validate(ustack, false); bool is_not_empty = ustack->step_active != NULL; - UndoPushReturn retval = UNDO_PUSH_RET_FAILURE; + eUndoPushReturn retval = UNDO_PUSH_RET_FAILURE; /* Might not be final place for this to be called - probably only want to call it from some * undo handlers, not all of them? */ @@ -602,7 +602,7 @@ UndoPushReturn BKE_undosys_step_push_with_type(UndoStack *ustack, return (retval | UNDO_PUSH_RET_SUCCESS); } -UndoPushReturn BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name) +eUndoPushReturn BKE_undosys_step_push(UndoStack *ustack, bContext *C, const char *name) { UNDO_NESTED_ASSERT(false); const UndoType *ut = ustack->step_init ? ustack->step_init->type : diff --git a/source/blender/blenkernel/intern/font.c b/source/blender/blenkernel/intern/vfont.c similarity index 97% rename from source/blender/blenkernel/intern/font.c rename to source/blender/blenkernel/intern/vfont.c index 842a701f525..43c8a59baad 100644 --- a/source/blender/blenkernel/intern/font.c +++ b/source/blender/blenkernel/intern/vfont.c @@ -34,12 +34,12 @@ #include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_math_base_safe.h" #include "BLI_path_util.h" #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_threads.h" #include "BLI_utildefines.h" -#include "BLI_vfontdata.h" #include "BLT_translation.h" @@ -50,12 +50,13 @@ #include "BKE_anim_path.h" #include "BKE_curve.h" -#include "BKE_font.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_packedFile.h" +#include "BKE_vfont.h" +#include "BKE_vfontdata.h" #include "BLO_read_write.h" @@ -76,7 +77,7 @@ static void vfont_init_data(ID *id) if (pf) { VFontData *vfd; - vfd = BLI_vfontdata_from_freetypefont(pf); + vfd = BKE_vfontdata_from_freetypefont(pf); if (vfd) { vfont->data = vfd; @@ -106,7 +107,7 @@ static void vfont_copy_data(Main *UNUSED(bmain), } if (vfont_dst->data) { - vfont_dst->data = BLI_vfontdata_copy(vfont_dst->data, flag_subdata); + vfont_dst->data = BKE_vfontdata_copy(vfont_dst->data, flag_subdata); } } @@ -160,7 +161,7 @@ IDTypeInfo IDType_ID_VF = { .name = "Font", .name_plural = "fonts", .translation_context = BLT_I18NCONTEXT_ID_VFONT, - .flags = IDTYPE_FLAGS_NO_ANIMDATA, + .flags = IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = vfont_init_data, .copy_data = vfont_copy_data, @@ -299,7 +300,7 @@ static VFontData *vfont_get_data(VFont *vfont) } if (pf) { - vfont->data = BLI_vfontdata_from_freetypefont(pf); + vfont->data = BKE_vfontdata_from_freetypefont(pf); if (pf != vfont->packedfile) { BKE_packedfile_free(pf); } @@ -334,19 +335,19 @@ VFont *BKE_vfont_load(Main *bmain, const char *filepath) if (pf) { VFontData *vfd; - vfd = BLI_vfontdata_from_freetypefont(pf); + vfd = BKE_vfontdata_from_freetypefont(pf); if (vfd) { /* If there's a font name, use it for the ID name. */ vfont = BKE_libblock_alloc(bmain, ID_VF, vfd->name[0] ? vfd->name : filename, 0); vfont->data = vfd; BLI_strncpy(vfont->filepath, filepath, sizeof(vfont->filepath)); - /* if autopack is on store the packedfile in de font structure */ + /* if auto-pack is on store the packed-file in de font structure */ if (!is_builtin && (G.fileflags & G_FILE_AUTOPACK)) { vfont->packedfile = pf; } - /* Do not add FO_BUILTIN_NAME to temporary listbase */ + /* Do not add #FO_BUILTIN_NAME to temporary list-base. */ if (!STREQ(filename, FO_BUILTIN_NAME)) { vfont->temp_pf = BKE_packedfile_new(NULL, filepath, BKE_main_blendfile_path(bmain)); } @@ -490,15 +491,15 @@ static void build_underline(Curve *cu, mul_v2_fl(bp[3].vec, font_size); } -static void buildchar(Curve *cu, - ListBase *nubase, - unsigned int character, - CharInfo *info, - float ofsx, - float ofsy, - float rot, - int charidx, - const float fsize) +void BKE_vfont_build_char(Curve *cu, + ListBase *nubase, + unsigned int character, + CharInfo *info, + float ofsx, + float ofsy, + float rot, + int charidx, + const float fsize) { VFontData *vfd = vfont_get_data(which_vfont(cu, info)); if (!vfd) { @@ -693,7 +694,7 @@ struct TempLineInfo { float x_min; /* left margin */ float x_max; /* right margin */ int char_nr; /* number of characters */ - int wspace_nr; /* number of whitespaces of line */ + int wspace_nr; /* number of white-spaces of line */ }; /* -------------------------------------------------------------------- */ @@ -794,15 +795,15 @@ static bool vfont_to_curve(Object *ob, bool ok = false; const float font_size = cu->fsize * iter_data->scale_to_fit; const bool word_wrap = iter_data->word_wrap; - const float xof_scale = cu->xof / font_size; - const float yof_scale = cu->yof / font_size; + const float xof_scale = safe_divide(cu->xof, font_size); + const float yof_scale = safe_divide(cu->yof, font_size); int last_line = -1; /* Length of the text disregarding \n breaks. */ float current_line_length = 0.0f; float longest_line_length = 0.0f; /* Text at the beginning of the last used text-box (use for y-axis alignment). - * We overallocate by one to simplify logic of getting last char. */ + * We over-allocate by one to simplify logic of getting last char. */ int *i_textbox_array = MEM_callocN(sizeof(*i_textbox_array) * (cu->totbox + 1), "TextBox initial char index"); @@ -889,7 +890,7 @@ static bool vfont_to_curve(Object *ob, linedist = cu->linedist; curbox = 0; - textbox_scale(&tb_scale, &cu->tb[curbox], 1.0f / font_size); + textbox_scale(&tb_scale, &cu->tb[curbox], safe_divide(1.0f, font_size)); use_textbox = (tb_scale.w != 0.0f); xof = MARGIN_X_MIN; @@ -953,7 +954,7 @@ static bool vfont_to_curve(Object *ob, * happen often once all the chars are load. */ if ((che = find_vfont_char(vfd, ascii)) == NULL) { - che = BLI_vfontchar_from_freetypefont(vfont, ascii); + che = BKE_vfontdata_char_from_freetypefont(vfont, ascii); } BLI_rw_mutex_unlock(&vfont_rwlock); } @@ -1135,7 +1136,7 @@ static bool vfont_to_curve(Object *ob, } } - /* linedata is now: width of line */ + /* Line-data is now: width of line. */ if (cu->spacemode != CU_ALIGN_X_LEFT) { ct = chartransdata; @@ -1499,7 +1500,7 @@ static bool vfont_to_curve(Object *ob, chartransdata = NULL; } else if (mode == FO_EDIT) { - /* make nurbdata */ + /* Make NURBS-data. */ BKE_nurbList_free(r_nubase); ct = chartransdata; @@ -1525,7 +1526,7 @@ static bool vfont_to_curve(Object *ob, } /* We do not want to see any character for \n or \r */ if (cha != '\n') { - buildchar(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size); + BKE_vfont_build_char(cu, r_nubase, cha, info, ct->xof, ct->yof, ct->rot, i, font_size); } if ((info->flag & CU_CHINFO_UNDERLINE) && (cha != '\n')) { diff --git a/source/blender/blenlib/intern/freetypefont.c b/source/blender/blenkernel/intern/vfontdata_freetype.c similarity index 96% rename from source/blender/blenlib/intern/freetypefont.c rename to source/blender/blenkernel/intern/vfontdata_freetype.c index 34de8fe7f6d..bd58d156d06 100644 --- a/source/blender/blenlib/intern/freetypefont.c +++ b/source/blender/blenkernel/intern/vfontdata_freetype.c @@ -23,7 +23,7 @@ */ /** \file - * \ingroup bli + * \ingroup bke */ #include @@ -42,7 +42,9 @@ #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_utildefines.h" -#include "BLI_vfontdata.h" + +#include "BKE_curve.h" +#include "BKE_vfontdata.h" #include "DNA_curve_types.h" #include "DNA_packedFile_types.h" @@ -98,7 +100,7 @@ static VChar *freetypechar_to_vchar(FT_Face face, FT_ULong charcode, VFontData * /* Start converting the FT data */ onpoints = (int *)MEM_callocN((ftoutline.n_contours) * sizeof(int), "onpoints"); - /* get number of on-curve points for beziertriples (including conic virtual on-points) */ + /* Get number of on-curve points for bezier-triples (including conic virtual on-points). */ for (j = 0, contour_prev = -1; j < ftoutline.n_contours; j++) { const int n = ftoutline.contours[j] - contour_prev; contour_prev = ftoutline.contours[j]; @@ -402,7 +404,7 @@ static bool check_freetypefont(PackedFile *pf) * \retval A new VFontData structure, or NULL * if unable to load. */ -VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) +VFontData *BKE_vfontdata_from_freetypefont(PackedFile *pf) { VFontData *vfd = NULL; @@ -425,10 +427,10 @@ VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf) static void *vfontdata_copy_characters_value_cb(const void *src) { - return BLI_vfontchar_copy(src, 0); + return BKE_vfontdata_char_copy(src); } -VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag)) +VFontData *BKE_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag)) { VFontData *vfont_dst = MEM_dupallocN(vfont_src); @@ -440,7 +442,7 @@ VFontData *BLI_vfontdata_copy(const VFontData *vfont_src, const int UNUSED(flag) return vfont_dst; } -VChar *BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character) +VChar *BKE_vfontdata_char_from_freetypefont(VFont *vfont, unsigned long character) { VChar *che = NULL; @@ -464,12 +466,7 @@ VChar *BLI_vfontchar_from_freetypefont(VFont *vfont, unsigned long character) return che; } -/* Yeah, this is very bad... But why is this in BLI in the first place, since it uses Nurb data? - * Anyway, do not feel like duplicating whole Nurb copy code here, - * so unless someone has a better idea... */ -#include "../../blenkernel/BKE_curve.h" - -VChar *BLI_vfontchar_copy(const VChar *vchar_src, const int UNUSED(flag)) +VChar *BKE_vfontdata_char_copy(const VChar *vchar_src) { VChar *vchar_dst = MEM_dupallocN(vchar_src); diff --git a/source/blender/blenkernel/intern/volume.cc b/source/blender/blenkernel/intern/volume.cc index 69452d6896f..a72b5268e1d 100644 --- a/source/blender/blenkernel/intern/volume.cc +++ b/source/blender/blenkernel/intern/volume.cc @@ -36,6 +36,7 @@ #include "BLI_math.h" #include "BLI_path_util.h" #include "BLI_string.h" +#include "BLI_string_ref.hh" #include "BLI_task.hh" #include "BLI_utildefines.h" @@ -71,6 +72,7 @@ static CLG_LogRef LOG = {"bke.volume"}; using blender::float3; using blender::float4x4; using blender::IndexRange; +using blender::StringRef; #ifdef WITH_OPENVDB # include @@ -556,7 +558,7 @@ static void volume_foreach_id(ID *id, LibraryForeachIDData *data) { Volume *volume = (Volume *)id; for (int i = 0; i < volume->totcol; i++) { - BKE_LIB_FOREACHID_PROCESS(data, volume->mat[i], IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, volume->mat[i], IDWALK_CB_USER); } } @@ -642,7 +644,7 @@ IDTypeInfo IDType_ID_VO = { /* name */ "Volume", /* name_plural */ "volumes", /* translation_context */ BLT_I18NCONTEXT_ID_VOLUME, - /* flags */ 0, + /* flags */ IDTYPE_FLAGS_APPEND_IS_REUSABLE, /* init_data */ volume_init_data, /* copy_data */ volume_copy_data, @@ -1451,6 +1453,21 @@ VolumeGrid *BKE_volume_grid_add(Volume *volume, const char *name, VolumeGridType #endif } +#ifdef WITH_OPENVDB +VolumeGrid *BKE_volume_grid_add_vdb(Volume &volume, + const StringRef name, + openvdb::GridBase::Ptr vdb_grid) +{ + VolumeGridVector &grids = *volume.runtime.grids; + BLI_assert(BKE_volume_grid_find_for_read(&volume, name.data()) == nullptr); + BLI_assert(BKE_volume_grid_type_openvdb(*vdb_grid) != VOLUME_GRID_UNKNOWN); + + vdb_grid->setName(name); + grids.emplace_back(vdb_grid); + return &grids.back(); +} +#endif + void BKE_volume_grid_remove(Volume *volume, VolumeGrid *grid) { #ifdef WITH_OPENVDB diff --git a/source/blender/blenkernel/intern/volume_to_mesh.cc b/source/blender/blenkernel/intern/volume_to_mesh.cc index e9d6eea4614..6e465b2fdf0 100644 --- a/source/blender/blenkernel/intern/volume_to_mesh.cc +++ b/source/blender/blenkernel/intern/volume_to_mesh.cc @@ -121,46 +121,66 @@ struct VolumeToMeshOp { } }; -static Mesh *new_mesh_from_openvdb_data(Span verts, - Span tris, - Span quads) +/** + * Convert mesh data from the format provided by OpenVDB into Blender's #Mesh data structure. + * This can be used to add mesh data from a grid into an existing mesh rather than merging multiple + * meshes later on. + */ +void fill_mesh_from_openvdb_data(const Span vdb_verts, + const Span vdb_tris, + const Span vdb_quads, + const int vert_offset, + const int poly_offset, + const int loop_offset, + MutableSpan verts, + MutableSpan polys, + MutableSpan loops) { - const int tot_loops = 3 * tris.size() + 4 * quads.size(); - const int tot_polys = tris.size() + quads.size(); - - Mesh *mesh = BKE_mesh_new_nomain(verts.size(), 0, 0, tot_loops, tot_polys); - /* Write vertices. */ - for (const int i : verts.index_range()) { - const blender::float3 co = blender::float3(verts[i].asV()); - copy_v3_v3(mesh->mvert[i].co, co); + for (const int i : vdb_verts.index_range()) { + const blender::float3 co = blender::float3(vdb_verts[i].asV()); + copy_v3_v3(verts[vert_offset + i].co, co); } /* Write triangles. */ - for (const int i : tris.index_range()) { - mesh->mpoly[i].loopstart = 3 * i; - mesh->mpoly[i].totloop = 3; + for (const int i : vdb_tris.index_range()) { + polys[poly_offset + i].loopstart = loop_offset + 3 * i; + polys[poly_offset + i].totloop = 3; for (int j = 0; j < 3; j++) { /* Reverse vertex order to get correct normals. */ - mesh->mloop[3 * i + j].v = tris[i][2 - j]; + loops[loop_offset + 3 * i + j].v = vert_offset + vdb_tris[i][2 - j]; } } /* Write quads. */ - const int poly_offset = tris.size(); - const int loop_offset = tris.size() * 3; - for (const int i : quads.index_range()) { - mesh->mpoly[poly_offset + i].loopstart = loop_offset + 4 * i; - mesh->mpoly[poly_offset + i].totloop = 4; + const int quad_offset = poly_offset + vdb_tris.size(); + const int quad_loop_offset = loop_offset + vdb_tris.size() * 3; + for (const int i : vdb_quads.index_range()) { + polys[quad_offset + i].loopstart = quad_loop_offset + 4 * i; + polys[quad_offset + i].totloop = 4; for (int j = 0; j < 4; j++) { /* Reverse vertex order to get correct normals. */ - mesh->mloop[loop_offset + 4 * i + j].v = quads[i][3 - j]; + loops[quad_loop_offset + 4 * i + j].v = vert_offset + vdb_quads[i][3 - j]; } } +} - BKE_mesh_calc_edges(mesh, false, false); - BKE_mesh_normals_tag_dirty(mesh); - return mesh; +/** + * Convert an OpenVDB volume grid to corresponding mesh data: vertex positions and quad and + * triangle indices. + */ +bke::OpenVDBMeshData volume_to_mesh_data(const openvdb::GridBase &grid, + const VolumeToMeshResolution &resolution, + const float threshold, + const float adaptivity) +{ + const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + + VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; + if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { + return {}; + } + return {std::move(to_mesh_op.verts), std::move(to_mesh_op.tris), std::move(to_mesh_op.quads)}; } Mesh *volume_to_mesh(const openvdb::GridBase &grid, @@ -168,14 +188,27 @@ Mesh *volume_to_mesh(const openvdb::GridBase &grid, const float threshold, const float adaptivity) { - const VolumeGridType grid_type = BKE_volume_grid_type_openvdb(grid); + const bke::OpenVDBMeshData mesh_data = volume_to_mesh_data( + grid, resolution, threshold, adaptivity); - VolumeToMeshOp to_mesh_op{grid, resolution, threshold, adaptivity}; - if (!BKE_volume_grid_type_operation(grid_type, to_mesh_op)) { - return nullptr; - } + const int tot_loops = 3 * mesh_data.tris.size() + 4 * mesh_data.quads.size(); + const int tot_polys = mesh_data.tris.size() + mesh_data.quads.size(); + Mesh *mesh = BKE_mesh_new_nomain(mesh_data.verts.size(), 0, 0, tot_loops, tot_polys); - return new_mesh_from_openvdb_data(to_mesh_op.verts, to_mesh_op.tris, to_mesh_op.quads); + fill_mesh_from_openvdb_data(mesh_data.verts, + mesh_data.tris, + mesh_data.quads, + 0, + 0, + 0, + {mesh->mvert, mesh->totvert}, + {mesh->mpoly, mesh->totpoly}, + {mesh->mloop, mesh->totloop}); + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + return mesh; } #endif /* WITH_OPENVDB */ diff --git a/source/blender/blenkernel/intern/workspace.c b/source/blender/blenkernel/intern/workspace.c index 3c168a6c7b2..6269cfc4349 100644 --- a/source/blender/blenkernel/intern/workspace.c +++ b/source/blender/blenkernel/intern/workspace.c @@ -82,7 +82,7 @@ static void workspace_foreach_id(ID *id, LibraryForeachIDData *data) WorkSpace *workspace = (WorkSpace *)id; LISTBASE_FOREACH (WorkSpaceLayout *, layout, &workspace->layouts) { - BKE_LIB_FOREACHID_PROCESS(data, layout->screen, IDWALK_CB_USER); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, layout->screen, IDWALK_CB_USER); } } diff --git a/source/blender/blenkernel/intern/world.c b/source/blender/blenkernel/intern/world.c index 4abe1ff0f20..2f0a282a298 100644 --- a/source/blender/blenkernel/intern/world.c +++ b/source/blender/blenkernel/intern/world.c @@ -131,7 +131,8 @@ static void world_foreach_id(ID *id, LibraryForeachIDData *data) if (world->nodetree) { /* nodetree **are owned by IDs**, treat them as mere sub-data and not real ID! */ - BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL( + data, BKE_library_foreach_ID_embedded(data, (ID **)&world->nodetree)); } } @@ -190,7 +191,7 @@ IDTypeInfo IDType_ID_WO = { .name = "World", .name_plural = "worlds", .translation_context = BLT_I18NCONTEXT_ID_WORLD, - .flags = 0, + .flags = IDTYPE_FLAGS_APPEND_IS_REUSABLE, .init_data = world_init_data, .copy_data = world_copy_data, diff --git a/source/blender/blenlib/BLI_array.hh b/source/blender/blenlib/BLI_array.hh index fc8fc615feb..32588b7450d 100644 --- a/source/blender/blenlib/BLI_array.hh +++ b/source/blender/blenlib/BLI_array.hh @@ -31,7 +31,7 @@ * * A main benefit of using Array over Vector is that it expresses the intent of the developer * better. It indicates that the size of the data structure is not expected to change. Furthermore, - * you can be more certain that an array does not overallocate. + * you can be more certain that an array does not over-allocate. * * blender::Array supports small object optimization to improve performance when the size turns out * to be small at run-time. @@ -276,6 +276,21 @@ class Array { initialized_fill_n(data_, size_, value); } + /** + * Return a reference to the first element in the array. + * This invokes undefined behavior when the array is empty. + */ + const T &first() const + { + BLI_assert(size_ > 0); + return *data_; + } + T &first() + { + BLI_assert(size_ > 0); + return *data_; + } + /** * Return a reference to the last element in the array. * This invokes undefined behavior when the array is empty. diff --git a/source/blender/blenlib/BLI_filereader.h b/source/blender/blenlib/BLI_filereader.h index 8d1fa3d1596..da223cddf40 100644 --- a/source/blender/blenlib/BLI_filereader.h +++ b/source/blender/blenlib/BLI_filereader.h @@ -59,9 +59,9 @@ typedef struct FileReader { /* Functions for opening the various types of FileReader. * They either succeed and return a valid FileReader, or fail and return NULL. * - * If a FileReader is created, it has to be cleaned up and freed by calling - * its close() function unless another FileReader has taken ownership - for example, - * Zstd and Gzip take over the base FileReader and will clean it up when their clean() is called. + * If a FileReader is created, it has to be cleaned up and freed by calling its close() + * function unless another FileReader has taken ownership - for example, `Zstd` & `Gzip` + * take over the base FileReader and will clean it up when their clean() is called. */ /* Create FileReader from raw file descriptor. */ @@ -71,9 +71,9 @@ FileReader *BLI_filereader_new_mmap(int filedes) ATTR_WARN_UNUSED_RESULT; /* Create FileReader from a region of memory. */ FileReader *BLI_filereader_new_memory(const void *data, size_t len) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Create FileReader from applying Zstd decompression on an underlying file. */ +/* Create FileReader from applying `Zstd` decompression on an underlying file. */ FileReader *BLI_filereader_new_zstd(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); -/* Create FileReader from applying Gzip decompression on an underlying file. */ +/* Create FileReader from applying `Gzip` decompression on an underlying file. */ FileReader *BLI_filereader_new_gzip(FileReader *base) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_float2.hh b/source/blender/blenlib/BLI_float2.hh index cf6e00ba938..bb4229db86e 100644 --- a/source/blender/blenlib/BLI_float2.hh +++ b/source/blender/blenlib/BLI_float2.hh @@ -115,6 +115,11 @@ struct float2 { return {a.x - b.x, a.y - b.y}; } + friend float2 operator-(const float2 &a, const float &b) + { + return {a.x - b, a.y - b}; + } + friend float2 operator*(const float2 &a, float b) { return {a.x * b, a.y * b}; @@ -137,6 +142,26 @@ struct float2 { return stream; } + static float2 safe_divide(const float2 &a, const float b) + { + return (b != 0.0f) ? a / b : float2(0.0f); + } + + static float2 floor(const float2 &a) + { + return float2(floorf(a.x), floorf(a.y)); + } + + /** + * Returns a normalized vector. The original vector is not changed. + */ + float2 normalized() const + { + float2 result; + normalize_v2_v2(result, *this); + return result; + } + static float dot(const float2 &a, const float2 &b) { return a.x * b.x + a.y * b.y; diff --git a/source/blender/blenlib/BLI_float3.hh b/source/blender/blenlib/BLI_float3.hh index 04aae375889..6ee0c4b973b 100644 --- a/source/blender/blenlib/BLI_float3.hh +++ b/source/blender/blenlib/BLI_float3.hh @@ -62,6 +62,11 @@ struct float3 { return {a.x + b.x, a.y + b.y, a.z + b.z}; } + friend float3 operator+(const float3 &a, const float &b) + { + return {a.x + b, a.y + b, a.z + b}; + } + float3 &operator+=(const float3 &b) { this->x += b.x; @@ -80,6 +85,11 @@ struct float3 { return {-a.x, -a.y, -a.z}; } + friend float3 operator-(const float3 &a, const float &b) + { + return {a.x - b, a.y - b, a.z - b}; + } + float3 &operator-=(const float3 &b) { this->x -= b.x; @@ -218,6 +228,16 @@ struct float3 { return result; } + static float3 safe_divide(const float3 &a, const float b) + { + return (b != 0.0f) ? a / b : float3(0.0f); + } + + static float3 floor(const float3 &a) + { + return float3(floorf(a.x), floorf(a.y), floorf(a.z)); + } + void invert() { x = -x; diff --git a/source/blender/blenlib/BLI_float4.hh b/source/blender/blenlib/BLI_float4.hh index b1feee3121b..5b487f6d029 100644 --- a/source/blender/blenlib/BLI_float4.hh +++ b/source/blender/blenlib/BLI_float4.hh @@ -44,6 +44,11 @@ struct float4 { return &x; } + friend float4 operator+(const float4 &a, const float &b) + { + return {a.x + b, a.y + b, a.z + b, a.w + b}; + } + operator const float *() const { return &x; @@ -58,11 +63,27 @@ struct float4 { return *this; } + friend float4 operator-(const float4 &a, const float4 &b) + { + return {a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w}; + } + + friend float4 operator-(const float4 &a, const float &b) + { + return {a.x - b, a.y - b, a.z - b, a.w - b}; + } + friend float4 operator+(const float4 &a, const float4 &b) { return {a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w}; } + friend float4 operator/(const float4 &a, float f) + { + BLI_assert(f != 0.0f); + return a * (1.0f / f); + } + float4 &operator*=(float factor) { x *= factor; @@ -81,6 +102,37 @@ struct float4 { { return b * a; } + + float length() const + { + return len_v4(*this); + } + + static float distance(const float4 &a, const float4 &b) + { + return (a - b).length(); + } + + static float4 safe_divide(const float4 &a, const float b) + { + return (b != 0.0f) ? a / b : float4(0.0f); + } + + static float4 interpolate(const float4 &a, const float4 &b, float t) + { + return a * (1 - t) + b * t; + } + + static float4 floor(const float4 &a) + { + return float4(floorf(a.x), floorf(a.y), floorf(a.z), floorf(a.w)); + } + + static float4 normalize(const float4 &a) + { + const float t = len_v4(a); + return (t != 0.0f) ? a / t : float4(0.0f); + } }; } // namespace blender diff --git a/source/blender/blenlib/BLI_float4x4.hh b/source/blender/blenlib/BLI_float4x4.hh index 347ce2caa34..b7f839f4ddf 100644 --- a/source/blender/blenlib/BLI_float4x4.hh +++ b/source/blender/blenlib/BLI_float4x4.hh @@ -45,6 +45,13 @@ struct float4x4 { return mat; } + static float4x4 from_location(const float3 location) + { + float4x4 mat = float4x4::identity(); + copy_v3_v3(mat.values[3], location); + return mat; + } + static float4x4 from_normalized_axis_data(const float3 location, const float3 forward, const float3 up) @@ -117,6 +124,11 @@ struct float4x4 { return result; } + void operator*=(const float4x4 &other) + { + mul_m4_m4_post(values, other.values); + } + /** * This also applies the translation on the vector. Use `m.ref_3x3() * v` if that is not * intended. diff --git a/source/blender/blenlib/BLI_function_ref.hh b/source/blender/blenlib/BLI_function_ref.hh index 70a064adc5d..71be7d7f029 100644 --- a/source/blender/blenlib/BLI_function_ref.hh +++ b/source/blender/blenlib/BLI_function_ref.hh @@ -110,6 +110,10 @@ template class FunctionRef { public: FunctionRef() = default; + FunctionRef(std::nullptr_t) + { + } + /** * A `FunctionRef` itself is a callable as well. However, we don't want that this * constructor is called when `Callable` is a `FunctionRef`. If we would allow this, it diff --git a/source/blender/blenlib/BLI_kdtree.h b/source/blender/blenlib/BLI_kdtree.h index 76f39dfbacb..bb61ba17d99 100644 --- a/source/blender/blenlib/BLI_kdtree.h +++ b/source/blender/blenlib/BLI_kdtree.h @@ -22,7 +22,7 @@ extern "C" { /** \file * \ingroup bli - * \brief A kd-tree for nearest neighbor search. + * \brief A KD-tree for nearest neighbor search. */ /* 1D version */ diff --git a/source/blender/blenlib/BLI_kdtree_impl.h b/source/blender/blenlib/BLI_kdtree_impl.h index 4b2a37830ae..26a22fc2ac4 100644 --- a/source/blender/blenlib/BLI_kdtree_impl.h +++ b/source/blender/blenlib/BLI_kdtree_impl.h @@ -16,7 +16,7 @@ /** \file * \ingroup bli - * \brief A kd-tree for nearest neighbor search. + * \brief A KD-tree for nearest neighbor search. */ #include "BLI_compiler_attrs.h" diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index bcda25ca533..be10b302144 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -438,9 +438,9 @@ bool isect_tri_tri_v2(const float p1[2], const float q2[2], const float r2[2]); -/* water-tight raycast (requires pre-calculation) */ +/* water-tight ray-cast (requires pre-calculation). */ struct IsectRayPrecalc { - /* Maximal dimension kz, and orthogonal dimensions. */ + /* Maximal dimension `kz`, and orthogonal dimensions. */ int kx, ky, kz; /* Shear constants. */ @@ -781,7 +781,7 @@ MINLINE void add_sh_shsh(float r[9], const float a[9], const float b[9]); MINLINE float dot_shsh(const float a[9], const float b[9]); MINLINE float eval_shv3(float r[9], const float v[3]); -MINLINE float diffuse_shv3(float r[9], const float v[3]); +MINLINE float diffuse_shv3(const float r[9], const float v[3]); MINLINE void vec_fac_to_sh(float r[9], const float v[3], const float f); MINLINE void madd_sh_shfl(float r[9], const float sh[9], const float f); diff --git a/source/blender/blenlib/BLI_math_matrix.h b/source/blender/blenlib/BLI_math_matrix.h index e38df58c1ca..241acebffa3 100644 --- a/source/blender/blenlib/BLI_math_matrix.h +++ b/source/blender/blenlib/BLI_math_matrix.h @@ -57,6 +57,7 @@ void copy_m4_m4_db(double m1[4][4], const double m2[4][4]); void copy_m3_m3d(float m1[3][3], const double m2[3][3]); /* float->double */ +void copy_m3d_m3(double m1[3][3], const float m2[3][3]); void copy_m4d_m4(double m1[4][4], const float m2[4][4]); void swap_m3m3(float m1[3][3], float m2[3][3]); @@ -291,6 +292,7 @@ float determinant_m3( float a1, float a2, float a3, float b1, float b2, float b3, float c1, float c2, float c3); float determinant_m3_array(const float m[3][3]); float determinant_m4_mat3_array(const float m[4][4]); +double determinant_m3_array_db(const double m[3][3]); float determinant_m4(const float m[4][4]); #define PSEUDOINVERSE_EPSILON 1e-8f @@ -326,13 +328,9 @@ void mat4_to_size(float size[3], const float M[4][4]); void mat4_to_size_fix_shear(float size[3], const float M[4][4]); -void translate_m3(float mat[3][3], float tx, float ty); void translate_m4(float mat[4][4], float tx, float ty, float tz); -void rotate_m3(float mat[3][3], const float angle); void rotate_m4(float mat[4][4], const char axis, const float angle); -void rescale_m3(float mat[3][3], const float scale[2]); void rescale_m4(float mat[4][4], const float scale[3]); -void transform_pivot_set_m3(float mat[3][3], const float pivot[2]); void transform_pivot_set_m4(float mat[4][4], const float pivot[3]); void mat4_to_rot(float rot[3][3], const float wmat[4][4]); @@ -343,10 +341,6 @@ void mat4_decompose(float loc[3], float quat[4], float size[3], const float wmat void mat3_polar_decompose(const float mat3[3][3], float r_U[3][3], float r_P[3][3]); -void loc_rot_size_to_mat3(float R[3][3], - const float loc[2], - const float angle, - const float size[2]); void loc_rot_size_to_mat4(float R[4][4], const float loc[3], const float rot[3][3], diff --git a/source/blender/blenlib/BLI_math_vector.h b/source/blender/blenlib/BLI_math_vector.h index 860ba14a3ed..62fd4a835ef 100644 --- a/source/blender/blenlib/BLI_math_vector.h +++ b/source/blender/blenlib/BLI_math_vector.h @@ -214,7 +214,7 @@ MINLINE void cross_v3_v3v3_db(double r[3], const double a[3], const double b[3]) MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const float v_curr[3]); -MINLINE void star_m3_v3(float rmat[3][3], float a[3]); +MINLINE void star_m3_v3(float rmat[3][3], const float a[3]); /*********************************** Length **********************************/ diff --git a/source/blender/blenlib/BLI_noise.hh b/source/blender/blenlib/BLI_noise.hh index 760ff082d06..a7af69f42a9 100644 --- a/source/blender/blenlib/BLI_noise.hh +++ b/source/blender/blenlib/BLI_noise.hh @@ -22,36 +22,77 @@ namespace blender::noise { -/* Perlin noise in the range [-1, 1]. */ +/* -------------------------------------------------------------------- */ +/** \name Hash Functions + * + * Create a randomized hash from the given inputs. Contrary to hash functions in `BLI_hash.hh` + * these functions produce better randomness but are more expensive to compute. + * \{ */ +/* Hash integers to `uint32_t`. */ +uint32_t hash(uint32_t kx); +uint32_t hash(uint32_t kx, uint32_t ky); +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz); +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw); + +/* Hash floats to `uint32_t`. */ +uint32_t hash_float(float kx); +uint32_t hash_float(float2 k); +uint32_t hash_float(float3 k); +uint32_t hash_float(float4 k); + +/* Hash integers to `float` between 0 and 1. */ +float hash_to_float(uint32_t kx); +float hash_to_float(uint32_t kx, uint32_t ky); +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz); +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw); + +/* Hash floats to `float` between 0 and 1. */ +float hash_float_to_float(float k); +float hash_float_to_float(float2 k); +float hash_float_to_float(float3 k); +float hash_float_to_float(float4 k); + +float2 hash_float_to_float2(float2 k); + +float3 hash_float_to_float3(float k); +float3 hash_float_to_float3(float2 k); +float3 hash_float_to_float3(float3 k); +float3 hash_float_to_float3(float4 k); + +float4 hash_float_to_float4(float4 k); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Perlin Noise + * \{ */ + +/* Perlin noise in the range [-1, 1]. */ float perlin_signed(float position); float perlin_signed(float2 position); float perlin_signed(float3 position); float perlin_signed(float4 position); /* Perlin noise in the range [0, 1]. */ - float perlin(float position); float perlin(float2 position); float perlin(float3 position); float perlin(float4 position); /* Fractal perlin noise in the range [0, 1]. */ - float perlin_fractal(float position, float octaves, float roughness); float perlin_fractal(float2 position, float octaves, float roughness); float perlin_fractal(float3 position, float octaves, float roughness); float perlin_fractal(float4 position, float octaves, float roughness); /* Positive distorted fractal perlin noise. */ - float perlin_fractal_distorted(float position, float octaves, float roughness, float distortion); float perlin_fractal_distorted(float2 position, float octaves, float roughness, float distortion); float perlin_fractal_distorted(float3 position, float octaves, float roughness, float distortion); float perlin_fractal_distorted(float4 position, float octaves, float roughness, float distortion); /* Positive distorted fractal perlin noise that outputs a float3. */ - float3 perlin_float3_fractal_distorted(float position, float octaves, float roughness, @@ -69,4 +110,199 @@ float3 perlin_float3_fractal_distorted(float4 position, float roughness, float distortion); +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Musgrave Multi Fractal + * \{ */ + +float musgrave_ridged_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_ridged_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_ridged_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_ridged_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); + +float musgrave_hybrid_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_hybrid_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_hybrid_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); +float musgrave_hybrid_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset, + const float gain); + +float musgrave_fBm(const float co, const float H, const float lacunarity, const float octaves); +float musgrave_fBm(const float2 co, const float H, const float lacunarity, const float octaves); +float musgrave_fBm(const float3 co, const float H, const float lacunarity, const float octaves); +float musgrave_fBm(const float4 co, const float H, const float lacunarity, const float octaves); + +float musgrave_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves); +float musgrave_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves); +float musgrave_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves); +float musgrave_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves); + +float musgrave_hetero_terrain(const float co, + const float H, + const float lacunarity, + const float octaves, + const float offset); +float musgrave_hetero_terrain(const float2 co, + const float H, + const float lacunarity, + const float octaves, + const float offset); +float musgrave_hetero_terrain(const float3 co, + const float H, + const float lacunarity, + const float octaves, + const float offset); +float musgrave_hetero_terrain(const float4 co, + const float H, + const float lacunarity, + const float octaves, + const float offset); + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Voronoi Noise + * \{ */ + +void voronoi_f1( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w); +void voronoi_smooth_f1(const float w, + const float smoothness, + const float randomness, + float *r_distance, + float3 *r_color, + float *r_w); +void voronoi_f2( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w); +void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float w, const float randomness, float *r_radius); + +void voronoi_f1(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_smooth_f1(const float2 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_f2(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position); +void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float2 coord, const float randomness, float *r_radius); + +void voronoi_f1(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_smooth_f1(const float3 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_f2(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position); +void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float3 coord, const float randomness, float *r_radius); + +void voronoi_f1(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_smooth_f1(const float4 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_f2(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position); +void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance); +void voronoi_n_sphere_radius(const float4 coord, const float randomness, float *r_radius); + +/** \} */ + } // namespace blender::noise diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index 2a56d11276a..e4774c58e84 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -55,6 +55,10 @@ bool BLI_path_name_at_index(const char *__restrict path, int *__restrict r_offset, int *__restrict r_len) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +/** Return true only if #containee_path is contained in #container_path. */ +bool BLI_path_contains(const char *container_path, + const char *containee_path) ATTR_WARN_UNUSED_RESULT; + const char *BLI_path_slash_rfind(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; int BLI_path_slash_ensure(char *string) ATTR_NONNULL(); void BLI_path_slash_rstrip(char *string) ATTR_NONNULL(); diff --git a/source/blender/blenlib/BLI_resource_scope.hh b/source/blender/blenlib/BLI_resource_scope.hh index edffb148477..f0d8e71478e 100644 --- a/source/blender/blenlib/BLI_resource_scope.hh +++ b/source/blender/blenlib/BLI_resource_scope.hh @@ -56,107 +56,115 @@ class ResourceScope : NonCopyable, NonMovable { Vector resources_; public: - ResourceScope() = default; + ResourceScope(); + ~ResourceScope(); - ~ResourceScope() - { - /* Free in reversed order. */ - for (int64_t i = resources_.size(); i--;) { - ResourceData &data = resources_[i]; - data.free(data.data); - } - } + template T *add(std::unique_ptr resource); + template T *add(destruct_ptr resource); + void add(void *userdata, void (*free)(void *)); - /** - * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when - * the collector is destructed. - */ - template T *add(std::unique_ptr resource) - { - BLI_assert(resource.get() != nullptr); - T *ptr = resource.release(); - if (ptr == nullptr) { - return nullptr; - } - this->add(ptr, [](void *data) { - T *typed_data = reinterpret_cast(data); - delete typed_data; - }); - return ptr; - } + template T &add_value(T &&value); + template void add_destruct_call(Func func); - /** - * Pass ownership of the resource to the ResourceScope. It will be destructed when the - * collector is destructed. - */ - template T *add(destruct_ptr resource) - { - T *ptr = resource.release(); - if (ptr == nullptr) { - return nullptr; - } - /* There is no need to keep track of such types. */ - if (std::is_trivially_destructible_v) { - return ptr; - } + template T &construct(Args &&...args); - this->add(ptr, [](void *data) { - T *typed_data = reinterpret_cast(data); - typed_data->~T(); - }); - return ptr; - } - - /** - * Pass ownership of some resource to the ResourceScope. The given free function will be - * called when the collector is destructed. - */ - void add(void *userdata, void (*free)(void *)) - { - ResourceData data; - data.data = userdata; - data.free = free; - resources_.append(data); - } - - /** - * Construct an object with the same value in the ResourceScope and return a reference to the - * new value. - */ - template T &add_value(T &&value) - { - return this->construct(std::forward(value)); - } - - /** - * The passed in function will be called when the scope is destructed. - */ - template void add_destruct_call(Func func) - { - void *buffer = allocator_.allocate(sizeof(Func), alignof(Func)); - new (buffer) Func(std::move(func)); - this->add(buffer, [](void *data) { (*(Func *)data)(); }); - } - - /** - * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory - * allocated through this allocator will be freed when the collector is destructed. - */ - LinearAllocator<> &linear_allocator() - { - return allocator_; - } - - /** - * Utility method to construct an instance of type T that will be owned by the ResourceScope. - */ - template T &construct(Args &&...args) - { - destruct_ptr value_ptr = allocator_.construct(std::forward(args)...); - T &value_ref = *value_ptr; - this->add(std::move(value_ptr)); - return value_ref; - } + LinearAllocator<> &linear_allocator(); }; +/* -------------------------------------------------------------------- */ +/** \name #ResourceScope Inline Methods + * \{ */ + +/** + * Pass ownership of the resource to the ResourceScope. It will be destructed and freed when + * the collector is destructed. + */ +template inline T *ResourceScope::add(std::unique_ptr resource) +{ + T *ptr = resource.release(); + if (ptr == nullptr) { + return nullptr; + } + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast(data); + delete typed_data; + }); + return ptr; +} + +/** + * Pass ownership of the resource to the ResourceScope. It will be destructed when the + * collector is destructed. + */ +template inline T *ResourceScope::add(destruct_ptr resource) +{ + T *ptr = resource.release(); + if (ptr == nullptr) { + return nullptr; + } + /* There is no need to keep track of such types. */ + if constexpr (std::is_trivially_destructible_v) { + return ptr; + } + + this->add(ptr, [](void *data) { + T *typed_data = reinterpret_cast(data); + typed_data->~T(); + }); + return ptr; +} + +/** + * Pass ownership of some resource to the ResourceScope. The given free function will be + * called when the collector is destructed. + */ +inline void ResourceScope::add(void *userdata, void (*free)(void *)) +{ + ResourceData data; + data.data = userdata; + data.free = free; + resources_.append(data); +} + +/** + * Construct an object with the same value in the ResourceScope and return a reference to the + * new value. + */ +template inline T &ResourceScope::add_value(T &&value) +{ + return this->construct(std::forward(value)); +} + +/** + * The passed in function will be called when the scope is destructed. + */ +template inline void ResourceScope::add_destruct_call(Func func) +{ + void *buffer = allocator_.allocate(sizeof(Func), alignof(Func)); + new (buffer) Func(std::move(func)); + this->add(buffer, [](void *data) { (*(Func *)data)(); }); +} + +/** + * Utility method to construct an instance of type T that will be owned by the ResourceScope. + */ +template inline T &ResourceScope::construct(Args &&...args) +{ + destruct_ptr value_ptr = allocator_.construct(std::forward(args)...); + T &value_ref = *value_ptr; + this->add(std::move(value_ptr)); + return value_ref; +} + +/** + * Returns a reference to a linear allocator that is owned by the ResourcesCollector. Memory + * allocated through this allocator will be freed when the collector is destructed. + */ +inline LinearAllocator<> &ResourceScope::linear_allocator() +{ + return allocator_; +} + +/** \} */ + } // namespace blender diff --git a/source/blender/blenlib/BLI_serialize.hh b/source/blender/blenlib/BLI_serialize.hh new file mode 100644 index 00000000000..051731ab801 --- /dev/null +++ b/source/blender/blenlib/BLI_serialize.hh @@ -0,0 +1,328 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup bli + * + * An abstraction layer for serialization formats. + * + * Allowing to read/write data to a serialization format like JSON. + * + * + * + * # Supported data types + * + * The abstraction layer has a limited set of data types it supports. + * There are specific classes that builds up the data structure that + * can be (de)serialized. + * + * - StringValue: for strings + * - IntValue: for integer values + * - DoubleValue: for double precision floating point numbers + * - BooleanValue: for boolean values + * - ArrayValue: An array of any supported value. + * - ObjectValue: A key value pair where keys are std::string. + * - NullValue: for null values. + * + * # Basic usage + * + * ## Serializing + * + * - Construct a structure that needs to be serialized using the `*Value` classes. + * - Construct the formatter you want to use + * - Invoke the formatter.serialize method passing an output stream and the value. + * + * The next example would format an integer value (42) as JSON the result will + * be stored inside `out`. + * + * \code{.cc} + * JsonFormatter json; + * std::stringstream out; + * IntValue test_value(42); + * json.serialize(out, test_value); + * \endcode + * + * ## Deserializing + * + * \code{.cc} + * std::stringstream is("42"); + * JsonFormatter json; + * std::unique_ptr value = json.deserialize(is); + * \endcode + * + * # Adding a new formatter + * + * To add a new formatter a new sub-class of `Formatter` must be created and the + * `serialize`/`deserialize` methods should be implemented. + * + */ + +#include + +#include "BLI_map.hh" +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +namespace blender::io::serialize { + +/** + * Enumeration containing all sub-classes of Value. It is used as for type checking. + * + * \see #Value::type() + */ +enum class eValueType { + String, + Int, + Array, + Null, + Boolean, + Double, + Object, +}; + +class Value; +class StringValue; +class ObjectValue; +template class PrimitiveValue; +using IntValue = PrimitiveValue; +using DoubleValue = PrimitiveValue; +using BooleanValue = PrimitiveValue; + +template +class ContainerValue; +/* ArrayValue stores its items as shared pointer as it shares data with a lookup table that can + * be created by calling `create_lookup`. */ +using ArrayValue = ContainerValue>, eValueType::Array>; + +/** + * Class containing a (de)serializable value. + * + * To serialize from or to a specific format the Value will be used as an intermediate container + * holding the values. Value class is abstract. There are concrete classes to for different data + * types. + * + * - `StringValue`: contains a string. + * - `IntValue`: contains an integer. + * - `ArrayValue`: contains an array of elements. Elements don't need to be the same type. + * - `NullValue`: represents nothing (null pointer or optional). + * - `BooleanValue`: contains a boolean (true/false). + * - `DoubleValue`: contains a double precision floating point number. + * - `ObjectValue`: represents an object (key value pairs where keys are strings and values can be + * of different types. + * + */ +class Value { + private: + eValueType type_; + + protected: + Value() = delete; + explicit Value(eValueType type) : type_(type) + { + } + + public: + virtual ~Value() = default; + eValueType type() const + { + return type_; + } + + /** + * Casts to a StringValue. + * Will return nullptr when it is a different type. + */ + const StringValue *as_string_value() const; + + /** + * Casts to an IntValue. + * Will return nullptr when it is a different type. + */ + const IntValue *as_int_value() const; + + /** + * Casts to a DoubleValue. + * Will return nullptr when it is a different type. + */ + const DoubleValue *as_double_value() const; + + /** + * Casts to a BooleanValue. + * Will return nullptr when it is a different type. + */ + const BooleanValue *as_boolean_value() const; + + /** + * Casts to an ArrayValue. + * Will return nullptr when it is a different type. + */ + const ArrayValue *as_array_value() const; + + /** + * Casts to an ObjectValue. + * Will return nullptr when it is a different type. + */ + const ObjectValue *as_object_value() const; +}; + +/** + * For generating value types that represent types that are typically known processor data types. + */ +template< + /** Wrapped c/cpp data type that is used to store the value. */ + typename T, + /** Value type of the class. */ + eValueType V> +class PrimitiveValue : public Value { + private: + T inner_value_{}; + + public: + explicit PrimitiveValue(const T value) : Value(V), inner_value_(value) + { + } + + const T value() const + { + return inner_value_; + } +}; + +class NullValue : public Value { + public: + NullValue() : Value(eValueType::Null) + { + } +}; + +class StringValue : public Value { + private: + std::string string_; + + public: + StringValue(const StringRef string) : Value(eValueType::String), string_(string) + { + } + + const std::string &value() const + { + return string_; + } +}; + +/** + * Template for arrays and objects. + * + * Both ArrayValue and ObjectValue store their values in an array. + */ +template< + /** The container type where the elements are stored in. */ + typename Container, + + /** ValueType representing the value (object/array). */ + eValueType V, + + /** Type of the data inside the container. */ + typename ContainerItem> +class ContainerValue : public Value { + public: + using Items = Container; + using Item = ContainerItem; + + private: + Container inner_value_; + + public: + ContainerValue() : Value(V) + { + } + + const Container &elements() const + { + return inner_value_; + } + + Container &elements() + { + return inner_value_; + } +}; + +/** + * Internal storage type for ObjectValue. + * + * The elements are stored as an key value pair. The value is a shared pointer so it can be shared + * when using `ObjectValue::create_lookup`. + */ +using ObjectElementType = std::pair>; + +/** + * Object is a key-value container where the key must be a std::string. + * Internally it is stored in a blender::Vector to ensure the order of keys. + */ +class ObjectValue : public ContainerValue, eValueType::Object> { + public: + using LookupValue = std::shared_ptr; + using Lookup = Map; + + /** + * Return a lookup map to quickly lookup by key. + * + * The lookup is owned by the caller. + */ + const Lookup create_lookup() const + { + Lookup result; + for (const Item &item : elements()) { + result.add_as(item.first, item.second); + } + return result; + } +}; + +/** + * Interface for any provided Formatter. + */ +class Formatter { + public: + virtual ~Formatter() = default; + + /** Serialize the value to the given stream. */ + virtual void serialize(std::ostream &os, const Value &value) = 0; + + /** Deserialize the stream. */ + virtual std::unique_ptr deserialize(std::istream &is) = 0; +}; + +/** + * Formatter to (de)serialize a JSON formatted stream. + */ +class JsonFormatter : public Formatter { + public: + /** + * The indentation level to use. + * Typically number of chars. Set to 0 to not use indentation. + */ + int8_t indentation_len = 0; + + public: + void serialize(std::ostream &os, const Value &value) override; + std::unique_ptr deserialize(std::istream &is) override; +}; + +} // namespace blender::io::serialize diff --git a/source/blender/blenlib/BLI_set.hh b/source/blender/blenlib/BLI_set.hh index a8ccf957f6c..2e0dfea70e9 100644 --- a/source/blender/blenlib/BLI_set.hh +++ b/source/blender/blenlib/BLI_set.hh @@ -423,6 +423,8 @@ class Set { int64_t total_slots_; int64_t current_slot_; + friend Set; + public: Iterator(const Slot *slots, int64_t total_slots, int64_t current_slot) : slots_(slots), total_slots_(total_slots), current_slot_(current_slot) @@ -467,6 +469,12 @@ class Set { { return !(a != b); } + + protected: + const Slot ¤t_slot() const + { + return slots_[current_slot_]; + } }; Iterator begin() const @@ -484,6 +492,20 @@ class Set { return Iterator(slots_.data(), slots_.size(), slots_.size()); } + /** + * Remove the key that the iterator is currently pointing at. It is valid to call this method + * while iterating over the set. However, after this method has been called, the removed element + * must not be accessed anymore. + */ + void remove(const Iterator &iterator) + { + /* The const cast is valid because this method itself is not const. */ + Slot &slot = const_cast(iterator.current_slot()); + BLI_assert(slot.is_occupied()); + slot.remove(); + removed_slots_++; + } + /** * Print common statistics like size and collision count. This is useful for debugging purposes. */ diff --git a/source/blender/blenlib/BLI_span.hh b/source/blender/blenlib/BLI_span.hh index 5adb47ba0b0..5b7981e0302 100644 --- a/source/blender/blenlib/BLI_span.hh +++ b/source/blender/blenlib/BLI_span.hh @@ -478,8 +478,8 @@ template class MutableSpan { using size_type = int64_t; protected: - T *data_; - int64_t size_; + T *data_ = nullptr; + int64_t size_ = 0; public: constexpr MutableSpan() = default; @@ -729,29 +729,4 @@ template class MutableSpan { } }; -/** - * Utilities to check that arrays have the same size in debug builds. - */ -template constexpr void assert_same_size(const T1 &v1, const T2 &v2) -{ - UNUSED_VARS_NDEBUG(v1, v2); -#ifdef DEBUG - int64_t size = v1.size(); - BLI_assert(size == v1.size()); - BLI_assert(size == v2.size()); -#endif -} - -template -constexpr void assert_same_size(const T1 &v1, const T2 &v2, const T3 &v3) -{ - UNUSED_VARS_NDEBUG(v1, v2, v3); -#ifdef DEBUG - int64_t size = v1.size(); - BLI_assert(size == v1.size()); - BLI_assert(size == v2.size()); - BLI_assert(size == v3.size()); -#endif -} - } /* namespace blender */ diff --git a/source/blender/blenlib/BLI_string_ref.hh b/source/blender/blenlib/BLI_string_ref.hh index 257a0ba143e..dc73208350f 100644 --- a/source/blender/blenlib/BLI_string_ref.hh +++ b/source/blender/blenlib/BLI_string_ref.hh @@ -64,133 +64,35 @@ class StringRefBase { const char *data_; int64_t size_; - constexpr StringRefBase(const char *data, const int64_t size) : data_(data), size_(size) - { - } + constexpr StringRefBase(const char *data, const int64_t size); public: /* Similar to string_view::npos, but signed. */ static constexpr int64_t not_found = -1; - /** - * Return the (byte-)length of the referenced string, without any null-terminator. - */ - constexpr int64_t size() const - { - return size_; - } + constexpr int64_t size() const; + constexpr bool is_empty() const; + constexpr const char *data() const; + constexpr operator Span() const; - constexpr bool is_empty() const - { - return size_ == 0; - } + operator std::string() const; + constexpr operator std::string_view() const; - /** - * Return a pointer to the start of the string. - */ - constexpr const char *data() const - { - return data_; - } + constexpr const char *begin() const; + constexpr const char *end() const; - constexpr operator Span() const - { - return Span(data_, size_); - } + constexpr IndexRange index_range() const; - /** - * Implicitly convert to std::string. This is convenient in most cases, but you have to be a bit - * careful not to convert to std::string accidentally. - */ - operator std::string() const - { - return std::string(data_, static_cast(size_)); - } + void unsafe_copy(char *dst) const; + void copy(char *dst, const int64_t dst_size) const; + template void copy(char (&dst)[N]) const; - constexpr operator std::string_view() const - { - return std::string_view(data_, static_cast(size_)); - } - - constexpr const char *begin() const - { - return data_; - } - - constexpr const char *end() const - { - return data_ + size_; - } - - constexpr IndexRange index_range() const - { - return IndexRange(size_); - } - - /** - * Copy the string into a buffer. The buffer has to be one byte larger than the size of the - * string, because the copied string will be null-terminated. Only use this when you are - * absolutely sure that the buffer is large enough. - */ - void unsafe_copy(char *dst) const - { - memcpy(dst, data_, static_cast(size_)); - dst[size_] = '\0'; - } - - /** - * Copy the string into a buffer. The copied string will be null-terminated. This invokes - * undefined behavior when dst_size is too small. (Should we define the behavior?) - */ - void copy(char *dst, const int64_t dst_size) const - { - if (size_ < dst_size) { - this->unsafe_copy(dst); - } - else { - BLI_assert(false); - dst[0] = '\0'; - } - } - - /** - * Copy the string into a char array. The copied string will be null-terminated. This invokes - * undefined behavior when dst is too small. - */ - template void copy(char (&dst)[N]) const - { - this->copy(dst, N); - } - - /** - * Returns true when the string begins with the given prefix. Otherwise false. - */ constexpr bool startswith(StringRef prefix) const; - - /** - * Returns true when the string ends with the given suffix. Otherwise false. - */ constexpr bool endswith(StringRef suffix) const; - constexpr StringRef substr(int64_t start, const int64_t size) const; - /** - * Get the first char in the string. This invokes undefined behavior when the string is empty. - */ - constexpr const char &front() const - { - BLI_assert(size_ >= 1); - return data_[0]; - } - - /** - * Get the last char in the string. This invokes undefined behavior when the string is empty. - */ - constexpr const char &back() const - { - BLI_assert(size_ >= 1); - return data_[size_ - 1]; - } + constexpr const char &front() const; + constexpr const char &back() const; /** * The behavior of those functions matches the standard library implementation of @@ -209,15 +111,7 @@ class StringRefBase { constexpr int64_t find_last_not_of(StringRef chars, int64_t pos = INT64_MAX) const; constexpr int64_t find_last_not_of(char c, int64_t pos = INT64_MAX) const; - /** - * Return a new StringRef that does not contain leading and trailing whitespace. - */ constexpr StringRef trim() const; - - /** - * Return a new StringRef that removes all the leading and trailing characters - * that occur in `characters_to_remove`. - */ constexpr StringRef trim(StringRef characters_to_remove) const; constexpr StringRef trim(char character_to_remove) const; }; @@ -228,57 +122,13 @@ class StringRefBase { class StringRefNull : public StringRefBase { public: - constexpr StringRefNull() : StringRefBase("", 0) - { - } + constexpr StringRefNull(); + constexpr StringRefNull(const char *str, const int64_t size); + StringRefNull(const char *str); + StringRefNull(const std::string &str); - /** - * Construct a StringRefNull from a null terminated c-string. The pointer must not point to - * NULL. - */ - StringRefNull(const char *str) : StringRefBase(str, static_cast(strlen(str))) - { - BLI_assert(str != nullptr); - BLI_assert(data_[size_] == '\0'); - } - - /** - * Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior - * when the given size is not the correct size of the string. - */ - constexpr StringRefNull(const char *str, const int64_t size) : StringRefBase(str, size) - { - BLI_assert(static_cast(strlen(str)) == size); - } - - /** - * Reference a std::string. Remember that when the std::string is destructed, the StringRefNull - * will point to uninitialized memory. - */ - StringRefNull(const std::string &str) : StringRefNull(str.c_str()) - { - } - - /** - * Get the char at the given index. - */ - constexpr char operator[](const int64_t index) const - { - BLI_assert(index >= 0); - /* Use '<=' instead of just '<', so that the null character can be accessed as well. */ - BLI_assert(index <= size_); - return data_[index]; - } - - /** - * Returns the beginning of a null-terminated char array. - * - * This is like ->data(), but can only be called on a StringRefNull. - */ - constexpr const char *c_str() const - { - return data_; - } + constexpr char operator[](const int64_t index) const; + constexpr const char *c_str() const; }; /** @@ -286,98 +136,449 @@ class StringRefNull : public StringRefBase { */ class StringRef : public StringRefBase { public: - constexpr StringRef() : StringRefBase(nullptr, 0) - { - } + constexpr StringRef(); + constexpr StringRef(StringRefNull other); + constexpr StringRef(const char *str); + constexpr StringRef(const char *str, const int64_t length); + constexpr StringRef(const char *begin, const char *one_after_end); + constexpr StringRef(std::string_view view); + StringRef(const std::string &str); - /** - * StringRefNull can be converted into StringRef, but not the other way around. - */ - constexpr StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) - { - } + constexpr StringRef drop_prefix(const int64_t n) const; + constexpr StringRef drop_known_prefix(StringRef prefix) const; + constexpr StringRef drop_suffix(const int64_t n) const; - /** - * Create a StringRef from a null-terminated c-string. - */ - constexpr StringRef(const char *str) - : StringRefBase(str, str ? static_cast(std::char_traits::length(str)) : 0) - { - } - - constexpr StringRef(const char *str, const int64_t length) : StringRefBase(str, length) - { - } - - /** - * Create a StringRef from a start and end pointer. This invokes undefined behavior when the - * second point points to a smaller address than the first one. - */ - constexpr StringRef(const char *begin, const char *one_after_end) - : StringRefBase(begin, static_cast(one_after_end - begin)) - { - BLI_assert(begin <= one_after_end); - } - - /** - * Reference a std::string. Remember that when the std::string is destructed, the StringRef - * will point to uninitialized memory. - */ - StringRef(const std::string &str) : StringRefBase(str.data(), static_cast(str.size())) - { - } - - constexpr StringRef(std::string_view view) - : StringRefBase(view.data(), static_cast(view.size())) - { - } - - /** - * Returns a new StringRef that does not contain the first n chars. This invokes undefined - * behavior when n is negative. - */ - constexpr StringRef drop_prefix(const int64_t n) const - { - BLI_assert(n >= 0); - const int64_t clamped_n = std::min(n, size_); - const int64_t new_size = size_ - clamped_n; - return StringRef(data_ + clamped_n, new_size); - } - - /** - * Return a new StringRef with the given prefix being skipped. This invokes undefined behavior if - * the string does not begin with the given prefix. - */ - constexpr StringRef drop_known_prefix(StringRef prefix) const - { - BLI_assert(this->startswith(prefix)); - return this->drop_prefix(prefix.size()); - } - - /** - * Return a new StringRef that does not contain the last n chars. This invokes undefined behavior - * when n is negative. - */ - constexpr StringRef drop_suffix(const int64_t n) const - { - BLI_assert(n >= 0); - const int64_t new_size = std::max(0, size_ - n); - return StringRef(data_, new_size); - } - - /** - * Get the char at the given index. - */ - constexpr char operator[](int64_t index) const - { - BLI_assert(index >= 0); - BLI_assert(index < size_); - return data_[index]; - } + constexpr char operator[](int64_t index) const; }; -/* More inline functions - ***************************************/ +/* -------------------------------------------------------------------- */ +/** \name #StringRefBase Inline Methods + * \{ */ + +constexpr StringRefBase::StringRefBase(const char *data, const int64_t size) + : data_(data), size_(size) +{ +} + +/** + * Return the (byte-)length of the referenced string, without any null-terminator. + */ +constexpr int64_t StringRefBase::size() const +{ + return size_; +} + +constexpr bool StringRefBase::is_empty() const +{ + return size_ == 0; +} + +/** + * Return a pointer to the start of the string. + */ +constexpr const char *StringRefBase::data() const +{ + return data_; +} + +constexpr StringRefBase::operator Span() const +{ + return Span(data_, size_); +} + +/** + * Implicitly convert to std::string. This is convenient in most cases, but you have to be a bit + * careful not to convert to std::string accidentally. + */ +inline StringRefBase::operator std::string() const +{ + return std::string(data_, static_cast(size_)); +} + +constexpr StringRefBase::operator std::string_view() const +{ + return std::string_view(data_, static_cast(size_)); +} + +constexpr const char *StringRefBase::begin() const +{ + return data_; +} + +constexpr const char *StringRefBase::end() const +{ + return data_ + size_; +} + +constexpr IndexRange StringRefBase::index_range() const +{ + return IndexRange(size_); +} + +/** + * Copy the string into a buffer. The buffer has to be one byte larger than the size of the + * string, because the copied string will be null-terminated. Only use this when you are + * absolutely sure that the buffer is large enough. + */ +inline void StringRefBase::unsafe_copy(char *dst) const +{ + if (size_ > 0) { + memcpy(dst, data_, static_cast(size_)); + } + dst[size_] = '\0'; +} + +/** + * Copy the string into a buffer. The copied string will be null-terminated. This invokes + * undefined behavior when dst_size is too small. (Should we define the behavior?) + */ +inline void StringRefBase::copy(char *dst, const int64_t dst_size) const +{ + if (size_ < dst_size) { + this->unsafe_copy(dst); + } + else { + BLI_assert(false); + dst[0] = '\0'; + } +} + +/** + * Copy the string into a char array. The copied string will be null-terminated. This invokes + * undefined behavior when dst is too small. + */ +template inline void StringRefBase::copy(char (&dst)[N]) const +{ + this->copy(dst, N); +} + +/** + * Return true when the string starts with the given prefix. + */ +constexpr bool StringRefBase::startswith(StringRef prefix) const +{ + if (size_ < prefix.size_) { + return false; + } + for (int64_t i = 0; i < prefix.size_; i++) { + if (data_[i] != prefix.data_[i]) { + return false; + } + } + return true; +} + +/** + * Return true when the string ends with the given suffix. + */ +constexpr bool StringRefBase::endswith(StringRef suffix) const +{ + if (size_ < suffix.size_) { + return false; + } + const int64_t offset = size_ - suffix.size_; + for (int64_t i = 0; i < suffix.size_; i++) { + if (data_[offset + i] != suffix.data_[i]) { + return false; + } + } + return true; +} + +/** + * Return a new #StringRef containing only a sub-string of the original string. This invokes + * undefined if the start or max_size is negative. + */ +constexpr StringRef StringRefBase::substr(const int64_t start, + const int64_t max_size = INT64_MAX) const +{ + BLI_assert(max_size >= 0); + BLI_assert(start >= 0); + const int64_t substr_size = std::min(max_size, size_ - start); + return StringRef(data_ + start, substr_size); +} + +/** + * Get the first char in the string. This invokes undefined behavior when the string is empty. + */ +constexpr const char &StringRefBase::front() const +{ + BLI_assert(size_ >= 1); + return data_[0]; +} + +/** + * Get the last char in the string. This invokes undefined behavior when the string is empty. + */ +constexpr const char &StringRefBase::back() const +{ + BLI_assert(size_ >= 1); + return data_[size_ - 1]; +} + +constexpr int64_t index_or_npos_to_int64(size_t index) +{ + /* The compiler will probably optimize this check away. */ + if (index == std::string_view::npos) { + return StringRef::not_found; + } + return static_cast(index); +} + +constexpr int64_t StringRefBase::find(char c, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64(std::string_view(*this).find(c, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find(StringRef str, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_first_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_first_of(char c, int64_t pos) const +{ + return this->find_first_of(StringRef(&c, 1), pos); +} + +constexpr int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_last_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_last_of(char c, int64_t pos) const +{ + return this->find_last_of(StringRef(&c, 1), pos); +} + +constexpr int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_first_not_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const +{ + return this->find_first_not_of(StringRef(&c, 1), pos); +} + +constexpr int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const +{ + BLI_assert(pos >= 0); + return index_or_npos_to_int64( + std::string_view(*this).find_last_not_of(chars, static_cast(pos))); +} + +constexpr int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const +{ + return this->find_last_not_of(StringRef(&c, 1), pos); +} + +constexpr StringRef StringRefBase::trim() const +{ + return this->trim(" \t\r\n"); +} + +/** + * Return a new StringRef that does not contain leading and trailing white-space. + */ +constexpr StringRef StringRefBase::trim(const char character_to_remove) const +{ + return this->trim(StringRef(&character_to_remove, 1)); +} + +/** + * Return a new StringRef that removes all the leading and trailing characters + * that occur in `characters_to_remove`. + */ +constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const +{ + const int64_t find_front = this->find_first_not_of(characters_to_remove); + if (find_front == not_found) { + return StringRef(); + } + const int64_t find_end = this->find_last_not_of(characters_to_remove); + /* `find_end` cannot be `not_found`, because that means the string is only + * `characters_to_remove`, in which case `find_front` would already have + * been `not_found`. */ + BLI_assert_msg(find_end != not_found, + "forward search found characters-to-not-remove, but backward search did not"); + const int64_t substr_len = find_end - find_front + 1; + return this->substr(find_front, substr_len); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #StringRefNull Inline Methods + * \{ */ + +constexpr StringRefNull::StringRefNull() : StringRefBase("", 0) +{ +} + +/** + * Construct a StringRefNull from a null terminated c-string. This invokes undefined behavior + * when the given size is not the correct size of the string. + */ +constexpr StringRefNull::StringRefNull(const char *str, const int64_t size) + : StringRefBase(str, size) +{ + BLI_assert(static_cast(strlen(str)) == size); +} + +/** + * Construct a StringRefNull from a null terminated c-string. The pointer must not point to + * NULL. + */ +inline StringRefNull::StringRefNull(const char *str) + : StringRefBase(str, static_cast(strlen(str))) +{ + BLI_assert(str != nullptr); + BLI_assert(data_[size_] == '\0'); +} + +/** + * Reference a std::string. Remember that when the std::string is destructed, the StringRefNull + * will point to uninitialized memory. + */ +inline StringRefNull::StringRefNull(const std::string &str) : StringRefNull(str.c_str()) +{ +} + +/** + * Get the char at the given index. + */ +constexpr char StringRefNull::operator[](const int64_t index) const +{ + BLI_assert(index >= 0); + /* Use '<=' instead of just '<', so that the null character can be accessed as well. */ + BLI_assert(index <= size_); + return data_[index]; +} + +/** + * Returns the beginning of a null-terminated char array. + * + * This is like ->data(), but can only be called on a StringRefNull. + */ +constexpr const char *StringRefNull::c_str() const +{ + return data_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #StringRef Inline Methods + * \{ */ + +constexpr StringRef::StringRef() : StringRefBase(nullptr, 0) +{ +} + +/** + * StringRefNull can be converted into StringRef, but not the other way around. + */ +constexpr StringRef::StringRef(StringRefNull other) : StringRefBase(other.data(), other.size()) +{ +} + +/** + * Create a StringRef from a null-terminated c-string. + */ +constexpr StringRef::StringRef(const char *str) + : StringRefBase(str, str ? static_cast(std::char_traits::length(str)) : 0) +{ +} + +constexpr StringRef::StringRef(const char *str, const int64_t length) : StringRefBase(str, length) +{ +} + +/** + * Returns a new StringRef that does not contain the first n chars. This invokes undefined + * behavior when n is negative. + */ +constexpr StringRef StringRef::drop_prefix(const int64_t n) const +{ + BLI_assert(n >= 0); + const int64_t clamped_n = std::min(n, size_); + const int64_t new_size = size_ - clamped_n; + return StringRef(data_ + clamped_n, new_size); +} + +/** + * Return a new StringRef with the given prefix being skipped. This invokes undefined behavior if + * the string does not begin with the given prefix. + */ +constexpr StringRef StringRef::drop_known_prefix(StringRef prefix) const +{ + BLI_assert(this->startswith(prefix)); + return this->drop_prefix(prefix.size()); +} + +/** + * Return a new StringRef that does not contain the last n chars. This invokes undefined behavior + * when n is negative. + */ +constexpr StringRef StringRef::drop_suffix(const int64_t n) const +{ + BLI_assert(n >= 0); + const int64_t new_size = std::max(0, size_ - n); + return StringRef(data_, new_size); +} + +/** + * Get the char at the given index. + */ +constexpr char StringRef::operator[](int64_t index) const +{ + BLI_assert(index >= 0); + BLI_assert(index < size_); + return data_[index]; +} + +/** + * Create a StringRef from a start and end pointer. This invokes undefined behavior when the + * second point points to a smaller address than the first one. + */ +constexpr StringRef::StringRef(const char *begin, const char *one_after_end) + : StringRefBase(begin, static_cast(one_after_end - begin)) +{ + BLI_assert(begin <= one_after_end); +} + +/** + * Reference a std::string. Remember that when the std::string is destructed, the StringRef + * will point to uninitialized memory. + */ +inline StringRef::StringRef(const std::string &str) + : StringRefBase(str.data(), static_cast(str.size())) +{ +} + +constexpr StringRef::StringRef(std::string_view view) + : StringRefBase(view.data(), static_cast(view.size())) +{ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator Overloads + * \{ */ inline std::ostream &operator<<(std::ostream &stream, StringRef ref) { @@ -404,182 +605,43 @@ inline std::string operator+(StringRef a, StringRef b) * not a problem when std::string_view is only used at api boundaries. To compare a StringRef and a * std::string_view, one should convert the std::string_view to StringRef (which is very cheap). * Ideally, we only use StringRef in our code to avoid this problem altogether. */ -constexpr inline bool operator==(StringRef a, StringRef b) +constexpr bool operator==(StringRef a, StringRef b) { if (a.size() != b.size()) { return false; } + if (a.data() == b.data()) { + /* This also avoids passing null to the call below, which would results in an ASAN warning. */ + return true; + } return STREQLEN(a.data(), b.data(), (size_t)a.size()); } -constexpr inline bool operator!=(StringRef a, StringRef b) +constexpr bool operator!=(StringRef a, StringRef b) { return !(a == b); } -constexpr inline bool operator<(StringRef a, StringRef b) +constexpr bool operator<(StringRef a, StringRef b) { return std::string_view(a) < std::string_view(b); } -constexpr inline bool operator>(StringRef a, StringRef b) +constexpr bool operator>(StringRef a, StringRef b) { return std::string_view(a) > std::string_view(b); } -constexpr inline bool operator<=(StringRef a, StringRef b) +constexpr bool operator<=(StringRef a, StringRef b) { return std::string_view(a) <= std::string_view(b); } -constexpr inline bool operator>=(StringRef a, StringRef b) +constexpr bool operator>=(StringRef a, StringRef b) { return std::string_view(a) >= std::string_view(b); } -/** - * Return true when the string starts with the given prefix. - */ -constexpr inline bool StringRefBase::startswith(StringRef prefix) const -{ - if (size_ < prefix.size_) { - return false; - } - for (int64_t i = 0; i < prefix.size_; i++) { - if (data_[i] != prefix.data_[i]) { - return false; - } - } - return true; -} - -/** - * Return true when the string ends with the given suffix. - */ -constexpr inline bool StringRefBase::endswith(StringRef suffix) const -{ - if (size_ < suffix.size_) { - return false; - } - const int64_t offset = size_ - suffix.size_; - for (int64_t i = 0; i < suffix.size_; i++) { - if (data_[offset + i] != suffix.data_[i]) { - return false; - } - } - return true; -} - -/** - * Return a new #StringRef containing only a sub-string of the original string. This invokes - * undefined if the start or max_size is negative. - */ -constexpr inline StringRef StringRefBase::substr(const int64_t start, - const int64_t max_size = INT64_MAX) const -{ - BLI_assert(max_size >= 0); - BLI_assert(start >= 0); - const int64_t substr_size = std::min(max_size, size_ - start); - return StringRef(data_ + start, substr_size); -} - -constexpr inline int64_t index_or_npos_to_int64(size_t index) -{ - /* The compiler will probably optimize this check away. */ - if (index == std::string_view::npos) { - return StringRef::not_found; - } - return static_cast(index); -} - -constexpr inline int64_t StringRefBase::find(char c, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64(std::string_view(*this).find(c, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find(StringRef str, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64(std::string_view(*this).find(str, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_first_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_first_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_first_of(char c, int64_t pos) const -{ - return this->find_first_of(StringRef(&c, 1), pos); -} - -constexpr inline int64_t StringRefBase::find_last_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_last_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_last_of(char c, int64_t pos) const -{ - return this->find_last_of(StringRef(&c, 1), pos); -} - -constexpr inline int64_t StringRefBase::find_first_not_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_first_not_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_first_not_of(char c, int64_t pos) const -{ - return this->find_first_not_of(StringRef(&c, 1), pos); -} - -constexpr inline int64_t StringRefBase::find_last_not_of(StringRef chars, int64_t pos) const -{ - BLI_assert(pos >= 0); - return index_or_npos_to_int64( - std::string_view(*this).find_last_not_of(chars, static_cast(pos))); -} - -constexpr inline int64_t StringRefBase::find_last_not_of(char c, int64_t pos) const -{ - return this->find_last_not_of(StringRef(&c, 1), pos); -} - -constexpr StringRef StringRefBase::trim() const -{ - return this->trim(" \t\r\n"); -} - -constexpr StringRef StringRefBase::trim(const char character_to_remove) const -{ - return this->trim(StringRef(&character_to_remove, 1)); -} - -/** - * Return a new StringRef that removes all the leading and trailing characters - * that occur in `characters_to_remove`. - */ -constexpr StringRef StringRefBase::trim(StringRef characters_to_remove) const -{ - const int64_t find_front = this->find_first_not_of(characters_to_remove); - if (find_front == not_found) { - return StringRef(); - } - const int64_t find_end = this->find_last_not_of(characters_to_remove); - /* `find_end` cannot be `not_found`, because that means the string is only - * `characters_to_remove`, in which case `find_front` would already have - * been `not_found`. */ - BLI_assert_msg(find_end != not_found, - "forward search found characters-to-not-remove, but backward search did not"); - const int64_t substr_len = find_end - find_front + 1; - return this->substr(find_front, substr_len); -} +/** \} */ } // namespace blender diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index 3b7463affc0..bf7547cc90b 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -64,7 +64,6 @@ const char *BLI_str_find_prev_char_utf8(const char *p, const char *str_start) const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1, 2); -/* wchar_t functions, copied from blenders own font.c originally */ size_t BLI_wstrlen_utf8(const wchar_t *src) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; size_t BLI_strlen_utf8_ex(const char *strc, size_t *r_len_bytes) ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/BLI_task.h b/source/blender/blenlib/BLI_task.h index 418db14e2f3..53ac5f7047b 100644 --- a/source/blender/blenlib/BLI_task.h +++ b/source/blender/blenlib/BLI_task.h @@ -62,10 +62,10 @@ int BLI_task_scheduler_num_threads(void); * be launched. */ -typedef enum TaskPriority { +typedef enum eTaskPriority { TASK_PRIORITY_LOW, TASK_PRIORITY_HIGH, -} TaskPriority; +} eTaskPriority; typedef struct TaskPool TaskPool; typedef void (*TaskRunFunction)(TaskPool *__restrict pool, void *taskdata); @@ -73,21 +73,21 @@ typedef void (*TaskFreeFunction)(TaskPool *__restrict pool, void *taskdata); /* Regular task pool that immediately starts executing tasks as soon as they * are pushed, either on the current or another thread. */ -TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create(void *userdata, eTaskPriority priority); /* Background: always run tasks in a background thread, never immediately * execute them. For running background jobs. */ -TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_background(void *userdata, eTaskPriority priority); /* Background Serial: run tasks one after the other in the background, * without parallelization between the tasks. */ -TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_background_serial(void *userdata, eTaskPriority priority); /* Suspended: don't execute tasks until work_and_wait is called. This is slower * as threads can't immediately start working. But it can be used if the data * structures the threads operate on are not fully initialized until all tasks * are created. */ -TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority); +TaskPool *BLI_task_pool_create_suspended(void *userdata, eTaskPriority priority); /* No threads: immediately executes tasks on the same thread. For debugging. */ TaskPool *BLI_task_pool_create_no_threads(void *userdata); diff --git a/source/blender/blenlib/BLI_task.hh b/source/blender/blenlib/BLI_task.hh index e2446ad143e..da7309837c8 100644 --- a/source/blender/blenlib/BLI_task.hh +++ b/source/blender/blenlib/BLI_task.hh @@ -28,10 +28,10 @@ # define NOMINMAX # define TBB_MIN_MAX_CLEANUP # endif -# include "tbb/parallel_reduce.h" # include # include # include +# include # include # ifdef WIN32 /* We cannot keep this defined, since other parts of the code deal with this on their own, leading diff --git a/source/blender/blenlib/BLI_timecode.h b/source/blender/blenlib/BLI_timecode.h index 12f4f93f700..5dfd9089598 100644 --- a/source/blender/blenlib/BLI_timecode.h +++ b/source/blender/blenlib/BLI_timecode.h @@ -20,7 +20,7 @@ #pragma once /** \file - * \ingroup BLI + * \ingroup bli */ #include "BLI_compiler_attrs.h" diff --git a/source/blender/blenlib/BLI_timer.h b/source/blender/blenlib/BLI_timer.h index 19275ff5b9a..c219b5502f3 100644 --- a/source/blender/blenlib/BLI_timer.h +++ b/source/blender/blenlib/BLI_timer.h @@ -22,7 +22,7 @@ #include "BLI_sys_types.h" /** \file - * \ingroup BLI + * \ingroup bli */ #ifdef __cplusplus diff --git a/source/blender/blenlib/BLI_uuid.h b/source/blender/blenlib/BLI_uuid.h index 15350849f67..ed0d31b625f 100644 --- a/source/blender/blenlib/BLI_uuid.h +++ b/source/blender/blenlib/BLI_uuid.h @@ -35,33 +35,74 @@ extern "C" { /** * UUID generator for random (version 4) UUIDs. See RFC4122 section 4.4. * This function is not thread-safe. */ -UUID BLI_uuid_generate_random(void); +bUUID BLI_uuid_generate_random(void); -/** Compare two UUIDs, return true iff they are equal. */ -bool BLI_uuid_equal(UUID uuid1, UUID uuid2); +/** + * Return the UUID nil value, consisting of all-zero fields. + */ +bUUID BLI_uuid_nil(void); + +/** Return true only if this is the nil UUID. */ +bool BLI_uuid_is_nil(bUUID uuid); + +/** Compare two UUIDs, return true only if they are equal. */ +bool BLI_uuid_equal(bUUID uuid1, bUUID uuid2); /** * Format UUID as string. * The buffer must be at least 37 bytes (36 bytes for the UUID + terminating 0). + * Use `UUID_STRING_LEN` from DNA_uuid_types.h if you want to use a constant for this. */ -void BLI_uuid_format(char *buffer, UUID uuid) ATTR_NONNULL(); +void BLI_uuid_format(char *buffer, bUUID uuid) ATTR_NONNULL(); /** * Parse a string as UUID. - * The string MUST be in the format xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx, + * The string MUST be in the format `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, * as produced by #BLI_uuid_format(). * * Return true if the string could be parsed, and false otherwise. In the latter case, the UUID may * have been partially updated. */ -bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) ATTR_NONNULL(); +bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) ATTR_NONNULL(); #ifdef __cplusplus } +# include # include /** Output the UUID as formatted ASCII string, see #BLI_uuid_format(). */ -std::ostream &operator<<(std::ostream &stream, UUID uuid); +std::ostream &operator<<(std::ostream &stream, bUUID uuid); + +namespace blender { + +class bUUID : public ::bUUID { + public: + /** + * Default constructor, used with `bUUID value{};`, will initialize to the nil UUID. + */ + bUUID() = default; + + /** Initialize from the bUUID DNA struct. */ + bUUID(const ::bUUID &struct_uuid); + + /** Initialize from 11 integers, 5 for the regular fields and 6 for the `node` array. */ + bUUID(std::initializer_list field_values); + + /** Initialize by parsing the string; undefined behavior when the string is invalid. */ + explicit bUUID(const std::string &string_formatted_uuid); + + uint64_t hash() const; +}; // namespace blender + +bool operator==(bUUID uuid1, bUUID uuid2); +bool operator!=(bUUID uuid1, bUUID uuid2); + +/** + * Lexicographic comparison of the UUIDs. + * Equivalent to string comparison on the formatted UUIDs. */ +bool operator<(bUUID uuid1, bUUID uuid2); + +} // namespace blender #endif diff --git a/source/blender/blenlib/BLI_winstuff.h b/source/blender/blenlib/BLI_winstuff.h index bf09b56c779..cbf1716602a 100644 --- a/source/blender/blenlib/BLI_winstuff.h +++ b/source/blender/blenlib/BLI_winstuff.h @@ -106,7 +106,7 @@ const char *dirname(char *path); /* Windows utility functions. */ bool BLI_windows_register_blend_extension(const bool background); -void BLI_windows_get_default_root_dir(char *root_dir); +void BLI_windows_get_default_root_dir(char root_dir[4]); int BLI_windows_get_executable_dir(char *str); #ifdef __cplusplus diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 1eaf007e01b..7db984aef5c 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -27,12 +27,12 @@ set(INC ../../../intern/guardedalloc ../../../intern/numaapi/include ../../../extern/wcwidth + ../../../extern/json/include ) set(INC_SYS ${ZLIB_INCLUDE_DIRS} ${ZSTD_INCLUDE_DIRS} - ${FREETYPE_INCLUDE_DIRS} ${GMP_INCLUDE_DIRS} ) @@ -81,7 +81,6 @@ set(SRC intern/filereader_memory.c intern/filereader_zstd.c intern/fnmatch.c - intern/freetypefont.c intern/gsqueue.c intern/hash_md5.c intern/hash_mm2a.c @@ -124,9 +123,11 @@ set(SRC intern/quadric.c intern/rand.cc intern/rct.c + intern/resource_scope.cc intern/scanfill.c intern/scanfill_utils.c intern/session_uuid.c + intern/serialize.cc intern/smallhash.c intern/sort.c intern/sort_utils.c @@ -283,6 +284,7 @@ set(SRC BLI_session_uuid.h BLI_set.hh BLI_set_slots.hh + BLI_serialize.hh BLI_simd.h BLI_smallhash.h BLI_sort.h @@ -317,7 +319,6 @@ set(SRC BLI_vector_adaptor.hh BLI_vector_set.hh BLI_vector_set_slots.hh - BLI_vfontdata.h BLI_virtual_array.hh BLI_virtual_vector_array.hh BLI_voronoi_2d.h @@ -333,7 +334,6 @@ set(LIB bf_intern_numaapi extern_wcwidth - ${FREETYPE_LIBRARY} ${ZLIB_LIBRARIES} ${ZSTD_LIBRARIES} ) @@ -367,6 +367,10 @@ if(WITH_GMP) endif() if(WIN32) + if (WITH_BLENDER_THUMBNAILER) + # Needed for querying the thumbnailer .dll in winstuff.c + add_definitions(-DWITH_BLENDER_THUMBNAILER) + endif() list(APPEND INC ../../../intern/utfconv ) @@ -451,6 +455,7 @@ if(WITH_GTESTS) tests/BLI_span_test.cc tests/BLI_stack_cxx_test.cc tests/BLI_stack_test.cc + tests/BLI_serialize_test.cc tests/BLI_string_ref_test.cc tests/BLI_string_search_test.cc tests/BLI_string_test.cc diff --git a/source/blender/blenlib/intern/boxpack_2d.c b/source/blender/blenlib/intern/boxpack_2d.c index 4a07f1134d0..cf5831cada5 100644 --- a/source/blender/blenlib/intern/boxpack_2d.c +++ b/source/blender/blenlib/intern/boxpack_2d.c @@ -516,7 +516,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r * flag verts on one or both of the boxes * as being used by checking the width or * height of both boxes */ - if (vert->tlb && vert->trb && (box == vert->tlb || box == vert->trb)) { + if (vert->tlb && vert->trb && (ELEM(box, vert->tlb, vert->trb))) { if (UNLIKELY(fabsf(vert->tlb->h - vert->trb->h) < EPSILON_MERGE)) { #ifdef USE_MERGE # define A (vert->trb->v[TL]) @@ -547,7 +547,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r vert->tlb->v[TR]->free &= ~(TRF | BRF); } } - else if (vert->blb && vert->brb && (box == vert->blb || box == vert->brb)) { + else if (vert->blb && vert->brb && (ELEM(box, vert->blb, vert->brb))) { if (UNLIKELY(fabsf(vert->blb->h - vert->brb->h) < EPSILON_MERGE)) { #ifdef USE_MERGE # define A (vert->blb->v[BR]) @@ -579,7 +579,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r } } /* Horizontal */ - if (vert->tlb && vert->blb && (box == vert->tlb || box == vert->blb)) { + if (vert->tlb && vert->blb && (ELEM(box, vert->tlb, vert->blb))) { if (UNLIKELY(fabsf(vert->tlb->w - vert->blb->w) < EPSILON_MERGE)) { #ifdef USE_MERGE # define A (vert->blb->v[TL]) @@ -610,7 +610,7 @@ void BLI_box_pack_2d(BoxPack *boxarray, const uint len, float *r_tot_x, float *r vert->tlb->v[BL]->free &= ~(BLF | BRF); } } - else if (vert->trb && vert->brb && (box == vert->trb || box == vert->brb)) { + else if (vert->trb && vert->brb && (ELEM(box, vert->trb, vert->brb))) { if (UNLIKELY(fabsf(vert->trb->w - vert->brb->w) < EPSILON_MERGE)) { #ifdef USE_MERGE diff --git a/source/blender/blenlib/intern/delaunay_2d.cc b/source/blender/blenlib/intern/delaunay_2d.cc index 4582ea69d9b..53e881a9fc7 100644 --- a/source/blender/blenlib/intern/delaunay_2d.cc +++ b/source/blender/blenlib/intern/delaunay_2d.cc @@ -2274,8 +2274,8 @@ void add_face_constraints(CDT_state *cdt_state, * making valid BMesh faces. */ int id = cdt_state->need_ids ? f : 0; add_face_ids(cdt_state, face_symedge0, id, fedge_start, fedge_end); - if (cdt_state->need_ids || (output_type == CDT_CONSTRAINTS_VALID_BMESH || - output_type == CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES)) { + if (cdt_state->need_ids || + ELEM(output_type, CDT_CONSTRAINTS_VALID_BMESH, CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES)) { add_face_ids(cdt_state, face_symedge0, f, fedge_start, fedge_end); } } diff --git a/source/blender/blenlib/intern/edgehash.c b/source/blender/blenlib/intern/edgehash.c index f95619803bf..6c397fae836 100644 --- a/source/blender/blenlib/intern/edgehash.c +++ b/source/blender/blenlib/intern/edgehash.c @@ -230,8 +230,8 @@ EdgeHash *BLI_edgehash_new_ex(const char *info, const uint reserve) UPDATE_SLOT_MASK(eh); eh->length = 0; eh->dummy_count = 0; - eh->entries = MEM_calloc_arrayN(sizeof(EdgeHashEntry), ENTRIES_CAPACITY(eh), "eh entries"); - eh->map = MEM_malloc_arrayN(sizeof(int32_t), MAP_CAPACITY(eh), "eh map"); + eh->entries = MEM_calloc_arrayN(ENTRIES_CAPACITY(eh), sizeof(EdgeHashEntry), "eh entries"); + eh->map = MEM_malloc_arrayN(MAP_CAPACITY(eh), sizeof(int32_t), "eh map"); CLEAR_MAP(eh); return eh; } @@ -515,8 +515,8 @@ EdgeSet *BLI_edgeset_new_ex(const char *info, const uint reserve) es->capacity_exp = calc_capacity_exp_for_reserve(reserve); UPDATE_SLOT_MASK(es); es->length = 0; - es->entries = MEM_malloc_arrayN(sizeof(Edge), ENTRIES_CAPACITY(es), "es entries"); - es->map = MEM_malloc_arrayN(sizeof(int32_t), MAP_CAPACITY(es), "es map"); + es->entries = MEM_malloc_arrayN(ENTRIES_CAPACITY(es), sizeof(Edge), "es entries"); + es->map = MEM_malloc_arrayN(MAP_CAPACITY(es), sizeof(int32_t), "es map"); CLEAR_MAP(es); return es; } diff --git a/source/blender/blenlib/intern/expr_pylike_eval.c b/source/blender/blenlib/intern/expr_pylike_eval.c index 4d1ba190c14..1acb8299aa2 100644 --- a/source/blender/blenlib/intern/expr_pylike_eval.c +++ b/source/blender/blenlib/intern/expr_pylike_eval.c @@ -653,7 +653,7 @@ static bool parse_add_func(ExprParseState *state, eOpCode code, int args, void * /* Extract the next token from raw characters. */ static bool parse_next_token(ExprParseState *state) { - /* Skip whitespace. */ + /* Skip white-space. */ while (isspace(*state->cur)) { state->cur++; } diff --git a/source/blender/blenlib/intern/fileops.c b/source/blender/blenlib/intern/fileops.c index 532a29b8147..c52feb097d2 100644 --- a/source/blender/blenlib/intern/fileops.c +++ b/source/blender/blenlib/intern/fileops.c @@ -30,8 +30,8 @@ #include -#include "zlib.h" -#include "zstd.h" +#include +#include #ifdef WIN32 # include "BLI_fileops_types.h" @@ -89,7 +89,7 @@ size_t BLI_file_zstd_from_mem_at_pos( total_written += output.pos; } - /* Finalize the Zstd frame. */ + /* Finalize the `Zstd` frame. */ size_t ret = 1; while (ret != 0) { ZSTD_outBuffer output = {out_buf, out_len, 0}; @@ -570,6 +570,7 @@ bool BLI_dir_create_recursive(const char *dirname) * blah1/blah2 (without slash) */ BLI_strncpy(tmp, dirname, sizeof(tmp)); + BLI_path_slash_native(tmp); BLI_path_slash_rstrip(tmp); /* check special case "c:\foo", don't try create "c:", harmless but prints an error below */ @@ -760,7 +761,7 @@ static int recursive_operation(const char *startfrom, # endif if (is_dir) { - /* recursively dig into a subfolder */ + /* Recurse into sub-directories. */ ret = recursive_operation( from_path, to_path, callback_dir_pre, callback_file, callback_dir_post); } diff --git a/source/blender/blenlib/intern/filereader_gzip.c b/source/blender/blenlib/intern/filereader_gzip.c index 72eb153a8b9..fe766e5da41 100644 --- a/source/blender/blenlib/intern/filereader_gzip.c +++ b/source/blender/blenlib/intern/filereader_gzip.c @@ -64,7 +64,7 @@ static ssize_t gzip_read(FileReader *reader, void *buffer, size_t size) int ret = inflate(&gzip->strm, Z_NO_FLUSH); - if (ret != Z_OK && ret != Z_BUF_ERROR) { + if (!ELEM(ret, Z_OK, Z_BUF_ERROR)) { break; } } diff --git a/source/blender/blenlib/intern/kdtree_impl.h b/source/blender/blenlib/intern/kdtree_impl.h index 0c9de0aa128..0b47be1f7ea 100644 --- a/source/blender/blenlib/intern/kdtree_impl.h +++ b/source/blender/blenlib/intern/kdtree_impl.h @@ -190,7 +190,7 @@ static uint kdtree_balance(KDTreeNode *nodes, uint nodes_len, uint axis, const u } } - /* set node and sort subnodes */ + /* Set node and sort sub-nodes. */ node = &nodes[median]; node->d = axis; axis = (axis + 1) % KD_DIMS; diff --git a/source/blender/blenlib/intern/list_sort_impl.h b/source/blender/blenlib/intern/list_sort_impl.h index 680044f9ccb..71f7f0e29a8 100644 --- a/source/blender/blenlib/intern/list_sort_impl.h +++ b/source/blender/blenlib/intern/list_sort_impl.h @@ -202,7 +202,7 @@ BLI_INLINE list_node *sweep_up(struct SortInfo *si, list_node *list, unsigned in } /** - * The 'ranks' array essentially captures the recursion stack of a mergesort. + * The 'ranks' array essentially captures the recursion stack of a merge-sort. * The merge tree is built in a bottom-up manner. The control loop for * updating the 'ranks' array is analogous to incrementing a binary integer, * and the `O(n)` time for counting `upto` n translates to `O(n)` merges when diff --git a/source/blender/blenlib/intern/math_base_inline.c b/source/blender/blenlib/intern/math_base_inline.c index 49f9faf1704..f609d5f8e8b 100644 --- a/source/blender/blenlib/intern/math_base_inline.c +++ b/source/blender/blenlib/intern/math_base_inline.c @@ -511,6 +511,22 @@ MINLINE float smoothminf(float a, float b, float c) } } +MINLINE float smoothstep(float edge0, float edge1, float x) +{ + float result; + if (x < edge0) { + result = 0.0f; + } + else if (x >= edge1) { + result = 1.0f; + } + else { + float t = (x - edge0) / (edge1 - edge0); + result = (3.0f - 2.0f * t) * (t * t); + } + return result; +} + MINLINE double min_dd(double a, double b) { return (a < b) ? a : b; diff --git a/source/blender/blenlib/intern/math_color_inline.c b/source/blender/blenlib/intern/math_color_inline.c index ad4b844175f..24c4143e587 100644 --- a/source/blender/blenlib/intern/math_color_inline.c +++ b/source/blender/blenlib/intern/math_color_inline.c @@ -27,7 +27,7 @@ #include "BLI_math_color.h" #include "BLI_utildefines.h" -#include "math.h" +#include #ifndef __MATH_COLOR_INLINE_C__ # define __MATH_COLOR_INLINE_C__ @@ -329,7 +329,7 @@ MINLINE float dither_random_value(float s, float t) hash0 -= floorf(hash0); hash1 -= floorf(hash1); /* Convert uniform distribution into triangle-shaped distribution. */ - return hash0 + hash0 - 0.5f; + return hash0 + hash1 - 0.5f; } MINLINE void float_to_byte_dither_v3( diff --git a/source/blender/blenlib/intern/math_geom_inline.c b/source/blender/blenlib/intern/math_geom_inline.c index 1757b0dd525..857cbfbde9c 100644 --- a/source/blender/blenlib/intern/math_geom_inline.c +++ b/source/blender/blenlib/intern/math_geom_inline.c @@ -99,7 +99,7 @@ MINLINE float dot_shsh(const float a[9], const float b[9]) return r; } -MINLINE float diffuse_shv3(float sh[9], const float v[3]) +MINLINE float diffuse_shv3(const float sh[9], const float v[3]) { /* See formula (13) in: * "An Efficient Representation for Irradiance Environment Maps" */ diff --git a/source/blender/blenlib/intern/math_matrix.c b/source/blender/blenlib/intern/math_matrix.c index b605c3eeead..b6d80d76be1 100644 --- a/source/blender/blenlib/intern/math_matrix.c +++ b/source/blender/blenlib/intern/math_matrix.c @@ -180,6 +180,21 @@ void copy_m4_m2(float m1[4][4], const float m2[2][2]) m1[3][3] = 1.0f; } +void copy_m3d_m3(double m1[3][3], const float m2[3][3]) +{ + m1[0][0] = m2[0][0]; + m1[0][1] = m2[0][1]; + m1[0][2] = m2[0][2]; + + m1[1][0] = m2[1][0]; + m1[1][1] = m2[1][1]; + m1[1][2] = m2[1][2]; + + m1[2][0] = m2[2][0]; + m1[2][1] = m2[2][1]; + m1[2][2] = m2[2][2]; +} + void copy_m4d_m4(double m1[4][4], const float m2[4][4]) { m1[0][0] = m2[0][0]; @@ -1113,6 +1128,13 @@ float determinant_m4_mat3_array(const float m[4][4]) m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])); } +double determinant_m3_array_db(const double m[3][3]) +{ + return (m[0][0] * (m[1][1] * m[2][2] - m[1][2] * m[2][1]) - + m[1][0] * (m[0][1] * m[2][2] - m[0][2] * m[2][1]) + + m[2][0] * (m[0][1] * m[1][2] - m[0][2] * m[1][1])); +} + bool invert_m3_ex(float m[3][3], const float epsilon) { float tmp[3][3]; @@ -2338,12 +2360,6 @@ void scale_m4_fl(float R[4][4], float scale) R[3][0] = R[3][1] = R[3][2] = 0.0; } -void translate_m3(float mat[3][3], float tx, float ty) -{ - mat[2][0] += (tx * mat[0][0] + ty * mat[1][0]); - mat[2][1] += (tx * mat[0][1] + ty * mat[1][1]); -} - void translate_m4(float mat[4][4], float Tx, float Ty, float Tz) { mat[3][0] += (Tx * mat[0][0] + Ty * mat[1][0] + Tz * mat[2][0]); @@ -2351,18 +2367,6 @@ void translate_m4(float mat[4][4], float Tx, float Ty, float Tz) mat[3][2] += (Tx * mat[0][2] + Ty * mat[1][2] + Tz * mat[2][2]); } -void rotate_m3(float mat[3][3], const float angle) -{ - const float angle_cos = cosf(angle); - const float angle_sin = sinf(angle); - - for (int col = 0; col < 3; col++) { - float temp = angle_cos * mat[0][col] + angle_sin * mat[1][col]; - mat[1][col] = -angle_sin * mat[0][col] + angle_cos * mat[1][col]; - mat[0][col] = temp; - } -} - /* TODO: enum for axis? */ /** * Rotate a matrix in-place. @@ -2408,12 +2412,6 @@ void rotate_m4(float mat[4][4], const char axis, const float angle) } } -void rescale_m3(float mat[3][3], const float scale[2]) -{ - mul_v3_fl(mat[0], scale[0]); - mul_v3_fl(mat[1], scale[1]); -} - /** Scale a matrix in-place. */ void rescale_m4(float mat[4][4], const float scale[3]) { @@ -2444,20 +2442,6 @@ void transform_pivot_set_m4(float mat[4][4], const float pivot[3]) mul_m4_m4m4(mat, mat, tmat); } -void transform_pivot_set_m3(float mat[3][3], const float pivot[2]) -{ - float tmat[3][3]; - - unit_m3(tmat); - - copy_v2_v2(tmat[2], pivot); - mul_m3_m3m3(mat, tmat, mat); - - /* invert the matrix */ - negate_v2(tmat[2]); - mul_m3_m3m3(mat, mat, tmat); -} - void blend_m3_m3m3(float out[3][3], const float dst[3][3], const float src[3][3], @@ -2637,21 +2621,6 @@ bool equals_m4m4(const float mat1[4][4], const float mat2[4][4]) equals_v4v4(mat1[2], mat2[2]) && equals_v4v4(mat1[3], mat2[3])); } -/** - * Make a 3x3 matrix out of 3 transform components. - * Matrices are made in the order: `loc * rot * scale` - */ -void loc_rot_size_to_mat3(float R[3][3], - const float loc[2], - const float angle, - const float size[2]) -{ - unit_m3(R); - translate_m3(R, loc[0], loc[1]); - rotate_m3(R, angle); - rescale_m3(R, size); -} - /** * Make a 4x4 matrix out of 3 transform components. * Matrices are made in the order: `scale * rot * loc` diff --git a/source/blender/blenlib/intern/math_vector_inline.c b/source/blender/blenlib/intern/math_vector_inline.c index ddfdaffb706..bb32b511005 100644 --- a/source/blender/blenlib/intern/math_vector_inline.c +++ b/source/blender/blenlib/intern/math_vector_inline.c @@ -991,7 +991,7 @@ MINLINE void add_newell_cross_v3_v3v3(float n[3], const float v_prev[3], const f n[2] += (v_prev[0] - v_curr[0]) * (v_prev[1] + v_curr[1]); } -MINLINE void star_m3_v3(float rmat[3][3], float a[3]) +MINLINE void star_m3_v3(float rmat[3][3], const float a[3]) { rmat[0][0] = rmat[1][1] = rmat[2][2] = 0.0; rmat[0][1] = -a[2]; @@ -1145,6 +1145,19 @@ MINLINE float len_v3v3(const float a[3], const float b[3]) return len_v3(d); } +MINLINE float len_v4(const float a[4]) +{ + return sqrtf(dot_v4v4(a, a)); +} + +MINLINE float len_v4v4(const float a[4], const float b[4]) +{ + float d[4]; + + sub_v4_v4v4(d, b, a); + return len_v4(d); +} + /** * \note any vectors containing `nan` will be zeroed out. */ diff --git a/source/blender/blenlib/intern/mesh_boolean.cc b/source/blender/blenlib/intern/mesh_boolean.cc index 90ffebdb422..8b029d11c3f 100644 --- a/source/blender/blenlib/intern/mesh_boolean.cc +++ b/source/blender/blenlib/intern/mesh_boolean.cc @@ -51,8 +51,8 @@ # include "BLI_mesh_boolean.hh" # ifdef WITH_TBB -# include "tbb/parallel_reduce.h" -# include "tbb/spin_mutex.h" +# include +# include # endif // # define PERFDEBUG diff --git a/source/blender/blenlib/intern/mesh_intersect.cc b/source/blender/blenlib/intern/mesh_intersect.cc index 5651e52799e..feb7b64f766 100644 --- a/source/blender/blenlib/intern/mesh_intersect.cc +++ b/source/blender/blenlib/intern/mesh_intersect.cc @@ -53,7 +53,7 @@ # include "BLI_mesh_intersect.hh" # ifdef WITH_TBB -# include "tbb/parallel_sort.h" +# include # endif // # define PERFDEBUG @@ -1162,7 +1162,7 @@ static int filter_plane_side(const double3 &p, } /* - * interesect_tri_tri and helper functions. + * #intersect_tri_tri and helper functions. * This code uses the algorithm of Guigue and Devillers, as described * in "Faster Triangle-Triangle Intersection Tests". * Adapted from code by Eric Haines: @@ -2225,8 +2225,7 @@ static bool face_is_degenerate(const Face *f) return false; } -/** Fast check for degenerate tris. Only tests for when verts are identical, - * not cases where there are zero-length edges. */ +/** Fast check for degenerate tris. It is OK if it returns true for nearly degenerate triangles. */ static bool any_degenerate_tris_fast(const Array triangulation) { for (const Face *f : triangulation) { @@ -2236,6 +2235,23 @@ static bool any_degenerate_tris_fast(const Array triangulation) if (v0 == v1 || v0 == v2 || v1 == v2) { return true; } + double3 da = v2->co - v0->co; + double3 db = v2->co - v1->co; + double da_length_squared = da.length_squared(); + double db_length_squared = db.length_squared(); + if (da_length_squared == 0.0 || db_length_squared == 0.0) { + return true; + } + /* |da x db| = |da| |db| sin t, where t is angle between them. + * The triangle is almost degenerate if sin t is almost 0. + * sin^2 t = |da x db|^2 / (|da|^2 |db|^2) + */ + double3 dab = double3::cross_high_precision(da, db); + double dab_length_squared = dab.length_squared(); + double sin_squared_t = dab_length_squared / (da_length_squared * db_length_squared); + if (sin_squared_t < 1e-8) { + return true; + } } return false; } diff --git a/source/blender/blenlib/intern/noise.c b/source/blender/blenlib/intern/noise.c index 9850de69b5a..a0938f4f713 100644 --- a/source/blender/blenlib/intern/noise.c +++ b/source/blender/blenlib/intern/noise.c @@ -1426,8 +1426,7 @@ float BLI_noise_mg_multi_fractal( } return value; - -} /* multifractal() */ +} /** * Heterogeneous procedural terrain function: stats by altitude method. diff --git a/source/blender/blenlib/intern/noise.cc b/source/blender/blenlib/intern/noise.cc index c057c12e543..959385bff31 100644 --- a/source/blender/blenlib/intern/noise.cc +++ b/source/blender/blenlib/intern/noise.cc @@ -46,12 +46,14 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include #include #include #include "BLI_float2.hh" #include "BLI_float3.hh" #include "BLI_float4.hh" +#include "BLI_math_base_safe.h" #include "BLI_noise.hh" #include "BLI_utildefines.h" @@ -109,7 +111,7 @@ BLI_INLINE void hash_bit_final(uint32_t &a, uint32_t &b, uint32_t &c) c -= hash_bit_rotate(b, 24); } -BLI_INLINE uint32_t hash(uint32_t kx) +uint32_t hash(uint32_t kx) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (1 << 2) + 13; @@ -120,7 +122,7 @@ BLI_INLINE uint32_t hash(uint32_t kx) return c; } -BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky) +uint32_t hash(uint32_t kx, uint32_t ky) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (2 << 2) + 13; @@ -132,7 +134,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky) return c; } -BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (3 << 2) + 13; @@ -145,7 +147,7 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz) return c; } -BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) { uint32_t a, b, c; a = b = c = 0xdeadbeef + (4 << 2) + 13; @@ -161,30 +163,6 @@ BLI_INLINE uint32_t hash(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) return c; } -/* Hashing a number of uint32_t into a float in the range [0, 1]. */ - -BLI_INLINE float hash_to_float(uint32_t kx) -{ - return static_cast(hash(kx)) / static_cast(0xFFFFFFFFu); -} - -BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky) -{ - return static_cast(hash(kx, ky)) / static_cast(0xFFFFFFFFu); -} - -BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz) -{ - return static_cast(hash(kx, ky, kz)) / static_cast(0xFFFFFFFFu); -} - -BLI_INLINE float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) -{ - return static_cast(hash(kx, ky, kz, kw)) / static_cast(0xFFFFFFFFu); -} - -/* Hashing a number of floats into a float in the range [0, 1]. */ - BLI_INLINE uint32_t float_as_uint(float f) { union { @@ -195,25 +173,114 @@ BLI_INLINE uint32_t float_as_uint(float f) return u.i; } -BLI_INLINE float hash_to_float(float k) +uint32_t hash_float(float kx) { - return hash_to_float(float_as_uint(k)); + return hash(float_as_uint(kx)); } -BLI_INLINE float hash_to_float(float2 k) +uint32_t hash_float(float2 k) { - return hash_to_float(float_as_uint(k.x), float_as_uint(k.y)); + return hash(float_as_uint(k.x), float_as_uint(k.y)); } -BLI_INLINE float hash_to_float(float3 k) +uint32_t hash_float(float3 k) { - return hash_to_float(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z)); + return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z)); } -BLI_INLINE float hash_to_float(float4 k) +uint32_t hash_float(float4 k) { - return hash_to_float( - float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w)); + return hash(float_as_uint(k.x), float_as_uint(k.y), float_as_uint(k.z), float_as_uint(k.w)); +} + +/* Hashing a number of uint32_t into a float in the range [0, 1]. */ + +BLI_INLINE float uint_to_float_01(uint32_t k) +{ + return static_cast(k) / static_cast(0xFFFFFFFFu); +} + +float hash_to_float(uint32_t kx) +{ + return uint_to_float_01(hash(kx)); +} + +float hash_to_float(uint32_t kx, uint32_t ky) +{ + return uint_to_float_01(hash(kx, ky)); +} + +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz) +{ + return uint_to_float_01(hash(kx, ky, kz)); +} + +float hash_to_float(uint32_t kx, uint32_t ky, uint32_t kz, uint32_t kw) +{ + return uint_to_float_01(hash(kx, ky, kz, kw)); +} + +/* Hashing a number of floats into a float in the range [0, 1]. */ + +float hash_float_to_float(float k) +{ + return uint_to_float_01(hash_float(k)); +} + +float hash_float_to_float(float2 k) +{ + return uint_to_float_01(hash_float(k)); +} + +float hash_float_to_float(float3 k) +{ + return uint_to_float_01(hash_float(k)); +} + +float hash_float_to_float(float4 k) +{ + return uint_to_float_01(hash_float(k)); +} + +float2 hash_float_to_float2(float2 k) +{ + return float2(hash_float_to_float(k), hash_float_to_float(float3(k.x, k.y, 1.0))); +} + +float3 hash_float_to_float3(float k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float2(k, 1.0)), + hash_float_to_float(float2(k, 2.0))); +} + +float3 hash_float_to_float3(float2 k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float3(k.x, k.y, 1.0)), + hash_float_to_float(float3(k.x, k.y, 2.0))); +} + +float3 hash_float_to_float3(float3 k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float4(k.x, k.y, k.z, 1.0)), + hash_float_to_float(float4(k.x, k.y, k.z, 2.0))); +} + +float3 hash_float_to_float3(float4 k) +{ + return float3(hash_float_to_float(k), + hash_float_to_float(float4(k.z, k.x, k.w, k.y)), + hash_float_to_float(float4(k.w, k.z, k.y, k.x))); +} + +float4 hash_float_to_float4(float4 k) +{ + return float4(hash_float_to_float(k), + hash_float_to_float(float4(k.w, k.x, k.y, k.z)), + hash_float_to_float(float4(k.z, k.w, k.x, k.y)), + hash_float_to_float(float4(k.y, k.z, k.w, k.x))); } /* ------------ @@ -343,7 +410,7 @@ BLI_INLINE float noise_grad(uint32_t hash, float x, float y, float z) { uint32_t h = hash & 15u; float u = h < 8u ? x : y; - float vt = ((h == 12u) || (h == 14u)) ? x : z; + float vt = ELEM(h, 12u, 14u) ? x : z; float v = h < 4u ? y : vt; return negate_if(u, h & 1u) + negate_if(v, h & 2u); } @@ -515,7 +582,7 @@ template float perlin_fractal_template(T position, float octaves, fl float amp = 1.0f; float maxamp = 0.0f; float sum = 0.0f; - octaves = CLAMPIS(octaves, 0.0f, 16.0f); + octaves = CLAMPIS(octaves, 0.0f, 15.0f); int n = static_cast(octaves); for (int i = 0; i <= n; i++) { float t = perlin(fscale * position); @@ -565,28 +632,28 @@ float perlin_fractal(float4 position, float octaves, float roughness) BLI_INLINE float random_float_offset(float seed) { - return 100.0f + hash_to_float(seed) * 100.0f; + return 100.0f + hash_float_to_float(seed) * 100.0f; } BLI_INLINE float2 random_float2_offset(float seed) { - return float2(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f); + return float2(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f); } BLI_INLINE float3 random_float3_offset(float seed) { - return float3(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f); + return float3(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f); } BLI_INLINE float4 random_float4_offset(float seed) { - return float4(100.0f + hash_to_float(float2(seed, 0.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 1.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 2.0f)) * 100.0f, - 100.0f + hash_to_float(float2(seed, 3.0f)) * 100.0f); + return float4(100.0f + hash_float_to_float(float2(seed, 0.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 1.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 2.0f)) * 100.0f, + 100.0f + hash_float_to_float(float2(seed, 3.0f)) * 100.0f); } /* Perlin noises to be added to the position to distort other noises. */ @@ -690,4 +757,1763 @@ float3 perlin_float3_fractal_distorted(float4 position, perlin_fractal(position + random_float4_offset(5.0f), octaves, roughness)); } +/* -------------- + * Musgrave Noise + * -------------- + */ + +/* 1D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 1D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 1D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain(const float co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset) +{ + float p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 1D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 1D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + +/* 2D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float2 co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float2 p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 2D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float2 p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 2D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain(const float2 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset) +{ + float2 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 2D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float2 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 2D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float2 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float2 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + +/* 3D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float3 co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float3 p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 3D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float3 p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 3D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain(const float3 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset) +{ + float3 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 3D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float3 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 3D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float3 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float3 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + +/* 4D Musgrave fBm + * + * H: fractal increment parameter + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * + * from "Texturing and Modelling: A procedural approach" + */ + +float musgrave_fBm(const float4 co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float4 p = co; + float value = 0.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value += perlin_signed(p) * pwr; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * perlin_signed(p) * pwr; + } + + return value; +} + +/* 4D Musgrave Multifractal + * + * H: highest fractal dimension + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + */ + +float musgrave_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves_unclamped) +{ + float4 p = co; + float value = 1.0f; + float pwr = 1.0f; + const float pwHL = powf(lacunarity, -H); + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 0; i < (int)octaves; i++) { + value *= (pwr * perlin_signed(p) + 1.0f); + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value *= (rmd * pwr * perlin_signed(p) + 1.0f); /* correct? */ + } + + return value; +} + +/* 4D Musgrave Heterogeneous Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hetero_terrain(const float4 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset) +{ + float4 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + /* first unscaled octave of function; later octaves are scaled */ + float value = offset + perlin_signed(p); + p *= lacunarity; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; i < (int)octaves; i++) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += increment; + pwr *= pwHL; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + float increment = (perlin_signed(p) + offset) * pwr * value; + value += rmd * increment; + } + + return value; +} + +/* 4D Hybrid Additive/Multiplicative Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_hybrid_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float4 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float value = perlin_signed(p) + offset; + float weight = gain * value; + p *= lacunarity; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; (weight > 0.001f) && (i < (int)octaves); i++) { + if (weight > 1.0f) { + weight = 1.0f; + } + + float signal = (perlin_signed(p) + offset) * pwr; + pwr *= pwHL; + value += weight * signal; + weight *= gain * signal; + p *= lacunarity; + } + + const float rmd = octaves - floorf(octaves); + if (rmd != 0.0f) { + value += rmd * ((perlin_signed(p) + offset) * pwr); + } + + return value; +} + +/* 4D Ridged Multifractal Terrain + * + * H: fractal dimension of the roughest area + * lacunarity: gap between successive frequencies + * octaves: number of frequencies in the fBm + * offset: raises the terrain from `sea level' + */ + +float musgrave_ridged_multi_fractal(const float4 co, + const float H, + const float lacunarity, + const float octaves_unclamped, + const float offset, + const float gain) +{ + float4 p = co; + const float pwHL = powf(lacunarity, -H); + float pwr = pwHL; + + float signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + float value = signal; + float weight = 1.0f; + + const float octaves = CLAMPIS(octaves_unclamped, 0.0f, 15.0f); + + for (int i = 1; i < (int)octaves; i++) { + p *= lacunarity; + weight = CLAMPIS(signal * gain, 0.0f, 1.0f); + signal = offset - fabsf(perlin_signed(p)); + signal *= signal; + signal *= weight; + value += signal * pwr; + pwr *= pwHL; + } + + return value; +} + +/* + * Voronoi: Ported from Cycles code. + * + * Original code is under the MIT License, Copyright (c) 2013 Inigo Quilez. + * + * Smooth Voronoi: + * + * - https://wiki.blender.org/wiki/User:OmarSquircleArt/GSoC2019/Documentation/Smooth_Voronoi + * + * Distance To Edge based on: + * + * - https://www.iquilezles.org/www/articles/voronoilines/voronoilines.htm + * - https://www.shadertoy.com/view/ldl3W8 + * + * With optimization to change -2..2 scan window to -1..1 for better performance, + * as explained in https://www.shadertoy.com/view/llG3zy. + */ + +/* **** 1D Voronoi **** */ + +/* Ensure to align with DNA. */ +enum { + NOISE_SHD_VORONOI_EUCLIDEAN = 0, + NOISE_SHD_VORONOI_MANHATTAN = 1, + NOISE_SHD_VORONOI_CHEBYCHEV = 2, + NOISE_SHD_VORONOI_MINKOWSKI = 3, +}; + +BLI_INLINE float voronoi_distance(const float a, const float b) +{ + return fabsf(b - a); +} + +void voronoi_f1( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float minDistance = 8.0f; + float targetOffset = 0.0f; + float targetPosition = 0.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_w != nullptr) { + *r_w = targetPosition + cellPosition; + } +} + +void voronoi_smooth_f1(const float w, + const float smoothness, + const float randomness, + float *r_distance, + float3 *r_color, + float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); + + float smoothDistance = 8.0f; + float smoothPosition = 0.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + for (int i = -2; i <= 2; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + if (r_color != nullptr || r_w != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_w != nullptr) { + smoothPosition = mix(smoothPosition, pointPosition, h) - correctionFactor; + } + } + } + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_w != nullptr) { + *r_w = cellPosition + smoothPosition; + } +} + +void voronoi_f2( + const float w, const float randomness, float *r_distance, float3 *r_color, float *r_w) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float offsetF1 = 0.0f; + float positionF1 = 0.0f; + float offsetF2 = 0.0f; + float positionF2 = 0.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance(pointPosition, localPosition); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_w != nullptr) { + *r_w = positionF2 + cellPosition; + } +} + +void voronoi_distance_to_edge(const float w, const float randomness, float *r_distance) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + const float midPointPosition = hash_float_to_float(cellPosition) * randomness; + const float leftPointPosition = -1.0f + hash_float_to_float(cellPosition - 1.0f) * randomness; + const float rightPointPosition = 1.0f + hash_float_to_float(cellPosition + 1.0f) * randomness; + const float distanceToMidLeft = fabsf((midPointPosition + leftPointPosition) / 2.0f - + localPosition); + const float distanceToMidRight = fabsf((midPointPosition + rightPointPosition) / 2.0f - + localPosition); + + *r_distance = std::min(distanceToMidLeft, distanceToMidRight); +} + +void voronoi_n_sphere_radius(const float w, const float randomness, float *r_radius) +{ + const float cellPosition = floorf(w); + const float localPosition = w - cellPosition; + + float closestPoint = 0.0f; + float closestPointOffset = 0.0f; + float minDistance = 8.0f; + for (int i = -1; i <= 1; i++) { + const float cellOffset = i; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = fabsf(pointPosition - localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + + minDistance = 8.0f; + float closestPointToClosestPoint = 0.0f; + for (int i = -1; i <= 1; i++) { + if (i == 0) { + continue; + } + const float cellOffset = i + closestPointOffset; + const float pointPosition = cellOffset + + hash_float_to_float(cellPosition + cellOffset) * randomness; + const float distanceToPoint = fabsf(closestPoint - pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + *r_radius = fabsf(closestPointToClosestPoint - closestPoint) / 2.0f; +} + +/* **** 2D Voronoi **** */ + +static float voronoi_distance(const float2 a, + const float2 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float2::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), fabsf(a.y - b.y)); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float2 targetOffset = float2(0.0f, 0.0f); + float2 targetPosition = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + float distanceToPoint = voronoi_distance(pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } +} + +void voronoi_smooth_f1(const float2 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float2 smoothPosition = float2(0.0f, 0.0f); + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float2::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } + } + } + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } +} + +void voronoi_f2(const float2 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float2 *r_position) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float2 offsetF1 = float2(0.0f, 0.0f); + float2 positionF1 = float2(0.0f, 0.0f); + float2 offsetF2 = float2(0.0f, 0.0f); + float2 positionF2 = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } +} + +void voronoi_distance_to_edge(const float2 coord, const float randomness, float *r_distance) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float2 vectorToClosest = float2(0.0f, 0.0f); + float minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 vectorToPoint = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness - + localPosition; + const float distanceToPoint = dot_v2v2(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + + minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 vectorToPoint = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness - + localPosition; + const float2 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v2v2(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v2v2((vectorToClosest + vectorToPoint) / 2.0f, + perpendicularToEdge.normalized()); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float2 coord, const float randomness, float *r_radius) +{ + const float2 cellPosition = float2::floor(coord); + const float2 localPosition = coord - cellPosition; + + float2 closestPoint = float2(0.0f, 0.0f); + float2 closestPointOffset = float2(0.0f, 0.0f); + float minDistance = 8.0f; + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float2 cellOffset = float2(i, j); + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float2::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + + minDistance = 8.0f; + float2 closestPointToClosestPoint = float2(0.0f, 0.0f); + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0) { + continue; + } + const float2 cellOffset = float2(i, j) + closestPointOffset; + const float2 pointPosition = cellOffset + + hash_float_to_float2(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float2::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + *r_radius = float2::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + +/* **** 3D Voronoi **** */ + +static float voronoi_distance(const float3 a, + const float3 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float3::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), std::max(fabsf(a.y - b.y), fabsf(a.z - b.z))); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) + + powf(fabsf(a.z - b.z), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float3 targetOffset = float3(0.0f, 0.0f, 0.0f); + float3 targetPosition = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + } + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } +} + +void voronoi_smooth_f1(const float3 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float3 smoothPosition = float3(0.0f, 0.0f, 0.0f); + for (int k = -2; k <= 2; k++) { + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float3::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } + } + } + } + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } +} + +void voronoi_f2(const float3 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float3 *r_position) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float3 offsetF1 = float3(0.0f, 0.0f, 0.0f); + float3 positionF1 = float3(0.0f, 0.0f, 0.0f); + float3 offsetF2 = float3(0.0f, 0.0f, 0.0f); + float3 positionF2 = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + } + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } +} + +void voronoi_distance_to_edge(const float3 coord, const float randomness, float *r_distance) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float3 vectorToClosest = float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 vectorToPoint = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness - + localPosition; + const float distanceToPoint = dot_v3v3(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + } + + minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 vectorToPoint = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness - + localPosition; + const float3 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v3v3(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v3v3((vectorToClosest + vectorToPoint) / 2.0f, + perpendicularToEdge.normalized()); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float3 coord, const float randomness, float *r_radius) +{ + const float3 cellPosition = float3::floor(coord); + const float3 localPosition = coord - cellPosition; + + float3 closestPoint = float3(0.0f, 0.0f, 0.0f); + float3 closestPointOffset = float3(0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float3 cellOffset = float3(i, j, k); + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float3::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + } + + minDistance = 8.0f; + float3 closestPointToClosestPoint = float3(0.0f, 0.0f, 0.0f); + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0) { + continue; + } + const float3 cellOffset = float3(i, j, k) + closestPointOffset; + const float3 pointPosition = cellOffset + + hash_float_to_float3(cellPosition + cellOffset) * randomness; + const float distanceToPoint = float3::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + } + *r_radius = float3::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + +/* **** 4D Voronoi **** */ + +static float voronoi_distance(const float4 a, + const float4 b, + const int metric, + const float exponent) +{ + switch (metric) { + case NOISE_SHD_VORONOI_EUCLIDEAN: + return float4::distance(a, b); + case NOISE_SHD_VORONOI_MANHATTAN: + return fabsf(a.x - b.x) + fabsf(a.y - b.y) + fabsf(a.z - b.z) + fabsf(a.w - b.w); + case NOISE_SHD_VORONOI_CHEBYCHEV: + return std::max(fabsf(a.x - b.x), + std::max(fabsf(a.y - b.y), std::max(fabsf(a.z - b.z), fabsf(a.w - b.w)))); + case NOISE_SHD_VORONOI_MINKOWSKI: + return powf(powf(fabsf(a.x - b.x), exponent) + powf(fabsf(a.y - b.y), exponent) + + powf(fabsf(a.z - b.z), exponent) + powf(fabsf(a.w - b.w), exponent), + 1.0f / exponent); + default: + BLI_assert_unreachable(); + break; + } + return 0.0f; +} + +void voronoi_f1(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float minDistance = 8.0f; + float4 targetOffset = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 targetPosition = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < minDistance) { + targetOffset = cellOffset; + minDistance = distanceToPoint; + targetPosition = pointPosition; + } + } + } + } + } + if (r_distance != nullptr) { + *r_distance = minDistance; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + targetOffset); + } + if (r_position != nullptr) { + *r_position = targetPosition + cellPosition; + } +} + +void voronoi_smooth_f1(const float4 coord, + const float smoothness, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + const float smoothness_clamped = max_ff(smoothness, FLT_MIN); + + float smoothDistance = 8.0f; + float3 smoothColor = float3(0.0f, 0.0f, 0.0f); + float4 smoothPosition = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -2; u <= 2; u++) { + for (int k = -2; k <= 2; k++) { + for (int j = -2; j <= 2; j++) { + for (int i = -2; i <= 2; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + const float h = smoothstep( + 0.0f, 1.0f, 0.5f + 0.5f * (smoothDistance - distanceToPoint) / smoothness_clamped); + float correctionFactor = smoothness * h * (1.0f - h); + smoothDistance = mix(smoothDistance, distanceToPoint, h) - correctionFactor; + if (r_color != nullptr || r_position != nullptr) { + correctionFactor /= 1.0f + 3.0f * smoothness; + if (r_color != nullptr) { + const float3 cellColor = hash_float_to_float3(cellPosition + cellOffset); + smoothColor = float3::interpolate(smoothColor, cellColor, h) - correctionFactor; + } + if (r_position != nullptr) { + smoothPosition = float4::interpolate(smoothPosition, pointPosition, h) - + correctionFactor; + } + } + } + } + } + } + if (r_distance != nullptr) { + *r_distance = smoothDistance; + } + if (r_color != nullptr) { + *r_color = smoothColor; + } + if (r_position != nullptr) { + *r_position = cellPosition + smoothPosition; + } +} + +void voronoi_f2(const float4 coord, + const float exponent, + const float randomness, + const int metric, + float *r_distance, + float3 *r_color, + float4 *r_position) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float distanceF1 = 8.0f; + float distanceF2 = 8.0f; + float4 offsetF1 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 positionF1 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 offsetF2 = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 positionF2 = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = voronoi_distance( + pointPosition, localPosition, metric, exponent); + if (distanceToPoint < distanceF1) { + distanceF2 = distanceF1; + distanceF1 = distanceToPoint; + offsetF2 = offsetF1; + offsetF1 = cellOffset; + positionF2 = positionF1; + positionF1 = pointPosition; + } + else if (distanceToPoint < distanceF2) { + distanceF2 = distanceToPoint; + offsetF2 = cellOffset; + positionF2 = pointPosition; + } + } + } + } + } + if (r_distance != nullptr) { + *r_distance = distanceF2; + } + if (r_color != nullptr) { + *r_color = hash_float_to_float3(cellPosition + offsetF2); + } + if (r_position != nullptr) { + *r_position = positionF2 + cellPosition; + } +} + +void voronoi_distance_to_edge(const float4 coord, const float randomness, float *r_distance) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float4 vectorToClosest = float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 vectorToPoint = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness - + localPosition; + const float distanceToPoint = dot_v4v4(vectorToPoint, vectorToPoint); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + vectorToClosest = vectorToPoint; + } + } + } + } + } + + minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 vectorToPoint = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness - + localPosition; + const float4 perpendicularToEdge = vectorToPoint - vectorToClosest; + if (dot_v4v4(perpendicularToEdge, perpendicularToEdge) > 0.0001f) { + const float distanceToEdge = dot_v4v4((vectorToClosest + vectorToPoint) / 2.0f, + float4::normalize(perpendicularToEdge)); + minDistance = std::min(minDistance, distanceToEdge); + } + } + } + } + } + *r_distance = minDistance; +} + +void voronoi_n_sphere_radius(const float4 coord, const float randomness, float *r_radius) +{ + const float4 cellPosition = float4::floor(coord); + const float4 localPosition = coord - cellPosition; + + float4 closestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f); + float4 closestPointOffset = float4(0.0f, 0.0f, 0.0f, 0.0f); + float minDistance = 8.0f; + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + const float4 cellOffset = float4(i, j, k, u); + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = float4::distance(pointPosition, localPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPoint = pointPosition; + closestPointOffset = cellOffset; + } + } + } + } + } + + minDistance = 8.0f; + float4 closestPointToClosestPoint = float4(0.0f, 0.0f, 0.0f, 0.0f); + for (int u = -1; u <= 1; u++) { + for (int k = -1; k <= 1; k++) { + for (int j = -1; j <= 1; j++) { + for (int i = -1; i <= 1; i++) { + if (i == 0 && j == 0 && k == 0 && u == 0) { + continue; + } + const float4 cellOffset = float4(i, j, k, u) + closestPointOffset; + const float4 pointPosition = cellOffset + + hash_float_to_float4(cellPosition + cellOffset) * + randomness; + const float distanceToPoint = float4::distance(closestPoint, pointPosition); + if (distanceToPoint < minDistance) { + minDistance = distanceToPoint; + closestPointToClosestPoint = pointPosition; + } + } + } + } + } + *r_radius = float4::distance(closestPointToClosestPoint, closestPoint) / 2.0f; +} + } // namespace blender::noise diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 4d0678035ba..182fe211110 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -48,7 +48,7 @@ # include # include #else -# include "unistd.h" +# include #endif /* WIN32 */ #include "MEM_guardedalloc.h" @@ -1730,6 +1730,9 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__re /** * Simple appending of filename to dir, does not check for valid path! * Puts result into `dst`, which may be same area as `dir`. + * + * \note Consider using #BLI_path_join for more general path joining + * that de-duplicates separators and can handle an arbitrary number of paths. */ void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, @@ -1741,9 +1744,13 @@ void BLI_join_dirfile(char *__restrict dst, #endif size_t dirlen = BLI_strnlen(dir, maxlen); - /* args can't match */ + /* Arguments can't match. */ BLI_assert(!ELEM(dst, dir, file)); + /* Files starting with a separator cause a double-slash which could later be interpreted + * as a relative path where: `dir == "/"` and `file == "/file"` would result in "//file". */ + BLI_assert(file[0] != SEP); + if (dirlen == maxlen) { memcpy(dst, dir, dirlen); dst[dirlen - 1] = '\0'; @@ -1935,6 +1942,39 @@ bool BLI_path_name_at_index(const char *__restrict path, return false; } +bool BLI_path_contains(const char *container_path, const char *containee_path) +{ + char container_native[PATH_MAX]; + char containee_native[PATH_MAX]; + + /* Keep space for a trailing slash. If the path is truncated by this, the containee path is + * longer than PATH_MAX and the result is ill-defined. */ + BLI_strncpy(container_native, container_path, PATH_MAX - 1); + BLI_strncpy(containee_native, containee_path, PATH_MAX); + + BLI_path_slash_native(container_native); + BLI_path_slash_native(containee_native); + + BLI_path_normalize(NULL, container_native); + BLI_path_normalize(NULL, containee_native); + +#ifdef WIN32 + BLI_str_tolower_ascii(container_native, PATH_MAX); + BLI_str_tolower_ascii(containee_native, PATH_MAX); +#endif + + if (STREQ(container_native, containee_native)) { + /* The paths are equal, they contain each other. */ + return true; + } + + /* Add a trailing slash to prevent same-prefix directories from matching. + * e.g. "/some/path" doesn't contain "/some/path_lib". */ + BLI_path_slash_ensure(container_native); + + return BLI_str_startswith(containee_native, container_native); +} + /** * Returns pointer to the leftmost path separator in string. Not actually used anywhere. */ diff --git a/source/blender/blenlib/intern/resource_scope.cc b/source/blender/blenlib/intern/resource_scope.cc new file mode 100644 index 00000000000..8c8a03b8ce5 --- /dev/null +++ b/source/blender/blenlib/intern/resource_scope.cc @@ -0,0 +1,32 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_resource_scope.hh" + +namespace blender { + +ResourceScope::ResourceScope() = default; + +ResourceScope::~ResourceScope() +{ + /* Free in reversed order. */ + for (int64_t i = resources_.size(); i--;) { + ResourceData &data = resources_[i]; + data.free(data.data); + } +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/scanfill.c b/source/blender/blenlib/intern/scanfill.c index f0cf19bf508..26ecc7552dc 100644 --- a/source/blender/blenlib/intern/scanfill.c +++ b/source/blender/blenlib/intern/scanfill.c @@ -448,7 +448,7 @@ static void splitlist(ScanFillContext *sf_ctx, ListBase *temped, unsigned short nr) { - /* everything is in templist, write only poly nr to fillist */ + /* Everything is in temp-list, write only poly nr to fill-list. */ ScanFillVert *eve, *eve_next; ScanFillEdge *eed, *eed_next; @@ -1040,13 +1040,13 @@ unsigned int BLI_scanfill_calc_ex(ScanFillContext *sf_ctx, const int flag, const } /* CURRENT STATUS: - * - eve->f :1 = available in edges - * - eve->poly_nr :polynumber - * - eve->edge_tot :amount of edges connected to vertex - * - eve->tmp.v :store! original vertex number + * - `eve->f`: 1 = available in edges. + * - `eve->poly_nr`: poly-number. + * - `eve->edge_tot`: amount of edges connected to vertex. + * - `eve->tmp.v`: store! original vertex number. * - * - eed->f :1 = boundary edge (optionally set by caller) - * - eed->poly_nr :poly number + * - `eed->f`: 1 = boundary edge (optionally set by caller). + * - `eed->poly_nr`: poly number. */ /* STEP 3: MAKE POLYFILL STRUCT */ diff --git a/source/blender/blenlib/intern/scanfill_utils.c b/source/blender/blenlib/intern/scanfill_utils.c index ec0f8659395..33c0f4afd01 100644 --- a/source/blender/blenlib/intern/scanfill_utils.c +++ b/source/blender/blenlib/intern/scanfill_utils.c @@ -156,15 +156,13 @@ static ScanFillEdge *edge_step(PolyInfo *poly_info, eed = (e_curr->next && e_curr != poly_info[poly_nr].edge_last) ? e_curr->next : poly_info[poly_nr].edge_first; - if ((v_curr == eed->v1 || v_curr == eed->v2) == true && - (ELEM(v_prev, eed->v1, eed->v2)) == false) { + if (ELEM(v_curr, eed->v1, eed->v2) == true && ELEM(v_prev, eed->v1, eed->v2) == false) { return eed; } eed = (e_curr->prev && e_curr != poly_info[poly_nr].edge_first) ? e_curr->prev : poly_info[poly_nr].edge_last; - if ((v_curr == eed->v1 || v_curr == eed->v2) == true && - (ELEM(v_prev, eed->v1, eed->v2)) == false) { + if (ELEM(v_curr, eed->v1, eed->v2) == true && ELEM(v_prev, eed->v1, eed->v2) == false) { return eed; } diff --git a/source/blender/blenlib/intern/serialize.cc b/source/blender/blenlib/intern/serialize.cc new file mode 100644 index 00000000000..52aff140e3e --- /dev/null +++ b/source/blender/blenlib/intern/serialize.cc @@ -0,0 +1,216 @@ +#include "BLI_serialize.hh" + +#include "json.hpp" + +namespace blender::io::serialize { + +const StringValue *Value::as_string_value() const +{ + if (type_ != eValueType::String) { + return nullptr; + } + return static_cast(this); +} + +const IntValue *Value::as_int_value() const +{ + if (type_ != eValueType::Int) { + return nullptr; + } + return static_cast(this); +} + +const DoubleValue *Value::as_double_value() const +{ + if (type_ != eValueType::Double) { + return nullptr; + } + return static_cast(this); +} + +const BooleanValue *Value::as_boolean_value() const +{ + if (type_ != eValueType::Boolean) { + return nullptr; + } + return static_cast(this); +} + +const ArrayValue *Value::as_array_value() const +{ + if (type_ != eValueType::Array) { + return nullptr; + } + return static_cast(this); +} + +const ObjectValue *Value::as_object_value() const +{ + if (type_ != eValueType::Object) { + return nullptr; + } + return static_cast(this); +} + +static void convert_to_json(nlohmann::ordered_json &j, const Value &value); +static void convert_to_json(nlohmann::ordered_json &j, const ArrayValue &value) +{ + const ArrayValue::Items &items = value.elements(); + /* Create a json array to store the elements. If this isn't done and items is empty it would + * return use a null value, in stead of an empty array. */ + j = "[]"_json; + for (const ArrayValue::Item &item_value : items) { + nlohmann::ordered_json json_item; + convert_to_json(json_item, *item_value); + j.push_back(json_item); + } +} + +static void convert_to_json(nlohmann::ordered_json &j, const ObjectValue &value) +{ + const ObjectValue::Items &attributes = value.elements(); + /* Create a json object to store the attributes. If this isn't done and attributes is empty it + * would return use a null value, in stead of an empty object. */ + j = "{}"_json; + for (const ObjectValue::Item &attribute : attributes) { + nlohmann::ordered_json json_item; + convert_to_json(json_item, *attribute.second); + j[attribute.first] = json_item; + } +} + +static void convert_to_json(nlohmann::ordered_json &j, const Value &value) +{ + switch (value.type()) { + case eValueType::String: { + j = value.as_string_value()->value(); + break; + } + + case eValueType::Int: { + j = value.as_int_value()->value(); + break; + } + + case eValueType::Array: { + const ArrayValue &array = *value.as_array_value(); + convert_to_json(j, array); + break; + } + + case eValueType::Object: { + const ObjectValue &object = *value.as_object_value(); + convert_to_json(j, object); + break; + } + + case eValueType::Null: { + j = nullptr; + break; + } + + case eValueType::Boolean: { + j = value.as_boolean_value()->value(); + break; + } + + case eValueType::Double: { + j = value.as_double_value()->value(); + } + } +} + +static std::unique_ptr convert_from_json(const nlohmann::ordered_json &j); +static std::unique_ptr convert_from_json_to_array(const nlohmann::ordered_json &j) +{ + std::unique_ptr array = std::make_unique(); + ArrayValue::Items &elements = array->elements(); + for (auto element : j.items()) { + nlohmann::ordered_json element_json = element.value(); + std::unique_ptr value = convert_from_json(element_json); + elements.append_as(value.release()); + } + return array; +} + +static std::unique_ptr convert_from_json_to_object(const nlohmann::ordered_json &j) +{ + std::unique_ptr object = std::make_unique(); + ObjectValue::Items &elements = object->elements(); + for (auto element : j.items()) { + std::string key = element.key(); + nlohmann::ordered_json element_json = element.value(); + std::unique_ptr value = convert_from_json(element_json); + elements.append_as(std::pair(key, value.release())); + } + return object; +} + +static std::unique_ptr convert_from_json(const nlohmann::ordered_json &j) +{ + switch (j.type()) { + case nlohmann::json::value_t::array: { + return convert_from_json_to_array(j); + } + + case nlohmann::json::value_t::object: { + return convert_from_json_to_object(j); + } + + case nlohmann::json::value_t::string: { + std::string value = j; + return std::make_unique(value); + } + + case nlohmann::json::value_t::null: { + return std::make_unique(); + } + + case nlohmann::json::value_t::boolean: { + return std::make_unique(j); + } + case nlohmann::json::value_t::number_integer: + case nlohmann::json::value_t::number_unsigned: { + return std::make_unique(j); + } + + case nlohmann::json::value_t::number_float: { + return std::make_unique(j); + } + + case nlohmann::json::value_t::binary: + case nlohmann::json::value_t::discarded: + /* + * Binary data isn't supported. + * Discarded is an internal type of nlohmann. + * + * Assert in case we need to parse them. + */ + BLI_assert_unreachable(); + return std::make_unique(); + } + + BLI_assert_unreachable(); + return std::make_unique(); +} + +void JsonFormatter::serialize(std::ostream &os, const Value &value) +{ + nlohmann::ordered_json j; + convert_to_json(j, value); + if (indentation_len) { + os << j.dump(indentation_len); + } + else { + os << j.dump(); + } +} + +std::unique_ptr JsonFormatter::deserialize(std::istream &is) +{ + nlohmann::ordered_json j; + is >> j; + return convert_from_json(j); +} + +} // namespace blender::io::serialize diff --git a/source/blender/blenlib/intern/session_uuid.c b/source/blender/blenlib/intern/session_uuid.c index 8ed96f02149..ac15a400a92 100644 --- a/source/blender/blenlib/intern/session_uuid.c +++ b/source/blender/blenlib/intern/session_uuid.c @@ -74,5 +74,5 @@ bool BLI_session_uuid_ghash_compare(const void *lhs_v, const void *rhs_v) { const SessionUUID *lhs = (const SessionUUID *)lhs_v; const SessionUUID *rhs = (const SessionUUID *)rhs_v; - return BLI_session_uuid_is_equal(lhs, rhs); + return !BLI_session_uuid_is_equal(lhs, rhs); } diff --git a/source/blender/blenlib/intern/string.c b/source/blender/blenlib/intern/string.c index 0ea784c95b0..e8be674b6c1 100644 --- a/source/blender/blenlib/intern/string.c +++ b/source/blender/blenlib/intern/string.c @@ -1026,7 +1026,7 @@ void BLI_str_toupper_ascii(char *str, const size_t len) } /** - * Strip whitespace from end of the string. + * Strip white-space from end of the string. */ void BLI_str_rstrip(char *str) { diff --git a/source/blender/blenlib/intern/string_search.cc b/source/blender/blenlib/intern/string_search.cc index a466c124073..08ba473f96b 100644 --- a/source/blender/blenlib/intern/string_search.cc +++ b/source/blender/blenlib/intern/string_search.cc @@ -24,6 +24,10 @@ #include "BLI_string_utf8.h" #include "BLI_timeit.hh" +/* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */ +#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" +#define UI_MENU_ARROW_SEP_UNICODE 0x25b6 + namespace blender::string_search { static int64_t count_utf8_code_points(StringRef str) @@ -350,8 +354,11 @@ void extract_normalized_words(StringRef str, LinearAllocator<> &allocator, Vector &r_words) { - const uint32_t unicode_space = BLI_str_utf8_as_unicode(" "); - const uint32_t unicode_right_triangle = BLI_str_utf8_as_unicode("▶"); + const uint32_t unicode_space = (uint32_t)' '; + const uint32_t unicode_right_triangle = UI_MENU_ARROW_SEP_UNICODE; + + BLI_assert(unicode_space == BLI_str_utf8_as_unicode(" ")); + BLI_assert(unicode_right_triangle == BLI_str_utf8_as_unicode(UI_MENU_ARROW_SEP)); auto is_separator = [&](uint32_t unicode) { return ELEM(unicode, unicode_space, unicode_right_triangle); diff --git a/source/blender/blenlib/intern/string_utils.c b/source/blender/blenlib/intern/string_utils.c index bd733aca4f1..b62b9c3bc7a 100644 --- a/source/blender/blenlib/intern/string_utils.c +++ b/source/blender/blenlib/intern/string_utils.c @@ -469,6 +469,8 @@ char *BLI_string_join_arrayN(const char *strings[], uint strings_len) for (uint i = 0; i < strings_len; i++) { c += BLI_strcpy_rlen(c, strings[i]); } + /* Only needed when `strings_len == 0`. */ + *c = '\0'; return result; } diff --git a/source/blender/blenlib/intern/task_pool.cc b/source/blender/blenlib/intern/task_pool.cc index cbb5bf34477..1651d4c2205 100644 --- a/source/blender/blenlib/intern/task_pool.cc +++ b/source/blender/blenlib/intern/task_pool.cc @@ -121,7 +121,7 @@ class Task { #ifdef WITH_TBB class TBBTaskGroup : public tbb::task_group { public: - TBBTaskGroup(TaskPriority priority) + TBBTaskGroup(eTaskPriority priority) { # if TBB_INTERFACE_VERSION_MAJOR >= 12 /* TODO: support priorities in TBB 2021, where they are only available as @@ -186,7 +186,7 @@ void Task::operator()() const * Tasks may be suspended until in all are created, to make it possible to * initialize data structures and create tasks in a single pass. */ -static void tbb_task_pool_create(TaskPool *pool, TaskPriority priority) +static void tbb_task_pool_create(TaskPool *pool, eTaskPriority priority) { if (pool->type == TASK_POOL_TBB_SUSPENDED) { pool->is_suspended = true; @@ -363,7 +363,7 @@ static void background_task_pool_free(TaskPool *pool) /* Task Pool */ -static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPriority priority) +static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, eTaskPriority priority) { const bool use_threads = BLI_task_scheduler_num_threads() > 1 && type != TASK_POOL_NO_THREADS; @@ -401,7 +401,7 @@ static TaskPool *task_pool_create_ex(void *userdata, TaskPoolType type, TaskPrio /** * Create a normal task pool. Tasks will be executed as soon as they are added. */ -TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_TBB, priority); } @@ -418,7 +418,7 @@ TaskPool *BLI_task_pool_create(void *userdata, TaskPriority priority) * they could end never being executed, since the 'fallback' background thread is already * busy with parent task in single-threaded context). */ -TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_background(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND, priority); } @@ -428,7 +428,7 @@ TaskPool *BLI_task_pool_create_background(void *userdata, TaskPriority priority) * for until BLI_task_pool_work_and_wait() is called. This helps reducing threading * overhead when pushing huge amount of small initial tasks from the main thread. */ -TaskPool *BLI_task_pool_create_suspended(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_suspended(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_TBB_SUSPENDED, priority); } @@ -446,7 +446,7 @@ TaskPool *BLI_task_pool_create_no_threads(void *userdata) * Task pool that executes one task after the other, possibly on different threads * but never in parallel. */ -TaskPool *BLI_task_pool_create_background_serial(void *userdata, TaskPriority priority) +TaskPool *BLI_task_pool_create_background_serial(void *userdata, eTaskPriority priority) { return task_pool_create_ex(userdata, TASK_POOL_BACKGROUND_SERIAL, priority); } diff --git a/source/blender/blenlib/intern/timecode.c b/source/blender/blenlib/intern/timecode.c index 69c383061fc..13f95ddf264 100644 --- a/source/blender/blenlib/intern/timecode.c +++ b/source/blender/blenlib/intern/timecode.c @@ -18,7 +18,7 @@ */ /** \file - * \ingroup blendlib + * \ingroup bli * * Time-Code string formatting */ diff --git a/source/blender/blenlib/intern/uuid.cc b/source/blender/blenlib/intern/uuid.cc index 7f1ec0e677b..de4602bf3ed 100644 --- a/source/blender/blenlib/intern/uuid.cc +++ b/source/blender/blenlib/intern/uuid.cc @@ -18,16 +18,21 @@ * \ingroup bli */ +#include "BLI_assert.h" #include "BLI_uuid.h" #include #include +#include #include +#include +#include +#include /* Ensure the UUID struct doesn't have any padding, to be compatible with memcmp(). */ -static_assert(sizeof(UUID) == 16, "expect UUIDs to be 128 bit exactly"); +static_assert(sizeof(bUUID) == 16, "expect UUIDs to be 128 bit exactly"); -UUID BLI_uuid_generate_random() +bUUID BLI_uuid_generate_random() { static std::mt19937_64 rng = []() { std::mt19937_64 rng; @@ -37,13 +42,25 @@ UUID BLI_uuid_generate_random() static_assert(std::mt19937_64::max() == 0xffffffffffffffffLL); struct timespec ts; +#ifdef __APPLE__ + /* `timespec_get()` is only available on macOS 10.15+, so until that's the minimum version + * supported by Blender, use another function to get the timespec. + * + * `clock_gettime()` is only available on POSIX, so not on Windows; Linux uses the newer C++11 + * function `timespec_get()` as well. */ + clock_gettime(CLOCK_REALTIME, &ts); +#else timespec_get(&ts, TIME_UTC); - rng.seed(ts.tv_nsec); +#endif + /* XOR the nanosecond and second fields, just in case the clock only has seconds resolution. */ + uint64_t seed = ts.tv_nsec; + seed ^= ts.tv_sec; + rng.seed(seed); return rng; }(); - UUID uuid; + bUUID uuid; /* RFC4122 suggests setting certain bits to a fixed value, and then randomizing the remaining * bits. The opposite is easier to implement, though, so that's what's done here. */ @@ -64,12 +81,23 @@ UUID BLI_uuid_generate_random() return uuid; } -bool BLI_uuid_equal(const UUID uuid1, const UUID uuid2) +bUUID BLI_uuid_nil(void) +{ + const bUUID nil = {0, 0, 0, 0, 0, {0}}; + return nil; +} + +bool BLI_uuid_is_nil(bUUID uuid) +{ + return BLI_uuid_equal(BLI_uuid_nil(), uuid); +} + +bool BLI_uuid_equal(const bUUID uuid1, const bUUID uuid2) { return std::memcmp(&uuid1, &uuid2, sizeof(uuid1)) == 0; } -void BLI_uuid_format(char *buffer, const UUID uuid) +void BLI_uuid_format(char *buffer, const bUUID uuid) { std::sprintf(buffer, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", @@ -86,7 +114,7 @@ void BLI_uuid_format(char *buffer, const UUID uuid) uuid.node[5]); } -bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) +bool BLI_uuid_parse_string(bUUID *uuid, const char *buffer) { const int num_fields_parsed = std::sscanf( buffer, @@ -105,10 +133,79 @@ bool BLI_uuid_parse_string(UUID *uuid, const char *buffer) return num_fields_parsed == 11; } -std::ostream &operator<<(std::ostream &stream, UUID uuid) +std::ostream &operator<<(std::ostream &stream, bUUID uuid) { std::string buffer(36, '\0'); BLI_uuid_format(buffer.data(), uuid); stream << buffer; return stream; } + +namespace blender { + +bUUID::bUUID(const std::initializer_list field_values) +{ + BLI_assert_msg(field_values.size() == 11, "bUUID requires 5 regular fields + 6 `node` values"); + + const auto *field_iter = field_values.begin(); + + this->time_low = *field_iter++; + this->time_mid = static_cast(*field_iter++); + this->time_hi_and_version = static_cast(*field_iter++); + this->clock_seq_hi_and_reserved = static_cast(*field_iter++); + this->clock_seq_low = static_cast(*field_iter++); + + std::copy(field_iter, field_values.end(), this->node); +} + +bUUID::bUUID(const std::string &string_formatted_uuid) +{ + const bool parsed_ok = BLI_uuid_parse_string(this, string_formatted_uuid.c_str()); + if (!parsed_ok) { + std::stringstream ss; + ss << "invalid UUID string " << string_formatted_uuid; + throw std::runtime_error(ss.str()); + } +} + +bUUID::bUUID(const ::bUUID &struct_uuid) +{ + *(static_cast<::bUUID *>(this)) = struct_uuid; +} + +uint64_t bUUID::hash() const +{ + /* Convert the struct into two 64-bit numbers, and XOR them to get the hash. */ + const uint64_t *uuid_as_int64 = reinterpret_cast(this); + return uuid_as_int64[0] ^ uuid_as_int64[1]; +} + +bool operator==(const bUUID uuid1, const bUUID uuid2) +{ + return BLI_uuid_equal(uuid1, uuid2); +} + +bool operator!=(const bUUID uuid1, const bUUID uuid2) +{ + return !(uuid1 == uuid2); +} + +bool operator<(const bUUID uuid1, const bUUID uuid2) +{ + auto simple_fields1 = std::tie(uuid1.time_low, + uuid1.time_mid, + uuid1.time_hi_and_version, + uuid1.clock_seq_hi_and_reserved, + uuid1.clock_seq_low); + auto simple_fields2 = std::tie(uuid2.time_low, + uuid2.time_mid, + uuid2.time_hi_and_version, + uuid2.clock_seq_hi_and_reserved, + uuid2.clock_seq_low); + if (simple_fields1 == simple_fields2) { + return std::memcmp(uuid1.node, uuid2.node, sizeof(uuid1.node)) < 0; + } + return simple_fields1 < simple_fields2; +} + +} // namespace blender diff --git a/source/blender/blenlib/intern/winstuff.c b/source/blender/blenlib/intern/winstuff.c index d5c9c5cd5e6..11345fc7242 100644 --- a/source/blender/blenlib/intern/winstuff.c +++ b/source/blender/blenlib/intern/winstuff.c @@ -172,12 +172,14 @@ bool BLI_windows_register_blend_extension(const bool background) return false; } +# ifdef WITH_BLENDER_THUMBNAILER BLI_windows_get_executable_dir(InstallDir); GetSystemDirectory(SysDir, FILE_MAXDIR); ThumbHandlerDLL = "BlendThumb.dll"; snprintf( RegCmd, MAX_PATH * 2, "%s\\regsvr32 /s \"%s\\%s\"", SysDir, InstallDir, ThumbHandlerDLL); system(RegCmd); +# endif RegCloseKey(root); printf("success (%s)\n", usr_mode ? "user" : "system"); @@ -191,7 +193,7 @@ bool BLI_windows_register_blend_extension(const bool background) return true; } -void BLI_windows_get_default_root_dir(char *root) +void BLI_windows_get_default_root_dir(char root[4]) { char str[MAX_PATH + 1]; diff --git a/source/blender/blenlib/tests/BLI_function_ref_test.cc b/source/blender/blenlib/tests/BLI_function_ref_test.cc index 74f5014142c..20c9ee5885e 100644 --- a/source/blender/blenlib/tests/BLI_function_ref_test.cc +++ b/source/blender/blenlib/tests/BLI_function_ref_test.cc @@ -124,4 +124,10 @@ TEST(function_ref, CallSafeVoid) EXPECT_EQ(value, 1); } +TEST(function_ref, InitializeWithNull) +{ + FunctionRef f{nullptr}; + EXPECT_FALSE(f); +} + } // namespace blender::tests diff --git a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc index 0329fc156c0..68111fb8eb1 100644 --- a/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc +++ b/source/blender/blenlib/tests/BLI_mesh_intersect_test.cc @@ -457,8 +457,8 @@ TEST(mesh_intersect, TwoTris) {4, 11, 6, 4}, /* 9: T11 edge (-1,1,1)(0,1/2,1/2) inside T4 edge. */ {4, 12, 6, 2}, /* 10: parallel planes, not intersecting. */ {4, 13, 6, 2}, /* 11: non-parallel planes, not intersecting, all one side. */ - {0, 14, 6, 2}, /* 12: non-paralel planes, not intersecting, alternate sides. */ - /* Following are all coplanar cases. */ + {0, 14, 6, 2}, /* 12: non-parallel planes, not intersecting, alternate sides. */ + /* Following are all co-planar cases. */ {15, 16, 6, 8}, /* 13: T16 inside T15. NOTE: dup'd tri is expected. */ {15, 17, 8, 8}, /* 14: T17 intersects one edge of T15 at (1,1,0)(3,3,0). */ {15, 18, 10, 12}, /* 15: T18 intersects T15 at (1,1,0)(3,3,0)(3,15/4,1/2)(0,3,2). */ @@ -970,7 +970,7 @@ static void fill_sphere_data(int nrings, static void spheresphere_test(int nrings, double y_offset, bool use_self) { - /* Make two uvspheres with nrings rings ad 2*nrings segments. */ + /* Make two UV-spheres with nrings rings ad 2*nrings segments. */ if (nrings < 2) { return; } diff --git a/source/blender/blenlib/tests/BLI_path_util_test.cc b/source/blender/blenlib/tests/BLI_path_util_test.cc index cf5135731e2..65b02a19960 100644 --- a/source/blender/blenlib/tests/BLI_path_util_test.cc +++ b/source/blender/blenlib/tests/BLI_path_util_test.cc @@ -655,3 +655,34 @@ TEST(path_util, PathRelPath) # undef PATH_REL #endif + +/* BLI_path_contains */ +TEST(path_util, PathContains) +{ + EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path")) << "A path contains itself"; + EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/inside")) + << "A path contains its subdirectory"; + EXPECT_TRUE(BLI_path_contains("/some/path", "/some/path/../path/inside")) + << "Paths should be normalised"; + EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "C:\\some\\path\\inside")) + << "Windows paths should be supported as well"; + + EXPECT_FALSE(BLI_path_contains("C:\\some\\path", "C:\\some\\other\\path")) + << "Windows paths should be supported as well"; + EXPECT_FALSE(BLI_path_contains("/some/path", "/")) + << "Root directory not be contained in a subdirectory"; + EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path/../outside")) + << "Paths should be normalised"; + EXPECT_FALSE(BLI_path_contains("/some/path", "/some/path_library")) + << "Just sharing a suffix is not enough, path semantics should be followed"; + EXPECT_FALSE(BLI_path_contains("/some/path", "./contents")) + << "Relative paths are not supported"; +} + +#ifdef WIN32 +TEST(path_util, PathContains_Windows_case_insensitive) +{ + EXPECT_TRUE(BLI_path_contains("C:\\some\\path", "c:\\SOME\\path\\inside")) + << "On Windows path comparison should ignore case"; +} +#endif diff --git a/source/blender/blenlib/tests/BLI_serialize_test.cc b/source/blender/blenlib/tests/BLI_serialize_test.cc new file mode 100644 index 00000000000..6c55a85ca1e --- /dev/null +++ b/source/blender/blenlib/tests/BLI_serialize_test.cc @@ -0,0 +1,207 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "BLI_serialize.hh" + +/* -------------------------------------------------------------------- */ +/* tests */ + +namespace blender::io::serialize::json::testing { + +TEST(serialize, string_to_json) +{ + JsonFormatter json; + std::stringstream out; + StringValue test_value("Hello JSON"); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "\"Hello JSON\""); +} + +static void test_int_to_json(int64_t value, StringRef expected) +{ + JsonFormatter json; + std::stringstream out; + IntValue test_value(value); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), expected); +} + +TEST(serialize, int_to_json) +{ + test_int_to_json(42, "42"); + test_int_to_json(-42, "-42"); + test_int_to_json(std::numeric_limits::max(), "2147483647"); + test_int_to_json(std::numeric_limits::min(), "-2147483648"); + test_int_to_json(std::numeric_limits::max(), "9223372036854775807"); + test_int_to_json(std::numeric_limits::min(), "-9223372036854775808"); +} + +TEST(serialize, double_to_json) +{ + JsonFormatter json; + std::stringstream out; + DoubleValue test_value(42.31); + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "42.31"); +} + +TEST(serialize, null_to_json) +{ + JsonFormatter json; + std::stringstream out; + NullValue test_value; + json.serialize(out, test_value); + EXPECT_EQ(out.str(), "null"); +} + +TEST(serialize, false_to_json) +{ + JsonFormatter json; + std::stringstream out; + BooleanValue value(false); + json.serialize(out, value); + EXPECT_EQ(out.str(), "false"); +} + +TEST(serialize, true_to_json) +{ + JsonFormatter json; + std::stringstream out; + BooleanValue value(true); + json.serialize(out, value); + EXPECT_EQ(out.str(), "true"); +} + +TEST(serialize, array_to_json) +{ + JsonFormatter json; + std::stringstream out; + ArrayValue value_array; + ArrayValue::Items &array = value_array.elements(); + array.append_as(new IntValue(42)); + array.append_as(new StringValue("Hello JSON")); + array.append_as(new NullValue); + array.append_as(new BooleanValue(false)); + array.append_as(new BooleanValue(true)); + + json.serialize(out, value_array); + EXPECT_EQ(out.str(), "[42,\"Hello JSON\",null,false,true]"); +} + +TEST(serialize, object_to_json) +{ + JsonFormatter json; + std::stringstream out; + ObjectValue value_object; + ObjectValue::Items &attributes = value_object.elements(); + attributes.append_as(std::pair(std::string("best_number"), new IntValue(42))); + + json.serialize(out, value_object); + EXPECT_EQ(out.str(), "{\"best_number\":42}"); +} + +TEST(serialize, json_roundtrip_ordering) +{ + const std::string input = + "[{\"_id\":\"614ada7c476c472ecbd0ecbb\",\"index\":0,\"guid\":\"d5b81381-cef8-4327-923d-" + "41e57ff79326\",\"isActive\":false,\"balance\":\"$2,062.25\",\"picture\":\"http://" + "placehold.it/32x32\",\"age\":26,\"eyeColor\":\"brown\",\"name\":\"Geneva " + "Vega\",\"gender\":\"female\",\"company\":\"SLOGANAUT\",\"email\":\"genevavega@sloganaut." + "com\",\"phone\":\"+1 (993) 432-2805\",\"address\":\"943 Christopher Avenue, Northchase, " + "Alabama, 5769\",\"about\":\"Eu cillum qui eu fugiat sit nulla eu duis. Aliqua nulla aliqua " + "ea tempor dolor fugiat sint consectetur exercitation ipsum magna ex. Aute laborum esse " + "magna nostrud in cillum et mollit proident. Deserunt ex minim adipisicing incididunt " + "incididunt dolore velit aliqua.\\r\\n\",\"registered\":\"2014-06-02T06:29:33 " + "-02:00\",\"latitude\":-66.003108,\"longitude\":44.038986,\"tags\":[\"exercitation\"," + "\"laborum\",\"velit\",\"magna\",\"officia\",\"aliqua\",\"laboris\"],\"friends\":[{\"id\":0," + "\"name\":\"Daniel Stuart\"},{\"id\":1,\"name\":\"Jackson " + "Velez\"},{\"id\":2,\"name\":\"Browning Boyd\"}],\"greeting\":\"Hello, Geneva Vega! You " + "have 8 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7cf28685063c6722af\"," + "\"index\":1,\"guid\":\"e157edf3-a86d-4984-b18d-e2fe568a9915\",\"isActive\":false," + "\"balance\":\"$3,550.44\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":40,\"eyeColor\":\"blue\",\"name\":\"Lamb " + "Lowe\",\"gender\":\"male\",\"company\":\"PROXSOFT\",\"email\":\"lamblowe@proxsoft.com\"," + "\"phone\":\"+1 (999) 573-2855\",\"address\":\"632 Rockwell Place, Diaperville, " + "Pennsylvania, 5050\",\"about\":\"Anim dolor deserunt esse quis velit adipisicing aute " + "nostrud velit minim culpa aute et tempor. Dolor aliqua reprehenderit anim voluptate. " + "Consequat proident ut culpa reprehenderit qui. Nisi proident velit cillum voluptate. " + "Ullamco id sunt quis aute adipisicing cupidatat consequat " + "aliquip.\\r\\n\",\"registered\":\"2014-09-06T06:13:36 " + "-02:00\",\"latitude\":-44.550228,\"longitude\":-80.893356,\"tags\":[\"anim\",\"id\"," + "\"irure\",\"do\",\"officia\",\"irure\",\"Lorem\"],\"friends\":[{\"id\":0,\"name\":" + "\"Faulkner Watkins\"},{\"id\":1,\"name\":\"Cecile Schneider\"},{\"id\":2,\"name\":\"Burt " + "Lester\"}],\"greeting\":\"Hello, Lamb Lowe! You have 1 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c235335fc56bc2f78\"," + "\"index\":2,\"guid\":\"8206bad1-8274-49fd-9223-d727589f22ca\",\"isActive\":false," + "\"balance\":\"$2,548.34\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":37,\"eyeColor\":\"blue\",\"name\":\"Sallie " + "Chase\",\"gender\":\"female\",\"company\":\"FLEETMIX\",\"email\":\"salliechase@fleetmix." + "com\",\"phone\":\"+1 (953) 453-3388\",\"address\":\"865 Irving Place, Chelsea, Utah, " + "9777\",\"about\":\"In magna exercitation incididunt exercitation dolor anim. Consectetur " + "dolore commodo elit cillum dolor reprehenderit magna minim et ex labore pariatur. Nulla " + "ullamco officia velit in aute proident nostrud. Duis deserunt et labore Lorem aliqua " + "eiusmod commodo sunt.\\r\\n\",\"registered\":\"2017-03-16T08:54:53 " + "-01:00\",\"latitude\":-78.481939,\"longitude\":-149.820215,\"tags\":[\"Lorem\",\"ipsum\"," + "\"in\",\"tempor\",\"consectetur\",\"voluptate\",\"elit\"],\"friends\":[{\"id\":0,\"name\":" + "\"Gibson Garner\"},{\"id\":1,\"name\":\"Anna Frank\"},{\"id\":2,\"name\":\"Roberson " + "Daugherty\"}],\"greeting\":\"Hello, Sallie Chase! You have 7 unread " + "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c93b63ecad5f9ba5e\",\"index\":3," + "\"guid\":\"924b02fc-7c27-481a-9941-db3b9403dfe1\",\"isActive\":true,\"balance\":\"$1,633." + "60\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":29,\"eyeColor\":\"brown\",\"name\":\"Grace " + "Mccall\",\"gender\":\"female\",\"company\":\"PIVITOL\",\"email\":\"gracemccall@pivitol." + "com\",\"phone\":\"+1 (964) 541-2514\",\"address\":\"734 Schaefer Street, Topaz, Virginia, " + "9137\",\"about\":\"Amet officia magna fugiat ut pariatur fugiat elit culpa voluptate elit " + "do proident culpa minim. Commodo do minim reprehenderit ut voluptate ut velit id esse " + "consequat. Labore ullamco deserunt irure eiusmod cillum tempor incididunt qui adipisicing " + "nostrud pariatur enim aliquip. Excepteur nostrud commodo consectetur esse duis irure " + "qui.\\r\\n\",\"registered\":\"2015-04-24T03:55:17 " + "-02:00\",\"latitude\":58.801446,\"longitude\":-157.413865,\"tags\":[\"do\",\"ea\",\"eu\"," + "\"eu\",\"qui\",\"duis\",\"sint\"],\"friends\":[{\"id\":0,\"name\":\"Carrie " + "Short\"},{\"id\":1,\"name\":\"Dickerson Barnes\"},{\"id\":2,\"name\":\"Rae " + "Rios\"}],\"greeting\":\"Hello, Grace Mccall! You have 5 unread " + "messages.\",\"favoriteFruit\":\"apple\"},{\"_id\":\"614ada7c9caf1353b0e22bbf\",\"index\":4," + "\"guid\":\"e5981ae1-90e4-41c4-9905-161522db700b\",\"isActive\":false,\"balance\":\"$3,660." + "34\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":31,\"eyeColor\":\"blue\",\"name\":\"Herring " + "Powers\",\"gender\":\"male\",\"company\":\"PYRAMIA\",\"email\":\"herringpowers@pyramia." + "com\",\"phone\":\"+1 (981) 541-2829\",\"address\":\"409 Furman Avenue, Waterloo, South " + "Carolina, 380\",\"about\":\"In officia culpa aliqua culpa pariatur aliqua mollit ex. Velit " + "est Lorem enim magna cillum sunt elit consectetur deserunt ea est consectetur fugiat " + "mollit. Aute Lorem excepteur minim esse qui. Id Lorem in tempor et. Nisi aliquip laborum " + "magna eu aute.\\r\\n\",\"registered\":\"2018-07-05T07:28:54 " + "-02:00\",\"latitude\":51.497405,\"longitude\":-129.422711,\"tags\":[\"eiusmod\",\"et\"," + "\"nostrud\",\"reprehenderit\",\"Lorem\",\"cillum\",\"nulla\"],\"friends\":[{\"id\":0," + "\"name\":\"Tonia Keith\"},{\"id\":1,\"name\":\"Leanne Rice\"},{\"id\":2,\"name\":\"Craig " + "Gregory\"}],\"greeting\":\"Hello, Herring Powers! You have 6 unread " + "messages.\",\"favoriteFruit\":\"strawberry\"},{\"_id\":\"614ada7c53a3d6da77468f25\"," + "\"index\":5,\"guid\":\"abb2eec9-c4f0-4a0d-b20a-5c8e50fe88a1\",\"isActive\":true," + "\"balance\":\"$1,481.08\",\"picture\":\"http://placehold.it/" + "32x32\",\"age\":31,\"eyeColor\":\"green\",\"name\":\"Lela " + "Dillard\",\"gender\":\"female\",\"company\":\"CEMENTION\",\"email\":\"leladillard@" + "cemention.com\",\"phone\":\"+1 (856) 456-3657\",\"address\":\"391 Diamond Street, Madaket, " + "Ohio, 9337\",\"about\":\"Tempor dolor ullamco esse cillum excepteur. Excepteur aliqua non " + "enim anim esse amet cupidatat non. Cillum excepteur occaecat cupidatat elit labore. " + "Pariatur ut esse sint elit. Velit sint magna et commodo sit velit labore consectetur irure " + "officia proident aliquip. Aliqua dolore ipsum voluptate veniam deserunt amet irure. Cillum " + "consequat veniam proident Lorem in anim enim veniam ea " + "nulla.\\r\\n\",\"registered\":\"2017-01-11T11:07:22 " + "-01:00\",\"latitude\":86.349081,\"longitude\":-179.983754,\"tags\":[\"consequat\"," + "\"labore\",\"consectetur\",\"dolor\",\"laborum\",\"eiusmod\",\"in\"],\"friends\":[{\"id\":" + "0,\"name\":\"Hancock Rivera\"},{\"id\":1,\"name\":\"Chasity " + "Oneil\"},{\"id\":2,\"name\":\"Whitaker Barr\"}],\"greeting\":\"Hello, Lela Dillard! You " + "have 3 unread messages.\",\"favoriteFruit\":\"strawberry\"}]"; + std::stringstream is(input); + + JsonFormatter json; + std::unique_ptr value = json.deserialize(is); + EXPECT_EQ(value->type(), eValueType::Array); + + std::stringstream out; + json.serialize(out, *value); + EXPECT_EQ(out.str(), input); +} + +} // namespace blender::io::serialize::json::testing diff --git a/source/blender/blenlib/tests/BLI_set_test.cc b/source/blender/blenlib/tests/BLI_set_test.cc index 3a4733a218f..28eb4df2ca6 100644 --- a/source/blender/blenlib/tests/BLI_set_test.cc +++ b/source/blender/blenlib/tests/BLI_set_test.cc @@ -544,6 +544,32 @@ TEST(set, GenericAlgorithms) EXPECT_EQ(std::count(set.begin(), set.end(), 20), 1); } +TEST(set, RemoveDuringIteration) +{ + Set set; + set.add(6); + set.add(5); + set.add(2); + set.add(3); + + EXPECT_EQ(set.size(), 4); + + using Iter = Set::Iterator; + Iter begin = set.begin(); + Iter end = set.end(); + for (Iter iter = begin; iter != end; ++iter) { + int item = *iter; + if (item == 2) { + set.remove(iter); + } + } + + EXPECT_EQ(set.size(), 3); + EXPECT_TRUE(set.contains(5)); + EXPECT_TRUE(set.contains(6)); + EXPECT_TRUE(set.contains(3)); +} + /** * Set this to 1 to activate the benchmark. It is disabled by default, because it prints a lot. */ diff --git a/source/blender/blenlib/tests/BLI_string_search_test.cc b/source/blender/blenlib/tests/BLI_string_search_test.cc index 0d1fd2cab96..aa1c0b41c44 100644 --- a/source/blender/blenlib/tests/BLI_string_search_test.cc +++ b/source/blender/blenlib/tests/BLI_string_search_test.cc @@ -8,6 +8,9 @@ namespace blender::string_search::tests { +/* Right arrow, keep in sync with #UI_MENU_ARROW_SEP in `UI_interface.h`. */ +#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" + TEST(string_search, damerau_levenshtein_distance) { EXPECT_EQ(damerau_levenshtein_distance("test", "test"), 0); @@ -30,14 +33,17 @@ TEST(string_search, get_fuzzy_match_errors) EXPECT_EQ(get_fuzzy_match_errors("", "abc"), 0); EXPECT_EQ(get_fuzzy_match_errors("hello", "hallo"), 1); EXPECT_EQ(get_fuzzy_match_errors("hap", "hello"), -1); - EXPECT_EQ(get_fuzzy_match_errors("armature", "▶restore"), -1); + EXPECT_EQ(get_fuzzy_match_errors("armature", UI_MENU_ARROW_SEP "restore"), -1); } TEST(string_search, extract_normalized_words) { LinearAllocator<> allocator; Vector words; - extract_normalized_words("hello world▶test another test▶ 3", allocator, words); + extract_normalized_words("hello world" UI_MENU_ARROW_SEP "test another test" UI_MENU_ARROW_SEP + " 3", + allocator, + words); EXPECT_EQ(words.size(), 6); EXPECT_EQ(words[0], "hello"); EXPECT_EQ(words[1], "world"); diff --git a/source/blender/blenlib/tests/BLI_uuid_test.cc b/source/blender/blenlib/tests/BLI_uuid_test.cc index 2c9da920897..111c21cb7d1 100644 --- a/source/blender/blenlib/tests/BLI_uuid_test.cc +++ b/source/blender/blenlib/tests/BLI_uuid_test.cc @@ -18,29 +18,31 @@ #include "BLI_uuid.h" +namespace blender::tests { + TEST(BLI_uuid, generate_random) { - const UUID uuid = BLI_uuid_generate_random(); + const bUUID uuid = BLI_uuid_generate_random(); - // The 4 MSbits represent the "version" of the UUID. + /* The 4 MSbits represent the "version" of the UUID. */ const uint16_t version = uuid.time_hi_and_version >> 12; EXPECT_EQ(version, 4); - // The 2 MSbits should be 0b10, indicating compliance with RFC4122. + /* The 2 MSbits should be 0b10, indicating compliance with RFC4122. */ const uint8_t reserved = uuid.clock_seq_hi_and_reserved >> 6; EXPECT_EQ(reserved, 0b10); } TEST(BLI_uuid, generate_many_random) { - const UUID first_uuid = BLI_uuid_generate_random(); + const bUUID first_uuid = BLI_uuid_generate_random(); /* Generate lots of UUIDs to get some indication that the randomness is okay. */ for (int i = 0; i < 1000000; ++i) { - const UUID uuid = BLI_uuid_generate_random(); - EXPECT_FALSE(BLI_uuid_equal(first_uuid, uuid)); + const bUUID uuid = BLI_uuid_generate_random(); + EXPECT_NE(first_uuid, uuid); - // Check that the non-random bits are set according to RFC4122. + /* Check that the non-random bits are set according to RFC4122. */ const uint16_t version = uuid.time_hi_and_version >> 12; EXPECT_EQ(version, 4); const uint8_t reserved = uuid.clock_seq_hi_and_reserved >> 6; @@ -48,18 +50,57 @@ TEST(BLI_uuid, generate_many_random) } } +TEST(BLI_uuid, nil_value) +{ + const bUUID nil_uuid = BLI_uuid_nil(); + const bUUID zeroes_uuid{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + const bUUID default_constructed{}; + + EXPECT_EQ(nil_uuid, zeroes_uuid); + EXPECT_TRUE(BLI_uuid_is_nil(nil_uuid)); + EXPECT_TRUE(BLI_uuid_is_nil(default_constructed)) + << "Default constructor should produce the nil value."; + + std::string buffer(36, '\0'); + BLI_uuid_format(buffer.data(), nil_uuid); + EXPECT_EQ("00000000-0000-0000-0000-000000000000", buffer); +} + TEST(BLI_uuid, equality) { - const UUID uuid1 = BLI_uuid_generate_random(); - const UUID uuid2 = BLI_uuid_generate_random(); + const bUUID uuid1 = BLI_uuid_generate_random(); + const bUUID uuid2 = BLI_uuid_generate_random(); - EXPECT_TRUE(BLI_uuid_equal(uuid1, uuid1)); - EXPECT_FALSE(BLI_uuid_equal(uuid1, uuid2)); + EXPECT_EQ(uuid1, uuid1); + EXPECT_NE(uuid1, uuid2); +} + +TEST(BLI_uuid, comparison_trivial) +{ + const bUUID uuid0{}; + const bUUID uuid1("11111111-1111-1111-1111-111111111111"); + const bUUID uuid2("22222222-2222-2222-2222-222222222222"); + + EXPECT_LT(uuid0, uuid1); + EXPECT_LT(uuid0, uuid2); + EXPECT_LT(uuid1, uuid2); +} + +TEST(BLI_uuid, comparison_byte_order_check) +{ + const bUUID uuid0{}; + /* Chosen to test byte ordering is taken into account correctly when comparing. */ + const bUUID uuid12("12222222-2222-2222-2222-222222222222"); + const bUUID uuid21("21111111-1111-1111-1111-111111111111"); + + EXPECT_LT(uuid0, uuid12); + EXPECT_LT(uuid0, uuid21); + EXPECT_LT(uuid12, uuid21); } TEST(BLI_uuid, string_formatting) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); memset(&uuid, 0, sizeof(uuid)); @@ -78,12 +119,12 @@ TEST(BLI_uuid, string_formatting) EXPECT_EQ("00000001-0002-0003-0405-060000000007", buffer); /* Somewhat more complex bit patterns. This is a version 1 UUID generated from Python. */ - const UUID uuid1 = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; + const bUUID uuid1 = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; BLI_uuid_format(buffer.data(), uuid1); EXPECT_EQ("d30a0e60-14a2-11ec-8b99-f7736944db8b", buffer); /* Namespace UUID, example listed in RFC4211. */ - const UUID namespace_dns = { + const bUUID namespace_dns = { 0x6ba7b810, 0x9dad, 0x11d1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}; BLI_uuid_format(buffer.data(), namespace_dns); EXPECT_EQ("6ba7b810-9dad-11d1-80b4-00c04fd430c8", buffer); @@ -91,7 +132,7 @@ TEST(BLI_uuid, string_formatting) TEST(BLI_uuid, string_parsing_ok) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); const bool parsed_ok = BLI_uuid_parse_string(&uuid, "d30a0e60-14a2-11ec-8b99-f7736944db8b"); @@ -102,7 +143,7 @@ TEST(BLI_uuid, string_parsing_ok) TEST(BLI_uuid, string_parsing_capitalisation) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); /* RFC4122 demands acceptance of upper-case hex digits. */ @@ -116,7 +157,7 @@ TEST(BLI_uuid, string_parsing_capitalisation) TEST(BLI_uuid, string_parsing_fail) { - UUID uuid; + bUUID uuid; std::string buffer(36, '\0'); const bool parsed_ok = BLI_uuid_parse_string(&uuid, "d30a0e60!14a2-11ec-8b99-f7736944db8b"); @@ -126,7 +167,9 @@ TEST(BLI_uuid, string_parsing_fail) TEST(BLI_uuid, stream_operator) { std::stringstream ss; - const UUID uuid = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; + const bUUID uuid = {3540651616, 5282, 4588, 139, 153, 0xf7, 0x73, 0x69, 0x44, 0xdb, 0x8b}; ss << uuid; EXPECT_EQ(ss.str(), "d30a0e60-14a2-11ec-8b99-f7736944db8b"); } + +} // namespace blender::tests diff --git a/source/blender/blenloader/BLO_blend_validate.h b/source/blender/blenloader/BLO_blend_validate.h index 78aa481d4b1..cdbf4bdd952 100644 --- a/source/blender/blenloader/BLO_blend_validate.h +++ b/source/blender/blenloader/BLO_blend_validate.h @@ -21,7 +21,7 @@ /** \file * \ingroup blenloader - * \brief Utils ensuring .blend file (i.e. Main) + * \brief Utilities ensuring `.blend` file (i.e. Main) * is in valid state during write and/or read process. */ diff --git a/source/blender/blenloader/BLO_read_write.h b/source/blender/blenloader/BLO_read_write.h index 86c7c367816..ca7ea6d8f09 100644 --- a/source/blender/blenloader/BLO_read_write.h +++ b/source/blender/blenloader/BLO_read_write.h @@ -43,7 +43,7 @@ /* for SDNA_TYPE_FROM_STRUCT() macro */ #include "dna_type_offsets.h" -#include "DNA_windowmanager_types.h" /* for ReportType */ +#include "DNA_windowmanager_types.h" /* for eReportType */ #ifdef __cplusplus extern "C" { @@ -69,28 +69,28 @@ struct ReportList; * DNA Struct Writing * ------------------ * - * Functions dealing with DNA structs begin with BLO_write_struct_*. + * Functions dealing with DNA structs begin with `BLO_write_struct_*`. * * DNA struct types can be identified in different ways: - * - Run-time Name: The name is provided as const char *. - * - Compile-time Name: The name is provided at compile time. This is more efficient. - * - Struct ID: Every DNA struct type has an integer ID that can be queried with - * BLO_get_struct_id_by_name. Providing this ID can be a useful optimization when many structs - * of the same type are stored AND if those structs are not in a continuous array. + * - Run-time Name: The name is provided as `const char *`. + * - Compile-time Name: The name is provided at compile time. This is more efficient. + * - Struct ID: Every DNA struct type has an integer ID that can be queried with + * #BLO_get_struct_id_by_name. Providing this ID can be a useful optimization when many + * structs of the same type are stored AND if those structs are not in a continuous array. * * Often only a single instance of a struct is written at once. However, sometimes it is necessary * to write arrays or linked lists. Separate functions for that are provided as well. * - * There is a special macro for writing id structs: BLO_write_id_struct. Those are handled - * differently from other structs. + * There is a special macro for writing id structs: #BLO_write_id_struct. + * Those are handled differently from other structs. * * Raw Data Writing * ---------------- * - * At the core there is BLO_write_raw, which can write arbitrary memory buffers to the file. The - * code that reads this data might have to correct its byte-order. For the common cases there are - * convenience functions that write and read arrays of simple types such as int32. Those will - * correct endianness automatically. + * At the core there is #BLO_write_raw, which can write arbitrary memory buffers to the file. + * The code that reads this data might have to correct its byte-order. For the common cases + * there are convenience functions that write and read arrays of simple types such as `int32`. + * Those will correct endianness automatically. */ /* Mapping between names and ids. */ @@ -252,7 +252,7 @@ void BLO_expand_id(BlendExpander *expander, struct ID *id); */ void BLO_reportf_wrap(struct BlendFileReadReport *reports, - ReportType type, + eReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(3, 4); diff --git a/source/blender/blenloader/BLO_readfile.h b/source/blender/blenloader/BLO_readfile.h index 4b7f29dd7dc..5a919ae3605 100644 --- a/source/blender/blenloader/BLO_readfile.h +++ b/source/blender/blenloader/BLO_readfile.h @@ -32,11 +32,13 @@ extern "C" { struct BHead; struct BlendThumbnail; +struct Collection; struct FileData; struct LinkNode; struct ListBase; struct Main; struct MemFile; +struct Object; struct ReportList; struct Scene; struct UserDef; @@ -111,8 +113,18 @@ typedef struct BlendFileReadReport { /* Some sub-categories of the above `missing_linked_id` counter. */ int missing_obdata; int missing_obproxies; + /* Number of root override IDs that were resynced. */ int resynced_lib_overrides; + + /* Number of (non-converted) linked proxies. */ + int linked_proxies; + /* Number of proxies converted to library overrides. */ + int proxies_to_lib_overrides_success; + /* Number of proxies that failed to convert to library overrides. */ + int proxies_to_lib_overrides_failures; + /* Number of sequencer strips that were not read because were in non-supported channels. */ + int vse_strips_skipped; } count; /* Number of libraries which had overrides that needed to be resynced, and a single linked list @@ -153,10 +165,10 @@ void BLO_blendfiledata_free(BlendFileData *bfd); /** \name BLO Blend File Handle API * \{ */ -struct BLODataBlockInfo { +typedef struct BLODataBlockInfo { char name[64]; /* MAX_NAME */ struct AssetMetaData *asset_data; -}; +} BLODataBlockInfo; BlendHandle *BLO_blendhandle_from_file(const char *filepath, struct BlendFileReadReport *reports); BlendHandle *BLO_blendhandle_from_memory(const void *mem, @@ -168,9 +180,8 @@ struct LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, const bool use_assets_only, int *r_tot_names); -struct LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, - int ofblocktype, - int *r_tot_info_items); +struct LinkNode * /*BLODataBlockInfo */ BLO_blendhandle_get_datablock_info( + BlendHandle *bh, int ofblocktype, const bool use_assets_only, int *r_tot_info_items); struct LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *r_tot_prev); struct PreviewImage *BLO_blendhandle_get_preview_for_id(BlendHandle *bh, int ofblocktype, @@ -209,6 +220,20 @@ typedef enum eBLOLibLinkFlags { * don't need to remember to set this flag. */ BLO_LIBLINK_NEEDS_ID_TAG_DOIT = 1 << 18, + /** Set fake user on appended IDs. */ + BLO_LIBLINK_APPEND_SET_FAKEUSER = 1 << 19, + /** Append (make local) also indirect dependencies of appended IDs coming from other libraries. + * NOTE: All IDs (including indirectly linked ones) coming from the same initial library are + * always made local. */ + BLO_LIBLINK_APPEND_RECURSIVE = 1 << 20, + /** Try to re-use previously appended matching ID on new append. */ + BLO_LIBLINK_APPEND_LOCAL_ID_REUSE = 1 << 21, + /** Clear the asset data. */ + BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR = 1 << 22, + /** Instantiate object data IDs (i.e. create objects for them if needed). */ + BLO_LIBLINK_OBDATA_INSTANCE = 1 << 24, + /** Instantiate collections as empties, instead of linking them into current view layer. */ + BLO_LIBLINK_COLLECTION_INSTANCE = 1 << 25, } eBLOLibLinkFlags; /** @@ -312,6 +337,14 @@ void BLO_sanitize_experimental_features_userpref_blend(struct UserDef *userdef); struct BlendThumbnail *BLO_thumbnail_from_file(const char *filepath); +void BLO_object_instantiate_object_base_instance_init(struct Main *bmain, + struct Collection *collection, + struct Object *ob, + struct ViewLayer *view_layer, + const struct View3D *v3d, + const int flag, + bool set_active); + /* datafiles (generated theme) */ extern const struct bTheme U_theme_default; extern const struct UserDef U_default; diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index f88b470809c..3306eb9e454 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -170,17 +170,19 @@ LinkNode *BLO_blendhandle_get_datablock_names(BlendHandle *bh, } /** - * Gets the names and asset-data (if ID is an asset) of all the data-blocks in a file of a certain - * type (e.g. all the scene names in a file). + * Gets the names and asset-data (if ID is an asset) of data-blocks in a file of a certain type. + * The data-blocks can be limited to assets. * * \param bh: The blendhandle to access. * \param ofblocktype: The type of names to get. + * \param use_assets_only: Limit the result to assets only. * \param tot_info_items: The length of the returned list. * \return A BLI_linklist of BLODataBlockInfo *. The links and #BLODataBlockInfo.asset_data should * be freed with MEM_freeN. */ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, int ofblocktype, + const bool use_assets_only, int *r_tot_info_items) { FileData *fd = (FileData *)bh; @@ -189,27 +191,34 @@ LinkNode *BLO_blendhandle_get_datablock_info(BlendHandle *bh, int tot = 0; for (bhead = blo_bhead_first(fd); bhead; bhead = blo_bhead_next(fd, bhead)) { + if (bhead->code == ENDB) { + break; + } if (bhead->code == ofblocktype) { - struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__); const char *name = blo_bhead_id_name(fd, bhead) + 2; + AssetMetaData *asset_meta_data = blo_bhead_id_asset_data_address(fd, bhead); - STRNCPY(info->name, name); + const bool is_asset = asset_meta_data != NULL; + const bool skip_datablock = use_assets_only && !is_asset; + if (skip_datablock) { + continue; + } + struct BLODataBlockInfo *info = MEM_mallocN(sizeof(*info), __func__); /* Lastly, read asset data from the following blocks. */ - info->asset_data = blo_bhead_id_asset_data_address(fd, bhead); - if (info->asset_data) { - bhead = blo_read_asset_data_block(fd, bhead, &info->asset_data); - /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the next - * non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */ + if (asset_meta_data) { + bhead = blo_read_asset_data_block(fd, bhead, &asset_meta_data); + /* blo_read_asset_data_block() reads all DATA heads and already advances bhead to the + * next non-DATA one. Go back, so the loop doesn't skip the non-DATA head. */ bhead = blo_bhead_prev(fd, bhead); } + STRNCPY(info->name, name); + info->asset_data = asset_meta_data; + BLI_linklist_prepend(&infos, info); tot++; } - else if (bhead->code == ENDB) { - break; - } } *r_tot_info_items = tot; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 15653264211..e4fe3e8da00 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -222,7 +222,7 @@ typedef struct BHeadN { * bit kludge but better than doubling up on prints, * we could alternatively have a versions of a report function which forces printing - campbell */ -void BLO_reportf_wrap(BlendFileReadReport *reports, ReportType type, const char *format, ...) +void BLO_reportf_wrap(BlendFileReadReport *reports, eReportType type, const char *format, ...) { char fixed_buf[1024]; /* should be long enough */ @@ -1230,13 +1230,13 @@ static FileData *blo_filedata_from_file_descriptor(const char *filepath, else if (BLI_file_magic_is_gzip(header)) { file = BLI_filereader_new_gzip(rawfile); if (file != NULL) { - rawfile = NULL; /* The Gzip FileReader takes ownership of `rawfile`. */ + rawfile = NULL; /* The `Gzip` #FileReader takes ownership of `rawfile`. */ } } else if (BLI_file_magic_is_zstd(header)) { file = BLI_filereader_new_zstd(rawfile); if (file != NULL) { - rawfile = NULL; /* The Zstd FileReader takes ownership of `rawfile`. */ + rawfile = NULL; /* The `Zstd` #FileReader takes ownership of `rawfile`. */ } } @@ -1351,7 +1351,6 @@ FileData *blo_filedata_from_memfile(MemFile *memfile, void blo_filedata_free(FileData *fd) { if (fd) { - fd->file->close(fd->file); /* Free all BHeadN data blocks */ #ifndef NDEBUG @@ -1365,6 +1364,7 @@ void blo_filedata_free(FileData *fd) MEM_freeN(new_bhead); } #endif + fd->file->close(fd->file); if (fd->filesdna) { DNA_sdna_free(fd->filesdna); @@ -2195,6 +2195,13 @@ static void direct_link_id_common( /* Initialize with provided tag. */ id->tag = tag; + if (ID_IS_LINKED(id)) { + id->library_weak_reference = NULL; + } + else { + BLO_read_data_address(reader, &id->library_weak_reference); + } + if (tag & LIB_TAG_ID_LINK_PLACEHOLDER) { /* For placeholder we only need to set the tag and properly initialize generic ID fields above, * no further data to read. */ @@ -2204,6 +2211,9 @@ static void direct_link_id_common( if (id->asset_data) { BLO_read_data_address(reader, &id->asset_data); BKE_asset_metadata_read(reader, id->asset_data); + /* Restore runtime asset type info. */ + const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id); + id->asset_data->local_type_info = id_type->asset_type_info; } /* Link direct data of ID properties. */ @@ -4020,11 +4030,13 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath) * does not always properly handle user counts, and/or that function does not take into * account old, deprecated data. */ BKE_main_id_refcount_recompute(bfd->main, false); - - /* After all data has been read and versioned, uses LIB_TAG_NEW. */ - ntreeUpdateAllNew(bfd->main); } + /* After all data has been read and versioned, uses LIB_TAG_NEW. Theoretically this should + * not be calculated in the undo case, but it is currently needed even on undo to recalculate + * a cache. */ + ntreeUpdateAllNew(bfd->main); + placeholders_ensure_valid(bfd->main); BKE_main_id_tag_all(bfd->main, LIB_TAG_NEW, false); @@ -4462,7 +4474,7 @@ static bool object_in_any_collection(Main *bmain, Object *ob) * Shared operations to perform on the object's base after adding it to the scene. */ static void object_base_instance_init( - Object *ob, bool set_selected, bool set_active, ViewLayer *view_layer, const View3D *v3d) + Object *ob, ViewLayer *view_layer, const View3D *v3d, const int flag, bool set_active) { Base *base = BKE_view_layer_base_find(view_layer, ob); @@ -4470,7 +4482,9 @@ static void object_base_instance_init( base->local_view_bits |= v3d->local_view_uuid; } - if (set_selected) { + if (flag & FILE_AUTOSELECT) { + /* All objects that use #FILE_AUTOSELECT must be selectable (unless linking data). */ + BLI_assert((base->flag & BASE_SELECTABLE) || (flag & FILE_LINK)); if (base->flag & BASE_SELECTABLE) { base->flag |= BASE_SELECTED; } @@ -4483,13 +4497,36 @@ static void object_base_instance_init( BKE_scene_object_base_flag_sync_from_base(base); } +/** + * Exported for link/append to create objects as well. + */ +void BLO_object_instantiate_object_base_instance_init(Main *bmain, + Collection *collection, + Object *ob, + ViewLayer *view_layer, + const View3D *v3d, + const int flag, + bool set_active) +{ + /* Auto-select and appending. */ + if ((flag & FILE_AUTOSELECT) && ((flag & FILE_LINK) == 0)) { + /* While in general the object should not be manipulated, + * when the user requests the object to be selected, ensure it's visible and selectable. */ + ob->visibility_flag &= ~(OB_HIDE_VIEWPORT | OB_HIDE_SELECT); + } + + BKE_collection_object_add(bmain, collection, ob); + + object_base_instance_init(ob, view_layer, v3d, flag, set_active); +} + static void add_loose_objects_to_scene(Main *mainvar, Main *bmain, Scene *scene, ViewLayer *view_layer, const View3D *v3d, Library *lib, - const short flag) + const int flag) { Collection *active_collection = NULL; const bool do_append = (flag & FILE_LINK) == 0; @@ -4499,7 +4536,10 @@ static void add_loose_objects_to_scene(Main *mainvar, /* Give all objects which are LIB_TAG_INDIRECT a base, * or for a collection when *lib has been set. */ LISTBASE_FOREACH (Object *, ob, &mainvar->objects) { - bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0; + /* NOTE: Even if this is a directly linked object and is tagged for instantiation, it might + * have already been instantiated through one of its owner collections, in which case we do not + * want to re-instantiate it in the active collection here. */ + bool do_it = (ob->id.tag & LIB_TAG_DOIT) != 0 && !BKE_scene_object_find(scene, ob); if (do_it || ((ob->id.tag & LIB_TAG_INDIRECT) != 0 && (ob->id.tag & LIB_TAG_PRE_EXISTING) == 0)) { if (do_append) { @@ -4528,13 +4568,11 @@ static void add_loose_objects_to_scene(Main *mainvar, CLAMP_MIN(ob->id.us, 0); ob->mode = OB_MODE_OBJECT; - BKE_collection_object_add(bmain, active_collection, ob); - - const bool set_selected = (flag & FILE_AUTOSELECT) != 0; /* Do NOT make base active here! screws up GUI stuff, * if you want it do it at the editor level. */ const bool set_active = false; - object_base_instance_init(ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, flag, set_active); ob->id.tag &= ~LIB_TAG_INDIRECT; ob->id.flag &= ~LIB_INDIRECT_WEAK_LINK; @@ -4549,9 +4587,9 @@ static void add_loose_object_data_to_scene(Main *mainvar, Scene *scene, ViewLayer *view_layer, const View3D *v3d, - const short flag) + const int flag) { - if ((flag & FILE_OBDATA_INSTANCE) == 0) { + if ((flag & BLO_LIBLINK_OBDATA_INSTANCE) == 0) { return; } @@ -4590,13 +4628,11 @@ static void add_loose_object_data_to_scene(Main *mainvar, id_us_plus(id); BKE_object_materials_test(bmain, ob, ob->data); - BKE_collection_object_add(bmain, active_collection, ob); - - const bool set_selected = (flag & FILE_AUTOSELECT) != 0; /* Do NOT make base active here! screws up GUI stuff, * if you want it do it at the editor level. */ bool set_active = false; - object_base_instance_init(ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, flag, set_active); copy_v3_v3(ob->loc, scene->cursor.location); } @@ -4610,7 +4646,7 @@ static void add_collections_to_scene(Main *mainvar, ViewLayer *view_layer, const View3D *v3d, Library *lib, - const short flag) + const int flag) { Collection *active_collection = scene->master_collection; if (flag & FILE_ACTIVE_COLLECTION) { @@ -4620,7 +4656,7 @@ static void add_collections_to_scene(Main *mainvar, /* Give all objects which are tagged a base. */ LISTBASE_FOREACH (Collection *, collection, &mainvar->collections) { - if ((flag & FILE_COLLECTION_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) { + if ((flag & BLO_LIBLINK_COLLECTION_INSTANCE) && (collection->id.tag & LIB_TAG_DOIT)) { /* Any indirect collection should not have been tagged. */ BLI_assert((collection->id.tag & LIB_TAG_INDIRECT) == 0); @@ -4629,13 +4665,12 @@ static void add_collections_to_scene(Main *mainvar, ob->type = OB_EMPTY; ob->empty_drawsize = U.collection_instance_empty_size; - BKE_collection_object_add(bmain, active_collection, ob); - const bool set_selected = (flag & FILE_AUTOSELECT) != 0; /* TODO: why is it OK to make this active here but not in other situations? * See other callers of #object_base_instance_init */ const bool set_active = set_selected; - object_base_instance_init(ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, flag, set_active); DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); @@ -4769,7 +4804,7 @@ int BLO_library_link_copypaste(Main *mainl, BlendHandle *bh, const uint64_t id_t if (blo_bhead_is_id_valid_type(bhead) && BKE_idtype_idcode_is_linkable((short)bhead->code) && (id_types_mask == 0 || (BKE_idtype_idcode_to_idfilter((short)bhead->code) & id_types_mask) != 0)) { - read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_INDIRECT, false, &id); + read_libblock(fd, mainl, bhead, LIB_TAG_NEED_EXPAND | LIB_TAG_EXTERN, false, &id); num_directly_linked++; } @@ -4778,6 +4813,13 @@ int BLO_library_link_copypaste(Main *mainl, BlendHandle *bh, const uint64_t id_t ListBase *lb = which_libbase(mainl, GS(id->name)); id_sort_by_name(lb, id, NULL); + /* Tag as loose object (or data associated with objects) + * needing to be instantiated (see also #link_named_part and its usage of + * #BLO_LIBLINK_NEEDS_ID_TAG_DOIT above). */ + if (library_link_idcode_needs_tag_check(GS(id->name), BLO_LIBLINK_NEEDS_ID_TAG_DOIT)) { + id->tag |= LIB_TAG_DOIT; + } + if (bhead->code == ID_OB) { /* Instead of instancing Base's directly, postpone until after collections are loaded * otherwise the base's flag is set incorrectly when collections are used */ @@ -4823,7 +4865,7 @@ static bool library_link_idcode_needs_tag_check(const short idcode, const int fl if (ELEM(idcode, ID_OB, ID_GR)) { return true; } - if (flag & FILE_OBDATA_INSTANCE) { + if (flag & BLO_LIBLINK_OBDATA_INSTANCE) { if (OB_DATA_SUPPORT_ID(idcode)) { return true; } @@ -5023,7 +5065,6 @@ static void library_link_end(Main *mainl, add_main_to_main(mainvar, main_newid); } - BKE_main_free(main_newid); blo_join_main((*fd)->mainlist); mainvar = (*fd)->mainlist->first; MEM_freeN((*fd)->mainlist); @@ -5037,6 +5078,15 @@ static void library_link_end(Main *mainl, placeholders_ensure_valid(mainvar); + /* Apply overrides of newly linked data if needed. Already existing IDs need to split out, to + * avoid re-applying their own overrides. */ + BLI_assert(BKE_main_is_empty(main_newid)); + split_main_newid(mainvar, main_newid); + BKE_lib_override_library_main_validate(main_newid, (*fd)->reports->reports); + BKE_lib_override_library_main_update(main_newid); + add_main_to_main(mainvar, main_newid); + BKE_main_free(main_newid); + BKE_main_id_tag_all(mainvar, LIB_TAG_NEW, false); /* Make all relative paths, relative to the open blend file. */ diff --git a/source/blender/blenloader/intern/readfile.h b/source/blender/blenloader/intern/readfile.h index beeed8e45ae..75152a05063 100644 --- a/source/blender/blenloader/intern/readfile.h +++ b/source/blender/blenloader/intern/readfile.h @@ -31,7 +31,7 @@ #include "BLI_filereader.h" #include "DNA_sdna_types.h" #include "DNA_space_types.h" -#include "DNA_windowmanager_types.h" /* for ReportType */ +#include "DNA_windowmanager_types.h" /* for eReportType */ struct BLI_mmap_file; struct BLOCacheStorage; diff --git a/source/blender/blenloader/intern/versioning_260.c b/source/blender/blenloader/intern/versioning_260.c index b71dd5a27bb..c4d04392cba 100644 --- a/source/blender/blenloader/intern/versioning_260.c +++ b/source/blender/blenloader/intern/versioning_260.c @@ -1384,7 +1384,6 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) tex->iuser.frames = 1; tex->iuser.sfra = 1; - tex->iuser.ok = 1; } } } @@ -1800,7 +1799,7 @@ void blo_do_versions_260(FileData *fd, Library *UNUSED(lib), Main *bmain) } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= SEQ_SHOW_GPENCIL; + sseq->flag |= SEQ_PREVIEW_SHOW_GPENCIL; break; } case SPACE_IMAGE: { diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index fa15e541e43..54d1efab7dd 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -651,13 +651,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) mat->line_col[3] = mat->alpha; } } - - if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - scene->r.preview_start_resolution = 64; - } - } } if (!MAIN_VERSION_ATLEAST(bmain, 271, 3)) { @@ -698,15 +691,6 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - if (!MAIN_VERSION_ATLEAST(bmain, 272, 0)) { - if (!DNA_struct_elem_find(fd->filesdna, "RenderData", "int", "preview_start_resolution")) { - Scene *scene; - for (scene = bmain->scenes.first; scene; scene = scene->id.next) { - scene->r.preview_start_resolution = 64; - } - } - } - if (!MAIN_VERSION_ATLEAST(bmain, 272, 1)) { Brush *br; for (br = bmain->brushes.first; br; br = br->id.next) { diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index bf0463432db..4333bdb851c 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -1717,18 +1717,8 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #blo_do_versions_280 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + /* Old forgotten versioning code. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { /* Paint Brush. This ensure that the brush paints by default. Used during the development and * patch review of the initial Sculpt Vertex Colors implementation (D5975) */ LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { @@ -1748,6 +1738,20 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) } } } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #blo_do_versions_280 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ + } } /* NOTE: This version patch is intended for versions < 2.52.2, @@ -1774,7 +1778,7 @@ static void do_versions_seq_set_cache_defaults(Editing *ed) static bool seq_update_flags_cb(Sequence *seq, void *UNUSED(user_data)) { - seq->flag &= ~(SEQ_FLAG_UNUSED_6 | SEQ_FLAG_UNUSED_18 | SEQ_FLAG_UNUSED_19 | SEQ_FLAG_UNUSED_21); + seq->flag &= ~((1 << 6) | (1 << 18) | (1 << 19) | (1 << 21)); if (seq->type == SEQ_TYPE_SPEED) { SpeedControlVars *s = (SpeedControlVars *)seq->effectdata; s->flags &= ~(SEQ_SPEED_UNUSED_1); @@ -3394,7 +3398,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) SpaceImage *sima = (SpaceImage *)sl; sima->flag &= ~(SI_FLAG_UNUSED_0 | SI_FLAG_UNUSED_1 | SI_FLAG_UNUSED_3 | SI_FLAG_UNUSED_6 | SI_FLAG_UNUSED_7 | SI_FLAG_UNUSED_8 | - SI_FLAG_UNUSED_17 | SI_FLAG_UNUSED_18 | SI_FLAG_UNUSED_23 | + SI_FLAG_UNUSED_17 | SI_CUSTOM_GRID | SI_FLAG_UNUSED_23 | SI_FLAG_UNUSED_24); break; } @@ -3416,8 +3420,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) case SPACE_FILE: { SpaceFile *sfile = (SpaceFile *)sl; if (sfile->params) { - sfile->params->flag &= ~(FILE_APPEND_SET_FAKEUSER | FILE_APPEND_RECURSIVE | - FILE_OBDATA_INSTANCE); + sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 | + FILE_PARAMS_FLAG_UNUSED_3); } break; } @@ -4513,7 +4517,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!DNA_struct_elem_find(fd->filesdna, "Image", "ListBase", "tiles")) { for (Image *ima = bmain->images.first; ima; ima = ima->id.next) { ImageTile *tile = MEM_callocN(sizeof(ImageTile), "Image Tile"); - tile->ok = 1; tile->tile_number = 1001; BLI_addtail(&ima->tiles, tile); } @@ -4966,7 +4969,7 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) for (SpaceLink *sl = area->spacedata.first; sl; sl = sl->next) { if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= SEQ_SHOW_FCURVES; + sseq->flag |= SEQ_TIMELINE_SHOW_FCURVES; } } } @@ -5066,17 +5069,8 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - #do_versions_after_linking_280 in this file. - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { + /* Old forgotten versioning code. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { /* Set the cloth wind factor to 1 for old forces. */ if (!DNA_struct_elem_find(fd->filesdna, "PartDeflect", "float", "f_wind_factor")) { LISTBASE_FOREACH (Object *, ob, &bmain->objects) { @@ -5096,10 +5090,22 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { /* Don't rotate light with the viewer by default, make it fixed. Shading settings can't be - * edited and this flag should always be set. So we can always execute this. */ + * edited and this flag should always be set. */ wm->xr.session_settings.shading.flag |= V3D_SHADING_WORLD_ORIENTATION; } + } + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - #do_versions_after_linking_280 in this file. + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_290.c b/source/blender/blenloader/intern/versioning_290.c index f023813555f..f82b7970a60 100644 --- a/source/blender/blenloader/intern/versioning_290.c +++ b/source/blender/blenloader/intern/versioning_290.c @@ -605,34 +605,8 @@ void do_versions_after_linking_290(Main *bmain, ReportList *UNUSED(reports)) * * To play safe we move all the inputs beyond 18 to their rightful new place. * In case users are doing unexpected things with not-really supported keyframeable channels. - * - * The for loop for the input ids is at the top level otherwise we lose the animation - * keyframe data. */ - for (int input_id = 21; input_id >= 18; input_id--) { - FOREACH_NODETREE_BEGIN (bmain, ntree, id) { - if (ntree->type == NTREE_SHADER) { - LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type != SH_NODE_BSDF_PRINCIPLED) { - continue; - } - - const size_t node_name_length = strlen(node->name); - const size_t node_name_escaped_max_length = (node_name_length * 2); - char *node_name_escaped = MEM_mallocN(node_name_escaped_max_length + 1, - "escaped name"); - BLI_str_escape(node_name_escaped, node->name, node_name_escaped_max_length); - char *rna_path_prefix = BLI_sprintfN("nodes[\"%s\"].inputs", node_name_escaped); - - BKE_animdata_fix_paths_rename_all_ex( - bmain, id, rna_path_prefix, NULL, NULL, input_id, input_id + 1, false); - MEM_freeN(rna_path_prefix); - MEM_freeN(node_name_escaped); - } - } - } - FOREACH_NODETREE_END; - } + version_node_socket_index_animdata(bmain, NTREE_SHADER, SH_NODE_BSDF_PRINCIPLED, 18, 1, 22); } /* Convert all Multires displacement to Catmull-Clark subdivision limit surface. */ @@ -1461,7 +1435,6 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { view_layer->cryptomatte_levels = 6; - view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE; } } } @@ -1529,8 +1502,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= (SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_SOURCE | - SEQ_SHOW_STRIP_DURATION); + sseq->flag |= (SEQ_SHOW_OVERLAY | SEQ_TIMELINE_SHOW_STRIP_NAME | + SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_DURATION); } } } @@ -1541,7 +1514,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (STREQ(node->idname, "GeometryNodeRandomAttribute")) { - STRNCPY(node->idname, "GeometryNodeAttributeRandomize"); + STRNCPY(node->idname, "GeometryLegacyNodeAttributeRandomize"); } } } @@ -1651,8 +1624,8 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 1)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_BOOLEAN, "Geometry A", "Geometry 1"); - version_node_socket_name(ntree, GEO_NODE_BOOLEAN, "Geometry B", "Geometry 2"); + version_node_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry A", "Geometry 1"); + version_node_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry B", "Geometry 2"); } } FOREACH_NODETREE_END; @@ -1986,7 +1959,7 @@ void blo_do_versions_290(FileData *fd, Library *UNUSED(lib), Main *bmain) if (!MAIN_VERSION_ATLEAST(bmain, 293, 18)) { FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { - version_node_socket_name(ntree, GEO_NODE_VOLUME_TO_MESH, "Grid", "Density"); + version_node_socket_name(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Grid", "Density"); } } FOREACH_NODETREE_END; diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 04c0a4ebc79..792aab0ec34 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -47,6 +47,7 @@ #include "BKE_action.h" #include "BKE_animsys.h" +#include "BKE_armature.h" #include "BKE_asset.h" #include "BKE_collection.h" #include "BKE_deform.h" @@ -55,6 +56,7 @@ #include "BKE_idprop.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_node.h" #include "RNA_access.h" @@ -64,6 +66,7 @@ #include "MEM_guardedalloc.h" #include "readfile.h" +#include "SEQ_iterator.h" #include "SEQ_sequencer.h" #include "RNA_access.h" @@ -109,7 +112,8 @@ static void version_idproperty_move_data_int(IDPropertyUIDataInt *ui_data, if (default_value != NULL) { if (default_value->type == IDP_ARRAY) { if (default_value->subtype == IDP_INT) { - ui_data->default_array = MEM_dupallocN(IDP_Array(default_value)); + ui_data->default_array = MEM_malloc_arrayN(default_value->len, sizeof(int), __func__); + memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(int) * default_value->len); ui_data->default_array_len = default_value->len; } } @@ -151,9 +155,18 @@ static void version_idproperty_move_data_float(IDPropertyUIDataFloat *ui_data, IDProperty *default_value = IDP_GetPropertyFromGroup(prop_ui_data, "default"); if (default_value != NULL) { if (default_value->type == IDP_ARRAY) { - if (ELEM(default_value->subtype, IDP_FLOAT, IDP_DOUBLE)) { - ui_data->default_array = MEM_dupallocN(IDP_Array(default_value)); - ui_data->default_array_len = default_value->len; + const int array_len = default_value->len; + ui_data->default_array_len = array_len; + if (default_value->subtype == IDP_FLOAT) { + ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__); + const float *old_default_array = IDP_Array(default_value); + for (int i = 0; i < ui_data->default_array_len; i++) { + ui_data->default_array[i] = (double)old_default_array[i]; + } + } + else if (default_value->subtype == IDP_DOUBLE) { + ui_data->default_array = MEM_malloc_arrayN(array_len, sizeof(double), __func__); + memcpy(ui_data->default_array, IDP_Array(default_value), sizeof(double) * array_len); } } else if (ELEM(default_value->type, IDP_DOUBLE, IDP_FLOAT)) { @@ -365,6 +378,7 @@ static void move_vertex_group_names_to_object_data(Main *bmain) /* Clear the list in case the it was already assigned from another object. */ BLI_freelistN(new_defbase); *new_defbase = object->defbase; + BKE_object_defgroup_active_index_set(object, object->actdef); } } } @@ -438,6 +452,143 @@ static void do_versions_sequencer_speed_effect_recursive(Scene *scene, const Lis #undef SEQ_SPEED_COMPRESS_IPO_Y } +static bool do_versions_sequencer_color_tags(Sequence *seq, void *UNUSED(user_data)) +{ + seq->color_tag = SEQUENCE_COLOR_NONE; + return true; +} + +static bool do_versions_sequencer_color_balance_sop(Sequence *seq, void *UNUSED(user_data)) +{ + LISTBASE_FOREACH (SequenceModifierData *, smd, &seq->modifiers) { + if (smd->type == seqModifierType_ColorBalance) { + StripColorBalance *cb = &((ColorBalanceModifierData *)smd)->color_balance; + cb->method = SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN; + for (int i = 0; i < 3; i++) { + copy_v3_fl(cb->slope, 1.0f); + copy_v3_fl(cb->offset, 1.0f); + copy_v3_fl(cb->power, 1.0f); + } + } + } + return true; +} + +static bNodeLink *find_connected_link(bNodeTree *ntree, bNodeSocket *in_socket) +{ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tosock == in_socket) { + return link; + } + } + return NULL; +} + +static void add_realize_instances_before_socket(bNodeTree *ntree, + bNode *node, + bNodeSocket *geometry_socket) +{ + BLI_assert(geometry_socket->type == SOCK_GEOMETRY); + bNodeLink *link = find_connected_link(ntree, geometry_socket); + if (link == NULL) { + return; + } + + /* If the realize instances node is already before this socket, no need to continue. */ + if (link->fromnode->type == GEO_NODE_REALIZE_INSTANCES) { + return; + } + + bNode *realize_node = nodeAddStaticNode(NULL, ntree, GEO_NODE_REALIZE_INSTANCES); + realize_node->parent = node->parent; + realize_node->locx = node->locx - 100; + realize_node->locy = node->locy; + nodeAddLink(ntree, link->fromnode, link->fromsock, realize_node, realize_node->inputs.first); + link->fromnode = realize_node; + link->fromsock = realize_node->outputs.first; +} + +/** + * If a node used to realize instances implicitly and will no longer do so in 3.0, add a "Realize + * Instances" node in front of it to avoid changing behavior. Don't do this if the node will be + * replaced anyway though. + */ +static void version_geometry_nodes_add_realize_instance_nodes(bNodeTree *ntree) +{ + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { + if (ELEM(node->type, + GEO_NODE_CAPTURE_ATTRIBUTE, + GEO_NODE_SEPARATE_COMPONENTS, + GEO_NODE_CONVEX_HULL, + GEO_NODE_CURVE_LENGTH, + GEO_NODE_MESH_BOOLEAN, + GEO_NODE_FILLET_CURVE, + GEO_NODE_RESAMPLE_CURVE, + GEO_NODE_CURVE_TO_MESH, + GEO_NODE_TRIM_CURVE, + GEO_NODE_REPLACE_MATERIAL, + GEO_NODE_SUBDIVIDE_MESH, + GEO_NODE_ATTRIBUTE_REMOVE, + GEO_NODE_TRIANGULATE)) { + bNodeSocket *geometry_socket = node->inputs.first; + add_realize_instances_before_socket(ntree, node, geometry_socket); + } + /* Also realize instances for the profile input of the curve to mesh node. */ + if (node->type == GEO_NODE_CURVE_TO_MESH) { + bNodeSocket *profile_socket = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + add_realize_instances_before_socket(ntree, node, profile_socket); + } + } +} + +/** + * The geometry nodes modifier used to realize instances for the next modifier implicitly. Now it + * is done with the realize instances node. It also used to convert meshes to point clouds + * automatically, which is also now done with a specific node. + */ +static bNodeTree *add_realize_node_tree(Main *bmain) +{ + bNodeTree *node_tree = ntreeAddTree(bmain, "Realize Instances 2.93 Legacy", "GeometryNodeTree"); + + ntreeAddSocketInterface(node_tree, SOCK_IN, "NodeSocketGeometry", "Geometry"); + ntreeAddSocketInterface(node_tree, SOCK_OUT, "NodeSocketGeometry", "Geometry"); + + bNode *group_input = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_INPUT); + group_input->locx = -400.0f; + bNode *group_output = nodeAddStaticNode(NULL, node_tree, NODE_GROUP_OUTPUT); + group_output->locx = 500.0f; + group_output->flag |= NODE_DO_OUTPUT; + + bNode *join = nodeAddStaticNode(NULL, node_tree, GEO_NODE_JOIN_GEOMETRY); + join->locx = group_output->locx - 175.0f; + join->locy = group_output->locy; + bNode *conv = nodeAddStaticNode(NULL, node_tree, GEO_NODE_POINTS_TO_VERTICES); + conv->locx = join->locx - 175.0f; + conv->locy = join->locy - 70.0; + bNode *separate = nodeAddStaticNode(NULL, node_tree, GEO_NODE_SEPARATE_COMPONENTS); + separate->locx = join->locx - 350.0f; + separate->locy = join->locy + 50.0f; + bNode *realize = nodeAddStaticNode(NULL, node_tree, GEO_NODE_REALIZE_INSTANCES); + realize->locx = separate->locx - 200.0f; + realize->locy = join->locy; + + nodeAddLink(node_tree, group_input, group_input->outputs.first, realize, realize->inputs.first); + nodeAddLink(node_tree, realize, realize->outputs.first, separate, separate->inputs.first); + nodeAddLink(node_tree, conv, conv->outputs.first, join, join->inputs.first); + nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 3), join, join->inputs.first); + nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 1), conv, conv->inputs.first); + nodeAddLink(node_tree, separate, BLI_findlink(&separate->outputs, 2), join, join->inputs.first); + nodeAddLink(node_tree, separate, separate->outputs.first, join, join->inputs.first); + nodeAddLink(node_tree, join, join->outputs.first, group_output, group_output->inputs.first); + + LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) { + nodeSetSelected(node, false); + } + + ntreeUpdateTree(bmain, node_tree); + return node_tree; +} + void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) { if (MAIN_VERSION_ATLEAST(bmain, 300, 0) && !MAIN_VERSION_ATLEAST(bmain, 300, 1)) { @@ -494,6 +645,128 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) } } + if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + ToolSettings *tool_settings = scene->toolsettings; + ImagePaintSettings *imapaint = &tool_settings->imapaint; + if (imapaint->canvas != NULL && + ELEM(imapaint->canvas->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + imapaint->canvas = NULL; + } + if (imapaint->stencil != NULL && + ELEM(imapaint->stencil->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + imapaint->stencil = NULL; + } + if (imapaint->clone != NULL && + ELEM(imapaint->clone->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + imapaint->clone = NULL; + } + } + + LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) { + if (brush->clone.image != NULL && + ELEM(brush->clone.image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)) { + brush->clone.image = NULL; + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 28)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_geometry_nodes_add_realize_instance_nodes(ntree); + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 30)) { + do_versions_idproperty_ui_data(bmain); + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 32)) { + /* Update Switch Node Non-Fields switch input to Switch_001. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tonode->type == GEO_NODE_SWITCH) { + if (STREQ(link->tosock->identifier, "Switch")) { + bNode *to_node = link->tonode; + + uint8_t mode = ((NodeSwitch *)to_node->storage)->input_type; + if (ELEM(mode, + SOCK_GEOMETRY, + SOCK_OBJECT, + SOCK_COLLECTION, + SOCK_TEXTURE, + SOCK_MATERIAL)) { + link->tosock = link->tosock->next; + } + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) { + /* This was missing from #move_vertex_group_names_to_object_data. */ + LISTBASE_FOREACH (Object *, object, &bmain->objects) { + if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_GPENCIL)) { + /* This uses the fact that the active vertex group index starts counting at 1. */ + if (BKE_object_defgroup_active_index_get(object) == 0) { + BKE_object_defgroup_active_index_set(object, object->actdef); + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 35)) { + /* Add a new modifier to realize instances from previous modifiers. + * Previously that was done automatically by geometry nodes. */ + bNodeTree *realize_instances_node_tree = NULL; + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH_MUTABLE (ModifierData *, md, &ob->modifiers) { + if (md->type != eModifierType_Nodes) { + continue; + } + if (md->next == NULL) { + break; + } + if (md->next->type == eModifierType_Nodes) { + continue; + } + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group == NULL) { + continue; + } + + NodesModifierData *new_nmd = (NodesModifierData *)BKE_modifier_new(eModifierType_Nodes); + STRNCPY(new_nmd->modifier.name, "Realize Instances 2.93 Legacy"); + BKE_modifier_unique_name(&ob->modifiers, &new_nmd->modifier); + BLI_insertlinkafter(&ob->modifiers, md, new_nmd); + if (realize_instances_node_tree == NULL) { + realize_instances_node_tree = add_realize_node_tree(bmain); + } + new_nmd->node_group = realize_instances_node_tree; + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 37)) { + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + LISTBASE_FOREACH_MUTABLE (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_BOUNDING_BOX) { + bNodeSocket *geometry_socket = node->inputs.first; + add_realize_instances_before_socket(ntree, node, geometry_socket); + } + } + } + } + } + /** * Versioning code until next subversion bump goes here. * @@ -506,7 +779,8 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ - do_versions_idproperty_ui_data(bmain); + + version_node_socket_index_animdata(bmain, NTREE_SHADER, SH_NODE_BSDF_PRINCIPLED, 4, 2, 25); } } @@ -645,12 +919,10 @@ static bool geometry_node_is_293_legacy(const short node_type) switch (node_type) { /* Not legacy: No attribute inputs or outputs. */ case GEO_NODE_TRIANGULATE: - case GEO_NODE_EDGE_SPLIT: case GEO_NODE_TRANSFORM: - case GEO_NODE_BOOLEAN: - case GEO_NODE_SUBDIVISION_SURFACE: + case GEO_NODE_MESH_BOOLEAN: case GEO_NODE_IS_VIEWPORT: - case GEO_NODE_MESH_SUBDIVIDE: + case GEO_NODE_SUBDIVIDE_MESH: case GEO_NODE_MESH_PRIMITIVE_CUBE: case GEO_NODE_MESH_PRIMITIVE_CIRCLE: case GEO_NODE_MESH_PRIMITIVE_UV_SPHERE: @@ -660,9 +932,9 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_MESH_PRIMITIVE_LINE: case GEO_NODE_MESH_PRIMITIVE_GRID: case GEO_NODE_BOUNDING_BOX: - case GEO_NODE_CURVE_RESAMPLE: + case GEO_NODE_RESAMPLE_CURVE: case GEO_NODE_INPUT_MATERIAL: - case GEO_NODE_MATERIAL_REPLACE: + case GEO_NODE_REPLACE_MATERIAL: case GEO_NODE_CURVE_LENGTH: case GEO_NODE_CONVEX_HULL: case GEO_NODE_SEPARATE_COMPONENTS: @@ -674,8 +946,8 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_VIEWER: case GEO_NODE_CURVE_PRIMITIVE_LINE: case GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL: - case GEO_NODE_CURVE_FILL: - case GEO_NODE_CURVE_TRIM: + case GEO_NODE_FILL_CURVE: + case GEO_NODE_TRIM_CURVE: case GEO_NODE_CURVE_TO_MESH: return false; @@ -684,7 +956,7 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_SET_POSITION: case GEO_NODE_INPUT_INDEX: case GEO_NODE_INPUT_NORMAL: - case GEO_NODE_ATTRIBUTE_CAPTURE: + case GEO_NODE_CAPTURE_ATTRIBUTE: return false; /* Maybe legacy: Might need special attribute handling, depending on design. */ @@ -695,15 +967,15 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_COLLECTION_INFO: return false; - /* Maybe legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ - case GEO_NODE_CURVE_ENDPOINTS: - case GEO_NODE_CURVE_TO_POINTS: + /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to + * generate a mesh for all grids in the volume. */ + case GEO_NODE_LEGACY_VOLUME_TO_MESH: return false; - /* Maybe legacy: Special case for grid names? Or finish patch from level set branch to generate - * a mesh for all grids in the volume. */ - case GEO_NODE_VOLUME_TO_MESH: - return false; + /* Legacy: Transferred *all* attributes before, will not transfer all built-ins now. */ + case GEO_NODE_LEGACY_CURVE_ENDPOINTS: + case GEO_NODE_LEGACY_CURVE_TO_POINTS: + return true; /* Legacy: Attribute operation completely replaced by field nodes. */ case GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE: @@ -716,10 +988,10 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR: case GEO_NODE_LEGACY_POINT_SCALE: case GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE: - case GEO_NODE_ATTRIBUTE_VECTOR_ROTATE: + case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE: case GEO_NODE_LEGACY_ATTRIBUTE_CURVE_MAP: case GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE: - case GEO_NODE_LECAGY_ATTRIBUTE_CLAMP: + case GEO_NODE_LEGACY_ATTRIBUTE_CLAMP: case GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH: case GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ: case GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ: @@ -741,15 +1013,17 @@ static bool geometry_node_is_293_legacy(const short node_type) case GEO_NODE_LEGACY_CURVE_SET_HANDLES: return true; - /* Legacy: More complex attribute inputs or outputs. */ - case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */ - case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ - case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ - case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ - case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */ - case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */ - case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */ - case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */ + /* Legacy: More complex attribute inputs or outputs. */ + case GEO_NODE_LEGACY_SUBDIVISION_SURFACE: /* Used "crease" attribute. */ + case GEO_NODE_LEGACY_EDGE_SPLIT: /* Needs selection input version. */ + case GEO_NODE_LEGACY_DELETE_GEOMETRY: /* Needs field input, domain drop-down. */ + case GEO_NODE_LEGACY_CURVE_SUBDIVIDE: /* Needs field count input. */ + case GEO_NODE_LEGACY_POINTS_TO_VOLUME: /* Needs field radius input. */ + case GEO_NODE_LEGACY_SELECT_BY_MATERIAL: /* Output anonymous attribute. */ + case GEO_NODE_LEGACY_POINT_TRANSLATE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_INSTANCE: /* Needs field inputs. */ + case GEO_NODE_LEGACY_POINT_DISTRIBUTE: /* Needs field input, remove max for random mode. */ + case GEO_NODE_LEGACY_ATTRIBUTE_CONVERT: /* Attribute Capture, Store Attribute. */ return true; } return false; @@ -775,6 +1049,227 @@ static void version_geometry_nodes_change_legacy_names(bNodeTree *ntree) } } +static bool seq_transform_origin_set(Sequence *seq, void *UNUSED(user_data)) +{ + StripTransform *transform = seq->strip->transform; + if (seq->strip->transform != NULL) { + transform->origin[0] = transform->origin[1] = 0.5f; + } + return true; +} + +static void do_version_subsurface_methods(bNode *node) +{ + if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { + if (!ELEM(node->custom1, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { + node->custom1 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS; + } + } + else if (node->type == SH_NODE_BSDF_PRINCIPLED) { + if (!ELEM(node->custom2, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { + node->custom2 = SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS; + } + } +} + +static void version_geometry_nodes_add_attribute_input_settings(NodesModifierData *nmd) +{ + /* Before versioning the properties, make sure it hasn't been done already. */ + LISTBASE_FOREACH (const IDProperty *, property, &nmd->settings.properties->data.group) { + if (strstr(property->name, "_use_attribute") || strstr(property->name, "_attribute_name")) { + return; + } + } + + LISTBASE_FOREACH_MUTABLE (IDProperty *, property, &nmd->settings.properties->data.group) { + if (!ELEM(property->type, IDP_FLOAT, IDP_INT, IDP_ARRAY)) { + continue; + } + + if (strstr(property->name, "_use_attribute") || strstr(property->name, "_attribute_name")) { + continue; + } + + char use_attribute_prop_name[MAX_IDPROP_NAME]; + BLI_snprintf(use_attribute_prop_name, + sizeof(use_attribute_prop_name), + "%s%s", + property->name, + "_use_attribute"); + + IDPropertyTemplate idprop = {0}; + IDProperty *use_attribute_prop = IDP_New(IDP_INT, &idprop, use_attribute_prop_name); + IDP_AddToGroup(nmd->settings.properties, use_attribute_prop); + + char attribute_name_prop_name[MAX_IDPROP_NAME]; + BLI_snprintf(attribute_name_prop_name, + sizeof(attribute_name_prop_name), + "%s%s", + property->name, + "_attribute_name"); + + IDProperty *attribute_prop = IDP_New(IDP_STRING, &idprop, attribute_name_prop_name); + IDP_AddToGroup(nmd->settings.properties, attribute_prop); + } +} + +/* Copy of the function before the fixes. */ +static void legacy_vec_roll_to_mat3_normalized(const float nor[3], + const float roll, + float r_mat[3][3]) +{ + const float SAFE_THRESHOLD = 1.0e-5f; /* theta above this value has good enough precision. */ + const float CRITICAL_THRESHOLD = 1.0e-9f; /* above this is safe under certain conditions. */ + const float THRESHOLD_SQUARED = CRITICAL_THRESHOLD * CRITICAL_THRESHOLD; + + const float x = nor[0]; + const float y = nor[1]; + const float z = nor[2]; + + const float theta = 1.0f + y; /* remapping Y from [-1,+1] to [0,2]. */ + const float theta_alt = x * x + z * z; /* Helper value for matrix calculations.*/ + float rMatrix[3][3], bMatrix[3][3]; + + BLI_ASSERT_UNIT_V3(nor); + + /* When theta is close to zero (nor is aligned close to negative Y Axis), + * we have to check we do have non-null X/Z components as well. + * Also, due to float precision errors, nor can be (0.0, -0.99999994, 0.0) which results + * in theta being close to zero. This will cause problems when theta is used as divisor. + */ + if (theta > SAFE_THRESHOLD || (theta > CRITICAL_THRESHOLD && theta_alt > THRESHOLD_SQUARED)) { + /* nor is *not* aligned to negative Y-axis (0,-1,0). */ + + bMatrix[0][1] = -x; + bMatrix[1][0] = x; + bMatrix[1][1] = y; + bMatrix[1][2] = z; + bMatrix[2][1] = -z; + + if (theta > SAFE_THRESHOLD) { + /* nor differs significantly from negative Y axis (0,-1,0): apply the general case. */ + bMatrix[0][0] = 1 - x * x / theta; + bMatrix[2][2] = 1 - z * z / theta; + bMatrix[2][0] = bMatrix[0][2] = -x * z / theta; + } + else { + /* nor is close to negative Y axis (0,-1,0): apply the special case. */ + bMatrix[0][0] = (x + z) * (x - z) / -theta_alt; + bMatrix[2][2] = -bMatrix[0][0]; + bMatrix[2][0] = bMatrix[0][2] = 2.0f * x * z / theta_alt; + } + } + else { + /* nor is very close to negative Y axis (0,-1,0): use simple symmetry by Z axis. */ + unit_m3(bMatrix); + bMatrix[0][0] = bMatrix[1][1] = -1.0; + } + + /* Make Roll matrix */ + axis_angle_normalized_to_mat3(rMatrix, nor, roll); + + /* Combine and output result */ + mul_m3_m3m3(r_mat, rMatrix, bMatrix); +} + +static void correct_bone_roll_value(const float head[3], + const float tail[3], + const float check_x_axis[3], + const float check_y_axis[3], + float *r_roll) +{ + const float SAFE_THRESHOLD = 1.0e-5f; + float vec[3], bone_mat[3][3], vec2[3]; + + /* Compute the Y axis vector. */ + sub_v3_v3v3(vec, tail, head); + normalize_v3(vec); + + /* Only correct when in the danger zone. */ + if (1.0f + vec[1] < SAFE_THRESHOLD * 2 && (vec[0] || vec[2])) { + /* Use the armature matrix to double-check if adjustment is needed. + * This should minimize issues if the file is bounced back and forth between + * 2.92 and 2.91, provided Edit Mode isn't entered on the armature in 2.91. */ + vec_roll_to_mat3(vec, *r_roll, bone_mat); + + UNUSED_VARS_NDEBUG(check_y_axis); + BLI_assert(dot_v3v3(bone_mat[1], check_y_axis) > 0.999f); + + if (dot_v3v3(bone_mat[0], check_x_axis) < 0.999f) { + /* Recompute roll using legacy code to interpret the old value. */ + legacy_vec_roll_to_mat3_normalized(vec, *r_roll, bone_mat); + mat3_to_vec_roll(bone_mat, vec2, r_roll); + BLI_assert(compare_v3v3(vec, vec2, 0.001f)); + } + } +} + +/* Update the armature Bone roll fields for bones very close to -Y direction. */ +static void do_version_bones_roll(ListBase *lb) +{ + LISTBASE_FOREACH (Bone *, bone, lb) { + /* Parent-relative orientation (used for posing). */ + correct_bone_roll_value( + bone->head, bone->tail, bone->bone_mat[0], bone->bone_mat[1], &bone->roll); + + /* Absolute orientation (used for Edit mode). */ + correct_bone_roll_value( + bone->arm_head, bone->arm_tail, bone->arm_mat[0], bone->arm_mat[1], &bone->arm_roll); + + do_version_bones_roll(&bone->childbase); + } +} + +static void version_geometry_nodes_set_position_node_offset(bNodeTree *ntree) +{ + /* Add the new Offset socket. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_SET_POSITION) { + continue; + } + if (BLI_listbase_count(&node->inputs) < 4) { + /* The offset socket didn't exist in the file yet. */ + return; + } + bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3); + if (old_offset_socket->type == SOCK_VECTOR) { + /* Versioning happened already. */ + return; + } + /* Change identifier of old socket, so that the there is no name collision. */ + STRNCPY(old_offset_socket->identifier, "Offset_old"); + nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_VECTOR, PROP_TRANSLATION, "Offset", "Offset"); + } + + /* Relink links that were connected to Position while Offset was enabled. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->tonode->type != GEO_NODE_SET_POSITION) { + continue; + } + if (!STREQ(link->tosock->identifier, "Position")) { + continue; + } + bNodeSocket *old_offset_socket = BLI_findlink(&link->tonode->inputs, 3); + /* This assumes that the offset is not linked to something else. That seems to be a reasonable + * assumption, because the node is probably only ever used in one or the other mode. */ + const bool offset_enabled = + ((bNodeSocketValueBoolean *)old_offset_socket->default_value)->value; + if (offset_enabled) { + /* Relink to new offset socket. */ + link->tosock = old_offset_socket->next; + } + } + + /* Remove old Offset socket. */ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != GEO_NODE_SET_POSITION) { + continue; + } + bNodeSocket *old_offset_socket = BLI_findlink(&node->inputs, 3); + nodeRemoveSocket(ntree, node, old_offset_socket); + } +} + /* NOLINTNEXTLINE: readability-function-size */ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) { @@ -854,7 +1349,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } FOREACH_NODETREE_END; - if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "int", "import_type")) { + if (!DNA_struct_elem_find(fd->filesdna, "FileAssetSelectParams", "short", "import_type")) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { @@ -988,7 +1483,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_MESH_SUBDIVIDE) { + if (node->type == GEO_NODE_SUBDIVIDE_MESH) { strcpy(node->idname, "GeometryNodeMeshSubdivide"); } } @@ -1029,6 +1524,15 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + if (ob->type == OB_GPENCIL) { + LISTBASE_FOREACH (GpencilModifierData *, md, &ob->greasepencil_modifiers) { + if (md->type == eGpencilModifierType_Lineart) { + LineartGpencilModifierData *lmd = (LineartGpencilModifierData *)md; + lmd->flags |= LRT_GPENCIL_USE_CACHE; + lmd->chain_smooth_tolerance = 0.2f; + } + } + } } } @@ -1076,7 +1580,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { if (sl->spacetype == SPACE_SEQ) { SpaceSeq *sseq = (SpaceSeq *)sl; - sseq->flag |= SEQ_SHOW_GRID; + sseq->flag |= SEQ_TIMELINE_SHOW_GRID; } } } @@ -1159,7 +1663,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { - if (node->type == GEO_NODE_SUBDIVISION_SURFACE) { + if (node->type == GEO_NODE_LEGACY_SUBDIVISION_SURFACE) { if (node->storage == NULL) { NodeGeometrySubdivisionSurface *data = MEM_callocN( sizeof(NodeGeometrySubdivisionSurface), __func__); @@ -1230,11 +1734,6 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } if (!MAIN_VERSION_ATLEAST(bmain, 300, 22)) { - LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { - if (ntree->type == NTREE_GEOMETRY) { - version_geometry_nodes_change_legacy_names(ntree); - } - } if (!DNA_struct_elem_find( fd->filesdna, "LineartGpencilModifierData", "bool", "use_crease_on_smooth")) { LISTBASE_FOREACH (Object *, ob, &bmain->objects) { @@ -1263,18 +1762,8 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ + if (!MAIN_VERSION_ATLEAST(bmain, 300, 23)) { for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { @@ -1287,5 +1776,400 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + int seq_show_safe_margins = (sseq->flag & SEQ_PREVIEW_SHOW_SAFE_MARGINS); + int seq_show_gpencil = (sseq->flag & SEQ_PREVIEW_SHOW_GPENCIL); + int seq_show_fcurves = (sseq->flag & SEQ_TIMELINE_SHOW_FCURVES); + int seq_show_safe_center = (sseq->flag & SEQ_PREVIEW_SHOW_SAFE_CENTER); + int seq_show_metadata = (sseq->flag & SEQ_PREVIEW_SHOW_METADATA); + int seq_show_strip_name = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_NAME); + int seq_show_strip_source = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE); + int seq_show_strip_duration = (sseq->flag & SEQ_TIMELINE_SHOW_STRIP_DURATION); + int seq_show_grid = (sseq->flag & SEQ_TIMELINE_SHOW_GRID); + int show_strip_offset = (sseq->draw_flag & SEQ_TIMELINE_SHOW_STRIP_OFFSETS); + sseq->preview_overlay.flag = (seq_show_safe_margins | seq_show_gpencil | + seq_show_safe_center | seq_show_metadata); + sseq->timeline_overlay.flag = (seq_show_fcurves | seq_show_strip_name | + seq_show_strip_source | seq_show_strip_duration | + seq_show_grid | show_strip_offset); + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 24)) { + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene); + sequencer_tool_settings->pivot_point = V3D_AROUND_CENTER_MEDIAN; + + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, seq_transform_origin_set, NULL); + } + } + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; + } + } + } + } + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + region->v2d.min[1] = 4.0f; + } + } + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 25)) { + FOREACH_NODETREE_BEGIN (bmain, ntree, id) { + if (ntree->type == NTREE_SHADER) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + do_version_subsurface_methods(node); + } + } + } + FOREACH_NODETREE_END; + + enum { + R_EXR_TILE_FILE = (1 << 10), + R_FULL_SAMPLE = (1 << 15), + }; + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + scene->r.scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE); + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 26)) { + LISTBASE_FOREACH (Object *, ob, &bmain->objects) { + LISTBASE_FOREACH (ModifierData *, md, &ob->modifiers) { + if (md->type == eModifierType_Nodes) { + version_geometry_nodes_add_attribute_input_settings((NodesModifierData *)md); + } + } + } + + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_FILE: { + SpaceFile *sfile = (SpaceFile *)sl; + if (sfile->params) { + sfile->params->flag &= ~(FILE_PARAMS_FLAG_UNUSED_1 | FILE_PARAMS_FLAG_UNUSED_2 | + FILE_PARAMS_FLAG_UNUSED_3 | FILE_PARAMS_FLAG_UNUSED_4); + } + + /* New default import type: Append with reuse. */ + if (sfile->asset_params) { + sfile->asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE; + } + break; + } + default: + break; + } + } + } + } + + /* Deprecate the random float node in favor of the random value node. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != FN_NODE_LEGACY_RANDOM_FLOAT) { + continue; + } + if (strstr(node->idname, "Legacy")) { + /* Make sure we haven't changed this idname already. */ + continue; + } + + char temp_idname[sizeof(node->idname)]; + BLI_strncpy(temp_idname, node->idname, sizeof(node->idname)); + + BLI_snprintf(node->idname, + sizeof(node->idname), + "FunctionNodeLegacy%s", + temp_idname + strlen("FunctionNode")); + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 29)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_SEQ: { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + LISTBASE_FOREACH (ARegion *, region, regionbase) { + if (region->regiontype == RGN_TYPE_WINDOW) { + region->v2d.max[1] = MAXSEQ; + } + } + break; + } + case SPACE_IMAGE: { + SpaceImage *sima = (SpaceImage *)sl; + sima->custom_grid_subdiv = 10; + break; + } + } + } + } + } + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_geometry_nodes_change_legacy_names(ntree); + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 31)) { + /* Swap header with the tool header so the regular header is always on the edge. */ + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + ListBase *regionbase = (sl == area->spacedata.first) ? &area->regionbase : + &sl->regionbase; + ARegion *region_tool = NULL, *region_head = NULL; + int region_tool_index = -1, region_head_index = -1, i; + LISTBASE_FOREACH_INDEX (ARegion *, region, regionbase, i) { + if (region->regiontype == RGN_TYPE_TOOL_HEADER) { + region_tool = region; + region_tool_index = i; + } + else if (region->regiontype == RGN_TYPE_HEADER) { + region_head = region; + region_head_index = i; + } + } + if ((region_tool && region_head) && (region_head_index > region_tool_index)) { + BLI_listbase_swaplinks(regionbase, region_tool, region_head); + } + } + } + } + + /* Set strip color tags to SEQUENCE_COLOR_NONE. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_tags, NULL); + } + } + + /* Show sequencer color tags by default. */ + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + if (sl->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)sl; + sseq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG; + } + } + } + } + + /* Set defaults for new color balance modifier parameters. */ + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + if (scene->ed != NULL) { + SEQ_for_each_callback(&scene->ed->seqbase, do_versions_sequencer_color_balance_sop, NULL); + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 33)) { + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) { + switch (sl->spacetype) { + case SPACE_SEQ: { + SpaceSeq *sseq = (SpaceSeq *)sl; + enum { SEQ_DRAW_SEQUENCE = 0 }; + if (sseq->mainb == SEQ_DRAW_SEQUENCE) { + sseq->mainb = SEQ_DRAW_IMG_IMBUF; + } + break; + } + case SPACE_TEXT: { + SpaceText *st = (SpaceText *)sl; + st->flags &= ~ST_FLAG_UNUSED_4; + break; + } + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 36)) { + /* Update the `idnames` for renamed geometry and function nodes. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + version_node_id(ntree, FN_NODE_COMPARE_FLOATS, "FunctionNodeCompareFloats"); + version_node_id(ntree, GEO_NODE_CAPTURE_ATTRIBUTE, "GeometryNodeCaptureAttribute"); + version_node_id(ntree, GEO_NODE_MESH_BOOLEAN, "GeometryNodeMeshBoolean"); + version_node_id(ntree, GEO_NODE_FILL_CURVE, "GeometryNodeFillCurve"); + version_node_id(ntree, GEO_NODE_FILLET_CURVE, "GeometryNodeFilletCurve"); + version_node_id(ntree, GEO_NODE_REVERSE_CURVE, "GeometryNodeReverseCurve"); + version_node_id(ntree, GEO_NODE_SAMPLE_CURVE, "GeometryNodeSampleCurve"); + version_node_id(ntree, GEO_NODE_RESAMPLE_CURVE, "GeometryNodeResampleCurve"); + version_node_id(ntree, GEO_NODE_SUBDIVIDE_CURVE, "GeometryNodeSubdivideCurve"); + version_node_id(ntree, GEO_NODE_TRIM_CURVE, "GeometryNodeTrimCurve"); + version_node_id(ntree, GEO_NODE_REPLACE_MATERIAL, "GeometryNodeReplaceMaterial"); + version_node_id(ntree, GEO_NODE_SUBDIVIDE_MESH, "GeometryNodeSubdivideMesh"); + version_node_id(ntree, GEO_NODE_SET_MATERIAL, "GeometryNodeSetMaterial"); + version_node_id(ntree, GEO_NODE_SPLIT_EDGES, "GeometryNodeSplitEdges"); + } + + /* Update bone roll after a fix to vec_roll_to_mat3_normalized. */ + LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) { + do_version_bones_roll(&arm->bonebase); + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 37)) { + /* Node Editor: toggle overlays on. */ + if (!DNA_struct_find(fd->filesdna, "SpaceNodeOverlay")) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { + if (space->spacetype == SPACE_NODE) { + SpaceNode *snode = (SpaceNode *)space; + snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; + snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; + } + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 38)) { + LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, space, &area->spacedata) { + if (space->spacetype == SPACE_FILE) { + SpaceFile *sfile = (SpaceFile *)space; + FileAssetSelectParams *asset_params = sfile->asset_params; + if (asset_params) { + asset_params->base_params.filter_id = FILTER_ID_ALL; + } + } + } + } + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 39)) { + LISTBASE_FOREACH (wmWindowManager *, wm, &bmain->wm) { + wm->xr.session_settings.base_scale = 1.0f; + wm->xr.session_settings.draw_flags |= (V3D_OFSDRAW_SHOW_SELECTION | + V3D_OFSDRAW_XR_SHOW_CONTROLLERS | + V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS); + } + } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 40)) { + /* Update the `idnames` for renamed geometry and function nodes. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + version_node_id(ntree, FN_NODE_SLICE_STRING, "FunctionNodeSliceString"); + version_geometry_nodes_set_position_node_offset(ntree); + version_node_id(ntree, GEO_NODE_LEGACY_VOLUME_TO_MESH, "GeometryNodeLegacyVolumeToMesh"); + } + + /* Add storage to viewer node. */ + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type != NTREE_GEOMETRY) { + continue; + } + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == GEO_NODE_VIEWER) { + if (node->storage == NULL) { + NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN( + sizeof(NodeGeometryViewer), __func__); + data->data_type = CD_PROP_FLOAT; + node->storage = data; + } + } + } + } + + LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) { + if (ntree->type == NTREE_GEOMETRY) { + version_node_input_socket_name( + ntree, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, "Geometry", "Mesh"); + version_node_input_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Points"); + version_node_output_socket_name(ntree, GEO_NODE_POINTS_TO_VOLUME, "Geometry", "Volume"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVISION_SURFACE, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_RESAMPLE_CURVE, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_CURVE, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_RADIUS, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_TILT, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_SET_CURVE_HANDLES, "Geometry", "Curve"); + version_node_socket_name(ntree, GEO_NODE_TRANSLATE_INSTANCES, "Geometry", "Instances"); + version_node_socket_name(ntree, GEO_NODE_ROTATE_INSTANCES, "Geometry", "Instances"); + version_node_socket_name(ntree, GEO_NODE_SCALE_INSTANCES, "Geometry", "Instances"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry", "Mesh"); + version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 1", "Mesh 1"); + version_node_input_socket_name(ntree, GEO_NODE_MESH_BOOLEAN, "Geometry 2", "Mesh 2"); + version_node_socket_name(ntree, GEO_NODE_SUBDIVIDE_MESH, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_TRIANGULATE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CONE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CUBE, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_GRID, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_CIRCLE, "Geometry", "Mesh"); + version_node_output_socket_name(ntree, GEO_NODE_MESH_PRIMITIVE_LINE, "Geometry", "Mesh"); + version_node_output_socket_name( + ntree, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, "Geometry", "Mesh"); + version_node_socket_name(ntree, GEO_NODE_SET_POINT_RADIUS, "Geometry", "Points"); + } + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/blenloader/intern/versioning_common.cc b/source/blender/blenloader/intern/versioning_common.cc index 3f13d1ec12e..c7ff496fa20 100644 --- a/source/blender/blenloader/intern/versioning_common.cc +++ b/source/blender/blenloader/intern/versioning_common.cc @@ -28,8 +28,10 @@ #include "BLI_listbase.h" #include "BLI_string.h" +#include "BKE_animsys.h" #include "BKE_lib_id.h" #include "BKE_main.h" +#include "BKE_node.h" #include "MEM_guardedalloc.h" @@ -87,6 +89,18 @@ ID *do_versions_rename_id(Main *bmain, return id; } +static void change_node_socket_name(ListBase *sockets, const char *old_name, const char *new_name) +{ + LISTBASE_FOREACH (bNodeSocket *, socket, sockets) { + if (STREQ(socket->name, old_name)) { + BLI_strncpy(socket->name, new_name, sizeof(socket->name)); + } + if (STREQ(socket->identifier, old_name)) { + BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); + } + } +} + void version_node_socket_name(bNodeTree *ntree, const int node_type, const char *old_name, @@ -94,22 +108,104 @@ void version_node_socket_name(bNodeTree *ntree, { LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == node_type) { - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - if (STREQ(socket->name, old_name)) { - BLI_strncpy(socket->name, new_name, sizeof(socket->name)); - } - if (STREQ(socket->identifier, old_name)) { - BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); - } - } - LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { - if (STREQ(socket->name, old_name)) { - BLI_strncpy(socket->name, new_name, sizeof(socket->name)); - } - if (STREQ(socket->identifier, old_name)) { - BLI_strncpy(socket->identifier, new_name, sizeof(socket->name)); - } + change_node_socket_name(&node->inputs, old_name, new_name); + change_node_socket_name(&node->outputs, old_name, new_name); + } + } +} + +void version_node_input_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + change_node_socket_name(&node->inputs, old_name, new_name); + } + } +} + +void version_node_output_socket_name(bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + change_node_socket_name(&node->outputs, old_name, new_name); + } + } +} + +/** + * Replace the ID name of all nodes in the tree with the given type with the new name. + */ +void version_node_id(bNodeTree *ntree, const int node_type, const char *new_name) +{ + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type == node_type) { + if (!STREQ(node->idname, new_name)) { + strcpy(node->idname, new_name); } } } } + +/** + * Adjust animation data for newly added node sockets. + * + * Node sockets are addressed by their index (in their RNA path, and thus FCurves/drivers), and + * thus when a new node is added in the middle of the list, existing animation data needs to be + * adjusted. + * + * Since this is about animation data, it only concerns input sockets. + * + * \param node_tree_type node tree type that has these nodes, for example NTREE_SHADER. + * \param node_type node type to adjust, for example SH_NODE_BSDF_PRINCIPLED. + * \param socket_index_orig the original index of the moved socket; when socket 4 moved to 6, + * pass 4 here. + * \param socket_index_offset the offset of the nodes, so when socket 4 moved to 6, + * pass 2 here. + * \param total_number_of_sockets the total number of sockets in the node. + */ +void version_node_socket_index_animdata(Main *bmain, + const int node_tree_type, + const int node_type, + const int socket_index_orig, + const int socket_index_offset, + const int total_number_of_sockets) +{ + + /* The for loop for the input ids is at the top level otherwise we lose the animation + * keyframe data. Not sure what causes that, so I (Sybren) moved the code here from + * versioning_290.c as-is (structure-wise). */ + for (int input_index = total_number_of_sockets - 1; input_index >= socket_index_orig; + input_index--) { + FOREACH_NODETREE_BEGIN (bmain, ntree, owner_id) { + if (ntree->type != node_tree_type) { + continue; + } + + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { + if (node->type != node_type) { + continue; + } + + const size_t node_name_length = strlen(node->name); + const size_t node_name_escaped_max_length = (node_name_length * 2); + char *node_name_escaped = (char *)MEM_mallocN(node_name_escaped_max_length + 1, + "escaped name"); + BLI_str_escape(node_name_escaped, node->name, node_name_escaped_max_length); + char *rna_path_prefix = BLI_sprintfN("nodes[\"%s\"].inputs", node_name_escaped); + + const int new_index = input_index + socket_index_offset; + BKE_animdata_fix_paths_rename_all_ex( + bmain, owner_id, rna_path_prefix, nullptr, nullptr, input_index, new_index, false); + MEM_freeN(rna_path_prefix); + MEM_freeN(node_name_escaped); + } + } + FOREACH_NODETREE_END; + } +} diff --git a/source/blender/blenloader/intern/versioning_common.h b/source/blender/blenloader/intern/versioning_common.h index c1fe2b591cd..ed1cafdca33 100644 --- a/source/blender/blenloader/intern/versioning_common.h +++ b/source/blender/blenloader/intern/versioning_common.h @@ -43,6 +43,24 @@ void version_node_socket_name(struct bNodeTree *ntree, const int node_type, const char *old_name, const char *new_name); +void version_node_input_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); +void version_node_output_socket_name(struct bNodeTree *ntree, + const int node_type, + const char *old_name, + const char *new_name); + +void version_node_socket_index_animdata( + Main *bmain, + int node_tree_type, /* NTREE_....., e.g. NTREE_SHADER */ + int node_type, /* SH_NODE_..., e.g. SH_NODE_BSDF_PRINCIPLED */ + int socket_index_orig, + int socket_index_offset, + int total_number_of_sockets); + +void version_node_id(struct bNodeTree *ntree, const int node_type, const char *new_name); #ifdef __cplusplus } diff --git a/source/blender/blenloader/intern/versioning_cycles.c b/source/blender/blenloader/intern/versioning_cycles.c index 90e6b43f02e..3df6af86618 100644 --- a/source/blender/blenloader/intern/versioning_cycles.c +++ b/source/blender/blenloader/intern/versioning_cycles.c @@ -1373,6 +1373,11 @@ void blo_do_versions_cycles(FileData *UNUSED(fd), Library *UNUSED(lib), Main *bm void do_versions_after_linking_cycles(Main *bmain) { + const int DENOISER_AUTO = 0; + const int DENOISER_NLM = 1; + const int DENOISER_OPTIX = 2; + const int DENOISER_OPENIMAGEDENOISE = 4; + if (!MAIN_VERSION_ATLEAST(bmain, 280, 66)) { /* Shader node tree changes. After lib linking so we have all the typeinfo * pointers and updated sockets and we can use the high level node API to @@ -1578,10 +1583,6 @@ void do_versions_after_linking_cycles(Main *bmain) } if (cscene) { - const int DENOISER_AUTO = 0; - const int DENOISER_NLM = 1; - const int DENOISER_OPTIX = 2; - /* Enable denoiser if it was enabled for one view layer before. */ cycles_property_int_set(cscene, "denoiser", (use_optix) ? DENOISER_OPTIX : DENOISER_NLM); cycles_property_boolean_set(cscene, "use_denoising", use_denoising); @@ -1637,4 +1638,17 @@ void do_versions_after_linking_cycles(Main *bmain) object->visibility_flag |= flag; } } + + if (!MAIN_VERSION_ATLEAST(bmain, 300, 25)) { + /* Removal of NLM denoiser. */ + for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { + IDProperty *cscene = cycles_properties_from_ID(&scene->id); + + if (cscene) { + if (cycles_property_int(cscene, "denoiser", DENOISER_NLM) == DENOISER_NLM) { + cycles_property_int_set(cscene, "denoiser", DENOISER_OPENIMAGEDENOISE); + } + } + } + } } diff --git a/source/blender/blenloader/intern/versioning_defaults.c b/source/blender/blenloader/intern/versioning_defaults.c index ae0dbb7f808..fa61b1ca6cd 100644 --- a/source/blender/blenloader/intern/versioning_defaults.c +++ b/source/blender/blenloader/intern/versioning_defaults.c @@ -54,6 +54,7 @@ #include "BKE_curveprofile.h" #include "BKE_customdata.h" #include "BKE_gpencil.h" +#include "BKE_idprop.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" @@ -156,12 +157,12 @@ static void blo_update_defaults_screen(bScreen *screen, } else if (area->spacetype == SPACE_SEQ) { SpaceSeq *seq = area->spacedata.first; - seq->flag |= SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | - SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_NAME | SEQ_SHOW_STRIP_DURATION | - SEQ_SHOW_GRID; - + seq->flag |= SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_USE_PROXIES | SEQ_SHOW_OVERLAY; seq->render_size = SEQ_RENDER_SIZE_PROXY_100; - seq->flag |= SEQ_USE_PROXIES; + seq->timeline_overlay.flag |= SEQ_TIMELINE_SHOW_STRIP_SOURCE | SEQ_TIMELINE_SHOW_STRIP_NAME | + SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID | + SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG; + seq->preview_overlay.flag |= SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; } else if (area->spacetype == SPACE_TEXT) { /* Show syntax and line numbers in Script workspace text editor. */ @@ -207,7 +208,8 @@ static void blo_update_defaults_screen(bScreen *screen, LISTBASE_FOREACH (ARegion *, region, regionbase) { if (region->regiontype == RGN_TYPE_TOOL_HEADER) { - if ((sl->spacetype == SPACE_IMAGE) && hide_image_tool_header) { + if (((sl->spacetype == SPACE_IMAGE) && hide_image_tool_header) || + sl->spacetype == SPACE_SEQ) { region->flag |= RGN_FLAG_HIDDEN; } else { @@ -292,7 +294,7 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) } /* Rename render layers. */ - BKE_view_layer_rename(bmain, scene, scene->view_layers.first, "View Layer"); + BKE_view_layer_rename(bmain, scene, scene->view_layers.first, "ViewLayer"); /* Disable Z pass by default. */ LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { @@ -357,6 +359,12 @@ static void blo_update_defaults_scene(Main *bmain, Scene *scene) if (ts->custom_bevel_profile_preset == NULL) { ts->custom_bevel_profile_preset = BKE_curveprofile_add(PROF_PRESET_LINE); } + + /* Clear ID properties so Cycles gets defaults. */ + IDProperty *idprop = IDP_GetProperties(&scene->id, false); + if (idprop) { + IDP_ClearProperty(idprop); + } } /** @@ -583,6 +591,12 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) bNodeSocket *roughness_socket = nodeFindSocket(node, SOCK_IN, "Roughness"); bNodeSocketValueFloat *roughness_data = roughness_socket->default_value; roughness_data->value = 0.4f; + node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; + nodeUpdate(ma->nodetree, node); + } + else if (node->type == SH_NODE_SUBSURFACE_SCATTERING) { + node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; + nodeUpdate(ma->nodetree, node); } } } diff --git a/source/blender/blenloader/intern/versioning_legacy.c b/source/blender/blenloader/intern/versioning_legacy.c index 62cc2aa3662..37bf4898cb3 100644 --- a/source/blender/blenloader/intern/versioning_legacy.c +++ b/source/blender/blenloader/intern/versioning_legacy.c @@ -391,7 +391,6 @@ static void do_version_ntree_242_2(bNodeTree *ntree) iuser->sfra = nia->sfra; iuser->offset = nia->nr - 1; iuser->cycl = nia->cyclic; - iuser->ok = 1; node->storage = iuser; MEM_freeN(nia); @@ -399,7 +398,6 @@ static void do_version_ntree_242_2(bNodeTree *ntree) else { ImageUser *iuser = node->storage = MEM_callocN(sizeof(ImageUser), "node image user"); iuser->sfra = 1; - iuser->ok = 1; } } } diff --git a/source/blender/blenloader/intern/versioning_userdef.c b/source/blender/blenloader/intern/versioning_userdef.c index f4853ff803f..3c470a7d3df 100644 --- a/source/blender/blenloader/intern/versioning_userdef.c +++ b/source/blender/blenloader/intern/versioning_userdef.c @@ -27,9 +27,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" -#ifdef WITH_INTERNATIONAL -# include "BLT_translation.h" -#endif +#include "BLT_translation.h" #include "DNA_anim_types.h" #include "DNA_collection_types.h" @@ -60,10 +58,6 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) { #define USER_VERSION_ATLEAST(ver, subver) MAIN_VERSION_ATLEAST(userdef, ver, subver) - if (!USER_VERSION_ATLEAST(280, 20)) { - memcpy(btheme, &U_theme_default, sizeof(*btheme)); - } - #define FROM_DEFAULT_V4_UCHAR(member) copy_v4_v4_uchar(btheme->member, U_theme_default.member) if (!USER_VERSION_ATLEAST(280, 25)) { @@ -291,6 +285,38 @@ static void do_versions_theme(const UserDef *userdef, bTheme *btheme) btheme->space_sequencer.grid[3] = 255; } + if (!USER_VERSION_ATLEAST(300, 30)) { + FROM_DEFAULT_V4_UCHAR(space_node.wire); + } + + if (!USER_VERSION_ATLEAST(300, 31)) { + for (int i = 0; i < SEQUENCE_COLOR_TOT; ++i) { + FROM_DEFAULT_V4_UCHAR(strip_color[i].color); + } + } + + if (!USER_VERSION_ATLEAST(300, 33)) { + /* Adjust the frame node alpha now that it is used differently. */ + btheme->space_node.movie[3] = U_theme_default.space_node.movie[3]; + } + + if (!USER_VERSION_ATLEAST(300, 34)) { + btheme->tui.panel_roundness = 0.4f; + } + + if (!USER_VERSION_ATLEAST(300, 37)) { + btheme->space_node.dash_alpha = 0.5f; + } + + if (!USER_VERSION_ATLEAST(300, 39)) { + FROM_DEFAULT_V4_UCHAR(space_node.grid); + btheme->space_node.grid_levels = 7; + } + + if (!USER_VERSION_ATLEAST(300, 40)) { + memcpy(btheme, &U_theme_default, sizeof(*btheme)); + } + /** * Versioning code until next subversion bump goes here. * @@ -728,7 +754,7 @@ void blo_do_versions_userdef(UserDef *userdef) } } - /* patch to set Dupli Lightprobes and Grease Pencil */ + /* Patch to set dupli light-probes and grease-pencil. */ if (!USER_VERSION_ATLEAST(280, 58)) { userdef->dupflag |= USER_DUP_LIGHTPROBE; userdef->dupflag |= USER_DUP_GPENCIL; @@ -893,6 +919,35 @@ void blo_do_versions_userdef(UserDef *userdef) userdef->flag &= ~USER_FLAG_UNUSED_5; } + if (!USER_VERSION_ATLEAST(300, 38)) { + /* Patch to set Dupli Lattice/Camera/Speaker. */ + userdef->dupflag |= USER_DUP_LATTICE; + userdef->dupflag |= USER_DUP_CAMERA; + userdef->dupflag |= USER_DUP_SPEAKER; + } + + if (!USER_VERSION_ATLEAST(300, 40)) { + /* Rename the default asset library from "Default" to "User Library". This isn't bullet proof + * since it doesn't handle translations and ignores user changes. But this was an alpha build + * (experimental) feature and the name is just for display in the UI anyway. So it doesn't have + * to work perfectly at all. */ + LISTBASE_FOREACH (bUserAssetLibrary *, asset_library, &userdef->asset_libraries) { + /* Ignores translations, since that would depend on the current preferences (global `U`). */ + if (STREQ(asset_library->name, "Default")) { + BKE_preferences_asset_library_name_set( + userdef, asset_library, BKE_PREFS_ASSET_LIBRARY_DEFAULT_NAME); + } + } + } + + if (!USER_VERSION_ATLEAST(300, 40)) { + LISTBASE_FOREACH (uiStyle *, style, &userdef->uistyles) { + const int default_title_points = 11; /* UI_DEFAULT_TITLE_POINTS */ + style->paneltitle.points = default_title_points; + style->grouplabel.points = default_title_points; + } + } + /** * Versioning code until next subversion bump goes here. * diff --git a/source/blender/blenloader/tests/blendfile_loading_base_test.cc b/source/blender/blenloader/tests/blendfile_loading_base_test.cc index 8afa631ffc5..f06f6f7d329 100644 --- a/source/blender/blenloader/tests/blendfile_loading_base_test.cc +++ b/source/blender/blenloader/tests/blendfile_loading_base_test.cc @@ -65,7 +65,6 @@ void BlendfileLoadingBaseTest::SetUpTestCase() BKE_idtype_init(); BKE_appdir_init(); IMB_init(); - BKE_images_init(); BKE_modifier_init(); DEG_register_node_types(); RNA_init(); diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index cf7697ad35f..221994aa313 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -1796,11 +1796,11 @@ bool BMO_op_vinitf(BMesh *bm, BMOperator *op, const int flag, const char *_fmt, while (*fmt) { if (state) { - /* jump past leading whitespace */ + /* Jump past leading white-space. */ i = strspn(fmt, " "); fmt += i; - /* ignore trailing whitespace */ + /* Ignore trailing white-space. */ if (!fmt[i]) { break; } diff --git a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c index 86a7d8153f0..103d7621f87 100644 --- a/source/blender/bmesh/intern/bmesh_polygon_edgenet.c +++ b/source/blender/bmesh/intern/bmesh_polygon_edgenet.c @@ -1135,11 +1135,13 @@ static BMVert *bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimi BMVert *v_other = BM_edge_other_vert(e_face_init ? e_face_init : v_delimit->e, v_delimit); BLI_SMALLSTACK_PUSH(search, v_other); - BM_elem_flag_disable(v_other, VERT_NOT_IN_STACK); + if (BM_elem_flag_test(v_other, VERT_NOT_IN_STACK)) { + BM_elem_flag_disable(v_other, VERT_NOT_IN_STACK); + BLI_linklist_prepend_alloca(&vert_stack, v_other); + } while ((v_other = BLI_SMALLSTACK_POP(search))) { BLI_assert(BM_elem_flag_test(v_other, VERT_NOT_IN_STACK) == false); - BLI_linklist_prepend_alloca(&vert_stack, v_other); BMEdge *e_iter = v_other->e; do { BMVert *v_step = BM_edge_other_vert(e_iter, v_other); @@ -1147,6 +1149,7 @@ static BMVert *bm_face_split_edgenet_partial_connect(BMesh *bm, BMVert *v_delimi if (BM_elem_flag_test(v_step, VERT_NOT_IN_STACK)) { BM_elem_flag_disable(v_step, VERT_NOT_IN_STACK); BLI_SMALLSTACK_PUSH(search, v_step); + BLI_linklist_prepend_alloca(&vert_stack, v_step); } } } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_other)) != v_other->e); diff --git a/source/blender/bmesh/intern/bmesh_structure.c b/source/blender/bmesh/intern/bmesh_structure.c index d5d72cd4ba3..1f1ad0bae5b 100644 --- a/source/blender/bmesh/intern/bmesh_structure.c +++ b/source/blender/bmesh/intern/bmesh_structure.c @@ -86,7 +86,8 @@ void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src) /** * \section bm_cycles BMesh Cycles - * (this is somewhat outdate, though bits of its API are still used) - joeedh + * + * NOTE(@joeedh): this is somewhat outdated, though bits of its API are still used. * * Cycles are circular doubly linked lists that form the basis of adjacency * information in the BME modeler. Full adjacency relations can be derived diff --git a/source/blender/bmesh/intern/bmesh_walkers.c b/source/blender/bmesh/intern/bmesh_walkers.c index 8bdf205babd..b8fdd534842 100644 --- a/source/blender/bmesh/intern/bmesh_walkers.c +++ b/source/blender/bmesh/intern/bmesh_walkers.c @@ -31,8 +31,7 @@ #include "bmesh_walkers_private.h" /** - * - joeedh - - * design notes: + * NOTE(@joeedh): Details on design. * * original design: walkers directly emulation recursive functions. * functions save their state onto a worklist, and also add new states diff --git a/source/blender/bmesh/operators/bmo_dissolve.c b/source/blender/bmesh/operators/bmo_dissolve.c index efba0ec99ec..360dcc2c79e 100644 --- a/source/blender/bmesh/operators/bmo_dissolve.c +++ b/source/blender/bmesh/operators/bmo_dissolve.c @@ -233,8 +233,8 @@ void bmo_dissolve_faces_exec(BMesh *bm, BMOperator *op) * This could optionally do a partial merge, where some faces are joined. */ /* Prevent these faces from being removed. */ - for (i = 0; i < faces_len; i++) { - BMO_face_flag_disable(bm, faces[i], FACE_ORIG); + for (int j = 0; j < faces_len; j++) { + BMO_face_flag_disable(bm, faces[j], FACE_ORIG); } } } diff --git a/source/blender/bmesh/tools/bmesh_bevel.c b/source/blender/bmesh/tools/bmesh_bevel.c index e306fe47770..6cf3641fed2 100644 --- a/source/blender/bmesh/tools/bmesh_bevel.c +++ b/source/blender/bmesh/tools/bmesh_bevel.c @@ -111,7 +111,7 @@ typedef struct EdgeHalf { bool is_bev; /** Is e->v2 the vertex at this end? */ bool is_rev; - /** Is e a seam for custom loopdata (e.g., UVs)? */ + /** Is e a seam for custom loop-data (e.g., UV's). */ bool is_seam; /** Used during the custom profile orientation pass. */ bool visited_rpo; @@ -3048,7 +3048,9 @@ static void build_boundary(BevelParams *bp, BevVert *bv, bool construct) } } else { - offset_meet(bp, e, e2, bv->v, e->fnext, true, co, eip); + /* Since all edges between e and e2 are in the same plane, it is OK + * to treat this like the case where there are no edges between. */ + offset_meet(bp, e, e2, bv->v, e->fnext, false, co, NULL); } } @@ -3582,7 +3584,7 @@ static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle) } /* Residue np + 2*i (if cycle) else np - 1 + 2*i: - * right offset for parm i matches its spec; weighted. */ + * right offset for parameter i matches its spec; weighted. */ int row = iscycle ? np + 2 * i : np - 1 + 2 * i; EIG_linear_solver_matrix_add(solver, row, i, weight); EIG_linear_solver_right_hand_side_add(solver, 0, row, weight * eright->offset_r); @@ -3595,7 +3597,7 @@ static void adjust_the_cycle_or_chain(BoundVert *vstart, bool iscycle) #endif /* Residue np + 2*i + 1 (if cycle) else np - 1 + 2*i + 1: - * left offset for parm i matches its spec; weighted. */ + * left offset for parameter i matches its spec; weighted. */ row = row + 1; EIG_linear_solver_matrix_add( solver, row, (i == np - 1) ? 0 : i + 1, weight * v->adjchain->sinratio); @@ -4689,7 +4691,7 @@ static VMesh *pipe_adj_vmesh(BevelParams *bp, BevVert *bv, BoundVert *vpipe) * vertices to snap to the midline on the pipe, not just to one plane or the other. */ bool even = (ns % 2) == 0; bool midline = even && k == half_ns && - ((i == 0 && j == half_ns) || (i == ipipe1 || i == ipipe2)); + ((i == 0 && j == half_ns) || (ELEM(i, ipipe1, ipipe2))); snap_to_pipe_profile(vpipe, midline, mesh_vert(vm, i, j, k)->co); } } @@ -5215,7 +5217,7 @@ static void bevel_build_rings(BevelParams *bp, BMesh *bm, BevVert *bv, BoundVert for (int i = 0; i < n_bndv; i++) { for (int j = 0; j <= ns2; j++) { for (int k = 0; k <= ns; k++) { - if (j == 0 && (k == 0 || k == ns)) { + if (j == 0 && (ELEM(k, 0, ns))) { continue; /* Boundary corners already made. */ } if (!is_canon(vm, i, j, k)) { @@ -5792,7 +5794,7 @@ static void build_vmesh(BevelParams *bp, BMesh *bm, BevVert *bv) /* Make sure the pipe case ADJ mesh is used for both the "Grid Fill" (ADJ) and cutoff options. */ BoundVert *vpipe = NULL; - if ((vm->count == 3 || vm->count == 4) && bp->seg > 1) { + if (ELEM(vm->count, 3, 4) && bp->seg > 1) { /* Result is passed to bevel_build_rings to avoid overhead. */ vpipe = pipe_test(bv); if (vpipe) { diff --git a/source/blender/bmesh/tools/bmesh_intersect.c b/source/blender/bmesh/tools/bmesh_intersect.c index d51661a08bb..947442c7bd8 100644 --- a/source/blender/bmesh/tools/bmesh_intersect.c +++ b/source/blender/bmesh/tools/bmesh_intersect.c @@ -901,7 +901,7 @@ static int isect_bvhtree_point_v3(BVHTree *tree, const float **looptris, const f const float dir[3] = {1.0f, 0.0f, 0.0f}; /* Need to initialize hit even tho it's not used. - * This is to make it so kd-tree believes we didn't intersect anything and + * This is to make it so KD-tree believes we didn't intersect anything and * keeps calling the intersect callback. */ hit.index = -1; diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index a7f9081f3fc..73e66ffeb03 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -74,8 +74,8 @@ extern "C" { * * during the preparation of the execution All ReadBufferOperation will receive an offset. * This offset is used during execution as an optimization trick - * Next all operations will be initialized for execution \see NodeOperation.initExecution - * Next all ExecutionGroup's will be initialized for execution \see ExecutionGroup.initExecution + * Next all operations will be initialized for execution \see NodeOperation.init_execution + * Next all ExecutionGroup's will be initialized for execution \see ExecutionGroup.init_execution * this all is controlled from \see ExecutionSystem.execute * * \section priority Render priority @@ -92,7 +92,7 @@ extern "C" { * When match the ExecutionGroup will be executed (this happens in serial) * * \see ExecutionSystem.execute control of the Render priority - * \see NodeOperation.getRenderPriority receive the render priority + * \see NodeOperation.get_render_priority receive the render priority * \see ExecutionGroup.execute the main loop to execute a whole ExecutionGroup * * \section order Chunk order @@ -121,7 +121,7 @@ extern "C" { * Chunk is finished. * * \see ExecutionGroup.execute - * \see ViewerOperation.getChunkOrder + * \see ViewerOperation.get_chunk_order * \see ChunkOrdering * * \section interest Area of interest @@ -152,13 +152,13 @@ extern "C" { * * In the above example ExecutionGroup B has an outputoperation (ViewerOperation) * and is being executed. - * The first chunk is evaluated [@ref ExecutionGroup.scheduleChunkWhenPossible], + * The first chunk is evaluated [@ref ExecutionGroup.schedule_chunk_when_possible], * but not all input chunks are available. * The relevant ExecutionGroup (that can calculate the missing chunks; ExecutionGroup A) * is asked to calculate the area ExecutionGroup B is missing. - * [@ref ExecutionGroup.scheduleAreaWhenPossible] + * [@ref ExecutionGroup.schedule_area_when_possible] * ExecutionGroup B checks what chunks the area spans, and tries to schedule these chunks. - * If all input data is available these chunks are scheduled [@ref ExecutionGroup.scheduleChunk] + * If all input data is available these chunks are scheduled [@ref ExecutionGroup.schedule_chunk] * *
  *
@@ -171,18 +171,18 @@ extern "C" {
  *            O------------------------------->O                                            |
  *            .                                O                                            |
  *            .                                O-------\                                    |
- *            .                                .       | ExecutionGroup.scheduleChunkWhenPossible
+ *            .                                .       | ExecutionGroup.schedule_chunk_when_possible
  *            .                                .  O----/ (*)                                |
  *            .                                .  O                                         |
  *            .                                .  O                                         |
- *            .                                .  O  ExecutionGroup.scheduleAreaWhenPossible|
+ *            .                                .  O  ExecutionGroup.schedule_area_when_possible|
  *            .                                .  O---------------------------------------->O
- *            .                                .  .                                         O----------\ ExecutionGroup.scheduleChunkWhenPossible
+ *            .                                .  .                                         O----------\ ExecutionGroup.schedule_chunk_when_possible
  *            .                                .  .                                         .          | (*)
  *            .                                .  .                                         .  O-------/
  *            .                                .  .                                         .  O
  *            .                                .  .                                         .  O
- *            .                                .  .                                         .  O-------\ ExecutionGroup.scheduleChunk
+ *            .                                .  .                                         .  O-------\ ExecutionGroup.schedule_chunk
  *            .                                .  .                                         .  .       |
  *            .                                .  .                                         .  .  O----/
  *            .                                .  .                                         .  O<=O
@@ -198,7 +198,7 @@ extern "C" {
  * This happens until all chunks of (ExecutionGroup B) are finished executing or the user break's the process.
  *
  * NodeOperation like the ScaleOperation can influence the area of interest by reimplementing the
- * [@ref NodeOperation.determineAreaOfInterest] method
+ * [@ref NodeOperation.determine_area_of_interest] method
  *
  * 
  *
@@ -221,13 +221,13 @@ extern "C" {
  *
  * \see ExecutionGroup.execute Execute a complete ExecutionGroup.
  * Halts until finished or breaked by user
- * \see ExecutionGroup.scheduleChunkWhenPossible Tries to schedule a single chunk,
+ * \see ExecutionGroup.schedule_chunk_when_possible Tries to schedule a single chunk,
  * checks if all input data is available. Can trigger dependent chunks to be calculated
- * \see ExecutionGroup.scheduleAreaWhenPossible
+ * \see ExecutionGroup.schedule_area_when_possible
  * Tries to schedule an area. This can be multiple chunks
- * (is called from [@ref ExecutionGroup.scheduleChunkWhenPossible])
- * \see ExecutionGroup.scheduleChunk Schedule a chunk on the WorkScheduler
- * \see NodeOperation.determineDependingAreaOfInterest Influence the area of interest of a chunk.
+ * (is called from [@ref ExecutionGroup.schedule_chunk_when_possible])
+ * \see ExecutionGroup.schedule_chunk Schedule a chunk on the WorkScheduler
+ * \see NodeOperation.determine_depending_area_of_interest Influence the area of interest of a chunk.
  * \see WriteBufferOperation Operation to write to a MemoryProxy/MemoryBuffer
  * \see ReadBufferOperation Operation to read from a MemoryProxy/MemoryBuffer
  * \see MemoryProxy proxy for information about memory image
@@ -283,16 +283,16 @@ extern "C" {
  * The OutputOperation of the ExecutionGroup is called to execute the area of the outputbuffer.
  *
  * \see ExecutionGroup
- * \see NodeOperation.executeRegion executes a single chunk of a NodeOperation
+ * \see NodeOperation.execute_region executes a single chunk of a NodeOperation
  * \see CPUDevice.execute
  *
  * \subsection GPUDevice OpenCLDevice
  *
  * To be completed!
- * \see NodeOperation.executeOpenCLRegion
+ * \see NodeOperation.execute_opencl_region
  * \see OpenCLDevice.execute
  *
- * \section executePixel executing a pixel
+ * \section execute_pixel executing a pixel
  * Finally the last step, the node functionality :)
  */
 
@@ -312,10 +312,10 @@ extern "C" {
  *    (true) or editing (false).
  *    based on this setting the system will work differently:
  *     - during rendering only Composite & the File output node will be calculated
- * \see NodeOperation.isOutputProgram(int rendering) of the specific operations
+ * \see NodeOperation.is_output_program(int rendering) of the specific operations
  *
  *     - during editing all output nodes will be calculated
- * \see NodeOperation.isOutputProgram(int rendering) of the specific operations
+ * \see NodeOperation.is_output_program(int rendering) of the specific operations
  *
  *     - another quality setting can be used bNodeTree.
  *       The quality is determined by the bNodeTree fields.
@@ -326,10 +326,10 @@ extern "C" {
  *     - output nodes can have different priorities in the WorkScheduler.
  * This is implemented in the COM_execute function.
  *
- * \param viewSettings:
+ * \param view_settings:
  *   reference to view settings used for color management
  *
- * \param displaySettings:
+ * \param display_settings:
  *   reference to display settings used for color management
  *
  * OCIO_TODO: this options only used in rare cases, namely in output file node,
@@ -343,13 +343,13 @@ void COM_execute(RenderData *render_data,
                  Scene *scene,
                  bNodeTree *node_tree,
                  int rendering,
-                 const ColorManagedViewSettings *viewSettings,
-                 const ColorManagedDisplaySettings *displaySettings,
-                 const char *viewName);
+                 const ColorManagedViewSettings *view_settings,
+                 const ColorManagedDisplaySettings *display_settings,
+                 const char *view_name);
 
 /**
  * \brief Deinitialize the compositor caches and allocated memory.
- * Use COM_clearCaches to only free the caches.
+ * Use COM_clear_caches to only free the caches.
  */
 void COM_deinitialize(void);
 
@@ -357,7 +357,7 @@ void COM_deinitialize(void);
  * \brief Clear all compositor caches. (Compositor system will still remain available).
  * To deinitialize the compositor use the COM_deinitialize method.
  */
-// void COM_clearCaches(void); // NOT YET WRITTEN
+// void COM_clear_caches(void); // NOT YET WRITTEN
 
 #ifdef __cplusplus
 }
diff --git a/source/blender/compositor/COM_defines.h b/source/blender/compositor/COM_defines.h
index 73c4343a230..1c3a28670df 100644
--- a/source/blender/compositor/COM_defines.h
+++ b/source/blender/compositor/COM_defines.h
@@ -18,11 +18,14 @@
 
 #pragma once
 
-#include "BLI_index_range.hh"
-#include "BLI_rect.h"
+#include "BLI_float2.hh"
+
+#include "DNA_vec_types.h"
 
 namespace blender::compositor {
 
+using Size2f = float2;
+
 enum class eExecutionModel {
   /**
    * Operations are executed from outputs to inputs grouped in execution groups and rendered
@@ -121,26 +124,7 @@ constexpr float COM_PREVIEW_SIZE = 140.f;
 constexpr float COM_RULE_OF_THIRDS_DIVIDER = 100.0f;
 constexpr float COM_BLUR_BOKEH_PIXELS = 512;
 
-constexpr rcti COM_SINGLE_ELEM_AREA = {0, 1, 0, 1};
-
-constexpr IndexRange XRange(const rcti &area)
-{
-  return IndexRange(area.xmin, area.xmax - area.xmin);
-}
-
-constexpr IndexRange YRange(const rcti &area)
-{
-  return IndexRange(area.ymin, area.ymax - area.ymin);
-}
-
-constexpr IndexRange XRange(const rcti *area)
-{
-  return XRange(*area);
-}
-
-constexpr IndexRange YRange(const rcti *area)
-{
-  return YRange(*area);
-}
+constexpr rcti COM_AREA_NONE = {0, 0, 0, 0};
+constexpr rcti COM_CONSTANT_INPUT_AREA_OF_INTEREST = COM_AREA_NONE;
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferOperation.cc b/source/blender/compositor/intern/COM_BufferOperation.cc
index cafdff89c8e..81ea645e482 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.cc
+++ b/source/blender/compositor/intern/COM_BufferOperation.cc
@@ -24,53 +24,51 @@ BufferOperation::BufferOperation(MemoryBuffer *buffer, DataType data_type)
 {
   buffer_ = buffer;
   inflated_buffer_ = nullptr;
-  /* TODO: Implement a MemoryBuffer get_size() method returning a Size2d type. Shorten following
-   * code to: set_resolution(buffer.get_size()) */
-  unsigned int resolution[2];
-  resolution[0] = buffer->getWidth();
-  resolution[1] = buffer->getHeight();
-  setResolution(resolution);
-  addOutputSocket(data_type);
-  flags.is_constant_operation = buffer_->is_a_single_elem();
-  flags.is_fullframe_operation = false;
+  set_canvas(buffer->get_rect());
+  add_output_socket(data_type);
+  flags_.is_constant_operation = buffer_->is_a_single_elem();
+  flags_.is_fullframe_operation = false;
 }
 
 const float *BufferOperation::get_constant_elem()
 {
   BLI_assert(buffer_->is_a_single_elem());
-  return buffer_->getBuffer();
+  return buffer_->get_buffer();
 }
 
-void BufferOperation::initExecution()
+void BufferOperation::init_execution()
 {
   if (buffer_->is_a_single_elem()) {
-    initMutex();
+    init_mutex();
   }
 }
 
-void *BufferOperation::initializeTileData(rcti * /*rect*/)
+void *BufferOperation::initialize_tile_data(rcti * /*rect*/)
 {
   if (buffer_->is_a_single_elem() == false) {
     return buffer_;
   }
 
-  lockMutex();
+  lock_mutex();
   if (!inflated_buffer_) {
     inflated_buffer_ = buffer_->inflate();
   }
-  unlockMutex();
+  unlock_mutex();
   return inflated_buffer_;
 }
 
-void BufferOperation::deinitExecution()
+void BufferOperation::deinit_execution()
 {
   if (buffer_->is_a_single_elem()) {
-    deinitMutex();
+    deinit_mutex();
   }
   delete inflated_buffer_;
 }
 
-void BufferOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler)
+void BufferOperation::execute_pixel_sampled(float output[4],
+                                            float x,
+                                            float y,
+                                            PixelSampler sampler)
 {
   switch (sampler) {
     case PixelSampler::Nearest:
@@ -78,16 +76,16 @@ void BufferOperation::executePixelSampled(float output[4], float x, float y, Pix
       break;
     case PixelSampler::Bilinear:
     default:
-      buffer_->readBilinear(output, x, y);
+      buffer_->read_bilinear(output, x, y);
       break;
     case PixelSampler::Bicubic:
       /* No bicubic. Same implementation as ReadBufferOperation. */
-      buffer_->readBilinear(output, x, y);
+      buffer_->read_bilinear(output, x, y);
       break;
   }
 }
 
-void BufferOperation::executePixelFiltered(
+void BufferOperation::execute_pixel_filtered(
     float output[4], float x, float y, float dx[2], float dy[2])
 {
   const float uv[2] = {x, y};
diff --git a/source/blender/compositor/intern/COM_BufferOperation.h b/source/blender/compositor/intern/COM_BufferOperation.h
index b4cbc0a56b6..4aba3a705dd 100644
--- a/source/blender/compositor/intern/COM_BufferOperation.h
+++ b/source/blender/compositor/intern/COM_BufferOperation.h
@@ -31,11 +31,12 @@ class BufferOperation : public ConstantOperation {
   BufferOperation(MemoryBuffer *buffer, DataType data_type);
 
   const float *get_constant_elem() override;
-  void *initializeTileData(rcti *rect) override;
-  void initExecution() override;
-  void deinitExecution() override;
-  void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override;
-  void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override;
+  void *initialize_tile_data(rcti *rect) override;
+  void init_execution() override;
+  void deinit_execution() override;
+  void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override;
+  void execute_pixel_filtered(
+      float output[4], float x, float y, float dx[2], float dy[2]) override;
 };
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_BufferRange.h b/source/blender/compositor/intern/COM_BufferRange.h
index ffdf1f2f1e5..7684ae52fab 100644
--- a/source/blender/compositor/intern/COM_BufferRange.h
+++ b/source/blender/compositor/intern/COM_BufferRange.h
@@ -19,7 +19,6 @@
 #pragma once
 
 #include "BLI_assert.h"
-#include "BLI_rect.h"
 
 #include 
 
diff --git a/source/blender/compositor/intern/COM_CPUDevice.cc b/source/blender/compositor/intern/COM_CPUDevice.cc
index 2ca5557e278..ad7a807b8fd 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.cc
+++ b/source/blender/compositor/intern/COM_CPUDevice.cc
@@ -19,12 +19,11 @@
 #include "COM_CPUDevice.h"
 
 #include "COM_ExecutionGroup.h"
-
-#include "BLI_rect.h"
+#include "COM_NodeOperation.h"
 
 namespace blender::compositor {
 
-CPUDevice::CPUDevice(int thread_id) : m_thread_id(thread_id)
+CPUDevice::CPUDevice(int thread_id) : thread_id_(thread_id)
 {
 }
 
@@ -32,11 +31,11 @@ void CPUDevice::execute(WorkPackage *work_package)
 {
   switch (work_package->type) {
     case eWorkPackageType::Tile: {
-      const unsigned int chunkNumber = work_package->chunk_number;
-      ExecutionGroup *executionGroup = work_package->execution_group;
+      const unsigned int chunk_number = work_package->chunk_number;
+      ExecutionGroup *execution_group = work_package->execution_group;
 
-      executionGroup->getOutputOperation()->executeRegion(&work_package->rect, chunkNumber);
-      executionGroup->finalizeChunkExecution(chunkNumber, nullptr);
+      execution_group->get_output_operation()->execute_region(&work_package->rect, chunk_number);
+      execution_group->finalize_chunk_execution(chunk_number, nullptr);
       break;
     }
     case eWorkPackageType::CustomFunction: {
diff --git a/source/blender/compositor/intern/COM_CPUDevice.h b/source/blender/compositor/intern/COM_CPUDevice.h
index 99629890b30..b5d1fd1fff1 100644
--- a/source/blender/compositor/intern/COM_CPUDevice.h
+++ b/source/blender/compositor/intern/COM_CPUDevice.h
@@ -39,11 +39,11 @@ class CPUDevice : public Device {
 
   int thread_id()
   {
-    return m_thread_id;
+    return thread_id_;
   }
 
  protected:
-  int m_thread_id;
+  int thread_id_;
 };
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_ChunkOrder.cc b/source/blender/compositor/intern/COM_ChunkOrder.cc
index a03718d720d..3e0246ce893 100644
--- a/source/blender/compositor/intern/COM_ChunkOrder.cc
+++ b/source/blender/compositor/intern/COM_ChunkOrder.cc
@@ -16,9 +16,9 @@
  * Copyright 2011, Blender Foundation.
  */
 
-#include "COM_ChunkOrder.h"
+#include 
 
-#include "BLI_math.h"
+#include "COM_ChunkOrder.h"
 
 namespace blender::compositor {
 
diff --git a/source/blender/compositor/intern/COM_CompositorContext.cc b/source/blender/compositor/intern/COM_CompositorContext.cc
index a93820b66dc..7b0f7b0f8fb 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.cc
+++ b/source/blender/compositor/intern/COM_CompositorContext.cc
@@ -17,37 +17,38 @@
  */
 
 #include "COM_CompositorContext.h"
-#include "COM_defines.h"
-#include 
-
-#include "BLI_assert.h"
-#include "DNA_userdef_types.h"
 
 namespace blender::compositor {
 
 CompositorContext::CompositorContext()
 {
-  this->m_scene = nullptr;
-  this->m_rd = nullptr;
-  this->m_quality = eCompositorQuality::High;
-  this->m_hasActiveOpenCLDevices = false;
-  this->m_fastCalculation = false;
-  this->m_viewSettings = nullptr;
-  this->m_displaySettings = nullptr;
-  this->m_bnodetree = nullptr;
+  scene_ = nullptr;
+  rd_ = nullptr;
+  quality_ = eCompositorQuality::High;
+  hasActiveOpenCLDevices_ = false;
+  fast_calculation_ = false;
+  view_settings_ = nullptr;
+  display_settings_ = nullptr;
+  bnodetree_ = nullptr;
 }
 
-int CompositorContext::getFramenumber() const
+int CompositorContext::get_framenumber() const
 {
-  BLI_assert(m_rd);
-  return m_rd->cfra;
+  BLI_assert(rd_);
+  return rd_->cfra;
+}
+
+Size2f CompositorContext::get_render_size() const
+{
+  return {get_render_data()->xsch * get_render_percentage_as_factor(),
+          get_render_data()->ysch * get_render_percentage_as_factor()};
 }
 
 eExecutionModel CompositorContext::get_execution_model() const
 {
   if (U.experimental.use_full_frame_compositor) {
-    BLI_assert(m_bnodetree != nullptr);
-    switch (m_bnodetree->execution_mode) {
+    BLI_assert(bnodetree_ != nullptr);
+    switch (bnodetree_->execution_mode) {
       case 1:
         return eExecutionModel::FullFrame;
       case 0:
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index c6e83f93777..e41576b6f69 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -18,16 +18,13 @@
 
 #pragma once
 
-#include "BLI_rect.h"
-
 #include "COM_Enums.h"
 
 #include "DNA_color_types.h"
 #include "DNA_node_types.h"
 #include "DNA_scene_types.h"
 
-#include 
-#include 
+struct bNodeInstanceHash;
 
 namespace blender::compositor {
 
@@ -41,55 +38,55 @@ class CompositorContext {
    * editor) This field is initialized in ExecutionSystem and must only be read from that point
    * on. \see ExecutionSystem
    */
-  bool m_rendering;
+  bool rendering_;
 
   /**
    * \brief The quality of the composite.
    * This field is initialized in ExecutionSystem and must only be read from that point on.
    * \see ExecutionSystem
    */
-  eCompositorQuality m_quality;
+  eCompositorQuality quality_;
 
-  Scene *m_scene;
+  Scene *scene_;
 
   /**
    * \brief Reference to the render data that is being composited.
    * This field is initialized in ExecutionSystem and must only be read from that point on.
    * \see ExecutionSystem
    */
-  RenderData *m_rd;
+  RenderData *rd_;
 
   /**
    * \brief reference to the bNodeTree
    * This field is initialized in ExecutionSystem and must only be read from that point on.
    * \see ExecutionSystem
    */
-  bNodeTree *m_bnodetree;
+  bNodeTree *bnodetree_;
 
   /**
    * \brief Preview image hash table
    * This field is initialized in ExecutionSystem and must only be read from that point on.
    */
-  bNodeInstanceHash *m_previews;
+  bNodeInstanceHash *previews_;
 
   /**
    * \brief does this system have active opencl devices?
    */
-  bool m_hasActiveOpenCLDevices;
+  bool hasActiveOpenCLDevices_;
 
   /**
    * \brief Skip slow nodes
    */
-  bool m_fastCalculation;
+  bool fast_calculation_;
 
   /* \brief color management settings */
-  const ColorManagedViewSettings *m_viewSettings;
-  const ColorManagedDisplaySettings *m_displaySettings;
+  const ColorManagedViewSettings *view_settings_;
+  const ColorManagedDisplaySettings *display_settings_;
 
   /**
    * \brief active rendering view name
    */
-  const char *m_viewName;
+  const char *view_name_;
 
  public:
   /**
@@ -100,194 +97,196 @@ class CompositorContext {
   /**
    * \brief set the rendering field of the context
    */
-  void setRendering(bool rendering)
+  void set_rendering(bool rendering)
   {
-    this->m_rendering = rendering;
+    rendering_ = rendering;
   }
 
   /**
    * \brief get the rendering field of the context
    */
-  bool isRendering() const
+  bool is_rendering() const
   {
-    return this->m_rendering;
+    return rendering_;
   }
 
   /**
    * \brief set the scene of the context
    */
-  void setRenderData(RenderData *rd)
+  void set_render_data(RenderData *rd)
   {
-    this->m_rd = rd;
+    rd_ = rd;
   }
 
   /**
    * \brief set the bnodetree of the context
    */
-  void setbNodeTree(bNodeTree *bnodetree)
+  void set_bnodetree(bNodeTree *bnodetree)
   {
-    this->m_bnodetree = bnodetree;
+    bnodetree_ = bnodetree;
   }
 
   /**
    * \brief get the bnodetree of the context
    */
-  const bNodeTree *getbNodeTree() const
+  const bNodeTree *get_bnodetree() const
   {
-    return this->m_bnodetree;
+    return bnodetree_;
   }
 
   /**
    * \brief get the scene of the context
    */
-  const RenderData *getRenderData() const
+  const RenderData *get_render_data() const
   {
-    return this->m_rd;
+    return rd_;
   }
 
-  void setScene(Scene *scene)
+  void set_scene(Scene *scene)
   {
-    m_scene = scene;
+    scene_ = scene;
   }
-  Scene *getScene() const
+  Scene *get_scene() const
   {
-    return m_scene;
+    return scene_;
   }
 
   /**
    * \brief set the preview image hash table
    */
-  void setPreviewHash(bNodeInstanceHash *previews)
+  void set_preview_hash(bNodeInstanceHash *previews)
   {
-    this->m_previews = previews;
+    previews_ = previews;
   }
 
   /**
    * \brief get the preview image hash table
    */
-  bNodeInstanceHash *getPreviewHash() const
+  bNodeInstanceHash *get_preview_hash() const
   {
-    return this->m_previews;
+    return previews_;
   }
 
   /**
    * \brief set view settings of color management
    */
-  void setViewSettings(const ColorManagedViewSettings *viewSettings)
+  void set_view_settings(const ColorManagedViewSettings *view_settings)
   {
-    this->m_viewSettings = viewSettings;
+    view_settings_ = view_settings;
   }
 
   /**
    * \brief get view settings of color management
    */
-  const ColorManagedViewSettings *getViewSettings() const
+  const ColorManagedViewSettings *get_view_settings() const
   {
-    return this->m_viewSettings;
+    return view_settings_;
   }
 
   /**
    * \brief set display settings of color management
    */
-  void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings)
+  void set_display_settings(const ColorManagedDisplaySettings *display_settings)
   {
-    this->m_displaySettings = displaySettings;
+    display_settings_ = display_settings;
   }
 
   /**
    * \brief get display settings of color management
    */
-  const ColorManagedDisplaySettings *getDisplaySettings() const
+  const ColorManagedDisplaySettings *get_display_settings() const
   {
-    return this->m_displaySettings;
+    return display_settings_;
   }
 
   /**
    * \brief set the quality
    */
-  void setQuality(eCompositorQuality quality)
+  void set_quality(eCompositorQuality quality)
   {
-    this->m_quality = quality;
+    quality_ = quality;
   }
 
   /**
    * \brief get the quality
    */
-  eCompositorQuality getQuality() const
+  eCompositorQuality get_quality() const
   {
-    return this->m_quality;
+    return quality_;
   }
 
   /**
    * \brief get the current frame-number of the scene in this context
    */
-  int getFramenumber() const;
+  int get_framenumber() const;
 
   /**
-   * \brief has this system active openclDevices?
+   * \brief has this system active opencl_devices?
    */
-  bool getHasActiveOpenCLDevices() const
+  bool get_has_active_opencl_devices() const
   {
-    return this->m_hasActiveOpenCLDevices;
+    return hasActiveOpenCLDevices_;
   }
 
   /**
-   * \brief set has this system active openclDevices?
+   * \brief set has this system active opencl_devices?
    */
   void setHasActiveOpenCLDevices(bool hasAvtiveOpenCLDevices)
   {
-    this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices;
+    hasActiveOpenCLDevices_ = hasAvtiveOpenCLDevices;
   }
 
   /** Whether it has a view with a specific name and not the default one. */
   bool has_explicit_view() const
   {
-    return m_viewName && m_viewName[0] != '\0';
+    return view_name_ && view_name_[0] != '\0';
   }
 
   /**
    * \brief get the active rendering view
    */
-  const char *getViewName() const
+  const char *get_view_name() const
   {
-    return this->m_viewName;
+    return view_name_;
   }
 
   /**
    * \brief set the active rendering view
    */
-  void setViewName(const char *viewName)
+  void set_view_name(const char *view_name)
   {
-    this->m_viewName = viewName;
+    view_name_ = view_name;
   }
 
-  int getChunksize() const
+  int get_chunksize() const
   {
-    return this->getbNodeTree()->chunksize;
+    return this->get_bnodetree()->chunksize;
   }
 
-  void setFastCalculation(bool fastCalculation)
+  void set_fast_calculation(bool fast_calculation)
   {
-    this->m_fastCalculation = fastCalculation;
+    fast_calculation_ = fast_calculation;
   }
-  bool isFastCalculation() const
+  bool is_fast_calculation() const
   {
-    return this->m_fastCalculation;
+    return fast_calculation_;
   }
-  bool isGroupnodeBufferEnabled() const
+  bool is_groupnode_buffer_enabled() const
   {
-    return (this->getbNodeTree()->flag & NTREE_COM_GROUPNODE_BUFFER) != 0;
+    return (this->get_bnodetree()->flag & NTREE_COM_GROUPNODE_BUFFER) != 0;
   }
 
   /**
    * \brief Get the render percentage as a factor.
    * The compositor uses a factor i.o. a percentage.
    */
-  float getRenderPercentageAsFactor() const
+  float get_render_percentage_as_factor() const
   {
-    return m_rd->size * 0.01f;
+    return rd_->size * 0.01f;
   }
 
+  Size2f get_render_size() const;
+
   /**
    * Get active execution model.
    */
diff --git a/source/blender/compositor/intern/COM_ConstantFolder.cc b/source/blender/compositor/intern/COM_ConstantFolder.cc
index 445a9ce7433..2cf3ac001d1 100644
--- a/source/blender/compositor/intern/COM_ConstantFolder.cc
+++ b/source/blender/compositor/intern/COM_ConstantFolder.cc
@@ -16,10 +16,11 @@
  * Copyright 2021, Blender Foundation.
  */
 
-#include "BLI_rect.h"
+#include "BLI_map.hh"
+#include "BLI_set.hh"
 
 #include "COM_ConstantFolder.h"
-#include "COM_ConstantOperation.h"
+#include "COM_NodeOperationBuilder.h"
 #include "COM_SetColorOperation.h"
 #include "COM_SetValueOperation.h"
 #include "COM_SetVectorOperation.h"
@@ -43,7 +44,7 @@ ConstantFolder::ConstantFolder(NodeOperationBuilder &operations_builder)
 static bool is_constant_foldable(NodeOperation *operation)
 {
   if (operation->get_flags().can_be_constant && !operation->get_flags().is_constant_operation) {
-    for (int i = 0; i < operation->getNumberOfInputSockets(); i++) {
+    for (int i = 0; i < operation->get_number_of_input_sockets(); i++) {
       NodeOperation *input = operation->get_input_operation(i);
       if (!input->get_flags().is_constant_operation ||
           !static_cast(input)->can_get_constant_elem()) {
@@ -71,17 +72,17 @@ static ConstantOperation *create_constant_operation(DataType data_type, const fl
   switch (data_type) {
     case DataType::Color: {
       SetColorOperation *color_op = new SetColorOperation();
-      color_op->setChannels(constant_elem);
+      color_op->set_channels(constant_elem);
       return color_op;
     }
     case DataType::Vector: {
       SetVectorOperation *vector_op = new SetVectorOperation();
-      vector_op->setVector(constant_elem);
+      vector_op->set_vector(constant_elem);
       return vector_op;
     }
     case DataType::Value: {
       SetValueOperation *value_op = new SetValueOperation();
-      value_op->setValue(*constant_elem);
+      value_op->set_value(*constant_elem);
       return value_op;
     }
     default: {
@@ -93,7 +94,7 @@ static ConstantOperation *create_constant_operation(DataType data_type, const fl
 
 ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation)
 {
-  const DataType data_type = operation->getOutputSocket()->getDataType();
+  const DataType data_type = operation->get_output_socket()->get_data_type();
   MemoryBuffer fold_buf(data_type, first_elem_area_);
   Vector input_bufs = get_constant_input_buffers(operation);
   operation->init_data();
@@ -101,7 +102,8 @@ ConstantOperation *ConstantFolder::fold_operation(NodeOperation *operation)
 
   MemoryBuffer *constant_buf = create_constant_buffer(data_type);
   constant_buf->copy_from(&fold_buf, first_elem_area_);
-  ConstantOperation *constant_op = create_constant_operation(data_type, constant_buf->getBuffer());
+  ConstantOperation *constant_op = create_constant_operation(data_type,
+                                                             constant_buf->get_buffer());
   operations_builder_.replace_operation_with_constant(operation, constant_op);
   constant_buffers_.add_new(constant_op, constant_buf);
   return constant_op;
@@ -116,14 +118,15 @@ MemoryBuffer *ConstantFolder::create_constant_buffer(const DataType data_type)
 
 Vector ConstantFolder::get_constant_input_buffers(NodeOperation *operation)
 {
-  const int num_inputs = operation->getNumberOfInputSockets();
+  const int num_inputs = operation->get_number_of_input_sockets();
   Vector inputs_bufs(num_inputs);
   for (int i = 0; i < num_inputs; i++) {
     BLI_assert(operation->get_input_operation(i)->get_flags().is_constant_operation);
     ConstantOperation *constant_op = static_cast(
         operation->get_input_operation(i));
     MemoryBuffer *constant_buf = constant_buffers_.lookup_or_add_cb(constant_op, [=] {
-      MemoryBuffer *buf = create_constant_buffer(constant_op->getOutputSocket()->getDataType());
+      MemoryBuffer *buf = create_constant_buffer(
+          constant_op->get_output_socket()->get_data_type());
       constant_op->render(buf, {first_elem_area_}, {});
       return buf;
     });
@@ -185,8 +188,8 @@ void ConstantFolder::get_operation_output_operations(NodeOperation *operation,
 {
   const Vector &links = operations_builder_.get_links();
   for (const Link &link : links) {
-    if (&link.from()->getOperation() == operation) {
-      r_outputs.append(&link.to()->getOperation());
+    if (&link.from()->get_operation() == operation) {
+      r_outputs.append(&link.to()->get_operation());
     }
   }
 }
diff --git a/source/blender/compositor/intern/COM_ConstantFolder.h b/source/blender/compositor/intern/COM_ConstantFolder.h
index 2432e859a5a..f31cb4f4e78 100644
--- a/source/blender/compositor/intern/COM_ConstantFolder.h
+++ b/source/blender/compositor/intern/COM_ConstantFolder.h
@@ -18,16 +18,12 @@
 
 #pragma once
 
-#include "BLI_map.hh"
-#include "BLI_set.hh"
-#include "BLI_vector.hh"
-
-#include "COM_NodeOperationBuilder.h"
 #include "COM_defines.h"
 
 namespace blender::compositor {
 
 class NodeOperation;
+class NodeOperationBuilder;
 class ConstantOperation;
 class MemoryBuffer;
 
diff --git a/source/blender/compositor/intern/COM_Converter.cc b/source/blender/compositor/intern/COM_Converter.cc
index 4b103c21c75..0af4ff7d98d 100644
--- a/source/blender/compositor/intern/COM_Converter.cc
+++ b/source/blender/compositor/intern/COM_Converter.cc
@@ -22,7 +22,6 @@
 
 #include "BKE_node.h"
 
-#include "COM_NodeOperation.h"
 #include "COM_NodeOperationBuilder.h"
 
 #include "COM_AlphaOverNode.h"
@@ -62,7 +61,6 @@
 #include "COM_DistanceMatteNode.h"
 #include "COM_DoubleEdgeMaskNode.h"
 #include "COM_EllipseMaskNode.h"
-#include "COM_ExecutionSystem.h"
 #include "COM_FilterNode.h"
 #include "COM_FlipNode.h"
 #include "COM_GammaNode.h"
@@ -435,8 +433,8 @@ Node *COM_convert_bnode(bNode *b_node)
 /* TODO(jbakker): make this an std::optional. */
 NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const NodeOperationInput &to)
 {
-  const DataType src_data_type = from.getDataType();
-  const DataType dst_data_type = to.getDataType();
+  const DataType src_data_type = from.get_data_type();
+  const DataType dst_data_type = to.get_data_type();
 
   if (src_data_type == DataType::Value && dst_data_type == DataType::Color) {
     return new ConvertValueToColorOperation();
@@ -460,49 +458,50 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from, const Node
   return nullptr;
 }
 
-void COM_convert_resolution(NodeOperationBuilder &builder,
-                            NodeOperationOutput *fromSocket,
-                            NodeOperationInput *toSocket)
+void COM_convert_canvas(NodeOperationBuilder &builder,
+                        NodeOperationOutput *from_socket,
+                        NodeOperationInput *to_socket)
 {
   /* Data type conversions are executed before resolutions to ensure convert operations have
    * resolution. This method have to ensure same datatypes are linked for new operations. */
-  BLI_assert(fromSocket->getDataType() == toSocket->getDataType());
-  ResizeMode mode = toSocket->getResizeMode();
+  BLI_assert(from_socket->get_data_type() == to_socket->get_data_type());
 
-  NodeOperation *toOperation = &toSocket->getOperation();
-  const float toWidth = toOperation->getWidth();
-  const float toHeight = toOperation->getHeight();
-  NodeOperation *fromOperation = &fromSocket->getOperation();
-  const float fromWidth = fromOperation->getWidth();
-  const float fromHeight = fromOperation->getHeight();
-  bool doCenter = false;
-  bool doScale = false;
-  float addX = (toWidth - fromWidth) / 2.0f;
-  float addY = (toHeight - fromHeight) / 2.0f;
+  ResizeMode mode = to_socket->get_resize_mode();
+  BLI_assert(mode != ResizeMode::None);
+
+  NodeOperation *to_operation = &to_socket->get_operation();
+  const float to_width = to_operation->get_width();
+  const float to_height = to_operation->get_height();
+  NodeOperation *from_operation = &from_socket->get_operation();
+  const float from_width = from_operation->get_width();
+  const float from_height = from_operation->get_height();
+  bool do_center = false;
+  bool do_scale = false;
   float scaleX = 0;
   float scaleY = 0;
 
   switch (mode) {
     case ResizeMode::None:
+    case ResizeMode::Align:
       break;
     case ResizeMode::Center:
-      doCenter = true;
+      do_center = true;
       break;
     case ResizeMode::FitWidth:
-      doCenter = true;
-      doScale = true;
-      scaleX = scaleY = toWidth / fromWidth;
+      do_center = true;
+      do_scale = true;
+      scaleX = scaleY = to_width / from_width;
       break;
     case ResizeMode::FitHeight:
-      doCenter = true;
-      doScale = true;
-      scaleX = scaleY = toHeight / fromHeight;
+      do_center = true;
+      do_scale = true;
+      scaleX = scaleY = to_height / from_height;
       break;
     case ResizeMode::FitAny:
-      doCenter = true;
-      doScale = true;
-      scaleX = toWidth / fromWidth;
-      scaleY = toHeight / fromHeight;
+      do_center = true;
+      do_scale = true;
+      scaleX = to_width / from_width;
+      scaleY = to_height / from_height;
       if (scaleX < scaleY) {
         scaleX = scaleY;
       }
@@ -511,70 +510,82 @@ void COM_convert_resolution(NodeOperationBuilder &builder,
       }
       break;
     case ResizeMode::Stretch:
-      doCenter = true;
-      doScale = true;
-      scaleX = toWidth / fromWidth;
-      scaleY = toHeight / fromHeight;
+      do_center = true;
+      do_scale = true;
+      scaleX = to_width / from_width;
+      scaleY = to_height / from_height;
       break;
   }
 
-  if (doCenter) {
-    NodeOperation *first = nullptr;
-    ScaleOperation *scaleOperation = nullptr;
-    if (doScale) {
-      scaleOperation = new ScaleRelativeOperation(fromSocket->getDataType());
-      scaleOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
-      scaleOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
-      first = scaleOperation;
-      SetValueOperation *sxop = new SetValueOperation();
-      sxop->setValue(scaleX);
-      builder.addLink(sxop->getOutputSocket(), scaleOperation->getInputSocket(1));
-      SetValueOperation *syop = new SetValueOperation();
-      syop->setValue(scaleY);
-      builder.addLink(syop->getOutputSocket(), scaleOperation->getInputSocket(2));
-      builder.addOperation(sxop);
-      builder.addOperation(syop);
+  float addX = do_center ? (to_width - from_width) / 2.0f : 0.0f;
+  float addY = do_center ? (to_height - from_height) / 2.0f : 0.0f;
+  NodeOperation *first = nullptr;
+  ScaleOperation *scale_operation = nullptr;
+  if (do_scale) {
+    scale_operation = new ScaleRelativeOperation(from_socket->get_data_type());
+    scale_operation->get_input_socket(1)->set_resize_mode(ResizeMode::None);
+    scale_operation->get_input_socket(2)->set_resize_mode(ResizeMode::None);
+    first = scale_operation;
+    SetValueOperation *sxop = new SetValueOperation();
+    sxop->set_value(scaleX);
+    builder.add_link(sxop->get_output_socket(), scale_operation->get_input_socket(1));
+    SetValueOperation *syop = new SetValueOperation();
+    syop->set_value(scaleY);
+    builder.add_link(syop->get_output_socket(), scale_operation->get_input_socket(2));
+    builder.add_operation(sxop);
+    builder.add_operation(syop);
 
-      unsigned int resolution[2] = {fromOperation->getWidth(), fromOperation->getHeight()};
-      scaleOperation->setResolution(resolution);
-      sxop->setResolution(resolution);
-      syop->setResolution(resolution);
-      builder.addOperation(scaleOperation);
+    rcti scale_canvas = from_operation->get_canvas();
+    if (builder.context().get_execution_model() == eExecutionModel::FullFrame) {
+      ScaleOperation::scale_area(scale_canvas, scaleX, scaleY);
+      scale_canvas.xmax = scale_canvas.xmin + to_operation->get_width();
+      scale_canvas.ymax = scale_canvas.ymin + to_operation->get_height();
+      addX = 0;
+      addY = 0;
     }
-
-    TranslateOperation *translateOperation = new TranslateOperation(toSocket->getDataType());
-    translateOperation->getInputSocket(1)->setResizeMode(ResizeMode::None);
-    translateOperation->getInputSocket(2)->setResizeMode(ResizeMode::None);
-    if (!first) {
-      first = translateOperation;
-    }
-    SetValueOperation *xop = new SetValueOperation();
-    xop->setValue(addX);
-    builder.addLink(xop->getOutputSocket(), translateOperation->getInputSocket(1));
-    SetValueOperation *yop = new SetValueOperation();
-    yop->setValue(addY);
-    builder.addLink(yop->getOutputSocket(), translateOperation->getInputSocket(2));
-    builder.addOperation(xop);
-    builder.addOperation(yop);
-
-    unsigned int resolution[2] = {toOperation->getWidth(), toOperation->getHeight()};
-    translateOperation->setResolution(resolution);
-    xop->setResolution(resolution);
-    yop->setResolution(resolution);
-    builder.addOperation(translateOperation);
-
-    if (doScale) {
-      translateOperation->getInputSocket(0)->setResizeMode(ResizeMode::None);
-      builder.addLink(scaleOperation->getOutputSocket(), translateOperation->getInputSocket(0));
-    }
-
-    /* remove previous link and replace */
-    builder.removeInputLink(toSocket);
-    first->getInputSocket(0)->setResizeMode(ResizeMode::None);
-    toSocket->setResizeMode(ResizeMode::None);
-    builder.addLink(fromSocket, first->getInputSocket(0));
-    builder.addLink(translateOperation->getOutputSocket(), toSocket);
+    scale_operation->set_canvas(scale_canvas);
+    sxop->set_canvas(scale_canvas);
+    syop->set_canvas(scale_canvas);
+    builder.add_operation(scale_operation);
   }
+
+  TranslateOperation *translate_operation = new TranslateOperation(to_socket->get_data_type());
+  translate_operation->get_input_socket(1)->set_resize_mode(ResizeMode::None);
+  translate_operation->get_input_socket(2)->set_resize_mode(ResizeMode::None);
+  if (!first) {
+    first = translate_operation;
+  }
+  SetValueOperation *xop = new SetValueOperation();
+  xop->set_value(addX);
+  builder.add_link(xop->get_output_socket(), translate_operation->get_input_socket(1));
+  SetValueOperation *yop = new SetValueOperation();
+  yop->set_value(addY);
+  builder.add_link(yop->get_output_socket(), translate_operation->get_input_socket(2));
+  builder.add_operation(xop);
+  builder.add_operation(yop);
+
+  rcti translate_canvas = to_operation->get_canvas();
+  if (mode == ResizeMode::Align) {
+    translate_canvas.xmax = translate_canvas.xmin + from_width;
+    translate_canvas.ymax = translate_canvas.ymin + from_height;
+  }
+  translate_operation->set_canvas(translate_canvas);
+  xop->set_canvas(translate_canvas);
+  yop->set_canvas(translate_canvas);
+  builder.add_operation(translate_operation);
+
+  if (do_scale) {
+    translate_operation->get_input_socket(0)->set_resize_mode(ResizeMode::None);
+    builder.add_link(scale_operation->get_output_socket(),
+                     translate_operation->get_input_socket(0));
+  }
+
+  /* remove previous link and replace */
+  builder.remove_input_link(to_socket);
+  first->get_input_socket(0)->set_resize_mode(ResizeMode::None);
+  to_socket->set_resize_mode(ResizeMode::None);
+  builder.add_link(from_socket, first->get_input_socket(0));
+  builder.add_link(translate_operation->get_output_socket(), to_socket);
 }
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Converter.h b/source/blender/compositor/intern/COM_Converter.h
index 28136437103..39bd44e8a9f 100644
--- a/source/blender/compositor/intern/COM_Converter.h
+++ b/source/blender/compositor/intern/COM_Converter.h
@@ -18,8 +18,6 @@
 
 #pragma once
 
-#include "COM_NodeOperation.h"
-
 #ifdef WITH_CXX_GUARDEDALLOC
 #  include "MEM_guardedalloc.h"
 #endif
@@ -29,6 +27,7 @@ struct bNode;
 namespace blender::compositor {
 
 class Node;
+class NodeOperation;
 class NodeOperationInput;
 class NodeOperationOutput;
 class NodeOperationBuilder;
@@ -65,8 +64,8 @@ NodeOperation *COM_convert_data_type(const NodeOperationOutput &from,
  * \note Conversion logic is implemented in this function.
  * \see InputSocketResizeMode for the possible conversions.
  */
-void COM_convert_resolution(NodeOperationBuilder &builder,
-                            NodeOperationOutput *fromSocket,
-                            NodeOperationInput *toSocket);
+void COM_convert_canvas(NodeOperationBuilder &builder,
+                        NodeOperationOutput *from_socket,
+                        NodeOperationInput *to_socket);
 
 }  // namespace blender::compositor
diff --git a/source/blender/compositor/intern/COM_Debug.cc b/source/blender/compositor/intern/COM_Debug.cc
index 007085ee528..50a69e55b2b 100644
--- a/source/blender/compositor/intern/COM_Debug.cc
+++ b/source/blender/compositor/intern/COM_Debug.cc
@@ -18,26 +18,16 @@
 
 #include "COM_Debug.h"
 
-#include 
-#include 
-#include 
-
 extern "C" {
 #include "BLI_fileops.h"
 #include "BLI_path_util.h"
-#include "BLI_string.h"
-#include "BLI_sys_types.h"
 
 #include "BKE_appdir.h"
-#include "BKE_node.h"
-#include "DNA_node_types.h"
 #include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
 }
 
-#include "COM_ExecutionSystem.h"
-#include "COM_Node.h"
-
+#include "COM_ExecutionGroup.h"
 #include "COM_ReadBufferOperation.h"
 #include "COM_SetValueOperation.h"
 #include "COM_ViewerOperation.h"
@@ -45,12 +35,12 @@ extern "C" {
 
 namespace blender::compositor {
 
-int DebugInfo::m_file_index = 0;
-DebugInfo::NodeNameMap DebugInfo::m_node_names;
-DebugInfo::OpNameMap DebugInfo::m_op_names;
-std::string DebugInfo::m_current_node_name;
-std::string DebugInfo::m_current_op_name;
-DebugInfo::GroupStateMap DebugInfo::m_group_states;
+int DebugInfo::file_index_ = 0;
+DebugInfo::NodeNameMap DebugInfo::node_names_;
+DebugInfo::OpNameMap DebugInfo::op_names_;
+std::string DebugInfo::current_node_name_;
+std::string DebugInfo::current_op_name_;
+DebugInfo::GroupStateMap DebugInfo::group_states_;
 
 static std::string operation_class_name(const NodeOperation *op)
 {
@@ -63,8 +53,8 @@ static std::string operation_class_name(const NodeOperation *op)
 
 std::string DebugInfo::node_name(const Node *node)
 {
-  NodeNameMap::const_iterator it = m_node_names.find(node);
-  if (it != m_node_names.end()) {
+  NodeNameMap::const_iterator it = node_names_.find(node);
+  if (it != node_names_.end()) {
     return it->second;
   }
   return "";
@@ -72,8 +62,8 @@ std::string DebugInfo::node_name(const Node *node)
 
 std::string DebugInfo::operation_name(const NodeOperation *op)
 {
-  OpNameMap::const_iterator it = m_op_names.find(op);
-  if (it != m_op_names.end()) {
+  OpNameMap::const_iterator it = op_names_.find(op);
+  if (it != op_names_.end()) {
     return it->second;
   }
   return "";
@@ -90,14 +80,14 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
   std::string fillcolor = "gainsboro";
   if (operation->get_flags().is_viewer_operation) {
     const ViewerOperation *viewer = (const ViewerOperation *)operation;
-    if (viewer->isActiveViewerOutput()) {
+    if (viewer->is_active_viewer_output()) {
       fillcolor = "lightskyblue1";
     }
     else {
       fillcolor = "lightskyblue3";
     }
   }
-  else if (operation->isOutputOperation(system->getContext().isRendering())) {
+  else if (operation->is_output_operation(system->get_context().is_rendering())) {
     fillcolor = "dodgerblue1";
   }
   else if (operation->get_flags().is_set_operation) {
@@ -122,16 +112,16 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
                   " [fillcolor=%s,style=filled,shape=record,label=\"{",
                   fillcolor.c_str());
 
-  int totinputs = operation->getNumberOfInputSockets();
+  int totinputs = operation->get_number_of_input_sockets();
   if (totinputs != 0) {
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
     for (int k = 0; k < totinputs; k++) {
-      NodeOperationInput *socket = operation->getInputSocket(k);
+      NodeOperationInput *socket = operation->get_input_socket(k);
       if (k != 0) {
         len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
       }
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "", socket);
-      switch (socket->getDataType()) {
+      switch (socket->get_data_type()) {
         case DataType::Value:
           len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "Value");
           break;
@@ -162,22 +152,24 @@ int DebugInfo::graphviz_operation(const ExecutionSystem *system,
 
   len += snprintf(str + len,
                   maxlen > len ? maxlen - len : 0,
-                  "#%d (%u,%u)",
+                  "#%d (%i,%i) (%u,%u)",
                   operation->get_id(),
-                  operation->getWidth(),
-                  operation->getHeight());
+                  operation->get_canvas().xmin,
+                  operation->get_canvas().ymin,
+                  operation->get_width(),
+                  operation->get_height());
 
-  int totoutputs = operation->getNumberOfOutputSockets();
+  int totoutputs = operation->get_number_of_output_sockets();
   if (totoutputs != 0) {
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "{");
     for (int k = 0; k < totoutputs; k++) {
-      NodeOperationOutput *socket = operation->getOutputSocket(k);
+      NodeOperationOutput *socket = operation->get_output_socket(k);
       if (k != 0) {
         len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "|");
       }
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "", socket);
-      switch (socket->getDataType()) {
+      switch (socket->get_data_type()) {
         case DataType::Value: {
           ConstantOperation *constant = operation->get_flags().is_constant_operation ?
                                             static_cast(operation) :
@@ -308,25 +300,25 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
 
   std::map> op_groups;
   int index = 0;
-  for (const ExecutionGroup *group : system->m_groups) {
+  for (const ExecutionGroup *group : system->groups_) {
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "// GROUP: %d\r\n", index);
     len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "subgraph cluster_%d{\r\n", index);
     /* used as a check for executing group */
-    if (m_group_states[group] == EG_WAIT) {
+    if (group_states_[group] == EG_WAIT) {
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=dashed\r\n");
     }
-    else if (m_group_states[group] == EG_RUNNING) {
+    else if (group_states_[group] == EG_RUNNING) {
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=filled\r\n");
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "color=black\r\n");
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "fillcolor=firebrick1\r\n");
     }
-    else if (m_group_states[group] == EG_FINISHED) {
+    else if (group_states_[group] == EG_FINISHED) {
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "style=filled\r\n");
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "color=black\r\n");
       len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "fillcolor=chartreuse4\r\n");
     }
 
-    for (NodeOperation *operation : group->m_operations) {
+    for (NodeOperation *operation : group->operations_) {
 
       sprintf(strbuf, "_%p", group);
       op_groups[operation].push_back(std::string(strbuf));
@@ -340,7 +332,7 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
   }
 
   /* operations not included in any group */
-  for (NodeOperation *operation : system->m_operations) {
+  for (NodeOperation *operation : system->operations_) {
     if (op_groups.find(operation) != op_groups.end()) {
       continue;
     }
@@ -351,10 +343,10 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
         system, operation, nullptr, str + len, maxlen > len ? maxlen - len : 0);
   }
 
-  for (NodeOperation *operation : system->m_operations) {
+  for (NodeOperation *operation : system->operations_) {
     if (operation->get_flags().is_read_buffer_operation) {
       ReadBufferOperation *read = (ReadBufferOperation *)operation;
-      WriteBufferOperation *write = read->getMemoryProxy()->getWriteBufferOperation();
+      WriteBufferOperation *write = read->get_memory_proxy()->get_write_buffer_operation();
       std::vector &read_groups = op_groups[read];
       std::vector &write_groups = op_groups[write];
 
@@ -372,16 +364,16 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
     }
   }
 
-  for (NodeOperation *op : system->m_operations) {
-    for (NodeOperationInput &to : op->m_inputs) {
-      NodeOperationOutput *from = to.getLink();
+  for (NodeOperation *op : system->operations_) {
+    for (NodeOperationInput &to : op->inputs_) {
+      NodeOperationOutput *from = to.get_link();
 
       if (!from) {
         continue;
       }
 
       std::string color;
-      switch (from->getDataType()) {
+      switch (from->get_data_type()) {
         case DataType::Value:
           color = "gray";
           break;
@@ -393,8 +385,8 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
           break;
       }
 
-      NodeOperation *to_op = &to.getOperation();
-      NodeOperation *from_op = &from->getOperation();
+      NodeOperation *to_op = &to.get_operation();
+      NodeOperation *from_op = &from->get_operation();
       std::vector &from_groups = op_groups[from_op];
       std::vector &to_groups = op_groups[to_op];
 
@@ -424,9 +416,9 @@ bool DebugInfo::graphviz_system(const ExecutionSystem *system, char *str, int ma
     }
   }
 
-  const bool has_execution_groups = system->getContext().get_execution_model() ==
+  const bool has_execution_groups = system->get_context().get_execution_model() ==
                                         eExecutionModel::Tiled &&
-                                    system->m_groups.size() > 0;
+                                    system->groups_.size() > 0;
   len += graphviz_legend(str + len, maxlen > len ? maxlen - len : 0, has_execution_groups);
 
   len += snprintf(str + len, maxlen > len ? maxlen - len : 0, "}\r\n");
@@ -445,13 +437,13 @@ void DebugInfo::graphviz(const ExecutionSystem *system, StringRefNull name)
     char filename[FILE_MAX];
 
     if (name.is_empty()) {
-      BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", m_file_index);
+      BLI_snprintf(basename, sizeof(basename), "compositor_%d.dot", file_index_);
     }
     else {
       BLI_strncpy(basename, (name + ".dot").c_str(), sizeof(basename));
     }
     BLI_join_dirfile(filename, sizeof(filename), BKE_tempdir_session(), basename);
-    m_file_index++;
+    file_index_++;
 
     std::cout << "Writing compositor debug to: " << filename << "\n";
 
@@ -468,11 +460,13 @@ static std::string get_operations_export_dir()
 
 void DebugInfo::export_operation(const NodeOperation *op, MemoryBuffer *render)
 {
-  ImBuf *ibuf = IMB_allocFromBuffer(nullptr,
-                                    render->getBuffer(),
-                                    render->getWidth(),
-                                    render->getHeight(),
-                                    render->get_num_channels());
+  const int width = render->get_width();
+  const int height = render->get_height();
+  const int num_channels = render->get_num_channels();
+
+  ImBuf *ibuf = IMB_allocImBuf(width, height, 8 * num_channels, IB_rectfloat);
+  MemoryBuffer mem_ibuf(ibuf->rect_float, 4, width, height);
+  mem_ibuf.copy_from(render, render->get_rect(), 0, num_channels, 0);
 
   const std::string file_name = operation_class_name(op) + "_" + std::to_string(op->get_id()) +
                                 ".png";
diff --git a/source/blender/compositor/intern/COM_Debug.h b/source/blender/compositor/intern/COM_Debug.h
index 23d99c7e529..021d6e744fb 100644
--- a/source/blender/compositor/intern/COM_Debug.h
+++ b/source/blender/compositor/intern/COM_Debug.h
@@ -21,9 +21,11 @@
 #include 
 #include 
 
+#include "BLI_vector.hh"
+
 #include "COM_ExecutionSystem.h"
-#include "COM_NodeOperation.h"
-#include "COM_defines.h"
+#include "COM_MemoryBuffer.h"
+#include "COM_Node.h"
 
 namespace blender::compositor {
 
@@ -34,6 +36,7 @@ static constexpr bool COM_GRAPHVIZ_SHOW_NODE_NAME = false;
 static constexpr bool COM_EXPORT_OPERATION_BUFFERS = false;
 
 class Node;
+class NodeOperation;
 class ExecutionSystem;
 class ExecutionGroup;
 
@@ -49,33 +52,33 @@ class DebugInfo {
   static std::string operation_name(const NodeOperation *op);
 
  private:
-  static int m_file_index;
+  static int file_index_;
   /** Map nodes to usable names for debug output. */
-  static NodeNameMap m_node_names;
+  static NodeNameMap node_names_;
   /** Map operations to usable names for debug output. */
-  static OpNameMap m_op_names;
+  static OpNameMap op_names_;
   /** Base name for all operations added by a node. */
-  static std::string m_current_node_name;
+  static std::string current_node_name_;
   /** Base name for automatic sub-operations. */
-  static std::string m_current_op_name;
+  static std::string current_op_name_;
   /** For visualizing group states. */
-  static GroupStateMap m_group_states;
+  static GroupStateMap group_states_;
 
  public:
   static void convert_started()
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_op_names.clear();
+      op_names_.clear();
     }
   }
 
   static void execute_started(const ExecutionSystem *system)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_file_index = 1;
-      m_group_states.clear();
-      for (ExecutionGroup *execution_group : system->m_groups) {
-        m_group_states[execution_group] = EG_WAIT;
+      file_index_ = 1;
+      group_states_.clear();
+      for (ExecutionGroup *execution_group : system->groups_) {
+        group_states_[execution_group] = EG_WAIT;
       }
     }
     if (COM_EXPORT_OPERATION_BUFFERS) {
@@ -86,41 +89,41 @@ class DebugInfo {
   static void node_added(const Node *node)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_node_names[node] = std::string(node->getbNode() ? node->getbNode()->name : "");
+      node_names_[node] = std::string(node->get_bnode() ? node->get_bnode()->name : "");
     }
   }
 
   static void node_to_operations(const Node *node)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_current_node_name = m_node_names[node];
+      current_node_name_ = node_names_[node];
     }
   }
 
   static void operation_added(const NodeOperation *operation)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_op_names[operation] = m_current_node_name;
+      op_names_[operation] = current_node_name_;
     }
   };
 
   static void operation_read_write_buffer(const NodeOperation *operation)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_current_op_name = m_op_names[operation];
+      current_op_name_ = op_names_[operation];
     }
   };
 
   static void execution_group_started(const ExecutionGroup *group)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_group_states[group] = EG_RUNNING;
+      group_states_[group] = EG_RUNNING;
     }
   };
   static void execution_group_finished(const ExecutionGroup *group)
   {
     if (COM_EXPORT_GRAPHVIZ) {
-      m_group_states[group] = EG_FINISHED;
+      group_states_[group] = EG_FINISHED;
     }
   };
 
diff --git a/source/blender/compositor/intern/COM_Device.h b/source/blender/compositor/intern/COM_Device.h
index c848672a405..a409ddf97a5 100644
--- a/source/blender/compositor/intern/COM_Device.h
+++ b/source/blender/compositor/intern/COM_Device.h
@@ -18,10 +18,14 @@
 
 #pragma once
 
-#include "COM_WorkPackage.h"
+#ifdef WITH_CXX_GUARDEDALLOC
+#  include "MEM_guardedalloc.h"
+#endif
 
 namespace blender::compositor {
 
+struct WorkPackage;
+
 /**
  * \brief Abstract class for device implementations to be used by the Compositor.
  * devices are queried, initialized and used by the WorkScheduler.
diff --git a/source/blender/compositor/intern/COM_Enums.cc b/source/blender/compositor/intern/COM_Enums.cc
index 2f20d2652ba..63a5a78436b 100644
--- a/source/blender/compositor/intern/COM_Enums.cc
+++ b/source/blender/compositor/intern/COM_Enums.cc
@@ -17,7 +17,6 @@
  */
 
 #include "COM_Enums.h"
-#include "BLI_rect.h"
 
 namespace blender::compositor {
 
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.cc b/source/blender/compositor/intern/COM_ExecutionGroup.cc
index 505a4066a25..d2198fc14e8 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.cc
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.cc
@@ -16,36 +16,21 @@
  * Copyright 2011, Blender Foundation.
  */
 
-#include 
-#include 
-#include 
-#include 
-
-#include "atomic_ops.h"
-
+#include "COM_ExecutionGroup.h"
 #include "COM_ChunkOrder.h"
 #include "COM_Debug.h"
-#include "COM_ExecutionGroup.h"
-#include "COM_ExecutionSystem.h"
 #include "COM_ReadBufferOperation.h"
 #include "COM_ViewerOperation.h"
 #include "COM_WorkScheduler.h"
 #include "COM_WriteBufferOperation.h"
 #include "COM_defines.h"
 
-#include "BLI_math.h"
 #include "BLI_rand.hh"
-#include "BLI_string.h"
 
 #include "BLT_translation.h"
 
-#include "MEM_guardedalloc.h"
-
 #include "PIL_time.h"
 
-#include "WM_api.h"
-#include "WM_types.h"
-
 namespace blender::compositor {
 
 std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags)
@@ -70,36 +55,36 @@ std::ostream &operator<<(std::ostream &os, const ExecutionGroupFlags &flags)
 
 ExecutionGroup::ExecutionGroup(int id)
 {
-  m_id = id;
-  this->m_bTree = nullptr;
-  this->m_height = 0;
-  this->m_width = 0;
-  this->m_max_read_buffer_offset = 0;
-  this->m_x_chunks_len = 0;
-  this->m_y_chunks_len = 0;
-  this->m_chunks_len = 0;
-  this->m_chunks_finished = 0;
-  BLI_rcti_init(&this->m_viewerBorder, 0, 0, 0, 0);
-  this->m_executionStartTime = 0;
+  id_ = id;
+  bTree_ = nullptr;
+  height_ = 0;
+  width_ = 0;
+  max_read_buffer_offset_ = 0;
+  x_chunks_len_ = 0;
+  y_chunks_len_ = 0;
+  chunks_len_ = 0;
+  chunks_finished_ = 0;
+  BLI_rcti_init(&viewer_border_, 0, 0, 0, 0);
+  execution_start_time_ = 0;
 }
 
 std::ostream &operator<<(std::ostream &os, const ExecutionGroup &execution_group)
 {
   os << "ExecutionGroup(id=" << execution_group.get_id();
   os << ",flags={" << execution_group.get_flags() << "}";
-  os << ",operation=" << *execution_group.getOutputOperation() << "";
+  os << ",operation=" << *execution_group.get_output_operation() << "";
   os << ")";
   return os;
 }
 
-eCompositorPriority ExecutionGroup::getRenderPriority()
+eCompositorPriority ExecutionGroup::get_render_priority()
 {
-  return this->getOutputOperation()->getRenderPriority();
+  return this->get_output_operation()->get_render_priority();
 }
 
 bool ExecutionGroup::can_contain(NodeOperation &operation)
 {
-  if (!m_flags.initialized) {
+  if (!flags_.initialized) {
     return true;
   }
 
@@ -114,7 +99,7 @@ bool ExecutionGroup::can_contain(NodeOperation &operation)
   }
 
   /* complex groups don't allow further ops (except read buffer and values, see above) */
-  if (m_flags.complex) {
+  if (flags_.complex) {
     return false;
   }
   /* complex ops can't be added to other groups (except their own, which they initialize, see
@@ -126,7 +111,7 @@ bool ExecutionGroup::can_contain(NodeOperation &operation)
   return true;
 }
 
-bool ExecutionGroup::addOperation(NodeOperation *operation)
+bool ExecutionGroup::add_operation(NodeOperation *operation)
 {
   if (!can_contain(*operation)) {
     return false;
@@ -134,34 +119,34 @@ bool ExecutionGroup::addOperation(NodeOperation *operation)
 
   if (!operation->get_flags().is_read_buffer_operation &&
       !operation->get_flags().is_write_buffer_operation) {
-    m_flags.complex = operation->get_flags().complex;
-    m_flags.open_cl = operation->get_flags().open_cl;
-    m_flags.single_threaded = operation->get_flags().single_threaded;
-    m_flags.initialized = true;
+    flags_.complex = operation->get_flags().complex;
+    flags_.open_cl = operation->get_flags().open_cl;
+    flags_.single_threaded = operation->get_flags().single_threaded;
+    flags_.initialized = true;
   }
 
-  m_operations.append(operation);
+  operations_.append(operation);
 
   return true;
 }
 
-NodeOperation *ExecutionGroup::getOutputOperation() const
+NodeOperation *ExecutionGroup::get_output_operation() const
 {
   return this
-      ->m_operations[0]; /* the first operation of the group is always the output operation. */
+      ->operations_[0]; /* the first operation of the group is always the output operation. */
 }
 
 void ExecutionGroup::init_work_packages()
 {
-  m_work_packages.clear();
-  if (this->m_chunks_len != 0) {
-    m_work_packages.resize(this->m_chunks_len);
-    for (unsigned int index = 0; index < m_chunks_len; index++) {
-      m_work_packages[index].type = eWorkPackageType::Tile;
-      m_work_packages[index].state = eWorkPackageState::NotScheduled;
-      m_work_packages[index].execution_group = this;
-      m_work_packages[index].chunk_number = index;
-      determineChunkRect(&m_work_packages[index].rect, index);
+  work_packages_.clear();
+  if (chunks_len_ != 0) {
+    work_packages_.resize(chunks_len_);
+    for (unsigned int index = 0; index < chunks_len_; index++) {
+      work_packages_[index].type = eWorkPackageType::Tile;
+      work_packages_[index].state = eWorkPackageState::NotScheduled;
+      work_packages_[index].execution_group = this;
+      work_packages_[index].chunk_number = index;
+      determine_chunk_rect(&work_packages_[index].rect, index);
     }
   }
 }
@@ -169,68 +154,68 @@ void ExecutionGroup::init_work_packages()
 void ExecutionGroup::init_read_buffer_operations()
 {
   unsigned int max_offset = 0;
-  for (NodeOperation *operation : m_operations) {
+  for (NodeOperation *operation : operations_) {
     if (operation->get_flags().is_read_buffer_operation) {
-      ReadBufferOperation *readOperation = static_cast(operation);
-      this->m_read_operations.append(readOperation);
-      max_offset = MAX2(max_offset, readOperation->getOffset());
+      ReadBufferOperation *read_operation = static_cast(operation);
+      read_operations_.append(read_operation);
+      max_offset = MAX2(max_offset, read_operation->get_offset());
     }
   }
   max_offset++;
-  this->m_max_read_buffer_offset = max_offset;
+  max_read_buffer_offset_ = max_offset;
 }
 
-void ExecutionGroup::initExecution()
+void ExecutionGroup::init_execution()
 {
   init_number_of_chunks();
   init_work_packages();
   init_read_buffer_operations();
 }
 
-void ExecutionGroup::deinitExecution()
+void ExecutionGroup::deinit_execution()
 {
-  m_work_packages.clear();
-  this->m_chunks_len = 0;
-  this->m_x_chunks_len = 0;
-  this->m_y_chunks_len = 0;
-  this->m_read_operations.clear();
-  this->m_bTree = nullptr;
+  work_packages_.clear();
+  chunks_len_ = 0;
+  x_chunks_len_ = 0;
+  y_chunks_len_ = 0;
+  read_operations_.clear();
+  bTree_ = nullptr;
 }
 
-void ExecutionGroup::determineResolution(unsigned int resolution[2])
+void ExecutionGroup::determine_resolution(unsigned int resolution[2])
 {
-  NodeOperation *operation = this->getOutputOperation();
-  resolution[0] = operation->getWidth();
-  resolution[1] = operation->getHeight();
-  this->setResolution(resolution);
-  BLI_rcti_init(&this->m_viewerBorder, 0, this->m_width, 0, this->m_height);
+  NodeOperation *operation = this->get_output_operation();
+  resolution[0] = operation->get_width();
+  resolution[1] = operation->get_height();
+  this->set_resolution(resolution);
+  BLI_rcti_init(&viewer_border_, 0, width_, 0, height_);
 }
 
 void ExecutionGroup::init_number_of_chunks()
 {
-  if (this->m_flags.single_threaded) {
-    this->m_x_chunks_len = 1;
-    this->m_y_chunks_len = 1;
-    this->m_chunks_len = 1;
+  if (flags_.single_threaded) {
+    x_chunks_len_ = 1;
+    y_chunks_len_ = 1;
+    chunks_len_ = 1;
   }
   else {
-    const float chunkSizef = this->m_chunkSize;
-    const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
-    const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
-    this->m_x_chunks_len = ceil(border_width / chunkSizef);
-    this->m_y_chunks_len = ceil(border_height / chunkSizef);
-    this->m_chunks_len = this->m_x_chunks_len * this->m_y_chunks_len;
+    const float chunk_sizef = chunk_size_;
+    const int border_width = BLI_rcti_size_x(&viewer_border_);
+    const int border_height = BLI_rcti_size_y(&viewer_border_);
+    x_chunks_len_ = ceil(border_width / chunk_sizef);
+    y_chunks_len_ = ceil(border_height / chunk_sizef);
+    chunks_len_ = x_chunks_len_ * y_chunks_len_;
   }
 }
 
 blender::Array ExecutionGroup::get_execution_order() const
 {
-  blender::Array chunk_order(m_chunks_len);
-  for (int chunk_index = 0; chunk_index < this->m_chunks_len; chunk_index++) {
+  blender::Array chunk_order(chunks_len_);
+  for (int chunk_index = 0; chunk_index < chunks_len_; chunk_index++) {
     chunk_order[chunk_index] = chunk_index;
   }
 
-  NodeOperation *operation = this->getOutputOperation();
+  NodeOperation *operation = this->get_output_operation();
   float centerX = 0.5f;
   float centerY = 0.5f;
   ChunkOrdering order_type = ChunkOrdering::Default;
@@ -239,11 +224,11 @@ blender::Array ExecutionGroup::get_execution_order() const
     ViewerOperation *viewer = (ViewerOperation *)operation;
     centerX = viewer->getCenterX();
     centerY = viewer->getCenterY();
-    order_type = viewer->getChunkOrder();
+    order_type = viewer->get_chunk_order();
   }
 
-  const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
-  const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
+  const int border_width = BLI_rcti_size_x(&viewer_border_);
+  const int border_height = BLI_rcti_size_y(&viewer_border_);
   int index;
   switch (order_type) {
     case ChunkOrdering::Random: {
@@ -256,17 +241,17 @@ blender::Array ExecutionGroup::get_execution_order() const
     }
     case ChunkOrdering::CenterOut: {
       ChunkOrderHotspot hotspot(border_width * centerX, border_height * centerY, 0.0f);
-      blender::Array chunk_orders(m_chunks_len);
-      for (index = 0; index < this->m_chunks_len; index++) {
-        const WorkPackage &work_package = m_work_packages[index];
+      blender::Array chunk_orders(chunks_len_);
+      for (index = 0; index < chunks_len_; index++) {
+        const WorkPackage &work_package = work_packages_[index];
         chunk_orders[index].index = index;
-        chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
-        chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
+        chunk_orders[index].x = work_package.rect.xmin - viewer_border_.xmin;
+        chunk_orders[index].y = work_package.rect.ymin - viewer_border_.ymin;
         chunk_orders[index].update_distance(&hotspot, 1);
       }
 
-      std::sort(&chunk_orders[0], &chunk_orders[this->m_chunks_len - 1]);
-      for (index = 0; index < this->m_chunks_len; index++) {
+      std::sort(&chunk_orders[0], &chunk_orders[chunks_len_ - 1]);
+      for (index = 0; index < chunks_len_; index++) {
         chunk_order[index] = chunk_orders[index].index;
       }
 
@@ -279,7 +264,7 @@ blender::Array ExecutionGroup::get_execution_order() const
       unsigned int my = border_height / 2;
       unsigned int bx = mx + 2 * tx;
       unsigned int by = my + 2 * ty;
-      float addition = this->m_chunks_len / COM_RULE_OF_THIRDS_DIVIDER;
+      float addition = chunks_len_ / COM_RULE_OF_THIRDS_DIVIDER;
 
       ChunkOrderHotspot hotspots[9]{
           ChunkOrderHotspot(mx, my, addition * 0),
@@ -293,18 +278,18 @@ blender::Array ExecutionGroup::get_execution_order() const
           ChunkOrderHotspot(mx, by, addition * 8),
       };
 
-      blender::Array chunk_orders(m_chunks_len);
-      for (index = 0; index < this->m_chunks_len; index++) {
-        const WorkPackage &work_package = m_work_packages[index];
+      blender::Array chunk_orders(chunks_len_);
+      for (index = 0; index < chunks_len_; index++) {
+        const WorkPackage &work_package = work_packages_[index];
         chunk_orders[index].index = index;
-        chunk_orders[index].x = work_package.rect.xmin - this->m_viewerBorder.xmin;
-        chunk_orders[index].y = work_package.rect.ymin - this->m_viewerBorder.ymin;
+        chunk_orders[index].x = work_package.rect.xmin - viewer_border_.xmin;
+        chunk_orders[index].y = work_package.rect.ymin - viewer_border_.ymin;
         chunk_orders[index].update_distance(hotspots, 9);
       }
 
-      std::sort(&chunk_orders[0], &chunk_orders[this->m_chunks_len]);
+      std::sort(&chunk_orders[0], &chunk_orders[chunks_len_]);
 
-      for (index = 0; index < this->m_chunks_len; index++) {
+      for (index = 0; index < chunks_len_; index++) {
         chunk_order[index] = chunk_orders[index].index;
       }
 
@@ -323,23 +308,23 @@ blender::Array ExecutionGroup::get_execution_order() const
  */
 void ExecutionGroup::execute(ExecutionSystem *graph)
 {
-  const CompositorContext &context = graph->getContext();
-  const bNodeTree *bTree = context.getbNodeTree();
-  if (this->m_width == 0 || this->m_height == 0) {
+  const CompositorContext &context = graph->get_context();
+  const bNodeTree *bTree = context.get_bnodetree();
+  if (width_ == 0 || height_ == 0) {
     return;
   } /** \note Break out... no pixels to calculate. */
   if (bTree->test_break && bTree->test_break(bTree->tbh)) {
     return;
   } /** \note Early break out for blur and preview nodes. */
-  if (this->m_chunks_len == 0) {
+  if (chunks_len_ == 0) {
     return;
   } /** \note Early break out. */
   unsigned int chunk_index;
 
-  this->m_executionStartTime = PIL_check_seconds_timer();
+  execution_start_time_ = PIL_check_seconds_timer();
 
-  this->m_chunks_finished = 0;
-  this->m_bTree = bTree;
+  chunks_finished_ = 0;
+  bTree_ = bTree;
 
   blender::Array chunk_order = get_execution_order();
 
@@ -348,27 +333,26 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
 
   bool breaked = false;
   bool finished = false;
-  unsigned int startIndex = 0;
-  const int maxNumberEvaluated = BLI_system_thread_count() * 2;
+  unsigned int start_index = 0;
+  const int max_number_evaluated = BLI_system_thread_count() * 2;
 
   while (!finished && !breaked) {
-    bool startEvaluated = false;
+    bool start_evaluated = false;
     finished = true;
-    int numberEvaluated = 0;
+    int number_evaluated = 0;
 
-    for (int index = startIndex;
-         index < this->m_chunks_len && numberEvaluated < maxNumberEvaluated;
+    for (int index = start_index; index < chunks_len_ && number_evaluated < max_number_evaluated;
          index++) {
       chunk_index = chunk_order[index];
-      int yChunk = chunk_index / this->m_x_chunks_len;
-      int xChunk = chunk_index - (yChunk * this->m_x_chunks_len);
-      const WorkPackage &work_package = m_work_packages[chunk_index];
+      int y_chunk = chunk_index / x_chunks_len_;
+      int x_chunk = chunk_index - (y_chunk * x_chunks_len_);
+      const WorkPackage &work_package = work_packages_[chunk_index];
       switch (work_package.state) {
         case eWorkPackageState::NotScheduled: {
-          scheduleChunkWhenPossible(graph, xChunk, yChunk);
+          schedule_chunk_when_possible(graph, x_chunk, y_chunk);
           finished = false;
-          startEvaluated = true;
-          numberEvaluated++;
+          start_evaluated = true;
+          number_evaluated++;
 
           if (bTree->update_draw) {
             bTree->update_draw(bTree->udh);
@@ -377,13 +361,13 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
         }
         case eWorkPackageState::Scheduled: {
           finished = false;
-          startEvaluated = true;
-          numberEvaluated++;
+          start_evaluated = true;
+          number_evaluated++;
           break;
         }
         case eWorkPackageState::Executed: {
-          if (!startEvaluated) {
-            startIndex = index + 1;
+          if (!start_evaluated) {
+            start_index = index + 1;
           }
         }
       };
@@ -399,139 +383,135 @@ void ExecutionGroup::execute(ExecutionSystem *graph)
   DebugInfo::graphviz(graph);
 }
 
-MemoryBuffer **ExecutionGroup::getInputBuffersOpenCL(int chunkNumber)
+MemoryBuffer **ExecutionGroup::get_input_buffers_opencl(int chunk_number)
 {
-  WorkPackage &work_package = m_work_packages[chunkNumber];
+  WorkPackage &work_package = work_packages_[chunk_number];
 
-  MemoryBuffer **memoryBuffers = (MemoryBuffer **)MEM_callocN(
-      sizeof(MemoryBuffer *) * this->m_max_read_buffer_offset, __func__);
+  MemoryBuffer **memory_buffers = (MemoryBuffer **)MEM_callocN(
+      sizeof(MemoryBuffer *) * max_read_buffer_offset_, __func__);
   rcti output;
-  for (ReadBufferOperation *readOperation : m_read_operations) {
-    MemoryProxy *memoryProxy = readOperation->getMemoryProxy();
-    this->determineDependingAreaOfInterest(&work_package.rect, readOperation, &output);
-    MemoryBuffer *memoryBuffer = memoryProxy->getExecutor()->constructConsolidatedMemoryBuffer(
-        *memoryProxy, output);
-    memoryBuffers[readOperation->getOffset()] = memoryBuffer;
+  for (ReadBufferOperation *read_operation : read_operations_) {
+    MemoryProxy *memory_proxy = read_operation->get_memory_proxy();
+    this->determine_depending_area_of_interest(&work_package.rect, read_operation, &output);
+    MemoryBuffer *memory_buffer =
+        memory_proxy->get_executor()->construct_consolidated_memory_buffer(*memory_proxy, output);
+    memory_buffers[read_operation->get_offset()] = memory_buffer;
   }
-  return memoryBuffers;
+  return memory_buffers;
 }
 
-MemoryBuffer *ExecutionGroup::constructConsolidatedMemoryBuffer(MemoryProxy &memoryProxy,
-                                                                rcti &rect)
+MemoryBuffer *ExecutionGroup::construct_consolidated_memory_buffer(MemoryProxy &memory_proxy,
+                                                                   rcti &rect)
 {
-  MemoryBuffer *imageBuffer = memoryProxy.getBuffer();
-  MemoryBuffer *result = new MemoryBuffer(&memoryProxy, rect, MemoryBufferState::Temporary);
-  result->fill_from(*imageBuffer);
+  MemoryBuffer *image_buffer = memory_proxy.get_buffer();
+  MemoryBuffer *result = new MemoryBuffer(&memory_proxy, rect, MemoryBufferState::Temporary);
+  result->fill_from(*image_buffer);
   return result;
 }
 
-void ExecutionGroup::finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers)
+void ExecutionGroup::finalize_chunk_execution(int chunk_number, MemoryBuffer **memory_buffers)
 {
-  WorkPackage &work_package = m_work_packages[chunkNumber];
+  WorkPackage &work_package = work_packages_[chunk_number];
   if (work_package.state == eWorkPackageState::Scheduled) {
     work_package.state = eWorkPackageState::Executed;
   }
 
-  atomic_add_and_fetch_u(&this->m_chunks_finished, 1);
-  if (memoryBuffers) {
-    for (unsigned int index = 0; index < this->m_max_read_buffer_offset; index++) {
-      MemoryBuffer *buffer = memoryBuffers[index];
+  atomic_add_and_fetch_u(&chunks_finished_, 1);
+  if (memory_buffers) {
+    for (unsigned int index = 0; index < max_read_buffer_offset_; index++) {
+      MemoryBuffer *buffer = memory_buffers[index];
       if (buffer) {
-        if (buffer->isTemporarily()) {
-          memoryBuffers[index] = nullptr;
+        if (buffer->is_temporarily()) {
+          memory_buffers[index] = nullptr;
           delete buffer;
         }
       }
     }
-    MEM_freeN(memoryBuffers);
+    MEM_freeN(memory_buffers);
   }
-  if (this->m_bTree) {
+  if (bTree_) {
     /* Status report is only performed for top level Execution Groups. */
-    float progress = this->m_chunks_finished;
-    progress /= this->m_chunks_len;
-    this->m_bTree->progress(this->m_bTree->prh, progress);
+    float progress = chunks_finished_;
+    progress /= chunks_len_;
+    bTree_->progress(bTree_->prh, progress);
 
     char buf[128];
-    BLI_snprintf(buf,
-                 sizeof(buf),
-                 TIP_("Compositing | Tile %u-%u"),
-                 this->m_chunks_finished,
-                 this->m_chunks_len);
-    this->m_bTree->stats_draw(this->m_bTree->sdh, buf);
+    BLI_snprintf(
+        buf, sizeof(buf), TIP_("Compositing | Tile %u-%u"), chunks_finished_, chunks_len_);
+    bTree_->stats_draw(bTree_->sdh, buf);
   }
 }
 
-inline void ExecutionGroup::determineChunkRect(rcti *r_rect,
-                                               const unsigned int xChunk,
-                                               const unsigned int yChunk) const
+inline void ExecutionGroup::determine_chunk_rect(rcti *r_rect,
+                                                 const unsigned int x_chunk,
+                                                 const unsigned int y_chunk) const
 {
-  const int border_width = BLI_rcti_size_x(&this->m_viewerBorder);
-  const int border_height = BLI_rcti_size_y(&this->m_viewerBorder);
+  const int border_width = BLI_rcti_size_x(&viewer_border_);
+  const int border_height = BLI_rcti_size_y(&viewer_border_);
 
-  if (this->m_flags.single_threaded) {
-    BLI_rcti_init(
-        r_rect, this->m_viewerBorder.xmin, border_width, this->m_viewerBorder.ymin, border_height);
+  if (flags_.single_threaded) {
+    BLI_rcti_init(r_rect, viewer_border_.xmin, border_width, viewer_border_.ymin, border_height);
   }
   else {
-    const unsigned int minx = xChunk * this->m_chunkSize + this->m_viewerBorder.xmin;
-    const unsigned int miny = yChunk * this->m_chunkSize + this->m_viewerBorder.ymin;
-    const unsigned int width = MIN2((unsigned int)this->m_viewerBorder.xmax, this->m_width);
-    const unsigned int height = MIN2((unsigned int)this->m_viewerBorder.ymax, this->m_height);
+    const unsigned int minx = x_chunk * chunk_size_ + viewer_border_.xmin;
+    const unsigned int miny = y_chunk * chunk_size_ + viewer_border_.ymin;
+    const unsigned int width = MIN2((unsigned int)viewer_border_.xmax, width_);
+    const unsigned int height = MIN2((unsigned int)viewer_border_.ymax, height_);
     BLI_rcti_init(r_rect,
-                  MIN2(minx, this->m_width),
-                  MIN2(minx + this->m_chunkSize, width),
-                  MIN2(miny, this->m_height),
-                  MIN2(miny + this->m_chunkSize, height));
+                  MIN2(minx, width_),
+                  MIN2(minx + chunk_size_, width),
+                  MIN2(miny, height_),
+                  MIN2(miny + chunk_size_, height));
   }
 }
 
-void ExecutionGroup::determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const
+void ExecutionGroup::determine_chunk_rect(rcti *r_rect, const unsigned int chunk_number) const
 {
-  const unsigned int yChunk = chunkNumber / this->m_x_chunks_len;
-  const unsigned int xChunk = chunkNumber - (yChunk * this->m_x_chunks_len);
-  determineChunkRect(r_rect, xChunk, yChunk);
+  const unsigned int y_chunk = chunk_number / x_chunks_len_;
+  const unsigned int x_chunk = chunk_number - (y_chunk * x_chunks_len_);
+  determine_chunk_rect(r_rect, x_chunk, y_chunk);
 }
 
-MemoryBuffer *ExecutionGroup::allocateOutputBuffer(rcti &rect)
+MemoryBuffer *ExecutionGroup::allocate_output_buffer(rcti &rect)
 {
   /* We assume that this method is only called from complex execution groups. */
-  NodeOperation *operation = this->getOutputOperation();
+  NodeOperation *operation = this->get_output_operation();
   if (operation->get_flags().is_write_buffer_operation) {
-    WriteBufferOperation *writeOperation = (WriteBufferOperation *)operation;
+    WriteBufferOperation *write_operation = (WriteBufferOperation *)operation;
     MemoryBuffer *buffer = new MemoryBuffer(
-        writeOperation->getMemoryProxy(), rect, MemoryBufferState::Temporary);
+        write_operation->get_memory_proxy(), rect, MemoryBufferState::Temporary);
     return buffer;
   }
   return nullptr;
 }
 
-bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area)
+bool ExecutionGroup::schedule_area_when_possible(ExecutionSystem *graph, rcti *area)
 {
-  if (this->m_flags.single_threaded) {
-    return scheduleChunkWhenPossible(graph, 0, 0);
+  if (flags_.single_threaded) {
+    return schedule_chunk_when_possible(graph, 0, 0);
   }
   /* Find all chunks inside the rect
    * determine `minxchunk`, `minychunk`, `maxxchunk`, `maxychunk`
    * where x and y are chunk-numbers. */
 
   int indexx, indexy;
-  int minx = max_ii(area->xmin - m_viewerBorder.xmin, 0);
-  int maxx = min_ii(area->xmax - m_viewerBorder.xmin, m_viewerBorder.xmax - m_viewerBorder.xmin);
-  int miny = max_ii(area->ymin - m_viewerBorder.ymin, 0);
-  int maxy = min_ii(area->ymax - m_viewerBorder.ymin, m_viewerBorder.ymax - m_viewerBorder.ymin);
-  int minxchunk = minx / (int)m_chunkSize;
-  int maxxchunk = (maxx + (int)m_chunkSize - 1) / (int)m_chunkSize;
-  int minychunk = miny / (int)m_chunkSize;
-  int maxychunk = (maxy + (int)m_chunkSize - 1) / (int)m_chunkSize;
+  int minx = max_ii(area->xmin - viewer_border_.xmin, 0);
+  int maxx = min_ii(area->xmax - viewer_border_.xmin, viewer_border_.xmax - viewer_border_.xmin);
+  int miny = max_ii(area->ymin - viewer_border_.ymin, 0);
+  int maxy = min_ii(area->ymax - viewer_border_.ymin, viewer_border_.ymax - viewer_border_.ymin);
+  int minxchunk = minx / (int)chunk_size_;
+  int maxxchunk = (maxx + (int)chunk_size_ - 1) / (int)chunk_size_;
+  int minychunk = miny / (int)chunk_size_;
+  int maxychunk = (maxy + (int)chunk_size_ - 1) / (int)chunk_size_;
   minxchunk = max_ii(minxchunk, 0);
   minychunk = max_ii(minychunk, 0);
-  maxxchunk = min_ii(maxxchunk, (int)m_x_chunks_len);
-  maxychunk = min_ii(maxychunk, (int)m_y_chunks_len);
+  maxxchunk = min_ii(maxxchunk, (int)x_chunks_len_);
+  maxychunk = min_ii(maxychunk, (int)y_chunks_len_);
 
   bool result = true;
   for (indexx = minxchunk; indexx < maxxchunk; indexx++) {
     for (indexy = minychunk; indexy < maxychunk; indexy++) {
-      if (!scheduleChunkWhenPossible(graph, indexx, indexy)) {
+      if (!schedule_chunk_when_possible(graph, indexx, indexy)) {
         result = false;
       }
     }
@@ -540,9 +520,9 @@ bool ExecutionGroup::scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area
   return result;
 }
 
-bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
+bool ExecutionGroup::schedule_chunk(unsigned int chunk_number)
 {
-  WorkPackage &work_package = m_work_packages[chunkNumber];
+  WorkPackage &work_package = work_packages_[chunk_number];
   if (work_package.state == eWorkPackageState::NotScheduled) {
     work_package.state = eWorkPackageState::Scheduled;
     WorkScheduler::schedule(&work_package);
@@ -551,20 +531,20 @@ bool ExecutionGroup::scheduleChunk(unsigned int chunkNumber)
   return false;
 }
 
-bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
-                                               const int chunk_x,
-                                               const int chunk_y)
+bool ExecutionGroup::schedule_chunk_when_possible(ExecutionSystem *graph,
+                                                  const int chunk_x,
+                                                  const int chunk_y)
 {
-  if (chunk_x < 0 || chunk_x >= (int)this->m_x_chunks_len) {
+  if (chunk_x < 0 || chunk_x >= (int)x_chunks_len_) {
     return true;
   }
-  if (chunk_y < 0 || chunk_y >= (int)this->m_y_chunks_len) {
+  if (chunk_y < 0 || chunk_y >= (int)y_chunks_len_) {
     return true;
   }
 
   /* Check if chunk is already executed or scheduled and not yet executed. */
-  const int chunk_index = chunk_y * this->m_x_chunks_len + chunk_x;
-  WorkPackage &work_package = m_work_packages[chunk_index];
+  const int chunk_index = chunk_y * x_chunks_len_ + chunk_x;
+  WorkPackage &work_package = work_packages_[chunk_index];
   if (work_package.state == eWorkPackageState::Executed) {
     return true;
   }
@@ -575,52 +555,45 @@ bool ExecutionGroup::scheduleChunkWhenPossible(ExecutionSystem *graph,
   bool can_be_executed = true;
   rcti area;
 
-  for (ReadBufferOperation *read_operation : m_read_operations) {
+  for (ReadBufferOperation *read_operation : read_operations_) {
     BLI_rcti_init(&area, 0, 0, 0, 0);
-    MemoryProxy *memory_proxy = read_operation->getMemoryProxy();
-    determineDependingAreaOfInterest(&work_package.rect, read_operation, &area);
-    ExecutionGroup *group = memory_proxy->getExecutor();
+    MemoryProxy *memory_proxy = read_operation->get_memory_proxy();
+    determine_depending_area_of_interest(&work_package.rect, read_operation, &area);
+    ExecutionGroup *group = memory_proxy->get_executor();
 
-    if (!group->scheduleAreaWhenPossible(graph, &area)) {
+    if (!group->schedule_area_when_possible(graph, &area)) {
       can_be_executed = false;
     }
   }
 
   if (can_be_executed) {
-    scheduleChunk(chunk_index);
+    schedule_chunk(chunk_index);
   }
 
   return false;
 }
 
-void ExecutionGroup::determineDependingAreaOfInterest(rcti *input,
-                                                      ReadBufferOperation *readOperation,
-                                                      rcti *output)
+void ExecutionGroup::determine_depending_area_of_interest(rcti *input,
+                                                          ReadBufferOperation *read_operation,
+                                                          rcti *output)
 {
-  this->getOutputOperation()->determineDependingAreaOfInterest(input, readOperation, output);
+  this->get_output_operation()->determine_depending_area_of_interest(
+      input, read_operation, output);
 }
 
-void ExecutionGroup::setViewerBorder(float xmin, float xmax, float ymin, float ymax)
+void ExecutionGroup::set_viewer_border(float xmin, float xmax, float ymin, float ymax)
 {
-  const NodeOperation &operation = *this->getOutputOperation();
+  const NodeOperation &operation = *this->get_output_operation();
   if (operation.get_flags().use_viewer_border) {
-    BLI_rcti_init(&this->m_viewerBorder,
-                  xmin * this->m_width,
-                  xmax * this->m_width,
-                  ymin * this->m_height,
-                  ymax * this->m_height);
+    BLI_rcti_init(&viewer_border_, xmin * width_, xmax * width_, ymin * height_, ymax * height_);
   }
 }
 
-void ExecutionGroup::setRenderBorder(float xmin, float xmax, float ymin, float ymax)
+void ExecutionGroup::set_render_border(float xmin, float xmax, float ymin, float ymax)
 {
-  const NodeOperation &operation = *this->getOutputOperation();
-  if (operation.isOutputOperation(true) && operation.get_flags().use_render_border) {
-    BLI_rcti_init(&this->m_viewerBorder,
-                  xmin * this->m_width,
-                  xmax * this->m_width,
-                  ymin * this->m_height,
-                  ymax * this->m_height);
+  const NodeOperation &operation = *this->get_output_operation();
+  if (operation.is_output_operation(true) && operation.get_flags().use_render_border) {
+    BLI_rcti_init(&viewer_border_, xmin * width_, xmax * width_, ymin * height_, ymax * height_);
   }
 }
 
diff --git a/source/blender/compositor/intern/COM_ExecutionGroup.h b/source/blender/compositor/intern/COM_ExecutionGroup.h
index cb593feabb0..ce5e9dd4699 100644
--- a/source/blender/compositor/intern/COM_ExecutionGroup.h
+++ b/source/blender/compositor/intern/COM_ExecutionGroup.h
@@ -22,25 +22,24 @@
 #  include "MEM_guardedalloc.h"
 #endif
 
+#include 
+
 #include "BLI_array.hh"
-#include "BLI_rect.h"
 #include "BLI_vector.hh"
 
-#include "COM_CompositorContext.h"
-#include "COM_Device.h"
-#include "COM_MemoryProxy.h"
-#include "COM_Node.h"
-#include "COM_NodeOperation.h"
+#include "COM_Enums.h"
 #include "COM_WorkPackage.h"
-#include 
+
+#include "DNA_node_types.h"
+#include "DNA_vec_types.h"
 
 namespace blender::compositor {
 
 class ExecutionSystem;
+class NodeOperation;
 class MemoryProxy;
 class MemoryBuffer;
 class ReadBufferOperation;
-class Device;
 
 struct ExecutionGroupFlags {
   bool initialized : 1;
@@ -87,84 +86,84 @@ class ExecutionGroup {
   /**
    * Id of the execution group. For debugging purposes.
    */
-  int m_id;
+  int id_;
 
   /**
    * \brief list of operations in this ExecutionGroup
    */
-  Vector m_operations;
+  Vector operations_;
 
-  ExecutionGroupFlags m_flags;
+  ExecutionGroupFlags flags_;
 
   /**
    * \brief Width of the output
    */
-  unsigned int m_width;
+  unsigned int width_;
 
   /**
    * \brief Height of the output
    */
-  unsigned int m_height;
+  unsigned int height_;
 
   /**
    * \brief size of a single chunk, being Width or of height
    * a chunk is always a square, except at the edges of the MemoryBuffer
    */
-  unsigned int m_chunkSize;
+  unsigned int chunk_size_;
 
   /**
    * \brief number of chunks in the x-axis
    */
-  unsigned int m_x_chunks_len;
+  unsigned int x_chunks_len_;
 
   /**
    * \brief number of chunks in the y-axis
    */
-  unsigned int m_y_chunks_len;
+  unsigned int y_chunks_len_;
 
   /**
    * \brief total number of chunks
    */
-  unsigned int m_chunks_len;
+  unsigned int chunks_len_;
 
   /**
    * \brief what is the maximum number field of all ReadBufferOperation in this ExecutionGroup.
    * \note this is used to construct the MemoryBuffers that will be passed during execution.
    */
-  unsigned int m_max_read_buffer_offset;
+  unsigned int max_read_buffer_offset_;
 
   /**
    * \brief All read operations of this execution group.
    */
-  Vector m_read_operations;
+  Vector read_operations_;
 
   /**
    * \brief reference to the original bNodeTree,
    * this field is only set for the 'top' execution group.
    * \note can only be used to call the callbacks for progress, status and break.
    */
-  const bNodeTree *m_bTree;
+  const bNodeTree *bTree_;
 
   /**
    * \brief total number of chunks that have been calculated for this ExecutionGroup
    */
-  unsigned int m_chunks_finished;
+  unsigned int chunks_finished_;
 
   /**
-   * \brief m_work_packages holds all unit of work.
+   * \brief work_packages_ holds all unit of work.
    */
-  Vector m_work_packages;
+  Vector work_packages_;
 
   /**
    * \brief denotes boundary for border compositing
    * \note measured in pixel space
    */
-  rcti m_viewerBorder;
+  rcti viewer_border_;
 
   /**
    * \brief start time of execution
    */
-  double m_executionStartTime;
+  double execution_start_time_;
 
   // methods
   /**
@@ -176,13 +175,14 @@ class ExecutionGroup {
   /**
    * \brief Determine the rect (minx, maxx, miny, maxy) of a chunk at a position.
    */
-  void determineChunkRect(rcti *r_rect,
-                          const unsigned int xChunk,
-                          const unsigned int yChunk) const;
+  void determine_chunk_rect(rcti *r_rect,
+                            const unsigned int x_chunk,
+                            const unsigned int y_chunk) const;
 
   /**
-   * \brief determine the number of chunks, based on the chunkSize, width and height.
-   * \note The result are stored in the fields numberOfChunks, numberOfXChunks, numberOfYChunks
+   * \brief determine the number of chunks, based on the chunk_size, width and height.
+   * \note The result are stored in the fields number_of_chunks, number_of_xchunks,
+   * number_of_ychunks
    */
   void init_number_of_chunks();
 
@@ -191,13 +191,13 @@ class ExecutionGroup {
    * \note scheduling succeeds when all input requirements are met and the chunks hasn't been
    * scheduled yet.
    * \param graph:
-   * \param xChunk:
-   * \param yChunk:
+   * \param x_chunk:
+   * \param y_chunk:
    * \return [true:false]
    * true: package(s) are scheduled
    * false: scheduling is deferred (depending workpackages are scheduled)
    */
-  bool scheduleChunkWhenPossible(ExecutionSystem *graph, const int chunk_x, const int chunk_y);
+  bool schedule_chunk_when_possible(ExecutionSystem *graph, const int chunk_x, const int chunk_y);
 
   /**
    * \brief try to schedule a specific area.
@@ -209,24 +209,24 @@ class ExecutionGroup {
    * true: package(s) are scheduled
    * false: scheduling is deferred (depending workpackages are scheduled)
    */
-  bool scheduleAreaWhenPossible(ExecutionSystem *graph, rcti *area);
+  bool schedule_area_when_possible(ExecutionSystem *graph, rcti *area);
 
   /**
    * \brief add a chunk to the WorkScheduler.
    * \param chunknumber:
    */
-  bool scheduleChunk(unsigned int chunkNumber);
+  bool schedule_chunk(unsigned int chunk_number);
 
   /**
    * \brief determine the area of interest of a certain input area
    * \note This method only evaluates a single ReadBufferOperation
    * \param input: the input area
-   * \param readOperation: The ReadBufferOperation where the area needs to be evaluated
+   * \param read_operation: The ReadBufferOperation where the area needs to be evaluated
    * \param output: the area needed of the ReadBufferOperation. Result
    */
-  void determineDependingAreaOfInterest(rcti *input,
-                                        ReadBufferOperation *readOperation,
-                                        rcti *output);
+  void determine_depending_area_of_interest(rcti *input,
+                                            ReadBufferOperation *read_operation,
+                                            rcti *output);
 
   /**
    * Return the execution order of the user visible chunks.
@@ -242,12 +242,12 @@ class ExecutionGroup {
 
   int get_id() const
   {
-    return m_id;
+    return id_;
   }
 
   const ExecutionGroupFlags get_flags() const
   {
-    return m_flags;
+    return flags_;
   }
 
   // methods
@@ -259,103 +259,95 @@ class ExecutionGroup {
    * \param operation:
    * \return True if the operation was successfully added
    */
-  bool addOperation(NodeOperation *operation);
+  bool add_operation(NodeOperation *operation);
 
   /**
    * \brief set whether this ExecutionGroup is an output
-   * \param isOutput:
+   * \param is_output:
    */
-  void setOutputExecutionGroup(bool is_output)
+  void set_output_execution_group(bool is_output)
   {
-    this->m_flags.is_output = is_output;
+    flags_.is_output = is_output;
   }
 
   /**
    * \brief determine the resolution of this ExecutionGroup
    * \param resolution:
    */
-  void determineResolution(unsigned int resolution[2]);
+  void determine_resolution(unsigned int resolution[2]);
 
   /**
    * \brief set the resolution of this executiongroup
    * \param resolution:
    */
-  void setResolution(unsigned int resolution[2])
+  void set_resolution(unsigned int resolution[2])
   {
-    this->m_width = resolution[0];
-    this->m_height = resolution[1];
+    width_ = resolution[0];
+    height_ = resolution[1];
   }
 
   /**
    * \brief get the width of this execution group
    */
-  unsigned int getWidth() const
+  unsigned int get_width() const
   {
-    return m_width;
+    return width_;
   }
 
   /**
    * \brief get the height of this execution group
    */
-  unsigned int getHeight() const
+  unsigned int get_height() const
   {
-    return m_height;
+    return height_;
   }
 
   /**
    * \brief get the output operation of this ExecutionGroup
    * \return NodeOperation *output operation
    */
-  NodeOperation *getOutputOperation() const;
+  NodeOperation *get_output_operation() const;
 
   /**
    * \brief compose multiple chunks into a single chunk
    * \return Memorybuffer *consolidated chunk
    */
-  MemoryBuffer *constructConsolidatedMemoryBuffer(MemoryProxy &memoryProxy, rcti &rect);
+  MemoryBuffer *construct_consolidated_memory_buffer(MemoryProxy &memory_proxy, rcti &rect);
 
   /**
-   * \brief initExecution is called just before the execution of the whole graph will be done.
-   * \note The implementation will calculate the chunkSize of this execution group.
+   * \brief init_execution is called just before the execution of the whole graph will be done.
+   * \note The implementation will calculate the chunk_size of this execution group.
    */
-  void initExecution();
+  void init_execution();
 
   /**
    * \brief get all inputbuffers needed to calculate an chunk
    * \note all inputbuffers must be executed
-   * \param chunkNumber: the chunk to be calculated
+   * \param chunk_number: the chunk to be calculated
    * \return (MemoryBuffer **) the inputbuffers
    */
-  MemoryBuffer **getInputBuffersCPU();
-
-  /**
-   * \brief get all inputbuffers needed to calculate an chunk
-   * \note all inputbuffers must be executed
-   * \param chunkNumber: the chunk to be calculated
-   * \return (MemoryBuffer **) the inputbuffers
-   */
-  MemoryBuffer **getInputBuffersOpenCL(int chunkNumber);
+  MemoryBuffer **get_input_buffers_opencl(int chunk_number);
 
   /**
    * \brief allocate the outputbuffer of a chunk
-   * \param chunkNumber: the number of the chunk in the ExecutionGroup
+   * \param chunk_number: the number of the chunk in the ExecutionGroup
    * \param rect: the rect of that chunk
-   * \see determineChunkRect
+   * \see determine_chunk_rect
    */
-  MemoryBuffer *allocateOutputBuffer(rcti &rect);
+  MemoryBuffer *allocate_output_buffer(rcti &rect);
 
   /**
    * \brief after a chunk is executed the needed resources can be freed or unlocked.
    * \param chunknumber:
    * \param memorybuffers:
    */
-  void finalizeChunkExecution(int chunkNumber, MemoryBuffer **memoryBuffers);
+  void finalize_chunk_execution(int chunk_number, MemoryBuffer **memory_buffers);
 
   /**
-   * \brief deinitExecution is called just after execution the whole graph.
+   * \brief deinit_execution is called just after execution the whole graph.
    * \note It will release all needed resources
    */
-  void deinitExecution();
+  void deinit_execution();
 
   /**
    * \brief schedule an ExecutionGroup
@@ -378,26 +370,26 @@ class ExecutionGroup {
   /**
    * \brief Determine the rect (minx, maxx, miny, maxy) of a chunk.
    */
-  void determineChunkRect(rcti *r_rect, const unsigned int chunkNumber) const;
+  void determine_chunk_rect(rcti *r_rect, const unsigned int chunk_number) const;
 
-  void setChunksize(int chunksize)
+  void set_chunksize(int chunksize)
   {
-    this->m_chunkSize = chunksize;
+    chunk_size_ = chunksize;
   }
 
   /**
    * \brief get the Render priority of this ExecutionGroup
    * \see ExecutionSystem.execute
    */
-  eCompositorPriority getRenderPriority();
+  eCompositorPriority get_render_priority();
 
   /**
    * \brief set border for viewer operation
    * \note all the coordinates are assumed to be in normalized space
    */
-  void setViewerBorder(float xmin, float xmax, float ymin, float ymax);
+  void set_viewer_border(float xmin, float xmax, float ymin, float ymax);
 
-  void setRenderBorder(float xmin, float xmax, float ymin, float ymax);
+  void set_render_border(float xmin, float xmax, float ymin, float ymax);
 
   /* allow the DebugInfo class to look at internals */
   friend class DebugInfo;
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.cc b/source/blender/compositor/intern/COM_ExecutionModel.cc
index b75b277e92c..b319aaa4b21 100644
--- a/source/blender/compositor/intern/COM_ExecutionModel.cc
+++ b/source/blender/compositor/intern/COM_ExecutionModel.cc
@@ -17,13 +17,14 @@
  */
 
 #include "COM_ExecutionModel.h"
+#include "COM_CompositorContext.h"
 
 namespace blender::compositor {
 
 ExecutionModel::ExecutionModel(CompositorContext &context, Span operations)
     : context_(context), operations_(operations)
 {
-  const bNodeTree *node_tree = context_.getbNodeTree();
+  const bNodeTree *node_tree = context_.get_bnodetree();
 
   const rctf *viewer_border = &node_tree->viewer_border;
   border_.use_viewer_border = (node_tree->flag & NTREE_VIEWER_BORDER) &&
@@ -31,10 +32,10 @@ ExecutionModel::ExecutionModel(CompositorContext &context, Span
                               viewer_border->ymin < viewer_border->ymax;
   border_.viewer_border = viewer_border;
 
-  const RenderData *rd = context_.getRenderData();
+  const RenderData *rd = context_.get_render_data();
   /* Case when cropping to render border happens is handled in
    * compositor output and render layer nodes. */
-  border_.use_render_border = context.isRendering() && (rd->mode & R_BORDER) &&
+  border_.use_render_border = context.is_rendering() && (rd->mode & R_BORDER) &&
                               !(rd->mode & R_CROP);
   border_.render_border = &rd->border;
 }
diff --git a/source/blender/compositor/intern/COM_ExecutionModel.h b/source/blender/compositor/intern/COM_ExecutionModel.h
index 452861ae4be..f0adc7d3e54 100644
--- a/source/blender/compositor/intern/COM_ExecutionModel.h
+++ b/source/blender/compositor/intern/COM_ExecutionModel.h
@@ -18,12 +18,9 @@
 
 #pragma once
 
-#include "BLI_rect.h"
-#include "BLI_vector.hh"
+#include "BLI_span.hh"
 
-#include "COM_ExecutionSystem.h"
-
-#include 
+#include "DNA_vec_types.h"
 
 #ifdef WITH_CXX_GUARDEDALLOC
 #  include "MEM_guardedalloc.h"
@@ -31,6 +28,8 @@
 
 namespace blender::compositor {
 
+class CompositorContext;
+class ExecutionSystem;
 class NodeOperation;
 
 /**
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cc b/source/blender/compositor/intern/COM_ExecutionSystem.cc
index c92e292c74f..82821a1ddb1 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cc
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cc
@@ -18,14 +18,13 @@
 
 #include "COM_ExecutionSystem.h"
 
-#include "BLI_utildefines.h"
-#include "PIL_time.h"
-
 #include "COM_Debug.h"
+#include "COM_ExecutionGroup.h"
 #include "COM_FullFrameExecutionModel.h"
 #include "COM_NodeOperation.h"
 #include "COM_NodeOperationBuilder.h"
 #include "COM_TiledExecutionModel.h"
+#include "COM_WorkPackage.h"
 #include "COM_WorkScheduler.h"
 
 #ifdef WITH_CXX_GUARDEDALLOC
@@ -39,45 +38,45 @@ ExecutionSystem::ExecutionSystem(RenderData *rd,
                                  bNodeTree *editingtree,
                                  bool rendering,
                                  bool fastcalculation,
-                                 const ColorManagedViewSettings *viewSettings,
-                                 const ColorManagedDisplaySettings *displaySettings,
-                                 const char *viewName)
+                                 const ColorManagedViewSettings *view_settings,
+                                 const ColorManagedDisplaySettings *display_settings,
+                                 const char *view_name)
 {
   num_work_threads_ = WorkScheduler::get_num_cpu_threads();
-  this->m_context.setViewName(viewName);
-  this->m_context.setScene(scene);
-  this->m_context.setbNodeTree(editingtree);
-  this->m_context.setPreviewHash(editingtree->previews);
-  this->m_context.setFastCalculation(fastcalculation);
+  context_.set_view_name(view_name);
+  context_.set_scene(scene);
+  context_.set_bnodetree(editingtree);
+  context_.set_preview_hash(editingtree->previews);
+  context_.set_fast_calculation(fastcalculation);
   /* initialize the CompositorContext */
   if (rendering) {
-    this->m_context.setQuality((eCompositorQuality)editingtree->render_quality);
+    context_.set_quality((eCompositorQuality)editingtree->render_quality);
   }
   else {
-    this->m_context.setQuality((eCompositorQuality)editingtree->edit_quality);
+    context_.set_quality((eCompositorQuality)editingtree->edit_quality);
   }
-  this->m_context.setRendering(rendering);
-  this->m_context.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() &&
-                                            (editingtree->flag & NTREE_COM_OPENCL));
+  context_.set_rendering(rendering);
+  context_.setHasActiveOpenCLDevices(WorkScheduler::has_gpu_devices() &&
+                                     (editingtree->flag & NTREE_COM_OPENCL));
 
-  this->m_context.setRenderData(rd);
-  this->m_context.setViewSettings(viewSettings);
-  this->m_context.setDisplaySettings(displaySettings);
+  context_.set_render_data(rd);
+  context_.set_view_settings(view_settings);
+  context_.set_display_settings(display_settings);
 
   BLI_mutex_init(&work_mutex_);
   BLI_condition_init(&work_finished_cond_);
 
   {
-    NodeOperationBuilder builder(&m_context, editingtree, this);
-    builder.convertToOperations(this);
+    NodeOperationBuilder builder(&context_, editingtree, this);
+    builder.convert_to_operations(this);
   }
 
-  switch (m_context.get_execution_model()) {
+  switch (context_.get_execution_model()) {
     case eExecutionModel::Tiled:
-      execution_model_ = new TiledExecutionModel(m_context, m_operations, m_groups);
+      execution_model_ = new TiledExecutionModel(context_, operations_, groups_);
       break;
     case eExecutionModel::FullFrame:
-      execution_model_ = new FullFrameExecutionModel(m_context, active_buffers_, m_operations);
+      execution_model_ = new FullFrameExecutionModel(context_, active_buffers_, operations_);
       break;
     default:
       BLI_assert_msg(0, "Non implemented execution model");
@@ -92,28 +91,28 @@ ExecutionSystem::~ExecutionSystem()
 
   delete execution_model_;
 
-  for (NodeOperation *operation : m_operations) {
+  for (NodeOperation *operation : operations_) {
     delete operation;
   }
-  this->m_operations.clear();
+  operations_.clear();
 
-  for (ExecutionGroup *group : m_groups) {
+  for (ExecutionGroup *group : groups_) {
     delete group;
   }
-  this->m_groups.clear();
+  groups_.clear();
 }
 
 void ExecutionSystem::set_operations(const Vector &operations,
                                      const Vector &groups)
 {
-  m_operations = operations;
-  m_groups = groups;
+  operations_ = operations;
+  groups_ = groups;
 }
 
 void ExecutionSystem::execute()
 {
   DebugInfo::execute_started(this);
-  for (NodeOperation *op : m_operations) {
+  for (NodeOperation *op : operations_) {
     op->init_data();
   }
   execution_model_->execute(*this);
@@ -185,7 +184,7 @@ void ExecutionSystem::execute_work(const rcti &work_rect,
 
 bool ExecutionSystem::is_breaked() const
 {
-  const bNodeTree *btree = m_context.getbNodeTree();
+  const bNodeTree *btree = context_.get_bnodetree();
   return btree->test_break(btree->tbh);
 }
 
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h
index bce96db52c7..4a3fecaca47 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -16,22 +16,23 @@
  * Copyright 2011, Blender Foundation.
  */
 
-class ExecutionGroup;
-
 #pragma once
 
-#include "BKE_text.h"
+#include 
 
-#include "COM_ExecutionGroup.h"
-#include "COM_Node.h"
-#include "COM_NodeOperation.h"
+#include "atomic_ops.h"
+
+#include "BLI_index_range.hh"
+#include "BLI_threads.h"
+#include "BLI_vector.hh"
+
+#include "COM_CompositorContext.h"
 #include "COM_SharedOperationBuffers.h"
 
 #include "DNA_color_types.h"
 #include "DNA_node_types.h"
-
-#include "BLI_vector.hh"
-#include "atomic_ops.h"
+#include "DNA_scene_types.h"
+#include "DNA_vec_types.h"
 
 namespace blender::compositor {
 
@@ -63,8 +64,8 @@ namespace blender::compositor {
  * based on settings; like MixNode. based on the selected Mixtype a different operation will be
  * used. for more information see the page about creating new Nodes. [@subpage newnode]
  *
- * \see ExecutionSystem.convertToOperations
- * \see Node.convertToOperations
+ * \see ExecutionSystem.convert_to_operations
+ * \see Node.convert_to_operations
  * \see NodeOperation base class for all operations in the system
  *
  * \section EM_Step3 Step3: add additional conversions to the operation system
@@ -88,7 +89,7 @@ namespace blender::compositor {
  *       Bottom left of the images are aligned.
  *
  * \see COM_convert_data_type Datatype conversions
- * \see Converter.convertResolution Image size conversions
+ * \see Converter.convert_resolution Image size conversions
  *
  * \section EM_Step4 Step4: group operations in executions groups
  * ExecutionGroup are groups of operations that are calculated as being one bigger operation.
@@ -111,14 +112,16 @@ namespace blender::compositor {
  * |cFAA  |           |cFAA  |         |cFAA   |           |cFAA   |
  * +------+           +------+         +-------+           +-------+
  * 
- * \see ExecutionSystem.groupOperations method doing this step - * \see ExecutionSystem.addReadWriteBufferOperations - * \see NodeOperation.isComplex + * \see ExecutionSystem.group_operations method doing this step + * \see ExecutionSystem.add_read_write_buffer_operations + * \see NodeOperation.is_complex * \see ExecutionGroup class representing the ExecutionGroup */ /* Forward declarations. */ +class ExecutionGroup; class ExecutionModel; +class NodeOperation; /** * \brief the ExecutionSystem contains the whole compositor tree. @@ -134,17 +137,17 @@ class ExecutionSystem { /** * \brief the context used during execution */ - CompositorContext m_context; + CompositorContext context_; /** * \brief vector of operations */ - Vector m_operations; + Vector operations_; /** * \brief vector of groups */ - Vector m_groups; + Vector groups_; /** * Active execution model implementation. @@ -172,9 +175,9 @@ class ExecutionSystem { bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name); /** * Destructor @@ -195,9 +198,9 @@ class ExecutionSystem { /** * \brief get the reference to the compositor context */ - const CompositorContext &getContext() const + const CompositorContext &get_context() const { - return this->m_context; + return context_; } SharedOperationBuffers &get_active_buffers() diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc index bd3a481d691..1295437785f 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.cc @@ -17,14 +17,13 @@ */ #include "COM_FullFrameExecutionModel.h" -#include "COM_Debug.h" -#include "COM_ExecutionGroup.h" -#include "COM_ReadBufferOperation.h" -#include "COM_ViewerOperation.h" -#include "COM_WorkScheduler.h" #include "BLT_translation.h" +#include "COM_Debug.h" +#include "COM_ViewerOperation.h" +#include "COM_WorkScheduler.h" + #ifdef WITH_CXX_GUARDEDALLOC # include "MEM_guardedalloc.h" #endif @@ -39,7 +38,7 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, num_operations_finished_(0) { priorities_.append(eCompositorPriority::High); - if (!context.isFastCalculation()) { + if (!context.is_fast_calculation()) { priorities_.append(eCompositorPriority::Medium); priorities_.append(eCompositorPriority::Low); } @@ -47,7 +46,7 @@ FullFrameExecutionModel::FullFrameExecutionModel(CompositorContext &context, void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) { - const bNodeTree *node_tree = this->context_.getbNodeTree(); + const bNodeTree *node_tree = this->context_.get_bnodetree(); node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Initializing execution")); DebugInfo::graphviz(&exec_system, "compositor_prior_rendering"); @@ -58,14 +57,14 @@ void FullFrameExecutionModel::execute(ExecutionSystem &exec_system) void FullFrameExecutionModel::determine_areas_to_render_and_reads() { - const bool is_rendering = context_.isRendering(); - const bNodeTree *node_tree = context_.getbNodeTree(); + const bool is_rendering = context_.is_rendering(); + const bNodeTree *node_tree = context_.get_bnodetree(); rcti area; for (eCompositorPriority priority : priorities_) { for (NodeOperation *op : operations_) { - op->setbNodeTree(node_tree); - if (op->isOutputOperation(is_rendering) && op->getRenderPriority() == priority) { + op->set_bnodetree(node_tree); + if (op->is_output_operation(is_rendering) && op->get_render_priority() == priority) { get_output_render_area(op, area); determine_areas_to_render(op, area); determine_reads(op); @@ -74,37 +73,62 @@ void FullFrameExecutionModel::determine_areas_to_render_and_reads() } } -Vector FullFrameExecutionModel::get_input_buffers(NodeOperation *op) +/** + * Returns input buffers with an offset relative to given output coordinates. Returned memory + * buffers must be deleted. + */ +Vector FullFrameExecutionModel::get_input_buffers(NodeOperation *op, + const int output_x, + const int output_y) { - const int num_inputs = op->getNumberOfInputSockets(); + const int num_inputs = op->get_number_of_input_sockets(); Vector inputs_buffers(num_inputs); for (int i = 0; i < num_inputs; i++) { - NodeOperation *input_op = op->get_input_operation(i); - inputs_buffers[i] = active_buffers_.get_rendered_buffer(input_op); + NodeOperation *input = op->get_input_operation(i); + const int offset_x = (input->get_canvas().xmin - op->get_canvas().xmin) + output_x; + const int offset_y = (input->get_canvas().ymin - op->get_canvas().ymin) + output_y; + MemoryBuffer *buf = active_buffers_.get_rendered_buffer(input); + + rcti rect = buf->get_rect(); + BLI_rcti_translate(&rect, offset_x, offset_y); + inputs_buffers[i] = new MemoryBuffer( + buf->get_buffer(), buf->get_num_channels(), rect, buf->is_a_single_elem()); } return inputs_buffers; } -MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op) +MemoryBuffer *FullFrameExecutionModel::create_operation_buffer(NodeOperation *op, + const int output_x, + const int output_y) { - rcti op_rect; - BLI_rcti_init(&op_rect, 0, op->getWidth(), 0, op->getHeight()); + rcti rect; + BLI_rcti_init( + &rect, output_x, output_x + op->get_width(), output_y, output_y + op->get_height()); - const DataType data_type = op->getOutputSocket(0)->getDataType(); + const DataType data_type = op->get_output_socket(0)->get_data_type(); const bool is_a_single_elem = op->get_flags().is_constant_operation; - return new MemoryBuffer(data_type, op_rect, is_a_single_elem); + return new MemoryBuffer(data_type, rect, is_a_single_elem); } void FullFrameExecutionModel::render_operation(NodeOperation *op) { - Vector input_bufs = get_input_buffers(op); + /* Output has no offset for easier image algorithms implementation on operations. */ + constexpr int output_x = 0; + constexpr int output_y = 0; - const bool has_outputs = op->getNumberOfOutputSockets() > 0; - MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op) : nullptr; - if (op->getWidth() > 0 && op->getHeight() > 0) { - Span areas = active_buffers_.get_areas_to_render(op); + const bool has_outputs = op->get_number_of_output_sockets() > 0; + MemoryBuffer *op_buf = has_outputs ? create_operation_buffer(op, output_x, output_y) : nullptr; + if (op->get_width() > 0 && op->get_height() > 0) { + Vector input_bufs = get_input_buffers(op, output_x, output_y); + const int op_offset_x = output_x - op->get_canvas().xmin; + const int op_offset_y = output_y - op->get_canvas().ymin; + Vector areas = active_buffers_.get_areas_to_render(op, op_offset_x, op_offset_y); op->render(op_buf, areas, input_bufs); DebugInfo::operation_rendered(op, op_buf); + + for (MemoryBuffer *buf : input_bufs) { + delete buf; + } } /* Even if operation has no resolution set the empty buffer. It will be clipped with a * TranslateOperation from convert resolutions if linked to an operation with resolution. */ @@ -118,19 +142,19 @@ void FullFrameExecutionModel::render_operation(NodeOperation *op) */ void FullFrameExecutionModel::render_operations() { - const bool is_rendering = context_.isRendering(); + const bool is_rendering = context_.is_rendering(); WorkScheduler::start(this->context_); for (eCompositorPriority priority : priorities_) { for (NodeOperation *op : operations_) { - const bool has_size = op->getWidth() > 0 && op->getHeight() > 0; - const bool is_priority_output = op->isOutputOperation(is_rendering) && - op->getRenderPriority() == priority; + const bool has_size = op->get_width() > 0 && op->get_height() > 0; + const bool is_priority_output = op->is_output_operation(is_rendering) && + op->get_render_priority() == priority; if (is_priority_output && has_size) { render_output_dependencies(op); render_operation(op); } - else if (is_priority_output && !has_size && op->isActiveViewerOutput()) { + else if (is_priority_output && !has_size && op->is_active_viewer_output()) { static_cast(op)->clear_display_buffer(); } } @@ -152,7 +176,7 @@ static Vector get_operation_dependencies(NodeOperation *operati Vector outputs(next_outputs); next_outputs.clear(); for (NodeOperation *output : outputs) { - for (int i = 0; i < output->getNumberOfInputSockets(); i++) { + for (int i = 0; i < output->get_number_of_input_sockets(); i++) { next_outputs.append(output->get_input_operation(i)); } } @@ -167,7 +191,7 @@ static Vector get_operation_dependencies(NodeOperation *operati void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_op) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); Vector dependencies = get_operation_dependencies(output_op); for (NodeOperation *op : dependencies) { if (!active_buffers_.is_operation_rendered(op)) { @@ -182,7 +206,7 @@ void FullFrameExecutionModel::render_output_dependencies(NodeOperation *output_o void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op, const rcti &output_area) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); Vector> stack; stack.append({output_op, output_area}); @@ -190,21 +214,21 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op std::pair pair = stack.pop_last(); NodeOperation *operation = pair.first; const rcti &render_area = pair.second; - if (active_buffers_.is_area_registered(operation, render_area)) { + if (BLI_rcti_is_empty(&render_area) || + active_buffers_.is_area_registered(operation, render_area)) { continue; } active_buffers_.register_area(operation, render_area); - const int num_inputs = operation->getNumberOfInputSockets(); + const int num_inputs = operation->get_number_of_input_sockets(); for (int i = 0; i < num_inputs; i++) { NodeOperation *input_op = operation->get_input_operation(i); - rcti input_op_rect, input_area; - BLI_rcti_init(&input_op_rect, 0, input_op->getWidth(), 0, input_op->getHeight()); + rcti input_area; operation->get_area_of_interest(input_op, render_area, input_area); /* Ensure area of interest is within operation bounds, cropping areas outside. */ - BLI_rcti_isect(&input_area, &input_op_rect, &input_area); + BLI_rcti_isect(&input_area, &input_op->get_canvas(), &input_area); stack.append({input_op, input_area}); } @@ -217,13 +241,13 @@ void FullFrameExecutionModel::determine_areas_to_render(NodeOperation *output_op */ void FullFrameExecutionModel::determine_reads(NodeOperation *output_op) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); Vector stack; stack.append(output_op); while (stack.size() > 0) { NodeOperation *operation = stack.pop_last(); - const int num_inputs = operation->getNumberOfInputSockets(); + const int num_inputs = operation->get_number_of_input_sockets(); for (int i = 0; i < num_inputs; i++) { NodeOperation *input_op = operation->get_input_operation(i); if (!active_buffers_.has_registered_reads(input_op)) { @@ -240,12 +264,11 @@ void FullFrameExecutionModel::determine_reads(NodeOperation *output_op) */ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, rcti &r_area) { - BLI_assert(output_op->isOutputOperation(context_.isRendering())); + BLI_assert(output_op->is_output_operation(context_.is_rendering())); /* By default return operation bounds (no border). */ - const int op_width = output_op->getWidth(); - const int op_height = output_op->getHeight(); - BLI_rcti_init(&r_area, 0, op_width, 0, op_height); + rcti canvas = output_op->get_canvas(); + r_area = canvas; const bool has_viewer_border = border_.use_viewer_border && (output_op->get_flags().is_viewer_operation || @@ -255,19 +278,20 @@ void FullFrameExecutionModel::get_output_render_area(NodeOperation *output_op, r /* Get border with normalized coordinates. */ const rctf *norm_border = has_viewer_border ? border_.viewer_border : border_.render_border; - /* Return de-normalized border. */ - BLI_rcti_init(&r_area, - norm_border->xmin * op_width, - norm_border->xmax * op_width, - norm_border->ymin * op_height, - norm_border->ymax * op_height); + /* Return de-normalized border within canvas. */ + const int w = output_op->get_width(); + const int h = output_op->get_height(); + r_area.xmin = canvas.xmin + norm_border->xmin * w; + r_area.xmax = canvas.xmin + norm_border->xmax * w; + r_area.ymin = canvas.ymin + norm_border->ymin * h; + r_area.ymax = canvas.ymin + norm_border->ymax * h; } } void FullFrameExecutionModel::operation_finished(NodeOperation *operation) { /* Report inputs reads so that buffers may be freed/reused. */ - const int num_inputs = operation->getNumberOfInputSockets(); + const int num_inputs = operation->get_number_of_input_sockets(); for (int i = 0; i < num_inputs; i++) { active_buffers_.read_finished(operation->get_input_operation(i)); } @@ -278,7 +302,7 @@ void FullFrameExecutionModel::operation_finished(NodeOperation *operation) void FullFrameExecutionModel::update_progress_bar() { - const bNodeTree *tree = context_.getbNodeTree(); + const bNodeTree *tree = context_.get_bnodetree(); if (tree) { const float progress = num_operations_finished_ / static_cast(operations_.size()); tree->progress(tree->prh, progress); diff --git a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h index f75d4f1afdc..36d42886c27 100644 --- a/source/blender/compositor/intern/COM_FullFrameExecutionModel.h +++ b/source/blender/compositor/intern/COM_FullFrameExecutionModel.h @@ -18,6 +18,9 @@ #pragma once +#include "BLI_vector.hh" + +#include "COM_Enums.h" #include "COM_ExecutionModel.h" #ifdef WITH_CXX_GUARDEDALLOC @@ -27,7 +30,11 @@ namespace blender::compositor { /* Forward declarations. */ -class ExecutionGroup; +class CompositorContext; +class ExecutionSystem; +class MemoryBuffer; +class NodeOperation; +class SharedOperationBuffers; /** * Fully renders operations in order from inputs to outputs. @@ -61,8 +68,10 @@ class FullFrameExecutionModel : public ExecutionModel { void determine_areas_to_render_and_reads(); void render_operations(); void render_output_dependencies(NodeOperation *output_op); - Vector get_input_buffers(NodeOperation *op); - MemoryBuffer *create_operation_buffer(NodeOperation *op); + Vector get_input_buffers(NodeOperation *op, + const int output_x, + const int output_y); + MemoryBuffer *create_operation_buffer(NodeOperation *op, const int output_x, const int output_y); void render_operation(NodeOperation *op); void operation_finished(NodeOperation *operation); diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.cc b/source/blender/compositor/intern/COM_MemoryBuffer.cc index 1fbf502fea6..96888ea1c96 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.cc +++ b/source/blender/compositor/intern/COM_MemoryBuffer.cc @@ -18,9 +18,10 @@ #include "COM_MemoryBuffer.h" +#include "COM_MemoryProxy.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf_types.h" -#include "MEM_guardedalloc.h" #define ASSERT_BUFFER_CONTAINS_AREA(buf, area) \ BLI_assert(BLI_rcti_inside_rcti(&(buf)->get_rect(), &(area))) @@ -32,7 +33,7 @@ BLI_assert((buf)->get_rect().ymax >= (y) + BLI_rcti_size_y(&(area))) #define ASSERT_VALID_ELEM_SIZE(buf, channel_offset, elem_size) \ - BLI_assert((buf)->get_num_channels() <= (channel_offset) + (elem_size)) + BLI_assert((buf)->get_num_channels() >= (channel_offset) + (elem_size)) namespace blender::compositor { @@ -43,32 +44,32 @@ static rcti create_rect(const int width, const int height) return rect; } -MemoryBuffer::MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state) +MemoryBuffer::MemoryBuffer(MemoryProxy *memory_proxy, const rcti &rect, MemoryBufferState state) { - m_rect = rect; - this->m_is_a_single_elem = false; - this->m_memoryProxy = memoryProxy; - this->m_num_channels = COM_data_type_num_channels(memoryProxy->getDataType()); - this->m_buffer = (float *)MEM_mallocN_aligned( - sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); + rect_ = rect; + is_a_single_elem_ = false; + memory_proxy_ = memory_proxy; + num_channels_ = COM_data_type_num_channels(memory_proxy->get_data_type()); + buffer_ = (float *)MEM_mallocN_aligned( + sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer"); owns_data_ = true; - this->m_state = state; - this->m_datatype = memoryProxy->getDataType(); + state_ = state; + datatype_ = memory_proxy->get_data_type(); set_strides(); } -MemoryBuffer::MemoryBuffer(DataType dataType, const rcti &rect, bool is_a_single_elem) +MemoryBuffer::MemoryBuffer(DataType data_type, const rcti &rect, bool is_a_single_elem) { - m_rect = rect; - this->m_is_a_single_elem = is_a_single_elem; - this->m_memoryProxy = nullptr; - this->m_num_channels = COM_data_type_num_channels(dataType); - this->m_buffer = (float *)MEM_mallocN_aligned( - sizeof(float) * buffer_len() * this->m_num_channels, 16, "COM_MemoryBuffer"); + rect_ = rect; + is_a_single_elem_ = is_a_single_elem; + memory_proxy_ = nullptr; + num_channels_ = COM_data_type_num_channels(data_type); + buffer_ = (float *)MEM_mallocN_aligned( + sizeof(float) * buffer_len() * num_channels_, 16, "COM_MemoryBuffer"); owns_data_ = true; - this->m_state = MemoryBufferState::Temporary; - this->m_datatype = dataType; + state_ = MemoryBufferState::Temporary; + datatype_ = data_type; set_strides(); } @@ -92,53 +93,54 @@ MemoryBuffer::MemoryBuffer(float *buffer, const rcti &rect, const bool is_a_single_elem) { - m_rect = rect; - m_is_a_single_elem = is_a_single_elem; - m_memoryProxy = nullptr; - m_num_channels = num_channels; - m_datatype = COM_num_channels_data_type(num_channels); - m_buffer = buffer; + rect_ = rect; + is_a_single_elem_ = is_a_single_elem; + memory_proxy_ = nullptr; + num_channels_ = num_channels; + datatype_ = COM_num_channels_data_type(num_channels); + buffer_ = buffer; owns_data_ = false; - m_state = MemoryBufferState::Temporary; + state_ = MemoryBufferState::Temporary; set_strides(); } -MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) - : MemoryBuffer(src.m_datatype, src.m_rect, false) +MemoryBuffer::MemoryBuffer(const MemoryBuffer &src) : MemoryBuffer(src.datatype_, src.rect_, false) { - m_memoryProxy = src.m_memoryProxy; + memory_proxy_ = src.memory_proxy_; /* src may be single elem buffer */ fill_from(src); } void MemoryBuffer::set_strides() { - if (m_is_a_single_elem) { + if (is_a_single_elem_) { this->elem_stride = 0; this->row_stride = 0; } else { - this->elem_stride = m_num_channels; - this->row_stride = getWidth() * m_num_channels; + this->elem_stride = num_channels_; + this->row_stride = get_width() * num_channels_; } + to_positive_x_stride_ = rect_.xmin < 0 ? -rect_.xmin + 1 : (rect_.xmin == 0 ? 1 : 0); + to_positive_y_stride_ = rect_.ymin < 0 ? -rect_.ymin + 1 : (rect_.ymin == 0 ? 1 : 0); } void MemoryBuffer::clear() { - memset(m_buffer, 0, buffer_len() * m_num_channels * sizeof(float)); + memset(buffer_, 0, buffer_len() * num_channels_ * sizeof(float)); } BuffersIterator MemoryBuffer::iterate_with(Span inputs) { - return iterate_with(inputs, m_rect); + return iterate_with(inputs, rect_); } BuffersIterator MemoryBuffer::iterate_with(Span inputs, const rcti &area) { - BuffersIteratorBuilder builder(m_buffer, m_rect, area, elem_stride); + BuffersIteratorBuilder builder(buffer_, rect_, area, elem_stride); for (MemoryBuffer *input : inputs) { - builder.add_input(input->getBuffer(), input->get_rect(), input->elem_stride); + builder.add_input(input->get_buffer(), input->get_rect(), input->elem_stride); } return builder.build(); } @@ -150,20 +152,20 @@ BuffersIterator MemoryBuffer::iterate_with(Span inputs, c MemoryBuffer *MemoryBuffer::inflate() const { BLI_assert(is_a_single_elem()); - MemoryBuffer *inflated = new MemoryBuffer(this->m_datatype, this->m_rect, false); - inflated->copy_from(this, this->m_rect); + MemoryBuffer *inflated = new MemoryBuffer(datatype_, rect_, false); + inflated->copy_from(this, rect_); return inflated; } float MemoryBuffer::get_max_value() const { - float result = this->m_buffer[0]; + float result = buffer_[0]; const unsigned int size = this->buffer_len(); unsigned int i; - const float *fp_src = this->m_buffer; + const float *fp_src = buffer_; - for (i = 0; i < size; i++, fp_src += this->m_num_channels) { + for (i = 0; i < size; i++, fp_src += num_channels_) { float value = *fp_src; if (value > result) { result = value; @@ -178,10 +180,10 @@ float MemoryBuffer::get_max_value(const rcti &rect) const rcti rect_clamp; /* first clamp the rect by the bounds or we get un-initialized values */ - BLI_rcti_isect(&rect, &this->m_rect, &rect_clamp); + BLI_rcti_isect(&rect, &rect_, &rect_clamp); if (!BLI_rcti_is_empty(&rect_clamp)) { - MemoryBuffer temp_buffer(this->m_datatype, rect_clamp); + MemoryBuffer temp_buffer(datatype_, rect_clamp); temp_buffer.fill_from(*this); return temp_buffer.get_max_value(); } @@ -192,9 +194,9 @@ float MemoryBuffer::get_max_value(const rcti &rect) const MemoryBuffer::~MemoryBuffer() { - if (this->m_buffer && owns_data_) { - MEM_freeN(this->m_buffer); - this->m_buffer = nullptr; + if (buffer_ && owns_data_) { + MEM_freeN(buffer_); + buffer_ = nullptr; } } @@ -246,7 +248,7 @@ void MemoryBuffer::copy_from(const MemoryBuffer *src, void MemoryBuffer::copy_from(const uchar *src, const rcti &area) { const int elem_stride = this->get_num_channels(); - const int row_stride = elem_stride * getWidth(); + const int row_stride = elem_stride * get_width(); copy_from(src, area, 0, this->get_num_channels(), elem_stride, row_stride, 0); } @@ -305,7 +307,7 @@ static void colorspace_to_scene_linear(MemoryBuffer *buf, const rcti &area, Colo const int height = BLI_rcti_size_y(&area); float *out = buf->get_elem(area.xmin, area.ymin); /* If area allows continuous memory do conversion in one step. Otherwise per row. */ - if (buf->getWidth() == width) { + if (buf->get_width() == width) { IMB_colormanagement_colorspace_to_scene_linear( out, width, height, buf->get_num_channels(), colorspace, false); } @@ -395,30 +397,28 @@ void MemoryBuffer::fill(const rcti &area, void MemoryBuffer::fill_from(const MemoryBuffer &src) { rcti overlap; - overlap.xmin = MAX2(this->m_rect.xmin, src.m_rect.xmin); - overlap.xmax = MIN2(this->m_rect.xmax, src.m_rect.xmax); - overlap.ymin = MAX2(this->m_rect.ymin, src.m_rect.ymin); - overlap.ymax = MIN2(this->m_rect.ymax, src.m_rect.ymax); + overlap.xmin = MAX2(rect_.xmin, src.rect_.xmin); + overlap.xmax = MIN2(rect_.xmax, src.rect_.xmax); + overlap.ymin = MAX2(rect_.ymin, src.rect_.ymin); + overlap.ymax = MIN2(rect_.ymax, src.rect_.ymax); copy_from(&src, overlap); } -void MemoryBuffer::writePixel(int x, int y, const float color[4]) +void MemoryBuffer::write_pixel(int x, int y, const float color[4]) { - if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && - y < this->m_rect.ymax) { + if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) { const int offset = get_coords_offset(x, y); - memcpy(&this->m_buffer[offset], color, sizeof(float) * this->m_num_channels); + memcpy(&buffer_[offset], color, sizeof(float) * num_channels_); } } -void MemoryBuffer::addPixel(int x, int y, const float color[4]) +void MemoryBuffer::add_pixel(int x, int y, const float color[4]) { - if (x >= this->m_rect.xmin && x < this->m_rect.xmax && y >= this->m_rect.ymin && - y < this->m_rect.ymax) { + if (x >= rect_.xmin && x < rect_.xmax && y >= rect_.ymin && y < rect_.ymax) { const int offset = get_coords_offset(x, y); - float *dst = &this->m_buffer[offset]; + float *dst = &buffer_[offset]; const float *src = color; - for (int i = 0; i < this->m_num_channels; i++, dst++, src++) { + for (int i = 0; i < num_channels_; i++, dst++, src++) { *dst += *src; } } @@ -433,11 +433,11 @@ static void read_ewa_elem(void *userdata, int x, int y, float result[4]) void MemoryBuffer::read_elem_filtered( const float x, const float y, float dx[2], float dy[2], float *out) const { - BLI_assert(this->m_datatype == DataType::Color); + BLI_assert(datatype_ == DataType::Color); const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; - float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); + float inv_width = 1.0f / (float)this->get_width(), inv_height = 1.0f / (float)this->get_height(); /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, * but compositor uses pixel space. For now let's just divide the values and * switch compositor to normalized space for EWA later. @@ -446,8 +446,8 @@ void MemoryBuffer::read_elem_filtered( float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height}; float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height}; - BLI_ewa_filter(this->getWidth(), - this->getHeight(), + BLI_ewa_filter(this->get_width(), + this->get_height(), false, true, uv_normal, @@ -468,12 +468,13 @@ static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4] /* TODO(manzanilla): to be removed with tiled implementation. */ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2]) { - if (m_is_a_single_elem) { - memcpy(result, m_buffer, sizeof(float) * this->m_num_channels); + if (is_a_single_elem_) { + memcpy(result, buffer_, sizeof(float) * num_channels_); } else { - BLI_assert(this->m_datatype == DataType::Color); - float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight(); + BLI_assert(datatype_ == DataType::Color); + float inv_width = 1.0f / (float)this->get_width(), + inv_height = 1.0f / (float)this->get_height(); /* TODO(sergey): Render pipeline uses normalized coordinates and derivatives, * but compositor uses pixel space. For now let's just divide the values and * switch compositor to normalized space for EWA later. @@ -482,8 +483,8 @@ void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivat float du_normal[2] = {derivatives[0][0] * inv_width, derivatives[0][1] * inv_height}; float dv_normal[2] = {derivatives[1][0] * inv_width, derivatives[1][1] * inv_height}; - BLI_ewa_filter(this->getWidth(), - this->getHeight(), + BLI_ewa_filter(this->get_width(), + this->get_height(), false, true, uv_normal, diff --git a/source/blender/compositor/intern/COM_MemoryBuffer.h b/source/blender/compositor/intern/COM_MemoryBuffer.h index f730d53acec..33d78c99ca6 100644 --- a/source/blender/compositor/intern/COM_MemoryBuffer.h +++ b/source/blender/compositor/intern/COM_MemoryBuffer.h @@ -21,12 +21,13 @@ #include "COM_BufferArea.h" #include "COM_BufferRange.h" #include "COM_BuffersIterator.h" -#include "COM_ExecutionGroup.h" -#include "COM_MemoryProxy.h" +#include "COM_Enums.h" -#include "BLI_math.h" +#include "BLI_math_interp.h" #include "BLI_rect.h" +struct ImBuf; + namespace blender::compositor { /** @@ -76,54 +77,60 @@ class MemoryBuffer { /** * \brief proxy of the memory (same for all chunks in the same buffer) */ - MemoryProxy *m_memoryProxy; + MemoryProxy *memory_proxy_; /** * \brief the type of buffer DataType::Value, DataType::Vector, DataType::Color */ - DataType m_datatype; + DataType datatype_; /** * \brief region of this buffer inside relative to the MemoryProxy */ - rcti m_rect; + rcti rect_; /** * \brief state of the buffer */ - MemoryBufferState m_state; + MemoryBufferState state_; /** * \brief the actual float buffer/data */ - float *m_buffer; + float *buffer_; /** * \brief the number of channels of a single value in the buffer. * For value buffers this is 1, vector 3 and color 4 */ - uint8_t m_num_channels; + uint8_t num_channels_; /** * Whether buffer is a single element in memory. */ - bool m_is_a_single_elem; + bool is_a_single_elem_; /** * Whether MemoryBuffer owns buffer data. */ bool owns_data_; + /** Stride to make any x coordinate within buffer positive (non-zero). */ + int to_positive_x_stride_; + + /** Stride to make any y coordinate within buffer positive (non-zero). */ + int to_positive_y_stride_; + public: /** * \brief construct new temporarily MemoryBuffer for an area */ - MemoryBuffer(MemoryProxy *memoryProxy, const rcti &rect, MemoryBufferState state); + MemoryBuffer(MemoryProxy *memory_proxy, const rcti &rect, MemoryBufferState state); /** * \brief construct new temporarily MemoryBuffer for an area */ - MemoryBuffer(DataType datatype, const rcti &rect, bool is_a_single_elem = false); + MemoryBuffer(DataType data_type, const rcti &rect, bool is_a_single_elem = false); MemoryBuffer( float *buffer, int num_channels, int width, int height, bool is_a_single_elem = false); @@ -146,29 +153,29 @@ class MemoryBuffer { */ bool is_a_single_elem() const { - return m_is_a_single_elem; + return is_a_single_elem_; } float &operator[](int index) { - BLI_assert(m_is_a_single_elem ? index < m_num_channels : - index < get_coords_offset(getWidth(), getHeight())); - return m_buffer[index]; + BLI_assert(is_a_single_elem_ ? index < num_channels_ : + index < get_coords_offset(get_width(), get_height())); + return buffer_[index]; } const float &operator[](int index) const { - BLI_assert(m_is_a_single_elem ? index < m_num_channels : - index < get_coords_offset(getWidth(), getHeight())); - return m_buffer[index]; + BLI_assert(is_a_single_elem_ ? index < num_channels_ : + index < get_coords_offset(get_width(), get_height())); + return buffer_[index]; } /** * Get offset needed to jump from buffer start to given coordinates. */ - int get_coords_offset(int x, int y) const + intptr_t get_coords_offset(int x, int y) const { - return (y - m_rect.ymin) * row_stride + (x - m_rect.xmin) * elem_stride; + return ((intptr_t)y - rect_.ymin) * row_stride + ((intptr_t)x - rect_.xmin) * elem_stride; } /** @@ -176,8 +183,8 @@ class MemoryBuffer { */ float *get_elem(int x, int y) { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax); - return m_buffer + get_coords_offset(x, y); + BLI_assert(has_coords(x, y)); + return buffer_ + get_coords_offset(x, y); } /** @@ -185,8 +192,8 @@ class MemoryBuffer { */ const float *get_elem(int x, int y) const { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax); - return m_buffer + get_coords_offset(x, y); + BLI_assert(has_coords(x, y)); + return buffer_ + get_coords_offset(x, y); } void read_elem(int x, int y, float *out) const @@ -196,7 +203,7 @@ class MemoryBuffer { void read_elem_checked(int x, int y, float *out) const { - if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { + if (!has_coords(x, y)) { clear_elem(out); } else { @@ -206,32 +213,25 @@ class MemoryBuffer { void read_elem_checked(float x, float y, float *out) const { - if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) { - clear_elem(out); - } - else { - read_elem(x, y, out); - } + read_elem_checked(floor_x(x), floor_y(y), out); } void read_elem_bilinear(float x, float y, float *out) const { /* Only clear past +/-1 borders to be able to smooth edges. */ - if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f || - y >= m_rect.ymax) { + if (x <= rect_.xmin - 1.0f || x >= rect_.xmax || y <= rect_.ymin - 1.0f || y >= rect_.ymax) { clear_elem(out); return; } - if (m_is_a_single_elem) { - if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin && - y < m_rect.ymax - 1.0f) { - memcpy(out, m_buffer, get_elem_bytes_len()); + if (is_a_single_elem_) { + if (x >= rect_.xmin && x < rect_.xmax - 1.0f && y >= rect_.ymin && y < rect_.ymax - 1.0f) { + memcpy(out, buffer_, get_elem_bytes_len()); return; } /* Do sampling at borders to smooth edges. */ - const float last_x = getWidth() - 1.0f; + const float last_x = get_width() - 1.0f; const float rel_x = get_relative_x(x); float single_x = 0.0f; if (rel_x < 0.0f) { @@ -241,7 +241,7 @@ class MemoryBuffer { single_x = rel_x - last_x; } - const float last_y = getHeight() - 1.0f; + const float last_y = get_height() - 1.0f; const float rel_y = get_relative_y(y); float single_y = 0.0f; if (rel_y < 0.0f) { @@ -251,15 +251,15 @@ class MemoryBuffer { single_y = rel_y - last_y; } - BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y); + BLI_bilinear_interpolation_fl(buffer_, out, 1, 1, num_channels_, single_x, single_y); return; } - BLI_bilinear_interpolation_fl(m_buffer, + BLI_bilinear_interpolation_fl(buffer_, out, - getWidth(), - getHeight(), - m_num_channels, + get_width(), + get_height(), + num_channels_, get_relative_x(x), get_relative_y(y)); } @@ -286,9 +286,8 @@ class MemoryBuffer { */ float &get_value(int x, int y, int channel) { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax && - channel >= 0 && channel < m_num_channels); - return m_buffer[get_coords_offset(x, y) + channel]; + BLI_assert(has_coords(x, y) && channel >= 0 && channel < num_channels_); + return buffer_[get_coords_offset(x, y) + channel]; } /** @@ -296,9 +295,8 @@ class MemoryBuffer { */ const float &get_value(int x, int y, int channel) const { - BLI_assert(x >= m_rect.xmin && x < m_rect.xmax && y >= m_rect.ymin && y < m_rect.ymax && - channel >= 0 && channel < m_num_channels); - return m_buffer[get_coords_offset(x, y) + channel]; + BLI_assert(has_coords(x, y) && channel >= 0 && channel < num_channels_); + return buffer_[get_coords_offset(x, y) + channel]; } /** @@ -306,8 +304,8 @@ class MemoryBuffer { */ const float *get_row_end(int y) const { - BLI_assert(y >= 0 && y < getHeight()); - return m_buffer + (is_a_single_elem() ? m_num_channels : get_coords_offset(getWidth(), y)); + BLI_assert(has_y(y)); + return buffer_ + (is_a_single_elem() ? num_channels_ : get_coords_offset(get_width(), y)); } /** @@ -316,7 +314,7 @@ class MemoryBuffer { */ int get_memory_width() const { - return is_a_single_elem() ? 1 : getWidth(); + return is_a_single_elem() ? 1 : get_width(); } /** @@ -325,17 +323,17 @@ class MemoryBuffer { */ int get_memory_height() const { - return is_a_single_elem() ? 1 : getHeight(); + return is_a_single_elem() ? 1 : get_height(); } uint8_t get_num_channels() const { - return this->m_num_channels; + return num_channels_; } uint8_t get_elem_bytes_len() const { - return this->m_num_channels * sizeof(float); + return num_channels_ * sizeof(float); } /** @@ -343,22 +341,22 @@ class MemoryBuffer { */ BufferRange as_range() { - return BufferRange(m_buffer, 0, buffer_len(), elem_stride); + return BufferRange(buffer_, 0, buffer_len(), elem_stride); } BufferRange as_range() const { - return BufferRange(m_buffer, 0, buffer_len(), elem_stride); + return BufferRange(buffer_, 0, buffer_len(), elem_stride); } BufferArea get_buffer_area(const rcti &area) { - return BufferArea(m_buffer, getWidth(), area, elem_stride); + return BufferArea(buffer_, get_width(), area, elem_stride); } BufferArea get_buffer_area(const rcti &area) const { - return BufferArea(m_buffer, getWidth(), area, elem_stride); + return BufferArea(buffer_, get_width(), area, elem_stride); } BuffersIterator iterate_with(Span inputs); @@ -368,25 +366,25 @@ class MemoryBuffer { * \brief get the data of this MemoryBuffer * \note buffer should already be available in memory */ - float *getBuffer() + float *get_buffer() { - return this->m_buffer; + return buffer_; } float *release_ownership_buffer() { owns_data_ = false; - return this->m_buffer; + return buffer_; } MemoryBuffer *inflate() const; inline void wrap_pixel(int &x, int &y, MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) { - const int w = getWidth(); - const int h = getHeight(); - x = x - m_rect.xmin; - y = y - m_rect.ymin; + const int w = get_width(); + const int h = get_height(); + x = x - rect_.xmin; + y = y - rect_.ymin; switch (extend_x) { case MemoryBufferExtend::Clip: @@ -426,8 +424,8 @@ class MemoryBuffer { break; } - x = x + m_rect.xmin; - y = y + m_rect.ymin; + x = x + rect_.xmin; + y = y + rect_.ymin; } inline void wrap_pixel(float &x, @@ -435,10 +433,10 @@ class MemoryBuffer { MemoryBufferExtend extend_x, MemoryBufferExtend extend_y) const { - const float w = (float)getWidth(); - const float h = (float)getHeight(); - x = x - m_rect.xmin; - y = y - m_rect.ymin; + const float w = (float)get_width(); + const float h = (float)get_height(); + x = x - rect_.xmin; + y = y - rect_.ymin; switch (extend_x) { case MemoryBufferExtend::Clip: @@ -478,8 +476,8 @@ class MemoryBuffer { break; } - x = x + m_rect.xmin; - y = y + m_rect.ymin; + x = x + rect_.xmin; + y = y + rect_.ymin; } /* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend @@ -490,28 +488,28 @@ class MemoryBuffer { MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) { - bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < m_rect.xmin || x >= m_rect.xmax)); - bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < m_rect.ymin || y >= m_rect.ymax)); + bool clip_x = (extend_x == MemoryBufferExtend::Clip && (x < rect_.xmin || x >= rect_.xmax)); + bool clip_y = (extend_y == MemoryBufferExtend::Clip && (y < rect_.ymin || y >= rect_.ymax)); if (clip_x || clip_y) { /* clip result outside rect is zero */ - memset(result, 0, this->m_num_channels * sizeof(float)); + memset(result, 0, num_channels_ * sizeof(float)); } else { int u = x; int v = y; this->wrap_pixel(u, v, extend_x, extend_y); const int offset = get_coords_offset(u, v); - float *buffer = &this->m_buffer[offset]; - memcpy(result, buffer, sizeof(float) * this->m_num_channels); + float *buffer = &buffer_[offset]; + memcpy(result, buffer, sizeof(float) * num_channels_); } } /* TODO(manzanilla): to be removed with tiled implementation. */ - inline void readNoCheck(float *result, - int x, - int y, - MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, - MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) + inline void read_no_check(float *result, + int x, + int y, + MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, + MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) { int u = x; int v = y; @@ -520,38 +518,38 @@ class MemoryBuffer { const int offset = get_coords_offset(u, v); BLI_assert(offset >= 0); - BLI_assert(offset < this->buffer_len() * this->m_num_channels); - BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < m_rect.xmin || u >= m_rect.xmax)) && - !(extend_y == MemoryBufferExtend::Clip && (v < m_rect.ymin || v >= m_rect.ymax))); - float *buffer = &this->m_buffer[offset]; - memcpy(result, buffer, sizeof(float) * this->m_num_channels); + BLI_assert(offset < this->buffer_len() * num_channels_); + BLI_assert(!(extend_x == MemoryBufferExtend::Clip && (u < rect_.xmin || u >= rect_.xmax)) && + !(extend_y == MemoryBufferExtend::Clip && (v < rect_.ymin || v >= rect_.ymax))); + float *buffer = &buffer_[offset]; + memcpy(result, buffer, sizeof(float) * num_channels_); } - void writePixel(int x, int y, const float color[4]); - void addPixel(int x, int y, const float color[4]); - inline void readBilinear(float *result, - float x, - float y, - MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, - MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const + void write_pixel(int x, int y, const float color[4]); + void add_pixel(int x, int y, const float color[4]); + inline void read_bilinear(float *result, + float x, + float y, + MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, + MemoryBufferExtend extend_y = MemoryBufferExtend::Clip) const { float u = x; float v = y; this->wrap_pixel(u, v, extend_x, extend_y); - if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= getWidth())) || - (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= getHeight()))) { - copy_vn_fl(result, this->m_num_channels, 0.0f); + if ((extend_x != MemoryBufferExtend::Repeat && (u < 0.0f || u >= get_width())) || + (extend_y != MemoryBufferExtend::Repeat && (v < 0.0f || v >= get_height()))) { + copy_vn_fl(result, num_channels_, 0.0f); return; } - if (m_is_a_single_elem) { - memcpy(result, m_buffer, sizeof(float) * this->m_num_channels); + if (is_a_single_elem_) { + memcpy(result, buffer_, sizeof(float) * num_channels_); } else { - BLI_bilinear_interpolation_wrap_fl(this->m_buffer, + BLI_bilinear_interpolation_wrap_fl(buffer_, result, - getWidth(), - getHeight(), - this->m_num_channels, + get_width(), + get_height(), + num_channels_, u, v, extend_x == MemoryBufferExtend::Repeat, @@ -564,9 +562,9 @@ class MemoryBuffer { /** * \brief is this MemoryBuffer a temporarily buffer (based on an area, not on a chunk) */ - inline bool isTemporarily() const + inline bool is_temporarily() const { - return this->m_state == MemoryBufferState::Temporary; + return state_ == MemoryBufferState::Temporary; } void copy_from(const MemoryBuffer *src, const rcti &area); @@ -619,8 +617,8 @@ class MemoryBuffer { void fill(const rcti &area, const float *value); void fill(const rcti &area, int channel_offset, const float *value, int value_size); /** - * \brief add the content from otherBuffer to this MemoryBuffer - * \param otherBuffer: source buffer + * \brief add the content from other_buffer to this MemoryBuffer + * \param other_buffer: source buffer * * \note take care when running this on a new buffer since it won't fill in * uninitialized values in areas where the buffers don't overlap. @@ -632,23 +630,23 @@ class MemoryBuffer { */ const rcti &get_rect() const { - return this->m_rect; + return rect_; } /** * \brief get the width of this MemoryBuffer */ - const int getWidth() const + const int get_width() const { - return BLI_rcti_size_x(&m_rect); + return BLI_rcti_size_x(&rect_); } /** * \brief get the height of this MemoryBuffer */ - const int getHeight() const + const int get_height() const { - return BLI_rcti_size_y(&m_rect); + return BLI_rcti_size_y(&rect_); } /** @@ -668,17 +666,45 @@ class MemoryBuffer { void clear_elem(float *out) const { - memset(out, 0, this->m_num_channels * sizeof(float)); + memset(out, 0, num_channels_ * sizeof(float)); } template T get_relative_x(T x) const { - return x - m_rect.xmin; + return x - rect_.xmin; } template T get_relative_y(T y) const { - return y - m_rect.ymin; + return y - rect_.ymin; + } + + template bool has_coords(T x, T y) const + { + return has_x(x) && has_y(y); + } + + template bool has_x(T x) const + { + return x >= rect_.xmin && x < rect_.xmax; + } + + template bool has_y(T y) const + { + return y >= rect_.ymin && y < rect_.ymax; + } + + /* Fast `floor(..)` functions. The caller should check result is within buffer bounds. + * It `ceil(..)` in near cases and when given coordinate + * is negative and less than buffer rect `min - 1`. */ + int floor_x(float x) const + { + return (int)(x + to_positive_x_stride_) - to_positive_x_stride_; + } + + int floor_y(float y) const + { + return (int)(y + to_positive_y_stride_) - to_positive_y_stride_; } void copy_single_elem_from(const MemoryBuffer *src, diff --git a/source/blender/compositor/intern/COM_MemoryProxy.cc b/source/blender/compositor/intern/COM_MemoryProxy.cc index 6023850c944..895990bc87f 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.cc +++ b/source/blender/compositor/intern/COM_MemoryProxy.cc @@ -17,18 +17,15 @@ */ #include "COM_MemoryProxy.h" - #include "COM_MemoryBuffer.h" -#include "BLI_rect.h" - namespace blender::compositor { MemoryProxy::MemoryProxy(DataType datatype) { - this->m_writeBufferOperation = nullptr; - this->m_executor = nullptr; - this->m_datatype = datatype; + write_buffer_operation_ = nullptr; + executor_ = nullptr; + datatype_ = datatype; } void MemoryProxy::allocate(unsigned int width, unsigned int height) @@ -39,14 +36,14 @@ void MemoryProxy::allocate(unsigned int width, unsigned int height) result.ymin = 0; result.ymax = height; - this->m_buffer = new MemoryBuffer(this, result, MemoryBufferState::Default); + buffer_ = new MemoryBuffer(this, result, MemoryBufferState::Default); } void MemoryProxy::free() { - if (this->m_buffer) { - delete this->m_buffer; - this->m_buffer = nullptr; + if (buffer_) { + delete buffer_; + buffer_ = nullptr; } } diff --git a/source/blender/compositor/intern/COM_MemoryProxy.h b/source/blender/compositor/intern/COM_MemoryProxy.h index 6814afada74..89149a47976 100644 --- a/source/blender/compositor/intern/COM_MemoryProxy.h +++ b/source/blender/compositor/intern/COM_MemoryProxy.h @@ -42,22 +42,22 @@ class MemoryProxy { /** * \brief reference to the output operation of the executiongroup */ - WriteBufferOperation *m_writeBufferOperation; + WriteBufferOperation *write_buffer_operation_; /** * \brief reference to the executor. the Execution group that can fill a chunk */ - ExecutionGroup *m_executor; + ExecutionGroup *executor_; /** * \brief the allocated memory */ - MemoryBuffer *m_buffer; + MemoryBuffer *buffer_; /** * \brief datatype of this MemoryProxy */ - DataType m_datatype; + DataType datatype_; public: MemoryProxy(DataType type); @@ -66,35 +66,35 @@ class MemoryProxy { * \brief set the ExecutionGroup that can be scheduled to calculate a certain chunk. * \param group: the ExecutionGroup to set */ - void setExecutor(ExecutionGroup *executor) + void set_executor(ExecutionGroup *executor) { - this->m_executor = executor; + executor_ = executor; } /** * \brief get the ExecutionGroup that can be scheduled to calculate a certain chunk. */ - ExecutionGroup *getExecutor() const + ExecutionGroup *get_executor() const { - return this->m_executor; + return executor_; } /** * \brief set the WriteBufferOperation that is responsible for writing to this MemoryProxy * \param operation: */ - void setWriteBufferOperation(WriteBufferOperation *operation) + void set_write_buffer_operation(WriteBufferOperation *operation) { - this->m_writeBufferOperation = operation; + write_buffer_operation_ = operation; } /** * \brief get the WriteBufferOperation that is responsible for writing to this MemoryProxy * \return WriteBufferOperation */ - WriteBufferOperation *getWriteBufferOperation() const + WriteBufferOperation *get_write_buffer_operation() const { - return this->m_writeBufferOperation; + return write_buffer_operation_; } /** @@ -110,14 +110,14 @@ class MemoryProxy { /** * \brief get the allocated memory */ - inline MemoryBuffer *getBuffer() + inline MemoryBuffer *get_buffer() { - return this->m_buffer; + return buffer_; } - inline DataType getDataType() + inline DataType get_data_type() { - return this->m_datatype; + return datatype_; } #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_MetaData.cc b/source/blender/compositor/intern/COM_MetaData.cc index a6fb84dfb87..530634a6e41 100644 --- a/source/blender/compositor/intern/COM_MetaData.cc +++ b/source/blender/compositor/intern/COM_MetaData.cc @@ -22,8 +22,6 @@ #include "RE_pipeline.h" -#include - namespace blender::compositor { void MetaData::add(const blender::StringRef key, const blender::StringRef value) @@ -31,9 +29,9 @@ void MetaData::add(const blender::StringRef key, const blender::StringRef value) entries_.add(key, value); } -void MetaData::addCryptomatteEntry(const blender::StringRef layer_name, - const blender::StringRefNull key, - const blender::StringRef value) +void MetaData::add_cryptomatte_entry(const blender::StringRef layer_name, + const blender::StringRefNull key, + const blender::StringRef value) { add(blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(layer_name, key), value); } @@ -42,7 +40,7 @@ void MetaData::addCryptomatteEntry(const blender::StringRef layer_name, * * When a conversion happens it will also add the cryptomatte name key with the given * `layer_name`. */ -void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name) +void MetaData::replace_hash_neutral_cryptomatte_keys(const blender::StringRef layer_name) { std::string cryptomatte_hash = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_HASH, ""); std::string cryptomatte_conversion = entries_.pop_default(META_DATA_KEY_CRYPTOMATTE_CONVERSION, @@ -51,27 +49,28 @@ void MetaData::replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_ if (cryptomatte_hash.length() || cryptomatte_conversion.length() || cryptomatte_manifest.length()) { - addCryptomatteEntry(layer_name, "name", layer_name); + add_cryptomatte_entry(layer_name, "name", layer_name); } if (cryptomatte_hash.length()) { - addCryptomatteEntry(layer_name, "hash", cryptomatte_hash); + add_cryptomatte_entry(layer_name, "hash", cryptomatte_hash); } if (cryptomatte_conversion.length()) { - addCryptomatteEntry(layer_name, "conversion", cryptomatte_conversion); + add_cryptomatte_entry(layer_name, "conversion", cryptomatte_conversion); } if (cryptomatte_manifest.length()) { - addCryptomatteEntry(layer_name, "manifest", cryptomatte_manifest); + add_cryptomatte_entry(layer_name, "manifest", cryptomatte_manifest); } } -void MetaData::addToRenderResult(RenderResult *render_result) const +void MetaData::add_to_render_result(RenderResult *render_result) const { for (Map::Item entry : entries_.items()) { BKE_render_result_stamp_data(render_result, entry.key.c_str(), entry.value.c_str()); } } -void MetaDataExtractCallbackData::addMetaData(blender::StringRef key, blender::StringRefNull value) +void MetaDataExtractCallbackData::add_meta_data(blender::StringRef key, + blender::StringRefNull value) { if (!meta_data) { meta_data = std::make_unique(); @@ -79,7 +78,7 @@ void MetaDataExtractCallbackData::addMetaData(blender::StringRef key, blender::S meta_data->add(key, value); } -void MetaDataExtractCallbackData::setCryptomatteKeys(blender::StringRef cryptomatte_layer_name) +void MetaDataExtractCallbackData::set_cryptomatte_keys(blender::StringRef cryptomatte_layer_name) { manifest_key = blender::bke::cryptomatte::BKE_cryptomatte_meta_data_key(cryptomatte_layer_name, "manifest"); @@ -97,13 +96,13 @@ void MetaDataExtractCallbackData::extract_cryptomatte_meta_data(void *_data, MetaDataExtractCallbackData *data = static_cast(_data); blender::StringRefNull key(propname); if (key == data->hash_key) { - data->addMetaData(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue); + data->add_meta_data(META_DATA_KEY_CRYPTOMATTE_HASH, propvalue); } else if (key == data->conversion_key) { - data->addMetaData(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue); + data->add_meta_data(META_DATA_KEY_CRYPTOMATTE_CONVERSION, propvalue); } else if (key == data->manifest_key) { - data->addMetaData(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue); + data->add_meta_data(META_DATA_KEY_CRYPTOMATTE_MANIFEST, propvalue); } } diff --git a/source/blender/compositor/intern/COM_MetaData.h b/source/blender/compositor/intern/COM_MetaData.h index a76540dc3af..0bb014525c3 100644 --- a/source/blender/compositor/intern/COM_MetaData.h +++ b/source/blender/compositor/intern/COM_MetaData.h @@ -44,14 +44,14 @@ constexpr blender::StringRef META_DATA_KEY_CRYPTOMATTE_NAME("cryptomatte/{hash}/ class MetaData { private: Map entries_; - void addCryptomatteEntry(const blender::StringRef layer_name, - const blender::StringRefNull key, - const blender::StringRef value); + void add_cryptomatte_entry(const blender::StringRef layer_name, + const blender::StringRefNull key, + const blender::StringRef value); public: void add(const blender::StringRef key, const blender::StringRef value); - void replaceHashNeutralCryptomatteKeys(const blender::StringRef layer_name); - void addToRenderResult(RenderResult *render_result) const; + void replace_hash_neutral_cryptomatte_keys(const blender::StringRef layer_name); + void add_to_render_result(RenderResult *render_result) const; #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:MetaData") #endif @@ -63,8 +63,8 @@ struct MetaDataExtractCallbackData { std::string conversion_key; std::string manifest_key; - void addMetaData(blender::StringRef key, blender::StringRefNull value); - void setCryptomatteKeys(blender::StringRef cryptomatte_layer_name); + void add_meta_data(blender::StringRef key, blender::StringRefNull value); + void set_cryptomatte_keys(blender::StringRef cryptomatte_layer_name); /* C type callback function (StampCallback). */ static void extract_cryptomatte_meta_data(void *_data, const char *propname, diff --git a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc index 7ccf6f76d9f..d118027202a 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_MultiThreadedOperation.cc @@ -7,7 +7,7 @@ MultiThreadedOperation::MultiThreadedOperation() { num_passes_ = 1; current_pass_ = 0; - flags.is_fullframe_operation = true; + flags_.is_fullframe_operation = true; } void MultiThreadedOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h index 3daa9eec474..c1835e85949 100644 --- a/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h +++ b/source/blender/compositor/intern/COM_MultiThreadedRowOperation.h @@ -18,6 +18,8 @@ #pragma once +#include "BLI_array.h" + #include "COM_MultiThreadedOperation.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_Node.cc b/source/blender/compositor/intern/COM_Node.cc index 6ac48e3646c..67af9496ca4 100644 --- a/source/blender/compositor/intern/COM_Node.cc +++ b/source/blender/compositor/intern/COM_Node.cc @@ -16,20 +16,10 @@ * Copyright 2011, Blender Foundation. */ -#include - #include "BKE_node.h" #include "RNA_access.h" -#include "COM_ExecutionSystem.h" -#include "COM_NodeOperation.h" -#include "COM_TranslateOperation.h" - -#include "COM_SocketProxyNode.h" - -#include "COM_defines.h" - #include "COM_Node.h" /* own include */ namespace blender::compositor { @@ -38,14 +28,14 @@ namespace blender::compositor { **** Node **** **************/ -Node::Node(bNode *editorNode, bool create_sockets) - : m_editorNodeTree(nullptr), - m_editorNode(editorNode), - m_inActiveGroup(false), - m_instanceKey(NODE_INSTANCE_KEY_NONE) +Node::Node(bNode *editor_node, bool create_sockets) + : editor_node_tree_(nullptr), + editor_node_(editor_node), + in_active_group_(false), + instance_key_(NODE_INSTANCE_KEY_NONE) { if (create_sockets) { - bNodeSocket *input = (bNodeSocket *)editorNode->inputs.first; + bNodeSocket *input = (bNodeSocket *)editor_node->inputs.first; while (input != nullptr) { DataType dt = DataType::Value; if (input->type == SOCK_RGBA) { @@ -55,10 +45,10 @@ Node::Node(bNode *editorNode, bool create_sockets) dt = DataType::Vector; } - this->addInputSocket(dt, input); + this->add_input_socket(dt, input); input = input->next; } - bNodeSocket *output = (bNodeSocket *)editorNode->outputs.first; + bNodeSocket *output = (bNodeSocket *)editor_node->outputs.first; while (output != nullptr) { DataType dt = DataType::Value; if (output->type == SOCK_RGBA) { @@ -68,7 +58,7 @@ Node::Node(bNode *editorNode, bool create_sockets) dt = DataType::Vector; } - this->addOutputSocket(dt, output); + this->add_output_socket(dt, output); output = output->next; } } @@ -76,51 +66,51 @@ Node::Node(bNode *editorNode, bool create_sockets) Node::~Node() { - while (!this->outputs.is_empty()) { - delete (this->outputs.pop_last()); + while (!outputs_.is_empty()) { + delete (outputs_.pop_last()); } - while (!this->inputs.is_empty()) { - delete (this->inputs.pop_last()); + while (!inputs_.is_empty()) { + delete (inputs_.pop_last()); } } -void Node::addInputSocket(DataType datatype) +void Node::add_input_socket(DataType datatype) { - this->addInputSocket(datatype, nullptr); + this->add_input_socket(datatype, nullptr); } -void Node::addInputSocket(DataType datatype, bNodeSocket *bSocket) +void Node::add_input_socket(DataType datatype, bNodeSocket *bSocket) { NodeInput *socket = new NodeInput(this, bSocket, datatype); - this->inputs.append(socket); + inputs_.append(socket); } -void Node::addOutputSocket(DataType datatype) +void Node::add_output_socket(DataType datatype) { - this->addOutputSocket(datatype, nullptr); + this->add_output_socket(datatype, nullptr); } -void Node::addOutputSocket(DataType datatype, bNodeSocket *bSocket) +void Node::add_output_socket(DataType datatype, bNodeSocket *bSocket) { NodeOutput *socket = new NodeOutput(this, bSocket, datatype); - outputs.append(socket); + outputs_.append(socket); } -NodeOutput *Node::getOutputSocket(unsigned int index) const +NodeOutput *Node::get_output_socket(unsigned int index) const { - return outputs[index]; + return outputs_[index]; } -NodeInput *Node::getInputSocket(unsigned int index) const +NodeInput *Node::get_input_socket(unsigned int index) const { - return inputs[index]; + return inputs_[index]; } -bNodeSocket *Node::getEditorInputSocket(int editorNodeInputSocketIndex) +bNodeSocket *Node::get_editor_input_socket(int editor_node_input_socket_index) { - bNodeSocket *bSock = (bNodeSocket *)this->getbNode()->inputs.first; + bNodeSocket *bSock = (bNodeSocket *)this->get_bnode()->inputs.first; int index = 0; while (bSock != nullptr) { - if (index == editorNodeInputSocketIndex) { + if (index == editor_node_input_socket_index) { return bSock; } index++; @@ -128,12 +118,12 @@ bNodeSocket *Node::getEditorInputSocket(int editorNodeInputSocketIndex) } return nullptr; } -bNodeSocket *Node::getEditorOutputSocket(int editorNodeOutputSocketIndex) +bNodeSocket *Node::get_editor_output_socket(int editor_node_output_socket_index) { - bNodeSocket *bSock = (bNodeSocket *)this->getbNode()->outputs.first; + bNodeSocket *bSock = (bNodeSocket *)this->get_bnode()->outputs.first; int index = 0; while (bSock != nullptr) { - if (index == editorNodeOutputSocketIndex) { + if (index == editor_node_output_socket_index) { return bSock; } index++; @@ -147,33 +137,33 @@ bNodeSocket *Node::getEditorOutputSocket(int editorNodeOutputSocketIndex) *******************/ NodeInput::NodeInput(Node *node, bNodeSocket *b_socket, DataType datatype) - : m_node(node), m_editorSocket(b_socket), m_datatype(datatype), m_link(nullptr) + : node_(node), editor_socket_(b_socket), datatype_(datatype), link_(nullptr) { } -void NodeInput::setLink(NodeOutput *link) +void NodeInput::set_link(NodeOutput *link) { - m_link = link; + link_ = link; } -float NodeInput::getEditorValueFloat() const +float NodeInput::get_editor_value_float() const { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get(&ptr, "default_value"); } -void NodeInput::getEditorValueColor(float *value) const +void NodeInput::get_editor_value_color(float *value) const { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } -void NodeInput::getEditorValueVector(float *value) const +void NodeInput::get_editor_value_vector(float *value) const { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } @@ -182,28 +172,28 @@ void NodeInput::getEditorValueVector(float *value) const ********************/ NodeOutput::NodeOutput(Node *node, bNodeSocket *b_socket, DataType datatype) - : m_node(node), m_editorSocket(b_socket), m_datatype(datatype) + : node_(node), editor_socket_(b_socket), datatype_(datatype) { } -float NodeOutput::getEditorValueFloat() +float NodeOutput::get_editor_value_float() { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get(&ptr, "default_value"); } -void NodeOutput::getEditorValueColor(float *value) +void NodeOutput::get_editor_value_color(float *value) { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } -void NodeOutput::getEditorValueVector(float *value) +void NodeOutput::get_editor_value_vector(float *value) { PointerRNA ptr; - RNA_pointer_create((ID *)getNode()->getbNodeTree(), &RNA_NodeSocket, getbNodeSocket(), &ptr); + RNA_pointer_create((ID *)get_node()->get_bnodetree(), &RNA_NodeSocket, get_bnode_socket(), &ptr); return RNA_float_get_array(&ptr, "default_value", value); } diff --git a/source/blender/compositor/intern/COM_Node.h b/source/blender/compositor/intern/COM_Node.h index 28b59397af9..dd126770303 100644 --- a/source/blender/compositor/intern/COM_Node.h +++ b/source/blender/compositor/intern/COM_Node.h @@ -22,9 +22,6 @@ #include "DNA_node_types.h" -#include -#include - /* common node includes * added here so node files don't have to include themselves */ @@ -44,52 +41,52 @@ class Node { /** * \brief stores the reference to the SDNA bNode struct */ - bNodeTree *m_editorNodeTree; + bNodeTree *editor_node_tree_; /** * \brief stores the reference to the SDNA bNode struct */ - bNode *m_editorNode; + bNode *editor_node_; /** * \brief Is this node part of the active group */ - bool m_inActiveGroup; + bool in_active_group_; /** * \brief Instance key to identify the node in an instance hash table */ - bNodeInstanceKey m_instanceKey; + bNodeInstanceKey instance_key_; protected: /** * \brief the list of actual input-sockets \see NodeInput */ - Vector inputs; + Vector inputs_; /** * \brief the list of actual output-sockets \see NodeOutput */ - Vector outputs; + Vector outputs_; public: - Node(bNode *editorNode, bool create_sockets = true); + Node(bNode *editor_node, bool create_sockets = true); virtual ~Node(); /** * \brief get the reference to the SDNA bNode struct */ - bNode *getbNode() const + bNode *get_bnode() const { - return m_editorNode; + return editor_node_; } /** * \brief get the reference to the SDNA bNodeTree struct */ - bNodeTree *getbNodeTree() const + bNodeTree *get_bnodetree() const { - return m_editorNodeTree; + return editor_node_tree_; } /** @@ -98,57 +95,55 @@ class Node { * node for highlight during execution. * \param bNode: */ - void setbNode(bNode *node) + void set_bnode(bNode *node) { - this->m_editorNode = node; + editor_node_ = node; } /** * \brief set the reference to the bNodeTree * \param bNodeTree: */ - void setbNodeTree(bNodeTree *nodetree) + void set_bnodetree(bNodeTree *nodetree) { - this->m_editorNodeTree = nodetree; + editor_node_tree_ = nodetree; } /** * \brief get access to the vector of input sockets */ - const Vector &getInputSockets() const + const Vector &get_input_sockets() const { - return this->inputs; + return inputs_; } /** * \brief get access to the vector of input sockets */ - const Vector &getOutputSockets() const + const Vector &get_output_sockets() const { - return this->outputs; + return outputs_; } /** - * get the reference to a certain outputsocket - * \param index: - * the index of the needed outputsocket + * Get the reference to a certain output-socket. + * \param index: The index of the needed output-socket. */ - NodeOutput *getOutputSocket(const unsigned int index = 0) const; + NodeOutput *get_output_socket(const unsigned int index = 0) const; /** - * get the reference to a certain inputsocket - * \param index: - * the index of the needed inputsocket + * get the reference to a certain input-socket. + * \param index: The index of the needed input-socket. */ - NodeInput *getInputSocket(const unsigned int index) const; + NodeInput *get_input_socket(const unsigned int index) const; /** * \brief Is this node in the active group (the group that is being edited) - * \param isInActiveGroup: + * \param is_in_active_group: */ - void setIsInActiveGroup(bool value) + void set_is_in_active_group(bool value) { - this->m_inActiveGroup = value; + in_active_group_ = value; } /** @@ -157,9 +152,9 @@ class Node { * the active group will be the main tree (all nodes that are not part of a group will be active) * \return bool [false:true] */ - inline bool isInActiveGroup() const + inline bool is_in_active_group() const { - return this->m_inActiveGroup; + return in_active_group_; } /** @@ -170,16 +165,16 @@ class Node { * \param system: the ExecutionSystem where the operations need to be added * \param context: reference to the CompositorContext */ - virtual void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const = 0; + virtual void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const = 0; - void setInstanceKey(bNodeInstanceKey instance_key) + void set_instance_key(bNodeInstanceKey instance_key) { - m_instanceKey = instance_key; + instance_key_ = instance_key; } - bNodeInstanceKey getInstanceKey() const + bNodeInstanceKey get_instance_key() const { - return m_instanceKey; + return instance_key_; } protected: @@ -188,19 +183,19 @@ class Node { * \note may only be called in an constructor * \param socket: the NodeInput to add */ - void addInputSocket(DataType datatype); - void addInputSocket(DataType datatype, bNodeSocket *socket); + void add_input_socket(DataType datatype); + void add_input_socket(DataType datatype, bNodeSocket *socket); /** * \brief add an NodeOutput to the collection of output-sockets * \note may only be called in an constructor * \param socket: the NodeOutput to add */ - void addOutputSocket(DataType datatype); - void addOutputSocket(DataType datatype, bNodeSocket *socket); + void add_output_socket(DataType datatype); + void add_output_socket(DataType datatype, bNodeSocket *socket); - bNodeSocket *getEditorInputSocket(int editorNodeInputSocketIndex); - bNodeSocket *getEditorOutputSocket(int editorNodeOutputSocketIndex); + bNodeSocket *get_editor_input_socket(int editor_node_input_socket_index); + bNodeSocket *get_editor_output_socket(int editor_node_output_socket_index); }; /** @@ -209,46 +204,46 @@ class Node { */ class NodeInput { private: - Node *m_node; - bNodeSocket *m_editorSocket; + Node *node_; + bNodeSocket *editor_socket_; - DataType m_datatype; + DataType datatype_; /** * \brief link connected to this NodeInput. * An input socket can only have a single link */ - NodeOutput *m_link; + NodeOutput *link_; public: NodeInput(Node *node, bNodeSocket *b_socket, DataType datatype); - Node *getNode() const + Node *get_node() const { - return this->m_node; + return node_; } - DataType getDataType() const + DataType get_data_type() const { - return m_datatype; + return datatype_; } - bNodeSocket *getbNodeSocket() const + bNodeSocket *get_bnode_socket() const { - return this->m_editorSocket; + return editor_socket_; } - void setLink(NodeOutput *link); - bool isLinked() const + void set_link(NodeOutput *link); + bool is_linked() const { - return m_link; + return link_; } - NodeOutput *getLink() + NodeOutput *get_link() { - return m_link; + return link_; } - float getEditorValueFloat() const; - void getEditorValueColor(float *value) const; - void getEditorValueVector(float *value) const; + float get_editor_value_float() const; + void get_editor_value_color(float *value) const; + void get_editor_value_vector(float *value) const; }; /** @@ -257,30 +252,30 @@ class NodeInput { */ class NodeOutput { private: - Node *m_node; - bNodeSocket *m_editorSocket; + Node *node_; + bNodeSocket *editor_socket_; - DataType m_datatype; + DataType datatype_; public: NodeOutput(Node *node, bNodeSocket *b_socket, DataType datatype); - Node *getNode() const + Node *get_node() const { - return this->m_node; + return node_; } - DataType getDataType() const + DataType get_data_type() const { - return m_datatype; + return datatype_; } - bNodeSocket *getbNodeSocket() const + bNodeSocket *get_bnode_socket() const { - return this->m_editorSocket; + return editor_socket_; } - float getEditorValueFloat(); - void getEditorValueColor(float *value); - void getEditorValueVector(float *value); + float get_editor_value_float(); + void get_editor_value_color(float *value); + void get_editor_value_vector(float *value); }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeConverter.cc b/source/blender/compositor/intern/COM_NodeConverter.cc index 49a2e7988c4..93ec559533b 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.cc +++ b/source/blender/compositor/intern/COM_NodeConverter.cc @@ -18,9 +18,7 @@ #include "BLI_utildefines.h" -#include "COM_Debug.h" - -#include "COM_NodeOperation.h" +#include "COM_Node.h" #include "COM_NodeOperationBuilder.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" @@ -31,136 +29,137 @@ namespace blender::compositor { -NodeConverter::NodeConverter(NodeOperationBuilder *builder) : m_builder(builder) +NodeConverter::NodeConverter(NodeOperationBuilder *builder) : builder_(builder) { } -void NodeConverter::addOperation(NodeOperation *operation) +void NodeConverter::add_operation(NodeOperation *operation) { - m_builder->addOperation(operation); + builder_->add_operation(operation); } -void NodeConverter::mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket) +void NodeConverter::map_input_socket(NodeInput *node_socket, NodeOperationInput *operation_socket) { - m_builder->mapInputSocket(node_socket, operation_socket); + builder_->map_input_socket(node_socket, operation_socket); } -void NodeConverter::mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket) +void NodeConverter::map_output_socket(NodeOutput *node_socket, + NodeOperationOutput *operation_socket) { - m_builder->mapOutputSocket(node_socket, operation_socket); + builder_->map_output_socket(node_socket, operation_socket); } -void NodeConverter::addLink(NodeOperationOutput *from, NodeOperationInput *to) +void NodeConverter::add_link(NodeOperationOutput *from, NodeOperationInput *to) { - m_builder->addLink(from, to); + builder_->add_link(from, to); } -void NodeConverter::addPreview(NodeOperationOutput *output) +void NodeConverter::add_preview(NodeOperationOutput *output) { - m_builder->addPreview(output); + builder_->add_preview(output); } -void NodeConverter::addNodeInputPreview(NodeInput *input) +void NodeConverter::add_node_input_preview(NodeInput *input) { - m_builder->addNodeInputPreview(input); + builder_->add_node_input_preview(input); } -NodeOperation *NodeConverter::setInvalidOutput(NodeOutput *output) +NodeOperation *NodeConverter::set_invalid_output(NodeOutput *output) { /* this is a really bad situation - bring on the pink! - so artists know this is bad */ const float warning_color[4] = {1.0f, 0.0f, 1.0f, 1.0f}; SetColorOperation *operation = new SetColorOperation(); - operation->setChannels(warning_color); + operation->set_channels(warning_color); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); return operation; } -NodeOperationOutput *NodeConverter::addInputProxy(NodeInput *input, bool use_conversion) +NodeOperationOutput *NodeConverter::add_input_proxy(NodeInput *input, bool use_conversion) { - SocketProxyOperation *proxy = new SocketProxyOperation(input->getDataType(), use_conversion); - m_builder->addOperation(proxy); + SocketProxyOperation *proxy = new SocketProxyOperation(input->get_data_type(), use_conversion); + builder_->add_operation(proxy); - m_builder->mapInputSocket(input, proxy->getInputSocket(0)); + builder_->map_input_socket(input, proxy->get_input_socket(0)); - return proxy->getOutputSocket(); + return proxy->get_output_socket(); } -NodeOperationInput *NodeConverter::addOutputProxy(NodeOutput *output, bool use_conversion) +NodeOperationInput *NodeConverter::add_output_proxy(NodeOutput *output, bool use_conversion) { - SocketProxyOperation *proxy = new SocketProxyOperation(output->getDataType(), use_conversion); - m_builder->addOperation(proxy); + SocketProxyOperation *proxy = new SocketProxyOperation(output->get_data_type(), use_conversion); + builder_->add_operation(proxy); - m_builder->mapOutputSocket(output, proxy->getOutputSocket()); + builder_->map_output_socket(output, proxy->get_output_socket()); - return proxy->getInputSocket(0); + return proxy->get_input_socket(0); } -void NodeConverter::addInputValue(NodeOperationInput *input, float value) +void NodeConverter::add_input_value(NodeOperationInput *input, float value) { SetValueOperation *operation = new SetValueOperation(); - operation->setValue(value); + operation->set_value(value); - m_builder->addOperation(operation); - m_builder->addLink(operation->getOutputSocket(), input); + builder_->add_operation(operation); + builder_->add_link(operation->get_output_socket(), input); } -void NodeConverter::addInputColor(NodeOperationInput *input, const float value[4]) +void NodeConverter::add_input_color(NodeOperationInput *input, const float value[4]) { SetColorOperation *operation = new SetColorOperation(); - operation->setChannels(value); + operation->set_channels(value); - m_builder->addOperation(operation); - m_builder->addLink(operation->getOutputSocket(), input); + builder_->add_operation(operation); + builder_->add_link(operation->get_output_socket(), input); } -void NodeConverter::addInputVector(NodeOperationInput *input, const float value[3]) +void NodeConverter::add_input_vector(NodeOperationInput *input, const float value[3]) { SetVectorOperation *operation = new SetVectorOperation(); - operation->setVector(value); + operation->set_vector(value); - m_builder->addOperation(operation); - m_builder->addLink(operation->getOutputSocket(), input); + builder_->add_operation(operation); + builder_->add_link(operation->get_output_socket(), input); } -void NodeConverter::addOutputValue(NodeOutput *output, float value) +void NodeConverter::add_output_value(NodeOutput *output, float value) { SetValueOperation *operation = new SetValueOperation(); - operation->setValue(value); + operation->set_value(value); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); } -void NodeConverter::addOutputColor(NodeOutput *output, const float value[4]) +void NodeConverter::add_output_color(NodeOutput *output, const float value[4]) { SetColorOperation *operation = new SetColorOperation(); - operation->setChannels(value); + operation->set_channels(value); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); } -void NodeConverter::addOutputVector(NodeOutput *output, const float value[3]) +void NodeConverter::add_output_vector(NodeOutput *output, const float value[3]) { SetVectorOperation *operation = new SetVectorOperation(); - operation->setVector(value); + operation->set_vector(value); - m_builder->addOperation(operation); - m_builder->mapOutputSocket(output, operation->getOutputSocket()); + builder_->add_operation(operation); + builder_->map_output_socket(output, operation->get_output_socket()); } -void NodeConverter::registerViewer(ViewerOperation *viewer) +void NodeConverter::register_viewer(ViewerOperation *viewer) { - m_builder->registerViewer(viewer); + builder_->register_viewer(viewer); } ViewerOperation *NodeConverter::active_viewer() const { - return m_builder->active_viewer(); + return builder_->active_viewer(); } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_NodeConverter.h b/source/blender/compositor/intern/COM_NodeConverter.h index b3f03485249..9193a28a77f 100644 --- a/source/blender/compositor/intern/COM_NodeConverter.h +++ b/source/blender/compositor/intern/COM_NodeConverter.h @@ -36,7 +36,7 @@ class ViewerOperation; /** * Interface type for converting a \a Node into \a NodeOperation. - * This is passed to \a Node::convertToOperation methods and allows them + * This is passed to \a Node::convert_to_operation methods and allows them * to register any number of operations, create links between them, * and map original node sockets to their inputs or outputs. */ @@ -48,7 +48,7 @@ class NodeConverter { * Insert a new operation into the operations graph. * The operation must be created by the node. */ - void addOperation(NodeOperation *operation); + void add_operation(NodeOperation *operation); /** * Map input socket of the node to an operation socket. @@ -57,7 +57,7 @@ class NodeConverter { * * \note A \a Node input can be mapped to multiple \a NodeOperation inputs. */ - void mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket); + void map_input_socket(NodeInput *node_socket, NodeOperationInput *operation_socket); /** * Map output socket of the node to an operation socket. * Links between nodes will then generate equivalent links between @@ -66,57 +66,57 @@ class NodeConverter { * \note A \a Node output can only be mapped to one \a NodeOperation output. * Any existing operation output mapping will be replaced. */ - void mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); + void map_output_socket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); /** * Create a proxy operation for a node input. * This operation will be removed later and replaced * by direct links between the connected operations. */ - NodeOperationOutput *addInputProxy(NodeInput *input, bool use_conversion); + NodeOperationOutput *add_input_proxy(NodeInput *input, bool use_conversion); /** * Create a proxy operation for a node output. * This operation will be removed later and replaced * by direct links between the connected operations. */ - NodeOperationInput *addOutputProxy(NodeOutput *output, bool use_conversion); + NodeOperationInput *add_output_proxy(NodeOutput *output, bool use_conversion); /** Define a constant input value. */ - void addInputValue(NodeOperationInput *input, float value); + void add_input_value(NodeOperationInput *input, float value); /** Define a constant input color. */ - void addInputColor(NodeOperationInput *input, const float value[4]); + void add_input_color(NodeOperationInput *input, const float value[4]); /** Define a constant input vector. */ - void addInputVector(NodeOperationInput *input, const float value[3]); + void add_input_vector(NodeOperationInput *input, const float value[3]); /** Define a constant output value. */ - void addOutputValue(NodeOutput *output, float value); + void add_output_value(NodeOutput *output, float value); /** Define a constant output color. */ - void addOutputColor(NodeOutput *output, const float value[4]); + void add_output_color(NodeOutput *output, const float value[4]); /** Define a constant output vector. */ - void addOutputVector(NodeOutput *output, const float value[3]); + void add_output_vector(NodeOutput *output, const float value[3]); /** Add an explicit link between two operations. */ - void addLink(NodeOperationOutput *from, NodeOperationInput *to); + void add_link(NodeOperationOutput *from, NodeOperationInput *to); /** Add a preview operation for a operation output. */ - void addPreview(NodeOperationOutput *output); + void add_preview(NodeOperationOutput *output); /** Add a preview operation for a node input. */ - void addNodeInputPreview(NodeInput *input); + void add_node_input_preview(NodeInput *input); /** * When a node has no valid data * \note missing image / group pointer, or missing renderlayer from EXR */ - NodeOperation *setInvalidOutput(NodeOutput *output); + NodeOperation *set_invalid_output(NodeOutput *output); /** Define a viewer operation as the active output, if possible */ - void registerViewer(ViewerOperation *viewer); + void register_viewer(ViewerOperation *viewer); /** The currently active viewer output operation */ ViewerOperation *active_viewer() const; private: /** The internal builder for storing the results of the graph construction. */ - NodeOperationBuilder *m_builder; + NodeOperationBuilder *builder_; #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeCompiler") diff --git a/source/blender/compositor/intern/COM_NodeGraph.cc b/source/blender/compositor/intern/COM_NodeGraph.cc index 1872fbcf656..64684b772d9 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.cc +++ b/source/blender/compositor/intern/COM_NodeGraph.cc @@ -18,17 +18,12 @@ #include -#include "BLI_listbase.h" -#include "BLI_utildefines.h" - #include "DNA_node_types.h" #include "BKE_node.h" -#include "COM_CompositorContext.h" #include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_Node.h" #include "COM_SocketProxyNode.h" #include "COM_NodeGraph.h" /* own include */ @@ -41,8 +36,8 @@ namespace blender::compositor { NodeGraph::~NodeGraph() { - while (m_nodes.size()) { - delete m_nodes.pop_last(); + while (nodes_.size()) { + delete nodes_.pop_last(); } } @@ -76,21 +71,21 @@ void NodeGraph::add_node(Node *node, bNodeInstanceKey key, bool is_active_group) { - node->setbNodeTree(b_ntree); - node->setInstanceKey(key); - node->setIsInActiveGroup(is_active_group); + node->set_bnodetree(b_ntree); + node->set_instance_key(key); + node->set_is_in_active_group(is_active_group); - m_nodes.append(node); + nodes_.append(node); DebugInfo::node_added(node); } -void NodeGraph::add_link(NodeOutput *fromSocket, NodeInput *toSocket) +void NodeGraph::add_link(NodeOutput *from_socket, NodeInput *to_socket) { - m_links.append(Link(fromSocket, toSocket)); + links_.append(Link(from_socket, to_socket)); /* register with the input */ - toSocket->setLink(fromSocket); + to_socket->set_link(from_socket); } void NodeGraph::add_bNodeTree(const CompositorContext &context, @@ -98,7 +93,7 @@ void NodeGraph::add_bNodeTree(const CompositorContext &context, bNodeTree *tree, bNodeInstanceKey parent_key) { - const bNodeTree *basetree = context.getbNodeTree(); + const bNodeTree *basetree = context.get_bnodetree(); /* Update viewers in the active edit-tree as well the base tree (for backdrop). */ bool is_active_group = (parent_key.value == basetree->active_viewer_key.value); @@ -109,7 +104,7 @@ void NodeGraph::add_bNodeTree(const CompositorContext &context, add_bNode(context, tree, node, key, is_active_group); } - NodeRange node_range(m_nodes.begin() + nodes_start, m_nodes.end()); + NodeRange node_range(nodes_.begin() + nodes_start, nodes_.end()); /* Add all node-links of the tree to the link list. */ for (bNodeLink *nodelink = (bNodeLink *)tree->links.first; nodelink; nodelink = nodelink->next) { add_bNodeLink(node_range, nodelink); @@ -129,7 +124,7 @@ void NodeGraph::add_bNode(const CompositorContext &context, } /* replace slow nodes with proxies for fast execution */ - if (context.isFastCalculation() && !COM_bnode_is_fast_node(*b_node)) { + if (context.is_fast_calculation() && !COM_bnode_is_fast_node(*b_node)) { add_proxies_skip(b_ntree, b_node, key, is_active_group); return; } @@ -154,8 +149,8 @@ NodeOutput *NodeGraph::find_output(const NodeRange &node_range, bNodeSocket *b_s { for (Vector::iterator it = node_range.first; it != node_range.second; ++it) { Node *node = *it; - for (NodeOutput *output : node->getOutputSockets()) { - if (output->getbNodeSocket() == b_socket) { + for (NodeOutput *output : node->get_output_sockets()) { + if (output->get_bnode_socket() == b_socket) { return output; } } @@ -185,8 +180,8 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink for (Vector::iterator it = node_range.first; it != node_range.second; ++it) { Node *node = *it; - for (NodeInput *input : node->getInputSockets()) { - if (input->getbNodeSocket() == b_nodelink->tosock && !input->isLinked()) { + for (NodeInput *input : node->get_input_sockets()) { + if (input->get_bnode_socket() == b_nodelink->tosock && !input->is_linked()) { add_link(output, input); } } @@ -263,7 +258,7 @@ void NodeGraph::add_proxies_group_outputs(const CompositorContext &context, b_sock_io = b_sock_io->next) { bNodeSocket *b_sock_group = find_b_node_output(b_node, b_sock_io->identifier); if (b_sock_group) { - if (context.isGroupnodeBufferEnabled() && + if (context.is_groupnode_buffer_enabled() && context.get_execution_model() == eExecutionModel::Tiled) { SocketBufferNode *buffer = new SocketBufferNode(b_node_io, b_sock_io, b_sock_group); add_node(buffer, b_group_tree, key, is_active_group); @@ -284,13 +279,13 @@ void NodeGraph::add_proxies_group(const CompositorContext &context, /* missing node group datablock can happen with library linking */ if (!b_group_tree) { - /* This error case its handled in convertToOperations() + /* This error case its handled in convert_to_operations() * so we don't get un-converted sockets. */ return; } /* use node list size before adding proxies, so they can be connected in add_bNodeTree */ - int nodes_start = m_nodes.size(); + int nodes_start = nodes_.size(); /* create proxy nodes for group input/output nodes */ for (bNode *b_node_io = (bNode *)b_group_tree->nodes.first; b_node_io; diff --git a/source/blender/compositor/intern/COM_NodeGraph.h b/source/blender/compositor/intern/COM_NodeGraph.h index dfcc6c2fcf9..476b99033c9 100644 --- a/source/blender/compositor/intern/COM_NodeGraph.h +++ b/source/blender/compositor/intern/COM_NodeGraph.h @@ -18,11 +18,6 @@ #pragma once -#include "BLI_vector.hh" - -#include -#include - #include "DNA_node_types.h" #ifdef WITH_CXX_GUARDEDALLOC @@ -52,19 +47,19 @@ class NodeGraph { }; private: - Vector m_nodes; - Vector m_links; + Vector nodes_; + Vector links_; public: ~NodeGraph(); const Vector &nodes() const { - return m_nodes; + return nodes_; } const Vector &links() const { - return m_links; + return links_; } void from_bNodeTree(const CompositorContext &context, bNodeTree *tree); @@ -76,7 +71,7 @@ class NodeGraph { static bNodeSocket *find_b_node_output(bNode *b_node, const char *identifier); void add_node(Node *node, bNodeTree *b_ntree, bNodeInstanceKey key, bool is_active_group); - void add_link(NodeOutput *fromSocket, NodeInput *toSocket); + void add_link(NodeOutput *from_socket, NodeInput *to_socket); void add_bNodeTree(const CompositorContext &context, int nodes_start, diff --git a/source/blender/compositor/intern/COM_NodeOperation.cc b/source/blender/compositor/intern/COM_NodeOperation.cc index 3bbd1b22d60..8a7ae1f4fcb 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.cc +++ b/source/blender/compositor/intern/COM_NodeOperation.cc @@ -17,13 +17,10 @@ */ #include -#include -#include #include "COM_BufferOperation.h" #include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" -#include "COM_defines.h" #include "COM_NodeOperation.h" /* own include */ @@ -35,10 +32,27 @@ namespace blender::compositor { NodeOperation::NodeOperation() { - this->m_resolutionInputSocketIndex = 0; - this->m_width = 0; - this->m_height = 0; - this->m_btree = nullptr; + canvas_input_index_ = 0; + canvas_ = COM_AREA_NONE; + btree_ = nullptr; +} + +/** Get constant value when operation is constant, otherwise return default_value. */ +float NodeOperation::get_constant_value_default(float default_value) +{ + BLI_assert(outputs_.size() > 0 && get_output_socket()->get_data_type() == DataType::Value); + return *get_constant_elem_default(&default_value); +} + +/** Get constant elem when operation is constant, otherwise return default_elem. */ +const float *NodeOperation::get_constant_elem_default(const float *default_elem) +{ + BLI_assert(outputs_.size() > 0); + if (get_flags().is_constant_operation) { + return static_cast(this)->get_constant_elem(); + } + + return default_elem; } /** @@ -48,7 +62,7 @@ NodeOperation::NodeOperation() */ std::optional NodeOperation::generate_hash() { - params_hash_ = get_default_hash_2(m_width, m_height); + params_hash_ = get_default_hash_2(canvas_.xmin, canvas_.xmax); /* Hash subclasses params. */ is_hash_output_params_implemented_ = true; @@ -57,22 +71,26 @@ std::optional NodeOperation::generate_hash() return std::nullopt; } - hash_param(getOutputSocket()->getDataType()); + hash_params(canvas_.ymin, canvas_.ymax); + if (outputs_.size() > 0) { + BLI_assert(outputs_.size() == 1); + hash_param(this->get_output_socket()->get_data_type()); + } NodeOperationHash hash; hash.params_hash_ = params_hash_; hash.parents_hash_ = 0; - for (NodeOperationInput &socket : m_inputs) { - if (!socket.isConnected()) { + for (NodeOperationInput &socket : inputs_) { + if (!socket.is_connected()) { continue; } - NodeOperation &input = socket.getLink()->getOperation(); + NodeOperation &input = socket.get_link()->get_operation(); const bool is_constant = input.get_flags().is_constant_operation; combine_hashes(hash.parents_hash_, get_default_hash(is_constant)); if (is_constant) { const float *elem = ((ConstantOperation *)&input)->get_constant_elem(); - const int num_channels = COM_data_type_num_channels(socket.getDataType()); + const int num_channels = COM_data_type_num_channels(socket.get_data_type()); for (const int i : IndexRange(num_channels)) { combine_hashes(hash.parents_hash_, get_default_hash(elem[i])); } @@ -88,145 +106,165 @@ std::optional NodeOperation::generate_hash() return hash; } -NodeOperationOutput *NodeOperation::getOutputSocket(unsigned int index) +NodeOperationOutput *NodeOperation::get_output_socket(unsigned int index) { - return &m_outputs[index]; + return &outputs_[index]; } -NodeOperationInput *NodeOperation::getInputSocket(unsigned int index) +NodeOperationInput *NodeOperation::get_input_socket(unsigned int index) { - return &m_inputs[index]; + return &inputs_[index]; } -void NodeOperation::addInputSocket(DataType datatype, ResizeMode resize_mode) +void NodeOperation::add_input_socket(DataType datatype, ResizeMode resize_mode) { - m_inputs.append(NodeOperationInput(this, datatype, resize_mode)); + inputs_.append(NodeOperationInput(this, datatype, resize_mode)); } -void NodeOperation::addOutputSocket(DataType datatype) +void NodeOperation::add_output_socket(DataType datatype) { - m_outputs.append(NodeOperationOutput(this, datatype)); + outputs_.append(NodeOperationOutput(this, datatype)); } -void NodeOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void NodeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - unsigned int used_resolution_index = 0; - if (m_resolutionInputSocketIndex == RESOLUTION_INPUT_ANY) { - for (NodeOperationInput &input : m_inputs) { - unsigned int any_resolution[2] = {0, 0}; - input.determineResolution(any_resolution, preferredResolution); - if (any_resolution[0] * any_resolution[1] > 0) { - resolution[0] = any_resolution[0]; - resolution[1] = any_resolution[1]; + unsigned int used_canvas_index = 0; + if (canvas_input_index_ == RESOLUTION_INPUT_ANY) { + for (NodeOperationInput &input : inputs_) { + rcti any_area = COM_AREA_NONE; + const bool determined = input.determine_canvas(preferred_area, any_area); + if (determined) { + r_area = any_area; break; } - used_resolution_index += 1; + used_canvas_index += 1; } } - else if (m_resolutionInputSocketIndex < m_inputs.size()) { - NodeOperationInput &input = m_inputs[m_resolutionInputSocketIndex]; - input.determineResolution(resolution, preferredResolution); - used_resolution_index = m_resolutionInputSocketIndex; + else if (canvas_input_index_ < inputs_.size()) { + NodeOperationInput &input = inputs_[canvas_input_index_]; + input.determine_canvas(preferred_area, r_area); + used_canvas_index = canvas_input_index_; } - if (modify_determined_resolution_fn_) { - modify_determined_resolution_fn_(resolution); + if (modify_determined_canvas_fn_) { + modify_determined_canvas_fn_(r_area); } - unsigned int temp2[2] = {resolution[0], resolution[1]}; - unsigned int temp[2]; - for (unsigned int index = 0; index < m_inputs.size(); index++) { - if (index == used_resolution_index) { + rcti unused_area; + const rcti &local_preferred_area = r_area; + for (unsigned int index = 0; index < inputs_.size(); index++) { + if (index == used_canvas_index) { continue; } - NodeOperationInput &input = m_inputs[index]; - if (input.isConnected()) { - input.determineResolution(temp, temp2); + NodeOperationInput &input = inputs_[index]; + if (input.is_connected()) { + input.determine_canvas(local_preferred_area, unused_area); } } } -void NodeOperation::setResolutionInputSocketIndex(unsigned int index) +void NodeOperation::set_canvas_input_index(unsigned int index) { - this->m_resolutionInputSocketIndex = index; + this->canvas_input_index_ = index; } void NodeOperation::init_data() { /* Pass. */ } -void NodeOperation::initExecution() +void NodeOperation::init_execution() { /* pass */ } -void NodeOperation::initMutex() +void NodeOperation::init_mutex() { - BLI_mutex_init(&this->m_mutex); + BLI_mutex_init(&mutex_); } -void NodeOperation::lockMutex() +void NodeOperation::lock_mutex() { - BLI_mutex_lock(&this->m_mutex); + BLI_mutex_lock(&mutex_); } -void NodeOperation::unlockMutex() +void NodeOperation::unlock_mutex() { - BLI_mutex_unlock(&this->m_mutex); + BLI_mutex_unlock(&mutex_); } -void NodeOperation::deinitMutex() +void NodeOperation::deinit_mutex() { - BLI_mutex_end(&this->m_mutex); + BLI_mutex_end(&mutex_); } -void NodeOperation::deinitExecution() +void NodeOperation::deinit_execution() { /* pass */ } -SocketReader *NodeOperation::getInputSocketReader(unsigned int inputSocketIndex) + +void NodeOperation::set_canvas(const rcti &canvas_area) { - return this->getInputSocket(inputSocketIndex)->getReader(); + canvas_ = canvas_area; + flags_.is_canvas_set = true; } -NodeOperation *NodeOperation::getInputOperation(unsigned int inputSocketIndex) +const rcti &NodeOperation::get_canvas() const { - NodeOperationInput *input = getInputSocket(inputSocketIndex); - if (input && input->isConnected()) { - return &input->getLink()->getOperation(); + return canvas_; +} + +/** + * Mainly used for re-determining canvas of constant operations in cases where preferred canvas + * depends on the constant element. + */ +void NodeOperation::unset_canvas() +{ + BLI_assert(inputs_.size() == 0); + flags_.is_canvas_set = false; +} + +SocketReader *NodeOperation::get_input_socket_reader(unsigned int index) +{ + return this->get_input_socket(index)->get_reader(); +} + +NodeOperation *NodeOperation::get_input_operation(int index) +{ + NodeOperationInput *input = get_input_socket(index); + if (input && input->is_connected()) { + return &input->get_link()->get_operation(); } return nullptr; } -bool NodeOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool NodeOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - if (m_inputs.size() == 0) { + if (inputs_.size() == 0) { BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax); return false; } - rcti tempOutput; + rcti temp_output; bool first = true; - for (int i = 0; i < getNumberOfInputSockets(); i++) { - NodeOperation *inputOperation = this->getInputOperation(i); - if (inputOperation && - inputOperation->determineDependingAreaOfInterest(input, readOperation, &tempOutput)) { + for (int i = 0; i < get_number_of_input_sockets(); i++) { + NodeOperation *input_operation = this->get_input_operation(i); + if (input_operation && input_operation->determine_depending_area_of_interest( + input, read_operation, &temp_output)) { if (first) { - output->xmin = tempOutput.xmin; - output->ymin = tempOutput.ymin; - output->xmax = tempOutput.xmax; - output->ymax = tempOutput.ymax; + output->xmin = temp_output.xmin; + output->ymin = temp_output.ymin; + output->xmax = temp_output.xmax; + output->ymax = temp_output.ymax; first = false; } else { - output->xmin = MIN2(output->xmin, tempOutput.xmin); - output->ymin = MIN2(output->ymin, tempOutput.ymin); - output->xmax = MAX2(output->xmax, tempOutput.xmax); - output->ymax = MAX2(output->ymax, tempOutput.ymax); + output->xmin = MIN2(output->xmin, temp_output.xmin); + output->ymin = MIN2(output->ymin, temp_output.ymin); + output->xmax = MAX2(output->xmax, temp_output.xmax); + output->ymax = MAX2(output->ymax, temp_output.ymax); } } } @@ -259,8 +297,8 @@ void NodeOperation::get_area_of_interest(const int input_idx, else { /* Non full-frame operations never implement this method. To ensure correctness assume * whole area is used. */ - NodeOperation *input_op = getInputOperation(input_idx); - BLI_rcti_init(&r_input_area, 0, input_op->getWidth(), 0, input_op->getHeight()); + NodeOperation *input_op = get_input_operation(input_idx); + r_input_area = input_op->get_canvas(); } } @@ -268,8 +306,8 @@ void NodeOperation::get_area_of_interest(NodeOperation *input_op, const rcti &output_area, rcti &r_input_area) { - for (int i = 0; i < getNumberOfInputSockets(); i++) { - if (input_op == getInputOperation(i)) { + for (int i = 0; i < get_number_of_input_sockets(); i++) { + if (input_op == get_input_operation(i)) { get_area_of_interest(i, output_area, r_input_area); return; } @@ -302,11 +340,11 @@ void NodeOperation::render_full_frame(MemoryBuffer *output_buf, Span areas, Span inputs_bufs) { - initExecution(); + init_execution(); for (const rcti &area : areas) { update_memory_buffer(output_buf, area, inputs_bufs); } - deinitExecution(); + deinit_execution(); } /** @@ -318,18 +356,18 @@ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, { Vector orig_input_links = replace_inputs_with_buffers(inputs_bufs); - initExecution(); - const bool is_output_operation = getNumberOfOutputSockets() == 0; + init_execution(); + const bool is_output_operation = get_number_of_output_sockets() == 0; if (!is_output_operation && output_buf->is_a_single_elem()) { float *output_elem = output_buf->get_elem(0, 0); - readSampled(output_elem, 0, 0, PixelSampler::Nearest); + read_sampled(output_elem, 0, 0, PixelSampler::Nearest); } else { for (const rcti &rect : areas) { exec_system_->execute_work(rect, [=](const rcti &split_rect) { rcti tile_rect = split_rect; if (is_output_operation) { - executeRegion(&tile_rect, 0); + execute_region(&tile_rect, 0); } else { render_tile(output_buf, &tile_rect); @@ -337,7 +375,7 @@ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, }); } } - deinitExecution(); + deinit_execution(); remove_buffers_and_restore_original_inputs(orig_input_links); } @@ -345,7 +383,7 @@ void NodeOperation::render_full_frame_fallback(MemoryBuffer *output_buf, void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) { const bool is_complex = get_flags().complex; - void *tile_data = is_complex ? initializeTileData(tile_rect) : nullptr; + void *tile_data = is_complex ? initialize_tile_data(tile_rect) : nullptr; const int elem_stride = output_buf->elem_stride; for (int y = tile_rect->ymin; y < tile_rect->ymax; y++) { float *output_elem = output_buf->get_elem(tile_rect->xmin, y); @@ -357,13 +395,13 @@ void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) } else { for (int x = tile_rect->xmin; x < tile_rect->xmax; x++) { - readSampled(output_elem, x, y, PixelSampler::Nearest); + read_sampled(output_elem, x, y, PixelSampler::Nearest); output_elem += elem_stride; } } } if (tile_data) { - deinitializeTileData(tile_rect, tile_data); + deinitialize_tile_data(tile_rect, tile_data); } } @@ -373,14 +411,15 @@ void NodeOperation::render_tile(MemoryBuffer *output_buf, rcti *tile_rect) Vector NodeOperation::replace_inputs_with_buffers( Span inputs_bufs) { - BLI_assert(inputs_bufs.size() == getNumberOfInputSockets()); + BLI_assert(inputs_bufs.size() == get_number_of_input_sockets()); Vector orig_links(inputs_bufs.size()); for (int i = 0; i < inputs_bufs.size(); i++) { - NodeOperationInput *input_socket = getInputSocket(i); - BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], input_socket->getDataType()); - orig_links[i] = input_socket->getLink(); - input_socket->setLink(buffer_op->getOutputSocket()); - buffer_op->initExecution(); + NodeOperationInput *input_socket = get_input_socket(i); + BufferOperation *buffer_op = new BufferOperation(inputs_bufs[i], + input_socket->get_data_type()); + orig_links[i] = input_socket->get_link(); + input_socket->set_link(buffer_op->get_output_socket()); + buffer_op->init_execution(); } return orig_links; } @@ -388,14 +427,14 @@ Vector NodeOperation::replace_inputs_with_buffers( void NodeOperation::remove_buffers_and_restore_original_inputs( Span original_inputs_links) { - BLI_assert(original_inputs_links.size() == getNumberOfInputSockets()); + BLI_assert(original_inputs_links.size() == get_number_of_input_sockets()); for (int i = 0; i < original_inputs_links.size(); i++) { NodeOperation *buffer_op = get_input_operation(i); BLI_assert(buffer_op != nullptr); BLI_assert(typeid(*buffer_op) == typeid(BufferOperation)); - buffer_op->deinitExecution(); - NodeOperationInput *input_socket = getInputSocket(i); - input_socket->setLink(original_inputs_links[i]); + buffer_op->deinit_execution(); + NodeOperationInput *input_socket = get_input_socket(i); + input_socket->set_link(original_inputs_links[i]); delete buffer_op; } } @@ -406,26 +445,32 @@ void NodeOperation::remove_buffers_and_restore_original_inputs( **** OpInput **** *****************/ -NodeOperationInput::NodeOperationInput(NodeOperation *op, DataType datatype, ResizeMode resizeMode) - : m_operation(op), m_datatype(datatype), m_resizeMode(resizeMode), m_link(nullptr) +NodeOperationInput::NodeOperationInput(NodeOperation *op, + DataType datatype, + ResizeMode resize_mode) + : operation_(op), datatype_(datatype), resize_mode_(resize_mode), link_(nullptr) { } -SocketReader *NodeOperationInput::getReader() +SocketReader *NodeOperationInput::get_reader() { - if (isConnected()) { - return &m_link->getOperation(); + if (is_connected()) { + return &link_->get_operation(); } return nullptr; } -void NodeOperationInput::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +/** + * \return Whether canvas area could be determined. + */ +bool NodeOperationInput::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (m_link) { - m_link->determineResolution(resolution, preferredResolution); + if (link_) { + link_->determine_canvas(preferred_area, r_area); + return !BLI_rcti_is_empty(&r_area); } + return false; } /****************** @@ -433,22 +478,20 @@ void NodeOperationInput::determineResolution(unsigned int resolution[2], ******************/ NodeOperationOutput::NodeOperationOutput(NodeOperation *op, DataType datatype) - : m_operation(op), m_datatype(datatype) + : operation_(op), datatype_(datatype) { } -void NodeOperationOutput::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void NodeOperationOutput::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation &operation = getOperation(); - if (operation.get_flags().is_resolution_set) { - resolution[0] = operation.getWidth(); - resolution[1] = operation.getHeight(); + NodeOperation &operation = get_operation(); + if (operation.get_flags().is_canvas_set) { + r_area = operation.get_canvas(); } else { - operation.determineResolution(resolution, preferredResolution); - if (resolution[0] > 0 && resolution[1] > 0) { - operation.setResolution(resolution); + operation.determine_canvas(preferred_area, r_area); + if (!BLI_rcti_is_empty(&r_area)) { + operation.set_canvas(r_area); } } } @@ -470,8 +513,8 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationFlags &node_operat if (node_operation_flags.use_viewer_border) { os << "view_border,"; } - if (node_operation_flags.is_resolution_set) { - os << "resolution_set,"; + if (node_operation_flags.is_canvas_set) { + os << "canvas_set,"; } if (node_operation_flags.is_set_operation) { os << "set_operation,"; @@ -518,9 +561,9 @@ std::ostream &operator<<(std::ostream &os, const NodeOperation &node_operation) os << ",flags={" << flags << "}"; if (flags.is_read_buffer_operation) { const ReadBufferOperation *read_operation = (const ReadBufferOperation *)&node_operation; - const MemoryProxy *proxy = read_operation->getMemoryProxy(); + const MemoryProxy *proxy = read_operation->get_memory_proxy(); if (proxy) { - const WriteBufferOperation *write_operation = proxy->getWriteBufferOperation(); + const WriteBufferOperation *write_operation = proxy->get_write_buffer_operation(); if (write_operation) { os << ",write=" << (NodeOperation &)*write_operation; } diff --git a/source/blender/compositor/intern/COM_NodeOperation.h b/source/blender/compositor/intern/COM_NodeOperation.h index ef7cf319222..627fffb1ec7 100644 --- a/source/blender/compositor/intern/COM_NodeOperation.h +++ b/source/blender/compositor/intern/COM_NodeOperation.h @@ -18,32 +18,32 @@ #pragma once +#include #include -#include -#include #include "BLI_ghash.h" #include "BLI_hash.hh" -#include "BLI_math_color.h" -#include "BLI_math_vector.h" +#include "BLI_rect.h" +#include "BLI_span.hh" #include "BLI_threads.h" +#include "BLI_utildefines.h" #include "COM_Enums.h" #include "COM_MemoryBuffer.h" -#include "COM_MemoryProxy.h" #include "COM_MetaData.h" -#include "COM_Node.h" #include "clew.h" +#include "DNA_node_types.h" + namespace blender::compositor { class OpenCLDevice; class ReadBufferOperation; -class WriteBufferOperation; class ExecutionSystem; - class NodeOperation; +class NodeOperationOutput; + typedef NodeOperation SocketReader; /** @@ -62,9 +62,13 @@ enum class ResizeMode { /** \brief Center the input image to the center of the working area of the node, no resizing * occurs */ Center = NS_CR_CENTER, - /** \brief The bottom left of the input image is the bottom left of the working area of the node, - * no resizing occurs */ + /** No resizing or translation. */ None = NS_CR_NONE, + /** + * Input image is translated so that its bottom left matches the bottom left of the working area + * of the node, no resizing occurs. + */ + Align = 100, /** \brief Fit the width of the input image to the width of the working area of the node */ FitWidth = NS_CR_FIT_WIDTH, /** \brief Fit the height of the input image to the height of the working area of the node */ @@ -79,58 +83,58 @@ enum class ResizeMode { class NodeOperationInput { private: - NodeOperation *m_operation; + NodeOperation *operation_; /** Datatype of this socket. Is used for automatically data transformation. * \section data-conversion */ - DataType m_datatype; + DataType datatype_; /** Resize mode of this socket */ - ResizeMode m_resizeMode; + ResizeMode resize_mode_; /** Connected output */ - NodeOperationOutput *m_link; + NodeOperationOutput *link_; public: NodeOperationInput(NodeOperation *op, DataType datatype, - ResizeMode resizeMode = ResizeMode::Center); + ResizeMode resize_mode = ResizeMode::Center); - NodeOperation &getOperation() const + NodeOperation &get_operation() const { - return *m_operation; + return *operation_; } - DataType getDataType() const + DataType get_data_type() const { - return m_datatype; + return datatype_; } - void setLink(NodeOperationOutput *link) + void set_link(NodeOperationOutput *link) { - m_link = link; + link_ = link; } - NodeOperationOutput *getLink() const + NodeOperationOutput *get_link() const { - return m_link; + return link_; } - bool isConnected() const + bool is_connected() const { - return m_link; + return link_; } - void setResizeMode(ResizeMode resizeMode) + void set_resize_mode(ResizeMode resize_mode) { - this->m_resizeMode = resizeMode; + resize_mode_ = resize_mode; } - ResizeMode getResizeMode() const + ResizeMode get_resize_mode() const { - return this->m_resizeMode; + return resize_mode_; } - SocketReader *getReader(); + SocketReader *get_reader(); - void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); + bool determine_canvas(const rcti &preferred_area, rcti &r_area); #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation") @@ -139,31 +143,26 @@ class NodeOperationInput { class NodeOperationOutput { private: - NodeOperation *m_operation; + NodeOperation *operation_; /** Datatype of this socket. Is used for automatically data transformation. * \section data-conversion */ - DataType m_datatype; + DataType datatype_; public: NodeOperationOutput(NodeOperation *op, DataType datatype); - NodeOperation &getOperation() const + NodeOperation &get_operation() const { - return *m_operation; + return *operation_; } - DataType getDataType() const + DataType get_data_type() const { - return m_datatype; + return datatype_; } - /** - * \brief determine the resolution of this data going through this socket - * \param resolution: the result of this operation - * \param preferredResolution: the preferable resolution as no resolution could be determined - */ - void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); + void determine_canvas(const rcti &preferred_area, rcti &r_area); #ifdef WITH_CXX_GUARDEDALLOC MEM_CXX_CLASS_ALLOC_FUNCS("COM:NodeOperation") @@ -211,9 +210,9 @@ struct NodeOperationFlags { bool use_viewer_border : 1; /** - * Is the resolution of the operation set. + * Is the canvas of the operation set. */ - bool is_resolution_set : 1; + bool is_canvas_set : 1; /** * Is this a set operation (value, color, vector). @@ -257,7 +256,7 @@ struct NodeOperationFlags { open_cl = false; use_render_border = false; use_viewer_border = false; - is_resolution_set = false; + is_canvas_set = false; is_set_operation = false; is_read_buffer_operation = false; is_write_buffer_operation = false; @@ -315,36 +314,36 @@ struct NodeOperationHash { */ class NodeOperation { private: - int m_id; - std::string m_name; - Vector m_inputs; - Vector m_outputs; + int id_; + std::string name_; + Vector inputs_; + Vector outputs_; size_t params_hash_; bool is_hash_output_params_implemented_; /** - * \brief the index of the input socket that will be used to determine the resolution + * \brief the index of the input socket that will be used to determine the canvas */ - unsigned int m_resolutionInputSocketIndex; + unsigned int canvas_input_index_; - std::function modify_determined_resolution_fn_; + std::function modify_determined_canvas_fn_; /** * \brief mutex reference for very special node initializations * \note only use when you really know what you are doing. * this mutex is used to share data among chunks in the same operation * \see TonemapOperation for an example of usage - * \see NodeOperation.initMutex initializes this mutex - * \see NodeOperation.deinitMutex deinitializes this mutex - * \see NodeOperation.getMutex retrieve a pointer to this mutex. + * \see NodeOperation.init_mutex initializes this mutex + * \see NodeOperation.deinit_mutex deinitializes this mutex + * \see NodeOperation.get_mutex retrieve a pointer to this mutex. */ - ThreadMutex m_mutex; + ThreadMutex mutex_; /** * \brief reference to the editing bNodeTree, used for break and update callback */ - const bNodeTree *m_btree; + const bNodeTree *btree_; protected: /** @@ -352,20 +351,12 @@ class NodeOperation { */ eExecutionModel execution_model_; - /** - * Width of the output of this operation. - */ - unsigned int m_width; - - /** - * Height of the output of this operation. - */ - unsigned int m_height; + rcti canvas_; /** * Flags how to evaluate this operation. */ - NodeOperationFlags flags; + NodeOperationFlags flags_; ExecutionSystem *exec_system_; @@ -374,67 +365,53 @@ class NodeOperation { { } - void set_execution_model(const eExecutionModel model) - { - execution_model_ = model; - } - void set_name(const std::string name) { - m_name = name; + name_ = name; } const std::string get_name() const { - return m_name; + return name_; } void set_id(const int id) { - m_id = id; + id_ = id; } const int get_id() const { - return m_id; + return id_; } + float get_constant_value_default(float default_value); + const float *get_constant_elem_default(const float *default_elem); + const NodeOperationFlags get_flags() const { - return flags; + return flags_; } std::optional generate_hash(); - unsigned int getNumberOfInputSockets() const + unsigned int get_number_of_input_sockets() const { - return m_inputs.size(); + return inputs_.size(); } - unsigned int getNumberOfOutputSockets() const + unsigned int get_number_of_output_sockets() const { - return m_outputs.size(); + return outputs_.size(); } - NodeOperationOutput *getOutputSocket(unsigned int index = 0); - NodeOperationInput *getInputSocket(unsigned int index); + NodeOperationOutput *get_output_socket(unsigned int index = 0); + NodeOperationInput *get_input_socket(unsigned int index); - NodeOperation *get_input_operation(int index) - { - /* TODO: Rename protected getInputOperation to get_input_operation and make it public replacing - * this method. */ - return getInputOperation(index); - } + NodeOperation *get_input_operation(int index); + + virtual void determine_canvas(const rcti &preferred_area, rcti &r_area); /** - * \brief determine the resolution of this node - * \note this method will not set the resolution, this is the responsibility of the caller - * \param resolution: the result of this operation - * \param preferredResolution: the preferable resolution as no resolution could be determined - */ - virtual void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]); - - /** - * \brief isOutputOperation determines whether this operation is an output of the + * \brief is_output_operation determines whether this operation is an output of the * ExecutionSystem during rendering or editing. * * Default behavior if not overridden, this operation will not be evaluated as being an output @@ -448,14 +425,19 @@ class NodeOperation { * * \return bool the result of this method */ - virtual bool isOutputOperation(bool /*rendering*/) const + virtual bool is_output_operation(bool /*rendering*/) const { return false; } - void setbNodeTree(const bNodeTree *tree) + void set_execution_model(const eExecutionModel model) { - this->m_btree = tree; + execution_model_ = model; + } + + void set_bnodetree(const bNodeTree *tree) + { + btree_ = tree; } void set_execution_system(ExecutionSystem *system) @@ -465,20 +447,20 @@ class NodeOperation { /** * Initializes operation data needed after operations are linked and resolutions determined. For - * rendering heap memory data use initExecution(). + * rendering heap memory data use init_execution(). */ virtual void init_data(); - virtual void initExecution(); + virtual void init_execution(); /** * \brief when a chunk is executed by a CPUDevice, this method is called * \ingroup execution * \param rect: the rectangle of the chunk (location and size) - * \param chunkNumber: the chunkNumber to be calculated - * \param memoryBuffers: all input MemoryBuffer's needed + * \param chunk_number: the chunk_number to be calculated + * \param memory_buffers: all input MemoryBuffer's needed */ - virtual void executeRegion(rcti * /*rect*/, unsigned int /*chunkNumber*/) + virtual void execute_region(rcti * /*rect*/, unsigned int /*chunk_number*/) { } @@ -490,15 +472,15 @@ class NodeOperation { * \param program: the OpenCL program containing all compositor kernels * \param queue: the OpenCL command queue of the device the chunk is executed on * \param rect: the rectangle of the chunk (location and size) - * \param chunkNumber: the chunkNumber to be calculated - * \param memoryBuffers: all input MemoryBuffer's needed - * \param outputBuffer: the outputbuffer to write to + * \param chunk_number: the chunk_number to be calculated + * \param memory_buffers: all input MemoryBuffer's needed + * \param output_buffer: the outputbuffer to write to */ - virtual void executeOpenCLRegion(OpenCLDevice * /*device*/, - rcti * /*rect*/, - unsigned int /*chunkNumber*/, - MemoryBuffer ** /*memoryBuffers*/, - MemoryBuffer * /*outputBuffer*/) + virtual void execute_opencl_region(OpenCLDevice * /*device*/, + rcti * /*rect*/, + unsigned int /*chunk_number*/, + MemoryBuffer ** /*memory_buffers*/, + MemoryBuffer * /*output_buffer*/) { } @@ -509,36 +491,27 @@ class NodeOperation { * \param context: the OpenCL context * \param program: the OpenCL program containing all compositor kernels * \param queue: the OpenCL command queue of the device the chunk is executed on - * \param outputMemoryBuffer: the allocated memory buffer in main CPU memory - * \param clOutputBuffer: the allocated memory buffer in OpenCLDevice memory - * \param inputMemoryBuffers: all input MemoryBuffer's needed - * \param clMemToCleanUp: all created cl_mem references must be added to this list. + * \param output_memory_buffer: the allocated memory buffer in main CPU memory + * \param cl_output_buffer: the allocated memory buffer in OpenCLDevice memory + * \param input_memory_buffers: all input MemoryBuffer's needed + * \param cl_mem_to_clean_up: all created cl_mem references must be added to this list. * Framework will clean this after execution - * \param clKernelsToCleanUp: all created cl_kernel references must be added to this list. + * \param cl_kernels_to_clean_up: all created cl_kernel references must be added to this list. * Framework will clean this after execution */ - virtual void executeOpenCL(OpenCLDevice * /*device*/, - MemoryBuffer * /*outputMemoryBuffer*/, - cl_mem /*clOutputBuffer*/, - MemoryBuffer ** /*inputMemoryBuffers*/, - std::list * /*clMemToCleanUp*/, - std::list * /*clKernelsToCleanUp*/) + virtual void execute_opencl(OpenCLDevice * /*device*/, + MemoryBuffer * /*output_memory_buffer*/, + cl_mem /*cl_output_buffer*/, + MemoryBuffer ** /*input_memory_buffers*/, + std::list * /*cl_mem_to_clean_up*/, + std::list * /*cl_kernels_to_clean_up*/) { } - virtual void deinitExecution(); + virtual void deinit_execution(); - /** - * \brief set the resolution - * \param resolution: the resolution to set - */ - void setResolution(unsigned int resolution[2]) - { - if (!this->flags.is_resolution_set) { - this->m_width = resolution[0]; - this->m_height = resolution[1]; - this->flags.is_resolution_set = true; - } - } + void set_canvas(const rcti &canvas_area); + const rcti &get_canvas() const; + void unset_canvas(); /** * \brief is this operation the active viewer output @@ -547,28 +520,28 @@ class NodeOperation { * \return [true:false] * \see BaseViewerOperation */ - virtual bool isActiveViewerOutput() const + virtual bool is_active_viewer_output() const { return false; } - virtual bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output); + virtual bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output); /** - * \brief set the index of the input socket that will determine the resolution of this + * \brief set the index of the input socket that will determine the canvas of this * operation \param index: the index to set */ - void setResolutionInputSocketIndex(unsigned int index); + void set_canvas_input_index(unsigned int index); /** - * Set a custom function to modify determined resolution from main input just before setting it - * as preferred resolution for the other inputs. + * Set a custom function to modify determined canvas from main input just before setting it + * as preferred for the other inputs. */ - void set_determined_resolution_modifier(std::function fn) + void set_determined_canvas_modifier(std::function fn) { - modify_determined_resolution_fn_ = fn; + modify_determined_canvas_fn_ = fn; } /** @@ -576,58 +549,58 @@ class NodeOperation { * \note only applicable for output operations like ViewerOperation * \return eCompositorPriority */ - virtual eCompositorPriority getRenderPriority() const + virtual eCompositorPriority get_render_priority() const { return eCompositorPriority::Low; } - inline bool isBraked() const + inline bool is_braked() const { - return this->m_btree->test_break(this->m_btree->tbh); + return btree_->test_break(btree_->tbh); } - inline void updateDraw() + inline void update_draw() { - if (this->m_btree->update_draw) { - this->m_btree->update_draw(this->m_btree->udh); + if (btree_->update_draw) { + btree_->update_draw(btree_->udh); } } - unsigned int getWidth() const + unsigned int get_width() const { - return m_width; + return BLI_rcti_size_x(&get_canvas()); } - unsigned int getHeight() const + unsigned int get_height() const { - return m_height; + return BLI_rcti_size_y(&get_canvas()); } - inline void readSampled(float result[4], float x, float y, PixelSampler sampler) + inline void read_sampled(float result[4], float x, float y, PixelSampler sampler) { - executePixelSampled(result, x, y, sampler); + execute_pixel_sampled(result, x, y, sampler); } - inline void readFiltered(float result[4], float x, float y, float dx[2], float dy[2]) + inline void read_filtered(float result[4], float x, float y, float dx[2], float dy[2]) { - executePixelFiltered(result, x, y, dx, dy); + execute_pixel_filtered(result, x, y, dx, dy); } - inline void read(float result[4], int x, int y, void *chunkData) + inline void read(float result[4], int x, int y, void *chunk_data) { - executePixel(result, x, y, chunkData); + execute_pixel(result, x, y, chunk_data); } - virtual void *initializeTileData(rcti * /*rect*/) + virtual void *initialize_tile_data(rcti * /*rect*/) { return 0; } - virtual void deinitializeTileData(rcti * /*rect*/, void * /*data*/) + virtual void deinitialize_tile_data(rcti * /*rect*/, void * /*data*/) { } - virtual MemoryBuffer *getInputMemoryBuffer(MemoryBuffer ** /*memoryBuffers*/) + virtual MemoryBuffer *get_input_memory_buffer(MemoryBuffer ** /*memory_buffers*/) { return 0; } @@ -636,7 +609,7 @@ class NodeOperation { * Return the meta data associated with this branch. * * The return parameter holds an instance or is an nullptr. */ - virtual std::unique_ptr getMetaData() + virtual std::unique_ptr get_meta_data() { return std::unique_ptr(); } @@ -694,26 +667,27 @@ class NodeOperation { combine_hashes(params_hash_, get_default_hash_3(param1, param2, param3)); } - void addInputSocket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center); - void addOutputSocket(DataType datatype); + void add_input_socket(DataType datatype, ResizeMode resize_mode = ResizeMode::Center); + void add_output_socket(DataType datatype); - void setWidth(unsigned int width) + /* TODO(manzanilla): to be removed with tiled implementation. */ + void set_width(unsigned int width) { - this->m_width = width; - this->flags.is_resolution_set = true; + canvas_.xmax = canvas_.xmin + width; + flags_.is_canvas_set = true; } - void setHeight(unsigned int height) + void set_height(unsigned int height) { - this->m_height = height; - this->flags.is_resolution_set = true; + canvas_.ymax = canvas_.ymin + height; + flags_.is_canvas_set = true; } - SocketReader *getInputSocketReader(unsigned int inputSocketindex); - NodeOperation *getInputOperation(unsigned int inputSocketindex); - void deinitMutex(); - void initMutex(); - void lockMutex(); - void unlockMutex(); + SocketReader *get_input_socket_reader(unsigned int index); + + void deinit_mutex(); + void init_mutex(); + void lock_mutex(); + void unlock_mutex(); /** * \brief set whether this operation is complex @@ -721,9 +695,9 @@ class NodeOperation { * Complex operations are typically doing many reads to calculate the output of a single pixel. * Mostly Filter types (Blurs, Convolution, Defocus etc) need this to be set to true. */ - void setComplex(bool complex) + void set_complex(bool complex) { - this->flags.complex = complex; + flags_.complex = complex; } /** @@ -732,12 +706,12 @@ class NodeOperation { * \param result: is a float[4] array to store the result * \param x: the x-coordinate of the pixel to calculate in image space * \param y: the y-coordinate of the pixel to calculate in image space - * \param inputBuffers: chunks that can be read by their ReadBufferOperation. + * \param input_buffers: chunks that can be read by their ReadBufferOperation. */ - virtual void executePixelSampled(float /*output*/[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) + virtual void execute_pixel_sampled(float /*output*/[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { } @@ -747,12 +721,12 @@ class NodeOperation { * \param result: is a float[4] array to store the result * \param x: the x-coordinate of the pixel to calculate in image space * \param y: the y-coordinate of the pixel to calculate in image space - * \param inputBuffers: chunks that can be read by their ReadBufferOperation. - * \param chunkData: chunk specific data a during execution time. + * \param input_buffers: chunks that can be read by their ReadBufferOperation. + * \param chunk_data: chunk specific data a during execution time. */ - virtual void executePixel(float output[4], int x, int y, void * /*chunkData*/) + virtual void execute_pixel(float output[4], int x, int y, void * /*chunk_data*/) { - executePixelSampled(output, x, y, PixelSampler::Nearest); + execute_pixel_sampled(output, x, y, PixelSampler::Nearest); } /** @@ -763,9 +737,9 @@ class NodeOperation { * \param y: the y-coordinate of the pixel to calculate in image space * \param dx: * \param dy: - * \param inputBuffers: chunks that can be read by their ReadBufferOperation. + * \param input_buffers: chunks that can be read by their ReadBufferOperation. */ - virtual void executePixelFiltered( + virtual void execute_pixel_filtered( float /*output*/[4], float /*x*/, float /*y*/, float /*dx*/[2], float /*dy*/[2]) { } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc index b2cd76be2c3..5dc66fea670 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.cc +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.cc @@ -16,23 +16,19 @@ * Copyright 2013, Blender Foundation. */ +#include + #include "BLI_multi_value_map.hh" -#include "BLI_utildefines.h" #include "COM_Converter.h" #include "COM_Debug.h" -#include "COM_ExecutionSystem.h" -#include "COM_Node.h" -#include "COM_NodeConverter.h" -#include "COM_SocketProxyNode.h" -#include "COM_NodeOperation.h" +#include "COM_ExecutionGroup.h" #include "COM_PreviewOperation.h" #include "COM_ReadBufferOperation.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" -#include "COM_SocketProxyOperation.h" #include "COM_ViewerOperation.h" #include "COM_WriteBufferOperation.h" @@ -44,39 +40,39 @@ namespace blender::compositor { NodeOperationBuilder::NodeOperationBuilder(const CompositorContext *context, bNodeTree *b_nodetree, ExecutionSystem *system) - : m_context(context), exec_system_(system), m_current_node(nullptr), m_active_viewer(nullptr) + : context_(context), exec_system_(system), current_node_(nullptr), active_viewer_(nullptr) { - m_graph.from_bNodeTree(*context, b_nodetree); + graph_.from_bNodeTree(*context, b_nodetree); } -void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) +void NodeOperationBuilder::convert_to_operations(ExecutionSystem *system) { /* interface handle for nodes */ NodeConverter converter(this); - for (Node *node : m_graph.nodes()) { - m_current_node = node; + for (Node *node : graph_.nodes()) { + current_node_ = node; DebugInfo::node_to_operations(node); - node->convertToOperations(converter, *m_context); + node->convert_to_operations(converter, *context_); } - m_current_node = nullptr; + current_node_ = nullptr; /* The input map constructed by nodes maps operation inputs to node inputs. * Inverting yields a map of node inputs to all connected operation inputs, * so multiple operations can use the same node input. */ blender::MultiValueMap inverse_input_map; - for (Map::MutableItem item : m_input_map.items()) { + for (Map::MutableItem item : input_map_.items()) { inverse_input_map.add(item.value, item.key); } - for (const NodeGraph::Link &link : m_graph.links()) { + for (const NodeGraph::Link &link : graph_.links()) { NodeOutput *from = link.from; NodeInput *to = link.to; - NodeOperationOutput *op_from = m_output_map.lookup_default(from, nullptr); + NodeOperationOutput *op_from = output_map_.lookup_default(from, nullptr); const blender::Span op_to_list = inverse_input_map.lookup(to); if (!op_from || op_to_list.is_empty()) { @@ -90,7 +86,7 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) } for (NodeOperationInput *op_to : op_to_list) { - addLink(op_from, op_to); + add_link(op_from, op_to); } } @@ -100,122 +96,122 @@ void NodeOperationBuilder::convertToOperations(ExecutionSystem *system) add_datatype_conversions(); - if (m_context->get_execution_model() == eExecutionModel::FullFrame) { + if (context_->get_execution_model() == eExecutionModel::FullFrame) { save_graphviz("compositor_prior_folding"); ConstantFolder folder(*this); folder.fold_operations(); } - determineResolutions(); + determine_canvases(); save_graphviz("compositor_prior_merging"); merge_equal_operations(); - if (m_context->get_execution_model() == eExecutionModel::Tiled) { + if (context_->get_execution_model() == eExecutionModel::Tiled) { /* surround complex ops with read/write buffer */ add_complex_operation_buffers(); } /* links not available from here on */ - /* XXX make m_links a local variable to avoid confusion! */ - m_links.clear(); + /* XXX make links_ a local variable to avoid confusion! */ + links_.clear(); prune_operations(); /* ensure topological (link-based) order of nodes */ /*sort_operations();*/ /* not needed yet */ - if (m_context->get_execution_model() == eExecutionModel::Tiled) { + if (context_->get_execution_model() == eExecutionModel::Tiled) { /* create execution groups */ group_operations(); } /* transfer resulting operations to the system */ - system->set_operations(m_operations, m_groups); + system->set_operations(operations_, groups_); } -void NodeOperationBuilder::addOperation(NodeOperation *operation) +void NodeOperationBuilder::add_operation(NodeOperation *operation) { - operation->set_id(m_operations.size()); - m_operations.append(operation); - if (m_current_node) { - operation->set_name(m_current_node->getbNode()->name); + operation->set_id(operations_.size()); + operations_.append(operation); + if (current_node_) { + operation->set_name(current_node_->get_bnode()->name); } - operation->set_execution_model(m_context->get_execution_model()); + operation->set_execution_model(context_->get_execution_model()); operation->set_execution_system(exec_system_); } void NodeOperationBuilder::replace_operation_with_constant(NodeOperation *operation, ConstantOperation *constant_operation) { - BLI_assert(constant_operation->getNumberOfInputSockets() == 0); + BLI_assert(constant_operation->get_number_of_input_sockets() == 0); unlink_inputs_and_relink_outputs(operation, constant_operation); - addOperation(constant_operation); + add_operation(constant_operation); } void NodeOperationBuilder::unlink_inputs_and_relink_outputs(NodeOperation *unlinked_op, NodeOperation *linked_op) { int i = 0; - while (i < m_links.size()) { - Link &link = m_links[i]; - if (&link.to()->getOperation() == unlinked_op) { - link.to()->setLink(nullptr); - m_links.remove(i); + while (i < links_.size()) { + Link &link = links_[i]; + if (&link.to()->get_operation() == unlinked_op) { + link.to()->set_link(nullptr); + links_.remove(i); continue; } - if (&link.from()->getOperation() == unlinked_op) { - link.to()->setLink(linked_op->getOutputSocket()); - m_links[i] = Link(linked_op->getOutputSocket(), link.to()); + if (&link.from()->get_operation() == unlinked_op) { + link.to()->set_link(linked_op->get_output_socket()); + links_[i] = Link(linked_op->get_output_socket(), link.to()); } i++; } } -void NodeOperationBuilder::mapInputSocket(NodeInput *node_socket, - NodeOperationInput *operation_socket) +void NodeOperationBuilder::map_input_socket(NodeInput *node_socket, + NodeOperationInput *operation_socket) { - BLI_assert(m_current_node); - BLI_assert(node_socket->getNode() == m_current_node); + BLI_assert(current_node_); + BLI_assert(node_socket->get_node() == current_node_); /* NOTE: this maps operation sockets to node sockets. - * for resolving links the map will be inverted first in convertToOperations, + * for resolving links the map will be inverted first in convert_to_operations, * to get a list of links for each node input socket. */ - m_input_map.add_new(operation_socket, node_socket); + input_map_.add_new(operation_socket, node_socket); } -void NodeOperationBuilder::mapOutputSocket(NodeOutput *node_socket, - NodeOperationOutput *operation_socket) +void NodeOperationBuilder::map_output_socket(NodeOutput *node_socket, + NodeOperationOutput *operation_socket) { - BLI_assert(m_current_node); - BLI_assert(node_socket->getNode() == m_current_node); + BLI_assert(current_node_); + BLI_assert(node_socket->get_node() == current_node_); - m_output_map.add_new(node_socket, operation_socket); + output_map_.add_new(node_socket, operation_socket); } -void NodeOperationBuilder::addLink(NodeOperationOutput *from, NodeOperationInput *to) +void NodeOperationBuilder::add_link(NodeOperationOutput *from, NodeOperationInput *to) { - if (to->isConnected()) { + if (to->is_connected()) { return; } - m_links.append(Link(from, to)); + links_.append(Link(from, to)); /* register with the input */ - to->setLink(from); + to->set_link(from); } -void NodeOperationBuilder::removeInputLink(NodeOperationInput *to) +void NodeOperationBuilder::remove_input_link(NodeOperationInput *to) { int index = 0; - for (Link &link : m_links) { + for (Link &link : links_) { if (link.to() == to) { /* unregister with the input */ - to->setLink(nullptr); + to->set_link(nullptr); - m_links.remove(index); + links_.remove(index); return; } index++; @@ -224,69 +220,69 @@ void NodeOperationBuilder::removeInputLink(NodeOperationInput *to) PreviewOperation *NodeOperationBuilder::make_preview_operation() const { - BLI_assert(m_current_node); + BLI_assert(current_node_); - if (!(m_current_node->getbNode()->flag & NODE_PREVIEW)) { + if (!(current_node_->get_bnode()->flag & NODE_PREVIEW)) { return nullptr; } /* previews only in the active group */ - if (!m_current_node->isInActiveGroup()) { + if (!current_node_->is_in_active_group()) { return nullptr; } /* do not calculate previews of hidden nodes */ - if (m_current_node->getbNode()->flag & NODE_HIDDEN) { + if (current_node_->get_bnode()->flag & NODE_HIDDEN) { return nullptr; } - bNodeInstanceHash *previews = m_context->getPreviewHash(); + bNodeInstanceHash *previews = context_->get_preview_hash(); if (previews) { - PreviewOperation *operation = new PreviewOperation(m_context->getViewSettings(), - m_context->getDisplaySettings(), - m_current_node->getbNode()->preview_xsize, - m_current_node->getbNode()->preview_ysize); - operation->setbNodeTree(m_context->getbNodeTree()); - operation->verifyPreview(previews, m_current_node->getInstanceKey()); + PreviewOperation *operation = new PreviewOperation(context_->get_view_settings(), + context_->get_display_settings(), + current_node_->get_bnode()->preview_xsize, + current_node_->get_bnode()->preview_ysize); + operation->set_bnodetree(context_->get_bnodetree()); + operation->verify_preview(previews, current_node_->get_instance_key()); return operation; } return nullptr; } -void NodeOperationBuilder::addPreview(NodeOperationOutput *output) +void NodeOperationBuilder::add_preview(NodeOperationOutput *output) { PreviewOperation *operation = make_preview_operation(); if (operation) { - addOperation(operation); + add_operation(operation); - addLink(output, operation->getInputSocket(0)); + add_link(output, operation->get_input_socket(0)); } } -void NodeOperationBuilder::addNodeInputPreview(NodeInput *input) +void NodeOperationBuilder::add_node_input_preview(NodeInput *input) { PreviewOperation *operation = make_preview_operation(); if (operation) { - addOperation(operation); + add_operation(operation); - mapInputSocket(input, operation->getInputSocket(0)); + map_input_socket(input, operation->get_input_socket(0)); } } -void NodeOperationBuilder::registerViewer(ViewerOperation *viewer) +void NodeOperationBuilder::register_viewer(ViewerOperation *viewer) { - if (m_active_viewer) { - if (m_current_node->isInActiveGroup()) { + if (active_viewer_) { + if (current_node_->is_in_active_group()) { /* deactivate previous viewer */ - m_active_viewer->setActive(false); + active_viewer_->set_active(false); - m_active_viewer = viewer; - viewer->setActive(true); + active_viewer_ = viewer; + viewer->set_active(true); } } else { - if (m_current_node->getbNodeTree() == m_context->getbNodeTree()) { - m_active_viewer = viewer; - viewer->setActive(true); + if (current_node_->get_bnodetree() == context_->get_bnodetree()) { + active_viewer_ = viewer; + viewer->set_active(true); } } } @@ -298,27 +294,27 @@ void NodeOperationBuilder::registerViewer(ViewerOperation *viewer) void NodeOperationBuilder::add_datatype_conversions() { Vector convert_links; - for (const Link &link : m_links) { + for (const Link &link : links_) { /* proxy operations can skip data type conversion */ - NodeOperation *from_op = &link.from()->getOperation(); - NodeOperation *to_op = &link.to()->getOperation(); + NodeOperation *from_op = &link.from()->get_operation(); + NodeOperation *to_op = &link.to()->get_operation(); if (!(from_op->get_flags().use_datatype_conversion || to_op->get_flags().use_datatype_conversion)) { continue; } - if (link.from()->getDataType() != link.to()->getDataType()) { + if (link.from()->get_data_type() != link.to()->get_data_type()) { convert_links.append(link); } } for (const Link &link : convert_links) { NodeOperation *converter = COM_convert_data_type(*link.from(), *link.to()); if (converter) { - addOperation(converter); + add_operation(converter); - removeInputLink(link.to()); - addLink(link.from(), converter->getInputSocket(0)); - addLink(converter->getOutputSocket(0), link.to()); + remove_input_link(link.to()); + add_link(link.from(), converter->get_input_socket(0)); + add_link(converter->get_output_socket(0), link.to()); } } } @@ -326,69 +322,69 @@ void NodeOperationBuilder::add_datatype_conversions() void NodeOperationBuilder::add_operation_input_constants() { /* NOTE: unconnected inputs cached first to avoid modifying - * m_operations while iterating over it + * operations_ while iterating over it */ Vector pending_inputs; - for (NodeOperation *op : m_operations) { - for (int k = 0; k < op->getNumberOfInputSockets(); ++k) { - NodeOperationInput *input = op->getInputSocket(k); - if (!input->isConnected()) { + for (NodeOperation *op : operations_) { + for (int k = 0; k < op->get_number_of_input_sockets(); ++k) { + NodeOperationInput *input = op->get_input_socket(k); + if (!input->is_connected()) { pending_inputs.append(input); } } } for (NodeOperationInput *input : pending_inputs) { - add_input_constant_value(input, m_input_map.lookup_default(input, nullptr)); + add_input_constant_value(input, input_map_.lookup_default(input, nullptr)); } } void NodeOperationBuilder::add_input_constant_value(NodeOperationInput *input, const NodeInput *node_input) { - switch (input->getDataType()) { + switch (input->get_data_type()) { case DataType::Value: { float value; - if (node_input && node_input->getbNodeSocket()) { - value = node_input->getEditorValueFloat(); + if (node_input && node_input->get_bnode_socket()) { + value = node_input->get_editor_value_float(); } else { value = 0.0f; } SetValueOperation *op = new SetValueOperation(); - op->setValue(value); - addOperation(op); - addLink(op->getOutputSocket(), input); + op->set_value(value); + add_operation(op); + add_link(op->get_output_socket(), input); break; } case DataType::Color: { float value[4]; - if (node_input && node_input->getbNodeSocket()) { - node_input->getEditorValueColor(value); + if (node_input && node_input->get_bnode_socket()) { + node_input->get_editor_value_color(value); } else { zero_v4(value); } SetColorOperation *op = new SetColorOperation(); - op->setChannels(value); - addOperation(op); - addLink(op->getOutputSocket(), input); + op->set_channels(value); + add_operation(op); + add_link(op->get_output_socket(), input); break; } case DataType::Vector: { float value[3]; - if (node_input && node_input->getbNodeSocket()) { - node_input->getEditorValueVector(value); + if (node_input && node_input->get_bnode_socket()) { + node_input->get_editor_value_vector(value); } else { zero_v3(value); } SetVectorOperation *op = new SetVectorOperation(); - op->setVector(value); - addOperation(op); - addLink(op->getOutputSocket(), input); + op->set_vector(value); + add_operation(op); + add_link(op->get_output_socket(), input); break; } } @@ -397,10 +393,10 @@ void NodeOperationBuilder::add_input_constant_value(NodeOperationInput *input, void NodeOperationBuilder::resolve_proxies() { Vector proxy_links; - for (const Link &link : m_links) { + for (const Link &link : links_) { /* don't replace links from proxy to proxy, since we may need them for replacing others! */ - if (link.from()->getOperation().get_flags().is_proxy_operation && - !link.to()->getOperation().get_flags().is_proxy_operation) { + if (link.from()->get_operation().get_flags().is_proxy_operation && + !link.to()->get_operation().get_flags().is_proxy_operation) { proxy_links.append(link); } } @@ -410,54 +406,65 @@ void NodeOperationBuilder::resolve_proxies() NodeOperationOutput *from = link.from(); do { /* walk upstream bypassing the proxy operation */ - from = from->getOperation().getInputSocket(0)->getLink(); - } while (from && from->getOperation().get_flags().is_proxy_operation); + from = from->get_operation().get_input_socket(0)->get_link(); + } while (from && from->get_operation().get_flags().is_proxy_operation); - removeInputLink(to); + remove_input_link(to); /* we may not have a final proxy input link, * in that case it just gets dropped */ if (from) { - addLink(from, to); + add_link(from, to); } } } -void NodeOperationBuilder::determineResolutions() +void NodeOperationBuilder::determine_canvases() { - /* determine all resolutions of the operations (Width/Height) */ - for (NodeOperation *op : m_operations) { - if (op->isOutputOperation(m_context->isRendering()) && !op->get_flags().is_preview_operation) { - unsigned int resolution[2] = {0, 0}; - unsigned int preferredResolution[2] = {0, 0}; - op->determineResolution(resolution, preferredResolution); - op->setResolution(resolution); + /* Determine all canvas areas of the operations. */ + const rcti &preferred_area = COM_AREA_NONE; + for (NodeOperation *op : operations_) { + if (op->is_output_operation(context_->is_rendering()) && + !op->get_flags().is_preview_operation) { + rcti canvas = COM_AREA_NONE; + op->determine_canvas(preferred_area, canvas); + op->set_canvas(canvas); } } - for (NodeOperation *op : m_operations) { - if (op->isOutputOperation(m_context->isRendering()) && op->get_flags().is_preview_operation) { - unsigned int resolution[2] = {0, 0}; - unsigned int preferredResolution[2] = {0, 0}; - op->determineResolution(resolution, preferredResolution); - op->setResolution(resolution); + for (NodeOperation *op : operations_) { + if (op->is_output_operation(context_->is_rendering()) && + op->get_flags().is_preview_operation) { + rcti canvas = COM_AREA_NONE; + op->determine_canvas(preferred_area, canvas); + op->set_canvas(canvas); } } - /* add convert resolution operations when needed */ + /* Convert operation canvases when needed. */ { Vector convert_links; - for (const Link &link : m_links) { - if (link.to()->getResizeMode() != ResizeMode::None) { - NodeOperation &from_op = link.from()->getOperation(); - NodeOperation &to_op = link.to()->getOperation(); - if (from_op.getWidth() != to_op.getWidth() || from_op.getHeight() != to_op.getHeight()) { + for (const Link &link : links_) { + if (link.to()->get_resize_mode() != ResizeMode::None) { + const rcti &from_canvas = link.from()->get_operation().get_canvas(); + const rcti &to_canvas = link.to()->get_operation().get_canvas(); + + bool needs_conversion; + if (link.to()->get_resize_mode() == ResizeMode::Align) { + needs_conversion = from_canvas.xmin != to_canvas.xmin || + from_canvas.ymin != to_canvas.ymin; + } + else { + needs_conversion = !BLI_rcti_compare(&from_canvas, &to_canvas); + } + + if (needs_conversion) { convert_links.append(link); } } } for (const Link &link : convert_links) { - COM_convert_resolution(*this, link.from(), link.to()); + COM_convert_canvas(*this, link.from(), link.to()); } } } @@ -480,7 +487,7 @@ void NodeOperationBuilder::merge_equal_operations() bool check_for_next_merge = true; while (check_for_next_merge) { /* Re-generate hashes with any change. */ - Vector hashes = generate_hashes(m_operations); + Vector hashes = generate_hashes(operations_); /* Make hashes be consecutive when they are equal. */ std::sort(hashes.begin(), hashes.end()); @@ -502,7 +509,7 @@ void NodeOperationBuilder::merge_equal_operations() void NodeOperationBuilder::merge_equal_operations(NodeOperation *from, NodeOperation *into) { unlink_inputs_and_relink_outputs(from, into); - m_operations.remove_first_occurrence_and_reorder(from); + operations_.remove_first_occurrence_and_reorder(from); delete from; } @@ -510,7 +517,7 @@ Vector NodeOperationBuilder::cache_output_links( NodeOperationOutput *output) const { Vector inputs; - for (const Link &link : m_links) { + for (const Link &link : links_) { if (link.from() == output) { inputs.append(link.to()); } @@ -521,9 +528,9 @@ Vector NodeOperationBuilder::cache_output_links( WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation( NodeOperationOutput *output) const { - for (const Link &link : m_links) { + for (const Link &link : links_) { if (link.from() == output) { - NodeOperation &op = link.to()->getOperation(); + NodeOperation &op = link.to()->get_operation(); if (op.get_flags().is_write_buffer_operation) { return (WriteBufferOperation *)(&op); } @@ -535,39 +542,39 @@ WriteBufferOperation *NodeOperationBuilder::find_attached_write_buffer_operation void NodeOperationBuilder::add_input_buffers(NodeOperation * /*operation*/, NodeOperationInput *input) { - if (!input->isConnected()) { + if (!input->is_connected()) { return; } - NodeOperationOutput *output = input->getLink(); - if (output->getOperation().get_flags().is_read_buffer_operation) { + NodeOperationOutput *output = input->get_link(); + if (output->get_operation().get_flags().is_read_buffer_operation) { /* input is already buffered, no need to add another */ return; } /* this link will be replaced below */ - removeInputLink(input); + remove_input_link(input); /* check of other end already has write operation, otherwise add a new one */ WriteBufferOperation *writeoperation = find_attached_write_buffer_operation(output); if (!writeoperation) { - writeoperation = new WriteBufferOperation(output->getDataType()); - writeoperation->setbNodeTree(m_context->getbNodeTree()); - addOperation(writeoperation); + writeoperation = new WriteBufferOperation(output->get_data_type()); + writeoperation->set_bnodetree(context_->get_bnodetree()); + add_operation(writeoperation); - addLink(output, writeoperation->getInputSocket(0)); + add_link(output, writeoperation->get_input_socket(0)); - writeoperation->readResolutionFromInputSocket(); + writeoperation->read_resolution_from_input_socket(); } /* add readbuffer op for the input */ - ReadBufferOperation *readoperation = new ReadBufferOperation(output->getDataType()); - readoperation->setMemoryProxy(writeoperation->getMemoryProxy()); - this->addOperation(readoperation); + ReadBufferOperation *readoperation = new ReadBufferOperation(output->get_data_type()); + readoperation->set_memory_proxy(writeoperation->get_memory_proxy()); + this->add_operation(readoperation); - addLink(readoperation->getOutputSocket(), input); + add_link(readoperation->get_output_socket(), input); - readoperation->readResolutionFromWriteBuffer(); + readoperation->read_resolution_from_write_buffer(); } void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, @@ -579,54 +586,54 @@ void NodeOperationBuilder::add_output_buffers(NodeOperation *operation, return; } - WriteBufferOperation *writeOperation = nullptr; + WriteBufferOperation *write_operation = nullptr; for (NodeOperationInput *target : targets) { /* try to find existing write buffer operation */ - if (target->getOperation().get_flags().is_write_buffer_operation) { - BLI_assert(writeOperation == nullptr); /* there should only be one write op connected */ - writeOperation = (WriteBufferOperation *)(&target->getOperation()); + if (target->get_operation().get_flags().is_write_buffer_operation) { + BLI_assert(write_operation == nullptr); /* there should only be one write op connected */ + write_operation = (WriteBufferOperation *)(&target->get_operation()); } else { /* remove all links to other nodes */ - removeInputLink(target); + remove_input_link(target); } } /* if no write buffer operation exists yet, create a new one */ - if (!writeOperation) { - writeOperation = new WriteBufferOperation(operation->getOutputSocket()->getDataType()); - writeOperation->setbNodeTree(m_context->getbNodeTree()); - addOperation(writeOperation); + if (!write_operation) { + write_operation = new WriteBufferOperation(operation->get_output_socket()->get_data_type()); + write_operation->set_bnodetree(context_->get_bnodetree()); + add_operation(write_operation); - addLink(output, writeOperation->getInputSocket(0)); + add_link(output, write_operation->get_input_socket(0)); } - writeOperation->readResolutionFromInputSocket(); + write_operation->read_resolution_from_input_socket(); /* add readbuffer op for every former connected input */ for (NodeOperationInput *target : targets) { - if (&target->getOperation() == writeOperation) { + if (&target->get_operation() == write_operation) { continue; /* skip existing write op links */ } ReadBufferOperation *readoperation = new ReadBufferOperation( - operation->getOutputSocket()->getDataType()); - readoperation->setMemoryProxy(writeOperation->getMemoryProxy()); - addOperation(readoperation); + operation->get_output_socket()->get_data_type()); + readoperation->set_memory_proxy(write_operation->get_memory_proxy()); + add_operation(readoperation); - addLink(readoperation->getOutputSocket(), target); + add_link(readoperation->get_output_socket(), target); - readoperation->readResolutionFromWriteBuffer(); + readoperation->read_resolution_from_write_buffer(); } } void NodeOperationBuilder::add_complex_operation_buffers() { /* NOTE: complex ops and get cached here first, since adding operations - * will invalidate iterators over the main m_operations + * will invalidate iterators over the main operations_ */ Vector complex_ops; - for (NodeOperation *operation : m_operations) { + for (NodeOperation *operation : operations_) { if (operation->get_flags().complex) { complex_ops.append(operation); } @@ -635,12 +642,12 @@ void NodeOperationBuilder::add_complex_operation_buffers() for (NodeOperation *op : complex_ops) { DebugInfo::operation_read_write_buffer(op); - for (int index = 0; index < op->getNumberOfInputSockets(); index++) { - add_input_buffers(op, op->getInputSocket(index)); + for (int index = 0; index < op->get_number_of_input_sockets(); index++) { + add_input_buffers(op, op->get_input_socket(index)); } - for (int index = 0; index < op->getNumberOfOutputSockets(); index++) { - add_output_buffers(op, op->getOutputSocket(index)); + for (int index = 0; index < op->get_number_of_output_sockets(); index++) { + add_output_buffers(op, op->get_output_socket(index)); } } } @@ -654,34 +661,34 @@ static void find_reachable_operations_recursive(Tags &reachable, NodeOperation * } reachable.insert(op); - for (int i = 0; i < op->getNumberOfInputSockets(); i++) { - NodeOperationInput *input = op->getInputSocket(i); - if (input->isConnected()) { - find_reachable_operations_recursive(reachable, &input->getLink()->getOperation()); + for (int i = 0; i < op->get_number_of_input_sockets(); i++) { + NodeOperationInput *input = op->get_input_socket(i); + if (input->is_connected()) { + find_reachable_operations_recursive(reachable, &input->get_link()->get_operation()); } } /* associated write-buffer operations are executed as well */ if (op->get_flags().is_read_buffer_operation) { ReadBufferOperation *read_op = (ReadBufferOperation *)op; - MemoryProxy *memproxy = read_op->getMemoryProxy(); - find_reachable_operations_recursive(reachable, memproxy->getWriteBufferOperation()); + MemoryProxy *memproxy = read_op->get_memory_proxy(); + find_reachable_operations_recursive(reachable, memproxy->get_write_buffer_operation()); } } void NodeOperationBuilder::prune_operations() { Tags reachable; - for (NodeOperation *op : m_operations) { + for (NodeOperation *op : operations_) { /* output operations are primary executed operations */ - if (op->isOutputOperation(m_context->isRendering())) { + if (op->is_output_operation(context_->is_rendering())) { find_reachable_operations_recursive(reachable, op); } } /* delete unreachable operations */ Vector reachable_ops; - for (NodeOperation *op : m_operations) { + for (NodeOperation *op : operations_) { if (reachable.find(op) != reachable.end()) { reachable_ops.append(op); } @@ -690,7 +697,7 @@ void NodeOperationBuilder::prune_operations() } } /* finally replace the operations list with the pruned list */ - m_operations = reachable_ops; + operations_ = reachable_ops; } /* topological (depth-first) sorting of operations */ @@ -703,10 +710,10 @@ static void sort_operations_recursive(Vector &sorted, } visited.insert(op); - for (int i = 0; i < op->getNumberOfInputSockets(); i++) { - NodeOperationInput *input = op->getInputSocket(i); - if (input->isConnected()) { - sort_operations_recursive(sorted, visited, &input->getLink()->getOperation()); + for (int i = 0; i < op->get_number_of_input_sockets(); i++) { + NodeOperationInput *input = op->get_input_socket(i); + if (input->is_connected()) { + sort_operations_recursive(sorted, visited, &input->get_link()->get_operation()); } } @@ -716,14 +723,14 @@ static void sort_operations_recursive(Vector &sorted, void NodeOperationBuilder::sort_operations() { Vector sorted; - sorted.reserve(m_operations.size()); + sorted.reserve(operations_.size()); Tags visited; - for (NodeOperation *operation : m_operations) { + for (NodeOperation *operation : operations_) { sort_operations_recursive(sorted, visited, operation); } - m_operations = sorted; + operations_ = sorted; } static void add_group_operations_recursive(Tags &visited, NodeOperation *op, ExecutionGroup *group) @@ -733,23 +740,23 @@ static void add_group_operations_recursive(Tags &visited, NodeOperation *op, Exe } visited.insert(op); - if (!group->addOperation(op)) { + if (!group->add_operation(op)) { return; } /* add all eligible input ops to the group */ - for (int i = 0; i < op->getNumberOfInputSockets(); i++) { - NodeOperationInput *input = op->getInputSocket(i); - if (input->isConnected()) { - add_group_operations_recursive(visited, &input->getLink()->getOperation(), group); + for (int i = 0; i < op->get_number_of_input_sockets(); i++) { + NodeOperationInput *input = op->get_input_socket(i); + if (input->is_connected()) { + add_group_operations_recursive(visited, &input->get_link()->get_operation(), group); } } } ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op) { - ExecutionGroup *group = new ExecutionGroup(this->m_groups.size()); - m_groups.append(group); + ExecutionGroup *group = new ExecutionGroup(groups_.size()); + groups_.append(group); Tags visited; add_group_operations_recursive(visited, op, group); @@ -759,20 +766,20 @@ ExecutionGroup *NodeOperationBuilder::make_group(NodeOperation *op) void NodeOperationBuilder::group_operations() { - for (NodeOperation *op : m_operations) { - if (op->isOutputOperation(m_context->isRendering())) { + for (NodeOperation *op : operations_) { + if (op->is_output_operation(context_->is_rendering())) { ExecutionGroup *group = make_group(op); - group->setOutputExecutionGroup(true); + group->set_output_execution_group(true); } /* add new groups for associated memory proxies where needed */ if (op->get_flags().is_read_buffer_operation) { ReadBufferOperation *read_op = (ReadBufferOperation *)op; - MemoryProxy *memproxy = read_op->getMemoryProxy(); + MemoryProxy *memproxy = read_op->get_memory_proxy(); - if (memproxy->getExecutor() == nullptr) { - ExecutionGroup *group = make_group(memproxy->getWriteBufferOperation()); - memproxy->setExecutor(group); + if (memproxy->get_executor() == nullptr) { + ExecutionGroup *group = make_group(memproxy->get_write_buffer_operation()); + memproxy->set_executor(group); } } } @@ -781,7 +788,7 @@ void NodeOperationBuilder::group_operations() void NodeOperationBuilder::save_graphviz(StringRefNull name) { if (COM_EXPORT_GRAPHVIZ) { - exec_system_->set_operations(m_operations, m_groups); + exec_system_->set_operations(operations_, groups_); DebugInfo::graphviz(exec_system_, name); } } @@ -799,15 +806,15 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder) os << "\n"; for (const NodeOperationBuilder::Link &link : builder.get_links()) { - os << " op" << link.from()->getOperation().get_id() << " -> op" - << link.to()->getOperation().get_id() << ";\n"; + os << " op" << link.from()->get_operation().get_id() << " -> op" + << link.to()->get_operation().get_id() << ";\n"; } for (const NodeOperation *operation : builder.get_operations()) { if (operation->get_flags().is_read_buffer_operation) { const ReadBufferOperation &read_operation = static_cast( *operation); const WriteBufferOperation &write_operation = - *read_operation.getMemoryProxy()->getWriteBufferOperation(); + *read_operation.get_memory_proxy()->get_write_buffer_operation(); os << " op" << write_operation.get_id() << " -> op" << read_operation.get_id() << ";\n"; } } @@ -819,7 +826,7 @@ std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder &builder) std::ostream &operator<<(std::ostream &os, const NodeOperationBuilder::Link &link) { - os << link.from()->getOperation().get_id() << " -> " << link.to()->getOperation().get_id(); + os << link.from()->get_operation().get_id() << " -> " << link.to()->get_operation().get_id(); return os; } diff --git a/source/blender/compositor/intern/COM_NodeOperationBuilder.h b/source/blender/compositor/intern/COM_NodeOperationBuilder.h index aca4d043d41..fcb2dd3800e 100644 --- a/source/blender/compositor/intern/COM_NodeOperationBuilder.h +++ b/source/blender/compositor/intern/COM_NodeOperationBuilder.h @@ -18,9 +18,8 @@ #pragma once -#include -#include -#include +#include "BLI_map.hh" +#include "BLI_vector.hh" #include "COM_NodeGraph.h" @@ -47,45 +46,45 @@ class NodeOperationBuilder { public: class Link { private: - NodeOperationOutput *m_from; - NodeOperationInput *m_to; + NodeOperationOutput *from_; + NodeOperationInput *to_; public: - Link(NodeOperationOutput *from, NodeOperationInput *to) : m_from(from), m_to(to) + Link(NodeOperationOutput *from, NodeOperationInput *to) : from_(from), to_(to) { } NodeOperationOutput *from() const { - return m_from; + return from_; } NodeOperationInput *to() const { - return m_to; + return to_; } }; private: - const CompositorContext *m_context; - NodeGraph m_graph; + const CompositorContext *context_; + NodeGraph graph_; ExecutionSystem *exec_system_; - Vector m_operations; - Vector m_links; - Vector m_groups; + Vector operations_; + Vector links_; + Vector groups_; /** Maps operation inputs to node inputs */ - Map m_input_map; + Map input_map_; /** Maps node outputs to operation outputs */ - Map m_output_map; + Map output_map_; - Node *m_current_node; + Node *current_node_; /** Operation that will be writing to the viewer image * Only one operation can occupy this place at a time, * to avoid race conditions */ - ViewerOperation *m_active_viewer; + ViewerOperation *active_viewer_; public: NodeOperationBuilder(const CompositorContext *context, @@ -94,44 +93,44 @@ class NodeOperationBuilder { const CompositorContext &context() const { - return *m_context; + return *context_; } - void convertToOperations(ExecutionSystem *system); + void convert_to_operations(ExecutionSystem *system); - void addOperation(NodeOperation *operation); + void add_operation(NodeOperation *operation); void replace_operation_with_constant(NodeOperation *operation, ConstantOperation *constant_operation); /** Map input socket of the current node to an operation socket */ - void mapInputSocket(NodeInput *node_socket, NodeOperationInput *operation_socket); + void map_input_socket(NodeInput *node_socket, NodeOperationInput *operation_socket); /** Map output socket of the current node to an operation socket */ - void mapOutputSocket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); + void map_output_socket(NodeOutput *node_socket, NodeOperationOutput *operation_socket); - void addLink(NodeOperationOutput *from, NodeOperationInput *to); - void removeInputLink(NodeOperationInput *to); + void add_link(NodeOperationOutput *from, NodeOperationInput *to); + void remove_input_link(NodeOperationInput *to); /** Add a preview operation for a operation output */ - void addPreview(NodeOperationOutput *output); + void add_preview(NodeOperationOutput *output); /** Add a preview operation for a node input */ - void addNodeInputPreview(NodeInput *input); + void add_node_input_preview(NodeInput *input); /** Define a viewer operation as the active output, if possible */ - void registerViewer(ViewerOperation *viewer); + void register_viewer(ViewerOperation *viewer); /** The currently active viewer output operation */ ViewerOperation *active_viewer() const { - return m_active_viewer; + return active_viewer_; } const Vector &get_operations() const { - return m_operations; + return operations_; } const Vector &get_links() const { - return m_links; + return links_; } protected: @@ -145,8 +144,8 @@ class NodeOperationBuilder { /** Replace proxy operations with direct links */ void resolve_proxies(); - /** Calculate resolution for each operation */ - void determineResolutions(); + /** Calculate canvas area for each operation. */ + void determine_canvases(); /** Helper function to store connected inputs for replacement */ Vector cache_output_links(NodeOperationOutput *output) const; diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.cc b/source/blender/compositor/intern/COM_OpenCLDevice.cc index 3409c8fa3bc..9a274abf806 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.cc +++ b/source/blender/compositor/intern/COM_OpenCLDevice.cc @@ -17,7 +17,9 @@ */ #include "COM_OpenCLDevice.h" -#include "COM_WorkScheduler.h" + +#include "COM_ExecutionGroup.h" +#include "COM_ReadBufferOperation.h" namespace blender::compositor { @@ -38,68 +40,69 @@ const cl_image_format IMAGE_FORMAT_VALUE = { OpenCLDevice::OpenCLDevice(cl_context context, cl_device_id device, cl_program program, - cl_int vendorId) + cl_int vendor_id) { - this->m_device = device; - this->m_context = context; - this->m_program = program; - this->m_queue = nullptr; - this->m_vendorID = vendorId; + device_ = device; + context_ = context; + program_ = program; + queue_ = nullptr; + vendor_id_ = vendor_id; cl_int error; - this->m_queue = clCreateCommandQueue(this->m_context, this->m_device, 0, &error); + queue_ = clCreateCommandQueue(context_, device_, 0, &error); } OpenCLDevice::OpenCLDevice(OpenCLDevice &&other) noexcept - : m_context(other.m_context), - m_device(other.m_device), - m_program(other.m_program), - m_queue(other.m_queue), - m_vendorID(other.m_vendorID) + : context_(other.context_), + device_(other.device_), + program_(other.program_), + queue_(other.queue_), + vendor_id_(other.vendor_id_) { - other.m_queue = nullptr; + other.queue_ = nullptr; } OpenCLDevice::~OpenCLDevice() { - if (this->m_queue) { - clReleaseCommandQueue(this->m_queue); + if (queue_) { + clReleaseCommandQueue(queue_); } } void OpenCLDevice::execute(WorkPackage *work_package) { - const unsigned int chunkNumber = work_package->chunk_number; - ExecutionGroup *executionGroup = work_package->execution_group; + const unsigned int chunk_number = work_package->chunk_number; + ExecutionGroup *execution_group = work_package->execution_group; - MemoryBuffer **inputBuffers = executionGroup->getInputBuffersOpenCL(chunkNumber); - MemoryBuffer *outputBuffer = executionGroup->allocateOutputBuffer(work_package->rect); + MemoryBuffer **input_buffers = execution_group->get_input_buffers_opencl(chunk_number); + MemoryBuffer *output_buffer = execution_group->allocate_output_buffer(work_package->rect); - executionGroup->getOutputOperation()->executeOpenCLRegion( - this, &work_package->rect, chunkNumber, inputBuffers, outputBuffer); + execution_group->get_output_operation()->execute_opencl_region( + this, &work_package->rect, chunk_number, input_buffers, output_buffer); - delete outputBuffer; + delete output_buffer; - executionGroup->finalizeChunkExecution(chunkNumber, inputBuffers); + execution_group->finalize_chunk_execution(chunk_number, input_buffers); } -cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - SocketReader *reader) +cl_mem OpenCLDevice::COM_cl_attach_memory_buffer_to_kernel_parameter( + cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + SocketReader *reader) { - return COM_clAttachMemoryBufferToKernelParameter(kernel, - parameterIndex, - offsetIndex, - cleanup, - inputMemoryBuffers, - (ReadBufferOperation *)reader); + return COM_cl_attach_memory_buffer_to_kernel_parameter(kernel, + parameter_index, + offset_index, + cleanup, + input_memory_buffers, + (ReadBufferOperation *)reader); } -const cl_image_format *OpenCLDevice::determineImageFormat(MemoryBuffer *memoryBuffer) +const cl_image_format *OpenCLDevice::determine_image_format(MemoryBuffer *memory_buffer) { - switch (memoryBuffer->get_num_channels()) { + switch (memory_buffer->get_num_channels()) { case 1: return &IMAGE_FORMAT_VALUE; break; @@ -116,166 +119,164 @@ const cl_image_format *OpenCLDevice::determineImageFormat(MemoryBuffer *memoryBu return &IMAGE_FORMAT_COLOR; } -cl_mem OpenCLDevice::COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - ReadBufferOperation *reader) +cl_mem OpenCLDevice::COM_cl_attach_memory_buffer_to_kernel_parameter( + cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + ReadBufferOperation *reader) { cl_int error; - MemoryBuffer *result = reader->getInputMemoryBuffer(inputMemoryBuffers); + MemoryBuffer *result = reader->get_input_memory_buffer(input_memory_buffers); - const cl_image_format *imageFormat = determineImageFormat(result); + const cl_image_format *image_format = determine_image_format(result); - cl_mem clBuffer = clCreateImage2D(this->m_context, - CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, - imageFormat, - result->getWidth(), - result->getHeight(), - 0, - result->getBuffer(), - &error); + cl_mem cl_buffer = clCreateImage2D(context_, + CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, + image_format, + result->get_width(), + result->get_height(), + 0, + result->get_buffer(), + &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } if (error == CL_SUCCESS) { - cleanup->push_back(clBuffer); + cleanup->push_back(cl_buffer); } - error = clSetKernelArg(kernel, parameterIndex, sizeof(cl_mem), &clBuffer); + error = clSetKernelArg(kernel, parameter_index, sizeof(cl_mem), &cl_buffer); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, offsetIndex, result); - return clBuffer; + COM_cl_attach_memory_buffer_offset_to_kernel_parameter(kernel, offset_index, result); + return cl_buffer; } -void OpenCLDevice::COM_clAttachMemoryBufferOffsetToKernelParameter(cl_kernel kernel, - int offsetIndex, - MemoryBuffer *memoryBuffer) +void OpenCLDevice::COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + cl_kernel kernel, int offset_index, MemoryBuffer *memory_buffer) { - if (offsetIndex != -1) { + if (offset_index != -1) { cl_int error; - const rcti &rect = memoryBuffer->get_rect(); + const rcti &rect = memory_buffer->get_rect(); cl_int2 offset = {{rect.xmin, rect.ymin}}; - error = clSetKernelArg(kernel, offsetIndex, sizeof(cl_int2), &offset); + error = clSetKernelArg(kernel, offset_index, sizeof(cl_int2), &offset); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } } } -void OpenCLDevice::COM_clAttachSizeToKernelParameter(cl_kernel kernel, - int offsetIndex, - NodeOperation *operation) +void OpenCLDevice::COM_cl_attach_size_to_kernel_parameter(cl_kernel kernel, + int offset_index, + NodeOperation *operation) { - if (offsetIndex != -1) { + if (offset_index != -1) { cl_int error; - cl_int2 offset = {{(cl_int)operation->getWidth(), (cl_int)operation->getHeight()}}; + cl_int2 offset = {{(cl_int)operation->get_width(), (cl_int)operation->get_height()}}; - error = clSetKernelArg(kernel, offsetIndex, sizeof(cl_int2), &offset); + error = clSetKernelArg(kernel, offset_index, sizeof(cl_int2), &offset); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } } } -void OpenCLDevice::COM_clAttachOutputMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - cl_mem clOutputMemoryBuffer) +void OpenCLDevice::COM_cl_attach_output_memory_buffer_to_kernel_parameter( + cl_kernel kernel, int parameter_index, cl_mem cl_output_memory_buffer) { cl_int error; - error = clSetKernelArg(kernel, parameterIndex, sizeof(cl_mem), &clOutputMemoryBuffer); + error = clSetKernelArg(kernel, parameter_index, sizeof(cl_mem), &cl_output_memory_buffer); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } } -void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, MemoryBuffer *outputMemoryBuffer) +void OpenCLDevice::COM_cl_enqueue_range(cl_kernel kernel, MemoryBuffer *output_memory_buffer) { cl_int error; const size_t size[] = { - (size_t)outputMemoryBuffer->getWidth(), - (size_t)outputMemoryBuffer->getHeight(), + (size_t)output_memory_buffer->get_width(), + (size_t)output_memory_buffer->get_height(), }; - error = clEnqueueNDRangeKernel( - this->m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); + error = clEnqueueNDRangeKernel(queue_, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } } -void OpenCLDevice::COM_clEnqueueRange(cl_kernel kernel, - MemoryBuffer *outputMemoryBuffer, - int offsetIndex, - NodeOperation *operation) +void OpenCLDevice::COM_cl_enqueue_range(cl_kernel kernel, + MemoryBuffer *output_memory_buffer, + int offset_index, + NodeOperation *operation) { cl_int error; - const int width = outputMemoryBuffer->getWidth(); - const int height = outputMemoryBuffer->getHeight(); + const int width = output_memory_buffer->get_width(); + const int height = output_memory_buffer->get_height(); int offsetx; int offsety; - int localSize = 1024; + int local_size = 1024; size_t size[2]; cl_int2 offset; - if (this->m_vendorID == NVIDIA) { - localSize = 32; + if (vendor_id_ == NVIDIA) { + local_size = 32; } bool breaked = false; - for (offsety = 0; offsety < height && (!breaked); offsety += localSize) { + for (offsety = 0; offsety < height && (!breaked); offsety += local_size) { offset.s[1] = offsety; - if (offsety + localSize < height) { - size[1] = localSize; + if (offsety + local_size < height) { + size[1] = local_size; } else { size[1] = height - offsety; } - for (offsetx = 0; offsetx < width && (!breaked); offsetx += localSize) { - if (offsetx + localSize < width) { - size[0] = localSize; + for (offsetx = 0; offsetx < width && (!breaked); offsetx += local_size) { + if (offsetx + local_size < width) { + size[0] = local_size; } else { size[0] = width - offsetx; } offset.s[0] = offsetx; - error = clSetKernelArg(kernel, offsetIndex, sizeof(cl_int2), &offset); + error = clSetKernelArg(kernel, offset_index, sizeof(cl_int2), &offset); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } error = clEnqueueNDRangeKernel( - this->m_queue, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); + queue_, kernel, 2, nullptr, size, nullptr, 0, nullptr, nullptr); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - clFlush(this->m_queue); - if (operation->isBraked()) { + clFlush(queue_); + if (operation->is_braked()) { breaked = false; } } } } -cl_kernel OpenCLDevice::COM_clCreateKernel(const char *kernelname, - std::list *clKernelsToCleanUp) +cl_kernel OpenCLDevice::COM_cl_create_kernel(const char *kernelname, + std::list *cl_kernels_to_clean_up) { cl_int error; - cl_kernel kernel = clCreateKernel(this->m_program, kernelname, &error); + cl_kernel kernel = clCreateKernel(program_, kernelname, &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } else { - if (clKernelsToCleanUp) { - clKernelsToCleanUp->push_back(kernel); + if (cl_kernels_to_clean_up) { + cl_kernels_to_clean_up->push_back(kernel); } } return kernel; diff --git a/source/blender/compositor/intern/COM_OpenCLDevice.h b/source/blender/compositor/intern/COM_OpenCLDevice.h index 826b0457a49..44a25747407 100644 --- a/source/blender/compositor/intern/COM_OpenCLDevice.h +++ b/source/blender/compositor/intern/COM_OpenCLDevice.h @@ -20,13 +20,20 @@ class OpenCLDevice; #pragma once +#include + #include "COM_Device.h" -#include "COM_ReadBufferOperation.h" -#include "COM_WorkScheduler.h" + #include "clew.h" namespace blender::compositor { +class NodeOperation; +class MemoryBuffer; +class ReadBufferOperation; + +typedef NodeOperation SocketReader; + /** * \brief device representing an GPU OpenCL device. * an instance of this class represents a single cl_device @@ -36,27 +43,27 @@ class OpenCLDevice : public Device { /** * \brief opencl context */ - cl_context m_context; + cl_context context_; /** * \brief opencl device */ - cl_device_id m_device; + cl_device_id device_; /** * \brief opencl program */ - cl_program m_program; + cl_program program_; /** * \brief opencl command queue */ - cl_command_queue m_queue; + cl_command_queue queue_; /** * \brief opencl vendor ID */ - cl_int m_vendorID; + cl_int vendor_id_; public: /** @@ -66,7 +73,7 @@ class OpenCLDevice : public Device { * \param program: * \param vendorID: */ - OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendorId); + OpenCLDevice(cl_context context, cl_device_id device, cl_program program, cl_int vendor_id); OpenCLDevice(OpenCLDevice &&other) noexcept; @@ -82,45 +89,46 @@ class OpenCLDevice : public Device { * \brief determine an image format * \param memorybuffer: */ - static const cl_image_format *determineImageFormat(MemoryBuffer *memoryBuffer); + static const cl_image_format *determine_image_format(MemoryBuffer *memory_buffer); - cl_context getContext() + cl_context get_context() { - return this->m_context; + return context_; } - cl_command_queue getQueue() + cl_command_queue get_queue() { - return this->m_queue; + return queue_; } - cl_mem COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - SocketReader *reader); - cl_mem COM_clAttachMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - int offsetIndex, - std::list *cleanup, - MemoryBuffer **inputMemoryBuffers, - ReadBufferOperation *reader); - void COM_clAttachMemoryBufferOffsetToKernelParameter(cl_kernel kernel, - int offsetIndex, - MemoryBuffer *memoryBuffers); - void COM_clAttachOutputMemoryBufferToKernelParameter(cl_kernel kernel, - int parameterIndex, - cl_mem clOutputMemoryBuffer); - void COM_clAttachSizeToKernelParameter(cl_kernel kernel, - int offsetIndex, - NodeOperation *operation); - void COM_clEnqueueRange(cl_kernel kernel, MemoryBuffer *outputMemoryBuffer); - void COM_clEnqueueRange(cl_kernel kernel, - MemoryBuffer *outputMemoryBuffer, - int offsetIndex, - NodeOperation *operation); - cl_kernel COM_clCreateKernel(const char *kernelname, std::list *clKernelsToCleanUp); + cl_mem COM_cl_attach_memory_buffer_to_kernel_parameter(cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + SocketReader *reader); + cl_mem COM_cl_attach_memory_buffer_to_kernel_parameter(cl_kernel kernel, + int parameter_index, + int offset_index, + std::list *cleanup, + MemoryBuffer **input_memory_buffers, + ReadBufferOperation *reader); + void COM_cl_attach_memory_buffer_offset_to_kernel_parameter(cl_kernel kernel, + int offset_index, + MemoryBuffer *memory_buffers); + void COM_cl_attach_output_memory_buffer_to_kernel_parameter(cl_kernel kernel, + int parameter_index, + cl_mem cl_output_memory_buffer); + void COM_cl_attach_size_to_kernel_parameter(cl_kernel kernel, + int offset_index, + NodeOperation *operation); + void COM_cl_enqueue_range(cl_kernel kernel, MemoryBuffer *output_memory_buffer); + void COM_cl_enqueue_range(cl_kernel kernel, + MemoryBuffer *output_memory_buffer, + int offset_index, + NodeOperation *operation); + cl_kernel COM_cl_create_kernel(const char *kernelname, + std::list *cl_kernels_to_clean_up); }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc index 7e0486b0f54..c67251b2d20 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.cc +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.cc @@ -17,7 +17,6 @@ */ #include "COM_SharedOperationBuffers.h" -#include "BLI_rect.h" #include "COM_NodeOperation.h" namespace blender::compositor { @@ -76,9 +75,17 @@ void SharedOperationBuffers::register_read(NodeOperation *read_op) /** * Get registered areas given operation needs to render. */ -blender::Span SharedOperationBuffers::get_areas_to_render(NodeOperation *op) +Vector SharedOperationBuffers::get_areas_to_render(NodeOperation *op, + const int offset_x, + const int offset_y) { - return get_buffer_data(op).render_areas.as_span(); + Span render_areas = get_buffer_data(op).render_areas.as_span(); + Vector dst_areas; + for (rcti dst : render_areas) { + BLI_rcti_translate(&dst, offset_x, offset_y); + dst_areas.append(std::move(dst)); + } + return dst_areas; } /** diff --git a/source/blender/compositor/intern/COM_SharedOperationBuffers.h b/source/blender/compositor/intern/COM_SharedOperationBuffers.h index f7763cd8ae4..d7d0dffabb5 100644 --- a/source/blender/compositor/intern/COM_SharedOperationBuffers.h +++ b/source/blender/compositor/intern/COM_SharedOperationBuffers.h @@ -19,16 +19,19 @@ #pragma once #include "BLI_map.hh" -#include "BLI_span.hh" #include "BLI_vector.hh" -#include "COM_MemoryBuffer.h" + +#include "DNA_vec_types.h" + #ifdef WITH_CXX_GUARDEDALLOC # include "MEM_guardedalloc.h" #endif -#include namespace blender::compositor { +class MemoryBuffer; +class NodeOperation; + /** * Stores and shares operations rendered buffers including render data. Buffers are * disposed once all dependent operations have finished reading them. @@ -53,7 +56,7 @@ class SharedOperationBuffers { bool has_registered_reads(NodeOperation *op); void register_read(NodeOperation *read_op); - blender::Span get_areas_to_render(NodeOperation *op); + Vector get_areas_to_render(NodeOperation *op, int offset_x, int offset_y); bool is_operation_rendered(NodeOperation *op); void set_rendered_buffer(NodeOperation *op, std::unique_ptr buffer); MemoryBuffer *get_rendered_buffer(NodeOperation *op); diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc index 01be6e1afed..5ad324459fd 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.cc +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.cc @@ -22,42 +22,42 @@ namespace blender::compositor { SingleThreadedOperation::SingleThreadedOperation() { - this->m_cachedInstance = nullptr; - flags.complex = true; - flags.single_threaded = true; + cached_instance_ = nullptr; + flags_.complex = true; + flags_.single_threaded = true; } -void SingleThreadedOperation::initExecution() +void SingleThreadedOperation::init_execution() { - initMutex(); + init_mutex(); } -void SingleThreadedOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void SingleThreadedOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { - this->m_cachedInstance->readNoCheck(output, x, y); + cached_instance_->read_no_check(output, x, y); } -void SingleThreadedOperation::deinitExecution() +void SingleThreadedOperation::deinit_execution() { - deinitMutex(); - if (this->m_cachedInstance) { - delete this->m_cachedInstance; - this->m_cachedInstance = nullptr; + deinit_mutex(); + if (cached_instance_) { + delete cached_instance_; + cached_instance_ = nullptr; } } -void *SingleThreadedOperation::initializeTileData(rcti *rect) +void *SingleThreadedOperation::initialize_tile_data(rcti *rect) { - if (this->m_cachedInstance) { - return this->m_cachedInstance; + if (cached_instance_) { + return cached_instance_; } - lockMutex(); - if (this->m_cachedInstance == nullptr) { + lock_mutex(); + if (cached_instance_ == nullptr) { // - this->m_cachedInstance = createMemoryBuffer(rect); + cached_instance_ = create_memory_buffer(rect); } - unlockMutex(); - return this->m_cachedInstance; + unlock_mutex(); + return cached_instance_; } } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_SingleThreadedOperation.h b/source/blender/compositor/intern/COM_SingleThreadedOperation.h index 9945f938ff9..7588e654f75 100644 --- a/source/blender/compositor/intern/COM_SingleThreadedOperation.h +++ b/source/blender/compositor/intern/COM_SingleThreadedOperation.h @@ -24,12 +24,12 @@ namespace blender::compositor { class SingleThreadedOperation : public NodeOperation { private: - MemoryBuffer *m_cachedInstance; + MemoryBuffer *cached_instance_; protected: - inline bool isCached() + inline bool is_cached() { - return this->m_cachedInstance != nullptr; + return cached_instance_ != nullptr; } public: @@ -38,21 +38,21 @@ class SingleThreadedOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - virtual MemoryBuffer *createMemoryBuffer(rcti *rect) = 0; + virtual MemoryBuffer *create_memory_buffer(rcti *rect) = 0; }; } // namespace blender::compositor diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.cc b/source/blender/compositor/intern/COM_TiledExecutionModel.cc index a081b80349d..e9f7310cead 100644 --- a/source/blender/compositor/intern/COM_TiledExecutionModel.cc +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.cc @@ -35,24 +35,24 @@ TiledExecutionModel::TiledExecutionModel(CompositorContext &context, Span groups) : ExecutionModel(context, operations), groups_(groups) { - const bNodeTree *node_tree = context.getbNodeTree(); + const bNodeTree *node_tree = context.get_bnodetree(); node_tree->stats_draw(node_tree->sdh, TIP_("Compositing | Determining resolution")); unsigned int resolution[2]; for (ExecutionGroup *group : groups_) { resolution[0] = 0; resolution[1] = 0; - group->determineResolution(resolution); + group->determine_resolution(resolution); if (border_.use_render_border) { const rctf *render_border = border_.render_border; - group->setRenderBorder( + group->set_render_border( render_border->xmin, render_border->xmax, render_border->ymin, render_border->ymax); } if (border_.use_viewer_border) { const rctf *viewer_border = border_.viewer_border; - group->setViewerBorder( + group->set_viewer_border( viewer_border->xmin, viewer_border->xmax, viewer_border->ymin, viewer_border->ymax); } } @@ -63,8 +63,8 @@ static void update_read_buffer_offset(Span operations) unsigned int order = 0; for (NodeOperation *operation : operations) { if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = (ReadBufferOperation *)operation; - readOperation->setOffset(order); + ReadBufferOperation *read_operation = (ReadBufferOperation *)operation; + read_operation->set_offset(order); order++; } } @@ -75,8 +75,8 @@ static void init_write_operations_for_execution(Span operations { for (NodeOperation *operation : operations) { if (operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); + operation->set_bnodetree(bTree); + operation->init_execution(); } } } @@ -85,8 +85,8 @@ static void link_write_buffers(Span operations) { for (NodeOperation *operation : operations) { if (operation->get_flags().is_read_buffer_operation) { - ReadBufferOperation *readOperation = static_cast(operation); - readOperation->updateMemoryBuffer(); + ReadBufferOperation *read_operation = static_cast(operation); + read_operation->update_memory_buffer(); } } } @@ -96,8 +96,8 @@ static void init_non_write_operations_for_execution(Span operat { for (NodeOperation *operation : operations) { if (!operation->get_flags().is_write_buffer_operation) { - operation->setbNodeTree(bTree); - operation->initExecution(); + operation->set_bnodetree(bTree); + operation->init_execution(); } } } @@ -106,27 +106,27 @@ static void init_execution_groups_for_execution(Span groups, const int chunk_size) { for (ExecutionGroup *execution_group : groups) { - execution_group->setChunksize(chunk_size); - execution_group->initExecution(); + execution_group->set_chunksize(chunk_size); + execution_group->init_execution(); } } void TiledExecutionModel::execute(ExecutionSystem &exec_system) { - const bNodeTree *editingtree = this->context_.getbNodeTree(); + const bNodeTree *editingtree = this->context_.get_bnodetree(); editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | Initializing execution")); update_read_buffer_offset(operations_); - init_write_operations_for_execution(operations_, context_.getbNodeTree()); + init_write_operations_for_execution(operations_, context_.get_bnodetree()); link_write_buffers(operations_); - init_non_write_operations_for_execution(operations_, context_.getbNodeTree()); - init_execution_groups_for_execution(groups_, context_.getChunksize()); + init_non_write_operations_for_execution(operations_, context_.get_bnodetree()); + init_execution_groups_for_execution(groups_, context_.get_chunksize()); WorkScheduler::start(context_); execute_groups(eCompositorPriority::High, exec_system); - if (!context_.isFastCalculation()) { + if (!context_.is_fast_calculation()) { execute_groups(eCompositorPriority::Medium, exec_system); execute_groups(eCompositorPriority::Low, exec_system); } @@ -136,11 +136,11 @@ void TiledExecutionModel::execute(ExecutionSystem &exec_system) editingtree->stats_draw(editingtree->sdh, TIP_("Compositing | De-initializing execution")); for (NodeOperation *operation : operations_) { - operation->deinitExecution(); + operation->deinit_execution(); } for (ExecutionGroup *execution_group : groups_) { - execution_group->deinitExecution(); + execution_group->deinit_execution(); } } @@ -149,7 +149,7 @@ void TiledExecutionModel::execute_groups(eCompositorPriority priority, { for (ExecutionGroup *execution_group : groups_) { if (execution_group->get_flags().is_output && - execution_group->getRenderPriority() == priority) { + execution_group->get_render_priority() == priority) { execution_group->execute(&exec_system); } } diff --git a/source/blender/compositor/intern/COM_TiledExecutionModel.h b/source/blender/compositor/intern/COM_TiledExecutionModel.h index 05a795b9f07..1f5345241e2 100644 --- a/source/blender/compositor/intern/COM_TiledExecutionModel.h +++ b/source/blender/compositor/intern/COM_TiledExecutionModel.h @@ -18,6 +18,7 @@ #pragma once +#include "COM_Enums.h" #include "COM_ExecutionModel.h" #ifdef WITH_CXX_GUARDEDALLOC diff --git a/source/blender/compositor/intern/COM_WorkPackage.cc b/source/blender/compositor/intern/COM_WorkPackage.cc index ea78c0d6333..7db44226574 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.cc +++ b/source/blender/compositor/intern/COM_WorkPackage.cc @@ -18,7 +18,6 @@ #include "COM_WorkPackage.h" -#include "COM_Enums.h" #include "COM_ExecutionGroup.h" namespace blender::compositor { diff --git a/source/blender/compositor/intern/COM_WorkPackage.h b/source/blender/compositor/intern/COM_WorkPackage.h index 20fca89aa4c..1fe50b9ecf3 100644 --- a/source/blender/compositor/intern/COM_WorkPackage.h +++ b/source/blender/compositor/intern/COM_WorkPackage.h @@ -24,7 +24,7 @@ #include "COM_Enums.h" -#include "BLI_rect.h" +#include "DNA_vec_types.h" #include #include @@ -43,7 +43,7 @@ struct WorkPackage { eWorkPackageState state = eWorkPackageState::NotScheduled; /** - * \brief executionGroup with the operations-setup to be evaluated + * \brief execution_group with the operations-setup to be evaluated */ ExecutionGroup *execution_group; diff --git a/source/blender/compositor/intern/COM_WorkScheduler.cc b/source/blender/compositor/intern/COM_WorkScheduler.cc index a08f9dd284c..c88cc556e72 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.cc +++ b/source/blender/compositor/intern/COM_WorkScheduler.cc @@ -16,15 +16,14 @@ * Copyright 2011, Blender Foundation. */ -#include -#include +#include "COM_WorkScheduler.h" #include "COM_CPUDevice.h" +#include "COM_CompositorContext.h" +#include "COM_ExecutionGroup.h" #include "COM_OpenCLDevice.h" #include "COM_OpenCLKernels.cl.h" -#include "COM_WorkScheduler.h" #include "COM_WriteBufferOperation.h" -#include "COM_compositor.h" #include "clew.h" @@ -33,7 +32,6 @@ #include "BLI_task.h" #include "BLI_threads.h" #include "BLI_vector.hh" -#include "PIL_time.h" #include "BKE_global.h" @@ -106,10 +104,10 @@ static struct { /** \name OpenCL Scheduling * \{ */ -static void CL_CALLBACK clContextError(const char *errinfo, - const void * /*private_info*/, - size_t /*cb*/, - void * /*user_data*/) +static void CL_CALLBACK cl_context_error(const char *errinfo, + const void * /*private_info*/, + size_t /*cb*/, + void * /*user_data*/) { printf("OPENCL error: %s\n", errinfo); } @@ -128,7 +126,7 @@ static void *thread_execute_gpu(void *data) static void opencl_start(const CompositorContext &context) { - if (context.getHasActiveOpenCLDevices()) { + if (context.get_has_active_opencl_devices()) { g_work_scheduler.opencl.queue = BLI_thread_queue_init(); BLI_threadpool_init(&g_work_scheduler.opencl.threads, thread_execute_gpu, @@ -188,35 +186,35 @@ static void opencl_initialize(const bool use_opencl) } if (clCreateContextFromType) { - cl_uint numberOfPlatforms = 0; + cl_uint number_of_platforms = 0; cl_int error; - error = clGetPlatformIDs(0, nullptr, &numberOfPlatforms); + error = clGetPlatformIDs(0, nullptr, &number_of_platforms); if (error == -1001) { } /* GPU not supported */ else if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } if (G.f & G_DEBUG) { - printf("%u number of platforms\n", numberOfPlatforms); + printf("%u number of platforms\n", number_of_platforms); } cl_platform_id *platforms = (cl_platform_id *)MEM_mallocN( - sizeof(cl_platform_id) * numberOfPlatforms, __func__); - error = clGetPlatformIDs(numberOfPlatforms, platforms, nullptr); - unsigned int indexPlatform; - for (indexPlatform = 0; indexPlatform < numberOfPlatforms; indexPlatform++) { - cl_platform_id platform = platforms[indexPlatform]; - cl_uint numberOfDevices = 0; - clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, nullptr, &numberOfDevices); - if (numberOfDevices <= 0) { + sizeof(cl_platform_id) * number_of_platforms, __func__); + error = clGetPlatformIDs(number_of_platforms, platforms, nullptr); + unsigned int index_platform; + for (index_platform = 0; index_platform < number_of_platforms; index_platform++) { + cl_platform_id platform = platforms[index_platform]; + cl_uint number_of_devices = 0; + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, nullptr, &number_of_devices); + if (number_of_devices <= 0) { continue; } cl_device_id *cldevices = (cl_device_id *)MEM_mallocN( - sizeof(cl_device_id) * numberOfDevices, __func__); - clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, numberOfDevices, cldevices, nullptr); + sizeof(cl_device_id) * number_of_devices, __func__); + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, number_of_devices, cldevices, nullptr); g_work_scheduler.opencl.context = clCreateContext( - nullptr, numberOfDevices, cldevices, clContextError, nullptr, &error); + nullptr, number_of_devices, cldevices, cl_context_error, nullptr, &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } @@ -224,7 +222,7 @@ static void opencl_initialize(const bool use_opencl) g_work_scheduler.opencl.program = clCreateProgramWithSource( g_work_scheduler.opencl.context, 1, cl_str, nullptr, &error); error = clBuildProgram(g_work_scheduler.opencl.program, - numberOfDevices, + number_of_devices, cldevices, nullptr, nullptr, @@ -257,9 +255,9 @@ static void opencl_initialize(const bool use_opencl) MEM_freeN(build_log); } else { - unsigned int indexDevices; - for (indexDevices = 0; indexDevices < numberOfDevices; indexDevices++) { - cl_device_id device = cldevices[indexDevices]; + unsigned int index_devices; + for (index_devices = 0; index_devices < number_of_devices; index_devices++) { + cl_device_id device = cldevices[index_devices]; cl_int vendorID = 0; cl_int error2 = clGetDeviceInfo( device, CL_DEVICE_VENDOR_ID, sizeof(cl_int), &vendorID, nullptr); diff --git a/source/blender/compositor/intern/COM_WorkScheduler.h b/source/blender/compositor/intern/COM_WorkScheduler.h index 297943aa63b..dfba5a03256 100644 --- a/source/blender/compositor/intern/COM_WorkScheduler.h +++ b/source/blender/compositor/intern/COM_WorkScheduler.h @@ -18,14 +18,16 @@ #pragma once -#include "COM_ExecutionGroup.h" - -#include "COM_Device.h" -#include "COM_WorkPackage.h" -#include "COM_defines.h" +#ifdef WITH_CXX_GUARDEDALLOC +# include "MEM_guardedalloc.h" +#endif namespace blender::compositor { +struct WorkPackage; + +class CompositorContext; + /** \brief the workscheduler * \ingroup execution */ @@ -83,7 +85,7 @@ struct WorkScheduler { * \brief Are there OpenCL capable GPU devices initialized? * the result of this method is stored in the CompositorContext * A node can generate a different operation tree when OpenCLDevices exists. - * \see CompositorContext.getHasActiveOpenCLDevices + * \see CompositorContext.get_has_active_opencl_devices */ static bool has_gpu_devices(); diff --git a/source/blender/compositor/intern/COM_compositor.cc b/source/blender/compositor/intern/COM_compositor.cc index c05234f3bd0..be70ae792cb 100644 --- a/source/blender/compositor/intern/COM_compositor.cc +++ b/source/blender/compositor/intern/COM_compositor.cc @@ -24,10 +24,8 @@ #include "BKE_scene.h" #include "COM_ExecutionSystem.h" -#include "COM_MovieDistortionOperation.h" #include "COM_WorkScheduler.h" #include "COM_compositor.h" -#include "clew.h" static struct { bool is_initialized = false; @@ -66,9 +64,9 @@ void COM_execute(RenderData *render_data, Scene *scene, bNodeTree *node_tree, int rendering, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name) { /* Initialize mutex, TODO: this mutex init is actually not thread safe and * should be done somewhere as part of blender startup, all the other @@ -97,8 +95,14 @@ void COM_execute(RenderData *render_data, /* Execute. */ const bool twopass = (node_tree->flag & NTREE_TWO_PASS) && !rendering; if (twopass) { - blender::compositor::ExecutionSystem fast_pass( - render_data, scene, node_tree, rendering, true, viewSettings, displaySettings, viewName); + blender::compositor::ExecutionSystem fast_pass(render_data, + scene, + node_tree, + rendering, + true, + view_settings, + display_settings, + view_name); fast_pass.execute(); if (node_tree->test_break(node_tree->tbh)) { @@ -108,7 +112,7 @@ void COM_execute(RenderData *render_data, } blender::compositor::ExecutionSystem system( - render_data, scene, node_tree, rendering, false, viewSettings, displaySettings, viewName); + render_data, scene, node_tree, rendering, false, view_settings, display_settings, view_name); system.execute(); BLI_mutex_unlock(&g_compositor.mutex); diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.cc b/source/blender/compositor/nodes/COM_AlphaOverNode.cc index 5e09902aee2..42836d0a575 100644 --- a/source/blender/compositor/nodes/COM_AlphaOverNode.cc +++ b/source/blender/compositor/nodes/COM_AlphaOverNode.cc @@ -21,50 +21,46 @@ #include "COM_AlphaOverKeyOperation.h" #include "COM_AlphaOverMixedOperation.h" #include "COM_AlphaOverPremultiplyOperation.h" -#include "COM_MixOperation.h" - -#include "COM_SetValueOperation.h" -#include "DNA_material_types.h" /* the ramp types */ namespace blender::compositor { -void AlphaOverNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void AlphaOverNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *color1Socket = this->getInputSocket(1); - NodeInput *color2Socket = this->getInputSocket(2); - bNode *editorNode = this->getbNode(); + NodeInput *color1Socket = this->get_input_socket(1); + NodeInput *color2Socket = this->get_input_socket(2); + bNode *editor_node = this->get_bnode(); - MixBaseOperation *convertProg; - NodeTwoFloats *ntf = (NodeTwoFloats *)editorNode->storage; + MixBaseOperation *convert_prog; + NodeTwoFloats *ntf = (NodeTwoFloats *)editor_node->storage; if (ntf->x != 0.0f) { - AlphaOverMixedOperation *mixOperation = new AlphaOverMixedOperation(); - mixOperation->setX(ntf->x); - convertProg = mixOperation; + AlphaOverMixedOperation *mix_operation = new AlphaOverMixedOperation(); + mix_operation->setX(ntf->x); + convert_prog = mix_operation; } - else if (editorNode->custom1) { - convertProg = new AlphaOverKeyOperation(); + else if (editor_node->custom1) { + convert_prog = new AlphaOverKeyOperation(); } else { - convertProg = new AlphaOverPremultiplyOperation(); + convert_prog = new AlphaOverPremultiplyOperation(); } - convertProg->setUseValueAlphaMultiply(false); - if (color1Socket->isLinked()) { - convertProg->setResolutionInputSocketIndex(1); + convert_prog->set_use_value_alpha_multiply(false); + if (color1Socket->is_linked()) { + convert_prog->set_canvas_input_index(1); } - else if (color2Socket->isLinked()) { - convertProg->setResolutionInputSocketIndex(2); + else if (color2Socket->is_linked()) { + convert_prog->set_canvas_input_index(2); } else { - convertProg->setResolutionInputSocketIndex(0); + convert_prog->set_canvas_input_index(0); } - converter.addOperation(convertProg); - converter.mapInputSocket(getInputSocket(0), convertProg->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), convertProg->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), convertProg->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), convertProg->getOutputSocket(0)); + converter.add_operation(convert_prog); + converter.map_input_socket(get_input_socket(0), convert_prog->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), convert_prog->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), convert_prog->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_AlphaOverNode.h b/source/blender/compositor/nodes/COM_AlphaOverNode.h index 201c8ed5b6e..447570fe604 100644 --- a/source/blender/compositor/nodes/COM_AlphaOverNode.h +++ b/source/blender/compositor/nodes/COM_AlphaOverNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class AlphaOverNode : public Node { public: - AlphaOverNode(bNode *editorNode) : Node(editorNode) + AlphaOverNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc index af4832665df..b11c57041d9 100644 --- a/source/blender/compositor/nodes/COM_AntiAliasingNode.cc +++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.cc @@ -20,41 +20,40 @@ #include "COM_AntiAliasingNode.h" #include "COM_SMAAOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { -void AntiAliasingNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void AntiAliasingNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeAntiAliasingData *data = (NodeAntiAliasingData *)node->storage; /* Edge Detection (First Pass) */ SMAAEdgeDetectionOperation *operation1 = nullptr; operation1 = new SMAAEdgeDetectionOperation(); - operation1->setThreshold(data->threshold); - operation1->setLocalContrastAdaptationFactor(data->contrast_limit); - converter.addOperation(operation1); + operation1->set_threshold(data->threshold); + operation1->set_local_contrast_adaptation_factor(data->contrast_limit); + converter.add_operation(operation1); - converter.mapInputSocket(getInputSocket(0), operation1->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation1->get_input_socket(0)); /* Blending Weight Calculation Pixel Shader (Second Pass) */ SMAABlendingWeightCalculationOperation *operation2 = new SMAABlendingWeightCalculationOperation(); - operation2->setCornerRounding(data->corner_rounding); - converter.addOperation(operation2); + operation2->set_corner_rounding(data->corner_rounding); + converter.add_operation(operation2); - converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0)); + converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0)); /* Neighborhood Blending Pixel Shader (Third Pass) */ SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation(); - converter.addOperation(operation3); + converter.add_operation(operation3); - converter.mapInputSocket(getInputSocket(0), operation3->getInputSocket(0)); - converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation3->get_input_socket(0)); + converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation3->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_AntiAliasingNode.h b/source/blender/compositor/nodes/COM_AntiAliasingNode.h index 7d3dd750864..05c51d5856a 100644 --- a/source/blender/compositor/nodes/COM_AntiAliasingNode.h +++ b/source/blender/compositor/nodes/COM_AntiAliasingNode.h @@ -30,11 +30,11 @@ namespace blender::compositor { */ class AntiAliasingNode : public Node { public: - AntiAliasingNode(bNode *editorNode) : Node(editorNode) + AntiAliasingNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc index 1b9da789d3c..2390f6305b2 100644 --- a/source/blender/compositor/nodes/COM_BilateralBlurNode.cc +++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.cc @@ -18,28 +18,26 @@ #include "COM_BilateralBlurNode.h" #include "COM_BilateralBlurOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_node_types.h" namespace blender::compositor { -BilateralBlurNode::BilateralBlurNode(bNode *editorNode) : Node(editorNode) +BilateralBlurNode::BilateralBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BilateralBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BilateralBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeBilateralBlurData *data = (NodeBilateralBlurData *)this->getbNode()->storage; + NodeBilateralBlurData *data = (NodeBilateralBlurData *)this->get_bnode()->storage; BilateralBlurOperation *operation = new BilateralBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setData(data); + operation->set_quality(context.get_quality()); + operation->set_data(data); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BilateralBlurNode.h b/source/blender/compositor/nodes/COM_BilateralBlurNode.h index fed2612ac02..6bef897f2ef 100644 --- a/source/blender/compositor/nodes/COM_BilateralBlurNode.h +++ b/source/blender/compositor/nodes/COM_BilateralBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BilateralBlurNode : public Node { public: - BilateralBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BilateralBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BlurNode.cc b/source/blender/compositor/nodes/COM_BlurNode.cc index c10bc2a05f0..97a7159de67 100644 --- a/source/blender/compositor/nodes/COM_BlurNode.cc +++ b/source/blender/compositor/nodes/COM_BlurNode.cc @@ -17,7 +17,6 @@ */ #include "COM_BlurNode.h" -#include "COM_ExecutionSystem.h" #include "COM_FastGaussianBlurOperation.h" #include "COM_GammaCorrectOperation.h" #include "COM_GaussianAlphaXBlurOperation.h" @@ -27,107 +26,106 @@ #include "COM_GaussianYBlurOperation.h" #include "COM_MathBaseOperation.h" #include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { -BlurNode::BlurNode(bNode *editorNode) : Node(editorNode) +BlurNode::BlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - NodeBlurData *data = (NodeBlurData *)editorNode->storage; - NodeInput *inputSizeSocket = this->getInputSocket(1); - bool connectedSizeSocket = inputSizeSocket->isLinked(); + bNode *editor_node = this->get_bnode(); + NodeBlurData *data = (NodeBlurData *)editor_node->storage; + NodeInput *input_size_socket = this->get_input_socket(1); + bool connected_size_socket = input_size_socket->is_linked(); - const float size = this->getInputSocket(1)->getEditorValueFloat(); - const bool extend_bounds = (editorNode->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0; + const float size = this->get_input_socket(1)->get_editor_value_float(); + const bool extend_bounds = (editor_node->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0; - eCompositorQuality quality = context.getQuality(); + eCompositorQuality quality = context.get_quality(); NodeOperation *input_operation = nullptr, *output_operation = nullptr; if (data->filtertype == R_FILTER_FAST_GAUSS) { FastGaussianBlurOperation *operationfgb = new FastGaussianBlurOperation(); - operationfgb->setData(data); - operationfgb->setExtendBounds(extend_bounds); - converter.addOperation(operationfgb); + operationfgb->set_data(data); + operationfgb->set_extend_bounds(extend_bounds); + converter.add_operation(operationfgb); - converter.mapInputSocket(getInputSocket(1), operationfgb->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), operationfgb->get_input_socket(1)); input_operation = operationfgb; output_operation = operationfgb; } - else if (editorNode->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) { + else if (editor_node->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) { MathAddOperation *clamp = new MathAddOperation(); SetValueOperation *zero = new SetValueOperation(); - zero->setValue(0.0f); - clamp->setUseClamp(true); + zero->set_value(0.0f); + clamp->set_use_clamp(true); - converter.addOperation(clamp); - converter.addOperation(zero); - converter.mapInputSocket(getInputSocket(1), clamp->getInputSocket(0)); - converter.addLink(zero->getOutputSocket(), clamp->getInputSocket(1)); + converter.add_operation(clamp); + converter.add_operation(zero); + converter.map_input_socket(get_input_socket(1), clamp->get_input_socket(0)); + converter.add_link(zero->get_output_socket(), clamp->get_input_socket(1)); GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation(); - operationx->setData(data); - operationx->setQuality(quality); - operationx->setSize(1.0f); - operationx->setFalloff(PROP_SMOOTH); - operationx->setSubtract(false); - operationx->setExtendBounds(extend_bounds); + operationx->set_data(data); + operationx->set_quality(quality); + operationx->set_size(1.0f); + operationx->set_falloff(PROP_SMOOTH); + operationx->set_subtract(false); + operationx->set_extend_bounds(extend_bounds); - converter.addOperation(operationx); - converter.addLink(clamp->getOutputSocket(), operationx->getInputSocket(0)); + converter.add_operation(operationx); + converter.add_link(clamp->get_output_socket(), operationx->get_input_socket(0)); GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); - operationy->setData(data); - operationy->setQuality(quality); - operationy->setSize(1.0f); - operationy->setFalloff(PROP_SMOOTH); - operationy->setSubtract(false); - operationy->setExtendBounds(extend_bounds); + operationy->set_data(data); + operationy->set_quality(quality); + operationy->set_size(1.0f); + operationy->set_falloff(PROP_SMOOTH); + operationy->set_subtract(false); + operationy->set_extend_bounds(extend_bounds); - converter.addOperation(operationy); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); + converter.add_operation(operationy); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); GaussianBlurReferenceOperation *operation = new GaussianBlurReferenceOperation(); - operation->setData(data); - operation->setQuality(quality); - operation->setExtendBounds(extend_bounds); + operation->set_data(data); + operation->set_quality(quality); + operation->set_extend_bounds(extend_bounds); - converter.addOperation(operation); - converter.addLink(operationy->getOutputSocket(), operation->getInputSocket(1)); + converter.add_operation(operation); + converter.add_link(operationy->get_output_socket(), operation->get_input_socket(1)); output_operation = operation; input_operation = operation; } else if (!data->bokeh) { GaussianXBlurOperation *operationx = new GaussianXBlurOperation(); - operationx->setData(data); - operationx->setQuality(quality); - operationx->checkOpenCL(); - operationx->setExtendBounds(extend_bounds); + operationx->set_data(data); + operationx->set_quality(quality); + operationx->check_opencl(); + operationx->set_extend_bounds(extend_bounds); - converter.addOperation(operationx); - converter.mapInputSocket(getInputSocket(1), operationx->getInputSocket(1)); + converter.add_operation(operationx); + converter.map_input_socket(get_input_socket(1), operationx->get_input_socket(1)); GaussianYBlurOperation *operationy = new GaussianYBlurOperation(); - operationy->setData(data); - operationy->setQuality(quality); - operationy->checkOpenCL(); - operationy->setExtendBounds(extend_bounds); + operationy->set_data(data); + operationy->set_quality(quality); + operationy->check_opencl(); + operationy->set_extend_bounds(extend_bounds); - converter.addOperation(operationy); - converter.mapInputSocket(getInputSocket(1), operationy->getInputSocket(1)); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); + converter.add_operation(operationy); + converter.map_input_socket(get_input_socket(1), operationy->get_input_socket(1)); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); - if (!connectedSizeSocket) { - operationx->setSize(size); - operationy->setSize(size); + if (!connected_size_socket) { + operationx->set_size(size); + operationy->set_size(size); } input_operation = operationx; @@ -135,15 +133,15 @@ void BlurNode::convertToOperations(NodeConverter &converter, } else { GaussianBokehBlurOperation *operation = new GaussianBokehBlurOperation(); - operation->setData(data); - operation->setQuality(quality); - operation->setExtendBounds(extend_bounds); + operation->set_data(data); + operation->set_quality(quality); + operation->set_extend_bounds(extend_bounds); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); - if (!connectedSizeSocket) { - operation->setSize(size); + if (!connected_size_socket) { + operation->set_size(size); } input_operation = operation; @@ -153,21 +151,21 @@ void BlurNode::convertToOperations(NodeConverter &converter, if (data->gamma) { GammaCorrectOperation *correct = new GammaCorrectOperation(); GammaUncorrectOperation *inverse = new GammaUncorrectOperation(); - converter.addOperation(correct); - converter.addOperation(inverse); + converter.add_operation(correct); + converter.add_operation(inverse); - converter.mapInputSocket(getInputSocket(0), correct->getInputSocket(0)); - converter.addLink(correct->getOutputSocket(), input_operation->getInputSocket(0)); - converter.addLink(output_operation->getOutputSocket(), inverse->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), inverse->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), correct->get_input_socket(0)); + converter.add_link(correct->get_output_socket(), input_operation->get_input_socket(0)); + converter.add_link(output_operation->get_output_socket(), inverse->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), inverse->get_output_socket()); - converter.addPreview(inverse->getOutputSocket()); + converter.add_preview(inverse->get_output_socket()); } else { - converter.mapInputSocket(getInputSocket(0), input_operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), output_operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), input_operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), output_operation->get_output_socket()); - converter.addPreview(output_operation->getOutputSocket()); + converter.add_preview(output_operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_BlurNode.h b/source/blender/compositor/nodes/COM_BlurNode.h index 61cdc17f3a9..816f359cab1 100644 --- a/source/blender/compositor/nodes/COM_BlurNode.h +++ b/source/blender/compositor/nodes/COM_BlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BlurNode : public Node { public: - BlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.cc b/source/blender/compositor/nodes/COM_BokehBlurNode.cc index c51a98c0f82..8f71621c435 100644 --- a/source/blender/compositor/nodes/COM_BokehBlurNode.cc +++ b/source/blender/compositor/nodes/COM_BokehBlurNode.cc @@ -18,61 +18,56 @@ #include "COM_BokehBlurNode.h" #include "COM_BokehBlurOperation.h" -#include "COM_ConvertDepthToRadiusOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_VariableSizeBokehBlurOperation.h" -#include "DNA_camera_types.h" -#include "DNA_node_types.h" -#include "DNA_object_types.h" namespace blender::compositor { -BokehBlurNode::BokehBlurNode(bNode *editorNode) : Node(editorNode) +BokehBlurNode::BokehBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BokehBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BokehBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *b_node = this->getbNode(); + bNode *b_node = this->get_bnode(); - NodeInput *inputSizeSocket = this->getInputSocket(2); + NodeInput *input_size_socket = this->get_input_socket(2); - bool connectedSizeSocket = inputSizeSocket->isLinked(); + bool connected_size_socket = input_size_socket->is_linked(); const bool extend_bounds = (b_node->custom1 & CMP_NODEFLAG_BLUR_EXTEND_BOUNDS) != 0; - if ((b_node->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) && connectedSizeSocket) { + if ((b_node->custom1 & CMP_NODEFLAG_BLUR_VARIABLE_SIZE) && connected_size_socket) { VariableSizeBokehBlurOperation *operation = new VariableSizeBokehBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setThreshold(0.0f); - operation->setMaxBlur(b_node->custom4); - operation->setDoScaleSize(true); + operation->set_quality(context.get_quality()); + operation->set_threshold(0.0f); + operation->set_max_blur(b_node->custom4); + operation->set_do_scale_size(true); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } else { BokehBlurOperation *operation = new BokehBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setExtendBounds(extend_bounds); + operation->set_quality(context.get_quality()); + operation->set_extend_bounds(extend_bounds); - converter.addOperation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.add_operation(operation); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); /* NOTE: on the bokeh blur operation the sockets are switched. * for this reason the next two lines are correct. Fix for T43771. */ - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(3)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(2)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(3)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); - if (!connectedSizeSocket) { - operation->setSize(this->getInputSocket(2)->getEditorValueFloat()); + if (!connected_size_socket) { + operation->set_size(this->get_input_socket(2)->get_editor_value_float()); } } } diff --git a/source/blender/compositor/nodes/COM_BokehBlurNode.h b/source/blender/compositor/nodes/COM_BokehBlurNode.h index 2c060936025..869eee44a6f 100644 --- a/source/blender/compositor/nodes/COM_BokehBlurNode.h +++ b/source/blender/compositor/nodes/COM_BokehBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BokehBlurNode : public Node { public: - BokehBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BokehBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.cc b/source/blender/compositor/nodes/COM_BokehImageNode.cc index 2b0a47c76bc..c860ef7a1c9 100644 --- a/source/blender/compositor/nodes/COM_BokehImageNode.cc +++ b/source/blender/compositor/nodes/COM_BokehImageNode.cc @@ -18,25 +18,24 @@ #include "COM_BokehImageNode.h" #include "COM_BokehImageOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -BokehImageNode::BokehImageNode(bNode *editorNode) : Node(editorNode) +BokehImageNode::BokehImageNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BokehImageNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void BokehImageNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { BokehImageOperation *operation = new BokehImageOperation(); - operation->setData((NodeBokehImage *)this->getbNode()->storage); + operation->set_data((NodeBokehImage *)this->get_bnode()->storage); - converter.addOperation(operation); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); - converter.addPreview(operation->getOutputSocket(0)); + converter.add_preview(operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BokehImageNode.h b/source/blender/compositor/nodes/COM_BokehImageNode.h index 323561a7e4f..cb685c93237 100644 --- a/source/blender/compositor/nodes/COM_BokehImageNode.h +++ b/source/blender/compositor/nodes/COM_BokehImageNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BokehImageNode : public Node { public: - BokehImageNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BokehImageNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.cc b/source/blender/compositor/nodes/COM_BoxMaskNode.cc index 14f42cc42f7..1cf05727a0a 100644 --- a/source/blender/compositor/nodes/COM_BoxMaskNode.cc +++ b/source/blender/compositor/nodes/COM_BoxMaskNode.cc @@ -18,59 +18,59 @@ #include "COM_BoxMaskNode.h" #include "COM_BoxMaskOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_ScaleOperation.h" #include "COM_SetValueOperation.h" namespace blender::compositor { -BoxMaskNode::BoxMaskNode(bNode *editorNode) : Node(editorNode) +BoxMaskNode::BoxMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BoxMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void BoxMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); BoxMaskOperation *operation; operation = new BoxMaskOperation(); - operation->setData((NodeBoxMask *)this->getbNode()->storage); - operation->setMaskType(this->getbNode()->custom1); - converter.addOperation(operation); + operation->set_data((NodeBoxMask *)this->get_bnode()->storage); + operation->set_mask_type(this->get_bnode()->custom1); + converter.add_operation(operation); - if (inputSocket->isLinked()) { - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + if (input_socket->is_linked()) { + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket()); } else { /* Value operation to produce original transparent image */ - SetValueOperation *valueOperation = new SetValueOperation(); - valueOperation->setValue(0.0f); - converter.addOperation(valueOperation); + SetValueOperation *value_operation = new SetValueOperation(); + value_operation->set_value(0.0f); + converter.add_operation(value_operation); /* Scale that image up to render resolution */ - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); - ScaleFixedSizeOperation *scaleOperation = new ScaleFixedSizeOperation(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); + ScaleFixedSizeOperation *scale_operation = new ScaleFixedSizeOperation(); - scaleOperation->setIsAspect(false); - scaleOperation->setIsCrop(false); - scaleOperation->setOffset(0.0f, 0.0f); - scaleOperation->setNewWidth(rd->xsch * render_size_factor); - scaleOperation->setNewHeight(rd->ysch * render_size_factor); - scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None); - converter.addOperation(scaleOperation); + scale_operation->set_is_aspect(false); + scale_operation->set_is_crop(false); + scale_operation->set_offset(0.0f, 0.0f); + scale_operation->set_new_width(rd->xsch * render_size_factor); + scale_operation->set_new_height(rd->ysch * render_size_factor); + scale_operation->get_input_socket(0)->set_resize_mode(ResizeMode::Align); + converter.add_operation(scale_operation); - converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0)); - converter.addLink(scaleOperation->getOutputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_link(value_operation->get_output_socket(0), + scale_operation->get_input_socket(0)); + converter.add_link(scale_operation->get_output_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BoxMaskNode.h b/source/blender/compositor/nodes/COM_BoxMaskNode.h index 46cedf7af75..68d98f6c0ea 100644 --- a/source/blender/compositor/nodes/COM_BoxMaskNode.h +++ b/source/blender/compositor/nodes/COM_BoxMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BoxMaskNode : public Node { public: - BoxMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BoxMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.cc b/source/blender/compositor/nodes/COM_BrightnessNode.cc index b64f1fea99f..0b87d5d3d49 100644 --- a/source/blender/compositor/nodes/COM_BrightnessNode.cc +++ b/source/blender/compositor/nodes/COM_BrightnessNode.cc @@ -18,27 +18,26 @@ #include "COM_BrightnessNode.h" #include "COM_BrightnessOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -BrightnessNode::BrightnessNode(bNode *editorNode) : Node(editorNode) +BrightnessNode::BrightnessNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void BrightnessNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void BrightnessNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); BrightnessOperation *operation = new BrightnessOperation(); - operation->setUsePremultiply((bnode->custom1 & 1) != 0); - converter.addOperation(operation); + operation->set_use_premultiply((bnode->custom1 & 1) != 0); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_BrightnessNode.h b/source/blender/compositor/nodes/COM_BrightnessNode.h index 1084108b1c3..82a383557ba 100644 --- a/source/blender/compositor/nodes/COM_BrightnessNode.h +++ b/source/blender/compositor/nodes/COM_BrightnessNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class BrightnessNode : public Node { public: - BrightnessNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + BrightnessNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc index d0f72274aea..cf8e8a7392d 100644 --- a/source/blender/compositor/nodes/COM_ChannelMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.cc @@ -17,26 +17,25 @@ */ #include "COM_ChannelMatteNode.h" -#include "BKE_node.h" #include "COM_ChannelMatteOperation.h" #include "COM_ConvertOperation.h" #include "COM_SetAlphaMultiplyOperation.h" namespace blender::compositor { -ChannelMatteNode::ChannelMatteNode(bNode *editorNode) : Node(editorNode) +ChannelMatteNode::ChannelMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ChannelMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ChannelMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); NodeOperation *convert = nullptr, *inv_convert = nullptr; /* colorspace */ @@ -53,9 +52,9 @@ void ChannelMatteNode::convertToOperations(NodeConverter &converter, break; case CMP_NODE_CHANNEL_MATTE_CS_YCC: /* YCC */ convert = new ConvertRGBToYCCOperation(); - ((ConvertRGBToYCCOperation *)convert)->setMode(BLI_YCC_ITU_BT709); + ((ConvertRGBToYCCOperation *)convert)->set_mode(BLI_YCC_ITU_BT709); inv_convert = new ConvertYCCToRGBOperation(); - ((ConvertYCCToRGBOperation *)inv_convert)->setMode(BLI_YCC_ITU_BT709); + ((ConvertYCCToRGBOperation *)inv_convert)->set_mode(BLI_YCC_ITU_BT709); break; default: break; @@ -63,36 +62,36 @@ void ChannelMatteNode::convertToOperations(NodeConverter &converter, ChannelMatteOperation *operation = new ChannelMatteOperation(); /* pass the ui properties to the operation */ - operation->setSettings((NodeChroma *)node->storage, node->custom2); - converter.addOperation(operation); + operation->set_settings((NodeChroma *)node->storage, node->custom2); + converter.add_operation(operation); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); if (convert != nullptr) { - converter.addOperation(convert); + converter.add_operation(convert); - converter.mapInputSocket(inputSocketImage, convert->getInputSocket(0)); - converter.addLink(convert->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(convert->getOutputSocket(), operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, convert->get_input_socket(0)); + converter.add_link(convert->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(convert->get_output_socket(), operation_alpha->get_input_socket(0)); } else { - converter.mapInputSocket(inputSocketImage, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, operation->get_input_socket(0)); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); } - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); if (inv_convert != nullptr) { - converter.addOperation(inv_convert); - converter.addLink(operationAlpha->getOutputSocket(0), inv_convert->getInputSocket(0)); - converter.mapOutputSocket(outputSocketImage, inv_convert->getOutputSocket()); - converter.addPreview(inv_convert->getOutputSocket()); + converter.add_operation(inv_convert); + converter.add_link(operation_alpha->get_output_socket(0), inv_convert->get_input_socket(0)); + converter.map_output_socket(output_socket_image, inv_convert->get_output_socket()); + converter.add_preview(inv_convert->get_output_socket()); } else { - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); + converter.add_preview(operation_alpha->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_ChannelMatteNode.h b/source/blender/compositor/nodes/COM_ChannelMatteNode.h index 46100b3f7ea..c071c516e6d 100644 --- a/source/blender/compositor/nodes/COM_ChannelMatteNode.h +++ b/source/blender/compositor/nodes/COM_ChannelMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ChannelMatteNode : public Node { public: - ChannelMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ChannelMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc index 9abf183a843..8db96e0308d 100644 --- a/source/blender/compositor/nodes/COM_ChromaMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.cc @@ -17,53 +17,52 @@ */ #include "COM_ChromaMatteNode.h" -#include "BKE_node.h" #include "COM_ChromaMatteOperation.h" #include "COM_ConvertOperation.h" #include "COM_SetAlphaMultiplyOperation.h" namespace blender::compositor { -ChromaMatteNode::ChromaMatteNode(bNode *editorNode) : Node(editorNode) +ChromaMatteNode::ChromaMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ChromaMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ChromaMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketKey = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_key = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); ConvertRGBToYCCOperation *operationRGBToYCC_Image = new ConvertRGBToYCCOperation(); ConvertRGBToYCCOperation *operationRGBToYCC_Key = new ConvertRGBToYCCOperation(); - operationRGBToYCC_Image->setMode(BLI_YCC_ITU_BT709); - operationRGBToYCC_Key->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(operationRGBToYCC_Image); - converter.addOperation(operationRGBToYCC_Key); + operationRGBToYCC_Image->set_mode(BLI_YCC_ITU_BT709); + operationRGBToYCC_Key->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(operationRGBToYCC_Image); + converter.add_operation(operationRGBToYCC_Key); ChromaMatteOperation *operation = new ChromaMatteOperation(); - operation->setSettings((NodeChroma *)editorsnode->storage); - converter.addOperation(operation); + operation->set_settings((NodeChroma *)editorsnode->storage); + converter.add_operation(operation); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); - converter.mapInputSocket(inputSocketImage, operationRGBToYCC_Image->getInputSocket(0)); - converter.mapInputSocket(inputSocketKey, operationRGBToYCC_Key->getInputSocket(0)); - converter.addLink(operationRGBToYCC_Image->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(operationRGBToYCC_Key->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket()); + converter.map_input_socket(input_socket_image, operationRGBToYCC_Image->get_input_socket(0)); + converter.map_input_socket(input_socket_key, operationRGBToYCC_Key->get_input_socket(0)); + converter.add_link(operationRGBToYCC_Image->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(operationRGBToYCC_Key->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket()); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.add_preview(operation_alpha->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ChromaMatteNode.h b/source/blender/compositor/nodes/COM_ChromaMatteNode.h index f3ddd013fa4..854c07c104b 100644 --- a/source/blender/compositor/nodes/COM_ChromaMatteNode.h +++ b/source/blender/compositor/nodes/COM_ChromaMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ChromaMatteNode : public Node { public: - ChromaMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ChromaMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc index 03e4e143061..e7c43161a00 100644 --- a/source/blender/compositor/nodes/COM_ColorBalanceNode.cc +++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.cc @@ -17,28 +17,25 @@ */ #include "COM_ColorBalanceNode.h" -#include "BKE_node.h" #include "COM_ColorBalanceASCCDLOperation.h" #include "COM_ColorBalanceLGGOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_MixOperation.h" namespace blender::compositor { -ColorBalanceNode::ColorBalanceNode(bNode *editorNode) : Node(editorNode) +ColorBalanceNode::ColorBalanceNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorBalanceNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorBalanceNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeColorBalance *n = (NodeColorBalance *)node->storage; - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputImageSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_image_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); NodeOperation *operation; if (node->custom1 == 0) { @@ -50,9 +47,9 @@ void ColorBalanceNode::convertToOperations(NodeConverter &converter, gamma_inv[c] = (n->gamma[c] != 0.0f) ? 1.0f / n->gamma[c] : 1000000.0f; } - operationLGG->setGain(n->gain); - operationLGG->setLift(lift_lgg); - operationLGG->setGammaInv(gamma_inv); + operationLGG->set_gain(n->gain); + operationLGG->set_lift(lift_lgg); + operationLGG->set_gamma_inv(gamma_inv); operation = operationLGG; } else { @@ -62,16 +59,16 @@ void ColorBalanceNode::convertToOperations(NodeConverter &converter, copy_v3_fl(offset, n->offset_basis); add_v3_v3(offset, n->offset); - operationCDL->setOffset(offset); - operationCDL->setPower(n->power); - operationCDL->setSlope(n->slope); + operationCDL->set_offset(offset); + operationCDL->set_power(n->power); + operationCDL->set_slope(n->slope); operation = operationCDL; } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputImageSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_image_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorBalanceNode.h b/source/blender/compositor/nodes/COM_ColorBalanceNode.h index 243713b4912..3ac0b7cdee1 100644 --- a/source/blender/compositor/nodes/COM_ColorBalanceNode.h +++ b/source/blender/compositor/nodes/COM_ColorBalanceNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorBalanceNode : public Node { public: - ColorBalanceNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorBalanceNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc index 6397b1d8e4b..49d8a7fec98 100644 --- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.cc @@ -18,30 +18,29 @@ #include "COM_ColorCorrectionNode.h" #include "COM_ColorCorrectionOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -ColorCorrectionNode::ColorCorrectionNode(bNode *editorNode) : Node(editorNode) +ColorCorrectionNode::ColorCorrectionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorCorrectionNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorCorrectionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = getbNode(); + bNode *editor_node = get_bnode(); ColorCorrectionOperation *operation = new ColorCorrectionOperation(); - operation->setData((NodeColorCorrection *)editorNode->storage); - operation->setRedChannelEnabled((editorNode->custom1 & 1) != 0); - operation->setGreenChannelEnabled((editorNode->custom1 & 2) != 0); - operation->setBlueChannelEnabled((editorNode->custom1 & 4) != 0); - converter.addOperation(operation); + operation->set_data((NodeColorCorrection *)editor_node->storage); + operation->set_red_channel_enabled((editor_node->custom1 & 1) != 0); + operation->set_green_channel_enabled((editor_node->custom1 & 2) != 0); + operation->set_blue_channel_enabled((editor_node->custom1 & 4) != 0); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h index aee07ee07a3..6fe4212e428 100644 --- a/source/blender/compositor/nodes/COM_ColorCorrectionNode.h +++ b/source/blender/compositor/nodes/COM_ColorCorrectionNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorCorrectionNode : public Node { public: - ColorCorrectionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorCorrectionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.cc b/source/blender/compositor/nodes/COM_ColorCurveNode.cc index 774dd689a46..5b59b2bee14 100644 --- a/source/blender/compositor/nodes/COM_ColorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_ColorCurveNode.cc @@ -18,43 +18,42 @@ #include "COM_ColorCurveNode.h" #include "COM_ColorCurveOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -ColorCurveNode::ColorCurveNode(bNode *editorNode) : Node(editorNode) +ColorCurveNode::ColorCurveNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorCurveNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorCurveNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - if (this->getInputSocket(2)->isLinked() || this->getInputSocket(3)->isLinked()) { + if (this->get_input_socket(2)->is_linked() || this->get_input_socket(3)->is_linked()) { ColorCurveOperation *operation = new ColorCurveOperation(); - operation->setCurveMapping((CurveMapping *)this->getbNode()->storage); - converter.addOperation(operation); + operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(3)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } else { ConstantLevelColorCurveOperation *operation = new ConstantLevelColorCurveOperation(); float col[4]; - this->getInputSocket(2)->getEditorValueColor(col); - operation->setBlackLevel(col); - this->getInputSocket(3)->getEditorValueColor(col); - operation->setWhiteLevel(col); - operation->setCurveMapping((CurveMapping *)this->getbNode()->storage); - converter.addOperation(operation); + this->get_input_socket(2)->get_editor_value_color(col); + operation->set_black_level(col); + this->get_input_socket(3)->get_editor_value_color(col); + operation->set_white_level(col); + operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_ColorCurveNode.h b/source/blender/compositor/nodes/COM_ColorCurveNode.h index 89786b47cf5..852f755bf95 100644 --- a/source/blender/compositor/nodes/COM_ColorCurveNode.h +++ b/source/blender/compositor/nodes/COM_ColorCurveNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorCurveNode : public Node { public: - ColorCurveNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorCurveNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.cc b/source/blender/compositor/nodes/COM_ColorExposureNode.cc index a8f164e6b66..2f5e6b0a7bf 100644 --- a/source/blender/compositor/nodes/COM_ColorExposureNode.cc +++ b/source/blender/compositor/nodes/COM_ColorExposureNode.cc @@ -18,24 +18,23 @@ #include "COM_ColorExposureNode.h" #include "COM_ColorExposureOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -ExposureNode::ExposureNode(bNode *editorNode) : Node(editorNode) +ExposureNode::ExposureNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ExposureNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ExposureNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { ExposureOperation *operation = new ExposureOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorExposureNode.h b/source/blender/compositor/nodes/COM_ColorExposureNode.h index df9bfc65f81..33b143e0676 100644 --- a/source/blender/compositor/nodes/COM_ColorExposureNode.h +++ b/source/blender/compositor/nodes/COM_ColorExposureNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ExposureNode : public Node { public: - ExposureNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ExposureNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.cc b/source/blender/compositor/nodes/COM_ColorMatteNode.cc index eadb8ce4f96..629ae932eb7 100644 --- a/source/blender/compositor/nodes/COM_ColorMatteNode.cc +++ b/source/blender/compositor/nodes/COM_ColorMatteNode.cc @@ -17,51 +17,50 @@ */ #include "COM_ColorMatteNode.h" -#include "BKE_node.h" #include "COM_ColorMatteOperation.h" #include "COM_ConvertOperation.h" #include "COM_SetAlphaMultiplyOperation.h" namespace blender::compositor { -ColorMatteNode::ColorMatteNode(bNode *editorNode) : Node(editorNode) +ColorMatteNode::ColorMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketKey = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_key = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); ConvertRGBToHSVOperation *operationRGBToHSV_Image = new ConvertRGBToHSVOperation(); ConvertRGBToHSVOperation *operationRGBToHSV_Key = new ConvertRGBToHSVOperation(); - converter.addOperation(operationRGBToHSV_Image); - converter.addOperation(operationRGBToHSV_Key); + converter.add_operation(operationRGBToHSV_Image); + converter.add_operation(operationRGBToHSV_Key); ColorMatteOperation *operation = new ColorMatteOperation(); - operation->setSettings((NodeChroma *)editorsnode->storage); - converter.addOperation(operation); + operation->set_settings((NodeChroma *)editorsnode->storage); + converter.add_operation(operation); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); - converter.mapInputSocket(inputSocketImage, operationRGBToHSV_Image->getInputSocket(0)); - converter.mapInputSocket(inputSocketKey, operationRGBToHSV_Key->getInputSocket(0)); - converter.addLink(operationRGBToHSV_Image->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(operationRGBToHSV_Key->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket_image, operationRGBToHSV_Image->get_input_socket(0)); + converter.map_input_socket(input_socket_key, operationRGBToHSV_Key->get_input_socket(0)); + converter.add_link(operationRGBToHSV_Image->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(operationRGBToHSV_Key->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket(0)); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.add_preview(operation_alpha->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorMatteNode.h b/source/blender/compositor/nodes/COM_ColorMatteNode.h index 9d70b6d8416..3e1bfd607e8 100644 --- a/source/blender/compositor/nodes/COM_ColorMatteNode.h +++ b/source/blender/compositor/nodes/COM_ColorMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorMatteNode : public Node { public: - ColorMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorNode.cc b/source/blender/compositor/nodes/COM_ColorNode.cc index f8277645a4b..2d875d703c7 100644 --- a/source/blender/compositor/nodes/COM_ColorNode.cc +++ b/source/blender/compositor/nodes/COM_ColorNode.cc @@ -17,27 +17,26 @@ */ #include "COM_ColorNode.h" -#include "COM_ExecutionSystem.h" #include "COM_SetColorOperation.h" namespace blender::compositor { -ColorNode::ColorNode(bNode *editorNode) : Node(editorNode) +ColorNode::ColorNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { SetColorOperation *operation = new SetColorOperation(); - NodeOutput *output = this->getOutputSocket(0); + NodeOutput *output = this->get_output_socket(0); float col[4]; - output->getEditorValueColor(col); - operation->setChannels(col); - converter.addOperation(operation); + output->get_editor_value_color(col); + operation->set_channels(col); + converter.add_operation(operation); - converter.mapOutputSocket(output, operation->getOutputSocket()); + converter.map_output_socket(output, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorNode.h b/source/blender/compositor/nodes/COM_ColorNode.h index ae3bf575bb4..f5b932f6321 100644 --- a/source/blender/compositor/nodes/COM_ColorNode.h +++ b/source/blender/compositor/nodes/COM_ColorNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorNode : public Node { public: - ColorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.cc b/source/blender/compositor/nodes/COM_ColorRampNode.cc index 6b44ef3057e..7ae83be65c2 100644 --- a/source/blender/compositor/nodes/COM_ColorRampNode.cc +++ b/source/blender/compositor/nodes/COM_ColorRampNode.cc @@ -17,40 +17,37 @@ */ #include "COM_ColorRampNode.h" -#include "BKE_node.h" #include "COM_ColorRampOperation.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_texture_types.h" namespace blender::compositor { -ColorRampNode::ColorRampNode(bNode *editorNode) : Node(editorNode) +ColorRampNode::ColorRampNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorRampNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorRampNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - NodeOutput *outputSocketAlpha = this->getOutputSocket(1); - bNode *editorNode = this->getbNode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + NodeOutput *output_socket_alpha = this->get_output_socket(1); + bNode *editor_node = this->get_bnode(); ColorRampOperation *operation = new ColorRampOperation(); - operation->setColorBand((ColorBand *)editorNode->storage); - converter.addOperation(operation); + operation->set_color_band((ColorBand *)editor_node->storage); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); SeparateChannelOperation *operation2 = new SeparateChannelOperation(); - operation2->setChannel(3); - converter.addOperation(operation2); + operation2->set_channel(3); + converter.add_operation(operation2); - converter.addLink(operation->getOutputSocket(), operation2->getInputSocket(0)); - converter.mapOutputSocket(outputSocketAlpha, operation2->getOutputSocket()); + converter.add_link(operation->get_output_socket(), operation2->get_input_socket(0)); + converter.map_output_socket(output_socket_alpha, operation2->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorRampNode.h b/source/blender/compositor/nodes/COM_ColorRampNode.h index d0c0e43d56c..70e10311b2a 100644 --- a/source/blender/compositor/nodes/COM_ColorRampNode.h +++ b/source/blender/compositor/nodes/COM_ColorRampNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorRampNode : public Node { public: - ColorRampNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorRampNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.cc b/source/blender/compositor/nodes/COM_ColorSpillNode.cc index 6119e635e59..063c515b460 100644 --- a/source/blender/compositor/nodes/COM_ColorSpillNode.cc +++ b/source/blender/compositor/nodes/COM_ColorSpillNode.cc @@ -17,35 +17,34 @@ */ #include "COM_ColorSpillNode.h" -#include "BKE_node.h" #include "COM_ColorSpillOperation.h" namespace blender::compositor { -ColorSpillNode::ColorSpillNode(bNode *editorNode) : Node(editorNode) +ColorSpillNode::ColorSpillNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorSpillNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorSpillNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketFac = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_fac = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); ColorSpillOperation *operation; operation = new ColorSpillOperation(); - operation->setSettings((NodeColorspill *)editorsnode->storage); - operation->setSpillChannel(editorsnode->custom1 - 1); /* Channel for spilling */ - operation->setSpillMethod(editorsnode->custom2); /* Channel method */ - converter.addOperation(operation); + operation->set_settings((NodeColorspill *)editorsnode->storage); + operation->set_spill_channel(editorsnode->custom1 - 1); /* Channel for spilling */ + operation->set_spill_method(editorsnode->custom2); /* Channel method */ + converter.add_operation(operation); - converter.mapInputSocket(inputSocketImage, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocketFac, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket()); + converter.map_input_socket(input_socket_image, operation->get_input_socket(0)); + converter.map_input_socket(input_socket_fac, operation->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorSpillNode.h b/source/blender/compositor/nodes/COM_ColorSpillNode.h index 731a76e8811..3a9b0e665e8 100644 --- a/source/blender/compositor/nodes/COM_ColorSpillNode.h +++ b/source/blender/compositor/nodes/COM_ColorSpillNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorSpillNode : public Node { public: - ColorSpillNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorSpillNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.cc b/source/blender/compositor/nodes/COM_ColorToBWNode.cc index dcedfc19e4d..c021ee59b9e 100644 --- a/source/blender/compositor/nodes/COM_ColorToBWNode.cc +++ b/source/blender/compositor/nodes/COM_ColorToBWNode.cc @@ -19,26 +19,25 @@ #include "COM_ColorToBWNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -ColorToBWNode::ColorToBWNode(bNode *editorNode) : Node(editorNode) +ColorToBWNode::ColorToBWNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ColorToBWNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ColorToBWNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *colorSocket = this->getInputSocket(0); - NodeOutput *valueSocket = this->getOutputSocket(0); + NodeInput *color_socket = this->get_input_socket(0); + NodeOutput *value_socket = this->get_output_socket(0); - ConvertColorToBWOperation *convertProg = new ConvertColorToBWOperation(); - converter.addOperation(convertProg); + ConvertColorToBWOperation *convert_prog = new ConvertColorToBWOperation(); + converter.add_operation(convert_prog); - converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0)); - converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0)); + converter.map_input_socket(color_socket, convert_prog->get_input_socket(0)); + converter.map_output_socket(value_socket, convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ColorToBWNode.h b/source/blender/compositor/nodes/COM_ColorToBWNode.h index 60c08a3c886..7018f30f559 100644 --- a/source/blender/compositor/nodes/COM_ColorToBWNode.h +++ b/source/blender/compositor/nodes/COM_ColorToBWNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class ColorToBWNode : public Node { public: - ColorToBWNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ColorToBWNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.cc b/source/blender/compositor/nodes/COM_CombineColorNode.cc index 8a2bbba1c1e..a37fc6ad5fa 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.cc +++ b/source/blender/compositor/nodes/COM_CombineColorNode.cc @@ -22,70 +22,70 @@ namespace blender::compositor { -CombineColorNode::CombineColorNode(bNode *editorNode) : Node(editorNode) +CombineColorNode::CombineColorNode(bNode *editor_node) : Node(editor_node) { } -void CombineColorNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void CombineColorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputRSocket = this->getInputSocket(0); - NodeInput *inputGSocket = this->getInputSocket(1); - NodeInput *inputBSocket = this->getInputSocket(2); - NodeInput *inputASocket = this->getInputSocket(3); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_rsocket = this->get_input_socket(0); + NodeInput *input_gsocket = this->get_input_socket(1); + NodeInput *input_bsocket = this->get_input_socket(2); + NodeInput *input_asocket = this->get_input_socket(3); + NodeOutput *output_socket = this->get_output_socket(0); CombineChannelsOperation *operation = new CombineChannelsOperation(); - if (inputRSocket->isLinked()) { - operation->setResolutionInputSocketIndex(0); + if (input_rsocket->is_linked()) { + operation->set_canvas_input_index(0); } - else if (inputGSocket->isLinked()) { - operation->setResolutionInputSocketIndex(1); + else if (input_gsocket->is_linked()) { + operation->set_canvas_input_index(1); } - else if (inputBSocket->isLinked()) { - operation->setResolutionInputSocketIndex(2); + else if (input_bsocket->is_linked()) { + operation->set_canvas_input_index(2); } else { - operation->setResolutionInputSocketIndex(3); + operation->set_canvas_input_index(3); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputRSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputGSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputBSocket, operation->getInputSocket(2)); - converter.mapInputSocket(inputASocket, operation->getInputSocket(3)); + converter.map_input_socket(input_rsocket, operation->get_input_socket(0)); + converter.map_input_socket(input_gsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_bsocket, operation->get_input_socket(2)); + converter.map_input_socket(input_asocket, operation->get_input_socket(3)); - NodeOperation *color_conv = getColorConverter(context); + NodeOperation *color_conv = get_color_converter(context); if (color_conv) { - converter.addOperation(color_conv); + converter.add_operation(color_conv); - converter.addLink(operation->getOutputSocket(), color_conv->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, color_conv->getOutputSocket()); + converter.add_link(operation->get_output_socket(), color_conv->get_input_socket(0)); + converter.map_output_socket(output_socket, color_conv->get_output_socket()); } else { - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.map_output_socket(output_socket, operation->get_output_socket()); } } -NodeOperation *CombineRGBANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineRGBANode::get_color_converter(const CompositorContext & /*context*/) const { return nullptr; /* no conversion needed */ } -NodeOperation *CombineHSVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineHSVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertHSVToRGBOperation(); } -NodeOperation *CombineYCCANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineYCCANode::get_color_converter(const CompositorContext & /*context*/) const { ConvertYCCToRGBOperation *operation = new ConvertYCCToRGBOperation(); - bNode *editorNode = this->getbNode(); - operation->setMode(editorNode->custom1); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); return operation; } -NodeOperation *CombineYUVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *CombineYUVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertYUVToRGBOperation(); } diff --git a/source/blender/compositor/nodes/COM_CombineColorNode.h b/source/blender/compositor/nodes/COM_CombineColorNode.h index 29d3fa37817..3252fc779f4 100644 --- a/source/blender/compositor/nodes/COM_CombineColorNode.h +++ b/source/blender/compositor/nodes/COM_CombineColorNode.h @@ -24,48 +24,48 @@ namespace blender::compositor { class CombineColorNode : public Node { public: - CombineColorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CombineColorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; protected: - virtual NodeOperation *getColorConverter(const CompositorContext &context) const = 0; + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; }; class CombineRGBANode : public CombineColorNode { public: - CombineRGBANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineRGBANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class CombineHSVANode : public CombineColorNode { public: - CombineHSVANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineHSVANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class CombineYCCANode : public CombineColorNode { public: - CombineYCCANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineYCCANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class CombineYUVANode : public CombineColorNode { public: - CombineYUVANode(bNode *editorNode) : CombineColorNode(editorNode) + CombineYUVANode(bNode *editor_node) : CombineColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cc b/source/blender/compositor/nodes/COM_CompositorNode.cc index 262fa550915..5121a7cc123 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.cc +++ b/source/blender/compositor/nodes/COM_CompositorNode.cc @@ -18,48 +18,47 @@ #include "COM_CompositorNode.h" #include "COM_CompositorOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -CompositorNode::CompositorNode(bNode *editorNode) : Node(editorNode) +CompositorNode::CompositorNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void CompositorNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void CompositorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - bool is_active = (editorNode->flag & NODE_DO_OUTPUT_RECALC) || context.isRendering(); - bool ignore_alpha = (editorNode->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; + bNode *editor_node = this->get_bnode(); + bool is_active = (editor_node->flag & NODE_DO_OUTPUT_RECALC) || context.is_rendering(); + bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; - NodeInput *imageSocket = this->getInputSocket(0); - NodeInput *alphaSocket = this->getInputSocket(1); - NodeInput *depthSocket = this->getInputSocket(2); + NodeInput *image_socket = this->get_input_socket(0); + NodeInput *alpha_socket = this->get_input_socket(1); + NodeInput *depth_socket = this->get_input_socket(2); - CompositorOperation *compositorOperation = new CompositorOperation(); - compositorOperation->setScene(context.getScene()); - compositorOperation->setSceneName(context.getScene()->id.name); - compositorOperation->setRenderData(context.getRenderData()); - compositorOperation->setViewName(context.getViewName()); - compositorOperation->setbNodeTree(context.getbNodeTree()); + CompositorOperation *compositor_operation = new CompositorOperation(); + compositor_operation->set_scene(context.get_scene()); + compositor_operation->set_scene_name(context.get_scene()->id.name); + compositor_operation->set_render_data(context.get_render_data()); + compositor_operation->set_view_name(context.get_view_name()); + compositor_operation->set_bnodetree(context.get_bnodetree()); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ - compositorOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); - compositorOperation->setActive(is_active); + compositor_operation->set_use_alpha_input(ignore_alpha || alpha_socket->is_linked()); + compositor_operation->set_active(is_active); - converter.addOperation(compositorOperation); - converter.mapInputSocket(imageSocket, compositorOperation->getInputSocket(0)); + converter.add_operation(compositor_operation); + converter.map_input_socket(image_socket, compositor_operation->get_input_socket(0)); /* only use alpha link if "use alpha" is enabled */ if (ignore_alpha) { - converter.addInputValue(compositorOperation->getInputSocket(1), 1.0f); + converter.add_input_value(compositor_operation->get_input_socket(1), 1.0f); } else { - converter.mapInputSocket(alphaSocket, compositorOperation->getInputSocket(1)); + converter.map_input_socket(alpha_socket, compositor_operation->get_input_socket(1)); } - converter.mapInputSocket(depthSocket, compositorOperation->getInputSocket(2)); + converter.map_input_socket(depth_socket, compositor_operation->get_input_socket(2)); - converter.addNodeInputPreview(imageSocket); + converter.add_node_input_preview(image_socket); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CompositorNode.h b/source/blender/compositor/nodes/COM_CompositorNode.h index 4da9f9a766f..68005230ba2 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.h +++ b/source/blender/compositor/nodes/COM_CompositorNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class CompositorNode : public Node { public: - CompositorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CompositorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc index ac4e45357dc..c2c4b989007 100644 --- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.cc @@ -18,15 +18,14 @@ #include "COM_ConvertAlphaNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -void ConvertAlphaNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ConvertAlphaNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { NodeOperation *operation = nullptr; - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); /* value hardcoded in rna_nodetree.c */ if (node->custom1 == 1) { @@ -36,10 +35,10 @@ void ConvertAlphaNode::convertToOperations(NodeConverter &converter, operation = new ConvertStraightToPremulOperation(); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h index f3d0ef2cd5b..8f26c82c110 100644 --- a/source/blender/compositor/nodes/COM_ConvertAlphaNode.h +++ b/source/blender/compositor/nodes/COM_ConvertAlphaNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class ConvertAlphaNode : public Node { public: - ConvertAlphaNode(bNode *editorNode) : Node(editorNode) + ConvertAlphaNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.cc b/source/blender/compositor/nodes/COM_CornerPinNode.cc index 3cfa20f4e05..0f7b58db89d 100644 --- a/source/blender/compositor/nodes/COM_CornerPinNode.cc +++ b/source/blender/compositor/nodes/COM_CornerPinNode.cc @@ -16,20 +16,19 @@ */ #include "COM_CornerPinNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PlaneCornerPinOperation.h" namespace blender::compositor { -CornerPinNode::CornerPinNode(bNode *editorNode) : Node(editorNode) +CornerPinNode::CornerPinNode(bNode *editor_node) : Node(editor_node) { } -void CornerPinNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void CornerPinNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *input_image = this->getInputSocket(0); + NodeInput *input_image = this->get_input_socket(0); /* NOTE: socket order differs between UI node and operations: * bNode uses intuitive order following top-down layout: * upper-left, upper-right, lower-left, lower-right @@ -38,22 +37,22 @@ void CornerPinNode::convertToOperations(NodeConverter &converter, */ const int node_corner_index[4] = {3, 4, 2, 1}; - NodeOutput *output_warped_image = this->getOutputSocket(0); - NodeOutput *output_plane = this->getOutputSocket(1); + NodeOutput *output_warped_image = this->get_output_socket(0); + NodeOutput *output_plane = this->get_output_socket(1); PlaneCornerPinWarpImageOperation *warp_image_operation = new PlaneCornerPinWarpImageOperation(); - converter.addOperation(warp_image_operation); + converter.add_operation(warp_image_operation); PlaneCornerPinMaskOperation *plane_mask_operation = new PlaneCornerPinMaskOperation(); - converter.addOperation(plane_mask_operation); + converter.add_operation(plane_mask_operation); - converter.mapInputSocket(input_image, warp_image_operation->getInputSocket(0)); + converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0)); for (int i = 0; i < 4; i++) { - NodeInput *corner_input = getInputSocket(node_corner_index[i]); - converter.mapInputSocket(corner_input, warp_image_operation->getInputSocket(i + 1)); - converter.mapInputSocket(corner_input, plane_mask_operation->getInputSocket(i)); + NodeInput *corner_input = get_input_socket(node_corner_index[i]); + converter.map_input_socket(corner_input, warp_image_operation->get_input_socket(i + 1)); + converter.map_input_socket(corner_input, plane_mask_operation->get_input_socket(i)); } - converter.mapOutputSocket(output_warped_image, warp_image_operation->getOutputSocket()); - converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket()); + converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket()); + converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CornerPinNode.h b/source/blender/compositor/nodes/COM_CornerPinNode.h index 779e057ebb5..a1b4d107126 100644 --- a/source/blender/compositor/nodes/COM_CornerPinNode.h +++ b/source/blender/compositor/nodes/COM_CornerPinNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class CornerPinNode : public Node { public: - CornerPinNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CornerPinNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CropNode.cc b/source/blender/compositor/nodes/COM_CropNode.cc index 3f01062c789..e33af232f17 100644 --- a/source/blender/compositor/nodes/COM_CropNode.cc +++ b/source/blender/compositor/nodes/COM_CropNode.cc @@ -21,31 +21,31 @@ namespace blender::compositor { -CropNode::CropNode(bNode *editorNode) : Node(editorNode) +CropNode::CropNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void CropNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void CropNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = getbNode(); - NodeTwoXYs *cropSettings = (NodeTwoXYs *)node->storage; + bNode *node = get_bnode(); + NodeTwoXYs *crop_settings = (NodeTwoXYs *)node->storage; bool relative = (bool)node->custom2; - bool cropImage = (bool)node->custom1; + bool crop_image = (bool)node->custom1; CropBaseOperation *operation; - if (cropImage) { + if (crop_image) { operation = new CropImageOperation(); } else { operation = new CropOperation(); } - operation->setCropSettings(cropSettings); - operation->setRelative(relative); - converter.addOperation(operation); + operation->set_crop_settings(crop_settings); + operation->set_relative(relative); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CropNode.h b/source/blender/compositor/nodes/COM_CropNode.h index be3c9a268f9..55324bb37df 100644 --- a/source/blender/compositor/nodes/COM_CropNode.h +++ b/source/blender/compositor/nodes/COM_CropNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class CropNode : public Node { public: - CropNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + CropNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.cc b/source/blender/compositor/nodes/COM_CryptomatteNode.cc index c04d98d6a2b..605dc1dc84d 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.cc +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.cc @@ -18,18 +18,11 @@ #include "COM_CryptomatteNode.h" #include "BKE_node.h" -#include "BLI_assert.h" -#include "BLI_hash_mm3.h" -#include "BLI_listbase.h" -#include "BLI_string.h" #include "COM_ConvertOperation.h" -#include "COM_CryptomatteOperation.h" #include "COM_MultilayerImageOperation.h" #include "COM_RenderLayersProg.h" #include "COM_SetAlphaMultiplyOperation.h" #include "COM_SetColorOperation.h" -#include -#include namespace blender::compositor { @@ -37,41 +30,41 @@ namespace blender::compositor { /** \name Cryptomatte Base * \{ */ -void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void CryptomatteBaseNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeOutput *output_image_socket = this->getOutputSocket(0); + NodeOutput *output_image_socket = this->get_output_socket(0); - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeCryptomatte *cryptomatte_settings = static_cast(node->storage); CryptomatteOperation *cryptomatte_operation = create_cryptomatte_operation( converter, context, *node, cryptomatte_settings); - converter.addOperation(cryptomatte_operation); + converter.add_operation(cryptomatte_operation); - NodeOutput *output_matte_socket = this->getOutputSocket(1); + NodeOutput *output_matte_socket = this->get_output_socket(1); SeparateChannelOperation *extract_mask_operation = new SeparateChannelOperation; - extract_mask_operation->setChannel(3); - converter.addOperation(extract_mask_operation); - converter.addLink(cryptomatte_operation->getOutputSocket(0), - extract_mask_operation->getInputSocket(0)); - converter.mapOutputSocket(output_matte_socket, extract_mask_operation->getOutputSocket(0)); + extract_mask_operation->set_channel(3); + converter.add_operation(extract_mask_operation); + converter.add_link(cryptomatte_operation->get_output_socket(0), + extract_mask_operation->get_input_socket(0)); + converter.map_output_socket(output_matte_socket, extract_mask_operation->get_output_socket(0)); - NodeInput *input_image_socket = this->getInputSocket(0); + NodeInput *input_image_socket = this->get_input_socket(0); SetAlphaMultiplyOperation *apply_mask_operation = new SetAlphaMultiplyOperation(); - converter.mapInputSocket(input_image_socket, apply_mask_operation->getInputSocket(0)); - converter.addOperation(apply_mask_operation); - converter.addLink(extract_mask_operation->getOutputSocket(0), - apply_mask_operation->getInputSocket(1)); - converter.mapOutputSocket(output_image_socket, apply_mask_operation->getOutputSocket(0)); + converter.map_input_socket(input_image_socket, apply_mask_operation->get_input_socket(0)); + converter.add_operation(apply_mask_operation); + converter.add_link(extract_mask_operation->get_output_socket(0), + apply_mask_operation->get_input_socket(1)); + converter.map_output_socket(output_image_socket, apply_mask_operation->get_output_socket(0)); - NodeOutput *output_pick_socket = this->getOutputSocket(2); + NodeOutput *output_pick_socket = this->get_output_socket(2); SetAlphaMultiplyOperation *extract_pick_operation = new SetAlphaMultiplyOperation(); - converter.addOperation(extract_pick_operation); - converter.addInputValue(extract_pick_operation->getInputSocket(1), 1.0f); - converter.addLink(cryptomatte_operation->getOutputSocket(0), - extract_pick_operation->getInputSocket(0)); - converter.mapOutputSocket(output_pick_socket, extract_pick_operation->getOutputSocket(0)); + converter.add_operation(extract_pick_operation); + converter.add_input_value(extract_pick_operation->get_input_socket(1), 1.0f); + converter.add_link(cryptomatte_operation->get_output_socket(0), + extract_pick_operation->get_input_socket(0)); + converter.map_output_socket(output_pick_socket, extract_pick_operation->get_output_socket(0)); } /** \} */ @@ -83,7 +76,7 @@ void CryptomatteBaseNode::convertToOperations(NodeConverter &converter, static std::string prefix_from_node(const CompositorContext &context, const bNode &node) { char prefix[MAX_NAME]; - ntreeCompositCryptomatteLayerPrefix(context.getScene(), &node, prefix, sizeof(prefix)); + ntreeCompositCryptomatteLayerPrefix(context.get_scene(), &node, prefix, sizeof(prefix)); return std::string(prefix, BLI_strnlen(prefix, sizeof(prefix))); } @@ -127,7 +120,7 @@ void CryptomatteNode::input_operations_from_render_source( RenderLayer *render_layer = RE_GetRenderLayer(render_result, view_layer->name); if (render_layer) { LISTBASE_FOREACH (RenderPass *, render_pass, &render_layer->passes) { - if (context.has_explicit_view() && !STREQ(render_pass->view, context.getViewName())) { + if (context.has_explicit_view() && !STREQ(render_pass->view, context.get_view_name())) { continue; } @@ -135,10 +128,10 @@ void CryptomatteNode::input_operations_from_render_source( if (blender::StringRef(combined_name).startswith(prefix)) { RenderLayersProg *op = new RenderLayersProg( render_pass->name, DataType::Color, render_pass->channels); - op->setScene(scene); - op->setLayerId(view_layer_id); - op->setRenderData(context.getRenderData()); - op->setViewName(context.getViewName()); + op->set_scene(scene); + op->set_layer_id(view_layer_id); + op->set_render_data(context.get_render_data()); + op->set_view_name(context.get_view_name()); r_input_operations.append(op); } } @@ -164,7 +157,7 @@ void CryptomatteNode::input_operations_from_image_source( } ImageUser *iuser = &cryptomatte_settings->iuser; - BKE_image_user_frame_calc(image, iuser, context.getFramenumber()); + BKE_image_user_frame_calc(image, iuser, context.get_framenumber()); ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, nullptr); if (image->rr) { @@ -174,7 +167,7 @@ void CryptomatteNode::input_operations_from_image_source( /* Heuristic to match image name with scene names, check if the view name exists in the * image. */ view = BLI_findstringindex( - &image->rr->views, context.getViewName(), offsetof(RenderView, name)); + &image->rr->views, context.get_view_name(), offsetof(RenderView, name)); if (view == -1) { view = 0; } @@ -196,10 +189,10 @@ void CryptomatteNode::input_operations_from_image_source( if (blender::StringRef(combined_name).startswith(prefix)) { MultilayerColorOperation *op = new MultilayerColorOperation( render_layer, render_pass, view); - op->setImage(image); - op->setImageUser(iuser); + op->set_image(image); + op->set_image_user(iuser); iuser->layer = layer_index; - op->setFramenumber(context.getFramenumber()); + op->set_framenumber(context.get_framenumber()); r_input_operations.append(op); } } @@ -224,10 +217,10 @@ Vector CryptomatteNode::create_input_operations(const Composito if (input_operations.is_empty()) { SetColorOperation *op = new SetColorOperation(); - op->setChannel1(0.0f); - op->setChannel2(1.0f); - op->setChannel3(0.0f); - op->setChannel4(0.0f); + op->set_channel1(0.0f); + op->set_channel2(1.0f); + op->set_channel3(0.0f); + op->set_channel4(0.0f); input_operations.append(op); } return input_operations; @@ -241,11 +234,11 @@ CryptomatteOperation *CryptomatteNode::create_cryptomatte_operation( Vector input_operations = create_input_operations(context, node); CryptomatteOperation *operation = new CryptomatteOperation(input_operations.size()); LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) { - operation->addObjectIndex(cryptomatte_entry->encoded_hash); + operation->add_object_index(cryptomatte_entry->encoded_hash); } for (int i = 0; i < input_operations.size(); ++i) { - converter.addOperation(input_operations[i]); - converter.addLink(input_operations[i]->getOutputSocket(), operation->getInputSocket(i)); + converter.add_operation(input_operations[i]); + converter.add_link(input_operations[i]->get_output_socket(), operation->get_input_socket(i)); } return operation; } @@ -262,16 +255,16 @@ CryptomatteOperation *CryptomatteLegacyNode::create_cryptomatte_operation( const bNode &UNUSED(node), const NodeCryptomatte *cryptomatte_settings) const { - const int num_inputs = inputs.size() - 1; + const int num_inputs = inputs_.size() - 1; CryptomatteOperation *operation = new CryptomatteOperation(num_inputs); if (cryptomatte_settings) { LISTBASE_FOREACH (CryptomatteEntry *, cryptomatte_entry, &cryptomatte_settings->entries) { - operation->addObjectIndex(cryptomatte_entry->encoded_hash); + operation->add_object_index(cryptomatte_entry->encoded_hash); } } for (int i = 0; i < num_inputs; i++) { - converter.mapInputSocket(this->getInputSocket(i + 1), operation->getInputSocket(i)); + converter.map_input_socket(this->get_input_socket(i + 1), operation->get_input_socket(i)); } return operation; diff --git a/source/blender/compositor/nodes/COM_CryptomatteNode.h b/source/blender/compositor/nodes/COM_CryptomatteNode.h index eacb49e2033..7ca1084886b 100644 --- a/source/blender/compositor/nodes/COM_CryptomatteNode.h +++ b/source/blender/compositor/nodes/COM_CryptomatteNode.h @@ -38,8 +38,8 @@ class CryptomatteBaseNode : public Node { } public: - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; protected: virtual CryptomatteOperation *create_cryptomatte_operation( diff --git a/source/blender/compositor/nodes/COM_DefocusNode.cc b/source/blender/compositor/nodes/COM_DefocusNode.cc index 2023e4f7118..684f9014c3c 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.cc +++ b/source/blender/compositor/nodes/COM_DefocusNode.cc @@ -19,71 +19,65 @@ #include "COM_DefocusNode.h" #include "COM_BokehImageOperation.h" #include "COM_ConvertDepthToRadiusOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_FastGaussianBlurOperation.h" #include "COM_GammaCorrectOperation.h" #include "COM_MathBaseOperation.h" #include "COM_SetValueOperation.h" #include "COM_VariableSizeBokehBlurOperation.h" -#include "DNA_camera_types.h" -#include "DNA_node_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" namespace blender::compositor { -DefocusNode::DefocusNode(bNode *editorNode) : Node(editorNode) +DefocusNode::DefocusNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DefocusNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DefocusNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeDefocus *data = (NodeDefocus *)node->storage; - Scene *scene = node->id ? (Scene *)node->id : context.getScene(); + Scene *scene = node->id ? (Scene *)node->id : context.get_scene(); Object *camob = scene ? scene->camera : nullptr; - NodeOperation *radiusOperation; + NodeOperation *radius_operation; if (data->no_zbuf) { MathMultiplyOperation *multiply = new MathMultiplyOperation(); SetValueOperation *multiplier = new SetValueOperation(); - multiplier->setValue(data->scale); - SetValueOperation *maxRadius = new SetValueOperation(); - maxRadius->setValue(data->maxblur); + multiplier->set_value(data->scale); + SetValueOperation *max_radius = new SetValueOperation(); + max_radius->set_value(data->maxblur); MathMinimumOperation *minimize = new MathMinimumOperation(); - converter.addOperation(multiply); - converter.addOperation(multiplier); - converter.addOperation(maxRadius); - converter.addOperation(minimize); + converter.add_operation(multiply); + converter.add_operation(multiplier); + converter.add_operation(max_radius); + converter.add_operation(minimize); - converter.mapInputSocket(getInputSocket(1), multiply->getInputSocket(0)); - converter.addLink(multiplier->getOutputSocket(), multiply->getInputSocket(1)); - converter.addLink(multiply->getOutputSocket(), minimize->getInputSocket(0)); - converter.addLink(maxRadius->getOutputSocket(), minimize->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), multiply->get_input_socket(0)); + converter.add_link(multiplier->get_output_socket(), multiply->get_input_socket(1)); + converter.add_link(multiply->get_output_socket(), minimize->get_input_socket(0)); + converter.add_link(max_radius->get_output_socket(), minimize->get_input_socket(1)); - radiusOperation = minimize; + radius_operation = minimize; } else { ConvertDepthToRadiusOperation *radius_op = new ConvertDepthToRadiusOperation(); - radius_op->setCameraObject(camob); - radius_op->setfStop(data->fstop); - radius_op->setMaxRadius(data->maxblur); - converter.addOperation(radius_op); + radius_op->set_camera_object(camob); + radius_op->setf_stop(data->fstop); + radius_op->set_max_radius(data->maxblur); + converter.add_operation(radius_op); - converter.mapInputSocket(getInputSocket(1), radius_op->getInputSocket(0)); + converter.map_input_socket(get_input_socket(1), radius_op->get_input_socket(0)); FastGaussianBlurValueOperation *blur = new FastGaussianBlurValueOperation(); /* maintain close pixels so far Z values don't bleed into the foreground */ - blur->setOverlay(FAST_GAUSS_OVERLAY_MIN); - converter.addOperation(blur); + blur->set_overlay(FAST_GAUSS_OVERLAY_MIN); + converter.add_operation(blur); - converter.addLink(radius_op->getOutputSocket(0), blur->getInputSocket(0)); - radius_op->setPostBlur(blur); + converter.add_link(radius_op->get_output_socket(0), blur->get_input_socket(0)); + radius_op->set_post_blur(blur); - radiusOperation = blur; + radius_operation = blur; } NodeBokehImage *bokehdata = new NodeBokehImage(); @@ -98,49 +92,49 @@ void DefocusNode::convertToOperations(NodeConverter &converter, bokehdata->lensshift = 0.0f; BokehImageOperation *bokeh = new BokehImageOperation(); - bokeh->setData(bokehdata); - bokeh->deleteDataOnFinish(); - converter.addOperation(bokeh); + bokeh->set_data(bokehdata); + bokeh->delete_data_on_finish(); + converter.add_operation(bokeh); #ifdef COM_DEFOCUS_SEARCH InverseSearchRadiusOperation *search = new InverseSearchRadiusOperation(); - search->setMaxBlur(data->maxblur); - converter.addOperation(search); + search->set_max_blur(data->maxblur); + converter.add_operation(search); - converter.addLink(radiusOperation->getOutputSocket(0), search->getInputSocket(0)); + converter.add_link(radius_operation->get_output_socket(0), search->get_input_socket(0)); #endif VariableSizeBokehBlurOperation *operation = new VariableSizeBokehBlurOperation(); if (data->preview) { - operation->setQuality(eCompositorQuality::Low); + operation->set_quality(eCompositorQuality::Low); } else { - operation->setQuality(context.getQuality()); + operation->set_quality(context.get_quality()); } - operation->setMaxBlur(data->maxblur); - operation->setThreshold(data->bthresh); - converter.addOperation(operation); + operation->set_max_blur(data->maxblur); + operation->set_threshold(data->bthresh); + converter.add_operation(operation); - converter.addLink(bokeh->getOutputSocket(), operation->getInputSocket(1)); - converter.addLink(radiusOperation->getOutputSocket(), operation->getInputSocket(2)); + converter.add_link(bokeh->get_output_socket(), operation->get_input_socket(1)); + converter.add_link(radius_operation->get_output_socket(), operation->get_input_socket(2)); #ifdef COM_DEFOCUS_SEARCH - converter.addLink(search->getOutputSocket(), operation->getInputSocket(3)); + converter.add_link(search->get_output_socket(), operation->get_input_socket(3)); #endif if (data->gamco) { GammaCorrectOperation *correct = new GammaCorrectOperation(); - converter.addOperation(correct); + converter.add_operation(correct); GammaUncorrectOperation *inverse = new GammaUncorrectOperation(); - converter.addOperation(inverse); + converter.add_operation(inverse); - converter.mapInputSocket(getInputSocket(0), correct->getInputSocket(0)); - converter.addLink(correct->getOutputSocket(), operation->getInputSocket(0)); - converter.addLink(operation->getOutputSocket(), inverse->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), inverse->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), correct->get_input_socket(0)); + converter.add_link(correct->get_output_socket(), operation->get_input_socket(0)); + converter.add_link(operation->get_output_socket(), inverse->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), inverse->get_output_socket()); } else { - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(), operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_DefocusNode.h b/source/blender/compositor/nodes/COM_DefocusNode.h index 5e51a0ccd52..58993495bee 100644 --- a/source/blender/compositor/nodes/COM_DefocusNode.h +++ b/source/blender/compositor/nodes/COM_DefocusNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DefocusNode : public Node { public: - DefocusNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DefocusNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.cc b/source/blender/compositor/nodes/COM_DenoiseNode.cc index e58a9c7ba9a..2ff8870f716 100644 --- a/source/blender/compositor/nodes/COM_DenoiseNode.cc +++ b/source/blender/compositor/nodes/COM_DenoiseNode.cc @@ -17,31 +17,54 @@ */ #include "COM_DenoiseNode.h" #include "COM_DenoiseOperation.h" -#include "COM_MixOperation.h" -#include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { -DenoiseNode::DenoiseNode(bNode *editorNode) : Node(editorNode) +DenoiseNode::DenoiseNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DenoiseNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DenoiseNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + if (!COM_is_denoise_supported()) { + converter.map_output_socket(get_output_socket(0), + converter.add_input_proxy(get_input_socket(0), false)); + return; + } + + bNode *node = this->get_bnode(); NodeDenoise *denoise = (NodeDenoise *)node->storage; DenoiseOperation *operation = new DenoiseOperation(); - converter.addOperation(operation); - operation->setDenoiseSettings(denoise); + converter.add_operation(operation); + operation->set_denoise_settings(denoise); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + if (denoise && denoise->prefilter == CMP_NODE_DENOISE_PREFILTER_ACCURATE) { + { + DenoisePrefilterOperation *normal_prefilter = new DenoisePrefilterOperation( + DataType::Vector); + normal_prefilter->set_image_name("normal"); + converter.add_operation(normal_prefilter); + converter.map_input_socket(get_input_socket(1), normal_prefilter->get_input_socket(0)); + converter.add_link(normal_prefilter->get_output_socket(), operation->get_input_socket(1)); + } + { + DenoisePrefilterOperation *albedo_prefilter = new DenoisePrefilterOperation(DataType::Color); + albedo_prefilter->set_image_name("albedo"); + converter.add_operation(albedo_prefilter); + converter.map_input_socket(get_input_socket(2), albedo_prefilter->get_input_socket(0)); + converter.add_link(albedo_prefilter->get_output_socket(), operation->get_input_socket(2)); + } + } + else { + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + } + + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DenoiseNode.h b/source/blender/compositor/nodes/COM_DenoiseNode.h index 91be8e3e3ad..7e18c22c1e9 100644 --- a/source/blender/compositor/nodes/COM_DenoiseNode.h +++ b/source/blender/compositor/nodes/COM_DenoiseNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DenoiseNode : public Node { public: - DenoiseNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DenoiseNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.cc b/source/blender/compositor/nodes/COM_DespeckleNode.cc index beda479025d..12c9d50f214 100644 --- a/source/blender/compositor/nodes/COM_DespeckleNode.cc +++ b/source/blender/compositor/nodes/COM_DespeckleNode.cc @@ -17,36 +17,33 @@ */ #include "COM_DespeckleNode.h" -#include "BLI_math.h" #include "COM_DespeckleOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_scene_types.h" namespace blender::compositor { -DespeckleNode::DespeckleNode(bNode *editorNode) : Node(editorNode) +DespeckleNode::DespeckleNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DespeckleNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DespeckleNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = this->getbNode(); - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputImageSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + bNode *editor_node = this->get_bnode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_image_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); DespeckleOperation *operation = new DespeckleOperation(); - operation->setThreshold(editorNode->custom3); - operation->setThresholdNeighbor(editorNode->custom4); - converter.addOperation(operation); + operation->set_threshold(editor_node->custom3); + operation->set_threshold_neighbor(editor_node->custom4); + converter.add_operation(operation); - converter.mapInputSocket(inputImageSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.map_input_socket(input_image_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket(0)); + converter.add_preview(operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DespeckleNode.h b/source/blender/compositor/nodes/COM_DespeckleNode.h index 2f268e99e1b..2c3af600aed 100644 --- a/source/blender/compositor/nodes/COM_DespeckleNode.h +++ b/source/blender/compositor/nodes/COM_DespeckleNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DespeckleNode : public Node { public: - DespeckleNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DespeckleNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc index 8c989bfc04e..ceb5be7bd08 100644 --- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.cc @@ -17,42 +17,41 @@ */ #include "COM_DifferenceMatteNode.h" -#include "BKE_node.h" #include "COM_DifferenceMatteOperation.h" #include "COM_SetAlphaMultiplyOperation.h" namespace blender::compositor { -DifferenceMatteNode::DifferenceMatteNode(bNode *editorNode) : Node(editorNode) +DifferenceMatteNode::DifferenceMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DifferenceMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DifferenceMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputSocket2 = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); - bNode *editorNode = this->getbNode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_socket2 = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); + bNode *editor_node = this->get_bnode(); - DifferenceMatteOperation *operationSet = new DifferenceMatteOperation(); - operationSet->setSettings((NodeChroma *)editorNode->storage); - converter.addOperation(operationSet); + DifferenceMatteOperation *operation_set = new DifferenceMatteOperation(); + operation_set->set_settings((NodeChroma *)editor_node->storage); + converter.add_operation(operation_set); - converter.mapInputSocket(inputSocket, operationSet->getInputSocket(0)); - converter.mapInputSocket(inputSocket2, operationSet->getInputSocket(1)); - converter.mapOutputSocket(outputSocketMatte, operationSet->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation_set->get_input_socket(0)); + converter.map_input_socket(input_socket2, operation_set->get_input_socket(1)); + converter.map_output_socket(output_socket_matte, operation_set->get_output_socket(0)); SetAlphaMultiplyOperation *operation = new SetAlphaMultiplyOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(operationSet->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket()); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(operation_set->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h index a173c723192..f99d4022507 100644 --- a/source/blender/compositor/nodes/COM_DifferenceMatteNode.h +++ b/source/blender/compositor/nodes/COM_DifferenceMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DifferenceMatteNode : public Node { public: - DifferenceMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DifferenceMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.cc b/source/blender/compositor/nodes/COM_DilateErodeNode.cc index 1867014f64b..b121fcb3724 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.cc +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.cc @@ -17,135 +17,133 @@ */ #include "COM_DilateErodeNode.h" -#include "BLI_math.h" #include "COM_AntiAliasOperation.h" #include "COM_DilateErodeOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_GaussianAlphaXBlurOperation.h" #include "COM_GaussianAlphaYBlurOperation.h" namespace blender::compositor { -DilateErodeNode::DilateErodeNode(bNode *editorNode) : Node(editorNode) +DilateErodeNode::DilateErodeNode(bNode *editor_node) : Node(editor_node) { /* initialize node data */ - NodeBlurData *data = &m_alpha_blur; + NodeBlurData *data = &alpha_blur_; memset(data, 0, sizeof(NodeBlurData)); data->filtertype = R_FILTER_GAUSS; - if (editorNode->custom2 > 0) { - data->sizex = data->sizey = editorNode->custom2; + if (editor_node->custom2 > 0) { + data->sizex = data->sizey = editor_node->custom2; } else { - data->sizex = data->sizey = -editorNode->custom2; + data->sizex = data->sizey = -editor_node->custom2; } } -void DilateErodeNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DilateErodeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE_THRESH) { + bNode *editor_node = this->get_bnode(); + if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_THRESH) { DilateErodeThresholdOperation *operation = new DilateErodeThresholdOperation(); - operation->setDistance(editorNode->custom2); - operation->setInset(editorNode->custom3); - converter.addOperation(operation); + operation->set_distance(editor_node->custom2); + operation->set_inset(editor_node->custom3); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); - if (editorNode->custom3 < 2.0f) { - AntiAliasOperation *antiAlias = new AntiAliasOperation(); - converter.addOperation(antiAlias); + if (editor_node->custom3 < 2.0f) { + AntiAliasOperation *anti_alias = new AntiAliasOperation(); + converter.add_operation(anti_alias); - converter.addLink(operation->getOutputSocket(), antiAlias->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), antiAlias->getOutputSocket(0)); + converter.add_link(operation->get_output_socket(), anti_alias->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), anti_alias->get_output_socket(0)); } else { - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } - else if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE) { - if (editorNode->custom2 > 0) { + else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE) { + if (editor_node->custom2 > 0) { DilateDistanceOperation *operation = new DilateDistanceOperation(); - operation->setDistance(editorNode->custom2); - converter.addOperation(operation); + operation->set_distance(editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { ErodeDistanceOperation *operation = new ErodeDistanceOperation(); - operation->setDistance(-editorNode->custom2); - converter.addOperation(operation); + operation->set_distance(-editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } - else if (editorNode->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) { + else if (editor_node->custom1 == CMP_NODE_DILATEERODE_DISTANCE_FEATHER) { /* this uses a modified gaussian blur function otherwise its far too slow */ - eCompositorQuality quality = context.getQuality(); + eCompositorQuality quality = context.get_quality(); GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation(); - operationx->setData(&m_alpha_blur); - operationx->setQuality(quality); - operationx->setFalloff(PROP_SMOOTH); - converter.addOperation(operationx); + operationx->set_data(&alpha_blur_); + operationx->set_quality(quality); + operationx->set_falloff(PROP_SMOOTH); + converter.add_operation(operationx); - converter.mapInputSocket(getInputSocket(0), operationx->getInputSocket(0)); - // converter.mapInputSocket(getInputSocket(1), operationx->getInputSocket(1)); // no size input - // yet + converter.map_input_socket(get_input_socket(0), operationx->get_input_socket(0)); + // converter.map_input_socket(get_input_socket(1), operationx->get_input_socket(1)); // no size + // input yet GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); - operationy->setData(&m_alpha_blur); - operationy->setQuality(quality); - operationy->setFalloff(PROP_SMOOTH); - converter.addOperation(operationy); + operationy->set_data(&alpha_blur_); + operationy->set_quality(quality); + operationy->set_falloff(PROP_SMOOTH); + converter.add_operation(operationy); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); - // converter.mapInputSocket(getInputSocket(1), operationy->getInputSocket(1)); // no size input - // yet - converter.mapOutputSocket(getOutputSocket(0), operationy->getOutputSocket()); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); + // converter.map_input_socket(get_input_socket(1), operationy->get_input_socket(1)); // no size + // input yet + converter.map_output_socket(get_output_socket(0), operationy->get_output_socket()); - converter.addPreview(operationy->getOutputSocket()); + converter.add_preview(operationy->get_output_socket()); /* TODO? */ /* see gaussian blue node for original usage */ #if 0 - if (!connectedSizeSocket) { - operationx->setSize(size); - operationy->setSize(size); + if (!connected_size_socket) { + operationx->set_size(size); + operationy->set_size(size); } #else - operationx->setSize(1.0f); - operationy->setSize(1.0f); + operationx->set_size(1.0f); + operationy->set_size(1.0f); #endif - operationx->setSubtract(editorNode->custom2 < 0); - operationy->setSubtract(editorNode->custom2 < 0); + operationx->set_subtract(editor_node->custom2 < 0); + operationy->set_subtract(editor_node->custom2 < 0); - if (editorNode->storage) { - NodeDilateErode *data_storage = (NodeDilateErode *)editorNode->storage; - operationx->setFalloff(data_storage->falloff); - operationy->setFalloff(data_storage->falloff); + if (editor_node->storage) { + NodeDilateErode *data_storage = (NodeDilateErode *)editor_node->storage; + operationx->set_falloff(data_storage->falloff); + operationy->set_falloff(data_storage->falloff); } } else { - if (editorNode->custom2 > 0) { + if (editor_node->custom2 > 0) { DilateStepOperation *operation = new DilateStepOperation(); - operation->setIterations(editorNode->custom2); - converter.addOperation(operation); + operation->set_iterations(editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { ErodeStepOperation *operation = new ErodeStepOperation(); - operation->setIterations(-editorNode->custom2); - converter.addOperation(operation); + operation->set_iterations(-editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } } diff --git a/source/blender/compositor/nodes/COM_DilateErodeNode.h b/source/blender/compositor/nodes/COM_DilateErodeNode.h index 7684d7e3834..df6abe7a6b6 100644 --- a/source/blender/compositor/nodes/COM_DilateErodeNode.h +++ b/source/blender/compositor/nodes/COM_DilateErodeNode.h @@ -28,12 +28,12 @@ namespace blender::compositor { */ class DilateErodeNode : public Node { /** only used for blurring alpha, since the dilate/erode node doesn't have this. */ - NodeBlurData m_alpha_blur; + NodeBlurData alpha_blur_; public: - DilateErodeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DilateErodeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc index 90c4236bce8..b8e3e44f41f 100644 --- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc +++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.cc @@ -18,27 +18,25 @@ #include "COM_DirectionalBlurNode.h" #include "COM_DirectionalBlurOperation.h" -#include "COM_ExecutionSystem.h" -#include "DNA_node_types.h" namespace blender::compositor { -DirectionalBlurNode::DirectionalBlurNode(bNode *editorNode) : Node(editorNode) +DirectionalBlurNode::DirectionalBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DirectionalBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DirectionalBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeDBlurData *data = (NodeDBlurData *)this->getbNode()->storage; + NodeDBlurData *data = (NodeDBlurData *)this->get_bnode()->storage; DirectionalBlurOperation *operation = new DirectionalBlurOperation(); - operation->setQuality(context.getQuality()); - operation->setData(data); - converter.addOperation(operation); + operation->set_quality(context.get_quality()); + operation->set_data(data); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h index ce3ef378aaf..96a5122d134 100644 --- a/source/blender/compositor/nodes/COM_DirectionalBlurNode.h +++ b/source/blender/compositor/nodes/COM_DirectionalBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DirectionalBlurNode : public Node { public: - DirectionalBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DirectionalBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.cc b/source/blender/compositor/nodes/COM_DisplaceNode.cc index f2ec750c595..06208fb7d89 100644 --- a/source/blender/compositor/nodes/COM_DisplaceNode.cc +++ b/source/blender/compositor/nodes/COM_DisplaceNode.cc @@ -19,32 +19,31 @@ #include "COM_DisplaceNode.h" #include "COM_DisplaceOperation.h" #include "COM_DisplaceSimpleOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -DisplaceNode::DisplaceNode(bNode *editorNode) : Node(editorNode) +DisplaceNode::DisplaceNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DisplaceNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void DisplaceNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { NodeOperation *operation; - if (context.getQuality() == eCompositorQuality::Low) { + if (context.get_quality() == eCompositorQuality::Low) { operation = new DisplaceSimpleOperation(); } else { operation = new DisplaceOperation(); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(3)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DisplaceNode.h b/source/blender/compositor/nodes/COM_DisplaceNode.h index b2495839da3..394624e8f60 100644 --- a/source/blender/compositor/nodes/COM_DisplaceNode.h +++ b/source/blender/compositor/nodes/COM_DisplaceNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DisplaceNode : public Node { public: - DisplaceNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DisplaceNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc index 4450c4a2f4a..db78e60fbbe 100644 --- a/source/blender/compositor/nodes/COM_DistanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.cc @@ -17,85 +17,84 @@ */ #include "COM_DistanceMatteNode.h" -#include "BKE_node.h" #include "COM_ConvertOperation.h" -#include "COM_DistanceRGBMatteOperation.h" #include "COM_DistanceYCCMatteOperation.h" #include "COM_SetAlphaMultiplyOperation.h" namespace blender::compositor { -DistanceMatteNode::DistanceMatteNode(bNode *editorNode) : Node(editorNode) +DistanceMatteNode::DistanceMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DistanceMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DistanceMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); + bNode *editorsnode = get_bnode(); NodeChroma *storage = (NodeChroma *)editorsnode->storage; - NodeInput *inputSocketImage = this->getInputSocket(0); - NodeInput *inputSocketKey = this->getInputSocket(1); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + NodeInput *input_socket_image = this->get_input_socket(0); + NodeInput *input_socket_key = this->get_input_socket(1); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); - SetAlphaMultiplyOperation *operationAlpha = new SetAlphaMultiplyOperation(); - converter.addOperation(operationAlpha); + SetAlphaMultiplyOperation *operation_alpha = new SetAlphaMultiplyOperation(); + converter.add_operation(operation_alpha); /* work in RGB color space */ NodeOperation *operation; if (storage->channel == 1) { DistanceRGBMatteOperation *matte = new DistanceRGBMatteOperation(); - matte->setSettings(storage); - converter.addOperation(matte); + matte->set_settings(storage); + converter.add_operation(matte); - converter.mapInputSocket(inputSocketImage, matte->getInputSocket(0)); - converter.mapInputSocket(inputSocketImage, operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, matte->get_input_socket(0)); + converter.map_input_socket(input_socket_image, operation_alpha->get_input_socket(0)); - converter.mapInputSocket(inputSocketKey, matte->getInputSocket(1)); + converter.map_input_socket(input_socket_key, matte->get_input_socket(1)); operation = matte; } /* work in YCbCr color space */ else { DistanceYCCMatteOperation *matte = new DistanceYCCMatteOperation(); - matte->setSettings(storage); - converter.addOperation(matte); + matte->set_settings(storage); + converter.add_operation(matte); - ConvertRGBToYCCOperation *operationYCCImage = new ConvertRGBToYCCOperation(); - ConvertRGBToYCCOperation *operationYCCMatte = new ConvertRGBToYCCOperation(); - operationYCCImage->setMode(BLI_YCC_ITU_BT709); - operationYCCMatte->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(operationYCCImage); - converter.addOperation(operationYCCMatte); + ConvertRGBToYCCOperation *operation_yccimage = new ConvertRGBToYCCOperation(); + ConvertRGBToYCCOperation *operation_yccmatte = new ConvertRGBToYCCOperation(); + operation_yccimage->set_mode(BLI_YCC_ITU_BT709); + operation_yccmatte->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(operation_yccimage); + converter.add_operation(operation_yccmatte); - converter.mapInputSocket(inputSocketImage, operationYCCImage->getInputSocket(0)); - converter.addLink(operationYCCImage->getOutputSocket(), matte->getInputSocket(0)); - converter.addLink(operationYCCImage->getOutputSocket(), operationAlpha->getInputSocket(0)); + converter.map_input_socket(input_socket_image, operation_yccimage->get_input_socket(0)); + converter.add_link(operation_yccimage->get_output_socket(), matte->get_input_socket(0)); + converter.add_link(operation_yccimage->get_output_socket(), + operation_alpha->get_input_socket(0)); - converter.mapInputSocket(inputSocketKey, operationYCCMatte->getInputSocket(0)); - converter.addLink(operationYCCMatte->getOutputSocket(), matte->getInputSocket(1)); + converter.map_input_socket(input_socket_key, operation_yccmatte->get_input_socket(0)); + converter.add_link(operation_yccmatte->get_output_socket(), matte->get_input_socket(1)); operation = matte; } - converter.mapOutputSocket(outputSocketMatte, operation->getOutputSocket(0)); - converter.addLink(operation->getOutputSocket(), operationAlpha->getInputSocket(1)); + converter.map_output_socket(output_socket_matte, operation->get_output_socket(0)); + converter.add_link(operation->get_output_socket(), operation_alpha->get_input_socket(1)); if (storage->channel != 1) { ConvertYCCToRGBOperation *inv_convert = new ConvertYCCToRGBOperation(); - inv_convert->setMode(BLI_YCC_ITU_BT709); + inv_convert->set_mode(BLI_YCC_ITU_BT709); - converter.addOperation(inv_convert); - converter.addLink(operationAlpha->getOutputSocket(0), inv_convert->getInputSocket(0)); - converter.mapOutputSocket(outputSocketImage, inv_convert->getOutputSocket()); - converter.addPreview(inv_convert->getOutputSocket()); + converter.add_operation(inv_convert); + converter.add_link(operation_alpha->get_output_socket(0), inv_convert->get_input_socket(0)); + converter.map_output_socket(output_socket_image, inv_convert->get_output_socket()); + converter.add_preview(inv_convert->get_output_socket()); } else { - converter.mapOutputSocket(outputSocketImage, operationAlpha->getOutputSocket()); - converter.addPreview(operationAlpha->getOutputSocket()); + converter.map_output_socket(output_socket_image, operation_alpha->get_output_socket()); + converter.add_preview(operation_alpha->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_DistanceMatteNode.h b/source/blender/compositor/nodes/COM_DistanceMatteNode.h index 0baa531b4d2..d12e69d8065 100644 --- a/source/blender/compositor/nodes/COM_DistanceMatteNode.h +++ b/source/blender/compositor/nodes/COM_DistanceMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DistanceMatteNode : public Node { public: - DistanceMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DistanceMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc index 847dcc2f8f1..b88348ccc60 100644 --- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc +++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.cc @@ -18,29 +18,28 @@ #include "COM_DoubleEdgeMaskNode.h" #include "COM_DoubleEdgeMaskOperation.h" -#include "COM_ExecutionSystem.h" namespace blender::compositor { -DoubleEdgeMaskNode::DoubleEdgeMaskNode(bNode *editorNode) : Node(editorNode) +DoubleEdgeMaskNode::DoubleEdgeMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void DoubleEdgeMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void DoubleEdgeMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { DoubleEdgeMaskOperation *operation; - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); operation = new DoubleEdgeMaskOperation(); - operation->setAdjecentOnly(bnode->custom1); - operation->setKeepInside(bnode->custom2); - converter.addOperation(operation); + operation->set_adjecent_only(bnode->custom1); + operation->set_keep_inside(bnode->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h index 90e009747c1..99b3b1d0546 100644 --- a/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h +++ b/source/blender/compositor/nodes/COM_DoubleEdgeMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DoubleEdgeMaskNode : public Node { public: - DoubleEdgeMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + DoubleEdgeMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc index 3b4f5ca8c94..1f6e2ab2a6c 100644 --- a/source/blender/compositor/nodes/COM_EllipseMaskNode.cc +++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.cc @@ -18,59 +18,59 @@ #include "COM_EllipseMaskNode.h" #include "COM_EllipseMaskOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_ScaleOperation.h" #include "COM_SetValueOperation.h" namespace blender::compositor { -EllipseMaskNode::EllipseMaskNode(bNode *editorNode) : Node(editorNode) +EllipseMaskNode::EllipseMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void EllipseMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void EllipseMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); EllipseMaskOperation *operation; operation = new EllipseMaskOperation(); - operation->setData((NodeEllipseMask *)this->getbNode()->storage); - operation->setMaskType(this->getbNode()->custom1); - converter.addOperation(operation); + operation->set_data((NodeEllipseMask *)this->get_bnode()->storage); + operation->set_mask_type(this->get_bnode()->custom1); + converter.add_operation(operation); - if (inputSocket->isLinked()) { - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + if (input_socket->is_linked()) { + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket()); } else { /* Value operation to produce original transparent image */ - SetValueOperation *valueOperation = new SetValueOperation(); - valueOperation->setValue(0.0f); - converter.addOperation(valueOperation); + SetValueOperation *value_operation = new SetValueOperation(); + value_operation->set_value(0.0f); + converter.add_operation(value_operation); /* Scale that image up to render resolution */ - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); - ScaleFixedSizeOperation *scaleOperation = new ScaleFixedSizeOperation(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); + ScaleFixedSizeOperation *scale_operation = new ScaleFixedSizeOperation(); - scaleOperation->setIsAspect(false); - scaleOperation->setIsCrop(false); - scaleOperation->setOffset(0.0f, 0.0f); - scaleOperation->setNewWidth(rd->xsch * render_size_factor); - scaleOperation->setNewHeight(rd->ysch * render_size_factor); - scaleOperation->getInputSocket(0)->setResizeMode(ResizeMode::None); - converter.addOperation(scaleOperation); + scale_operation->set_is_aspect(false); + scale_operation->set_is_crop(false); + scale_operation->set_offset(0.0f, 0.0f); + scale_operation->set_new_width(rd->xsch * render_size_factor); + scale_operation->set_new_height(rd->ysch * render_size_factor); + scale_operation->get_input_socket(0)->set_resize_mode(ResizeMode::Align); + converter.add_operation(scale_operation); - converter.addLink(valueOperation->getOutputSocket(0), scaleOperation->getInputSocket(0)); - converter.addLink(scaleOperation->getOutputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_link(value_operation->get_output_socket(0), + scale_operation->get_input_socket(0)); + converter.add_link(scale_operation->get_output_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_EllipseMaskNode.h b/source/blender/compositor/nodes/COM_EllipseMaskNode.h index cbe189be9f6..4a582b63d91 100644 --- a/source/blender/compositor/nodes/COM_EllipseMaskNode.h +++ b/source/blender/compositor/nodes/COM_EllipseMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class EllipseMaskNode : public Node { public: - EllipseMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + EllipseMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FilterNode.cc b/source/blender/compositor/nodes/COM_FilterNode.cc index 351219155c2..2108e68cbec 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.cc +++ b/source/blender/compositor/nodes/COM_FilterNode.cc @@ -19,26 +19,23 @@ #include "COM_FilterNode.h" #include "BKE_node.h" #include "COM_ConvolutionEdgeFilterOperation.h" -#include "COM_ConvolutionFilterOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_MixOperation.h" namespace blender::compositor { -FilterNode::FilterNode(bNode *editorNode) : Node(editorNode) +FilterNode::FilterNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void FilterNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void FilterNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputImageSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_image_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); ConvolutionFilterOperation *operation = nullptr; - switch (this->getbNode()->custom1) { + switch (this->get_bnode()->custom1) { case CMP_FILT_SOFT: operation = new ConvolutionFilterOperation(); operation->set3x3Filter(1 / 16.0f, @@ -88,13 +85,13 @@ void FilterNode::convertToOperations(NodeConverter &converter, operation->set3x3Filter(0, 0, 0, 0, 1, 0, 0, 0, 0); break; } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputImageSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.map_input_socket(input_image_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket(0)); + converter.add_preview(operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FilterNode.h b/source/blender/compositor/nodes/COM_FilterNode.h index f7f4176cea5..c160e11fd9f 100644 --- a/source/blender/compositor/nodes/COM_FilterNode.h +++ b/source/blender/compositor/nodes/COM_FilterNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class FilterNode : public Node { public: - FilterNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + FilterNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FlipNode.cc b/source/blender/compositor/nodes/COM_FlipNode.cc index bca6cd3c4f7..ec9a4f9419b 100644 --- a/source/blender/compositor/nodes/COM_FlipNode.cc +++ b/source/blender/compositor/nodes/COM_FlipNode.cc @@ -18,23 +18,22 @@ #include "COM_FlipNode.h" -#include "COM_ExecutionSystem.h" #include "COM_FlipOperation.h" namespace blender::compositor { -FlipNode::FlipNode(bNode *editorNode) : Node(editorNode) +FlipNode::FlipNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void FlipNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void FlipNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); FlipOperation *operation = new FlipOperation(); - switch (this->getbNode()->custom1) { + switch (this->get_bnode()->custom1) { case 0: /* TODO: I didn't find any constants in the old implementation, * should I introduce them. */ operation->setFlipX(true); @@ -50,9 +49,9 @@ void FlipNode::convertToOperations(NodeConverter &converter, break; } - converter.addOperation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_FlipNode.h b/source/blender/compositor/nodes/COM_FlipNode.h index ee61d09fbba..40dc44edbbe 100644 --- a/source/blender/compositor/nodes/COM_FlipNode.h +++ b/source/blender/compositor/nodes/COM_FlipNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class FlipNode : public Node { public: - FlipNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + FlipNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GammaNode.cc b/source/blender/compositor/nodes/COM_GammaNode.cc index 52148a80a8f..3c4e885ff76 100644 --- a/source/blender/compositor/nodes/COM_GammaNode.cc +++ b/source/blender/compositor/nodes/COM_GammaNode.cc @@ -17,25 +17,24 @@ */ #include "COM_GammaNode.h" -#include "COM_ExecutionSystem.h" #include "COM_GammaOperation.h" namespace blender::compositor { -GammaNode::GammaNode(bNode *editorNode) : Node(editorNode) +GammaNode::GammaNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void GammaNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void GammaNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { GammaOperation *operation = new GammaOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GammaNode.h b/source/blender/compositor/nodes/COM_GammaNode.h index 29c9ed170fa..3c7a8ee4983 100644 --- a/source/blender/compositor/nodes/COM_GammaNode.h +++ b/source/blender/compositor/nodes/COM_GammaNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class GammaNode : public Node { public: - GammaNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + GammaNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GlareNode.cc b/source/blender/compositor/nodes/COM_GlareNode.cc index cd0b5306be1..77b1162f7cc 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.cc +++ b/source/blender/compositor/nodes/COM_GlareNode.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareNode.h" -#include "COM_FastGaussianBlurOperation.h" #include "COM_GlareFogGlowOperation.h" #include "COM_GlareGhostOperation.h" #include "COM_GlareSimpleStarOperation.h" @@ -25,19 +24,18 @@ #include "COM_GlareThresholdOperation.h" #include "COM_MixOperation.h" #include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { -GlareNode::GlareNode(bNode *editorNode) : Node(editorNode) +GlareNode::GlareNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void GlareNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void GlareNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); NodeGlare *glare = (NodeGlare *)node->storage; GlareBaseOperation *glareoperation = nullptr; @@ -57,30 +55,31 @@ void GlareNode::convertToOperations(NodeConverter &converter, break; } BLI_assert(glareoperation); - glareoperation->setGlareSettings(glare); + glareoperation->set_glare_settings(glare); - GlareThresholdOperation *thresholdOperation = new GlareThresholdOperation(); - thresholdOperation->setGlareSettings(glare); + GlareThresholdOperation *threshold_operation = new GlareThresholdOperation(); + threshold_operation->set_glare_settings(glare); SetValueOperation *mixvalueoperation = new SetValueOperation(); - mixvalueoperation->setValue(glare->mix); + mixvalueoperation->set_value(glare->mix); MixGlareOperation *mixoperation = new MixGlareOperation(); - mixoperation->setResolutionInputSocketIndex(1); - mixoperation->getInputSocket(2)->setResizeMode(ResizeMode::FitAny); + mixoperation->set_canvas_input_index(1); + mixoperation->get_input_socket(2)->set_resize_mode(ResizeMode::FitAny); - converter.addOperation(glareoperation); - converter.addOperation(thresholdOperation); - converter.addOperation(mixvalueoperation); - converter.addOperation(mixoperation); + converter.add_operation(glareoperation); + converter.add_operation(threshold_operation); + converter.add_operation(mixvalueoperation); + converter.add_operation(mixoperation); - converter.mapInputSocket(getInputSocket(0), thresholdOperation->getInputSocket(0)); - converter.addLink(thresholdOperation->getOutputSocket(), glareoperation->getInputSocket(0)); + converter.map_input_socket(get_input_socket(0), threshold_operation->get_input_socket(0)); + converter.add_link(threshold_operation->get_output_socket(), + glareoperation->get_input_socket(0)); - converter.addLink(mixvalueoperation->getOutputSocket(), mixoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(0), mixoperation->getInputSocket(1)); - converter.addLink(glareoperation->getOutputSocket(), mixoperation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(), mixoperation->getOutputSocket()); + converter.add_link(mixvalueoperation->get_output_socket(), mixoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(0), mixoperation->get_input_socket(1)); + converter.add_link(glareoperation->get_output_socket(), mixoperation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(), mixoperation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_GlareNode.h b/source/blender/compositor/nodes/COM_GlareNode.h index 7db5fa85e04..85e657984cd 100644 --- a/source/blender/compositor/nodes/COM_GlareNode.h +++ b/source/blender/compositor/nodes/COM_GlareNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class GlareNode : public Node { public: - GlareNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + GlareNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc index 5042d217f9a..6cf9a5356c4 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.cc @@ -19,50 +19,47 @@ #include "COM_HueSaturationValueCorrectNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_HueSaturationValueCorrectOperation.h" #include "COM_MixOperation.h" -#include "COM_SetColorOperation.h" -#include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { -HueSaturationValueCorrectNode::HueSaturationValueCorrectNode(bNode *editorNode) : Node(editorNode) +HueSaturationValueCorrectNode::HueSaturationValueCorrectNode(bNode *editor_node) + : Node(editor_node) { /* pass */ } -void HueSaturationValueCorrectNode::convertToOperations( +void HueSaturationValueCorrectNode::convert_to_operations( NodeConverter &converter, const CompositorContext & /*context*/) const { - NodeInput *valueSocket = this->getInputSocket(0); - NodeInput *colorSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); - bNode *editorsnode = getbNode(); + NodeInput *value_socket = this->get_input_socket(0); + NodeInput *color_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); + bNode *editorsnode = get_bnode(); CurveMapping *storage = (CurveMapping *)editorsnode->storage; ConvertRGBToHSVOperation *rgbToHSV = new ConvertRGBToHSVOperation(); - converter.addOperation(rgbToHSV); + converter.add_operation(rgbToHSV); ConvertHSVToRGBOperation *hsvToRGB = new ConvertHSVToRGBOperation(); - converter.addOperation(hsvToRGB); + converter.add_operation(hsvToRGB); HueSaturationValueCorrectOperation *changeHSV = new HueSaturationValueCorrectOperation(); - changeHSV->setCurveMapping(storage); - converter.addOperation(changeHSV); + changeHSV->set_curve_mapping(storage); + converter.add_operation(changeHSV); MixBlendOperation *blend = new MixBlendOperation(); - blend->setResolutionInputSocketIndex(1); - converter.addOperation(blend); + blend->set_canvas_input_index(1); + converter.add_operation(blend); - converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0)); - converter.addLink(rgbToHSV->getOutputSocket(), changeHSV->getInputSocket(0)); - converter.addLink(changeHSV->getOutputSocket(), hsvToRGB->getInputSocket(0)); - converter.addLink(hsvToRGB->getOutputSocket(), blend->getInputSocket(2)); - converter.mapInputSocket(colorSocket, blend->getInputSocket(1)); - converter.mapInputSocket(valueSocket, blend->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, blend->getOutputSocket()); + converter.map_input_socket(color_socket, rgbToHSV->get_input_socket(0)); + converter.add_link(rgbToHSV->get_output_socket(), changeHSV->get_input_socket(0)); + converter.add_link(changeHSV->get_output_socket(), hsvToRGB->get_input_socket(0)); + converter.add_link(hsvToRGB->get_output_socket(), blend->get_input_socket(2)); + converter.map_input_socket(color_socket, blend->get_input_socket(1)); + converter.map_input_socket(value_socket, blend->get_input_socket(0)); + converter.map_output_socket(output_socket, blend->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h index d75b2ba51ca..1f59c06cb9f 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h +++ b/source/blender/compositor/nodes/COM_HueSaturationValueCorrectNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class HueSaturationValueCorrectNode : public Node { public: - HueSaturationValueCorrectNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + HueSaturationValueCorrectNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc index 54d2caa75af..fa296f54d8f 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc +++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.cc @@ -20,52 +20,48 @@ #include "COM_ChangeHSVOperation.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_MixOperation.h" -#include "COM_SetColorOperation.h" -#include "COM_SetValueOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { -HueSaturationValueNode::HueSaturationValueNode(bNode *editorNode) : Node(editorNode) +HueSaturationValueNode::HueSaturationValueNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void HueSaturationValueNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void HueSaturationValueNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *colorSocket = this->getInputSocket(0); - NodeInput *hueSocket = this->getInputSocket(1); - NodeInput *saturationSocket = this->getInputSocket(2); - NodeInput *valueSocket = this->getInputSocket(3); - NodeInput *facSocket = this->getInputSocket(4); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *color_socket = this->get_input_socket(0); + NodeInput *hue_socket = this->get_input_socket(1); + NodeInput *saturation_socket = this->get_input_socket(2); + NodeInput *value_socket = this->get_input_socket(3); + NodeInput *fac_socket = this->get_input_socket(4); + NodeOutput *output_socket = this->get_output_socket(0); ConvertRGBToHSVOperation *rgbToHSV = new ConvertRGBToHSVOperation(); - converter.addOperation(rgbToHSV); + converter.add_operation(rgbToHSV); ConvertHSVToRGBOperation *hsvToRGB = new ConvertHSVToRGBOperation(); - converter.addOperation(hsvToRGB); + converter.add_operation(hsvToRGB); ChangeHSVOperation *changeHSV = new ChangeHSVOperation(); - converter.mapInputSocket(hueSocket, changeHSV->getInputSocket(1)); - converter.mapInputSocket(saturationSocket, changeHSV->getInputSocket(2)); - converter.mapInputSocket(valueSocket, changeHSV->getInputSocket(3)); - converter.addOperation(changeHSV); + converter.map_input_socket(hue_socket, changeHSV->get_input_socket(1)); + converter.map_input_socket(saturation_socket, changeHSV->get_input_socket(2)); + converter.map_input_socket(value_socket, changeHSV->get_input_socket(3)); + converter.add_operation(changeHSV); MixBlendOperation *blend = new MixBlendOperation(); - blend->setResolutionInputSocketIndex(1); - converter.addOperation(blend); + blend->set_canvas_input_index(1); + converter.add_operation(blend); - converter.mapInputSocket(colorSocket, rgbToHSV->getInputSocket(0)); - converter.addLink(rgbToHSV->getOutputSocket(), changeHSV->getInputSocket(0)); - converter.addLink(changeHSV->getOutputSocket(), hsvToRGB->getInputSocket(0)); - converter.addLink(hsvToRGB->getOutputSocket(), blend->getInputSocket(2)); - converter.mapInputSocket(colorSocket, blend->getInputSocket(1)); - converter.mapInputSocket(facSocket, blend->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, blend->getOutputSocket()); + converter.map_input_socket(color_socket, rgbToHSV->get_input_socket(0)); + converter.add_link(rgbToHSV->get_output_socket(), changeHSV->get_input_socket(0)); + converter.add_link(changeHSV->get_output_socket(), hsvToRGB->get_input_socket(0)); + converter.add_link(hsvToRGB->get_output_socket(), blend->get_input_socket(2)); + converter.map_input_socket(color_socket, blend->get_input_socket(1)); + converter.map_input_socket(fac_socket, blend->get_input_socket(0)); + converter.map_output_socket(output_socket, blend->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h index 0b295158cc7..abb7d0afebe 100644 --- a/source/blender/compositor/nodes/COM_HueSaturationValueNode.h +++ b/source/blender/compositor/nodes/COM_HueSaturationValueNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class HueSaturationValueNode : public Node { public: - HueSaturationValueNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + HueSaturationValueNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.cc b/source/blender/compositor/nodes/COM_IDMaskNode.cc index b51e79f2dea..4d761f25fd3 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.cc +++ b/source/blender/compositor/nodes/COM_IDMaskNode.cc @@ -17,52 +17,51 @@ */ #include "COM_IDMaskNode.h" -#include "COM_ExecutionSystem.h" #include "COM_IDMaskOperation.h" #include "COM_SMAAOperation.h" namespace blender::compositor { -IDMaskNode::IDMaskNode(bNode *editorNode) : Node(editorNode) +IDMaskNode::IDMaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void IDMaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void IDMaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); IDMaskOperation *operation; operation = new IDMaskOperation(); - operation->setObjectIndex(bnode->custom1); - converter.addOperation(operation); + operation->set_object_index(bnode->custom1); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - if (bnode->custom2 == 0 || context.getRenderData()->scemode & R_FULL_SAMPLE) { - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + if (bnode->custom2 == 0) { + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { SMAAEdgeDetectionOperation *operation1 = nullptr; operation1 = new SMAAEdgeDetectionOperation(); - converter.addOperation(operation1); + converter.add_operation(operation1); - converter.addLink(operation->getOutputSocket(0), operation1->getInputSocket(0)); + converter.add_link(operation->get_output_socket(0), operation1->get_input_socket(0)); /* Blending Weight Calculation Pixel Shader (Second Pass). */ SMAABlendingWeightCalculationOperation *operation2 = new SMAABlendingWeightCalculationOperation(); - converter.addOperation(operation2); + converter.add_operation(operation2); - converter.addLink(operation1->getOutputSocket(), operation2->getInputSocket(0)); + converter.add_link(operation1->get_output_socket(), operation2->get_input_socket(0)); /* Neighborhood Blending Pixel Shader (Third Pass). */ SMAANeighborhoodBlendingOperation *operation3 = new SMAANeighborhoodBlendingOperation(); - converter.addOperation(operation3); + converter.add_operation(operation3); - converter.addLink(operation->getOutputSocket(0), operation3->getInputSocket(0)); - converter.addLink(operation2->getOutputSocket(), operation3->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation3->getOutputSocket()); + converter.add_link(operation->get_output_socket(0), operation3->get_input_socket(0)); + converter.add_link(operation2->get_output_socket(), operation3->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation3->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_IDMaskNode.h b/source/blender/compositor/nodes/COM_IDMaskNode.h index f702732a8ed..8eba2af715f 100644 --- a/source/blender/compositor/nodes/COM_IDMaskNode.h +++ b/source/blender/compositor/nodes/COM_IDMaskNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class IDMaskNode : public Node { public: - IDMaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + IDMaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ImageNode.cc b/source/blender/compositor/nodes/COM_ImageNode.cc index 20476144efa..cc1c4109d9e 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cc +++ b/source/blender/compositor/nodes/COM_ImageNode.cc @@ -17,35 +17,30 @@ */ #include "COM_ImageNode.h" -#include "BKE_node.h" -#include "BLI_utildefines.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_ImageOperation.h" #include "COM_MultilayerImageOperation.h" -#include "COM_SeparateColorNode.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" namespace blender::compositor { -ImageNode::ImageNode(bNode *editorNode) : Node(editorNode) +ImageNode::ImageNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter, - RenderLayer *render_layer, - RenderPass *render_pass, - Image *image, - ImageUser *user, - int framenumber, - int outputsocketIndex, - int view, - DataType datatype) const +NodeOperation *ImageNode::do_multilayer_check(NodeConverter &converter, + RenderLayer *render_layer, + RenderPass *render_pass, + Image *image, + ImageUser *user, + int framenumber, + int outputsocket_index, + int view, + DataType datatype) const { - NodeOutput *outputSocket = this->getOutputSocket(outputsocketIndex); + NodeOutput *output_socket = this->get_output_socket(outputsocket_index); MultilayerBaseOperation *operation = nullptr; switch (datatype) { case DataType::Value: @@ -60,27 +55,27 @@ NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter, default: break; } - operation->setImage(image); - operation->setImageUser(user); - operation->setFramenumber(framenumber); + operation->set_image(image); + operation->set_image_user(user); + operation->set_framenumber(framenumber); - converter.addOperation(operation); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output_socket, operation->get_output_socket()); return operation; } -void ImageNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void ImageNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { /** Image output */ - NodeOutput *outputImage = this->getOutputSocket(0); - bNode *editorNode = this->getbNode(); - Image *image = (Image *)editorNode->id; - ImageUser *imageuser = (ImageUser *)editorNode->storage; - int framenumber = context.getFramenumber(); - bool outputStraightAlpha = (editorNode->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0; - BKE_image_user_frame_calc(image, imageuser, context.getFramenumber()); + NodeOutput *output_image = this->get_output_socket(0); + bNode *editor_node = this->get_bnode(); + Image *image = (Image *)editor_node->id; + ImageUser *imageuser = (ImageUser *)editor_node->storage; + int framenumber = context.get_framenumber(); + bool output_straight_alpha = (editor_node->custom1 & CMP_NODE_IMAGE_USE_STRAIGHT_OUTPUT) != 0; + BKE_image_user_frame_calc(image, imageuser, context.get_framenumber()); /* force a load, we assume iuser index will be set OK anyway */ if (image && image->type == IMA_TYPE_MULTILAYER) { bool is_multilayer_ok = false; @@ -90,17 +85,17 @@ void ImageNode::convertToOperations(NodeConverter &converter, if (rl) { is_multilayer_ok = true; - for (int64_t index = 0; index < outputs.size(); index++) { - NodeOutput *socket = outputs[index]; + for (int64_t index = 0; index < outputs_.size(); index++) { + NodeOutput *socket = outputs_[index]; NodeOperation *operation = nullptr; - bNodeSocket *bnodeSocket = socket->getbNodeSocket(); - NodeImageLayer *storage = (NodeImageLayer *)bnodeSocket->storage; + bNodeSocket *bnode_socket = socket->get_bnode_socket(); + NodeImageLayer *storage = (NodeImageLayer *)bnode_socket->storage; RenderPass *rpass = (RenderPass *)BLI_findstring( &rl->passes, storage->pass_name, offsetof(RenderPass, name)); int view = 0; if (STREQ(storage->pass_name, RE_PASSNAME_COMBINED) && - STREQ(bnodeSocket->name, "Alpha")) { + STREQ(bnode_socket->name, "Alpha")) { /* Alpha output is already handled with the associated combined output. */ continue; } @@ -114,7 +109,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, /* heuristic to match image name with scene names * check if the view name exists in the image */ view = BLI_findstringindex( - &image->rr->views, context.getViewName(), offsetof(RenderView, name)); + &image->rr->views, context.get_view_name(), offsetof(RenderView, name)); if (view == -1) { view = 0; } @@ -127,64 +122,64 @@ void ImageNode::convertToOperations(NodeConverter &converter, if (rpass) { switch (rpass->channels) { case 1: - operation = doMultilayerCheck(converter, - rl, - rpass, - image, - imageuser, - framenumber, - index, - view, - DataType::Value); + operation = do_multilayer_check(converter, + rl, + rpass, + image, + imageuser, + framenumber, + index, + view, + DataType::Value); break; /* using image operations for both 3 and 4 channels (RGB and RGBA respectively) */ /* XXX any way to detect actual vector images? */ case 3: - operation = doMultilayerCheck(converter, - rl, - rpass, - image, - imageuser, - framenumber, - index, - view, - DataType::Vector); + operation = do_multilayer_check(converter, + rl, + rpass, + image, + imageuser, + framenumber, + index, + view, + DataType::Vector); break; case 4: - operation = doMultilayerCheck(converter, - rl, - rpass, - image, - imageuser, - framenumber, - index, - view, - DataType::Color); + operation = do_multilayer_check(converter, + rl, + rpass, + image, + imageuser, + framenumber, + index, + view, + DataType::Color); break; default: /* dummy operation is added below */ break; } if (index == 0 && operation) { - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } - if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && !(bnodeSocket->flag & SOCK_UNAVAIL)) { - for (NodeOutput *alphaSocket : getOutputSockets()) { - bNodeSocket *bnodeAlphaSocket = alphaSocket->getbNodeSocket(); - if (!STREQ(bnodeAlphaSocket->name, "Alpha")) { + if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && !(bnode_socket->flag & SOCK_UNAVAIL)) { + for (NodeOutput *alpha_socket : get_output_sockets()) { + bNodeSocket *bnode_alpha_socket = alpha_socket->get_bnode_socket(); + if (!STREQ(bnode_alpha_socket->name, "Alpha")) { continue; } - NodeImageLayer *alphaStorage = (NodeImageLayer *)bnodeSocket->storage; - if (!STREQ(alphaStorage->pass_name, RE_PASSNAME_COMBINED)) { + NodeImageLayer *alpha_storage = (NodeImageLayer *)bnode_socket->storage; + if (!STREQ(alpha_storage->pass_name, RE_PASSNAME_COMBINED)) { continue; } SeparateChannelOperation *separate_operation; separate_operation = new SeparateChannelOperation(); - separate_operation->setChannel(3); - converter.addOperation(separate_operation); - converter.addLink(operation->getOutputSocket(), - separate_operation->getInputSocket(0)); - converter.mapOutputSocket(alphaSocket, separate_operation->getOutputSocket()); + separate_operation->set_channel(3); + converter.add_operation(separate_operation); + converter.add_link(operation->get_output_socket(), + separate_operation->get_input_socket(0)); + converter.map_output_socket(alpha_socket, separate_operation->get_output_socket()); break; } } @@ -192,7 +187,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, /* In case we can't load the layer. */ if (operation == nullptr) { - converter.setInvalidOutput(getOutputSocket(index)); + converter.set_invalid_output(get_output_socket(index)); } } } @@ -201,69 +196,70 @@ void ImageNode::convertToOperations(NodeConverter &converter, /* without this, multilayer that fail to load will crash blender T32490. */ if (is_multilayer_ok == false) { - for (NodeOutput *output : getOutputSockets()) { - converter.setInvalidOutput(output); + for (NodeOutput *output : get_output_sockets()) { + converter.set_invalid_output(output); } } } else { - const int64_t numberOfOutputs = getOutputSockets().size(); - if (numberOfOutputs > 0) { + const int64_t number_of_outputs = get_output_sockets().size(); + if (number_of_outputs > 0) { ImageOperation *operation = new ImageOperation(); - operation->setImage(image); - operation->setImageUser(imageuser); - operation->setFramenumber(framenumber); - operation->setRenderData(context.getRenderData()); - operation->setViewName(context.getViewName()); - converter.addOperation(operation); + operation->set_image(image); + operation->set_image_user(imageuser); + operation->set_framenumber(framenumber); + operation->set_render_data(context.get_render_data()); + operation->set_view_name(context.get_view_name()); + converter.add_operation(operation); - if (outputStraightAlpha) { - NodeOperation *alphaConvertOperation = new ConvertPremulToStraightOperation(); + if (output_straight_alpha) { + NodeOperation *alpha_convert_operation = new ConvertPremulToStraightOperation(); - converter.addOperation(alphaConvertOperation); - converter.mapOutputSocket(outputImage, alphaConvertOperation->getOutputSocket()); - converter.addLink(operation->getOutputSocket(0), alphaConvertOperation->getInputSocket(0)); + converter.add_operation(alpha_convert_operation); + converter.map_output_socket(output_image, alpha_convert_operation->get_output_socket()); + converter.add_link(operation->get_output_socket(0), + alpha_convert_operation->get_input_socket(0)); } else { - converter.mapOutputSocket(outputImage, operation->getOutputSocket()); + converter.map_output_socket(output_image, operation->get_output_socket()); } - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } - if (numberOfOutputs > 1) { - NodeOutput *alphaImage = this->getOutputSocket(1); - ImageAlphaOperation *alphaOperation = new ImageAlphaOperation(); - alphaOperation->setImage(image); - alphaOperation->setImageUser(imageuser); - alphaOperation->setFramenumber(framenumber); - alphaOperation->setRenderData(context.getRenderData()); - alphaOperation->setViewName(context.getViewName()); - converter.addOperation(alphaOperation); + if (number_of_outputs > 1) { + NodeOutput *alpha_image = this->get_output_socket(1); + ImageAlphaOperation *alpha_operation = new ImageAlphaOperation(); + alpha_operation->set_image(image); + alpha_operation->set_image_user(imageuser); + alpha_operation->set_framenumber(framenumber); + alpha_operation->set_render_data(context.get_render_data()); + alpha_operation->set_view_name(context.get_view_name()); + converter.add_operation(alpha_operation); - converter.mapOutputSocket(alphaImage, alphaOperation->getOutputSocket()); + converter.map_output_socket(alpha_image, alpha_operation->get_output_socket()); } - if (numberOfOutputs > 2) { - NodeOutput *depthImage = this->getOutputSocket(2); - ImageDepthOperation *depthOperation = new ImageDepthOperation(); - depthOperation->setImage(image); - depthOperation->setImageUser(imageuser); - depthOperation->setFramenumber(framenumber); - depthOperation->setRenderData(context.getRenderData()); - depthOperation->setViewName(context.getViewName()); - converter.addOperation(depthOperation); + if (number_of_outputs > 2) { + NodeOutput *depth_image = this->get_output_socket(2); + ImageDepthOperation *depth_operation = new ImageDepthOperation(); + depth_operation->set_image(image); + depth_operation->set_image_user(imageuser); + depth_operation->set_framenumber(framenumber); + depth_operation->set_render_data(context.get_render_data()); + depth_operation->set_view_name(context.get_view_name()); + converter.add_operation(depth_operation); - converter.mapOutputSocket(depthImage, depthOperation->getOutputSocket()); + converter.map_output_socket(depth_image, depth_operation->get_output_socket()); } - if (numberOfOutputs > 3) { + if (number_of_outputs > 3) { /* happens when unlinking image datablock from multilayer node */ - for (int i = 3; i < numberOfOutputs; i++) { - NodeOutput *output = this->getOutputSocket(i); + for (int i = 3; i < number_of_outputs; i++) { + NodeOutput *output = this->get_output_socket(i); NodeOperation *operation = nullptr; - switch (output->getDataType()) { + switch (output->get_data_type()) { case DataType::Value: { SetValueOperation *valueoperation = new SetValueOperation(); - valueoperation->setValue(0.0f); + valueoperation->set_value(0.0f); operation = valueoperation; break; } @@ -277,10 +273,10 @@ void ImageNode::convertToOperations(NodeConverter &converter, } case DataType::Color: { SetColorOperation *coloroperation = new SetColorOperation(); - coloroperation->setChannel1(0.0f); - coloroperation->setChannel2(0.0f); - coloroperation->setChannel3(0.0f); - coloroperation->setChannel4(0.0f); + coloroperation->set_channel1(0.0f); + coloroperation->set_channel2(0.0f); + coloroperation->set_channel3(0.0f); + coloroperation->set_channel4(0.0f); operation = coloroperation; break; } @@ -288,8 +284,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, if (operation) { /* not supporting multiview for this generic case */ - converter.addOperation(operation); - converter.mapOutputSocket(output, operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output, operation->get_output_socket()); } } } diff --git a/source/blender/compositor/nodes/COM_ImageNode.h b/source/blender/compositor/nodes/COM_ImageNode.h index 047cc496f83..7dd948be946 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.h +++ b/source/blender/compositor/nodes/COM_ImageNode.h @@ -34,20 +34,20 @@ namespace blender::compositor { */ class ImageNode : public Node { private: - NodeOperation *doMultilayerCheck(NodeConverter &converter, - RenderLayer *render_layer, - RenderPass *render_pass, - Image *image, - ImageUser *user, - int framenumber, - int outputsocketIndex, - int view, - DataType datatype) const; + NodeOperation *do_multilayer_check(NodeConverter &converter, + RenderLayer *render_layer, + RenderPass *render_pass, + Image *image, + ImageUser *user, + int framenumber, + int outputsocket_index, + int view, + DataType datatype) const; public: - ImageNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ImageNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_InpaintNode.cc b/source/blender/compositor/nodes/COM_InpaintNode.cc index 01ec5523939..1feb618d47a 100644 --- a/source/blender/compositor/nodes/COM_InpaintNode.cc +++ b/source/blender/compositor/nodes/COM_InpaintNode.cc @@ -17,32 +17,29 @@ */ #include "COM_InpaintNode.h" -#include "BLI_math.h" -#include "COM_ExecutionSystem.h" #include "COM_InpaintOperation.h" -#include "DNA_scene_types.h" namespace blender::compositor { -InpaintNode::InpaintNode(bNode *editorNode) : Node(editorNode) +InpaintNode::InpaintNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void InpaintNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void InpaintNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = this->getbNode(); + bNode *editor_node = this->get_bnode(); - /* if (editorNode->custom1 == CMP_NODE_INPAINT_SIMPLE) { */ + /* if (editor_node->custom1 == CMP_NODE_INPAINT_SIMPLE) { */ if (true) { InpaintSimpleOperation *operation = new InpaintSimpleOperation(); - operation->setIterations(editorNode->custom2); - converter.addOperation(operation); + operation->set_iterations(editor_node->custom2); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } diff --git a/source/blender/compositor/nodes/COM_InpaintNode.h b/source/blender/compositor/nodes/COM_InpaintNode.h index 3a10c11bf61..389ca1d56f4 100644 --- a/source/blender/compositor/nodes/COM_InpaintNode.h +++ b/source/blender/compositor/nodes/COM_InpaintNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class InpaintNode : public Node { public: - InpaintNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + InpaintNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_InvertNode.cc b/source/blender/compositor/nodes/COM_InvertNode.cc index 5fe2033227f..ffde5b1e12e 100644 --- a/source/blender/compositor/nodes/COM_InvertNode.cc +++ b/source/blender/compositor/nodes/COM_InvertNode.cc @@ -18,28 +18,27 @@ #include "COM_InvertNode.h" #include "BKE_node.h" -#include "COM_ExecutionSystem.h" #include "COM_InvertOperation.h" namespace blender::compositor { -InvertNode::InvertNode(bNode *editorNode) : Node(editorNode) +InvertNode::InvertNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void InvertNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void InvertNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { InvertOperation *operation = new InvertOperation(); - bNode *node = this->getbNode(); - operation->setColor(node->custom1 & CMP_CHAN_RGB); - operation->setAlpha(node->custom1 & CMP_CHAN_A); - converter.addOperation(operation); + bNode *node = this->get_bnode(); + operation->set_color(node->custom1 & CMP_CHAN_RGB); + operation->set_alpha(node->custom1 & CMP_CHAN_A); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_InvertNode.h b/source/blender/compositor/nodes/COM_InvertNode.h index 1cc975b8236..8f2eb258ab5 100644 --- a/source/blender/compositor/nodes/COM_InvertNode.h +++ b/source/blender/compositor/nodes/COM_InvertNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class InvertNode : public Node { public: - InvertNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + InvertNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_KeyingNode.cc b/source/blender/compositor/nodes/COM_KeyingNode.cc index 0af328a3601..d3130fdd2eb 100644 --- a/source/blender/compositor/nodes/COM_KeyingNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingNode.cc @@ -18,8 +18,6 @@ #include "COM_KeyingNode.h" -#include "COM_ExecutionSystem.h" - #include "COM_KeyingBlurOperation.h" #include "COM_KeyingClipOperation.h" #include "COM_KeyingDespillOperation.h" @@ -39,112 +37,114 @@ namespace blender::compositor { -KeyingNode::KeyingNode(bNode *editorNode) : Node(editorNode) +KeyingNode::KeyingNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -NodeOperationOutput *KeyingNode::setupPreBlur(NodeConverter &converter, - NodeInput *inputImage, - int size) const +NodeOperationOutput *KeyingNode::setup_pre_blur(NodeConverter &converter, + NodeInput *input_image, + int size) const { ConvertRGBToYCCOperation *convertRGBToYCCOperation = new ConvertRGBToYCCOperation(); - convertRGBToYCCOperation->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(convertRGBToYCCOperation); + convertRGBToYCCOperation->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(convertRGBToYCCOperation); - converter.mapInputSocket(inputImage, convertRGBToYCCOperation->getInputSocket(0)); + converter.map_input_socket(input_image, convertRGBToYCCOperation->get_input_socket(0)); - CombineChannelsOperation *combineOperation = new CombineChannelsOperation(); - converter.addOperation(combineOperation); + CombineChannelsOperation *combine_operation = new CombineChannelsOperation(); + converter.add_operation(combine_operation); for (int channel = 0; channel < 4; channel++) { - SeparateChannelOperation *separateOperation = new SeparateChannelOperation(); - separateOperation->setChannel(channel); - converter.addOperation(separateOperation); + SeparateChannelOperation *separate_operation = new SeparateChannelOperation(); + separate_operation->set_channel(channel); + converter.add_operation(separate_operation); - converter.addLink(convertRGBToYCCOperation->getOutputSocket(0), - separateOperation->getInputSocket(0)); + converter.add_link(convertRGBToYCCOperation->get_output_socket(0), + separate_operation->get_input_socket(0)); if (ELEM(channel, 0, 3)) { - converter.addLink(separateOperation->getOutputSocket(0), - combineOperation->getInputSocket(channel)); + converter.add_link(separate_operation->get_output_socket(0), + combine_operation->get_input_socket(channel)); } else { - KeyingBlurOperation *blurXOperation = new KeyingBlurOperation(); - blurXOperation->setSize(size); - blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X); - converter.addOperation(blurXOperation); + KeyingBlurOperation *blur_xoperation = new KeyingBlurOperation(); + blur_xoperation->set_size(size); + blur_xoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_X); + converter.add_operation(blur_xoperation); - KeyingBlurOperation *blurYOperation = new KeyingBlurOperation(); - blurYOperation->setSize(size); - blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y); - converter.addOperation(blurYOperation); + KeyingBlurOperation *blur_yoperation = new KeyingBlurOperation(); + blur_yoperation->set_size(size); + blur_yoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_Y); + converter.add_operation(blur_yoperation); - converter.addLink(separateOperation->getOutputSocket(), blurXOperation->getInputSocket(0)); - converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0)); - converter.addLink(blurYOperation->getOutputSocket(0), - combineOperation->getInputSocket(channel)); + converter.add_link(separate_operation->get_output_socket(), + blur_xoperation->get_input_socket(0)); + converter.add_link(blur_xoperation->get_output_socket(), + blur_yoperation->get_input_socket(0)); + converter.add_link(blur_yoperation->get_output_socket(0), + combine_operation->get_input_socket(channel)); } } ConvertYCCToRGBOperation *convertYCCToRGBOperation = new ConvertYCCToRGBOperation(); - convertYCCToRGBOperation->setMode(BLI_YCC_ITU_BT709); - converter.addOperation(convertYCCToRGBOperation); + convertYCCToRGBOperation->set_mode(BLI_YCC_ITU_BT709); + converter.add_operation(convertYCCToRGBOperation); - converter.addLink(combineOperation->getOutputSocket(0), - convertYCCToRGBOperation->getInputSocket(0)); + converter.add_link(combine_operation->get_output_socket(0), + convertYCCToRGBOperation->get_input_socket(0)); - return convertYCCToRGBOperation->getOutputSocket(0); + return convertYCCToRGBOperation->get_output_socket(0); } -NodeOperationOutput *KeyingNode::setupPostBlur(NodeConverter &converter, - NodeOperationOutput *postBlurInput, - int size) const +NodeOperationOutput *KeyingNode::setup_post_blur(NodeConverter &converter, + NodeOperationOutput *post_blur_input, + int size) const { - KeyingBlurOperation *blurXOperation = new KeyingBlurOperation(); - blurXOperation->setSize(size); - blurXOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_X); - converter.addOperation(blurXOperation); + KeyingBlurOperation *blur_xoperation = new KeyingBlurOperation(); + blur_xoperation->set_size(size); + blur_xoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_X); + converter.add_operation(blur_xoperation); - KeyingBlurOperation *blurYOperation = new KeyingBlurOperation(); - blurYOperation->setSize(size); - blurYOperation->setAxis(KeyingBlurOperation::BLUR_AXIS_Y); - converter.addOperation(blurYOperation); + KeyingBlurOperation *blur_yoperation = new KeyingBlurOperation(); + blur_yoperation->set_size(size); + blur_yoperation->set_axis(KeyingBlurOperation::BLUR_AXIS_Y); + converter.add_operation(blur_yoperation); - converter.addLink(postBlurInput, blurXOperation->getInputSocket(0)); - converter.addLink(blurXOperation->getOutputSocket(), blurYOperation->getInputSocket(0)); + converter.add_link(post_blur_input, blur_xoperation->get_input_socket(0)); + converter.add_link(blur_xoperation->get_output_socket(), blur_yoperation->get_input_socket(0)); - return blurYOperation->getOutputSocket(); + return blur_yoperation->get_output_socket(); } -NodeOperationOutput *KeyingNode::setupDilateErode(NodeConverter &converter, - NodeOperationOutput *dilateErodeInput, - int distance) const +NodeOperationOutput *KeyingNode::setup_dilate_erode(NodeConverter &converter, + NodeOperationOutput *dilate_erode_input, + int distance) const { - DilateDistanceOperation *dilateErodeOperation; + DilateDistanceOperation *dilate_erode_operation; if (distance > 0) { - dilateErodeOperation = new DilateDistanceOperation(); - dilateErodeOperation->setDistance(distance); + dilate_erode_operation = new DilateDistanceOperation(); + dilate_erode_operation->set_distance(distance); } else { - dilateErodeOperation = new ErodeDistanceOperation(); - dilateErodeOperation->setDistance(-distance); + dilate_erode_operation = new ErodeDistanceOperation(); + dilate_erode_operation->set_distance(-distance); } - converter.addOperation(dilateErodeOperation); + converter.add_operation(dilate_erode_operation); - converter.addLink(dilateErodeInput, dilateErodeOperation->getInputSocket(0)); + converter.add_link(dilate_erode_input, dilate_erode_operation->get_input_socket(0)); - return dilateErodeOperation->getOutputSocket(0); + return dilate_erode_operation->get_output_socket(0); } -NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter, - const CompositorContext &context, - NodeOperationOutput *featherInput, - int falloff, - int distance) const +NodeOperationOutput *KeyingNode::setup_feather(NodeConverter &converter, + const CompositorContext &context, + NodeOperationOutput *feather_input, + int falloff, + int distance) const { /* this uses a modified gaussian blur function otherwise its far too slow */ - eCompositorQuality quality = context.getQuality(); + eCompositorQuality quality = context.get_quality(); /* initialize node data */ NodeBlurData data; @@ -158,196 +158,198 @@ NodeOperationOutput *KeyingNode::setupFeather(NodeConverter &converter, } GaussianAlphaXBlurOperation *operationx = new GaussianAlphaXBlurOperation(); - operationx->setData(&data); - operationx->setQuality(quality); - operationx->setSize(1.0f); - operationx->setSubtract(distance < 0); - operationx->setFalloff(falloff); - converter.addOperation(operationx); + operationx->set_data(&data); + operationx->set_quality(quality); + operationx->set_size(1.0f); + operationx->set_subtract(distance < 0); + operationx->set_falloff(falloff); + converter.add_operation(operationx); GaussianAlphaYBlurOperation *operationy = new GaussianAlphaYBlurOperation(); - operationy->setData(&data); - operationy->setQuality(quality); - operationy->setSize(1.0f); - operationy->setSubtract(distance < 0); - operationy->setFalloff(falloff); - converter.addOperation(operationy); + operationy->set_data(&data); + operationy->set_quality(quality); + operationy->set_size(1.0f); + operationy->set_subtract(distance < 0); + operationy->set_falloff(falloff); + converter.add_operation(operationy); - converter.addLink(featherInput, operationx->getInputSocket(0)); - converter.addLink(operationx->getOutputSocket(), operationy->getInputSocket(0)); + converter.add_link(feather_input, operationx->get_input_socket(0)); + converter.add_link(operationx->get_output_socket(), operationy->get_input_socket(0)); - return operationy->getOutputSocket(); + return operationy->get_output_socket(); } -NodeOperationOutput *KeyingNode::setupDespill(NodeConverter &converter, - NodeOperationOutput *despillInput, - NodeInput *inputScreen, - float factor, - float colorBalance) const +NodeOperationOutput *KeyingNode::setup_despill(NodeConverter &converter, + NodeOperationOutput *despill_input, + NodeInput *input_screen, + float factor, + float color_balance) const { - KeyingDespillOperation *despillOperation = new KeyingDespillOperation(); - despillOperation->setDespillFactor(factor); - despillOperation->setColorBalance(colorBalance); - converter.addOperation(despillOperation); + KeyingDespillOperation *despill_operation = new KeyingDespillOperation(); + despill_operation->set_despill_factor(factor); + despill_operation->set_color_balance(color_balance); + converter.add_operation(despill_operation); - converter.addLink(despillInput, despillOperation->getInputSocket(0)); - converter.mapInputSocket(inputScreen, despillOperation->getInputSocket(1)); + converter.add_link(despill_input, despill_operation->get_input_socket(0)); + converter.map_input_socket(input_screen, despill_operation->get_input_socket(1)); - return despillOperation->getOutputSocket(0); + return despill_operation->get_output_socket(0); } -NodeOperationOutput *KeyingNode::setupClip(NodeConverter &converter, - NodeOperationOutput *clipInput, - int kernelRadius, - float kernelTolerance, - float clipBlack, - float clipWhite, - bool edgeMatte) const +NodeOperationOutput *KeyingNode::setup_clip(NodeConverter &converter, + NodeOperationOutput *clip_input, + int kernel_radius, + float kernel_tolerance, + float clip_black, + float clip_white, + bool edge_matte) const { - KeyingClipOperation *clipOperation = new KeyingClipOperation(); - clipOperation->setKernelRadius(kernelRadius); - clipOperation->setKernelTolerance(kernelTolerance); - clipOperation->setClipBlack(clipBlack); - clipOperation->setClipWhite(clipWhite); - clipOperation->setIsEdgeMatte(edgeMatte); - converter.addOperation(clipOperation); + KeyingClipOperation *clip_operation = new KeyingClipOperation(); + clip_operation->set_kernel_radius(kernel_radius); + clip_operation->set_kernel_tolerance(kernel_tolerance); + clip_operation->set_clip_black(clip_black); + clip_operation->set_clip_white(clip_white); + clip_operation->set_is_edge_matte(edge_matte); + converter.add_operation(clip_operation); - converter.addLink(clipInput, clipOperation->getInputSocket(0)); + converter.add_link(clip_input, clip_operation->get_input_socket(0)); - return clipOperation->getOutputSocket(0); + return clip_operation->get_output_socket(0); } -void KeyingNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void KeyingNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - NodeKeyingData *keying_data = (NodeKeyingData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + NodeKeyingData *keying_data = (NodeKeyingData *)editor_node->storage; - NodeInput *inputImage = this->getInputSocket(0); - NodeInput *inputScreen = this->getInputSocket(1); - NodeInput *inputGarbageMatte = this->getInputSocket(2); - NodeInput *inputCoreMatte = this->getInputSocket(3); - NodeOutput *outputImage = this->getOutputSocket(0); - NodeOutput *outputMatte = this->getOutputSocket(1); - NodeOutput *outputEdges = this->getOutputSocket(2); - NodeOperationOutput *postprocessedMatte = nullptr, *postprocessedImage = nullptr, - *edgesMatte = nullptr; + NodeInput *input_image = this->get_input_socket(0); + NodeInput *input_screen = this->get_input_socket(1); + NodeInput *input_garbage_matte = this->get_input_socket(2); + NodeInput *input_core_matte = this->get_input_socket(3); + NodeOutput *output_image = this->get_output_socket(0); + NodeOutput *output_matte = this->get_output_socket(1); + NodeOutput *output_edges = this->get_output_socket(2); + NodeOperationOutput *postprocessed_matte = nullptr, *postprocessed_image = nullptr, + *edges_matte = nullptr; /* keying operation */ - KeyingOperation *keyingOperation = new KeyingOperation(); - keyingOperation->setScreenBalance(keying_data->screen_balance); - converter.addOperation(keyingOperation); + KeyingOperation *keying_operation = new KeyingOperation(); + keying_operation->set_screen_balance(keying_data->screen_balance); + converter.add_operation(keying_operation); - converter.mapInputSocket(inputScreen, keyingOperation->getInputSocket(1)); + converter.map_input_socket(input_screen, keying_operation->get_input_socket(1)); if (keying_data->blur_pre) { /* Chroma pre-blur operation for input of keying operation. */ - NodeOperationOutput *preBlurredImage = setupPreBlur( - converter, inputImage, keying_data->blur_pre); - converter.addLink(preBlurredImage, keyingOperation->getInputSocket(0)); + NodeOperationOutput *pre_blurred_image = setup_pre_blur( + converter, input_image, keying_data->blur_pre); + converter.add_link(pre_blurred_image, keying_operation->get_input_socket(0)); } else { - converter.mapInputSocket(inputImage, keyingOperation->getInputSocket(0)); + converter.map_input_socket(input_image, keying_operation->get_input_socket(0)); } - postprocessedMatte = keyingOperation->getOutputSocket(); + postprocessed_matte = keying_operation->get_output_socket(); /* black / white clipping */ if (keying_data->clip_black > 0.0f || keying_data->clip_white < 1.0f) { - postprocessedMatte = setupClip(converter, - postprocessedMatte, - keying_data->edge_kernel_radius, - keying_data->edge_kernel_tolerance, - keying_data->clip_black, - keying_data->clip_white, - false); + postprocessed_matte = setup_clip(converter, + postprocessed_matte, + keying_data->edge_kernel_radius, + keying_data->edge_kernel_tolerance, + keying_data->clip_black, + keying_data->clip_white, + false); } /* output edge matte */ - edgesMatte = setupClip(converter, - postprocessedMatte, - keying_data->edge_kernel_radius, - keying_data->edge_kernel_tolerance, - keying_data->clip_black, - keying_data->clip_white, - true); + edges_matte = setup_clip(converter, + postprocessed_matte, + keying_data->edge_kernel_radius, + keying_data->edge_kernel_tolerance, + keying_data->clip_black, + keying_data->clip_white, + true); /* apply garbage matte */ - if (inputGarbageMatte->isLinked()) { - SetValueOperation *valueOperation = new SetValueOperation(); - valueOperation->setValue(1.0f); - converter.addOperation(valueOperation); + if (input_garbage_matte->is_linked()) { + SetValueOperation *value_operation = new SetValueOperation(); + value_operation->set_value(1.0f); + converter.add_operation(value_operation); - MathSubtractOperation *subtractOperation = new MathSubtractOperation(); - converter.addOperation(subtractOperation); + MathSubtractOperation *subtract_operation = new MathSubtractOperation(); + converter.add_operation(subtract_operation); - MathMinimumOperation *minOperation = new MathMinimumOperation(); - converter.addOperation(minOperation); + MathMinimumOperation *min_operation = new MathMinimumOperation(); + converter.add_operation(min_operation); - converter.addLink(valueOperation->getOutputSocket(), subtractOperation->getInputSocket(0)); - converter.mapInputSocket(inputGarbageMatte, subtractOperation->getInputSocket(1)); + converter.add_link(value_operation->get_output_socket(), + subtract_operation->get_input_socket(0)); + converter.map_input_socket(input_garbage_matte, subtract_operation->get_input_socket(1)); - converter.addLink(subtractOperation->getOutputSocket(), minOperation->getInputSocket(0)); - converter.addLink(postprocessedMatte, minOperation->getInputSocket(1)); + converter.add_link(subtract_operation->get_output_socket(), + min_operation->get_input_socket(0)); + converter.add_link(postprocessed_matte, min_operation->get_input_socket(1)); - postprocessedMatte = minOperation->getOutputSocket(); + postprocessed_matte = min_operation->get_output_socket(); } /* apply core matte */ - if (inputCoreMatte->isLinked()) { - MathMaximumOperation *maxOperation = new MathMaximumOperation(); - converter.addOperation(maxOperation); + if (input_core_matte->is_linked()) { + MathMaximumOperation *max_operation = new MathMaximumOperation(); + converter.add_operation(max_operation); - converter.mapInputSocket(inputCoreMatte, maxOperation->getInputSocket(0)); - converter.addLink(postprocessedMatte, maxOperation->getInputSocket(1)); + converter.map_input_socket(input_core_matte, max_operation->get_input_socket(0)); + converter.add_link(postprocessed_matte, max_operation->get_input_socket(1)); - postprocessedMatte = maxOperation->getOutputSocket(); + postprocessed_matte = max_operation->get_output_socket(); } /* apply blur on matte if needed */ if (keying_data->blur_post) { - postprocessedMatte = setupPostBlur(converter, postprocessedMatte, keying_data->blur_post); + postprocessed_matte = setup_post_blur(converter, postprocessed_matte, keying_data->blur_post); } /* matte dilate/erode */ if (keying_data->dilate_distance != 0) { - postprocessedMatte = setupDilateErode( - converter, postprocessedMatte, keying_data->dilate_distance); + postprocessed_matte = setup_dilate_erode( + converter, postprocessed_matte, keying_data->dilate_distance); } /* matte feather */ if (keying_data->feather_distance != 0) { - postprocessedMatte = setupFeather(converter, - context, - postprocessedMatte, - keying_data->feather_falloff, - keying_data->feather_distance); + postprocessed_matte = setup_feather(converter, + context, + postprocessed_matte, + keying_data->feather_falloff, + keying_data->feather_distance); } /* set alpha channel to output image */ - SetAlphaMultiplyOperation *alphaOperation = new SetAlphaMultiplyOperation(); - converter.addOperation(alphaOperation); + SetAlphaMultiplyOperation *alpha_operation = new SetAlphaMultiplyOperation(); + converter.add_operation(alpha_operation); - converter.mapInputSocket(inputImage, alphaOperation->getInputSocket(0)); - converter.addLink(postprocessedMatte, alphaOperation->getInputSocket(1)); + converter.map_input_socket(input_image, alpha_operation->get_input_socket(0)); + converter.add_link(postprocessed_matte, alpha_operation->get_input_socket(1)); - postprocessedImage = alphaOperation->getOutputSocket(); + postprocessed_image = alpha_operation->get_output_socket(); /* despill output image */ if (keying_data->despill_factor > 0.0f) { - postprocessedImage = setupDespill(converter, - postprocessedImage, - inputScreen, - keying_data->despill_factor, - keying_data->despill_balance); + postprocessed_image = setup_despill(converter, + postprocessed_image, + input_screen, + keying_data->despill_factor, + keying_data->despill_balance); } /* connect result to output sockets */ - converter.mapOutputSocket(outputImage, postprocessedImage); - converter.mapOutputSocket(outputMatte, postprocessedMatte); + converter.map_output_socket(output_image, postprocessed_image); + converter.map_output_socket(output_matte, postprocessed_matte); - if (edgesMatte) { - converter.mapOutputSocket(outputEdges, edgesMatte); + if (edges_matte) { + converter.map_output_socket(output_edges, edges_matte); } } diff --git a/source/blender/compositor/nodes/COM_KeyingNode.h b/source/blender/compositor/nodes/COM_KeyingNode.h index 6d5e3ca1883..ed70433fea3 100644 --- a/source/blender/compositor/nodes/COM_KeyingNode.h +++ b/source/blender/compositor/nodes/COM_KeyingNode.h @@ -28,37 +28,37 @@ namespace blender::compositor { */ class KeyingNode : public Node { protected: - NodeOperationOutput *setupPreBlur(NodeConverter &converter, - NodeInput *inputImage, - int size) const; - NodeOperationOutput *setupPostBlur(NodeConverter &converter, - NodeOperationOutput *postBlurInput, - int size) const; - NodeOperationOutput *setupDilateErode(NodeConverter &converter, - NodeOperationOutput *dilateErodeInput, - int distance) const; - NodeOperationOutput *setupFeather(NodeConverter &converter, - const CompositorContext &context, - NodeOperationOutput *featherInput, - int falloff, - int distance) const; - NodeOperationOutput *setupDespill(NodeConverter &converter, - NodeOperationOutput *despillInput, - NodeInput *inputScreen, - float factor, - float colorBalance) const; - NodeOperationOutput *setupClip(NodeConverter &converter, - NodeOperationOutput *clipInput, - int kernelRadius, - float kernelTolerance, - float clipBlack, - float clipWhite, - bool edgeMatte) const; + NodeOperationOutput *setup_pre_blur(NodeConverter &converter, + NodeInput *input_image, + int size) const; + NodeOperationOutput *setup_post_blur(NodeConverter &converter, + NodeOperationOutput *post_blur_input, + int size) const; + NodeOperationOutput *setup_dilate_erode(NodeConverter &converter, + NodeOperationOutput *dilate_erode_input, + int distance) const; + NodeOperationOutput *setup_feather(NodeConverter &converter, + const CompositorContext &context, + NodeOperationOutput *feather_input, + int falloff, + int distance) const; + NodeOperationOutput *setup_despill(NodeConverter &converter, + NodeOperationOutput *despill_input, + NodeInput *input_screen, + float factor, + float color_balance) const; + NodeOperationOutput *setup_clip(NodeConverter &converter, + NodeOperationOutput *clip_input, + int kernel_radius, + float kernel_tolerance, + float clip_black, + float clip_white, + bool edge_matte) const; public: - KeyingNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + KeyingNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc index 43574d02d80..bf5d7bb237a 100644 --- a/source/blender/compositor/nodes/COM_KeyingScreenNode.cc +++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.cc @@ -17,35 +17,32 @@ */ #include "COM_KeyingScreenNode.h" -#include "COM_ExecutionSystem.h" #include "COM_KeyingScreenOperation.h" -#include "DNA_movieclip_types.h" - namespace blender::compositor { -KeyingScreenNode::KeyingScreenNode(bNode *editorNode) : Node(editorNode) +KeyingScreenNode::KeyingScreenNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void KeyingScreenNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void KeyingScreenNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - MovieClip *clip = (MovieClip *)editorNode->id; - NodeKeyingScreenData *keyingscreen_data = (NodeKeyingScreenData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + MovieClip *clip = (MovieClip *)editor_node->id; + NodeKeyingScreenData *keyingscreen_data = (NodeKeyingScreenData *)editor_node->storage; - NodeOutput *outputScreen = this->getOutputSocket(0); + NodeOutput *output_screen = this->get_output_socket(0); /* Always connect the output image. */ KeyingScreenOperation *operation = new KeyingScreenOperation(); - operation->setMovieClip(clip); - operation->setTrackingObject(keyingscreen_data->tracking_object); - operation->setFramenumber(context.getFramenumber()); - converter.addOperation(operation); + operation->set_movie_clip(clip); + operation->set_tracking_object(keyingscreen_data->tracking_object); + operation->set_framenumber(context.get_framenumber()); + converter.add_operation(operation); - converter.mapOutputSocket(outputScreen, operation->getOutputSocket()); + converter.map_output_socket(output_screen, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_KeyingScreenNode.h b/source/blender/compositor/nodes/COM_KeyingScreenNode.h index f2ad3b344f1..3704081041f 100644 --- a/source/blender/compositor/nodes/COM_KeyingScreenNode.h +++ b/source/blender/compositor/nodes/COM_KeyingScreenNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class KeyingScreenNode : public Node { public: - KeyingScreenNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + KeyingScreenNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.cc b/source/blender/compositor/nodes/COM_LensDistortionNode.cc index f5226d31989..7cef21487df 100644 --- a/source/blender/compositor/nodes/COM_LensDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_LensDistortionNode.cc @@ -17,48 +17,47 @@ */ #include "COM_LensDistortionNode.h" -#include "COM_ExecutionSystem.h" #include "COM_ProjectorLensDistortionOperation.h" #include "COM_ScreenLensDistortionOperation.h" namespace blender::compositor { -LensDistortionNode::LensDistortionNode(bNode *editorNode) : Node(editorNode) +LensDistortionNode::LensDistortionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void LensDistortionNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void LensDistortionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorNode = this->getbNode(); - NodeLensDist *data = (NodeLensDist *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + NodeLensDist *data = (NodeLensDist *)editor_node->storage; if (data->proj) { ProjectorLensDistortionOperation *operation = new ProjectorLensDistortionOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } else { ScreenLensDistortionOperation *operation = new ScreenLensDistortionOperation(); - operation->setFit(data->fit); - operation->setJitter(data->jit); + operation->set_fit(data->fit); + operation->set_jitter(data->jit); - if (!getInputSocket(1)->isLinked()) { - operation->setDistortion(getInputSocket(1)->getEditorValueFloat()); + if (!get_input_socket(1)->is_linked()) { + operation->set_distortion(get_input_socket(1)->get_editor_value_float()); } - if (!getInputSocket(2)->isLinked()) { - operation->setDispersion(getInputSocket(2)->getEditorValueFloat()); + if (!get_input_socket(2)->is_linked()) { + operation->set_dispersion(get_input_socket(2)->get_editor_value_float()); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } diff --git a/source/blender/compositor/nodes/COM_LensDistortionNode.h b/source/blender/compositor/nodes/COM_LensDistortionNode.h index 4de1b0fe4da..154b8c20b86 100644 --- a/source/blender/compositor/nodes/COM_LensDistortionNode.h +++ b/source/blender/compositor/nodes/COM_LensDistortionNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class LensDistortionNode : public Node { public: - LensDistortionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + LensDistortionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc index 920da53231f..645cbe91fd2 100644 --- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc +++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.cc @@ -17,41 +17,39 @@ */ #include "COM_LuminanceMatteNode.h" -#include "BKE_node.h" -#include "COM_ConvertOperation.h" #include "COM_LuminanceMatteOperation.h" #include "COM_SetAlphaMultiplyOperation.h" namespace blender::compositor { -LuminanceMatteNode::LuminanceMatteNode(bNode *editorNode) : Node(editorNode) +LuminanceMatteNode::LuminanceMatteNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void LuminanceMatteNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void LuminanceMatteNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *editorsnode = getbNode(); - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocketImage = this->getOutputSocket(0); - NodeOutput *outputSocketMatte = this->getOutputSocket(1); + bNode *editorsnode = get_bnode(); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket_image = this->get_output_socket(0); + NodeOutput *output_socket_matte = this->get_output_socket(1); - LuminanceMatteOperation *operationSet = new LuminanceMatteOperation(); - operationSet->setSettings((NodeChroma *)editorsnode->storage); - converter.addOperation(operationSet); + LuminanceMatteOperation *operation_set = new LuminanceMatteOperation(); + operation_set->set_settings((NodeChroma *)editorsnode->storage); + converter.add_operation(operation_set); - converter.mapInputSocket(inputSocket, operationSet->getInputSocket(0)); - converter.mapOutputSocket(outputSocketMatte, operationSet->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation_set->get_input_socket(0)); + converter.map_output_socket(output_socket_matte, operation_set->get_output_socket(0)); SetAlphaMultiplyOperation *operation = new SetAlphaMultiplyOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(operationSet->getOutputSocket(), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketImage, operation->getOutputSocket()); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(operation_set->get_output_socket(), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_image, operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h index ef4ebc8ad92..1f934c73d1e 100644 --- a/source/blender/compositor/nodes/COM_LuminanceMatteNode.h +++ b/source/blender/compositor/nodes/COM_LuminanceMatteNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class LuminanceMatteNode : public Node { public: - LuminanceMatteNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + LuminanceMatteNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.cc b/source/blender/compositor/nodes/COM_MapRangeNode.cc index 718a6d9e47b..45f3113ca9e 100644 --- a/source/blender/compositor/nodes/COM_MapRangeNode.cc +++ b/source/blender/compositor/nodes/COM_MapRangeNode.cc @@ -18,36 +18,35 @@ #include "COM_MapRangeNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MapRangeOperation.h" namespace blender::compositor { -MapRangeNode::MapRangeNode(bNode *editorNode) : Node(editorNode) +MapRangeNode::MapRangeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MapRangeNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MapRangeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *valueSocket = this->getInputSocket(0); - NodeInput *sourceMinSocket = this->getInputSocket(1); - NodeInput *sourceMaxSocket = this->getInputSocket(2); - NodeInput *destMinSocket = this->getInputSocket(3); - NodeInput *destMaxSocket = this->getInputSocket(4); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *value_socket = this->get_input_socket(0); + NodeInput *source_min_socket = this->get_input_socket(1); + NodeInput *source_max_socket = this->get_input_socket(2); + NodeInput *dest_min_socket = this->get_input_socket(3); + NodeInput *dest_max_socket = this->get_input_socket(4); + NodeOutput *output_socket = this->get_output_socket(0); MapRangeOperation *operation = new MapRangeOperation(); - operation->setUseClamp(this->getbNode()->custom1); - converter.addOperation(operation); + operation->set_use_clamp(this->get_bnode()->custom1); + converter.add_operation(operation); - converter.mapInputSocket(valueSocket, operation->getInputSocket(0)); - converter.mapInputSocket(sourceMinSocket, operation->getInputSocket(1)); - converter.mapInputSocket(sourceMaxSocket, operation->getInputSocket(2)); - converter.mapInputSocket(destMinSocket, operation->getInputSocket(3)); - converter.mapInputSocket(destMaxSocket, operation->getInputSocket(4)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(value_socket, operation->get_input_socket(0)); + converter.map_input_socket(source_min_socket, operation->get_input_socket(1)); + converter.map_input_socket(source_max_socket, operation->get_input_socket(2)); + converter.map_input_socket(dest_min_socket, operation->get_input_socket(3)); + converter.map_input_socket(dest_max_socket, operation->get_input_socket(4)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapRangeNode.h b/source/blender/compositor/nodes/COM_MapRangeNode.h index ad6fd78a7d5..c9323ac00c2 100644 --- a/source/blender/compositor/nodes/COM_MapRangeNode.h +++ b/source/blender/compositor/nodes/COM_MapRangeNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MapRangeNode : public Node { public: - MapRangeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MapRangeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapUVNode.cc b/source/blender/compositor/nodes/COM_MapUVNode.cc index 4b7a9e8af0f..9c794165e0b 100644 --- a/source/blender/compositor/nodes/COM_MapUVNode.cc +++ b/source/blender/compositor/nodes/COM_MapUVNode.cc @@ -17,29 +17,28 @@ */ #include "COM_MapUVNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MapUVOperation.h" namespace blender::compositor { -MapUVNode::MapUVNode(bNode *editorNode) : Node(editorNode) +MapUVNode::MapUVNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MapUVNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MapUVNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); MapUVOperation *operation = new MapUVOperation(); - operation->setAlpha((float)node->custom1); - operation->setResolutionInputSocketIndex(1); - converter.addOperation(operation); + operation->set_alpha((float)node->custom1); + operation->set_canvas_input_index(1); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapUVNode.h b/source/blender/compositor/nodes/COM_MapUVNode.h index f7f4db167ea..eebfd0e66fa 100644 --- a/source/blender/compositor/nodes/COM_MapUVNode.h +++ b/source/blender/compositor/nodes/COM_MapUVNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class MapUVNode : public Node { public: - MapUVNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MapUVNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapValueNode.cc b/source/blender/compositor/nodes/COM_MapValueNode.cc index ae48bda6cb8..8c81a902863 100644 --- a/source/blender/compositor/nodes/COM_MapValueNode.cc +++ b/source/blender/compositor/nodes/COM_MapValueNode.cc @@ -18,30 +18,29 @@ #include "COM_MapValueNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MapValueOperation.h" namespace blender::compositor { -MapValueNode::MapValueNode(bNode *editorNode) : Node(editorNode) +MapValueNode::MapValueNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MapValueNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MapValueNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - TexMapping *storage = (TexMapping *)this->getbNode()->storage; + TexMapping *storage = (TexMapping *)this->get_bnode()->storage; - NodeInput *colorSocket = this->getInputSocket(0); - NodeOutput *valueSocket = this->getOutputSocket(0); + NodeInput *color_socket = this->get_input_socket(0); + NodeOutput *value_socket = this->get_output_socket(0); - MapValueOperation *convertProg = new MapValueOperation(); - convertProg->setSettings(storage); - converter.addOperation(convertProg); + MapValueOperation *convert_prog = new MapValueOperation(); + convert_prog->set_settings(storage); + converter.add_operation(convert_prog); - converter.mapInputSocket(colorSocket, convertProg->getInputSocket(0)); - converter.mapOutputSocket(valueSocket, convertProg->getOutputSocket(0)); + converter.map_input_socket(color_socket, convert_prog->get_input_socket(0)); + converter.map_output_socket(value_socket, convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MapValueNode.h b/source/blender/compositor/nodes/COM_MapValueNode.h index dcac1d6e3c5..4516d7eb016 100644 --- a/source/blender/compositor/nodes/COM_MapValueNode.h +++ b/source/blender/compositor/nodes/COM_MapValueNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MapValueNode : public Node { public: - MapValueNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MapValueNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MaskNode.cc b/source/blender/compositor/nodes/COM_MaskNode.cc index b5b23798160..b1e0506089e 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.cc +++ b/source/blender/compositor/nodes/COM_MaskNode.cc @@ -17,58 +17,55 @@ */ #include "COM_MaskNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MaskOperation.h" -#include "DNA_mask_types.h" - namespace blender::compositor { -MaskNode::MaskNode(bNode *editorNode) : Node(editorNode) +MaskNode::MaskNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MaskNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void MaskNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); - NodeOutput *outputMask = this->getOutputSocket(0); + NodeOutput *output_mask = this->get_output_socket(0); - bNode *editorNode = this->getbNode(); - NodeMask *data = (NodeMask *)editorNode->storage; - Mask *mask = (Mask *)editorNode->id; + bNode *editor_node = this->get_bnode(); + NodeMask *data = (NodeMask *)editor_node->storage; + Mask *mask = (Mask *)editor_node->id; /* Always connect the output image. */ MaskOperation *operation = new MaskOperation(); - if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED) { - operation->setMaskWidth(data->size_x); - operation->setMaskHeight(data->size_y); + if (editor_node->custom1 & CMP_NODEFLAG_MASK_FIXED) { + operation->set_mask_width(data->size_x); + operation->set_mask_height(data->size_y); } - else if (editorNode->custom1 & CMP_NODEFLAG_MASK_FIXED_SCENE) { - operation->setMaskWidth(data->size_x * render_size_factor); - operation->setMaskHeight(data->size_y * render_size_factor); + else if (editor_node->custom1 & CMP_NODEFLAG_MASK_FIXED_SCENE) { + operation->set_mask_width(data->size_x * render_size_factor); + operation->set_mask_height(data->size_y * render_size_factor); } else { - operation->setMaskWidth(rd->xsch * render_size_factor); - operation->setMaskHeight(rd->ysch * render_size_factor); + operation->set_mask_width(rd->xsch * render_size_factor); + operation->set_mask_height(rd->ysch * render_size_factor); } - operation->setMask(mask); - operation->setFramenumber(context.getFramenumber()); - operation->setFeather((bool)(editorNode->custom1 & CMP_NODEFLAG_MASK_NO_FEATHER) == 0); + operation->set_mask(mask); + operation->set_framenumber(context.get_framenumber()); + operation->set_feather((bool)(editor_node->custom1 & CMP_NODEFLAG_MASK_NO_FEATHER) == 0); - if ((editorNode->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) && (editorNode->custom2 > 1) && - (editorNode->custom3 > FLT_EPSILON)) { - operation->setMotionBlurSamples(editorNode->custom2); - operation->setMotionBlurShutter(editorNode->custom3); + if ((editor_node->custom1 & CMP_NODEFLAG_MASK_MOTION_BLUR) && (editor_node->custom2 > 1) && + (editor_node->custom3 > FLT_EPSILON)) { + operation->set_motion_blur_samples(editor_node->custom2); + operation->set_motion_blur_shutter(editor_node->custom3); } - converter.addOperation(operation); - converter.mapOutputSocket(outputMask, operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output_mask, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MaskNode.h b/source/blender/compositor/nodes/COM_MaskNode.h index 5890cf5957a..0d9d4284b27 100644 --- a/source/blender/compositor/nodes/COM_MaskNode.h +++ b/source/blender/compositor/nodes/COM_MaskNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MaskNode : public Node { public: - MaskNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MaskNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MathNode.cc b/source/blender/compositor/nodes/COM_MathNode.cc index dd0d8931d58..6102aed0f88 100644 --- a/source/blender/compositor/nodes/COM_MathNode.cc +++ b/source/blender/compositor/nodes/COM_MathNode.cc @@ -17,17 +17,16 @@ */ #include "COM_MathNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MathBaseOperation.h" namespace blender::compositor { -void MathNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MathNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { MathBaseOperation *operation = nullptr; - switch (this->getbNode()->custom1) { + switch (this->get_bnode()->custom1) { case NODE_MATH_ADD: operation = new MathAddOperation(); break; @@ -151,14 +150,14 @@ void MathNode::convertToOperations(NodeConverter &converter, } if (operation) { - bool useClamp = getbNode()->custom2; - operation->setUseClamp(useClamp); - converter.addOperation(operation); + bool use_clamp = get_bnode()->custom2; + operation->set_use_clamp(use_clamp); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_MathNode.h b/source/blender/compositor/nodes/COM_MathNode.h index 5db59e62bab..7f60fc03fcd 100644 --- a/source/blender/compositor/nodes/COM_MathNode.h +++ b/source/blender/compositor/nodes/COM_MathNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class MathNode : public Node { public: - MathNode(bNode *editorNode) : Node(editorNode) + MathNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MixNode.cc b/source/blender/compositor/nodes/COM_MixNode.cc index cfa8d0ee6a6..ae7736b4b67 100644 --- a/source/blender/compositor/nodes/COM_MixNode.cc +++ b/source/blender/compositor/nodes/COM_MixNode.cc @@ -20,97 +20,95 @@ #include "COM_MixOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_SetValueOperation.h" #include "DNA_material_types.h" /* the ramp types */ namespace blender::compositor { -MixNode::MixNode(bNode *editorNode) : Node(editorNode) +MixNode::MixNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MixNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void MixNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *valueSocket = this->getInputSocket(0); - NodeInput *color1Socket = this->getInputSocket(1); - NodeInput *color2Socket = this->getInputSocket(2); - NodeOutput *outputSocket = this->getOutputSocket(0); - bNode *editorNode = this->getbNode(); - bool useAlphaPremultiply = (this->getbNode()->custom2 & 1) != 0; - bool useClamp = (this->getbNode()->custom2 & 2) != 0; + NodeInput *value_socket = this->get_input_socket(0); + NodeInput *color1Socket = this->get_input_socket(1); + NodeInput *color2Socket = this->get_input_socket(2); + NodeOutput *output_socket = this->get_output_socket(0); + bNode *editor_node = this->get_bnode(); + bool use_alpha_premultiply = (this->get_bnode()->custom2 & 1) != 0; + bool use_clamp = (this->get_bnode()->custom2 & 2) != 0; - MixBaseOperation *convertProg; - switch (editorNode->custom1) { + MixBaseOperation *convert_prog; + switch (editor_node->custom1) { case MA_RAMP_ADD: - convertProg = new MixAddOperation(); + convert_prog = new MixAddOperation(); break; case MA_RAMP_MULT: - convertProg = new MixMultiplyOperation(); + convert_prog = new MixMultiplyOperation(); break; case MA_RAMP_LIGHT: - convertProg = new MixLightenOperation(); + convert_prog = new MixLightenOperation(); break; case MA_RAMP_BURN: - convertProg = new MixColorBurnOperation(); + convert_prog = new MixColorBurnOperation(); break; case MA_RAMP_HUE: - convertProg = new MixHueOperation(); + convert_prog = new MixHueOperation(); break; case MA_RAMP_COLOR: - convertProg = new MixColorOperation(); + convert_prog = new MixColorOperation(); break; case MA_RAMP_SOFT: - convertProg = new MixSoftLightOperation(); + convert_prog = new MixSoftLightOperation(); break; case MA_RAMP_SCREEN: - convertProg = new MixScreenOperation(); + convert_prog = new MixScreenOperation(); break; case MA_RAMP_LINEAR: - convertProg = new MixLinearLightOperation(); + convert_prog = new MixLinearLightOperation(); break; case MA_RAMP_DIFF: - convertProg = new MixDifferenceOperation(); + convert_prog = new MixDifferenceOperation(); break; case MA_RAMP_SAT: - convertProg = new MixSaturationOperation(); + convert_prog = new MixSaturationOperation(); break; case MA_RAMP_DIV: - convertProg = new MixDivideOperation(); + convert_prog = new MixDivideOperation(); break; case MA_RAMP_SUB: - convertProg = new MixSubtractOperation(); + convert_prog = new MixSubtractOperation(); break; case MA_RAMP_DARK: - convertProg = new MixDarkenOperation(); + convert_prog = new MixDarkenOperation(); break; case MA_RAMP_OVERLAY: - convertProg = new MixOverlayOperation(); + convert_prog = new MixOverlayOperation(); break; case MA_RAMP_VAL: - convertProg = new MixValueOperation(); + convert_prog = new MixValueOperation(); break; case MA_RAMP_DODGE: - convertProg = new MixDodgeOperation(); + convert_prog = new MixDodgeOperation(); break; case MA_RAMP_BLEND: default: - convertProg = new MixBlendOperation(); + convert_prog = new MixBlendOperation(); break; } - convertProg->setUseValueAlphaMultiply(useAlphaPremultiply); - convertProg->setUseClamp(useClamp); - converter.addOperation(convertProg); + convert_prog->set_use_value_alpha_multiply(use_alpha_premultiply); + convert_prog->set_use_clamp(use_clamp); + converter.add_operation(convert_prog); - converter.mapInputSocket(valueSocket, convertProg->getInputSocket(0)); - converter.mapInputSocket(color1Socket, convertProg->getInputSocket(1)); - converter.mapInputSocket(color2Socket, convertProg->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, convertProg->getOutputSocket(0)); + converter.map_input_socket(value_socket, convert_prog->get_input_socket(0)); + converter.map_input_socket(color1Socket, convert_prog->get_input_socket(1)); + converter.map_input_socket(color2Socket, convert_prog->get_input_socket(2)); + converter.map_output_socket(output_socket, convert_prog->get_output_socket(0)); - converter.addPreview(convertProg->getOutputSocket(0)); + converter.add_preview(convert_prog->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MixNode.h b/source/blender/compositor/nodes/COM_MixNode.h index 81f9c41871e..1a6bdf1cf57 100644 --- a/source/blender/compositor/nodes/COM_MixNode.h +++ b/source/blender/compositor/nodes/COM_MixNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MixNode : public Node { public: - MixNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MixNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.cc b/source/blender/compositor/nodes/COM_MovieClipNode.cc index b80071d27c7..b71e379304e 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.cc +++ b/source/blender/compositor/nodes/COM_MovieClipNode.cc @@ -17,10 +17,8 @@ */ #include "COM_MovieClipNode.h" -#include "COM_ConvertColorProfileOperation.h" -#include "COM_ExecutionSystem.h" + #include "COM_MovieClipOperation.h" -#include "COM_SetValueOperation.h" #include "BKE_movieclip.h" #include "BKE_tracking.h" @@ -31,58 +29,58 @@ namespace blender::compositor { -MovieClipNode::MovieClipNode(bNode *editorNode) : Node(editorNode) +MovieClipNode::MovieClipNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MovieClipNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void MovieClipNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeOutput *outputMovieClip = this->getOutputSocket(0); - NodeOutput *alphaMovieClip = this->getOutputSocket(1); - NodeOutput *offsetXMovieClip = this->getOutputSocket(2); - NodeOutput *offsetYMovieClip = this->getOutputSocket(3); - NodeOutput *scaleMovieClip = this->getOutputSocket(4); - NodeOutput *angleMovieClip = this->getOutputSocket(5); + NodeOutput *output_movie_clip = this->get_output_socket(0); + NodeOutput *alpha_movie_clip = this->get_output_socket(1); + NodeOutput *offset_xmovie_clip = this->get_output_socket(2); + NodeOutput *offset_ymovie_clip = this->get_output_socket(3); + NodeOutput *scale_movie_clip = this->get_output_socket(4); + NodeOutput *angle_movie_clip = this->get_output_socket(5); - bNode *editorNode = this->getbNode(); - MovieClip *movieClip = (MovieClip *)editorNode->id; - MovieClipUser *movieClipUser = (MovieClipUser *)editorNode->storage; - bool cacheFrame = !context.isRendering(); + bNode *editor_node = this->get_bnode(); + MovieClip *movie_clip = (MovieClip *)editor_node->id; + MovieClipUser *movie_clip_user = (MovieClipUser *)editor_node->storage; + bool cache_frame = !context.is_rendering(); ImBuf *ibuf = nullptr; - if (movieClip) { - if (cacheFrame) { - ibuf = BKE_movieclip_get_ibuf(movieClip, movieClipUser); + if (movie_clip) { + if (cache_frame) { + ibuf = BKE_movieclip_get_ibuf(movie_clip, movie_clip_user); } else { ibuf = BKE_movieclip_get_ibuf_flag( - movieClip, movieClipUser, movieClip->flag, MOVIECLIP_CACHE_SKIP); + movie_clip, movie_clip_user, movie_clip->flag, MOVIECLIP_CACHE_SKIP); } } /* Always connect the output image. */ MovieClipOperation *operation = new MovieClipOperation(); - operation->setMovieClip(movieClip); - operation->setMovieClipUser(movieClipUser); - operation->setFramenumber(context.getFramenumber()); - operation->setCacheFrame(cacheFrame); + operation->set_movie_clip(movie_clip); + operation->set_movie_clip_user(movie_clip_user); + operation->set_framenumber(context.get_framenumber()); + operation->set_cache_frame(cache_frame); - converter.addOperation(operation); - converter.mapOutputSocket(outputMovieClip, operation->getOutputSocket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_output_socket(output_movie_clip, operation->get_output_socket()); + converter.add_preview(operation->get_output_socket()); - MovieClipAlphaOperation *alphaOperation = new MovieClipAlphaOperation(); - alphaOperation->setMovieClip(movieClip); - alphaOperation->setMovieClipUser(movieClipUser); - alphaOperation->setFramenumber(context.getFramenumber()); - alphaOperation->setCacheFrame(cacheFrame); + MovieClipAlphaOperation *alpha_operation = new MovieClipAlphaOperation(); + alpha_operation->set_movie_clip(movie_clip); + alpha_operation->set_movie_clip_user(movie_clip_user); + alpha_operation->set_framenumber(context.get_framenumber()); + alpha_operation->set_cache_frame(cache_frame); - converter.addOperation(alphaOperation); - converter.mapOutputSocket(alphaMovieClip, alphaOperation->getOutputSocket()); + converter.add_operation(alpha_operation); + converter.map_output_socket(alpha_movie_clip, alpha_operation->get_output_socket()); - MovieTrackingStabilization *stab = &movieClip->tracking.stabilization; + MovieTrackingStabilization *stab = &movie_clip->tracking.stabilization; float loc[2], scale, angle; loc[0] = 0.0f; loc[1] = 0.0f; @@ -91,18 +89,18 @@ void MovieClipNode::convertToOperations(NodeConverter &converter, if (ibuf) { if (stab->flag & TRACKING_2D_STABILIZATION) { - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movieClip, - context.getFramenumber()); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip, + context.get_framenumber()); BKE_tracking_stabilization_data_get( - movieClip, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); + movie_clip, clip_framenr, ibuf->x, ibuf->y, loc, &scale, &angle); } } - converter.addOutputValue(offsetXMovieClip, loc[0]); - converter.addOutputValue(offsetYMovieClip, loc[1]); - converter.addOutputValue(scaleMovieClip, scale); - converter.addOutputValue(angleMovieClip, angle); + converter.add_output_value(offset_xmovie_clip, loc[0]); + converter.add_output_value(offset_ymovie_clip, loc[1]); + converter.add_output_value(scale_movie_clip, scale); + converter.add_output_value(angle_movie_clip, angle); if (ibuf) { IMB_freeImBuf(ibuf); diff --git a/source/blender/compositor/nodes/COM_MovieClipNode.h b/source/blender/compositor/nodes/COM_MovieClipNode.h index a469ce9e2a4..cf9e7a3e01e 100644 --- a/source/blender/compositor/nodes/COM_MovieClipNode.h +++ b/source/blender/compositor/nodes/COM_MovieClipNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class MovieClipNode : public Node { public: - MovieClipNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MovieClipNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc index 8f17ef8bb98..03b780198cd 100644 --- a/source/blender/compositor/nodes/COM_MovieDistortionNode.cc +++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.cc @@ -18,33 +18,31 @@ #include "COM_MovieDistortionNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MovieDistortionOperation.h" -#include "DNA_movieclip_types.h" namespace blender::compositor { -MovieDistortionNode::MovieDistortionNode(bNode *editorNode) : Node(editorNode) +MovieDistortionNode::MovieDistortionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void MovieDistortionNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void MovieDistortionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); MovieClip *clip = (MovieClip *)bnode->id; - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); MovieDistortionOperation *operation = new MovieDistortionOperation(bnode->custom1 == 1); - operation->setMovieClip(clip); - operation->setFramenumber(context.getFramenumber()); - converter.addOperation(operation); + operation->set_movie_clip(clip); + operation->set_framenumber(context.get_framenumber()); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_MovieDistortionNode.h b/source/blender/compositor/nodes/COM_MovieDistortionNode.h index 0c1610aa3d3..530b0dd6f97 100644 --- a/source/blender/compositor/nodes/COM_MovieDistortionNode.h +++ b/source/blender/compositor/nodes/COM_MovieDistortionNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class MovieDistortionNode : public Node { public: - MovieDistortionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + MovieDistortionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalNode.cc b/source/blender/compositor/nodes/COM_NormalNode.cc index 5a97b0932ef..f9875c6015c 100644 --- a/source/blender/compositor/nodes/COM_NormalNode.cc +++ b/source/blender/compositor/nodes/COM_NormalNode.cc @@ -17,44 +17,42 @@ */ #include "COM_NormalNode.h" -#include "BKE_node.h" #include "COM_DotproductOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_SetVectorOperation.h" namespace blender::compositor { -NormalNode::NormalNode(bNode *editorNode) : Node(editorNode) +NormalNode::NormalNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void NormalNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void NormalNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - NodeOutput *outputSocketDotproduct = this->getOutputSocket(1); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + NodeOutput *output_socket_dotproduct = this->get_output_socket(1); - SetVectorOperation *operationSet = new SetVectorOperation(); + SetVectorOperation *operation_set = new SetVectorOperation(); float normal[3]; - outputSocket->getEditorValueVector(normal); + output_socket->get_editor_value_vector(normal); /* animation can break normalization, this restores it */ normalize_v3(normal); - operationSet->setX(normal[0]); - operationSet->setY(normal[1]); - operationSet->setZ(normal[2]); - operationSet->setW(0.0f); - converter.addOperation(operationSet); + operation_set->setX(normal[0]); + operation_set->setY(normal[1]); + operation_set->setZ(normal[2]); + operation_set->setW(0.0f); + converter.add_operation(operation_set); - converter.mapOutputSocket(outputSocket, operationSet->getOutputSocket(0)); + converter.map_output_socket(output_socket, operation_set->get_output_socket(0)); DotproductOperation *operation = new DotproductOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(operationSet->getOutputSocket(0), operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocketDotproduct, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(operation_set->get_output_socket(0), operation->get_input_socket(1)); + converter.map_output_socket(output_socket_dotproduct, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalNode.h b/source/blender/compositor/nodes/COM_NormalNode.h index 6d5cbb394a0..2fcbb280c20 100644 --- a/source/blender/compositor/nodes/COM_NormalNode.h +++ b/source/blender/compositor/nodes/COM_NormalNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class NormalNode : public Node { public: - NormalNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + NormalNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.cc b/source/blender/compositor/nodes/COM_NormalizeNode.cc index 639dd8e5a51..0dbd9e1837c 100644 --- a/source/blender/compositor/nodes/COM_NormalizeNode.cc +++ b/source/blender/compositor/nodes/COM_NormalizeNode.cc @@ -17,24 +17,23 @@ */ #include "COM_NormalizeNode.h" -#include "COM_ExecutionSystem.h" #include "COM_NormalizeOperation.h" namespace blender::compositor { -NormalizeNode::NormalizeNode(bNode *editorNode) : Node(editorNode) +NormalizeNode::NormalizeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void NormalizeNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void NormalizeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { NormalizeOperation *operation = new NormalizeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_NormalizeNode.h b/source/blender/compositor/nodes/COM_NormalizeNode.h index 7770fc49b61..50d3b939b30 100644 --- a/source/blender/compositor/nodes/COM_NormalizeNode.h +++ b/source/blender/compositor/nodes/COM_NormalizeNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class NormalizeNode : public Node { public: - NormalizeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + NormalizeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cc b/source/blender/compositor/nodes/COM_OutputFileNode.cc index 25ec7331849..1a9908d4a93 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cc +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cc @@ -17,52 +17,46 @@ */ #include "COM_OutputFileNode.h" -#include "COM_ExecutionSystem.h" -#include "COM_OutputFileOperation.h" - -#include "BKE_scene.h" - -#include "BLI_path_util.h" namespace blender::compositor { -OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode) +OutputFileNode::OutputFileNode(bNode *editor_node) : Node(editor_node) { /* pass */ } void OutputFileNode::add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const { - for (NodeInput *input : inputs) { + for (NodeInput *input : inputs_) { NodeImageMultiFileSocket *sockdata = - (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage; + (NodeImageMultiFileSocket *)input->get_bnode_socket()->storage; /* NOTE: layer becomes an empty placeholder if the input is not linked. */ - operation.add_layer(sockdata->layer, input->getDataType(), input->isLinked()); + operation.add_layer(sockdata->layer, input->get_data_type(), input->is_linked()); } } void OutputFileNode::map_input_sockets(NodeConverter &converter, OutputOpenExrMultiLayerOperation &operation) const { - bool previewAdded = false; + bool preview_added = false; int index = 0; - for (NodeInput *input : inputs) { - converter.mapInputSocket(input, operation.getInputSocket(index++)); + for (NodeInput *input : inputs_) { + converter.map_input_socket(input, operation.get_input_socket(index++)); - if (!previewAdded) { - converter.addNodeInputPreview(input); - previewAdded = true; + if (!preview_added) { + converter.add_node_input_preview(input); + preview_added = true; } } } -void OutputFileNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void OutputFileNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeImageMultiFile *storage = (NodeImageMultiFile *)this->getbNode()->storage; - const bool is_multiview = (context.getRenderData()->scemode & R_MULTIVIEW) != 0; + NodeImageMultiFile *storage = (NodeImageMultiFile *)this->get_bnode()->storage; + const bool is_multiview = (context.get_render_data()->scemode & R_MULTIVIEW) != 0; - if (!context.isRendering()) { + if (!context.is_rendering()) { /* only output files when rendering a sequence - * otherwise, it overwrites the output files just * scrubbing through the timeline when the compositor updates. @@ -73,40 +67,40 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, if (storage->format.imtype == R_IMF_IMTYPE_MULTILAYER) { const bool use_half_float = (storage->format.depth == R_IMF_CHAN_DEPTH_16); /* single output operation for the multilayer file */ - OutputOpenExrMultiLayerOperation *outputOperation; + OutputOpenExrMultiLayerOperation *output_operation; if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) { - outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(context.getScene(), - context.getRenderData(), - context.getbNodeTree(), - storage->base_path, - storage->format.exr_codec, - use_half_float, - context.getViewName()); + output_operation = new OutputOpenExrMultiLayerMultiViewOperation(context.get_scene(), + context.get_render_data(), + context.get_bnodetree(), + storage->base_path, + storage->format.exr_codec, + use_half_float, + context.get_view_name()); } else { - outputOperation = new OutputOpenExrMultiLayerOperation(context.getScene(), - context.getRenderData(), - context.getbNodeTree(), - storage->base_path, - storage->format.exr_codec, - use_half_float, - context.getViewName()); + output_operation = new OutputOpenExrMultiLayerOperation(context.get_scene(), + context.get_render_data(), + context.get_bnodetree(), + storage->base_path, + storage->format.exr_codec, + use_half_float, + context.get_view_name()); } - converter.addOperation(outputOperation); + converter.add_operation(output_operation); /* First add all inputs. Inputs are stored in a Vector and can be moved to a different * memory address during this time. */ - add_input_sockets(*outputOperation); + add_input_sockets(*output_operation); /* After adding the sockets the memory addresses will stick. */ - map_input_sockets(converter, *outputOperation); + map_input_sockets(converter, *output_operation); } else { /* single layer format */ - bool previewAdded = false; - for (NodeInput *input : inputs) { - if (input->isLinked()) { + bool preview_added = false; + for (NodeInput *input : inputs_) { + if (input->is_linked()) { NodeImageMultiFileSocket *sockdata = - (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage; + (NodeImageMultiFileSocket *)input->get_bnode_socket()->storage; ImageFormatData *format = (sockdata->use_node_format ? &storage->format : &sockdata->format); char path[FILE_MAX]; @@ -114,50 +108,50 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, /* combine file path for the input */ BLI_join_dirfile(path, FILE_MAX, storage->base_path, sockdata->path); - NodeOperation *outputOperation = nullptr; + NodeOperation *output_operation = nullptr; if (is_multiview && format->views_format == R_IMF_VIEWS_MULTIVIEW) { - outputOperation = new OutputOpenExrSingleLayerMultiViewOperation( - context.getRenderData(), - context.getbNodeTree(), - input->getDataType(), + output_operation = new OutputOpenExrSingleLayerMultiViewOperation( + context.get_render_data(), + context.get_bnodetree(), + input->get_data_type(), format, path, - context.getViewSettings(), - context.getDisplaySettings(), - context.getViewName(), + context.get_view_settings(), + context.get_display_settings(), + context.get_view_name(), sockdata->save_as_render); } else if ((!is_multiview) || (format->views_format == R_IMF_VIEWS_INDIVIDUAL)) { - outputOperation = new OutputSingleLayerOperation(context.getRenderData(), - context.getbNodeTree(), - input->getDataType(), - format, - path, - context.getViewSettings(), - context.getDisplaySettings(), - context.getViewName(), - sockdata->save_as_render); + output_operation = new OutputSingleLayerOperation(context.get_render_data(), + context.get_bnodetree(), + input->get_data_type(), + format, + path, + context.get_view_settings(), + context.get_display_settings(), + context.get_view_name(), + sockdata->save_as_render); } else { /* R_IMF_VIEWS_STEREO_3D */ - outputOperation = new OutputStereoOperation(context.getRenderData(), - context.getbNodeTree(), - input->getDataType(), - format, - path, - sockdata->layer, - context.getViewSettings(), - context.getDisplaySettings(), - context.getViewName(), - sockdata->save_as_render); + output_operation = new OutputStereoOperation(context.get_render_data(), + context.get_bnodetree(), + input->get_data_type(), + format, + path, + sockdata->layer, + context.get_view_settings(), + context.get_display_settings(), + context.get_view_name(), + sockdata->save_as_render); } - converter.addOperation(outputOperation); - converter.mapInputSocket(input, outputOperation->getInputSocket(0)); + converter.add_operation(output_operation); + converter.map_input_socket(input, output_operation->get_input_socket(0)); - if (!previewAdded) { - converter.addNodeInputPreview(input); - previewAdded = true; + if (!preview_added) { + converter.add_node_input_preview(input); + preview_added = true; } } } diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.h b/source/blender/compositor/nodes/COM_OutputFileNode.h index c64128a708f..4c5575cc6b9 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.h +++ b/source/blender/compositor/nodes/COM_OutputFileNode.h @@ -32,9 +32,9 @@ namespace blender::compositor { */ class OutputFileNode : public Node { public: - OutputFileNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + OutputFileNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; private: void add_input_sockets(OutputOpenExrMultiLayerOperation &operation) const; diff --git a/source/blender/compositor/nodes/COM_PixelateNode.cc b/source/blender/compositor/nodes/COM_PixelateNode.cc index 396f339e5a2..251ef7b192a 100644 --- a/source/blender/compositor/nodes/COM_PixelateNode.cc +++ b/source/blender/compositor/nodes/COM_PixelateNode.cc @@ -18,33 +18,32 @@ #include "COM_PixelateNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PixelateOperation.h" namespace blender::compositor { -PixelateNode::PixelateNode(bNode *editorNode) : Node(editorNode) +PixelateNode::PixelateNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void PixelateNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void PixelateNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - DataType datatype = inputSocket->getDataType(); + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + DataType datatype = input_socket->get_data_type(); - if (inputSocket->isLinked()) { - NodeOutput *link = inputSocket->getLink(); - datatype = link->getDataType(); + if (input_socket->is_linked()) { + NodeOutput *link = input_socket->get_link(); + datatype = link->get_data_type(); } PixelateOperation *operation = new PixelateOperation(datatype); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PixelateNode.h b/source/blender/compositor/nodes/COM_PixelateNode.h index 1a6555550cf..c52a73cafa6 100644 --- a/source/blender/compositor/nodes/COM_PixelateNode.h +++ b/source/blender/compositor/nodes/COM_PixelateNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class PixelateNode : public Node { public: - PixelateNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + PixelateNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc index 54a0f4d0452..0d2623f6e8b 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.cc @@ -17,60 +17,55 @@ */ #include "COM_PlaneTrackDeformNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PlaneTrackOperation.h" -#include "BKE_movieclip.h" -#include "BKE_node.h" -#include "BKE_tracking.h" - namespace blender::compositor { -PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editorNode) : Node(editorNode) +PlaneTrackDeformNode::PlaneTrackDeformNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void PlaneTrackDeformNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void PlaneTrackDeformNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - MovieClip *clip = (MovieClip *)editorNode->id; - NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + MovieClip *clip = (MovieClip *)editor_node->id; + NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)editor_node->storage; - int frame_number = context.getFramenumber(); + int frame_number = context.get_framenumber(); - NodeInput *input_image = this->getInputSocket(0); - NodeOutput *output_warped_image = this->getOutputSocket(0); - NodeOutput *output_plane = this->getOutputSocket(1); + NodeInput *input_image = this->get_input_socket(0); + NodeOutput *output_warped_image = this->get_output_socket(0); + NodeOutput *output_plane = this->get_output_socket(1); PlaneTrackWarpImageOperation *warp_image_operation = new PlaneTrackWarpImageOperation(); - warp_image_operation->setMovieClip(clip); - warp_image_operation->setTrackingObject(data->tracking_object); - warp_image_operation->setPlaneTrackName(data->plane_track_name); - warp_image_operation->setFramenumber(frame_number); + warp_image_operation->set_movie_clip(clip); + warp_image_operation->set_tracking_object(data->tracking_object); + warp_image_operation->set_plane_track_name(data->plane_track_name); + warp_image_operation->set_framenumber(frame_number); if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { - warp_image_operation->setMotionBlurSamples(data->motion_blur_samples); - warp_image_operation->setMotionBlurShutter(data->motion_blur_shutter); + warp_image_operation->set_motion_blur_samples(data->motion_blur_samples); + warp_image_operation->set_motion_blur_shutter(data->motion_blur_shutter); } - converter.addOperation(warp_image_operation); + converter.add_operation(warp_image_operation); - converter.mapInputSocket(input_image, warp_image_operation->getInputSocket(0)); - converter.mapOutputSocket(output_warped_image, warp_image_operation->getOutputSocket()); + converter.map_input_socket(input_image, warp_image_operation->get_input_socket(0)); + converter.map_output_socket(output_warped_image, warp_image_operation->get_output_socket()); PlaneTrackMaskOperation *plane_mask_operation = new PlaneTrackMaskOperation(); - plane_mask_operation->setMovieClip(clip); - plane_mask_operation->setTrackingObject(data->tracking_object); - plane_mask_operation->setPlaneTrackName(data->plane_track_name); - plane_mask_operation->setFramenumber(frame_number); + plane_mask_operation->set_movie_clip(clip); + plane_mask_operation->set_tracking_object(data->tracking_object); + plane_mask_operation->set_plane_track_name(data->plane_track_name); + plane_mask_operation->set_framenumber(frame_number); if (data->flag & CMP_NODEFLAG_PLANETRACKDEFORM_MOTION_BLUR) { - plane_mask_operation->setMotionBlurSamples(data->motion_blur_samples); - plane_mask_operation->setMotionBlurShutter(data->motion_blur_shutter); + plane_mask_operation->set_motion_blur_samples(data->motion_blur_samples); + plane_mask_operation->set_motion_blur_shutter(data->motion_blur_shutter); } - converter.addOperation(plane_mask_operation); + converter.add_operation(plane_mask_operation); - converter.mapOutputSocket(output_plane, plane_mask_operation->getOutputSocket()); + converter.map_output_socket(output_plane, plane_mask_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h index 307738fcaf0..16367423454 100644 --- a/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h +++ b/source/blender/compositor/nodes/COM_PlaneTrackDeformNode.h @@ -31,9 +31,9 @@ namespace blender::compositor { */ class PlaneTrackDeformNode : public Node { public: - PlaneTrackDeformNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + PlaneTrackDeformNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.cc b/source/blender/compositor/nodes/COM_PosterizeNode.cc index 9f5a69961a4..c055a954c01 100644 --- a/source/blender/compositor/nodes/COM_PosterizeNode.cc +++ b/source/blender/compositor/nodes/COM_PosterizeNode.cc @@ -17,25 +17,24 @@ */ #include "COM_PosterizeNode.h" -#include "COM_ExecutionSystem.h" #include "COM_PosterizeOperation.h" namespace blender::compositor { -PosterizeNode::PosterizeNode(bNode *editorNode) : Node(editorNode) +PosterizeNode::PosterizeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void PosterizeNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void PosterizeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { PosterizeOperation *operation = new PosterizeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_PosterizeNode.h b/source/blender/compositor/nodes/COM_PosterizeNode.h index bb9bef2bdd0..d19e598632a 100644 --- a/source/blender/compositor/nodes/COM_PosterizeNode.h +++ b/source/blender/compositor/nodes/COM_PosterizeNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class PosterizeNode : public Node { public: - PosterizeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + PosterizeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cc b/source/blender/compositor/nodes/COM_RenderLayersNode.cc index 6744e98ecdb..4b82c5cbc21 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cc +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cc @@ -17,76 +17,72 @@ */ #include "COM_RenderLayersNode.h" -#include "COM_RenderLayersProg.h" -#include "COM_RotateOperation.h" -#include "COM_ScaleOperation.h" #include "COM_SetColorOperation.h" #include "COM_SetValueOperation.h" #include "COM_SetVectorOperation.h" -#include "COM_TranslateOperation.h" namespace blender::compositor { -RenderLayersNode::RenderLayersNode(bNode *editorNode) : Node(editorNode) +RenderLayersNode::RenderLayersNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void RenderLayersNode::testSocketLink(NodeConverter &converter, - const CompositorContext &context, - NodeOutput *output, - RenderLayersProg *operation, - Scene *scene, - int layerId, - bool is_preview) const +void RenderLayersNode::test_socket_link(NodeConverter &converter, + const CompositorContext &context, + NodeOutput *output, + RenderLayersProg *operation, + Scene *scene, + int layer_id, + bool is_preview) const { - operation->setScene(scene); - operation->setLayerId(layerId); - operation->setRenderData(context.getRenderData()); - operation->setViewName(context.getViewName()); + operation->set_scene(scene); + operation->set_layer_id(layer_id); + operation->set_render_data(context.get_render_data()); + operation->set_view_name(context.get_view_name()); - converter.mapOutputSocket(output, operation->getOutputSocket()); - converter.addOperation(operation); + converter.map_output_socket(output, operation->get_output_socket()); + converter.add_operation(operation); if (is_preview) { /* only for image socket */ - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); } } -void RenderLayersNode::testRenderLink(NodeConverter &converter, - const CompositorContext &context, - Render *re) const +void RenderLayersNode::test_render_link(NodeConverter &converter, + const CompositorContext &context, + Render *re) const { - Scene *scene = (Scene *)this->getbNode()->id; - const short layerId = this->getbNode()->custom1; + Scene *scene = (Scene *)this->get_bnode()->id; + const short layer_id = this->get_bnode()->custom1; RenderResult *rr = RE_AcquireResultRead(re); if (rr == nullptr) { - missingRenderLink(converter); + missing_render_link(converter); return; } - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, layerId); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, layer_id); if (view_layer == nullptr) { - missingRenderLink(converter); + missing_render_link(converter); return; } RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl == nullptr) { - missingRenderLink(converter); + missing_render_link(converter); return; } - for (NodeOutput *output : getOutputSockets()) { - NodeImageLayer *storage = (NodeImageLayer *)output->getbNodeSocket()->storage; + for (NodeOutput *output : get_output_sockets()) { + NodeImageLayer *storage = (NodeImageLayer *)output->get_bnode_socket()->storage; RenderPass *rpass = (RenderPass *)BLI_findstring( &rl->passes, storage->pass_name, offsetof(RenderPass, name)); if (rpass == nullptr) { - missingSocketLink(converter, output); + missing_socket_link(converter, output); continue; } RenderLayersProg *operation; bool is_preview; if (STREQ(rpass->name, RE_PASSNAME_COMBINED) && - STREQ(output->getbNodeSocket()->name, "Alpha")) { + STREQ(output->get_bnode_socket()->name, "Alpha")) { operation = new RenderLayersAlphaProg(rpass->name, DataType::Value, rpass->channels); is_preview = false; } @@ -112,33 +108,33 @@ void RenderLayersNode::testRenderLink(NodeConverter &converter, break; } operation = new RenderLayersProg(rpass->name, type, rpass->channels); - is_preview = STREQ(output->getbNodeSocket()->name, "Image"); + is_preview = STREQ(output->get_bnode_socket()->name, "Image"); } - testSocketLink(converter, context, output, operation, scene, layerId, is_preview); + test_socket_link(converter, context, output, operation, scene, layer_id, is_preview); } } -void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *output) const +void RenderLayersNode::missing_socket_link(NodeConverter &converter, NodeOutput *output) const { NodeOperation *operation; - switch (output->getDataType()) { + switch (output->get_data_type()) { case DataType::Color: { const float color[4] = {0.0f, 0.0f, 0.0f, 0.0f}; SetColorOperation *color_operation = new SetColorOperation(); - color_operation->setChannels(color); + color_operation->set_channels(color); operation = color_operation; break; } case DataType::Vector: { const float vector[3] = {0.0f, 0.0f, 0.0f}; SetVectorOperation *vector_operation = new SetVectorOperation(); - vector_operation->setVector(vector); + vector_operation->set_vector(vector); operation = vector_operation; break; } case DataType::Value: { SetValueOperation *value_operation = new SetValueOperation(); - value_operation->setValue(0.0f); + value_operation->set_value(0.0f); operation = value_operation; break; } @@ -148,29 +144,29 @@ void RenderLayersNode::missingSocketLink(NodeConverter &converter, NodeOutput *o } } - converter.mapOutputSocket(output, operation->getOutputSocket()); - converter.addOperation(operation); + converter.map_output_socket(output, operation->get_output_socket()); + converter.add_operation(operation); } -void RenderLayersNode::missingRenderLink(NodeConverter &converter) const +void RenderLayersNode::missing_render_link(NodeConverter &converter) const { - for (NodeOutput *output : outputs) { - missingSocketLink(converter, output); + for (NodeOutput *output : outputs_) { + missing_socket_link(converter, output); } } -void RenderLayersNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void RenderLayersNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - Scene *scene = (Scene *)this->getbNode()->id; + Scene *scene = (Scene *)this->get_bnode()->id; Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr; if (re != nullptr) { - testRenderLink(converter, context, re); + test_render_link(converter, context, re); RE_ReleaseResult(re); } else { - missingRenderLink(converter); + missing_render_link(converter); } } diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.h b/source/blender/compositor/nodes/COM_RenderLayersNode.h index 4eb2427c8e0..33e61c58336 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.h +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.h @@ -31,24 +31,24 @@ namespace blender::compositor { */ class RenderLayersNode : public Node { public: - RenderLayersNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + RenderLayersNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; private: - void testSocketLink(NodeConverter &converter, - const CompositorContext &context, - NodeOutput *output, - RenderLayersProg *operation, - Scene *scene, - int layerId, - bool is_preview) const; - void testRenderLink(NodeConverter &converter, - const CompositorContext &context, - Render *re) const; + void test_socket_link(NodeConverter &converter, + const CompositorContext &context, + NodeOutput *output, + RenderLayersProg *operation, + Scene *scene, + int layer_id, + bool is_preview) const; + void test_render_link(NodeConverter &converter, + const CompositorContext &context, + Render *re) const; - void missingSocketLink(NodeConverter &converter, NodeOutput *output) const; - void missingRenderLink(NodeConverter &converter) const; + void missing_socket_link(NodeConverter &converter, NodeOutput *output) const; + void missing_render_link(NodeConverter &converter) const; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_RotateNode.cc b/source/blender/compositor/nodes/COM_RotateNode.cc index c2fd8ed5594..64f49893335 100644 --- a/source/blender/compositor/nodes/COM_RotateNode.cc +++ b/source/blender/compositor/nodes/COM_RotateNode.cc @@ -18,45 +18,44 @@ #include "COM_RotateNode.h" -#include "COM_ExecutionSystem.h" #include "COM_RotateOperation.h" #include "COM_SetSamplerOperation.h" namespace blender::compositor { -RotateNode::RotateNode(bNode *editorNode) : Node(editorNode) +RotateNode::RotateNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void RotateNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void RotateNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputDegreeSocket = this->getInputSocket(1); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_degree_socket = this->get_input_socket(1); + NodeOutput *output_socket = this->get_output_socket(0); RotateOperation *operation = new RotateOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - PixelSampler sampler = (PixelSampler)this->getbNode()->custom1; + PixelSampler sampler = (PixelSampler)this->get_bnode()->custom1; switch (context.get_execution_model()) { case eExecutionModel::Tiled: { SetSamplerOperation *sampler_op = new SetSamplerOperation(); - sampler_op->setSampler(sampler); - converter.addOperation(sampler_op); - converter.addLink(sampler_op->getOutputSocket(), operation->getInputSocket(0)); - converter.mapInputSocket(inputSocket, sampler_op->getInputSocket(0)); + sampler_op->set_sampler(sampler); + converter.add_operation(sampler_op); + converter.add_link(sampler_op->get_output_socket(), operation->get_input_socket(0)); + converter.map_input_socket(input_socket, sampler_op->get_input_socket(0)); break; } case eExecutionModel::FullFrame: { operation->set_sampler(sampler); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); break; } } - converter.mapInputSocket(inputDegreeSocket, operation->getInputSocket(1)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_degree_socket, operation->get_input_socket(1)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_RotateNode.h b/source/blender/compositor/nodes/COM_RotateNode.h index 5d8bcb2e3e4..fa50cd1f689 100644 --- a/source/blender/compositor/nodes/COM_RotateNode.h +++ b/source/blender/compositor/nodes/COM_RotateNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class RotateNode : public Node { public: - RotateNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + RotateNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ScaleNode.cc b/source/blender/compositor/nodes/COM_ScaleNode.cc index 819d2e72f30..56f1d998db8 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.cc +++ b/source/blender/compositor/nodes/COM_ScaleNode.cc @@ -19,89 +19,93 @@ #include "COM_ScaleNode.h" #include "BKE_node.h" -#include "COM_ExecutionSystem.h" #include "COM_ScaleOperation.h" -#include "COM_SetSamplerOperation.h" #include "COM_SetValueOperation.h" namespace blender::compositor { -ScaleNode::ScaleNode(bNode *editorNode) : Node(editorNode) +ScaleNode::ScaleNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ScaleNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void ScaleNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputXSocket = this->getInputSocket(1); - NodeInput *inputYSocket = this->getInputSocket(2); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_xsocket = this->get_input_socket(1); + NodeInput *input_ysocket = this->get_input_socket(2); + NodeOutput *output_socket = this->get_output_socket(0); switch (bnode->custom1) { case CMP_SCALE_RELATIVE: { ScaleRelativeOperation *operation = new ScaleRelativeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputXSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_xsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_ysocket, operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); + + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); break; } case CMP_SCALE_SCENEPERCENT: { - SetValueOperation *scaleFactorOperation = new SetValueOperation(); - scaleFactorOperation->setValue(context.getRenderPercentageAsFactor()); - converter.addOperation(scaleFactorOperation); + SetValueOperation *scale_factor_operation = new SetValueOperation(); + scale_factor_operation->set_value(context.get_render_percentage_as_factor()); + converter.add_operation(scale_factor_operation); ScaleRelativeOperation *operation = new ScaleRelativeOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.addLink(scaleFactorOperation->getOutputSocket(), operation->getInputSocket(1)); - converter.addLink(scaleFactorOperation->getOutputSocket(), operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.add_link(scale_factor_operation->get_output_socket(), + operation->get_input_socket(1)); + converter.add_link(scale_factor_operation->get_output_socket(), + operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); break; } case CMP_SCALE_RENDERPERCENT: { - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); ScaleFixedSizeOperation *operation = new ScaleFixedSizeOperation(); /* framing options */ - operation->setIsAspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0); - operation->setIsCrop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0); - operation->setOffset(bnode->custom3, bnode->custom4); - operation->setNewWidth(rd->xsch * render_size_factor); - operation->setNewHeight(rd->ysch * render_size_factor); - operation->getInputSocket(0)->setResizeMode(ResizeMode::None); - converter.addOperation(operation); + operation->set_is_aspect((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_ASPECT) != 0); + operation->set_is_crop((bnode->custom2 & CMP_SCALE_RENDERSIZE_FRAME_CROP) != 0); + operation->set_offset(bnode->custom3, bnode->custom4); + operation->set_new_width(rd->xsch * render_size_factor); + operation->set_new_height(rd->ysch * render_size_factor); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 3.0f); break; } case CMP_SCALE_ABSOLUTE: { /* TODO: what is the use of this one.... perhaps some issues when the ui was updated... */ ScaleAbsoluteOperation *operation = new ScaleAbsoluteOperation(); - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapInputSocket(inputXSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_input_socket(input_xsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_ysocket, operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); - operation->setVariableSize(inputXSocket->isLinked() || inputYSocket->isLinked()); + operation->set_variable_size(input_xsocket->is_linked() || input_ysocket->is_linked()); + operation->set_scale_canvas_max_size(context.get_render_size() * 1.5f); break; } diff --git a/source/blender/compositor/nodes/COM_ScaleNode.h b/source/blender/compositor/nodes/COM_ScaleNode.h index 186ffa8bdce..7800a7acb8b 100644 --- a/source/blender/compositor/nodes/COM_ScaleNode.h +++ b/source/blender/compositor/nodes/COM_ScaleNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ScaleNode : public Node { public: - ScaleNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ScaleNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.cc b/source/blender/compositor/nodes/COM_SeparateColorNode.cc index fcaf52c701d..046a813895e 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.cc +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.cc @@ -22,102 +22,102 @@ namespace blender::compositor { -SeparateColorNode::SeparateColorNode(bNode *editorNode) : Node(editorNode) +SeparateColorNode::SeparateColorNode(bNode *editor_node) : Node(editor_node) { } -void SeparateColorNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void SeparateColorNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *imageSocket = this->getInputSocket(0); - NodeOutput *outputRSocket = this->getOutputSocket(0); - NodeOutput *outputGSocket = this->getOutputSocket(1); - NodeOutput *outputBSocket = this->getOutputSocket(2); - NodeOutput *outputASocket = this->getOutputSocket(3); + NodeInput *image_socket = this->get_input_socket(0); + NodeOutput *output_rsocket = this->get_output_socket(0); + NodeOutput *output_gsocket = this->get_output_socket(1); + NodeOutput *output_bsocket = this->get_output_socket(2); + NodeOutput *output_asocket = this->get_output_socket(3); - NodeOperation *color_conv = getColorConverter(context); + NodeOperation *color_conv = get_color_converter(context); if (color_conv) { - converter.addOperation(color_conv); + converter.add_operation(color_conv); - converter.mapInputSocket(imageSocket, color_conv->getInputSocket(0)); + converter.map_input_socket(image_socket, color_conv->get_input_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(0); - converter.addOperation(operation); + operation->set_channel(0); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputRSocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_rsocket, operation->get_output_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(1); - converter.addOperation(operation); + operation->set_channel(1); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputGSocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_gsocket, operation->get_output_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(2); - converter.addOperation(operation); + operation->set_channel(2); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputBSocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_bsocket, operation->get_output_socket(0)); } { SeparateChannelOperation *operation = new SeparateChannelOperation(); - operation->setChannel(3); - converter.addOperation(operation); + operation->set_channel(3); + converter.add_operation(operation); if (color_conv) { - converter.addLink(color_conv->getOutputSocket(), operation->getInputSocket(0)); + converter.add_link(color_conv->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(imageSocket, operation->getInputSocket(0)); + converter.map_input_socket(image_socket, operation->get_input_socket(0)); } - converter.mapOutputSocket(outputASocket, operation->getOutputSocket(0)); + converter.map_output_socket(output_asocket, operation->get_output_socket(0)); } } -NodeOperation *SeparateRGBANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateRGBANode::get_color_converter(const CompositorContext & /*context*/) const { return nullptr; /* no conversion needed */ } -NodeOperation *SeparateHSVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateHSVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertRGBToHSVOperation(); } -NodeOperation *SeparateYCCANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateYCCANode::get_color_converter(const CompositorContext & /*context*/) const { ConvertRGBToYCCOperation *operation = new ConvertRGBToYCCOperation(); - bNode *editorNode = this->getbNode(); - operation->setMode(editorNode->custom1); + bNode *editor_node = this->get_bnode(); + operation->set_mode(editor_node->custom1); return operation; } -NodeOperation *SeparateYUVANode::getColorConverter(const CompositorContext & /*context*/) const +NodeOperation *SeparateYUVANode::get_color_converter(const CompositorContext & /*context*/) const { return new ConvertRGBToYUVOperation(); } diff --git a/source/blender/compositor/nodes/COM_SeparateColorNode.h b/source/blender/compositor/nodes/COM_SeparateColorNode.h index eaf543df51f..0dfcd676772 100644 --- a/source/blender/compositor/nodes/COM_SeparateColorNode.h +++ b/source/blender/compositor/nodes/COM_SeparateColorNode.h @@ -24,48 +24,48 @@ namespace blender::compositor { class SeparateColorNode : public Node { public: - SeparateColorNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SeparateColorNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; protected: - virtual NodeOperation *getColorConverter(const CompositorContext &context) const = 0; + virtual NodeOperation *get_color_converter(const CompositorContext &context) const = 0; }; class SeparateRGBANode : public SeparateColorNode { public: - SeparateRGBANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateRGBANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class SeparateHSVANode : public SeparateColorNode { public: - SeparateHSVANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateHSVANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class SeparateYCCANode : public SeparateColorNode { public: - SeparateYCCANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateYCCANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; class SeparateYUVANode : public SeparateColorNode { public: - SeparateYUVANode(bNode *editorNode) : SeparateColorNode(editorNode) + SeparateYUVANode(bNode *editor_node) : SeparateColorNode(editor_node) { } - NodeOperation *getColorConverter(const CompositorContext &context) const override; + NodeOperation *get_color_converter(const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.cc b/source/blender/compositor/nodes/COM_SetAlphaNode.cc index dc41c126ba8..58b8df98bc8 100644 --- a/source/blender/compositor/nodes/COM_SetAlphaNode.cc +++ b/source/blender/compositor/nodes/COM_SetAlphaNode.cc @@ -17,17 +17,16 @@ */ #include "COM_SetAlphaNode.h" -#include "COM_ExecutionSystem.h" #include "COM_SetAlphaMultiplyOperation.h" #include "COM_SetAlphaReplaceOperation.h" namespace blender::compositor { -void SetAlphaNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SetAlphaNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - const bNode *editorNode = this->getbNode(); - const NodeSetAlpha *storage = static_cast(editorNode->storage); + const bNode *editor_node = this->get_bnode(); + const NodeSetAlpha *storage = static_cast(editor_node->storage); NodeOperation *operation = nullptr; switch (storage->mode) { case CMP_NODE_SETALPHA_MODE_APPLY: @@ -38,15 +37,15 @@ void SetAlphaNode::convertToOperations(NodeConverter &converter, break; } - if (!this->getInputSocket(0)->isLinked() && this->getInputSocket(1)->isLinked()) { - operation->setResolutionInputSocketIndex(1); + if (!this->get_input_socket(0)->is_linked() && this->get_input_socket(1)->is_linked()) { + operation->set_canvas_input_index(1); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SetAlphaNode.h b/source/blender/compositor/nodes/COM_SetAlphaNode.h index c8d340eb64b..9e369838bbe 100644 --- a/source/blender/compositor/nodes/COM_SetAlphaNode.h +++ b/source/blender/compositor/nodes/COM_SetAlphaNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class SetAlphaNode : public Node { public: - SetAlphaNode(bNode *editorNode) : Node(editorNode) + SetAlphaNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.cc b/source/blender/compositor/nodes/COM_SocketProxyNode.cc index b3aa1770551..e7c8a90a48b 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.cc +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.cc @@ -17,91 +17,87 @@ */ #include "COM_SocketProxyNode.h" -#include "COM_ExecutionSystem.h" #include "COM_ReadBufferOperation.h" -#include "COM_SetColorOperation.h" -#include "COM_SetValueOperation.h" -#include "COM_SetVectorOperation.h" -#include "COM_SocketProxyOperation.h" #include "COM_WriteBufferOperation.h" namespace blender::compositor { -SocketProxyNode::SocketProxyNode(bNode *editorNode, - bNodeSocket *editorInput, - bNodeSocket *editorOutput, +SocketProxyNode::SocketProxyNode(bNode *editor_node, + bNodeSocket *editor_input, + bNodeSocket *editor_output, bool use_conversion) - : Node(editorNode, false), m_use_conversion(use_conversion) + : Node(editor_node, false), use_conversion_(use_conversion) { DataType dt; dt = DataType::Value; - if (editorInput->type == SOCK_RGBA) { + if (editor_input->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorInput->type == SOCK_VECTOR) { + if (editor_input->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addInputSocket(dt, editorInput); + this->add_input_socket(dt, editor_input); dt = DataType::Value; - if (editorOutput->type == SOCK_RGBA) { + if (editor_output->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorOutput->type == SOCK_VECTOR) { + if (editor_output->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addOutputSocket(dt, editorOutput); + this->add_output_socket(dt, editor_output); } -void SocketProxyNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SocketProxyNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeOperationOutput *proxy_output = converter.addInputProxy(getInputSocket(0), m_use_conversion); - converter.mapOutputSocket(getOutputSocket(), proxy_output); + NodeOperationOutput *proxy_output = converter.add_input_proxy(get_input_socket(0), + use_conversion_); + converter.map_output_socket(get_output_socket(), proxy_output); } -SocketBufferNode::SocketBufferNode(bNode *editorNode, - bNodeSocket *editorInput, - bNodeSocket *editorOutput) - : Node(editorNode, false) +SocketBufferNode::SocketBufferNode(bNode *editor_node, + bNodeSocket *editor_input, + bNodeSocket *editor_output) + : Node(editor_node, false) { DataType dt; dt = DataType::Value; - if (editorInput->type == SOCK_RGBA) { + if (editor_input->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorInput->type == SOCK_VECTOR) { + if (editor_input->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addInputSocket(dt, editorInput); + this->add_input_socket(dt, editor_input); dt = DataType::Value; - if (editorOutput->type == SOCK_RGBA) { + if (editor_output->type == SOCK_RGBA) { dt = DataType::Color; } - if (editorOutput->type == SOCK_VECTOR) { + if (editor_output->type == SOCK_VECTOR) { dt = DataType::Vector; } - this->addOutputSocket(dt, editorOutput); + this->add_output_socket(dt, editor_output); } -void SocketBufferNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SocketBufferNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeOutput *output = this->getOutputSocket(0); - NodeInput *input = this->getInputSocket(0); + NodeOutput *output = this->get_output_socket(0); + NodeInput *input = this->get_input_socket(0); - DataType datatype = output->getDataType(); - WriteBufferOperation *writeOperation = new WriteBufferOperation(datatype); - ReadBufferOperation *readOperation = new ReadBufferOperation(datatype); - readOperation->setMemoryProxy(writeOperation->getMemoryProxy()); - converter.addOperation(writeOperation); - converter.addOperation(readOperation); + DataType datatype = output->get_data_type(); + WriteBufferOperation *write_operation = new WriteBufferOperation(datatype); + ReadBufferOperation *read_operation = new ReadBufferOperation(datatype); + read_operation->set_memory_proxy(write_operation->get_memory_proxy()); + converter.add_operation(write_operation); + converter.add_operation(read_operation); - converter.mapInputSocket(input, writeOperation->getInputSocket(0)); - converter.mapOutputSocket(output, readOperation->getOutputSocket()); + converter.map_input_socket(input, write_operation->get_input_socket(0)); + converter.map_output_socket(output, read_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SocketProxyNode.h b/source/blender/compositor/nodes/COM_SocketProxyNode.h index d19fb802767..50079ed4718 100644 --- a/source/blender/compositor/nodes/COM_SocketProxyNode.h +++ b/source/blender/compositor/nodes/COM_SocketProxyNode.h @@ -28,32 +28,32 @@ namespace blender::compositor { */ class SocketProxyNode : public Node { public: - SocketProxyNode(bNode *editorNode, - bNodeSocket *editorInput, - bNodeSocket *editorOutput, + SocketProxyNode(bNode *editor_node, + bNodeSocket *editor_input, + bNodeSocket *editor_output, bool use_conversion); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; - bool getUseConversion() const + bool get_use_conversion() const { - return m_use_conversion; + return use_conversion_; } - void setUseConversion(bool use_conversion) + void set_use_conversion(bool use_conversion) { - m_use_conversion = use_conversion; + use_conversion_ = use_conversion; } private: /** If true, the proxy will convert input and output data to/from the proxy socket types. */ - bool m_use_conversion; + bool use_conversion_; }; class SocketBufferNode : public Node { public: - SocketBufferNode(bNode *editorNode, bNodeSocket *editorInput, bNodeSocket *editorOutput); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SocketBufferNode(bNode *editor_node, bNodeSocket *editor_input, bNodeSocket *editor_output); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cc b/source/blender/compositor/nodes/COM_SplitViewerNode.cc index 582c650f205..c3234fad0fb 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cc +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cc @@ -17,62 +17,59 @@ */ #include "COM_SplitViewerNode.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_scene.h" -#include "COM_ExecutionSystem.h" #include "COM_SplitOperation.h" #include "COM_ViewerOperation.h" namespace blender::compositor { -SplitViewerNode::SplitViewerNode(bNode *editorNode) : Node(editorNode) +SplitViewerNode::SplitViewerNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SplitViewerNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void SplitViewerNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - bool do_output = (editorNode->flag & NODE_DO_OUTPUT_RECALC || context.isRendering()) && - (editorNode->flag & NODE_DO_OUTPUT); + bNode *editor_node = this->get_bnode(); + bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) && + (editor_node->flag & NODE_DO_OUTPUT); - NodeInput *image1Socket = this->getInputSocket(0); - NodeInput *image2Socket = this->getInputSocket(1); - Image *image = (Image *)this->getbNode()->id; - ImageUser *imageUser = (ImageUser *)this->getbNode()->storage; + NodeInput *image1Socket = this->get_input_socket(0); + NodeInput *image2Socket = this->get_input_socket(1); + Image *image = (Image *)this->get_bnode()->id; + ImageUser *image_user = (ImageUser *)this->get_bnode()->storage; - SplitOperation *splitViewerOperation = new SplitOperation(); - splitViewerOperation->setSplitPercentage(this->getbNode()->custom1); - splitViewerOperation->setXSplit(!this->getbNode()->custom2); + SplitOperation *split_viewer_operation = new SplitOperation(); + split_viewer_operation->set_split_percentage(this->get_bnode()->custom1); + split_viewer_operation->set_xsplit(!this->get_bnode()->custom2); - converter.addOperation(splitViewerOperation); - converter.mapInputSocket(image1Socket, splitViewerOperation->getInputSocket(0)); - converter.mapInputSocket(image2Socket, splitViewerOperation->getInputSocket(1)); + converter.add_operation(split_viewer_operation); + converter.map_input_socket(image1Socket, split_viewer_operation->get_input_socket(0)); + converter.map_input_socket(image2Socket, split_viewer_operation->get_input_socket(1)); - ViewerOperation *viewerOperation = new ViewerOperation(); - viewerOperation->setImage(image); - viewerOperation->setImageUser(imageUser); - viewerOperation->setViewSettings(context.getViewSettings()); - viewerOperation->setDisplaySettings(context.getDisplaySettings()); - viewerOperation->setRenderData(context.getRenderData()); - viewerOperation->setViewName(context.getViewName()); + ViewerOperation *viewer_operation = new ViewerOperation(); + viewer_operation->set_image(image); + viewer_operation->set_image_user(image_user); + viewer_operation->set_view_settings(context.get_view_settings()); + viewer_operation->set_display_settings(context.get_display_settings()); + viewer_operation->set_render_data(context.get_render_data()); + viewer_operation->set_view_name(context.get_view_name()); /* defaults - the viewer node has these options but not exposed for split view * we could use the split to define an area of interest on one axis at least */ - viewerOperation->setChunkOrder(ChunkOrdering::Default); - viewerOperation->setCenterX(0.5f); - viewerOperation->setCenterY(0.5f); + viewer_operation->set_chunk_order(ChunkOrdering::Default); + viewer_operation->setCenterX(0.5f); + viewer_operation->setCenterY(0.5f); - converter.addOperation(viewerOperation); - converter.addLink(splitViewerOperation->getOutputSocket(), viewerOperation->getInputSocket(0)); + converter.add_operation(viewer_operation); + converter.add_link(split_viewer_operation->get_output_socket(), + viewer_operation->get_input_socket(0)); - converter.addPreview(splitViewerOperation->getOutputSocket()); + converter.add_preview(split_viewer_operation->get_output_socket()); if (do_output) { - converter.registerViewer(viewerOperation); + converter.register_viewer(viewer_operation); } } diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.h b/source/blender/compositor/nodes/COM_SplitViewerNode.h index 8a42775eb0d..579eca9d8e1 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.h +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class SplitViewerNode : public Node { public: - SplitViewerNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SplitViewerNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc index 90f62c6d562..9892c4f5c05 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.cc +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.cc @@ -17,123 +17,169 @@ */ #include "COM_Stabilize2dNode.h" -#include "COM_ExecutionSystem.h" #include "COM_MovieClipAttributeOperation.h" #include "COM_RotateOperation.h" #include "COM_ScaleOperation.h" #include "COM_SetSamplerOperation.h" -#include "COM_TransformOperation.h" #include "COM_TranslateOperation.h" -#include "BKE_tracking.h" - -#include "DNA_movieclip_types.h" - namespace blender::compositor { -Stabilize2dNode::Stabilize2dNode(bNode *editorNode) : Node(editorNode) +Stabilize2dNode::Stabilize2dNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void Stabilize2dNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void Stabilize2dNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - NodeInput *imageInput = this->getInputSocket(0); - MovieClip *clip = (MovieClip *)editorNode->id; - bool invert = (editorNode->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0; - const PixelSampler sampler = (PixelSampler)editorNode->custom1; + bNode *editor_node = this->get_bnode(); + NodeInput *image_input = this->get_input_socket(0); + MovieClip *clip = (MovieClip *)editor_node->id; + bool invert = (editor_node->custom2 & CMP_NODEFLAG_STABILIZE_INVERSE) != 0; + const PixelSampler sampler = (PixelSampler)editor_node->custom1; - MovieClipAttributeOperation *scaleAttribute = new MovieClipAttributeOperation(); - MovieClipAttributeOperation *angleAttribute = new MovieClipAttributeOperation(); - MovieClipAttributeOperation *xAttribute = new MovieClipAttributeOperation(); - MovieClipAttributeOperation *yAttribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *scale_attribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *angle_attribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *x_attribute = new MovieClipAttributeOperation(); + MovieClipAttributeOperation *y_attribute = new MovieClipAttributeOperation(); - scaleAttribute->setAttribute(MCA_SCALE); - scaleAttribute->setFramenumber(context.getFramenumber()); - scaleAttribute->setMovieClip(clip); - scaleAttribute->setInvert(invert); + scale_attribute->set_attribute(MCA_SCALE); + scale_attribute->set_framenumber(context.get_framenumber()); + scale_attribute->set_movie_clip(clip); + scale_attribute->set_invert(invert); - angleAttribute->setAttribute(MCA_ANGLE); - angleAttribute->setFramenumber(context.getFramenumber()); - angleAttribute->setMovieClip(clip); - angleAttribute->setInvert(invert); + angle_attribute->set_attribute(MCA_ANGLE); + angle_attribute->set_framenumber(context.get_framenumber()); + angle_attribute->set_movie_clip(clip); + angle_attribute->set_invert(invert); - xAttribute->setAttribute(MCA_X); - xAttribute->setFramenumber(context.getFramenumber()); - xAttribute->setMovieClip(clip); - xAttribute->setInvert(invert); + x_attribute->set_attribute(MCA_X); + x_attribute->set_framenumber(context.get_framenumber()); + x_attribute->set_movie_clip(clip); + x_attribute->set_invert(invert); - yAttribute->setAttribute(MCA_Y); - yAttribute->setFramenumber(context.getFramenumber()); - yAttribute->setMovieClip(clip); - yAttribute->setInvert(invert); + y_attribute->set_attribute(MCA_Y); + y_attribute->set_framenumber(context.get_framenumber()); + y_attribute->set_movie_clip(clip); + y_attribute->set_invert(invert); - converter.addOperation(scaleAttribute); - converter.addOperation(angleAttribute); - converter.addOperation(xAttribute); - converter.addOperation(yAttribute); + converter.add_operation(scale_attribute); + converter.add_operation(angle_attribute); + converter.add_operation(x_attribute); + converter.add_operation(y_attribute); switch (context.get_execution_model()) { case eExecutionModel::Tiled: { - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - scaleOperation->setSampler(sampler); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - TranslateOperation *translateOperation = new TranslateOperation(); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + scale_operation->set_sampler(sampler); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + TranslateOperation *translate_operation = new TranslateOperation(); SetSamplerOperation *psoperation = new SetSamplerOperation(); - psoperation->setSampler(sampler); + psoperation->set_sampler(sampler); - converter.addOperation(scaleOperation); - converter.addOperation(translateOperation); - converter.addOperation(rotateOperation); - converter.addOperation(psoperation); + converter.add_operation(scale_operation); + converter.add_operation(translate_operation); + converter.add_operation(rotate_operation); + converter.add_operation(psoperation); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(1)); - converter.addLink(scaleAttribute->getOutputSocket(), scaleOperation->getInputSocket(2)); + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(1)); + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(2)); - converter.addLink(angleAttribute->getOutputSocket(), rotateOperation->getInputSocket(1)); + converter.add_link(angle_attribute->get_output_socket(), + rotate_operation->get_input_socket(1)); - converter.addLink(xAttribute->getOutputSocket(), translateOperation->getInputSocket(1)); - converter.addLink(yAttribute->getOutputSocket(), translateOperation->getInputSocket(2)); + converter.add_link(x_attribute->get_output_socket(), + translate_operation->get_input_socket(1)); + converter.add_link(y_attribute->get_output_socket(), + translate_operation->get_input_socket(2)); - converter.mapOutputSocket(getOutputSocket(), psoperation->getOutputSocket()); + converter.map_output_socket(get_output_socket(), psoperation->get_output_socket()); if (invert) { /* Translate -> Rotate -> Scale. */ - converter.mapInputSocket(imageInput, translateOperation->getInputSocket(0)); + converter.map_input_socket(image_input, translate_operation->get_input_socket(0)); - converter.addLink(translateOperation->getOutputSocket(), - rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), scaleOperation->getInputSocket(0)); + converter.add_link(translate_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + scale_operation->get_input_socket(0)); - converter.addLink(scaleOperation->getOutputSocket(), psoperation->getInputSocket(0)); + converter.add_link(scale_operation->get_output_socket(), psoperation->get_input_socket(0)); } else { /* Scale -> Rotate -> Translate. */ - converter.mapInputSocket(imageInput, scaleOperation->getInputSocket(0)); + converter.map_input_socket(image_input, scale_operation->get_input_socket(0)); - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.addLink(rotateOperation->getOutputSocket(), - translateOperation->getInputSocket(0)); + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); - converter.addLink(translateOperation->getOutputSocket(), psoperation->getInputSocket(0)); + converter.add_link(translate_operation->get_output_socket(), + psoperation->get_input_socket(0)); } break; } case eExecutionModel::FullFrame: { - TransformOperation *transform_op = new TransformOperation(); - transform_op->set_sampler(sampler); - transform_op->set_convert_rotate_degree_to_rad(false); - transform_op->set_invert(invert); - converter.addOperation(transform_op); - converter.mapInputSocket(imageInput, transform_op->getInputSocket(0)); - converter.addLink(xAttribute->getOutputSocket(), transform_op->getInputSocket(1)); - converter.addLink(yAttribute->getOutputSocket(), transform_op->getInputSocket(2)); - converter.addLink(angleAttribute->getOutputSocket(), transform_op->getInputSocket(3)); - converter.addLink(scaleAttribute->getOutputSocket(), transform_op->getInputSocket(4)); - converter.mapOutputSocket(getOutputSocket(), transform_op->getOutputSocket()); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + scale_operation->set_sampler(sampler); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + rotate_operation->set_sampler(sampler); + TranslateOperation *translate_operation = new TranslateCanvasOperation(); + + converter.add_operation(scale_operation); + converter.add_operation(translate_operation); + converter.add_operation(rotate_operation); + + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(1)); + converter.add_link(scale_attribute->get_output_socket(), + scale_operation->get_input_socket(2)); + + converter.add_link(angle_attribute->get_output_socket(), + rotate_operation->get_input_socket(1)); + + converter.add_link(x_attribute->get_output_socket(), + translate_operation->get_input_socket(1)); + converter.add_link(y_attribute->get_output_socket(), + translate_operation->get_input_socket(2)); + + NodeOperationInput *stabilization_socket = nullptr; + if (invert) { + /* Translate -> Rotate -> Scale. */ + stabilization_socket = translate_operation->get_input_socket(0); + converter.map_input_socket(image_input, translate_operation->get_input_socket(0)); + + converter.add_link(translate_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + scale_operation->get_input_socket(0)); + + converter.map_output_socket(get_output_socket(), scale_operation->get_output_socket()); + } + else { + /* Scale -> Rotate -> Translate. */ + stabilization_socket = scale_operation->get_input_socket(0); + converter.map_input_socket(image_input, scale_operation->get_input_socket(0)); + + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); + + converter.map_output_socket(get_output_socket(), translate_operation->get_output_socket()); + } + + x_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + y_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + scale_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + angle_attribute->set_socket_input_resolution_for_stabilization(stabilization_socket); + break; } } } diff --git a/source/blender/compositor/nodes/COM_Stabilize2dNode.h b/source/blender/compositor/nodes/COM_Stabilize2dNode.h index 34ed8871e33..0c6afb272e9 100644 --- a/source/blender/compositor/nodes/COM_Stabilize2dNode.h +++ b/source/blender/compositor/nodes/COM_Stabilize2dNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class Stabilize2dNode : public Node { public: - Stabilize2dNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + Stabilize2dNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.cc b/source/blender/compositor/nodes/COM_SunBeamsNode.cc index 1e5aa0b8020..ba42b79f307 100644 --- a/source/blender/compositor/nodes/COM_SunBeamsNode.cc +++ b/source/blender/compositor/nodes/COM_SunBeamsNode.cc @@ -20,24 +20,24 @@ namespace blender::compositor { -SunBeamsNode::SunBeamsNode(bNode *editorNode) : Node(editorNode) +SunBeamsNode::SunBeamsNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SunBeamsNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SunBeamsNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *inputSocket = this->getInputSocket(0); - NodeOutput *outputSocket = this->getOutputSocket(0); - NodeSunBeams *data = (NodeSunBeams *)getbNode()->storage; + NodeInput *input_socket = this->get_input_socket(0); + NodeOutput *output_socket = this->get_output_socket(0); + NodeSunBeams *data = (NodeSunBeams *)get_bnode()->storage; SunBeamsOperation *operation = new SunBeamsOperation(); - operation->setData(*data); - converter.addOperation(operation); + operation->set_data(*data); + converter.add_operation(operation); - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SunBeamsNode.h b/source/blender/compositor/nodes/COM_SunBeamsNode.h index 8b68d3f4cb5..478b5ce39f8 100644 --- a/source/blender/compositor/nodes/COM_SunBeamsNode.h +++ b/source/blender/compositor/nodes/COM_SunBeamsNode.h @@ -27,9 +27,9 @@ namespace blender::compositor { */ class SunBeamsNode : public Node { public: - SunBeamsNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SunBeamsNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchNode.cc b/source/blender/compositor/nodes/COM_SwitchNode.cc index 4006d10dafb..d0bc0f6dea2 100644 --- a/source/blender/compositor/nodes/COM_SwitchNode.cc +++ b/source/blender/compositor/nodes/COM_SwitchNode.cc @@ -20,25 +20,25 @@ namespace blender::compositor { -SwitchNode::SwitchNode(bNode *editorNode) : Node(editorNode) +SwitchNode::SwitchNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SwitchNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void SwitchNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - bool condition = this->getbNode()->custom1; + bool condition = this->get_bnode()->custom1; NodeOperationOutput *result; if (!condition) { - result = converter.addInputProxy(getInputSocket(0), false); + result = converter.add_input_proxy(get_input_socket(0), false); } else { - result = converter.addInputProxy(getInputSocket(1), false); + result = converter.add_input_proxy(get_input_socket(1), false); } - converter.mapOutputSocket(getOutputSocket(0), result); + converter.map_output_socket(get_output_socket(0), result); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchNode.h b/source/blender/compositor/nodes/COM_SwitchNode.h index aa6caa2e59f..eafdeb4ac74 100644 --- a/source/blender/compositor/nodes/COM_SwitchNode.h +++ b/source/blender/compositor/nodes/COM_SwitchNode.h @@ -30,9 +30,9 @@ namespace blender::compositor { */ class SwitchNode : public Node { public: - SwitchNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SwitchNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cc b/source/blender/compositor/nodes/COM_SwitchViewNode.cc index 395122dd11b..735ccaf3513 100644 --- a/source/blender/compositor/nodes/COM_SwitchViewNode.cc +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cc @@ -17,28 +17,27 @@ */ #include "COM_SwitchViewNode.h" -#include "BLI_listbase.h" namespace blender::compositor { -SwitchViewNode::SwitchViewNode(bNode *editorNode) : Node(editorNode) +SwitchViewNode::SwitchViewNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void SwitchViewNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void SwitchViewNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { NodeOperationOutput *result; - const char *viewName = context.getViewName(); - bNode *bnode = this->getbNode(); + const char *view_name = context.get_view_name(); + bNode *bnode = this->get_bnode(); /* get the internal index of the socket with a matching name */ - int nr = BLI_findstringindex(&bnode->inputs, viewName, offsetof(bNodeSocket, name)); + int nr = BLI_findstringindex(&bnode->inputs, view_name, offsetof(bNodeSocket, name)); nr = MAX2(nr, 0); - result = converter.addInputProxy(getInputSocket(nr), false); - converter.mapOutputSocket(getOutputSocket(0), result); + result = converter.add_input_proxy(get_input_socket(nr), false); + converter.map_output_socket(get_output_socket(0), result); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.h b/source/blender/compositor/nodes/COM_SwitchViewNode.h index ce6de52182c..227c076f181 100644 --- a/source/blender/compositor/nodes/COM_SwitchViewNode.h +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.h @@ -30,9 +30,9 @@ namespace blender::compositor { */ class SwitchViewNode : public Node { public: - SwitchViewNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + SwitchViewNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TextureNode.cc b/source/blender/compositor/nodes/COM_TextureNode.cc index 317355b8c9a..ca57680781f 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.cc +++ b/source/blender/compositor/nodes/COM_TextureNode.cc @@ -17,44 +17,43 @@ */ #include "COM_TextureNode.h" -#include "COM_ExecutionSystem.h" #include "COM_TextureOperation.h" namespace blender::compositor { -TextureNode::TextureNode(bNode *editorNode) : Node(editorNode) +TextureNode::TextureNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TextureNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TextureNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - Tex *texture = (Tex *)editorNode->id; + bNode *editor_node = this->get_bnode(); + Tex *texture = (Tex *)editor_node->id; TextureOperation *operation = new TextureOperation(); - const ColorManagedDisplaySettings *displaySettings = context.getDisplaySettings(); - bool sceneColorManage = !STREQ(displaySettings->display_device, "None"); - operation->setTexture(texture); - operation->setRenderData(context.getRenderData()); - operation->setSceneColorManage(sceneColorManage); - converter.addOperation(operation); + const ColorManagedDisplaySettings *display_settings = context.get_display_settings(); + bool scene_color_manage = !STREQ(display_settings->display_device, "None"); + operation->set_texture(texture); + operation->set_render_data(context.get_render_data()); + operation->set_scene_color_manage(scene_color_manage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(1), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(1), operation->get_output_socket()); - converter.addPreview(operation->getOutputSocket()); + converter.add_preview(operation->get_output_socket()); - TextureAlphaOperation *alphaOperation = new TextureAlphaOperation(); - alphaOperation->setTexture(texture); - alphaOperation->setRenderData(context.getRenderData()); - alphaOperation->setSceneColorManage(sceneColorManage); - converter.addOperation(alphaOperation); + TextureAlphaOperation *alpha_operation = new TextureAlphaOperation(); + alpha_operation->set_texture(texture); + alpha_operation->set_render_data(context.get_render_data()); + alpha_operation->set_scene_color_manage(scene_color_manage); + converter.add_operation(alpha_operation); - converter.mapInputSocket(getInputSocket(0), alphaOperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), alphaOperation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(0), alphaOperation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), alpha_operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), alpha_operation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(0), alpha_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TextureNode.h b/source/blender/compositor/nodes/COM_TextureNode.h index b886e3b74e1..bd477e6c6e6 100644 --- a/source/blender/compositor/nodes/COM_TextureNode.h +++ b/source/blender/compositor/nodes/COM_TextureNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class TextureNode : public Node { public: - TextureNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TextureNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TimeNode.cc b/source/blender/compositor/nodes/COM_TimeNode.cc index c14c5344eee..0d1532f62f9 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.cc +++ b/source/blender/compositor/nodes/COM_TimeNode.cc @@ -17,29 +17,27 @@ */ #include "COM_TimeNode.h" -#include "COM_ExecutionSystem.h" + #include "COM_SetValueOperation.h" #include "BKE_colortools.h" -#include "BLI_utildefines.h" - namespace blender::compositor { -TimeNode::TimeNode(bNode *editorNode) : Node(editorNode) +TimeNode::TimeNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TimeNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TimeNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { SetValueOperation *operation = new SetValueOperation(); - bNode *node = this->getbNode(); + bNode *node = this->get_bnode(); /* stack order output: fac */ float fac = 0.0f; - const int framenumber = context.getFramenumber(); + const int framenumber = context.get_framenumber(); if (framenumber < node->custom1) { fac = 0.0f; @@ -48,15 +46,15 @@ void TimeNode::convertToOperations(NodeConverter &converter, fac = 1.0f; } else if (node->custom1 < node->custom2) { - fac = (context.getFramenumber() - node->custom1) / (float)(node->custom2 - node->custom1); + fac = (context.get_framenumber() - node->custom1) / (float)(node->custom2 - node->custom1); } BKE_curvemapping_init((CurveMapping *)node->storage); fac = BKE_curvemapping_evaluateF((CurveMapping *)node->storage, 0, fac); - operation->setValue(clamp_f(fac, 0.0f, 1.0f)); - converter.addOperation(operation); + operation->set_value(clamp_f(fac, 0.0f, 1.0f)); + converter.add_operation(operation); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TimeNode.h b/source/blender/compositor/nodes/COM_TimeNode.h index 5688e2cff03..5c5a0c98058 100644 --- a/source/blender/compositor/nodes/COM_TimeNode.h +++ b/source/blender/compositor/nodes/COM_TimeNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class TimeNode : public Node { public: - TimeNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TimeNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TonemapNode.cc b/source/blender/compositor/nodes/COM_TonemapNode.cc index 844fe3e8cb6..54ba84d46bd 100644 --- a/source/blender/compositor/nodes/COM_TonemapNode.cc +++ b/source/blender/compositor/nodes/COM_TonemapNode.cc @@ -17,28 +17,27 @@ */ #include "COM_TonemapNode.h" -#include "COM_ExecutionSystem.h" #include "COM_TonemapOperation.h" namespace blender::compositor { -TonemapNode::TonemapNode(bNode *editorNode) : Node(editorNode) +TonemapNode::TonemapNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TonemapNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void TonemapNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeTonemap *data = (NodeTonemap *)this->getbNode()->storage; + NodeTonemap *data = (NodeTonemap *)this->get_bnode()->storage; TonemapOperation *operation = data->type == 1 ? new PhotoreceptorTonemapOperation() : new TonemapOperation(); - operation->setData(data); - converter.addOperation(operation); + operation->set_data(data); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket(0)); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket(0)); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TonemapNode.h b/source/blender/compositor/nodes/COM_TonemapNode.h index cac9004c32a..3c67472bf98 100644 --- a/source/blender/compositor/nodes/COM_TonemapNode.h +++ b/source/blender/compositor/nodes/COM_TonemapNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class TonemapNode : public Node { public: - TonemapNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TonemapNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.cc b/source/blender/compositor/nodes/COM_TrackPositionNode.cc index 3fb5fc02f20..ced09d8535f 100644 --- a/source/blender/compositor/nodes/COM_TrackPositionNode.cc +++ b/source/blender/compositor/nodes/COM_TrackPositionNode.cc @@ -19,7 +19,6 @@ #include "COM_TrackPositionNode.h" #include "COM_ConvertOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_TrackPositionOperation.h" #include "DNA_movieclip_types.h" @@ -28,7 +27,7 @@ namespace blender::compositor { -TrackPositionNode::TrackPositionNode(bNode *editorNode) : Node(editorNode) +TrackPositionNode::TrackPositionNode(bNode *editor_node) : Node(editor_node) { /* pass */ } @@ -41,58 +40,58 @@ static TrackPositionOperation *create_motion_operation(NodeConverter &converter, int delta) { TrackPositionOperation *operation = new TrackPositionOperation(); - operation->setMovieClip(clip); - operation->setTrackingObject(trackpos_data->tracking_object); - operation->setTrackName(trackpos_data->track_name); - operation->setFramenumber(frame_number); - operation->setAxis(axis); - operation->setPosition(CMP_TRACKPOS_ABSOLUTE); - operation->setRelativeFrame(frame_number + delta); - operation->setSpeedOutput(true); - converter.addOperation(operation); + operation->set_movie_clip(clip); + operation->set_tracking_object(trackpos_data->tracking_object); + operation->set_track_name(trackpos_data->track_name); + operation->set_framenumber(frame_number); + operation->set_axis(axis); + operation->set_position(CMP_TRACKPOS_ABSOLUTE); + operation->set_relative_frame(frame_number + delta); + operation->set_speed_output(true); + converter.add_operation(operation); return operation; } -void TrackPositionNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TrackPositionNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - MovieClip *clip = (MovieClip *)editorNode->id; - NodeTrackPosData *trackpos_data = (NodeTrackPosData *)editorNode->storage; + bNode *editor_node = this->get_bnode(); + MovieClip *clip = (MovieClip *)editor_node->id; + NodeTrackPosData *trackpos_data = (NodeTrackPosData *)editor_node->storage; - NodeOutput *outputX = this->getOutputSocket(0); - NodeOutput *outputY = this->getOutputSocket(1); - NodeOutput *outputSpeed = this->getOutputSocket(2); + NodeOutput *outputX = this->get_output_socket(0); + NodeOutput *outputY = this->get_output_socket(1); + NodeOutput *output_speed = this->get_output_socket(2); int frame_number; - if (editorNode->custom1 == CMP_TRACKPOS_ABSOLUTE_FRAME) { - frame_number = editorNode->custom2; + if (editor_node->custom1 == CMP_TRACKPOS_ABSOLUTE_FRAME) { + frame_number = editor_node->custom2; } else { - frame_number = context.getFramenumber(); + frame_number = context.get_framenumber(); } TrackPositionOperation *operationX = new TrackPositionOperation(); - operationX->setMovieClip(clip); - operationX->setTrackingObject(trackpos_data->tracking_object); - operationX->setTrackName(trackpos_data->track_name); - operationX->setFramenumber(frame_number); - operationX->setAxis(0); - operationX->setPosition(editorNode->custom1); - operationX->setRelativeFrame(editorNode->custom2); - converter.addOperation(operationX); - converter.mapOutputSocket(outputX, operationX->getOutputSocket()); + operationX->set_movie_clip(clip); + operationX->set_tracking_object(trackpos_data->tracking_object); + operationX->set_track_name(trackpos_data->track_name); + operationX->set_framenumber(frame_number); + operationX->set_axis(0); + operationX->set_position(editor_node->custom1); + operationX->set_relative_frame(editor_node->custom2); + converter.add_operation(operationX); + converter.map_output_socket(outputX, operationX->get_output_socket()); TrackPositionOperation *operationY = new TrackPositionOperation(); - operationY->setMovieClip(clip); - operationY->setTrackingObject(trackpos_data->tracking_object); - operationY->setTrackName(trackpos_data->track_name); - operationY->setFramenumber(frame_number); - operationY->setAxis(1); - operationY->setPosition(editorNode->custom1); - operationY->setRelativeFrame(editorNode->custom2); - converter.addOperation(operationY); - converter.mapOutputSocket(outputY, operationY->getOutputSocket()); + operationY->set_movie_clip(clip); + operationY->set_tracking_object(trackpos_data->tracking_object); + operationY->set_track_name(trackpos_data->track_name); + operationY->set_framenumber(frame_number); + operationY->set_axis(1); + operationY->set_position(editor_node->custom1); + operationY->set_relative_frame(editor_node->custom2); + converter.add_operation(operationY); + converter.map_output_socket(outputY, operationY->get_output_socket()); TrackPositionOperation *operationMotionPreX = create_motion_operation( converter, clip, trackpos_data, 0, frame_number, -1); @@ -104,12 +103,16 @@ void TrackPositionNode::convertToOperations(NodeConverter &converter, converter, clip, trackpos_data, 1, frame_number, 1); CombineChannelsOperation *combine_operation = new CombineChannelsOperation(); - converter.addOperation(combine_operation); - converter.addLink(operationMotionPreX->getOutputSocket(), combine_operation->getInputSocket(0)); - converter.addLink(operationMotionPreY->getOutputSocket(), combine_operation->getInputSocket(1)); - converter.addLink(operationMotionPostX->getOutputSocket(), combine_operation->getInputSocket(2)); - converter.addLink(operationMotionPostY->getOutputSocket(), combine_operation->getInputSocket(3)); - converter.mapOutputSocket(outputSpeed, combine_operation->getOutputSocket()); + converter.add_operation(combine_operation); + converter.add_link(operationMotionPreX->get_output_socket(), + combine_operation->get_input_socket(0)); + converter.add_link(operationMotionPreY->get_output_socket(), + combine_operation->get_input_socket(1)); + converter.add_link(operationMotionPostX->get_output_socket(), + combine_operation->get_input_socket(2)); + converter.add_link(operationMotionPostY->get_output_socket(), + combine_operation->get_input_socket(3)); + converter.map_output_socket(output_speed, combine_operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TrackPositionNode.h b/source/blender/compositor/nodes/COM_TrackPositionNode.h index 665ba36fe09..d0b1648b0c9 100644 --- a/source/blender/compositor/nodes/COM_TrackPositionNode.h +++ b/source/blender/compositor/nodes/COM_TrackPositionNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class TrackPositionNode : public Node { public: - TrackPositionNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TrackPositionNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TransformNode.cc b/source/blender/compositor/nodes/COM_TransformNode.cc index d2fb7b54633..cd60e8c17db 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.cc +++ b/source/blender/compositor/nodes/COM_TransformNode.cc @@ -17,72 +17,92 @@ */ #include "COM_TransformNode.h" -#include "COM_ExecutionSystem.h" #include "COM_RotateOperation.h" #include "COM_ScaleOperation.h" #include "COM_SetSamplerOperation.h" -#include "COM_SetValueOperation.h" -#include "COM_TransformOperation.h" #include "COM_TranslateOperation.h" namespace blender::compositor { -TransformNode::TransformNode(bNode *editorNode) : Node(editorNode) +TransformNode::TransformNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TransformNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TransformNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - NodeInput *imageInput = this->getInputSocket(0); - NodeInput *xInput = this->getInputSocket(1); - NodeInput *yInput = this->getInputSocket(2); - NodeInput *angleInput = this->getInputSocket(3); - NodeInput *scaleInput = this->getInputSocket(4); + NodeInput *image_input = this->get_input_socket(0); + NodeInput *x_input = this->get_input_socket(1); + NodeInput *y_input = this->get_input_socket(2); + NodeInput *angle_input = this->get_input_socket(3); + NodeInput *scale_input = this->get_input_socket(4); switch (context.get_execution_model()) { case eExecutionModel::Tiled: { - ScaleRelativeOperation *scaleOperation = new ScaleRelativeOperation(); - converter.addOperation(scaleOperation); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + converter.add_operation(scale_operation); - RotateOperation *rotateOperation = new RotateOperation(); - rotateOperation->setDoDegree2RadConversion(false); - converter.addOperation(rotateOperation); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + converter.add_operation(rotate_operation); - TranslateOperation *translateOperation = new TranslateOperation(); - converter.addOperation(translateOperation); + TranslateOperation *translate_operation = new TranslateOperation(); + converter.add_operation(translate_operation); SetSamplerOperation *sampler = new SetSamplerOperation(); - sampler->setSampler((PixelSampler)this->getbNode()->custom1); - converter.addOperation(sampler); + sampler->set_sampler((PixelSampler)this->get_bnode()->custom1); + converter.add_operation(sampler); - converter.mapInputSocket(imageInput, sampler->getInputSocket(0)); - converter.addLink(sampler->getOutputSocket(), scaleOperation->getInputSocket(0)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(1)); - converter.mapInputSocket(scaleInput, scaleOperation->getInputSocket(2)); // xscale = yscale + converter.map_input_socket(image_input, sampler->get_input_socket(0)); + converter.add_link(sampler->get_output_socket(), scale_operation->get_input_socket(0)); + converter.map_input_socket(scale_input, scale_operation->get_input_socket(1)); + converter.map_input_socket(scale_input, + scale_operation->get_input_socket(2)); // xscale = yscale - converter.addLink(scaleOperation->getOutputSocket(), rotateOperation->getInputSocket(0)); - converter.mapInputSocket(angleInput, rotateOperation->getInputSocket(1)); + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.map_input_socket(angle_input, rotate_operation->get_input_socket(1)); - converter.addLink(rotateOperation->getOutputSocket(), translateOperation->getInputSocket(0)); - converter.mapInputSocket(xInput, translateOperation->getInputSocket(1)); - converter.mapInputSocket(yInput, translateOperation->getInputSocket(2)); + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); + converter.map_input_socket(x_input, translate_operation->get_input_socket(1)); + converter.map_input_socket(y_input, translate_operation->get_input_socket(2)); - converter.mapOutputSocket(getOutputSocket(), translateOperation->getOutputSocket()); + converter.map_output_socket(get_output_socket(), translate_operation->get_output_socket()); break; } case eExecutionModel::FullFrame: { - TransformOperation *op = new TransformOperation(); - op->set_sampler((PixelSampler)this->getbNode()->custom1); - converter.addOperation(op); + ScaleRelativeOperation *scale_operation = new ScaleRelativeOperation(); + converter.add_operation(scale_operation); - converter.mapInputSocket(imageInput, op->getInputSocket(0)); - converter.mapInputSocket(xInput, op->getInputSocket(1)); - converter.mapInputSocket(yInput, op->getInputSocket(2)); - converter.mapInputSocket(angleInput, op->getInputSocket(3)); - converter.mapInputSocket(scaleInput, op->getInputSocket(4)); - converter.mapOutputSocket(getOutputSocket(), op->getOutputSocket()); + RotateOperation *rotate_operation = new RotateOperation(); + rotate_operation->set_do_degree2_rad_conversion(false); + converter.add_operation(rotate_operation); + + TranslateOperation *translate_operation = new TranslateCanvasOperation(); + converter.add_operation(translate_operation); + + PixelSampler sampler = (PixelSampler)this->get_bnode()->custom1; + scale_operation->set_sampler(sampler); + rotate_operation->set_sampler(sampler); + scale_operation->set_scale_canvas_max_size(context.get_render_size()); + + converter.map_input_socket(image_input, scale_operation->get_input_socket(0)); + converter.map_input_socket(scale_input, scale_operation->get_input_socket(1)); + converter.map_input_socket(scale_input, + scale_operation->get_input_socket(2)); // xscale = yscale + + converter.add_link(scale_operation->get_output_socket(), + rotate_operation->get_input_socket(0)); + converter.map_input_socket(angle_input, rotate_operation->get_input_socket(1)); + + converter.add_link(rotate_operation->get_output_socket(), + translate_operation->get_input_socket(0)); + converter.map_input_socket(x_input, translate_operation->get_input_socket(1)); + converter.map_input_socket(y_input, translate_operation->get_input_socket(2)); + + converter.map_output_socket(get_output_socket(), translate_operation->get_output_socket()); break; } } diff --git a/source/blender/compositor/nodes/COM_TransformNode.h b/source/blender/compositor/nodes/COM_TransformNode.h index 137e162256d..66255d63813 100644 --- a/source/blender/compositor/nodes/COM_TransformNode.h +++ b/source/blender/compositor/nodes/COM_TransformNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class TransformNode : public Node { public: - TransformNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TransformNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_TranslateNode.cc b/source/blender/compositor/nodes/COM_TranslateNode.cc index 3a3e98c3472..b9f96d92942 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.cc +++ b/source/blender/compositor/nodes/COM_TranslateNode.cc @@ -18,58 +18,59 @@ #include "COM_TranslateNode.h" -#include "COM_ExecutionSystem.h" #include "COM_TranslateOperation.h" #include "COM_WrapOperation.h" #include "COM_WriteBufferOperation.h" namespace blender::compositor { -TranslateNode::TranslateNode(bNode *editorNode) : Node(editorNode) +TranslateNode::TranslateNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void TranslateNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void TranslateNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *bnode = this->getbNode(); + bNode *bnode = this->get_bnode(); NodeTranslateData *data = (NodeTranslateData *)bnode->storage; - NodeInput *inputSocket = this->getInputSocket(0); - NodeInput *inputXSocket = this->getInputSocket(1); - NodeInput *inputYSocket = this->getInputSocket(2); - NodeOutput *outputSocket = this->getOutputSocket(0); + NodeInput *input_socket = this->get_input_socket(0); + NodeInput *input_xsocket = this->get_input_socket(1); + NodeInput *input_ysocket = this->get_input_socket(2); + NodeOutput *output_socket = this->get_output_socket(0); - TranslateOperation *operation = new TranslateOperation(); + TranslateOperation *operation = context.get_execution_model() == eExecutionModel::Tiled ? + new TranslateOperation() : + new TranslateCanvasOperation(); operation->set_wrapping(data->wrap_axis); if (data->relative) { - const RenderData *rd = context.getRenderData(); - const float render_size_factor = context.getRenderPercentageAsFactor(); + const RenderData *rd = context.get_render_data(); + const float render_size_factor = context.get_render_percentage_as_factor(); float fx = rd->xsch * render_size_factor; float fy = rd->ysch * render_size_factor; operation->setFactorXY(fx, fy); } - converter.addOperation(operation); - converter.mapInputSocket(inputXSocket, operation->getInputSocket(1)); - converter.mapInputSocket(inputYSocket, operation->getInputSocket(2)); - converter.mapOutputSocket(outputSocket, operation->getOutputSocket(0)); + converter.add_operation(operation); + converter.map_input_socket(input_xsocket, operation->get_input_socket(1)); + converter.map_input_socket(input_ysocket, operation->get_input_socket(2)); + converter.map_output_socket(output_socket, operation->get_output_socket(0)); if (data->wrap_axis && context.get_execution_model() != eExecutionModel::FullFrame) { /* TODO: To be removed with tiled implementation. */ - WriteBufferOperation *writeOperation = new WriteBufferOperation(DataType::Color); - WrapOperation *wrapOperation = new WrapOperation(DataType::Color); - wrapOperation->setMemoryProxy(writeOperation->getMemoryProxy()); - wrapOperation->setWrapping(data->wrap_axis); + WriteBufferOperation *write_operation = new WriteBufferOperation(DataType::Color); + WrapOperation *wrap_operation = new WrapOperation(DataType::Color); + wrap_operation->set_memory_proxy(write_operation->get_memory_proxy()); + wrap_operation->set_wrapping(data->wrap_axis); - converter.addOperation(writeOperation); - converter.addOperation(wrapOperation); - converter.mapInputSocket(inputSocket, writeOperation->getInputSocket(0)); - converter.addLink(wrapOperation->getOutputSocket(), operation->getInputSocket(0)); + converter.add_operation(write_operation); + converter.add_operation(wrap_operation); + converter.map_input_socket(input_socket, write_operation->get_input_socket(0)); + converter.add_link(wrap_operation->get_output_socket(), operation->get_input_socket(0)); } else { - converter.mapInputSocket(inputSocket, operation->getInputSocket(0)); + converter.map_input_socket(input_socket, operation->get_input_socket(0)); } } diff --git a/source/blender/compositor/nodes/COM_TranslateNode.h b/source/blender/compositor/nodes/COM_TranslateNode.h index 0cea234bff8..5ccbb01f3f0 100644 --- a/source/blender/compositor/nodes/COM_TranslateNode.h +++ b/source/blender/compositor/nodes/COM_TranslateNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class TranslateNode : public Node { public: - TranslateNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + TranslateNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ValueNode.cc b/source/blender/compositor/nodes/COM_ValueNode.cc index 6b640fa2a3a..892a68dabcc 100644 --- a/source/blender/compositor/nodes/COM_ValueNode.cc +++ b/source/blender/compositor/nodes/COM_ValueNode.cc @@ -17,25 +17,24 @@ */ #include "COM_ValueNode.h" -#include "COM_ExecutionSystem.h" #include "COM_SetValueOperation.h" namespace blender::compositor { -ValueNode::ValueNode(bNode *editorNode) : Node(editorNode) +ValueNode::ValueNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ValueNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ValueNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { SetValueOperation *operation = new SetValueOperation(); - NodeOutput *output = this->getOutputSocket(0); - operation->setValue(output->getEditorValueFloat()); - converter.addOperation(operation); + NodeOutput *output = this->get_output_socket(0); + operation->set_value(output->get_editor_value_float()); + converter.add_operation(operation); - converter.mapOutputSocket(output, operation->getOutputSocket()); + converter.map_output_socket(output, operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ValueNode.h b/source/blender/compositor/nodes/COM_ValueNode.h index 1401b2c7e0a..d70013c48af 100644 --- a/source/blender/compositor/nodes/COM_ValueNode.h +++ b/source/blender/compositor/nodes/COM_ValueNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ValueNode : public Node { public: - ValueNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ValueNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.cc b/source/blender/compositor/nodes/COM_VectorBlurNode.cc index 7aa5f5668c9..b3f32465a34 100644 --- a/source/blender/compositor/nodes/COM_VectorBlurNode.cc +++ b/source/blender/compositor/nodes/COM_VectorBlurNode.cc @@ -18,30 +18,29 @@ #include "COM_VectorBlurNode.h" #include "COM_VectorBlurOperation.h" -#include "DNA_node_types.h" namespace blender::compositor { -VectorBlurNode::VectorBlurNode(bNode *editorNode) : Node(editorNode) +VectorBlurNode::VectorBlurNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void VectorBlurNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void VectorBlurNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *node = this->getbNode(); - NodeBlurData *vectorBlurSettings = (NodeBlurData *)node->storage; + bNode *node = this->get_bnode(); + NodeBlurData *vector_blur_settings = (NodeBlurData *)node->storage; VectorBlurOperation *operation = new VectorBlurOperation(); - operation->setVectorBlurSettings(vectorBlurSettings); - operation->setQuality(context.getQuality()); - converter.addOperation(operation); + operation->set_vector_blur_settings(vector_blur_settings); + operation->set_quality(context.get_quality()); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorBlurNode.h b/source/blender/compositor/nodes/COM_VectorBlurNode.h index 8c98a0b81a1..fb8a70ab2ec 100644 --- a/source/blender/compositor/nodes/COM_VectorBlurNode.h +++ b/source/blender/compositor/nodes/COM_VectorBlurNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class VectorBlurNode : public Node { public: - VectorBlurNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + VectorBlurNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.cc b/source/blender/compositor/nodes/COM_VectorCurveNode.cc index f2fd80cd93e..d064c5787fd 100644 --- a/source/blender/compositor/nodes/COM_VectorCurveNode.cc +++ b/source/blender/compositor/nodes/COM_VectorCurveNode.cc @@ -17,25 +17,24 @@ */ #include "COM_VectorCurveNode.h" -#include "COM_ExecutionSystem.h" #include "COM_VectorCurveOperation.h" namespace blender::compositor { -VectorCurveNode::VectorCurveNode(bNode *editorNode) : Node(editorNode) +VectorCurveNode::VectorCurveNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void VectorCurveNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void VectorCurveNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { VectorCurveOperation *operation = new VectorCurveOperation(); - operation->setCurveMapping((CurveMapping *)this->getbNode()->storage); - converter.addOperation(operation); + operation->set_curve_mapping((CurveMapping *)this->get_bnode()->storage); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); } } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_VectorCurveNode.h b/source/blender/compositor/nodes/COM_VectorCurveNode.h index ee4df5d3a42..901bd6f64b9 100644 --- a/source/blender/compositor/nodes/COM_VectorCurveNode.h +++ b/source/blender/compositor/nodes/COM_VectorCurveNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class VectorCurveNode : public Node { public: - VectorCurveNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + VectorCurveNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc index 5a03972c89d..79d535fc58a 100644 --- a/source/blender/compositor/nodes/COM_ViewLevelsNode.cc +++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.cc @@ -17,48 +17,45 @@ */ #include "COM_ViewLevelsNode.h" -#include "COM_CalculateMeanOperation.h" #include "COM_CalculateStandardDeviationOperation.h" -#include "COM_ExecutionSystem.h" -#include "COM_SetValueOperation.h" namespace blender::compositor { -ViewLevelsNode::ViewLevelsNode(bNode *editorNode) : Node(editorNode) +ViewLevelsNode::ViewLevelsNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ViewLevelsNode::convertToOperations(NodeConverter &converter, - const CompositorContext & /*context*/) const +void ViewLevelsNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - NodeInput *input = this->getInputSocket(0); - if (input->isLinked()) { + NodeInput *input = this->get_input_socket(0); + if (input->is_linked()) { /* Add preview to input-socket. */ /* calculate mean operation */ { CalculateMeanOperation *operation = new CalculateMeanOperation(); - operation->setSetting(this->getbNode()->custom1); + operation->set_setting(this->get_bnode()->custom1); - converter.addOperation(operation); - converter.mapInputSocket(input, operation->getInputSocket(0)); - converter.mapOutputSocket(this->getOutputSocket(0), operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_input_socket(input, operation->get_input_socket(0)); + converter.map_output_socket(this->get_output_socket(0), operation->get_output_socket()); } /* calculate standard deviation operation */ { CalculateStandardDeviationOperation *operation = new CalculateStandardDeviationOperation(); - operation->setSetting(this->getbNode()->custom1); + operation->set_setting(this->get_bnode()->custom1); - converter.addOperation(operation); - converter.mapInputSocket(input, operation->getInputSocket(0)); - converter.mapOutputSocket(this->getOutputSocket(1), operation->getOutputSocket()); + converter.add_operation(operation); + converter.map_input_socket(input, operation->get_input_socket(0)); + converter.map_output_socket(this->get_output_socket(1), operation->get_output_socket()); } } else { - converter.addOutputValue(getOutputSocket(0), 0.0f); - converter.addOutputValue(getOutputSocket(1), 0.0f); + converter.add_output_value(get_output_socket(0), 0.0f); + converter.add_output_value(get_output_socket(1), 0.0f); } } diff --git a/source/blender/compositor/nodes/COM_ViewLevelsNode.h b/source/blender/compositor/nodes/COM_ViewLevelsNode.h index 055d871498e..5415bbb2ee9 100644 --- a/source/blender/compositor/nodes/COM_ViewLevelsNode.h +++ b/source/blender/compositor/nodes/COM_ViewLevelsNode.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ViewLevelsNode : public Node { public: - ViewLevelsNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ViewLevelsNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cc b/source/blender/compositor/nodes/COM_ViewerNode.cc index 3833a8d7ca8..7efb23b849d 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cc +++ b/source/blender/compositor/nodes/COM_ViewerNode.cc @@ -17,71 +17,66 @@ */ #include "COM_ViewerNode.h" -#include "BKE_global.h" -#include "BKE_image.h" -#include "BKE_scene.h" -#include "BLI_listbase.h" -#include "COM_ExecutionSystem.h" #include "COM_ViewerOperation.h" namespace blender::compositor { -ViewerNode::ViewerNode(bNode *editorNode) : Node(editorNode) +ViewerNode::ViewerNode(bNode *editor_node) : Node(editor_node) { /* pass */ } -void ViewerNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void ViewerNode::convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const { - bNode *editorNode = this->getbNode(); - bool do_output = (editorNode->flag & NODE_DO_OUTPUT_RECALC || context.isRendering()) && - (editorNode->flag & NODE_DO_OUTPUT); - bool ignore_alpha = (editorNode->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; + bNode *editor_node = this->get_bnode(); + bool do_output = (editor_node->flag & NODE_DO_OUTPUT_RECALC || context.is_rendering()) && + (editor_node->flag & NODE_DO_OUTPUT); + bool ignore_alpha = (editor_node->custom2 & CMP_NODE_OUTPUT_IGNORE_ALPHA) != 0; - NodeInput *imageSocket = this->getInputSocket(0); - NodeInput *alphaSocket = this->getInputSocket(1); - NodeInput *depthSocket = this->getInputSocket(2); - Image *image = (Image *)this->getbNode()->id; - ImageUser *imageUser = (ImageUser *)this->getbNode()->storage; - ViewerOperation *viewerOperation = new ViewerOperation(); - viewerOperation->setbNodeTree(context.getbNodeTree()); - viewerOperation->setImage(image); - viewerOperation->setImageUser(imageUser); - viewerOperation->setChunkOrder((ChunkOrdering)editorNode->custom1); - viewerOperation->setCenterX(editorNode->custom3); - viewerOperation->setCenterY(editorNode->custom4); + NodeInput *image_socket = this->get_input_socket(0); + NodeInput *alpha_socket = this->get_input_socket(1); + NodeInput *depth_socket = this->get_input_socket(2); + Image *image = (Image *)this->get_bnode()->id; + ImageUser *image_user = (ImageUser *)this->get_bnode()->storage; + ViewerOperation *viewer_operation = new ViewerOperation(); + viewer_operation->set_bnodetree(context.get_bnodetree()); + viewer_operation->set_image(image); + viewer_operation->set_image_user(image_user); + viewer_operation->set_chunk_order((ChunkOrdering)editor_node->custom1); + viewer_operation->setCenterX(editor_node->custom3); + viewer_operation->setCenterY(editor_node->custom4); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ - viewerOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); - viewerOperation->setRenderData(context.getRenderData()); - viewerOperation->setViewName(context.getViewName()); + viewer_operation->set_use_alpha_input(ignore_alpha || alpha_socket->is_linked()); + viewer_operation->set_render_data(context.get_render_data()); + viewer_operation->set_view_name(context.get_view_name()); - viewerOperation->setViewSettings(context.getViewSettings()); - viewerOperation->setDisplaySettings(context.getDisplaySettings()); + viewer_operation->set_view_settings(context.get_view_settings()); + viewer_operation->set_display_settings(context.get_display_settings()); - viewerOperation->setResolutionInputSocketIndex(0); - if (!imageSocket->isLinked()) { - if (alphaSocket->isLinked()) { - viewerOperation->setResolutionInputSocketIndex(1); + viewer_operation->set_canvas_input_index(0); + if (!image_socket->is_linked()) { + if (alpha_socket->is_linked()) { + viewer_operation->set_canvas_input_index(1); } } - converter.addOperation(viewerOperation); - converter.mapInputSocket(imageSocket, viewerOperation->getInputSocket(0)); + converter.add_operation(viewer_operation); + converter.map_input_socket(image_socket, viewer_operation->get_input_socket(0)); /* only use alpha link if "use alpha" is enabled */ if (ignore_alpha) { - converter.addInputValue(viewerOperation->getInputSocket(1), 1.0f); + converter.add_input_value(viewer_operation->get_input_socket(1), 1.0f); } else { - converter.mapInputSocket(alphaSocket, viewerOperation->getInputSocket(1)); + converter.map_input_socket(alpha_socket, viewer_operation->get_input_socket(1)); } - converter.mapInputSocket(depthSocket, viewerOperation->getInputSocket(2)); + converter.map_input_socket(depth_socket, viewer_operation->get_input_socket(2)); - converter.addNodeInputPreview(imageSocket); + converter.add_node_input_preview(image_socket); if (do_output) { - converter.registerViewer(viewerOperation); + converter.register_viewer(viewer_operation); } } diff --git a/source/blender/compositor/nodes/COM_ViewerNode.h b/source/blender/compositor/nodes/COM_ViewerNode.h index 544a5e6a504..ce9076666bc 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.h +++ b/source/blender/compositor/nodes/COM_ViewerNode.h @@ -29,9 +29,9 @@ namespace blender::compositor { */ class ViewerNode : public Node { public: - ViewerNode(bNode *editorNode); - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + ViewerNode(bNode *editor_node); + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.cc b/source/blender/compositor/nodes/COM_ZCombineNode.cc index ddf66740578..52cb1451ba1 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.cc +++ b/source/blender/compositor/nodes/COM_ZCombineNode.cc @@ -18,43 +18,37 @@ #include "COM_ZCombineNode.h" -#include "COM_ZCombineOperation.h" - #include "COM_AntiAliasOperation.h" -#include "COM_ExecutionSystem.h" #include "COM_MathBaseOperation.h" -#include "COM_MixOperation.h" -#include "COM_SetValueOperation.h" - -#include "DNA_material_types.h" /* the ramp types */ +#include "COM_ZCombineOperation.h" namespace blender::compositor { -void ZCombineNode::convertToOperations(NodeConverter &converter, - const CompositorContext &context) const +void ZCombineNode::convert_to_operations(NodeConverter &converter, + const CompositorContext & /*context*/) const { - if ((context.getRenderData()->scemode & R_FULL_SAMPLE) || this->getbNode()->custom2) { + if (this->get_bnode()->custom2) { ZCombineOperation *operation = nullptr; - if (this->getbNode()->custom1) { + if (this->get_bnode()->custom1) { operation = new ZCombineAlphaOperation(); } else { operation = new ZCombineOperation(); } - converter.addOperation(operation); + converter.add_operation(operation); - converter.mapInputSocket(getInputSocket(0), operation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(1), operation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), operation->getInputSocket(2)); - converter.mapInputSocket(getInputSocket(3), operation->getInputSocket(3)); - converter.mapOutputSocket(getOutputSocket(0), operation->getOutputSocket()); + converter.map_input_socket(get_input_socket(0), operation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(1), operation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), operation->get_input_socket(2)); + converter.map_input_socket(get_input_socket(3), operation->get_input_socket(3)); + converter.map_output_socket(get_output_socket(0), operation->get_output_socket()); MathMinimumOperation *zoperation = new MathMinimumOperation(); - converter.addOperation(zoperation); + converter.add_operation(zoperation); - converter.mapInputSocket(getInputSocket(1), zoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), zoperation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(1), zoperation->getOutputSocket()); + converter.map_input_socket(get_input_socket(1), zoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(3), zoperation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(1), zoperation->get_output_socket()); } else { /* XXX custom1 is "use_alpha", what on earth is this supposed to do here?!? */ @@ -62,44 +56,42 @@ void ZCombineNode::convertToOperations(NodeConverter &converter, /* Step 1 create mask. */ NodeOperation *maskoperation; - if (this->getbNode()->custom1) { + if (this->get_bnode()->custom1) { maskoperation = new MathGreaterThanOperation(); - converter.addOperation(maskoperation); - - converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1)); } else { maskoperation = new MathLessThanOperation(); - converter.addOperation(maskoperation); - - converter.mapInputSocket(getInputSocket(1), maskoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), maskoperation->getInputSocket(1)); } + converter.add_operation(maskoperation); + + converter.map_input_socket(get_input_socket(1), maskoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(3), maskoperation->get_input_socket(1)); /* Step 2 anti alias mask bit of an expensive operation, but does the trick. */ AntiAliasOperation *antialiasoperation = new AntiAliasOperation(); - converter.addOperation(antialiasoperation); + converter.add_operation(antialiasoperation); - converter.addLink(maskoperation->getOutputSocket(), antialiasoperation->getInputSocket(0)); + converter.add_link(maskoperation->get_output_socket(), + antialiasoperation->get_input_socket(0)); /* use mask to blend between the input colors. */ - ZCombineMaskOperation *zcombineoperation = this->getbNode()->custom1 ? + ZCombineMaskOperation *zcombineoperation = this->get_bnode()->custom1 ? new ZCombineMaskAlphaOperation() : new ZCombineMaskOperation(); - converter.addOperation(zcombineoperation); + converter.add_operation(zcombineoperation); - converter.addLink(antialiasoperation->getOutputSocket(), zcombineoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(0), zcombineoperation->getInputSocket(1)); - converter.mapInputSocket(getInputSocket(2), zcombineoperation->getInputSocket(2)); - converter.mapOutputSocket(getOutputSocket(0), zcombineoperation->getOutputSocket()); + converter.add_link(antialiasoperation->get_output_socket(), + zcombineoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(0), zcombineoperation->get_input_socket(1)); + converter.map_input_socket(get_input_socket(2), zcombineoperation->get_input_socket(2)); + converter.map_output_socket(get_output_socket(0), zcombineoperation->get_output_socket()); MathMinimumOperation *zoperation = new MathMinimumOperation(); - converter.addOperation(zoperation); + converter.add_operation(zoperation); - converter.mapInputSocket(getInputSocket(1), zoperation->getInputSocket(0)); - converter.mapInputSocket(getInputSocket(3), zoperation->getInputSocket(1)); - converter.mapOutputSocket(getOutputSocket(1), zoperation->getOutputSocket()); + converter.map_input_socket(get_input_socket(1), zoperation->get_input_socket(0)); + converter.map_input_socket(get_input_socket(3), zoperation->get_input_socket(1)); + converter.map_output_socket(get_output_socket(1), zoperation->get_output_socket()); } } diff --git a/source/blender/compositor/nodes/COM_ZCombineNode.h b/source/blender/compositor/nodes/COM_ZCombineNode.h index 82f2f30fb3c..4761b336fc1 100644 --- a/source/blender/compositor/nodes/COM_ZCombineNode.h +++ b/source/blender/compositor/nodes/COM_ZCombineNode.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class ZCombineNode : public Node { public: - ZCombineNode(bNode *editorNode) : Node(editorNode) + ZCombineNode(bNode *editor_node) : Node(editor_node) { } - void convertToOperations(NodeConverter &converter, - const CompositorContext &context) const override; + void convert_to_operations(NodeConverter &converter, + const CompositorContext &context) const override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc index 30e7fab4027..5b8d376cf79 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.cc @@ -22,36 +22,36 @@ namespace blender::compositor { AlphaOverKeyOperation::AlphaOverKeyOperation() { - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } -void AlphaOverKeyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void AlphaOverKeyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputOverColor[4]; + float input_color1[4]; + float input_over_color[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_over_color, x, y, sampler); - if (inputOverColor[3] <= 0.0f) { - copy_v4_v4(output, inputColor1); + if (input_over_color[3] <= 0.0f) { + copy_v4_v4(output, input_color1); } - else if (value[0] == 1.0f && inputOverColor[3] >= 1.0f) { - copy_v4_v4(output, inputOverColor); + else if (value[0] == 1.0f && input_over_color[3] >= 1.0f) { + copy_v4_v4(output, input_over_color); } else { - float premul = value[0] * inputOverColor[3]; + float premul = value[0] * input_over_color[3]; float mul = 1.0f - premul; - output[0] = (mul * inputColor1[0]) + premul * inputOverColor[0]; - output[1] = (mul * inputColor1[1]) + premul * inputOverColor[1]; - output[2] = (mul * inputColor1[2]) + premul * inputOverColor[2]; - output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3]; + output[0] = (mul * input_color1[0]) + premul * input_over_color[0]; + output[1] = (mul * input_color1[1]) + premul * input_over_color[1]; + output[2] = (mul * input_color1[2]) + premul * input_over_color[2]; + output[3] = (mul * input_color1[3]) + value[0] * input_over_color[3]; } } diff --git a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h index 960fbc98fe9..cfcb655ceca 100644 --- a/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverKeyOperation.h @@ -33,7 +33,7 @@ class AlphaOverKeyOperation : public MixBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc index 0cc179ea209..42efca996ab 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.cc @@ -22,38 +22,38 @@ namespace blender::compositor { AlphaOverMixedOperation::AlphaOverMixedOperation() { - this->m_x = 0.0f; - this->flags.can_be_constant = true; + x_ = 0.0f; + flags_.can_be_constant = true; } -void AlphaOverMixedOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void AlphaOverMixedOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputOverColor[4]; + float input_color1[4]; + float input_over_color[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_over_color, x, y, sampler); - if (inputOverColor[3] <= 0.0f) { - copy_v4_v4(output, inputColor1); + if (input_over_color[3] <= 0.0f) { + copy_v4_v4(output, input_color1); } - else if (value[0] == 1.0f && inputOverColor[3] >= 1.0f) { - copy_v4_v4(output, inputOverColor); + else if (value[0] == 1.0f && input_over_color[3] >= 1.0f) { + copy_v4_v4(output, input_over_color); } else { - float addfac = 1.0f - this->m_x + inputOverColor[3] * this->m_x; + float addfac = 1.0f - x_ + input_over_color[3] * x_; float premul = value[0] * addfac; - float mul = 1.0f - value[0] * inputOverColor[3]; + float mul = 1.0f - value[0] * input_over_color[3]; - output[0] = (mul * inputColor1[0]) + premul * inputOverColor[0]; - output[1] = (mul * inputColor1[1]) + premul * inputOverColor[1]; - output[2] = (mul * inputColor1[2]) + premul * inputOverColor[2]; - output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3]; + output[0] = (mul * input_color1[0]) + premul * input_over_color[0]; + output[1] = (mul * input_color1[1]) + premul * input_over_color[1]; + output[2] = (mul * input_color1[2]) + premul * input_over_color[2]; + output[3] = (mul * input_color1[3]) + value[0] * input_over_color[3]; } } @@ -71,7 +71,7 @@ void AlphaOverMixedOperation::update_memory_buffer_row(PixelCursor &p) copy_v4_v4(p.out, over_color); } else { - const float addfac = 1.0f - this->m_x + over_color[3] * this->m_x; + const float addfac = 1.0f - x_ + over_color[3] * x_; const float premul = value * addfac; const float mul = 1.0f - value * over_color[3]; diff --git a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h index 2b88cd5f421..32ff77fdecd 100644 --- a/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverMixedOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class AlphaOverMixedOperation : public MixBaseOperation { private: - float m_x; + float x_; public: /** @@ -39,11 +39,11 @@ class AlphaOverMixedOperation : public MixBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void setX(float x) { - this->m_x = x; + x_ = x; } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc index a57e8c7f8a3..a627705fab5 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.cc @@ -22,36 +22,36 @@ namespace blender::compositor { AlphaOverPremultiplyOperation::AlphaOverPremultiplyOperation() { - this->flags.can_be_constant = true; + flags_.can_be_constant = true; } -void AlphaOverPremultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void AlphaOverPremultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputOverColor[4]; + float input_color1[4]; + float input_over_color[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputOverColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_over_color, x, y, sampler); /* Zero alpha values should still permit an add of RGB data */ - if (inputOverColor[3] < 0.0f) { - copy_v4_v4(output, inputColor1); + if (input_over_color[3] < 0.0f) { + copy_v4_v4(output, input_color1); } - else if (value[0] == 1.0f && inputOverColor[3] >= 1.0f) { - copy_v4_v4(output, inputOverColor); + else if (value[0] == 1.0f && input_over_color[3] >= 1.0f) { + copy_v4_v4(output, input_over_color); } else { - float mul = 1.0f - value[0] * inputOverColor[3]; + float mul = 1.0f - value[0] * input_over_color[3]; - output[0] = (mul * inputColor1[0]) + value[0] * inputOverColor[0]; - output[1] = (mul * inputColor1[1]) + value[0] * inputOverColor[1]; - output[2] = (mul * inputColor1[2]) + value[0] * inputOverColor[2]; - output[3] = (mul * inputColor1[3]) + value[0] * inputOverColor[3]; + output[0] = (mul * input_color1[0]) + value[0] * input_over_color[0]; + output[1] = (mul * input_color1[1]) + value[0] * input_over_color[1]; + output[2] = (mul * input_color1[2]) + value[0] * input_over_color[2]; + output[3] = (mul * input_color1[3]) + value[0] * input_over_color[3]; } } @@ -62,7 +62,8 @@ void AlphaOverPremultiplyOperation::update_memory_buffer_row(PixelCursor &p) const float *over_color = p.color2; const float value = *p.value; - if (over_color[3] <= 0.0f) { + /* Zero alpha values should still permit an add of RGB data. */ + if (over_color[3] < 0.0f) { copy_v4_v4(p.out, color1); } else if (value == 1.0f && over_color[3] >= 1.0f) { diff --git a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h index 701bc07cc27..cc4cb615de8 100644 --- a/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h +++ b/source/blender/compositor/operations/COM_AlphaOverPremultiplyOperation.h @@ -33,7 +33,7 @@ class AlphaOverPremultiplyOperation : public MixBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.cc b/source/blender/compositor/operations/COM_AntiAliasOperation.cc index deccbb28f49..2695e47aa60 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.cc +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.cc @@ -17,12 +17,6 @@ */ #include "COM_AntiAliasOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" - -#include "MEM_guardedalloc.h" - -#include "RE_texture.h" namespace blender::compositor { @@ -116,26 +110,26 @@ static int extrapolate9(float *E0, AntiAliasOperation::AntiAliasOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_valueReader = nullptr; - this->flags.complex = true; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + value_reader_ = nullptr; + flags_.complex = true; } -void AntiAliasOperation::initExecution() +void AntiAliasOperation::init_execution() { - this->m_valueReader = this->getInputSocketReader(0); + value_reader_ = this->get_input_socket_reader(0); } -void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) +void AntiAliasOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *input_buffer = (MemoryBuffer *)data; - const int buffer_width = input_buffer->getWidth(), buffer_height = input_buffer->getHeight(); + const int buffer_width = input_buffer->get_width(), buffer_height = input_buffer->get_height(); if (y < 0 || y >= buffer_height || x < 0 || x >= buffer_width) { output[0] = 0.0f; } else { - const float *buffer = input_buffer->getBuffer(); + const float *buffer = input_buffer->get_buffer(); const float *row_curr = &buffer[y * buffer_width]; if (x == 0 || x == buffer_width - 1 || y == 0 || y == buffer_height - 1) { output[0] = row_curr[x]; @@ -179,27 +173,27 @@ void AntiAliasOperation::executePixel(float output[4], int x, int y, void *data) } } -void AntiAliasOperation::deinitExecution() +void AntiAliasOperation::deinit_execution() { - this->m_valueReader = nullptr; + value_reader_ = nullptr; } -bool AntiAliasOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool AntiAliasOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti imageInput; - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = input->xmax + 1; - imageInput.xmin = input->xmin - 1; - imageInput.ymax = input->ymax + 1; - imageInput.ymin = input->ymin - 1; - return operation->determineDependingAreaOfInterest(&imageInput, readOperation, output); + rcti image_input; + NodeOperation *operation = get_input_operation(0); + image_input.xmax = input->xmax + 1; + image_input.xmin = input->xmin - 1; + image_input.ymax = input->ymax + 1; + image_input.ymin = input->ymin - 1; + return operation->determine_depending_area_of_interest(&image_input, read_operation, output); } -void *AntiAliasOperation::initializeTileData(rcti *rect) +void *AntiAliasOperation::initialize_tile_data(rcti *rect) { - return getInputOperation(0)->initializeTileData(rect); + return get_input_operation(0)->initialize_tile_data(rect); } void AntiAliasOperation::get_area_of_interest(const int input_idx, diff --git a/source/blender/compositor/operations/COM_AntiAliasOperation.h b/source/blender/compositor/operations/COM_AntiAliasOperation.h index b5048248425..0a6a63bf57e 100644 --- a/source/blender/compositor/operations/COM_AntiAliasOperation.h +++ b/source/blender/compositor/operations/COM_AntiAliasOperation.h @@ -33,7 +33,7 @@ class AntiAliasOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_valueReader; + SocketReader *value_reader_; public: AntiAliasOperation(); @@ -41,22 +41,22 @@ class AntiAliasOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void deinit_execution() override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc index 44680c3acd1..32780ad4ea8 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.cc @@ -17,73 +17,70 @@ */ #include "COM_BilateralBlurOperation.h" -#include "BLI_math.h" - -#include "RE_pipeline.h" namespace blender::compositor { BilateralBlurOperation::BilateralBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->flags.complex = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + flags_.complex = true; - this->m_inputColorProgram = nullptr; - this->m_inputDeterminatorProgram = nullptr; + input_color_program_ = nullptr; + input_determinator_program_ = nullptr; } -void BilateralBlurOperation::initExecution() +void BilateralBlurOperation::init_execution() { - this->m_inputColorProgram = getInputSocketReader(0); - this->m_inputDeterminatorProgram = getInputSocketReader(1); - QualityStepHelper::initExecution(COM_QH_INCREASE); + input_color_program_ = get_input_socket_reader(0); + input_determinator_program_ = get_input_socket_reader(1); + QualityStepHelper::init_execution(COM_QH_INCREASE); } -void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *data) +void BilateralBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { /* Read the determinator color at x, y, * this will be used as the reference color for the determinator. */ - float determinatorReferenceColor[4]; + float determinator_reference_color[4]; float determinator[4]; - float tempColor[4]; - float blurColor[4]; - float blurDivider; - float space = this->m_space; - float sigmacolor = this->m_data->sigma_color; + float temp_color[4]; + float blur_color[4]; + float blur_divider; + float space = space_; + float sigmacolor = data_->sigma_color; int minx = floor(x - space); int maxx = ceil(x + space); int miny = floor(y - space); int maxy = ceil(y + space); - float deltaColor; - this->m_inputDeterminatorProgram->read(determinatorReferenceColor, x, y, data); + float delta_color; + input_determinator_program_->read(determinator_reference_color, x, y, data); - zero_v4(blurColor); - blurDivider = 0.0f; + zero_v4(blur_color); + blur_divider = 0.0f; /* TODO(sergey): This isn't really good bilateral filter, it should be * using gaussian bell for weights. Also sigma_color doesn't seem to be * used correct at all. */ - for (int yi = miny; yi < maxy; yi += QualityStepHelper::getStep()) { - for (int xi = minx; xi < maxx; xi += QualityStepHelper::getStep()) { + for (int yi = miny; yi < maxy; yi += QualityStepHelper::get_step()) { + for (int xi = minx; xi < maxx; xi += QualityStepHelper::get_step()) { /* Read determinator. */ - this->m_inputDeterminatorProgram->read(determinator, xi, yi, data); - deltaColor = (fabsf(determinatorReferenceColor[0] - determinator[0]) + - fabsf(determinatorReferenceColor[1] - determinator[1]) + - /* Do not take the alpha channel into account. */ - fabsf(determinatorReferenceColor[2] - determinator[2])); - if (deltaColor < sigmacolor) { + input_determinator_program_->read(determinator, xi, yi, data); + delta_color = (fabsf(determinator_reference_color[0] - determinator[0]) + + fabsf(determinator_reference_color[1] - determinator[1]) + + /* Do not take the alpha channel into account. */ + fabsf(determinator_reference_color[2] - determinator[2])); + if (delta_color < sigmacolor) { /* Add this to the blur. */ - this->m_inputColorProgram->read(tempColor, xi, yi, data); - add_v4_v4(blurColor, tempColor); - blurDivider += 1.0f; + input_color_program_->read(temp_color, xi, yi, data); + add_v4_v4(blur_color, temp_color); + blur_divider += 1.0f; } } } - if (blurDivider > 0.0f) { - mul_v4_v4fl(output, blurColor, 1.0f / blurDivider); + if (blur_divider > 0.0f) { + mul_v4_v4fl(output, blur_color, 1.0f / blur_divider); } else { output[0] = 0.0f; @@ -93,32 +90,31 @@ void BilateralBlurOperation::executePixel(float output[4], int x, int y, void *d } } -void BilateralBlurOperation::deinitExecution() +void BilateralBlurOperation::deinit_execution() { - this->m_inputColorProgram = nullptr; - this->m_inputDeterminatorProgram = nullptr; + input_color_program_ = nullptr; + input_determinator_program_ = nullptr; } -bool BilateralBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool BilateralBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - int add = ceil(this->m_space) + 1; + rcti new_input; + int add = ceil(space_) + 1; - newInput.xmax = input->xmax + (add); - newInput.xmin = input->xmin - (add); - newInput.ymax = input->ymax + (add); - newInput.ymin = input->ymin - (add); + new_input.xmax = input->xmax + (add); + new_input.xmin = input->xmin - (add); + new_input.ymax = input->ymax + (add); + new_input.ymin = input->ymin - (add); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void BilateralBlurOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &output_area, rcti &r_input_area) { - const int add = ceil(this->m_space) + 1; + const int add = ceil(space_) + 1; r_input_area.xmax = output_area.xmax + (add); r_input_area.xmin = output_area.xmin - (add); @@ -176,11 +172,11 @@ void BilateralBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { PixelCursor p = {}; - p.step = QualityStepHelper::getStep(); - p.sigma_color = this->m_data->sigma_color; + p.step = QualityStepHelper::get_step(); + p.sigma_color = data_->sigma_color; p.input_color = inputs[0]; p.input_determinator = inputs[1]; - const float space = this->m_space; + const float space = space_; for (int y = area.ymin; y < area.ymax; y++) { p.out = output->get_elem(area.xmin, y); /* This will be used as the reference color for the determinator. */ diff --git a/source/blender/compositor/operations/COM_BilateralBlurOperation.h b/source/blender/compositor/operations/COM_BilateralBlurOperation.h index 517c5292827..42df82a5296 100644 --- a/source/blender/compositor/operations/COM_BilateralBlurOperation.h +++ b/source/blender/compositor/operations/COM_BilateralBlurOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class BilateralBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *m_inputColorProgram; - SocketReader *m_inputDeterminatorProgram; - NodeBilateralBlurData *m_data; - float m_space; + SocketReader *input_color_program_; + SocketReader *input_determinator_program_; + NodeBilateralBlurData *data_; + float space_; public: BilateralBlurOperation(); @@ -36,26 +36,26 @@ class BilateralBlurOperation : public MultiThreadedOperation, public QualityStep /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setData(NodeBilateralBlurData *data) + void set_data(NodeBilateralBlurData *data) { - this->m_data = data; - this->m_space = data->sigma_space + data->iter; + data_ = data; + space_ = data->sigma_space + data->iter; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.cc b/source/blender/compositor/operations/COM_BlurBaseOperation.cc index 058b422c4a5..aefe74a7e26 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.cc @@ -19,9 +19,6 @@ #include "COM_BlurBaseOperation.h" #include "COM_ConstantOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" - #include "RE_pipeline.h" namespace blender::compositor { @@ -29,52 +26,52 @@ namespace blender::compositor { BlurBaseOperation::BlurBaseOperation(DataType data_type) { /* data_type is almost always DataType::Color except for alpha-blur */ - this->addInputSocket(data_type); - this->addInputSocket(DataType::Value); - this->addOutputSocket(data_type); - this->flags.complex = true; - this->m_inputProgram = nullptr; - memset(&m_data, 0, sizeof(NodeBlurData)); - this->m_size = 1.0f; - this->m_sizeavailable = false; - this->m_extend_bounds = false; + this->add_input_socket(data_type); + this->add_input_socket(DataType::Value); + this->add_output_socket(data_type); + flags_.complex = true; + input_program_ = nullptr; + memset(&data_, 0, sizeof(NodeBlurData)); + size_ = 1.0f; + sizeavailable_ = false; + extend_bounds_ = false; use_variable_size_ = false; } void BlurBaseOperation::init_data() { if (execution_model_ == eExecutionModel::FullFrame) { - updateSize(); + update_size(); } - this->m_data.image_in_width = this->getWidth(); - this->m_data.image_in_height = this->getHeight(); - if (this->m_data.relative) { + data_.image_in_width = this->get_width(); + data_.image_in_height = this->get_height(); + if (data_.relative) { int sizex, sizey; - switch (this->m_data.aspect) { + switch (data_.aspect) { case CMP_NODE_BLUR_ASPECT_Y: - sizex = sizey = this->m_data.image_in_width; + sizex = sizey = data_.image_in_width; break; case CMP_NODE_BLUR_ASPECT_X: - sizex = sizey = this->m_data.image_in_height; + sizex = sizey = data_.image_in_height; break; default: - BLI_assert(this->m_data.aspect == CMP_NODE_BLUR_ASPECT_NONE); - sizex = this->m_data.image_in_width; - sizey = this->m_data.image_in_height; + BLI_assert(data_.aspect == CMP_NODE_BLUR_ASPECT_NONE); + sizex = data_.image_in_width; + sizey = data_.image_in_height; break; } - this->m_data.sizex = round_fl_to_int(this->m_data.percentx * 0.01f * sizex); - this->m_data.sizey = round_fl_to_int(this->m_data.percenty * 0.01f * sizey); + data_.sizex = round_fl_to_int(data_.percentx * 0.01f * sizex); + data_.sizey = round_fl_to_int(data_.percenty * 0.01f * sizey); } } -void BlurBaseOperation::initExecution() +void BlurBaseOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputSize = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_size_ = this->get_input_socket_reader(1); - QualityStepHelper::initExecution(COM_QH_MULTIPLY); + QualityStepHelper::init_execution(COM_QH_MULTIPLY); } float *BlurBaseOperation::make_gausstab(float rad, int size) @@ -89,7 +86,7 @@ float *BlurBaseOperation::make_gausstab(float rad, int size) sum = 0.0f; float fac = (rad > 0.0f ? 1.0f / rad : 0.0f); for (i = -size; i <= size; i++) { - val = RE_filter_value(this->m_data.filtertype, (float)i * fac); + val = RE_filter_value(data_.filtertype, (float)i * fac); sum += val; gausstab[i + size] = val; } @@ -166,77 +163,76 @@ float *BlurBaseOperation::make_dist_fac_inverse(float rad, int size, int falloff return dist_fac_invert; } -void BlurBaseOperation::deinitExecution() +void BlurBaseOperation::deinit_execution() { - this->m_inputProgram = nullptr; - this->m_inputSize = nullptr; + input_program_ = nullptr; + input_size_ = nullptr; } -void BlurBaseOperation::setData(const NodeBlurData *data) +void BlurBaseOperation::set_data(const NodeBlurData *data) { - memcpy(&m_data, data, sizeof(NodeBlurData)); + memcpy(&data_, data, sizeof(NodeBlurData)); } int BlurBaseOperation::get_blur_size(eDimension dim) const { switch (dim) { case eDimension::X: - return m_data.sizex; + return data_.sizex; case eDimension::Y: - return m_data.sizey; + return data_.sizey; } return -1; } -void BlurBaseOperation::updateSize() +void BlurBaseOperation::update_size() { - if (this->m_sizeavailable || use_variable_size_) { + if (sizeavailable_ || use_variable_size_) { return; } switch (execution_model_) { case eExecutionModel::Tiled: { float result[4]; - this->getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); - this->m_size = result[0]; + this->get_input_socket_reader(1)->read_sampled(result, 0, 0, PixelSampler::Nearest); + size_ = result[0]; break; } case eExecutionModel::FullFrame: { NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX); if (size_input->get_flags().is_constant_operation) { - m_size = *static_cast(size_input)->get_constant_elem(); + size_ = *static_cast(size_input)->get_constant_elem(); } /* Else use default. */ break; } } - this->m_sizeavailable = true; + sizeavailable_ = true; } -void BlurBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void BlurBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (!m_extend_bounds) { - NodeOperation::determineResolution(resolution, preferredResolution); + if (!extend_bounds_) { + NodeOperation::determine_canvas(preferred_area, r_area); return; } switch (execution_model_) { case eExecutionModel::Tiled: { - NodeOperation::determineResolution(resolution, preferredResolution); - resolution[0] += 2 * m_size * m_data.sizex; - resolution[1] += 2 * m_size * m_data.sizey; + NodeOperation::determine_canvas(preferred_area, r_area); + r_area.xmax += 2 * size_ * data_.sizex; + r_area.ymax += 2 * size_ * data_.sizey; break; } case eExecutionModel::FullFrame: { /* Setting a modifier ensures all non main inputs have extended bounds as preferred - * resolution, avoiding unnecessary resolution conversions that would hide constant + * canvas, avoiding unnecessary canvas conversions that would hide constant * operations. */ - set_determined_resolution_modifier([=](unsigned int res[2]) { + set_determined_canvas_modifier([=](rcti &canvas) { /* Rounding to even prevents jiggling in backdrop while switching size values. */ - res[0] += round_to_even(2 * m_size * m_data.sizex); - res[1] += round_to_even(2 * m_size * m_data.sizey); + canvas.xmax += round_to_even(2 * size_ * data_.sizex); + canvas.ymax += round_to_even(2 * size_ * data_.sizey); }); - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); break; } } @@ -251,7 +247,7 @@ void BlurBaseOperation::get_area_of_interest(const int input_idx, r_input_area = output_area; break; case 1: - r_input_area = use_variable_size_ ? output_area : COM_SINGLE_ELEM_AREA; + r_input_area = use_variable_size_ ? output_area : COM_CONSTANT_INPUT_AREA_OF_INTEREST; break; } } diff --git a/source/blender/compositor/operations/COM_BlurBaseOperation.h b/source/blender/compositor/operations/COM_BlurBaseOperation.h index 78b1e919aa6..ab378983100 100644 --- a/source/blender/compositor/operations/COM_BlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_BlurBaseOperation.h @@ -29,7 +29,7 @@ namespace blender::compositor { class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelper { private: - bool m_extend_bounds; + bool extend_bounds_; protected: static constexpr int IMAGE_INPUT_INDEX = 0; @@ -43,17 +43,17 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe #endif float *make_dist_fac_inverse(float rad, int size, int falloff); - void updateSize(); + void update_size(); /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - SocketReader *m_inputSize; - NodeBlurData m_data; + SocketReader *input_program_; + SocketReader *input_size_; + NodeBlurData data_; - float m_size; - bool m_sizeavailable; + float size_; + bool sizeavailable_; /* Flags for inheriting classes. */ bool use_variable_size_; @@ -63,30 +63,29 @@ class BlurBaseOperation : public MultiThreadedOperation, public QualityStepHelpe /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(const NodeBlurData *data); + void set_data(const NodeBlurData *data); - void setSize(float size) + void set_size(float size) { - this->m_size = size; - this->m_sizeavailable = true; + size_ = size; + sizeavailable_ = true; } - void setExtendBounds(bool extend_bounds) + void set_extend_bounds(bool extend_bounds) { - this->m_extend_bounds = extend_bounds; + extend_bounds_ = extend_bounds; } int get_blur_size(eDimension dim) const; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; virtual void get_area_of_interest(int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.cc b/source/blender/compositor/operations/COM_BokehBlurOperation.cc index f2a43b7dbca..3c8dd94c094 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.cc @@ -19,11 +19,8 @@ #include "COM_BokehBlurOperation.h" #include "COM_ConstantOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "RE_pipeline.h" - namespace blender::compositor { constexpr int IMAGE_INPUT_INDEX = 0; @@ -33,109 +30,109 @@ constexpr int SIZE_INPUT_INDEX = 3; BokehBlurOperation::BokehBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - flags.complex = true; - flags.open_cl = true; + flags_.complex = true; + flags_.open_cl = true; - this->m_size = 1.0f; - this->m_sizeavailable = false; - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputBoundingBoxReader = nullptr; + size_ = 1.0f; + sizeavailable_ = false; + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_bounding_box_reader_ = nullptr; - this->m_extend_bounds = false; + extend_bounds_ = false; } void BokehBlurOperation::init_data() { if (execution_model_ == eExecutionModel::FullFrame) { - updateSize(); + update_size(); } NodeOperation *bokeh = get_input_operation(BOKEH_INPUT_INDEX); - const int width = bokeh->getWidth(); - const int height = bokeh->getHeight(); + const int width = bokeh->get_width(); + const int height = bokeh->get_height(); const float dimension = MIN2(width, height); - m_bokehMidX = width / 2.0f; - m_bokehMidY = height / 2.0f; - m_bokehDimension = dimension / 2.0f; + bokeh_mid_x_ = width / 2.0f; + bokeh_mid_y_ = height / 2.0f; + bokehDimension_ = dimension / 2.0f; } -void *BokehBlurOperation::initializeTileData(rcti * /*rect*/) +void *BokehBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); - if (!this->m_sizeavailable) { - updateSize(); + lock_mutex(); + if (!sizeavailable_) { + update_size(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } -void BokehBlurOperation::initExecution() +void BokehBlurOperation::init_execution() { - initMutex(); + init_mutex(); - this->m_inputProgram = getInputSocketReader(0); - this->m_inputBokehProgram = getInputSocketReader(1); - this->m_inputBoundingBoxReader = getInputSocketReader(2); + input_program_ = get_input_socket_reader(0); + input_bokeh_program_ = get_input_socket_reader(1); + input_bounding_box_reader_ = get_input_socket_reader(2); - QualityStepHelper::initExecution(COM_QH_INCREASE); + QualityStepHelper::init_execution(COM_QH_INCREASE); } -void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) +void BokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float color_accum[4]; - float tempBoundingBox[4]; + float temp_bounding_box[4]; float bokeh[4]; - this->m_inputBoundingBoxReader->readSampled(tempBoundingBox, x, y, PixelSampler::Nearest); - if (tempBoundingBox[0] > 0.0f) { + input_bounding_box_reader_->read_sampled(temp_bounding_box, x, y, PixelSampler::Nearest); + if (temp_bounding_box[0] > 0.0f) { float multiplier_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - const float max_dim = MAX2(this->getWidth(), this->getHeight()); - int pixelSize = this->m_size * max_dim / 100.0f; + const float max_dim = MAX2(this->get_width(), this->get_height()); + int pixel_size = size_ * max_dim / 100.0f; zero_v4(color_accum); - if (pixelSize < 2) { - this->m_inputProgram->readSampled(color_accum, x, y, PixelSampler::Nearest); + if (pixel_size < 2) { + input_program_->read_sampled(color_accum, x, y, PixelSampler::Nearest); multiplier_accum[0] = 1.0f; multiplier_accum[1] = 1.0f; multiplier_accum[2] = 1.0f; multiplier_accum[3] = 1.0f; } - int miny = y - pixelSize; - int maxy = y + pixelSize; - int minx = x - pixelSize; - int maxx = x + pixelSize; + int miny = y - pixel_size; + int maxy = y + pixel_size; + int minx = x - pixel_size; + int maxx = x + pixel_size; miny = MAX2(miny, input_rect.ymin); minx = MAX2(minx, input_rect.xmin); maxy = MIN2(maxy, input_rect.ymax); maxx = MIN2(maxx, input_rect.xmax); - int step = getStep(); - int offsetadd = getOffsetAdd() * COM_DATA_TYPE_COLOR_CHANNELS; + int step = get_step(); + int offsetadd = get_offset_add() * COM_DATA_TYPE_COLOR_CHANNELS; - float m = this->m_bokehDimension / pixelSize; + float m = bokehDimension_ / pixel_size; for (int ny = miny; ny < maxy; ny += step) { int bufferindex = ((minx - bufferstartx) * COM_DATA_TYPE_COLOR_CHANNELS) + ((ny - bufferstarty) * COM_DATA_TYPE_COLOR_CHANNELS * bufferwidth); for (int nx = minx; nx < maxx; nx += step) { - float u = this->m_bokehMidX - (nx - x) * m; - float v = this->m_bokehMidY - (ny - y) * m; - this->m_inputBokehProgram->readSampled(bokeh, u, v, PixelSampler::Nearest); + float u = bokeh_mid_x_ - (nx - x) * m; + float v = bokeh_mid_y_ - (ny - y) * m; + input_bokeh_program_->read_sampled(bokeh, u, v, PixelSampler::Nearest); madd_v4_v4v4(color_accum, bokeh, &buffer[bufferindex]); add_v4_v4(multiplier_accum, bokeh); bufferindex += offsetadd; @@ -147,150 +144,149 @@ void BokehBlurOperation::executePixel(float output[4], int x, int y, void *data) output[3] = color_accum[3] * (1.0f / multiplier_accum[3]); } else { - this->m_inputProgram->readSampled(output, x, y, PixelSampler::Nearest); + input_program_->read_sampled(output, x, y, PixelSampler::Nearest); } } -void BokehBlurOperation::deinitExecution() +void BokehBlurOperation::deinit_execution() { - deinitMutex(); - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputBoundingBoxReader = nullptr; + deinit_mutex(); + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_bounding_box_reader_ = nullptr; } -bool BokehBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool BokehBlurOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; - rcti bokehInput; - const float max_dim = MAX2(this->getWidth(), this->getHeight()); + rcti new_input; + rcti bokeh_input; + const float max_dim = MAX2(this->get_width(), this->get_height()); - if (this->m_sizeavailable) { - newInput.xmax = input->xmax + (this->m_size * max_dim / 100.0f); - newInput.xmin = input->xmin - (this->m_size * max_dim / 100.0f); - newInput.ymax = input->ymax + (this->m_size * max_dim / 100.0f); - newInput.ymin = input->ymin - (this->m_size * max_dim / 100.0f); + if (sizeavailable_) { + new_input.xmax = input->xmax + (size_ * max_dim / 100.0f); + new_input.xmin = input->xmin - (size_ * max_dim / 100.0f); + new_input.ymax = input->ymax + (size_ * max_dim / 100.0f); + new_input.ymin = input->ymin - (size_ * max_dim / 100.0f); } else { - newInput.xmax = input->xmax + (10.0f * max_dim / 100.0f); - newInput.xmin = input->xmin - (10.0f * max_dim / 100.0f); - newInput.ymax = input->ymax + (10.0f * max_dim / 100.0f); - newInput.ymin = input->ymin - (10.0f * max_dim / 100.0f); + new_input.xmax = input->xmax + (10.0f * max_dim / 100.0f); + new_input.xmin = input->xmin - (10.0f * max_dim / 100.0f); + new_input.ymax = input->ymax + (10.0f * max_dim / 100.0f); + new_input.ymin = input->ymin - (10.0f * max_dim / 100.0f); } - NodeOperation *operation = getInputOperation(1); - bokehInput.xmax = operation->getWidth(); - bokehInput.xmin = 0; - bokehInput.ymax = operation->getHeight(); - bokehInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&bokehInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(1); + bokeh_input.xmax = operation->get_width(); + bokeh_input.xmin = 0; + bokeh_input.ymax = operation->get_height(); + bokeh_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&bokeh_input, read_operation, output)) { return true; } - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } - if (!this->m_sizeavailable) { - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + if (!sizeavailable_) { + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } } return false; } -void BokehBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void BokehBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel kernel = device->COM_clCreateKernel("bokehBlurKernel", nullptr); - if (!this->m_sizeavailable) { - updateSize(); + cl_kernel kernel = device->COM_cl_create_kernel("bokeh_blur_kernel", nullptr); + if (!sizeavailable_) { + update_size(); } - const float max_dim = MAX2(this->getWidth(), this->getHeight()); - cl_int radius = this->m_size * max_dim / 100.0f; - cl_int step = this->getStep(); + const float max_dim = MAX2(this->get_width(), this->get_height()); + cl_int radius = size_ * max_dim / 100.0f; + cl_int step = this->get_step(); - device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBoundingBoxReader); - device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 1, 4, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); - device->COM_clAttachMemoryBufferToKernelParameter( - kernel, 2, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBokehProgram); - device->COM_clAttachOutputMemoryBufferToKernelParameter(kernel, 3, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(kernel, 5, outputMemoryBuffer); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + kernel, 0, -1, cl_mem_to_clean_up, input_memory_buffers, input_bounding_box_reader_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + kernel, 1, 4, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + kernel, 2, -1, cl_mem_to_clean_up, input_memory_buffers, input_bokeh_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter(kernel, 3, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter(kernel, 5, output_memory_buffer); clSetKernelArg(kernel, 6, sizeof(cl_int), &radius); clSetKernelArg(kernel, 7, sizeof(cl_int), &step); - device->COM_clAttachSizeToKernelParameter(kernel, 8, this); + device->COM_cl_attach_size_to_kernel_parameter(kernel, 8, this); - device->COM_clEnqueueRange(kernel, outputMemoryBuffer, 9, this); + device->COM_cl_enqueue_range(kernel, output_memory_buffer, 9, this); } -void BokehBlurOperation::updateSize() +void BokehBlurOperation::update_size() { - if (this->m_sizeavailable) { + if (sizeavailable_) { return; } switch (execution_model_) { case eExecutionModel::Tiled: { float result[4]; - this->getInputSocketReader(3)->readSampled(result, 0, 0, PixelSampler::Nearest); - this->m_size = result[0]; - CLAMP(this->m_size, 0.0f, 10.0f); + this->get_input_socket_reader(3)->read_sampled(result, 0, 0, PixelSampler::Nearest); + size_ = result[0]; + CLAMP(size_, 0.0f, 10.0f); break; } case eExecutionModel::FullFrame: { NodeOperation *size_input = get_input_operation(SIZE_INPUT_INDEX); if (size_input->get_flags().is_constant_operation) { - m_size = *static_cast(size_input)->get_constant_elem(); - CLAMP(m_size, 0.0f, 10.0f); + size_ = *static_cast(size_input)->get_constant_elem(); + CLAMP(size_, 0.0f, 10.0f); } /* Else use default. */ break; } } - this->m_sizeavailable = true; + sizeavailable_ = true; } -void BokehBlurOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void BokehBlurOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (!m_extend_bounds) { - NodeOperation::determineResolution(resolution, preferredResolution); + if (!extend_bounds_) { + NodeOperation::determine_canvas(preferred_area, r_area); return; } switch (execution_model_) { case eExecutionModel::Tiled: { - NodeOperation::determineResolution(resolution, preferredResolution); - const float max_dim = MAX2(resolution[0], resolution[1]); - resolution[0] += 2 * this->m_size * max_dim / 100.0f; - resolution[1] += 2 * this->m_size * max_dim / 100.0f; + NodeOperation::determine_canvas(preferred_area, r_area); + const float max_dim = MAX2(BLI_rcti_size_x(&r_area), BLI_rcti_size_y(&r_area)); + r_area.xmax += 2 * size_ * max_dim / 100.0f; + r_area.ymax += 2 * size_ * max_dim / 100.0f; break; } case eExecutionModel::FullFrame: { - set_determined_resolution_modifier([=](unsigned int res[2]) { - const float max_dim = MAX2(res[0], res[1]); + set_determined_canvas_modifier([=](rcti &canvas) { + const float max_dim = MAX2(BLI_rcti_size_x(&canvas), BLI_rcti_size_y(&canvas)); /* Rounding to even prevents image jiggling in backdrop while switching size values. */ - float add_size = round_to_even(2 * this->m_size * max_dim / 100.0f); - res[0] += add_size; - res[1] += add_size; + float add_size = round_to_even(2 * size_ * max_dim / 100.0f); + canvas.xmax += add_size; + canvas.ymax += add_size; }); - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); break; } } @@ -302,8 +298,8 @@ void BokehBlurOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const float max_dim = MAX2(this->getWidth(), this->getHeight()); - const float add_size = m_size * max_dim / 100.0f; + const float max_dim = MAX2(this->get_width(), this->get_height()); + const float add_size = size_ * max_dim / 100.0f; r_input_area.xmin = output_area.xmin - add_size; r_input_area.xmax = output_area.xmax + add_size; r_input_area.ymin = output_area.ymin - add_size; @@ -311,18 +307,15 @@ void BokehBlurOperation::get_area_of_interest(const int input_idx, break; } case BOKEH_INPUT_INDEX: { - NodeOperation *bokeh_input = getInputOperation(BOKEH_INPUT_INDEX); - r_input_area.xmin = 0; - r_input_area.xmax = bokeh_input->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = bokeh_input->getHeight(); + NodeOperation *bokeh_input = get_input_operation(BOKEH_INPUT_INDEX); + r_input_area = bokeh_input->get_canvas(); break; } case BOUNDING_BOX_INPUT_INDEX: r_input_area = output_area; break; case SIZE_INPUT_INDEX: { - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; break; } } @@ -332,9 +325,9 @@ void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float max_dim = MAX2(this->getWidth(), this->getHeight()); - const int pixel_size = m_size * max_dim / 100.0f; - const float m = m_bokehDimension / pixel_size; + const float max_dim = MAX2(this->get_width(), this->get_height()); + const int pixel_size = size_ * max_dim / 100.0f; + const float m = bokehDimension_ / pixel_size; const MemoryBuffer *image_input = inputs[IMAGE_INPUT_INDEX]; const MemoryBuffer *bokeh_input = inputs[BOKEH_INPUT_INDEX]; @@ -363,15 +356,15 @@ void BokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, const int maxy = MIN2(y + pixel_size, image_rect.ymax); const int minx = MAX2(x - pixel_size, image_rect.xmin); const int maxx = MIN2(x + pixel_size, image_rect.xmax); - const int step = getStep(); + const int step = get_step(); const int elem_stride = image_input->elem_stride * step; const int row_stride = image_input->row_stride * step; const float *row_color = image_input->get_elem(minx, miny); for (int ny = miny; ny < maxy; ny += step, row_color += row_stride) { const float *color = row_color; - const float v = m_bokehMidY - (ny - y) * m; + const float v = bokeh_mid_y_ - (ny - y) * m; for (int nx = minx; nx < maxx; nx += step, color += elem_stride) { - const float u = m_bokehMidX - (nx - x) * m; + const float u = bokeh_mid_x_ - (nx - x) * m; float bokeh[4]; bokeh_input->read_elem_checked(u, v, bokeh); madd_v4_v4v4(color_accum, bokeh, color); diff --git a/source/blender/compositor/operations/COM_BokehBlurOperation.h b/source/blender/compositor/operations/COM_BokehBlurOperation.h index 59c14305393..5f6a880f890 100644 --- a/source/blender/compositor/operations/COM_BokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_BokehBlurOperation.h @@ -25,63 +25,62 @@ namespace blender::compositor { class BokehBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *m_inputProgram; - SocketReader *m_inputBokehProgram; - SocketReader *m_inputBoundingBoxReader; - void updateSize(); - float m_size; - bool m_sizeavailable; + SocketReader *input_program_; + SocketReader *input_bokeh_program_; + SocketReader *input_bounding_box_reader_; + void update_size(); + float size_; + bool sizeavailable_; - float m_bokehMidX; - float m_bokehMidY; - float m_bokehDimension; - bool m_extend_bounds; + float bokeh_mid_x_; + float bokeh_mid_y_; + float bokehDimension_; + bool extend_bounds_; public: BokehBlurOperation(); void init_data() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setSize(float size) + void set_size(float size) { - this->m_size = size; - this->m_sizeavailable = true; + size_ = size; + sizeavailable_ = true; } - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; - void setExtendBounds(bool extend_bounds) + void set_extend_bounds(bool extend_bounds) { - this->m_extend_bounds = extend_bounds; + extend_bounds_ = extend_bounds; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.cc b/source/blender/compositor/operations/COM_BokehImageOperation.cc index bd5b25b5af8..9e7c1162052 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.cc +++ b/source/blender/compositor/operations/COM_BokehImageOperation.cc @@ -17,139 +17,141 @@ */ #include "COM_BokehImageOperation.h" -#include "BLI_math.h" namespace blender::compositor { BokehImageOperation::BokehImageOperation() { - this->addOutputSocket(DataType::Color); - this->m_deleteData = false; + this->add_output_socket(DataType::Color); + delete_data_ = false; } -void BokehImageOperation::initExecution() +void BokehImageOperation::init_execution() { - this->m_center[0] = getWidth() / 2; - this->m_center[1] = getHeight() / 2; - this->m_inverseRounding = 1.0f - this->m_data->rounding; - this->m_circularDistance = getWidth() / 2; - this->m_flapRad = (float)(M_PI * 2) / this->m_data->flaps; - this->m_flapRadAdd = this->m_data->angle; - while (this->m_flapRadAdd < 0.0f) { - this->m_flapRadAdd += (float)(M_PI * 2.0); + center_[0] = get_width() / 2; + center_[1] = get_height() / 2; + inverse_rounding_ = 1.0f - data_->rounding; + circular_distance_ = get_width() / 2; + flap_rad_ = (float)(M_PI * 2) / data_->flaps; + flap_rad_add_ = data_->angle; + while (flap_rad_add_ < 0.0f) { + flap_rad_add_ += (float)(M_PI * 2.0); } - while (this->m_flapRadAdd > (float)M_PI) { - this->m_flapRadAdd -= (float)(M_PI * 2.0); + while (flap_rad_add_ > (float)M_PI) { + flap_rad_add_ -= (float)(M_PI * 2.0); } } -void BokehImageOperation::detemineStartPointOfFlap(float r[2], int flapNumber, float distance) +void BokehImageOperation::detemine_start_point_of_flap(float r[2], int flap_number, float distance) { - r[0] = sinf(this->m_flapRad * flapNumber + this->m_flapRadAdd) * distance + this->m_center[0]; - r[1] = cosf(this->m_flapRad * flapNumber + this->m_flapRadAdd) * distance + this->m_center[1]; + r[0] = sinf(flap_rad_ * flap_number + flap_rad_add_) * distance + center_[0]; + r[1] = cosf(flap_rad_ * flap_number + flap_rad_add_) * distance + center_[1]; } -float BokehImageOperation::isInsideBokeh(float distance, float x, float y) +float BokehImageOperation::is_inside_bokeh(float distance, float x, float y) { - float insideBokeh = 0.0f; - const float deltaX = x - this->m_center[0]; - const float deltaY = y - this->m_center[1]; - float closestPoint[2]; - float lineP1[2]; - float lineP2[2]; + float inside_bokeh = 0.0f; + const float deltaX = x - center_[0]; + const float deltaY = y - center_[1]; + float closest_point[2]; + float line_p1[2]; + float line_p2[2]; float point[2]; point[0] = x; point[1] = y; - const float distanceToCenter = len_v2v2(point, this->m_center); + const float distance_to_center = len_v2v2(point, center_); const float bearing = (atan2f(deltaX, deltaY) + (float)(M_PI * 2.0)); - int flapNumber = (int)((bearing - this->m_flapRadAdd) / this->m_flapRad); + int flap_number = (int)((bearing - flap_rad_add_) / flap_rad_); - detemineStartPointOfFlap(lineP1, flapNumber, distance); - detemineStartPointOfFlap(lineP2, flapNumber + 1, distance); - closest_to_line_v2(closestPoint, point, lineP1, lineP2); + detemine_start_point_of_flap(line_p1, flap_number, distance); + detemine_start_point_of_flap(line_p2, flap_number + 1, distance); + closest_to_line_v2(closest_point, point, line_p1, line_p2); - const float distanceLineToCenter = len_v2v2(this->m_center, closestPoint); - const float distanceRoundingToCenter = this->m_inverseRounding * distanceLineToCenter + - this->m_data->rounding * distance; + const float distance_line_to_center = len_v2v2(center_, closest_point); + const float distance_rounding_to_center = inverse_rounding_ * distance_line_to_center + + data_->rounding * distance; - const float catadioptricDistanceToCenter = distanceRoundingToCenter * this->m_data->catadioptric; - if (distanceRoundingToCenter >= distanceToCenter && - catadioptricDistanceToCenter <= distanceToCenter) { - if (distanceRoundingToCenter - distanceToCenter < 1.0f) { - insideBokeh = (distanceRoundingToCenter - distanceToCenter); + const float catadioptric_distance_to_center = distance_rounding_to_center * data_->catadioptric; + if (distance_rounding_to_center >= distance_to_center && + catadioptric_distance_to_center <= distance_to_center) { + if (distance_rounding_to_center - distance_to_center < 1.0f) { + inside_bokeh = (distance_rounding_to_center - distance_to_center); } - else if (this->m_data->catadioptric != 0.0f && - distanceToCenter - catadioptricDistanceToCenter < 1.0f) { - insideBokeh = (distanceToCenter - catadioptricDistanceToCenter); + else if (data_->catadioptric != 0.0f && + distance_to_center - catadioptric_distance_to_center < 1.0f) { + inside_bokeh = (distance_to_center - catadioptric_distance_to_center); } else { - insideBokeh = 1.0f; + inside_bokeh = 1.0f; } } - return insideBokeh; + return inside_bokeh; } -void BokehImageOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void BokehImageOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - float shift = this->m_data->lensshift; + float shift = data_->lensshift; float shift2 = shift / 2.0f; - float distance = this->m_circularDistance; - float insideBokehMax = isInsideBokeh(distance, x, y); - float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), x, y); - float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), x, y); + float distance = circular_distance_; + float inside_bokeh_max = is_inside_bokeh(distance, x, y); + float inside_bokeh_med = is_inside_bokeh(distance - fabsf(shift2 * distance), x, y); + float inside_bokeh_min = is_inside_bokeh(distance - fabsf(shift * distance), x, y); if (shift < 0) { - output[0] = insideBokehMax; - output[1] = insideBokehMed; - output[2] = insideBokehMin; + output[0] = inside_bokeh_max; + output[1] = inside_bokeh_med; + output[2] = inside_bokeh_min; } else { - output[0] = insideBokehMin; - output[1] = insideBokehMed; - output[2] = insideBokehMax; + output[0] = inside_bokeh_min; + output[1] = inside_bokeh_med; + output[2] = inside_bokeh_max; } - output[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; + output[3] = (inside_bokeh_max + inside_bokeh_med + inside_bokeh_min) / 3.0f; } void BokehImageOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - const float shift = this->m_data->lensshift; + const float shift = data_->lensshift; const float shift2 = shift / 2.0f; - const float distance = this->m_circularDistance; + const float distance = circular_distance_; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - const float insideBokehMax = isInsideBokeh(distance, it.x, it.y); - const float insideBokehMed = isInsideBokeh(distance - fabsf(shift2 * distance), it.x, it.y); - const float insideBokehMin = isInsideBokeh(distance - fabsf(shift * distance), it.x, it.y); + const float inside_bokeh_max = is_inside_bokeh(distance, it.x, it.y); + const float inside_bokeh_med = is_inside_bokeh( + distance - fabsf(shift2 * distance), it.x, it.y); + const float inside_bokeh_min = is_inside_bokeh(distance - fabsf(shift * distance), it.x, it.y); if (shift < 0) { - it.out[0] = insideBokehMax; - it.out[1] = insideBokehMed; - it.out[2] = insideBokehMin; + it.out[0] = inside_bokeh_max; + it.out[1] = inside_bokeh_med; + it.out[2] = inside_bokeh_min; } else { - it.out[0] = insideBokehMin; - it.out[1] = insideBokehMed; - it.out[2] = insideBokehMax; + it.out[0] = inside_bokeh_min; + it.out[1] = inside_bokeh_med; + it.out[2] = inside_bokeh_max; } - it.out[3] = (insideBokehMax + insideBokehMed + insideBokehMin) / 3.0f; + it.out[3] = (inside_bokeh_max + inside_bokeh_med + inside_bokeh_min) / 3.0f; } } -void BokehImageOperation::deinitExecution() +void BokehImageOperation::deinit_execution() { - if (this->m_deleteData) { - if (this->m_data) { - delete this->m_data; - this->m_data = nullptr; + if (delete_data_) { + if (data_) { + delete data_; + data_ = nullptr; } } } -void BokehImageOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void BokehImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = COM_BLUR_BOKEH_PIXELS; - resolution[1] = COM_BLUR_BOKEH_PIXELS; + BLI_rcti_init(&r_area, + preferred_area.xmin, + preferred_area.xmin + COM_BLUR_BOKEH_PIXELS, + preferred_area.ymin, + preferred_area.ymin + COM_BLUR_BOKEH_PIXELS); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BokehImageOperation.h b/source/blender/compositor/operations/COM_BokehImageOperation.h index 2527233fabd..6c7818724b3 100644 --- a/source/blender/compositor/operations/COM_BokehImageOperation.h +++ b/source/blender/compositor/operations/COM_BokehImageOperation.h @@ -54,46 +54,46 @@ class BokehImageOperation : public MultiThreadedOperation { /** * \brief Settings of the bokeh image */ - NodeBokehImage *m_data; + NodeBokehImage *data_; /** * \brief precalculate center of the image */ - float m_center[2]; + float center_[2]; /** * \brief 1.0-rounding */ - float m_inverseRounding; + float inverse_rounding_; /** * \brief distance of a full circle lens */ - float m_circularDistance; + float circular_distance_; /** * \brief radius when the first flap starts */ - float m_flapRad; + float flap_rad_; /** * \brief radians of a single flap */ - float m_flapRadAdd; + float flap_rad_add_; /** - * \brief should the m_data field by deleted when this operation is finished + * \brief should the data_ field by deleted when this operation is finished */ - bool m_deleteData; + bool delete_data_; /** * \brief determine the coordinate of a flap corner. * * \param r: result in bokeh-image space are stored [x,y] - * \param flapNumber: the flap number to calculate + * \param flap_number: the flap number to calculate * \param distance: the lens distance is used to simulate lens shifts */ - void detemineStartPointOfFlap(float r[2], int flapNumber, float distance); + void detemine_start_point_of_flap(float r[2], int flap_number, float distance); /** * \brief Determine if a coordinate is inside the bokeh image @@ -104,7 +104,7 @@ class BokehImageOperation : public MultiThreadedOperation { * \param y: the y coordinate of the pixel to evaluate * \return float range 0..1 0 is completely outside */ - float isInsideBokeh(float distance, float x, float y); + float is_inside_bokeh(float distance, float x, float y); public: BokehImageOperation(); @@ -112,44 +112,43 @@ class BokehImageOperation : public MultiThreadedOperation { /** * \brief The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * \brief Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * \brief Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * \brief determine the resolution of this operation. currently fixed at [COM_BLUR_BOKEH_PIXELS, - * COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferredResolution: + * COM_BLUR_BOKEH_PIXELS] \param resolution: \param preferred_resolution: */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; /** * \brief set the node data * \param data: */ - void setData(NodeBokehImage *data) + void set_data(NodeBokehImage *data) { - this->m_data = data; + data_ = data; } /** - * \brief deleteDataOnFinish + * \brief delete_data_on_finish * * There are cases that the compositor uses this operation on its own (see defocus node) - * the deleteDataOnFinish must only be called when the data has been created by the compositor. - *It should not be called when the data has been created by the node-editor/user. + * the delete_data_on_finish must only be called when the data has been created by the + *compositor. It should not be called when the data has been created by the node-editor/user. */ - void deleteDataOnFinish() + void delete_data_on_finish() { - this->m_deleteData = true; + delete_data_ = true; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.cc b/source/blender/compositor/operations/COM_BoxMaskOperation.cc index 15bb19660dc..c79179a3e0a 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.cc +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.cc @@ -17,73 +17,74 @@ */ #include "COM_BoxMaskOperation.h" -#include "BLI_math.h" -#include "DNA_node_types.h" namespace blender::compositor { BoxMaskOperation::BoxMaskOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; - this->m_cosine = 0.0f; - this->m_sine = 0.0f; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_mask_ = nullptr; + input_value_ = nullptr; + cosine_ = 0.0f; + sine_ = 0.0f; } -void BoxMaskOperation::initExecution() +void BoxMaskOperation::init_execution() { - this->m_inputMask = this->getInputSocketReader(0); - this->m_inputValue = this->getInputSocketReader(1); - const double rad = (double)this->m_data->rotation; - this->m_cosine = cos(rad); - this->m_sine = sin(rad); - this->m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); + input_mask_ = this->get_input_socket_reader(0); + input_value_ = this->get_input_socket_reader(1); + const double rad = (double)data_->rotation; + cosine_ = cos(rad); + sine_ = sin(rad); + aspect_ratio_ = ((float)this->get_width()) / this->get_height(); } -void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void BoxMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputMask[4]; - float inputValue[4]; + float input_mask[4]; + float input_value[4]; - float rx = x / this->getWidth(); - float ry = y / this->getHeight(); + float rx = x / MAX2(this->get_width() - 1.0f, FLT_EPSILON); + float ry = y / MAX2(this->get_height() - 1.0f, FLT_EPSILON); - const float dy = (ry - this->m_data->y) / this->m_aspectRatio; - const float dx = rx - this->m_data->x; - rx = this->m_data->x + (this->m_cosine * dx + this->m_sine * dy); - ry = this->m_data->y + (-this->m_sine * dx + this->m_cosine * dy); + const float dy = (ry - data_->y) / aspect_ratio_; + const float dx = rx - data_->x; + rx = data_->x + (cosine_ * dx + sine_ * dy); + ry = data_->y + (-sine_ * dx + cosine_ * dy); - this->m_inputMask->readSampled(inputMask, x, y, sampler); - this->m_inputValue->readSampled(inputValue, x, y, sampler); + input_mask_->read_sampled(input_mask, x, y, sampler); + input_value_->read_sampled(input_value, x, y, sampler); - float halfHeight = this->m_data->height / 2.0f; - float halfWidth = this->m_data->width / 2.0f; - bool inside = (rx > this->m_data->x - halfWidth && rx < this->m_data->x + halfWidth && - ry > this->m_data->y - halfHeight && ry < this->m_data->y + halfHeight); + float half_height = data_->height / 2.0f + FLT_EPSILON; + float half_width = data_->width / 2.0f + FLT_EPSILON; + bool inside = (rx >= data_->x - half_width && rx <= data_->x + half_width && + ry >= data_->y - half_height && ry <= data_->y + half_height); - switch (this->m_maskType) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: if (inside) { - output[0] = MAX2(inputMask[0], inputValue[0]); + output[0] = MAX2(input_mask[0], input_value[0]); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_SUBTRACT: if (inside) { - output[0] = inputMask[0] - inputValue[0]; + output[0] = input_mask[0] - input_value[0]; CLAMP(output[0], 0, 1); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_MULTIPLY: if (inside) { - output[0] = inputMask[0] * inputValue[0]; + output[0] = input_mask[0] * input_value[0]; } else { output[0] = 0; @@ -91,15 +92,15 @@ void BoxMaskOperation::executePixelSampled(float output[4], float x, float y, Pi break; case CMP_NODE_MASKTYPE_NOT: if (inside) { - if (inputMask[0] > 0.0f) { + if (input_mask[0] > 0.0f) { output[0] = 0; } else { - output[0] = inputValue[0]; + output[0] = input_value[0]; } } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; } @@ -110,7 +111,7 @@ void BoxMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { MaskFunc mask_func; - switch (m_maskType) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: mask_func = [](const bool is_inside, const float *mask, const float *value) { return is_inside ? MAX2(mask[0], value[0]) : mask[0]; @@ -143,30 +144,30 @@ void BoxMaskOperation::apply_mask(MemoryBuffer *output, Span inputs, MaskFunc mask_func) { - const float op_w = this->getWidth(); - const float op_h = this->getHeight(); - const float half_w = this->m_data->width / 2.0f; - const float half_h = this->m_data->height / 2.0f; + const float op_last_x = MAX2(this->get_width() - 1.0f, FLT_EPSILON); + const float op_last_y = MAX2(this->get_height() - 1.0f, FLT_EPSILON); + const float half_w = data_->width / 2.0f + FLT_EPSILON; + const float half_h = data_->height / 2.0f + FLT_EPSILON; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - const float op_ry = it.y / op_h; - const float dy = (op_ry - this->m_data->y) / m_aspectRatio; - const float op_rx = it.x / op_w; - const float dx = op_rx - this->m_data->x; - const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy); - const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy); + const float op_ry = it.y / op_last_y; + const float dy = (op_ry - data_->y) / aspect_ratio_; + const float op_rx = it.x / op_last_x; + const float dx = op_rx - data_->x; + const float rx = data_->x + (cosine_ * dx + sine_ * dy); + const float ry = data_->y + (-sine_ * dx + cosine_ * dy); - const bool inside = (rx > this->m_data->x - half_w && rx < this->m_data->x + half_w && - ry > this->m_data->y - half_h && ry < this->m_data->y + half_h); + const bool inside = (rx >= data_->x - half_w && rx <= data_->x + half_w && + ry >= data_->y - half_h && ry <= data_->y + half_h); const float *mask = it.in(0); const float *value = it.in(1); *it.out = mask_func(inside, mask, value); } } -void BoxMaskOperation::deinitExecution() +void BoxMaskOperation::deinit_execution() { - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; + input_mask_ = nullptr; + input_value_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BoxMaskOperation.h b/source/blender/compositor/operations/COM_BoxMaskOperation.h index 4c48dde844a..4aee88556ee 100644 --- a/source/blender/compositor/operations/COM_BoxMaskOperation.h +++ b/source/blender/compositor/operations/COM_BoxMaskOperation.h @@ -27,17 +27,17 @@ class BoxMaskOperation : public MultiThreadedOperation { using MaskFunc = std::function; /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputMask; - SocketReader *m_inputValue; + SocketReader *input_mask_; + SocketReader *input_value_; - float m_sine; - float m_cosine; - float m_aspectRatio; - int m_maskType; + float sine_; + float cosine_; + float aspect_ratio_; + int mask_type_; - NodeBoxMask *m_data; + NodeBoxMask *data_; public: BoxMaskOperation(); @@ -45,26 +45,26 @@ class BoxMaskOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeBoxMask *data) + void set_data(NodeBoxMask *data) { - this->m_data = data; + data_ = data; } - void setMaskType(int maskType) + void set_mask_type(int mask_type) { - this->m_maskType = maskType; + mask_type_ = mask_type; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.cc b/source/blender/compositor/operations/COM_BrightnessOperation.cc index 7878eca2bbd..cebcc14b13a 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.cc +++ b/source/blender/compositor/operations/COM_BrightnessOperation.cc @@ -22,41 +22,41 @@ namespace blender::compositor { BrightnessOperation::BrightnessOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_use_premultiply = false; - flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + use_premultiply_ = false; + flags_.can_be_constant = true; } -void BrightnessOperation::setUsePremultiply(bool use_premultiply) +void BrightnessOperation::set_use_premultiply(bool use_premultiply) { - this->m_use_premultiply = use_premultiply; + use_premultiply_ = use_premultiply; } -void BrightnessOperation::initExecution() +void BrightnessOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputBrightnessProgram = this->getInputSocketReader(1); - this->m_inputContrastProgram = this->getInputSocketReader(2); + input_program_ = this->get_input_socket_reader(0); + input_brightness_program_ = this->get_input_socket_reader(1); + input_contrast_program_ = this->get_input_socket_reader(2); } -void BrightnessOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void BrightnessOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; + float input_value[4]; float a, b; - float inputBrightness[4]; - float inputContrast[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputBrightnessProgram->readSampled(inputBrightness, x, y, sampler); - this->m_inputContrastProgram->readSampled(inputContrast, x, y, sampler); - float brightness = inputBrightness[0]; - float contrast = inputContrast[0]; + float input_brightness[4]; + float input_contrast[4]; + input_program_->read_sampled(input_value, x, y, sampler); + input_brightness_program_->read_sampled(input_brightness, x, y, sampler); + input_contrast_program_->read_sampled(input_contrast, x, y, sampler); + float brightness = input_brightness[0]; + float contrast = input_contrast[0]; brightness /= 100.0f; float delta = contrast / 200.0f; /* @@ -74,14 +74,14 @@ void BrightnessOperation::executePixelSampled(float output[4], a = max_ff(1.0f - delta * 2.0f, 0.0f); b = a * brightness + delta; } - if (this->m_use_premultiply) { - premul_to_straight_v4(inputValue); + if (use_premultiply_) { + premul_to_straight_v4(input_value); } - output[0] = a * inputValue[0] + b; - output[1] = a * inputValue[1] + b; - output[2] = a * inputValue[2] + b; - output[3] = inputValue[3]; - if (this->m_use_premultiply) { + output[0] = a * input_value[0] + b; + output[1] = a * input_value[1] + b; + output[2] = a * input_value[2] + b; + output[3] = input_value[3]; + if (use_premultiply_) { straight_to_premul_v4(output); } } @@ -113,7 +113,7 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, b = a * brightness + delta; } const float *color; - if (this->m_use_premultiply) { + if (use_premultiply_) { premul_to_straight_v4_v4(tmp_color, in_color); color = tmp_color; } @@ -124,17 +124,17 @@ void BrightnessOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[1] = a * color[1] + b; it.out[2] = a * color[2] + b; it.out[3] = color[3]; - if (this->m_use_premultiply) { + if (use_premultiply_) { straight_to_premul_v4(it.out); } } } -void BrightnessOperation::deinitExecution() +void BrightnessOperation::deinit_execution() { - this->m_inputProgram = nullptr; - this->m_inputBrightnessProgram = nullptr; - this->m_inputContrastProgram = nullptr; + input_program_ = nullptr; + input_brightness_program_ = nullptr; + input_contrast_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_BrightnessOperation.h b/source/blender/compositor/operations/COM_BrightnessOperation.h index 64b4fa0dbe2..003baa96613 100644 --- a/source/blender/compositor/operations/COM_BrightnessOperation.h +++ b/source/blender/compositor/operations/COM_BrightnessOperation.h @@ -25,13 +25,13 @@ namespace blender::compositor { class BrightnessOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - SocketReader *m_inputBrightnessProgram; - SocketReader *m_inputContrastProgram; + SocketReader *input_program_; + SocketReader *input_brightness_program_; + SocketReader *input_contrast_program_; - bool m_use_premultiply; + bool use_premultiply_; public: BrightnessOperation(); @@ -39,19 +39,19 @@ class BrightnessOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setUsePremultiply(bool use_premultiply); + void set_use_premultiply(bool use_premultiply); void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc index 7457ac9e227..b8d85675041 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.cc @@ -17,8 +17,7 @@ */ #include "COM_CalculateMeanOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" + #include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" @@ -27,74 +26,73 @@ namespace blender::compositor { CalculateMeanOperation::CalculateMeanOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addOutputSocket(DataType::Value); - this->m_imageReader = nullptr; - this->m_iscalculated = false; - this->m_setting = 1; - this->flags.complex = true; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Value); + image_reader_ = nullptr; + iscalculated_ = false; + setting_ = 1; + flags_.complex = true; } -void CalculateMeanOperation::initExecution() +void CalculateMeanOperation::init_execution() { - this->m_imageReader = this->getInputSocketReader(0); - this->m_iscalculated = false; - NodeOperation::initMutex(); + image_reader_ = this->get_input_socket_reader(0); + iscalculated_ = false; + NodeOperation::init_mutex(); } -void CalculateMeanOperation::executePixel(float output[4], int /*x*/, int /*y*/, void * /*data*/) +void CalculateMeanOperation::execute_pixel(float output[4], int /*x*/, int /*y*/, void * /*data*/) { - output[0] = this->m_result; + output[0] = result_; } -void CalculateMeanOperation::deinitExecution() +void CalculateMeanOperation::deinit_execution() { - this->m_imageReader = nullptr; - NodeOperation::deinitMutex(); + image_reader_ = nullptr; + NodeOperation::deinit_mutex(); } -bool CalculateMeanOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool CalculateMeanOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti imageInput; - if (this->m_iscalculated) { + rcti image_input; + if (iscalculated_) { return false; } - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; } -void *CalculateMeanOperation::initializeTileData(rcti *rect) +void *CalculateMeanOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (!this->m_iscalculated) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); - calculateMean(tile); - this->m_iscalculated = true; + lock_mutex(); + if (!iscalculated_) { + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); + calculate_mean(tile); + iscalculated_ = true; } - unlockMutex(); + unlock_mutex(); return nullptr; } -void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) +void CalculateMeanOperation::calculate_mean(MemoryBuffer *tile) { - this->m_result = 0.0f; - float *buffer = tile->getBuffer(); - int size = tile->getWidth() * tile->getHeight(); + result_ = 0.0f; + float *buffer = tile->get_buffer(); + int size = tile->get_width() * tile->get_height(); int pixels = 0; float sum = 0.0f; for (int i = 0, offset = 0; i < size; i++, offset += 4) { if (buffer[offset + 3] > 0) { pixels++; - switch (this->m_setting) { + switch (setting_) { case 1: { sum += IMB_colormanagement_get_luminance(&buffer[offset]); break; @@ -126,12 +124,12 @@ void CalculateMeanOperation::calculateMean(MemoryBuffer *tile) } } } - this->m_result = sum / pixels; + result_ = sum / pixels; } -void CalculateMeanOperation::setSetting(int setting) +void CalculateMeanOperation::set_setting(int setting) { - this->m_setting = setting; + setting_ = setting; switch (setting) { case 1: { setting_func_ = IMB_colormanagement_get_luminance; @@ -165,21 +163,17 @@ void CalculateMeanOperation::get_area_of_interest(int input_idx, rcti &r_input_area) { BLI_assert(input_idx == 0); - NodeOperation *operation = getInputOperation(input_idx); - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = operation->getWidth(); - r_input_area.ymax = operation->getHeight(); + r_input_area = get_input_operation(input_idx)->get_canvas(); } void CalculateMeanOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - if (!this->m_iscalculated) { + if (!iscalculated_) { MemoryBuffer *input = inputs[0]; - m_result = calc_mean(input); - this->m_iscalculated = true; + result_ = calc_mean(input); + iscalculated_ = true; } } @@ -187,7 +181,7 @@ void CalculateMeanOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->fill(area, &m_result); + output->fill(area, &result_); } float CalculateMeanOperation::calc_mean(const MemoryBuffer *input) diff --git a/source/blender/compositor/operations/COM_CalculateMeanOperation.h b/source/blender/compositor/operations/COM_CalculateMeanOperation.h index 779ca79b38a..7c4fafb2f27 100644 --- a/source/blender/compositor/operations/COM_CalculateMeanOperation.h +++ b/source/blender/compositor/operations/COM_CalculateMeanOperation.h @@ -39,11 +39,11 @@ class CalculateMeanOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_imageReader; + SocketReader *image_reader_; - bool m_iscalculated; - float m_result; - int m_setting; + bool iscalculated_; + float result_; + int setting_; std::function setting_func_; public: @@ -52,24 +52,24 @@ class CalculateMeanOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void setSetting(int setting); + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void set_setting(int setting); void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; @@ -82,7 +82,7 @@ class CalculateMeanOperation : public MultiThreadedOperation { Span inputs) override; protected: - void calculateMean(MemoryBuffer *tile); + void calculate_mean(MemoryBuffer *tile); float calc_mean(const MemoryBuffer *input); private: diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc index 494b66cb888..06b3114845e 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.cc @@ -17,39 +17,38 @@ */ #include "COM_CalculateStandardDeviationOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" + #include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" namespace blender::compositor { -void CalculateStandardDeviationOperation::executePixel(float output[4], - int /*x*/, - int /*y*/, - void * /*data*/) +void CalculateStandardDeviationOperation::execute_pixel(float output[4], + int /*x*/, + int /*y*/, + void * /*data*/) { - output[0] = this->m_standardDeviation; + output[0] = standard_deviation_; } -void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) +void *CalculateStandardDeviationOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (!this->m_iscalculated) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); - CalculateMeanOperation::calculateMean(tile); - this->m_standardDeviation = 0.0f; - float *buffer = tile->getBuffer(); - int size = tile->getWidth() * tile->getHeight(); + lock_mutex(); + if (!iscalculated_) { + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); + CalculateMeanOperation::calculate_mean(tile); + standard_deviation_ = 0.0f; + float *buffer = tile->get_buffer(); + int size = tile->get_width() * tile->get_height(); int pixels = 0; float sum = 0.0f; - float mean = this->m_result; + float mean = result_; for (int i = 0, offset = 0; i < size; i++, offset += 4) { if (buffer[offset + 3] > 0) { pixels++; - switch (this->m_setting) { + switch (setting_) { case 1: /* rgb combined */ { float value = IMB_colormanagement_get_luminance(&buffer[offset]); @@ -90,17 +89,17 @@ void *CalculateStandardDeviationOperation::initializeTileData(rcti *rect) } } } - this->m_standardDeviation = sqrt(sum / (float)(pixels - 1)); - this->m_iscalculated = true; + standard_deviation_ = sqrt(sum / (float)(pixels - 1)); + iscalculated_ = true; } - unlockMutex(); + unlock_mutex(); return nullptr; } void CalculateStandardDeviationOperation::update_memory_buffer_started( MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - if (!this->m_iscalculated) { + if (!iscalculated_) { const MemoryBuffer *input = inputs[0]; const float mean = CalculateMeanOperation::calc_mean(input); @@ -113,17 +112,16 @@ void CalculateStandardDeviationOperation::update_memory_buffer_started( join.sum += chunk.sum; join.num_pixels += chunk.num_pixels; }); - this->m_standardDeviation = total.num_pixels <= 1 ? - 0.0f : - sqrt(total.sum / (float)(total.num_pixels - 1)); - this->m_iscalculated = true; + standard_deviation_ = total.num_pixels <= 1 ? 0.0f : + sqrt(total.sum / (float)(total.num_pixels - 1)); + iscalculated_ = true; } } void CalculateStandardDeviationOperation::update_memory_buffer_partial( MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->fill(area, &m_standardDeviation); + output->fill(area, &standard_deviation_); } using PixelsSum = CalculateMeanOperation::PixelsSum; diff --git a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h index 20de4cf4701..95b9eb21982 100644 --- a/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h +++ b/source/blender/compositor/operations/COM_CalculateStandardDeviationOperation.h @@ -31,15 +31,15 @@ namespace blender::compositor { */ class CalculateStandardDeviationOperation : public CalculateMeanOperation { protected: - float m_standardDeviation; + float standard_deviation_; public: /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; void update_memory_buffer_started(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc index 1e3e7806968..7c64b1954d4 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.cc +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.cc @@ -22,54 +22,54 @@ namespace blender::compositor { ChangeHSVOperation::ChangeHSVOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->m_inputOperation = nullptr; - this->flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_operation_ = nullptr; + flags_.can_be_constant = true; } -void ChangeHSVOperation::initExecution() +void ChangeHSVOperation::init_execution() { - this->m_inputOperation = getInputSocketReader(0); - this->m_hueOperation = getInputSocketReader(1); - this->m_saturationOperation = getInputSocketReader(2); - this->m_valueOperation = getInputSocketReader(3); + input_operation_ = get_input_socket_reader(0); + hue_operation_ = get_input_socket_reader(1); + saturation_operation_ = get_input_socket_reader(2); + value_operation_ = get_input_socket_reader(3); } -void ChangeHSVOperation::deinitExecution() +void ChangeHSVOperation::deinit_execution() { - this->m_inputOperation = nullptr; - this->m_hueOperation = nullptr; - this->m_saturationOperation = nullptr; - this->m_valueOperation = nullptr; + input_operation_ = nullptr; + hue_operation_ = nullptr; + saturation_operation_ = nullptr; + value_operation_ = nullptr; } -void ChangeHSVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ChangeHSVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; + float input_color1[4]; float hue[4], saturation[4], value[4]; - this->m_inputOperation->readSampled(inputColor1, x, y, sampler); - this->m_hueOperation->readSampled(hue, x, y, sampler); - this->m_saturationOperation->readSampled(saturation, x, y, sampler); - this->m_valueOperation->readSampled(value, x, y, sampler); + input_operation_->read_sampled(input_color1, x, y, sampler); + hue_operation_->read_sampled(hue, x, y, sampler); + saturation_operation_->read_sampled(saturation, x, y, sampler); + value_operation_->read_sampled(value, x, y, sampler); - output[0] = inputColor1[0] + (hue[0] - 0.5f); + output[0] = input_color1[0] + (hue[0] - 0.5f); if (output[0] > 1.0f) { output[0] -= 1.0f; } else if (output[0] < 0.0f) { output[0] += 1.0f; } - output[1] = inputColor1[1] * saturation[0]; - output[2] = inputColor1[2] * value[0]; - output[3] = inputColor1[3]; + output[1] = input_color1[1] * saturation[0]; + output[2] = input_color1[2] * value[0]; + output[3] = input_color1[3]; } void ChangeHSVOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ChangeHSVOperation.h b/source/blender/compositor/operations/COM_ChangeHSVOperation.h index e7bc3274f25..35e606f63df 100644 --- a/source/blender/compositor/operations/COM_ChangeHSVOperation.h +++ b/source/blender/compositor/operations/COM_ChangeHSVOperation.h @@ -28,10 +28,10 @@ namespace blender::compositor { */ class ChangeHSVOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - SocketReader *m_hueOperation; - SocketReader *m_saturationOperation; - SocketReader *m_valueOperation; + SocketReader *input_operation_; + SocketReader *hue_operation_; + SocketReader *saturation_operation_; + SocketReader *value_operation_; public: /** @@ -39,13 +39,13 @@ class ChangeHSVOperation : public MultiThreadedOperation { */ ChangeHSVOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc index 65742d0cfcc..af20193118c 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.cc @@ -17,55 +17,54 @@ */ #include "COM_ChannelMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { ChannelMatteOperation::ChannelMatteOperation() { - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - this->m_inputImageProgram = nullptr; - flags.can_be_constant = true; + input_image_program_ = nullptr; + flags_.can_be_constant = true; } -void ChannelMatteOperation::initExecution() +void ChannelMatteOperation::init_execution() { - this->m_inputImageProgram = this->getInputSocketReader(0); + input_image_program_ = this->get_input_socket_reader(0); - this->m_limit_range = this->m_limit_max - this->m_limit_min; + limit_range_ = limit_max_ - limit_min_; - switch (this->m_limit_method) { + switch (limit_method_) { /* SINGLE */ case 0: { /* 123 / RGB / HSV / YUV / YCC */ - const int matte_channel = this->m_matte_channel - 1; - const int limit_channel = this->m_limit_channel - 1; - this->m_ids[0] = matte_channel; - this->m_ids[1] = limit_channel; - this->m_ids[2] = limit_channel; + const int matte_channel = matte_channel_ - 1; + const int limit_channel = limit_channel_ - 1; + ids_[0] = matte_channel; + ids_[1] = limit_channel; + ids_[2] = limit_channel; break; } /* MAX */ case 1: { - switch (this->m_matte_channel) { + switch (matte_channel_) { case 1: { - this->m_ids[0] = 0; - this->m_ids[1] = 1; - this->m_ids[2] = 2; + ids_[0] = 0; + ids_[1] = 1; + ids_[2] = 2; break; } case 2: { - this->m_ids[0] = 1; - this->m_ids[1] = 0; - this->m_ids[2] = 2; + ids_[0] = 1; + ids_[1] = 0; + ids_[2] = 2; break; } case 3: { - this->m_ids[0] = 2; - this->m_ids[1] = 0; - this->m_ids[2] = 1; + ids_[0] = 2; + ids_[1] = 0; + ids_[2] = 1; break; } default: @@ -78,34 +77,34 @@ void ChannelMatteOperation::initExecution() } } -void ChannelMatteOperation::deinitExecution() +void ChannelMatteOperation::deinit_execution() { - this->m_inputImageProgram = nullptr; + input_image_program_ = nullptr; } -void ChannelMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ChannelMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor[4]; + float in_color[4]; float alpha; - const float limit_max = this->m_limit_max; - const float limit_min = this->m_limit_min; - const float limit_range = this->m_limit_range; + const float limit_max = limit_max_; + const float limit_min = limit_min_; + const float limit_range = limit_range_; - this->m_inputImageProgram->readSampled(inColor, x, y, sampler); + input_image_program_->read_sampled(in_color, x, y, sampler); /* matte operation */ - alpha = inColor[this->m_ids[0]] - MAX2(inColor[this->m_ids[1]], inColor[this->m_ids[2]]); + alpha = in_color[ids_[0]] - MAX2(in_color[ids_[1]], in_color[ids_[2]]); /* flip because 0.0 is transparent, not 1.0 */ alpha = 1.0f - alpha; /* test range */ if (alpha > limit_max) { - alpha = inColor[3]; /* Whatever it was prior. */ + alpha = in_color[3]; /* Whatever it was prior. */ } else if (alpha < limit_min) { alpha = 0.0f; @@ -119,7 +118,7 @@ void ChannelMatteOperation::executePixelSampled(float output[4], */ /* Don't make something that was more transparent less transparent. */ - output[0] = MIN2(alpha, inColor[3]); + output[0] = MIN2(alpha, in_color[3]); } void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -130,20 +129,20 @@ void ChannelMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const float *color = it.in(0); /* Matte operation. */ - float alpha = color[this->m_ids[0]] - MAX2(color[this->m_ids[1]], color[this->m_ids[2]]); + float alpha = color[ids_[0]] - MAX2(color[ids_[1]], color[ids_[2]]); /* Flip because 0.0 is transparent, not 1.0. */ alpha = 1.0f - alpha; /* Test range. */ - if (alpha > m_limit_max) { + if (alpha > limit_max_) { alpha = color[3]; /* Whatever it was prior. */ } - else if (alpha < m_limit_min) { + else if (alpha < limit_min_) { alpha = 0.0f; } else { /* Blend. */ - alpha = (alpha - m_limit_min) / m_limit_range; + alpha = (alpha - limit_min_) / limit_range_; } /* Store matte(alpha) value in [0] to go with diff --git a/source/blender/compositor/operations/COM_ChannelMatteOperation.h b/source/blender/compositor/operations/COM_ChannelMatteOperation.h index ba50105dd3b..64b173885d5 100644 --- a/source/blender/compositor/operations/COM_ChannelMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChannelMatteOperation.h @@ -28,16 +28,16 @@ namespace blender::compositor { */ class ChannelMatteOperation : public MultiThreadedOperation { private: - SocketReader *m_inputImageProgram; + SocketReader *input_image_program_; - /* int m_color_space; */ /* node->custom1 */ /* UNUSED */ /* TODO ? */ - int m_matte_channel; /* node->custom2 */ - int m_limit_method; /* node->algorithm */ - int m_limit_channel; /* node->channel */ - float m_limit_max; /* node->storage->t1 */ - float m_limit_min; /* node->storage->t2 */ + /* int color_space_; */ /* node->custom1 */ /* UNUSED */ /* TODO ? */ + int matte_channel_; /* node->custom2 */ + int limit_method_; /* node->algorithm */ + int limit_channel_; /* node->channel */ + float limit_max_; /* node->storage->t1 */ + float limit_min_; /* node->storage->t2 */ - float m_limit_range; + float limit_range_; /** ids to use for the operations (max and simple) * alpha = in[ids[0]] - MAX2(in[ids[1]], in[ids[2]]) @@ -47,7 +47,7 @@ class ChannelMatteOperation : public MultiThreadedOperation { * ids[2] = ids[1] * alpha = in[ids[0]] - MAX2(in[ids[1]], in[ids[2]]) */ - int m_ids[3]; + int ids_[3]; public: /** @@ -58,18 +58,18 @@ class ChannelMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma, const int custom2) + void set_settings(NodeChroma *node_chroma, const int custom2) { - this->m_limit_max = nodeChroma->t1; - this->m_limit_min = nodeChroma->t2; - this->m_limit_method = nodeChroma->algorithm; - this->m_limit_channel = nodeChroma->channel; - this->m_matte_channel = custom2; + limit_max_ = node_chroma->t1; + limit_min_ = node_chroma->t2; + limit_method_ = node_chroma->algorithm; + limit_channel_ = node_chroma->channel; + matte_channel_ = custom2; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc index 0784f266b19..d0fc65ef331 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.cc @@ -17,51 +17,50 @@ */ #include "COM_ChromaMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { ChromaMatteOperation::ChromaMatteOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; - flags.can_be_constant = true; + input_image_program_ = nullptr; + input_key_program_ = nullptr; + flags_.can_be_constant = true; } -void ChromaMatteOperation::initExecution() +void ChromaMatteOperation::init_execution() { - this->m_inputImageProgram = this->getInputSocketReader(0); - this->m_inputKeyProgram = this->getInputSocketReader(1); + input_image_program_ = this->get_input_socket_reader(0); + input_key_program_ = this->get_input_socket_reader(1); } -void ChromaMatteOperation::deinitExecution() +void ChromaMatteOperation::deinit_execution() { - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; } -void ChromaMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ChromaMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inKey[4]; - float inImage[4]; + float in_key[4]; + float in_image[4]; - const float acceptance = this->m_settings->t1; /* in radians */ - const float cutoff = this->m_settings->t2; /* in radians */ - const float gain = this->m_settings->fstrength; + const float acceptance = settings_->t1; /* in radians */ + const float cutoff = settings_->t2; /* in radians */ + const float gain = settings_->fstrength; float x_angle, z_angle, alpha; float theta, beta; float kfg; - this->m_inputKeyProgram->readSampled(inKey, x, y, sampler); - this->m_inputImageProgram->readSampled(inImage, x, y, sampler); + input_key_program_->read_sampled(in_key, x, y, sampler); + input_image_program_->read_sampled(in_image, x, y, sampler); /* Store matte(alpha) value in [0] to go with * #COM_SetAlphaMultiplyOperation and the Value output. */ @@ -70,19 +69,19 @@ void ChromaMatteOperation::executePixelSampled(float output[4], /* Find theta, the angle that the color space should be rotated based on key. */ /* rescale to -1.0..1.0 */ - // inImage[0] = (inImage[0] * 2.0f) - 1.0f; // UNUSED - inImage[1] = (inImage[1] * 2.0f) - 1.0f; - inImage[2] = (inImage[2] * 2.0f) - 1.0f; + // in_image[0] = (in_image[0] * 2.0f) - 1.0f; // UNUSED + in_image[1] = (in_image[1] * 2.0f) - 1.0f; + in_image[2] = (in_image[2] * 2.0f) - 1.0f; - // inKey[0] = (inKey[0] * 2.0f) - 1.0f; // UNUSED - inKey[1] = (inKey[1] * 2.0f) - 1.0f; - inKey[2] = (inKey[2] * 2.0f) - 1.0f; + // in_key[0] = (in_key[0] * 2.0f) - 1.0f; // UNUSED + in_key[1] = (in_key[1] * 2.0f) - 1.0f; + in_key[2] = (in_key[2] * 2.0f) - 1.0f; - theta = atan2(inKey[2], inKey[1]); + theta = atan2(in_key[2], in_key[1]); /* Rotate the cb and cr into x/z space. */ - x_angle = inImage[1] * cosf(theta) + inImage[2] * sinf(theta); - z_angle = inImage[2] * cosf(theta) - inImage[1] * sinf(theta); + x_angle = in_image[1] * cosf(theta) + in_image[2] * sinf(theta); + z_angle = in_image[2] * cosf(theta) - in_image[1] * sinf(theta); /* If within the acceptance angle. */ /* If kfg is <0 then the pixel is outside of the key color. */ @@ -99,15 +98,15 @@ void ChromaMatteOperation::executePixelSampled(float output[4], } /* don't make something that was more transparent less transparent */ - if (alpha < inImage[3]) { + if (alpha < in_image[3]) { output[0] = alpha; } else { - output[0] = inImage[3]; + output[0] = in_image[3]; } } - else { /* Pixel is outside key color. */ - output[0] = inImage[3]; /* Make pixel just as transparent as it was before. */ + else { /* Pixel is outside key color. */ + output[0] = in_image[3]; /* Make pixel just as transparent as it was before. */ } } @@ -115,9 +114,9 @@ void ChromaMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float acceptance = this->m_settings->t1; /* In radians. */ - const float cutoff = this->m_settings->t2; /* In radians. */ - const float gain = this->m_settings->fstrength; + const float acceptance = settings_->t1; /* In radians. */ + const float cutoff = settings_->t2; /* In radians. */ + const float gain = settings_->fstrength; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float *in_image = it.in(0); const float *in_key = it.in(1); diff --git a/source/blender/compositor/operations/COM_ChromaMatteOperation.h b/source/blender/compositor/operations/COM_ChromaMatteOperation.h index 065349910a7..954daef1be7 100644 --- a/source/blender/compositor/operations/COM_ChromaMatteOperation.h +++ b/source/blender/compositor/operations/COM_ChromaMatteOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ChromaMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; - SocketReader *m_inputKeyProgram; + NodeChroma *settings_; + SocketReader *input_image_program_; + SocketReader *input_key_program_; public: /** @@ -41,14 +41,14 @@ class ChromaMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - this->m_settings = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc index aee8c0d52e8..533854b62dd 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorBalanceASCCDLOperation.h" -#include "BLI_math.h" namespace blender::compositor { @@ -35,46 +34,43 @@ inline float colorbalance_cdl(float in, float offset, float power, float slope) ColorBalanceASCCDLOperation::ColorBalanceASCCDLOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; - this->setResolutionInputSocketIndex(1); - flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; + this->set_canvas_input_index(1); + flags_.can_be_constant = true; } -void ColorBalanceASCCDLOperation::initExecution() +void ColorBalanceASCCDLOperation::init_execution() { - this->m_inputValueOperation = this->getInputSocketReader(0); - this->m_inputColorOperation = this->getInputSocketReader(1); + input_value_operation_ = this->get_input_socket_reader(0); + input_color_operation_ = this->get_input_socket_reader(1); } -void ColorBalanceASCCDLOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorBalanceASCCDLOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; + float input_color[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColorOperation->readSampled(inputColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color_operation_->read_sampled(input_color, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; - output[0] = mfac * inputColor[0] + - fac * colorbalance_cdl( - inputColor[0], this->m_offset[0], this->m_power[0], this->m_slope[0]); - output[1] = mfac * inputColor[1] + - fac * colorbalance_cdl( - inputColor[1], this->m_offset[1], this->m_power[1], this->m_slope[1]); - output[2] = mfac * inputColor[2] + - fac * colorbalance_cdl( - inputColor[2], this->m_offset[2], this->m_power[2], this->m_slope[2]); - output[3] = inputColor[3]; + output[0] = mfac * input_color[0] + + fac * colorbalance_cdl(input_color[0], offset_[0], power_[0], slope_[0]); + output[1] = mfac * input_color[1] + + fac * colorbalance_cdl(input_color[1], offset_[1], power_[1], slope_[1]); + output[2] = mfac * input_color[2] + + fac * colorbalance_cdl(input_color[2], offset_[2], power_[2], slope_[2]); + output[3] = input_color[3]; } void ColorBalanceASCCDLOperation::update_memory_buffer_row(PixelCursor &p) @@ -85,19 +81,19 @@ void ColorBalanceASCCDLOperation::update_memory_buffer_row(PixelCursor &p) const float fac = MIN2(1.0f, in_factor[0]); const float fac_m = 1.0f - fac; p.out[0] = fac_m * in_color[0] + - fac * colorbalance_cdl(in_color[0], m_offset[0], m_power[0], m_slope[0]); + fac * colorbalance_cdl(in_color[0], offset_[0], power_[0], slope_[0]); p.out[1] = fac_m * in_color[1] + - fac * colorbalance_cdl(in_color[1], m_offset[1], m_power[1], m_slope[1]); + fac * colorbalance_cdl(in_color[1], offset_[1], power_[1], slope_[1]); p.out[2] = fac_m * in_color[2] + - fac * colorbalance_cdl(in_color[2], m_offset[2], m_power[2], m_slope[2]); + fac * colorbalance_cdl(in_color[2], offset_[2], power_[2], slope_[2]); p.out[3] = in_color[3]; } } -void ColorBalanceASCCDLOperation::deinitExecution() +void ColorBalanceASCCDLOperation::deinit_execution() { - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h index d161ea66af2..64622e399d3 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceASCCDLOperation.h @@ -29,14 +29,14 @@ namespace blender::compositor { class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { protected: /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *m_inputValueOperation; - SocketReader *m_inputColorOperation; + SocketReader *input_value_operation_; + SocketReader *input_color_operation_; - float m_offset[3]; - float m_power[3]; - float m_slope[3]; + float offset_[3]; + float power_[3]; + float slope_[3]; public: /** @@ -47,29 +47,29 @@ class ColorBalanceASCCDLOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setOffset(float offset[3]) + void set_offset(float offset[3]) { - copy_v3_v3(this->m_offset, offset); + copy_v3_v3(offset_, offset); } - void setPower(float power[3]) + void set_power(float power[3]) { - copy_v3_v3(this->m_power, power); + copy_v3_v3(power_, power); } - void setSlope(float slope[3]) + void set_slope(float slope[3]) { - copy_v3_v3(this->m_slope, slope); + copy_v3_v3(slope_, slope); } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc index 674cb79a238..606fd560cff 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorBalanceLGGOperation.h" -#include "BLI_math.h" namespace blender::compositor { @@ -40,46 +39,43 @@ inline float colorbalance_lgg(float in, float lift_lgg, float gamma_inv, float g ColorBalanceLGGOperation::ColorBalanceLGGOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; - this->setResolutionInputSocketIndex(1); - flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; + this->set_canvas_input_index(1); + flags_.can_be_constant = true; } -void ColorBalanceLGGOperation::initExecution() +void ColorBalanceLGGOperation::init_execution() { - this->m_inputValueOperation = this->getInputSocketReader(0); - this->m_inputColorOperation = this->getInputSocketReader(1); + input_value_operation_ = this->get_input_socket_reader(0); + input_color_operation_ = this->get_input_socket_reader(1); } -void ColorBalanceLGGOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorBalanceLGGOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; + float input_color[4]; float value[4]; - this->m_inputValueOperation->readSampled(value, x, y, sampler); - this->m_inputColorOperation->readSampled(inputColor, x, y, sampler); + input_value_operation_->read_sampled(value, x, y, sampler); + input_color_operation_->read_sampled(input_color, x, y, sampler); float fac = value[0]; fac = MIN2(1.0f, fac); const float mfac = 1.0f - fac; - output[0] = mfac * inputColor[0] + - fac * colorbalance_lgg( - inputColor[0], this->m_lift[0], this->m_gamma_inv[0], this->m_gain[0]); - output[1] = mfac * inputColor[1] + - fac * colorbalance_lgg( - inputColor[1], this->m_lift[1], this->m_gamma_inv[1], this->m_gain[1]); - output[2] = mfac * inputColor[2] + - fac * colorbalance_lgg( - inputColor[2], this->m_lift[2], this->m_gamma_inv[2], this->m_gain[2]); - output[3] = inputColor[3]; + output[0] = mfac * input_color[0] + + fac * colorbalance_lgg(input_color[0], lift_[0], gamma_inv_[0], gain_[0]); + output[1] = mfac * input_color[1] + + fac * colorbalance_lgg(input_color[1], lift_[1], gamma_inv_[1], gain_[1]); + output[2] = mfac * input_color[2] + + fac * colorbalance_lgg(input_color[2], lift_[2], gamma_inv_[2], gain_[2]); + output[3] = input_color[3]; } void ColorBalanceLGGOperation::update_memory_buffer_row(PixelCursor &p) @@ -90,19 +86,19 @@ void ColorBalanceLGGOperation::update_memory_buffer_row(PixelCursor &p) const float fac = MIN2(1.0f, in_factor[0]); const float fac_m = 1.0f - fac; p.out[0] = fac_m * in_color[0] + - fac * colorbalance_lgg(in_color[0], m_lift[0], m_gamma_inv[0], m_gain[0]); + fac * colorbalance_lgg(in_color[0], lift_[0], gamma_inv_[0], gain_[0]); p.out[1] = fac_m * in_color[1] + - fac * colorbalance_lgg(in_color[1], m_lift[1], m_gamma_inv[1], m_gain[1]); + fac * colorbalance_lgg(in_color[1], lift_[1], gamma_inv_[1], gain_[1]); p.out[2] = fac_m * in_color[2] + - fac * colorbalance_lgg(in_color[2], m_lift[2], m_gamma_inv[2], m_gain[2]); + fac * colorbalance_lgg(in_color[2], lift_[2], gamma_inv_[2], gain_[2]); p.out[3] = in_color[3]; } } -void ColorBalanceLGGOperation::deinitExecution() +void ColorBalanceLGGOperation::deinit_execution() { - this->m_inputValueOperation = nullptr; - this->m_inputColorOperation = nullptr; + input_value_operation_ = nullptr; + input_color_operation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h index 4bc929ed76c..738669e71f9 100644 --- a/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h +++ b/source/blender/compositor/operations/COM_ColorBalanceLGGOperation.h @@ -29,14 +29,14 @@ namespace blender::compositor { class ColorBalanceLGGOperation : public MultiThreadedRowOperation { protected: /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *m_inputValueOperation; - SocketReader *m_inputColorOperation; + SocketReader *input_value_operation_; + SocketReader *input_color_operation_; - float m_gain[3]; - float m_lift[3]; - float m_gamma_inv[3]; + float gain_[3]; + float lift_[3]; + float gamma_inv_[3]; public: /** @@ -47,29 +47,29 @@ class ColorBalanceLGGOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setGain(const float gain[3]) + void set_gain(const float gain[3]) { - copy_v3_v3(this->m_gain, gain); + copy_v3_v3(gain_, gain); } - void setLift(const float lift[3]) + void set_lift(const float lift[3]) { - copy_v3_v3(this->m_lift, lift); + copy_v3_v3(lift_, lift); } - void setGammaInv(const float gamma_inv[3]) + void set_gamma_inv(const float gamma_inv[3]) { - copy_v3_v3(this->m_gamma_inv, gamma_inv); + copy_v3_v3(gamma_inv_, gamma_inv); } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc index d3557e541c0..2e7ce6b8497 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_ColorCorrectionOperation.h" -#include "BLI_math.h" #include "IMB_colormanagement.h" @@ -25,20 +24,20 @@ namespace blender::compositor { ColorCorrectionOperation::ColorCorrectionOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->m_inputImage = nullptr; - this->m_inputMask = nullptr; - this->m_redChannelEnabled = true; - this->m_greenChannelEnabled = true; - this->m_blueChannelEnabled = true; - flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_image_ = nullptr; + input_mask_ = nullptr; + red_channel_enabled_ = true; + green_channel_enabled_ = true; + blue_channel_enabled_ = true; + flags_.can_be_constant = true; } -void ColorCorrectionOperation::initExecution() +void ColorCorrectionOperation::init_execution() { - this->m_inputImage = this->getInputSocketReader(0); - this->m_inputMask = this->getInputSocketReader(1); + input_image_ = this->get_input_socket_reader(0); + input_mask_ = this->get_input_socket_reader(1); } /* Calculate x^y if the function is defined. Otherwise return the given fallback value. */ @@ -50,74 +49,71 @@ BLI_INLINE float color_correct_powf_safe(const float x, const float y, const flo return powf(x, y); } -void ColorCorrectionOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorCorrectionOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputImageColor[4]; - float inputMask[4]; - this->m_inputImage->readSampled(inputImageColor, x, y, sampler); - this->m_inputMask->readSampled(inputMask, x, y, sampler); + float input_image_color[4]; + float input_mask[4]; + input_image_->read_sampled(input_image_color, x, y, sampler); + input_mask_->read_sampled(input_mask, x, y, sampler); - float level = (inputImageColor[0] + inputImageColor[1] + inputImageColor[2]) / 3.0f; - float contrast = this->m_data->master.contrast; - float saturation = this->m_data->master.saturation; - float gamma = this->m_data->master.gamma; - float gain = this->m_data->master.gain; - float lift = this->m_data->master.lift; + float level = (input_image_color[0] + input_image_color[1] + input_image_color[2]) / 3.0f; + float contrast = data_->master.contrast; + float saturation = data_->master.saturation; + float gamma = data_->master.gamma; + float gain = data_->master.gain; + float lift = data_->master.lift; float r, g, b; - float value = inputMask[0]; + float value = input_mask[0]; value = MIN2(1.0f, value); const float mvalue = 1.0f - value; - float levelShadows = 0.0; - float levelMidtones = 0.0; - float levelHighlights = 0.0; + float level_shadows = 0.0; + float level_midtones = 0.0; + float level_highlights = 0.0; #define MARGIN 0.10f #define MARGIN_DIV (0.5f / MARGIN) - if (level < this->m_data->startmidtones - MARGIN) { - levelShadows = 1.0f; + if (level < data_->startmidtones - MARGIN) { + level_shadows = 1.0f; } - else if (level < this->m_data->startmidtones + MARGIN) { - levelMidtones = ((level - this->m_data->startmidtones) * MARGIN_DIV) + 0.5f; - levelShadows = 1.0f - levelMidtones; + else if (level < data_->startmidtones + MARGIN) { + level_midtones = ((level - data_->startmidtones) * MARGIN_DIV) + 0.5f; + level_shadows = 1.0f - level_midtones; } - else if (level < this->m_data->endmidtones - MARGIN) { - levelMidtones = 1.0f; + else if (level < data_->endmidtones - MARGIN) { + level_midtones = 1.0f; } - else if (level < this->m_data->endmidtones + MARGIN) { - levelHighlights = ((level - this->m_data->endmidtones) * MARGIN_DIV) + 0.5f; - levelMidtones = 1.0f - levelHighlights; + else if (level < data_->endmidtones + MARGIN) { + level_highlights = ((level - data_->endmidtones) * MARGIN_DIV) + 0.5f; + level_midtones = 1.0f - level_highlights; } else { - levelHighlights = 1.0f; + level_highlights = 1.0f; } #undef MARGIN #undef MARGIN_DIV - contrast *= (levelShadows * this->m_data->shadows.contrast) + - (levelMidtones * this->m_data->midtones.contrast) + - (levelHighlights * this->m_data->highlights.contrast); - saturation *= (levelShadows * this->m_data->shadows.saturation) + - (levelMidtones * this->m_data->midtones.saturation) + - (levelHighlights * this->m_data->highlights.saturation); - gamma *= (levelShadows * this->m_data->shadows.gamma) + - (levelMidtones * this->m_data->midtones.gamma) + - (levelHighlights * this->m_data->highlights.gamma); - gain *= (levelShadows * this->m_data->shadows.gain) + - (levelMidtones * this->m_data->midtones.gain) + - (levelHighlights * this->m_data->highlights.gain); - lift += (levelShadows * this->m_data->shadows.lift) + - (levelMidtones * this->m_data->midtones.lift) + - (levelHighlights * this->m_data->highlights.lift); + contrast *= (level_shadows * data_->shadows.contrast) + + (level_midtones * data_->midtones.contrast) + + (level_highlights * data_->highlights.contrast); + saturation *= (level_shadows * data_->shadows.saturation) + + (level_midtones * data_->midtones.saturation) + + (level_highlights * data_->highlights.saturation); + gamma *= (level_shadows * data_->shadows.gamma) + (level_midtones * data_->midtones.gamma) + + (level_highlights * data_->highlights.gamma); + gain *= (level_shadows * data_->shadows.gain) + (level_midtones * data_->midtones.gain) + + (level_highlights * data_->highlights.gain); + lift += (level_shadows * data_->shadows.lift) + (level_midtones * data_->midtones.lift) + + (level_highlights * data_->highlights.lift); float invgamma = 1.0f / gamma; - float luma = IMB_colormanagement_get_luminance(inputImageColor); + float luma = IMB_colormanagement_get_luminance(input_image_color); - r = inputImageColor[0]; - g = inputImageColor[1]; - b = inputImageColor[2]; + r = input_image_color[0]; + g = input_image_color[1]; + b = input_image_color[2]; r = (luma + saturation * (r - luma)); g = (luma + saturation * (g - luma)); @@ -133,29 +129,29 @@ void ColorCorrectionOperation::executePixelSampled(float output[4], b = color_correct_powf_safe(b * gain + lift, invgamma, b); /* Mix with mask. */ - r = mvalue * inputImageColor[0] + value * r; - g = mvalue * inputImageColor[1] + value * g; - b = mvalue * inputImageColor[2] + value * b; + r = mvalue * input_image_color[0] + value * r; + g = mvalue * input_image_color[1] + value * g; + b = mvalue * input_image_color[2] + value * b; - if (this->m_redChannelEnabled) { + if (red_channel_enabled_) { output[0] = r; } else { - output[0] = inputImageColor[0]; + output[0] = input_image_color[0]; } - if (this->m_greenChannelEnabled) { + if (green_channel_enabled_) { output[1] = g; } else { - output[1] = inputImageColor[1]; + output[1] = input_image_color[1]; } - if (this->m_blueChannelEnabled) { + if (blue_channel_enabled_) { output[2] = b; } else { - output[2] = inputImageColor[2]; + output[2] = input_image_color[2]; } - output[3] = inputImageColor[3]; + output[3] = input_image_color[3]; } void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) @@ -170,43 +166,40 @@ void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) float level_highlights = 0.0f; constexpr float MARGIN = 0.10f; constexpr float MARGIN_DIV = 0.5f / MARGIN; - if (level < this->m_data->startmidtones - MARGIN) { + if (level < data_->startmidtones - MARGIN) { level_shadows = 1.0f; } - else if (level < this->m_data->startmidtones + MARGIN) { - level_midtones = ((level - this->m_data->startmidtones) * MARGIN_DIV) + 0.5f; + else if (level < data_->startmidtones + MARGIN) { + level_midtones = ((level - data_->startmidtones) * MARGIN_DIV) + 0.5f; level_shadows = 1.0f - level_midtones; } - else if (level < this->m_data->endmidtones - MARGIN) { + else if (level < data_->endmidtones - MARGIN) { level_midtones = 1.0f; } - else if (level < this->m_data->endmidtones + MARGIN) { - level_highlights = ((level - this->m_data->endmidtones) * MARGIN_DIV) + 0.5f; + else if (level < data_->endmidtones + MARGIN) { + level_highlights = ((level - data_->endmidtones) * MARGIN_DIV) + 0.5f; level_midtones = 1.0f - level_highlights; } else { level_highlights = 1.0f; } - float contrast = this->m_data->master.contrast; - float saturation = this->m_data->master.saturation; - float gamma = this->m_data->master.gamma; - float gain = this->m_data->master.gain; - float lift = this->m_data->master.lift; - contrast *= level_shadows * this->m_data->shadows.contrast + - level_midtones * this->m_data->midtones.contrast + - level_highlights * this->m_data->highlights.contrast; - saturation *= level_shadows * this->m_data->shadows.saturation + - level_midtones * this->m_data->midtones.saturation + - level_highlights * this->m_data->highlights.saturation; - gamma *= level_shadows * this->m_data->shadows.gamma + - level_midtones * this->m_data->midtones.gamma + - level_highlights * this->m_data->highlights.gamma; - gain *= level_shadows * this->m_data->shadows.gain + - level_midtones * this->m_data->midtones.gain + - level_highlights * this->m_data->highlights.gain; - lift += level_shadows * this->m_data->shadows.lift + - level_midtones * this->m_data->midtones.lift + - level_highlights * this->m_data->highlights.lift; + float contrast = data_->master.contrast; + float saturation = data_->master.saturation; + float gamma = data_->master.gamma; + float gain = data_->master.gain; + float lift = data_->master.lift; + contrast *= level_shadows * data_->shadows.contrast + + level_midtones * data_->midtones.contrast + + level_highlights * data_->highlights.contrast; + saturation *= level_shadows * data_->shadows.saturation + + level_midtones * data_->midtones.saturation + + level_highlights * data_->highlights.saturation; + gamma *= level_shadows * data_->shadows.gamma + level_midtones * data_->midtones.gamma + + level_highlights * data_->highlights.gamma; + gain *= level_shadows * data_->shadows.gain + level_midtones * data_->midtones.gain + + level_highlights * data_->highlights.gain; + lift += level_shadows * data_->shadows.lift + level_midtones * data_->midtones.lift + + level_highlights * data_->highlights.lift; const float inv_gamma = 1.0f / gamma; const float luma = IMB_colormanagement_get_luminance(in_color); @@ -226,22 +219,22 @@ void ColorCorrectionOperation::update_memory_buffer_row(PixelCursor &p) /* Mix with mask. */ const float value = MIN2(1.0f, in_mask[0]); - const float m_value = 1.0f - value; - r = m_value * in_color[0] + value * r; - g = m_value * in_color[1] + value * g; - b = m_value * in_color[2] + value * b; + const float value_ = 1.0f - value; + r = value_ * in_color[0] + value * r; + g = value_ * in_color[1] + value * g; + b = value_ * in_color[2] + value * b; - p.out[0] = m_redChannelEnabled ? r : in_color[0]; - p.out[1] = m_greenChannelEnabled ? g : in_color[1]; - p.out[2] = m_blueChannelEnabled ? b : in_color[2]; + p.out[0] = red_channel_enabled_ ? r : in_color[0]; + p.out[1] = green_channel_enabled_ ? g : in_color[1]; + p.out[2] = blue_channel_enabled_ ? b : in_color[2]; p.out[3] = in_color[3]; } } -void ColorCorrectionOperation::deinitExecution() +void ColorCorrectionOperation::deinit_execution() { - this->m_inputImage = nullptr; - this->m_inputMask = nullptr; + input_image_ = nullptr; + input_mask_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h index 32b5e02e77a..b0d52507204 100644 --- a/source/blender/compositor/operations/COM_ColorCorrectionOperation.h +++ b/source/blender/compositor/operations/COM_ColorCorrectionOperation.h @@ -25,15 +25,15 @@ namespace blender::compositor { class ColorCorrectionOperation : public MultiThreadedRowOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputImage; - SocketReader *m_inputMask; - NodeColorCorrection *m_data; + SocketReader *input_image_; + SocketReader *input_mask_; + NodeColorCorrection *data_; - bool m_redChannelEnabled; - bool m_greenChannelEnabled; - bool m_blueChannelEnabled; + bool red_channel_enabled_; + bool green_channel_enabled_; + bool blue_channel_enabled_; public: ColorCorrectionOperation(); @@ -41,33 +41,33 @@ class ColorCorrectionOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeColorCorrection *data) + void set_data(NodeColorCorrection *data) { - this->m_data = data; + data_ = data; } - void setRedChannelEnabled(bool enabled) + void set_red_channel_enabled(bool enabled) { - this->m_redChannelEnabled = enabled; + red_channel_enabled_ = enabled; } - void setGreenChannelEnabled(bool enabled) + void set_green_channel_enabled(bool enabled) { - this->m_greenChannelEnabled = enabled; + green_channel_enabled_ = enabled; } - void setBlueChannelEnabled(bool enabled) + void set_blue_channel_enabled(bool enabled) { - this->m_blueChannelEnabled = enabled; + blue_channel_enabled_ = enabled; } void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.cc b/source/blender/compositor/operations/COM_ColorCurveOperation.cc index 646238460ba..7cfa1c09298 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.cc @@ -20,42 +20,40 @@ #include "BKE_colortools.h" -#include "MEM_guardedalloc.h" - namespace blender::compositor { ColorCurveOperation::ColorCurveOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; - this->m_inputBlackProgram = nullptr; - this->m_inputWhiteProgram = nullptr; + input_fac_program_ = nullptr; + input_image_program_ = nullptr; + input_black_program_ = nullptr; + input_white_program_ = nullptr; - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); } -void ColorCurveOperation::initExecution() +void ColorCurveOperation::init_execution() { - CurveBaseOperation::initExecution(); - this->m_inputFacProgram = this->getInputSocketReader(0); - this->m_inputImageProgram = this->getInputSocketReader(1); - this->m_inputBlackProgram = this->getInputSocketReader(2); - this->m_inputWhiteProgram = this->getInputSocketReader(3); + CurveBaseOperation::init_execution(); + input_fac_program_ = this->get_input_socket_reader(0); + input_image_program_ = this->get_input_socket_reader(1); + input_black_program_ = this->get_input_socket_reader(2); + input_white_program_ = this->get_input_socket_reader(3); - BKE_curvemapping_premultiply(this->m_curveMapping, 0); + BKE_curvemapping_premultiply(curve_mapping_, 0); } -void ColorCurveOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorCurveOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - CurveMapping *cumap = this->m_curveMapping; + CurveMapping *cumap = curve_mapping_; float fac[4]; float image[4]; @@ -65,15 +63,15 @@ void ColorCurveOperation::executePixelSampled(float output[4], float white[4]; float bwmul[3]; - this->m_inputBlackProgram->readSampled(black, x, y, sampler); - this->m_inputWhiteProgram->readSampled(white, x, y, sampler); + input_black_program_->read_sampled(black, x, y, sampler); + input_white_program_->read_sampled(white, x, y, sampler); /* get our own local bwmul value, * since we can't be threadsafe and use cumap->bwmul & friends */ BKE_curvemapping_set_black_white_ex(black, white, bwmul); - this->m_inputFacProgram->readSampled(fac, x, y, sampler); - this->m_inputImageProgram->readSampled(image, x, y, sampler); + input_fac_program_->read_sampled(fac, x, y, sampler); + input_image_program_->read_sampled(image, x, y, sampler); if (*fac >= 1.0f) { BKE_curvemapping_evaluate_premulRGBF_ex(cumap, output, image, black, bwmul); @@ -89,20 +87,20 @@ void ColorCurveOperation::executePixelSampled(float output[4], output[3] = image[3]; } -void ColorCurveOperation::deinitExecution() +void ColorCurveOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; - this->m_inputBlackProgram = nullptr; - this->m_inputWhiteProgram = nullptr; + CurveBaseOperation::deinit_execution(); + input_fac_program_ = nullptr; + input_image_program_ = nullptr; + input_black_program_ = nullptr; + input_white_program_ = nullptr; } void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = this->m_curveMapping; + CurveMapping *cumap = curve_mapping_; float bwmul[3]; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { /* Local versions of `cumap->black` and `cumap->white`. */ @@ -132,63 +130,63 @@ void ColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, ConstantLevelColorCurveOperation::ConstantLevelColorCurveOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; + input_fac_program_ = nullptr; + input_image_program_ = nullptr; - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); } -void ConstantLevelColorCurveOperation::initExecution() +void ConstantLevelColorCurveOperation::init_execution() { - CurveBaseOperation::initExecution(); - this->m_inputFacProgram = this->getInputSocketReader(0); - this->m_inputImageProgram = this->getInputSocketReader(1); + CurveBaseOperation::init_execution(); + input_fac_program_ = this->get_input_socket_reader(0); + input_image_program_ = this->get_input_socket_reader(1); - BKE_curvemapping_premultiply(this->m_curveMapping, 0); + BKE_curvemapping_premultiply(curve_mapping_, 0); - BKE_curvemapping_set_black_white(this->m_curveMapping, this->m_black, this->m_white); + BKE_curvemapping_set_black_white(curve_mapping_, black_, white_); } -void ConstantLevelColorCurveOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConstantLevelColorCurveOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float fac[4]; float image[4]; - this->m_inputFacProgram->readSampled(fac, x, y, sampler); - this->m_inputImageProgram->readSampled(image, x, y, sampler); + input_fac_program_->read_sampled(fac, x, y, sampler); + input_image_program_->read_sampled(image, x, y, sampler); if (*fac >= 1.0f) { - BKE_curvemapping_evaluate_premulRGBF(this->m_curveMapping, output, image); + BKE_curvemapping_evaluate_premulRGBF(curve_mapping_, output, image); } else if (*fac <= 0.0f) { copy_v3_v3(output, image); } else { float col[4]; - BKE_curvemapping_evaluate_premulRGBF(this->m_curveMapping, col, image); + BKE_curvemapping_evaluate_premulRGBF(curve_mapping_, col, image); interp_v3_v3v3(output, image, col, *fac); } output[3] = image[3]; } -void ConstantLevelColorCurveOperation::deinitExecution() +void ConstantLevelColorCurveOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - this->m_inputFacProgram = nullptr; - this->m_inputImageProgram = nullptr; + CurveBaseOperation::deinit_execution(); + input_fac_program_ = nullptr; + input_image_program_ = nullptr; } void ConstantLevelColorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *cumap = this->m_curveMapping; + CurveMapping *cumap = curve_mapping_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float fac = *it.in(0); const float *image = it.in(1); diff --git a/source/blender/compositor/operations/COM_ColorCurveOperation.h b/source/blender/compositor/operations/COM_ColorCurveOperation.h index d8271e56d1d..eaebf1f3ff6 100644 --- a/source/blender/compositor/operations/COM_ColorCurveOperation.h +++ b/source/blender/compositor/operations/COM_ColorCurveOperation.h @@ -19,20 +19,18 @@ #pragma once #include "COM_CurveBaseOperation.h" -#include "COM_NodeOperation.h" -#include "DNA_color_types.h" namespace blender::compositor { class ColorCurveOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputFacProgram; - SocketReader *m_inputImageProgram; - SocketReader *m_inputBlackProgram; - SocketReader *m_inputWhiteProgram; + SocketReader *input_fac_program_; + SocketReader *input_image_program_; + SocketReader *input_black_program_; + SocketReader *input_white_program_; public: ColorCurveOperation(); @@ -40,17 +38,17 @@ class ColorCurveOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -60,12 +58,12 @@ class ColorCurveOperation : public CurveBaseOperation { class ConstantLevelColorCurveOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputFacProgram; - SocketReader *m_inputImageProgram; - float m_black[3]; - float m_white[3]; + SocketReader *input_fac_program_; + SocketReader *input_image_program_; + float black_[3]; + float white_[3]; public: ConstantLevelColorCurveOperation(); @@ -73,25 +71,25 @@ class ConstantLevelColorCurveOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setBlackLevel(float black[3]) + void set_black_level(float black[3]) { - copy_v3_v3(this->m_black, black); + copy_v3_v3(black_, black); } - void setWhiteLevel(float white[3]) + void set_white_level(float white[3]) { - copy_v3_v3(this->m_white, white); + copy_v3_v3(white_, white); } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.cc b/source/blender/compositor/operations/COM_ColorExposureOperation.cc index 228550a31c5..890a0ab4ccb 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.cc +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.cc @@ -22,35 +22,35 @@ namespace blender::compositor { ExposureOperation::ExposureOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + flags_.can_be_constant = true; } -void ExposureOperation::initExecution() +void ExposureOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputExposureProgram = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_exposure_program_ = this->get_input_socket_reader(1); } -void ExposureOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ExposureOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; - float inputExposure[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputExposureProgram->readSampled(inputExposure, x, y, sampler); - const float exposure = pow(2, inputExposure[0]); + float input_value[4]; + float input_exposure[4]; + input_program_->read_sampled(input_value, x, y, sampler); + input_exposure_program_->read_sampled(input_exposure, x, y, sampler); + const float exposure = pow(2, input_exposure[0]); - output[0] = inputValue[0] * exposure; - output[1] = inputValue[1] * exposure; - output[2] = inputValue[2] * exposure; + output[0] = input_value[0] * exposure; + output[1] = input_value[1] * exposure; + output[2] = input_value[2] * exposure; - output[3] = inputValue[3]; + output[3] = input_value[3]; } void ExposureOperation::update_memory_buffer_row(PixelCursor &p) @@ -66,10 +66,10 @@ void ExposureOperation::update_memory_buffer_row(PixelCursor &p) } } -void ExposureOperation::deinitExecution() +void ExposureOperation::deinit_execution() { - this->m_inputProgram = nullptr; - this->m_inputExposureProgram = nullptr; + input_program_ = nullptr; + input_exposure_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ColorExposureOperation.h b/source/blender/compositor/operations/COM_ColorExposureOperation.h index 1eb790e8d52..6de93a24ddd 100644 --- a/source/blender/compositor/operations/COM_ColorExposureOperation.h +++ b/source/blender/compositor/operations/COM_ColorExposureOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class ExposureOperation : public MultiThreadedRowOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - SocketReader *m_inputExposureProgram; + SocketReader *input_program_; + SocketReader *input_exposure_program_; public: ExposureOperation(); @@ -36,17 +36,17 @@ class ExposureOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.cc b/source/blender/compositor/operations/COM_ColorMatteOperation.cc index dec6571f217..14123d99b0f 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.cc +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.cc @@ -17,49 +17,48 @@ */ #include "COM_ColorMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { ColorMatteOperation::ColorMatteOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; - flags.can_be_constant = true; + input_image_program_ = nullptr; + input_key_program_ = nullptr; + flags_.can_be_constant = true; } -void ColorMatteOperation::initExecution() +void ColorMatteOperation::init_execution() { - this->m_inputImageProgram = this->getInputSocketReader(0); - this->m_inputKeyProgram = this->getInputSocketReader(1); + input_image_program_ = this->get_input_socket_reader(0); + input_key_program_ = this->get_input_socket_reader(1); } -void ColorMatteOperation::deinitExecution() +void ColorMatteOperation::deinit_execution() { - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; } -void ColorMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor[4]; - float inKey[4]; + float in_color[4]; + float in_key[4]; - const float hue = this->m_settings->t1; - const float sat = this->m_settings->t2; - const float val = this->m_settings->t3; + const float hue = settings_->t1; + const float sat = settings_->t2; + const float val = settings_->t3; float h_wrap; - this->m_inputImageProgram->readSampled(inColor, x, y, sampler); - this->m_inputKeyProgram->readSampled(inKey, x, y, sampler); + input_image_program_->read_sampled(in_color, x, y, sampler); + input_key_program_->read_sampled(in_key, x, y, sampler); /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. @@ -68,18 +67,19 @@ void ColorMatteOperation::executePixelSampled(float output[4], if ( /* Do hue last because it needs to wrap, and does some more checks. */ - /* sat */ (fabsf(inColor[1] - inKey[1]) < sat) && - /* val */ (fabsf(inColor[2] - inKey[2]) < val) && + /* sat */ (fabsf(in_color[1] - in_key[1]) < sat) && + /* val */ (fabsf(in_color[2] - in_key[2]) < val) && /* multiply by 2 because it wraps on both sides of the hue, * otherwise 0.5 would key all hue's */ - /* hue */ ((h_wrap = 2.0f * fabsf(inColor[0] - inKey[0])) < hue || (2.0f - h_wrap) < hue)) { + /* hue */ + ((h_wrap = 2.0f * fabsf(in_color[0] - in_key[0])) < hue || (2.0f - h_wrap) < hue)) { output[0] = 0.0f; /* make transparent */ } - else { /* Pixel is outside key color. */ - output[0] = inColor[3]; /* Make pixel just as transparent as it was before. */ + else { /* Pixel is outside key color. */ + output[0] = in_color[3]; /* Make pixel just as transparent as it was before. */ } } @@ -87,9 +87,9 @@ void ColorMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float hue = m_settings->t1; - const float sat = m_settings->t2; - const float val = m_settings->t3; + const float hue = settings_->t1; + const float sat = settings_->t2; + const float val = settings_->t3; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float *in_color = it.in(0); const float *in_key = it.in(1); diff --git a/source/blender/compositor/operations/COM_ColorMatteOperation.h b/source/blender/compositor/operations/COM_ColorMatteOperation.h index 49d06e62e65..97cf568621a 100644 --- a/source/blender/compositor/operations/COM_ColorMatteOperation.h +++ b/source/blender/compositor/operations/COM_ColorMatteOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class ColorMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; - SocketReader *m_inputKeyProgram; + NodeChroma *settings_; + SocketReader *input_image_program_; + SocketReader *input_key_program_; public: /** @@ -41,14 +41,14 @@ class ColorMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - this->m_settings = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.cc b/source/blender/compositor/operations/COM_ColorRampOperation.cc index 6c1b23ea731..94b840a6f73 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.cc +++ b/source/blender/compositor/operations/COM_ColorRampOperation.cc @@ -24,32 +24,32 @@ namespace blender::compositor { ColorRampOperation::ColorRampOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_colorBand = nullptr; - this->flags.can_be_constant = true; + input_program_ = nullptr; + color_band_ = nullptr; + flags_.can_be_constant = true; } -void ColorRampOperation::initExecution() +void ColorRampOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void ColorRampOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorRampOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float values[4]; - this->m_inputProgram->readSampled(values, x, y, sampler); - BKE_colorband_evaluate(this->m_colorBand, values[0], output); + input_program_->read_sampled(values, x, y, sampler); + BKE_colorband_evaluate(color_band_, values[0], output); } -void ColorRampOperation::deinitExecution() +void ColorRampOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; } void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -57,7 +57,7 @@ void ColorRampOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - BKE_colorband_evaluate(m_colorBand, *it.in(0), it.out); + BKE_colorband_evaluate(color_band_, *it.in(0), it.out); } } diff --git a/source/blender/compositor/operations/COM_ColorRampOperation.h b/source/blender/compositor/operations/COM_ColorRampOperation.h index ab64b9928fd..2faaab4ba54 100644 --- a/source/blender/compositor/operations/COM_ColorRampOperation.h +++ b/source/blender/compositor/operations/COM_ColorRampOperation.h @@ -26,10 +26,10 @@ namespace blender::compositor { class ColorRampOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - ColorBand *m_colorBand; + SocketReader *input_program_; + ColorBand *color_band_; public: ColorRampOperation(); @@ -37,21 +37,21 @@ class ColorRampOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setColorBand(ColorBand *colorBand) + void set_color_band(ColorBand *color_band) { - this->m_colorBand = colorBand; + color_band_ = color_band; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.cc b/source/blender/compositor/operations/COM_ColorSpillOperation.cc index 5bf7a9ee9cd..d614c963488 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.cc +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.cc @@ -17,101 +17,98 @@ */ #include "COM_ColorSpillOperation.h" -#include "BLI_math.h" #define AVG(a, b) ((a + b) / 2) namespace blender::compositor { ColorSpillOperation::ColorSpillOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Value); - addOutputSocket(DataType::Color); + add_input_socket(DataType::Color); + add_input_socket(DataType::Value); + add_output_socket(DataType::Color); - this->m_inputImageReader = nullptr; - this->m_inputFacReader = nullptr; - this->m_spillChannel = 1; /* GREEN */ - this->m_spillMethod = 0; - flags.can_be_constant = true; + input_image_reader_ = nullptr; + input_fac_reader_ = nullptr; + spill_channel_ = 1; /* GREEN */ + spill_method_ = 0; + flags_.can_be_constant = true; } -void ColorSpillOperation::initExecution() +void ColorSpillOperation::init_execution() { - this->m_inputImageReader = this->getInputSocketReader(0); - this->m_inputFacReader = this->getInputSocketReader(1); - if (this->m_spillChannel == 0) { - this->m_rmut = -1.0f; - this->m_gmut = 1.0f; - this->m_bmut = 1.0f; - this->m_channel2 = 1; - this->m_channel3 = 2; - if (this->m_settings->unspill == 0) { - this->m_settings->uspillr = 1.0f; - this->m_settings->uspillg = 0.0f; - this->m_settings->uspillb = 0.0f; + input_image_reader_ = this->get_input_socket_reader(0); + input_fac_reader_ = this->get_input_socket_reader(1); + if (spill_channel_ == 0) { + rmut_ = -1.0f; + gmut_ = 1.0f; + bmut_ = 1.0f; + channel2_ = 1; + channel3_ = 2; + if (settings_->unspill == 0) { + settings_->uspillr = 1.0f; + settings_->uspillg = 0.0f; + settings_->uspillb = 0.0f; } } - else if (this->m_spillChannel == 1) { - this->m_rmut = 1.0f; - this->m_gmut = -1.0f; - this->m_bmut = 1.0f; - this->m_channel2 = 0; - this->m_channel3 = 2; - if (this->m_settings->unspill == 0) { - this->m_settings->uspillr = 0.0f; - this->m_settings->uspillg = 1.0f; - this->m_settings->uspillb = 0.0f; + else if (spill_channel_ == 1) { + rmut_ = 1.0f; + gmut_ = -1.0f; + bmut_ = 1.0f; + channel2_ = 0; + channel3_ = 2; + if (settings_->unspill == 0) { + settings_->uspillr = 0.0f; + settings_->uspillg = 1.0f; + settings_->uspillb = 0.0f; } } else { - this->m_rmut = 1.0f; - this->m_gmut = 1.0f; - this->m_bmut = -1.0f; + rmut_ = 1.0f; + gmut_ = 1.0f; + bmut_ = -1.0f; - this->m_channel2 = 0; - this->m_channel3 = 1; - if (this->m_settings->unspill == 0) { - this->m_settings->uspillr = 0.0f; - this->m_settings->uspillg = 0.0f; - this->m_settings->uspillb = 1.0f; + channel2_ = 0; + channel3_ = 1; + if (settings_->unspill == 0) { + settings_->uspillr = 0.0f; + settings_->uspillg = 0.0f; + settings_->uspillb = 1.0f; } } } -void ColorSpillOperation::deinitExecution() +void ColorSpillOperation::deinit_execution() { - this->m_inputImageReader = nullptr; - this->m_inputFacReader = nullptr; + input_image_reader_ = nullptr; + input_fac_reader_ = nullptr; } -void ColorSpillOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ColorSpillOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float fac[4]; float input[4]; - this->m_inputFacReader->readSampled(fac, x, y, sampler); - this->m_inputImageReader->readSampled(input, x, y, sampler); + input_fac_reader_->read_sampled(fac, x, y, sampler); + input_image_reader_->read_sampled(input, x, y, sampler); float rfac = MIN2(1.0f, fac[0]); float map; - switch (this->m_spillMethod) { + switch (spill_method_) { case 0: /* simple */ - map = rfac * (input[this->m_spillChannel] - - (this->m_settings->limscale * input[this->m_settings->limchan])); + map = rfac * (input[spill_channel_] - (settings_->limscale * input[settings_->limchan])); break; default: /* average */ - map = rfac * - (input[this->m_spillChannel] - - (this->m_settings->limscale * AVG(input[this->m_channel2], input[this->m_channel3]))); + map = rfac * (input[spill_channel_] - + (settings_->limscale * AVG(input[channel2_], input[channel3_]))); break; } if (map > 0.0f) { - output[0] = input[0] + this->m_rmut * (this->m_settings->uspillr * map); - output[1] = input[1] + this->m_gmut * (this->m_settings->uspillg * map); - output[2] = input[2] + this->m_bmut * (this->m_settings->uspillb * map); + output[0] = input[0] + rmut_ * (settings_->uspillr * map); + output[1] = input[1] + gmut_ * (settings_->uspillg * map); + output[2] = input[2] + bmut_ * (settings_->uspillb * map); output[3] = input[3]; } else { @@ -128,21 +125,20 @@ void ColorSpillOperation::update_memory_buffer_partial(MemoryBuffer *output, const float factor = MIN2(1.0f, *it.in(1)); float map; - switch (m_spillMethod) { + switch (spill_method_) { case 0: /* simple */ - map = factor * - (color[m_spillChannel] - (m_settings->limscale * color[m_settings->limchan])); + map = factor * (color[spill_channel_] - (settings_->limscale * color[settings_->limchan])); break; default: /* average */ - map = factor * (color[m_spillChannel] - - (m_settings->limscale * AVG(color[m_channel2], color[m_channel3]))); + map = factor * (color[spill_channel_] - + (settings_->limscale * AVG(color[channel2_], color[channel3_]))); break; } if (map > 0.0f) { - it.out[0] = color[0] + m_rmut * (m_settings->uspillr * map); - it.out[1] = color[1] + m_gmut * (m_settings->uspillg * map); - it.out[2] = color[2] + m_bmut * (m_settings->uspillb * map); + it.out[0] = color[0] + rmut_ * (settings_->uspillr * map); + it.out[1] = color[1] + gmut_ * (settings_->uspillg * map); + it.out[2] = color[2] + bmut_ * (settings_->uspillb * map); it.out[3] = color[3]; } else { diff --git a/source/blender/compositor/operations/COM_ColorSpillOperation.h b/source/blender/compositor/operations/COM_ColorSpillOperation.h index 6a5e688c160..bfb0c6c963f 100644 --- a/source/blender/compositor/operations/COM_ColorSpillOperation.h +++ b/source/blender/compositor/operations/COM_ColorSpillOperation.h @@ -28,14 +28,14 @@ namespace blender::compositor { */ class ColorSpillOperation : public MultiThreadedOperation { protected: - NodeColorspill *m_settings; - SocketReader *m_inputImageReader; - SocketReader *m_inputFacReader; - int m_spillChannel; - int m_spillMethod; - int m_channel2; - int m_channel3; - float m_rmut, m_gmut, m_bmut; + NodeColorspill *settings_; + SocketReader *input_image_reader_; + SocketReader *input_fac_reader_; + int spill_channel_; + int spill_method_; + int channel2_; + int channel3_; + float rmut_, gmut_, bmut_; public: /** @@ -46,25 +46,25 @@ class ColorSpillOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeColorspill *nodeColorSpill) + void set_settings(NodeColorspill *node_color_spill) { - this->m_settings = nodeColorSpill; + settings_ = node_color_spill; } - void setSpillChannel(int channel) + void set_spill_channel(int channel) { - this->m_spillChannel = channel; + spill_channel_ = channel; } - void setSpillMethod(int method) + void set_spill_method(int method) { - this->m_spillMethod = method; + spill_method_ = method; } - float calculateMapValue(float fac, float *input); + float calculate_map_value(float fac, float *input); void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cc b/source/blender/compositor/operations/COM_CompositorOperation.cc index fb9e2e43c60..354997ebd2e 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cc +++ b/source/blender/compositor/operations/COM_CompositorOperation.cc @@ -17,94 +17,86 @@ */ #include "COM_CompositorOperation.h" + #include "BKE_global.h" #include "BKE_image.h" -#include "BLI_listbase.h" -#include "MEM_guardedalloc.h" - -#include "BLI_threads.h" #include "RE_pipeline.h" -#include "RE_texture.h" - -#include "render_types.h" - -#include "PIL_time.h" namespace blender::compositor { CompositorOperation::CompositorOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); - this->setRenderData(nullptr); - this->m_outputBuffer = nullptr; - this->m_depthBuffer = nullptr; - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; + this->set_render_data(nullptr); + output_buffer_ = nullptr; + depth_buffer_ = nullptr; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; - this->m_useAlphaInput = false; - this->m_active = false; + use_alpha_input_ = false; + active_ = false; - this->m_scene = nullptr; - this->m_sceneName[0] = '\0'; - this->m_viewName = nullptr; + scene_ = nullptr; + scene_name_[0] = '\0'; + view_name_ = nullptr; - flags.use_render_border = true; + flags_.use_render_border = true; } -void CompositorOperation::initExecution() +void CompositorOperation::init_execution() { - if (!this->m_active) { + if (!active_) { return; } /* When initializing the tree during initial load the width and height can be zero. */ - this->m_imageInput = getInputSocketReader(0); - this->m_alphaInput = getInputSocketReader(1); - this->m_depthInput = getInputSocketReader(2); - if (this->getWidth() * this->getHeight() != 0) { - this->m_outputBuffer = (float *)MEM_callocN( - sizeof(float[4]) * this->getWidth() * this->getHeight(), "CompositorOperation"); + image_input_ = get_input_socket_reader(0); + alpha_input_ = get_input_socket_reader(1); + depth_input_ = get_input_socket_reader(2); + if (this->get_width() * this->get_height() != 0) { + output_buffer_ = (float *)MEM_callocN( + sizeof(float[4]) * this->get_width() * this->get_height(), "CompositorOperation"); } - if (this->m_depthInput != nullptr) { - this->m_depthBuffer = (float *)MEM_callocN( - sizeof(float) * this->getWidth() * this->getHeight(), "CompositorOperation"); + if (depth_input_ != nullptr) { + depth_buffer_ = (float *)MEM_callocN(sizeof(float) * this->get_width() * this->get_height(), + "CompositorOperation"); } } -void CompositorOperation::deinitExecution() +void CompositorOperation::deinit_execution() { - if (!this->m_active) { + if (!active_) { return; } - if (!isBraked()) { - Render *re = RE_GetSceneRender(this->m_scene); + if (!is_braked()) { + Render *re = RE_GetSceneRender(scene_); RenderResult *rr = RE_AcquireResultWrite(re); if (rr) { - RenderView *rv = RE_RenderViewGetByName(rr, this->m_viewName); + RenderView *rv = RE_RenderViewGetByName(rr, view_name_); if (rv->rectf != nullptr) { MEM_freeN(rv->rectf); } - rv->rectf = this->m_outputBuffer; + rv->rectf = output_buffer_; if (rv->rectz != nullptr) { MEM_freeN(rv->rectz); } - rv->rectz = this->m_depthBuffer; + rv->rectz = depth_buffer_; rr->have_combined = true; } else { - if (this->m_outputBuffer) { - MEM_freeN(this->m_outputBuffer); + if (output_buffer_) { + MEM_freeN(output_buffer_); } - if (this->m_depthBuffer) { - MEM_freeN(this->m_depthBuffer); + if (depth_buffer_) { + MEM_freeN(depth_buffer_); } } @@ -121,26 +113,26 @@ void CompositorOperation::deinitExecution() BLI_thread_unlock(LOCK_DRAW_IMAGE); } else { - if (this->m_outputBuffer) { - MEM_freeN(this->m_outputBuffer); + if (output_buffer_) { + MEM_freeN(output_buffer_); } - if (this->m_depthBuffer) { - MEM_freeN(this->m_depthBuffer); + if (depth_buffer_) { + MEM_freeN(depth_buffer_); } } - this->m_outputBuffer = nullptr; - this->m_depthBuffer = nullptr; - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; + output_buffer_ = nullptr; + depth_buffer_ = nullptr; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; } -void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void CompositorOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { float color[8]; // 7 is enough - float *buffer = this->m_outputBuffer; - float *zbuffer = this->m_depthBuffer; + float *buffer = output_buffer_; + float *zbuffer = depth_buffer_; if (!buffer) { return; @@ -149,8 +141,8 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) int y1 = rect->ymin; int x2 = rect->xmax; int y2 = rect->ymax; - int offset = (y1 * this->getWidth() + x1); - int add = (this->getWidth() - (x2 - x1)); + int offset = (y1 * this->get_width() + x1); + int add = (this->get_width() - (x2 - x1)); int offset4 = offset * COM_DATA_TYPE_COLOR_CHANNELS; int x; int y; @@ -158,7 +150,7 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) int dx = 0, dy = 0; #if 0 - const RenderData *rd = this->m_rd; + const RenderData *rd = rd_; if (rd->mode & R_BORDER && rd->mode & R_CROP) { /** @@ -191,8 +183,8 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) int full_width = rd->xsch * rd->size / 100; int full_height = rd->ysch * rd->size / 100; - dx = rd->border.xmin * full_width - (full_width - this->getWidth()) / 2.0f; - dy = rd->border.ymin * full_height - (full_height - this->getHeight()) / 2.0f; + dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; + dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; } #endif @@ -200,18 +192,18 @@ void CompositorOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (x = x1; x < x2 && (!breaked); x++) { int input_x = x + dx, input_y = y + dy; - this->m_imageInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); - if (this->m_useAlphaInput) { - this->m_alphaInput->readSampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); + image_input_->read_sampled(color, input_x, input_y, PixelSampler::Nearest); + if (use_alpha_input_) { + alpha_input_->read_sampled(&(color[3]), input_x, input_y, PixelSampler::Nearest); } copy_v4_v4(buffer + offset4, color); - this->m_depthInput->readSampled(color, input_x, input_y, PixelSampler::Nearest); + depth_input_->read_sampled(color, input_x, input_y, PixelSampler::Nearest); zbuffer[offset] = color[0]; offset4 += COM_DATA_TYPE_COLOR_CHANNELS; offset++; - if (isBraked()) { + if (is_braked()) { breaked = true; } } @@ -224,27 +216,26 @@ void CompositorOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(outp const rcti &area, Span inputs) { - if (!m_outputBuffer) { + if (!output_buffer_) { return; } - MemoryBuffer output_buf(m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + MemoryBuffer output_buf(output_buffer_, COM_DATA_TYPE_COLOR_CHANNELS, get_width(), get_height()); output_buf.copy_from(inputs[0], area); - if (this->m_useAlphaInput) { + if (use_alpha_input_) { output_buf.copy_from(inputs[1], area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); } - MemoryBuffer depth_buf(m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + MemoryBuffer depth_buf(depth_buffer_, COM_DATA_TYPE_VALUE_CHANNELS, get_width(), get_height()); depth_buf.copy_from(inputs[2], area); } -void CompositorOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void CompositorOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; + int width = rd_->xsch * rd_->size / 100; + int height = rd_->ysch * rd_->size / 100; /* Check actual render resolution with cropping it may differ with cropped border.rendering * Fix for T31777 Border Crop gives black (easy). */ - Render *re = RE_GetSceneRender(this->m_scene); + Render *re = RE_GetSceneRender(scene_); if (re) { RenderResult *rr = RE_AcquireResultRead(re); if (rr) { @@ -254,13 +245,19 @@ void CompositorOperation::determineResolution(unsigned int resolution[2], RE_ReleaseResult(re); } - preferredResolution[0] = width; - preferredResolution[1] = height; + rcti local_preferred; + BLI_rcti_init(&local_preferred, 0, width, 0, height); - NodeOperation::determineResolution(resolution, preferredResolution); - - resolution[0] = width; - resolution[1] = height; + switch (execution_model_) { + case eExecutionModel::Tiled: + NodeOperation::determine_canvas(local_preferred, r_area); + r_area = local_preferred; + break; + case eExecutionModel::FullFrame: + set_determined_canvas_modifier([&](rcti &canvas) { canvas = local_preferred; }); + NodeOperation::determine_canvas(local_preferred, r_area); + break; + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 66367ec8bae..a0960074708 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -18,8 +18,6 @@ #pragma once -#include "BLI_rect.h" -#include "BLI_string.h" #include "COM_MultiThreadedOperation.h" struct Scene; @@ -31,99 +29,98 @@ namespace blender::compositor { */ class CompositorOperation : public MultiThreadedOperation { private: - const struct Scene *m_scene; + const struct Scene *scene_; /** * \brief Scene name, used for getting the render output, includes 'SC' prefix. */ - char m_sceneName[MAX_ID_NAME]; + char scene_name_[MAX_ID_NAME]; /** * \brief local reference to the scene */ - const RenderData *m_rd; + const RenderData *rd_; /** * \brief reference to the output float buffer */ - float *m_outputBuffer; + float *output_buffer_; /** * \brief reference to the output depth float buffer */ - float *m_depthBuffer; + float *depth_buffer_; /** * \brief local reference to the input image operation */ - SocketReader *m_imageInput; + SocketReader *image_input_; /** * \brief local reference to the input alpha operation */ - SocketReader *m_alphaInput; + SocketReader *alpha_input_; /** * \brief local reference to the depth operation */ - SocketReader *m_depthInput; + SocketReader *depth_input_; /** * \brief Ignore any alpha input */ - bool m_useAlphaInput; + bool use_alpha_input_; /** * \brief operation is active for calculating final compo result */ - bool m_active; + bool active_; /** * \brief View name, used for multiview */ - const char *m_viewName; + const char *view_name_; public: CompositorOperation(); - bool isActiveCompositorOutput() const + bool is_active_compositor_output() const { - return this->m_active; + return active_; } - void executeRegion(rcti *rect, unsigned int tileNumber) override; - void setScene(const struct Scene *scene) + void execute_region(rcti *rect, unsigned int tile_number) override; + void set_scene(const struct Scene *scene) { - m_scene = scene; + scene_ = scene; } - void setSceneName(const char *sceneName) + void set_scene_name(const char *scene_name) { - BLI_strncpy(this->m_sceneName, sceneName, sizeof(this->m_sceneName)); + BLI_strncpy(scene_name_, scene_name, sizeof(scene_name_)); } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - this->m_viewName = viewName; + view_name_ = view_name; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { - this->m_rd = rd; + rd_ = rd; } - bool isOutputOperation(bool /*rendering*/) const override + bool is_output_operation(bool /*rendering*/) const override { - return this->isActiveCompositorOutput(); + return this->is_active_compositor_output(); } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override { return eCompositorPriority::Medium; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; - void setUseAlphaInput(bool value) + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + void set_use_alpha_input(bool value) { - this->m_useAlphaInput = value; + use_alpha_input_ = value; } - void setActive(bool active) + void set_active(bool active) { - this->m_active = active; + active_ = active; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConstantOperation.cc b/source/blender/compositor/operations/COM_ConstantOperation.cc index 33d51cca432..400c04d9592 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.cc +++ b/source/blender/compositor/operations/COM_ConstantOperation.cc @@ -22,14 +22,14 @@ namespace blender::compositor { ConstantOperation::ConstantOperation() { - needs_resolution_to_get_constant_ = false; - flags.is_constant_operation = true; - flags.is_fullframe_operation = true; + needs_canvas_to_get_constant_ = false; + flags_.is_constant_operation = true; + flags_.is_fullframe_operation = true; } bool ConstantOperation::can_get_constant_elem() const { - return !needs_resolution_to_get_constant_ || this->flags.is_resolution_set; + return !needs_canvas_to_get_constant_ || flags_.is_canvas_set; } void ConstantOperation::update_memory_buffer(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConstantOperation.h b/source/blender/compositor/operations/COM_ConstantOperation.h index 31b8d30254b..d0db1ef1366 100644 --- a/source/blender/compositor/operations/COM_ConstantOperation.h +++ b/source/blender/compositor/operations/COM_ConstantOperation.h @@ -22,7 +22,7 @@ namespace blender::compositor { -/* TODO(manzanilla): After removing tiled implementation, implement a default #determineResolution +/* TODO(manzanilla): After removing tiled implementation, implement a default #determine_resolution * for all constant operations and make all initialization and deinitilization methods final. */ /** * Base class for operations that are always constant. Operations that can be constant only when @@ -31,7 +31,7 @@ namespace blender::compositor { */ class ConstantOperation : public NodeOperation { protected: - bool needs_resolution_to_get_constant_; + bool needs_canvas_to_get_constant_; public: ConstantOperation(); diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc index c00fe5d5f61..db75b2724c4 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.cc @@ -24,31 +24,31 @@ namespace blender::compositor { ConvertColorProfileOperation::ConvertColorProfileOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_inputOperation = nullptr; - this->m_predivided = false; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_operation_ = nullptr; + predivided_ = false; } -void ConvertColorProfileOperation::initExecution() +void ConvertColorProfileOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void ConvertColorProfileOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorProfileOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color[4]; - this->m_inputOperation->readSampled(color, x, y, sampler); + input_operation_->read_sampled(color, x, y, sampler); IMB_buffer_float_from_float( - output, color, 4, this->m_toProfile, this->m_fromProfile, this->m_predivided, 1, 1, 0, 0); + output, color, 4, to_profile_, from_profile_, predivided_, 1, 1, 0, 0); } -void ConvertColorProfileOperation::deinitExecution() +void ConvertColorProfileOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h index 6162408501b..960d267b906 100644 --- a/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h +++ b/source/blender/compositor/operations/COM_ConvertColorProfileOperation.h @@ -29,24 +29,24 @@ namespace blender::compositor { class ConvertColorProfileOperation : public NodeOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputOperation; + SocketReader *input_operation_; /** * \brief color profile where to convert from */ - int m_fromProfile; + int from_profile_; /** * \brief color profile where to convert to */ - int m_toProfile; + int to_profile_; /** * \brief is color predivided */ - bool m_predivided; + bool predivided_; public: /** @@ -57,29 +57,29 @@ class ConvertColorProfileOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setFromColorProfile(int colorProfile) + void set_from_color_profile(int color_profile) { - this->m_fromProfile = colorProfile; + from_profile_ = color_profile; } - void setToColorProfile(int colorProfile) + void set_to_color_profile(int color_profile) { - this->m_toProfile = colorProfile; + to_profile_ = color_profile; } - void setPredivided(bool predivided) + void set_predivided(bool predivided) { - this->m_predivided = predivided; + predivided_ = predivided; } }; diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc index 405ba03abf3..a781b3ea1ed 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.cc @@ -18,73 +18,71 @@ #include "COM_ConvertDepthToRadiusOperation.h" #include "BKE_camera.h" -#include "BLI_math.h" #include "DNA_camera_types.h" namespace blender::compositor { ConvertDepthToRadiusOperation::ConvertDepthToRadiusOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; - this->m_fStop = 128.0f; - this->m_cameraObject = nullptr; - this->m_maxRadius = 32.0f; - this->m_blurPostOperation = nullptr; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; + f_stop_ = 128.0f; + camera_object_ = nullptr; + max_radius_ = 32.0f; + blur_post_operation_ = nullptr; } -float ConvertDepthToRadiusOperation::determineFocalDistance() +float ConvertDepthToRadiusOperation::determine_focal_distance() { - if (this->m_cameraObject && this->m_cameraObject->type == OB_CAMERA) { - Camera *camera = (Camera *)this->m_cameraObject->data; - this->m_cam_lens = camera->lens; - return BKE_camera_object_dof_distance(this->m_cameraObject); + if (camera_object_ && camera_object_->type == OB_CAMERA) { + Camera *camera = (Camera *)camera_object_->data; + cam_lens_ = camera->lens; + return BKE_camera_object_dof_distance(camera_object_); } return 10.0f; } -void ConvertDepthToRadiusOperation::initExecution() +void ConvertDepthToRadiusOperation::init_execution() { float cam_sensor = DEFAULT_SENSOR_WIDTH; Camera *camera = nullptr; - if (this->m_cameraObject && this->m_cameraObject->type == OB_CAMERA) { - camera = (Camera *)this->m_cameraObject->data; + if (camera_object_ && camera_object_->type == OB_CAMERA) { + camera = (Camera *)camera_object_->data; cam_sensor = BKE_camera_sensor_size(camera->sensor_fit, camera->sensor_x, camera->sensor_y); } - this->m_inputOperation = this->getInputSocketReader(0); - float focalDistance = determineFocalDistance(); - if (focalDistance == 0.0f) { - focalDistance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ + input_operation_ = this->get_input_socket_reader(0); + float focal_distance = determine_focal_distance(); + if (focal_distance == 0.0f) { + focal_distance = 1e10f; /* If the DOF is 0.0 then set it to be far away. */ } - this->m_inverseFocalDistance = 1.0f / focalDistance; - this->m_aspect = (this->getWidth() > this->getHeight()) ? - (this->getHeight() / (float)this->getWidth()) : - (this->getWidth() / (float)this->getHeight()); - this->m_aperture = 0.5f * (this->m_cam_lens / (this->m_aspect * cam_sensor)) / this->m_fStop; - const float minsz = MIN2(getWidth(), getHeight()); - this->m_dof_sp = - minsz / ((cam_sensor / 2.0f) / - this->m_cam_lens); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ + inverse_focal_distance_ = 1.0f / focal_distance; + aspect_ = (this->get_width() > this->get_height()) ? + (this->get_height() / (float)this->get_width()) : + (this->get_width() / (float)this->get_height()); + aperture_ = 0.5f * (cam_lens_ / (aspect_ * cam_sensor)) / f_stop_; + const float minsz = MIN2(get_width(), get_height()); + dof_sp_ = minsz / ((cam_sensor / 2.0f) / + cam_lens_); /* <- == `aspect * MIN2(img->x, img->y) / tan(0.5f * fov)` */ - if (this->m_blurPostOperation) { - m_blurPostOperation->setSigma(MIN2(m_aperture * 128.0f, this->m_maxRadius)); + if (blur_post_operation_) { + blur_post_operation_->set_sigma(MIN2(aperture_ * 128.0f, max_radius_)); } } -void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertDepthToRadiusOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; + float input_value[4]; float z; float radius; - this->m_inputOperation->readSampled(inputValue, x, y, sampler); - z = inputValue[0]; + input_operation_->read_sampled(input_value, x, y, sampler); + z = input_value[0]; if (z != 0.0f) { float iZ = (1.0f / z); @@ -94,15 +92,14 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], /* Scale crad back to original maximum and blend. */ crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad); #endif - radius = 0.5f * fabsf(this->m_aperture * - (this->m_dof_sp * (this->m_inverseFocalDistance - iZ) - 1.0f)); + radius = 0.5f * fabsf(aperture_ * (dof_sp_ * (inverse_focal_distance_ - iZ) - 1.0f)); /* 'bug' T6615, limit minimum radius to 1 pixel, * not really a solution, but somewhat mitigates the problem. */ if (radius < 0.0f) { radius = 0.0f; } - if (radius > this->m_maxRadius) { - radius = this->m_maxRadius; + if (radius > max_radius_) { + radius = max_radius_; } output[0] = radius; } @@ -111,9 +108,9 @@ void ConvertDepthToRadiusOperation::executePixelSampled(float output[4], } } -void ConvertDepthToRadiusOperation::deinitExecution() +void ConvertDepthToRadiusOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -136,10 +133,10 @@ void ConvertDepthToRadiusOperation::update_memory_buffer_partial(MemoryBuffer *o * `crad->rect[px] = bcrad + wts->rect[px] * (scf * crad->rect[px] - bcrad);` */ #endif const float radius = 0.5f * - fabsf(m_aperture * (m_dof_sp * (m_inverseFocalDistance - inv_z) - 1.0f)); + fabsf(aperture_ * (dof_sp_ * (inverse_focal_distance_ - inv_z) - 1.0f)); /* Bug T6615, limit minimum radius to 1 pixel, * not really a solution, but somewhat mitigates the problem. */ - *it.out = CLAMPIS(radius, 0.0f, m_maxRadius); + *it.out = CLAMPIS(radius, 0.0f, max_radius_); } } diff --git a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h index 3d163843d06..72d19eb3dd8 100644 --- a/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h +++ b/source/blender/compositor/operations/COM_ConvertDepthToRadiusOperation.h @@ -31,19 +31,19 @@ namespace blender::compositor { class ConvertDepthToRadiusOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputOperation; - float m_fStop; - float m_aspect; - float m_maxRadius; - float m_inverseFocalDistance; - float m_aperture; - float m_cam_lens; - float m_dof_sp; - Object *m_cameraObject; + SocketReader *input_operation_; + float f_stop_; + float aspect_; + float max_radius_; + float inverse_focal_distance_; + float aperture_; + float cam_lens_; + float dof_sp_; + Object *camera_object_; - FastGaussianBlurValueOperation *m_blurPostOperation; + FastGaussianBlurValueOperation *blur_post_operation_; public: /** @@ -54,34 +54,34 @@ class ConvertDepthToRadiusOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setfStop(float fStop) + void setf_stop(float f_stop) { - this->m_fStop = fStop; + f_stop_ = f_stop; } - void setMaxRadius(float maxRadius) + void set_max_radius(float max_radius) { - this->m_maxRadius = maxRadius; + max_radius_ = max_radius; } - void setCameraObject(Object *camera) + void set_camera_object(Object *camera) { - this->m_cameraObject = camera; + camera_object_ = camera; } - float determineFocalDistance(); - void setPostBlur(FastGaussianBlurValueOperation *operation) + float determine_focal_distance(); + void set_post_blur(FastGaussianBlurValueOperation *operation) { - this->m_blurPostOperation = operation; + blur_post_operation_ = operation; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ConvertOperation.cc b/source/blender/compositor/operations/COM_ConvertOperation.cc index 9a3733dda5b..ec321494c6f 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.cc +++ b/source/blender/compositor/operations/COM_ConvertOperation.cc @@ -26,18 +26,18 @@ namespace blender::compositor { ConvertBaseOperation::ConvertBaseOperation() { - this->m_inputOperation = nullptr; - this->flags.can_be_constant = true; + input_operation_ = nullptr; + flags_.can_be_constant = true; } -void ConvertBaseOperation::initExecution() +void ConvertBaseOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void ConvertBaseOperation::deinitExecution() +void ConvertBaseOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } void ConvertBaseOperation::hash_output_params() @@ -56,17 +56,17 @@ void ConvertBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, ConvertValueToColorOperation::ConvertValueToColorOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); } -void ConvertValueToColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertValueToColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float value; - this->m_inputOperation->readSampled(&value, x, y, sampler); + input_operation_->read_sampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; output[3] = 1.0f; } @@ -83,18 +83,18 @@ void ConvertValueToColorOperation::update_memory_buffer_partial(BuffersIterator< ConvertColorToValueOperation::ConvertColorToValueOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); } -void ConvertColorToValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorToValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); - output[0] = (inputColor[0] + inputColor[1] + inputColor[2]) / 3.0f; + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + output[0] = (input_color[0] + input_color[1] + input_color[2]) / 3.0f; } void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -109,18 +109,18 @@ void ConvertColorToValueOperation::update_memory_buffer_partial(BuffersIterator< ConvertColorToBWOperation::ConvertColorToBWOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); } -void ConvertColorToBWOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorToBWOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); - output[0] = IMB_colormanagement_get_luminance(inputColor); + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + output[0] = IMB_colormanagement_get_luminance(input_color); } void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -134,17 +134,17 @@ void ConvertColorToBWOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Vector); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Vector); } -void ConvertColorToVectorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertColorToVectorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color[4]; - this->m_inputOperation->readSampled(color, x, y, sampler); + input_operation_->read_sampled(color, x, y, sampler); copy_v3_v3(output, color); } @@ -159,17 +159,17 @@ void ConvertColorToVectorOperation::update_memory_buffer_partial(BuffersIterator ConvertValueToVectorOperation::ConvertValueToVectorOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Vector); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Vector); } -void ConvertValueToVectorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertValueToVectorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float value; - this->m_inputOperation->readSampled(&value, x, y, sampler); + input_operation_->read_sampled(&value, x, y, sampler); output[0] = output[1] = output[2] = value; } @@ -184,16 +184,16 @@ void ConvertValueToVectorOperation::update_memory_buffer_partial(BuffersIterator ConvertVectorToColorOperation::ConvertVectorToColorOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Color); } -void ConvertVectorToColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertVectorToColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - this->m_inputOperation->readSampled(output, x, y, sampler); + input_operation_->read_sampled(output, x, y, sampler); output[3] = 1.0f; } @@ -209,17 +209,17 @@ void ConvertVectorToColorOperation::update_memory_buffer_partial(BuffersIterator ConvertVectorToValueOperation::ConvertVectorToValueOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Value); } -void ConvertVectorToValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertVectorToValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - this->m_inputOperation->readSampled(input, x, y, sampler); + input_operation_->read_sampled(input, x, y, sampler); output[0] = (input[0] + input[1] + input[2]) / 3.0f; } @@ -235,55 +235,55 @@ void ConvertVectorToValueOperation::update_memory_buffer_partial(BuffersIterator ConvertRGBToYCCOperation::ConvertRGBToYCCOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertRGBToYCCOperation::setMode(int mode) +void ConvertRGBToYCCOperation::set_mode(int mode) { switch (mode) { case 0: - this->m_mode = BLI_YCC_ITU_BT601; + mode_ = BLI_YCC_ITU_BT601; break; case 2: - this->m_mode = BLI_YCC_JFIF_0_255; + mode_ = BLI_YCC_JFIF_0_255; break; case 1: default: - this->m_mode = BLI_YCC_ITU_BT709; + mode_ = BLI_YCC_ITU_BT709; break; } } -void ConvertRGBToYCCOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertRGBToYCCOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; + float input_color[4]; float color[3]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + input_operation_->read_sampled(input_color, x, y, sampler); rgb_to_ycc( - inputColor[0], inputColor[1], inputColor[2], &color[0], &color[1], &color[2], this->m_mode); + input_color[0], input_color[1], input_color[2], &color[0], &color[1], &color[2], mode_); /* divided by 255 to normalize for viewing in */ /* R,G,B --> Y,Cb,Cr */ mul_v3_v3fl(output, color, 1.0f / 255.0f); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertRGBToYCCOperation::hash_output_params() { ConvertBaseOperation::hash_output_params(); - hash_param(m_mode); + hash_param(mode_); } void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIterator &it) { for (; !it.is_end(); ++it) { const float *in = it.in(0); - rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], this->m_mode); + rgb_to_ycc(in[0], in[1], in[2], &it.out[0], &it.out[1], &it.out[2], mode_); /* Normalize for viewing (#rgb_to_ycc returns 0-255 values). */ mul_v3_fl(it.out, 1.0f / 255.0f); @@ -295,52 +295,47 @@ void ConvertRGBToYCCOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertYCCToRGBOperation::setMode(int mode) +void ConvertYCCToRGBOperation::set_mode(int mode) { switch (mode) { case 0: - this->m_mode = BLI_YCC_ITU_BT601; + mode_ = BLI_YCC_ITU_BT601; break; case 2: - this->m_mode = BLI_YCC_JFIF_0_255; + mode_ = BLI_YCC_JFIF_0_255; break; case 1: default: - this->m_mode = BLI_YCC_ITU_BT709; + mode_ = BLI_YCC_ITU_BT709; break; } } -void ConvertYCCToRGBOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertYCCToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); /* need to un-normalize the data */ /* R,G,B --> Y,Cb,Cr */ - mul_v3_fl(inputColor, 255.0f); + mul_v3_fl(input_color, 255.0f); - ycc_to_rgb(inputColor[0], - inputColor[1], - inputColor[2], - &output[0], - &output[1], - &output[2], - this->m_mode); - output[3] = inputColor[3]; + ycc_to_rgb( + input_color[0], input_color[1], input_color[2], &output[0], &output[1], &output[2], mode_); + output[3] = input_color[3]; } void ConvertYCCToRGBOperation::hash_output_params() { ConvertBaseOperation::hash_output_params(); - hash_param(m_mode); + hash_param(mode_); } void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -348,13 +343,8 @@ void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIteratorm_mode); + ycc_to_rgb( + in[0] * 255.0f, in[1] * 255.0f, in[2] * 255.0f, &it.out[0], &it.out[1], &it.out[2], mode_); it.out[3] = in[3]; } } @@ -363,25 +353,25 @@ void ConvertYCCToRGBOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertRGBToYUVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertRGBToYUVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); - rgb_to_yuv(inputColor[0], - inputColor[1], - inputColor[2], + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_yuv(input_color[0], + input_color[1], + input_color[2], &output[0], &output[1], &output[2], BLI_YUV_ITU_BT709); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -397,25 +387,25 @@ void ConvertRGBToYUVOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertYUVToRGBOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertYUVToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); - yuv_to_rgb(inputColor[0], - inputColor[1], - inputColor[2], + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + yuv_to_rgb(input_color[0], + input_color[1], + input_color[2], &output[0], &output[1], &output[2], BLI_YUV_ITU_BT709); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -431,19 +421,19 @@ void ConvertYUVToRGBOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertRGBToHSVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertRGBToHSVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); - rgb_to_hsv_v(inputColor, output); - output[3] = inputColor[3]; + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + rgb_to_hsv_v(input_color, output); + output[3] = input_color[3]; } void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -459,22 +449,22 @@ void ConvertRGBToHSVOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertHSVToRGBOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertHSVToRGBOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputOperation->readSampled(inputColor, x, y, sampler); - hsv_to_rgb_v(inputColor, output); + float input_color[4]; + input_operation_->read_sampled(input_color, x, y, sampler); + hsv_to_rgb_v(input_color, output); output[0] = max_ff(output[0], 0.0f); output[1] = max_ff(output[1], 0.0f); output[2] = max_ff(output[2], 0.0f); - output[3] = inputColor[3]; + output[3] = input_color[3]; } void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -493,17 +483,17 @@ void ConvertHSVToRGBOperation::update_memory_buffer_partial(BuffersIteratoraddInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertPremulToStraightOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertPremulToStraightOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { ColorSceneLinear4f input; - this->m_inputOperation->readSampled(input, x, y, sampler); + input_operation_->read_sampled(input, x, y, sampler); ColorSceneLinear4f converted = input.unpremultiply_alpha(); copy_v4_v4(output, converted); } @@ -519,17 +509,17 @@ void ConvertPremulToStraightOperation::update_memory_buffer_partial(BuffersItera ConvertStraightToPremulOperation::ConvertStraightToPremulOperation() : ConvertBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void ConvertStraightToPremulOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ConvertStraightToPremulOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { ColorSceneLinear4f input; - this->m_inputOperation->readSampled(input, x, y, sampler); + input_operation_->read_sampled(input, x, y, sampler); ColorSceneLinear4f converted = input.premultiply_alpha(); copy_v4_v4(output, converted); } @@ -545,28 +535,28 @@ void ConvertStraightToPremulOperation::update_memory_buffer_partial(BuffersItera SeparateChannelOperation::SeparateChannelOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; } -void SeparateChannelOperation::initExecution() +void SeparateChannelOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void SeparateChannelOperation::deinitExecution() +void SeparateChannelOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } -void SeparateChannelOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void SeparateChannelOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - this->m_inputOperation->readSampled(input, x, y, sampler); - output[0] = input[this->m_channel]; + input_operation_->read_sampled(input, x, y, sampler); + output[0] = input[channel_]; } void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -574,7 +564,7 @@ void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - it.out[0] = it.in(0)[this->m_channel]; + it.out[0] = it.in(0)[channel_]; } } @@ -582,54 +572,54 @@ void SeparateChannelOperation::update_memory_buffer_partial(MemoryBuffer *output CombineChannelsOperation::CombineChannelsOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_inputChannel1Operation = nullptr; - this->m_inputChannel2Operation = nullptr; - this->m_inputChannel3Operation = nullptr; - this->m_inputChannel4Operation = nullptr; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); + input_channel1_operation_ = nullptr; + input_channel2_operation_ = nullptr; + input_channel3_operation_ = nullptr; + input_channel4_operation_ = nullptr; } -void CombineChannelsOperation::initExecution() +void CombineChannelsOperation::init_execution() { - this->m_inputChannel1Operation = this->getInputSocketReader(0); - this->m_inputChannel2Operation = this->getInputSocketReader(1); - this->m_inputChannel3Operation = this->getInputSocketReader(2); - this->m_inputChannel4Operation = this->getInputSocketReader(3); + input_channel1_operation_ = this->get_input_socket_reader(0); + input_channel2_operation_ = this->get_input_socket_reader(1); + input_channel3_operation_ = this->get_input_socket_reader(2); + input_channel4_operation_ = this->get_input_socket_reader(3); } -void CombineChannelsOperation::deinitExecution() +void CombineChannelsOperation::deinit_execution() { - this->m_inputChannel1Operation = nullptr; - this->m_inputChannel2Operation = nullptr; - this->m_inputChannel3Operation = nullptr; - this->m_inputChannel4Operation = nullptr; + input_channel1_operation_ = nullptr; + input_channel2_operation_ = nullptr; + input_channel3_operation_ = nullptr; + input_channel4_operation_ = nullptr; } -void CombineChannelsOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void CombineChannelsOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - if (this->m_inputChannel1Operation) { - this->m_inputChannel1Operation->readSampled(input, x, y, sampler); + if (input_channel1_operation_) { + input_channel1_operation_->read_sampled(input, x, y, sampler); output[0] = input[0]; } - if (this->m_inputChannel2Operation) { - this->m_inputChannel2Operation->readSampled(input, x, y, sampler); + if (input_channel2_operation_) { + input_channel2_operation_->read_sampled(input, x, y, sampler); output[1] = input[0]; } - if (this->m_inputChannel3Operation) { - this->m_inputChannel3Operation->readSampled(input, x, y, sampler); + if (input_channel3_operation_) { + input_channel3_operation_->read_sampled(input, x, y, sampler); output[2] = input[0]; } - if (this->m_inputChannel4Operation) { - this->m_inputChannel4Operation->readSampled(input, x, y, sampler); + if (input_channel4_operation_) { + input_channel4_operation_->read_sampled(input, x, y, sampler); output[3] = input[0]; } } diff --git a/source/blender/compositor/operations/COM_ConvertOperation.h b/source/blender/compositor/operations/COM_ConvertOperation.h index 72864b3c5e2..3c4ce358eec 100644 --- a/source/blender/compositor/operations/COM_ConvertOperation.h +++ b/source/blender/compositor/operations/COM_ConvertOperation.h @@ -24,13 +24,13 @@ namespace blender::compositor { class ConvertBaseOperation : public MultiThreadedOperation { protected: - SocketReader *m_inputOperation; + SocketReader *input_operation_; public: ConvertBaseOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -45,7 +45,7 @@ class ConvertValueToColorOperation : public ConvertBaseOperation { public: ConvertValueToColorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -55,7 +55,7 @@ class ConvertColorToValueOperation : public ConvertBaseOperation { public: ConvertColorToValueOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -65,7 +65,7 @@ class ConvertColorToBWOperation : public ConvertBaseOperation { public: ConvertColorToBWOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -75,7 +75,7 @@ class ConvertColorToVectorOperation : public ConvertBaseOperation { public: ConvertColorToVectorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -85,7 +85,7 @@ class ConvertValueToVectorOperation : public ConvertBaseOperation { public: ConvertValueToVectorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -95,7 +95,7 @@ class ConvertVectorToColorOperation : public ConvertBaseOperation { public: ConvertVectorToColorOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -105,7 +105,7 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation { public: ConvertVectorToValueOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -114,15 +114,15 @@ class ConvertVectorToValueOperation : public ConvertBaseOperation { class ConvertRGBToYCCOperation : public ConvertBaseOperation { private: /** YCbCr mode (Jpeg, ITU601, ITU709) */ - int m_mode; + int mode_; public: ConvertRGBToYCCOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** Set the YCC mode */ - void setMode(int mode); + void set_mode(int mode); protected: void hash_output_params() override; @@ -132,15 +132,15 @@ class ConvertRGBToYCCOperation : public ConvertBaseOperation { class ConvertYCCToRGBOperation : public ConvertBaseOperation { private: /** YCbCr mode (Jpeg, ITU601, ITU709) */ - int m_mode; + int mode_; public: ConvertYCCToRGBOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** Set the YCC mode */ - void setMode(int mode); + void set_mode(int mode); protected: void hash_output_params() override; @@ -151,7 +151,7 @@ class ConvertRGBToYUVOperation : public ConvertBaseOperation { public: ConvertRGBToYUVOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -161,7 +161,7 @@ class ConvertYUVToRGBOperation : public ConvertBaseOperation { public: ConvertYUVToRGBOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -171,7 +171,7 @@ class ConvertRGBToHSVOperation : public ConvertBaseOperation { public: ConvertRGBToHSVOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -181,7 +181,7 @@ class ConvertHSVToRGBOperation : public ConvertBaseOperation { public: ConvertHSVToRGBOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -191,7 +191,7 @@ class ConvertPremulToStraightOperation : public ConvertBaseOperation { public: ConvertPremulToStraightOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -201,7 +201,7 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation { public: ConvertStraightToPremulOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -209,19 +209,19 @@ class ConvertStraightToPremulOperation : public ConvertBaseOperation { class SeparateChannelOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - int m_channel; + SocketReader *input_operation_; + int channel_; public: SeparateChannelOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setChannel(int channel) + void set_channel(int channel) { - this->m_channel = channel; + channel_ = channel; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -231,17 +231,17 @@ class SeparateChannelOperation : public MultiThreadedOperation { class CombineChannelsOperation : public MultiThreadedOperation { private: - SocketReader *m_inputChannel1Operation; - SocketReader *m_inputChannel2Operation; - SocketReader *m_inputChannel3Operation; - SocketReader *m_inputChannel4Operation; + SocketReader *input_channel1_operation_; + SocketReader *input_channel2_operation_; + SocketReader *input_channel3_operation_; + SocketReader *input_channel4_operation_; public: CombineChannelsOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc index 9127a871b04..7ba1f0bbe14 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.cc @@ -17,11 +17,10 @@ */ #include "COM_ConvolutionEdgeFilterOperation.h" -#include "BLI_math.h" namespace blender::compositor { -void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void ConvolutionEdgeFilterOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float in1[4], in2[4], res1[4] = {0.0}, res2[4] = {0.0}; @@ -31,52 +30,52 @@ void ConvolutionEdgeFilterOperation::executePixel(float output[4], int x, int y, int y1 = y - 1; int y2 = y; int y3 = y + 1; - CLAMP(x1, 0, getWidth() - 1); - CLAMP(x2, 0, getWidth() - 1); - CLAMP(x3, 0, getWidth() - 1); - CLAMP(y1, 0, getHeight() - 1); - CLAMP(y2, 0, getHeight() - 1); - CLAMP(y3, 0, getHeight() - 1); + CLAMP(x1, 0, get_width() - 1); + CLAMP(x2, 0, get_width() - 1); + CLAMP(x3, 0, get_width() - 1); + CLAMP(y1, 0, get_height() - 1); + CLAMP(y2, 0, get_height() - 1); + CLAMP(y3, 0, get_height() - 1); float value[4]; - this->m_inputValueOperation->read(value, x2, y2, nullptr); + input_value_operation_->read(value, x2, y2, nullptr); float mval = 1.0f - value[0]; - this->m_inputOperation->read(in1, x1, y1, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[0]); - madd_v3_v3fl(res2, in1, this->m_filter[0]); + input_operation_->read(in1, x1, y1, nullptr); + madd_v3_v3fl(res1, in1, filter_[0]); + madd_v3_v3fl(res2, in1, filter_[0]); - this->m_inputOperation->read(in1, x2, y1, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[1]); - madd_v3_v3fl(res2, in1, this->m_filter[3]); + input_operation_->read(in1, x2, y1, nullptr); + madd_v3_v3fl(res1, in1, filter_[1]); + madd_v3_v3fl(res2, in1, filter_[3]); - this->m_inputOperation->read(in1, x3, y1, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[2]); - madd_v3_v3fl(res2, in1, this->m_filter[6]); + input_operation_->read(in1, x3, y1, nullptr); + madd_v3_v3fl(res1, in1, filter_[2]); + madd_v3_v3fl(res2, in1, filter_[6]); - this->m_inputOperation->read(in1, x1, y2, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[3]); - madd_v3_v3fl(res2, in1, this->m_filter[1]); + input_operation_->read(in1, x1, y2, nullptr); + madd_v3_v3fl(res1, in1, filter_[3]); + madd_v3_v3fl(res2, in1, filter_[1]); - this->m_inputOperation->read(in2, x2, y2, nullptr); - madd_v3_v3fl(res1, in2, this->m_filter[4]); - madd_v3_v3fl(res2, in2, this->m_filter[4]); + input_operation_->read(in2, x2, y2, nullptr); + madd_v3_v3fl(res1, in2, filter_[4]); + madd_v3_v3fl(res2, in2, filter_[4]); - this->m_inputOperation->read(in1, x3, y2, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[5]); - madd_v3_v3fl(res2, in1, this->m_filter[7]); + input_operation_->read(in1, x3, y2, nullptr); + madd_v3_v3fl(res1, in1, filter_[5]); + madd_v3_v3fl(res2, in1, filter_[7]); - this->m_inputOperation->read(in1, x1, y3, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[6]); - madd_v3_v3fl(res2, in1, this->m_filter[2]); + input_operation_->read(in1, x1, y3, nullptr); + madd_v3_v3fl(res1, in1, filter_[6]); + madd_v3_v3fl(res2, in1, filter_[2]); - this->m_inputOperation->read(in1, x2, y3, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[7]); - madd_v3_v3fl(res2, in1, this->m_filter[5]); + input_operation_->read(in1, x2, y3, nullptr); + madd_v3_v3fl(res1, in1, filter_[7]); + madd_v3_v3fl(res2, in1, filter_[5]); - this->m_inputOperation->read(in1, x3, y3, nullptr); - madd_v3_v3fl(res1, in1, this->m_filter[8]); - madd_v3_v3fl(res2, in1, this->m_filter[8]); + input_operation_->read(in1, x3, y3, nullptr); + madd_v3_v3fl(res1, in1, filter_[8]); + madd_v3_v3fl(res2, in1, filter_[8]); output[0] = sqrt(res1[0] * res1[0] + res2[0] * res2[0]); output[1] = sqrt(res1[1] * res1[1] + res2[1] * res2[1]); @@ -100,8 +99,8 @@ void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer * Span inputs) { const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; - const int last_x = getWidth() - 1; - const int last_y = getHeight() - 1; + const int last_x = get_width() - 1; + const int last_y = get_height() - 1; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; @@ -113,44 +112,44 @@ void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer * float res2[4] = {0}; const float *color = center_color + down_offset + left_offset; - madd_v3_v3fl(res1, color, m_filter[0]); + madd_v3_v3fl(res1, color, filter_[0]); copy_v3_v3(res2, res1); color = center_color + down_offset; - madd_v3_v3fl(res1, color, m_filter[1]); - madd_v3_v3fl(res2, color, m_filter[3]); + madd_v3_v3fl(res1, color, filter_[1]); + madd_v3_v3fl(res2, color, filter_[3]); color = center_color + down_offset + right_offset; - madd_v3_v3fl(res1, color, m_filter[2]); - madd_v3_v3fl(res2, color, m_filter[6]); + madd_v3_v3fl(res1, color, filter_[2]); + madd_v3_v3fl(res2, color, filter_[6]); color = center_color + left_offset; - madd_v3_v3fl(res1, color, m_filter[3]); - madd_v3_v3fl(res2, color, m_filter[1]); + madd_v3_v3fl(res1, color, filter_[3]); + madd_v3_v3fl(res2, color, filter_[1]); { float rgb_filtered[3]; - mul_v3_v3fl(rgb_filtered, center_color, m_filter[4]); + mul_v3_v3fl(rgb_filtered, center_color, filter_[4]); add_v3_v3(res1, rgb_filtered); add_v3_v3(res2, rgb_filtered); } color = center_color + right_offset; - madd_v3_v3fl(res1, color, m_filter[5]); - madd_v3_v3fl(res2, color, m_filter[7]); + madd_v3_v3fl(res1, color, filter_[5]); + madd_v3_v3fl(res2, color, filter_[7]); color = center_color + up_offset + left_offset; - madd_v3_v3fl(res1, color, m_filter[6]); - madd_v3_v3fl(res2, color, m_filter[2]); + madd_v3_v3fl(res1, color, filter_[6]); + madd_v3_v3fl(res2, color, filter_[2]); color = center_color + up_offset; - madd_v3_v3fl(res1, color, m_filter[7]); - madd_v3_v3fl(res2, color, m_filter[5]); + madd_v3_v3fl(res1, color, filter_[7]); + madd_v3_v3fl(res2, color, filter_[5]); { color = center_color + up_offset + right_offset; float rgb_filtered[3]; - mul_v3_v3fl(rgb_filtered, color, m_filter[8]); + mul_v3_v3fl(rgb_filtered, color, filter_[8]); add_v3_v3(res1, rgb_filtered); add_v3_v3(res2, rgb_filtered); } @@ -160,10 +159,10 @@ void ConvolutionEdgeFilterOperation::update_memory_buffer_partial(MemoryBuffer * it.out[2] = sqrt(res1[2] * res1[2] + res2[2] * res2[2]); const float factor = *it.in(FACTOR_INPUT_INDEX); - const float m_factor = 1.0f - factor; - it.out[0] = it.out[0] * factor + center_color[0] * m_factor; - it.out[1] = it.out[1] * factor + center_color[1] * m_factor; - it.out[2] = it.out[2] * factor + center_color[2] * m_factor; + const float factor_ = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * factor_; + it.out[1] = it.out[1] * factor + center_color[1] * factor_; + it.out[2] = it.out[2] * factor + center_color[2] * factor_; it.out[3] = center_color[3]; diff --git a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h index bd38e27165a..a89a9412cf2 100644 --- a/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionEdgeFilterOperation.h @@ -24,7 +24,7 @@ namespace blender::compositor { class ConvolutionEdgeFilterOperation : public ConvolutionFilterOperation { public: - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc index 11a077229fd..2e9990348c1 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.cc @@ -18,50 +18,46 @@ #include "COM_ConvolutionFilterOperation.h" -#include "BLI_utildefines.h" - -#include "MEM_guardedalloc.h" - namespace blender::compositor { ConvolutionFilterOperation::ConvolutionFilterOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->flags.complex = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); + input_operation_ = nullptr; + flags_.complex = true; } -void ConvolutionFilterOperation::initExecution() +void ConvolutionFilterOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputValueOperation = this->getInputSocketReader(1); + input_operation_ = this->get_input_socket_reader(0); + input_value_operation_ = this->get_input_socket_reader(1); } void ConvolutionFilterOperation::set3x3Filter( float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9) { - this->m_filter[0] = f1; - this->m_filter[1] = f2; - this->m_filter[2] = f3; - this->m_filter[3] = f4; - this->m_filter[4] = f5; - this->m_filter[5] = f6; - this->m_filter[6] = f7; - this->m_filter[7] = f8; - this->m_filter[8] = f9; - this->m_filterHeight = 3; - this->m_filterWidth = 3; + filter_[0] = f1; + filter_[1] = f2; + filter_[2] = f3; + filter_[3] = f4; + filter_[4] = f5; + filter_[5] = f6; + filter_[6] = f7; + filter_[7] = f8; + filter_[8] = f9; + filter_height_ = 3; + filter_width_ = 3; } -void ConvolutionFilterOperation::deinitExecution() +void ConvolutionFilterOperation::deinit_execution() { - this->m_inputOperation = nullptr; - this->m_inputValueOperation = nullptr; + input_operation_ = nullptr; + input_value_operation_ = nullptr; } -void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void ConvolutionFilterOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float in1[4]; float in2[4]; @@ -71,35 +67,35 @@ void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, voi int y1 = y - 1; int y2 = y; int y3 = y + 1; - CLAMP(x1, 0, getWidth() - 1); - CLAMP(x2, 0, getWidth() - 1); - CLAMP(x3, 0, getWidth() - 1); - CLAMP(y1, 0, getHeight() - 1); - CLAMP(y2, 0, getHeight() - 1); - CLAMP(y3, 0, getHeight() - 1); + CLAMP(x1, 0, get_width() - 1); + CLAMP(x2, 0, get_width() - 1); + CLAMP(x3, 0, get_width() - 1); + CLAMP(y1, 0, get_height() - 1); + CLAMP(y2, 0, get_height() - 1); + CLAMP(y3, 0, get_height() - 1); float value[4]; - this->m_inputValueOperation->read(value, x2, y2, nullptr); + input_value_operation_->read(value, x2, y2, nullptr); const float mval = 1.0f - value[0]; zero_v4(output); - this->m_inputOperation->read(in1, x1, y1, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[0]); - this->m_inputOperation->read(in1, x2, y1, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[1]); - this->m_inputOperation->read(in1, x3, y1, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[2]); - this->m_inputOperation->read(in1, x1, y2, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[3]); - this->m_inputOperation->read(in2, x2, y2, nullptr); - madd_v4_v4fl(output, in2, this->m_filter[4]); - this->m_inputOperation->read(in1, x3, y2, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[5]); - this->m_inputOperation->read(in1, x1, y3, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[6]); - this->m_inputOperation->read(in1, x2, y3, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[7]); - this->m_inputOperation->read(in1, x3, y3, nullptr); - madd_v4_v4fl(output, in1, this->m_filter[8]); + input_operation_->read(in1, x1, y1, nullptr); + madd_v4_v4fl(output, in1, filter_[0]); + input_operation_->read(in1, x2, y1, nullptr); + madd_v4_v4fl(output, in1, filter_[1]); + input_operation_->read(in1, x3, y1, nullptr); + madd_v4_v4fl(output, in1, filter_[2]); + input_operation_->read(in1, x1, y2, nullptr); + madd_v4_v4fl(output, in1, filter_[3]); + input_operation_->read(in2, x2, y2, nullptr); + madd_v4_v4fl(output, in2, filter_[4]); + input_operation_->read(in1, x3, y2, nullptr); + madd_v4_v4fl(output, in1, filter_[5]); + input_operation_->read(in1, x1, y3, nullptr); + madd_v4_v4fl(output, in1, filter_[6]); + input_operation_->read(in1, x2, y3, nullptr); + madd_v4_v4fl(output, in1, filter_[7]); + input_operation_->read(in1, x3, y3, nullptr); + madd_v4_v4fl(output, in1, filter_[8]); output[0] = output[0] * value[0] + in2[0] * mval; output[1] = output[1] * value[0] + in2[1] * mval; @@ -113,18 +109,18 @@ void ConvolutionFilterOperation::executePixel(float output[4], int x, int y, voi output[3] = MAX2(output[3], 0.0f); } -bool ConvolutionFilterOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool ConvolutionFilterOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - int addx = (this->m_filterWidth - 1) / 2 + 1; - int addy = (this->m_filterHeight - 1) / 2 + 1; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; + rcti new_input; + int addx = (filter_width_ - 1) / 2 + 1; + int addy = (filter_height_ - 1) / 2 + 1; + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void ConvolutionFilterOperation::get_area_of_interest(const int input_idx, @@ -133,8 +129,8 @@ void ConvolutionFilterOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const int add_x = (m_filterWidth - 1) / 2 + 1; - const int add_y = (m_filterHeight - 1) / 2 + 1; + const int add_x = (filter_width_ - 1) / 2 + 1; + const int add_y = (filter_height_ - 1) / 2 + 1; r_input_area.xmin = output_area.xmin - add_x; r_input_area.xmax = output_area.xmax + add_x; r_input_area.ymin = output_area.ymin - add_y; @@ -153,8 +149,8 @@ void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *outp Span inputs) { const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; - const int last_x = getWidth() - 1; - const int last_y = getHeight() - 1; + const int last_x = get_width() - 1; + const int last_y = get_height() - 1; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const int left_offset = (it.x == 0) ? 0 : -image->elem_stride; const int right_offset = (it.x == last_x) ? 0 : image->elem_stride; @@ -163,22 +159,22 @@ void ConvolutionFilterOperation::update_memory_buffer_partial(MemoryBuffer *outp const float *center_color = it.in(IMAGE_INPUT_INDEX); zero_v4(it.out); - madd_v4_v4fl(it.out, center_color + down_offset + left_offset, m_filter[0]); - madd_v4_v4fl(it.out, center_color + down_offset, m_filter[1]); - madd_v4_v4fl(it.out, center_color + down_offset + right_offset, m_filter[2]); - madd_v4_v4fl(it.out, center_color + left_offset, m_filter[3]); - madd_v4_v4fl(it.out, center_color, m_filter[4]); - madd_v4_v4fl(it.out, center_color + right_offset, m_filter[5]); - madd_v4_v4fl(it.out, center_color + up_offset + left_offset, m_filter[6]); - madd_v4_v4fl(it.out, center_color + up_offset, m_filter[7]); - madd_v4_v4fl(it.out, center_color + up_offset + right_offset, m_filter[8]); + madd_v4_v4fl(it.out, center_color + down_offset + left_offset, filter_[0]); + madd_v4_v4fl(it.out, center_color + down_offset, filter_[1]); + madd_v4_v4fl(it.out, center_color + down_offset + right_offset, filter_[2]); + madd_v4_v4fl(it.out, center_color + left_offset, filter_[3]); + madd_v4_v4fl(it.out, center_color, filter_[4]); + madd_v4_v4fl(it.out, center_color + right_offset, filter_[5]); + madd_v4_v4fl(it.out, center_color + up_offset + left_offset, filter_[6]); + madd_v4_v4fl(it.out, center_color + up_offset, filter_[7]); + madd_v4_v4fl(it.out, center_color + up_offset + right_offset, filter_[8]); const float factor = *it.in(FACTOR_INPUT_INDEX); - const float m_factor = 1.0f - factor; - it.out[0] = it.out[0] * factor + center_color[0] * m_factor; - it.out[1] = it.out[1] * factor + center_color[1] * m_factor; - it.out[2] = it.out[2] * factor + center_color[2] * m_factor; - it.out[3] = it.out[3] * factor + center_color[3] * m_factor; + const float factor_ = 1.0f - factor; + it.out[0] = it.out[0] * factor + center_color[0] * factor_; + it.out[1] = it.out[1] * factor + center_color[1] * factor_; + it.out[2] = it.out[2] * factor + center_color[2] * factor_; + it.out[3] = it.out[3] * factor + center_color[3] * factor_; /* Make sure we don't return negative color. */ CLAMP4_MIN(it.out, 0.0f); diff --git a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h index 7e12c7faa5c..d764c7c9081 100644 --- a/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h +++ b/source/blender/compositor/operations/COM_ConvolutionFilterOperation.h @@ -28,25 +28,25 @@ class ConvolutionFilterOperation : public MultiThreadedOperation { static constexpr int FACTOR_INPUT_INDEX = 1; private: - int m_filterWidth; - int m_filterHeight; + int filter_width_; + int filter_height_; protected: - SocketReader *m_inputOperation; - SocketReader *m_inputValueOperation; - float m_filter[9]; + SocketReader *input_operation_; + SocketReader *input_value_operation_; + float filter_[9]; public: ConvolutionFilterOperation(); void set3x3Filter( float f1, float f2, float f3, float f4, float f5, float f6, float f7, float f8, float f9); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; virtual void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_CropOperation.cc b/source/blender/compositor/operations/COM_CropOperation.cc index 12833660fcb..5d78ed9d41a 100644 --- a/source/blender/compositor/operations/COM_CropOperation.cc +++ b/source/blender/compositor/operations/COM_CropOperation.cc @@ -17,27 +17,26 @@ */ #include "COM_CropOperation.h" -#include "BLI_math.h" namespace blender::compositor { CropBaseOperation::CropBaseOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addOutputSocket(DataType::Color); - this->m_inputOperation = nullptr; - this->m_settings = nullptr; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Color); + input_operation_ = nullptr; + settings_ = nullptr; } -void CropBaseOperation::updateArea() +void CropBaseOperation::update_area() { - SocketReader *inputReference = this->getInputSocketReader(0); - float width = inputReference->getWidth(); - float height = inputReference->getHeight(); - NodeTwoXYs local_settings = *this->m_settings; + SocketReader *input_reference = this->get_input_socket_reader(0); + float width = input_reference->get_width(); + float height = input_reference->get_height(); + NodeTwoXYs local_settings = *settings_; if (width > 0.0f && height > 0.0f) { - if (this->m_relative) { + if (relative_) { local_settings.x1 = width * local_settings.fac_x1; local_settings.x2 = width * local_settings.fac_x2; local_settings.y1 = height * local_settings.fac_y1; @@ -56,28 +55,28 @@ void CropBaseOperation::updateArea() local_settings.y2 = height - 1; } - this->m_xmax = MAX2(local_settings.x1, local_settings.x2) + 1; - this->m_xmin = MIN2(local_settings.x1, local_settings.x2); - this->m_ymax = MAX2(local_settings.y1, local_settings.y2) + 1; - this->m_ymin = MIN2(local_settings.y1, local_settings.y2); + xmax_ = MAX2(local_settings.x1, local_settings.x2) + 1; + xmin_ = MIN2(local_settings.x1, local_settings.x2); + ymax_ = MAX2(local_settings.y1, local_settings.y2) + 1; + ymin_ = MIN2(local_settings.y1, local_settings.y2); } else { - this->m_xmax = 0; - this->m_xmin = 0; - this->m_ymax = 0; - this->m_ymin = 0; + xmax_ = 0; + xmin_ = 0; + ymax_ = 0; + ymin_ = 0; } } -void CropBaseOperation::initExecution() +void CropBaseOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); - updateArea(); + input_operation_ = this->get_input_socket_reader(0); + update_area(); } -void CropBaseOperation::deinitExecution() +void CropBaseOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } CropOperation::CropOperation() : CropBaseOperation() @@ -85,10 +84,10 @@ CropOperation::CropOperation() : CropBaseOperation() /* pass */ } -void CropOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void CropOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - if ((x < this->m_xmax && x >= this->m_xmin) && (y < this->m_ymax && y >= this->m_ymin)) { - this->m_inputOperation->readSampled(output, x, y, sampler); + if ((x < xmax_ && x >= xmin_) && (y < ymax_ && y >= ymin_)) { + input_operation_->read_sampled(output, x, y, sampler); } else { zero_v4(output); @@ -100,7 +99,7 @@ void CropOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { rcti crop_area; - BLI_rcti_init(&crop_area, m_xmin, m_xmax, m_ymin, m_ymax); + BLI_rcti_init(&crop_area, xmin_, xmax_, ymin_, ymax_); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { if (BLI_rcti_isect_pt(&crop_area, it.x, it.y)) { copy_v4_v4(it.out, it.in(0)); @@ -116,18 +115,18 @@ CropImageOperation::CropImageOperation() : CropBaseOperation() /* pass */ } -bool CropImageOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool CropImageOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + this->m_xmin; - newInput.xmin = input->xmin + this->m_xmin; - newInput.ymax = input->ymax + this->m_ymin; - newInput.ymin = input->ymin + this->m_ymin; + new_input.xmax = input->xmax + xmin_; + new_input.xmin = input->xmin + xmin_; + new_input.ymax = input->ymax + ymin_; + new_input.ymin = input->ymin + ymin_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void CropImageOperation::get_area_of_interest(const int input_idx, @@ -136,28 +135,27 @@ void CropImageOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = output_area.xmax + this->m_xmin; - r_input_area.xmin = output_area.xmin + this->m_xmin; - r_input_area.ymax = output_area.ymax + this->m_ymin; - r_input_area.ymin = output_area.ymin + this->m_ymin; + r_input_area.xmax = output_area.xmax + xmin_; + r_input_area.xmin = output_area.xmin + xmin_; + r_input_area.ymax = output_area.ymax + ymin_; + r_input_area.ymin = output_area.ymin + ymin_; } -void CropImageOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void CropImageOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation::determineResolution(resolution, preferredResolution); - updateArea(); - resolution[0] = this->m_xmax - this->m_xmin; - resolution[1] = this->m_ymax - this->m_ymin; + NodeOperation::determine_canvas(preferred_area, r_area); + update_area(); + r_area.xmax = r_area.xmin + (xmax_ - xmin_); + r_area.ymax = r_area.ymin + (ymax_ - ymin_); } -void CropImageOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void CropImageOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - if (x >= 0 && x < getWidth() && y >= 0 && y < getHeight()) { - this->m_inputOperation->readSampled(output, (x + this->m_xmin), (y + this->m_ymin), sampler); + if (x >= 0 && x < get_width() && y >= 0 && y < get_height()) { + input_operation_->read_sampled(output, (x + xmin_), (y + ymin_), sampler); } else { zero_v4(output); @@ -169,11 +167,11 @@ void CropImageOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { rcti op_area; - BLI_rcti_init(&op_area, 0, getWidth(), 0, getHeight()); + BLI_rcti_init(&op_area, 0, get_width(), 0, get_height()); const MemoryBuffer *input = inputs[0]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { if (BLI_rcti_isect_pt(&op_area, it.x, it.y)) { - input->read_elem_checked(it.x + this->m_xmin, it.y + this->m_ymin, it.out); + input->read_elem_checked(it.x + xmin_, it.y + ymin_, it.out); } else { zero_v4(it.out); diff --git a/source/blender/compositor/operations/COM_CropOperation.h b/source/blender/compositor/operations/COM_CropOperation.h index 57caa4e5834..69bfd72b052 100644 --- a/source/blender/compositor/operations/COM_CropOperation.h +++ b/source/blender/compositor/operations/COM_CropOperation.h @@ -24,27 +24,27 @@ namespace blender::compositor { class CropBaseOperation : public MultiThreadedOperation { protected: - SocketReader *m_inputOperation; - NodeTwoXYs *m_settings; - bool m_relative; - int m_xmax; - int m_xmin; - int m_ymax; - int m_ymin; + SocketReader *input_operation_; + NodeTwoXYs *settings_; + bool relative_; + int xmax_; + int xmin_; + int ymax_; + int ymin_; - void updateArea(); + void update_area(); public: CropBaseOperation(); - void initExecution() override; - void deinitExecution() override; - void setCropSettings(NodeTwoXYs *settings) + void init_execution() override; + void deinit_execution() override; + void set_crop_settings(NodeTwoXYs *settings) { - this->m_settings = settings; + settings_ = settings; } - void setRelative(bool rel) + void set_relative(bool rel) { - this->m_relative = rel; + relative_ = rel; } }; @@ -52,7 +52,7 @@ class CropOperation : public CropBaseOperation { private: public: CropOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -63,12 +63,11 @@ class CropImageOperation : public CropBaseOperation { private: public: CropImageOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.cc b/source/blender/compositor/operations/COM_CryptomatteOperation.cc index 02e7c5607d8..8a246269691 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.cc +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.cc @@ -24,27 +24,27 @@ CryptomatteOperation::CryptomatteOperation(size_t num_inputs) { inputs.resize(num_inputs); for (size_t i = 0; i < num_inputs; i++) { - this->addInputSocket(DataType::Color); + this->add_input_socket(DataType::Color); } - this->addOutputSocket(DataType::Color); - this->flags.complex = true; + this->add_output_socket(DataType::Color); + flags_.complex = true; } -void CryptomatteOperation::initExecution() +void CryptomatteOperation::init_execution() { for (size_t i = 0; i < inputs.size(); i++) { - inputs[i] = this->getInputSocketReader(i); + inputs[i] = this->get_input_socket_reader(i); } } -void CryptomatteOperation::addObjectIndex(float objectIndex) +void CryptomatteOperation::add_object_index(float object_index) { - if (objectIndex != 0.0f) { - m_objectIndex.append(objectIndex); + if (object_index != 0.0f) { + object_index_.append(object_index); } } -void CryptomatteOperation::executePixel(float output[4], int x, int y, void *data) +void CryptomatteOperation::execute_pixel(float output[4], int x, int y, void *data) { float input[4]; output[0] = output[1] = output[2] = output[3] = 0.0f; @@ -60,7 +60,7 @@ void CryptomatteOperation::executePixel(float output[4], int x, int y, void *dat output[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); output[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } - for (float hash : m_objectIndex) { + for (float hash : object_index_) { if (input[0] == hash) { output[3] += input[1]; } @@ -89,7 +89,7 @@ void CryptomatteOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[1] = ((float)(m3hash << 8) / (float)UINT32_MAX); it.out[2] = ((float)(m3hash << 16) / (float)UINT32_MAX); } - for (const float hash : m_objectIndex) { + for (const float hash : object_index_) { if (input[0] == hash) { it.out[3] += input[1]; } diff --git a/source/blender/compositor/operations/COM_CryptomatteOperation.h b/source/blender/compositor/operations/COM_CryptomatteOperation.h index f1bf4cdf624..2fa6fbc8390 100644 --- a/source/blender/compositor/operations/COM_CryptomatteOperation.h +++ b/source/blender/compositor/operations/COM_CryptomatteOperation.h @@ -24,17 +24,17 @@ namespace blender::compositor { class CryptomatteOperation : public MultiThreadedOperation { private: - Vector m_objectIndex; + Vector object_index_; public: Vector inputs; CryptomatteOperation(size_t num_inputs = 6); - void initExecution() override; - void executePixel(float output[4], int x, int y, void *data) override; + void init_execution() override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void addObjectIndex(float objectIndex); + void add_object_index(float object_index); void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.cc b/source/blender/compositor/operations/COM_CurveBaseOperation.cc index 3c4b27aa4cf..a014fb17750 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.cc +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.cc @@ -24,37 +24,37 @@ namespace blender::compositor { CurveBaseOperation::CurveBaseOperation() { - this->m_curveMapping = nullptr; - this->flags.can_be_constant = true; + curve_mapping_ = nullptr; + flags_.can_be_constant = true; } CurveBaseOperation::~CurveBaseOperation() { - if (this->m_curveMapping) { - BKE_curvemapping_free(this->m_curveMapping); - this->m_curveMapping = nullptr; + if (curve_mapping_) { + BKE_curvemapping_free(curve_mapping_); + curve_mapping_ = nullptr; } } -void CurveBaseOperation::initExecution() +void CurveBaseOperation::init_execution() { - BKE_curvemapping_init(this->m_curveMapping); + BKE_curvemapping_init(curve_mapping_); } -void CurveBaseOperation::deinitExecution() +void CurveBaseOperation::deinit_execution() { - if (this->m_curveMapping) { - BKE_curvemapping_free(this->m_curveMapping); - this->m_curveMapping = nullptr; + if (curve_mapping_) { + BKE_curvemapping_free(curve_mapping_); + curve_mapping_ = nullptr; } } -void CurveBaseOperation::setCurveMapping(CurveMapping *mapping) +void CurveBaseOperation::set_curve_mapping(CurveMapping *mapping) { /* duplicate the curve to avoid glitches while drawing, see bug T32374. */ - if (this->m_curveMapping) { - BKE_curvemapping_free(this->m_curveMapping); + if (curve_mapping_) { + BKE_curvemapping_free(curve_mapping_); } - this->m_curveMapping = BKE_curvemapping_copy(mapping); + curve_mapping_ = BKE_curvemapping_copy(mapping); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_CurveBaseOperation.h b/source/blender/compositor/operations/COM_CurveBaseOperation.h index da665e7ea60..d3548c36870 100644 --- a/source/blender/compositor/operations/COM_CurveBaseOperation.h +++ b/source/blender/compositor/operations/COM_CurveBaseOperation.h @@ -19,16 +19,17 @@ #pragma once #include "COM_MultiThreadedOperation.h" -#include "DNA_color_types.h" + +struct CurveMapping; namespace blender::compositor { class CurveBaseOperation : public MultiThreadedOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - CurveMapping *m_curveMapping; + CurveMapping *curve_mapping_; public: CurveBaseOperation(); @@ -37,10 +38,10 @@ class CurveBaseOperation : public MultiThreadedOperation { /** * Initialize the execution */ - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setCurveMapping(CurveMapping *mapping); + void set_curve_mapping(CurveMapping *mapping); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.cc b/source/blender/compositor/operations/COM_DenoiseOperation.cc index e7f2d5a740a..0174c50450c 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.cc +++ b/source/blender/compositor/operations/COM_DenoiseOperation.cc @@ -17,183 +17,252 @@ */ #include "COM_DenoiseOperation.h" -#include "BLI_math.h" #include "BLI_system.h" #ifdef WITH_OPENIMAGEDENOISE # include "BLI_threads.h" # include static pthread_mutex_t oidn_lock = BLI_MUTEX_INITIALIZER; #endif -#include namespace blender::compositor { -DenoiseOperation::DenoiseOperation() +bool COM_is_denoise_supported() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_settings = nullptr; - flags.is_fullframe_operation = true; - output_rendered_ = false; -} -void DenoiseOperation::initExecution() -{ - SingleThreadedOperation::initExecution(); - this->m_inputProgramColor = getInputSocketReader(0); - this->m_inputProgramNormal = getInputSocketReader(1); - this->m_inputProgramAlbedo = getInputSocketReader(2); +#ifdef WITH_OPENIMAGEDENOISE + /* Always supported through Accelerate framework BNNS on macOS. */ +# ifdef __APPLE__ + return true; +# else + return BLI_cpu_support_sse41(); +# endif + +#else + return false; +#endif } -void DenoiseOperation::deinitExecution() -{ - this->m_inputProgramColor = nullptr; - this->m_inputProgramNormal = nullptr; - this->m_inputProgramAlbedo = nullptr; - SingleThreadedOperation::deinitExecution(); -} +class DenoiseFilter { + private: +#ifdef WITH_OPENIMAGEDENOISE + oidn::DeviceRef device_; + oidn::FilterRef filter_; +#endif + bool initialized_ = false; -MemoryBuffer *DenoiseOperation::createMemoryBuffer(rcti *rect2) -{ - MemoryBuffer *tileColor = (MemoryBuffer *)this->m_inputProgramColor->initializeTileData(rect2); - MemoryBuffer *tileNormal = (MemoryBuffer *)this->m_inputProgramNormal->initializeTileData(rect2); - MemoryBuffer *tileAlbedo = (MemoryBuffer *)this->m_inputProgramAlbedo->initializeTileData(rect2); - rcti rect; - rect.xmin = 0; - rect.ymin = 0; - rect.xmax = getWidth(); - rect.ymax = getHeight(); - MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - this->generateDenoise(result, tileColor, tileNormal, tileAlbedo, this->m_settings); - return result; -} - -bool DenoiseOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) -{ - if (isCached()) { - return false; - } - - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); -} - -void DenoiseOperation::generateDenoise(MemoryBuffer *output, - MemoryBuffer *input_color, - MemoryBuffer *input_normal, - MemoryBuffer *input_albedo, - NodeDenoise *settings) -{ - BLI_assert(input_color->getBuffer()); - if (!input_color->getBuffer()) { - return; + public: + ~DenoiseFilter() + { + BLI_assert(!initialized_); } #ifdef WITH_OPENIMAGEDENOISE - /* Always supported through Accelerate framework BNNS on macOS. */ -# ifndef __APPLE__ - if (BLI_cpu_support_sse41()) -# endif + void init_and_lock_denoiser(MemoryBuffer *output) { - /* OpenImageDenoise needs full buffers. */ - MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : - input_color; - MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ? - input_normal->inflate() : - input_normal; - MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ? - input_albedo->inflate() : - input_albedo; - /* Since it's memory intensive, it's better to run only one instance of OIDN at a time. - * OpenImageDenoise is multithreaded internally and should use all available cores nonetheless. - */ + * OpenImageDenoise is multithreaded internally and should use all available cores + * nonetheless. */ BLI_mutex_lock(&oidn_lock); - oidn::DeviceRef device = oidn::newDevice(); - device.commit(); + device_ = oidn::newDevice(); + device_.set("setAffinity", false); + device_.commit(); + filter_ = device_.newFilter("RT"); + initialized_ = true; + set_image("output", output); + } - oidn::FilterRef filter = device.newFilter("RT"); - filter.setImage("color", - buf_color->getBuffer(), - oidn::Format::Float3, - buf_color->getWidth(), - buf_color->getHeight(), - 0, - sizeof(float[4])); - if (buf_normal && buf_normal->getBuffer()) { - filter.setImage("normal", - buf_normal->getBuffer(), - oidn::Format::Float3, - buf_normal->getWidth(), - buf_normal->getHeight(), - 0, - sizeof(float[3])); - } - if (buf_albedo && buf_albedo->getBuffer()) { - filter.setImage("albedo", - buf_albedo->getBuffer(), - oidn::Format::Float3, - buf_albedo->getWidth(), - buf_albedo->getHeight(), - 0, - sizeof(float[4])); - } - filter.setImage("output", - output->getBuffer(), - oidn::Format::Float3, - buf_color->getWidth(), - buf_color->getHeight(), - 0, - sizeof(float[4])); - - BLI_assert(settings); - if (settings) { - filter.set("hdr", settings->hdr); - filter.set("srgb", false); - } - - filter.commit(); - filter.execute(); + void deinit_and_unlock_denoiser() + { BLI_mutex_unlock(&oidn_lock); + initialized_ = false; + } - /* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */ - output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3); + void set_image(const StringRef name, MemoryBuffer *buffer) + { + BLI_assert(initialized_); + BLI_assert(!buffer->is_a_single_elem()); + filter_.setImage(name.data(), + buffer->get_buffer(), + oidn::Format::Float3, + buffer->get_width(), + buffer->get_height(), + 0, + buffer->get_elem_bytes_len()); + } - /* Delete inflated buffers. */ - if (input_color->is_a_single_elem()) { - delete buf_color; - } - if (input_normal && input_normal->is_a_single_elem()) { - delete buf_normal; - } - if (input_albedo && input_albedo->is_a_single_elem()) { - delete buf_albedo; - } + template void set(const StringRef option_name, T value) + { + BLI_assert(initialized_); + filter_.set(option_name.data(), value); + } - return; + void execute() + { + BLI_assert(initialized_); + filter_.commit(); + filter_.execute(); + } + +#else + void init_and_lock_denoiser(MemoryBuffer *UNUSED(output)) + { + } + + void deinit_and_unlock_denoiser() + { + } + + void set_image(const StringRef UNUSED(name), MemoryBuffer *UNUSED(buffer)) + { + } + + template void set(const StringRef UNUSED(option_name), T UNUSED(value)) + { + } + + void execute() + { } #endif - /* If built without OIDN or running on an unsupported CPU, just pass through. */ - UNUSED_VARS(input_albedo, input_normal, settings); - output->copy_from(input_color, input_color->get_rect()); +}; + +DenoiseBaseOperation::DenoiseBaseOperation() +{ + flags_.is_fullframe_operation = true; + output_rendered_ = false; } -void DenoiseOperation::get_area_of_interest(const int UNUSED(input_idx), - const rcti &UNUSED(output_area), - rcti &r_input_area) +bool DenoiseBaseOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + if (is_cached()) { + return false; + } + + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); +} + +void DenoiseBaseOperation::get_area_of_interest(const int UNUSED(input_idx), + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + r_input_area = this->get_canvas(); +} + +DenoiseOperation::DenoiseOperation() +{ + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + settings_ = nullptr; +} +void DenoiseOperation::init_execution() +{ + SingleThreadedOperation::init_execution(); + input_program_color_ = get_input_socket_reader(0); + input_program_normal_ = get_input_socket_reader(1); + input_program_albedo_ = get_input_socket_reader(2); +} + +void DenoiseOperation::deinit_execution() +{ + input_program_color_ = nullptr; + input_program_normal_ = nullptr; + input_program_albedo_ = nullptr; + SingleThreadedOperation::deinit_execution(); +} + +static bool are_guiding_passes_noise_free(NodeDenoise *settings) +{ + switch (settings->prefilter) { + case CMP_NODE_DENOISE_PREFILTER_NONE: + case CMP_NODE_DENOISE_PREFILTER_ACCURATE: /* Prefiltered with #DenoisePrefilterOperation. */ + return true; + case CMP_NODE_DENOISE_PREFILTER_FAST: + default: + return false; + } +} + +void DenoiseOperation::hash_output_params() +{ + if (settings_) { + hash_params((int)settings_->hdr, are_guiding_passes_noise_free(settings_)); + } +} + +MemoryBuffer *DenoiseOperation::create_memory_buffer(rcti *rect2) +{ + MemoryBuffer *tile_color = (MemoryBuffer *)input_program_color_->initialize_tile_data(rect2); + MemoryBuffer *tile_normal = (MemoryBuffer *)input_program_normal_->initialize_tile_data(rect2); + MemoryBuffer *tile_albedo = (MemoryBuffer *)input_program_albedo_->initialize_tile_data(rect2); + rcti rect; + rect.xmin = 0; + rect.ymin = 0; + rect.xmax = get_width(); + rect.ymax = get_height(); + MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); + this->generate_denoise(result, tile_color, tile_normal, tile_albedo, settings_); + return result; +} + +void DenoiseOperation::generate_denoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, + NodeDenoise *settings) +{ + BLI_assert(input_color->get_buffer()); + if (!input_color->get_buffer()) { + return; + } + + BLI_assert(COM_is_denoise_supported()); + /* OpenImageDenoise needs full buffers. */ + MemoryBuffer *buf_color = input_color->is_a_single_elem() ? input_color->inflate() : input_color; + MemoryBuffer *buf_normal = input_normal && input_normal->is_a_single_elem() ? + input_normal->inflate() : + input_normal; + MemoryBuffer *buf_albedo = input_albedo && input_albedo->is_a_single_elem() ? + input_albedo->inflate() : + input_albedo; + + DenoiseFilter filter; + filter.init_and_lock_denoiser(output); + + filter.set_image("color", buf_color); + filter.set_image("normal", buf_normal); + filter.set_image("albedo", buf_albedo); + + BLI_assert(settings); + if (settings) { + filter.set("hdr", settings->hdr); + filter.set("srgb", false); + filter.set("cleanAux", are_guiding_passes_noise_free(settings)); + } + + filter.execute(); + filter.deinit_and_unlock_denoiser(); + + /* Copy the alpha channel, OpenImageDenoise currently only supports RGB. */ + output->copy_from(input_color, input_color->get_rect(), 3, COM_DATA_TYPE_VALUE_CHANNELS, 3); + + /* Delete inflated buffers. */ + if (input_color->is_a_single_elem()) { + delete buf_color; + } + if (input_normal && input_normal->is_a_single_elem()) { + delete buf_normal; + } + if (input_albedo && input_albedo->is_a_single_elem()) { + delete buf_albedo; + } } void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, @@ -201,7 +270,60 @@ void DenoiseOperation::update_memory_buffer(MemoryBuffer *output, Span inputs) { if (!output_rendered_) { - this->generateDenoise(output, inputs[0], inputs[1], inputs[2], m_settings); + this->generate_denoise(output, inputs[0], inputs[1], inputs[2], settings_); + output_rendered_ = true; + } +} + +DenoisePrefilterOperation::DenoisePrefilterOperation(DataType data_type) +{ + this->add_input_socket(data_type); + this->add_output_socket(data_type); + image_name_ = ""; +} + +void DenoisePrefilterOperation::hash_output_params() +{ + hash_param(image_name_); +} + +MemoryBuffer *DenoisePrefilterOperation::create_memory_buffer(rcti *rect2) +{ + MemoryBuffer *input = (MemoryBuffer *)this->get_input_operation(0)->initialize_tile_data(rect2); + rcti rect; + BLI_rcti_init(&rect, 0, get_width(), 0, get_height()); + + MemoryBuffer *result = new MemoryBuffer(get_output_socket()->get_data_type(), rect); + generate_denoise(result, input); + + return result; +} + +void DenoisePrefilterOperation::generate_denoise(MemoryBuffer *output, MemoryBuffer *input) +{ + BLI_assert(COM_is_denoise_supported()); + + /* Denoising needs full buffers. */ + MemoryBuffer *input_buf = input->is_a_single_elem() ? input->inflate() : input; + + DenoiseFilter filter; + filter.init_and_lock_denoiser(output); + filter.set_image(image_name_, input_buf); + filter.execute(); + filter.deinit_and_unlock_denoiser(); + + /* Delete inflated buffers. */ + if (input->is_a_single_elem()) { + delete input_buf; + } +} + +void DenoisePrefilterOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &UNUSED(area), + Span inputs) +{ + if (!output_rendered_) { + this->generate_denoise(output, inputs[0]); output_rendered_ = true; } } diff --git a/source/blender/compositor/operations/COM_DenoiseOperation.h b/source/blender/compositor/operations/COM_DenoiseOperation.h index 48209c3eacf..a3eb1f57f2f 100644 --- a/source/blender/compositor/operations/COM_DenoiseOperation.h +++ b/source/blender/compositor/operations/COM_DenoiseOperation.h @@ -23,55 +23,91 @@ namespace blender::compositor { -class DenoiseOperation : public SingleThreadedOperation { +bool COM_is_denoise_supported(); + +class DenoiseBaseOperation : public SingleThreadedOperation { + protected: + bool output_rendered_; + + protected: + DenoiseBaseOperation(); + + public: + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + + void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; +}; + +class DenoiseOperation : public DenoiseBaseOperation { private: /** * \brief Cached reference to the input programs */ - SocketReader *m_inputProgramColor; - SocketReader *m_inputProgramAlbedo; - SocketReader *m_inputProgramNormal; + SocketReader *input_program_color_; + SocketReader *input_program_albedo_; + SocketReader *input_program_normal_; /** * \brief settings of the denoise node. */ - NodeDenoise *m_settings; - - bool output_rendered_; + NodeDenoise *settings_; public: DenoiseOperation(); /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setDenoiseSettings(NodeDenoise *settings) + void set_denoise_settings(NodeDenoise *settings) { - this->m_settings = settings; + settings_ = settings; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer(MemoryBuffer *output, const rcti &area, Span inputs) override; protected: - void generateDenoise(MemoryBuffer *output, - MemoryBuffer *input_color, - MemoryBuffer *input_normal, - MemoryBuffer *input_albedo, - NodeDenoise *settings); + void hash_output_params() override; + void generate_denoise(MemoryBuffer *output, + MemoryBuffer *input_color, + MemoryBuffer *input_normal, + MemoryBuffer *input_albedo, + NodeDenoise *settings); - MemoryBuffer *createMemoryBuffer(rcti *rect) override; + MemoryBuffer *create_memory_buffer(rcti *rect) override; +}; + +class DenoisePrefilterOperation : public DenoiseBaseOperation { + private: + std::string image_name_; + + public: + DenoisePrefilterOperation(DataType data_type); + + void set_image_name(StringRef name) + { + image_name_ = name; + } + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) override; + + protected: + void hash_output_params() override; + MemoryBuffer *create_memory_buffer(rcti *rect) override; + + private: + void generate_denoise(MemoryBuffer *output, MemoryBuffer *input); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.cc b/source/blender/compositor/operations/COM_DespeckleOperation.cc index 19bd7b2af6f..c60a4167dfc 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.cc +++ b/source/blender/compositor/operations/COM_DespeckleOperation.cc @@ -20,29 +20,27 @@ #include "COM_DespeckleOperation.h" -#include "BLI_utildefines.h" - namespace blender::compositor { DespeckleOperation::DespeckleOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->flags.complex = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); + input_operation_ = nullptr; + flags_.complex = true; } -void DespeckleOperation::initExecution() +void DespeckleOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputValueOperation = this->getInputSocketReader(1); + input_operation_ = this->get_input_socket_reader(0); + input_value_operation_ = this->get_input_socket_reader(1); } -void DespeckleOperation::deinitExecution() +void DespeckleOperation::deinit_execution() { - this->m_inputOperation = nullptr; - this->m_inputValueOperation = nullptr; + input_operation_ = nullptr; + input_value_operation_ = nullptr; } BLI_INLINE int color_diff(const float a[3], const float b[3], const float threshold) @@ -51,7 +49,7 @@ BLI_INLINE int color_diff(const float a[3], const float b[3], const float thresh (fabsf(a[2] - b[2]) > threshold)); } -void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void DespeckleOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float w = 0.0f; float color_org[4]; @@ -64,17 +62,17 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da int y1 = y - 1; int y2 = y; int y3 = y + 1; - CLAMP(x1, 0, getWidth() - 1); - CLAMP(x2, 0, getWidth() - 1); - CLAMP(x3, 0, getWidth() - 1); - CLAMP(y1, 0, getHeight() - 1); - CLAMP(y2, 0, getHeight() - 1); - CLAMP(y3, 0, getHeight() - 1); + CLAMP(x1, 0, get_width() - 1); + CLAMP(x2, 0, get_width() - 1); + CLAMP(x3, 0, get_width() - 1); + CLAMP(y1, 0, get_height() - 1); + CLAMP(y2, 0, get_height() - 1); + CLAMP(y3, 0, get_height() - 1); float value[4]; - this->m_inputValueOperation->read(value, x2, y2, nullptr); + input_value_operation_->read(value, x2, y2, nullptr); // const float mval = 1.0f - value[0]; - this->m_inputOperation->read(color_org, x2, y2, nullptr); + input_operation_->read(color_org, x2, y2, nullptr); #define TOT_DIV_ONE 1.0f #define TOT_DIV_CNR (float)M_SQRT1_2 @@ -84,7 +82,7 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da #define COLOR_ADD(fac) \ { \ madd_v4_v4fl(color_mid, in1, fac); \ - if (color_diff(in1, color_org, this->m_threshold)) { \ + if (color_diff(in1, color_org, threshold_)) { \ w += fac; \ madd_v4_v4fl(color_mid_ok, in1, fac); \ } \ @@ -93,34 +91,34 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da zero_v4(color_mid); zero_v4(color_mid_ok); - this->m_inputOperation->read(in1, x1, y1, nullptr); + input_operation_->read(in1, x1, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - this->m_inputOperation->read(in1, x2, y1, nullptr); + input_operation_->read(in1, x2, y1, nullptr); COLOR_ADD(TOT_DIV_ONE) - this->m_inputOperation->read(in1, x3, y1, nullptr); + input_operation_->read(in1, x3, y1, nullptr); COLOR_ADD(TOT_DIV_CNR) - this->m_inputOperation->read(in1, x1, y2, nullptr); + input_operation_->read(in1, x1, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) #if 0 - this->m_inputOperation->read(in2, x2, y2, nullptr); - madd_v4_v4fl(color_mid, in2, this->m_filter[4]); + input_operation_->read(in2, x2, y2, nullptr); + madd_v4_v4fl(color_mid, in2, filter_[4]); #endif - this->m_inputOperation->read(in1, x3, y2, nullptr); + input_operation_->read(in1, x3, y2, nullptr); COLOR_ADD(TOT_DIV_ONE) - this->m_inputOperation->read(in1, x1, y3, nullptr); + input_operation_->read(in1, x1, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) - this->m_inputOperation->read(in1, x2, y3, nullptr); + input_operation_->read(in1, x2, y3, nullptr); COLOR_ADD(TOT_DIV_ONE) - this->m_inputOperation->read(in1, x3, y3, nullptr); + input_operation_->read(in1, x3, y3, nullptr); COLOR_ADD(TOT_DIV_CNR) mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); // mul_v4_fl(color_mid, 1.0f / w); - if ((w != 0.0f) && ((w / WTOT) > (this->m_threshold_neighbor)) && - color_diff(color_mid, color_org, this->m_threshold)) { + if ((w != 0.0f) && ((w / WTOT) > (threshold_neighbor_)) && + color_diff(color_mid, color_org, threshold_)) { mul_v4_fl(color_mid_ok, 1.0f / w); interp_v4_v4v4(output, color_org, color_mid_ok, value[0]); } @@ -134,19 +132,19 @@ void DespeckleOperation::executePixel(float output[4], int x, int y, void * /*da #undef COLOR_ADD } -bool DespeckleOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DespeckleOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; - int addx = 2; //(this->m_filterWidth - 1) / 2 + 1; - int addy = 2; //(this->m_filterHeight - 1) / 2 + 1; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; + rcti new_input; + int addx = 2; //(filter_width_ - 1) / 2 + 1; + int addy = 2; //(filter_height_ - 1) / 2 + 1; + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DespeckleOperation::get_area_of_interest(const int input_idx, @@ -155,8 +153,8 @@ void DespeckleOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - const int add_x = 2; //(this->m_filterWidth - 1) / 2 + 1; - const int add_y = 2; //(this->m_filterHeight - 1) / 2 + 1; + const int add_x = 2; //(filter_width_ - 1) / 2 + 1; + const int add_y = 2; //(filter_height_ - 1) / 2 + 1; r_input_area.xmin = output_area.xmin - add_x; r_input_area.xmax = output_area.xmax + add_x; r_input_area.ymin = output_area.ymin - add_y; @@ -175,8 +173,8 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; - const int last_x = getWidth() - 1; - const int last_y = getHeight() - 1; + const int last_x = get_width() - 1; + const int last_y = get_height() - 1; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const int x1 = MAX2(it.x - 1, 0); const int x2 = it.x; @@ -199,7 +197,7 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, #define COLOR_ADD(fac) \ { \ madd_v4_v4fl(color_mid, in1, fac); \ - if (color_diff(in1, color_org, m_threshold)) { \ + if (color_diff(in1, color_org, threshold_)) { \ w += fac; \ madd_v4_v4fl(color_mid_ok, in1, fac); \ } \ @@ -219,7 +217,7 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, #if 0 const float* in2 = image->get_elem(x2, y2); - madd_v4_v4fl(color_mid, in2, this->m_filter[4]); + madd_v4_v4fl(color_mid, in2, filter_[4]); #endif in1 = image->get_elem(x3, y2); @@ -234,8 +232,8 @@ void DespeckleOperation::update_memory_buffer_partial(MemoryBuffer *output, mul_v4_fl(color_mid, 1.0f / (4.0f + (4.0f * (float)M_SQRT1_2))); // mul_v4_fl(color_mid, 1.0f / w); - if ((w != 0.0f) && ((w / WTOT) > (m_threshold_neighbor)) && - color_diff(color_mid, color_org, m_threshold)) { + if ((w != 0.0f) && ((w / WTOT) > (threshold_neighbor_)) && + color_diff(color_mid, color_org, threshold_)) { const float factor = *it.in(FACTOR_INPUT_INDEX); mul_v4_fl(color_mid_ok, 1.0f / w); interp_v4_v4v4(it.out, color_org, color_mid_ok, factor); diff --git a/source/blender/compositor/operations/COM_DespeckleOperation.h b/source/blender/compositor/operations/COM_DespeckleOperation.h index 70d6c2227f4..bb5b012a330 100644 --- a/source/blender/compositor/operations/COM_DespeckleOperation.h +++ b/source/blender/compositor/operations/COM_DespeckleOperation.h @@ -27,34 +27,34 @@ class DespeckleOperation : public MultiThreadedOperation { constexpr static int IMAGE_INPUT_INDEX = 0; constexpr static int FACTOR_INPUT_INDEX = 1; - float m_threshold; - float m_threshold_neighbor; + float threshold_; + float threshold_neighbor_; - // int m_filterWidth; - // int m_filterHeight; + // int filter_width_; + // int filter_height_; protected: - SocketReader *m_inputOperation; - SocketReader *m_inputValueOperation; + SocketReader *input_operation_; + SocketReader *input_value_operation_; public: DespeckleOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void setThreshold(float threshold) + void set_threshold(float threshold) { - this->m_threshold = threshold; + threshold_ = threshold; } - void setThresholdNeighbor(float threshold) + void set_threshold_neighbor(float threshold) { - this->m_threshold_neighbor = threshold; + threshold_neighbor_ = threshold; } - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc index 31714b03b06..251dc4cc161 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.cc @@ -17,50 +17,49 @@ */ #include "COM_DifferenceMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { DifferenceMatteOperation::DifferenceMatteOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - this->m_inputImage1Program = nullptr; - this->m_inputImage2Program = nullptr; - flags.can_be_constant = true; + input_image1_program_ = nullptr; + input_image2_program_ = nullptr; + flags_.can_be_constant = true; } -void DifferenceMatteOperation::initExecution() +void DifferenceMatteOperation::init_execution() { - this->m_inputImage1Program = this->getInputSocketReader(0); - this->m_inputImage2Program = this->getInputSocketReader(1); + input_image1_program_ = this->get_input_socket_reader(0); + input_image2_program_ = this->get_input_socket_reader(1); } -void DifferenceMatteOperation::deinitExecution() +void DifferenceMatteOperation::deinit_execution() { - this->m_inputImage1Program = nullptr; - this->m_inputImage2Program = nullptr; + input_image1_program_ = nullptr; + input_image2_program_ = nullptr; } -void DifferenceMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DifferenceMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor1[4]; - float inColor2[4]; + float in_color1[4]; + float in_color2[4]; - const float tolerance = this->m_settings->t1; - const float falloff = this->m_settings->t2; + const float tolerance = settings_->t1; + const float falloff = settings_->t2; float difference; float alpha; - this->m_inputImage1Program->readSampled(inColor1, x, y, sampler); - this->m_inputImage2Program->readSampled(inColor2, x, y, sampler); + input_image1_program_->read_sampled(in_color1, x, y, sampler); + input_image2_program_->read_sampled(in_color2, x, y, sampler); - difference = (fabsf(inColor2[0] - inColor1[0]) + fabsf(inColor2[1] - inColor1[1]) + - fabsf(inColor2[2] - inColor1[2])); + difference = (fabsf(in_color2[0] - in_color1[0]) + fabsf(in_color2[1] - in_color1[1]) + + fabsf(in_color2[2] - in_color1[2])); /* average together the distances */ difference = difference / 3.0f; @@ -74,16 +73,16 @@ void DifferenceMatteOperation::executePixelSampled(float output[4], difference = difference - tolerance; alpha = difference / falloff; /* Only change if more transparent than before. */ - if (alpha < inColor1[3]) { + if (alpha < in_color1[3]) { output[0] = alpha; } else { /* leave as before */ - output[0] = inColor1[3]; + output[0] = in_color1[3]; } } else { /* foreground object */ - output[0] = inColor1[3]; + output[0] = in_color1[3]; } } @@ -101,8 +100,8 @@ void DifferenceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output /* Average together the distances. */ difference = difference / 3.0f; - const float tolerance = m_settings->t1; - const float falloff = m_settings->t2; + const float tolerance = settings_->t1; + const float falloff = settings_->t2; /* Make 100% transparent. */ if (difference <= tolerance) { diff --git a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h index 0a86535d946..700dabc9b8d 100644 --- a/source/blender/compositor/operations/COM_DifferenceMatteOperation.h +++ b/source/blender/compositor/operations/COM_DifferenceMatteOperation.h @@ -28,9 +28,9 @@ namespace blender::compositor { */ class DifferenceMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImage1Program; - SocketReader *m_inputImage2Program; + NodeChroma *settings_; + SocketReader *input_image1_program_; + SocketReader *input_image2_program_; public: /** @@ -41,14 +41,14 @@ class DifferenceMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - this->m_settings = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.cc b/source/blender/compositor/operations/COM_DilateErodeOperation.cc index 28b40021cd9..2abc4f71b5e 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.cc +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.cc @@ -17,79 +17,76 @@ */ #include "COM_DilateErodeOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "MEM_guardedalloc.h" - namespace blender::compositor { /* DilateErode Distance Threshold */ DilateErodeThresholdOperation::DilateErodeThresholdOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->flags.complex = true; - this->m_inputProgram = nullptr; - this->m_inset = 0.0f; - this->m__switch = 0.5f; - this->m_distance = 0.0f; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + flags_.complex = true; + input_program_ = nullptr; + inset_ = 0.0f; + switch_ = 0.5f; + distance_ = 0.0f; } void DilateErodeThresholdOperation::init_data() { - if (this->m_distance < 0.0f) { - this->m_scope = -this->m_distance + this->m_inset; + if (distance_ < 0.0f) { + scope_ = -distance_ + inset_; } else { - if (this->m_inset * 2 > this->m_distance) { - this->m_scope = MAX2(this->m_inset * 2 - this->m_distance, this->m_distance); + if (inset_ * 2 > distance_) { + scope_ = MAX2(inset_ * 2 - distance_, distance_); } else { - this->m_scope = this->m_distance; + scope_ = distance_; } } - if (this->m_scope < 3) { - this->m_scope = 3; + if (scope_ < 3) { + scope_ = 3; } } -void DilateErodeThresholdOperation::initExecution() +void DilateErodeThresholdOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void *DilateErodeThresholdOperation::initializeTileData(rcti * /*rect*/) +void *DilateErodeThresholdOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + void *buffer = input_program_->initialize_tile_data(nullptr); return buffer; } -void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, void *data) +void DilateErodeThresholdOperation::execute_pixel(float output[4], int x, int y, void *data) { - float inputValue[4]; - const float sw = this->m__switch; - const float distance = this->m_distance; + float input_value[4]; + const float sw = switch_; + const float distance = distance_; float pixelvalue; - const float rd = this->m_scope * this->m_scope; - const float inset = this->m_inset; + const float rd = scope_ * scope_; + const float inset = inset_; float mindist = rd * 2; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - this->m_scope, input_rect.xmin); - const int miny = MAX2(y - this->m_scope, input_rect.ymin); - const int maxx = MIN2(x + this->m_scope, input_rect.xmax); - const int maxy = MIN2(y + this->m_scope, input_rect.ymax); - const int bufferWidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + const rcti &input_rect = input_buffer->get_rect(); + const int minx = MAX2(x - scope_, input_rect.xmin); + const int miny = MAX2(y - scope_, input_rect.ymin); + const int maxx = MIN2(x + scope_, input_rect.xmax); + const int maxy = MIN2(y + scope_, input_rect.ymax); + const int buffer_width = input_buffer->get_width(); int offset; - inputBuffer->read(inputValue, x, y); - if (inputValue[0] > sw) { + input_buffer->read(input_value, x, y); + if (input_value[0] > sw) { for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { if (buffer[offset] < sw) { const float dx = xi - x; @@ -104,7 +101,7 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, else { for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { if (buffer[offset] > sw) { const float dx = xi - x; @@ -147,22 +144,22 @@ void DilateErodeThresholdOperation::executePixel(float output[4], int x, int y, } } -void DilateErodeThresholdOperation::deinitExecution() +void DilateErodeThresholdOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; } -bool DilateErodeThresholdOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool DilateErodeThresholdOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + this->m_scope; - newInput.xmin = input->xmin - this->m_scope; - newInput.ymax = input->ymax + this->m_scope; - newInput.ymin = input->ymin - this->m_scope; + new_input.xmax = input->xmax + scope_; + new_input.xmin = input->xmin - scope_; + new_input.ymax = input->ymax + scope_; + new_input.ymin = input->ymin - scope_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DilateErodeThresholdOperation::get_area_of_interest(const int input_idx, @@ -171,10 +168,10 @@ void DilateErodeThresholdOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_scope; - r_input_area.xmax = output_area.xmax + m_scope; - r_input_area.ymin = output_area.ymin - m_scope; - r_input_area.ymax = output_area.ymax + m_scope; + r_input_area.xmin = output_area.xmin - scope_; + r_input_area.xmax = output_area.xmax + scope_; + r_input_area.ymin = output_area.ymin - scope_; + r_input_area.ymax = output_area.ymax + scope_; } struct DilateErodeThresholdOperation::PixelData { @@ -225,21 +222,21 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o { const MemoryBuffer *input = inputs[0]; const rcti &input_rect = input->get_rect(); - const float rd = m_scope * m_scope; - const float inset = m_inset; + const float rd = scope_ * scope_; + const float inset = inset_; PixelData p; - p.sw = m__switch; + p.sw = switch_; p.distance = rd * 2; p.elem_stride = input->elem_stride; p.row_stride = input->row_stride; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { p.x = it.x; p.y = it.y; - p.xmin = MAX2(p.x - m_scope, input_rect.xmin); - p.ymin = MAX2(p.y - m_scope, input_rect.ymin); - p.xmax = MIN2(p.x + m_scope, input_rect.xmax); - p.ymax = MIN2(p.y + m_scope, input_rect.ymax); + p.xmin = MAX2(p.x - scope_, input_rect.xmin); + p.ymin = MAX2(p.y - scope_, input_rect.ymin); + p.xmax = MIN2(p.x + scope_, input_rect.xmax); + p.ymax = MIN2(p.y + scope_, input_rect.ymax); p.elem = it.in(0); float pixel_value; @@ -250,8 +247,8 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o pixel_value = sqrtf(get_min_distance(p)); } - if (m_distance > 0.0f) { - const float delta = m_distance - pixel_value; + if (distance_ > 0.0f) { + const float delta = distance_ - pixel_value; if (delta >= 0.0f) { *it.out = delta >= inset ? 1.0f : delta / inset; } @@ -260,7 +257,7 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o } } else { - const float delta = -m_distance + pixel_value; + const float delta = -distance_ + pixel_value; if (delta < 0.0f) { *it.out = delta < -inset ? 1.0f : (-delta) / inset; } @@ -274,53 +271,53 @@ void DilateErodeThresholdOperation::update_memory_buffer_partial(MemoryBuffer *o /* Dilate Distance. */ DilateDistanceOperation::DilateDistanceOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputProgram = nullptr; - this->m_distance = 0.0f; - flags.complex = true; - flags.open_cl = true; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_program_ = nullptr; + distance_ = 0.0f; + flags_.complex = true; + flags_.open_cl = true; } void DilateDistanceOperation::init_data() { - this->m_scope = this->m_distance; - if (this->m_scope < 3) { - this->m_scope = 3; + scope_ = distance_; + if (scope_ < 3) { + scope_ = 3; } } -void DilateDistanceOperation::initExecution() +void DilateDistanceOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void *DilateDistanceOperation::initializeTileData(rcti * /*rect*/) +void *DilateDistanceOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + void *buffer = input_program_->initialize_tile_data(nullptr); return buffer; } -void DilateDistanceOperation::executePixel(float output[4], int x, int y, void *data) +void DilateDistanceOperation::execute_pixel(float output[4], int x, int y, void *data) { - const float distance = this->m_distance; + const float distance = distance_; const float mindist = distance * distance; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - this->m_scope, input_rect.xmin); - const int miny = MAX2(y - this->m_scope, input_rect.ymin); - const int maxx = MIN2(x + this->m_scope, input_rect.xmax); - const int maxy = MIN2(y + this->m_scope, input_rect.ymax); - const int bufferWidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + const rcti &input_rect = input_buffer->get_rect(); + const int minx = MAX2(x - scope_, input_rect.xmin); + const int miny = MAX2(y - scope_, input_rect.ymin); + const int maxx = MIN2(x + scope_, input_rect.xmax); + const int maxy = MIN2(y + scope_, input_rect.ymax); + const int buffer_width = input_buffer->get_width(); int offset; float value = 0.0f; for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { const float dx = xi - x; const float dis = dx * dx + dy * dy; @@ -333,45 +330,46 @@ void DilateDistanceOperation::executePixel(float output[4], int x, int y, void * output[0] = value; } -void DilateDistanceOperation::deinitExecution() +void DilateDistanceOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; } -bool DilateDistanceOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DilateDistanceOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + this->m_scope; - newInput.xmin = input->xmin - this->m_scope; - newInput.ymax = input->ymax + this->m_scope; - newInput.ymin = input->ymin - this->m_scope; + new_input.xmax = input->xmax + scope_; + new_input.xmin = input->xmin - scope_; + new_input.ymax = input->ymax + scope_; + new_input.ymin = input->ymin - scope_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } -void DilateDistanceOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void DilateDistanceOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel dilateKernel = device->COM_clCreateKernel("dilateKernel", nullptr); + cl_kernel dilate_kernel = device->COM_cl_create_kernel("dilate_kernel", nullptr); - cl_int distanceSquared = this->m_distance * this->m_distance; - cl_int scope = this->m_scope; + cl_int distance_squared = distance_ * distance_; + cl_int scope = scope_; - device->COM_clAttachMemoryBufferToKernelParameter( - dilateKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); - device->COM_clAttachOutputMemoryBufferToKernelParameter(dilateKernel, 1, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(dilateKernel, 3, outputMemoryBuffer); - clSetKernelArg(dilateKernel, 4, sizeof(cl_int), &scope); - clSetKernelArg(dilateKernel, 5, sizeof(cl_int), &distanceSquared); - device->COM_clAttachSizeToKernelParameter(dilateKernel, 6, this); - device->COM_clEnqueueRange(dilateKernel, outputMemoryBuffer, 7, this); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + dilate_kernel, 0, 2, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + dilate_kernel, 1, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + dilate_kernel, 3, output_memory_buffer); + clSetKernelArg(dilate_kernel, 4, sizeof(cl_int), &scope); + clSetKernelArg(dilate_kernel, 5, sizeof(cl_int), &distance_squared); + device->COM_cl_attach_size_to_kernel_parameter(dilate_kernel, 6, this); + device->COM_cl_enqueue_range(dilate_kernel, output_memory_buffer, 7, this); } void DilateDistanceOperation::get_area_of_interest(const int input_idx, @@ -380,10 +378,10 @@ void DilateDistanceOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_scope; - r_input_area.xmax = output_area.xmax + m_scope; - r_input_area.ymin = output_area.ymin - m_scope; - r_input_area.ymax = output_area.ymax + m_scope; + r_input_area.xmin = output_area.xmin - scope_; + r_input_area.xmax = output_area.xmax + scope_; + r_input_area.ymin = output_area.ymin - scope_; + r_input_area.ymax = output_area.ymax + scope_; } struct DilateDistanceOperation::PixelData { @@ -453,7 +451,7 @@ void DilateDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - PixelData p(inputs[0], m_distance, m_scope); + PixelData p(inputs[0], distance_, scope_); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { p.update(it); *it.out = get_distance_value(p, 0.0f); @@ -466,26 +464,26 @@ ErodeDistanceOperation::ErodeDistanceOperation() : DilateDistanceOperation() /* pass */ } -void ErodeDistanceOperation::executePixel(float output[4], int x, int y, void *data) +void ErodeDistanceOperation::execute_pixel(float output[4], int x, int y, void *data) { - const float distance = this->m_distance; + const float distance = distance_; const float mindist = distance * distance; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - const rcti &input_rect = inputBuffer->get_rect(); - const int minx = MAX2(x - this->m_scope, input_rect.xmin); - const int miny = MAX2(y - this->m_scope, input_rect.ymin); - const int maxx = MIN2(x + this->m_scope, input_rect.xmax); - const int maxy = MIN2(y + this->m_scope, input_rect.ymax); - const int bufferWidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + const rcti &input_rect = input_buffer->get_rect(); + const int minx = MAX2(x - scope_, input_rect.xmin); + const int miny = MAX2(y - scope_, input_rect.ymin); + const int maxx = MIN2(x + scope_, input_rect.xmax); + const int maxy = MIN2(y + scope_, input_rect.ymax); + const int buffer_width = input_buffer->get_width(); int offset; float value = 1.0f; for (int yi = miny; yi < maxy; yi++) { const float dy = yi - y; - offset = ((yi - input_rect.ymin) * bufferWidth + (minx - input_rect.xmin)); + offset = ((yi - input_rect.ymin) * buffer_width + (minx - input_rect.xmin)); for (int xi = minx; xi < maxx; xi++) { const float dx = xi - x; const float dis = dx * dx + dy * dy; @@ -498,33 +496,35 @@ void ErodeDistanceOperation::executePixel(float output[4], int x, int y, void *d output[0] = value; } -void ErodeDistanceOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void ErodeDistanceOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel erodeKernel = device->COM_clCreateKernel("erodeKernel", nullptr); + cl_kernel erode_kernel = device->COM_cl_create_kernel("erode_kernel", nullptr); - cl_int distanceSquared = this->m_distance * this->m_distance; - cl_int scope = this->m_scope; + cl_int distance_squared = distance_ * distance_; + cl_int scope = scope_; - device->COM_clAttachMemoryBufferToKernelParameter( - erodeKernel, 0, 2, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); - device->COM_clAttachOutputMemoryBufferToKernelParameter(erodeKernel, 1, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(erodeKernel, 3, outputMemoryBuffer); - clSetKernelArg(erodeKernel, 4, sizeof(cl_int), &scope); - clSetKernelArg(erodeKernel, 5, sizeof(cl_int), &distanceSquared); - device->COM_clAttachSizeToKernelParameter(erodeKernel, 6, this); - device->COM_clEnqueueRange(erodeKernel, outputMemoryBuffer, 7, this); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + erode_kernel, 0, 2, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + erode_kernel, 1, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + erode_kernel, 3, output_memory_buffer); + clSetKernelArg(erode_kernel, 4, sizeof(cl_int), &scope); + clSetKernelArg(erode_kernel, 5, sizeof(cl_int), &distance_squared); + device->COM_cl_attach_size_to_kernel_parameter(erode_kernel, 6, this); + device->COM_cl_enqueue_range(erode_kernel, output_memory_buffer, 7, this); } void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - PixelData p(inputs[0], m_distance, m_scope); + PixelData p(inputs[0], distance_, scope_); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { p.update(it); *it.out = get_distance_value(p, 1.0f); @@ -534,17 +534,17 @@ void ErodeDistanceOperation::update_memory_buffer_partial(MemoryBuffer *output, /* Dilate step */ DilateStepOperation::DilateStepOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->flags.complex = true; - this->m_inputProgram = nullptr; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + flags_.complex = true; + input_program_ = nullptr; } -void DilateStepOperation::initExecution() +void DilateStepOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -/* Small helper to pass data from initializeTileData to executePixel. */ +/* Small helper to pass data from initialize_tile_data to execute_pixel. */ struct tile_info { rcti rect; int width; @@ -564,15 +564,15 @@ static tile_info *create_cache(int xmin, int xmax, int ymin, int ymax) return result; } -void *DilateStepOperation::initializeTileData(rcti *rect) +void *DilateStepOperation::initialize_tile_data(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputProgram->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)input_program_->initialize_tile_data(nullptr); int x, y, i; - int width = tile->getWidth(); - int height = tile->getHeight(); - float *buffer = tile->getBuffer(); + int width = tile->get_width(); + int height = tile->get_height(); + float *buffer = tile->get_buffer(); - int half_window = this->m_iterations; + int half_window = iterations_; int window = half_window * 2 + 1; int xmin = MAX2(0, rect->xmin - half_window); @@ -654,7 +654,7 @@ void *DilateStepOperation::initializeTileData(rcti *rect) return result; } -void DilateStepOperation::executePixel(float output[4], int x, int y, void *data) +void DilateStepOperation::execute_pixel(float output[4], int x, int y, void *data) { tile_info *tile = (tile_info *)data; int nx = x - tile->rect.xmin; @@ -662,30 +662,30 @@ void DilateStepOperation::executePixel(float output[4], int x, int y, void *data output[0] = tile->buffer[tile->width * ny + nx]; } -void DilateStepOperation::deinitExecution() +void DilateStepOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; } -void DilateStepOperation::deinitializeTileData(rcti * /*rect*/, void *data) +void DilateStepOperation::deinitialize_tile_data(rcti * /*rect*/, void *data) { tile_info *tile = (tile_info *)data; MEM_freeN(tile->buffer); MEM_freeN(tile); } -bool DilateStepOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DilateStepOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; - int it = this->m_iterations; - newInput.xmax = input->xmax + it; - newInput.xmin = input->xmin - it; - newInput.ymax = input->ymax + it; - newInput.ymin = input->ymin - it; + rcti new_input; + int it = iterations_; + new_input.xmax = input->xmax + it; + new_input.xmin = input->xmin - it; + new_input.ymax = input->ymax + it; + new_input.ymin = input->ymin - it; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DilateStepOperation::get_area_of_interest(const int input_idx, @@ -694,10 +694,10 @@ void DilateStepOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_iterations; - r_input_area.xmax = output_area.xmax + m_iterations; - r_input_area.ymin = output_area.ymin - m_iterations; - r_input_area.ymax = output_area.ymax + m_iterations; + r_input_area.xmin = output_area.xmin - iterations_; + r_input_area.xmax = output_area.xmax + iterations_; + r_input_area.ymin = output_area.ymin - iterations_; + r_input_area.ymax = output_area.ymax + iterations_; } template @@ -709,8 +709,8 @@ static void step_update_memory_buffer(MemoryBuffer *output, { TCompareSelector selector; - const int width = output->getWidth(); - const int height = output->getHeight(); + const int width = output->get_width(); + const int height = output->get_height(); const int half_window = num_iterations; const int window = half_window * 2 + 1; @@ -783,7 +783,8 @@ static void step_update_memory_buffer(MemoryBuffer *output, start = half_window + (i - 1) * window + 1; for (int y = -MIN2(0, start); y < window - MAX2(0, start + window - bheight); y++) { - result.get_value(x, y + start + area.ymin, 0) = selector(temp[y], temp[y + window - 1]); + result.get_value(x + area.xmin, y + start + area.ymin, 0) = selector(temp[y], + temp[y + window - 1]); } } } @@ -805,7 +806,7 @@ void DilateStepOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - step_update_memory_buffer(output, inputs[0], area, m_iterations, -FLT_MAX); + step_update_memory_buffer(output, inputs[0], area, iterations_, -FLT_MAX); } /* Erode step */ @@ -814,15 +815,15 @@ ErodeStepOperation::ErodeStepOperation() : DilateStepOperation() /* pass */ } -void *ErodeStepOperation::initializeTileData(rcti *rect) +void *ErodeStepOperation::initialize_tile_data(rcti *rect) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputProgram->initializeTileData(nullptr); + MemoryBuffer *tile = (MemoryBuffer *)input_program_->initialize_tile_data(nullptr); int x, y, i; - int width = tile->getWidth(); - int height = tile->getHeight(); - float *buffer = tile->getBuffer(); + int width = tile->get_width(); + int height = tile->get_height(); + float *buffer = tile->get_buffer(); - int half_window = this->m_iterations; + int half_window = iterations_; int window = half_window * 2 + 1; int xmin = MAX2(0, rect->xmin - half_window); @@ -915,7 +916,7 @@ void ErodeStepOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - step_update_memory_buffer(output, inputs[0], area, m_iterations, FLT_MAX); + step_update_memory_buffer(output, inputs[0], area, iterations_, FLT_MAX); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DilateErodeOperation.h b/source/blender/compositor/operations/COM_DilateErodeOperation.h index 9c32a5ac1fd..04d25a1fca6 100644 --- a/source/blender/compositor/operations/COM_DilateErodeOperation.h +++ b/source/blender/compositor/operations/COM_DilateErodeOperation.h @@ -28,19 +28,19 @@ class DilateErodeThresholdOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; - float m_distance; - float m__switch; - float m_inset; + float distance_; + float switch_; + float inset_; /** * determines the area of interest to track pixels * keep this one as small as possible for speed gain. */ - int m_scope; + int scope_; public: DilateErodeThresholdOperation(); @@ -48,36 +48,36 @@ class DilateErodeThresholdOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setDistance(float distance) + void set_distance(float distance) { - this->m_distance = distance; + distance_ = distance; } - void setSwitch(float sw) + void set_switch(float sw) { - this->m__switch = sw; + switch_ = sw; } - void setInset(float inset) + void set_inset(float inset) { - this->m_inset = inset; + inset_ = inset; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, @@ -91,11 +91,11 @@ class DilateDistanceOperation : public MultiThreadedOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - float m_distance; - int m_scope; + SocketReader *input_program_; + float distance_; + int scope_; public: DilateDistanceOperation(); @@ -103,34 +103,34 @@ class DilateDistanceOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setDistance(float distance) + void set_distance(float distance) { - this->m_distance = distance; + distance_ = distance; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; virtual void update_memory_buffer_partial(MemoryBuffer *output, @@ -145,14 +145,14 @@ class ErodeDistanceOperation : public DilateDistanceOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -162,11 +162,11 @@ class ErodeDistanceOperation : public DilateDistanceOperation { class DilateStepOperation : public MultiThreadedOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; - int m_iterations; + int iterations_; public: DilateStepOperation(); @@ -174,28 +174,28 @@ class DilateStepOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; - void deinitializeTileData(rcti *rect, void *data) override; + void deinit_execution() override; + void deinitialize_tile_data(rcti *rect, void *data) override; - void setIterations(int iterations) + void set_iterations(int iterations) { - this->m_iterations = iterations; + iterations_ = iterations; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) final; virtual void update_memory_buffer_partial(MemoryBuffer *output, @@ -207,7 +207,7 @@ class ErodeStepOperation : public DilateStepOperation { public: ErodeStepOperation(); - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc index 102025ed915..1104ef6f71f 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.cc @@ -19,131 +19,127 @@ #include "COM_DirectionalBlurOperation.h" #include "COM_OpenCLDevice.h" -#include "BLI_math.h" - -#include "RE_pipeline.h" - namespace blender::compositor { DirectionalBlurOperation::DirectionalBlurOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - flags.complex = true; - flags.open_cl = true; - this->m_inputProgram = nullptr; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + flags_.complex = true; + flags_.open_cl = true; + input_program_ = nullptr; } -void DirectionalBlurOperation::initExecution() +void DirectionalBlurOperation::init_execution() { - this->m_inputProgram = getInputSocketReader(0); - QualityStepHelper::initExecution(COM_QH_INCREASE); - const float angle = this->m_data->angle; - const float zoom = this->m_data->zoom; - const float spin = this->m_data->spin; - const float iterations = this->m_data->iter; - const float distance = this->m_data->distance; - const float center_x = this->m_data->center_x; - const float center_y = this->m_data->center_y; - const float width = getWidth(); - const float height = getHeight(); + input_program_ = get_input_socket_reader(0); + QualityStepHelper::init_execution(COM_QH_INCREASE); + const float angle = data_->angle; + const float zoom = data_->zoom; + const float spin = data_->spin; + const float iterations = data_->iter; + const float distance = data_->distance; + const float center_x = data_->center_x; + const float center_y = data_->center_y; + const float width = get_width(); + const float height = get_height(); const float a = angle; const float itsc = 1.0f / powf(2.0f, (float)iterations); float D; D = distance * sqrtf(width * width + height * height); - this->m_center_x_pix = center_x * width; - this->m_center_y_pix = center_y * height; + center_x_pix_ = center_x * width; + center_y_pix_ = center_y * height; - this->m_tx = itsc * D * cosf(a); - this->m_ty = -itsc * D * sinf(a); - this->m_sc = itsc * zoom; - this->m_rot = itsc * spin; + tx_ = itsc * D * cosf(a); + ty_ = -itsc * D * sinf(a); + sc_ = itsc * zoom; + rot_ = itsc * spin; } -void DirectionalBlurOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void DirectionalBlurOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { - const int iterations = pow(2.0f, this->m_data->iter); + const int iterations = pow(2.0f, data_->iter); float col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float col2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - this->m_inputProgram->readSampled(col2, x, y, PixelSampler::Bilinear); - float ltx = this->m_tx; - float lty = this->m_ty; - float lsc = this->m_sc; - float lrot = this->m_rot; + input_program_->read_sampled(col2, x, y, PixelSampler::Bilinear); + float ltx = tx_; + float lty = ty_; + float lsc = sc_; + float lrot = rot_; /* blur the image */ for (int i = 0; i < iterations; i++) { const float cs = cosf(lrot), ss = sinf(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (y - this->m_center_y_pix) + lty; - const float u = isc * (x - this->m_center_x_pix) + ltx; + const float v = isc * (y - center_y_pix_) + lty; + const float u = isc * (x - center_x_pix_) + ltx; - this->m_inputProgram->readSampled(col, - cs * u + ss * v + this->m_center_x_pix, - cs * v - ss * u + this->m_center_y_pix, - PixelSampler::Bilinear); + input_program_->read_sampled(col, + cs * u + ss * v + center_x_pix_, + cs * v - ss * u + center_y_pix_, + PixelSampler::Bilinear); add_v4_v4(col2, col); /* double transformations */ - ltx += this->m_tx; - lty += this->m_ty; - lrot += this->m_rot; - lsc += this->m_sc; + ltx += tx_; + lty += ty_; + lrot += rot_; + lsc += sc_; } mul_v4_v4fl(output, col2, 1.0f / (iterations + 1)); } -void DirectionalBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void DirectionalBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel directionalBlurKernel = device->COM_clCreateKernel("directionalBlurKernel", nullptr); + cl_kernel directional_blur_kernel = device->COM_cl_create_kernel("directional_blur_kernel", + nullptr); - cl_int iterations = pow(2.0f, this->m_data->iter); - cl_float2 ltxy = {{this->m_tx, this->m_ty}}; - cl_float2 centerpix = {{this->m_center_x_pix, this->m_center_y_pix}}; - cl_float lsc = this->m_sc; - cl_float lrot = this->m_rot; + cl_int iterations = pow(2.0f, data_->iter); + cl_float2 ltxy = {{tx_, ty_}}; + cl_float2 centerpix = {{center_x_pix_, center_y_pix_}}; + cl_float lsc = sc_; + cl_float lrot = rot_; - device->COM_clAttachMemoryBufferToKernelParameter( - directionalBlurKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); - device->COM_clAttachOutputMemoryBufferToKernelParameter( - directionalBlurKernel, 1, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter( - directionalBlurKernel, 2, outputMemoryBuffer); - clSetKernelArg(directionalBlurKernel, 3, sizeof(cl_int), &iterations); - clSetKernelArg(directionalBlurKernel, 4, sizeof(cl_float), &lsc); - clSetKernelArg(directionalBlurKernel, 5, sizeof(cl_float), &lrot); - clSetKernelArg(directionalBlurKernel, 6, sizeof(cl_float2), <xy); - clSetKernelArg(directionalBlurKernel, 7, sizeof(cl_float2), ¢erpix); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + directional_blur_kernel, 0, -1, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + directional_blur_kernel, 1, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + directional_blur_kernel, 2, output_memory_buffer); + clSetKernelArg(directional_blur_kernel, 3, sizeof(cl_int), &iterations); + clSetKernelArg(directional_blur_kernel, 4, sizeof(cl_float), &lsc); + clSetKernelArg(directional_blur_kernel, 5, sizeof(cl_float), &lrot); + clSetKernelArg(directional_blur_kernel, 6, sizeof(cl_float2), <xy); + clSetKernelArg(directional_blur_kernel, 7, sizeof(cl_float2), ¢erpix); - device->COM_clEnqueueRange(directionalBlurKernel, outputMemoryBuffer, 8, this); + device->COM_cl_enqueue_range(directional_blur_kernel, output_memory_buffer, 8, this); } -void DirectionalBlurOperation::deinitExecution() +void DirectionalBlurOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; } -bool DirectionalBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool DirectionalBlurOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void DirectionalBlurOperation::get_area_of_interest(const int input_idx, @@ -152,10 +148,7 @@ void DirectionalBlurOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area = this->get_canvas(); } void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -163,7 +156,7 @@ void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { const MemoryBuffer *input = inputs[0]; - const int iterations = pow(2.0f, this->m_data->iter); + const int iterations = pow(2.0f, data_->iter); for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const int x = it.x; const int y = it.y; @@ -173,27 +166,27 @@ void DirectionalBlurOperation::update_memory_buffer_partial(MemoryBuffer *output /* Blur pixel. */ /* TODO(manzanilla): Many values used on iterations can be calculated beforehand. Create a * table on operation initialization. */ - float ltx = this->m_tx; - float lty = this->m_ty; - float lsc = this->m_sc; - float lrot = this->m_rot; + float ltx = tx_; + float lty = ty_; + float lsc = sc_; + float lrot = rot_; for (int i = 0; i < iterations; i++) { const float cs = cosf(lrot), ss = sinf(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (y - this->m_center_y_pix) + lty; - const float u = isc * (x - this->m_center_x_pix) + ltx; + const float v = isc * (y - center_y_pix_) + lty; + const float u = isc * (x - center_x_pix_) + ltx; float color[4]; input->read_elem_bilinear( - cs * u + ss * v + this->m_center_x_pix, cs * v - ss * u + this->m_center_y_pix, color); + cs * u + ss * v + center_x_pix_, cs * v - ss * u + center_y_pix_, color); add_v4_v4(color_accum, color); /* Double transformations. */ - ltx += this->m_tx; - lty += this->m_ty; - lrot += this->m_rot; - lsc += this->m_sc; + ltx += tx_; + lty += ty_; + lrot += rot_; + lsc += sc_; } mul_v4_v4fl(it.out, color_accum, 1.0f / (iterations + 1)); diff --git a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h index 9a982bf6481..2dd2fc98ccd 100644 --- a/source/blender/compositor/operations/COM_DirectionalBlurOperation.h +++ b/source/blender/compositor/operations/COM_DirectionalBlurOperation.h @@ -25,12 +25,12 @@ namespace blender::compositor { class DirectionalBlurOperation : public MultiThreadedOperation, public QualityStepHelper { private: - SocketReader *m_inputProgram; - NodeDBlurData *m_data; + SocketReader *input_program_; + NodeDBlurData *data_; - float m_center_x_pix, m_center_y_pix; - float m_tx, m_ty; - float m_sc, m_rot; + float center_x_pix_, center_y_pix_; + float tx_, ty_; + float sc_, rot_; public: DirectionalBlurOperation(); @@ -38,33 +38,33 @@ class DirectionalBlurOperation : public MultiThreadedOperation, public QualitySt /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setData(NodeDBlurData *data) + void set_data(NodeDBlurData *data) { - this->m_data = data; + data_ = data; } - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.cc b/source/blender/compositor/operations/COM_DisplaceOperation.cc index a4c01fda7ca..4742692b75d 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceOperation.cc @@ -17,62 +17,60 @@ */ #include "COM_DisplaceOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" namespace blender::compositor { DisplaceOperation::DisplaceOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->flags.complex = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + flags_.complex = true; - this->m_inputColorProgram = nullptr; + input_color_program_ = nullptr; } -void DisplaceOperation::initExecution() +void DisplaceOperation::init_execution() { - this->m_inputColorProgram = this->getInputSocketReader(0); - NodeOperation *vector = this->getInputSocketReader(1); - NodeOperation *scale_x = this->getInputSocketReader(2); - NodeOperation *scale_y = this->getInputSocketReader(3); + input_color_program_ = this->get_input_socket_reader(0); + NodeOperation *vector = this->get_input_socket_reader(1); + NodeOperation *scale_x = this->get_input_socket_reader(2); + NodeOperation *scale_y = this->get_input_socket_reader(3); if (execution_model_ == eExecutionModel::Tiled) { vector_read_fn_ = [=](float x, float y, float *out) { - vector->readSampled(out, x, y, PixelSampler::Bilinear); + vector->read_sampled(out, x, y, PixelSampler::Bilinear); }; scale_x_read_fn_ = [=](float x, float y, float *out) { - scale_x->readSampled(out, x, y, PixelSampler::Nearest); + scale_x->read_sampled(out, x, y, PixelSampler::Nearest); }; scale_y_read_fn_ = [=](float x, float y, float *out) { - scale_y->readSampled(out, x, y, PixelSampler::Nearest); + scale_y->read_sampled(out, x, y, PixelSampler::Nearest); }; } - this->m_width_x4 = this->getWidth() * 4; - this->m_height_x4 = this->getHeight() * 4; - input_vector_width_ = vector->getWidth(); - input_vector_height_ = vector->getHeight(); + width_x4_ = this->get_width() * 4; + height_x4_ = this->get_height() * 4; + input_vector_width_ = vector->get_width(); + input_vector_height_ = vector->get_height(); } -void DisplaceOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void DisplaceOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float xy[2] = {x, y}; float uv[2], deriv[2][2]; - pixelTransform(xy, uv, deriv); + pixel_transform(xy, uv, deriv); if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { - this->m_inputColorProgram->readSampled(output, uv[0], uv[1], PixelSampler::Bilinear); + input_color_program_->read_sampled(output, uv[0], uv[1], PixelSampler::Bilinear); } else { /* EWA filtering (without nearest it gets blurry with NO distortion) */ - this->m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + input_color_program_->read_filtered(output, uv[0], uv[1], deriv[0], deriv[1]); } } @@ -94,7 +92,7 @@ bool DisplaceOperation::read_displacement( return true; } -void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2]) +void DisplaceOperation::pixel_transform(const float xy[2], float r_uv[2], float r_deriv[2][2]) { float col[4]; float uv[2]; /* temporary variables for derivative estimation */ @@ -106,8 +104,8 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r float ys = col[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ - CLAMP(xs, -m_width_x4, m_width_x4); - CLAMP(ys, -m_height_x4, m_height_x4); + CLAMP(xs, -width_x4_, width_x4_); + CLAMP(ys, -height_x4_, height_x4_); /* displaced pixel in uv coords, for image sampling */ read_displacement(xy[0], xy[1], xs, ys, xy, r_uv[0], r_uv[1]); @@ -153,52 +151,52 @@ void DisplaceOperation::pixelTransform(const float xy[2], float r_uv[2], float r } } -void DisplaceOperation::deinitExecution() +void DisplaceOperation::deinit_execution() { - this->m_inputColorProgram = nullptr; + input_color_program_ = nullptr; vector_read_fn_ = nullptr; scale_x_read_fn_ = nullptr; scale_y_read_fn_ = nullptr; } -bool DisplaceOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DisplaceOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti colorInput; - rcti vectorInput; + rcti color_input; + rcti vector_input; NodeOperation *operation = nullptr; /* the vector buffer only needs a 2x2 buffer. The image needs whole buffer */ /* image */ - operation = getInputOperation(0); - colorInput.xmax = operation->getWidth(); - colorInput.xmin = 0; - colorInput.ymax = operation->getHeight(); - colorInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&colorInput, readOperation, output)) { + operation = get_input_operation(0); + color_input.xmax = operation->get_width(); + color_input.xmin = 0; + color_input.ymax = operation->get_height(); + color_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&color_input, read_operation, output)) { return true; } /* vector */ - operation = getInputOperation(1); - vectorInput.xmax = input->xmax + 1; - vectorInput.xmin = input->xmin - 1; - vectorInput.ymax = input->ymax + 1; - vectorInput.ymin = input->ymin - 1; - if (operation->determineDependingAreaOfInterest(&vectorInput, readOperation, output)) { + operation = get_input_operation(1); + vector_input.xmax = input->xmax + 1; + vector_input.xmin = input->xmin - 1; + vector_input.ymax = input->ymax + 1; + vector_input.ymin = input->ymin - 1; + if (operation->determine_depending_area_of_interest(&vector_input, read_operation, output)) { return true; } /* scale x */ - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } /* scale y */ - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } @@ -211,10 +209,7 @@ void DisplaceOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case 0: { - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = getInputOperation(input_idx)->getWidth(); - r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + r_input_area = get_input_operation(input_idx)->get_canvas(); break; } case 1: { @@ -251,7 +246,7 @@ void DisplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, float uv[2]; float deriv[2][2]; - pixelTransform(xy, uv, deriv); + pixel_transform(xy, uv, deriv); if (is_zero_v2(deriv[0]) && is_zero_v2(deriv[1])) { input_color->read_elem_bilinear(uv[0], uv[1], it.out); } diff --git a/source/blender/compositor/operations/COM_DisplaceOperation.h b/source/blender/compositor/operations/COM_DisplaceOperation.h index 5be914ab672..a6f6927407f 100644 --- a/source/blender/compositor/operations/COM_DisplaceOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceOperation.h @@ -25,12 +25,12 @@ namespace blender::compositor { class DisplaceOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputColorProgram; + SocketReader *input_color_program_; - float m_width_x4; - float m_height_x4; + float width_x4_; + float height_x4_; int input_vector_width_; int input_vector_height_; @@ -45,26 +45,26 @@ class DisplaceOperation : public MultiThreadedOperation { /** * we need a 2x2 differential filter for Vector Input and full buffer for the image */ - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2]); + void pixel_transform(const float xy[2], float r_uv[2], float r_deriv[2][2]); /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc index e1c531bd49e..729b01d924d 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.cc @@ -17,115 +17,112 @@ */ #include "COM_DisplaceSimpleOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" namespace blender::compositor { DisplaceSimpleOperation::DisplaceSimpleOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; + input_color_program_ = nullptr; + input_vector_program_ = nullptr; + input_scale_xprogram_ = nullptr; + input_scale_yprogram_ = nullptr; } -void DisplaceSimpleOperation::initExecution() +void DisplaceSimpleOperation::init_execution() { - this->m_inputColorProgram = this->getInputSocketReader(0); - this->m_inputVectorProgram = this->getInputSocketReader(1); - this->m_inputScaleXProgram = this->getInputSocketReader(2); - this->m_inputScaleYProgram = this->getInputSocketReader(3); + input_color_program_ = this->get_input_socket_reader(0); + input_vector_program_ = this->get_input_socket_reader(1); + input_scale_xprogram_ = this->get_input_socket_reader(2); + input_scale_yprogram_ = this->get_input_socket_reader(3); - this->m_width_x4 = this->getWidth() * 4; - this->m_height_x4 = this->getHeight() * 4; + width_x4_ = this->get_width() * 4; + height_x4_ = this->get_height() * 4; } /* minimum distance (in pixels) a pixel has to be displaced * in order to take effect */ // #define DISPLACE_EPSILON 0.01f -void DisplaceSimpleOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DisplaceSimpleOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inVector[4]; - float inScale[4]; + float in_vector[4]; + float in_scale[4]; float p_dx, p_dy; /* main displacement in pixel space */ float u, v; - this->m_inputScaleXProgram->readSampled(inScale, x, y, sampler); - float xs = inScale[0]; - this->m_inputScaleYProgram->readSampled(inScale, x, y, sampler); - float ys = inScale[0]; + input_scale_xprogram_->read_sampled(in_scale, x, y, sampler); + float xs = in_scale[0]; + input_scale_yprogram_->read_sampled(in_scale, x, y, sampler); + float ys = in_scale[0]; /* clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers */ - CLAMP(xs, -this->m_width_x4, this->m_width_x4); - CLAMP(ys, -this->m_height_x4, this->m_height_x4); + CLAMP(xs, -width_x4_, width_x4_); + CLAMP(ys, -height_x4_, height_x4_); - this->m_inputVectorProgram->readSampled(inVector, x, y, sampler); - p_dx = inVector[0] * xs; - p_dy = inVector[1] * ys; + input_vector_program_->read_sampled(in_vector, x, y, sampler); + p_dx = in_vector[0] * xs; + p_dy = in_vector[1] * ys; /* displaced pixel in uv coords, for image sampling */ /* clamp nodes to avoid glitches */ u = x - p_dx + 0.5f; v = y - p_dy + 0.5f; - CLAMP(u, 0.0f, this->getWidth() - 1.0f); - CLAMP(v, 0.0f, this->getHeight() - 1.0f); + CLAMP(u, 0.0f, this->get_width() - 1.0f); + CLAMP(v, 0.0f, this->get_height() - 1.0f); - this->m_inputColorProgram->readSampled(output, u, v, sampler); + input_color_program_->read_sampled(output, u, v, sampler); } -void DisplaceSimpleOperation::deinitExecution() +void DisplaceSimpleOperation::deinit_execution() { - this->m_inputColorProgram = nullptr; - this->m_inputVectorProgram = nullptr; - this->m_inputScaleXProgram = nullptr; - this->m_inputScaleYProgram = nullptr; + input_color_program_ = nullptr; + input_vector_program_ = nullptr; + input_scale_xprogram_ = nullptr; + input_scale_yprogram_ = nullptr; } -bool DisplaceSimpleOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool DisplaceSimpleOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti colorInput; + rcti color_input; NodeOperation *operation = nullptr; /* the vector buffer only needs a 2x2 buffer. The image needs whole buffer */ /* image */ - operation = getInputOperation(0); - colorInput.xmax = operation->getWidth(); - colorInput.xmin = 0; - colorInput.ymax = operation->getHeight(); - colorInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&colorInput, readOperation, output)) { + operation = get_input_operation(0); + color_input.xmax = operation->get_width(); + color_input.xmin = 0; + color_input.ymax = operation->get_height(); + color_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&color_input, read_operation, output)) { return true; } /* vector */ - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } /* scale x */ - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } /* scale y */ - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } @@ -138,10 +135,7 @@ void DisplaceSimpleOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case 0: { - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = getInputOperation(input_idx)->getWidth(); - r_input_area.ymax = getInputOperation(input_idx)->getHeight(); + r_input_area = get_input_operation(input_idx)->get_canvas(); break; } default: { @@ -155,8 +149,8 @@ void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); const MemoryBuffer *input_color = inputs[0]; for (BuffersIterator it = output->iterate_with(inputs.drop_front(1), area); !it.is_end(); ++it) { @@ -165,8 +159,8 @@ void DisplaceSimpleOperation::update_memory_buffer_partial(MemoryBuffer *output, /* Clamp x and y displacement to triple image resolution - * to prevent hangs from huge values mistakenly plugged in eg. z buffers. */ - CLAMP(scale_x, -m_width_x4, m_width_x4); - CLAMP(scale_y, -m_height_x4, m_height_x4); + CLAMP(scale_x, -width_x4_, width_x4_); + CLAMP(scale_y, -height_x4_, height_x4_); /* Main displacement in pixel space. */ const float *vector = it.in(0); diff --git a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h index 99f52155466..c16146dc464 100644 --- a/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h +++ b/source/blender/compositor/operations/COM_DisplaceSimpleOperation.h @@ -25,15 +25,15 @@ namespace blender::compositor { class DisplaceSimpleOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputColorProgram; - SocketReader *m_inputVectorProgram; - SocketReader *m_inputScaleXProgram; - SocketReader *m_inputScaleYProgram; + SocketReader *input_color_program_; + SocketReader *input_vector_program_; + SocketReader *input_scale_xprogram_; + SocketReader *input_scale_yprogram_; - float m_width_x4; - float m_height_x4; + float width_x4_; + float height_x4_; public: DisplaceSimpleOperation(); @@ -41,24 +41,24 @@ class DisplaceSimpleOperation : public MultiThreadedOperation { /** * we need a full buffer for the image */ - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc index 8155ff769a0..7618e318c77 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.cc @@ -17,56 +17,55 @@ */ #include "COM_DistanceRGBMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { DistanceRGBMatteOperation::DistanceRGBMatteOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; - flags.can_be_constant = true; + input_image_program_ = nullptr; + input_key_program_ = nullptr; + flags_.can_be_constant = true; } -void DistanceRGBMatteOperation::initExecution() +void DistanceRGBMatteOperation::init_execution() { - this->m_inputImageProgram = this->getInputSocketReader(0); - this->m_inputKeyProgram = this->getInputSocketReader(1); + input_image_program_ = this->get_input_socket_reader(0); + input_key_program_ = this->get_input_socket_reader(1); } -void DistanceRGBMatteOperation::deinitExecution() +void DistanceRGBMatteOperation::deinit_execution() { - this->m_inputImageProgram = nullptr; - this->m_inputKeyProgram = nullptr; + input_image_program_ = nullptr; + input_key_program_ = nullptr; } -float DistanceRGBMatteOperation::calculateDistance(const float key[4], const float image[4]) +float DistanceRGBMatteOperation::calculate_distance(const float key[4], const float image[4]) { return len_v3v3(key, image); } -void DistanceRGBMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DistanceRGBMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inKey[4]; - float inImage[4]; + float in_key[4]; + float in_image[4]; - const float tolerance = this->m_settings->t1; - const float falloff = this->m_settings->t2; + const float tolerance = settings_->t1; + const float falloff = settings_->t2; float distance; float alpha; - this->m_inputKeyProgram->readSampled(inKey, x, y, sampler); - this->m_inputImageProgram->readSampled(inImage, x, y, sampler); + input_key_program_->read_sampled(in_key, x, y, sampler); + input_image_program_->read_sampled(in_image, x, y, sampler); - distance = this->calculateDistance(inKey, inImage); + distance = this->calculate_distance(in_key, in_image); /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. @@ -81,16 +80,16 @@ void DistanceRGBMatteOperation::executePixelSampled(float output[4], distance = distance - tolerance; alpha = distance / falloff; /* Only change if more transparent than before. */ - if (alpha < inImage[3]) { + if (alpha < in_image[3]) { output[0] = alpha; } else { /* leave as before */ - output[0] = inImage[3]; + output[0] = in_image[3]; } } else { /* leave as before */ - output[0] = inImage[3]; + output[0] = in_image[3]; } } @@ -102,9 +101,9 @@ void DistanceRGBMatteOperation::update_memory_buffer_partial(MemoryBuffer *outpu const float *in_image = it.in(0); const float *in_key = it.in(1); - float distance = this->calculateDistance(in_key, in_image); - const float tolerance = this->m_settings->t1; - const float falloff = this->m_settings->t2; + float distance = this->calculate_distance(in_key, in_image); + const float tolerance = settings_->t1; + const float falloff = settings_->t2; /* Store matte(alpha) value in [0] to go with * COM_SetAlphaMultiplyOperation and the Value output. diff --git a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h index ba6682214ae..306a4e316f5 100644 --- a/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceRGBMatteOperation.h @@ -28,11 +28,11 @@ namespace blender::compositor { */ class DistanceRGBMatteOperation : public MultiThreadedOperation { protected: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; - SocketReader *m_inputKeyProgram; + NodeChroma *settings_; + SocketReader *input_image_program_; + SocketReader *input_key_program_; - virtual float calculateDistance(const float key[4], const float image[4]); + virtual float calculate_distance(const float key[4], const float image[4]); public: /** @@ -43,14 +43,14 @@ class DistanceRGBMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - this->m_settings = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc index 50e473ea5b3..682c01a63d5 100644 --- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc +++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.cc @@ -17,11 +17,10 @@ */ #include "COM_DistanceYCCMatteOperation.h" -#include "BLI_math.h" namespace blender::compositor { -float DistanceYCCMatteOperation::calculateDistance(const float key[4], const float image[4]) +float DistanceYCCMatteOperation::calculate_distance(const float key[4], const float image[4]) { /* only measure the second 2 values */ return len_v2v2(key + 1, image + 1); diff --git a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h index 0e178fddc39..1d5ed9f0de1 100644 --- a/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h +++ b/source/blender/compositor/operations/COM_DistanceYCCMatteOperation.h @@ -29,7 +29,7 @@ namespace blender::compositor { */ class DistanceYCCMatteOperation : public DistanceRGBMatteOperation { protected: - float calculateDistance(const float key[4], const float image[4]) override; + float calculate_distance(const float key[4], const float image[4]) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_DotproductOperation.cc b/source/blender/compositor/operations/COM_DotproductOperation.cc index 875b161e208..ce1432547ae 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.cc +++ b/source/blender/compositor/operations/COM_DotproductOperation.cc @@ -22,37 +22,37 @@ namespace blender::compositor { DotproductOperation::DotproductOperation() { - this->addInputSocket(DataType::Vector); - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Value); - this->setResolutionInputSocketIndex(0); - this->m_input1Operation = nullptr; - this->m_input2Operation = nullptr; - flags.can_be_constant = true; + this->add_input_socket(DataType::Vector); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Value); + this->set_canvas_input_index(0); + input1Operation_ = nullptr; + input2Operation_ = nullptr; + flags_.can_be_constant = true; } -void DotproductOperation::initExecution() +void DotproductOperation::init_execution() { - this->m_input1Operation = this->getInputSocketReader(0); - this->m_input2Operation = this->getInputSocketReader(1); + input1Operation_ = this->get_input_socket_reader(0); + input2Operation_ = this->get_input_socket_reader(1); } -void DotproductOperation::deinitExecution() +void DotproductOperation::deinit_execution() { - this->m_input1Operation = nullptr; - this->m_input2Operation = nullptr; + input1Operation_ = nullptr; + input2Operation_ = nullptr; } /** \todo current implementation is the inverse of a dot-product. not 'logically' correct */ -void DotproductOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void DotproductOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input1[4]; float input2[4]; - this->m_input1Operation->readSampled(input1, x, y, sampler); - this->m_input2Operation->readSampled(input2, x, y, sampler); + input1Operation_->read_sampled(input1, x, y, sampler); + input2Operation_->read_sampled(input2, x, y, sampler); output[0] = -(input1[0] * input2[0] + input1[1] * input2[1] + input1[2] * input2[2]); } diff --git a/source/blender/compositor/operations/COM_DotproductOperation.h b/source/blender/compositor/operations/COM_DotproductOperation.h index c3f39d43fff..f9e5d4d39c4 100644 --- a/source/blender/compositor/operations/COM_DotproductOperation.h +++ b/source/blender/compositor/operations/COM_DotproductOperation.h @@ -24,15 +24,15 @@ namespace blender::compositor { class DotproductOperation : public MultiThreadedOperation { private: - SocketReader *m_input1Operation; - SocketReader *m_input2Operation; + SocketReader *input1Operation_; + SocketReader *input2Operation_; public: DotproductOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc index 55ae19ad194..22899bd396a 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.cc @@ -18,10 +18,7 @@ #include -#include "BLI_math.h" #include "COM_DoubleEdgeMaskOperation.h" -#include "DNA_node_types.h" -#include "MEM_guardedalloc.h" namespace blender::compositor { @@ -948,8 +945,8 @@ static void do_createEdgeLocationBuffer(unsigned int t, const unsigned int *lres, float *res, unsigned short *gbuf, - unsigned int *innerEdgeOffset, - unsigned int *outerEdgeOffset, + unsigned int *inner_edge_offset, + unsigned int *outer_edge_offset, unsigned int isz, unsigned int gsz) { @@ -959,14 +956,14 @@ static void do_createEdgeLocationBuffer(unsigned int t, unsigned int dmin; /* Minimum edge distance. */ unsigned int rsl; /* Long used for finding fast `1.0/sqrt`. */ - unsigned int gradientFillOffset; + unsigned int gradient_fill_offset; /* For looping inner edge pixel indexes, represents current position from offset. */ - unsigned int innerAccum = 0; + unsigned int inner_accum = 0; /* For looping outer edge pixel indexes, represents current position from offset. */ - unsigned int outerAccum = 0; + unsigned int outer_accum = 0; /* For looping gradient pixel indexes, represents current position from offset. */ - unsigned int gradientAccum = 0; + unsigned int gradient_accum = 0; /* Disable clang-format to prevent line-wrapping. */ /* clang-format off */ @@ -999,7 +996,7 @@ static void do_createEdgeLocationBuffer(unsigned int t, * ..oooo... * .oggggo.. * .oggiggo. - * .ogiFigo. + * .ogi_figo. * .oggiggo. * .oggggo.. * ..oooo... @@ -1012,7 +1009,7 @@ static void do_createEdgeLocationBuffer(unsigned int t, * * The memory in gbuf[] after filling will look like this: * - * gradientFillOffset (0 pixels) innerEdgeOffset (18 pixels) outerEdgeOffset (22 pixels) + * gradient_fill_offset (0 pixels) inner_edge_offset (18 pixels) outer_edge_offset (22 pixels) * / / / * / / / * |X Y X Y X Y X Y > = 0; x--) { - gradientFillOffset = x << 1; - t = gbuf[gradientFillOffset]; /* Calculate column of pixel indexed by `gbuf[x]`. */ - fsz = gbuf[gradientFillOffset + 1]; /* Calculate row of pixel indexed by `gbuf[x]`. */ - dmin = 0xffffffff; /* Reset min distance to edge pixel. */ - for (a = outerEdgeOffset + osz - 1; a >= outerEdgeOffset; + gradient_fill_offset = x << 1; + t = gbuf[gradient_fill_offset]; /* Calculate column of pixel indexed by `gbuf[x]`. */ + fsz = gbuf[gradient_fill_offset + 1]; /* Calculate row of pixel indexed by `gbuf[x]`. */ + dmin = 0xffffffff; /* Reset min distance to edge pixel. */ + for (a = outer_edge_offset + osz - 1; a >= outer_edge_offset; a--) { /* Loop through all outer edge buffer pixels. */ ud = a << 1; dy = t - gbuf[ud]; /* Set dx to gradient pixel column - outer edge pixel row. */ @@ -1176,7 +1173,7 @@ static void do_fillGradientBuffer(unsigned int rw, odist = odist * (rsopf - (rsf * odist * odist)); /* -- This line can be iterated for more accuracy. -- */ dmin = 0xffffffff; /* Reset min distance to edge pixel. */ - for (a = innerEdgeOffset + isz - 1; a >= innerEdgeOffset; + for (a = inner_edge_offset + isz - 1; a >= inner_edge_offset; a--) { /* Loop through all inside edge pixels. */ ud = a << 1; dy = t - gbuf[ud]; /* Compute delta in Y from gradient pixel to inside edge pixel. */ @@ -1204,14 +1201,14 @@ static void do_fillGradientBuffer(unsigned int rw, /* Here we reconstruct the pixel's memory location in the CompBuf by * `Pixel Index = Pixel Column + ( Pixel Row * Row Width )`. */ - res[gbuf[gradientFillOffset + 1] + (gbuf[gradientFillOffset] * rw)] = + res[gbuf[gradient_fill_offset + 1] + (gbuf[gradient_fill_offset] * rw)] = (idist / (idist + odist)); /* Set intensity. */ } } /* End of copy. */ -void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float *res) +void DoubleEdgeMaskOperation::do_double_edge_mask(float *imask, float *omask, float *res) { unsigned int *lres; /* Pointer to output pixel buffer (for bit operations). */ unsigned int *limask; /* Pointer to inner mask (for bit operations). */ @@ -1225,17 +1222,17 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float unsigned int osz = 0; /* Size (in pixels) of outside edge pixel index buffer. */ unsigned int gsz = 0; /* Size (in pixels) of gradient pixel index buffer. */ unsigned int rsize[3]; /* Size storage to pass to helper functions. */ - unsigned int innerEdgeOffset = + unsigned int inner_edge_offset = 0; /* Offset into final buffer where inner edge pixel indexes start. */ - unsigned int outerEdgeOffset = + unsigned int outer_edge_offset = 0; /* Offset into final buffer where outer edge pixel indexes start. */ unsigned short *gbuf; /* Gradient/inner/outer pixel location index buffer. */ if (true) { /* If both input sockets have some data coming in... */ - rw = this->getWidth(); /* Width of a row of pixels. */ - t = (rw * this->getHeight()) - 1; /* Determine size of the frame. */ + rw = this->get_width(); /* Width of a row of pixels. */ + t = (rw * this->get_height()) - 1; /* Determine size of the frame. */ memset(res, 0, sizeof(float) * @@ -1270,8 +1267,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float * * Each version has slightly different criteria for detecting an edge pixel. */ - if (this->m_adjacentOnly) { /* If "adjacent only" inner edge mode is turned on. */ - if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ + if (adjacent_only_) { /* If "adjacent only" inner edge mode is turned on. */ + if (keep_inside_) { /* If "keep inside" buffer edge mode is turned on. */ do_adjacentKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1284,8 +1281,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float /* Detect edges in all non-border pixels in the buffer. */ do_adjacentEdgeDetection(t, rw, limask, lomask, lres, res, rsize, isz, osz, gsz); } - else { /* "all" inner edge mode is turned on. */ - if (this->m_keepInside) { /* If "keep inside" buffer edge mode is turned on. */ + else { /* "all" inner edge mode is turned on. */ + if (keep_inside_) { /* If "keep inside" buffer edge mode is turned on. */ do_allKeepBorders(t, rw, limask, lomask, lres, res, rsize); } else { /* "bleed out" buffer edge mode is turned on. */ @@ -1312,8 +1309,8 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float gbuf = (unsigned short *)MEM_callocN(sizeof(unsigned short) * fsz * 2, "DEM"); do_createEdgeLocationBuffer( - t, rw, lres, res, gbuf, &innerEdgeOffset, &outerEdgeOffset, isz, gsz); - do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, innerEdgeOffset, outerEdgeOffset); + t, rw, lres, res, gbuf, &inner_edge_offset, &outer_edge_offset, isz, gsz); + do_fillGradientBuffer(rw, res, gbuf, isz, osz, gsz, inner_edge_offset, outer_edge_offset); /* Free the gradient index buffer. */ MEM_freeN(gbuf); @@ -1322,76 +1319,75 @@ void DoubleEdgeMaskOperation::doDoubleEdgeMask(float *imask, float *omask, float DoubleEdgeMaskOperation::DoubleEdgeMaskOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputInnerMask = nullptr; - this->m_inputOuterMask = nullptr; - this->m_adjacentOnly = false; - this->m_keepInside = false; - this->flags.complex = true; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_inner_mask_ = nullptr; + input_outer_mask_ = nullptr; + adjacent_only_ = false; + keep_inside_ = false; + flags_.complex = true; is_output_rendered_ = false; } -bool DoubleEdgeMaskOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool DoubleEdgeMaskOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - if (this->m_cachedInstance == nullptr) { - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + if (cached_instance_ == nullptr) { + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } return false; } -void DoubleEdgeMaskOperation::initExecution() +void DoubleEdgeMaskOperation::init_execution() { - this->m_inputInnerMask = this->getInputSocketReader(0); - this->m_inputOuterMask = this->getInputSocketReader(1); - initMutex(); - this->m_cachedInstance = nullptr; + input_inner_mask_ = this->get_input_socket_reader(0); + input_outer_mask_ = this->get_input_socket_reader(1); + init_mutex(); + cached_instance_ = nullptr; } -void *DoubleEdgeMaskOperation::initializeTileData(rcti *rect) +void *DoubleEdgeMaskOperation::initialize_tile_data(rcti *rect) { - if (this->m_cachedInstance) { - return this->m_cachedInstance; + if (cached_instance_) { + return cached_instance_; } - lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *innerMask = (MemoryBuffer *)this->m_inputInnerMask->initializeTileData(rect); - MemoryBuffer *outerMask = (MemoryBuffer *)this->m_inputOuterMask->initializeTileData(rect); - float *data = (float *)MEM_mallocN(sizeof(float) * this->getWidth() * this->getHeight(), + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *inner_mask = (MemoryBuffer *)input_inner_mask_->initialize_tile_data(rect); + MemoryBuffer *outer_mask = (MemoryBuffer *)input_outer_mask_->initialize_tile_data(rect); + float *data = (float *)MEM_mallocN(sizeof(float) * this->get_width() * this->get_height(), __func__); - float *imask = innerMask->getBuffer(); - float *omask = outerMask->getBuffer(); - doDoubleEdgeMask(imask, omask, data); - this->m_cachedInstance = data; + float *imask = inner_mask->get_buffer(); + float *omask = outer_mask->get_buffer(); + do_double_edge_mask(imask, omask, data); + cached_instance_ = data; } - unlockMutex(); - return this->m_cachedInstance; + unlock_mutex(); + return cached_instance_; } -void DoubleEdgeMaskOperation::executePixel(float output[4], int x, int y, void *data) +void DoubleEdgeMaskOperation::execute_pixel(float output[4], int x, int y, void *data) { float *buffer = (float *)data; - int index = (y * this->getWidth() + x); + int index = (y * this->get_width() + x); output[0] = buffer[index]; } -void DoubleEdgeMaskOperation::deinitExecution() +void DoubleEdgeMaskOperation::deinit_execution() { - this->m_inputInnerMask = nullptr; - this->m_inputOuterMask = nullptr; - deinitMutex(); - if (this->m_cachedInstance) { - MEM_freeN(this->m_cachedInstance); - this->m_cachedInstance = nullptr; + input_inner_mask_ = nullptr; + input_outer_mask_ = nullptr; + deinit_mutex(); + if (cached_instance_) { + MEM_freeN(cached_instance_); + cached_instance_ = nullptr; } } @@ -1399,10 +1395,7 @@ void DoubleEdgeMaskOperation::get_area_of_interest(int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - r_input_area.xmax = this->getWidth(); - r_input_area.xmin = 0; - r_input_area.ymax = this->getHeight(); - r_input_area.ymin = 0; + r_input_area = this->get_canvas(); } void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output, @@ -1418,11 +1411,11 @@ void DoubleEdgeMaskOperation::update_memory_buffer(MemoryBuffer *output, MemoryBuffer *outer_mask = input_outer_mask->is_a_single_elem() ? input_outer_mask->inflate() : input_outer_mask; - BLI_assert(output->getWidth() == this->getWidth()); - BLI_assert(output->getHeight() == this->getHeight()); + BLI_assert(output->get_width() == this->get_width()); + BLI_assert(output->get_height() == this->get_height()); /* TODO(manzanilla): Once tiled implementation is removed, use execution system to run * multi-threaded where possible. */ - doDoubleEdgeMask(inner_mask->getBuffer(), outer_mask->getBuffer(), output->getBuffer()); + do_double_edge_mask(inner_mask->get_buffer(), outer_mask->get_buffer(), output->get_buffer()); is_output_rendered_ = true; if (inner_mask != input_inner_mask) { diff --git a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h index 45a80bbbbf0..f2086d9ebd4 100644 --- a/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h +++ b/source/blender/compositor/operations/COM_DoubleEdgeMaskOperation.h @@ -25,50 +25,50 @@ namespace blender::compositor { class DoubleEdgeMaskOperation : public NodeOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputOuterMask; - SocketReader *m_inputInnerMask; - bool m_adjacentOnly; - bool m_keepInside; + SocketReader *input_outer_mask_; + SocketReader *input_inner_mask_; + bool adjacent_only_; + bool keep_inside_; /* TODO(manzanilla): To be removed with tiled implementation. */ - float *m_cachedInstance; + float *cached_instance_; bool is_output_rendered_; public: DoubleEdgeMaskOperation(); - void doDoubleEdgeMask(float *imask, float *omask, float *res); + void do_double_edge_mask(float *imask, float *omask, float *res); /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setAdjecentOnly(bool adjacentOnly) + void set_adjecent_only(bool adjacent_only) { - this->m_adjacentOnly = adjacentOnly; + adjacent_only_ = adjacent_only; } - void setKeepInside(bool keepInside) + void set_keep_inside(bool keep_inside) { - this->m_keepInside = keepInside; + keep_inside_ = keep_inside; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc index eb1fd98a590..a051e06d15e 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.cc +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.cc @@ -17,84 +17,80 @@ */ #include "COM_EllipseMaskOperation.h" -#include "BLI_math.h" -#include "DNA_node_types.h" - -#include namespace blender::compositor { EllipseMaskOperation::EllipseMaskOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; - this->m_cosine = 0.0f; - this->m_sine = 0.0f; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_mask_ = nullptr; + input_value_ = nullptr; + cosine_ = 0.0f; + sine_ = 0.0f; } -void EllipseMaskOperation::initExecution() +void EllipseMaskOperation::init_execution() { - this->m_inputMask = this->getInputSocketReader(0); - this->m_inputValue = this->getInputSocketReader(1); - const double rad = (double)this->m_data->rotation; - this->m_cosine = cos(rad); - this->m_sine = sin(rad); - this->m_aspectRatio = ((float)this->getWidth()) / this->getHeight(); + input_mask_ = this->get_input_socket_reader(0); + input_value_ = this->get_input_socket_reader(1); + const double rad = (double)data_->rotation; + cosine_ = cos(rad); + sine_ = sin(rad); + aspect_ratio_ = ((float)this->get_width()) / this->get_height(); } -void EllipseMaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void EllipseMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputMask[4]; - float inputValue[4]; + float input_mask[4]; + float input_value[4]; - float rx = x / this->getWidth(); - float ry = y / this->getHeight(); + float rx = x / MAX2(this->get_width() - 1.0f, FLT_EPSILON); + float ry = y / MAX2(this->get_height() - 1.0f, FLT_EPSILON); - const float dy = (ry - this->m_data->y) / this->m_aspectRatio; - const float dx = rx - this->m_data->x; - rx = this->m_data->x + (this->m_cosine * dx + this->m_sine * dy); - ry = this->m_data->y + (-this->m_sine * dx + this->m_cosine * dy); + const float dy = (ry - data_->y) / aspect_ratio_; + const float dx = rx - data_->x; + rx = data_->x + (cosine_ * dx + sine_ * dy); + ry = data_->y + (-sine_ * dx + cosine_ * dy); - this->m_inputMask->readSampled(inputMask, x, y, sampler); - this->m_inputValue->readSampled(inputValue, x, y, sampler); + input_mask_->read_sampled(input_mask, x, y, sampler); + input_value_->read_sampled(input_value, x, y, sampler); - const float halfHeight = (this->m_data->height) / 2.0f; - const float halfWidth = this->m_data->width / 2.0f; - float sx = rx - this->m_data->x; + const float half_height = (data_->height) / 2.0f; + const float half_width = data_->width / 2.0f; + float sx = rx - data_->x; sx *= sx; - const float tx = halfWidth * halfWidth; - float sy = ry - this->m_data->y; + const float tx = half_width * half_width; + float sy = ry - data_->y; sy *= sy; - const float ty = halfHeight * halfHeight; + const float ty = half_height * half_height; - bool inside = ((sx / tx) + (sy / ty)) < 1.0f; + bool inside = ((sx / tx) + (sy / ty)) <= (1.0f + FLT_EPSILON); - switch (this->m_maskType) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: if (inside) { - output[0] = MAX2(inputMask[0], inputValue[0]); + output[0] = MAX2(input_mask[0], input_value[0]); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_SUBTRACT: if (inside) { - output[0] = inputMask[0] - inputValue[0]; + output[0] = input_mask[0] - input_value[0]; CLAMP(output[0], 0, 1); } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; case CMP_NODE_MASKTYPE_MULTIPLY: if (inside) { - output[0] = inputMask[0] * inputValue[0]; + output[0] = input_mask[0] * input_value[0]; } else { output[0] = 0; @@ -102,15 +98,15 @@ void EllipseMaskOperation::executePixelSampled(float output[4], break; case CMP_NODE_MASKTYPE_NOT: if (inside) { - if (inputMask[0] > 0.0f) { + if (input_mask[0] > 0.0f) { output[0] = 0; } else { - output[0] = inputValue[0]; + output[0] = input_value[0]; } } else { - output[0] = inputMask[0]; + output[0] = input_mask[0]; } break; } @@ -121,7 +117,7 @@ void EllipseMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { MaskFunc mask_func; - switch (m_maskType) { + switch (mask_type_) { case CMP_NODE_MASKTYPE_ADD: mask_func = [](const bool is_inside, const float *mask, const float *value) { return is_inside ? MAX2(mask[0], value[0]) : mask[0]; @@ -156,28 +152,28 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, { const MemoryBuffer *input_mask = inputs[0]; const MemoryBuffer *input_value = inputs[1]; - const float op_w = this->getWidth(); - const float op_h = this->getHeight(); - const float half_w = this->m_data->width / 2.0f; - const float half_h = this->m_data->height / 2.0f; + const float op_last_x = MAX2(this->get_width() - 1.0f, FLT_EPSILON); + const float op_last_y = MAX2(this->get_height() - 1.0f, FLT_EPSILON); + const float half_w = data_->width / 2.0f; + const float half_h = data_->height / 2.0f; const float tx = half_w * half_w; const float ty = half_h * half_h; - for (const int y : YRange(area)) { - const float op_ry = y / op_h; - const float dy = (op_ry - this->m_data->y) / m_aspectRatio; + for (int y = area.ymin; y < area.ymax; y++) { + const float op_ry = y / op_last_y; + const float dy = (op_ry - data_->y) / aspect_ratio_; float *out = output->get_elem(area.xmin, y); const float *mask = input_mask->get_elem(area.xmin, y); const float *value = input_value->get_elem(area.xmin, y); - for (const int x : XRange(area)) { - const float op_rx = x / op_w; - const float dx = op_rx - this->m_data->x; - const float rx = this->m_data->x + (m_cosine * dx + m_sine * dy); - const float ry = this->m_data->y + (-m_sine * dx + m_cosine * dy); - float sx = rx - this->m_data->x; + for (int x = area.xmin; x < area.xmax; x++) { + const float op_rx = x / op_last_x; + const float dx = op_rx - data_->x; + const float rx = data_->x + (cosine_ * dx + sine_ * dy); + const float ry = data_->y + (-sine_ * dx + cosine_ * dy); + float sx = rx - data_->x; sx *= sx; - float sy = ry - this->m_data->y; + float sy = ry - data_->y; sy *= sy; - const bool inside = ((sx / tx) + (sy / ty)) < 1.0f; + const bool inside = ((sx / tx) + (sy / ty)) <= (1.0f + FLT_EPSILON); out[0] = mask_func(inside, mask, value); mask += input_mask->elem_stride; @@ -187,10 +183,10 @@ void EllipseMaskOperation::apply_mask(MemoryBuffer *output, } } -void EllipseMaskOperation::deinitExecution() +void EllipseMaskOperation::deinit_execution() { - this->m_inputMask = nullptr; - this->m_inputValue = nullptr; + input_mask_ = nullptr; + input_value_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_EllipseMaskOperation.h b/source/blender/compositor/operations/COM_EllipseMaskOperation.h index fba3f979d26..7a6f96ab1b1 100644 --- a/source/blender/compositor/operations/COM_EllipseMaskOperation.h +++ b/source/blender/compositor/operations/COM_EllipseMaskOperation.h @@ -27,17 +27,17 @@ class EllipseMaskOperation : public MultiThreadedOperation { using MaskFunc = std::function; /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputMask; - SocketReader *m_inputValue; + SocketReader *input_mask_; + SocketReader *input_value_; - float m_sine; - float m_cosine; - float m_aspectRatio; - int m_maskType; + float sine_; + float cosine_; + float aspect_ratio_; + int mask_type_; - NodeEllipseMask *m_data; + NodeEllipseMask *data_; public: EllipseMaskOperation(); @@ -45,26 +45,26 @@ class EllipseMaskOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeEllipseMask *data) + void set_data(NodeEllipseMask *data) { - this->m_data = data; + data_ = data; } - void setMaskType(int maskType) + void set_mask_type(int mask_type) { - this->m_maskType = maskType; + mask_type_ = mask_type; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc index e0fc45811cb..850fad1d96a 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.cc @@ -18,105 +18,103 @@ #include -#include "BLI_utildefines.h" #include "COM_FastGaussianBlurOperation.h" -#include "MEM_guardedalloc.h" namespace blender::compositor { FastGaussianBlurOperation::FastGaussianBlurOperation() : BlurBaseOperation(DataType::Color) { - this->m_iirgaus = nullptr; + iirgaus_ = nullptr; } -void FastGaussianBlurOperation::executePixel(float output[4], int x, int y, void *data) +void FastGaussianBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - MemoryBuffer *newData = (MemoryBuffer *)data; - newData->read(output, x, y); + MemoryBuffer *new_data = (MemoryBuffer *)data; + new_data->read(output, x, y); } -bool FastGaussianBlurOperation::determineDependingAreaOfInterest( - rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) +bool FastGaussianBlurOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; + rcti new_input; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } - if (this->m_iirgaus) { + if (iirgaus_) { return false; } - newInput.xmin = 0; - newInput.ymin = 0; - newInput.xmax = this->getWidth(); - newInput.ymax = this->getHeight(); + new_input.xmin = 0; + new_input.ymin = 0; + new_input.xmax = this->get_width(); + new_input.ymax = this->get_height(); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void FastGaussianBlurOperation::init_data() { BlurBaseOperation::init_data(); - this->m_sx = this->m_data.sizex * this->m_size / 2.0f; - this->m_sy = this->m_data.sizey * this->m_size / 2.0f; + sx_ = data_.sizex * size_ / 2.0f; + sy_ = data_.sizey * size_ / 2.0f; } -void FastGaussianBlurOperation::initExecution() +void FastGaussianBlurOperation::init_execution() { - BlurBaseOperation::initExecution(); - BlurBaseOperation::initMutex(); + BlurBaseOperation::init_execution(); + BlurBaseOperation::init_mutex(); } -void FastGaussianBlurOperation::deinitExecution() +void FastGaussianBlurOperation::deinit_execution() { - if (this->m_iirgaus) { - delete this->m_iirgaus; - this->m_iirgaus = nullptr; + if (iirgaus_) { + delete iirgaus_; + iirgaus_ = nullptr; } - BlurBaseOperation::deinitMutex(); + BlurBaseOperation::deinit_mutex(); } -void *FastGaussianBlurOperation::initializeTileData(rcti *rect) +void *FastGaussianBlurOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (!this->m_iirgaus) { - MemoryBuffer *newBuf = (MemoryBuffer *)this->m_inputProgram->initializeTileData(rect); - MemoryBuffer *copy = new MemoryBuffer(*newBuf); - updateSize(); + lock_mutex(); + if (!iirgaus_) { + MemoryBuffer *new_buf = (MemoryBuffer *)input_program_->initialize_tile_data(rect); + MemoryBuffer *copy = new MemoryBuffer(*new_buf); + update_size(); int c; - this->m_sx = this->m_data.sizex * this->m_size / 2.0f; - this->m_sy = this->m_data.sizey * this->m_size / 2.0f; + sx_ = data_.sizex * size_ / 2.0f; + sy_ = data_.sizey * size_ / 2.0f; - if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) { + if ((sx_ == sy_) && (sx_ > 0.0f)) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, this->m_sx, c, 3); + IIR_gauss(copy, sx_, c, 3); } } else { - if (this->m_sx > 0.0f) { + if (sx_ > 0.0f) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, this->m_sx, c, 1); + IIR_gauss(copy, sx_, c, 1); } } - if (this->m_sy > 0.0f) { + if (sy_ > 0.0f) { for (c = 0; c < COM_DATA_TYPE_COLOR_CHANNELS; c++) { - IIR_gauss(copy, this->m_sy, c, 2); + IIR_gauss(copy, sy_, c, 2); } } } - this->m_iirgaus = copy; + iirgaus_ = copy; } - unlockMutex(); - return this->m_iirgaus; + unlock_mutex(); + return iirgaus_; } void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, @@ -127,11 +125,11 @@ void FastGaussianBlurOperation::IIR_gauss(MemoryBuffer *src, BLI_assert(!src->is_a_single_elem()); double q, q2, sc, cf[4], tsM[9], tsu[3], tsv[3]; double *X, *Y, *W; - const unsigned int src_width = src->getWidth(); - const unsigned int src_height = src->getHeight(); + const unsigned int src_width = src->get_width(); + const unsigned int src_height = src->get_height(); unsigned int x, y, sz; unsigned int i; - float *buffer = src->getBuffer(); + float *buffer = src->get_buffer(); const uint8_t num_channels = src->get_num_channels(); /* <0.5 not valid, though can have a possibly useful sort of sharpening effect. */ @@ -271,10 +269,7 @@ void FastGaussianBlurOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: - r_input_area.xmin = 0; - r_input_area.xmax = getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = getHeight(); + r_input_area = this->get_canvas(); break; default: BlurBaseOperation::get_area_of_interest(input_idx, output_area, r_input_area); @@ -295,24 +290,24 @@ void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *outpu image = output; } else { - image = new MemoryBuffer(getOutputSocket()->getDataType(), area); + image = new MemoryBuffer(get_output_socket()->get_data_type(), area); } image->copy_from(input, area); - if ((this->m_sx == this->m_sy) && (this->m_sx > 0.0f)) { + if ((sx_ == sy_) && (sx_ > 0.0f)) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, this->m_sx, c, 3); + IIR_gauss(image, sx_, c, 3); } } else { - if (this->m_sx > 0.0f) { + if (sx_ > 0.0f) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, this->m_sx, c, 1); + IIR_gauss(image, sx_, c, 1); } } - if (this->m_sy > 0.0f) { + if (sy_ > 0.0f) { for (const int c : IndexRange(COM_DATA_TYPE_COLOR_CHANNELS)) { - IIR_gauss(image, this->m_sy, c, 2); + IIR_gauss(image, sy_, c, 2); } } } @@ -325,75 +320,75 @@ void FastGaussianBlurOperation::update_memory_buffer_started(MemoryBuffer *outpu FastGaussianBlurValueOperation::FastGaussianBlurValueOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_iirgaus = nullptr; - this->m_inputprogram = nullptr; - this->m_sigma = 1.0f; - this->m_overlay = 0; - flags.complex = true; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + iirgaus_ = nullptr; + inputprogram_ = nullptr; + sigma_ = 1.0f; + overlay_ = 0; + flags_.complex = true; } -void FastGaussianBlurValueOperation::executePixel(float output[4], int x, int y, void *data) +void FastGaussianBlurValueOperation::execute_pixel(float output[4], int x, int y, void *data) { - MemoryBuffer *newData = (MemoryBuffer *)data; - newData->read(output, x, y); + MemoryBuffer *new_data = (MemoryBuffer *)data; + new_data->read(output, x, y); } -bool FastGaussianBlurValueOperation::determineDependingAreaOfInterest( - rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) +bool FastGaussianBlurValueOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - if (this->m_iirgaus) { + if (iirgaus_) { return false; } - newInput.xmin = 0; - newInput.ymin = 0; - newInput.xmax = this->getWidth(); - newInput.ymax = this->getHeight(); + new_input.xmin = 0; + new_input.ymin = 0; + new_input.xmax = this->get_width(); + new_input.ymax = this->get_height(); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } -void FastGaussianBlurValueOperation::initExecution() +void FastGaussianBlurValueOperation::init_execution() { - this->m_inputprogram = getInputSocketReader(0); - initMutex(); + inputprogram_ = get_input_socket_reader(0); + init_mutex(); } -void FastGaussianBlurValueOperation::deinitExecution() +void FastGaussianBlurValueOperation::deinit_execution() { - if (this->m_iirgaus) { - delete this->m_iirgaus; - this->m_iirgaus = nullptr; + if (iirgaus_) { + delete iirgaus_; + iirgaus_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) +void *FastGaussianBlurValueOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (!this->m_iirgaus) { - MemoryBuffer *newBuf = (MemoryBuffer *)this->m_inputprogram->initializeTileData(rect); - MemoryBuffer *copy = new MemoryBuffer(*newBuf); - FastGaussianBlurOperation::IIR_gauss(copy, this->m_sigma, 0, 3); + lock_mutex(); + if (!iirgaus_) { + MemoryBuffer *new_buf = (MemoryBuffer *)inputprogram_->initialize_tile_data(rect); + MemoryBuffer *copy = new MemoryBuffer(*new_buf); + FastGaussianBlurOperation::IIR_gauss(copy, sigma_, 0, 3); - if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) { - float *src = newBuf->getBuffer(); - float *dst = copy->getBuffer(); - for (int i = copy->getWidth() * copy->getHeight(); i != 0; + if (overlay_ == FAST_GAUSS_OVERLAY_MIN) { + float *src = new_buf->get_buffer(); + float *dst = copy->get_buffer(); + for (int i = copy->get_width() * copy->get_height(); i != 0; i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) { if (*src < *dst) { *dst = *src; } } } - else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) { - float *src = newBuf->getBuffer(); - float *dst = copy->getBuffer(); - for (int i = copy->getWidth() * copy->getHeight(); i != 0; + else if (overlay_ == FAST_GAUSS_OVERLAY_MAX) { + float *src = new_buf->get_buffer(); + float *dst = copy->get_buffer(); + for (int i = copy->get_width() * copy->get_height(); i != 0; i--, src += COM_DATA_TYPE_VALUE_CHANNELS, dst += COM_DATA_TYPE_VALUE_CHANNELS) { if (*src > *dst) { *dst = *src; @@ -401,31 +396,28 @@ void *FastGaussianBlurValueOperation::initializeTileData(rcti *rect) } } - this->m_iirgaus = copy; + iirgaus_ = copy; } - unlockMutex(); - return this->m_iirgaus; + unlock_mutex(); + return iirgaus_; } void FastGaussianBlurValueOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - r_input_area.xmin = 0; - r_input_area.xmax = getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = getHeight(); + r_input_area = this->get_canvas(); } void FastGaussianBlurValueOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - if (m_iirgaus == nullptr) { + if (iirgaus_ == nullptr) { const MemoryBuffer *image = inputs[0]; MemoryBuffer *gauss = new MemoryBuffer(*image); - FastGaussianBlurOperation::IIR_gauss(gauss, m_sigma, 0, 3); - m_iirgaus = gauss; + FastGaussianBlurOperation::IIR_gauss(gauss, sigma_, 0, 3); + iirgaus_ = gauss; } } @@ -434,13 +426,13 @@ void FastGaussianBlurValueOperation::update_memory_buffer_partial(MemoryBuffer * Span inputs) { MemoryBuffer *image = inputs[0]; - BuffersIterator it = output->iterate_with({image, m_iirgaus}, area); - if (this->m_overlay == FAST_GAUSS_OVERLAY_MIN) { + BuffersIterator it = output->iterate_with({image, iirgaus_}, area); + if (overlay_ == FAST_GAUSS_OVERLAY_MIN) { for (; !it.is_end(); ++it) { *it.out = MIN2(*it.in(0), *it.in(1)); } } - else if (this->m_overlay == FAST_GAUSS_OVERLAY_MAX) { + else if (overlay_ == FAST_GAUSS_OVERLAY_MAX) { for (; !it.is_end(); ++it) { *it.out = MAX2(*it.in(0), *it.in(1)); } diff --git a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h index f42fc76a119..8d33e5ef244 100644 --- a/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h +++ b/source/blender/compositor/operations/COM_FastGaussianBlurOperation.h @@ -25,22 +25,22 @@ namespace blender::compositor { class FastGaussianBlurOperation : public BlurBaseOperation { private: - float m_sx; - float m_sy; - MemoryBuffer *m_iirgaus; + float sx_; + float sy_; + MemoryBuffer *iirgaus_; public: FastGaussianBlurOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; static void IIR_gauss(MemoryBuffer *src, float sigma, unsigned int channel, unsigned int xy); - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; void init_data() override; - void deinitExecution() override; - void initExecution() override; + void deinit_execution() override; + void init_execution() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, @@ -61,35 +61,35 @@ enum { class FastGaussianBlurValueOperation : public MultiThreadedOperation { private: - float m_sigma; - MemoryBuffer *m_iirgaus; - SocketReader *m_inputprogram; + float sigma_; + MemoryBuffer *iirgaus_; + SocketReader *inputprogram_; /** * -1: re-mix with darker * 0: do nothing * 1 re-mix with lighter */ - int m_overlay; + int overlay_; public: FastGaussianBlurValueOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixel(float output[4], int x, int y, void *data) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void *initializeTileData(rcti *rect) override; - void deinitExecution() override; - void initExecution() override; - void setSigma(float sigma) + void *initialize_tile_data(rcti *rect) override; + void deinit_execution() override; + void init_execution() override; + void set_sigma(float sigma) { - this->m_sigma = sigma; + sigma_ = sigma; } /* used for DOF blurring ZBuffer */ - void setOverlay(int overlay) + void set_overlay(int overlay) { - this->m_overlay = overlay; + overlay_ = overlay; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_FlipOperation.cc b/source/blender/compositor/operations/COM_FlipOperation.cc index d0dc6c0b570..7ca12a504b7 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.cc +++ b/source/blender/compositor/operations/COM_FlipOperation.cc @@ -22,57 +22,75 @@ namespace blender::compositor { FlipOperation::FlipOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->m_flipX = true; - this->m_flipY = false; + this->add_input_socket(DataType::Color, ResizeMode::None); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); + input_operation_ = nullptr; + flip_x_ = true; + flip_y_ = false; } -void FlipOperation::initExecution() +void FlipOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void FlipOperation::deinitExecution() +void FlipOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } -void FlipOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void FlipOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float nx = this->m_flipX ? ((int)this->getWidth() - 1) - x : x; - float ny = this->m_flipY ? ((int)this->getHeight() - 1) - y : y; + float nx = flip_x_ ? ((int)this->get_width() - 1) - x : x; + float ny = flip_y_ ? ((int)this->get_height() - 1) - y : y; - this->m_inputOperation->readSampled(output, nx, ny, sampler); + input_operation_->read_sampled(output, nx, ny, sampler); } -bool FlipOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool FlipOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - if (this->m_flipX) { - const int w = (int)this->getWidth() - 1; - newInput.xmax = (w - input->xmin) + 1; - newInput.xmin = (w - input->xmax) - 1; + if (flip_x_) { + const int w = (int)this->get_width() - 1; + new_input.xmax = (w - input->xmin) + 1; + new_input.xmin = (w - input->xmax) - 1; } else { - newInput.xmin = input->xmin; - newInput.xmax = input->xmax; + new_input.xmin = input->xmin; + new_input.xmax = input->xmax; } - if (this->m_flipY) { - const int h = (int)this->getHeight() - 1; - newInput.ymax = (h - input->ymin) + 1; - newInput.ymin = (h - input->ymax) - 1; + if (flip_y_) { + const int h = (int)this->get_height() - 1; + new_input.ymax = (h - input->ymin) + 1; + new_input.ymin = (h - input->ymax) - 1; } else { - newInput.ymin = input->ymin; - newInput.ymax = input->ymax; + new_input.ymin = input->ymin; + new_input.ymax = input->ymax; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); +} + +void FlipOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + NodeOperation::determine_canvas(preferred_area, r_area); + if (execution_model_ == eExecutionModel::FullFrame) { + rcti input_area = r_area; + if (flip_x_) { + const int width = BLI_rcti_size_x(&input_area) - 1; + r_area.xmax = (width - input_area.xmin) + 1; + r_area.xmin = (width - input_area.xmax) + 1; + } + if (flip_y_) { + const int height = BLI_rcti_size_y(&input_area) - 1; + r_area.ymax = (height - input_area.ymin) + 1; + r_area.ymin = (height - input_area.ymax) + 1; + } + } } void FlipOperation::get_area_of_interest(const int input_idx, @@ -81,19 +99,19 @@ void FlipOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - if (this->m_flipX) { - const int w = (int)this->getWidth() - 1; + if (flip_x_) { + const int w = (int)this->get_width() - 1; r_input_area.xmax = (w - output_area.xmin) + 1; - r_input_area.xmin = (w - output_area.xmax) - 1; + r_input_area.xmin = (w - output_area.xmax) + 1; } else { r_input_area.xmin = output_area.xmin; r_input_area.xmax = output_area.xmax; } - if (this->m_flipY) { - const int h = (int)this->getHeight() - 1; + if (flip_y_) { + const int h = (int)this->get_height() - 1; r_input_area.ymax = (h - output_area.ymin) + 1; - r_input_area.ymin = (h - output_area.ymax) - 1; + r_input_area.ymin = (h - output_area.ymax) + 1; } else { r_input_area.ymin = output_area.ymin; @@ -106,10 +124,12 @@ void FlipOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *input_img = inputs[0]; + const int input_offset_x = input_img->get_rect().xmin; + const int input_offset_y = input_img->get_rect().ymin; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - const int nx = this->m_flipX ? ((int)this->getWidth() - 1) - it.x : it.x; - const int ny = this->m_flipY ? ((int)this->getHeight() - 1) - it.y : it.y; - input_img->read_elem(nx, ny, it.out); + const int nx = flip_x_ ? ((int)this->get_width() - 1) - it.x : it.x; + const int ny = flip_y_ ? ((int)this->get_height() - 1) - it.y : it.y; + input_img->read_elem(input_offset_x + nx, input_offset_y + ny, it.out); } } diff --git a/source/blender/compositor/operations/COM_FlipOperation.h b/source/blender/compositor/operations/COM_FlipOperation.h index dba7f82c341..001fd652740 100644 --- a/source/blender/compositor/operations/COM_FlipOperation.h +++ b/source/blender/compositor/operations/COM_FlipOperation.h @@ -24,28 +24,29 @@ namespace blender::compositor { class FlipOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - bool m_flipX; - bool m_flipY; + SocketReader *input_operation_; + bool flip_x_; + bool flip_y_; public: FlipOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void setFlipX(bool flipX) { - this->m_flipX = flipX; + flip_x_ = flipX; } void setFlipY(bool flipY) { - this->m_flipY = flipY; + flip_y_ = flipY; } + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc index 1bff3b965c6..57d2ebd1b84 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.cc @@ -17,45 +17,44 @@ */ #include "COM_GammaCorrectOperation.h" -#include "BLI_math.h" namespace blender::compositor { GammaCorrectOperation::GammaCorrectOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + flags_.can_be_constant = true; } -void GammaCorrectOperation::initExecution() +void GammaCorrectOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void GammaCorrectOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void GammaCorrectOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputProgram->readSampled(inputColor, x, y, sampler); - if (inputColor[3] > 0.0f) { - inputColor[0] /= inputColor[3]; - inputColor[1] /= inputColor[3]; - inputColor[2] /= inputColor[3]; + float input_color[4]; + input_program_->read_sampled(input_color, x, y, sampler); + if (input_color[3] > 0.0f) { + input_color[0] /= input_color[3]; + input_color[1] /= input_color[3]; + input_color[2] /= input_color[3]; } /* check for negative to avoid nan's */ - output[0] = inputColor[0] > 0.0f ? inputColor[0] * inputColor[0] : 0.0f; - output[1] = inputColor[1] > 0.0f ? inputColor[1] * inputColor[1] : 0.0f; - output[2] = inputColor[2] > 0.0f ? inputColor[2] * inputColor[2] : 0.0f; - output[3] = inputColor[3]; + output[0] = input_color[0] > 0.0f ? input_color[0] * input_color[0] : 0.0f; + output[1] = input_color[1] > 0.0f ? input_color[1] * input_color[1] : 0.0f; + output[2] = input_color[2] > 0.0f ? input_color[2] * input_color[2] : 0.0f; + output[3] = input_color[3]; - if (inputColor[3] > 0.0f) { - output[0] *= inputColor[3]; - output[1] *= inputColor[3]; - output[2] *= inputColor[3]; + if (input_color[3] > 0.0f) { + output[0] *= input_color[3]; + output[1] *= input_color[3]; + output[2] *= input_color[3]; } } @@ -87,46 +86,46 @@ void GammaCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void GammaCorrectOperation::deinitExecution() +void GammaCorrectOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; } GammaUncorrectOperation::GammaUncorrectOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + flags_.can_be_constant = true; } -void GammaUncorrectOperation::initExecution() +void GammaUncorrectOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void GammaUncorrectOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void GammaUncorrectOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor[4]; - this->m_inputProgram->readSampled(inputColor, x, y, sampler); + float input_color[4]; + input_program_->read_sampled(input_color, x, y, sampler); - if (inputColor[3] > 0.0f) { - inputColor[0] /= inputColor[3]; - inputColor[1] /= inputColor[3]; - inputColor[2] /= inputColor[3]; + if (input_color[3] > 0.0f) { + input_color[0] /= input_color[3]; + input_color[1] /= input_color[3]; + input_color[2] /= input_color[3]; } - output[0] = inputColor[0] > 0.0f ? sqrtf(inputColor[0]) : 0.0f; - output[1] = inputColor[1] > 0.0f ? sqrtf(inputColor[1]) : 0.0f; - output[2] = inputColor[2] > 0.0f ? sqrtf(inputColor[2]) : 0.0f; - output[3] = inputColor[3]; + output[0] = input_color[0] > 0.0f ? sqrtf(input_color[0]) : 0.0f; + output[1] = input_color[1] > 0.0f ? sqrtf(input_color[1]) : 0.0f; + output[2] = input_color[2] > 0.0f ? sqrtf(input_color[2]) : 0.0f; + output[3] = input_color[3]; - if (inputColor[3] > 0.0f) { - output[0] *= inputColor[3]; - output[1] *= inputColor[3]; - output[2] *= inputColor[3]; + if (input_color[3] > 0.0f) { + output[0] *= input_color[3]; + output[1] *= input_color[3]; + output[2] *= input_color[3]; } } @@ -157,9 +156,9 @@ void GammaUncorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void GammaUncorrectOperation::deinitExecution() +void GammaUncorrectOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaCorrectOperation.h b/source/blender/compositor/operations/COM_GammaCorrectOperation.h index 2a9fde70e87..e13d96184ad 100644 --- a/source/blender/compositor/operations/COM_GammaCorrectOperation.h +++ b/source/blender/compositor/operations/COM_GammaCorrectOperation.h @@ -25,9 +25,9 @@ namespace blender::compositor { class GammaCorrectOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; public: GammaCorrectOperation(); @@ -35,17 +35,17 @@ class GammaCorrectOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -55,9 +55,9 @@ class GammaCorrectOperation : public MultiThreadedOperation { class GammaUncorrectOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; public: GammaUncorrectOperation(); @@ -65,17 +65,17 @@ class GammaUncorrectOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_GammaOperation.cc b/source/blender/compositor/operations/COM_GammaOperation.cc index 396d3942b06..3ea7f35f1b5 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.cc +++ b/source/blender/compositor/operations/COM_GammaOperation.cc @@ -17,39 +17,38 @@ */ #include "COM_GammaOperation.h" -#include "BLI_math.h" namespace blender::compositor { GammaOperation::GammaOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_inputGammaProgram = nullptr; - flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + input_gamma_program_ = nullptr; + flags_.can_be_constant = true; } -void GammaOperation::initExecution() +void GammaOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputGammaProgram = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_gamma_program_ = this->get_input_socket_reader(1); } -void GammaOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void GammaOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float inputValue[4]; - float inputGamma[4]; + float input_value[4]; + float input_gamma[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputGammaProgram->readSampled(inputGamma, x, y, sampler); - const float gamma = inputGamma[0]; + input_program_->read_sampled(input_value, x, y, sampler); + input_gamma_program_->read_sampled(input_gamma, x, y, sampler); + const float gamma = input_gamma[0]; /* check for negative to avoid nan's */ - output[0] = inputValue[0] > 0.0f ? powf(inputValue[0], gamma) : inputValue[0]; - output[1] = inputValue[1] > 0.0f ? powf(inputValue[1], gamma) : inputValue[1]; - output[2] = inputValue[2] > 0.0f ? powf(inputValue[2], gamma) : inputValue[2]; + output[0] = input_value[0] > 0.0f ? powf(input_value[0], gamma) : input_value[0]; + output[1] = input_value[1] > 0.0f ? powf(input_value[1], gamma) : input_value[1]; + output[2] = input_value[2] > 0.0f ? powf(input_value[2], gamma) : input_value[2]; - output[3] = inputValue[3]; + output[3] = input_value[3]; } void GammaOperation::update_memory_buffer_row(PixelCursor &p) @@ -66,10 +65,10 @@ void GammaOperation::update_memory_buffer_row(PixelCursor &p) } } -void GammaOperation::deinitExecution() +void GammaOperation::deinit_execution() { - this->m_inputProgram = nullptr; - this->m_inputGammaProgram = nullptr; + input_program_ = nullptr; + input_gamma_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GammaOperation.h b/source/blender/compositor/operations/COM_GammaOperation.h index 713d3d8484f..d8703a0a19a 100644 --- a/source/blender/compositor/operations/COM_GammaOperation.h +++ b/source/blender/compositor/operations/COM_GammaOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class GammaOperation : public MultiThreadedRowOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - SocketReader *m_inputGammaProgram; + SocketReader *input_program_; + SocketReader *input_gamma_program_; public: GammaOperation(); @@ -36,17 +36,17 @@ class GammaOperation : public MultiThreadedRowOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_row(PixelCursor &p) override; }; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc index 9bdc652b466..1a8c000cdd8 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.cc @@ -23,9 +23,9 @@ namespace blender::compositor { GaussianAlphaBlurBaseOperation::GaussianAlphaBlurBaseOperation(eDimension dim) : BlurBaseOperation(DataType::Value) { - this->m_gausstab = nullptr; - this->m_filtersize = 0; - this->m_falloff = -1; /* Intentionally invalid, so we can detect uninitialized values. */ + gausstab_ = nullptr; + filtersize_ = 0; + falloff_ = -1; /* Intentionally invalid, so we can detect uninitialized values. */ dimension_ = dim; } @@ -33,33 +33,33 @@ void GaussianAlphaBlurBaseOperation::init_data() { BlurBaseOperation::init_data(); if (execution_model_ == eExecutionModel::FullFrame) { - rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f); + rad_ = max_ff(size_ * this->get_blur_size(dimension_), 0.0f); rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); } } -void GaussianAlphaBlurBaseOperation::initExecution() +void GaussianAlphaBlurBaseOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); if (execution_model_ == eExecutionModel::FullFrame) { - m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad_, m_filtersize, m_falloff); + gausstab_ = BlurBaseOperation::make_gausstab(rad_, filtersize_); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad_, filtersize_, falloff_); } } -void GaussianAlphaBlurBaseOperation::deinitExecution() +void GaussianAlphaBlurBaseOperation::deinit_execution() { - BlurBaseOperation::deinitExecution(); + BlurBaseOperation::deinit_execution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } - if (this->m_distbuf_inv) { - MEM_freeN(this->m_distbuf_inv); - this->m_distbuf_inv = nullptr; + if (distbuf_inv_) { + MEM_freeN(distbuf_inv_); + distbuf_inv_ = nullptr; } } @@ -75,12 +75,12 @@ void GaussianAlphaBlurBaseOperation::get_area_of_interest(const int input_idx, r_input_area = output_area; switch (dimension_) { case eDimension::X: - r_input_area.xmin = output_area.xmin - m_filtersize - 1; - r_input_area.xmax = output_area.xmax + m_filtersize + 1; + r_input_area.xmin = output_area.xmin - filtersize_ - 1; + r_input_area.xmax = output_area.xmax + filtersize_ + 1; break; case eDimension::Y: - r_input_area.ymin = output_area.ymin - m_filtersize - 1; - r_input_area.ymax = output_area.ymax + m_filtersize + 1; + r_input_area.ymin = output_area.ymin - filtersize_ - 1; + r_input_area.ymax = output_area.ymax + filtersize_ + 1; break; } } @@ -119,8 +119,8 @@ void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer * for (; !it.is_end(); ++it) { const int coord = get_current_coord(); - const int coord_min = max_ii(coord - m_filtersize, min_input_coord); - const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord); + const int coord_min = max_ii(coord - filtersize_, min_input_coord); + const int coord_max = min_ii(coord + filtersize_ + 1, max_input_coord); /* *** This is the main part which is different to #GaussianBlurBaseOperation. *** */ /* Gauss. */ @@ -128,27 +128,27 @@ void GaussianAlphaBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer * float multiplier_accum = 0.0f; /* Dilate. */ - const bool do_invert = m_do_subtract; + const bool do_invert = do_subtract_; /* Init with the current color to avoid unneeded lookups. */ float value_max = finv_test(*it.in(0), do_invert); float distfacinv_max = 1.0f; /* 0 to 1 */ - const int step = QualityStepHelper::getStep(); + const int step = QualityStepHelper::get_step(); const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; const int in_stride = elem_stride * step; - int index = (coord_min - coord) + m_filtersize; + int index = (coord_min - coord) + filtersize_; const int index_end = index + (coord_max - coord_min); for (; index < index_end; in += in_stride, index += step) { float value = finv_test(*in, do_invert); /* Gauss. */ - float multiplier = m_gausstab[index]; + float multiplier = gausstab_[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; /* Dilate - find most extreme color. */ if (value > value_max) { - multiplier = m_distbuf_inv[index]; + multiplier = distbuf_inv_[index]; value *= multiplier; if (value > value_max) { value_max = value; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h index d7ca975ca0a..0f83a67cef7 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaBlurBaseOperation.h @@ -24,11 +24,11 @@ namespace blender::compositor { class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { protected: - float *m_gausstab; - float *m_distbuf_inv; - int m_falloff; /* Falloff for #distbuf_inv. */ - bool m_do_subtract; - int m_filtersize; + float *gausstab_; + float *distbuf_inv_; + int falloff_; /* Falloff for #distbuf_inv. */ + bool do_subtract_; + int filtersize_; float rad_; eDimension dimension_; @@ -36,8 +36,8 @@ class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { GaussianAlphaBlurBaseOperation(eDimension dim); virtual void init_data() override; - virtual void initExecution() override; - virtual void deinitExecution() override; + virtual void init_execution() override; + virtual void deinit_execution() override; void get_area_of_interest(const int input_idx, const rcti &output_area, @@ -49,13 +49,13 @@ class GaussianAlphaBlurBaseOperation : public BlurBaseOperation { /** * Set subtract for Dilate/Erode functionality */ - void setSubtract(bool subtract) + void set_subtract(bool subtract) { - this->m_do_subtract = subtract; + do_subtract_ = subtract; } - void setFalloff(int falloff) + void set_falloff(int falloff) { - this->m_falloff = falloff; + falloff_ = falloff; } }; diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc index 6710ed3cf5b..de0010fcb58 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.cc @@ -17,10 +17,6 @@ */ #include "COM_GaussianAlphaXBlurOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { @@ -29,49 +25,49 @@ GaussianAlphaXBlurOperation::GaussianAlphaXBlurOperation() { } -void *GaussianAlphaXBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianAlphaXBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); - if (!this->m_sizeavailable) { - updateGauss(); + lock_mutex(); + if (!sizeavailable_) { + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } -void GaussianAlphaXBlurOperation::initExecution() +void GaussianAlphaXBlurOperation::init_execution() { - GaussianAlphaBlurBaseOperation::initExecution(); + GaussianAlphaBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizex, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizex, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } -void GaussianAlphaXBlurOperation::updateGauss() +void GaussianAlphaXBlurOperation::update_gauss() { - if (this->m_gausstab == nullptr) { - updateSize(); - float rad = max_ff(m_size * m_data.sizex, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (gausstab_ == nullptr) { + update_size(); + float rad = max_ff(size_ * data_.sizex, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); } - if (this->m_distbuf_inv == nullptr) { - updateSize(); - float rad = max_ff(m_size * m_data.sizex, 0.0f); + if (distbuf_inv_ == nullptr) { + update_size(); + float rad = max_ff(size_ * data_.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } @@ -80,23 +76,23 @@ BLI_INLINE float finv_test(const float f, const bool test) return (LIKELY(test == false)) ? f : 1.0f - f; } -void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianAlphaXBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - const bool do_invert = this->m_do_subtract; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); - const rcti &input_rect = inputBuffer->get_rect(); + const bool do_invert = do_subtract_; + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); + const rcti &input_rect = input_buffer->get_rect(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - const rcti &rect = inputBuffer->get_rect(); - int xmin = max_ii(x - m_filtersize, rect.xmin); - int xmax = min_ii(x + m_filtersize + 1, rect.xmax); + const rcti &rect = input_buffer->get_rect(); + int xmin = max_ii(x - filtersize_, rect.xmin); + int xmax = min_ii(x + filtersize_ + 1, rect.xmax); int ymin = max_ii(y, rect.ymin); /* *** this is the main part which is different to 'GaussianXBlurOperation' *** */ - int step = getStep(); + int step = get_step(); int bufferindex = ((xmin - bufferstartx)) + ((ymin - bufferstarty) * bufferwidth); /* gauss */ @@ -110,20 +106,20 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo float distfacinv_max = 1.0f; /* 0 to 1 */ for (int nx = xmin; nx < xmax; nx += step) { - const int index = (nx - x) + this->m_filtersize; + const int index = (nx - x) + filtersize_; float value = finv_test(buffer[bufferindex], do_invert); float multiplier; /* gauss */ { - multiplier = this->m_gausstab[index]; + multiplier = gausstab_[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; } /* dilate - find most extreme color */ if (value > value_max) { - multiplier = this->m_distbuf_inv[index]; + multiplier = distbuf_inv_[index]; value *= multiplier; if (value > value_max) { value_max = value; @@ -139,54 +135,54 @@ void GaussianAlphaXBlurOperation::executePixel(float output[4], int x, int y, vo output[0] = finv_test(value_final, do_invert); } -void GaussianAlphaXBlurOperation::deinitExecution() +void GaussianAlphaXBlurOperation::deinit_execution() { - GaussianAlphaBlurBaseOperation::deinitExecution(); + GaussianAlphaBlurBaseOperation::deinit_execution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } - if (this->m_distbuf_inv) { - MEM_freeN(this->m_distbuf_inv); - this->m_distbuf_inv = nullptr; + if (distbuf_inv_) { + MEM_freeN(distbuf_inv_); + distbuf_inv_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -bool GaussianAlphaXBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianAlphaXBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; #if 0 /* until we add size input */ - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } else #endif { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { - newInput.xmax = input->xmax + this->m_filtersize + 1; - newInput.xmin = input->xmin - this->m_filtersize - 1; - newInput.ymax = input->ymax; - newInput.ymin = input->ymin; + if (sizeavailable_ && gausstab_ != nullptr) { + new_input.xmax = input->xmax + filtersize_ + 1; + new_input.xmin = input->xmin - filtersize_ - 1; + new_input.ymax = input->ymax; + new_input.ymin = input->ymin; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h index 2a44c639665..31b4a582c4b 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaXBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianAlphaXBlurOperation(); @@ -33,22 +33,22 @@ class GaussianAlphaXBlurOperation : public GaussianAlphaBlurBaseOperation { /** * \brief The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * \brief Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc index 09aeddb6573..90a80e6779a 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.cc @@ -17,10 +17,6 @@ */ #include "COM_GaussianAlphaYBlurOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { @@ -29,51 +25,51 @@ GaussianAlphaYBlurOperation::GaussianAlphaYBlurOperation() { } -void *GaussianAlphaYBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianAlphaYBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); - if (!this->m_sizeavailable) { - updateGauss(); + lock_mutex(); + if (!sizeavailable_) { + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianAlphaYBlurOperation::initExecution() +void GaussianAlphaYBlurOperation::init_execution() { - GaussianAlphaBlurBaseOperation::initExecution(); + GaussianAlphaBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizey, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizey, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianAlphaYBlurOperation::updateGauss() +void GaussianAlphaYBlurOperation::update_gauss() { - if (this->m_gausstab == nullptr) { - updateSize(); - float rad = max_ff(m_size * m_data.sizey, 0.0f); + if (gausstab_ == nullptr) { + update_size(); + float rad = max_ff(size_ * data_.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); } - if (this->m_distbuf_inv == nullptr) { - updateSize(); - float rad = max_ff(m_size * m_data.sizey, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (distbuf_inv_ == nullptr) { + update_size(); + float rad = max_ff(size_ * data_.sizey, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - m_distbuf_inv = BlurBaseOperation::make_dist_fac_inverse(rad, m_filtersize, m_falloff); + distbuf_inv_ = BlurBaseOperation::make_dist_fac_inverse(rad, filtersize_, falloff_); } } @@ -82,22 +78,22 @@ BLI_INLINE float finv_test(const float f, const bool test) return (LIKELY(test == false)) ? f : 1.0f - f; } -void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianAlphaYBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - const bool do_invert = this->m_do_subtract; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + const bool do_invert = do_subtract_; + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; int xmin = max_ii(x, input_rect.xmin); - int ymin = max_ii(y - m_filtersize, input_rect.ymin); - int ymax = min_ii(y + m_filtersize + 1, input_rect.ymax); + int ymin = max_ii(y - filtersize_, input_rect.ymin); + int ymax = min_ii(y + filtersize_ + 1, input_rect.ymax); /* *** this is the main part which is different to 'GaussianYBlurOperation' *** */ - int step = getStep(); + int step = get_step(); /* gauss */ float alpha_accum = 0.0f; @@ -112,20 +108,20 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo for (int ny = ymin; ny < ymax; ny += step) { int bufferindex = ((xmin - bufferstartx)) + ((ny - bufferstarty) * bufferwidth); - const int index = (ny - y) + this->m_filtersize; + const int index = (ny - y) + filtersize_; float value = finv_test(buffer[bufferindex], do_invert); float multiplier; /* gauss */ { - multiplier = this->m_gausstab[index]; + multiplier = gausstab_[index]; alpha_accum += value * multiplier; multiplier_accum += multiplier; } /* dilate - find most extreme color */ if (value > value_max) { - multiplier = this->m_distbuf_inv[index]; + multiplier = distbuf_inv_[index]; value *= multiplier; if (value > value_max) { value_max = value; @@ -140,54 +136,54 @@ void GaussianAlphaYBlurOperation::executePixel(float output[4], int x, int y, vo output[0] = finv_test(value_final, do_invert); } -void GaussianAlphaYBlurOperation::deinitExecution() +void GaussianAlphaYBlurOperation::deinit_execution() { - GaussianAlphaBlurBaseOperation::deinitExecution(); + GaussianAlphaBlurBaseOperation::deinit_execution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } - if (this->m_distbuf_inv) { - MEM_freeN(this->m_distbuf_inv); - this->m_distbuf_inv = nullptr; + if (distbuf_inv_) { + MEM_freeN(distbuf_inv_); + distbuf_inv_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -bool GaussianAlphaYBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianAlphaYBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; #if 0 /* until we add size input */ - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } else #endif { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { - newInput.xmax = input->xmax; - newInput.xmin = input->xmin; - newInput.ymax = input->ymax + this->m_filtersize + 1; - newInput.ymin = input->ymin - this->m_filtersize - 1; + if (sizeavailable_ && gausstab_ != nullptr) { + new_input.xmax = input->xmax; + new_input.xmin = input->xmin; + new_input.ymax = input->ymax + filtersize_ + 1; + new_input.ymin = input->ymin - filtersize_ - 1; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h index ef01f7e0f92..28dc289c445 100644 --- a/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianAlphaYBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianAlphaYBlurOperation : public GaussianAlphaBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianAlphaYBlurOperation(); @@ -33,22 +33,22 @@ class GaussianAlphaYBlurOperation : public GaussianAlphaBlurBaseOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc index 959f599fab4..9f4ee376fbf 100644 --- a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.cc @@ -23,11 +23,11 @@ namespace blender::compositor { GaussianBlurBaseOperation::GaussianBlurBaseOperation(eDimension dim) : BlurBaseOperation(DataType::Color) { - m_gausstab = nullptr; + gausstab_ = nullptr; #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = nullptr; + gausstab_sse_ = nullptr; #endif - m_filtersize = 0; + filtersize_ = 0; rad_ = 0.0f; dimension_ = dim; } @@ -36,35 +36,35 @@ void GaussianBlurBaseOperation::init_data() { BlurBaseOperation::init_data(); if (execution_model_ == eExecutionModel::FullFrame) { - rad_ = max_ff(m_size * this->get_blur_size(dimension_), 0.0f); + rad_ = max_ff(size_ * this->get_blur_size(dimension_), 0.0f); rad_ = min_ff(rad_, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad_), MAX_GAUSSTAB_RADIUS); } } -void GaussianBlurBaseOperation::initExecution() +void GaussianBlurBaseOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); if (execution_model_ == eExecutionModel::FullFrame) { - m_gausstab = BlurBaseOperation::make_gausstab(rad_, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad_, filtersize_); #ifdef BLI_HAVE_SSE2 - m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } -void GaussianBlurBaseOperation::deinitExecution() +void GaussianBlurBaseOperation::deinit_execution() { - BlurBaseOperation::deinitExecution(); + BlurBaseOperation::deinit_execution(); - if (m_gausstab) { - MEM_freeN(m_gausstab); - m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } #ifdef BLI_HAVE_SSE2 - if (m_gausstab_sse) { - MEM_freeN(m_gausstab_sse); - m_gausstab_sse = nullptr; + if (gausstab_sse_) { + MEM_freeN(gausstab_sse_); + gausstab_sse_ = nullptr; } #endif } @@ -81,12 +81,12 @@ void GaussianBlurBaseOperation::get_area_of_interest(const int input_idx, r_input_area = output_area; switch (dimension_) { case eDimension::X: - r_input_area.xmin = output_area.xmin - m_filtersize - 1; - r_input_area.xmax = output_area.xmax + m_filtersize + 1; + r_input_area.xmin = output_area.xmin - filtersize_ - 1; + r_input_area.xmax = output_area.xmax + filtersize_ + 1; break; case eDimension::Y: - r_input_area.ymin = output_area.ymin - m_filtersize - 1; - r_input_area.ymax = output_area.ymax + m_filtersize + 1; + r_input_area.ymin = output_area.ymin - filtersize_ - 1; + r_input_area.ymax = output_area.ymax + filtersize_ + 1; break; } } @@ -120,29 +120,29 @@ void GaussianBlurBaseOperation::update_memory_buffer_partial(MemoryBuffer *outpu for (; !it.is_end(); ++it) { const int coord = get_current_coord(); - const int coord_min = max_ii(coord - m_filtersize, min_input_coord); - const int coord_max = min_ii(coord + m_filtersize + 1, max_input_coord); + const int coord_min = max_ii(coord - filtersize_, min_input_coord); + const int coord_max = min_ii(coord + filtersize_ + 1, max_input_coord); float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float multiplier_accum = 0.0f; - const int step = QualityStepHelper::getStep(); + const int step = QualityStepHelper::get_step(); const float *in = it.in(0) + ((intptr_t)coord_min - coord) * elem_stride; const int in_stride = elem_stride * step; - int gauss_idx = (coord_min - coord) + m_filtersize; + int gauss_idx = (coord_min - coord) + filtersize_; const int gauss_end = gauss_idx + (coord_max - coord_min); #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) { __m128 reg_a = _mm_load_ps(in); - reg_a = _mm_mul_ps(reg_a, m_gausstab_sse[gauss_idx]); + reg_a = _mm_mul_ps(reg_a, gausstab_sse_[gauss_idx]); accum_r = _mm_add_ps(accum_r, reg_a); - multiplier_accum += m_gausstab[gauss_idx]; + multiplier_accum += gausstab_[gauss_idx]; } _mm_store_ps(color_accum, accum_r); #else for (; gauss_idx < gauss_end; in += in_stride, gauss_idx += step) { - const float multiplier = m_gausstab[gauss_idx]; + const float multiplier = gausstab_[gauss_idx]; madd_v4_v4fl(color_accum, in, multiplier); multiplier_accum += multiplier; } diff --git a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h index c0b27078a24..31b045241f8 100644 --- a/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h +++ b/source/blender/compositor/operations/COM_GaussianBlurBaseOperation.h @@ -24,11 +24,11 @@ namespace blender::compositor { class GaussianBlurBaseOperation : public BlurBaseOperation { protected: - float *m_gausstab; + float *gausstab_; #ifdef BLI_HAVE_SSE2 - __m128 *m_gausstab_sse; + __m128 *gausstab_sse_; #endif - int m_filtersize; + int filtersize_; float rad_; eDimension dimension_; @@ -36,8 +36,8 @@ class GaussianBlurBaseOperation : public BlurBaseOperation { GaussianBlurBaseOperation(eDimension dim); virtual void init_data() override; - virtual void initExecution() override; - virtual void deinitExecution() override; + virtual void init_execution() override; + virtual void deinit_execution() override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc index aafc269abac..db5f9c7c35d 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.cc @@ -17,8 +17,6 @@ */ #include "COM_GaussianBokehBlurOperation.h" -#include "BLI_math.h" -#include "MEM_guardedalloc.h" #include "RE_pipeline.h" @@ -26,57 +24,57 @@ namespace blender::compositor { GaussianBokehBlurOperation::GaussianBokehBlurOperation() : BlurBaseOperation(DataType::Color) { - this->m_gausstab = nullptr; + gausstab_ = nullptr; } -void *GaussianBokehBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianBokehBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); - if (!this->m_sizeavailable) { - updateGauss(); + lock_mutex(); + if (!sizeavailable_) { + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } void GaussianBokehBlurOperation::init_data() { BlurBaseOperation::init_data(); - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); - if (!this->m_sizeavailable) { - updateSize(); + if (!sizeavailable_) { + update_size(); } - radxf_ = this->m_size * (float)this->m_data.sizex; + radxf_ = size_ * (float)data_.sizex; CLAMP(radxf_, 0.0f, width / 2.0f); /* Vertical. */ - radyf_ = this->m_size * (float)this->m_data.sizey; + radyf_ = size_ * (float)data_.sizey; CLAMP(radyf_, 0.0f, height / 2.0f); - this->m_radx = ceil(radxf_); - this->m_rady = ceil(radyf_); + radx_ = ceil(radxf_); + rady_ = ceil(radyf_); } -void GaussianBokehBlurOperation::initExecution() +void GaussianBokehBlurOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); - if (this->m_sizeavailable) { - updateGauss(); + if (sizeavailable_) { + update_gauss(); } } -void GaussianBokehBlurOperation::updateGauss() +void GaussianBokehBlurOperation::update_gauss() { - if (this->m_gausstab == nullptr) { - int ddwidth = 2 * this->m_radx + 1; - int ddheight = 2 * this->m_rady + 1; + if (gausstab_ == nullptr) { + int ddwidth = 2 * radx_ + 1; + int ddheight = 2 * rady_ + 1; int n = ddwidth * ddheight; /* create a full filter image */ float *ddgauss = (float *)MEM_mallocN(sizeof(float) * n, __func__); @@ -84,12 +82,12 @@ void GaussianBokehBlurOperation::updateGauss() float sum = 0.0f; float facx = (radxf_ > 0.0f ? 1.0f / radxf_ : 0.0f); float facy = (radyf_ > 0.0f ? 1.0f / radyf_ : 0.0f); - for (int j = -this->m_rady; j <= this->m_rady; j++) { - for (int i = -this->m_radx; i <= this->m_radx; i++, dgauss++) { + for (int j = -rady_; j <= rady_; j++) { + for (int i = -radx_; i <= radx_; i++, dgauss++) { float fj = (float)j * facy; float fi = (float)i * facx; float dist = sqrt(fj * fj + fi * fi); - *dgauss = RE_filter_value(this->m_data.filtertype, dist); + *dgauss = RE_filter_value(data_.filtertype, dist); sum += *dgauss; } @@ -103,96 +101,97 @@ void GaussianBokehBlurOperation::updateGauss() } } else { - int center = m_rady * ddwidth + m_radx; + int center = rady_ * ddwidth + radx_; ddgauss[center] = 1.0f; } - this->m_gausstab = ddgauss; + gausstab_ = ddgauss; } } -void GaussianBokehBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - float tempColor[4]; - tempColor[0] = 0; - tempColor[1] = 0; - tempColor[2] = 0; - tempColor[3] = 0; + float temp_color[4]; + temp_color[0] = 0; + temp_color[1] = 0; + temp_color[2] = 0; + temp_color[3] = 0; float multiplier_accum = 0; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); - const rcti &input_rect = inputBuffer->get_rect(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); + const rcti &input_rect = input_buffer->get_rect(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - int ymin = max_ii(y - this->m_rady, input_rect.ymin); - int ymax = min_ii(y + this->m_rady + 1, input_rect.ymax); - int xmin = max_ii(x - this->m_radx, input_rect.xmin); - int xmax = min_ii(x + this->m_radx + 1, input_rect.xmax); + int ymin = max_ii(y - rady_, input_rect.ymin); + int ymax = min_ii(y + rady_ + 1, input_rect.ymax); + int xmin = max_ii(x - radx_, input_rect.xmin); + int xmax = min_ii(x + radx_ + 1, input_rect.xmax); int index; - int step = QualityStepHelper::getStep(); - int offsetadd = QualityStepHelper::getOffsetAdd(); - const int addConst = (xmin - x + this->m_radx); - const int mulConst = (this->m_radx * 2 + 1); + int step = QualityStepHelper::get_step(); + int offsetadd = QualityStepHelper::get_offset_add(); + const int add_const = (xmin - x + radx_); + const int mul_const = (radx_ * 2 + 1); for (int ny = ymin; ny < ymax; ny += step) { - index = ((ny - y) + this->m_rady) * mulConst + addConst; + index = ((ny - y) + rady_) * mul_const + add_const; int bufferindex = ((xmin - bufferstartx) * 4) + ((ny - bufferstarty) * 4 * bufferwidth); for (int nx = xmin; nx < xmax; nx += step) { - const float multiplier = this->m_gausstab[index]; - madd_v4_v4fl(tempColor, &buffer[bufferindex], multiplier); + const float multiplier = gausstab_[index]; + madd_v4_v4fl(temp_color, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; index += step; bufferindex += offsetadd; } } - mul_v4_v4fl(output, tempColor, 1.0f / multiplier_accum); + mul_v4_v4fl(output, temp_color, 1.0f / multiplier_accum); } -void GaussianBokehBlurOperation::deinitExecution() +void GaussianBokehBlurOperation::deinit_execution() { - BlurBaseOperation::deinitExecution(); + BlurBaseOperation::deinit_execution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } - deinitMutex(); + deinit_mutex(); } -bool GaussianBokehBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianBokehBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); + rcti new_input; + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + NodeOperation *operation = this->get_input_operation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } - if (this->m_sizeavailable && this->m_gausstab != nullptr) { - newInput.xmin = 0; - newInput.ymin = 0; - newInput.xmax = this->getWidth(); - newInput.ymax = this->getHeight(); + if (sizeavailable_ && gausstab_ != nullptr) { + new_input.xmin = 0; + new_input.ymin = 0; + new_input.xmax = this->get_width(); + new_input.ymax = this->get_height(); } else { - int addx = this->m_radx; - int addy = this->m_rady; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; + int addx = radx_; + int addy = rady_; + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; } - return BlurBaseOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return BlurBaseOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } void GaussianBokehBlurOperation::get_area_of_interest(const int input_idx, @@ -204,10 +203,10 @@ void GaussianBokehBlurOperation::get_area_of_interest(const int input_idx, return; } - r_input_area.xmax = output_area.xmax + m_radx; - r_input_area.xmin = output_area.xmin - m_radx; - r_input_area.ymax = output_area.ymax + m_rady; - r_input_area.ymin = output_area.ymin - m_rady; + r_input_area.xmax = output_area.xmax + radx_; + r_input_area.xmin = output_area.xmin - radx_; + r_input_area.ymax = output_area.ymax + rady_; + r_input_area.ymin = output_area.ymin - rady_; } void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -221,29 +220,29 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp const int x = it.x; const int y = it.y; - const int ymin = max_ii(y - this->m_rady, input_rect.ymin); - const int ymax = min_ii(y + this->m_rady + 1, input_rect.ymax); - const int xmin = max_ii(x - this->m_radx, input_rect.xmin); - const int xmax = min_ii(x + this->m_radx + 1, input_rect.xmax); + const int ymin = max_ii(y - rady_, input_rect.ymin); + const int ymax = min_ii(y + rady_ + 1, input_rect.ymax); + const int xmin = max_ii(x - radx_, input_rect.xmin); + const int xmax = min_ii(x + radx_ + 1, input_rect.xmax); - float tempColor[4] = {0}; + float temp_color[4] = {0}; float multiplier_accum = 0; - const int step = QualityStepHelper::getStep(); + const int step = QualityStepHelper::get_step(); const int elem_step = step * input->elem_stride; - const int add_const = (xmin - x + this->m_radx); - const int mul_const = (this->m_radx * 2 + 1); + const int add_const = (xmin - x + radx_); + const int mul_const = (radx_ * 2 + 1); for (int ny = ymin; ny < ymax; ny += step) { const float *color = input->get_elem(xmin, ny); - int gauss_index = ((ny - y) + this->m_rady) * mul_const + add_const; + int gauss_index = ((ny - y) + rady_) * mul_const + add_const; const int gauss_end = gauss_index + (xmax - xmin); for (; gauss_index < gauss_end; gauss_index += step, color += elem_step) { - const float multiplier = this->m_gausstab[gauss_index]; - madd_v4_v4fl(tempColor, color, multiplier); + const float multiplier = gausstab_[gauss_index]; + madd_v4_v4fl(temp_color, color, multiplier); multiplier_accum += multiplier; } } - mul_v4_v4fl(it.out, tempColor, 1.0f / multiplier_accum); + mul_v4_v4fl(it.out, temp_color, 1.0f / multiplier_accum); } } @@ -251,110 +250,110 @@ void GaussianBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer *outp GaussianBlurReferenceOperation::GaussianBlurReferenceOperation() : BlurBaseOperation(DataType::Color) { - this->m_maintabs = nullptr; + maintabs_ = nullptr; use_variable_size_ = true; } void GaussianBlurReferenceOperation::init_data() { /* Setup variables for gausstab and area of interest. */ - this->m_data.image_in_width = this->getWidth(); - this->m_data.image_in_height = this->getHeight(); - if (this->m_data.relative) { - switch (this->m_data.aspect) { + data_.image_in_width = this->get_width(); + data_.image_in_height = this->get_height(); + if (data_.relative) { + switch (data_.aspect) { case CMP_NODE_BLUR_ASPECT_NONE: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_width); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height); + data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_width); + data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_height); break; case CMP_NODE_BLUR_ASPECT_Y: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_width); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_width); + data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_width); + data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_width); break; case CMP_NODE_BLUR_ASPECT_X: - this->m_data.sizex = (int)(this->m_data.percentx * 0.01f * this->m_data.image_in_height); - this->m_data.sizey = (int)(this->m_data.percenty * 0.01f * this->m_data.image_in_height); + data_.sizex = (int)(data_.percentx * 0.01f * data_.image_in_height); + data_.sizey = (int)(data_.percenty * 0.01f * data_.image_in_height); break; } } /* Horizontal. */ - m_filtersizex = (float)this->m_data.sizex; - int imgx = getWidth() / 2; - if (m_filtersizex > imgx) { - m_filtersizex = imgx; + filtersizex_ = (float)data_.sizex; + int imgx = get_width() / 2; + if (filtersizex_ > imgx) { + filtersizex_ = imgx; } - else if (m_filtersizex < 1) { - m_filtersizex = 1; + else if (filtersizex_ < 1) { + filtersizex_ = 1; } - m_radx = (float)m_filtersizex; + radx_ = (float)filtersizex_; /* Vertical. */ - m_filtersizey = (float)this->m_data.sizey; - int imgy = getHeight() / 2; - if (m_filtersizey > imgy) { - m_filtersizey = imgy; + filtersizey_ = (float)data_.sizey; + int imgy = get_height() / 2; + if (filtersizey_ > imgy) { + filtersizey_ = imgy; } - else if (m_filtersizey < 1) { - m_filtersizey = 1; + else if (filtersizey_ < 1) { + filtersizey_ = 1; } - m_rady = (float)m_filtersizey; + rady_ = (float)filtersizey_; } -void *GaussianBlurReferenceOperation::initializeTileData(rcti * /*rect*/) +void *GaussianBlurReferenceOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = getInputOperation(0)->initializeTileData(nullptr); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); return buffer; } -void GaussianBlurReferenceOperation::initExecution() +void GaussianBlurReferenceOperation::init_execution() { - BlurBaseOperation::initExecution(); + BlurBaseOperation::init_execution(); - updateGauss(); + update_gauss(); } -void GaussianBlurReferenceOperation::updateGauss() +void GaussianBlurReferenceOperation::update_gauss() { int i; - int x = MAX2(m_filtersizex, m_filtersizey); - m_maintabs = (float **)MEM_mallocN(x * sizeof(float *), "gauss array"); + int x = MAX2(filtersizex_, filtersizey_); + maintabs_ = (float **)MEM_mallocN(x * sizeof(float *), "gauss array"); for (i = 0; i < x; i++) { - m_maintabs[i] = make_gausstab(i + 1, i + 1); + maintabs_[i] = make_gausstab(i + 1, i + 1); } } -void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianBlurReferenceOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *memorybuffer = (MemoryBuffer *)data; - float *buffer = memorybuffer->getBuffer(); + float *buffer = memorybuffer->get_buffer(); float *gausstabx, *gausstabcenty; float *gausstaby, *gausstabcentx; int i, j; float *src; float sum, val; float rval, gval, bval, aval; - int imgx = getWidth(); - int imgy = getHeight(); - float tempSize[4]; - this->m_inputSize->read(tempSize, x, y, data); - float refSize = tempSize[0]; - int refradx = (int)(refSize * m_radx); - int refrady = (int)(refSize * m_rady); - if (refradx > m_filtersizex) { - refradx = m_filtersizex; + int imgx = get_width(); + int imgy = get_height(); + float temp_size[4]; + input_size_->read(temp_size, x, y, data); + float ref_size = temp_size[0]; + int refradx = (int)(ref_size * radx_); + int refrady = (int)(ref_size * rady_); + if (refradx > filtersizex_) { + refradx = filtersizex_; } else if (refradx < 1) { refradx = 1; } - if (refrady > m_filtersizey) { - refrady = m_filtersizey; + if (refrady > filtersizey_) { + refrady = filtersizey_; } else if (refrady < 1) { refrady = 1; } if (refradx == 1 && refrady == 1) { - memorybuffer->readNoCheck(output, x, y); + memorybuffer->read_no_check(output, x, y); } else { int minxr = x - refradx < 0 ? -x : -refradx; @@ -364,9 +363,9 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, float *srcd = buffer + COM_DATA_TYPE_COLOR_CHANNELS * ((y + minyr) * imgx + x + minxr); - gausstabx = m_maintabs[refradx - 1]; + gausstabx = maintabs_[refradx - 1]; gausstabcentx = gausstabx + refradx; - gausstaby = m_maintabs[refrady - 1]; + gausstaby = maintabs_[refrady - 1]; gausstabcenty = gausstaby + refrady; sum = gval = rval = bval = aval = 0.0f; @@ -390,34 +389,34 @@ void GaussianBlurReferenceOperation::executePixel(float output[4], int x, int y, } } -void GaussianBlurReferenceOperation::deinitExecution() +void GaussianBlurReferenceOperation::deinit_execution() { int x, i; - x = MAX2(this->m_filtersizex, this->m_filtersizey); + x = MAX2(filtersizex_, filtersizey_); for (i = 0; i < x; i++) { - MEM_freeN(this->m_maintabs[i]); + MEM_freeN(maintabs_[i]); } - MEM_freeN(this->m_maintabs); - BlurBaseOperation::deinitExecution(); + MEM_freeN(maintabs_); + BlurBaseOperation::deinit_execution(); } -bool GaussianBlurReferenceOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool GaussianBlurReferenceOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - NodeOperation *operation = this->getInputOperation(1); + rcti new_input; + NodeOperation *operation = this->get_input_operation(1); - if (operation->determineDependingAreaOfInterest(input, readOperation, output)) { + if (operation->determine_depending_area_of_interest(input, read_operation, output)) { return true; } - int addx = this->m_data.sizex + 2; - int addy = this->m_data.sizey + 2; - newInput.xmax = input->xmax + addx; - newInput.xmin = input->xmin - addx; - newInput.ymax = input->ymax + addy; - newInput.ymin = input->ymin - addy; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + int addx = data_.sizex + 2; + int addy = data_.sizey + 2; + new_input.xmax = input->xmax + addx; + new_input.xmin = input->xmin - addx; + new_input.ymax = input->ymax + addy; + new_input.ymin = input->ymin - addy; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx, @@ -429,8 +428,8 @@ void GaussianBlurReferenceOperation::get_area_of_interest(const int input_idx, return; } - const int add_x = this->m_data.sizex + 2; - const int add_y = this->m_data.sizey + 2; + const int add_x = data_.sizex + 2; + const int add_y = data_.sizey + 2; r_input_area.xmax = output_area.xmax + add_x; r_input_area.xmin = output_area.xmin - add_x; r_input_area.ymax = output_area.ymax + add_y; @@ -445,16 +444,16 @@ void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer * MemoryBuffer *size_input = inputs[SIZE_INPUT_INDEX]; for (BuffersIterator it = output->iterate_with({size_input}, area); !it.is_end(); ++it) { const float ref_size = *it.in(0); - int ref_radx = (int)(ref_size * m_radx); - int ref_rady = (int)(ref_size * m_rady); - if (ref_radx > m_filtersizex) { - ref_radx = m_filtersizex; + int ref_radx = (int)(ref_size * radx_); + int ref_rady = (int)(ref_size * rady_); + if (ref_radx > filtersizex_) { + ref_radx = filtersizex_; } else if (ref_radx < 1) { ref_radx = 1; } - if (ref_rady > m_filtersizey) { - ref_rady = m_filtersizey; + if (ref_rady > filtersizey_) { + ref_rady = filtersizey_; } else if (ref_rady < 1) { ref_rady = 1; @@ -467,16 +466,16 @@ void GaussianBlurReferenceOperation::update_memory_buffer_partial(MemoryBuffer * continue; } - const int w = getWidth(); - const int height = getHeight(); + const int w = get_width(); + const int height = get_height(); const int minxr = x - ref_radx < 0 ? -x : -ref_radx; const int maxxr = x + ref_radx > w ? w - x : ref_radx; const int minyr = y - ref_rady < 0 ? -y : -ref_rady; const int maxyr = y + ref_rady > height ? height - y : ref_rady; - const float *gausstabx = m_maintabs[ref_radx - 1]; + const float *gausstabx = maintabs_[ref_radx - 1]; const float *gausstabcentx = gausstabx + ref_radx; - const float *gausstaby = m_maintabs[ref_rady - 1]; + const float *gausstaby = maintabs_[ref_rady - 1]; const float *gausstabcenty = gausstaby + ref_rady; float gauss_sum = 0.0f; diff --git a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h index a64b5b327b0..923daf7a447 100644 --- a/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianBokehBlurOperation.h @@ -26,30 +26,30 @@ namespace blender::compositor { class GaussianBokehBlurOperation : public BlurBaseOperation { private: - float *m_gausstab; - int m_radx, m_rady; + float *gausstab_; + int radx_, rady_; float radxf_; float radyf_; - void updateGauss(); + void update_gauss(); public: GaussianBokehBlurOperation(); void init_data() override; - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, @@ -61,32 +61,32 @@ class GaussianBokehBlurOperation : public BlurBaseOperation { class GaussianBlurReferenceOperation : public BlurBaseOperation { private: - float **m_maintabs; + float **maintabs_; - void updateGauss(); - int m_filtersizex; - int m_filtersizey; - float m_radx; - float m_rady; + void update_gauss(); + int filtersizex_; + int filtersizey_; + float radx_; + float rady_; public: GaussianBlurReferenceOperation(); void init_data() override; - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc index 8d686265231..b43db5ba664 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.cc @@ -17,11 +17,7 @@ */ #include "COM_GaussianXBlurOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { @@ -29,86 +25,84 @@ GaussianXBlurOperation::GaussianXBlurOperation() : GaussianBlurBaseOperation(eDi { } -void *GaussianXBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianXBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); - if (!this->m_sizeavailable) { - updateGauss(); + lock_mutex(); + if (!sizeavailable_) { + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianXBlurOperation::initExecution() +void GaussianXBlurOperation::init_execution() { - GaussianBlurBaseOperation::initExecution(); + GaussianBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizex, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizex, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); /* TODO(sergey): De-duplicate with the case below and Y blur. */ - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } /* TODO(manzanilla): to be removed with tiled implementation. */ -void GaussianXBlurOperation::updateGauss() +void GaussianXBlurOperation::update_gauss() { - if (this->m_gausstab == nullptr) { - updateSize(); - float rad = max_ff(m_size * m_data.sizex, 0.0f); + if (gausstab_ == nullptr) { + update_size(); + float rad = max_ff(size_ * data_.sizex, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } -void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianXBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float multiplier_accum = 0.0f; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; - int xmin = max_ii(x - m_filtersize, input_rect.xmin); - int xmax = min_ii(x + m_filtersize + 1, input_rect.xmax); + int xmin = max_ii(x - filtersize_, input_rect.xmin); + int xmax = min_ii(x + filtersize_ + 1, input_rect.xmax); int ymin = max_ii(y, input_rect.ymin); - int step = getStep(); - int offsetadd = getOffsetAdd(); + int step = get_step(); + int offsetadd = get_offset_add(); int bufferindex = ((xmin - bufferstartx) * 4) + ((ymin - bufferstarty) * 4 * bufferwidth); #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); - for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; - nx += step, index += step) { + for (int nx = xmin, index = (xmin - x) + filtersize_; nx < xmax; nx += step, index += step) { __m128 reg_a = _mm_load_ps(&buffer[bufferindex]); - reg_a = _mm_mul_ps(reg_a, this->m_gausstab_sse[index]); + reg_a = _mm_mul_ps(reg_a, gausstab_sse_[index]); accum_r = _mm_add_ps(accum_r, reg_a); - multiplier_accum += this->m_gausstab[index]; + multiplier_accum += gausstab_[index]; bufferindex += offsetadd; } _mm_store_ps(color_accum, accum_r); #else - for (int nx = xmin, index = (xmin - x) + this->m_filtersize; nx < xmax; - nx += step, index += step) { - const float multiplier = this->m_gausstab[index]; + for (int nx = xmin, index = (xmin - x) + filtersize_; nx < xmax; nx += step, index += step) { + const float multiplier = gausstab_[index]; madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; bufferindex += offsetadd; @@ -117,91 +111,90 @@ void GaussianXBlurOperation::executePixel(float output[4], int x, int y, void *d mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum); } -void GaussianXBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void GaussianXBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel gaussianXBlurOperationKernel = device->COM_clCreateKernel( - "gaussianXBlurOperationKernel", nullptr); - cl_int filter_size = this->m_filtersize; + cl_kernel gaussian_xblur_operation_kernel = device->COM_cl_create_kernel( + "gaussian_xblur_operation_kernel", nullptr); + cl_int filter_size = filtersize_; - cl_mem gausstab = clCreateBuffer(device->getContext(), + cl_mem gausstab = clCreateBuffer(device->get_context(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, - sizeof(float) * (this->m_filtersize * 2 + 1), - this->m_gausstab, + sizeof(float) * (filtersize_ * 2 + 1), + gausstab_, nullptr); - device->COM_clAttachMemoryBufferToKernelParameter(gaussianXBlurOperationKernel, - 0, - 1, - clMemToCleanUp, - inputMemoryBuffers, - this->m_inputProgram); - device->COM_clAttachOutputMemoryBufferToKernelParameter( - gaussianXBlurOperationKernel, 2, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter( - gaussianXBlurOperationKernel, 3, outputMemoryBuffer); - clSetKernelArg(gaussianXBlurOperationKernel, 4, sizeof(cl_int), &filter_size); - device->COM_clAttachSizeToKernelParameter(gaussianXBlurOperationKernel, 5, this); - clSetKernelArg(gaussianXBlurOperationKernel, 6, sizeof(cl_mem), &gausstab); + device->COM_cl_attach_memory_buffer_to_kernel_parameter(gaussian_xblur_operation_kernel, + 0, + 1, + cl_mem_to_clean_up, + input_memory_buffers, + input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + gaussian_xblur_operation_kernel, 2, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + gaussian_xblur_operation_kernel, 3, output_memory_buffer); + clSetKernelArg(gaussian_xblur_operation_kernel, 4, sizeof(cl_int), &filter_size); + device->COM_cl_attach_size_to_kernel_parameter(gaussian_xblur_operation_kernel, 5, this); + clSetKernelArg(gaussian_xblur_operation_kernel, 6, sizeof(cl_mem), &gausstab); - device->COM_clEnqueueRange(gaussianXBlurOperationKernel, outputMemoryBuffer, 7, this); + device->COM_cl_enqueue_range(gaussian_xblur_operation_kernel, output_memory_buffer, 7, this); clReleaseMemObject(gausstab); } -void GaussianXBlurOperation::deinitExecution() +void GaussianXBlurOperation::deinit_execution() { - GaussianBlurBaseOperation::deinitExecution(); + GaussianBlurBaseOperation::deinit_execution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } #ifdef BLI_HAVE_SSE2 - if (this->m_gausstab_sse) { - MEM_freeN(this->m_gausstab_sse); - this->m_gausstab_sse = nullptr; + if (gausstab_sse_) { + MEM_freeN(gausstab_sse_); + gausstab_sse_ = nullptr; } #endif - deinitMutex(); + deinit_mutex(); } -bool GaussianXBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool GaussianXBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - if (!this->m_sizeavailable) { - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + if (!sizeavailable_) { + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } } { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { - newInput.xmax = input->xmax + this->m_filtersize + 1; - newInput.xmin = input->xmin - this->m_filtersize - 1; - newInput.ymax = input->ymax; - newInput.ymin = input->ymin; + if (sizeavailable_ && gausstab_ != nullptr) { + new_input.xmax = input->xmax + filtersize_ + 1; + new_input.xmin = input->xmin - filtersize_ - 1; + new_input.ymax = input->ymax; + new_input.ymin = input->ymin; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h index e09e57bad67..72356381da3 100644 --- a/source/blender/compositor/operations/COM_GaussianXBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianXBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianXBlurOperation : public GaussianBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianXBlurOperation(); @@ -33,33 +33,33 @@ class GaussianXBlurOperation : public GaussianBlurBaseOperation { /** * \brief The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * \brief Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void checkOpenCL() + void check_opencl() { - flags.open_cl = (m_data.sizex >= 128); + flags_.open_cl = (data_.sizex >= 128); } }; diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc index 32d469a0ae4..639536ebcc1 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.cc @@ -17,11 +17,7 @@ */ #include "COM_GaussianYBlurOperation.h" -#include "BLI_math.h" #include "COM_OpenCLDevice.h" -#include "MEM_guardedalloc.h" - -#include "RE_pipeline.h" namespace blender::compositor { @@ -29,85 +25,85 @@ GaussianYBlurOperation::GaussianYBlurOperation() : GaussianBlurBaseOperation(eDi { } -void *GaussianYBlurOperation::initializeTileData(rcti * /*rect*/) +void *GaussianYBlurOperation::initialize_tile_data(rcti * /*rect*/) { - lockMutex(); - if (!this->m_sizeavailable) { - updateGauss(); + lock_mutex(); + if (!sizeavailable_) { + update_gauss(); } - void *buffer = getInputOperation(0)->initializeTileData(nullptr); - unlockMutex(); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); + unlock_mutex(); return buffer; } -void GaussianYBlurOperation::initExecution() +void GaussianYBlurOperation::init_execution() { - GaussianBlurBaseOperation::initExecution(); + GaussianBlurBaseOperation::init_execution(); - initMutex(); + init_mutex(); - if (this->m_sizeavailable && execution_model_ == eExecutionModel::Tiled) { - float rad = max_ff(m_size * m_data.sizey, 0.0f); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + if (sizeavailable_ && execution_model_ == eExecutionModel::Tiled) { + float rad = max_ff(size_ * data_.sizey, 0.0f); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } -void GaussianYBlurOperation::updateGauss() +void GaussianYBlurOperation::update_gauss() { - if (this->m_gausstab == nullptr) { - updateSize(); - float rad = max_ff(m_size * m_data.sizey, 0.0f); + if (gausstab_ == nullptr) { + update_size(); + float rad = max_ff(size_ * data_.sizey, 0.0f); rad = min_ff(rad, MAX_GAUSSTAB_RADIUS); - m_filtersize = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); + filtersize_ = min_ii(ceil(rad), MAX_GAUSSTAB_RADIUS); - this->m_gausstab = BlurBaseOperation::make_gausstab(rad, m_filtersize); + gausstab_ = BlurBaseOperation::make_gausstab(rad, filtersize_); #ifdef BLI_HAVE_SSE2 - this->m_gausstab_sse = BlurBaseOperation::convert_gausstab_sse(this->m_gausstab, m_filtersize); + gausstab_sse_ = BlurBaseOperation::convert_gausstab_sse(gausstab_, filtersize_); #endif } } -void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *data) +void GaussianYBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float ATTR_ALIGN(16) color_accum[4] = {0.0f, 0.0f, 0.0f, 0.0f}; float multiplier_accum = 0.0f; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const rcti &input_rect = inputBuffer->get_rect(); - float *buffer = inputBuffer->getBuffer(); - int bufferwidth = inputBuffer->getWidth(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const rcti &input_rect = input_buffer->get_rect(); + float *buffer = input_buffer->get_buffer(); + int bufferwidth = input_buffer->get_width(); int bufferstartx = input_rect.xmin; int bufferstarty = input_rect.ymin; int xmin = max_ii(x, input_rect.xmin); - int ymin = max_ii(y - m_filtersize, input_rect.ymin); - int ymax = min_ii(y + m_filtersize + 1, input_rect.ymax); + int ymin = max_ii(y - filtersize_, input_rect.ymin); + int ymax = min_ii(y + filtersize_ + 1, input_rect.ymax); int index; - int step = getStep(); - const int bufferIndexx = ((xmin - bufferstartx) * 4); + int step = get_step(); + const int buffer_indexx = ((xmin - bufferstartx) * 4); #ifdef BLI_HAVE_SSE2 __m128 accum_r = _mm_load_ps(color_accum); for (int ny = ymin; ny < ymax; ny += step) { - index = (ny - y) + this->m_filtersize; - int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); - const float multiplier = this->m_gausstab[index]; + index = (ny - y) + filtersize_; + int bufferindex = buffer_indexx + ((ny - bufferstarty) * 4 * bufferwidth); + const float multiplier = gausstab_[index]; __m128 reg_a = _mm_load_ps(&buffer[bufferindex]); - reg_a = _mm_mul_ps(reg_a, this->m_gausstab_sse[index]); + reg_a = _mm_mul_ps(reg_a, gausstab_sse_[index]); accum_r = _mm_add_ps(accum_r, reg_a); multiplier_accum += multiplier; } _mm_store_ps(color_accum, accum_r); #else for (int ny = ymin; ny < ymax; ny += step) { - index = (ny - y) + this->m_filtersize; - int bufferindex = bufferIndexx + ((ny - bufferstarty) * 4 * bufferwidth); - const float multiplier = this->m_gausstab[index]; + index = (ny - y) + filtersize_; + int bufferindex = buffer_indexx + ((ny - bufferstarty) * 4 * bufferwidth); + const float multiplier = gausstab_[index]; madd_v4_v4fl(color_accum, &buffer[bufferindex], multiplier); multiplier_accum += multiplier; } @@ -115,91 +111,90 @@ void GaussianYBlurOperation::executePixel(float output[4], int x, int y, void *d mul_v4_v4fl(output, color_accum, 1.0f / multiplier_accum); } -void GaussianYBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void GaussianYBlurOperation::execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel gaussianYBlurOperationKernel = device->COM_clCreateKernel( - "gaussianYBlurOperationKernel", nullptr); - cl_int filter_size = this->m_filtersize; + cl_kernel gaussian_yblur_operation_kernel = device->COM_cl_create_kernel( + "gaussian_yblur_operation_kernel", nullptr); + cl_int filter_size = filtersize_; - cl_mem gausstab = clCreateBuffer(device->getContext(), + cl_mem gausstab = clCreateBuffer(device->get_context(), CL_MEM_READ_ONLY | CL_MEM_USE_HOST_PTR, - sizeof(float) * (this->m_filtersize * 2 + 1), - this->m_gausstab, + sizeof(float) * (filtersize_ * 2 + 1), + gausstab_, nullptr); - device->COM_clAttachMemoryBufferToKernelParameter(gaussianYBlurOperationKernel, - 0, - 1, - clMemToCleanUp, - inputMemoryBuffers, - this->m_inputProgram); - device->COM_clAttachOutputMemoryBufferToKernelParameter( - gaussianYBlurOperationKernel, 2, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter( - gaussianYBlurOperationKernel, 3, outputMemoryBuffer); - clSetKernelArg(gaussianYBlurOperationKernel, 4, sizeof(cl_int), &filter_size); - device->COM_clAttachSizeToKernelParameter(gaussianYBlurOperationKernel, 5, this); - clSetKernelArg(gaussianYBlurOperationKernel, 6, sizeof(cl_mem), &gausstab); + device->COM_cl_attach_memory_buffer_to_kernel_parameter(gaussian_yblur_operation_kernel, + 0, + 1, + cl_mem_to_clean_up, + input_memory_buffers, + input_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + gaussian_yblur_operation_kernel, 2, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + gaussian_yblur_operation_kernel, 3, output_memory_buffer); + clSetKernelArg(gaussian_yblur_operation_kernel, 4, sizeof(cl_int), &filter_size); + device->COM_cl_attach_size_to_kernel_parameter(gaussian_yblur_operation_kernel, 5, this); + clSetKernelArg(gaussian_yblur_operation_kernel, 6, sizeof(cl_mem), &gausstab); - device->COM_clEnqueueRange(gaussianYBlurOperationKernel, outputMemoryBuffer, 7, this); + device->COM_cl_enqueue_range(gaussian_yblur_operation_kernel, output_memory_buffer, 7, this); clReleaseMemObject(gausstab); } -void GaussianYBlurOperation::deinitExecution() +void GaussianYBlurOperation::deinit_execution() { - GaussianBlurBaseOperation::deinitExecution(); + GaussianBlurBaseOperation::deinit_execution(); - if (this->m_gausstab) { - MEM_freeN(this->m_gausstab); - this->m_gausstab = nullptr; + if (gausstab_) { + MEM_freeN(gausstab_); + gausstab_ = nullptr; } #ifdef BLI_HAVE_SSE2 - if (this->m_gausstab_sse) { - MEM_freeN(this->m_gausstab_sse); - this->m_gausstab_sse = nullptr; + if (gausstab_sse_) { + MEM_freeN(gausstab_sse_); + gausstab_sse_ = nullptr; } #endif - deinitMutex(); + deinit_mutex(); } -bool GaussianYBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool GaussianYBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - if (!m_sizeavailable) { - rcti sizeInput; - sizeInput.xmin = 0; - sizeInput.ymin = 0; - sizeInput.xmax = 5; - sizeInput.ymax = 5; - NodeOperation *operation = this->getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&sizeInput, readOperation, output)) { + if (!sizeavailable_) { + rcti size_input; + size_input.xmin = 0; + size_input.ymin = 0; + size_input.xmax = 5; + size_input.ymax = 5; + NodeOperation *operation = this->get_input_operation(1); + if (operation->determine_depending_area_of_interest(&size_input, read_operation, output)) { return true; } } { - if (this->m_sizeavailable && this->m_gausstab != nullptr) { - newInput.xmax = input->xmax; - newInput.xmin = input->xmin; - newInput.ymax = input->ymax + this->m_filtersize + 1; - newInput.ymin = input->ymin - this->m_filtersize - 1; + if (sizeavailable_ && gausstab_ != nullptr) { + new_input.xmax = input->xmax; + new_input.xmin = input->xmin; + new_input.ymax = input->ymax + filtersize_ + 1; + new_input.ymin = input->ymin - filtersize_ - 1; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } } diff --git a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h index bb33f8b74cb..817e5a137c2 100644 --- a/source/blender/compositor/operations/COM_GaussianYBlurOperation.h +++ b/source/blender/compositor/operations/COM_GaussianYBlurOperation.h @@ -25,7 +25,7 @@ namespace blender::compositor { /* TODO(manzanilla): everything to be removed with tiled implementation except the constructor. */ class GaussianYBlurOperation : public GaussianBlurBaseOperation { private: - void updateGauss(); + void update_gauss(); public: GaussianYBlurOperation(); @@ -33,33 +33,33 @@ class GaussianYBlurOperation : public GaussianBlurBaseOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; /** * \brief initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void *initialize_tile_data(rcti *rect) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void checkOpenCL() + void check_opencl() { - flags.open_cl = (m_data.sizex >= 128); + flags_.open_cl = (data_.sizex >= 128); } }; diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.cc b/source/blender/compositor/operations/COM_GlareBaseOperation.cc index 90755d9f27a..766b20326db 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.cc +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.cc @@ -17,56 +17,89 @@ */ #include "COM_GlareBaseOperation.h" -#include "BLI_math.h" namespace blender::compositor { GlareBaseOperation::GlareBaseOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_settings = nullptr; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + settings_ = nullptr; + flags_.is_fullframe_operation = true; + is_output_rendered_ = false; } -void GlareBaseOperation::initExecution() +void GlareBaseOperation::init_execution() { - SingleThreadedOperation::initExecution(); - this->m_inputProgram = getInputSocketReader(0); + SingleThreadedOperation::init_execution(); + input_program_ = get_input_socket_reader(0); } -void GlareBaseOperation::deinitExecution() +void GlareBaseOperation::deinit_execution() { - this->m_inputProgram = nullptr; - SingleThreadedOperation::deinitExecution(); + input_program_ = nullptr; + SingleThreadedOperation::deinit_execution(); } -MemoryBuffer *GlareBaseOperation::createMemoryBuffer(rcti *rect2) +MemoryBuffer *GlareBaseOperation::create_memory_buffer(rcti *rect2) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputProgram->initializeTileData(rect2); + MemoryBuffer *tile = (MemoryBuffer *)input_program_->initialize_tile_data(rect2); rcti rect; rect.xmin = 0; rect.ymin = 0; - rect.xmax = getWidth(); - rect.ymax = getHeight(); + rect.xmax = get_width(); + rect.ymax = get_height(); MemoryBuffer *result = new MemoryBuffer(DataType::Color, rect); - float *data = result->getBuffer(); - this->generateGlare(data, tile, this->m_settings); + float *data = result->get_buffer(); + this->generate_glare(data, tile, settings_); return result; } -bool GlareBaseOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool GlareBaseOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - if (isCached()) { + if (is_cached()) { return false; } - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); +} + +void GlareBaseOperation::get_area_of_interest(const int input_idx, + const rcti &UNUSED(output_area), + rcti &r_input_area) +{ + BLI_assert(input_idx == 0); + UNUSED_VARS_NDEBUG(input_idx); + r_input_area.xmin = 0; + r_input_area.xmax = this->get_width(); + r_input_area.ymin = 0; + r_input_area.ymax = this->get_height(); +} + +void GlareBaseOperation::update_memory_buffer(MemoryBuffer *output, + const rcti &UNUSED(area), + Span inputs) +{ + if (!is_output_rendered_) { + MemoryBuffer *input = inputs[0]; + const bool is_input_inflated = input->is_a_single_elem(); + if (is_input_inflated) { + input = input->inflate(); + } + + this->generate_glare(output->get_buffer(), input, settings_); + is_output_rendered_ = true; + + if (is_input_inflated) { + delete input; + } + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareBaseOperation.h b/source/blender/compositor/operations/COM_GlareBaseOperation.h index 6dac6f5ecc7..09db5efcec9 100644 --- a/source/blender/compositor/operations/COM_GlareBaseOperation.h +++ b/source/blender/compositor/operations/COM_GlareBaseOperation.h @@ -40,40 +40,50 @@ typedef float fRGB[4]; class GlareBaseOperation : public SingleThreadedOperation { private: /** - * \brief Cached reference to the inputProgram + * \brief Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; /** * \brief settings of the glare node. */ - NodeGlare *m_settings; + NodeGlare *settings_; + + bool is_output_rendered_; public: /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setGlareSettings(NodeGlare *settings) + void set_glare_settings(NodeGlare *settings) { - this->m_settings = settings; + settings_ = settings; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + + void get_area_of_interest(const int input_idx, + const rcti &output_area, + rcti &r_input_area) final; + + void update_memory_buffer(MemoryBuffer *output, + const rcti &area, + Span inputs) final; protected: GlareBaseOperation(); - virtual void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) = 0; + virtual void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) = 0; - MemoryBuffer *createMemoryBuffer(rcti *rect) override; + MemoryBuffer *create_memory_buffer(rcti *rect) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc index 0026615f08b..fa2e2597452 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareFogGlowOperation.h" -#include "MEM_guardedalloc.h" namespace blender::compositor { @@ -28,7 +27,7 @@ namespace blender::compositor { using fREAL = float; /* Returns next highest power of 2 of x, as well its log2 in L2. */ -static unsigned int nextPow2(unsigned int x, unsigned int *L2) +static unsigned int next_pow2(unsigned int x, unsigned int *L2) { unsigned int pw, x_notpow2 = x & (x - 1); *L2 = 0; @@ -263,24 +262,24 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) int x, y, ch; int xbl, ybl, nxb, nyb, xbsz, ybsz; bool in2done = false; - const unsigned int kernelWidth = in2->getWidth(); - const unsigned int kernelHeight = in2->getHeight(); - const unsigned int imageWidth = in1->getWidth(); - const unsigned int imageHeight = in1->getHeight(); - float *kernelBuffer = in2->getBuffer(); - float *imageBuffer = in1->getBuffer(); + const unsigned int kernel_width = in2->get_width(); + const unsigned int kernel_height = in2->get_height(); + const unsigned int image_width = in1->get_width(); + const unsigned int image_height = in1->get_height(); + float *kernel_buffer = in2->get_buffer(); + float *image_buffer = in1->get_buffer(); MemoryBuffer *rdst = new MemoryBuffer(DataType::Color, in1->get_rect()); - memset(rdst->getBuffer(), + memset(rdst->get_buffer(), 0, - rdst->getWidth() * rdst->getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + rdst->get_width() * rdst->get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); /* Convolution result width & height. */ - w2 = 2 * kernelWidth - 1; - h2 = 2 * kernelHeight - 1; + w2 = 2 * kernel_width - 1; + h2 = 2 * kernel_height - 1; /* FFT pow2 required size & log2. */ - w2 = nextPow2(w2, &log2_w); - h2 = nextPow2(h2, &log2_h); + w2 = next_pow2(w2, &log2_w); + h2 = next_pow2(h2, &log2_h); /* Allocate space. */ data1 = (fREAL *)MEM_callocN(3 * w2 * h2 * sizeof(fREAL), "convolve_fast FHT data1"); @@ -288,9 +287,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Normalize convolutor. */ wt[0] = wt[1] = wt[2] = 0.0f; - for (y = 0; y < kernelHeight; y++) { - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS]; - for (x = 0; x < kernelWidth; x++) { + for (y = 0; y < kernel_height; y++) { + colp = (fRGB *)&kernel_buffer[y * kernel_width * COM_DATA_TYPE_COLOR_CHANNELS]; + for (x = 0; x < kernel_width; x++) { add_v3_v3(wt, colp[x]); } } @@ -303,9 +302,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) if (wt[2] != 0.0f) { wt[2] = 1.0f / wt[2]; } - for (y = 0; y < kernelHeight; y++) { - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS]; - for (x = 0; x < kernelWidth; x++) { + for (y = 0; y < kernel_height; y++) { + colp = (fRGB *)&kernel_buffer[y * kernel_width * COM_DATA_TYPE_COLOR_CHANNELS]; + for (x = 0; x < kernel_width; x++) { mul_v3_v3(colp[x], wt); } } @@ -314,16 +313,16 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) * only need to calc data1 once. */ /* Block add-overlap. */ - hw = kernelWidth >> 1; - hh = kernelHeight >> 1; - xbsz = (w2 + 1) - kernelWidth; - ybsz = (h2 + 1) - kernelHeight; - nxb = imageWidth / xbsz; - if (imageWidth % xbsz) { + hw = kernel_width >> 1; + hh = kernel_height >> 1; + xbsz = (w2 + 1) - kernel_width; + ybsz = (h2 + 1) - kernel_height; + nxb = image_width / xbsz; + if (image_width % xbsz) { nxb++; } - nyb = imageHeight / ybsz; - if (imageHeight % ybsz) { + nyb = image_height / ybsz; + if (image_height % ybsz) { nyb++; } for (ybl = 0; ybl < nyb; ybl++) { @@ -336,10 +335,10 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Only need to calc fht data from in2 once, can re-use for every block. */ if (!in2done) { /* in2, channel ch -> data1 */ - for (y = 0; y < kernelHeight; y++) { + for (y = 0; y < kernel_height; y++) { fp = &data1ch[y * w2]; - colp = (fRGB *)&kernelBuffer[y * kernelWidth * COM_DATA_TYPE_COLOR_CHANNELS]; - for (x = 0; x < kernelWidth; x++) { + colp = (fRGB *)&kernel_buffer[y * kernel_width * COM_DATA_TYPE_COLOR_CHANNELS]; + for (x = 0; x < kernel_width; x++) { fp[x] = colp[x][ch]; } } @@ -349,14 +348,14 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) memset(data2, 0, w2 * h2 * sizeof(fREAL)); for (y = 0; y < ybsz; y++) { int yy = ybl * ybsz + y; - if (yy >= imageHeight) { + if (yy >= image_height) { continue; } fp = &data2[y * w2]; - colp = (fRGB *)&imageBuffer[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS]; + colp = (fRGB *)&image_buffer[yy * image_width * COM_DATA_TYPE_COLOR_CHANNELS]; for (x = 0; x < xbsz; x++) { int xx = xbl * xbsz + x; - if (xx >= imageWidth) { + if (xx >= image_width) { continue; } fp[x] = colp[xx][ch]; @@ -366,9 +365,9 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Forward FHT * zero pad data start is different for each == height+1. */ if (!in2done) { - FHT2D(data1ch, log2_w, log2_h, kernelHeight + 1, 0); + FHT2D(data1ch, log2_w, log2_h, kernel_height + 1, 0); } - FHT2D(data2, log2_w, log2_h, kernelHeight + 1, 0); + FHT2D(data2, log2_w, log2_h, kernel_height + 1, 0); /* FHT2D transposed data, row/col now swapped * convolve & inverse FHT. */ @@ -379,14 +378,14 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) /* Overlap-add result. */ for (y = 0; y < (int)h2; y++) { const int yy = ybl * ybsz + y - hh; - if ((yy < 0) || (yy >= imageHeight)) { + if ((yy < 0) || (yy >= image_height)) { continue; } fp = &data2[y * w2]; - colp = (fRGB *)&rdst->getBuffer()[yy * imageWidth * COM_DATA_TYPE_COLOR_CHANNELS]; + colp = (fRGB *)&rdst->get_buffer()[yy * image_width * COM_DATA_TYPE_COLOR_CHANNELS]; for (x = 0; x < (int)w2; x++) { const int xx = xbl * xbsz + x - hw; - if ((xx < 0) || (xx >= imageWidth)) { + if ((xx < 0) || (xx >= image_width)) { continue; } colp[xx][ch] += fp[x]; @@ -400,14 +399,14 @@ static void convolve(float *dst, MemoryBuffer *in1, MemoryBuffer *in2) MEM_freeN(data2); MEM_freeN(data1); memcpy(dst, - rdst->getBuffer(), - sizeof(float) * imageWidth * imageHeight * COM_DATA_TYPE_COLOR_CHANNELS); + rdst->get_buffer(), + sizeof(float) * image_width * image_height * COM_DATA_TYPE_COLOR_CHANNELS); delete (rdst); } -void GlareFogGlowOperation::generateGlare(float *data, - MemoryBuffer *inputTile, - NodeGlare *settings) +void GlareFogGlowOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { int x, y; float scale, u, v, r, w, d; @@ -418,9 +417,9 @@ void GlareFogGlowOperation::generateGlare(float *data, /* Temp. src image * make the convolution kernel. */ - rcti kernelRect; - BLI_rcti_init(&kernelRect, 0, sz, 0, sz); - ckrn = new MemoryBuffer(DataType::Color, kernelRect); + rcti kernel_rect; + BLI_rcti_init(&kernel_rect, 0, sz, 0, sz); + ckrn = new MemoryBuffer(DataType::Color, kernel_rect); scale = 0.25f * sqrtf((float)(sz * sz)); @@ -438,11 +437,11 @@ void GlareFogGlowOperation::generateGlare(float *data, * actually, Hanning window is ok, `cos^2` for some reason is slower. */ w = (0.5f + 0.5f * cosf(u * (float)M_PI)) * (0.5f + 0.5f * cosf(v * (float)M_PI)); mul_v3_fl(fcol, w); - ckrn->writePixel(x, y, fcol); + ckrn->write_pixel(x, y, fcol); } } - convolve(data, inputTile, ckrn); + convolve(data, input_tile, ckrn); delete ckrn; } diff --git a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h index 5701f76ab13..648351fa692 100644 --- a/source/blender/compositor/operations/COM_GlareFogGlowOperation.h +++ b/source/blender/compositor/operations/COM_GlareFogGlowOperation.h @@ -31,7 +31,7 @@ class GlareFogGlowOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.cc b/source/blender/compositor/operations/COM_GlareGhostOperation.cc index 22c8767632e..930c723e94e 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.cc +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.cc @@ -17,12 +17,11 @@ */ #include "COM_GlareGhostOperation.h" -#include "BLI_math.h" #include "COM_FastGaussianBlurOperation.h" namespace blender::compositor { -static float smoothMask(float x, float y) +static float smooth_mask(float x, float y) { float t; x = 2.0f * x - 1.0f; @@ -34,7 +33,9 @@ static float smoothMask(float x, float y) return 0.0f; } -void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) +void GlareGhostOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { const int qt = 1 << settings->quality; const float s1 = 4.0f / (float)qt, s2 = 2.0f * s1; @@ -43,8 +44,8 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No float sc, isc, u, v, sm, s, t, ofs, scalef[64]; const float cmo = 1.0f - settings->colmod; - MemoryBuffer gbuf(*inputTile); - MemoryBuffer tbuf1(*inputTile); + MemoryBuffer gbuf(*input_tile); + MemoryBuffer tbuf1(*input_tile); bool breaked = false; @@ -52,7 +53,7 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No if (!breaked) { FastGaussianBlurOperation::IIR_gauss(&tbuf1, s1, 1, 3); } - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { @@ -61,19 +62,19 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No MemoryBuffer tbuf2(tbuf1); - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { FastGaussianBlurOperation::IIR_gauss(&tbuf2, s2, 0, 3); } - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { FastGaussianBlurOperation::IIR_gauss(&tbuf2, s2, 1, 3); } - if (isBraked()) { + if (is_braked()) { breaked = true; } if (!breaked) { @@ -101,59 +102,59 @@ void GlareGhostOperation::generateGlare(float *data, MemoryBuffer *inputTile, No sc = 2.13; isc = -0.97; - for (y = 0; y < gbuf.getHeight() && (!breaked); y++) { - v = ((float)y + 0.5f) / (float)gbuf.getHeight(); - for (x = 0; x < gbuf.getWidth(); x++) { - u = ((float)x + 0.5f) / (float)gbuf.getWidth(); + for (y = 0; y < gbuf.get_height() && (!breaked); y++) { + v = ((float)y + 0.5f) / (float)gbuf.get_height(); + for (x = 0; x < gbuf.get_width(); x++) { + u = ((float)x + 0.5f) / (float)gbuf.get_width(); s = (u - 0.5f) * sc + 0.5f; t = (v - 0.5f) * sc + 0.5f; - tbuf1.readBilinear(c, s * gbuf.getWidth(), t * gbuf.getHeight()); - sm = smoothMask(s, t); + tbuf1.read_bilinear(c, s * gbuf.get_width(), t * gbuf.get_height()); + sm = smooth_mask(s, t); mul_v3_fl(c, sm); s = (u - 0.5f) * isc + 0.5f; t = (v - 0.5f) * isc + 0.5f; - tbuf2.readBilinear(tc, s * gbuf.getWidth() - 0.5f, t * gbuf.getHeight() - 0.5f); - sm = smoothMask(s, t); + tbuf2.read_bilinear(tc, s * gbuf.get_width() - 0.5f, t * gbuf.get_height() - 0.5f); + sm = smooth_mask(s, t); madd_v3_v3fl(c, tc, sm); - gbuf.writePixel(x, y, c); + gbuf.write_pixel(x, y, c); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } - memset(tbuf1.getBuffer(), + memset(tbuf1.get_buffer(), 0, - tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + tbuf1.get_width() * tbuf1.get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); for (n = 1; n < settings->iter && (!breaked); n++) { - for (y = 0; y < gbuf.getHeight() && (!breaked); y++) { - v = ((float)y + 0.5f) / (float)gbuf.getHeight(); - for (x = 0; x < gbuf.getWidth(); x++) { - u = ((float)x + 0.5f) / (float)gbuf.getWidth(); + for (y = 0; y < gbuf.get_height() && (!breaked); y++) { + v = ((float)y + 0.5f) / (float)gbuf.get_height(); + for (x = 0; x < gbuf.get_width(); x++) { + u = ((float)x + 0.5f) / (float)gbuf.get_width(); tc[0] = tc[1] = tc[2] = 0.0f; for (p = 0; p < 4; p++) { np = (n << 2) + p; s = (u - 0.5f) * scalef[np] + 0.5f; t = (v - 0.5f) * scalef[np] + 0.5f; - gbuf.readBilinear(c, s * gbuf.getWidth() - 0.5f, t * gbuf.getHeight() - 0.5f); + gbuf.read_bilinear(c, s * gbuf.get_width() - 0.5f, t * gbuf.get_height() - 0.5f); mul_v3_v3(c, cm[np]); - sm = smoothMask(s, t) * 0.25f; + sm = smooth_mask(s, t) * 0.25f; madd_v3_v3fl(tc, c, sm); } - tbuf1.addPixel(x, y, tc); + tbuf1.add_pixel(x, y, tc); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } - memcpy(gbuf.getBuffer(), - tbuf1.getBuffer(), - tbuf1.getWidth() * tbuf1.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + memcpy(gbuf.get_buffer(), + tbuf1.get_buffer(), + tbuf1.get_width() * tbuf1.get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); } memcpy(data, - gbuf.getBuffer(), - gbuf.getWidth() * gbuf.getHeight() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); + gbuf.get_buffer(), + gbuf.get_width() * gbuf.get_height() * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float)); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareGhostOperation.h b/source/blender/compositor/operations/COM_GlareGhostOperation.h index 60256d8e0ef..e84084c3f6e 100644 --- a/source/blender/compositor/operations/COM_GlareGhostOperation.h +++ b/source/blender/compositor/operations/COM_GlareGhostOperation.h @@ -31,7 +31,7 @@ class GlareGhostOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc index cc24a50a307..5d8dca055aa 100644 --- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc +++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.cc @@ -20,26 +20,26 @@ namespace blender::compositor { -void GlareSimpleStarOperation::generateGlare(float *data, - MemoryBuffer *inputTile, - NodeGlare *settings) +void GlareSimpleStarOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { int i, x, y, ym, yp, xm, xp; float c[4] = {0, 0, 0, 0}, tc[4] = {0, 0, 0, 0}; const float f1 = 1.0f - settings->fade; const float f2 = (1.0f - f1) * 0.5f; - MemoryBuffer tbuf1(*inputTile); - MemoryBuffer tbuf2(*inputTile); + MemoryBuffer tbuf1(*input_tile); + MemoryBuffer tbuf2(*input_tile); bool breaked = false; for (i = 0; i < settings->iter && (!breaked); i++) { // // (x || x-1, y-1) to (x || x+1, y+1) // // F - for (y = 0; y < this->getHeight() && (!breaked); y++) { + for (y = 0; y < this->get_height() && (!breaked); y++) { ym = y - i; yp = y + i; - for (x = 0; x < this->getWidth(); x++) { + for (x = 0; x < this->get_width(); x++) { xm = x - i; xp = x + i; tbuf1.read(c, x, y); @@ -49,7 +49,7 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf1.read(tc, (settings->star_45 ? xp : x), yp); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf1.writePixel(x, y, c); + tbuf1.write_pixel(x, y, c); tbuf2.read(c, x, y); mul_v3_fl(c, f1); @@ -58,17 +58,17 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf2.read(tc, xp, (settings->star_45 ? ym : y)); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf2.writePixel(x, y, c); + tbuf2.write_pixel(x, y, c); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } // // B - for (y = this->getHeight() - 1; y >= 0 && (!breaked); y--) { + for (y = this->get_height() - 1; y >= 0 && (!breaked); y--) { ym = y - i; yp = y + i; - for (x = this->getWidth() - 1; x >= 0; x--) { + for (x = this->get_width() - 1; x >= 0; x--) { xm = x - i; xp = x + i; tbuf1.read(c, x, y); @@ -78,7 +78,7 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf1.read(tc, (settings->star_45 ? xp : x), yp); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf1.writePixel(x, y, c); + tbuf1.write_pixel(x, y, c); tbuf2.read(c, x, y); mul_v3_fl(c, f1); @@ -87,16 +87,16 @@ void GlareSimpleStarOperation::generateGlare(float *data, tbuf2.read(tc, xp, (settings->star_45 ? ym : y)); madd_v3_v3fl(c, tc, f2); c[3] = 1.0f; - tbuf2.writePixel(x, y, c); + tbuf2.write_pixel(x, y, c); } - if (isBraked()) { + if (is_braked()) { breaked = true; } } } - for (i = 0; i < this->getWidth() * this->getHeight() * 4; i++) { - data[i] = tbuf1.getBuffer()[i] + tbuf2.getBuffer()[i]; + for (i = 0; i < this->get_width() * this->get_height() * 4; i++) { + data[i] = tbuf1.get_buffer()[i] + tbuf2.get_buffer()[i]; } } diff --git a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h index 4a074f53e7b..be162bbda44 100644 --- a/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h +++ b/source/blender/compositor/operations/COM_GlareSimpleStarOperation.h @@ -31,7 +31,7 @@ class GlareSimpleStarOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc index 5ca64b02586..f4d9b588917 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.cc +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.cc @@ -17,26 +17,25 @@ */ #include "COM_GlareStreaksOperation.h" -#include "BLI_math.h" namespace blender::compositor { -void GlareStreaksOperation::generateGlare(float *data, - MemoryBuffer *inputTile, - NodeGlare *settings) +void GlareStreaksOperation::generate_glare(float *data, + MemoryBuffer *input_tile, + NodeGlare *settings) { int x, y, n; unsigned int nump = 0; float c1[4], c2[4], c3[4], c4[4]; float a, ang = DEG2RADF(360.0f) / (float)settings->streaks; - int size = inputTile->getWidth() * inputTile->getHeight(); + int size = input_tile->get_width() * input_tile->get_height(); int size4 = size * 4; bool breaked = false; - MemoryBuffer tsrc(*inputTile); - MemoryBuffer tdst(DataType::Color, inputTile->get_rect()); + MemoryBuffer tsrc(*input_tile); + MemoryBuffer tdst(DataType::Color, input_tile->get_rect()); tdst.clear(); memset(data, 0, size4 * sizeof(float)); @@ -51,9 +50,9 @@ void GlareStreaksOperation::generateGlare(float *data, /* Color-modulation amount relative to current pass. */ const float cmo = 1.0f - (float)pow((double)settings->colmod, (double)n + 1); - float *tdstcol = tdst.getBuffer(); - for (y = 0; y < tsrc.getHeight() && (!breaked); y++) { - for (x = 0; x < tsrc.getWidth(); x++, tdstcol += 4) { + float *tdstcol = tdst.get_buffer(); + for (y = 0; y < tsrc.get_height() && (!breaked); y++) { + for (x = 0; x < tsrc.get_width(); x++, tdstcol += 4) { /* First pass no offset, always same for every pass, exact copy, * otherwise results in uneven brightness, only need once. */ if (n == 0) { @@ -62,9 +61,9 @@ void GlareStreaksOperation::generateGlare(float *data, else { c1[0] = c1[1] = c1[2] = 0; } - tsrc.readBilinear(c2, x + vxp, y + vyp); - tsrc.readBilinear(c3, x + vxp * 2.0f, y + vyp * 2.0f); - tsrc.readBilinear(c4, x + vxp * 3.0f, y + vyp * 3.0f); + tsrc.read_bilinear(c2, x + vxp, y + vyp); + tsrc.read_bilinear(c3, x + vxp * 2.0f, y + vyp * 2.0f); + tsrc.read_bilinear(c4, x + vxp * 3.0f, y + vyp * 3.0f); /* Modulate color to look vaguely similar to a color spectrum. */ c2[1] *= cmo; c2[2] *= cmo; @@ -80,14 +79,14 @@ void GlareStreaksOperation::generateGlare(float *data, tdstcol[2] = 0.5f * (tdstcol[2] + c1[2] + wt * (c2[2] + wt * (c3[2] + wt * c4[2]))); tdstcol[3] = 1.0f; } - if (isBraked()) { + if (is_braked()) { breaked = true; } } - memcpy(tsrc.getBuffer(), tdst.getBuffer(), sizeof(float) * size4); + memcpy(tsrc.get_buffer(), tdst.get_buffer(), sizeof(float) * size4); } - float *sourcebuffer = tsrc.getBuffer(); + float *sourcebuffer = tsrc.get_buffer(); float factor = 1.0f / (float)(6 - settings->iter); for (int i = 0; i < size4; i += 4) { madd_v3_v3fl(&data[i], &sourcebuffer[i], factor); @@ -95,7 +94,7 @@ void GlareStreaksOperation::generateGlare(float *data, } tdst.clear(); - memcpy(tsrc.getBuffer(), inputTile->getBuffer(), sizeof(float) * size4); + memcpy(tsrc.get_buffer(), input_tile->get_buffer(), sizeof(float) * size4); nump++; } } diff --git a/source/blender/compositor/operations/COM_GlareStreaksOperation.h b/source/blender/compositor/operations/COM_GlareStreaksOperation.h index 487c910960a..bad73687afd 100644 --- a/source/blender/compositor/operations/COM_GlareStreaksOperation.h +++ b/source/blender/compositor/operations/COM_GlareStreaksOperation.h @@ -31,7 +31,7 @@ class GlareStreaksOperation : public GlareBaseOperation { } protected: - void generateGlare(float *data, MemoryBuffer *inputTile, NodeGlare *settings) override; + void generate_glare(float *data, MemoryBuffer *input_tile, NodeGlare *settings) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc index 1d3402f5b7b..0538b4e48a0 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.cc +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_GlareThresholdOperation.h" -#include "BLI_math.h" #include "IMB_colormanagement.h" @@ -25,32 +24,33 @@ namespace blender::compositor { GlareThresholdOperation::GlareThresholdOperation() { - this->addInputSocket(DataType::Color, ResizeMode::FitAny); - this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; + this->add_input_socket(DataType::Color, ResizeMode::FitAny); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; } -void GlareThresholdOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void GlareThresholdOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation::determineResolution(resolution, preferredResolution); - resolution[0] = resolution[0] / (1 << this->m_settings->quality); - resolution[1] = resolution[1] / (1 << this->m_settings->quality); + NodeOperation::determine_canvas(preferred_area, r_area); + const int width = BLI_rcti_size_x(&r_area) / (1 << settings_->quality); + const int height = BLI_rcti_size_y(&r_area) / (1 << settings_->quality); + r_area.xmax = r_area.xmin + width; + r_area.ymax = r_area.ymin + height; } -void GlareThresholdOperation::initExecution() +void GlareThresholdOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); + input_program_ = this->get_input_socket_reader(0); } -void GlareThresholdOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void GlareThresholdOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - const float threshold = this->m_settings->threshold; + const float threshold = settings_->threshold; - this->m_inputProgram->readSampled(output, x, y, sampler); + input_program_->read_sampled(output, x, y, sampler); if (IMB_colormanagement_get_luminance(output) >= threshold) { output[0] -= threshold; output[1] -= threshold; @@ -65,9 +65,29 @@ void GlareThresholdOperation::executePixelSampled(float output[4], } } -void GlareThresholdOperation::deinitExecution() +void GlareThresholdOperation::deinit_execution() { - this->m_inputProgram = nullptr; + input_program_ = nullptr; +} + +void GlareThresholdOperation::update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) +{ + const float threshold = settings_->threshold; + for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { + const float *color = it.in(0); + if (IMB_colormanagement_get_luminance(color) >= threshold) { + it.out[0] = color[0] - threshold; + it.out[1] = color[1] - threshold; + it.out[2] = color[2] - threshold; + + CLAMP3_MIN(it.out, 0.0f); + } + else { + zero_v3(it.out); + } + } } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_GlareThresholdOperation.h b/source/blender/compositor/operations/COM_GlareThresholdOperation.h index a6e971dada7..41eac34375d 100644 --- a/source/blender/compositor/operations/COM_GlareThresholdOperation.h +++ b/source/blender/compositor/operations/COM_GlareThresholdOperation.h @@ -18,22 +18,22 @@ #pragma once -#include "COM_NodeOperation.h" +#include "COM_MultiThreadedOperation.h" #include "DNA_light_types.h" namespace blender::compositor { -class GlareThresholdOperation : public NodeOperation { +class GlareThresholdOperation : public MultiThreadedOperation { private: /** - * \brief Cached reference to the inputProgram + * \brief Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; /** * \brief settings of the glare node. */ - NodeGlare *m_settings; + NodeGlare *settings_; public: GlareThresholdOperation(); @@ -41,25 +41,27 @@ class GlareThresholdOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setGlareSettings(NodeGlare *settings) + void set_glare_settings(NodeGlare *settings) { - this->m_settings = settings; + settings_ = settings; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + void update_memory_buffer_partial(MemoryBuffer *output, + const rcti &area, + Span inputs) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc index 5ae868c5964..d1fad29498a 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.cc @@ -18,7 +18,7 @@ #include "COM_HueSaturationValueCorrectOperation.h" -#include "BLI_math.h" +#include "BLI_math_vector.h" #include "BKE_colortools.h" @@ -26,36 +26,36 @@ namespace blender::compositor { HueSaturationValueCorrectOperation::HueSaturationValueCorrectOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - this->m_inputProgram = nullptr; + input_program_ = nullptr; } -void HueSaturationValueCorrectOperation::initExecution() +void HueSaturationValueCorrectOperation::init_execution() { - CurveBaseOperation::initExecution(); - this->m_inputProgram = this->getInputSocketReader(0); + CurveBaseOperation::init_execution(); + input_program_ = this->get_input_socket_reader(0); } -void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void HueSaturationValueCorrectOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float hsv[4], f; - this->m_inputProgram->readSampled(hsv, x, y, sampler); + input_program_->read_sampled(hsv, x, y, sampler); /* adjust hue, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 0, hsv[0]); hsv[0] += f - 0.5f; /* adjust saturation, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* adjust value, scaling returned default 0.5 up to 1 */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* mod 1.0 */ @@ -67,10 +67,10 @@ void HueSaturationValueCorrectOperation::executePixelSampled(float output[4], output[3] = hsv[3]; } -void HueSaturationValueCorrectOperation::deinitExecution() +void HueSaturationValueCorrectOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - this->m_inputProgram = nullptr; + CurveBaseOperation::deinit_execution(); + input_program_ = nullptr; } void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -82,15 +82,15 @@ void HueSaturationValueCorrectOperation::update_memory_buffer_partial(MemoryBuff copy_v4_v4(hsv, it.in(0)); /* Adjust hue, scaling returned default 0.5 up to 1. */ - float f = BKE_curvemapping_evaluateF(this->m_curveMapping, 0, hsv[0]); + float f = BKE_curvemapping_evaluateF(curve_mapping_, 0, hsv[0]); hsv[0] += f - 0.5f; /* Adjust saturation, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 1, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 1, hsv[0]); hsv[1] *= (f * 2.0f); /* Adjust value, scaling returned default 0.5 up to 1. */ - f = BKE_curvemapping_evaluateF(this->m_curveMapping, 2, hsv[0]); + f = BKE_curvemapping_evaluateF(curve_mapping_, 2, hsv[0]); hsv[2] *= (f * 2.0f); hsv[0] = hsv[0] - floorf(hsv[0]); /* Mod 1.0. */ diff --git a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h index 6c1b66aba1f..ac64d31e494 100644 --- a/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h +++ b/source/blender/compositor/operations/COM_HueSaturationValueCorrectOperation.h @@ -26,9 +26,9 @@ namespace blender::compositor { class HueSaturationValueCorrectOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; public: HueSaturationValueCorrectOperation(); @@ -36,17 +36,17 @@ class HueSaturationValueCorrectOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.cc b/source/blender/compositor/operations/COM_IDMaskOperation.cc index bb11ac8b1be..503cbf5eb31 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.cc +++ b/source/blender/compositor/operations/COM_IDMaskOperation.cc @@ -22,25 +22,25 @@ namespace blender::compositor { IDMaskOperation::IDMaskOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->flags.complex = true; - flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + flags_.complex = true; + flags_.can_be_constant = true; } -void *IDMaskOperation::initializeTileData(rcti *rect) +void *IDMaskOperation::initialize_tile_data(rcti *rect) { - void *buffer = getInputOperation(0)->initializeTileData(rect); + void *buffer = get_input_operation(0)->initialize_tile_data(rect); return buffer; } -void IDMaskOperation::executePixel(float output[4], int x, int y, void *data) +void IDMaskOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *input_buffer = (MemoryBuffer *)data; - const int buffer_width = input_buffer->getWidth(); - float *buffer = input_buffer->getBuffer(); + const int buffer_width = input_buffer->get_width(); + float *buffer = input_buffer->get_buffer(); int buffer_index = (y * buffer_width + x); - output[0] = (roundf(buffer[buffer_index]) == this->m_objectIndex) ? 1.0f : 0.0f; + output[0] = (roundf(buffer[buffer_index]) == object_index_) ? 1.0f : 0.0f; } void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -54,7 +54,7 @@ void IDMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, const float *in = input->get_elem(area.xmin, y); const float *row_end = out + width * output->elem_stride; while (out < row_end) { - out[0] = (roundf(in[0]) == m_objectIndex) ? 1.0f : 0.0f; + out[0] = (roundf(in[0]) == object_index_) ? 1.0f : 0.0f; in += input->elem_stride; out += output->elem_stride; } diff --git a/source/blender/compositor/operations/COM_IDMaskOperation.h b/source/blender/compositor/operations/COM_IDMaskOperation.h index c2e13641b46..3a7ceec8c49 100644 --- a/source/blender/compositor/operations/COM_IDMaskOperation.h +++ b/source/blender/compositor/operations/COM_IDMaskOperation.h @@ -24,17 +24,17 @@ namespace blender::compositor { class IDMaskOperation : public MultiThreadedOperation { private: - float m_objectIndex; + float object_index_; public: IDMaskOperation(); - void *initializeTileData(rcti *rect) override; - void executePixel(float output[4], int x, int y, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void setObjectIndex(float objectIndex) + void set_object_index(float object_index) { - this->m_objectIndex = objectIndex; + object_index_ = object_index; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ImageOperation.cc b/source/blender/compositor/operations/COM_ImageOperation.cc index e78d389410f..e32343cfbf2 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cc +++ b/source/blender/compositor/operations/COM_ImageOperation.cc @@ -18,117 +18,107 @@ #include "COM_ImageOperation.h" -#include "BKE_image.h" #include "BKE_scene.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "DNA_image_types.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" -#include "RE_pipeline.h" -#include "RE_texture.h" - namespace blender::compositor { BaseImageOperation::BaseImageOperation() { - this->m_image = nullptr; - this->m_buffer = nullptr; - this->m_imageFloatBuffer = nullptr; - this->m_imageByteBuffer = nullptr; - this->m_imageUser = nullptr; - this->m_imagewidth = 0; - this->m_imageheight = 0; - this->m_framenumber = 0; - this->m_depthBuffer = nullptr; + image_ = nullptr; + buffer_ = nullptr; + image_float_buffer_ = nullptr; + image_byte_buffer_ = nullptr; + image_user_ = nullptr; + imagewidth_ = 0; + imageheight_ = 0; + framenumber_ = 0; + image_depth_buffer_ = nullptr; depth_buffer_ = nullptr; - this->m_numberOfChannels = 0; - this->m_rd = nullptr; - this->m_viewName = nullptr; + number_of_channels_ = 0; + rd_ = nullptr; + view_name_ = nullptr; } ImageOperation::ImageOperation() : BaseImageOperation() { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } ImageAlphaOperation::ImageAlphaOperation() : BaseImageOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } ImageDepthOperation::ImageDepthOperation() : BaseImageOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } -ImBuf *BaseImageOperation::getImBuf() +ImBuf *BaseImageOperation::get_im_buf() { ImBuf *ibuf; - ImageUser iuser = *this->m_imageUser; + ImageUser iuser = *image_user_; - if (this->m_image == nullptr) { + if (image_ == nullptr) { return nullptr; } /* local changes to the original ImageUser */ - if (BKE_image_is_multilayer(this->m_image) == false) { - iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + if (BKE_image_is_multilayer(image_) == false) { + iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, view_name_); } - ibuf = BKE_image_acquire_ibuf(this->m_image, &iuser, nullptr); + ibuf = BKE_image_acquire_ibuf(image_, &iuser, nullptr); if (ibuf == nullptr || (ibuf->rect == nullptr && ibuf->rect_float == nullptr)) { - BKE_image_release_ibuf(this->m_image, ibuf, nullptr); + BKE_image_release_ibuf(image_, ibuf, nullptr); return nullptr; } return ibuf; } -void BaseImageOperation::initExecution() +void BaseImageOperation::init_execution() { - ImBuf *stackbuf = getImBuf(); - this->m_buffer = stackbuf; + ImBuf *stackbuf = get_im_buf(); + buffer_ = stackbuf; if (stackbuf) { - this->m_imageFloatBuffer = stackbuf->rect_float; - this->m_imageByteBuffer = stackbuf->rect; - this->m_depthBuffer = stackbuf->zbuf_float; + image_float_buffer_ = stackbuf->rect_float; + image_byte_buffer_ = stackbuf->rect; + image_depth_buffer_ = stackbuf->zbuf_float; if (stackbuf->zbuf_float) { depth_buffer_ = new MemoryBuffer(stackbuf->zbuf_float, 1, stackbuf->x, stackbuf->y); } - this->m_imagewidth = stackbuf->x; - this->m_imageheight = stackbuf->y; - this->m_numberOfChannels = stackbuf->channels; + imagewidth_ = stackbuf->x; + imageheight_ = stackbuf->y; + number_of_channels_ = stackbuf->channels; } } -void BaseImageOperation::deinitExecution() +void BaseImageOperation::deinit_execution() { - this->m_imageFloatBuffer = nullptr; - this->m_imageByteBuffer = nullptr; - BKE_image_release_ibuf(this->m_image, this->m_buffer, nullptr); + image_float_buffer_ = nullptr; + image_byte_buffer_ = nullptr; + BKE_image_release_ibuf(image_, buffer_, nullptr); if (depth_buffer_) { delete depth_buffer_; depth_buffer_ = nullptr; } } -void BaseImageOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void BaseImageOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - ImBuf *stackbuf = getImBuf(); + ImBuf *stackbuf = get_im_buf(); - resolution[0] = 0; - resolution[1] = 0; + r_area = COM_AREA_NONE; if (stackbuf) { - resolution[0] = stackbuf->x; - resolution[1] = stackbuf->y; + BLI_rcti_init(&r_area, 0, stackbuf->x, 0, stackbuf->y); } - BKE_image_release_ibuf(this->m_image, stackbuf, nullptr); + BKE_image_release_ibuf(image_, stackbuf, nullptr); } -static void sampleImageAtLocation( +static void sample_image_at_location( ImBuf *ibuf, float x, float y, PixelSampler sampler, bool make_linear_rgb, float color[4]) { if (ibuf->rect_float) { @@ -164,17 +154,17 @@ static void sampleImageAtLocation( } } -void ImageOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void ImageOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { int ix = x, iy = y; - if (this->m_imageFloatBuffer == nullptr && this->m_imageByteBuffer == nullptr) { + if (image_float_buffer_ == nullptr && image_byte_buffer_ == nullptr) { zero_v4(output); } - else if (ix < 0 || iy < 0 || ix >= this->m_buffer->x || iy >= this->m_buffer->y) { + else if (ix < 0 || iy < 0 || ix >= buffer_->x || iy >= buffer_->y) { zero_v4(output); } else { - sampleImageAtLocation(this->m_buffer, x, y, sampler, true, output); + sample_image_at_location(buffer_, x, y, sampler, true, output); } } @@ -182,22 +172,22 @@ void ImageOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->copy_from(m_buffer, area, true); + output->copy_from(buffer_, area, true); } -void ImageAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ImageAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float tempcolor[4]; - if (this->m_imageFloatBuffer == nullptr && this->m_imageByteBuffer == nullptr) { + if (image_float_buffer_ == nullptr && image_byte_buffer_ == nullptr) { output[0] = 0.0f; } else { tempcolor[3] = 1.0f; - sampleImageAtLocation(this->m_buffer, x, y, sampler, false, tempcolor); + sample_image_at_location(buffer_, x, y, sampler, false, tempcolor); output[0] = tempcolor[3]; } } @@ -206,24 +196,24 @@ void ImageAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->copy_from(m_buffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); + output->copy_from(buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } -void ImageDepthOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void ImageDepthOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - if (this->m_depthBuffer == nullptr) { + if (image_depth_buffer_ == nullptr) { output[0] = 0.0f; } else { - if (x < 0 || y < 0 || x >= this->getWidth() || y >= this->getHeight()) { + if (x < 0 || y < 0 || x >= this->get_width() || y >= this->get_height()) { output[0] = 0.0f; } else { - int offset = y * this->m_width + x; - output[0] = this->m_depthBuffer[offset]; + int offset = y * get_width() + x; + output[0] = image_depth_buffer_[offset]; } } } diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index f8b4239c9f8..054398604b6 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -34,53 +34,52 @@ namespace blender::compositor { */ class BaseImageOperation : public MultiThreadedOperation { protected: - ImBuf *m_buffer; - Image *m_image; - ImageUser *m_imageUser; + ImBuf *buffer_; + Image *image_; + ImageUser *image_user_; /* TODO: Remove raw buffers when removing Tiled implementation. */ - float *m_imageFloatBuffer; - unsigned int *m_imageByteBuffer; - float *m_depthBuffer; + float *image_float_buffer_; + unsigned int *image_byte_buffer_; + float *image_depth_buffer_; MemoryBuffer *depth_buffer_; - int m_imageheight; - int m_imagewidth; - int m_framenumber; - int m_numberOfChannels; - const RenderData *m_rd; - const char *m_viewName; + int imageheight_; + int imagewidth_; + int framenumber_; + int number_of_channels_; + const RenderData *rd_; + const char *view_name_; BaseImageOperation(); /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - virtual ImBuf *getImBuf(); + virtual ImBuf *get_im_buf(); public: - void initExecution() override; - void deinitExecution() override; - void setImage(Image *image) + void init_execution() override; + void deinit_execution() override; + void set_image(Image *image) { - this->m_image = image; + image_ = image; } - void setImageUser(ImageUser *imageuser) + void set_image_user(ImageUser *imageuser) { - this->m_imageUser = imageuser; + image_user_ = imageuser; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { - this->m_rd = rd; + rd_ = rd; } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - this->m_viewName = viewName; + view_name_ = view_name; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { - this->m_framenumber = framenumber; + framenumber_ = framenumber; } }; class ImageOperation : public BaseImageOperation { @@ -89,7 +88,7 @@ class ImageOperation : public BaseImageOperation { * Constructor */ ImageOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -101,7 +100,7 @@ class ImageAlphaOperation : public BaseImageOperation { * Constructor */ ImageAlphaOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -113,7 +112,7 @@ class ImageDepthOperation : public BaseImageOperation { * Constructor */ ImageDepthOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_InpaintOperation.cc b/source/blender/compositor/operations/COM_InpaintOperation.cc index 5e76c41752c..2d632bb3681 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.cc +++ b/source/blender/compositor/operations/COM_InpaintOperation.cc @@ -19,44 +19,41 @@ #include "MEM_guardedalloc.h" #include "COM_InpaintOperation.h" -#include "COM_OpenCLDevice.h" - -#include "BLI_math.h" namespace blender::compositor { #define ASSERT_XY_RANGE(x, y) \ - BLI_assert(x >= 0 && x < this->getWidth() && y >= 0 && y < this->getHeight()) + BLI_assert(x >= 0 && x < this->get_width() && y >= 0 && y < this->get_height()) /* In-paint (simple convolve using average of known pixels). */ InpaintSimpleOperation::InpaintSimpleOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->flags.complex = true; - this->m_inputImageProgram = nullptr; - this->m_pixelorder = nullptr; - this->m_manhattan_distance = nullptr; - this->m_cached_buffer = nullptr; - this->m_cached_buffer_ready = false; - flags.is_fullframe_operation = true; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + flags_.complex = true; + input_image_program_ = nullptr; + pixelorder_ = nullptr; + manhattan_distance_ = nullptr; + cached_buffer_ = nullptr; + cached_buffer_ready_ = false; + flags_.is_fullframe_operation = true; } -void InpaintSimpleOperation::initExecution() +void InpaintSimpleOperation::init_execution() { - this->m_inputImageProgram = this->getInputSocketReader(0); + input_image_program_ = this->get_input_socket_reader(0); - this->m_pixelorder = nullptr; - this->m_manhattan_distance = nullptr; - this->m_cached_buffer = nullptr; - this->m_cached_buffer_ready = false; + pixelorder_ = nullptr; + manhattan_distance_ = nullptr; + cached_buffer_ = nullptr; + cached_buffer_ready_ = false; - this->initMutex(); + this->init_mutex(); } void InpaintSimpleOperation::clamp_xy(int &x, int &y) { - int width = this->getWidth(); - int height = this->getHeight(); + int width = this->get_width(); + int height = this->get_height(); if (x < 0) { x = 0; @@ -75,32 +72,32 @@ void InpaintSimpleOperation::clamp_xy(int &x, int &y) float *InpaintSimpleOperation::get_pixel(int x, int y) { - int width = this->getWidth(); + int width = this->get_width(); ASSERT_XY_RANGE(x, y); - return &this->m_cached_buffer[y * width * COM_DATA_TYPE_COLOR_CHANNELS + - x * COM_DATA_TYPE_COLOR_CHANNELS]; + return &cached_buffer_[y * width * COM_DATA_TYPE_COLOR_CHANNELS + + x * COM_DATA_TYPE_COLOR_CHANNELS]; } int InpaintSimpleOperation::mdist(int x, int y) { - int width = this->getWidth(); + int width = this->get_width(); ASSERT_XY_RANGE(x, y); - return this->m_manhattan_distance[y * width + x]; + return manhattan_distance_[y * width + x]; } bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) { - int width = this->getWidth(); + int width = this->get_width(); - if (curr >= this->m_area_size) { + if (curr >= area_size_) { return false; } - int r = this->m_pixelorder[curr++]; + int r = pixelorder_[curr++]; x = r % width; y = r / width; @@ -114,10 +111,9 @@ bool InpaintSimpleOperation::next_pixel(int &x, int &y, int &curr, int iters) void InpaintSimpleOperation::calc_manhattan_distance() { - int width = this->getWidth(); - int height = this->getHeight(); - short *m = this->m_manhattan_distance = (short *)MEM_mallocN(sizeof(short) * width * height, - __func__); + int width = this->get_width(); + int height = this->get_height(); + short *m = manhattan_distance_ = (short *)MEM_mallocN(sizeof(short) * width * height, __func__); int *offsets; offsets = (int *)MEM_callocN(sizeof(int) * (width + height + 1), @@ -163,12 +159,12 @@ void InpaintSimpleOperation::calc_manhattan_distance() offsets[i] += offsets[i - 1]; } - this->m_area_size = offsets[width + height]; - this->m_pixelorder = (int *)MEM_mallocN(sizeof(int) * this->m_area_size, __func__); + area_size_ = offsets[width + height]; + pixelorder_ = (int *)MEM_mallocN(sizeof(int) * area_size_, __func__); for (int i = 0; i < width * height; i++) { if (m[i] > 0) { - this->m_pixelorder[offsets[m[i] - 1]++] = i; + pixelorder_[offsets[m[i] - 1]++] = i; } } @@ -217,74 +213,73 @@ void InpaintSimpleOperation::pix_step(int x, int y) } } -void *InpaintSimpleOperation::initializeTileData(rcti *rect) +void *InpaintSimpleOperation::initialize_tile_data(rcti *rect) { - if (this->m_cached_buffer_ready) { - return this->m_cached_buffer; + if (cached_buffer_ready_) { + return cached_buffer_; } - lockMutex(); - if (!this->m_cached_buffer_ready) { - MemoryBuffer *buf = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect); - this->m_cached_buffer = (float *)MEM_dupallocN(buf->getBuffer()); + lock_mutex(); + if (!cached_buffer_ready_) { + MemoryBuffer *buf = (MemoryBuffer *)input_image_program_->initialize_tile_data(rect); + cached_buffer_ = (float *)MEM_dupallocN(buf->get_buffer()); this->calc_manhattan_distance(); int curr = 0; int x, y; - while (this->next_pixel(x, y, curr, this->m_iterations)) { + while (this->next_pixel(x, y, curr, iterations_)) { this->pix_step(x, y); } - this->m_cached_buffer_ready = true; + cached_buffer_ready_ = true; } - unlockMutex(); - return this->m_cached_buffer; + unlock_mutex(); + return cached_buffer_; } -void InpaintSimpleOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void InpaintSimpleOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { this->clamp_xy(x, y); copy_v4_v4(output, this->get_pixel(x, y)); } -void InpaintSimpleOperation::deinitExecution() +void InpaintSimpleOperation::deinit_execution() { - this->m_inputImageProgram = nullptr; - this->deinitMutex(); - if (this->m_cached_buffer) { - MEM_freeN(this->m_cached_buffer); - this->m_cached_buffer = nullptr; + input_image_program_ = nullptr; + this->deinit_mutex(); + if (cached_buffer_) { + MEM_freeN(cached_buffer_); + cached_buffer_ = nullptr; } - if (this->m_pixelorder) { - MEM_freeN(this->m_pixelorder); - this->m_pixelorder = nullptr; + if (pixelorder_) { + MEM_freeN(pixelorder_); + pixelorder_ = nullptr; } - if (this->m_manhattan_distance) { - MEM_freeN(this->m_manhattan_distance); - this->m_manhattan_distance = nullptr; + if (manhattan_distance_) { + MEM_freeN(manhattan_distance_); + manhattan_distance_ = nullptr; } - this->m_cached_buffer_ready = false; + cached_buffer_ready_ = false; } -bool InpaintSimpleOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool InpaintSimpleOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - if (this->m_cached_buffer_ready) { + if (cached_buffer_ready_) { return false; } - rcti newInput; + rcti new_input; - newInput.xmax = getWidth(); - newInput.xmin = 0; - newInput.ymax = getHeight(); - newInput.ymin = 0; + new_input.xmax = get_width(); + new_input.xmin = 0; + new_input.ymax = get_height(); + new_input.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void InpaintSimpleOperation::get_area_of_interest(const int input_idx, @@ -293,10 +288,7 @@ void InpaintSimpleOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area = this->get_canvas(); } void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, @@ -305,28 +297,28 @@ void InpaintSimpleOperation::update_memory_buffer(MemoryBuffer *output, { /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ MemoryBuffer *input = inputs[0]; - if (!m_cached_buffer_ready) { + if (!cached_buffer_ready_) { if (input->is_a_single_elem()) { MemoryBuffer *tmp = input->inflate(); - m_cached_buffer = tmp->release_ownership_buffer(); + cached_buffer_ = tmp->release_ownership_buffer(); delete tmp; } else { - m_cached_buffer = (float *)MEM_dupallocN(input->getBuffer()); + cached_buffer_ = (float *)MEM_dupallocN(input->get_buffer()); } this->calc_manhattan_distance(); int curr = 0; int x, y; - while (this->next_pixel(x, y, curr, this->m_iterations)) { + while (this->next_pixel(x, y, curr, iterations_)) { this->pix_step(x, y); } - m_cached_buffer_ready = true; + cached_buffer_ready_ = true; } - const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); - MemoryBuffer buf(m_cached_buffer, num_channels, input->getWidth(), input->getHeight()); + const int num_channels = COM_data_type_num_channels(get_output_socket()->get_data_type()); + MemoryBuffer buf(cached_buffer_, num_channels, input->get_width(), input->get_height()); output->copy_from(&buf, area); } diff --git a/source/blender/compositor/operations/COM_InpaintOperation.h b/source/blender/compositor/operations/COM_InpaintOperation.h index e11610bd263..b0d44e22b85 100644 --- a/source/blender/compositor/operations/COM_InpaintOperation.h +++ b/source/blender/compositor/operations/COM_InpaintOperation.h @@ -25,18 +25,18 @@ namespace blender::compositor { class InpaintSimpleOperation : public NodeOperation { protected: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputImageProgram; + SocketReader *input_image_program_; - int m_iterations; + int iterations_; - float *m_cached_buffer; - bool m_cached_buffer_ready; + float *cached_buffer_; + bool cached_buffer_ready_; - int *m_pixelorder; - int m_area_size; - short *m_manhattan_distance; + int *pixelorder_; + int area_size_; + short *manhattan_distance_; public: InpaintSimpleOperation(); @@ -44,27 +44,27 @@ class InpaintSimpleOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setIterations(int iterations) + void set_iterations(int iterations) { - this->m_iterations = iterations; + iterations_ = iterations; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_InvertOperation.cc b/source/blender/compositor/operations/COM_InvertOperation.cc index 4f71a1d0d1d..3722282bd73 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.cc +++ b/source/blender/compositor/operations/COM_InvertOperation.cc @@ -22,53 +22,56 @@ namespace blender::compositor { InvertOperation::InvertOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_inputValueProgram = nullptr; - this->m_inputColorProgram = nullptr; - this->m_color = true; - this->m_alpha = false; - setResolutionInputSocketIndex(1); - this->flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_program_ = nullptr; + input_color_program_ = nullptr; + color_ = true; + alpha_ = false; + set_canvas_input_index(1); + flags_.can_be_constant = true; } -void InvertOperation::initExecution() +void InvertOperation::init_execution() { - this->m_inputValueProgram = this->getInputSocketReader(0); - this->m_inputColorProgram = this->getInputSocketReader(1); + input_value_program_ = this->get_input_socket_reader(0); + input_color_program_ = this->get_input_socket_reader(1); } -void InvertOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void InvertOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; - float inputColor[4]; - this->m_inputValueProgram->readSampled(inputValue, x, y, sampler); - this->m_inputColorProgram->readSampled(inputColor, x, y, sampler); + float input_value[4]; + float input_color[4]; + input_value_program_->read_sampled(input_value, x, y, sampler); + input_color_program_->read_sampled(input_color, x, y, sampler); - const float value = inputValue[0]; - const float invertedValue = 1.0f - value; + const float value = input_value[0]; + const float inverted_value = 1.0f - value; - if (this->m_color) { - output[0] = (1.0f - inputColor[0]) * value + inputColor[0] * invertedValue; - output[1] = (1.0f - inputColor[1]) * value + inputColor[1] * invertedValue; - output[2] = (1.0f - inputColor[2]) * value + inputColor[2] * invertedValue; + if (color_) { + output[0] = (1.0f - input_color[0]) * value + input_color[0] * inverted_value; + output[1] = (1.0f - input_color[1]) * value + input_color[1] * inverted_value; + output[2] = (1.0f - input_color[2]) * value + input_color[2] * inverted_value; } else { - copy_v3_v3(output, inputColor); + copy_v3_v3(output, input_color); } - if (this->m_alpha) { - output[3] = (1.0f - inputColor[3]) * value + inputColor[3] * invertedValue; + if (alpha_) { + output[3] = (1.0f - input_color[3]) * value + input_color[3] * inverted_value; } else { - output[3] = inputColor[3]; + output[3] = input_color[3]; } } -void InvertOperation::deinitExecution() +void InvertOperation::deinit_execution() { - this->m_inputValueProgram = nullptr; - this->m_inputColorProgram = nullptr; + input_value_program_ = nullptr; + input_color_program_ = nullptr; } void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -80,7 +83,7 @@ void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, const float inverted_value = 1.0f - value; const float *color = it.in(1); - if (this->m_color) { + if (color_) { it.out[0] = (1.0f - color[0]) * value + color[0] * inverted_value; it.out[1] = (1.0f - color[1]) * value + color[1] * inverted_value; it.out[2] = (1.0f - color[2]) * value + color[2] * inverted_value; @@ -89,7 +92,7 @@ void InvertOperation::update_memory_buffer_partial(MemoryBuffer *output, copy_v3_v3(it.out, color); } - if (this->m_alpha) { + if (alpha_) { it.out[3] = (1.0f - color[3]) * value + color[3] * inverted_value; } else { diff --git a/source/blender/compositor/operations/COM_InvertOperation.h b/source/blender/compositor/operations/COM_InvertOperation.h index a084bf5d559..19abca968c0 100644 --- a/source/blender/compositor/operations/COM_InvertOperation.h +++ b/source/blender/compositor/operations/COM_InvertOperation.h @@ -25,13 +25,13 @@ namespace blender::compositor { class InvertOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputValueProgram; - SocketReader *m_inputColorProgram; + SocketReader *input_value_program_; + SocketReader *input_color_program_; - bool m_alpha; - bool m_color; + bool alpha_; + bool color_; public: InvertOperation(); @@ -39,25 +39,25 @@ class InvertOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setColor(bool color) + void set_color(bool color) { - this->m_color = color; + color_ = color; } - void setAlpha(bool alpha) + void set_alpha(bool alpha) { - this->m_alpha = alpha; + alpha_ = alpha; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc index d5ebd5e9df7..7fbe96d382b 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.cc @@ -18,53 +18,47 @@ #include "COM_KeyingBlurOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { KeyingBlurOperation::KeyingBlurOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); - this->m_size = 0; - this->m_axis = BLUR_AXIS_X; + size_ = 0; + axis_ = BLUR_AXIS_X; - this->flags.complex = true; + flags_.complex = true; } -void *KeyingBlurOperation::initializeTileData(rcti *rect) +void *KeyingBlurOperation::initialize_tile_data(rcti *rect) { - void *buffer = getInputOperation(0)->initializeTileData(rect); + void *buffer = get_input_operation(0)->initialize_tile_data(rect); return buffer; } -void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data) +void KeyingBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - const int bufferWidth = inputBuffer->getWidth(); - float *buffer = inputBuffer->getBuffer(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + const int buffer_width = input_buffer->get_width(); + float *buffer = input_buffer->get_buffer(); int count = 0; float average = 0.0f; - if (this->m_axis == 0) { - const int start = MAX2(0, x - this->m_size + 1), end = MIN2(bufferWidth, x + this->m_size); + if (axis_ == 0) { + const int start = MAX2(0, x - size_ + 1), end = MIN2(buffer_width, x + size_); for (int cx = start; cx < end; cx++) { - int bufferIndex = (y * bufferWidth + cx); - average += buffer[bufferIndex]; + int buffer_index = (y * buffer_width + cx); + average += buffer[buffer_index]; count++; } } else { - const int start = MAX2(0, y - this->m_size + 1), - end = MIN2(inputBuffer->getHeight(), y + this->m_size); + const int start = MAX2(0, y - size_ + 1), end = MIN2(input_buffer->get_height(), y + size_); for (int cy = start; cy < end; cy++) { - int bufferIndex = (cy * bufferWidth + x); - average += buffer[bufferIndex]; + int buffer_index = (cy * buffer_width + x); + average += buffer[buffer_index]; count++; } } @@ -74,44 +68,44 @@ void KeyingBlurOperation::executePixel(float output[4], int x, int y, void *data output[0] = average; } -bool KeyingBlurOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool KeyingBlurOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - if (this->m_axis == BLUR_AXIS_X) { - newInput.xmin = input->xmin - this->m_size; - newInput.ymin = input->ymin; - newInput.xmax = input->xmax + this->m_size; - newInput.ymax = input->ymax; + if (axis_ == BLUR_AXIS_X) { + new_input.xmin = input->xmin - size_; + new_input.ymin = input->ymin; + new_input.xmax = input->xmax + size_; + new_input.ymax = input->ymax; } else { - newInput.xmin = input->xmin; - newInput.ymin = input->ymin - this->m_size; - newInput.xmax = input->xmax; - newInput.ymax = input->ymax + this->m_size; + new_input.xmin = input->xmin; + new_input.ymin = input->ymin - size_; + new_input.xmax = input->xmax; + new_input.ymax = input->ymax + size_; } - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void KeyingBlurOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &output_area, rcti &r_input_area) { - switch (m_axis) { + switch (axis_) { case BLUR_AXIS_X: - r_input_area.xmin = output_area.xmin - m_size; + r_input_area.xmin = output_area.xmin - size_; r_input_area.ymin = output_area.ymin; - r_input_area.xmax = output_area.xmax + m_size; + r_input_area.xmax = output_area.xmax + size_; r_input_area.ymax = output_area.ymax; break; case BLUR_AXIS_Y: r_input_area.xmin = output_area.xmin; - r_input_area.ymin = output_area.ymin - m_size; + r_input_area.ymin = output_area.ymin - size_; r_input_area.xmax = output_area.xmax; - r_input_area.ymax = output_area.ymax + m_size; + r_input_area.ymax = output_area.ymax + size_; break; default: BLI_assert_msg(0, "Unknown axis"); @@ -129,23 +123,23 @@ void KeyingBlurOperation::update_memory_buffer_partial(MemoryBuffer *output, int coord_max; int elem_stride; std::function get_current_coord; - switch (m_axis) { + switch (axis_) { case BLUR_AXIS_X: get_current_coord = [&] { return it.x; }; - coord_max = this->getWidth(); + coord_max = this->get_width(); elem_stride = input->elem_stride; break; case BLUR_AXIS_Y: get_current_coord = [&] { return it.y; }; - coord_max = this->getHeight(); + coord_max = this->get_height(); elem_stride = input->row_stride; break; } for (; !it.is_end(); ++it) { const int coord = get_current_coord(); - const int start_coord = MAX2(0, coord - m_size + 1); - const int end_coord = MIN2(coord_max, coord + m_size); + const int start_coord = MAX2(0, coord - size_ + 1); + const int end_coord = MIN2(coord_max, coord + size_); const int count = end_coord - start_coord; float sum = 0.0f; diff --git a/source/blender/compositor/operations/COM_KeyingBlurOperation.h b/source/blender/compositor/operations/COM_KeyingBlurOperation.h index b290b905e63..11fde5f5436 100644 --- a/source/blender/compositor/operations/COM_KeyingBlurOperation.h +++ b/source/blender/compositor/operations/COM_KeyingBlurOperation.h @@ -27,8 +27,8 @@ namespace blender::compositor { */ class KeyingBlurOperation : public MultiThreadedOperation { protected: - int m_size; - int m_axis; + int size_; + int axis_; public: enum BlurAxis { @@ -38,22 +38,22 @@ class KeyingBlurOperation : public MultiThreadedOperation { KeyingBlurOperation(); - void setSize(int value) + void set_size(int value) { - this->m_size = value; + size_ = value; } - void setAxis(int value) + void set_axis(int value) { - this->m_axis = value; + axis_ = value; } - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.cc b/source/blender/compositor/operations/COM_KeyingClipOperation.cc index 817c920ed91..d52b2801811 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.cc @@ -18,56 +18,51 @@ #include "COM_KeyingClipOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { KeyingClipOperation::KeyingClipOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); - this->m_kernelRadius = 3; - this->m_kernelTolerance = 0.1f; + kernel_radius_ = 3; + kernel_tolerance_ = 0.1f; - this->m_clipBlack = 0.0f; - this->m_clipWhite = 1.0f; + clip_black_ = 0.0f; + clip_white_ = 1.0f; - this->m_isEdgeMatte = false; + is_edge_matte_ = false; - this->flags.complex = true; + flags_.complex = true; } -void *KeyingClipOperation::initializeTileData(rcti *rect) +void *KeyingClipOperation::initialize_tile_data(rcti *rect) { - void *buffer = getInputOperation(0)->initializeTileData(rect); + void *buffer = get_input_operation(0)->initialize_tile_data(rect); return buffer; } -void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data) +void KeyingClipOperation::execute_pixel(float output[4], int x, int y, void *data) { - const int delta = this->m_kernelRadius; - const float tolerance = this->m_kernelTolerance; + const int delta = kernel_radius_; + const float tolerance = kernel_tolerance_; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - float *buffer = inputBuffer->getBuffer(); + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + float *buffer = input_buffer->get_buffer(); - int bufferWidth = inputBuffer->getWidth(); - int bufferHeight = inputBuffer->getHeight(); + int buffer_width = input_buffer->get_width(); + int buffer_height = input_buffer->get_height(); - float value = buffer[(y * bufferWidth + x)]; + float value = buffer[(y * buffer_width + x)]; bool ok = false; int start_x = max_ff(0, x - delta + 1), start_y = max_ff(0, y - delta + 1), - end_x = min_ff(x + delta - 1, bufferWidth - 1), - end_y = min_ff(y + delta - 1, bufferHeight - 1); + end_x = min_ff(x + delta - 1, buffer_width - 1), + end_y = min_ff(y + delta - 1, buffer_height - 1); - int count = 0, totalCount = (end_x - start_x + 1) * (end_y - start_y + 1) - 1; - int thresholdCount = ceil((float)totalCount * 0.9f); + int count = 0, total_count = (end_x - start_x + 1) * (end_y - start_y + 1) - 1; + int threshold_count = ceil((float)total_count * 0.9f); if (delta == 0) { ok = true; @@ -79,19 +74,19 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data continue; } - int bufferIndex = (cy * bufferWidth + cx); - float currentValue = buffer[bufferIndex]; + int buffer_index = (cy * buffer_width + cx); + float current_value = buffer[buffer_index]; - if (fabsf(currentValue - value) < tolerance) { + if (fabsf(current_value - value) < tolerance) { count++; - if (count >= thresholdCount) { + if (count >= threshold_count) { ok = true; } } } } - if (this->m_isEdgeMatte) { + if (is_edge_matte_) { if (ok) { output[0] = 0.0f; } @@ -103,31 +98,31 @@ void KeyingClipOperation::executePixel(float output[4], int x, int y, void *data output[0] = value; if (ok) { - if (output[0] < this->m_clipBlack) { + if (output[0] < clip_black_) { output[0] = 0.0f; } - else if (output[0] >= this->m_clipWhite) { + else if (output[0] >= clip_white_) { output[0] = 1.0f; } else { - output[0] = (output[0] - this->m_clipBlack) / (this->m_clipWhite - this->m_clipBlack); + output[0] = (output[0] - clip_black_) / (clip_white_ - clip_black_); } } } } -bool KeyingClipOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool KeyingClipOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmin = input->xmin - this->m_kernelRadius; - newInput.ymin = input->ymin - this->m_kernelRadius; - newInput.xmax = input->xmax + this->m_kernelRadius; - newInput.ymax = input->ymax + this->m_kernelRadius; + new_input.xmin = input->xmin - kernel_radius_; + new_input.ymin = input->ymin - kernel_radius_; + new_input.xmax = input->xmax + kernel_radius_; + new_input.ymax = input->ymax + kernel_radius_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void KeyingClipOperation::get_area_of_interest(const int input_idx, @@ -136,10 +131,10 @@ void KeyingClipOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_kernelRadius; - r_input_area.xmax = output_area.xmax + m_kernelRadius; - r_input_area.ymin = output_area.ymin - m_kernelRadius; - r_input_area.ymax = output_area.ymax + m_kernelRadius; + r_input_area.xmin = output_area.xmin - kernel_radius_; + r_input_area.xmax = output_area.xmax + kernel_radius_; + r_input_area.ymin = output_area.ymin - kernel_radius_; + r_input_area.ymax = output_area.ymax + kernel_radius_; } void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -149,10 +144,10 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, const MemoryBuffer *input = inputs[0]; BuffersIterator it = output->iterate_with(inputs, area); - const int delta = m_kernelRadius; - const float tolerance = m_kernelTolerance; - const int width = this->getWidth(); - const int height = this->getHeight(); + const int delta = kernel_radius_; + const float tolerance = kernel_tolerance_; + const int width = this->get_width(); + const int height = this->get_height(); const int row_stride = input->row_stride; const int elem_stride = input->elem_stride; for (; !it.is_end(); ++it) { @@ -195,21 +190,21 @@ void KeyingClipOperation::update_memory_buffer_partial(MemoryBuffer *output, } } - if (m_isEdgeMatte) { + if (is_edge_matte_) { *it.out = ok ? 0.0f : 1.0f; } else { if (!ok) { *it.out = value; } - else if (value < m_clipBlack) { + else if (value < clip_black_) { *it.out = 0.0f; } - else if (value >= m_clipWhite) { + else if (value >= clip_white_) { *it.out = 1.0f; } else { - *it.out = (value - m_clipBlack) / (m_clipWhite - m_clipBlack); + *it.out = (value - clip_black_) / (clip_white_ - clip_black_); } } } diff --git a/source/blender/compositor/operations/COM_KeyingClipOperation.h b/source/blender/compositor/operations/COM_KeyingClipOperation.h index 1a17d591781..3bdc7281683 100644 --- a/source/blender/compositor/operations/COM_KeyingClipOperation.h +++ b/source/blender/compositor/operations/COM_KeyingClipOperation.h @@ -27,47 +27,47 @@ namespace blender::compositor { */ class KeyingClipOperation : public MultiThreadedOperation { protected: - float m_clipBlack; - float m_clipWhite; + float clip_black_; + float clip_white_; - int m_kernelRadius; - float m_kernelTolerance; + int kernel_radius_; + float kernel_tolerance_; - bool m_isEdgeMatte; + bool is_edge_matte_; public: KeyingClipOperation(); - void setClipBlack(float value) + void set_clip_black(float value) { - this->m_clipBlack = value; + clip_black_ = value; } - void setClipWhite(float value) + void set_clip_white(float value) { - this->m_clipWhite = value; + clip_white_ = value; } - void setKernelRadius(int value) + void set_kernel_radius(int value) { - this->m_kernelRadius = value; + kernel_radius_ = value; } - void setKernelTolerance(float value) + void set_kernel_tolerance(float value) { - this->m_kernelTolerance = value; + kernel_tolerance_ = value; } - void setIsEdgeMatte(bool value) + void set_is_edge_matte(bool value) { - this->m_isEdgeMatte = value; + is_edge_matte_ = value; } - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc index 620b767e584..d200d1e5eb3 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.cc @@ -18,51 +18,46 @@ #include "COM_KeyingDespillOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { KeyingDespillOperation::KeyingDespillOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - this->m_despillFactor = 0.5f; - this->m_colorBalance = 0.5f; + despill_factor_ = 0.5f; + color_balance_ = 0.5f; - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; - flags.can_be_constant = true; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; + flags_.can_be_constant = true; } -void KeyingDespillOperation::initExecution() +void KeyingDespillOperation::init_execution() { - this->m_pixelReader = this->getInputSocketReader(0); - this->m_screenReader = this->getInputSocketReader(1); + pixel_reader_ = this->get_input_socket_reader(0); + screen_reader_ = this->get_input_socket_reader(1); } -void KeyingDespillOperation::deinitExecution() +void KeyingDespillOperation::deinit_execution() { - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; } -void KeyingDespillOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void KeyingDespillOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float pixelColor[4]; - float screenColor[4]; + float pixel_color[4]; + float screen_color[4]; - this->m_pixelReader->readSampled(pixelColor, x, y, sampler); - this->m_screenReader->readSampled(screenColor, x, y, sampler); + pixel_reader_->read_sampled(pixel_color, x, y, sampler); + screen_reader_->read_sampled(screen_color, x, y, sampler); - const int screen_primary_channel = max_axis_v3(screenColor); + const int screen_primary_channel = max_axis_v3(screen_color); const int other_1 = (screen_primary_channel + 1) % 3; const int other_2 = (screen_primary_channel + 2) % 3; @@ -71,15 +66,15 @@ void KeyingDespillOperation::executePixelSampled(float output[4], float average_value, amount; - average_value = this->m_colorBalance * pixelColor[min_channel] + - (1.0f - this->m_colorBalance) * pixelColor[max_channel]; - amount = (pixelColor[screen_primary_channel] - average_value); + average_value = color_balance_ * pixel_color[min_channel] + + (1.0f - color_balance_) * pixel_color[max_channel]; + amount = (pixel_color[screen_primary_channel] - average_value); - copy_v4_v4(output, pixelColor); + copy_v4_v4(output, pixel_color); - const float amount_despill = this->m_despillFactor * amount; + const float amount_despill = despill_factor_ * amount; if (amount_despill > 0.0f) { - output[screen_primary_channel] = pixelColor[screen_primary_channel] - amount_despill; + output[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill; } } @@ -98,13 +93,13 @@ void KeyingDespillOperation::update_memory_buffer_partial(MemoryBuffer *output, const int min_channel = MIN2(other_1, other_2); const int max_channel = MAX2(other_1, other_2); - const float average_value = m_colorBalance * pixel_color[min_channel] + - (1.0f - m_colorBalance) * pixel_color[max_channel]; + const float average_value = color_balance_ * pixel_color[min_channel] + + (1.0f - color_balance_) * pixel_color[max_channel]; const float amount = (pixel_color[screen_primary_channel] - average_value); copy_v4_v4(it.out, pixel_color); - const float amount_despill = m_despillFactor * amount; + const float amount_despill = despill_factor_ * amount; if (amount_despill > 0.0f) { it.out[screen_primary_channel] = pixel_color[screen_primary_channel] - amount_despill; } diff --git a/source/blender/compositor/operations/COM_KeyingDespillOperation.h b/source/blender/compositor/operations/COM_KeyingDespillOperation.h index 16bed651d3a..b57fbd09e74 100644 --- a/source/blender/compositor/operations/COM_KeyingDespillOperation.h +++ b/source/blender/compositor/operations/COM_KeyingDespillOperation.h @@ -27,27 +27,27 @@ namespace blender::compositor { */ class KeyingDespillOperation : public MultiThreadedOperation { protected: - SocketReader *m_pixelReader; - SocketReader *m_screenReader; - float m_despillFactor; - float m_colorBalance; + SocketReader *pixel_reader_; + SocketReader *screen_reader_; + float despill_factor_; + float color_balance_; public: KeyingDespillOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setDespillFactor(float value) + void set_despill_factor(float value) { - this->m_despillFactor = value; + despill_factor_ = value; } - void setColorBalance(float value) + void set_color_balance(float value) { - this->m_colorBalance = value; + color_balance_ = value; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_KeyingOperation.cc b/source/blender/compositor/operations/COM_KeyingOperation.cc index 3edb5a5d34e..c7ab3aca2e2 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingOperation.cc @@ -18,14 +18,9 @@ #include "COM_KeyingOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - namespace blender::compositor { -static float get_pixel_saturation(const float pixelColor[4], +static float get_pixel_saturation(const float pixel_color[4], float screen_balance, int primary_channel) { @@ -35,43 +30,46 @@ static float get_pixel_saturation(const float pixelColor[4], const int min_channel = MIN2(other_1, other_2); const int max_channel = MAX2(other_1, other_2); - const float val = screen_balance * pixelColor[min_channel] + - (1.0f - screen_balance) * pixelColor[max_channel]; + const float val = screen_balance * pixel_color[min_channel] + + (1.0f - screen_balance) * pixel_color[max_channel]; - return (pixelColor[primary_channel] - val) * fabsf(1.0f - val); + return (pixel_color[primary_channel] - val) * fabsf(1.0f - val); } KeyingOperation::KeyingOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Value); - this->m_screenBalance = 0.5f; + screen_balance_ = 0.5f; - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; } -void KeyingOperation::initExecution() +void KeyingOperation::init_execution() { - this->m_pixelReader = this->getInputSocketReader(0); - this->m_screenReader = this->getInputSocketReader(1); + pixel_reader_ = this->get_input_socket_reader(0); + screen_reader_ = this->get_input_socket_reader(1); } -void KeyingOperation::deinitExecution() +void KeyingOperation::deinit_execution() { - this->m_pixelReader = nullptr; - this->m_screenReader = nullptr; + pixel_reader_ = nullptr; + screen_reader_ = nullptr; } -void KeyingOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void KeyingOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float pixel_color[4]; float screen_color[4]; - this->m_pixelReader->readSampled(pixel_color, x, y, sampler); - this->m_screenReader->readSampled(screen_color, x, y, sampler); + pixel_reader_->read_sampled(pixel_color, x, y, sampler); + screen_reader_->read_sampled(screen_color, x, y, sampler); const int primary_channel = max_axis_v3(screen_color); const float min_pixel_color = min_fff(pixel_color[0], pixel_color[1], pixel_color[2]); @@ -85,9 +83,8 @@ void KeyingOperation::executePixelSampled(float output[4], float x, float y, Pix output[0] = 1.0f; } else { - float saturation = get_pixel_saturation(pixel_color, this->m_screenBalance, primary_channel); - float screen_saturation = get_pixel_saturation( - screen_color, this->m_screenBalance, primary_channel); + float saturation = get_pixel_saturation(pixel_color, screen_balance_, primary_channel); + float screen_saturation = get_pixel_saturation(screen_color, screen_balance_, primary_channel); if (saturation < 0) { /* means main channel of pixel is different from screen, @@ -130,9 +127,9 @@ void KeyingOperation::update_memory_buffer_partial(MemoryBuffer *output, it.out[0] = 1.0f; } else { - const float saturation = get_pixel_saturation(pixel_color, m_screenBalance, primary_channel); + const float saturation = get_pixel_saturation(pixel_color, screen_balance_, primary_channel); const float screen_saturation = get_pixel_saturation( - screen_color, m_screenBalance, primary_channel); + screen_color, screen_balance_, primary_channel); if (saturation < 0) { /* Means main channel of pixel is different from screen, diff --git a/source/blender/compositor/operations/COM_KeyingOperation.h b/source/blender/compositor/operations/COM_KeyingOperation.h index e134ad54896..d7be4872065 100644 --- a/source/blender/compositor/operations/COM_KeyingOperation.h +++ b/source/blender/compositor/operations/COM_KeyingOperation.h @@ -31,23 +31,23 @@ namespace blender::compositor { */ class KeyingOperation : public MultiThreadedOperation { protected: - SocketReader *m_pixelReader; - SocketReader *m_screenReader; + SocketReader *pixel_reader_; + SocketReader *screen_reader_; - float m_screenBalance; + float screen_balance_; public: KeyingOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setScreenBalance(float value) + void set_screen_balance(float value) { - this->m_screenBalance = value; + screen_balance_ = value; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc index c00aafc19a2..c34eaad87ef 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.cc +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.cc @@ -18,12 +18,6 @@ #include "COM_KeyingScreenOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" - #include "BKE_movieclip.h" #include "BKE_tracking.h" @@ -34,32 +28,32 @@ namespace blender::compositor { KeyingScreenOperation::KeyingScreenOperation() { - this->addOutputSocket(DataType::Color); - this->m_movieClip = nullptr; - this->m_framenumber = 0; - this->m_trackingObject[0] = 0; - flags.complex = true; - m_cachedTriangulation = nullptr; + this->add_output_socket(DataType::Color); + movie_clip_ = nullptr; + framenumber_ = 0; + tracking_object_[0] = 0; + flags_.complex = true; + cached_triangulation_ = nullptr; } -void KeyingScreenOperation::initExecution() +void KeyingScreenOperation::init_execution() { - initMutex(); + init_mutex(); if (execution_model_ == eExecutionModel::FullFrame) { - BLI_assert(m_cachedTriangulation == nullptr); - if (m_movieClip) { - m_cachedTriangulation = buildVoronoiTriangulation(); + BLI_assert(cached_triangulation_ == nullptr); + if (movie_clip_) { + cached_triangulation_ = build_voronoi_triangulation(); } } else { - this->m_cachedTriangulation = nullptr; + cached_triangulation_ = nullptr; } } -void KeyingScreenOperation::deinitExecution() +void KeyingScreenOperation::deinit_execution() { - if (this->m_cachedTriangulation) { - TriangulationData *triangulation = this->m_cachedTriangulation; + if (cached_triangulation_) { + TriangulationData *triangulation = cached_triangulation_; if (triangulation->triangulated_points) { MEM_freeN(triangulation->triangulated_points); @@ -73,17 +67,17 @@ void KeyingScreenOperation::deinitExecution() MEM_freeN(triangulation->triangles_AABB); } - MEM_freeN(this->m_cachedTriangulation); + MEM_freeN(cached_triangulation_); - this->m_cachedTriangulation = nullptr; + cached_triangulation_ = nullptr; } } -KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTriangulation() +KeyingScreenOperation::TriangulationData *KeyingScreenOperation::build_voronoi_triangulation() { MovieClipUser user = {0}; TriangulationData *triangulation; - MovieTracking *tracking = &this->m_movieClip->tracking; + MovieTracking *tracking = &movie_clip_->tracking; MovieTrackingTrack *track; VoronoiSite *sites, *site; ImBuf *ibuf; @@ -91,12 +85,12 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri ListBase edges = {nullptr, nullptr}; int sites_total; int i; - int width = this->getWidth(); - int height = this->getHeight(); - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, this->m_framenumber); + int width = this->get_width(); + int height = this->get_height(); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, framenumber_); - if (this->m_trackingObject[0]) { - MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, this->m_trackingObject); + if (tracking_object_[0]) { + MovieTrackingObject *object = BKE_tracking_object_get_named(tracking, tracking_object_); if (!object) { return nullptr; @@ -132,7 +126,7 @@ KeyingScreenOperation::TriangulationData *KeyingScreenOperation::buildVoronoiTri } BKE_movieclip_user_set_frame(&user, clip_frame); - ibuf = BKE_movieclip_get_ibuf(this->m_movieClip, &user); + ibuf = BKE_movieclip_get_ibuf(movie_clip_, &user); if (!ibuf) { return nullptr; @@ -243,7 +237,7 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * int chunk_size = 20; int i; - triangulation = this->m_cachedTriangulation; + triangulation = cached_triangulation_; if (!triangulation) { return nullptr; @@ -275,24 +269,24 @@ KeyingScreenOperation::TileData *KeyingScreenOperation::triangulate(const rcti * return tile_data; } -void *KeyingScreenOperation::initializeTileData(rcti *rect) +void *KeyingScreenOperation::initialize_tile_data(rcti *rect) { - if (this->m_movieClip == nullptr) { + if (movie_clip_ == nullptr) { return nullptr; } - if (!this->m_cachedTriangulation) { - lockMutex(); - if (this->m_cachedTriangulation == nullptr) { - this->m_cachedTriangulation = buildVoronoiTriangulation(); + if (!cached_triangulation_) { + lock_mutex(); + if (cached_triangulation_ == nullptr) { + cached_triangulation_ = build_voronoi_triangulation(); } - unlockMutex(); + unlock_mutex(); } return triangulate(rect); } -void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data) +void KeyingScreenOperation::deinitialize_tile_data(rcti * /*rect*/, void *data) { TileData *tile_data = (TileData *)data; @@ -303,35 +297,32 @@ void KeyingScreenOperation::deinitializeTileData(rcti * /*rect*/, void *data) MEM_freeN(tile_data); } -void KeyingScreenOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void KeyingScreenOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = 0; - resolution[1] = 0; + r_area = COM_AREA_NONE; - if (this->m_movieClip) { + if (movie_clip_) { MovieClipUser user = {0}; int width, height; - int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_framenumber); + int clip_frame = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, framenumber_); BKE_movieclip_user_set_frame(&user, clip_frame); - BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height); - - resolution[0] = width; - resolution[1] = height; + BKE_movieclip_get_size(movie_clip_, &user, &width, &height); + r_area = preferred_area; + r_area.xmax = r_area.xmin + width; + r_area.ymax = r_area.ymin + height; } } -void KeyingScreenOperation::executePixel(float output[4], int x, int y, void *data) +void KeyingScreenOperation::execute_pixel(float output[4], int x, int y, void *data) { output[0] = 0.0f; output[1] = 0.0f; output[2] = 0.0f; output[3] = 1.0f; - if (this->m_movieClip && data) { - TriangulationData *triangulation = this->m_cachedTriangulation; + if (movie_clip_ && data) { + TriangulationData *triangulation = cached_triangulation_; TileData *tile_data = (TileData *)data; int i; float co[2] = {(float)x, (float)y}; @@ -365,7 +356,7 @@ void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - if (m_movieClip == nullptr) { + if (movie_clip_ == nullptr) { output->fill(area, COM_COLOR_BLACK); return; } @@ -375,7 +366,7 @@ void KeyingScreenOperation::update_memory_buffer_partial(MemoryBuffer *output, const int *triangles = tri_area->triangles; const int num_triangles = tri_area->triangles_total; - const TriangulationData *triangulation = m_cachedTriangulation; + const TriangulationData *triangulation = cached_triangulation_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { copy_v4_v4(it.out, COM_COLOR_BLACK); diff --git a/source/blender/compositor/operations/COM_KeyingScreenOperation.h b/source/blender/compositor/operations/COM_KeyingScreenOperation.h index 0bc47dbea30..3450a82a485 100644 --- a/source/blender/compositor/operations/COM_KeyingScreenOperation.h +++ b/source/blender/compositor/operations/COM_KeyingScreenOperation.h @@ -49,42 +49,41 @@ class KeyingScreenOperation : public MultiThreadedOperation { int triangles_total; } TileData; - MovieClip *m_movieClip; - int m_framenumber; - TriangulationData *m_cachedTriangulation; - char m_trackingObject[64]; + MovieClip *movie_clip_; + int framenumber_; + TriangulationData *cached_triangulation_; + char tracking_object_[64]; /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - TriangulationData *buildVoronoiTriangulation(); + TriangulationData *build_voronoi_triangulation(); public: KeyingScreenOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - this->m_movieClip = clip; + movie_clip_ = clip; } - void setTrackingObject(const char *object) + void set_tracking_object(const char *object) { - BLI_strncpy(this->m_trackingObject, object, sizeof(this->m_trackingObject)); + BLI_strncpy(tracking_object_, object, sizeof(tracking_object_)); } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { - this->m_framenumber = framenumber; + framenumber_ = framenumber; } - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc index c642c60b912..f2543d0d159 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.cc @@ -17,7 +17,6 @@ */ #include "COM_LuminanceMatteOperation.h" -#include "BLI_math.h" #include "IMB_colormanagement.h" @@ -25,39 +24,39 @@ namespace blender::compositor { LuminanceMatteOperation::LuminanceMatteOperation() { - addInputSocket(DataType::Color); - addOutputSocket(DataType::Value); + add_input_socket(DataType::Color); + add_output_socket(DataType::Value); - this->m_inputImageProgram = nullptr; - flags.can_be_constant = true; + input_image_program_ = nullptr; + flags_.can_be_constant = true; } -void LuminanceMatteOperation::initExecution() +void LuminanceMatteOperation::init_execution() { - this->m_inputImageProgram = this->getInputSocketReader(0); + input_image_program_ = this->get_input_socket_reader(0); } -void LuminanceMatteOperation::deinitExecution() +void LuminanceMatteOperation::deinit_execution() { - this->m_inputImageProgram = nullptr; + input_image_program_ = nullptr; } -void LuminanceMatteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void LuminanceMatteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inColor[4]; - this->m_inputImageProgram->readSampled(inColor, x, y, sampler); + float in_color[4]; + input_image_program_->read_sampled(in_color, x, y, sampler); - const float high = this->m_settings->t1; - const float low = this->m_settings->t2; - const float luminance = IMB_colormanagement_get_luminance(inColor); + const float high = settings_->t1; + const float low = settings_->t2; + const float luminance = IMB_colormanagement_get_luminance(in_color); float alpha; /* one line thread-friend algorithm: - * output[0] = MIN2(inputValue[3], MIN2(1.0f, MAX2(0.0f, ((luminance - low) / (high - low)))); + * output[0] = MIN2(input_value[3], MIN2(1.0f, MAX2(0.0f, ((luminance - low) / (high - low)))); */ /* test range */ @@ -76,7 +75,7 @@ void LuminanceMatteOperation::executePixelSampled(float output[4], */ /* don't make something that was more transparent less transparent */ - output[0] = min_ff(alpha, inColor[3]); + output[0] = min_ff(alpha, in_color[3]); } void LuminanceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -92,8 +91,8 @@ void LuminanceMatteOperation::update_memory_buffer_partial(MemoryBuffer *output, */ /* Test range. */ - const float high = m_settings->t1; - const float low = m_settings->t2; + const float high = settings_->t1; + const float low = settings_->t2; float alpha; if (luminance > high) { alpha = 1.0f; diff --git a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h index aedfc715382..7f9ccfc83ac 100644 --- a/source/blender/compositor/operations/COM_LuminanceMatteOperation.h +++ b/source/blender/compositor/operations/COM_LuminanceMatteOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { */ class LuminanceMatteOperation : public MultiThreadedOperation { private: - NodeChroma *m_settings; - SocketReader *m_inputImageProgram; + NodeChroma *settings_; + SocketReader *input_image_program_; public: /** @@ -40,14 +40,14 @@ class LuminanceMatteOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setSettings(NodeChroma *nodeChroma) + void set_settings(NodeChroma *node_chroma) { - this->m_settings = nodeChroma; + settings_ = node_chroma; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.cc b/source/blender/compositor/operations/COM_MapRangeOperation.cc index 82fb033bf24..7d63a473500 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.cc +++ b/source/blender/compositor/operations/COM_MapRangeOperation.cc @@ -22,45 +22,45 @@ namespace blender::compositor { MapRangeOperation::MapRangeOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; - this->m_useClamp = false; - flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; + use_clamp_ = false; + flags_.can_be_constant = true; } -void MapRangeOperation::initExecution() +void MapRangeOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_sourceMinOperation = this->getInputSocketReader(1); - this->m_sourceMaxOperation = this->getInputSocketReader(2); - this->m_destMinOperation = this->getInputSocketReader(3); - this->m_destMaxOperation = this->getInputSocketReader(4); + input_operation_ = this->get_input_socket_reader(0); + source_min_operation_ = this->get_input_socket_reader(1); + source_max_operation_ = this->get_input_socket_reader(2); + dest_min_operation_ = this->get_input_socket_reader(3); + dest_max_operation_ = this->get_input_socket_reader(4); } /* The code below assumes all data is inside range +- this, and that input buffer is single channel */ #define BLENDER_ZMAX 10000.0f -void MapRangeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MapRangeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float inputs[8]; /* includes the 5 inputs + 3 pads */ float value; float source_min, source_max; float dest_min, dest_max; - this->m_inputOperation->readSampled(inputs, x, y, sampler); - this->m_sourceMinOperation->readSampled(inputs + 1, x, y, sampler); - this->m_sourceMaxOperation->readSampled(inputs + 2, x, y, sampler); - this->m_destMinOperation->readSampled(inputs + 3, x, y, sampler); - this->m_destMaxOperation->readSampled(inputs + 4, x, y, sampler); + input_operation_->read_sampled(inputs, x, y, sampler); + source_min_operation_->read_sampled(inputs + 1, x, y, sampler); + source_max_operation_->read_sampled(inputs + 2, x, y, sampler); + dest_min_operation_->read_sampled(inputs + 3, x, y, sampler); + dest_max_operation_->read_sampled(inputs + 4, x, y, sampler); value = inputs[0]; source_min = inputs[1]; @@ -84,7 +84,7 @@ void MapRangeOperation::executePixelSampled(float output[4], value = dest_min; } - if (this->m_useClamp) { + if (use_clamp_) { if (dest_max > dest_min) { CLAMP(value, dest_min, dest_max); } @@ -96,13 +96,13 @@ void MapRangeOperation::executePixelSampled(float output[4], output[0] = value; } -void MapRangeOperation::deinitExecution() +void MapRangeOperation::deinit_execution() { - this->m_inputOperation = nullptr; - this->m_sourceMinOperation = nullptr; - this->m_sourceMaxOperation = nullptr; - this->m_destMinOperation = nullptr; - this->m_destMaxOperation = nullptr; + input_operation_ = nullptr; + source_min_operation_ = nullptr; + source_max_operation_ = nullptr; + dest_min_operation_ = nullptr; + dest_max_operation_ = nullptr; } void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -131,7 +131,7 @@ void MapRangeOperation::update_memory_buffer_partial(MemoryBuffer *output, value = dest_min; } - if (m_useClamp) { + if (use_clamp_) { if (dest_max > dest_min) { CLAMP(value, dest_min, dest_max); } diff --git a/source/blender/compositor/operations/COM_MapRangeOperation.h b/source/blender/compositor/operations/COM_MapRangeOperation.h index a01be14d528..0d0f3c90e35 100644 --- a/source/blender/compositor/operations/COM_MapRangeOperation.h +++ b/source/blender/compositor/operations/COM_MapRangeOperation.h @@ -30,15 +30,15 @@ namespace blender::compositor { class MapRangeOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputOperation; - SocketReader *m_sourceMinOperation; - SocketReader *m_sourceMaxOperation; - SocketReader *m_destMinOperation; - SocketReader *m_destMaxOperation; + SocketReader *input_operation_; + SocketReader *source_min_operation_; + SocketReader *source_max_operation_; + SocketReader *dest_min_operation_; + SocketReader *dest_max_operation_; - bool m_useClamp; + bool use_clamp_; public: /** @@ -49,24 +49,24 @@ class MapRangeOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * Clamp the output */ - void setUseClamp(bool value) + void set_use_clamp(bool value) { - this->m_useClamp = value; + use_clamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MapUVOperation.cc b/source/blender/compositor/operations/COM_MapUVOperation.cc index ad047c619f8..8f46332c9bc 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.cc +++ b/source/blender/compositor/operations/COM_MapUVOperation.cc @@ -17,71 +17,70 @@ */ #include "COM_MapUVOperation.h" -#include "BLI_math.h" namespace blender::compositor { MapUVOperation::MapUVOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Color); - this->m_alpha = 0.0f; - this->flags.complex = true; - setResolutionInputSocketIndex(1); + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Color); + alpha_ = 0.0f; + flags_.complex = true; + set_canvas_input_index(UV_INPUT_INDEX); - this->m_inputUVProgram = nullptr; - this->m_inputColorProgram = nullptr; + inputUVProgram_ = nullptr; + input_color_program_ = nullptr; } void MapUVOperation::init_data() { - NodeOperation *image_input = get_input_operation(0); - image_width_ = image_input->getWidth(); - image_height_ = image_input->getHeight(); + NodeOperation *image_input = get_input_operation(IMAGE_INPUT_INDEX); + image_width_ = image_input->get_width(); + image_height_ = image_input->get_height(); - NodeOperation *uv_input = get_input_operation(1); - uv_width_ = uv_input->getWidth(); - uv_height_ = uv_input->getHeight(); + NodeOperation *uv_input = get_input_operation(UV_INPUT_INDEX); + uv_width_ = uv_input->get_width(); + uv_height_ = uv_input->get_height(); } -void MapUVOperation::initExecution() +void MapUVOperation::init_execution() { - this->m_inputColorProgram = this->getInputSocketReader(0); - this->m_inputUVProgram = this->getInputSocketReader(1); + input_color_program_ = this->get_input_socket_reader(0); + inputUVProgram_ = this->get_input_socket_reader(1); if (execution_model_ == eExecutionModel::Tiled) { uv_input_read_fn_ = [=](float x, float y, float *out) { - this->m_inputUVProgram->readSampled(out, x, y, PixelSampler::Bilinear); + inputUVProgram_->read_sampled(out, x, y, PixelSampler::Bilinear); }; } } -void MapUVOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MapUVOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float xy[2] = {x, y}; float uv[2], deriv[2][2], alpha; - pixelTransform(xy, uv, deriv, alpha); + pixel_transform(xy, uv, deriv, alpha); if (alpha == 0.0f) { zero_v4(output); return; } /* EWA filtering */ - this->m_inputColorProgram->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + input_color_program_->read_filtered(output, uv[0], uv[1], deriv[0], deriv[1]); /* UV to alpha threshold */ - const float threshold = this->m_alpha * 0.05f; + const float threshold = alpha_ * 0.05f; /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. * this calculation is not very well defined, should be looked into if it becomes a problem ... */ float du = len_v2(deriv[0]); float dv = len_v2(deriv[1]); - float factor = 1.0f - threshold * (du / m_inputColorProgram->getWidth() + - dv / m_inputColorProgram->getHeight()); + float factor = 1.0f - threshold * (du / input_color_program_->get_width() + + dv / input_color_program_->get_height()); if (factor < 0.0f) { alpha = 0.0f; } @@ -112,10 +111,10 @@ bool MapUVOperation::read_uv(float x, float y, float &r_u, float &r_v, float &r_ return true; } -void MapUVOperation::pixelTransform(const float xy[2], - float r_uv[2], - float r_deriv[2][2], - float &r_alpha) +void MapUVOperation::pixel_transform(const float xy[2], + float r_uv[2], + float r_deriv[2][2], + float &r_alpha) { float uv[2], alpha; /* temporary variables for derivative estimation */ int num; @@ -163,37 +162,37 @@ void MapUVOperation::pixelTransform(const float xy[2], } } -void MapUVOperation::deinitExecution() +void MapUVOperation::deinit_execution() { - this->m_inputUVProgram = nullptr; - this->m_inputColorProgram = nullptr; + inputUVProgram_ = nullptr; + input_color_program_ = nullptr; } -bool MapUVOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool MapUVOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti colorInput; - rcti uvInput; + rcti color_input; + rcti uv_input; NodeOperation *operation = nullptr; /* the uv buffer only needs a 3x3 buffer. The image needs whole buffer */ - operation = getInputOperation(0); - colorInput.xmax = operation->getWidth(); - colorInput.xmin = 0; - colorInput.ymax = operation->getHeight(); - colorInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&colorInput, readOperation, output)) { + operation = get_input_operation(0); + color_input.xmax = operation->get_width(); + color_input.xmin = 0; + color_input.ymax = operation->get_height(); + color_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&color_input, read_operation, output)) { return true; } - operation = getInputOperation(1); - uvInput.xmax = input->xmax + 1; - uvInput.xmin = input->xmin - 1; - uvInput.ymax = input->ymax + 1; - uvInput.ymin = input->ymin - 1; - if (operation->determineDependingAreaOfInterest(&uvInput, readOperation, output)) { + operation = get_input_operation(1); + uv_input.xmax = input->xmax + 1; + uv_input.xmin = input->xmin - 1; + uv_input.ymax = input->ymax + 1; + uv_input.ymin = input->ymin - 1; + if (operation->determine_depending_area_of_interest(&uv_input, read_operation, output)) { return true; } @@ -205,14 +204,11 @@ void MapUVOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { switch (input_idx) { - case 0: { - r_input_area.xmin = 0; - r_input_area.xmax = image_width_; - r_input_area.ymin = 0; - r_input_area.ymax = image_height_; + case IMAGE_INPUT_INDEX: { + r_input_area = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); break; } - case 1: { + case UV_INPUT_INDEX: { r_input_area = output_area; expand_area_for_sampler(r_input_area, PixelSampler::Bilinear); break; @@ -224,7 +220,7 @@ void MapUVOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - const MemoryBuffer *uv_input = inputs[1]; + const MemoryBuffer *uv_input = inputs[UV_INPUT_INDEX]; uv_input_read_fn_ = [=](float x, float y, float *out) { uv_input->read_elem_bilinear(x, y, out); }; @@ -234,13 +230,13 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const MemoryBuffer *input_image = inputs[0]; + const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { float xy[2] = {(float)it.x, (float)it.y}; float uv[2]; float deriv[2][2]; float alpha; - pixelTransform(xy, uv, deriv, alpha); + pixel_transform(xy, uv, deriv, alpha); if (alpha == 0.0f) { zero_v4(it.out); continue; @@ -250,7 +246,7 @@ void MapUVOperation::update_memory_buffer_partial(MemoryBuffer *output, input_image->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); /* UV to alpha threshold. */ - const float threshold = this->m_alpha * 0.05f; + const float threshold = alpha_ * 0.05f; /* XXX alpha threshold is used to fade out pixels on boundaries with invalid derivatives. * this calculation is not very well defined, should be looked into if it becomes a problem ... */ diff --git a/source/blender/compositor/operations/COM_MapUVOperation.h b/source/blender/compositor/operations/COM_MapUVOperation.h index 65fbcb461c9..38d848f61a0 100644 --- a/source/blender/compositor/operations/COM_MapUVOperation.h +++ b/source/blender/compositor/operations/COM_MapUVOperation.h @@ -24,18 +24,20 @@ namespace blender::compositor { class MapUVOperation : public MultiThreadedOperation { private: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int UV_INPUT_INDEX = 1; /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputUVProgram; - SocketReader *m_inputColorProgram; + SocketReader *inputUVProgram_; + SocketReader *input_color_program_; int uv_width_; int uv_height_; int image_width_; int image_height_; - float m_alpha; + float alpha_; std::function uv_input_read_fn_; @@ -45,32 +47,32 @@ class MapUVOperation : public MultiThreadedOperation { /** * we need a 3x3 differential filter for UV Input and full buffer for the image */ - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void pixelTransform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha); + void pixel_transform(const float xy[2], float r_uv[2], float r_deriv[2][2], float &r_alpha); void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setAlpha(float alpha) + void set_alpha(float alpha) { - this->m_alpha = alpha; + alpha_ = alpha; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; diff --git a/source/blender/compositor/operations/COM_MapValueOperation.cc b/source/blender/compositor/operations/COM_MapValueOperation.cc index 94fecc3f49e..1e6570f2f64 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.cc +++ b/source/blender/compositor/operations/COM_MapValueOperation.cc @@ -22,25 +22,25 @@ namespace blender::compositor { MapValueOperation::MapValueOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputOperation = nullptr; - flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_operation_ = nullptr; + flags_.can_be_constant = true; } -void MapValueOperation::initExecution() +void MapValueOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void MapValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MapValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float src[4]; - this->m_inputOperation->readSampled(src, x, y, sampler); - TexMapping *texmap = this->m_settings; + input_operation_->read_sampled(src, x, y, sampler); + TexMapping *texmap = settings_; float value = (src[0] + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { @@ -56,9 +56,9 @@ void MapValueOperation::executePixelSampled(float output[4], output[0] = value; } -void MapValueOperation::deinitExecution() +void MapValueOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -67,7 +67,7 @@ void MapValueOperation::update_memory_buffer_partial(MemoryBuffer *output, { for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float input = *it.in(0); - TexMapping *texmap = this->m_settings; + TexMapping *texmap = settings_; float value = (input + texmap->loc[0]) * texmap->size[0]; if (texmap->flag & TEXMAP_CLIP_MIN) { if (value < texmap->min[0]) { diff --git a/source/blender/compositor/operations/COM_MapValueOperation.h b/source/blender/compositor/operations/COM_MapValueOperation.h index a595eac3155..77d3a4a2932 100644 --- a/source/blender/compositor/operations/COM_MapValueOperation.h +++ b/source/blender/compositor/operations/COM_MapValueOperation.h @@ -30,10 +30,10 @@ namespace blender::compositor { class MapValueOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputOperation; - TexMapping *m_settings; + SocketReader *input_operation_; + TexMapping *settings_; public: /** @@ -44,24 +44,24 @@ class MapValueOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * \brief set the TexMapping settings */ - void setSettings(TexMapping *settings) + void set_settings(TexMapping *settings) { - this->m_settings = settings; + settings_ = settings; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MaskOperation.cc b/source/blender/compositor/operations/COM_MaskOperation.cc index 84992f23924..4e4cc820edb 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.cc +++ b/source/blender/compositor/operations/COM_MaskOperation.cc @@ -18,11 +18,6 @@ #include "COM_MaskOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" - #include "BKE_lib_id.h" #include "BKE_mask.h" @@ -30,40 +25,35 @@ namespace blender::compositor { MaskOperation::MaskOperation() { - this->addOutputSocket(DataType::Value); - this->m_mask = nullptr; - this->m_maskWidth = 0; - this->m_maskHeight = 0; - this->m_maskWidthInv = 0.0f; - this->m_maskHeightInv = 0.0f; - this->m_frame_shutter = 0.0f; - this->m_frame_number = 0; - this->m_rasterMaskHandleTot = 1; - memset(this->m_rasterMaskHandles, 0, sizeof(this->m_rasterMaskHandles)); + this->add_output_socket(DataType::Value); + mask_ = nullptr; + mask_width_ = 0; + mask_height_ = 0; + mask_width_inv_ = 0.0f; + mask_height_inv_ = 0.0f; + frame_shutter_ = 0.0f; + frame_number_ = 0; + raster_mask_handle_tot_ = 1; + memset(raster_mask_handles_, 0, sizeof(raster_mask_handles_)); } -void MaskOperation::initExecution() +void MaskOperation::init_execution() { - if (this->m_mask && this->m_rasterMaskHandles[0] == nullptr) { - if (this->m_rasterMaskHandleTot == 1) { - this->m_rasterMaskHandles[0] = BKE_maskrasterize_handle_new(); + if (mask_ && raster_mask_handles_[0] == nullptr) { + if (raster_mask_handle_tot_ == 1) { + raster_mask_handles_[0] = BKE_maskrasterize_handle_new(); - BKE_maskrasterize_handle_init(this->m_rasterMaskHandles[0], - this->m_mask, - this->m_maskWidth, - this->m_maskHeight, - true, - true, - this->m_do_feather); + BKE_maskrasterize_handle_init( + raster_mask_handles_[0], mask_, mask_width_, mask_height_, true, true, do_feather_); } else { /* make a throw away copy of the mask */ - const float frame = (float)this->m_frame_number - this->m_frame_shutter; - const float frame_step = (this->m_frame_shutter * 2.0f) / this->m_rasterMaskHandleTot; + const float frame = (float)frame_number_ - frame_shutter_; + const float frame_step = (frame_shutter_ * 2.0f) / raster_mask_handle_tot_; float frame_iter = frame; Mask *mask_temp = (Mask *)BKE_id_copy_ex( - nullptr, &this->m_mask->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); + nullptr, &mask_->id, nullptr, LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); /* trick so we can get unkeyed edits to display */ { @@ -72,24 +62,24 @@ void MaskOperation::initExecution() for (masklay = (MaskLayer *)mask_temp->masklayers.first; masklay; masklay = masklay->next) { - masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, this->m_frame_number); + masklay_shape = BKE_mask_layer_shape_verify_frame(masklay, frame_number_); BKE_mask_layer_shape_from_mask(masklay, masklay_shape); } } - for (unsigned int i = 0; i < this->m_rasterMaskHandleTot; i++) { - this->m_rasterMaskHandles[i] = BKE_maskrasterize_handle_new(); + for (unsigned int i = 0; i < raster_mask_handle_tot_; i++) { + raster_mask_handles_[i] = BKE_maskrasterize_handle_new(); /* re-eval frame info */ BKE_mask_evaluate(mask_temp, frame_iter, true); - BKE_maskrasterize_handle_init(this->m_rasterMaskHandles[i], + BKE_maskrasterize_handle_init(raster_mask_handles_[i], mask_temp, - this->m_maskWidth, - this->m_maskHeight, + mask_width_, + mask_height_, true, true, - this->m_do_feather); + do_feather_); frame_iter += frame_step; } @@ -99,48 +89,41 @@ void MaskOperation::initExecution() } } -void MaskOperation::deinitExecution() +void MaskOperation::deinit_execution() { - for (unsigned int i = 0; i < this->m_rasterMaskHandleTot; i++) { - if (this->m_rasterMaskHandles[i]) { - BKE_maskrasterize_handle_free(this->m_rasterMaskHandles[i]); - this->m_rasterMaskHandles[i] = nullptr; + for (unsigned int i = 0; i < raster_mask_handle_tot_; i++) { + if (raster_mask_handles_[i]) { + BKE_maskrasterize_handle_free(raster_mask_handles_[i]); + raster_mask_handles_[i] = nullptr; } } } -void MaskOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (this->m_maskWidth == 0 || this->m_maskHeight == 0) { - NodeOperation::determineResolution(resolution, preferredResolution); + if (mask_width_ == 0 || mask_height_ == 0) { + r_area = COM_AREA_NONE; } else { - unsigned int nr[2]; - - nr[0] = this->m_maskWidth; - nr[1] = this->m_maskHeight; - - NodeOperation::determineResolution(resolution, nr); - - resolution[0] = this->m_maskWidth; - resolution[1] = this->m_maskHeight; + r_area = preferred_area; + r_area.xmax = r_area.xmin + mask_width_; + r_area.ymax = r_area.ymin + mask_height_; } } -void MaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { const float xy[2] = { - (x * this->m_maskWidthInv) + this->m_mask_px_ofs[0], - (y * this->m_maskHeightInv) + this->m_mask_px_ofs[1], + (x * mask_width_inv_) + mask_px_ofs_[0], + (y * mask_height_inv_) + mask_px_ofs_[1], }; - if (this->m_rasterMaskHandleTot == 1) { - if (this->m_rasterMaskHandles[0]) { - output[0] = BKE_maskrasterize_handle_sample(this->m_rasterMaskHandles[0], xy); + if (raster_mask_handle_tot_ == 1) { + if (raster_mask_handles_[0]) { + output[0] = BKE_maskrasterize_handle_sample(raster_mask_handles_[0], xy); } else { output[0] = 0.0f; @@ -150,14 +133,14 @@ void MaskOperation::executePixelSampled(float output[4], /* In case loop below fails. */ output[0] = 0.0f; - for (unsigned int i = 0; i < this->m_rasterMaskHandleTot; i++) { - if (this->m_rasterMaskHandles[i]) { - output[0] += BKE_maskrasterize_handle_sample(this->m_rasterMaskHandles[i], xy); + for (unsigned int i = 0; i < raster_mask_handle_tot_; i++) { + if (raster_mask_handles_[i]) { + output[0] += BKE_maskrasterize_handle_sample(raster_mask_handles_[i], xy); } } /* until we get better falloff */ - output[0] /= this->m_rasterMaskHandleTot; + output[0] /= raster_mask_handle_tot_; } } @@ -173,23 +156,23 @@ void MaskOperation::update_memory_buffer_partial(MemoryBuffer *output, float xy[2]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - xy[0] = it.x * m_maskWidthInv + m_mask_px_ofs[0]; - xy[1] = it.y * m_maskHeightInv + m_mask_px_ofs[1]; + xy[0] = it.x * mask_width_inv_ + mask_px_ofs_[0]; + xy[1] = it.y * mask_height_inv_ + mask_px_ofs_[1]; *it.out = 0.0f; for (MaskRasterHandle *handle : handles) { *it.out += BKE_maskrasterize_handle_sample(handle, xy); } /* Until we get better falloff. */ - *it.out /= m_rasterMaskHandleTot; + *it.out /= raster_mask_handle_tot_; } } Vector MaskOperation::get_non_null_handles() const { Vector handles; - for (int i = 0; i < m_rasterMaskHandleTot; i++) { - MaskRasterHandle *handle = m_rasterMaskHandles[i]; + for (int i = 0; i < raster_mask_handle_tot_; i++) { + MaskRasterHandle *handle = raster_mask_handles_[i]; if (handle == nullptr) { continue; } diff --git a/source/blender/compositor/operations/COM_MaskOperation.h b/source/blender/compositor/operations/COM_MaskOperation.h index 81e344c0451..5fc0ed96c22 100644 --- a/source/blender/compositor/operations/COM_MaskOperation.h +++ b/source/blender/compositor/operations/COM_MaskOperation.h @@ -33,71 +33,70 @@ namespace blender::compositor { */ class MaskOperation : public MultiThreadedOperation { protected: - Mask *m_mask; + Mask *mask_; /* NOTE: these are used more like aspect, * but they _do_ impact on mask detail */ - int m_maskWidth; - int m_maskHeight; - float m_maskWidthInv; /* `1 / m_maskWidth` */ - float m_maskHeightInv; /* `1 / m_maskHeight` */ - float m_mask_px_ofs[2]; + int mask_width_; + int mask_height_; + float mask_width_inv_; /* `1 / mask_width_` */ + float mask_height_inv_; /* `1 / mask_height_` */ + float mask_px_ofs_[2]; - float m_frame_shutter; - int m_frame_number; + float frame_shutter_; + int frame_number_; - bool m_do_feather; + bool do_feather_; - struct MaskRasterHandle *m_rasterMaskHandles[CMP_NODE_MASK_MBLUR_SAMPLES_MAX]; - unsigned int m_rasterMaskHandleTot; + struct MaskRasterHandle *raster_mask_handles_[CMP_NODE_MASK_MBLUR_SAMPLES_MAX]; + unsigned int raster_mask_handle_tot_; /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; public: MaskOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setMask(Mask *mask) + void set_mask(Mask *mask) { - this->m_mask = mask; + mask_ = mask; } - void setMaskWidth(int width) + void set_mask_width(int width) { - this->m_maskWidth = width; - this->m_maskWidthInv = 1.0f / (float)width; - this->m_mask_px_ofs[0] = this->m_maskWidthInv * 0.5f; + mask_width_ = width; + mask_width_inv_ = 1.0f / (float)width; + mask_px_ofs_[0] = mask_width_inv_ * 0.5f; } - void setMaskHeight(int height) + void set_mask_height(int height) { - this->m_maskHeight = height; - this->m_maskHeightInv = 1.0f / (float)height; - this->m_mask_px_ofs[1] = this->m_maskHeightInv * 0.5f; + mask_height_ = height; + mask_height_inv_ = 1.0f / (float)height; + mask_px_ofs_[1] = mask_height_inv_ * 0.5f; } - void setFramenumber(int frame_number) + void set_framenumber(int frame_number) { - this->m_frame_number = frame_number; + frame_number_ = frame_number; } - void setFeather(bool feather) + void set_feather(bool feather) { - this->m_do_feather = feather; + do_feather_ = feather; } - void setMotionBlurSamples(int samples) + void set_motion_blur_samples(int samples) { - this->m_rasterMaskHandleTot = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); + raster_mask_handle_tot_ = MIN2(MAX2(1, samples), CMP_NODE_MASK_MBLUR_SAMPLES_MAX); } - void setMotionBlurShutter(float shutter) + void set_motion_blur_shutter(float shutter) { - this->m_frame_shutter = shutter; + frame_shutter_ = shutter; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.cc b/source/blender/compositor/operations/COM_MathBaseOperation.cc index 2256dce011b..86993ffe887 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.cc +++ b/source/blender/compositor/operations/COM_MathBaseOperation.cc @@ -18,60 +18,55 @@ #include "COM_MathBaseOperation.h" -#include "BLI_math.h" - namespace blender::compositor { MathBaseOperation::MathBaseOperation() { /* TODO(manzanilla): after removing tiled implementation, template this class to only add needed * number of inputs. */ - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_inputValue1Operation = nullptr; - this->m_inputValue2Operation = nullptr; - this->m_inputValue3Operation = nullptr; - this->m_useClamp = false; - this->flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + input_value1_operation_ = nullptr; + input_value2_operation_ = nullptr; + input_value3_operation_ = nullptr; + use_clamp_ = false; + flags_.can_be_constant = true; } -void MathBaseOperation::initExecution() +void MathBaseOperation::init_execution() { - this->m_inputValue1Operation = this->getInputSocketReader(0); - this->m_inputValue2Operation = this->getInputSocketReader(1); - this->m_inputValue3Operation = this->getInputSocketReader(2); + input_value1_operation_ = this->get_input_socket_reader(0); + input_value2_operation_ = this->get_input_socket_reader(1); + input_value3_operation_ = this->get_input_socket_reader(2); } -void MathBaseOperation::deinitExecution() +void MathBaseOperation::deinit_execution() { - this->m_inputValue1Operation = nullptr; - this->m_inputValue2Operation = nullptr; - this->m_inputValue3Operation = nullptr; + input_value1_operation_ = nullptr; + input_value2_operation_ = nullptr; + input_value3_operation_ = nullptr; } -void MathBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MathBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperationInput *socket; - unsigned int tempPreferredResolution[2] = {0, 0}; - unsigned int tempResolution[2]; - - socket = this->getInputSocket(0); - socket->determineResolution(tempResolution, tempPreferredResolution); - if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) { - this->setResolutionInputSocketIndex(0); + rcti temp_area; + socket = this->get_input_socket(0); + const bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area); + if (determined) { + this->set_canvas_input_index(0); } else { - this->setResolutionInputSocketIndex(1); + this->set_canvas_input_index(1); } - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); } -void MathBaseOperation::clampIfNeeded(float *color) +void MathBaseOperation::clamp_if_needed(float *color) { - if (this->m_useClamp) { + if (use_clamp_) { CLAMP(color[0], 0.0f, 1.0f); } } @@ -84,70 +79,73 @@ void MathBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, update_memory_buffer_partial(it); } -void MathAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MathAddOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = inputValue1[0] + inputValue2[0]; + output[0] = input_value1[0] + input_value2[0]; - clampIfNeeded(output); + clamp_if_needed(output); } -void MathSubtractOperation::executePixelSampled(float output[4], +void MathSubtractOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] - input_value2[0]; + + clamp_if_needed(output); +} + +void MathMultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] * input_value2[0]; + + clamp_if_needed(output); +} + +void MathDivideOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = inputValue1[0] - inputValue2[0]; - - clampIfNeeded(output); -} - -void MathMultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - - output[0] = inputValue1[0] * inputValue2[0]; - - clampIfNeeded(output); -} - -void MathDivideOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - - if (inputValue2[0] == 0) { /* We don't want to divide by zero. */ + if (input_value2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0; } else { - output[0] = inputValue1[0] / inputValue2[0]; + output[0] = input_value1[0] / input_value2[0]; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathDivideOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -158,20 +156,20 @@ void MathDivideOperation::update_memory_buffer_partial(BuffersIterator &i } } -void MathSineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = sin(inputValue1[0]); + output[0] = sin(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -182,20 +180,20 @@ void MathSineOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathCosineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathCosineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = cos(inputValue1[0]); + output[0] = cos(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathCosineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -206,20 +204,20 @@ void MathCosineOperation::update_memory_buffer_partial(BuffersIterator &i } } -void MathTangentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathTangentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = tan(inputValue1[0]); + output[0] = tan(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathTangentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -230,20 +228,20 @@ void MathTangentOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathHyperbolicSineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathHyperbolicSineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = sinh(inputValue1[0]); + output[0] = sinh(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathHyperbolicSineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -254,20 +252,20 @@ void MathHyperbolicSineOperation::update_memory_buffer_partial(BuffersIteratorm_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = cosh(inputValue1[0]); + output[0] = cosh(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathHyperbolicCosineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -278,20 +276,20 @@ void MathHyperbolicCosineOperation::update_memory_buffer_partial(BuffersIterator } } -void MathHyperbolicTangentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathHyperbolicTangentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = tanh(inputValue1[0]); + output[0] = tanh(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathHyperbolicTangentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -302,25 +300,25 @@ void MathHyperbolicTangentOperation::update_memory_buffer_partial(BuffersIterato } } -void MathArcSineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcSineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { - output[0] = asin(inputValue1[0]); + if (input_value1[0] <= 1 && input_value1[0] >= -1) { + output[0] = asin(input_value1[0]); } else { output[0] = 0.0; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcSineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -331,25 +329,25 @@ void MathArcSineOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathArcCosineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcCosineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] <= 1 && inputValue1[0] >= -1) { - output[0] = acos(inputValue1[0]); + if (input_value1[0] <= 1 && input_value1[0] >= -1) { + output[0] = acos(input_value1[0]); } else { output[0] = 0.0; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcCosineOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -360,20 +358,20 @@ void MathArcCosineOperation::update_memory_buffer_partial(BuffersIterator } } -void MathArcTangentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcTangentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = atan(inputValue1[0]); + output[0] = atan(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcTangentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -384,32 +382,32 @@ void MathArcTangentOperation::update_memory_buffer_partial(BuffersIteratorm_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] >= 0) { - output[0] = pow(inputValue1[0], inputValue2[0]); + if (input_value1[0] >= 0) { + output[0] = pow(input_value1[0], input_value2[0]); } else { - float y_mod_1 = fmod(inputValue2[0], 1); + float y_mod_1 = fmod(input_value2[0], 1); /* if input value is not nearly an integer, fall back to zero, nicer than straight rounding */ if (y_mod_1 > 0.999f || y_mod_1 < 0.001f) { - output[0] = pow(inputValue1[0], floorf(inputValue2[0] + 0.5f)); + output[0] = pow(input_value1[0], floorf(input_value2[0] + 0.5f)); } else { output[0] = 0.0; } } - clampIfNeeded(output); + clamp_if_needed(output); } void MathPowerOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -435,25 +433,25 @@ void MathPowerOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathLogarithmOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathLogarithmOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] > 0 && inputValue2[0] > 0) { - output[0] = log(inputValue1[0]) / log(inputValue2[0]); + if (input_value1[0] > 0 && input_value2[0] > 0) { + output[0] = log(input_value1[0]) / log(input_value2[0]); } else { output[0] = 0.0; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathLogarithmOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -471,20 +469,20 @@ void MathLogarithmOperation::update_memory_buffer_partial(BuffersIterator } } -void MathMinimumOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathMinimumOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = MIN2(inputValue1[0], inputValue2[0]); + output[0] = MIN2(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathMinimumOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -495,20 +493,20 @@ void MathMinimumOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathMaximumOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathMaximumOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = MAX2(inputValue1[0], inputValue2[0]); + output[0] = MAX2(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathMaximumOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -519,20 +517,20 @@ void MathMaximumOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathRoundOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathRoundOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = round(inputValue1[0]); + output[0] = round(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathRoundOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -543,57 +541,57 @@ void MathRoundOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathLessThanOperation::executePixelSampled(float output[4], +void MathLessThanOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] < input_value2[0] ? 1.0f : 0.0f; + + clamp_if_needed(output); +} + +void MathGreaterThanOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) +{ + float input_value1[4]; + float input_value2[4]; + + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + + output[0] = input_value1[0] > input_value2[0] ? 1.0f : 0.0f; + + clamp_if_needed(output); +} + +void MathModuloOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = inputValue1[0] < inputValue2[0] ? 1.0f : 0.0f; - - clampIfNeeded(output); -} - -void MathGreaterThanOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - - output[0] = inputValue1[0] > inputValue2[0] ? 1.0f : 0.0f; - - clampIfNeeded(output); -} - -void MathModuloOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) -{ - float inputValue1[4]; - float inputValue2[4]; - - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - - if (inputValue2[0] == 0) { + if (input_value2[0] == 0) { output[0] = 0.0; } else { - output[0] = fmod(inputValue1[0], inputValue2[0]); + output[0] = fmod(input_value1[0], input_value2[0]); } - clampIfNeeded(output); + clamp_if_needed(output); } void MathModuloOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -605,18 +603,18 @@ void MathModuloOperation::update_memory_buffer_partial(BuffersIterator &i } } -void MathAbsoluteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathAbsoluteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = fabs(inputValue1[0]); + output[0] = fabs(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathAbsoluteOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -627,18 +625,18 @@ void MathAbsoluteOperation::update_memory_buffer_partial(BuffersIterator } } -void MathRadiansOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathRadiansOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = DEG2RADF(inputValue1[0]); + output[0] = DEG2RADF(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathRadiansOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -649,18 +647,18 @@ void MathRadiansOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathDegreesOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathDegreesOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = RAD2DEGF(inputValue1[0]); + output[0] = RAD2DEGF(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathDegreesOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -671,20 +669,20 @@ void MathDegreesOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathArcTan2Operation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathArcTan2Operation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = atan2(inputValue1[0], inputValue2[0]); + output[0] = atan2(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathArcTan2Operation::update_memory_buffer_partial(BuffersIterator &it) @@ -695,18 +693,18 @@ void MathArcTan2Operation::update_memory_buffer_partial(BuffersIterator & } } -void MathFloorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathFloorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = floor(inputValue1[0]); + output[0] = floor(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathFloorOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -717,18 +715,18 @@ void MathFloorOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathCeilOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathCeilOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = ceil(inputValue1[0]); + output[0] = ceil(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathCeilOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -739,18 +737,18 @@ void MathCeilOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathFractOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathFractOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = inputValue1[0] - floor(inputValue1[0]); + output[0] = input_value1[0] - floor(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathFractOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -761,23 +759,23 @@ void MathFractOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathSqrtOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSqrtOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - if (inputValue1[0] > 0) { - output[0] = sqrt(inputValue1[0]); + if (input_value1[0] > 0) { + output[0] = sqrt(input_value1[0]); } else { output[0] = 0.0f; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathSqrtOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -788,23 +786,23 @@ void MathSqrtOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathInverseSqrtOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathInverseSqrtOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - if (inputValue1[0] > 0) { - output[0] = 1.0f / sqrt(inputValue1[0]); + if (input_value1[0] > 0) { + output[0] = 1.0f / sqrt(input_value1[0]); } else { output[0] = 0.0f; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathInverseSqrtOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -815,18 +813,18 @@ void MathInverseSqrtOperation::update_memory_buffer_partial(BuffersIteratorm_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = compatible_signf(inputValue1[0]); + output[0] = compatible_signf(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSignOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -837,18 +835,18 @@ void MathSignOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathExponentOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathExponentOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = expf(inputValue1[0]); + output[0] = expf(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathExponentOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -859,18 +857,18 @@ void MathExponentOperation::update_memory_buffer_partial(BuffersIterator } } -void MathTruncOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathTruncOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; + float input_value1[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); - output[0] = (inputValue1[0] >= 0.0f) ? floor(inputValue1[0]) : ceil(inputValue1[0]); + output[0] = (input_value1[0] >= 0.0f) ? floor(input_value1[0]) : ceil(input_value1[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathTruncOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -882,25 +880,25 @@ void MathTruncOperation::update_memory_buffer_partial(BuffersIterator &it } } -void MathSnapOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSnapOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - if (inputValue1[0] == 0 || inputValue2[0] == 0) { /* We don't want to divide by zero. */ + if (input_value1[0] == 0 || input_value2[0] == 0) { /* We don't want to divide by zero. */ output[0] = 0.0f; } else { - output[0] = floorf(inputValue1[0] / inputValue2[0]) * inputValue2[0]; + output[0] = floorf(input_value1[0] / input_value2[0]) * input_value2[0]; } - clampIfNeeded(output); + clamp_if_needed(output); } void MathSnapOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -918,22 +916,22 @@ void MathSnapOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathWrapOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathWrapOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = wrapf(inputValue1[0], inputValue2[0], inputValue3[0]); + output[0] = wrapf(input_value1[0], input_value2[0], input_value3[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathWrapOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -944,20 +942,20 @@ void MathWrapOperation::update_memory_buffer_partial(BuffersIterator &it) } } -void MathPingpongOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathPingpongOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; + float input_value1[4]; + float input_value2[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); - output[0] = pingpongf(inputValue1[0], inputValue2[0]); + output[0] = pingpongf(input_value1[0], input_value2[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathPingpongOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -968,23 +966,23 @@ void MathPingpongOperation::update_memory_buffer_partial(BuffersIterator } } -void MathCompareOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathCompareOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = (fabsf(inputValue1[0] - inputValue2[0]) <= MAX2(inputValue3[0], 1e-5f)) ? 1.0f : - 0.0f; + output[0] = (fabsf(input_value1[0] - input_value2[0]) <= MAX2(input_value3[0], 1e-5f)) ? 1.0f : + 0.0f; - clampIfNeeded(output); + clamp_if_needed(output); } void MathCompareOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -995,22 +993,22 @@ void MathCompareOperation::update_memory_buffer_partial(BuffersIterator & } } -void MathMultiplyAddOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathMultiplyAddOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = inputValue1[0] * inputValue2[0] + inputValue3[0]; + output[0] = input_value1[0] * input_value2[0] + input_value3[0]; - clampIfNeeded(output); + clamp_if_needed(output); } void MathMultiplyAddOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -1021,22 +1019,22 @@ void MathMultiplyAddOperation::update_memory_buffer_partial(BuffersIteratorm_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = smoothminf(inputValue1[0], inputValue2[0], inputValue3[0]); + output[0] = smoothminf(input_value1[0], input_value2[0], input_value3[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSmoothMinOperation::update_memory_buffer_partial(BuffersIterator &it) @@ -1047,22 +1045,22 @@ void MathSmoothMinOperation::update_memory_buffer_partial(BuffersIterator } } -void MathSmoothMaxOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MathSmoothMaxOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue1[4]; - float inputValue2[4]; - float inputValue3[4]; + float input_value1[4]; + float input_value2[4]; + float input_value3[4]; - this->m_inputValue1Operation->readSampled(inputValue1, x, y, sampler); - this->m_inputValue2Operation->readSampled(inputValue2, x, y, sampler); - this->m_inputValue3Operation->readSampled(inputValue3, x, y, sampler); + input_value1_operation_->read_sampled(input_value1, x, y, sampler); + input_value2_operation_->read_sampled(input_value2, x, y, sampler); + input_value3_operation_->read_sampled(input_value3, x, y, sampler); - output[0] = -smoothminf(-inputValue1[0], -inputValue2[0], inputValue3[0]); + output[0] = -smoothminf(-input_value1[0], -input_value2[0], input_value3[0]); - clampIfNeeded(output); + clamp_if_needed(output); } void MathSmoothMaxOperation::update_memory_buffer_partial(BuffersIterator &it) diff --git a/source/blender/compositor/operations/COM_MathBaseOperation.h b/source/blender/compositor/operations/COM_MathBaseOperation.h index d2da05db68e..dc5a16ba4a1 100644 --- a/source/blender/compositor/operations/COM_MathBaseOperation.h +++ b/source/blender/compositor/operations/COM_MathBaseOperation.h @@ -29,13 +29,13 @@ namespace blender::compositor { class MathBaseOperation : public MultiThreadedOperation { protected: /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *m_inputValue1Operation; - SocketReader *m_inputValue2Operation; - SocketReader *m_inputValue3Operation; + SocketReader *input_value1_operation_; + SocketReader *input_value2_operation_; + SocketReader *input_value3_operation_; - bool m_useClamp; + bool use_clamp_; protected: /** @@ -44,11 +44,11 @@ class MathBaseOperation : public MultiThreadedOperation { MathBaseOperation(); /* TODO(manzanilla): to be removed with tiled implementation. */ - void clampIfNeeded(float color[4]); + void clamp_if_needed(float color[4]); float clamp_when_enabled(float value) { - if (this->m_useClamp) { + if (use_clamp_) { return CLAMPIS(value, 0.0f, 1.0f); } return value; @@ -56,7 +56,7 @@ class MathBaseOperation : public MultiThreadedOperation { void clamp_when_enabled(float *out) { - if (this->m_useClamp) { + if (use_clamp_) { CLAMP(*out, 0.0f, 1.0f); } } @@ -65,22 +65,21 @@ class MathBaseOperation : public MultiThreadedOperation { /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; /** * Determine resolution */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setUseClamp(bool value) + void set_use_clamp(bool value) { - this->m_useClamp = value; + use_clamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -105,40 +104,40 @@ class MathFunctor2Operation : public MathBaseOperation { class MathAddOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathSubtractOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathMultiplyOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathDivideOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathSineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathCosineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathTangentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -146,21 +145,21 @@ class MathTangentOperation : public MathBaseOperation { class MathHyperbolicSineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathHyperbolicCosineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathHyperbolicTangentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -168,72 +167,72 @@ class MathHyperbolicTangentOperation : public MathBaseOperation { class MathArcSineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathArcCosineOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathArcTangentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathPowerOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathLogarithmOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathMinimumOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathMaximumOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathRoundOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; }; class MathLessThanOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathGreaterThanOperation : public MathFunctor2Operation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MathModuloOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -241,7 +240,7 @@ class MathModuloOperation : public MathBaseOperation { class MathAbsoluteOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -249,7 +248,7 @@ class MathAbsoluteOperation : public MathBaseOperation { class MathRadiansOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -257,7 +256,7 @@ class MathRadiansOperation : public MathBaseOperation { class MathDegreesOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -265,7 +264,7 @@ class MathDegreesOperation : public MathBaseOperation { class MathArcTan2Operation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -273,7 +272,7 @@ class MathArcTan2Operation : public MathBaseOperation { class MathFloorOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -281,7 +280,7 @@ class MathFloorOperation : public MathBaseOperation { class MathCeilOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -289,7 +288,7 @@ class MathCeilOperation : public MathBaseOperation { class MathFractOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -297,7 +296,7 @@ class MathFractOperation : public MathBaseOperation { class MathSqrtOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -305,7 +304,7 @@ class MathSqrtOperation : public MathBaseOperation { class MathInverseSqrtOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -313,7 +312,7 @@ class MathInverseSqrtOperation : public MathBaseOperation { class MathSignOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -321,7 +320,7 @@ class MathSignOperation : public MathBaseOperation { class MathExponentOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -329,7 +328,7 @@ class MathExponentOperation : public MathBaseOperation { class MathTruncOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -337,7 +336,7 @@ class MathTruncOperation : public MathBaseOperation { class MathSnapOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -345,7 +344,7 @@ class MathSnapOperation : public MathBaseOperation { class MathWrapOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -353,7 +352,7 @@ class MathWrapOperation : public MathBaseOperation { class MathPingpongOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -361,7 +360,7 @@ class MathPingpongOperation : public MathBaseOperation { class MathCompareOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -369,7 +368,7 @@ class MathCompareOperation : public MathBaseOperation { class MathMultiplyAddOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -377,7 +376,7 @@ class MathMultiplyAddOperation : public MathBaseOperation { class MathSmoothMinOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; @@ -385,7 +384,7 @@ class MathSmoothMinOperation : public MathBaseOperation { class MathSmoothMaxOperation : public MathBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_partial(BuffersIterator &it) override; diff --git a/source/blender/compositor/operations/COM_MixOperation.cc b/source/blender/compositor/operations/COM_MixOperation.cc index 77ecbf60356..09bbb633459 100644 --- a/source/blender/compositor/operations/COM_MixOperation.cc +++ b/source/blender/compositor/operations/COM_MixOperation.cc @@ -18,84 +18,83 @@ #include "COM_MixOperation.h" -#include "BLI_math.h" - namespace blender::compositor { /* ******** Mix Base Operation ******** */ MixBaseOperation::MixBaseOperation() { - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_inputValueOperation = nullptr; - this->m_inputColor1Operation = nullptr; - this->m_inputColor2Operation = nullptr; - this->setUseValueAlphaMultiply(false); - this->setUseClamp(false); - flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + input_value_operation_ = nullptr; + input_color1_operation_ = nullptr; + input_color2_operation_ = nullptr; + this->set_use_value_alpha_multiply(false); + this->set_use_clamp(false); + flags_.can_be_constant = true; } -void MixBaseOperation::initExecution() +void MixBaseOperation::init_execution() { - this->m_inputValueOperation = this->getInputSocketReader(0); - this->m_inputColor1Operation = this->getInputSocketReader(1); - this->m_inputColor2Operation = this->getInputSocketReader(2); + input_value_operation_ = this->get_input_socket_reader(0); + input_color1_operation_ = this->get_input_socket_reader(1); + input_color2_operation_ = this->get_input_socket_reader(2); } -void MixBaseOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MixBaseOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = valuem * (inputColor1[0]) + value * (inputColor2[0]); - output[1] = valuem * (inputColor1[1]) + value * (inputColor2[1]); - output[2] = valuem * (inputColor1[2]) + value * (inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = valuem * (input_color1[0]) + value * (input_color2[0]); + output[1] = valuem * (input_color1[1]) + value * (input_color2[1]); + output[2] = valuem * (input_color1[2]) + value * (input_color2[2]); + output[3] = input_color1[3]; } -void MixBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MixBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { NodeOperationInput *socket; - unsigned int tempPreferredResolution[2] = {0, 0}; - unsigned int tempResolution[2]; + rcti temp_area; - socket = this->getInputSocket(1); - socket->determineResolution(tempResolution, tempPreferredResolution); - if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) { - this->setResolutionInputSocketIndex(1); + socket = this->get_input_socket(1); + bool determined = socket->determine_canvas(COM_AREA_NONE, temp_area); + if (determined) { + this->set_canvas_input_index(1); } else { - socket = this->getInputSocket(2); - socket->determineResolution(tempResolution, tempPreferredResolution); - if ((tempResolution[0] != 0) && (tempResolution[1] != 0)) { - this->setResolutionInputSocketIndex(2); + socket = this->get_input_socket(2); + determined = socket->determine_canvas(COM_AREA_NONE, temp_area); + if (determined) { + this->set_canvas_input_index(2); } else { - this->setResolutionInputSocketIndex(0); + this->set_canvas_input_index(0); } } - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); } -void MixBaseOperation::deinitExecution() +void MixBaseOperation::deinit_execution() { - this->m_inputValueOperation = nullptr; - this->m_inputColor1Operation = nullptr; - this->m_inputColor2Operation = nullptr; + input_value_operation_ = nullptr; + input_color1_operation_ = nullptr; + input_color2_operation_ = nullptr; } void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -111,7 +110,7 @@ void MixBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, p.value_stride = input_value->elem_stride; p.color1_stride = input_color1->elem_stride; p.color2_stride = input_color2->elem_stride; - for (const int y : YRange(area)) { + for (int y = area.ymin; y < area.ymax; y++) { p.out = output->get_elem(area.xmin, y); p.row_end = p.out + width * output->elem_stride; p.value = input_value->get_elem(area.xmin, y); @@ -125,7 +124,7 @@ void MixBaseOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -139,33 +138,36 @@ void MixBaseOperation::update_memory_buffer_row(PixelCursor &p) /* ******** Mix Add Operation ******** */ -void MixAddOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MixAddOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - output[0] = inputColor1[0] + value * inputColor2[0]; - output[1] = inputColor1[1] + value * inputColor2[1]; - output[2] = inputColor1[2] + value * inputColor2[2]; - output[3] = inputColor1[3]; + output[0] = input_color1[0] + value * input_color2[0]; + output[1] = input_color1[1] + value * input_color2[1]; + output[2] = input_color1[2] + value * input_color2[2]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixAddOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } p.out[0] = p.color1[0] + value * p.color2[0]; @@ -173,45 +175,45 @@ void MixAddOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = p.color1[2] + value * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Blend Operation ******** */ -void MixBlendOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixBlendOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float value; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); - value = inputValue[0]; + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); + value = input_value[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = valuem * (inputColor1[0]) + value * (inputColor2[0]); - output[1] = valuem * (inputColor1[1]) + value * (inputColor2[1]); - output[2] = valuem * (inputColor1[2]) + value * (inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = valuem * (input_color1[0]) + value * (input_color2[0]); + output[1] = valuem * (input_color1[1]) + value * (input_color2[1]); + output[2] = valuem * (input_color1[2]) + value * (input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixBlendOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } float value_m = 1.0f - value; @@ -220,39 +222,39 @@ void MixBlendOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = value_m * p.color1[2] + value * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Burn Operation ******** */ -void MixColorBurnOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixColorBurnOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float tmp; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - tmp = valuem + value * inputColor2[0]; + tmp = valuem + value * input_color2[0]; if (tmp <= 0.0f) { output[0] = 0.0f; } else { - tmp = 1.0f - (1.0f - inputColor1[0]) / tmp; + tmp = 1.0f - (1.0f - input_color1[0]) / tmp; if (tmp < 0.0f) { output[0] = 0.0f; } @@ -264,12 +266,12 @@ void MixColorBurnOperation::executePixelSampled(float output[4], } } - tmp = valuem + value * inputColor2[1]; + tmp = valuem + value * input_color2[1]; if (tmp <= 0.0f) { output[1] = 0.0f; } else { - tmp = 1.0f - (1.0f - inputColor1[1]) / tmp; + tmp = 1.0f - (1.0f - input_color1[1]) / tmp; if (tmp < 0.0f) { output[1] = 0.0f; } @@ -281,12 +283,12 @@ void MixColorBurnOperation::executePixelSampled(float output[4], } } - tmp = valuem + value * inputColor2[2]; + tmp = valuem + value * input_color2[2]; if (tmp <= 0.0f) { output[2] = 0.0f; } else { - tmp = 1.0f - (1.0f - inputColor1[2]) / tmp; + tmp = 1.0f - (1.0f - input_color1[2]) / tmp; if (tmp < 0.0f) { output[2] = 0.0f; } @@ -298,16 +300,16 @@ void MixColorBurnOperation::executePixelSampled(float output[4], } } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixColorBurnOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -340,56 +342,56 @@ void MixColorBurnOperation::update_memory_buffer_row(PixelCursor &p) } p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Color Operation ******** */ -void MixColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float colH, colS, colV; - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); if (colS != 0.0f) { float rH, rS, rV; float tmpr, tmpg, tmpb; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); hsv_to_rgb(colH, colS, rV, &tmpr, &tmpg, &tmpb); - output[0] = (valuem * inputColor1[0]) + (value * tmpr); - output[1] = (valuem * inputColor1[1]) + (value * tmpg); - output[2] = (valuem * inputColor1[2]) + (value * tmpb); + output[0] = (valuem * input_color1[0]) + (value * tmpr); + output[1] = (valuem * input_color1[1]) + (value * tmpg); + output[2] = (valuem * input_color1[2]) + (value * tmpb); } else { - copy_v3_v3(output, inputColor1); + copy_v3_v3(output, input_color1); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixColorOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -410,44 +412,44 @@ void MixColorOperation::update_memory_buffer_row(PixelCursor &p) } p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Darken Operation ******** */ -void MixDarkenOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDarkenOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = min_ff(inputColor1[0], inputColor2[0]) * value + inputColor1[0] * valuem; - output[1] = min_ff(inputColor1[1], inputColor2[1]) * value + inputColor1[1] * valuem; - output[2] = min_ff(inputColor1[2], inputColor2[2]) * value + inputColor1[2] * valuem; - output[3] = inputColor1[3]; + output[0] = min_ff(input_color1[0], input_color2[0]) * value + input_color1[0] * valuem; + output[1] = min_ff(input_color1[1], input_color2[1]) * value + input_color1[1] * valuem; + output[2] = min_ff(input_color1[2], input_color2[2]) * value + input_color1[2] * valuem; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDarkenOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } float value_m = 1.0f - value; @@ -456,44 +458,44 @@ void MixDarkenOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = min_ff(p.color1[2], p.color2[2]) * value + p.color1[2] * value_m; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Difference Operation ******** */ -void MixDifferenceOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDifferenceOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = valuem * inputColor1[0] + value * fabsf(inputColor1[0] - inputColor2[0]); - output[1] = valuem * inputColor1[1] + value * fabsf(inputColor1[1] - inputColor2[1]); - output[2] = valuem * inputColor1[2] + value * fabsf(inputColor1[2] - inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = valuem * input_color1[0] + value * fabsf(input_color1[0] - input_color2[0]); + output[1] = valuem * input_color1[1] + value * fabsf(input_color1[1] - input_color2[1]); + output[2] = valuem * input_color1[2] + value * fabsf(input_color1[2] - input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -502,61 +504,61 @@ void MixDifferenceOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = value_m * p.color1[2] + value * fabsf(p.color1[2] - p.color2[2]); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Difference Operation ******** */ -void MixDivideOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDivideOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - if (inputColor2[0] != 0.0f) { - output[0] = valuem * (inputColor1[0]) + value * (inputColor1[0]) / inputColor2[0]; + if (input_color2[0] != 0.0f) { + output[0] = valuem * (input_color1[0]) + value * (input_color1[0]) / input_color2[0]; } else { output[0] = 0.0f; } - if (inputColor2[1] != 0.0f) { - output[1] = valuem * (inputColor1[1]) + value * (inputColor1[1]) / inputColor2[1]; + if (input_color2[1] != 0.0f) { + output[1] = valuem * (input_color1[1]) + value * (input_color1[1]) / input_color2[1]; } else { output[1] = 0.0f; } - if (inputColor2[2] != 0.0f) { - output[2] = valuem * (inputColor1[2]) + value * (inputColor1[2]) / inputColor2[2]; + if (input_color2[2] != 0.0f) { + output[2] = valuem * (input_color1[2]) + value * (input_color1[2]) / input_color2[2]; } else { output[2] = 0.0f; } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDivideOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -582,39 +584,39 @@ void MixDivideOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Dodge Operation ******** */ -void MixDodgeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixDodgeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float tmp; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - if (inputColor1[0] != 0.0f) { - tmp = 1.0f - value * inputColor2[0]; + if (input_color1[0] != 0.0f) { + tmp = 1.0f - value * input_color2[0]; if (tmp <= 0.0f) { output[0] = 1.0f; } else { - tmp = inputColor1[0] / tmp; + tmp = input_color1[0] / tmp; if (tmp > 1.0f) { output[0] = 1.0f; } @@ -627,13 +629,13 @@ void MixDodgeOperation::executePixelSampled(float output[4], output[0] = 0.0f; } - if (inputColor1[1] != 0.0f) { - tmp = 1.0f - value * inputColor2[1]; + if (input_color1[1] != 0.0f) { + tmp = 1.0f - value * input_color2[1]; if (tmp <= 0.0f) { output[1] = 1.0f; } else { - tmp = inputColor1[1] / tmp; + tmp = input_color1[1] / tmp; if (tmp > 1.0f) { output[1] = 1.0f; } @@ -646,13 +648,13 @@ void MixDodgeOperation::executePixelSampled(float output[4], output[1] = 0.0f; } - if (inputColor1[2] != 0.0f) { - tmp = 1.0f - value * inputColor2[2]; + if (input_color1[2] != 0.0f) { + tmp = 1.0f - value * input_color2[2]; if (tmp <= 0.0f) { output[2] = 1.0f; } else { - tmp = inputColor1[2] / tmp; + tmp = input_color1[2] / tmp; if (tmp > 1.0f) { output[2] = 1.0f; } @@ -665,16 +667,16 @@ void MixDodgeOperation::executePixelSampled(float output[4], output[2] = 0.0f; } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixDodgeOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } @@ -723,27 +725,27 @@ void MixDodgeOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Glare Operation ******** */ -void MixGlareOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixGlareOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; float value, input_weight, glare_weight; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); - value = inputValue[0]; + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); + value = input_value[0]; /* Linear interpolation between 3 cases: * value=-1:output=input value=0:output=input+glare value=1:output=glare */ @@ -755,12 +757,12 @@ void MixGlareOperation::executePixelSampled(float output[4], input_weight = 1.0f - value; glare_weight = 1.0f; } - output[0] = input_weight * MAX2(inputColor1[0], 0.0f) + glare_weight * inputColor2[0]; - output[1] = input_weight * MAX2(inputColor1[1], 0.0f) + glare_weight * inputColor2[1]; - output[2] = input_weight * MAX2(inputColor1[2], 0.0f) + glare_weight * inputColor2[2]; - output[3] = inputColor1[3]; + output[0] = input_weight * MAX2(input_color1[0], 0.0f) + glare_weight * input_color2[0]; + output[1] = input_weight * MAX2(input_color1[1], 0.0f) + glare_weight * input_color2[1]; + output[2] = input_weight * MAX2(input_color1[2], 0.0f) + glare_weight * input_color2[2]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixGlareOperation::update_memory_buffer_row(PixelCursor &p) @@ -785,53 +787,56 @@ void MixGlareOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = input_weight * MAX2(p.color1[2], 0.0f) + glare_weight * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Hue Operation ******** */ -void MixHueOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void MixHueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float colH, colS, colV; - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); if (colS != 0.0f) { float rH, rS, rV; float tmpr, tmpg, tmpb; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); hsv_to_rgb(colH, rS, rV, &tmpr, &tmpg, &tmpb); - output[0] = valuem * (inputColor1[0]) + value * tmpr; - output[1] = valuem * (inputColor1[1]) + value * tmpg; - output[2] = valuem * (inputColor1[2]) + value * tmpb; + output[0] = valuem * (input_color1[0]) + value * tmpr; + output[1] = valuem * (input_color1[1]) + value * tmpg; + output[2] = valuem * (input_color1[2]) + value * tmpb; } else { - copy_v3_v3(output, inputColor1); + copy_v3_v3(output, input_color1); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixHueOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -852,62 +857,62 @@ void MixHueOperation::update_memory_buffer_row(PixelCursor &p) } p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Lighten Operation ******** */ -void MixLightenOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixLightenOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float tmp; - tmp = value * inputColor2[0]; - if (tmp > inputColor1[0]) { + tmp = value * input_color2[0]; + if (tmp > input_color1[0]) { output[0] = tmp; } else { - output[0] = inputColor1[0]; + output[0] = input_color1[0]; } - tmp = value * inputColor2[1]; - if (tmp > inputColor1[1]) { + tmp = value * input_color2[1]; + if (tmp > input_color1[1]) { output[1] = tmp; } else { - output[1] = inputColor1[1]; + output[1] = input_color1[1]; } - tmp = value * inputColor2[2]; - if (tmp > inputColor1[2]) { + tmp = value * input_color2[2]; + if (tmp > input_color1[2]) { output[2] = tmp; } else { - output[2] = inputColor1[2]; + output[2] = input_color1[2]; } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixLightenOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } @@ -922,59 +927,59 @@ void MixLightenOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Linear Light Operation ******** */ -void MixLinearLightOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixLinearLightOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - if (inputColor2[0] > 0.5f) { - output[0] = inputColor1[0] + value * (2.0f * (inputColor2[0] - 0.5f)); + if (input_color2[0] > 0.5f) { + output[0] = input_color1[0] + value * (2.0f * (input_color2[0] - 0.5f)); } else { - output[0] = inputColor1[0] + value * (2.0f * (inputColor2[0]) - 1.0f); + output[0] = input_color1[0] + value * (2.0f * (input_color2[0]) - 1.0f); } - if (inputColor2[1] > 0.5f) { - output[1] = inputColor1[1] + value * (2.0f * (inputColor2[1] - 0.5f)); + if (input_color2[1] > 0.5f) { + output[1] = input_color1[1] + value * (2.0f * (input_color2[1] - 0.5f)); } else { - output[1] = inputColor1[1] + value * (2.0f * (inputColor2[1]) - 1.0f); + output[1] = input_color1[1] + value * (2.0f * (input_color2[1]) - 1.0f); } - if (inputColor2[2] > 0.5f) { - output[2] = inputColor1[2] + value * (2.0f * (inputColor2[2] - 0.5f)); + if (input_color2[2] > 0.5f) { + output[2] = input_color1[2] + value * (2.0f * (input_color2[2] - 0.5f)); } else { - output[2] = inputColor1[2] + value * (2.0f * (inputColor2[2]) - 1.0f); + output[2] = input_color1[2] + value * (2.0f * (input_color2[2]) - 1.0f); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixLinearLightOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } if (p.color2[0] > 0.5f) { @@ -998,44 +1003,44 @@ void MixLinearLightOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Multiply Operation ******** */ -void MixMultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixMultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = inputColor1[0] * (valuem + value * inputColor2[0]); - output[1] = inputColor1[1] * (valuem + value * inputColor2[1]); - output[2] = inputColor1[2] * (valuem + value * inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = input_color1[0] * (valuem + value * input_color2[0]); + output[1] = input_color1[1] * (valuem + value * input_color2[1]); + output[2] = input_color1[2] * (valuem + value * input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixMultiplyOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1045,61 +1050,64 @@ void MixMultiplyOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Overlay Operation ******** */ -void MixOverlayOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixOverlayOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - if (inputColor1[0] < 0.5f) { - output[0] = inputColor1[0] * (valuem + 2.0f * value * inputColor2[0]); + if (input_color1[0] < 0.5f) { + output[0] = input_color1[0] * (valuem + 2.0f * value * input_color2[0]); } else { - output[0] = 1.0f - (valuem + 2.0f * value * (1.0f - inputColor2[0])) * (1.0f - inputColor1[0]); + output[0] = 1.0f - + (valuem + 2.0f * value * (1.0f - input_color2[0])) * (1.0f - input_color1[0]); } - if (inputColor1[1] < 0.5f) { - output[1] = inputColor1[1] * (valuem + 2.0f * value * inputColor2[1]); + if (input_color1[1] < 0.5f) { + output[1] = input_color1[1] * (valuem + 2.0f * value * input_color2[1]); } else { - output[1] = 1.0f - (valuem + 2.0f * value * (1.0f - inputColor2[1])) * (1.0f - inputColor1[1]); + output[1] = 1.0f - + (valuem + 2.0f * value * (1.0f - input_color2[1])) * (1.0f - input_color1[1]); } - if (inputColor1[2] < 0.5f) { - output[2] = inputColor1[2] * (valuem + 2.0f * value * inputColor2[2]); + if (input_color1[2] < 0.5f) { + output[2] = input_color1[2] * (valuem + 2.0f * value * input_color2[2]); } else { - output[2] = 1.0f - (valuem + 2.0f * value * (1.0f - inputColor2[2])) * (1.0f - inputColor1[2]); + output[2] = 1.0f - + (valuem + 2.0f * value * (1.0f - input_color2[2])) * (1.0f - input_color1[2]); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixOverlayOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1124,53 +1132,53 @@ void MixOverlayOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Saturation Operation ******** */ -void MixSaturationOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixSaturationOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float rH, rS, rV; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); if (rS != 0.0f) { float colH, colS, colV; - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); hsv_to_rgb(rH, (valuem * rS + value * colS), rV, &output[0], &output[1], &output[2]); } else { - copy_v3_v3(output, inputColor1); + copy_v3_v3(output, input_color1); } - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixSaturationOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1188,45 +1196,45 @@ void MixSaturationOperation::update_memory_buffer_row(PixelCursor &p) p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Screen Operation ******** */ -void MixScreenOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixScreenOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; - output[0] = 1.0f - (valuem + value * (1.0f - inputColor2[0])) * (1.0f - inputColor1[0]); - output[1] = 1.0f - (valuem + value * (1.0f - inputColor2[1])) * (1.0f - inputColor1[1]); - output[2] = 1.0f - (valuem + value * (1.0f - inputColor2[2])) * (1.0f - inputColor1[2]); - output[3] = inputColor1[3]; + output[0] = 1.0f - (valuem + value * (1.0f - input_color2[0])) * (1.0f - input_color1[0]); + output[1] = 1.0f - (valuem + value * (1.0f - input_color2[1])) * (1.0f - input_color1[1]); + output[2] = 1.0f - (valuem + value * (1.0f - input_color2[2])) * (1.0f - input_color1[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixScreenOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1236,57 +1244,57 @@ void MixScreenOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = 1.0f - (value_m + value * (1.0f - p.color2[2])) * (1.0f - p.color1[2]); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Soft Light Operation ******** */ -void MixSoftLightOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixSoftLightOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float scr, scg, scb; /* first calculate non-fac based Screen mix */ - scr = 1.0f - (1.0f - inputColor2[0]) * (1.0f - inputColor1[0]); - scg = 1.0f - (1.0f - inputColor2[1]) * (1.0f - inputColor1[1]); - scb = 1.0f - (1.0f - inputColor2[2]) * (1.0f - inputColor1[2]); + scr = 1.0f - (1.0f - input_color2[0]) * (1.0f - input_color1[0]); + scg = 1.0f - (1.0f - input_color2[1]) * (1.0f - input_color1[1]); + scb = 1.0f - (1.0f - input_color2[2]) * (1.0f - input_color1[2]); - output[0] = valuem * (inputColor1[0]) + - value * (((1.0f - inputColor1[0]) * inputColor2[0] * (inputColor1[0])) + - (inputColor1[0] * scr)); - output[1] = valuem * (inputColor1[1]) + - value * (((1.0f - inputColor1[1]) * inputColor2[1] * (inputColor1[1])) + - (inputColor1[1] * scg)); - output[2] = valuem * (inputColor1[2]) + - value * (((1.0f - inputColor1[2]) * inputColor2[2] * (inputColor1[2])) + - (inputColor1[2] * scb)); - output[3] = inputColor1[3]; + output[0] = valuem * (input_color1[0]) + + value * (((1.0f - input_color1[0]) * input_color2[0] * (input_color1[0])) + + (input_color1[0] * scr)); + output[1] = valuem * (input_color1[1]) + + value * (((1.0f - input_color1[1]) * input_color2[1] * (input_color1[1])) + + (input_color1[1] * scg)); + output[2] = valuem * (input_color1[2]) + + value * (((1.0f - input_color1[2]) * input_color2[2] * (input_color1[2])) + + (input_color1[2] * scb)); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixSoftLightOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } const float value_m = 1.0f - value; @@ -1305,43 +1313,43 @@ void MixSoftLightOperation::update_memory_buffer_row(PixelCursor &p) value * ((1.0f - p.color1[2]) * p.color2[2] * p.color1[2] + p.color1[2] * scb); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Subtract Operation ******** */ -void MixSubtractOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixSubtractOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } - output[0] = inputColor1[0] - value * (inputColor2[0]); - output[1] = inputColor1[1] - value * (inputColor2[1]); - output[2] = inputColor1[2] - value * (inputColor2[2]); - output[3] = inputColor1[3]; + output[0] = input_color1[0] - value * (input_color2[0]); + output[1] = input_color1[1] - value * (input_color2[1]); + output[2] = input_color1[2] - value * (input_color2[2]); + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixSubtractOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } p.out[0] = p.color1[0] - value * p.color2[0]; @@ -1349,47 +1357,47 @@ void MixSubtractOperation::update_memory_buffer_row(PixelCursor &p) p.out[2] = p.color1[2] - value * p.color2[2]; p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } /* ******** Mix Value Operation ******** */ -void MixValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MixValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputColor1[4]; - float inputColor2[4]; - float inputValue[4]; + float input_color1[4]; + float input_color2[4]; + float input_value[4]; - this->m_inputValueOperation->readSampled(inputValue, x, y, sampler); - this->m_inputColor1Operation->readSampled(inputColor1, x, y, sampler); - this->m_inputColor2Operation->readSampled(inputColor2, x, y, sampler); + input_value_operation_->read_sampled(input_value, x, y, sampler); + input_color1_operation_->read_sampled(input_color1, x, y, sampler); + input_color2_operation_->read_sampled(input_color2, x, y, sampler); - float value = inputValue[0]; - if (this->useValueAlphaMultiply()) { - value *= inputColor2[3]; + float value = input_value[0]; + if (this->use_value_alpha_multiply()) { + value *= input_color2[3]; } float valuem = 1.0f - value; float rH, rS, rV; float colH, colS, colV; - rgb_to_hsv(inputColor1[0], inputColor1[1], inputColor1[2], &rH, &rS, &rV); - rgb_to_hsv(inputColor2[0], inputColor2[1], inputColor2[2], &colH, &colS, &colV); + rgb_to_hsv(input_color1[0], input_color1[1], input_color1[2], &rH, &rS, &rV); + rgb_to_hsv(input_color2[0], input_color2[1], input_color2[2], &colH, &colS, &colV); hsv_to_rgb(rH, rS, (valuem * rV + value * colV), &output[0], &output[1], &output[2]); - output[3] = inputColor1[3]; + output[3] = input_color1[3]; - clampIfNeeded(output); + clamp_if_needed(output); } void MixValueOperation::update_memory_buffer_row(PixelCursor &p) { while (p.out < p.row_end) { float value = p.value[0]; - if (this->useValueAlphaMultiply()) { + if (this->use_value_alpha_multiply()) { value *= p.color2[3]; } float value_m = 1.0f - value; @@ -1401,7 +1409,7 @@ void MixValueOperation::update_memory_buffer_row(PixelCursor &p) hsv_to_rgb(rH, rS, (value_m * rV + value * colV), &p.out[0], &p.out[1], &p.out[2]); p.out[3] = p.color1[3]; - clampIfNeeded(p.out); + clamp_if_needed(p.out); p.next(); } } diff --git a/source/blender/compositor/operations/COM_MixOperation.h b/source/blender/compositor/operations/COM_MixOperation.h index 7ef9d78d58f..bf5639f43a3 100644 --- a/source/blender/compositor/operations/COM_MixOperation.h +++ b/source/blender/compositor/operations/COM_MixOperation.h @@ -51,17 +51,17 @@ class MixBaseOperation : public MultiThreadedOperation { }; /** - * Prefetched reference to the inputProgram + * Prefetched reference to the input_program */ - SocketReader *m_inputValueOperation; - SocketReader *m_inputColor1Operation; - SocketReader *m_inputColor2Operation; - bool m_valueAlphaMultiply; - bool m_useClamp; + SocketReader *input_value_operation_; + SocketReader *input_color1_operation_; + SocketReader *input_color2_operation_; + bool value_alpha_multiply_; + bool use_clamp_; - inline void clampIfNeeded(float color[4]) + inline void clamp_if_needed(float color[4]) { - if (m_useClamp) { + if (use_clamp_) { clamp_v4(color, 0.0f, 1.0f); } } @@ -75,32 +75,31 @@ class MixBaseOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setUseValueAlphaMultiply(const bool value) + void set_use_value_alpha_multiply(const bool value) { - this->m_valueAlphaMultiply = value; + value_alpha_multiply_ = value; } - inline bool useValueAlphaMultiply() + inline bool use_value_alpha_multiply() { - return this->m_valueAlphaMultiply; + return value_alpha_multiply_; } - void setUseClamp(bool value) + void set_use_clamp(bool value) { - this->m_useClamp = value; + use_clamp_ = value; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -113,7 +112,7 @@ class MixBaseOperation : public MultiThreadedOperation { class MixAddOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -121,7 +120,7 @@ class MixAddOperation : public MixBaseOperation { class MixBlendOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -129,7 +128,7 @@ class MixBlendOperation : public MixBaseOperation { class MixColorBurnOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -137,7 +136,7 @@ class MixColorBurnOperation : public MixBaseOperation { class MixColorOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -145,7 +144,7 @@ class MixColorOperation : public MixBaseOperation { class MixDarkenOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -153,7 +152,7 @@ class MixDarkenOperation : public MixBaseOperation { class MixDifferenceOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -161,7 +160,7 @@ class MixDifferenceOperation : public MixBaseOperation { class MixDivideOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -169,7 +168,7 @@ class MixDivideOperation : public MixBaseOperation { class MixDodgeOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -177,7 +176,7 @@ class MixDodgeOperation : public MixBaseOperation { class MixGlareOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -185,7 +184,7 @@ class MixGlareOperation : public MixBaseOperation { class MixHueOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -193,7 +192,7 @@ class MixHueOperation : public MixBaseOperation { class MixLightenOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -201,7 +200,7 @@ class MixLightenOperation : public MixBaseOperation { class MixLinearLightOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -209,7 +208,7 @@ class MixLinearLightOperation : public MixBaseOperation { class MixMultiplyOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -217,7 +216,7 @@ class MixMultiplyOperation : public MixBaseOperation { class MixOverlayOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -225,7 +224,7 @@ class MixOverlayOperation : public MixBaseOperation { class MixSaturationOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -233,7 +232,7 @@ class MixSaturationOperation : public MixBaseOperation { class MixScreenOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -241,7 +240,7 @@ class MixScreenOperation : public MixBaseOperation { class MixSoftLightOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -249,7 +248,7 @@ class MixSoftLightOperation : public MixBaseOperation { class MixSubtractOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; @@ -257,7 +256,7 @@ class MixSubtractOperation : public MixBaseOperation { class MixValueOperation : public MixBaseOperation { public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; protected: void update_memory_buffer_row(PixelCursor &p) override; diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc index e36e93984fb..1765012d873 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.cc @@ -25,15 +25,16 @@ namespace blender::compositor { MovieClipAttributeOperation::MovieClipAttributeOperation() { - this->addOutputSocket(DataType::Value); - this->m_framenumber = 0; - this->m_attribute = MCA_X; - this->m_invert = false; - needs_resolution_to_get_constant_ = true; + this->add_output_socket(DataType::Value); + framenumber_ = 0; + attribute_ = MCA_X; + invert_ = false; + needs_canvas_to_get_constant_ = true; is_value_calculated_ = false; + stabilization_resolution_socket_ = nullptr; } -void MovieClipAttributeOperation::initExecution() +void MovieClipAttributeOperation::init_execution() { if (!is_value_calculated_) { calc_value(); @@ -42,9 +43,9 @@ void MovieClipAttributeOperation::initExecution() void MovieClipAttributeOperation::calc_value() { - BLI_assert(this->get_flags().is_resolution_set); + BLI_assert(this->get_flags().is_canvas_set); is_value_calculated_ = true; - if (this->m_clip == nullptr) { + if (clip_ == nullptr) { return; } float loc[2], scale, angle; @@ -52,46 +53,53 @@ void MovieClipAttributeOperation::calc_value() loc[1] = 0.0f; scale = 1.0f; angle = 0.0f; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_clip, this->m_framenumber); - BKE_tracking_stabilization_data_get( - this->m_clip, clip_framenr, getWidth(), getHeight(), loc, &scale, &angle); - switch (this->m_attribute) { + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(clip_, framenumber_); + NodeOperation &stabilization_operation = + stabilization_resolution_socket_ ? + stabilization_resolution_socket_->get_link()->get_operation() : + *this; + BKE_tracking_stabilization_data_get(clip_, + clip_framenr, + stabilization_operation.get_width(), + stabilization_operation.get_height(), + loc, + &scale, + &angle); + switch (attribute_) { case MCA_SCALE: - this->m_value = scale; + value_ = scale; break; case MCA_ANGLE: - this->m_value = angle; + value_ = angle; break; case MCA_X: - this->m_value = loc[0]; + value_ = loc[0]; break; case MCA_Y: - this->m_value = loc[1]; + value_ = loc[1]; break; } - if (this->m_invert) { - if (this->m_attribute != MCA_SCALE) { - this->m_value = -this->m_value; + if (invert_) { + if (attribute_ != MCA_SCALE) { + value_ = -value_; } else { - this->m_value = 1.0f / this->m_value; + value_ = 1.0f / value_; } } } -void MovieClipAttributeOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void MovieClipAttributeOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { - output[0] = this->m_value; + output[0] = value_; } -void MovieClipAttributeOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void MovieClipAttributeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } const float *MovieClipAttributeOperation::get_constant_elem() @@ -99,7 +107,7 @@ const float *MovieClipAttributeOperation::get_constant_elem() if (!is_value_calculated_) { calc_value(); } - return &m_value; + return &value_; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h index 28c39d4dad3..9f333189811 100644 --- a/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipAttributeOperation.h @@ -36,12 +36,13 @@ typedef enum MovieClipAttribute { */ class MovieClipAttributeOperation : public ConstantOperation { private: - MovieClip *m_clip; - float m_value; - int m_framenumber; - bool m_invert; - MovieClipAttribute m_attribute; + MovieClip *clip_; + float value_; + int framenumber_; + bool invert_; + MovieClipAttribute attribute_; bool is_value_calculated_; + NodeOperationInput *stabilization_resolution_socket_; public: /** @@ -49,32 +50,39 @@ class MovieClipAttributeOperation : public ConstantOperation { */ MovieClipAttributeOperation(); - void initExecution() override; + void init_execution() override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; const float *get_constant_elem() override; - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - this->m_clip = clip; + clip_ = clip; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { - this->m_framenumber = framenumber; + framenumber_ = framenumber; } - void setAttribute(MovieClipAttribute attribute) + void set_attribute(MovieClipAttribute attribute) { - this->m_attribute = attribute; + attribute_ = attribute; } - void setInvert(bool invert) + void set_invert(bool invert) { - this->m_invert = invert; + invert_ = invert; + } + + /** + * Set an operation socket which input will be used to get the resolution for stabilization. + */ + void set_socket_input_resolution_for_stabilization(NodeOperationInput *input_socket) + { + stabilization_resolution_socket_ = input_socket; } private: diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.cc b/source/blender/compositor/operations/COM_MovieClipOperation.cc index e520b928edf..a73b408e3ff 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.cc +++ b/source/blender/compositor/operations/COM_MovieClipOperation.cc @@ -18,9 +18,6 @@ #include "COM_MovieClipOperation.h" -#include "BLI_listbase.h" -#include "BLI_math.h" - #include "BKE_image.h" #include "BKE_movieclip.h" @@ -30,30 +27,30 @@ namespace blender::compositor { MovieClipBaseOperation::MovieClipBaseOperation() { - this->m_movieClip = nullptr; - this->m_movieClipBuffer = nullptr; - this->m_movieClipUser = nullptr; - this->m_movieClipwidth = 0; - this->m_movieClipheight = 0; - this->m_framenumber = 0; + movie_clip_ = nullptr; + movie_clip_buffer_ = nullptr; + movie_clip_user_ = nullptr; + movie_clipwidth_ = 0; + movie_clipheight_ = 0; + framenumber_ = 0; } -void MovieClipBaseOperation::initExecution() +void MovieClipBaseOperation::init_execution() { - if (this->m_movieClip) { - BKE_movieclip_user_set_frame(this->m_movieClipUser, this->m_framenumber); + if (movie_clip_) { + BKE_movieclip_user_set_frame(movie_clip_user_, framenumber_); ImBuf *ibuf; - if (this->m_cacheFrame) { - ibuf = BKE_movieclip_get_ibuf(this->m_movieClip, this->m_movieClipUser); + if (cache_frame_) { + ibuf = BKE_movieclip_get_ibuf(movie_clip_, movie_clip_user_); } else { ibuf = BKE_movieclip_get_ibuf_flag( - this->m_movieClip, this->m_movieClipUser, this->m_movieClip->flag, MOVIECLIP_CACHE_SKIP); + movie_clip_, movie_clip_user_, movie_clip_->flag, MOVIECLIP_CACHE_SKIP); } if (ibuf) { - this->m_movieClipBuffer = ibuf; + movie_clip_buffer_ = ibuf; if (ibuf->rect_float == nullptr || ibuf->userflags & IB_RECT_INVALID) { IMB_float_from_rect(ibuf); ibuf->userflags &= ~IB_RECT_INVALID; @@ -62,37 +59,31 @@ void MovieClipBaseOperation::initExecution() } } -void MovieClipBaseOperation::deinitExecution() +void MovieClipBaseOperation::deinit_execution() { - if (this->m_movieClipBuffer) { - IMB_freeImBuf(this->m_movieClipBuffer); + if (movie_clip_buffer_) { + IMB_freeImBuf(movie_clip_buffer_); - this->m_movieClipBuffer = nullptr; + movie_clip_buffer_ = nullptr; } } -void MovieClipBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void MovieClipBaseOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - resolution[0] = 0; - resolution[1] = 0; - - if (this->m_movieClip) { + r_area = COM_AREA_NONE; + if (movie_clip_) { int width, height; - - BKE_movieclip_get_size(this->m_movieClip, this->m_movieClipUser, &width, &height); - - resolution[0] = width; - resolution[1] = height; + BKE_movieclip_get_size(movie_clip_, movie_clip_user_, &width, &height); + BLI_rcti_init(&r_area, 0, width, 0, height); } } -void MovieClipBaseOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MovieClipBaseOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - ImBuf *ibuf = this->m_movieClipBuffer; + ImBuf *ibuf = movie_clip_buffer_; if (ibuf == nullptr) { zero_v4(output); @@ -120,8 +111,8 @@ void MovieClipBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - if (m_movieClipBuffer) { - output->copy_from(m_movieClipBuffer, area); + if (movie_clip_buffer_) { + output->copy_from(movie_clip_buffer_, area); } else { output->fill(area, COM_COLOR_TRANSPARENT); @@ -130,21 +121,21 @@ void MovieClipBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, MovieClipOperation::MovieClipOperation() : MovieClipBaseOperation() { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } MovieClipAlphaOperation::MovieClipAlphaOperation() : MovieClipBaseOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } -void MovieClipAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MovieClipAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float result[4]; - MovieClipBaseOperation::executePixelSampled(result, x, y, sampler); + MovieClipBaseOperation::execute_pixel_sampled(result, x, y, sampler); output[0] = result[3]; } @@ -152,8 +143,8 @@ void MovieClipAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - if (m_movieClipBuffer) { - output->copy_from(m_movieClipBuffer, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); + if (movie_clip_buffer_) { + output->copy_from(movie_clip_buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } else { output->fill(area, COM_VALUE_ZERO); diff --git a/source/blender/compositor/operations/COM_MovieClipOperation.h b/source/blender/compositor/operations/COM_MovieClipOperation.h index 0a0c4c00f81..e576917df1b 100644 --- a/source/blender/compositor/operations/COM_MovieClipOperation.h +++ b/source/blender/compositor/operations/COM_MovieClipOperation.h @@ -30,43 +30,42 @@ namespace blender::compositor { */ class MovieClipBaseOperation : public MultiThreadedOperation { protected: - MovieClip *m_movieClip; - MovieClipUser *m_movieClipUser; - ImBuf *m_movieClipBuffer; - int m_movieClipheight; - int m_movieClipwidth; - int m_framenumber; - bool m_cacheFrame; + MovieClip *movie_clip_; + MovieClipUser *movie_clip_user_; + ImBuf *movie_clip_buffer_; + int movie_clipheight_; + int movie_clipwidth_; + int framenumber_; + bool cache_frame_; /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; public: MovieClipBaseOperation(); - void initExecution() override; - void deinitExecution() override; - void setMovieClip(MovieClip *image) + void init_execution() override; + void deinit_execution() override; + void set_movie_clip(MovieClip *image) { - this->m_movieClip = image; + movie_clip_ = image; } - void setMovieClipUser(MovieClipUser *imageuser) + void set_movie_clip_user(MovieClipUser *imageuser) { - this->m_movieClipUser = imageuser; + movie_clip_user_ = imageuser; } - void setCacheFrame(bool value) + void set_cache_frame(bool value) { - this->m_cacheFrame = value; + cache_frame_ = value; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { - this->m_framenumber = framenumber; + framenumber_ = framenumber; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -81,7 +80,7 @@ class MovieClipOperation : public MovieClipBaseOperation { class MovieClipAlphaOperation : public MovieClipBaseOperation { public: MovieClipAlphaOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc index d3424959061..d04a970bc03 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.cc @@ -19,113 +19,117 @@ #include "COM_MovieDistortionOperation.h" #include "BKE_movieclip.h" -#include "BKE_tracking.h" - -#include "BLI_linklist.h" namespace blender::compositor { MovieDistortionOperation::MovieDistortionOperation(bool distortion) { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->m_movieClip = nullptr; - this->m_apply = distortion; + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); + input_operation_ = nullptr; + movie_clip_ = nullptr; + apply_ = distortion; } -void MovieDistortionOperation::initExecution() +void MovieDistortionOperation::init_data() { - this->m_inputOperation = this->getInputSocketReader(0); - if (this->m_movieClip) { - MovieTracking *tracking = &this->m_movieClip->tracking; - MovieClipUser clipUser = {0}; + if (movie_clip_) { + MovieTracking *tracking = &movie_clip_->tracking; + MovieClipUser clip_user = {0}; int calibration_width, calibration_height; - BKE_movieclip_user_set_frame(&clipUser, this->m_framenumber); - BKE_movieclip_get_size(this->m_movieClip, &clipUser, &calibration_width, &calibration_height); + BKE_movieclip_user_set_frame(&clip_user, framenumber_); + BKE_movieclip_get_size(movie_clip_, &clip_user, &calibration_width, &calibration_height); float delta[2]; rcti full_frame; full_frame.xmin = full_frame.ymin = 0; - full_frame.xmax = this->m_width; - full_frame.ymax = this->m_height; + full_frame.xmax = this->get_width(); + full_frame.ymax = this->get_height(); BKE_tracking_max_distortion_delta_across_bound( - tracking, this->m_width, this->m_height, &full_frame, !this->m_apply, delta); + tracking, this->get_width(), this->get_height(), &full_frame, !apply_, delta); /* 5 is just in case we didn't hit real max of distortion in * BKE_tracking_max_undistortion_delta_across_bound */ - m_margin[0] = delta[0] + 5; - m_margin[1] = delta[1] + 5; + margin_[0] = delta[0] + 5; + margin_[1] = delta[1] + 5; - this->m_distortion = BKE_tracking_distortion_new( - tracking, calibration_width, calibration_height); - this->m_calibration_width = calibration_width; - this->m_calibration_height = calibration_height; - this->m_pixel_aspect = tracking->camera.pixel_aspect; + calibration_width_ = calibration_width; + calibration_height_ = calibration_height; + pixel_aspect_ = tracking->camera.pixel_aspect; } else { - m_margin[0] = m_margin[1] = 0; - this->m_distortion = nullptr; + margin_[0] = margin_[1] = 0; } } -void MovieDistortionOperation::deinitExecution() +void MovieDistortionOperation::init_execution() { - this->m_inputOperation = nullptr; - this->m_movieClip = nullptr; - if (this->m_distortion != nullptr) { - BKE_tracking_distortion_free(this->m_distortion); + input_operation_ = this->get_input_socket_reader(0); + if (movie_clip_) { + MovieTracking *tracking = &movie_clip_->tracking; + distortion_ = BKE_tracking_distortion_new(tracking, calibration_width_, calibration_height_); + } + else { + distortion_ = nullptr; } } -void MovieDistortionOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MovieDistortionOperation::deinit_execution() { - if (this->m_distortion != nullptr) { + input_operation_ = nullptr; + movie_clip_ = nullptr; + if (distortion_ != nullptr) { + BKE_tracking_distortion_free(distortion_); + } +} + +void MovieDistortionOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) +{ + if (distortion_ != nullptr) { /* float overscan = 0.0f; */ - const float pixel_aspect = this->m_pixel_aspect; - const float w = (float)this->m_width /* / (1 + overscan) */; - const float h = (float)this->m_height /* / (1 + overscan) */; - const float aspx = w / (float)this->m_calibration_width; - const float aspy = h / (float)this->m_calibration_height; + const float pixel_aspect = pixel_aspect_; + const float w = (float)this->get_width() /* / (1 + overscan) */; + const float h = (float)this->get_height() /* / (1 + overscan) */; + const float aspx = w / (float)calibration_width_; + const float aspy = h / (float)calibration_height_; float in[2]; float out[2]; in[0] = (x /* - 0.5 * overscan * w */) / aspx; in[1] = (y /* - 0.5 * overscan * h */) / aspy / pixel_aspect; - if (this->m_apply) { - BKE_tracking_distortion_undistort_v2(this->m_distortion, in, out); + if (apply_) { + BKE_tracking_distortion_undistort_v2(distortion_, in, out); } else { - BKE_tracking_distortion_distort_v2(this->m_distortion, in, out); + BKE_tracking_distortion_distort_v2(distortion_, in, out); } float u = out[0] * aspx /* + 0.5 * overscan * w */, v = (out[1] * aspy /* + 0.5 * overscan * h */) * pixel_aspect; - this->m_inputOperation->readSampled(output, u, v, PixelSampler::Bilinear); + input_operation_->read_sampled(output, u, v, PixelSampler::Bilinear); } else { - this->m_inputOperation->readSampled(output, x, y, PixelSampler::Bilinear); + input_operation_->read_sampled(output, x, y, PixelSampler::Bilinear); } } -bool MovieDistortionOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool MovieDistortionOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - newInput.xmin = input->xmin - m_margin[0]; - newInput.ymin = input->ymin - m_margin[1]; - newInput.xmax = input->xmax + m_margin[0]; - newInput.ymax = input->ymax + m_margin[1]; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + rcti new_input; + new_input.xmin = input->xmin - margin_[0]; + new_input.ymin = input->ymin - margin_[1]; + new_input.xmax = input->xmax + margin_[0]; + new_input.ymax = input->ymax + margin_[1]; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void MovieDistortionOperation::get_area_of_interest(const int input_idx, @@ -134,10 +138,10 @@ void MovieDistortionOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin - m_margin[0]; - r_input_area.ymin = output_area.ymin - m_margin[1]; - r_input_area.xmax = output_area.xmax + m_margin[0]; - r_input_area.ymax = output_area.ymax + m_margin[1]; + r_input_area.xmin = output_area.xmin - margin_[0]; + r_input_area.ymin = output_area.ymin - margin_[1]; + r_input_area.xmax = output_area.xmax + margin_[0]; + r_input_area.ymax = output_area.ymax + margin_[1]; } void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -145,28 +149,28 @@ void MovieDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output Span inputs) { const MemoryBuffer *input_img = inputs[0]; - if (this->m_distortion == nullptr) { + if (distortion_ == nullptr) { output->copy_from(input_img, area); return; } /* `float overscan = 0.0f;` */ - const float pixel_aspect = this->m_pixel_aspect; - const float w = (float)this->m_width /* `/ (1 + overscan)` */; - const float h = (float)this->m_height /* `/ (1 + overscan)` */; - const float aspx = w / (float)this->m_calibration_width; - const float aspy = h / (float)this->m_calibration_height; + const float pixel_aspect = pixel_aspect_; + const float w = (float)this->get_width() /* `/ (1 + overscan)` */; + const float h = (float)this->get_height() /* `/ (1 + overscan)` */; + const float aspx = w / (float)calibration_width_; + const float aspy = h / (float)calibration_height_; float xy[2]; float distorted_xy[2]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { xy[0] = (it.x /* `- 0.5 * overscan * w` */) / aspx; xy[1] = (it.y /* `- 0.5 * overscan * h` */) / aspy / pixel_aspect; - if (this->m_apply) { - BKE_tracking_distortion_undistort_v2(this->m_distortion, xy, distorted_xy); + if (apply_) { + BKE_tracking_distortion_undistort_v2(distortion_, xy, distorted_xy); } else { - BKE_tracking_distortion_distort_v2(this->m_distortion, xy, distorted_xy); + BKE_tracking_distortion_distort_v2(distortion_, xy, distorted_xy); } const float u = distorted_xy[0] * aspx /* `+ 0.5 * overscan * w` */; diff --git a/source/blender/compositor/operations/COM_MovieDistortionOperation.h b/source/blender/compositor/operations/COM_MovieDistortionOperation.h index 69c2f9c269c..cf430c74f30 100644 --- a/source/blender/compositor/operations/COM_MovieDistortionOperation.h +++ b/source/blender/compositor/operations/COM_MovieDistortionOperation.h @@ -28,36 +28,37 @@ namespace blender::compositor { class MovieDistortionOperation : public MultiThreadedOperation { private: - SocketReader *m_inputOperation; - MovieClip *m_movieClip; - int m_margin[2]; + SocketReader *input_operation_; + MovieClip *movie_clip_; + int margin_[2]; protected: - bool m_apply; - int m_framenumber; + bool apply_; + int framenumber_; - struct MovieDistortion *m_distortion; - int m_calibration_width, m_calibration_height; - float m_pixel_aspect; + struct MovieDistortion *distortion_; + int calibration_width_, calibration_height_; + float pixel_aspect_; public: MovieDistortionOperation(bool distortion); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_data() override; + void init_execution() override; + void deinit_execution() override; - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - this->m_movieClip = clip; + movie_clip_ = clip; } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { - this->m_framenumber = framenumber; + framenumber_ = framenumber; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc index 3a5de944a00..5549cd1e937 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cc +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cc @@ -19,7 +19,6 @@ #include "COM_MultilayerImageOperation.h" #include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" namespace blender::compositor { @@ -27,27 +26,27 @@ MultilayerBaseOperation::MultilayerBaseOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) { - this->m_passId = BLI_findindex(&render_layer->passes, render_pass); - this->m_view = view; - this->m_renderLayer = render_layer; - this->m_renderPass = render_pass; + pass_id_ = BLI_findindex(&render_layer->passes, render_pass); + view_ = view; + render_layer_ = render_layer; + render_pass_ = render_pass; } -ImBuf *MultilayerBaseOperation::getImBuf() +ImBuf *MultilayerBaseOperation::get_im_buf() { /* temporarily changes the view to get the right ImBuf */ - int view = this->m_imageUser->view; + int view = image_user_->view; - this->m_imageUser->view = this->m_view; - this->m_imageUser->pass = this->m_passId; + image_user_->view = view_; + image_user_->pass = pass_id_; - if (BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser)) { - ImBuf *ibuf = BaseImageOperation::getImBuf(); - this->m_imageUser->view = view; + if (BKE_image_multilayer_index(image_->rr, image_user_)) { + ImBuf *ibuf = BaseImageOperation::get_im_buf(); + image_user_->view = view; return ibuf; } - this->m_imageUser->view = view; + image_user_->view = view; return nullptr; } @@ -55,17 +54,17 @@ void MultilayerBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - output->copy_from(m_buffer, area); + output->copy_from(buffer_, area); } -std::unique_ptr MultilayerColorOperation::getMetaData() +std::unique_ptr MultilayerColorOperation::get_meta_data() { - BLI_assert(this->m_buffer); + BLI_assert(buffer_); MetaDataExtractCallbackData callback_data = {nullptr}; - RenderResult *render_result = this->m_image->rr; + RenderResult *render_result = image_->rr; if (render_result && render_result->stamp_data) { - RenderLayer *render_layer = this->m_renderLayer; - RenderPass *render_pass = this->m_renderPass; + RenderLayer *render_layer = render_layer_; + RenderPass *render_pass = render_pass_; std::string full_layer_name = std::string(render_layer->name, BLI_strnlen(render_layer->name, sizeof(render_layer->name))) + @@ -73,7 +72,7 @@ std::unique_ptr MultilayerColorOperation::getMetaData() std::string(render_pass->name, BLI_strnlen(render_pass->name, sizeof(render_pass->name))); blender::StringRef cryptomatte_layer_name = blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(full_layer_name); - callback_data.setCryptomatteKeys(cryptomatte_layer_name); + callback_data.set_cryptomatte_keys(cryptomatte_layer_name); BKE_stamp_info_callback(&callback_data, render_result->stamp_data, @@ -84,83 +83,83 @@ std::unique_ptr MultilayerColorOperation::getMetaData() return std::move(callback_data.meta_data); } -void MultilayerColorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void MultilayerColorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - if (this->m_imageFloatBuffer == nullptr) { + if (image_float_buffer_ == nullptr) { zero_v4(output); } else { - if (this->m_numberOfChannels == 4) { + if (number_of_channels_ == 4) { switch (sampler) { case PixelSampler::Nearest: - nearest_interpolation_color(this->m_buffer, nullptr, output, x, y); + nearest_interpolation_color(buffer_, nullptr, output, x, y); break; case PixelSampler::Bilinear: - bilinear_interpolation_color(this->m_buffer, nullptr, output, x, y); + bilinear_interpolation_color(buffer_, nullptr, output, x, y); break; case PixelSampler::Bicubic: - bicubic_interpolation_color(this->m_buffer, nullptr, output, x, y); + bicubic_interpolation_color(buffer_, nullptr, output, x, y); break; } } else { int yi = y; int xi = x; - if (xi < 0 || yi < 0 || (unsigned int)xi >= this->getWidth() || - (unsigned int)yi >= this->getHeight()) { + if (xi < 0 || yi < 0 || (unsigned int)xi >= this->get_width() || + (unsigned int)yi >= this->get_height()) { zero_v4(output); } else { - int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &this->m_imageFloatBuffer[offset]); + int offset = (yi * this->get_width() + xi) * 3; + copy_v3_v3(output, &image_float_buffer_[offset]); } } } } -void MultilayerValueOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MultilayerValueOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - if (this->m_imageFloatBuffer == nullptr) { + if (image_float_buffer_ == nullptr) { output[0] = 0.0f; } else { int yi = y; int xi = x; - if (xi < 0 || yi < 0 || (unsigned int)xi >= this->getWidth() || - (unsigned int)yi >= this->getHeight()) { + if (xi < 0 || yi < 0 || (unsigned int)xi >= this->get_width() || + (unsigned int)yi >= this->get_height()) { output[0] = 0.0f; } else { - float result = this->m_imageFloatBuffer[yi * this->getWidth() + xi]; + float result = image_float_buffer_[yi * this->get_width() + xi]; output[0] = result; } } } -void MultilayerVectorOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void MultilayerVectorOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - if (this->m_imageFloatBuffer == nullptr) { + if (image_float_buffer_ == nullptr) { output[0] = 0.0f; } else { int yi = y; int xi = x; - if (xi < 0 || yi < 0 || (unsigned int)xi >= this->getWidth() || - (unsigned int)yi >= this->getHeight()) { + if (xi < 0 || yi < 0 || (unsigned int)xi >= this->get_width() || + (unsigned int)yi >= this->get_height()) { output[0] = 0.0f; } else { - int offset = (yi * this->getWidth() + xi) * 3; - copy_v3_v3(output, &this->m_imageFloatBuffer[offset]); + int offset = (yi * this->get_width() + xi) * 3; + copy_v3_v3(output, &image_float_buffer_[offset]); } } } diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h index a682ca1941c..ea5b0fb46ea 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h @@ -24,13 +24,13 @@ namespace blender::compositor { class MultilayerBaseOperation : public BaseImageOperation { private: - int m_passId; - int m_view; + int pass_id_; + int view_; protected: - RenderLayer *m_renderLayer; - RenderPass *m_renderPass; - ImBuf *getImBuf() override; + RenderLayer *render_layer_; + RenderPass *render_pass_; + ImBuf *get_im_buf() override; public: /** @@ -48,10 +48,10 @@ class MultilayerColorOperation : public MultilayerBaseOperation { MultilayerColorOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) : MultilayerBaseOperation(render_layer, render_pass, view) { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - std::unique_ptr getMetaData() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + std::unique_ptr get_meta_data() override; }; class MultilayerValueOperation : public MultilayerBaseOperation { @@ -59,9 +59,9 @@ class MultilayerValueOperation : public MultilayerBaseOperation { MultilayerValueOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) : MultilayerBaseOperation(render_layer, render_pass, view) { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; class MultilayerVectorOperation : public MultilayerBaseOperation { @@ -69,9 +69,9 @@ class MultilayerVectorOperation : public MultilayerBaseOperation { MultilayerVectorOperation(RenderLayer *render_layer, RenderPass *render_pass, int view) : MultilayerBaseOperation(render_layer, render_pass, view) { - this->addOutputSocket(DataType::Vector); + this->add_output_socket(DataType::Vector); } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.cc b/source/blender/compositor/operations/COM_NormalizeOperation.cc index c3e72d2575f..0625cdb5ddb 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.cc +++ b/source/blender/compositor/operations/COM_NormalizeOperation.cc @@ -22,25 +22,25 @@ namespace blender::compositor { NormalizeOperation::NormalizeOperation() { - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Value); - this->m_imageReader = nullptr; - this->m_cachedInstance = nullptr; - this->flags.complex = true; - flags.can_be_constant = true; + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Value); + image_reader_ = nullptr; + cached_instance_ = nullptr; + flags_.complex = true; + flags_.can_be_constant = true; } -void NormalizeOperation::initExecution() +void NormalizeOperation::init_execution() { - this->m_imageReader = this->getInputSocketReader(0); - NodeOperation::initMutex(); + image_reader_ = this->get_input_socket_reader(0); + NodeOperation::init_mutex(); } -void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) +void NormalizeOperation::execute_pixel(float output[4], int x, int y, void *data) { /* using generic two floats struct to store `x: min`, `y: multiply` */ NodeTwoFloats *minmult = (NodeTwoFloats *)data; - this->m_imageReader->read(output, x, y, nullptr); + image_reader_->read(output, x, y, nullptr); output[0] = (output[0] - minmult->x) * minmult->y; @@ -53,30 +53,30 @@ void NormalizeOperation::executePixel(float output[4], int x, int y, void *data) } } -void NormalizeOperation::deinitExecution() +void NormalizeOperation::deinit_execution() { - this->m_imageReader = nullptr; - delete this->m_cachedInstance; - m_cachedInstance = nullptr; - NodeOperation::deinitMutex(); + image_reader_ = nullptr; + delete cached_instance_; + cached_instance_ = nullptr; + NodeOperation::deinit_mutex(); } -bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool NormalizeOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - rcti imageInput; - if (this->m_cachedInstance) { + rcti image_input; + if (cached_instance_) { return false; } - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; + NodeOperation *operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; @@ -86,16 +86,16 @@ bool NormalizeOperation::determineDependingAreaOfInterest(rcti * /*input*/, */ #define BLENDER_ZMAX 10000.0f -void *NormalizeOperation::initializeTileData(rcti *rect) +void *NormalizeOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); /* using generic two floats struct to store `x: min`, `y: multiply`. */ NodeTwoFloats *minmult = new NodeTwoFloats(); - float *buffer = tile->getBuffer(); - int p = tile->getWidth() * tile->getHeight(); + float *buffer = tile->get_buffer(); + int p = tile->get_width() * tile->get_height(); float *bc = buffer; float minv = 1.0f + BLENDER_ZMAX; @@ -117,14 +117,14 @@ void *NormalizeOperation::initializeTileData(rcti *rect) /* The rare case of flat buffer would cause a divide by 0 */ minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); - this->m_cachedInstance = minmult; + cached_instance_ = minmult; } - unlockMutex(); - return this->m_cachedInstance; + unlock_mutex(); + return cached_instance_; } -void NormalizeOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) +void NormalizeOperation::deinitialize_tile_data(rcti * /*rect*/, void * /*data*/) { /* pass */ } @@ -133,18 +133,14 @@ void NormalizeOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - NodeOperation *input = get_input_operation(0); - r_input_area.xmin = 0; - r_input_area.xmax = input->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = input->getHeight(); + r_input_area = get_input_operation(0)->get_canvas(); } void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output), const rcti &UNUSED(area), Span inputs) { - if (m_cachedInstance == nullptr) { + if (cached_instance_ == nullptr) { MemoryBuffer *input = inputs[0]; /* Using generic two floats struct to store `x: min`, `y: multiply`. */ @@ -166,7 +162,7 @@ void NormalizeOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(outpu /* The case of a flat buffer would cause a divide by 0. */ minmult->y = ((maxv != minv) ? 1.0f / (maxv - minv) : 0.0f); - m_cachedInstance = minmult; + cached_instance_ = minmult; } } @@ -174,7 +170,7 @@ void NormalizeOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - NodeTwoFloats *minmult = m_cachedInstance; + NodeTwoFloats *minmult = cached_instance_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { const float input_value = *it.in(0); diff --git a/source/blender/compositor/operations/COM_NormalizeOperation.h b/source/blender/compositor/operations/COM_NormalizeOperation.h index 7af2aad8a88..dd9abcfad26 100644 --- a/source/blender/compositor/operations/COM_NormalizeOperation.h +++ b/source/blender/compositor/operations/COM_NormalizeOperation.h @@ -32,13 +32,13 @@ class NormalizeOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_imageReader; + SocketReader *image_reader_; /** * \brief temporarily cache of the execution storage * it stores `x->min` and `y->multiply`. */ - NodeTwoFloats *m_cachedInstance; + NodeTwoFloats *cached_instance_; public: NormalizeOperation(); @@ -46,24 +46,24 @@ class NormalizeOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_OpenCLKernels.cl b/source/blender/compositor/operations/COM_OpenCLKernels.cl index ebe8a6d08ec..d01e209d3e4 100644 --- a/source/blender/compositor/operations/COM_OpenCLKernels.cl +++ b/source/blender/compositor/operations/COM_OpenCLKernels.cl @@ -29,101 +29,101 @@ const sampler_t SAMPLER_NEAREST_CLAMP = CLK_NORMALIZED_COORDS_FALSE | CLK_ADDRES __constant const int2 zero = {0,0}; // KERNEL --- BOKEH BLUR --- -__kernel void bokehBlurKernel(__read_only image2d_t boundingBox, __read_only image2d_t inputImage, - __read_only image2d_t bokehImage, __write_only image2d_t output, - int2 offsetInput, int2 offsetOutput, int radius, int step, int2 dimension, int2 offset) +__kernel void bokeh_blur_kernel(__read_only image2d_t bounding_box, __read_only image2d_t input_image, + __read_only image2d_t bokeh_image, __write_only image2d_t output, + int2 offset_input, int2 offset_output, int radius, int step, int2 dimension, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - float tempBoundingBox; + float temp_bounding_box; float4 color = {0.0f,0.0f,0.0f,0.0f}; float4 multiplyer = {0.0f,0.0f,0.0f,0.0f}; float4 bokeh; const float radius2 = radius*2.0f; - const int2 realCoordinate = coords + offsetOutput; - int2 imageCoordinates = realCoordinate - offsetInput; + const int2 real_coordinate = coords + offset_output; + int2 image_coordinates = real_coordinate - offset_input; - tempBoundingBox = read_imagef(boundingBox, SAMPLER_NEAREST, coords).s0; + temp_bounding_box = read_imagef(bounding_box, SAMPLER_NEAREST, coords).s0; - if (tempBoundingBox > 0.0f && radius > 0 ) { - const int2 bokehImageDim = get_image_dim(bokehImage); - const int2 bokehImageCenter = bokehImageDim/2; - const int2 minXY = max(realCoordinate - radius, zero); - const int2 maxXY = min(realCoordinate + radius, dimension); + if (temp_bounding_box > 0.0f && radius > 0 ) { + const int2 bokeh_image_dim = get_image_dim(bokeh_image); + const int2 bokeh_image_center = bokeh_image_dim/2; + const int2 minXY = max(real_coordinate - radius, zero); + const int2 maxXY = min(real_coordinate + radius, dimension); int nx, ny; float2 uv; - int2 inputXy; + int2 input_xy; if (radius < 2) { - color = read_imagef(inputImage, SAMPLER_NEAREST, imageCoordinates); + color = read_imagef(input_image, SAMPLER_NEAREST, image_coordinates); multiplyer = (float4)(1.0f, 1.0f, 1.0f, 1.0f); } - for (ny = minXY.y, inputXy.y = ny - offsetInput.y ; ny < maxXY.y ; ny += step, inputXy.y += step) { - uv.y = ((realCoordinate.y-ny)/radius2)*bokehImageDim.y+bokehImageCenter.y; + for (ny = minXY.y, input_xy.y = ny - offset_input.y ; ny < maxXY.y ; ny += step, input_xy.y += step) { + uv.y = ((real_coordinate.y-ny)/radius2)*bokeh_image_dim.y+bokeh_image_center.y; - for (nx = minXY.x, inputXy.x = nx - offsetInput.x; nx < maxXY.x ; nx += step, inputXy.x += step) { - uv.x = ((realCoordinate.x-nx)/radius2)*bokehImageDim.x+bokehImageCenter.x; - bokeh = read_imagef(bokehImage, SAMPLER_NEAREST, uv); - color += bokeh * read_imagef(inputImage, SAMPLER_NEAREST, inputXy); + for (nx = minXY.x, input_xy.x = nx - offset_input.x; nx < maxXY.x ; nx += step, input_xy.x += step) { + uv.x = ((real_coordinate.x-nx)/radius2)*bokeh_image_dim.x+bokeh_image_center.x; + bokeh = read_imagef(bokeh_image, SAMPLER_NEAREST, uv); + color += bokeh * read_imagef(input_image, SAMPLER_NEAREST, input_xy); multiplyer += bokeh; } } color /= multiplyer; } else { - color = read_imagef(inputImage, SAMPLER_NEAREST, imageCoordinates); + color = read_imagef(input_image, SAMPLER_NEAREST, image_coordinates); } write_imagef(output, coords, color); } //KERNEL --- DEFOCUS /VARIABLESIZEBOKEHBLUR --- -__kernel void defocusKernel(__read_only image2d_t inputImage, __read_only image2d_t bokehImage, - __read_only image2d_t inputSize, - __write_only image2d_t output, int2 offsetInput, int2 offsetOutput, - int step, int maxBlurScalar, float threshold, float scalar, int2 dimension, int2 offset) +__kernel void defocus_kernel(__read_only image2d_t input_image, __read_only image2d_t bokeh_image, + __read_only image2d_t input_size, + __write_only image2d_t output, int2 offset_input, int2 offset_output, + int step, int max_blur_scalar, float threshold, float scalar, int2 dimension, int2 offset) { float4 color = {1.0f, 0.0f, 0.0f, 1.0f}; int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; - float4 readColor; - float4 tempColor; + float4 read_color; + float4 temp_color; float4 bokeh; float size; float4 multiplier_accum = {1.0f, 1.0f, 1.0f, 1.0f}; float4 color_accum; - int minx = max(realCoordinate.s0 - maxBlurScalar, 0); - int miny = max(realCoordinate.s1 - maxBlurScalar, 0); - int maxx = min(realCoordinate.s0 + maxBlurScalar, dimension.s0); - int maxy = min(realCoordinate.s1 + maxBlurScalar, dimension.s1); + int minx = max(real_coordinate.s0 - max_blur_scalar, 0); + int miny = max(real_coordinate.s1 - max_blur_scalar, 0); + int maxx = min(real_coordinate.s0 + max_blur_scalar, dimension.s0); + int maxy = min(real_coordinate.s1 + max_blur_scalar, dimension.s1); { - int2 inputCoordinate = realCoordinate - offsetInput; - float size_center = read_imagef(inputSize, SAMPLER_NEAREST, inputCoordinate).s0 * scalar; - color_accum = read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate); - readColor = color_accum; + int2 input_coordinate = real_coordinate - offset_input; + float size_center = read_imagef(input_size, SAMPLER_NEAREST, input_coordinate).s0 * scalar; + color_accum = read_imagef(input_image, SAMPLER_NEAREST, input_coordinate); + read_color = color_accum; if (size_center > threshold) { for (int ny = miny; ny < maxy; ny += step) { - inputCoordinate.s1 = ny - offsetInput.s1; - float dy = ny - realCoordinate.s1; + input_coordinate.s1 = ny - offset_input.s1; + float dy = ny - real_coordinate.s1; for (int nx = minx; nx < maxx; nx += step) { - float dx = nx - realCoordinate.s0; + float dx = nx - real_coordinate.s0; if (dx != 0 || dy != 0) { - inputCoordinate.s0 = nx - offsetInput.s0; - size = min(read_imagef(inputSize, SAMPLER_NEAREST, inputCoordinate).s0 * scalar, size_center); + input_coordinate.s0 = nx - offset_input.s0; + size = min(read_imagef(input_size, SAMPLER_NEAREST, input_coordinate).s0 * scalar, size_center); if (size > threshold) { if (size >= fabs(dx) && size >= fabs(dy)) { float2 uv = {256.0f + dx * 255.0f / size, 256.0f + dy * 255.0f / size}; - bokeh = read_imagef(bokehImage, SAMPLER_NEAREST, uv); - tempColor = read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate); - color_accum += bokeh * tempColor; + bokeh = read_imagef(bokeh_image, SAMPLER_NEAREST, uv); + temp_color = read_imagef(input_image, SAMPLER_NEAREST, input_coordinate); + color_accum += bokeh * temp_color; multiplier_accum += bokeh; } } @@ -140,7 +140,7 @@ __kernel void defocusKernel(__read_only image2d_t inputImage, __read_only image2 { /* factor from 0-1 */ float fac = (size_center - threshold) / threshold; - color = (readColor * (1.0f - fac)) + (color * fac); + color = (read_color * (1.0f - fac)) + (color * fac); } write_imagef(output, coords, color); @@ -149,28 +149,28 @@ __kernel void defocusKernel(__read_only image2d_t inputImage, __read_only image2 // KERNEL --- DILATE --- -__kernel void dilateKernel(__read_only image2d_t inputImage, __write_only image2d_t output, - int2 offsetInput, int2 offsetOutput, int scope, int distanceSquared, int2 dimension, +__kernel void dilate_kernel(__read_only image2d_t input_image, __write_only image2d_t output, + int2 offset_input, int2 offset_output, int scope, int distance_squared, int2 dimension, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; - const int2 minXY = max(realCoordinate - scope, zero); - const int2 maxXY = min(realCoordinate + scope, dimension); + const int2 minXY = max(real_coordinate - scope, zero); + const int2 maxXY = min(real_coordinate + scope, dimension); float value = 0.0f; int nx, ny; - int2 inputXy; + int2 input_xy; - for (ny = minXY.y, inputXy.y = ny - offsetInput.y ; ny < maxXY.y ; ny ++, inputXy.y++) { - const float deltaY = (realCoordinate.y - ny); - for (nx = minXY.x, inputXy.x = nx - offsetInput.x; nx < maxXY.x ; nx ++, inputXy.x++) { - const float deltaX = (realCoordinate.x - nx); - const float measuredDistance = deltaX * deltaX + deltaY * deltaY; - if (measuredDistance <= distanceSquared) { - value = max(value, read_imagef(inputImage, SAMPLER_NEAREST, inputXy).s0); + for (ny = minXY.y, input_xy.y = ny - offset_input.y ; ny < maxXY.y ; ny ++, input_xy.y++) { + const float deltaY = (real_coordinate.y - ny); + for (nx = minXY.x, input_xy.x = nx - offset_input.x; nx < maxXY.x ; nx ++, input_xy.x++) { + const float deltaX = (real_coordinate.x - nx); + const float measured_distance = deltaX * deltaX + deltaY * deltaY; + if (measured_distance <= distance_squared) { + value = max(value, read_imagef(input_image, SAMPLER_NEAREST, input_xy).s0); } } } @@ -180,28 +180,28 @@ __kernel void dilateKernel(__read_only image2d_t inputImage, __write_only image } // KERNEL --- DILATE --- -__kernel void erodeKernel(__read_only image2d_t inputImage, __write_only image2d_t output, - int2 offsetInput, int2 offsetOutput, int scope, int distanceSquared, int2 dimension, +__kernel void erode_kernel(__read_only image2d_t input_image, __write_only image2d_t output, + int2 offset_input, int2 offset_output, int scope, int distance_squared, int2 dimension, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; - const int2 minXY = max(realCoordinate - scope, zero); - const int2 maxXY = min(realCoordinate + scope, dimension); + const int2 minXY = max(real_coordinate - scope, zero); + const int2 maxXY = min(real_coordinate + scope, dimension); float value = 1.0f; int nx, ny; - int2 inputXy; + int2 input_xy; - for (ny = minXY.y, inputXy.y = ny - offsetInput.y ; ny < maxXY.y ; ny ++, inputXy.y++) { - for (nx = minXY.x, inputXy.x = nx - offsetInput.x; nx < maxXY.x ; nx ++, inputXy.x++) { - const float deltaX = (realCoordinate.x - nx); - const float deltaY = (realCoordinate.y - ny); - const float measuredDistance = deltaX * deltaX+deltaY * deltaY; - if (measuredDistance <= distanceSquared) { - value = min(value, read_imagef(inputImage, SAMPLER_NEAREST, inputXy).s0); + for (ny = minXY.y, input_xy.y = ny - offset_input.y ; ny < maxXY.y ; ny ++, input_xy.y++) { + for (nx = minXY.x, input_xy.x = nx - offset_input.x; nx < maxXY.x ; nx ++, input_xy.x++) { + const float deltaX = (real_coordinate.x - nx); + const float deltaY = (real_coordinate.y - ny); + const float measured_distance = deltaX * deltaX+deltaY * deltaY; + if (measured_distance <= distance_squared) { + value = min(value, read_imagef(input_image, SAMPLER_NEAREST, input_xy).s0); } } } @@ -211,34 +211,34 @@ __kernel void erodeKernel(__read_only image2d_t inputImage, __write_only image2 } // KERNEL --- DIRECTIONAL BLUR --- -__kernel void directionalBlurKernel(__read_only image2d_t inputImage, __write_only image2d_t output, - int2 offsetOutput, int iterations, float scale, float rotation, float2 translate, +__kernel void directional_blur_kernel(__read_only image2d_t input_image, __write_only image2d_t output, + int2 offset_output, int iterations, float scale, float rotation, float2 translate, float2 center, int2 offset) { int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; + const int2 real_coordinate = coords + offset_output; float4 col; float2 ltxy = translate; float lsc = scale; float lrot = rotation; - col = read_imagef(inputImage, SAMPLER_NEAREST, realCoordinate); + col = read_imagef(input_image, SAMPLER_NEAREST, real_coordinate); /* blur the image */ for (int i = 0; i < iterations; ++i) { const float cs = cos(lrot), ss = sin(lrot); const float isc = 1.0f / (1.0f + lsc); - const float v = isc * (realCoordinate.s1 - center.s1) + ltxy.s1; - const float u = isc * (realCoordinate.s0 - center.s0) + ltxy.s0; + const float v = isc * (real_coordinate.s1 - center.s1) + ltxy.s1; + const float u = isc * (real_coordinate.s0 - center.s0) + ltxy.s0; float2 uv = { cs * u + ss * v + center.s0, cs * v - ss * u + center.s1 }; - col += read_imagef(inputImage, SAMPLER_NEAREST_CLAMP, uv); + col += read_imagef(input_image, SAMPLER_NEAREST_CLAMP, uv); /* double transformations */ ltxy += translate; @@ -252,10 +252,10 @@ __kernel void directionalBlurKernel(__read_only image2d_t inputImage, __write_o } // KERNEL --- GAUSSIAN BLUR --- -__kernel void gaussianXBlurOperationKernel(__read_only image2d_t inputImage, - int2 offsetInput, +__kernel void gaussian_xblur_operation_kernel(__read_only image2d_t input_image, + int2 offset_input, __write_only image2d_t output, - int2 offsetOutput, + int2 offset_output, int filter_size, int2 dimension, __global float *gausstab, @@ -264,17 +264,17 @@ __kernel void gaussianXBlurOperationKernel(__read_only image2d_t inputImage, float4 color = {0.0f, 0.0f, 0.0f, 0.0f}; int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; - int2 inputCoordinate = realCoordinate - offsetInput; + const int2 real_coordinate = coords + offset_output; + int2 input_coordinate = real_coordinate - offset_input; float weight = 0.0f; - int xmin = max(realCoordinate.x - filter_size, 0) - offsetInput.x; - int xmax = min(realCoordinate.x + filter_size + 1, dimension.x) - offsetInput.x; + int xmin = max(real_coordinate.x - filter_size, 0) - offset_input.x; + int xmax = min(real_coordinate.x + filter_size + 1, dimension.x) - offset_input.x; - for (int nx = xmin, i = max(filter_size - realCoordinate.x, 0); nx < xmax; ++nx, ++i) { + for (int nx = xmin, i = max(filter_size - real_coordinate.x, 0); nx < xmax; ++nx, ++i) { float w = gausstab[i]; - inputCoordinate.x = nx; - color += read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate) * w; + input_coordinate.x = nx; + color += read_imagef(input_image, SAMPLER_NEAREST, input_coordinate) * w; weight += w; } @@ -283,10 +283,10 @@ __kernel void gaussianXBlurOperationKernel(__read_only image2d_t inputImage, write_imagef(output, coords, color); } -__kernel void gaussianYBlurOperationKernel(__read_only image2d_t inputImage, - int2 offsetInput, +__kernel void gaussian_yblur_operation_kernel(__read_only image2d_t input_image, + int2 offset_input, __write_only image2d_t output, - int2 offsetOutput, + int2 offset_output, int filter_size, int2 dimension, __global float *gausstab, @@ -295,17 +295,17 @@ __kernel void gaussianYBlurOperationKernel(__read_only image2d_t inputImage, float4 color = {0.0f, 0.0f, 0.0f, 0.0f}; int2 coords = {get_global_id(0), get_global_id(1)}; coords += offset; - const int2 realCoordinate = coords + offsetOutput; - int2 inputCoordinate = realCoordinate - offsetInput; + const int2 real_coordinate = coords + offset_output; + int2 input_coordinate = real_coordinate - offset_input; float weight = 0.0f; - int ymin = max(realCoordinate.y - filter_size, 0) - offsetInput.y; - int ymax = min(realCoordinate.y + filter_size + 1, dimension.y) - offsetInput.y; + int ymin = max(real_coordinate.y - filter_size, 0) - offset_input.y; + int ymax = min(real_coordinate.y + filter_size + 1, dimension.y) - offset_input.y; - for (int ny = ymin, i = max(filter_size - realCoordinate.y, 0); ny < ymax; ++ny, ++i) { + for (int ny = ymin, i = max(filter_size - real_coordinate.y, 0); ny < ymax; ++ny, ++i) { float w = gausstab[i]; - inputCoordinate.y = ny; - color += read_imagef(inputImage, SAMPLER_NEAREST, inputCoordinate) * w; + input_coordinate.y = ny; + color += read_imagef(input_image, SAMPLER_NEAREST, input_coordinate) * w; weight += w; } diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc index 5b6f650d40e..7f2968a4719 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cc @@ -17,22 +17,11 @@ */ #include "COM_OutputFileMultiViewOperation.h" -#include "COM_OutputFileOperation.h" -#include - -#include "BLI_listbase.h" -#include "BLI_path_util.h" -#include "BLI_string.h" - -#include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_scene.h" -#include "DNA_color_types.h" -#include "MEM_guardedalloc.h" - #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -47,19 +36,26 @@ OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOpe DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender) - : OutputSingleLayerOperation( - rd, tree, datatype, format, path, viewSettings, displaySettings, viewName, saveAsRender) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render) + : OutputSingleLayerOperation(rd, + tree, + datatype, + format, + path, + view_settings, + display_settings, + view_name, + save_as_render) { } void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filename) { - size_t width = this->getWidth(); - size_t height = this->getHeight(); + size_t width = this->get_width(); + size_t height = this->get_height(); SceneRenderView *srv; if (width != 0 && height != 0) { @@ -67,27 +63,26 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { return exrhandle; } IMB_exr_clear_channels(exrhandle); - for (srv = (SceneRenderView *)this->m_rd->views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) { + for (srv = (SceneRenderView *)rd_->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd_, srv) == false) { continue; } IMB_exr_add_view(exrhandle, srv->name); - add_exr_channels(exrhandle, nullptr, this->m_datatype, srv->name, width, false, nullptr); + add_exr_channels(exrhandle, nullptr, datatype_, srv->name, width, false, nullptr); } BLI_make_existing_file(filename); /* prepare the file with all the channels */ - if (IMB_exr_begin_write( - exrhandle, filename, width, height, this->m_format->exr_codec, nullptr) == 0) { + if (!IMB_exr_begin_write(exrhandle, filename, width, height, format_->exr_codec, nullptr)) { printf("Error Writing Singlelayer Multiview Openexr\n"); IMB_exr_close(exrhandle); } @@ -99,43 +94,43 @@ void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filenam return nullptr; } -void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() +void OutputOpenExrSingleLayerMultiViewOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { void *exrhandle; char filename[FILE_MAX]; BKE_image_path_from_imtype(filename, - this->m_path, + path_, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, + rd_->cfra, R_IMF_IMTYPE_OPENEXR, - (this->m_rd->scemode & R_EXTENSION) != 0, + (rd_->scemode & R_EXTENSION) != 0, true, nullptr); exrhandle = this->get_handle(filename); add_exr_channels(exrhandle, nullptr, - this->m_datatype, - this->m_viewName, + datatype_, + view_name_, width, - this->m_format->depth == R_IMF_CHAN_DEPTH_16, - this->m_outputBuffer); + format_->depth == R_IMF_CHAN_DEPTH_16, + output_buffer_); /* memory can only be freed after we write all views to the file */ - this->m_outputBuffer = nullptr; - this->m_imageInput = nullptr; + output_buffer_ = nullptr; + image_input_ = nullptr; /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(rd_, view_name_)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ - free_exr_channels(exrhandle, this->m_rd, nullptr, this->m_datatype); + free_exr_channels(exrhandle, rd_, nullptr, datatype_); /* remove exr handle and data */ IMB_exr_close(exrhandle); @@ -152,15 +147,15 @@ OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOpera const char *path, char exr_codec, bool exr_half_float, - const char *viewName) - : OutputOpenExrMultiLayerOperation(scene, rd, tree, path, exr_codec, exr_half_float, viewName) + const char *view_name) + : OutputOpenExrMultiLayerOperation(scene, rd, tree, path, exr_codec, exr_half_float, view_name) { } void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename) { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { @@ -170,28 +165,28 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename /* get a new global handle */ exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { return exrhandle; } IMB_exr_clear_channels(exrhandle); /* check renderdata for amount of views */ - for (srv = (SceneRenderView *)this->m_rd->views.first; srv; srv = srv->next) { + for (srv = (SceneRenderView *)rd_->views.first; srv; srv = srv->next) { - if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) { + if (BKE_scene_multiview_is_render_view_active(rd_, srv) == false) { continue; } IMB_exr_add_view(exrhandle, srv->name); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { + for (unsigned int i = 0; i < layers_.size(); i++) { add_exr_channels(exrhandle, - this->m_layers[i].name, - this->m_layers[i].datatype, + layers_[i].name, + layers_[i].datatype, srv->name, width, - this->m_exr_half_float, + exr_half_float_, nullptr); } } @@ -199,9 +194,8 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename BLI_make_existing_file(filename); /* prepare the file with all the channels for the header */ - StampData *stamp_data = createStampData(); - if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data) == - 0) { + StampData *stamp_data = create_stamp_data(); + if (!IMB_exr_begin_write(exrhandle, filename, width, height, exr_codec_, stamp_data)) { printf("Error Writing Multilayer Multiview Openexr\n"); IMB_exr_close(exrhandle); BKE_stamp_data_free(stamp_data); @@ -215,50 +209,49 @@ void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename return nullptr; } -void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() +void OutputOpenExrMultiLayerMultiViewOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { void *exrhandle; char filename[FILE_MAX]; BKE_image_path_from_imtype(filename, - this->m_path, + path_, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, + rd_->cfra, R_IMF_IMTYPE_MULTILAYER, - (this->m_rd->scemode & R_EXTENSION) != 0, + (rd_->scemode & R_EXTENSION) != 0, true, nullptr); exrhandle = this->get_handle(filename); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { + for (unsigned int i = 0; i < layers_.size(); i++) { add_exr_channels(exrhandle, - this->m_layers[i].name, - this->m_layers[i].datatype, - this->m_viewName, + layers_[i].name, + layers_[i].datatype, + view_name_, width, - this->m_exr_half_float, - this->m_layers[i].outputBuffer); + exr_half_float_, + layers_[i].output_buffer); } - for (unsigned int i = 0; i < this->m_layers.size(); i++) { + for (unsigned int i = 0; i < layers_.size(); i++) { /* memory can only be freed after we write all views to the file */ - this->m_layers[i].outputBuffer = nullptr; - this->m_layers[i].imageInput = nullptr; + layers_[i].output_buffer = nullptr; + layers_[i].image_input = nullptr; } /* ready to close the file */ - if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(rd_, view_name_)) { IMB_exr_write_channels(exrhandle); /* free buffer memory for all the views */ - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - free_exr_channels( - exrhandle, this->m_rd, this->m_layers[i].name, this->m_layers[i].datatype); + for (unsigned int i = 0; i < layers_.size(); i++) { + free_exr_channels(exrhandle, rd_, layers_[i].name, layers_[i].datatype); } IMB_exr_close(exrhandle); @@ -274,21 +267,28 @@ OutputStereoOperation::OutputStereoOperation(const RenderData *rd, ImageFormatData *format, const char *path, const char *name, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender) - : OutputSingleLayerOperation( - rd, tree, datatype, format, path, viewSettings, displaySettings, viewName, saveAsRender) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render) + : OutputSingleLayerOperation(rd, + tree, + datatype, + format, + path, + view_settings, + display_settings, + view_name, + save_as_render) { - BLI_strncpy(this->m_name, name, sizeof(this->m_name)); - this->m_channels = get_datatype_size(datatype); + BLI_strncpy(name_, name, sizeof(name_)); + channels_ = get_datatype_size(datatype); } void *OutputStereoOperation::get_handle(const char *filename) { - size_t width = this->getWidth(); - size_t height = this->getHeight(); + size_t width = this->get_width(); + size_t height = this->get_height(); const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; size_t i; @@ -297,7 +297,7 @@ void *OutputStereoOperation::get_handle(const char *filename) exrhandle = IMB_exr_get_handle_name(filename); - if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + if (!BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { return exrhandle; } @@ -312,32 +312,32 @@ void *OutputStereoOperation::get_handle(const char *filename) return nullptr; } -void OutputStereoOperation::deinitExecution() +void OutputStereoOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { void *exrhandle; - exrhandle = this->get_handle(this->m_path); - float *buf = this->m_outputBuffer; + exrhandle = this->get_handle(path_); + float *buf = output_buffer_; /* populate single EXR channel with view data */ IMB_exr_add_channel(exrhandle, nullptr, - this->m_name, - this->m_viewName, + name_, + view_name_, 1, - this->m_channels * width * height, + channels_ * width * height, buf, - this->m_format->depth == R_IMF_CHAN_DEPTH_16); + format_->depth == R_IMF_CHAN_DEPTH_16); - this->m_imageInput = nullptr; - this->m_outputBuffer = nullptr; + image_input_ = nullptr; + output_buffer_ = nullptr; /* create stereo ibuf */ - if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + if (BKE_scene_multiview_is_render_view_last(rd_, view_name_)) { ImBuf *ibuf[3] = {nullptr}; const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; char filename[FILE_MAX]; @@ -345,33 +345,33 @@ void OutputStereoOperation::deinitExecution() /* get rectf from EXR */ for (i = 0; i < 2; i++) { - float *rectf = IMB_exr_channel_rect(exrhandle, nullptr, this->m_name, names[i]); - ibuf[i] = IMB_allocImBuf(width, height, this->m_format->planes, 0); + float *rectf = IMB_exr_channel_rect(exrhandle, nullptr, name_, names[i]); + ibuf[i] = IMB_allocImBuf(width, height, format_->planes, 0); - ibuf[i]->channels = this->m_channels; + ibuf[i]->channels = channels_; ibuf[i]->rect_float = rectf; ibuf[i]->mall |= IB_rectfloat; - ibuf[i]->dither = this->m_rd->dither_intensity; + ibuf[i]->dither = rd_->dither_intensity; /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ IMB_colormanagement_imbuf_for_write( - ibuf[i], true, false, this->m_viewSettings, this->m_displaySettings, this->m_format); + ibuf[i], true, false, view_settings_, display_settings_, format_); IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); } /* create stereo buffer */ - ibuf[2] = IMB_stereo3d_ImBuf(this->m_format, ibuf[0], ibuf[1]); + ibuf[2] = IMB_stereo3d_ImBuf(format_, ibuf[0], ibuf[1]); BKE_image_path_from_imformat(filename, - this->m_path, + path_, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, - this->m_format, - (this->m_rd->scemode & R_EXTENSION) != 0, + rd_->cfra, + format_, + (rd_->scemode & R_EXTENSION) != 0, true, nullptr); - BKE_imbuf_write(ibuf[2], filename, this->m_format); + BKE_imbuf_write(ibuf[2], filename, format_); /* imbuf knows which rects are not part of ibuf */ for (i = 0; i < 3; i++) { diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h index 6230a6f306b..9f8bf2ed127 100644 --- a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -38,13 +38,13 @@ class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOpera DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render); void *get_handle(const char *filename); - void deinitExecution() override; + void deinit_execution() override; }; /* Writes inputs into OpenEXR multilayer channels. */ @@ -57,16 +57,16 @@ class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayer const char *path, char exr_codec, bool exr_half_float, - const char *viewName); + const char *view_name); void *get_handle(const char *filename); - void deinitExecution() override; + void deinit_execution() override; }; class OutputStereoOperation : public OutputSingleLayerOperation { private: - char m_name[FILE_MAX]; - size_t m_channels; + char name_[FILE_MAX]; + size_t channels_; public: OutputStereoOperation(const RenderData *rd, @@ -75,12 +75,12 @@ class OutputStereoOperation : public OutputSingleLayerOperation { struct ImageFormatData *format, const char *path, const char *name, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render); void *get_handle(const char *filename); - void deinitExecution() override; + void deinit_execution() override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cc b/source/blender/compositor/operations/COM_OutputFileOperation.cc index 402d29893a4..77e291e9dc4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cc +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cc @@ -18,21 +18,15 @@ #include "COM_OutputFileOperation.h" -#include "COM_MetaData.h" - -#include - #include "BLI_listbase.h" #include "BLI_path_util.h" #include "BLI_string.h" -#include "BKE_global.h" #include "BKE_image.h" #include "BKE_main.h" #include "BKE_scene.h" #include "DNA_color_types.h" -#include "MEM_guardedalloc.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" @@ -43,9 +37,9 @@ namespace blender::compositor { void add_exr_channels(void *exrhandle, - const char *layerName, + const char *layer_name, const DataType datatype, - const char *viewName, + const char *view_name, const size_t width, bool use_half_float, float *buf) @@ -54,51 +48,63 @@ void add_exr_channels(void *exrhandle, switch (datatype) { case DataType::Value: IMB_exr_add_channel( - exrhandle, layerName, "V", viewName, 1, width, buf ? buf : nullptr, use_half_float); + exrhandle, layer_name, "V", view_name, 1, width, buf ? buf : nullptr, use_half_float); break; case DataType::Vector: - IMB_exr_add_channel( - exrhandle, layerName, "X", viewName, 3, 3 * width, buf ? buf : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, + "X", + view_name, + 3, + 3 * width, + buf ? buf : nullptr, + use_half_float); + IMB_exr_add_channel(exrhandle, + layer_name, "Y", - viewName, + view_name, 3, 3 * width, buf ? buf + 1 : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, "Z", - viewName, + view_name, 3, 3 * width, buf ? buf + 2 : nullptr, use_half_float); break; case DataType::Color: - IMB_exr_add_channel( - exrhandle, layerName, "R", viewName, 4, 4 * width, buf ? buf : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, + "R", + view_name, + 4, + 4 * width, + buf ? buf : nullptr, + use_half_float); + IMB_exr_add_channel(exrhandle, + layer_name, "G", - viewName, + view_name, 4, 4 * width, buf ? buf + 1 : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, "B", - viewName, + view_name, 4, 4 * width, buf ? buf + 2 : nullptr, use_half_float); IMB_exr_add_channel(exrhandle, - layerName, + layer_name, "A", - viewName, + view_name, 4, 4 * width, buf ? buf + 3 : nullptr, @@ -111,7 +117,7 @@ void add_exr_channels(void *exrhandle, void free_exr_channels(void *exrhandle, const RenderData *rd, - const char *layerName, + const char *layer_name, const DataType datatype) { SceneRenderView *srv; @@ -127,13 +133,13 @@ void free_exr_channels(void *exrhandle, /* the pointer is stored in the first channel of each datatype */ switch (datatype) { case DataType::Value: - rect = IMB_exr_channel_rect(exrhandle, layerName, "V", srv->name); + rect = IMB_exr_channel_rect(exrhandle, layer_name, "V", srv->name); break; case DataType::Vector: - rect = IMB_exr_channel_rect(exrhandle, layerName, "X", srv->name); + rect = IMB_exr_channel_rect(exrhandle, layer_name, "X", srv->name); break; case DataType::Color: - rect = IMB_exr_channel_rect(exrhandle, layerName, "R", srv->name); + rect = IMB_exr_channel_rect(exrhandle, layer_name, "R", srv->name); break; default: break; @@ -193,7 +199,7 @@ static void write_buffer_rect(rcti *rect, for (y = y1; y < y2 && (!breaked); y++) { for (x = x1; x < x2 && (!breaked); x++) { - reader->readSampled(color, x, y, PixelSampler::Nearest); + reader->read_sampled(color, x, y, PixelSampler::Nearest); for (i = 0; i < size; i++) { buffer[offset + i] = color[i]; @@ -214,74 +220,69 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender) + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render) { - this->m_rd = rd; - this->m_tree = tree; + rd_ = rd; + tree_ = tree; - this->addInputSocket(datatype); + this->add_input_socket(datatype); - this->m_outputBuffer = nullptr; - this->m_datatype = datatype; - this->m_imageInput = nullptr; + output_buffer_ = nullptr; + datatype_ = datatype; + image_input_ = nullptr; - this->m_format = format; - BLI_strncpy(this->m_path, path, sizeof(this->m_path)); + format_ = format; + BLI_strncpy(path_, path, sizeof(path_)); - this->m_viewSettings = viewSettings; - this->m_displaySettings = displaySettings; - this->m_viewName = viewName; - this->m_saveAsRender = saveAsRender; + view_settings_ = view_settings; + display_settings_ = display_settings; + view_name_ = view_name; + save_as_render_ = save_as_render; } -void OutputSingleLayerOperation::initExecution() +void OutputSingleLayerOperation::init_execution() { - this->m_imageInput = getInputSocketReader(0); - this->m_outputBuffer = init_buffer(this->getWidth(), this->getHeight(), this->m_datatype); + image_input_ = get_input_socket_reader(0); + output_buffer_ = init_buffer(this->get_width(), this->get_height(), datatype_); } -void OutputSingleLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void OutputSingleLayerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - write_buffer_rect(rect, - this->m_tree, - this->m_imageInput, - this->m_outputBuffer, - this->getWidth(), - this->m_datatype); + write_buffer_rect(rect, tree_, image_input_, output_buffer_, this->get_width(), datatype_); } -void OutputSingleLayerOperation::deinitExecution() +void OutputSingleLayerOperation::deinit_execution() { - if (this->getWidth() * this->getHeight() != 0) { + if (this->get_width() * this->get_height() != 0) { - int size = get_datatype_size(this->m_datatype); - ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), this->m_format->planes, 0); + int size = get_datatype_size(datatype_); + ImBuf *ibuf = IMB_allocImBuf(this->get_width(), this->get_height(), format_->planes, 0); char filename[FILE_MAX]; const char *suffix; ibuf->channels = size; - ibuf->rect_float = this->m_outputBuffer; + ibuf->rect_float = output_buffer_; ibuf->mall |= IB_rectfloat; - ibuf->dither = this->m_rd->dither_intensity; + ibuf->dither = rd_->dither_intensity; IMB_colormanagement_imbuf_for_write( - ibuf, m_saveAsRender, false, m_viewSettings, m_displaySettings, this->m_format); + ibuf, save_as_render_, false, view_settings_, display_settings_, format_); - suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); + suffix = BKE_scene_multiview_view_suffix_get(rd_, view_name_); BKE_image_path_from_imformat(filename, - this->m_path, + path_, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, - this->m_format, - (this->m_rd->scemode & R_EXTENSION) != 0, + rd_->cfra, + format_, + (rd_->scemode & R_EXTENSION) != 0, true, suffix); - if (0 == BKE_imbuf_write(ibuf, filename, this->m_format)) { + if (0 == BKE_imbuf_write(ibuf, filename, format_)) { printf("Cannot save Node File Output to %s\n", filename); } else { @@ -290,22 +291,22 @@ void OutputSingleLayerOperation::deinitExecution() IMB_freeImBuf(ibuf); } - this->m_outputBuffer = nullptr; - this->m_imageInput = nullptr; + output_buffer_ = nullptr; + image_input_ = nullptr; } void OutputSingleLayerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), const rcti &area, Span inputs) { - if (!m_outputBuffer) { + if (!output_buffer_) { return; } - MemoryBuffer output_buf(m_outputBuffer, - COM_data_type_num_channels(this->m_datatype), - this->getWidth(), - this->getHeight()); + MemoryBuffer output_buf(output_buffer_, + COM_data_type_num_channels(datatype_), + this->get_width(), + this->get_height()); const MemoryBuffer *input_image = inputs[0]; output_buf.copy_from(input_image, area); } @@ -318,9 +319,9 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo this->datatype = datatype_; this->use_layer = use_layer_; - /* these are created in initExecution */ - this->outputBuffer = nullptr; - this->imageInput = nullptr; + /* these are created in init_execution */ + this->output_buffer = nullptr; + this->image_input = nullptr; } OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene *scene, @@ -329,116 +330,112 @@ OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(const Scene * const char *path, char exr_codec, bool exr_half_float, - const char *viewName) + const char *view_name) { - this->m_scene = scene; - this->m_rd = rd; - this->m_tree = tree; + scene_ = scene; + rd_ = rd; + tree_ = tree; - BLI_strncpy(this->m_path, path, sizeof(this->m_path)); - this->m_exr_codec = exr_codec; - this->m_exr_half_float = exr_half_float; - this->m_viewName = viewName; - this->setResolutionInputSocketIndex(RESOLUTION_INPUT_ANY); + BLI_strncpy(path_, path, sizeof(path_)); + exr_codec_ = exr_codec; + exr_half_float_ = exr_half_float; + view_name_ = view_name; + this->set_canvas_input_index(RESOLUTION_INPUT_ANY); } void OutputOpenExrMultiLayerOperation::add_layer(const char *name, DataType datatype, bool use_layer) { - this->addInputSocket(datatype); - this->m_layers.append(OutputOpenExrLayer(name, datatype, use_layer)); + this->add_input_socket(datatype); + layers_.append(OutputOpenExrLayer(name, datatype, use_layer)); } -StampData *OutputOpenExrMultiLayerOperation::createStampData() const +StampData *OutputOpenExrMultiLayerOperation::create_stamp_data() const { /* StampData API doesn't provide functions to modify an instance without having a RenderResult. */ RenderResult render_result; - StampData *stamp_data = BKE_stamp_info_from_scene_static(m_scene); + StampData *stamp_data = BKE_stamp_info_from_scene_static(scene_); render_result.stamp_data = stamp_data; - for (const OutputOpenExrLayer &layer : m_layers) { + for (const OutputOpenExrLayer &layer : layers_) { /* Skip unconnected sockets. */ - if (layer.imageInput == nullptr) { + if (layer.image_input == nullptr) { continue; } - std::unique_ptr meta_data = layer.imageInput->getMetaData(); + std::unique_ptr meta_data = layer.image_input->get_meta_data(); if (meta_data) { blender::StringRef layer_name = blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name( blender::StringRef(layer.name, BLI_strnlen(layer.name, sizeof(layer.name)))); - meta_data->replaceHashNeutralCryptomatteKeys(layer_name); - meta_data->addToRenderResult(&render_result); + meta_data->replace_hash_neutral_cryptomatte_keys(layer_name); + meta_data->add_to_render_result(&render_result); } } return stamp_data; } -void OutputOpenExrMultiLayerOperation::initExecution() +void OutputOpenExrMultiLayerOperation::init_execution() { - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - if (this->m_layers[i].use_layer) { - SocketReader *reader = getInputSocketReader(i); - this->m_layers[i].imageInput = reader; - this->m_layers[i].outputBuffer = init_buffer( - this->getWidth(), this->getHeight(), this->m_layers[i].datatype); + for (unsigned int i = 0; i < layers_.size(); i++) { + if (layers_[i].use_layer) { + SocketReader *reader = get_input_socket_reader(i); + layers_[i].image_input = reader; + layers_[i].output_buffer = init_buffer( + this->get_width(), this->get_height(), layers_[i].datatype); } } } -void OutputOpenExrMultiLayerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void OutputOpenExrMultiLayerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - OutputOpenExrLayer &layer = this->m_layers[i]; - if (layer.imageInput) { - write_buffer_rect(rect, - this->m_tree, - layer.imageInput, - layer.outputBuffer, - this->getWidth(), - layer.datatype); + for (unsigned int i = 0; i < layers_.size(); i++) { + OutputOpenExrLayer &layer = layers_[i]; + if (layer.image_input) { + write_buffer_rect( + rect, tree_, layer.image_input, layer.output_buffer, this->get_width(), layer.datatype); } } } -void OutputOpenExrMultiLayerOperation::deinitExecution() +void OutputOpenExrMultiLayerOperation::deinit_execution() { - unsigned int width = this->getWidth(); - unsigned int height = this->getHeight(); + unsigned int width = this->get_width(); + unsigned int height = this->get_height(); if (width != 0 && height != 0) { char filename[FILE_MAX]; const char *suffix; void *exrhandle = IMB_exr_get_handle(); - suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); + suffix = BKE_scene_multiview_view_suffix_get(rd_, view_name_); BKE_image_path_from_imtype(filename, - this->m_path, + path_, BKE_main_blendfile_path_from_global(), - this->m_rd->cfra, + rd_->cfra, R_IMF_IMTYPE_MULTILAYER, - (this->m_rd->scemode & R_EXTENSION) != 0, + (rd_->scemode & R_EXTENSION) != 0, true, suffix); BLI_make_existing_file(filename); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - OutputOpenExrLayer &layer = this->m_layers[i]; - if (!layer.imageInput) { + for (unsigned int i = 0; i < layers_.size(); i++) { + OutputOpenExrLayer &layer = layers_[i]; + if (!layer.image_input) { continue; /* skip unconnected sockets */ } add_exr_channels(exrhandle, - this->m_layers[i].name, - this->m_layers[i].datatype, + layers_[i].name, + layers_[i].datatype, "", width, - this->m_exr_half_float, - this->m_layers[i].outputBuffer); + exr_half_float_, + layers_[i].output_buffer); } /* when the filename has no permissions, this can fail */ - StampData *stamp_data = createStampData(); - if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec, stamp_data)) { + StampData *stamp_data = create_stamp_data(); + if (IMB_exr_begin_write(exrhandle, filename, width, height, exr_codec_, stamp_data)) { IMB_exr_write_channels(exrhandle); } else { @@ -448,13 +445,13 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } IMB_exr_close(exrhandle); - for (unsigned int i = 0; i < this->m_layers.size(); i++) { - if (this->m_layers[i].outputBuffer) { - MEM_freeN(this->m_layers[i].outputBuffer); - this->m_layers[i].outputBuffer = nullptr; + for (unsigned int i = 0; i < layers_.size(); i++) { + if (layers_[i].output_buffer) { + MEM_freeN(layers_[i].output_buffer); + layers_[i].output_buffer = nullptr; } - this->m_layers[i].imageInput = nullptr; + layers_[i].image_input = nullptr; } BKE_stamp_data_free(stamp_data); } @@ -465,13 +462,13 @@ void OutputOpenExrMultiLayerOperation::update_memory_buffer_partial(MemoryBuffer Span inputs) { const MemoryBuffer *input_image = inputs[0]; - for (int i = 0; i < this->m_layers.size(); i++) { - OutputOpenExrLayer &layer = this->m_layers[i]; - if (layer.outputBuffer) { - MemoryBuffer output_buf(layer.outputBuffer, + for (int i = 0; i < layers_.size(); i++) { + OutputOpenExrLayer &layer = layers_[i]; + if (layer.output_buffer) { + MemoryBuffer output_buf(layer.output_buffer, COM_data_type_num_channels(layer.datatype), - this->getWidth(), - this->getHeight()); + this->get_width(), + this->get_height()); output_buf.copy_from(input_image, area); } } diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index 057cee0c43e..2a18b056c12 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -32,21 +32,21 @@ namespace blender::compositor { /* Writes the image to a single-layer file. */ class OutputSingleLayerOperation : public MultiThreadedOperation { protected: - const RenderData *m_rd; - const bNodeTree *m_tree; + const RenderData *rd_; + const bNodeTree *tree_; - ImageFormatData *m_format; - char m_path[FILE_MAX]; + ImageFormatData *format_; + char path_[FILE_MAX]; - float *m_outputBuffer; - DataType m_datatype; - SocketReader *m_imageInput; + float *output_buffer_; + DataType datatype_; + SocketReader *image_input_; - const ColorManagedViewSettings *m_viewSettings; - const ColorManagedDisplaySettings *m_displaySettings; + const ColorManagedViewSettings *view_settings_; + const ColorManagedDisplaySettings *display_settings_; - const char *m_viewName; - bool m_saveAsRender; + const char *view_name_; + bool save_as_render_; public: OutputSingleLayerOperation(const RenderData *rd, @@ -54,19 +54,19 @@ class OutputSingleLayerOperation : public MultiThreadedOperation { DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const char *viewName, - const bool saveAsRender); + const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const char *view_name, + const bool save_as_render); - void executeRegion(rcti *rect, unsigned int tileNumber) override; - bool isOutputOperation(bool /*rendering*/) const override + void execute_region(rcti *rect, unsigned int tile_number) override; + bool is_output_operation(bool /*rendering*/) const override { return true; } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override { return eCompositorPriority::Low; } @@ -85,24 +85,24 @@ struct OutputOpenExrLayer { bool use_layer; /* internals */ - float *outputBuffer; - SocketReader *imageInput; + float *output_buffer; + SocketReader *image_input; }; /* Writes inputs into OpenEXR multilayer channels. */ class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { protected: - const Scene *m_scene; - const RenderData *m_rd; - const bNodeTree *m_tree; + const Scene *scene_; + const RenderData *rd_; + const bNodeTree *tree_; - char m_path[FILE_MAX]; - char m_exr_codec; - bool m_exr_half_float; - Vector m_layers; - const char *m_viewName; + char path_[FILE_MAX]; + char exr_codec_; + bool exr_half_float_; + Vector layers_; + const char *view_name_; - StampData *createStampData() const; + StampData *create_stamp_data() const; public: OutputOpenExrMultiLayerOperation(const Scene *scene, @@ -111,18 +111,18 @@ class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { const char *path, char exr_codec, bool exr_half_float, - const char *viewName); + const char *view_name); void add_layer(const char *name, DataType datatype, bool use_layer); - void executeRegion(rcti *rect, unsigned int tileNumber) override; - bool isOutputOperation(bool /*rendering*/) const override + void execute_region(rcti *rect, unsigned int tile_number) override; + bool is_output_operation(bool /*rendering*/) const override { return true; } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override { return eCompositorPriority::Low; } @@ -133,15 +133,15 @@ class OutputOpenExrMultiLayerOperation : public MultiThreadedOperation { }; void add_exr_channels(void *exrhandle, - const char *layerName, + const char *layer_name, const DataType datatype, - const char *viewName, + const char *view_name, const size_t width, bool use_half_float, float *buf); void free_exr_channels(void *exrhandle, const RenderData *rd, - const char *layerName, + const char *layer_name, const DataType datatype); int get_datatype_size(DataType datatype); diff --git a/source/blender/compositor/operations/COM_PixelateOperation.cc b/source/blender/compositor/operations/COM_PixelateOperation.cc index 94827cd1b02..869e2357126 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.cc +++ b/source/blender/compositor/operations/COM_PixelateOperation.cc @@ -20,32 +20,32 @@ namespace blender::compositor { -PixelateOperation::PixelateOperation(DataType datatype) +PixelateOperation::PixelateOperation(DataType data_type) { - this->addInputSocket(datatype); - this->addOutputSocket(datatype); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; + this->add_input_socket(data_type); + this->add_output_socket(data_type); + this->set_canvas_input_index(0); + input_operation_ = nullptr; } -void PixelateOperation::initExecution() +void PixelateOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void PixelateOperation::deinitExecution() +void PixelateOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } -void PixelateOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void PixelateOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float nx = round(x); float ny = round(y); - this->m_inputOperation->readSampled(output, nx, ny, sampler); + input_operation_->read_sampled(output, nx, ny, sampler); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PixelateOperation.h b/source/blender/compositor/operations/COM_PixelateOperation.h index e8b272853da..51a480b98e9 100644 --- a/source/blender/compositor/operations/COM_PixelateOperation.h +++ b/source/blender/compositor/operations/COM_PixelateOperation.h @@ -34,33 +34,33 @@ class PixelateOperation : public NodeOperation { /** * \brief cached reference to the input operation */ - SocketReader *m_inputOperation; + SocketReader *input_operation_; public: /** * \brief PixelateOperation - * \param dataType: the datatype to create this operator for (saves datatype conversions) + * \param data_type: the datatype to create this operator for (saves datatype conversions) */ - PixelateOperation(DataType dataType); + PixelateOperation(DataType data_type); /** * \brief initialization of the execution */ - void initExecution() override; + void init_execution() override; /** * \brief de-initialization of the execution */ - void deinitExecution() override; + void deinit_execution() override; /** - * \brief executePixel + * \brief execute_pixel * \param output: result * \param x: x-coordinate * \param y: y-coordinate * \param sampler: sampler */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc index d2a06ddd7c4..8467a5d9d97 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.cc @@ -17,15 +17,6 @@ #include "COM_PlaneCornerPinOperation.h" #include "COM_ConstantOperation.h" -#include "COM_ReadBufferOperation.h" - -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" - -#include "BKE_node.h" namespace blender::compositor { @@ -65,11 +56,11 @@ static bool check_corners(float corners[4][2]) } /* TODO(manzanilla): to be removed with tiled implementation. */ -static void readCornersFromSockets(rcti *rect, SocketReader *readers[4], float corners[4][2]) +static void read_corners_from_sockets(rcti *rect, SocketReader *readers[4], float corners[4][2]) { for (int i = 0; i < 4; i++) { float result[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - readers[i]->readSampled(result, rect->xmin, rect->ymin, PixelSampler::Nearest); + readers[i]->read_sampled(result, rect->xmin, rect->ymin, PixelSampler::Nearest); corners[i][0] = result[0]; corners[i][1] = result[1]; } @@ -143,18 +134,18 @@ static void read_input_corners(NodeOperation *op, const int first_input_idx, flo /* ******** PlaneCornerPinMaskOperation ******** */ -PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : m_corners_ready(false) +PlaneCornerPinMaskOperation::PlaneCornerPinMaskOperation() : corners_ready_(false) { - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); /* XXX this is stupid: we need to make this "complex", - * so we can use the initializeTileData function + * so we can use the initialize_tile_data function * to read corners from input sockets ... */ - flags.complex = true; + flags_.complex = true; } void PlaneCornerPinMaskOperation::init_data() @@ -162,62 +153,60 @@ void PlaneCornerPinMaskOperation::init_data() if (execution_model_ == eExecutionModel::FullFrame) { float corners[4][2]; read_input_corners(this, 0, corners); - calculateCorners(corners, true, 0); + calculate_corners(corners, true, 0); } } -/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinitExecution and do the +/* TODO(manzanilla): to be removed with tiled implementation. Same for #deinit_execution and do the * same on #PlaneCornerPinWarpImageOperation. */ -void PlaneCornerPinMaskOperation::initExecution() +void PlaneCornerPinMaskOperation::init_execution() { - PlaneDistortMaskOperation::initExecution(); + PlaneDistortMaskOperation::init_execution(); - initMutex(); + init_mutex(); } -void PlaneCornerPinMaskOperation::deinitExecution() +void PlaneCornerPinMaskOperation::deinit_execution() { - PlaneDistortMaskOperation::deinitExecution(); + PlaneDistortMaskOperation::deinit_execution(); - deinitMutex(); + deinit_mutex(); } -void *PlaneCornerPinMaskOperation::initializeTileData(rcti *rect) +void *PlaneCornerPinMaskOperation::initialize_tile_data(rcti *rect) { - void *data = PlaneDistortMaskOperation::initializeTileData(rect); + void *data = PlaneDistortMaskOperation::initialize_tile_data(rect); /* get corner values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), * we don't have a nice generic system for that yet */ - lockMutex(); - if (!m_corners_ready) { + lock_mutex(); + if (!corners_ready_) { SocketReader *readers[4] = { - getInputSocketReader(0), - getInputSocketReader(1), - getInputSocketReader(2), - getInputSocketReader(3), + get_input_socket_reader(0), + get_input_socket_reader(1), + get_input_socket_reader(2), + get_input_socket_reader(3), }; float corners[4][2]; - readCornersFromSockets(rect, readers, corners); - calculateCorners(corners, true, 0); + read_corners_from_sockets(rect, readers, corners); + calculate_corners(corners, true, 0); - m_corners_ready = true; + corners_ready_ = true; } - unlockMutex(); + unlock_mutex(); return data; } -void PlaneCornerPinMaskOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void PlaneCornerPinMaskOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { if (execution_model_ == eExecutionModel::FullFrame) { - /* Determine inputs resolution. */ - PlaneDistortMaskOperation::determineResolution(resolution, preferredResolution); + /* Determine input canvases. */ + PlaneDistortMaskOperation::determine_canvas(preferred_area, r_area); } - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -225,17 +214,17 @@ void PlaneCornerPinMaskOperation::get_area_of_interest(const int UNUSED(input_id rcti &r_input_area) { /* All corner inputs are used as constants. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; } /* ******** PlaneCornerPinWarpImageOperation ******** */ -PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : m_corners_ready(false) +PlaneCornerPinWarpImageOperation::PlaneCornerPinWarpImageOperation() : corners_ready_(false) { - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); - addInputSocket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); + add_input_socket(DataType::Vector); } void PlaneCornerPinWarpImageOperation::init_data() @@ -243,57 +232,58 @@ void PlaneCornerPinWarpImageOperation::init_data() if (execution_model_ == eExecutionModel::FullFrame) { float corners[4][2]; read_input_corners(this, 1, corners); - calculateCorners(corners, true, 0); + calculate_corners(corners, true, 0); } } -void PlaneCornerPinWarpImageOperation::initExecution() +void PlaneCornerPinWarpImageOperation::init_execution() { - PlaneDistortWarpImageOperation::initExecution(); + PlaneDistortWarpImageOperation::init_execution(); - initMutex(); + init_mutex(); } -void PlaneCornerPinWarpImageOperation::deinitExecution() +void PlaneCornerPinWarpImageOperation::deinit_execution() { - PlaneDistortWarpImageOperation::deinitExecution(); + PlaneDistortWarpImageOperation::deinit_execution(); - deinitMutex(); + deinit_mutex(); } -void *PlaneCornerPinWarpImageOperation::initializeTileData(rcti *rect) +void *PlaneCornerPinWarpImageOperation::initialize_tile_data(rcti *rect) { - void *data = PlaneDistortWarpImageOperation::initializeTileData(rect); + void *data = PlaneDistortWarpImageOperation::initialize_tile_data(rect); /* get corner values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), * we don't have a nice generic system for that yet */ - lockMutex(); - if (!m_corners_ready) { + lock_mutex(); + if (!corners_ready_) { /* corner sockets start at index 1 */ SocketReader *readers[4] = { - getInputSocketReader(1), - getInputSocketReader(2), - getInputSocketReader(3), - getInputSocketReader(4), + get_input_socket_reader(1), + get_input_socket_reader(2), + get_input_socket_reader(3), + get_input_socket_reader(4), }; float corners[4][2]; - readCornersFromSockets(rect, readers, corners); - calculateCorners(corners, true, 0); + read_corners_from_sockets(rect, readers, corners); + calculate_corners(corners, true, 0); - m_corners_ready = true; + corners_ready_ = true; } - unlockMutex(); + unlock_mutex(); return data; } -bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool PlaneCornerPinWarpImageOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { for (int i = 0; i < 4; i++) { - if (getInputOperation(i + 1)->determineDependingAreaOfInterest(input, readOperation, output)) { + if (get_input_operation(i + 1)->determine_depending_area_of_interest( + input, read_operation, output)) { return true; } } @@ -304,12 +294,12 @@ bool PlaneCornerPinWarpImageOperation::determineDependingAreaOfInterest( */ output->xmin = 0; output->ymin = 0; - output->xmax = getInputOperation(0)->getWidth(); - output->ymax = getInputOperation(0)->getHeight(); + output->xmax = get_input_operation(0)->get_width(); + output->ymax = get_input_operation(0)->get_height(); return true; #if 0 - return PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( - input, readOperation, output); + return PlaneDistortWarpImageOperation::determine_depending_area_of_interest( + input, read_operation, output); #endif } @@ -322,7 +312,7 @@ void PlaneCornerPinWarpImageOperation::get_area_of_interest(const int input_idx, } else { /* Corner inputs are used as constants. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; } } diff --git a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h index 2831e937147..bb2166bf8b8 100644 --- a/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h +++ b/source/blender/compositor/operations/COM_PlaneCornerPinOperation.h @@ -32,39 +32,38 @@ namespace blender::compositor { class PlaneCornerPinMaskOperation : public PlaneDistortMaskOperation { private: /* TODO(manzanilla): to be removed with tiled implementation. */ - bool m_corners_ready; + bool corners_ready_; public: PlaneCornerPinMaskOperation(); void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; class PlaneCornerPinWarpImageOperation : public PlaneDistortWarpImageOperation { private: - bool m_corners_ready; + bool corners_ready_; public: PlaneCornerPinWarpImageOperation(); void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; }; diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc index ccabb3cf11c..a11a358c71d 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.cc @@ -18,47 +18,40 @@ #include "COM_PlaneDistortCommonOperation.h" -#include "MEM_guardedalloc.h" - #include "BLI_jitter_2d.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" -#include "BKE_movieclip.h" -#include "BKE_node.h" #include "BKE_tracking.h" namespace blender::compositor { PlaneDistortBaseOperation::PlaneDistortBaseOperation() - : m_motion_blur_samples(1), m_motion_blur_shutter(0.5f) + : motion_blur_samples_(1), motion_blur_shutter_(0.5f) { } -void PlaneDistortBaseOperation::calculateCorners(const float corners[4][2], - bool normalized, - int sample) +void PlaneDistortBaseOperation::calculate_corners(const float corners[4][2], + bool normalized, + int sample) { - BLI_assert(sample < this->m_motion_blur_samples); - MotionSample *sample_data = &this->m_samples[sample]; + BLI_assert(sample < motion_blur_samples_); + MotionSample *sample_data = &samples_[sample]; if (normalized) { for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0] * this->getWidth(); - sample_data->frameSpaceCorners[i][1] = corners[i][1] * this->getHeight(); + sample_data->frame_space_corners[i][0] = corners[i][0] * this->get_width(); + sample_data->frame_space_corners[i][1] = corners[i][1] * this->get_height(); } } else { for (int i = 0; i < 4; i++) { - sample_data->frameSpaceCorners[i][0] = corners[i][0]; - sample_data->frameSpaceCorners[i][1] = corners[i][1]; + sample_data->frame_space_corners[i][0] = corners[i][0]; + sample_data->frame_space_corners[i][1] = corners[i][1]; } } } /* ******** PlaneDistort WarpImage ******** */ -BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2]) +BLI_INLINE void warp_coord(float x, float y, float matrix[3][3], float uv[2], float deriv[2][2]) { float vec[3] = {x, y, 1.0f}; mul_m3_v3(matrix, vec); @@ -73,58 +66,58 @@ BLI_INLINE void warpCoord(float x, float y, float matrix[3][3], float uv[2], flo PlaneDistortWarpImageOperation::PlaneDistortWarpImageOperation() : PlaneDistortBaseOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addOutputSocket(DataType::Color); - this->m_pixelReader = nullptr; - this->flags.complex = true; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Color); + pixel_reader_ = nullptr; + flags_.complex = true; } -void PlaneDistortWarpImageOperation::calculateCorners(const float corners[4][2], - bool normalized, - int sample) +void PlaneDistortWarpImageOperation::calculate_corners(const float corners[4][2], + bool normalized, + int sample) { - PlaneDistortBaseOperation::calculateCorners(corners, normalized, sample); + PlaneDistortBaseOperation::calculate_corners(corners, normalized, sample); const NodeOperation *image = get_input_operation(0); - const int width = image->getWidth(); - const int height = image->getHeight(); + const int width = image->get_width(); + const int height = image->get_height(); float frame_corners[4][2] = { {0.0f, 0.0f}, {(float)width, 0.0f}, {(float)width, (float)height}, {0.0f, (float)height}}; - MotionSample *sample_data = &this->m_samples[sample]; + MotionSample *sample_data = &samples_[sample]; BKE_tracking_homography_between_two_quads( - sample_data->frameSpaceCorners, frame_corners, sample_data->perspectiveMatrix); + sample_data->frame_space_corners, frame_corners, sample_data->perspective_matrix); } -void PlaneDistortWarpImageOperation::initExecution() +void PlaneDistortWarpImageOperation::init_execution() { - this->m_pixelReader = this->getInputSocketReader(0); + pixel_reader_ = this->get_input_socket_reader(0); } -void PlaneDistortWarpImageOperation::deinitExecution() +void PlaneDistortWarpImageOperation::deinit_execution() { - this->m_pixelReader = nullptr; + pixel_reader_ = nullptr; } -void PlaneDistortWarpImageOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void PlaneDistortWarpImageOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float uv[2]; float deriv[2][2]; - if (this->m_motion_blur_samples == 1) { - warpCoord(x, y, this->m_samples[0].perspectiveMatrix, uv, deriv); - m_pixelReader->readFiltered(output, uv[0], uv[1], deriv[0], deriv[1]); + if (motion_blur_samples_ == 1) { + warp_coord(x, y, samples_[0].perspective_matrix, uv, deriv); + pixel_reader_->read_filtered(output, uv[0], uv[1], deriv[0], deriv[1]); } else { zero_v4(output); - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + for (int sample = 0; sample < motion_blur_samples_; sample++) { float color[4]; - warpCoord(x, y, this->m_samples[sample].perspectiveMatrix, uv, deriv); - m_pixelReader->readFiltered(color, uv[0], uv[1], deriv[0], deriv[1]); + warp_coord(x, y, samples_[sample].perspective_matrix, uv, deriv); + pixel_reader_->read_filtered(color, uv[0], uv[1], deriv[0], deriv[1]); add_v4_v4(output, color); } - mul_v4_fl(output, 1.0f / (float)this->m_motion_blur_samples); + mul_v4_fl(output, 1.0f / (float)motion_blur_samples_); } } @@ -136,54 +129,54 @@ void PlaneDistortWarpImageOperation::update_memory_buffer_partial(MemoryBuffer * float uv[2]; float deriv[2][2]; BuffersIterator it = output->iterate_with({}, area); - if (this->m_motion_blur_samples == 1) { + if (motion_blur_samples_ == 1) { for (; !it.is_end(); ++it) { - warpCoord(it.x, it.y, this->m_samples[0].perspectiveMatrix, uv, deriv); + warp_coord(it.x, it.y, samples_[0].perspective_matrix, uv, deriv); input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], it.out); } } else { for (; !it.is_end(); ++it) { zero_v4(it.out); - for (const int sample : IndexRange(this->m_motion_blur_samples)) { + for (const int sample : IndexRange(motion_blur_samples_)) { float color[4]; - warpCoord(it.x, it.y, this->m_samples[sample].perspectiveMatrix, uv, deriv); + warp_coord(it.x, it.y, samples_[sample].perspective_matrix, uv, deriv); input_img->read_elem_filtered(uv[0], uv[1], deriv[0], deriv[1], color); add_v4_v4(it.out, color); } - mul_v4_fl(it.out, 1.0f / (float)this->m_motion_blur_samples); + mul_v4_fl(it.out, 1.0f / (float)motion_blur_samples_); } } } -bool PlaneDistortWarpImageOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool PlaneDistortWarpImageOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { float min[2], max[2]; INIT_MINMAX2(min, max); - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + for (int sample = 0; sample < motion_blur_samples_; sample++) { float UVs[4][2]; float deriv[2][2]; - MotionSample *sample_data = &this->m_samples[sample]; + MotionSample *sample_data = &samples_[sample]; /* TODO(sergey): figure out proper way to do this. */ - warpCoord(input->xmin - 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); - warpCoord(input->xmax + 2, input->ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); - warpCoord(input->xmax + 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv); - warpCoord(input->xmin - 2, input->ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv); + warp_coord(input->xmin - 2, input->ymin - 2, sample_data->perspective_matrix, UVs[0], deriv); + warp_coord(input->xmax + 2, input->ymin - 2, sample_data->perspective_matrix, UVs[1], deriv); + warp_coord(input->xmax + 2, input->ymax + 2, sample_data->perspective_matrix, UVs[2], deriv); + warp_coord(input->xmin - 2, input->ymax + 2, sample_data->perspective_matrix, UVs[3], deriv); for (int i = 0; i < 4; i++) { minmax_v2v2_v2(min, max, UVs[i]); } } - rcti newInput; + rcti new_input; - newInput.xmin = min[0] - 1; - newInput.ymin = min[1] - 1; - newInput.xmax = max[0] + 1; - newInput.ymax = max[1] + 1; + new_input.xmin = min[0] - 1; + new_input.ymin = min[1] - 1; + new_input.xmax = max[0] + 1; + new_input.ymax = max[1] + 1; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, @@ -196,29 +189,26 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, } /* TODO: figure out the area needed for warping and EWA filtering. */ - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = get_input_operation(0)->getWidth(); - r_input_area.ymax = get_input_operation(0)->getHeight(); + r_input_area = get_input_operation(0)->get_canvas(); /* Old implementation but resulting coordinates are way out of input operation bounds and in some * cases the area result may incorrectly cause cropping. */ #if 0 float min[2], max[2]; INIT_MINMAX2(min, max); - for (int sample = 0; sample < this->m_motion_blur_samples; sample++) { + for (int sample = 0; sample < motion_blur_samples_; sample++) { float UVs[4][2]; float deriv[2][2]; - MotionSample *sample_data = &this->m_samples[sample]; + MotionSample *sample_data = &samples_[sample]; /* TODO(sergey): figure out proper way to do this. */ - warpCoord( - output_area.xmin - 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[0], deriv); - warpCoord( - output_area.xmax + 2, output_area.ymin - 2, sample_data->perspectiveMatrix, UVs[1], deriv); - warpCoord( - output_area.xmax + 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[2], deriv); - warpCoord( - output_area.xmin - 2, output_area.ymax + 2, sample_data->perspectiveMatrix, UVs[3], deriv); + warp_coord( + output_area.xmin - 2, output_area.ymin - 2, sample_data->perspective_matrix, UVs[0], deriv); + warp_coord( + output_area.xmax + 2, output_area.ymin - 2, sample_data->perspective_matrix, UVs[1], deriv); + warp_coord( + output_area.xmax + 2, output_area.ymax + 2, sample_data->perspective_matrix, UVs[2], deriv); + warp_coord( + output_area.xmin - 2, output_area.ymax + 2, sample_data->perspective_matrix, UVs[3], deriv); for (int i = 0; i < 4; i++) { minmax_v2v2_v2(min, max, UVs[i]); } @@ -235,61 +225,61 @@ void PlaneDistortWarpImageOperation::get_area_of_interest(const int input_idx, PlaneDistortMaskOperation::PlaneDistortMaskOperation() : PlaneDistortBaseOperation() { - addOutputSocket(DataType::Value); + add_output_socket(DataType::Value); /* Currently hardcoded to 8 samples. */ - m_osa = 8; + osa_ = 8; } -void PlaneDistortMaskOperation::initExecution() +void PlaneDistortMaskOperation::init_execution() { - BLI_jitter_init(m_jitter, m_osa); + BLI_jitter_init(jitter_, osa_); } -void PlaneDistortMaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void PlaneDistortMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { float point[2]; int inside_counter = 0; - if (this->m_motion_blur_samples == 1) { - MotionSample *sample_data = &this->m_samples[0]; - for (int sample = 0; sample < this->m_osa; sample++) { - point[0] = x + this->m_jitter[sample][0]; - point[1] = y + this->m_jitter[sample][1]; + if (motion_blur_samples_ == 1) { + MotionSample *sample_data = &samples_[0]; + for (int sample = 0; sample < osa_; sample++) { + point[0] = x + jitter_[sample][0]; + point[1] = y + jitter_[sample][1]; if (isect_point_tri_v2(point, - sample_data->frameSpaceCorners[0], - sample_data->frameSpaceCorners[1], - sample_data->frameSpaceCorners[2]) || + sample_data->frame_space_corners[0], + sample_data->frame_space_corners[1], + sample_data->frame_space_corners[2]) || isect_point_tri_v2(point, - sample_data->frameSpaceCorners[0], - sample_data->frameSpaceCorners[2], - sample_data->frameSpaceCorners[3])) { + sample_data->frame_space_corners[0], + sample_data->frame_space_corners[2], + sample_data->frame_space_corners[3])) { inside_counter++; } } - output[0] = (float)inside_counter / this->m_osa; + output[0] = (float)inside_counter / osa_; } else { - for (int motion_sample = 0; motion_sample < this->m_motion_blur_samples; motion_sample++) { - MotionSample *sample_data = &this->m_samples[motion_sample]; - for (int osa_sample = 0; osa_sample < this->m_osa; osa_sample++) { - point[0] = x + this->m_jitter[osa_sample][0]; - point[1] = y + this->m_jitter[osa_sample][1]; + for (int motion_sample = 0; motion_sample < motion_blur_samples_; motion_sample++) { + MotionSample *sample_data = &samples_[motion_sample]; + for (int osa_sample = 0; osa_sample < osa_; osa_sample++) { + point[0] = x + jitter_[osa_sample][0]; + point[1] = y + jitter_[osa_sample][1]; if (isect_point_tri_v2(point, - sample_data->frameSpaceCorners[0], - sample_data->frameSpaceCorners[1], - sample_data->frameSpaceCorners[2]) || + sample_data->frame_space_corners[0], + sample_data->frame_space_corners[1], + sample_data->frame_space_corners[2]) || isect_point_tri_v2(point, - sample_data->frameSpaceCorners[0], - sample_data->frameSpaceCorners[2], - sample_data->frameSpaceCorners[3])) { + sample_data->frame_space_corners[0], + sample_data->frame_space_corners[2], + sample_data->frame_space_corners[3])) { inside_counter++; } } } - output[0] = (float)inside_counter / (this->m_osa * this->m_motion_blur_samples); + output[0] = (float)inside_counter / (osa_ * motion_blur_samples_); } } @@ -299,11 +289,11 @@ void PlaneDistortMaskOperation::update_memory_buffer_partial(MemoryBuffer *outpu { for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { int inside_count = 0; - for (const int motion_sample : IndexRange(this->m_motion_blur_samples)) { - MotionSample &sample = this->m_samples[motion_sample]; + for (const int motion_sample : IndexRange(motion_blur_samples_)) { + MotionSample &sample = samples_[motion_sample]; inside_count += get_jitter_samples_inside_count(it.x, it.y, sample); } - *it.out = (float)inside_count / (this->m_osa * this->m_motion_blur_samples); + *it.out = (float)inside_count / (osa_ * motion_blur_samples_); } } @@ -313,17 +303,17 @@ int PlaneDistortMaskOperation::get_jitter_samples_inside_count(int x, { float point[2]; int inside_count = 0; - for (int sample = 0; sample < this->m_osa; sample++) { - point[0] = x + this->m_jitter[sample][0]; - point[1] = y + this->m_jitter[sample][1]; + for (int sample = 0; sample < osa_; sample++) { + point[0] = x + jitter_[sample][0]; + point[1] = y + jitter_[sample][1]; if (isect_point_tri_v2(point, - sample_data.frameSpaceCorners[0], - sample_data.frameSpaceCorners[1], - sample_data.frameSpaceCorners[2]) || + sample_data.frame_space_corners[0], + sample_data.frame_space_corners[1], + sample_data.frame_space_corners[2]) || isect_point_tri_v2(point, - sample_data.frameSpaceCorners[0], - sample_data.frameSpaceCorners[2], - sample_data.frameSpaceCorners[3])) { + sample_data.frame_space_corners[0], + sample_data.frame_space_corners[2], + sample_data.frame_space_corners[3])) { inside_count++; } } diff --git a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h index 3ef9c1dfab8..bf330ed7230 100644 --- a/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h +++ b/source/blender/compositor/operations/COM_PlaneDistortCommonOperation.h @@ -35,27 +35,27 @@ namespace blender::compositor { class PlaneDistortBaseOperation : public MultiThreadedOperation { protected: struct MotionSample { - float frameSpaceCorners[4][2]; /* Corners coordinates in pixel space. */ - float perspectiveMatrix[3][3]; + float frame_space_corners[4][2]; /* Corners coordinates in pixel space. */ + float perspective_matrix[3][3]; }; - MotionSample m_samples[PLANE_DISTORT_MAX_SAMPLES]; - int m_motion_blur_samples; - float m_motion_blur_shutter; + MotionSample samples_[PLANE_DISTORT_MAX_SAMPLES]; + int motion_blur_samples_; + float motion_blur_shutter_; public: PlaneDistortBaseOperation(); - void setMotionBlurSamples(int samples) + void set_motion_blur_samples(int samples) { BLI_assert(samples <= PLANE_DISTORT_MAX_SAMPLES); - this->m_motion_blur_samples = samples; + motion_blur_samples_ = samples; } - void setMotionBlurShutter(float shutter) + void set_motion_blur_shutter(float shutter) { - this->m_motion_blur_shutter = shutter; + motion_blur_shutter_ = shutter; } - virtual void calculateCorners(const float corners[4][2], bool normalized, int sample); + virtual void calculate_corners(const float corners[4][2], bool normalized, int sample); private: friend class PlaneTrackCommon; @@ -63,21 +63,21 @@ class PlaneDistortBaseOperation : public MultiThreadedOperation { class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { protected: - SocketReader *m_pixelReader; + SocketReader *pixel_reader_; public: PlaneDistortWarpImageOperation(); - void calculateCorners(const float corners[4][2], bool normalized, int sample) override; + void calculate_corners(const float corners[4][2], bool normalized, int sample) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, @@ -87,15 +87,15 @@ class PlaneDistortWarpImageOperation : public PlaneDistortBaseOperation { class PlaneDistortMaskOperation : public PlaneDistortBaseOperation { protected: - int m_osa; - float m_jitter[32][2]; + int osa_; + float jitter_[32][2]; public: PlaneDistortMaskOperation(); - void initExecution() override; + void init_execution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc index bf24f843ca2..28da200c615 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.cc +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.cc @@ -17,16 +17,8 @@ */ #include "COM_PlaneTrackOperation.h" -#include "COM_ReadBufferOperation.h" - -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" #include "BKE_movieclip.h" -#include "BKE_node.h" #include "BKE_tracking.h" namespace blender::compositor { @@ -35,67 +27,65 @@ namespace blender::compositor { PlaneTrackCommon::PlaneTrackCommon() { - this->m_movieClip = nullptr; - this->m_framenumber = 0; - this->m_trackingObjectName[0] = '\0'; - this->m_planeTrackName[0] = '\0'; + movie_clip_ = nullptr; + framenumber_ = 0; + tracking_object_name_[0] = '\0'; + plane_track_name_[0] = '\0'; } void PlaneTrackCommon::read_and_calculate_corners(PlaneDistortBaseOperation *distort_op) { float corners[4][2]; - if (distort_op->m_motion_blur_samples == 1) { - readCornersFromTrack(corners, this->m_framenumber); - distort_op->calculateCorners(corners, true, 0); + if (distort_op->motion_blur_samples_ == 1) { + read_corners_from_track(corners, framenumber_); + distort_op->calculate_corners(corners, true, 0); } else { - const float frame = (float)this->m_framenumber - distort_op->m_motion_blur_shutter; - const float frame_step = (distort_op->m_motion_blur_shutter * 2.0f) / - distort_op->m_motion_blur_samples; + const float frame = (float)framenumber_ - distort_op->motion_blur_shutter_; + const float frame_step = (distort_op->motion_blur_shutter_ * 2.0f) / + distort_op->motion_blur_samples_; float frame_iter = frame; - for (int sample = 0; sample < distort_op->m_motion_blur_samples; sample++) { - readCornersFromTrack(corners, frame_iter); - distort_op->calculateCorners(corners, true, sample); + for (int sample = 0; sample < distort_op->motion_blur_samples_; sample++) { + read_corners_from_track(corners, frame_iter); + distort_op->calculate_corners(corners, true, sample); frame_iter += frame_step; } } } -void PlaneTrackCommon::readCornersFromTrack(float corners[4][2], float frame) +void PlaneTrackCommon::read_corners_from_track(float corners[4][2], float frame) { MovieTracking *tracking; MovieTrackingObject *object; - if (!this->m_movieClip) { + if (!movie_clip_) { return; } - tracking = &this->m_movieClip->tracking; + tracking = &movie_clip_->tracking; - object = BKE_tracking_object_get_named(tracking, this->m_trackingObjectName); + object = BKE_tracking_object_get_named(tracking, tracking_object_name_); if (object) { MovieTrackingPlaneTrack *plane_track; - plane_track = BKE_tracking_plane_track_get_named(tracking, object, this->m_planeTrackName); + plane_track = BKE_tracking_plane_track_get_named(tracking, object, plane_track_name_); if (plane_track) { - float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, frame); + float clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, frame); BKE_tracking_plane_marker_get_subframe_corners(plane_track, clip_framenr, corners); } } } -void PlaneTrackCommon::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void PlaneTrackCommon::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = 0; - resolution[1] = 0; - - if (this->m_movieClip) { + r_area = COM_AREA_NONE; + if (movie_clip_) { int width, height; MovieClipUser user = {0}; - BKE_movieclip_user_set_frame(&user, this->m_framenumber); - BKE_movieclip_get_size(this->m_movieClip, &user, &width, &height); - resolution[0] = width; - resolution[1] = height; + BKE_movieclip_user_set_frame(&user, framenumber_); + BKE_movieclip_get_size(movie_clip_, &user, &width, &height); + r_area = preferred_area; + r_area.xmax = r_area.xmin + width; + r_area.ymax = r_area.ymin + height; } } @@ -110,9 +100,9 @@ void PlaneTrackMaskOperation::init_data() } /* TODO(manzanilla): to be removed with tiled implementation. */ -void PlaneTrackMaskOperation::initExecution() +void PlaneTrackMaskOperation::init_execution() { - PlaneDistortMaskOperation::initExecution(); + PlaneDistortMaskOperation::init_execution(); if (execution_model_ == eExecutionModel::Tiled) { PlaneTrackCommon::read_and_calculate_corners(this); } @@ -129,9 +119,9 @@ void PlaneTrackWarpImageOperation::init_data() } /* TODO(manzanilla): to be removed with tiled implementation. */ -void PlaneTrackWarpImageOperation::initExecution() +void PlaneTrackWarpImageOperation::init_execution() { - PlaneDistortWarpImageOperation::initExecution(); + PlaneDistortWarpImageOperation::init_execution(); if (execution_model_ == eExecutionModel::Tiled) { PlaneTrackCommon::read_and_calculate_corners(this); } diff --git a/source/blender/compositor/operations/COM_PlaneTrackOperation.h b/source/blender/compositor/operations/COM_PlaneTrackOperation.h index d2027755162..4c584ca43f4 100644 --- a/source/blender/compositor/operations/COM_PlaneTrackOperation.h +++ b/source/blender/compositor/operations/COM_PlaneTrackOperation.h @@ -32,39 +32,39 @@ namespace blender::compositor { class PlaneTrackCommon { protected: - MovieClip *m_movieClip; - int m_framenumber; - char m_trackingObjectName[64]; - char m_planeTrackName[64]; + MovieClip *movie_clip_; + int framenumber_; + char tracking_object_name_[64]; + char plane_track_name_[64]; /* NOTE: this class is not an operation itself (to prevent virtual inheritance issues) * implementation classes must make wrappers to use these methods, see below. */ void read_and_calculate_corners(PlaneDistortBaseOperation *distort_op); - void determineResolution(unsigned int resolution[2], unsigned int preferredResolution[2]); + void determine_canvas(const rcti &preferred_area, rcti &r_area); public: PlaneTrackCommon(); - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - this->m_movieClip = clip; + movie_clip_ = clip; } - void setTrackingObject(char *object) + void set_tracking_object(char *object) { - BLI_strncpy(this->m_trackingObjectName, object, sizeof(this->m_trackingObjectName)); + BLI_strncpy(tracking_object_name_, object, sizeof(tracking_object_name_)); } - void setPlaneTrackName(char *plane_track) + void set_plane_track_name(char *plane_track) { - BLI_strncpy(this->m_planeTrackName, plane_track, sizeof(this->m_planeTrackName)); + BLI_strncpy(plane_track_name_, plane_track, sizeof(plane_track_name_)); } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { - this->m_framenumber = framenumber; + framenumber_ = framenumber; } private: - void readCornersFromTrack(float corners[4][2], float frame); + void read_corners_from_track(float corners[4][2], float frame); }; class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTrackCommon { @@ -75,15 +75,15 @@ class PlaneTrackMaskOperation : public PlaneDistortMaskOperation, public PlaneTr void init_data() override; - void initExecution() override; + void init_execution() override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override + void determine_canvas(const rcti &preferred_area, rcti &r_area) override { - PlaneTrackCommon::determineResolution(resolution, preferredResolution); + PlaneTrackCommon::determine_canvas(preferred_area, r_area); - unsigned int temp[2]; - NodeOperation::determineResolution(temp, resolution); + rcti unused; + rcti &preferred = r_area; + NodeOperation::determine_canvas(preferred, unused); } }; @@ -96,14 +96,15 @@ class PlaneTrackWarpImageOperation : public PlaneDistortWarpImageOperation, void init_data() override; - void initExecution() override; + void init_execution() override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override + void determine_canvas(const rcti &preferred_area, rcti &r_area) override { - PlaneTrackCommon::determineResolution(resolution, preferredResolution); - unsigned int temp[2]; - NodeOperation::determineResolution(temp, resolution); + PlaneTrackCommon::determine_canvas(preferred_area, r_area); + + rcti unused; + rcti &preferred = r_area; + NodeOperation::determine_canvas(preferred, unused); } }; diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.cc b/source/blender/compositor/operations/COM_PosterizeOperation.cc index db5860f48f8..585acb31e68 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.cc +++ b/source/blender/compositor/operations/COM_PosterizeOperation.cc @@ -22,37 +22,37 @@ namespace blender::compositor { PosterizeOperation::PosterizeOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->m_inputProgram = nullptr; - this->m_inputStepsProgram = nullptr; - flags.can_be_constant = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + input_program_ = nullptr; + input_steps_program_ = nullptr; + flags_.can_be_constant = true; } -void PosterizeOperation::initExecution() +void PosterizeOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->m_inputStepsProgram = this->getInputSocketReader(1); + input_program_ = this->get_input_socket_reader(0); + input_steps_program_ = this->get_input_socket_reader(1); } -void PosterizeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void PosterizeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float inputValue[4]; - float inputSteps[4]; + float input_value[4]; + float input_steps[4]; - this->m_inputProgram->readSampled(inputValue, x, y, sampler); - this->m_inputStepsProgram->readSampled(inputSteps, x, y, sampler); - CLAMP(inputSteps[0], 2.0f, 1024.0f); - const float steps_inv = 1.0f / inputSteps[0]; + input_program_->read_sampled(input_value, x, y, sampler); + input_steps_program_->read_sampled(input_steps, x, y, sampler); + CLAMP(input_steps[0], 2.0f, 1024.0f); + const float steps_inv = 1.0f / input_steps[0]; - output[0] = floor(inputValue[0] / steps_inv) * steps_inv; - output[1] = floor(inputValue[1] / steps_inv) * steps_inv; - output[2] = floor(inputValue[2] / steps_inv) * steps_inv; - output[3] = inputValue[3]; + output[0] = floor(input_value[0] / steps_inv) * steps_inv; + output[1] = floor(input_value[1] / steps_inv) * steps_inv; + output[2] = floor(input_value[2] / steps_inv) * steps_inv; + output[3] = input_value[3]; } void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -73,10 +73,10 @@ void PosterizeOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void PosterizeOperation::deinitExecution() +void PosterizeOperation::deinit_execution() { - this->m_inputProgram = nullptr; - this->m_inputStepsProgram = nullptr; + input_program_ = nullptr; + input_steps_program_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_PosterizeOperation.h b/source/blender/compositor/operations/COM_PosterizeOperation.h index c625cbb83c6..70e516bccf8 100644 --- a/source/blender/compositor/operations/COM_PosterizeOperation.h +++ b/source/blender/compositor/operations/COM_PosterizeOperation.h @@ -25,10 +25,10 @@ namespace blender::compositor { class PosterizeOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - SocketReader *m_inputStepsProgram; + SocketReader *input_program_; + SocketReader *input_steps_program_; public: PosterizeOperation(); @@ -36,17 +36,17 @@ class PosterizeOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_PreviewOperation.cc b/source/blender/compositor/operations/COM_PreviewOperation.cc index fa8b5ffcabf..a3fe0acebdf 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.cc +++ b/source/blender/compositor/operations/COM_PreviewOperation.cc @@ -17,127 +17,114 @@ */ #include "COM_PreviewOperation.h" -#include "BKE_image.h" -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" -#include "BLI_utildefines.h" -#include "COM_defines.h" -#include "MEM_guardedalloc.h" -#include "PIL_time.h" -#include "WM_api.h" -#include "WM_types.h" #include "BKE_node.h" #include "IMB_colormanagement.h" -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" namespace blender::compositor { -PreviewOperation::PreviewOperation(const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - const unsigned int defaultWidth, - const unsigned int defaultHeight) +PreviewOperation::PreviewOperation(const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + const unsigned int default_width, + const unsigned int default_height) { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->m_preview = nullptr; - this->m_outputBuffer = nullptr; - this->m_input = nullptr; - this->m_divider = 1.0f; - this->m_viewSettings = viewSettings; - this->m_displaySettings = displaySettings; - this->m_defaultWidth = defaultWidth; - this->m_defaultHeight = defaultHeight; - flags.use_viewer_border = true; - flags.is_preview_operation = true; + this->add_input_socket(DataType::Color, ResizeMode::Align); + preview_ = nullptr; + output_buffer_ = nullptr; + input_ = nullptr; + divider_ = 1.0f; + view_settings_ = view_settings; + display_settings_ = display_settings; + default_width_ = default_width; + default_height_ = default_height; + flags_.use_viewer_border = true; + flags_.is_preview_operation = true; } -void PreviewOperation::verifyPreview(bNodeInstanceHash *previews, bNodeInstanceKey key) +void PreviewOperation::verify_preview(bNodeInstanceHash *previews, bNodeInstanceKey key) { /* Size (0, 0) ensures the preview rect is not allocated in advance, - * this is set later in initExecution once the resolution is determined. + * this is set later in init_execution once the resolution is determined. */ - this->m_preview = BKE_node_preview_verify(previews, key, 0, 0, true); + preview_ = BKE_node_preview_verify(previews, key, 0, 0, true); } -void PreviewOperation::initExecution() +void PreviewOperation::init_execution() { - this->m_input = getInputSocketReader(0); + input_ = get_input_socket_reader(0); - if (this->getWidth() == (unsigned int)this->m_preview->xsize && - this->getHeight() == (unsigned int)this->m_preview->ysize) { - this->m_outputBuffer = this->m_preview->rect; + if (this->get_width() == (unsigned int)preview_->xsize && + this->get_height() == (unsigned int)preview_->ysize) { + output_buffer_ = preview_->rect; } - if (this->m_outputBuffer == nullptr) { - this->m_outputBuffer = (unsigned char *)MEM_callocN( - sizeof(unsigned char) * 4 * getWidth() * getHeight(), "PreviewOperation"); - if (this->m_preview->rect) { - MEM_freeN(this->m_preview->rect); + if (output_buffer_ == nullptr) { + output_buffer_ = (unsigned char *)MEM_callocN( + sizeof(unsigned char) * 4 * get_width() * get_height(), "PreviewOperation"); + if (preview_->rect) { + MEM_freeN(preview_->rect); } - this->m_preview->xsize = getWidth(); - this->m_preview->ysize = getHeight(); - this->m_preview->rect = this->m_outputBuffer; + preview_->xsize = get_width(); + preview_->ysize = get_height(); + preview_->rect = output_buffer_; } } -void PreviewOperation::deinitExecution() +void PreviewOperation::deinit_execution() { - this->m_outputBuffer = nullptr; - this->m_input = nullptr; + output_buffer_ = nullptr; + input_ = nullptr; } -void PreviewOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void PreviewOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { int offset; float color[4]; struct ColormanageProcessor *cm_processor; - cm_processor = IMB_colormanagement_display_processor_new(this->m_viewSettings, - this->m_displaySettings); + cm_processor = IMB_colormanagement_display_processor_new(view_settings_, display_settings_); for (int y = rect->ymin; y < rect->ymax; y++) { - offset = (y * getWidth() + rect->xmin) * 4; + offset = (y * get_width() + rect->xmin) * 4; for (int x = rect->xmin; x < rect->xmax; x++) { - float rx = floor(x / this->m_divider); - float ry = floor(y / this->m_divider); + float rx = floor(x / divider_); + float ry = floor(y / divider_); color[0] = 0.0f; color[1] = 0.0f; color[2] = 0.0f; color[3] = 1.0f; - this->m_input->readSampled(color, rx, ry, PixelSampler::Nearest); + input_->read_sampled(color, rx, ry, PixelSampler::Nearest); IMB_colormanagement_processor_apply_v4(cm_processor, color); - rgba_float_to_uchar(this->m_outputBuffer + offset, color); + rgba_float_to_uchar(output_buffer_ + offset, color); offset += 4; } } IMB_colormanagement_processor_free(cm_processor); } -bool PreviewOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool PreviewOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmin = input->xmin / this->m_divider; - newInput.xmax = input->xmax / this->m_divider; - newInput.ymin = input->ymin / this->m_divider; - newInput.ymax = input->ymax / this->m_divider; + new_input.xmin = input->xmin / divider_; + new_input.xmax = input->xmax / divider_; + new_input.ymin = input->ymin / divider_; + new_input.ymax = input->ymax / divider_; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } -void PreviewOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void PreviewOperation::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { /* Use default preview resolution as preferred ensuring it has size so that * generated inputs (which don't have resolution on their own) are displayed */ - BLI_assert(this->m_defaultWidth > 0 && this->m_defaultHeight > 0); - unsigned int previewPreferredRes[2] = {this->m_defaultWidth, this->m_defaultHeight}; - NodeOperation::determineResolution(resolution, previewPreferredRes); + BLI_assert(default_width_ > 0 && default_height_ > 0); + rcti local_preferred; + BLI_rcti_init(&local_preferred, 0, default_width_, 0, default_height_); + NodeOperation::determine_canvas(local_preferred, r_area); /* If resolution is 0 there are two possible scenarios: * - Either node is not connected at all @@ -148,25 +135,24 @@ void PreviewOperation::determineResolution(unsigned int resolution[2], * The latter case would only happen if an input doesn't set any resolution ignoring output * preferred resolution. In such case preview size will be 0 too. */ - int width = resolution[0]; - int height = resolution[1]; - this->m_divider = 0.0f; + int width = BLI_rcti_size_x(&r_area); + int height = BLI_rcti_size_y(&r_area); + divider_ = 0.0f; if (width > 0 && height > 0) { if (width > height) { - this->m_divider = (float)COM_PREVIEW_SIZE / (width); + divider_ = (float)COM_PREVIEW_SIZE / (width); } else { - this->m_divider = (float)COM_PREVIEW_SIZE / (height); + divider_ = (float)COM_PREVIEW_SIZE / (height); } } - width = width * this->m_divider; - height = height * this->m_divider; + width = width * divider_; + height = height * divider_; - resolution[0] = width; - resolution[1] = height; + BLI_rcti_init(&r_area, r_area.xmin, r_area.xmin + width, r_area.ymin, r_area.ymin + height); } -eCompositorPriority PreviewOperation::getRenderPriority() const +eCompositorPriority PreviewOperation::get_render_priority() const { return eCompositorPriority::Low; } @@ -178,10 +164,10 @@ void PreviewOperation::get_area_of_interest(const int input_idx, BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmin = output_area.xmin / m_divider; - r_input_area.xmax = output_area.xmax / m_divider; - r_input_area.ymin = output_area.ymin / m_divider; - r_input_area.ymax = output_area.ymax / m_divider; + r_input_area.xmin = output_area.xmin / divider_; + r_input_area.xmax = output_area.xmax / divider_; + r_input_area.ymin = output_area.ymin / divider_; + r_input_area.ymax = output_area.ymax / divider_; } void PreviewOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), @@ -190,16 +176,16 @@ void PreviewOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output) { MemoryBuffer *input = inputs[0]; struct ColormanageProcessor *cm_processor = IMB_colormanagement_display_processor_new( - m_viewSettings, m_displaySettings); + view_settings_, display_settings_); rcti buffer_area; - BLI_rcti_init(&buffer_area, 0, this->getWidth(), 0, this->getHeight()); + BLI_rcti_init(&buffer_area, 0, this->get_width(), 0, this->get_height()); BuffersIteratorBuilder it_builder( - m_outputBuffer, buffer_area, area, COM_data_type_num_channels(DataType::Color)); + output_buffer_, buffer_area, area, COM_data_type_num_channels(DataType::Color)); for (BuffersIterator it = it_builder.build(); !it.is_end(); ++it) { - const float rx = it.x / m_divider; - const float ry = it.y / m_divider; + const float rx = it.x / divider_; + const float ry = it.y / divider_; float color[4]; input->read_elem_checked(rx, ry, color); diff --git a/source/blender/compositor/operations/COM_PreviewOperation.h b/source/blender/compositor/operations/COM_PreviewOperation.h index 05dae9c4dd8..97d1884c9a7 100644 --- a/source/blender/compositor/operations/COM_PreviewOperation.h +++ b/source/blender/compositor/operations/COM_PreviewOperation.h @@ -28,41 +28,40 @@ namespace blender::compositor { class PreviewOperation : public MultiThreadedOperation { protected: - unsigned char *m_outputBuffer; + unsigned char *output_buffer_; /** * \brief holds reference to the SDNA bNode, where this nodes will render the preview image for */ - bNodePreview *m_preview; - SocketReader *m_input; - float m_divider; - unsigned int m_defaultWidth; - unsigned int m_defaultHeight; + bNodePreview *preview_; + SocketReader *input_; + float divider_; + unsigned int default_width_; + unsigned int default_height_; - const ColorManagedViewSettings *m_viewSettings; - const ColorManagedDisplaySettings *m_displaySettings; + const ColorManagedViewSettings *view_settings_; + const ColorManagedDisplaySettings *display_settings_; public: - PreviewOperation(const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings, - unsigned int defaultWidth, - unsigned int defaultHeight); - void verifyPreview(bNodeInstanceHash *previews, bNodeInstanceKey key); + PreviewOperation(const ColorManagedViewSettings *view_settings, + const ColorManagedDisplaySettings *display_settings, + unsigned int default_width, + unsigned int default_height); + void verify_preview(bNodeInstanceHash *previews, bNodeInstanceKey key); - bool isOutputOperation(bool /*rendering*/) const override + bool is_output_operation(bool /*rendering*/) const override { return !G.background; } - void initExecution() override; - void deinitExecution() override; - eCompositorPriority getRenderPriority() const override; + void init_execution() override; + void deinit_execution() override; + eCompositorPriority get_render_priority() const override; - void executeRegion(rcti *rect, unsigned int tileNumber) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + void execute_region(rcti *rect, unsigned int tile_number) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc index fcab5dd5751..4813af511f7 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.cc @@ -19,20 +19,17 @@ #include "COM_ProjectorLensDistortionOperation.h" #include "COM_ConstantOperation.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" - namespace blender::compositor { ProjectorLensDistortionOperation::ProjectorLensDistortionOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->flags.complex = true; - this->m_inputProgram = nullptr; - this->m_dispersionAvailable = false; - this->m_dispersion = 0.0f; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + flags_.complex = true; + input_program_ = nullptr; + dispersion_available_ = false; + dispersion_ = 0.0f; } void ProjectorLensDistortionOperation::init_data() @@ -40,95 +37,111 @@ void ProjectorLensDistortionOperation::init_data() if (execution_model_ == eExecutionModel::FullFrame) { NodeOperation *dispersion_input = get_input_operation(1); if (dispersion_input->get_flags().is_constant_operation) { - this->m_dispersion = - static_cast(dispersion_input)->get_constant_elem()[0]; + dispersion_ = static_cast(dispersion_input)->get_constant_elem()[0]; } - this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f); - this->m_kr2 = this->m_kr * 20; + kr_ = 0.25f * max_ff(min_ff(dispersion_, 1.0f), 0.0f); + kr2_ = kr_ * 20; } } -void ProjectorLensDistortionOperation::initExecution() +void ProjectorLensDistortionOperation::init_execution() { - this->initMutex(); - this->m_inputProgram = this->getInputSocketReader(0); + this->init_mutex(); + input_program_ = this->get_input_socket_reader(0); } -void *ProjectorLensDistortionOperation::initializeTileData(rcti * /*rect*/) +void *ProjectorLensDistortionOperation::initialize_tile_data(rcti * /*rect*/) { - updateDispersion(); - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + update_dispersion(); + void *buffer = input_program_->initialize_tile_data(nullptr); return buffer; } -void ProjectorLensDistortionOperation::executePixel(float output[4], int x, int y, void *data) +void ProjectorLensDistortionOperation::execute_pixel(float output[4], int x, int y, void *data) { - float inputValue[4]; - const float height = this->getHeight(); - const float width = this->getWidth(); + float input_value[4]; + const float height = this->get_height(); + const float width = this->get_width(); const float v = (y + 0.5f) / height; const float u = (x + 0.5f) / width; - MemoryBuffer *inputBuffer = (MemoryBuffer *)data; - inputBuffer->readBilinear(inputValue, (u * width + this->m_kr2) - 0.5f, v * height - 0.5f); - output[0] = inputValue[0]; - inputBuffer->read(inputValue, x, y); - output[1] = inputValue[1]; - inputBuffer->readBilinear(inputValue, (u * width - this->m_kr2) - 0.5f, v * height - 0.5f); - output[2] = inputValue[2]; + MemoryBuffer *input_buffer = (MemoryBuffer *)data; + input_buffer->read_bilinear(input_value, (u * width + kr2_) - 0.5f, v * height - 0.5f); + output[0] = input_value[0]; + input_buffer->read(input_value, x, y); + output[1] = input_value[1]; + input_buffer->read_bilinear(input_value, (u * width - kr2_) - 0.5f, v * height - 0.5f); + output[2] = input_value[2]; output[3] = 1.0f; } -void ProjectorLensDistortionOperation::deinitExecution() +void ProjectorLensDistortionOperation::deinit_execution() { - this->deinitMutex(); - this->m_inputProgram = nullptr; + this->deinit_mutex(); + input_program_ = nullptr; } -bool ProjectorLensDistortionOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool ProjectorLensDistortionOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - if (this->m_dispersionAvailable) { - newInput.ymax = input->ymax; - newInput.ymin = input->ymin; - newInput.xmin = input->xmin - this->m_kr2 - 2; - newInput.xmax = input->xmax + this->m_kr2 + 2; + rcti new_input; + if (dispersion_available_) { + new_input.ymax = input->ymax; + new_input.ymin = input->ymin; + new_input.xmin = input->xmin - kr2_ - 2; + new_input.xmax = input->xmax + kr2_ + 2; } else { - rcti dispInput; - BLI_rcti_init(&dispInput, 0, 5, 0, 5); - if (this->getInputOperation(1)->determineDependingAreaOfInterest( - &dispInput, readOperation, output)) { + rcti disp_input; + BLI_rcti_init(&disp_input, 0, 5, 0, 5); + if (this->get_input_operation(1)->determine_depending_area_of_interest( + &disp_input, read_operation, output)) { return true; } - newInput.xmin = input->xmin - 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ - newInput.ymin = input->ymin; - newInput.ymax = input->ymax; - newInput.xmax = input->xmax + 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ + new_input.xmin = input->xmin - 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ + new_input.ymin = input->ymin; + new_input.ymax = input->ymax; + new_input.xmax = input->xmax + 7; /* (0.25f * 20 * 1) + 2 == worse case dispersion */ } - if (this->getInputOperation(0)->determineDependingAreaOfInterest( - &newInput, readOperation, output)) { + if (this->get_input_operation(0)->determine_depending_area_of_interest( + &new_input, read_operation, output)) { return true; } return false; } /* TODO(manzanilla): to be removed with tiled implementation. */ -void ProjectorLensDistortionOperation::updateDispersion() +void ProjectorLensDistortionOperation::update_dispersion() { - if (this->m_dispersionAvailable) { + if (dispersion_available_) { return; } - this->lockMutex(); - if (!this->m_dispersionAvailable) { + this->lock_mutex(); + if (!dispersion_available_) { float result[4]; - this->getInputSocketReader(1)->readSampled(result, 1, 1, PixelSampler::Nearest); - this->m_dispersion = result[0]; - this->m_kr = 0.25f * max_ff(min_ff(this->m_dispersion, 1.0f), 0.0f); - this->m_kr2 = this->m_kr * 20; - this->m_dispersionAvailable = true; + this->get_input_socket_reader(1)->read_sampled(result, 1, 1, PixelSampler::Nearest); + dispersion_ = result[0]; + kr_ = 0.25f * max_ff(min_ff(dispersion_, 1.0f), 0.0f); + kr2_ = kr_ * 20; + dispersion_available_ = true; } - this->unlockMutex(); + this->unlock_mutex(); +} + +void ProjectorLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + switch (execution_model_) { + case eExecutionModel::FullFrame: { + set_determined_canvas_modifier([=](rcti &canvas) { + /* Ensure screen space. */ + BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin); + }); + break; + } + default: + break; + } + + NodeOperation::determine_canvas(preferred_area, r_area); } void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, @@ -137,14 +150,14 @@ void ProjectorLensDistortionOperation::get_area_of_interest(const int input_idx, { if (input_idx == 1) { /* Dispersion input is used as constant only. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; return; } r_input_area.ymax = output_area.ymax; r_input_area.ymin = output_area.ymin; - r_input_area.xmin = output_area.xmin - this->m_kr2 - 2; - r_input_area.xmax = output_area.xmax + this->m_kr2 + 2; + r_input_area.xmin = output_area.xmin - kr2_ - 2; + r_input_area.xmax = output_area.xmax + kr2_ + 2; } void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -152,17 +165,17 @@ void ProjectorLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer Span inputs) { const MemoryBuffer *input_image = inputs[0]; - const float height = this->getHeight(); - const float width = this->getWidth(); + const float height = this->get_height(); + const float width = this->get_width(); float color[4]; for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { const float v = (it.y + 0.5f) / height; const float u = (it.x + 0.5f) / width; - input_image->read_elem_bilinear((u * width + this->m_kr2) - 0.5f, v * height - 0.5f, color); + input_image->read_elem_bilinear((u * width + kr2_) - 0.5f, v * height - 0.5f, color); it.out[0] = color[0]; input_image->read_elem(it.x, it.y, color); it.out[1] = color[1]; - input_image->read_elem_bilinear((u * width - this->m_kr2) - 0.5f, v * height - 0.5f, color); + input_image->read_elem_bilinear((u * width - kr2_) - 0.5f, v * height - 0.5f, color); it.out[2] = color[2]; it.out[3] = 1.0f; } diff --git a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h index 7c7626bf271..ea90840e61c 100644 --- a/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ProjectorLensDistortionOperation.h @@ -26,15 +26,15 @@ namespace blender::compositor { class ProjectorLensDistortionOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; - float m_dispersion; + float dispersion_; /* TODO(manzanilla): to be removed with tiled implementation. */ - bool m_dispersionAvailable; + bool dispersion_available_; - float m_kr, m_kr2; + float kr_, kr2_; public: ProjectorLensDistortionOperation(); @@ -42,26 +42,27 @@ class ProjectorLensDistortionOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void init_data() override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void updateDispersion(); + void update_dispersion(); + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.cc b/source/blender/compositor/operations/COM_QualityStepHelper.cc index e347d278ce4..895e341d550 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.cc +++ b/source/blender/compositor/operations/COM_QualityStepHelper.cc @@ -22,45 +22,45 @@ namespace blender::compositor { QualityStepHelper::QualityStepHelper() { - this->m_quality = eCompositorQuality::High; - this->m_step = 1; - this->m_offsetadd = 4; + quality_ = eCompositorQuality::High; + step_ = 1; + offsetadd_ = 4; } -void QualityStepHelper::initExecution(QualityHelper helper) +void QualityStepHelper::init_execution(QualityHelper helper) { switch (helper) { case COM_QH_INCREASE: - switch (this->m_quality) { + switch (quality_) { case eCompositorQuality::High: default: - this->m_step = 1; - this->m_offsetadd = 1; + step_ = 1; + offsetadd_ = 1; break; case eCompositorQuality::Medium: - this->m_step = 2; - this->m_offsetadd = 2; + step_ = 2; + offsetadd_ = 2; break; case eCompositorQuality::Low: - this->m_step = 3; - this->m_offsetadd = 3; + step_ = 3; + offsetadd_ = 3; break; } break; case COM_QH_MULTIPLY: - switch (this->m_quality) { + switch (quality_) { case eCompositorQuality::High: default: - this->m_step = 1; - this->m_offsetadd = 4; + step_ = 1; + offsetadd_ = 4; break; case eCompositorQuality::Medium: - this->m_step = 2; - this->m_offsetadd = 8; + step_ = 2; + offsetadd_ = 8; break; case eCompositorQuality::Low: - this->m_step = 4; - this->m_offsetadd = 16; + step_ = 4; + offsetadd_ = 16; break; } break; diff --git a/source/blender/compositor/operations/COM_QualityStepHelper.h b/source/blender/compositor/operations/COM_QualityStepHelper.h index c5f16a58686..6f168babeae 100644 --- a/source/blender/compositor/operations/COM_QualityStepHelper.h +++ b/source/blender/compositor/operations/COM_QualityStepHelper.h @@ -29,31 +29,31 @@ typedef enum QualityHelper { class QualityStepHelper { private: - eCompositorQuality m_quality; - int m_step; - int m_offsetadd; + eCompositorQuality quality_; + int step_; + int offsetadd_; protected: /** * Initialize the execution */ - void initExecution(QualityHelper helper); + void init_execution(QualityHelper helper); - inline int getStep() const + inline int get_step() const { - return this->m_step; + return step_; } - inline int getOffsetAdd() const + inline int get_offset_add() const { - return this->m_offsetadd; + return offsetadd_; } public: QualityStepHelper(); - void setQuality(eCompositorQuality quality) + void set_quality(eCompositorQuality quality) { - this->m_quality = quality; + quality_ = quality; } }; diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.cc b/source/blender/compositor/operations/COM_ReadBufferOperation.cc index d35d2516f16..b0e0b2c219f 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.cc +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.cc @@ -17,122 +17,124 @@ */ #include "COM_ReadBufferOperation.h" + +#include "COM_ExecutionGroup.h" #include "COM_WriteBufferOperation.h" -#include "COM_defines.h" namespace blender::compositor { ReadBufferOperation::ReadBufferOperation(DataType datatype) { - this->addOutputSocket(datatype); - this->m_single_value = false; - this->m_offset = 0; - this->m_buffer = nullptr; - flags.is_read_buffer_operation = true; + this->add_output_socket(datatype); + single_value_ = false; + offset_ = 0; + buffer_ = nullptr; + flags_.is_read_buffer_operation = true; } -void *ReadBufferOperation::initializeTileData(rcti * /*rect*/) +void *ReadBufferOperation::initialize_tile_data(rcti * /*rect*/) { - return m_buffer; + return buffer_; } -void ReadBufferOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void ReadBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - if (this->m_memoryProxy != nullptr) { - WriteBufferOperation *operation = this->m_memoryProxy->getWriteBufferOperation(); - operation->determineResolution(resolution, preferredResolution); - operation->setResolution(resolution); + if (memory_proxy_ != nullptr) { + WriteBufferOperation *operation = memory_proxy_->get_write_buffer_operation(); + operation->determine_canvas(preferred_area, r_area); + operation->set_canvas(r_area); /** \todo may not occur! But does with blur node. */ - if (this->m_memoryProxy->getExecutor()) { - this->m_memoryProxy->getExecutor()->setResolution(resolution); + if (memory_proxy_->get_executor()) { + uint resolution[2] = {static_cast(BLI_rcti_size_x(&r_area)), + static_cast(BLI_rcti_size_y(&r_area))}; + memory_proxy_->get_executor()->set_resolution(resolution); } - m_single_value = operation->isSingleValue(); + single_value_ = operation->is_single_value(); } } -void ReadBufferOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ReadBufferOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - if (m_single_value) { + if (single_value_) { /* write buffer has a single value stored at (0,0) */ - m_buffer->read(output, 0, 0); + buffer_->read(output, 0, 0); } else { switch (sampler) { case PixelSampler::Nearest: - m_buffer->read(output, x, y); + buffer_->read(output, x, y); break; case PixelSampler::Bilinear: default: - m_buffer->readBilinear(output, x, y); + buffer_->read_bilinear(output, x, y); break; case PixelSampler::Bicubic: - m_buffer->readBilinear(output, x, y); + buffer_->read_bilinear(output, x, y); break; } } } -void ReadBufferOperation::executePixelExtend(float output[4], - float x, - float y, - PixelSampler sampler, - MemoryBufferExtend extend_x, - MemoryBufferExtend extend_y) +void ReadBufferOperation::execute_pixel_extend(float output[4], + float x, + float y, + PixelSampler sampler, + MemoryBufferExtend extend_x, + MemoryBufferExtend extend_y) { - if (m_single_value) { + if (single_value_) { /* write buffer has a single value stored at (0,0) */ - m_buffer->read(output, 0, 0); + buffer_->read(output, 0, 0); } else if (sampler == PixelSampler::Nearest) { - m_buffer->read(output, x, y, extend_x, extend_y); + buffer_->read(output, x, y, extend_x, extend_y); } else { - m_buffer->readBilinear(output, x, y, extend_x, extend_y); + buffer_->read_bilinear(output, x, y, extend_x, extend_y); } } -void ReadBufferOperation::executePixelFiltered( +void ReadBufferOperation::execute_pixel_filtered( float output[4], float x, float y, float dx[2], float dy[2]) { - if (m_single_value) { + if (single_value_) { /* write buffer has a single value stored at (0,0) */ - m_buffer->read(output, 0, 0); + buffer_->read(output, 0, 0); } else { const float uv[2] = {x, y}; const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}}; - m_buffer->readEWA(output, uv, deriv); + buffer_->readEWA(output, uv, deriv); } } -bool ReadBufferOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ReadBufferOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - if (this == readOperation) { + if (this == read_operation) { BLI_rcti_init(output, input->xmin, input->xmax, input->ymin, input->ymax); return true; } return false; } -void ReadBufferOperation::readResolutionFromWriteBuffer() +void ReadBufferOperation::read_resolution_from_write_buffer() { - if (this->m_memoryProxy != nullptr) { - WriteBufferOperation *operation = this->m_memoryProxy->getWriteBufferOperation(); - this->setWidth(operation->getWidth()); - this->setHeight(operation->getHeight()); + if (memory_proxy_ != nullptr) { + WriteBufferOperation *operation = memory_proxy_->get_write_buffer_operation(); + this->set_width(operation->get_width()); + this->set_height(operation->get_height()); } } -void ReadBufferOperation::updateMemoryBuffer() +void ReadBufferOperation::update_memory_buffer() { - this->m_buffer = this->getMemoryProxy()->getBuffer(); + buffer_ = this->get_memory_proxy()->get_buffer(); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ReadBufferOperation.h b/source/blender/compositor/operations/COM_ReadBufferOperation.h index 8b96b961a43..7f461d1c925 100644 --- a/source/blender/compositor/operations/COM_ReadBufferOperation.h +++ b/source/blender/compositor/operations/COM_ReadBufferOperation.h @@ -26,52 +26,52 @@ namespace blender::compositor { class ReadBufferOperation : public NodeOperation { private: - MemoryProxy *m_memoryProxy; - bool m_single_value; /* single value stored in buffer, copied from associated write operation */ - unsigned int m_offset; - MemoryBuffer *m_buffer; + MemoryProxy *memory_proxy_; + bool single_value_; /* single value stored in buffer, copied from associated write operation */ + unsigned int offset_; + MemoryBuffer *buffer_; public: ReadBufferOperation(DataType datatype); - void setMemoryProxy(MemoryProxy *memoryProxy) + void set_memory_proxy(MemoryProxy *memory_proxy) { - this->m_memoryProxy = memoryProxy; + memory_proxy_ = memory_proxy; } - MemoryProxy *getMemoryProxy() const + MemoryProxy *get_memory_proxy() const { - return this->m_memoryProxy; + return memory_proxy_; } - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void *initializeTileData(rcti *rect) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void executePixelExtend(float output[4], - float x, - float y, - PixelSampler sampler, - MemoryBufferExtend extend_x, - MemoryBufferExtend extend_y); - void executePixelFiltered(float output[4], float x, float y, float dx[2], float dy[2]) override; - void setOffset(unsigned int offset) + void *initialize_tile_data(rcti *rect) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_extend(float output[4], + float x, + float y, + PixelSampler sampler, + MemoryBufferExtend extend_x, + MemoryBufferExtend extend_y); + void execute_pixel_filtered( + float output[4], float x, float y, float dx[2], float dy[2]) override; + void set_offset(unsigned int offset) { - this->m_offset = offset; + offset_ = offset; } - unsigned int getOffset() const + unsigned int get_offset() const { - return this->m_offset; + return offset_; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - MemoryBuffer *getInputMemoryBuffer(MemoryBuffer **memoryBuffers) override + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + MemoryBuffer *get_input_memory_buffer(MemoryBuffer **memory_buffers) override { - return memoryBuffers[this->m_offset]; + return memory_buffers[offset_]; } - void readResolutionFromWriteBuffer(); - void updateMemoryBuffer(); + void read_resolution_from_write_buffer(); + void update_memory_buffer(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cc b/source/blender/compositor/operations/COM_RenderLayersProg.cc index 72e2c92c9cf..7650def2c87 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cc +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cc @@ -18,39 +18,27 @@ #include "COM_RenderLayersProg.h" -#include "COM_MetaData.h" - #include "BKE_image.h" -#include "BKE_scene.h" - -#include "BLI_listbase.h" -#include "BLI_string.h" -#include "BLI_string_ref.hh" - -#include "DNA_scene_types.h" - -#include "RE_pipeline.h" -#include "RE_texture.h" namespace blender::compositor { /* ******** Render Layers Base Prog ******** */ -RenderLayersProg::RenderLayersProg(const char *passName, DataType type, int elementsize) - : m_passName(passName) +RenderLayersProg::RenderLayersProg(const char *pass_name, DataType type, int elementsize) + : pass_name_(pass_name) { - this->setScene(nullptr); - this->m_inputBuffer = nullptr; - this->m_elementsize = elementsize; - this->m_rd = nullptr; + this->set_scene(nullptr); + input_buffer_ = nullptr; + elementsize_ = elementsize; + rd_ = nullptr; layer_buffer_ = nullptr; - this->addOutputSocket(type); + this->add_output_socket(type); } -void RenderLayersProg::initExecution() +void RenderLayersProg::init_execution() { - Scene *scene = this->getScene(); + Scene *scene = this->get_scene(); Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr; RenderResult *rr = nullptr; @@ -59,15 +47,14 @@ void RenderLayersProg::initExecution() } if (rr) { - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId()); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, get_layer_id()); if (view_layer) { RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl) { - this->m_inputBuffer = RE_RenderLayerGetPass( - rl, this->m_passName.c_str(), this->m_viewName); - if (m_inputBuffer) { - layer_buffer_ = new MemoryBuffer(m_inputBuffer, m_elementsize, getWidth(), getHeight()); + input_buffer_ = RE_RenderLayerGetPass(rl, pass_name_.c_str(), view_name_); + if (input_buffer_) { + layer_buffer_ = new MemoryBuffer(input_buffer_, elementsize_, get_width(), get_height()); } } } @@ -78,17 +65,17 @@ void RenderLayersProg::initExecution() } } -void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelSampler sampler) +void RenderLayersProg::do_interpolation(float output[4], float x, float y, PixelSampler sampler) { unsigned int offset; - int width = this->getWidth(), height = this->getHeight(); + int width = this->get_width(), height = this->get_height(); int ix = x, iy = y; if (ix < 0 || iy < 0 || ix >= width || iy >= height) { - if (this->m_elementsize == 1) { + if (elementsize_ == 1) { output[0] = 0.0f; } - else if (this->m_elementsize == 3) { + else if (elementsize_ == 3) { zero_v3(output); } else { @@ -99,48 +86,49 @@ void RenderLayersProg::doInterpolation(float output[4], float x, float y, PixelS switch (sampler) { case PixelSampler::Nearest: { - offset = (iy * width + ix) * this->m_elementsize; + offset = (iy * width + ix) * elementsize_; - if (this->m_elementsize == 1) { - output[0] = this->m_inputBuffer[offset]; + if (elementsize_ == 1) { + output[0] = input_buffer_[offset]; } - else if (this->m_elementsize == 3) { - copy_v3_v3(output, &this->m_inputBuffer[offset]); + else if (elementsize_ == 3) { + copy_v3_v3(output, &input_buffer_[offset]); } else { - copy_v4_v4(output, &this->m_inputBuffer[offset]); + copy_v4_v4(output, &input_buffer_[offset]); } break; } case PixelSampler::Bilinear: - BLI_bilinear_interpolation_fl( - this->m_inputBuffer, output, width, height, this->m_elementsize, x, y); + BLI_bilinear_interpolation_fl(input_buffer_, output, width, height, elementsize_, x, y); break; case PixelSampler::Bicubic: - BLI_bicubic_interpolation_fl( - this->m_inputBuffer, output, width, height, this->m_elementsize, x, y); + BLI_bicubic_interpolation_fl(input_buffer_, output, width, height, elementsize_, x, y); break; } } -void RenderLayersProg::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void RenderLayersProg::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { #if 0 - const RenderData *rd = this->m_rd; + const RenderData *rd = rd_; int dx = 0, dy = 0; if (rd->mode & R_BORDER && rd->mode & R_CROP) { - /* see comment in executeRegion describing coordinate mapping, + /* see comment in execute_region describing coordinate mapping, * here it simply goes other way around */ int full_width = rd->xsch * rd->size / 100; int full_height = rd->ysch * rd->size / 100; - dx = rd->border.xmin * full_width - (full_width - this->getWidth()) / 2.0f; - dy = rd->border.ymin * full_height - (full_height - this->getHeight()) / 2.0f; + dx = rd->border.xmin * full_width - (full_width - this->get_width()) / 2.0f; + dy = rd->border.ymin * full_height - (full_height - this->get_height()) / 2.0f; } int ix = x - dx; @@ -149,8 +137,8 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi #ifndef NDEBUG { - const DataType data_type = this->getOutputSocket()->getDataType(); - int actual_element_size = this->m_elementsize; + const DataType data_type = this->get_output_socket()->get_data_type(); + int actual_element_size = elementsize_; int expected_element_size; if (data_type == DataType::Value) { expected_element_size = 1; @@ -169,8 +157,8 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi } #endif - if (this->m_inputBuffer == nullptr) { - int elemsize = this->m_elementsize; + if (input_buffer_ == nullptr) { + int elemsize = elementsize_; if (elemsize == 1) { output[0] = 0.0f; } @@ -183,40 +171,37 @@ void RenderLayersProg::executePixelSampled(float output[4], float x, float y, Pi } } else { - doInterpolation(output, x, y, sampler); + do_interpolation(output, x, y, sampler); } } -void RenderLayersProg::deinitExecution() +void RenderLayersProg::deinit_execution() { - this->m_inputBuffer = nullptr; + input_buffer_ = nullptr; if (layer_buffer_) { delete layer_buffer_; layer_buffer_ = nullptr; } } -void RenderLayersProg::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void RenderLayersProg::determine_canvas(const rcti &UNUSED(preferred_area), rcti &r_area) { - Scene *sce = this->getScene(); + Scene *sce = this->get_scene(); Render *re = (sce) ? RE_GetSceneRender(sce) : nullptr; RenderResult *rr = nullptr; - resolution[0] = 0; - resolution[1] = 0; + r_area = COM_AREA_NONE; if (re) { rr = RE_AcquireResultRead(re); } if (rr) { - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, getLayerId()); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&sce->view_layers, get_layer_id()); if (view_layer) { RenderLayer *rl = RE_GetRenderLayer(rr, view_layer->name); if (rl) { - resolution[0] = rl->rectx; - resolution[1] = rl->recty; + BLI_rcti_init(&r_area, 0, rl->rectx, 0, rl->recty); } } } @@ -226,9 +211,9 @@ void RenderLayersProg::determineResolution(unsigned int resolution[2], } } -std::unique_ptr RenderLayersProg::getMetaData() +std::unique_ptr RenderLayersProg::get_meta_data() { - Scene *scene = this->getScene(); + Scene *scene = this->get_scene(); Render *re = (scene) ? RE_GetSceneRender(scene) : nullptr; RenderResult *render_result = nullptr; MetaDataExtractCallbackData callback_data = {nullptr}; @@ -238,15 +223,15 @@ std::unique_ptr RenderLayersProg::getMetaData() } if (render_result && render_result->stamp_data) { - ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, getLayerId()); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, get_layer_id()); if (view_layer) { std::string full_layer_name = std::string( view_layer->name, BLI_strnlen(view_layer->name, sizeof(view_layer->name))) + - "." + m_passName; + "." + pass_name_; blender::StringRef cryptomatte_layer_name = blender::bke::cryptomatte::BKE_cryptomatte_extract_layer_name(full_layer_name); - callback_data.setCryptomatteKeys(cryptomatte_layer_name); + callback_data.set_cryptomatte_keys(cryptomatte_layer_name); BKE_stamp_info_callback(&callback_data, render_result->stamp_data, @@ -267,28 +252,28 @@ void RenderLayersProg::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span UNUSED(inputs)) { - BLI_assert(output->get_num_channels() >= m_elementsize); + BLI_assert(output->get_num_channels() >= elementsize_); if (layer_buffer_) { - output->copy_from(layer_buffer_, area, 0, m_elementsize, 0); + output->copy_from(layer_buffer_, area, 0, elementsize_, 0); } else { - std::unique_ptr zero_elem = std::make_unique(m_elementsize); - output->fill(area, 0, zero_elem.get(), m_elementsize); + std::unique_ptr zero_elem = std::make_unique(elementsize_); + output->fill(area, 0, zero_elem.get(), elementsize_); } } /* ******** Render Layers AO Operation ******** */ -void RenderLayersAOOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void RenderLayersAOOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float *inputBuffer = this->getInputBuffer(); - if (inputBuffer == nullptr) { + float *input_buffer = this->get_input_buffer(); + if (input_buffer == nullptr) { zero_v3(output); } else { - doInterpolation(output, x, y, sampler); + do_interpolation(output, x, y, sampler); } output[3] = 1.0f; } @@ -298,7 +283,7 @@ void RenderLayersAOOperation::update_memory_buffer_partial(MemoryBuffer *output, Span UNUSED(inputs)) { BLI_assert(output->get_num_channels() == COM_DATA_TYPE_COLOR_CHANNELS); - BLI_assert(m_elementsize == COM_DATA_TYPE_COLOR_CHANNELS); + BLI_assert(elementsize_ == COM_DATA_TYPE_COLOR_CHANNELS); if (layer_buffer_) { output->copy_from(layer_buffer_, area, 0, COM_DATA_TYPE_VECTOR_CHANNELS, 0); } @@ -309,19 +294,19 @@ void RenderLayersAOOperation::update_memory_buffer_partial(MemoryBuffer *output, } /* ******** Render Layers Alpha Operation ******** */ -void RenderLayersAlphaProg::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void RenderLayersAlphaProg::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - float *inputBuffer = this->getInputBuffer(); + float *input_buffer = this->get_input_buffer(); - if (inputBuffer == nullptr) { + if (input_buffer == nullptr) { output[0] = 0.0f; } else { float temp[4]; - doInterpolation(temp, x, y, sampler); + do_interpolation(temp, x, y, sampler); output[0] = temp[3]; } } @@ -331,7 +316,7 @@ void RenderLayersAlphaProg::update_memory_buffer_partial(MemoryBuffer *output, Span UNUSED(inputs)) { BLI_assert(output->get_num_channels() == COM_DATA_TYPE_VALUE_CHANNELS); - BLI_assert(m_elementsize == COM_DATA_TYPE_COLOR_CHANNELS); + BLI_assert(elementsize_ == COM_DATA_TYPE_COLOR_CHANNELS); if (layer_buffer_) { output->copy_from(layer_buffer_, area, 3, COM_DATA_TYPE_VALUE_CHANNELS, 0); } @@ -341,22 +326,22 @@ void RenderLayersAlphaProg::update_memory_buffer_partial(MemoryBuffer *output, } /* ******** Render Layers Depth Operation ******** */ -void RenderLayersDepthProg::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void RenderLayersDepthProg::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { int ix = x; int iy = y; - float *inputBuffer = this->getInputBuffer(); + float *input_buffer = this->get_input_buffer(); - if (inputBuffer == nullptr || ix < 0 || iy < 0 || ix >= (int)this->getWidth() || - iy >= (int)this->getHeight()) { + if (input_buffer == nullptr || ix < 0 || iy < 0 || ix >= (int)this->get_width() || + iy >= (int)this->get_height()) { output[0] = 10e10f; } else { - unsigned int offset = (iy * this->getWidth() + ix); - output[0] = inputBuffer[offset]; + unsigned int offset = (iy * this->get_width() + ix); + output[0] = input_buffer[offset]; } } @@ -365,7 +350,7 @@ void RenderLayersDepthProg::update_memory_buffer_partial(MemoryBuffer *output, Span UNUSED(inputs)) { BLI_assert(output->get_num_channels() == COM_DATA_TYPE_VALUE_CHANNELS); - BLI_assert(m_elementsize == COM_DATA_TYPE_VALUE_CHANNELS); + BLI_assert(elementsize_ == COM_DATA_TYPE_VALUE_CHANNELS); if (layer_buffer_) { output->copy_from(layer_buffer_, area); } diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index dd76a56d645..79afe3c2a8c 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -38,17 +38,17 @@ class RenderLayersProg : public MultiThreadedOperation { /** * Reference to the scene object. */ - Scene *m_scene; + Scene *scene_; /** - * layerId of the layer where this operation needs to get its data from + * layer_id of the layer where this operation needs to get its data from */ - short m_layerId; + short layer_id_; /** - * viewName of the view to use (unless another view is specified by the node + * view_name of the view to use (unless another view is specified by the node */ - const char *m_viewName; + const char *view_name_; const MemoryBuffer *layer_buffer_; @@ -56,79 +56,78 @@ class RenderLayersProg : public MultiThreadedOperation { * Cached instance to the float buffer inside the layer. * TODO: To be removed with tiled implementation. */ - float *m_inputBuffer; + float *input_buffer_; /** * Render-pass where this operation needs to get its data from. */ - std::string m_passName; + std::string pass_name_; - int m_elementsize; + int elementsize_; /** * \brief render data used for active rendering */ - const RenderData *m_rd; + const RenderData *rd_; /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; /** * retrieve the reference to the float buffer of the renderer. */ - inline float *getInputBuffer() + inline float *get_input_buffer() { - return this->m_inputBuffer; + return input_buffer_; } - void doInterpolation(float output[4], float x, float y, PixelSampler sampler); + void do_interpolation(float output[4], float x, float y, PixelSampler sampler); public: /** * Constructor */ - RenderLayersProg(const char *passName, DataType type, int elementsize); + RenderLayersProg(const char *pass_name, DataType type, int elementsize); /** * setter for the scene field. Will be called from * \see RenderLayerNode to set the actual scene where * the data will be retrieved from. */ - void setScene(Scene *scene) + void set_scene(Scene *scene) { - this->m_scene = scene; + scene_ = scene; } - Scene *getScene() const + Scene *get_scene() const { - return this->m_scene; + return scene_; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { - this->m_rd = rd; + rd_ = rd; } - void setLayerId(short layerId) + void set_layer_id(short layer_id) { - this->m_layerId = layerId; + layer_id_ = layer_id; } - short getLayerId() const + short get_layer_id() const { - return this->m_layerId; + return layer_id_; } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - this->m_viewName = viewName; + view_name_ = view_name; } - const char *getViewName() + const char *get_view_name() { - return this->m_viewName; + return view_name_; } - void initExecution() override; - void deinitExecution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_execution() override; + void deinit_execution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - std::unique_ptr getMetaData() override; + std::unique_ptr get_meta_data() override; virtual void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -137,11 +136,11 @@ class RenderLayersProg : public MultiThreadedOperation { class RenderLayersAOOperation : public RenderLayersProg { public: - RenderLayersAOOperation(const char *passName, DataType type, int elementsize) - : RenderLayersProg(passName, type, elementsize) + RenderLayersAOOperation(const char *pass_name, DataType type, int elementsize) + : RenderLayersProg(pass_name, type, elementsize) { } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -150,11 +149,11 @@ class RenderLayersAOOperation : public RenderLayersProg { class RenderLayersAlphaProg : public RenderLayersProg { public: - RenderLayersAlphaProg(const char *passName, DataType type, int elementsize) - : RenderLayersProg(passName, type, elementsize) + RenderLayersAlphaProg(const char *pass_name, DataType type, int elementsize) + : RenderLayersProg(pass_name, type, elementsize) { } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -163,11 +162,11 @@ class RenderLayersAlphaProg : public RenderLayersProg { class RenderLayersDepthProg : public RenderLayersProg { public: - RenderLayersDepthProg(const char *passName, DataType type, int elementsize) - : RenderLayersProg(passName, type, elementsize) + RenderLayersDepthProg(const char *pass_name, DataType type, int elementsize) + : RenderLayersProg(pass_name, type, elementsize) { } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_RotateOperation.cc b/source/blender/compositor/operations/COM_RotateOperation.cc index e3c482c15cb..8129516f81c 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.cc +++ b/source/blender/compositor/operations/COM_RotateOperation.cc @@ -17,25 +17,37 @@ */ #include "COM_RotateOperation.h" -#include "COM_ConstantOperation.h" - -#include "BLI_math.h" namespace blender::compositor { RotateOperation::RotateOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_imageSocket = nullptr; - this->m_degreeSocket = nullptr; - this->m_doDegree2RadConversion = false; - this->m_isDegreeSet = false; + this->add_input_socket(DataType::Color, ResizeMode::None); + this->add_input_socket(DataType::Value, ResizeMode::None); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); + image_socket_ = nullptr; + degree_socket_ = nullptr; + do_degree2_rad_conversion_ = false; + is_degree_set_ = false; sampler_ = PixelSampler::Bilinear; } +void RotateOperation::get_rotation_center(const rcti &area, float &r_x, float &r_y) +{ + r_x = (BLI_rcti_size_x(&area) - 1) / 2.0; + r_y = (BLI_rcti_size_y(&area) - 1) / 2.0; +} + +void RotateOperation::get_rotation_offset(const rcti &input_canvas, + const rcti &rotate_canvas, + float &r_offset_x, + float &r_offset_y) +{ + r_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&rotate_canvas)) / 2.0f; + r_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&rotate_canvas)) / 2.0f; +} + void RotateOperation::get_area_rotation_bounds(const rcti &area, const float center_x, const float center_y, @@ -48,14 +60,14 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area, const float dxmax = area.xmax - center_x; const float dymax = area.ymax - center_y; - const float x1 = center_x + (cosine * dxmin + sine * dymin); - const float x2 = center_x + (cosine * dxmax + sine * dymin); - const float x3 = center_x + (cosine * dxmin + sine * dymax); - const float x4 = center_x + (cosine * dxmax + sine * dymax); - const float y1 = center_y + (-sine * dxmin + cosine * dymin); - const float y2 = center_y + (-sine * dxmax + cosine * dymin); - const float y3 = center_y + (-sine * dxmin + cosine * dymax); - const float y4 = center_y + (-sine * dxmax + cosine * dymax); + const float x1 = center_x + (cosine * dxmin + (-sine) * dymin); + const float x2 = center_x + (cosine * dxmax + (-sine) * dymin); + const float x3 = center_x + (cosine * dxmin + (-sine) * dymax); + const float x4 = center_x + (cosine * dxmax + (-sine) * dymax); + const float y1 = center_y + (sine * dxmin + cosine * dymin); + const float y2 = center_y + (sine * dxmax + cosine * dymin); + const float y3 = center_y + (sine * dxmin + cosine * dymax); + const float y4 = center_y + (sine * dxmax + cosine * dymax); const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); @@ -67,96 +79,161 @@ void RotateOperation::get_area_rotation_bounds(const rcti &area, r_bounds.ymax = ceil(maxy); } +void RotateOperation::get_area_rotation_bounds_inverted(const rcti &area, + const float center_x, + const float center_y, + const float sine, + const float cosine, + rcti &r_bounds) +{ + get_area_rotation_bounds(area, center_x, center_y, -sine, cosine, r_bounds); +} + +void RotateOperation::get_rotation_area_of_interest(const rcti &input_canvas, + const rcti &rotate_canvas, + const float sine, + const float cosine, + const rcti &output_area, + rcti &r_input_area) +{ + float center_x, center_y; + get_rotation_center(input_canvas, center_x, center_y); + + float rotate_offset_x, rotate_offset_y; + get_rotation_offset(input_canvas, rotate_canvas, rotate_offset_x, rotate_offset_y); + + r_input_area = output_area; + BLI_rcti_translate(&r_input_area, rotate_offset_x, rotate_offset_y); + get_area_rotation_bounds_inverted(r_input_area, center_x, center_y, sine, cosine, r_input_area); +} + +void RotateOperation::get_rotation_canvas(const rcti &input_canvas, + const float sine, + const float cosine, + rcti &r_canvas) +{ + float center_x, center_y; + get_rotation_center(input_canvas, center_x, center_y); + + rcti rot_bounds; + get_area_rotation_bounds(input_canvas, center_x, center_y, sine, cosine, rot_bounds); + + float offset_x, offset_y; + get_rotation_offset(input_canvas, rot_bounds, offset_x, offset_y); + r_canvas = rot_bounds; + BLI_rcti_translate(&r_canvas, -offset_x, -offset_y); +} + void RotateOperation::init_data() { - this->m_centerX = (getWidth() - 1) / 2.0; - this->m_centerY = (getHeight() - 1) / 2.0; + if (execution_model_ == eExecutionModel::Tiled) { + get_rotation_center(get_canvas(), center_x_, center_y_); + } } -void RotateOperation::initExecution() +void RotateOperation::init_execution() { - this->m_imageSocket = this->getInputSocketReader(0); - this->m_degreeSocket = this->getInputSocketReader(1); + image_socket_ = this->get_input_socket_reader(0); + degree_socket_ = this->get_input_socket_reader(1); } -void RotateOperation::deinitExecution() +void RotateOperation::deinit_execution() { - this->m_imageSocket = nullptr; - this->m_degreeSocket = nullptr; + image_socket_ = nullptr; + degree_socket_ = nullptr; } -inline void RotateOperation::ensureDegree() +inline void RotateOperation::ensure_degree() { - if (!this->m_isDegreeSet) { + if (!is_degree_set_) { float degree[4]; switch (execution_model_) { case eExecutionModel::Tiled: - this->m_degreeSocket->readSampled(degree, 0, 0, PixelSampler::Nearest); + degree_socket_->read_sampled(degree, 0, 0, PixelSampler::Nearest); break; case eExecutionModel::FullFrame: - NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX); - const bool is_constant_degree = degree_op->get_flags().is_constant_operation; - degree[0] = is_constant_degree ? - static_cast(degree_op)->get_constant_elem()[0] : - 0.0f; + degree[0] = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); break; } double rad; - if (this->m_doDegree2RadConversion) { + if (do_degree2_rad_conversion_) { rad = DEG2RAD((double)degree[0]); } else { rad = degree[0]; } - this->m_cosine = cos(rad); - this->m_sine = sin(rad); + cosine_ = cos(rad); + sine_ = sin(rad); - this->m_isDegreeSet = true; + is_degree_set_ = true; } } -void RotateOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void RotateOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - ensureDegree(); - const float dy = y - this->m_centerY; - const float dx = x - this->m_centerX; - const float nx = this->m_centerX + (this->m_cosine * dx + this->m_sine * dy); - const float ny = this->m_centerY + (-this->m_sine * dx + this->m_cosine * dy); - this->m_imageSocket->readSampled(output, nx, ny, sampler); + ensure_degree(); + const float dy = y - center_y_; + const float dx = x - center_x_; + const float nx = center_x_ + (cosine_ * dx + sine_ * dy); + const float ny = center_y_ + (-sine_ * dx + cosine_ * dy); + image_socket_->read_sampled(output, nx, ny, sampler); } -bool RotateOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool RotateOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - ensureDegree(); - rcti newInput; + ensure_degree(); + rcti new_input; - const float dxmin = input->xmin - this->m_centerX; - const float dymin = input->ymin - this->m_centerY; - const float dxmax = input->xmax - this->m_centerX; - const float dymax = input->ymax - this->m_centerY; + const float dxmin = input->xmin - center_x_; + const float dymin = input->ymin - center_y_; + const float dxmax = input->xmax - center_x_; + const float dymax = input->ymax - center_y_; - const float x1 = this->m_centerX + (this->m_cosine * dxmin + this->m_sine * dymin); - const float x2 = this->m_centerX + (this->m_cosine * dxmax + this->m_sine * dymin); - const float x3 = this->m_centerX + (this->m_cosine * dxmin + this->m_sine * dymax); - const float x4 = this->m_centerX + (this->m_cosine * dxmax + this->m_sine * dymax); - const float y1 = this->m_centerY + (-this->m_sine * dxmin + this->m_cosine * dymin); - const float y2 = this->m_centerY + (-this->m_sine * dxmax + this->m_cosine * dymin); - const float y3 = this->m_centerY + (-this->m_sine * dxmin + this->m_cosine * dymax); - const float y4 = this->m_centerY + (-this->m_sine * dxmax + this->m_cosine * dymax); + const float x1 = center_x_ + (cosine_ * dxmin + sine_ * dymin); + const float x2 = center_x_ + (cosine_ * dxmax + sine_ * dymin); + const float x3 = center_x_ + (cosine_ * dxmin + sine_ * dymax); + const float x4 = center_x_ + (cosine_ * dxmax + sine_ * dymax); + const float y1 = center_y_ + (-sine_ * dxmin + cosine_ * dymin); + const float y2 = center_y_ + (-sine_ * dxmax + cosine_ * dymin); + const float y3 = center_y_ + (-sine_ * dxmin + cosine_ * dymax); + const float y4 = center_y_ + (-sine_ * dxmax + cosine_ * dymax); const float minx = MIN2(x1, MIN2(x2, MIN2(x3, x4))); const float maxx = MAX2(x1, MAX2(x2, MAX2(x3, x4))); const float miny = MIN2(y1, MIN2(y2, MIN2(y3, y4))); const float maxy = MAX2(y1, MAX2(y2, MAX2(y3, y4))); - newInput.xmax = ceil(maxx) + 1; - newInput.xmin = floor(minx) - 1; - newInput.ymax = ceil(maxy) + 1; - newInput.ymin = floor(miny) - 1; + new_input.xmax = ceil(maxx) + 1; + new_input.xmin = floor(minx) - 1; + new_input.ymax = ceil(maxy) + 1; + new_input.ymin = floor(miny) - 1; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); +} + +void RotateOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + if (execution_model_ == eExecutionModel::Tiled) { + NodeOperation::determine_canvas(preferred_area, r_area); + return; + } + + const bool image_determined = + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (image_determined) { + rcti input_canvas = r_area; + rcti unused; + get_input_socket(DEGREE_INPUT_INDEX)->determine_canvas(input_canvas, unused); + + ensure_degree(); + + get_rotation_canvas(input_canvas, sine_, cosine_, r_area); + } } void RotateOperation::get_area_of_interest(const int input_idx, @@ -164,13 +241,15 @@ void RotateOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { if (input_idx == DEGREE_INPUT_INDEX) { - /* Degrees input is always used as constant. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; return; } - ensureDegree(); - get_area_rotation_bounds(output_area, m_centerX, m_centerY, m_sine, m_cosine, r_input_area); + ensure_degree(); + + const rcti &input_image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); + get_rotation_area_of_interest( + input_image_canvas, this->get_canvas(), sine_, cosine_, output_area, r_input_area); expand_area_for_sampler(r_input_area, sampler_); } @@ -178,13 +257,20 @@ void RotateOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - ensureDegree(); const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; + + NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); + float center_x, center_y; + get_rotation_center(image_op->get_canvas(), center_x, center_y); + float rotate_offset_x, rotate_offset_y; + get_rotation_offset( + image_op->get_canvas(), this->get_canvas(), rotate_offset_x, rotate_offset_y); + for (BuffersIterator it = output->iterate_with({}, area); !it.is_end(); ++it) { - float x = it.x; - float y = it.y; - rotate_coords(x, y, m_centerX, m_centerY, m_sine, m_cosine); - input_img->read_elem_sampled(x, y, sampler_, it.out); + float x = rotate_offset_x + it.x + canvas_.xmin; + float y = rotate_offset_y + it.y + canvas_.ymin; + rotate_coords(x, y, center_x, center_y, sine_, cosine_); + input_img->read_elem_sampled(x - canvas_.xmin, y - canvas_.ymin, sampler_, it.out); } } diff --git a/source/blender/compositor/operations/COM_RotateOperation.h b/source/blender/compositor/operations/COM_RotateOperation.h index f0de699f9ce..3970d3a2776 100644 --- a/source/blender/compositor/operations/COM_RotateOperation.h +++ b/source/blender/compositor/operations/COM_RotateOperation.h @@ -27,14 +27,16 @@ class RotateOperation : public MultiThreadedOperation { constexpr static int IMAGE_INPUT_INDEX = 0; constexpr static int DEGREE_INPUT_INDEX = 1; - SocketReader *m_imageSocket; - SocketReader *m_degreeSocket; - float m_centerX; - float m_centerY; - float m_cosine; - float m_sine; - bool m_doDegree2RadConversion; - bool m_isDegreeSet; + SocketReader *image_socket_; + SocketReader *degree_socket_; + /* TODO(manzanilla): to be removed with tiled implementation. */ + float center_x_; + float center_y_; + + float cosine_; + float sine_; + bool do_degree2_rad_conversion_; + bool is_degree_set_; PixelSampler sampler_; public: @@ -49,24 +51,45 @@ class RotateOperation : public MultiThreadedOperation { y = center_y + (-sine * dx + cosine * dy); } + static void get_rotation_center(const rcti &area, float &r_x, float &r_y); + static void get_rotation_offset(const rcti &input_canvas, + const rcti &rotate_canvas, + float &r_offset_x, + float &r_offset_y); static void get_area_rotation_bounds(const rcti &area, const float center_x, const float center_y, const float sine, const float cosine, rcti &r_bounds); + static void get_area_rotation_bounds_inverted(const rcti &area, + const float center_x, + const float center_y, + const float sine, + const float cosine, + rcti &r_bounds); + static void get_rotation_area_of_interest(const rcti &input_canvas, + const rcti &rotate_canvas, + const float sine, + const float cosine, + const rcti &output_area, + rcti &r_input_area); + static void get_rotation_canvas(const rcti &input_canvas, + const float sine, + const float cosine, + rcti &r_canvas); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; - void setDoDegree2RadConversion(bool abool) + void set_do_degree2_rad_conversion(bool abool) { - this->m_doDegree2RadConversion = abool; + do_degree2_rad_conversion_ = abool; } void set_sampler(PixelSampler sampler) @@ -74,12 +97,14 @@ class RotateOperation : public MultiThreadedOperation { sampler_ = sampler; } - void ensureDegree(); + void ensure_degree(); void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; + + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SMAAOperation.cc b/source/blender/compositor/operations/COM_SMAAOperation.cc index 4153b9c8523..ef67b6ca5f9 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.cc +++ b/source/blender/compositor/operations/COM_SMAAOperation.cc @@ -20,7 +20,6 @@ #include "COM_SMAAOperation.h" #include "BKE_node.h" -#include "BLI_math.h" #include "COM_SMAAAreaTexture.h" extern "C" { @@ -39,7 +38,7 @@ namespace blender::compositor { * * This file is based on SMAA-CPP: * - * https://github.com/iRi-E/smaa-cpp + * https://github.com/i_ri-E/smaa-cpp * * Currently only SMAA 1x mode is provided, so the operation will be done * with no spatial multi-sampling nor temporal super-sampling. @@ -65,7 +64,7 @@ namespace blender::compositor { * #buffer->read_elem_checked. */ static inline void sample(SocketReader *reader, int x, int y, float color[4]) { - if (x < 0 || x >= reader->getWidth() || y < 0 || y >= reader->getHeight()) { + if (x < 0 || x >= reader->get_width() || y < 0 || y >= reader->get_height()) { color[0] = color[1] = color[2] = color[3] = 0.0; return; } @@ -168,50 +167,50 @@ static void area_diag(int d1, int d2, int e1, int e2, float weights[2]) SMAAEdgeDetectionOperation::SMAAEdgeDetectionOperation() { - this->addInputSocket(DataType::Color); /* image */ - this->addInputSocket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ - this->addOutputSocket(DataType::Color); - this->flags.complex = true; - this->m_imageReader = nullptr; - this->m_valueReader = nullptr; - this->setThreshold(CMP_DEFAULT_SMAA_THRESHOLD); - this->setLocalContrastAdaptationFactor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); + this->add_input_socket(DataType::Color); /* image */ + this->add_input_socket(DataType::Value); /* Depth, material ID, etc. TODO: currently unused. */ + this->add_output_socket(DataType::Color); + flags_.complex = true; + image_reader_ = nullptr; + value_reader_ = nullptr; + this->set_threshold(CMP_DEFAULT_SMAA_THRESHOLD); + this->set_local_contrast_adaptation_factor(CMP_DEFAULT_SMAA_CONTRAST_LIMIT); } -void SMAAEdgeDetectionOperation::initExecution() +void SMAAEdgeDetectionOperation::init_execution() { - this->m_imageReader = this->getInputSocketReader(0); - this->m_valueReader = this->getInputSocketReader(1); + image_reader_ = this->get_input_socket_reader(0); + value_reader_ = this->get_input_socket_reader(1); } -void SMAAEdgeDetectionOperation::deinitExecution() +void SMAAEdgeDetectionOperation::deinit_execution() { - this->m_imageReader = nullptr; - this->m_valueReader = nullptr; + image_reader_ = nullptr; + value_reader_ = nullptr; } -void SMAAEdgeDetectionOperation::setThreshold(float threshold) +void SMAAEdgeDetectionOperation::set_threshold(float threshold) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 0.5 */ - m_threshold = scalenorm(0, 0.5, threshold); + threshold_ = scalenorm(0, 0.5, threshold); } -void SMAAEdgeDetectionOperation::setLocalContrastAdaptationFactor(float factor) +void SMAAEdgeDetectionOperation::set_local_contrast_adaptation_factor(float factor) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 1 and 10 */ - m_contrast_limit = scalenorm(1, 10, factor); + contrast_limit_ = scalenorm(1, 10, factor); } -bool SMAAEdgeDetectionOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool SMAAEdgeDetectionOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - newInput.xmax = input->xmax + 1; - newInput.xmin = input->xmin - 2; - newInput.ymax = input->ymax + 1; - newInput.ymin = input->ymin - 2; + rcti new_input; + new_input.xmax = input->xmax + 1; + new_input.xmin = input->xmin - 2; + new_input.ymax = input->ymax + 1; + new_input.ymin = input->ymin - 2; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void SMAAEdgeDetectionOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -224,23 +223,23 @@ void SMAAEdgeDetectionOperation::get_area_of_interest(const int UNUSED(input_idx r_input_area.ymin = output_area.ymin - 2; } -void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, void * /*data*/) +void SMAAEdgeDetectionOperation::execute_pixel(float output[4], int x, int y, void * /*data*/) { float color[4]; /* Calculate luma deltas: */ - sample(m_imageReader, x, y, color); + sample(image_reader_, x, y, color); float L = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x - 1, y, color); + sample(image_reader_, x - 1, y, color); float Lleft = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x, y - 1, color); + sample(image_reader_, x, y - 1, color); float Ltop = IMB_colormanagement_get_luminance(color); float Dleft = fabsf(L - Lleft); float Dtop = fabsf(L - Ltop); /* We do the usual threshold: */ - output[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f; - output[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f; + output[0] = (x > 0 && Dleft >= threshold_) ? 1.0f : 0.0f; + output[1] = (y > 0 && Dtop >= threshold_) ? 1.0f : 0.0f; output[2] = 0.0f; output[3] = 1.0f; @@ -250,36 +249,36 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi } /* Calculate right and bottom deltas: */ - sample(m_imageReader, x + 1, y, color); + sample(image_reader_, x + 1, y, color); float Lright = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x, y + 1, color); + sample(image_reader_, x, y + 1, color); float Lbottom = IMB_colormanagement_get_luminance(color); float Dright = fabsf(L - Lright); float Dbottom = fabsf(L - Lbottom); /* Calculate the maximum delta in the direct neighborhood: */ - float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); + float max_delta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); /* Calculate luma used for both left and top edges: */ - sample(m_imageReader, x - 1, y - 1, color); + sample(image_reader_, x - 1, y - 1, color); float Llefttop = IMB_colormanagement_get_luminance(color); /* Left edge */ if (output[0] != 0.0f) { /* Calculate deltas around the left pixel: */ - sample(m_imageReader, x - 2, y, color); + sample(image_reader_, x - 2, y, color); float Lleftleft = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x - 1, y + 1, color); + sample(image_reader_, x - 1, y + 1, color); float Lleftbottom = IMB_colormanagement_get_luminance(color); float Dleftleft = fabsf(Lleft - Lleftleft); float Dlefttop = fabsf(Lleft - Llefttop); float Dleftbottom = fabsf(Lleft - Lleftbottom); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); + max_delta = fmaxf(max_delta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dleft) { + if (max_delta > contrast_limit_ * Dleft) { output[0] = 0.0f; } } @@ -287,19 +286,19 @@ void SMAAEdgeDetectionOperation::executePixel(float output[4], int x, int y, voi /* Top edge */ if (output[1] != 0.0f) { /* Calculate top-top delta: */ - sample(m_imageReader, x, y - 2, color); + sample(image_reader_, x, y - 2, color); float Ltoptop = IMB_colormanagement_get_luminance(color); - sample(m_imageReader, x + 1, y - 1, color); + sample(image_reader_, x + 1, y - 1, color); float Ltopright = IMB_colormanagement_get_luminance(color); float Dtoptop = fabsf(Ltop - Ltoptop); float Dtopleft = fabsf(Ltop - Llefttop); float Dtopright = fabsf(Ltop - Ltopright); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); + max_delta = fmaxf(max_delta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dtop) { + if (max_delta > contrast_limit_ * Dtop) { output[1] = 0.0f; } } @@ -326,8 +325,8 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dtop = fabsf(L - Ltop); /* We do the usual threshold: */ - it.out[0] = (x > 0 && Dleft >= m_threshold) ? 1.0f : 0.0f; - it.out[1] = (y > 0 && Dtop >= m_threshold) ? 1.0f : 0.0f; + it.out[0] = (x > 0 && Dleft >= threshold_) ? 1.0f : 0.0f; + it.out[1] = (y > 0 && Dtop >= threshold_) ? 1.0f : 0.0f; it.out[2] = 0.0f; it.out[3] = 1.0f; @@ -345,7 +344,7 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dbottom = fabsf(L - Lbottom); /* Calculate the maximum delta in the direct neighborhood: */ - float maxDelta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); + float max_delta = fmaxf(fmaxf(Dleft, Dright), fmaxf(Dtop, Dbottom)); /* Calculate luma used for both left and top edges: */ image->read_elem_checked(x - 1, y - 1, color); @@ -363,10 +362,10 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dleftbottom = fabsf(Lleft - Lleftbottom); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); + max_delta = fmaxf(max_delta, fmaxf(Dleftleft, fmaxf(Dlefttop, Dleftbottom))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dleft) { + if (max_delta > contrast_limit_ * Dleft) { it.out[0] = 0.0f; } } @@ -383,10 +382,10 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp const float Dtopright = fabsf(Ltop - Ltopright); /* Calculate the final maximum delta: */ - maxDelta = fmaxf(maxDelta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); + max_delta = fmaxf(max_delta, fmaxf(Dtoptop, fmaxf(Dtopleft, Dtopright))); /* Local contrast adaptation: */ - if (maxDelta > m_contrast_limit * Dtop) { + if (max_delta > contrast_limit_ * Dtop) { it.out[1] = 0.0f; } } @@ -399,47 +398,47 @@ void SMAAEdgeDetectionOperation::update_memory_buffer_partial(MemoryBuffer *outp SMAABlendingWeightCalculationOperation::SMAABlendingWeightCalculationOperation() { - this->addInputSocket(DataType::Color); /* edges */ - this->addOutputSocket(DataType::Color); - this->flags.complex = true; - this->m_imageReader = nullptr; - this->setCornerRounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); + this->add_input_socket(DataType::Color); /* edges */ + this->add_output_socket(DataType::Color); + flags_.complex = true; + image_reader_ = nullptr; + this->set_corner_rounding(CMP_DEFAULT_SMAA_CORNER_ROUNDING); } -void *SMAABlendingWeightCalculationOperation::initializeTileData(rcti *rect) +void *SMAABlendingWeightCalculationOperation::initialize_tile_data(rcti *rect) { - return getInputOperation(0)->initializeTileData(rect); + return get_input_operation(0)->initialize_tile_data(rect); } -void SMAABlendingWeightCalculationOperation::initExecution() +void SMAABlendingWeightCalculationOperation::init_execution() { - this->m_imageReader = this->getInputSocketReader(0); + image_reader_ = this->get_input_socket_reader(0); if (execution_model_ == eExecutionModel::Tiled) { - sample_image_fn_ = [=](int x, int y, float *out) { sample(m_imageReader, x, y, out); }; + sample_image_fn_ = [=](int x, int y, float *out) { sample(image_reader_, x, y, out); }; } } -void SMAABlendingWeightCalculationOperation::setCornerRounding(float rounding) +void SMAABlendingWeightCalculationOperation::set_corner_rounding(float rounding) { /* UI values are between 0 and 1 for simplicity but algorithm expects values between 0 and 100 */ - m_corner_rounding = static_cast(scalenorm(0, 100, rounding)); + corner_rounding_ = static_cast(scalenorm(0, 100, rounding)); } -void SMAABlendingWeightCalculationOperation::executePixel(float output[4], - int x, - int y, - void * /*data*/) +void SMAABlendingWeightCalculationOperation::execute_pixel(float output[4], + int x, + int y, + void * /*data*/) { float edges[4], c[4]; zero_v4(output); - sample(m_imageReader, x, y, edges); + sample(image_reader_, x, y, edges); /* Edge at north */ if (edges[1] > 0.0f) { /* Diagonals have both north and west edges, so calculating weights for them */ /* in one of the boundaries is enough. */ - calculateDiagWeights(x, y, edges, output); + calculate_diag_weights(x, y, edges, output); /* We give priority to diagonals, so if we find a diagonal we skip. */ /* horizontal/vertical processing. */ @@ -448,25 +447,25 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], } /* Find the distance to the left and the right: */ - int left = searchXLeft(x, y); - int right = searchXRight(x, y); + int left = search_xleft(x, y); + int right = search_xright(x, y); int d1 = x - left, d2 = right - x; /* Fetch the left and right crossing edges: */ int e1 = 0, e2 = 0; - sample(m_imageReader, left, y - 1, c); + sample(image_reader_, left, y - 1, c); if (c[0] > 0.0) { e1 += 1; } - sample(m_imageReader, left, y, c); + sample(image_reader_, left, y, c); if (c[0] > 0.0) { e1 += 2; } - sample(m_imageReader, right + 1, y - 1, c); + sample(image_reader_, right + 1, y - 1, c); if (c[0] > 0.0) { e2 += 1; } - sample(m_imageReader, right + 1, y, c); + sample(image_reader_, right + 1, y, c); if (c[0] > 0.0) { e2 += 2; } @@ -476,38 +475,38 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], area(d1, d2, e1, e2, output); /* R, G */ /* Fix corners: */ - if (m_corner_rounding) { - detectHorizontalCornerPattern(output, left, right, y, d1, d2); + if (corner_rounding_) { + detect_horizontal_corner_pattern(output, left, right, y, d1, d2); } } /* Edge at west */ if (edges[0] > 0.0f) { /* Did we already do diagonal search for this west edge from the left neighboring pixel? */ - if (isVerticalSearchUnneeded(x, y)) { + if (is_vertical_search_unneeded(x, y)) { return; } /* Find the distance to the top and the bottom: */ - int top = searchYUp(x, y); - int bottom = searchYDown(x, y); + int top = search_yup(x, y); + int bottom = search_ydown(x, y); int d1 = y - top, d2 = bottom - y; /* Fetch the top and bottom crossing edges: */ int e1 = 0, e2 = 0; - sample(m_imageReader, x - 1, top, c); + sample(image_reader_, x - 1, top, c); if (c[1] > 0.0) { e1 += 1; } - sample(m_imageReader, x, top, c); + sample(image_reader_, x, top, c); if (c[1] > 0.0) { e1 += 2; } - sample(m_imageReader, x - 1, bottom + 1, c); + sample(image_reader_, x - 1, bottom + 1, c); if (c[1] > 0.0) { e2 += 1; } - sample(m_imageReader, x, bottom + 1, c); + sample(image_reader_, x, bottom + 1, c); if (c[1] > 0.0) { e2 += 2; } @@ -516,8 +515,8 @@ void SMAABlendingWeightCalculationOperation::executePixel(float output[4], area(d1, d2, e1, e2, output + 2); /* B, A */ /* Fix corners: */ - if (m_corner_rounding) { - detectVerticalCornerPattern(output + 2, x, top, bottom, d1, d2); + if (corner_rounding_) { + detect_vertical_corner_pattern(output + 2, x, top, bottom, d1, d2); } } } @@ -545,7 +544,7 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( if (edges[1] > 0.0f) { /* Diagonals have both north and west edges, so calculating weights for them */ /* in one of the boundaries is enough. */ - calculateDiagWeights(x, y, edges, it.out); + calculate_diag_weights(x, y, edges, it.out); /* We give priority to diagonals, so if we find a diagonal we skip. */ /* horizontal/vertical processing. */ @@ -554,8 +553,8 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( } /* Find the distance to the left and the right: */ - int left = searchXLeft(x, y); - int right = searchXRight(x, y); + int left = search_xleft(x, y); + int right = search_xright(x, y); int d1 = x - left, d2 = right - x; /* Fetch the left and right crossing edges: */ @@ -582,21 +581,21 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( area(d1, d2, e1, e2, it.out); /* R, G */ /* Fix corners: */ - if (m_corner_rounding) { - detectHorizontalCornerPattern(it.out, left, right, y, d1, d2); + if (corner_rounding_) { + detect_horizontal_corner_pattern(it.out, left, right, y, d1, d2); } } /* Edge at west */ if (edges[0] > 0.0f) { /* Did we already do diagonal search for this west edge from the left neighboring pixel? */ - if (isVerticalSearchUnneeded(x, y)) { + if (is_vertical_search_unneeded(x, y)) { continue; } /* Find the distance to the top and the bottom: */ - int top = searchYUp(x, y); - int bottom = searchYDown(x, y); + int top = search_yup(x, y); + int bottom = search_ydown(x, y); int d1 = y - top, d2 = bottom - y; /* Fetch the top and bottom crossing edges: */ @@ -622,31 +621,31 @@ void SMAABlendingWeightCalculationOperation::update_memory_buffer_partial( area(d1, d2, e1, e2, it.out + 2); /* B, A */ /* Fix corners: */ - if (m_corner_rounding) { - detectVerticalCornerPattern(it.out + 2, x, top, bottom, d1, d2); + if (corner_rounding_) { + detect_vertical_corner_pattern(it.out + 2, x, top, bottom, d1, d2); } } } } -void SMAABlendingWeightCalculationOperation::deinitExecution() +void SMAABlendingWeightCalculationOperation::deinit_execution() { - this->m_imageReader = nullptr; + image_reader_ = nullptr; } -bool SMAABlendingWeightCalculationOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool SMAABlendingWeightCalculationOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); - newInput.xmin = input->xmin - - fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); - newInput.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); - newInput.ymin = input->ymin - - fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); + new_input.xmax = input->xmax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG + 1); + new_input.xmin = input->xmin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG + 1); + new_input.ymax = input->ymax + fmax(SMAA_MAX_SEARCH_STEPS, SMAA_MAX_SEARCH_STEPS_DIAG); + new_input.ymin = input->ymin - + fmax(fmax(SMAA_MAX_SEARCH_STEPS - 1, 1), SMAA_MAX_SEARCH_STEPS_DIAG); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void SMAABlendingWeightCalculationOperation::get_area_of_interest(const int UNUSED(input_idx), @@ -668,7 +667,7 @@ void SMAABlendingWeightCalculationOperation::get_area_of_interest(const int UNUS /** * These functions allows to perform diagonal pattern searches. */ -int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, bool *found) +int SMAABlendingWeightCalculationOperation::search_diag1(int x, int y, int dir, bool *found) { float e[4]; int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; @@ -691,7 +690,7 @@ int SMAABlendingWeightCalculationOperation::searchDiag1(int x, int y, int dir, b return x - dir; } -int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, bool *found) +int SMAABlendingWeightCalculationOperation::search_diag2(int x, int y, int dir, bool *found) { float e[4]; int end = x + SMAA_MAX_SEARCH_STEPS_DIAG * dir; @@ -718,10 +717,10 @@ int SMAABlendingWeightCalculationOperation::searchDiag2(int x, int y, int dir, b /** * This searches for diagonal patterns and returns the corresponding weights. */ -void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, - int y, - const float edges[2], - float weights[2]) +void SMAABlendingWeightCalculationOperation::calculate_diag_weights(int x, + int y, + const float edges[2], + float weights[2]) { int d1, d2; bool d1_found, d2_found; @@ -735,13 +734,13 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, /* Search for the line ends: */ if (edges[0] > 0.0f) { - d1 = x - searchDiag1(x, y, -1, &d1_found); + d1 = x - search_diag1(x, y, -1, &d1_found); } else { d1 = 0; d1_found = true; } - d2 = searchDiag1(x, y, 1, &d2_found) - x; + d2 = search_diag1(x, y, 1, &d2_found) - x; if (d1 + d2 > 2) { /* d1 + d2 + 1 > 3 */ int e1 = 0, e2 = 0; @@ -779,10 +778,10 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, } /* Search for the line ends: */ - d1 = x - searchDiag2(x, y, -1, &d1_found); + d1 = x - search_diag2(x, y, -1, &d1_found); sample_image_fn_(x + 1, y, e); if (e[0] > 0.0f) { - d2 = searchDiag2(x, y, 1, &d2_found) - x; + d2 = search_diag2(x, y, 1, &d2_found) - x; } else { d2 = 0; @@ -827,7 +826,7 @@ void SMAABlendingWeightCalculationOperation::calculateDiagWeights(int x, } } -bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int y) +bool SMAABlendingWeightCalculationOperation::is_vertical_search_unneeded(int x, int y) { int d1, d2; bool found; @@ -840,12 +839,12 @@ bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int /* Search for the line ends: */ sample_image_fn_(x - 1, y, e); if (e[1] > 0.0f) { - d1 = x - searchDiag2(x - 1, y, -1, &found); + d1 = x - search_diag2(x - 1, y, -1, &found); } else { d1 = 0; } - d2 = searchDiag2(x - 1, y, 1, &found) - x; + d2 = search_diag2(x - 1, y, 1, &found) - x; return (d1 + d2 > 2); /* d1 + d2 + 1 > 3 */ } @@ -853,7 +852,7 @@ bool SMAABlendingWeightCalculationOperation::isVerticalSearchUnneeded(int x, int /*-----------------------------------------------------------------------------*/ /* Horizontal/Vertical Search Functions */ -int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) +int SMAABlendingWeightCalculationOperation::search_xleft(int x, int y) { int end = x - SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -876,7 +875,7 @@ int SMAABlendingWeightCalculationOperation::searchXLeft(int x, int y) return x + 1; } -int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) +int SMAABlendingWeightCalculationOperation::search_xright(int x, int y) { int end = x + SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -897,7 +896,7 @@ int SMAABlendingWeightCalculationOperation::searchXRight(int x, int y) return x - 1; } -int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) +int SMAABlendingWeightCalculationOperation::search_yup(int x, int y) { int end = y - SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -920,7 +919,7 @@ int SMAABlendingWeightCalculationOperation::searchYUp(int x, int y) return y + 1; } -int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) +int SMAABlendingWeightCalculationOperation::search_ydown(int x, int y) { int end = y + SMAA_MAX_SEARCH_STEPS; float e[4]; @@ -944,11 +943,11 @@ int SMAABlendingWeightCalculationOperation::searchYDown(int x, int y) /*-----------------------------------------------------------------------------*/ /* Corner Detection Functions */ -void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( +void SMAABlendingWeightCalculationOperation::detect_horizontal_corner_pattern( float weights[2], int left, int right, int y, int d1, int d2) { float factor[2] = {1.0f, 1.0f}; - float rounding = m_corner_rounding / 100.0f; + float rounding = corner_rounding_ / 100.0f; float e[4]; /* Reduce blending for pixels in the center of a line. */ @@ -973,11 +972,11 @@ void SMAABlendingWeightCalculationOperation::detectHorizontalCornerPattern( weights[1] *= CLAMPIS(factor[1], 0.0f, 1.0f); } -void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( +void SMAABlendingWeightCalculationOperation::detect_vertical_corner_pattern( float weights[2], int x, int top, int bottom, int d1, int d2) { float factor[2] = {1.0f, 1.0f}; - float rounding = m_corner_rounding / 100.0f; + float rounding = corner_rounding_ / 100.0f; float e[4]; /* Reduce blending for pixels in the center of a line. */ @@ -1008,43 +1007,43 @@ void SMAABlendingWeightCalculationOperation::detectVerticalCornerPattern( SMAANeighborhoodBlendingOperation::SMAANeighborhoodBlendingOperation() { - this->addInputSocket(DataType::Color); /* image */ - this->addInputSocket(DataType::Color); /* blend */ - this->addOutputSocket(DataType::Color); - this->flags.complex = true; - this->m_image1Reader = nullptr; - this->m_image2Reader = nullptr; + this->add_input_socket(DataType::Color); /* image */ + this->add_input_socket(DataType::Color); /* blend */ + this->add_output_socket(DataType::Color); + flags_.complex = true; + image1Reader_ = nullptr; + image2Reader_ = nullptr; } -void *SMAANeighborhoodBlendingOperation::initializeTileData(rcti *rect) +void *SMAANeighborhoodBlendingOperation::initialize_tile_data(rcti *rect) { - return getInputOperation(0)->initializeTileData(rect); + return get_input_operation(0)->initialize_tile_data(rect); } -void SMAANeighborhoodBlendingOperation::initExecution() +void SMAANeighborhoodBlendingOperation::init_execution() { - this->m_image1Reader = this->getInputSocketReader(0); - this->m_image2Reader = this->getInputSocketReader(1); + image1Reader_ = this->get_input_socket_reader(0); + image2Reader_ = this->get_input_socket_reader(1); } -void SMAANeighborhoodBlendingOperation::executePixel(float output[4], - int x, - int y, - void * /*data*/) +void SMAANeighborhoodBlendingOperation::execute_pixel(float output[4], + int x, + int y, + void * /*data*/) { float w[4]; /* Fetch the blending weights for current pixel: */ - sample(m_image2Reader, x, y, w); + sample(image2Reader_, x, y, w); float left = w[2], top = w[0]; - sample(m_image2Reader, x + 1, y, w); + sample(image2Reader_, x + 1, y, w); float right = w[3]; - sample(m_image2Reader, x, y + 1, w); + sample(image2Reader_, x, y + 1, w); float bottom = w[1]; /* Is there any blending weight with a value greater than 0.0? */ if (right + bottom + left + top < 1e-5f) { - sample(m_image1Reader, x, y, output); + sample(image1Reader_, x, y, output); return; } @@ -1068,8 +1067,8 @@ void SMAANeighborhoodBlendingOperation::executePixel(float output[4], } /* We exploit bilinear filtering to mix current pixel with the chosen neighbor: */ - samplefunc(m_image1Reader, x, y, offset1, color1); - samplefunc(m_image1Reader, x, y, offset2, color2); + samplefunc(image1Reader_, x, y, offset1, color1); + samplefunc(image1Reader_, x, y, offset2, color2); mul_v4_v4fl(output, color1, weight1); madd_v4_v4fl(output, color2, weight2); @@ -1128,23 +1127,23 @@ void SMAANeighborhoodBlendingOperation::update_memory_buffer_partial(MemoryBuffe } } -void SMAANeighborhoodBlendingOperation::deinitExecution() +void SMAANeighborhoodBlendingOperation::deinit_execution() { - this->m_image1Reader = nullptr; - this->m_image2Reader = nullptr; + image1Reader_ = nullptr; + image2Reader_ = nullptr; } -bool SMAANeighborhoodBlendingOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool SMAANeighborhoodBlendingOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = input->xmax + 1; - newInput.xmin = input->xmin - 1; - newInput.ymax = input->ymax + 1; - newInput.ymin = input->ymin - 1; + new_input.xmax = input->xmax + 1; + new_input.xmin = input->xmin - 1; + new_input.ymax = input->ymax + 1; + new_input.ymin = input->ymin - 1; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void SMAANeighborhoodBlendingOperation::get_area_of_interest(const int UNUSED(input_idx), diff --git a/source/blender/compositor/operations/COM_SMAAOperation.h b/source/blender/compositor/operations/COM_SMAAOperation.h index 91b9299ee43..65a88d43fdf 100644 --- a/source/blender/compositor/operations/COM_SMAAOperation.h +++ b/source/blender/compositor/operations/COM_SMAAOperation.h @@ -29,11 +29,11 @@ namespace blender::compositor { class SMAAEdgeDetectionOperation : public MultiThreadedOperation { protected: - SocketReader *m_imageReader; - SocketReader *m_valueReader; + SocketReader *image_reader_; + SocketReader *value_reader_; - float m_threshold; - float m_contrast_limit; + float threshold_; + float contrast_limit_; public: SMAAEdgeDetectionOperation(); @@ -41,25 +41,25 @@ class SMAAEdgeDetectionOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - virtual void executePixel(float output[4], int x, int y, void *data) override; + virtual void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setThreshold(float threshold); + void set_threshold(float threshold); - void setLocalContrastAdaptationFactor(float factor); + void set_local_contrast_adaptation_factor(float factor); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, @@ -72,9 +72,9 @@ class SMAAEdgeDetectionOperation : public MultiThreadedOperation { class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { private: - SocketReader *m_imageReader; + SocketReader *image_reader_; std::function sample_image_fn_; - int m_corner_rounding; + int corner_rounding_; public: SMAABlendingWeightCalculationOperation(); @@ -82,24 +82,24 @@ class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setCornerRounding(float rounding); + void set_corner_rounding(float rounding); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, @@ -111,20 +111,22 @@ class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { private: /* Diagonal Search Functions */ - int searchDiag1(int x, int y, int dir, bool *found); - int searchDiag2(int x, int y, int dir, bool *found); - void calculateDiagWeights(int x, int y, const float edges[2], float weights[2]); - bool isVerticalSearchUnneeded(int x, int y); + int search_diag1(int x, int y, int dir, bool *found); + int search_diag2(int x, int y, int dir, bool *found); + void calculate_diag_weights(int x, int y, const float edges[2], float weights[2]); + bool is_vertical_search_unneeded(int x, int y); /* Horizontal/Vertical Search Functions */ - int searchXLeft(int x, int y); - int searchXRight(int x, int y); - int searchYUp(int x, int y); - int searchYDown(int x, int y); + int search_xleft(int x, int y); + int search_xright(int x, int y); + int search_yup(int x, int y); + int search_ydown(int x, int y); /* Corner Detection Functions */ - void detectHorizontalCornerPattern(float weights[2], int left, int right, int y, int d1, int d2); - void detectVerticalCornerPattern(float weights[2], int x, int top, int bottom, int d1, int d2); + void detect_horizontal_corner_pattern( + float weights[2], int left, int right, int y, int d1, int d2); + void detect_vertical_corner_pattern( + float weights[2], int x, int top, int bottom, int d1, int d2); }; /*-----------------------------------------------------------------------------*/ @@ -132,8 +134,8 @@ class SMAABlendingWeightCalculationOperation : public MultiThreadedOperation { class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation { private: - SocketReader *m_image1Reader; - SocketReader *m_image2Reader; + SocketReader *image1Reader_; + SocketReader *image2Reader_; public: SMAANeighborhoodBlendingOperation(); @@ -141,22 +143,22 @@ class SMAANeighborhoodBlendingOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; - void *initializeTileData(rcti *rect) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_ScaleOperation.cc b/source/blender/compositor/operations/COM_ScaleOperation.cc index 0161b837915..350934b0d3b 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.cc +++ b/source/blender/compositor/operations/COM_ScaleOperation.cc @@ -31,11 +31,16 @@ namespace blender::compositor { BaseScaleOperation::BaseScaleOperation() { #ifdef USE_FORCE_BILINEAR - m_sampler = (int)PixelSampler::Bilinear; + sampler_ = (int)PixelSampler::Bilinear; #else - m_sampler = -1; + sampler_ = -1; #endif - m_variable_size = false; + variable_size_ = false; +} + +void BaseScaleOperation::set_scale_canvas_max_size(Size2f size) +{ + max_scale_canvas_size_ = size; } ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color) @@ -44,68 +49,117 @@ ScaleOperation::ScaleOperation() : ScaleOperation(DataType::Color) ScaleOperation::ScaleOperation(DataType data_type) : BaseScaleOperation() { - this->addInputSocket(data_type); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(data_type); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; + this->add_input_socket(data_type, ResizeMode::None); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(data_type); + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; } float ScaleOperation::get_constant_scale(const int input_op_idx, const float factor) { - const bool is_constant = getInputOperation(input_op_idx)->get_flags().is_constant_operation; + const bool is_constant = get_input_operation(input_op_idx)->get_flags().is_constant_operation; if (is_constant) { - return ((ConstantOperation *)getInputOperation(input_op_idx))->get_constant_elem()[0] * factor; + return ((ConstantOperation *)get_input_operation(input_op_idx))->get_constant_elem()[0] * + factor; } return 1.0f; } -float ScaleOperation::get_constant_scale_x() +float ScaleOperation::get_constant_scale_x(const float width) { - return get_constant_scale(1, get_relative_scale_x_factor()); + return get_constant_scale(X_INPUT_INDEX, get_relative_scale_x_factor(width)); } -float ScaleOperation::get_constant_scale_y() +float ScaleOperation::get_constant_scale_y(const float height) { - return get_constant_scale(2, get_relative_scale_y_factor()); + return get_constant_scale(Y_INPUT_INDEX, get_relative_scale_y_factor(height)); } -void ScaleOperation::scale_area( - rcti &rect, float center_x, float center_y, float scale_x, float scale_y) +bool ScaleOperation::is_scaling_variable() { - rect.xmin = scale_coord(rect.xmin, center_x, scale_x); - rect.xmax = scale_coord(rect.xmax, center_x, scale_x); - rect.ymin = scale_coord(rect.ymin, center_y, scale_y); - rect.ymax = scale_coord(rect.ymax, center_y, scale_y); + return !get_input_operation(X_INPUT_INDEX)->get_flags().is_constant_operation || + !get_input_operation(Y_INPUT_INDEX)->get_flags().is_constant_operation; } -void ScaleOperation::scale_area(rcti &rect, float scale_x, float scale_y) +void ScaleOperation::scale_area(rcti &area, float relative_scale_x, float relative_scale_y) { - scale_area(rect, m_centerX, m_centerY, scale_x, scale_y); + const rcti src_area = area; + const float center_x = BLI_rcti_size_x(&area) / 2.0f; + const float center_y = BLI_rcti_size_y(&area) / 2.0f; + area.xmin = floorf(scale_coord(area.xmin, center_x, relative_scale_x)); + area.xmax = ceilf(scale_coord(area.xmax, center_x, relative_scale_x)); + area.ymin = floorf(scale_coord(area.ymin, center_y, relative_scale_y)); + area.ymax = ceilf(scale_coord(area.ymax, center_y, relative_scale_y)); + + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(src_area, area, scale_offset_x, scale_offset_y); + BLI_rcti_translate(&area, -scale_offset_x, -scale_offset_y); +} + +void ScaleOperation::clamp_area_size_max(rcti &area, Size2f max_size) +{ + + if (BLI_rcti_size_x(&area) > max_size.x) { + area.xmax = area.xmin + max_size.x; + } + if (BLI_rcti_size_y(&area) > max_size.y) { + area.ymax = area.ymin + max_size.y; + } } void ScaleOperation::init_data() { - m_centerX = getWidth() / 2.0f; - m_centerY = getHeight() / 2.0f; + canvas_center_x_ = canvas_.xmin + get_width() / 2.0f; + canvas_center_y_ = canvas_.ymin + get_height() / 2.0f; } -void ScaleOperation::initExecution() +void ScaleOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputXOperation = this->getInputSocketReader(1); - this->m_inputYOperation = this->getInputSocketReader(2); + input_operation_ = this->get_input_socket_reader(0); + input_xoperation_ = this->get_input_socket_reader(1); + input_yoperation_ = this->get_input_socket_reader(2); } -void ScaleOperation::deinitExecution() +void ScaleOperation::deinit_execution() { - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; +} + +void ScaleOperation::get_scale_offset(const rcti &input_canvas, + const rcti &scale_canvas, + float &r_scale_offset_x, + float &r_scale_offset_y) +{ + r_scale_offset_x = (BLI_rcti_size_x(&input_canvas) - BLI_rcti_size_x(&scale_canvas)) / 2.0f; + r_scale_offset_y = (BLI_rcti_size_y(&input_canvas) - BLI_rcti_size_y(&scale_canvas)) / 2.0f; +} + +void ScaleOperation::get_scale_area_of_interest(const rcti &input_canvas, + const rcti &scale_canvas, + const float relative_scale_x, + const float relative_scale_y, + const rcti &output_area, + rcti &r_input_area) +{ + const float scale_center_x = BLI_rcti_size_x(&input_canvas) / 2.0f; + const float scale_center_y = BLI_rcti_size_y(&input_canvas) / 2.0f; + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(input_canvas, scale_canvas, scale_offset_x, scale_offset_y); + + r_input_area.xmin = floorf( + scale_coord_inverted(output_area.xmin + scale_offset_x, scale_center_x, relative_scale_x)); + r_input_area.xmax = ceilf( + scale_coord_inverted(output_area.xmax + scale_offset_x, scale_center_x, relative_scale_x)); + r_input_area.ymin = floorf( + scale_coord_inverted(output_area.ymin + scale_offset_y, scale_center_y, relative_scale_y)); + r_input_area.ymax = ceilf( + scale_coord_inverted(output_area.ymax + scale_offset_y, scale_center_y, relative_scale_y)); } void ScaleOperation::get_area_of_interest(const int input_idx, @@ -113,32 +167,87 @@ void ScaleOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { r_input_area = output_area; - if (input_idx != 0 || m_variable_size) { + if (input_idx != 0 || is_scaling_variable()) { return; } - float scale_x = get_constant_scale_x(); - float scale_y = get_constant_scale_y(); - scale_area(r_input_area, scale_x, scale_y); - expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); + NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); + const float scale_x = get_constant_scale_x(image_op->get_width()); + const float scale_y = get_constant_scale_y(image_op->get_height()); + + get_scale_area_of_interest( + image_op->get_canvas(), this->get_canvas(), scale_x, scale_y, output_area, r_input_area); + expand_area_for_sampler(r_input_area, (PixelSampler)sampler_); } void ScaleOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const MemoryBuffer *input_img = inputs[0]; - MemoryBuffer *input_x = inputs[1]; - MemoryBuffer *input_y = inputs[2]; - const float scale_x_factor = get_relative_scale_x_factor(); - const float scale_y_factor = get_relative_scale_y_factor(); + NodeOperation *input_image_op = get_input_operation(IMAGE_INPUT_INDEX); + const int input_image_width = input_image_op->get_width(); + const int input_image_height = input_image_op->get_height(); + const float scale_x_factor = get_relative_scale_x_factor(input_image_width); + const float scale_y_factor = get_relative_scale_y_factor(input_image_height); + const float scale_center_x = input_image_width / 2.0f; + const float scale_center_y = input_image_height / 2.0f; + float from_scale_offset_x, from_scale_offset_y; + ScaleOperation::get_scale_offset( + input_image_op->get_canvas(), this->get_canvas(), from_scale_offset_x, from_scale_offset_y); + + const MemoryBuffer *input_image = inputs[IMAGE_INPUT_INDEX]; + MemoryBuffer *input_x = inputs[X_INPUT_INDEX]; + MemoryBuffer *input_y = inputs[Y_INPUT_INDEX]; BuffersIterator it = output->iterate_with({input_x, input_y}, area); for (; !it.is_end(); ++it) { const float rel_scale_x = *it.in(0) * scale_x_factor; const float rel_scale_y = *it.in(1) * scale_y_factor; - const float scaled_x = scale_coord(it.x, m_centerX, rel_scale_x); - const float scaled_y = scale_coord(it.y, m_centerY, rel_scale_y); - input_img->read_elem_sampled(scaled_x, scaled_y, (PixelSampler)m_sampler, it.out); + const float scaled_x = scale_coord_inverted( + from_scale_offset_x + canvas_.xmin + it.x, scale_center_x, rel_scale_x); + const float scaled_y = scale_coord_inverted( + from_scale_offset_y + canvas_.ymin + it.y, scale_center_y, rel_scale_y); + + input_image->read_elem_sampled( + scaled_x - canvas_.xmin, scaled_y - canvas_.ymin, (PixelSampler)sampler_, it.out); + } +} + +void ScaleOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + if (execution_model_ == eExecutionModel::Tiled) { + NodeOperation::determine_canvas(preferred_area, r_area); + return; + } + + const bool image_determined = + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (image_determined) { + rcti image_canvas = r_area; + rcti unused; + NodeOperationInput *x_socket = get_input_socket(X_INPUT_INDEX); + NodeOperationInput *y_socket = get_input_socket(Y_INPUT_INDEX); + x_socket->determine_canvas(image_canvas, unused); + y_socket->determine_canvas(image_canvas, unused); + if (is_scaling_variable()) { + /* Do not scale canvas. */ + return; + } + + /* Determine scaled canvas. */ + const float input_width = BLI_rcti_size_x(&r_area); + const float input_height = BLI_rcti_size_y(&r_area); + const float scale_x = get_constant_scale_x(input_width); + const float scale_y = get_constant_scale_y(input_height); + scale_area(r_area, scale_x, scale_y); + const Size2f max_scale_size = {MAX2(input_width, max_scale_canvas_size_.x), + MAX2(input_height, max_scale_canvas_size_.y)}; + clamp_area_size_max(r_area, max_scale_size); + + /* Re-determine canvases of x and y constant inputs with scaled canvas as preferred. */ + get_input_operation(X_INPUT_INDEX)->unset_canvas(); + get_input_operation(Y_INPUT_INDEX)->unset_canvas(); + x_socket->determine_canvas(r_area, unused); + y_socket->determine_canvas(r_area, unused); } } @@ -150,231 +259,266 @@ ScaleRelativeOperation::ScaleRelativeOperation(DataType data_type) : ScaleOperat { } -void ScaleRelativeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ScaleRelativeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - PixelSampler effective_sampler = getEffectiveSampler(sampler); + PixelSampler effective_sampler = get_effective_sampler(sampler); float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); - this->m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); + input_xoperation_->read_sampled(scaleX, x, y, effective_sampler); + input_yoperation_->read_sampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; const float scy = scaleY[0]; - float nx = this->m_centerX + (x - this->m_centerX) / scx; - float ny = this->m_centerY + (y - this->m_centerY) / scy; - this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); + float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / scx; + float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / scy; + input_operation_->read_sampled(output, nx, ny, effective_sampler); } -bool ScaleRelativeOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ScaleRelativeOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - if (!m_variable_size) { + rcti new_input; + if (!variable_size_) { float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + input_xoperation_->read_sampled(scaleX, 0, 0, PixelSampler::Nearest); + input_yoperation_->read_sampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; - newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / scx + 1; - newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / scx - 1; - newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / scy + 1; - newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / scy - 1; + new_input.xmax = this->canvas_center_x_ + (input->xmax - this->canvas_center_x_) / scx + 1; + new_input.xmin = this->canvas_center_x_ + (input->xmin - this->canvas_center_x_) / scx - 1; + new_input.ymax = this->canvas_center_y_ + (input->ymax - this->canvas_center_y_) / scy + 1; + new_input.ymin = this->canvas_center_y_ + (input->ymin - this->canvas_center_y_) / scy - 1; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return BaseScaleOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } -void ScaleAbsoluteOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ScaleAbsoluteOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - PixelSampler effective_sampler = getEffectiveSampler(sampler); + PixelSampler effective_sampler = get_effective_sampler(sampler); float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, x, y, effective_sampler); - this->m_inputYOperation->readSampled(scaleY, x, y, effective_sampler); + input_xoperation_->read_sampled(scaleX, x, y, effective_sampler); + input_yoperation_->read_sampled(scaleY, x, y, effective_sampler); const float scx = scaleX[0]; /* Target absolute scale. */ const float scy = scaleY[0]; /* Target absolute scale. */ - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); /* Divide. */ - float relativeXScale = scx / width; - float relativeYScale = scy / height; + float relative_xscale = scx / width; + float relative_yscale = scy / height; - float nx = this->m_centerX + (x - this->m_centerX) / relativeXScale; - float ny = this->m_centerY + (y - this->m_centerY) / relativeYScale; + float nx = this->canvas_center_x_ + (x - this->canvas_center_x_) / relative_xscale; + float ny = this->canvas_center_y_ + (y - this->canvas_center_y_) / relative_yscale; - this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); + input_operation_->read_sampled(output, nx, ny, effective_sampler); } -bool ScaleAbsoluteOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ScaleAbsoluteOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - if (!m_variable_size) { + rcti new_input; + if (!variable_size_) { float scaleX[4]; float scaleY[4]; - this->m_inputXOperation->readSampled(scaleX, 0, 0, PixelSampler::Nearest); - this->m_inputYOperation->readSampled(scaleY, 0, 0, PixelSampler::Nearest); + input_xoperation_->read_sampled(scaleX, 0, 0, PixelSampler::Nearest); + input_yoperation_->read_sampled(scaleY, 0, 0, PixelSampler::Nearest); const float scx = scaleX[0]; const float scy = scaleY[0]; - const float width = this->getWidth(); - const float height = this->getHeight(); + const float width = this->get_width(); + const float height = this->get_height(); /* Divide. */ - float relateveXScale = scx / width; - float relateveYScale = scy / height; + float relateve_xscale = scx / width; + float relateve_yscale = scy / height; - newInput.xmax = this->m_centerX + (input->xmax - this->m_centerX) / relateveXScale; - newInput.xmin = this->m_centerX + (input->xmin - this->m_centerX) / relateveXScale; - newInput.ymax = this->m_centerY + (input->ymax - this->m_centerY) / relateveYScale; - newInput.ymin = this->m_centerY + (input->ymin - this->m_centerY) / relateveYScale; + new_input.xmax = this->canvas_center_x_ + + (input->xmax - this->canvas_center_x_) / relateve_xscale; + new_input.xmin = this->canvas_center_x_ + + (input->xmin - this->canvas_center_x_) / relateve_xscale; + new_input.ymax = this->canvas_center_y_ + + (input->ymax - this->canvas_center_y_) / relateve_yscale; + new_input.ymin = this->canvas_center_y_ + + (input->ymin - this->canvas_center_y_) / relateve_yscale; } else { - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; } - return ScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return ScaleOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } /* Absolute fixed size. */ ScaleFixedSizeOperation::ScaleFixedSizeOperation() : BaseScaleOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->m_is_offset = false; + this->add_input_socket(DataType::Color, ResizeMode::None); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); + input_operation_ = nullptr; + is_offset_ = false; } -void ScaleFixedSizeOperation::init_data() +void ScaleFixedSizeOperation::init_data(const rcti &input_canvas) { - const NodeOperation *input_op = getInputOperation(0); - this->m_relX = input_op->getWidth() / (float)this->m_newWidth; - this->m_relY = input_op->getHeight() / (float)this->m_newHeight; + const int input_width = BLI_rcti_size_x(&input_canvas); + const int input_height = BLI_rcti_size_y(&input_canvas); + rel_x_ = input_width / (float)new_width_; + rel_y_ = input_height / (float)new_height_; /* *** all the options below are for a fairly special case - camera framing *** */ - if (this->m_offsetX != 0.0f || this->m_offsetY != 0.0f) { - this->m_is_offset = true; + if (offset_x_ != 0.0f || offset_y_ != 0.0f) { + is_offset_ = true; - if (this->m_newWidth > this->m_newHeight) { - this->m_offsetX *= this->m_newWidth; - this->m_offsetY *= this->m_newWidth; + if (new_width_ > new_height_) { + offset_x_ *= new_width_; + offset_y_ *= new_width_; } else { - this->m_offsetX *= this->m_newHeight; - this->m_offsetY *= this->m_newHeight; + offset_x_ *= new_height_; + offset_y_ *= new_height_; } } - if (this->m_is_aspect) { + if (is_aspect_) { /* apply aspect from clip */ - const float w_src = input_op->getWidth(); - const float h_src = input_op->getHeight(); + const float w_src = input_width; + const float h_src = input_height; /* destination aspect is already applied from the camera frame */ - const float w_dst = this->m_newWidth; - const float h_dst = this->m_newHeight; + const float w_dst = new_width_; + const float h_dst = new_height_; const float asp_src = w_src / h_src; const float asp_dst = w_dst / h_dst; if (fabsf(asp_src - asp_dst) >= FLT_EPSILON) { - if ((asp_src > asp_dst) == (this->m_is_crop == true)) { + if ((asp_src > asp_dst) == (is_crop_ == true)) { /* fit X */ const float div = asp_src / asp_dst; - this->m_relX /= div; - this->m_offsetX += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; + rel_x_ /= div; + offset_x_ += ((w_src - (w_src * div)) / (w_src / w_dst)) / 2.0f; + if (is_crop_ && execution_model_ == eExecutionModel::FullFrame) { + int fit_width = new_width_ * div; + if (fit_width > max_scale_canvas_size_.x) { + fit_width = max_scale_canvas_size_.x; + } + + const int added_width = fit_width - new_width_; + new_width_ += added_width; + offset_x_ += added_width / 2.0f; + } } else { /* fit Y */ const float div = asp_dst / asp_src; - this->m_relY /= div; - this->m_offsetY += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; + rel_y_ /= div; + offset_y_ += ((h_src - (h_src * div)) / (h_src / h_dst)) / 2.0f; + if (is_crop_ && execution_model_ == eExecutionModel::FullFrame) { + int fit_height = new_height_ * div; + if (fit_height > max_scale_canvas_size_.y) { + fit_height = max_scale_canvas_size_.y; + } + + const int added_height = fit_height - new_height_; + new_height_ += added_height; + offset_y_ += added_height / 2.0f; + } } - this->m_is_offset = true; + is_offset_ = true; } } /* *** end framing options *** */ } -void ScaleFixedSizeOperation::initExecution() +void ScaleFixedSizeOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); + input_operation_ = this->get_input_socket_reader(0); } -void ScaleFixedSizeOperation::deinitExecution() +void ScaleFixedSizeOperation::deinit_execution() { - this->m_inputOperation = nullptr; + input_operation_ = nullptr; } -void ScaleFixedSizeOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ScaleFixedSizeOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - PixelSampler effective_sampler = getEffectiveSampler(sampler); + PixelSampler effective_sampler = get_effective_sampler(sampler); - if (this->m_is_offset) { - float nx = ((x - this->m_offsetX) * this->m_relX); - float ny = ((y - this->m_offsetY) * this->m_relY); - this->m_inputOperation->readSampled(output, nx, ny, effective_sampler); + if (is_offset_) { + float nx = ((x - offset_x_) * rel_x_); + float ny = ((y - offset_y_) * rel_y_); + input_operation_->read_sampled(output, nx, ny, effective_sampler); } else { - this->m_inputOperation->readSampled( - output, x * this->m_relX, y * this->m_relY, effective_sampler); + input_operation_->read_sampled(output, x * rel_x_, y * rel_y_, effective_sampler); } } -bool ScaleFixedSizeOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool ScaleFixedSizeOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; + rcti new_input; - newInput.xmax = (input->xmax - m_offsetX) * this->m_relX + 1; - newInput.xmin = (input->xmin - m_offsetX) * this->m_relX; - newInput.ymax = (input->ymax - m_offsetY) * this->m_relY + 1; - newInput.ymin = (input->ymin - m_offsetY) * this->m_relY; + new_input.xmax = (input->xmax - offset_x_) * rel_x_ + 1; + new_input.xmin = (input->xmin - offset_x_) * rel_x_; + new_input.ymax = (input->ymax - offset_y_) * rel_y_ + 1; + new_input.ymin = (input->ymin - offset_y_) * rel_y_; - return BaseScaleOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return BaseScaleOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } -void ScaleFixedSizeOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void ScaleFixedSizeOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - unsigned int nr[2]; - nr[0] = this->m_newWidth; - nr[1] = this->m_newHeight; - BaseScaleOperation::determineResolution(resolution, nr); - resolution[0] = this->m_newWidth; - resolution[1] = this->m_newHeight; + rcti local_preferred = preferred_area; + local_preferred.xmax = local_preferred.xmin + new_width_; + local_preferred.ymax = local_preferred.ymin + new_height_; + rcti input_canvas; + const bool input_determined = get_input_socket(0)->determine_canvas(local_preferred, + input_canvas); + if (input_determined) { + init_data(input_canvas); + r_area = input_canvas; + if (execution_model_ == eExecutionModel::FullFrame) { + r_area.xmin /= rel_x_; + r_area.ymin /= rel_y_; + r_area.xmin += offset_x_; + r_area.ymin += offset_y_; + } + + r_area.xmax = r_area.xmin + new_width_; + r_area.ymax = r_area.ymin + new_height_; + } } void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, @@ -383,11 +527,12 @@ void ScaleFixedSizeOperation::get_area_of_interest(const int input_idx, { BLI_assert(input_idx == 0); UNUSED_VARS_NDEBUG(input_idx); - r_input_area.xmax = (output_area.xmax - m_offsetX) * this->m_relX; - r_input_area.xmin = (output_area.xmin - m_offsetX) * this->m_relX; - r_input_area.ymax = (output_area.ymax - m_offsetY) * this->m_relY; - r_input_area.ymin = (output_area.ymin - m_offsetY) * this->m_relY; - expand_area_for_sampler(r_input_area, (PixelSampler)m_sampler); + + r_input_area.xmax = ceilf((output_area.xmax - offset_x_) * rel_x_); + r_input_area.xmin = floorf((output_area.xmin - offset_x_) * rel_x_); + r_input_area.ymax = ceilf((output_area.ymax - offset_y_) * rel_y_); + r_input_area.ymin = floorf((output_area.ymin - offset_y_) * rel_y_); + expand_area_for_sampler(r_input_area, (PixelSampler)sampler_); } void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -395,18 +540,21 @@ void ScaleFixedSizeOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *input_img = inputs[0]; - PixelSampler sampler = (PixelSampler)m_sampler; + PixelSampler sampler = (PixelSampler)sampler_; BuffersIterator it = output->iterate_with({}, area); - if (this->m_is_offset) { + if (is_offset_) { for (; !it.is_end(); ++it) { - const float nx = (it.x - this->m_offsetX) * this->m_relX; - const float ny = (it.y - this->m_offsetY) * this->m_relY; - input_img->read_elem_sampled(nx, ny, sampler, it.out); + const float nx = (canvas_.xmin + it.x - offset_x_) * rel_x_; + const float ny = (canvas_.ymin + it.y - offset_y_) * rel_y_; + input_img->read_elem_sampled(nx - canvas_.xmin, ny - canvas_.ymin, sampler, it.out); } } else { for (; !it.is_end(); ++it) { - input_img->read_elem_sampled(it.x * this->m_relX, it.y * this->m_relY, sampler, it.out); + input_img->read_elem_sampled((canvas_.xmin + it.x) * rel_x_ - canvas_.xmin, + (canvas_.ymin + it.y) * rel_y_ - canvas_.ymin, + sampler, + it.out); } } } diff --git a/source/blender/compositor/operations/COM_ScaleOperation.h b/source/blender/compositor/operations/COM_ScaleOperation.h index 65762d1ce62..7710aa34c54 100644 --- a/source/blender/compositor/operations/COM_ScaleOperation.h +++ b/source/blender/compositor/operations/COM_ScaleOperation.h @@ -24,37 +24,48 @@ namespace blender::compositor { class BaseScaleOperation : public MultiThreadedOperation { public: - void setSampler(PixelSampler sampler) + static constexpr float DEFAULT_MAX_SCALE_CANVAS_SIZE = 12000; + + public: + void set_sampler(PixelSampler sampler) { - this->m_sampler = (int)sampler; + sampler_ = (int)sampler; } - void setVariableSize(bool variable_size) + void set_variable_size(bool variable_size) { - m_variable_size = variable_size; + variable_size_ = variable_size; }; + void set_scale_canvas_max_size(Size2f size); + protected: BaseScaleOperation(); - PixelSampler getEffectiveSampler(PixelSampler sampler) + PixelSampler get_effective_sampler(PixelSampler sampler) { - return (m_sampler == -1) ? sampler : (PixelSampler)m_sampler; + return (sampler_ == -1) ? sampler : (PixelSampler)sampler_; } - int m_sampler; - bool m_variable_size; + Size2f max_scale_canvas_size_ = {DEFAULT_MAX_SCALE_CANVAS_SIZE, DEFAULT_MAX_SCALE_CANVAS_SIZE}; + int sampler_; + /* TODO(manzanilla): to be removed with tiled implementation. */ + bool variable_size_; }; class ScaleOperation : public BaseScaleOperation { public: - static constexpr float MIN_SCALE = 0.0001f; + static constexpr float MIN_RELATIVE_SCALE = 0.0001f; protected: - SocketReader *m_inputOperation; - SocketReader *m_inputXOperation; - SocketReader *m_inputYOperation; - float m_centerX; - float m_centerY; + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int X_INPUT_INDEX = 1; + static constexpr int Y_INPUT_INDEX = 2; + + SocketReader *input_operation_; + SocketReader *input_xoperation_; + SocketReader *input_yoperation_; + float canvas_center_x_; + float canvas_center_y_; public: ScaleOperation(); @@ -62,43 +73,66 @@ class ScaleOperation : public BaseScaleOperation { static float scale_coord(const float coord, const float center, const float relative_scale) { - return center + (coord - center) / MAX2(relative_scale, MIN_SCALE); + return center + (coord - center) * MAX2(relative_scale, MIN_RELATIVE_SCALE); } - static void scale_area(rcti &rect, float center_x, float center_y, float scale_x, float scale_y); + + static float scale_coord_inverted(const float coord, + const float center, + const float relative_scale) + { + return center + (coord - center) / MAX2(relative_scale, MIN_RELATIVE_SCALE); + } + + static void get_scale_offset(const rcti &input_canvas, + const rcti &scale_canvas, + float &r_scale_offset_x, + float &r_scale_offset_y); + static void scale_area(rcti &area, float relative_scale_x, float relative_scale_y); + static void get_scale_area_of_interest(const rcti &input_canvas, + const rcti &scale_canvas, + const float relative_scale_x, + const float relative_scale_y, + const rcti &output_area, + rcti &r_input_area); + static void clamp_area_size_max(rcti &area, Size2f max_size); void init_data() override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + protected: - virtual float get_relative_scale_x_factor() = 0; - virtual float get_relative_scale_y_factor() = 0; + virtual float get_relative_scale_x_factor(float width) = 0; + virtual float get_relative_scale_y_factor(float height) = 0; private: + bool is_scaling_variable(); float get_constant_scale(int input_op_idx, float factor); - float get_constant_scale_x(); - float get_constant_scale_y(); - void scale_area(rcti &rect, float scale_x, float scale_y); + float get_constant_scale_x(float width); + float get_constant_scale_y(float height); }; class ScaleRelativeOperation : public ScaleOperation { public: ScaleRelativeOperation(); ScaleRelativeOperation(DataType data_type); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - float get_relative_scale_x_factor() override + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + float get_relative_scale_x_factor(float UNUSED(width)) override { return 1.0f; } - float get_relative_scale_y_factor() override + + float get_relative_scale_y_factor(float UNUSED(height)) override { return 1.0f; } @@ -106,74 +140,77 @@ class ScaleRelativeOperation : public ScaleOperation { class ScaleAbsoluteOperation : public ScaleOperation { public: - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - float get_relative_scale_x_factor() override + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + float get_relative_scale_x_factor(float width) override { - return 1.0f / getWidth(); + return 1.0f / width; } - float get_relative_scale_y_factor() override + + float get_relative_scale_y_factor(float height) override { - return 1.0f / getHeight(); + return 1.0f / height; } }; class ScaleFixedSizeOperation : public BaseScaleOperation { - SocketReader *m_inputOperation; - int m_newWidth; - int m_newHeight; - float m_relX; - float m_relY; + SocketReader *input_operation_; + int new_width_; + int new_height_; + float rel_x_; + float rel_y_; /* center is only used for aspect correction */ - float m_offsetX; - float m_offsetY; - bool m_is_aspect; - bool m_is_crop; + float offset_x_; + float offset_y_; + bool is_aspect_; + bool is_crop_; /* set from other properties on initialization, * check if we need to apply offset */ - bool m_is_offset; + bool is_offset_; public: ScaleFixedSizeOperation(); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void init_data() override; - void initExecution() override; - void deinitExecution() override; - void setNewWidth(int width) + void init_execution() override; + void deinit_execution() override; + void set_new_width(int width) { - this->m_newWidth = width; + new_width_ = width; } - void setNewHeight(int height) + void set_new_height(int height) { - this->m_newHeight = height; + new_height_ = height; } - void setIsAspect(bool is_aspect) + void set_is_aspect(bool is_aspect) { - this->m_is_aspect = is_aspect; + is_aspect_ = is_aspect; } - void setIsCrop(bool is_crop) + void set_is_crop(bool is_crop) { - this->m_is_crop = is_crop; + is_crop_ = is_crop; } - void setOffset(float x, float y) + void set_offset(float x, float y) { - this->m_offsetX = x; - this->m_offsetY = y; + offset_x_ = x; + offset_y_ = y; } void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; + + private: + void init_data(const rcti &input_canvas); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc index 628da686a42..e36770b11a1 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.cc @@ -17,11 +17,10 @@ */ #include "COM_ScreenLensDistortionOperation.h" + #include "COM_ConstantOperation.h" -#include "BLI_math.h" #include "BLI_rand.h" -#include "BLI_utildefines.h" #include "PIL_time.h" @@ -29,96 +28,96 @@ namespace blender::compositor { ScreenLensDistortionOperation::ScreenLensDistortionOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); - this->flags.complex = true; - this->m_inputProgram = nullptr; - this->m_distortion = 0.0f; - this->m_dispersion = 0.0f; - this->m_distortion_const = false; - this->m_dispersion_const = false; - this->m_variables_ready = false; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); + flags_.complex = true; + input_program_ = nullptr; + distortion_ = 0.0f; + dispersion_ = 0.0f; + distortion_const_ = false; + dispersion_const_ = false; + variables_ready_ = false; } -void ScreenLensDistortionOperation::setDistortion(float distortion) +void ScreenLensDistortionOperation::set_distortion(float distortion) { - m_distortion = distortion; - m_distortion_const = true; + distortion_ = distortion; + distortion_const_ = true; } -void ScreenLensDistortionOperation::setDispersion(float dispersion) +void ScreenLensDistortionOperation::set_dispersion(float dispersion) { - m_dispersion = dispersion; - m_dispersion_const = true; + dispersion_ = dispersion; + dispersion_const_ = true; } void ScreenLensDistortionOperation::init_data() { - this->m_cx = 0.5f * (float)getWidth(); - this->m_cy = 0.5f * (float)getHeight(); + cx_ = 0.5f * (float)get_width(); + cy_ = 0.5f * (float)get_height(); switch (execution_model_) { case eExecutionModel::FullFrame: { NodeOperation *distortion_op = get_input_operation(1); NodeOperation *dispersion_op = get_input_operation(2); - if (!m_distortion_const && distortion_op->get_flags().is_constant_operation) { - m_distortion = static_cast(distortion_op)->get_constant_elem()[0]; + if (!distortion_const_ && distortion_op->get_flags().is_constant_operation) { + distortion_ = static_cast(distortion_op)->get_constant_elem()[0]; } - if (!m_dispersion_const && distortion_op->get_flags().is_constant_operation) { - m_dispersion = static_cast(dispersion_op)->get_constant_elem()[0]; + if (!dispersion_const_ && distortion_op->get_flags().is_constant_operation) { + dispersion_ = static_cast(dispersion_op)->get_constant_elem()[0]; } - updateVariables(m_distortion, m_dispersion); + update_variables(distortion_, dispersion_); break; } case eExecutionModel::Tiled: { /* If both are constant, init variables once. */ - if (m_distortion_const && m_dispersion_const) { - updateVariables(m_distortion, m_dispersion); - m_variables_ready = true; + if (distortion_const_ && dispersion_const_) { + update_variables(distortion_, dispersion_); + variables_ready_ = true; } break; } } } -void ScreenLensDistortionOperation::initExecution() +void ScreenLensDistortionOperation::init_execution() { - this->m_inputProgram = this->getInputSocketReader(0); - this->initMutex(); + input_program_ = this->get_input_socket_reader(0); + this->init_mutex(); uint rng_seed = (uint)(PIL_check_seconds_timer_i() & UINT_MAX); - rng_seed ^= (uint)POINTER_AS_INT(m_inputProgram); - this->m_rng = BLI_rng_new(rng_seed); + rng_seed ^= (uint)POINTER_AS_INT(input_program_); + rng_ = BLI_rng_new(rng_seed); } -void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) +void *ScreenLensDistortionOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = this->m_inputProgram->initializeTileData(nullptr); + void *buffer = input_program_->initialize_tile_data(nullptr); /* get distortion/dispersion values once, by reading inputs at (0,0) * XXX this assumes invariable values (no image inputs), * we don't have a nice generic system for that yet */ - if (!m_variables_ready) { - this->lockMutex(); + if (!variables_ready_) { + this->lock_mutex(); - if (!m_distortion_const) { + if (!distortion_const_) { float result[4]; - getInputSocketReader(1)->readSampled(result, 0, 0, PixelSampler::Nearest); - m_distortion = result[0]; + get_input_socket_reader(1)->read_sampled(result, 0, 0, PixelSampler::Nearest); + distortion_ = result[0]; } - if (!m_dispersion_const) { + if (!dispersion_const_) { float result[4]; - getInputSocketReader(2)->readSampled(result, 0, 0, PixelSampler::Nearest); - m_dispersion = result[0]; + get_input_socket_reader(2)->read_sampled(result, 0, 0, PixelSampler::Nearest); + dispersion_ = result[0]; } - updateVariables(m_distortion, m_dispersion); - m_variables_ready = true; + update_variables(distortion_, dispersion_); + variables_ready_ = true; - this->unlockMutex(); + this->unlock_mutex(); } return buffer; @@ -126,15 +125,15 @@ void *ScreenLensDistortionOperation::initializeTileData(rcti * /*rect*/) void ScreenLensDistortionOperation::get_uv(const float xy[2], float uv[2]) const { - uv[0] = m_sc * ((xy[0] + 0.5f) - m_cx) / m_cx; - uv[1] = m_sc * ((xy[1] + 0.5f) - m_cy) / m_cy; + uv[0] = sc_ * ((xy[0] + 0.5f) - cx_) / cx_; + uv[1] = sc_ * ((xy[1] + 0.5f) - cy_) / cy_; } void ScreenLensDistortionOperation::distort_uv(const float uv[2], float t, float xy[2]) const { float d = 1.0f / (1.0f + sqrtf(t)); - xy[0] = (uv[0] * d + 0.5f) * getWidth() - 0.5f; - xy[1] = (uv[1] * d + 0.5f) * getHeight() - 0.5f; + xy[0] = (uv[0] * d + 0.5f) * get_width() - 0.5f; + xy[1] = (uv[1] * d + 0.5f) * get_height() - 0.5f; } bool ScreenLensDistortionOperation::get_delta(float r_sq, @@ -163,21 +162,21 @@ void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer, float color[4]; float dsf = len_v2v2(delta[a], delta[b]) + 1.0f; - int ds = m_jitter ? (dsf < 4.0f ? 2 : (int)sqrtf(dsf)) : (int)dsf; + int ds = jitter_ ? (dsf < 4.0f ? 2 : (int)sqrtf(dsf)) : (int)dsf; float sd = 1.0f / (float)ds; - float k4 = m_k4[a]; - float dk4 = m_dk4[a]; + float k4 = k4_[a]; + float dk4 = dk4_[a]; for (float z = 0; z < ds; z++) { - float tz = (z + (m_jitter ? BLI_rng_get_float(m_rng) : 0.5f)) * sd; + float tz = (z + (jitter_ ? BLI_rng_get_float(rng_) : 0.5f)) * sd; float t = 1.0f - (k4 + tz * dk4) * r_sq; float xy[2]; distort_uv(uv, t, xy); switch (execution_model_) { case eExecutionModel::Tiled: - buffer->readBilinear(color, xy[0], xy[1]); + buffer->read_bilinear(color, xy[0], xy[1]); break; case eExecutionModel::FullFrame: buffer->read_elem_bilinear(xy[0], xy[1], color); @@ -191,7 +190,7 @@ void ScreenLensDistortionOperation::accumulate(const MemoryBuffer *buffer, } } -void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, void *data) +void ScreenLensDistortionOperation::execute_pixel(float output[4], int x, int y, void *data) { MemoryBuffer *buffer = (MemoryBuffer *)data; float xy[2] = {(float)x, (float)y}; @@ -203,9 +202,9 @@ void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, float delta[3][2]; float sum[4] = {0, 0, 0, 0}; - bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]); - bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]); - bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]); + bool valid_r = get_delta(uv_dot, k4_[0], uv, delta[0]); + bool valid_g = get_delta(uv_dot, k4_[1], uv, delta[1]); + bool valid_b = get_delta(uv_dot, k4_[2], uv, delta[2]); if (valid_r && valid_g && valid_b) { accumulate(buffer, 0, 1, uv_dot, uv, delta, sum, count); @@ -229,11 +228,11 @@ void ScreenLensDistortionOperation::executePixel(float output[4], int x, int y, } } -void ScreenLensDistortionOperation::deinitExecution() +void ScreenLensDistortionOperation::deinit_execution() { - this->deinitMutex(); - this->m_inputProgram = nullptr; - BLI_rng_free(this->m_rng); + this->deinit_mutex(); + input_program_ = nullptr; + BLI_rng_free(rng_); } void ScreenLensDistortionOperation::determineUV(float result[6], float x, float y) const @@ -246,27 +245,27 @@ void ScreenLensDistortionOperation::determineUV(float result[6], float x, float copy_v2_v2(result + 0, xy); copy_v2_v2(result + 2, xy); copy_v2_v2(result + 4, xy); - get_delta(uv_dot, m_k4[0], uv, result + 0); - get_delta(uv_dot, m_k4[1], uv, result + 2); - get_delta(uv_dot, m_k4[2], uv, result + 4); + get_delta(uv_dot, k4_[0], uv, result + 0); + get_delta(uv_dot, k4_[1], uv, result + 2); + get_delta(uv_dot, k4_[2], uv, result + 4); } -bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( - rcti * /*input*/, ReadBufferOperation *readOperation, rcti *output) +bool ScreenLensDistortionOperation::determine_depending_area_of_interest( + rcti * /*input*/, ReadBufferOperation *read_operation, rcti *output) { - rcti newInputValue; - newInputValue.xmin = 0; - newInputValue.ymin = 0; - newInputValue.xmax = 2; - newInputValue.ymax = 2; + rcti new_input_value; + new_input_value.xmin = 0; + new_input_value.ymin = 0; + new_input_value.xmax = 2; + new_input_value.ymax = 2; - NodeOperation *operation = getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&newInputValue, readOperation, output)) { + NodeOperation *operation = get_input_operation(1); + if (operation->determine_depending_area_of_interest(&new_input_value, read_operation, output)) { return true; } - operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(&newInputValue, readOperation, output)) { + operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(&new_input_value, read_operation, output)) { return true; } @@ -276,34 +275,34 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( * So now just use the full image area, which may not be as efficient but works at least ... */ #if 1 - rcti imageInput; + rcti image_input; - operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; + operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; #else - rcti newInput; + rcti new_input; const float margin = 2; - BLI_rcti_init_minmax(&newInput); + BLI_rcti_init_minmax(&new_input); - if (m_dispersion_const && m_distortion_const) { + if (dispersion_const_ && distortion_const_) { /* update from fixed distortion/dispersion */ # define UPDATE_INPUT(x, y) \ { \ float coords[6]; \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 @@ -316,26 +315,26 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( } else { /* use maximum dispersion 1.0 if not const */ - float dispersion = m_dispersion_const ? m_dispersion : 1.0f; + float dispersion = dispersion_const_ ? dispersion_ : 1.0f; # define UPDATE_INPUT(x, y, distortion) \ { \ float coords[6]; \ - updateVariables(distortion, dispersion); \ + update_variables(distortion, dispersion); \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 - if (m_distortion_const) { + if (distortion_const_) { /* update from fixed distortion */ - UPDATE_INPUT(input->xmin, input->xmax, m_distortion); - UPDATE_INPUT(input->xmin, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymin, m_distortion); + UPDATE_INPUT(input->xmin, input->xmax, distortion_); + UPDATE_INPUT(input->xmin, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymin, distortion_); } else { /* update from min/max distortion (-1..1) */ @@ -353,33 +352,50 @@ bool ScreenLensDistortionOperation::determineDependingAreaOfInterest( } } - newInput.xmin -= margin; - newInput.ymin -= margin; - newInput.xmax += margin; - newInput.ymax += margin; + new_input.xmin -= margin; + new_input.ymin -= margin; + new_input.xmax += margin; + new_input.ymax += margin; - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } return false; #endif } -void ScreenLensDistortionOperation::updateVariables(float distortion, float dispersion) +void ScreenLensDistortionOperation::update_variables(float distortion, float dispersion) { - m_k[1] = max_ff(min_ff(distortion, 1.0f), -0.999f); + k_[1] = max_ff(min_ff(distortion, 1.0f), -0.999f); /* Smaller dispersion range for somewhat more control. */ float d = 0.25f * max_ff(min_ff(dispersion, 1.0f), 0.0f); - m_k[0] = max_ff(min_ff((m_k[1] + d), 1.0f), -0.999f); - m_k[2] = max_ff(min_ff((m_k[1] - d), 1.0f), -0.999f); - m_maxk = max_fff(m_k[0], m_k[1], m_k[2]); - m_sc = (m_fit && (m_maxk > 0.0f)) ? (1.0f / (1.0f + 2.0f * m_maxk)) : (1.0f / (1.0f + m_maxk)); - m_dk4[0] = 4.0f * (m_k[1] - m_k[0]); - m_dk4[1] = 4.0f * (m_k[2] - m_k[1]); - m_dk4[2] = 0.0f; /* unused */ + k_[0] = max_ff(min_ff((k_[1] + d), 1.0f), -0.999f); + k_[2] = max_ff(min_ff((k_[1] - d), 1.0f), -0.999f); + maxk_ = max_fff(k_[0], k_[1], k_[2]); + sc_ = (fit_ && (maxk_ > 0.0f)) ? (1.0f / (1.0f + 2.0f * maxk_)) : (1.0f / (1.0f + maxk_)); + dk4_[0] = 4.0f * (k_[1] - k_[0]); + dk4_[1] = 4.0f * (k_[2] - k_[1]); + dk4_[2] = 0.0f; /* unused */ - mul_v3_v3fl(m_k4, m_k, 4.0f); + mul_v3_v3fl(k4_, k_, 4.0f); +} + +void ScreenLensDistortionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + switch (execution_model_) { + case eExecutionModel::FullFrame: { + set_determined_canvas_modifier([=](rcti &canvas) { + /* Ensure screen space. */ + BLI_rcti_translate(&canvas, -canvas.xmin, -canvas.ymin); + }); + break; + } + default: + break; + } + + NodeOperation::determine_canvas(preferred_area, r_area); } void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, @@ -388,7 +404,7 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, { if (input_idx != 0) { /* Dispersion and distortion inputs are used as constants only. */ - r_input_area = COM_SINGLE_ELEM_AREA; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; } /* XXX the original method of estimating the area-of-interest does not work @@ -397,28 +413,25 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, * So now just use the full image area, which may not be as efficient but works at least ... */ #if 1 - NodeOperation *image = getInputOperation(0); - r_input_area.xmax = image->getWidth(); - r_input_area.xmin = 0; - r_input_area.ymax = image->getHeight(); - r_input_area.ymin = 0; + NodeOperation *image = get_input_operation(0); + r_input_area = image->get_canvas(); #else /* Original method in tiled implementation. */ - rcti newInput; + rcti new_input; const float margin = 2; - BLI_rcti_init_minmax(&newInput); + BLI_rcti_init_minmax(&new_input); - if (m_dispersion_const && m_distortion_const) { + if (dispersion_const_ && distortion_const_) { /* update from fixed distortion/dispersion */ # define UPDATE_INPUT(x, y) \ { \ float coords[6]; \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 @@ -431,26 +444,26 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, } else { /* use maximum dispersion 1.0 if not const */ - float dispersion = m_dispersion_const ? m_dispersion : 1.0f; + float dispersion = dispersion_const_ ? dispersion_ : 1.0f; # define UPDATE_INPUT(x, y, distortion) \ { \ float coords[6]; \ - updateVariables(distortion, dispersion); \ + update_variables(distortion, dispersion); \ determineUV(coords, x, y); \ - newInput.xmin = min_ffff(newInput.xmin, coords[0], coords[2], coords[4]); \ - newInput.ymin = min_ffff(newInput.ymin, coords[1], coords[3], coords[5]); \ - newInput.xmax = max_ffff(newInput.xmax, coords[0], coords[2], coords[4]); \ - newInput.ymax = max_ffff(newInput.ymax, coords[1], coords[3], coords[5]); \ + new_input.xmin = min_ffff(new_input.xmin, coords[0], coords[2], coords[4]); \ + new_input.ymin = min_ffff(new_input.ymin, coords[1], coords[3], coords[5]); \ + new_input.xmax = max_ffff(new_input.xmax, coords[0], coords[2], coords[4]); \ + new_input.ymax = max_ffff(new_input.ymax, coords[1], coords[3], coords[5]); \ } \ (void)0 - if (m_distortion_const) { + if (distortion_const_) { /* update from fixed distortion */ - UPDATE_INPUT(input->xmin, input->xmax, m_distortion); - UPDATE_INPUT(input->xmin, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymax, m_distortion); - UPDATE_INPUT(input->xmax, input->ymin, m_distortion); + UPDATE_INPUT(input->xmin, input->xmax, distortion_); + UPDATE_INPUT(input->xmin, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymax, distortion_); + UPDATE_INPUT(input->xmax, input->ymin, distortion_); } else { /* update from min/max distortion (-1..1) */ @@ -468,13 +481,13 @@ void ScreenLensDistortionOperation::get_area_of_interest(const int input_idx, } } - newInput.xmin -= margin; - newInput.ymin -= margin; - newInput.xmax += margin; - newInput.ymax += margin; + new_input.xmin -= margin; + new_input.ymin -= margin; + new_input.xmax += margin; + new_input.ymax += margin; - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } return false; @@ -493,9 +506,9 @@ void ScreenLensDistortionOperation::update_memory_buffer_partial(MemoryBuffer *o const float uv_dot = len_squared_v2(uv); float delta[3][2]; - const bool valid_r = get_delta(uv_dot, m_k4[0], uv, delta[0]); - const bool valid_g = get_delta(uv_dot, m_k4[1], uv, delta[1]); - const bool valid_b = get_delta(uv_dot, m_k4[2], uv, delta[2]); + const bool valid_r = get_delta(uv_dot, k4_[0], uv, delta[0]); + const bool valid_g = get_delta(uv_dot, k4_[1], uv, delta[1]); + const bool valid_b = get_delta(uv_dot, k4_[2], uv, delta[2]); if (!(valid_r && valid_g && valid_b)) { zero_v4(it.out); continue; diff --git a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h index 616fc8883b0..fb610b90466 100644 --- a/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h +++ b/source/blender/compositor/operations/COM_ScreenLensDistortionOperation.h @@ -28,24 +28,24 @@ namespace blender::compositor { class ScreenLensDistortionOperation : public MultiThreadedOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; - struct RNG *m_rng; + SocketReader *input_program_; + struct RNG *rng_; - bool m_fit; - bool m_jitter; + bool fit_; + bool jitter_; - float m_dispersion; - float m_distortion; - bool m_dispersion_const; - bool m_distortion_const; - bool m_variables_ready; - float m_k[3]; - float m_k4[3]; - float m_dk4[3]; - float m_maxk; - float m_sc, m_cx, m_cy; + float dispersion_; + float distortion_; + bool dispersion_const_; + bool distortion_const_; + bool variables_ready_; + float k_[3]; + float k4_[3]; + float dk4_[3]; + float maxk_; + float sc_, cx_, cy_; public: ScreenLensDistortionOperation(); @@ -55,37 +55,38 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setFit(bool fit) + void set_fit(bool fit) { - m_fit = fit; + fit_ = fit; } - void setJitter(bool jitter) + void set_jitter(bool jitter) { - m_jitter = jitter; + jitter_ = jitter; } /** Set constant distortion value */ - void setDistortion(float distortion); + void set_distortion(float distortion); /** Set constant dispersion value */ - void setDispersion(float dispersion); + void set_dispersion(float dispersion); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -93,7 +94,7 @@ class ScreenLensDistortionOperation : public MultiThreadedOperation { private: void determineUV(float result[6], float x, float y) const; - void updateVariables(float distortion, float dispersion); + void update_variables(float distortion, float dispersion); void get_uv(const float xy[2], float uv[2]) const; void distort_uv(const float uv[2], float t, float xy[2]) const; diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc index e4686ffa76d..9f193985d38 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.cc @@ -22,39 +22,39 @@ namespace blender::compositor { SetAlphaMultiplyOperation::SetAlphaMultiplyOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; - this->flags.can_be_constant = true; + input_color_ = nullptr; + input_alpha_ = nullptr; + flags_.can_be_constant = true; } -void SetAlphaMultiplyOperation::initExecution() +void SetAlphaMultiplyOperation::init_execution() { - this->m_inputColor = getInputSocketReader(0); - this->m_inputAlpha = getInputSocketReader(1); + input_color_ = get_input_socket_reader(0); + input_alpha_ = get_input_socket_reader(1); } -void SetAlphaMultiplyOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void SetAlphaMultiplyOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color_input[4]; float alpha_input[4]; - this->m_inputColor->readSampled(color_input, x, y, sampler); - this->m_inputAlpha->readSampled(alpha_input, x, y, sampler); + input_color_->read_sampled(color_input, x, y, sampler); + input_alpha_->read_sampled(alpha_input, x, y, sampler); mul_v4_v4fl(output, color_input, alpha_input[0]); } -void SetAlphaMultiplyOperation::deinitExecution() +void SetAlphaMultiplyOperation::deinit_execution() { - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; + input_color_ = nullptr; + input_alpha_ = nullptr; } void SetAlphaMultiplyOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h index 44885318901..2aa56800ff7 100644 --- a/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaMultiplyOperation.h @@ -29,16 +29,16 @@ namespace blender::compositor { */ class SetAlphaMultiplyOperation : public MultiThreadedOperation { private: - SocketReader *m_inputColor; - SocketReader *m_inputAlpha; + SocketReader *input_color_; + SocketReader *input_alpha_; public: SetAlphaMultiplyOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc index 434f5d9b63c..c4490cab178 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.cc @@ -22,37 +22,37 @@ namespace blender::compositor { SetAlphaReplaceOperation::SetAlphaReplaceOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; - this->flags.can_be_constant = true; + input_color_ = nullptr; + input_alpha_ = nullptr; + flags_.can_be_constant = true; } -void SetAlphaReplaceOperation::initExecution() +void SetAlphaReplaceOperation::init_execution() { - this->m_inputColor = getInputSocketReader(0); - this->m_inputAlpha = getInputSocketReader(1); + input_color_ = get_input_socket_reader(0); + input_alpha_ = get_input_socket_reader(1); } -void SetAlphaReplaceOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void SetAlphaReplaceOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float alpha_input[4]; - this->m_inputColor->readSampled(output, x, y, sampler); - this->m_inputAlpha->readSampled(alpha_input, x, y, sampler); + input_color_->read_sampled(output, x, y, sampler); + input_alpha_->read_sampled(alpha_input, x, y, sampler); output[3] = alpha_input[0]; } -void SetAlphaReplaceOperation::deinitExecution() +void SetAlphaReplaceOperation::deinit_execution() { - this->m_inputColor = nullptr; - this->m_inputAlpha = nullptr; + input_color_ = nullptr; + input_alpha_ = nullptr; } void SetAlphaReplaceOperation::update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h index 2c2d4cddf5b..ea27e6af672 100644 --- a/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h +++ b/source/blender/compositor/operations/COM_SetAlphaReplaceOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { */ class SetAlphaReplaceOperation : public MultiThreadedOperation { private: - SocketReader *m_inputColor; - SocketReader *m_inputAlpha; + SocketReader *input_color_; + SocketReader *input_alpha_; public: /** @@ -40,10 +40,10 @@ class SetAlphaReplaceOperation : public MultiThreadedOperation { /** * the inner loop of this program */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_SetColorOperation.cc b/source/blender/compositor/operations/COM_SetColorOperation.cc index dbe45fa60db..b7fee856053 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.cc +++ b/source/blender/compositor/operations/COM_SetColorOperation.cc @@ -22,23 +22,21 @@ namespace blender::compositor { SetColorOperation::SetColorOperation() { - this->addOutputSocket(DataType::Color); - flags.is_set_operation = true; + this->add_output_socket(DataType::Color); + flags_.is_set_operation = true; } -void SetColorOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void SetColorOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { - copy_v4_v4(output, this->m_color); + copy_v4_v4(output, color_); } -void SetColorOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SetColorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetColorOperation.h b/source/blender/compositor/operations/COM_SetColorOperation.h index f546d5e7668..0e04c0324b8 100644 --- a/source/blender/compositor/operations/COM_SetColorOperation.h +++ b/source/blender/compositor/operations/COM_SetColorOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class SetColorOperation : public ConstantOperation { private: - float m_color[4]; + float color_[4]; public: /** @@ -38,53 +38,52 @@ class SetColorOperation : public ConstantOperation { const float *get_constant_elem() override { - return m_color; + return color_; } - float getChannel1() + float get_channel1() { - return this->m_color[0]; + return color_[0]; } - void setChannel1(float value) + void set_channel1(float value) { - this->m_color[0] = value; + color_[0] = value; } - float getChannel2() + float get_channel2() { - return this->m_color[1]; + return color_[1]; } - void setChannel2(float value) + void set_channel2(float value) { - this->m_color[1] = value; + color_[1] = value; } - float getChannel3() + float get_channel3() { - return this->m_color[2]; + return color_[2]; } - void setChannel3(float value) + void set_channel3(float value) { - this->m_color[2] = value; + color_[2] = value; } - float getChannel4() + float get_channel4() { - return this->m_color[3]; + return color_[3]; } - void setChannel4(const float value) + void set_channel4(const float value) { - this->m_color[3] = value; + color_[3] = value; } - void setChannels(const float value[4]) + void set_channels(const float value[4]) { - copy_v4_v4(this->m_color, value); + copy_v4_v4(color_, value); } /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.cc b/source/blender/compositor/operations/COM_SetSamplerOperation.cc index e68774736f3..6f5ac63317b 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.cc +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.cc @@ -22,25 +22,25 @@ namespace blender::compositor { SetSamplerOperation::SetSamplerOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); } -void SetSamplerOperation::initExecution() +void SetSamplerOperation::init_execution() { - this->m_reader = this->getInputSocketReader(0); + reader_ = this->get_input_socket_reader(0); } -void SetSamplerOperation::deinitExecution() +void SetSamplerOperation::deinit_execution() { - this->m_reader = nullptr; + reader_ = nullptr; } -void SetSamplerOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void SetSamplerOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - this->m_reader->readSampled(output, x, y, this->m_sampler); + reader_->read_sampled(output, x, y, sampler_); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetSamplerOperation.h b/source/blender/compositor/operations/COM_SetSamplerOperation.h index d355d937806..27ef6603ae4 100644 --- a/source/blender/compositor/operations/COM_SetSamplerOperation.h +++ b/source/blender/compositor/operations/COM_SetSamplerOperation.h @@ -28,8 +28,8 @@ namespace blender::compositor { */ class SetSamplerOperation : public NodeOperation { private: - PixelSampler m_sampler; - SocketReader *m_reader; + PixelSampler sampler_; + SocketReader *reader_; public: /** @@ -37,17 +37,17 @@ class SetSamplerOperation : public NodeOperation { */ SetSamplerOperation(); - void setSampler(PixelSampler sampler) + void set_sampler(PixelSampler sampler) { - this->m_sampler = sampler; + sampler_ = sampler; } /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_execution() override; + void deinit_execution() override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.cc b/source/blender/compositor/operations/COM_SetValueOperation.cc index ef43cf64653..d11a35aefc3 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.cc +++ b/source/blender/compositor/operations/COM_SetValueOperation.cc @@ -22,23 +22,21 @@ namespace blender::compositor { SetValueOperation::SetValueOperation() { - this->addOutputSocket(DataType::Value); - flags.is_set_operation = true; + this->add_output_socket(DataType::Value); + flags_.is_set_operation = true; } -void SetValueOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void SetValueOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { - output[0] = this->m_value; + output[0] = value_; } -void SetValueOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SetValueOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetValueOperation.h b/source/blender/compositor/operations/COM_SetValueOperation.h index 726624c1c6a..4c824d00294 100644 --- a/source/blender/compositor/operations/COM_SetValueOperation.h +++ b/source/blender/compositor/operations/COM_SetValueOperation.h @@ -28,7 +28,7 @@ namespace blender::compositor { */ class SetValueOperation : public ConstantOperation { private: - float m_value; + float value_; public: /** @@ -38,24 +38,24 @@ class SetValueOperation : public ConstantOperation { const float *get_constant_elem() override { - return &m_value; + return &value_; } - float getValue() + float get_value() { - return this->m_value; + return value_; } - void setValue(float value) + void set_value(float value) { - this->m_value = value; + value_ = value; } /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.cc b/source/blender/compositor/operations/COM_SetVectorOperation.cc index 7b8cf44048c..c1db429b41e 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.cc +++ b/source/blender/compositor/operations/COM_SetVectorOperation.cc @@ -17,31 +17,28 @@ */ #include "COM_SetVectorOperation.h" -#include "COM_defines.h" namespace blender::compositor { SetVectorOperation::SetVectorOperation() { - this->addOutputSocket(DataType::Vector); - flags.is_set_operation = true; + this->add_output_socket(DataType::Vector); + flags_.is_set_operation = true; } -void SetVectorOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void SetVectorOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { output[0] = vector_.x; output[1] = vector_.y; output[2] = vector_.z; } -void SetVectorOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SetVectorOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SetVectorOperation.h b/source/blender/compositor/operations/COM_SetVectorOperation.h index 41fd06659d6..291fef37817 100644 --- a/source/blender/compositor/operations/COM_SetVectorOperation.h +++ b/source/blender/compositor/operations/COM_SetVectorOperation.h @@ -82,12 +82,11 @@ class SetVectorOperation : public ConstantOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setVector(const float vector[3]) + void set_vector(const float vector[3]) { setX(vector[0]); setY(vector[1]); diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.cc b/source/blender/compositor/operations/COM_SocketProxyOperation.cc index 39876439b7b..fd37f0b5f4f 100644 --- a/source/blender/compositor/operations/COM_SocketProxyOperation.cc +++ b/source/blender/compositor/operations/COM_SocketProxyOperation.cc @@ -22,15 +22,15 @@ namespace blender::compositor { SocketProxyOperation::SocketProxyOperation(DataType type, bool use_conversion) { - this->addInputSocket(type); - this->addOutputSocket(type); - flags.is_proxy_operation = true; - flags.use_datatype_conversion = use_conversion; + this->add_input_socket(type); + this->add_output_socket(type); + flags_.is_proxy_operation = true; + flags_.use_datatype_conversion = use_conversion; } -std::unique_ptr SocketProxyOperation::getMetaData() +std::unique_ptr SocketProxyOperation::get_meta_data() { - return this->getInputSocket(0)->getReader()->getMetaData(); + return this->get_input_socket(0)->get_reader()->get_meta_data(); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SocketProxyOperation.h b/source/blender/compositor/operations/COM_SocketProxyOperation.h index 1d3b76055bd..e9740cf43bc 100644 --- a/source/blender/compositor/operations/COM_SocketProxyOperation.h +++ b/source/blender/compositor/operations/COM_SocketProxyOperation.h @@ -26,7 +26,7 @@ class SocketProxyOperation : public NodeOperation { public: SocketProxyOperation(DataType type, bool use_conversion); - std::unique_ptr getMetaData() override; + std::unique_ptr get_meta_data() override; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_SplitOperation.cc b/source/blender/compositor/operations/COM_SplitOperation.cc index b2a50e2c740..e98c5c986f5 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.cc +++ b/source/blender/compositor/operations/COM_SplitOperation.cc @@ -17,77 +17,66 @@ */ #include "COM_SplitOperation.h" -#include "BKE_image.h" -#include "BLI_listbase.h" -#include "BLI_math_color.h" -#include "BLI_math_vector.h" -#include "BLI_utildefines.h" -#include "MEM_guardedalloc.h" - -#include "IMB_imbuf.h" -#include "IMB_imbuf_types.h" namespace blender::compositor { SplitOperation::SplitOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->m_image1Input = nullptr; - this->m_image2Input = nullptr; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + image1Input_ = nullptr; + image2Input_ = nullptr; } -void SplitOperation::initExecution() +void SplitOperation::init_execution() { /* When initializing the tree during initial load the width and height can be zero. */ - this->m_image1Input = getInputSocketReader(0); - this->m_image2Input = getInputSocketReader(1); + image1Input_ = get_input_socket_reader(0); + image2Input_ = get_input_socket_reader(1); } -void SplitOperation::deinitExecution() +void SplitOperation::deinit_execution() { - this->m_image1Input = nullptr; - this->m_image2Input = nullptr; + image1Input_ = nullptr; + image2Input_ = nullptr; } -void SplitOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void SplitOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - int perc = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f : - this->m_splitPercentage * this->getHeight() / 100.0f; - bool image1 = this->m_xSplit ? x > perc : y > perc; + int perc = x_split_ ? split_percentage_ * this->get_width() / 100.0f : + split_percentage_ * this->get_height() / 100.0f; + bool image1 = x_split_ ? x > perc : y > perc; if (image1) { - this->m_image1Input->readSampled(output, x, y, PixelSampler::Nearest); + image1Input_->read_sampled(output, x, y, PixelSampler::Nearest); } else { - this->m_image2Input->readSampled(output, x, y, PixelSampler::Nearest); + image2Input_->read_sampled(output, x, y, PixelSampler::Nearest); } } -void SplitOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void SplitOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - unsigned int tempPreferredResolution[2] = {0, 0}; - unsigned int tempResolution[2]; + rcti unused_area; - this->getInputSocket(0)->determineResolution(tempResolution, tempPreferredResolution); - this->setResolutionInputSocketIndex((tempResolution[0] && tempResolution[1]) ? 0 : 1); + const bool determined = this->get_input_socket(0)->determine_canvas(COM_AREA_NONE, unused_area); + this->set_canvas_input_index(determined ? 0 : 1); - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); } void SplitOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const int percent = this->m_xSplit ? this->m_splitPercentage * this->getWidth() / 100.0f : - this->m_splitPercentage * this->getHeight() / 100.0f; - const size_t elem_bytes = COM_data_type_bytes_len(getOutputSocket()->getDataType()); + const int percent = x_split_ ? split_percentage_ * this->get_width() / 100.0f : + split_percentage_ * this->get_height() / 100.0f; + const size_t elem_bytes = COM_data_type_bytes_len(get_output_socket()->get_data_type()); for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { - const bool is_image1 = this->m_xSplit ? it.x > percent : it.y > percent; + const bool is_image1 = x_split_ ? it.x > percent : it.y > percent; memcpy(it.out, it.in(is_image1 ? 0 : 1), elem_bytes); } } diff --git a/source/blender/compositor/operations/COM_SplitOperation.h b/source/blender/compositor/operations/COM_SplitOperation.h index 2d09d2a07dc..2d2b2e89cca 100644 --- a/source/blender/compositor/operations/COM_SplitOperation.h +++ b/source/blender/compositor/operations/COM_SplitOperation.h @@ -24,26 +24,25 @@ namespace blender::compositor { class SplitOperation : public MultiThreadedOperation { private: - SocketReader *m_image1Input; - SocketReader *m_image2Input; + SocketReader *image1Input_; + SocketReader *image2Input_; - float m_splitPercentage; - bool m_xSplit; + float split_percentage_; + bool x_split_; public: SplitOperation(); - void initExecution() override; - void deinitExecution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; - void setSplitPercentage(float splitPercentage) + void init_execution() override; + void deinit_execution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + void set_split_percentage(float split_percentage) { - this->m_splitPercentage = splitPercentage; + split_percentage_ = split_percentage; } - void setXSplit(bool xsplit) + void set_xsplit(bool xsplit) { - this->m_xSplit = xsplit; + x_split_ = xsplit; } void update_memory_buffer_partial(MemoryBuffer *output, diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.cc b/source/blender/compositor/operations/COM_SunBeamsOperation.cc index ad96e3a02ba..ba1363d1193 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.cc +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.cc @@ -23,22 +23,22 @@ namespace blender::compositor { SunBeamsOperation::SunBeamsOperation() { - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); - this->setResolutionInputSocketIndex(0); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); + this->set_canvas_input_index(0); - this->flags.complex = true; + flags_.complex = true; } void SunBeamsOperation::calc_rays_common_data() { /* convert to pixels */ - this->m_source_px[0] = this->m_data.source[0] * this->getWidth(); - this->m_source_px[1] = this->m_data.source[1] * this->getHeight(); - this->m_ray_length_px = this->m_data.ray_length * MAX2(this->getWidth(), this->getHeight()); + source_px_[0] = data_.source[0] * this->get_width(); + source_px_[1] = data_.source[1] * this->get_height(); + ray_length_px_ = data_.ray_length * MAX2(this->get_width(), this->get_height()); } -void SunBeamsOperation::initExecution() +void SunBeamsOperation::init_execution() { calc_rays_common_data(); } @@ -64,16 +64,6 @@ template struct BufferLineAccumulator { /* utility functions implementing the matrix transform to/from sector space */ - static inline void buffer_to_sector(const float source[2], int x, int y, int &u, int &v) - { - int x0 = (int)source[0]; - int y0 = (int)source[1]; - x -= x0; - y -= y0; - u = x * fxu + y * fyu; - v = x * fxv + y * fyv; - } - static inline void buffer_to_sector(const float source[2], float x, float y, float &u, float &v) { int x0 = (int)source[0]; @@ -92,14 +82,6 @@ template struct BufferLineAccumulator { y = y0 + u * fyu + v * fyv; } - static inline void sector_to_buffer(const float source[2], float u, float v, float &x, float &y) - { - int x0 = (int)source[0]; - int y0 = (int)source[1]; - x = (float)x0 + u * fxu + v * fxv; - y = (float)y0 + u * fyu + v * fyv; - } - /** * Set up the initial buffer pointer and calculate necessary variables for looping. * @@ -127,9 +109,9 @@ template struct BufferLineAccumulator { buffer_to_sector(source, co[0], co[1], pu, pv); /* line angle */ - float tan_phi = pv / pu; - float dr = sqrtf(tan_phi * tan_phi + 1.0f); - float cos_phi = 1.0f / dr; + double tan_phi = pv / (double)pu; + double dr = sqrt(tan_phi * tan_phi + 1.0); + double cos_phi = 1.0 / dr; /* clamp u range to avoid influence of pixels "behind" the source */ float umin = max_ff(pu - cos_phi * dist_min, 0.0f); @@ -143,9 +125,9 @@ template struct BufferLineAccumulator { sector_to_buffer(source, end, (int)ceilf(v), x, y); - falloff_factor = dist_max > dist_min ? dr / (float)(dist_max - dist_min) : 0.0f; + falloff_factor = dist_max > dist_min ? dr / (double)(dist_max - dist_min) : 0.0f; - float *iter = input->getBuffer() + input->get_coords_offset(x, y); + float *iter = input->get_buffer() + input->get_coords_offset(x, y); return iter; } @@ -312,18 +294,17 @@ static void accumulate_line(MemoryBuffer *input, } } -void *SunBeamsOperation::initializeTileData(rcti * /*rect*/) +void *SunBeamsOperation::initialize_tile_data(rcti * /*rect*/) { - void *buffer = getInputOperation(0)->initializeTileData(nullptr); + void *buffer = get_input_operation(0)->initialize_tile_data(nullptr); return buffer; } -void SunBeamsOperation::executePixel(float output[4], int x, int y, void *data) +void SunBeamsOperation::execute_pixel(float output[4], int x, int y, void *data) { const float co[2] = {(float)x, (float)y}; - accumulate_line( - (MemoryBuffer *)data, output, co, this->m_source_px, 0.0f, this->m_ray_length_px); + accumulate_line((MemoryBuffer *)data, output, co, source_px_, 0.0f, ray_length_px_); } static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], float ray_length) @@ -341,21 +322,21 @@ static void calc_ray_shift(rcti *rect, float x, float y, const float source[2], BLI_rcti_do_minmax_v(rect, ico); } -bool SunBeamsOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool SunBeamsOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { /* Enlarges the rect by moving each corner toward the source. * This is the maximum distance that pixels can influence each other * and gives a rect that contains all possible accumulated pixels. */ rcti rect = *input; - calc_ray_shift(&rect, input->xmin, input->ymin, this->m_source_px, this->m_ray_length_px); - calc_ray_shift(&rect, input->xmin, input->ymax, this->m_source_px, this->m_ray_length_px); - calc_ray_shift(&rect, input->xmax, input->ymin, this->m_source_px, this->m_ray_length_px); - calc_ray_shift(&rect, input->xmax, input->ymax, this->m_source_px, this->m_ray_length_px); + calc_ray_shift(&rect, input->xmin, input->ymin, source_px_, ray_length_px_); + calc_ray_shift(&rect, input->xmin, input->ymax, source_px_, ray_length_px_); + calc_ray_shift(&rect, input->xmax, input->ymin, source_px_, ray_length_px_); + calc_ray_shift(&rect, input->xmax, input->ymax, source_px_, ray_length_px_); - return NodeOperation::determineDependingAreaOfInterest(&rect, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&rect, read_operation, output); } void SunBeamsOperation::get_area_of_interest(const int input_idx, @@ -370,10 +351,10 @@ void SunBeamsOperation::get_area_of_interest(const int input_idx, /* Enlarges the rect by moving each corner toward the source. * This is the maximum distance that pixels can influence each other * and gives a rect that contains all possible accumulated pixels. */ - calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, m_source_px, m_ray_length_px); - calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, m_source_px, m_ray_length_px); - calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, m_source_px, m_ray_length_px); - calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, m_source_px, m_ray_length_px); + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymin, source_px_, ray_length_px_); + calc_ray_shift(&r_input_area, output_area.xmin, output_area.ymax, source_px_, ray_length_px_); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymin, source_px_, ray_length_px_); + calc_ray_shift(&r_input_area, output_area.xmax, output_area.ymax, source_px_, ray_length_px_); } void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -387,7 +368,7 @@ void SunBeamsOperation::update_memory_buffer_partial(MemoryBuffer *output, float *out_elem = output->get_elem(area.xmin, y); for (int x = area.xmin; x < area.xmax; x++) { coords[0] = x; - accumulate_line(input, out_elem, coords, m_source_px, 0.0f, m_ray_length_px); + accumulate_line(input, out_elem, coords, source_px_, 0.0f, ray_length_px_); out_elem += output->elem_stride; } } diff --git a/source/blender/compositor/operations/COM_SunBeamsOperation.h b/source/blender/compositor/operations/COM_SunBeamsOperation.h index 71fc04453fe..c40d540996e 100644 --- a/source/blender/compositor/operations/COM_SunBeamsOperation.h +++ b/source/blender/compositor/operations/COM_SunBeamsOperation.h @@ -25,19 +25,19 @@ class SunBeamsOperation : public MultiThreadedOperation { public: SunBeamsOperation(); - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setData(const NodeSunBeams &data) + void set_data(const NodeSunBeams &data) { - m_data = data; + data_ = data; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -49,10 +49,10 @@ class SunBeamsOperation : public MultiThreadedOperation { void calc_rays_common_data(); private: - NodeSunBeams m_data; + NodeSunBeams data_; - float m_source_px[2]; - float m_ray_length_px; + float source_px_[2]; + float ray_length_px_; }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TextureOperation.cc b/source/blender/compositor/operations/COM_TextureOperation.cc index c8e0844d35f..2ed83e87569 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.cc +++ b/source/blender/compositor/operations/COM_TextureOperation.cc @@ -22,140 +22,114 @@ #include "BKE_image.h" #include "BKE_node.h" -#include "BLI_listbase.h" -#include "BLI_threads.h" - namespace blender::compositor { TextureBaseOperation::TextureBaseOperation() { - this->addInputSocket(DataType::Vector); // offset - this->addInputSocket(DataType::Vector); // size - this->m_texture = nullptr; - this->m_inputSize = nullptr; - this->m_inputOffset = nullptr; - this->m_rd = nullptr; - this->m_pool = nullptr; - this->m_sceneColorManage = false; - flags.complex = true; + this->add_input_socket(DataType::Vector); // offset + this->add_input_socket(DataType::Vector); // size + texture_ = nullptr; + input_size_ = nullptr; + input_offset_ = nullptr; + rd_ = nullptr; + pool_ = nullptr; + scene_color_manage_ = false; + flags_.complex = true; } TextureOperation::TextureOperation() : TextureBaseOperation() { - this->addOutputSocket(DataType::Color); + this->add_output_socket(DataType::Color); } TextureAlphaOperation::TextureAlphaOperation() : TextureBaseOperation() { - this->addOutputSocket(DataType::Value); + this->add_output_socket(DataType::Value); } -void TextureBaseOperation::initExecution() +void TextureBaseOperation::init_execution() { - this->m_inputOffset = getInputSocketReader(0); - this->m_inputSize = getInputSocketReader(1); - this->m_pool = BKE_image_pool_new(); - if (this->m_texture != nullptr && this->m_texture->nodetree != nullptr && - this->m_texture->use_nodes) { - ntreeTexBeginExecTree(this->m_texture->nodetree); + input_offset_ = get_input_socket_reader(0); + input_size_ = get_input_socket_reader(1); + pool_ = BKE_image_pool_new(); + if (texture_ != nullptr && texture_->nodetree != nullptr && texture_->use_nodes) { + ntreeTexBeginExecTree(texture_->nodetree); } - NodeOperation::initExecution(); + NodeOperation::init_execution(); } -void TextureBaseOperation::deinitExecution() +void TextureBaseOperation::deinit_execution() { - this->m_inputSize = nullptr; - this->m_inputOffset = nullptr; - BKE_image_pool_free(this->m_pool); - this->m_pool = nullptr; - if (this->m_texture != nullptr && this->m_texture->use_nodes && - this->m_texture->nodetree != nullptr && this->m_texture->nodetree->execdata != nullptr) { - ntreeTexEndExecTree(this->m_texture->nodetree->execdata); + input_size_ = nullptr; + input_offset_ = nullptr; + BKE_image_pool_free(pool_); + pool_ = nullptr; + if (texture_ != nullptr && texture_->use_nodes && texture_->nodetree != nullptr && + texture_->nodetree->execdata != nullptr) { + ntreeTexEndExecTree(texture_->nodetree->execdata); } - NodeOperation::deinitExecution(); + NodeOperation::deinit_execution(); } -void TextureBaseOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void TextureBaseOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - switch (execution_model_) { - case eExecutionModel::Tiled: { - if (preferredResolution[0] == 0 || preferredResolution[1] == 0) { - int width = this->m_rd->xsch * this->m_rd->size / 100; - int height = this->m_rd->ysch * this->m_rd->size / 100; - resolution[0] = width; - resolution[1] = height; - } - else { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; - } - break; - } - case eExecutionModel::FullFrame: { - /* Determine inputs resolutions. */ - unsigned int temp[2]; - NodeOperation::determineResolution(temp, preferredResolution); + r_area = preferred_area; + if (BLI_rcti_is_empty(&preferred_area)) { + int width = rd_->xsch * rd_->size / 100; + int height = rd_->ysch * rd_->size / 100; + r_area.xmax = preferred_area.xmin + width; + r_area.ymax = preferred_area.ymin + height; + } - /* We don't use inputs resolutions because they are only used as parameters, not image data. - */ - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; - break; - } + if (execution_model_ == eExecutionModel::FullFrame) { + /* Determine inputs. */ + rcti temp; + NodeOperation::determine_canvas(r_area, temp); } } -void TextureAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void TextureAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float color[4]; - TextureBaseOperation::executePixelSampled(color, x, y, sampler); + TextureBaseOperation::execute_pixel_sampled(color, x, y, sampler); output[0] = color[3]; } -void TextureBaseOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void TextureBaseOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { TexResult texres = {0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0, nullptr}; - float textureSize[4]; - float textureOffset[4]; + float texture_size[4]; + float texture_offset[4]; float vec[3]; int retval; - const float cx = this->getWidth() / 2; - const float cy = this->getHeight() / 2; - float u = (x - cx) / this->getWidth() * 2; - float v = (y - cy) / this->getHeight() * 2; + const float cx = this->get_width() / 2; + const float cy = this->get_height() / 2; + float u = (x - cx) / this->get_width() * 2; + float v = (y - cy) / this->get_height() * 2; /* When no interpolation/filtering happens in multitex() force nearest interpolation. * We do it here because (a) we can't easily say multitex() that we want nearest * interpolation and (b) in such configuration multitex() simply floor's the value * which often produces artifacts. */ - if (m_texture != nullptr && (m_texture->imaflag & TEX_INTERPOL) == 0) { + if (texture_ != nullptr && (texture_->imaflag & TEX_INTERPOL) == 0) { u += 0.5f / cx; v += 0.5f / cy; } - this->m_inputSize->readSampled(textureSize, x, y, sampler); - this->m_inputOffset->readSampled(textureOffset, x, y, sampler); + input_size_->read_sampled(texture_size, x, y, sampler); + input_offset_->read_sampled(texture_offset, x, y, sampler); - vec[0] = textureSize[0] * (u + textureOffset[0]); - vec[1] = textureSize[1] * (v + textureOffset[1]); - vec[2] = textureSize[2] * textureOffset[2]; + vec[0] = texture_size[0] * (u + texture_offset[0]); + vec[1] = texture_size[1] * (v + texture_offset[1]); + vec[2] = texture_size[2] * texture_offset[2]; const int thread_id = WorkScheduler::current_thread_id(); - retval = multitex_ext(this->m_texture, - vec, - nullptr, - nullptr, - 0, - &texres, - thread_id, - m_pool, - m_sceneColorManage, - false); + retval = multitex_ext( + texture_, vec, nullptr, nullptr, 0, &texres, thread_id, pool_, scene_color_manage_, false); output[3] = texres.talpha ? texres.ta : texres.tin; if (retval & TEX_RGB) { @@ -172,8 +146,8 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - const int op_width = this->getWidth(); - const int op_height = this->getHeight(); + const int op_width = this->get_width(); + const int op_height = this->get_height(); const float center_x = op_width / 2; const float center_y = op_height / 2; TexResult tex_result = {0}; @@ -190,7 +164,7 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, * interpolation and (b) in such configuration multitex() simply floor's the value * which often produces artifacts. */ - if (m_texture != nullptr && (m_texture->imaflag & TEX_INTERPOL) == 0) { + if (texture_ != nullptr && (texture_->imaflag & TEX_INTERPOL) == 0) { u += 0.5f / center_x; v += 0.5f / center_y; } @@ -199,15 +173,15 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output, vec[1] = tex_size[1] * (v + tex_offset[1]); vec[2] = tex_size[2] * tex_offset[2]; - const int retval = multitex_ext(this->m_texture, + const int retval = multitex_ext(texture_, vec, nullptr, nullptr, 0, &tex_result, thread_id, - m_pool, - m_sceneColorManage, + pool_, + scene_color_manage_, false); it.out[3] = tex_result.talpha ? tex_result.ta : tex_result.tin; diff --git a/source/blender/compositor/operations/COM_TextureOperation.h b/source/blender/compositor/operations/COM_TextureOperation.h index 1e95cb270d0..bf1bb1affb8 100644 --- a/source/blender/compositor/operations/COM_TextureOperation.h +++ b/source/blender/compositor/operations/COM_TextureOperation.h @@ -35,19 +35,18 @@ namespace blender::compositor { */ class TextureBaseOperation : public MultiThreadedOperation { private: - Tex *m_texture; - const RenderData *m_rd; - SocketReader *m_inputSize; - SocketReader *m_inputOffset; - struct ImagePool *m_pool; - bool m_sceneColorManage; + Tex *texture_; + const RenderData *rd_; + SocketReader *input_size_; + SocketReader *input_offset_; + struct ImagePool *pool_; + bool scene_color_manage_; protected: /** * Determine the output resolution. */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; /** * Constructor @@ -55,21 +54,21 @@ class TextureBaseOperation : public MultiThreadedOperation { TextureBaseOperation(); public: - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void setTexture(Tex *texture) + void set_texture(Tex *texture) { - this->m_texture = texture; + texture_ = texture; } - void initExecution() override; - void deinitExecution() override; - void setRenderData(const RenderData *rd) + void init_execution() override; + void deinit_execution() override; + void set_render_data(const RenderData *rd) { - this->m_rd = rd; + rd_ = rd; } - void setSceneColorManage(bool sceneColorManage) + void set_scene_color_manage(bool scene_color_manage) { - this->m_sceneColorManage = sceneColorManage; + scene_color_manage_ = scene_color_manage; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -84,7 +83,7 @@ class TextureOperation : public TextureBaseOperation { class TextureAlphaOperation : public TextureBaseOperation { public: TextureAlphaOperation(); - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_TonemapOperation.cc b/source/blender/compositor/operations/COM_TonemapOperation.cc index 20da468eeb1..bc370e3496b 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.cc +++ b/source/blender/compositor/operations/COM_TonemapOperation.cc @@ -17,10 +17,8 @@ */ #include "COM_TonemapOperation.h" -#include "COM_ExecutionSystem.h" -#include "BLI_math.h" -#include "BLI_utildefines.h" +#include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" @@ -28,28 +26,28 @@ namespace blender::compositor { TonemapOperation::TonemapOperation() { - this->addInputSocket(DataType::Color, ResizeMode::None); - this->addOutputSocket(DataType::Color); - this->m_imageReader = nullptr; - this->m_data = nullptr; - this->m_cachedInstance = nullptr; - this->flags.complex = true; + this->add_input_socket(DataType::Color, ResizeMode::Align); + this->add_output_socket(DataType::Color); + image_reader_ = nullptr; + data_ = nullptr; + cached_instance_ = nullptr; + flags_.complex = true; } -void TonemapOperation::initExecution() +void TonemapOperation::init_execution() { - this->m_imageReader = this->getInputSocketReader(0); - NodeOperation::initMutex(); + image_reader_ = this->get_input_socket_reader(0); + NodeOperation::init_mutex(); } -void TonemapOperation::executePixel(float output[4], int x, int y, void *data) +void TonemapOperation::execute_pixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - this->m_imageReader->read(output, x, y, nullptr); + image_reader_->read(output, x, y, nullptr); mul_v3_fl(output, avg->al); - float dr = output[0] + this->m_data->offset; - float dg = output[1] + this->m_data->offset; - float db = output[2] + this->m_data->offset; + float dr = output[0] + data_->offset; + float dg = output[1] + data_->offset; + float db = output[2] + data_->offset; output[0] /= ((dr == 0.0f) ? 1.0f : dr); output[1] /= ((dg == 0.0f) ? 1.0f : dg); output[2] /= ((db == 0.0f) ? 1.0f : db); @@ -60,16 +58,16 @@ void TonemapOperation::executePixel(float output[4], int x, int y, void *data) output[2] = powf(MAX2(output[2], 0.0f), igm); } } -void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, void *data) +void PhotoreceptorTonemapOperation::execute_pixel(float output[4], int x, int y, void *data) { AvgLogLum *avg = (AvgLogLum *)data; - NodeTonemap *ntm = this->m_data; + NodeTonemap *ntm = data_; - const float f = expf(-this->m_data->f); + const float f = expf(-data_->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c, ia = 1.0f - ntm->a; - this->m_imageReader->read(output, x, y, nullptr); + image_reader_->read(output, x, y, nullptr); const float L = IMB_colormanagement_get_luminance(output); float I_l = output[0] + ic * (L - output[0]); @@ -86,41 +84,41 @@ void PhotoreceptorTonemapOperation::executePixel(float output[4], int x, int y, output[2] /= (output[2] + powf(f * I_a, m)); } -void TonemapOperation::deinitExecution() +void TonemapOperation::deinit_execution() { - this->m_imageReader = nullptr; - delete this->m_cachedInstance; - NodeOperation::deinitMutex(); + image_reader_ = nullptr; + delete cached_instance_; + NodeOperation::deinit_mutex(); } -bool TonemapOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool TonemapOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - rcti imageInput; + rcti image_input; - NodeOperation *operation = getInputOperation(0); - imageInput.xmax = operation->getWidth(); - imageInput.xmin = 0; - imageInput.ymax = operation->getHeight(); - imageInput.ymin = 0; - if (operation->determineDependingAreaOfInterest(&imageInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(0); + image_input.xmax = operation->get_width(); + image_input.xmin = 0; + image_input.ymax = operation->get_height(); + image_input.ymin = 0; + if (operation->determine_depending_area_of_interest(&image_input, read_operation, output)) { return true; } return false; } -void *TonemapOperation::initializeTileData(rcti *rect) +void *TonemapOperation::initialize_tile_data(rcti *rect) { - lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_imageReader->initializeTileData(rect); + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)image_reader_->initialize_tile_data(rect); AvgLogLum *data = new AvgLogLum(); - float *buffer = tile->getBuffer(); + float *buffer = tile->get_buffer(); float lsum = 0.0f; - int p = tile->getWidth() * tile->getHeight(); + int p = tile->get_width() * tile->get_height(); float *bc = buffer; float avl, maxl = -1e10f, minl = 1e10f; const float sc = 1.0f / p; @@ -142,15 +140,15 @@ void *TonemapOperation::initializeTileData(rcti *rect) avl = lsum * sc; data->auto_key = (maxl > minl) ? ((maxl - avl) / (maxl - minl)) : 1.0f; float al = exp((double)avl); - data->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al); - data->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma); - this->m_cachedInstance = data; + data->al = (al == 0.0f) ? 0.0f : (data_->key / al); + data->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma); + cached_instance_ = data; } - unlockMutex(); - return this->m_cachedInstance; + unlock_mutex(); + return cached_instance_; } -void TonemapOperation::deinitializeTileData(rcti * /*rect*/, void * /*data*/) +void TonemapOperation::deinitialize_tile_data(rcti * /*rect*/, void * /*data*/) { /* pass */ } @@ -160,11 +158,7 @@ void TonemapOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { BLI_assert(input_idx == 0); - NodeOperation *operation = getInputOperation(input_idx); - r_input_area.xmin = 0; - r_input_area.ymin = 0; - r_input_area.xmax = operation->getWidth(); - r_input_area.ymax = operation->getHeight(); + r_input_area = get_input_operation(input_idx)->get_canvas(); } struct Luminance { @@ -195,7 +189,7 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const rcti &UNUSED(area), Span inputs) { - if (this->m_cachedInstance == nullptr) { + if (cached_instance_ == nullptr) { Luminance lum = {0}; const MemoryBuffer *input = inputs[0]; exec_system_->execute_work( @@ -219,9 +213,9 @@ void TonemapOperation::update_memory_buffer_started(MemoryBuffer *UNUSED(output) const float avg_log = lum.log_sum / lum.num_pixels; avg->auto_key = (max_log > min_log) ? ((max_log - avg_log) / (max_log - min_log)) : 1.0f; const float al = exp((double)avg_log); - avg->al = (al == 0.0f) ? 0.0f : (this->m_data->key / al); - avg->igm = (this->m_data->gamma == 0.0f) ? 1 : (1.0f / this->m_data->gamma); - this->m_cachedInstance = avg; + avg->al = (al == 0.0f) ? 0.0f : (data_->key / al); + avg->igm = (data_->gamma == 0.0f) ? 1 : (1.0f / data_->gamma); + cached_instance_ = avg; } } @@ -229,9 +223,9 @@ void TonemapOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - AvgLogLum *avg = m_cachedInstance; + AvgLogLum *avg = cached_instance_; const float igm = avg->igm; - const float offset = this->m_data->offset; + const float offset = data_->offset; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { copy_v4_v4(it.out, it.in(0)); mul_v3_fl(it.out, avg->al); @@ -253,9 +247,9 @@ void PhotoreceptorTonemapOperation::update_memory_buffer_partial(MemoryBuffer *o const rcti &area, Span inputs) { - AvgLogLum *avg = m_cachedInstance; - NodeTonemap *ntm = this->m_data; - const float f = expf(-this->m_data->f); + AvgLogLum *avg = cached_instance_; + NodeTonemap *ntm = data_; + const float f = expf(-data_->f); const float m = (ntm->m > 0.0f) ? ntm->m : (0.3f + 0.7f * powf(avg->auto_key, 1.4f)); const float ic = 1.0f - ntm->c; const float ia = 1.0f - ntm->a; diff --git a/source/blender/compositor/operations/COM_TonemapOperation.h b/source/blender/compositor/operations/COM_TonemapOperation.h index 56b57730ec1..a20c061965e 100644 --- a/source/blender/compositor/operations/COM_TonemapOperation.h +++ b/source/blender/compositor/operations/COM_TonemapOperation.h @@ -44,17 +44,17 @@ class TonemapOperation : public MultiThreadedOperation { /** * \brief Cached reference to the reader */ - SocketReader *m_imageReader; + SocketReader *image_reader_; /** * \brief settings of the Tonemap */ - NodeTonemap *m_data; + NodeTonemap *data_; /** * \brief temporarily cache of the execution storage */ - AvgLogLum *m_cachedInstance; + AvgLogLum *cached_instance_; public: TonemapOperation(); @@ -62,29 +62,29 @@ class TonemapOperation : public MultiThreadedOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void setData(NodeTonemap *data) + void set_data(NodeTonemap *data) { - this->m_data = data; + data_ = data; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_started(MemoryBuffer *output, @@ -106,7 +106,7 @@ class PhotoreceptorTonemapOperation : public TonemapOperation { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.cc b/source/blender/compositor/operations/COM_TrackPositionOperation.cc index 0f4be16a620..fc6be97a29c 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.cc +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.cc @@ -18,12 +18,6 @@ #include "COM_TrackPositionOperation.h" -#include "MEM_guardedalloc.h" - -#include "BLI_listbase.h" -#include "BLI_math.h" -#include "BLI_math_color.h" - #include "BKE_movieclip.h" #include "BKE_node.h" #include "BKE_tracking.h" @@ -32,20 +26,20 @@ namespace blender::compositor { TrackPositionOperation::TrackPositionOperation() { - this->addOutputSocket(DataType::Value); - this->m_movieClip = nullptr; - this->m_framenumber = 0; - this->m_trackingObjectName[0] = 0; - this->m_trackName[0] = 0; - this->m_axis = 0; - this->m_position = CMP_TRACKPOS_ABSOLUTE; - this->m_relativeFrame = 0; - this->m_speed_output = false; - flags.is_set_operation = true; + this->add_output_socket(DataType::Value); + movie_clip_ = nullptr; + framenumber_ = 0; + tracking_object_name_[0] = 0; + track_name_[0] = 0; + axis_ = 0; + position_ = CMP_TRACKPOS_ABSOLUTE; + relative_frame_ = 0; + speed_output_ = false; + flags_.is_set_operation = true; is_track_position_calculated_ = false; } -void TrackPositionOperation::initExecution() +void TrackPositionOperation::init_execution() { if (!is_track_position_calculated_) { calc_track_position(); @@ -60,92 +54,91 @@ void TrackPositionOperation::calc_track_position() MovieTrackingObject *object; track_position_ = 0; - zero_v2(this->m_markerPos); - zero_v2(this->m_relativePos); + zero_v2(marker_pos_); + zero_v2(relative_pos_); - if (!this->m_movieClip) { + if (!movie_clip_) { return; } - tracking = &this->m_movieClip->tracking; + tracking = &movie_clip_->tracking; - BKE_movieclip_user_set_frame(&user, this->m_framenumber); - BKE_movieclip_get_size(this->m_movieClip, &user, &this->m_width, &this->m_height); + BKE_movieclip_user_set_frame(&user, framenumber_); + BKE_movieclip_get_size(movie_clip_, &user, &width_, &height_); - object = BKE_tracking_object_get_named(tracking, this->m_trackingObjectName); + object = BKE_tracking_object_get_named(tracking, tracking_object_name_); if (object) { MovieTrackingTrack *track; - track = BKE_tracking_track_get_named(tracking, object, this->m_trackName); + track = BKE_tracking_track_get_named(tracking, object, track_name_); if (track) { MovieTrackingMarker *marker; - int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_framenumber); + int clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, framenumber_); marker = BKE_tracking_marker_get(track, clip_framenr); - copy_v2_v2(this->m_markerPos, marker->pos); + copy_v2_v2(marker_pos_, marker->pos); - if (this->m_speed_output) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_relativeFrame); + if (speed_output_) { + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, + relative_frame_); marker = BKE_tracking_marker_get_exact(track, relative_clip_framenr); if (marker != nullptr && (marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(this->m_relativePos, marker->pos); + copy_v2_v2(relative_pos_, marker->pos); } else { - copy_v2_v2(this->m_relativePos, this->m_markerPos); + copy_v2_v2(relative_pos_, marker_pos_); } - if (this->m_relativeFrame < this->m_framenumber) { - swap_v2_v2(this->m_relativePos, this->m_markerPos); + if (relative_frame_ < framenumber_) { + swap_v2_v2(relative_pos_, marker_pos_); } } - else if (this->m_position == CMP_TRACKPOS_RELATIVE_START) { + else if (position_ == CMP_TRACKPOS_RELATIVE_START) { int i; for (i = 0; i < track->markersnr; i++) { marker = &track->markers[i]; if ((marker->flag & MARKER_DISABLED) == 0) { - copy_v2_v2(this->m_relativePos, marker->pos); + copy_v2_v2(relative_pos_, marker->pos); break; } } } - else if (this->m_position == CMP_TRACKPOS_RELATIVE_FRAME) { - int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(this->m_movieClip, - this->m_relativeFrame); + else if (position_ == CMP_TRACKPOS_RELATIVE_FRAME) { + int relative_clip_framenr = BKE_movieclip_remap_scene_to_clip_frame(movie_clip_, + relative_frame_); marker = BKE_tracking_marker_get(track, relative_clip_framenr); - copy_v2_v2(this->m_relativePos, marker->pos); + copy_v2_v2(relative_pos_, marker->pos); } } } - track_position_ = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis]; - if (this->m_axis == 0) { - track_position_ *= this->m_width; + track_position_ = marker_pos_[axis_] - relative_pos_[axis_]; + if (axis_ == 0) { + track_position_ *= width_; } else { - track_position_ *= this->m_height; + track_position_ *= height_; } } -void TrackPositionOperation::executePixelSampled(float output[4], - float /*x*/, - float /*y*/, - PixelSampler /*sampler*/) +void TrackPositionOperation::execute_pixel_sampled(float output[4], + float /*x*/, + float /*y*/, + PixelSampler /*sampler*/) { - output[0] = this->m_markerPos[this->m_axis] - this->m_relativePos[this->m_axis]; + output[0] = marker_pos_[axis_] - relative_pos_[axis_]; - if (this->m_axis == 0) { - output[0] *= this->m_width; + if (axis_ == 0) { + output[0] *= width_; } else { - output[0] *= this->m_height; + output[0] *= height_; } } @@ -157,11 +150,9 @@ const float *TrackPositionOperation::get_constant_elem() return &track_position_; } -void TrackPositionOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void TrackPositionOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - resolution[0] = preferredResolution[0]; - resolution[1] = preferredResolution[1]; + r_area = preferred_area; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TrackPositionOperation.h b/source/blender/compositor/operations/COM_TrackPositionOperation.h index f716bd97737..1de8b72f783 100644 --- a/source/blender/compositor/operations/COM_TrackPositionOperation.h +++ b/source/blender/compositor/operations/COM_TrackPositionOperation.h @@ -35,66 +35,65 @@ namespace blender::compositor { */ class TrackPositionOperation : public ConstantOperation { protected: - MovieClip *m_movieClip; - int m_framenumber; - char m_trackingObjectName[64]; - char m_trackName[64]; - int m_axis; - int m_position; - int m_relativeFrame; - bool m_speed_output; + MovieClip *movie_clip_; + int framenumber_; + char tracking_object_name_[64]; + char track_name_[64]; + int axis_; + int position_; + int relative_frame_; + bool speed_output_; - int m_width, m_height; - float m_markerPos[2]; - float m_relativePos[2]; + int width_, height_; + float marker_pos_[2]; + float relative_pos_[2]; float track_position_; bool is_track_position_calculated_; /** * Determine the output resolution. The resolution is retrieved from the Renderer */ - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; public: TrackPositionOperation(); - void setMovieClip(MovieClip *clip) + void set_movie_clip(MovieClip *clip) { - this->m_movieClip = clip; + movie_clip_ = clip; } - void setTrackingObject(char *object) + void set_tracking_object(char *object) { - BLI_strncpy(this->m_trackingObjectName, object, sizeof(this->m_trackingObjectName)); + BLI_strncpy(tracking_object_name_, object, sizeof(tracking_object_name_)); } - void setTrackName(char *track) + void set_track_name(char *track) { - BLI_strncpy(this->m_trackName, track, sizeof(this->m_trackName)); + BLI_strncpy(track_name_, track, sizeof(track_name_)); } - void setFramenumber(int framenumber) + void set_framenumber(int framenumber) { - this->m_framenumber = framenumber; + framenumber_ = framenumber; } - void setAxis(int value) + void set_axis(int value) { - this->m_axis = value; + axis_ = value; } - void setPosition(int value) + void set_position(int value) { - this->m_position = value; + position_ = value; } - void setRelativeFrame(int value) + void set_relative_frame(int value) { - this->m_relativeFrame = value; + relative_frame_ = value; } - void setSpeedOutput(bool speed_output) + void set_speed_output(bool speed_output) { - this->m_speed_output = speed_output; + speed_output_ = speed_output; } - void initExecution() override; + void init_execution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; const float *get_constant_elem() override; diff --git a/source/blender/compositor/operations/COM_TransformOperation.cc b/source/blender/compositor/operations/COM_TransformOperation.cc index 2feaa0ae16d..be9bb32e7f0 100644 --- a/source/blender/compositor/operations/COM_TransformOperation.cc +++ b/source/blender/compositor/operations/COM_TransformOperation.cc @@ -17,65 +17,47 @@ */ #include "COM_TransformOperation.h" -#include "COM_ConstantOperation.h" #include "COM_RotateOperation.h" #include "COM_ScaleOperation.h" -#include "BLI_math.h" - namespace blender::compositor { TransformOperation::TransformOperation() { - addInputSocket(DataType::Color); - addInputSocket(DataType::Value); - addInputSocket(DataType::Value); - addInputSocket(DataType::Value); - addInputSocket(DataType::Value); - addOutputSocket(DataType::Color); + add_input_socket(DataType::Color, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_input_socket(DataType::Value, ResizeMode::None); + add_output_socket(DataType::Color); translate_factor_x_ = 1.0f; translate_factor_y_ = 1.0f; convert_degree_to_rad_ = false; sampler_ = PixelSampler::Bilinear; invert_ = false; + max_scale_canvas_size_ = {ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE, + ScaleOperation::DEFAULT_MAX_SCALE_CANVAS_SIZE}; +} + +void TransformOperation::set_scale_canvas_max_size(Size2f size) +{ + max_scale_canvas_size_ = size; } void TransformOperation::init_data() { - /* Translation. */ - translate_x_ = 0; - NodeOperation *x_op = getInputOperation(X_INPUT_INDEX); - if (x_op->get_flags().is_constant_operation) { - translate_x_ = static_cast(x_op)->get_constant_elem()[0] * - translate_factor_x_; - } - translate_y_ = 0; - NodeOperation *y_op = getInputOperation(Y_INPUT_INDEX); - if (y_op->get_flags().is_constant_operation) { - translate_y_ = static_cast(y_op)->get_constant_elem()[0] * - translate_factor_y_; - } - /* Scaling. */ - scale_center_x_ = getWidth() / 2.0; - scale_center_y_ = getHeight() / 2.0; - constant_scale_ = 1.0f; - NodeOperation *scale_op = getInputOperation(SCALE_INPUT_INDEX); - if (scale_op->get_flags().is_constant_operation) { - constant_scale_ = static_cast(scale_op)->get_constant_elem()[0]; - } + translate_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f) * + translate_factor_x_; + translate_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f) * + translate_factor_y_; - /* Rotation. */ - rotate_center_x_ = (getWidth() - 1.0) / 2.0; - rotate_center_y_ = (getHeight() - 1.0) / 2.0; - NodeOperation *degree_op = getInputOperation(DEGREE_INPUT_INDEX); - const bool is_constant_degree = degree_op->get_flags().is_constant_operation; - const float degree = is_constant_degree ? - static_cast(degree_op)->get_constant_elem()[0] : - 0.0f; + const float degree = get_input_operation(DEGREE_INPUT_INDEX)->get_constant_value_default(0.0f); const double rad = convert_degree_to_rad_ ? DEG2RAD((double)degree) : degree; rotate_cosine_ = cos(rad); rotate_sine_ = sin(rad); + + scale_ = get_input_operation(SCALE_INPUT_INDEX)->get_constant_value_default(1.0f); } void TransformOperation::get_area_of_interest(const int input_idx, @@ -84,26 +66,41 @@ void TransformOperation::get_area_of_interest(const int input_idx, { switch (input_idx) { case IMAGE_INPUT_INDEX: { - BLI_rcti_translate(&r_input_area, translate_x_, translate_y_); - ScaleOperation::scale_area( - r_input_area, scale_center_x_, scale_center_y_, constant_scale_, constant_scale_); - RotateOperation::get_area_rotation_bounds(r_input_area, - rotate_center_x_, - rotate_center_y_, - rotate_sine_, - rotate_cosine_, - r_input_area); + NodeOperation *image_op = get_input_operation(IMAGE_INPUT_INDEX); + const rcti &image_canvas = image_op->get_canvas(); + if (invert_) { + /* Scale -> Rotate -> Translate. */ + r_input_area = output_area; + BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_); + RotateOperation::get_rotation_area_of_interest(scale_canvas_, + rotate_canvas_, + rotate_sine_, + rotate_cosine_, + r_input_area, + r_input_area); + ScaleOperation::get_scale_area_of_interest( + image_canvas, scale_canvas_, scale_, scale_, r_input_area, r_input_area); + } + else { + /* Translate -> Rotate -> Scale. */ + ScaleOperation::get_scale_area_of_interest( + rotate_canvas_, scale_canvas_, scale_, scale_, output_area, r_input_area); + RotateOperation::get_rotation_area_of_interest(translate_canvas_, + rotate_canvas_, + rotate_sine_, + rotate_cosine_, + r_input_area, + r_input_area); + BLI_rcti_translate(&r_input_area, -translate_x_, -translate_y_); + } expand_area_for_sampler(r_input_area, sampler_); break; } case X_INPUT_INDEX: case Y_INPUT_INDEX: - case DEGREE_INPUT_INDEX: { - r_input_area = COM_SINGLE_ELEM_AREA; - break; - } + case DEGREE_INPUT_INDEX: case SCALE_INPUT_INDEX: { - r_input_area = output_area; + r_input_area = COM_CONSTANT_INPUT_AREA_OF_INTEREST; break; } } @@ -114,8 +111,7 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output, Span inputs) { const MemoryBuffer *input_img = inputs[IMAGE_INPUT_INDEX]; - MemoryBuffer *input_scale = inputs[SCALE_INPUT_INDEX]; - BuffersIterator it = output->iterate_with({input_scale}, area); + BuffersIterator it = output->iterate_with({}, area); if (invert_) { transform_inverted(it, input_img); } @@ -124,31 +120,111 @@ void TransformOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void TransformOperation::transform(BuffersIterator &it, const MemoryBuffer *input_img) +void TransformOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - for (; !it.is_end(); ++it) { - const float scale = *it.in(0); - float x = it.x - translate_x_; - float y = it.y - translate_y_; - RotateOperation::rotate_coords( - x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_); - x = ScaleOperation::scale_coord(x, scale_center_x_, scale); - y = ScaleOperation::scale_coord(y, scale_center_y_, scale); - input_img->read_elem_sampled(x, y, sampler_, it.out); + const bool image_determined = + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (image_determined) { + rcti image_canvas = r_area; + rcti unused; + get_input_socket(X_INPUT_INDEX)->determine_canvas(image_canvas, unused); + get_input_socket(Y_INPUT_INDEX)->determine_canvas(image_canvas, unused); + get_input_socket(DEGREE_INPUT_INDEX)->determine_canvas(image_canvas, unused); + get_input_socket(SCALE_INPUT_INDEX)->determine_canvas(image_canvas, unused); + + init_data(); + if (invert_) { + /* Scale -> Rotate -> Translate. */ + scale_canvas_ = image_canvas; + ScaleOperation::scale_area(scale_canvas_, scale_, scale_); + const Size2f max_scale_size = { + MAX2(BLI_rcti_size_x(&image_canvas), max_scale_canvas_size_.x), + MAX2(BLI_rcti_size_y(&image_canvas), max_scale_canvas_size_.y)}; + ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size); + + RotateOperation::get_rotation_canvas( + scale_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_); + + translate_canvas_ = rotate_canvas_; + BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_); + + r_area = translate_canvas_; + } + else { + /* Translate -> Rotate -> Scale. */ + translate_canvas_ = image_canvas; + BLI_rcti_translate(&translate_canvas_, translate_x_, translate_y_); + + RotateOperation::get_rotation_canvas( + translate_canvas_, rotate_sine_, rotate_cosine_, rotate_canvas_); + + scale_canvas_ = rotate_canvas_; + ScaleOperation::scale_area(scale_canvas_, scale_, scale_); + + const Size2f max_scale_size = { + MAX2(BLI_rcti_size_x(&rotate_canvas_), max_scale_canvas_size_.x), + MAX2(BLI_rcti_size_y(&rotate_canvas_), max_scale_canvas_size_.y)}; + ScaleOperation::clamp_area_size_max(scale_canvas_, max_scale_size); + + r_area = scale_canvas_; + } } } +/** Translate -> Rotate -> Scale. */ +void TransformOperation::transform(BuffersIterator &it, const MemoryBuffer *input_img) +{ + float rotate_center_x, rotate_center_y; + RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y); + float rotate_offset_x, rotate_offset_y; + RotateOperation::get_rotation_offset( + translate_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y); + + const float scale_center_x = BLI_rcti_size_x(&rotate_canvas_) / 2.0f; + const float scale_center_y = BLI_rcti_size_y(&rotate_canvas_) / 2.0f; + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(rotate_canvas_, scale_canvas_, scale_offset_x, scale_offset_y); + + for (; !it.is_end(); ++it) { + float x = ScaleOperation::scale_coord_inverted(it.x + scale_offset_x, scale_center_x, scale_); + float y = ScaleOperation::scale_coord_inverted(it.y + scale_offset_y, scale_center_y, scale_); + + x = rotate_offset_x + x; + y = rotate_offset_y + y; + RotateOperation::rotate_coords( + x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_); + + input_img->read_elem_sampled(x - translate_x_, y - translate_y_, sampler_, it.out); + } +} + +/** Scale -> Rotate -> Translate. */ void TransformOperation::transform_inverted(BuffersIterator &it, const MemoryBuffer *input_img) { + const rcti &image_canvas = get_input_operation(IMAGE_INPUT_INDEX)->get_canvas(); + const float scale_center_x = BLI_rcti_size_x(&image_canvas) / 2.0f - translate_x_; + const float scale_center_y = BLI_rcti_size_y(&image_canvas) / 2.0f - translate_y_; + float scale_offset_x, scale_offset_y; + ScaleOperation::get_scale_offset(image_canvas, scale_canvas_, scale_offset_x, scale_offset_y); + + float rotate_center_x, rotate_center_y; + RotateOperation::get_rotation_center(translate_canvas_, rotate_center_x, rotate_center_y); + rotate_center_x -= translate_x_; + rotate_center_y -= translate_y_; + float rotate_offset_x, rotate_offset_y; + RotateOperation::get_rotation_offset( + scale_canvas_, rotate_canvas_, rotate_offset_x, rotate_offset_y); + for (; !it.is_end(); ++it) { - const float scale = *it.in(0); - float x = ScaleOperation::scale_coord(it.x, scale_center_x_, scale); - float y = ScaleOperation::scale_coord(it.y, scale_center_y_, scale); + float x = rotate_offset_x + (it.x - translate_x_); + float y = rotate_offset_y + (it.y - translate_y_); RotateOperation::rotate_coords( - x, y, rotate_center_x_, rotate_center_y_, rotate_sine_, rotate_cosine_); - x -= translate_x_; - y -= translate_y_; + x, y, rotate_center_x, rotate_center_y, rotate_sine_, rotate_cosine_); + + x = ScaleOperation::scale_coord_inverted(x + scale_offset_x, scale_center_x, scale_); + y = ScaleOperation::scale_coord_inverted(y + scale_offset_y, scale_center_y, scale_); + input_img->read_elem_sampled(x, y, sampler_, it.out); } } diff --git a/source/blender/compositor/operations/COM_TransformOperation.h b/source/blender/compositor/operations/COM_TransformOperation.h index 480998a0207..3c5584a1bea 100644 --- a/source/blender/compositor/operations/COM_TransformOperation.h +++ b/source/blender/compositor/operations/COM_TransformOperation.h @@ -30,15 +30,14 @@ class TransformOperation : public MultiThreadedOperation { constexpr static int DEGREE_INPUT_INDEX = 3; constexpr static int SCALE_INPUT_INDEX = 4; - float scale_center_x_; - float scale_center_y_; - float rotate_center_x_; - float rotate_center_y_; float rotate_cosine_; float rotate_sine_; - float translate_x_; - float translate_y_; - float constant_scale_; + int translate_x_; + int translate_y_; + float scale_; + rcti scale_canvas_; + rcti rotate_canvas_; + rcti translate_canvas_; /* Set variables. */ PixelSampler sampler_; @@ -46,6 +45,7 @@ class TransformOperation : public MultiThreadedOperation { float translate_factor_x_; float translate_factor_y_; bool invert_; + Size2f max_scale_canvas_size_; public: TransformOperation(); @@ -71,16 +71,18 @@ class TransformOperation : public MultiThreadedOperation { invert_ = value; } + void set_scale_canvas_max_size(Size2f size); + void init_data() override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + private: - /** Translate -> Rotate -> Scale. */ void transform(BuffersIterator &it, const MemoryBuffer *input_img); - /** Scale -> Rotate -> Translate. */ void transform_inverted(BuffersIterator &it, const MemoryBuffer *input_img); }; diff --git a/source/blender/compositor/operations/COM_TranslateOperation.cc b/source/blender/compositor/operations/COM_TranslateOperation.cc index a3db086e974..89c7468a67c 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.cc +++ b/source/blender/compositor/operations/COM_TranslateOperation.cc @@ -23,69 +23,70 @@ namespace blender::compositor { TranslateOperation::TranslateOperation() : TranslateOperation(DataType::Color) { } -TranslateOperation::TranslateOperation(DataType data_type) +TranslateOperation::TranslateOperation(DataType data_type, ResizeMode resize_mode) { - this->addInputSocket(data_type); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); - this->addOutputSocket(data_type); - this->setResolutionInputSocketIndex(0); - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; - this->m_isDeltaSet = false; - this->m_factorX = 1.0f; - this->m_factorY = 1.0f; + this->add_input_socket(data_type, resize_mode); + this->add_input_socket(DataType::Value, ResizeMode::None); + this->add_input_socket(DataType::Value, ResizeMode::None); + this->add_output_socket(data_type); + this->set_canvas_input_index(0); + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; + is_delta_set_ = false; + factor_x_ = 1.0f; + factor_y_ = 1.0f; this->x_extend_mode_ = MemoryBufferExtend::Clip; this->y_extend_mode_ = MemoryBufferExtend::Clip; } -void TranslateOperation::initExecution() + +void TranslateOperation::init_execution() { - this->m_inputOperation = this->getInputSocketReader(0); - this->m_inputXOperation = this->getInputSocketReader(1); - this->m_inputYOperation = this->getInputSocketReader(2); + input_operation_ = this->get_input_socket_reader(0); + input_xoperation_ = this->get_input_socket_reader(1); + input_yoperation_ = this->get_input_socket_reader(2); } -void TranslateOperation::deinitExecution() +void TranslateOperation::deinit_execution() { - this->m_inputOperation = nullptr; - this->m_inputXOperation = nullptr; - this->m_inputYOperation = nullptr; + input_operation_ = nullptr; + input_xoperation_ = nullptr; + input_yoperation_ = nullptr; } -void TranslateOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler /*sampler*/) +void TranslateOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler /*sampler*/) { - ensureDelta(); + ensure_delta(); - float originalXPos = x - this->getDeltaX(); - float originalYPos = y - this->getDeltaY(); + float original_xpos = x - this->getDeltaX(); + float original_ypos = y - this->getDeltaY(); - this->m_inputOperation->readSampled(output, originalXPos, originalYPos, PixelSampler::Bilinear); + input_operation_->read_sampled(output, original_xpos, original_ypos, PixelSampler::Bilinear); } -bool TranslateOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool TranslateOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; + rcti new_input; - ensureDelta(); + ensure_delta(); - newInput.xmin = input->xmin - this->getDeltaX(); - newInput.xmax = input->xmax - this->getDeltaX(); - newInput.ymin = input->ymin - this->getDeltaY(); - newInput.ymax = input->ymax - this->getDeltaY(); + new_input.xmin = input->xmin - this->getDeltaX(); + new_input.xmax = input->xmax - this->getDeltaX(); + new_input.ymin = input->ymin - this->getDeltaY(); + new_input.ymax = input->ymax - this->getDeltaY(); - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } void TranslateOperation::setFactorXY(float factorX, float factorY) { - m_factorX = factorX; - m_factorY = factorY; + factor_x_ = factorX; + factor_y_ = factorY; } void TranslateOperation::set_wrapping(int wrapping_type) @@ -111,7 +112,7 @@ void TranslateOperation::get_area_of_interest(const int input_idx, rcti &r_input_area) { if (input_idx == 0) { - ensureDelta(); + ensure_delta(); r_input_area = output_area; if (x_extend_mode_ == MemoryBufferExtend::Clip) { const int delta_x = this->getDeltaX(); @@ -122,6 +123,9 @@ void TranslateOperation::get_area_of_interest(const int input_idx, BLI_rcti_translate(&r_input_area, 0, -delta_y); } } + else { + r_input_area = output_area; + } } void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output, @@ -142,4 +146,27 @@ void TranslateOperation::update_memory_buffer_partial(MemoryBuffer *output, } } +TranslateCanvasOperation::TranslateCanvasOperation() + : TranslateOperation(DataType::Color, ResizeMode::None) +{ +} + +void TranslateCanvasOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) +{ + const bool determined = + get_input_socket(IMAGE_INPUT_INDEX)->determine_canvas(preferred_area, r_area); + if (determined) { + NodeOperationInput *x_socket = get_input_socket(X_INPUT_INDEX); + NodeOperationInput *y_socket = get_input_socket(Y_INPUT_INDEX); + rcti unused; + x_socket->determine_canvas(r_area, unused); + y_socket->determine_canvas(r_area, unused); + + ensure_delta(); + const float delta_x = x_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaX() : 0.0f; + const float delta_y = y_extend_mode_ == MemoryBufferExtend::Clip ? getDeltaY() : 0.0f; + BLI_rcti_translate(&r_area, delta_x, delta_y); + } +} + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_TranslateOperation.h b/source/blender/compositor/operations/COM_TranslateOperation.h index ce1965cecef..30e489bf395 100644 --- a/source/blender/compositor/operations/COM_TranslateOperation.h +++ b/source/blender/compositor/operations/COM_TranslateOperation.h @@ -24,62 +24,61 @@ namespace blender::compositor { class TranslateOperation : public MultiThreadedOperation { + protected: + static constexpr int IMAGE_INPUT_INDEX = 0; + static constexpr int X_INPUT_INDEX = 1; + static constexpr int Y_INPUT_INDEX = 2; + private: - SocketReader *m_inputOperation; - SocketReader *m_inputXOperation; - SocketReader *m_inputYOperation; - float m_deltaX; - float m_deltaY; - bool m_isDeltaSet; - float m_factorX; - float m_factorY; + SocketReader *input_operation_; + SocketReader *input_xoperation_; + SocketReader *input_yoperation_; + float delta_x_; + float delta_y_; + bool is_delta_set_; + float factor_x_; + float factor_y_; + + protected: MemoryBufferExtend x_extend_mode_; MemoryBufferExtend y_extend_mode_; public: TranslateOperation(); - TranslateOperation(DataType data_type); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + TranslateOperation(DataType data_type, ResizeMode mode = ResizeMode::Center); + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; float getDeltaX() { - return this->m_deltaX * this->m_factorX; + return delta_x_ * factor_x_; } float getDeltaY() { - return this->m_deltaY * this->m_factorY; + return delta_y_ * factor_y_; } - inline void ensureDelta() + inline void ensure_delta() { - if (!this->m_isDeltaSet) { + if (!is_delta_set_) { if (execution_model_ == eExecutionModel::Tiled) { - float tempDelta[4]; - this->m_inputXOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - this->m_deltaX = tempDelta[0]; - this->m_inputYOperation->readSampled(tempDelta, 0, 0, PixelSampler::Nearest); - this->m_deltaY = tempDelta[0]; + float temp_delta[4]; + input_xoperation_->read_sampled(temp_delta, 0, 0, PixelSampler::Nearest); + delta_x_ = temp_delta[0]; + input_yoperation_->read_sampled(temp_delta, 0, 0, PixelSampler::Nearest); + delta_y_ = temp_delta[0]; } else { - this->m_deltaX = 0; - NodeOperation *x_op = getInputOperation(1); - if (x_op->get_flags().is_constant_operation) { - this->m_deltaX = ((ConstantOperation *)x_op)->get_constant_elem()[0]; - } - this->m_deltaY = 0; - NodeOperation *y_op = getInputOperation(2); - if (y_op->get_flags().is_constant_operation) { - this->m_deltaY = ((ConstantOperation *)y_op)->get_constant_elem()[0]; - } + delta_x_ = get_input_operation(X_INPUT_INDEX)->get_constant_value_default(0.0f); + delta_y_ = get_input_operation(Y_INPUT_INDEX)->get_constant_value_default(0.0f); } - this->m_isDeltaSet = true; + is_delta_set_ = true; } } @@ -93,4 +92,10 @@ class TranslateOperation : public MultiThreadedOperation { Span inputs) override; }; +class TranslateCanvasOperation : public TranslateOperation { + public: + TranslateCanvasOperation(); + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; +}; + } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc index b8274576cb5..891518d53bf 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.cc @@ -17,139 +17,135 @@ */ #include "COM_VariableSizeBokehBlurOperation.h" -#include "BLI_math.h" -#include "COM_ExecutionSystem.h" #include "COM_OpenCLDevice.h" -#include "RE_pipeline.h" - namespace blender::compositor { VariableSizeBokehBlurOperation::VariableSizeBokehBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color, ResizeMode::None); /* Do not resize the bokeh image. */ - this->addInputSocket(DataType::Value); /* Radius. */ + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color, ResizeMode::Align); /* Do not resize the bokeh image. */ + this->add_input_socket(DataType::Value); /* Radius. */ #ifdef COM_DEFOCUS_SEARCH /* Inverse search radius optimization structure. */ - this->addInputSocket(DataType::Color, ResizeMode::None); + this->add_input_socket(DataType::Color, ResizeMode::None); #endif - this->addOutputSocket(DataType::Color); - flags.complex = true; - flags.open_cl = true; + this->add_output_socket(DataType::Color); + flags_.complex = true; + flags_.open_cl = true; - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputSizeProgram = nullptr; - this->m_maxBlur = 32.0f; - this->m_threshold = 1.0f; - this->m_do_size_scale = false; + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_size_program_ = nullptr; + max_blur_ = 32.0f; + threshold_ = 1.0f; + do_size_scale_ = false; #ifdef COM_DEFOCUS_SEARCH - this->m_inputSearchProgram = nullptr; + input_search_program_ = nullptr; #endif } -void VariableSizeBokehBlurOperation::initExecution() +void VariableSizeBokehBlurOperation::init_execution() { - this->m_inputProgram = getInputSocketReader(0); - this->m_inputBokehProgram = getInputSocketReader(1); - this->m_inputSizeProgram = getInputSocketReader(2); + input_program_ = get_input_socket_reader(0); + input_bokeh_program_ = get_input_socket_reader(1); + input_size_program_ = get_input_socket_reader(2); #ifdef COM_DEFOCUS_SEARCH - this->m_inputSearchProgram = getInputSocketReader(3); + input_search_program_ = get_input_socket_reader(3); #endif - QualityStepHelper::initExecution(COM_QH_INCREASE); + QualityStepHelper::init_execution(COM_QH_INCREASE); } struct VariableSizeBokehBlurTileData { MemoryBuffer *color; MemoryBuffer *bokeh; MemoryBuffer *size; - int maxBlurScalar; + int max_blur_scalar; }; -void *VariableSizeBokehBlurOperation::initializeTileData(rcti *rect) +void *VariableSizeBokehBlurOperation::initialize_tile_data(rcti *rect) { VariableSizeBokehBlurTileData *data = new VariableSizeBokehBlurTileData(); - data->color = (MemoryBuffer *)this->m_inputProgram->initializeTileData(rect); - data->bokeh = (MemoryBuffer *)this->m_inputBokehProgram->initializeTileData(rect); - data->size = (MemoryBuffer *)this->m_inputSizeProgram->initializeTileData(rect); + data->color = (MemoryBuffer *)input_program_->initialize_tile_data(rect); + data->bokeh = (MemoryBuffer *)input_bokeh_program_->initialize_tile_data(rect); + data->size = (MemoryBuffer *)input_size_program_->initialize_tile_data(rect); rcti rect2; - this->determineDependingAreaOfInterest( - rect, (ReadBufferOperation *)this->m_inputSizeProgram, &rect2); + this->determine_depending_area_of_interest( + rect, (ReadBufferOperation *)input_size_program_, &rect2); - const float max_dim = MAX2(m_width, m_height); - const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const float max_dim = MAX2(this->get_width(), this->get_height()); + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - data->maxBlurScalar = (int)(data->size->get_max_value(rect2) * scalar); - CLAMP(data->maxBlurScalar, 1.0f, this->m_maxBlur); + data->max_blur_scalar = (int)(data->size->get_max_value(rect2) * scalar); + CLAMP(data->max_blur_scalar, 1.0f, max_blur_); return data; } -void VariableSizeBokehBlurOperation::deinitializeTileData(rcti * /*rect*/, void *data) +void VariableSizeBokehBlurOperation::deinitialize_tile_data(rcti * /*rect*/, void *data) { VariableSizeBokehBlurTileData *result = (VariableSizeBokehBlurTileData *)data; delete result; } -void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, void *data) +void VariableSizeBokehBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { - VariableSizeBokehBlurTileData *tileData = (VariableSizeBokehBlurTileData *)data; - MemoryBuffer *inputProgramBuffer = tileData->color; - MemoryBuffer *inputBokehBuffer = tileData->bokeh; - MemoryBuffer *inputSizeBuffer = tileData->size; - float *inputSizeFloatBuffer = inputSizeBuffer->getBuffer(); - float *inputProgramFloatBuffer = inputProgramBuffer->getBuffer(); - float readColor[4]; + VariableSizeBokehBlurTileData *tile_data = (VariableSizeBokehBlurTileData *)data; + MemoryBuffer *input_program_buffer = tile_data->color; + MemoryBuffer *input_bokeh_buffer = tile_data->bokeh; + MemoryBuffer *input_size_buffer = tile_data->size; + float *input_size_float_buffer = input_size_buffer->get_buffer(); + float *input_program_float_buffer = input_program_buffer->get_buffer(); + float read_color[4]; float bokeh[4]; - float tempSize[4]; + float temp_size[4]; float multiplier_accum[4]; float color_accum[4]; - const float max_dim = MAX2(m_width, m_height); - const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; - int maxBlurScalar = tileData->maxBlurScalar; + const float max_dim = MAX2(get_width(), get_height()); + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; + int max_blur_scalar = tile_data->max_blur_scalar; - BLI_assert(inputBokehBuffer->getWidth() == COM_BLUR_BOKEH_PIXELS); - BLI_assert(inputBokehBuffer->getHeight() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(input_bokeh_buffer->get_width() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(input_bokeh_buffer->get_height() == COM_BLUR_BOKEH_PIXELS); #ifdef COM_DEFOCUS_SEARCH float search[4]; - this->m_inputSearchProgram->read(search, - x / InverseSearchRadiusOperation::DIVIDER, - y / InverseSearchRadiusOperation::DIVIDER, - nullptr); + input_search_program_->read(search, + x / InverseSearchRadiusOperation::DIVIDER, + y / InverseSearchRadiusOperation::DIVIDER, + nullptr); int minx = search[0]; int miny = search[1]; int maxx = search[2]; int maxy = search[3]; #else - int minx = MAX2(x - maxBlurScalar, 0); - int miny = MAX2(y - maxBlurScalar, 0); - int maxx = MIN2(x + maxBlurScalar, (int)m_width); - int maxy = MIN2(y + maxBlurScalar, (int)m_height); + int minx = MAX2(x - max_blur_scalar, 0); + int miny = MAX2(y - max_blur_scalar, 0); + int maxx = MIN2(x + max_blur_scalar, (int)get_width()); + int maxy = MIN2(y + max_blur_scalar, (int)get_height()); #endif { - inputSizeBuffer->readNoCheck(tempSize, x, y); - inputProgramBuffer->readNoCheck(readColor, x, y); + input_size_buffer->read_no_check(temp_size, x, y); + input_program_buffer->read_no_check(read_color, x, y); - copy_v4_v4(color_accum, readColor); + copy_v4_v4(color_accum, read_color); copy_v4_fl(multiplier_accum, 1.0f); - float size_center = tempSize[0] * scalar; + float size_center = temp_size[0] * scalar; - const int addXStepValue = QualityStepHelper::getStep(); - const int addYStepValue = addXStepValue; - const int addXStepColor = addXStepValue * COM_DATA_TYPE_COLOR_CHANNELS; + const int add_xstep_value = QualityStepHelper::get_step(); + const int add_ystep_value = add_xstep_value; + const int add_xstep_color = add_xstep_value * COM_DATA_TYPE_COLOR_CHANNELS; - if (size_center > this->m_threshold) { - for (int ny = miny; ny < maxy; ny += addYStepValue) { + if (size_center > threshold_) { + for (int ny = miny; ny < maxy; ny += add_ystep_value) { float dy = ny - y; - int offsetValueNy = ny * inputSizeBuffer->getWidth(); - int offsetValueNxNy = offsetValueNy + (minx); - int offsetColorNxNy = offsetValueNxNy * COM_DATA_TYPE_COLOR_CHANNELS; - for (int nx = minx; nx < maxx; nx += addXStepValue) { + int offset_value_ny = ny * input_size_buffer->get_width(); + int offset_value_nx_ny = offset_value_ny + (minx); + int offset_color_nx_ny = offset_value_nx_ny * COM_DATA_TYPE_COLOR_CHANNELS; + for (int nx = minx; nx < maxx; nx += add_xstep_value) { if (nx != x || ny != y) { - float size = MIN2(inputSizeFloatBuffer[offsetValueNxNy] * scalar, size_center); - if (size > this->m_threshold) { + float size = MIN2(input_size_float_buffer[offset_value_nx_ny] * scalar, size_center); + if (size > threshold_) { float dx = nx - x; if (size > fabsf(dx) && size > fabsf(dy)) { float uv[2] = { @@ -158,14 +154,14 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, (float)(COM_BLUR_BOKEH_PIXELS / 2) + (dy / size) * (float)((COM_BLUR_BOKEH_PIXELS / 2) - 1), }; - inputBokehBuffer->read(bokeh, uv[0], uv[1]); - madd_v4_v4v4(color_accum, bokeh, &inputProgramFloatBuffer[offsetColorNxNy]); + input_bokeh_buffer->read(bokeh, uv[0], uv[1]); + madd_v4_v4v4(color_accum, bokeh, &input_program_float_buffer[offset_color_nx_ny]); add_v4_v4(multiplier_accum, bokeh); } } } - offsetColorNxNy += addXStepColor; - offsetValueNxNy += addXStepValue; + offset_color_nx_ny += add_xstep_color; + offset_value_nx_ny += add_xstep_value; } } } @@ -176,102 +172,105 @@ void VariableSizeBokehBlurOperation::executePixel(float output[4], int x, int y, output[3] = color_accum[3] / multiplier_accum[3]; /* blend in out values over the threshold, otherwise we get sharp, ugly transitions */ - if ((size_center > this->m_threshold) && (size_center < this->m_threshold * 2.0f)) { + if ((size_center > threshold_) && (size_center < threshold_ * 2.0f)) { /* factor from 0-1 */ - float fac = (size_center - this->m_threshold) / this->m_threshold; - interp_v4_v4v4(output, readColor, output, fac); + float fac = (size_center - threshold_) / threshold_; + interp_v4_v4v4(output, read_color, output, fac); } } } -void VariableSizeBokehBlurOperation::executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list * /*clKernelsToCleanUp*/) +void VariableSizeBokehBlurOperation::execute_opencl( + OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list * /*cl_kernels_to_clean_up*/) { - cl_kernel defocusKernel = device->COM_clCreateKernel("defocusKernel", nullptr); + cl_kernel defocus_kernel = device->COM_cl_create_kernel("defocus_kernel", nullptr); - cl_int step = this->getStep(); - cl_int maxBlur; - cl_float threshold = this->m_threshold; + cl_int step = this->get_step(); + cl_int max_blur; + cl_float threshold = threshold_; - MemoryBuffer *sizeMemoryBuffer = this->m_inputSizeProgram->getInputMemoryBuffer( - inputMemoryBuffers); + MemoryBuffer *size_memory_buffer = input_size_program_->get_input_memory_buffer( + input_memory_buffers); - const float max_dim = MAX2(m_width, m_height); - cl_float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const float max_dim = MAX2(get_width(), get_height()); + cl_float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; - maxBlur = (cl_int)min_ff(sizeMemoryBuffer->get_max_value() * scalar, (float)this->m_maxBlur); + max_blur = (cl_int)min_ff(size_memory_buffer->get_max_value() * scalar, (float)max_blur_); - device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 0, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputProgram); - device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 1, -1, clMemToCleanUp, inputMemoryBuffers, this->m_inputBokehProgram); - device->COM_clAttachMemoryBufferToKernelParameter( - defocusKernel, 2, 4, clMemToCleanUp, inputMemoryBuffers, this->m_inputSizeProgram); - device->COM_clAttachOutputMemoryBufferToKernelParameter(defocusKernel, 3, clOutputBuffer); - device->COM_clAttachMemoryBufferOffsetToKernelParameter(defocusKernel, 5, outputMemoryBuffer); - clSetKernelArg(defocusKernel, 6, sizeof(cl_int), &step); - clSetKernelArg(defocusKernel, 7, sizeof(cl_int), &maxBlur); - clSetKernelArg(defocusKernel, 8, sizeof(cl_float), &threshold); - clSetKernelArg(defocusKernel, 9, sizeof(cl_float), &scalar); - device->COM_clAttachSizeToKernelParameter(defocusKernel, 10, this); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + defocus_kernel, 0, -1, cl_mem_to_clean_up, input_memory_buffers, input_program_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + defocus_kernel, 1, -1, cl_mem_to_clean_up, input_memory_buffers, input_bokeh_program_); + device->COM_cl_attach_memory_buffer_to_kernel_parameter( + defocus_kernel, 2, 4, cl_mem_to_clean_up, input_memory_buffers, input_size_program_); + device->COM_cl_attach_output_memory_buffer_to_kernel_parameter( + defocus_kernel, 3, cl_output_buffer); + device->COM_cl_attach_memory_buffer_offset_to_kernel_parameter( + defocus_kernel, 5, output_memory_buffer); + clSetKernelArg(defocus_kernel, 6, sizeof(cl_int), &step); + clSetKernelArg(defocus_kernel, 7, sizeof(cl_int), &max_blur); + clSetKernelArg(defocus_kernel, 8, sizeof(cl_float), &threshold); + clSetKernelArg(defocus_kernel, 9, sizeof(cl_float), &scalar); + device->COM_cl_attach_size_to_kernel_parameter(defocus_kernel, 10, this); - device->COM_clEnqueueRange(defocusKernel, outputMemoryBuffer, 11, this); + device->COM_cl_enqueue_range(defocus_kernel, output_memory_buffer, 11, this); } -void VariableSizeBokehBlurOperation::deinitExecution() +void VariableSizeBokehBlurOperation::deinit_execution() { - this->m_inputProgram = nullptr; - this->m_inputBokehProgram = nullptr; - this->m_inputSizeProgram = nullptr; + input_program_ = nullptr; + input_bokeh_program_ = nullptr; + input_size_program_ = nullptr; #ifdef COM_DEFOCUS_SEARCH - this->m_inputSearchProgram = nullptr; + input_search_program_ = nullptr; #endif } -bool VariableSizeBokehBlurOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool VariableSizeBokehBlurOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newInput; - rcti bokehInput; + rcti new_input; + rcti bokeh_input; - const float max_dim = MAX2(m_width, m_height); - const float scalar = this->m_do_size_scale ? (max_dim / 100.0f) : 1.0f; - int maxBlurScalar = this->m_maxBlur * scalar; + const float max_dim = MAX2(get_width(), get_height()); + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; + int max_blur_scalar = max_blur_ * scalar; - newInput.xmax = input->xmax + maxBlurScalar + 2; - newInput.xmin = input->xmin - maxBlurScalar + 2; - newInput.ymax = input->ymax + maxBlurScalar - 2; - newInput.ymin = input->ymin - maxBlurScalar - 2; - bokehInput.xmax = COM_BLUR_BOKEH_PIXELS; - bokehInput.xmin = 0; - bokehInput.ymax = COM_BLUR_BOKEH_PIXELS; - bokehInput.ymin = 0; + new_input.xmax = input->xmax + max_blur_scalar + 2; + new_input.xmin = input->xmin - max_blur_scalar + 2; + new_input.ymax = input->ymax + max_blur_scalar - 2; + new_input.ymin = input->ymin - max_blur_scalar - 2; + bokeh_input.xmax = COM_BLUR_BOKEH_PIXELS; + bokeh_input.xmin = 0; + bokeh_input.ymax = COM_BLUR_BOKEH_PIXELS; + bokeh_input.ymin = 0; - NodeOperation *operation = getInputOperation(2); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + NodeOperation *operation = get_input_operation(2); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } - operation = getInputOperation(1); - if (operation->determineDependingAreaOfInterest(&bokehInput, readOperation, output)) { + operation = get_input_operation(1); + if (operation->determine_depending_area_of_interest(&bokeh_input, read_operation, output)) { return true; } #ifdef COM_DEFOCUS_SEARCH - rcti searchInput; - searchInput.xmax = (input->xmax / InverseSearchRadiusOperation::DIVIDER) + 1; - searchInput.xmin = (input->xmin / InverseSearchRadiusOperation::DIVIDER) - 1; - searchInput.ymax = (input->ymax / InverseSearchRadiusOperation::DIVIDER) + 1; - searchInput.ymin = (input->ymin / InverseSearchRadiusOperation::DIVIDER) - 1; - operation = getInputOperation(3); - if (operation->determineDependingAreaOfInterest(&searchInput, readOperation, output)) { + rcti search_input; + search_input.xmax = (input->xmax / InverseSearchRadiusOperation::DIVIDER) + 1; + search_input.xmin = (input->xmin / InverseSearchRadiusOperation::DIVIDER) - 1; + search_input.ymax = (input->ymax / InverseSearchRadiusOperation::DIVIDER) + 1; + search_input.ymin = (input->ymin / InverseSearchRadiusOperation::DIVIDER) - 1; + operation = get_input_operation(3); + if (operation->determine_depending_area_of_interest(&search_input, read_operation, output)) { return true; } #endif - operation = getInputOperation(0); - if (operation->determineDependingAreaOfInterest(&newInput, readOperation, output)) { + operation = get_input_operation(0); + if (operation->determine_depending_area_of_interest(&new_input, read_operation, output)) { return true; } return false; @@ -284,9 +283,9 @@ void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx, switch (input_idx) { case IMAGE_INPUT_INDEX: case SIZE_INPUT_INDEX: { - const float max_dim = MAX2(getWidth(), getHeight()); - const float scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; - const int max_blur_scalar = m_maxBlur * scalar; + const float max_dim = MAX2(get_width(), get_height()); + const float scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; + const int max_blur_scalar = max_blur_ * scalar; r_input_area.xmax = output_area.xmax + max_blur_scalar + 2; r_input_area.xmin = output_area.xmin - max_blur_scalar - 2; r_input_area.ymax = output_area.ymax + max_blur_scalar + 2; @@ -294,10 +293,9 @@ void VariableSizeBokehBlurOperation::get_area_of_interest(const int input_idx, break; } case BOKEH_INPUT_INDEX: { - r_input_area.xmax = COM_BLUR_BOKEH_PIXELS; - r_input_area.xmin = 0; - r_input_area.ymax = COM_BLUR_BOKEH_PIXELS; - r_input_area.ymin = 0; + r_input_area = output_area; + r_input_area.xmax = r_input_area.xmin + COM_BLUR_BOKEH_PIXELS; + r_input_area.ymax = r_input_area.ymin + COM_BLUR_BOKEH_PIXELS; break; } #ifdef COM_DEFOCUS_SEARCH @@ -329,8 +327,8 @@ struct PixelData { static void blur_pixel(int x, int y, PixelData &p) { - BLI_assert(p.bokeh_input->getWidth() == COM_BLUR_BOKEH_PIXELS); - BLI_assert(p.bokeh_input->getHeight() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(p.bokeh_input->get_width() == COM_BLUR_BOKEH_PIXELS); + BLI_assert(p.bokeh_input->get_height() == COM_BLUR_BOKEH_PIXELS); #ifdef COM_DEFOCUS_SEARCH float search[4]; @@ -395,20 +393,20 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * p.bokeh_input = inputs[BOKEH_INPUT_INDEX]; p.size_input = inputs[SIZE_INPUT_INDEX]; p.image_input = inputs[IMAGE_INPUT_INDEX]; - p.step = QualityStepHelper::getStep(); - p.threshold = m_threshold; - p.image_width = this->getWidth(); - p.image_height = this->getHeight(); + p.step = QualityStepHelper::get_step(); + p.threshold = threshold_; + p.image_width = this->get_width(); + p.image_height = this->get_height(); rcti scalar_area; this->get_area_of_interest(SIZE_INPUT_INDEX, area, scalar_area); BLI_rcti_isect(&scalar_area, &p.size_input->get_rect(), &scalar_area); const float max_size = p.size_input->get_max_value(scalar_area); - const float max_dim = MAX2(this->getWidth(), this->getHeight()); - p.scalar = m_do_size_scale ? (max_dim / 100.0f) : 1.0f; + const float max_dim = MAX2(this->get_width(), this->get_height()); + p.scalar = do_size_scale_ ? (max_dim / 100.0f) : 1.0f; p.max_blur_scalar = static_cast(max_size * p.scalar); - CLAMP(p.max_blur_scalar, 1, m_maxBlur); + CLAMP(p.max_blur_scalar, 1, max_blur_); for (BuffersIterator it = output->iterate_with({p.image_input, p.size_input}, area); !it.is_end(); @@ -441,34 +439,34 @@ void VariableSizeBokehBlurOperation::update_memory_buffer_partial(MemoryBuffer * /* #InverseSearchRadiusOperation. */ InverseSearchRadiusOperation::InverseSearchRadiusOperation() { - this->addInputSocket(DataType::Value, ResizeMode::None); /* Radius. */ - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value, ResizeMode::Align); /* Radius. */ + this->add_output_socket(DataType::Color); this->flags.complex = true; - this->m_inputRadius = nullptr; + input_radius_ = nullptr; } -void InverseSearchRadiusOperation::initExecution() +void InverseSearchRadiusOperation::init_execution() { - this->m_inputRadius = this->getInputSocketReader(0); + input_radius_ = this->get_input_socket_reader(0); } -void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) +void *InverseSearchRadiusOperation::initialize_tile_data(rcti *rect) { MemoryBuffer *data = new MemoryBuffer(DataType::Color, rect); - float *buffer = data->getBuffer(); + float *buffer = data->get_buffer(); int x, y; - int width = this->m_inputRadius->getWidth(); - int height = this->m_inputRadius->getHeight(); + int width = input_radius_->get_width(); + int height = input_radius_->get_height(); float temp[4]; int offset = 0; for (y = rect->ymin; y < rect->ymax; y++) { for (x = rect->xmin; x < rect->xmax; x++) { int rx = x * DIVIDER; int ry = y * DIVIDER; - buffer[offset] = MAX2(rx - m_maxBlur, 0); - buffer[offset + 1] = MAX2(ry - m_maxBlur, 0); - buffer[offset + 2] = MIN2(rx + DIVIDER + m_maxBlur, width); - buffer[offset + 3] = MIN2(ry + DIVIDER + m_maxBlur, height); + buffer[offset] = MAX2(rx - max_blur_, 0); + buffer[offset + 1] = MAX2(ry - max_blur_, 0); + buffer[offset + 2] = MIN2(rx + DIVIDER + max_blur_, width); + buffer[offset + 3] = MIN2(ry + DIVIDER + max_blur_, height); offset += 4; } } @@ -483,7 +481,7 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) for (int x2 = 0; x2 < DIVIDER; x2++) { for (int y2 = 0; y2 < DIVIDER; y2++) { - this->m_inputRadius->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); + input_radius_->read(temp, rx + x2, ry + y2, PixelSampler::Nearest); if (radius < temp[0]) { radius = temp[0]; maxx = x2; @@ -491,15 +489,15 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) } } } - int impactRadius = ceil(radius / DIVIDER); - for (int x2 = x - impactRadius; x2 < x + impactRadius; x2++) { - for (int y2 = y - impactRadius; y2 < y + impactRadius; y2++) { + int impact_radius = ceil(radius / DIVIDER); + for (int x2 = x - impact_radius; x2 < x + impact_radius; x2++) { + for (int y2 = y - impact_radius; y2 < y + impact_radius; y2++) { data->read(temp, x2, y2); temp[0] = MIN2(temp[0], maxx); temp[1] = MIN2(temp[1], maxy); temp[2] = MAX2(temp[2], maxx); temp[3] = MAX2(temp[3], maxy); - data->writePixel(x2, y2, temp); + data->write_pixel(x2, y2, temp); } } } @@ -508,13 +506,13 @@ void *InverseSearchRadiusOperation::initializeTileData(rcti *rect) return data; } -void InverseSearchRadiusOperation::executePixelChunk(float output[4], int x, int y, void *data) +void InverseSearchRadiusOperation::execute_pixel_chunk(float output[4], int x, int y, void *data) { MemoryBuffer *buffer = (MemoryBuffer *)data; - buffer->readNoCheck(output, x, y); + buffer->read_no_check(output, x, y); } -void InverseSearchRadiusOperation::deinitializeTileData(rcti *rect, void *data) +void InverseSearchRadiusOperation::deinitialize_tile_data(rcti *rect, void *data) { if (data) { MemoryBuffer *mb = (MemoryBuffer *)data; @@ -522,28 +520,28 @@ void InverseSearchRadiusOperation::deinitializeTileData(rcti *rect, void *data) } } -void InverseSearchRadiusOperation::deinitExecution() +void InverseSearchRadiusOperation::deinit_execution() { - this->m_inputRadius = nullptr; + input_radius_ = nullptr; } -void InverseSearchRadiusOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void InverseSearchRadiusOperation::determine_resolution(unsigned int resolution[2], + unsigned int preferred_resolution[2]) { - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_resolution(resolution, preferred_resolution); resolution[0] = resolution[0] / DIVIDER; resolution[1] = resolution[1] / DIVIDER; } -bool InverseSearchRadiusOperation::determineDependingAreaOfInterest( - rcti *input, ReadBufferOperation *readOperation, rcti *output) +bool InverseSearchRadiusOperation::determine_depending_area_of_interest( + rcti *input, ReadBufferOperation *read_operation, rcti *output) { - rcti newRect; - newRect.ymin = input->ymin * DIVIDER - m_maxBlur; - newRect.ymax = input->ymax * DIVIDER + m_maxBlur; - newRect.xmin = input->xmin * DIVIDER - m_maxBlur; - newRect.xmax = input->xmax * DIVIDER + m_maxBlur; - return NodeOperation::determineDependingAreaOfInterest(&newRect, readOperation, output); + rcti new_rect; + new_rect.ymin = input->ymin * DIVIDER - max_blur_; + new_rect.ymax = input->ymax * DIVIDER + max_blur_; + new_rect.xmin = input->xmin * DIVIDER - max_blur_; + new_rect.xmax = input->xmax * DIVIDER + max_blur_; + return NodeOperation::determine_depending_area_of_interest(&new_rect, read_operation, output); } #endif diff --git a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h index 56b4677087b..7e53ed8a979 100644 --- a/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h +++ b/source/blender/compositor/operations/COM_VariableSizeBokehBlurOperation.h @@ -34,14 +34,14 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua static constexpr int DEFOCUS_INPUT_INDEX = 3; #endif - int m_maxBlur; - float m_threshold; - bool m_do_size_scale; /* scale size, matching 'BokehBlurNode' */ - SocketReader *m_inputProgram; - SocketReader *m_inputBokehProgram; - SocketReader *m_inputSizeProgram; + int max_blur_; + float threshold_; + bool do_size_scale_; /* scale size, matching 'BokehBlurNode' */ + SocketReader *input_program_; + SocketReader *input_bokeh_program_; + SocketReader *input_size_program_; #ifdef COM_DEFOCUS_SEARCH - SocketReader *m_inputSearchProgram; + SocketReader *input_search_program_; #endif public: @@ -50,47 +50,47 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; - void setMaxBlur(int maxRadius) + void set_max_blur(int max_radius) { - this->m_maxBlur = maxRadius; + max_blur_ = max_radius; } - void setThreshold(float threshold) + void set_threshold(float threshold) { - this->m_threshold = threshold; + threshold_ = threshold; } - void setDoScaleSize(bool scale_size) + void set_do_scale_size(bool scale_size) { - this->m_do_size_scale = scale_size; + do_size_scale_ = scale_size; } - void executeOpenCL(OpenCLDevice *device, - MemoryBuffer *outputMemoryBuffer, - cl_mem clOutputBuffer, - MemoryBuffer **inputMemoryBuffers, - std::list *clMemToCleanUp, - std::list *clKernelsToCleanUp) override; + void execute_opencl(OpenCLDevice *device, + MemoryBuffer *output_memory_buffer, + cl_mem cl_output_buffer, + MemoryBuffer **input_memory_buffers, + std::list *cl_mem_to_clean_up, + std::list *cl_kernels_to_clean_up) override; void get_area_of_interest(int input_idx, const rcti &output_area, rcti &r_input_area) override; void update_memory_buffer_partial(MemoryBuffer *output, @@ -102,8 +102,8 @@ class VariableSizeBokehBlurOperation : public MultiThreadedOperation, public Qua #ifdef COM_DEFOCUS_SEARCH class InverseSearchRadiusOperation : public NodeOperation { private: - int m_maxBlur; - SocketReader *m_inputRadius; + int max_blur_; + SocketReader *input_radius_; public: static const int DIVIDER = 4; @@ -113,29 +113,28 @@ class InverseSearchRadiusOperation : public NodeOperation { /** * The inner loop of this operation. */ - void executePixelChunk(float output[4], int x, int y, void *data); + void execute_pixel_chunk(float output[4], int x, int y, void *data); /** * Initialize the execution */ - void initExecution() override; - void *initializeTileData(rcti *rect) override; - void deinitializeTileData(rcti *rect, void *data) override; + void init_execution() override; + void *initialize_tile_data(rcti *rect) override; + void deinitialize_tile_data(rcti *rect, void *data) override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; - void setMaxBlur(int maxRadius) + void set_max_blur(int max_radius) { - this->m_maxBlur = maxRadius; + max_blur_ = max_radius; } }; #endif diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.cc b/source/blender/compositor/operations/COM_VectorBlurOperation.cc index 5405e6d424a..b220990db76 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.cc +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.cc @@ -16,12 +16,7 @@ * Copyright 2011, Blender Foundation. */ -#include - -#include "MEM_guardedalloc.h" - #include "BLI_jitter_2d.h" -#include "BLI_math.h" #include "COM_VectorBlurOperation.h" @@ -47,76 +42,76 @@ void antialias_tagbuf(int xsize, int ysize, char *rectmove); /* VectorBlurOperation */ VectorBlurOperation::VectorBlurOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); /* ZBUF */ - this->addInputSocket(DataType::Color); /* SPEED */ - this->addOutputSocket(DataType::Color); - this->m_settings = nullptr; - this->m_cachedInstance = nullptr; - this->m_inputImageProgram = nullptr; - this->m_inputSpeedProgram = nullptr; - this->m_inputZProgram = nullptr; - flags.complex = true; - flags.is_fullframe_operation = true; + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); /* ZBUF */ + this->add_input_socket(DataType::Color); /* SPEED */ + this->add_output_socket(DataType::Color); + settings_ = nullptr; + cached_instance_ = nullptr; + input_image_program_ = nullptr; + input_speed_program_ = nullptr; + input_zprogram_ = nullptr; + flags_.complex = true; + flags_.is_fullframe_operation = true; } -void VectorBlurOperation::initExecution() +void VectorBlurOperation::init_execution() { - initMutex(); - this->m_inputImageProgram = getInputSocketReader(0); - this->m_inputZProgram = getInputSocketReader(1); - this->m_inputSpeedProgram = getInputSocketReader(2); - this->m_cachedInstance = nullptr; - QualityStepHelper::initExecution(COM_QH_INCREASE); + init_mutex(); + input_image_program_ = get_input_socket_reader(0); + input_zprogram_ = get_input_socket_reader(1); + input_speed_program_ = get_input_socket_reader(2); + cached_instance_ = nullptr; + QualityStepHelper::init_execution(COM_QH_INCREASE); } -void VectorBlurOperation::executePixel(float output[4], int x, int y, void *data) +void VectorBlurOperation::execute_pixel(float output[4], int x, int y, void *data) { float *buffer = (float *)data; - int index = (y * this->getWidth() + x) * COM_DATA_TYPE_COLOR_CHANNELS; + int index = (y * this->get_width() + x) * COM_DATA_TYPE_COLOR_CHANNELS; copy_v4_v4(output, &buffer[index]); } -void VectorBlurOperation::deinitExecution() +void VectorBlurOperation::deinit_execution() { - deinitMutex(); - this->m_inputImageProgram = nullptr; - this->m_inputSpeedProgram = nullptr; - this->m_inputZProgram = nullptr; - if (this->m_cachedInstance) { - MEM_freeN(this->m_cachedInstance); - this->m_cachedInstance = nullptr; + deinit_mutex(); + input_image_program_ = nullptr; + input_speed_program_ = nullptr; + input_zprogram_ = nullptr; + if (cached_instance_) { + MEM_freeN(cached_instance_); + cached_instance_ = nullptr; } } -void *VectorBlurOperation::initializeTileData(rcti *rect) +void *VectorBlurOperation::initialize_tile_data(rcti *rect) { - if (this->m_cachedInstance) { - return this->m_cachedInstance; + if (cached_instance_) { + return cached_instance_; } - lockMutex(); - if (this->m_cachedInstance == nullptr) { - MemoryBuffer *tile = (MemoryBuffer *)this->m_inputImageProgram->initializeTileData(rect); - MemoryBuffer *speed = (MemoryBuffer *)this->m_inputSpeedProgram->initializeTileData(rect); - MemoryBuffer *z = (MemoryBuffer *)this->m_inputZProgram->initializeTileData(rect); - float *data = (float *)MEM_dupallocN(tile->getBuffer()); - this->generateVectorBlur(data, tile, speed, z); - this->m_cachedInstance = data; + lock_mutex(); + if (cached_instance_ == nullptr) { + MemoryBuffer *tile = (MemoryBuffer *)input_image_program_->initialize_tile_data(rect); + MemoryBuffer *speed = (MemoryBuffer *)input_speed_program_->initialize_tile_data(rect); + MemoryBuffer *z = (MemoryBuffer *)input_zprogram_->initialize_tile_data(rect); + float *data = (float *)MEM_dupallocN(tile->get_buffer()); + this->generate_vector_blur(data, tile, speed, z); + cached_instance_ = data; } - unlockMutex(); - return this->m_cachedInstance; + unlock_mutex(); + return cached_instance_; } -bool VectorBlurOperation::determineDependingAreaOfInterest(rcti * /*input*/, - ReadBufferOperation *readOperation, - rcti *output) +bool VectorBlurOperation::determine_depending_area_of_interest(rcti * /*input*/, + ReadBufferOperation *read_operation, + rcti *output) { - if (this->m_cachedInstance == nullptr) { - rcti newInput; - newInput.xmax = this->getWidth(); - newInput.xmin = 0; - newInput.ymax = this->getHeight(); - newInput.ymin = 0; - return NodeOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + if (cached_instance_ == nullptr) { + rcti new_input; + new_input.xmax = this->get_width(); + new_input.xmin = 0; + new_input.ymax = this->get_height(); + new_input.ymin = 0; + return NodeOperation::determine_depending_area_of_interest(&new_input, read_operation, output); } return false; @@ -126,10 +121,7 @@ void VectorBlurOperation::get_area_of_interest(const int UNUSED(input_idx), const rcti &UNUSED(output_area), rcti &r_input_area) { - r_input_area.xmin = 0; - r_input_area.xmax = this->getWidth(); - r_input_area.ymin = 0; - r_input_area.ymax = this->getHeight(); + r_input_area = this->get_canvas(); } void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, @@ -137,12 +129,12 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, Span inputs) { /* TODO(manzanilla): once tiled implementation is removed, run multi-threaded where possible. */ - if (!m_cachedInstance) { + if (!cached_instance_) { MemoryBuffer *image = inputs[IMAGE_INPUT_INDEX]; const bool is_image_inflated = image->is_a_single_elem(); image = is_image_inflated ? image->inflate() : image; - /* Must be a copy because it's modified in #generateVectorBlur. */ + /* Must be a copy because it's modified in #generate_vector_blur. */ MemoryBuffer *speed = inputs[SPEED_INPUT_INDEX]; speed = speed->is_a_single_elem() ? speed->inflate() : new MemoryBuffer(*speed); @@ -150,8 +142,8 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, const bool is_z_inflated = z->is_a_single_elem(); z = is_z_inflated ? z->inflate() : z; - m_cachedInstance = (float *)MEM_dupallocN(image->getBuffer()); - this->generateVectorBlur(m_cachedInstance, image, speed, z); + cached_instance_ = (float *)MEM_dupallocN(image->get_buffer()); + this->generate_vector_blur(cached_instance_, image, speed, z); if (is_image_inflated) { delete image; @@ -162,29 +154,29 @@ void VectorBlurOperation::update_memory_buffer(MemoryBuffer *output, } } - const int num_channels = COM_data_type_num_channels(getOutputSocket()->getDataType()); - MemoryBuffer buf(m_cachedInstance, num_channels, this->getWidth(), this->getHeight()); + const int num_channels = COM_data_type_num_channels(get_output_socket()->get_data_type()); + MemoryBuffer buf(cached_instance_, num_channels, this->get_width(), this->get_height()); output->copy_from(&buf, area); } -void VectorBlurOperation::generateVectorBlur(float *data, - MemoryBuffer *inputImage, - MemoryBuffer *inputSpeed, - MemoryBuffer *inputZ) +void VectorBlurOperation::generate_vector_blur(float *data, + MemoryBuffer *input_image, + MemoryBuffer *input_speed, + MemoryBuffer *inputZ) { NodeBlurData blurdata; - blurdata.samples = this->m_settings->samples / QualityStepHelper::getStep(); - blurdata.maxspeed = this->m_settings->maxspeed; - blurdata.minspeed = this->m_settings->minspeed; - blurdata.curved = this->m_settings->curved; - blurdata.fac = this->m_settings->fac; + blurdata.samples = settings_->samples / QualityStepHelper::get_step(); + blurdata.maxspeed = settings_->maxspeed; + blurdata.minspeed = settings_->minspeed; + blurdata.curved = settings_->curved; + blurdata.fac = settings_->fac; zbuf_accumulate_vecblur(&blurdata, - this->getWidth(), - this->getHeight(), + this->get_width(), + this->get_height(), data, - inputImage->getBuffer(), - inputSpeed->getBuffer(), - inputZ->getBuffer()); + input_image->get_buffer(), + input_speed->get_buffer(), + inputZ->get_buffer()); } /* ****************** Spans ******************************* */ diff --git a/source/blender/compositor/operations/COM_VectorBlurOperation.h b/source/blender/compositor/operations/COM_VectorBlurOperation.h index c30c150db3c..efcb5001fd4 100644 --- a/source/blender/compositor/operations/COM_VectorBlurOperation.h +++ b/source/blender/compositor/operations/COM_VectorBlurOperation.h @@ -31,18 +31,18 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { static constexpr int SPEED_INPUT_INDEX = 2; /** - * \brief Cached reference to the inputProgram + * \brief Cached reference to the input_program */ - SocketReader *m_inputImageProgram; - SocketReader *m_inputSpeedProgram; - SocketReader *m_inputZProgram; + SocketReader *input_image_program_; + SocketReader *input_speed_program_; + SocketReader *input_zprogram_; /** * \brief settings of the glare node. */ - NodeBlurData *m_settings; + NodeBlurData *settings_; - float *m_cachedInstance; + float *cached_instance_; public: VectorBlurOperation(); @@ -50,27 +50,27 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { /** * The inner loop of this operation. */ - void executePixel(float output[4], int x, int y, void *data) override; + void execute_pixel(float output[4], int x, int y, void *data) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; - void *initializeTileData(rcti *rect) override; + void *initialize_tile_data(rcti *rect) override; - void setVectorBlurSettings(NodeBlurData *settings) + void set_vector_blur_settings(NodeBlurData *settings) { - this->m_settings = settings; + settings_ = settings; } - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; void get_area_of_interest(const int input_idx, const rcti &output_area, @@ -80,10 +80,10 @@ class VectorBlurOperation : public NodeOperation, public QualityStepHelper { Span inputs) override; protected: - void generateVectorBlur(float *data, - MemoryBuffer *inputImage, - MemoryBuffer *inputSpeed, - MemoryBuffer *inputZ); + void generate_vector_blur(float *data, + MemoryBuffer *input_image, + MemoryBuffer *input_speed, + MemoryBuffer *inputZ); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.cc b/source/blender/compositor/operations/COM_VectorCurveOperation.cc index c2087fd071e..c7cd8dc5684 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.cc +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.cc @@ -24,40 +24,40 @@ namespace blender::compositor { VectorCurveOperation::VectorCurveOperation() { - this->addInputSocket(DataType::Vector); - this->addOutputSocket(DataType::Vector); + this->add_input_socket(DataType::Vector); + this->add_output_socket(DataType::Vector); - this->m_inputProgram = nullptr; + input_program_ = nullptr; } -void VectorCurveOperation::initExecution() +void VectorCurveOperation::init_execution() { - CurveBaseOperation::initExecution(); - this->m_inputProgram = this->getInputSocketReader(0); + CurveBaseOperation::init_execution(); + input_program_ = this->get_input_socket_reader(0); } -void VectorCurveOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void VectorCurveOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float input[4]; - this->m_inputProgram->readSampled(input, x, y, sampler); + input_program_->read_sampled(input, x, y, sampler); - BKE_curvemapping_evaluate_premulRGBF(this->m_curveMapping, output, input); + BKE_curvemapping_evaluate_premulRGBF(curve_mapping_, output, input); } -void VectorCurveOperation::deinitExecution() +void VectorCurveOperation::deinit_execution() { - CurveBaseOperation::deinitExecution(); - this->m_inputProgram = nullptr; + CurveBaseOperation::deinit_execution(); + input_program_ = nullptr; } void VectorCurveOperation::update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) { - CurveMapping *curve_map = this->m_curveMapping; + CurveMapping *curve_map = curve_mapping_; for (BuffersIterator it = output->iterate_with(inputs, area); !it.is_end(); ++it) { BKE_curvemapping_evaluate_premulRGBF(curve_map, it.out, it.in(0)); } diff --git a/source/blender/compositor/operations/COM_VectorCurveOperation.h b/source/blender/compositor/operations/COM_VectorCurveOperation.h index 27b3ad69e17..87864e08e25 100644 --- a/source/blender/compositor/operations/COM_VectorCurveOperation.h +++ b/source/blender/compositor/operations/COM_VectorCurveOperation.h @@ -26,9 +26,9 @@ namespace blender::compositor { class VectorCurveOperation : public CurveBaseOperation { private: /** - * Cached reference to the inputProgram + * Cached reference to the input_program */ - SocketReader *m_inputProgram; + SocketReader *input_program_; public: VectorCurveOperation(); @@ -36,17 +36,17 @@ class VectorCurveOperation : public CurveBaseOperation { /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; /** * Initialize the execution */ - void initExecution() override; + void init_execution() override; /** * Deinitialize the execution */ - void deinitExecution() override; + void deinit_execution() override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cc b/source/blender/compositor/operations/COM_ViewerOperation.cc index a038e8994d8..205596b46d1 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cc +++ b/source/blender/compositor/operations/COM_ViewerOperation.cc @@ -19,14 +19,7 @@ #include "COM_ViewerOperation.h" #include "BKE_image.h" #include "BKE_scene.h" -#include "BLI_listbase.h" -#include "BLI_math_color.h" -#include "BLI_math_vector.h" -#include "BLI_utildefines.h" -#include "MEM_guardedalloc.h" -#include "PIL_time.h" -#include "WM_api.h" -#include "WM_types.h" +#include "COM_ExecutionSystem.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" @@ -34,56 +27,58 @@ namespace blender::compositor { +static int MAX_VIEWER_TRANSLATION_PADDING = 12000; + ViewerOperation::ViewerOperation() { - this->setImage(nullptr); - this->setImageUser(nullptr); - this->m_outputBuffer = nullptr; - this->m_depthBuffer = nullptr; - this->m_active = false; - this->m_doDepthBuffer = false; - this->m_viewSettings = nullptr; - this->m_displaySettings = nullptr; - this->m_useAlphaInput = false; + this->set_image(nullptr); + this->set_image_user(nullptr); + output_buffer_ = nullptr; + depth_buffer_ = nullptr; + active_ = false; + do_depth_buffer_ = false; + view_settings_ = nullptr; + display_settings_ = nullptr; + use_alpha_input_ = false; - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Value); - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; - this->m_rd = nullptr; - this->m_viewName = nullptr; - flags.use_viewer_border = true; - flags.is_viewer_operation = true; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; + rd_ = nullptr; + view_name_ = nullptr; + flags_.use_viewer_border = true; + flags_.is_viewer_operation = true; } -void ViewerOperation::initExecution() +void ViewerOperation::init_execution() { /* When initializing the tree during initial load the width and height can be zero. */ - this->m_imageInput = getInputSocketReader(0); - this->m_alphaInput = getInputSocketReader(1); - this->m_depthInput = getInputSocketReader(2); - this->m_doDepthBuffer = (this->m_depthInput != nullptr); + image_input_ = get_input_socket_reader(0); + alpha_input_ = get_input_socket_reader(1); + depth_input_ = get_input_socket_reader(2); + do_depth_buffer_ = (depth_input_ != nullptr); - if (isActiveViewerOutput()) { - initImage(); + if (is_active_viewer_output() && !exec_system_->is_breaked()) { + init_image(); } } -void ViewerOperation::deinitExecution() +void ViewerOperation::deinit_execution() { - this->m_imageInput = nullptr; - this->m_alphaInput = nullptr; - this->m_depthInput = nullptr; - this->m_outputBuffer = nullptr; + image_input_ = nullptr; + alpha_input_ = nullptr; + depth_input_ = nullptr; + output_buffer_ = nullptr; } -void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void ViewerOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - float *buffer = this->m_outputBuffer; - float *depthbuffer = this->m_depthBuffer; + float *buffer = output_buffer_; + float *depthbuffer = depth_buffer_; if (!buffer) { return; } @@ -91,9 +86,9 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) const int y1 = rect->ymin; const int x2 = rect->xmax; const int y2 = rect->ymax; - const int offsetadd = (this->getWidth() - (x2 - x1)); + const int offsetadd = (this->get_width() - (x2 - x1)); const int offsetadd4 = offsetadd * 4; - int offset = (y1 * this->getWidth() + x1); + int offset = (y1 * this->get_width() + x1); int offset4 = offset * 4; float alpha[4], depth[4]; int x; @@ -102,117 +97,131 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) for (y = y1; y < y2 && (!breaked); y++) { for (x = x1; x < x2; x++) { - this->m_imageInput->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); - if (this->m_useAlphaInput) { - this->m_alphaInput->readSampled(alpha, x, y, PixelSampler::Nearest); + image_input_->read_sampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + if (use_alpha_input_) { + alpha_input_->read_sampled(alpha, x, y, PixelSampler::Nearest); buffer[offset4 + 3] = alpha[0]; } - this->m_depthInput->readSampled(depth, x, y, PixelSampler::Nearest); + depth_input_->read_sampled(depth, x, y, PixelSampler::Nearest); depthbuffer[offset] = depth[0]; offset++; offset4 += 4; } - if (isBraked()) { + if (is_braked()) { breaked = true; } offset += offsetadd; offset4 += offsetadd4; } - updateImage(rect); + update_image(rect); } -void ViewerOperation::determineResolution(unsigned int resolution[2], - unsigned int /*preferredResolution*/[2]) +void ViewerOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - const int sceneRenderWidth = this->m_rd->xsch * this->m_rd->size / 100; - const int sceneRenderHeight = this->m_rd->ysch * this->m_rd->size / 100; + const int scene_render_width = rd_->xsch * rd_->size / 100; + const int scene_render_height = rd_->ysch * rd_->size / 100; - unsigned int localPrefRes[2] = {static_cast(sceneRenderWidth), - static_cast(sceneRenderHeight)}; - NodeOperation::determineResolution(resolution, localPrefRes); + rcti local_preferred = preferred_area; + local_preferred.xmax = local_preferred.xmin + scene_render_width; + local_preferred.ymax = local_preferred.ymin + scene_render_height; + + NodeOperation::determine_canvas(local_preferred, r_area); } -void ViewerOperation::initImage() +void ViewerOperation::init_image() { - Image *ima = this->m_image; - ImageUser iuser = *this->m_imageUser; + Image *ima = image_; + ImageUser iuser = *image_user_; void *lock; ImBuf *ibuf; /* make sure the image has the correct number of views */ - if (ima && BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { - BKE_image_ensure_viewer_views(this->m_rd, ima, this->m_imageUser); + if (ima && BKE_scene_multiview_is_render_view_first(rd_, view_name_)) { + BKE_image_ensure_viewer_views(rd_, ima, image_user_); } BLI_thread_lock(LOCK_DRAW_IMAGE); /* local changes to the original ImageUser */ - iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + iuser.multi_index = BKE_scene_multiview_view_id_get(rd_, view_name_); ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock); if (!ibuf) { BLI_thread_unlock(LOCK_DRAW_IMAGE); return; } - if (ibuf->x != (int)getWidth() || ibuf->y != (int)getHeight()) { + int padding_x = abs(canvas_.xmin) * 2; + int padding_y = abs(canvas_.ymin) * 2; + if (padding_x > MAX_VIEWER_TRANSLATION_PADDING) { + padding_x = MAX_VIEWER_TRANSLATION_PADDING; + } + if (padding_y > MAX_VIEWER_TRANSLATION_PADDING) { + padding_y = MAX_VIEWER_TRANSLATION_PADDING; + } + + display_width_ = get_width() + padding_x; + display_height_ = get_height() + padding_y; + if (ibuf->x != display_width_ || ibuf->y != display_height_) { imb_freerectImBuf(ibuf); imb_freerectfloatImBuf(ibuf); IMB_freezbuffloatImBuf(ibuf); - ibuf->x = getWidth(); - ibuf->y = getHeight(); + ibuf->x = display_width_; + ibuf->y = display_height_; /* zero size can happen if no image buffers exist to define a sensible resolution */ if (ibuf->x > 0 && ibuf->y > 0) { imb_addrectfloatImBuf(ibuf); } - ImageTile *tile = BKE_image_get_tile(ima, 0); - tile->ok = IMA_OK_LOADED; ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; } - if (m_doDepthBuffer) { + if (do_depth_buffer_) { addzbuffloatImBuf(ibuf); } /* now we combine the input with ibuf */ - this->m_outputBuffer = ibuf->rect_float; + output_buffer_ = ibuf->rect_float; /* needed for display buffer update */ - this->m_ibuf = ibuf; + ibuf_ = ibuf; - if (m_doDepthBuffer) { - this->m_depthBuffer = ibuf->zbuf_float; + if (do_depth_buffer_) { + depth_buffer_ = ibuf->zbuf_float; } - BKE_image_release_ibuf(this->m_image, this->m_ibuf, lock); + BKE_image_release_ibuf(image_, ibuf_, lock); BLI_thread_unlock(LOCK_DRAW_IMAGE); } -void ViewerOperation::updateImage(const rcti *rect) +void ViewerOperation::update_image(const rcti *rect) { - float *buffer = m_outputBuffer; - IMB_partial_display_buffer_update(this->m_ibuf, + if (exec_system_->is_breaked()) { + return; + } + + float *buffer = output_buffer_; + IMB_partial_display_buffer_update(ibuf_, buffer, nullptr, - getWidth(), + display_width_, 0, 0, - this->m_viewSettings, - this->m_displaySettings, + view_settings_, + display_settings_, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - this->m_image->gpuflag |= IMA_GPU_REFRESH; - this->updateDraw(); + image_->gpuflag |= IMA_GPU_REFRESH; + this->update_draw(); } -eCompositorPriority ViewerOperation::getRenderPriority() const +eCompositorPriority ViewerOperation::get_render_priority() const { - if (this->isActiveViewerOutput()) { + if (this->is_active_viewer_output()) { return eCompositorPriority::High; } @@ -223,39 +232,56 @@ void ViewerOperation::update_memory_buffer_partial(MemoryBuffer *UNUSED(output), const rcti &area, Span inputs) { - if (!m_outputBuffer) { + if (!output_buffer_) { return; } + const int offset_x = area.xmin + (canvas_.xmin > 0 ? canvas_.xmin * 2 : 0); + const int offset_y = area.ymin + (canvas_.ymin > 0 ? canvas_.ymin * 2 : 0); MemoryBuffer output_buffer( - m_outputBuffer, COM_DATA_TYPE_COLOR_CHANNELS, getWidth(), getHeight()); + output_buffer_, COM_DATA_TYPE_COLOR_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_image = inputs[0]; - output_buffer.copy_from(input_image, area); - if (this->m_useAlphaInput) { + output_buffer.copy_from(input_image, area, offset_x, offset_y); + if (use_alpha_input_) { const MemoryBuffer *input_alpha = inputs[1]; - output_buffer.copy_from(input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, 3); + output_buffer.copy_from( + input_alpha, area, 0, COM_DATA_TYPE_VALUE_CHANNELS, offset_x, offset_y, 3); } - if (m_depthBuffer) { + if (depth_buffer_) { MemoryBuffer depth_buffer( - m_depthBuffer, COM_DATA_TYPE_VALUE_CHANNELS, getWidth(), getHeight()); + depth_buffer_, COM_DATA_TYPE_VALUE_CHANNELS, display_width_, display_height_); const MemoryBuffer *input_depth = inputs[2]; - depth_buffer.copy_from(input_depth, area); + depth_buffer.copy_from(input_depth, area, offset_x, offset_y); } - updateImage(&area); + rcti display_area; + BLI_rcti_init(&display_area, + offset_x, + offset_x + BLI_rcti_size_x(&area), + offset_y, + offset_y + BLI_rcti_size_y(&area)); + update_image(&display_area); } void ViewerOperation::clear_display_buffer() { - BLI_assert(isActiveViewerOutput()); - initImage(); - size_t buf_bytes = (size_t)m_ibuf->y * m_ibuf->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float); + BLI_assert(is_active_viewer_output()); + if (exec_system_->is_breaked()) { + return; + } + + init_image(); + if (output_buffer_ == nullptr) { + return; + } + + size_t buf_bytes = (size_t)ibuf_->y * ibuf_->x * COM_DATA_TYPE_COLOR_CHANNELS * sizeof(float); if (buf_bytes > 0) { - memset(m_outputBuffer, 0, buf_bytes); + memset(output_buffer_, 0, buf_bytes); rcti display_area; - BLI_rcti_init(&display_area, 0, m_ibuf->x, 0, m_ibuf->y); - updateImage(&display_area); + BLI_rcti_init(&display_area, 0, ibuf_->x, 0, ibuf_->y); + update_image(&display_area); } } diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index 06ac501a535..7fc5ae36ad9 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -28,103 +28,105 @@ namespace blender::compositor { class ViewerOperation : public MultiThreadedOperation { private: /* TODO(manzanilla): To be removed together with tiled implementation. */ - float *m_outputBuffer; - float *m_depthBuffer; + float *output_buffer_; + float *depth_buffer_; - Image *m_image; - ImageUser *m_imageUser; - bool m_active; - float m_centerX; - float m_centerY; - ChunkOrdering m_chunkOrder; - bool m_doDepthBuffer; - ImBuf *m_ibuf; - bool m_useAlphaInput; - const RenderData *m_rd; - const char *m_viewName; + Image *image_; + ImageUser *image_user_; + bool active_; + float center_x_; + float center_y_; + ChunkOrdering chunk_order_; + bool do_depth_buffer_; + ImBuf *ibuf_; + bool use_alpha_input_; + const RenderData *rd_; + const char *view_name_; - const ColorManagedViewSettings *m_viewSettings; - const ColorManagedDisplaySettings *m_displaySettings; + const ColorManagedViewSettings *view_settings_; + const ColorManagedDisplaySettings *display_settings_; - SocketReader *m_imageInput; - SocketReader *m_alphaInput; - SocketReader *m_depthInput; + SocketReader *image_input_; + SocketReader *alpha_input_; + SocketReader *depth_input_; + + int display_width_; + int display_height_; public: ViewerOperation(); - void initExecution() override; - void deinitExecution() override; - void executeRegion(rcti *rect, unsigned int tileNumber) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; - bool isOutputOperation(bool /*rendering*/) const override + void init_execution() override; + void deinit_execution() override; + void execute_region(rcti *rect, unsigned int tile_number) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + bool is_output_operation(bool /*rendering*/) const override { if (G.background) { return false; } - return isActiveViewerOutput(); + return is_active_viewer_output(); } - void setImage(Image *image) + void set_image(Image *image) { - this->m_image = image; + image_ = image; } - void setImageUser(ImageUser *imageUser) + void set_image_user(ImageUser *image_user) { - this->m_imageUser = imageUser; + image_user_ = image_user; } - bool isActiveViewerOutput() const override + bool is_active_viewer_output() const override { - return this->m_active; + return active_; } - void setActive(bool active) + void set_active(bool active) { - this->m_active = active; + active_ = active; } void setCenterX(float centerX) { - this->m_centerX = centerX; + center_x_ = centerX; } void setCenterY(float centerY) { - this->m_centerY = centerY; + center_y_ = centerY; } - void setChunkOrder(ChunkOrdering tileOrder) + void set_chunk_order(ChunkOrdering tile_order) { - this->m_chunkOrder = tileOrder; + chunk_order_ = tile_order; } float getCenterX() const { - return this->m_centerX; + return center_x_; } float getCenterY() const { - return this->m_centerY; + return center_y_; } - ChunkOrdering getChunkOrder() const + ChunkOrdering get_chunk_order() const { - return this->m_chunkOrder; + return chunk_order_; } - eCompositorPriority getRenderPriority() const override; - void setUseAlphaInput(bool value) + eCompositorPriority get_render_priority() const override; + void set_use_alpha_input(bool value) { - this->m_useAlphaInput = value; + use_alpha_input_ = value; } - void setRenderData(const RenderData *rd) + void set_render_data(const RenderData *rd) { - this->m_rd = rd; + rd_ = rd; } - void setViewName(const char *viewName) + void set_view_name(const char *view_name) { - this->m_viewName = viewName; + view_name_ = view_name; } - void setViewSettings(const ColorManagedViewSettings *viewSettings) + void set_view_settings(const ColorManagedViewSettings *view_settings) { - this->m_viewSettings = viewSettings; + view_settings_ = view_settings; } - void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) + void set_display_settings(const ColorManagedDisplaySettings *display_settings) { - this->m_displaySettings = displaySettings; + display_settings_ = display_settings; } void update_memory_buffer_partial(MemoryBuffer *output, @@ -134,8 +136,8 @@ class ViewerOperation : public MultiThreadedOperation { void clear_display_buffer(); private: - void updateImage(const rcti *rect); - void initImage(); + void update_image(const rcti *rect); + void init_image(); }; } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WrapOperation.cc b/source/blender/compositor/operations/COM_WrapOperation.cc index 888602114cc..336e1b72520 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.cc +++ b/source/blender/compositor/operations/COM_WrapOperation.cc @@ -24,98 +24,99 @@ namespace blender::compositor { WrapOperation::WrapOperation(DataType datatype) : ReadBufferOperation(datatype) { - this->m_wrappingType = CMP_NODE_WRAP_NONE; + wrapping_type_ = CMP_NODE_WRAP_NONE; } -inline float WrapOperation::getWrappedOriginalXPos(float x) +inline float WrapOperation::get_wrapped_original_xpos(float x) { - if (this->getWidth() == 0) { + if (this->get_width() == 0) { return 0; } while (x < 0) { - x += this->m_width; + x += this->get_width(); } - return fmodf(x, this->getWidth()); + return fmodf(x, this->get_width()); } -inline float WrapOperation::getWrappedOriginalYPos(float y) +inline float WrapOperation::get_wrapped_original_ypos(float y) { - if (this->getHeight() == 0) { + if (this->get_height() == 0) { return 0; } while (y < 0) { - y += this->m_height; + y += this->get_height(); } - return fmodf(y, this->getHeight()); + return fmodf(y, this->get_height()); } -void WrapOperation::executePixelSampled(float output[4], float x, float y, PixelSampler sampler) +void WrapOperation::execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) { float nx, ny; nx = x; ny = y; MemoryBufferExtend extend_x = MemoryBufferExtend::Clip, extend_y = MemoryBufferExtend::Clip; - switch (m_wrappingType) { + switch (wrapping_type_) { case CMP_NODE_WRAP_NONE: - /* Intentionally empty, originalXPos and originalYPos have been set before. */ + /* Intentionally empty, original_xpos and original_ypos have been set before. */ break; case CMP_NODE_WRAP_X: /* Wrap only on the x-axis. */ - nx = this->getWrappedOriginalXPos(x); + nx = this->get_wrapped_original_xpos(x); extend_x = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_Y: /* Wrap only on the y-axis. */ - ny = this->getWrappedOriginalYPos(y); + ny = this->get_wrapped_original_ypos(y); extend_y = MemoryBufferExtend::Repeat; break; case CMP_NODE_WRAP_XY: /* Wrap on both. */ - nx = this->getWrappedOriginalXPos(x); - ny = this->getWrappedOriginalYPos(y); + nx = this->get_wrapped_original_xpos(x); + ny = this->get_wrapped_original_ypos(y); extend_x = MemoryBufferExtend::Repeat; extend_y = MemoryBufferExtend::Repeat; break; } - executePixelExtend(output, nx, ny, sampler, extend_x, extend_y); + execute_pixel_extend(output, nx, ny, sampler, extend_x, extend_y); } -bool WrapOperation::determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) +bool WrapOperation::determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) { - rcti newInput; - newInput.xmin = input->xmin; - newInput.xmax = input->xmax; - newInput.ymin = input->ymin; - newInput.ymax = input->ymax; + rcti new_input; + new_input.xmin = input->xmin; + new_input.xmax = input->xmax; + new_input.ymin = input->ymin; + new_input.ymax = input->ymax; - if (ELEM(m_wrappingType, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { + if (ELEM(wrapping_type_, CMP_NODE_WRAP_X, CMP_NODE_WRAP_XY)) { /* Wrap only on the x-axis if tile is wrapping. */ - newInput.xmin = getWrappedOriginalXPos(input->xmin); - newInput.xmax = roundf(getWrappedOriginalXPos(input->xmax)); - if (newInput.xmin >= newInput.xmax) { - newInput.xmin = 0; - newInput.xmax = this->getWidth(); + new_input.xmin = get_wrapped_original_xpos(input->xmin); + new_input.xmax = roundf(get_wrapped_original_xpos(input->xmax)); + if (new_input.xmin >= new_input.xmax) { + new_input.xmin = 0; + new_input.xmax = this->get_width(); } } - if (ELEM(m_wrappingType, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { + if (ELEM(wrapping_type_, CMP_NODE_WRAP_Y, CMP_NODE_WRAP_XY)) { /* Wrap only on the y-axis if tile is wrapping. */ - newInput.ymin = getWrappedOriginalYPos(input->ymin); - newInput.ymax = roundf(getWrappedOriginalYPos(input->ymax)); - if (newInput.ymin >= newInput.ymax) { - newInput.ymin = 0; - newInput.ymax = this->getHeight(); + new_input.ymin = get_wrapped_original_ypos(input->ymin); + new_input.ymax = roundf(get_wrapped_original_ypos(input->ymax)); + if (new_input.ymin >= new_input.ymax) { + new_input.ymin = 0; + new_input.ymax = this->get_height(); } } - return ReadBufferOperation::determineDependingAreaOfInterest(&newInput, readOperation, output); + return ReadBufferOperation::determine_depending_area_of_interest( + &new_input, read_operation, output); } -void WrapOperation::setWrapping(int wrapping_type) +void WrapOperation::set_wrapping(int wrapping_type) { - m_wrappingType = wrapping_type; + wrapping_type_ = wrapping_type; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WrapOperation.h b/source/blender/compositor/operations/COM_WrapOperation.h index 6279129a550..e8ec97fa5f2 100644 --- a/source/blender/compositor/operations/COM_WrapOperation.h +++ b/source/blender/compositor/operations/COM_WrapOperation.h @@ -24,18 +24,18 @@ namespace blender::compositor { class WrapOperation : public ReadBufferOperation { private: - int m_wrappingType; + int wrapping_type_; public: WrapOperation(DataType datatype); - bool determineDependingAreaOfInterest(rcti *input, - ReadBufferOperation *readOperation, - rcti *output) override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + bool determine_depending_area_of_interest(rcti *input, + ReadBufferOperation *read_operation, + rcti *output) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; - void setWrapping(int wrapping_type); - float getWrappedOriginalXPos(float x); - float getWrappedOriginalYPos(float y); + void set_wrapping(int wrapping_type); + float get_wrapped_original_xpos(float x); + float get_wrapped_original_ypos(float y); void setFactorXY(float factorX, float factorY); }; diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.cc b/source/blender/compositor/operations/COM_WriteBufferOperation.cc index 6380f6bf3df..2511488615e 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.cc +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.cc @@ -18,54 +18,52 @@ #include "COM_WriteBufferOperation.h" #include "COM_OpenCLDevice.h" -#include "COM_defines.h" -#include namespace blender::compositor { WriteBufferOperation::WriteBufferOperation(DataType datatype) { - this->addInputSocket(datatype); - this->m_memoryProxy = new MemoryProxy(datatype); - this->m_memoryProxy->setWriteBufferOperation(this); - this->m_memoryProxy->setExecutor(nullptr); - flags.is_write_buffer_operation = true; + this->add_input_socket(datatype); + memory_proxy_ = new MemoryProxy(datatype); + memory_proxy_->set_write_buffer_operation(this); + memory_proxy_->set_executor(nullptr); + flags_.is_write_buffer_operation = true; } WriteBufferOperation::~WriteBufferOperation() { - if (this->m_memoryProxy) { - delete this->m_memoryProxy; - this->m_memoryProxy = nullptr; + if (memory_proxy_) { + delete memory_proxy_; + memory_proxy_ = nullptr; } } -void WriteBufferOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void WriteBufferOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { - this->m_input->readSampled(output, x, y, sampler); + input_->read_sampled(output, x, y, sampler); } -void WriteBufferOperation::initExecution() +void WriteBufferOperation::init_execution() { - this->m_input = this->getInputOperation(0); - this->m_memoryProxy->allocate(this->m_width, this->m_height); + input_ = this->get_input_operation(0); + memory_proxy_->allocate(this->get_width(), this->get_height()); } -void WriteBufferOperation::deinitExecution() +void WriteBufferOperation::deinit_execution() { - this->m_input = nullptr; - this->m_memoryProxy->free(); + input_ = nullptr; + memory_proxy_->free(); } -void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) +void WriteBufferOperation::execute_region(rcti *rect, unsigned int /*tile_number*/) { - MemoryBuffer *memoryBuffer = this->m_memoryProxy->getBuffer(); - float *buffer = memoryBuffer->getBuffer(); - const uint8_t num_channels = memoryBuffer->get_num_channels(); - if (this->m_input->get_flags().complex) { - void *data = this->m_input->initializeTileData(rect); + MemoryBuffer *memory_buffer = memory_proxy_->get_buffer(); + float *buffer = memory_buffer->get_buffer(); + const uint8_t num_channels = memory_buffer->get_num_channels(); + if (input_->get_flags().complex) { + void *data = input_->initialize_tile_data(rect); int x1 = rect->xmin; int y1 = rect->ymin; int x2 = rect->xmax; @@ -74,17 +72,17 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ int y; bool breaked = false; for (y = y1; y < y2 && (!breaked); y++) { - int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; + int offset4 = (y * memory_buffer->get_width() + x1) * num_channels; for (x = x1; x < x2; x++) { - this->m_input->read(&(buffer[offset4]), x, y, data); + input_->read(&(buffer[offset4]), x, y, data); offset4 += num_channels; } - if (isBraked()) { + if (is_braked()) { breaked = true; } } if (data) { - this->m_input->deinitializeTileData(rect, data); + input_->deinitialize_tile_data(rect, data); data = nullptr; } } @@ -98,25 +96,25 @@ void WriteBufferOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/ int y; bool breaked = false; for (y = y1; y < y2 && (!breaked); y++) { - int offset4 = (y * memoryBuffer->getWidth() + x1) * num_channels; + int offset4 = (y * memory_buffer->get_width() + x1) * num_channels; for (x = x1; x < x2; x++) { - this->m_input->readSampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); + input_->read_sampled(&(buffer[offset4]), x, y, PixelSampler::Nearest); offset4 += num_channels; } - if (isBraked()) { + if (is_braked()) { breaked = true; } } } } -void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, - rcti * /*rect*/, - unsigned int /*chunkNumber*/, - MemoryBuffer **inputMemoryBuffers, - MemoryBuffer *outputBuffer) +void WriteBufferOperation::execute_opencl_region(OpenCLDevice *device, + rcti * /*rect*/, + unsigned int /*chunk_number*/, + MemoryBuffer **input_memory_buffers, + MemoryBuffer *output_buffer) { - float *outputFloatBuffer = outputBuffer->getBuffer(); + float *output_float_buffer = output_buffer->get_buffer(); cl_int error; /* * 1. create cl_mem from outputbuffer @@ -127,55 +125,55 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, * NOTE: list of cl_mem will be filled by 2, and needs to be cleaned up by 4 */ /* STEP 1 */ - const unsigned int outputBufferWidth = outputBuffer->getWidth(); - const unsigned int outputBufferHeight = outputBuffer->getHeight(); + const unsigned int output_buffer_width = output_buffer->get_width(); + const unsigned int output_buffer_height = output_buffer->get_height(); - const cl_image_format *imageFormat = OpenCLDevice::determineImageFormat(outputBuffer); + const cl_image_format *image_format = OpenCLDevice::determine_image_format(output_buffer); - cl_mem clOutputBuffer = clCreateImage2D(device->getContext(), - CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, - imageFormat, - outputBufferWidth, - outputBufferHeight, - 0, - outputFloatBuffer, - &error); + cl_mem cl_output_buffer = clCreateImage2D(device->get_context(), + CL_MEM_WRITE_ONLY | CL_MEM_USE_HOST_PTR, + image_format, + output_buffer_width, + output_buffer_height, + 0, + output_float_buffer, + &error); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } /* STEP 2 */ - std::list *clMemToCleanUp = new std::list(); - clMemToCleanUp->push_back(clOutputBuffer); - std::list *clKernelsToCleanUp = new std::list(); + std::list *cl_mem_to_clean_up = new std::list(); + cl_mem_to_clean_up->push_back(cl_output_buffer); + std::list *cl_kernels_to_clean_up = new std::list(); - this->m_input->executeOpenCL(device, - outputBuffer, - clOutputBuffer, - inputMemoryBuffers, - clMemToCleanUp, - clKernelsToCleanUp); + input_->execute_opencl(device, + output_buffer, + cl_output_buffer, + input_memory_buffers, + cl_mem_to_clean_up, + cl_kernels_to_clean_up); /* STEP 3 */ size_t origin[3] = {0, 0, 0}; - size_t region[3] = {outputBufferWidth, outputBufferHeight, 1}; + size_t region[3] = {output_buffer_width, output_buffer_height, 1}; // clFlush(queue); // clFinish(queue); - error = clEnqueueBarrier(device->getQueue()); + error = clEnqueueBarrier(device->get_queue()); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - error = clEnqueueReadImage(device->getQueue(), - clOutputBuffer, + error = clEnqueueReadImage(device->get_queue(), + cl_output_buffer, CL_TRUE, origin, region, 0, 0, - outputFloatBuffer, + output_float_buffer, 0, nullptr, nullptr); @@ -183,50 +181,49 @@ void WriteBufferOperation::executeOpenCLRegion(OpenCLDevice *device, printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - this->getMemoryProxy()->getBuffer()->fill_from(*outputBuffer); + this->get_memory_proxy()->get_buffer()->fill_from(*output_buffer); /* STEP 4 */ - while (!clMemToCleanUp->empty()) { - cl_mem mem = clMemToCleanUp->front(); + while (!cl_mem_to_clean_up->empty()) { + cl_mem mem = cl_mem_to_clean_up->front(); error = clReleaseMemObject(mem); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - clMemToCleanUp->pop_front(); + cl_mem_to_clean_up->pop_front(); } - while (!clKernelsToCleanUp->empty()) { - cl_kernel kernel = clKernelsToCleanUp->front(); + while (!cl_kernels_to_clean_up->empty()) { + cl_kernel kernel = cl_kernels_to_clean_up->front(); error = clReleaseKernel(kernel); if (error != CL_SUCCESS) { printf("CLERROR[%d]: %s\n", error, clewErrorString(error)); } - clKernelsToCleanUp->pop_front(); + cl_kernels_to_clean_up->pop_front(); } - delete clKernelsToCleanUp; + delete cl_kernels_to_clean_up; } -void WriteBufferOperation::determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) +void WriteBufferOperation::determine_canvas(const rcti &preferred_area, rcti &r_area) { - NodeOperation::determineResolution(resolution, preferredResolution); + NodeOperation::determine_canvas(preferred_area, r_area); /* make sure there is at least one pixel stored in case the input is a single value */ - m_single_value = false; - if (resolution[0] == 0) { - resolution[0] = 1; - m_single_value = true; + single_value_ = false; + if (BLI_rcti_size_x(&r_area) == 0) { + r_area.xmax += 1; + single_value_ = true; } - if (resolution[1] == 0) { - resolution[1] = 1; - m_single_value = true; + if (BLI_rcti_size_y(&r_area) == 0) { + r_area.ymax += 1; + single_value_ = true; } } -void WriteBufferOperation::readResolutionFromInputSocket() +void WriteBufferOperation::read_resolution_from_input_socket() { - NodeOperation *inputOperation = this->getInputOperation(0); - this->setWidth(inputOperation->getWidth()); - this->setHeight(inputOperation->getHeight()); + NodeOperation *input_operation = this->get_input_operation(0); + this->set_width(input_operation->get_width()); + this->set_height(input_operation->get_height()); } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_WriteBufferOperation.h b/source/blender/compositor/operations/COM_WriteBufferOperation.h index 2817fbe24b9..cf6b7f655ce 100644 --- a/source/blender/compositor/operations/COM_WriteBufferOperation.h +++ b/source/blender/compositor/operations/COM_WriteBufferOperation.h @@ -31,37 +31,36 @@ class MemoryProxy; * \ingroup Operation */ class WriteBufferOperation : public NodeOperation { - MemoryProxy *m_memoryProxy; - bool m_single_value; /* single value stored in buffer */ - NodeOperation *m_input; + MemoryProxy *memory_proxy_; + bool single_value_; /* single value stored in buffer */ + NodeOperation *input_; public: WriteBufferOperation(DataType datatype); ~WriteBufferOperation(); - MemoryProxy *getMemoryProxy() + MemoryProxy *get_memory_proxy() { - return this->m_memoryProxy; + return memory_proxy_; } - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; - bool isSingleValue() const + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; + bool is_single_value() const { - return m_single_value; + return single_value_; } - void executeRegion(rcti *rect, unsigned int tileNumber) override; - void initExecution() override; - void deinitExecution() override; - void executeOpenCLRegion(OpenCLDevice *device, - rcti *rect, - unsigned int chunkNumber, - MemoryBuffer **memoryBuffers, - MemoryBuffer *outputBuffer) override; - void determineResolution(unsigned int resolution[2], - unsigned int preferredResolution[2]) override; - void readResolutionFromInputSocket(); - inline NodeOperation *getInput() + void execute_region(rcti *rect, unsigned int tile_number) override; + void init_execution() override; + void deinit_execution() override; + void execute_opencl_region(OpenCLDevice *device, + rcti *rect, + unsigned int chunk_number, + MemoryBuffer **memory_buffers, + MemoryBuffer *output_buffer) override; + void determine_canvas(const rcti &preferred_area, rcti &r_area) override; + void read_resolution_from_input_socket(); + inline NodeOperation *get_input() { - return m_input; + return input_; } }; diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.cc b/source/blender/compositor/operations/COM_ZCombineOperation.cc index 7050c3b2d83..c9b3e70a9cd 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.cc +++ b/source/blender/compositor/operations/COM_ZCombineOperation.cc @@ -17,48 +17,47 @@ */ #include "COM_ZCombineOperation.h" -#include "BLI_utildefines.h" namespace blender::compositor { ZCombineOperation::ZCombineOperation() { - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Value); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Value); + this->add_output_socket(DataType::Color); - this->m_image1Reader = nullptr; - this->m_depth1Reader = nullptr; - this->m_image2Reader = nullptr; - this->m_depth2Reader = nullptr; - this->flags.can_be_constant = true; + image1Reader_ = nullptr; + depth1Reader_ = nullptr; + image2Reader_ = nullptr; + depth2Reader_ = nullptr; + flags_.can_be_constant = true; } -void ZCombineOperation::initExecution() +void ZCombineOperation::init_execution() { - this->m_image1Reader = this->getInputSocketReader(0); - this->m_depth1Reader = this->getInputSocketReader(1); - this->m_image2Reader = this->getInputSocketReader(2); - this->m_depth2Reader = this->getInputSocketReader(3); + image1Reader_ = this->get_input_socket_reader(0); + depth1Reader_ = this->get_input_socket_reader(1); + image2Reader_ = this->get_input_socket_reader(2); + depth2Reader_ = this->get_input_socket_reader(3); } -void ZCombineOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float depth1[4]; float depth2[4]; - this->m_depth1Reader->readSampled(depth1, x, y, sampler); - this->m_depth2Reader->readSampled(depth2, x, y, sampler); + depth1Reader_->read_sampled(depth1, x, y, sampler); + depth2Reader_->read_sampled(depth2, x, y, sampler); if (depth1[0] < depth2[0]) { - this->m_image1Reader->readSampled(output, x, y, sampler); + image1Reader_->read_sampled(output, x, y, sampler); } else { - this->m_image2Reader->readSampled(output, x, y, sampler); + image2Reader_->read_sampled(output, x, y, sampler); } } @@ -74,25 +73,25 @@ void ZCombineOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void ZCombineAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float depth1[4]; float depth2[4]; float color1[4]; float color2[4]; - this->m_depth1Reader->readSampled(depth1, x, y, sampler); - this->m_depth2Reader->readSampled(depth2, x, y, sampler); + depth1Reader_->read_sampled(depth1, x, y, sampler); + depth2Reader_->read_sampled(depth2, x, y, sampler); if (depth1[0] <= depth2[0]) { - this->m_image1Reader->readSampled(color1, x, y, sampler); - this->m_image2Reader->readSampled(color2, x, y, sampler); + image1Reader_->read_sampled(color1, x, y, sampler); + image2Reader_->read_sampled(color2, x, y, sampler); } else { - this->m_image1Reader->readSampled(color2, x, y, sampler); - this->m_image2Reader->readSampled(color1, x, y, sampler); + image1Reader_->read_sampled(color2, x, y, sampler); + image2Reader_->read_sampled(color1, x, y, sampler); } float fac = color1[3]; float ifac = 1.0f - fac; @@ -128,46 +127,46 @@ void ZCombineAlphaOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void ZCombineOperation::deinitExecution() +void ZCombineOperation::deinit_execution() { - this->m_image1Reader = nullptr; - this->m_depth1Reader = nullptr; - this->m_image2Reader = nullptr; - this->m_depth2Reader = nullptr; + image1Reader_ = nullptr; + depth1Reader_ = nullptr; + image2Reader_ = nullptr; + depth2Reader_ = nullptr; } // MASK combine ZCombineMaskOperation::ZCombineMaskOperation() { - this->addInputSocket(DataType::Value); // mask - this->addInputSocket(DataType::Color); - this->addInputSocket(DataType::Color); - this->addOutputSocket(DataType::Color); + this->add_input_socket(DataType::Value); // mask + this->add_input_socket(DataType::Color); + this->add_input_socket(DataType::Color); + this->add_output_socket(DataType::Color); - this->m_maskReader = nullptr; - this->m_image1Reader = nullptr; - this->m_image2Reader = nullptr; + mask_reader_ = nullptr; + image1Reader_ = nullptr; + image2Reader_ = nullptr; } -void ZCombineMaskOperation::initExecution() +void ZCombineMaskOperation::init_execution() { - this->m_maskReader = this->getInputSocketReader(0); - this->m_image1Reader = this->getInputSocketReader(1); - this->m_image2Reader = this->getInputSocketReader(2); + mask_reader_ = this->get_input_socket_reader(0); + image1Reader_ = this->get_input_socket_reader(1); + image2Reader_ = this->get_input_socket_reader(2); } -void ZCombineMaskOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineMaskOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float mask[4]; float color1[4]; float color2[4]; - this->m_maskReader->readSampled(mask, x, y, sampler); - this->m_image1Reader->readSampled(color1, x, y, sampler); - this->m_image2Reader->readSampled(color2, x, y, sampler); + mask_reader_->read_sampled(mask, x, y, sampler); + image1Reader_->read_sampled(color1, x, y, sampler); + image2Reader_->read_sampled(color2, x, y, sampler); interp_v4_v4v4(output, color1, color2, 1.0f - mask[0]); } @@ -184,18 +183,18 @@ void ZCombineMaskOperation::update_memory_buffer_partial(MemoryBuffer *output, } } -void ZCombineMaskAlphaOperation::executePixelSampled(float output[4], - float x, - float y, - PixelSampler sampler) +void ZCombineMaskAlphaOperation::execute_pixel_sampled(float output[4], + float x, + float y, + PixelSampler sampler) { float mask[4]; float color1[4]; float color2[4]; - this->m_maskReader->readSampled(mask, x, y, sampler); - this->m_image1Reader->readSampled(color1, x, y, sampler); - this->m_image2Reader->readSampled(color2, x, y, sampler); + mask_reader_->read_sampled(mask, x, y, sampler); + image1Reader_->read_sampled(color1, x, y, sampler); + image2Reader_->read_sampled(color2, x, y, sampler); float fac = (1.0f - mask[0]) * (1.0f - color1[3]) + mask[0] * color2[3]; float mfac = 1.0f - fac; @@ -224,11 +223,11 @@ void ZCombineMaskAlphaOperation::update_memory_buffer_partial(MemoryBuffer *outp } } -void ZCombineMaskOperation::deinitExecution() +void ZCombineMaskOperation::deinit_execution() { - this->m_image1Reader = nullptr; - this->m_maskReader = nullptr; - this->m_image2Reader = nullptr; + image1Reader_ = nullptr; + mask_reader_ = nullptr; + image2Reader_ = nullptr; } } // namespace blender::compositor diff --git a/source/blender/compositor/operations/COM_ZCombineOperation.h b/source/blender/compositor/operations/COM_ZCombineOperation.h index acd60b6c866..ffc1bdd93b4 100644 --- a/source/blender/compositor/operations/COM_ZCombineOperation.h +++ b/source/blender/compositor/operations/COM_ZCombineOperation.h @@ -28,10 +28,10 @@ namespace blender::compositor { */ class ZCombineOperation : public MultiThreadedOperation { protected: - SocketReader *m_image1Reader; - SocketReader *m_depth1Reader; - SocketReader *m_image2Reader; - SocketReader *m_depth2Reader; + SocketReader *image1Reader_; + SocketReader *depth1Reader_; + SocketReader *image2Reader_; + SocketReader *depth2Reader_; public: /** @@ -39,13 +39,13 @@ class ZCombineOperation : public MultiThreadedOperation { */ ZCombineOperation(); - void initExecution() override; - void deinitExecution() override; + void init_execution() override; + void deinit_execution() override; /** * The inner loop of this operation. */ - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -53,7 +53,7 @@ class ZCombineOperation : public MultiThreadedOperation { }; class ZCombineAlphaOperation : public ZCombineOperation { - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, @@ -62,23 +62,23 @@ class ZCombineAlphaOperation : public ZCombineOperation { class ZCombineMaskOperation : public MultiThreadedOperation { protected: - SocketReader *m_maskReader; - SocketReader *m_image1Reader; - SocketReader *m_image2Reader; + SocketReader *mask_reader_; + SocketReader *image1Reader_; + SocketReader *image2Reader_; public: ZCombineMaskOperation(); - void initExecution() override; - void deinitExecution() override; - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void init_execution() override; + void deinit_execution() override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, Span inputs) override; }; class ZCombineMaskAlphaOperation : public ZCombineMaskOperation { - void executePixelSampled(float output[4], float x, float y, PixelSampler sampler) override; + void execute_pixel_sampled(float output[4], float x, float y, PixelSampler sampler) override; void update_memory_buffer_partial(MemoryBuffer *output, const rcti &area, diff --git a/source/blender/compositor/tests/COM_NodeOperation_test.cc b/source/blender/compositor/tests/COM_NodeOperation_test.cc index 94e9fdeedb1..ea61df22f88 100644 --- a/source/blender/compositor/tests/COM_NodeOperation_test.cc +++ b/source/blender/compositor/tests/COM_NodeOperation_test.cc @@ -27,9 +27,9 @@ class NonHashedOperation : public NodeOperation { NonHashedOperation(int id) { set_id(id); - addOutputSocket(DataType::Value); - setWidth(2); - setHeight(3); + add_output_socket(DataType::Value); + set_width(2); + set_height(3); } }; @@ -40,9 +40,9 @@ class NonHashedConstantOperation : public ConstantOperation { NonHashedConstantOperation(int id) { set_id(id); - addOutputSocket(DataType::Value); - setWidth(2); - setHeight(3); + add_output_socket(DataType::Value); + set_width(2); + set_height(3); constant_ = 1.0f; } @@ -65,14 +65,14 @@ class HashedOperation : public NodeOperation { public: HashedOperation(NodeOperation &input, int width, int height) { - addInputSocket(DataType::Value); - addOutputSocket(DataType::Color); - setWidth(width); - setHeight(height); + add_input_socket(DataType::Value); + add_output_socket(DataType::Color); + set_width(width); + set_height(height); param1 = 2; param2 = 7.0f; - getInputSocket(0)->setLink(input.getOutputSocket()); + get_input_socket(0)->set_link(input.get_output_socket()); } void set_param1(int value) diff --git a/source/blender/datatoc/datatoc_icon.c b/source/blender/datatoc/datatoc_icon.c index ba7120daa92..b4ee1c6d4c9 100644 --- a/source/blender/datatoc/datatoc_icon.c +++ b/source/blender/datatoc/datatoc_icon.c @@ -32,7 +32,7 @@ # include #endif -#include "png.h" +#include /* for Win32 DIR functions */ #ifdef WIN32 @@ -245,7 +245,7 @@ static struct IconInfo *icon_merge_context_info_for_icon_head(struct IconMergeCo static void icon_merge_context_register_icon(struct IconMergeContext *context, const char *file_name, - struct IconHead *icon_head) + const struct IconHead *icon_head) { context->read_icons = realloc(context->read_icons, sizeof(struct IconInfo) * (context->num_read_icons + 1)); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index 36c6b56caae..a09f79ffa39 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -63,6 +63,7 @@ #include "DNA_sound_types.h" #include "DNA_speaker_types.h" #include "DNA_texture_types.h" +#include "DNA_vfont_types.h" #include "DNA_world_types.h" #include "BKE_action.h" @@ -1466,7 +1467,7 @@ void DepsgraphNodeBuilder::build_shapekeys(Key *key) } /* ObData Geometry Evaluation */ -// XXX: what happens if the datablock is shared! +/* XXX: what happens if the datablock is shared! */ void DepsgraphNodeBuilder::build_object_data_geometry(Object *object, bool is_object_visible) { OperationNode *op_node; @@ -1764,6 +1765,9 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) else if (id_type == ID_MC) { build_movieclip((MovieClip *)id); } + else if (id_type == ID_VF) { + build_vfont((VFont *)id); + } else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { bNodeTree *group_ntree = (bNodeTree *)id; build_nodetree(group_ntree); @@ -1780,7 +1784,7 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree) build_idproperties(socket->prop); } - // TODO: link from nodetree to owner_component? + /* TODO: link from nodetree to owner_component? */ } /* Recursively build graph for material */ @@ -2015,6 +2019,17 @@ void DepsgraphNodeBuilder::build_simulation(Simulation *simulation) }); } +void DepsgraphNodeBuilder::build_vfont(VFont *vfont) +{ + if (built_map_.checkIsBuiltAndTag(vfont)) { + return; + } + build_parameters(&vfont->id); + build_idproperties(vfont->id.properties); + add_operation_node( + &vfont->id, NodeType::GENERIC_DATABLOCK, OperationCode::GENERIC_DATABLOCK_UPDATE); +} + static bool seq_node_build_cb(Sequence *seq, void *user_data) { DepsgraphNodeBuilder *nb = (DepsgraphNodeBuilder *)user_data; diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index 2378f3fc100..d31290ecbff 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -55,6 +55,7 @@ struct Scene; struct Simulation; struct Speaker; struct Tex; +struct VFont; struct World; struct bAction; struct bArmature; @@ -235,6 +236,7 @@ class DepsgraphNodeBuilder : public DepsgraphBuilder { virtual void build_scene_sequencer(Scene *scene); virtual void build_scene_audio(Scene *scene); virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer); + virtual void build_vfont(VFont *vfont); /* Per-ID information about what was already in the dependency graph. * Allows to re-use certain values, to speed up following evaluation. */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index 28cfc5a9e1a..55e8c5ed033 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -65,6 +65,7 @@ #include "DNA_sound_types.h" #include "DNA_speaker_types.h" #include "DNA_texture_types.h" +#include "DNA_vfont_types.h" #include "DNA_volume_types.h" #include "DNA_world_types.h" @@ -2496,6 +2497,11 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree) OperationKey clip_key(id, NodeType::PARAMETERS, OperationCode::MOVIECLIP_EVAL); add_relation(clip_key, shading_key, "Clip -> Node"); } + else if (id_type == ID_VF) { + build_vfont((VFont *)id); + ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK); + add_relation(vfont_key, shading_key, "VFont -> Node"); + } else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) { bNodeTree *group_ntree = (bNodeTree *)id; build_nodetree(group_ntree); @@ -2842,6 +2848,15 @@ void DepsgraphRelationBuilder::build_scene_speakers(Scene * /*scene*/, ViewLayer } } +void DepsgraphRelationBuilder::build_vfont(VFont *vfont) +{ + if (built_map_.checkIsBuiltAndTag(vfont)) { + return; + } + build_parameters(&vfont->id); + build_idproperties(vfont->id.properties); +} + void DepsgraphRelationBuilder::build_copy_on_write_relations() { for (IDNode *id_node : graph_->id_nodes) { diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.h b/source/blender/depsgraph/intern/builder/deg_builder_relations.h index 1ad61c25305..f0393544511 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.h @@ -72,6 +72,7 @@ struct Simulation; struct Speaker; struct Tex; struct ViewLayer; +struct VFont; struct World; struct bAction; struct bArmature; @@ -296,6 +297,7 @@ class DepsgraphRelationBuilder : public DepsgraphBuilder { virtual void build_scene_sequencer(Scene *scene); virtual void build_scene_audio(Scene *scene); virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer); + virtual void build_vfont(VFont *vfont); virtual void build_nested_datablock(ID *owner, ID *id); virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree); diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc index cc7ce871419..0314219b725 100644 --- a/source/blender/depsgraph/intern/depsgraph_eval.cc +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -44,6 +44,7 @@ #include "intern/node/deg_node_time.h" #include "intern/depsgraph.h" +#include "intern/depsgraph_tag.h" namespace deg = blender::deg; @@ -54,6 +55,7 @@ static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph) BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->frame); } + deg::graph_tag_ids_for_visible_update(deg_graph); deg::deg_graph_flush_updates(deg_graph); deg::deg_evaluate_on_refresh(deg_graph); } diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc index c816c7b8db5..d6877adb66b 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval.cc @@ -370,8 +370,6 @@ static TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state) */ void deg_evaluate_on_refresh(Depsgraph *graph) { - graph_tag_ids_for_visible_update(graph); - /* Nothing to update, early out. */ if (graph->entry_tags.is_empty()) { return; diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc index a844d23b558..68a72638c7d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.cc @@ -511,12 +511,6 @@ inline bool check_datablock_expanded(const ID *id_cow) struct RemapCallbackUserData { /* Dependency graph for which remapping is happening. */ const Depsgraph *depsgraph; - /* Create placeholder for ID nodes for cases when we need to remap original - * ID to it[s CoW version but we don't have required ID node yet. - * - * This happens when expansion happens a ta construction time. */ - DepsgraphNodeBuilder *node_builder; - bool create_placeholders; }; int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) @@ -526,38 +520,11 @@ int foreach_libblock_remap_callback(LibraryIDLinkCallbackData *cb_data) return IDWALK_RET_NOP; } - ID *id_self = cb_data->id_self; RemapCallbackUserData *user_data = (RemapCallbackUserData *)cb_data->user_data; const Depsgraph *depsgraph = user_data->depsgraph; ID *id_orig = *id_p; if (deg_copy_on_write_is_needed(id_orig)) { - ID *id_cow; - if (user_data->create_placeholders) { - /* Special workaround to stop creating temp datablocks for - * objects which are coming from scene's collection and which - * are never linked to any of layers. - * - * TODO(sergey): Ideally we need to tell ID looper to ignore - * those or at least make it more reliable check where the - * pointer is coming from. */ - const ID_Type id_type = GS(id_orig->name); - const ID_Type id_type_self = GS(id_self->name); - if (id_type == ID_OB && id_type_self == ID_SCE) { - IDNode *id_node = depsgraph->find_id_node(id_orig); - if (id_node == nullptr) { - id_cow = id_orig; - } - else { - id_cow = id_node->id_cow; - } - } - else { - id_cow = user_data->node_builder->ensure_cow_id(id_orig); - } - } - else { - id_cow = depsgraph->get_cow_id(id_orig); - } + ID *id_cow = depsgraph->get_cow_id(id_orig); BLI_assert(id_cow != nullptr); DEG_COW_PRINT( " Remapping datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); @@ -834,34 +801,29 @@ int foreach_libblock_validate_callback(LibraryIDLinkCallbackData *cb_data) return IDWALK_RET_NOP; } -} // namespace - /* Actual implementation of logic which "expands" all the data which was not * yet copied-on-write. * * NOTE: Expects that CoW datablock is empty. */ -ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, - const IDNode *id_node, - DepsgraphNodeBuilder *node_builder, - bool create_placeholders) +ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode *id_node) { const ID *id_orig = id_node->id_orig; ID *id_cow = id_node->id_cow; const int id_cow_recalc = id_cow->recalc; + /* No need to expand such datablocks, their copied ID is same as original * one already. */ if (!deg_copy_on_write_is_needed(id_orig)) { return id_cow; } + DEG_COW_PRINT( "Expanding datablock for %s: id_orig=%p id_cow=%p\n", id_orig->name, id_orig, id_cow); + /* Sanity checks. */ - /* NOTE: Disabled for now, conflicts when re-using evaluated datablock when - * rebuilding dependencies. */ - if (check_datablock_expanded(id_cow) && create_placeholders) { - deg_free_copy_on_write_datablock(id_cow); - } - // BLI_assert(check_datablock_expanded(id_cow) == false); + BLI_assert(check_datablock_expanded(id_cow) == false); + BLI_assert(id_cow->py_instance == nullptr); + /* Copy data from original ID to a copied version. */ /* TODO(sergey): Avoid doing full ID copy somehow, make Mesh to reference * original geometry arrays for until those are modified. */ @@ -914,8 +876,6 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, /* Perform remapping of the nodes. */ RemapCallbackUserData user_data = {nullptr}; user_data.depsgraph = depsgraph; - user_data.node_builder = node_builder; - user_data.create_placeholders = create_placeholders; BKE_library_foreach_ID_link(nullptr, id_cow, foreach_libblock_remap_callback, @@ -928,16 +888,7 @@ ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, return id_cow; } -/* NOTE: Depsgraph is supposed to have ID node already. */ -ID *deg_expand_copy_on_write_datablock(const Depsgraph *depsgraph, - ID *id_orig, - DepsgraphNodeBuilder *node_builder, - bool create_placeholders) -{ - IDNode *id_node = depsgraph->find_id_node(id_orig); - BLI_assert(id_node != nullptr); - return deg_expand_copy_on_write_datablock(depsgraph, id_node, node_builder, create_placeholders); -} +} // namespace ID *deg_update_copy_on_write_datablock(const Depsgraph *depsgraph, const IDNode *id_node) { @@ -1065,6 +1016,7 @@ void deg_free_copy_on_write_datablock(ID *id_cow) break; } discard_edit_mode_pointers(id_cow); + BKE_libblock_free_data_py(id_cow); BKE_libblock_free_datablock(id_cow, 0); BKE_libblock_free_data(id_cow, false); /* Signal datablock as not being expanded. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h index d0bb841caab..70e510b5ef9 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_copy_on_write.h @@ -50,18 +50,6 @@ struct Depsgraph; class DepsgraphNodeBuilder; struct IDNode; -/* Get fully expanded (ready for use) copy-on-write data-block for the given - * original data-block. - */ -ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, - const IDNode *id_node, - DepsgraphNodeBuilder *node_builder = nullptr, - bool create_placeholders = false); -ID *deg_expand_copy_on_write_datablock(const struct Depsgraph *depsgraph, - struct ID *id_orig, - DepsgraphNodeBuilder *node_builder = nullptr, - bool create_placeholders = false); - /* Makes sure given CoW data-block is brought back to state of the original * data-block. */ diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc index 7893e8c64c1..8bf64af7d5d 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc @@ -52,7 +52,9 @@ void RuntimeBackup::init_from_id(ID *id) } have_backup = true; + /* Clear, so freeing the expanded data doesn't touch this Python reference. */ id_data.py_instance = id->py_instance; + id->py_instance = nullptr; animation_backup.init_from_id(id); diff --git a/source/blender/depsgraph/intern/node/deg_node.cc b/source/blender/depsgraph/intern/node/deg_node.cc index fee5342df59..16089ba27dd 100644 --- a/source/blender/depsgraph/intern/node/deg_node.cc +++ b/source/blender/depsgraph/intern/node/deg_node.cc @@ -177,7 +177,7 @@ eDepsSceneComponentType nodeTypeToSceneComponent(NodeType type) case NodeType::SIMULATION: return DEG_SCENE_COMP_PARAMETERS; } - BLI_assert_msg(0, "Unhandled node type, not suppsed to happen."); + BLI_assert_msg(0, "Unhandled node type, not supposed to happen."); return DEG_SCENE_COMP_PARAMETERS; } diff --git a/source/blender/depsgraph/intern/node/deg_node_component.cc b/source/blender/depsgraph/intern/node/deg_node_component.cc index a29618cefa8..0947fad7670 100644 --- a/source/blender/depsgraph/intern/node/deg_node_component.cc +++ b/source/blender/depsgraph/intern/node/deg_node_component.cc @@ -90,7 +90,7 @@ ComponentNode::ComponentNode() void ComponentNode::init(const ID * /*id*/, const char * /*subdata*/) { /* hook up eval context? */ - // XXX: maybe this needs a special API? + /* XXX: maybe this needs a special API? */ } /* Free 'component' node */ diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index 71115c5ceb9..3577b836d77 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -61,6 +61,7 @@ set(SRC intern/mesh_extractors/extract_mesh_ibo_lines_paint_mask.cc intern/mesh_extractors/extract_mesh_ibo_points.cc intern/mesh_extractors/extract_mesh_ibo_tris.cc + intern/mesh_extractors/extract_mesh_vbo_attributes.cc intern/mesh_extractors/extract_mesh_vbo_edge_fac.cc intern/mesh_extractors/extract_mesh_vbo_edit_data.cc intern/mesh_extractors/extract_mesh_vbo_edituv_data.cc @@ -106,10 +107,13 @@ set(SRC intern/draw_manager_texture.c intern/draw_select_buffer.c intern/draw_shader.c + intern/draw_texture_pool.cc intern/draw_view.c + intern/draw_view_data.cc + intern/smaa_textures.c engines/basic/basic_engine.c - engines/image/image_engine.c - engines/image/image_shader.c + engines/image/image_engine.cc + engines/image/image_shader.cc engines/eevee/eevee_bloom.c engines/eevee/eevee_cryptomatte.c engines/eevee/eevee_data.c @@ -188,7 +192,6 @@ set(SRC engines/overlay/overlay_wireframe.c DRW_engine.h - DRW_engine_types.h DRW_select_buffer.h intern/DRW_render.h intern/draw_cache.h @@ -205,7 +208,9 @@ set(SRC intern/draw_manager_testing.h intern/draw_manager_text.h intern/draw_shader.h + intern/draw_texture_pool.h intern/draw_view.h + intern/draw_view_data.h intern/mesh_extractors/extract_mesh.h intern/smaa_textures.h engines/basic/basic_engine.h @@ -215,7 +220,7 @@ set(SRC engines/eevee/eevee_private.h engines/external/external_engine.h engines/image/image_engine.h - engines/image/image_private.h + engines/image/image_private.hh engines/workbench/workbench_engine.h engines/workbench/workbench_private.h engines/select/select_engine.h diff --git a/source/blender/draw/DRW_engine.h b/source/blender/draw/DRW_engine.h index a125a13eaf9..af6c8ea62b2 100644 --- a/source/blender/draw/DRW_engine.h +++ b/source/blender/draw/DRW_engine.h @@ -26,13 +26,12 @@ #include "DNA_object_enums.h" -#include "DRW_engine_types.h" - #ifdef __cplusplus extern "C" { #endif struct ARegion; +struct DRWData; struct DRWInstanceDataList; struct Depsgraph; struct DrawEngineType; @@ -57,8 +56,6 @@ void DRW_engines_free(void); bool DRW_engine_render_support(struct DrawEngineType *draw_engine_type); void DRW_engine_register(struct DrawEngineType *draw_engine_type); -void DRW_engine_viewport_data_size_get( - const void *engine_type, int *r_fbl_len, int *r_txl_len, int *r_psl_len, int *r_stl_len); typedef struct DRWUpdateContext { struct Main *bmain; @@ -176,6 +173,15 @@ void DRW_deferred_shader_remove(struct GPUMaterial *mat); struct DrawDataList *DRW_drawdatalist_from_id(struct ID *id); void DRW_drawdata_free(struct ID *id); +struct DRWData *DRW_viewport_data_create(void); +void DRW_viewport_data_free(struct DRWData *drw_data); + +bool DRW_opengl_context_release(void); +void DRW_opengl_context_activate(bool drw_state); + +/* We may want to move this into a more general location. */ +void DRW_draw_cursor_2d_ex(const struct ARegion *region, const float cursor[2]); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index 87f5c6f5857..f4fdb9d0912 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -45,6 +45,7 @@ extern char datatoc_depth_vert_glsl[]; extern char datatoc_conservative_depth_geom_glsl[]; extern char datatoc_common_view_lib_glsl[]; +extern char datatoc_common_pointcloud_lib_glsl[]; /* *********** LISTS *********** */ @@ -56,6 +57,7 @@ typedef struct BASIC_StorageList { typedef struct BASIC_PassList { struct DRWPass *depth_pass[2]; + struct DRWPass *depth_pass_pointcloud[2]; struct DRWPass *depth_pass_cull[2]; } BASIC_PassList; @@ -70,7 +72,9 @@ typedef struct BASIC_Data { typedef struct BASIC_Shaders { /* Depth Pre Pass */ struct GPUShader *depth; + struct GPUShader *pointcloud_depth; struct GPUShader *depth_conservative; + struct GPUShader *pointcloud_depth_conservative; } BASIC_Shaders; /* *********** STATIC *********** */ @@ -83,6 +87,7 @@ typedef struct BASIC_PrivateData { DRWShadingGroup *depth_shgrp[2]; DRWShadingGroup *depth_shgrp_cull[2]; DRWShadingGroup *depth_hair_shgrp[2]; + DRWShadingGroup *depth_pointcloud_shgrp[2]; bool use_material_slot_selection; } BASIC_PrivateData; /* Transient data */ @@ -106,6 +111,20 @@ static void basic_engine_init(void *UNUSED(vedata)) .defs = (const char *[]){sh_cfg->def, NULL}, }); + sh_data->pointcloud_depth = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); + sh_data->depth_conservative = GPU_shader_create_from_arrays({ .vert = (const char *[]){sh_cfg->lib, datatoc_common_view_lib_glsl, @@ -118,6 +137,25 @@ static void basic_engine_init(void *UNUSED(vedata)) .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, .defs = (const char *[]){sh_cfg->def, "#define CONSERVATIVE_RASTER\n", NULL}, }); + + sh_data->pointcloud_depth_conservative = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_pointcloud_lib_glsl, + datatoc_depth_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_conservative_depth_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_depth_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + "#define CONSERVATIVE_RASTER\n", + "#define POINTCLOUD\n", + "#define INSTANCED_ATTR\n", + "#define UNIFORM_RESOURCE_ID\n", + NULL}, + }); } } @@ -150,9 +188,17 @@ static void basic_cache_init(void *vedata) DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + sh = DRW_state_is_select() ? sh_data->pointcloud_depth_conservative : sh_data->pointcloud_depth; + DRW_PASS_CREATE(psl->depth_pass_pointcloud[i], state | clip_state | infront_state); + stl->g_data->depth_pointcloud_shgrp[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_pointcloud[i]); + DRW_shgroup_uniform_vec2(grp, "sizeViewport", DRW_viewport_size_get(), 1); + DRW_shgroup_uniform_vec2(grp, "sizeViewportInv", DRW_viewport_invert_size_get(), 1); + stl->g_data->depth_hair_shgrp[i] = grp = DRW_shgroup_create(sh_data->depth, psl->depth_pass[i]); + + sh = DRW_state_is_select() ? sh_data->depth_conservative : sh_data->depth; state |= DRW_STATE_CULL_BACK; DRW_PASS_CREATE(psl->depth_pass_cull[i], state | clip_state | infront_state); stl->g_data->depth_shgrp_cull[i] = grp = DRW_shgroup_create(sh, psl->depth_pass_cull[i]); @@ -231,8 +277,16 @@ static void basic_cache_populate(void *vedata, Object *ob) !DRW_state_is_image_render(); const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)); - DRWShadingGroup *shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : - stl->g_data->depth_shgrp[do_in_front]; + + DRWShadingGroup *shgrp = NULL; + + if (ob->type == OB_POINTCLOUD) { + shgrp = stl->g_data->depth_pointcloud_shgrp[do_in_front]; + } + else { + shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : + stl->g_data->depth_shgrp[do_in_front]; + } if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, false, false); @@ -273,8 +327,10 @@ static void basic_draw_scene(void *vedata) BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl; DRW_draw_pass(psl->depth_pass[0]); + DRW_draw_pass(psl->depth_pass_pointcloud[0]); DRW_draw_pass(psl->depth_pass_cull[0]); DRW_draw_pass(psl->depth_pass[1]); + DRW_draw_pass(psl->depth_pass_pointcloud[1]); DRW_draw_pass(psl->depth_pass_cull[1]); } @@ -284,6 +340,8 @@ static void basic_engine_free(void) BASIC_Shaders *sh_data = &e_data.sh_data[i]; DRW_SHADER_FREE_SAFE(sh_data->depth); DRW_SHADER_FREE_SAFE(sh_data->depth_conservative); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth); + DRW_SHADER_FREE_SAFE(sh_data->pointcloud_depth_conservative); } } diff --git a/source/blender/draw/engines/basic/shaders/depth_vert.glsl b/source/blender/draw/engines/basic/shaders/depth_vert.glsl index 318d0acef6f..be2a7d56d19 100644 --- a/source/blender/draw/engines/basic/shaders/depth_vert.glsl +++ b/source/blender/draw/engines/basic/shaders/depth_vert.glsl @@ -3,7 +3,9 @@ RESOURCE_ID_VARYING #endif +#ifndef POINTCLOUD in vec3 pos; +#endif void main() { @@ -12,7 +14,12 @@ void main() PASS_RESOURCE_ID #endif +#ifdef POINTCLOUD + vec3 world_pos = pointcloud_get_pos(); +#else vec3 world_pos = point_object_to_world(pos); +#endif + gl_Position = point_world_to_ndc(world_pos); #ifdef USE_WORLD_CLIP_PLANES diff --git a/source/blender/draw/engines/eevee/eevee_cryptomatte.c b/source/blender/draw/engines/eevee/eevee_cryptomatte.c index 76a1b561972..4c9ce9dbd65 100644 --- a/source/blender/draw/engines/eevee/eevee_cryptomatte.c +++ b/source/blender/draw/engines/eevee/eevee_cryptomatte.c @@ -139,8 +139,6 @@ void EEVEE_cryptomatte_renderpasses_init(EEVEE_Data *vedata) g_data->cryptomatte_session = session; g_data->render_passes |= EEVEE_RENDER_PASS_CRYPTOMATTE | EEVEE_RENDER_PASS_VOLUME_LIGHT; - g_data->cryptomatte_accurate_mode = (view_layer->cryptomatte_flag & - VIEW_LAYER_CRYPTOMATTE_ACCURATE) != 0; } } @@ -166,12 +164,12 @@ void EEVEE_cryptomatte_output_init(EEVEE_ViewLayerData *UNUSED(sldata), if (g_data->cryptomatte_accum_buffer == NULL) { g_data->cryptomatte_accum_buffer = MEM_calloc_arrayN( - sizeof(EEVEE_CryptomatteSample), buffer_size * eevee_cryptomatte_pixel_stride(view_layer), + sizeof(EEVEE_CryptomatteSample), __func__); /* Download buffer should store a float per active cryptomatte layer. */ g_data->cryptomatte_download_buffer = MEM_malloc_arrayN( - sizeof(float), buffer_size * num_cryptomatte_layers, __func__); + buffer_size * num_cryptomatte_layers, sizeof(float), __func__); } else { /* During multiview rendering the `cryptomatte_accum_buffer` is deallocated after all views @@ -257,7 +255,7 @@ static void eevee_cryptomatte_hair_cache_populate(EEVEE_Data *vedata, { DRWShadingGroup *grp = eevee_cryptomatte_shading_group_create( vedata, sldata, ob, material, true); - DRW_shgroup_hair_create_sub(ob, psys, md, grp); + DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL); } void EEVEE_cryptomatte_object_hair_cache_populate(EEVEE_Data *vedata, @@ -405,7 +403,6 @@ void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EE { EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; - EEVEE_PrivateData *g_data = stl->g_data; EEVEE_EffectsInfo *effects = stl->effects; EEVEE_PassList *psl = vedata->psl; const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -413,10 +410,9 @@ void EEVEE_cryptomatte_output_accumulate(EEVEE_ViewLayerData *UNUSED(sldata), EE const int cryptomatte_levels = view_layer->cryptomatte_levels; const int current_sample = effects->taa_current_sample; - /* In accurate mode all render samples are evaluated. In inaccurate mode this is limited to the - * number of cryptomatte levels. This will reduce the overhead of downloading the GPU buffer and - * integrating it into the accum buffer. */ - if (g_data->cryptomatte_accurate_mode || current_sample < cryptomatte_levels) { + /* Render samples used by cryptomatte are limited to the number of cryptomatte levels. This will + * reduce the overhead of downloading the GPU buffer and integrating it into the accum buffer. */ + if (current_sample < cryptomatte_levels) { static float clear_color[4] = {0.0}; GPU_framebuffer_bind(fbl->cryptomatte_fb); GPU_framebuffer_clear_color(fbl->cryptomatte_fb, clear_color); diff --git a/source/blender/draw/engines/eevee/eevee_effects.c b/source/blender/draw/engines/eevee/eevee_effects.c index 3a38edecec6..c2f3e3a6d95 100644 --- a/source/blender/draw/engines/eevee/eevee_effects.c +++ b/source/blender/draw/engines/eevee/eevee_effects.c @@ -38,7 +38,8 @@ static struct { struct GPUTexture *color_src; int depth_src_layer; - float cube_texel_size; + /* Size can be vec3. But we only use 2 components in the shader. */ + float texel_size[2]; } e_data = {NULL}; /* Engine data */ #define SETUP_BUFFER(tex, fb, fb_color) \ @@ -93,6 +94,10 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, effects = stl->effects; + int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL; + effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div; + effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div; + effects->enabled_effects = 0; effects->enabled_effects |= (G.debug_value == 9) ? EFFECT_VELOCITY_BUFFER : 0; effects->enabled_effects |= EEVEE_motion_blur_init(sldata, vedata); @@ -117,9 +122,6 @@ void EEVEE_effects_init(EEVEE_ViewLayerData *sldata, /** * MinMax Pyramid */ - int div = 1 << MAX_SCREEN_BUFFERS_LOD_LEVEL; - effects->hiz_size[0] = divide_ceil_u(size_fs[0], div) * div; - effects->hiz_size[1] = divide_ceil_u(size_fs[1], div) * div; if (GPU_type_matches(GPU_DEVICE_INTEL, GPU_OS_ANY, GPU_DRIVER_ANY)) { /* Intel gpu seems to have problem rendering to only depth hiz_format */ @@ -259,6 +261,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_PASS_CREATE(psl->color_downsample_ps, DRW_STATE_WRITE_COLOR); grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_sh_get(), psl->color_downsample_ps); DRW_shgroup_uniform_texture_ex(grp, "source", txl->filtered_radiance, GPU_SAMPLER_FILTER); + DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1); DRW_shgroup_call_procedural_triangles(grp, NULL, 1); } @@ -267,7 +270,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) grp = DRW_shgroup_create(EEVEE_shaders_effect_downsample_cube_sh_get(), psl->color_downsample_cube_ps); DRW_shgroup_uniform_texture_ref(grp, "source", &e_data.color_src); - DRW_shgroup_uniform_float(grp, "texelSize", &e_data.cube_texel_size, 1); + DRW_shgroup_uniform_float(grp, "texelSize", e_data.texel_size, 1); DRW_shgroup_uniform_int_copy(grp, "Layer", 0); DRW_shgroup_call_instances(grp, NULL, quad, 6); } @@ -277,6 +280,7 @@ void EEVEE_effects_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) DRW_PASS_CREATE(psl->maxz_downlevel_ps, downsample_write); grp = DRW_shgroup_create(EEVEE_shaders_effect_maxz_downlevel_sh_get(), psl->maxz_downlevel_ps); DRW_shgroup_uniform_texture_ref_ex(grp, "depthBuffer", &txl->maxzbuffer, GPU_SAMPLER_DEFAULT); + DRW_shgroup_uniform_vec2(grp, "texelSize", e_data.texel_size, 1); DRW_shgroup_call(grp, quad, NULL); /* Copy depth buffer to top level of HiZ */ @@ -345,16 +349,22 @@ static void min_downsample_cb(void *vedata, int UNUSED(level)) } #endif -static void max_downsample_cb(void *vedata, int UNUSED(level)) +static void max_downsample_cb(void *vedata, int level) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl; + int texture_size[3]; + GPU_texture_get_mipmap_size(txl->maxzbuffer, level - 1, texture_size); + e_data.texel_size[0] = 1.0f / texture_size[0]; + e_data.texel_size[1] = 1.0f / texture_size[1]; DRW_draw_pass(psl->maxz_downlevel_ps); } static void simple_downsample_cube_cb(void *vedata, int level) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; - e_data.cube_texel_size = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src); + e_data.texel_size[0] = (float)(1 << level) / (float)GPU_texture_width(e_data.color_src); + e_data.texel_size[1] = e_data.texel_size[0]; DRW_draw_pass(psl->color_downsample_cube_ps); } @@ -390,9 +400,14 @@ void EEVEE_create_minmax_buffer(EEVEE_Data *vedata, GPUTexture *depth_src, int l } } -static void downsample_radiance_cb(void *vedata, int UNUSED(level)) +static void downsample_radiance_cb(void *vedata, int level) { EEVEE_PassList *psl = ((EEVEE_Data *)vedata)->psl; + EEVEE_TextureList *txl = ((EEVEE_Data *)vedata)->txl; + int texture_size[3]; + GPU_texture_get_mipmap_size(txl->filtered_radiance, level - 1, texture_size); + e_data.texel_size[0] = 1.0f / texture_size[0]; + e_data.texel_size[1] = 1.0f / texture_size[1]; DRW_draw_pass(psl->color_downsample_ps); } @@ -473,7 +488,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) EEVEE_depth_of_field_draw(vedata); /* NOTE: Lookdev drawing happens before TAA but after - * motion blur and dof to avoid distortions. + * motion blur and DOF to avoid distortions. * Velocity resolve use a hack to exclude lookdev * spheres from creating shimmering re-projection vectors. */ EEVEE_lookdev_draw(vedata); @@ -485,7 +500,7 @@ void EEVEE_draw_effects(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) * the swapping of the buffers. */ EEVEE_renderpasses_output_accumulate(sldata, vedata, true); - /* Save the final texture and framebuffer for final transformation or read. */ + /* Save the final texture and frame-buffer for final transformation or read. */ effects->final_tx = effects->source_buffer; effects->final_fb = (effects->target_buffer != fbl->main_color_fb) ? fbl->main_fb : fbl->effect_fb; diff --git a/source/blender/draw/engines/eevee/eevee_engine.c b/source/blender/draw/engines/eevee/eevee_engine.c index 6a66e8b1a58..68975cff48c 100644 --- a/source/blender/draw/engines/eevee/eevee_engine.c +++ b/source/blender/draw/engines/eevee/eevee_engine.c @@ -468,6 +468,9 @@ static void eevee_render_to_image(void *vedata, g_data->render_sample_count_per_timestep = EEVEE_temporal_sampling_sample_count_get(scene, ved->stl); + /* Reset in case the same engine is used on multiple views. */ + EEVEE_temporal_sampling_reset(vedata); + /* Compute start time. The motion blur will cover `[time ...time + shuttertime]`. */ float time = initial_frame + initial_subframe; switch (scene->eevee.motion_blur_position) { @@ -648,6 +651,8 @@ RenderEngineType DRW_engine_viewport_eevee_type = { NULL, NULL, NULL, + NULL, + NULL, &EEVEE_render_update_passes, &draw_engine_eevee_type, {NULL, NULL, NULL}, diff --git a/source/blender/draw/engines/eevee/eevee_lightprobes.c b/source/blender/draw/engines/eevee/eevee_lightprobes.c index 8598655477f..ab083253499 100644 --- a/source/blender/draw/engines/eevee/eevee_lightprobes.c +++ b/source/blender/draw/engines/eevee/eevee_lightprobes.c @@ -1203,6 +1203,8 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v return; } + float hiz_uv_scale_prev[2] = {UNPACK2(common_data->hiz_uv_scale)}; + /* Temporary Remove all planar reflections (avoid lag effect). */ common_data->prb_num_planar = 0; /* Turn off ssr to avoid black specular */ @@ -1212,6 +1214,9 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ray_type = EEVEE_RAY_GLOSSY; common_data->ray_depth = 1.0f; + /* Planar reflections are rendered at the `hiz` resolution, so no need to scaling. */ + copy_v2_fl(common_data->hiz_uv_scale, 1.0f); + GPU_uniformbuf_update(sldata->common_ubo, &sldata->common_data); /* Rendering happens here! */ @@ -1227,6 +1232,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v common_data->ssr_toggle = true; common_data->ssrefract_toggle = true; common_data->sss_toggle = true; + copy_v2_v2(common_data->hiz_uv_scale, hiz_uv_scale_prev); /* Prefilter for SSR */ if ((vedata->stl->effects->enabled_effects & EFFECT_SSR) != 0) { @@ -1234,7 +1240,7 @@ void EEVEE_lightprobes_refresh_planar(EEVEE_ViewLayerData *sldata, EEVEE_Data *v } if (DRW_state_is_image_render()) { - /* Sort transparents because planar reflections could have re-sorted them. */ + /* Sort the transparent passes because planar reflections could have re-sorted them. */ DRW_pass_sort_shgroup_z(vedata->psl->transparent_pass); } diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index 9ecb737192e..a627bcd9488 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -769,15 +769,16 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata, EeveeMaterialCache matcache = eevee_material_cache_get(vedata, sldata, ob, matnr - 1, true); if (matcache.depth_grp) { - *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp); + *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp, NULL); DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat); } if (matcache.shading_grp) { - *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp); + *matcache.shading_grp_p = DRW_shgroup_hair_create_sub( + ob, psys, md, matcache.shading_grp, matcache.shading_gpumat); DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat); } if (matcache.shadow_grp) { - *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp); + *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp, NULL); DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); *cast_shadow = true; } diff --git a/source/blender/draw/engines/eevee/eevee_motion_blur.c b/source/blender/draw/engines/eevee/eevee_motion_blur.c index 2e200c8e053..1eff2a3af24 100644 --- a/source/blender/draw/engines/eevee/eevee_motion_blur.c +++ b/source/blender/draw/engines/eevee/eevee_motion_blur.c @@ -269,7 +269,7 @@ void EEVEE_motion_blur_hair_cache_populate(EEVEE_ViewLayerData *UNUSED(sldata), GPUTexture *tex_prev = mb_hair->psys[psys_id].hair_pos_tx[MB_PREV]; GPUTexture *tex_next = mb_hair->psys[psys_id].hair_pos_tx[MB_NEXT]; - grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp); + grp = DRW_shgroup_hair_create_sub(ob, psys, md, effects->motion_blur.hair_grp, NULL); DRW_shgroup_uniform_mat4(grp, "prevModelMatrix", mb_data->obmat[MB_PREV]); DRW_shgroup_uniform_mat4(grp, "currModelMatrix", mb_data->obmat[MB_CURR]); DRW_shgroup_uniform_mat4(grp, "nextModelMatrix", mb_data->obmat[MB_NEXT]); @@ -405,8 +405,8 @@ void EEVEE_motion_blur_cache_finish(EEVEE_Data *vedata) /* Push instances attributes to the GPU. */ DRW_render_instance_buffer_finish(); - /* Need to be called after DRW_render_instance_buffer_finish() */ - /* Also we weed to have a correct fbo bound for DRW_hair_update */ + /* Need to be called after #DRW_render_instance_buffer_finish() */ + /* Also we weed to have a correct FBO bound for #DRW_hair_update. */ GPU_framebuffer_bind(vedata->fbl->main_fb); DRW_hair_update(); diff --git a/source/blender/draw/engines/eevee/eevee_occlusion.c b/source/blender/draw/engines/eevee/eevee_occlusion.c index 4c2024a6f65..1acd9950012 100644 --- a/source/blender/draw/engines/eevee/eevee_occlusion.c +++ b/source/blender/draw/engines/eevee/eevee_occlusion.c @@ -77,14 +77,14 @@ int EEVEE_occlusion_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) common_data->ao_bounce_fac = (scene_eval->eevee.flag & SCE_EEVEE_GTAO_BOUNCE) ? 1.0f : 0.0f; effects->gtao_horizons_renderpass = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type); + UNPACK2(effects->hiz_size), GPU_RGBA8, &draw_engine_eevee_type); GPU_framebuffer_ensure_config( &fbl->gtao_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_renderpass)}); if (G.debug_value == 6) { effects->gtao_horizons_debug = DRW_texture_pool_query_2d( - fs_size[0], fs_size[1], GPU_RGBA8, &draw_engine_eevee_type); + UNPACK2(fs_size), GPU_RGBA8, &draw_engine_eevee_type); GPU_framebuffer_ensure_config( &fbl->gtao_debug_fb, {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(effects->gtao_horizons_debug)}); @@ -188,20 +188,32 @@ void EEVEE_occlusion_cache_init(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) } } -void EEVEE_occlusion_compute(EEVEE_ViewLayerData *UNUSED(sldata), EEVEE_Data *vedata) +void EEVEE_occlusion_compute(EEVEE_ViewLayerData *sldata, EEVEE_Data *vedata) { EEVEE_PassList *psl = vedata->psl; EEVEE_FramebufferList *fbl = vedata->fbl; EEVEE_StorageList *stl = vedata->stl; EEVEE_EffectsInfo *effects = stl->effects; + EEVEE_CommonUniformBuffer *common_data = &sldata->common_data; if ((effects->enabled_effects & EFFECT_GTAO) != 0) { DRW_stats_group_start("GTAO Horizon Scan"); GPU_framebuffer_bind(fbl->gtao_fb); + /** NOTE(fclem): Kind of fragile. We need this to make sure everything lines up + * nicely during planar reflection. */ + if (common_data->ray_type != EEVEE_RAY_GLOSSY) { + const float *viewport_size = DRW_viewport_size_get(); + GPU_framebuffer_viewport_set(fbl->gtao_fb, 0, 0, UNPACK2(viewport_size)); + } + DRW_draw_pass(psl->ao_horizon_search); + if (common_data->ray_type != EEVEE_RAY_GLOSSY) { + GPU_framebuffer_viewport_reset(fbl->gtao_fb); + } + if (GPU_mip_render_workaround() || GPU_type_matches(GPU_DEVICE_INTEL_UHD, GPU_OS_WIN, GPU_DRIVER_ANY)) { /* Fix dot corruption on intel HD5XX/HD6XX series. */ diff --git a/source/blender/draw/engines/eevee/eevee_private.h b/source/blender/draw/engines/eevee/eevee_private.h index f51b4fa0127..eae5d161cc3 100644 --- a/source/blender/draw/engines/eevee/eevee_private.h +++ b/source/blender/draw/engines/eevee/eevee_private.h @@ -1042,7 +1042,6 @@ typedef struct EEVEE_PrivateData { int aov_hash; int num_aovs_used; struct CryptomatteSession *cryptomatte_session; - bool cryptomatte_accurate_mode; EEVEE_CryptomatteSample *cryptomatte_accum_buffer; float *cryptomatte_download_buffer; diff --git a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl index d4e3b879426..93641443cac 100644 --- a/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl +++ b/source/blender/draw/engines/eevee/shaders/ambient_occlusion_lib.glsl @@ -379,7 +379,7 @@ float specular_occlusion( /* Visibility to cone angle (eq. 18). */ float vis_angle = fast_acos(sqrt(1 - visibility)); /* Roughness to cone angle (eq. 26). */ - float spec_angle = max(0.001, fast_acos(cone_cosine(roughness))); + float spec_angle = max(0.00990998744964599609375, fast_acos(cone_cosine(roughness))); /* Angle between cone axes. */ float cone_cone_dist = fast_acos(saturate(dot(visibility_dir, specular_dir))); float cone_nor_dist = fast_acos(saturate(dot(N, specular_dir))); diff --git a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl index d1cb25af82f..9fc258da185 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_downsample_frag.glsl @@ -9,14 +9,16 @@ uniform sampler2D source; uniform float fireflyFactor; +#ifndef COPY_SRC +uniform vec2 texelSize; +#endif + out vec4 FragColor; void main() { - vec2 texel_size = 1.0 / vec2(textureSize(source, 0)); - vec2 uvs = gl_FragCoord.xy * texel_size; - #ifdef COPY_SRC + vec2 uvs = gl_FragCoord.xy / vec2(textureSize(source, 0)); FragColor = textureLod(source, uvs, 0.0); FragColor = safe_color(FragColor); @@ -25,7 +27,10 @@ void main() FragColor *= 1.0 - max(0.0, luma - fireflyFactor) / luma; #else - vec4 ofs = texel_size.xyxy * vec4(0.75, 0.75, -0.75, -0.75); + /* NOTE(@fclem): textureSize() does not work the same on all implementations + * when changing the min and max texture levels. Use uniform instead (see T87801). */ + vec2 uvs = gl_FragCoord.xy * texelSize; + vec4 ofs = texelSize.xyxy * vec4(0.75, 0.75, -0.75, -0.75); uvs *= 2.0; FragColor = textureLod(source, uvs + ofs.xy, 0.0); diff --git a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl index ccb65d2e5a6..8ef39a55921 100644 --- a/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl +++ b/source/blender/draw/engines/eevee/shaders/effect_minmaxz_frag.glsl @@ -14,6 +14,10 @@ uniform int depthLayer; uniform sampler2D depthBuffer; #endif +#ifndef COPY_DEPTH +uniform vec2 texelSize; +#endif + #ifdef LAYERED # define sampleLowerMip(t) texture(depthBuffer, vec3(t, depthLayer)).r # define gatherLowerMip(t) textureGather(depthBuffer, vec3(t, depthLayer)) @@ -41,23 +45,24 @@ out vec4 fragColor; void main() { vec2 texel = gl_FragCoord.xy; - vec2 texel_size = 1.0 / vec2(textureSize(depthBuffer, 0).xy); #ifdef COPY_DEPTH - vec2 uv = texel * texel_size; + vec2 uv = texel / vec2(textureSize(depthBuffer, 0).xy); float val = sampleLowerMip(uv); #else - vec2 uv = texel * 2.0 * texel_size; + /* NOTE(@fclem): textureSize() does not work the same on all implementations + * when changing the min and max texture levels. Use uniform instead (see T87801). */ + vec2 uv = texel * 2.0 * texelSize; vec4 samp; # ifdef GPU_ARB_texture_gather samp = gatherLowerMip(uv); # else - samp.x = sampleLowerMip(uv + vec2(-0.5, -0.5) * texel_size); - samp.y = sampleLowerMip(uv + vec2(-0.5, 0.5) * texel_size); - samp.z = sampleLowerMip(uv + vec2(0.5, -0.5) * texel_size); - samp.w = sampleLowerMip(uv + vec2(0.5, 0.5) * texel_size); + samp.x = sampleLowerMip(uv + vec2(-0.5, -0.5) * texelSize); + samp.y = sampleLowerMip(uv + vec2(-0.5, 0.5) * texelSize); + samp.z = sampleLowerMip(uv + vec2(0.5, -0.5) * texelSize); + samp.w = sampleLowerMip(uv + vec2(0.5, 0.5) * texelSize); # endif float val = minmax4(samp.x, samp.y, samp.z, samp.w); diff --git a/source/blender/draw/engines/external/external_engine.c b/source/blender/draw/engines/external/external_engine.c index 89ee3f1b293..cc548a53a8e 100644 --- a/source/blender/draw/engines/external/external_engine.c +++ b/source/blender/draw/engines/external/external_engine.c @@ -32,13 +32,19 @@ #include "BKE_object.h" #include "BKE_particle.h" +#include "ED_image.h" #include "ED_screen.h" +#include "GPU_batch.h" +#include "GPU_debug.h" #include "GPU_matrix.h" #include "GPU_shader.h" #include "GPU_state.h" #include "GPU_viewport.h" +#include "RE_engine.h" +#include "RE_pipeline.h" + #include "external_engine.h" /* own include */ /* Shaders */ @@ -137,6 +143,22 @@ static void external_engine_init(void *vedata) } } +/* Add shading group call which will take care of writing to the depth buffer, so that the + * alpha-under overlay will happen for the render buffer. */ +static void external_cache_image_add(DRWShadingGroup *grp) +{ + float obmat[4][4]; + unit_m4(obmat); + scale_m4_fl(obmat, 0.5f); + + /* NOTE: Use the same Z-depth value as in the regular image drawing engine. */ + translate_m4(obmat, 1.0f, 1.0f, 0.75f); + + GPUBatch *geom = DRW_cache_quad_get(); + + DRW_shgroup_call_obmat(grp, geom, obmat); +} + static void external_cache_init(void *vedata) { EXTERNAL_PassList *psl = ((EXTERNAL_Data *)vedata)->psl; @@ -162,14 +184,33 @@ static void external_cache_init(void *vedata) stl->g_data->depth_shgrp = DRW_shgroup_create(e_data.depth_sh, psl->depth_pass); } - /* Do not draw depth pass when overlays are turned off. */ - stl->g_data->need_depth = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + if (v3d != NULL) { + /* Do not draw depth pass when overlays are turned off. */ + stl->g_data->need_depth = (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0; + } + else if (draw_ctx->space_data != NULL) { + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type == SPACE_IMAGE) { + external_cache_image_add(stl->g_data->depth_shgrp); + + stl->g_data->need_depth = true; + stl->g_data->update_depth = true; + } + } } static void external_cache_populate(void *vedata, Object *ob) { + const DRWContextState *draw_ctx = DRW_context_state_get(); EXTERNAL_StorageList *stl = ((EXTERNAL_Data *)vedata)->stl; + if (draw_ctx->space_data != NULL) { + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type == SPACE_IMAGE) { + return; + } + } + if (!(DRW_object_is_renderable(ob) && DRW_object_visibility_in_active_context(ob) & OB_VISIBLE_SELF)) { return; @@ -210,13 +251,11 @@ static void external_cache_finish(void *UNUSED(vedata)) { } -static void external_draw_scene_do(void *vedata) +static void external_draw_scene_do_v3d(void *vedata) { const DRWContextState *draw_ctx = DRW_context_state_get(); - Scene *scene = draw_ctx->scene; RegionView3D *rv3d = draw_ctx->rv3d; ARegion *region = draw_ctx->region; - const RenderEngineType *type; DRW_state_reset_ex(DRW_STATE_DEFAULT & ~DRW_STATE_DEPTH_LESS_EQUAL); @@ -229,8 +268,6 @@ static void external_draw_scene_do(void *vedata) } RenderEngine *engine = RE_engine_create(engine_type); - engine->tile_x = scene->r.tilex; - engine->tile_y = scene->r.tiley; engine_type->view_update(engine, draw_ctx->evil_C, draw_ctx->depsgraph); rv3d->render_engine = engine; } @@ -241,7 +278,7 @@ static void external_draw_scene_do(void *vedata) ED_region_pixelspace(region); /* Render result draw. */ - type = rv3d->render_engine->type; + const RenderEngineType *type = rv3d->render_engine->type; type->view_draw(rv3d->render_engine, draw_ctx->evil_C, draw_ctx->depsgraph); GPU_bgl_end(); @@ -259,6 +296,116 @@ static void external_draw_scene_do(void *vedata) } } +/* Configure current matrix stack so that the external engine can use the same drawing code for + * both viewport and image editor drawing. + * + * The engine draws result in the pixel space, and is applying render offset. For image editor we + * need to switch from normalized space to pixel space, and "un-apply" offset. */ +static void external_image_space_matrix_set(const RenderEngine *engine) +{ + BLI_assert(engine != NULL); + + const DRWContextState *draw_ctx = DRW_context_state_get(); + const DRWView *view = DRW_view_get_active(); + struct SpaceImage *space_image = (struct SpaceImage *)draw_ctx->space_data; + + /* Apply current view as transformation matrix. + * This will configure drawing for normalized space with current zoom and pan applied. */ + + float view_matrix[4][4]; + DRW_view_viewmat_get(view, view_matrix, false); + + float projection_matrix[4][4]; + DRW_view_winmat_get(view, projection_matrix, false); + + GPU_matrix_projection_set(projection_matrix); + GPU_matrix_set(view_matrix); + + /* Switch from normalized space to pixel space. */ + { + int width, height; + ED_space_image_get_size(space_image, &width, &height); + + const float width_inv = width ? 1.0f / width : 0.0f; + const float height_inv = height ? 1.0f / height : 0.0f; + GPU_matrix_scale_2f(width_inv, height_inv); + } + + /* Un-apply render offset. */ + { + Render *render = engine->re; + rctf view_rect; + rcti render_rect; + RE_GetViewPlane(render, &view_rect, &render_rect); + + GPU_matrix_translate_2f(-render_rect.xmin, -render_rect.ymin); + } +} + +static void external_draw_scene_do_image(void *UNUSED(vedata)) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene = draw_ctx->scene; + Render *re = RE_GetSceneRender(scene); + RenderEngine *engine = RE_engine_get(re); + + /* Is tested before enabling the drawing engine. */ + BLI_assert(re != NULL); + BLI_assert(engine != NULL); + + const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + /* Clear the depth buffer to the value used by the background overlay so that the overlay is not + * happening outside of the drawn image. + * + * NOTE: The external engine only draws color. The depth is taken care of using the depth pass + * which initialized the depth to the values expected by the background overlay. */ + GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f); + + GPU_matrix_push_projection(); + GPU_matrix_push(); + + external_image_space_matrix_set(engine); + + GPU_debug_group_begin("External Engine"); + + const RenderEngineType *engine_type = engine->type; + BLI_assert(engine_type != NULL); + BLI_assert(engine_type->draw != NULL); + + engine_type->draw(engine, draw_ctx->evil_C, draw_ctx->depsgraph); + + GPU_debug_group_end(); + + GPU_matrix_pop(); + GPU_matrix_pop_projection(); + + DRW_state_reset(); + GPU_bgl_end(); + + RE_engine_draw_release(re); +} + +static void external_draw_scene_do(void *vedata) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + + if (draw_ctx->v3d != NULL) { + external_draw_scene_do_v3d(vedata); + return; + } + + if (draw_ctx->space_data == NULL) { + return; + } + + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type == SPACE_IMAGE) { + external_draw_scene_do_image(vedata); + return; + } +} + static void external_draw_scene(void *vedata) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -297,7 +444,7 @@ static void external_engine_free(void) static const DrawEngineDataSize external_data_size = DRW_VIEWPORT_DATA_SIZE(EXTERNAL_Data); -static DrawEngineType draw_engine_external_type = { +DrawEngineType draw_engine_external_type = { NULL, NULL, N_("External"), @@ -330,8 +477,45 @@ RenderEngineType DRW_engine_viewport_external_type = { NULL, NULL, NULL, + NULL, + NULL, &draw_engine_external_type, {NULL, NULL, NULL}, }; +bool DRW_engine_external_acquire_for_image_editor(void) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const SpaceLink *space_data = draw_ctx->space_data; + Scene *scene = draw_ctx->scene; + + if (space_data == NULL) { + return false; + } + + const eSpace_Type space_type = draw_ctx->space_data->spacetype; + if (space_type != SPACE_IMAGE) { + return false; + } + + struct SpaceImage *space_image = (struct SpaceImage *)space_data; + const Image *image = ED_space_image(space_image); + if (image == NULL || image->type != IMA_TYPE_R_RESULT) { + return false; + } + + if (image->render_slot != image->last_render_slot) { + return false; + } + + /* Render is allocated on main thread, so it is safe to access it from here. */ + Render *re = RE_GetSceneRender(scene); + + if (re == NULL) { + return false; + } + + return RE_engine_draw_acquire(re); +} + #undef EXTERNAL_ENGINE diff --git a/source/blender/draw/engines/external/external_engine.h b/source/blender/draw/engines/external/external_engine.h index c645fb99e0e..14ec4e2d3c5 100644 --- a/source/blender/draw/engines/external/external_engine.h +++ b/source/blender/draw/engines/external/external_engine.h @@ -22,4 +22,12 @@ #pragma once +extern DrawEngineType draw_engine_external_type; extern RenderEngineType DRW_engine_viewport_external_type; + +/* Check whether an external engine is to be used to draw content of an image editor. + * If the drawing is possible, the render engine is "acquired" so that it is not freed by the + * render engine for until drawing is finished. + * + * NOTE: Released by the draw engine when it is done drawing. */ +bool DRW_engine_external_acquire_for_image_editor(void); diff --git a/source/blender/draw/engines/gpencil/gpencil_draw_data.c b/source/blender/draw/engines/gpencil/gpencil_draw_data.c index e3e84dd4c8c..4636b9e949a 100644 --- a/source/blender/draw/engines/gpencil/gpencil_draw_data.c +++ b/source/blender/draw/engines/gpencil/gpencil_draw_data.c @@ -59,7 +59,6 @@ static struct GPUTexture *gpencil_image_texture_get(Image *image, bool *r_alpha_ struct GPUTexture *gpu_tex = NULL; void *lock; - iuser.ok = true; ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); if (ibuf != NULL && ibuf->rect != NULL) { diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 1078cebdbff..50e32040522 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -120,15 +120,9 @@ void GPENCIL_engine_init(void *ved) bool use_scene_world = false; if (v3d) { - use_scene_lights = ((v3d->shading.type == OB_MATERIAL) && - (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || - ((v3d->shading.type == OB_RENDER) && - (v3d->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); + use_scene_lights = V3D_USES_SCENE_LIGHTS(v3d); - use_scene_world = ((v3d->shading.type == OB_MATERIAL) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD)) || - ((v3d->shading.type == OB_RENDER) && - (v3d->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + use_scene_world = V3D_USES_SCENE_WORLD(v3d); stl->pd->v3d_color_type = (v3d->shading.type == OB_SOLID) ? v3d->shading.color_type : -1; /* Special case: If Vertex Paint mode, use always Vertex mode. */ diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 34fe29055d6..674aca29662 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -298,14 +298,14 @@ typedef struct GPENCIL_PrivateData { /* Current frame */ int cfra; /* If we are rendering for final render (F12). - * NOTE: set to false for viewport and opengl rendering (including VSE scene rendering), but set - * to true when rendering in `OB_RENDER` shading mode (viewport or opengl rendering) */ + * NOTE: set to false for viewport and opengl rendering (including sequencer scene rendering), + * but set to true when rendering in #OB_RENDER shading mode (viewport or opengl rendering). */ bool is_render; /* If we are in viewport display (used for VFX). */ bool is_viewport; /* True in selection and auto_depth drawing */ bool draw_depth_only; - /* Is shading set to wireframe. */ + /* Is shading set to wire-frame. */ bool draw_wireframe; /* Used by the depth merge step. */ int is_stroke_order_3d; diff --git a/source/blender/draw/engines/image/image_engine.c b/source/blender/draw/engines/image/image_engine.cc similarity index 89% rename from source/blender/draw/engines/image/image_engine.c rename to source/blender/draw/engines/image/image_engine.cc index 662238df270..776639f5224 100644 --- a/source/blender/draw/engines/image/image_engine.c +++ b/source/blender/draw/engines/image/image_engine.cc @@ -39,7 +39,9 @@ #include "GPU_batch.h" #include "image_engine.h" -#include "image_private.h" +#include "image_private.hh" + +namespace blender::draw::image_engine { #define IMAGE_DRAW_FLAG_SHOW_ALPHA (1 << 0) #define IMAGE_DRAW_FLAG_APPLY_ALPHA (1 << 1) @@ -107,7 +109,7 @@ static void space_image_gpu_texture_get(Image *image, { const DRWContextState *draw_ctx = DRW_context_state_get(); SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - if (BKE_image_is_multilayer(image)) { + if (image->rr != nullptr) { /* Update multi-index and pass for the current eye. */ BKE_image_multilayer_index(image->rr, &sima->iuser); } @@ -115,14 +117,14 @@ static void space_image_gpu_texture_get(Image *image, BKE_image_multiview_index(image, &sima->iuser); } - if (ibuf == NULL) { + if (ibuf == nullptr) { return; } - if (ibuf->rect == NULL && ibuf->rect_float == NULL) { + if (ibuf->rect == nullptr && ibuf->rect_float == nullptr) { /* This code-path is only supposed to happen when drawing a lazily-allocatable render result. - * In all the other cases the `ED_space_image_acquire_buffer()` is expected to return NULL as - * an image buffer when it has no pixels. */ + * In all the other cases the `ED_space_image_acquire_buffer()` is expected to return nullptr + * as an image buffer when it has no pixels. */ BLI_assert(image->type == IMA_TYPE_R_RESULT); @@ -150,7 +152,7 @@ static void space_image_gpu_texture_get(Image *image, } else if (image->source == IMA_SRC_TILED) { *r_gpu_texture = BKE_image_get_gpu_tiles(image, iuser, ibuf); - *r_tex_tile_data = BKE_image_get_gpu_tilemap(image, iuser, NULL); + *r_tex_tile_data = BKE_image_get_gpu_tilemap(image, iuser, nullptr); *r_owns_texture = false; } else { @@ -168,7 +170,7 @@ static void space_node_gpu_texture_get(Image *image, { *r_gpu_texture = BKE_image_get_gpu_texture(image, iuser, ibuf); *r_owns_texture = false; - *r_tex_tile_data = NULL; + *r_tex_tile_data = nullptr; } static void image_gpu_texture_get(Image *image, @@ -204,7 +206,7 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser const char space_type = draw_ctx->space_data->spacetype; const Scene *scene = draw_ctx->scene; - GPUTexture *tex_tile_data = NULL; + GPUTexture *tex_tile_data = nullptr; image_gpu_texture_get(image, iuser, ibuf, &pd->texture, &pd->owns_texture, &tex_tile_data); if (pd->texture) { @@ -218,7 +220,7 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser } const bool use_premul_alpha = BKE_image_has_gpu_texture_premultiplied_alpha(image, ibuf); - const bool is_tiled_texture = tex_tile_data != NULL; + const bool is_tiled_texture = tex_tile_data != nullptr; int draw_flags = 0; if (space_type == SPACE_IMAGE) { @@ -307,11 +309,11 @@ static void image_cache_image(IMAGE_Data *vedata, Image *image, ImageUser *iuser GPUShader *shader = IMAGE_shader_image_get(is_tiled_texture); DRWShadingGroup *shgrp = DRW_shgroup_create(shader, psl->image_pass); if (is_tiled_texture) { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, 0); + DRW_shgroup_uniform_texture_ex(shgrp, "imageTileArray", pd->texture, GPU_SAMPLER_DEFAULT); DRW_shgroup_uniform_texture(shgrp, "imageTileData", tex_tile_data); } else { - DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, 0); + DRW_shgroup_uniform_texture_ex(shgrp, "imageTexture", pd->texture, GPU_SAMPLER_DEFAULT); } DRW_shgroup_uniform_vec2_copy(shgrp, "farNearDistances", far_near); DRW_shgroup_uniform_vec4_copy(shgrp, "color", color); @@ -332,13 +334,13 @@ static void IMAGE_engine_init(void *ved) IMAGE_Data *vedata = (IMAGE_Data *)ved; IMAGE_StorageList *stl = vedata->stl; if (!stl->pd) { - stl->pd = MEM_callocN(sizeof(IMAGE_PrivateData), __func__); + stl->pd = static_cast(MEM_callocN(sizeof(IMAGE_PrivateData), __func__)); } IMAGE_PrivateData *pd = stl->pd; - pd->ibuf = NULL; - pd->lock = NULL; - pd->texture = NULL; + pd->ibuf = nullptr; + pd->lock = nullptr; + pd->texture = nullptr; } static void IMAGE_cache_init(void *ved) @@ -352,14 +354,14 @@ static void IMAGE_cache_init(void *ved) { /* Write depth is needed for background overlay rendering. Near depth is used for * transparency checker and Far depth is used for indicating the image size. */ - DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS | - DRW_STATE_BLEND_ALPHA_PREMUL; + DRWState state = static_cast(DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | + DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL); psl->image_pass = DRW_pass_create("Image", state); } const SpaceLink *space_link = draw_ctx->space_data; const char space_type = space_link->spacetype; - pd->view = NULL; + pd->view = nullptr; if (space_type == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; Image *image = ED_space_image(sima); @@ -372,15 +374,15 @@ static void IMAGE_cache_init(void *ved) ARegion *region = draw_ctx->region; Main *bmain = CTX_data_main(draw_ctx->evil_C); Image *image = BKE_image_ensure_viewer(bmain, IMA_TYPE_COMPOSITE, "Viewer Node"); - ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, &pd->lock); + ImBuf *ibuf = BKE_image_acquire_ibuf(image, nullptr, &pd->lock); { /* Setup a screen pixel view. The backdrop of the node editor doesn't follow the region. */ float winmat[4][4], viewmat[4][4]; orthographic_m4(viewmat, 0.0, region->winx, 0.0, region->winy, 0.0, 1.0); unit_m4(winmat); - pd->view = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); + pd->view = DRW_view_create(viewmat, winmat, nullptr, nullptr, nullptr); } - image_cache_image(vedata, image, NULL, ibuf); + image_cache_image(vedata, image, nullptr, ibuf); pd->image = image; pd->ibuf = ibuf; } @@ -405,14 +407,14 @@ static void image_draw_finish(IMAGE_Data *ved) else if (space_type == SPACE_NODE) { BKE_image_release_ibuf(pd->image, pd->ibuf, pd->lock); } - pd->image = NULL; - pd->ibuf = NULL; + pd->image = nullptr; + pd->ibuf = nullptr; if (pd->texture && pd->owns_texture) { GPU_texture_free(pd->texture); pd->owns_texture = false; } - pd->texture = NULL; + pd->texture = nullptr; } static void IMAGE_draw_scene(void *ved) @@ -428,11 +430,11 @@ static void IMAGE_draw_scene(void *ved) DRW_view_set_active(pd->view); DRW_draw_pass(psl->image_pass); - DRW_view_set_active(NULL); + DRW_view_set_active(nullptr); image_draw_finish(vedata); } -static void IMAGE_engine_free(void) +static void IMAGE_engine_free() { IMAGE_shader_free(); } @@ -441,19 +443,27 @@ static void IMAGE_engine_free(void) static const DrawEngineDataSize IMAGE_data_size = DRW_VIEWPORT_DATA_SIZE(IMAGE_Data); +} // namespace blender::draw::image_engine + +extern "C" { + +using namespace blender::draw::image_engine; + DrawEngineType draw_engine_image_type = { - NULL, /* next */ - NULL, /* prev */ + nullptr, /* next */ + nullptr, /* prev */ N_("UV/Image"), /* idname */ &IMAGE_data_size, /* vedata_size */ &IMAGE_engine_init, /* engine_init */ &IMAGE_engine_free, /* engine_free */ &IMAGE_cache_init, /* cache_init */ &IMAGE_cache_populate, /* cache_populate */ - NULL, /* cache_finish */ + nullptr, /* cache_finish */ &IMAGE_draw_scene, /* draw_scene */ - NULL, /* view_update */ - NULL, /* id_update */ - NULL, /* render_to_image */ - NULL, /* store_metadata */ + nullptr, /* view_update */ + nullptr, /* id_update */ + nullptr, /* render_to_image */ + nullptr, /* store_metadata */ }; +} + diff --git a/source/blender/draw/engines/image/image_engine.h b/source/blender/draw/engines/image/image_engine.h index 0098d863ef9..ba1e87088f1 100644 --- a/source/blender/draw/engines/image/image_engine.h +++ b/source/blender/draw/engines/image/image_engine.h @@ -22,4 +22,13 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + extern DrawEngineType draw_engine_image_type; + +#ifdef __cplusplus +} +#endif + diff --git a/source/blender/draw/engines/image/image_private.h b/source/blender/draw/engines/image/image_private.hh similarity index 84% rename from source/blender/draw/engines/image/image_private.h rename to source/blender/draw/engines/image/image_private.hh index 76a94e68da1..434e27eff65 100644 --- a/source/blender/draw/engines/image/image_private.h +++ b/source/blender/draw/engines/image/image_private.hh @@ -20,10 +20,6 @@ * \ingroup draw_engine */ -#ifdef __cplusplus -extern "C" { -#endif - /* Forward declarations */ struct GPUTexture; struct ImBuf; @@ -31,13 +27,15 @@ struct Image; /* *********** LISTS *********** */ +namespace blender::draw::image_engine { + /* GPUViewport.storage * Is freed every time the viewport engine changes. */ -typedef struct IMAGE_PassList { +struct IMAGE_PassList { DRWPass *image_pass; -} IMAGE_PassList; +}; -typedef struct IMAGE_PrivateData { +struct IMAGE_PrivateData { void *lock; struct ImBuf *ibuf; struct Image *image; @@ -45,25 +43,24 @@ typedef struct IMAGE_PrivateData { struct GPUTexture *texture; bool owns_texture; -} IMAGE_PrivateData; +}; -typedef struct IMAGE_StorageList { +struct IMAGE_StorageList { IMAGE_PrivateData *pd; -} IMAGE_StorageList; +}; -typedef struct IMAGE_Data { +struct IMAGE_Data { void *engine_type; DRWViewportEmptyList *fbl; DRWViewportEmptyList *txl; IMAGE_PassList *psl; IMAGE_StorageList *stl; -} IMAGE_Data; +}; /* image_shader.c */ GPUShader *IMAGE_shader_image_get(bool is_tiled_image); void IMAGE_shader_library_ensure(void); void IMAGE_shader_free(void); -#ifdef __cplusplus -} -#endif +} // namespace blender::draw::image_engine + diff --git a/source/blender/draw/engines/image/image_shader.c b/source/blender/draw/engines/image/image_shader.cc similarity index 83% rename from source/blender/draw/engines/image/image_shader.c rename to source/blender/draw/engines/image/image_shader.cc index 691c0d7029a..1c6abf36505 100644 --- a/source/blender/draw/engines/image/image_shader.c +++ b/source/blender/draw/engines/image/image_shader.cc @@ -27,27 +27,31 @@ #include "GPU_batch.h" #include "image_engine.h" -#include "image_private.h" +#include "image_private.hh" +extern "C" { extern char datatoc_common_colormanagement_lib_glsl[]; extern char datatoc_common_globals_lib_glsl[]; extern char datatoc_common_view_lib_glsl[]; extern char datatoc_engine_image_frag_glsl[]; extern char datatoc_engine_image_vert_glsl[]; +} -typedef struct IMAGE_Shaders { +namespace blender::draw::image_engine { + +struct IMAGE_Shaders { GPUShader *image_sh[2]; -} IMAGE_Shaders; +}; static struct { IMAGE_Shaders shaders; DRWShaderLibrary *lib; -} e_data = {{{0}}}; /* Engine data */ +} e_data = {{{nullptr}}}; /* Engine data */ -void IMAGE_shader_library_ensure(void) +void IMAGE_shader_library_ensure() { - if (e_data.lib == NULL) { + if (e_data.lib == nullptr) { e_data.lib = DRW_shader_library_create(); /* NOTE: These need to be ordered by dependencies. */ DRW_SHADER_LIB_ADD(e_data.lib, common_colormanagement_lib); @@ -60,18 +64,18 @@ GPUShader *IMAGE_shader_image_get(bool is_tiled_image) { const int index = is_tiled_image ? 1 : 0; IMAGE_Shaders *sh_data = &e_data.shaders; - if (!sh_data->image_sh[index]) { + if (sh_data->image_sh[index] == nullptr) { sh_data->image_sh[index] = DRW_shader_create_with_shaderlib( datatoc_engine_image_vert_glsl, - NULL, + nullptr, datatoc_engine_image_frag_glsl, e_data.lib, - is_tiled_image ? "#define TILED_IMAGE\n" : NULL); + is_tiled_image ? "#define TILED_IMAGE\n" : nullptr); } return sh_data->image_sh[index]; } -void IMAGE_shader_free(void) +void IMAGE_shader_free() { GPUShader **sh_data_as_array = (GPUShader **)&e_data.shaders; for (int i = 0; i < (sizeof(IMAGE_Shaders) / sizeof(GPUShader *)); i++) { @@ -80,3 +84,5 @@ void IMAGE_shader_free(void) DRW_SHADER_LIB_FREE_SAFE(e_data.lib); } + +} // namespace blender::draw::image_engine diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c index 5356700f156..b899767acc4 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_text.c +++ b/source/blender/draw/engines/overlay/overlay_edit_text.c @@ -22,7 +22,7 @@ #include "DRW_render.h" -#include "BKE_font.h" +#include "BKE_vfont.h" #include "DNA_curve_types.h" diff --git a/source/blender/draw/engines/overlay/overlay_edit_uv.c b/source/blender/draw/engines/overlay/overlay_edit_uv.c index 985f8a6785c..983df1ceac8 100644 --- a/source/blender/draw/engines/overlay/overlay_edit_uv.c +++ b/source/blender/draw/engines/overlay/overlay_edit_uv.c @@ -340,34 +340,42 @@ void OVERLAY_edit_uv_cache_init(OVERLAY_Data *vedata) if (pd->edit_uv.do_stencil_overlay) { const Brush *brush = BKE_paint_brush(&ts->imapaint.paint); - - DRW_PASS_CREATE(psl->edit_uv_stencil_ps, - DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | DRW_STATE_BLEND_ALPHA_PREMUL); - GPUShader *sh = OVERLAY_shader_edit_uv_stencil_image(); - GPUBatch *geom = DRW_cache_quad_get(); - DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_stencil_ps); Image *stencil_image = brush->clone.image; ImBuf *stencil_ibuf = BKE_image_acquire_ibuf(stencil_image, NULL, &pd->edit_uv.stencil_lock); - pd->edit_uv.stencil_ibuf = stencil_ibuf; - pd->edit_uv.stencil_image = stencil_image; - GPUTexture *stencil_texture = BKE_image_get_gpu_texture(stencil_image, NULL, stencil_ibuf); - DRW_shgroup_uniform_texture(grp, "imgTexture", stencil_texture); - DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", true); - DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true); - DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){1.0f, 1.0f, 1.0f, brush->clone.alpha}); - float size_image[2]; - BKE_image_get_size_fl(image, NULL, size_image); - float size_stencil_image[2] = {stencil_ibuf->x, stencil_ibuf->y}; + if (stencil_ibuf == NULL) { + pd->edit_uv.stencil_ibuf = NULL; + pd->edit_uv.stencil_image = NULL; + } + else { + DRW_PASS_CREATE(psl->edit_uv_stencil_ps, + DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_ALWAYS | + DRW_STATE_BLEND_ALPHA_PREMUL); + GPUShader *sh = OVERLAY_shader_edit_uv_stencil_image(); + GPUBatch *geom = DRW_cache_quad_get(); + DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->edit_uv_stencil_ps); + pd->edit_uv.stencil_ibuf = stencil_ibuf; + pd->edit_uv.stencil_image = stencil_image; + GPUTexture *stencil_texture = BKE_image_get_gpu_texture(stencil_image, NULL, stencil_ibuf); + DRW_shgroup_uniform_texture(grp, "imgTexture", stencil_texture); + DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", true); + DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true); + DRW_shgroup_uniform_vec4_copy( + grp, "color", (float[4]){1.0f, 1.0f, 1.0f, brush->clone.alpha}); - float obmat[4][4]; - unit_m4(obmat); - obmat[3][1] = brush->clone.offset[1]; - obmat[3][0] = brush->clone.offset[0]; - obmat[0][0] = size_stencil_image[0] / size_image[0]; - obmat[1][1] = size_stencil_image[1] / size_image[1]; + float size_image[2]; + BKE_image_get_size_fl(image, NULL, size_image); + float size_stencil_image[2] = {stencil_ibuf->x, stencil_ibuf->y}; - DRW_shgroup_call_obmat(grp, geom, obmat); + float obmat[4][4]; + unit_m4(obmat); + obmat[3][1] = brush->clone.offset[1]; + obmat[3][0] = brush->clone.offset[0]; + obmat[0][0] = size_stencil_image[0] / size_image[0]; + obmat[1][1] = size_stencil_image[1] / size_image[1]; + + DRW_shgroup_call_obmat(grp, geom, obmat); + } } else { pd->edit_uv.stencil_ibuf = NULL; diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c index b07e86000fd..2bdceb5f3db 100644 --- a/source/blender/draw/engines/overlay/overlay_engine.c +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -56,7 +56,7 @@ static void OVERLAY_engine_init(void *vedata) OVERLAY_shader_library_ensure(); if (!stl->pd) { - /* Alloc transient pointers */ + /* Allocate transient pointers. */ stl->pd = MEM_callocN(sizeof(*stl->pd), __func__); } @@ -235,7 +235,7 @@ BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bo *do_init = true; } else if ((*dupli_data)->base_flag != ob->base_flag) { - /* Select state might have change, reinit. */ + /* Select state might have change, reinitialize. */ *do_init = true; } return *dupli_data; @@ -463,9 +463,14 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) case OB_LIGHTPROBE: OVERLAY_lightprobe_cache_populate(vedata, ob); break; - case OB_LATTICE: - OVERLAY_lattice_cache_populate(vedata, ob); + case OB_LATTICE: { + /* Unlike the other types above, lattices actually have a bounding box defined, so hide the + * lattice wires if only the bounding-box is requested. */ + if (ob->dt > OB_BOUNDBOX) { + OVERLAY_lattice_cache_populate(vedata, ob); + } break; + } } } @@ -473,7 +478,7 @@ static void OVERLAY_cache_populate(void *vedata, Object *ob) OVERLAY_particle_cache_populate(vedata, ob); } - /* Relationship, object center, bounbox ... */ + /* Relationship, object center, bounding-box... etc. */ if (!pd->hide_overlays) { OVERLAY_extra_cache_populate(vedata, ob); } @@ -576,7 +581,7 @@ static void OVERLAY_draw_scene(void *vedata) OVERLAY_extra_blend_draw(vedata); OVERLAY_volume_draw(vedata); - /* These overlays are drawn here to avoid artifacts with wireframe opacity. */ + /* These overlays are drawn here to avoid artifacts with wire-frame opacity. */ switch (pd->ctx_mode) { case CTX_MODE_SCULPT: OVERLAY_sculpt_draw(vedata); diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c index 8f9db7f6f13..98db7136398 100644 --- a/source/blender/draw/engines/overlay/overlay_extra.c +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -696,7 +696,7 @@ void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Lightprobe +/** \name Light-probe * \{ */ void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob) diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c index 60cda9f2d61..4a551c4dec5 100644 --- a/source/blender/draw/engines/overlay/overlay_grid.c +++ b/source/blender/draw/engines/overlay/overlay_grid.c @@ -23,6 +23,7 @@ #include "DRW_render.h" #include "DNA_camera_types.h" +#include "DNA_screen_types.h" #include "DEG_depsgraph_query.h" @@ -46,6 +47,7 @@ enum { GRID_BACK = (1 << 9), GRID_CAMERA = (1 << 10), PLANE_IMAGE = (1 << 11), + CUSTOM_GRID = (1 << 12), }; void OVERLAY_grid_init(OVERLAY_Data *vedata) @@ -61,6 +63,7 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) if (pd->space_type == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; + View2D *v2d = &draw_ctx->region->v2d; if (sima->mode == SI_MODE_UV || !ED_space_image_has_buffer(sima)) { shd->grid_flag = GRID_BACK | PLANE_IMAGE | SHOW_GRID; } @@ -68,15 +71,21 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) shd->grid_flag = 0; } + if (sima->flag & SI_CUSTOM_GRID) { + shd->grid_flag |= CUSTOM_GRID; + } + shd->grid_distance = 1.0f; copy_v3_fl3(shd->grid_size, 1.0f, 1.0f, 1.0f); if (sima->mode == SI_MODE_UV) { shd->grid_size[0] = (float)sima->tile_grid_shape[0]; shd->grid_size[1] = (float)sima->tile_grid_shape[1]; } - for (int step = 0; step < 8; step++) { - shd->grid_steps[step] = powf(4, step) * (1.0f / 16.0f); - } + + const int grid_size = SI_GRID_STEPS_LEN; + shd->zoom_factor = ED_space_image_zoom_level(v2d, grid_size); + ED_space_image_grid_steps(sima, shd->grid_steps, grid_size); + return; } @@ -191,6 +200,15 @@ void OVERLAY_grid_init(OVERLAY_Data *vedata) shd->grid_distance = dist / 2.0f; ED_view3d_grid_steps(scene, v3d, rv3d, shd->grid_steps); + + if ((v3d->flag & (V3D_XR_SESSION_SURFACE | V3D_XR_SESSION_MIRROR)) != 0) { + /* The calculations for the grid parameters assume that the view matrix has no scale component, + * which may not be correct if the user is "shrunk" or "enlarged" by zooming in or out. + * Therefore, we need to compensate the values here. */ + float viewinvscale = len_v3( + viewinv[0]); /* Assumption is uniform scaling (all column vectors are of same length). */ + shd->grid_distance *= viewinvscale; + } } void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) @@ -248,6 +266,7 @@ void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) grp = DRW_shgroup_create(sh, psl->grid_ps); DRW_shgroup_uniform_int(grp, "gridFlag", &shd->grid_flag, 1); + DRW_shgroup_uniform_float_copy(grp, "zoomFactor", shd->zoom_factor); DRW_shgroup_uniform_vec3(grp, "planeAxes", shd->grid_axes, 1); DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.c index ada07499d1c..2e9a9bc5c64 100644 --- a/source/blender/draw/engines/overlay/overlay_image.c +++ b/source/blender/draw/engines/overlay/overlay_image.c @@ -344,7 +344,6 @@ void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) if (tex) { image_camera_background_matrix_get(cam, bgpic, draw_ctx, aspect, mat); - mul_m4_m4m4(mat, modelmat, mat); const bool is_foreground = (bgpic->flag & CAM_BGIMG_FLAG_FOREGROUND) != 0; /* Alpha is clamped just below 1.0 to fix background images to interfere with foreground * images. Without this a background image with 1.0 will be rendered on top of a transparent @@ -361,6 +360,7 @@ void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) DRW_shgroup_uniform_texture(grp, "imgTexture", tex); DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", use_alpha_premult); DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true); + DRW_shgroup_uniform_bool_copy(grp, "isCameraBackground", true); DRW_shgroup_uniform_bool_copy(grp, "depthSet", true); DRW_shgroup_uniform_vec4_copy(grp, "color", color_premult_alpha); DRW_shgroup_call_obmat(grp, DRW_cache_quad_get(), mat); @@ -446,6 +446,7 @@ void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) DRW_shgroup_uniform_texture(grp, "imgTexture", tex); DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", use_alpha_premult); DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", use_alpha_blend); + DRW_shgroup_uniform_bool_copy(grp, "isCameraBackground", false); DRW_shgroup_uniform_bool_copy(grp, "depthSet", depth_mode != OB_EMPTY_IMAGE_DEPTH_DEFAULT); DRW_shgroup_uniform_vec4_copy(grp, "color", ob->color); DRW_shgroup_call_obmat(grp, DRW_cache_quad_get(), mat); diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h index 23df571e8de..def278f98df 100644 --- a/source/blender/draw/engines/overlay/overlay_private.h +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -139,9 +139,10 @@ typedef struct OVERLAY_ShadingData { /** Grid */ float grid_axes[3], grid_distance; float zplane_axes[3], grid_size[3]; - float grid_steps[8]; + float grid_steps[SI_GRID_STEPS_LEN]; float inv_viewport_size[2]; float grid_line_size; + float zoom_factor; /* Only for UV editor */ int grid_flag; int zpos_flag; int zneg_flag; diff --git a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl index 3220adbff36..9feca644bd3 100644 --- a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl +++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl @@ -15,8 +15,9 @@ uniform float lineKernel = 0.0; uniform sampler2D depthBuffer; uniform int gridFlag; +uniform float zoomFactor; -#define STEPS_LEN 8 +#define STEPS_LEN 8 /* Match: #SI_GRID_STEPS_LEN */ uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0); #define AXIS_X (1 << 0) @@ -28,6 +29,8 @@ uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, #define PLANE_YZ (1 << 6) #define GRID_BACK (1 << 9) /* grid is behind objects */ #define GRID_CAMERA (1 << 10) /* In camera view */ +#define PLANE_IMAGE (1 << 11) /* UV/Image Image editor */ +#define CUSTOM_GRID (1 << 12) /* UV Editor only */ #define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */ @@ -122,9 +125,17 @@ void main() * would be more accurate, but not really necessary. */ float grid_res = dot(dFdxPos, screenVecs[0].xyz); - /* The gride begins to appear when it comprises 4 pixels */ + /* The grid begins to appear when it comprises 4 pixels */ grid_res *= 4; + /* For UV/Image editor use zoomFactor */ + if ((gridFlag & PLANE_IMAGE) != 0 && + /* Grid begins to appear when the length of one grid unit is at least + * (256/grid_size) pixels Value of grid_size defined in `overlay_grid.c`. */ + (gridFlag & CUSTOM_GRID) == 0) { + grid_res = zoomFactor; + } + /* from biggest to smallest */ vec4 scale; #if 0 diff --git a/source/blender/draw/engines/overlay/shaders/image_vert.glsl b/source/blender/draw/engines/overlay/shaders/image_vert.glsl index 621e1d8068b..a44ea7081ba 100644 --- a/source/blender/draw/engines/overlay/shaders/image_vert.glsl +++ b/source/blender/draw/engines/overlay/shaders/image_vert.glsl @@ -1,5 +1,6 @@ uniform bool depthSet; +uniform bool isCameraBackground; in vec3 pos; @@ -7,8 +8,14 @@ out vec2 uvs; void main() { - vec3 world_pos = point_object_to_world(pos); - gl_Position = point_world_to_ndc(world_pos); + if (isCameraBackground) { + vec3 vP = (ModelMatrix * vec4(pos, 1.0)).xyz; + gl_Position = point_view_to_ndc(vP); + } + else { + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + } if (depthSet) { /* Result in a position at 1.0 (far plane). Small epsilon to avoid precision issue. diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index 96ab8a28e09..20edd78597b 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -388,6 +388,8 @@ RenderEngineType DRW_engine_viewport_select_type = { NULL, NULL, NULL, + NULL, + NULL, &draw_engine_select_type, {NULL, NULL, NULL}, }; diff --git a/source/blender/draw/engines/workbench/workbench_engine.c b/source/blender/draw/engines/workbench/workbench_engine.c index 7e374ba016c..874a56fabf3 100644 --- a/source/blender/draw/engines/workbench/workbench_engine.c +++ b/source/blender/draw/engines/workbench/workbench_engine.c @@ -233,7 +233,7 @@ static void workbench_cache_hair_populate(WORKBENCH_PrivateData *wpd, workbench_image_hair_setup(wpd, ob, matnr, ima, NULL, state) : workbench_material_hair_setup(wpd, ob, matnr, color_type); - DRW_shgroup_hair_create_sub(ob, psys, md, grp); + DRW_shgroup_hair_create_sub(ob, psys, md, grp, NULL); } /** @@ -639,6 +639,8 @@ RenderEngineType DRW_engine_viewport_workbench_type = { NULL, NULL, NULL, + NULL, + NULL, &workbench_render_update_passes, &draw_engine_workbench, {NULL, NULL, NULL}, diff --git a/source/blender/draw/engines/workbench/workbench_render.c b/source/blender/draw/engines/workbench/workbench_render.c index f2d178f6565..125bee87614 100644 --- a/source/blender/draw/engines/workbench/workbench_render.c +++ b/source/blender/draw/engines/workbench/workbench_render.c @@ -185,7 +185,7 @@ void workbench_render(void *ved, RenderEngine *engine, RenderLayer *render_layer DRW_render_instance_buffer_finish(); - /* Also we weed to have a correct fbo bound for DRW_hair_update */ + /* Also we weed to have a correct FBO bound for #DRW_hair_update */ GPU_framebuffer_bind(dfbl->default_fb); DRW_hair_update(); diff --git a/source/blender/draw/intern/DRW_render.h b/source/blender/draw/intern/DRW_render.h index 660a4adaf51..c1cab931f6a 100644 --- a/source/blender/draw/intern/DRW_render.h +++ b/source/blender/draw/intern/DRW_render.h @@ -24,8 +24,6 @@ #pragma once -#include "DRW_engine_types.h" - #include "BLI_listbase.h" #include "BLI_math_matrix.h" #include "BLI_math_vector.h" @@ -56,6 +54,7 @@ #include "draw_debug.h" #include "draw_manager_profiling.h" +#include "draw_view_data.h" #include "MEM_guardedalloc.h" @@ -623,6 +622,7 @@ const DRWView *DRW_view_default_get(void); void DRW_view_default_set(DRWView *view); void DRW_view_reset(void); void DRW_view_set_active(DRWView *view); +const DRWView *DRW_view_get_active(void); void DRW_view_clip_planes_set(DRWView *view, float (*planes)[4], int plane_len); void DRW_view_camtexco_set(DRWView *view, float texco[4]); diff --git a/source/blender/draw/intern/draw_cache.c b/source/blender/draw/intern/draw_cache.c index a3a5d6b065a..7b66026f7ad 100644 --- a/source/blender/draw/intern/draw_cache.c +++ b/source/blender/draw/intern/draw_cache.c @@ -2431,9 +2431,9 @@ GPUBatch *DRW_cache_bone_stick_get(void) /* Bone rectangle */ pos[0] = 0.0f; for (int i = 0; i < 6; i++) { - pos[1] = (ELEM(i, 0, 3)) ? 0.0f : ((i < 3) ? 1.0f : -1.0f); - flag = ((i < 2 || i > 4) ? POS_HEAD : POS_TAIL) | ((i == 0 || i == 3) ? 0 : COL_WIRE) | - COL_BONE | POS_BONE; + pos[1] = ELEM(i, 0, 3) ? 0.0f : ((i < 3) ? 1.0f : -1.0f); + flag = ((i < 2 || i > 4) ? POS_HEAD : POS_TAIL) | (ELEM(i, 0, 3) ? 0 : COL_WIRE) | COL_BONE | + POS_BONE; GPU_vertbuf_attr_set(vbo, attr_id.pos, v, pos); GPU_vertbuf_attr_set(vbo, attr_id.flag, v, &flag); GPU_indexbuf_add_generic_vert(&elb, v++); diff --git a/source/blender/draw/intern/draw_cache_extract.h b/source/blender/draw/intern/draw_cache_extract.h index a680cc0d6b7..ba42cdf66e7 100644 --- a/source/blender/draw/intern/draw_cache_extract.h +++ b/source/blender/draw/intern/draw_cache_extract.h @@ -24,6 +24,10 @@ struct TaskGraph; +#include "DNA_customdata_types.h" + +#include "BKE_attribute.h" + #include "GPU_batch.h" #include "GPU_index_buffer.h" #include "GPU_vertex_buffer.h" @@ -56,7 +60,6 @@ typedef struct DRW_MeshCDMask { uint32_t uv : 8; uint32_t tan : 8; uint32_t vcol : 8; - uint32_t sculpt_vcol : 8; uint32_t orco : 1; uint32_t tan_orco : 1; uint32_t sculpt_overlays : 1; @@ -64,10 +67,10 @@ typedef struct DRW_MeshCDMask { * modifiers could remove it. (see T68857) */ uint32_t edit_uv : 1; } DRW_MeshCDMask; -/* Keep `DRW_MeshCDMask` struct within an `uint64_t`. +/* Keep `DRW_MeshCDMask` struct within an `uint32_t`. * bit-wise and atomic operations are used to compare and update the struct. * See `mesh_cd_layers_type_*` functions. */ -BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint64_t), "DRW_MeshCDMask exceeds 64 bits") +BLI_STATIC_ASSERT(sizeof(DRW_MeshCDMask) <= sizeof(uint32_t), "DRW_MeshCDMask exceeds 32 bits") typedef enum eMRIterType { MR_ITER_LOOPTRI = 1 << 0, MR_ITER_POLY = 1 << 1, @@ -76,6 +79,17 @@ typedef enum eMRIterType { } eMRIterType; ENUM_OPERATORS(eMRIterType, MR_ITER_LVERT) +typedef struct DRW_AttributeRequest { + CustomDataType cd_type; + int layer_index; + AttributeDomain domain; +} DRW_AttributeRequest; + +typedef struct DRW_MeshAttributes { + DRW_AttributeRequest requests[GPU_MAX_ATTR]; + int num_requests; +} DRW_MeshAttributes; + typedef enum eMRDataType { MR_DATA_NONE = 0, MR_DATA_POLY_NOR = 1 << 1, @@ -133,6 +147,7 @@ typedef struct MeshBufferList { GPUVertBuf *edge_idx; /* extend */ GPUVertBuf *poly_idx; GPUVertBuf *fdot_idx; + GPUVertBuf *attr[GPU_MAX_ATTR]; } vbo; /* Index Buffers: * Only need to be updated when topology changes. */ @@ -285,6 +300,8 @@ typedef struct MeshBatchCache { DRW_MeshCDMask cd_used, cd_needed, cd_used_over_time; + DRW_MeshAttributes attr_used, attr_needed, attr_used_over_time; + int lastmatch; /* Valid only if edge_detection is up to date. */ diff --git a/source/blender/draw/intern/draw_cache_extract_mesh.cc b/source/blender/draw/intern/draw_cache_extract_mesh.cc index 06c449fe590..f3b72503907 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh.cc @@ -650,6 +650,9 @@ static void mesh_buffer_cache_create_requested(struct TaskGraph *task_graph, EXTRACT_ADD_REQUESTED(vbo, vert_idx); EXTRACT_ADD_REQUESTED(vbo, fdot_idx); EXTRACT_ADD_REQUESTED(vbo, skin_roots); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + EXTRACT_ADD_REQUESTED(vbo, attr[i]); + } EXTRACT_ADD_REQUESTED(ibo, tris); if (DRW_ibo_requested(mbuflist->ibo.lines_loose)) { diff --git a/source/blender/draw/intern/draw_cache_impl_curve.cc b/source/blender/draw/intern/draw_cache_impl_curve.cc index 0804745fab5..1108d40125b 100644 --- a/source/blender/draw/intern/draw_cache_impl_curve.cc +++ b/source/blender/draw/intern/draw_cache_impl_curve.cc @@ -36,9 +36,9 @@ #include "BKE_curve.h" #include "BKE_displist.h" -#include "BKE_font.h" #include "BKE_geometry_set.hh" #include "BKE_spline.hh" +#include "BKE_vfont.h" #include "GPU_batch.h" #include "GPU_capabilities.h" @@ -342,6 +342,9 @@ static void curve_cd_calc_used_gpu_layers(CustomDataMask *cd_layers, case CD_ORCO: *cd_layers |= CD_MASK_ORCO; break; + case CD_HAIRLENGTH: + *cd_layers |= CD_MASK_HAIRLENGTH; + break; } } } diff --git a/source/blender/draw/intern/draw_cache_impl_displist.c b/source/blender/draw/intern/draw_cache_impl_displist.c index 1fed5d79697..fb8f998f036 100644 --- a/source/blender/draw/intern/draw_cache_impl_displist.c +++ b/source/blender/draw/intern/draw_cache_impl_displist.c @@ -372,10 +372,10 @@ static void surf_uv_quad(const DispList *dl, const uint quad[4], float r_uv[4][2 r_uv[i][1] = (quad[i] % dl->nr) / (float)orco_sizeu; /* cyclic correction */ - if ((i == 1 || i == 2) && r_uv[i][0] == 0.0f) { + if (ELEM(i, 1, 2) && r_uv[i][0] == 0.0f) { r_uv[i][0] = 1.0f; } - if ((i == 0 || i == 1) && r_uv[i][1] == 0.0f) { + if (ELEM(i, 0, 1) && r_uv[i][1] == 0.0f) { r_uv[i][1] = 1.0f; } } diff --git a/source/blender/draw/intern/draw_cache_impl_hair.c b/source/blender/draw/intern/draw_cache_impl_hair.c index 6424b21666d..41a0cca8a8f 100644 --- a/source/blender/draw/intern/draw_cache_impl_hair.c +++ b/source/blender/draw/intern/draw_cache_impl_hair.c @@ -27,6 +27,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_listbase.h" #include "BLI_math_base.h" #include "BLI_math_vector.h" #include "BLI_utildefines.h" @@ -37,6 +38,7 @@ #include "BKE_hair.h" #include "GPU_batch.h" +#include "GPU_material.h" #include "GPU_texture.h" #include "draw_cache_impl.h" /* own include */ @@ -141,7 +143,9 @@ static void ensure_seg_pt_count(Hair *hair, ParticleHairCache *hair_cache) } } -static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *attr_step) +static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, + GPUVertBufRaw *attr_step, + GPUVertBufRaw *length_step) { /* TODO: use hair radius layer if available. */ HairCurve *curve = hair->curves; @@ -162,6 +166,8 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a seg_data[3] = total_len; co_prev = curve_co[j]; } + /* Assign length value*/ + *(float *)GPU_vertbuf_raw_step(length_step) = total_len; if (total_len > 0.0f) { /* Divide by total length to have a [0-1] number. */ for (int j = 0; j < curve->numpoints; j++, seg_data_first += 4) { @@ -171,28 +177,48 @@ static void hair_batch_cache_fill_segments_proc_pos(Hair *hair, GPUVertBufRaw *a } } -static void hair_batch_cache_ensure_procedural_pos(Hair *hair, ParticleHairCache *cache) +static void hair_batch_cache_ensure_procedural_pos(Hair *hair, + ParticleHairCache *cache, + GPUMaterial *gpu_material) { - if (cache->proc_point_buf != NULL) { - return; + if (cache->proc_point_buf == NULL) { + /* initialize vertex format */ + GPUVertFormat format = {0}; + uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + cache->proc_point_buf = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); + + GPUVertBufRaw point_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &point_step); + + GPUVertFormat length_format = {0}; + uint length_id = GPU_vertformat_attr_add( + &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + + cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format); + GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len); + + GPUVertBufRaw length_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step); + + hair_batch_cache_fill_segments_proc_pos(hair, &point_step, &length_step); + + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(cache->proc_point_buf); + cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf); } - /* initialize vertex format */ - GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - cache->proc_point_buf = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); - - GPUVertBufRaw pos_step; - GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step); - - hair_batch_cache_fill_segments_proc_pos(hair, &pos_step); - - /* Create vbo immediately to bind to texture buffer. */ - GPU_vertbuf_use(cache->proc_point_buf); - - cache->point_tex = GPU_texture_create_from_vertbuf("hair_point", cache->proc_point_buf); + if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex) { + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) { + if (attr->type == CD_HAIRLENGTH) { + GPU_vertbuf_use(cache->proc_length_buf); + cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf); + break; + } + } + } } static void hair_batch_cache_fill_strands_data(Hair *hair, @@ -310,6 +336,7 @@ static void hair_batch_cache_ensure_procedural_indices(Hair *hair, /* Ensure all textures and buffers needed for GPU accelerated drawing. */ bool hair_ensure_procedural_data(Object *object, ParticleHairCache **r_hair_cache, + GPUMaterial *gpu_material, int subdiv, int thickness_res) { @@ -325,7 +352,7 @@ bool hair_ensure_procedural_data(Object *object, /* Refreshed on combing and simulation. */ if ((*r_hair_cache)->proc_point_buf == NULL) { ensure_seg_pt_count(hair, &cache->hair); - hair_batch_cache_ensure_procedural_pos(hair, &cache->hair); + hair_batch_cache_ensure_procedural_pos(hair, &cache->hair, gpu_material); need_ft_update = true; } diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index f36d3ab21f9..41475851292 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -41,6 +41,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "BKE_attribute.h" #include "BKE_customdata.h" #include "BKE_deform.h" #include "BKE_editmesh.h" @@ -121,6 +122,8 @@ # define _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5) _MDEPS_ASSERT5(b, n1, n2, n3, n4); _MDEPS_ASSERT2(b, n5) # define _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6) _MDEPS_ASSERT6(b, n1, n2, n3, n4, n5); _MDEPS_ASSERT2(b, n6) # define _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7) _MDEPS_ASSERT7(b, n1, n2, n3, n4, n5, n6); _MDEPS_ASSERT2(b, n7) +# define _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20) _MDEPS_ASSERT8(b, n1, n2, n3, n4, n5, n6, n7); _MDEPS_ASSERT8(b, n8, n9, n10, n11, n12, n13, n14); _MDEPS_ASSERT7(b, n15, n16, n17, n18, n19, n20) +# define _MDEPS_ASSERT22(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20, n21) _MDEPS_ASSERT21(b, n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12, n13, n14, n15, n16, n17, n18, n19, n20); _MDEPS_ASSERT2(b, n21); # define MDEPS_ASSERT_FLAG(...) VA_NARGS_CALL_OVERLOAD(_MDEPS_ASSERT, __VA_ARGS__) # define MDEPS_ASSERT(batch_name, ...) MDEPS_ASSERT_FLAG(BATCH_FLAG(batch_name), __VA_ARGS__) @@ -192,6 +195,21 @@ static const DRWBatchFlag g_buffer_deps[] = { [BUFFER_INDEX(vbo.edge_idx)] = BATCH_FLAG(edit_selection_edges), [BUFFER_INDEX(vbo.poly_idx)] = BATCH_FLAG(edit_selection_faces), [BUFFER_INDEX(vbo.fdot_idx)] = BATCH_FLAG(edit_selection_fdots), + [BUFFER_INDEX(vbo.attr) + 0] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 1] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 2] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 3] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 4] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 5] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 6] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 7] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 8] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 9] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 10] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 11] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 12] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 13] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, + [BUFFER_INDEX(vbo.attr) + 14] = BATCH_FLAG(surface) | SURFACE_PER_MAT_FLAG, [BUFFER_INDEX(ibo.tris)] = BATCH_FLAG(surface, surface_weights, @@ -240,12 +258,12 @@ static void mesh_batch_cache_discard_batch(MeshBatchCache *cache, const DRWBatch /* Return true is all layers in _b_ are inside _a_. */ BLI_INLINE bool mesh_cd_layers_type_overlap(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return (*((uint64_t *)&a) & *((uint64_t *)&b)) == *((uint64_t *)&b); + return (*((uint32_t *)&a) & *((uint32_t *)&b)) == *((uint32_t *)&b); } BLI_INLINE bool mesh_cd_layers_type_equal(DRW_MeshCDMask a, DRW_MeshCDMask b) { - return *((uint64_t *)&a) == *((uint64_t *)&b); + return *((uint32_t *)&a) == *((uint32_t *)&b); } BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) @@ -253,12 +271,11 @@ BLI_INLINE void mesh_cd_layers_type_merge(DRW_MeshCDMask *a, DRW_MeshCDMask b) uint32_t *a_p = (uint32_t *)a; uint32_t *b_p = (uint32_t *)&b; atomic_fetch_and_or_uint32(a_p, *b_p); - atomic_fetch_and_or_uint32(a_p + 1, *(b_p + 1)); } BLI_INLINE void mesh_cd_layers_type_clear(DRW_MeshCDMask *a) { - *((uint64_t *)a) = 0; + *((uint32_t *)a) = 0; } BLI_INLINE const Mesh *editmesh_final_or_this(const Mesh *me) @@ -271,6 +288,95 @@ static void mesh_cd_calc_edit_uv_layer(const Mesh *UNUSED(me), DRW_MeshCDMask *c cd_used->edit_uv = 1; } +/** \name DRW_MeshAttributes + * + * Utilities for handling requested attributes. + * \{ */ + +/* Return true if the given DRW_AttributeRequest is already in the requests. */ +static bool has_request(const DRW_MeshAttributes *requests, DRW_AttributeRequest req) +{ + for (int i = 0; i < requests->num_requests; i++) { + const DRW_AttributeRequest src_req = requests->requests[i]; + if (src_req.domain != req.domain) { + continue; + } + if (src_req.layer_index != req.layer_index) { + continue; + } + if (src_req.cd_type != req.cd_type) { + continue; + } + return true; + } + return false; +} + +static void mesh_attrs_merge_requests(const DRW_MeshAttributes *src_requests, + DRW_MeshAttributes *dst_requests) +{ + for (int i = 0; i < src_requests->num_requests; i++) { + if (dst_requests->num_requests == GPU_MAX_ATTR) { + return; + } + + if (has_request(dst_requests, src_requests->requests[i])) { + continue; + } + + dst_requests->requests[dst_requests->num_requests] = src_requests->requests[i]; + dst_requests->num_requests += 1; + } +} + +static void drw_mesh_attributes_clear(DRW_MeshAttributes *attributes) +{ + memset(attributes, 0, sizeof(DRW_MeshAttributes)); +} + +static void drw_mesh_attributes_merge(DRW_MeshAttributes *dst, + const DRW_MeshAttributes *src, + ThreadMutex *mesh_render_mutex) +{ + BLI_mutex_lock(mesh_render_mutex); + mesh_attrs_merge_requests(src, dst); + BLI_mutex_unlock(mesh_render_mutex); +} + +/* Return true if all requests in b are in a. */ +static bool drw_mesh_attributes_overlap(DRW_MeshAttributes *a, DRW_MeshAttributes *b) +{ + if (a->num_requests != b->num_requests) { + return false; + } + + for (int i = 0; i < a->num_requests; i++) { + if (!has_request(a, b->requests[i])) { + return false; + } + } + + return true; +} + +static void drw_mesh_attributes_add_request(DRW_MeshAttributes *attrs, + CustomDataType type, + int layer, + AttributeDomain domain) +{ + if (attrs->num_requests >= GPU_MAX_ATTR) { + return; + } + + DRW_AttributeRequest *req = &attrs->requests[attrs->num_requests]; + req->cd_type = type; + req->layer_index = layer; + req->domain = domain; + attrs->num_requests += 1; +} + +/** \} */ + BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -286,6 +392,36 @@ BLI_INLINE const CustomData *mesh_cd_ldata_get_from_mesh(const Mesh *me) return &me->ldata; } +BLI_INLINE const CustomData *mesh_cd_pdata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->pdata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->pdata; + break; + } + + BLI_assert(0); + return &me->pdata; +} + +BLI_INLINE const CustomData *mesh_cd_edata_get_from_mesh(const Mesh *me) +{ + switch ((eMeshWrapperType)me->runtime.wrapper_type) { + case ME_WRAPPER_TYPE_MDATA: + return &me->edata; + break; + case ME_WRAPPER_TYPE_BMESH: + return &me->edit_mesh->bm->edata; + break; + } + + BLI_assert(0); + return &me->edata; +} + BLI_INLINE const CustomData *mesh_cd_vdata_get_from_mesh(const Mesh *me) { switch ((eMeshWrapperType)me->runtime.wrapper_type) { @@ -321,14 +457,14 @@ static void mesh_cd_calc_active_mask_uv_layer(const Mesh *me, DRW_MeshCDMask *cd } } -static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshCDMask *cd_used) +static void mesh_cd_calc_active_vcol_layer(const Mesh *me, DRW_MeshAttributes *attrs_used) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); int layer = CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR); if (layer != -1) { - cd_used->sculpt_vcol |= (1 << layer); + drw_mesh_attributes_add_request(attrs_used, CD_PROP_COLOR, layer, ATTR_DOMAIN_POINT); } } @@ -343,13 +479,45 @@ static void mesh_cd_calc_active_mloopcol_layer(const Mesh *me, DRW_MeshCDMask *c } } +static bool custom_data_match_attribute(const CustomData *custom_data, + const char *name, + int *r_layer_index, + int *r_type) +{ + const int possible_attribute_types[6] = { + CD_PROP_BOOL, + CD_PROP_INT32, + CD_PROP_FLOAT, + CD_PROP_FLOAT2, + CD_PROP_FLOAT3, + CD_PROP_COLOR, + }; + + for (int i = 0; i < ARRAY_SIZE(possible_attribute_types); i++) { + const int attr_type = possible_attribute_types[i]; + int layer_index = CustomData_get_named_layer(custom_data, attr_type, name); + if (layer_index == -1) { + continue; + } + + *r_layer_index = layer_index; + *r_type = attr_type; + return true; + } + + return false; +} + static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, struct GPUMaterial **gpumat_array, - int gpumat_array_len) + int gpumat_array_len, + DRW_MeshAttributes *attributes) { const Mesh *me_final = editmesh_final_or_this(me); const CustomData *cd_ldata = mesh_cd_ldata_get_from_mesh(me_final); + const CustomData *cd_pdata = mesh_cd_pdata_get_from_mesh(me_final); const CustomData *cd_vdata = mesh_cd_vdata_get_from_mesh(me_final); + const CustomData *cd_edata = mesh_cd_edata_get_from_mesh(me_final); /* See: DM_vertex_attributes_from_gpu for similar logic */ DRW_MeshCDMask cd_used; @@ -363,6 +531,8 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, const char *name = gpu_attr->name; int type = gpu_attr->type; int layer = -1; + /* ATTR_DOMAIN_NUM is standard for "invalid value". */ + AttributeDomain domain = ATTR_DOMAIN_NUM; if (type == CD_AUTO_FROM_NAME) { /* We need to deduct what exact layer is used. @@ -389,6 +559,27 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, type = CD_TANGENT; } #endif + if (layer == -1) { + /* Try to match a generic attribute, we use the first attribute domain with a + * matching name. */ + if (custom_data_match_attribute(cd_vdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_POINT; + } + else if (custom_data_match_attribute(cd_ldata, name, &layer, &type)) { + domain = ATTR_DOMAIN_CORNER; + } + else if (custom_data_match_attribute(cd_pdata, name, &layer, &type)) { + domain = ATTR_DOMAIN_FACE; + } + else if (custom_data_match_attribute(cd_edata, name, &layer, &type)) { + domain = ATTR_DOMAIN_EDGE; + } + else { + layer = -1; + domain = ATTR_DOMAIN_NUM; + } + } + if (layer == -1) { continue; } @@ -430,31 +621,6 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, } break; } - case CD_PROP_COLOR: { - /* Sculpt Vertex Colors */ - bool use_mloop_cols = false; - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name) : - CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR); - /* Fallback to Vertex Color data */ - if (layer == -1) { - layer = (name[0] != '\0') ? - CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name) : - CustomData_get_render_layer(cd_ldata, CD_MLOOPCOL); - use_mloop_cols = true; - } - } - if (layer != -1) { - if (use_mloop_cols) { - cd_used.vcol |= (1 << layer); - } - else { - cd_used.sculpt_vcol |= (1 << layer); - } - } - break; - } case CD_MCOL: { /* Vertex Color Data */ if (layer == -1) { @@ -471,6 +637,17 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, cd_used.orco = 1; break; } + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: + case CD_PROP_FLOAT2: + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + if (layer != -1 && domain != ATTR_DOMAIN_NUM) { + drw_mesh_attributes_add_request(attributes, type, layer, domain); + } + break; + } } } } @@ -933,14 +1110,14 @@ static void texpaint_request_active_vcol(MeshBatchCache *cache, Mesh *me) static void sculpt_request_active_vcol(MeshBatchCache *cache, Mesh *me) { - DRW_MeshCDMask cd_needed; - mesh_cd_layers_type_clear(&cd_needed); - mesh_cd_calc_active_vcol_layer(me, &cd_needed); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + mesh_cd_calc_active_vcol_layer(me, &attrs_needed); - BLI_assert(cd_needed.sculpt_vcol != 0 && + BLI_assert(attrs_needed.num_requests != 0 && "No MPropCol layer available in Sculpt, but batches requested anyway!"); - mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, me->runtime.render_mutex); } GPUBatch *DRW_mesh_batch_cache_get_all_verts(Mesh *me) @@ -1013,11 +1190,16 @@ GPUBatch **DRW_mesh_batch_cache_get_surface_shaded(Mesh *me, uint gpumat_array_len) { MeshBatchCache *cache = mesh_batch_cache_get(me); - DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers(me, gpumat_array, gpumat_array_len); + DRW_MeshAttributes attrs_needed; + drw_mesh_attributes_clear(&attrs_needed); + DRW_MeshCDMask cd_needed = mesh_cd_calc_used_gpu_layers( + me, gpumat_array, gpumat_array_len, &attrs_needed); BLI_assert(gpumat_array_len == cache->mat_len); mesh_cd_layers_type_merge(&cache->cd_needed, cd_needed); + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + drw_mesh_attributes_merge(&cache->attr_needed, &attrs_needed, mesh_render_mutex); mesh_batch_cache_request_surface_batches(cache); return cache->surface_per_mat; } @@ -1294,11 +1476,25 @@ void DRW_mesh_batch_cache_free_old(Mesh *me, int ctime) cache->lastmatch = ctime; } + if (drw_mesh_attributes_overlap(&cache->attr_used_over_time, &cache->attr_used)) { + cache->lastmatch = ctime; + } + if (ctime - cache->lastmatch > U.vbotimeout) { mesh_batch_cache_discard_shaded_tri(cache); } mesh_cd_layers_type_clear(&cache->cd_used_over_time); + drw_mesh_attributes_clear(&cache->attr_used_over_time); +} + +static void drw_add_attributes_vbo(GPUBatch *batch, + MeshBufferList *mbuflist, + DRW_MeshAttributes *attr_used) +{ + for (int i = 0; i < attr_used->num_requests; i++) { + DRW_vbo_request(batch, &mbuflist->vbo.attr[i]); + } } #ifdef DEBUG @@ -1407,12 +1603,15 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } } + ThreadMutex *mesh_render_mutex = (ThreadMutex *)me->runtime.render_mutex; + /* Verify that all surface batches have needed attribute layers. */ /* TODO(fclem): We could be a bit smarter here and only do it per * material. */ bool cd_overlap = mesh_cd_layers_type_overlap(cache->cd_used, cache->cd_needed); - if (cd_overlap == false) { + bool attr_overlap = drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed); + if (cd_overlap == false || attr_overlap == false) { FOREACH_MESH_BUFFER_CACHE (cache, mbc) { if ((cache->cd_used.uv & cache->cd_needed.uv) != cache->cd_needed.uv) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.uv); @@ -1428,11 +1627,14 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.sculpt_overlays != cache->cd_needed.sculpt_overlays) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.sculpt_data); } - if (((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) || - ((cache->cd_used.sculpt_vcol & cache->cd_needed.sculpt_vcol) != - cache->cd_needed.sculpt_vcol)) { + if ((cache->cd_used.vcol & cache->cd_needed.vcol) != cache->cd_needed.vcol) { GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.vcol); } + if (!drw_mesh_attributes_overlap(&cache->attr_used, &cache->attr_needed)) { + for (int i = 0; i < GPU_MAX_ATTR; i++) { + GPU_VERTBUF_DISCARD_SAFE(mbc->buff.vbo.attr[i]); + } + } } /* We can't discard batches at this point as they have been * referenced for drawing. Just clear them in place. */ @@ -1443,9 +1645,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, cache->batch_ready &= ~(MBC_SURFACE); mesh_cd_layers_type_merge(&cache->cd_used, cache->cd_needed); + drw_mesh_attributes_merge(&cache->attr_used, &cache->attr_needed, mesh_render_mutex); } mesh_cd_layers_type_merge(&cache->cd_used_over_time, cache->cd_needed); mesh_cd_layers_type_clear(&cache->cd_needed); + + drw_mesh_attributes_merge(&cache->attr_used_over_time, &cache->attr_needed, mesh_render_mutex); + drw_mesh_attributes_clear(&cache->attr_needed); } if (batch_requested & MBC_EDITUV) { @@ -1504,7 +1710,27 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MeshBufferList *mbuflist = &cache->final.buff; /* Initialize batches and request VBO's & IBO's. */ - MDEPS_ASSERT(surface, ibo.tris, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.vcol); + MDEPS_ASSERT(surface, + ibo.tris, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.vcol, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); if (DRW_batch_requested(cache->batch.surface, GPU_PRIM_TRIS)) { DRW_ibo_request(cache->batch.surface, &mbuflist->ibo.tris); /* Order matters. First ones override latest VBO's attributes. */ @@ -1513,9 +1739,10 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if (cache->cd_used.uv != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.uv); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->batch.surface, &mbuflist->vbo.vcol); } + drw_add_attributes_vbo(cache->batch.surface, mbuflist, &cache->attr_used); } MDEPS_ASSERT(all_verts, vbo.pos_nor); if (DRW_batch_requested(cache->batch.all_verts, GPU_PRIM_POINTS)) { @@ -1578,8 +1805,28 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, } /* Per Material */ - MDEPS_ASSERT_FLAG( - SURFACE_PER_MAT_FLAG, vbo.lnor, vbo.pos_nor, vbo.uv, vbo.tan, vbo.vcol, vbo.orco); + MDEPS_ASSERT_FLAG(SURFACE_PER_MAT_FLAG, + vbo.lnor, + vbo.pos_nor, + vbo.uv, + vbo.tan, + vbo.vcol, + vbo.orco, + vbo.attr[0], + vbo.attr[1], + vbo.attr[2], + vbo.attr[3], + vbo.attr[4], + vbo.attr[5], + vbo.attr[6], + vbo.attr[7], + vbo.attr[8], + vbo.attr[9], + vbo.attr[10], + vbo.attr[11], + vbo.attr[12], + vbo.attr[13], + vbo.attr[14]); MDEPS_ASSERT_INDEX(TRIS_PER_MAT_INDEX, SURFACE_PER_MAT_FLAG); for (int i = 0; i < cache->mat_len; i++) { if (DRW_batch_requested(cache->surface_per_mat[i], GPU_PRIM_TRIS)) { @@ -1593,12 +1840,13 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, if ((cache->cd_used.tan != 0) || (cache->cd_used.tan_orco != 0)) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.tan); } - if (cache->cd_used.vcol != 0 || cache->cd_used.sculpt_vcol != 0) { + if (cache->cd_used.vcol != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.vcol); } if (cache->cd_used.orco != 0) { DRW_vbo_request(cache->surface_per_mat[i], &mbuflist->vbo.orco); } + drw_add_attributes_vbo(cache->surface_per_mat[i], mbuflist, &cache->attr_used); } } @@ -1749,6 +1997,9 @@ void DRW_mesh_batch_cache_create_requested(struct TaskGraph *task_graph, MDEPS_ASSERT_MAP(vbo.edituv_stretch_angle); MDEPS_ASSERT_MAP(vbo.fdots_uv); MDEPS_ASSERT_MAP(vbo.fdots_edituv_data); + for (int i = 0; i < GPU_MAX_ATTR; i++) { + MDEPS_ASSERT_MAP(vbo.attr[i]); + } MDEPS_ASSERT_MAP(ibo.tris); MDEPS_ASSERT_MAP(ibo.lines); diff --git a/source/blender/draw/intern/draw_cache_impl_particles.c b/source/blender/draw/intern/draw_cache_impl_particles.c index 5c51f24a435..96bdca7d935 100644 --- a/source/blender/draw/intern/draw_cache_impl_particles.c +++ b/source/blender/draw/intern/draw_cache_impl_particles.c @@ -46,6 +46,7 @@ #include "ED_particle.h" #include "GPU_batch.h" +#include "GPU_material.h" #include "DEG_depsgraph_query.h" @@ -183,7 +184,9 @@ void particle_batch_cache_clear_hair(ParticleHairCache *hair_cache) { /* TODO: more granular update tagging. */ GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_point_buf); + GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_length_buf); DRW_TEXTURE_FREE_SAFE(hair_cache->point_tex); + DRW_TEXTURE_FREE_SAFE(hair_cache->length_tex); GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_buf); GPU_VERTBUF_DISCARD_SAFE(hair_cache->proc_strand_seg_buf); @@ -609,7 +612,8 @@ static int particle_batch_cache_fill_segments(ParticleSystem *psys, static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_cache, const int num_path_keys, - GPUVertBufRaw *attr_step) + GPUVertBufRaw *attr_step, + GPUVertBufRaw *length_step) { for (int i = 0; i < num_path_keys; i++) { ParticleCacheKey *path = path_cache[i]; @@ -630,6 +634,8 @@ static void particle_batch_cache_fill_segments_proc_pos(ParticleCacheKey **path_ seg_data[3] = total_len; co_prev = path[j].co; } + /* Assign length value*/ + *(float *)GPU_vertbuf_raw_step(length_step) = total_len; if (total_len > 0.0f) { /* Divide by total length to have a [0-1] number. */ for (int j = 0; j <= path->segments; j++, seg_data_first += 4) { @@ -1079,40 +1085,64 @@ static void particle_batch_cache_ensure_procedural_indices(PTCacheEdit *edit, static void particle_batch_cache_ensure_procedural_pos(PTCacheEdit *edit, ParticleSystem *psys, - ParticleHairCache *cache) + ParticleHairCache *cache, + GPUMaterial *gpu_material) { - if (cache->proc_point_buf != NULL) { - return; - } + if (cache->proc_point_buf == NULL) { + /* initialize vertex format */ + GPUVertFormat pos_format = {0}; + uint pos_id = GPU_vertformat_attr_add( + &pos_format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - /* initialize vertex format */ - GPUVertFormat format = {0}; - uint pos_id = GPU_vertformat_attr_add(&format, "posTime", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + cache->proc_point_buf = GPU_vertbuf_create_with_format(&pos_format); + GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); - cache->proc_point_buf = GPU_vertbuf_create_with_format(&format); - GPU_vertbuf_data_alloc(cache->proc_point_buf, cache->point_len); + GPUVertBufRaw pos_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step); - GPUVertBufRaw pos_step; - GPU_vertbuf_attr_get_raw_data(cache->proc_point_buf, pos_id, &pos_step); + GPUVertFormat length_format = {0}; + uint length_id = GPU_vertformat_attr_add( + &length_format, "hairLength", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); - if (edit != NULL && edit->pathcache != NULL) { - particle_batch_cache_fill_segments_proc_pos(edit->pathcache, edit->totcached, &pos_step); - } - else { - if ((psys->pathcache != NULL) && - (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) { - particle_batch_cache_fill_segments_proc_pos(psys->pathcache, psys->totpart, &pos_step); + cache->proc_length_buf = GPU_vertbuf_create_with_format(&length_format); + GPU_vertbuf_data_alloc(cache->proc_length_buf, cache->strands_len); + + GPUVertBufRaw length_step; + GPU_vertbuf_attr_get_raw_data(cache->proc_length_buf, length_id, &length_step); + + if (edit != NULL && edit->pathcache != NULL) { + particle_batch_cache_fill_segments_proc_pos( + edit->pathcache, edit->totcached, &pos_step, &length_step); } - if (psys->childcache) { - const int child_count = psys->totchild * psys->part->disp / 100; - particle_batch_cache_fill_segments_proc_pos(psys->childcache, child_count, &pos_step); + else { + if ((psys->pathcache != NULL) && + (!psys->childcache || (psys->part->draw & PART_DRAW_PARENT))) { + particle_batch_cache_fill_segments_proc_pos( + psys->pathcache, psys->totpart, &pos_step, &length_step); + } + if (psys->childcache) { + const int child_count = psys->totchild * psys->part->disp / 100; + particle_batch_cache_fill_segments_proc_pos( + psys->childcache, child_count, &pos_step, &length_step); + } + } + + /* Create vbo immediately to bind to texture buffer. */ + GPU_vertbuf_use(cache->proc_point_buf); + cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf); + } + + /* Checking hair length separately, only allocating gpu memory when needed. */ + if (gpu_material && cache->proc_length_buf != NULL && cache->length_tex == NULL) { + ListBase gpu_attrs = GPU_material_attributes(gpu_material); + LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &gpu_attrs) { + if (attr->type == CD_HAIRLENGTH) { + GPU_vertbuf_use(cache->proc_length_buf); + cache->length_tex = GPU_texture_create_from_vertbuf("hair_length", cache->proc_length_buf); + break; + } } } - - /* Create vbo immediately to bind to texture buffer. */ - GPU_vertbuf_use(cache->proc_point_buf); - - cache->point_tex = GPU_texture_create_from_vertbuf("part_point", cache->proc_point_buf); } static void particle_batch_cache_ensure_pos_and_seg(PTCacheEdit *edit, @@ -1649,6 +1679,7 @@ bool particles_ensure_procedural_data(Object *object, ParticleSystem *psys, ModifierData *md, ParticleHairCache **r_hair_cache, + GPUMaterial *gpu_material, int subdiv, int thickness_res) { @@ -1666,9 +1697,11 @@ bool particles_ensure_procedural_data(Object *object, (*r_hair_cache)->final[subdiv].strands_res = 1 << (part->draw_step + subdiv); /* Refreshed on combing and simulation. */ - if ((*r_hair_cache)->proc_point_buf == NULL) { + if ((*r_hair_cache)->proc_point_buf == NULL || + (gpu_material && (*r_hair_cache)->length_tex == NULL)) { ensure_seg_pt_count(source.edit, source.psys, &cache->hair); - particle_batch_cache_ensure_procedural_pos(source.edit, source.psys, &cache->hair); + particle_batch_cache_ensure_procedural_pos( + source.edit, source.psys, &cache->hair, gpu_material); need_ft_update = true; } diff --git a/source/blender/draw/intern/draw_color_management.cc b/source/blender/draw/intern/draw_color_management.cc index 23fa18c83c5..70035c7c6f8 100644 --- a/source/blender/draw/intern/draw_color_management.cc +++ b/source/blender/draw/intern/draw_color_management.cc @@ -30,6 +30,7 @@ #include "GPU_texture.h" #include "DNA_space_types.h" +#include "DNA_view3d_types.h" #include "BKE_colortools.h" @@ -60,14 +61,8 @@ static eDRWColorManagementType drw_color_management_type_for_v3d(const Scene &sc { const bool use_workbench = BKE_scene_uses_blender_workbench(&scene); - const bool use_scene_lights = ((v3d.shading.type == OB_MATERIAL) && - (v3d.shading.flag & V3D_SHADING_SCENE_LIGHTS)) || - ((v3d.shading.type == OB_RENDER) && - (v3d.shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER)); - const bool use_scene_world = ((v3d.shading.type == OB_MATERIAL) && - (v3d.shading.flag & V3D_SHADING_SCENE_WORLD)) || - ((v3d.shading.type == OB_RENDER) && - (v3d.shading.flag & V3D_SHADING_SCENE_WORLD_RENDER)); + const bool use_scene_lights = V3D_USES_SCENE_LIGHTS(&v3d); + const bool use_scene_world = V3D_USES_SCENE_WORLD(&v3d); if ((use_workbench && v3d.shading.type == OB_RENDER) || use_scene_lights || use_scene_world) { return eDRWColorManagementType::UseRenderSettings; diff --git a/source/blender/draw/intern/draw_common.h b/source/blender/draw/intern/draw_common.h index 1eaf2bee236..2913877c9c1 100644 --- a/source/blender/draw/intern/draw_common.h +++ b/source/blender/draw/intern/draw_common.h @@ -29,6 +29,7 @@ struct Object; struct ParticleSystem; struct RegionView3D; struct ViewLayer; +struct GPUMaterial; #define UBO_FIRST_COLOR colorWire #define UBO_LAST_COLOR colorUVShadow @@ -175,7 +176,8 @@ bool DRW_object_axis_orthogonal_to_view(struct Object *ob, int axis); struct DRWShadingGroup *DRW_shgroup_hair_create_sub(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, - struct DRWShadingGroup *shgrp); + struct DRWShadingGroup *shgrp, + struct GPUMaterial *gpu_material); struct GPUVertBuf *DRW_hair_pos_buffer_get(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md); diff --git a/source/blender/draw/intern/draw_hair.c b/source/blender/draw/intern/draw_hair.c index c2e25389091..5c7eb083fc9 100644 --- a/source/blender/draw/intern/draw_hair.c +++ b/source/blender/draw/intern/draw_hair.c @@ -38,6 +38,7 @@ #include "GPU_batch.h" #include "GPU_capabilities.h" #include "GPU_compute.h" +#include "GPU_material.h" #include "GPU_shader.h" #include "GPU_texture.h" #include "GPU_vertex_buffer.h" @@ -172,18 +173,23 @@ static void drw_hair_particle_cache_update_transform_feedback(ParticleHairCache } } -static ParticleHairCache *drw_hair_particle_cache_get( - Object *object, ParticleSystem *psys, ModifierData *md, int subdiv, int thickness_res) +static ParticleHairCache *drw_hair_particle_cache_get(Object *object, + ParticleSystem *psys, + ModifierData *md, + GPUMaterial *gpu_material, + int subdiv, + int thickness_res) { bool update; ParticleHairCache *cache; if (psys) { /* Old particle hair. */ - update = particles_ensure_procedural_data(object, psys, md, &cache, subdiv, thickness_res); + update = particles_ensure_procedural_data( + object, psys, md, &cache, gpu_material, subdiv, thickness_res); } else { /* New hair object. */ - update = hair_ensure_procedural_data(object, &cache, subdiv, thickness_res); + update = hair_ensure_procedural_data(object, &cache, gpu_material, subdiv, thickness_res); } if (update) { @@ -206,7 +212,8 @@ GPUVertBuf *DRW_hair_pos_buffer_get(Object *object, ParticleSystem *psys, Modifi int subdiv = scene->r.hair_subdiv; int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; - ParticleHairCache *cache = drw_hair_particle_cache_get(object, psys, md, subdiv, thickness_res); + ParticleHairCache *cache = drw_hair_particle_cache_get( + object, psys, md, NULL, subdiv, thickness_res); return cache->final[subdiv].proc_buf; } @@ -248,7 +255,8 @@ void DRW_hair_duplimat_get(Object *object, DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, ParticleSystem *psys, ModifierData *md, - DRWShadingGroup *shgrp_parent) + DRWShadingGroup *shgrp_parent, + GPUMaterial *gpu_material) { const DRWContextState *draw_ctx = DRW_context_state_get(); Scene *scene = draw_ctx->scene; @@ -258,7 +266,7 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, int thickness_res = (scene->r.hair_type == SCE_HAIR_SHAPE_STRAND) ? 1 : 2; ParticleHairCache *hair_cache = drw_hair_particle_cache_get( - object, psys, md, subdiv, thickness_res); + object, psys, md, gpu_material, subdiv, thickness_res); DRWShadingGroup *shgrp = DRW_shgroup_create_sub(shgrp_parent); @@ -308,6 +316,9 @@ DRWShadingGroup *DRW_shgroup_hair_create_sub(Object *object, } DRW_shgroup_uniform_texture(shgrp, "hairPointBuffer", hair_cache->final[subdiv].proc_tex); + if (hair_cache->length_tex) { + DRW_shgroup_uniform_texture(shgrp, "hairLen", hair_cache->length_tex); + } DRW_shgroup_uniform_int(shgrp, "hairStrandsRes", &hair_cache->final[subdiv].strands_res, 1); DRW_shgroup_uniform_int_copy(shgrp, "hairThicknessRes", thickness_res); DRW_shgroup_uniform_float_copy(shgrp, "hairRadShape", hair_rad_shape); diff --git a/source/blender/draw/intern/draw_hair_private.h b/source/blender/draw/intern/draw_hair_private.h index 1f58d8d0ead..289a1690fc6 100644 --- a/source/blender/draw/intern/draw_hair_private.h +++ b/source/blender/draw/intern/draw_hair_private.h @@ -66,6 +66,10 @@ typedef struct ParticleHairCache { GPUVertBuf *proc_strand_buf; GPUTexture *strand_tex; + /* Hair Length */ + GPUVertBuf *proc_length_buf; + GPUTexture *length_tex; + GPUVertBuf *proc_strand_seg_buf; GPUTexture *strand_seg_tex; @@ -93,11 +97,13 @@ bool particles_ensure_procedural_data(struct Object *object, struct ParticleSystem *psys, struct ModifierData *md, struct ParticleHairCache **r_hair_cache, + struct GPUMaterial *gpu_material, int subdiv, int thickness_res); bool hair_ensure_procedural_data(struct Object *object, struct ParticleHairCache **r_hair_cache, + struct GPUMaterial *gpu_material, int subdiv, int thickness_res); diff --git a/source/blender/draw/intern/draw_instance_data.c b/source/blender/draw/intern/draw_instance_data.c index e055192eb21..29112ee4788 100644 --- a/source/blender/draw/intern/draw_instance_data.c +++ b/source/blender/draw/intern/draw_instance_data.c @@ -364,6 +364,8 @@ void DRW_instance_data_list_free(DRWInstanceDataList *idatalist) BLI_memblock_destroy(idatalist->pool_batching, (MemblockValFreeFP)temp_batch_free); BLI_remlink(&g_idatalists, idatalist); + + MEM_freeN(idatalist); } void DRW_instance_data_list_reset(DRWInstanceDataList *idatalist) diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 47adc0acc60..1d9bc607590 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -51,6 +51,7 @@ #include "BKE_pbvh.h" #include "BKE_pointcache.h" #include "BKE_pointcloud.h" +#include "BKE_screen.h" #include "BKE_volume.h" #include "DNA_camera_types.h" @@ -84,10 +85,12 @@ #include "wm_window.h" #include "draw_color_management.h" +#include "draw_manager.h" #include "draw_manager_profiling.h" #include "draw_manager_testing.h" #include "draw_manager_text.h" #include "draw_shader.h" +#include "draw_texture_pool.h" /* only for callbacks */ #include "draw_cache_impl.h" @@ -111,7 +114,10 @@ /** Render State: No persistent data between draw calls. */ DRWManager DST = {NULL}; -static ListBase DRW_engines = {NULL, NULL}; +static struct { + ListBase /*DRWRegisteredDrawEngine*/ engines; + int len; +} g_registered_engines = {{NULL}}; static void drw_state_prepare_clean_for_draw(DRWManager *dst) { @@ -315,35 +321,6 @@ struct DupliObject *DRW_object_get_dupli(const Object *UNUSED(ob)) /** \name Viewport (DRW_viewport) * \{ */ -void *drw_viewport_engine_data_ensure(void *engine_type) -{ - void *data = GPU_viewport_engine_data_get(DST.viewport, engine_type); - - if (data == NULL) { - data = GPU_viewport_engine_data_create(DST.viewport, engine_type); - } - return data; -} - -void DRW_engine_viewport_data_size_get( - const void *engine_type_v, int *r_fbl_len, int *r_txl_len, int *r_psl_len, int *r_stl_len) -{ - const DrawEngineType *engine_type = engine_type_v; - - if (r_fbl_len) { - *r_fbl_len = engine_type->vedata_size->fbl_len; - } - if (r_txl_len) { - *r_txl_len = engine_type->vedata_size->txl_len; - } - if (r_psl_len) { - *r_psl_len = engine_type->vedata_size->psl_len; - } - if (r_stl_len) { - *r_stl_len = engine_type->vedata_size->stl_len; - } -} - /* WARNING: only use for custom pipeline. 99% of the time, you don't want to use this. */ void DRW_render_viewport_size_set(const int size[2]) { @@ -373,39 +350,6 @@ const float *DRW_viewport_pixelsize_get(void) return &DST.pixsize; } -static void drw_viewport_cache_resize(void) -{ - /* Release the memiter before clearing the mempools that references them */ - GPU_viewport_cache_release(DST.viewport); - - if (DST.vmempool != NULL) { - /* Release Image textures. */ - BLI_memblock_iter iter; - GPUTexture **tex; - BLI_memblock_iternew(DST.vmempool->images, &iter); - while ((tex = BLI_memblock_iterstep(&iter))) { - GPU_texture_free(*tex); - } - - BLI_memblock_clear(DST.vmempool->commands, NULL); - BLI_memblock_clear(DST.vmempool->commands_small, NULL); - BLI_memblock_clear(DST.vmempool->callbuffers, NULL); - BLI_memblock_clear(DST.vmempool->obmats, NULL); - BLI_memblock_clear(DST.vmempool->obinfos, NULL); - BLI_memblock_clear(DST.vmempool->cullstates, NULL); - BLI_memblock_clear(DST.vmempool->shgroups, NULL); - BLI_memblock_clear(DST.vmempool->uniforms, NULL); - BLI_memblock_clear(DST.vmempool->passes, NULL); - BLI_memblock_clear(DST.vmempool->views, NULL); - BLI_memblock_clear(DST.vmempool->images, NULL); - - DRW_uniform_attrs_pool_clear_all(DST.vmempool->obattrs_ubo_pool); - } - - DRW_instance_data_list_free_unused(DST.idatalist); - DRW_instance_data_list_resize(DST.idatalist); -} - /* Not a viewport variable, we could split this out. */ static void drw_context_state_init(void) { @@ -465,107 +409,212 @@ static void draw_unit_state_create(void) DRW_handle_increment(&DST.resource_handle); } -/* It also stores viewport variable to an immutable place: DST - * This is because a cache uniform only store reference - * to its value. And we don't want to invalidate the cache - * if this value change per viewport */ -static void drw_viewport_var_init(void) +DRWData *DRW_viewport_data_create(void) { - RegionView3D *rv3d = DST.draw_ctx.rv3d; - ARegion *region = DST.draw_ctx.region; + DRWData *drw_data = MEM_callocN(sizeof(DRWData), "DRWData"); - /* Refresh DST.size */ - if (DST.viewport) { - int size[2]; - GPU_viewport_size_get(DST.viewport, size); - DST.size[0] = size[0]; - DST.size[1] = size[1]; - DST.inv_size[0] = 1.0f / size[0]; - DST.inv_size[1] = 1.0f / size[1]; + drw_data->texture_pool = DRW_texture_pool_create(); - DefaultFramebufferList *fbl = (DefaultFramebufferList *)GPU_viewport_framebuffer_list_get( - DST.viewport); - DST.default_framebuffer = fbl->default_fb; + drw_data->idatalist = DRW_instance_data_list_create(); - DST.vmempool = GPU_viewport_mempool_get(DST.viewport); + drw_data->commands = BLI_memblock_create(sizeof(DRWCommandChunk)); + drw_data->commands_small = BLI_memblock_create(sizeof(DRWCommandSmallChunk)); + drw_data->callbuffers = BLI_memblock_create(sizeof(DRWCallBuffer)); + drw_data->shgroups = BLI_memblock_create(sizeof(DRWShadingGroup)); + drw_data->uniforms = BLI_memblock_create(sizeof(DRWUniformChunk)); + drw_data->views = BLI_memblock_create(sizeof(DRWView)); + drw_data->images = BLI_memblock_create(sizeof(GPUTexture *)); + drw_data->obattrs_ubo_pool = DRW_uniform_attrs_pool_new(); + { + uint chunk_len = sizeof(DRWObjectMatrix) * DRW_RESOURCE_CHUNK_LEN; + drw_data->obmats = BLI_memblock_create_ex(sizeof(DRWObjectMatrix), chunk_len); + } + { + uint chunk_len = sizeof(DRWObjectInfos) * DRW_RESOURCE_CHUNK_LEN; + drw_data->obinfos = BLI_memblock_create_ex(sizeof(DRWObjectInfos), chunk_len); + } + { + uint chunk_len = sizeof(DRWCullingState) * DRW_RESOURCE_CHUNK_LEN; + drw_data->cullstates = BLI_memblock_create_ex(sizeof(DRWCullingState), chunk_len); + } + { + uint chunk_len = sizeof(DRWPass) * DRW_RESOURCE_CHUNK_LEN; + drw_data->passes = BLI_memblock_create_ex(sizeof(DRWPass), chunk_len); + } - if (DST.vmempool->commands == NULL) { - DST.vmempool->commands = BLI_memblock_create(sizeof(DRWCommandChunk)); - } - if (DST.vmempool->commands_small == NULL) { - DST.vmempool->commands_small = BLI_memblock_create(sizeof(DRWCommandSmallChunk)); - } - if (DST.vmempool->callbuffers == NULL) { - DST.vmempool->callbuffers = BLI_memblock_create(sizeof(DRWCallBuffer)); - } - if (DST.vmempool->obmats == NULL) { - uint chunk_len = sizeof(DRWObjectMatrix) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->obmats = BLI_memblock_create_ex(sizeof(DRWObjectMatrix), chunk_len); - } - if (DST.vmempool->obinfos == NULL) { - uint chunk_len = sizeof(DRWObjectInfos) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->obinfos = BLI_memblock_create_ex(sizeof(DRWObjectInfos), chunk_len); - } - if (DST.vmempool->cullstates == NULL) { - uint chunk_len = sizeof(DRWCullingState) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->cullstates = BLI_memblock_create_ex(sizeof(DRWCullingState), chunk_len); - } - if (DST.vmempool->shgroups == NULL) { - DST.vmempool->shgroups = BLI_memblock_create(sizeof(DRWShadingGroup)); - } - if (DST.vmempool->uniforms == NULL) { - DST.vmempool->uniforms = BLI_memblock_create(sizeof(DRWUniformChunk)); - } - if (DST.vmempool->views == NULL) { - DST.vmempool->views = BLI_memblock_create(sizeof(DRWView)); - } - if (DST.vmempool->passes == NULL) { - uint chunk_len = sizeof(DRWPass) * DRW_RESOURCE_CHUNK_LEN; - DST.vmempool->passes = BLI_memblock_create_ex(sizeof(DRWPass), chunk_len); - } - if (DST.vmempool->images == NULL) { - DST.vmempool->images = BLI_memblock_create(sizeof(GPUTexture *)); - } - if (DST.vmempool->obattrs_ubo_pool == NULL) { - DST.vmempool->obattrs_ubo_pool = DRW_uniform_attrs_pool_new(); - } + for (int i = 0; i < 2; i++) { + drw_data->view_data[i] = DRW_view_data_create(&g_registered_engines.engines); + } + return drw_data; +} - DST.resource_handle = 0; - DST.pass_handle = 0; +/* Reduce ref count of the textures used by a viewport. */ +static void draw_texture_release(DRWData *drw_data) +{ + /* Release Image textures. */ + BLI_memblock_iter iter; + GPUTexture **tex; + BLI_memblock_iternew(drw_data->images, &iter); + while ((tex = BLI_memblock_iterstep(&iter))) { + GPU_texture_free(*tex); + } +} - draw_unit_state_create(); +static void drw_viewport_data_reset(DRWData *drw_data) +{ + draw_texture_release(drw_data); - DST.idatalist = GPU_viewport_instance_data_list_get(DST.viewport); - DRW_instance_data_list_reset(DST.idatalist); + BLI_memblock_clear(drw_data->commands, NULL); + BLI_memblock_clear(drw_data->commands_small, NULL); + BLI_memblock_clear(drw_data->callbuffers, NULL); + BLI_memblock_clear(drw_data->obmats, NULL); + BLI_memblock_clear(drw_data->obinfos, NULL); + BLI_memblock_clear(drw_data->cullstates, NULL); + BLI_memblock_clear(drw_data->shgroups, NULL); + BLI_memblock_clear(drw_data->uniforms, NULL); + BLI_memblock_clear(drw_data->passes, NULL); + BLI_memblock_clear(drw_data->views, NULL); + BLI_memblock_clear(drw_data->images, NULL); + DRW_uniform_attrs_pool_clear_all(drw_data->obattrs_ubo_pool); + DRW_instance_data_list_free_unused(drw_data->idatalist); + DRW_instance_data_list_resize(drw_data->idatalist); + DRW_instance_data_list_reset(drw_data->idatalist); + DRW_texture_pool_reset(drw_data->texture_pool); +} + +void DRW_viewport_data_free(DRWData *drw_data) +{ + draw_texture_release(drw_data); + + BLI_memblock_destroy(drw_data->commands, NULL); + BLI_memblock_destroy(drw_data->commands_small, NULL); + BLI_memblock_destroy(drw_data->callbuffers, NULL); + BLI_memblock_destroy(drw_data->obmats, NULL); + BLI_memblock_destroy(drw_data->obinfos, NULL); + BLI_memblock_destroy(drw_data->cullstates, NULL); + BLI_memblock_destroy(drw_data->shgroups, NULL); + BLI_memblock_destroy(drw_data->uniforms, NULL); + BLI_memblock_destroy(drw_data->views, NULL); + BLI_memblock_destroy(drw_data->passes, NULL); + BLI_memblock_destroy(drw_data->images, NULL); + DRW_uniform_attrs_pool_free(drw_data->obattrs_ubo_pool); + DRW_instance_data_list_free(drw_data->idatalist); + DRW_texture_pool_free(drw_data->texture_pool); + for (int i = 0; i < 2; i++) { + DRW_view_data_free(drw_data->view_data[i]); + } + if (drw_data->matrices_ubo != NULL) { + for (int i = 0; i < drw_data->ubo_len; i++) { + GPU_uniformbuf_free(drw_data->matrices_ubo[i]); + GPU_uniformbuf_free(drw_data->obinfos_ubo[i]); + } + MEM_freeN(drw_data->matrices_ubo); + MEM_freeN(drw_data->obinfos_ubo); + } + MEM_freeN(drw_data); +} + +static DRWData *drw_viewport_data_ensure(GPUViewport *viewport) +{ + DRWData **vmempool_p = GPU_viewport_data_get(viewport); + DRWData *vmempool = *vmempool_p; + + if (vmempool == NULL) { + *vmempool_p = vmempool = DRW_viewport_data_create(); + } + return vmempool; +} + +/** + * Sets DST.viewport, DST.size and a lot of other important variables. + * Needs to be called before enabling any draw engine. + * - viewport can be NULL. In this case the data will not be stored and will be free at + * drw_manager_exit(). + * - size can be NULL to get it from viewport. + * - if viewport and size are NULL, size is set to (1, 1). + * + * Important: drw_manager_init can be called multiple times before drw_manager_exit. + */ +static void drw_manager_init(DRWManager *dst, GPUViewport *viewport, const int size[2]) +{ + RegionView3D *rv3d = dst->draw_ctx.rv3d; + ARegion *region = dst->draw_ctx.region; + + int view = (viewport) ? GPU_viewport_active_view_get(viewport) : 0; + + if (!dst->viewport && dst->vmempool) { + /* Manager was init first without a viewport, created DRWData, but is being re-init. + * In this case, keep the old data. */ + /* If it is being re-init with a valid viewport, it means there is something wrong. */ + BLI_assert(viewport == NULL); + } + else if (viewport) { + /* Use viewport's persistent DRWData. */ + dst->vmempool = drw_viewport_data_ensure(viewport); } else { - DST.size[0] = 0; - DST.size[1] = 0; - - DST.inv_size[0] = 0; - DST.inv_size[1] = 0; - - DST.default_framebuffer = NULL; - DST.vmempool = NULL; + /* Create temporary DRWData. Freed in drw_manager_exit(). */ + dst->vmempool = DRW_viewport_data_create(); } - DST.primary_view_ct = 0; + dst->viewport = viewport; + dst->view_data_active = dst->vmempool->view_data[view]; + dst->resource_handle = 0; + dst->pass_handle = 0; + dst->primary_view_ct = 0; + + drw_viewport_data_reset(dst->vmempool); + + bool do_validation = true; + if (size == NULL && viewport == NULL) { + /* Avoid division by 0. Engines will either override this or not use it. */ + dst->size[0] = 1.0f; + dst->size[1] = 1.0f; + } + else if (size == NULL) { + BLI_assert(viewport); + GPUTexture *tex = GPU_viewport_color_texture(viewport, 0); + dst->size[0] = GPU_texture_width(tex); + dst->size[1] = GPU_texture_height(tex); + } + else { + BLI_assert(size); + dst->size[0] = size[0]; + dst->size[1] = size[1]; + /* Fix case when used in DRW_cache_restart(). */ + do_validation = false; + } + dst->inv_size[0] = 1.0f / dst->size[0]; + dst->inv_size[1] = 1.0f / dst->size[1]; + + if (do_validation) { + DRW_view_data_texture_list_size_validate(dst->view_data_active, (int[2]){UNPACK2(dst->size)}); + } + + if (viewport) { + DRW_view_data_default_lists_from_viewport(dst->view_data_active, viewport); + } + + DefaultFramebufferList *dfbl = DRW_view_data_default_framebuffer_list_get(dst->view_data_active); + dst->default_framebuffer = dfbl->default_fb; + + draw_unit_state_create(); if (rv3d != NULL) { - normalize_v3_v3(DST.screenvecs[0], rv3d->viewinv[0]); - normalize_v3_v3(DST.screenvecs[1], rv3d->viewinv[1]); + normalize_v3_v3(dst->screenvecs[0], rv3d->viewinv[0]); + normalize_v3_v3(dst->screenvecs[1], rv3d->viewinv[1]); - DST.pixsize = rv3d->pixsize; - DST.view_default = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL); - DRW_view_camtexco_set(DST.view_default, rv3d->viewcamtexcofac); + dst->pixsize = rv3d->pixsize; + dst->view_default = DRW_view_create(rv3d->viewmat, rv3d->winmat, NULL, NULL, NULL); + DRW_view_camtexco_set(dst->view_default, rv3d->viewcamtexcofac); - if (DST.draw_ctx.sh_cfg == GPU_SHADER_CFG_CLIPPED) { + if (dst->draw_ctx.sh_cfg == GPU_SHADER_CFG_CLIPPED) { int plane_len = (RV3D_LOCK_FLAGS(rv3d) & RV3D_BOXCLIP) ? 4 : 6; - DRW_view_clip_planes_set(DST.view_default, rv3d->clip, plane_len); + DRW_view_clip_planes_set(dst->view_default, rv3d->clip, plane_len); } - DST.view_active = DST.view_default; - DST.view_previous = NULL; + dst->view_active = dst->view_default; + dst->view_previous = NULL; } else if (region) { View2D *v2d = ®ion->v2d; @@ -581,49 +630,64 @@ static void drw_viewport_var_init(void) winmat[3][0] = -1.0f; winmat[3][1] = -1.0f; - DST.view_default = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); - DST.view_active = DST.view_default; - DST.view_previous = NULL; + dst->view_default = DRW_view_create(viewmat, winmat, NULL, NULL, NULL); + dst->view_active = dst->view_default; + dst->view_previous = NULL; } else { - zero_v3(DST.screenvecs[0]); - zero_v3(DST.screenvecs[1]); + zero_v3(dst->screenvecs[0]); + zero_v3(dst->screenvecs[1]); - DST.pixsize = 1.0f; - DST.view_default = NULL; - DST.view_active = NULL; - DST.view_previous = NULL; + dst->pixsize = 1.0f; + dst->view_default = NULL; + dst->view_active = NULL; + dst->view_previous = NULL; } /* fclem: Is this still needed ? */ - if (DST.draw_ctx.object_edit && rv3d) { - ED_view3d_init_mats_rv3d(DST.draw_ctx.object_edit, rv3d); + if (dst->draw_ctx.object_edit && rv3d) { + ED_view3d_init_mats_rv3d(dst->draw_ctx.object_edit, rv3d); } if (G_draw.view_ubo == NULL) { G_draw.view_ubo = GPU_uniformbuf_create_ex(sizeof(DRWViewUboStorage), NULL, "G_draw.view_ubo"); } - if (DST.draw_list == NULL) { - DST.draw_list = GPU_draw_list_create(DRW_DRAWLIST_LEN); + if (dst->draw_list == NULL) { + dst->draw_list = GPU_draw_list_create(DRW_DRAWLIST_LEN); } - memset(DST.object_instance_data, 0x0, sizeof(DST.object_instance_data)); + memset(dst->object_instance_data, 0x0, sizeof(dst->object_instance_data)); +} + +static void drw_manager_exit(DRWManager *dst) +{ + if (dst->vmempool != NULL && dst->viewport == NULL) { + DRW_viewport_data_free(dst->vmempool); + } + dst->vmempool = NULL; + dst->viewport = NULL; +#ifdef DEBUG + /* Avoid accidental reuse. */ + drw_state_ensure_not_reused(dst); +#endif } DefaultFramebufferList *DRW_viewport_framebuffer_list_get(void) { - return GPU_viewport_framebuffer_list_get(DST.viewport); + return DRW_view_data_default_framebuffer_list_get(DST.view_data_active); } DefaultTextureList *DRW_viewport_texture_list_get(void) { - return GPU_viewport_texture_list_get(DST.viewport); + return DRW_view_data_default_texture_list_get(DST.view_data_active); } void DRW_viewport_request_redraw(void) { - GPU_viewport_tag_update(DST.viewport); + if (DST.viewport) { + GPU_viewport_tag_update(DST.viewport); + } } /** \} */ @@ -671,7 +735,7 @@ static void drw_duplidata_load(Object *ob) void **value; if (!BLI_ghash_ensure_p(DST.dupli_ghash, key, &value)) { - *value = MEM_callocN(sizeof(void *) * DST.enabled_engine_count, __func__); + *value = MEM_callocN(sizeof(void *) * g_registered_engines.len, __func__); /* TODO: Meh a bit out of place but this is nice as it is * only done once per instance type. */ @@ -686,7 +750,7 @@ static void drw_duplidata_load(Object *ob) static void duplidata_value_free(void *val) { void **dupli_datas = val; - for (int i = 0; i < DST.enabled_engine_count; i++) { + for (int i = 0; i < g_registered_engines.len; i++) { MEM_SAFE_FREE(dupli_datas[i]); } MEM_freeN(val); @@ -720,13 +784,9 @@ void **DRW_duplidata_get(void *vedata) if (DST.dupli_source == NULL) { return NULL; } - /* XXX Search engine index by using vedata array */ - for (int i = 0; i < DST.enabled_engine_count; i++) { - if (DST.vedata_array[i] == vedata) { - return &DST.dupli_datas[i]; - } - } - return NULL; + ViewportEngineData *ved = (ViewportEngineData *)vedata; + DRWRegisteredDrawEngine *engine_type = (DRWRegisteredDrawEngine *)ved->engine_type; + return &DST.dupli_datas[engine_type->index]; } /** \} */ @@ -873,7 +933,7 @@ DrawData *DRW_drawdata_ensure(ID *id, size_t fsize = size / sizeof(float); BLI_assert(fsize < MAX_INSTANCE_DATA_SIZE); if (DST.object_instance_data[fsize] == NULL) { - DST.object_instance_data[fsize] = DRW_instance_data_request(DST.idatalist, fsize); + DST.object_instance_data[fsize] = DRW_instance_data_request(DST.vmempool->idatalist, fsize); } dd = (DrawData *)DRW_instance_data_next(DST.object_instance_data[fsize]); memset(dd, 0, size); @@ -967,11 +1027,12 @@ void DRW_cache_free_old_batches(Main *bmain) static void drw_engines_init(void) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { PROFILE_START(stime); + const DrawEngineDataSize *data_size = engine->vedata_size; + memset(data->psl->passes, 0, sizeof(*data->psl->passes) * data_size->psl_len); + if (engine->engine_init) { engine->engine_init(data); } @@ -982,15 +1043,7 @@ static void drw_engines_init(void) static void drw_engines_cache_init(void) { - DST.enabled_engine_count = BLI_listbase_count(&DST.enabled_engines); - DST.vedata_array = MEM_mallocN(sizeof(void *) * DST.enabled_engine_count, __func__); - - int i = 0; - for (LinkData *link = DST.enabled_engines.first; link; link = link->next, i++) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); - DST.vedata_array[i] = data; - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (data->text_draw_cache) { DRW_text_cache_destroy(data->text_draw_cache); data->text_draw_cache = NULL; @@ -1011,10 +1064,7 @@ static void drw_engines_world_update(Scene *scene) return; } - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (engine->id_update) { engine->id_update(data, &scene->world->id); } @@ -1036,11 +1086,7 @@ static void drw_engines_cache_populate(Object *ob) drw_batch_cache_validate(ob); } - int i = 0; - for (LinkData *link = DST.enabled_engines.first; link; link = link->next, i++) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = DST.vedata_array[i]; - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (engine->id_update) { engine->id_update(data, &ob->id); } @@ -1063,25 +1109,17 @@ static void drw_engines_cache_populate(Object *ob) static void drw_engines_cache_finish(void) { - int i = 0; - for (LinkData *link = DST.enabled_engines.first; link; link = link->next, i++) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = DST.vedata_array[i]; - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (engine->cache_finish) { engine->cache_finish(data); } } - MEM_freeN(DST.vedata_array); } static void drw_engines_draw_scene(void) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { PROFILE_START(stime); - if (engine->draw_scene) { DRW_stats_group_start(engine->idname); engine->draw_scene(data); @@ -1091,7 +1129,6 @@ static void drw_engines_draw_scene(void) } DRW_stats_group_end(); } - PROFILE_END_UPDATE(data->render_time, stime); } /* Reset state after drawing */ @@ -1100,9 +1137,7 @@ static void drw_engines_draw_scene(void) static void drw_engines_draw_text(void) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { PROFILE_START(stime); if (data->text_draw_cache) { @@ -1116,10 +1151,7 @@ static void drw_engines_draw_text(void) /* Draw render engine info. */ void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height) { - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { if (data->info[0] != '\0') { char *chr_current = data->info; char *chr_start = chr_current; @@ -1158,12 +1190,10 @@ void DRW_draw_region_engine_info(int xoffset, int *yoffset, int line_height) static void use_drw_engine(DrawEngineType *engine) { - LinkData *ld = MEM_callocN(sizeof(LinkData), "enabled engine link data"); - ld->data = engine; - BLI_addtail(&DST.enabled_engines, ld); + DRW_view_data_use_engine(DST.view_data_active, engine); } -/* Gather all draw engines needed and store them in DST.enabled_engines +/* Gather all draw engines needed and store them in DST.view_data_active * That also define the rendering order of engines */ static void drw_engines_enable_from_engine(const RenderEngineType *engine_type, eDrawType drawtype) { @@ -1197,6 +1227,18 @@ static void drw_engines_enable_basic(void) use_drw_engine(&draw_engine_basic_type); } +static void drw_engine_enable_image_editor(void) +{ + if (DRW_engine_external_acquire_for_image_editor()) { + use_drw_engine(&draw_engine_external_type); + } + else { + use_drw_engine(&draw_engine_image_type); + } + + use_drw_engine(&draw_engine_overlay_type); +} + static void drw_engines_enable_editors(void) { SpaceLink *space_data = DST.draw_ctx.space_data; @@ -1205,8 +1247,7 @@ static void drw_engines_enable_editors(void) } if (space_data->spacetype == SPACE_IMAGE) { - use_drw_engine(&draw_engine_image_type); - use_drw_engine(&draw_engine_overlay_type); + drw_engine_enable_image_editor(); } else if (space_data->spacetype == SPACE_NODE) { /* Only enable when drawing the space image backdrop. */ @@ -1241,22 +1282,12 @@ static void drw_engines_enable(ViewLayer *UNUSED(view_layer), static void drw_engines_disable(void) { - BLI_freelistN(&DST.enabled_engines); + DRW_view_data_reset(DST.view_data_active); } static void drw_engines_data_validate(void) { - int enabled_engines = BLI_listbase_count(&DST.enabled_engines); - void **engine_handle_array = BLI_array_alloca(engine_handle_array, enabled_engines + 1); - int i = 0; - - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *engine = link->data; - engine_handle_array[i++] = engine; - } - engine_handle_array[i] = NULL; - - GPU_viewport_engines_data_validate(DST.viewport, engine_handle_array); + DRW_view_data_free_unused(DST.view_data_active); } /* Fast check to see if gpencil drawing engine is needed. @@ -1283,55 +1314,57 @@ void DRW_notify_view_update(const DRWUpdateContext *update_ctx) Scene *scene = update_ctx->scene; ViewLayer *view_layer = update_ctx->view_layer; + GPUViewport *viewport = WM_draw_region_get_viewport(region); + if (!viewport) { + return; + } + const bool gpencil_engine_needed = drw_gpencil_engine_needed(depsgraph, v3d); + /* XXX Really nasty locking. But else this could + * be executed by the material previews thread + * while rendering a viewport. */ + BLI_ticket_mutex_lock(DST.gl_context_mutex); + + /* Reset before using it. */ + drw_state_prepare_clean_for_draw(&DST); + + DST.draw_ctx = (DRWContextState){ + .region = region, + .rv3d = rv3d, + .v3d = v3d, + .scene = scene, + .view_layer = view_layer, + .obact = OBACT(view_layer), + .engine_type = engine_type, + .depsgraph = depsgraph, + .object_mode = OB_MODE_OBJECT, + }; + + /* Custom lightweight initialize to avoid resetting the memory-pools. */ + DST.viewport = viewport; + DST.vmempool = drw_viewport_data_ensure(DST.viewport); + /* Separate update for each stereo view. */ - for (int view = 0; view < 2; view++) { - GPUViewport *viewport = WM_draw_region_get_viewport(region); - if (!viewport) { - continue; - } - - /* XXX Really nasty locking. But else this could - * be executed by the material previews thread - * while rendering a viewport. */ - BLI_ticket_mutex_lock(DST.gl_context_mutex); - - /* Reset before using it. */ - drw_state_prepare_clean_for_draw(&DST); - - DST.viewport = viewport; - GPU_viewport_active_view_set(viewport, view); - DST.draw_ctx = (DRWContextState){ - .region = region, - .rv3d = rv3d, - .v3d = v3d, - .scene = scene, - .view_layer = view_layer, - .obact = OBACT(view_layer), - .engine_type = engine_type, - .depsgraph = depsgraph, - .object_mode = OB_MODE_OBJECT, - }; + int view_count = GPU_viewport_is_stereo_get(viewport) ? 2 : 1; + for (int view = 0; view < view_count; view++) { + DST.view_data_active = DST.vmempool->view_data[view]; drw_engines_enable(view_layer, engine_type, gpencil_engine_needed); drw_engines_data_validate(); - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { - DrawEngineType *draw_engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine); - + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, draw_engine, data) { if (draw_engine->view_update) { draw_engine->view_update(data); } } - DST.viewport = NULL; - drw_engines_disable(); - - BLI_ticket_mutex_unlock(DST.gl_context_mutex); } + + drw_manager_exit(&DST); + + BLI_ticket_mutex_unlock(DST.gl_context_mutex); } /** \} */ @@ -1391,6 +1424,27 @@ void DRW_draw_callbacks_post_scene(void) ED_region_draw_cb_draw(DST.draw_ctx.evil_C, DST.draw_ctx.region, REGION_DRAW_POST_VIEW); +#ifdef WITH_XR_OPENXR + /* XR callbacks (controllers, custom draw functions) for session mirror. */ + if ((v3d->flag & V3D_XR_SESSION_MIRROR) != 0) { + if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) { + ARegionType *art = WM_xr_surface_controller_region_type_get(); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) { + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + } + } +#endif + /* Callback can be nasty and do whatever they want with the state. * Don't trust them! */ DRW_state_reset(); @@ -1437,6 +1491,46 @@ void DRW_draw_callbacks_post_scene(void) ED_annotation_draw_view3d(DEG_get_input_scene(depsgraph), depsgraph, v3d, region, true); GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } + +#ifdef WITH_XR_OPENXR + if ((v3d->flag & V3D_XR_SESSION_SURFACE) != 0) { + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + DRW_state_reset(); + + GPU_framebuffer_bind(dfbl->overlay_fb); + + GPU_matrix_projection_set(rv3d->winmat); + GPU_matrix_set(rv3d->viewmat); + + /* XR callbacks (controllers, custom draw functions) for session surface. */ + if (((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) || + ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0)) { + GPU_depth_test(GPU_DEPTH_NONE); + GPU_apply_state(); + + if ((v3d->flag2 & V3D_XR_SHOW_CONTROLLERS) != 0) { + ARegionType *art = WM_xr_surface_controller_region_type_get(); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + if ((v3d->flag2 & V3D_XR_SHOW_CUSTOM_OVERLAYS) != 0) { + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (art) { + ED_region_surface_draw_cb_draw(art, REGION_DRAW_POST_VIEW); + } + } + } + + DRW_state_reset(); + } + + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + } +#endif } } @@ -1502,8 +1596,6 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, RegionView3D *rv3d = region->regiondata; DST.draw_ctx.evil_C = evil_C; - DST.viewport = viewport; - /* Setup viewport */ DST.draw_ctx = (DRWContextState){ .region = region, .rv3d = rv3d, @@ -1520,8 +1612,8 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, drw_task_graph_init(); drw_context_state_init(); - drw_viewport_var_init(); - DRW_viewport_colormanagement_set(DST.viewport); + drw_manager_init(&DST, viewport, NULL); + DRW_viewport_colormanagement_set(viewport); const int object_type_exclude_viewport = v3d->object_type_exclude_viewport; /* Check if scene needs to perform the populate loop */ @@ -1580,7 +1672,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_render_instance_buffer_finish(); #ifdef USE_PROFILE - double *cache_time = GPU_viewport_cache_time_get(DST.viewport); + double *cache_time = DRW_view_data_cache_time_get(DST.view_data_active); PROFILE_END_UPDATE(*cache_time, stime); #endif } @@ -1620,12 +1712,7 @@ void DRW_draw_render_loop_ex(struct Depsgraph *depsgraph, DRW_state_reset(); drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } void DRW_draw_render_loop(struct Depsgraph *depsgraph, @@ -1663,6 +1750,9 @@ void DRW_draw_render_loop_offscreen(struct Depsgraph *depsgraph, GPU_viewport_bind_from_offscreen(render_viewport, ofs); + /* Just here to avoid an assert but shouldn't be required in practice. */ + GPU_framebuffer_restore(); + /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); DST.options.is_image_render = is_image_render; @@ -1724,7 +1814,8 @@ static void DRW_render_gpencil_to_image(RenderEngine *engine, const rcti *rect) { if (draw_engine_gpencil_type.render_to_image) { - ViewportEngineData *gpdata = drw_viewport_engine_data_ensure(&draw_engine_gpencil_type); + ViewportEngineData *gpdata = DRW_view_data_engine_data_get_ensure(DST.view_data_active, + &draw_engine_gpencil_type); draw_engine_gpencil_type.render_to_image(gpdata, engine, render_layer, rect); } } @@ -1758,11 +1849,9 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph }; drw_context_state_init(); - DST.viewport = GPU_viewport_create(); const int size[2] = {engine->resolution_x, engine->resolution_y}; - GPU_viewport_size_set(DST.viewport, size); - drw_viewport_var_init(); + drw_manager_init(&DST, NULL, size); /* Main rendering. */ rctf view_rect; @@ -1782,13 +1871,12 @@ void DRW_render_gpencil(struct RenderEngine *engine, struct Depsgraph *depsgraph DRW_render_gpencil_to_image(engine, render_layer, &render_rect); } - /* Force cache to reset. */ - drw_viewport_cache_resize(); - GPU_viewport_free(DST.viewport); DRW_state_reset(); GPU_depth_test(GPU_DEPTH_NONE); + drw_manager_exit(&DST); + /* Restore Drawing area. */ GPU_framebuffer_restore(); @@ -1837,13 +1925,12 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) }; drw_context_state_init(); - DST.viewport = GPU_viewport_create(); const int size[2] = {engine->resolution_x, engine->resolution_y}; - GPU_viewport_size_set(DST.viewport, size); - drw_viewport_var_init(); + drw_manager_init(&DST, NULL, size); - ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine_type); + ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active, + draw_engine_type); /* Main rendering. */ rctf view_rect; @@ -1887,16 +1974,9 @@ void DRW_render_to_image(RenderEngine *engine, struct Depsgraph *depsgraph) engine_type->draw_engine->store_metadata(data, final_render_result); } - /* Force cache to reset. */ - drw_viewport_cache_resize(); - - GPU_viewport_free(DST.viewport); GPU_framebuffer_restore(); -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); /* Reset state after drawing */ DRW_state_reset(); @@ -1965,21 +2045,17 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, }; drw_context_state_init(); - DST.viewport = GPU_viewport_create(); - const int size[2] = {1, 1}; - GPU_viewport_size_set(DST.viewport, size); - - drw_viewport_var_init(); + drw_manager_init(&DST, NULL, NULL); DRW_hair_init(); - ViewportEngineData *data = drw_viewport_engine_data_ensure(draw_engine_type); + ViewportEngineData *data = DRW_view_data_engine_data_get_ensure(DST.view_data_active, + draw_engine_type); /* Execute the callback */ callback(data, user_data); DST.buffer_finish_called = false; - GPU_viewport_free(DST.viewport); GPU_framebuffer_restore(); /* The use of custom pipeline in other thread using the same @@ -1988,33 +2064,18 @@ void DRW_custom_pipeline(DrawEngineType *draw_engine_type, * GPU_finish to sync seems to fix the issue. (see T62997) */ GPU_finish(); -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } /* Used when the render engine want to redo another cache populate inside the same render frame. */ void DRW_cache_restart(void) { - /* Save viewport size. */ - float size[2], inv_size[2]; - copy_v2_v2(size, DST.size); - copy_v2_v2(inv_size, DST.inv_size); - - /* Force cache to reset. */ - drw_viewport_cache_resize(); - - drw_viewport_var_init(); + drw_manager_init(&DST, DST.viewport, (int[2]){UNPACK2(DST.size)}); DST.buffer_finish_called = false; DRW_hair_init(); - - /* Restore. */ - copy_v2_v2(DST.size, size); - copy_v2_v2(DST.inv_size, inv_size); } void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, @@ -2026,9 +2087,6 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph); DST.draw_ctx.evil_C = evil_C; - DST.viewport = viewport; - - /* Setup viewport */ DST.draw_ctx = (DRWContextState){ .region = region, .scene = scene, @@ -2042,8 +2100,8 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, }; drw_context_state_init(); - drw_viewport_var_init(); - DRW_viewport_colormanagement_set(DST.viewport); + drw_manager_init(&DST, viewport, NULL); + DRW_viewport_colormanagement_set(viewport); /* TODO(jbakker): Only populate when editor needs to draw object. * for the image editor this is when showing UV's. */ @@ -2087,7 +2145,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, DRW_render_instance_buffer_finish(); #ifdef USE_PROFILE - double *cache_time = GPU_viewport_cache_time_get(DST.viewport); + double *cache_time = DRW_view_data_cache_time_get(DST.view_data_active); PROFILE_END_UPDATE(*cache_time, stime); #endif } @@ -2169,12 +2227,7 @@ void DRW_draw_render_loop_2d_ex(struct Depsgraph *depsgraph, DRW_state_reset(); drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } static struct DRWSelectBuffer { @@ -2211,7 +2264,7 @@ void DRW_render_instance_buffer_finish(void) { BLI_assert_msg(!DST.buffer_finish_called, "DRW_render_instance_buffer_finish called twice!"); DST.buffer_finish_called = true; - DRW_instance_buffer_finish(DST.idatalist); + DRW_instance_buffer_finish(DST.vmempool->idatalist); drw_resource_buffer_finish(DST.vmempool); } @@ -2294,11 +2347,22 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, } } - const int viewport_size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)}; - struct GPUViewport *viewport = GPU_viewport_create(); - GPU_viewport_size_set(viewport, viewport_size); + /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */ + DST.draw_ctx = (DRWContextState){ + .region = region, + .rv3d = rv3d, + .v3d = v3d, + .scene = scene, + .view_layer = view_layer, + .obact = obact, + .engine_type = engine_type, + .depsgraph = depsgraph, + }; + drw_context_state_init(); + + const int viewport_size[2] = {BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)}; + drw_manager_init(&DST, NULL, viewport_size); - DST.viewport = viewport; DST.options.is_select = true; DST.options.is_material_select = do_material_sub_selection; drw_task_graph_init(); @@ -2326,22 +2390,6 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, } drw_engines_data_validate(); - /* Setup viewport */ - - /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */ - DST.draw_ctx = (DRWContextState){ - .region = region, - .rv3d = rv3d, - .v3d = v3d, - .scene = scene, - .view_layer = view_layer, - .obact = obact, - .engine_type = engine_type, - .depsgraph = depsgraph, - }; - drw_context_state_init(); - drw_viewport_var_init(); - /* Update UBO's */ DRW_globals_update(); @@ -2445,14 +2493,10 @@ void DRW_draw_select_loop(struct Depsgraph *depsgraph, DRW_state_reset(); drw_engines_disable(); -# ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -# endif + drw_manager_exit(&DST); + GPU_framebuffer_restore(); - /* Cleanup for selection state */ - GPU_viewport_free(viewport); #endif /* USE_GPU_SELECT */ } @@ -2474,7 +2518,6 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, DRW_opengl_context_enable(); } - DST.viewport = viewport; DST.options.is_depth = true; /* Instead of 'DRW_context_state_init(C, &DST.draw_ctx)', assign from args */ @@ -2488,18 +2531,21 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, .engine_type = engine_type, .depsgraph = depsgraph, }; + drw_context_state_init(); drw_task_graph_init(); - drw_engines_data_validate(); /* Setup frame-buffer. */ - DefaultFramebufferList *fbl = (DefaultFramebufferList *)GPU_viewport_framebuffer_list_get( - DST.viewport); - GPU_framebuffer_bind(fbl->depth_only_fb); - GPU_framebuffer_clear_depth(fbl->depth_only_fb, 1.0f); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); - /* Setup viewport */ - drw_context_state_init(); - drw_viewport_var_init(); + GPUFrameBuffer *depth_fb = NULL; + GPU_framebuffer_ensure_config(&depth_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); + + GPU_framebuffer_bind(depth_fb); + GPU_framebuffer_clear_depth(depth_fb, 1.0f); /* Update UBO's */ DRW_globals_update(); @@ -2548,15 +2594,11 @@ static void drw_draw_depth_loop_impl(struct Depsgraph *depsgraph, /* TODO: Reading depth for operators should be done here. */ GPU_framebuffer_restore(); + GPU_framebuffer_free(depth_fb); drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); /* Changing context. */ if (use_opengl_context) { @@ -2575,6 +2617,11 @@ void DRW_draw_depth_loop(struct Depsgraph *depsgraph, /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); + /* Required by `drw_manager_init()` */ + DST.draw_ctx.region = region; + DST.draw_ctx.rv3d = region->regiondata; + drw_manager_init(&DST, viewport, NULL); + /* Get list of enabled engines */ { /* Required by `DRW_state_draw_support()` */ @@ -2600,6 +2647,11 @@ void DRW_draw_depth_loop_gpencil(struct Depsgraph *depsgraph, /* Reset before using it. */ drw_state_prepare_clean_for_draw(&DST); + /* Required by `drw_manager_init()` */ + DST.draw_ctx.region = region; + DST.draw_ctx.rv3d = region->regiondata; + drw_manager_init(&DST, viewport, NULL); + use_drw_engine(&draw_engine_gpencil_type); drw_draw_depth_loop_impl(depsgraph, region, v3d, viewport, false); @@ -2637,9 +2689,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons drw_task_graph_init(); drw_context_state_init(); - /* Setup viewport */ - DST.viewport = viewport; - drw_viewport_var_init(); + drw_manager_init(&DST, viewport, NULL); /* Update UBO's */ UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW); @@ -2666,7 +2716,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons DRW_render_instance_buffer_finish(); #else DST.buffer_finish_called = true; - // DRW_instance_buffer_finish(DST.idatalist); + // DRW_instance_buffer_finish(DST.vmempool->idatalist); drw_resource_buffer_finish(DST.vmempool); #endif } @@ -2678,12 +2728,7 @@ void DRW_draw_select_id(Depsgraph *depsgraph, ARegion *region, View3D *v3d, cons drw_engines_disable(); - drw_viewport_cache_resize(); - -#ifdef DEBUG - /* Avoid accidental reuse. */ - drw_state_ensure_not_reused(&DST); -#endif + drw_manager_exit(&DST); } /** @@ -2699,10 +2744,17 @@ void DRW_draw_depth_object( GPU_matrix_mul(object->obmat); /* Setup frame-buffer. */ - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); - GPU_framebuffer_bind(fbl->depth_only_fb); - GPU_framebuffer_clear_depth(fbl->depth_only_fb, 1.0f); + GPUFrameBuffer *depth_fb = NULL; + GPU_framebuffer_ensure_config(&depth_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); + + GPU_framebuffer_bind(depth_fb); + GPU_framebuffer_clear_depth(depth_fb, 1.0f); GPU_depth_test(GPU_DEPTH_LESS_EQUAL); const float(*world_clip_planes)[4] = NULL; @@ -2752,6 +2804,8 @@ void DRW_draw_depth_object( GPU_matrix_set(rv3d->viewmat); GPU_depth_test(GPU_DEPTH_NONE); GPU_framebuffer_restore(); + + GPU_framebuffer_free(depth_fb); } /** \} */ @@ -2884,7 +2938,12 @@ bool DRW_engine_render_support(DrawEngineType *draw_engine_type) void DRW_engine_register(DrawEngineType *draw_engine_type) { - BLI_addtail(&DRW_engines, draw_engine_type); + DRWRegisteredDrawEngine *draw_engine = MEM_mallocN(sizeof(DRWRegisteredDrawEngine), __func__); + draw_engine->draw_engine = draw_engine_type; + draw_engine->index = g_registered_engines.len; + + BLI_addtail(&g_registered_engines.engines, draw_engine); + g_registered_engines.len = BLI_listbase_count(&g_registered_engines.engines); } void DRW_engines_register(void) @@ -2935,8 +2994,27 @@ void DRW_engines_register(void) } } +static void drw_registered_engines_free(void) +{ + DRWRegisteredDrawEngine *next; + for (DRWRegisteredDrawEngine *type = g_registered_engines.engines.first; type; type = next) { + next = type->next; + BLI_remlink(&R_engines, type); + + if (type->draw_engine->engine_free) { + type->draw_engine->engine_free(); + } + MEM_freeN(type); + } + + BLI_listbase_clear(&g_registered_engines.engines); + g_registered_engines.len = 0; +} + void DRW_engines_free(void) { + drw_registered_engines_free(); + if (DST.gl_context == NULL) { /* Nothing has been setup. Nothing to clear. * Otherwise, DRW_opengl_context_enable can @@ -2955,16 +3033,6 @@ void DRW_engines_free(void) DRW_stats_free(); DRW_globals_free(); - DrawEngineType *next; - for (DrawEngineType *type = DRW_engines.first; type; type = next) { - next = type->next; - BLI_remlink(&R_engines, type); - - if (type->engine_free) { - type->engine_free(); - } - } - DRW_UBO_FREE_SAFE(G_draw.block_ubo); DRW_UBO_FREE_SAFE(G_draw.view_ubo); DRW_TEXTURE_FREE_SAFE(G_draw.ramp); @@ -3188,3 +3256,66 @@ void DRW_draw_state_init_gtests(eGPUShaderConfig sh_cfg) #endif /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw manager context release/activation + * + * These functions are used in cases when an OpenGL context creation is needed during the draw. + * This happens, for example, when an external engine needs to create its own OpenGL context from + * the engine initialization. + * + * Example of context creation: + * + * const bool drw_state = DRW_opengl_context_release(); + * gl_context = WM_opengl_context_create(); + * DRW_opengl_context_activate(drw_state); + * + * Example of context destruction: + * + * const bool drw_state = DRW_opengl_context_release(); + * WM_opengl_context_activate(gl_context); + * WM_opengl_context_dispose(gl_context); + * DRW_opengl_context_activate(drw_state); + * + * + * NOTE: Will only perform context modification when on main thread. This way these functions can + * be used in an engine without check on whether it is a draw manager which manages OpenGL context + * on the current thread. The downside of this is that if the engine performs OpenGL creation from + * a non-main thread, that thread is supposed to not have OpenGL context ever bound by Blender. + * + * \{ */ + +bool DRW_opengl_context_release(void) +{ + if (!BLI_thread_is_main()) { + return false; + } + + if (GPU_context_active_get() != DST.gpu_context) { + /* Context release is requested from the outside of the draw manager main draw loop, indicate + * this to the `DRW_opengl_context_activate()` so that it restores drawable of the window. */ + return false; + } + + GPU_context_active_set(NULL); + WM_opengl_context_release(DST.gl_context); + + return true; +} + +void DRW_opengl_context_activate(bool drw_state) +{ + if (!BLI_thread_is_main()) { + return; + } + + if (drw_state) { + WM_opengl_context_activate(DST.gl_context); + GPU_context_active_set(DST.gpu_context); + } + else { + wm_window_reset_drawable(); + } +} + +/** \} */ diff --git a/source/blender/draw/intern/draw_manager.h b/source/blender/draw/intern/draw_manager.h index c09126c98ef..162fe9b5fd1 100644 --- a/source/blender/draw/intern/draw_manager.h +++ b/source/blender/draw/intern/draw_manager.h @@ -97,6 +97,17 @@ struct Object; #endif /* USE_PROFILE */ /* ------------ Data Structure --------------- */ +/** + * Data structure to for registered draw engines that can store draw manager + * specific data. + */ +typedef struct DRWRegisteredDrawEngine { + void /*DRWRegisteredDrawEngine*/ *next, *prev; + DrawEngineType *draw_engine; + /** Index of the type in the lists. Index is used for dupli data. */ + int index; +} DRWRegisteredDrawEngine; + /** * Data structure containing all drawcalls organized by passes and materials. * DRWPass > DRWShadingGroup > DRWCall > DRWCallState @@ -495,6 +506,35 @@ typedef struct DRWDebugSphere { float color[4]; } DRWDebugSphere; +/* ------------- Memory Pools ------------ */ + +/* Contains memory pools information */ +typedef struct DRWData { + /** Instance data. */ + DRWInstanceDataList *idatalist; + /** Memory-pools for draw-calls. */ + struct BLI_memblock *commands; + struct BLI_memblock *commands_small; + struct BLI_memblock *callbuffers; + struct BLI_memblock *obmats; + struct BLI_memblock *obinfos; + struct BLI_memblock *cullstates; + struct BLI_memblock *shgroups; + struct BLI_memblock *uniforms; + struct BLI_memblock *views; + struct BLI_memblock *passes; + struct BLI_memblock *images; + struct GPUUniformBuf **matrices_ubo; + struct GPUUniformBuf **obinfos_ubo; + struct GHash *obattrs_ubo_pool; + uint ubo_len; + /** Texture pool to reuse temp texture across engines. */ + /* TODO(fclem) the pool could be shared even between viewports. */ + struct DRWTexturePool *texture_pool; + /** Per stereo view data. Contains engine data and default framebuffers. */ + struct DRWViewData *view_data[2]; +} DRWData; + /* ------------- DRAW MANAGER ------------ */ typedef struct DupliKey { @@ -509,8 +549,10 @@ typedef struct DupliKey { typedef struct DRWManager { /* TODO: clean up this struct a bit. */ /* Cache generation */ - ViewportMemoryPool *vmempool; - DRWInstanceDataList *idatalist; + /* TODO(fclem) Rename to data. */ + DRWData *vmempool; + /** Active view data structure for one of the 2 stereo view. Not related to DRWView. */ + struct DRWViewData *view_data_active; /* State of the object being evaluated if already allocated. */ DRWResourceHandle ob_handle; /** True if current DST.ob_state has its matching DRWObjectInfos init. */ @@ -567,10 +609,6 @@ typedef struct DRWManager { /* Convenience pointer to text_store owned by the viewport */ struct DRWTextStore **text_store_p; - ListBase enabled_engines; /* RenderEngineType */ - void **vedata_array; /* ViewportEngineData */ - int enabled_engine_count; /* Length of enabled_engines list. */ - bool buffer_finish_called; /* Avoid bad usage of DRW_render_instance_buffer_finish */ DRWView *view_default; @@ -627,7 +665,7 @@ void drw_batch_cache_validate(Object *ob); void drw_batch_cache_generate_requested(struct Object *ob); void drw_batch_cache_generate_requested_delayed(Object *ob); -void drw_resource_buffer_finish(ViewportMemoryPool *vmempool); +void drw_resource_buffer_finish(DRWData *vmempool); /* Procedural Drawing */ GPUBatch *drw_cache_procedural_points_get(void); @@ -641,6 +679,13 @@ void drw_uniform_attrs_pool_update(struct GHash *table, struct Object *dupli_parent, struct DupliObject *dupli_source); +double *drw_engine_data_cache_time_get(GPUViewport *viewport); +void *drw_engine_data_engine_data_create(GPUViewport *viewport, void *engine_type); +void *drw_engine_data_engine_data_get(GPUViewport *viewport, void *engine_handle); +bool drw_engine_data_engines_data_validate(GPUViewport *viewport, void **engine_handle_array); +void drw_engine_data_cache_release(GPUViewport *viewport); +void drw_engine_data_free(GPUViewport *viewport); + #ifdef __cplusplus } #endif diff --git a/source/blender/draw/intern/draw_manager_data.c b/source/blender/draw/intern/draw_manager_data.c index af331c86a8b..5d3e3db866f 100644 --- a/source/blender/draw/intern/draw_manager_data.c +++ b/source/blender/draw/intern/draw_manager_data.c @@ -87,7 +87,7 @@ static void draw_call_sort(DRWCommand *array, DRWCommand *array_tmp, int array_l memcpy(array, array_tmp, sizeof(*array) * array_len); } -void drw_resource_buffer_finish(ViewportMemoryPool *vmempool) +void drw_resource_buffer_finish(DRWData *vmempool) { int chunk_id = DRW_handle_chunk_get(&DST.resource_handle); int elem_id = DRW_handle_id_get(&DST.resource_handle); @@ -908,7 +908,8 @@ void DRW_shgroup_call_instances_with_attrs(DRWShadingGroup *shgroup, drw_command_set_select_id(shgroup, NULL, DST.select_id); } DRWResourceHandle handle = drw_resource_handle(shgroup, ob ? ob->obmat : NULL, ob); - GPUBatch *batch = DRW_temp_batch_instance_request(DST.idatalist, NULL, inst_attributes, geom); + GPUBatch *batch = DRW_temp_batch_instance_request( + DST.vmempool->idatalist, NULL, inst_attributes, geom); drw_command_draw_instance(shgroup, batch, handle, 0, true); } @@ -1128,7 +1129,7 @@ DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup, BLI_assert(format != NULL); DRWCallBuffer *callbuf = BLI_memblock_alloc(DST.vmempool->callbuffers); - callbuf->buf = DRW_temp_buffer_request(DST.idatalist, format, &callbuf->count); + callbuf->buf = DRW_temp_buffer_request(DST.vmempool->idatalist, format, &callbuf->count); callbuf->buf_select = NULL; callbuf->count = 0; @@ -1138,12 +1139,12 @@ DRWCallBuffer *DRW_shgroup_call_buffer(DRWShadingGroup *shgroup, GPU_vertformat_attr_add(&inst_select_format, "selectId", GPU_COMP_I32, 1, GPU_FETCH_INT); } callbuf->buf_select = DRW_temp_buffer_request( - DST.idatalist, &inst_select_format, &callbuf->count); + DST.vmempool->idatalist, &inst_select_format, &callbuf->count); drw_command_set_select_id(shgroup, callbuf->buf_select, -1); } DRWResourceHandle handle = drw_resource_handle(shgroup, NULL, NULL); - GPUBatch *batch = DRW_temp_batch_request(DST.idatalist, callbuf->buf, prim_type); + GPUBatch *batch = DRW_temp_batch_request(DST.vmempool->idatalist, callbuf->buf, prim_type); drw_command_draw(shgroup, batch, handle); return callbuf; @@ -1157,7 +1158,7 @@ DRWCallBuffer *DRW_shgroup_call_buffer_instance(DRWShadingGroup *shgroup, BLI_assert(format != NULL); DRWCallBuffer *callbuf = BLI_memblock_alloc(DST.vmempool->callbuffers); - callbuf->buf = DRW_temp_buffer_request(DST.idatalist, format, &callbuf->count); + callbuf->buf = DRW_temp_buffer_request(DST.vmempool->idatalist, format, &callbuf->count); callbuf->buf_select = NULL; callbuf->count = 0; @@ -1167,12 +1168,13 @@ DRWCallBuffer *DRW_shgroup_call_buffer_instance(DRWShadingGroup *shgroup, GPU_vertformat_attr_add(&inst_select_format, "selectId", GPU_COMP_I32, 1, GPU_FETCH_INT); } callbuf->buf_select = DRW_temp_buffer_request( - DST.idatalist, &inst_select_format, &callbuf->count); + DST.vmempool->idatalist, &inst_select_format, &callbuf->count); drw_command_set_select_id(shgroup, callbuf->buf_select, -1); } DRWResourceHandle handle = drw_resource_handle(shgroup, NULL, NULL); - GPUBatch *batch = DRW_temp_batch_instance_request(DST.idatalist, callbuf->buf, NULL, geom); + GPUBatch *batch = DRW_temp_batch_instance_request( + DST.vmempool->idatalist, callbuf->buf, NULL, geom); drw_command_draw(shgroup, batch, handle); return callbuf; @@ -1682,10 +1684,12 @@ static void draw_frustum_bound_sphere_calc(const BoundBox *bbox, bsphere->center[0] = farcenter[0] * z / e; bsphere->center[1] = farcenter[1] * z / e; bsphere->center[2] = z; - bsphere->radius = len_v3v3(bsphere->center, farpoint); - /* Transform to world space. */ - mul_m4_v3(viewinv, bsphere->center); + /* For XR, the view matrix may contain a scale factor. Then, transforming only the center + * into world space after calculating the radius will result in incorrect behavior. */ + mul_m4_v3(viewinv, bsphere->center); /* Transform to world space. */ + mul_m4_v3(viewinv, farpoint); + bsphere->radius = len_v3v3(bsphere->center, farpoint); } } diff --git a/source/blender/draw/intern/draw_manager_exec.c b/source/blender/draw/intern/draw_manager_exec.c index 22356a3c57b..aa01ca7a262 100644 --- a/source/blender/draw/intern/draw_manager_exec.c +++ b/source/blender/draw/intern/draw_manager_exec.c @@ -367,6 +367,11 @@ void DRW_view_set_active(DRWView *view) DST.view_active = (view) ? view : DST.view_default; } +const DRWView *DRW_view_get_active(void) +{ + return DST.view_active; +} + /* Return True if the given BoundSphere intersect the current view frustum */ static bool draw_culling_sphere_test(const BoundSphere *frustum_bsphere, const float (*frustum_planes)[4], diff --git a/source/blender/draw/intern/draw_manager_profiling.c b/source/blender/draw/intern/draw_manager_profiling.c index d9ba2cbf932..87d4c34b3ed 100644 --- a/source/blender/draw/intern/draw_manager_profiling.c +++ b/source/blender/draw/intern/draw_manager_profiling.c @@ -257,10 +257,8 @@ void DRW_stats_draw(const rcti *rect) /* Engines rows */ char time_to_txt[16]; - LISTBASE_FOREACH (LinkData *, link, &DST.enabled_engines) { + DRW_ENABLED_ENGINE_ITER (DST.view_data_active, engine, data) { u = 0; - DrawEngineType *engine = link->data; - ViewportEngineData *data = drw_viewport_engine_data_ensure(engine); draw_stat_5row(rect, u++, v, engine->idname, sizeof(engine->idname)); @@ -297,7 +295,7 @@ void DRW_stats_draw(const rcti *rect) v += 2; u = 0; - double *cache_time = GPU_viewport_cache_time_get(DST.viewport); + double *cache_time = DRW_view_data_cache_time_get(DST.view_data_active); sprintf(col_label, "Cache Time"); draw_stat_5row(rect, u++, v, col_label, sizeof(col_label)); sprintf(time_to_txt, "%.2fms", *cache_time); diff --git a/source/blender/draw/intern/draw_manager_text.h b/source/blender/draw/intern/draw_manager_text.h index 760259018bb..883b37c52af 100644 --- a/source/blender/draw/intern/draw_manager_text.h +++ b/source/blender/draw/intern/draw_manager_text.h @@ -22,6 +22,10 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + struct ARegion; struct DRWTextStore; struct Object; @@ -57,3 +61,7 @@ enum { /* draw_manager.c */ struct DRWTextStore *DRW_text_cache_ensure(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/draw_manager_texture.c b/source/blender/draw/intern/draw_manager_texture.c index 99e8ba968a2..86242468e4a 100644 --- a/source/blender/draw/intern/draw_manager_texture.c +++ b/source/blender/draw/intern/draw_manager_texture.c @@ -21,6 +21,7 @@ */ #include "draw_manager.h" +#include "draw_texture_pool.h" #ifndef NDEBUG /* Maybe gpu_texture.c is a better place for this. */ @@ -147,7 +148,7 @@ GPUTexture *DRW_texture_pool_query_2d(int w, DrawEngineType *engine_type) { BLI_assert(drw_texture_format_supports_framebuffer(format)); - GPUTexture *tex = GPU_viewport_texture_pool_query(DST.viewport, engine_type, w, h, format); + GPUTexture *tex = DRW_texture_pool_query(DST.vmempool->texture_pool, w, h, format, engine_type); return tex; } diff --git a/source/blender/draw/intern/draw_texture_pool.cc b/source/blender/draw/intern/draw_texture_pool.cc new file mode 100644 index 00000000000..709bf808874 --- /dev/null +++ b/source/blender/draw/intern/draw_texture_pool.cc @@ -0,0 +1,140 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#include "BKE_global.h" + +#include "BLI_vector.hh" + +#include "draw_texture_pool.h" + +using namespace blender; + +struct DRWTexturePoolHandle { + uint64_t users_bits; + GPUTexture *texture; +}; + +struct DRWTexturePool { + Vector users; + Vector handles; + /* Cache last result to avoid linear search each time. */ + int last_user_id = -1; +}; + +DRWTexturePool *DRW_texture_pool_create(void) +{ + return new DRWTexturePool(); +} + +void DRW_texture_pool_free(DRWTexturePool *pool) +{ + /* Resetting the pool twice will effectively free all textures. */ + DRW_texture_pool_reset(pool); + DRW_texture_pool_reset(pool); + delete pool; +} + +/** + * Try to find a texture corresponding to params into the texture pool. + * If no texture was found, create one and add it to the pool. + */ +GPUTexture *DRW_texture_pool_query( + DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user) +{ + int user_id = pool->last_user_id; + /* Try cached value. */ + if (user_id != -1) { + if (pool->users[user_id] != user) { + user_id = -1; + } + } + /* Try to find inside previous users. */ + if (user_id == -1) { + user_id = pool->users.first_index_of_try(user); + } + /* No chance, needs to add it to the user list. */ + if (user_id == -1) { + user_id = pool->users.size(); + pool->users.append(user); + /* If there is more than 63 users, better refactor this system. */ + BLI_assert(user_id < 64); + } + pool->last_user_id = user_id; + + uint64_t user_bit = 1llu << user_id; + for (DRWTexturePoolHandle &handle : pool->handles) { + /* Skip if the user is already using this texture. */ + if (user_bit & handle.users_bits) { + continue; + } + /* If everything matches reuse the texture. */ + if ((GPU_texture_format(handle.texture) == format) && + (GPU_texture_width(handle.texture) == width) && + (GPU_texture_height(handle.texture) == height)) { + handle.users_bits |= user_bit; + return handle.texture; + } + } + + char name[16] = "DRW_tex_pool"; + if (G.debug & G_DEBUG_GPU) { + int texture_id = pool->handles.size(); + SNPRINTF(name, "DRW_tex_pool_%d", texture_id); + } + + DRWTexturePoolHandle handle; + handle.users_bits = user_bit; + handle.texture = GPU_texture_create_2d(name, width, height, 1, format, nullptr); + pool->handles.append(handle); + /* Doing filtering for depth does not make sense when not doing shadow mapping, + * and enabling texture filtering on integer texture make them unreadable. */ + bool do_filter = !GPU_texture_depth(handle.texture) && !GPU_texture_integer(handle.texture); + GPU_texture_filter_mode(handle.texture, do_filter); + + return handle.texture; +} + +/* Resets the user bits for each texture in the pool and delete unused ones. */ +void DRW_texture_pool_reset(DRWTexturePool *pool) +{ + pool->last_user_id = -1; + + for (auto it = pool->handles.rbegin(); it != pool->handles.rend(); ++it) { + DRWTexturePoolHandle &handle = *it; + if (handle.users_bits == 0) { + if (handle.texture) { + GPU_texture_free(handle.texture); + handle.texture = nullptr; + } + } + else { + handle.users_bits = 0; + } + } + + /* Reverse iteration to make sure we only reorder with known good handles. */ + for (int i = pool->handles.size() - 1; i >= 0; i--) { + if (!pool->handles[i].texture) { + pool->handles.remove_and_reorder(i); + } + } +} diff --git a/source/blender/draw/DRW_engine_types.h b/source/blender/draw/intern/draw_texture_pool.h similarity index 54% rename from source/blender/draw/DRW_engine_types.h rename to source/blender/draw/intern/draw_texture_pool.h index 807f654f559..f0b8f775c75 100644 --- a/source/blender/draw/DRW_engine_types.h +++ b/source/blender/draw/intern/draw_texture_pool.h @@ -13,38 +13,34 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * - * Copyright 2016, Blender Foundation. + * Copyright 2021, Blender Foundation. */ /** \file * \ingroup draw + * + * Texture pool + * A pool that gives temporary render targets that can be reused through other parts of the + * render pipeline. + * Expect texture data is garbage when acquiring it. */ #pragma once +#include "GPU_texture.h" + +typedef struct DRWTexturePool DRWTexturePool; + #ifdef __cplusplus extern "C" { #endif -/* Buffer and textures used by the viewport by default */ -typedef struct DefaultFramebufferList { - struct GPUFrameBuffer *default_fb; - struct GPUFrameBuffer *overlay_fb; - struct GPUFrameBuffer *in_front_fb; - struct GPUFrameBuffer *color_only_fb; - struct GPUFrameBuffer *depth_only_fb; - struct GPUFrameBuffer *overlay_only_fb; - struct GPUFrameBuffer *stereo_comp_fb; -} DefaultFramebufferList; +DRWTexturePool *DRW_texture_pool_create(void); +void DRW_texture_pool_free(DRWTexturePool *pool); -typedef struct DefaultTextureList { - struct GPUTexture *color; - struct GPUTexture *color_overlay; - struct GPUTexture *color_stereo; - struct GPUTexture *color_overlay_stereo; - struct GPUTexture *depth; - struct GPUTexture *depth_in_front; -} DefaultTextureList; +GPUTexture *DRW_texture_pool_query( + DRWTexturePool *pool, int width, int height, eGPUTextureFormat format, void *user); +void DRW_texture_pool_reset(DRWTexturePool *pool); #ifdef __cplusplus } diff --git a/source/blender/draw/intern/draw_view.c b/source/blender/draw/intern/draw_view.c index 90bb3762473..de43d2aba0f 100644 --- a/source/blender/draw/intern/draw_view.c +++ b/source/blender/draw/intern/draw_view.c @@ -28,6 +28,7 @@ #include "DNA_view3d_types.h" #include "ED_screen.h" +#include "ED_util.h" #include "ED_view3d.h" #include "GPU_immediate.h" @@ -39,6 +40,7 @@ #include "WM_types.h" +#include "BKE_global.h" #include "BKE_object.h" #include "BKE_paint.h" @@ -62,6 +64,10 @@ void DRW_draw_region_info(void) static bool is_cursor_visible(const DRWContextState *draw_ctx, Scene *scene, ViewLayer *view_layer) { + if (G.moving & G_TRANSFORM_CURSOR) { + return true; + } + View3D *v3d = draw_ctx->v3d; if ((v3d->flag2 & V3D_HIDE_OVERLAYS) || (v3d->overlay.flag & V3D_OVERLAY_HIDE_CURSOR)) { return false; @@ -226,6 +232,46 @@ static bool is_cursor_visible_2d(const DRWContextState *draw_ctx) return (sima->overlay.flag & SI_OVERLAY_SHOW_OVERLAYS) != 0; } +/* -------------------------------------------------------------------- */ +/** \name Generic Cursor + * \{ */ + +/** + * \note This doesn't require the draw context to be in use. + */ +void DRW_draw_cursor_2d_ex(const ARegion *region, const float cursor[2]) +{ + int co[2]; + UI_view2d_view_to_region(®ion->v2d, cursor[0], cursor[1], &co[0], &co[1]); + + /* Draw nice Anti Aliased cursor. */ + GPU_line_width(1.0f); + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_smooth(true); + + /* Draw lines */ + float original_proj[4][4]; + GPU_matrix_projection_get(original_proj); + GPU_matrix_push(); + ED_region_pixelspace(region); + GPU_matrix_translate_2f(co[0] + 0.5f, co[1] + 0.5f); + GPU_matrix_scale_2f(U.widget_unit, U.widget_unit); + + GPUBatch *cursor_batch = DRW_cache_cursor_get(true); + + GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR); + GPU_batch_set_shader(cursor_batch, shader); + + GPU_batch_draw(cursor_batch); + + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); + GPU_matrix_pop(); + GPU_matrix_projection_set(original_proj); +} + +/** \} */ + void DRW_draw_cursor_2d(void) { const DRWContextState *draw_ctx = DRW_context_state_get(); @@ -236,33 +282,8 @@ void DRW_draw_cursor_2d(void) GPU_depth_test(GPU_DEPTH_NONE); if (is_cursor_visible_2d(draw_ctx)) { - SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; - int co[2]; - UI_view2d_view_to_region(®ion->v2d, sima->cursor[0], sima->cursor[1], &co[0], &co[1]); - - /* Draw nice Anti Aliased cursor. */ - GPU_line_width(1.0f); - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_smooth(true); - - /* Draw lines */ - float original_proj[4][4]; - GPU_matrix_projection_get(original_proj); - GPU_matrix_push(); - ED_region_pixelspace(region); - GPU_matrix_translate_2f(co[0] + 0.5f, co[1] + 0.5f); - GPU_matrix_scale_2f(U.widget_unit, U.widget_unit); - - GPUBatch *cursor_batch = DRW_cache_cursor_get(true); - GPUShader *shader = GPU_shader_get_builtin_shader(GPU_SHADER_2D_FLAT_COLOR); - GPU_batch_set_shader(cursor_batch, shader); - - GPU_batch_draw(cursor_batch); - - GPU_blend(GPU_BLEND_NONE); - GPU_line_smooth(false); - GPU_matrix_pop(); - GPU_matrix_projection_set(original_proj); + const SpaceImage *sima = (SpaceImage *)draw_ctx->space_data; + DRW_draw_cursor_2d_ex(region, sima->cursor); } } /** \} */ diff --git a/source/blender/draw/intern/draw_view.h b/source/blender/draw/intern/draw_view.h index 24fabaae05e..2646918cd3e 100644 --- a/source/blender/draw/intern/draw_view.h +++ b/source/blender/draw/intern/draw_view.h @@ -22,6 +22,8 @@ #pragma once +struct ARegion; + void DRW_draw_region_info(void); void DRW_clear_background(void); void DRW_draw_cursor(void); diff --git a/source/blender/draw/intern/draw_view_data.cc b/source/blender/draw/intern/draw_view_data.cc new file mode 100644 index 00000000000..55ebbf82c29 --- /dev/null +++ b/source/blender/draw/intern/draw_view_data.cc @@ -0,0 +1,243 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw + */ + +#include "BLI_vector.hh" + +#include "GPU_viewport.h" + +#include "DRW_render.h" + +#include "draw_instance_data.h" + +#include "draw_manager_text.h" + +#include "draw_manager.h" +#include "draw_view_data.h" + +using namespace blender; + +struct DRWViewData { + DefaultFramebufferList dfbl = {}; + DefaultTextureList dtxl = {}; + /** True indicates the textures inside dtxl are from the viewport and should not be freed. */ + bool from_viewport = false; + /** Common size for texture in the engines texture list. + * We free all texture lists if it changes. */ + int texture_list_size[2] = {0, 0}; + + double cache_time = 0.0; + + Vector engines; + Vector enabled_engines; +}; + +/** + * Creates a view data with all possible engines type for this view. + * + * `engine_types` contains `DRWRegisteredDrawEngine`. + * */ +DRWViewData *DRW_view_data_create(ListBase *engine_types) +{ + DRWViewData *view_data = new DRWViewData(); + LISTBASE_FOREACH (DRWRegisteredDrawEngine *, type, engine_types) { + ViewportEngineData engine = {}; + engine.engine_type = type; + view_data->engines.append(engine); + } + return view_data; +} + +void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, GPUViewport *viewport) +{ + int active_view = GPU_viewport_active_view_get(viewport); + view_data->from_viewport = true; + + DefaultFramebufferList *dfbl = &view_data->dfbl; + DefaultTextureList *dtxl = &view_data->dtxl; + /* Depth texture is shared between both stereo views. */ + dtxl->depth = GPU_viewport_depth_texture(viewport); + dtxl->color = GPU_viewport_color_texture(viewport, active_view); + dtxl->color_overlay = GPU_viewport_overlay_texture(viewport, active_view); + + GPU_framebuffer_ensure_config(&dfbl->default_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(dtxl->color), + }); + GPU_framebuffer_ensure_config(&dfbl->overlay_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), + }); + GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_NONE, + }); + GPU_framebuffer_ensure_config(&dfbl->color_only_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(dtxl->color), + }); + GPU_framebuffer_ensure_config(&dfbl->overlay_only_fb, + { + GPU_ATTACHMENT_NONE, + GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), + }); +} + +static void draw_viewport_engines_data_clear(ViewportEngineData *data) +{ + DrawEngineType *engine_type = data->engine_type->draw_engine; + const DrawEngineDataSize *data_size = engine_type->vedata_size; + + for (int i = 0; data->fbl && i < data_size->fbl_len; i++) { + GPU_FRAMEBUFFER_FREE_SAFE(data->fbl->framebuffers[i]); + } + for (int i = 0; data->txl && i < data_size->txl_len; i++) { + GPU_TEXTURE_FREE_SAFE(data->txl->textures[i]); + } + for (int i = 0; data->stl && i < data_size->stl_len; i++) { + MEM_SAFE_FREE(data->stl->storage[i]); + } + + MEM_SAFE_FREE(data->fbl); + MEM_SAFE_FREE(data->txl); + MEM_SAFE_FREE(data->psl); + MEM_SAFE_FREE(data->stl); + + if (data->text_draw_cache) { + DRW_text_cache_destroy(data->text_draw_cache); + data->text_draw_cache = nullptr; + } +} + +static void draw_view_data_clear(DRWViewData *view_data) +{ + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.default_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.overlay_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.in_front_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.color_only_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.depth_only_fb); + GPU_FRAMEBUFFER_FREE_SAFE(view_data->dfbl.overlay_only_fb); + + if (!view_data->from_viewport) { + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.color); + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.color_overlay); + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.depth); + } + GPU_TEXTURE_FREE_SAFE(view_data->dtxl.depth_in_front); + + for (ViewportEngineData &engine : view_data->engines) { + draw_viewport_engines_data_clear(&engine); + } + + view_data->texture_list_size[0] = view_data->texture_list_size[1] = 0; + view_data->cache_time = 0.0f; +} + +void DRW_view_data_free(DRWViewData *view_data) +{ + draw_view_data_clear(view_data); + delete view_data; +} + +void DRW_view_data_texture_list_size_validate(DRWViewData *view_data, const int size[2]) +{ + if (!equals_v2v2_int(view_data->texture_list_size, size)) { + draw_view_data_clear(view_data); + copy_v2_v2_int(view_data->texture_list_size, size); + } +} + +ViewportEngineData *DRW_view_data_engine_data_get_ensure(DRWViewData *view_data, + DrawEngineType *engine_type) +{ + for (ViewportEngineData &engine : view_data->engines) { + if (engine.engine_type->draw_engine == engine_type) { + if (engine.fbl == nullptr) { + const DrawEngineDataSize *data_size = engine_type->vedata_size; + engine.fbl = (FramebufferList *)MEM_calloc_arrayN( + data_size->fbl_len, sizeof(GPUFrameBuffer *), "FramebufferList"); + engine.txl = (TextureList *)MEM_calloc_arrayN( + data_size->txl_len, sizeof(GPUTexture *), "TextureList"); + engine.psl = (PassList *)MEM_calloc_arrayN( + data_size->psl_len, sizeof(DRWPass *), "PassList"); + engine.stl = (StorageList *)MEM_calloc_arrayN( + data_size->stl_len, sizeof(void *), "StorageList"); + } + return &engine; + } + } + return nullptr; +} + +void DRW_view_data_use_engine(DRWViewData *view_data, DrawEngineType *engine_type) +{ + ViewportEngineData *engine = DRW_view_data_engine_data_get_ensure(view_data, engine_type); + view_data->enabled_engines.append(engine); +} + +void DRW_view_data_reset(DRWViewData *view_data) +{ + view_data->enabled_engines.clear(); +} + +void DRW_view_data_free_unused(DRWViewData *view_data) +{ + for (ViewportEngineData &engine : view_data->engines) { + if (view_data->enabled_engines.first_index_of_try(&engine) == -1) { + draw_viewport_engines_data_clear(&engine); + } + } +} + +double *DRW_view_data_cache_time_get(DRWViewData *view_data) +{ + return &view_data->cache_time; +} + +DefaultFramebufferList *DRW_view_data_default_framebuffer_list_get(DRWViewData *view_data) +{ + return &view_data->dfbl; +} + +DefaultTextureList *DRW_view_data_default_texture_list_get(DRWViewData *view_data) +{ + return &view_data->dtxl; +} + +void DRW_view_data_enabled_engine_iter_begin(DRWEngineIterator *iterator, DRWViewData *view_data) +{ + iterator->id = 0; + iterator->end = view_data->enabled_engines.size(); + iterator->engines = view_data->enabled_engines.data(); +} + +ViewportEngineData *DRW_view_data_enabled_engine_iter_step(DRWEngineIterator *iterator) +{ + if (iterator->id >= iterator->end) { + return nullptr; + } + ViewportEngineData *engine = iterator->engines[iterator->id++]; + return engine; +} diff --git a/source/blender/draw/intern/draw_view_data.h b/source/blender/draw/intern/draw_view_data.h new file mode 100644 index 00000000000..c8176170a61 --- /dev/null +++ b/source/blender/draw/intern/draw_view_data.h @@ -0,0 +1,139 @@ +/* + * 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. + * + * Copyright 2021, Blender Foundation. + */ + +/** \file + * \ingroup draw + * + * Engine data + * Structure containing each draw engine instance data. + */ + +#pragma once + +#define GPU_INFO_SIZE 512 /* IMA_MAX_RENDER_TEXT */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct GPUViewport; +struct DrawEngineType; +struct DRWRegisteredDrawEngine; + +/* NOTE these structs are only here for reading the actual lists from the engine. + * The actual length of them is stored in a ViewportEngineData_Info. + * The length of 1 is just here to avoid compiler warning. */ +typedef struct FramebufferList { + struct GPUFrameBuffer *framebuffers[1]; +} FramebufferList; + +typedef struct TextureList { + struct GPUTexture *textures[1]; +} TextureList; + +typedef struct PassList { + struct DRWPass *passes[1]; +} PassList; + +/* Stores custom structs from the engine that have been MEM_(m/c)allocN'ed. */ +typedef struct StorageList { + void *storage[1]; +} StorageList; + +typedef struct ViewportEngineData { + /* Not owning pointer to the draw engine. */ + struct DRWRegisteredDrawEngine *engine_type; + + FramebufferList *fbl; + TextureList *txl; + PassList *psl; + StorageList *stl; + char info[GPU_INFO_SIZE]; + + /* we may want to put this elsewhere */ + struct DRWTextStore *text_draw_cache; + + /* Profiling data */ + double init_time; + double render_time; + double background_time; +} ViewportEngineData; + +typedef struct ViewportEngineData_Info { + int fbl_len; + int txl_len; + int psl_len; + int stl_len; +} ViewportEngineData_Info; + +/* Buffer and textures used by the viewport by default */ +typedef struct DefaultFramebufferList { + struct GPUFrameBuffer *default_fb; + struct GPUFrameBuffer *overlay_fb; + struct GPUFrameBuffer *in_front_fb; + struct GPUFrameBuffer *color_only_fb; + struct GPUFrameBuffer *depth_only_fb; + struct GPUFrameBuffer *overlay_only_fb; +} DefaultFramebufferList; + +typedef struct DefaultTextureList { + struct GPUTexture *color; + struct GPUTexture *color_overlay; + struct GPUTexture *depth; + struct GPUTexture *depth_in_front; +} DefaultTextureList; + +typedef struct DRWViewData DRWViewData; + +DRWViewData *DRW_view_data_create(ListBase *engine_types); +void DRW_view_data_free(DRWViewData *view_data); + +void DRW_view_data_default_lists_from_viewport(DRWViewData *view_data, + struct GPUViewport *viewport); +void DRW_view_data_texture_list_size_validate(DRWViewData *view_data, const int size[2]); +ViewportEngineData *DRW_view_data_engine_data_get_ensure(DRWViewData *view_data, + struct DrawEngineType *engine_type_); +void DRW_view_data_use_engine(DRWViewData *view_data, struct DrawEngineType *engine_type); +void DRW_view_data_reset(DRWViewData *view_data); +void DRW_view_data_free_unused(DRWViewData *view_data); +double *DRW_view_data_cache_time_get(DRWViewData *view_data); +DefaultFramebufferList *DRW_view_data_default_framebuffer_list_get(DRWViewData *view_data); +DefaultTextureList *DRW_view_data_default_texture_list_get(DRWViewData *view_data); + +typedef struct DRWEngineIterator { + int id, end; + ViewportEngineData **engines; +} DRWEngineIterator; + +/* Iterate over used engines of this view_data. */ +void DRW_view_data_enabled_engine_iter_begin(DRWEngineIterator *iterator, DRWViewData *view_data); +ViewportEngineData *DRW_view_data_enabled_engine_iter_step(DRWEngineIterator *iterator); + +#define DRW_ENABLED_ENGINE_ITER(view_data_, engine_, data_) \ + DRWEngineIterator iterator; \ + ViewportEngineData *data_; \ + struct DrawEngineType *engine_; \ + DRW_view_data_enabled_engine_iter_begin(&iterator, view_data_); \ + /* WATCH Comma operator trickery ahead! This tests engine_ == NULL. */ \ + while ((data_ = DRW_view_data_enabled_engine_iter_step(&iterator), \ + engine_ = (data_ != NULL) ? (struct DrawEngineType *)data_->engine_type->draw_engine : \ + NULL)) + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh.h b/source/blender/draw/intern/mesh_extractors/extract_mesh.h index d9f397fd8b8..d1ffef4fe92 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh.h +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh.h @@ -328,6 +328,7 @@ extern const MeshExtract extract_poly_idx; extern const MeshExtract extract_edge_idx; extern const MeshExtract extract_vert_idx; extern const MeshExtract extract_fdot_idx; +extern const MeshExtract extract_attr[GPU_MAX_ATTR]; #ifdef __cplusplus } diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc new file mode 100644 index 00000000000..9edefe32fbc --- /dev/null +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_attributes.cc @@ -0,0 +1,398 @@ +/* + * 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) 2021 by Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup draw + */ + +#include "MEM_guardedalloc.h" + +#include + +#include "BLI_float2.hh" +#include "BLI_float3.hh" +#include "BLI_float4.hh" +#include "BLI_string.h" + +#include "BKE_attribute.h" + +#include "extract_mesh.h" + +namespace blender::draw { + +/* ---------------------------------------------------------------------- */ +/** \name Extract Attributes + * \{ */ + +static CustomData *get_custom_data_for_domain(const MeshRenderData *mr, AttributeDomain domain) +{ + switch (domain) { + default: { + return nullptr; + } + case ATTR_DOMAIN_POINT: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + } + case ATTR_DOMAIN_CORNER: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; + } + case ATTR_DOMAIN_FACE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->pdata : &mr->me->pdata; + } + case ATTR_DOMAIN_EDGE: { + return (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->edata : &mr->me->edata; + } + } +} + +/* Utility to convert from the type used in the attributes to the types for the VBO. + * This is mostly used to promote integers and booleans to floats, as other types (float, float2, + * etc.) directly map to available GPU types. Booleans are still converted as attributes are vec4 + * in the shader. + */ +template struct attribute_type_converter { + static VBOType convert_value(AttributeType value) + { + if constexpr (std::is_same_v) { + return value; + } + + /* This should only concern bools which are converted to floats. */ + return static_cast(value); + } +}; + +/* Similar to the one in #extract_mesh_vcol_vbo.cc */ +struct gpuMeshCol { + ushort r, g, b, a; +}; + +template<> struct attribute_type_converter { + static gpuMeshCol convert_value(MPropCol value) + { + gpuMeshCol result; + result.r = unit_float_to_ushort_clamp(value.color[0]); + result.g = unit_float_to_ushort_clamp(value.color[1]); + result.b = unit_float_to_ushort_clamp(value.color[2]); + result.a = unit_float_to_ushort_clamp(value.color[3]); + return result; + } +}; + +/* Return the number of component for the attribute's value type, or 0 if is it unsupported. */ +static uint gpu_component_size_for_attribute_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_BOOL: + case CD_PROP_INT32: + case CD_PROP_FLOAT: { + /* TODO(kevindietrich) : should be 1 when scalar attributes conversion is handled by us. See + * comment #extract_attr_init. */ + return 3; + } + case CD_PROP_FLOAT2: { + return 2; + } + case CD_PROP_FLOAT3: { + return 3; + } + case CD_PROP_COLOR: { + return 4; + } + default: { + return 0; + } + } +} + +static GPUVertFetchMode get_fetch_mode_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_FETCH_INT_TO_FLOAT; + } + case CD_PROP_COLOR: { + return GPU_FETCH_INT_TO_FLOAT_UNIT; + } + default: { + return GPU_FETCH_FLOAT; + } + } +} + +static GPUVertCompType get_comp_type_for_type(CustomDataType type) +{ + switch (type) { + case CD_PROP_INT32: { + return GPU_COMP_I32; + } + case CD_PROP_COLOR: { + return GPU_COMP_U16; + } + default: { + return GPU_COMP_F32; + } + } +} + +static void init_vbo_for_attribute(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + GPUVertCompType comp_type = get_comp_type_for_type(request.cd_type); + GPUVertFetchMode fetch_mode = get_fetch_mode_for_type(request.cd_type); + const uint comp_size = gpu_component_size_for_attribute_type(request.cd_type); + /* We should not be here if the attribute type is not supported. */ + BLI_assert(comp_size != 0); + + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name( + custom_data, request.cd_type, request.layer_index); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* Attributes use auto-name. */ + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + + GPUVertFormat format = {0}; + GPU_vertformat_deinterleave(&format); + GPU_vertformat_attr_add(&format, attr_name, comp_type, comp_size, fetch_mode); + GPU_vertbuf_init_with_format(vbo, &format); + GPU_vertbuf_data_alloc(vbo, static_cast(mr->loop_len)); +} + +template +static void fill_vertbuf_with_attribute(const MeshRenderData *mr, + VBOType *vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + const MPoly *mpoly = mr->mpoly; + const MLoop *mloop = mr->mloop; + + const AttributeType *attr_data = static_cast( + CustomData_get_layer_n(custom_data, request.cd_type, layer_index)); + + using converter = attribute_type_converter; + + switch (request.domain) { + default: { + BLI_assert(false); + break; + } + case ATTR_DOMAIN_POINT: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->v]); + } + break; + } + case ATTR_DOMAIN_CORNER: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++) { + *vbo_data = converter::convert_value(attr_data[ml_index]); + } + break; + } + case ATTR_DOMAIN_EDGE: { + for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vbo_data++, mloop++) { + *vbo_data = converter::convert_value(attr_data[mloop->e]); + } + break; + } + case ATTR_DOMAIN_FACE: { + for (int mp_index = 0; mp_index < mr->poly_len; mp_index++) { + const MPoly &poly = mpoly[mp_index]; + const VBOType value = converter::convert_value(attr_data[mp_index]); + for (int l = 0; l < poly.totloop; l++) { + *vbo_data++ = value; + } + } + break; + } + } +} + +template +static void fill_vertbuf_with_attribute_bm(const MeshRenderData *mr, + VBOType *&vbo_data, + const DRW_AttributeRequest &request) +{ + const CustomData *custom_data = get_custom_data_for_domain(mr, request.domain); + BLI_assert(custom_data); + const int layer_index = request.layer_index; + + int cd_ofs = CustomData_get_n_offset(custom_data, request.cd_type, layer_index); + + using converter = attribute_type_converter; + + BMIter f_iter; + BMFace *efa; + BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter, *l_first; + l_iter = l_first = BM_FACE_FIRST_LOOP(efa); + do { + const AttributeType *attr_data = nullptr; + if (request.domain == ATTR_DOMAIN_POINT) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_CORNER) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_FACE) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(efa, cd_ofs)); + } + else if (request.domain == ATTR_DOMAIN_EDGE) { + attr_data = static_cast(BM_ELEM_CD_GET_VOID_P(l_iter->e, cd_ofs)); + } + else { + BLI_assert(false); + continue; + } + *vbo_data = converter::convert_value(*attr_data); + vbo_data++; + } while ((l_iter = l_iter->next) != l_first); + } +} + +template +static void extract_attr_generic(const MeshRenderData *mr, + GPUVertBuf *vbo, + const DRW_AttributeRequest &request) +{ + VBOType *vbo_data = static_cast(GPU_vertbuf_get_data(vbo)); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + fill_vertbuf_with_attribute_bm(mr, vbo_data, request); + } + else { + fill_vertbuf_with_attribute(mr, vbo_data, request); + } +} + +static void extract_attr_init(const MeshRenderData *mr, + struct MeshBatchCache *cache, + void *buf, + void *UNUSED(tls_data), + int index) +{ + const DRW_MeshAttributes *attrs_used = &cache->attr_used; + const DRW_AttributeRequest &request = attrs_used->requests[index]; + + GPUVertBuf *vbo = static_cast(buf); + + init_vbo_for_attribute(mr, vbo, request); + + /* TODO(kevindietrich) : float3 is used for scalar attributes as the implicit conversion done by + * OpenGL to vec4 for a scalar `s` will produce a `vec4(s, 0, 0, 1)`. However, following the + * Blender convention, it should be `vec4(s, s, s, 1)`. This could be resolved using a similar + * texture as for volume attribute, so we can control the conversion ourselves. */ + switch (request.cd_type) { + case CD_PROP_BOOL: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_INT32: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT2: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_FLOAT3: { + extract_attr_generic(mr, vbo, request); + break; + } + case CD_PROP_COLOR: { + extract_attr_generic(mr, vbo, request); + break; + } + default: { + BLI_assert(false); + } + } +} + +/* Wrappers around extract_attr_init so we can pass the index of the attribute that we want to + * extract. The overall API does not allow us to pass this in a convenient way. */ +#define EXTRACT_INIT_WRAPPER(index) \ + static void extract_attr_init##index( \ + const MeshRenderData *mr, struct MeshBatchCache *cache, void *buf, void *tls_data) \ + { \ + extract_attr_init(mr, cache, buf, tls_data, index); \ + } + +EXTRACT_INIT_WRAPPER(0) +EXTRACT_INIT_WRAPPER(1) +EXTRACT_INIT_WRAPPER(2) +EXTRACT_INIT_WRAPPER(3) +EXTRACT_INIT_WRAPPER(4) +EXTRACT_INIT_WRAPPER(5) +EXTRACT_INIT_WRAPPER(6) +EXTRACT_INIT_WRAPPER(7) +EXTRACT_INIT_WRAPPER(8) +EXTRACT_INIT_WRAPPER(9) +EXTRACT_INIT_WRAPPER(10) +EXTRACT_INIT_WRAPPER(11) +EXTRACT_INIT_WRAPPER(12) +EXTRACT_INIT_WRAPPER(13) +EXTRACT_INIT_WRAPPER(14) + +template constexpr MeshExtract create_extractor_attr(ExtractInitFn fn) +{ + MeshExtract extractor = {nullptr}; + extractor.init = fn; + extractor.data_type = MR_DATA_NONE; + extractor.data_size = 0; + extractor.use_threading = false; + extractor.mesh_buffer_offset = offsetof(MeshBufferList, vbo.attr[index]); + return extractor; +} + +/** \} */ + +} // namespace blender::draw + +extern "C" { +#define CREATE_EXTRACTOR_ATTR(index) \ + blender::draw::create_extractor_attr(blender::draw::extract_attr_init##index) + +const MeshExtract extract_attr[GPU_MAX_ATTR] = { + CREATE_EXTRACTOR_ATTR(0), + CREATE_EXTRACTOR_ATTR(1), + CREATE_EXTRACTOR_ATTR(2), + CREATE_EXTRACTOR_ATTR(3), + CREATE_EXTRACTOR_ATTR(4), + CREATE_EXTRACTOR_ATTR(5), + CREATE_EXTRACTOR_ATTR(6), + CREATE_EXTRACTOR_ATTR(7), + CREATE_EXTRACTOR_ATTR(8), + CREATE_EXTRACTOR_ATTR(9), + CREATE_EXTRACTOR_ATTR(10), + CREATE_EXTRACTOR_ATTR(11), + CREATE_EXTRACTOR_ATTR(12), + CREATE_EXTRACTOR_ATTR(13), + CREATE_EXTRACTOR_ATTR(14), +}; +} diff --git a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc index 21d0dea0537..ed4f2649b77 100644 --- a/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc +++ b/source/blender/draw/intern/mesh_extractors/extract_mesh_vbo_vcol.cc @@ -42,11 +42,11 @@ static void extract_vcol_init(const MeshRenderData *mr, GPUVertFormat format = {0}; GPU_vertformat_deinterleave(&format); - CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; CustomData *cd_vdata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->vdata : &mr->me->vdata; + CustomData *cd_ldata = (mr->extract_type == MR_EXTRACT_BMESH) ? &mr->bm->ldata : &mr->me->ldata; uint32_t vcol_layers = cache->cd_used.vcol; - uint32_t svcol_layers = cache->cd_used.sculpt_vcol; +#if 0 for (int i = 0; i < MAX_MCOL; i++) { if (vcol_layers & (1 << i)) { char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; @@ -64,36 +64,51 @@ static void extract_vcol_init(const MeshRenderData *mr, } /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs` and sculpt vertex colors. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1 && - CustomData_get_named_layer_index(cd_vdata, CD_PROP_COLOR, layer_name) == -1) { + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); GPU_vertformat_alias_add(&format, attr_name); } } } +#endif - /* Sculpt Vertex Colors */ - for (int i = 0; i < 8; i++) { - if (svcol_layers & (1 << i)) { - char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; - const char *layer_name = CustomData_get_layer_name(cd_vdata, CD_PROP_COLOR, i); - GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + /* + note that there are three color attribute types that operate over two domains + (verts and face corners) + */ + int vcol_types[3] = {CD_MLOOPCOL, CD_PROP_COLOR, CD_PROP_FLOAT3}; - BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); - GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + for (int i = 0; i < 3; i++) { + int type = vcol_types[i]; - if (i == CustomData_get_render_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "c"); - } - if (i == CustomData_get_active_layer(cd_vdata, CD_PROP_COLOR)) { - GPU_vertformat_alias_add(&format, "ac"); - } - /* Gather number of auto layers. */ - /* We only do `vcols` that are not overridden by `uvs`. */ - if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { - BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); - GPU_vertformat_alias_add(&format, attr_name); + for (int step = 0; step < 2; step++) { + CustomData *cdata = step ? cd_ldata : cd_vdata; + int count = CustomData_number_of_layers(cdata, type); + + for (int j = 0; j < count; j++) { + int idx = CustomData_get_layer_index_n(cdata, type, j); + + char attr_name[32], attr_safe_name[GPU_MAX_SAFE_ATTR_NAME]; + const char *layer_name = CustomData_get_layer_name(cdata, type, i); + GPU_vertformat_safe_attr_name(layer_name, attr_safe_name, GPU_MAX_SAFE_ATTR_NAME); + + BLI_snprintf(attr_name, sizeof(attr_name), "c%s", attr_safe_name); + GPU_vertformat_attr_add(&format, attr_name, GPU_COMP_U16, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + + if (i == CustomData_get_render_layer(cdata, type)) { + GPU_vertformat_alias_add(&format, "c"); + } + if (i == CustomData_get_active_layer(cdata, type)) { + GPU_vertformat_alias_add(&format, "ac"); + } + + /* Gather number of auto layers. */ + /* We only do `vcols` that are not overridden by `uvs`. */ + if (CustomData_get_named_layer_index(cd_ldata, CD_MLOOPUV, layer_name) == -1) { + BLI_snprintf(attr_name, sizeof(attr_name), "a%s", attr_safe_name); + GPU_vertformat_alias_add(&format, attr_name); + } } } } @@ -106,63 +121,141 @@ static void extract_vcol_init(const MeshRenderData *mr, }; gpuMeshVcol *vcol_data = (gpuMeshVcol *)GPU_vertbuf_get_data(vbo); - MLoop *loops = (MLoop *)CustomData_get_layer(cd_ldata, CD_MLOOP); - for (int i = 0; i < MAX_MCOL; i++) { - if (vcol_layers & (1 << i)) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_ldata, CD_MLOOPCOL, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MLoopCol *mloopcol = (const MLoopCol *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); - } - } - else { - const MLoopCol *mloopcol = (MLoopCol *)CustomData_get_layer_n(cd_ldata, CD_MLOOPCOL, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, mloopcol++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->r]); - vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->g]); - vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[mloopcol->b]); - vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); - } - } - } + for (int i = 0; i < 3; i++) { + int type = vcol_types[i]; - if (svcol_layers & (1 << i)) { - if (mr->extract_type == MR_EXTRACT_BMESH) { - int cd_ofs = CustomData_get_n_offset(cd_vdata, CD_PROP_COLOR, i); - BMIter f_iter; - BMFace *efa; - BM_ITER_MESH (efa, &f_iter, mr->bm, BM_FACES_OF_MESH) { - BMLoop *l_iter, *l_first; - l_iter = l_first = BM_FACE_FIRST_LOOP(efa); - do { - const MPropCol *prop_col = (const MPropCol *)BM_ELEM_CD_GET_VOID_P(l_iter->v, cd_ofs); - vcol_data->r = unit_float_to_ushort_clamp(prop_col->color[0]); - vcol_data->g = unit_float_to_ushort_clamp(prop_col->color[1]); - vcol_data->b = unit_float_to_ushort_clamp(prop_col->color[2]); - vcol_data->a = unit_float_to_ushort_clamp(prop_col->color[3]); - vcol_data++; - } while ((l_iter = l_iter->next) != l_first); + for (int step = 0; step < 2; step++) { + CustomData *cdata = step ? cd_ldata : cd_vdata; + int count = CustomData_number_of_layers(cdata, type); + + for (int j = 0; j < count; j++) { + int idx = CustomData_get_layer_index_n(cdata, type, j); + + if (mr->extract_type == MR_EXTRACT_BMESH) { + BMFace *f; + BMIter iter; + + int cd_vcol = cdata->layers[idx].offset; + + BM_ITER_MESH (f, &iter, mr->bm, BM_FACES_OF_MESH) { + BMLoop *l_iter = BM_FACE_FIRST_LOOP(f); + do { + BMElem *elem = step ? (BMElem *)l_iter : (BMElem *)l_iter->v; + + switch (type) { + case CD_PROP_FLOAT3: + case CD_PROP_COLOR: { + float *color = (float *)BM_ELEM_CD_GET_VOID_P(elem, cd_vcol); + + vcol_data->r = unit_float_to_ushort_clamp(color[0]); + vcol_data->g = unit_float_to_ushort_clamp(color[1]); + vcol_data->b = unit_float_to_ushort_clamp(color[2]); + vcol_data->a = unit_float_to_ushort_clamp(type == CD_PROP_COLOR ? color[3] : + 1.0f); + + break; + } + case CD_MLOOPCOL: { + MLoopCol *mloopcol = (MLoopCol *)BM_ELEM_CD_GET_VOID_P(elem, cd_vcol); + + vcol_data->r = unit_float_to_ushort_clamp( + BLI_color_from_srgb_table[mloopcol->r]); + vcol_data->g = unit_float_to_ushort_clamp( + BLI_color_from_srgb_table[mloopcol->r]); + vcol_data->b = unit_float_to_ushort_clamp( + BLI_color_from_srgb_table[mloopcol->r]); + vcol_data->a = unit_float_to_ushort_clamp(mloopcol->a * (1.0f / 255.0f)); + break; + } + } + + vcol_data++; + } while ((l_iter = l_iter->next) != BM_FACE_FIRST_LOOP(f)); + } } - } - else { - MPropCol *vcol = (MPropCol *)CustomData_get_layer_n(cd_vdata, CD_PROP_COLOR, i); - for (int ml_index = 0; ml_index < mr->loop_len; ml_index++, vcol_data++) { - vcol_data->r = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[0]); - vcol_data->g = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[1]); - vcol_data->b = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[2]); - vcol_data->a = unit_float_to_ushort_clamp(vcol[loops[ml_index].v].color[3]); + else { + using MPropCol3 = struct { + float color[3]; + }; + + switch (type) { + case CD_PROP_FLOAT3: { + MPropCol3 *colors = (MPropCol3 *)cdata->layers[idx].data; + + if (step) { + for (int k = 0; k < mr->loop_len; k++, vcol_data++, colors++) { + vcol_data->r = unit_float_to_ushort_clamp(colors->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(colors->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(colors->color[2]); + unit_float_to_ushort_clamp(1.0f); + } + } + else { + const MLoop *ml = mr->mloop; + + for (int k = 0; k < mr->loop_len; k++, vcol_data++, ml++) { + MPropCol3 *color = colors + ml->v; + + vcol_data->r = unit_float_to_ushort_clamp(color->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(color->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(color->color[2]); + vcol_data->a = unit_float_to_ushort_clamp(1.0f); + } + } + break; + } + case CD_PROP_COLOR: { + MPropCol *colors = (MPropCol *)cdata->layers[idx].data; + + if (step) { + for (int k = 0; k < mr->loop_len; k++, vcol_data++, colors++) { + vcol_data->r = unit_float_to_ushort_clamp(colors->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(colors->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(colors->color[2]); + vcol_data->a = unit_float_to_ushort_clamp(colors->color[3]); + } + } + else { + const MLoop *ml = mr->mloop; + + for (int k = 0; k < mr->loop_len; k++, vcol_data++, ml++) { + MPropCol *color = colors + ml->v; + + vcol_data->r = unit_float_to_ushort_clamp(color->color[0]); + vcol_data->g = unit_float_to_ushort_clamp(color->color[1]); + vcol_data->b = unit_float_to_ushort_clamp(color->color[2]); + vcol_data->a = unit_float_to_ushort_clamp(color->color[3]); + } + } + break; + } + case CD_MLOOPCOL: { + MLoopCol *colors = (MLoopCol *)cdata->layers[idx].data; + + if (step) { + for (int k = 0; k < mr->loop_len; k++, vcol_data++, colors++) { + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[colors->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[colors->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[colors->b]); + vcol_data->a = unit_float_to_ushort_clamp((float)colors->a * (1.0f / 255.0f)); + } + } + else { + const MLoop *ml = mr->mloop; + + for (int k = 0; k < mr->loop_len; k++, vcol_data++, ml++) { + MLoopCol *color = colors + ml->v; + + vcol_data->r = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[color->r]); + vcol_data->g = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[color->g]); + vcol_data->b = unit_float_to_ushort_clamp(BLI_color_from_srgb_table[color->b]); + vcol_data->a = unit_float_to_ushort_clamp((float)color->a * (1.0f / 255.0f)); + } + } + break; + } + } } } } diff --git a/source/blender/draw/intern/shaders/common_hair_lib.glsl b/source/blender/draw/intern/shaders/common_hair_lib.glsl index 02c335ddae2..6cc7f09a852 100644 --- a/source/blender/draw/intern/shaders/common_hair_lib.glsl +++ b/source/blender/draw/intern/shaders/common_hair_lib.glsl @@ -210,6 +210,12 @@ void hair_get_pos_tan_binor_time(bool is_persp, } } +float hair_get_customdata_float(const samplerBuffer cd_buf) +{ + int id = hair_get_strand_id(); + return texelFetch(cd_buf, id).r; +} + vec2 hair_get_customdata_vec2(const samplerBuffer cd_buf) { int id = hair_get_strand_id(); diff --git a/source/blender/draw/intern/smaa_textures.c b/source/blender/draw/intern/smaa_textures.c new file mode 100644 index 00000000000..b34a641d838 --- /dev/null +++ b/source/blender/draw/intern/smaa_textures.c @@ -0,0 +1,15068 @@ +/** + * Copyright (C) 2013 Jorge Jimenez + * Copyright (C) 2013 Jose I. Echevarria + * Copyright (C) 2013 Belen Masia + * Copyright (C) 2013 Fernando Navarro + * Copyright (C) 2013 Diego Gutierrez + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is furnished to + * do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. As clarification, there + * is no requirement that the copyright notice and permission be included in + * binary distributions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "smaa_textures.h" + +/* Don't re-wrap large data definitions. */ +/* clang-format off */ + +/** + * Stored in R8G8 format. Load it in the following format: + * - DX10: DXGI_FORMAT_R8G8_UNORM + */ +const unsigned char areaTexBytes[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x7b, 0x41, 0x5d, + 0x42, 0x54, 0x41, 0x4f, 0x42, 0x4b, 0x42, 0x49, 0x42, 0x48, 0x41, 0x47, + 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, + 0x42, 0x43, 0x41, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, + 0x04, 0x7f, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, + 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, + 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x7f, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x62, 0x20, 0x4d, 0x2a, 0x48, 0x30, 0x44, 0x33, + 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, 0x43, 0x39, 0x42, 0x39, + 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x41, 0x3b, + 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x00, 0x5d, 0x0c, 0x49, + 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, + 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, + 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x6d, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, 0x48, 0x26, 0x47, 0x29, + 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, + 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, 0x43, 0x36, 0x42, 0x36, + 0x42, 0x37, 0x42, 0x37, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, + 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, + 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, + 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, + 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x72, 0x06, 0x5f, 0x0d, + 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, 0x49, 0x23, 0x47, 0x25, + 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, 0x44, 0x2d, 0x44, 0x2e, + 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, 0x43, 0x32, 0x43, 0x33, + 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, + 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, + 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, + 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, + 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, 0x5a, 0x0e, 0x54, 0x13, + 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, 0x49, 0x22, 0x48, 0x23, + 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, + 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, 0x00, 0x70, 0x02, 0x60, + 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, + 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, + 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, 0x54, 0x12, 0x51, 0x15, + 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, 0x49, 0x21, 0x48, 0x22, + 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, 0x45, 0x28, 0x45, 0x29, + 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, + 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, + 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, + 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x78, 0x02, 0x6b, 0x05, + 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4e, 0x17, + 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x20, 0x48, 0x21, + 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, 0x46, 0x27, 0x45, 0x27, + 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, + 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, + 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, + 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, + 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, 0x65, 0x07, 0x5e, 0x0a, + 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4f, 0x16, 0x4e, 0x18, + 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x1f, 0x48, 0x21, + 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, 0x00, 0x75, 0x01, 0x69, + 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, + 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, + 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, + 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, 0x5d, 0x0b, 0x59, 0x0d, + 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, 0x4e, 0x17, 0x4d, 0x19, + 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, 0x49, 0x1f, 0x49, 0x20, + 0x48, 0x21, 0x48, 0x22, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, + 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, + 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, + 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x01, 0x71, 0x02, + 0x69, 0x05, 0x63, 0x07, 0x5f, 0x09, 0x5b, 0x0c, 0x58, 0x0e, 0x55, 0x10, + 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x4c, 0x19, + 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, 0x49, 0x1f, 0x49, 0x20, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, + 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, + 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, + 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, 0x6b, 0x04, 0x64, 0x06, + 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, + 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, 0x4d, 0x18, 0x4b, 0x1a, + 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, 0x00, 0x77, 0x00, 0x6e, + 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, + 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, + 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, + 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, 0x63, 0x07, 0x5e, 0x09, + 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, + 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, + 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, + 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, + 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, + 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, + 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x74, 0x02, + 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, 0x5d, 0x0a, 0x5a, 0x0b, + 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, + 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, + 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, + 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, + 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, 0x6f, 0x02, 0x69, 0x04, + 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, 0x5a, 0x0c, 0x58, 0x0d, + 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x13, 0x4f, 0x15, + 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, 0x00, 0x78, 0x00, 0x71, + 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, + 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, + 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, + 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, + 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, + 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, + 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, 0x67, 0x05, 0x63, 0x06, + 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, 0x57, 0x0d, 0x55, 0x0e, + 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, 0x50, 0x15, 0x4f, 0x15, + 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, + 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, + 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, + 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, + 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, + 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, + 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, 0x61, 0x07, 0x5e, 0x09, + 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, 0x55, 0x0e, 0x55, 0x11, + 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x15, + 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, + 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, + 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x1f, 0x00, 0x3f, + 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, + 0x00, 0x79, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, + 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x7e, 0x00, 0x7e, 0x1f, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, + 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, 0x72, 0x02, 0x6d, 0x03, + 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, 0x5e, 0x09, 0x5c, 0x0b, + 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x51, 0x11, + 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, 0x00, 0x79, 0x00, 0x73, + 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, + 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x3f, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x58, 0x00, 0x44, 0x00, 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, + 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, + 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, + 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, + 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, 0x6a, 0x03, 0x67, 0x05, + 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, 0x5b, 0x0b, 0x5a, 0x0c, + 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, 0x52, 0x11, 0x52, 0x12, + 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, + 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, + 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, + 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, + 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, + 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x55, + 0x00, 0x20, 0x00, 0x3e, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, + 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, + 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x66, 0x00, 0x3f, 0x00, + 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, + 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, + 0x00, 0x79, 0x00, 0x7a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x01, + 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, 0x65, 0x06, 0x61, 0x07, + 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0d, + 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, + 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, + 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, + 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, + 0x00, 0x73, 0x00, 0x75, 0x00, 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, + 0x00, 0x28, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, + 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, + 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, + 0x00, 0x73, 0x00, 0x75, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x01, + 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, + 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, 0x74, 0x01, 0x6f, 0x02, + 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, 0x61, 0x07, 0x5f, 0x09, + 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, 0x57, 0x0d, 0x55, 0x0e, + 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x00, 0x79, 0x00, 0x74, + 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, + 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, + 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x00, 0x79, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, + 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, + 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1b, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, + 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x21, 0x5d, 0x0c, 0x68, + 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, + 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, + 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, + 0x42, 0x40, 0x17, 0x55, 0x0c, 0x60, 0x08, 0x66, 0x05, 0x6a, 0x04, 0x6d, + 0x03, 0x6f, 0x02, 0x71, 0x02, 0x73, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, + 0x01, 0x76, 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, + 0x00, 0x78, 0x00, 0x78, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x21, 0x5d, 0x0c, 0x68, + 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, + 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, + 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x7b, 0x00, 0x72, + 0x00, 0x5a, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, + 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x7a, 0x00, 0x71, 0x00, + 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, + 0x00, 0x62, 0x00, 0x65, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, + 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, + 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, + 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x57, 0x16, 0x2d, 0x2c, + 0x1b, 0x3b, 0x12, 0x45, 0x0d, 0x4d, 0x0b, 0x53, 0x08, 0x57, 0x07, 0x5b, + 0x05, 0x5e, 0x05, 0x61, 0x04, 0x63, 0x04, 0x65, 0x03, 0x67, 0x02, 0x68, + 0x02, 0x69, 0x02, 0x6b, 0x02, 0x6c, 0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, + 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, + 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, + 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, + 0x00, 0x57, 0x00, 0x5b, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, + 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, + 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, + 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, + 0x00, 0x57, 0x00, 0x5b, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, + 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, + 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, + 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, + 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, + 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, + 0x02, 0x70, 0x01, 0x70, 0x61, 0x0c, 0x3b, 0x1b, 0x28, 0x28, 0x1d, 0x32, + 0x16, 0x3a, 0x11, 0x41, 0x0e, 0x47, 0x0c, 0x4b, 0x0a, 0x4f, 0x09, 0x53, + 0x07, 0x55, 0x06, 0x58, 0x05, 0x5a, 0x05, 0x5c, 0x05, 0x5e, 0x04, 0x60, + 0x04, 0x61, 0x04, 0x63, 0x03, 0x64, 0x02, 0x66, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, + 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, + 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, + 0x02, 0x70, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, + 0x00, 0x13, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, + 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, + 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x33, 0x41, 0x22, 0x46, + 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, + 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, + 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, + 0x67, 0x07, 0x45, 0x12, 0x32, 0x1d, 0x26, 0x26, 0x1e, 0x2e, 0x18, 0x34, + 0x14, 0x3a, 0x11, 0x3f, 0x0f, 0x44, 0x0c, 0x47, 0x0b, 0x4b, 0x0a, 0x4d, + 0x09, 0x51, 0x07, 0x52, 0x07, 0x55, 0x06, 0x57, 0x05, 0x58, 0x05, 0x5a, + 0x05, 0x5c, 0x04, 0x5d, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x33, 0x41, 0x22, 0x46, + 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, + 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, + 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, + 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x7d, 0x00, 0x79, + 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, + 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, + 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, + 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x7d, 0x00, 0x79, 0x00, + 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, + 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, + 0x00, 0x40, 0x00, 0x46, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, + 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, + 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x6b, 0x05, 0x4d, 0x0d, + 0x3b, 0x16, 0x2e, 0x1e, 0x25, 0x25, 0x1f, 0x2b, 0x1a, 0x31, 0x16, 0x36, + 0x13, 0x3a, 0x10, 0x3e, 0x0f, 0x42, 0x0d, 0x45, 0x0c, 0x47, 0x0a, 0x4a, + 0x0a, 0x4c, 0x09, 0x4f, 0x07, 0x51, 0x07, 0x52, 0x07, 0x54, 0x06, 0x56, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, + 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, + 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, + 0x00, 0x34, 0x00, 0x3b, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, + 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, + 0x00, 0x34, 0x00, 0x3b, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, + 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, + 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, + 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, + 0x04, 0x64, 0x04, 0x65, 0x6e, 0x04, 0x53, 0x0a, 0x41, 0x11, 0x34, 0x18, + 0x2b, 0x1f, 0x24, 0x24, 0x1f, 0x29, 0x1b, 0x2e, 0x17, 0x33, 0x15, 0x37, + 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x40, 0x0d, 0x43, 0x0c, 0x45, 0x0c, 0x48, + 0x0a, 0x4a, 0x0a, 0x4c, 0x09, 0x4e, 0x07, 0x4f, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, + 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, + 0x04, 0x64, 0x04, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, + 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x38, 0x3f, 0x2c, 0x41, + 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, + 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, + 0x70, 0x03, 0x58, 0x08, 0x47, 0x0e, 0x3a, 0x14, 0x31, 0x1a, 0x29, 0x1f, + 0x24, 0x24, 0x20, 0x28, 0x1c, 0x2d, 0x19, 0x31, 0x16, 0x34, 0x14, 0x37, + 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x3f, 0x0e, 0x42, 0x0c, 0x44, 0x0c, 0x46, + 0x0b, 0x47, 0x0a, 0x4a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x38, 0x3f, 0x2c, 0x41, + 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, + 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, + 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, + 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x7e, 0x00, 0x7c, 0x00, + 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, + 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, + 0x00, 0x1e, 0x00, 0x26, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, + 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, + 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x71, 0x02, 0x5b, 0x07, + 0x4c, 0x0c, 0x3f, 0x11, 0x36, 0x16, 0x2e, 0x1b, 0x29, 0x20, 0x24, 0x23, + 0x20, 0x28, 0x1d, 0x2b, 0x19, 0x2f, 0x17, 0x32, 0x16, 0x35, 0x12, 0x37, + 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3f, 0x0e, 0x41, 0x0c, 0x42, 0x0c, 0x45, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, + 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, + 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x13, 0x00, 0x1b, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, + 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, + 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, + 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x13, 0x00, 0x1b, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, + 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, + 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, + 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, + 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, + 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5c, 0x07, 0x5e, 0x73, 0x02, 0x5e, 0x05, 0x4f, 0x0a, 0x44, 0x0f, + 0x3a, 0x13, 0x33, 0x18, 0x2d, 0x1c, 0x27, 0x20, 0x23, 0x23, 0x20, 0x27, + 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x30, 0x16, 0x33, 0x14, 0x35, 0x12, 0x38, + 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3e, 0x0f, 0x41, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, + 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, + 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x07, 0x5c, 0x07, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, + 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, + 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, + 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, + 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, + 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x3a, 0x3e, 0x31, 0x40, + 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, + 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, + 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x74, 0x01, 0x62, 0x05, 0x53, 0x08, 0x47, 0x0c, 0x3e, 0x11, 0x37, 0x15, + 0x31, 0x19, 0x2b, 0x1d, 0x27, 0x20, 0x23, 0x23, 0x20, 0x26, 0x1d, 0x2a, + 0x1b, 0x2c, 0x19, 0x2f, 0x16, 0x31, 0x16, 0x34, 0x13, 0x35, 0x12, 0x38, + 0x12, 0x3b, 0x0f, 0x3b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x31, 0x40, + 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, + 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, + 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, + 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, + 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7e, 0x00, 0x7d, 0x00, + 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x08, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, + 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, + 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, + 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x75, 0x01, 0x64, 0x04, + 0x56, 0x07, 0x4b, 0x0b, 0x42, 0x0f, 0x3a, 0x12, 0x34, 0x16, 0x2f, 0x19, + 0x2a, 0x1d, 0x26, 0x20, 0x23, 0x23, 0x21, 0x26, 0x1d, 0x29, 0x1b, 0x2b, + 0x19, 0x2e, 0x18, 0x30, 0x16, 0x32, 0x15, 0x35, 0x12, 0x35, 0x12, 0x38, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, + 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, + 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, + 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, + 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, + 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, + 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, + 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, + 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, + 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, + 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, + 0x0b, 0x57, 0x0a, 0x58, 0x76, 0x01, 0x66, 0x04, 0x58, 0x06, 0x4d, 0x0a, + 0x45, 0x0d, 0x3e, 0x10, 0x37, 0x14, 0x32, 0x17, 0x2d, 0x1a, 0x2a, 0x1d, + 0x26, 0x21, 0x22, 0x22, 0x21, 0x26, 0x1d, 0x28, 0x1c, 0x2b, 0x19, 0x2c, + 0x19, 0x30, 0x16, 0x30, 0x16, 0x33, 0x14, 0x35, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, + 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, + 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, + 0x0b, 0x57, 0x0a, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x33, 0x3f, + 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, + 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, + 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, + 0x77, 0x01, 0x67, 0x03, 0x5a, 0x05, 0x51, 0x09, 0x48, 0x0c, 0x40, 0x0f, + 0x3a, 0x12, 0x35, 0x16, 0x30, 0x18, 0x2c, 0x1b, 0x29, 0x1d, 0x26, 0x21, + 0x22, 0x22, 0x21, 0x26, 0x1d, 0x27, 0x1d, 0x2b, 0x1a, 0x2b, 0x19, 0x2e, + 0x17, 0x30, 0x16, 0x31, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x33, 0x3f, + 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, + 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, + 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, + 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, + 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x77, 0x01, 0x68, 0x02, + 0x5d, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, 0x0d, 0x3d, 0x10, 0x38, 0x12, + 0x33, 0x16, 0x2f, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x21, 0x22, 0x22, + 0x21, 0x26, 0x1e, 0x26, 0x1d, 0x2a, 0x1a, 0x2b, 0x19, 0x2d, 0x18, 0x30, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, + 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, + 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, + 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, + 0x0d, 0x53, 0x0d, 0x53, 0x77, 0x00, 0x6a, 0x02, 0x5e, 0x04, 0x55, 0x07, + 0x4c, 0x0a, 0x45, 0x0c, 0x40, 0x0f, 0x3a, 0x12, 0x35, 0x14, 0x31, 0x16, + 0x2e, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x22, 0x21, 0x22, 0x21, 0x25, + 0x1e, 0x26, 0x1d, 0x29, 0x1b, 0x2b, 0x19, 0x2b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, + 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, + 0x0d, 0x53, 0x0d, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x35, 0x3f, + 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, + 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, + 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, + 0x78, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, 0x06, 0x4f, 0x09, 0x48, 0x0c, + 0x42, 0x0e, 0x3c, 0x10, 0x38, 0x12, 0x34, 0x16, 0x30, 0x18, 0x2d, 0x19, + 0x2b, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, + 0x1d, 0x28, 0x1c, 0x2b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x35, 0x3f, + 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, + 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, + 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, + 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, + 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, + 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x78, 0x00, 0x6d, 0x02, + 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, 0x0a, 0x44, 0x0c, 0x3f, 0x0f, + 0x3a, 0x12, 0x35, 0x14, 0x32, 0x16, 0x30, 0x19, 0x2b, 0x19, 0x2a, 0x1d, + 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, 0x1d, 0x27, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, + 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, + 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, + 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, + 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, + 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, + 0x11, 0x50, 0x0f, 0x50, 0x78, 0x00, 0x6d, 0x02, 0x63, 0x04, 0x5a, 0x05, + 0x53, 0x07, 0x4c, 0x0a, 0x46, 0x0c, 0x41, 0x0f, 0x3c, 0x0f, 0x38, 0x12, + 0x35, 0x16, 0x30, 0x16, 0x2e, 0x19, 0x2b, 0x1a, 0x29, 0x1d, 0x26, 0x1e, + 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1f, 0x26, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, + 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, + 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, + 0x11, 0x50, 0x0f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, + 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, + 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, + 0x78, 0x00, 0x6e, 0x01, 0x65, 0x03, 0x5c, 0x05, 0x54, 0x07, 0x4e, 0x09, + 0x48, 0x0b, 0x43, 0x0c, 0x3e, 0x0f, 0x3b, 0x12, 0x36, 0x12, 0x33, 0x16, + 0x30, 0x17, 0x2d, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x1e, 0x25, 0x22, + 0x21, 0x22, 0x21, 0x24, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, + 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, + 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, + 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, + 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, + 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x79, 0x00, 0x6f, 0x01, + 0x66, 0x02, 0x5d, 0x04, 0x56, 0x06, 0x4f, 0x07, 0x4a, 0x0a, 0x45, 0x0c, + 0x41, 0x0f, 0x3b, 0x0f, 0x38, 0x12, 0x35, 0x14, 0x31, 0x16, 0x30, 0x18, + 0x2b, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x1f, 0x24, 0x22, 0x21, 0x22, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, + 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, + 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, + 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, + 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x74, 0x00, 0x77, 0x00, 0x79, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x08, 0x00, 0x44, 0x00, + 0x56, 0x00, 0x61, 0x00, 0x67, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x70, 0x00, + 0x71, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x76, 0x00, + 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x44, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, + 0x79, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, + 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0b, 0x57, 0x06, + 0x5f, 0x03, 0x63, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6d, 0x01, 0x6e, 0x01, + 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, + 0x76, 0x00, 0x76, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, 0x00, + 0x82, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x40, 0x17, 0x17, 0x2c, 0x0c, 0x3b, 0x07, + 0x45, 0x05, 0x4d, 0x04, 0x53, 0x03, 0x57, 0x02, 0x5c, 0x02, 0x5e, 0x01, + 0x61, 0x01, 0x63, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x00, 0x69, 0x00, + 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x41, 0x20, 0x4d, 0x0b, + 0x58, 0x06, 0x5e, 0x03, 0x64, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6c, 0x01, + 0x6f, 0x01, 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, + 0x76, 0x00, 0x75, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x15, 0x4f, 0x0d, 0x56, 0x09, 0x59, 0x06, + 0x5f, 0x05, 0x62, 0x04, 0x65, 0x03, 0x67, 0x02, 0x69, 0x02, 0x6b, 0x02, + 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x70, 0x01, + 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x74, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x55, 0x0c, 0x2c, 0x1b, 0x1b, 0x27, 0x12, 0x32, 0x0d, 0x3a, 0x0b, + 0x41, 0x08, 0x47, 0x07, 0x4c, 0x05, 0x4f, 0x05, 0x53, 0x04, 0x56, 0x04, + 0x58, 0x03, 0x5a, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x61, 0x01, + 0x63, 0x01, 0x64, 0x01, 0x41, 0x2a, 0x47, 0x16, 0x4f, 0x0d, 0x54, 0x09, + 0x5b, 0x06, 0x5f, 0x05, 0x62, 0x04, 0x64, 0x03, 0x67, 0x02, 0x69, 0x02, + 0x6b, 0x02, 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x70, 0x01, + 0x71, 0x01, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x1d, 0x4a, 0x14, 0x50, 0x0e, 0x54, 0x0b, 0x59, 0x08, 0x5c, 0x07, + 0x5f, 0x06, 0x60, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, 0x68, 0x02, + 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6c, 0x02, 0x6e, 0x02, 0x6f, 0x01, + 0x70, 0x01, 0x70, 0x01, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x60, 0x08, 0x3b, + 0x12, 0x28, 0x1d, 0x1d, 0x26, 0x16, 0x2e, 0x11, 0x34, 0x0e, 0x3a, 0x0c, + 0x3f, 0x0a, 0x44, 0x08, 0x47, 0x07, 0x4b, 0x06, 0x4d, 0x05, 0x51, 0x05, + 0x52, 0x04, 0x55, 0x04, 0x57, 0x04, 0x58, 0x04, 0x5a, 0x03, 0x5c, 0x02, + 0x42, 0x30, 0x45, 0x1d, 0x4b, 0x14, 0x4f, 0x0e, 0x55, 0x0b, 0x59, 0x08, + 0x5c, 0x07, 0x5e, 0x06, 0x61, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, + 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6b, 0x02, 0x6d, 0x02, 0x6e, 0x02, + 0x6f, 0x01, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x22, 0x48, 0x19, + 0x4d, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5c, 0x07, + 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, + 0x68, 0x03, 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6d, 0x02, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x66, 0x05, 0x45, 0x0d, 0x32, 0x16, 0x26, + 0x1e, 0x1e, 0x25, 0x18, 0x2b, 0x14, 0x31, 0x11, 0x36, 0x0f, 0x3a, 0x0c, + 0x3e, 0x0b, 0x42, 0x0a, 0x45, 0x09, 0x47, 0x07, 0x4a, 0x07, 0x4c, 0x06, + 0x4f, 0x05, 0x51, 0x05, 0x52, 0x05, 0x54, 0x04, 0x41, 0x33, 0x44, 0x22, + 0x48, 0x19, 0x4c, 0x13, 0x51, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x08, + 0x5d, 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, + 0x67, 0x03, 0x67, 0x03, 0x69, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x26, 0x47, 0x1d, 0x4a, 0x17, 0x4d, 0x12, + 0x51, 0x0f, 0x54, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5b, 0x08, 0x5d, 0x07, + 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, + 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x69, 0x02, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x6a, 0x04, 0x4d, 0x0b, 0x3a, 0x11, 0x2e, 0x18, 0x25, 0x1f, 0x1f, + 0x24, 0x1a, 0x29, 0x16, 0x2e, 0x13, 0x33, 0x11, 0x37, 0x0f, 0x3a, 0x0d, + 0x3d, 0x0c, 0x40, 0x0a, 0x43, 0x0a, 0x45, 0x09, 0x48, 0x07, 0x4a, 0x07, + 0x4c, 0x07, 0x4e, 0x06, 0x41, 0x35, 0x43, 0x26, 0x47, 0x1d, 0x49, 0x17, + 0x4e, 0x12, 0x51, 0x0f, 0x54, 0x0d, 0x55, 0x0b, 0x59, 0x09, 0x5b, 0x08, + 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x63, 0x04, + 0x65, 0x03, 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x29, 0x45, 0x20, 0x49, 0x1a, 0x4b, 0x15, 0x4f, 0x12, 0x51, 0x0f, + 0x54, 0x0d, 0x55, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, 0x5d, 0x07, + 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x64, 0x05, 0x65, 0x04, + 0x65, 0x03, 0x66, 0x03, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x6d, 0x03, 0x52, + 0x08, 0x41, 0x0e, 0x34, 0x14, 0x2b, 0x1a, 0x24, 0x1f, 0x1f, 0x24, 0x1b, + 0x28, 0x17, 0x2d, 0x15, 0x31, 0x12, 0x34, 0x10, 0x37, 0x0f, 0x3a, 0x0d, + 0x3d, 0x0c, 0x40, 0x0b, 0x42, 0x0a, 0x44, 0x0a, 0x46, 0x09, 0x47, 0x07, + 0x41, 0x36, 0x43, 0x29, 0x46, 0x20, 0x48, 0x1a, 0x4c, 0x15, 0x4f, 0x12, + 0x51, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, + 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x60, 0x05, 0x62, 0x05, 0x64, 0x05, + 0x65, 0x04, 0x65, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2b, 0x45, 0x23, + 0x48, 0x1d, 0x49, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, 0x53, 0x0e, + 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, 0x5d, 0x07, + 0x5f, 0x07, 0x5f, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x05, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x6f, 0x02, 0x57, 0x07, 0x47, 0x0c, 0x3a, + 0x11, 0x31, 0x16, 0x29, 0x1b, 0x24, 0x20, 0x20, 0x23, 0x1c, 0x28, 0x19, + 0x2b, 0x16, 0x2f, 0x14, 0x32, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3a, 0x0e, + 0x3c, 0x0c, 0x3f, 0x0c, 0x41, 0x0b, 0x42, 0x0a, 0x41, 0x37, 0x43, 0x2b, + 0x45, 0x23, 0x47, 0x1d, 0x4a, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, + 0x53, 0x0e, 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, + 0x5d, 0x07, 0x5e, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, + 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, + 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, + 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, 0x00, 0x79, 0x00, 0x7b, 0x00, + 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, + 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x43, 0x2d, 0x44, 0x25, 0x46, 0x1f, 0x48, 0x1b, + 0x4b, 0x17, 0x4d, 0x14, 0x50, 0x12, 0x51, 0x10, 0x53, 0x0e, 0x55, 0x0c, + 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x08, 0x5c, 0x07, + 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x06, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x71, 0x02, 0x5b, 0x05, 0x4b, 0x0a, 0x3f, 0x0f, 0x36, 0x13, 0x2e, + 0x18, 0x29, 0x1c, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x27, 0x19, 0x2a, 0x17, + 0x2d, 0x16, 0x30, 0x12, 0x33, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3b, 0x0e, + 0x3c, 0x0c, 0x3e, 0x0c, 0x41, 0x38, 0x43, 0x2d, 0x44, 0x25, 0x45, 0x1f, + 0x49, 0x1b, 0x4b, 0x17, 0x4d, 0x14, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x0e, + 0x55, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5b, 0x08, + 0x5e, 0x07, 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x00, 0x00, 0x0a, 0x00, + 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, + 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x44, 0x00, + 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x75, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, + 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, + 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x42, 0x2f, 0x44, 0x27, 0x46, 0x22, 0x47, 0x1d, 0x4a, 0x19, 0x4c, 0x16, + 0x4e, 0x13, 0x4f, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, + 0x58, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x07, 0x5e, 0x07, + 0x5f, 0x07, 0x5f, 0x06, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x5e, + 0x05, 0x4f, 0x08, 0x44, 0x0c, 0x3a, 0x10, 0x33, 0x15, 0x2d, 0x19, 0x28, + 0x1d, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1a, 0x2a, 0x18, 0x2c, 0x16, + 0x2f, 0x14, 0x31, 0x12, 0x34, 0x12, 0x35, 0x0f, 0x38, 0x0f, 0x3b, 0x0f, + 0x41, 0x39, 0x42, 0x2f, 0x44, 0x27, 0x45, 0x22, 0x48, 0x1d, 0x4a, 0x19, + 0x4c, 0x16, 0x4d, 0x14, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, + 0x56, 0x0c, 0x58, 0x0b, 0x59, 0x0a, 0x59, 0x09, 0x5c, 0x09, 0x5c, 0x07, + 0x5e, 0x07, 0x5f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, + 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, + 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, + 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, + 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x55, 0x00, 0x20, 0x00, 0x3e, 0x00, + 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, + 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, + 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, + 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, + 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x42, 0x30, 0x44, 0x29, + 0x45, 0x23, 0x46, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, 0x4d, 0x13, + 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0b, + 0x59, 0x0b, 0x59, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, 0x5e, 0x07, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x61, 0x04, 0x52, 0x07, 0x47, + 0x0b, 0x3e, 0x0f, 0x37, 0x12, 0x31, 0x16, 0x2b, 0x19, 0x27, 0x1d, 0x23, + 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1b, 0x29, 0x19, 0x2b, 0x16, 0x2e, 0x16, + 0x30, 0x13, 0x32, 0x12, 0x35, 0x12, 0x36, 0x0f, 0x41, 0x39, 0x42, 0x30, + 0x44, 0x29, 0x44, 0x23, 0x47, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, + 0x4e, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, + 0x57, 0x0b, 0x57, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, + 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, + 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, + 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, + 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x3a, 0x00, + 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, + 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, + 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, + 0x73, 0x00, 0x75, 0x00, 0x42, 0x32, 0x44, 0x2a, 0x45, 0x25, 0x46, 0x21, + 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4c, 0x15, 0x4e, 0x13, 0x50, 0x11, + 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x57, 0x0c, 0x57, 0x0b, + 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x74, 0x01, 0x64, 0x04, 0x55, 0x06, 0x4a, 0x0a, 0x42, 0x0d, 0x3a, + 0x10, 0x34, 0x14, 0x2f, 0x17, 0x2a, 0x1a, 0x26, 0x1d, 0x23, 0x21, 0x21, + 0x22, 0x1d, 0x26, 0x1b, 0x28, 0x19, 0x2b, 0x18, 0x2c, 0x16, 0x30, 0x15, + 0x30, 0x12, 0x33, 0x12, 0x41, 0x3a, 0x42, 0x32, 0x44, 0x2a, 0x44, 0x25, + 0x46, 0x21, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4d, 0x15, 0x4e, 0x13, + 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x56, 0x0c, + 0x58, 0x0b, 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, + 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, + 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, + 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x6e, 0x00, + 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, + 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, + 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, + 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, + 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x45, 0x22, 0x48, 0x1f, 0x49, 0x1b, + 0x4b, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, + 0x53, 0x0e, 0x55, 0x0e, 0x55, 0x0c, 0x56, 0x0c, 0x57, 0x0b, 0x59, 0x0b, + 0x5a, 0x0a, 0x5a, 0x09, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x75, 0x01, 0x65, + 0x03, 0x58, 0x05, 0x4d, 0x09, 0x45, 0x0c, 0x3e, 0x0f, 0x37, 0x12, 0x32, + 0x16, 0x2d, 0x18, 0x2a, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, + 0x26, 0x1c, 0x27, 0x19, 0x2b, 0x19, 0x2b, 0x16, 0x2e, 0x16, 0x30, 0x14, + 0x41, 0x3a, 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x46, 0x22, 0x48, 0x1f, + 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, + 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0e, 0x54, 0x0c, 0x57, 0x0c, 0x57, 0x0b, + 0x59, 0x0b, 0x5a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, + 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, + 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, + 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x3a, 0x00, + 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, + 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, + 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, + 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, + 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x42, 0x33, 0x43, 0x2d, + 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x4a, 0x1a, 0x4a, 0x18, + 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, + 0x55, 0x0e, 0x54, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x0b, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x76, 0x01, 0x67, 0x02, 0x5a, 0x05, 0x50, + 0x07, 0x47, 0x0a, 0x40, 0x0d, 0x3a, 0x10, 0x35, 0x12, 0x30, 0x16, 0x2c, + 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, 0x26, 0x1d, + 0x26, 0x1a, 0x2a, 0x19, 0x2b, 0x17, 0x2d, 0x16, 0x41, 0x3b, 0x42, 0x33, + 0x43, 0x2d, 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x49, 0x1a, + 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, + 0x53, 0x0e, 0x54, 0x0e, 0x55, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, + 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, + 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, + 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, + 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, + 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, + 0x57, 0x00, 0x5b, 0x00, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x45, 0x25, + 0x46, 0x21, 0x48, 0x1e, 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x17, 0x4d, 0x15, + 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x53, 0x0e, + 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x57, 0x0b, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x68, 0x02, 0x5c, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, + 0x0c, 0x3d, 0x0f, 0x37, 0x12, 0x33, 0x14, 0x2f, 0x16, 0x2b, 0x19, 0x28, + 0x1c, 0x26, 0x1d, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1a, + 0x29, 0x19, 0x2b, 0x18, 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, + 0x45, 0x25, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1b, 0x4b, 0x19, 0x4c, 0x17, + 0x4d, 0x15, 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, + 0x54, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, + 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, + 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, + 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, + 0x42, 0x34, 0x43, 0x2f, 0x44, 0x2a, 0x44, 0x26, 0x46, 0x22, 0x47, 0x1f, + 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, 0x4e, 0x13, + 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, 0x54, 0x0e, 0x55, 0x0d, + 0x56, 0x0c, 0x57, 0x0c, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x69, + 0x02, 0x5e, 0x04, 0x55, 0x06, 0x4c, 0x09, 0x45, 0x0c, 0x3f, 0x0e, 0x3a, + 0x10, 0x35, 0x12, 0x31, 0x16, 0x2e, 0x18, 0x2b, 0x19, 0x27, 0x1d, 0x26, + 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1b, 0x28, 0x19, + 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x43, 0x2a, 0x45, 0x26, 0x46, 0x22, + 0x47, 0x1f, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, + 0x4e, 0x13, 0x50, 0x13, 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, + 0x55, 0x0d, 0x56, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, + 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, + 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, + 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, + 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, + 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, + 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x42, 0x35, 0x43, 0x30, + 0x44, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1c, + 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, 0x50, 0x12, + 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x55, 0x0c, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, + 0x05, 0x4f, 0x07, 0x48, 0x0a, 0x42, 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x34, + 0x14, 0x30, 0x16, 0x2c, 0x19, 0x2b, 0x19, 0x26, 0x1d, 0x25, 0x1e, 0x22, + 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1c, 0x41, 0x3b, 0x42, 0x35, + 0x43, 0x30, 0x43, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x47, 0x1e, + 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, + 0x50, 0x12, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, + 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, + 0x34, 0x00, 0x3b, 0x00, 0x42, 0x36, 0x43, 0x30, 0x44, 0x2c, 0x44, 0x28, + 0x45, 0x25, 0x46, 0x22, 0x48, 0x1f, 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x19, + 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x12, 0x50, 0x11, + 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, + 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x10, 0x35, 0x12, 0x32, 0x16, 0x30, + 0x16, 0x2b, 0x19, 0x2a, 0x1a, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, + 0x21, 0x1e, 0x25, 0x1d, 0x41, 0x3b, 0x42, 0x36, 0x43, 0x30, 0x43, 0x2c, + 0x44, 0x28, 0x45, 0x25, 0x46, 0x22, 0x47, 0x1f, 0x49, 0x1d, 0x49, 0x1a, + 0x4b, 0x19, 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x4f, 0x12, + 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, + 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, + 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, + 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, + 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, + 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, + 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, + 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, + 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, + 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, 0x46, 0x23, + 0x47, 0x20, 0x47, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x17, + 0x4d, 0x15, 0x4e, 0x14, 0x4f, 0x13, 0x4f, 0x12, 0x51, 0x11, 0x52, 0x11, + 0x52, 0x0f, 0x54, 0x0e, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6d, + 0x01, 0x62, 0x03, 0x5a, 0x05, 0x52, 0x07, 0x4c, 0x09, 0x46, 0x0b, 0x41, + 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x35, 0x12, 0x30, 0x16, 0x2e, 0x17, 0x2b, + 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1f, + 0x41, 0x3c, 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, + 0x46, 0x23, 0x46, 0x20, 0x48, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, + 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, 0x4e, 0x13, 0x50, 0x12, 0x51, 0x11, + 0x52, 0x11, 0x52, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, + 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, + 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, + 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, + 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, + 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, + 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, + 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x42, 0x37, 0x42, 0x32, + 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, 0x47, 0x1f, + 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, 0x4e, 0x15, + 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, + 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6e, 0x01, 0x64, 0x02, 0x5b, + 0x04, 0x54, 0x06, 0x4e, 0x07, 0x47, 0x0a, 0x42, 0x0c, 0x3e, 0x0f, 0x3b, + 0x0f, 0x35, 0x12, 0x33, 0x14, 0x30, 0x16, 0x2d, 0x19, 0x2b, 0x19, 0x28, + 0x1c, 0x26, 0x1d, 0x25, 0x1f, 0x22, 0x21, 0x22, 0x41, 0x3c, 0x42, 0x37, + 0x42, 0x32, 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, + 0x48, 0x1f, 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, + 0x4e, 0x15, 0x4d, 0x14, 0x50, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, + 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, + 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, + 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, + 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, + 0x13, 0x00, 0x1b, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x61, 0x20, 0x4d, 0x2a, + 0x48, 0x30, 0x44, 0x33, 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, + 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, + 0x42, 0x3b, 0x41, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, + 0x04, 0x3d, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, + 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, + 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, + 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x6c, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, + 0x48, 0x26, 0x47, 0x29, 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, + 0x44, 0x31, 0x44, 0x32, 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, + 0x43, 0x36, 0x42, 0x36, 0x42, 0x37, 0x42, 0x37, 0x00, 0x5d, 0x0c, 0x49, + 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, + 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, + 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, + 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, + 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, + 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, + 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, + 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, + 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x72, 0x06, 0x5f, 0x0d, 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, + 0x49, 0x23, 0x47, 0x25, 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, + 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, + 0x43, 0x32, 0x43, 0x33, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, + 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, + 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, + 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, + 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, + 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, + 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, + 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, + 0x00, 0x08, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, + 0x5a, 0x0e, 0x54, 0x13, 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, + 0x49, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, + 0x45, 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, + 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, + 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, + 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, + 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, + 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, + 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, + 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, + 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, + 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, + 0x54, 0x12, 0x51, 0x15, 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, + 0x49, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, + 0x45, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x70, 0x02, 0x60, + 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, + 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, + 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, + 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, + 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x78, 0x02, 0x6b, 0x05, 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, + 0x51, 0x14, 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, + 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, + 0x46, 0x27, 0x45, 0x27, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, + 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, + 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, + 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, + 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, + 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, + 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, + 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, + 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, + 0x65, 0x07, 0x5e, 0x0a, 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, + 0x4f, 0x16, 0x4e, 0x18, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, + 0x49, 0x1f, 0x48, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, + 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, + 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, + 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, + 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x01, 0x01, + 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, + 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, + 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, + 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, + 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, + 0x5d, 0x0b, 0x59, 0x0d, 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, + 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, + 0x49, 0x1f, 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x00, 0x75, 0x01, 0x69, + 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, + 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x1b, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, + 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, + 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, + 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7b, 0x01, 0x71, 0x02, 0x69, 0x05, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0c, + 0x58, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, + 0x4e, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, + 0x49, 0x1f, 0x49, 0x20, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, + 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, + 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, + 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, + 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, + 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, + 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, + 0x6b, 0x04, 0x64, 0x06, 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, + 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, + 0x4d, 0x18, 0x4b, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, + 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, + 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, + 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, + 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, + 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, + 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, + 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, + 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, + 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, + 0x63, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, + 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, + 0x4c, 0x19, 0x4c, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6e, + 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, + 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, + 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, + 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, + 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, + 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7c, 0x00, 0x74, 0x02, 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, + 0x5d, 0x0a, 0x5a, 0x0b, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, + 0x52, 0x13, 0x50, 0x14, 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, + 0x4c, 0x19, 0x4c, 0x1a, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, + 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, + 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, + 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, + 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, + 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, + 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, + 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, + 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, + 0x6f, 0x02, 0x69, 0x04, 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, + 0x5a, 0x0c, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, + 0x50, 0x13, 0x4f, 0x15, 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, + 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, + 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, + 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, + 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, + 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, + 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, + 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, + 0x67, 0x05, 0x63, 0x06, 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, + 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, + 0x50, 0x15, 0x4f, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x78, 0x00, 0x71, + 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, + 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, + 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, + 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, + 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7d, 0x00, 0x76, 0x01, 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, + 0x61, 0x07, 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, + 0x55, 0x0e, 0x55, 0x11, 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, + 0x4f, 0x15, 0x4e, 0x15, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, + 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, + 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, + 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, + 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, + 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, + 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, + 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, + 0x72, 0x02, 0x6d, 0x03, 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, + 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, + 0x54, 0x11, 0x51, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, + 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, + 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, + 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, + 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, + 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, + 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, + 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, + 0x6a, 0x03, 0x67, 0x05, 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, + 0x5b, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, + 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, + 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, + 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, + 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, + 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, + 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, + 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, + 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x7e, 0x00, 0x78, 0x01, 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, + 0x65, 0x06, 0x62, 0x07, 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, + 0x59, 0x0c, 0x57, 0x0d, 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, + 0x52, 0x12, 0x51, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, + 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, + 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, + 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, + 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, + 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, + 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, + 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, + 0x74, 0x01, 0x6f, 0x02, 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, + 0x61, 0x07, 0x5f, 0x09, 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, + 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, + 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, + 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, + 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, + 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, + 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, + 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, + 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x79, 0x00, 0x74, 0x01, 0x70, 0x02, + 0x6c, 0x03, 0x69, 0x03, 0x66, 0x05, 0x63, 0x06, 0x62, 0x07, 0x5f, 0x07, + 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, + 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x00, 0x79, 0x00, 0x74, + 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, + 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, + 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x66, 0x7f, 0x4d, 0x68, 0x49, 0x5c, 0x47, 0x55, 0x45, 0x51, 0x45, 0x4e, + 0x44, 0x4c, 0x43, 0x4a, 0x43, 0x49, 0x43, 0x48, 0x43, 0x47, 0x42, 0x47, + 0x42, 0x46, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, + 0x42, 0x44, 0x42, 0x44, 0x57, 0x7f, 0x2e, 0x58, 0x34, 0x4d, 0x37, 0x49, + 0x39, 0x47, 0x3a, 0x45, 0x3b, 0x44, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, + 0x3c, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x99, 0x7f, 0x4d, 0x58, + 0x49, 0x4d, 0x47, 0x49, 0x45, 0x47, 0x45, 0x45, 0x44, 0x44, 0x43, 0x44, + 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, + 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa5, 0x16, 0x74, 0x23, + 0x62, 0x29, 0x59, 0x2d, 0x53, 0x30, 0x50, 0x32, 0x4e, 0x34, 0x4c, 0x35, + 0x4a, 0x36, 0x49, 0x37, 0x48, 0x38, 0x48, 0x38, 0x47, 0x39, 0x46, 0x39, + 0x46, 0x39, 0x46, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x44, 0x3b, + 0x23, 0x2f, 0x25, 0x35, 0x2a, 0x37, 0x2e, 0x39, 0x31, 0x3a, 0x33, 0x3b, + 0x34, 0x3b, 0x35, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x38, 0x3d, + 0x39, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x77, 0x27, 0x5f, 0x31, 0x56, 0x35, 0x52, 0x38, + 0x4f, 0x39, 0x4c, 0x3a, 0x4b, 0x3b, 0x49, 0x3b, 0x48, 0x3c, 0x48, 0x3c, + 0x47, 0x3c, 0x47, 0x3c, 0x46, 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, + 0x45, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, 0x00, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xbb, 0x01, 0x91, 0x09, 0x78, 0x11, 0x6b, 0x17, + 0x62, 0x1c, 0x5c, 0x20, 0x58, 0x23, 0x55, 0x25, 0x52, 0x28, 0x51, 0x2a, + 0x4f, 0x2b, 0x4e, 0x2c, 0x4d, 0x2e, 0x4c, 0x2f, 0x4b, 0x30, 0x4a, 0x31, + 0x49, 0x31, 0x49, 0x32, 0x48, 0x33, 0x47, 0x33, 0x22, 0x25, 0x23, 0x2a, + 0x26, 0x2e, 0x2a, 0x31, 0x2c, 0x33, 0x2e, 0x34, 0x30, 0x36, 0x31, 0x37, + 0x32, 0x37, 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x3a, + 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3a, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, + 0x7a, 0x1a, 0x68, 0x24, 0x5f, 0x2a, 0x5a, 0x2e, 0x55, 0x31, 0x53, 0x33, + 0x51, 0x34, 0x4f, 0x35, 0x4d, 0x36, 0x4c, 0x37, 0x4b, 0x38, 0x4b, 0x38, + 0x4a, 0x39, 0x49, 0x39, 0x49, 0x39, 0x48, 0x3a, 0x47, 0x3a, 0x47, 0x3a, + 0x46, 0x3b, 0x46, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x39, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xc6, 0x00, 0xa3, 0x02, 0x8a, 0x07, 0x7a, 0x0c, 0x6f, 0x11, 0x68, 0x15, + 0x62, 0x18, 0x5e, 0x1b, 0x5b, 0x1d, 0x58, 0x20, 0x56, 0x22, 0x54, 0x23, + 0x53, 0x25, 0x51, 0x26, 0x50, 0x28, 0x4e, 0x29, 0x4d, 0x2a, 0x4d, 0x2b, + 0x4d, 0x2c, 0x4c, 0x2c, 0x22, 0x23, 0x22, 0x27, 0x25, 0x2a, 0x27, 0x2c, + 0x2a, 0x2e, 0x2b, 0x30, 0x2d, 0x32, 0x2e, 0x33, 0x30, 0x34, 0x31, 0x34, + 0x32, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x34, 0x37, 0x35, 0x38, + 0x36, 0x38, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x7b, 0x16, 0x6d, 0x1f, + 0x65, 0x24, 0x5f, 0x28, 0x5b, 0x2b, 0x58, 0x2d, 0x55, 0x2f, 0x54, 0x31, + 0x52, 0x32, 0x50, 0x33, 0x4f, 0x34, 0x4e, 0x35, 0x4d, 0x35, 0x4c, 0x36, + 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x4a, 0x38, 0x4a, 0x39, 0x49, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, + 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xcc, 0x00, 0xaf, 0x00, + 0x98, 0x03, 0x87, 0x06, 0x7b, 0x0a, 0x73, 0x0e, 0x6c, 0x11, 0x67, 0x13, + 0x63, 0x16, 0x5f, 0x18, 0x5c, 0x1a, 0x5a, 0x1c, 0x58, 0x1e, 0x56, 0x20, + 0x55, 0x21, 0x54, 0x22, 0x52, 0x24, 0x51, 0x25, 0x50, 0x26, 0x4f, 0x27, + 0x22, 0x22, 0x22, 0x25, 0x24, 0x27, 0x26, 0x2a, 0x28, 0x2b, 0x29, 0x2d, + 0x2b, 0x2f, 0x2c, 0x30, 0x2d, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, + 0x31, 0x34, 0x32, 0x35, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, + 0x35, 0x36, 0x35, 0x37, 0x7c, 0x15, 0x71, 0x1b, 0x68, 0x20, 0x63, 0x24, + 0x5f, 0x27, 0x5c, 0x29, 0x59, 0x2c, 0x57, 0x2d, 0x55, 0x2f, 0x54, 0x30, + 0x53, 0x31, 0x51, 0x32, 0x50, 0x32, 0x4f, 0x33, 0x4f, 0x34, 0x4e, 0x35, + 0x4d, 0x35, 0x4c, 0x35, 0x4b, 0x35, 0x4a, 0x36, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, + 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, + 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xcf, 0x00, 0xb7, 0x00, 0xa2, 0x00, 0x92, 0x03, + 0x85, 0x06, 0x7c, 0x09, 0x75, 0x0b, 0x6f, 0x0e, 0x6a, 0x11, 0x66, 0x13, + 0x63, 0x15, 0x5f, 0x17, 0x5e, 0x19, 0x5c, 0x1a, 0x5a, 0x1c, 0x57, 0x1d, + 0x56, 0x1f, 0x55, 0x1f, 0x55, 0x21, 0x54, 0x22, 0x22, 0x22, 0x22, 0x24, + 0x23, 0x26, 0x25, 0x28, 0x26, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, + 0x2c, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x31, 0x30, 0x32, 0x30, 0x33, + 0x30, 0x33, 0x32, 0x33, 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, + 0x7c, 0x14, 0x73, 0x19, 0x6c, 0x1e, 0x67, 0x22, 0x63, 0x24, 0x5f, 0x26, + 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2f, + 0x53, 0x30, 0x52, 0x31, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4f, 0x33, + 0x4f, 0x34, 0x4e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, + 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd2, 0x00, 0xbd, 0x00, 0xaa, 0x00, 0x9b, 0x01, 0x8e, 0x03, 0x84, 0x05, + 0x7c, 0x08, 0x76, 0x0a, 0x71, 0x0c, 0x6c, 0x0f, 0x69, 0x11, 0x66, 0x12, + 0x63, 0x14, 0x60, 0x16, 0x5e, 0x17, 0x5d, 0x19, 0x5b, 0x1a, 0x59, 0x1b, + 0x57, 0x1c, 0x56, 0x1e, 0x22, 0x22, 0x22, 0x23, 0x23, 0x25, 0x24, 0x27, + 0x25, 0x28, 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, + 0x2d, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x32, 0x30, 0x33, + 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x7d, 0x13, 0x75, 0x18, + 0x6e, 0x1c, 0x69, 0x1f, 0x65, 0x22, 0x62, 0x24, 0x60, 0x26, 0x5c, 0x28, + 0x5b, 0x29, 0x59, 0x2a, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x53, 0x31, 0x51, 0x31, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd3, 0x00, 0xc2, 0x00, + 0xb1, 0x00, 0xa2, 0x00, 0x96, 0x02, 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x07, + 0x77, 0x09, 0x73, 0x0b, 0x6f, 0x0d, 0x6a, 0x0f, 0x68, 0x11, 0x66, 0x12, + 0x63, 0x14, 0x60, 0x15, 0x5f, 0x16, 0x5e, 0x18, 0x5c, 0x19, 0x5a, 0x1a, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x29, + 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x31, 0x30, 0x32, + 0x30, 0x33, 0x30, 0x33, 0x7d, 0x13, 0x76, 0x17, 0x70, 0x1b, 0x6b, 0x1e, + 0x68, 0x20, 0x65, 0x22, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x29, + 0x5a, 0x2a, 0x58, 0x2b, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x53, 0x30, 0x53, 0x31, 0x51, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, + 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, + 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd4, 0x00, 0xc5, 0x00, 0xb6, 0x00, 0xa8, 0x00, + 0x9c, 0x00, 0x92, 0x02, 0x8a, 0x03, 0x82, 0x05, 0x7d, 0x07, 0x78, 0x09, + 0x74, 0x0a, 0x71, 0x0c, 0x6c, 0x0e, 0x69, 0x0f, 0x68, 0x11, 0x65, 0x12, + 0x63, 0x13, 0x60, 0x14, 0x5f, 0x16, 0x5e, 0x17, 0x21, 0x22, 0x22, 0x23, + 0x22, 0x24, 0x23, 0x25, 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x29, + 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x31, + 0x7d, 0x12, 0x77, 0x16, 0x71, 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x66, 0x20, + 0x64, 0x23, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x28, 0x5b, 0x2a, + 0x58, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, + 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, + 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd5, 0x00, 0xc7, 0x00, 0xba, 0x00, 0xad, 0x00, 0xa2, 0x00, 0x98, 0x01, + 0x8f, 0x02, 0x88, 0x03, 0x82, 0x05, 0x7e, 0x07, 0x78, 0x08, 0x74, 0x0a, + 0x72, 0x0b, 0x6e, 0x0c, 0x6b, 0x0e, 0x69, 0x0f, 0x67, 0x11, 0x65, 0x12, + 0x63, 0x13, 0x60, 0x14, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x27, 0x29, 0x28, 0x2b, + 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x7d, 0x12, 0x77, 0x15, + 0x72, 0x18, 0x6e, 0x1b, 0x6b, 0x1d, 0x68, 0x20, 0x65, 0x21, 0x64, 0x23, + 0x60, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x59, 0x2a, + 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, + 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, + 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, + 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd6, 0x00, 0xc9, 0x00, + 0xbd, 0x00, 0xb2, 0x00, 0xa7, 0x00, 0x9d, 0x00, 0x95, 0x01, 0x8e, 0x02, + 0x87, 0x03, 0x82, 0x05, 0x7e, 0x06, 0x79, 0x08, 0x75, 0x09, 0x73, 0x0b, + 0x70, 0x0c, 0x6c, 0x0d, 0x6a, 0x0e, 0x68, 0x0f, 0x67, 0x11, 0x65, 0x12, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x26, + 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, + 0x2d, 0x2f, 0x2d, 0x30, 0x7d, 0x12, 0x78, 0x15, 0x73, 0x18, 0x70, 0x1a, + 0x6d, 0x1d, 0x69, 0x1e, 0x67, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x24, + 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2a, + 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, + 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, + 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd7, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb5, 0x00, + 0xab, 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x92, 0x02, 0x8d, 0x02, 0x87, 0x03, + 0x81, 0x05, 0x7e, 0x06, 0x79, 0x07, 0x76, 0x09, 0x74, 0x0a, 0x71, 0x0b, + 0x6e, 0x0c, 0x6b, 0x0d, 0x6a, 0x0e, 0x68, 0x10, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x24, 0x23, 0x25, 0x24, 0x25, 0x25, 0x27, 0x25, 0x27, + 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, + 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, + 0x7e, 0x12, 0x79, 0x15, 0x75, 0x17, 0x71, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, + 0x69, 0x1f, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x25, + 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5c, 0x29, 0x59, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, + 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, + 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd7, 0x00, 0xcc, 0x00, 0xc2, 0x00, 0xb9, 0x00, 0xae, 0x00, 0xa6, 0x00, + 0x9e, 0x00, 0x97, 0x01, 0x90, 0x02, 0x8b, 0x02, 0x85, 0x04, 0x81, 0x05, + 0x7e, 0x06, 0x7a, 0x07, 0x76, 0x08, 0x74, 0x09, 0x72, 0x0b, 0x6f, 0x0c, + 0x6c, 0x0c, 0x6a, 0x0e, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, + 0x26, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x7e, 0x12, 0x79, 0x15, + 0x75, 0x17, 0x72, 0x19, 0x6e, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x20, + 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x5a, 0x2a, 0x58, 0x2a, 0x57, 0x2a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, + 0x00, 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xce, 0x00, + 0xc5, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa1, 0x00, 0x9b, 0x00, + 0x94, 0x01, 0x8f, 0x02, 0x8a, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, + 0x7b, 0x07, 0x77, 0x08, 0x75, 0x09, 0x73, 0x0a, 0x71, 0x0b, 0x6e, 0x0c, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, + 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, + 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, + 0x2b, 0x2d, 0x2b, 0x2d, 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x16, 0x72, 0x18, + 0x6f, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, + 0x64, 0x23, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x28, 0x5b, 0x2a, 0x59, 0x2a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, + 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, + 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, + 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, + 0xb5, 0x00, 0xad, 0x00, 0xa6, 0x00, 0x9e, 0x00, 0x99, 0x00, 0x92, 0x01, + 0x8e, 0x02, 0x89, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7b, 0x07, + 0x77, 0x07, 0x75, 0x09, 0x73, 0x09, 0x72, 0x0b, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, + 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, + 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x15, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, + 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, + 0x00, 0x2d, 0x00, 0x28, 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, + 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, + 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, + 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbf, 0x00, 0xb8, 0x00, 0xaf, 0x00, + 0xa9, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x01, 0x90, 0x02, 0x8d, 0x02, + 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x07, 0x77, 0x07, + 0x76, 0x09, 0x74, 0x09, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, + 0x26, 0x27, 0x27, 0x27, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7a, 0x13, + 0x76, 0x15, 0x74, 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, + 0x69, 0x1e, 0x67, 0x20, 0x65, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x58, 0x00, 0x3d, + 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x0b, 0x07, 0x17, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x47, 0x07, 0x0f, 0x0f, + 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, + 0x00, 0x3f, 0x00, 0x3f, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd1, 0x00, + 0xc9, 0x00, 0xc1, 0x00, 0xba, 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, + 0x9e, 0x00, 0x9a, 0x00, 0x93, 0x01, 0x8f, 0x02, 0x8c, 0x02, 0x88, 0x03, + 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x06, 0x78, 0x07, 0x76, 0x08, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, + 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, + 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x75, 0x17, + 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x69, 0x1f, + 0x65, 0x20, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x25, 0x5f, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, + 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, + 0x00, 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x27, 0x02, + 0x02, 0x12, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, + 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3e, 0x7f, 0x00, 0x3f, 0x00, 0x05, 0x05, 0x00, 0x1c, + 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, + 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc3, 0x00, + 0xbb, 0x00, 0xb5, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, + 0x98, 0x00, 0x92, 0x01, 0x8e, 0x02, 0x8b, 0x02, 0x87, 0x03, 0x83, 0x04, + 0x81, 0x05, 0x7f, 0x06, 0x7c, 0x06, 0x78, 0x07, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, + 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, + 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, + 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, + 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x68, 0x20, 0x65, 0x20, + 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, + 0x5f, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, + 0x00, 0x24, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, + 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, + 0x00, 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, + 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, + 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x6f, 0x00, 0x22, 0x00, 0x09, 0x0b, + 0x00, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x35, + 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, + 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x19, + 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, + 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb7, 0x00, + 0xb0, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x95, 0x01, + 0x91, 0x01, 0x8e, 0x02, 0x8a, 0x02, 0x86, 0x03, 0x83, 0x04, 0x81, 0x05, + 0x7f, 0x06, 0x7c, 0x06, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, + 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x7f, 0x11, 0x7b, 0x13, + 0x77, 0x15, 0x76, 0x16, 0x72, 0x18, 0x72, 0x19, 0x6d, 0x1a, 0x6d, 0x1b, + 0x6b, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, + 0x64, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x5f, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, + 0x00, 0x25, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, + 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x42, + 0x00, 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, + 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, + 0x00, 0x39, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, 0x00, 0x0e, 0x08, 0x02, 0x0e, + 0x00, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x31, + 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0xb2, 0x00, 0x9c, 0x00, + 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, + 0x00, 0x33, 0x00, 0x35, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xda, 0x00, 0xd2, 0x00, + 0xcb, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xac, 0x00, + 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x01, 0x90, 0x02, + 0x8d, 0x02, 0x89, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, + 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, + 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x29, 0x7f, 0x11, 0x7b, 0x13, 0x78, 0x15, 0x76, 0x15, + 0x73, 0x18, 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, + 0x69, 0x1d, 0x69, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, + 0x00, 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, + 0x00, 0x35, 0x00, 0x37, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, + 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x00, 0xa4, 0x00, + 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x00, 0x12, 0x06, 0x07, 0x0c, 0x00, 0x10, + 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x2e, + 0x00, 0x30, 0x00, 0x32, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, + 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, + 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, + 0x33, 0x22, 0x28, 0x22, 0x25, 0x22, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, + 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, + 0x22, 0x21, 0x22, 0x21, 0x9d, 0x05, 0x6b, 0x0d, 0x51, 0x12, 0x45, 0x15, + 0x3d, 0x17, 0x38, 0x18, 0x35, 0x1a, 0x32, 0x1b, 0x30, 0x1b, 0x2f, 0x1c, + 0x2d, 0x1c, 0x2d, 0x1c, 0x2c, 0x1d, 0x2b, 0x1d, 0x2a, 0x1e, 0x2a, 0x1e, + 0x29, 0x1e, 0x29, 0x1e, 0x28, 0x1e, 0x28, 0x1e, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x5f, 0x13, 0x46, 0x17, 0x39, 0x1a, 0x33, 0x1b, 0x2f, 0x1c, 0x2d, 0x1d, + 0x2b, 0x1e, 0x2a, 0x1e, 0x29, 0x1e, 0x28, 0x1f, 0x27, 0x1f, 0x27, 0x1f, + 0x27, 0x1f, 0x26, 0x1f, 0x26, 0x20, 0x26, 0x20, 0x25, 0x20, 0x25, 0x20, + 0x25, 0x20, 0x25, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, + 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, + 0x00, 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, + 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, 0xad, 0x00, 0x8f, 0x00, 0x68, 0x00, + 0x42, 0x00, 0x20, 0x00, 0x14, 0x05, 0x0b, 0x0a, 0x04, 0x0d, 0x00, 0x12, + 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2c, + 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, + 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x13, + 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x37, 0x28, 0x2d, 0x25, + 0x28, 0x23, 0x26, 0x23, 0x25, 0x23, 0x24, 0x22, 0x24, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0xbc, 0x00, 0x8e, 0x01, 0x73, 0x04, 0x60, 0x08, 0x55, 0x0b, 0x4c, 0x0d, + 0x47, 0x0f, 0x42, 0x11, 0x3e, 0x12, 0x3b, 0x13, 0x39, 0x14, 0x37, 0x15, + 0x36, 0x16, 0x34, 0x16, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x19, + 0x2f, 0x19, 0x2f, 0x19, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x6f, 0x11, 0x58, 0x11, + 0x4a, 0x13, 0x41, 0x15, 0x3b, 0x16, 0x37, 0x17, 0x34, 0x18, 0x32, 0x19, + 0x30, 0x1a, 0x2e, 0x1a, 0x2d, 0x1b, 0x2c, 0x1b, 0x2c, 0x1c, 0x2b, 0x1c, + 0x2a, 0x1c, 0x2a, 0x1d, 0x29, 0x1d, 0x29, 0x1d, 0x28, 0x1d, 0x28, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, + 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x3e, 0x00, 0x3a, + 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, + 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, 0x00, 0x5c, 0x00, 0x3c, 0x00, + 0x20, 0x00, 0x16, 0x04, 0x0e, 0x08, 0x07, 0x0c, 0x02, 0x0e, 0x00, 0x13, + 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0xbb, 0x00, 0xb5, 0x00, + 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, + 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x12, + 0x00, 0x17, 0x00, 0x1c, 0x38, 0x2c, 0x30, 0x28, 0x2b, 0x26, 0x29, 0x25, + 0x27, 0x24, 0x26, 0x24, 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xc6, 0x00, 0xa4, 0x00, + 0x89, 0x00, 0x77, 0x02, 0x67, 0x04, 0x5e, 0x06, 0x55, 0x08, 0x50, 0x0a, + 0x4b, 0x0c, 0x47, 0x0d, 0x44, 0x0e, 0x41, 0x0f, 0x3e, 0x10, 0x3d, 0x11, + 0x3b, 0x12, 0x39, 0x12, 0x38, 0x13, 0x37, 0x14, 0x35, 0x15, 0x35, 0x16, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x74, 0x11, 0x63, 0x11, 0x55, 0x11, 0x4c, 0x12, + 0x44, 0x13, 0x40, 0x14, 0x3b, 0x15, 0x39, 0x16, 0x36, 0x17, 0x34, 0x17, + 0x33, 0x18, 0x31, 0x18, 0x30, 0x19, 0x2f, 0x19, 0x2e, 0x1a, 0x2d, 0x1a, + 0x2d, 0x1a, 0x2c, 0x1b, 0x2b, 0x1b, 0x2b, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, + 0x00, 0x26, 0x00, 0x28, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, + 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, + 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb5, 0x00, + 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, 0x00, 0x37, 0x00, 0x20, 0x00, + 0x17, 0x04, 0x10, 0x07, 0x0a, 0x0a, 0x05, 0x0d, 0x00, 0x0f, 0x00, 0x14, + 0x00, 0x19, 0x00, 0x1d, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, + 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, + 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, + 0x3a, 0x2f, 0x32, 0x2b, 0x2e, 0x28, 0x2b, 0x27, 0x28, 0x25, 0x27, 0x25, + 0x27, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, + 0x24, 0x23, 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, + 0x78, 0x01, 0x6c, 0x03, 0x64, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x51, 0x09, + 0x4e, 0x0a, 0x4a, 0x0b, 0x48, 0x0c, 0x45, 0x0c, 0x43, 0x0e, 0x40, 0x0f, + 0x3f, 0x0f, 0x3d, 0x0f, 0x3c, 0x11, 0x3b, 0x12, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x77, 0x11, 0x69, 0x11, 0x5e, 0x11, 0x54, 0x11, 0x4d, 0x11, 0x47, 0x12, + 0x43, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x39, 0x15, 0x38, 0x16, 0x36, 0x16, + 0x35, 0x17, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x18, 0x2f, 0x18, + 0x2f, 0x19, 0x2e, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, + 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, + 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xaa, 0x00, 0x96, 0x00, + 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x20, 0x00, 0x18, 0x03, + 0x11, 0x06, 0x0c, 0x09, 0x07, 0x0c, 0x03, 0x0e, 0x00, 0x10, 0x00, 0x15, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, + 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, + 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3b, 0x32, 0x34, 0x2d, + 0x2f, 0x2a, 0x2c, 0x28, 0x2a, 0x27, 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, + 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, + 0xcf, 0x00, 0xb9, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x01, + 0x70, 0x02, 0x68, 0x03, 0x61, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x53, 0x07, + 0x50, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0c, 0x45, 0x0c, 0x44, 0x0c, + 0x42, 0x0d, 0x41, 0x0f, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x78, 0x11, 0x6d, 0x11, + 0x63, 0x11, 0x5b, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x49, 0x12, 0x45, 0x12, + 0x41, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x3a, 0x14, 0x39, 0x15, 0x37, 0x16, + 0x36, 0x16, 0x35, 0x17, 0x33, 0x17, 0x33, 0x17, 0x32, 0x17, 0x31, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, + 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x3e, 0x00, 0x3d, + 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, + 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, 0x00, 0x89, 0x00, 0x73, 0x00, + 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x19, 0x03, 0x13, 0x06, + 0x0e, 0x08, 0x09, 0x0b, 0x05, 0x0d, 0x01, 0x0e, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, + 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, + 0x0b, 0x00, 0x03, 0x00, 0x3c, 0x34, 0x35, 0x2f, 0x31, 0x2c, 0x2e, 0x2a, + 0x2c, 0x28, 0x2a, 0x27, 0x28, 0x27, 0x28, 0x26, 0x27, 0x25, 0x27, 0x25, + 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0xd2, 0x00, 0xbe, 0x00, + 0xad, 0x00, 0x9d, 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, + 0x6b, 0x02, 0x65, 0x04, 0x60, 0x04, 0x5b, 0x05, 0x58, 0x06, 0x54, 0x07, + 0x51, 0x07, 0x4f, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0b, 0x46, 0x0c, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7a, 0x11, 0x70, 0x11, 0x67, 0x11, 0x5f, 0x11, + 0x59, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x46, 0x12, 0x43, 0x13, + 0x41, 0x13, 0x3e, 0x13, 0x3d, 0x14, 0x3b, 0x14, 0x39, 0x14, 0x38, 0x15, + 0x37, 0x16, 0x36, 0x16, 0x35, 0x16, 0x34, 0x17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, + 0x00, 0x14, 0x00, 0x18, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, + 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, 0x00, 0x6a, 0x00, 0x56, 0x00, + 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x19, 0x03, 0x14, 0x05, 0x0f, 0x08, + 0x0b, 0x0a, 0x07, 0x0c, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, + 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, + 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x00, + 0x3c, 0x35, 0x36, 0x31, 0x32, 0x2e, 0x2f, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, + 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, + 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, + 0x24, 0x23, 0x24, 0x23, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, + 0x98, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x01, 0x6e, 0x02, + 0x67, 0x02, 0x63, 0x04, 0x5f, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x07, + 0x52, 0x07, 0x50, 0x07, 0x4e, 0x09, 0x4c, 0x0a, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7a, 0x11, 0x72, 0x11, 0x6a, 0x11, 0x63, 0x11, 0x5d, 0x11, 0x57, 0x11, + 0x52, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x48, 0x12, 0x44, 0x12, 0x42, 0x13, + 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, + 0x38, 0x15, 0x37, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, + 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa8, 0x00, + 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x1f, 0x00, 0x1a, 0x02, 0x15, 0x05, 0x10, 0x07, 0x0c, 0x09, + 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, + 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, + 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, 0x3c, 0x36, 0x37, 0x32, + 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, 0x2a, 0x28, + 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, + 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, + 0xd5, 0x00, 0xc5, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, + 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x01, 0x6f, 0x01, 0x69, 0x02, + 0x66, 0x02, 0x61, 0x04, 0x5e, 0x04, 0x5a, 0x05, 0x58, 0x05, 0x55, 0x06, + 0x53, 0x07, 0x51, 0x07, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x73, 0x11, + 0x6c, 0x11, 0x66, 0x11, 0x60, 0x11, 0x5b, 0x11, 0x56, 0x11, 0x52, 0x11, + 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, 0x12, 0x44, 0x12, 0x41, 0x13, + 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, + 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x09, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x8f, 0x00, + 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2d, 0x00, + 0x1f, 0x00, 0x1a, 0x02, 0x16, 0x04, 0x11, 0x06, 0xbe, 0x00, 0xbc, 0x00, + 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, + 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, + 0x2c, 0x00, 0x23, 0x00, 0x3c, 0x37, 0x38, 0x33, 0x34, 0x30, 0x31, 0x2e, + 0x2f, 0x2c, 0x2d, 0x2a, 0x2c, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0xd5, 0x00, 0xc8, 0x00, + 0xbb, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, + 0x82, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x70, 0x01, 0x6c, 0x02, 0x67, 0x02, + 0x64, 0x03, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x05, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x75, 0x11, 0x6e, 0x11, 0x68, 0x11, + 0x63, 0x11, 0x5e, 0x11, 0x5a, 0x11, 0x56, 0x11, 0x52, 0x11, 0x4f, 0x11, + 0x4c, 0x11, 0x49, 0x11, 0x47, 0x12, 0x44, 0x12, 0x43, 0x12, 0x41, 0x13, + 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x09, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, + 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, + 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x86, 0x00, 0x77, 0x00, + 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2c, 0x00, 0x1f, 0x00, + 0x1b, 0x02, 0x16, 0x04, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, + 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, + 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x00, + 0x3d, 0x38, 0x38, 0x34, 0x35, 0x31, 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2c, + 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, + 0x25, 0x25, 0x25, 0x24, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, + 0xaa, 0x00, 0xa0, 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, + 0x7d, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6d, 0x01, 0x69, 0x02, 0x66, 0x02, + 0x63, 0x04, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7c, 0x11, 0x76, 0x11, 0x70, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x61, 0x11, + 0x5d, 0x11, 0x59, 0x11, 0x55, 0x11, 0x52, 0x11, 0x4f, 0x11, 0x4c, 0x11, + 0x49, 0x11, 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x13, 0x41, 0x13, + 0x3f, 0x13, 0x3e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, + 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, + 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xb0, 0x00, + 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, 0x00, 0x70, 0x00, 0x61, 0x00, + 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x1b, 0x02, + 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, + 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, + 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x3d, 0x38, 0x39, 0x35, + 0x36, 0x32, 0x33, 0x30, 0x31, 0x2e, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, + 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, + 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, + 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, + 0x9d, 0x00, 0x96, 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, + 0x77, 0x00, 0x73, 0x01, 0x6f, 0x01, 0x6b, 0x02, 0x67, 0x02, 0x64, 0x02, + 0x62, 0x04, 0x5f, 0x04, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, + 0x71, 0x11, 0x6c, 0x11, 0x67, 0x11, 0x63, 0x11, 0x5f, 0x11, 0x5c, 0x11, + 0x58, 0x11, 0x55, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4c, 0x11, 0x4a, 0x11, + 0x48, 0x11, 0x46, 0x12, 0x44, 0x12, 0x43, 0x12, 0x42, 0x13, 0x40, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, + 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xa9, 0x00, 0x9e, 0x00, + 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x4f, 0x00, + 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0xbe, 0x00, 0xbd, 0x00, + 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, + 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, + 0x48, 0x00, 0x3f, 0x00, 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x33, 0x30, + 0x32, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0xd7, 0x00, 0xcc, 0x00, + 0xc3, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, + 0x93, 0x00, 0x8d, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, + 0x74, 0x00, 0x70, 0x01, 0x6c, 0x01, 0x69, 0x02, 0x66, 0x02, 0x63, 0x02, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, 0x72, 0x11, 0x6e, 0x11, + 0x69, 0x11, 0x65, 0x11, 0x61, 0x11, 0x5d, 0x11, 0x5a, 0x11, 0x57, 0x11, + 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, + 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x34, 0x32, 0x33, 0x30, 0x30, 0x2e, + 0x30, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, + 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, 0x27, 0x27, + 0x27, 0x27, 0x27, 0x25, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, + 0xb4, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, + 0x8c, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, + 0x71, 0x01, 0x6d, 0x01, 0x6b, 0x02, 0x68, 0x02, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7c, 0x11, 0x78, 0x11, 0x73, 0x11, 0x6f, 0x11, 0x6b, 0x11, 0x67, 0x11, + 0x63, 0x11, 0x60, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, 0x54, 0x11, + 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, + 0x46, 0x12, 0x45, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x3a, 0x36, + 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2d, 0x2e, 0x2d, + 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, + 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, + 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, + 0xa8, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, + 0x86, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x01, + 0x6f, 0x01, 0x6b, 0x01, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x78, 0x11, + 0x74, 0x11, 0x70, 0x11, 0x6c, 0x11, 0x68, 0x11, 0x65, 0x11, 0x61, 0x11, + 0x5f, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, + 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x35, 0x36, 0x33, + 0x33, 0x31, 0x32, 0x30, 0x30, 0x2e, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, + 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, + 0x28, 0x27, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0xd8, 0x00, 0xd0, 0x00, + 0xc8, 0x00, 0xc0, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, + 0x9f, 0x00, 0x99, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, + 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x01, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x71, 0x11, + 0x6d, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x63, 0x11, 0x60, 0x11, 0x5d, 0x11, + 0x5b, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, + 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x36, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, + 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2c, 0x2b, + 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x27, 0x29, 0x27, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, + 0xbb, 0x00, 0xb4, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x92, 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, + 0x7d, 0x00, 0x7a, 0x00, 0x76, 0x00, 0x73, 0x00, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x72, 0x11, 0x6e, 0x11, 0x6b, 0x11, + 0x68, 0x11, 0x65, 0x11, 0x62, 0x11, 0x5f, 0x11, 0x5c, 0x11, 0x5a, 0x11, + 0x58, 0x11, 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, + 0x4c, 0x11, 0x4a, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x38, + 0x39, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x30, 0x32, 0x30, 0x30, 0x2e, + 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, + 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, + 0xb0, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, + 0x91, 0x00, 0x8d, 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, + 0x7a, 0x00, 0x77, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, + 0x76, 0x11, 0x72, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, 0x66, 0x11, + 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, + 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x39, 0x39, 0x36, 0x36, 0x34, + 0x35, 0x33, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, + 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd2, 0x00, + 0xcb, 0x00, 0xc4, 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, + 0xa8, 0x00, 0xa2, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, + 0x8b, 0x00, 0x88, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x00, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, 0x76, 0x11, 0x73, 0x11, + 0x70, 0x11, 0x6d, 0x11, 0x6a, 0x11, 0x67, 0x11, 0x65, 0x11, 0x62, 0x11, + 0x5f, 0x11, 0x5d, 0x11, 0x5b, 0x11, 0x59, 0x11, 0x56, 0x11, 0x55, 0x11, + 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3b, 0x3b, 0x39, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, + 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, + 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, + 0xc0, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, + 0xa0, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, + 0x88, 0x00, 0x83, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x7d, 0x11, 0x7a, 0x11, 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6e, 0x11, + 0x6b, 0x11, 0x68, 0x11, 0x66, 0x11, 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, + 0x5c, 0x11, 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x55, 0x11, 0x52, 0x11, + 0x51, 0x11, 0x4f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3b, 0x3b, 0x39, + 0x39, 0x36, 0x37, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x31, 0x32, 0x30, + 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc7, 0x00, 0xc1, 0x00, 0xbc, 0x00, + 0xb7, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa2, 0x00, 0x9e, 0x00, + 0x9a, 0x00, 0x95, 0x00, 0x93, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x87, 0x00, + 0x83, 0x00, 0x81, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, + 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, + 0x67, 0x11, 0x64, 0x11, 0x62, 0x11, 0x60, 0x11, 0x5e, 0x11, 0x5b, 0x11, + 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x52, 0x11, 0x51, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0xb8, 0x00, 0xc5, 0x00, 0xcb, 0x00, + 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, + 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x44, 0x00, 0x9d, 0x00, 0xbc, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, + 0xd2, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, + 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0x90, 0x00, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, + 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, + 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x68, 0x01, 0x8b, 0x00, 0xa0, 0x00, 0xad, 0x00, 0xb6, 0x00, 0xbc, 0x00, + 0xc1, 0x00, 0xc4, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, + 0xcd, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, 0x00, + 0xd2, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x25, 0x05, 0x6b, 0x00, + 0x8e, 0x00, 0xa4, 0x00, 0xb0, 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, + 0xc5, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, + 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, + 0x81, 0x02, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, + 0xcd, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, + 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x09, 0x72, 0x02, + 0x87, 0x00, 0x96, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb0, 0x00, 0xb5, 0x00, + 0xb9, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, + 0xc7, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x23, 0x0d, 0x51, 0x01, 0x73, 0x00, 0x89, 0x00, + 0x9a, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, + 0xbe, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, + 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0x80, 0x06, 0x97, 0x00, + 0xa8, 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, + 0xca, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, + 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4f, 0x11, 0x65, 0x07, 0x76, 0x03, 0x84, 0x01, + 0x8f, 0x00, 0x99, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xad, 0x00, 0xb1, 0x00, + 0xb4, 0x00, 0xb8, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc0, 0x00, + 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x12, 0x45, 0x04, 0x60, 0x00, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, + 0x9d, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, + 0xba, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, + 0xc4, 0x00, 0xc6, 0x00, 0x7f, 0x09, 0x91, 0x02, 0x9e, 0x00, 0xaa, 0x00, + 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, + 0xc8, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, + 0xcf, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4b, 0x17, 0x5d, 0x0c, 0x6c, 0x06, 0x78, 0x03, 0x83, 0x01, 0x8c, 0x00, + 0x94, 0x00, 0x9b, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xae, 0x00, + 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, 0x00, + 0xbe, 0x00, 0xbf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x15, 0x3d, 0x08, + 0x55, 0x02, 0x67, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, + 0x9f, 0x00, 0xa5, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, + 0xb6, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, + 0x7f, 0x0a, 0x8d, 0x04, 0x99, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, + 0xb6, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, + 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, + 0xcd, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x1c, 0x57, 0x11, + 0x64, 0x0a, 0x70, 0x06, 0x79, 0x03, 0x82, 0x02, 0x8a, 0x00, 0x91, 0x00, + 0x97, 0x00, 0x9c, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xad, 0x00, + 0xaf, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x17, 0x38, 0x0b, 0x4c, 0x04, 0x5e, 0x01, + 0x6c, 0x00, 0x7a, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, + 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, + 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x7f, 0x0b, 0x8a, 0x05, + 0x94, 0x02, 0x9d, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, + 0xb9, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, + 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x47, 0x20, 0x54, 0x15, 0x5f, 0x0e, 0x69, 0x09, + 0x72, 0x05, 0x7a, 0x03, 0x82, 0x02, 0x88, 0x01, 0x8f, 0x00, 0x94, 0x00, + 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa8, 0x00, 0xac, 0x00, + 0xae, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x18, 0x35, 0x0d, 0x47, 0x06, 0x55, 0x03, 0x64, 0x01, 0x70, 0x00, + 0x7a, 0x00, 0x83, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, + 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, + 0xb2, 0x00, 0xb4, 0x00, 0x7f, 0x0c, 0x89, 0x06, 0x92, 0x03, 0x99, 0x01, + 0xa0, 0x00, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, + 0xba, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, + 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x46, 0x23, 0x50, 0x18, 0x5b, 0x11, 0x64, 0x0b, 0x6c, 0x08, 0x74, 0x05, + 0x7b, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8c, 0x00, 0x91, 0x00, 0x95, 0x00, + 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xab, 0x00, + 0xac, 0x00, 0xaf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1a, 0x32, 0x0f, + 0x42, 0x08, 0x50, 0x04, 0x5c, 0x02, 0x68, 0x00, 0x72, 0x00, 0x7b, 0x00, + 0x83, 0x00, 0x8a, 0x00, 0x90, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, + 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x7f, 0x0d, 0x88, 0x07, 0x8f, 0x04, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, + 0xa7, 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, + 0xbb, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, + 0xc5, 0x00, 0xc6, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, 0x00, + 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x3b, 0x0b, 0x6f, 0x00, 0x9f, 0x00, 0xaf, 0x00, 0xb5, 0x00, 0xb9, 0x00, + 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, + 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, + 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0x47, 0x07, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, + 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x45, 0x25, 0x4e, 0x1b, + 0x57, 0x13, 0x60, 0x0e, 0x68, 0x0a, 0x6f, 0x07, 0x76, 0x05, 0x7b, 0x03, + 0x82, 0x02, 0x86, 0x02, 0x8b, 0x01, 0x90, 0x00, 0x93, 0x00, 0x97, 0x00, + 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1b, 0x30, 0x11, 0x3e, 0x0a, 0x4b, 0x05, + 0x57, 0x03, 0x61, 0x01, 0x6b, 0x00, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, + 0x88, 0x00, 0x8f, 0x00, 0x93, 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, + 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0x7f, 0x0d, 0x86, 0x08, + 0x8d, 0x05, 0x94, 0x02, 0x9a, 0x01, 0x9f, 0x00, 0xa4, 0x00, 0xa8, 0x00, + 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, + 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, + 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, 0x00, + 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x07, 0x17, 0x27, 0x02, + 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb5, 0x00, + 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0xbc, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6d, 0x00, 0x54, 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, + 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x0f, 0x0f, 0x3f, 0x00, + 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, + 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0x45, 0x28, 0x4d, 0x1d, 0x55, 0x16, 0x5d, 0x11, + 0x64, 0x0c, 0x6b, 0x09, 0x71, 0x07, 0x77, 0x05, 0x7b, 0x03, 0x81, 0x02, + 0x85, 0x02, 0x8a, 0x01, 0x8e, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, + 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1b, 0x2f, 0x12, 0x3b, 0x0c, 0x47, 0x07, 0x51, 0x04, 0x5c, 0x02, + 0x65, 0x01, 0x6e, 0x00, 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, + 0x8d, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, + 0xa2, 0x00, 0xa5, 0x00, 0x7f, 0x0d, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x03, + 0x97, 0x02, 0x9c, 0x01, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, + 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, + 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, + 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, + 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x2c, 0x02, 0x12, 0x22, 0x00, 0x58, 0x00, + 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xae, 0x00, + 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x6b, 0x00, + 0x28, 0x00, 0x52, 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, + 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, + 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, + 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, + 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0x44, 0x2a, 0x4b, 0x20, 0x53, 0x18, 0x5a, 0x13, 0x61, 0x0f, 0x67, 0x0b, + 0x6d, 0x09, 0x73, 0x07, 0x77, 0x05, 0x7c, 0x03, 0x81, 0x02, 0x85, 0x02, + 0x88, 0x01, 0x8d, 0x01, 0x90, 0x00, 0x93, 0x00, 0x96, 0x00, 0x9a, 0x00, + 0x9d, 0x00, 0x9f, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1c, 0x2d, 0x13, + 0x39, 0x0d, 0x44, 0x09, 0x4e, 0x05, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, + 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, + 0x90, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, + 0x7f, 0x0e, 0x85, 0x09, 0x8b, 0x06, 0x90, 0x04, 0x95, 0x02, 0x9a, 0x02, + 0x9e, 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, + 0xbd, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, + 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x00, 0x36, 0x00, 0x25, 0x09, 0x0b, 0x21, 0x00, 0x4a, 0x00, 0x68, 0x00, + 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, 0x00, 0xa3, 0x00, 0xa8, 0x00, + 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, + 0x39, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, + 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0x00, 0x33, 0x00, 0x1c, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, + 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, + 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0x43, 0x2b, 0x4a, 0x22, + 0x51, 0x1a, 0x57, 0x15, 0x5e, 0x11, 0x63, 0x0d, 0x69, 0x0a, 0x6e, 0x08, + 0x74, 0x06, 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, + 0x8c, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9b, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1c, 0x2d, 0x14, 0x37, 0x0e, 0x41, 0x0a, + 0x4a, 0x07, 0x53, 0x04, 0x5b, 0x02, 0x63, 0x01, 0x69, 0x00, 0x70, 0x00, + 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, + 0x92, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x7f, 0x0e, 0x85, 0x0a, + 0x8a, 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x01, 0xa0, 0x00, + 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, + 0xb3, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, 0x00, + 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, + 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x3a, 0x00, 0x2f, + 0x00, 0x16, 0x0e, 0x08, 0x20, 0x00, 0x42, 0x00, 0x5c, 0x00, 0x6f, 0x00, + 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa2, 0x00, + 0xa6, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xa6, 0x00, 0x98, 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, + 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, + 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x38, 0x00, 0x2a, + 0x00, 0x09, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, + 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, + 0xab, 0x00, 0xad, 0x00, 0x43, 0x2c, 0x49, 0x23, 0x50, 0x1c, 0x56, 0x17, + 0x5b, 0x12, 0x61, 0x0f, 0x66, 0x0c, 0x6b, 0x0a, 0x6f, 0x08, 0x75, 0x06, + 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x8b, 0x01, + 0x8e, 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x22, 0x1c, 0x2c, 0x15, 0x36, 0x0f, 0x3e, 0x0b, 0x48, 0x07, 0x50, 0x05, + 0x58, 0x04, 0x5f, 0x02, 0x66, 0x01, 0x6c, 0x00, 0x71, 0x00, 0x77, 0x00, + 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, + 0x94, 0x00, 0x97, 0x00, 0x7f, 0x0e, 0x84, 0x0a, 0x89, 0x07, 0x8d, 0x05, + 0x92, 0x03, 0x96, 0x02, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x00, 0xa4, 0x00, + 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, + 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, 0x00, + 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, + 0x93, 0x00, 0x98, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x02, 0x0e, + 0x12, 0x06, 0x20, 0x00, 0x3c, 0x00, 0x52, 0x00, 0x64, 0x00, 0x73, 0x00, + 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x00, 0xa6, 0x00, + 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, + 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, + 0x93, 0x00, 0x98, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, + 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, + 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, + 0x43, 0x2e, 0x48, 0x25, 0x4e, 0x1e, 0x54, 0x19, 0x59, 0x14, 0x5f, 0x11, + 0x63, 0x0e, 0x69, 0x0b, 0x6c, 0x09, 0x71, 0x07, 0x76, 0x06, 0x78, 0x05, + 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x86, 0x02, 0x8a, 0x01, 0x8e, 0x01, + 0x90, 0x00, 0x92, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1d, 0x2b, 0x16, + 0x34, 0x10, 0x3d, 0x0c, 0x45, 0x09, 0x4c, 0x06, 0x54, 0x04, 0x5b, 0x02, + 0x61, 0x02, 0x67, 0x01, 0x6d, 0x00, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, + 0x81, 0x00, 0x86, 0x00, 0x89, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, + 0x7f, 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x04, 0x94, 0x03, + 0x98, 0x02, 0x9c, 0x01, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, + 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, + 0xb6, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, + 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, + 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x07, 0x0c, 0x14, 0x05, + 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x75, 0x00, + 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, + 0x48, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, + 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, + 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, + 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, + 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x00, 0x42, 0x2f, 0x48, 0x26, + 0x4d, 0x20, 0x52, 0x1a, 0x58, 0x16, 0x5c, 0x12, 0x61, 0x0f, 0x66, 0x0c, + 0x6b, 0x0b, 0x6d, 0x09, 0x73, 0x07, 0x76, 0x06, 0x79, 0x05, 0x7d, 0x04, + 0x81, 0x03, 0x83, 0x02, 0x86, 0x02, 0x89, 0x02, 0x8d, 0x01, 0x8f, 0x01, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1d, 0x2a, 0x16, 0x33, 0x11, 0x3b, 0x0c, + 0x43, 0x0a, 0x4a, 0x07, 0x51, 0x05, 0x58, 0x04, 0x5e, 0x02, 0x64, 0x01, + 0x69, 0x01, 0x6f, 0x00, 0x74, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, + 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x7f, 0x0e, 0x83, 0x0b, + 0x88, 0x08, 0x8c, 0x06, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, + 0x9d, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, + 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, + 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x3a, + 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x0b, 0x0a, 0x16, 0x04, 0x20, 0x00, + 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, 0x00, 0x6d, 0x00, 0x77, 0x00, + 0x7f, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, + 0x1c, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, + 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x38, + 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, + 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, + 0x8b, 0x00, 0x91, 0x00, 0x42, 0x30, 0x47, 0x28, 0x4c, 0x21, 0x51, 0x1c, + 0x56, 0x17, 0x5a, 0x14, 0x60, 0x11, 0x63, 0x0e, 0x68, 0x0c, 0x6c, 0x0a, + 0x6f, 0x08, 0x74, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, + 0x83, 0x02, 0x85, 0x02, 0x88, 0x02, 0x8c, 0x01, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x2a, 0x17, 0x32, 0x12, 0x39, 0x0e, 0x40, 0x0a, 0x48, 0x07, + 0x4f, 0x05, 0x55, 0x04, 0x5a, 0x03, 0x60, 0x02, 0x66, 0x01, 0x6b, 0x00, + 0x70, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, + 0x88, 0x00, 0x8b, 0x00, 0x7f, 0x0f, 0x83, 0x0b, 0x87, 0x09, 0x8b, 0x07, + 0x8e, 0x05, 0x92, 0x03, 0x96, 0x02, 0x99, 0x02, 0x9b, 0x01, 0x9e, 0x01, + 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, + 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, + 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x33, 0x00, 0x27, + 0x00, 0x18, 0x04, 0x0d, 0x0e, 0x08, 0x17, 0x04, 0x20, 0x00, 0x32, 0x00, + 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x70, 0x00, 0x78, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, + 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, + 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, + 0x00, 0x0b, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, + 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, + 0x42, 0x31, 0x47, 0x29, 0x4b, 0x22, 0x50, 0x1d, 0x55, 0x19, 0x58, 0x15, + 0x5e, 0x12, 0x61, 0x0f, 0x65, 0x0d, 0x6a, 0x0b, 0x6c, 0x09, 0x70, 0x08, + 0x75, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, 0x83, 0x02, + 0x85, 0x02, 0x87, 0x02, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, + 0x31, 0x12, 0x38, 0x0f, 0x3f, 0x0c, 0x45, 0x09, 0x4c, 0x07, 0x52, 0x05, + 0x58, 0x04, 0x5d, 0x02, 0x63, 0x02, 0x67, 0x01, 0x6c, 0x00, 0x71, 0x00, + 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, + 0x7f, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x04, + 0x94, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0xa0, 0x01, 0xa2, 0x00, + 0xa4, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, + 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x12, + 0x07, 0x0c, 0x10, 0x07, 0x18, 0x03, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, + 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, + 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, + 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, + 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x46, 0x2a, + 0x4a, 0x24, 0x4f, 0x1f, 0x53, 0x1a, 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, + 0x63, 0x0e, 0x67, 0x0c, 0x6b, 0x0b, 0x6d, 0x09, 0x72, 0x07, 0x75, 0x07, + 0x77, 0x06, 0x79, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, 0x30, 0x13, 0x37, 0x0f, + 0x3d, 0x0c, 0x44, 0x0a, 0x4a, 0x07, 0x50, 0x05, 0x55, 0x04, 0x5b, 0x04, + 0x60, 0x02, 0x64, 0x01, 0x69, 0x01, 0x6d, 0x00, 0x72, 0x00, 0x76, 0x00, + 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, 0x00, 0x7f, 0x0f, 0x83, 0x0c, + 0x86, 0x09, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x02, + 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa5, 0x00, + 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x02, 0x0e, 0x0a, 0x0a, + 0x11, 0x06, 0x19, 0x03, 0x1f, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, + 0x53, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, + 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, + 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, + 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, + 0x68, 0x00, 0x70, 0x00, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4f, 0x1f, + 0x52, 0x1b, 0x57, 0x18, 0x59, 0x14, 0x5f, 0x12, 0x62, 0x0f, 0x65, 0x0d, + 0x6a, 0x0c, 0x6c, 0x0a, 0x6e, 0x09, 0x73, 0x07, 0x76, 0x06, 0x78, 0x06, + 0x7a, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0x21, 0x1e, 0x28, 0x19, 0x2f, 0x14, 0x35, 0x0f, 0x3c, 0x0c, 0x42, 0x0a, + 0x48, 0x07, 0x4e, 0x06, 0x53, 0x05, 0x58, 0x04, 0x5d, 0x02, 0x62, 0x02, + 0x66, 0x01, 0x6b, 0x01, 0x6f, 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, + 0x7d, 0x00, 0x81, 0x00, 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x07, + 0x8c, 0x06, 0x8f, 0x05, 0x92, 0x03, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, + 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, + 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x31, + 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x05, 0x0d, 0x0c, 0x09, 0x13, 0x06, + 0x19, 0x03, 0x1f, 0x00, 0x2d, 0x00, 0x3a, 0x00, 0x45, 0x00, 0x4f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, + 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, + 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, + 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, + 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, + 0x42, 0x33, 0x45, 0x2c, 0x48, 0x26, 0x4e, 0x21, 0x50, 0x1c, 0x56, 0x19, + 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, 0x62, 0x0e, 0x67, 0x0c, 0x6a, 0x0b, + 0x6d, 0x0a, 0x70, 0x09, 0x74, 0x07, 0x76, 0x06, 0x78, 0x06, 0x7a, 0x05, + 0x7e, 0x04, 0x81, 0x03, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x28, 0x19, + 0x2f, 0x15, 0x35, 0x11, 0x3b, 0x0d, 0x41, 0x0b, 0x46, 0x09, 0x4c, 0x07, + 0x51, 0x05, 0x55, 0x04, 0x5b, 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, + 0x6b, 0x00, 0x70, 0x00, 0x73, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, + 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, 0x06, 0x8f, 0x05, + 0x91, 0x04, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, + 0xa0, 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, + 0xac, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x23, + 0x00, 0x19, 0x00, 0x0f, 0x07, 0x0c, 0x0e, 0x08, 0x14, 0x05, 0x1a, 0x02, + 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, + 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, + 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, + 0x00, 0x0c, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, + 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x00, 0x5d, 0x1a, 0x6d, 0x16, + 0x72, 0x15, 0x75, 0x14, 0x77, 0x13, 0x78, 0x13, 0x79, 0x13, 0x7a, 0x12, + 0x7a, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7c, 0x12, 0x7c, 0x11, + 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, + 0xbd, 0x02, 0xa4, 0x06, 0x97, 0x09, 0x91, 0x0a, 0x8d, 0x0b, 0x8a, 0x0c, + 0x89, 0x0d, 0x88, 0x0d, 0x86, 0x0d, 0x86, 0x0e, 0x85, 0x0e, 0x85, 0x0e, + 0x84, 0x0e, 0x84, 0x0e, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, + 0x82, 0x0f, 0x82, 0x0f, 0x33, 0x11, 0x5f, 0x11, 0x6f, 0x11, 0x74, 0x11, + 0x77, 0x11, 0x78, 0x11, 0x7a, 0x11, 0x7a, 0x11, 0x7b, 0x11, 0x7b, 0x11, + 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, 0x7d, 0x11, + 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1d, 0x00, 0x14, + 0x03, 0x0e, 0x09, 0x0b, 0x0f, 0x08, 0x15, 0x05, 0x1a, 0x02, 0x1f, 0x00, + 0x2b, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3d, + 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, + 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, + 0x49, 0x00, 0x51, 0x00, 0x50, 0x24, 0x5f, 0x1f, 0x66, 0x1b, 0x6b, 0x19, + 0x6e, 0x18, 0x70, 0x17, 0x73, 0x16, 0x73, 0x15, 0x75, 0x15, 0x76, 0x15, + 0x76, 0x15, 0x77, 0x14, 0x77, 0x14, 0x78, 0x13, 0x78, 0x13, 0x79, 0x13, + 0x79, 0x13, 0x7a, 0x13, 0x7a, 0x13, 0x7b, 0x13, 0xcc, 0x00, 0xb5, 0x00, + 0xa8, 0x02, 0x9e, 0x04, 0x99, 0x05, 0x94, 0x06, 0x92, 0x07, 0x8f, 0x08, + 0x8d, 0x09, 0x8c, 0x09, 0x8b, 0x0a, 0x8a, 0x0a, 0x89, 0x0b, 0x88, 0x0b, + 0x88, 0x0b, 0x87, 0x0c, 0x87, 0x0c, 0x86, 0x0c, 0x86, 0x0c, 0x86, 0x0c, + 0x23, 0x13, 0x46, 0x11, 0x58, 0x11, 0x63, 0x11, 0x69, 0x11, 0x6d, 0x11, + 0x70, 0x11, 0x72, 0x11, 0x73, 0x11, 0x75, 0x11, 0x76, 0x11, 0x77, 0x11, + 0x77, 0x11, 0x78, 0x11, 0x78, 0x11, 0x79, 0x11, 0x79, 0x11, 0x79, 0x11, + 0x7a, 0x11, 0x7a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, + 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x10, 0x05, 0x0d, + 0x0b, 0x0a, 0x10, 0x07, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x2a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, + 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, + 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, + 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, + 0x4a, 0x2a, 0x57, 0x24, 0x5f, 0x20, 0x64, 0x1e, 0x68, 0x1c, 0x6a, 0x1b, + 0x6d, 0x19, 0x6e, 0x18, 0x70, 0x18, 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, + 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, + 0x76, 0x15, 0x77, 0x14, 0xd1, 0x00, 0xc0, 0x00, 0xb3, 0x00, 0xaa, 0x01, + 0xa2, 0x02, 0x9d, 0x03, 0x99, 0x04, 0x96, 0x05, 0x94, 0x06, 0x92, 0x06, + 0x90, 0x07, 0x8f, 0x07, 0x8d, 0x08, 0x8d, 0x08, 0x8c, 0x09, 0x8b, 0x09, + 0x8a, 0x09, 0x8a, 0x0a, 0x89, 0x0a, 0x89, 0x0b, 0x22, 0x17, 0x39, 0x11, + 0x4a, 0x11, 0x55, 0x11, 0x5e, 0x11, 0x63, 0x11, 0x67, 0x11, 0x6a, 0x11, + 0x6c, 0x11, 0x6e, 0x11, 0x70, 0x11, 0x71, 0x11, 0x72, 0x11, 0x73, 0x11, + 0x74, 0x11, 0x75, 0x11, 0x75, 0x11, 0x76, 0x11, 0x76, 0x11, 0x77, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x32, 0x00, 0x2c, + 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x01, 0x0e, 0x07, 0x0c, 0x0c, 0x09, + 0x11, 0x06, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, + 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, + 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, + 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, + 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x2e, 0x53, 0x28, + 0x5a, 0x24, 0x5f, 0x22, 0x63, 0x1f, 0x66, 0x1e, 0x69, 0x1c, 0x6a, 0x1b, + 0x6c, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x70, 0x18, 0x71, 0x18, 0x72, 0x18, + 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, + 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x01, + 0xa0, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x97, 0x04, 0x95, 0x05, 0x93, 0x05, + 0x92, 0x06, 0x91, 0x06, 0x90, 0x07, 0x8e, 0x07, 0x8e, 0x07, 0x8d, 0x07, + 0x8c, 0x08, 0x8c, 0x09, 0x22, 0x1a, 0x33, 0x13, 0x41, 0x11, 0x4c, 0x11, + 0x54, 0x11, 0x5b, 0x11, 0x5f, 0x11, 0x63, 0x11, 0x66, 0x11, 0x68, 0x11, + 0x6a, 0x11, 0x6c, 0x11, 0x6e, 0x11, 0x6f, 0x11, 0x70, 0x11, 0x71, 0x11, + 0x72, 0x11, 0x72, 0x11, 0x73, 0x11, 0x74, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x07, 0x7f, 0x00, + 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, + 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, + 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x47, 0x07, 0x0f, 0x0f, 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, + 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, + 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x31, 0x50, 0x2b, 0x56, 0x27, 0x5b, 0x24, + 0x5f, 0x22, 0x62, 0x20, 0x65, 0x1f, 0x67, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, + 0x6c, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, 0x71, 0x18, + 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x17, 0xd6, 0x00, 0xcb, 0x00, + 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, 0x00, 0xa6, 0x01, 0xa2, 0x01, + 0x9f, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x96, 0x04, 0x94, 0x05, + 0x93, 0x05, 0x92, 0x06, 0x91, 0x06, 0x90, 0x06, 0x8f, 0x06, 0x8f, 0x07, + 0x22, 0x1b, 0x2f, 0x15, 0x3b, 0x12, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x11, + 0x59, 0x11, 0x5d, 0x11, 0x60, 0x11, 0x63, 0x11, 0x66, 0x11, 0x67, 0x11, + 0x69, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, 0x6f, 0x11, + 0x70, 0x11, 0x71, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, + 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, + 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0x00, + 0x05, 0x05, 0x00, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, + 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, + 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x33, 0x4d, 0x2e, 0x53, 0x29, 0x58, 0x26, 0x5c, 0x24, 0x5f, 0x22, + 0x61, 0x21, 0x65, 0x20, 0x65, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, + 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, + 0x71, 0x18, 0x72, 0x18, 0xd7, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbd, 0x00, + 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, 0x00, 0xa4, 0x01, 0xa1, 0x02, + 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x97, 0x03, 0x96, 0x04, + 0x94, 0x05, 0x93, 0x05, 0x92, 0x05, 0x91, 0x06, 0x21, 0x1c, 0x2d, 0x16, + 0x37, 0x13, 0x40, 0x11, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x11, 0x57, 0x11, + 0x5b, 0x11, 0x5e, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x67, 0x11, + 0x68, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, + 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, + 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, + 0x00, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, + 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x34, 0x4c, 0x2f, + 0x51, 0x2c, 0x56, 0x28, 0x5a, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, + 0x64, 0x20, 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, + 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, + 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, 0x00, 0xba, 0x00, 0xb5, 0x00, + 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, 0x01, 0xa2, 0x01, 0xa0, 0x02, + 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x97, 0x03, 0x96, 0x03, + 0x95, 0x04, 0x94, 0x05, 0x22, 0x1d, 0x2b, 0x17, 0x34, 0x14, 0x3b, 0x12, + 0x43, 0x11, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x5a, 0x11, + 0x5d, 0x11, 0x5f, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, + 0x68, 0x11, 0x69, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, + 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, + 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, + 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, + 0x00, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, + 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x36, 0x4a, 0x31, 0x50, 0x2d, 0x54, 0x2a, + 0x57, 0x28, 0x5b, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, 0x63, 0x20, + 0x65, 0x20, 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, + 0x6a, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0xd9, 0x00, 0xd1, 0x00, + 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb4, 0x00, 0xb0, 0x00, + 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0x9f, 0x02, + 0x9d, 0x02, 0x9b, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x98, 0x03, 0x97, 0x03, + 0x21, 0x1e, 0x2a, 0x18, 0x32, 0x15, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, + 0x4a, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x59, 0x11, 0x5c, 0x11, + 0x5d, 0x11, 0x60, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, + 0x67, 0x11, 0x68, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x09, 0x1d, 0x00, + 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, + 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, + 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, + 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x36, 0x49, 0x32, 0x4e, 0x2f, 0x52, 0x2c, 0x56, 0x29, 0x58, 0x27, + 0x5b, 0x26, 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, + 0x65, 0x1f, 0x66, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, + 0x6a, 0x1b, 0x6c, 0x1a, 0xd9, 0x00, 0xd2, 0x00, 0xcc, 0x00, 0xc6, 0x00, + 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, 0x00, 0xaf, 0x00, 0xac, 0x00, + 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9e, 0x02, + 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x02, 0x22, 0x1e, 0x29, 0x19, + 0x30, 0x16, 0x36, 0x13, 0x3c, 0x12, 0x41, 0x11, 0x46, 0x11, 0x4a, 0x11, + 0x4f, 0x11, 0x52, 0x11, 0x55, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, + 0x5f, 0x11, 0x60, 0x11, 0x62, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, + 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, + 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, + 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, + 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x37, 0x48, 0x33, + 0x4d, 0x30, 0x51, 0x2d, 0x54, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x26, + 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, + 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, + 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, 0x00, 0xc3, 0x00, 0xbe, 0x00, + 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, + 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x02, 0x9e, 0x02, + 0x9d, 0x02, 0x9c, 0x02, 0x21, 0x1e, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x14, + 0x39, 0x13, 0x3f, 0x12, 0x43, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, + 0x52, 0x11, 0x55, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5c, 0x11, 0x5d, 0x11, + 0x5f, 0x11, 0x61, 0x11, 0x62, 0x11, 0x63, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, + 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, + 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, + 0x97, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, + 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, + 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x43, 0x38, 0x48, 0x34, 0x4c, 0x31, 0x4f, 0x2e, + 0x53, 0x2c, 0x56, 0x2a, 0x57, 0x28, 0x5a, 0x27, 0x5c, 0x26, 0x5d, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, + 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0xd9, 0x00, 0xd4, 0x00, + 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xb9, 0x00, + 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, + 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9f, 0x02, 0x9e, 0x02, + 0x22, 0x1f, 0x27, 0x1a, 0x2d, 0x17, 0x33, 0x15, 0x38, 0x13, 0x3c, 0x13, + 0x41, 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, + 0x54, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x5e, 0x11, + 0x5f, 0x11, 0x61, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, + 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, + 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, 0x00, + 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, + 0x00, 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x38, 0x47, 0x35, 0x4b, 0x32, 0x4e, 0x2f, 0x52, 0x2d, 0x53, 0x2b, + 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, + 0x66, 0x1e, 0x68, 0x1d, 0xda, 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcb, 0x00, + 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb5, 0x00, + 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, + 0xa4, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x01, 0x21, 0x1f, 0x27, 0x1b, + 0x2c, 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x12, 0x42, 0x11, + 0x45, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, + 0x56, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, 0x5d, 0x11, 0x5e, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x08, 0x00, + 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, + 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, + 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, + 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x35, + 0x4a, 0x32, 0x4e, 0x30, 0x50, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, + 0x58, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x22, 0x63, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, + 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc4, 0x00, + 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb4, 0x00, 0xb2, 0x00, + 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa5, 0x00, + 0xa4, 0x01, 0xa2, 0x01, 0x22, 0x1f, 0x27, 0x1b, 0x2c, 0x18, 0x30, 0x16, + 0x35, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x11, 0x47, 0x11, + 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x56, 0x11, + 0x58, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, + 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x0f, 0x00, 0x20, 0x00, + 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, + 0x74, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, + 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, + 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x36, 0x4a, 0x33, 0x4d, 0x31, + 0x4f, 0x2e, 0x52, 0x2d, 0x53, 0x2b, 0x57, 0x2a, 0x57, 0x28, 0x5a, 0x27, + 0x5c, 0x27, 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, + 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, + 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xbf, 0x00, + 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, + 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa6, 0x00, 0xa4, 0x00, + 0x21, 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x15, 0x37, 0x14, + 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, + 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x55, 0x11, 0x57, 0x11, + 0x59, 0x11, 0x5a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, + 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, + 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, + 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, + 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, + 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x39, 0x45, 0x36, 0x49, 0x34, 0x4c, 0x31, 0x4f, 0x30, 0x51, 0x2e, + 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x28, 0x5b, 0x27, 0x5c, 0x27, + 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0x61, 0x21, + 0x64, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, 0xd2, 0x00, 0xce, 0x00, + 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, + 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, + 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0x22, 0x1f, 0x26, 0x1c, + 0x2a, 0x19, 0x2e, 0x17, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, + 0x40, 0x12, 0x43, 0x11, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, + 0x4f, 0x11, 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x58, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, + 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, + 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, + 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, + 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, + 0x48, 0x35, 0x4a, 0x32, 0x4e, 0x31, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2b, + 0x57, 0x2a, 0x57, 0x29, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x25, + 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, + 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xc8, 0x00, + 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb7, 0x00, + 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, + 0xa9, 0x00, 0xa8, 0x00, 0x21, 0x20, 0x26, 0x1c, 0x2a, 0x1a, 0x2d, 0x18, + 0x31, 0x16, 0x35, 0x14, 0x38, 0x13, 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, + 0x44, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, + 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, + 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x01, 0x00, + 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, + 0x53, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, + 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, + 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, + 0x4d, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, + 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0xdb, 0x00, 0xd7, 0x00, + 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc3, 0x00, + 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, 0x00, 0xb7, 0x00, 0xb5, 0x00, + 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xaa, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x15, + 0x37, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, + 0x47, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, + 0x53, 0x11, 0x55, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, + 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x06, 0x00, 0x13, 0x00, + 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, + 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, + 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, + 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x30, + 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x28, + 0x5a, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd7, 0x00, 0xd4, 0x00, 0xd0, 0x00, + 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, + 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, + 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, 0x00, 0x21, 0x20, 0x25, 0x1d, + 0x29, 0x1a, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x13, + 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x11, 0x45, 0x11, 0x47, 0x11, + 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x52, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, + 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, + 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, + 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, + 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, + 0x46, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x2e, 0x53, 0x2e, + 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0x60, 0x23, + 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, 0x00, 0xce, 0x00, 0xcb, 0x00, + 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbc, 0x00, + 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, + 0xaf, 0x00, 0xad, 0x00, 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x18, + 0x2f, 0x17, 0x32, 0x16, 0x35, 0x14, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, + 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, + 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, + 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, + 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, + 0x36, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, + 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, + 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x36, 0x4a, 0x35, + 0x4a, 0x32, 0x4e, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, + 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x26, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd8, 0x00, + 0xd5, 0x00, 0xd2, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xca, 0x00, 0xc6, 0x00, + 0xc4, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb9, 0x00, + 0xb8, 0x00, 0xb5, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, + 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2e, 0x17, 0x31, 0x16, + 0x34, 0x15, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, + 0x42, 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, + 0x4e, 0x11, 0x4f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, + 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x27, 0x68, 0x32, 0x51, 0x36, 0x4a, 0x38, 0x47, + 0x3a, 0x46, 0x3a, 0x45, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, 0x3c, 0x43, + 0x3c, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x41, 0x0e, 0x16, 0x27, + 0x23, 0x31, 0x2a, 0x35, 0x2e, 0x38, 0x31, 0x39, 0x33, 0x3a, 0x34, 0x3b, + 0x35, 0x3b, 0x36, 0x3c, 0x37, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x39, 0x3d, + 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, + 0x68, 0x00, 0x32, 0x27, 0x36, 0x31, 0x38, 0x35, 0x3a, 0x38, 0x3a, 0x39, + 0x3b, 0x3a, 0x3c, 0x3b, 0x3c, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, + 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x32, 0x28, 0x36, 0x2c, 0x38, 0x2f, 0x3a, 0x32, 0x3a, 0x34, 0x3b, + 0x35, 0x3c, 0x36, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x39, 0x3d, + 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x00, 0x97, 0x01, 0x67, 0x09, 0x56, 0x11, 0x4f, + 0x17, 0x4b, 0x1c, 0x49, 0x20, 0x47, 0x23, 0x46, 0x25, 0x45, 0x28, 0x44, + 0x2a, 0x44, 0x2b, 0x43, 0x2c, 0x43, 0x2e, 0x43, 0x2f, 0x42, 0x30, 0x42, + 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x1a, 0x5c, 0x25, 0x4f, + 0x2a, 0x4a, 0x2e, 0x48, 0x31, 0x46, 0x33, 0x45, 0x34, 0x44, 0x36, 0x44, + 0x37, 0x43, 0x37, 0x43, 0x38, 0x43, 0x38, 0x42, 0x39, 0x42, 0x39, 0x42, + 0x39, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, + 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x28, 0x25, 0x2d, + 0x28, 0x30, 0x2b, 0x32, 0x2e, 0x34, 0x2f, 0x35, 0x31, 0x36, 0x32, 0x37, + 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x3a, + 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3b, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, + 0x00, 0xb7, 0x00, 0x8b, 0x02, 0x72, 0x07, 0x65, 0x0c, 0x5c, 0x11, 0x57, + 0x15, 0x53, 0x18, 0x50, 0x1b, 0x4e, 0x1d, 0x4d, 0x20, 0x4b, 0x22, 0x4a, + 0x23, 0x49, 0x25, 0x48, 0x26, 0x48, 0x28, 0x47, 0x29, 0x47, 0x2a, 0x46, + 0x2b, 0x46, 0x2c, 0x45, 0x16, 0x6d, 0x1f, 0x5f, 0x24, 0x57, 0x28, 0x53, + 0x2b, 0x50, 0x2d, 0x4d, 0x2f, 0x4c, 0x31, 0x4a, 0x32, 0x49, 0x33, 0x48, + 0x34, 0x48, 0x35, 0x47, 0x35, 0x46, 0x36, 0x46, 0x36, 0x45, 0x37, 0x45, + 0x38, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x2e, 0x00, + 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x25, 0x23, 0x28, 0x26, 0x2b, 0x28, 0x2e, + 0x2a, 0x2f, 0x2c, 0x31, 0x2e, 0x32, 0x2f, 0x33, 0x30, 0x34, 0x31, 0x35, + 0x32, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x35, 0x38, 0x36, 0x38, + 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x00, 0xc5, 0x00, 0xa0, + 0x00, 0x87, 0x03, 0x76, 0x06, 0x6b, 0x0a, 0x64, 0x0e, 0x5f, 0x11, 0x5b, + 0x13, 0x57, 0x16, 0x55, 0x18, 0x53, 0x1a, 0x51, 0x1c, 0x50, 0x1e, 0x4e, + 0x20, 0x4d, 0x21, 0x4c, 0x22, 0x4b, 0x24, 0x4a, 0x25, 0x49, 0x26, 0x48, + 0x15, 0x72, 0x1b, 0x66, 0x20, 0x5f, 0x24, 0x5a, 0x27, 0x56, 0x29, 0x53, + 0x2c, 0x51, 0x2d, 0x50, 0x2f, 0x4e, 0x30, 0x4d, 0x31, 0x4c, 0x32, 0x4b, + 0x32, 0x4a, 0x33, 0x49, 0x34, 0x49, 0x35, 0x48, 0x35, 0x48, 0x35, 0x47, + 0x35, 0x46, 0x36, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x24, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x24, 0x23, 0x26, 0x25, 0x29, 0x27, 0x2b, 0x28, 0x2c, 0x2a, 0x2e, + 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, + 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, + 0x35, 0x36, 0x36, 0x37, 0x00, 0xcb, 0x00, 0xad, 0x00, 0x96, 0x01, 0x84, + 0x03, 0x78, 0x06, 0x70, 0x09, 0x69, 0x0b, 0x64, 0x0e, 0x60, 0x10, 0x5d, + 0x13, 0x59, 0x15, 0x57, 0x17, 0x56, 0x19, 0x54, 0x1a, 0x52, 0x1c, 0x51, + 0x1d, 0x50, 0x1f, 0x4f, 0x1f, 0x4f, 0x21, 0x4e, 0x14, 0x75, 0x19, 0x6b, + 0x1e, 0x64, 0x22, 0x5f, 0x24, 0x5b, 0x27, 0x58, 0x29, 0x56, 0x2a, 0x54, + 0x2c, 0x52, 0x2d, 0x51, 0x2e, 0x4f, 0x2f, 0x4e, 0x30, 0x4e, 0x31, 0x4d, + 0x31, 0x4c, 0x32, 0x4a, 0x33, 0x4a, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, + 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x23, 0x25, + 0x24, 0x27, 0x25, 0x28, 0x27, 0x2a, 0x28, 0x2c, 0x2a, 0x2d, 0x2b, 0x2e, + 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x32, 0x30, 0x33, + 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, + 0x00, 0xcf, 0x00, 0xb6, 0x00, 0xa0, 0x00, 0x8f, 0x01, 0x83, 0x03, 0x79, + 0x05, 0x72, 0x08, 0x6c, 0x0a, 0x68, 0x0c, 0x64, 0x0f, 0x61, 0x10, 0x5e, + 0x12, 0x5b, 0x14, 0x59, 0x16, 0x58, 0x17, 0x56, 0x19, 0x55, 0x1a, 0x53, + 0x1b, 0x52, 0x1c, 0x50, 0x13, 0x77, 0x18, 0x6e, 0x1c, 0x68, 0x1f, 0x63, + 0x22, 0x5f, 0x24, 0x5c, 0x26, 0x59, 0x28, 0x57, 0x29, 0x56, 0x2a, 0x54, + 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x50, 0x2e, 0x4f, 0x2f, 0x4f, 0x31, 0x4e, + 0x31, 0x4d, 0x31, 0x4d, 0x31, 0x4c, 0x32, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, + 0x00, 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, + 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x22, 0x24, 0x24, 0x26, 0x25, 0x27, + 0x26, 0x29, 0x27, 0x2a, 0x28, 0x2b, 0x2a, 0x2d, 0x2a, 0x2d, 0x2c, 0x2e, + 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, + 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x00, 0xd1, 0x00, 0xbc, + 0x00, 0xa9, 0x00, 0x99, 0x00, 0x8c, 0x02, 0x82, 0x03, 0x7a, 0x05, 0x74, + 0x07, 0x6f, 0x09, 0x6b, 0x0b, 0x67, 0x0d, 0x63, 0x0f, 0x61, 0x10, 0x5f, + 0x12, 0x5c, 0x14, 0x5a, 0x15, 0x58, 0x16, 0x58, 0x18, 0x57, 0x19, 0x56, + 0x13, 0x78, 0x17, 0x70, 0x1b, 0x6a, 0x1e, 0x66, 0x20, 0x62, 0x22, 0x5f, + 0x24, 0x5c, 0x26, 0x5b, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x56, 0x2b, 0x53, + 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x51, 0x2e, 0x50, 0x2f, 0x4f, 0x30, 0x4f, + 0x31, 0x4f, 0x31, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, + 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, + 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x22, 0x22, 0x24, 0x23, 0x25, 0x24, 0x27, 0x25, 0x27, 0x27, 0x28, + 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, + 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x33, + 0x30, 0x33, 0x31, 0x33, 0x00, 0xd3, 0x00, 0xc1, 0x00, 0xaf, 0x00, 0xa1, + 0x00, 0x94, 0x00, 0x8a, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x76, 0x07, 0x71, + 0x09, 0x6d, 0x0a, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, + 0x12, 0x5d, 0x13, 0x5c, 0x14, 0x59, 0x16, 0x58, 0x13, 0x79, 0x16, 0x72, + 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x65, 0x20, 0x61, 0x23, 0x60, 0x24, 0x5c, + 0x26, 0x5b, 0x27, 0x59, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, + 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2e, 0x51, 0x2e, 0x4f, 0x2f, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, + 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x28, 0x26, 0x28, 0x28, 0x2a, + 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, + 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, + 0x00, 0xd4, 0x00, 0xc4, 0x00, 0xb5, 0x00, 0xa7, 0x00, 0x9b, 0x00, 0x91, + 0x01, 0x88, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x07, 0x73, 0x08, 0x6e, + 0x0a, 0x6b, 0x0b, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, + 0x12, 0x5f, 0x13, 0x5c, 0x12, 0x79, 0x15, 0x73, 0x18, 0x6e, 0x1b, 0x6a, + 0x1d, 0x67, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, + 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, + 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, + 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, + 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, + 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, + 0x2a, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, + 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x00, 0xd5, 0x00, 0xc7, + 0x00, 0xb9, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8f, 0x01, 0x87, + 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x06, 0x74, 0x08, 0x6f, 0x09, 0x6c, + 0x0b, 0x6b, 0x0c, 0x68, 0x0d, 0x65, 0x0e, 0x63, 0x0f, 0x62, 0x10, 0x60, + 0x12, 0x7a, 0x15, 0x75, 0x18, 0x70, 0x1a, 0x6c, 0x1d, 0x69, 0x1e, 0x65, + 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, 0x27, 0x5b, + 0x27, 0x58, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, + 0x2c, 0x53, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, + 0x00, 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, + 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, + 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, + 0x2d, 0x30, 0x2d, 0x30, 0x00, 0xd6, 0x00, 0xc9, 0x00, 0xbc, 0x00, 0xb1, + 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x94, 0x00, 0x8c, 0x02, 0x86, 0x02, 0x81, + 0x03, 0x7c, 0x05, 0x78, 0x06, 0x75, 0x07, 0x71, 0x09, 0x6d, 0x0a, 0x6c, + 0x0b, 0x6a, 0x0c, 0x67, 0x0d, 0x65, 0x0e, 0x62, 0x12, 0x7b, 0x15, 0x76, + 0x18, 0x72, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x63, + 0x22, 0x60, 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, + 0x28, 0x57, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2a, 0x55, 0x2b, 0x53, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, + 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, + 0x00, 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x21, 0x22, 0x22, 0x23, + 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, + 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2c, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, + 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xbf, 0x00, 0xb4, 0x00, 0xab, 0x00, 0xa1, + 0x00, 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x85, 0x02, 0x81, 0x04, 0x7c, + 0x05, 0x78, 0x06, 0x76, 0x07, 0x73, 0x08, 0x6f, 0x09, 0x6c, 0x0b, 0x6b, + 0x0c, 0x6a, 0x0c, 0x67, 0x12, 0x7b, 0x15, 0x76, 0x17, 0x72, 0x19, 0x6e, + 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, + 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x58, + 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, + 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, + 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, + 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, + 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, + 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x00, 0xd7, 0x00, 0xcc, + 0x00, 0xc2, 0x00, 0xb8, 0x00, 0xae, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x95, + 0x00, 0x90, 0x01, 0x8a, 0x02, 0x85, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x78, + 0x06, 0x76, 0x07, 0x74, 0x08, 0x70, 0x09, 0x6d, 0x0a, 0x6c, 0x0b, 0x6a, + 0x12, 0x7b, 0x14, 0x77, 0x16, 0x73, 0x18, 0x70, 0x1a, 0x6d, 0x1c, 0x6a, + 0x1d, 0x69, 0x1e, 0x65, 0x20, 0x65, 0x20, 0x62, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x28, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, + 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, + 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, + 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, + 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, + 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, + 0x2b, 0x2d, 0x2c, 0x2d, 0x00, 0xd7, 0x00, 0xcd, 0x00, 0xc4, 0x00, 0xbb, + 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8e, + 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x79, 0x06, 0x77, + 0x07, 0x75, 0x07, 0x72, 0x09, 0x6e, 0x09, 0x6d, 0x12, 0x7b, 0x14, 0x77, + 0x15, 0x73, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6b, 0x1d, 0x69, 0x1d, 0x67, + 0x1f, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, + 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, + 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, + 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, + 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, + 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, + 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, 0x28, 0x29, + 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, + 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, 0xb4, 0x00, 0xad, + 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x92, 0x01, 0x8d, 0x02, 0x88, + 0x02, 0x84, 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x07, 0x75, + 0x07, 0x73, 0x09, 0x70, 0x12, 0x7c, 0x13, 0x78, 0x15, 0x75, 0x18, 0x72, + 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, + 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x59, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, + 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, + 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, + 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, + 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, + 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, + 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd0, + 0x00, 0xc7, 0x00, 0xbe, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, + 0x00, 0x9b, 0x00, 0x95, 0x00, 0x90, 0x01, 0x8c, 0x02, 0x87, 0x02, 0x84, + 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x06, 0x76, 0x07, 0x74, + 0x11, 0x7c, 0x13, 0x78, 0x15, 0x76, 0x17, 0x72, 0x18, 0x6f, 0x1a, 0x6d, + 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x64, + 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, + 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, + 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0xb2, + 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0b, 0x3b, 0x00, 0x6f, 0x00, 0x9f, 0x00, 0xaf, + 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, + 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, + 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, + 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, + 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc0, + 0x00, 0xba, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, + 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, + 0x04, 0x7d, 0x05, 0x79, 0x06, 0x78, 0x06, 0x76, 0x11, 0x7c, 0x13, 0x79, + 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, + 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, 0x63, 0x22, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, + 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, + 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x6d, 0x00, 0x54, + 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, 0xb0, 0x00, 0xb3, + 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, + 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x17, 0x07, 0x02, 0x27, 0x00, 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, + 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, + 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x0f, 0x0f, 0x00, 0x3f, + 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, + 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbd, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, + 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, + 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, + 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, + 0x00, 0xae, 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x96, 0x00, 0x92, + 0x01, 0x8e, 0x01, 0x89, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, + 0x05, 0x7a, 0x06, 0x78, 0x11, 0x7c, 0x13, 0x79, 0x15, 0x76, 0x16, 0x73, + 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, + 0x1e, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x5f, 0x24, 0x5c, 0x26, 0x5c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, + 0x00, 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, + 0x00, 0xb6, 0x00, 0xb7, 0x00, 0x8d, 0x00, 0x6b, 0x00, 0x28, 0x00, 0x52, + 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, 0xa6, 0x00, 0xab, + 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x12, 0x02, + 0x00, 0x22, 0x00, 0x58, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, + 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, + 0x00, 0xb8, 0x00, 0xb9, 0x26, 0x00, 0x05, 0x05, 0x00, 0x3f, 0x00, 0x6d, + 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, + 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, + 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, + 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x00, 0xd9, 0x00, 0xd2, + 0x00, 0xca, 0x00, 0xc4, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, + 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, + 0x02, 0x89, 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x05, 0x7a, + 0x11, 0x7c, 0x13, 0x7a, 0x15, 0x76, 0x15, 0x74, 0x18, 0x72, 0x18, 0x6f, + 0x1a, 0x6d, 0x1a, 0x6c, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x67, 0x1f, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x5f, 0x24, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, + 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, 0x39, 0x00, 0x57, + 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, + 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x25, 0x00, 0x0b, 0x09, 0x00, 0x21, + 0x00, 0x4a, 0x00, 0x68, 0x00, 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, + 0x00, 0xa3, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, + 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, + 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, + 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, + 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x2a, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc6, + 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa6, 0x00, 0xa1, + 0x00, 0x9d, 0x00, 0x97, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x02, 0x88, + 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x11, 0x7c, 0x13, 0x7a, + 0x15, 0x76, 0x15, 0x75, 0x18, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, + 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, + 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, + 0x00, 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, + 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0x98, + 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, + 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, + 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x00, 0x2f, 0x00, 0x16, 0x00, 0x08, 0x0e, 0x00, 0x20, 0x00, 0x42, + 0x00, 0x5c, 0x00, 0x6f, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, + 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x38, 0x00, 0x2a, 0x00, + 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, + 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xab, 0x00, 0xad, 0x16, 0xa4, 0x01, 0xbb, 0x00, 0xc6, 0x00, 0xcc, + 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, + 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, + 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x05, 0x9d, 0x00, 0xbc, + 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd5, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, + 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x02, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, 0xd5, + 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xdb, + 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, + 0x00, 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, + 0x00, 0x93, 0x00, 0x98, 0x00, 0xb2, 0x00, 0xa6, 0x00, 0x85, 0x00, 0x57, + 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x52, 0x00, 0x64, + 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, + 0x23, 0x00, 0x0e, 0x02, 0x06, 0x12, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x52, + 0x00, 0x64, 0x00, 0x73, 0x00, 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, + 0x00, 0x9a, 0x00, 0x9e, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x00, 0x00, 0x05, + 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, + 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, + 0x23, 0x74, 0x09, 0x91, 0x02, 0xa3, 0x00, 0xaf, 0x00, 0xb7, 0x00, 0xbd, + 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, + 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, + 0x00, 0xd2, 0x00, 0xd3, 0x0d, 0x6b, 0x01, 0x8e, 0x00, 0xa4, 0x00, 0xb0, + 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc8, 0x00, 0xca, + 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, + 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x06, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, 0xcd, + 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd7, + 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, + 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, + 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, + 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, + 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x00, + 0x0c, 0x07, 0x05, 0x14, 0x00, 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, + 0x00, 0x6a, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, + 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x00, 0x0f, 0x00, 0x29, + 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, + 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x2a, 0x61, 0x11, 0x78, + 0x07, 0x8a, 0x03, 0x98, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, + 0x00, 0xba, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc6, + 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, + 0x12, 0x51, 0x04, 0x73, 0x00, 0x89, 0x00, 0x9a, 0x00, 0xa4, 0x00, 0xad, + 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc3, + 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, + 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x09, 0x97, 0x02, 0xa8, + 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xca, + 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, + 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, 0xd5, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, + 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0xbb, 0x00, 0xb3, + 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, + 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, + 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x00, 0x0a, 0x0b, + 0x04, 0x16, 0x00, 0x20, 0x00, 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, + 0x00, 0x6d, 0x00, 0x77, 0x00, 0x7f, 0x00, 0x85, 0x3d, 0x00, 0x38, 0x00, + 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, + 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, + 0x00, 0x8b, 0x00, 0x91, 0x2e, 0x59, 0x17, 0x6b, 0x0c, 0x7a, 0x06, 0x87, + 0x03, 0x92, 0x01, 0x9b, 0x00, 0xa2, 0x00, 0xa8, 0x00, 0xad, 0x00, 0xb1, + 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, + 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x15, 0x45, 0x08, 0x60, + 0x02, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, 0x9d, 0x00, 0xa4, 0x00, 0xaa, + 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, + 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0a, 0x91, 0x04, 0x9e, 0x01, 0xaa, 0x00, 0xb2, + 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, 0xc8, + 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, + 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, + 0x00, 0x60, 0x00, 0x6a, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, + 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, + 0x33, 0x00, 0x27, 0x00, 0x18, 0x00, 0x0d, 0x04, 0x08, 0x0e, 0x04, 0x17, + 0x00, 0x20, 0x00, 0x32, 0x00, 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, + 0x00, 0x70, 0x00, 0x78, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, + 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, + 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, + 0x31, 0x53, 0x1c, 0x62, 0x11, 0x70, 0x0a, 0x7b, 0x06, 0x85, 0x03, 0x8e, + 0x02, 0x96, 0x00, 0x9c, 0x00, 0xa2, 0x00, 0xa7, 0x00, 0xab, 0x00, 0xae, + 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, + 0x00, 0xbe, 0x00, 0xbf, 0x17, 0x3d, 0x0b, 0x55, 0x04, 0x67, 0x01, 0x78, + 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, 0x9f, 0x00, 0xa5, 0x00, 0xaa, + 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, + 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0b, 0x8d, 0x05, 0x99, 0x02, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, + 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc7, + 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcd, + 0x00, 0xce, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, + 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, + 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, + 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, + 0x1f, 0x00, 0x12, 0x00, 0x0c, 0x07, 0x07, 0x10, 0x03, 0x18, 0x00, 0x20, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, + 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x00, + 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, + 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x33, 0x50, 0x20, 0x5c, + 0x15, 0x68, 0x0e, 0x72, 0x09, 0x7c, 0x05, 0x84, 0x03, 0x8c, 0x02, 0x92, + 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, + 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, + 0x18, 0x38, 0x0d, 0x4c, 0x06, 0x5e, 0x03, 0x6c, 0x01, 0x7a, 0x00, 0x84, + 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, + 0x00, 0xba, 0x00, 0xbc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0c, 0x8a, 0x06, 0x94, + 0x03, 0x9d, 0x01, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, 0xb9, + 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, + 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0xbd, 0x00, 0xb9, + 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, + 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, + 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, + 0x0e, 0x02, 0x0a, 0x0a, 0x06, 0x11, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2e, + 0x00, 0x3c, 0x00, 0x48, 0x00, 0x53, 0x00, 0x5c, 0x3e, 0x00, 0x3c, 0x00, + 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x14, + 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, + 0x00, 0x68, 0x00, 0x70, 0x34, 0x4e, 0x23, 0x58, 0x18, 0x62, 0x10, 0x6c, + 0x0b, 0x75, 0x08, 0x7c, 0x05, 0x83, 0x03, 0x8a, 0x02, 0x8f, 0x01, 0x95, + 0x00, 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, + 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb5, 0x1a, 0x35, 0x0f, 0x47, + 0x08, 0x55, 0x04, 0x64, 0x02, 0x70, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8b, + 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x89, 0x07, 0x92, 0x04, 0x99, 0x02, 0xa0, + 0x01, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, + 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc5, + 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2d, 0x00, 0x39, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, + 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, 0x3a, 0x00, 0x25, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, + 0x38, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x0d, 0x05, + 0x09, 0x0c, 0x06, 0x13, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x3a, + 0x00, 0x45, 0x00, 0x4f, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, + 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, 0x00, 0x18, 0x00, 0x26, + 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, + 0x35, 0x4c, 0x26, 0x55, 0x1b, 0x5e, 0x13, 0x67, 0x0e, 0x6f, 0x0a, 0x76, + 0x07, 0x7d, 0x05, 0x82, 0x03, 0x88, 0x02, 0x8e, 0x02, 0x92, 0x01, 0x97, + 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, + 0x00, 0xac, 0x00, 0xaf, 0x1b, 0x33, 0x10, 0x42, 0x0a, 0x50, 0x05, 0x5c, + 0x03, 0x68, 0x01, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x90, + 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0d, 0x88, 0x08, 0x8f, 0x05, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, 0xa7, + 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, + 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc5, + 0x00, 0xc6, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, + 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, + 0x2c, 0x00, 0x23, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x0c, 0x07, 0x08, 0x0e, + 0x05, 0x14, 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, + 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, + 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, + 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x36, 0x4a, 0x28, 0x52, + 0x1d, 0x5b, 0x16, 0x63, 0x10, 0x6a, 0x0c, 0x71, 0x09, 0x77, 0x07, 0x7d, + 0x05, 0x82, 0x03, 0x87, 0x02, 0x8c, 0x02, 0x90, 0x01, 0x94, 0x00, 0x99, + 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xaa, + 0x1b, 0x30, 0x12, 0x3e, 0x0c, 0x4b, 0x07, 0x57, 0x04, 0x61, 0x02, 0x6b, + 0x01, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8f, 0x00, 0x93, + 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa8, + 0x00, 0xaa, 0x00, 0xac, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x86, 0x09, 0x8d, + 0x06, 0x94, 0x03, 0x9a, 0x02, 0x9f, 0x01, 0xa4, 0x00, 0xa8, 0x00, 0xac, + 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, + 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0xbe, 0x00, 0xbb, + 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, + 0x1d, 0x00, 0x14, 0x00, 0x0e, 0x03, 0x0b, 0x09, 0x08, 0x0f, 0x05, 0x15, + 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x3f, 0x00, 0x3d, 0x00, + 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x00, + 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, + 0x00, 0x49, 0x00, 0x51, 0x37, 0x49, 0x2a, 0x51, 0x20, 0x58, 0x18, 0x5f, + 0x13, 0x66, 0x0f, 0x6c, 0x0b, 0x73, 0x09, 0x78, 0x07, 0x7e, 0x05, 0x82, + 0x03, 0x86, 0x02, 0x8b, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x96, 0x00, 0x9a, + 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa4, 0x1c, 0x2f, 0x13, 0x3b, + 0x0d, 0x47, 0x09, 0x51, 0x05, 0x5c, 0x04, 0x65, 0x02, 0x6e, 0x01, 0x75, + 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8d, 0x00, 0x91, 0x00, 0x96, + 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa7, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x04, 0x97, + 0x02, 0x9c, 0x02, 0xa1, 0x01, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, + 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, + 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, + 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, + 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x10, 0x00, 0x0d, 0x05, 0x0a, 0x0b, 0x07, 0x10, 0x04, 0x16, 0x02, 0x1b, + 0x00, 0x1f, 0x00, 0x2a, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, + 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, + 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, + 0x38, 0x48, 0x2b, 0x4f, 0x22, 0x56, 0x1a, 0x5c, 0x15, 0x63, 0x11, 0x69, + 0x0d, 0x6e, 0x0a, 0x74, 0x08, 0x78, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x85, + 0x03, 0x8a, 0x02, 0x8e, 0x02, 0x90, 0x01, 0x93, 0x00, 0x98, 0x00, 0x9b, + 0x00, 0x9d, 0x00, 0x9f, 0x1c, 0x2d, 0x14, 0x39, 0x0e, 0x44, 0x0a, 0x4e, + 0x07, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, 0x6f, 0x00, 0x76, 0x00, 0x7d, + 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x94, 0x00, 0x97, + 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0e, 0x85, 0x0a, 0x8b, 0x07, 0x90, 0x05, 0x95, 0x03, 0x9a, 0x02, 0x9e, + 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, + 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, + 0x00, 0xbe, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, + 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, + 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, + 0x32, 0x00, 0x2c, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0e, 0x01, + 0x0c, 0x07, 0x09, 0x0c, 0x06, 0x11, 0x04, 0x16, 0x02, 0x1b, 0x00, 0x1f, + 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, + 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x19, + 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x38, 0x48, 0x2d, 0x4e, + 0x23, 0x54, 0x1c, 0x5a, 0x17, 0x5f, 0x12, 0x66, 0x0f, 0x6a, 0x0c, 0x71, + 0x0a, 0x74, 0x08, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x89, + 0x02, 0x8d, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x95, 0x00, 0x99, 0x00, 0x9b, + 0x1c, 0x2d, 0x15, 0x37, 0x0f, 0x41, 0x0b, 0x4a, 0x07, 0x53, 0x05, 0x5b, + 0x04, 0x63, 0x02, 0x69, 0x01, 0x70, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, + 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x96, 0x00, 0x99, + 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x85, 0x0a, 0x8a, + 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x02, 0xa0, 0x01, 0xa3, + 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb3, + 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x39, 0x47, 0x2e, 0x4d, 0x25, 0x53, 0x1e, 0x58, + 0x19, 0x5e, 0x14, 0x63, 0x11, 0x68, 0x0e, 0x6c, 0x0b, 0x72, 0x09, 0x75, + 0x07, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8c, + 0x02, 0x8e, 0x01, 0x91, 0x01, 0x93, 0x00, 0x97, 0x1d, 0x2c, 0x16, 0x36, + 0x10, 0x3e, 0x0c, 0x48, 0x09, 0x50, 0x06, 0x58, 0x04, 0x5f, 0x02, 0x66, + 0x02, 0x6c, 0x01, 0x71, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, + 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x84, 0x0b, 0x89, 0x08, 0x8d, 0x06, 0x92, + 0x04, 0x96, 0x03, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa7, + 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, + 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x39, 0x46, 0x2f, 0x4c, 0x26, 0x51, 0x20, 0x56, 0x1a, 0x5c, 0x16, 0x60, + 0x12, 0x66, 0x0f, 0x69, 0x0c, 0x6e, 0x0b, 0x73, 0x09, 0x76, 0x07, 0x7a, + 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8e, + 0x02, 0x90, 0x01, 0x92, 0x1d, 0x2b, 0x16, 0x34, 0x11, 0x3d, 0x0c, 0x45, + 0x0a, 0x4c, 0x07, 0x54, 0x05, 0x5b, 0x04, 0x61, 0x02, 0x67, 0x01, 0x6d, + 0x01, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x89, + 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x05, 0x94, 0x03, 0x98, + 0x02, 0x9c, 0x02, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, + 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, + 0x00, 0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x46, 0x30, 0x4b, + 0x28, 0x50, 0x21, 0x55, 0x1c, 0x59, 0x17, 0x5e, 0x14, 0x63, 0x10, 0x68, + 0x0e, 0x6b, 0x0c, 0x70, 0x0a, 0x74, 0x08, 0x76, 0x07, 0x7b, 0x06, 0x7e, + 0x05, 0x81, 0x04, 0x83, 0x03, 0x87, 0x02, 0x8a, 0x02, 0x8d, 0x02, 0x8f, + 0x1e, 0x2a, 0x17, 0x33, 0x12, 0x3b, 0x0e, 0x43, 0x0a, 0x4a, 0x07, 0x51, + 0x05, 0x58, 0x04, 0x5e, 0x03, 0x64, 0x02, 0x69, 0x01, 0x6f, 0x00, 0x74, + 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, + 0x00, 0x8f, 0x00, 0x93, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0b, 0x88, + 0x09, 0x8c, 0x07, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, + 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, + 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x46, 0x31, 0x4a, 0x29, 0x4e, 0x22, 0x54, + 0x1d, 0x57, 0x19, 0x5d, 0x15, 0x60, 0x12, 0x65, 0x0f, 0x69, 0x0d, 0x6c, + 0x0b, 0x71, 0x09, 0x74, 0x08, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, + 0x04, 0x83, 0x03, 0x86, 0x02, 0x8a, 0x02, 0x8c, 0x1e, 0x2a, 0x18, 0x32, + 0x12, 0x39, 0x0f, 0x40, 0x0c, 0x48, 0x09, 0x4f, 0x07, 0x55, 0x05, 0x5a, + 0x04, 0x60, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x70, 0x00, 0x75, 0x00, 0x79, + 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8b, 0x07, 0x8e, + 0x06, 0x92, 0x04, 0x96, 0x03, 0x99, 0x02, 0x9b, 0x02, 0x9e, 0x01, 0xa1, + 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, + 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x45, 0x31, 0x49, 0x2a, 0x4d, 0x24, 0x52, 0x1f, 0x56, 0x1a, 0x5b, + 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, 0x0e, 0x6a, 0x0c, 0x6e, 0x0b, 0x72, + 0x09, 0x75, 0x07, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, + 0x03, 0x85, 0x02, 0x89, 0x1e, 0x29, 0x18, 0x31, 0x13, 0x38, 0x0f, 0x3f, + 0x0c, 0x45, 0x0a, 0x4c, 0x07, 0x52, 0x05, 0x58, 0x04, 0x5d, 0x04, 0x63, + 0x02, 0x67, 0x01, 0x6c, 0x01, 0x71, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, + 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x05, 0x94, + 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x02, 0xa0, 0x01, 0xa2, 0x00, 0xa4, + 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, + 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0x32, 0x48, + 0x2b, 0x4d, 0x25, 0x51, 0x20, 0x55, 0x1b, 0x59, 0x18, 0x5e, 0x14, 0x60, + 0x12, 0x65, 0x0f, 0x68, 0x0d, 0x6b, 0x0c, 0x6f, 0x0a, 0x73, 0x09, 0x75, + 0x07, 0x77, 0x06, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, + 0x1e, 0x29, 0x19, 0x30, 0x14, 0x37, 0x0f, 0x3d, 0x0c, 0x44, 0x0a, 0x4a, + 0x07, 0x50, 0x06, 0x55, 0x05, 0x5b, 0x04, 0x60, 0x02, 0x64, 0x02, 0x69, + 0x01, 0x6d, 0x01, 0x72, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, + 0x00, 0x83, 0x00, 0x87, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x86, + 0x0a, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x03, 0x99, + 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa5, 0x00, 0xa7, + 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x45, 0x33, 0x48, 0x2c, 0x4d, 0x26, 0x4f, + 0x21, 0x55, 0x1c, 0x57, 0x19, 0x5c, 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, + 0x0e, 0x6a, 0x0c, 0x6c, 0x0b, 0x71, 0x0a, 0x73, 0x09, 0x76, 0x07, 0x78, + 0x06, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x1e, 0x28, 0x19, 0x2f, + 0x15, 0x35, 0x10, 0x3c, 0x0d, 0x42, 0x0b, 0x48, 0x09, 0x4e, 0x07, 0x53, + 0x05, 0x58, 0x04, 0x5d, 0x04, 0x62, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x6f, + 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, + 0x06, 0x8f, 0x05, 0x92, 0x04, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, 0x9d, + 0x02, 0x9f, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xa9, + 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x44, 0x33, 0x47, 0x2c, 0x4c, 0x27, 0x4f, 0x22, 0x54, 0x1e, 0x56, + 0x1a, 0x5a, 0x17, 0x5e, 0x14, 0x60, 0x12, 0x65, 0x10, 0x68, 0x0e, 0x6a, + 0x0c, 0x6e, 0x0b, 0x72, 0x09, 0x74, 0x08, 0x76, 0x07, 0x78, 0x06, 0x7c, + 0x06, 0x7f, 0x05, 0x81, 0x1e, 0x28, 0x19, 0x2f, 0x16, 0x35, 0x12, 0x3b, + 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4c, 0x07, 0x51, 0x05, 0x55, 0x05, 0x5b, + 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, 0x6b, 0x01, 0x70, 0x00, 0x73, + 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x81, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x0f, 0x82, 0x0c, 0x86, 0x0b, 0x89, 0x09, 0x8c, 0x07, 0x8f, 0x06, 0x91, + 0x05, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, + 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xac, + 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x23, 0x25, 0x22, + 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0x44, 0x05, 0x25, 0x0d, 0x23, 0x12, 0x22, + 0x15, 0x22, 0x17, 0x22, 0x18, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1b, 0x22, + 0x1c, 0x22, 0x1c, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1d, 0x22, 0x1e, 0x22, + 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x10, 0x33, 0x13, 0x23, + 0x17, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1e, 0x22, + 0x1e, 0x22, 0x1e, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, + 0x1f, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x35, 0x25, 0x2b, 0x23, 0x27, 0x22, 0x25, 0x22, + 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0x9d, 0x00, 0x6b, 0x01, 0x51, 0x04, 0x45, 0x08, 0x3d, 0x0b, 0x38, + 0x0d, 0x35, 0x0f, 0x33, 0x10, 0x30, 0x12, 0x2f, 0x13, 0x2d, 0x14, 0x2d, + 0x15, 0x2c, 0x16, 0x2b, 0x16, 0x2a, 0x17, 0x2a, 0x18, 0x29, 0x18, 0x29, + 0x19, 0x28, 0x19, 0x28, 0x10, 0x5f, 0x10, 0x46, 0x11, 0x39, 0x13, 0x33, + 0x15, 0x2f, 0x16, 0x2d, 0x17, 0x2b, 0x18, 0x2a, 0x19, 0x29, 0x1a, 0x28, + 0x1a, 0x27, 0x1b, 0x27, 0x1b, 0x27, 0x1c, 0x26, 0x1c, 0x26, 0x1c, 0x26, + 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x2a, 0x2e, 0x26, 0x2a, 0x25, 0x27, 0x24, 0x26, 0x23, 0x25, 0x23, + 0x24, 0x23, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xbc, 0x00, 0x8e, + 0x00, 0x73, 0x00, 0x60, 0x02, 0x55, 0x04, 0x4c, 0x06, 0x47, 0x08, 0x42, + 0x0a, 0x3e, 0x0c, 0x3b, 0x0d, 0x39, 0x0e, 0x37, 0x0f, 0x36, 0x10, 0x34, + 0x11, 0x33, 0x12, 0x32, 0x12, 0x31, 0x13, 0x30, 0x14, 0x2f, 0x15, 0x2f, + 0x10, 0x6f, 0x10, 0x58, 0x10, 0x4a, 0x11, 0x41, 0x12, 0x3b, 0x13, 0x37, + 0x14, 0x34, 0x15, 0x32, 0x16, 0x30, 0x17, 0x2e, 0x17, 0x2d, 0x18, 0x2c, + 0x18, 0x2c, 0x19, 0x2b, 0x19, 0x2a, 0x1a, 0x2a, 0x1a, 0x29, 0x1a, 0x29, + 0x1b, 0x28, 0x1b, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x2e, 0x31, 0x29, + 0x2c, 0x27, 0x2a, 0x26, 0x28, 0x25, 0x27, 0x24, 0x25, 0x24, 0x25, 0x23, + 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x22, 0x23, 0x22, + 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xc6, 0x00, 0xa4, 0x00, 0x89, 0x00, 0x77, + 0x00, 0x67, 0x01, 0x5e, 0x03, 0x55, 0x04, 0x50, 0x05, 0x4b, 0x07, 0x47, + 0x09, 0x44, 0x0a, 0x41, 0x0b, 0x3e, 0x0c, 0x3d, 0x0c, 0x3b, 0x0e, 0x39, + 0x0f, 0x38, 0x0f, 0x37, 0x0f, 0x35, 0x10, 0x35, 0x10, 0x74, 0x10, 0x63, + 0x10, 0x55, 0x10, 0x4c, 0x11, 0x44, 0x11, 0x40, 0x12, 0x3b, 0x13, 0x39, + 0x13, 0x36, 0x14, 0x34, 0x15, 0x33, 0x16, 0x31, 0x16, 0x30, 0x17, 0x2f, + 0x17, 0x2e, 0x18, 0x2d, 0x18, 0x2d, 0x18, 0x2c, 0x18, 0x2b, 0x19, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x33, 0x2c, 0x2f, 0x29, 0x2b, 0x28, + 0x29, 0x26, 0x28, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, + 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6c, + 0x01, 0x64, 0x02, 0x5c, 0x03, 0x57, 0x04, 0x51, 0x05, 0x4e, 0x07, 0x4a, + 0x07, 0x48, 0x09, 0x45, 0x0a, 0x43, 0x0a, 0x40, 0x0c, 0x3f, 0x0c, 0x3d, + 0x0c, 0x3c, 0x0d, 0x3b, 0x10, 0x77, 0x10, 0x69, 0x10, 0x5e, 0x10, 0x54, + 0x10, 0x4d, 0x11, 0x47, 0x11, 0x43, 0x12, 0x3f, 0x12, 0x3c, 0x13, 0x39, + 0x13, 0x38, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x32, 0x16, 0x31, + 0x17, 0x30, 0x17, 0x2f, 0x17, 0x2f, 0x17, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x33, 0x34, 0x2e, 0x30, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, + 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, + 0x23, 0x23, 0x23, 0x23, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xcf, 0x00, 0xb9, + 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x68, + 0x01, 0x61, 0x02, 0x5c, 0x04, 0x57, 0x04, 0x53, 0x05, 0x50, 0x06, 0x4c, + 0x07, 0x4a, 0x07, 0x48, 0x09, 0x45, 0x0a, 0x44, 0x0a, 0x42, 0x0b, 0x41, + 0x10, 0x78, 0x10, 0x6d, 0x10, 0x63, 0x10, 0x5b, 0x10, 0x53, 0x10, 0x4e, + 0x11, 0x49, 0x11, 0x45, 0x11, 0x41, 0x12, 0x3f, 0x13, 0x3c, 0x13, 0x3a, + 0x13, 0x39, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x33, + 0x16, 0x32, 0x16, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x34, 0x36, 0x30, + 0x32, 0x2d, 0x2f, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, + 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd2, 0x00, 0xbe, 0x00, 0xad, 0x00, 0x9d, + 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x6b, 0x01, 0x65, + 0x02, 0x60, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x54, 0x05, 0x51, 0x05, 0x4f, + 0x07, 0x4c, 0x07, 0x4a, 0x07, 0x48, 0x09, 0x46, 0x10, 0x7a, 0x10, 0x70, + 0x10, 0x67, 0x10, 0x5f, 0x10, 0x59, 0x10, 0x53, 0x10, 0x4e, 0x11, 0x4a, + 0x11, 0x46, 0x11, 0x43, 0x12, 0x41, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x13, 0x39, 0x13, 0x38, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x35, 0x37, 0x31, 0x33, 0x2e, 0x30, 0x2c, + 0x2d, 0x2a, 0x2c, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, + 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x98, 0x00, 0x8d, + 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x00, 0x6e, 0x01, 0x67, 0x01, 0x63, + 0x02, 0x5f, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x05, 0x52, 0x05, 0x50, + 0x06, 0x4e, 0x07, 0x4c, 0x10, 0x7a, 0x10, 0x72, 0x10, 0x6a, 0x10, 0x63, + 0x10, 0x5d, 0x10, 0x57, 0x10, 0x52, 0x10, 0x4e, 0x11, 0x4a, 0x11, 0x48, + 0x11, 0x44, 0x11, 0x42, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x13, 0x3a, 0x13, 0x39, 0x14, 0x38, 0x14, 0x37, 0x0f, 0x00, 0x1f, 0x00, + 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x0b, 0x3b, 0x17, 0x07, 0x2c, 0x00, 0x36, 0x00, + 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x3d, 0x00, + 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3f, 0x00, 0x3f, 0x00, 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, + 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, + 0x3c, 0x37, 0x37, 0x32, 0x34, 0x30, 0x31, 0x2d, 0x2f, 0x2c, 0x2d, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, + 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x25, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc5, + 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x83, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6f, 0x00, 0x69, 0x01, 0x66, 0x02, 0x61, + 0x02, 0x5e, 0x03, 0x5a, 0x04, 0x58, 0x04, 0x55, 0x05, 0x53, 0x05, 0x51, + 0x10, 0x7b, 0x10, 0x73, 0x10, 0x6c, 0x10, 0x66, 0x10, 0x60, 0x10, 0x5b, + 0x10, 0x56, 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, + 0x11, 0x44, 0x12, 0x41, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x13, 0x3a, 0x13, 0x39, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x2e, 0x00, + 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x6f, 0x02, 0x27, 0x12, 0x02, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, + 0x38, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, 0x00, + 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x00, 0x7f, 0x00, 0x3f, 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, + 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, + 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3c, 0x37, 0x38, 0x33, + 0x34, 0x31, 0x32, 0x2f, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x29, + 0x2b, 0x28, 0x29, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, + 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc8, 0x00, 0xbb, 0x00, 0xaf, + 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, 0x82, 0x00, 0x7c, + 0x00, 0x76, 0x00, 0x70, 0x00, 0x6c, 0x01, 0x67, 0x01, 0x64, 0x02, 0x60, + 0x02, 0x5d, 0x04, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x10, 0x7b, 0x10, 0x75, + 0x10, 0x6e, 0x10, 0x68, 0x10, 0x63, 0x10, 0x5e, 0x10, 0x5a, 0x10, 0x56, + 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x44, + 0x11, 0x43, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2c, 0x00, + 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x9f, 0x00, 0x6f, + 0x00, 0x22, 0x0b, 0x09, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, + 0x33, 0x00, 0x35, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, 0x00, + 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, + 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0xa5, 0x00, 0x7f, + 0x00, 0x3f, 0x00, 0x12, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, + 0x3a, 0x00, 0x3a, 0x00, 0x3d, 0x38, 0x39, 0x34, 0x35, 0x32, 0x33, 0x30, + 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x29, 0x2b, 0x28, + 0x29, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, + 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa0, + 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, 0x7d, 0x00, 0x77, + 0x00, 0x71, 0x00, 0x6d, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x02, 0x60, + 0x02, 0x5d, 0x04, 0x5b, 0x10, 0x7c, 0x10, 0x76, 0x10, 0x70, 0x10, 0x6a, + 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, 0x10, 0x59, 0x10, 0x55, 0x10, 0x52, + 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, + 0x12, 0x42, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3a, 0x00, 0x00, 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, + 0x08, 0x0e, 0x0e, 0x02, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x42, 0x00, + 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, + 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3a, 0x00, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, + 0x00, 0x1d, 0x00, 0x05, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, + 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, + 0x3d, 0x38, 0x39, 0x35, 0x36, 0x33, 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, + 0x2e, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, 0x28, 0x28, + 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, + 0x27, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd6, 0x00, 0xcc, + 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x96, + 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x77, 0x00, 0x73, + 0x00, 0x6f, 0x00, 0x6b, 0x01, 0x67, 0x01, 0x64, 0x02, 0x62, 0x02, 0x5f, + 0x10, 0x7c, 0x10, 0x77, 0x10, 0x71, 0x10, 0x6c, 0x10, 0x67, 0x10, 0x63, + 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x58, 0x10, 0x55, 0x10, 0x51, 0x10, 0x4f, + 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, 0x44, 0x11, 0x43, + 0x12, 0x42, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, + 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, + 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x06, 0x12, + 0x0c, 0x07, 0x10, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, + 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, 0x00, + 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, + 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, + 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, + 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, + 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x3d, 0x39, 0x3a, 0x36, + 0x36, 0x33, 0x34, 0x31, 0x32, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, + 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd7, 0x00, 0xcc, 0x00, 0xc3, 0x00, 0xba, + 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8d, + 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x74, 0x00, 0x70, + 0x00, 0x6c, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x10, 0x7c, 0x10, 0x77, + 0x10, 0x72, 0x10, 0x6e, 0x10, 0x69, 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, + 0x10, 0x5a, 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, + 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, 0x12, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, + 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xb9, 0x00, 0xad, + 0x00, 0x8f, 0x00, 0x68, 0x00, 0x42, 0x00, 0x20, 0x05, 0x14, 0x0a, 0x0b, + 0x0d, 0x04, 0x12, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, + 0x29, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, + 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xba, 0x00, 0xb1, + 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, + 0x00, 0x08, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, + 0x22, 0x00, 0x25, 0x00, 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x32, + 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, + 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, 0xb4, 0x00, 0xac, + 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, 0x8c, 0x00, 0x87, + 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, 0x71, 0x00, 0x6d, + 0x01, 0x6b, 0x01, 0x68, 0x10, 0x7c, 0x10, 0x78, 0x10, 0x73, 0x10, 0x6f, + 0x10, 0x6b, 0x10, 0x67, 0x10, 0x63, 0x10, 0x60, 0x10, 0x5c, 0x10, 0x59, + 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, + 0x11, 0x49, 0x11, 0x47, 0x11, 0x46, 0x11, 0x45, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, + 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, + 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x20, 0x04, 0x16, 0x08, 0x0e, 0x0c, 0x07, + 0x0e, 0x02, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, + 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, + 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, + 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, + 0x00, 0x04, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, + 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x32, 0x30, + 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x2a, + 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, + 0x27, 0x27, 0x27, 0x27, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xcf, + 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, + 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, + 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6b, + 0x10, 0x7d, 0x10, 0x78, 0x10, 0x74, 0x10, 0x70, 0x10, 0x6c, 0x10, 0x68, + 0x10, 0x65, 0x10, 0x61, 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x59, 0x10, 0x56, + 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, + 0x11, 0x48, 0x11, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, + 0x00, 0xbc, 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, + 0x00, 0x37, 0x00, 0x20, 0x04, 0x17, 0x07, 0x10, 0x0a, 0x0a, 0x0d, 0x05, + 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, + 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, + 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, + 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, + 0x00, 0x01, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x3d, 0x3a, 0x3a, 0x37, + 0x38, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x2f, 0x30, 0x2d, + 0x2e, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, + 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc0, + 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x99, + 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, 0x00, 0x7d, + 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x10, 0x7d, 0x10, 0x79, + 0x10, 0x75, 0x10, 0x71, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x66, 0x10, 0x63, + 0x10, 0x60, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x58, 0x10, 0x56, 0x10, 0x54, + 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, + 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbc, 0x00, 0xb8, + 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, + 0x00, 0x20, 0x03, 0x18, 0x06, 0x11, 0x09, 0x0c, 0x0c, 0x07, 0x0e, 0x03, + 0x10, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, + 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbd, 0x00, 0xb9, + 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, + 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, + 0x00, 0x00, 0x07, 0x00, 0x3d, 0x3a, 0x3a, 0x38, 0x38, 0x36, 0x36, 0x33, + 0x34, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, + 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, + 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x92, + 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, + 0x00, 0x76, 0x00, 0x73, 0x10, 0x7d, 0x10, 0x79, 0x10, 0x75, 0x10, 0x72, + 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, + 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, + 0x10, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, + 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, + 0x00, 0x89, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, + 0x03, 0x19, 0x06, 0x13, 0x08, 0x0e, 0x0b, 0x09, 0x0d, 0x05, 0x0e, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, + 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, + 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, + 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, + 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, + 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x36, 0x34, 0x35, 0x33, 0x33, 0x31, + 0x32, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, + 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd1, + 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, + 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, + 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x77, + 0x10, 0x7d, 0x10, 0x79, 0x10, 0x76, 0x10, 0x72, 0x10, 0x6f, 0x10, 0x6c, + 0x10, 0x69, 0x10, 0x66, 0x10, 0x63, 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, + 0x10, 0x59, 0x10, 0x57, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, + 0x11, 0x4e, 0x11, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, + 0x00, 0x6a, 0x00, 0x56, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x03, 0x19, + 0x05, 0x14, 0x08, 0x0f, 0x0a, 0x0b, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, + 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, + 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, + 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, + 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x3d, 0x3b, 0x3b, 0x39, + 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x30, + 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, + 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc4, + 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, 0xa8, 0x00, 0xa2, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, 0x8b, 0x00, 0x88, + 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x10, 0x7d, 0x10, 0x7a, + 0x10, 0x76, 0x10, 0x73, 0x10, 0x70, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x67, + 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x59, + 0x10, 0x56, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbd, 0x00, 0xbb, + 0x00, 0xb3, 0x00, 0xa8, 0x00, 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, + 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x02, 0x1a, 0x05, 0x15, + 0x07, 0x10, 0x09, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, + 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbe, 0x00, 0xbc, + 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, + 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, + 0x00, 0x21, 0x00, 0x19, 0x3d, 0x3b, 0x3b, 0x39, 0x39, 0x36, 0x37, 0x35, + 0x36, 0x33, 0x33, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, + 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, + 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, + 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xba, + 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9b, + 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x88, 0x00, 0x83, + 0x00, 0x81, 0x00, 0x7d, 0x10, 0x7d, 0x10, 0x7a, 0x10, 0x77, 0x10, 0x74, + 0x10, 0x71, 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x63, + 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x56, + 0x10, 0x55, 0x10, 0x52, 0x10, 0x51, 0x10, 0x4f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, + 0x00, 0x9e, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, + 0x00, 0x3c, 0x00, 0x2d, 0x00, 0x1f, 0x02, 0x1a, 0x04, 0x16, 0x06, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, + 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, + 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, + 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, + 0x27, 0x76, 0x1a, 0x79, 0x16, 0x7b, 0x15, 0x7c, 0x14, 0x7c, 0x13, 0x7d, + 0x13, 0x7d, 0x13, 0x7d, 0x12, 0x7d, 0x12, 0x7d, 0x12, 0x7e, 0x12, 0x7e, + 0x12, 0x7e, 0x12, 0x7e, 0x12, 0x7e, 0x11, 0x7e, 0x11, 0x7e, 0x11, 0x7e, + 0x11, 0x7f, 0x11, 0x7f, 0x13, 0x5f, 0x10, 0x6f, 0x10, 0x74, 0x10, 0x77, + 0x10, 0x78, 0x10, 0x7a, 0x10, 0x7a, 0x10, 0x7b, 0x10, 0x7b, 0x10, 0x7c, + 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, + 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x00, 0x90, 0x02, 0x81, + 0x06, 0x80, 0x09, 0x7f, 0x0a, 0x7f, 0x0b, 0x7f, 0x0c, 0x7f, 0x0d, 0x7f, + 0x0d, 0x7f, 0x0d, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, + 0x0e, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, + 0x00, 0x86, 0x00, 0x77, 0x00, 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, + 0x00, 0x2c, 0x00, 0x1f, 0x02, 0x1b, 0x04, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, + 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, + 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, + 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, + 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x32, 0x5f, 0x25, 0x68, + 0x1f, 0x6d, 0x1c, 0x71, 0x19, 0x72, 0x18, 0x74, 0x17, 0x76, 0x16, 0x77, + 0x16, 0x77, 0x15, 0x78, 0x15, 0x79, 0x15, 0x79, 0x14, 0x7a, 0x14, 0x7a, + 0x13, 0x7a, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, + 0x17, 0x46, 0x11, 0x58, 0x10, 0x63, 0x10, 0x69, 0x10, 0x6d, 0x10, 0x70, + 0x10, 0x72, 0x10, 0x73, 0x10, 0x75, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, + 0x10, 0x78, 0x10, 0x78, 0x10, 0x79, 0x10, 0x79, 0x10, 0x79, 0x10, 0x7a, + 0x10, 0x7a, 0x10, 0x7a, 0x00, 0xbd, 0x00, 0xa4, 0x00, 0x97, 0x02, 0x91, + 0x04, 0x8d, 0x05, 0x8a, 0x06, 0x89, 0x07, 0x88, 0x08, 0x86, 0x09, 0x86, + 0x09, 0x85, 0x0a, 0x85, 0x0a, 0x84, 0x0b, 0x84, 0x0b, 0x83, 0x0b, 0x83, + 0x0c, 0x83, 0x0c, 0x83, 0x0c, 0x82, 0x0c, 0x82, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbc, + 0x00, 0xb8, 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, + 0x00, 0x70, 0x00, 0x61, 0x00, 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, + 0x00, 0x1f, 0x02, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, + 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, + 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbd, + 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, + 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, + 0x00, 0x3f, 0x00, 0x36, 0x36, 0x56, 0x2a, 0x5f, 0x24, 0x65, 0x20, 0x68, + 0x1e, 0x6c, 0x1c, 0x6e, 0x1b, 0x70, 0x19, 0x71, 0x18, 0x72, 0x18, 0x73, + 0x18, 0x75, 0x17, 0x75, 0x16, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x77, + 0x15, 0x77, 0x15, 0x77, 0x15, 0x78, 0x15, 0x79, 0x1a, 0x39, 0x13, 0x4a, + 0x11, 0x55, 0x10, 0x5e, 0x10, 0x63, 0x10, 0x67, 0x10, 0x6a, 0x10, 0x6c, + 0x10, 0x6e, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, + 0x10, 0x75, 0x10, 0x75, 0x10, 0x76, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, + 0x00, 0xcc, 0x00, 0xb5, 0x00, 0xa8, 0x00, 0x9e, 0x01, 0x99, 0x02, 0x94, + 0x03, 0x92, 0x04, 0x8f, 0x05, 0x8d, 0x06, 0x8c, 0x06, 0x8b, 0x07, 0x8a, + 0x07, 0x89, 0x08, 0x88, 0x08, 0x88, 0x09, 0x87, 0x09, 0x87, 0x09, 0x86, + 0x0a, 0x86, 0x0a, 0x86, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, + 0x00, 0xa9, 0x00, 0x9e, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, + 0x00, 0x5c, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, + 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, + 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, + 0x04, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, + 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, + 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, + 0x38, 0x52, 0x2e, 0x59, 0x28, 0x5f, 0x24, 0x63, 0x22, 0x67, 0x1f, 0x69, + 0x1e, 0x6b, 0x1c, 0x6d, 0x1b, 0x6e, 0x1a, 0x70, 0x1a, 0x71, 0x19, 0x72, + 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x17, 0x75, 0x17, 0x76, 0x16, 0x76, + 0x15, 0x76, 0x15, 0x76, 0x1b, 0x33, 0x15, 0x41, 0x12, 0x4c, 0x11, 0x54, + 0x10, 0x5b, 0x10, 0x5f, 0x10, 0x63, 0x10, 0x66, 0x10, 0x68, 0x10, 0x6a, + 0x10, 0x6c, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, + 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, 0x10, 0x74, 0x00, 0xd1, 0x00, 0xc0, + 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9d, 0x01, 0x99, 0x02, 0x96, + 0x02, 0x94, 0x03, 0x92, 0x04, 0x90, 0x05, 0x8f, 0x05, 0x8d, 0x06, 0x8d, + 0x06, 0x8c, 0x07, 0x8b, 0x07, 0x8a, 0x07, 0x8a, 0x07, 0x89, 0x08, 0x89, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, + 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, + 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, + 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, + 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x4f, 0x31, 0x55, + 0x2b, 0x5b, 0x27, 0x5f, 0x24, 0x63, 0x22, 0x65, 0x20, 0x68, 0x1f, 0x69, + 0x1d, 0x6b, 0x1d, 0x6d, 0x1b, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, 0x1a, 0x71, + 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x18, 0x74, + 0x1c, 0x2f, 0x16, 0x3b, 0x13, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x10, 0x59, + 0x10, 0x5d, 0x10, 0x60, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x69, + 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, + 0x10, 0x71, 0x10, 0x71, 0x00, 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, + 0x00, 0xaa, 0x00, 0xa4, 0x00, 0xa0, 0x01, 0x9c, 0x01, 0x9a, 0x02, 0x97, + 0x02, 0x95, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, 0x05, 0x8e, + 0x06, 0x8e, 0x06, 0x8d, 0x06, 0x8c, 0x06, 0x8c, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, + 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, + 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, + 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0f, 0x0f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, + 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, + 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x4c, 0x33, 0x53, 0x2e, 0x58, 0x29, 0x5c, + 0x27, 0x5f, 0x24, 0x62, 0x22, 0x64, 0x20, 0x66, 0x20, 0x68, 0x1e, 0x69, + 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, + 0x1a, 0x71, 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x1d, 0x2d, 0x17, 0x37, + 0x14, 0x40, 0x12, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x10, 0x57, 0x10, 0x5b, + 0x10, 0x5e, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x68, + 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, + 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, + 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9f, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, + 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, + 0x05, 0x8f, 0x05, 0x8f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, + 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, + 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x05, 0x05, + 0x00, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, + 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, + 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x4b, 0x34, 0x51, 0x2f, 0x55, 0x2c, 0x59, 0x29, 0x5c, 0x26, 0x60, + 0x24, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, + 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, + 0x1a, 0x6f, 0x1a, 0x71, 0x1e, 0x2b, 0x18, 0x34, 0x15, 0x3b, 0x13, 0x43, + 0x12, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x5a, 0x10, 0x5d, + 0x10, 0x5f, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x68, + 0x10, 0x69, 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x00, 0xd7, 0x00, 0xcd, + 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, + 0x00, 0xa4, 0x00, 0xa1, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, + 0x02, 0x97, 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, + 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, + 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, + 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, + 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x49, 0x36, 0x4f, + 0x31, 0x54, 0x2d, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x26, 0x60, 0x24, 0x61, + 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, + 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, + 0x1e, 0x2a, 0x19, 0x32, 0x16, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, 0x4a, + 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, + 0x10, 0x60, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x67, + 0x10, 0x68, 0x10, 0x69, 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, + 0x00, 0xba, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, + 0x00, 0xa2, 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x99, + 0x02, 0x97, 0x02, 0x96, 0x03, 0x95, 0x03, 0x94, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, + 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, + 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, + 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x38, 0x00, 0x2a, 0x00, 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, + 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, + 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x48, 0x37, 0x4d, 0x32, 0x51, 0x2f, 0x55, + 0x2c, 0x58, 0x29, 0x5b, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x63, + 0x22, 0x65, 0x20, 0x65, 0x20, 0x66, 0x1f, 0x68, 0x1e, 0x69, 0x1d, 0x69, + 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1e, 0x29, 0x1a, 0x30, + 0x17, 0x36, 0x14, 0x3c, 0x13, 0x41, 0x12, 0x46, 0x11, 0x4a, 0x11, 0x4f, + 0x11, 0x52, 0x10, 0x55, 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5f, + 0x10, 0x60, 0x10, 0x62, 0x10, 0x63, 0x10, 0x65, 0x10, 0x65, 0x10, 0x67, + 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, + 0x00, 0xb4, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, + 0x00, 0xa1, 0x01, 0x9f, 0x01, 0x9d, 0x01, 0x9b, 0x02, 0x9a, 0x02, 0x99, + 0x02, 0x98, 0x02, 0x97, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, + 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x02, 0x00, + 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, + 0x19, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, + 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, + 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x48, 0x37, 0x4c, 0x33, 0x50, 0x30, 0x54, 0x2d, 0x57, 0x2a, 0x59, + 0x29, 0x5c, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x62, 0x22, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, 0x1d, 0x69, + 0x1d, 0x69, 0x1d, 0x6a, 0x1f, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x15, 0x39, + 0x13, 0x3f, 0x13, 0x43, 0x12, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, 0x52, + 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5f, + 0x10, 0x61, 0x10, 0x62, 0x10, 0x63, 0x10, 0x64, 0x00, 0xd9, 0x00, 0xd2, + 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, + 0x00, 0xaf, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, + 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, + 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x04, 0x00, + 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, + 0x00, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, + 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, + 0x34, 0x4f, 0x31, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x5a, 0x28, 0x5c, + 0x27, 0x5d, 0x26, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, + 0x20, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, 0x1d, 0x69, + 0x1f, 0x27, 0x1b, 0x2d, 0x18, 0x33, 0x16, 0x38, 0x14, 0x3c, 0x13, 0x41, + 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, + 0x10, 0x57, 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x10, 0x5f, + 0x10, 0x61, 0x10, 0x62, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, + 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, + 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x00, 0xa1, + 0x01, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, + 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, + 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, + 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, + 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x51, + 0x2f, 0x53, 0x2d, 0x57, 0x2b, 0x58, 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5d, + 0x25, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, + 0x20, 0x65, 0x20, 0x66, 0x1f, 0x69, 0x1e, 0x69, 0x1f, 0x27, 0x1b, 0x2c, + 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x13, 0x42, 0x12, 0x45, + 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, + 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5e, 0x10, 0x60, + 0x00, 0xd9, 0x00, 0xd4, 0x00, 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, + 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, + 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, 0x00, 0xa0, + 0x01, 0x9f, 0x01, 0x9e, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, + 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, + 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, + 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, + 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, + 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x46, 0x39, 0x4a, 0x35, 0x4d, 0x32, 0x50, 0x30, 0x53, 0x2e, 0x55, + 0x2c, 0x57, 0x2a, 0x58, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5d, 0x25, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, + 0x20, 0x65, 0x20, 0x67, 0x1f, 0x27, 0x1c, 0x2c, 0x19, 0x30, 0x17, 0x35, + 0x15, 0x39, 0x14, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x12, 0x47, 0x11, 0x49, + 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, 0x10, 0x58, + 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x00, 0xda, 0x00, 0xd4, + 0x00, 0xd0, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, + 0x00, 0xb8, 0x00, 0xb5, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, + 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa3, 0x00, 0xa1, 0x01, 0xa0, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, + 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, + 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, + 0x14, 0x00, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x46, 0x39, 0x49, + 0x36, 0x4c, 0x33, 0x4f, 0x31, 0x52, 0x2e, 0x53, 0x2d, 0x57, 0x2b, 0x57, + 0x2a, 0x59, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x62, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, + 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x16, 0x37, 0x14, 0x3b, + 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4d, + 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, + 0x10, 0x5a, 0x10, 0x5b, 0x00, 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, + 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, + 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, + 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa2, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, + 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, + 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, + 0x00, 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, + 0x00, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, + 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, 0x36, 0x4b, 0x34, 0x4f, + 0x31, 0x50, 0x30, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x5a, + 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x61, 0x22, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x26, 0x1c, 0x2a, + 0x1a, 0x2e, 0x18, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, + 0x12, 0x43, 0x12, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, + 0x11, 0x51, 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x10, 0x5a, + 0x00, 0xda, 0x00, 0xd6, 0x00, 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, + 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, + 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, + 0x00, 0xa6, 0x00, 0xa4, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, + 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, + 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, + 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, + 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, + 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x45, 0x3a, 0x48, 0x37, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x53, + 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x58, 0x29, 0x5b, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, + 0x23, 0x64, 0x21, 0x65, 0x20, 0x26, 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x31, + 0x17, 0x35, 0x15, 0x38, 0x14, 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, + 0x12, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, + 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x00, 0xda, 0x00, 0xd6, + 0x00, 0xd2, 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, + 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, + 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, + 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, + 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, + 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, + 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, + 0x38, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x54, + 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, + 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x16, 0x37, + 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x13, 0x42, 0x12, 0x44, 0x11, 0x47, + 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x53, + 0x10, 0x55, 0x10, 0x56, 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, + 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, + 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, + 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, + 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, + 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, + 0x00, 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, + 0x12, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, + 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4c, + 0x33, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, + 0x2a, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, + 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x29, + 0x1b, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x14, 0x3b, + 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x12, 0x45, 0x11, 0x47, 0x11, 0x4a, + 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x10, 0x54, + 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, + 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, + 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, + 0x00, 0xab, 0x00, 0xaa, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, + 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, + 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, + 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, + 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x4a, 0x35, 0x4b, 0x34, 0x4f, 0x31, 0x4f, + 0x31, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x58, + 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, 0x24, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2f, + 0x17, 0x32, 0x16, 0x35, 0x15, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, + 0x13, 0x42, 0x12, 0x44, 0x12, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, 0x4c, + 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x00, 0xdb, 0x00, 0xd7, + 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, + 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, + 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, + 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, + 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, + 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, + 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, + 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x51, 0x2f, 0x53, + 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x29, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x26, 0x5f, 0x24, 0x60, 0x23, 0x60, + 0x20, 0x25, 0x1d, 0x28, 0x1c, 0x2b, 0x1a, 0x2e, 0x18, 0x31, 0x17, 0x34, + 0x16, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, 0x42, + 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, 0x4e, + 0x11, 0x4f, 0x11, 0x51, 0x00, 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, + 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, + 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, + 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x56, 0x7f, 0x47, 0x63, + 0x44, 0x57, 0x43, 0x52, 0x43, 0x4e, 0x42, 0x4c, 0x42, 0x4a, 0x42, 0x49, + 0x42, 0x48, 0x42, 0x47, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, + 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, + 0x4c, 0x7f, 0x27, 0x4d, 0x2f, 0x46, 0x33, 0x44, 0x36, 0x43, 0x37, 0x43, + 0x39, 0x42, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, 0x3b, 0x41, + 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x8e, 0x7f, 0x47, 0x4d, 0x44, 0x46, 0x43, 0x44, + 0x43, 0x43, 0x42, 0x43, 0x42, 0x42, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, + 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x85, 0x1b, 0x60, 0x27, 0x54, 0x2d, 0x4e, 0x30, + 0x4b, 0x33, 0x49, 0x34, 0x47, 0x35, 0x46, 0x37, 0x45, 0x38, 0x45, 0x38, + 0x44, 0x39, 0x43, 0x39, 0x43, 0x3a, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, + 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x09, 0x43, 0x16, 0x3f, + 0x1f, 0x3e, 0x25, 0x3e, 0x29, 0x3e, 0x2c, 0x3e, 0x2f, 0x3e, 0x30, 0x3e, + 0x32, 0x3e, 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x36, 0x3e, + 0x37, 0x3e, 0x37, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x39, 0x3e, + 0x59, 0x31, 0x4f, 0x38, 0x4b, 0x3a, 0x48, 0x3b, 0x47, 0x3c, 0x46, 0x3c, + 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x4c, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x96, 0x05, 0x74, 0x0f, 0x63, 0x17, 0x5a, 0x1d, 0x54, 0x21, 0x51, 0x24, + 0x4e, 0x27, 0x4c, 0x29, 0x4a, 0x2b, 0x49, 0x2d, 0x49, 0x2e, 0x48, 0x2f, + 0x48, 0x31, 0x47, 0x31, 0x46, 0x32, 0x46, 0x33, 0x44, 0x33, 0x44, 0x34, + 0x43, 0x34, 0x43, 0x35, 0x09, 0x43, 0x11, 0x3e, 0x18, 0x3c, 0x1d, 0x3c, + 0x22, 0x3c, 0x25, 0x3c, 0x27, 0x3c, 0x2a, 0x3c, 0x2c, 0x3d, 0x2d, 0x3d, + 0x2f, 0x3d, 0x30, 0x3e, 0x31, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, + 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x5b, 0x2a, 0x53, 0x31, + 0x4f, 0x34, 0x4c, 0x36, 0x4b, 0x38, 0x4a, 0x39, 0x48, 0x39, 0x47, 0x3a, + 0x46, 0x3b, 0x46, 0x3b, 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, + 0x44, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, + 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x9e, 0x01, 0x81, 0x06, + 0x6f, 0x0d, 0x64, 0x12, 0x5d, 0x16, 0x58, 0x1a, 0x54, 0x1d, 0x53, 0x20, + 0x50, 0x22, 0x4e, 0x24, 0x4c, 0x26, 0x4b, 0x27, 0x4a, 0x29, 0x49, 0x2a, + 0x49, 0x2b, 0x49, 0x2c, 0x48, 0x2d, 0x48, 0x2e, 0x48, 0x2f, 0x47, 0x2f, + 0x09, 0x44, 0x0f, 0x3e, 0x14, 0x3c, 0x19, 0x3c, 0x1d, 0x3b, 0x20, 0x3b, + 0x23, 0x3b, 0x25, 0x3c, 0x27, 0x3c, 0x28, 0x3c, 0x2a, 0x3b, 0x2c, 0x3b, + 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, 0x3c, 0x2f, 0x3c, 0x30, 0x3d, 0x31, 0x3e, + 0x32, 0x3e, 0x32, 0x3e, 0x5c, 0x28, 0x56, 0x2d, 0x52, 0x31, 0x4f, 0x33, + 0x4e, 0x35, 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, 0x47, 0x39, + 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x3a, 0x46, 0x3a, 0x46, 0x3b, + 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, 0x00, + 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xa4, 0x00, 0x8a, 0x03, 0x78, 0x07, 0x6d, 0x0c, + 0x65, 0x10, 0x60, 0x13, 0x5b, 0x16, 0x57, 0x19, 0x55, 0x1b, 0x53, 0x1e, + 0x52, 0x1f, 0x4f, 0x22, 0x4d, 0x23, 0x4c, 0x24, 0x4b, 0x25, 0x4b, 0x27, + 0x4a, 0x28, 0x49, 0x29, 0x49, 0x29, 0x49, 0x2b, 0x09, 0x45, 0x0d, 0x40, + 0x11, 0x3d, 0x16, 0x3c, 0x19, 0x3b, 0x1d, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, + 0x23, 0x3b, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x29, 0x3c, 0x2a, 0x3b, + 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, + 0x5d, 0x27, 0x57, 0x2c, 0x54, 0x2f, 0x52, 0x31, 0x4f, 0x32, 0x4e, 0x34, + 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x48, 0x39, + 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x46, 0x39, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5b, 0x00, 0x55, 0x00, 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, 0x00, + 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xa7, 0x00, 0x91, 0x01, 0x80, 0x04, 0x75, 0x08, 0x6c, 0x0b, 0x65, 0x0e, + 0x61, 0x11, 0x5d, 0x14, 0x59, 0x16, 0x56, 0x19, 0x55, 0x1a, 0x54, 0x1c, + 0x53, 0x1e, 0x51, 0x1f, 0x4f, 0x21, 0x4d, 0x22, 0x4c, 0x23, 0x4b, 0x24, + 0x4b, 0x25, 0x4b, 0x26, 0x09, 0x45, 0x0d, 0x41, 0x10, 0x3e, 0x14, 0x3d, + 0x17, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, 0x3b, 0x20, 0x3b, 0x22, 0x3a, + 0x23, 0x3a, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x28, 0x3c, 0x29, 0x3c, + 0x2a, 0x3c, 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x5d, 0x26, 0x59, 0x2a, + 0x56, 0x2d, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, + 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, + 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, + 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa9, 0x00, 0x96, 0x00, + 0x86, 0x02, 0x7a, 0x05, 0x72, 0x08, 0x6b, 0x0b, 0x65, 0x0e, 0x62, 0x10, + 0x5f, 0x12, 0x5b, 0x14, 0x58, 0x16, 0x56, 0x18, 0x55, 0x1a, 0x54, 0x1b, + 0x53, 0x1d, 0x52, 0x1e, 0x50, 0x1f, 0x4f, 0x20, 0x4d, 0x21, 0x4c, 0x22, + 0x09, 0x46, 0x0c, 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, 0x22, 0x3a, + 0x24, 0x3a, 0x25, 0x3a, 0x26, 0x3b, 0x27, 0x3c, 0x28, 0x3c, 0x29, 0x3c, + 0x2a, 0x3c, 0x2a, 0x3c, 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2c, 0x53, 0x2e, + 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, + 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, + 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, + 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xab, 0x00, 0x9a, 0x00, 0x8b, 0x01, 0x7f, 0x03, + 0x77, 0x06, 0x71, 0x08, 0x6a, 0x0b, 0x66, 0x0d, 0x63, 0x0f, 0x60, 0x11, + 0x5d, 0x13, 0x59, 0x14, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x54, 0x1b, + 0x53, 0x1c, 0x53, 0x1d, 0x51, 0x1e, 0x50, 0x1f, 0x0a, 0x46, 0x0c, 0x42, + 0x0e, 0x3f, 0x11, 0x3f, 0x13, 0x3c, 0x16, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, + 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3a, 0x23, 0x3a, + 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3c, 0x27, 0x3c, 0x28, 0x3c, + 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x30, + 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, 0x4d, 0x35, 0x4b, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x38, + 0x4a, 0x39, 0x48, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, + 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, + 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xac, 0x00, 0x9d, 0x00, 0x8f, 0x01, 0x85, 0x02, 0x7b, 0x04, 0x75, 0x06, + 0x6f, 0x08, 0x69, 0x0b, 0x66, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5e, 0x12, + 0x5b, 0x13, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x55, 0x1a, + 0x54, 0x1b, 0x53, 0x1c, 0x0a, 0x47, 0x0b, 0x42, 0x0e, 0x40, 0x10, 0x3f, + 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, + 0x1d, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x23, 0x3a, + 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3b, 0x5e, 0x26, 0x5b, 0x28, + 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x35, 0x4b, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, + 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, + 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, + 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xad, 0x00, 0xa0, 0x00, + 0x92, 0x00, 0x89, 0x02, 0x7f, 0x03, 0x78, 0x05, 0x73, 0x07, 0x6e, 0x09, + 0x69, 0x0b, 0x66, 0x0c, 0x64, 0x0e, 0x62, 0x0f, 0x60, 0x11, 0x5c, 0x12, + 0x59, 0x14, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x18, 0x55, 0x1a, + 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x13, 0x3c, + 0x15, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x3a, + 0x1f, 0x3b, 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x24, 0x3a, + 0x24, 0x3a, 0x24, 0x39, 0x5e, 0x26, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2b, + 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x32, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, + 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x8b, 0x01, + 0x83, 0x02, 0x7b, 0x04, 0x76, 0x05, 0x73, 0x07, 0x6e, 0x09, 0x69, 0x0a, + 0x66, 0x0c, 0x64, 0x0d, 0x62, 0x0f, 0x61, 0x10, 0x5e, 0x11, 0x5b, 0x13, + 0x58, 0x14, 0x58, 0x15, 0x56, 0x16, 0x56, 0x18, 0x0a, 0x47, 0x0b, 0x43, + 0x0d, 0x42, 0x0f, 0x3f, 0x11, 0x3f, 0x12, 0x3e, 0x14, 0x3c, 0x16, 0x3c, + 0x17, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3b, + 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x21, 0x3a, 0x22, 0x3a, 0x24, 0x3a, + 0x5e, 0x26, 0x5c, 0x27, 0x59, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, + 0x00, 0x33, 0x00, 0x28, 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, + 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x87, 0x02, 0x7f, 0x03, + 0x79, 0x04, 0x75, 0x06, 0x71, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0c, + 0x64, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5f, 0x11, 0x5c, 0x12, 0x59, 0x13, + 0x58, 0x14, 0x58, 0x15, 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0e, 0x40, + 0x10, 0x3e, 0x11, 0x3f, 0x13, 0x3c, 0x15, 0x3b, 0x17, 0x3c, 0x17, 0x3c, + 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, + 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x5e, 0x25, 0x5c, 0x27, + 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, + 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, + 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xaf, 0x00, 0xa5, 0x00, + 0x9a, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x83, 0x02, 0x7c, 0x03, 0x77, 0x05, + 0x74, 0x06, 0x70, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x64, 0x0c, + 0x63, 0x0e, 0x61, 0x0f, 0x60, 0x11, 0x5d, 0x11, 0x5b, 0x13, 0x59, 0x13, + 0x0a, 0x47, 0x0a, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3e, 0x11, 0x3f, + 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, 0x1f, 0x3b, + 0x20, 0x3b, 0x21, 0x3b, 0x5e, 0x25, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, + 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, + 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, + 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, + 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, + 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, + 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x92, 0x00, + 0x8c, 0x01, 0x86, 0x02, 0x7e, 0x02, 0x7a, 0x04, 0x76, 0x05, 0x73, 0x06, + 0x70, 0x07, 0x6b, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0e, + 0x62, 0x0e, 0x61, 0x10, 0x5e, 0x11, 0x5c, 0x12, 0x0a, 0x47, 0x0a, 0x44, + 0x0c, 0x42, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x13, 0x3d, + 0x15, 0x3c, 0x16, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, + 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x39, 0x1f, 0x3b, 0x1f, 0x3b, 0x1f, 0x3b, + 0x5f, 0x25, 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, + 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, + 0x4e, 0x34, 0x4c, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, + 0x00, 0x41, 0x00, 0x39, 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, + 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, + 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, + 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xb0, 0x00, 0xa7, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8e, 0x00, 0x88, 0x01, + 0x82, 0x02, 0x7c, 0x03, 0x78, 0x05, 0x75, 0x06, 0x72, 0x07, 0x6f, 0x08, + 0x6a, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x62, 0x0e, + 0x61, 0x0f, 0x60, 0x11, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, + 0x0e, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3b, + 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, + 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x5f, 0x25, 0x5c, 0x27, + 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, + 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, + 0x00, 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, + 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa7, 0x00, + 0xa0, 0x00, 0x97, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x85, 0x02, 0x7e, 0x02, + 0x7a, 0x03, 0x77, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6e, 0x08, 0x6a, 0x09, + 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x63, 0x0e, 0x61, 0x0f, + 0x0a, 0x47, 0x0a, 0x45, 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3e, + 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x15, 0x3a, 0x17, 0x3c, + 0x17, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, + 0x1d, 0x39, 0x1f, 0x39, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x17, 0x00, 0x2f, + 0x00, 0x4c, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, + 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, + 0x00, 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x14, 0x01, 0x29, + 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x5f, 0x00, 0x5f, 0x31, 0x11, 0x03, 0x23, 0x00, 0x46, 0x00, 0x52, + 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, + 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x99, 0x00, + 0x91, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7c, 0x03, 0x79, 0x04, + 0x76, 0x05, 0x74, 0x06, 0x71, 0x07, 0x6d, 0x08, 0x6a, 0x09, 0x68, 0x0a, + 0x66, 0x0b, 0x65, 0x0c, 0x64, 0x0c, 0x63, 0x0e, 0x0a, 0x47, 0x0a, 0x45, + 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3e, 0x11, 0x3f, + 0x13, 0x3e, 0x13, 0x3c, 0x15, 0x3c, 0x16, 0x3a, 0x17, 0x3c, 0x17, 0x3c, + 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, + 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, + 0x00, 0x4f, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, + 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, + 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x16, 0x04, 0x00, 0x27, 0x00, 0x41, + 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, + 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, + 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, + 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, + 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0xb1, 0x00, 0xa9, 0x00, 0xa2, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x8e, 0x00, + 0x89, 0x01, 0x84, 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x05, 0x75, 0x05, + 0x73, 0x06, 0x71, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, + 0x65, 0x0c, 0x64, 0x0c, 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, + 0x0e, 0x42, 0x0f, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, + 0x15, 0x3c, 0x15, 0x3b, 0x17, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, + 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x5f, 0x25, 0x5c, 0x27, + 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, + 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, + 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, + 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5f, 0x00, 0x48, + 0x00, 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, + 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, + 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x82, 0x00, 0x57, 0x00, 0x12, 0x01, 0x00, 0x17, 0x00, 0x2f, 0x00, 0x3e, + 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x85, 0x00, 0x5f, 0x00, + 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, + 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, + 0x00, 0x59, 0x00, 0x5a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xa9, 0x00, + 0xa3, 0x00, 0x9d, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x01, 0x87, 0x02, + 0x81, 0x02, 0x7c, 0x03, 0x79, 0x03, 0x77, 0x05, 0x75, 0x06, 0x72, 0x06, + 0x70, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, + 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, + 0x0f, 0x3e, 0x11, 0x3e, 0x11, 0x3f, 0x13, 0x3f, 0x13, 0x3c, 0x15, 0x3c, + 0x15, 0x3a, 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, + 0x1a, 0x3b, 0x1c, 0x3b, 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x28, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x50, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, + 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, + 0x00, 0x56, 0x00, 0x57, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, + 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, + 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x78, 0x00, + 0x43, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x23, 0x00, 0x31, 0x00, 0x3b, + 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, + 0x00, 0x55, 0x00, 0x56, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x00, + 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, + 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, + 0x96, 0x00, 0x90, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x84, 0x02, 0x7e, 0x02, + 0x7b, 0x03, 0x78, 0x04, 0x76, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6f, 0x07, + 0x6b, 0x09, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x0a, 0x47, 0x0a, 0x46, + 0x0b, 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3f, 0x10, 0x3d, + 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3c, 0x16, 0x3a, + 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, + 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, + 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, + 0x00, 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, + 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, + 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x87, 0x00, 0x61, 0x00, 0x36, 0x00, + 0x10, 0x00, 0x02, 0x0a, 0x00, 0x1a, 0x00, 0x27, 0x00, 0x32, 0x00, 0x3a, + 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, 0x4e, 0x00, 0x50, + 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x00, + 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, + 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x28, 0x3c, 0x16, 0x41, + 0x11, 0x43, 0x0f, 0x44, 0x0e, 0x45, 0x0d, 0x46, 0x0c, 0x46, 0x0c, 0x46, + 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x6e, 0x1c, 0x3a, 0x29, 0x27, 0x31, 0x1e, 0x36, 0x19, 0x3a, 0x17, 0x3b, + 0x14, 0x3d, 0x13, 0x3f, 0x11, 0x40, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, + 0x0f, 0x43, 0x0f, 0x43, 0x0e, 0x43, 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, + 0x0c, 0x44, 0x0c, 0x44, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x3c, 0x32, 0x22, 0x38, + 0x18, 0x3c, 0x14, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x43, + 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x44, 0x0c, 0x45, 0x0c, 0x45, + 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, 0x0b, 0x45, 0x0b, 0x46, 0x0b, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x1e, 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, + 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x57, + 0x00, 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, + 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, + 0x00, 0x49, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, 0x00, 0x2e, 0x00, 0x10, 0x00, + 0x04, 0x08, 0x00, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x38, + 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x9a, 0x00, 0x91, 0x00, + 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x00, 0x00, 0x08, + 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, + 0x00, 0x42, 0x00, 0x45, 0x30, 0x38, 0x20, 0x3a, 0x18, 0x3d, 0x14, 0x3e, + 0x12, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0x0d, 0x43, + 0x0d, 0x43, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, + 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x47, 0x8a, 0x03, 0x59, 0x0f, + 0x40, 0x18, 0x32, 0x1f, 0x2a, 0x24, 0x24, 0x28, 0x20, 0x2c, 0x1c, 0x2f, + 0x1b, 0x31, 0x18, 0x33, 0x17, 0x34, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, + 0x13, 0x39, 0x12, 0x3a, 0x12, 0x3a, 0x12, 0x3b, 0x11, 0x3c, 0x10, 0x3c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x4a, 0x25, 0x31, 0x2b, 0x25, 0x30, 0x1e, 0x33, + 0x1a, 0x36, 0x17, 0x38, 0x15, 0x39, 0x13, 0x3b, 0x12, 0x3c, 0x11, 0x3d, + 0x10, 0x3e, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x40, + 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, + 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, + 0x00, 0x41, 0x00, 0x44, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, + 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, + 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x94, 0x00, + 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, 0x00, 0x10, 0x00, 0x06, 0x07, + 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x38, + 0x00, 0x3c, 0x00, 0x40, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, + 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x10, + 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, + 0x34, 0x38, 0x25, 0x39, 0x1e, 0x3b, 0x19, 0x3c, 0x16, 0x3d, 0x14, 0x3d, + 0x13, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, 0x0e, 0x42, + 0x0e, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x43, 0x0d, 0x43, + 0x0d, 0x44, 0x0d, 0x44, 0x97, 0x00, 0x6c, 0x06, 0x52, 0x0d, 0x43, 0x12, + 0x38, 0x18, 0x30, 0x1c, 0x2a, 0x20, 0x27, 0x23, 0x23, 0x26, 0x20, 0x28, + 0x1e, 0x2a, 0x1c, 0x2c, 0x1b, 0x2e, 0x19, 0x2f, 0x18, 0x31, 0x17, 0x32, + 0x16, 0x33, 0x16, 0x34, 0x16, 0x35, 0x15, 0x35, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x50, 0x24, 0x3b, 0x27, 0x2e, 0x2a, 0x26, 0x2d, 0x21, 0x2f, 0x1d, 0x32, + 0x1a, 0x33, 0x18, 0x35, 0x16, 0x36, 0x15, 0x38, 0x14, 0x39, 0x13, 0x3a, + 0x12, 0x3a, 0x11, 0x3b, 0x11, 0x3c, 0x11, 0x3c, 0x10, 0x3d, 0x10, 0x3e, + 0x10, 0x3e, 0x0f, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, + 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, + 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, + 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x87, 0x00, 0x71, 0x00, + 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, 0x00, 0x07, 0x06, 0x00, 0x0b, + 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, + 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, + 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, + 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x36, 0x39, 0x2a, 0x39, + 0x22, 0x39, 0x1d, 0x3a, 0x1a, 0x3b, 0x17, 0x3d, 0x15, 0x3d, 0x13, 0x3d, + 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, + 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0e, 0x41, 0x0e, 0x42, 0x0d, 0x42, + 0x9e, 0x00, 0x79, 0x02, 0x61, 0x07, 0x50, 0x0b, 0x44, 0x10, 0x3b, 0x14, + 0x34, 0x18, 0x2f, 0x1b, 0x2b, 0x1e, 0x28, 0x20, 0x25, 0x23, 0x23, 0x25, + 0x21, 0x27, 0x1f, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, 0x1b, 0x2c, 0x19, 0x2e, + 0x19, 0x2f, 0x19, 0x30, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x54, 0x23, 0x42, 0x25, + 0x35, 0x27, 0x2d, 0x29, 0x27, 0x2c, 0x22, 0x2e, 0x1f, 0x2f, 0x1c, 0x31, + 0x1a, 0x33, 0x19, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, + 0x13, 0x38, 0x13, 0x39, 0x12, 0x3a, 0x11, 0x3a, 0x11, 0x3b, 0x11, 0x3c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, + 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x5e, 0x00, 0x5b, + 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, + 0x00, 0x30, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, + 0x37, 0x00, 0x22, 0x00, 0x10, 0x00, 0x08, 0x05, 0x02, 0x0a, 0x00, 0x12, + 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x9d, 0x00, 0x99, 0x00, + 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, + 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, 0x19, + 0x00, 0x20, 0x00, 0x26, 0x38, 0x39, 0x2d, 0x39, 0x25, 0x39, 0x20, 0x3a, + 0x1d, 0x3b, 0x1a, 0x3b, 0x18, 0x3c, 0x16, 0x3d, 0x14, 0x3d, 0x13, 0x3d, + 0x13, 0x3d, 0x12, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, 0x0f, 0x41, + 0x0f, 0x42, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0xa2, 0x00, 0x84, 0x00, + 0x6c, 0x04, 0x5a, 0x07, 0x4e, 0x0b, 0x45, 0x0f, 0x3e, 0x12, 0x38, 0x15, + 0x32, 0x18, 0x2f, 0x1a, 0x2c, 0x1d, 0x29, 0x1f, 0x26, 0x21, 0x24, 0x22, + 0x22, 0x24, 0x21, 0x26, 0x1f, 0x27, 0x1e, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x56, 0x23, 0x47, 0x24, 0x3b, 0x25, 0x32, 0x27, + 0x2c, 0x29, 0x27, 0x2b, 0x24, 0x2d, 0x21, 0x2e, 0x1e, 0x30, 0x1c, 0x31, + 0x1b, 0x32, 0x19, 0x33, 0x18, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, + 0x14, 0x37, 0x14, 0x38, 0x13, 0x38, 0x13, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, + 0x00, 0x27, 0x00, 0x2c, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, + 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, + 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, 0x00, 0x46, 0x00, 0x32, 0x00, + 0x20, 0x00, 0x10, 0x00, 0x09, 0x04, 0x03, 0x09, 0x00, 0x0f, 0x00, 0x17, + 0x00, 0x1e, 0x00, 0x24, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, + 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, + 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, + 0x39, 0x3a, 0x2f, 0x39, 0x28, 0x39, 0x23, 0x39, 0x20, 0x3a, 0x1c, 0x3b, + 0x1a, 0x3a, 0x18, 0x3b, 0x17, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, + 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, + 0x0f, 0x40, 0x0f, 0x41, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x02, 0x63, 0x05, + 0x57, 0x07, 0x4d, 0x0b, 0x45, 0x0e, 0x3e, 0x11, 0x39, 0x13, 0x35, 0x16, + 0x32, 0x18, 0x2e, 0x1a, 0x2c, 0x1c, 0x29, 0x1d, 0x27, 0x1f, 0x26, 0x22, + 0x24, 0x22, 0x21, 0x23, 0x21, 0x25, 0x20, 0x26, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, 0x26, 0x30, 0x27, 0x2b, 0x29, + 0x27, 0x2a, 0x24, 0x2c, 0x22, 0x2d, 0x20, 0x2e, 0x1e, 0x30, 0x1c, 0x30, + 0x1b, 0x32, 0x1a, 0x32, 0x18, 0x33, 0x18, 0x34, 0x17, 0x34, 0x16, 0x35, + 0x16, 0x36, 0x15, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, + 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, + 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x86, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, + 0x10, 0x00, 0x09, 0x04, 0x04, 0x08, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1a, + 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, + 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, + 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x3a, 0x3a, 0x31, 0x3a, + 0x2a, 0x39, 0x25, 0x3a, 0x22, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1a, 0x3a, + 0x19, 0x3a, 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, + 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x10, 0x40, + 0xa8, 0x00, 0x90, 0x00, 0x7c, 0x01, 0x6b, 0x02, 0x5f, 0x05, 0x55, 0x07, + 0x4c, 0x0a, 0x45, 0x0d, 0x40, 0x0f, 0x3c, 0x12, 0x37, 0x14, 0x34, 0x16, + 0x31, 0x18, 0x2e, 0x19, 0x2b, 0x1b, 0x2a, 0x1d, 0x28, 0x1e, 0x26, 0x20, + 0x25, 0x22, 0x23, 0x22, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x59, 0x23, 0x4d, 0x23, + 0x43, 0x24, 0x3b, 0x25, 0x34, 0x26, 0x2f, 0x27, 0x2b, 0x29, 0x28, 0x2a, + 0x25, 0x2b, 0x23, 0x2d, 0x21, 0x2e, 0x1f, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x17, 0x34, 0x16, 0x34, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, + 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, + 0x00, 0x16, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, 0x00, 0x7d, 0x00, 0x6d, 0x00, + 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x10, 0x00, + 0x0a, 0x04, 0x05, 0x07, 0x00, 0x0b, 0x00, 0x11, 0x9e, 0x00, 0x9c, 0x00, + 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, + 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, + 0x01, 0x00, 0x00, 0x06, 0x3a, 0x3b, 0x32, 0x3a, 0x2c, 0x39, 0x27, 0x3a, + 0x23, 0x39, 0x21, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, + 0x17, 0x3b, 0x17, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, + 0x13, 0x3d, 0x13, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0xaa, 0x00, 0x94, 0x00, + 0x82, 0x00, 0x72, 0x01, 0x65, 0x04, 0x5b, 0x05, 0x53, 0x07, 0x4b, 0x0a, + 0x45, 0x0c, 0x41, 0x0f, 0x3d, 0x11, 0x39, 0x12, 0x36, 0x15, 0x33, 0x16, + 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1d, 0x29, 0x1d, 0x26, 0x1e, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5a, 0x23, 0x4f, 0x23, 0x46, 0x24, 0x3e, 0x24, + 0x37, 0x25, 0x32, 0x26, 0x2e, 0x27, 0x2b, 0x28, 0x28, 0x2a, 0x25, 0x2b, + 0x23, 0x2c, 0x21, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + 0x00, 0x0e, 0x00, 0x14, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, + 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, + 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, 0x00, 0x65, 0x00, 0x56, 0x00, + 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x0a, 0x03, + 0x06, 0x07, 0x01, 0x0a, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, + 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, + 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x00, + 0x3b, 0x3b, 0x33, 0x3b, 0x2d, 0x39, 0x29, 0x39, 0x25, 0x3a, 0x22, 0x39, + 0x20, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, 0x18, 0x3a, + 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x13, 0x3f, 0x13, 0x3d, + 0x13, 0x3d, 0x13, 0x3d, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x01, + 0x6b, 0x02, 0x61, 0x04, 0x58, 0x06, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, + 0x42, 0x0e, 0x3e, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x16, 0x32, 0x16, + 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1c, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, 0x24, 0x3a, 0x25, 0x35, 0x25, + 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2b, 0x24, 0x2b, + 0x22, 0x2d, 0x21, 0x2d, 0x1f, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x1b, 0x30, 0x1a, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, + 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, + 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x90, 0x00, + 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x42, 0x00, + 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x06, 0x06, + 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, + 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x3b, 0x3c, 0x34, 0x3a, + 0x2f, 0x3a, 0x2a, 0x39, 0x26, 0x3a, 0x23, 0x3a, 0x21, 0x39, 0x1f, 0x3a, + 0x1d, 0x3b, 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3b, + 0x17, 0x3c, 0x15, 0x3c, 0x15, 0x3d, 0x15, 0x3f, 0x13, 0x3f, 0x13, 0x3e, + 0xac, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x03, + 0x5e, 0x05, 0x57, 0x07, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, 0x42, 0x0d, + 0x3f, 0x0f, 0x3b, 0x11, 0x39, 0x12, 0x35, 0x14, 0x34, 0x16, 0x31, 0x16, + 0x30, 0x18, 0x2e, 0x19, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x52, 0x23, + 0x4a, 0x23, 0x43, 0x24, 0x3d, 0x24, 0x38, 0x25, 0x34, 0x26, 0x30, 0x27, + 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x24, 0x2b, 0x22, 0x2c, + 0x21, 0x2d, 0x20, 0x2d, 0x1f, 0x2e, 0x1d, 0x2e, 0x1d, 0x30, 0x1c, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x5f, 0x00, 0x5e, + 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, + 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3e, 0x00, 0x31, 0x00, + 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x9e, 0x00, 0x9d, 0x00, + 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, + 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, + 0x1f, 0x00, 0x17, 0x00, 0x3b, 0x3c, 0x35, 0x3a, 0x30, 0x3b, 0x2c, 0x39, + 0x28, 0x3a, 0x25, 0x3b, 0x22, 0x39, 0x21, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, + 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3c, + 0x16, 0x3c, 0x15, 0x3c, 0x15, 0x3e, 0x14, 0x3f, 0xad, 0x00, 0x9d, 0x00, + 0x8e, 0x00, 0x81, 0x00, 0x76, 0x01, 0x6b, 0x02, 0x63, 0x04, 0x5c, 0x05, + 0x56, 0x07, 0x50, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x40, 0x0f, + 0x3c, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x14, 0x33, 0x16, 0x30, 0x16, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x53, 0x23, 0x4c, 0x23, 0x45, 0x23, + 0x40, 0x24, 0x3b, 0x25, 0x36, 0x25, 0x33, 0x26, 0x30, 0x27, 0x2d, 0x28, + 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2d, + 0x20, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, + 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, + 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, + 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6b, 0x00, + 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x24, 0x00, + 0x19, 0x00, 0x0f, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, + 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, + 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x00, + 0x3c, 0x3d, 0x36, 0x3a, 0x31, 0x3b, 0x2c, 0x3a, 0x29, 0x39, 0x26, 0x3a, + 0x24, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3c, + 0x1a, 0x3c, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0x17, 0x3c, + 0x15, 0x3c, 0x15, 0x3c, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, + 0x79, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x60, 0x04, 0x59, 0x05, 0x54, 0x07, + 0x4f, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x41, 0x0f, 0x3d, 0x0f, + 0x3b, 0x11, 0x38, 0x12, 0x35, 0x12, 0x35, 0x15, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, 0x23, 0x42, 0x24, 0x3c, 0x24, + 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, 0x27, 0x2d, 0x28, 0x2a, 0x28, + 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x21, 0x2d, + 0x20, 0x2d, 0x20, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3d, 0x36, 0x3a, + 0x32, 0x3b, 0x2d, 0x3b, 0x2b, 0x39, 0x28, 0x3a, 0x25, 0x3a, 0x23, 0x3a, + 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, + 0x19, 0x3a, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3c, 0x16, 0x3c, + 0xad, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x01, + 0x6c, 0x02, 0x65, 0x03, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x07, 0x4f, 0x08, + 0x4b, 0x0a, 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0e, 0x3e, 0x0f, 0x3b, 0x0f, + 0x3a, 0x12, 0x37, 0x12, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x55, 0x23, + 0x4f, 0x23, 0x49, 0x23, 0x44, 0x24, 0x3f, 0x24, 0x3b, 0x25, 0x37, 0x25, + 0x34, 0x26, 0x31, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, + 0x27, 0x2a, 0x25, 0x2b, 0x24, 0x2b, 0x22, 0x2b, 0x22, 0x2d, 0x20, 0x2d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3b, 0x2f, 0x3b, + 0x2b, 0x39, 0x29, 0x39, 0x26, 0x3a, 0x24, 0x3b, 0x22, 0x3a, 0x21, 0x39, + 0x1f, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, + 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0xae, 0x00, 0xa2, 0x00, + 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, + 0x61, 0x04, 0x5c, 0x05, 0x57, 0x05, 0x52, 0x07, 0x4e, 0x08, 0x4b, 0x0a, + 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0d, 0x3f, 0x0f, 0x3c, 0x0f, 0x3b, 0x11, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x56, 0x23, 0x50, 0x23, 0x4a, 0x23, + 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x35, 0x25, 0x33, 0x26, + 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, + 0x25, 0x2a, 0x24, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3a, 0x2f, 0x3b, 0x2c, 0x3a, 0x2a, 0x39, + 0x27, 0x3a, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, + 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, + 0x19, 0x3a, 0x17, 0x3a, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, + 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, 0x6c, 0x02, 0x65, 0x02, 0x5f, 0x04, + 0x5a, 0x05, 0x56, 0x05, 0x52, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0b, + 0x45, 0x0c, 0x41, 0x0c, 0x40, 0x0f, 0x3d, 0x0f, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, 0x23, 0x47, 0x23, 0x42, 0x24, + 0x3e, 0x24, 0x3b, 0x25, 0x37, 0x25, 0x34, 0x25, 0x32, 0x26, 0x30, 0x26, + 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, + 0x25, 0x2b, 0x23, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x38, 0x3c, + 0x34, 0x3a, 0x30, 0x3b, 0x2d, 0x3b, 0x2a, 0x39, 0x28, 0x39, 0x26, 0x3a, + 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, 0x1e, 0x3b, + 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, 0x19, 0x3a, + 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, + 0x76, 0x00, 0x6f, 0x01, 0x68, 0x02, 0x62, 0x04, 0x5e, 0x04, 0x59, 0x05, + 0x55, 0x06, 0x51, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, + 0x41, 0x0c, 0x41, 0x0f, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x57, 0x23, + 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3c, 0x24, + 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, + 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, 0x25, 0x2b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x38, 0x3c, 0x34, 0x3a, 0x31, 0x3b, + 0x2e, 0x3c, 0x2b, 0x39, 0x29, 0x39, 0x27, 0x3a, 0x25, 0x3a, 0x24, 0x3b, + 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, + 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3a, 0xaf, 0x00, 0xa6, 0x00, + 0x9b, 0x00, 0x91, 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x01, + 0x6b, 0x02, 0x66, 0x02, 0x61, 0x04, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x07, + 0x50, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0c, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4d, 0x23, + 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, 0x24, 0x3b, 0x24, 0x38, 0x25, + 0x35, 0x25, 0x33, 0x26, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, + 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x3e, 0x38, 0x3d, 0x35, 0x3a, 0x32, 0x3b, 0x2e, 0x3b, 0x2c, 0x3a, + 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x39, + 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, + 0x1a, 0x3c, 0x19, 0x3c, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, + 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x02, + 0x63, 0x03, 0x5f, 0x04, 0x5b, 0x05, 0x56, 0x05, 0x54, 0x07, 0x4f, 0x07, + 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x46, 0x0c, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, 0x23, 0x4a, 0x23, 0x46, 0x23, + 0x43, 0x24, 0x3f, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, + 0x33, 0x26, 0x30, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, 0x2a, 0x28, + 0x28, 0x28, 0x28, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, + 0x35, 0x3a, 0x32, 0x3a, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, 0x39, 0x28, 0x39, + 0x27, 0x3a, 0x24, 0x3a, 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, + 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, + 0xb0, 0x00, 0xa7, 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, + 0x7e, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x04, + 0x5d, 0x04, 0x5a, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4f, 0x07, 0x4e, 0x09, + 0x4a, 0x0a, 0x47, 0x0a, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, + 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, + 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, 0x25, 0x33, 0x25, 0x32, 0x26, + 0x2f, 0x26, 0x2f, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, 0x35, 0x3b, 0x32, 0x3a, + 0x30, 0x3b, 0x2e, 0x3c, 0x2b, 0x3a, 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, + 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x39, + 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0xb1, 0x00, 0xa7, 0x00, + 0x9f, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x87, 0x00, 0x80, 0x00, 0x79, 0x00, + 0x73, 0x00, 0x6e, 0x01, 0x69, 0x02, 0x64, 0x02, 0x60, 0x04, 0x5c, 0x04, + 0x59, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4e, 0x07, 0x4e, 0x09, 0x4a, 0x0a, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, + 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, + 0x4c, 0x23, 0x48, 0x23, 0x45, 0x23, 0x42, 0x24, 0x3f, 0x24, 0x3c, 0x24, + 0x39, 0x25, 0x37, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, 0x26, + 0x2e, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x00, 0x94, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xa9, 0x00, + 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, + 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, + 0xb2, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x21, 0x00, 0x6e, 0x00, + 0x8a, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, + 0xaa, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, + 0xae, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, + 0x6c, 0x00, 0x92, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, + 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, + 0xb2, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, + 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x04, 0x70, 0x01, + 0x7f, 0x00, 0x88, 0x00, 0x90, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9c, 0x00, + 0x9f, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, + 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x1c, 0x3a, 0x03, 0x59, 0x00, 0x6c, 0x00, + 0x79, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, + 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, + 0xa5, 0x00, 0xa6, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x60, 0x0e, 0x79, 0x01, + 0x88, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, + 0xa5, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, + 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4e, 0x0f, 0x60, 0x07, 0x6d, 0x03, 0x77, 0x01, + 0x7f, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, + 0x97, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, 0x00, 0xa1, 0x00, + 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x29, 0x27, 0x0f, 0x40, 0x06, 0x52, 0x02, 0x61, 0x00, 0x6c, 0x00, + 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, + 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, + 0x9d, 0x00, 0x9e, 0x00, 0x60, 0x14, 0x6f, 0x07, 0x7b, 0x03, 0x84, 0x01, + 0x8c, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, + 0xa1, 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, + 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x17, 0x56, 0x0d, 0x62, 0x07, 0x6b, 0x04, 0x73, 0x02, 0x79, 0x01, + 0x7e, 0x01, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, + 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, + 0x9e, 0x00, 0x9f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x31, 0x1e, 0x18, + 0x32, 0x0d, 0x43, 0x07, 0x50, 0x04, 0x5a, 0x02, 0x63, 0x01, 0x6b, 0x00, + 0x72, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, + 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, + 0x60, 0x18, 0x6b, 0x0c, 0x74, 0x06, 0x7d, 0x03, 0x83, 0x02, 0x89, 0x01, + 0x8d, 0x00, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, + 0x9e, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, + 0xa5, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x1d, 0x51, 0x12, + 0x5b, 0x0c, 0x63, 0x08, 0x6a, 0x05, 0x70, 0x03, 0x77, 0x02, 0x7b, 0x02, + 0x7e, 0x01, 0x82, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8c, 0x00, 0x8e, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x97, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x36, 0x19, 0x1f, 0x2a, 0x12, 0x38, 0x0b, + 0x44, 0x07, 0x4e, 0x05, 0x57, 0x02, 0x5f, 0x01, 0x65, 0x01, 0x6b, 0x00, + 0x71, 0x00, 0x76, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, + 0x87, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x60, 0x1b, 0x68, 0x0f, + 0x70, 0x09, 0x77, 0x05, 0x7d, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8b, 0x00, + 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, + 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x45, 0x21, 0x4e, 0x16, 0x56, 0x10, 0x5d, 0x0b, + 0x65, 0x08, 0x69, 0x06, 0x6f, 0x04, 0x74, 0x03, 0x78, 0x02, 0x7b, 0x02, + 0x7e, 0x01, 0x81, 0x01, 0x85, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, 0x00, + 0x8e, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3a, 0x17, 0x24, 0x24, 0x18, 0x30, 0x10, 0x3b, 0x0b, 0x45, 0x07, + 0x4d, 0x05, 0x55, 0x04, 0x5b, 0x02, 0x61, 0x01, 0x67, 0x01, 0x6b, 0x00, + 0x6f, 0x00, 0x74, 0x00, 0x77, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, + 0x83, 0x00, 0x86, 0x00, 0x60, 0x1d, 0x67, 0x12, 0x6d, 0x0c, 0x73, 0x08, + 0x79, 0x05, 0x7e, 0x03, 0x82, 0x02, 0x86, 0x02, 0x89, 0x01, 0x8c, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, + 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x44, 0x24, 0x4c, 0x1a, 0x53, 0x13, 0x59, 0x0e, 0x5f, 0x0b, 0x65, 0x08, + 0x69, 0x06, 0x6d, 0x05, 0x73, 0x04, 0x76, 0x03, 0x79, 0x02, 0x7c, 0x02, + 0x7e, 0x01, 0x81, 0x01, 0x84, 0x00, 0x87, 0x00, 0x89, 0x00, 0x8b, 0x00, + 0x8c, 0x00, 0x8d, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3b, 0x14, 0x28, + 0x20, 0x1c, 0x2a, 0x14, 0x34, 0x0f, 0x3e, 0x0b, 0x45, 0x07, 0x4c, 0x05, + 0x53, 0x04, 0x58, 0x03, 0x5e, 0x02, 0x63, 0x01, 0x67, 0x01, 0x6c, 0x00, + 0x6f, 0x00, 0x72, 0x00, 0x76, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, + 0x60, 0x1d, 0x66, 0x14, 0x6b, 0x0e, 0x71, 0x0a, 0x76, 0x07, 0x7a, 0x05, + 0x7e, 0x03, 0x81, 0x02, 0x85, 0x02, 0x88, 0x01, 0x8b, 0x01, 0x8d, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, + 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x27, 0x4a, 0x1d, + 0x4f, 0x16, 0x57, 0x11, 0x5b, 0x0e, 0x61, 0x0b, 0x66, 0x08, 0x69, 0x07, + 0x6d, 0x05, 0x71, 0x04, 0x75, 0x03, 0x77, 0x02, 0x7a, 0x02, 0x7c, 0x02, + 0x7e, 0x01, 0x80, 0x01, 0x83, 0x01, 0x86, 0x00, 0x88, 0x00, 0x89, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3d, 0x13, 0x2c, 0x1c, 0x20, 0x27, 0x18, + 0x2f, 0x12, 0x38, 0x0e, 0x3e, 0x0a, 0x45, 0x07, 0x4b, 0x06, 0x51, 0x05, + 0x57, 0x04, 0x5c, 0x02, 0x60, 0x02, 0x65, 0x01, 0x67, 0x01, 0x6c, 0x00, + 0x6f, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, 0x60, 0x1e, 0x65, 0x16, + 0x6a, 0x10, 0x6f, 0x0c, 0x73, 0x09, 0x77, 0x07, 0x7b, 0x05, 0x7e, 0x03, + 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8e, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, + 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, 0x00, + 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x2c, 0x14, 0x57, 0x00, + 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x8c, 0x00, 0x63, 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, + 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x31, 0x11, 0x5f, 0x00, + 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x43, 0x29, 0x48, 0x20, 0x4d, 0x19, 0x54, 0x14, + 0x58, 0x10, 0x5c, 0x0d, 0x62, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6c, 0x06, + 0x70, 0x05, 0x74, 0x04, 0x76, 0x03, 0x78, 0x02, 0x7a, 0x02, 0x7c, 0x02, + 0x7e, 0x02, 0x80, 0x01, 0x82, 0x01, 0x85, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x3f, 0x11, 0x2f, 0x1b, 0x23, 0x23, 0x1b, 0x2b, 0x15, 0x32, 0x11, + 0x39, 0x0d, 0x40, 0x0a, 0x45, 0x08, 0x4b, 0x07, 0x51, 0x05, 0x56, 0x04, + 0x59, 0x03, 0x5e, 0x02, 0x61, 0x02, 0x65, 0x01, 0x68, 0x01, 0x6b, 0x00, + 0x6e, 0x00, 0x71, 0x00, 0x60, 0x1f, 0x64, 0x17, 0x69, 0x11, 0x6d, 0x0d, + 0x71, 0x0a, 0x75, 0x08, 0x78, 0x06, 0x7b, 0x05, 0x7e, 0x04, 0x81, 0x03, + 0x84, 0x02, 0x86, 0x02, 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8e, 0x00, + 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x01, 0x29, 0x16, 0x04, 0x57, 0x00, 0x78, 0x00, + 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, + 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x4d, 0x00, + 0x60, 0x00, 0x77, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, + 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, + 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, + 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x43, 0x2b, 0x47, 0x22, 0x4c, 0x1b, 0x51, 0x16, 0x56, 0x12, 0x59, 0x0f, + 0x5e, 0x0d, 0x63, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6b, 0x06, 0x6f, 0x05, + 0x73, 0x05, 0x75, 0x03, 0x77, 0x03, 0x79, 0x02, 0x7a, 0x02, 0x7c, 0x02, + 0x7e, 0x02, 0x7f, 0x01, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x11, 0x31, + 0x18, 0x26, 0x20, 0x1e, 0x28, 0x18, 0x2f, 0x13, 0x35, 0x0f, 0x3c, 0x0c, + 0x41, 0x0a, 0x46, 0x08, 0x4b, 0x07, 0x50, 0x05, 0x54, 0x04, 0x58, 0x04, + 0x5c, 0x02, 0x5f, 0x02, 0x62, 0x02, 0x66, 0x01, 0x69, 0x01, 0x6b, 0x00, + 0x60, 0x20, 0x64, 0x18, 0x68, 0x13, 0x6b, 0x0f, 0x6f, 0x0c, 0x73, 0x09, + 0x76, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, + 0x85, 0x02, 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x00, + 0x90, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, 0x00, + 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, + 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, + 0x00, 0x49, 0x00, 0x27, 0x12, 0x01, 0x43, 0x00, 0x61, 0x00, 0x74, 0x00, + 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, + 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, + 0x60, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, + 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, + 0x00, 0x46, 0x00, 0x1f, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, + 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, + 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x43, 0x2d, 0x46, 0x24, + 0x4b, 0x1e, 0x4f, 0x19, 0x55, 0x14, 0x58, 0x11, 0x5b, 0x0e, 0x5f, 0x0c, + 0x64, 0x0a, 0x66, 0x09, 0x68, 0x07, 0x6a, 0x06, 0x6e, 0x06, 0x72, 0x05, + 0x74, 0x04, 0x76, 0x03, 0x78, 0x03, 0x79, 0x02, 0x7b, 0x02, 0x7c, 0x02, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x10, 0x33, 0x17, 0x28, 0x1e, 0x20, + 0x25, 0x1a, 0x2c, 0x16, 0x32, 0x12, 0x37, 0x0f, 0x3d, 0x0c, 0x42, 0x0a, + 0x46, 0x08, 0x4b, 0x07, 0x4f, 0x05, 0x53, 0x05, 0x57, 0x04, 0x5a, 0x04, + 0x5e, 0x02, 0x61, 0x02, 0x63, 0x01, 0x66, 0x01, 0x60, 0x20, 0x63, 0x19, + 0x67, 0x14, 0x6a, 0x10, 0x6e, 0x0d, 0x71, 0x0b, 0x74, 0x09, 0x77, 0x07, + 0x7a, 0x06, 0x7c, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, + 0x87, 0x02, 0x88, 0x02, 0x8a, 0x01, 0x8c, 0x01, 0x8d, 0x00, 0x8e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, 0x00, + 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, + 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x54, 0x00, 0x41, + 0x00, 0x17, 0x10, 0x00, 0x36, 0x00, 0x51, 0x00, 0x64, 0x00, 0x71, 0x00, + 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8d, 0x00, 0x90, 0x00, + 0x92, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x89, 0x00, 0x77, 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, + 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, + 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x52, 0x00, 0x3c, + 0x00, 0x0d, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, + 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, + 0x93, 0x00, 0x95, 0x00, 0x42, 0x2e, 0x45, 0x26, 0x4b, 0x1f, 0x4d, 0x1a, + 0x52, 0x16, 0x56, 0x13, 0x59, 0x10, 0x5c, 0x0e, 0x61, 0x0c, 0x64, 0x0a, + 0x66, 0x09, 0x68, 0x07, 0x6a, 0x07, 0x6d, 0x06, 0x71, 0x05, 0x74, 0x05, + 0x75, 0x03, 0x77, 0x03, 0x78, 0x02, 0x7a, 0x02, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x09, 0x41, 0x0f, 0x34, 0x16, 0x2a, 0x1c, 0x23, 0x23, 0x1d, 0x29, 0x18, + 0x2e, 0x14, 0x34, 0x11, 0x39, 0x0e, 0x3e, 0x0c, 0x42, 0x0a, 0x47, 0x08, + 0x4b, 0x07, 0x4f, 0x05, 0x52, 0x05, 0x56, 0x04, 0x59, 0x04, 0x5c, 0x03, + 0x5f, 0x02, 0x62, 0x02, 0x60, 0x20, 0x63, 0x1a, 0x66, 0x15, 0x6a, 0x11, + 0x6d, 0x0e, 0x70, 0x0c, 0x73, 0x0a, 0x75, 0x08, 0x78, 0x07, 0x7a, 0x06, + 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, 0x86, 0x02, + 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, + 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, + 0x86, 0x00, 0x89, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x0e, + 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, 0x00, 0x64, 0x00, 0x6f, 0x00, + 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x83, 0x00, + 0x60, 0x00, 0x30, 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, + 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, + 0x86, 0x00, 0x89, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, + 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, + 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, + 0x42, 0x2f, 0x44, 0x27, 0x4a, 0x22, 0x4c, 0x1c, 0x50, 0x18, 0x55, 0x14, + 0x57, 0x12, 0x5a, 0x0f, 0x5d, 0x0d, 0x62, 0x0c, 0x64, 0x0a, 0x66, 0x09, + 0x68, 0x08, 0x6a, 0x07, 0x6c, 0x06, 0x70, 0x05, 0x73, 0x05, 0x75, 0x04, + 0x76, 0x03, 0x78, 0x03, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x42, 0x0f, 0x36, + 0x15, 0x2c, 0x1b, 0x25, 0x21, 0x1f, 0x26, 0x1a, 0x2c, 0x16, 0x31, 0x12, + 0x36, 0x0f, 0x3a, 0x0d, 0x3f, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, + 0x4e, 0x05, 0x52, 0x05, 0x55, 0x04, 0x58, 0x04, 0x5b, 0x04, 0x5d, 0x02, + 0x60, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x69, 0x12, 0x6c, 0x0f, 0x6e, 0x0d, + 0x71, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, + 0x7f, 0x04, 0x81, 0x03, 0x82, 0x02, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, + 0x89, 0x02, 0x8a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, + 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, + 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x02, 0x0a, 0x10, 0x00, + 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x65, 0x00, 0x6d, 0x00, + 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, + 0x22, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, + 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, + 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, + 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, + 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x00, 0x42, 0x31, 0x44, 0x29, + 0x49, 0x23, 0x4c, 0x1e, 0x4e, 0x1a, 0x53, 0x16, 0x56, 0x13, 0x58, 0x11, + 0x5a, 0x0f, 0x5f, 0x0d, 0x63, 0x0b, 0x64, 0x0a, 0x66, 0x09, 0x68, 0x08, + 0x6a, 0x07, 0x6b, 0x06, 0x6f, 0x06, 0x72, 0x05, 0x74, 0x05, 0x75, 0x03, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x43, 0x0f, 0x37, 0x14, 0x2e, 0x19, 0x27, + 0x1f, 0x21, 0x24, 0x1c, 0x29, 0x18, 0x2e, 0x15, 0x33, 0x12, 0x37, 0x0f, + 0x3b, 0x0c, 0x40, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, 0x4e, 0x06, + 0x51, 0x05, 0x55, 0x05, 0x56, 0x04, 0x5a, 0x04, 0x60, 0x21, 0x63, 0x1b, + 0x65, 0x17, 0x68, 0x13, 0x6b, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0a, + 0x75, 0x09, 0x77, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x04, + 0x81, 0x03, 0x82, 0x03, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x56, + 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x04, 0x08, 0x10, 0x00, 0x25, 0x00, + 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x6c, 0x00, + 0x72, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, + 0x01, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x55, + 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, + 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, + 0x77, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x44, 0x2a, 0x48, 0x24, 0x4b, 0x1f, + 0x4d, 0x1b, 0x51, 0x18, 0x55, 0x15, 0x57, 0x12, 0x59, 0x10, 0x5b, 0x0e, + 0x5f, 0x0c, 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x6a, 0x07, + 0x6b, 0x06, 0x6e, 0x06, 0x71, 0x05, 0x73, 0x05, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x43, 0x0e, 0x38, 0x13, 0x2f, 0x18, 0x28, 0x1d, 0x22, 0x22, 0x1d, + 0x27, 0x19, 0x2b, 0x16, 0x30, 0x12, 0x35, 0x11, 0x39, 0x0f, 0x3c, 0x0c, + 0x41, 0x0b, 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x50, 0x05, + 0x54, 0x05, 0x55, 0x04, 0x60, 0x21, 0x63, 0x1c, 0x65, 0x17, 0x68, 0x14, + 0x6a, 0x11, 0x6c, 0x0e, 0x6f, 0x0c, 0x71, 0x0b, 0x73, 0x09, 0x76, 0x08, + 0x78, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, + 0x82, 0x03, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x3b, + 0x00, 0x27, 0x00, 0x13, 0x06, 0x07, 0x10, 0x00, 0x22, 0x00, 0x32, 0x00, + 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, + 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, + 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, + 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, + 0x00, 0x1f, 0x00, 0x08, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, + 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, + 0x42, 0x32, 0x44, 0x2b, 0x47, 0x25, 0x4b, 0x21, 0x4c, 0x1d, 0x4f, 0x19, + 0x54, 0x16, 0x56, 0x14, 0x58, 0x11, 0x5a, 0x10, 0x5d, 0x0e, 0x61, 0x0c, + 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, + 0x6d, 0x06, 0x71, 0x05, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x43, 0x0e, 0x39, + 0x12, 0x31, 0x17, 0x2a, 0x1c, 0x24, 0x21, 0x1f, 0x26, 0x1b, 0x2a, 0x18, + 0x2e, 0x16, 0x32, 0x12, 0x35, 0x0f, 0x3a, 0x0f, 0x3d, 0x0c, 0x41, 0x0b, + 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x53, 0x05, + 0x60, 0x21, 0x62, 0x1c, 0x65, 0x18, 0x67, 0x15, 0x6a, 0x12, 0x6c, 0x0f, + 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x07, + 0x7a, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x82, 0x03, + 0x83, 0x02, 0x85, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, + 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, 0x32, 0x00, 0x20, + 0x00, 0x0e, 0x07, 0x06, 0x10, 0x00, 0x20, 0x00, 0x2f, 0x00, 0x3b, 0x00, + 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, + 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, + 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, + 0x00, 0x03, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, + 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x00, 0x42, 0x33, 0x43, 0x2c, + 0x46, 0x27, 0x4a, 0x22, 0x4c, 0x1e, 0x4d, 0x1b, 0x52, 0x18, 0x55, 0x15, + 0x57, 0x13, 0x58, 0x11, 0x5a, 0x0f, 0x5e, 0x0e, 0x62, 0x0c, 0x63, 0x0b, + 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, 0x6c, 0x06, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x32, 0x16, 0x2b, + 0x1b, 0x26, 0x1f, 0x22, 0x24, 0x1d, 0x28, 0x19, 0x2b, 0x16, 0x30, 0x14, + 0x34, 0x12, 0x37, 0x0f, 0x3b, 0x0e, 0x3e, 0x0c, 0x41, 0x0b, 0x45, 0x0a, + 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x60, 0x22, 0x62, 0x1d, + 0x65, 0x19, 0x66, 0x15, 0x69, 0x13, 0x6b, 0x11, 0x6d, 0x0e, 0x6f, 0x0c, + 0x71, 0x0b, 0x73, 0x0a, 0x76, 0x09, 0x77, 0x07, 0x79, 0x07, 0x7a, 0x06, + 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x83, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, + 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, 0x1a, 0x00, 0x0b, + 0x08, 0x05, 0x10, 0x00, 0x1e, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x42, 0x00, + 0x4b, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, + 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, + 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, + 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, + 0x54, 0x00, 0x5b, 0x00, 0x42, 0x33, 0x43, 0x2d, 0x45, 0x28, 0x49, 0x23, + 0x4b, 0x1f, 0x4d, 0x1c, 0x50, 0x19, 0x55, 0x16, 0x56, 0x14, 0x58, 0x12, + 0x59, 0x11, 0x5b, 0x0e, 0x5f, 0x0d, 0x62, 0x0c, 0x63, 0x0b, 0x65, 0x0a, + 0x66, 0x09, 0x68, 0x09, 0x69, 0x07, 0x6b, 0x07, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x19, 0x27, 0x1e, 0x22, + 0x21, 0x1e, 0x26, 0x1a, 0x2b, 0x18, 0x2e, 0x16, 0x31, 0x12, 0x35, 0x11, + 0x38, 0x0f, 0x3b, 0x0d, 0x3f, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, + 0x4a, 0x07, 0x4e, 0x07, 0x60, 0x22, 0x62, 0x1d, 0x64, 0x19, 0x66, 0x16, + 0x68, 0x13, 0x6a, 0x11, 0x6c, 0x0f, 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, + 0x74, 0x09, 0x76, 0x08, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, + 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, + 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x02, 0x0a, 0x09, 0x04, + 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, 0x00, 0x3e, 0x00, 0x46, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, + 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, + 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, + 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, + 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x49, 0x24, 0x4b, 0x20, 0x4c, 0x1d, + 0x4e, 0x1a, 0x53, 0x18, 0x55, 0x15, 0x56, 0x13, 0x58, 0x11, 0x5a, 0x10, + 0x5c, 0x0e, 0x60, 0x0d, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, + 0x68, 0x09, 0x69, 0x07, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3b, + 0x11, 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1d, 0x23, 0x21, 0x20, 0x25, 0x1d, + 0x29, 0x19, 0x2b, 0x16, 0x30, 0x14, 0x33, 0x12, 0x35, 0x0f, 0x3a, 0x0f, + 0x3c, 0x0c, 0x40, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, 0x4a, 0x07, + 0x60, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x14, 0x6a, 0x11, + 0x6c, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x71, 0x0b, 0x73, 0x0a, 0x75, 0x09, + 0x76, 0x07, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, 0x7e, 0x05, + 0x7f, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, + 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x03, 0x09, 0x09, 0x04, 0x10, 0x00, + 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, + 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, + 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, + 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, + 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x00, 0x42, 0x34, 0x43, 0x2f, + 0x44, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4c, 0x1e, 0x4d, 0x1b, 0x51, 0x18, + 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x59, 0x11, 0x5a, 0x0f, 0x5d, 0x0e, + 0x61, 0x0c, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x09, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, + 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x2f, + 0x19, 0x2a, 0x1c, 0x25, 0x20, 0x22, 0x23, 0x1d, 0x26, 0x1a, 0x2b, 0x18, + 0x2e, 0x16, 0x30, 0x12, 0x35, 0x12, 0x37, 0x0f, 0x3b, 0x0f, 0x3d, 0x0c, + 0x41, 0x0c, 0x42, 0x0a, 0x46, 0x0a, 0x47, 0x09, 0x60, 0x22, 0x62, 0x1e, + 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6b, 0x11, 0x6d, 0x0e, + 0x6e, 0x0d, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x09, 0x76, 0x09, 0x77, 0x07, + 0x79, 0x07, 0x7a, 0x06, 0x7c, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x26, + 0x00, 0x1a, 0x00, 0x0f, 0x04, 0x08, 0x0a, 0x04, 0x10, 0x00, 0x1b, 0x00, + 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, + 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, + 0x00, 0x10, 0x00, 0x03, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, + 0x33, 0x00, 0x3b, 0x00, 0x4f, 0x2a, 0x56, 0x28, 0x59, 0x27, 0x5a, 0x26, + 0x5c, 0x26, 0x5c, 0x26, 0x5c, 0x26, 0x5d, 0x26, 0x5d, 0x26, 0x5d, 0x25, + 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5e, 0x25, 0x5e, 0x25, + 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x92, 0x0e, 0x79, 0x14, + 0x6f, 0x18, 0x6b, 0x1b, 0x68, 0x1d, 0x67, 0x1d, 0x66, 0x1e, 0x65, 0x1f, + 0x64, 0x20, 0x64, 0x20, 0x63, 0x20, 0x63, 0x21, 0x63, 0x21, 0x63, 0x21, + 0x63, 0x21, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, + 0x16, 0x23, 0x3c, 0x23, 0x4a, 0x23, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, + 0x58, 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, + 0x5b, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, + 0x5d, 0x23, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, + 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x22, 0x00, 0x17, + 0x00, 0x0c, 0x05, 0x07, 0x0a, 0x03, 0x0f, 0x00, 0x1a, 0x00, 0x24, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, + 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, + 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, + 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, + 0x48, 0x31, 0x4f, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, 0x58, 0x29, + 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0xa1, 0x01, 0x88, 0x07, 0x7b, 0x0c, 0x74, 0x0f, + 0x70, 0x12, 0x6d, 0x14, 0x6b, 0x16, 0x6a, 0x17, 0x69, 0x18, 0x68, 0x19, + 0x67, 0x1a, 0x66, 0x1b, 0x66, 0x1b, 0x65, 0x1c, 0x65, 0x1c, 0x65, 0x1d, + 0x65, 0x1d, 0x64, 0x1d, 0x64, 0x1e, 0x64, 0x1e, 0x0a, 0x32, 0x22, 0x25, + 0x31, 0x24, 0x3b, 0x23, 0x42, 0x23, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, + 0x4f, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, + 0x56, 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, + 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x0b, + 0x06, 0x07, 0x0b, 0x03, 0x0f, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, + 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, + 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, + 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, + 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x00, 0x45, 0x34, 0x4b, 0x31, + 0x4f, 0x2f, 0x52, 0x2d, 0x54, 0x2c, 0x55, 0x2b, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, + 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, + 0xa7, 0x00, 0x92, 0x03, 0x84, 0x06, 0x7d, 0x09, 0x77, 0x0c, 0x73, 0x0e, + 0x71, 0x10, 0x6f, 0x11, 0x6d, 0x13, 0x6b, 0x14, 0x6a, 0x15, 0x6a, 0x16, + 0x69, 0x17, 0x68, 0x17, 0x68, 0x18, 0x67, 0x19, 0x66, 0x19, 0x66, 0x1a, + 0x66, 0x1a, 0x66, 0x1a, 0x0a, 0x38, 0x18, 0x2b, 0x25, 0x27, 0x2e, 0x25, + 0x35, 0x24, 0x3b, 0x23, 0x3f, 0x23, 0x43, 0x23, 0x46, 0x23, 0x48, 0x23, + 0x4a, 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, + 0x52, 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, + 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, 0x40, 0x00, 0x37, + 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x01, 0x0a, 0x06, 0x06, + 0x0b, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, + 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, + 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, + 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, + 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, + 0x17, 0x00, 0x1f, 0x00, 0x44, 0x36, 0x49, 0x33, 0x4d, 0x31, 0x4f, 0x2f, + 0x51, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x56, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, + 0x59, 0x28, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0xaa, 0x00, 0x98, 0x01, + 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x08, 0x79, 0x0a, 0x76, 0x0c, 0x73, 0x0d, + 0x71, 0x0f, 0x6f, 0x10, 0x6e, 0x11, 0x6d, 0x12, 0x6c, 0x13, 0x6b, 0x14, + 0x6a, 0x15, 0x6a, 0x15, 0x69, 0x16, 0x68, 0x17, 0x68, 0x17, 0x68, 0x18, + 0x0a, 0x3c, 0x14, 0x30, 0x1e, 0x2a, 0x26, 0x27, 0x2d, 0x25, 0x32, 0x24, + 0x36, 0x24, 0x3b, 0x24, 0x3e, 0x23, 0x41, 0x23, 0x43, 0x23, 0x45, 0x23, + 0x47, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, + 0x4f, 0x23, 0x50, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, + 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, + 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x03, 0x23, + 0x00, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, + 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, + 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x38, 0x48, 0x35, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x58, 0x29, 0xac, 0x00, 0x9d, 0x00, 0x91, 0x02, 0x89, 0x03, + 0x82, 0x05, 0x7e, 0x07, 0x7a, 0x09, 0x77, 0x0a, 0x75, 0x0c, 0x73, 0x0d, + 0x71, 0x0e, 0x70, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6c, 0x12, 0x6c, 0x13, + 0x6b, 0x13, 0x6a, 0x14, 0x6a, 0x15, 0x6a, 0x15, 0x09, 0x3f, 0x11, 0x33, + 0x1a, 0x2d, 0x21, 0x29, 0x27, 0x27, 0x2c, 0x26, 0x30, 0x25, 0x34, 0x24, + 0x37, 0x24, 0x3a, 0x24, 0x3d, 0x23, 0x40, 0x23, 0x42, 0x23, 0x44, 0x23, + 0x45, 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, + 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, + 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, + 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, + 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x39, 0x46, 0x36, + 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2b, 0x55, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, + 0xae, 0x00, 0xa1, 0x00, 0x96, 0x01, 0x8d, 0x02, 0x87, 0x03, 0x82, 0x05, + 0x7e, 0x07, 0x7b, 0x08, 0x78, 0x09, 0x76, 0x0b, 0x74, 0x0c, 0x73, 0x0d, + 0x71, 0x0e, 0x70, 0x0e, 0x6f, 0x0f, 0x6e, 0x11, 0x6d, 0x11, 0x6c, 0x11, + 0x6c, 0x12, 0x6b, 0x13, 0x09, 0x40, 0x10, 0x36, 0x17, 0x2f, 0x1d, 0x2c, + 0x22, 0x29, 0x27, 0x27, 0x2b, 0x26, 0x2f, 0x25, 0x32, 0x25, 0x35, 0x24, + 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x23, 0x40, 0x23, 0x42, 0x23, + 0x44, 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, + 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, + 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, + 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, + 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, + 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2c, 0x55, 0x2a, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0xaf, 0x00, 0xa3, 0x00, + 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x86, 0x03, 0x81, 0x05, 0x7e, 0x06, + 0x7b, 0x07, 0x79, 0x09, 0x77, 0x0a, 0x75, 0x0b, 0x74, 0x0c, 0x73, 0x0c, + 0x71, 0x0d, 0x71, 0x0e, 0x6f, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6d, 0x11, + 0x09, 0x41, 0x0f, 0x38, 0x15, 0x32, 0x1a, 0x2e, 0x1f, 0x2b, 0x24, 0x29, + 0x27, 0x27, 0x2b, 0x26, 0x2e, 0x25, 0x31, 0x25, 0x34, 0x25, 0x36, 0x24, + 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x23, 0x40, 0x23, 0x41, 0x23, + 0x43, 0x23, 0x44, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x1f, 0x00, + 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, + 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, 0x00, + 0x4d, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, + 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, + 0x00, 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x57, 0x2a, + 0x57, 0x2a, 0x57, 0x2a, 0xb0, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x95, 0x00, + 0x8e, 0x02, 0x89, 0x02, 0x85, 0x03, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, + 0x7a, 0x08, 0x78, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x73, 0x0c, 0x73, 0x0c, + 0x71, 0x0d, 0x71, 0x0e, 0x70, 0x0e, 0x6e, 0x0f, 0x09, 0x42, 0x0e, 0x39, + 0x13, 0x33, 0x18, 0x2f, 0x1c, 0x2d, 0x21, 0x2a, 0x24, 0x29, 0x28, 0x27, + 0x2b, 0x26, 0x2e, 0x26, 0x30, 0x25, 0x33, 0x25, 0x35, 0x25, 0x37, 0x24, + 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x23, 0x40, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x1f, 0x00, 0x3b, 0x00, + 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, + 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, + 0x1f, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, + 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, + 0x46, 0x36, 0x49, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, + 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, 0x00, 0x91, 0x01, 0x8c, 0x02, + 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, 0x7a, 0x07, + 0x79, 0x09, 0x77, 0x09, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x73, 0x0c, + 0x71, 0x0d, 0x71, 0x0e, 0x09, 0x43, 0x0e, 0x3b, 0x12, 0x35, 0x16, 0x31, + 0x1a, 0x2e, 0x1e, 0x2c, 0x22, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, + 0x2d, 0x26, 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x37, 0x24, + 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, + 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, + 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, + 0x82, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, + 0x09, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, + 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x37, 0x48, 0x35, + 0x4a, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0xb1, 0x00, 0xa9, 0x00, + 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x87, 0x03, + 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x08, + 0x78, 0x09, 0x76, 0x0a, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x72, 0x0c, + 0x09, 0x44, 0x0d, 0x3c, 0x11, 0x36, 0x15, 0x33, 0x19, 0x30, 0x1c, 0x2d, + 0x20, 0x2b, 0x23, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, 0x2d, 0x26, + 0x2f, 0x26, 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x24, 0x38, 0x24, + 0x39, 0x24, 0x3a, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, + 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, + 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, 0x00, + 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, + 0x00, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, + 0x00, 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3c, 0x43, 0x39, 0x46, 0x38, 0x46, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, + 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9c, 0x00, + 0x96, 0x00, 0x91, 0x01, 0x8d, 0x02, 0x89, 0x02, 0x86, 0x03, 0x83, 0x04, + 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x09, + 0x77, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x74, 0x0b, 0x0a, 0x44, 0x0d, 0x3d, + 0x10, 0x38, 0x14, 0x34, 0x17, 0x31, 0x1b, 0x2e, 0x1e, 0x2d, 0x21, 0x2b, + 0x23, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2d, 0x26, 0x2e, 0x26, + 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x24, 0x38, 0x24, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, + 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, + 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, + 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x43, 0x39, + 0x46, 0x39, 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x32, + 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, + 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, 0x00, 0x93, 0x00, + 0x8f, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, + 0x7f, 0x06, 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x08, 0x78, 0x09, + 0x76, 0x09, 0x76, 0x0a, 0x0a, 0x44, 0x0c, 0x3e, 0x10, 0x39, 0x13, 0x35, + 0x16, 0x32, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2c, 0x21, 0x2b, 0x24, 0x2a, + 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x30, 0x25, + 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, + 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x0f, 0x00, + 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, + 0x60, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, + 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, + 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, 0x46, 0x38, + 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xac, 0x00, + 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x01, 0x8e, 0x01, + 0x8a, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x05, + 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x07, 0x78, 0x09, 0x77, 0x09, + 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x36, 0x15, 0x33, 0x18, 0x30, + 0x1b, 0x2e, 0x1d, 0x2d, 0x20, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x26, 0x28, + 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x2f, 0x26, 0x31, 0x25, + 0x33, 0x25, 0x33, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, + 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, + 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, + 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, + 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, + 0x00, 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x36, 0x49, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, + 0x53, 0x2e, 0x53, 0x2e, 0xb3, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa1, 0x00, + 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8c, 0x02, 0x89, 0x02, + 0x87, 0x02, 0x85, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7d, 0x06, + 0x7c, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x08, 0x0a, 0x45, 0x0c, 0x3f, + 0x0f, 0x3a, 0x11, 0x37, 0x14, 0x34, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, + 0x1e, 0x2d, 0x21, 0x2b, 0x22, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, + 0x2a, 0x27, 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x30, 0x25, 0x32, 0x25, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, + 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, + 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, + 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, + 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3a, + 0x44, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, + 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, 0x00, 0x9d, 0x00, 0x99, 0x00, + 0x95, 0x00, 0x91, 0x01, 0x8e, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x86, 0x02, + 0x84, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, + 0x7b, 0x07, 0x7a, 0x07, 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3b, 0x11, 0x38, + 0x13, 0x35, 0x16, 0x32, 0x18, 0x30, 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2c, + 0x21, 0x2b, 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, + 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x2f, 0x25, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, + 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, + 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, + 0x3d, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, + 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, + 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x39, + 0x46, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, + 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2f, 0x52, 0x2e, 0xb3, 0x00, 0xae, 0x00, + 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x93, 0x00, + 0x8f, 0x01, 0x8d, 0x02, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x03, 0x84, 0x03, + 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x7c, 0x07, + 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, 0x36, 0x15, 0x33, + 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2b, 0x22, 0x2b, + 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, + 0x2d, 0x26, 0x2f, 0x26, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, + 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, + 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, + 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, + 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, + 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3c, 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, + 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4c, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x2f, 0xb3, 0x00, 0xae, 0x00, 0xa9, 0x00, 0xa4, 0x00, + 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, 0x01, + 0x8c, 0x02, 0x89, 0x02, 0x87, 0x02, 0x86, 0x03, 0x84, 0x03, 0x82, 0x04, + 0x80, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x0a, 0x45, 0x0b, 0x40, + 0x0e, 0x3c, 0x10, 0x39, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x30, + 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x24, 0x2a, + 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x2c, 0x26, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, + 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, + 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, + 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, + 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3c, + 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa1, 0x00, 0x9d, 0x00, + 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, + 0x89, 0x02, 0x87, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, + 0x7f, 0x05, 0x7e, 0x06, 0x0a, 0x45, 0x0b, 0x41, 0x0e, 0x3d, 0x10, 0x3a, + 0x11, 0x37, 0x14, 0x34, 0x16, 0x32, 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, + 0x1d, 0x2d, 0x20, 0x2c, 0x21, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x25, 0x29, + 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, + 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, + 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, + 0x1f, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, + 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, + 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, + 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, + 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x9a, 0x00, 0x97, 0x00, + 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x02, 0x8a, 0x02, 0x88, 0x02, + 0x86, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, 0x7f, 0x05, + 0x0a, 0x45, 0x0b, 0x41, 0x0d, 0x3e, 0x10, 0x3a, 0x11, 0x38, 0x13, 0x35, + 0x16, 0x33, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x2e, 0x1d, 0x2e, 0x1e, 0x2d, + 0x20, 0x2b, 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x25, 0x28, 0x28, 0x28, + 0x28, 0x28, 0x2a, 0x27, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, + 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, + 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, + 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, + 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, + 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x39, + 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, + 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, + 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, 0xab, 0x00, 0xa7, 0x00, + 0xa3, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x95, 0x00, 0x92, 0x00, + 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x88, 0x02, 0x86, 0x02, + 0x85, 0x03, 0x82, 0x03, 0x82, 0x04, 0x80, 0x05, 0x0a, 0x46, 0x0b, 0x42, + 0x0d, 0x3e, 0x0f, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x15, 0x34, 0x16, 0x32, + 0x18, 0x30, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2d, 0x20, 0x2d, 0x20, 0x2b, + 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, + 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x32, 0x74, 0x38, 0x57, 0x3a, 0x4f, 0x3c, 0x4b, 0x3c, 0x49, 0x3d, 0x47, + 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, + 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, + 0x3d, 0x42, 0x3d, 0x42, 0x41, 0x09, 0x1b, 0x31, 0x27, 0x38, 0x2d, 0x3a, + 0x31, 0x3b, 0x33, 0x3c, 0x35, 0x3c, 0x36, 0x3d, 0x37, 0x3d, 0x38, 0x3d, + 0x38, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, + 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x73, 0x00, 0x38, 0x31, + 0x3a, 0x38, 0x3c, 0x3a, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x3d, 0x28, 0x38, 0x30, + 0x38, 0x34, 0x39, 0x36, 0x39, 0x38, 0x3a, 0x39, 0x3a, 0x39, 0x3b, 0x3a, + 0x3b, 0x3b, 0x3c, 0x3b, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3e, 0x3c, + 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3d, 0x3e, 0x3d, 0x3e, 0x3d, + 0x00, 0x7e, 0x05, 0x58, 0x0f, 0x4e, 0x17, 0x49, 0x1d, 0x47, 0x21, 0x45, + 0x24, 0x44, 0x27, 0x43, 0x29, 0x43, 0x2b, 0x43, 0x2d, 0x43, 0x2e, 0x42, + 0x2f, 0x42, 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x33, 0x42, + 0x34, 0x42, 0x34, 0x42, 0x2b, 0x4e, 0x31, 0x47, 0x34, 0x45, 0x36, 0x44, + 0x38, 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, + 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, + 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x2f, 0x00, + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x16, 0x3a, 0x20, 0x39, 0x25, 0x39, 0x29, + 0x39, 0x2d, 0x39, 0x2f, 0x3a, 0x31, 0x3a, 0x32, 0x3b, 0x33, 0x3a, 0x34, + 0x3a, 0x35, 0x3a, 0x36, 0x3a, 0x36, 0x3b, 0x37, 0x3b, 0x37, 0x3c, 0x38, + 0x3c, 0x38, 0x3d, 0x38, 0x3e, 0x39, 0x3e, 0x39, 0x00, 0x93, 0x01, 0x70, + 0x06, 0x60, 0x0d, 0x56, 0x12, 0x52, 0x16, 0x4e, 0x1a, 0x4c, 0x1d, 0x4a, + 0x20, 0x48, 0x22, 0x47, 0x24, 0x46, 0x26, 0x45, 0x27, 0x44, 0x29, 0x44, + 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x43, 0x2d, 0x43, 0x2e, 0x43, 0x2f, 0x43, + 0x28, 0x56, 0x2e, 0x4f, 0x31, 0x4b, 0x33, 0x49, 0x35, 0x48, 0x36, 0x46, + 0x37, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, + 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, + 0x3d, 0x42, 0x3d, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, 0x00, 0x22, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x43, 0x11, 0x3d, 0x18, 0x3b, 0x1e, 0x3a, 0x22, 0x39, 0x25, 0x39, 0x28, + 0x39, 0x2a, 0x39, 0x2c, 0x39, 0x2d, 0x3a, 0x2f, 0x3b, 0x30, 0x3b, 0x31, + 0x3b, 0x32, 0x3b, 0x33, 0x3a, 0x33, 0x3a, 0x34, 0x3a, 0x34, 0x3a, 0x35, + 0x3a, 0x35, 0x3b, 0x35, 0x00, 0x9d, 0x00, 0x7f, 0x03, 0x6d, 0x07, 0x62, + 0x0c, 0x5b, 0x10, 0x56, 0x13, 0x52, 0x16, 0x50, 0x19, 0x4d, 0x1c, 0x4c, + 0x1e, 0x4b, 0x1f, 0x4b, 0x22, 0x4a, 0x23, 0x49, 0x24, 0x48, 0x25, 0x47, + 0x27, 0x46, 0x28, 0x45, 0x29, 0x44, 0x29, 0x44, 0x27, 0x59, 0x2c, 0x53, + 0x2f, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x48, 0x35, 0x47, + 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, + 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, 0x39, 0x42, 0x3a, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, + 0x00, 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x0f, 0x3e, 0x14, + 0x3c, 0x19, 0x3a, 0x1d, 0x3a, 0x20, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x27, + 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2c, 0x3a, 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, + 0x3b, 0x2f, 0x3b, 0x30, 0x3b, 0x31, 0x3b, 0x32, 0x3a, 0x32, 0x3a, 0x33, + 0x00, 0xa3, 0x00, 0x88, 0x01, 0x77, 0x04, 0x6b, 0x08, 0x63, 0x0b, 0x5d, + 0x0e, 0x59, 0x11, 0x57, 0x14, 0x54, 0x16, 0x51, 0x19, 0x4f, 0x1a, 0x4d, + 0x1c, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x21, 0x4b, 0x22, 0x4a, 0x23, 0x49, + 0x24, 0x49, 0x25, 0x49, 0x27, 0x5a, 0x2a, 0x55, 0x2d, 0x52, 0x2f, 0x4f, + 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x49, 0x35, 0x48, + 0x35, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, + 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x55, 0x00, + 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x0e, 0x40, 0x12, 0x3d, 0x16, 0x3b, 0x1a, + 0x3b, 0x1d, 0x3a, 0x20, 0x39, 0x22, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2b, 0x3a, 0x2c, 0x3b, 0x2d, + 0x3c, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, 0x30, 0x00, 0xa7, 0x00, 0x90, + 0x00, 0x7e, 0x02, 0x73, 0x05, 0x6a, 0x08, 0x65, 0x0b, 0x5f, 0x0d, 0x5b, + 0x10, 0x58, 0x12, 0x56, 0x14, 0x55, 0x16, 0x52, 0x18, 0x50, 0x1a, 0x4e, + 0x1b, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x20, 0x4b, 0x21, 0x4b, + 0x26, 0x5b, 0x2a, 0x57, 0x2c, 0x54, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4e, + 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, + 0x35, 0x47, 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x39, 0x46, 0x39, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, + 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x46, 0x0d, 0x41, 0x11, 0x3d, 0x14, 0x3d, 0x17, 0x3b, 0x1a, 0x3b, 0x1c, + 0x3b, 0x1e, 0x39, 0x21, 0x39, 0x22, 0x3a, 0x23, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x39, 0x2b, 0x3b, 0x2c, + 0x3c, 0x2d, 0x3c, 0x2e, 0x00, 0xa9, 0x00, 0x95, 0x00, 0x85, 0x01, 0x79, + 0x03, 0x70, 0x06, 0x69, 0x08, 0x65, 0x0b, 0x61, 0x0d, 0x5c, 0x0f, 0x59, + 0x11, 0x58, 0x13, 0x56, 0x14, 0x55, 0x16, 0x53, 0x18, 0x51, 0x19, 0x4f, + 0x1b, 0x4d, 0x1c, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x26, 0x5c, 0x29, 0x58, + 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4d, + 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, + 0x35, 0x48, 0x35, 0x47, 0x36, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, + 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0f, + 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x18, 0x3a, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, + 0x39, 0x20, 0x39, 0x21, 0x39, 0x22, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x27, 0x39, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x3a, 0x2b, + 0x00, 0xab, 0x00, 0x99, 0x00, 0x8b, 0x01, 0x7e, 0x02, 0x77, 0x04, 0x6f, + 0x06, 0x69, 0x08, 0x66, 0x0b, 0x62, 0x0d, 0x5e, 0x0e, 0x5b, 0x10, 0x59, + 0x12, 0x57, 0x13, 0x56, 0x15, 0x55, 0x16, 0x54, 0x18, 0x52, 0x19, 0x50, + 0x1a, 0x4e, 0x1b, 0x4d, 0x26, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2c, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, + 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x49, 0x35, 0x48, 0x36, 0x46, 0x37, 0x46, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, + 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, + 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0e, 0x40, 0x11, 0x3d, 0x13, + 0x3d, 0x16, 0x3b, 0x18, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1e, 0x3a, 0x1f, + 0x39, 0x21, 0x39, 0x21, 0x3a, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x27, 0x39, 0x27, 0x39, 0x28, 0x39, 0x2a, 0x00, 0xac, 0x00, 0x9c, + 0x00, 0x8f, 0x00, 0x83, 0x02, 0x7b, 0x03, 0x74, 0x05, 0x6d, 0x07, 0x69, + 0x09, 0x66, 0x0b, 0x63, 0x0c, 0x5f, 0x0e, 0x5c, 0x0f, 0x5a, 0x11, 0x58, + 0x12, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x18, 0x53, 0x18, 0x51, + 0x26, 0x5d, 0x27, 0x59, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, + 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, + 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, + 0x00, 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, + 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0b, 0x42, 0x0d, 0x41, 0x10, 0x3e, 0x13, 0x3d, 0x15, 0x3c, 0x17, + 0x3a, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x20, + 0x39, 0x21, 0x3a, 0x22, 0x3b, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, + 0x3a, 0x27, 0x39, 0x27, 0x00, 0xad, 0x00, 0x9f, 0x00, 0x92, 0x00, 0x88, + 0x01, 0x7e, 0x02, 0x78, 0x04, 0x72, 0x05, 0x6d, 0x07, 0x69, 0x09, 0x66, + 0x0a, 0x64, 0x0c, 0x61, 0x0d, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, + 0x13, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x26, 0x5d, 0x27, 0x5a, + 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, + 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, + 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, + 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x19, + 0x3a, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x21, + 0x39, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x3a, 0x24, 0x3a, 0x26, + 0x00, 0xae, 0x00, 0xa2, 0x00, 0x94, 0x00, 0x8b, 0x00, 0x82, 0x02, 0x7b, + 0x03, 0x76, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x69, 0x09, 0x66, 0x0a, 0x64, + 0x0c, 0x62, 0x0d, 0x5f, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x58, 0x12, 0x58, + 0x13, 0x56, 0x14, 0x56, 0x25, 0x5d, 0x27, 0x5b, 0x29, 0x57, 0x2a, 0x57, + 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4d, 0x33, 0x4a, 0x34, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, + 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, + 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0f, 0x40, 0x11, + 0x3d, 0x13, 0x3e, 0x14, 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x19, 0x3b, 0x1a, + 0x3c, 0x1c, 0x3b, 0x1d, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, + 0x3a, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x00, 0xae, 0x00, 0xa3, + 0x00, 0x97, 0x00, 0x8e, 0x00, 0x86, 0x01, 0x7e, 0x02, 0x79, 0x03, 0x75, + 0x05, 0x70, 0x06, 0x6b, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, + 0x0c, 0x5f, 0x0e, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, 0x13, 0x58, + 0x25, 0x5d, 0x27, 0x5c, 0x29, 0x58, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, + 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, + 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0b, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x10, 0x3e, 0x12, 0x3d, 0x13, + 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x39, 0x21, + 0x3b, 0x22, 0x3b, 0x23, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, + 0x00, 0x8a, 0x01, 0x81, 0x02, 0x7c, 0x02, 0x77, 0x04, 0x74, 0x05, 0x6f, + 0x06, 0x6a, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, 0x0c, 0x61, + 0x0e, 0x5e, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x59, 0x25, 0x5d, 0x27, 0x5c, + 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, + 0x00, 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, + 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x44, 0x0d, + 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, + 0x3c, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, + 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x1f, 0x39, 0x21, 0x39, 0x21, 0x3a, 0x21, + 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x92, 0x00, 0x8c, 0x00, 0x85, + 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x76, 0x05, 0x73, 0x06, 0x6e, 0x07, 0x6a, + 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x5f, + 0x0e, 0x5c, 0x0f, 0x5a, 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x59, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, + 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, + 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, + 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0f, + 0x40, 0x11, 0x3d, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x16, 0x3b, 0x17, + 0x3a, 0x19, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1e, + 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x00, 0xb0, 0x00, 0xa7, + 0x00, 0x9e, 0x00, 0x94, 0x00, 0x8e, 0x00, 0x88, 0x01, 0x81, 0x02, 0x7c, + 0x02, 0x78, 0x03, 0x75, 0x05, 0x72, 0x06, 0x6d, 0x07, 0x6a, 0x08, 0x68, + 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x60, 0x0e, 0x5d, + 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2c, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4e, 0x31, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, + 0x00, 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, + 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, + 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, + 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x10, 0x3e, 0x11, + 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, + 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, + 0x39, 0x1f, 0x39, 0x1f, 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xa0, 0x00, 0x96, + 0x00, 0x90, 0x00, 0x8a, 0x00, 0x84, 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x77, + 0x04, 0x74, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, + 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x63, 0x0c, 0x61, 0x25, 0x5e, 0x27, 0x5c, + 0x27, 0x5c, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x8c, 0x00, 0x63, + 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x14, 0x2c, 0x00, 0x57, 0x00, 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x11, 0x31, 0x00, 0x5f, + 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0b, + 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x12, 0x3d, 0x13, + 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, 0x3b, 0x19, + 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, + 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x98, 0x00, 0x91, 0x00, 0x8c, + 0x00, 0x87, 0x01, 0x80, 0x02, 0x7c, 0x02, 0x79, 0x03, 0x76, 0x05, 0x74, + 0x05, 0x70, 0x06, 0x6b, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, + 0x0b, 0x64, 0x0c, 0x63, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x58, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, + 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, + 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x63, 0x00, 0x4d, 0x00, 0x60, 0x00, 0x77, + 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x01, 0x04, 0x16, + 0x00, 0x57, 0x00, 0x78, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, + 0x00, 0x9d, 0x00, 0x9d, 0x23, 0x03, 0x00, 0x20, 0x00, 0x5f, 0x00, 0x7c, + 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, + 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, + 0x42, 0x0f, 0x40, 0x11, 0x3d, 0x11, 0x3d, 0x13, 0x3f, 0x13, 0x3d, 0x15, + 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3b, 0x19, 0x3c, 0x1a, + 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3b, 0x1e, 0x00, 0xb1, 0x00, 0xa9, + 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8d, 0x00, 0x89, 0x01, 0x83, + 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x03, 0x75, 0x05, 0x73, 0x06, 0x6f, + 0x06, 0x6b, 0x07, 0x69, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x0b, 0x64, + 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, + 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, + 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, 0x60, 0x00, 0x70, + 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, + 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x27, 0x00, 0x01, 0x12, 0x00, 0x43, + 0x00, 0x61, 0x00, 0x74, 0x00, 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, + 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, + 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, + 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, + 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x41, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, 0x42, 0x0f, 0x40, 0x10, + 0x3e, 0x11, 0x3d, 0x13, 0x3d, 0x13, 0x3f, 0x15, 0x3c, 0x15, 0x3c, 0x17, + 0x3a, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1c, 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa3, 0x00, 0x9c, + 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x86, 0x01, 0x80, 0x02, 0x7c, + 0x02, 0x79, 0x03, 0x77, 0x04, 0x75, 0x05, 0x72, 0x06, 0x6e, 0x07, 0x6b, + 0x07, 0x69, 0x09, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x24, 0x5e, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, + 0x00, 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, + 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x89, 0x00, 0x77, + 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, + 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, + 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x54, 0x00, 0x41, 0x00, 0x17, 0x00, 0x00, 0x10, 0x00, 0x36, 0x00, 0x51, + 0x00, 0x64, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, + 0x00, 0x8d, 0x00, 0x90, 0x00, 0x92, 0x00, 0x93, 0x52, 0x00, 0x3c, 0x00, + 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, + 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, + 0x00, 0x93, 0x00, 0x95, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, + 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, + 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x40, 0x11, 0x3d, 0x11, + 0x3d, 0x13, 0x3f, 0x13, 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, + 0x3a, 0x18, 0x3b, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, + 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x95, 0x00, 0x90, + 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7e, 0x02, 0x7b, 0x02, 0x78, + 0x03, 0x76, 0x05, 0x74, 0x05, 0x71, 0x06, 0x6d, 0x07, 0x6b, 0x07, 0x69, + 0x09, 0x68, 0x09, 0x66, 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x50, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, + 0x00, 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, + 0x00, 0x86, 0x00, 0x89, 0x00, 0x90, 0x00, 0x83, 0x00, 0x60, 0x00, 0x30, + 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x5e, 0x00, 0x69, + 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, + 0x2f, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, + 0x00, 0x64, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, + 0x00, 0x89, 0x00, 0x8b, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x00, + 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, + 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, + 0x1b, 0x85, 0x05, 0x96, 0x01, 0x9e, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xa9, + 0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, + 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, + 0x00, 0xb2, 0x00, 0xb2, 0x1c, 0x6e, 0x03, 0x8a, 0x00, 0x97, 0x00, 0x9e, + 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xac, + 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, + 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x0e, 0x92, 0x01, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xae, + 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb2, + 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, + 0x00, 0xb4, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, + 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, + 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, 0x22, 0x00, 0x01, + 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, + 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x00, + 0x0a, 0x02, 0x00, 0x10, 0x00, 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, + 0x00, 0x65, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, + 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, 0x00, 0x04, 0x00, 0x1f, + 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, + 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x27, 0x60, 0x0f, 0x73, + 0x07, 0x81, 0x03, 0x8a, 0x01, 0x91, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9d, + 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, + 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, + 0x29, 0x3a, 0x0f, 0x59, 0x06, 0x6c, 0x02, 0x79, 0x00, 0x84, 0x00, 0x8b, + 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, + 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa6, + 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x14, 0x79, 0x07, 0x88, + 0x03, 0x92, 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa5, + 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, 0xac, + 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, + 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x9b, 0x00, 0x92, + 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x18, + 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, + 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x56, 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x00, 0x08, 0x04, + 0x00, 0x10, 0x00, 0x25, 0x00, 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, + 0x00, 0x65, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x77, 0x5c, 0x00, 0x55, 0x00, + 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x33, + 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, + 0x00, 0x77, 0x00, 0x7b, 0x2d, 0x54, 0x17, 0x63, 0x0d, 0x6f, 0x07, 0x78, + 0x04, 0x80, 0x02, 0x86, 0x01, 0x8b, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x95, + 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, + 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x31, 0x27, 0x18, 0x40, + 0x0d, 0x52, 0x07, 0x61, 0x04, 0x6c, 0x02, 0x75, 0x01, 0x7c, 0x00, 0x82, + 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, + 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x18, 0x6f, 0x0c, 0x7b, 0x06, 0x84, 0x03, 0x8c, + 0x02, 0x91, 0x01, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, + 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, + 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, + 0x00, 0x5f, 0x00, 0x66, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, + 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x26, + 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, + 0x4d, 0x00, 0x3b, 0x00, 0x27, 0x00, 0x13, 0x00, 0x07, 0x06, 0x00, 0x10, + 0x00, 0x22, 0x00, 0x32, 0x00, 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, + 0x00, 0x65, 0x00, 0x6b, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, + 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, + 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, + 0x30, 0x4e, 0x1d, 0x5a, 0x12, 0x64, 0x0c, 0x6d, 0x08, 0x75, 0x05, 0x7a, + 0x03, 0x7f, 0x02, 0x85, 0x02, 0x89, 0x01, 0x8b, 0x00, 0x8e, 0x00, 0x90, + 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, + 0x00, 0x9e, 0x00, 0x9f, 0x36, 0x1e, 0x1f, 0x32, 0x12, 0x43, 0x0b, 0x50, + 0x07, 0x5a, 0x05, 0x63, 0x02, 0x6b, 0x01, 0x72, 0x01, 0x78, 0x00, 0x7c, + 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, + 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x1b, 0x6b, 0x0f, 0x74, 0x09, 0x7d, 0x05, 0x83, 0x03, 0x89, 0x02, 0x8d, + 0x01, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, + 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, + 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, + 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, + 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, + 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, + 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, + 0x32, 0x00, 0x20, 0x00, 0x0e, 0x00, 0x06, 0x07, 0x00, 0x10, 0x00, 0x20, + 0x00, 0x2f, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, + 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, + 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, + 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x33, 0x4b, 0x21, 0x54, + 0x16, 0x5d, 0x10, 0x65, 0x0b, 0x6c, 0x08, 0x72, 0x06, 0x77, 0x04, 0x7b, + 0x03, 0x7f, 0x02, 0x83, 0x02, 0x87, 0x01, 0x8a, 0x01, 0x8c, 0x00, 0x8e, + 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x98, + 0x3a, 0x19, 0x24, 0x2a, 0x18, 0x38, 0x10, 0x44, 0x0b, 0x4e, 0x07, 0x57, + 0x05, 0x5f, 0x04, 0x65, 0x02, 0x6b, 0x01, 0x71, 0x01, 0x76, 0x00, 0x79, + 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, 0x87, 0x00, 0x88, 0x00, 0x8b, + 0x00, 0x8d, 0x00, 0x8f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x68, 0x12, 0x70, + 0x0c, 0x77, 0x08, 0x7d, 0x05, 0x82, 0x03, 0x87, 0x02, 0x8b, 0x02, 0x8e, + 0x01, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, + 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x9d, 0x00, 0x99, + 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, + 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, + 0x1a, 0x00, 0x0b, 0x00, 0x05, 0x08, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x2c, + 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x5e, 0x00, 0x5b, 0x00, + 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, + 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, + 0x00, 0x54, 0x00, 0x5b, 0x34, 0x49, 0x25, 0x51, 0x1a, 0x58, 0x13, 0x60, + 0x0e, 0x65, 0x0b, 0x6b, 0x08, 0x71, 0x06, 0x75, 0x05, 0x78, 0x04, 0x7b, + 0x03, 0x7f, 0x02, 0x83, 0x02, 0x86, 0x01, 0x88, 0x01, 0x8a, 0x00, 0x8c, + 0x00, 0x8d, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x3b, 0x17, 0x28, 0x24, + 0x1c, 0x30, 0x14, 0x3b, 0x0f, 0x45, 0x0b, 0x4d, 0x07, 0x55, 0x05, 0x5b, + 0x04, 0x61, 0x03, 0x67, 0x02, 0x6b, 0x01, 0x6f, 0x01, 0x74, 0x00, 0x77, + 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x83, 0x00, 0x86, 0x00, 0x87, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x67, 0x14, 0x6d, 0x0e, 0x73, 0x0a, 0x79, + 0x07, 0x7e, 0x05, 0x82, 0x03, 0x86, 0x02, 0x89, 0x02, 0x8c, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, + 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, + 0x00, 0x33, 0x00, 0x3d, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, + 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x22, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, + 0x56, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x00, + 0x0a, 0x02, 0x04, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, + 0x00, 0x3e, 0x00, 0x46, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, + 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x12, + 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, + 0x36, 0x47, 0x27, 0x4e, 0x1d, 0x54, 0x16, 0x5b, 0x11, 0x61, 0x0e, 0x65, + 0x0b, 0x6a, 0x08, 0x6f, 0x07, 0x73, 0x05, 0x76, 0x05, 0x79, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x82, 0x02, 0x85, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8b, + 0x00, 0x8c, 0x00, 0x8d, 0x3d, 0x14, 0x2c, 0x20, 0x20, 0x2a, 0x18, 0x34, + 0x12, 0x3e, 0x0e, 0x45, 0x0a, 0x4c, 0x07, 0x53, 0x06, 0x58, 0x05, 0x5e, + 0x04, 0x63, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x72, 0x00, 0x76, + 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x1e, 0x66, 0x16, 0x6b, 0x10, 0x71, 0x0c, 0x76, 0x09, 0x7a, 0x07, 0x7e, + 0x05, 0x81, 0x03, 0x85, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, 0x99, + 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, + 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, + 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x09, 0x03, + 0x04, 0x09, 0x00, 0x10, 0x00, 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, + 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, + 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, + 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x37, 0x46, 0x29, 0x4c, + 0x20, 0x52, 0x19, 0x57, 0x14, 0x5d, 0x10, 0x62, 0x0d, 0x66, 0x0b, 0x69, + 0x09, 0x6e, 0x07, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x7a, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x82, 0x02, 0x84, 0x02, 0x87, 0x01, 0x88, 0x01, 0x89, + 0x3f, 0x13, 0x2f, 0x1c, 0x23, 0x27, 0x1b, 0x2f, 0x15, 0x38, 0x10, 0x3e, + 0x0d, 0x45, 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x57, 0x05, 0x5c, 0x04, 0x60, + 0x03, 0x65, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x71, 0x00, 0x75, + 0x00, 0x77, 0x00, 0x79, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1f, 0x65, 0x17, 0x6a, + 0x11, 0x6f, 0x0d, 0x73, 0x0a, 0x77, 0x08, 0x7b, 0x06, 0x7e, 0x05, 0x81, + 0x04, 0x84, 0x03, 0x87, 0x02, 0x89, 0x02, 0x8b, 0x01, 0x8e, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, + 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, + 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, + 0x32, 0x00, 0x26, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x08, 0x04, 0x04, 0x0a, + 0x00, 0x10, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x2f, 0x5e, 0x00, 0x5d, 0x00, + 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, + 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, + 0x00, 0x33, 0x00, 0x3b, 0x38, 0x45, 0x2b, 0x4a, 0x22, 0x50, 0x1c, 0x55, + 0x16, 0x59, 0x12, 0x5f, 0x0f, 0x63, 0x0d, 0x66, 0x0b, 0x69, 0x09, 0x6e, + 0x07, 0x71, 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x7a, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x81, 0x02, 0x84, 0x02, 0x86, 0x40, 0x11, 0x31, 0x1b, + 0x26, 0x23, 0x1e, 0x2b, 0x18, 0x33, 0x13, 0x39, 0x0f, 0x40, 0x0c, 0x45, + 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x56, 0x05, 0x59, 0x04, 0x5e, 0x04, 0x61, + 0x02, 0x65, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6e, 0x01, 0x71, 0x00, 0x73, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x64, 0x18, 0x69, 0x13, 0x6d, 0x0f, 0x71, + 0x0c, 0x75, 0x09, 0x78, 0x07, 0x7b, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, + 0x03, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8e, 0x01, 0x8f, + 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0b, 0x00, 0x16, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, + 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, 0x47, 0x00, 0x37, + 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, + 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x07, 0x05, 0x03, 0x0a, 0x00, 0x0f, + 0x00, 0x1a, 0x00, 0x24, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, + 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, + 0x38, 0x45, 0x2d, 0x49, 0x24, 0x4e, 0x1e, 0x53, 0x19, 0x56, 0x14, 0x5b, + 0x11, 0x60, 0x0e, 0x63, 0x0c, 0x66, 0x0a, 0x69, 0x09, 0x6c, 0x07, 0x70, + 0x06, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x79, 0x03, 0x7a, 0x03, 0x7c, + 0x02, 0x7e, 0x02, 0x80, 0x40, 0x11, 0x32, 0x18, 0x28, 0x20, 0x20, 0x28, + 0x1a, 0x2f, 0x16, 0x35, 0x12, 0x3c, 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4b, + 0x08, 0x50, 0x07, 0x54, 0x05, 0x58, 0x05, 0x5c, 0x04, 0x5f, 0x04, 0x62, + 0x02, 0x66, 0x02, 0x69, 0x01, 0x6b, 0x01, 0x6e, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x20, 0x64, 0x19, 0x68, 0x14, 0x6b, 0x10, 0x6f, 0x0d, 0x73, 0x0b, 0x76, + 0x09, 0x79, 0x07, 0x7c, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, + 0x02, 0x88, 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8e, 0x01, 0x90, + 0x00, 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, + 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, + 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, + 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, + 0x14, 0x00, 0x0b, 0x00, 0x07, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x00, 0x19, + 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, + 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x01, + 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x39, 0x44, 0x2e, 0x49, + 0x26, 0x4c, 0x1f, 0x52, 0x1a, 0x55, 0x16, 0x58, 0x13, 0x5d, 0x10, 0x61, + 0x0e, 0x64, 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6c, 0x07, 0x70, 0x07, 0x72, + 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x79, 0x03, 0x7b, 0x02, 0x7c, + 0x41, 0x10, 0x34, 0x17, 0x2a, 0x1e, 0x23, 0x25, 0x1d, 0x2c, 0x18, 0x32, + 0x14, 0x37, 0x10, 0x3d, 0x0e, 0x42, 0x0c, 0x46, 0x0a, 0x4b, 0x08, 0x4f, + 0x07, 0x53, 0x05, 0x57, 0x05, 0x5a, 0x04, 0x5e, 0x04, 0x61, 0x03, 0x63, + 0x02, 0x66, 0x02, 0x69, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x63, 0x1a, 0x67, + 0x15, 0x6a, 0x11, 0x6e, 0x0e, 0x71, 0x0c, 0x74, 0x0a, 0x77, 0x08, 0x7a, + 0x07, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x87, + 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, + 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, + 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, + 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, + 0x40, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, + 0x0a, 0x01, 0x06, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x5f, 0x00, 0x5d, 0x00, + 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, + 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0d, + 0x00, 0x17, 0x00, 0x1f, 0x39, 0x43, 0x2f, 0x48, 0x27, 0x4b, 0x22, 0x4f, + 0x1c, 0x54, 0x18, 0x56, 0x14, 0x59, 0x12, 0x5e, 0x0f, 0x62, 0x0d, 0x64, + 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6b, 0x08, 0x6f, 0x07, 0x72, 0x06, 0x74, + 0x05, 0x75, 0x05, 0x77, 0x04, 0x78, 0x03, 0x7a, 0x42, 0x0f, 0x36, 0x16, + 0x2c, 0x1c, 0x25, 0x23, 0x1f, 0x29, 0x1a, 0x2e, 0x16, 0x34, 0x12, 0x39, + 0x0f, 0x3e, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4f, 0x07, 0x52, + 0x05, 0x56, 0x05, 0x59, 0x04, 0x5c, 0x04, 0x5f, 0x04, 0x62, 0x02, 0x64, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x6a, 0x12, 0x6d, + 0x0f, 0x70, 0x0d, 0x73, 0x0b, 0x75, 0x09, 0x78, 0x07, 0x7a, 0x06, 0x7d, + 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, + 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8c, 0x01, 0x8d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3a, 0x43, 0x31, 0x48, 0x29, 0x4a, 0x23, 0x4d, 0x1e, 0x53, 0x1a, 0x55, + 0x16, 0x57, 0x13, 0x5b, 0x11, 0x60, 0x0f, 0x62, 0x0d, 0x64, 0x0b, 0x66, + 0x0a, 0x68, 0x09, 0x6a, 0x08, 0x6e, 0x07, 0x71, 0x06, 0x73, 0x06, 0x75, + 0x05, 0x76, 0x05, 0x78, 0x43, 0x0f, 0x37, 0x15, 0x2e, 0x1b, 0x27, 0x21, + 0x21, 0x26, 0x1c, 0x2c, 0x18, 0x31, 0x15, 0x36, 0x12, 0x3a, 0x0f, 0x3f, + 0x0c, 0x43, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4e, 0x07, 0x52, 0x06, 0x55, + 0x05, 0x58, 0x05, 0x5b, 0x04, 0x5d, 0x04, 0x60, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x21, 0x63, 0x1b, 0x66, 0x17, 0x69, 0x13, 0x6c, 0x10, 0x6e, 0x0e, 0x71, + 0x0c, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x06, 0x7f, + 0x05, 0x81, 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x89, + 0x02, 0x8a, 0x02, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x31, 0x47, + 0x2a, 0x49, 0x24, 0x4c, 0x1f, 0x51, 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, + 0x12, 0x5c, 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0b, 0x66, 0x0a, 0x68, + 0x09, 0x6a, 0x08, 0x6d, 0x07, 0x71, 0x06, 0x72, 0x06, 0x74, 0x05, 0x75, + 0x43, 0x0f, 0x38, 0x14, 0x2f, 0x19, 0x28, 0x1f, 0x22, 0x24, 0x1d, 0x29, + 0x19, 0x2e, 0x16, 0x33, 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x40, 0x0c, 0x43, + 0x0b, 0x47, 0x0a, 0x4b, 0x09, 0x4e, 0x07, 0x51, 0x07, 0x55, 0x05, 0x56, + 0x05, 0x5a, 0x04, 0x5c, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, + 0x17, 0x68, 0x14, 0x6b, 0x11, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0b, 0x75, + 0x09, 0x77, 0x08, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, + 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, 0x89, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4b, + 0x21, 0x4f, 0x1d, 0x53, 0x19, 0x55, 0x16, 0x57, 0x14, 0x59, 0x11, 0x5e, + 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x6a, + 0x08, 0x6c, 0x07, 0x70, 0x07, 0x72, 0x06, 0x73, 0x43, 0x0e, 0x39, 0x13, + 0x31, 0x18, 0x2a, 0x1d, 0x24, 0x22, 0x1f, 0x27, 0x1b, 0x2b, 0x18, 0x30, + 0x16, 0x35, 0x12, 0x39, 0x0f, 0x3c, 0x0f, 0x41, 0x0c, 0x44, 0x0b, 0x47, + 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x50, 0x07, 0x54, 0x05, 0x55, 0x05, 0x59, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, 0x18, 0x68, 0x15, 0x6a, + 0x12, 0x6c, 0x0f, 0x6f, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x78, + 0x07, 0x79, 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, + 0x03, 0x84, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x33, 0x45, 0x2c, 0x49, 0x27, 0x4b, 0x22, 0x4d, 0x1e, 0x52, + 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, 0x13, 0x5b, 0x10, 0x5f, 0x0f, 0x61, + 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x08, 0x6c, + 0x07, 0x6f, 0x07, 0x72, 0x44, 0x0e, 0x3a, 0x12, 0x32, 0x17, 0x2b, 0x1c, + 0x26, 0x21, 0x21, 0x26, 0x1d, 0x2a, 0x19, 0x2e, 0x16, 0x32, 0x14, 0x35, + 0x12, 0x3a, 0x0f, 0x3d, 0x0e, 0x41, 0x0c, 0x44, 0x0b, 0x47, 0x0a, 0x4a, + 0x09, 0x4e, 0x07, 0x4f, 0x07, 0x53, 0x05, 0x55, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x22, 0x62, 0x1d, 0x65, 0x19, 0x67, 0x15, 0x6a, 0x13, 0x6c, 0x10, 0x6e, + 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7a, + 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, 0x03, 0x83, + 0x03, 0x85, 0x02, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x33, 0x44, + 0x2d, 0x48, 0x28, 0x4a, 0x23, 0x4c, 0x1f, 0x50, 0x1c, 0x53, 0x19, 0x55, + 0x16, 0x57, 0x14, 0x58, 0x12, 0x5c, 0x10, 0x60, 0x0e, 0x62, 0x0d, 0x63, + 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x07, 0x6e, + 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x1b, 0x27, 0x1f, 0x21, 0x24, + 0x1e, 0x28, 0x1a, 0x2b, 0x18, 0x30, 0x16, 0x34, 0x12, 0x37, 0x11, 0x3b, + 0x0f, 0x3e, 0x0d, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, + 0x07, 0x4f, 0x07, 0x53, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x65, + 0x19, 0x66, 0x16, 0x69, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6f, 0x0d, 0x71, + 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x77, 0x08, 0x79, 0x07, 0x7a, 0x06, 0x7c, + 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x83, 0x03, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x34, 0x44, 0x2e, 0x48, 0x29, 0x49, + 0x24, 0x4b, 0x20, 0x4f, 0x1d, 0x53, 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, + 0x13, 0x59, 0x11, 0x5d, 0x10, 0x61, 0x0e, 0x62, 0x0d, 0x63, 0x0c, 0x65, + 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x44, 0x0d, 0x3b, 0x12, + 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1e, 0x23, 0x22, 0x20, 0x26, 0x1d, 0x2b, + 0x19, 0x2e, 0x16, 0x31, 0x14, 0x35, 0x12, 0x38, 0x0f, 0x3b, 0x0f, 0x3f, + 0x0c, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x4e, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, + 0x14, 0x6a, 0x11, 0x6c, 0x10, 0x6e, 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, + 0x0a, 0x76, 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, + 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x82, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x48, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4d, + 0x1e, 0x51, 0x1b, 0x54, 0x18, 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x5b, + 0x10, 0x5e, 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, + 0x0a, 0x68, 0x09, 0x69, 0x44, 0x0c, 0x3c, 0x11, 0x35, 0x16, 0x2f, 0x19, + 0x2a, 0x1d, 0x25, 0x22, 0x21, 0x25, 0x1d, 0x29, 0x1a, 0x2b, 0x18, 0x30, + 0x16, 0x33, 0x12, 0x35, 0x12, 0x3a, 0x0f, 0x3c, 0x0f, 0x40, 0x0c, 0x41, + 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x22, 0x62, 0x1e, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6c, + 0x10, 0x6e, 0x0e, 0x70, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x75, 0x09, 0x76, + 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, + 0x05, 0x80, 0x04, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x35, 0x43, + 0x2f, 0x47, 0x2b, 0x49, 0x26, 0x4b, 0x22, 0x4c, 0x1f, 0x4f, 0x1c, 0x53, + 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, 0x13, 0x59, 0x12, 0x5c, 0x10, 0x60, + 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, 0x0a, 0x68, + 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x30, 0x19, 0x2b, 0x1c, 0x26, 0x20, + 0x21, 0x23, 0x1e, 0x26, 0x1c, 0x2b, 0x19, 0x2e, 0x16, 0x30, 0x15, 0x35, + 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x3d, 0x0f, 0x41, 0x0c, 0x42, 0x0c, 0x46, + 0x0a, 0x47, 0x0a, 0x4a, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, + 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1e, 0x64, + 0x1a, 0x66, 0x18, 0x68, 0x15, 0x6a, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6e, + 0x0e, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x77, 0x08, 0x79, + 0x07, 0x7a, 0x07, 0x7c, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x44, 0x09, 0x43, 0x09, 0x44, 0x09, 0x45, 0x09, + 0x45, 0x09, 0x46, 0x09, 0x46, 0x09, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x22, 0x1c, 0x0a, 0x29, 0x0a, 0x31, 0x09, 0x36, 0x09, 0x3a, 0x09, + 0x3b, 0x09, 0x3d, 0x09, 0x3f, 0x09, 0x40, 0x09, 0x40, 0x09, 0x41, 0x09, + 0x42, 0x09, 0x43, 0x09, 0x43, 0x0a, 0x43, 0x0a, 0x44, 0x0a, 0x44, 0x0a, + 0x44, 0x0a, 0x44, 0x0a, 0x23, 0x16, 0x32, 0x0a, 0x38, 0x0a, 0x3c, 0x0a, + 0x3f, 0x09, 0x40, 0x09, 0x41, 0x09, 0x42, 0x09, 0x43, 0x09, 0x44, 0x09, + 0x44, 0x0a, 0x44, 0x0a, 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, + 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x16, 0x3e, 0x11, 0x3f, 0x0f, 0x40, 0x0d, 0x41, 0x0d, 0x41, 0x0c, + 0x42, 0x0c, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0a, + 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x46, 0x0a, + 0x46, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x6e, 0x03, 0x3a, + 0x0f, 0x27, 0x18, 0x1e, 0x1f, 0x19, 0x24, 0x17, 0x28, 0x14, 0x2c, 0x13, + 0x2f, 0x11, 0x31, 0x11, 0x32, 0x10, 0x34, 0x0f, 0x36, 0x0f, 0x37, 0x0f, + 0x38, 0x0e, 0x39, 0x0e, 0x3a, 0x0d, 0x3a, 0x0d, 0x3b, 0x0c, 0x3c, 0x0c, + 0x23, 0x3c, 0x25, 0x22, 0x2b, 0x18, 0x30, 0x14, 0x33, 0x11, 0x36, 0x10, + 0x38, 0x0f, 0x39, 0x0e, 0x3b, 0x0e, 0x3c, 0x0d, 0x3d, 0x0d, 0x3e, 0x0c, + 0x3f, 0x0c, 0x3f, 0x0c, 0x40, 0x0c, 0x40, 0x0c, 0x40, 0x0b, 0x41, 0x0b, + 0x41, 0x0b, 0x42, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1f, 0x3c, 0x18, + 0x3c, 0x14, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x0f, 0x3f, 0x0e, 0x40, 0x0e, + 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x42, 0x0c, + 0x42, 0x0b, 0x42, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0b, 0x44, 0x0b, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x8a, 0x00, 0x59, 0x06, 0x40, 0x0d, 0x32, + 0x12, 0x2a, 0x18, 0x24, 0x1c, 0x20, 0x20, 0x1c, 0x23, 0x1b, 0x26, 0x18, + 0x28, 0x17, 0x2a, 0x16, 0x2c, 0x15, 0x2e, 0x14, 0x2f, 0x13, 0x31, 0x12, + 0x32, 0x12, 0x33, 0x12, 0x34, 0x11, 0x35, 0x10, 0x23, 0x4a, 0x24, 0x31, + 0x27, 0x25, 0x2a, 0x1e, 0x2d, 0x1a, 0x2f, 0x17, 0x32, 0x15, 0x33, 0x13, + 0x35, 0x12, 0x36, 0x11, 0x38, 0x10, 0x39, 0x10, 0x3a, 0x0f, 0x3a, 0x0f, + 0x3b, 0x0e, 0x3c, 0x0e, 0x3c, 0x0e, 0x3d, 0x0e, 0x3e, 0x0d, 0x3e, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x25, 0x3c, 0x1d, 0x3c, 0x19, 0x3c, 0x16, + 0x3d, 0x14, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, + 0x40, 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, + 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0x97, 0x00, 0x6c, 0x02, 0x52, 0x07, 0x43, 0x0b, 0x38, 0x10, 0x30, + 0x14, 0x2a, 0x18, 0x27, 0x1b, 0x23, 0x1e, 0x20, 0x20, 0x1e, 0x23, 0x1c, + 0x25, 0x1b, 0x27, 0x19, 0x28, 0x18, 0x2a, 0x17, 0x2b, 0x16, 0x2c, 0x16, + 0x2e, 0x16, 0x2f, 0x15, 0x23, 0x50, 0x23, 0x3b, 0x25, 0x2e, 0x27, 0x26, + 0x29, 0x21, 0x2c, 0x1d, 0x2e, 0x1a, 0x2f, 0x18, 0x31, 0x16, 0x33, 0x15, + 0x34, 0x14, 0x35, 0x13, 0x36, 0x12, 0x37, 0x11, 0x38, 0x11, 0x38, 0x11, + 0x39, 0x10, 0x3a, 0x10, 0x3a, 0x10, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x29, 0x3c, 0x22, 0x3b, 0x1d, 0x3b, 0x19, 0x3c, 0x17, 0x3c, 0x15, + 0x3c, 0x13, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x10, 0x3e, 0x0f, + 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0d, + 0x42, 0x0d, 0x42, 0x0d, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x9e, 0x00, 0x79, + 0x00, 0x61, 0x04, 0x50, 0x07, 0x44, 0x0b, 0x3b, 0x0f, 0x34, 0x12, 0x2f, + 0x15, 0x2b, 0x18, 0x28, 0x1a, 0x25, 0x1d, 0x23, 0x1f, 0x21, 0x21, 0x1f, + 0x22, 0x1d, 0x24, 0x1c, 0x26, 0x1b, 0x27, 0x19, 0x28, 0x19, 0x2a, 0x19, + 0x23, 0x54, 0x23, 0x42, 0x24, 0x35, 0x25, 0x2d, 0x27, 0x27, 0x29, 0x22, + 0x2b, 0x1f, 0x2d, 0x1c, 0x2e, 0x1a, 0x30, 0x19, 0x31, 0x17, 0x32, 0x16, + 0x33, 0x15, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, 0x37, 0x12, 0x37, 0x11, + 0x38, 0x11, 0x38, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2c, 0x3c, 0x25, + 0x3b, 0x20, 0x3b, 0x1d, 0x3b, 0x1a, 0x3c, 0x17, 0x3c, 0x16, 0x3c, 0x14, + 0x3c, 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3d, 0x10, + 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa2, 0x00, 0x84, 0x00, 0x6c, 0x02, 0x5a, + 0x05, 0x4e, 0x07, 0x45, 0x0b, 0x3e, 0x0e, 0x38, 0x10, 0x33, 0x13, 0x2f, + 0x16, 0x2c, 0x18, 0x29, 0x1a, 0x26, 0x1c, 0x24, 0x1d, 0x22, 0x1f, 0x21, + 0x21, 0x1f, 0x21, 0x1e, 0x23, 0x1d, 0x25, 0x1c, 0x23, 0x56, 0x23, 0x47, + 0x23, 0x3b, 0x24, 0x32, 0x26, 0x2c, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x21, + 0x2c, 0x1e, 0x2d, 0x1c, 0x2e, 0x1b, 0x30, 0x19, 0x30, 0x18, 0x32, 0x17, + 0x32, 0x16, 0x33, 0x15, 0x34, 0x14, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2e, 0x3c, 0x27, 0x3b, 0x23, 0x3b, 0x1f, + 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x15, 0x3c, 0x14, + 0x3c, 0x13, 0x3e, 0x13, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, + 0x3d, 0x10, 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x01, 0x63, 0x02, 0x57, 0x05, 0x4d, + 0x07, 0x45, 0x0a, 0x3e, 0x0d, 0x39, 0x0f, 0x35, 0x12, 0x32, 0x14, 0x2e, + 0x16, 0x2c, 0x18, 0x29, 0x19, 0x27, 0x1b, 0x26, 0x1d, 0x24, 0x1e, 0x22, + 0x20, 0x22, 0x21, 0x20, 0x23, 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, + 0x25, 0x30, 0x26, 0x2b, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x22, 0x2b, 0x20, + 0x2d, 0x1e, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x18, 0x31, 0x18, + 0x32, 0x17, 0x32, 0x16, 0x33, 0x16, 0x34, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x30, 0x3c, 0x2a, 0x3c, 0x25, 0x3a, 0x21, 0x3b, 0x1e, 0x3a, 0x1c, + 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x16, 0x3b, 0x15, 0x3c, 0x14, + 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, + 0x3d, 0x10, 0x3e, 0x0f, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa8, 0x00, 0x90, + 0x00, 0x7c, 0x00, 0x6b, 0x01, 0x5f, 0x04, 0x55, 0x05, 0x4c, 0x07, 0x45, + 0x0a, 0x40, 0x0c, 0x3c, 0x0f, 0x37, 0x10, 0x34, 0x12, 0x31, 0x15, 0x2e, + 0x16, 0x2b, 0x18, 0x2a, 0x19, 0x28, 0x1a, 0x26, 0x1d, 0x25, 0x1d, 0x23, + 0x23, 0x59, 0x23, 0x4d, 0x23, 0x43, 0x24, 0x3b, 0x24, 0x34, 0x25, 0x2f, + 0x26, 0x2b, 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2c, 0x1f, + 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x31, 0x18, + 0x32, 0x17, 0x32, 0x16, 0x17, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x56, 0x00, + 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, + 0x14, 0x2c, 0x29, 0x01, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, + 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, 0x00, + 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, + 0x11, 0x31, 0x23, 0x03, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, + 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x3e, 0x32, 0x3d, 0x2c, + 0x3c, 0x26, 0x3b, 0x23, 0x3b, 0x20, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, + 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x14, + 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaa, 0x00, 0x94, 0x00, 0x82, 0x00, 0x72, + 0x01, 0x65, 0x02, 0x5b, 0x04, 0x53, 0x06, 0x4b, 0x08, 0x45, 0x0a, 0x41, + 0x0c, 0x3d, 0x0e, 0x39, 0x0f, 0x36, 0x12, 0x33, 0x12, 0x30, 0x16, 0x2e, + 0x16, 0x2b, 0x18, 0x2b, 0x19, 0x29, 0x1a, 0x26, 0x23, 0x5a, 0x23, 0x4f, + 0x23, 0x46, 0x23, 0x3e, 0x24, 0x37, 0x25, 0x32, 0x25, 0x2e, 0x26, 0x2b, + 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1e, + 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x30, 0x18, + 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, 0x00, 0x4f, 0x00, 0x55, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x57, 0x04, 0x16, + 0x27, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, + 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, 0x00, + 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x20, + 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, + 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5d, 0x00, 0x3e, 0x33, 0x3d, 0x2d, 0x3c, 0x28, 0x3b, 0x25, + 0x3a, 0x22, 0x3b, 0x1f, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, + 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x13, + 0x3d, 0x13, 0x3f, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6b, 0x01, 0x61, + 0x03, 0x58, 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3e, + 0x0d, 0x3a, 0x0f, 0x37, 0x11, 0x35, 0x12, 0x32, 0x14, 0x30, 0x16, 0x2e, + 0x16, 0x2b, 0x18, 0x2b, 0x23, 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, + 0x24, 0x3a, 0x24, 0x35, 0x25, 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x27, 0x28, + 0x28, 0x26, 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2c, 0x1f, 0x2d, 0x1e, + 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, 0x00, 0x4a, 0x00, 0x4f, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x82, 0x00, 0x57, 0x01, 0x12, 0x17, 0x00, + 0x2f, 0x00, 0x3e, 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, + 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x48, 0x00, + 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, + 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, + 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, + 0x3e, 0x34, 0x3d, 0x2f, 0x3b, 0x2a, 0x3c, 0x26, 0x3a, 0x23, 0x3b, 0x21, + 0x3b, 0x1f, 0x3b, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x17, + 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x3c, 0x13, + 0x3e, 0x13, 0x3f, 0x13, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xac, 0x00, 0x9b, + 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x02, 0x5e, 0x04, 0x57, + 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3f, 0x0c, 0x3b, + 0x0f, 0x39, 0x0f, 0x35, 0x12, 0x34, 0x12, 0x31, 0x14, 0x30, 0x16, 0x2e, + 0x23, 0x5b, 0x23, 0x52, 0x23, 0x4a, 0x23, 0x43, 0x23, 0x3d, 0x24, 0x38, + 0x25, 0x34, 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, + 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1f, 0x2d, 0x1d, + 0x2e, 0x1d, 0x2e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, + 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x00, 0x91, 0x00, 0x78, 0x00, 0x43, 0x00, 0x10, 0x0e, 0x00, 0x23, 0x00, + 0x31, 0x00, 0x3b, 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, + 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, 0x00, + 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, + 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, + 0x00, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, + 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, + 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, 0x00, 0x3e, 0x35, 0x3e, 0x30, + 0x3b, 0x2c, 0x3c, 0x28, 0x3b, 0x25, 0x3a, 0x22, 0x3b, 0x20, 0x3b, 0x1f, + 0x3a, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, + 0x3c, 0x17, 0x3a, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x14, 0x3c, 0x13, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0x9d, 0x00, 0x8e, 0x00, 0x81, + 0x00, 0x76, 0x00, 0x6b, 0x01, 0x63, 0x02, 0x5c, 0x04, 0x56, 0x05, 0x50, + 0x07, 0x4b, 0x08, 0x47, 0x0a, 0x43, 0x0c, 0x40, 0x0c, 0x3c, 0x0f, 0x3a, + 0x0f, 0x37, 0x11, 0x35, 0x12, 0x33, 0x12, 0x30, 0x23, 0x5b, 0x23, 0x53, + 0x23, 0x4c, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3b, 0x24, 0x36, 0x25, 0x33, + 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x2a, 0x25, + 0x2a, 0x23, 0x2b, 0x22, 0x2b, 0x20, 0x2c, 0x20, 0x2d, 0x1e, 0x2d, 0x1d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, + 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, + 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x96, 0x00, 0x87, + 0x00, 0x61, 0x00, 0x36, 0x00, 0x10, 0x0a, 0x02, 0x1a, 0x00, 0x27, 0x00, + 0x32, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, + 0x4e, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, 0x00, + 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, + 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x97, 0x00, 0x8a, + 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, + 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, + 0x4b, 0x00, 0x4e, 0x00, 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2c, 0x3c, 0x29, + 0x3c, 0x26, 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x39, 0x1d, + 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, + 0x3a, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6f, + 0x01, 0x67, 0x02, 0x60, 0x03, 0x59, 0x04, 0x54, 0x05, 0x4f, 0x07, 0x4b, + 0x08, 0x47, 0x0a, 0x43, 0x0b, 0x41, 0x0c, 0x3d, 0x0e, 0x3b, 0x0f, 0x38, + 0x0f, 0x35, 0x12, 0x35, 0x23, 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, + 0x23, 0x42, 0x24, 0x3c, 0x24, 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, + 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x29, 0x25, 0x2a, 0x23, + 0x2b, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, + 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x00, 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, + 0x00, 0x2e, 0x00, 0x10, 0x08, 0x04, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, + 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x57, 0x00, + 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, + 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, + 0x49, 0x00, 0x4c, 0x00, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, + 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, + 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, + 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2d, 0x3b, 0x2a, 0x3c, 0x27, 0x3a, 0x25, + 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1c, 0x3b, 0x1c, + 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, + 0x3a, 0x16, 0x3b, 0x15, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0xa0, + 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x00, 0x6c, 0x01, 0x65, + 0x02, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4b, 0x08, 0x47, + 0x0a, 0x44, 0x0b, 0x41, 0x0c, 0x3e, 0x0d, 0x3b, 0x0f, 0x3a, 0x0f, 0x37, + 0x23, 0x5c, 0x23, 0x55, 0x23, 0x4f, 0x23, 0x49, 0x23, 0x44, 0x23, 0x3f, + 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2e, 0x26, 0x2c, + 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x22, + 0x2b, 0x22, 0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, + 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, + 0x00, 0x9b, 0x00, 0x94, 0x00, 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, + 0x00, 0x10, 0x07, 0x06, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, + 0x32, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, + 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, + 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, + 0x00, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, + 0x00, 0x1f, 0x00, 0x0c, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, + 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, 0x00, 0x3e, 0x37, 0x3e, 0x32, + 0x3c, 0x2f, 0x3b, 0x2b, 0x3c, 0x28, 0x3b, 0x26, 0x3a, 0x24, 0x3a, 0x22, + 0x3b, 0x21, 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, + 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x17, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x97, 0x00, 0x8b, + 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x61, 0x02, 0x5c, + 0x04, 0x57, 0x05, 0x52, 0x05, 0x4e, 0x07, 0x4b, 0x09, 0x47, 0x0a, 0x44, + 0x0b, 0x41, 0x0c, 0x3f, 0x0c, 0x3c, 0x0f, 0x3b, 0x23, 0x5c, 0x23, 0x56, + 0x23, 0x50, 0x23, 0x4a, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, + 0x25, 0x35, 0x25, 0x33, 0x25, 0x30, 0x26, 0x2e, 0x26, 0x2c, 0x27, 0x2a, + 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x23, 0x2b, 0x22, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, + 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x96, + 0x00, 0x87, 0x00, 0x71, 0x00, 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, + 0x06, 0x07, 0x0b, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, + 0x32, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, + 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, + 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x97, + 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, + 0x2c, 0x00, 0x31, 0x00, 0x3e, 0x37, 0x3e, 0x33, 0x3c, 0x2f, 0x3b, 0x2c, + 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x20, + 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, + 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x17, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x7a, + 0x00, 0x72, 0x00, 0x6c, 0x01, 0x65, 0x02, 0x5f, 0x04, 0x5a, 0x04, 0x56, + 0x05, 0x52, 0x06, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, + 0x0c, 0x40, 0x0c, 0x3d, 0x23, 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, + 0x23, 0x47, 0x23, 0x42, 0x23, 0x3e, 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, + 0x25, 0x32, 0x25, 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, + 0x28, 0x27, 0x29, 0x25, 0x2a, 0x25, 0x2a, 0x23, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, + 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, + 0x00, 0x64, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x22, 0x00, 0x10, 0x05, 0x08, + 0x0a, 0x02, 0x12, 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, + 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, + 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, + 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, + 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, + 0x00, 0x04, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, + 0x3e, 0x38, 0x3e, 0x33, 0x3d, 0x30, 0x3b, 0x2d, 0x3c, 0x2a, 0x3c, 0x27, + 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, + 0x39, 0x1e, 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, + 0x3c, 0x18, 0x3c, 0x17, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa5, + 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, 0x76, 0x00, 0x6f, + 0x01, 0x68, 0x02, 0x62, 0x02, 0x5e, 0x04, 0x59, 0x04, 0x55, 0x05, 0x51, + 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, 0x0c, 0x41, + 0x23, 0x5c, 0x23, 0x57, 0x23, 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, + 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x24, 0x36, 0x25, 0x34, 0x25, 0x31, + 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, + 0x28, 0x25, 0x2a, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, + 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x10, 0x04, 0x09, 0x09, 0x03, + 0x0f, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, + 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, + 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, + 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, + 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, + 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x3e, 0x38, 0x3e, 0x34, + 0x3e, 0x31, 0x3b, 0x2e, 0x3b, 0x2b, 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, + 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1d, + 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x19, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x91, + 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, + 0x02, 0x61, 0x03, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x05, 0x50, 0x07, 0x4e, + 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x42, 0x23, 0x5c, 0x23, 0x58, + 0x23, 0x53, 0x23, 0x4d, 0x23, 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, + 0x24, 0x3b, 0x24, 0x38, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, + 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9d, 0x00, 0x9b, + 0x00, 0x93, 0x00, 0x86, 0x00, 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, + 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x10, 0x04, 0x09, 0x08, 0x04, 0x0c, 0x00, + 0x14, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, + 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9e, 0x00, 0x9b, + 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, + 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, + 0x09, 0x00, 0x11, 0x00, 0x3e, 0x38, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2e, + 0x3b, 0x2c, 0x3c, 0x2a, 0x3c, 0x27, 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x23, + 0x3b, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x3a, 0x1f, 0x39, 0x1d, 0x3a, 0x1c, + 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x83, + 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x01, 0x63, 0x02, 0x5f, + 0x04, 0x5b, 0x04, 0x56, 0x05, 0x54, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, + 0x09, 0x47, 0x0a, 0x46, 0x23, 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, + 0x23, 0x4a, 0x23, 0x46, 0x23, 0x43, 0x23, 0x3f, 0x24, 0x3c, 0x24, 0x39, + 0x24, 0x36, 0x25, 0x34, 0x25, 0x32, 0x25, 0x30, 0x26, 0x2f, 0x26, 0x2d, + 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, + 0x00, 0x7d, 0x00, 0x6d, 0x00, 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, + 0x00, 0x1d, 0x00, 0x10, 0x04, 0x0a, 0x07, 0x05, 0x0b, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, + 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, + 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, + 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, + 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, + 0x3e, 0x39, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, + 0x3c, 0x28, 0x3b, 0x27, 0x39, 0x24, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, + 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, + 0x3b, 0x1a, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, + 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xb0, 0x00, 0xa7, + 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, 0x7e, 0x00, 0x77, + 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x02, 0x5d, 0x04, 0x5a, + 0x04, 0x55, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, + 0x23, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, + 0x23, 0x44, 0x23, 0x40, 0x24, 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, + 0x25, 0x33, 0x25, 0x32, 0x25, 0x2f, 0x26, 0x2f, 0x26, 0x2c, 0x27, 0x2c, + 0x27, 0x2a, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, + 0x00, 0x65, 0x00, 0x56, 0x00, 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, + 0x00, 0x10, 0x03, 0x0a, 0x07, 0x06, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, + 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, + 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, + 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x32, 0x59, 0x2a, 0x5b, + 0x28, 0x5c, 0x27, 0x5d, 0x27, 0x5d, 0x26, 0x5e, 0x26, 0x5e, 0x26, 0x5e, + 0x26, 0x5e, 0x26, 0x5e, 0x25, 0x5e, 0x25, 0x5e, 0x25, 0x5f, 0x25, 0x5f, + 0x25, 0x5f, 0x25, 0x5f, 0x25, 0x5f, 0x24, 0x5f, 0x24, 0x5f, 0x24, 0x60, + 0x32, 0x3c, 0x25, 0x4a, 0x24, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, 0x58, + 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, 0x5b, + 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5d, + 0x23, 0x5d, 0x23, 0x5d, 0x00, 0x6c, 0x0e, 0x60, 0x14, 0x60, 0x18, 0x60, + 0x1b, 0x60, 0x1d, 0x60, 0x1d, 0x60, 0x1e, 0x60, 0x1f, 0x60, 0x20, 0x60, + 0x20, 0x60, 0x20, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, + 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x97, 0x00, 0x90, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, + 0x00, 0x50, 0x00, 0x42, 0x00, 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, + 0x03, 0x0b, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, + 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9d, + 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, + 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, + 0x00, 0x16, 0x00, 0x0d, 0x38, 0x4f, 0x31, 0x53, 0x2e, 0x56, 0x2c, 0x57, + 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x38, 0x22, 0x2b, 0x31, + 0x27, 0x3b, 0x25, 0x42, 0x24, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, 0x4f, + 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, 0x56, + 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, + 0x00, 0x92, 0x01, 0x79, 0x07, 0x6f, 0x0c, 0x6b, 0x0f, 0x68, 0x12, 0x67, + 0x14, 0x66, 0x16, 0x65, 0x17, 0x64, 0x18, 0x64, 0x19, 0x63, 0x1a, 0x63, + 0x1b, 0x63, 0x1b, 0x63, 0x1c, 0x63, 0x1c, 0x62, 0x1d, 0x62, 0x1d, 0x62, + 0x1d, 0x62, 0x1e, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, + 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, + 0x00, 0x3e, 0x00, 0x31, 0x00, 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x03, 0x0b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, + 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, + 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, + 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, + 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, + 0x3a, 0x4b, 0x34, 0x4f, 0x31, 0x52, 0x2f, 0x54, 0x2d, 0x56, 0x2c, 0x57, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, + 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, + 0x27, 0x5c, 0x27, 0x5c, 0x3c, 0x18, 0x30, 0x25, 0x2a, 0x2e, 0x27, 0x35, + 0x25, 0x3b, 0x24, 0x3f, 0x24, 0x43, 0x24, 0x46, 0x23, 0x48, 0x23, 0x4a, + 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, 0x52, + 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x54, 0x00, 0xa1, 0x00, 0x88, + 0x03, 0x7b, 0x06, 0x74, 0x09, 0x70, 0x0c, 0x6d, 0x0e, 0x6b, 0x10, 0x6a, + 0x11, 0x69, 0x13, 0x68, 0x14, 0x67, 0x15, 0x66, 0x16, 0x66, 0x17, 0x65, + 0x17, 0x65, 0x18, 0x65, 0x19, 0x65, 0x19, 0x64, 0x1a, 0x64, 0x1a, 0x64, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, + 0x00, 0x77, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, + 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, + 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, + 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, + 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, + 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x3b, 0x48, 0x36, 0x4c, + 0x33, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2d, 0x55, 0x2c, 0x57, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, + 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5b, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, + 0x3f, 0x14, 0x33, 0x1e, 0x2d, 0x26, 0x29, 0x2d, 0x27, 0x32, 0x26, 0x36, + 0x25, 0x3b, 0x24, 0x3e, 0x24, 0x41, 0x24, 0x43, 0x23, 0x45, 0x23, 0x47, + 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, 0x4f, + 0x23, 0x50, 0x23, 0x50, 0x00, 0xa7, 0x00, 0x92, 0x01, 0x84, 0x03, 0x7d, + 0x05, 0x77, 0x08, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6d, 0x0f, 0x6b, + 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x69, 0x13, 0x68, 0x14, 0x68, 0x15, 0x67, + 0x15, 0x66, 0x16, 0x66, 0x17, 0x66, 0x17, 0x66, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x31, 0x23, 0x03, + 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, + 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, + 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x31, 0x00, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, + 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, + 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x4f, + 0x31, 0x51, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, 0x2c, 0x56, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x2a, 0x59, 0x40, 0x11, 0x36, 0x1a, + 0x2f, 0x21, 0x2c, 0x27, 0x29, 0x2c, 0x27, 0x30, 0x26, 0x34, 0x25, 0x37, + 0x25, 0x3a, 0x24, 0x3d, 0x24, 0x40, 0x24, 0x42, 0x24, 0x44, 0x23, 0x45, + 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4c, + 0x00, 0xaa, 0x00, 0x98, 0x00, 0x8c, 0x02, 0x83, 0x03, 0x7d, 0x05, 0x79, + 0x07, 0x76, 0x09, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, 0x0e, 0x6d, + 0x0f, 0x6c, 0x10, 0x6b, 0x11, 0x6a, 0x12, 0x6a, 0x13, 0x69, 0x13, 0x68, + 0x14, 0x68, 0x15, 0x68, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x1f, 0x00, 0x3c, 0x00, + 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, + 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x00, 0x20, + 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, + 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, + 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x46, 0x39, 0x4a, 0x36, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x50, + 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, + 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x2a, 0x57, 0x2a, 0x57, 0x41, 0x10, 0x38, 0x17, 0x32, 0x1d, 0x2e, 0x22, + 0x2b, 0x27, 0x29, 0x2b, 0x27, 0x2f, 0x26, 0x32, 0x25, 0x35, 0x25, 0x38, + 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, 0x23, 0x44, + 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x48, 0x00, 0xac, 0x00, 0x9d, + 0x00, 0x91, 0x01, 0x89, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7a, 0x07, 0x77, + 0x08, 0x75, 0x09, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0d, 0x6e, 0x0e, 0x6e, + 0x0e, 0x6c, 0x0f, 0x6c, 0x10, 0x6b, 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x6a, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, + 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, + 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, + 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, + 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, + 0x37, 0x4a, 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, + 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, + 0x42, 0x0f, 0x39, 0x15, 0x33, 0x1a, 0x2f, 0x1f, 0x2d, 0x24, 0x2a, 0x27, + 0x29, 0x2b, 0x27, 0x2e, 0x26, 0x31, 0x26, 0x34, 0x25, 0x36, 0x25, 0x38, + 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x24, 0x40, 0x24, 0x41, 0x24, 0x43, + 0x23, 0x44, 0x23, 0x45, 0x00, 0xae, 0x00, 0xa1, 0x00, 0x96, 0x00, 0x8d, + 0x01, 0x87, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x78, 0x07, 0x76, + 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0c, 0x6f, 0x0d, 0x6e, + 0x0e, 0x6d, 0x0f, 0x6c, 0x10, 0x6c, 0x10, 0x6b, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, + 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, + 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, + 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, + 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, + 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4a, + 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x55, + 0x2c, 0x57, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x43, 0x0e, 0x3b, 0x13, + 0x35, 0x18, 0x31, 0x1c, 0x2e, 0x21, 0x2c, 0x24, 0x2a, 0x28, 0x28, 0x2b, + 0x27, 0x2e, 0x27, 0x30, 0x26, 0x33, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, + 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, + 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x99, 0x00, 0x91, 0x00, 0x8b, 0x02, 0x86, + 0x02, 0x81, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x79, 0x07, 0x77, 0x08, 0x75, + 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, + 0x0e, 0x6e, 0x0e, 0x6d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, + 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, + 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, + 0x28, 0x00, 0x02, 0x00, 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, + 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, + 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4b, 0x34, 0x4e, + 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, + 0x2c, 0x56, 0x2b, 0x57, 0x44, 0x0e, 0x3c, 0x12, 0x36, 0x16, 0x33, 0x1a, + 0x30, 0x1e, 0x2d, 0x22, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x27, 0x2d, + 0x27, 0x30, 0x26, 0x32, 0x26, 0x34, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, + 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x00, 0xb0, 0x00, 0xa5, + 0x00, 0x9d, 0x00, 0x95, 0x00, 0x8e, 0x01, 0x89, 0x02, 0x85, 0x03, 0x81, + 0x04, 0x7e, 0x05, 0x7c, 0x06, 0x7a, 0x07, 0x78, 0x07, 0x76, 0x09, 0x75, + 0x09, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x70, 0x0d, 0x6e, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, + 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, + 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, + 0x00, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, + 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, + 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, + 0x44, 0x0d, 0x3d, 0x11, 0x38, 0x15, 0x34, 0x19, 0x31, 0x1c, 0x2e, 0x20, + 0x2d, 0x23, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2f, + 0x26, 0x31, 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, + 0x24, 0x3a, 0x24, 0x3c, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, + 0x00, 0x91, 0x00, 0x8c, 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7e, + 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x74, + 0x0a, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, + 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, + 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, + 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, + 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, + 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0d, 0x3e, 0x10, + 0x39, 0x14, 0x35, 0x17, 0x32, 0x1b, 0x30, 0x1e, 0x2e, 0x21, 0x2c, 0x23, + 0x2b, 0x26, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2e, 0x26, 0x30, + 0x26, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, + 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, + 0x01, 0x8b, 0x02, 0x87, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, + 0x06, 0x7b, 0x06, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x09, 0x74, + 0x0a, 0x73, 0x0b, 0x72, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, + 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, + 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, + 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, + 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, + 0x35, 0x4b, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0c, 0x3f, 0x10, 0x3a, 0x13, 0x36, 0x16, + 0x33, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2d, 0x21, 0x2b, 0x24, 0x2a, 0x26, + 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x26, 0x30, 0x26, 0x31, + 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x37, 0x00, 0xb2, 0x00, 0xaa, + 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, 0x01, 0x89, + 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x06, 0x7b, + 0x06, 0x79, 0x07, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x75, 0x09, 0x74, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, + 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x07, 0x00, + 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, + 0x2b, 0x00, 0x17, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, + 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x46, + 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, + 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, + 0x45, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x37, 0x15, 0x34, 0x18, 0x32, 0x1b, + 0x30, 0x1d, 0x2e, 0x20, 0x2d, 0x22, 0x2b, 0x24, 0x2a, 0x26, 0x2a, 0x28, + 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x27, 0x2f, 0x26, 0x31, 0x26, 0x32, + 0x25, 0x33, 0x25, 0x35, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, + 0x00, 0x98, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x01, 0x88, 0x02, 0x85, + 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, + 0x07, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, + 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, + 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x03, 0x00, 0x0c, 0x00, + 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, + 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, + 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, 0x39, 0x46, + 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x33, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, + 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0f, + 0x3b, 0x11, 0x38, 0x14, 0x35, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, + 0x2d, 0x21, 0x2c, 0x22, 0x2b, 0x25, 0x2a, 0x26, 0x29, 0x28, 0x28, 0x2a, + 0x28, 0x2c, 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x30, 0x26, 0x32, 0x25, 0x33, + 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, + 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8a, 0x02, 0x88, 0x02, 0x85, 0x02, 0x83, + 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, + 0x07, 0x78, 0x07, 0x77, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, + 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, + 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, + 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, + 0x00, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, + 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x44, 0x3a, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, + 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, + 0x36, 0x16, 0x33, 0x18, 0x31, 0x1b, 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x21, + 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, + 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x2f, 0x26, 0x31, 0x00, 0xb3, 0x00, 0xac, + 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, + 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x82, 0x03, 0x81, + 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x79, 0x07, 0x79, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, + 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, + 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, + 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, + 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x44, + 0x3b, 0x46, 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2f, 0x53, + 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x18, + 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2d, 0x22, 0x2b, 0x23, + 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2d, + 0x27, 0x2f, 0x26, 0x2f, 0x00, 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, + 0x00, 0x9d, 0x00, 0x99, 0x00, 0x95, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8b, + 0x02, 0x88, 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, + 0x05, 0x7e, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x7a, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, + 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, + 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, + 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, + 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, + 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, + 0x39, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x45, 0x0b, 0x41, 0x0e, + 0x3d, 0x10, 0x3a, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x31, 0x1b, + 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x20, 0x2c, 0x22, 0x2b, 0x24, 0x2a, 0x25, + 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x27, 0x2e, + 0x00, 0xb3, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, + 0x00, 0x96, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8d, 0x01, 0x8a, 0x02, 0x88, + 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7e, + 0x05, 0x7c, 0x06, 0x7c, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, + 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, + 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, + 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, + 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, + 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x47, + 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, + 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x45, 0x0b, 0x41, 0x0e, 0x3e, 0x10, 0x3a, 0x11, + 0x38, 0x14, 0x35, 0x16, 0x33, 0x18, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, + 0x2e, 0x20, 0x2d, 0x21, 0x2b, 0x22, 0x2b, 0x24, 0x2a, 0x25, 0x2a, 0x27, + 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x00, 0xb3, 0x00, 0xae, + 0x00, 0xa9, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, + 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x86, + 0x02, 0x84, 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x05, 0x7c, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, + 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, + 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, + 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, + 0x09, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, + 0x3d, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x4a, 0x36, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x46, 0x0b, 0x42, 0x0d, 0x3e, 0x10, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x16, + 0x34, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x30, 0x1d, 0x2e, 0x1e, 0x2d, 0x20, + 0x2d, 0x22, 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x25, 0x2a, 0x28, 0x28, 0x28, + 0x28, 0x2a, 0x28, 0x2c, 0x00, 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, + 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, + 0x00, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x83, + 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, + 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, + 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, + 0x00, 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, + 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, + 0x00, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, + 0x39, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4a, + 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, 0x31, 0x4f, + 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x46, 0x0b, 0x42, 0x0d, + 0x3e, 0x0f, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x16, 0x33, 0x18, + 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2e, 0x20, 0x2d, 0x20, 0x2c, 0x22, + 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x26, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, + 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, + 0x00, 0x9a, 0x00, 0x97, 0x00, 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, + 0x01, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x02, 0x85, 0x02, 0x83, 0x03, 0x82, + 0x03, 0x80, 0x04, 0x7f, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, + 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, 0x00, + 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x16, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, + 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, + 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, + 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, + 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, + 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, + 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, + 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, + 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, + 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, + 0x00, 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, + 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, + 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, + 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, + 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, + 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, + 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, + 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, + 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, + 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, + 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, + 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, + 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, + 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x4c, 0x04, 0x11, 0x09, 0x01, 0x11, 0x00, 0x17, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1a, + 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, + 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x21, + 0x00, 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x87, 0x00, 0x39, 0x01, 0x0f, 0x07, 0x02, 0x0c, 0x00, 0x12, 0x00, 0x17, + 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x9f, 0x00, 0x5f, 0x00, + 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, + 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0b, 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, + 0x00, 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, + 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x87, 0x00, + 0x32, 0x00, 0x19, 0x05, 0x0b, 0x09, 0x03, 0x0b, 0x00, 0x0e, 0x00, 0x12, + 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, + 0x00, 0x1c, 0x00, 0x1c, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, + 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, + 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, + 0x00, 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, + 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, + 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xcd, 0x00, 0xaf, 0x00, 0x6e, 0x00, 0x31, 0x00, + 0x1e, 0x04, 0x12, 0x07, 0x0a, 0x09, 0x04, 0x0a, 0x00, 0x0b, 0x00, 0x0f, + 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, + 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, + 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, + 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x2c, 0x00, 0x28, + 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, + 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, + 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, 0x00, 0x30, 0x00, 0x22, 0x03, + 0x17, 0x06, 0x0f, 0x07, 0x0a, 0x09, 0x05, 0x0a, 0x02, 0x0b, 0x00, 0x0c, + 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0xd7, 0x00, 0xc9, 0x00, + 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, + 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x09, + 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, + 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, + 0x00, 0x18, 0x00, 0x19, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, + 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, + 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0xcb, 0x00, + 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, 0x00, 0x24, 0x02, 0x1b, 0x05, + 0x14, 0x06, 0x0e, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x03, 0x0b, 0x00, 0x0b, + 0x00, 0x0d, 0x00, 0x0f, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, + 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, + 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, + 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, + 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xb8, 0x00, 0x96, 0x00, + 0x71, 0x00, 0x4e, 0x00, 0x30, 0x00, 0x26, 0x02, 0x1e, 0x04, 0x17, 0x06, + 0x12, 0x07, 0x0d, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x04, 0x0a, 0x01, 0x0b, + 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, + 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, + 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, + 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, + 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, + 0x00, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, 0x00, 0x87, 0x00, 0x67, 0x00, + 0x4a, 0x00, 0x30, 0x00, 0x27, 0x02, 0x20, 0x03, 0x1a, 0x05, 0x14, 0x06, + 0x10, 0x07, 0x0c, 0x08, 0x09, 0x09, 0x07, 0x0a, 0xdc, 0x00, 0xd7, 0x00, + 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, + 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, + 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, + 0x00, 0x10, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, + 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, + 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, 0x00, 0x60, 0x00, 0x46, 0x00, + 0x30, 0x00, 0x28, 0x01, 0x21, 0x03, 0x1c, 0x04, 0x17, 0x06, 0x13, 0x07, + 0x0f, 0x08, 0x0c, 0x08, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, + 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, + 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, + 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, + 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xba, 0x00, + 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x44, 0x00, 0x2f, 0x00, + 0x29, 0x01, 0x23, 0x03, 0x1e, 0x04, 0x19, 0x05, 0x15, 0x06, 0x11, 0x07, + 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, + 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, + 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, + 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, + 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0xad, 0x00, 0x98, 0x00, + 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, 0x00, 0x2f, 0x00, 0x29, 0x01, + 0x24, 0x02, 0x1f, 0x04, 0x1b, 0x05, 0x17, 0x06, 0xdd, 0x00, 0xdb, 0x00, + 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, + 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, + 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, + 0x00, 0x07, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, + 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, + 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, + 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, 0x00, 0x8e, 0x00, 0x79, 0x00, + 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x25, 0x02, + 0x20, 0x03, 0x1c, 0x04, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, + 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, + 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, + 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, + 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x60, 0x00, + 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x26, 0x02, 0x21, 0x03, + 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, + 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, + 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, + 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb1, 0x00, + 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, + 0x3d, 0x00, 0x2f, 0x00, 0x2b, 0x01, 0x26, 0x02, 0xde, 0x00, 0xdc, 0x00, + 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, + 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, + 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, + 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, 0x00, 0xa8, 0x00, 0x98, 0x00, + 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, 0x00, 0x4a, 0x00, 0x3c, 0x00, + 0x2f, 0x00, 0x2b, 0x01, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, + 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, + 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, + 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, + 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd0, 0x00, + 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, 0x00, 0x91, 0x00, 0x82, 0x00, + 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, 0x00, 0x3b, 0x00, 0x2f, 0x00, + 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, + 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, + 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, 0x00, + 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xde, 0x00, 0x4c, 0x04, 0x87, 0x00, 0xbc, 0x00, 0xcd, 0x00, + 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x76, 0x00, + 0x9b, 0x00, 0xac, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xde, 0x00, 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, + 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, + 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, 0x00, + 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, + 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0x11, 0x09, 0x39, 0x01, 0x87, 0x00, 0xaf, 0x00, 0xc1, 0x00, 0xcb, 0x00, + 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, + 0xab, 0x00, 0xc0, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, + 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0x23, 0x03, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, + 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, + 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, 0x00, + 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, + 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x01, 0x11, 0x0f, 0x07, + 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, 0x00, 0xb8, 0x00, 0xc1, 0x00, + 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd6, 0x00, + 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9b, 0x00, 0x75, 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, + 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, + 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x03, 0x09, 0x1f, 0x00, + 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, + 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, + 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, + 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, + 0xca, 0x00, 0xcd, 0x00, 0x00, 0x17, 0x02, 0x0c, 0x19, 0x05, 0x31, 0x00, + 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, + 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x96, 0x00, + 0x5b, 0x00, 0x13, 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, + 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, + 0xca, 0x00, 0xcd, 0x00, 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, + 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, + 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, + 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, + 0x00, 0x1b, 0x00, 0x12, 0x0b, 0x09, 0x1e, 0x04, 0x30, 0x00, 0x55, 0x00, + 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb5, 0x00, + 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, + 0x03, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, + 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, + 0x00, 0x18, 0x00, 0x0a, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, + 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, + 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, + 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1c, 0x00, 0x17, + 0x03, 0x0b, 0x12, 0x07, 0x22, 0x03, 0x30, 0x00, 0x4e, 0x00, 0x67, 0x00, + 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, + 0xb6, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xcd, 0x00, 0xc0, 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, + 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, + 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1a, 0x00, 0x11, + 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, + 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, + 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, + 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, + 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x0e, 0x0a, 0x09, + 0x17, 0x06, 0x24, 0x02, 0x30, 0x00, 0x4a, 0x00, 0x60, 0x00, 0x72, 0x00, + 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, + 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, + 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, + 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, + 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, + 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, 0x00, + 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, + 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x04, 0x0a, 0x0f, 0x07, 0x1b, 0x05, + 0x26, 0x02, 0x30, 0x00, 0x46, 0x00, 0x5a, 0x00, 0x6b, 0x00, 0x79, 0x00, + 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, + 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, + 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, + 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, + 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, + 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, + 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x15, 0x00, 0x0b, 0x0a, 0x09, 0x14, 0x06, 0x1e, 0x04, 0x27, 0x02, + 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x7e, 0x00, + 0x88, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, + 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, + 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1d, 0x00, 0x1a, + 0x00, 0x0f, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, + 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, + 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, + 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0f, + 0x05, 0x0a, 0x0e, 0x08, 0x17, 0x06, 0x20, 0x03, 0x28, 0x01, 0x2f, 0x00, + 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x82, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, + 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, + 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, + 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, + 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, + 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x02, 0x0b, 0x09, 0x09, + 0x12, 0x07, 0x1a, 0x05, 0x21, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x40, 0x00, + 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, + 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, + 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, + 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, + 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, + 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1f, 0x00, 0x1d, + 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x06, 0x0a, 0x0d, 0x08, 0x14, 0x06, + 0x1c, 0x04, 0x23, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x3e, 0x00, 0x4c, 0x00, + 0x59, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, + 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, + 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, + 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, + 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x16, + 0x00, 0x0f, 0x03, 0x0b, 0x09, 0x09, 0x10, 0x07, 0x17, 0x06, 0x1e, 0x04, + 0x24, 0x02, 0x2a, 0x01, 0x2f, 0x00, 0x3d, 0x00, 0x4a, 0x00, 0x56, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, + 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, + 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, + 0x22, 0x00, 0x30, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, + 0x00, 0x05, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, + 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x11, 0x00, 0x0b, + 0x06, 0x0a, 0x0c, 0x08, 0x13, 0x07, 0x19, 0x05, 0x1f, 0x04, 0x25, 0x02, + 0x2a, 0x01, 0x2f, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, + 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, + 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, + 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, + 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, + 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1e, + 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x04, 0x0a, 0x09, 0x09, + 0x0f, 0x08, 0x15, 0x06, 0x1b, 0x05, 0x20, 0x03, 0x26, 0x02, 0x2b, 0x01, + 0x2f, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, + 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, + 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1d, + 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, + 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, + 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, + 0x00, 0x15, 0x00, 0x0f, 0x01, 0x0b, 0x07, 0x0a, 0x0c, 0x08, 0x11, 0x07, + 0x17, 0x06, 0x1c, 0x04, 0x21, 0x03, 0x26, 0x02, 0x2b, 0x01, 0x2f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, + 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, + 0x00, 0x0e, 0x00, 0x05, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, + 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, + 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, + 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, + 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, + 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x5f, 0x00, + 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, + 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, + 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, + 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, + 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x09, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, + 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, + 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, 0x00, + 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, + 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, + 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, + 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, + 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, + 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, + 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, + 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, + 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, + 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x11, 0x06, 0x00, 0x25, 0x00, + 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, + 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, 0x00, + 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, + 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, + 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, + 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, + 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, + 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, + 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, + 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, + 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, + 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, + 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, + 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, + 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, + 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, + 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, + 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, + 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x0b, 0x00, 0x1c, 0x00, + 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, + 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, + 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, + 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, + 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, + 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, + 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, + 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, + 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, + 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, + 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, + 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, + 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, + 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x06, 0x00, + 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, + 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, + 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, + 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, + 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, + 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, + 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, + 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, + 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, + 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, + 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, + 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, + 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, + 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, + 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, + 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, + 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, + 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, + 0x00, 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1a, 0x00, + 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0e, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, + 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, + 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, + 0x00, 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, + 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, + 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, + 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, + 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, + 0x00, 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, + 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, + 0x00, 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, + 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, + 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, + 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, + 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, + 0x00, 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, + 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, + 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, + 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, + 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, + 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, + 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, + 0x00, 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xde, 0x00, 0xa6, 0x00, 0x76, 0x00, 0x9b, 0x00, 0xac, + 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x4c, 0x00, 0x87, + 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, + 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, + 0x00, 0xde, 0x00, 0xde, 0x01, 0x61, 0x00, 0x9f, 0x00, 0xc5, 0x00, 0xd2, + 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, + 0x00, 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, + 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, 0xab, 0x00, 0xc0, + 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, + 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x01, 0x39, 0x00, 0x87, 0x00, 0xaf, + 0x00, 0xc1, 0x00, 0xcb, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, + 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, + 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, + 0x00, 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, + 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x9b, 0x00, 0x75, + 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, 0xae, 0x00, 0xb9, + 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, + 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x11, 0x01, 0x07, 0x0f, 0x00, 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, + 0x00, 0xb8, 0x00, 0xc1, 0x00, 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, + 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x09, 0x03, 0x00, 0x1f, + 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, + 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, + 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, + 0x00, 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, + 0x00, 0xca, 0x00, 0xcd, 0x00, 0xac, 0x00, 0x96, 0x00, 0x5b, 0x00, 0x13, + 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, 0xa5, 0x00, 0xb0, + 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x0c, 0x02, + 0x05, 0x19, 0x00, 0x31, 0x00, 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, + 0x00, 0xb2, 0x00, 0xba, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, + 0x00, 0xce, 0x00, 0xd0, 0x13, 0x00, 0x01, 0x04, 0x00, 0x32, 0x00, 0x5f, + 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, + 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, + 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, + 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, 0x03, 0x00, 0x2f, + 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, + 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x09, 0x0b, 0x04, 0x1e, + 0x00, 0x30, 0x00, 0x55, 0x00, 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, + 0x00, 0xad, 0x00, 0xb5, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, + 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, + 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, + 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, + 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xcd, 0x00, 0xc0, + 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x27, 0x00, 0x47, + 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, + 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x03, 0x07, 0x12, 0x03, 0x22, 0x00, 0x30, + 0x00, 0x4e, 0x00, 0x67, 0x00, 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, + 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xbb, 0x1a, 0x00, 0x11, 0x00, + 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, + 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, + 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, + 0x00, 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, + 0x00, 0x99, 0x00, 0xa0, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, + 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x55, + 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, + 0x0e, 0x00, 0x09, 0x0a, 0x06, 0x17, 0x02, 0x24, 0x00, 0x30, 0x00, 0x4a, + 0x00, 0x60, 0x00, 0x72, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, + 0x00, 0xa8, 0x00, 0xae, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x00, 0x00, 0x14, + 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, + 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, + 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, + 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, + 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, + 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x0a, 0x04, + 0x07, 0x0f, 0x05, 0x1b, 0x02, 0x26, 0x00, 0x30, 0x00, 0x46, 0x00, 0x5a, + 0x00, 0x6b, 0x00, 0x79, 0x00, 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, + 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x36, + 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, + 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, + 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0xdb, 0x00, 0xd4, + 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, + 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, + 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x0b, 0x00, 0x09, 0x0a, 0x06, 0x14, + 0x04, 0x1e, 0x02, 0x27, 0x00, 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, + 0x00, 0x72, 0x00, 0x7e, 0x00, 0x88, 0x00, 0x91, 0x1d, 0x00, 0x1a, 0x00, + 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, + 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, + 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, + 0x00, 0x5c, 0x00, 0x68, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, + 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, + 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, + 0x17, 0x00, 0x0f, 0x00, 0x0a, 0x05, 0x08, 0x0e, 0x06, 0x17, 0x03, 0x20, + 0x01, 0x28, 0x00, 0x2f, 0x00, 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, + 0x00, 0x78, 0x00, 0x82, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x00, + 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, + 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, + 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, + 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x15, + 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, + 0x0b, 0x02, 0x09, 0x09, 0x07, 0x12, 0x05, 0x1a, 0x03, 0x21, 0x01, 0x29, + 0x00, 0x2f, 0x00, 0x40, 0x00, 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, + 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x04, 0x00, 0x13, + 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, + 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0xdd, 0x00, 0xd9, + 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, + 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, + 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x0a, 0x06, + 0x08, 0x0d, 0x06, 0x14, 0x04, 0x1c, 0x03, 0x23, 0x01, 0x29, 0x00, 0x2f, + 0x00, 0x3e, 0x00, 0x4c, 0x00, 0x59, 0x00, 0x64, 0x1e, 0x00, 0x1c, 0x00, + 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x29, + 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, + 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x00, 0x22, 0x00, 0x30, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, + 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, 0x55, 0x00, 0x3e, + 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1b, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x09, 0x09, 0x07, 0x10, + 0x06, 0x17, 0x04, 0x1e, 0x02, 0x24, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3d, + 0x00, 0x4a, 0x00, 0x56, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, + 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, + 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, + 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, + 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, + 0x11, 0x00, 0x0b, 0x00, 0x0a, 0x06, 0x08, 0x0c, 0x07, 0x13, 0x05, 0x19, + 0x04, 0x1f, 0x02, 0x25, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x48, + 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x01, + 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, + 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xdd, 0x00, 0xdb, + 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, + 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, + 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, + 0x0a, 0x04, 0x09, 0x09, 0x08, 0x0f, 0x06, 0x15, 0x05, 0x1b, 0x03, 0x20, + 0x02, 0x26, 0x01, 0x2b, 0x00, 0x2f, 0x00, 0x3b, 0x1f, 0x00, 0x1d, 0x00, + 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x13, + 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, + 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, + 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, 0x7b, 0x00, 0x68, + 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0b, 0x01, 0x0a, 0x07, + 0x08, 0x0c, 0x07, 0x11, 0x06, 0x17, 0x04, 0x1c, 0x03, 0x21, 0x02, 0x26, + 0x01, 0x2b, 0x00, 0x2f, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, + 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, + 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x04, 0x4c, 0x09, 0x11, + 0x11, 0x01, 0x17, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x01, 0x61, 0x03, 0x23, + 0x09, 0x03, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, + 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x87, 0x01, 0x39, 0x07, 0x0f, 0x0c, 0x02, + 0x12, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x21, 0x00, + 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, + 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, + 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0xbc, 0x00, 0x87, 0x00, 0x32, 0x05, 0x19, 0x09, 0x0b, 0x0b, 0x03, + 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, + 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, 0x00, + 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, + 0x00, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, + 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xcd, 0x00, 0xaf, + 0x00, 0x6e, 0x00, 0x31, 0x04, 0x1e, 0x07, 0x12, 0x09, 0x0a, 0x0a, 0x04, + 0x0b, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, + 0x18, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, + 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xd2, 0x00, 0xbc, + 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, + 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, + 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, + 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, + 0x00, 0x30, 0x03, 0x22, 0x06, 0x17, 0x07, 0x0f, 0x09, 0x0a, 0x0a, 0x05, + 0x0b, 0x02, 0x0c, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x28, 0x00, + 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, + 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, + 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, + 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, + 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, + 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x00, 0xd8, 0x00, 0xcb, 0x00, 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, + 0x02, 0x24, 0x05, 0x1b, 0x06, 0x14, 0x08, 0x0e, 0x09, 0x09, 0x0a, 0x06, + 0x0b, 0x03, 0x0b, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, 0x00, + 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, + 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x00, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, + 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, + 0x00, 0x06, 0x00, 0x01, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xda, 0x00, 0xd1, + 0x00, 0xb8, 0x00, 0x96, 0x00, 0x71, 0x00, 0x4e, 0x00, 0x30, 0x02, 0x26, + 0x04, 0x1e, 0x06, 0x17, 0x07, 0x12, 0x08, 0x0d, 0x09, 0x09, 0x0a, 0x06, + 0x0a, 0x04, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xdb, 0x00, 0xd5, + 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, + 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, + 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, + 0x00, 0x87, 0x00, 0x67, 0x00, 0x4a, 0x00, 0x30, 0x02, 0x27, 0x03, 0x20, + 0x05, 0x1a, 0x06, 0x14, 0x07, 0x10, 0x08, 0x0c, 0x09, 0x09, 0x0a, 0x07, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, + 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, + 0x13, 0x00, 0x14, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, + 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, + 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, + 0x00, 0x60, 0x00, 0x46, 0x00, 0x30, 0x01, 0x28, 0x03, 0x21, 0x04, 0x1c, + 0x06, 0x17, 0x07, 0x13, 0x08, 0x0f, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, + 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, + 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, + 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, + 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdc, 0x00, 0xd8, + 0x00, 0xcc, 0x00, 0xba, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, + 0x00, 0x44, 0x00, 0x2f, 0x01, 0x29, 0x03, 0x23, 0x04, 0x1e, 0x05, 0x19, + 0x06, 0x15, 0x07, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, + 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, + 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdd, 0x00, 0xda, + 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, + 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, + 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, + 0x00, 0xad, 0x00, 0x98, 0x00, 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, + 0x00, 0x2f, 0x01, 0x29, 0x02, 0x24, 0x04, 0x1f, 0x05, 0x1b, 0x06, 0x17, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, + 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, + 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, + 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, + 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, + 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, + 0x00, 0x8e, 0x00, 0x79, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, + 0x01, 0x2a, 0x02, 0x25, 0x03, 0x20, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, + 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, + 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, + 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xdd, 0x00, 0xdb, + 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, + 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x01, 0x2a, + 0x02, 0x26, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, + 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, + 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, + 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, + 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, + 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3d, 0x00, 0x2f, 0x01, 0x2b, 0x02, 0x26, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, + 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, + 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, + 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, + 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, + 0x00, 0xa8, 0x00, 0x98, 0x00, 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, + 0x00, 0x4a, 0x00, 0x3c, 0x00, 0x2f, 0x01, 0x2b, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, + 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, + 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, + 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, + 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, + 0x00, 0x91, 0x00, 0x82, 0x00, 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, + 0x00, 0x3b, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, + 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, + 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, + 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, + 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, + 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x03, 0x23, 0x09, 0x03, 0x13, 0x00, + 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, + 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x00, 0x9f, + 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, + 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, + 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, 0x0a, 0x00, 0x11, 0x00, + 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, + 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, + 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, + 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, + 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x03, 0x00, 0x0a, 0x00, + 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, + 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x09, 0x03, 0x00, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, + 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, + 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, + 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x05, 0x00, + 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x04, + 0x00, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, + 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, + 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, + 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x01, 0x00, + 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, + 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, + 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, + 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, + 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, + 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1a, 0x00, 0x11, 0x00, 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, + 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, + 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, + 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, + 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, + 0x03, 0x00, 0x00, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, + 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, + 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, + 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, + 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, + 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, + 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, + 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, + 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, + 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, + 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, + 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, + 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, + 0x12, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, + 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, + 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, + 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, + 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, + 0x00, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, + 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, + 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, + 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, + 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, + 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, + 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, + 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, + 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, + 0x18, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, + 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, + 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, + 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, + 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, + 0x09, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, + 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, + 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, + 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, + 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, + 0x00, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, + 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, + 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, + 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, + 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, + 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, + 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 +}; + +/** + * Stored in R8 format. Load it in the following format: + * - DX10: DXGI_FORMAT_R8_UNORM + */ +const unsigned char searchTexBytes[] = { + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, + 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, + 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, + 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, + 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, + 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, +}; + +/* clang-format on */ diff --git a/source/blender/draw/intern/smaa_textures.h b/source/blender/draw/intern/smaa_textures.h index 8f150c6cd7d..bb3e3bdfb2f 100644 --- a/source/blender/draw/intern/smaa_textures.h +++ b/source/blender/draw/intern/smaa_textures.h @@ -33,15051 +33,19 @@ #define AREATEX_PITCH (AREATEX_WIDTH * 2) #define AREATEX_SIZE (AREATEX_HEIGHT * AREATEX_PITCH) -/* Don't re-wrap large data definitions. */ -/* clang-format off */ - /** * Stored in R8G8 format. Load it in the following format: * - DX10: DXGI_FORMAT_R8G8_UNORM */ -static const unsigned char areaTexBytes[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x7b, 0x41, 0x5d, - 0x42, 0x54, 0x41, 0x4f, 0x42, 0x4b, 0x42, 0x49, 0x42, 0x48, 0x41, 0x47, - 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, - 0x42, 0x43, 0x41, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, - 0x04, 0x7f, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, - 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, - 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x7f, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x62, 0x20, 0x4d, 0x2a, 0x48, 0x30, 0x44, 0x33, - 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, 0x43, 0x39, 0x42, 0x39, - 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x41, 0x3b, - 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x00, 0x5d, 0x0c, 0x49, - 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, - 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, - 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, 0x00, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x6d, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, 0x48, 0x26, 0x47, 0x29, - 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, 0x44, 0x31, 0x44, 0x32, - 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, 0x43, 0x36, 0x42, 0x36, - 0x42, 0x37, 0x42, 0x37, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, - 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, - 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, - 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, - 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x72, 0x06, 0x5f, 0x0d, - 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, 0x49, 0x23, 0x47, 0x25, - 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, 0x44, 0x2d, 0x44, 0x2e, - 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, 0x43, 0x32, 0x43, 0x33, - 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, - 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, - 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, - 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, - 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, 0x5a, 0x0e, 0x54, 0x13, - 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, 0x49, 0x22, 0x48, 0x23, - 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, - 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, 0x00, 0x70, 0x02, 0x60, - 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, - 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, - 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, 0x54, 0x12, 0x51, 0x15, - 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, 0x49, 0x21, 0x48, 0x22, - 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, 0x45, 0x28, 0x45, 0x29, - 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, - 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, - 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, - 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x78, 0x02, 0x6b, 0x05, - 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4e, 0x17, - 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x20, 0x48, 0x21, - 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, 0x46, 0x27, 0x45, 0x27, - 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, - 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, - 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, - 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, - 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, 0x65, 0x07, 0x5e, 0x0a, - 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, 0x4f, 0x16, 0x4e, 0x18, - 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, 0x49, 0x1f, 0x48, 0x21, - 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, 0x00, 0x75, 0x01, 0x69, - 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, - 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, - 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, - 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, 0x5d, 0x0b, 0x59, 0x0d, - 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, 0x4e, 0x17, 0x4d, 0x19, - 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, 0x49, 0x1f, 0x49, 0x20, - 0x48, 0x21, 0x48, 0x22, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, - 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, - 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, - 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x01, 0x71, 0x02, - 0x69, 0x05, 0x63, 0x07, 0x5f, 0x09, 0x5b, 0x0c, 0x58, 0x0e, 0x55, 0x10, - 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x4c, 0x19, - 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, 0x49, 0x1f, 0x49, 0x20, - 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, - 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, - 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, - 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, - 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, - 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, 0x6b, 0x04, 0x64, 0x06, - 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, - 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, 0x4d, 0x18, 0x4b, 0x1a, - 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, 0x00, 0x77, 0x00, 0x6e, - 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, - 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, - 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, - 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, - 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, 0x63, 0x07, 0x5e, 0x09, - 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, - 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, - 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, - 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, - 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, - 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, - 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, - 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x74, 0x02, - 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, 0x5d, 0x0a, 0x5a, 0x0b, - 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, - 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, 0x4c, 0x19, 0x4c, 0x1a, - 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, - 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, - 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, - 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, 0x6f, 0x02, 0x69, 0x04, - 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, 0x5a, 0x0c, 0x58, 0x0d, - 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x13, 0x4f, 0x15, - 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, 0x00, 0x78, 0x00, 0x71, - 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, - 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, - 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, - 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, - 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, - 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, - 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, 0x67, 0x05, 0x63, 0x06, - 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, 0x57, 0x0d, 0x55, 0x0e, - 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, 0x50, 0x15, 0x4f, 0x15, - 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, - 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, - 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, - 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, - 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, - 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, - 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, - 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, 0x61, 0x07, 0x5e, 0x09, - 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, 0x55, 0x0e, 0x55, 0x11, - 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x15, - 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, - 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, - 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x1f, 0x00, 0x3f, - 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, - 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, - 0x00, 0x79, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, - 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, - 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x7e, 0x00, 0x7e, 0x1f, 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, - 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, 0x72, 0x02, 0x6d, 0x03, - 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, 0x5e, 0x09, 0x5c, 0x0b, - 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x51, 0x11, - 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, 0x00, 0x79, 0x00, 0x73, - 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, - 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x3f, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x58, 0x00, 0x44, 0x00, 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, - 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, - 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, - 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, - 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, - 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, 0x6a, 0x03, 0x67, 0x05, - 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, 0x5b, 0x0b, 0x5a, 0x0c, - 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, 0x52, 0x11, 0x52, 0x12, - 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, - 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, - 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, - 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, - 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, - 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x55, - 0x00, 0x20, 0x00, 0x3e, 0x00, 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, - 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, - 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, - 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, - 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x66, 0x00, 0x3f, 0x00, - 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, - 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, - 0x00, 0x79, 0x00, 0x7a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x01, - 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, 0x65, 0x06, 0x61, 0x07, - 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0d, - 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, - 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, - 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, - 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, - 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, - 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, - 0x00, 0x73, 0x00, 0x75, 0x00, 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, - 0x00, 0x28, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, - 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, - 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, - 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, - 0x00, 0x73, 0x00, 0x75, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x00, 0x01, 0x01, - 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, - 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, 0x74, 0x01, 0x6f, 0x02, - 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, 0x61, 0x07, 0x5f, 0x09, - 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, 0x57, 0x0d, 0x55, 0x0e, - 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, 0x00, 0x79, 0x00, 0x74, - 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, - 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, - 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, - 0x00, 0x79, 0x00, 0x6e, 0x00, 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, - 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, - 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, - 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x1b, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, - 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x21, 0x5d, 0x0c, 0x68, - 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, - 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, - 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, - 0x42, 0x40, 0x17, 0x55, 0x0c, 0x60, 0x08, 0x66, 0x05, 0x6a, 0x04, 0x6d, - 0x03, 0x6f, 0x02, 0x71, 0x02, 0x73, 0x01, 0x73, 0x01, 0x74, 0x01, 0x75, - 0x01, 0x76, 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, - 0x00, 0x78, 0x00, 0x78, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x21, 0x5d, 0x0c, 0x68, - 0x06, 0x6d, 0x04, 0x71, 0x02, 0x72, 0x02, 0x74, 0x01, 0x75, 0x01, 0x76, - 0x01, 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, - 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, - 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x7b, 0x00, 0x72, - 0x00, 0x5a, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, - 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, - 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, - 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x7a, 0x00, 0x71, 0x00, - 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, - 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, - 0x00, 0x62, 0x00, 0x65, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, - 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, - 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, - 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x57, 0x16, 0x2d, 0x2c, - 0x1b, 0x3b, 0x12, 0x45, 0x0d, 0x4d, 0x0b, 0x53, 0x08, 0x57, 0x07, 0x5b, - 0x05, 0x5e, 0x05, 0x61, 0x04, 0x63, 0x04, 0x65, 0x03, 0x67, 0x02, 0x68, - 0x02, 0x69, 0x02, 0x6b, 0x02, 0x6c, 0x01, 0x6d, 0x01, 0x6e, 0x01, 0x6f, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x2b, 0x49, 0x16, 0x53, 0x0d, 0x5b, 0x09, 0x60, - 0x07, 0x64, 0x05, 0x67, 0x04, 0x69, 0x03, 0x6b, 0x03, 0x6c, 0x02, 0x6e, - 0x02, 0x6f, 0x02, 0x70, 0x01, 0x71, 0x01, 0x71, 0x01, 0x72, 0x01, 0x73, - 0x01, 0x73, 0x01, 0x74, 0x00, 0x74, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, - 0x00, 0x57, 0x00, 0x5b, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, - 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, - 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, - 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, - 0x00, 0x57, 0x00, 0x5b, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, - 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, - 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, - 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, - 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, - 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, - 0x02, 0x70, 0x01, 0x70, 0x61, 0x0c, 0x3b, 0x1b, 0x28, 0x28, 0x1d, 0x32, - 0x16, 0x3a, 0x11, 0x41, 0x0e, 0x47, 0x0c, 0x4b, 0x0a, 0x4f, 0x09, 0x53, - 0x07, 0x55, 0x06, 0x58, 0x05, 0x5a, 0x05, 0x5c, 0x05, 0x5e, 0x04, 0x60, - 0x04, 0x61, 0x04, 0x63, 0x03, 0x64, 0x02, 0x66, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x31, 0x43, 0x1d, 0x4b, 0x14, 0x51, 0x0e, 0x56, 0x0b, 0x5b, 0x08, 0x5e, - 0x07, 0x61, 0x06, 0x63, 0x05, 0x65, 0x04, 0x67, 0x04, 0x68, 0x03, 0x69, - 0x03, 0x6a, 0x03, 0x6c, 0x02, 0x6d, 0x02, 0x6d, 0x02, 0x6e, 0x02, 0x6f, - 0x02, 0x70, 0x01, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, - 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, - 0x00, 0x13, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, - 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, - 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, - 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, - 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, - 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x33, 0x41, 0x22, 0x46, - 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, - 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, - 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, - 0x67, 0x07, 0x45, 0x12, 0x32, 0x1d, 0x26, 0x26, 0x1e, 0x2e, 0x18, 0x34, - 0x14, 0x3a, 0x11, 0x3f, 0x0f, 0x44, 0x0c, 0x47, 0x0b, 0x4b, 0x0a, 0x4d, - 0x09, 0x51, 0x07, 0x52, 0x07, 0x55, 0x06, 0x57, 0x05, 0x58, 0x05, 0x5a, - 0x05, 0x5c, 0x04, 0x5d, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x33, 0x41, 0x22, 0x46, - 0x19, 0x4c, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5d, - 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x66, 0x04, 0x66, - 0x04, 0x68, 0x03, 0x69, 0x03, 0x69, 0x03, 0x6a, 0x03, 0x6b, 0x02, 0x6c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, - 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x7d, 0x00, 0x79, - 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, - 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, - 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, - 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x7d, 0x00, 0x79, 0x00, - 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, - 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, - 0x00, 0x40, 0x00, 0x46, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, - 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, - 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x6b, 0x05, 0x4d, 0x0d, - 0x3b, 0x16, 0x2e, 0x1e, 0x25, 0x25, 0x1f, 0x2b, 0x1a, 0x31, 0x16, 0x36, - 0x13, 0x3a, 0x10, 0x3e, 0x0f, 0x42, 0x0d, 0x45, 0x0c, 0x47, 0x0a, 0x4a, - 0x0a, 0x4c, 0x09, 0x4f, 0x07, 0x51, 0x07, 0x52, 0x07, 0x54, 0x06, 0x56, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x35, 0x40, 0x27, 0x44, 0x1d, 0x48, 0x17, 0x4c, - 0x12, 0x50, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5e, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, 0x04, 0x65, - 0x04, 0x66, 0x04, 0x66, 0x04, 0x67, 0x03, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, - 0x00, 0x34, 0x00, 0x3b, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, - 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, - 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, - 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, - 0x00, 0x34, 0x00, 0x3b, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, - 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, - 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, - 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, - 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, - 0x04, 0x64, 0x04, 0x65, 0x6e, 0x04, 0x53, 0x0a, 0x41, 0x11, 0x34, 0x18, - 0x2b, 0x1f, 0x24, 0x24, 0x1f, 0x29, 0x1b, 0x2e, 0x17, 0x33, 0x15, 0x37, - 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x40, 0x0d, 0x43, 0x0c, 0x45, 0x0c, 0x48, - 0x0a, 0x4a, 0x0a, 0x4c, 0x09, 0x4e, 0x07, 0x4f, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x37, 0x3f, 0x29, 0x43, 0x21, 0x46, 0x1a, 0x49, 0x16, 0x4d, 0x12, 0x50, - 0x10, 0x52, 0x0d, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x06, 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x63, - 0x04, 0x64, 0x04, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, - 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, - 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, - 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, - 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, - 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x38, 0x3f, 0x2c, 0x41, - 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, - 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, - 0x70, 0x03, 0x58, 0x08, 0x47, 0x0e, 0x3a, 0x14, 0x31, 0x1a, 0x29, 0x1f, - 0x24, 0x24, 0x20, 0x28, 0x1c, 0x2d, 0x19, 0x31, 0x16, 0x34, 0x14, 0x37, - 0x12, 0x3a, 0x10, 0x3d, 0x0f, 0x3f, 0x0e, 0x42, 0x0c, 0x44, 0x0c, 0x46, - 0x0b, 0x47, 0x0a, 0x4a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x38, 0x3f, 0x2c, 0x41, - 0x23, 0x44, 0x1d, 0x47, 0x19, 0x4a, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x52, - 0x0e, 0x54, 0x0c, 0x56, 0x0b, 0x57, 0x0a, 0x59, 0x09, 0x5a, 0x08, 0x5c, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x61, 0x05, 0x61, 0x05, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, - 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, - 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, - 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x7e, 0x00, 0x7c, 0x00, - 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, - 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, - 0x00, 0x1e, 0x00, 0x26, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, - 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, - 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x71, 0x02, 0x5b, 0x07, - 0x4c, 0x0c, 0x3f, 0x11, 0x36, 0x16, 0x2e, 0x1b, 0x29, 0x20, 0x24, 0x23, - 0x20, 0x28, 0x1d, 0x2b, 0x19, 0x2f, 0x17, 0x32, 0x16, 0x35, 0x12, 0x37, - 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3f, 0x0e, 0x41, 0x0c, 0x42, 0x0c, 0x45, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x39, 0x3f, 0x2e, 0x41, 0x26, 0x43, 0x20, 0x46, - 0x1b, 0x48, 0x17, 0x4b, 0x14, 0x4d, 0x12, 0x4f, 0x10, 0x51, 0x0e, 0x53, - 0x0d, 0x55, 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5d, 0x07, 0x5e, 0x06, 0x5f, 0x06, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x13, 0x00, 0x1b, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, - 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, - 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, - 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, - 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x13, 0x00, 0x1b, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, - 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, - 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, - 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, - 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, - 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5c, 0x07, 0x5e, 0x73, 0x02, 0x5e, 0x05, 0x4f, 0x0a, 0x44, 0x0f, - 0x3a, 0x13, 0x33, 0x18, 0x2d, 0x1c, 0x27, 0x20, 0x23, 0x23, 0x20, 0x27, - 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x30, 0x16, 0x33, 0x14, 0x35, 0x12, 0x38, - 0x12, 0x3a, 0x10, 0x3c, 0x0f, 0x3e, 0x0f, 0x41, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x39, 0x3e, 0x2f, 0x40, 0x28, 0x42, 0x22, 0x45, 0x1d, 0x47, 0x19, 0x49, - 0x16, 0x4b, 0x14, 0x4d, 0x11, 0x4f, 0x10, 0x51, 0x0f, 0x53, 0x0d, 0x54, - 0x0c, 0x55, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x07, 0x5c, 0x07, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, - 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, - 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, - 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, - 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, - 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, - 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x3a, 0x3e, 0x31, 0x40, - 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, - 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, - 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x74, 0x01, 0x62, 0x05, 0x53, 0x08, 0x47, 0x0c, 0x3e, 0x11, 0x37, 0x15, - 0x31, 0x19, 0x2b, 0x1d, 0x27, 0x20, 0x23, 0x23, 0x20, 0x26, 0x1d, 0x2a, - 0x1b, 0x2c, 0x19, 0x2f, 0x16, 0x31, 0x16, 0x34, 0x13, 0x35, 0x12, 0x38, - 0x12, 0x3b, 0x0f, 0x3b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x31, 0x40, - 0x29, 0x42, 0x23, 0x44, 0x1f, 0x46, 0x1b, 0x48, 0x18, 0x4a, 0x15, 0x4c, - 0x13, 0x4d, 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x55, - 0x0b, 0x56, 0x0b, 0x57, 0x0a, 0x58, 0x09, 0x59, 0x09, 0x5b, 0x08, 0x5b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, - 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, - 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x7e, 0x00, 0x7d, 0x00, - 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, - 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, - 0x00, 0x00, 0x00, 0x08, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, - 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, - 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, - 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x75, 0x01, 0x64, 0x04, - 0x56, 0x07, 0x4b, 0x0b, 0x42, 0x0f, 0x3a, 0x12, 0x34, 0x16, 0x2f, 0x19, - 0x2a, 0x1d, 0x26, 0x20, 0x23, 0x23, 0x21, 0x26, 0x1d, 0x29, 0x1b, 0x2b, - 0x19, 0x2e, 0x18, 0x30, 0x16, 0x32, 0x15, 0x35, 0x12, 0x35, 0x12, 0x38, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3a, 0x3e, 0x32, 0x3f, 0x2b, 0x41, 0x25, 0x43, - 0x21, 0x45, 0x1d, 0x46, 0x1a, 0x48, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, - 0x11, 0x4f, 0x10, 0x50, 0x0f, 0x52, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, - 0x0b, 0x56, 0x0b, 0x58, 0x09, 0x58, 0x09, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, - 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, - 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, - 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, - 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, - 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, - 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, - 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, - 0x0b, 0x57, 0x0a, 0x58, 0x76, 0x01, 0x66, 0x04, 0x58, 0x06, 0x4d, 0x0a, - 0x45, 0x0d, 0x3e, 0x10, 0x37, 0x14, 0x32, 0x17, 0x2d, 0x1a, 0x2a, 0x1d, - 0x26, 0x21, 0x22, 0x22, 0x21, 0x26, 0x1d, 0x28, 0x1c, 0x2b, 0x19, 0x2c, - 0x19, 0x30, 0x16, 0x30, 0x16, 0x33, 0x14, 0x35, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x3b, 0x3e, 0x33, 0x3f, 0x2c, 0x41, 0x27, 0x42, 0x22, 0x44, 0x1f, 0x45, - 0x1c, 0x47, 0x19, 0x49, 0x17, 0x4a, 0x15, 0x4c, 0x13, 0x4e, 0x11, 0x4f, - 0x10, 0x50, 0x0f, 0x51, 0x0e, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x0b, 0x55, - 0x0b, 0x57, 0x0a, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x33, 0x3f, - 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, - 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, - 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, - 0x77, 0x01, 0x67, 0x03, 0x5a, 0x05, 0x51, 0x09, 0x48, 0x0c, 0x40, 0x0f, - 0x3a, 0x12, 0x35, 0x16, 0x30, 0x18, 0x2c, 0x1b, 0x29, 0x1d, 0x26, 0x21, - 0x22, 0x22, 0x21, 0x26, 0x1d, 0x27, 0x1d, 0x2b, 0x1a, 0x2b, 0x19, 0x2e, - 0x17, 0x30, 0x16, 0x31, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x33, 0x3f, - 0x2d, 0x40, 0x28, 0x42, 0x24, 0x44, 0x20, 0x45, 0x1d, 0x46, 0x1a, 0x48, - 0x18, 0x49, 0x16, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x10, 0x50, - 0x0f, 0x51, 0x0f, 0x53, 0x0d, 0x53, 0x0d, 0x55, 0x0b, 0x55, 0x0b, 0x56, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, - 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, - 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x77, 0x01, 0x68, 0x02, - 0x5d, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, 0x0d, 0x3d, 0x10, 0x38, 0x12, - 0x33, 0x16, 0x2f, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x21, 0x22, 0x22, - 0x21, 0x26, 0x1e, 0x26, 0x1d, 0x2a, 0x1a, 0x2b, 0x19, 0x2d, 0x18, 0x30, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3b, 0x3e, 0x34, 0x3f, 0x2e, 0x40, 0x29, 0x41, - 0x25, 0x43, 0x21, 0x44, 0x1e, 0x45, 0x1c, 0x47, 0x19, 0x48, 0x18, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, - 0x0f, 0x52, 0x0d, 0x53, 0x0d, 0x54, 0x0c, 0x55, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, - 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, - 0x0d, 0x53, 0x0d, 0x53, 0x77, 0x00, 0x6a, 0x02, 0x5e, 0x04, 0x55, 0x07, - 0x4c, 0x0a, 0x45, 0x0c, 0x40, 0x0f, 0x3a, 0x12, 0x35, 0x14, 0x31, 0x16, - 0x2e, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x22, 0x21, 0x22, 0x21, 0x25, - 0x1e, 0x26, 0x1d, 0x29, 0x1b, 0x2b, 0x19, 0x2b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x3c, 0x3e, 0x35, 0x3f, 0x2f, 0x40, 0x2a, 0x41, 0x26, 0x42, 0x22, 0x44, - 0x20, 0x45, 0x1d, 0x46, 0x1b, 0x48, 0x18, 0x48, 0x17, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x52, - 0x0d, 0x53, 0x0d, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x35, 0x3f, - 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, - 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, - 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, - 0x78, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, 0x06, 0x4f, 0x09, 0x48, 0x0c, - 0x42, 0x0e, 0x3c, 0x10, 0x38, 0x12, 0x34, 0x16, 0x30, 0x18, 0x2d, 0x19, - 0x2b, 0x1d, 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, - 0x1d, 0x28, 0x1c, 0x2b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x35, 0x3f, - 0x30, 0x3f, 0x2b, 0x40, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, 0x1e, 0x45, - 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, 0x15, 0x4c, 0x13, 0x4c, - 0x13, 0x4e, 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x0e, 0x53, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, - 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, - 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, - 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x78, 0x00, 0x6d, 0x02, - 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, 0x0a, 0x44, 0x0c, 0x3f, 0x0f, - 0x3a, 0x12, 0x35, 0x14, 0x32, 0x16, 0x30, 0x19, 0x2b, 0x19, 0x2a, 0x1d, - 0x26, 0x1e, 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1e, 0x26, 0x1d, 0x27, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x36, 0x3f, 0x30, 0x3f, 0x2c, 0x40, - 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, - 0x19, 0x48, 0x18, 0x4a, 0x15, 0x4a, 0x15, 0x4c, 0x13, 0x4c, 0x13, 0x4e, - 0x11, 0x4e, 0x11, 0x50, 0x0f, 0x50, 0x0f, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, - 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, - 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, - 0x11, 0x50, 0x0f, 0x50, 0x78, 0x00, 0x6d, 0x02, 0x63, 0x04, 0x5a, 0x05, - 0x53, 0x07, 0x4c, 0x0a, 0x46, 0x0c, 0x41, 0x0f, 0x3c, 0x0f, 0x38, 0x12, - 0x35, 0x16, 0x30, 0x16, 0x2e, 0x19, 0x2b, 0x1a, 0x29, 0x1d, 0x26, 0x1e, - 0x25, 0x22, 0x21, 0x22, 0x21, 0x25, 0x1f, 0x26, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x3c, 0x3e, 0x37, 0x3e, 0x31, 0x3f, 0x2d, 0x40, 0x29, 0x41, 0x26, 0x42, - 0x23, 0x44, 0x20, 0x45, 0x1e, 0x45, 0x1c, 0x46, 0x1a, 0x48, 0x18, 0x48, - 0x17, 0x4a, 0x15, 0x4a, 0x14, 0x4c, 0x13, 0x4c, 0x12, 0x4e, 0x11, 0x4e, - 0x11, 0x50, 0x0f, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, - 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, - 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, - 0x78, 0x00, 0x6e, 0x01, 0x65, 0x03, 0x5c, 0x05, 0x54, 0x07, 0x4e, 0x09, - 0x48, 0x0b, 0x43, 0x0c, 0x3e, 0x0f, 0x3b, 0x12, 0x36, 0x12, 0x33, 0x16, - 0x30, 0x17, 0x2d, 0x19, 0x2b, 0x1b, 0x28, 0x1d, 0x26, 0x1e, 0x25, 0x22, - 0x21, 0x22, 0x21, 0x24, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, - 0x32, 0x3f, 0x2e, 0x40, 0x2a, 0x41, 0x27, 0x42, 0x24, 0x43, 0x21, 0x44, - 0x1f, 0x45, 0x1d, 0x46, 0x1b, 0x47, 0x1a, 0x48, 0x18, 0x49, 0x16, 0x4a, - 0x15, 0x4b, 0x14, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x11, 0x50, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, - 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, - 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x79, 0x00, 0x6f, 0x01, - 0x66, 0x02, 0x5d, 0x04, 0x56, 0x06, 0x4f, 0x07, 0x4a, 0x0a, 0x45, 0x0c, - 0x41, 0x0f, 0x3b, 0x0f, 0x38, 0x12, 0x35, 0x14, 0x31, 0x16, 0x30, 0x18, - 0x2b, 0x19, 0x2b, 0x1c, 0x27, 0x1d, 0x26, 0x1f, 0x24, 0x22, 0x21, 0x22, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7b, - 0x00, 0x7b, 0x00, 0x7b, 0x3c, 0x3e, 0x37, 0x3e, 0x33, 0x3f, 0x2e, 0x3f, - 0x2b, 0x40, 0x28, 0x41, 0x25, 0x42, 0x22, 0x44, 0x20, 0x45, 0x1e, 0x45, - 0x1c, 0x46, 0x1b, 0x48, 0x19, 0x48, 0x18, 0x4a, 0x16, 0x4a, 0x15, 0x4b, - 0x13, 0x4c, 0x13, 0x4d, 0x12, 0x4e, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x74, 0x00, 0x77, 0x00, 0x79, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x81, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x08, 0x00, 0x44, 0x00, - 0x56, 0x00, 0x61, 0x00, 0x67, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x70, 0x00, - 0x71, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, 0x76, 0x00, - 0x77, 0x00, 0x77, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, - 0x44, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, - 0x79, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7b, 0x00, 0x7c, 0x00, - 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x0b, 0x57, 0x06, - 0x5f, 0x03, 0x63, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6d, 0x01, 0x6e, 0x01, - 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, 0x76, 0x00, - 0x76, 0x00, 0x76, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, 0x79, 0x00, - 0x82, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x40, 0x17, 0x17, 0x2c, 0x0c, 0x3b, 0x07, - 0x45, 0x05, 0x4d, 0x04, 0x53, 0x03, 0x57, 0x02, 0x5c, 0x02, 0x5e, 0x01, - 0x61, 0x01, 0x63, 0x01, 0x66, 0x01, 0x67, 0x01, 0x68, 0x00, 0x69, 0x00, - 0x6b, 0x00, 0x6c, 0x00, 0x6d, 0x00, 0x6e, 0x00, 0x41, 0x20, 0x4d, 0x0b, - 0x58, 0x06, 0x5e, 0x03, 0x64, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6c, 0x01, - 0x6f, 0x01, 0x71, 0x00, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x75, 0x00, - 0x76, 0x00, 0x75, 0x00, 0x77, 0x00, 0x78, 0x00, 0x78, 0x00, 0x78, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x15, 0x4f, 0x0d, 0x56, 0x09, 0x59, 0x06, - 0x5f, 0x05, 0x62, 0x04, 0x65, 0x03, 0x67, 0x02, 0x69, 0x02, 0x6b, 0x02, - 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x71, 0x01, 0x70, 0x01, - 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x74, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x55, 0x0c, 0x2c, 0x1b, 0x1b, 0x27, 0x12, 0x32, 0x0d, 0x3a, 0x0b, - 0x41, 0x08, 0x47, 0x07, 0x4c, 0x05, 0x4f, 0x05, 0x53, 0x04, 0x56, 0x04, - 0x58, 0x03, 0x5a, 0x02, 0x5d, 0x02, 0x5e, 0x02, 0x60, 0x02, 0x61, 0x01, - 0x63, 0x01, 0x64, 0x01, 0x41, 0x2a, 0x47, 0x16, 0x4f, 0x0d, 0x54, 0x09, - 0x5b, 0x06, 0x5f, 0x05, 0x62, 0x04, 0x64, 0x03, 0x67, 0x02, 0x69, 0x02, - 0x6b, 0x02, 0x6c, 0x02, 0x6e, 0x01, 0x6f, 0x01, 0x70, 0x01, 0x70, 0x01, - 0x71, 0x01, 0x72, 0x00, 0x73, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x1d, 0x4a, 0x14, 0x50, 0x0e, 0x54, 0x0b, 0x59, 0x08, 0x5c, 0x07, - 0x5f, 0x06, 0x60, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, 0x68, 0x02, - 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6c, 0x02, 0x6e, 0x02, 0x6f, 0x01, - 0x70, 0x01, 0x70, 0x01, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x60, 0x08, 0x3b, - 0x12, 0x28, 0x1d, 0x1d, 0x26, 0x16, 0x2e, 0x11, 0x34, 0x0e, 0x3a, 0x0c, - 0x3f, 0x0a, 0x44, 0x08, 0x47, 0x07, 0x4b, 0x06, 0x4d, 0x05, 0x51, 0x05, - 0x52, 0x04, 0x55, 0x04, 0x57, 0x04, 0x58, 0x04, 0x5a, 0x03, 0x5c, 0x02, - 0x42, 0x30, 0x45, 0x1d, 0x4b, 0x14, 0x4f, 0x0e, 0x55, 0x0b, 0x59, 0x08, - 0x5c, 0x07, 0x5e, 0x06, 0x61, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, - 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6b, 0x02, 0x6d, 0x02, 0x6e, 0x02, - 0x6f, 0x01, 0x70, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x22, 0x48, 0x19, - 0x4d, 0x13, 0x50, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x5a, 0x08, 0x5c, 0x07, - 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, 0x67, 0x03, - 0x68, 0x03, 0x68, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, 0x6d, 0x02, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x66, 0x05, 0x45, 0x0d, 0x32, 0x16, 0x26, - 0x1e, 0x1e, 0x25, 0x18, 0x2b, 0x14, 0x31, 0x11, 0x36, 0x0f, 0x3a, 0x0c, - 0x3e, 0x0b, 0x42, 0x0a, 0x45, 0x09, 0x47, 0x07, 0x4a, 0x07, 0x4c, 0x06, - 0x4f, 0x05, 0x51, 0x05, 0x52, 0x05, 0x54, 0x04, 0x41, 0x33, 0x44, 0x22, - 0x48, 0x19, 0x4c, 0x13, 0x51, 0x0f, 0x54, 0x0c, 0x57, 0x0a, 0x59, 0x08, - 0x5d, 0x07, 0x5f, 0x06, 0x61, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, - 0x67, 0x03, 0x67, 0x03, 0x69, 0x02, 0x6a, 0x02, 0x6b, 0x02, 0x6c, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x26, 0x47, 0x1d, 0x4a, 0x17, 0x4d, 0x12, - 0x51, 0x0f, 0x54, 0x0d, 0x56, 0x0b, 0x58, 0x09, 0x5b, 0x08, 0x5d, 0x07, - 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x04, 0x65, 0x03, - 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x69, 0x02, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x6a, 0x04, 0x4d, 0x0b, 0x3a, 0x11, 0x2e, 0x18, 0x25, 0x1f, 0x1f, - 0x24, 0x1a, 0x29, 0x16, 0x2e, 0x13, 0x33, 0x11, 0x37, 0x0f, 0x3a, 0x0d, - 0x3d, 0x0c, 0x40, 0x0a, 0x43, 0x0a, 0x45, 0x09, 0x48, 0x07, 0x4a, 0x07, - 0x4c, 0x07, 0x4e, 0x06, 0x41, 0x35, 0x43, 0x26, 0x47, 0x1d, 0x49, 0x17, - 0x4e, 0x12, 0x51, 0x0f, 0x54, 0x0d, 0x55, 0x0b, 0x59, 0x09, 0x5b, 0x08, - 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x62, 0x05, 0x63, 0x05, 0x63, 0x04, - 0x65, 0x03, 0x67, 0x03, 0x67, 0x03, 0x68, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x29, 0x45, 0x20, 0x49, 0x1a, 0x4b, 0x15, 0x4f, 0x12, 0x51, 0x0f, - 0x54, 0x0d, 0x55, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, 0x5d, 0x07, - 0x5f, 0x06, 0x60, 0x06, 0x61, 0x05, 0x62, 0x05, 0x64, 0x05, 0x65, 0x04, - 0x65, 0x03, 0x66, 0x03, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x6d, 0x03, 0x52, - 0x08, 0x41, 0x0e, 0x34, 0x14, 0x2b, 0x1a, 0x24, 0x1f, 0x1f, 0x24, 0x1b, - 0x28, 0x17, 0x2d, 0x15, 0x31, 0x12, 0x34, 0x10, 0x37, 0x0f, 0x3a, 0x0d, - 0x3d, 0x0c, 0x40, 0x0b, 0x42, 0x0a, 0x44, 0x0a, 0x46, 0x09, 0x47, 0x07, - 0x41, 0x36, 0x43, 0x29, 0x46, 0x20, 0x48, 0x1a, 0x4c, 0x15, 0x4f, 0x12, - 0x51, 0x0f, 0x53, 0x0d, 0x56, 0x0b, 0x58, 0x0a, 0x5a, 0x09, 0x5b, 0x08, - 0x5d, 0x07, 0x5f, 0x06, 0x60, 0x06, 0x60, 0x05, 0x62, 0x05, 0x64, 0x05, - 0x65, 0x04, 0x65, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x2b, 0x45, 0x23, - 0x48, 0x1d, 0x49, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, 0x53, 0x0e, - 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, 0x5d, 0x07, - 0x5f, 0x07, 0x5f, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, 0x64, 0x05, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x6f, 0x02, 0x57, 0x07, 0x47, 0x0c, 0x3a, - 0x11, 0x31, 0x16, 0x29, 0x1b, 0x24, 0x20, 0x20, 0x23, 0x1c, 0x28, 0x19, - 0x2b, 0x16, 0x2f, 0x14, 0x32, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3a, 0x0e, - 0x3c, 0x0c, 0x3f, 0x0c, 0x41, 0x0b, 0x42, 0x0a, 0x41, 0x37, 0x43, 0x2b, - 0x45, 0x23, 0x47, 0x1d, 0x4a, 0x18, 0x4d, 0x14, 0x4f, 0x12, 0x51, 0x10, - 0x53, 0x0e, 0x55, 0x0c, 0x57, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5c, 0x08, - 0x5d, 0x07, 0x5e, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x05, 0x63, 0x05, - 0x1f, 0x00, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, - 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, - 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x58, 0x00, 0x70, 0x00, 0x77, 0x00, 0x79, 0x00, 0x7b, 0x00, - 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x1f, 0x1f, 0x3f, 0x00, - 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x43, 0x2d, 0x44, 0x25, 0x46, 0x1f, 0x48, 0x1b, - 0x4b, 0x17, 0x4d, 0x14, 0x50, 0x12, 0x51, 0x10, 0x53, 0x0e, 0x55, 0x0c, - 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x08, 0x5c, 0x07, - 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x62, 0x06, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x71, 0x02, 0x5b, 0x05, 0x4b, 0x0a, 0x3f, 0x0f, 0x36, 0x13, 0x2e, - 0x18, 0x29, 0x1c, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x27, 0x19, 0x2a, 0x17, - 0x2d, 0x16, 0x30, 0x12, 0x33, 0x12, 0x35, 0x10, 0x38, 0x0f, 0x3b, 0x0e, - 0x3c, 0x0c, 0x3e, 0x0c, 0x41, 0x38, 0x43, 0x2d, 0x44, 0x25, 0x45, 0x1f, - 0x49, 0x1b, 0x4b, 0x17, 0x4d, 0x14, 0x4f, 0x11, 0x51, 0x10, 0x53, 0x0e, - 0x55, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5b, 0x08, - 0x5e, 0x07, 0x5f, 0x07, 0x60, 0x06, 0x61, 0x06, 0x00, 0x00, 0x0a, 0x00, - 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, - 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x44, 0x00, - 0x55, 0x00, 0x67, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x75, 0x00, 0x78, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, - 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, - 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, - 0x42, 0x2f, 0x44, 0x27, 0x46, 0x22, 0x47, 0x1d, 0x4a, 0x19, 0x4c, 0x16, - 0x4e, 0x13, 0x4f, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, - 0x58, 0x0b, 0x59, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x07, 0x5e, 0x07, - 0x5f, 0x07, 0x5f, 0x06, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x5e, - 0x05, 0x4f, 0x08, 0x44, 0x0c, 0x3a, 0x10, 0x33, 0x15, 0x2d, 0x19, 0x28, - 0x1d, 0x23, 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1a, 0x2a, 0x18, 0x2c, 0x16, - 0x2f, 0x14, 0x31, 0x12, 0x34, 0x12, 0x35, 0x0f, 0x38, 0x0f, 0x3b, 0x0f, - 0x41, 0x39, 0x42, 0x2f, 0x44, 0x27, 0x45, 0x22, 0x48, 0x1d, 0x4a, 0x19, - 0x4c, 0x16, 0x4d, 0x14, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, - 0x56, 0x0c, 0x58, 0x0b, 0x59, 0x0a, 0x59, 0x09, 0x5c, 0x09, 0x5c, 0x07, - 0x5e, 0x07, 0x5f, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x2d, 0x00, - 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, - 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, - 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, - 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x70, 0x00, 0x55, 0x00, 0x20, 0x00, 0x3e, 0x00, - 0x50, 0x00, 0x5a, 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, - 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, - 0x00, 0x66, 0x00, 0x3f, 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, - 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, - 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x42, 0x30, 0x44, 0x29, - 0x45, 0x23, 0x46, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, 0x4d, 0x13, - 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0b, - 0x59, 0x0b, 0x59, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, 0x5e, 0x07, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x73, 0x01, 0x61, 0x04, 0x52, 0x07, 0x47, - 0x0b, 0x3e, 0x0f, 0x37, 0x12, 0x31, 0x16, 0x2b, 0x19, 0x27, 0x1d, 0x23, - 0x20, 0x20, 0x23, 0x1d, 0x26, 0x1b, 0x29, 0x19, 0x2b, 0x16, 0x2e, 0x16, - 0x30, 0x13, 0x32, 0x12, 0x35, 0x12, 0x36, 0x0f, 0x41, 0x39, 0x42, 0x30, - 0x44, 0x29, 0x44, 0x23, 0x47, 0x1f, 0x49, 0x1b, 0x4b, 0x18, 0x4c, 0x15, - 0x4e, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x56, 0x0c, - 0x57, 0x0b, 0x57, 0x0b, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x5c, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, - 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, - 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, - 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, - 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, - 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x77, 0x00, 0x67, 0x00, 0x3e, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x3a, 0x00, - 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, - 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x72, 0x00, 0x5c, - 0x00, 0x2d, 0x01, 0x01, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, - 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, - 0x73, 0x00, 0x75, 0x00, 0x42, 0x32, 0x44, 0x2a, 0x45, 0x25, 0x46, 0x21, - 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4c, 0x15, 0x4e, 0x13, 0x50, 0x11, - 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x57, 0x0c, 0x57, 0x0b, - 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x5c, 0x09, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x74, 0x01, 0x64, 0x04, 0x55, 0x06, 0x4a, 0x0a, 0x42, 0x0d, 0x3a, - 0x10, 0x34, 0x14, 0x2f, 0x17, 0x2a, 0x1a, 0x26, 0x1d, 0x23, 0x21, 0x21, - 0x22, 0x1d, 0x26, 0x1b, 0x28, 0x19, 0x2b, 0x18, 0x2c, 0x16, 0x30, 0x15, - 0x30, 0x12, 0x33, 0x12, 0x41, 0x3a, 0x42, 0x32, 0x44, 0x2a, 0x44, 0x25, - 0x46, 0x21, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x17, 0x4d, 0x15, 0x4e, 0x13, - 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0d, 0x55, 0x0c, 0x56, 0x0c, - 0x58, 0x0b, 0x5a, 0x0a, 0x5a, 0x09, 0x5b, 0x09, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, - 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, - 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, - 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79, 0x00, 0x6e, 0x00, - 0x50, 0x00, 0x28, 0x00, 0x01, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, - 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, - 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, - 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, - 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x45, 0x22, 0x48, 0x1f, 0x49, 0x1b, - 0x4b, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, - 0x53, 0x0e, 0x55, 0x0e, 0x55, 0x0c, 0x56, 0x0c, 0x57, 0x0b, 0x59, 0x0b, - 0x5a, 0x0a, 0x5a, 0x09, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x75, 0x01, 0x65, - 0x03, 0x58, 0x05, 0x4d, 0x09, 0x45, 0x0c, 0x3e, 0x0f, 0x37, 0x12, 0x32, - 0x16, 0x2d, 0x18, 0x2a, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, - 0x26, 0x1c, 0x27, 0x19, 0x2b, 0x19, 0x2b, 0x16, 0x2e, 0x16, 0x30, 0x14, - 0x41, 0x3a, 0x42, 0x32, 0x43, 0x2c, 0x44, 0x26, 0x46, 0x22, 0x48, 0x1f, - 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x16, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x11, - 0x52, 0x10, 0x53, 0x0e, 0x55, 0x0e, 0x54, 0x0c, 0x57, 0x0c, 0x57, 0x0b, - 0x59, 0x0b, 0x5a, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, - 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, - 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, - 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, - 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x3a, 0x00, - 0x1b, 0x00, 0x01, 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, - 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, - 0x00, 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, - 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, - 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x42, 0x33, 0x43, 0x2d, - 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x4a, 0x1a, 0x4a, 0x18, - 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, 0x53, 0x0e, - 0x55, 0x0e, 0x54, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, 0x5a, 0x0b, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x76, 0x01, 0x67, 0x02, 0x5a, 0x05, 0x50, - 0x07, 0x47, 0x0a, 0x40, 0x0d, 0x3a, 0x10, 0x35, 0x12, 0x30, 0x16, 0x2c, - 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x22, 0x21, 0x21, 0x22, 0x1d, 0x26, 0x1d, - 0x26, 0x1a, 0x2a, 0x19, 0x2b, 0x17, 0x2d, 0x16, 0x41, 0x3b, 0x42, 0x33, - 0x43, 0x2d, 0x44, 0x28, 0x45, 0x23, 0x47, 0x20, 0x48, 0x1d, 0x49, 0x1a, - 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x11, 0x52, 0x10, - 0x53, 0x0e, 0x54, 0x0e, 0x55, 0x0d, 0x57, 0x0c, 0x57, 0x0b, 0x58, 0x0b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, - 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, - 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, - 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, - 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, - 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x7c, 0x00, 0x75, - 0x00, 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, - 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, - 0x57, 0x00, 0x5b, 0x00, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x45, 0x25, - 0x46, 0x21, 0x48, 0x1e, 0x49, 0x1b, 0x4a, 0x19, 0x4c, 0x17, 0x4d, 0x15, - 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x53, 0x0e, - 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x57, 0x0b, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x77, 0x00, 0x68, 0x02, 0x5c, 0x05, 0x52, 0x07, 0x4a, 0x0a, 0x43, - 0x0c, 0x3d, 0x0f, 0x37, 0x12, 0x33, 0x14, 0x2f, 0x16, 0x2b, 0x19, 0x28, - 0x1c, 0x26, 0x1d, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1a, - 0x29, 0x19, 0x2b, 0x18, 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, - 0x45, 0x25, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1b, 0x4b, 0x19, 0x4c, 0x17, - 0x4d, 0x15, 0x4e, 0x14, 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, - 0x54, 0x0e, 0x55, 0x0d, 0x56, 0x0c, 0x57, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, - 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, - 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, - 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, - 0x4c, 0x00, 0x51, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, - 0x42, 0x34, 0x43, 0x2f, 0x44, 0x2a, 0x44, 0x26, 0x46, 0x22, 0x47, 0x1f, - 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, 0x4e, 0x13, - 0x50, 0x13, 0x50, 0x11, 0x52, 0x11, 0x51, 0x0f, 0x54, 0x0e, 0x55, 0x0d, - 0x56, 0x0c, 0x57, 0x0c, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x69, - 0x02, 0x5e, 0x04, 0x55, 0x06, 0x4c, 0x09, 0x45, 0x0c, 0x3f, 0x0e, 0x3a, - 0x10, 0x35, 0x12, 0x31, 0x16, 0x2e, 0x18, 0x2b, 0x19, 0x27, 0x1d, 0x26, - 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1b, 0x28, 0x19, - 0x41, 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x43, 0x2a, 0x45, 0x26, 0x46, 0x22, - 0x47, 0x1f, 0x48, 0x1d, 0x4a, 0x1a, 0x4b, 0x18, 0x4c, 0x17, 0x4e, 0x15, - 0x4e, 0x13, 0x50, 0x13, 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, - 0x55, 0x0d, 0x56, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, - 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, - 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, - 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, - 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, - 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, - 0x00, 0x23, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, - 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x42, 0x35, 0x43, 0x30, - 0x44, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x48, 0x1e, 0x48, 0x1c, - 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, 0x50, 0x12, - 0x50, 0x11, 0x51, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x55, 0x0c, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x77, 0x00, 0x6b, 0x02, 0x60, 0x04, 0x57, - 0x05, 0x4f, 0x07, 0x48, 0x0a, 0x42, 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x34, - 0x14, 0x30, 0x16, 0x2c, 0x19, 0x2b, 0x19, 0x26, 0x1d, 0x25, 0x1e, 0x22, - 0x21, 0x22, 0x21, 0x1e, 0x25, 0x1d, 0x26, 0x1c, 0x41, 0x3b, 0x42, 0x35, - 0x43, 0x30, 0x43, 0x2b, 0x44, 0x27, 0x45, 0x24, 0x46, 0x21, 0x47, 0x1e, - 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x16, 0x4e, 0x15, 0x4e, 0x13, - 0x50, 0x12, 0x50, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, - 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, - 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, - 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, - 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0e, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, - 0x34, 0x00, 0x3b, 0x00, 0x42, 0x36, 0x43, 0x30, 0x44, 0x2c, 0x44, 0x28, - 0x45, 0x25, 0x46, 0x22, 0x48, 0x1f, 0x48, 0x1d, 0x49, 0x1a, 0x4b, 0x19, - 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x50, 0x12, 0x50, 0x11, - 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x55, 0x0e, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x00, 0x77, 0x00, 0x6c, 0x02, 0x61, 0x04, 0x58, 0x05, 0x51, 0x07, 0x4a, - 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x10, 0x35, 0x12, 0x32, 0x16, 0x30, - 0x16, 0x2b, 0x19, 0x2a, 0x1a, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, - 0x21, 0x1e, 0x25, 0x1d, 0x41, 0x3b, 0x42, 0x36, 0x43, 0x30, 0x43, 0x2c, - 0x44, 0x28, 0x45, 0x25, 0x46, 0x22, 0x47, 0x1f, 0x49, 0x1d, 0x49, 0x1a, - 0x4b, 0x19, 0x4c, 0x18, 0x4d, 0x15, 0x4e, 0x15, 0x4f, 0x13, 0x4f, 0x12, - 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, 0x54, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, - 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, - 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, - 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, - 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, - 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, - 0x29, 0x00, 0x30, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, - 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, - 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, - 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, 0x46, 0x23, - 0x47, 0x20, 0x47, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, 0x4c, 0x17, - 0x4d, 0x15, 0x4e, 0x14, 0x4f, 0x13, 0x4f, 0x12, 0x51, 0x11, 0x52, 0x11, - 0x52, 0x0f, 0x54, 0x0e, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6d, - 0x01, 0x62, 0x03, 0x5a, 0x05, 0x52, 0x07, 0x4c, 0x09, 0x46, 0x0b, 0x41, - 0x0c, 0x3c, 0x0f, 0x38, 0x12, 0x35, 0x12, 0x30, 0x16, 0x2e, 0x17, 0x2b, - 0x19, 0x29, 0x1b, 0x26, 0x1d, 0x25, 0x1e, 0x22, 0x21, 0x22, 0x21, 0x1f, - 0x41, 0x3c, 0x42, 0x36, 0x42, 0x31, 0x43, 0x2d, 0x44, 0x29, 0x45, 0x26, - 0x46, 0x23, 0x46, 0x20, 0x48, 0x1e, 0x49, 0x1c, 0x4b, 0x1a, 0x4b, 0x18, - 0x4c, 0x17, 0x4d, 0x15, 0x4e, 0x14, 0x4e, 0x13, 0x50, 0x12, 0x51, 0x11, - 0x52, 0x11, 0x52, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, - 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, - 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, - 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, - 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, - 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, - 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, - 0x00, 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, - 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x42, 0x37, 0x42, 0x32, - 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, 0x47, 0x1f, - 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, 0x4e, 0x15, - 0x4e, 0x14, 0x4f, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, 0x52, 0x0f, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x82, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x83, 0x00, 0x81, 0x00, 0x83, 0x00, 0x83, 0x00, - 0x83, 0x00, 0x83, 0x00, 0x00, 0x78, 0x00, 0x6e, 0x01, 0x64, 0x02, 0x5b, - 0x04, 0x54, 0x06, 0x4e, 0x07, 0x47, 0x0a, 0x42, 0x0c, 0x3e, 0x0f, 0x3b, - 0x0f, 0x35, 0x12, 0x33, 0x14, 0x30, 0x16, 0x2d, 0x19, 0x2b, 0x19, 0x28, - 0x1c, 0x26, 0x1d, 0x25, 0x1f, 0x22, 0x21, 0x22, 0x41, 0x3c, 0x42, 0x37, - 0x42, 0x32, 0x43, 0x2d, 0x44, 0x2a, 0x45, 0x27, 0x45, 0x23, 0x46, 0x21, - 0x48, 0x1f, 0x49, 0x1d, 0x49, 0x1a, 0x4b, 0x19, 0x4c, 0x18, 0x4c, 0x16, - 0x4e, 0x15, 0x4d, 0x14, 0x50, 0x13, 0x50, 0x12, 0x51, 0x11, 0x52, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, - 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, - 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, - 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, - 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, - 0x00, 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, - 0x13, 0x00, 0x1b, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x61, 0x20, 0x4d, 0x2a, - 0x48, 0x30, 0x44, 0x33, 0x44, 0x35, 0x44, 0x36, 0x43, 0x37, 0x42, 0x38, - 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3b, - 0x42, 0x3b, 0x41, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, 0x42, 0x3c, - 0x04, 0x3d, 0x22, 0x3d, 0x2b, 0x3d, 0x30, 0x3d, 0x33, 0x3d, 0x35, 0x3d, - 0x37, 0x3d, 0x38, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, - 0x3c, 0x3d, 0x3c, 0x3d, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, - 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x6c, 0x0b, 0x57, 0x16, 0x4f, 0x1d, 0x4a, 0x22, - 0x48, 0x26, 0x47, 0x29, 0x46, 0x2b, 0x44, 0x2d, 0x44, 0x2f, 0x44, 0x30, - 0x44, 0x31, 0x44, 0x32, 0x43, 0x33, 0x43, 0x34, 0x43, 0x34, 0x42, 0x35, - 0x43, 0x36, 0x42, 0x36, 0x42, 0x37, 0x42, 0x37, 0x00, 0x5d, 0x0c, 0x49, - 0x16, 0x43, 0x1d, 0x41, 0x22, 0x40, 0x26, 0x3f, 0x29, 0x3f, 0x2c, 0x3f, - 0x2e, 0x3e, 0x2f, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, 0x33, 0x3e, - 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x37, 0x3e, 0x37, 0x3e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, - 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, - 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, - 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, - 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, - 0x00, 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, - 0x00, 0x13, 0x00, 0x09, 0x00, 0x00, 0x08, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x72, 0x06, 0x5f, 0x0d, 0x56, 0x14, 0x4f, 0x19, 0x4d, 0x1d, 0x4a, 0x20, - 0x49, 0x23, 0x47, 0x25, 0x46, 0x27, 0x46, 0x29, 0x45, 0x2a, 0x45, 0x2c, - 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2f, 0x43, 0x30, 0x44, 0x30, 0x44, 0x31, - 0x43, 0x32, 0x43, 0x33, 0x00, 0x68, 0x06, 0x54, 0x0d, 0x4b, 0x14, 0x47, - 0x19, 0x44, 0x1d, 0x43, 0x20, 0x41, 0x23, 0x41, 0x26, 0x40, 0x27, 0x40, - 0x29, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, 0x2d, 0x3f, 0x2e, 0x3f, 0x2f, 0x3f, - 0x30, 0x3f, 0x30, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, - 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, - 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, - 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, - 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, - 0x00, 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, - 0x00, 0x08, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x75, 0x03, 0x64, 0x09, - 0x5a, 0x0e, 0x54, 0x13, 0x51, 0x17, 0x4e, 0x1a, 0x4c, 0x1d, 0x49, 0x1f, - 0x49, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x46, 0x28, 0x45, 0x29, - 0x45, 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x44, 0x2d, 0x44, 0x2e, 0x44, 0x2e, - 0x00, 0x6d, 0x04, 0x5b, 0x09, 0x51, 0x0e, 0x4c, 0x13, 0x48, 0x17, 0x46, - 0x1a, 0x44, 0x1d, 0x43, 0x1f, 0x42, 0x22, 0x42, 0x24, 0x41, 0x25, 0x41, - 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, 0x2b, 0x3f, 0x2c, 0x3f, - 0x2d, 0x3f, 0x2e, 0x3f, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x3f, 0x00, 0x66, 0x00, 0x72, 0x00, - 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, - 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x1f, 0x00, 0x3f, - 0x00, 0x66, 0x00, 0x72, 0x00, 0x78, 0x00, 0x7a, 0x00, 0x7c, 0x00, 0x7c, - 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x7e, - 0x00, 0x7e, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x77, 0x02, 0x68, 0x06, 0x5f, 0x0b, 0x58, 0x0f, - 0x54, 0x12, 0x51, 0x15, 0x4f, 0x18, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1f, - 0x49, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x25, 0x46, 0x26, 0x45, 0x27, - 0x45, 0x28, 0x45, 0x29, 0x45, 0x2a, 0x44, 0x2b, 0x00, 0x70, 0x02, 0x60, - 0x07, 0x56, 0x0b, 0x50, 0x0f, 0x4c, 0x12, 0x49, 0x16, 0x47, 0x18, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x43, 0x21, 0x42, 0x22, 0x42, 0x24, 0x41, - 0x25, 0x41, 0x26, 0x40, 0x27, 0x40, 0x28, 0x40, 0x29, 0x40, 0x2a, 0x3f, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x0a, 0x0a, 0x3f, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x71, 0x00, - 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x7b, 0x00, 0x7c, 0x00, - 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x0a, 0x0a, 0x00, 0x3f, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x71, 0x00, 0x75, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, - 0x00, 0x7b, 0x00, 0x7c, 0x00, 0x7c, 0x00, 0x7d, 0x00, 0x7d, 0x00, 0x7d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x78, 0x02, 0x6b, 0x05, 0x62, 0x08, 0x5b, 0x0c, 0x57, 0x0f, 0x54, 0x12, - 0x51, 0x14, 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, - 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x46, 0x24, 0x46, 0x25, 0x46, 0x26, - 0x46, 0x27, 0x45, 0x27, 0x00, 0x72, 0x02, 0x64, 0x05, 0x5b, 0x08, 0x54, - 0x0c, 0x50, 0x0f, 0x4d, 0x12, 0x4a, 0x14, 0x48, 0x17, 0x47, 0x19, 0x46, - 0x1b, 0x45, 0x1d, 0x44, 0x1f, 0x44, 0x20, 0x42, 0x21, 0x42, 0x22, 0x42, - 0x24, 0x41, 0x25, 0x41, 0x26, 0x41, 0x27, 0x40, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x00, 0x3f, - 0x03, 0x03, 0x2d, 0x00, 0x48, 0x00, 0x59, 0x00, 0x63, 0x00, 0x6a, 0x00, - 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, 0x00, 0x77, 0x00, 0x78, 0x00, - 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x66, 0x00, 0x3f, 0x00, 0x03, 0x03, 0x00, 0x2d, 0x00, 0x48, 0x00, 0x59, - 0x00, 0x63, 0x00, 0x6a, 0x00, 0x6e, 0x00, 0x72, 0x00, 0x74, 0x00, 0x76, - 0x00, 0x77, 0x00, 0x78, 0x00, 0x79, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x79, 0x01, 0x6d, 0x04, - 0x65, 0x07, 0x5e, 0x0a, 0x5a, 0x0d, 0x56, 0x0f, 0x54, 0x12, 0x51, 0x14, - 0x4f, 0x16, 0x4e, 0x18, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x4a, 0x1e, - 0x49, 0x1f, 0x48, 0x21, 0x48, 0x22, 0x48, 0x23, 0x47, 0x23, 0x46, 0x25, - 0x00, 0x74, 0x01, 0x66, 0x04, 0x5e, 0x07, 0x57, 0x0a, 0x53, 0x0d, 0x4f, - 0x10, 0x4d, 0x12, 0x4b, 0x14, 0x49, 0x16, 0x48, 0x18, 0x46, 0x1a, 0x45, - 0x1b, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x20, 0x43, 0x21, 0x42, 0x22, 0x42, - 0x23, 0x42, 0x24, 0x41, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, 0x2d, 0x01, 0x01, - 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x64, 0x00, - 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, 0x00, 0x73, 0x00, 0x75, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x5c, 0x00, - 0x2d, 0x00, 0x01, 0x01, 0x00, 0x22, 0x00, 0x3a, 0x00, 0x4a, 0x00, 0x56, - 0x00, 0x5e, 0x00, 0x64, 0x00, 0x69, 0x00, 0x6d, 0x00, 0x6f, 0x00, 0x71, - 0x00, 0x73, 0x00, 0x75, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7a, 0x01, 0x6f, 0x03, 0x67, 0x06, 0x60, 0x08, - 0x5d, 0x0b, 0x59, 0x0d, 0x56, 0x10, 0x53, 0x11, 0x51, 0x14, 0x50, 0x15, - 0x4e, 0x17, 0x4d, 0x19, 0x4c, 0x1a, 0x4b, 0x1b, 0x4b, 0x1d, 0x48, 0x1e, - 0x49, 0x1f, 0x49, 0x20, 0x48, 0x21, 0x48, 0x22, 0x00, 0x75, 0x01, 0x69, - 0x03, 0x61, 0x06, 0x5a, 0x08, 0x56, 0x0b, 0x52, 0x0d, 0x4f, 0x10, 0x4d, - 0x12, 0x4b, 0x14, 0x4a, 0x16, 0x48, 0x17, 0x47, 0x19, 0x46, 0x1a, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x20, 0x43, 0x21, 0x42, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, 0x00, 0x1b, 0x00, - 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x61, 0x00, - 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x48, 0x00, 0x22, 0x00, - 0x00, 0x00, 0x00, 0x1b, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x54, - 0x00, 0x5b, 0x00, 0x61, 0x00, 0x65, 0x00, 0x68, 0x00, 0x6b, 0x00, 0x6e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7b, 0x01, 0x71, 0x02, 0x69, 0x05, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0c, - 0x58, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4e, 0x16, - 0x4e, 0x18, 0x4c, 0x19, 0x4c, 0x1a, 0x4a, 0x1c, 0x4b, 0x1d, 0x49, 0x1e, - 0x49, 0x1f, 0x49, 0x20, 0x00, 0x76, 0x01, 0x6b, 0x03, 0x63, 0x05, 0x5d, - 0x07, 0x58, 0x09, 0x54, 0x0c, 0x52, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, - 0x13, 0x4a, 0x15, 0x49, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x44, 0x1e, 0x44, 0x1f, 0x44, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7a, 0x00, 0x71, - 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x16, 0x00, 0x28, 0x00, - 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x00, 0x59, 0x00, 0x5e, 0x00, - 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7a, 0x00, 0x71, 0x00, 0x59, 0x00, 0x3a, 0x00, 0x1b, 0x00, 0x00, 0x00, - 0x00, 0x16, 0x00, 0x28, 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, - 0x00, 0x59, 0x00, 0x5e, 0x00, 0x62, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7b, 0x00, 0x72, 0x02, - 0x6b, 0x04, 0x64, 0x06, 0x61, 0x08, 0x5d, 0x0a, 0x5a, 0x0c, 0x56, 0x0e, - 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x50, 0x15, 0x4f, 0x16, 0x4e, 0x17, - 0x4d, 0x18, 0x4b, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x4b, 0x1d, 0x49, 0x1d, - 0x00, 0x77, 0x00, 0x6c, 0x02, 0x65, 0x04, 0x5f, 0x06, 0x5a, 0x08, 0x57, - 0x0a, 0x54, 0x0c, 0x51, 0x0e, 0x4f, 0x10, 0x4d, 0x11, 0x4c, 0x13, 0x4a, - 0x15, 0x49, 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x1a, 0x46, 0x1b, 0x45, - 0x1c, 0x45, 0x1d, 0x45, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x63, 0x00, 0x4a, - 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x13, 0x00, 0x23, 0x00, 0x30, 0x00, - 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, 0x00, 0x57, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x75, 0x00, - 0x63, 0x00, 0x4a, 0x00, 0x30, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x13, - 0x00, 0x23, 0x00, 0x30, 0x00, 0x3b, 0x00, 0x44, 0x00, 0x4c, 0x00, 0x52, - 0x00, 0x57, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7c, 0x00, 0x73, 0x02, 0x6c, 0x03, 0x66, 0x05, - 0x63, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x10, - 0x53, 0x11, 0x52, 0x13, 0x50, 0x14, 0x4f, 0x15, 0x4e, 0x17, 0x4c, 0x18, - 0x4c, 0x19, 0x4c, 0x1a, 0x4b, 0x1a, 0x4b, 0x1c, 0x00, 0x77, 0x00, 0x6e, - 0x02, 0x66, 0x04, 0x61, 0x05, 0x5c, 0x07, 0x59, 0x09, 0x56, 0x0b, 0x53, - 0x0d, 0x51, 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, - 0x15, 0x48, 0x17, 0x48, 0x18, 0x47, 0x19, 0x46, 0x1b, 0x46, 0x1b, 0x45, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x28, - 0x00, 0x13, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x35, 0x00, - 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7c, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x56, 0x00, - 0x3f, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2b, 0x00, 0x35, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x4c, 0x00, 0x51, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7c, 0x00, 0x74, 0x02, 0x6e, 0x03, 0x68, 0x05, 0x64, 0x06, 0x60, 0x08, - 0x5d, 0x0a, 0x5a, 0x0b, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, - 0x52, 0x13, 0x50, 0x14, 0x50, 0x15, 0x4d, 0x16, 0x4e, 0x18, 0x4c, 0x18, - 0x4c, 0x19, 0x4c, 0x1a, 0x00, 0x77, 0x00, 0x6f, 0x02, 0x68, 0x03, 0x63, - 0x05, 0x5e, 0x06, 0x5a, 0x08, 0x57, 0x0a, 0x55, 0x0b, 0x53, 0x0d, 0x50, - 0x0f, 0x4f, 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x49, - 0x16, 0x48, 0x18, 0x48, 0x18, 0x46, 0x19, 0x46, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x79, - 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, 0x23, 0x00, 0x10, - 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, 0x00, 0x30, 0x00, 0x39, 0x00, - 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7d, 0x00, 0x79, 0x00, 0x6e, 0x00, 0x5e, 0x00, 0x4b, 0x00, 0x37, 0x00, - 0x23, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x27, - 0x00, 0x30, 0x00, 0x39, 0x00, 0x40, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x75, 0x01, - 0x6f, 0x02, 0x69, 0x04, 0x65, 0x06, 0x62, 0x07, 0x5f, 0x09, 0x5b, 0x0b, - 0x5a, 0x0c, 0x58, 0x0d, 0x56, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, - 0x50, 0x13, 0x4f, 0x15, 0x4e, 0x15, 0x4e, 0x17, 0x4d, 0x18, 0x4c, 0x18, - 0x00, 0x78, 0x00, 0x70, 0x01, 0x69, 0x03, 0x64, 0x04, 0x60, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x52, 0x0d, 0x50, 0x0f, 0x4f, - 0x10, 0x4e, 0x11, 0x4c, 0x13, 0x4b, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x48, - 0x17, 0x48, 0x18, 0x47, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x64, - 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0e, 0x00, 0x00, - 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, 0x00, 0x34, 0x00, 0x3b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7d, 0x00, 0x7a, 0x00, - 0x72, 0x00, 0x64, 0x00, 0x54, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2c, - 0x00, 0x34, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x76, 0x01, 0x70, 0x02, 0x6a, 0x03, - 0x67, 0x05, 0x63, 0x06, 0x60, 0x08, 0x5d, 0x09, 0x5b, 0x0b, 0x59, 0x0c, - 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x10, 0x53, 0x11, 0x52, 0x13, 0x4f, 0x13, - 0x50, 0x15, 0x4f, 0x15, 0x4e, 0x16, 0x4e, 0x18, 0x00, 0x78, 0x00, 0x71, - 0x01, 0x6a, 0x03, 0x66, 0x04, 0x61, 0x05, 0x5d, 0x06, 0x5a, 0x08, 0x58, - 0x09, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0e, 0x50, 0x0f, 0x4e, 0x10, 0x4e, - 0x11, 0x4c, 0x13, 0x4c, 0x13, 0x4a, 0x15, 0x4a, 0x15, 0x49, 0x16, 0x48, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5b, 0x00, 0x4b, - 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x0c, 0x00, - 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7b, 0x00, 0x74, 0x00, 0x69, 0x00, - 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2b, 0x00, 0x1b, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x20, 0x00, 0x29, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7d, 0x00, 0x76, 0x01, 0x71, 0x02, 0x6c, 0x03, 0x68, 0x05, 0x64, 0x06, - 0x61, 0x07, 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x59, 0x0c, 0x57, 0x0e, - 0x55, 0x0e, 0x55, 0x11, 0x52, 0x11, 0x51, 0x12, 0x51, 0x13, 0x50, 0x14, - 0x4f, 0x15, 0x4e, 0x15, 0x00, 0x79, 0x00, 0x71, 0x01, 0x6c, 0x02, 0x66, - 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5c, 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, - 0x0b, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, - 0x13, 0x4c, 0x13, 0x4b, 0x14, 0x4a, 0x15, 0x4a, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, - 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, 0x44, 0x00, 0x35, - 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, - 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x53, 0x00, - 0x44, 0x00, 0x35, 0x00, 0x27, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x77, 0x01, - 0x72, 0x02, 0x6d, 0x03, 0x69, 0x04, 0x66, 0x06, 0x63, 0x07, 0x5f, 0x08, - 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0c, 0x58, 0x0c, 0x57, 0x0e, 0x55, 0x0f, - 0x54, 0x11, 0x51, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x14, 0x50, 0x15, - 0x00, 0x79, 0x00, 0x72, 0x01, 0x6d, 0x02, 0x68, 0x03, 0x63, 0x04, 0x60, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x56, 0x0b, 0x54, 0x0c, 0x53, - 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, - 0x13, 0x4b, 0x14, 0x4a, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, 0x77, 0x00, 0x6f, - 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, 0x30, 0x00, 0x23, - 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x0a, 0x00, 0x13, 0x00, 0x1b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7c, 0x00, - 0x77, 0x00, 0x6f, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x3e, 0x00, - 0x30, 0x00, 0x23, 0x00, 0x16, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x13, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7d, 0x00, 0x78, 0x01, 0x72, 0x02, 0x6e, 0x02, - 0x6a, 0x03, 0x67, 0x05, 0x64, 0x06, 0x60, 0x07, 0x5f, 0x09, 0x5c, 0x0a, - 0x5b, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, 0x55, 0x0f, 0x53, 0x11, - 0x52, 0x11, 0x52, 0x12, 0x51, 0x13, 0x50, 0x13, 0x00, 0x79, 0x00, 0x73, - 0x01, 0x6d, 0x02, 0x69, 0x03, 0x65, 0x04, 0x61, 0x05, 0x5e, 0x06, 0x5c, - 0x07, 0x59, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, 0x0d, 0x53, 0x0d, 0x51, - 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4c, 0x12, 0x4c, 0x13, 0x4b, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5e, - 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x15, - 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x71, 0x00, - 0x68, 0x00, 0x5e, 0x00, 0x52, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, - 0x20, 0x00, 0x15, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x7e, 0x00, 0x78, 0x01, 0x73, 0x02, 0x6e, 0x02, 0x6b, 0x03, 0x68, 0x05, - 0x65, 0x06, 0x62, 0x07, 0x5f, 0x07, 0x5e, 0x09, 0x5c, 0x0b, 0x5a, 0x0b, - 0x59, 0x0c, 0x57, 0x0d, 0x56, 0x0e, 0x54, 0x0f, 0x54, 0x11, 0x52, 0x11, - 0x52, 0x12, 0x51, 0x13, 0x00, 0x79, 0x00, 0x73, 0x01, 0x6e, 0x02, 0x69, - 0x03, 0x66, 0x04, 0x62, 0x05, 0x5f, 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, - 0x09, 0x56, 0x0b, 0x55, 0x0b, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, - 0x0f, 0x4e, 0x11, 0x4e, 0x11, 0x4d, 0x12, 0x4c, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, - 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, 0x57, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x09, - 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7e, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x73, 0x00, 0x6b, 0x00, 0x62, 0x00, - 0x57, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1e, 0x00, - 0x13, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x78, 0x00, - 0x74, 0x01, 0x6f, 0x02, 0x6c, 0x03, 0x68, 0x04, 0x65, 0x05, 0x62, 0x06, - 0x61, 0x07, 0x5f, 0x09, 0x5c, 0x09, 0x5b, 0x0b, 0x5a, 0x0b, 0x58, 0x0c, - 0x57, 0x0d, 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x52, 0x12, - 0x00, 0x79, 0x00, 0x74, 0x00, 0x6f, 0x02, 0x6a, 0x03, 0x66, 0x04, 0x63, - 0x05, 0x60, 0x05, 0x5e, 0x06, 0x5b, 0x07, 0x59, 0x09, 0x58, 0x09, 0x55, - 0x0b, 0x55, 0x0c, 0x53, 0x0d, 0x52, 0x0d, 0x50, 0x0f, 0x50, 0x0f, 0x4e, - 0x11, 0x4e, 0x11, 0x4d, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x75, - 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x46, 0x00, 0x3b, - 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x7d, 0x00, - 0x7a, 0x00, 0x75, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, - 0x46, 0x00, 0x3b, 0x00, 0x30, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x12, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x7e, 0x00, 0x79, 0x00, 0x74, 0x01, 0x70, 0x02, - 0x6c, 0x03, 0x69, 0x03, 0x66, 0x05, 0x63, 0x06, 0x62, 0x07, 0x5f, 0x07, - 0x5e, 0x09, 0x5c, 0x0a, 0x5a, 0x0b, 0x5a, 0x0c, 0x57, 0x0c, 0x56, 0x0e, - 0x55, 0x0e, 0x55, 0x0f, 0x54, 0x11, 0x52, 0x11, 0x00, 0x79, 0x00, 0x74, - 0x00, 0x70, 0x01, 0x6b, 0x02, 0x67, 0x03, 0x64, 0x04, 0x61, 0x05, 0x5f, - 0x06, 0x5d, 0x07, 0x5b, 0x08, 0x58, 0x09, 0x57, 0x0a, 0x55, 0x0b, 0x54, - 0x0c, 0x53, 0x0d, 0x51, 0x0e, 0x50, 0x0f, 0x50, 0x0f, 0x4e, 0x11, 0x4e, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x66, 0x7f, 0x4d, 0x68, 0x49, 0x5c, 0x47, 0x55, 0x45, 0x51, 0x45, 0x4e, - 0x44, 0x4c, 0x43, 0x4a, 0x43, 0x49, 0x43, 0x48, 0x43, 0x47, 0x42, 0x47, - 0x42, 0x46, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, 0x42, 0x44, - 0x42, 0x44, 0x42, 0x44, 0x57, 0x7f, 0x2e, 0x58, 0x34, 0x4d, 0x37, 0x49, - 0x39, 0x47, 0x3a, 0x45, 0x3b, 0x44, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, - 0x3c, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x99, 0x7f, 0x4d, 0x58, - 0x49, 0x4d, 0x47, 0x49, 0x45, 0x47, 0x45, 0x45, 0x44, 0x44, 0x43, 0x44, - 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x42, 0x42, 0x42, 0x42, 0x42, 0x42, - 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa5, 0x16, 0x74, 0x23, - 0x62, 0x29, 0x59, 0x2d, 0x53, 0x30, 0x50, 0x32, 0x4e, 0x34, 0x4c, 0x35, - 0x4a, 0x36, 0x49, 0x37, 0x48, 0x38, 0x48, 0x38, 0x47, 0x39, 0x46, 0x39, - 0x46, 0x39, 0x46, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x45, 0x3a, 0x44, 0x3b, - 0x23, 0x2f, 0x25, 0x35, 0x2a, 0x37, 0x2e, 0x39, 0x31, 0x3a, 0x33, 0x3b, - 0x34, 0x3b, 0x35, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x38, 0x3d, - 0x39, 0x3d, 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x77, 0x27, 0x5f, 0x31, 0x56, 0x35, 0x52, 0x38, - 0x4f, 0x39, 0x4c, 0x3a, 0x4b, 0x3b, 0x49, 0x3b, 0x48, 0x3c, 0x48, 0x3c, - 0x47, 0x3c, 0x47, 0x3c, 0x46, 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, - 0x45, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x44, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, 0x00, - 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xbb, 0x01, 0x91, 0x09, 0x78, 0x11, 0x6b, 0x17, - 0x62, 0x1c, 0x5c, 0x20, 0x58, 0x23, 0x55, 0x25, 0x52, 0x28, 0x51, 0x2a, - 0x4f, 0x2b, 0x4e, 0x2c, 0x4d, 0x2e, 0x4c, 0x2f, 0x4b, 0x30, 0x4a, 0x31, - 0x49, 0x31, 0x49, 0x32, 0x48, 0x33, 0x47, 0x33, 0x22, 0x25, 0x23, 0x2a, - 0x26, 0x2e, 0x2a, 0x31, 0x2c, 0x33, 0x2e, 0x34, 0x30, 0x36, 0x31, 0x37, - 0x32, 0x37, 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x3a, - 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3a, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, - 0x7a, 0x1a, 0x68, 0x24, 0x5f, 0x2a, 0x5a, 0x2e, 0x55, 0x31, 0x53, 0x33, - 0x51, 0x34, 0x4f, 0x35, 0x4d, 0x36, 0x4c, 0x37, 0x4b, 0x38, 0x4b, 0x38, - 0x4a, 0x39, 0x49, 0x39, 0x49, 0x39, 0x48, 0x3a, 0x47, 0x3a, 0x47, 0x3a, - 0x46, 0x3b, 0x46, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x39, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xc6, 0x00, 0xa3, 0x02, 0x8a, 0x07, 0x7a, 0x0c, 0x6f, 0x11, 0x68, 0x15, - 0x62, 0x18, 0x5e, 0x1b, 0x5b, 0x1d, 0x58, 0x20, 0x56, 0x22, 0x54, 0x23, - 0x53, 0x25, 0x51, 0x26, 0x50, 0x28, 0x4e, 0x29, 0x4d, 0x2a, 0x4d, 0x2b, - 0x4d, 0x2c, 0x4c, 0x2c, 0x22, 0x23, 0x22, 0x27, 0x25, 0x2a, 0x27, 0x2c, - 0x2a, 0x2e, 0x2b, 0x30, 0x2d, 0x32, 0x2e, 0x33, 0x30, 0x34, 0x31, 0x34, - 0x32, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x34, 0x37, 0x35, 0x38, - 0x36, 0x38, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x7b, 0x16, 0x6d, 0x1f, - 0x65, 0x24, 0x5f, 0x28, 0x5b, 0x2b, 0x58, 0x2d, 0x55, 0x2f, 0x54, 0x31, - 0x52, 0x32, 0x50, 0x33, 0x4f, 0x34, 0x4e, 0x35, 0x4d, 0x35, 0x4c, 0x36, - 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x4a, 0x38, 0x4a, 0x39, 0x49, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, - 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xcc, 0x00, 0xaf, 0x00, - 0x98, 0x03, 0x87, 0x06, 0x7b, 0x0a, 0x73, 0x0e, 0x6c, 0x11, 0x67, 0x13, - 0x63, 0x16, 0x5f, 0x18, 0x5c, 0x1a, 0x5a, 0x1c, 0x58, 0x1e, 0x56, 0x20, - 0x55, 0x21, 0x54, 0x22, 0x52, 0x24, 0x51, 0x25, 0x50, 0x26, 0x4f, 0x27, - 0x22, 0x22, 0x22, 0x25, 0x24, 0x27, 0x26, 0x2a, 0x28, 0x2b, 0x29, 0x2d, - 0x2b, 0x2f, 0x2c, 0x30, 0x2d, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, - 0x31, 0x34, 0x32, 0x35, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, - 0x35, 0x36, 0x35, 0x37, 0x7c, 0x15, 0x71, 0x1b, 0x68, 0x20, 0x63, 0x24, - 0x5f, 0x27, 0x5c, 0x29, 0x59, 0x2c, 0x57, 0x2d, 0x55, 0x2f, 0x54, 0x30, - 0x53, 0x31, 0x51, 0x32, 0x50, 0x32, 0x4f, 0x33, 0x4f, 0x34, 0x4e, 0x35, - 0x4d, 0x35, 0x4c, 0x35, 0x4b, 0x35, 0x4a, 0x36, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, - 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, - 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xcf, 0x00, 0xb7, 0x00, 0xa2, 0x00, 0x92, 0x03, - 0x85, 0x06, 0x7c, 0x09, 0x75, 0x0b, 0x6f, 0x0e, 0x6a, 0x11, 0x66, 0x13, - 0x63, 0x15, 0x5f, 0x17, 0x5e, 0x19, 0x5c, 0x1a, 0x5a, 0x1c, 0x57, 0x1d, - 0x56, 0x1f, 0x55, 0x1f, 0x55, 0x21, 0x54, 0x22, 0x22, 0x22, 0x22, 0x24, - 0x23, 0x26, 0x25, 0x28, 0x26, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, - 0x2c, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x31, 0x30, 0x32, 0x30, 0x33, - 0x30, 0x33, 0x32, 0x33, 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, - 0x7c, 0x14, 0x73, 0x19, 0x6c, 0x1e, 0x67, 0x22, 0x63, 0x24, 0x5f, 0x26, - 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2f, - 0x53, 0x30, 0x52, 0x31, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4f, 0x33, - 0x4f, 0x34, 0x4e, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, - 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd2, 0x00, 0xbd, 0x00, 0xaa, 0x00, 0x9b, 0x01, 0x8e, 0x03, 0x84, 0x05, - 0x7c, 0x08, 0x76, 0x0a, 0x71, 0x0c, 0x6c, 0x0f, 0x69, 0x11, 0x66, 0x12, - 0x63, 0x14, 0x60, 0x16, 0x5e, 0x17, 0x5d, 0x19, 0x5b, 0x1a, 0x59, 0x1b, - 0x57, 0x1c, 0x56, 0x1e, 0x22, 0x22, 0x22, 0x23, 0x23, 0x25, 0x24, 0x27, - 0x25, 0x28, 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, - 0x2d, 0x2f, 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x32, 0x30, 0x33, - 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x33, 0x7d, 0x13, 0x75, 0x18, - 0x6e, 0x1c, 0x69, 0x1f, 0x65, 0x22, 0x62, 0x24, 0x60, 0x26, 0x5c, 0x28, - 0x5b, 0x29, 0x59, 0x2a, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x53, 0x31, 0x51, 0x31, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, - 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd3, 0x00, 0xc2, 0x00, - 0xb1, 0x00, 0xa2, 0x00, 0x96, 0x02, 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x07, - 0x77, 0x09, 0x73, 0x0b, 0x6f, 0x0d, 0x6a, 0x0f, 0x68, 0x11, 0x66, 0x12, - 0x63, 0x14, 0x60, 0x15, 0x5f, 0x16, 0x5e, 0x18, 0x5c, 0x19, 0x5a, 0x1a, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x29, - 0x27, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x31, 0x30, 0x32, - 0x30, 0x33, 0x30, 0x33, 0x7d, 0x13, 0x76, 0x17, 0x70, 0x1b, 0x6b, 0x1e, - 0x68, 0x20, 0x65, 0x22, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x29, - 0x5a, 0x2a, 0x58, 0x2b, 0x57, 0x2c, 0x57, 0x2d, 0x55, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x53, 0x30, 0x53, 0x31, 0x51, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, - 0x00, 0x25, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, - 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd4, 0x00, 0xc5, 0x00, 0xb6, 0x00, 0xa8, 0x00, - 0x9c, 0x00, 0x92, 0x02, 0x8a, 0x03, 0x82, 0x05, 0x7d, 0x07, 0x78, 0x09, - 0x74, 0x0a, 0x71, 0x0c, 0x6c, 0x0e, 0x69, 0x0f, 0x68, 0x11, 0x65, 0x12, - 0x63, 0x13, 0x60, 0x14, 0x5f, 0x16, 0x5e, 0x17, 0x21, 0x22, 0x22, 0x23, - 0x22, 0x24, 0x23, 0x25, 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x29, - 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x30, 0x31, - 0x7d, 0x12, 0x77, 0x16, 0x71, 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x66, 0x20, - 0x64, 0x23, 0x61, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x28, 0x5b, 0x2a, - 0x58, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, - 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, - 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd5, 0x00, 0xc7, 0x00, 0xba, 0x00, 0xad, 0x00, 0xa2, 0x00, 0x98, 0x01, - 0x8f, 0x02, 0x88, 0x03, 0x82, 0x05, 0x7e, 0x07, 0x78, 0x08, 0x74, 0x0a, - 0x72, 0x0b, 0x6e, 0x0c, 0x6b, 0x0e, 0x69, 0x0f, 0x67, 0x11, 0x65, 0x12, - 0x63, 0x13, 0x60, 0x14, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, - 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x27, 0x29, 0x28, 0x2b, - 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x2e, 0x30, 0x2f, 0x30, 0x7d, 0x12, 0x77, 0x15, - 0x72, 0x18, 0x6e, 0x1b, 0x6b, 0x1d, 0x68, 0x20, 0x65, 0x21, 0x64, 0x23, - 0x60, 0x24, 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x59, 0x2a, - 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x54, 0x2e, 0x53, 0x2e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, - 0x00, 0x3a, 0x00, 0x34, 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, - 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, - 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd6, 0x00, 0xc9, 0x00, - 0xbd, 0x00, 0xb2, 0x00, 0xa7, 0x00, 0x9d, 0x00, 0x95, 0x01, 0x8e, 0x02, - 0x87, 0x03, 0x82, 0x05, 0x7e, 0x06, 0x79, 0x08, 0x75, 0x09, 0x73, 0x0b, - 0x70, 0x0c, 0x6c, 0x0d, 0x6a, 0x0e, 0x68, 0x0f, 0x67, 0x11, 0x65, 0x12, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x26, - 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, - 0x2a, 0x2b, 0x2b, 0x2c, 0x2a, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, - 0x2d, 0x2f, 0x2d, 0x30, 0x7d, 0x12, 0x78, 0x15, 0x73, 0x18, 0x70, 0x1a, - 0x6d, 0x1d, 0x69, 0x1e, 0x67, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x24, - 0x60, 0x26, 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5a, 0x2a, 0x58, 0x2a, - 0x57, 0x2a, 0x57, 0x2b, 0x57, 0x2c, 0x56, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, - 0x00, 0x30, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, - 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd7, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb5, 0x00, - 0xab, 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x92, 0x02, 0x8d, 0x02, 0x87, 0x03, - 0x81, 0x05, 0x7e, 0x06, 0x79, 0x07, 0x76, 0x09, 0x74, 0x0a, 0x71, 0x0b, - 0x6e, 0x0c, 0x6b, 0x0d, 0x6a, 0x0e, 0x68, 0x10, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x23, 0x23, 0x24, 0x23, 0x25, 0x24, 0x25, 0x25, 0x27, 0x25, 0x27, - 0x27, 0x28, 0x27, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, - 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2e, - 0x7e, 0x12, 0x79, 0x15, 0x75, 0x17, 0x71, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, - 0x69, 0x1f, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x25, - 0x5d, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x5c, 0x29, 0x59, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, - 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, - 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd7, 0x00, 0xcc, 0x00, 0xc2, 0x00, 0xb9, 0x00, 0xae, 0x00, 0xa6, 0x00, - 0x9e, 0x00, 0x97, 0x01, 0x90, 0x02, 0x8b, 0x02, 0x85, 0x04, 0x81, 0x05, - 0x7e, 0x06, 0x7a, 0x07, 0x76, 0x08, 0x74, 0x09, 0x72, 0x0b, 0x6f, 0x0c, - 0x6c, 0x0c, 0x6a, 0x0e, 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, - 0x23, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, - 0x26, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x7e, 0x12, 0x79, 0x15, - 0x75, 0x17, 0x72, 0x19, 0x6e, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x20, - 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x5a, 0x2a, 0x58, 0x2a, 0x57, 0x2a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3c, 0x00, 0x38, 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, - 0x00, 0x1c, 0x00, 0x16, 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xce, 0x00, - 0xc5, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa1, 0x00, 0x9b, 0x00, - 0x94, 0x01, 0x8f, 0x02, 0x8a, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, - 0x7b, 0x07, 0x77, 0x08, 0x75, 0x09, 0x73, 0x0a, 0x71, 0x0b, 0x6e, 0x0c, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, - 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, - 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, - 0x2b, 0x2d, 0x2b, 0x2d, 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x16, 0x72, 0x18, - 0x6f, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, - 0x64, 0x23, 0x61, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x28, 0x5b, 0x2a, 0x59, 0x2a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, - 0x00, 0x35, 0x00, 0x31, 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, - 0x00, 0x14, 0x00, 0x0f, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, - 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, - 0xb5, 0x00, 0xad, 0x00, 0xa6, 0x00, 0x9e, 0x00, 0x99, 0x00, 0x92, 0x01, - 0x8e, 0x02, 0x89, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7b, 0x07, - 0x77, 0x07, 0x75, 0x09, 0x73, 0x09, 0x72, 0x0b, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, - 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, - 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, - 0x7e, 0x12, 0x7a, 0x14, 0x76, 0x15, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, - 0x6c, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, - 0x00, 0x2d, 0x00, 0x28, 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, - 0x00, 0x0d, 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, - 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, - 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbf, 0x00, 0xb8, 0x00, 0xaf, 0x00, - 0xa9, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x01, 0x90, 0x02, 0x8d, 0x02, - 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x07, 0x77, 0x07, - 0x76, 0x09, 0x74, 0x09, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, - 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, - 0x26, 0x27, 0x27, 0x27, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, - 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7a, 0x13, - 0x76, 0x15, 0x74, 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, - 0x69, 0x1e, 0x67, 0x20, 0x65, 0x20, 0x65, 0x21, 0x63, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, - 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x58, 0x00, 0x3d, - 0x00, 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, - 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x0b, 0x07, 0x17, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3a, 0x00, 0x3c, - 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x47, 0x07, 0x0f, 0x0f, - 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, - 0x00, 0x3f, 0x00, 0x3f, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd1, 0x00, - 0xc9, 0x00, 0xc1, 0x00, 0xba, 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, - 0x9e, 0x00, 0x9a, 0x00, 0x93, 0x01, 0x8f, 0x02, 0x8c, 0x02, 0x88, 0x03, - 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7b, 0x06, 0x78, 0x07, 0x76, 0x08, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, - 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, 0x27, 0x27, - 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, - 0x2a, 0x2b, 0x2b, 0x2b, 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x75, 0x17, - 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x69, 0x1f, - 0x65, 0x20, 0x65, 0x20, 0x65, 0x22, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x25, 0x5f, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, - 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, - 0x00, 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x27, 0x02, - 0x02, 0x12, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x38, 0x00, 0x3a, - 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3e, 0x7f, 0x00, 0x3f, 0x00, 0x05, 0x05, 0x00, 0x1c, - 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, - 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc3, 0x00, - 0xbb, 0x00, 0xb5, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, - 0x98, 0x00, 0x92, 0x01, 0x8e, 0x02, 0x8b, 0x02, 0x87, 0x03, 0x83, 0x04, - 0x81, 0x05, 0x7f, 0x06, 0x7c, 0x06, 0x78, 0x07, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, - 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, - 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, - 0x7e, 0x11, 0x7b, 0x13, 0x77, 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, - 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x68, 0x20, 0x65, 0x20, - 0x65, 0x20, 0x65, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x25, - 0x5f, 0x27, 0x5c, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, - 0x00, 0x24, 0x00, 0x2c, 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, - 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, - 0x00, 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, - 0x00, 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, - 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9f, 0x00, 0x6f, 0x00, 0x22, 0x00, 0x09, 0x0b, - 0x00, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x33, 0x00, 0x35, - 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, - 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, 0x00, 0x09, 0x00, 0x19, - 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, - 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xd9, 0x00, 0xd2, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb7, 0x00, - 0xb0, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x95, 0x01, - 0x91, 0x01, 0x8e, 0x02, 0x8a, 0x02, 0x86, 0x03, 0x83, 0x04, 0x81, 0x05, - 0x7f, 0x06, 0x7c, 0x06, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, - 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, - 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x7f, 0x11, 0x7b, 0x13, - 0x77, 0x15, 0x76, 0x16, 0x72, 0x18, 0x72, 0x19, 0x6d, 0x1a, 0x6d, 0x1b, - 0x6b, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, - 0x64, 0x22, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x5f, 0x26, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, - 0x00, 0x25, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, - 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x4c, 0x00, 0x42, - 0x00, 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, - 0x00, 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, - 0x00, 0x39, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, 0x00, 0x0e, 0x08, 0x02, 0x0e, - 0x00, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, 0x2f, 0x00, 0x31, - 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0xb2, 0x00, 0x9c, 0x00, - 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x16, - 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, - 0x00, 0x33, 0x00, 0x35, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xda, 0x00, 0xd2, 0x00, - 0xcb, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xac, 0x00, - 0xa8, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x01, 0x90, 0x02, - 0x8d, 0x02, 0x89, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x06, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, - 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, - 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x29, 0x7f, 0x11, 0x7b, 0x13, 0x78, 0x15, 0x76, 0x15, - 0x73, 0x18, 0x72, 0x18, 0x6f, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, - 0x69, 0x1d, 0x69, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x65, 0x21, 0x64, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, - 0x00, 0x25, 0x00, 0x2a, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, - 0x00, 0x35, 0x00, 0x37, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, - 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, - 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb5, 0x00, 0xa4, 0x00, - 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x00, 0x12, 0x06, 0x07, 0x0c, 0x00, 0x10, - 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, 0x2c, 0x00, 0x2e, - 0x00, 0x30, 0x00, 0x32, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, - 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, - 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, - 0x33, 0x22, 0x28, 0x22, 0x25, 0x22, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, - 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, - 0x22, 0x21, 0x22, 0x21, 0x9d, 0x05, 0x6b, 0x0d, 0x51, 0x12, 0x45, 0x15, - 0x3d, 0x17, 0x38, 0x18, 0x35, 0x1a, 0x32, 0x1b, 0x30, 0x1b, 0x2f, 0x1c, - 0x2d, 0x1c, 0x2d, 0x1c, 0x2c, 0x1d, 0x2b, 0x1d, 0x2a, 0x1e, 0x2a, 0x1e, - 0x29, 0x1e, 0x29, 0x1e, 0x28, 0x1e, 0x28, 0x1e, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x5f, 0x13, 0x46, 0x17, 0x39, 0x1a, 0x33, 0x1b, 0x2f, 0x1c, 0x2d, 0x1d, - 0x2b, 0x1e, 0x2a, 0x1e, 0x29, 0x1e, 0x28, 0x1f, 0x27, 0x1f, 0x27, 0x1f, - 0x27, 0x1f, 0x26, 0x1f, 0x26, 0x20, 0x26, 0x20, 0x25, 0x20, 0x25, 0x20, - 0x25, 0x20, 0x25, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, - 0x00, 0x25, 0x00, 0x29, 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, - 0x00, 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, - 0x00, 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xb9, 0x00, 0xad, 0x00, 0x8f, 0x00, 0x68, 0x00, - 0x42, 0x00, 0x20, 0x00, 0x14, 0x05, 0x0b, 0x0a, 0x04, 0x0d, 0x00, 0x12, - 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, 0x29, 0x00, 0x2c, - 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, - 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x13, - 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x37, 0x28, 0x2d, 0x25, - 0x28, 0x23, 0x26, 0x23, 0x25, 0x23, 0x24, 0x22, 0x24, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0xbc, 0x00, 0x8e, 0x01, 0x73, 0x04, 0x60, 0x08, 0x55, 0x0b, 0x4c, 0x0d, - 0x47, 0x0f, 0x42, 0x11, 0x3e, 0x12, 0x3b, 0x13, 0x39, 0x14, 0x37, 0x15, - 0x36, 0x16, 0x34, 0x16, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x19, - 0x2f, 0x19, 0x2f, 0x19, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x6f, 0x11, 0x58, 0x11, - 0x4a, 0x13, 0x41, 0x15, 0x3b, 0x16, 0x37, 0x17, 0x34, 0x18, 0x32, 0x19, - 0x30, 0x1a, 0x2e, 0x1a, 0x2d, 0x1b, 0x2c, 0x1b, 0x2c, 0x1c, 0x2b, 0x1c, - 0x2a, 0x1c, 0x2a, 0x1d, 0x29, 0x1d, 0x29, 0x1d, 0x28, 0x1d, 0x28, 0x1d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, - 0x00, 0x26, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x3e, 0x00, 0x3a, - 0x00, 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, - 0x00, 0x2b, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, 0x00, 0x5c, 0x00, 0x3c, 0x00, - 0x20, 0x00, 0x16, 0x04, 0x0e, 0x08, 0x07, 0x0c, 0x02, 0x0e, 0x00, 0x13, - 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0xbb, 0x00, 0xb5, 0x00, - 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, - 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, 0x00, 0x0c, 0x00, 0x12, - 0x00, 0x17, 0x00, 0x1c, 0x38, 0x2c, 0x30, 0x28, 0x2b, 0x26, 0x29, 0x25, - 0x27, 0x24, 0x26, 0x24, 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x22, - 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0xc6, 0x00, 0xa4, 0x00, - 0x89, 0x00, 0x77, 0x02, 0x67, 0x04, 0x5e, 0x06, 0x55, 0x08, 0x50, 0x0a, - 0x4b, 0x0c, 0x47, 0x0d, 0x44, 0x0e, 0x41, 0x0f, 0x3e, 0x10, 0x3d, 0x11, - 0x3b, 0x12, 0x39, 0x12, 0x38, 0x13, 0x37, 0x14, 0x35, 0x15, 0x35, 0x16, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x74, 0x11, 0x63, 0x11, 0x55, 0x11, 0x4c, 0x12, - 0x44, 0x13, 0x40, 0x14, 0x3b, 0x15, 0x39, 0x16, 0x36, 0x17, 0x34, 0x17, - 0x33, 0x18, 0x31, 0x18, 0x30, 0x19, 0x2f, 0x19, 0x2e, 0x1a, 0x2d, 0x1a, - 0x2d, 0x1a, 0x2c, 0x1b, 0x2b, 0x1b, 0x2b, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x0f, 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, - 0x00, 0x26, 0x00, 0x28, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, - 0x00, 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, - 0x00, 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb5, 0x00, - 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, 0x00, 0x37, 0x00, 0x20, 0x00, - 0x17, 0x04, 0x10, 0x07, 0x0a, 0x0a, 0x05, 0x0d, 0x00, 0x0f, 0x00, 0x14, - 0x00, 0x19, 0x00, 0x1d, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, - 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, - 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, - 0x3a, 0x2f, 0x32, 0x2b, 0x2e, 0x28, 0x2b, 0x27, 0x28, 0x25, 0x27, 0x25, - 0x27, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, - 0x24, 0x23, 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x23, 0x22, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, - 0x78, 0x01, 0x6c, 0x03, 0x64, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x51, 0x09, - 0x4e, 0x0a, 0x4a, 0x0b, 0x48, 0x0c, 0x45, 0x0c, 0x43, 0x0e, 0x40, 0x0f, - 0x3f, 0x0f, 0x3d, 0x0f, 0x3c, 0x11, 0x3b, 0x12, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x77, 0x11, 0x69, 0x11, 0x5e, 0x11, 0x54, 0x11, 0x4d, 0x11, 0x47, 0x12, - 0x43, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x39, 0x15, 0x38, 0x16, 0x36, 0x16, - 0x35, 0x17, 0x33, 0x17, 0x32, 0x18, 0x31, 0x18, 0x30, 0x18, 0x2f, 0x18, - 0x2f, 0x19, 0x2e, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x0d, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, - 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, - 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, - 0x00, 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xaa, 0x00, 0x96, 0x00, - 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, 0x00, 0x20, 0x00, 0x18, 0x03, - 0x11, 0x06, 0x0c, 0x09, 0x07, 0x0c, 0x03, 0x0e, 0x00, 0x10, 0x00, 0x15, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, - 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, - 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, 0x3b, 0x32, 0x34, 0x2d, - 0x2f, 0x2a, 0x2c, 0x28, 0x2a, 0x27, 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, - 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x23, 0x24, 0x23, - 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x22, - 0xcf, 0x00, 0xb9, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x01, - 0x70, 0x02, 0x68, 0x03, 0x61, 0x04, 0x5c, 0x05, 0x57, 0x07, 0x53, 0x07, - 0x50, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0c, 0x45, 0x0c, 0x44, 0x0c, - 0x42, 0x0d, 0x41, 0x0f, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x78, 0x11, 0x6d, 0x11, - 0x63, 0x11, 0x5b, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x49, 0x12, 0x45, 0x12, - 0x41, 0x13, 0x3f, 0x13, 0x3c, 0x14, 0x3a, 0x14, 0x39, 0x15, 0x37, 0x16, - 0x36, 0x16, 0x35, 0x17, 0x33, 0x17, 0x33, 0x17, 0x32, 0x17, 0x31, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, - 0x00, 0x11, 0x00, 0x16, 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x3e, 0x00, 0x3d, - 0x00, 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, - 0x00, 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, - 0x00, 0x1a, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, 0x00, 0x89, 0x00, 0x73, 0x00, - 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x19, 0x03, 0x13, 0x06, - 0x0e, 0x08, 0x09, 0x0b, 0x05, 0x0d, 0x01, 0x0e, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, - 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, - 0x0b, 0x00, 0x03, 0x00, 0x3c, 0x34, 0x35, 0x2f, 0x31, 0x2c, 0x2e, 0x2a, - 0x2c, 0x28, 0x2a, 0x27, 0x28, 0x27, 0x28, 0x26, 0x27, 0x25, 0x27, 0x25, - 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, - 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, 0xd2, 0x00, 0xbe, 0x00, - 0xad, 0x00, 0x9d, 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, - 0x6b, 0x02, 0x65, 0x04, 0x60, 0x04, 0x5b, 0x05, 0x58, 0x06, 0x54, 0x07, - 0x51, 0x07, 0x4f, 0x09, 0x4c, 0x0a, 0x4a, 0x0a, 0x48, 0x0b, 0x46, 0x0c, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7a, 0x11, 0x70, 0x11, 0x67, 0x11, 0x5f, 0x11, - 0x59, 0x11, 0x53, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x46, 0x12, 0x43, 0x13, - 0x41, 0x13, 0x3e, 0x13, 0x3d, 0x14, 0x3b, 0x14, 0x39, 0x14, 0x38, 0x15, - 0x37, 0x16, 0x36, 0x16, 0x35, 0x16, 0x34, 0x17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, - 0x00, 0x14, 0x00, 0x18, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, - 0x00, 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, 0x00, 0x6a, 0x00, 0x56, 0x00, - 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x19, 0x03, 0x14, 0x05, 0x0f, 0x08, - 0x0b, 0x0a, 0x07, 0x0c, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, - 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, - 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x00, - 0x3c, 0x35, 0x36, 0x31, 0x32, 0x2e, 0x2f, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, - 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, - 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, - 0x24, 0x23, 0x24, 0x23, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, - 0x98, 0x00, 0x8d, 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x01, 0x6e, 0x02, - 0x67, 0x02, 0x63, 0x04, 0x5f, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x07, - 0x52, 0x07, 0x50, 0x07, 0x4e, 0x09, 0x4c, 0x0a, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7a, 0x11, 0x72, 0x11, 0x6a, 0x11, 0x63, 0x11, 0x5d, 0x11, 0x57, 0x11, - 0x52, 0x11, 0x4e, 0x11, 0x4a, 0x11, 0x48, 0x12, 0x44, 0x12, 0x42, 0x13, - 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, - 0x38, 0x15, 0x37, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, - 0x00, 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa8, 0x00, - 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, - 0x2e, 0x00, 0x1f, 0x00, 0x1a, 0x02, 0x15, 0x05, 0x10, 0x07, 0x0c, 0x09, - 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, - 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, - 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, 0x3c, 0x36, 0x37, 0x32, - 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2b, 0x2d, 0x2a, 0x2b, 0x28, 0x2a, 0x28, - 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, - 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, - 0xd5, 0x00, 0xc5, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, - 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x01, 0x6f, 0x01, 0x69, 0x02, - 0x66, 0x02, 0x61, 0x04, 0x5e, 0x04, 0x5a, 0x05, 0x58, 0x05, 0x55, 0x06, - 0x53, 0x07, 0x51, 0x07, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x73, 0x11, - 0x6c, 0x11, 0x66, 0x11, 0x60, 0x11, 0x5b, 0x11, 0x56, 0x11, 0x52, 0x11, - 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, 0x12, 0x44, 0x12, 0x41, 0x13, - 0x40, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x14, 0x3a, 0x14, 0x39, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, - 0x00, 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x09, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x8f, 0x00, - 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2d, 0x00, - 0x1f, 0x00, 0x1a, 0x02, 0x16, 0x04, 0x11, 0x06, 0xbe, 0x00, 0xbc, 0x00, - 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, - 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, - 0x2c, 0x00, 0x23, 0x00, 0x3c, 0x37, 0x38, 0x33, 0x34, 0x30, 0x31, 0x2e, - 0x2f, 0x2c, 0x2d, 0x2a, 0x2c, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, - 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, 0x25, 0x24, 0xd5, 0x00, 0xc8, 0x00, - 0xbb, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, - 0x82, 0x00, 0x7c, 0x00, 0x76, 0x00, 0x70, 0x01, 0x6c, 0x02, 0x67, 0x02, - 0x64, 0x03, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x58, 0x05, 0x55, 0x05, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7b, 0x11, 0x75, 0x11, 0x6e, 0x11, 0x68, 0x11, - 0x63, 0x11, 0x5e, 0x11, 0x5a, 0x11, 0x56, 0x11, 0x52, 0x11, 0x4f, 0x11, - 0x4c, 0x11, 0x49, 0x11, 0x47, 0x12, 0x44, 0x12, 0x43, 0x12, 0x41, 0x13, - 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x09, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, - 0x00, 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, - 0x00, 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x86, 0x00, 0x77, 0x00, - 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2c, 0x00, 0x1f, 0x00, - 0x1b, 0x02, 0x16, 0x04, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, - 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, - 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x00, - 0x3d, 0x38, 0x38, 0x34, 0x35, 0x31, 0x33, 0x2f, 0x30, 0x2d, 0x2e, 0x2c, - 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x27, 0x28, 0x27, - 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, - 0x25, 0x25, 0x25, 0x24, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, - 0xaa, 0x00, 0xa0, 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, - 0x7d, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6d, 0x01, 0x69, 0x02, 0x66, 0x02, - 0x63, 0x04, 0x60, 0x04, 0x5d, 0x04, 0x5b, 0x05, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7c, 0x11, 0x76, 0x11, 0x70, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x61, 0x11, - 0x5d, 0x11, 0x59, 0x11, 0x55, 0x11, 0x52, 0x11, 0x4f, 0x11, 0x4c, 0x11, - 0x49, 0x11, 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x13, 0x41, 0x13, - 0x3f, 0x13, 0x3e, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, - 0x00, 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, - 0x00, 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xb0, 0x00, - 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, 0x00, 0x70, 0x00, 0x61, 0x00, - 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x1b, 0x02, - 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, - 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, - 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x3d, 0x38, 0x39, 0x35, - 0x36, 0x32, 0x33, 0x30, 0x31, 0x2e, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, - 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, - 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, - 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, - 0x9d, 0x00, 0x96, 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, - 0x77, 0x00, 0x73, 0x01, 0x6f, 0x01, 0x6b, 0x02, 0x67, 0x02, 0x64, 0x02, - 0x62, 0x04, 0x5f, 0x04, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, - 0x71, 0x11, 0x6c, 0x11, 0x67, 0x11, 0x63, 0x11, 0x5f, 0x11, 0x5c, 0x11, - 0x58, 0x11, 0x55, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4c, 0x11, 0x4a, 0x11, - 0x48, 0x11, 0x46, 0x12, 0x44, 0x12, 0x43, 0x12, 0x42, 0x13, 0x40, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, - 0x00, 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xa9, 0x00, 0x9e, 0x00, - 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, 0x00, 0x5c, 0x00, 0x4f, 0x00, - 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0xbe, 0x00, 0xbd, 0x00, - 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, - 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, - 0x48, 0x00, 0x3f, 0x00, 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x33, 0x30, - 0x32, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x29, - 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0xd7, 0x00, 0xcc, 0x00, - 0xc3, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, - 0x93, 0x00, 0x8d, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, - 0x74, 0x00, 0x70, 0x01, 0x6c, 0x01, 0x69, 0x02, 0x66, 0x02, 0x63, 0x02, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7c, 0x11, 0x77, 0x11, 0x72, 0x11, 0x6e, 0x11, - 0x69, 0x11, 0x65, 0x11, 0x61, 0x11, 0x5d, 0x11, 0x5a, 0x11, 0x57, 0x11, - 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, - 0x47, 0x11, 0x45, 0x12, 0x44, 0x12, 0x42, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x39, 0x39, 0x36, 0x36, 0x33, 0x34, 0x32, 0x33, 0x30, 0x30, 0x2e, - 0x30, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, - 0x29, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, 0x27, 0x27, - 0x27, 0x27, 0x27, 0x25, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, - 0xb4, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, - 0x8c, 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, - 0x71, 0x01, 0x6d, 0x01, 0x6b, 0x02, 0x68, 0x02, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7c, 0x11, 0x78, 0x11, 0x73, 0x11, 0x6f, 0x11, 0x6b, 0x11, 0x67, 0x11, - 0x63, 0x11, 0x60, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, 0x54, 0x11, - 0x51, 0x11, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, - 0x46, 0x12, 0x45, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x39, 0x3a, 0x36, - 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2d, 0x2e, 0x2d, - 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, - 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, - 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, - 0xa8, 0x00, 0xa1, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, - 0x86, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x01, - 0x6f, 0x01, 0x6b, 0x01, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x78, 0x11, - 0x74, 0x11, 0x70, 0x11, 0x6c, 0x11, 0x68, 0x11, 0x65, 0x11, 0x61, 0x11, - 0x5f, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, - 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x35, 0x36, 0x33, - 0x33, 0x31, 0x32, 0x30, 0x30, 0x2e, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, - 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, - 0x28, 0x27, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0xd8, 0x00, 0xd0, 0x00, - 0xc8, 0x00, 0xc0, 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, - 0x9f, 0x00, 0x99, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, - 0x81, 0x00, 0x7d, 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x01, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x71, 0x11, - 0x6d, 0x11, 0x6a, 0x11, 0x66, 0x11, 0x63, 0x11, 0x60, 0x11, 0x5d, 0x11, - 0x5b, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x51, 0x11, 0x4f, 0x11, - 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x3a, 0x3a, 0x37, 0x38, 0x36, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, - 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2c, 0x2b, - 0x2b, 0x2b, 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x27, 0x29, 0x27, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, - 0xbb, 0x00, 0xb4, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x92, 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, - 0x7d, 0x00, 0x7a, 0x00, 0x76, 0x00, 0x73, 0x00, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7d, 0x11, 0x79, 0x11, 0x75, 0x11, 0x72, 0x11, 0x6e, 0x11, 0x6b, 0x11, - 0x68, 0x11, 0x65, 0x11, 0x62, 0x11, 0x5f, 0x11, 0x5c, 0x11, 0x5a, 0x11, - 0x58, 0x11, 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, - 0x4c, 0x11, 0x4a, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x38, - 0x39, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x30, 0x32, 0x30, 0x30, 0x2e, - 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, - 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, - 0xb0, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, - 0x91, 0x00, 0x8d, 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, - 0x7a, 0x00, 0x77, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x79, 0x11, - 0x76, 0x11, 0x72, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, 0x66, 0x11, - 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, 0x5c, 0x11, 0x59, 0x11, 0x57, 0x11, - 0x55, 0x11, 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3a, 0x3b, 0x39, 0x39, 0x36, 0x36, 0x34, - 0x35, 0x33, 0x33, 0x31, 0x33, 0x30, 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, - 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x29, - 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd2, 0x00, - 0xcb, 0x00, 0xc4, 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, - 0xa8, 0x00, 0xa2, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, - 0x8b, 0x00, 0x88, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x00, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, 0x76, 0x11, 0x73, 0x11, - 0x70, 0x11, 0x6d, 0x11, 0x6a, 0x11, 0x67, 0x11, 0x65, 0x11, 0x62, 0x11, - 0x5f, 0x11, 0x5d, 0x11, 0x5b, 0x11, 0x59, 0x11, 0x56, 0x11, 0x55, 0x11, - 0x53, 0x11, 0x51, 0x11, 0x4f, 0x11, 0x4e, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x3b, 0x3b, 0x39, 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, - 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, - 0x28, 0x28, 0x28, 0x28, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, - 0xc0, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, - 0xa0, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, - 0x88, 0x00, 0x83, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x7d, 0x11, 0x7a, 0x11, 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6e, 0x11, - 0x6b, 0x11, 0x68, 0x11, 0x66, 0x11, 0x63, 0x11, 0x61, 0x11, 0x5e, 0x11, - 0x5c, 0x11, 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x55, 0x11, 0x52, 0x11, - 0x51, 0x11, 0x4f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3b, 0x3b, 0x39, - 0x39, 0x36, 0x37, 0x36, 0x36, 0x33, 0x34, 0x33, 0x33, 0x31, 0x32, 0x30, - 0x30, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, - 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc7, 0x00, 0xc1, 0x00, 0xbc, 0x00, - 0xb7, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa2, 0x00, 0x9e, 0x00, - 0x9a, 0x00, 0x95, 0x00, 0x93, 0x00, 0x8e, 0x00, 0x8b, 0x00, 0x87, 0x00, - 0x83, 0x00, 0x81, 0x00, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x7d, 0x11, 0x7a, 0x11, - 0x77, 0x11, 0x74, 0x11, 0x71, 0x11, 0x6f, 0x11, 0x6c, 0x11, 0x69, 0x11, - 0x67, 0x11, 0x64, 0x11, 0x62, 0x11, 0x60, 0x11, 0x5e, 0x11, 0x5b, 0x11, - 0x5a, 0x11, 0x58, 0x11, 0x56, 0x11, 0x54, 0x11, 0x52, 0x11, 0x51, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0xb8, 0x00, 0xc5, 0x00, 0xcb, 0x00, - 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, - 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, - 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x44, 0x00, 0x9d, 0x00, 0xbc, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, - 0xd2, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, - 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, - 0xd9, 0x00, 0xd9, 0x00, 0x90, 0x00, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, - 0xd5, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, - 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x68, 0x01, 0x8b, 0x00, 0xa0, 0x00, 0xad, 0x00, 0xb6, 0x00, 0xbc, 0x00, - 0xc1, 0x00, 0xc4, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, - 0xcd, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, 0x00, - 0xd2, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x25, 0x05, 0x6b, 0x00, - 0x8e, 0x00, 0xa4, 0x00, 0xb0, 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, - 0xc5, 0x00, 0xc8, 0x00, 0xca, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, - 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, - 0x81, 0x02, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, - 0xcd, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, - 0xd4, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, - 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x09, 0x72, 0x02, - 0x87, 0x00, 0x96, 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb0, 0x00, 0xb5, 0x00, - 0xb9, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, - 0xc7, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x23, 0x0d, 0x51, 0x01, 0x73, 0x00, 0x89, 0x00, - 0x9a, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, - 0xbe, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, - 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0x80, 0x06, 0x97, 0x00, - 0xa8, 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, - 0xca, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, - 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4f, 0x11, 0x65, 0x07, 0x76, 0x03, 0x84, 0x01, - 0x8f, 0x00, 0x99, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xad, 0x00, 0xb1, 0x00, - 0xb4, 0x00, 0xb8, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc0, 0x00, - 0xc2, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x22, 0x12, 0x45, 0x04, 0x60, 0x00, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, - 0x9d, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, - 0xba, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, - 0xc4, 0x00, 0xc6, 0x00, 0x7f, 0x09, 0x91, 0x02, 0x9e, 0x00, 0xaa, 0x00, - 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, - 0xc8, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, - 0xcf, 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4b, 0x17, 0x5d, 0x0c, 0x6c, 0x06, 0x78, 0x03, 0x83, 0x01, 0x8c, 0x00, - 0x94, 0x00, 0x9b, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xae, 0x00, - 0xb1, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, 0x00, - 0xbe, 0x00, 0xbf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x15, 0x3d, 0x08, - 0x55, 0x02, 0x67, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, - 0x9f, 0x00, 0xa5, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, - 0xb6, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, - 0x7f, 0x0a, 0x8d, 0x04, 0x99, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, - 0xb6, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, - 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, - 0xcd, 0x00, 0xce, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x49, 0x1c, 0x57, 0x11, - 0x64, 0x0a, 0x70, 0x06, 0x79, 0x03, 0x82, 0x02, 0x8a, 0x00, 0x91, 0x00, - 0x97, 0x00, 0x9c, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xad, 0x00, - 0xaf, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x17, 0x38, 0x0b, 0x4c, 0x04, 0x5e, 0x01, - 0x6c, 0x00, 0x7a, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, - 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, - 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x7f, 0x0b, 0x8a, 0x05, - 0x94, 0x02, 0x9d, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, - 0xb9, 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, - 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x47, 0x20, 0x54, 0x15, 0x5f, 0x0e, 0x69, 0x09, - 0x72, 0x05, 0x7a, 0x03, 0x82, 0x02, 0x88, 0x01, 0x8f, 0x00, 0x94, 0x00, - 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa8, 0x00, 0xac, 0x00, - 0xae, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x22, 0x18, 0x35, 0x0d, 0x47, 0x06, 0x55, 0x03, 0x64, 0x01, 0x70, 0x00, - 0x7a, 0x00, 0x83, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, - 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, - 0xb2, 0x00, 0xb4, 0x00, 0x7f, 0x0c, 0x89, 0x06, 0x92, 0x03, 0x99, 0x01, - 0xa0, 0x00, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, - 0xba, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, - 0xc5, 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x46, 0x23, 0x50, 0x18, 0x5b, 0x11, 0x64, 0x0b, 0x6c, 0x08, 0x74, 0x05, - 0x7b, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8c, 0x00, 0x91, 0x00, 0x95, 0x00, - 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xab, 0x00, - 0xac, 0x00, 0xaf, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1a, 0x32, 0x0f, - 0x42, 0x08, 0x50, 0x04, 0x5c, 0x02, 0x68, 0x00, 0x72, 0x00, 0x7b, 0x00, - 0x83, 0x00, 0x8a, 0x00, 0x90, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, - 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0x7f, 0x0d, 0x88, 0x07, 0x8f, 0x04, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, - 0xa7, 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, - 0xbb, 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, - 0xc5, 0x00, 0xc6, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, 0x00, - 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, - 0x3b, 0x0b, 0x6f, 0x00, 0x9f, 0x00, 0xaf, 0x00, 0xb5, 0x00, 0xb9, 0x00, - 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, - 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, - 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, - 0x47, 0x07, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, - 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x45, 0x25, 0x4e, 0x1b, - 0x57, 0x13, 0x60, 0x0e, 0x68, 0x0a, 0x6f, 0x07, 0x76, 0x05, 0x7b, 0x03, - 0x82, 0x02, 0x86, 0x02, 0x8b, 0x01, 0x90, 0x00, 0x93, 0x00, 0x97, 0x00, - 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1b, 0x30, 0x11, 0x3e, 0x0a, 0x4b, 0x05, - 0x57, 0x03, 0x61, 0x01, 0x6b, 0x00, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, - 0x88, 0x00, 0x8f, 0x00, 0x93, 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, - 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0x7f, 0x0d, 0x86, 0x08, - 0x8d, 0x05, 0x94, 0x02, 0x9a, 0x01, 0x9f, 0x00, 0xa4, 0x00, 0xa8, 0x00, - 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, - 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, - 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, 0x00, - 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x07, 0x17, 0x27, 0x02, - 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb2, 0x00, 0xb5, 0x00, - 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, - 0xbc, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x6d, 0x00, 0x54, 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, - 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x0f, 0x0f, 0x3f, 0x00, - 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, - 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0x45, 0x28, 0x4d, 0x1d, 0x55, 0x16, 0x5d, 0x11, - 0x64, 0x0c, 0x6b, 0x09, 0x71, 0x07, 0x77, 0x05, 0x7b, 0x03, 0x81, 0x02, - 0x85, 0x02, 0x8a, 0x01, 0x8e, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, - 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x21, 0x1b, 0x2f, 0x12, 0x3b, 0x0c, 0x47, 0x07, 0x51, 0x04, 0x5c, 0x02, - 0x65, 0x01, 0x6e, 0x00, 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, - 0x8d, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, - 0xa2, 0x00, 0xa5, 0x00, 0x7f, 0x0d, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x03, - 0x97, 0x02, 0x9c, 0x01, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, - 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, - 0xbc, 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, - 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, - 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x2c, 0x02, 0x12, 0x22, 0x00, 0x58, 0x00, - 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xaa, 0x00, 0xae, 0x00, - 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x8d, 0x00, 0x6b, 0x00, - 0x28, 0x00, 0x52, 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, - 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, - 0xb6, 0x00, 0xb7, 0x00, 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, - 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, - 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0x44, 0x2a, 0x4b, 0x20, 0x53, 0x18, 0x5a, 0x13, 0x61, 0x0f, 0x67, 0x0b, - 0x6d, 0x09, 0x73, 0x07, 0x77, 0x05, 0x7c, 0x03, 0x81, 0x02, 0x85, 0x02, - 0x88, 0x01, 0x8d, 0x01, 0x90, 0x00, 0x93, 0x00, 0x96, 0x00, 0x9a, 0x00, - 0x9d, 0x00, 0x9f, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1c, 0x2d, 0x13, - 0x39, 0x0d, 0x44, 0x09, 0x4e, 0x05, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, - 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, - 0x90, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, - 0x7f, 0x0e, 0x85, 0x09, 0x8b, 0x06, 0x90, 0x04, 0x95, 0x02, 0x9a, 0x02, - 0x9e, 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, - 0xbd, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, - 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0x00, 0x36, 0x00, 0x25, 0x09, 0x0b, 0x21, 0x00, 0x4a, 0x00, 0x68, 0x00, - 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, 0x00, 0xa3, 0x00, 0xa8, 0x00, - 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, - 0x39, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, - 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0x00, 0x33, 0x00, 0x1c, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, - 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, - 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0x43, 0x2b, 0x4a, 0x22, - 0x51, 0x1a, 0x57, 0x15, 0x5e, 0x11, 0x63, 0x0d, 0x69, 0x0a, 0x6e, 0x08, - 0x74, 0x06, 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, - 0x8c, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9b, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1c, 0x2d, 0x14, 0x37, 0x0e, 0x41, 0x0a, - 0x4a, 0x07, 0x53, 0x04, 0x5b, 0x02, 0x63, 0x01, 0x69, 0x00, 0x70, 0x00, - 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, - 0x92, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x7f, 0x0e, 0x85, 0x0a, - 0x8a, 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x01, 0xa0, 0x00, - 0xa3, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, - 0xb3, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, 0x00, - 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, - 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x3a, 0x00, 0x2f, - 0x00, 0x16, 0x0e, 0x08, 0x20, 0x00, 0x42, 0x00, 0x5c, 0x00, 0x6f, 0x00, - 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa2, 0x00, - 0xa6, 0x00, 0xa9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xa6, 0x00, 0x98, 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, - 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, - 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x38, 0x00, 0x2a, - 0x00, 0x09, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, - 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, - 0xab, 0x00, 0xad, 0x00, 0x43, 0x2c, 0x49, 0x23, 0x50, 0x1c, 0x56, 0x17, - 0x5b, 0x12, 0x61, 0x0f, 0x66, 0x0c, 0x6b, 0x0a, 0x6f, 0x08, 0x75, 0x06, - 0x78, 0x05, 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x8b, 0x01, - 0x8e, 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x22, 0x1c, 0x2c, 0x15, 0x36, 0x0f, 0x3e, 0x0b, 0x48, 0x07, 0x50, 0x05, - 0x58, 0x04, 0x5f, 0x02, 0x66, 0x01, 0x6c, 0x00, 0x71, 0x00, 0x77, 0x00, - 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, - 0x94, 0x00, 0x97, 0x00, 0x7f, 0x0e, 0x84, 0x0a, 0x89, 0x07, 0x8d, 0x05, - 0x92, 0x03, 0x96, 0x02, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x00, 0xa4, 0x00, - 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, - 0xb5, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, 0x00, - 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, - 0x93, 0x00, 0x98, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x02, 0x0e, - 0x12, 0x06, 0x20, 0x00, 0x3c, 0x00, 0x52, 0x00, 0x64, 0x00, 0x73, 0x00, - 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, 0x00, 0x9a, 0x00, 0x9e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb2, 0x00, 0xa6, 0x00, - 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, - 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, - 0x93, 0x00, 0x98, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, - 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, - 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, - 0x43, 0x2e, 0x48, 0x25, 0x4e, 0x1e, 0x54, 0x19, 0x59, 0x14, 0x5f, 0x11, - 0x63, 0x0e, 0x69, 0x0b, 0x6c, 0x09, 0x71, 0x07, 0x76, 0x06, 0x78, 0x05, - 0x7c, 0x04, 0x81, 0x03, 0x84, 0x02, 0x86, 0x02, 0x8a, 0x01, 0x8e, 0x01, - 0x90, 0x00, 0x92, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1d, 0x2b, 0x16, - 0x34, 0x10, 0x3d, 0x0c, 0x45, 0x09, 0x4c, 0x06, 0x54, 0x04, 0x5b, 0x02, - 0x61, 0x02, 0x67, 0x01, 0x6d, 0x00, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, - 0x81, 0x00, 0x86, 0x00, 0x89, 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, - 0x7f, 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x04, 0x94, 0x03, - 0x98, 0x02, 0x9c, 0x01, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, - 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, - 0xb6, 0x00, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, - 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, - 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x07, 0x0c, 0x14, 0x05, - 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, 0x00, 0x6a, 0x00, 0x75, 0x00, - 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, - 0x48, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, - 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, - 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, - 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, - 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x00, 0x42, 0x2f, 0x48, 0x26, - 0x4d, 0x20, 0x52, 0x1a, 0x58, 0x16, 0x5c, 0x12, 0x61, 0x0f, 0x66, 0x0c, - 0x6b, 0x0b, 0x6d, 0x09, 0x73, 0x07, 0x76, 0x06, 0x79, 0x05, 0x7d, 0x04, - 0x81, 0x03, 0x83, 0x02, 0x86, 0x02, 0x89, 0x02, 0x8d, 0x01, 0x8f, 0x01, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x22, 0x1d, 0x2a, 0x16, 0x33, 0x11, 0x3b, 0x0c, - 0x43, 0x0a, 0x4a, 0x07, 0x51, 0x05, 0x58, 0x04, 0x5e, 0x02, 0x64, 0x01, - 0x69, 0x01, 0x6f, 0x00, 0x74, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, - 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x7f, 0x0e, 0x83, 0x0b, - 0x88, 0x08, 0x8c, 0x06, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, - 0x9d, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, - 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, - 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x3a, - 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x0b, 0x0a, 0x16, 0x04, 0x20, 0x00, - 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, 0x00, 0x6d, 0x00, 0x77, 0x00, - 0x7f, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, - 0x1c, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, - 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x3d, 0x00, 0x38, - 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, - 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, - 0x8b, 0x00, 0x91, 0x00, 0x42, 0x30, 0x47, 0x28, 0x4c, 0x21, 0x51, 0x1c, - 0x56, 0x17, 0x5a, 0x14, 0x60, 0x11, 0x63, 0x0e, 0x68, 0x0c, 0x6c, 0x0a, - 0x6f, 0x08, 0x74, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, - 0x83, 0x02, 0x85, 0x02, 0x88, 0x02, 0x8c, 0x01, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x21, 0x1e, 0x2a, 0x17, 0x32, 0x12, 0x39, 0x0e, 0x40, 0x0a, 0x48, 0x07, - 0x4f, 0x05, 0x55, 0x04, 0x5a, 0x03, 0x60, 0x02, 0x66, 0x01, 0x6b, 0x00, - 0x70, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, - 0x88, 0x00, 0x8b, 0x00, 0x7f, 0x0f, 0x83, 0x0b, 0x87, 0x09, 0x8b, 0x07, - 0x8e, 0x05, 0x92, 0x03, 0x96, 0x02, 0x99, 0x02, 0x9b, 0x01, 0x9e, 0x01, - 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, - 0xaf, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, - 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x33, 0x00, 0x27, - 0x00, 0x18, 0x04, 0x0d, 0x0e, 0x08, 0x17, 0x04, 0x20, 0x00, 0x32, 0x00, - 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, 0x00, 0x70, 0x00, 0x78, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, 0x00, - 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, - 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, - 0x60, 0x00, 0x6a, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, - 0x00, 0x0b, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, - 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, - 0x42, 0x31, 0x47, 0x29, 0x4b, 0x22, 0x50, 0x1d, 0x55, 0x19, 0x58, 0x15, - 0x5e, 0x12, 0x61, 0x0f, 0x65, 0x0d, 0x6a, 0x0b, 0x6c, 0x09, 0x70, 0x08, - 0x75, 0x07, 0x77, 0x06, 0x79, 0x05, 0x7d, 0x04, 0x81, 0x03, 0x83, 0x02, - 0x85, 0x02, 0x87, 0x02, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, - 0x31, 0x12, 0x38, 0x0f, 0x3f, 0x0c, 0x45, 0x09, 0x4c, 0x07, 0x52, 0x05, - 0x58, 0x04, 0x5d, 0x02, 0x63, 0x02, 0x67, 0x01, 0x6c, 0x00, 0x71, 0x00, - 0x75, 0x00, 0x79, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, - 0x7f, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x04, - 0x94, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x01, 0xa0, 0x01, 0xa2, 0x00, - 0xa4, 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, - 0xb0, 0x00, 0xb2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, - 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x12, - 0x07, 0x0c, 0x10, 0x07, 0x18, 0x03, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, - 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, - 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, - 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, - 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x46, 0x2a, - 0x4a, 0x24, 0x4f, 0x1f, 0x53, 0x1a, 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, - 0x63, 0x0e, 0x67, 0x0c, 0x6b, 0x0b, 0x6d, 0x09, 0x72, 0x07, 0x75, 0x07, - 0x77, 0x06, 0x79, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x29, 0x18, 0x30, 0x13, 0x37, 0x0f, - 0x3d, 0x0c, 0x44, 0x0a, 0x4a, 0x07, 0x50, 0x05, 0x55, 0x04, 0x5b, 0x04, - 0x60, 0x02, 0x64, 0x01, 0x69, 0x01, 0x6d, 0x00, 0x72, 0x00, 0x76, 0x00, - 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, 0x00, 0x7f, 0x0f, 0x83, 0x0c, - 0x86, 0x09, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x02, - 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x00, 0xa3, 0x00, 0xa5, 0x00, - 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, - 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x02, 0x0e, 0x0a, 0x0a, - 0x11, 0x06, 0x19, 0x03, 0x1f, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, - 0x53, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, - 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x3e, 0x00, 0x3c, - 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, - 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, - 0x68, 0x00, 0x70, 0x00, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4f, 0x1f, - 0x52, 0x1b, 0x57, 0x18, 0x59, 0x14, 0x5f, 0x12, 0x62, 0x0f, 0x65, 0x0d, - 0x6a, 0x0c, 0x6c, 0x0a, 0x6e, 0x09, 0x73, 0x07, 0x76, 0x06, 0x78, 0x06, - 0x7a, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0x21, 0x1e, 0x28, 0x19, 0x2f, 0x14, 0x35, 0x0f, 0x3c, 0x0c, 0x42, 0x0a, - 0x48, 0x07, 0x4e, 0x06, 0x53, 0x05, 0x58, 0x04, 0x5d, 0x02, 0x62, 0x02, - 0x66, 0x01, 0x6b, 0x01, 0x6f, 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, - 0x7d, 0x00, 0x81, 0x00, 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x07, - 0x8c, 0x06, 0x8f, 0x05, 0x92, 0x03, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, - 0x9d, 0x01, 0x9f, 0x01, 0xa1, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, - 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x31, - 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x05, 0x0d, 0x0c, 0x09, 0x13, 0x06, - 0x19, 0x03, 0x1f, 0x00, 0x2d, 0x00, 0x3a, 0x00, 0x45, 0x00, 0x4f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, - 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, - 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2d, 0x00, 0x39, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, - 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, - 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, - 0x42, 0x33, 0x45, 0x2c, 0x48, 0x26, 0x4e, 0x21, 0x50, 0x1c, 0x56, 0x19, - 0x58, 0x16, 0x5c, 0x13, 0x60, 0x11, 0x62, 0x0e, 0x67, 0x0c, 0x6a, 0x0b, - 0x6d, 0x0a, 0x70, 0x09, 0x74, 0x07, 0x76, 0x06, 0x78, 0x06, 0x7a, 0x05, - 0x7e, 0x04, 0x81, 0x03, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x21, 0x1e, 0x28, 0x19, - 0x2f, 0x15, 0x35, 0x11, 0x3b, 0x0d, 0x41, 0x0b, 0x46, 0x09, 0x4c, 0x07, - 0x51, 0x05, 0x55, 0x04, 0x5b, 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, - 0x6b, 0x00, 0x70, 0x00, 0x73, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, - 0x7f, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, 0x06, 0x8f, 0x05, - 0x91, 0x04, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, - 0xa0, 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, - 0xac, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, - 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, 0x2c, 0x00, 0x23, - 0x00, 0x19, 0x00, 0x0f, 0x07, 0x0c, 0x0e, 0x08, 0x14, 0x05, 0x1a, 0x02, - 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, - 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, - 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, - 0x00, 0x0c, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, - 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x00, 0x5d, 0x1a, 0x6d, 0x16, - 0x72, 0x15, 0x75, 0x14, 0x77, 0x13, 0x78, 0x13, 0x79, 0x13, 0x7a, 0x12, - 0x7a, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7b, 0x12, 0x7c, 0x12, 0x7c, 0x11, - 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, - 0xbd, 0x02, 0xa4, 0x06, 0x97, 0x09, 0x91, 0x0a, 0x8d, 0x0b, 0x8a, 0x0c, - 0x89, 0x0d, 0x88, 0x0d, 0x86, 0x0d, 0x86, 0x0e, 0x85, 0x0e, 0x85, 0x0e, - 0x84, 0x0e, 0x84, 0x0e, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, 0x83, 0x0f, - 0x82, 0x0f, 0x82, 0x0f, 0x33, 0x11, 0x5f, 0x11, 0x6f, 0x11, 0x74, 0x11, - 0x77, 0x11, 0x78, 0x11, 0x7a, 0x11, 0x7a, 0x11, 0x7b, 0x11, 0x7b, 0x11, - 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7c, 0x11, 0x7d, 0x11, 0x7d, 0x11, - 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7d, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1d, 0x00, 0x14, - 0x03, 0x0e, 0x09, 0x0b, 0x0f, 0x08, 0x15, 0x05, 0x1a, 0x02, 0x1f, 0x00, - 0x2b, 0x00, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x3f, 0x00, 0x3d, - 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, - 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, - 0x49, 0x00, 0x51, 0x00, 0x50, 0x24, 0x5f, 0x1f, 0x66, 0x1b, 0x6b, 0x19, - 0x6e, 0x18, 0x70, 0x17, 0x73, 0x16, 0x73, 0x15, 0x75, 0x15, 0x76, 0x15, - 0x76, 0x15, 0x77, 0x14, 0x77, 0x14, 0x78, 0x13, 0x78, 0x13, 0x79, 0x13, - 0x79, 0x13, 0x7a, 0x13, 0x7a, 0x13, 0x7b, 0x13, 0xcc, 0x00, 0xb5, 0x00, - 0xa8, 0x02, 0x9e, 0x04, 0x99, 0x05, 0x94, 0x06, 0x92, 0x07, 0x8f, 0x08, - 0x8d, 0x09, 0x8c, 0x09, 0x8b, 0x0a, 0x8a, 0x0a, 0x89, 0x0b, 0x88, 0x0b, - 0x88, 0x0b, 0x87, 0x0c, 0x87, 0x0c, 0x86, 0x0c, 0x86, 0x0c, 0x86, 0x0c, - 0x23, 0x13, 0x46, 0x11, 0x58, 0x11, 0x63, 0x11, 0x69, 0x11, 0x6d, 0x11, - 0x70, 0x11, 0x72, 0x11, 0x73, 0x11, 0x75, 0x11, 0x76, 0x11, 0x77, 0x11, - 0x77, 0x11, 0x78, 0x11, 0x78, 0x11, 0x79, 0x11, 0x79, 0x11, 0x79, 0x11, - 0x7a, 0x11, 0x7a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, - 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x10, 0x05, 0x0d, - 0x0b, 0x0a, 0x10, 0x07, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x2a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, - 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, - 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, - 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, - 0x4a, 0x2a, 0x57, 0x24, 0x5f, 0x20, 0x64, 0x1e, 0x68, 0x1c, 0x6a, 0x1b, - 0x6d, 0x19, 0x6e, 0x18, 0x70, 0x18, 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, - 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, - 0x76, 0x15, 0x77, 0x14, 0xd1, 0x00, 0xc0, 0x00, 0xb3, 0x00, 0xaa, 0x01, - 0xa2, 0x02, 0x9d, 0x03, 0x99, 0x04, 0x96, 0x05, 0x94, 0x06, 0x92, 0x06, - 0x90, 0x07, 0x8f, 0x07, 0x8d, 0x08, 0x8d, 0x08, 0x8c, 0x09, 0x8b, 0x09, - 0x8a, 0x09, 0x8a, 0x0a, 0x89, 0x0a, 0x89, 0x0b, 0x22, 0x17, 0x39, 0x11, - 0x4a, 0x11, 0x55, 0x11, 0x5e, 0x11, 0x63, 0x11, 0x67, 0x11, 0x6a, 0x11, - 0x6c, 0x11, 0x6e, 0x11, 0x70, 0x11, 0x71, 0x11, 0x72, 0x11, 0x73, 0x11, - 0x74, 0x11, 0x75, 0x11, 0x75, 0x11, 0x76, 0x11, 0x76, 0x11, 0x77, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, 0x32, 0x00, 0x2c, - 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x01, 0x0e, 0x07, 0x0c, 0x0c, 0x09, - 0x11, 0x06, 0x16, 0x04, 0x1b, 0x02, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, - 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, - 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, - 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, - 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x2e, 0x53, 0x28, - 0x5a, 0x24, 0x5f, 0x22, 0x63, 0x1f, 0x66, 0x1e, 0x69, 0x1c, 0x6a, 0x1b, - 0x6c, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x70, 0x18, 0x71, 0x18, 0x72, 0x18, - 0x72, 0x17, 0x72, 0x17, 0x73, 0x16, 0x74, 0x15, 0x75, 0x15, 0x76, 0x15, - 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x01, - 0xa0, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x97, 0x04, 0x95, 0x05, 0x93, 0x05, - 0x92, 0x06, 0x91, 0x06, 0x90, 0x07, 0x8e, 0x07, 0x8e, 0x07, 0x8d, 0x07, - 0x8c, 0x08, 0x8c, 0x09, 0x22, 0x1a, 0x33, 0x13, 0x41, 0x11, 0x4c, 0x11, - 0x54, 0x11, 0x5b, 0x11, 0x5f, 0x11, 0x63, 0x11, 0x66, 0x11, 0x68, 0x11, - 0x6a, 0x11, 0x6c, 0x11, 0x6e, 0x11, 0x6f, 0x11, 0x70, 0x11, 0x71, 0x11, - 0x72, 0x11, 0x72, 0x11, 0x73, 0x11, 0x74, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x07, 0x7f, 0x00, - 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, - 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, - 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x47, 0x07, 0x0f, 0x0f, 0x00, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, - 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, - 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x31, 0x50, 0x2b, 0x56, 0x27, 0x5b, 0x24, - 0x5f, 0x22, 0x62, 0x20, 0x65, 0x1f, 0x67, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, - 0x6c, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, 0x71, 0x18, - 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x17, 0xd6, 0x00, 0xcb, 0x00, - 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, 0x00, 0xa6, 0x01, 0xa2, 0x01, - 0x9f, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x96, 0x04, 0x94, 0x05, - 0x93, 0x05, 0x92, 0x06, 0x91, 0x06, 0x90, 0x06, 0x8f, 0x06, 0x8f, 0x07, - 0x22, 0x1b, 0x2f, 0x15, 0x3b, 0x12, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x11, - 0x59, 0x11, 0x5d, 0x11, 0x60, 0x11, 0x63, 0x11, 0x66, 0x11, 0x67, 0x11, - 0x69, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, 0x6f, 0x11, - 0x70, 0x11, 0x71, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0f, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, - 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, - 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, 0x00, - 0x05, 0x05, 0x00, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, - 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, - 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x33, 0x4d, 0x2e, 0x53, 0x29, 0x58, 0x26, 0x5c, 0x24, 0x5f, 0x22, - 0x61, 0x21, 0x65, 0x20, 0x65, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x6a, 0x1b, - 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, 0x6f, 0x18, - 0x71, 0x18, 0x72, 0x18, 0xd7, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbd, 0x00, - 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, 0x00, 0xa4, 0x01, 0xa1, 0x02, - 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x03, 0x98, 0x03, 0x97, 0x03, 0x96, 0x04, - 0x94, 0x05, 0x93, 0x05, 0x92, 0x05, 0x91, 0x06, 0x21, 0x1c, 0x2d, 0x16, - 0x37, 0x13, 0x40, 0x11, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x11, 0x57, 0x11, - 0x5b, 0x11, 0x5e, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x67, 0x11, - 0x68, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x6c, 0x11, 0x6d, 0x11, 0x6e, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x26, 0x05, 0x05, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, - 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, - 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, 0x00, - 0x00, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, - 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x34, 0x4c, 0x2f, - 0x51, 0x2c, 0x56, 0x28, 0x5a, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, - 0x64, 0x20, 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, - 0x6b, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x19, - 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, 0x00, 0xba, 0x00, 0xb5, 0x00, - 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, 0x01, 0xa2, 0x01, 0xa0, 0x02, - 0x9e, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x97, 0x03, 0x96, 0x03, - 0x95, 0x04, 0x94, 0x05, 0x22, 0x1d, 0x2b, 0x17, 0x34, 0x14, 0x3b, 0x12, - 0x43, 0x11, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x5a, 0x11, - 0x5d, 0x11, 0x5f, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, - 0x68, 0x11, 0x69, 0x11, 0x6a, 0x11, 0x6b, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, - 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, - 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, - 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, 0x00, - 0x00, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, - 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x36, 0x4a, 0x31, 0x50, 0x2d, 0x54, 0x2a, - 0x57, 0x28, 0x5b, 0x26, 0x5c, 0x24, 0x60, 0x23, 0x61, 0x21, 0x63, 0x20, - 0x65, 0x20, 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, - 0x6a, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, 0x1a, 0xd9, 0x00, 0xd1, 0x00, - 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb4, 0x00, 0xb0, 0x00, - 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0x9f, 0x02, - 0x9d, 0x02, 0x9b, 0x02, 0x9a, 0x02, 0x99, 0x03, 0x98, 0x03, 0x97, 0x03, - 0x21, 0x1e, 0x2a, 0x18, 0x32, 0x15, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, - 0x4a, 0x11, 0x4e, 0x11, 0x52, 0x11, 0x56, 0x11, 0x59, 0x11, 0x5c, 0x11, - 0x5d, 0x11, 0x60, 0x11, 0x61, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, - 0x67, 0x11, 0x68, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x09, 0x1d, 0x00, - 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, - 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, 0x00, - 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, - 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x36, 0x49, 0x32, 0x4e, 0x2f, 0x52, 0x2c, 0x56, 0x29, 0x58, 0x27, - 0x5b, 0x26, 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, - 0x65, 0x1f, 0x66, 0x1e, 0x68, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1c, - 0x6a, 0x1b, 0x6c, 0x1a, 0xd9, 0x00, 0xd2, 0x00, 0xcc, 0x00, 0xc6, 0x00, - 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, 0x00, 0xaf, 0x00, 0xac, 0x00, - 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9e, 0x02, - 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, 0x02, 0x22, 0x1e, 0x29, 0x19, - 0x30, 0x16, 0x36, 0x13, 0x3c, 0x12, 0x41, 0x11, 0x46, 0x11, 0x4a, 0x11, - 0x4f, 0x11, 0x52, 0x11, 0x55, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, - 0x5f, 0x11, 0x60, 0x11, 0x62, 0x11, 0x63, 0x11, 0x65, 0x11, 0x66, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, - 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, - 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, 0x00, - 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0x02, - 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x37, 0x48, 0x33, - 0x4d, 0x30, 0x51, 0x2d, 0x54, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x26, - 0x5d, 0x24, 0x60, 0x23, 0x60, 0x22, 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, - 0x65, 0x1f, 0x66, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0x69, 0x1d, - 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, 0x00, 0xc3, 0x00, 0xbe, 0x00, - 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, - 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x02, 0x9e, 0x02, - 0x9d, 0x02, 0x9c, 0x02, 0x21, 0x1e, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x14, - 0x39, 0x13, 0x3f, 0x12, 0x43, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, - 0x52, 0x11, 0x55, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5c, 0x11, 0x5d, 0x11, - 0x5f, 0x11, 0x61, 0x11, 0x62, 0x11, 0x63, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, - 0x00, 0x23, 0x00, 0x0b, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, - 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, - 0x97, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, 0x00, - 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x00, 0x00, 0x04, - 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x43, 0x38, 0x48, 0x34, 0x4c, 0x31, 0x4f, 0x2e, - 0x53, 0x2c, 0x56, 0x2a, 0x57, 0x28, 0x5a, 0x27, 0x5c, 0x26, 0x5d, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, - 0x65, 0x1e, 0x67, 0x1d, 0x69, 0x1d, 0x69, 0x1d, 0xd9, 0x00, 0xd4, 0x00, - 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xb9, 0x00, - 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, - 0xa6, 0x00, 0xa4, 0x01, 0xa2, 0x01, 0xa0, 0x01, 0x9f, 0x02, 0x9e, 0x02, - 0x22, 0x1f, 0x27, 0x1a, 0x2d, 0x17, 0x33, 0x15, 0x38, 0x13, 0x3c, 0x13, - 0x41, 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, - 0x54, 0x11, 0x57, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x5e, 0x11, - 0x5f, 0x11, 0x61, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, - 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, - 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, 0x00, - 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, 0x00, - 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, - 0x00, 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x38, 0x47, 0x35, 0x4b, 0x32, 0x4e, 0x2f, 0x52, 0x2d, 0x53, 0x2b, - 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, - 0x66, 0x1e, 0x68, 0x1d, 0xda, 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcb, 0x00, - 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb5, 0x00, - 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, - 0xa4, 0x00, 0xa3, 0x01, 0xa1, 0x01, 0xa0, 0x01, 0x21, 0x1f, 0x27, 0x1b, - 0x2c, 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x12, 0x42, 0x11, - 0x45, 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, - 0x56, 0x11, 0x58, 0x11, 0x5a, 0x11, 0x5c, 0x11, 0x5d, 0x11, 0x5e, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x08, 0x00, - 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, - 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, - 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, 0x00, - 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x35, - 0x4a, 0x32, 0x4e, 0x30, 0x50, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, - 0x58, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x22, 0x63, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x1f, - 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc4, 0x00, - 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb4, 0x00, 0xb2, 0x00, - 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa5, 0x00, - 0xa4, 0x01, 0xa2, 0x01, 0x22, 0x1f, 0x27, 0x1b, 0x2c, 0x18, 0x30, 0x16, - 0x35, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x11, 0x47, 0x11, - 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x56, 0x11, - 0x58, 0x11, 0x59, 0x11, 0x5b, 0x11, 0x5c, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, - 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x0f, 0x00, 0x20, 0x00, - 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, - 0x74, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, 0x00, - 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, 0x00, - 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x46, 0x36, 0x4a, 0x33, 0x4d, 0x31, - 0x4f, 0x2e, 0x52, 0x2d, 0x53, 0x2b, 0x57, 0x2a, 0x57, 0x28, 0x5a, 0x27, - 0x5c, 0x27, 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, - 0x62, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, - 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc2, 0x00, 0xbf, 0x00, - 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, - 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, 0x00, 0xa6, 0x00, 0xa4, 0x00, - 0x21, 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x15, 0x37, 0x14, - 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, - 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x54, 0x11, 0x55, 0x11, 0x57, 0x11, - 0x59, 0x11, 0x5a, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, - 0x00, 0x1b, 0x00, 0x0c, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, - 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, - 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, 0x00, - 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, 0x00, - 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x39, 0x45, 0x36, 0x49, 0x34, 0x4c, 0x31, 0x4f, 0x30, 0x51, 0x2e, - 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x28, 0x5b, 0x27, 0x5c, 0x27, - 0x5c, 0x25, 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0x61, 0x21, - 0x64, 0x20, 0x65, 0x20, 0xda, 0x00, 0xd6, 0x00, 0xd2, 0x00, 0xce, 0x00, - 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, - 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, - 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0x22, 0x1f, 0x26, 0x1c, - 0x2a, 0x19, 0x2e, 0x17, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, - 0x40, 0x12, 0x43, 0x11, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, - 0x4f, 0x11, 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x58, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, - 0x00, 0x04, 0x0a, 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, - 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, 0x00, - 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, 0x00, - 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, - 0x48, 0x35, 0x4a, 0x32, 0x4e, 0x31, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2b, - 0x57, 0x2a, 0x57, 0x29, 0x58, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x25, - 0x5e, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, 0x21, 0x64, 0x20, - 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xc8, 0x00, - 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb7, 0x00, - 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, - 0xa9, 0x00, 0xa8, 0x00, 0x21, 0x20, 0x26, 0x1c, 0x2a, 0x1a, 0x2d, 0x18, - 0x31, 0x16, 0x35, 0x14, 0x38, 0x13, 0x3b, 0x13, 0x3e, 0x12, 0x41, 0x12, - 0x44, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, - 0x51, 0x11, 0x53, 0x11, 0x55, 0x11, 0x56, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, - 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x01, 0x00, - 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, - 0x53, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, - 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, 0x00, - 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3a, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, - 0x4d, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, - 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x22, 0xdb, 0x00, 0xd7, 0x00, - 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, 0x00, 0xc6, 0x00, 0xc3, 0x00, - 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, 0x00, 0xb7, 0x00, 0xb5, 0x00, - 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xaa, 0x00, - 0x21, 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x15, - 0x37, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, - 0x47, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x11, - 0x53, 0x11, 0x55, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, - 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x06, 0x00, 0x13, 0x00, - 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, - 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, 0x00, - 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, 0x00, - 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x30, - 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x28, - 0x5a, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x25, 0x5f, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd7, 0x00, 0xd4, 0x00, 0xd0, 0x00, - 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, - 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, - 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, 0x00, 0x21, 0x20, 0x25, 0x1d, - 0x29, 0x1a, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x13, - 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x11, 0x45, 0x11, 0x47, 0x11, - 0x4a, 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x52, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, - 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, - 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, 0x00, - 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, 0x00, - 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, - 0x46, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x2e, 0x53, 0x2e, - 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x27, 0x5b, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0x60, 0x23, - 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, 0x00, 0xce, 0x00, 0xcb, 0x00, - 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbc, 0x00, - 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, - 0xaf, 0x00, 0xad, 0x00, 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x18, - 0x2f, 0x17, 0x32, 0x16, 0x35, 0x14, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, - 0x3f, 0x12, 0x42, 0x12, 0x44, 0x11, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, - 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, - 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, - 0x00, 0x07, 0x03, 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, - 0x36, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, 0x00, - 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, 0x00, - 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x36, 0x4a, 0x35, - 0x4a, 0x32, 0x4e, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2b, - 0x56, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x59, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x26, 0x5c, 0x24, 0x5f, 0x23, 0x60, 0x23, 0xdb, 0x00, 0xd8, 0x00, - 0xd5, 0x00, 0xd2, 0x00, 0xcf, 0x00, 0xcc, 0x00, 0xca, 0x00, 0xc6, 0x00, - 0xc4, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb9, 0x00, - 0xb8, 0x00, 0xb5, 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, - 0x21, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2e, 0x17, 0x31, 0x16, - 0x34, 0x15, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, - 0x42, 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, - 0x4e, 0x11, 0x4f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, - 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x7f, 0x11, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x27, 0x68, 0x32, 0x51, 0x36, 0x4a, 0x38, 0x47, - 0x3a, 0x46, 0x3a, 0x45, 0x3b, 0x44, 0x3c, 0x43, 0x3c, 0x43, 0x3c, 0x43, - 0x3c, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x41, 0x0e, 0x16, 0x27, - 0x23, 0x31, 0x2a, 0x35, 0x2e, 0x38, 0x31, 0x39, 0x33, 0x3a, 0x34, 0x3b, - 0x35, 0x3b, 0x36, 0x3c, 0x37, 0x3c, 0x38, 0x3c, 0x38, 0x3c, 0x39, 0x3d, - 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3b, 0x3d, - 0x68, 0x00, 0x32, 0x27, 0x36, 0x31, 0x38, 0x35, 0x3a, 0x38, 0x3a, 0x39, - 0x3b, 0x3a, 0x3c, 0x3b, 0x3c, 0x3b, 0x3c, 0x3c, 0x3c, 0x3c, 0x3d, 0x3c, - 0x3d, 0x3c, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x32, 0x28, 0x36, 0x2c, 0x38, 0x2f, 0x3a, 0x32, 0x3a, 0x34, 0x3b, - 0x35, 0x3c, 0x36, 0x3c, 0x37, 0x3c, 0x37, 0x3c, 0x38, 0x3d, 0x39, 0x3d, - 0x39, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x00, 0x97, 0x01, 0x67, 0x09, 0x56, 0x11, 0x4f, - 0x17, 0x4b, 0x1c, 0x49, 0x20, 0x47, 0x23, 0x46, 0x25, 0x45, 0x28, 0x44, - 0x2a, 0x44, 0x2b, 0x43, 0x2c, 0x43, 0x2e, 0x43, 0x2f, 0x42, 0x30, 0x42, - 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x1a, 0x5c, 0x25, 0x4f, - 0x2a, 0x4a, 0x2e, 0x48, 0x31, 0x46, 0x33, 0x45, 0x34, 0x44, 0x36, 0x44, - 0x37, 0x43, 0x37, 0x43, 0x38, 0x43, 0x38, 0x42, 0x39, 0x42, 0x39, 0x42, - 0x39, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x5f, - 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x33, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x28, 0x25, 0x2d, - 0x28, 0x30, 0x2b, 0x32, 0x2e, 0x34, 0x2f, 0x35, 0x31, 0x36, 0x32, 0x37, - 0x33, 0x38, 0x34, 0x38, 0x35, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x3a, - 0x37, 0x3a, 0x37, 0x3a, 0x38, 0x3b, 0x38, 0x3b, 0x39, 0x3b, 0x39, 0x3b, - 0x00, 0xb7, 0x00, 0x8b, 0x02, 0x72, 0x07, 0x65, 0x0c, 0x5c, 0x11, 0x57, - 0x15, 0x53, 0x18, 0x50, 0x1b, 0x4e, 0x1d, 0x4d, 0x20, 0x4b, 0x22, 0x4a, - 0x23, 0x49, 0x25, 0x48, 0x26, 0x48, 0x28, 0x47, 0x29, 0x47, 0x2a, 0x46, - 0x2b, 0x46, 0x2c, 0x45, 0x16, 0x6d, 0x1f, 0x5f, 0x24, 0x57, 0x28, 0x53, - 0x2b, 0x50, 0x2d, 0x4d, 0x2f, 0x4c, 0x31, 0x4a, 0x32, 0x49, 0x33, 0x48, - 0x34, 0x48, 0x35, 0x47, 0x35, 0x46, 0x36, 0x46, 0x36, 0x45, 0x37, 0x45, - 0x38, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x8b, 0x00, 0x44, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x00, 0x2e, 0x00, - 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x25, 0x23, 0x28, 0x26, 0x2b, 0x28, 0x2e, - 0x2a, 0x2f, 0x2c, 0x31, 0x2e, 0x32, 0x2f, 0x33, 0x30, 0x34, 0x31, 0x35, - 0x32, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x37, 0x35, 0x38, 0x36, 0x38, - 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x36, 0x39, 0x00, 0xc5, 0x00, 0xa0, - 0x00, 0x87, 0x03, 0x76, 0x06, 0x6b, 0x0a, 0x64, 0x0e, 0x5f, 0x11, 0x5b, - 0x13, 0x57, 0x16, 0x55, 0x18, 0x53, 0x1a, 0x51, 0x1c, 0x50, 0x1e, 0x4e, - 0x20, 0x4d, 0x21, 0x4c, 0x22, 0x4b, 0x24, 0x4a, 0x25, 0x49, 0x26, 0x48, - 0x15, 0x72, 0x1b, 0x66, 0x20, 0x5f, 0x24, 0x5a, 0x27, 0x56, 0x29, 0x53, - 0x2c, 0x51, 0x2d, 0x50, 0x2f, 0x4e, 0x30, 0x4d, 0x31, 0x4c, 0x32, 0x4b, - 0x32, 0x4a, 0x33, 0x49, 0x34, 0x49, 0x35, 0x48, 0x35, 0x48, 0x35, 0x47, - 0x35, 0x46, 0x36, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x6d, 0x00, 0x33, 0x00, 0x01, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x24, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x24, 0x23, 0x26, 0x25, 0x29, 0x27, 0x2b, 0x28, 0x2c, 0x2a, 0x2e, - 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x33, 0x30, 0x33, - 0x32, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, 0x33, 0x36, 0x34, 0x36, - 0x35, 0x36, 0x36, 0x37, 0x00, 0xcb, 0x00, 0xad, 0x00, 0x96, 0x01, 0x84, - 0x03, 0x78, 0x06, 0x70, 0x09, 0x69, 0x0b, 0x64, 0x0e, 0x60, 0x10, 0x5d, - 0x13, 0x59, 0x15, 0x57, 0x17, 0x56, 0x19, 0x54, 0x1a, 0x52, 0x1c, 0x51, - 0x1d, 0x50, 0x1f, 0x4f, 0x1f, 0x4f, 0x21, 0x4e, 0x14, 0x75, 0x19, 0x6b, - 0x1e, 0x64, 0x22, 0x5f, 0x24, 0x5b, 0x27, 0x58, 0x29, 0x56, 0x2a, 0x54, - 0x2c, 0x52, 0x2d, 0x51, 0x2e, 0x4f, 0x2f, 0x4e, 0x30, 0x4e, 0x31, 0x4d, - 0x31, 0x4c, 0x32, 0x4a, 0x33, 0x4a, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, - 0x00, 0x85, 0x00, 0x57, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x23, 0x25, - 0x24, 0x27, 0x25, 0x28, 0x27, 0x2a, 0x28, 0x2c, 0x2a, 0x2d, 0x2b, 0x2e, - 0x2c, 0x2f, 0x2d, 0x30, 0x2e, 0x31, 0x2f, 0x32, 0x30, 0x32, 0x30, 0x33, - 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x33, 0x35, 0x33, 0x36, 0x33, 0x36, - 0x00, 0xcf, 0x00, 0xb6, 0x00, 0xa0, 0x00, 0x8f, 0x01, 0x83, 0x03, 0x79, - 0x05, 0x72, 0x08, 0x6c, 0x0a, 0x68, 0x0c, 0x64, 0x0f, 0x61, 0x10, 0x5e, - 0x12, 0x5b, 0x14, 0x59, 0x16, 0x58, 0x17, 0x56, 0x19, 0x55, 0x1a, 0x53, - 0x1b, 0x52, 0x1c, 0x50, 0x13, 0x77, 0x18, 0x6e, 0x1c, 0x68, 0x1f, 0x63, - 0x22, 0x5f, 0x24, 0x5c, 0x26, 0x59, 0x28, 0x57, 0x29, 0x56, 0x2a, 0x54, - 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x50, 0x2e, 0x4f, 0x2f, 0x4f, 0x31, 0x4e, - 0x31, 0x4d, 0x31, 0x4d, 0x31, 0x4c, 0x32, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, - 0x00, 0x48, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, - 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x23, 0x22, 0x24, 0x24, 0x26, 0x25, 0x27, - 0x26, 0x29, 0x27, 0x2a, 0x28, 0x2b, 0x2a, 0x2d, 0x2a, 0x2d, 0x2c, 0x2e, - 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x31, 0x30, 0x32, 0x30, 0x33, - 0x30, 0x33, 0x31, 0x33, 0x32, 0x33, 0x33, 0x34, 0x00, 0xd1, 0x00, 0xbc, - 0x00, 0xa9, 0x00, 0x99, 0x00, 0x8c, 0x02, 0x82, 0x03, 0x7a, 0x05, 0x74, - 0x07, 0x6f, 0x09, 0x6b, 0x0b, 0x67, 0x0d, 0x63, 0x0f, 0x61, 0x10, 0x5f, - 0x12, 0x5c, 0x14, 0x5a, 0x15, 0x58, 0x16, 0x58, 0x18, 0x57, 0x19, 0x56, - 0x13, 0x78, 0x17, 0x70, 0x1b, 0x6a, 0x1e, 0x66, 0x20, 0x62, 0x22, 0x5f, - 0x24, 0x5c, 0x26, 0x5b, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x56, 0x2b, 0x53, - 0x2c, 0x53, 0x2d, 0x52, 0x2e, 0x51, 0x2e, 0x50, 0x2f, 0x4f, 0x30, 0x4f, - 0x31, 0x4f, 0x31, 0x4e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbb, 0x00, 0xb3, 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, - 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, - 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x22, 0x22, 0x24, 0x23, 0x25, 0x24, 0x27, 0x25, 0x27, 0x27, 0x28, - 0x28, 0x2a, 0x28, 0x2b, 0x2a, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2e, - 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x33, - 0x30, 0x33, 0x31, 0x33, 0x00, 0xd3, 0x00, 0xc1, 0x00, 0xaf, 0x00, 0xa1, - 0x00, 0x94, 0x00, 0x8a, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x76, 0x07, 0x71, - 0x09, 0x6d, 0x0a, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, - 0x12, 0x5d, 0x13, 0x5c, 0x14, 0x59, 0x16, 0x58, 0x13, 0x79, 0x16, 0x72, - 0x19, 0x6d, 0x1c, 0x69, 0x1f, 0x65, 0x20, 0x61, 0x23, 0x60, 0x24, 0x5c, - 0x26, 0x5b, 0x27, 0x59, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, - 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2e, 0x51, 0x2e, 0x4f, 0x2f, 0x4f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb6, - 0x00, 0xa6, 0x00, 0x8e, 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, - 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, 0x26, 0x28, 0x26, 0x28, 0x28, 0x2a, - 0x28, 0x2b, 0x2a, 0x2b, 0x2b, 0x2d, 0x2a, 0x2d, 0x2c, 0x2d, 0x2d, 0x2e, - 0x2d, 0x30, 0x2d, 0x30, 0x2e, 0x30, 0x30, 0x30, 0x30, 0x31, 0x30, 0x32, - 0x00, 0xd4, 0x00, 0xc4, 0x00, 0xb5, 0x00, 0xa7, 0x00, 0x9b, 0x00, 0x91, - 0x01, 0x88, 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x07, 0x73, 0x08, 0x6e, - 0x0a, 0x6b, 0x0b, 0x69, 0x0c, 0x66, 0x0e, 0x63, 0x0f, 0x61, 0x10, 0x60, - 0x12, 0x5f, 0x13, 0x5c, 0x12, 0x79, 0x15, 0x73, 0x18, 0x6e, 0x1b, 0x6a, - 0x1d, 0x67, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, - 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, - 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, - 0x00, 0x7e, 0x00, 0x64, 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, - 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, - 0x24, 0x26, 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, - 0x2a, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, - 0x2d, 0x30, 0x2d, 0x30, 0x2f, 0x30, 0x30, 0x30, 0x00, 0xd5, 0x00, 0xc7, - 0x00, 0xb9, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8f, 0x01, 0x87, - 0x02, 0x82, 0x03, 0x7b, 0x05, 0x77, 0x06, 0x74, 0x08, 0x6f, 0x09, 0x6c, - 0x0b, 0x6b, 0x0c, 0x68, 0x0d, 0x65, 0x0e, 0x63, 0x0f, 0x62, 0x10, 0x60, - 0x12, 0x7a, 0x15, 0x75, 0x18, 0x70, 0x1a, 0x6c, 0x1d, 0x69, 0x1e, 0x65, - 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x24, 0x5d, 0x26, 0x5c, 0x27, 0x5b, - 0x27, 0x58, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2b, 0x53, - 0x2c, 0x53, 0x2e, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, - 0x00, 0x59, 0x00, 0x41, 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, - 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x21, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, 0x25, 0x27, - 0x25, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2d, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, - 0x2d, 0x30, 0x2d, 0x30, 0x00, 0xd6, 0x00, 0xc9, 0x00, 0xbc, 0x00, 0xb1, - 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x94, 0x00, 0x8c, 0x02, 0x86, 0x02, 0x81, - 0x03, 0x7c, 0x05, 0x78, 0x06, 0x75, 0x07, 0x71, 0x09, 0x6d, 0x0a, 0x6c, - 0x0b, 0x6a, 0x0c, 0x67, 0x0d, 0x65, 0x0e, 0x62, 0x12, 0x7b, 0x15, 0x76, - 0x18, 0x72, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x63, - 0x22, 0x60, 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, - 0x28, 0x57, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2a, 0x55, 0x2b, 0x53, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xba, - 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, - 0x00, 0x3a, 0x00, 0x25, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, - 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x21, 0x22, 0x22, 0x23, - 0x22, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x27, 0x26, 0x27, - 0x27, 0x28, 0x27, 0x28, 0x28, 0x2a, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2c, 0x2b, 0x2d, 0x2c, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x2f, - 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xbf, 0x00, 0xb4, 0x00, 0xab, 0x00, 0xa1, - 0x00, 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x85, 0x02, 0x81, 0x04, 0x7c, - 0x05, 0x78, 0x06, 0x76, 0x07, 0x73, 0x08, 0x6f, 0x09, 0x6c, 0x0b, 0x6b, - 0x0c, 0x6a, 0x0c, 0x67, 0x12, 0x7b, 0x15, 0x76, 0x17, 0x72, 0x19, 0x6e, - 0x1a, 0x6c, 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, - 0x23, 0x60, 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x58, - 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, - 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, - 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, - 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, - 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, - 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, - 0x2b, 0x2d, 0x2b, 0x2d, 0x2d, 0x2d, 0x2d, 0x2d, 0x00, 0xd7, 0x00, 0xcc, - 0x00, 0xc2, 0x00, 0xb8, 0x00, 0xae, 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x95, - 0x00, 0x90, 0x01, 0x8a, 0x02, 0x85, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x78, - 0x06, 0x76, 0x07, 0x74, 0x08, 0x70, 0x09, 0x6d, 0x0a, 0x6c, 0x0b, 0x6a, - 0x12, 0x7b, 0x14, 0x77, 0x16, 0x73, 0x18, 0x70, 0x1a, 0x6d, 0x1c, 0x6a, - 0x1d, 0x69, 0x1e, 0x65, 0x20, 0x65, 0x20, 0x62, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x5d, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x28, 0x57, - 0x2a, 0x57, 0x2a, 0x57, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, - 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, - 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, - 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, 0x24, 0x25, - 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, - 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2d, - 0x2b, 0x2d, 0x2c, 0x2d, 0x00, 0xd7, 0x00, 0xcd, 0x00, 0xc4, 0x00, 0xbb, - 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8e, - 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7c, 0x05, 0x79, 0x06, 0x77, - 0x07, 0x75, 0x07, 0x72, 0x09, 0x6e, 0x09, 0x6d, 0x12, 0x7b, 0x14, 0x77, - 0x15, 0x73, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6b, 0x1d, 0x69, 0x1d, 0x67, - 0x1f, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, - 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x27, 0x58, 0x29, 0x57, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, - 0x00, 0xb6, 0x00, 0xad, 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, - 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, - 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, - 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x26, - 0x25, 0x27, 0x27, 0x27, 0x27, 0x28, 0x26, 0x28, 0x28, 0x28, 0x28, 0x29, - 0x28, 0x2b, 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2c, 0x2b, 0x2d, - 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc6, 0x00, 0xbd, 0x00, 0xb4, 0x00, 0xad, - 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x92, 0x01, 0x8d, 0x02, 0x88, - 0x02, 0x84, 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x07, 0x75, - 0x07, 0x73, 0x09, 0x70, 0x12, 0x7c, 0x13, 0x78, 0x15, 0x75, 0x18, 0x72, - 0x19, 0x6e, 0x1a, 0x6d, 0x1c, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, - 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x27, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, - 0x00, 0xa5, 0x00, 0x98, 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, - 0x00, 0x49, 0x00, 0x39, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, - 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x22, 0x23, - 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x25, 0x27, - 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, - 0x29, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd0, - 0x00, 0xc7, 0x00, 0xbe, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, - 0x00, 0x9b, 0x00, 0x95, 0x00, 0x90, 0x01, 0x8c, 0x02, 0x87, 0x02, 0x84, - 0x03, 0x81, 0x04, 0x7d, 0x05, 0x79, 0x06, 0x77, 0x06, 0x76, 0x07, 0x74, - 0x11, 0x7c, 0x13, 0x78, 0x15, 0x76, 0x17, 0x72, 0x18, 0x6f, 0x1a, 0x6d, - 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x68, 0x1f, 0x65, 0x20, 0x65, 0x20, 0x64, - 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5e, 0x25, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x00, 0x2f, 0x00, 0x5f, 0x00, 0x99, 0x00, 0xac, - 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, - 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, - 0x00, 0x9a, 0x00, 0x6d, 0x00, 0x8d, 0x00, 0x9b, 0x00, 0xa6, 0x00, 0xb2, - 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0b, 0x3b, 0x00, 0x6f, 0x00, 0x9f, 0x00, 0xaf, - 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, - 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xba, - 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbe, - 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x24, 0x23, 0x24, - 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, - 0x27, 0x29, 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x29, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2b, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc0, - 0x00, 0xba, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, - 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, - 0x04, 0x7d, 0x05, 0x79, 0x06, 0x78, 0x06, 0x76, 0x11, 0x7c, 0x13, 0x79, - 0x15, 0x76, 0x17, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, 0x1c, 0x69, - 0x1d, 0x69, 0x1d, 0x66, 0x20, 0x65, 0x20, 0x65, 0x20, 0x63, 0x22, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x5f, 0x00, 0x8b, 0x00, 0x9f, 0x00, 0xaa, - 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, - 0x00, 0xbb, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x6d, 0x00, 0x54, - 0x00, 0x6b, 0x00, 0x87, 0x00, 0x98, 0x00, 0xa6, 0x00, 0xb0, 0x00, 0xb3, - 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbb, - 0x00, 0xbc, 0x00, 0xbc, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x17, 0x07, 0x02, 0x27, 0x00, 0x6f, 0x00, 0x93, 0x00, 0xa4, 0x00, 0xad, - 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, - 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, 0x0f, 0x0f, 0x00, 0x3f, - 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb5, 0x00, 0xb7, - 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbc, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbd, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, - 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, - 0x27, 0x28, 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x2a, 0x2b, - 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, - 0x00, 0xae, 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x96, 0x00, 0x92, - 0x01, 0x8e, 0x01, 0x89, 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, - 0x05, 0x7a, 0x06, 0x78, 0x11, 0x7c, 0x13, 0x79, 0x15, 0x76, 0x16, 0x73, - 0x18, 0x72, 0x19, 0x6e, 0x1a, 0x6d, 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, - 0x1e, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x62, 0x22, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x5f, 0x24, 0x5c, 0x26, 0x5c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x05, 0x00, 0x44, 0x00, 0x6d, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, - 0x00, 0xa6, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, - 0x00, 0xb6, 0x00, 0xb7, 0x00, 0x8d, 0x00, 0x6b, 0x00, 0x28, 0x00, 0x52, - 0x00, 0x70, 0x00, 0x85, 0x00, 0x95, 0x00, 0x9f, 0x00, 0xa6, 0x00, 0xab, - 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, 0x00, 0xb7, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x12, 0x02, - 0x00, 0x22, 0x00, 0x58, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9c, 0x00, 0xa4, - 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb7, - 0x00, 0xb8, 0x00, 0xb9, 0x26, 0x00, 0x05, 0x05, 0x00, 0x3f, 0x00, 0x6d, - 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, 0x00, 0xae, 0x00, 0xb1, - 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xb9, 0x00, 0xba, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, - 0x23, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x24, 0x25, 0x25, 0x25, - 0x25, 0x27, 0x26, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x29, 0x28, 0x2b, 0x28, 0x2b, 0x00, 0xd9, 0x00, 0xd2, - 0x00, 0xca, 0x00, 0xc4, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, - 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, - 0x02, 0x89, 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x05, 0x7a, - 0x11, 0x7c, 0x13, 0x7a, 0x15, 0x76, 0x15, 0x74, 0x18, 0x72, 0x18, 0x6f, - 0x1a, 0x6d, 0x1a, 0x6c, 0x1c, 0x69, 0x1d, 0x69, 0x1d, 0x67, 0x1f, 0x65, - 0x20, 0x65, 0x20, 0x65, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x5f, 0x24, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x33, 0x00, 0x57, 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, - 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, - 0x00, 0x9b, 0x00, 0x87, 0x00, 0x52, 0x00, 0x11, 0x00, 0x39, 0x00, 0x57, - 0x00, 0x70, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa3, - 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x36, 0x00, 0x25, 0x00, 0x0b, 0x09, 0x00, 0x21, - 0x00, 0x4a, 0x00, 0x68, 0x00, 0x7d, 0x00, 0x8b, 0x00, 0x96, 0x00, 0x9d, - 0x00, 0xa3, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, - 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, 0x00, 0x62, 0x00, 0x7a, - 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, 0x00, 0xa9, 0x00, 0xac, - 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x21, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x23, 0x23, 0x23, 0x23, 0x24, - 0x23, 0x24, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25, 0x25, 0x26, 0x25, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x27, 0x28, 0x27, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x2a, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc6, - 0x00, 0xbe, 0x00, 0xb9, 0x00, 0xb1, 0x00, 0xac, 0x00, 0xa6, 0x00, 0xa1, - 0x00, 0x9d, 0x00, 0x97, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x02, 0x88, - 0x02, 0x85, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7e, 0x11, 0x7c, 0x13, 0x7a, - 0x15, 0x76, 0x15, 0x75, 0x18, 0x72, 0x18, 0x71, 0x1a, 0x6d, 0x1a, 0x6d, - 0x1b, 0x6a, 0x1d, 0x69, 0x1d, 0x69, 0x1e, 0x66, 0x20, 0x65, 0x20, 0x65, - 0x20, 0x64, 0x21, 0x61, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x28, - 0x00, 0x48, 0x00, 0x5f, 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, - 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0x98, - 0x00, 0x70, 0x00, 0x39, 0x00, 0x02, 0x00, 0x28, 0x00, 0x48, 0x00, 0x5f, - 0x00, 0x71, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x91, 0x00, 0x98, 0x00, 0x9d, - 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0x00, 0x2f, 0x00, 0x16, 0x00, 0x08, 0x0e, 0x00, 0x20, 0x00, 0x42, - 0x00, 0x5c, 0x00, 0x6f, 0x00, 0x7e, 0x00, 0x89, 0x00, 0x92, 0x00, 0x99, - 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x38, 0x00, 0x2a, 0x00, - 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7f, - 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xab, 0x00, 0xad, 0x16, 0xa4, 0x01, 0xbb, 0x00, 0xc6, 0x00, 0xcc, - 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd5, 0x00, 0xd6, - 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd8, 0x00, 0xd9, - 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xda, 0x05, 0x9d, 0x00, 0xbc, - 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xcf, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd5, - 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd8, - 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x02, 0xbd, 0x00, 0xcc, 0x00, 0xd1, 0x00, 0xd5, - 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xd9, 0x00, 0xd9, - 0x00, 0xd9, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xda, 0x00, 0xdb, - 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x3d, - 0x00, 0x52, 0x00, 0x64, 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, - 0x00, 0x93, 0x00, 0x98, 0x00, 0xb2, 0x00, 0xa6, 0x00, 0x85, 0x00, 0x57, - 0x00, 0x28, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x52, 0x00, 0x64, - 0x00, 0x71, 0x00, 0x7c, 0x00, 0x85, 0x00, 0x8d, 0x00, 0x93, 0x00, 0x98, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, - 0x23, 0x00, 0x0e, 0x02, 0x06, 0x12, 0x00, 0x20, 0x00, 0x3c, 0x00, 0x52, - 0x00, 0x64, 0x00, 0x73, 0x00, 0x7e, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x95, - 0x00, 0x9a, 0x00, 0x9e, 0x3a, 0x00, 0x31, 0x00, 0x19, 0x00, 0x00, 0x05, - 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, 0x00, 0x77, 0x00, 0x82, - 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa5, - 0x23, 0x74, 0x09, 0x91, 0x02, 0xa3, 0x00, 0xaf, 0x00, 0xb7, 0x00, 0xbd, - 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, - 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd2, - 0x00, 0xd2, 0x00, 0xd3, 0x0d, 0x6b, 0x01, 0x8e, 0x00, 0xa4, 0x00, 0xb0, - 0x00, 0xb9, 0x00, 0xbe, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc8, 0x00, 0xca, - 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xcf, 0x00, 0xd0, 0x00, 0xd1, - 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x06, 0xa4, 0x00, 0xb5, 0x00, 0xc0, 0x00, 0xc6, 0x00, 0xcb, 0x00, 0xcd, - 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, - 0x00, 0xd5, 0x00, 0xd6, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd7, 0x00, 0xd7, - 0x00, 0xd8, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, - 0x00, 0x59, 0x00, 0x66, 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, - 0x00, 0xba, 0x00, 0xb0, 0x00, 0x95, 0x00, 0x70, 0x00, 0x48, 0x00, 0x21, - 0x00, 0x01, 0x00, 0x1c, 0x00, 0x35, 0x00, 0x48, 0x00, 0x59, 0x00, 0x66, - 0x00, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x89, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x18, 0x00, - 0x0c, 0x07, 0x05, 0x14, 0x00, 0x20, 0x00, 0x37, 0x00, 0x4c, 0x00, 0x5c, - 0x00, 0x6a, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x86, 0x00, 0x8d, 0x00, 0x92, - 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, 0x00, 0x0f, 0x00, 0x29, - 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x84, - 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, 0x2a, 0x61, 0x11, 0x78, - 0x07, 0x8a, 0x03, 0x98, 0x01, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, - 0x00, 0xba, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc5, 0x00, 0xc6, - 0x00, 0xc7, 0x00, 0xc9, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, - 0x12, 0x51, 0x04, 0x73, 0x00, 0x89, 0x00, 0x9a, 0x00, 0xa4, 0x00, 0xad, - 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc3, - 0x00, 0xc5, 0x00, 0xc6, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, - 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x09, 0x97, 0x02, 0xa8, - 0x00, 0xb3, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc7, 0x00, 0xca, - 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd1, - 0x00, 0xd2, 0x00, 0xd3, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0xd4, 0x00, 0xd5, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, - 0x00, 0x5d, 0x00, 0x68, 0x00, 0x72, 0x00, 0x7a, 0x00, 0xbb, 0x00, 0xb3, - 0x00, 0x9f, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x1c, 0x00, 0x00, - 0x00, 0x19, 0x00, 0x2e, 0x00, 0x41, 0x00, 0x50, 0x00, 0x5d, 0x00, 0x68, - 0x00, 0x72, 0x00, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x20, 0x00, 0x10, 0x00, 0x0a, 0x0b, - 0x04, 0x16, 0x00, 0x20, 0x00, 0x34, 0x00, 0x46, 0x00, 0x56, 0x00, 0x62, - 0x00, 0x6d, 0x00, 0x77, 0x00, 0x7f, 0x00, 0x85, 0x3d, 0x00, 0x38, 0x00, - 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x2c, 0x00, 0x3f, - 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, 0x00, 0x7e, 0x00, 0x85, - 0x00, 0x8b, 0x00, 0x91, 0x2e, 0x59, 0x17, 0x6b, 0x0c, 0x7a, 0x06, 0x87, - 0x03, 0x92, 0x01, 0x9b, 0x00, 0xa2, 0x00, 0xa8, 0x00, 0xad, 0x00, 0xb1, - 0x00, 0xb5, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, - 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, 0x15, 0x45, 0x08, 0x60, - 0x02, 0x77, 0x00, 0x87, 0x00, 0x94, 0x00, 0x9d, 0x00, 0xa4, 0x00, 0xaa, - 0x00, 0xaf, 0x00, 0xb3, 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbc, 0x00, 0xbe, - 0x00, 0xc0, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, 0x00, 0xc6, 0x00, 0xc7, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0a, 0x91, 0x04, 0x9e, 0x01, 0xaa, 0x00, 0xb2, - 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc6, 0x00, 0xc8, - 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcd, 0x00, 0xce, 0x00, 0xcf, - 0x00, 0xd0, 0x00, 0xd0, 0x00, 0xd1, 0x00, 0xd2, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x16, 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, - 0x00, 0x60, 0x00, 0x6a, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xa6, 0x00, 0x8e, - 0x00, 0x71, 0x00, 0x52, 0x00, 0x35, 0x00, 0x19, 0x00, 0x00, 0x00, 0x16, - 0x00, 0x29, 0x00, 0x3a, 0x00, 0x49, 0x00, 0x55, 0x00, 0x60, 0x00, 0x6a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, - 0x33, 0x00, 0x27, 0x00, 0x18, 0x00, 0x0d, 0x04, 0x08, 0x0e, 0x04, 0x17, - 0x00, 0x20, 0x00, 0x32, 0x00, 0x42, 0x00, 0x50, 0x00, 0x5c, 0x00, 0x67, - 0x00, 0x70, 0x00, 0x78, 0x3d, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x1f, 0x00, - 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, 0x00, 0x3f, 0x00, 0x4e, - 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, 0x00, 0x80, 0x00, 0x86, - 0x31, 0x53, 0x1c, 0x62, 0x11, 0x70, 0x0a, 0x7b, 0x06, 0x85, 0x03, 0x8e, - 0x02, 0x96, 0x00, 0x9c, 0x00, 0xa2, 0x00, 0xa7, 0x00, 0xab, 0x00, 0xae, - 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, - 0x00, 0xbe, 0x00, 0xbf, 0x17, 0x3d, 0x0b, 0x55, 0x04, 0x67, 0x01, 0x78, - 0x00, 0x85, 0x00, 0x90, 0x00, 0x98, 0x00, 0x9f, 0x00, 0xa5, 0x00, 0xaa, - 0x00, 0xad, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, - 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc0, 0x00, 0xc1, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0b, 0x8d, 0x05, 0x99, 0x02, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, - 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc3, 0x00, 0xc5, 0x00, 0xc7, - 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xcb, 0x00, 0xcc, 0x00, 0xcc, 0x00, 0xcd, - 0x00, 0xce, 0x00, 0xcf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x13, 0x00, 0x25, 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, - 0x00, 0xbc, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x97, 0x00, 0x7e, 0x00, 0x64, - 0x00, 0x48, 0x00, 0x2e, 0x00, 0x16, 0x00, 0x00, 0x00, 0x13, 0x00, 0x25, - 0x00, 0x35, 0x00, 0x43, 0x00, 0x4f, 0x00, 0x59, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, - 0x1f, 0x00, 0x12, 0x00, 0x0c, 0x07, 0x07, 0x10, 0x03, 0x18, 0x00, 0x20, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4c, 0x00, 0x57, 0x00, 0x61, 0x00, 0x6a, - 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, 0x14, 0x00, 0x02, 0x00, - 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4d, 0x00, 0x58, - 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, 0x33, 0x50, 0x20, 0x5c, - 0x15, 0x68, 0x0e, 0x72, 0x09, 0x7c, 0x05, 0x84, 0x03, 0x8c, 0x02, 0x92, - 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, - 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, - 0x18, 0x38, 0x0d, 0x4c, 0x06, 0x5e, 0x03, 0x6c, 0x01, 0x7a, 0x00, 0x84, - 0x00, 0x8d, 0x00, 0x95, 0x00, 0x9b, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, - 0x00, 0xba, 0x00, 0xbc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0c, 0x8a, 0x06, 0x94, - 0x03, 0x9d, 0x01, 0xa4, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb5, 0x00, 0xb9, - 0x00, 0xbc, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc6, - 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xc9, 0x00, 0xca, 0x00, 0xcb, 0x00, 0xcc, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x49, 0x00, 0xbd, 0x00, 0xb9, - 0x00, 0xae, 0x00, 0x9e, 0x00, 0x89, 0x00, 0x71, 0x00, 0x59, 0x00, 0x41, - 0x00, 0x29, 0x00, 0x13, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, - 0x00, 0x3d, 0x00, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, - 0x0e, 0x02, 0x0a, 0x0a, 0x06, 0x11, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2e, - 0x00, 0x3c, 0x00, 0x48, 0x00, 0x53, 0x00, 0x5c, 0x3e, 0x00, 0x3c, 0x00, - 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, 0x00, 0x04, 0x00, 0x14, - 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x56, 0x00, 0x60, - 0x00, 0x68, 0x00, 0x70, 0x34, 0x4e, 0x23, 0x58, 0x18, 0x62, 0x10, 0x6c, - 0x0b, 0x75, 0x08, 0x7c, 0x05, 0x83, 0x03, 0x8a, 0x02, 0x8f, 0x01, 0x95, - 0x00, 0x9a, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa9, 0x00, 0xac, - 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb5, 0x1a, 0x35, 0x0f, 0x47, - 0x08, 0x55, 0x04, 0x64, 0x02, 0x70, 0x00, 0x7a, 0x00, 0x83, 0x00, 0x8b, - 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xab, 0x00, 0xae, 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb7, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x89, 0x07, 0x92, 0x04, 0x99, 0x02, 0xa0, - 0x01, 0xa6, 0x00, 0xab, 0x00, 0xb0, 0x00, 0xb4, 0x00, 0xb7, 0x00, 0xba, - 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc4, 0x00, 0xc5, - 0x00, 0xc6, 0x00, 0xc7, 0x00, 0xc8, 0x00, 0xca, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2d, 0x00, 0x39, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, - 0x00, 0x91, 0x00, 0x7c, 0x00, 0x66, 0x00, 0x50, 0x00, 0x3a, 0x00, 0x25, - 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, - 0x38, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x13, 0x00, 0x0d, 0x05, - 0x09, 0x0c, 0x06, 0x13, 0x03, 0x19, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x3a, - 0x00, 0x45, 0x00, 0x4f, 0x3e, 0x00, 0x3c, 0x00, 0x36, 0x00, 0x2d, 0x00, - 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, 0x00, 0x18, 0x00, 0x26, - 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, 0x00, 0x5d, 0x00, 0x65, - 0x35, 0x4c, 0x26, 0x55, 0x1b, 0x5e, 0x13, 0x67, 0x0e, 0x6f, 0x0a, 0x76, - 0x07, 0x7d, 0x05, 0x82, 0x03, 0x88, 0x02, 0x8e, 0x02, 0x92, 0x01, 0x97, - 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, - 0x00, 0xac, 0x00, 0xaf, 0x1b, 0x33, 0x10, 0x42, 0x0a, 0x50, 0x05, 0x5c, - 0x03, 0x68, 0x01, 0x72, 0x00, 0x7b, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x90, - 0x00, 0x96, 0x00, 0x99, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0d, 0x88, 0x08, 0x8f, 0x05, 0x96, 0x02, 0x9c, 0x01, 0xa2, 0x00, 0xa7, - 0x00, 0xac, 0x00, 0xb0, 0x00, 0xb3, 0x00, 0xb6, 0x00, 0xb9, 0x00, 0xbb, - 0x00, 0xbd, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc5, - 0x00, 0xc6, 0x00, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, - 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb3, 0x00, 0xa7, 0x00, 0x98, 0x00, 0x85, - 0x00, 0x72, 0x00, 0x5d, 0x00, 0x49, 0x00, 0x35, 0x00, 0x22, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x33, 0x00, - 0x2c, 0x00, 0x23, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x0c, 0x07, 0x08, 0x0e, - 0x05, 0x14, 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x38, 0x00, 0x42, - 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, 0x25, 0x00, 0x19, 0x00, - 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x34, - 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, 0x36, 0x4a, 0x28, 0x52, - 0x1d, 0x5b, 0x16, 0x63, 0x10, 0x6a, 0x0c, 0x71, 0x09, 0x77, 0x07, 0x7d, - 0x05, 0x82, 0x03, 0x87, 0x02, 0x8c, 0x02, 0x90, 0x01, 0x94, 0x00, 0x99, - 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa1, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xaa, - 0x1b, 0x30, 0x12, 0x3e, 0x0c, 0x4b, 0x07, 0x57, 0x04, 0x61, 0x02, 0x6b, - 0x01, 0x73, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8f, 0x00, 0x93, - 0x00, 0x97, 0x00, 0x9c, 0x00, 0x9f, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa8, - 0x00, 0xaa, 0x00, 0xac, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0d, 0x86, 0x09, 0x8d, - 0x06, 0x94, 0x03, 0x9a, 0x02, 0x9f, 0x01, 0xa4, 0x00, 0xa8, 0x00, 0xac, - 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbc, - 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0xc3, 0x00, 0xc4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1b, 0x00, 0xbe, 0x00, 0xbb, - 0x00, 0xb5, 0x00, 0xaa, 0x00, 0x9d, 0x00, 0x8d, 0x00, 0x7b, 0x00, 0x68, - 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x0e, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x26, 0x00, - 0x1d, 0x00, 0x14, 0x00, 0x0e, 0x03, 0x0b, 0x09, 0x08, 0x0f, 0x05, 0x15, - 0x02, 0x1a, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x3f, 0x00, 0x3d, 0x00, - 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, 0x12, 0x00, 0x06, 0x00, - 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x35, 0x00, 0x3f, - 0x00, 0x49, 0x00, 0x51, 0x37, 0x49, 0x2a, 0x51, 0x20, 0x58, 0x18, 0x5f, - 0x13, 0x66, 0x0f, 0x6c, 0x0b, 0x73, 0x09, 0x78, 0x07, 0x7e, 0x05, 0x82, - 0x03, 0x86, 0x02, 0x8b, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x96, 0x00, 0x9a, - 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa4, 0x1c, 0x2f, 0x13, 0x3b, - 0x0d, 0x47, 0x09, 0x51, 0x05, 0x5c, 0x04, 0x65, 0x02, 0x6e, 0x01, 0x75, - 0x00, 0x7c, 0x00, 0x82, 0x00, 0x88, 0x00, 0x8d, 0x00, 0x91, 0x00, 0x96, - 0x00, 0x99, 0x00, 0x9c, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa5, 0x00, 0xa7, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x86, 0x09, 0x8c, 0x06, 0x92, 0x04, 0x97, - 0x02, 0x9c, 0x02, 0xa1, 0x01, 0xa5, 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, - 0x00, 0xb2, 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, - 0x00, 0xbe, 0x00, 0xbf, 0x00, 0xc1, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0d, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xad, - 0x00, 0xa1, 0x00, 0x93, 0x00, 0x83, 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, - 0x00, 0x3d, 0x00, 0x2d, 0x00, 0x1d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, - 0x10, 0x00, 0x0d, 0x05, 0x0a, 0x0b, 0x07, 0x10, 0x04, 0x16, 0x02, 0x1b, - 0x00, 0x1f, 0x00, 0x2a, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x33, 0x00, - 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0b, - 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, 0x00, 0x3f, 0x00, 0x48, - 0x38, 0x48, 0x2b, 0x4f, 0x22, 0x56, 0x1a, 0x5c, 0x15, 0x63, 0x11, 0x69, - 0x0d, 0x6e, 0x0a, 0x74, 0x08, 0x78, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x85, - 0x03, 0x8a, 0x02, 0x8e, 0x02, 0x90, 0x01, 0x93, 0x00, 0x98, 0x00, 0x9b, - 0x00, 0x9d, 0x00, 0x9f, 0x1c, 0x2d, 0x14, 0x39, 0x0e, 0x44, 0x0a, 0x4e, - 0x07, 0x57, 0x04, 0x60, 0x02, 0x67, 0x01, 0x6f, 0x00, 0x76, 0x00, 0x7d, - 0x00, 0x81, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x94, 0x00, 0x97, - 0x00, 0x9a, 0x00, 0x9d, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0e, 0x85, 0x0a, 0x8b, 0x07, 0x90, 0x05, 0x95, 0x03, 0x9a, 0x02, 0x9e, - 0x01, 0xa2, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, - 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbd, - 0x00, 0xbe, 0x00, 0xbf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x98, - 0x00, 0x89, 0x00, 0x7a, 0x00, 0x6a, 0x00, 0x59, 0x00, 0x49, 0x00, 0x39, - 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, - 0x32, 0x00, 0x2c, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0e, 0x01, - 0x0c, 0x07, 0x09, 0x0c, 0x06, 0x11, 0x04, 0x16, 0x02, 0x1b, 0x00, 0x1f, - 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, 0x2e, 0x00, 0x25, 0x00, - 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x19, - 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, 0x38, 0x48, 0x2d, 0x4e, - 0x23, 0x54, 0x1c, 0x5a, 0x17, 0x5f, 0x12, 0x66, 0x0f, 0x6a, 0x0c, 0x71, - 0x0a, 0x74, 0x08, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x89, - 0x02, 0x8d, 0x02, 0x8f, 0x01, 0x92, 0x01, 0x95, 0x00, 0x99, 0x00, 0x9b, - 0x1c, 0x2d, 0x15, 0x37, 0x0f, 0x41, 0x0b, 0x4a, 0x07, 0x53, 0x05, 0x5b, - 0x04, 0x63, 0x02, 0x69, 0x01, 0x70, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, - 0x00, 0x87, 0x00, 0x8a, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x96, 0x00, 0x99, - 0x00, 0x9b, 0x00, 0x9e, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x85, 0x0a, 0x8a, - 0x07, 0x8f, 0x05, 0x93, 0x03, 0x98, 0x02, 0x9c, 0x02, 0xa0, 0x01, 0xa3, - 0x00, 0xa6, 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb2, 0x00, 0xb3, - 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x39, 0x47, 0x2e, 0x4d, 0x25, 0x53, 0x1e, 0x58, - 0x19, 0x5e, 0x14, 0x63, 0x11, 0x68, 0x0e, 0x6c, 0x0b, 0x72, 0x09, 0x75, - 0x07, 0x79, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8c, - 0x02, 0x8e, 0x01, 0x91, 0x01, 0x93, 0x00, 0x97, 0x1d, 0x2c, 0x16, 0x36, - 0x10, 0x3e, 0x0c, 0x48, 0x09, 0x50, 0x06, 0x58, 0x04, 0x5f, 0x02, 0x66, - 0x02, 0x6c, 0x01, 0x71, 0x00, 0x77, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, - 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x9a, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0e, 0x84, 0x0b, 0x89, 0x08, 0x8d, 0x06, 0x92, - 0x04, 0x96, 0x03, 0x9a, 0x02, 0x9e, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa7, - 0x00, 0xaa, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, - 0x00, 0xb7, 0x00, 0xb8, 0x00, 0xba, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x39, 0x46, 0x2f, 0x4c, 0x26, 0x51, 0x20, 0x56, 0x1a, 0x5c, 0x16, 0x60, - 0x12, 0x66, 0x0f, 0x69, 0x0c, 0x6e, 0x0b, 0x73, 0x09, 0x76, 0x07, 0x7a, - 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8e, - 0x02, 0x90, 0x01, 0x92, 0x1d, 0x2b, 0x16, 0x34, 0x11, 0x3d, 0x0c, 0x45, - 0x0a, 0x4c, 0x07, 0x54, 0x05, 0x5b, 0x04, 0x61, 0x02, 0x67, 0x01, 0x6d, - 0x01, 0x73, 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x86, 0x00, 0x89, - 0x00, 0x8d, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0e, 0x84, 0x0b, 0x88, 0x08, 0x8d, 0x06, 0x91, 0x05, 0x94, 0x03, 0x98, - 0x02, 0x9c, 0x02, 0x9f, 0x01, 0xa2, 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xaa, - 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb5, 0x00, 0xb6, - 0x00, 0xb8, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x46, 0x30, 0x4b, - 0x28, 0x50, 0x21, 0x55, 0x1c, 0x59, 0x17, 0x5e, 0x14, 0x63, 0x10, 0x68, - 0x0e, 0x6b, 0x0c, 0x70, 0x0a, 0x74, 0x08, 0x76, 0x07, 0x7b, 0x06, 0x7e, - 0x05, 0x81, 0x04, 0x83, 0x03, 0x87, 0x02, 0x8a, 0x02, 0x8d, 0x02, 0x8f, - 0x1e, 0x2a, 0x17, 0x33, 0x12, 0x3b, 0x0e, 0x43, 0x0a, 0x4a, 0x07, 0x51, - 0x05, 0x58, 0x04, 0x5e, 0x03, 0x64, 0x02, 0x69, 0x01, 0x6f, 0x00, 0x74, - 0x00, 0x78, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, - 0x00, 0x8f, 0x00, 0x93, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0b, 0x88, - 0x09, 0x8c, 0x07, 0x90, 0x05, 0x93, 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, - 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xad, - 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb8, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x46, 0x31, 0x4a, 0x29, 0x4e, 0x22, 0x54, - 0x1d, 0x57, 0x19, 0x5d, 0x15, 0x60, 0x12, 0x65, 0x0f, 0x69, 0x0d, 0x6c, - 0x0b, 0x71, 0x09, 0x74, 0x08, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, - 0x04, 0x83, 0x03, 0x86, 0x02, 0x8a, 0x02, 0x8c, 0x1e, 0x2a, 0x18, 0x32, - 0x12, 0x39, 0x0f, 0x40, 0x0c, 0x48, 0x09, 0x4f, 0x07, 0x55, 0x05, 0x5a, - 0x04, 0x60, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x70, 0x00, 0x75, 0x00, 0x79, - 0x00, 0x7d, 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8b, 0x07, 0x8e, - 0x06, 0x92, 0x04, 0x96, 0x03, 0x99, 0x02, 0x9b, 0x02, 0x9e, 0x01, 0xa1, - 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, - 0x00, 0xb0, 0x00, 0xb2, 0x00, 0xb4, 0x00, 0xb5, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0x45, 0x31, 0x49, 0x2a, 0x4d, 0x24, 0x52, 0x1f, 0x56, 0x1a, 0x5b, - 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, 0x0e, 0x6a, 0x0c, 0x6e, 0x0b, 0x72, - 0x09, 0x75, 0x07, 0x77, 0x07, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, - 0x03, 0x85, 0x02, 0x89, 0x1e, 0x29, 0x18, 0x31, 0x13, 0x38, 0x0f, 0x3f, - 0x0c, 0x45, 0x0a, 0x4c, 0x07, 0x52, 0x05, 0x58, 0x04, 0x5d, 0x04, 0x63, - 0x02, 0x67, 0x01, 0x6c, 0x01, 0x71, 0x00, 0x75, 0x00, 0x79, 0x00, 0x7d, - 0x00, 0x81, 0x00, 0x84, 0x00, 0x88, 0x00, 0x8b, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0f, 0x83, 0x0c, 0x87, 0x09, 0x8a, 0x07, 0x8e, 0x06, 0x91, 0x05, 0x94, - 0x03, 0x97, 0x02, 0x9a, 0x02, 0x9d, 0x02, 0xa0, 0x01, 0xa2, 0x00, 0xa4, - 0x00, 0xa7, 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, - 0x00, 0xb2, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x45, 0x32, 0x48, - 0x2b, 0x4d, 0x25, 0x51, 0x20, 0x55, 0x1b, 0x59, 0x18, 0x5e, 0x14, 0x60, - 0x12, 0x65, 0x0f, 0x68, 0x0d, 0x6b, 0x0c, 0x6f, 0x0a, 0x73, 0x09, 0x75, - 0x07, 0x77, 0x06, 0x7b, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, - 0x1e, 0x29, 0x19, 0x30, 0x14, 0x37, 0x0f, 0x3d, 0x0c, 0x44, 0x0a, 0x4a, - 0x07, 0x50, 0x06, 0x55, 0x05, 0x5b, 0x04, 0x60, 0x02, 0x64, 0x02, 0x69, - 0x01, 0x6d, 0x01, 0x72, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, - 0x00, 0x83, 0x00, 0x87, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x83, 0x0c, 0x86, - 0x0a, 0x8a, 0x07, 0x8d, 0x06, 0x90, 0x05, 0x93, 0x03, 0x96, 0x03, 0x99, - 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, 0x01, 0xa3, 0x00, 0xa5, 0x00, 0xa7, - 0x00, 0xa9, 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb2, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3b, 0x45, 0x33, 0x48, 0x2c, 0x4d, 0x26, 0x4f, - 0x21, 0x55, 0x1c, 0x57, 0x19, 0x5c, 0x16, 0x5f, 0x13, 0x63, 0x10, 0x67, - 0x0e, 0x6a, 0x0c, 0x6c, 0x0b, 0x71, 0x0a, 0x73, 0x09, 0x76, 0x07, 0x78, - 0x06, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x1e, 0x28, 0x19, 0x2f, - 0x15, 0x35, 0x10, 0x3c, 0x0d, 0x42, 0x0b, 0x48, 0x09, 0x4e, 0x07, 0x53, - 0x05, 0x58, 0x04, 0x5d, 0x04, 0x62, 0x02, 0x66, 0x02, 0x6b, 0x01, 0x6f, - 0x00, 0x73, 0x00, 0x76, 0x00, 0x7a, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x83, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x0f, 0x82, 0x0c, 0x86, 0x0a, 0x89, 0x08, 0x8c, - 0x06, 0x8f, 0x05, 0x92, 0x04, 0x95, 0x03, 0x98, 0x02, 0x9a, 0x02, 0x9d, - 0x02, 0x9f, 0x01, 0xa1, 0x01, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xa9, - 0x00, 0xab, 0x00, 0xad, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x44, 0x33, 0x47, 0x2c, 0x4c, 0x27, 0x4f, 0x22, 0x54, 0x1e, 0x56, - 0x1a, 0x5a, 0x17, 0x5e, 0x14, 0x60, 0x12, 0x65, 0x10, 0x68, 0x0e, 0x6a, - 0x0c, 0x6e, 0x0b, 0x72, 0x09, 0x74, 0x08, 0x76, 0x07, 0x78, 0x06, 0x7c, - 0x06, 0x7f, 0x05, 0x81, 0x1e, 0x28, 0x19, 0x2f, 0x16, 0x35, 0x12, 0x3b, - 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4c, 0x07, 0x51, 0x05, 0x55, 0x05, 0x5b, - 0x04, 0x5f, 0x02, 0x63, 0x02, 0x68, 0x01, 0x6b, 0x01, 0x70, 0x00, 0x73, - 0x00, 0x77, 0x00, 0x7b, 0x00, 0x7d, 0x00, 0x81, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x0f, 0x82, 0x0c, 0x86, 0x0b, 0x89, 0x09, 0x8c, 0x07, 0x8f, 0x06, 0x91, - 0x05, 0x94, 0x03, 0x97, 0x02, 0x99, 0x02, 0x9c, 0x02, 0x9e, 0x01, 0xa0, - 0x01, 0xa2, 0x00, 0xa4, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xac, - 0x00, 0xad, 0x00, 0xaf, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x23, 0x25, 0x22, - 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0x44, 0x05, 0x25, 0x0d, 0x23, 0x12, 0x22, - 0x15, 0x22, 0x17, 0x22, 0x18, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1b, 0x22, - 0x1c, 0x22, 0x1c, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1d, 0x22, 0x1e, 0x22, - 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x1e, 0x22, 0x10, 0x33, 0x13, 0x23, - 0x17, 0x22, 0x1a, 0x22, 0x1b, 0x22, 0x1c, 0x22, 0x1d, 0x22, 0x1e, 0x22, - 0x1e, 0x22, 0x1e, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, 0x1f, 0x22, - 0x1f, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, 0x20, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x35, 0x25, 0x2b, 0x23, 0x27, 0x22, 0x25, 0x22, - 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0x9d, 0x00, 0x6b, 0x01, 0x51, 0x04, 0x45, 0x08, 0x3d, 0x0b, 0x38, - 0x0d, 0x35, 0x0f, 0x33, 0x10, 0x30, 0x12, 0x2f, 0x13, 0x2d, 0x14, 0x2d, - 0x15, 0x2c, 0x16, 0x2b, 0x16, 0x2a, 0x17, 0x2a, 0x18, 0x29, 0x18, 0x29, - 0x19, 0x28, 0x19, 0x28, 0x10, 0x5f, 0x10, 0x46, 0x11, 0x39, 0x13, 0x33, - 0x15, 0x2f, 0x16, 0x2d, 0x17, 0x2b, 0x18, 0x2a, 0x19, 0x29, 0x1a, 0x28, - 0x1a, 0x27, 0x1b, 0x27, 0x1b, 0x27, 0x1c, 0x26, 0x1c, 0x26, 0x1c, 0x26, - 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x1d, 0x25, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x2a, 0x2e, 0x26, 0x2a, 0x25, 0x27, 0x24, 0x26, 0x23, 0x25, 0x23, - 0x24, 0x23, 0x24, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x22, 0x22, 0x22, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xbc, 0x00, 0x8e, - 0x00, 0x73, 0x00, 0x60, 0x02, 0x55, 0x04, 0x4c, 0x06, 0x47, 0x08, 0x42, - 0x0a, 0x3e, 0x0c, 0x3b, 0x0d, 0x39, 0x0e, 0x37, 0x0f, 0x36, 0x10, 0x34, - 0x11, 0x33, 0x12, 0x32, 0x12, 0x31, 0x13, 0x30, 0x14, 0x2f, 0x15, 0x2f, - 0x10, 0x6f, 0x10, 0x58, 0x10, 0x4a, 0x11, 0x41, 0x12, 0x3b, 0x13, 0x37, - 0x14, 0x34, 0x15, 0x32, 0x16, 0x30, 0x17, 0x2e, 0x17, 0x2d, 0x18, 0x2c, - 0x18, 0x2c, 0x19, 0x2b, 0x19, 0x2a, 0x1a, 0x2a, 0x1a, 0x29, 0x1a, 0x29, - 0x1b, 0x28, 0x1b, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x2e, 0x31, 0x29, - 0x2c, 0x27, 0x2a, 0x26, 0x28, 0x25, 0x27, 0x24, 0x25, 0x24, 0x25, 0x23, - 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x22, 0x23, 0x22, - 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x22, 0x22, 0x22, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xc6, 0x00, 0xa4, 0x00, 0x89, 0x00, 0x77, - 0x00, 0x67, 0x01, 0x5e, 0x03, 0x55, 0x04, 0x50, 0x05, 0x4b, 0x07, 0x47, - 0x09, 0x44, 0x0a, 0x41, 0x0b, 0x3e, 0x0c, 0x3d, 0x0c, 0x3b, 0x0e, 0x39, - 0x0f, 0x38, 0x0f, 0x37, 0x0f, 0x35, 0x10, 0x35, 0x10, 0x74, 0x10, 0x63, - 0x10, 0x55, 0x10, 0x4c, 0x11, 0x44, 0x11, 0x40, 0x12, 0x3b, 0x13, 0x39, - 0x13, 0x36, 0x14, 0x34, 0x15, 0x33, 0x16, 0x31, 0x16, 0x30, 0x17, 0x2f, - 0x17, 0x2e, 0x18, 0x2d, 0x18, 0x2d, 0x18, 0x2c, 0x18, 0x2b, 0x19, 0x2b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x31, 0x33, 0x2c, 0x2f, 0x29, 0x2b, 0x28, - 0x29, 0x26, 0x28, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x23, 0x23, - 0x23, 0x23, 0x23, 0x22, 0x23, 0x22, 0x23, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xcd, 0x00, 0xb0, 0x00, 0x9a, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6c, - 0x01, 0x64, 0x02, 0x5c, 0x03, 0x57, 0x04, 0x51, 0x05, 0x4e, 0x07, 0x4a, - 0x07, 0x48, 0x09, 0x45, 0x0a, 0x43, 0x0a, 0x40, 0x0c, 0x3f, 0x0c, 0x3d, - 0x0c, 0x3c, 0x0d, 0x3b, 0x10, 0x77, 0x10, 0x69, 0x10, 0x5e, 0x10, 0x54, - 0x10, 0x4d, 0x11, 0x47, 0x11, 0x43, 0x12, 0x3f, 0x12, 0x3c, 0x13, 0x39, - 0x13, 0x38, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x32, 0x16, 0x31, - 0x17, 0x30, 0x17, 0x2f, 0x17, 0x2f, 0x17, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x33, 0x34, 0x2e, 0x30, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, - 0x29, 0x26, 0x27, 0x25, 0x27, 0x25, 0x26, 0x24, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, - 0x23, 0x23, 0x23, 0x23, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xcf, 0x00, 0xb9, - 0x00, 0xa4, 0x00, 0x94, 0x00, 0x85, 0x00, 0x7a, 0x00, 0x70, 0x00, 0x68, - 0x01, 0x61, 0x02, 0x5c, 0x04, 0x57, 0x04, 0x53, 0x05, 0x50, 0x06, 0x4c, - 0x07, 0x4a, 0x07, 0x48, 0x09, 0x45, 0x0a, 0x44, 0x0a, 0x42, 0x0b, 0x41, - 0x10, 0x78, 0x10, 0x6d, 0x10, 0x63, 0x10, 0x5b, 0x10, 0x53, 0x10, 0x4e, - 0x11, 0x49, 0x11, 0x45, 0x11, 0x41, 0x12, 0x3f, 0x13, 0x3c, 0x13, 0x3a, - 0x13, 0x39, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x33, 0x16, 0x33, - 0x16, 0x32, 0x16, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x34, 0x36, 0x30, - 0x32, 0x2d, 0x2f, 0x2b, 0x2d, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, - 0x27, 0x26, 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x23, 0x24, 0x23, 0x24, 0x23, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd2, 0x00, 0xbe, 0x00, 0xad, 0x00, 0x9d, - 0x00, 0x90, 0x00, 0x84, 0x00, 0x7a, 0x00, 0x72, 0x00, 0x6b, 0x01, 0x65, - 0x02, 0x60, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x54, 0x05, 0x51, 0x05, 0x4f, - 0x07, 0x4c, 0x07, 0x4a, 0x07, 0x48, 0x09, 0x46, 0x10, 0x7a, 0x10, 0x70, - 0x10, 0x67, 0x10, 0x5f, 0x10, 0x59, 0x10, 0x53, 0x10, 0x4e, 0x11, 0x4a, - 0x11, 0x46, 0x11, 0x43, 0x12, 0x41, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x13, 0x39, 0x13, 0x38, 0x14, 0x37, 0x14, 0x36, 0x14, 0x35, 0x15, 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x35, 0x37, 0x31, 0x33, 0x2e, 0x30, 0x2c, - 0x2d, 0x2a, 0x2c, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, - 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd3, 0x00, 0xc2, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x98, 0x00, 0x8d, - 0x00, 0x83, 0x00, 0x7b, 0x00, 0x73, 0x00, 0x6e, 0x01, 0x67, 0x01, 0x63, - 0x02, 0x5f, 0x02, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x05, 0x52, 0x05, 0x50, - 0x06, 0x4e, 0x07, 0x4c, 0x10, 0x7a, 0x10, 0x72, 0x10, 0x6a, 0x10, 0x63, - 0x10, 0x5d, 0x10, 0x57, 0x10, 0x52, 0x10, 0x4e, 0x11, 0x4a, 0x11, 0x48, - 0x11, 0x44, 0x11, 0x42, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x13, 0x3a, 0x13, 0x39, 0x14, 0x38, 0x14, 0x37, 0x0f, 0x00, 0x1f, 0x00, - 0x33, 0x00, 0x39, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x3f, 0x00, 0x3f, 0x00, 0x0b, 0x3b, 0x17, 0x07, 0x2c, 0x00, 0x36, 0x00, - 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x3d, 0x00, - 0x4c, 0x00, 0x4c, 0x00, 0x48, 0x00, 0x42, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x3f, 0x00, 0x3f, 0x00, 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, - 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, - 0x3c, 0x37, 0x37, 0x32, 0x34, 0x30, 0x31, 0x2d, 0x2f, 0x2c, 0x2d, 0x2b, - 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x27, 0x28, 0x27, 0x28, 0x27, 0x27, 0x26, - 0x27, 0x25, 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x25, 0x24, 0x24, 0x24, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc5, - 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x83, - 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6f, 0x00, 0x69, 0x01, 0x66, 0x02, 0x61, - 0x02, 0x5e, 0x03, 0x5a, 0x04, 0x58, 0x04, 0x55, 0x05, 0x53, 0x05, 0x51, - 0x10, 0x7b, 0x10, 0x73, 0x10, 0x6c, 0x10, 0x66, 0x10, 0x60, 0x10, 0x5b, - 0x10, 0x56, 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4b, 0x11, 0x48, 0x11, 0x45, - 0x11, 0x44, 0x12, 0x41, 0x12, 0x40, 0x12, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x13, 0x3a, 0x13, 0x39, 0x00, 0x00, 0x05, 0x00, 0x1f, 0x00, 0x2e, 0x00, - 0x35, 0x00, 0x38, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x00, 0x6f, 0x02, 0x27, 0x12, 0x02, 0x25, 0x00, 0x2f, 0x00, 0x35, 0x00, - 0x38, 0x00, 0x3a, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x30, 0x00, 0x39, 0x00, 0x42, 0x00, - 0x41, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x00, 0x7f, 0x00, 0x3f, 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, - 0x35, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, - 0x3d, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3c, 0x37, 0x38, 0x33, - 0x34, 0x31, 0x32, 0x2f, 0x30, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x29, - 0x2b, 0x28, 0x29, 0x28, 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x25, - 0x27, 0x25, 0x27, 0x25, 0x25, 0x25, 0x25, 0x25, 0x25, 0x24, 0x25, 0x24, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd5, 0x00, 0xc8, 0x00, 0xbb, 0x00, 0xaf, - 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x92, 0x00, 0x8a, 0x00, 0x82, 0x00, 0x7c, - 0x00, 0x76, 0x00, 0x70, 0x00, 0x6c, 0x01, 0x67, 0x01, 0x64, 0x02, 0x60, - 0x02, 0x5d, 0x04, 0x5b, 0x04, 0x58, 0x04, 0x55, 0x10, 0x7b, 0x10, 0x75, - 0x10, 0x6e, 0x10, 0x68, 0x10, 0x63, 0x10, 0x5e, 0x10, 0x5a, 0x10, 0x56, - 0x10, 0x52, 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x44, - 0x11, 0x43, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x13, 0x3d, 0x13, 0x3b, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x16, 0x00, 0x24, 0x00, 0x2c, 0x00, - 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, - 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0x9f, 0x00, 0x6f, - 0x00, 0x22, 0x0b, 0x09, 0x16, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, - 0x33, 0x00, 0x35, 0x00, 0x37, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, - 0x3b, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x39, 0x00, 0x16, 0x00, 0x28, 0x00, 0x2f, 0x00, 0x2e, 0x00, - 0x31, 0x00, 0x35, 0x00, 0x37, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3b, 0x00, - 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x00, 0xa5, 0x00, 0x7f, - 0x00, 0x3f, 0x00, 0x12, 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, - 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, - 0x3a, 0x00, 0x3a, 0x00, 0x3d, 0x38, 0x39, 0x34, 0x35, 0x32, 0x33, 0x30, - 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, 0x2c, 0x2b, 0x2b, 0x29, 0x2b, 0x28, - 0x29, 0x28, 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, - 0x27, 0x25, 0x26, 0x25, 0x25, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd6, 0x00, 0xca, 0x00, 0xbe, 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa0, - 0x00, 0x98, 0x00, 0x90, 0x00, 0x88, 0x00, 0x82, 0x00, 0x7d, 0x00, 0x77, - 0x00, 0x71, 0x00, 0x6d, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x02, 0x60, - 0x02, 0x5d, 0x04, 0x5b, 0x10, 0x7c, 0x10, 0x76, 0x10, 0x70, 0x10, 0x6a, - 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, 0x10, 0x59, 0x10, 0x55, 0x10, 0x52, - 0x10, 0x4f, 0x11, 0x4c, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, - 0x12, 0x42, 0x12, 0x41, 0x12, 0x3f, 0x13, 0x3e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, - 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, - 0x39, 0x00, 0x3a, 0x00, 0x00, 0xaf, 0x00, 0x93, 0x00, 0x58, 0x00, 0x21, - 0x08, 0x0e, 0x0e, 0x02, 0x18, 0x00, 0x20, 0x00, 0x27, 0x00, 0x2b, 0x00, - 0x2f, 0x00, 0x31, 0x00, 0x33, 0x00, 0x35, 0x00, 0x36, 0x00, 0x38, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x42, 0x00, - 0x28, 0x00, 0x09, 0x00, 0x16, 0x00, 0x1d, 0x00, 0x25, 0x00, 0x2b, 0x00, - 0x2f, 0x00, 0x32, 0x00, 0x34, 0x00, 0x36, 0x00, 0x37, 0x00, 0x38, 0x00, - 0x39, 0x00, 0x3a, 0x00, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, - 0x00, 0x1d, 0x00, 0x05, 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, - 0x29, 0x00, 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, - 0x3d, 0x38, 0x39, 0x35, 0x36, 0x33, 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, - 0x2e, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x28, 0x2b, 0x28, 0x28, 0x28, - 0x28, 0x27, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x27, 0x25, - 0x27, 0x25, 0x25, 0x25, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd6, 0x00, 0xcc, - 0x00, 0xc0, 0x00, 0xb7, 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x96, - 0x00, 0x8f, 0x00, 0x88, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x77, 0x00, 0x73, - 0x00, 0x6f, 0x00, 0x6b, 0x01, 0x67, 0x01, 0x64, 0x02, 0x62, 0x02, 0x5f, - 0x10, 0x7c, 0x10, 0x77, 0x10, 0x71, 0x10, 0x6c, 0x10, 0x67, 0x10, 0x63, - 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x58, 0x10, 0x55, 0x10, 0x51, 0x10, 0x4f, - 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x48, 0x11, 0x46, 0x11, 0x44, 0x11, 0x43, - 0x12, 0x42, 0x12, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, - 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, - 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x7a, 0x00, 0x4a, 0x00, 0x20, 0x06, 0x12, - 0x0c, 0x07, 0x10, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x24, 0x00, 0x28, 0x00, - 0x2c, 0x00, 0x2e, 0x00, 0x30, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x41, 0x00, 0x2f, 0x00, 0x16, 0x00, - 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x2a, 0x00, - 0x2d, 0x00, 0x30, 0x00, 0x32, 0x00, 0x34, 0x00, 0x35, 0x00, 0x37, 0x00, - 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, - 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, - 0x25, 0x00, 0x29, 0x00, 0x2b, 0x00, 0x2e, 0x00, 0x3d, 0x39, 0x3a, 0x36, - 0x36, 0x33, 0x34, 0x31, 0x32, 0x30, 0x30, 0x2d, 0x2f, 0x2d, 0x2d, 0x2b, - 0x2d, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x27, - 0x28, 0x26, 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x25, 0x27, 0x25, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd7, 0x00, 0xcc, 0x00, 0xc3, 0x00, 0xba, - 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa0, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8d, - 0x00, 0x87, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x74, 0x00, 0x70, - 0x00, 0x6c, 0x01, 0x69, 0x01, 0x66, 0x02, 0x63, 0x10, 0x7c, 0x10, 0x77, - 0x10, 0x72, 0x10, 0x6e, 0x10, 0x69, 0x10, 0x65, 0x10, 0x61, 0x10, 0x5d, - 0x10, 0x5a, 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, - 0x11, 0x4b, 0x11, 0x49, 0x11, 0x47, 0x11, 0x45, 0x11, 0x44, 0x12, 0x42, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, - 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xb9, 0x00, 0xad, - 0x00, 0x8f, 0x00, 0x68, 0x00, 0x42, 0x00, 0x20, 0x05, 0x14, 0x0a, 0x0b, - 0x0d, 0x04, 0x12, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x23, 0x00, 0x26, 0x00, - 0x29, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x00, 0x3d, 0x00, 0x2e, 0x00, 0x1d, 0x00, 0x0d, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, - 0x2c, 0x00, 0x2f, 0x00, 0x31, 0x00, 0x32, 0x00, 0x00, 0xba, 0x00, 0xb1, - 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, - 0x00, 0x08, 0x02, 0x00, 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, - 0x22, 0x00, 0x25, 0x00, 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x32, - 0x33, 0x30, 0x31, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2a, 0x2c, 0x2b, - 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, - 0x28, 0x27, 0x27, 0x27, 0x27, 0x27, 0x27, 0x26, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc5, 0x00, 0xbc, 0x00, 0xb4, 0x00, 0xac, - 0x00, 0xa5, 0x00, 0x9e, 0x00, 0x97, 0x00, 0x91, 0x00, 0x8c, 0x00, 0x87, - 0x00, 0x81, 0x00, 0x7d, 0x00, 0x78, 0x00, 0x75, 0x00, 0x71, 0x00, 0x6d, - 0x01, 0x6b, 0x01, 0x68, 0x10, 0x7c, 0x10, 0x78, 0x10, 0x73, 0x10, 0x6f, - 0x10, 0x6b, 0x10, 0x67, 0x10, 0x63, 0x10, 0x60, 0x10, 0x5c, 0x10, 0x59, - 0x10, 0x57, 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, - 0x11, 0x49, 0x11, 0x47, 0x11, 0x46, 0x11, 0x45, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, - 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xba, 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x7d, - 0x00, 0x5c, 0x00, 0x3c, 0x00, 0x20, 0x04, 0x16, 0x08, 0x0e, 0x0c, 0x07, - 0x0e, 0x02, 0x13, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x21, 0x00, 0x25, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3a, 0x00, - 0x31, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1d, 0x00, 0x22, 0x00, 0x26, 0x00, 0x29, 0x00, - 0x2b, 0x00, 0x2d, 0x00, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, - 0x00, 0x6f, 0x00, 0x56, 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, - 0x00, 0x04, 0x04, 0x00, 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, - 0x3d, 0x39, 0x3a, 0x36, 0x37, 0x34, 0x35, 0x33, 0x33, 0x30, 0x32, 0x30, - 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, 0x2c, 0x2b, 0x2b, 0x2a, - 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, 0x28, 0x27, - 0x27, 0x27, 0x27, 0x27, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xcf, - 0x00, 0xc6, 0x00, 0xbe, 0x00, 0xb6, 0x00, 0xaf, 0x00, 0xa8, 0x00, 0xa1, - 0x00, 0x9c, 0x00, 0x96, 0x00, 0x90, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, - 0x00, 0x7d, 0x00, 0x79, 0x00, 0x75, 0x00, 0x72, 0x00, 0x6f, 0x00, 0x6b, - 0x10, 0x7d, 0x10, 0x78, 0x10, 0x74, 0x10, 0x70, 0x10, 0x6c, 0x10, 0x68, - 0x10, 0x65, 0x10, 0x61, 0x10, 0x5f, 0x10, 0x5c, 0x10, 0x59, 0x10, 0x56, - 0x10, 0x54, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4b, 0x11, 0x4a, - 0x11, 0x48, 0x11, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, - 0x00, 0xbc, 0x00, 0xb5, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x6f, 0x00, 0x52, - 0x00, 0x37, 0x00, 0x20, 0x04, 0x17, 0x07, 0x10, 0x0a, 0x0a, 0x0d, 0x05, - 0x0f, 0x00, 0x14, 0x00, 0x19, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2b, 0x00, - 0x1f, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x15, 0x00, 0x1a, 0x00, 0x1f, 0x00, 0x22, 0x00, 0x26, 0x00, 0x28, 0x00, - 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, - 0x00, 0x53, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, - 0x00, 0x01, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x3d, 0x3a, 0x3a, 0x37, - 0x38, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x2f, 0x30, 0x2d, - 0x2e, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, - 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x29, 0x27, 0x27, 0x27, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc8, 0x00, 0xc0, - 0x00, 0xb9, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x99, - 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8a, 0x00, 0x86, 0x00, 0x81, 0x00, 0x7d, - 0x00, 0x79, 0x00, 0x76, 0x00, 0x73, 0x00, 0x70, 0x10, 0x7d, 0x10, 0x79, - 0x10, 0x75, 0x10, 0x71, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x66, 0x10, 0x63, - 0x10, 0x60, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x58, 0x10, 0x56, 0x10, 0x54, - 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4d, 0x11, 0x4c, 0x11, 0x4a, 0x11, 0x49, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, - 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbc, 0x00, 0xb8, - 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7e, 0x00, 0x64, 0x00, 0x4c, 0x00, 0x34, - 0x00, 0x20, 0x03, 0x18, 0x06, 0x11, 0x09, 0x0c, 0x0c, 0x07, 0x0e, 0x03, - 0x10, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x37, 0x00, 0x2f, 0x00, 0x25, 0x00, 0x1b, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x13, 0x00, - 0x18, 0x00, 0x1c, 0x00, 0x20, 0x00, 0x23, 0x00, 0x00, 0xbd, 0x00, 0xb9, - 0x00, 0xae, 0x00, 0x9e, 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, - 0x00, 0x3f, 0x00, 0x30, 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, - 0x00, 0x00, 0x07, 0x00, 0x3d, 0x3a, 0x3a, 0x38, 0x38, 0x36, 0x36, 0x33, - 0x34, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, - 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x28, 0x27, 0x28, 0x27, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xc9, 0x00, 0xc2, 0x00, 0xbb, 0x00, 0xb4, - 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x92, - 0x00, 0x8e, 0x00, 0x89, 0x00, 0x85, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, - 0x00, 0x76, 0x00, 0x73, 0x10, 0x7d, 0x10, 0x79, 0x10, 0x75, 0x10, 0x72, - 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, - 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, - 0x10, 0x4f, 0x11, 0x4e, 0x11, 0x4c, 0x11, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, - 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9d, - 0x00, 0x89, 0x00, 0x73, 0x00, 0x5c, 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, - 0x03, 0x19, 0x06, 0x13, 0x08, 0x0e, 0x0b, 0x09, 0x0d, 0x05, 0x0e, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, - 0x39, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x11, 0x00, 0x16, 0x00, - 0x1a, 0x00, 0x1d, 0x00, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, - 0x00, 0x94, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, - 0x00, 0x32, 0x00, 0x26, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, - 0x3d, 0x3a, 0x3b, 0x38, 0x39, 0x36, 0x36, 0x34, 0x35, 0x33, 0x33, 0x31, - 0x32, 0x30, 0x30, 0x2f, 0x30, 0x2d, 0x2e, 0x2d, 0x2d, 0x2c, 0x2d, 0x2a, - 0x2c, 0x2b, 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd1, - 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xaa, - 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, - 0x00, 0x89, 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7a, 0x00, 0x77, - 0x10, 0x7d, 0x10, 0x79, 0x10, 0x76, 0x10, 0x72, 0x10, 0x6f, 0x10, 0x6c, - 0x10, 0x69, 0x10, 0x66, 0x10, 0x63, 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, - 0x10, 0x59, 0x10, 0x57, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, - 0x11, 0x4e, 0x11, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, - 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa3, 0x00, 0x92, 0x00, 0x7e, - 0x00, 0x6a, 0x00, 0x56, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x03, 0x19, - 0x05, 0x14, 0x08, 0x0f, 0x0a, 0x0b, 0x0c, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, 0x3a, 0x00, 0x34, 0x00, - 0x2d, 0x00, 0x25, 0x00, 0x1d, 0x00, 0x15, 0x00, 0x0d, 0x00, 0x06, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x0b, 0x00, 0x10, 0x00, 0x14, 0x00, 0x18, 0x00, - 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, - 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, - 0x00, 0x28, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0e, 0x3d, 0x3b, 0x3b, 0x39, - 0x3a, 0x36, 0x36, 0x35, 0x36, 0x33, 0x33, 0x32, 0x33, 0x30, 0x30, 0x30, - 0x30, 0x2e, 0x2f, 0x2d, 0x2d, 0x2d, 0x2d, 0x2c, 0x2d, 0x2b, 0x2b, 0x2b, - 0x2b, 0x2b, 0x2b, 0x2a, 0x2b, 0x28, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x00, 0xd9, 0x00, 0xd2, 0x00, 0xcb, 0x00, 0xc4, - 0x00, 0xbe, 0x00, 0xb8, 0x00, 0xb2, 0x00, 0xad, 0x00, 0xa8, 0x00, 0xa2, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x94, 0x00, 0x90, 0x00, 0x8b, 0x00, 0x88, - 0x00, 0x84, 0x00, 0x81, 0x00, 0x7d, 0x00, 0x7b, 0x10, 0x7d, 0x10, 0x7a, - 0x10, 0x76, 0x10, 0x73, 0x10, 0x70, 0x10, 0x6d, 0x10, 0x6a, 0x10, 0x67, - 0x10, 0x65, 0x10, 0x62, 0x10, 0x5f, 0x10, 0x5d, 0x10, 0x5b, 0x10, 0x59, - 0x10, 0x56, 0x10, 0x55, 0x10, 0x53, 0x10, 0x51, 0x10, 0x4f, 0x11, 0x4e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbd, 0x00, 0xbb, - 0x00, 0xb3, 0x00, 0xa8, 0x00, 0x99, 0x00, 0x87, 0x00, 0x75, 0x00, 0x62, - 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x02, 0x1a, 0x05, 0x15, - 0x07, 0x10, 0x09, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x36, 0x00, 0x30, 0x00, 0x29, 0x00, - 0x22, 0x00, 0x1a, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x05, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x13, 0x00, 0x00, 0xbe, 0x00, 0xbc, - 0x00, 0xb6, 0x00, 0xac, 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, - 0x00, 0x66, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, - 0x00, 0x21, 0x00, 0x19, 0x3d, 0x3b, 0x3b, 0x39, 0x39, 0x36, 0x37, 0x35, - 0x36, 0x33, 0x33, 0x33, 0x33, 0x30, 0x31, 0x30, 0x30, 0x2f, 0x30, 0x2d, - 0x2e, 0x2d, 0x2d, 0x2d, 0x2d, 0x2b, 0x2d, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, - 0x2b, 0x29, 0x2b, 0x28, 0x29, 0x28, 0x28, 0x28, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, 0x21, 0x22, - 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc0, 0x00, 0xba, - 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa0, 0x00, 0x9b, - 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x88, 0x00, 0x83, - 0x00, 0x81, 0x00, 0x7d, 0x10, 0x7d, 0x10, 0x7a, 0x10, 0x77, 0x10, 0x74, - 0x10, 0x71, 0x10, 0x6e, 0x10, 0x6b, 0x10, 0x68, 0x10, 0x65, 0x10, 0x63, - 0x10, 0x61, 0x10, 0x5e, 0x10, 0x5c, 0x10, 0x5a, 0x10, 0x58, 0x10, 0x56, - 0x10, 0x55, 0x10, 0x52, 0x10, 0x51, 0x10, 0x4f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xab, - 0x00, 0x9e, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, - 0x00, 0x3c, 0x00, 0x2d, 0x00, 0x1f, 0x02, 0x1a, 0x04, 0x16, 0x06, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3b, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x26, 0x00, 0x1f, 0x00, - 0x18, 0x00, 0x11, 0x00, 0x0b, 0x00, 0x05, 0x00, 0x00, 0x00, 0x05, 0x00, - 0x09, 0x00, 0x0d, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, - 0x00, 0xa5, 0x00, 0x99, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, - 0x00, 0x56, 0x00, 0x4a, 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, - 0x27, 0x76, 0x1a, 0x79, 0x16, 0x7b, 0x15, 0x7c, 0x14, 0x7c, 0x13, 0x7d, - 0x13, 0x7d, 0x13, 0x7d, 0x12, 0x7d, 0x12, 0x7d, 0x12, 0x7e, 0x12, 0x7e, - 0x12, 0x7e, 0x12, 0x7e, 0x12, 0x7e, 0x11, 0x7e, 0x11, 0x7e, 0x11, 0x7e, - 0x11, 0x7f, 0x11, 0x7f, 0x13, 0x5f, 0x10, 0x6f, 0x10, 0x74, 0x10, 0x77, - 0x10, 0x78, 0x10, 0x7a, 0x10, 0x7a, 0x10, 0x7b, 0x10, 0x7b, 0x10, 0x7c, - 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7c, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, - 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x10, 0x7d, 0x00, 0x90, 0x02, 0x81, - 0x06, 0x80, 0x09, 0x7f, 0x0a, 0x7f, 0x0b, 0x7f, 0x0c, 0x7f, 0x0d, 0x7f, - 0x0d, 0x7f, 0x0d, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, 0x0e, 0x7f, - 0x0e, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, 0x0f, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x95, - 0x00, 0x86, 0x00, 0x77, 0x00, 0x67, 0x00, 0x57, 0x00, 0x48, 0x00, 0x3a, - 0x00, 0x2c, 0x00, 0x1f, 0x02, 0x1b, 0x04, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x38, 0x00, - 0x34, 0x00, 0x2f, 0x00, 0x29, 0x00, 0x22, 0x00, 0x1c, 0x00, 0x16, 0x00, - 0x10, 0x00, 0x0a, 0x00, 0x05, 0x00, 0x00, 0x00, 0x04, 0x00, 0x09, 0x00, - 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, - 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, - 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2d, 0x32, 0x5f, 0x25, 0x68, - 0x1f, 0x6d, 0x1c, 0x71, 0x19, 0x72, 0x18, 0x74, 0x17, 0x76, 0x16, 0x77, - 0x16, 0x77, 0x15, 0x78, 0x15, 0x79, 0x15, 0x79, 0x14, 0x7a, 0x14, 0x7a, - 0x13, 0x7a, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, 0x13, 0x7b, - 0x17, 0x46, 0x11, 0x58, 0x10, 0x63, 0x10, 0x69, 0x10, 0x6d, 0x10, 0x70, - 0x10, 0x72, 0x10, 0x73, 0x10, 0x75, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, - 0x10, 0x78, 0x10, 0x78, 0x10, 0x79, 0x10, 0x79, 0x10, 0x79, 0x10, 0x7a, - 0x10, 0x7a, 0x10, 0x7a, 0x00, 0xbd, 0x00, 0xa4, 0x00, 0x97, 0x02, 0x91, - 0x04, 0x8d, 0x05, 0x8a, 0x06, 0x89, 0x07, 0x88, 0x08, 0x86, 0x09, 0x86, - 0x09, 0x85, 0x0a, 0x85, 0x0a, 0x84, 0x0b, 0x84, 0x0b, 0x83, 0x0b, 0x83, - 0x0c, 0x83, 0x0c, 0x83, 0x0c, 0x82, 0x0c, 0x82, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbc, - 0x00, 0xb8, 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9a, 0x00, 0x8d, 0x00, 0x7f, - 0x00, 0x70, 0x00, 0x61, 0x00, 0x53, 0x00, 0x45, 0x00, 0x38, 0x00, 0x2b, - 0x00, 0x1f, 0x02, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3e, 0x00, 0x3c, 0x00, 0x39, 0x00, 0x35, 0x00, 0x31, 0x00, - 0x2b, 0x00, 0x26, 0x00, 0x20, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0f, 0x00, - 0x09, 0x00, 0x04, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0xbe, 0x00, 0xbd, - 0x00, 0xb9, 0x00, 0xb3, 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, - 0x00, 0x80, 0x00, 0x74, 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, - 0x00, 0x3f, 0x00, 0x36, 0x36, 0x56, 0x2a, 0x5f, 0x24, 0x65, 0x20, 0x68, - 0x1e, 0x6c, 0x1c, 0x6e, 0x1b, 0x70, 0x19, 0x71, 0x18, 0x72, 0x18, 0x73, - 0x18, 0x75, 0x17, 0x75, 0x16, 0x76, 0x15, 0x76, 0x15, 0x76, 0x15, 0x77, - 0x15, 0x77, 0x15, 0x77, 0x15, 0x78, 0x15, 0x79, 0x1a, 0x39, 0x13, 0x4a, - 0x11, 0x55, 0x10, 0x5e, 0x10, 0x63, 0x10, 0x67, 0x10, 0x6a, 0x10, 0x6c, - 0x10, 0x6e, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, - 0x10, 0x75, 0x10, 0x75, 0x10, 0x76, 0x10, 0x76, 0x10, 0x77, 0x10, 0x77, - 0x00, 0xcc, 0x00, 0xb5, 0x00, 0xa8, 0x00, 0x9e, 0x01, 0x99, 0x02, 0x94, - 0x03, 0x92, 0x04, 0x8f, 0x05, 0x8d, 0x06, 0x8c, 0x06, 0x8b, 0x07, 0x8a, - 0x07, 0x89, 0x08, 0x88, 0x08, 0x88, 0x09, 0x87, 0x09, 0x87, 0x09, 0x86, - 0x0a, 0x86, 0x0a, 0x86, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb2, - 0x00, 0xa9, 0x00, 0x9e, 0x00, 0x92, 0x00, 0x85, 0x00, 0x78, 0x00, 0x6a, - 0x00, 0x5c, 0x00, 0x4f, 0x00, 0x42, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, - 0x3d, 0x00, 0x3a, 0x00, 0x37, 0x00, 0x32, 0x00, 0x2d, 0x00, 0x28, 0x00, - 0x23, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x09, 0x00, - 0x04, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, - 0x00, 0xad, 0x00, 0xa5, 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, - 0x00, 0x70, 0x00, 0x65, 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, - 0x38, 0x52, 0x2e, 0x59, 0x28, 0x5f, 0x24, 0x63, 0x22, 0x67, 0x1f, 0x69, - 0x1e, 0x6b, 0x1c, 0x6d, 0x1b, 0x6e, 0x1a, 0x70, 0x1a, 0x71, 0x19, 0x72, - 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x17, 0x75, 0x17, 0x76, 0x16, 0x76, - 0x15, 0x76, 0x15, 0x76, 0x1b, 0x33, 0x15, 0x41, 0x12, 0x4c, 0x11, 0x54, - 0x10, 0x5b, 0x10, 0x5f, 0x10, 0x63, 0x10, 0x66, 0x10, 0x68, 0x10, 0x6a, - 0x10, 0x6c, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, 0x10, 0x71, 0x10, 0x72, - 0x10, 0x72, 0x10, 0x73, 0x10, 0x74, 0x10, 0x74, 0x00, 0xd1, 0x00, 0xc0, - 0x00, 0xb3, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9d, 0x01, 0x99, 0x02, 0x96, - 0x02, 0x94, 0x03, 0x92, 0x04, 0x90, 0x05, 0x8f, 0x05, 0x8d, 0x06, 0x8d, - 0x06, 0x8c, 0x07, 0x8b, 0x07, 0x8a, 0x07, 0x8a, 0x07, 0x89, 0x08, 0x89, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x47, 0x0f, 0x0f, 0x26, 0x00, 0x33, 0x00, 0x38, 0x00, 0x3a, 0x00, - 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x00, - 0x3e, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x47, 0x00, 0x7f, 0x00, 0xa5, 0x00, 0xb2, - 0x00, 0xb7, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, - 0x00, 0xbd, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, 0x00, 0xbe, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x39, 0x4f, 0x31, 0x55, - 0x2b, 0x5b, 0x27, 0x5f, 0x24, 0x63, 0x22, 0x65, 0x20, 0x68, 0x1f, 0x69, - 0x1d, 0x6b, 0x1d, 0x6d, 0x1b, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, 0x1a, 0x71, - 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x72, 0x18, 0x73, 0x18, 0x74, - 0x1c, 0x2f, 0x16, 0x3b, 0x13, 0x44, 0x11, 0x4d, 0x11, 0x53, 0x10, 0x59, - 0x10, 0x5d, 0x10, 0x60, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x69, - 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, 0x10, 0x70, - 0x10, 0x71, 0x10, 0x71, 0x00, 0xd5, 0x00, 0xc6, 0x00, 0xbb, 0x00, 0xb2, - 0x00, 0xaa, 0x00, 0xa4, 0x00, 0xa0, 0x01, 0x9c, 0x01, 0x9a, 0x02, 0x97, - 0x02, 0x95, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, 0x05, 0x8e, - 0x06, 0x8e, 0x06, 0x8d, 0x06, 0x8c, 0x06, 0x8c, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x3f, - 0x05, 0x05, 0x1c, 0x00, 0x2a, 0x00, 0x31, 0x00, 0x35, 0x00, 0x38, 0x00, - 0x39, 0x00, 0x3b, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3d, 0x00, 0x3d, 0x00, - 0x3d, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0x0f, 0x00, 0x3f, 0x00, 0x7f, 0x00, 0x9c, 0x00, 0xaa, 0x00, 0xb1, - 0x00, 0xb5, 0x00, 0xb7, 0x00, 0xb9, 0x00, 0xba, 0x00, 0xbb, 0x00, 0xbc, - 0x00, 0xbc, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0xbd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x4c, 0x33, 0x53, 0x2e, 0x58, 0x29, 0x5c, - 0x27, 0x5f, 0x24, 0x62, 0x22, 0x64, 0x20, 0x66, 0x20, 0x68, 0x1e, 0x69, - 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1a, 0x6d, 0x1a, 0x6e, 0x1a, 0x6f, - 0x1a, 0x71, 0x19, 0x72, 0x18, 0x72, 0x18, 0x72, 0x1d, 0x2d, 0x17, 0x37, - 0x14, 0x40, 0x12, 0x47, 0x11, 0x4e, 0x11, 0x53, 0x10, 0x57, 0x10, 0x5b, - 0x10, 0x5e, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x67, 0x10, 0x68, - 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x10, 0x6d, 0x10, 0x6e, 0x10, 0x6f, - 0x00, 0xd6, 0x00, 0xcb, 0x00, 0xc0, 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xab, - 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9f, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, - 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, 0x05, 0x90, - 0x05, 0x8f, 0x05, 0x8f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xa5, 0x00, 0x7f, 0x00, 0x3f, 0x00, 0x12, - 0x09, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2a, 0x00, 0x2f, 0x00, 0x32, 0x00, - 0x34, 0x00, 0x36, 0x00, 0x38, 0x00, 0x39, 0x00, 0x3a, 0x00, 0x3a, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x00, 0x05, 0x05, - 0x00, 0x3f, 0x00, 0x6d, 0x00, 0x88, 0x00, 0x99, 0x00, 0xa3, 0x00, 0xaa, - 0x00, 0xae, 0x00, 0xb1, 0x00, 0xb4, 0x00, 0xb6, 0x00, 0xb7, 0x00, 0xb8, - 0x00, 0xb9, 0x00, 0xba, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x4b, 0x34, 0x51, 0x2f, 0x55, 0x2c, 0x59, 0x29, 0x5c, 0x26, 0x60, - 0x24, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, - 0x1d, 0x6a, 0x1d, 0x6c, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, - 0x1a, 0x6f, 0x1a, 0x71, 0x1e, 0x2b, 0x18, 0x34, 0x15, 0x3b, 0x13, 0x43, - 0x12, 0x49, 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x5a, 0x10, 0x5d, - 0x10, 0x5f, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x68, - 0x10, 0x69, 0x10, 0x6a, 0x10, 0x6b, 0x10, 0x6c, 0x00, 0xd7, 0x00, 0xcd, - 0x00, 0xc5, 0x00, 0xbd, 0x00, 0xb6, 0x00, 0xb0, 0x00, 0xab, 0x00, 0xa7, - 0x00, 0xa4, 0x00, 0xa1, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x98, - 0x02, 0x97, 0x02, 0x96, 0x03, 0x94, 0x03, 0x93, 0x03, 0x92, 0x04, 0x91, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb2, 0x00, 0x9c, 0x00, 0x6d, 0x00, 0x3f, 0x00, 0x1d, 0x00, 0x05, - 0x0b, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x25, 0x00, 0x29, 0x00, 0x2d, 0x00, - 0x30, 0x00, 0x32, 0x00, 0x33, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x1c, 0x00, 0x00, 0x12, 0x00, 0x3f, - 0x00, 0x62, 0x00, 0x7a, 0x00, 0x8a, 0x00, 0x96, 0x00, 0x9e, 0x00, 0xa4, - 0x00, 0xa9, 0x00, 0xac, 0x00, 0xaf, 0x00, 0xb1, 0x00, 0xb3, 0x00, 0xb4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x49, 0x36, 0x4f, - 0x31, 0x54, 0x2d, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x26, 0x60, 0x24, 0x61, - 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, - 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1a, 0x6d, 0x1a, 0x6d, - 0x1e, 0x2a, 0x19, 0x32, 0x16, 0x39, 0x13, 0x3f, 0x12, 0x45, 0x11, 0x4a, - 0x11, 0x4e, 0x11, 0x52, 0x10, 0x56, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, - 0x10, 0x60, 0x10, 0x61, 0x10, 0x63, 0x10, 0x65, 0x10, 0x66, 0x10, 0x67, - 0x10, 0x68, 0x10, 0x69, 0x00, 0xd8, 0x00, 0xcf, 0x00, 0xc7, 0x00, 0xc0, - 0x00, 0xba, 0x00, 0xb5, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa8, 0x00, 0xa5, - 0x00, 0xa2, 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9c, 0x02, 0x9a, 0x02, 0x99, - 0x02, 0x97, 0x02, 0x96, 0x03, 0x95, 0x03, 0x94, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb7, 0x00, 0xaa, - 0x00, 0x88, 0x00, 0x62, 0x00, 0x3f, 0x00, 0x24, 0x00, 0x0f, 0x00, 0x00, - 0x0b, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x21, 0x00, 0x25, 0x00, 0x29, 0x00, - 0x2b, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x38, 0x00, 0x2a, 0x00, 0x09, 0x00, 0x00, 0x1d, 0x00, 0x3f, 0x00, 0x5b, - 0x00, 0x6f, 0x00, 0x7f, 0x00, 0x8b, 0x00, 0x94, 0x00, 0x9b, 0x00, 0xa0, - 0x00, 0xa5, 0x00, 0xa8, 0x00, 0xab, 0x00, 0xad, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x48, 0x37, 0x4d, 0x32, 0x51, 0x2f, 0x55, - 0x2c, 0x58, 0x29, 0x5b, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x63, - 0x22, 0x65, 0x20, 0x65, 0x20, 0x66, 0x1f, 0x68, 0x1e, 0x69, 0x1d, 0x69, - 0x1d, 0x69, 0x1d, 0x6b, 0x1c, 0x6d, 0x1b, 0x6d, 0x1e, 0x29, 0x1a, 0x30, - 0x17, 0x36, 0x14, 0x3c, 0x13, 0x41, 0x12, 0x46, 0x11, 0x4a, 0x11, 0x4f, - 0x11, 0x52, 0x10, 0x55, 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5f, - 0x10, 0x60, 0x10, 0x62, 0x10, 0x63, 0x10, 0x65, 0x10, 0x65, 0x10, 0x67, - 0x00, 0xd9, 0x00, 0xd1, 0x00, 0xca, 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xb9, - 0x00, 0xb4, 0x00, 0xb0, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa3, - 0x00, 0xa1, 0x01, 0x9f, 0x01, 0x9d, 0x01, 0x9b, 0x02, 0x9a, 0x02, 0x99, - 0x02, 0x98, 0x02, 0x97, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xba, 0x00, 0xb1, 0x00, 0x99, 0x00, 0x7a, - 0x00, 0x5b, 0x00, 0x3f, 0x00, 0x29, 0x00, 0x16, 0x00, 0x08, 0x02, 0x00, - 0x0c, 0x00, 0x13, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x22, 0x00, 0x25, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x31, 0x00, - 0x19, 0x00, 0x00, 0x05, 0x00, 0x24, 0x00, 0x3f, 0x00, 0x56, 0x00, 0x68, - 0x00, 0x77, 0x00, 0x82, 0x00, 0x8b, 0x00, 0x93, 0x00, 0x99, 0x00, 0x9d, - 0x00, 0xa1, 0x00, 0xa5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x48, 0x37, 0x4c, 0x33, 0x50, 0x30, 0x54, 0x2d, 0x57, 0x2a, 0x59, - 0x29, 0x5c, 0x27, 0x5d, 0x26, 0x60, 0x24, 0x60, 0x23, 0x62, 0x22, 0x65, - 0x20, 0x65, 0x20, 0x65, 0x20, 0x67, 0x1f, 0x69, 0x1d, 0x69, 0x1d, 0x69, - 0x1d, 0x69, 0x1d, 0x6a, 0x1f, 0x28, 0x1a, 0x2e, 0x17, 0x34, 0x15, 0x39, - 0x13, 0x3f, 0x13, 0x43, 0x12, 0x48, 0x11, 0x4b, 0x11, 0x4f, 0x11, 0x52, - 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5f, - 0x10, 0x61, 0x10, 0x62, 0x10, 0x63, 0x10, 0x64, 0x00, 0xd9, 0x00, 0xd2, - 0x00, 0xcc, 0x00, 0xc6, 0x00, 0xc1, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xb3, - 0x00, 0xaf, 0x00, 0xac, 0x00, 0xa9, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, - 0x00, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x02, 0x9a, 0x02, 0x99, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbb, 0x00, 0xb5, 0x00, 0xa3, 0x00, 0x8a, 0x00, 0x6f, 0x00, 0x56, - 0x00, 0x3f, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0f, 0x00, 0x04, 0x04, 0x00, - 0x0c, 0x00, 0x12, 0x00, 0x17, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x23, 0x00, 0x0b, 0x00, - 0x00, 0x0f, 0x00, 0x29, 0x00, 0x3f, 0x00, 0x53, 0x00, 0x63, 0x00, 0x70, - 0x00, 0x7b, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, - 0x34, 0x4f, 0x31, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x5a, 0x28, 0x5c, - 0x27, 0x5d, 0x26, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, - 0x20, 0x65, 0x20, 0x65, 0x20, 0x68, 0x1e, 0x69, 0x1d, 0x69, 0x1d, 0x69, - 0x1f, 0x27, 0x1b, 0x2d, 0x18, 0x33, 0x16, 0x38, 0x14, 0x3c, 0x13, 0x41, - 0x12, 0x44, 0x11, 0x48, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, - 0x10, 0x57, 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x10, 0x5f, - 0x10, 0x61, 0x10, 0x62, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcd, 0x00, 0xc8, - 0x00, 0xc3, 0x00, 0xbe, 0x00, 0xba, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, - 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa3, 0x00, 0xa1, - 0x01, 0xa0, 0x01, 0x9e, 0x01, 0x9d, 0x02, 0x9c, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xb7, - 0x00, 0xaa, 0x00, 0x96, 0x00, 0x7f, 0x00, 0x68, 0x00, 0x53, 0x00, 0x3f, - 0x00, 0x2e, 0x00, 0x20, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, - 0x0c, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x00, 0x38, 0x00, 0x2a, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x16, - 0x00, 0x2c, 0x00, 0x3f, 0x00, 0x50, 0x00, 0x5e, 0x00, 0x6b, 0x00, 0x75, - 0x00, 0x7e, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x51, - 0x2f, 0x53, 0x2d, 0x57, 0x2b, 0x58, 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5d, - 0x25, 0x60, 0x23, 0x60, 0x23, 0x61, 0x23, 0x64, 0x21, 0x65, 0x20, 0x65, - 0x20, 0x65, 0x20, 0x66, 0x1f, 0x69, 0x1e, 0x69, 0x1f, 0x27, 0x1b, 0x2c, - 0x18, 0x31, 0x16, 0x36, 0x14, 0x3a, 0x13, 0x3e, 0x13, 0x42, 0x12, 0x45, - 0x11, 0x49, 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, - 0x10, 0x58, 0x10, 0x5a, 0x10, 0x5c, 0x10, 0x5d, 0x10, 0x5e, 0x10, 0x60, - 0x00, 0xd9, 0x00, 0xd4, 0x00, 0xce, 0x00, 0xca, 0x00, 0xc5, 0x00, 0xc1, - 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, - 0x00, 0xaa, 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa2, 0x00, 0xa0, - 0x01, 0x9f, 0x01, 0x9e, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xae, 0x00, 0x9e, - 0x00, 0x8b, 0x00, 0x77, 0x00, 0x63, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x30, - 0x00, 0x23, 0x00, 0x18, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x00, 0x39, 0x00, - 0x2f, 0x00, 0x1f, 0x00, 0x0b, 0x00, 0x00, 0x08, 0x00, 0x1c, 0x00, 0x2e, - 0x00, 0x3f, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x66, 0x00, 0x70, 0x00, 0x78, - 0x00, 0x80, 0x00, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x46, 0x39, 0x4a, 0x35, 0x4d, 0x32, 0x50, 0x30, 0x53, 0x2e, 0x55, - 0x2c, 0x57, 0x2a, 0x58, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5d, 0x25, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, - 0x20, 0x65, 0x20, 0x67, 0x1f, 0x27, 0x1c, 0x2c, 0x19, 0x30, 0x17, 0x35, - 0x15, 0x39, 0x14, 0x3d, 0x13, 0x40, 0x12, 0x44, 0x12, 0x47, 0x11, 0x49, - 0x11, 0x4c, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x56, 0x10, 0x58, - 0x10, 0x59, 0x10, 0x5b, 0x10, 0x5c, 0x10, 0x5e, 0x00, 0xda, 0x00, 0xd4, - 0x00, 0xd0, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc2, 0x00, 0xbe, 0x00, 0xbb, - 0x00, 0xb8, 0x00, 0xb5, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, - 0x00, 0xa8, 0x00, 0xa6, 0x00, 0xa4, 0x00, 0xa3, 0x00, 0xa1, 0x01, 0xa0, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb1, 0x00, 0xa4, 0x00, 0x94, 0x00, 0x82, - 0x00, 0x70, 0x00, 0x5e, 0x00, 0x4e, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x26, - 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0b, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3b, 0x00, 0x32, 0x00, 0x25, 0x00, - 0x14, 0x00, 0x02, 0x00, 0x00, 0x0f, 0x00, 0x20, 0x00, 0x30, 0x00, 0x3f, - 0x00, 0x4d, 0x00, 0x58, 0x00, 0x63, 0x00, 0x6c, 0x00, 0x74, 0x00, 0x7b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x46, 0x39, 0x49, - 0x36, 0x4c, 0x33, 0x4f, 0x31, 0x52, 0x2e, 0x53, 0x2d, 0x57, 0x2b, 0x57, - 0x2a, 0x59, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x62, 0x22, 0x65, 0x20, 0x65, 0x20, 0x65, 0x20, 0x65, - 0x1f, 0x26, 0x1c, 0x2b, 0x19, 0x2f, 0x17, 0x33, 0x16, 0x37, 0x14, 0x3b, - 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, 0x11, 0x47, 0x11, 0x4a, 0x11, 0x4d, - 0x11, 0x4f, 0x11, 0x51, 0x10, 0x54, 0x10, 0x55, 0x10, 0x57, 0x10, 0x59, - 0x10, 0x5a, 0x10, 0x5b, 0x00, 0xda, 0x00, 0xd5, 0x00, 0xd1, 0x00, 0xcc, - 0x00, 0xc8, 0x00, 0xc4, 0x00, 0xc1, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb7, - 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xaa, 0x00, 0xa9, - 0x00, 0xa7, 0x00, 0xa5, 0x00, 0xa4, 0x00, 0xa2, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbd, 0x00, 0xbb, - 0x00, 0xb4, 0x00, 0xa9, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7b, 0x00, 0x6b, - 0x00, 0x5b, 0x00, 0x4d, 0x00, 0x3f, 0x00, 0x33, 0x00, 0x28, 0x00, 0x1f, - 0x00, 0x16, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x3c, 0x00, 0x34, 0x00, 0x29, 0x00, 0x1b, 0x00, 0x0c, 0x00, - 0x00, 0x04, 0x00, 0x14, 0x00, 0x23, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4b, - 0x00, 0x56, 0x00, 0x60, 0x00, 0x68, 0x00, 0x70, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, 0x36, 0x4b, 0x34, 0x4f, - 0x31, 0x50, 0x30, 0x53, 0x2e, 0x55, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x5a, - 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x61, 0x22, 0x64, 0x21, 0x65, 0x20, 0x65, 0x20, 0x26, 0x1c, 0x2a, - 0x1a, 0x2e, 0x18, 0x32, 0x16, 0x36, 0x14, 0x39, 0x13, 0x3d, 0x13, 0x40, - 0x12, 0x43, 0x12, 0x45, 0x11, 0x48, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, - 0x11, 0x51, 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x10, 0x5a, - 0x00, 0xda, 0x00, 0xd6, 0x00, 0xd1, 0x00, 0xcd, 0x00, 0xc9, 0x00, 0xc6, - 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, 0x00, 0xb9, 0x00, 0xb6, 0x00, 0xb3, - 0x00, 0xb1, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa7, - 0x00, 0xa6, 0x00, 0xa4, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb6, 0x00, 0xac, - 0x00, 0xa0, 0x00, 0x93, 0x00, 0x84, 0x00, 0x75, 0x00, 0x66, 0x00, 0x58, - 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3c, 0x00, - 0x36, 0x00, 0x2d, 0x00, 0x21, 0x00, 0x13, 0x00, 0x04, 0x00, 0x00, 0x0a, - 0x00, 0x18, 0x00, 0x26, 0x00, 0x33, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x54, - 0x00, 0x5d, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x45, 0x3a, 0x48, 0x37, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x53, - 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x58, 0x29, 0x5b, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x61, - 0x23, 0x64, 0x21, 0x65, 0x20, 0x26, 0x1d, 0x2a, 0x1a, 0x2d, 0x18, 0x31, - 0x17, 0x35, 0x15, 0x38, 0x14, 0x3b, 0x13, 0x3e, 0x13, 0x41, 0x12, 0x44, - 0x12, 0x46, 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, - 0x10, 0x53, 0x10, 0x55, 0x10, 0x56, 0x10, 0x58, 0x00, 0xda, 0x00, 0xd6, - 0x00, 0xd2, 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc7, 0x00, 0xc4, 0x00, 0xc1, - 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb3, 0x00, 0xb1, - 0x00, 0xaf, 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x00, 0xa6, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xb7, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, - 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x70, 0x00, 0x63, 0x00, 0x56, 0x00, 0x4a, - 0x00, 0x3f, 0x00, 0x35, 0x00, 0x2c, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x3d, 0x00, 0x38, 0x00, 0x30, 0x00, - 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x01, 0x00, 0x0e, 0x00, 0x1c, - 0x00, 0x28, 0x00, 0x34, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x53, 0x00, 0x5b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, - 0x38, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x54, - 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5e, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x63, - 0x20, 0x25, 0x1d, 0x29, 0x1a, 0x2d, 0x18, 0x30, 0x17, 0x33, 0x16, 0x37, - 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, 0x13, 0x42, 0x12, 0x44, 0x11, 0x47, - 0x11, 0x49, 0x11, 0x4b, 0x11, 0x4d, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x53, - 0x10, 0x55, 0x10, 0x56, 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xcf, - 0x00, 0xcc, 0x00, 0xc8, 0x00, 0xc5, 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbc, - 0x00, 0xba, 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb1, 0x00, 0xaf, - 0x00, 0xad, 0x00, 0xab, 0x00, 0xa9, 0x00, 0xa8, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, - 0x00, 0xb8, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9d, 0x00, 0x92, 0x00, 0x85, - 0x00, 0x78, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3f, - 0x00, 0x36, 0x00, 0x2d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x00, 0x3d, 0x00, 0x39, 0x00, 0x32, 0x00, 0x29, 0x00, 0x1e, 0x00, - 0x12, 0x00, 0x06, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, - 0x00, 0x35, 0x00, 0x3f, 0x00, 0x49, 0x00, 0x51, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4c, - 0x33, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, - 0x2a, 0x57, 0x2a, 0x5a, 0x28, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, - 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x29, - 0x1b, 0x2c, 0x18, 0x2f, 0x17, 0x33, 0x16, 0x36, 0x14, 0x39, 0x14, 0x3b, - 0x13, 0x3e, 0x13, 0x41, 0x12, 0x43, 0x12, 0x45, 0x11, 0x47, 0x11, 0x4a, - 0x11, 0x4c, 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x10, 0x54, - 0x00, 0xdb, 0x00, 0xd7, 0x00, 0xd3, 0x00, 0xd0, 0x00, 0xcc, 0x00, 0xc9, - 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, 0x00, 0xbe, 0x00, 0xbb, 0x00, 0xb9, - 0x00, 0xb7, 0x00, 0xb5, 0x00, 0xb3, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, - 0x00, 0xab, 0x00, 0xaa, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xb9, 0x00, 0xb3, - 0x00, 0xab, 0x00, 0xa1, 0x00, 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x74, - 0x00, 0x68, 0x00, 0x5d, 0x00, 0x53, 0x00, 0x49, 0x00, 0x3f, 0x00, 0x36, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3d, 0x00, - 0x3a, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, 0x00, 0x21, 0x00, 0x2c, 0x00, 0x36, - 0x00, 0x3f, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x4a, 0x35, 0x4b, 0x34, 0x4f, 0x31, 0x4f, - 0x31, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x58, - 0x2a, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5f, 0x24, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x20, 0x25, 0x1d, 0x28, 0x1b, 0x2b, 0x19, 0x2f, - 0x17, 0x32, 0x16, 0x35, 0x15, 0x38, 0x14, 0x3a, 0x13, 0x3d, 0x13, 0x3f, - 0x13, 0x42, 0x12, 0x44, 0x12, 0x46, 0x11, 0x48, 0x11, 0x4a, 0x11, 0x4c, - 0x11, 0x4e, 0x11, 0x4f, 0x11, 0x51, 0x10, 0x52, 0x00, 0xdb, 0x00, 0xd7, - 0x00, 0xd4, 0x00, 0xd0, 0x00, 0xcd, 0x00, 0xca, 0x00, 0xc7, 0x00, 0xc5, - 0x00, 0xc2, 0x00, 0xbf, 0x00, 0xbd, 0x00, 0xbb, 0x00, 0xb8, 0x00, 0xb6, - 0x00, 0xb4, 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x00, 0xac, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xbe, 0x00, 0xbd, 0x00, 0xba, 0x00, 0xb4, 0x00, 0xad, 0x00, 0xa5, - 0x00, 0x9b, 0x00, 0x91, 0x00, 0x86, 0x00, 0x7b, 0x00, 0x70, 0x00, 0x65, - 0x00, 0x5b, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x3e, 0x00, 0x3a, 0x00, 0x35, 0x00, - 0x2e, 0x00, 0x25, 0x00, 0x1c, 0x00, 0x11, 0x00, 0x07, 0x00, 0x00, 0x03, - 0x00, 0x0e, 0x00, 0x19, 0x00, 0x23, 0x00, 0x2d, 0x00, 0x36, 0x00, 0x3f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, - 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x51, 0x2f, 0x53, - 0x2e, 0x53, 0x2e, 0x56, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x29, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x26, 0x5f, 0x24, 0x60, 0x23, 0x60, - 0x20, 0x25, 0x1d, 0x28, 0x1c, 0x2b, 0x1a, 0x2e, 0x18, 0x31, 0x17, 0x34, - 0x16, 0x37, 0x14, 0x39, 0x13, 0x3b, 0x13, 0x3e, 0x13, 0x40, 0x12, 0x42, - 0x12, 0x45, 0x11, 0x46, 0x11, 0x49, 0x11, 0x4a, 0x11, 0x4c, 0x11, 0x4e, - 0x11, 0x4f, 0x11, 0x51, 0x00, 0xdb, 0x00, 0xd8, 0x00, 0xd4, 0x00, 0xd1, - 0x00, 0xce, 0x00, 0xcb, 0x00, 0xc8, 0x00, 0xc6, 0x00, 0xc3, 0x00, 0xc1, - 0x00, 0xbe, 0x00, 0xbc, 0x00, 0xba, 0x00, 0xb8, 0x00, 0xb6, 0x00, 0xb4, - 0x00, 0xb2, 0x00, 0xb0, 0x00, 0xaf, 0x00, 0xad, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, 0x10, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x56, 0x7f, 0x47, 0x63, - 0x44, 0x57, 0x43, 0x52, 0x43, 0x4e, 0x42, 0x4c, 0x42, 0x4a, 0x42, 0x49, - 0x42, 0x48, 0x42, 0x47, 0x42, 0x46, 0x42, 0x45, 0x42, 0x45, 0x42, 0x45, - 0x42, 0x44, 0x42, 0x44, 0x42, 0x44, 0x42, 0x43, 0x42, 0x43, 0x42, 0x43, - 0x4c, 0x7f, 0x27, 0x4d, 0x2f, 0x46, 0x33, 0x44, 0x36, 0x43, 0x37, 0x43, - 0x39, 0x42, 0x3a, 0x41, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, 0x3b, 0x41, - 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x8e, 0x7f, 0x47, 0x4d, 0x44, 0x46, 0x43, 0x44, - 0x43, 0x43, 0x42, 0x43, 0x42, 0x42, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, - 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, - 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x42, 0x41, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x85, 0x1b, 0x60, 0x27, 0x54, 0x2d, 0x4e, 0x30, - 0x4b, 0x33, 0x49, 0x34, 0x47, 0x35, 0x46, 0x37, 0x45, 0x38, 0x45, 0x38, - 0x44, 0x39, 0x43, 0x39, 0x43, 0x3a, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, - 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x42, 0x3b, 0x09, 0x43, 0x16, 0x3f, - 0x1f, 0x3e, 0x25, 0x3e, 0x29, 0x3e, 0x2c, 0x3e, 0x2f, 0x3e, 0x30, 0x3e, - 0x32, 0x3e, 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x36, 0x3e, 0x36, 0x3e, - 0x37, 0x3e, 0x37, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x38, 0x3e, 0x39, 0x3e, - 0x59, 0x31, 0x4f, 0x38, 0x4b, 0x3a, 0x48, 0x3b, 0x47, 0x3c, 0x46, 0x3c, - 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x4c, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x96, 0x05, 0x74, 0x0f, 0x63, 0x17, 0x5a, 0x1d, 0x54, 0x21, 0x51, 0x24, - 0x4e, 0x27, 0x4c, 0x29, 0x4a, 0x2b, 0x49, 0x2d, 0x49, 0x2e, 0x48, 0x2f, - 0x48, 0x31, 0x47, 0x31, 0x46, 0x32, 0x46, 0x33, 0x44, 0x33, 0x44, 0x34, - 0x43, 0x34, 0x43, 0x35, 0x09, 0x43, 0x11, 0x3e, 0x18, 0x3c, 0x1d, 0x3c, - 0x22, 0x3c, 0x25, 0x3c, 0x27, 0x3c, 0x2a, 0x3c, 0x2c, 0x3d, 0x2d, 0x3d, - 0x2f, 0x3d, 0x30, 0x3e, 0x31, 0x3e, 0x31, 0x3e, 0x32, 0x3e, 0x33, 0x3e, - 0x33, 0x3e, 0x34, 0x3e, 0x35, 0x3e, 0x35, 0x3e, 0x5b, 0x2a, 0x53, 0x31, - 0x4f, 0x34, 0x4c, 0x36, 0x4b, 0x38, 0x4a, 0x39, 0x48, 0x39, 0x47, 0x3a, - 0x46, 0x3b, 0x46, 0x3b, 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, - 0x44, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, - 0x00, 0x22, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x9e, 0x01, 0x81, 0x06, - 0x6f, 0x0d, 0x64, 0x12, 0x5d, 0x16, 0x58, 0x1a, 0x54, 0x1d, 0x53, 0x20, - 0x50, 0x22, 0x4e, 0x24, 0x4c, 0x26, 0x4b, 0x27, 0x4a, 0x29, 0x49, 0x2a, - 0x49, 0x2b, 0x49, 0x2c, 0x48, 0x2d, 0x48, 0x2e, 0x48, 0x2f, 0x47, 0x2f, - 0x09, 0x44, 0x0f, 0x3e, 0x14, 0x3c, 0x19, 0x3c, 0x1d, 0x3b, 0x20, 0x3b, - 0x23, 0x3b, 0x25, 0x3c, 0x27, 0x3c, 0x28, 0x3c, 0x2a, 0x3b, 0x2c, 0x3b, - 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, 0x3c, 0x2f, 0x3c, 0x30, 0x3d, 0x31, 0x3e, - 0x32, 0x3e, 0x32, 0x3e, 0x5c, 0x28, 0x56, 0x2d, 0x52, 0x31, 0x4f, 0x33, - 0x4e, 0x35, 0x4b, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, 0x47, 0x39, - 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x3a, 0x46, 0x3a, 0x46, 0x3b, - 0x46, 0x3c, 0x46, 0x3c, 0x46, 0x3d, 0x45, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, 0x00, - 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xa4, 0x00, 0x8a, 0x03, 0x78, 0x07, 0x6d, 0x0c, - 0x65, 0x10, 0x60, 0x13, 0x5b, 0x16, 0x57, 0x19, 0x55, 0x1b, 0x53, 0x1e, - 0x52, 0x1f, 0x4f, 0x22, 0x4d, 0x23, 0x4c, 0x24, 0x4b, 0x25, 0x4b, 0x27, - 0x4a, 0x28, 0x49, 0x29, 0x49, 0x29, 0x49, 0x2b, 0x09, 0x45, 0x0d, 0x40, - 0x11, 0x3d, 0x16, 0x3c, 0x19, 0x3b, 0x1d, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, - 0x23, 0x3b, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x29, 0x3c, 0x2a, 0x3b, - 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, - 0x5d, 0x27, 0x57, 0x2c, 0x54, 0x2f, 0x52, 0x31, 0x4f, 0x32, 0x4e, 0x34, - 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, 0x48, 0x39, - 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, - 0x46, 0x39, 0x46, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5b, 0x00, 0x55, 0x00, 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, 0x00, - 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xa7, 0x00, 0x91, 0x01, 0x80, 0x04, 0x75, 0x08, 0x6c, 0x0b, 0x65, 0x0e, - 0x61, 0x11, 0x5d, 0x14, 0x59, 0x16, 0x56, 0x19, 0x55, 0x1a, 0x54, 0x1c, - 0x53, 0x1e, 0x51, 0x1f, 0x4f, 0x21, 0x4d, 0x22, 0x4c, 0x23, 0x4b, 0x24, - 0x4b, 0x25, 0x4b, 0x26, 0x09, 0x45, 0x0d, 0x41, 0x10, 0x3e, 0x14, 0x3d, - 0x17, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, 0x3b, 0x20, 0x3b, 0x22, 0x3a, - 0x23, 0x3a, 0x25, 0x3b, 0x26, 0x3c, 0x28, 0x3c, 0x28, 0x3c, 0x29, 0x3c, - 0x2a, 0x3c, 0x2b, 0x3b, 0x2c, 0x3b, 0x2d, 0x3b, 0x5d, 0x26, 0x59, 0x2a, - 0x56, 0x2d, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, - 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x37, 0x4a, 0x38, 0x49, 0x39, - 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, - 0x00, 0x4a, 0x00, 0x38, 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xa9, 0x00, 0x96, 0x00, - 0x86, 0x02, 0x7a, 0x05, 0x72, 0x08, 0x6b, 0x0b, 0x65, 0x0e, 0x62, 0x10, - 0x5f, 0x12, 0x5b, 0x14, 0x58, 0x16, 0x56, 0x18, 0x55, 0x1a, 0x54, 0x1b, - 0x53, 0x1d, 0x52, 0x1e, 0x50, 0x1f, 0x4f, 0x20, 0x4d, 0x21, 0x4c, 0x22, - 0x09, 0x46, 0x0c, 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, - 0x1a, 0x3b, 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x21, 0x3a, 0x22, 0x3a, - 0x24, 0x3a, 0x25, 0x3a, 0x26, 0x3b, 0x27, 0x3c, 0x28, 0x3c, 0x29, 0x3c, - 0x2a, 0x3c, 0x2a, 0x3c, 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2c, 0x53, 0x2e, - 0x53, 0x30, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, 0x4a, 0x38, - 0x48, 0x39, 0x47, 0x39, 0x46, 0x39, 0x46, 0x39, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, - 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, - 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xab, 0x00, 0x9a, 0x00, 0x8b, 0x01, 0x7f, 0x03, - 0x77, 0x06, 0x71, 0x08, 0x6a, 0x0b, 0x66, 0x0d, 0x63, 0x0f, 0x60, 0x11, - 0x5d, 0x13, 0x59, 0x14, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x54, 0x1b, - 0x53, 0x1c, 0x53, 0x1d, 0x51, 0x1e, 0x50, 0x1f, 0x0a, 0x46, 0x0c, 0x42, - 0x0e, 0x3f, 0x11, 0x3f, 0x13, 0x3c, 0x16, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, - 0x1c, 0x3a, 0x1e, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3a, 0x23, 0x3a, - 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3c, 0x27, 0x3c, 0x28, 0x3c, - 0x5e, 0x26, 0x5a, 0x29, 0x57, 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x30, - 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, 0x4d, 0x35, 0x4b, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x38, - 0x4a, 0x39, 0x48, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, - 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, - 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xac, 0x00, 0x9d, 0x00, 0x8f, 0x01, 0x85, 0x02, 0x7b, 0x04, 0x75, 0x06, - 0x6f, 0x08, 0x69, 0x0b, 0x66, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5e, 0x12, - 0x5b, 0x13, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x19, 0x55, 0x1a, - 0x54, 0x1b, 0x53, 0x1c, 0x0a, 0x47, 0x0b, 0x42, 0x0e, 0x40, 0x10, 0x3f, - 0x12, 0x3d, 0x15, 0x3c, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, - 0x1d, 0x3b, 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x23, 0x3a, - 0x24, 0x3a, 0x25, 0x39, 0x26, 0x3a, 0x27, 0x3b, 0x5e, 0x26, 0x5b, 0x28, - 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, 0x4e, 0x35, 0x4b, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x36, 0x4a, 0x37, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, - 0x00, 0x55, 0x00, 0x4b, 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, - 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, - 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xad, 0x00, 0xa0, 0x00, - 0x92, 0x00, 0x89, 0x02, 0x7f, 0x03, 0x78, 0x05, 0x73, 0x07, 0x6e, 0x09, - 0x69, 0x0b, 0x66, 0x0c, 0x64, 0x0e, 0x62, 0x0f, 0x60, 0x11, 0x5c, 0x12, - 0x59, 0x14, 0x58, 0x15, 0x57, 0x16, 0x56, 0x18, 0x55, 0x18, 0x55, 0x1a, - 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x13, 0x3c, - 0x15, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x3a, - 0x1f, 0x3b, 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x24, 0x3a, - 0x24, 0x3a, 0x24, 0x39, 0x5e, 0x26, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2b, - 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2f, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x32, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, - 0x00, 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xae, 0x00, 0xa2, 0x00, 0x95, 0x00, 0x8b, 0x01, - 0x83, 0x02, 0x7b, 0x04, 0x76, 0x05, 0x73, 0x07, 0x6e, 0x09, 0x69, 0x0a, - 0x66, 0x0c, 0x64, 0x0d, 0x62, 0x0f, 0x61, 0x10, 0x5e, 0x11, 0x5b, 0x13, - 0x58, 0x14, 0x58, 0x15, 0x56, 0x16, 0x56, 0x18, 0x0a, 0x47, 0x0b, 0x43, - 0x0d, 0x42, 0x0f, 0x3f, 0x11, 0x3f, 0x12, 0x3e, 0x14, 0x3c, 0x16, 0x3c, - 0x17, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3b, - 0x1f, 0x3b, 0x20, 0x3b, 0x21, 0x3b, 0x21, 0x3a, 0x22, 0x3a, 0x24, 0x3a, - 0x5e, 0x26, 0x5c, 0x27, 0x59, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x32, 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, - 0x00, 0x33, 0x00, 0x28, 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, - 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x87, 0x02, 0x7f, 0x03, - 0x79, 0x04, 0x75, 0x06, 0x71, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0c, - 0x64, 0x0d, 0x63, 0x0e, 0x61, 0x10, 0x5f, 0x11, 0x5c, 0x12, 0x59, 0x13, - 0x58, 0x14, 0x58, 0x15, 0x0a, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0e, 0x40, - 0x10, 0x3e, 0x11, 0x3f, 0x13, 0x3c, 0x15, 0x3b, 0x17, 0x3c, 0x17, 0x3c, - 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, - 0x1f, 0x3b, 0x21, 0x3b, 0x21, 0x3b, 0x22, 0x3a, 0x5e, 0x25, 0x5c, 0x27, - 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x33, 0x4e, 0x34, 0x4c, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x59, 0x00, 0x53, 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, - 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, - 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xaf, 0x00, 0xa5, 0x00, - 0x9a, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x83, 0x02, 0x7c, 0x03, 0x77, 0x05, - 0x74, 0x06, 0x70, 0x07, 0x6c, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x64, 0x0c, - 0x63, 0x0e, 0x61, 0x0f, 0x60, 0x11, 0x5d, 0x11, 0x5b, 0x13, 0x59, 0x13, - 0x0a, 0x47, 0x0a, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3e, 0x11, 0x3f, - 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, - 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x3a, 0x1f, 0x3b, 0x1f, 0x3b, - 0x20, 0x3b, 0x21, 0x3b, 0x5e, 0x25, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, - 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, - 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, - 0x4f, 0x33, 0x4d, 0x35, 0x4b, 0x35, 0x4a, 0x35, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, - 0x00, 0x4e, 0x00, 0x46, 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, - 0x00, 0x18, 0x00, 0x0f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, - 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x92, 0x00, - 0x8c, 0x01, 0x86, 0x02, 0x7e, 0x02, 0x7a, 0x04, 0x76, 0x05, 0x73, 0x06, - 0x70, 0x07, 0x6b, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0e, - 0x62, 0x0e, 0x61, 0x10, 0x5e, 0x11, 0x5c, 0x12, 0x0a, 0x47, 0x0a, 0x44, - 0x0c, 0x42, 0x0d, 0x41, 0x0f, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x13, 0x3d, - 0x15, 0x3c, 0x16, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, - 0x1c, 0x3b, 0x1c, 0x39, 0x1e, 0x39, 0x1f, 0x3b, 0x1f, 0x3b, 0x1f, 0x3b, - 0x5f, 0x25, 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, - 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x33, - 0x4e, 0x34, 0x4c, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, - 0x00, 0x41, 0x00, 0x39, 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, - 0x00, 0x0e, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, - 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, - 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xb0, 0x00, 0xa7, 0x00, 0x9f, 0x00, 0x95, 0x00, 0x8e, 0x00, 0x88, 0x01, - 0x82, 0x02, 0x7c, 0x03, 0x78, 0x05, 0x75, 0x06, 0x72, 0x07, 0x6f, 0x08, - 0x6a, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x62, 0x0e, - 0x61, 0x0f, 0x60, 0x11, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, - 0x0e, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3b, - 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, - 0x1c, 0x3a, 0x1d, 0x39, 0x1f, 0x3a, 0x1f, 0x3b, 0x5f, 0x25, 0x5c, 0x27, - 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x32, 0x4f, 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, - 0x00, 0x5b, 0x00, 0x57, 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, - 0x00, 0x35, 0x00, 0x2c, 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, - 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa7, 0x00, - 0xa0, 0x00, 0x97, 0x00, 0x90, 0x00, 0x8a, 0x01, 0x85, 0x02, 0x7e, 0x02, - 0x7a, 0x03, 0x77, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6e, 0x08, 0x6a, 0x09, - 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, 0x63, 0x0d, 0x63, 0x0e, 0x61, 0x0f, - 0x0a, 0x47, 0x0a, 0x45, 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3e, - 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x15, 0x3a, 0x17, 0x3c, - 0x17, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, - 0x1d, 0x39, 0x1f, 0x39, 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x59, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x17, 0x00, 0x2f, - 0x00, 0x4c, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, - 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, - 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, - 0x00, 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x14, 0x01, 0x29, - 0x00, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, - 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x5f, 0x00, 0x5f, 0x31, 0x11, 0x03, 0x23, 0x00, 0x46, 0x00, 0x52, - 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, - 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x99, 0x00, - 0x91, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7c, 0x03, 0x79, 0x04, - 0x76, 0x05, 0x74, 0x06, 0x71, 0x07, 0x6d, 0x08, 0x6a, 0x09, 0x68, 0x0a, - 0x66, 0x0b, 0x65, 0x0c, 0x64, 0x0c, 0x63, 0x0e, 0x0a, 0x47, 0x0a, 0x45, - 0x0b, 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3e, 0x11, 0x3f, - 0x13, 0x3e, 0x13, 0x3c, 0x15, 0x3c, 0x16, 0x3a, 0x17, 0x3c, 0x17, 0x3c, - 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3a, 0x1d, 0x39, - 0x5f, 0x25, 0x5c, 0x27, 0x5c, 0x27, 0x5a, 0x29, 0x57, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2f, 0x51, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, - 0x00, 0x4f, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, - 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, - 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, - 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x57, 0x00, 0x16, 0x04, 0x00, 0x27, 0x00, 0x41, - 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, - 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, - 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, - 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, - 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0xb1, 0x00, 0xa9, 0x00, 0xa2, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x8e, 0x00, - 0x89, 0x01, 0x84, 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x05, 0x75, 0x05, - 0x73, 0x06, 0x71, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, - 0x65, 0x0c, 0x64, 0x0c, 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, - 0x0e, 0x42, 0x0f, 0x40, 0x10, 0x3d, 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3d, - 0x15, 0x3c, 0x15, 0x3b, 0x17, 0x3a, 0x17, 0x3c, 0x17, 0x3c, 0x19, 0x3c, - 0x19, 0x3c, 0x1a, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x5f, 0x25, 0x5c, 0x27, - 0x5c, 0x27, 0x5b, 0x28, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x30, - 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, - 0x00, 0x4a, 0x00, 0x4f, 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, - 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5f, 0x00, 0x48, - 0x00, 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, - 0x00, 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, - 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x82, 0x00, 0x57, 0x00, 0x12, 0x01, 0x00, 0x17, 0x00, 0x2f, 0x00, 0x3e, - 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, 0x56, 0x00, 0x57, - 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x85, 0x00, 0x5f, 0x00, - 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, - 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, - 0x00, 0x59, 0x00, 0x5a, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xa9, 0x00, - 0xa3, 0x00, 0x9d, 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x01, 0x87, 0x02, - 0x81, 0x02, 0x7c, 0x03, 0x79, 0x03, 0x77, 0x05, 0x75, 0x06, 0x72, 0x06, - 0x70, 0x07, 0x6c, 0x08, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x65, 0x0c, - 0x0a, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, - 0x0f, 0x3e, 0x11, 0x3e, 0x11, 0x3f, 0x13, 0x3f, 0x13, 0x3c, 0x15, 0x3c, - 0x15, 0x3a, 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, - 0x1a, 0x3b, 0x1c, 0x3b, 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5b, 0x28, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x50, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x01, 0x00, 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, - 0x00, 0x47, 0x00, 0x4b, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, - 0x00, 0x56, 0x00, 0x57, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, - 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, - 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x78, 0x00, - 0x43, 0x00, 0x10, 0x00, 0x00, 0x0e, 0x00, 0x23, 0x00, 0x31, 0x00, 0x3b, - 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, 0x51, 0x00, 0x53, - 0x00, 0x55, 0x00, 0x56, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x00, - 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, - 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, - 0x96, 0x00, 0x90, 0x00, 0x8c, 0x00, 0x88, 0x01, 0x84, 0x02, 0x7e, 0x02, - 0x7b, 0x03, 0x78, 0x04, 0x76, 0x05, 0x74, 0x06, 0x72, 0x07, 0x6f, 0x07, - 0x6b, 0x09, 0x69, 0x09, 0x68, 0x0a, 0x66, 0x0b, 0x0a, 0x47, 0x0a, 0x46, - 0x0b, 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x3f, 0x10, 0x3d, - 0x11, 0x3f, 0x12, 0x3f, 0x13, 0x3e, 0x14, 0x3c, 0x15, 0x3c, 0x16, 0x3a, - 0x17, 0x3b, 0x17, 0x3c, 0x18, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3b, - 0x5f, 0x24, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x58, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x30, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x14, 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, - 0x00, 0x44, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, - 0x00, 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, - 0x00, 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, - 0x00, 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x87, 0x00, 0x61, 0x00, 0x36, 0x00, - 0x10, 0x00, 0x02, 0x0a, 0x00, 0x1a, 0x00, 0x27, 0x00, 0x32, 0x00, 0x3a, - 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, 0x4e, 0x00, 0x50, - 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x00, - 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, - 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x28, 0x3c, 0x16, 0x41, - 0x11, 0x43, 0x0f, 0x44, 0x0e, 0x45, 0x0d, 0x46, 0x0c, 0x46, 0x0c, 0x46, - 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0b, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x6e, 0x1c, 0x3a, 0x29, 0x27, 0x31, 0x1e, 0x36, 0x19, 0x3a, 0x17, 0x3b, - 0x14, 0x3d, 0x13, 0x3f, 0x11, 0x40, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, - 0x0f, 0x43, 0x0f, 0x43, 0x0e, 0x43, 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, - 0x0c, 0x44, 0x0c, 0x44, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x3c, 0x32, 0x22, 0x38, - 0x18, 0x3c, 0x14, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x43, - 0x0e, 0x44, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x44, 0x0c, 0x45, 0x0c, 0x45, - 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, 0x0b, 0x45, 0x0b, 0x46, 0x0b, 0x46, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x10, 0x00, 0x1e, 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, - 0x00, 0x42, 0x00, 0x46, 0x00, 0x49, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x57, - 0x00, 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, - 0x00, 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, - 0x00, 0x49, 0x00, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, 0x00, 0x2e, 0x00, 0x10, 0x00, - 0x04, 0x08, 0x00, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, 0x32, 0x00, 0x38, - 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x9a, 0x00, 0x91, 0x00, - 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x00, 0x00, 0x08, - 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, - 0x00, 0x42, 0x00, 0x45, 0x30, 0x38, 0x20, 0x3a, 0x18, 0x3d, 0x14, 0x3e, - 0x12, 0x40, 0x10, 0x41, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0x0d, 0x43, - 0x0d, 0x43, 0x0d, 0x44, 0x0d, 0x44, 0x0c, 0x45, 0x0c, 0x45, 0x0b, 0x45, - 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x46, 0x0b, 0x47, 0x8a, 0x03, 0x59, 0x0f, - 0x40, 0x18, 0x32, 0x1f, 0x2a, 0x24, 0x24, 0x28, 0x20, 0x2c, 0x1c, 0x2f, - 0x1b, 0x31, 0x18, 0x33, 0x17, 0x34, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, - 0x13, 0x39, 0x12, 0x3a, 0x12, 0x3a, 0x12, 0x3b, 0x11, 0x3c, 0x10, 0x3c, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x4a, 0x25, 0x31, 0x2b, 0x25, 0x30, 0x1e, 0x33, - 0x1a, 0x36, 0x17, 0x38, 0x15, 0x39, 0x13, 0x3b, 0x12, 0x3c, 0x11, 0x3d, - 0x10, 0x3e, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x40, - 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, - 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, - 0x00, 0x41, 0x00, 0x44, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, - 0x00, 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, - 0x00, 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x94, 0x00, - 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, 0x00, 0x10, 0x00, 0x06, 0x07, - 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x38, - 0x00, 0x3c, 0x00, 0x40, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, - 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, 0x00, 0x03, 0x00, 0x10, - 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, - 0x34, 0x38, 0x25, 0x39, 0x1e, 0x3b, 0x19, 0x3c, 0x16, 0x3d, 0x14, 0x3d, - 0x13, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, 0x0e, 0x42, - 0x0e, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x43, 0x0d, 0x43, - 0x0d, 0x44, 0x0d, 0x44, 0x97, 0x00, 0x6c, 0x06, 0x52, 0x0d, 0x43, 0x12, - 0x38, 0x18, 0x30, 0x1c, 0x2a, 0x20, 0x27, 0x23, 0x23, 0x26, 0x20, 0x28, - 0x1e, 0x2a, 0x1c, 0x2c, 0x1b, 0x2e, 0x19, 0x2f, 0x18, 0x31, 0x17, 0x32, - 0x16, 0x33, 0x16, 0x34, 0x16, 0x35, 0x15, 0x35, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x50, 0x24, 0x3b, 0x27, 0x2e, 0x2a, 0x26, 0x2d, 0x21, 0x2f, 0x1d, 0x32, - 0x1a, 0x33, 0x18, 0x35, 0x16, 0x36, 0x15, 0x38, 0x14, 0x39, 0x13, 0x3a, - 0x12, 0x3a, 0x11, 0x3b, 0x11, 0x3c, 0x11, 0x3c, 0x10, 0x3d, 0x10, 0x3e, - 0x10, 0x3e, 0x0f, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, - 0x00, 0x20, 0x00, 0x28, 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, - 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, - 0x00, 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, - 0x00, 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x87, 0x00, 0x71, 0x00, - 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, 0x00, 0x07, 0x06, 0x00, 0x0b, - 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, 0x32, 0x00, 0x37, - 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, - 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x15, - 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x36, 0x39, 0x2a, 0x39, - 0x22, 0x39, 0x1d, 0x3a, 0x1a, 0x3b, 0x17, 0x3d, 0x15, 0x3d, 0x13, 0x3d, - 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x40, 0x10, 0x41, 0x0f, 0x41, 0x0f, 0x42, - 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0e, 0x41, 0x0e, 0x42, 0x0d, 0x42, - 0x9e, 0x00, 0x79, 0x02, 0x61, 0x07, 0x50, 0x0b, 0x44, 0x10, 0x3b, 0x14, - 0x34, 0x18, 0x2f, 0x1b, 0x2b, 0x1e, 0x28, 0x20, 0x25, 0x23, 0x23, 0x25, - 0x21, 0x27, 0x1f, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, 0x1b, 0x2c, 0x19, 0x2e, - 0x19, 0x2f, 0x19, 0x30, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x54, 0x23, 0x42, 0x25, - 0x35, 0x27, 0x2d, 0x29, 0x27, 0x2c, 0x22, 0x2e, 0x1f, 0x2f, 0x1c, 0x31, - 0x1a, 0x33, 0x19, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, 0x14, 0x38, - 0x13, 0x38, 0x13, 0x39, 0x12, 0x3a, 0x11, 0x3a, 0x11, 0x3b, 0x11, 0x3c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, - 0x00, 0x24, 0x00, 0x2a, 0x00, 0x30, 0x00, 0x35, 0x00, 0x5e, 0x00, 0x5b, - 0x00, 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, - 0x00, 0x30, 0x00, 0x35, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, - 0x37, 0x00, 0x22, 0x00, 0x10, 0x00, 0x08, 0x05, 0x02, 0x0a, 0x00, 0x12, - 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x9d, 0x00, 0x99, 0x00, - 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, - 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, 0x00, 0x10, 0x00, 0x19, - 0x00, 0x20, 0x00, 0x26, 0x38, 0x39, 0x2d, 0x39, 0x25, 0x39, 0x20, 0x3a, - 0x1d, 0x3b, 0x1a, 0x3b, 0x18, 0x3c, 0x16, 0x3d, 0x14, 0x3d, 0x13, 0x3d, - 0x13, 0x3d, 0x12, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, 0x0f, 0x41, - 0x0f, 0x42, 0x0f, 0x42, 0x0e, 0x42, 0x0e, 0x42, 0xa2, 0x00, 0x84, 0x00, - 0x6c, 0x04, 0x5a, 0x07, 0x4e, 0x0b, 0x45, 0x0f, 0x3e, 0x12, 0x38, 0x15, - 0x32, 0x18, 0x2f, 0x1a, 0x2c, 0x1d, 0x29, 0x1f, 0x26, 0x21, 0x24, 0x22, - 0x22, 0x24, 0x21, 0x26, 0x1f, 0x27, 0x1e, 0x28, 0x1d, 0x2a, 0x1c, 0x2b, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x56, 0x23, 0x47, 0x24, 0x3b, 0x25, 0x32, 0x27, - 0x2c, 0x29, 0x27, 0x2b, 0x24, 0x2d, 0x21, 0x2e, 0x1e, 0x30, 0x1c, 0x31, - 0x1b, 0x32, 0x19, 0x33, 0x18, 0x34, 0x17, 0x35, 0x16, 0x36, 0x15, 0x37, - 0x14, 0x37, 0x14, 0x38, 0x13, 0x38, 0x13, 0x39, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, - 0x00, 0x27, 0x00, 0x2c, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, - 0x00, 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, - 0x00, 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, 0x00, 0x46, 0x00, 0x32, 0x00, - 0x20, 0x00, 0x10, 0x00, 0x09, 0x04, 0x03, 0x09, 0x00, 0x0f, 0x00, 0x17, - 0x00, 0x1e, 0x00, 0x24, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, - 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, - 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, - 0x39, 0x3a, 0x2f, 0x39, 0x28, 0x39, 0x23, 0x39, 0x20, 0x3a, 0x1c, 0x3b, - 0x1a, 0x3a, 0x18, 0x3b, 0x17, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, - 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x11, 0x40, 0x10, 0x40, - 0x0f, 0x40, 0x0f, 0x41, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x02, 0x63, 0x05, - 0x57, 0x07, 0x4d, 0x0b, 0x45, 0x0e, 0x3e, 0x11, 0x39, 0x13, 0x35, 0x16, - 0x32, 0x18, 0x2e, 0x1a, 0x2c, 0x1c, 0x29, 0x1d, 0x27, 0x1f, 0x26, 0x22, - 0x24, 0x22, 0x21, 0x23, 0x21, 0x25, 0x20, 0x26, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, 0x26, 0x30, 0x27, 0x2b, 0x29, - 0x27, 0x2a, 0x24, 0x2c, 0x22, 0x2d, 0x20, 0x2e, 0x1e, 0x30, 0x1c, 0x30, - 0x1b, 0x32, 0x1a, 0x32, 0x18, 0x33, 0x18, 0x34, 0x17, 0x34, 0x16, 0x35, - 0x16, 0x36, 0x15, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, - 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, - 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, - 0x00, 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x86, 0x00, - 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, - 0x10, 0x00, 0x09, 0x04, 0x04, 0x08, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1a, - 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, - 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, - 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, 0x3a, 0x3a, 0x31, 0x3a, - 0x2a, 0x39, 0x25, 0x3a, 0x22, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1a, 0x3a, - 0x19, 0x3a, 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, - 0x13, 0x3d, 0x12, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x10, 0x40, - 0xa8, 0x00, 0x90, 0x00, 0x7c, 0x01, 0x6b, 0x02, 0x5f, 0x05, 0x55, 0x07, - 0x4c, 0x0a, 0x45, 0x0d, 0x40, 0x0f, 0x3c, 0x12, 0x37, 0x14, 0x34, 0x16, - 0x31, 0x18, 0x2e, 0x19, 0x2b, 0x1b, 0x2a, 0x1d, 0x28, 0x1e, 0x26, 0x20, - 0x25, 0x22, 0x23, 0x22, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x59, 0x23, 0x4d, 0x23, - 0x43, 0x24, 0x3b, 0x25, 0x34, 0x26, 0x2f, 0x27, 0x2b, 0x29, 0x28, 0x2a, - 0x25, 0x2b, 0x23, 0x2d, 0x21, 0x2e, 0x1f, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x17, 0x34, 0x16, 0x34, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x0f, 0x00, 0x16, 0x00, 0x1c, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, - 0x00, 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, - 0x00, 0x16, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, 0x00, 0x7d, 0x00, 0x6d, 0x00, - 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1d, 0x00, 0x10, 0x00, - 0x0a, 0x04, 0x05, 0x07, 0x00, 0x0b, 0x00, 0x11, 0x9e, 0x00, 0x9c, 0x00, - 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, - 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, - 0x01, 0x00, 0x00, 0x06, 0x3a, 0x3b, 0x32, 0x3a, 0x2c, 0x39, 0x27, 0x3a, - 0x23, 0x39, 0x21, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, - 0x17, 0x3b, 0x17, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x14, 0x3e, 0x13, 0x3d, - 0x13, 0x3d, 0x13, 0x3d, 0x11, 0x3d, 0x11, 0x3e, 0xaa, 0x00, 0x94, 0x00, - 0x82, 0x00, 0x72, 0x01, 0x65, 0x04, 0x5b, 0x05, 0x53, 0x07, 0x4b, 0x0a, - 0x45, 0x0c, 0x41, 0x0f, 0x3d, 0x11, 0x39, 0x12, 0x36, 0x15, 0x33, 0x16, - 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1d, 0x29, 0x1d, 0x26, 0x1e, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5a, 0x23, 0x4f, 0x23, 0x46, 0x24, 0x3e, 0x24, - 0x37, 0x25, 0x32, 0x26, 0x2e, 0x27, 0x2b, 0x28, 0x28, 0x2a, 0x25, 0x2b, - 0x23, 0x2c, 0x21, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x1b, 0x31, 0x1a, 0x32, 0x19, 0x32, 0x18, 0x33, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, - 0x00, 0x0e, 0x00, 0x14, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, - 0x00, 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, - 0x00, 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, 0x00, 0x65, 0x00, 0x56, 0x00, - 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, 0x00, 0x10, 0x00, 0x0a, 0x03, - 0x06, 0x07, 0x01, 0x0a, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, - 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, - 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x00, - 0x3b, 0x3b, 0x33, 0x3b, 0x2d, 0x39, 0x29, 0x39, 0x25, 0x3a, 0x22, 0x39, - 0x20, 0x39, 0x1e, 0x3b, 0x1c, 0x3c, 0x1a, 0x3a, 0x19, 0x3a, 0x18, 0x3a, - 0x17, 0x3c, 0x16, 0x3c, 0x15, 0x3d, 0x15, 0x3e, 0x13, 0x3f, 0x13, 0x3d, - 0x13, 0x3d, 0x13, 0x3d, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x01, - 0x6b, 0x02, 0x61, 0x04, 0x58, 0x06, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, - 0x42, 0x0e, 0x3e, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x16, 0x32, 0x16, - 0x30, 0x18, 0x2e, 0x19, 0x2b, 0x1a, 0x2b, 0x1c, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, 0x24, 0x3a, 0x25, 0x35, 0x25, - 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2b, 0x24, 0x2b, - 0x22, 0x2d, 0x21, 0x2d, 0x1f, 0x2e, 0x1e, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x1b, 0x30, 0x1a, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, - 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, - 0x00, 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, - 0x00, 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x90, 0x00, - 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x42, 0x00, - 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x06, 0x06, - 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, - 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x3b, 0x3c, 0x34, 0x3a, - 0x2f, 0x3a, 0x2a, 0x39, 0x26, 0x3a, 0x23, 0x3a, 0x21, 0x39, 0x1f, 0x3a, - 0x1d, 0x3b, 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3b, - 0x17, 0x3c, 0x15, 0x3c, 0x15, 0x3d, 0x15, 0x3f, 0x13, 0x3f, 0x13, 0x3e, - 0xac, 0x00, 0x9b, 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x03, - 0x5e, 0x05, 0x57, 0x07, 0x51, 0x08, 0x4b, 0x0a, 0x46, 0x0c, 0x42, 0x0d, - 0x3f, 0x0f, 0x3b, 0x11, 0x39, 0x12, 0x35, 0x14, 0x34, 0x16, 0x31, 0x16, - 0x30, 0x18, 0x2e, 0x19, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x52, 0x23, - 0x4a, 0x23, 0x43, 0x24, 0x3d, 0x24, 0x38, 0x25, 0x34, 0x26, 0x30, 0x27, - 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x24, 0x2b, 0x22, 0x2c, - 0x21, 0x2d, 0x20, 0x2d, 0x1f, 0x2e, 0x1d, 0x2e, 0x1d, 0x30, 0x1c, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x5f, 0x00, 0x5e, - 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, - 0x00, 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, - 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, 0x00, 0x3e, 0x00, 0x31, 0x00, - 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x9e, 0x00, 0x9d, 0x00, - 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, - 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, - 0x1f, 0x00, 0x17, 0x00, 0x3b, 0x3c, 0x35, 0x3a, 0x30, 0x3b, 0x2c, 0x39, - 0x28, 0x3a, 0x25, 0x3b, 0x22, 0x39, 0x21, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, - 0x1c, 0x3c, 0x1a, 0x3b, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3c, - 0x16, 0x3c, 0x15, 0x3c, 0x15, 0x3e, 0x14, 0x3f, 0xad, 0x00, 0x9d, 0x00, - 0x8e, 0x00, 0x81, 0x00, 0x76, 0x01, 0x6b, 0x02, 0x63, 0x04, 0x5c, 0x05, - 0x56, 0x07, 0x50, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x40, 0x0f, - 0x3c, 0x0f, 0x3a, 0x12, 0x37, 0x12, 0x35, 0x14, 0x33, 0x16, 0x30, 0x16, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5b, 0x23, 0x53, 0x23, 0x4c, 0x23, 0x45, 0x23, - 0x40, 0x24, 0x3b, 0x25, 0x36, 0x25, 0x33, 0x26, 0x30, 0x27, 0x2d, 0x28, - 0x2a, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2d, - 0x20, 0x2d, 0x20, 0x2e, 0x1e, 0x2e, 0x1d, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, - 0x00, 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, - 0x00, 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, - 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6b, 0x00, - 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, 0x00, 0x2f, 0x00, 0x24, 0x00, - 0x19, 0x00, 0x0f, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, - 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, - 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x00, - 0x3c, 0x3d, 0x36, 0x3a, 0x31, 0x3b, 0x2c, 0x3a, 0x29, 0x39, 0x26, 0x3a, - 0x24, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3c, - 0x1a, 0x3c, 0x19, 0x3a, 0x19, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0x17, 0x3c, - 0x15, 0x3c, 0x15, 0x3c, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, - 0x79, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x60, 0x04, 0x59, 0x05, 0x54, 0x07, - 0x4f, 0x08, 0x4b, 0x0a, 0x47, 0x0c, 0x43, 0x0c, 0x41, 0x0f, 0x3d, 0x0f, - 0x3b, 0x11, 0x38, 0x12, 0x35, 0x12, 0x35, 0x15, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, 0x23, 0x42, 0x24, 0x3c, 0x24, - 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, 0x27, 0x2d, 0x28, 0x2a, 0x28, - 0x28, 0x2a, 0x26, 0x2a, 0x25, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x21, 0x2d, - 0x20, 0x2d, 0x20, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3d, 0x36, 0x3a, - 0x32, 0x3b, 0x2d, 0x3b, 0x2b, 0x39, 0x28, 0x3a, 0x25, 0x3a, 0x23, 0x3a, - 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, - 0x19, 0x3a, 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3c, 0x16, 0x3c, - 0xad, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x01, - 0x6c, 0x02, 0x65, 0x03, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x07, 0x4f, 0x08, - 0x4b, 0x0a, 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0e, 0x3e, 0x0f, 0x3b, 0x0f, - 0x3a, 0x12, 0x37, 0x12, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x55, 0x23, - 0x4f, 0x23, 0x49, 0x23, 0x44, 0x24, 0x3f, 0x24, 0x3b, 0x25, 0x37, 0x25, - 0x34, 0x26, 0x31, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, - 0x27, 0x2a, 0x25, 0x2b, 0x24, 0x2b, 0x22, 0x2b, 0x22, 0x2d, 0x20, 0x2d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3b, 0x2f, 0x3b, - 0x2b, 0x39, 0x29, 0x39, 0x26, 0x3a, 0x24, 0x3b, 0x22, 0x3a, 0x21, 0x39, - 0x1f, 0x39, 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, - 0x19, 0x3a, 0x18, 0x3a, 0x17, 0x3a, 0x17, 0x3b, 0xae, 0x00, 0xa2, 0x00, - 0x97, 0x00, 0x8b, 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, - 0x61, 0x04, 0x5c, 0x05, 0x57, 0x05, 0x52, 0x07, 0x4e, 0x08, 0x4b, 0x0a, - 0x47, 0x0b, 0x44, 0x0c, 0x41, 0x0d, 0x3f, 0x0f, 0x3c, 0x0f, 0x3b, 0x11, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x56, 0x23, 0x50, 0x23, 0x4a, 0x23, - 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x35, 0x25, 0x33, 0x26, - 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, - 0x25, 0x2a, 0x24, 0x2b, 0x23, 0x2b, 0x22, 0x2c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3c, 0x3e, 0x37, 0x3b, 0x33, 0x3a, 0x2f, 0x3b, 0x2c, 0x3a, 0x2a, 0x39, - 0x27, 0x3a, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, - 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, - 0x19, 0x3a, 0x17, 0x3a, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, - 0x84, 0x00, 0x7a, 0x00, 0x72, 0x01, 0x6c, 0x02, 0x65, 0x02, 0x5f, 0x04, - 0x5a, 0x05, 0x56, 0x05, 0x52, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0b, - 0x45, 0x0c, 0x41, 0x0c, 0x40, 0x0f, 0x3d, 0x0f, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, 0x23, 0x47, 0x23, 0x42, 0x24, - 0x3e, 0x24, 0x3b, 0x25, 0x37, 0x25, 0x34, 0x25, 0x32, 0x26, 0x30, 0x26, - 0x2e, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, - 0x25, 0x2b, 0x23, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x3e, 0x38, 0x3c, - 0x34, 0x3a, 0x30, 0x3b, 0x2d, 0x3b, 0x2a, 0x39, 0x28, 0x39, 0x26, 0x3a, - 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, 0x1f, 0x39, 0x1e, 0x3b, - 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3b, 0x19, 0x3a, 0x19, 0x3a, - 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, - 0x76, 0x00, 0x6f, 0x01, 0x68, 0x02, 0x62, 0x04, 0x5e, 0x04, 0x59, 0x05, - 0x55, 0x06, 0x51, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, - 0x41, 0x0c, 0x41, 0x0f, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x57, 0x23, - 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, 0x3c, 0x24, - 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, - 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x29, 0x27, 0x2a, 0x25, 0x2a, 0x25, 0x2b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x38, 0x3c, 0x34, 0x3a, 0x31, 0x3b, - 0x2e, 0x3c, 0x2b, 0x39, 0x29, 0x39, 0x27, 0x3a, 0x25, 0x3a, 0x24, 0x3b, - 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, - 0x1c, 0x3c, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3a, 0xaf, 0x00, 0xa6, 0x00, - 0x9b, 0x00, 0x91, 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x01, - 0x6b, 0x02, 0x66, 0x02, 0x61, 0x04, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x07, - 0x50, 0x07, 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0c, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5c, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4d, 0x23, - 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, 0x24, 0x3b, 0x24, 0x38, 0x25, - 0x35, 0x25, 0x33, 0x26, 0x31, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, - 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, 0x26, 0x2a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x3e, 0x38, 0x3d, 0x35, 0x3a, 0x32, 0x3b, 0x2e, 0x3b, 0x2c, 0x3a, - 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x39, - 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, - 0x1a, 0x3c, 0x19, 0x3c, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, - 0x8b, 0x00, 0x83, 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x02, - 0x63, 0x03, 0x5f, 0x04, 0x5b, 0x05, 0x56, 0x05, 0x54, 0x07, 0x4f, 0x07, - 0x4e, 0x09, 0x4a, 0x0a, 0x47, 0x0a, 0x46, 0x0c, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, 0x23, 0x4a, 0x23, 0x46, 0x23, - 0x43, 0x24, 0x3f, 0x24, 0x3c, 0x24, 0x39, 0x25, 0x36, 0x25, 0x34, 0x25, - 0x33, 0x26, 0x30, 0x26, 0x2f, 0x27, 0x2d, 0x27, 0x2c, 0x28, 0x2a, 0x28, - 0x28, 0x28, 0x28, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, - 0x35, 0x3a, 0x32, 0x3a, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, 0x39, 0x28, 0x39, - 0x27, 0x3a, 0x24, 0x3a, 0x24, 0x3b, 0x22, 0x3b, 0x21, 0x39, 0x20, 0x39, - 0x1f, 0x39, 0x1f, 0x3a, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0x1a, 0x3c, - 0xb0, 0x00, 0xa7, 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, - 0x7e, 0x00, 0x77, 0x00, 0x71, 0x01, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x04, - 0x5d, 0x04, 0x5a, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4f, 0x07, 0x4e, 0x09, - 0x4a, 0x0a, 0x47, 0x0a, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, - 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, 0x23, 0x44, 0x23, 0x40, 0x24, - 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, 0x25, 0x33, 0x25, 0x32, 0x26, - 0x2f, 0x26, 0x2f, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x28, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x3e, 0x39, 0x3e, 0x35, 0x3b, 0x32, 0x3a, - 0x30, 0x3b, 0x2e, 0x3c, 0x2b, 0x3a, 0x2a, 0x39, 0x27, 0x39, 0x26, 0x3a, - 0x24, 0x3a, 0x23, 0x3b, 0x21, 0x3a, 0x21, 0x39, 0x1f, 0x39, 0x1f, 0x39, - 0x1e, 0x3b, 0x1c, 0x3b, 0x1c, 0x3b, 0x1c, 0x3c, 0xb1, 0x00, 0xa7, 0x00, - 0x9f, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x87, 0x00, 0x80, 0x00, 0x79, 0x00, - 0x73, 0x00, 0x6e, 0x01, 0x69, 0x02, 0x64, 0x02, 0x60, 0x04, 0x5c, 0x04, - 0x59, 0x05, 0x55, 0x05, 0x53, 0x07, 0x4e, 0x07, 0x4e, 0x09, 0x4a, 0x0a, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, - 0x0a, 0x47, 0x0a, 0x47, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, - 0x4c, 0x23, 0x48, 0x23, 0x45, 0x23, 0x42, 0x24, 0x3f, 0x24, 0x3c, 0x24, - 0x39, 0x25, 0x37, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, 0x26, - 0x2e, 0x27, 0x2c, 0x27, 0x2c, 0x28, 0x2a, 0x28, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x00, 0x94, 0x00, 0x9e, 0x00, 0xa3, 0x00, 0xa7, 0x00, 0xa9, 0x00, - 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, - 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, - 0xb2, 0x00, 0xb2, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x21, 0x00, 0x6e, 0x00, - 0x8a, 0x00, 0x97, 0x00, 0x9e, 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, - 0xaa, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, - 0xae, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, - 0x6c, 0x00, 0x92, 0x00, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, - 0xae, 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, - 0xb2, 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, - 0xb3, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x04, 0x70, 0x01, - 0x7f, 0x00, 0x88, 0x00, 0x90, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9c, 0x00, - 0x9f, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, 0x00, - 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x1c, 0x3a, 0x03, 0x59, 0x00, 0x6c, 0x00, - 0x79, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, - 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, - 0xa5, 0x00, 0xa6, 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x60, 0x0e, 0x79, 0x01, - 0x88, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, - 0xa5, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, - 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4e, 0x0f, 0x60, 0x07, 0x6d, 0x03, 0x77, 0x01, - 0x7f, 0x00, 0x85, 0x00, 0x8b, 0x00, 0x8f, 0x00, 0x92, 0x00, 0x94, 0x00, - 0x97, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, 0x00, 0xa0, 0x00, 0xa1, 0x00, - 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x0a, 0x29, 0x27, 0x0f, 0x40, 0x06, 0x52, 0x02, 0x61, 0x00, 0x6c, 0x00, - 0x75, 0x00, 0x7c, 0x00, 0x82, 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, - 0x91, 0x00, 0x94, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, - 0x9d, 0x00, 0x9e, 0x00, 0x60, 0x14, 0x6f, 0x07, 0x7b, 0x03, 0x84, 0x01, - 0x8c, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, - 0xa1, 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, - 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x49, 0x17, 0x56, 0x0d, 0x62, 0x07, 0x6b, 0x04, 0x73, 0x02, 0x79, 0x01, - 0x7e, 0x01, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, - 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, - 0x9e, 0x00, 0x9f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x31, 0x1e, 0x18, - 0x32, 0x0d, 0x43, 0x07, 0x50, 0x04, 0x5a, 0x02, 0x63, 0x01, 0x6b, 0x00, - 0x72, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, - 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, - 0x60, 0x18, 0x6b, 0x0c, 0x74, 0x06, 0x7d, 0x03, 0x83, 0x02, 0x89, 0x01, - 0x8d, 0x00, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, - 0x9e, 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, - 0xa5, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x1d, 0x51, 0x12, - 0x5b, 0x0c, 0x63, 0x08, 0x6a, 0x05, 0x70, 0x03, 0x77, 0x02, 0x7b, 0x02, - 0x7e, 0x01, 0x82, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8c, 0x00, 0x8e, 0x00, - 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x97, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x36, 0x19, 0x1f, 0x2a, 0x12, 0x38, 0x0b, - 0x44, 0x07, 0x4e, 0x05, 0x57, 0x02, 0x5f, 0x01, 0x65, 0x01, 0x6b, 0x00, - 0x71, 0x00, 0x76, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, - 0x87, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x60, 0x1b, 0x68, 0x0f, - 0x70, 0x09, 0x77, 0x05, 0x7d, 0x03, 0x82, 0x02, 0x87, 0x01, 0x8b, 0x00, - 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, - 0x9b, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x45, 0x21, 0x4e, 0x16, 0x56, 0x10, 0x5d, 0x0b, - 0x65, 0x08, 0x69, 0x06, 0x6f, 0x04, 0x74, 0x03, 0x78, 0x02, 0x7b, 0x02, - 0x7e, 0x01, 0x81, 0x01, 0x85, 0x00, 0x88, 0x00, 0x8a, 0x00, 0x8c, 0x00, - 0x8e, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x09, 0x3a, 0x17, 0x24, 0x24, 0x18, 0x30, 0x10, 0x3b, 0x0b, 0x45, 0x07, - 0x4d, 0x05, 0x55, 0x04, 0x5b, 0x02, 0x61, 0x01, 0x67, 0x01, 0x6b, 0x00, - 0x6f, 0x00, 0x74, 0x00, 0x77, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, - 0x83, 0x00, 0x86, 0x00, 0x60, 0x1d, 0x67, 0x12, 0x6d, 0x0c, 0x73, 0x08, - 0x79, 0x05, 0x7e, 0x03, 0x82, 0x02, 0x86, 0x02, 0x89, 0x01, 0x8c, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, - 0x9a, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x44, 0x24, 0x4c, 0x1a, 0x53, 0x13, 0x59, 0x0e, 0x5f, 0x0b, 0x65, 0x08, - 0x69, 0x06, 0x6d, 0x05, 0x73, 0x04, 0x76, 0x03, 0x79, 0x02, 0x7c, 0x02, - 0x7e, 0x01, 0x81, 0x01, 0x84, 0x00, 0x87, 0x00, 0x89, 0x00, 0x8b, 0x00, - 0x8c, 0x00, 0x8d, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3b, 0x14, 0x28, - 0x20, 0x1c, 0x2a, 0x14, 0x34, 0x0f, 0x3e, 0x0b, 0x45, 0x07, 0x4c, 0x05, - 0x53, 0x04, 0x58, 0x03, 0x5e, 0x02, 0x63, 0x01, 0x67, 0x01, 0x6c, 0x00, - 0x6f, 0x00, 0x72, 0x00, 0x76, 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, - 0x60, 0x1d, 0x66, 0x14, 0x6b, 0x0e, 0x71, 0x0a, 0x76, 0x07, 0x7a, 0x05, - 0x7e, 0x03, 0x81, 0x02, 0x85, 0x02, 0x88, 0x01, 0x8b, 0x01, 0x8d, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, - 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x27, 0x4a, 0x1d, - 0x4f, 0x16, 0x57, 0x11, 0x5b, 0x0e, 0x61, 0x0b, 0x66, 0x08, 0x69, 0x07, - 0x6d, 0x05, 0x71, 0x04, 0x75, 0x03, 0x77, 0x02, 0x7a, 0x02, 0x7c, 0x02, - 0x7e, 0x01, 0x80, 0x01, 0x83, 0x01, 0x86, 0x00, 0x88, 0x00, 0x89, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x3d, 0x13, 0x2c, 0x1c, 0x20, 0x27, 0x18, - 0x2f, 0x12, 0x38, 0x0e, 0x3e, 0x0a, 0x45, 0x07, 0x4b, 0x06, 0x51, 0x05, - 0x57, 0x04, 0x5c, 0x02, 0x60, 0x02, 0x65, 0x01, 0x67, 0x01, 0x6c, 0x00, - 0x6f, 0x00, 0x71, 0x00, 0x75, 0x00, 0x77, 0x00, 0x60, 0x1e, 0x65, 0x16, - 0x6a, 0x10, 0x6f, 0x0c, 0x73, 0x09, 0x77, 0x07, 0x7b, 0x05, 0x7e, 0x03, - 0x81, 0x03, 0x84, 0x02, 0x87, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8e, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, - 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, 0x00, - 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x2c, 0x14, 0x57, 0x00, - 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, 0x00, - 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x8c, 0x00, 0x63, 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, - 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x31, 0x11, 0x5f, 0x00, - 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, - 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x43, 0x29, 0x48, 0x20, 0x4d, 0x19, 0x54, 0x14, - 0x58, 0x10, 0x5c, 0x0d, 0x62, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6c, 0x06, - 0x70, 0x05, 0x74, 0x04, 0x76, 0x03, 0x78, 0x02, 0x7a, 0x02, 0x7c, 0x02, - 0x7e, 0x02, 0x80, 0x01, 0x82, 0x01, 0x85, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x09, 0x3f, 0x11, 0x2f, 0x1b, 0x23, 0x23, 0x1b, 0x2b, 0x15, 0x32, 0x11, - 0x39, 0x0d, 0x40, 0x0a, 0x45, 0x08, 0x4b, 0x07, 0x51, 0x05, 0x56, 0x04, - 0x59, 0x03, 0x5e, 0x02, 0x61, 0x02, 0x65, 0x01, 0x68, 0x01, 0x6b, 0x00, - 0x6e, 0x00, 0x71, 0x00, 0x60, 0x1f, 0x64, 0x17, 0x69, 0x11, 0x6d, 0x0d, - 0x71, 0x0a, 0x75, 0x08, 0x78, 0x06, 0x7b, 0x05, 0x7e, 0x04, 0x81, 0x03, - 0x84, 0x02, 0x86, 0x02, 0x88, 0x01, 0x8a, 0x01, 0x8c, 0x01, 0x8e, 0x00, - 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x00, 0x00, 0x0d, 0x00, - 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, 0x00, - 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, - 0x9c, 0x00, 0x9d, 0x00, 0x01, 0x29, 0x16, 0x04, 0x57, 0x00, 0x78, 0x00, - 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, - 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x00, 0x4d, 0x00, - 0x60, 0x00, 0x77, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, - 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, - 0x9c, 0x00, 0x9d, 0x00, 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, - 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, - 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, - 0x43, 0x2b, 0x47, 0x22, 0x4c, 0x1b, 0x51, 0x16, 0x56, 0x12, 0x59, 0x0f, - 0x5e, 0x0d, 0x63, 0x0b, 0x66, 0x09, 0x69, 0x07, 0x6b, 0x06, 0x6f, 0x05, - 0x73, 0x05, 0x75, 0x03, 0x77, 0x03, 0x79, 0x02, 0x7a, 0x02, 0x7c, 0x02, - 0x7e, 0x02, 0x7f, 0x01, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x11, 0x31, - 0x18, 0x26, 0x20, 0x1e, 0x28, 0x18, 0x2f, 0x13, 0x35, 0x0f, 0x3c, 0x0c, - 0x41, 0x0a, 0x46, 0x08, 0x4b, 0x07, 0x50, 0x05, 0x54, 0x04, 0x58, 0x04, - 0x5c, 0x02, 0x5f, 0x02, 0x62, 0x02, 0x66, 0x01, 0x69, 0x01, 0x6b, 0x00, - 0x60, 0x20, 0x64, 0x18, 0x68, 0x13, 0x6b, 0x0f, 0x6f, 0x0c, 0x73, 0x09, - 0x76, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7e, 0x04, 0x81, 0x03, 0x83, 0x02, - 0x85, 0x02, 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8d, 0x01, 0x8e, 0x00, - 0x90, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, 0x00, - 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, - 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, - 0x00, 0x49, 0x00, 0x27, 0x12, 0x01, 0x43, 0x00, 0x61, 0x00, 0x74, 0x00, - 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, 0x00, 0x93, 0x00, 0x95, 0x00, - 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, - 0x60, 0x00, 0x70, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, - 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, - 0x00, 0x46, 0x00, 0x1f, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, - 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, - 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x43, 0x2d, 0x46, 0x24, - 0x4b, 0x1e, 0x4f, 0x19, 0x55, 0x14, 0x58, 0x11, 0x5b, 0x0e, 0x5f, 0x0c, - 0x64, 0x0a, 0x66, 0x09, 0x68, 0x07, 0x6a, 0x06, 0x6e, 0x06, 0x72, 0x05, - 0x74, 0x04, 0x76, 0x03, 0x78, 0x03, 0x79, 0x02, 0x7b, 0x02, 0x7c, 0x02, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x40, 0x10, 0x33, 0x17, 0x28, 0x1e, 0x20, - 0x25, 0x1a, 0x2c, 0x16, 0x32, 0x12, 0x37, 0x0f, 0x3d, 0x0c, 0x42, 0x0a, - 0x46, 0x08, 0x4b, 0x07, 0x4f, 0x05, 0x53, 0x05, 0x57, 0x04, 0x5a, 0x04, - 0x5e, 0x02, 0x61, 0x02, 0x63, 0x01, 0x66, 0x01, 0x60, 0x20, 0x63, 0x19, - 0x67, 0x14, 0x6a, 0x10, 0x6e, 0x0d, 0x71, 0x0b, 0x74, 0x09, 0x77, 0x07, - 0x7a, 0x06, 0x7c, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, - 0x87, 0x02, 0x88, 0x02, 0x8a, 0x01, 0x8c, 0x01, 0x8d, 0x00, 0x8e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, 0x00, - 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, - 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x54, 0x00, 0x41, - 0x00, 0x17, 0x10, 0x00, 0x36, 0x00, 0x51, 0x00, 0x64, 0x00, 0x71, 0x00, - 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, 0x00, 0x8d, 0x00, 0x90, 0x00, - 0x92, 0x00, 0x93, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x89, 0x00, 0x77, 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, - 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, - 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x52, 0x00, 0x3c, - 0x00, 0x0d, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, - 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, - 0x93, 0x00, 0x95, 0x00, 0x42, 0x2e, 0x45, 0x26, 0x4b, 0x1f, 0x4d, 0x1a, - 0x52, 0x16, 0x56, 0x13, 0x59, 0x10, 0x5c, 0x0e, 0x61, 0x0c, 0x64, 0x0a, - 0x66, 0x09, 0x68, 0x07, 0x6a, 0x07, 0x6d, 0x06, 0x71, 0x05, 0x74, 0x05, - 0x75, 0x03, 0x77, 0x03, 0x78, 0x02, 0x7a, 0x02, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x09, 0x41, 0x0f, 0x34, 0x16, 0x2a, 0x1c, 0x23, 0x23, 0x1d, 0x29, 0x18, - 0x2e, 0x14, 0x34, 0x11, 0x39, 0x0e, 0x3e, 0x0c, 0x42, 0x0a, 0x47, 0x08, - 0x4b, 0x07, 0x4f, 0x05, 0x52, 0x05, 0x56, 0x04, 0x59, 0x04, 0x5c, 0x03, - 0x5f, 0x02, 0x62, 0x02, 0x60, 0x20, 0x63, 0x1a, 0x66, 0x15, 0x6a, 0x11, - 0x6d, 0x0e, 0x70, 0x0c, 0x73, 0x0a, 0x75, 0x08, 0x78, 0x07, 0x7a, 0x06, - 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x83, 0x02, 0x85, 0x02, 0x86, 0x02, - 0x88, 0x02, 0x89, 0x01, 0x8b, 0x01, 0x8c, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, - 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, - 0x86, 0x00, 0x89, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x2f, 0x00, 0x0e, - 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, 0x00, 0x64, 0x00, 0x6f, 0x00, - 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, 0x00, 0x89, 0x00, 0x8b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x90, 0x00, 0x83, 0x00, - 0x60, 0x00, 0x30, 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, - 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, - 0x86, 0x00, 0x89, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, - 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, - 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, - 0x42, 0x2f, 0x44, 0x27, 0x4a, 0x22, 0x4c, 0x1c, 0x50, 0x18, 0x55, 0x14, - 0x57, 0x12, 0x5a, 0x0f, 0x5d, 0x0d, 0x62, 0x0c, 0x64, 0x0a, 0x66, 0x09, - 0x68, 0x08, 0x6a, 0x07, 0x6c, 0x06, 0x70, 0x05, 0x73, 0x05, 0x75, 0x04, - 0x76, 0x03, 0x78, 0x03, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x42, 0x0f, 0x36, - 0x15, 0x2c, 0x1b, 0x25, 0x21, 0x1f, 0x26, 0x1a, 0x2c, 0x16, 0x31, 0x12, - 0x36, 0x0f, 0x3a, 0x0d, 0x3f, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, - 0x4e, 0x05, 0x52, 0x05, 0x55, 0x04, 0x58, 0x04, 0x5b, 0x04, 0x5d, 0x02, - 0x60, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x69, 0x12, 0x6c, 0x0f, 0x6e, 0x0d, - 0x71, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, - 0x7f, 0x04, 0x81, 0x03, 0x82, 0x02, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, - 0x89, 0x02, 0x8a, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, - 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, - 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x02, 0x0a, 0x10, 0x00, - 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, 0x00, 0x65, 0x00, 0x6d, 0x00, - 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, - 0x22, 0x00, 0x01, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, - 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, - 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, - 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, - 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x00, 0x42, 0x31, 0x44, 0x29, - 0x49, 0x23, 0x4c, 0x1e, 0x4e, 0x1a, 0x53, 0x16, 0x56, 0x13, 0x58, 0x11, - 0x5a, 0x0f, 0x5f, 0x0d, 0x63, 0x0b, 0x64, 0x0a, 0x66, 0x09, 0x68, 0x08, - 0x6a, 0x07, 0x6b, 0x06, 0x6f, 0x06, 0x72, 0x05, 0x74, 0x05, 0x75, 0x03, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x09, 0x43, 0x0f, 0x37, 0x14, 0x2e, 0x19, 0x27, - 0x1f, 0x21, 0x24, 0x1c, 0x29, 0x18, 0x2e, 0x15, 0x33, 0x12, 0x37, 0x0f, - 0x3b, 0x0c, 0x40, 0x0c, 0x43, 0x0a, 0x47, 0x08, 0x4b, 0x07, 0x4e, 0x06, - 0x51, 0x05, 0x55, 0x05, 0x56, 0x04, 0x5a, 0x04, 0x60, 0x21, 0x63, 0x1b, - 0x65, 0x17, 0x68, 0x13, 0x6b, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0a, - 0x75, 0x09, 0x77, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x04, - 0x81, 0x03, 0x82, 0x03, 0x84, 0x02, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x56, - 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x04, 0x08, 0x10, 0x00, 0x25, 0x00, - 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, 0x00, 0x65, 0x00, 0x6c, 0x00, - 0x72, 0x00, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, - 0x01, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x5c, 0x00, 0x55, - 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, - 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, - 0x77, 0x00, 0x7b, 0x00, 0x42, 0x31, 0x44, 0x2a, 0x48, 0x24, 0x4b, 0x1f, - 0x4d, 0x1b, 0x51, 0x18, 0x55, 0x15, 0x57, 0x12, 0x59, 0x10, 0x5b, 0x0e, - 0x5f, 0x0c, 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x6a, 0x07, - 0x6b, 0x06, 0x6e, 0x06, 0x71, 0x05, 0x73, 0x05, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x0a, 0x43, 0x0e, 0x38, 0x13, 0x2f, 0x18, 0x28, 0x1d, 0x22, 0x22, 0x1d, - 0x27, 0x19, 0x2b, 0x16, 0x30, 0x12, 0x35, 0x11, 0x39, 0x0f, 0x3c, 0x0c, - 0x41, 0x0b, 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x50, 0x05, - 0x54, 0x05, 0x55, 0x04, 0x60, 0x21, 0x63, 0x1c, 0x65, 0x17, 0x68, 0x14, - 0x6a, 0x11, 0x6c, 0x0e, 0x6f, 0x0c, 0x71, 0x0b, 0x73, 0x09, 0x76, 0x08, - 0x78, 0x07, 0x79, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, - 0x82, 0x03, 0x84, 0x02, 0x85, 0x02, 0x86, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4d, 0x00, 0x3b, - 0x00, 0x27, 0x00, 0x13, 0x06, 0x07, 0x10, 0x00, 0x22, 0x00, 0x32, 0x00, - 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, 0x00, - 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, - 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, - 0x5f, 0x00, 0x66, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, - 0x00, 0x1f, 0x00, 0x08, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, - 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, - 0x42, 0x32, 0x44, 0x2b, 0x47, 0x25, 0x4b, 0x21, 0x4c, 0x1d, 0x4f, 0x19, - 0x54, 0x16, 0x56, 0x14, 0x58, 0x11, 0x5a, 0x10, 0x5d, 0x0e, 0x61, 0x0c, - 0x63, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, - 0x6d, 0x06, 0x71, 0x05, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x43, 0x0e, 0x39, - 0x12, 0x31, 0x17, 0x2a, 0x1c, 0x24, 0x21, 0x1f, 0x26, 0x1b, 0x2a, 0x18, - 0x2e, 0x16, 0x32, 0x12, 0x35, 0x0f, 0x3a, 0x0f, 0x3d, 0x0c, 0x41, 0x0b, - 0x44, 0x0a, 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x53, 0x05, - 0x60, 0x21, 0x62, 0x1c, 0x65, 0x18, 0x67, 0x15, 0x6a, 0x12, 0x6c, 0x0f, - 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x09, 0x76, 0x07, 0x79, 0x07, - 0x7a, 0x06, 0x7c, 0x05, 0x7d, 0x05, 0x7f, 0x04, 0x81, 0x03, 0x82, 0x03, - 0x83, 0x02, 0x85, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, - 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, 0x32, 0x00, 0x20, - 0x00, 0x0e, 0x07, 0x06, 0x10, 0x00, 0x20, 0x00, 0x2f, 0x00, 0x3b, 0x00, - 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, - 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, - 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, - 0x00, 0x03, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, - 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x00, 0x42, 0x33, 0x43, 0x2c, - 0x46, 0x27, 0x4a, 0x22, 0x4c, 0x1e, 0x4d, 0x1b, 0x52, 0x18, 0x55, 0x15, - 0x57, 0x13, 0x58, 0x11, 0x5a, 0x0f, 0x5e, 0x0e, 0x62, 0x0c, 0x63, 0x0b, - 0x65, 0x0a, 0x66, 0x09, 0x68, 0x08, 0x69, 0x07, 0x6b, 0x07, 0x6c, 0x06, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x32, 0x16, 0x2b, - 0x1b, 0x26, 0x1f, 0x22, 0x24, 0x1d, 0x28, 0x19, 0x2b, 0x16, 0x30, 0x14, - 0x34, 0x12, 0x37, 0x0f, 0x3b, 0x0e, 0x3e, 0x0c, 0x41, 0x0b, 0x45, 0x0a, - 0x47, 0x09, 0x4a, 0x07, 0x4e, 0x07, 0x4f, 0x05, 0x60, 0x22, 0x62, 0x1d, - 0x65, 0x19, 0x66, 0x15, 0x69, 0x13, 0x6b, 0x11, 0x6d, 0x0e, 0x6f, 0x0c, - 0x71, 0x0b, 0x73, 0x0a, 0x76, 0x09, 0x77, 0x07, 0x79, 0x07, 0x7a, 0x06, - 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x83, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, - 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, 0x1a, 0x00, 0x0b, - 0x08, 0x05, 0x10, 0x00, 0x1e, 0x00, 0x2c, 0x00, 0x37, 0x00, 0x42, 0x00, - 0x4b, 0x00, 0x53, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, - 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x5e, 0x00, 0x5b, - 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, - 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, - 0x54, 0x00, 0x5b, 0x00, 0x42, 0x33, 0x43, 0x2d, 0x45, 0x28, 0x49, 0x23, - 0x4b, 0x1f, 0x4d, 0x1c, 0x50, 0x19, 0x55, 0x16, 0x56, 0x14, 0x58, 0x12, - 0x59, 0x11, 0x5b, 0x0e, 0x5f, 0x0d, 0x62, 0x0c, 0x63, 0x0b, 0x65, 0x0a, - 0x66, 0x09, 0x68, 0x09, 0x69, 0x07, 0x6b, 0x07, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0x0a, 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x19, 0x27, 0x1e, 0x22, - 0x21, 0x1e, 0x26, 0x1a, 0x2b, 0x18, 0x2e, 0x16, 0x31, 0x12, 0x35, 0x11, - 0x38, 0x0f, 0x3b, 0x0d, 0x3f, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, - 0x4a, 0x07, 0x4e, 0x07, 0x60, 0x22, 0x62, 0x1d, 0x64, 0x19, 0x66, 0x16, - 0x68, 0x13, 0x6a, 0x11, 0x6c, 0x0f, 0x6e, 0x0d, 0x71, 0x0c, 0x73, 0x0b, - 0x74, 0x09, 0x76, 0x08, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, - 0x7e, 0x05, 0x7f, 0x04, 0x80, 0x03, 0x82, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, - 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x02, 0x0a, 0x09, 0x04, - 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, 0x00, 0x3e, 0x00, 0x46, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, - 0x33, 0x00, 0x3d, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, - 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, - 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, - 0x42, 0x34, 0x43, 0x2e, 0x44, 0x29, 0x49, 0x24, 0x4b, 0x20, 0x4c, 0x1d, - 0x4e, 0x1a, 0x53, 0x18, 0x55, 0x15, 0x56, 0x13, 0x58, 0x11, 0x5a, 0x10, - 0x5c, 0x0e, 0x60, 0x0d, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, - 0x68, 0x09, 0x69, 0x07, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3b, - 0x11, 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1d, 0x23, 0x21, 0x20, 0x25, 0x1d, - 0x29, 0x19, 0x2b, 0x16, 0x30, 0x14, 0x33, 0x12, 0x35, 0x0f, 0x3a, 0x0f, - 0x3c, 0x0c, 0x40, 0x0c, 0x41, 0x0a, 0x45, 0x0a, 0x47, 0x09, 0x4a, 0x07, - 0x60, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x14, 0x6a, 0x11, - 0x6c, 0x10, 0x6e, 0x0e, 0x70, 0x0c, 0x71, 0x0b, 0x73, 0x0a, 0x75, 0x09, - 0x76, 0x07, 0x78, 0x07, 0x79, 0x06, 0x7b, 0x06, 0x7c, 0x05, 0x7e, 0x05, - 0x7f, 0x04, 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, - 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, - 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x03, 0x09, 0x09, 0x04, 0x10, 0x00, - 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, - 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, - 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, - 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, - 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x00, 0x42, 0x34, 0x43, 0x2f, - 0x44, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4c, 0x1e, 0x4d, 0x1b, 0x51, 0x18, - 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x59, 0x11, 0x5a, 0x0f, 0x5d, 0x0e, - 0x61, 0x0c, 0x63, 0x0c, 0x64, 0x0b, 0x65, 0x0a, 0x66, 0x09, 0x68, 0x09, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, - 0xb7, 0x00, 0xb7, 0x00, 0x0a, 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x2f, - 0x19, 0x2a, 0x1c, 0x25, 0x20, 0x22, 0x23, 0x1d, 0x26, 0x1a, 0x2b, 0x18, - 0x2e, 0x16, 0x30, 0x12, 0x35, 0x12, 0x37, 0x0f, 0x3b, 0x0f, 0x3d, 0x0c, - 0x41, 0x0c, 0x42, 0x0a, 0x46, 0x0a, 0x47, 0x09, 0x60, 0x22, 0x62, 0x1e, - 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6b, 0x11, 0x6d, 0x0e, - 0x6e, 0x0d, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x09, 0x76, 0x09, 0x77, 0x07, - 0x79, 0x07, 0x7a, 0x06, 0x7c, 0x06, 0x7c, 0x05, 0x7e, 0x05, 0x7f, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x26, - 0x00, 0x1a, 0x00, 0x0f, 0x04, 0x08, 0x0a, 0x04, 0x10, 0x00, 0x1b, 0x00, - 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, - 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, - 0x00, 0x10, 0x00, 0x03, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, - 0x33, 0x00, 0x3b, 0x00, 0x4f, 0x2a, 0x56, 0x28, 0x59, 0x27, 0x5a, 0x26, - 0x5c, 0x26, 0x5c, 0x26, 0x5c, 0x26, 0x5d, 0x26, 0x5d, 0x26, 0x5d, 0x25, - 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5d, 0x25, 0x5e, 0x25, 0x5e, 0x25, - 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x5e, 0x24, 0x92, 0x0e, 0x79, 0x14, - 0x6f, 0x18, 0x6b, 0x1b, 0x68, 0x1d, 0x67, 0x1d, 0x66, 0x1e, 0x65, 0x1f, - 0x64, 0x20, 0x64, 0x20, 0x63, 0x20, 0x63, 0x21, 0x63, 0x21, 0x63, 0x21, - 0x63, 0x21, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, 0x62, 0x22, - 0x16, 0x23, 0x3c, 0x23, 0x4a, 0x23, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, - 0x58, 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, - 0x5b, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, - 0x5d, 0x23, 0x5d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, - 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, 0x22, 0x00, 0x17, - 0x00, 0x0c, 0x05, 0x07, 0x0a, 0x03, 0x0f, 0x00, 0x1a, 0x00, 0x24, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, - 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, - 0x0b, 0x00, 0x16, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, - 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, - 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, - 0x48, 0x31, 0x4f, 0x2e, 0x53, 0x2c, 0x55, 0x2a, 0x57, 0x29, 0x58, 0x29, - 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0xa1, 0x01, 0x88, 0x07, 0x7b, 0x0c, 0x74, 0x0f, - 0x70, 0x12, 0x6d, 0x14, 0x6b, 0x16, 0x6a, 0x17, 0x69, 0x18, 0x68, 0x19, - 0x67, 0x1a, 0x66, 0x1b, 0x66, 0x1b, 0x65, 0x1c, 0x65, 0x1c, 0x65, 0x1d, - 0x65, 0x1d, 0x64, 0x1d, 0x64, 0x1e, 0x64, 0x1e, 0x0a, 0x32, 0x22, 0x25, - 0x31, 0x24, 0x3b, 0x23, 0x42, 0x23, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, - 0x4f, 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, - 0x56, 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, - 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, 0x14, 0x00, 0x0b, - 0x06, 0x07, 0x0b, 0x03, 0x0f, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, - 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, - 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, - 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, - 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, - 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x00, 0x45, 0x34, 0x4b, 0x31, - 0x4f, 0x2f, 0x52, 0x2d, 0x54, 0x2c, 0x55, 0x2b, 0x57, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, 0x59, 0x28, 0x59, 0x27, 0x5a, 0x27, - 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, - 0xa7, 0x00, 0x92, 0x03, 0x84, 0x06, 0x7d, 0x09, 0x77, 0x0c, 0x73, 0x0e, - 0x71, 0x10, 0x6f, 0x11, 0x6d, 0x13, 0x6b, 0x14, 0x6a, 0x15, 0x6a, 0x16, - 0x69, 0x17, 0x68, 0x17, 0x68, 0x18, 0x67, 0x19, 0x66, 0x19, 0x66, 0x1a, - 0x66, 0x1a, 0x66, 0x1a, 0x0a, 0x38, 0x18, 0x2b, 0x25, 0x27, 0x2e, 0x25, - 0x35, 0x24, 0x3b, 0x23, 0x3f, 0x23, 0x43, 0x23, 0x46, 0x23, 0x48, 0x23, - 0x4a, 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, - 0x52, 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, - 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, 0x40, 0x00, 0x37, - 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x01, 0x0a, 0x06, 0x06, - 0x0b, 0x03, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, - 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, - 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, - 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, - 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, - 0x17, 0x00, 0x1f, 0x00, 0x44, 0x36, 0x49, 0x33, 0x4d, 0x31, 0x4f, 0x2f, - 0x51, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x56, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x29, 0x58, 0x29, - 0x59, 0x28, 0x5a, 0x27, 0x5b, 0x27, 0x5c, 0x27, 0xaa, 0x00, 0x98, 0x01, - 0x8c, 0x03, 0x83, 0x05, 0x7d, 0x08, 0x79, 0x0a, 0x76, 0x0c, 0x73, 0x0d, - 0x71, 0x0f, 0x6f, 0x10, 0x6e, 0x11, 0x6d, 0x12, 0x6c, 0x13, 0x6b, 0x14, - 0x6a, 0x15, 0x6a, 0x15, 0x69, 0x16, 0x68, 0x17, 0x68, 0x17, 0x68, 0x18, - 0x0a, 0x3c, 0x14, 0x30, 0x1e, 0x2a, 0x26, 0x27, 0x2d, 0x25, 0x32, 0x24, - 0x36, 0x24, 0x3b, 0x24, 0x3e, 0x23, 0x41, 0x23, 0x43, 0x23, 0x45, 0x23, - 0x47, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, - 0x4f, 0x23, 0x50, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, - 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, - 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x11, 0x03, 0x23, - 0x00, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, - 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, - 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x43, 0x38, 0x48, 0x35, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x58, 0x29, 0xac, 0x00, 0x9d, 0x00, 0x91, 0x02, 0x89, 0x03, - 0x82, 0x05, 0x7e, 0x07, 0x7a, 0x09, 0x77, 0x0a, 0x75, 0x0c, 0x73, 0x0d, - 0x71, 0x0e, 0x70, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6c, 0x12, 0x6c, 0x13, - 0x6b, 0x13, 0x6a, 0x14, 0x6a, 0x15, 0x6a, 0x15, 0x09, 0x3f, 0x11, 0x33, - 0x1a, 0x2d, 0x21, 0x29, 0x27, 0x27, 0x2c, 0x26, 0x30, 0x25, 0x34, 0x24, - 0x37, 0x24, 0x3a, 0x24, 0x3d, 0x23, 0x40, 0x23, 0x42, 0x23, 0x44, 0x23, - 0x45, 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x23, 0x20, 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, - 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, - 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x00, 0x00, 0x1f, 0x00, 0x3c, - 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, - 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0x39, 0x46, 0x36, - 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2b, 0x55, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, - 0xae, 0x00, 0xa1, 0x00, 0x96, 0x01, 0x8d, 0x02, 0x87, 0x03, 0x82, 0x05, - 0x7e, 0x07, 0x7b, 0x08, 0x78, 0x09, 0x76, 0x0b, 0x74, 0x0c, 0x73, 0x0d, - 0x71, 0x0e, 0x70, 0x0e, 0x6f, 0x0f, 0x6e, 0x11, 0x6d, 0x11, 0x6c, 0x11, - 0x6c, 0x12, 0x6b, 0x13, 0x09, 0x40, 0x10, 0x36, 0x17, 0x2f, 0x1d, 0x2c, - 0x22, 0x29, 0x27, 0x27, 0x2b, 0x26, 0x2f, 0x25, 0x32, 0x25, 0x35, 0x24, - 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x23, 0x40, 0x23, 0x42, 0x23, - 0x44, 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, - 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, - 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, - 0x99, 0x00, 0x9a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x00, 0x0d, 0x00, 0x28, 0x00, 0x39, - 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, - 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x39, 0x45, 0x37, 0x48, 0x35, 0x4a, 0x33, - 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x54, 0x2c, 0x55, 0x2a, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0xaf, 0x00, 0xa3, 0x00, - 0x99, 0x00, 0x91, 0x01, 0x8b, 0x02, 0x86, 0x03, 0x81, 0x05, 0x7e, 0x06, - 0x7b, 0x07, 0x79, 0x09, 0x77, 0x0a, 0x75, 0x0b, 0x74, 0x0c, 0x73, 0x0c, - 0x71, 0x0d, 0x71, 0x0e, 0x6f, 0x0f, 0x6e, 0x10, 0x6e, 0x11, 0x6d, 0x11, - 0x09, 0x41, 0x0f, 0x38, 0x15, 0x32, 0x1a, 0x2e, 0x1f, 0x2b, 0x24, 0x29, - 0x27, 0x27, 0x2b, 0x26, 0x2e, 0x25, 0x31, 0x25, 0x34, 0x25, 0x36, 0x24, - 0x38, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x23, 0x40, 0x23, 0x41, 0x23, - 0x43, 0x23, 0x44, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x1f, 0x00, - 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, - 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, 0x00, - 0x4d, 0x00, 0x1f, 0x00, 0x00, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, - 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, - 0x00, 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3a, 0x45, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x53, 0x2c, 0x55, 0x2b, 0x57, 0x2a, - 0x57, 0x2a, 0x57, 0x2a, 0xb0, 0x00, 0xa5, 0x00, 0x9d, 0x00, 0x95, 0x00, - 0x8e, 0x02, 0x89, 0x02, 0x85, 0x03, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, - 0x7a, 0x08, 0x78, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x73, 0x0c, 0x73, 0x0c, - 0x71, 0x0d, 0x71, 0x0e, 0x70, 0x0e, 0x6e, 0x0f, 0x09, 0x42, 0x0e, 0x39, - 0x13, 0x33, 0x18, 0x2f, 0x1c, 0x2d, 0x21, 0x2a, 0x24, 0x29, 0x28, 0x27, - 0x2b, 0x26, 0x2e, 0x26, 0x30, 0x25, 0x33, 0x25, 0x35, 0x25, 0x37, 0x24, - 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x23, 0x40, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x1f, 0x00, 0x3b, 0x00, - 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, - 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, 0x00, - 0x1f, 0x00, 0x04, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, - 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, - 0x46, 0x36, 0x49, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2c, 0x54, 0x2b, 0x56, 0x2a, - 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, 0x00, 0x91, 0x01, 0x8c, 0x02, - 0x88, 0x03, 0x84, 0x04, 0x81, 0x05, 0x7e, 0x06, 0x7c, 0x07, 0x7a, 0x07, - 0x79, 0x09, 0x77, 0x09, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x73, 0x0c, - 0x71, 0x0d, 0x71, 0x0e, 0x09, 0x43, 0x0e, 0x3b, 0x12, 0x35, 0x16, 0x31, - 0x1a, 0x2e, 0x1e, 0x2c, 0x22, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, - 0x2d, 0x26, 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x37, 0x24, - 0x39, 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, - 0x00, 0x39, 0x00, 0x1a, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, - 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, - 0x82, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, 0x00, - 0x09, 0x00, 0x00, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, - 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x37, 0x48, 0x35, - 0x4a, 0x35, 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0xb1, 0x00, 0xa9, 0x00, - 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, 0x01, 0x8b, 0x02, 0x87, 0x03, - 0x84, 0x04, 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x08, - 0x78, 0x09, 0x76, 0x0a, 0x76, 0x0b, 0x74, 0x0b, 0x73, 0x0c, 0x72, 0x0c, - 0x09, 0x44, 0x0d, 0x3c, 0x11, 0x36, 0x15, 0x33, 0x19, 0x30, 0x1c, 0x2d, - 0x20, 0x2b, 0x23, 0x2a, 0x25, 0x28, 0x28, 0x27, 0x2a, 0x27, 0x2d, 0x26, - 0x2f, 0x26, 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x24, 0x38, 0x24, - 0x39, 0x24, 0x3a, 0x24, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, - 0x00, 0x10, 0x09, 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, - 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, 0x00, - 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, 0x00, - 0x00, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, - 0x00, 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3c, 0x43, 0x39, 0x46, 0x38, 0x46, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, - 0x50, 0x2e, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xaa, 0x00, 0xa2, 0x00, 0x9c, 0x00, - 0x96, 0x00, 0x91, 0x01, 0x8d, 0x02, 0x89, 0x02, 0x86, 0x03, 0x83, 0x04, - 0x81, 0x05, 0x7f, 0x06, 0x7d, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x09, - 0x77, 0x09, 0x76, 0x0a, 0x75, 0x0b, 0x74, 0x0b, 0x0a, 0x44, 0x0d, 0x3d, - 0x10, 0x38, 0x14, 0x34, 0x17, 0x31, 0x1b, 0x2e, 0x1e, 0x2d, 0x21, 0x2b, - 0x23, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2d, 0x26, 0x2e, 0x26, - 0x30, 0x25, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x24, 0x38, 0x24, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, - 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, - 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, 0x00, - 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3c, 0x43, 0x39, - 0x46, 0x39, 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x32, - 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, - 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x98, 0x00, 0x93, 0x00, - 0x8f, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, - 0x7f, 0x06, 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x08, 0x78, 0x09, - 0x76, 0x09, 0x76, 0x0a, 0x0a, 0x44, 0x0c, 0x3e, 0x10, 0x39, 0x13, 0x35, - 0x16, 0x32, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2c, 0x21, 0x2b, 0x24, 0x2a, - 0x26, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x30, 0x25, - 0x31, 0x25, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, - 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, 0x03, 0x0f, 0x00, - 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, - 0x60, 0x00, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, 0x00, - 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x00, 0x00, 0x07, - 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, 0x46, 0x38, - 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x2f, 0x51, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0xb2, 0x00, 0xac, 0x00, - 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x91, 0x01, 0x8e, 0x01, - 0x8a, 0x02, 0x88, 0x02, 0x85, 0x03, 0x83, 0x04, 0x81, 0x05, 0x7f, 0x05, - 0x7d, 0x06, 0x7c, 0x07, 0x7a, 0x07, 0x79, 0x07, 0x78, 0x09, 0x77, 0x09, - 0x0a, 0x44, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x36, 0x15, 0x33, 0x18, 0x30, - 0x1b, 0x2e, 0x1d, 0x2d, 0x20, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x26, 0x28, - 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x26, 0x2e, 0x26, 0x2f, 0x26, 0x31, 0x25, - 0x33, 0x25, 0x33, 0x25, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, - 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, - 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, 0x00, - 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, 0x00, - 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0c, - 0x00, 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x36, 0x49, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, - 0x53, 0x2e, 0x53, 0x2e, 0xb3, 0x00, 0xac, 0x00, 0xa7, 0x00, 0xa1, 0x00, - 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8c, 0x02, 0x89, 0x02, - 0x87, 0x02, 0x85, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7d, 0x06, - 0x7c, 0x06, 0x7b, 0x07, 0x79, 0x07, 0x79, 0x08, 0x0a, 0x45, 0x0c, 0x3f, - 0x0f, 0x3a, 0x11, 0x37, 0x14, 0x34, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, - 0x1e, 0x2d, 0x21, 0x2b, 0x22, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, - 0x2a, 0x27, 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x30, 0x25, 0x32, 0x25, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, - 0x00, 0x1b, 0x00, 0x0b, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, - 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, 0x00, - 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, 0x00, - 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3a, - 0x44, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2e, 0x53, 0x2e, 0x53, 0x2e, - 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, 0x00, 0x9d, 0x00, 0x99, 0x00, - 0x95, 0x00, 0x91, 0x01, 0x8e, 0x01, 0x8b, 0x02, 0x88, 0x02, 0x86, 0x02, - 0x84, 0x03, 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, - 0x7b, 0x07, 0x7a, 0x07, 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3b, 0x11, 0x38, - 0x13, 0x35, 0x16, 0x32, 0x18, 0x30, 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2c, - 0x21, 0x2b, 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, - 0x2c, 0x27, 0x2d, 0x26, 0x2f, 0x26, 0x2f, 0x25, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, - 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, - 0x00, 0x07, 0x06, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, - 0x3d, 0x00, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, 0x00, - 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, 0x00, - 0x14, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3b, 0x44, 0x39, 0x46, 0x39, - 0x46, 0x38, 0x47, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x34, - 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x30, 0x50, 0x2f, 0x52, 0x2e, 0xb3, 0x00, 0xae, 0x00, - 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, 0x00, 0x93, 0x00, - 0x8f, 0x01, 0x8d, 0x02, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x03, 0x84, 0x03, - 0x82, 0x04, 0x81, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x7c, 0x07, - 0x0a, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, 0x36, 0x15, 0x33, - 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2b, 0x22, 0x2b, - 0x23, 0x2a, 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, - 0x2d, 0x26, 0x2f, 0x26, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, - 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, - 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, - 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, 0x00, - 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, 0x00, - 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x42, 0x3c, 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, - 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4c, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x2f, 0xb3, 0x00, 0xae, 0x00, 0xa9, 0x00, 0xa4, 0x00, - 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, 0x00, 0x91, 0x01, 0x8e, 0x01, - 0x8c, 0x02, 0x89, 0x02, 0x87, 0x02, 0x86, 0x03, 0x84, 0x03, 0x82, 0x04, - 0x80, 0x05, 0x7f, 0x05, 0x7e, 0x06, 0x7c, 0x06, 0x0a, 0x45, 0x0b, 0x40, - 0x0e, 0x3c, 0x10, 0x39, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x30, - 0x1b, 0x2e, 0x1d, 0x2d, 0x1f, 0x2d, 0x20, 0x2b, 0x22, 0x2b, 0x24, 0x2a, - 0x25, 0x29, 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x2c, 0x26, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, - 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, 0x00, 0x0a, 0x00, - 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, 0x00, - 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, 0x00, - 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3c, - 0x43, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x37, 0x48, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x33, 0x4d, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, 0x00, 0xa1, 0x00, 0x9d, 0x00, - 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, - 0x89, 0x02, 0x87, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, - 0x7f, 0x05, 0x7e, 0x06, 0x0a, 0x45, 0x0b, 0x41, 0x0e, 0x3d, 0x10, 0x3a, - 0x11, 0x37, 0x14, 0x34, 0x16, 0x32, 0x18, 0x31, 0x1a, 0x30, 0x1c, 0x2e, - 0x1d, 0x2d, 0x20, 0x2c, 0x21, 0x2b, 0x22, 0x2a, 0x24, 0x2a, 0x25, 0x29, - 0x27, 0x28, 0x28, 0x28, 0x2a, 0x27, 0x2c, 0x27, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, - 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, - 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, - 0x1f, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, 0x00, - 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, 0x00, - 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x39, 0x46, 0x39, - 0x46, 0x39, 0x46, 0x39, 0x46, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x35, 0x4a, 0x34, 0x4b, 0x32, 0x4e, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, - 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, 0x00, 0x9a, 0x00, 0x97, 0x00, - 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x02, 0x8a, 0x02, 0x88, 0x02, - 0x86, 0x02, 0x85, 0x03, 0x83, 0x03, 0x82, 0x04, 0x80, 0x05, 0x7f, 0x05, - 0x0a, 0x45, 0x0b, 0x41, 0x0d, 0x3e, 0x10, 0x3a, 0x11, 0x38, 0x13, 0x35, - 0x16, 0x33, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x2e, 0x1d, 0x2e, 0x1e, 0x2d, - 0x20, 0x2b, 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x25, 0x28, 0x28, 0x28, - 0x28, 0x28, 0x2a, 0x27, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, - 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, - 0x00, 0x11, 0x00, 0x06, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, - 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, - 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, 0x00, - 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3a, 0x45, 0x39, 0x46, 0x39, 0x46, 0x39, - 0x46, 0x37, 0x49, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, - 0x4a, 0x34, 0x4c, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, - 0x4f, 0x31, 0x4f, 0x31, 0xb4, 0x00, 0xaf, 0x00, 0xab, 0x00, 0xa7, 0x00, - 0xa3, 0x00, 0x9f, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x95, 0x00, 0x92, 0x00, - 0x90, 0x01, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x88, 0x02, 0x86, 0x02, - 0x85, 0x03, 0x82, 0x03, 0x82, 0x04, 0x80, 0x05, 0x0a, 0x46, 0x0b, 0x42, - 0x0d, 0x3e, 0x0f, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x15, 0x34, 0x16, 0x32, - 0x18, 0x30, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2d, 0x20, 0x2d, 0x20, 0x2b, - 0x22, 0x2b, 0x23, 0x2a, 0x25, 0x2a, 0x26, 0x28, 0x28, 0x28, 0x28, 0x28, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, - 0x60, 0x23, 0x60, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x32, 0x74, 0x38, 0x57, 0x3a, 0x4f, 0x3c, 0x4b, 0x3c, 0x49, 0x3d, 0x47, - 0x3d, 0x46, 0x3d, 0x45, 0x3d, 0x45, 0x3d, 0x44, 0x3d, 0x43, 0x3d, 0x43, - 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x43, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, - 0x3d, 0x42, 0x3d, 0x42, 0x41, 0x09, 0x1b, 0x31, 0x27, 0x38, 0x2d, 0x3a, - 0x31, 0x3b, 0x33, 0x3c, 0x35, 0x3c, 0x36, 0x3d, 0x37, 0x3d, 0x38, 0x3d, - 0x38, 0x3d, 0x39, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, 0x3a, 0x3d, - 0x3b, 0x3d, 0x3b, 0x3d, 0x3b, 0x3d, 0x3c, 0x3d, 0x73, 0x00, 0x38, 0x31, - 0x3a, 0x38, 0x3c, 0x3a, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4f, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x2f, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x3d, 0x28, 0x38, 0x30, - 0x38, 0x34, 0x39, 0x36, 0x39, 0x38, 0x3a, 0x39, 0x3a, 0x39, 0x3b, 0x3a, - 0x3b, 0x3b, 0x3c, 0x3b, 0x3c, 0x3b, 0x3d, 0x3c, 0x3d, 0x3c, 0x3e, 0x3c, - 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3c, 0x3e, 0x3d, 0x3e, 0x3d, 0x3e, 0x3d, - 0x00, 0x7e, 0x05, 0x58, 0x0f, 0x4e, 0x17, 0x49, 0x1d, 0x47, 0x21, 0x45, - 0x24, 0x44, 0x27, 0x43, 0x29, 0x43, 0x2b, 0x43, 0x2d, 0x43, 0x2e, 0x42, - 0x2f, 0x42, 0x31, 0x42, 0x31, 0x42, 0x32, 0x42, 0x33, 0x42, 0x33, 0x42, - 0x34, 0x42, 0x34, 0x42, 0x2b, 0x4e, 0x31, 0x47, 0x34, 0x45, 0x36, 0x44, - 0x38, 0x43, 0x39, 0x42, 0x39, 0x42, 0x3a, 0x41, 0x3b, 0x41, 0x3b, 0x41, - 0x3c, 0x41, 0x3c, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, - 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x3d, 0x41, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x4f, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4c, 0x00, 0x2f, 0x00, - 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x16, 0x3a, 0x20, 0x39, 0x25, 0x39, 0x29, - 0x39, 0x2d, 0x39, 0x2f, 0x3a, 0x31, 0x3a, 0x32, 0x3b, 0x33, 0x3a, 0x34, - 0x3a, 0x35, 0x3a, 0x36, 0x3a, 0x36, 0x3b, 0x37, 0x3b, 0x37, 0x3c, 0x38, - 0x3c, 0x38, 0x3d, 0x38, 0x3e, 0x39, 0x3e, 0x39, 0x00, 0x93, 0x01, 0x70, - 0x06, 0x60, 0x0d, 0x56, 0x12, 0x52, 0x16, 0x4e, 0x1a, 0x4c, 0x1d, 0x4a, - 0x20, 0x48, 0x22, 0x47, 0x24, 0x46, 0x26, 0x45, 0x27, 0x44, 0x29, 0x44, - 0x2a, 0x44, 0x2b, 0x44, 0x2c, 0x43, 0x2d, 0x43, 0x2e, 0x43, 0x2f, 0x43, - 0x28, 0x56, 0x2e, 0x4f, 0x31, 0x4b, 0x33, 0x49, 0x35, 0x48, 0x36, 0x46, - 0x37, 0x45, 0x38, 0x45, 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, - 0x39, 0x42, 0x3a, 0x42, 0x3a, 0x42, 0x3b, 0x42, 0x3c, 0x42, 0x3c, 0x42, - 0x3d, 0x42, 0x3d, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x8f, 0x00, 0x73, 0x00, 0x38, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x56, 0x00, 0x45, 0x00, 0x22, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x43, 0x11, 0x3d, 0x18, 0x3b, 0x1e, 0x3a, 0x22, 0x39, 0x25, 0x39, 0x28, - 0x39, 0x2a, 0x39, 0x2c, 0x39, 0x2d, 0x3a, 0x2f, 0x3b, 0x30, 0x3b, 0x31, - 0x3b, 0x32, 0x3b, 0x33, 0x3a, 0x33, 0x3a, 0x34, 0x3a, 0x34, 0x3a, 0x35, - 0x3a, 0x35, 0x3b, 0x35, 0x00, 0x9d, 0x00, 0x7f, 0x03, 0x6d, 0x07, 0x62, - 0x0c, 0x5b, 0x10, 0x56, 0x13, 0x52, 0x16, 0x50, 0x19, 0x4d, 0x1c, 0x4c, - 0x1e, 0x4b, 0x1f, 0x4b, 0x22, 0x4a, 0x23, 0x49, 0x24, 0x48, 0x25, 0x47, - 0x27, 0x46, 0x28, 0x45, 0x29, 0x44, 0x29, 0x44, 0x27, 0x59, 0x2c, 0x53, - 0x2f, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x48, 0x35, 0x47, - 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, - 0x39, 0x44, 0x39, 0x44, 0x39, 0x43, 0x39, 0x43, 0x39, 0x42, 0x3a, 0x42, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x84, - 0x00, 0x5b, 0x00, 0x2a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x59, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x44, 0x0f, 0x3e, 0x14, - 0x3c, 0x19, 0x3a, 0x1d, 0x3a, 0x20, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x27, - 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2c, 0x3a, 0x2c, 0x3b, 0x2d, 0x3b, 0x2f, - 0x3b, 0x2f, 0x3b, 0x30, 0x3b, 0x31, 0x3b, 0x32, 0x3a, 0x32, 0x3a, 0x33, - 0x00, 0xa3, 0x00, 0x88, 0x01, 0x77, 0x04, 0x6b, 0x08, 0x63, 0x0b, 0x5d, - 0x0e, 0x59, 0x11, 0x57, 0x14, 0x54, 0x16, 0x51, 0x19, 0x4f, 0x1a, 0x4d, - 0x1c, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x21, 0x4b, 0x22, 0x4a, 0x23, 0x49, - 0x24, 0x49, 0x25, 0x49, 0x27, 0x5a, 0x2a, 0x55, 0x2d, 0x52, 0x2f, 0x4f, - 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x49, 0x35, 0x48, - 0x35, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, - 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x45, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x99, 0x00, 0x8d, 0x00, 0x6f, 0x00, 0x48, - 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x55, 0x00, - 0x42, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x0e, 0x40, 0x12, 0x3d, 0x16, 0x3b, 0x1a, - 0x3b, 0x1d, 0x3a, 0x20, 0x39, 0x22, 0x39, 0x23, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2b, 0x3a, 0x2c, 0x3b, 0x2d, - 0x3c, 0x2e, 0x3b, 0x2e, 0x3b, 0x2f, 0x3b, 0x30, 0x00, 0xa7, 0x00, 0x90, - 0x00, 0x7e, 0x02, 0x73, 0x05, 0x6a, 0x08, 0x65, 0x0b, 0x5f, 0x0d, 0x5b, - 0x10, 0x58, 0x12, 0x56, 0x14, 0x55, 0x16, 0x52, 0x18, 0x50, 0x1a, 0x4e, - 0x1b, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x1f, 0x4b, 0x20, 0x4b, 0x21, 0x4b, - 0x26, 0x5b, 0x2a, 0x57, 0x2c, 0x54, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4e, - 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, - 0x35, 0x47, 0x36, 0x46, 0x37, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, - 0x39, 0x46, 0x39, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9b, 0x00, 0x92, 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, - 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x46, 0x0d, 0x41, 0x11, 0x3d, 0x14, 0x3d, 0x17, 0x3b, 0x1a, 0x3b, 0x1c, - 0x3b, 0x1e, 0x39, 0x21, 0x39, 0x22, 0x3a, 0x23, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x39, 0x2b, 0x3b, 0x2c, - 0x3c, 0x2d, 0x3c, 0x2e, 0x00, 0xa9, 0x00, 0x95, 0x00, 0x85, 0x01, 0x79, - 0x03, 0x70, 0x06, 0x69, 0x08, 0x65, 0x0b, 0x61, 0x0d, 0x5c, 0x0f, 0x59, - 0x11, 0x58, 0x13, 0x56, 0x14, 0x55, 0x16, 0x53, 0x18, 0x51, 0x19, 0x4f, - 0x1b, 0x4d, 0x1c, 0x4d, 0x1d, 0x4c, 0x1e, 0x4c, 0x26, 0x5c, 0x29, 0x58, - 0x2b, 0x55, 0x2d, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4d, - 0x32, 0x4b, 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x49, - 0x35, 0x48, 0x35, 0x47, 0x36, 0x46, 0x38, 0x46, 0x39, 0x46, 0x39, 0x46, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x95, - 0x00, 0x84, 0x00, 0x6c, 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0f, - 0x3f, 0x13, 0x3d, 0x15, 0x3c, 0x18, 0x3a, 0x1a, 0x3b, 0x1c, 0x3b, 0x1e, - 0x39, 0x20, 0x39, 0x21, 0x39, 0x22, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x27, 0x39, 0x28, 0x39, 0x29, 0x39, 0x2a, 0x39, 0x2a, 0x3a, 0x2b, - 0x00, 0xab, 0x00, 0x99, 0x00, 0x8b, 0x01, 0x7e, 0x02, 0x77, 0x04, 0x6f, - 0x06, 0x69, 0x08, 0x66, 0x0b, 0x62, 0x0d, 0x5e, 0x0e, 0x5b, 0x10, 0x59, - 0x12, 0x57, 0x13, 0x56, 0x15, 0x55, 0x16, 0x54, 0x18, 0x52, 0x19, 0x50, - 0x1a, 0x4e, 0x1b, 0x4d, 0x26, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2c, 0x53, - 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, - 0x33, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x49, 0x35, 0x48, 0x36, 0x46, 0x37, 0x46, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, - 0x00, 0x5e, 0x00, 0x45, 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, - 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x46, 0x0c, 0x42, 0x0e, 0x40, 0x11, 0x3d, 0x13, - 0x3d, 0x16, 0x3b, 0x18, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1e, 0x3a, 0x1f, - 0x39, 0x21, 0x39, 0x21, 0x3a, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x27, 0x39, 0x27, 0x39, 0x28, 0x39, 0x2a, 0x00, 0xac, 0x00, 0x9c, - 0x00, 0x8f, 0x00, 0x83, 0x02, 0x7b, 0x03, 0x74, 0x05, 0x6d, 0x07, 0x69, - 0x09, 0x66, 0x0b, 0x63, 0x0c, 0x5f, 0x0e, 0x5c, 0x0f, 0x5a, 0x11, 0x58, - 0x12, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x18, 0x53, 0x18, 0x51, - 0x26, 0x5d, 0x27, 0x59, 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, - 0x2f, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, - 0x34, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x49, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, - 0x00, 0x3c, 0x00, 0x26, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, - 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0b, 0x42, 0x0d, 0x41, 0x10, 0x3e, 0x13, 0x3d, 0x15, 0x3c, 0x17, - 0x3a, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x20, - 0x39, 0x21, 0x3a, 0x22, 0x3b, 0x23, 0x3b, 0x24, 0x3a, 0x25, 0x3a, 0x26, - 0x3a, 0x27, 0x39, 0x27, 0x00, 0xad, 0x00, 0x9f, 0x00, 0x92, 0x00, 0x88, - 0x01, 0x7e, 0x02, 0x78, 0x04, 0x72, 0x05, 0x6d, 0x07, 0x69, 0x09, 0x66, - 0x0a, 0x64, 0x0c, 0x61, 0x0d, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, - 0x13, 0x57, 0x14, 0x56, 0x15, 0x55, 0x16, 0x55, 0x26, 0x5d, 0x27, 0x5a, - 0x2a, 0x57, 0x2a, 0x56, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x32, 0x4b, 0x33, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, - 0x00, 0x91, 0x00, 0x83, 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, - 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, - 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, - 0x41, 0x0f, 0x3f, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x19, - 0x3a, 0x1a, 0x3c, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x21, - 0x39, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x3a, 0x24, 0x3a, 0x26, - 0x00, 0xae, 0x00, 0xa2, 0x00, 0x94, 0x00, 0x8b, 0x00, 0x82, 0x02, 0x7b, - 0x03, 0x76, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x69, 0x09, 0x66, 0x0a, 0x64, - 0x0c, 0x62, 0x0d, 0x5f, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x58, 0x12, 0x58, - 0x13, 0x56, 0x14, 0x56, 0x25, 0x5d, 0x27, 0x5b, 0x29, 0x57, 0x2a, 0x57, - 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4d, 0x33, 0x4a, 0x34, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, - 0x00, 0x79, 0x00, 0x68, 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, - 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0b, 0x43, 0x0d, 0x42, 0x0f, 0x40, 0x11, - 0x3d, 0x13, 0x3e, 0x14, 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x19, 0x3b, 0x1a, - 0x3c, 0x1c, 0x3b, 0x1d, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, - 0x3a, 0x21, 0x3b, 0x22, 0x3b, 0x24, 0x3a, 0x24, 0x00, 0xae, 0x00, 0xa3, - 0x00, 0x97, 0x00, 0x8e, 0x00, 0x86, 0x01, 0x7e, 0x02, 0x79, 0x03, 0x75, - 0x05, 0x70, 0x06, 0x6b, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, - 0x0c, 0x5f, 0x0e, 0x5d, 0x0f, 0x5a, 0x10, 0x59, 0x11, 0x58, 0x13, 0x58, - 0x25, 0x5d, 0x27, 0x5c, 0x29, 0x58, 0x2a, 0x57, 0x2a, 0x56, 0x2d, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x33, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, - 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, - 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0b, 0x44, 0x0d, 0x42, 0x0e, 0x41, 0x10, 0x3e, 0x12, 0x3d, 0x13, - 0x3e, 0x15, 0x3c, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x1a, 0x3c, 0x1c, - 0x3b, 0x1c, 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x39, 0x21, - 0x3b, 0x22, 0x3b, 0x23, 0x00, 0xaf, 0x00, 0xa5, 0x00, 0x99, 0x00, 0x90, - 0x00, 0x8a, 0x01, 0x81, 0x02, 0x7c, 0x02, 0x77, 0x04, 0x74, 0x05, 0x6f, - 0x06, 0x6a, 0x07, 0x68, 0x09, 0x66, 0x0a, 0x64, 0x0b, 0x63, 0x0c, 0x61, - 0x0e, 0x5e, 0x0e, 0x5b, 0x10, 0x5a, 0x10, 0x59, 0x25, 0x5d, 0x27, 0x5c, - 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4e, 0x31, 0x4c, 0x33, 0x4a, 0x34, 0x4a, 0x35, 0x4a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x97, 0x00, 0x8e, 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, - 0x00, 0x47, 0x00, 0x37, 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, - 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x44, 0x0d, - 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, - 0x3c, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, - 0x3b, 0x1e, 0x39, 0x1f, 0x39, 0x1f, 0x39, 0x21, 0x39, 0x21, 0x3a, 0x21, - 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9c, 0x00, 0x92, 0x00, 0x8c, 0x00, 0x85, - 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x76, 0x05, 0x73, 0x06, 0x6e, 0x07, 0x6a, - 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x5f, - 0x0e, 0x5c, 0x0f, 0x5a, 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x59, 0x2a, 0x57, - 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x50, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4d, 0x32, 0x4b, 0x34, 0x4a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, - 0x00, 0x86, 0x00, 0x7a, 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, - 0x00, 0x33, 0x00, 0x25, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, - 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, - 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0f, - 0x40, 0x11, 0x3d, 0x12, 0x3d, 0x13, 0x3e, 0x15, 0x3c, 0x16, 0x3b, 0x17, - 0x3a, 0x19, 0x3a, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1e, - 0x3a, 0x1f, 0x39, 0x1f, 0x39, 0x20, 0x39, 0x21, 0x00, 0xb0, 0x00, 0xa7, - 0x00, 0x9e, 0x00, 0x94, 0x00, 0x8e, 0x00, 0x88, 0x01, 0x81, 0x02, 0x7c, - 0x02, 0x78, 0x03, 0x75, 0x05, 0x72, 0x06, 0x6d, 0x07, 0x6a, 0x08, 0x68, - 0x09, 0x66, 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x62, 0x0d, 0x60, 0x0e, 0x5d, - 0x25, 0x5d, 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2c, 0x54, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4e, 0x31, 0x4c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, - 0x00, 0x72, 0x00, 0x66, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, - 0x00, 0x22, 0x00, 0x16, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, - 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, - 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0a, 0x45, 0x0c, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x10, 0x3e, 0x11, - 0x3d, 0x13, 0x3e, 0x14, 0x3d, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, - 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, - 0x39, 0x1f, 0x39, 0x1f, 0x00, 0xb0, 0x00, 0xa7, 0x00, 0xa0, 0x00, 0x96, - 0x00, 0x90, 0x00, 0x8a, 0x00, 0x84, 0x01, 0x7e, 0x02, 0x7a, 0x03, 0x77, - 0x04, 0x74, 0x05, 0x71, 0x06, 0x6c, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, - 0x0a, 0x65, 0x0b, 0x63, 0x0c, 0x63, 0x0c, 0x61, 0x25, 0x5e, 0x27, 0x5c, - 0x27, 0x5c, 0x29, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x55, 0x2d, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x00, 0x27, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x8f, 0x00, 0x96, 0x00, 0x99, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x8c, 0x00, 0x63, - 0x00, 0x7f, 0x00, 0x89, 0x00, 0x90, 0x00, 0x96, 0x00, 0x9b, 0x00, 0x9c, - 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x2c, 0x00, 0x57, 0x00, 0x82, 0x00, 0x91, 0x00, 0x96, 0x00, 0x99, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x11, 0x31, 0x00, 0x5f, - 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, - 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x45, 0x0b, - 0x42, 0x0d, 0x42, 0x0e, 0x41, 0x0f, 0x3f, 0x11, 0x3d, 0x12, 0x3d, 0x13, - 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, 0x3a, 0x19, 0x3b, 0x19, - 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3a, 0x1f, 0x39, 0x1f, - 0x00, 0xb1, 0x00, 0xa8, 0x00, 0xa1, 0x00, 0x98, 0x00, 0x91, 0x00, 0x8c, - 0x00, 0x87, 0x01, 0x80, 0x02, 0x7c, 0x02, 0x79, 0x03, 0x76, 0x05, 0x74, - 0x05, 0x70, 0x06, 0x6b, 0x07, 0x6a, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x65, - 0x0b, 0x64, 0x0c, 0x63, 0x25, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x29, 0x58, - 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x51, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x0d, - 0x00, 0x4f, 0x00, 0x73, 0x00, 0x84, 0x00, 0x8d, 0x00, 0x92, 0x00, 0x95, - 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, - 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x63, 0x00, 0x4d, 0x00, 0x60, 0x00, 0x77, - 0x00, 0x83, 0x00, 0x8c, 0x00, 0x92, 0x00, 0x95, 0x00, 0x98, 0x00, 0x99, - 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x01, 0x04, 0x16, - 0x00, 0x57, 0x00, 0x78, 0x00, 0x87, 0x00, 0x8f, 0x00, 0x94, 0x00, 0x96, - 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, - 0x00, 0x9d, 0x00, 0x9d, 0x23, 0x03, 0x00, 0x20, 0x00, 0x5f, 0x00, 0x7c, - 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9d, - 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, - 0x42, 0x0f, 0x40, 0x11, 0x3d, 0x11, 0x3d, 0x13, 0x3f, 0x13, 0x3d, 0x15, - 0x3c, 0x16, 0x3b, 0x17, 0x3a, 0x18, 0x3a, 0x19, 0x3b, 0x19, 0x3c, 0x1a, - 0x3c, 0x1c, 0x3b, 0x1c, 0x3b, 0x1d, 0x3b, 0x1e, 0x00, 0xb1, 0x00, 0xa9, - 0x00, 0xa2, 0x00, 0x9a, 0x00, 0x93, 0x00, 0x8d, 0x00, 0x89, 0x01, 0x83, - 0x02, 0x7e, 0x02, 0x7a, 0x03, 0x78, 0x03, 0x75, 0x05, 0x73, 0x06, 0x6f, - 0x06, 0x6b, 0x07, 0x69, 0x08, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x0b, 0x64, - 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x28, 0x59, 0x2a, 0x57, 0x2a, 0x57, - 0x2a, 0x57, 0x2b, 0x55, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x38, - 0x00, 0x5b, 0x00, 0x6f, 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, - 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, - 0x00, 0x7f, 0x00, 0x60, 0x00, 0x24, 0x00, 0x48, 0x00, 0x60, 0x00, 0x70, - 0x00, 0x7c, 0x00, 0x84, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x93, - 0x00, 0x95, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x49, 0x00, 0x27, 0x00, 0x01, 0x12, 0x00, 0x43, - 0x00, 0x61, 0x00, 0x74, 0x00, 0x7f, 0x00, 0x87, 0x00, 0x8c, 0x00, 0x90, - 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, - 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, 0x00, 0x68, 0x00, 0x79, - 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, 0x00, 0x94, 0x00, 0x96, - 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, 0x41, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x47, 0x0a, 0x46, 0x0b, 0x43, 0x0d, 0x41, 0x0e, 0x42, 0x0f, 0x40, 0x10, - 0x3e, 0x11, 0x3d, 0x13, 0x3d, 0x13, 0x3f, 0x15, 0x3c, 0x15, 0x3c, 0x17, - 0x3a, 0x17, 0x3a, 0x18, 0x3b, 0x19, 0x3b, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, - 0x3b, 0x1c, 0x3b, 0x1c, 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa3, 0x00, 0x9c, - 0x00, 0x94, 0x00, 0x8f, 0x00, 0x8b, 0x00, 0x86, 0x01, 0x80, 0x02, 0x7c, - 0x02, 0x79, 0x03, 0x77, 0x04, 0x75, 0x05, 0x72, 0x06, 0x6e, 0x07, 0x6b, - 0x07, 0x69, 0x09, 0x68, 0x09, 0x66, 0x0a, 0x66, 0x24, 0x5e, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5a, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2c, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x50, 0x30, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x2a, 0x00, 0x48, - 0x00, 0x5d, 0x00, 0x6c, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, - 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, 0x00, 0x92, 0x00, 0x89, 0x00, 0x77, - 0x00, 0x48, 0x00, 0x0f, 0x00, 0x30, 0x00, 0x48, 0x00, 0x5d, 0x00, 0x6c, - 0x00, 0x76, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, - 0x00, 0x90, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x54, 0x00, 0x41, 0x00, 0x17, 0x00, 0x00, 0x10, 0x00, 0x36, 0x00, 0x51, - 0x00, 0x64, 0x00, 0x71, 0x00, 0x7a, 0x00, 0x81, 0x00, 0x86, 0x00, 0x8a, - 0x00, 0x8d, 0x00, 0x90, 0x00, 0x92, 0x00, 0x93, 0x52, 0x00, 0x3c, 0x00, - 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, 0x00, 0x6a, 0x00, 0x76, - 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, 0x00, 0x8f, 0x00, 0x91, - 0x00, 0x93, 0x00, 0x95, 0x41, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, 0x42, 0x3d, 0x42, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x41, 0x3d, - 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x47, 0x0a, 0x46, 0x0b, - 0x44, 0x0d, 0x42, 0x0d, 0x42, 0x0e, 0x40, 0x0f, 0x40, 0x11, 0x3d, 0x11, - 0x3d, 0x13, 0x3f, 0x13, 0x3e, 0x15, 0x3c, 0x15, 0x3c, 0x17, 0x3a, 0x17, - 0x3a, 0x18, 0x3b, 0x19, 0x3c, 0x19, 0x3c, 0x1a, 0x3c, 0x1c, 0x3b, 0x1c, - 0x00, 0xb2, 0x00, 0xaa, 0x00, 0xa4, 0x00, 0x9e, 0x00, 0x95, 0x00, 0x90, - 0x00, 0x8c, 0x00, 0x88, 0x01, 0x82, 0x02, 0x7e, 0x02, 0x7b, 0x02, 0x78, - 0x03, 0x76, 0x05, 0x74, 0x05, 0x71, 0x06, 0x6d, 0x07, 0x6b, 0x07, 0x69, - 0x09, 0x68, 0x09, 0x66, 0x24, 0x5e, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5b, - 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2b, 0x54, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x52, 0x2f, 0x50, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, - 0x00, 0x5e, 0x00, 0x69, 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, - 0x00, 0x86, 0x00, 0x89, 0x00, 0x90, 0x00, 0x83, 0x00, 0x60, 0x00, 0x30, - 0x00, 0x02, 0x00, 0x22, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x5e, 0x00, 0x69, - 0x00, 0x72, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x83, 0x00, 0x86, 0x00, 0x89, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x59, 0x00, 0x4d, 0x00, - 0x2f, 0x00, 0x0e, 0x00, 0x00, 0x10, 0x00, 0x2e, 0x00, 0x46, 0x00, 0x57, - 0x00, 0x64, 0x00, 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x81, 0x00, 0x85, - 0x00, 0x89, 0x00, 0x8b, 0x58, 0x00, 0x4a, 0x00, 0x28, 0x00, 0x02, 0x00, - 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x74, - 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8d, - 0x1b, 0x85, 0x05, 0x96, 0x01, 0x9e, 0x00, 0xa4, 0x00, 0xa7, 0x00, 0xa9, - 0x00, 0xab, 0x00, 0xac, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, - 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, - 0x00, 0xb2, 0x00, 0xb2, 0x1c, 0x6e, 0x03, 0x8a, 0x00, 0x97, 0x00, 0x9e, - 0x00, 0xa2, 0x00, 0xa6, 0x00, 0xa8, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xac, - 0x00, 0xad, 0x00, 0xad, 0x00, 0xad, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, - 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x0e, 0x92, 0x01, 0xa1, 0x00, 0xa7, 0x00, 0xaa, 0x00, 0xac, 0x00, 0xae, - 0x00, 0xaf, 0x00, 0xb0, 0x00, 0xb1, 0x00, 0xb1, 0x00, 0xb2, 0x00, 0xb2, - 0x00, 0xb2, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, 0x00, 0xb3, - 0x00, 0xb4, 0x00, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, - 0x00, 0x5e, 0x00, 0x68, 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, - 0x00, 0x96, 0x00, 0x8c, 0x00, 0x70, 0x00, 0x48, 0x00, 0x22, 0x00, 0x01, - 0x00, 0x1c, 0x00, 0x33, 0x00, 0x45, 0x00, 0x53, 0x00, 0x5e, 0x00, 0x68, - 0x00, 0x6f, 0x00, 0x75, 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x3e, 0x00, 0x23, 0x00, - 0x0a, 0x02, 0x00, 0x10, 0x00, 0x29, 0x00, 0x3d, 0x00, 0x4e, 0x00, 0x5b, - 0x00, 0x65, 0x00, 0x6d, 0x00, 0x74, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, - 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, 0x00, 0x04, 0x00, 0x1f, - 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, 0x00, 0x6b, 0x00, 0x73, - 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, 0x27, 0x60, 0x0f, 0x73, - 0x07, 0x81, 0x03, 0x8a, 0x01, 0x91, 0x00, 0x95, 0x00, 0x99, 0x00, 0x9d, - 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa7, - 0x00, 0xa7, 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, - 0x29, 0x3a, 0x0f, 0x59, 0x06, 0x6c, 0x02, 0x79, 0x00, 0x84, 0x00, 0x8b, - 0x00, 0x90, 0x00, 0x94, 0x00, 0x98, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9f, - 0x00, 0xa0, 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa5, 0x00, 0xa6, 0x00, 0xa6, - 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x14, 0x79, 0x07, 0x88, - 0x03, 0x92, 0x01, 0x98, 0x00, 0x9d, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa5, - 0x00, 0xa8, 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xab, 0x00, 0xac, 0x00, 0xac, - 0x00, 0xad, 0x00, 0xae, 0x00, 0xae, 0x00, 0xae, 0x00, 0xaf, 0x00, 0xaf, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x18, 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, - 0x00, 0x5f, 0x00, 0x66, 0x00, 0x6d, 0x00, 0x72, 0x00, 0x9b, 0x00, 0x92, - 0x00, 0x7c, 0x00, 0x5d, 0x00, 0x3c, 0x00, 0x1c, 0x00, 0x01, 0x00, 0x18, - 0x00, 0x2c, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x66, - 0x00, 0x6d, 0x00, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5c, 0x00, 0x56, 0x00, 0x47, 0x00, 0x31, 0x00, 0x1a, 0x00, 0x08, 0x04, - 0x00, 0x10, 0x00, 0x25, 0x00, 0x37, 0x00, 0x46, 0x00, 0x52, 0x00, 0x5d, - 0x00, 0x65, 0x00, 0x6c, 0x00, 0x72, 0x00, 0x77, 0x5c, 0x00, 0x55, 0x00, - 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x33, - 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, 0x00, 0x6b, 0x00, 0x72, - 0x00, 0x77, 0x00, 0x7b, 0x2d, 0x54, 0x17, 0x63, 0x0d, 0x6f, 0x07, 0x78, - 0x04, 0x80, 0x02, 0x86, 0x01, 0x8b, 0x01, 0x8f, 0x00, 0x92, 0x00, 0x95, - 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, - 0x00, 0xa2, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa4, 0x31, 0x27, 0x18, 0x40, - 0x0d, 0x52, 0x07, 0x61, 0x04, 0x6c, 0x02, 0x75, 0x01, 0x7c, 0x00, 0x82, - 0x00, 0x87, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x91, 0x00, 0x94, 0x00, 0x97, - 0x00, 0x98, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x18, 0x6f, 0x0c, 0x7b, 0x06, 0x84, 0x03, 0x8c, - 0x02, 0x91, 0x01, 0x96, 0x00, 0x99, 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa1, - 0x00, 0xa2, 0x00, 0xa4, 0x00, 0xa5, 0x00, 0xa7, 0x00, 0xa7, 0x00, 0xa8, - 0x00, 0xa9, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0xab, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x14, 0x00, 0x26, 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, - 0x00, 0x5f, 0x00, 0x66, 0x00, 0x9c, 0x00, 0x95, 0x00, 0x84, 0x00, 0x6c, - 0x00, 0x4f, 0x00, 0x32, 0x00, 0x18, 0x00, 0x00, 0x00, 0x14, 0x00, 0x26, - 0x00, 0x36, 0x00, 0x43, 0x00, 0x4e, 0x00, 0x57, 0x00, 0x5f, 0x00, 0x66, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, - 0x4d, 0x00, 0x3b, 0x00, 0x27, 0x00, 0x13, 0x00, 0x07, 0x06, 0x00, 0x10, - 0x00, 0x22, 0x00, 0x32, 0x00, 0x40, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5e, - 0x00, 0x65, 0x00, 0x6b, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x36, 0x00, - 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x30, 0x00, 0x3f, - 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, 0x00, 0x6c, 0x00, 0x71, - 0x30, 0x4e, 0x1d, 0x5a, 0x12, 0x64, 0x0c, 0x6d, 0x08, 0x75, 0x05, 0x7a, - 0x03, 0x7f, 0x02, 0x85, 0x02, 0x89, 0x01, 0x8b, 0x00, 0x8e, 0x00, 0x90, - 0x00, 0x92, 0x00, 0x94, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9b, 0x00, 0x9c, - 0x00, 0x9e, 0x00, 0x9f, 0x36, 0x1e, 0x1f, 0x32, 0x12, 0x43, 0x0b, 0x50, - 0x07, 0x5a, 0x05, 0x63, 0x02, 0x6b, 0x01, 0x72, 0x01, 0x78, 0x00, 0x7c, - 0x00, 0x81, 0x00, 0x85, 0x00, 0x88, 0x00, 0x8b, 0x00, 0x8e, 0x00, 0x90, - 0x00, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x1b, 0x6b, 0x0f, 0x74, 0x09, 0x7d, 0x05, 0x83, 0x03, 0x89, 0x02, 0x8d, - 0x01, 0x91, 0x00, 0x95, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9c, 0x00, 0x9e, - 0x00, 0x9f, 0x00, 0xa1, 0x00, 0xa3, 0x00, 0xa3, 0x00, 0xa4, 0x00, 0xa5, - 0x00, 0xa6, 0x00, 0xa7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, - 0x00, 0x22, 0x00, 0x30, 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, - 0x00, 0x9c, 0x00, 0x98, 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5e, 0x00, 0x45, - 0x00, 0x2c, 0x00, 0x14, 0x00, 0x00, 0x00, 0x12, 0x00, 0x22, 0x00, 0x30, - 0x00, 0x3d, 0x00, 0x47, 0x00, 0x50, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x42, 0x00, - 0x32, 0x00, 0x20, 0x00, 0x0e, 0x00, 0x06, 0x07, 0x00, 0x10, 0x00, 0x20, - 0x00, 0x2f, 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x58, 0x00, 0x5f, - 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x17, 0x00, - 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, 0x00, 0x3b, 0x00, 0x46, - 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, 0x33, 0x4b, 0x21, 0x54, - 0x16, 0x5d, 0x10, 0x65, 0x0b, 0x6c, 0x08, 0x72, 0x06, 0x77, 0x04, 0x7b, - 0x03, 0x7f, 0x02, 0x83, 0x02, 0x87, 0x01, 0x8a, 0x01, 0x8c, 0x00, 0x8e, - 0x00, 0x90, 0x00, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x98, - 0x3a, 0x19, 0x24, 0x2a, 0x18, 0x38, 0x10, 0x44, 0x0b, 0x4e, 0x07, 0x57, - 0x05, 0x5f, 0x04, 0x65, 0x02, 0x6b, 0x01, 0x71, 0x01, 0x76, 0x00, 0x79, - 0x00, 0x7e, 0x00, 0x80, 0x00, 0x84, 0x00, 0x87, 0x00, 0x88, 0x00, 0x8b, - 0x00, 0x8d, 0x00, 0x8f, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x68, 0x12, 0x70, - 0x0c, 0x77, 0x08, 0x7d, 0x05, 0x82, 0x03, 0x87, 0x02, 0x8b, 0x02, 0x8e, - 0x01, 0x91, 0x00, 0x94, 0x00, 0x96, 0x00, 0x98, 0x00, 0x9a, 0x00, 0x9b, - 0x00, 0x9d, 0x00, 0x9f, 0x00, 0xa0, 0x00, 0xa1, 0x00, 0xa2, 0x00, 0xa3, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0x2c, 0x00, 0x37, 0x00, 0x41, 0x00, 0x4a, 0x00, 0x9d, 0x00, 0x99, - 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x69, 0x00, 0x53, 0x00, 0x3c, 0x00, 0x26, - 0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x2c, 0x00, 0x37, - 0x00, 0x41, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5b, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3a, 0x00, 0x2a, 0x00, - 0x1a, 0x00, 0x0b, 0x00, 0x05, 0x08, 0x00, 0x10, 0x00, 0x1e, 0x00, 0x2c, - 0x00, 0x37, 0x00, 0x42, 0x00, 0x4b, 0x00, 0x53, 0x5e, 0x00, 0x5b, 0x00, - 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, - 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, 0x00, 0x43, 0x00, 0x4c, - 0x00, 0x54, 0x00, 0x5b, 0x34, 0x49, 0x25, 0x51, 0x1a, 0x58, 0x13, 0x60, - 0x0e, 0x65, 0x0b, 0x6b, 0x08, 0x71, 0x06, 0x75, 0x05, 0x78, 0x04, 0x7b, - 0x03, 0x7f, 0x02, 0x83, 0x02, 0x86, 0x01, 0x88, 0x01, 0x8a, 0x00, 0x8c, - 0x00, 0x8d, 0x00, 0x8f, 0x00, 0x90, 0x00, 0x92, 0x3b, 0x17, 0x28, 0x24, - 0x1c, 0x30, 0x14, 0x3b, 0x0f, 0x45, 0x0b, 0x4d, 0x07, 0x55, 0x05, 0x5b, - 0x04, 0x61, 0x03, 0x67, 0x02, 0x6b, 0x01, 0x6f, 0x01, 0x74, 0x00, 0x77, - 0x00, 0x7a, 0x00, 0x7e, 0x00, 0x80, 0x00, 0x83, 0x00, 0x86, 0x00, 0x87, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x1d, 0x67, 0x14, 0x6d, 0x0e, 0x73, 0x0a, 0x79, - 0x07, 0x7e, 0x05, 0x82, 0x03, 0x86, 0x02, 0x89, 0x02, 0x8c, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x96, 0x00, 0x97, 0x00, 0x99, 0x00, 0x9a, - 0x00, 0x9b, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, - 0x00, 0x33, 0x00, 0x3d, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x83, - 0x00, 0x72, 0x00, 0x5e, 0x00, 0x4a, 0x00, 0x36, 0x00, 0x22, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x1c, 0x00, 0x28, 0x00, 0x33, 0x00, 0x3d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, - 0x56, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x32, 0x00, 0x24, 0x00, 0x15, 0x00, - 0x0a, 0x02, 0x04, 0x09, 0x00, 0x10, 0x00, 0x1d, 0x00, 0x29, 0x00, 0x34, - 0x00, 0x3e, 0x00, 0x46, 0x5e, 0x00, 0x5b, 0x00, 0x54, 0x00, 0x49, 0x00, - 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, 0x00, 0x04, 0x00, 0x12, - 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, 0x00, 0x49, 0x00, 0x50, - 0x36, 0x47, 0x27, 0x4e, 0x1d, 0x54, 0x16, 0x5b, 0x11, 0x61, 0x0e, 0x65, - 0x0b, 0x6a, 0x08, 0x6f, 0x07, 0x73, 0x05, 0x76, 0x05, 0x79, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x82, 0x02, 0x85, 0x01, 0x88, 0x01, 0x89, 0x01, 0x8b, - 0x00, 0x8c, 0x00, 0x8d, 0x3d, 0x14, 0x2c, 0x20, 0x20, 0x2a, 0x18, 0x34, - 0x12, 0x3e, 0x0e, 0x45, 0x0a, 0x4c, 0x07, 0x53, 0x06, 0x58, 0x05, 0x5e, - 0x04, 0x63, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x72, 0x00, 0x76, - 0x00, 0x78, 0x00, 0x7c, 0x00, 0x7e, 0x00, 0x80, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x1e, 0x66, 0x16, 0x6b, 0x10, 0x71, 0x0c, 0x76, 0x09, 0x7a, 0x07, 0x7e, - 0x05, 0x81, 0x03, 0x85, 0x03, 0x88, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x96, 0x00, 0x98, 0x00, 0x99, - 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, - 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x93, 0x00, 0x88, 0x00, 0x79, 0x00, 0x68, - 0x00, 0x55, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x0d, 0x00, 0x1a, 0x00, 0x25, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, - 0x44, 0x00, 0x38, 0x00, 0x2c, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x09, 0x03, - 0x04, 0x09, 0x00, 0x10, 0x00, 0x1c, 0x00, 0x27, 0x00, 0x31, 0x00, 0x3a, - 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x33, 0x00, - 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x1f, - 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, 0x37, 0x46, 0x29, 0x4c, - 0x20, 0x52, 0x19, 0x57, 0x14, 0x5d, 0x10, 0x62, 0x0d, 0x66, 0x0b, 0x69, - 0x09, 0x6e, 0x07, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x7a, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x82, 0x02, 0x84, 0x02, 0x87, 0x01, 0x88, 0x01, 0x89, - 0x3f, 0x13, 0x2f, 0x1c, 0x23, 0x27, 0x1b, 0x2f, 0x15, 0x38, 0x10, 0x3e, - 0x0d, 0x45, 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x57, 0x05, 0x5c, 0x04, 0x60, - 0x03, 0x65, 0x02, 0x67, 0x02, 0x6c, 0x01, 0x6f, 0x01, 0x71, 0x00, 0x75, - 0x00, 0x77, 0x00, 0x79, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x1f, 0x65, 0x17, 0x6a, - 0x11, 0x6f, 0x0d, 0x73, 0x0a, 0x77, 0x08, 0x7b, 0x06, 0x7e, 0x05, 0x81, - 0x04, 0x84, 0x03, 0x87, 0x02, 0x89, 0x02, 0x8b, 0x01, 0x8e, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0c, 0x00, 0x18, 0x00, 0x22, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x95, 0x00, 0x8b, 0x00, 0x7e, 0x00, 0x6f, 0x00, 0x5f, 0x00, 0x4e, - 0x00, 0x3d, 0x00, 0x2c, 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x0c, - 0x00, 0x18, 0x00, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, - 0x32, 0x00, 0x26, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x08, 0x04, 0x04, 0x0a, - 0x00, 0x10, 0x00, 0x1b, 0x00, 0x25, 0x00, 0x2f, 0x5e, 0x00, 0x5d, 0x00, - 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, 0x2c, 0x00, 0x1e, 0x00, - 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x2a, - 0x00, 0x33, 0x00, 0x3b, 0x38, 0x45, 0x2b, 0x4a, 0x22, 0x50, 0x1c, 0x55, - 0x16, 0x59, 0x12, 0x5f, 0x0f, 0x63, 0x0d, 0x66, 0x0b, 0x69, 0x09, 0x6e, - 0x07, 0x71, 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x7a, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x81, 0x02, 0x84, 0x02, 0x86, 0x40, 0x11, 0x31, 0x1b, - 0x26, 0x23, 0x1e, 0x2b, 0x18, 0x33, 0x13, 0x39, 0x0f, 0x40, 0x0c, 0x45, - 0x0a, 0x4b, 0x08, 0x51, 0x07, 0x56, 0x05, 0x59, 0x04, 0x5e, 0x04, 0x61, - 0x02, 0x65, 0x02, 0x68, 0x02, 0x6b, 0x01, 0x6e, 0x01, 0x71, 0x00, 0x73, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x64, 0x18, 0x69, 0x13, 0x6d, 0x0f, 0x71, - 0x0c, 0x75, 0x09, 0x78, 0x07, 0x7b, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x84, - 0x03, 0x86, 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8e, 0x01, 0x8f, - 0x01, 0x91, 0x00, 0x93, 0x00, 0x94, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0b, 0x00, 0x16, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8e, - 0x00, 0x83, 0x00, 0x75, 0x00, 0x66, 0x00, 0x57, 0x00, 0x47, 0x00, 0x37, - 0x00, 0x28, 0x00, 0x1a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x16, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, 0x38, 0x00, 0x2d, 0x00, - 0x22, 0x00, 0x17, 0x00, 0x0c, 0x00, 0x07, 0x05, 0x03, 0x0a, 0x00, 0x0f, - 0x00, 0x1a, 0x00, 0x24, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x52, 0x00, - 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, 0x19, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x29, 0x00, 0x31, - 0x38, 0x45, 0x2d, 0x49, 0x24, 0x4e, 0x1e, 0x53, 0x19, 0x56, 0x14, 0x5b, - 0x11, 0x60, 0x0e, 0x63, 0x0c, 0x66, 0x0a, 0x69, 0x09, 0x6c, 0x07, 0x70, - 0x06, 0x73, 0x06, 0x75, 0x05, 0x77, 0x04, 0x79, 0x03, 0x7a, 0x03, 0x7c, - 0x02, 0x7e, 0x02, 0x80, 0x40, 0x11, 0x32, 0x18, 0x28, 0x20, 0x20, 0x28, - 0x1a, 0x2f, 0x16, 0x35, 0x12, 0x3c, 0x0f, 0x41, 0x0c, 0x46, 0x0a, 0x4b, - 0x08, 0x50, 0x07, 0x54, 0x05, 0x58, 0x05, 0x5c, 0x04, 0x5f, 0x04, 0x62, - 0x02, 0x66, 0x02, 0x69, 0x01, 0x6b, 0x01, 0x6e, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x20, 0x64, 0x19, 0x68, 0x14, 0x6b, 0x10, 0x6f, 0x0d, 0x73, 0x0b, 0x76, - 0x09, 0x79, 0x07, 0x7c, 0x06, 0x7e, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, - 0x02, 0x88, 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8d, 0x01, 0x8e, 0x01, 0x90, - 0x00, 0x91, 0x00, 0x92, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0a, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x98, 0x00, 0x90, 0x00, 0x86, 0x00, 0x7a, - 0x00, 0x6d, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x41, 0x00, 0x33, 0x00, 0x25, - 0x00, 0x18, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, - 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x28, 0x00, 0x1e, 0x00, - 0x14, 0x00, 0x0b, 0x00, 0x07, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x00, 0x19, - 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, 0x4b, 0x00, 0x42, 0x00, - 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x01, - 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, 0x39, 0x44, 0x2e, 0x49, - 0x26, 0x4c, 0x1f, 0x52, 0x1a, 0x55, 0x16, 0x58, 0x13, 0x5d, 0x10, 0x61, - 0x0e, 0x64, 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6c, 0x07, 0x70, 0x07, 0x72, - 0x06, 0x74, 0x05, 0x76, 0x05, 0x78, 0x03, 0x79, 0x03, 0x7b, 0x02, 0x7c, - 0x41, 0x10, 0x34, 0x17, 0x2a, 0x1e, 0x23, 0x25, 0x1d, 0x2c, 0x18, 0x32, - 0x14, 0x37, 0x10, 0x3d, 0x0e, 0x42, 0x0c, 0x46, 0x0a, 0x4b, 0x08, 0x4f, - 0x07, 0x53, 0x05, 0x57, 0x05, 0x5a, 0x04, 0x5e, 0x04, 0x61, 0x03, 0x63, - 0x02, 0x66, 0x02, 0x69, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x20, 0x63, 0x1a, 0x67, - 0x15, 0x6a, 0x11, 0x6e, 0x0e, 0x71, 0x0c, 0x74, 0x0a, 0x77, 0x08, 0x7a, - 0x07, 0x7c, 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x87, - 0x02, 0x88, 0x02, 0x8a, 0x02, 0x8c, 0x01, 0x8d, 0x01, 0x8e, 0x01, 0x90, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, - 0x00, 0x99, 0x00, 0x92, 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x66, - 0x00, 0x58, 0x00, 0x4a, 0x00, 0x3d, 0x00, 0x2f, 0x00, 0x22, 0x00, 0x16, - 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x48, 0x00, - 0x40, 0x00, 0x37, 0x00, 0x2d, 0x00, 0x24, 0x00, 0x1a, 0x00, 0x11, 0x00, - 0x0a, 0x01, 0x06, 0x06, 0x03, 0x0b, 0x00, 0x0f, 0x5f, 0x00, 0x5d, 0x00, - 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, 0x3c, 0x00, 0x31, 0x00, - 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0d, - 0x00, 0x17, 0x00, 0x1f, 0x39, 0x43, 0x2f, 0x48, 0x27, 0x4b, 0x22, 0x4f, - 0x1c, 0x54, 0x18, 0x56, 0x14, 0x59, 0x12, 0x5e, 0x0f, 0x62, 0x0d, 0x64, - 0x0c, 0x66, 0x0a, 0x68, 0x09, 0x6b, 0x08, 0x6f, 0x07, 0x72, 0x06, 0x74, - 0x05, 0x75, 0x05, 0x77, 0x04, 0x78, 0x03, 0x7a, 0x42, 0x0f, 0x36, 0x16, - 0x2c, 0x1c, 0x25, 0x23, 0x1f, 0x29, 0x1a, 0x2e, 0x16, 0x34, 0x12, 0x39, - 0x0f, 0x3e, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4f, 0x07, 0x52, - 0x05, 0x56, 0x05, 0x59, 0x04, 0x5c, 0x04, 0x5f, 0x04, 0x62, 0x02, 0x64, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1b, 0x66, 0x16, 0x6a, 0x12, 0x6d, - 0x0f, 0x70, 0x0d, 0x73, 0x0b, 0x75, 0x09, 0x78, 0x07, 0x7a, 0x06, 0x7d, - 0x06, 0x7f, 0x05, 0x81, 0x04, 0x83, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, - 0x02, 0x89, 0x02, 0x8b, 0x02, 0x8c, 0x01, 0x8d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3a, 0x43, 0x31, 0x48, 0x29, 0x4a, 0x23, 0x4d, 0x1e, 0x53, 0x1a, 0x55, - 0x16, 0x57, 0x13, 0x5b, 0x11, 0x60, 0x0f, 0x62, 0x0d, 0x64, 0x0b, 0x66, - 0x0a, 0x68, 0x09, 0x6a, 0x08, 0x6e, 0x07, 0x71, 0x06, 0x73, 0x06, 0x75, - 0x05, 0x76, 0x05, 0x78, 0x43, 0x0f, 0x37, 0x15, 0x2e, 0x1b, 0x27, 0x21, - 0x21, 0x26, 0x1c, 0x2c, 0x18, 0x31, 0x15, 0x36, 0x12, 0x3a, 0x0f, 0x3f, - 0x0c, 0x43, 0x0c, 0x47, 0x0a, 0x4b, 0x08, 0x4e, 0x07, 0x52, 0x06, 0x55, - 0x05, 0x58, 0x05, 0x5b, 0x04, 0x5d, 0x04, 0x60, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x21, 0x63, 0x1b, 0x66, 0x17, 0x69, 0x13, 0x6c, 0x10, 0x6e, 0x0e, 0x71, - 0x0c, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x06, 0x7f, - 0x05, 0x81, 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x89, - 0x02, 0x8a, 0x02, 0x8b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x31, 0x47, - 0x2a, 0x49, 0x24, 0x4c, 0x1f, 0x51, 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, - 0x12, 0x5c, 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0b, 0x66, 0x0a, 0x68, - 0x09, 0x6a, 0x08, 0x6d, 0x07, 0x71, 0x06, 0x72, 0x06, 0x74, 0x05, 0x75, - 0x43, 0x0f, 0x38, 0x14, 0x2f, 0x19, 0x28, 0x1f, 0x22, 0x24, 0x1d, 0x29, - 0x19, 0x2e, 0x16, 0x33, 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x40, 0x0c, 0x43, - 0x0b, 0x47, 0x0a, 0x4b, 0x09, 0x4e, 0x07, 0x51, 0x07, 0x55, 0x05, 0x56, - 0x05, 0x5a, 0x04, 0x5c, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, - 0x17, 0x68, 0x14, 0x6b, 0x11, 0x6e, 0x0e, 0x70, 0x0c, 0x73, 0x0b, 0x75, - 0x09, 0x77, 0x08, 0x79, 0x07, 0x7b, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, - 0x04, 0x82, 0x03, 0x84, 0x03, 0x86, 0x02, 0x87, 0x02, 0x88, 0x02, 0x89, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3a, 0x42, 0x32, 0x46, 0x2b, 0x49, 0x25, 0x4b, - 0x21, 0x4f, 0x1d, 0x53, 0x19, 0x55, 0x16, 0x57, 0x14, 0x59, 0x11, 0x5e, - 0x10, 0x61, 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x6a, - 0x08, 0x6c, 0x07, 0x70, 0x07, 0x72, 0x06, 0x73, 0x43, 0x0e, 0x39, 0x13, - 0x31, 0x18, 0x2a, 0x1d, 0x24, 0x22, 0x1f, 0x27, 0x1b, 0x2b, 0x18, 0x30, - 0x16, 0x35, 0x12, 0x39, 0x0f, 0x3c, 0x0f, 0x41, 0x0c, 0x44, 0x0b, 0x47, - 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x50, 0x07, 0x54, 0x05, 0x55, 0x05, 0x59, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x21, 0x63, 0x1c, 0x65, 0x18, 0x68, 0x15, 0x6a, - 0x12, 0x6c, 0x0f, 0x6f, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x78, - 0x07, 0x79, 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, - 0x03, 0x84, 0x03, 0x85, 0x02, 0x86, 0x02, 0x88, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x42, 0x33, 0x45, 0x2c, 0x49, 0x27, 0x4b, 0x22, 0x4d, 0x1e, 0x52, - 0x1b, 0x54, 0x18, 0x56, 0x15, 0x58, 0x13, 0x5b, 0x10, 0x5f, 0x0f, 0x61, - 0x0e, 0x63, 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x08, 0x6c, - 0x07, 0x6f, 0x07, 0x72, 0x44, 0x0e, 0x3a, 0x12, 0x32, 0x17, 0x2b, 0x1c, - 0x26, 0x21, 0x21, 0x26, 0x1d, 0x2a, 0x19, 0x2e, 0x16, 0x32, 0x14, 0x35, - 0x12, 0x3a, 0x0f, 0x3d, 0x0e, 0x41, 0x0c, 0x44, 0x0b, 0x47, 0x0a, 0x4a, - 0x09, 0x4e, 0x07, 0x4f, 0x07, 0x53, 0x05, 0x55, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x22, 0x62, 0x1d, 0x65, 0x19, 0x67, 0x15, 0x6a, 0x13, 0x6c, 0x10, 0x6e, - 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x79, 0x07, 0x7a, - 0x07, 0x7c, 0x06, 0x7d, 0x05, 0x7f, 0x05, 0x81, 0x04, 0x82, 0x03, 0x83, - 0x03, 0x85, 0x02, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x33, 0x44, - 0x2d, 0x48, 0x28, 0x4a, 0x23, 0x4c, 0x1f, 0x50, 0x1c, 0x53, 0x19, 0x55, - 0x16, 0x57, 0x14, 0x58, 0x12, 0x5c, 0x10, 0x60, 0x0e, 0x62, 0x0d, 0x63, - 0x0c, 0x65, 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x07, 0x6e, - 0x44, 0x0d, 0x3a, 0x12, 0x33, 0x16, 0x2c, 0x1b, 0x27, 0x1f, 0x21, 0x24, - 0x1e, 0x28, 0x1a, 0x2b, 0x18, 0x30, 0x16, 0x34, 0x12, 0x37, 0x11, 0x3b, - 0x0f, 0x3e, 0x0d, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, - 0x07, 0x4f, 0x07, 0x53, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x65, - 0x19, 0x66, 0x16, 0x69, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6f, 0x0d, 0x71, - 0x0c, 0x73, 0x0b, 0x76, 0x09, 0x77, 0x08, 0x79, 0x07, 0x7a, 0x06, 0x7c, - 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x83, 0x03, 0x85, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x34, 0x44, 0x2e, 0x48, 0x29, 0x49, - 0x24, 0x4b, 0x20, 0x4f, 0x1d, 0x53, 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, - 0x13, 0x59, 0x11, 0x5d, 0x10, 0x61, 0x0e, 0x62, 0x0d, 0x63, 0x0c, 0x65, - 0x0b, 0x66, 0x0a, 0x68, 0x09, 0x69, 0x09, 0x6b, 0x44, 0x0d, 0x3b, 0x12, - 0x34, 0x16, 0x2e, 0x19, 0x28, 0x1e, 0x23, 0x22, 0x20, 0x26, 0x1d, 0x2b, - 0x19, 0x2e, 0x16, 0x31, 0x14, 0x35, 0x12, 0x38, 0x0f, 0x3b, 0x0f, 0x3f, - 0x0c, 0x41, 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x07, 0x4e, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1d, 0x64, 0x1a, 0x66, 0x17, 0x68, - 0x14, 0x6a, 0x11, 0x6c, 0x10, 0x6e, 0x0e, 0x71, 0x0c, 0x73, 0x0b, 0x74, - 0x0a, 0x76, 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, - 0x05, 0x7f, 0x05, 0x80, 0x04, 0x82, 0x03, 0x82, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3b, 0x42, 0x34, 0x43, 0x2f, 0x48, 0x29, 0x49, 0x25, 0x4b, 0x21, 0x4d, - 0x1e, 0x51, 0x1b, 0x54, 0x18, 0x55, 0x16, 0x56, 0x14, 0x58, 0x13, 0x5b, - 0x10, 0x5e, 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, - 0x0a, 0x68, 0x09, 0x69, 0x44, 0x0c, 0x3c, 0x11, 0x35, 0x16, 0x2f, 0x19, - 0x2a, 0x1d, 0x25, 0x22, 0x21, 0x25, 0x1d, 0x29, 0x1a, 0x2b, 0x18, 0x30, - 0x16, 0x33, 0x12, 0x35, 0x12, 0x3a, 0x0f, 0x3c, 0x0f, 0x40, 0x0c, 0x41, - 0x0c, 0x45, 0x0a, 0x47, 0x0a, 0x4a, 0x09, 0x4e, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x22, 0x62, 0x1e, 0x64, 0x1a, 0x66, 0x17, 0x68, 0x15, 0x6a, 0x12, 0x6c, - 0x10, 0x6e, 0x0e, 0x70, 0x0d, 0x71, 0x0c, 0x73, 0x0b, 0x75, 0x09, 0x76, - 0x09, 0x78, 0x07, 0x79, 0x07, 0x7b, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, - 0x05, 0x80, 0x04, 0x82, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3b, 0x42, 0x35, 0x43, - 0x2f, 0x47, 0x2b, 0x49, 0x26, 0x4b, 0x22, 0x4c, 0x1f, 0x4f, 0x1c, 0x53, - 0x1a, 0x55, 0x18, 0x56, 0x15, 0x58, 0x13, 0x59, 0x12, 0x5c, 0x10, 0x60, - 0x0f, 0x61, 0x0e, 0x63, 0x0c, 0x64, 0x0c, 0x66, 0x0b, 0x66, 0x0a, 0x68, - 0x44, 0x0c, 0x3c, 0x10, 0x35, 0x15, 0x30, 0x19, 0x2b, 0x1c, 0x26, 0x20, - 0x21, 0x23, 0x1e, 0x26, 0x1c, 0x2b, 0x19, 0x2e, 0x16, 0x30, 0x15, 0x35, - 0x12, 0x37, 0x11, 0x3b, 0x0f, 0x3d, 0x0f, 0x41, 0x0c, 0x42, 0x0c, 0x46, - 0x0a, 0x47, 0x0a, 0x4a, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, - 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x00, 0xb7, 0x22, 0x62, 0x1e, 0x64, - 0x1a, 0x66, 0x18, 0x68, 0x15, 0x6a, 0x13, 0x6b, 0x10, 0x6d, 0x0f, 0x6e, - 0x0e, 0x71, 0x0c, 0x72, 0x0b, 0x74, 0x0a, 0x76, 0x09, 0x77, 0x08, 0x79, - 0x07, 0x7a, 0x07, 0x7c, 0x06, 0x7c, 0x06, 0x7e, 0x05, 0x7f, 0x05, 0x80, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x44, 0x09, 0x43, 0x09, 0x44, 0x09, 0x45, 0x09, - 0x45, 0x09, 0x46, 0x09, 0x46, 0x09, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0x22, 0x1c, 0x0a, 0x29, 0x0a, 0x31, 0x09, 0x36, 0x09, 0x3a, 0x09, - 0x3b, 0x09, 0x3d, 0x09, 0x3f, 0x09, 0x40, 0x09, 0x40, 0x09, 0x41, 0x09, - 0x42, 0x09, 0x43, 0x09, 0x43, 0x0a, 0x43, 0x0a, 0x44, 0x0a, 0x44, 0x0a, - 0x44, 0x0a, 0x44, 0x0a, 0x23, 0x16, 0x32, 0x0a, 0x38, 0x0a, 0x3c, 0x0a, - 0x3f, 0x09, 0x40, 0x09, 0x41, 0x09, 0x42, 0x09, 0x43, 0x09, 0x44, 0x09, - 0x44, 0x0a, 0x44, 0x0a, 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, - 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3f, 0x16, 0x3e, 0x11, 0x3f, 0x0f, 0x40, 0x0d, 0x41, 0x0d, 0x41, 0x0c, - 0x42, 0x0c, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0a, - 0x44, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x45, 0x0a, 0x46, 0x0a, 0x46, 0x0a, - 0x46, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x6e, 0x03, 0x3a, - 0x0f, 0x27, 0x18, 0x1e, 0x1f, 0x19, 0x24, 0x17, 0x28, 0x14, 0x2c, 0x13, - 0x2f, 0x11, 0x31, 0x11, 0x32, 0x10, 0x34, 0x0f, 0x36, 0x0f, 0x37, 0x0f, - 0x38, 0x0e, 0x39, 0x0e, 0x3a, 0x0d, 0x3a, 0x0d, 0x3b, 0x0c, 0x3c, 0x0c, - 0x23, 0x3c, 0x25, 0x22, 0x2b, 0x18, 0x30, 0x14, 0x33, 0x11, 0x36, 0x10, - 0x38, 0x0f, 0x39, 0x0e, 0x3b, 0x0e, 0x3c, 0x0d, 0x3d, 0x0d, 0x3e, 0x0c, - 0x3f, 0x0c, 0x3f, 0x0c, 0x40, 0x0c, 0x40, 0x0c, 0x40, 0x0b, 0x41, 0x0b, - 0x41, 0x0b, 0x42, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x1f, 0x3c, 0x18, - 0x3c, 0x14, 0x3d, 0x11, 0x3e, 0x11, 0x3f, 0x0f, 0x3f, 0x0e, 0x40, 0x0e, - 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x42, 0x0c, - 0x42, 0x0b, 0x42, 0x0b, 0x43, 0x0b, 0x43, 0x0b, 0x44, 0x0b, 0x44, 0x0b, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x8a, 0x00, 0x59, 0x06, 0x40, 0x0d, 0x32, - 0x12, 0x2a, 0x18, 0x24, 0x1c, 0x20, 0x20, 0x1c, 0x23, 0x1b, 0x26, 0x18, - 0x28, 0x17, 0x2a, 0x16, 0x2c, 0x15, 0x2e, 0x14, 0x2f, 0x13, 0x31, 0x12, - 0x32, 0x12, 0x33, 0x12, 0x34, 0x11, 0x35, 0x10, 0x23, 0x4a, 0x24, 0x31, - 0x27, 0x25, 0x2a, 0x1e, 0x2d, 0x1a, 0x2f, 0x17, 0x32, 0x15, 0x33, 0x13, - 0x35, 0x12, 0x36, 0x11, 0x38, 0x10, 0x39, 0x10, 0x3a, 0x0f, 0x3a, 0x0f, - 0x3b, 0x0e, 0x3c, 0x0e, 0x3c, 0x0e, 0x3d, 0x0e, 0x3e, 0x0d, 0x3e, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x25, 0x3c, 0x1d, 0x3c, 0x19, 0x3c, 0x16, - 0x3d, 0x14, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x10, 0x3f, 0x0f, 0x3f, 0x0f, - 0x40, 0x0e, 0x41, 0x0e, 0x41, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, - 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0d, 0x42, 0x0c, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0x97, 0x00, 0x6c, 0x02, 0x52, 0x07, 0x43, 0x0b, 0x38, 0x10, 0x30, - 0x14, 0x2a, 0x18, 0x27, 0x1b, 0x23, 0x1e, 0x20, 0x20, 0x1e, 0x23, 0x1c, - 0x25, 0x1b, 0x27, 0x19, 0x28, 0x18, 0x2a, 0x17, 0x2b, 0x16, 0x2c, 0x16, - 0x2e, 0x16, 0x2f, 0x15, 0x23, 0x50, 0x23, 0x3b, 0x25, 0x2e, 0x27, 0x26, - 0x29, 0x21, 0x2c, 0x1d, 0x2e, 0x1a, 0x2f, 0x18, 0x31, 0x16, 0x33, 0x15, - 0x34, 0x14, 0x35, 0x13, 0x36, 0x12, 0x37, 0x11, 0x38, 0x11, 0x38, 0x11, - 0x39, 0x10, 0x3a, 0x10, 0x3a, 0x10, 0x3b, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x29, 0x3c, 0x22, 0x3b, 0x1d, 0x3b, 0x19, 0x3c, 0x17, 0x3c, 0x15, - 0x3c, 0x13, 0x3d, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x10, 0x3e, 0x0f, - 0x3f, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, 0x42, 0x0e, 0x42, 0x0d, - 0x42, 0x0d, 0x42, 0x0d, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0x9e, 0x00, 0x79, - 0x00, 0x61, 0x04, 0x50, 0x07, 0x44, 0x0b, 0x3b, 0x0f, 0x34, 0x12, 0x2f, - 0x15, 0x2b, 0x18, 0x28, 0x1a, 0x25, 0x1d, 0x23, 0x1f, 0x21, 0x21, 0x1f, - 0x22, 0x1d, 0x24, 0x1c, 0x26, 0x1b, 0x27, 0x19, 0x28, 0x19, 0x2a, 0x19, - 0x23, 0x54, 0x23, 0x42, 0x24, 0x35, 0x25, 0x2d, 0x27, 0x27, 0x29, 0x22, - 0x2b, 0x1f, 0x2d, 0x1c, 0x2e, 0x1a, 0x30, 0x19, 0x31, 0x17, 0x32, 0x16, - 0x33, 0x15, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, 0x37, 0x12, 0x37, 0x11, - 0x38, 0x11, 0x38, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2c, 0x3c, 0x25, - 0x3b, 0x20, 0x3b, 0x1d, 0x3b, 0x1a, 0x3c, 0x17, 0x3c, 0x16, 0x3c, 0x14, - 0x3c, 0x13, 0x3e, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3d, 0x10, - 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x40, 0x0e, 0x40, 0x0e, 0x41, 0x0e, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa2, 0x00, 0x84, 0x00, 0x6c, 0x02, 0x5a, - 0x05, 0x4e, 0x07, 0x45, 0x0b, 0x3e, 0x0e, 0x38, 0x10, 0x33, 0x13, 0x2f, - 0x16, 0x2c, 0x18, 0x29, 0x1a, 0x26, 0x1c, 0x24, 0x1d, 0x22, 0x1f, 0x21, - 0x21, 0x1f, 0x21, 0x1e, 0x23, 0x1d, 0x25, 0x1c, 0x23, 0x56, 0x23, 0x47, - 0x23, 0x3b, 0x24, 0x32, 0x26, 0x2c, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x21, - 0x2c, 0x1e, 0x2d, 0x1c, 0x2e, 0x1b, 0x30, 0x19, 0x30, 0x18, 0x32, 0x17, - 0x32, 0x16, 0x33, 0x15, 0x34, 0x14, 0x34, 0x14, 0x35, 0x13, 0x36, 0x13, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3e, 0x2e, 0x3c, 0x27, 0x3b, 0x23, 0x3b, 0x1f, - 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x15, 0x3c, 0x14, - 0x3c, 0x13, 0x3e, 0x13, 0x3f, 0x11, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, - 0x3d, 0x10, 0x3e, 0x0f, 0x3f, 0x0f, 0x40, 0x0f, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xa6, 0x00, 0x8b, 0x00, 0x75, 0x01, 0x63, 0x02, 0x57, 0x05, 0x4d, - 0x07, 0x45, 0x0a, 0x3e, 0x0d, 0x39, 0x0f, 0x35, 0x12, 0x32, 0x14, 0x2e, - 0x16, 0x2c, 0x18, 0x29, 0x19, 0x27, 0x1b, 0x26, 0x1d, 0x24, 0x1e, 0x22, - 0x20, 0x22, 0x21, 0x20, 0x23, 0x58, 0x23, 0x4a, 0x23, 0x3f, 0x24, 0x36, - 0x25, 0x30, 0x26, 0x2b, 0x27, 0x27, 0x29, 0x24, 0x2a, 0x22, 0x2b, 0x20, - 0x2d, 0x1e, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x18, 0x31, 0x18, - 0x32, 0x17, 0x32, 0x16, 0x33, 0x16, 0x34, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x30, 0x3c, 0x2a, 0x3c, 0x25, 0x3a, 0x21, 0x3b, 0x1e, 0x3a, 0x1c, - 0x3b, 0x1a, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x16, 0x3b, 0x15, 0x3c, 0x14, - 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, - 0x3d, 0x10, 0x3e, 0x0f, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xa8, 0x00, 0x90, - 0x00, 0x7c, 0x00, 0x6b, 0x01, 0x5f, 0x04, 0x55, 0x05, 0x4c, 0x07, 0x45, - 0x0a, 0x40, 0x0c, 0x3c, 0x0f, 0x37, 0x10, 0x34, 0x12, 0x31, 0x15, 0x2e, - 0x16, 0x2b, 0x18, 0x2a, 0x19, 0x28, 0x1a, 0x26, 0x1d, 0x25, 0x1d, 0x23, - 0x23, 0x59, 0x23, 0x4d, 0x23, 0x43, 0x24, 0x3b, 0x24, 0x34, 0x25, 0x2f, - 0x26, 0x2b, 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2c, 0x1f, - 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x31, 0x18, - 0x32, 0x17, 0x32, 0x16, 0x17, 0x00, 0x2f, 0x00, 0x4c, 0x00, 0x56, 0x00, - 0x59, 0x00, 0x5b, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, - 0x14, 0x2c, 0x29, 0x01, 0x49, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5b, 0x00, - 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x62, 0x00, - 0x62, 0x00, 0x5f, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x5f, 0x00, 0x5f, 0x00, - 0x11, 0x31, 0x23, 0x03, 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, - 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5f, 0x00, 0x3e, 0x32, 0x3d, 0x2c, - 0x3c, 0x26, 0x3b, 0x23, 0x3b, 0x20, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, - 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x14, - 0x3d, 0x13, 0x3e, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x3f, 0x11, 0x3e, 0x11, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaa, 0x00, 0x94, 0x00, 0x82, 0x00, 0x72, - 0x01, 0x65, 0x02, 0x5b, 0x04, 0x53, 0x06, 0x4b, 0x08, 0x45, 0x0a, 0x41, - 0x0c, 0x3d, 0x0e, 0x39, 0x0f, 0x36, 0x12, 0x33, 0x12, 0x30, 0x16, 0x2e, - 0x16, 0x2b, 0x18, 0x2b, 0x19, 0x29, 0x1a, 0x26, 0x23, 0x5a, 0x23, 0x4f, - 0x23, 0x46, 0x23, 0x3e, 0x24, 0x37, 0x25, 0x32, 0x25, 0x2e, 0x26, 0x2b, - 0x27, 0x28, 0x28, 0x25, 0x2a, 0x23, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1e, - 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x30, 0x19, 0x30, 0x18, - 0x00, 0x00, 0x07, 0x00, 0x2f, 0x00, 0x45, 0x00, 0x4f, 0x00, 0x55, 0x00, - 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x57, 0x04, 0x16, - 0x27, 0x00, 0x41, 0x00, 0x4d, 0x00, 0x53, 0x00, 0x56, 0x00, 0x59, 0x00, - 0x5a, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x4c, 0x00, 0x3b, 0x00, 0x48, 0x00, 0x55, 0x00, 0x58, 0x00, 0x57, 0x00, - 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5c, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x00, 0x5f, 0x00, 0x20, - 0x1f, 0x00, 0x3c, 0x00, 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, - 0x59, 0x00, 0x5b, 0x00, 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5d, 0x00, 0x3e, 0x33, 0x3d, 0x2d, 0x3c, 0x28, 0x3b, 0x25, - 0x3a, 0x22, 0x3b, 0x1f, 0x3b, 0x1e, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, - 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x13, - 0x3d, 0x13, 0x3f, 0x13, 0x3f, 0x12, 0x3f, 0x11, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xaa, 0x00, 0x98, 0x00, 0x87, 0x00, 0x78, 0x00, 0x6b, 0x01, 0x61, - 0x03, 0x58, 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3e, - 0x0d, 0x3a, 0x0f, 0x37, 0x11, 0x35, 0x12, 0x32, 0x14, 0x30, 0x16, 0x2e, - 0x16, 0x2b, 0x18, 0x2b, 0x23, 0x5a, 0x23, 0x51, 0x23, 0x48, 0x23, 0x41, - 0x24, 0x3a, 0x24, 0x35, 0x25, 0x31, 0x26, 0x2e, 0x27, 0x2a, 0x27, 0x28, - 0x28, 0x26, 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2c, 0x1f, 0x2d, 0x1e, - 0x2d, 0x1d, 0x2e, 0x1c, 0x2e, 0x1b, 0x30, 0x1a, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x22, 0x00, 0x36, 0x00, 0x42, 0x00, 0x4a, 0x00, 0x4f, 0x00, - 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, - 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x82, 0x00, 0x57, 0x01, 0x12, 0x17, 0x00, - 0x2f, 0x00, 0x3e, 0x00, 0x47, 0x00, 0x4d, 0x00, 0x51, 0x00, 0x53, 0x00, - 0x56, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x5b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x48, 0x00, - 0x1b, 0x00, 0x34, 0x00, 0x40, 0x00, 0x44, 0x00, 0x4a, 0x00, 0x4f, 0x00, - 0x53, 0x00, 0x55, 0x00, 0x57, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5a, 0x00, - 0x5b, 0x00, 0x5b, 0x00, 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, - 0x28, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, - 0x54, 0x00, 0x56, 0x00, 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, - 0x3e, 0x34, 0x3d, 0x2f, 0x3b, 0x2a, 0x3c, 0x26, 0x3a, 0x23, 0x3b, 0x21, - 0x3b, 0x1f, 0x3b, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x17, - 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x3c, 0x13, - 0x3e, 0x13, 0x3f, 0x13, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xac, 0x00, 0x9b, - 0x00, 0x8b, 0x00, 0x7c, 0x00, 0x71, 0x01, 0x67, 0x02, 0x5e, 0x04, 0x57, - 0x05, 0x51, 0x07, 0x4b, 0x08, 0x46, 0x0a, 0x42, 0x0c, 0x3f, 0x0c, 0x3b, - 0x0f, 0x39, 0x0f, 0x35, 0x12, 0x34, 0x12, 0x31, 0x14, 0x30, 0x16, 0x2e, - 0x23, 0x5b, 0x23, 0x52, 0x23, 0x4a, 0x23, 0x43, 0x23, 0x3d, 0x24, 0x38, - 0x25, 0x34, 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, - 0x2a, 0x24, 0x2a, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x1f, 0x2d, 0x1d, - 0x2e, 0x1d, 0x2e, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x19, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, - 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, - 0x00, 0x91, 0x00, 0x78, 0x00, 0x43, 0x00, 0x10, 0x0e, 0x00, 0x23, 0x00, - 0x31, 0x00, 0x3b, 0x00, 0x42, 0x00, 0x48, 0x00, 0x4c, 0x00, 0x4f, 0x00, - 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x55, 0x00, 0x34, 0x00, 0x0b, 0x00, - 0x1f, 0x00, 0x2b, 0x00, 0x38, 0x00, 0x40, 0x00, 0x47, 0x00, 0x4b, 0x00, - 0x4f, 0x00, 0x51, 0x00, 0x53, 0x00, 0x55, 0x00, 0x56, 0x00, 0x57, 0x00, - 0x00, 0x92, 0x00, 0x7c, 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, - 0x2a, 0x00, 0x36, 0x00, 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, - 0x4f, 0x00, 0x52, 0x00, 0x53, 0x00, 0x55, 0x00, 0x3e, 0x35, 0x3e, 0x30, - 0x3b, 0x2c, 0x3c, 0x28, 0x3b, 0x25, 0x3a, 0x22, 0x3b, 0x20, 0x3b, 0x1f, - 0x3a, 0x1d, 0x3a, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, - 0x3c, 0x17, 0x3a, 0x16, 0x3b, 0x15, 0x3c, 0x15, 0x3c, 0x14, 0x3c, 0x13, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0x9d, 0x00, 0x8e, 0x00, 0x81, - 0x00, 0x76, 0x00, 0x6b, 0x01, 0x63, 0x02, 0x5c, 0x04, 0x56, 0x05, 0x50, - 0x07, 0x4b, 0x08, 0x47, 0x0a, 0x43, 0x0c, 0x40, 0x0c, 0x3c, 0x0f, 0x3a, - 0x0f, 0x37, 0x11, 0x35, 0x12, 0x33, 0x12, 0x30, 0x23, 0x5b, 0x23, 0x53, - 0x23, 0x4c, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3b, 0x24, 0x36, 0x25, 0x33, - 0x25, 0x30, 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x2a, 0x25, - 0x2a, 0x23, 0x2b, 0x22, 0x2b, 0x20, 0x2c, 0x20, 0x2d, 0x1e, 0x2d, 0x1d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, - 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, - 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x96, 0x00, 0x87, - 0x00, 0x61, 0x00, 0x36, 0x00, 0x10, 0x0a, 0x02, 0x1a, 0x00, 0x27, 0x00, - 0x32, 0x00, 0x3a, 0x00, 0x40, 0x00, 0x44, 0x00, 0x48, 0x00, 0x4b, 0x00, - 0x4e, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x62, 0x00, 0x58, 0x00, 0x40, 0x00, 0x1f, 0x00, 0x01, 0x00, 0x14, 0x00, - 0x24, 0x00, 0x2f, 0x00, 0x38, 0x00, 0x3f, 0x00, 0x44, 0x00, 0x48, 0x00, - 0x4c, 0x00, 0x4e, 0x00, 0x50, 0x00, 0x52, 0x00, 0x00, 0x97, 0x00, 0x8a, - 0x00, 0x68, 0x00, 0x42, 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, - 0x2b, 0x00, 0x34, 0x00, 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, - 0x4b, 0x00, 0x4e, 0x00, 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2c, 0x3c, 0x29, - 0x3c, 0x26, 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x39, 0x1d, - 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3c, 0x17, - 0x3a, 0x17, 0x3a, 0x15, 0x3c, 0x15, 0x3c, 0x15, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xad, 0x00, 0x9f, 0x00, 0x91, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6f, - 0x01, 0x67, 0x02, 0x60, 0x03, 0x59, 0x04, 0x54, 0x05, 0x4f, 0x07, 0x4b, - 0x08, 0x47, 0x0a, 0x43, 0x0b, 0x41, 0x0c, 0x3d, 0x0e, 0x3b, 0x0f, 0x38, - 0x0f, 0x35, 0x12, 0x35, 0x23, 0x5b, 0x23, 0x54, 0x23, 0x4e, 0x23, 0x47, - 0x23, 0x42, 0x24, 0x3c, 0x24, 0x38, 0x25, 0x35, 0x25, 0x32, 0x26, 0x2f, - 0x26, 0x2d, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x26, 0x29, 0x25, 0x2a, 0x23, - 0x2b, 0x22, 0x2b, 0x21, 0x2b, 0x20, 0x2d, 0x20, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, - 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, - 0x49, 0x00, 0x4c, 0x00, 0x00, 0x99, 0x00, 0x8f, 0x00, 0x74, 0x00, 0x51, - 0x00, 0x2e, 0x00, 0x10, 0x08, 0x04, 0x13, 0x00, 0x20, 0x00, 0x2a, 0x00, - 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x48, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x57, 0x00, - 0x44, 0x00, 0x2b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1e, 0x00, - 0x29, 0x00, 0x32, 0x00, 0x38, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x46, 0x00, - 0x49, 0x00, 0x4c, 0x00, 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, - 0x00, 0x3b, 0x00, 0x1f, 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, - 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, - 0x3e, 0x36, 0x3e, 0x31, 0x3b, 0x2d, 0x3b, 0x2a, 0x3c, 0x27, 0x3a, 0x25, - 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1c, 0x3b, 0x1c, - 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, - 0x3a, 0x16, 0x3b, 0x15, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xad, 0x00, 0xa0, - 0x00, 0x94, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x74, 0x00, 0x6c, 0x01, 0x65, - 0x02, 0x5e, 0x04, 0x58, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4b, 0x08, 0x47, - 0x0a, 0x44, 0x0b, 0x41, 0x0c, 0x3e, 0x0d, 0x3b, 0x0f, 0x3a, 0x0f, 0x37, - 0x23, 0x5c, 0x23, 0x55, 0x23, 0x4f, 0x23, 0x49, 0x23, 0x44, 0x23, 0x3f, - 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, 0x25, 0x31, 0x26, 0x2e, 0x26, 0x2c, - 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x22, - 0x2b, 0x22, 0x2b, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, - 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, - 0x00, 0x9b, 0x00, 0x94, 0x00, 0x7f, 0x00, 0x64, 0x00, 0x46, 0x00, 0x29, - 0x00, 0x10, 0x07, 0x06, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, 0x2c, 0x00, - 0x32, 0x00, 0x38, 0x00, 0x3c, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, 0x4a, 0x00, 0x38, 0x00, - 0x24, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x1a, 0x00, 0x24, 0x00, - 0x2c, 0x00, 0x33, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x41, 0x00, 0x44, 0x00, - 0x00, 0x9b, 0x00, 0x95, 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, - 0x00, 0x1f, 0x00, 0x0c, 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, - 0x2c, 0x00, 0x32, 0x00, 0x37, 0x00, 0x3c, 0x00, 0x3e, 0x37, 0x3e, 0x32, - 0x3c, 0x2f, 0x3b, 0x2b, 0x3c, 0x28, 0x3b, 0x26, 0x3a, 0x24, 0x3a, 0x22, - 0x3b, 0x21, 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, - 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x17, 0x3c, 0x17, 0x3b, 0x17, 0x3a, 0x17, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xae, 0x00, 0xa2, 0x00, 0x97, 0x00, 0x8b, - 0x00, 0x80, 0x00, 0x77, 0x00, 0x6f, 0x01, 0x67, 0x02, 0x61, 0x02, 0x5c, - 0x04, 0x57, 0x05, 0x52, 0x05, 0x4e, 0x07, 0x4b, 0x09, 0x47, 0x0a, 0x44, - 0x0b, 0x41, 0x0c, 0x3f, 0x0c, 0x3c, 0x0f, 0x3b, 0x23, 0x5c, 0x23, 0x56, - 0x23, 0x50, 0x23, 0x4a, 0x23, 0x45, 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, - 0x25, 0x35, 0x25, 0x33, 0x25, 0x30, 0x26, 0x2e, 0x26, 0x2c, 0x27, 0x2a, - 0x28, 0x28, 0x28, 0x27, 0x29, 0x25, 0x2a, 0x24, 0x2a, 0x23, 0x2b, 0x22, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, - 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x96, - 0x00, 0x87, 0x00, 0x71, 0x00, 0x57, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x10, - 0x06, 0x07, 0x0b, 0x00, 0x15, 0x00, 0x1f, 0x00, 0x26, 0x00, 0x2d, 0x00, - 0x32, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5d, 0x00, 0x59, 0x00, 0x4f, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x1e, 0x00, - 0x0e, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x17, 0x00, 0x20, 0x00, 0x28, 0x00, - 0x2e, 0x00, 0x34, 0x00, 0x39, 0x00, 0x3d, 0x00, 0x00, 0x9c, 0x00, 0x97, - 0x00, 0x8a, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, - 0x2c, 0x00, 0x31, 0x00, 0x3e, 0x37, 0x3e, 0x33, 0x3c, 0x2f, 0x3b, 0x2c, - 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, 0x3a, 0x23, 0x3b, 0x21, 0x3b, 0x20, - 0x3b, 0x1f, 0x3a, 0x1e, 0x39, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, - 0x3c, 0x19, 0x3c, 0x18, 0x3c, 0x17, 0x3b, 0x17, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x98, 0x00, 0x8e, 0x00, 0x84, 0x00, 0x7a, - 0x00, 0x72, 0x00, 0x6c, 0x01, 0x65, 0x02, 0x5f, 0x04, 0x5a, 0x04, 0x56, - 0x05, 0x52, 0x06, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, - 0x0c, 0x40, 0x0c, 0x3d, 0x23, 0x5c, 0x23, 0x56, 0x23, 0x51, 0x23, 0x4c, - 0x23, 0x47, 0x23, 0x42, 0x23, 0x3e, 0x24, 0x3b, 0x24, 0x37, 0x25, 0x34, - 0x25, 0x32, 0x25, 0x30, 0x26, 0x2e, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, - 0x28, 0x27, 0x29, 0x25, 0x2a, 0x25, 0x2a, 0x23, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, - 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x7a, - 0x00, 0x64, 0x00, 0x4e, 0x00, 0x37, 0x00, 0x22, 0x00, 0x10, 0x05, 0x08, - 0x0a, 0x02, 0x12, 0x00, 0x1a, 0x00, 0x22, 0x00, 0x28, 0x00, 0x2d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, - 0x53, 0x00, 0x47, 0x00, 0x38, 0x00, 0x29, 0x00, 0x1a, 0x00, 0x0c, 0x00, - 0x00, 0x00, 0x0b, 0x00, 0x14, 0x00, 0x1d, 0x00, 0x24, 0x00, 0x2a, 0x00, - 0x30, 0x00, 0x35, 0x00, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, - 0x00, 0x6b, 0x00, 0x57, 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, - 0x00, 0x04, 0x07, 0x00, 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, - 0x3e, 0x38, 0x3e, 0x33, 0x3d, 0x30, 0x3b, 0x2d, 0x3c, 0x2a, 0x3c, 0x27, - 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, - 0x39, 0x1e, 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, - 0x3c, 0x18, 0x3c, 0x17, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa5, - 0x00, 0x99, 0x00, 0x90, 0x00, 0x87, 0x00, 0x7e, 0x00, 0x76, 0x00, 0x6f, - 0x01, 0x68, 0x02, 0x62, 0x02, 0x5e, 0x04, 0x59, 0x04, 0x55, 0x05, 0x51, - 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x41, 0x0c, 0x41, - 0x23, 0x5c, 0x23, 0x57, 0x23, 0x52, 0x23, 0x4d, 0x23, 0x48, 0x23, 0x44, - 0x23, 0x40, 0x24, 0x3c, 0x24, 0x39, 0x24, 0x36, 0x25, 0x34, 0x25, 0x31, - 0x26, 0x2f, 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x27, - 0x28, 0x25, 0x2a, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, - 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x90, 0x00, 0x81, 0x00, 0x6f, 0x00, 0x5b, - 0x00, 0x46, 0x00, 0x32, 0x00, 0x20, 0x00, 0x10, 0x04, 0x09, 0x09, 0x03, - 0x0f, 0x00, 0x17, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x55, 0x00, 0x4b, 0x00, - 0x3f, 0x00, 0x32, 0x00, 0x24, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, - 0x09, 0x00, 0x12, 0x00, 0x1a, 0x00, 0x21, 0x00, 0x27, 0x00, 0x2c, 0x00, - 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, - 0x00, 0x50, 0x00, 0x3f, 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, - 0x03, 0x00, 0x0c, 0x00, 0x14, 0x00, 0x1b, 0x00, 0x3e, 0x38, 0x3e, 0x34, - 0x3e, 0x31, 0x3b, 0x2e, 0x3b, 0x2b, 0x3c, 0x29, 0x3c, 0x27, 0x39, 0x25, - 0x3a, 0x24, 0x3a, 0x21, 0x3b, 0x21, 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1d, - 0x3a, 0x1c, 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x3c, 0x19, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xaf, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x91, - 0x00, 0x88, 0x00, 0x80, 0x00, 0x78, 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, - 0x02, 0x61, 0x03, 0x5c, 0x04, 0x58, 0x05, 0x55, 0x05, 0x50, 0x07, 0x4e, - 0x07, 0x4a, 0x09, 0x47, 0x0a, 0x45, 0x0a, 0x42, 0x23, 0x5c, 0x23, 0x58, - 0x23, 0x53, 0x23, 0x4d, 0x23, 0x49, 0x23, 0x45, 0x23, 0x41, 0x24, 0x3d, - 0x24, 0x3b, 0x24, 0x38, 0x25, 0x35, 0x25, 0x33, 0x25, 0x31, 0x26, 0x2f, - 0x26, 0x2d, 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x28, 0x26, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9d, 0x00, 0x9b, - 0x00, 0x93, 0x00, 0x86, 0x00, 0x76, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, - 0x00, 0x2f, 0x00, 0x1e, 0x00, 0x10, 0x04, 0x09, 0x08, 0x04, 0x0c, 0x00, - 0x14, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5c, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x44, 0x00, 0x38, 0x00, - 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x09, 0x00, - 0x11, 0x00, 0x18, 0x00, 0x1e, 0x00, 0x24, 0x00, 0x00, 0x9e, 0x00, 0x9b, - 0x00, 0x94, 0x00, 0x89, 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, - 0x00, 0x3b, 0x00, 0x2d, 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, - 0x09, 0x00, 0x11, 0x00, 0x3e, 0x38, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2e, - 0x3b, 0x2c, 0x3c, 0x2a, 0x3c, 0x27, 0x3a, 0x26, 0x3a, 0x24, 0x3a, 0x23, - 0x3b, 0x21, 0x3b, 0x20, 0x3b, 0x1f, 0x3a, 0x1f, 0x39, 0x1d, 0x3a, 0x1c, - 0x3b, 0x1c, 0x3b, 0x1a, 0x3c, 0x19, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x00, 0xb0, 0x00, 0xa6, 0x00, 0x9d, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x83, - 0x00, 0x7c, 0x00, 0x75, 0x00, 0x6e, 0x01, 0x69, 0x01, 0x63, 0x02, 0x5f, - 0x04, 0x5b, 0x04, 0x56, 0x05, 0x54, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, - 0x09, 0x47, 0x0a, 0x46, 0x23, 0x5d, 0x23, 0x58, 0x23, 0x53, 0x23, 0x4f, - 0x23, 0x4a, 0x23, 0x46, 0x23, 0x43, 0x23, 0x3f, 0x24, 0x3c, 0x24, 0x39, - 0x24, 0x36, 0x25, 0x34, 0x25, 0x32, 0x25, 0x30, 0x26, 0x2f, 0x26, 0x2d, - 0x27, 0x2c, 0x27, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x95, 0x00, 0x8a, - 0x00, 0x7d, 0x00, 0x6d, 0x00, 0x5d, 0x00, 0x4c, 0x00, 0x3b, 0x00, 0x2c, - 0x00, 0x1d, 0x00, 0x10, 0x04, 0x0a, 0x07, 0x05, 0x0b, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x58, 0x00, 0x51, 0x00, 0x48, 0x00, 0x3e, 0x00, 0x33, 0x00, 0x28, 0x00, - 0x1d, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0f, 0x00, - 0x16, 0x00, 0x1c, 0x00, 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, - 0x00, 0x80, 0x00, 0x73, 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, - 0x00, 0x2b, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, - 0x3e, 0x39, 0x3e, 0x35, 0x3e, 0x32, 0x3b, 0x2f, 0x3b, 0x2d, 0x3c, 0x2a, - 0x3c, 0x28, 0x3b, 0x27, 0x39, 0x24, 0x3a, 0x24, 0x3a, 0x22, 0x3b, 0x21, - 0x3b, 0x1f, 0x3b, 0x1f, 0x39, 0x1f, 0x39, 0x1d, 0x3b, 0x1c, 0x3b, 0x1c, - 0x3b, 0x1a, 0x3c, 0x19, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, - 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x47, 0x0a, 0x00, 0xb0, 0x00, 0xa7, - 0x00, 0x9e, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x86, 0x00, 0x7e, 0x00, 0x77, - 0x00, 0x71, 0x00, 0x6b, 0x01, 0x66, 0x02, 0x62, 0x02, 0x5d, 0x04, 0x5a, - 0x04, 0x55, 0x05, 0x53, 0x05, 0x4f, 0x07, 0x4e, 0x07, 0x4a, 0x09, 0x47, - 0x23, 0x5d, 0x23, 0x58, 0x23, 0x54, 0x23, 0x50, 0x23, 0x4c, 0x23, 0x48, - 0x23, 0x44, 0x23, 0x40, 0x24, 0x3d, 0x24, 0x3a, 0x24, 0x38, 0x25, 0x36, - 0x25, 0x33, 0x25, 0x32, 0x25, 0x2f, 0x26, 0x2f, 0x26, 0x2c, 0x27, 0x2c, - 0x27, 0x2a, 0x28, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8d, 0x00, 0x81, 0x00, 0x74, - 0x00, 0x65, 0x00, 0x56, 0x00, 0x46, 0x00, 0x37, 0x00, 0x29, 0x00, 0x1c, - 0x00, 0x10, 0x03, 0x0a, 0x07, 0x06, 0x0a, 0x01, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, - 0x4c, 0x00, 0x42, 0x00, 0x39, 0x00, 0x2e, 0x00, 0x24, 0x00, 0x1a, 0x00, - 0x11, 0x00, 0x08, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0e, 0x00, 0x14, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, - 0x00, 0x6b, 0x00, 0x5e, 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, - 0x00, 0x1f, 0x00, 0x15, 0x00, 0x0c, 0x00, 0x03, 0x32, 0x59, 0x2a, 0x5b, - 0x28, 0x5c, 0x27, 0x5d, 0x27, 0x5d, 0x26, 0x5e, 0x26, 0x5e, 0x26, 0x5e, - 0x26, 0x5e, 0x26, 0x5e, 0x25, 0x5e, 0x25, 0x5e, 0x25, 0x5f, 0x25, 0x5f, - 0x25, 0x5f, 0x25, 0x5f, 0x25, 0x5f, 0x24, 0x5f, 0x24, 0x5f, 0x24, 0x60, - 0x32, 0x3c, 0x25, 0x4a, 0x24, 0x50, 0x23, 0x54, 0x23, 0x56, 0x23, 0x58, - 0x23, 0x59, 0x23, 0x5a, 0x23, 0x5a, 0x23, 0x5b, 0x23, 0x5b, 0x23, 0x5b, - 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5c, 0x23, 0x5d, - 0x23, 0x5d, 0x23, 0x5d, 0x00, 0x6c, 0x0e, 0x60, 0x14, 0x60, 0x18, 0x60, - 0x1b, 0x60, 0x1d, 0x60, 0x1d, 0x60, 0x1e, 0x60, 0x1f, 0x60, 0x20, 0x60, - 0x20, 0x60, 0x20, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, 0x21, 0x60, - 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x22, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x97, 0x00, 0x90, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6c, 0x00, 0x5e, - 0x00, 0x50, 0x00, 0x42, 0x00, 0x34, 0x00, 0x27, 0x00, 0x1b, 0x00, 0x0f, - 0x03, 0x0b, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x46, 0x00, - 0x3d, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0f, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0d, 0x00, 0x00, 0x9e, 0x00, 0x9d, - 0x00, 0x98, 0x00, 0x91, 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, - 0x00, 0x59, 0x00, 0x4c, 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, - 0x00, 0x16, 0x00, 0x0d, 0x38, 0x4f, 0x31, 0x53, 0x2e, 0x56, 0x2c, 0x57, - 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x38, 0x22, 0x2b, 0x31, - 0x27, 0x3b, 0x25, 0x42, 0x24, 0x47, 0x23, 0x4a, 0x23, 0x4d, 0x23, 0x4f, - 0x23, 0x51, 0x23, 0x52, 0x23, 0x53, 0x23, 0x54, 0x23, 0x55, 0x23, 0x56, - 0x23, 0x56, 0x23, 0x57, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, 0x23, 0x58, - 0x00, 0x92, 0x01, 0x79, 0x07, 0x6f, 0x0c, 0x6b, 0x0f, 0x68, 0x12, 0x67, - 0x14, 0x66, 0x16, 0x65, 0x17, 0x64, 0x18, 0x64, 0x19, 0x63, 0x1a, 0x63, - 0x1b, 0x63, 0x1b, 0x63, 0x1c, 0x63, 0x1c, 0x62, 0x1d, 0x62, 0x1d, 0x62, - 0x1d, 0x62, 0x1e, 0x62, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x92, - 0x00, 0x89, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x58, 0x00, 0x4b, - 0x00, 0x3e, 0x00, 0x31, 0x00, 0x25, 0x00, 0x1a, 0x00, 0x0f, 0x03, 0x0b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, - 0x5b, 0x00, 0x56, 0x00, 0x50, 0x00, 0x49, 0x00, 0x41, 0x00, 0x39, 0x00, - 0x30, 0x00, 0x27, 0x00, 0x1e, 0x00, 0x16, 0x00, 0x0e, 0x00, 0x07, 0x00, - 0x00, 0x00, 0x06, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, - 0x00, 0x8b, 0x00, 0x82, 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, - 0x00, 0x49, 0x00, 0x3d, 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, - 0x3a, 0x4b, 0x34, 0x4f, 0x31, 0x52, 0x2f, 0x54, 0x2d, 0x56, 0x2c, 0x57, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5a, - 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, 0x27, 0x5c, - 0x27, 0x5c, 0x27, 0x5c, 0x3c, 0x18, 0x30, 0x25, 0x2a, 0x2e, 0x27, 0x35, - 0x25, 0x3b, 0x24, 0x3f, 0x24, 0x43, 0x24, 0x46, 0x23, 0x48, 0x23, 0x4a, - 0x23, 0x4c, 0x23, 0x4e, 0x23, 0x4f, 0x23, 0x50, 0x23, 0x51, 0x23, 0x52, - 0x23, 0x53, 0x23, 0x53, 0x23, 0x54, 0x23, 0x54, 0x00, 0xa1, 0x00, 0x88, - 0x03, 0x7b, 0x06, 0x74, 0x09, 0x70, 0x0c, 0x6d, 0x0e, 0x6b, 0x10, 0x6a, - 0x11, 0x69, 0x13, 0x68, 0x14, 0x67, 0x15, 0x66, 0x16, 0x66, 0x17, 0x65, - 0x17, 0x65, 0x18, 0x65, 0x19, 0x65, 0x19, 0x64, 0x1a, 0x64, 0x1a, 0x64, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, - 0x00, 0x77, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x46, 0x00, 0x3a, - 0x00, 0x2f, 0x00, 0x24, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x5e, 0x00, 0x5b, 0x00, 0x57, 0x00, - 0x52, 0x00, 0x4c, 0x00, 0x44, 0x00, 0x3d, 0x00, 0x35, 0x00, 0x2c, 0x00, - 0x24, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x0d, 0x00, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, - 0x00, 0x7b, 0x00, 0x71, 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, - 0x00, 0x3b, 0x00, 0x31, 0x00, 0x28, 0x00, 0x1f, 0x3b, 0x48, 0x36, 0x4c, - 0x33, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2d, 0x55, 0x2c, 0x57, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, - 0x2a, 0x59, 0x29, 0x5a, 0x29, 0x5b, 0x28, 0x5b, 0x27, 0x5c, 0x27, 0x5c, - 0x3f, 0x14, 0x33, 0x1e, 0x2d, 0x26, 0x29, 0x2d, 0x27, 0x32, 0x26, 0x36, - 0x25, 0x3b, 0x24, 0x3e, 0x24, 0x41, 0x24, 0x43, 0x23, 0x45, 0x23, 0x47, - 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4d, 0x23, 0x4d, 0x23, 0x4f, - 0x23, 0x50, 0x23, 0x50, 0x00, 0xa7, 0x00, 0x92, 0x01, 0x84, 0x03, 0x7d, - 0x05, 0x77, 0x08, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6d, 0x0f, 0x6b, - 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x69, 0x13, 0x68, 0x14, 0x68, 0x15, 0x67, - 0x15, 0x66, 0x16, 0x66, 0x17, 0x66, 0x17, 0x66, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x31, 0x23, 0x03, - 0x46, 0x00, 0x52, 0x00, 0x58, 0x00, 0x5a, 0x00, 0x5c, 0x00, 0x5d, 0x00, - 0x5d, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, 0x5e, 0x00, - 0x5e, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x31, 0x00, 0x5f, 0x00, 0x85, 0x00, 0x92, 0x00, 0x97, 0x00, 0x9a, - 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x9e, 0x00, 0x9e, - 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3c, 0x47, 0x38, 0x4b, 0x35, 0x4e, 0x32, 0x4f, - 0x31, 0x51, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, 0x2c, 0x56, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x58, 0x2a, 0x59, 0x40, 0x11, 0x36, 0x1a, - 0x2f, 0x21, 0x2c, 0x27, 0x29, 0x2c, 0x27, 0x30, 0x26, 0x34, 0x25, 0x37, - 0x25, 0x3a, 0x24, 0x3d, 0x24, 0x40, 0x24, 0x42, 0x24, 0x44, 0x23, 0x45, - 0x23, 0x47, 0x23, 0x48, 0x23, 0x49, 0x23, 0x4a, 0x23, 0x4c, 0x23, 0x4c, - 0x00, 0xaa, 0x00, 0x98, 0x00, 0x8c, 0x02, 0x83, 0x03, 0x7d, 0x05, 0x79, - 0x07, 0x76, 0x09, 0x73, 0x0a, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, 0x0e, 0x6d, - 0x0f, 0x6c, 0x10, 0x6b, 0x11, 0x6a, 0x12, 0x6a, 0x13, 0x69, 0x13, 0x68, - 0x14, 0x68, 0x15, 0x68, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x5f, 0x00, 0x20, 0x1f, 0x00, 0x3c, 0x00, - 0x4a, 0x00, 0x51, 0x00, 0x55, 0x00, 0x58, 0x00, 0x59, 0x00, 0x5b, 0x00, - 0x5b, 0x00, 0x5c, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, 0x5d, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x00, 0x20, - 0x00, 0x5f, 0x00, 0x7c, 0x00, 0x8a, 0x00, 0x91, 0x00, 0x95, 0x00, 0x97, - 0x00, 0x99, 0x00, 0x9a, 0x00, 0x9b, 0x00, 0x9c, 0x00, 0x9c, 0x00, 0x9d, - 0x00, 0x9d, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x46, 0x39, 0x4a, 0x36, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x50, - 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, - 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x2a, 0x57, 0x2a, 0x57, 0x41, 0x10, 0x38, 0x17, 0x32, 0x1d, 0x2e, 0x22, - 0x2b, 0x27, 0x29, 0x2b, 0x27, 0x2f, 0x26, 0x32, 0x25, 0x35, 0x25, 0x38, - 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, 0x23, 0x44, - 0x23, 0x45, 0x23, 0x46, 0x23, 0x48, 0x23, 0x48, 0x00, 0xac, 0x00, 0x9d, - 0x00, 0x91, 0x01, 0x89, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7a, 0x07, 0x77, - 0x08, 0x75, 0x09, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0d, 0x6e, 0x0e, 0x6e, - 0x0e, 0x6c, 0x0f, 0x6c, 0x10, 0x6b, 0x10, 0x6a, 0x11, 0x6a, 0x12, 0x6a, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x85, 0x00, 0x5f, 0x00, 0x1f, 0x0d, 0x00, 0x28, 0x00, 0x39, 0x00, - 0x43, 0x00, 0x4a, 0x00, 0x4e, 0x00, 0x52, 0x00, 0x54, 0x00, 0x56, 0x00, - 0x57, 0x00, 0x59, 0x00, 0x59, 0x00, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x46, 0x00, 0x1f, 0x00, 0x00, 0x1f, 0x00, 0x4d, - 0x00, 0x68, 0x00, 0x79, 0x00, 0x83, 0x00, 0x8a, 0x00, 0x8e, 0x00, 0x92, - 0x00, 0x94, 0x00, 0x96, 0x00, 0x97, 0x00, 0x98, 0x00, 0x99, 0x00, 0x9a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x39, 0x48, - 0x37, 0x4a, 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x54, 0x2d, 0x56, - 0x2c, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x2a, 0x57, - 0x42, 0x0f, 0x39, 0x15, 0x33, 0x1a, 0x2f, 0x1f, 0x2d, 0x24, 0x2a, 0x27, - 0x29, 0x2b, 0x27, 0x2e, 0x26, 0x31, 0x26, 0x34, 0x25, 0x36, 0x25, 0x38, - 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3e, 0x24, 0x40, 0x24, 0x41, 0x24, 0x43, - 0x23, 0x44, 0x23, 0x45, 0x00, 0xae, 0x00, 0xa1, 0x00, 0x96, 0x00, 0x8d, - 0x01, 0x87, 0x02, 0x82, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x78, 0x07, 0x76, - 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x70, 0x0c, 0x6f, 0x0d, 0x6e, - 0x0e, 0x6d, 0x0f, 0x6c, 0x10, 0x6c, 0x10, 0x6b, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x92, 0x00, 0x7c, - 0x00, 0x4d, 0x00, 0x1f, 0x02, 0x00, 0x1a, 0x00, 0x2a, 0x00, 0x36, 0x00, - 0x3e, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4d, 0x00, 0x4f, 0x00, 0x52, 0x00, - 0x53, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x52, 0x00, 0x3c, 0x00, 0x0d, 0x00, 0x00, 0x1f, 0x00, 0x42, 0x00, 0x5a, - 0x00, 0x6a, 0x00, 0x76, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x89, 0x00, 0x8c, - 0x00, 0x8f, 0x00, 0x91, 0x00, 0x93, 0x00, 0x95, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x45, 0x3a, 0x47, 0x38, 0x4a, 0x35, 0x4a, - 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2d, 0x55, - 0x2c, 0x57, 0x2b, 0x57, 0x2a, 0x57, 0x2a, 0x57, 0x43, 0x0e, 0x3b, 0x13, - 0x35, 0x18, 0x31, 0x1c, 0x2e, 0x21, 0x2c, 0x24, 0x2a, 0x28, 0x28, 0x2b, - 0x27, 0x2e, 0x27, 0x30, 0x26, 0x33, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, - 0x25, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x24, 0x40, 0x24, 0x42, - 0x00, 0xaf, 0x00, 0xa3, 0x00, 0x99, 0x00, 0x91, 0x00, 0x8b, 0x02, 0x86, - 0x02, 0x81, 0x03, 0x7e, 0x05, 0x7b, 0x06, 0x79, 0x07, 0x77, 0x08, 0x75, - 0x09, 0x74, 0x0a, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x6f, 0x0d, 0x6e, - 0x0e, 0x6e, 0x0e, 0x6d, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x68, 0x00, 0x42, - 0x00, 0x1f, 0x00, 0x04, 0x10, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x34, 0x00, - 0x3b, 0x00, 0x41, 0x00, 0x45, 0x00, 0x49, 0x00, 0x4b, 0x00, 0x4e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x4a, 0x00, - 0x28, 0x00, 0x02, 0x00, 0x00, 0x1f, 0x00, 0x3b, 0x00, 0x4f, 0x00, 0x5f, - 0x00, 0x6b, 0x00, 0x74, 0x00, 0x7b, 0x00, 0x80, 0x00, 0x85, 0x00, 0x88, - 0x00, 0x8b, 0x00, 0x8d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x44, 0x3b, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4b, 0x34, 0x4e, - 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, - 0x2c, 0x56, 0x2b, 0x57, 0x44, 0x0e, 0x3c, 0x12, 0x36, 0x16, 0x33, 0x1a, - 0x30, 0x1e, 0x2d, 0x22, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x27, 0x2d, - 0x27, 0x30, 0x26, 0x32, 0x26, 0x34, 0x25, 0x35, 0x25, 0x37, 0x25, 0x39, - 0x24, 0x3b, 0x24, 0x3c, 0x24, 0x3d, 0x24, 0x3f, 0x00, 0xb0, 0x00, 0xa5, - 0x00, 0x9d, 0x00, 0x95, 0x00, 0x8e, 0x01, 0x89, 0x02, 0x85, 0x03, 0x81, - 0x04, 0x7e, 0x05, 0x7c, 0x06, 0x7a, 0x07, 0x78, 0x07, 0x76, 0x09, 0x75, - 0x09, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x0c, 0x70, 0x0d, 0x6e, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9a, 0x00, 0x91, 0x00, 0x79, 0x00, 0x5a, 0x00, 0x3b, 0x00, 0x1f, - 0x00, 0x09, 0x08, 0x00, 0x17, 0x00, 0x22, 0x00, 0x2c, 0x00, 0x33, 0x00, - 0x39, 0x00, 0x3e, 0x00, 0x42, 0x00, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5a, 0x00, 0x51, 0x00, 0x39, 0x00, 0x1a, 0x00, - 0x00, 0x04, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x48, 0x00, 0x57, 0x00, 0x62, - 0x00, 0x6b, 0x00, 0x73, 0x00, 0x79, 0x00, 0x7e, 0x00, 0x82, 0x00, 0x85, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x44, 0x3b, 0x46, - 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x55, - 0x44, 0x0d, 0x3d, 0x11, 0x38, 0x15, 0x34, 0x19, 0x31, 0x1c, 0x2e, 0x20, - 0x2d, 0x23, 0x2b, 0x25, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2f, - 0x26, 0x31, 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, - 0x24, 0x3a, 0x24, 0x3c, 0x00, 0xb1, 0x00, 0xa8, 0x00, 0x9f, 0x00, 0x98, - 0x00, 0x91, 0x00, 0x8c, 0x01, 0x88, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7e, - 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x74, - 0x0a, 0x73, 0x0b, 0x73, 0x0b, 0x71, 0x0c, 0x71, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9b, 0x00, 0x95, - 0x00, 0x83, 0x00, 0x6a, 0x00, 0x4f, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x0c, - 0x03, 0x00, 0x10, 0x00, 0x1b, 0x00, 0x24, 0x00, 0x2c, 0x00, 0x32, 0x00, - 0x37, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5c, 0x00, 0x55, 0x00, 0x43, 0x00, 0x2a, 0x00, 0x10, 0x00, 0x00, 0x09, - 0x00, 0x1f, 0x00, 0x33, 0x00, 0x43, 0x00, 0x50, 0x00, 0x5b, 0x00, 0x64, - 0x00, 0x6b, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0d, 0x3e, 0x10, - 0x39, 0x14, 0x35, 0x17, 0x32, 0x1b, 0x30, 0x1e, 0x2e, 0x21, 0x2c, 0x23, - 0x2b, 0x26, 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2d, 0x27, 0x2e, 0x26, 0x30, - 0x26, 0x32, 0x25, 0x34, 0x25, 0x35, 0x25, 0x36, 0x25, 0x38, 0x25, 0x39, - 0x00, 0xb1, 0x00, 0xa9, 0x00, 0xa1, 0x00, 0x9a, 0x00, 0x94, 0x00, 0x8f, - 0x01, 0x8b, 0x02, 0x87, 0x02, 0x84, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, - 0x06, 0x7b, 0x06, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x09, 0x74, - 0x0a, 0x73, 0x0b, 0x72, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x97, 0x00, 0x8a, 0x00, 0x76, - 0x00, 0x5f, 0x00, 0x48, 0x00, 0x33, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x0b, 0x00, 0x15, 0x00, 0x1e, 0x00, 0x25, 0x00, 0x2c, 0x00, 0x31, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x58, 0x00, - 0x4a, 0x00, 0x36, 0x00, 0x1f, 0x00, 0x08, 0x00, 0x00, 0x0c, 0x00, 0x1f, - 0x00, 0x30, 0x00, 0x3f, 0x00, 0x4b, 0x00, 0x55, 0x00, 0x5e, 0x00, 0x65, - 0x00, 0x6c, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x42, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, - 0x35, 0x4b, 0x35, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x52, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x44, 0x0c, 0x3f, 0x10, 0x3a, 0x13, 0x36, 0x16, - 0x33, 0x19, 0x30, 0x1c, 0x2e, 0x1f, 0x2d, 0x21, 0x2b, 0x24, 0x2a, 0x26, - 0x2a, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x26, 0x30, 0x26, 0x31, - 0x26, 0x33, 0x25, 0x34, 0x25, 0x36, 0x25, 0x37, 0x00, 0xb2, 0x00, 0xaa, - 0x00, 0xa2, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x91, 0x00, 0x8d, 0x01, 0x89, - 0x02, 0x86, 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x06, 0x7b, - 0x06, 0x79, 0x07, 0x79, 0x07, 0x77, 0x08, 0x76, 0x09, 0x75, 0x09, 0x74, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x8e, 0x00, 0x7e, 0x00, 0x6b, 0x00, 0x57, - 0x00, 0x43, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x11, 0x00, 0x04, 0x07, 0x00, - 0x10, 0x00, 0x19, 0x00, 0x20, 0x00, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x4e, 0x00, 0x3e, 0x00, - 0x2b, 0x00, 0x17, 0x00, 0x03, 0x00, 0x00, 0x0f, 0x00, 0x1f, 0x00, 0x2e, - 0x00, 0x3b, 0x00, 0x46, 0x00, 0x50, 0x00, 0x59, 0x00, 0x60, 0x00, 0x66, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x46, - 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, - 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x51, 0x2f, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, - 0x45, 0x0c, 0x3f, 0x0f, 0x3a, 0x12, 0x37, 0x15, 0x34, 0x18, 0x32, 0x1b, - 0x30, 0x1d, 0x2e, 0x20, 0x2d, 0x22, 0x2b, 0x24, 0x2a, 0x26, 0x2a, 0x28, - 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2e, 0x27, 0x2f, 0x26, 0x31, 0x26, 0x32, - 0x25, 0x33, 0x25, 0x35, 0x00, 0xb2, 0x00, 0xab, 0x00, 0xa4, 0x00, 0x9e, - 0x00, 0x98, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8b, 0x01, 0x88, 0x02, 0x85, - 0x02, 0x83, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, - 0x07, 0x79, 0x07, 0x78, 0x07, 0x76, 0x09, 0x76, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x9a, - 0x00, 0x92, 0x00, 0x84, 0x00, 0x74, 0x00, 0x62, 0x00, 0x50, 0x00, 0x3f, - 0x00, 0x2e, 0x00, 0x1f, 0x00, 0x12, 0x00, 0x06, 0x03, 0x00, 0x0c, 0x00, - 0x14, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5b, 0x00, 0x52, 0x00, 0x45, 0x00, 0x34, 0x00, 0x22, 0x00, - 0x10, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x2d, 0x00, 0x38, - 0x00, 0x43, 0x00, 0x4c, 0x00, 0x54, 0x00, 0x5b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, 0x39, 0x46, - 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x33, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x51, - 0x30, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0f, - 0x3b, 0x11, 0x38, 0x14, 0x35, 0x17, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, - 0x2d, 0x21, 0x2c, 0x22, 0x2b, 0x25, 0x2a, 0x26, 0x29, 0x28, 0x28, 0x2a, - 0x28, 0x2c, 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x30, 0x26, 0x32, 0x25, 0x33, - 0x00, 0xb2, 0x00, 0xac, 0x00, 0xa5, 0x00, 0x9f, 0x00, 0x9a, 0x00, 0x96, - 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8a, 0x02, 0x88, 0x02, 0x85, 0x02, 0x83, - 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7a, 0x06, 0x79, - 0x07, 0x78, 0x07, 0x77, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9b, 0x00, 0x94, 0x00, 0x89, - 0x00, 0x7b, 0x00, 0x6b, 0x00, 0x5b, 0x00, 0x4b, 0x00, 0x3b, 0x00, 0x2d, - 0x00, 0x1f, 0x00, 0x13, 0x00, 0x08, 0x00, 0x00, 0x09, 0x00, 0x11, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5b, 0x00, - 0x54, 0x00, 0x49, 0x00, 0x3b, 0x00, 0x2c, 0x00, 0x1b, 0x00, 0x0b, 0x00, - 0x00, 0x04, 0x00, 0x12, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x36, 0x00, 0x40, - 0x00, 0x49, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x42, 0x3d, 0x44, 0x3a, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, 0x33, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x53, - 0x2e, 0x53, 0x2e, 0x53, 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x38, 0x13, - 0x36, 0x16, 0x33, 0x18, 0x31, 0x1b, 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x21, - 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, - 0x27, 0x2d, 0x27, 0x2f, 0x26, 0x2f, 0x26, 0x31, 0x00, 0xb3, 0x00, 0xac, - 0x00, 0xa7, 0x00, 0xa1, 0x00, 0x9b, 0x00, 0x97, 0x00, 0x93, 0x00, 0x8f, - 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x82, 0x03, 0x81, - 0x04, 0x7f, 0x05, 0x7d, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x79, 0x07, 0x79, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9c, 0x00, 0x96, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x73, - 0x00, 0x64, 0x00, 0x55, 0x00, 0x46, 0x00, 0x38, 0x00, 0x2b, 0x00, 0x1f, - 0x00, 0x14, 0x00, 0x0a, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5c, 0x00, 0x56, 0x00, 0x4d, 0x00, - 0x41, 0x00, 0x33, 0x00, 0x24, 0x00, 0x15, 0x00, 0x07, 0x00, 0x00, 0x06, - 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x34, 0x00, 0x3d, 0x00, 0x46, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x44, - 0x3b, 0x46, 0x39, 0x46, 0x39, 0x47, 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x50, 0x30, 0x52, 0x2f, 0x53, - 0x45, 0x0c, 0x40, 0x0e, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x18, - 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1e, 0x2d, 0x20, 0x2d, 0x22, 0x2b, 0x23, - 0x2b, 0x25, 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2d, - 0x27, 0x2f, 0x26, 0x2f, 0x00, 0xb3, 0x00, 0xad, 0x00, 0xa7, 0x00, 0xa3, - 0x00, 0x9d, 0x00, 0x99, 0x00, 0x95, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8b, - 0x02, 0x88, 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, - 0x05, 0x7e, 0x05, 0x7c, 0x06, 0x7b, 0x06, 0x7a, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9c, - 0x00, 0x97, 0x00, 0x8f, 0x00, 0x85, 0x00, 0x79, 0x00, 0x6b, 0x00, 0x5e, - 0x00, 0x50, 0x00, 0x43, 0x00, 0x36, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x15, - 0x00, 0x0c, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5e, 0x00, 0x5d, 0x00, 0x57, 0x00, 0x4f, 0x00, 0x45, 0x00, 0x39, 0x00, - 0x2c, 0x00, 0x1e, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x08, 0x00, 0x14, - 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x33, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, - 0x39, 0x46, 0x39, 0x49, 0x36, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4c, 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x52, 0x45, 0x0b, 0x41, 0x0e, - 0x3d, 0x10, 0x3a, 0x12, 0x37, 0x14, 0x34, 0x17, 0x32, 0x19, 0x31, 0x1b, - 0x30, 0x1d, 0x2e, 0x1f, 0x2d, 0x20, 0x2c, 0x22, 0x2b, 0x24, 0x2a, 0x25, - 0x2a, 0x27, 0x29, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x27, 0x2e, - 0x00, 0xb3, 0x00, 0xae, 0x00, 0xa8, 0x00, 0xa3, 0x00, 0x9f, 0x00, 0x9a, - 0x00, 0x96, 0x00, 0x93, 0x00, 0x8f, 0x01, 0x8d, 0x01, 0x8a, 0x02, 0x88, - 0x02, 0x86, 0x02, 0x84, 0x03, 0x82, 0x03, 0x81, 0x04, 0x7f, 0x05, 0x7e, - 0x05, 0x7c, 0x06, 0x7c, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x98, 0x00, 0x91, - 0x00, 0x88, 0x00, 0x7e, 0x00, 0x72, 0x00, 0x65, 0x00, 0x59, 0x00, 0x4c, - 0x00, 0x40, 0x00, 0x34, 0x00, 0x2a, 0x00, 0x1f, 0x00, 0x16, 0x00, 0x0d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, - 0x59, 0x00, 0x52, 0x00, 0x49, 0x00, 0x3e, 0x00, 0x32, 0x00, 0x25, 0x00, - 0x19, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x0a, 0x00, 0x15, 0x00, 0x1f, - 0x00, 0x29, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3d, 0x42, 0x3d, 0x43, 0x3c, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x47, - 0x38, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4d, - 0x33, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x45, 0x0b, 0x41, 0x0e, 0x3e, 0x10, 0x3a, 0x11, - 0x38, 0x14, 0x35, 0x16, 0x33, 0x18, 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, - 0x2e, 0x20, 0x2d, 0x21, 0x2b, 0x22, 0x2b, 0x24, 0x2a, 0x25, 0x2a, 0x27, - 0x28, 0x28, 0x28, 0x2a, 0x28, 0x2c, 0x27, 0x2c, 0x00, 0xb3, 0x00, 0xae, - 0x00, 0xa9, 0x00, 0xa4, 0x00, 0xa0, 0x00, 0x9b, 0x00, 0x98, 0x00, 0x94, - 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, 0x01, 0x89, 0x02, 0x87, 0x02, 0x86, - 0x02, 0x84, 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x05, 0x7c, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9e, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x93, 0x00, 0x8b, 0x00, 0x82, - 0x00, 0x77, 0x00, 0x6c, 0x00, 0x60, 0x00, 0x54, 0x00, 0x49, 0x00, 0x3d, - 0x00, 0x33, 0x00, 0x29, 0x00, 0x1f, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x5e, 0x00, 0x5d, 0x00, 0x59, 0x00, 0x53, 0x00, - 0x4b, 0x00, 0x42, 0x00, 0x37, 0x00, 0x2c, 0x00, 0x20, 0x00, 0x14, 0x00, - 0x09, 0x00, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x16, 0x00, 0x1f, 0x00, 0x28, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, - 0x3d, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x46, 0x39, 0x4a, 0x36, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4b, 0x34, 0x4e, 0x32, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x46, 0x0b, 0x42, 0x0d, 0x3e, 0x10, 0x3b, 0x11, 0x38, 0x13, 0x36, 0x16, - 0x34, 0x17, 0x32, 0x19, 0x30, 0x1b, 0x30, 0x1d, 0x2e, 0x1e, 0x2d, 0x20, - 0x2d, 0x22, 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x25, 0x2a, 0x28, 0x28, 0x28, - 0x28, 0x2a, 0x28, 0x2c, 0x00, 0xb3, 0x00, 0xae, 0x00, 0xaa, 0x00, 0xa5, - 0x00, 0xa1, 0x00, 0x9d, 0x00, 0x99, 0x00, 0x96, 0x00, 0x93, 0x00, 0x90, - 0x00, 0x8d, 0x01, 0x8b, 0x02, 0x89, 0x02, 0x87, 0x02, 0x85, 0x02, 0x83, - 0x03, 0x82, 0x03, 0x80, 0x04, 0x7f, 0x05, 0x7e, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x9d, - 0x00, 0x9a, 0x00, 0x95, 0x00, 0x8d, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, - 0x00, 0x66, 0x00, 0x5b, 0x00, 0x50, 0x00, 0x46, 0x00, 0x3b, 0x00, 0x31, - 0x00, 0x28, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x5f, 0x00, 0x5d, 0x00, 0x5a, 0x00, 0x55, 0x00, 0x4e, 0x00, 0x45, 0x00, - 0x3c, 0x00, 0x31, 0x00, 0x26, 0x00, 0x1b, 0x00, 0x11, 0x00, 0x06, 0x00, - 0x00, 0x03, 0x00, 0x0d, 0x00, 0x17, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x3d, 0x42, 0x3d, 0x42, 0x3d, 0x45, 0x3a, 0x46, - 0x39, 0x46, 0x39, 0x46, 0x39, 0x48, 0x37, 0x4a, 0x35, 0x4a, 0x35, 0x4a, - 0x35, 0x4a, 0x35, 0x4a, 0x35, 0x4c, 0x34, 0x4f, 0x31, 0x4f, 0x31, 0x4f, - 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x31, 0x4f, 0x46, 0x0b, 0x42, 0x0d, - 0x3e, 0x0f, 0x3c, 0x11, 0x39, 0x13, 0x37, 0x15, 0x34, 0x16, 0x33, 0x18, - 0x32, 0x1a, 0x30, 0x1c, 0x2e, 0x1d, 0x2e, 0x20, 0x2d, 0x20, 0x2c, 0x22, - 0x2b, 0x23, 0x2b, 0x25, 0x2a, 0x26, 0x2a, 0x28, 0x28, 0x28, 0x28, 0x2a, - 0x00, 0xb4, 0x00, 0xaf, 0x00, 0xaa, 0x00, 0xa6, 0x00, 0xa2, 0x00, 0x9e, - 0x00, 0x9a, 0x00, 0x97, 0x00, 0x94, 0x00, 0x91, 0x00, 0x8e, 0x01, 0x8c, - 0x01, 0x8a, 0x02, 0x88, 0x02, 0x86, 0x02, 0x85, 0x02, 0x83, 0x03, 0x82, - 0x03, 0x80, 0x04, 0x7f, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, - 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x23, 0x60, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, 0x00, - 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, 0x00, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x16, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, - 0x00, 0x0c, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, - 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, - 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, - 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1b, 0x00, 0x17, 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, - 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, - 0x00, 0x15, 0x00, 0x10, 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, - 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, - 0x00, 0x0e, 0x00, 0x0a, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, - 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, - 0x00, 0x09, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, - 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, - 0x00, 0x19, 0x00, 0x16, 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, - 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, - 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, - 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, - 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, - 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, - 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, - 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, - 0x00, 0x10, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, - 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, - 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, - 0x00, 0x0c, 0x00, 0x09, 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x4c, 0x04, 0x11, 0x09, 0x01, 0x11, 0x00, 0x17, - 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, 0x00, 0x18, 0x00, 0x1a, - 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, - 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x2b, 0x00, 0x21, - 0x00, 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x87, 0x00, 0x39, 0x01, 0x0f, 0x07, 0x02, 0x0c, 0x00, 0x12, 0x00, 0x17, - 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x9f, 0x00, 0x5f, 0x00, - 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, - 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, - 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x0b, 0x00, 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, - 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, - 0x00, 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, - 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0x87, 0x00, - 0x32, 0x00, 0x19, 0x05, 0x0b, 0x09, 0x03, 0x0b, 0x00, 0x0e, 0x00, 0x12, - 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, - 0x00, 0x1c, 0x00, 0x1c, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, - 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, - 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x08, 0x00, 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, - 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, - 0x00, 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, - 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, - 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xcd, 0x00, 0xaf, 0x00, 0x6e, 0x00, 0x31, 0x00, - 0x1e, 0x04, 0x12, 0x07, 0x0a, 0x09, 0x04, 0x0a, 0x00, 0x0b, 0x00, 0x0f, - 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, - 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, - 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, - 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, - 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, - 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x2c, 0x00, 0x28, - 0x00, 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, - 0x00, 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, - 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, 0x00, 0x30, 0x00, 0x22, 0x03, - 0x17, 0x06, 0x0f, 0x07, 0x0a, 0x09, 0x05, 0x0a, 0x02, 0x0b, 0x00, 0x0c, - 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0xd7, 0x00, 0xc9, 0x00, - 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, - 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x09, - 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, - 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, - 0x00, 0x18, 0x00, 0x19, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, - 0x00, 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, - 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd8, 0x00, 0xcb, 0x00, - 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, 0x00, 0x24, 0x02, 0x1b, 0x05, - 0x14, 0x06, 0x0e, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x03, 0x0b, 0x00, 0x0b, - 0x00, 0x0d, 0x00, 0x0f, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, - 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, - 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, 0x00, 0x02, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, - 0x00, 0x0e, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, - 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, - 0x00, 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, - 0x00, 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xb8, 0x00, 0x96, 0x00, - 0x71, 0x00, 0x4e, 0x00, 0x30, 0x00, 0x26, 0x02, 0x1e, 0x04, 0x17, 0x06, - 0x12, 0x07, 0x0d, 0x08, 0x09, 0x09, 0x06, 0x0a, 0x04, 0x0a, 0x01, 0x0b, - 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, - 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, - 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, - 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x14, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, - 0x00, 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, - 0x00, 0x13, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, 0x00, 0x87, 0x00, 0x67, 0x00, - 0x4a, 0x00, 0x30, 0x00, 0x27, 0x02, 0x20, 0x03, 0x1a, 0x05, 0x14, 0x06, - 0x10, 0x07, 0x0c, 0x08, 0x09, 0x09, 0x07, 0x0a, 0xdc, 0x00, 0xd7, 0x00, - 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, - 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, - 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, - 0x00, 0x10, 0x00, 0x11, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, - 0x00, 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, - 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, 0x00, 0x60, 0x00, 0x46, 0x00, - 0x30, 0x00, 0x28, 0x01, 0x21, 0x03, 0x1c, 0x04, 0x17, 0x06, 0x13, 0x07, - 0x0f, 0x08, 0x0c, 0x08, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, - 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, - 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x03, 0x00, 0x06, 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, - 0x00, 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, - 0x00, 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xba, 0x00, - 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, 0x00, 0x44, 0x00, 0x2f, 0x00, - 0x29, 0x01, 0x23, 0x03, 0x1e, 0x04, 0x19, 0x05, 0x15, 0x06, 0x11, 0x07, - 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, - 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, - 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, - 0x00, 0x05, 0x00, 0x08, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, - 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, - 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, 0x00, 0xad, 0x00, 0x98, 0x00, - 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, 0x00, 0x2f, 0x00, 0x29, 0x01, - 0x24, 0x02, 0x1f, 0x04, 0x1b, 0x05, 0x17, 0x06, 0xdd, 0x00, 0xdb, 0x00, - 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, - 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, - 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, - 0x00, 0x07, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, - 0x00, 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, - 0x00, 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, - 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, 0x00, 0x8e, 0x00, 0x79, 0x00, - 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x25, 0x02, - 0x20, 0x03, 0x1c, 0x04, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, - 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, - 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, - 0x00, 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, - 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, 0x00, 0x72, 0x00, 0x60, 0x00, - 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x00, 0x2a, 0x01, 0x26, 0x02, 0x21, 0x03, - 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, - 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, - 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, - 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb1, 0x00, - 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, 0x00, 0x5c, 0x00, 0x4c, 0x00, - 0x3d, 0x00, 0x2f, 0x00, 0x2b, 0x01, 0x26, 0x02, 0xde, 0x00, 0xdc, 0x00, - 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, - 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, - 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, - 0x00, 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, 0x00, 0xa8, 0x00, 0x98, 0x00, - 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, 0x00, 0x4a, 0x00, 0x3c, 0x00, - 0x2f, 0x00, 0x2b, 0x01, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, - 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, - 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, - 0x00, 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, - 0x00, 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd0, 0x00, - 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, 0x00, 0x91, 0x00, 0x82, 0x00, - 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, 0x00, 0x3b, 0x00, 0x2f, 0x00, - 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, - 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, - 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, 0x00, - 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xde, 0x00, 0x4c, 0x04, 0x87, 0x00, 0xbc, 0x00, 0xcd, 0x00, - 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xa6, 0x00, 0x76, 0x00, - 0x9b, 0x00, 0xac, 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xde, 0x00, 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, - 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, - 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, 0x00, - 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, - 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, - 0x11, 0x09, 0x39, 0x01, 0x87, 0x00, 0xaf, 0x00, 0xc1, 0x00, 0xcb, 0x00, - 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, - 0xab, 0x00, 0xc0, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, - 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, - 0x23, 0x03, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, - 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, - 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, 0x00, - 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, - 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x01, 0x11, 0x0f, 0x07, - 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, 0x00, 0xb8, 0x00, 0xc1, 0x00, - 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd6, 0x00, - 0xd7, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9b, 0x00, 0x75, 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, - 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, - 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x03, 0x09, 0x1f, 0x00, - 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, - 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, - 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, - 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, - 0xca, 0x00, 0xcd, 0x00, 0x00, 0x17, 0x02, 0x0c, 0x19, 0x05, 0x31, 0x00, - 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, 0x00, 0xb2, 0x00, 0xba, 0x00, - 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xce, 0x00, 0xd0, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xac, 0x00, 0x96, 0x00, - 0x5b, 0x00, 0x13, 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, - 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, - 0xca, 0x00, 0xcd, 0x00, 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, - 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, - 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, - 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, - 0x00, 0x1b, 0x00, 0x12, 0x0b, 0x09, 0x1e, 0x04, 0x30, 0x00, 0x55, 0x00, - 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, 0x00, 0xad, 0x00, 0xb5, 0x00, - 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, - 0x03, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, - 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, - 0x00, 0x18, 0x00, 0x0a, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, - 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, - 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, - 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, - 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1c, 0x00, 0x17, - 0x03, 0x0b, 0x12, 0x07, 0x22, 0x03, 0x30, 0x00, 0x4e, 0x00, 0x67, 0x00, - 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, 0x00, 0xaa, 0x00, 0xb1, 0x00, - 0xb6, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xcd, 0x00, 0xc0, 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, - 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, - 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x1a, 0x00, 0x11, - 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, - 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, - 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, - 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, - 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x0e, 0x0a, 0x09, - 0x17, 0x06, 0x24, 0x02, 0x30, 0x00, 0x4a, 0x00, 0x60, 0x00, 0x72, 0x00, - 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, 0x00, - 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, - 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, - 0x99, 0x00, 0xa0, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, - 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, - 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, 0x00, - 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, - 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x04, 0x0a, 0x0f, 0x07, 0x1b, 0x05, - 0x26, 0x02, 0x30, 0x00, 0x46, 0x00, 0x5a, 0x00, 0x6b, 0x00, 0x79, 0x00, - 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, - 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, - 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, - 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, - 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, - 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, - 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x15, 0x00, 0x0b, 0x0a, 0x09, 0x14, 0x06, 0x1e, 0x04, 0x27, 0x02, - 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, 0x00, 0x72, 0x00, 0x7e, 0x00, - 0x88, 0x00, 0x91, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, - 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, - 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x1d, 0x00, 0x1a, - 0x00, 0x0f, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, - 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, - 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, - 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x17, 0x00, 0x0f, - 0x05, 0x0a, 0x0e, 0x08, 0x17, 0x06, 0x20, 0x03, 0x28, 0x01, 0x2f, 0x00, - 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, 0x00, 0x78, 0x00, 0x82, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, - 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, - 0x19, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, - 0x5c, 0x00, 0x68, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, - 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, - 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, - 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x02, 0x0b, 0x09, 0x09, - 0x12, 0x07, 0x1a, 0x05, 0x21, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x40, 0x00, - 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, - 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, - 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, - 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, - 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, - 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1f, 0x00, 0x1d, - 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x06, 0x0a, 0x0d, 0x08, 0x14, 0x06, - 0x1c, 0x04, 0x23, 0x03, 0x29, 0x01, 0x2f, 0x00, 0x3e, 0x00, 0x4c, 0x00, - 0x59, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, - 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, - 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, - 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, - 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x16, - 0x00, 0x0f, 0x03, 0x0b, 0x09, 0x09, 0x10, 0x07, 0x17, 0x06, 0x1e, 0x04, - 0x24, 0x02, 0x2a, 0x01, 0x2f, 0x00, 0x3d, 0x00, 0x4a, 0x00, 0x56, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, - 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, - 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, - 0x22, 0x00, 0x30, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, - 0x00, 0x05, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, - 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, 0x11, 0x00, 0x0b, - 0x06, 0x0a, 0x0c, 0x08, 0x13, 0x07, 0x19, 0x05, 0x1f, 0x04, 0x25, 0x02, - 0x2a, 0x01, 0x2f, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, - 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, - 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, - 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, - 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, - 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1e, - 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x04, 0x0a, 0x09, 0x09, - 0x0f, 0x08, 0x15, 0x06, 0x1b, 0x05, 0x20, 0x03, 0x26, 0x02, 0x2b, 0x01, - 0x2f, 0x00, 0x3b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, - 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, - 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x1f, 0x00, 0x1d, - 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, - 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, - 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, - 0x00, 0x15, 0x00, 0x0f, 0x01, 0x0b, 0x07, 0x0a, 0x0c, 0x08, 0x11, 0x07, - 0x17, 0x06, 0x1c, 0x04, 0x21, 0x03, 0x26, 0x02, 0x2b, 0x01, 0x2f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, - 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, - 0x00, 0x0e, 0x00, 0x05, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, - 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x61, 0x01, 0x9f, 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, - 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, - 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x61, 0x01, 0x23, 0x03, 0x03, 0x09, 0x00, 0x13, - 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, - 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x23, 0x03, 0x5f, 0x00, - 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, - 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, - 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x00, 0x04, 0x01, 0x00, 0x0a, 0x00, 0x11, - 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, - 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x09, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, - 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, - 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, 0x00, - 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x00, 0x00, 0x03, 0x00, 0x0a, - 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, - 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x13, 0x04, 0x01, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, - 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, - 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, 0x00, - 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x05, - 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, - 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, - 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, - 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, 0x00, - 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x00, 0x00, 0x01, - 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x00, 0x11, 0x06, 0x00, 0x25, 0x00, - 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, - 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, 0x00, - 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, 0x00, - 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, 0x00, - 0x00, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, - 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, - 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, 0x00, - 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, 0x00, - 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, - 0x00, 0x0a, 0x09, 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, - 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, - 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, 0x00, - 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, 0x00, - 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, - 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, - 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, - 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, 0x00, - 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, 0x00, - 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x0b, 0x00, 0x1c, 0x00, - 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, - 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, 0x00, - 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, 0x00, - 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, - 0x00, 0x14, 0x00, 0x09, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, - 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, - 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, 0x00, - 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, 0x00, - 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, - 0x00, 0x01, 0x0c, 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, - 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, - 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, 0x00, - 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, 0x00, - 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, 0x05, 0x06, 0x00, - 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, - 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, 0x00, - 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, 0x00, - 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, - 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, - 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, - 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, 0x00, - 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, 0x00, - 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, - 0x00, 0x0c, 0x00, 0x02, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, - 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, - 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, 0x00, - 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, 0x00, - 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, - 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, - 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, 0x00, - 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, 0x00, - 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x6f, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xb2, 0x00, 0x6f, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x00, 0xa2, - 0x00, 0x4f, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x7f, 0x00, 0x3c, - 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1a, 0x00, - 0x12, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd6, 0x00, 0xc6, 0x00, 0x9c, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x01, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0e, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xd9, 0x00, 0xcd, - 0x00, 0xae, 0x00, 0x82, 0x00, 0x54, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, - 0x00, 0x6f, 0x00, 0x47, 0x00, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, - 0x00, 0x3d, 0x00, 0x1d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, - 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd7, - 0x00, 0xc7, 0x00, 0xb0, 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, - 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, - 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, - 0x00, 0xa0, 0x00, 0x84, 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, - 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, - 0x00, 0x78, 0x00, 0x5d, 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, - 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, - 0x00, 0xd1, 0x00, 0xc3, 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, - 0x00, 0x55, 0x00, 0x3e, 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, - 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, - 0x00, 0xb7, 0x00, 0xa4, 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, - 0x00, 0x39, 0x00, 0x24, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, - 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, - 0x00, 0x99, 0x00, 0x85, 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, - 0x00, 0x22, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, - 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, - 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd6, 0x00, 0xcd, 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, - 0x00, 0x7b, 0x00, 0x68, 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, - 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, - 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x37, 0x00, 0x6f, - 0x00, 0xb2, 0x00, 0xc8, 0x00, 0xd2, 0x00, 0xd6, 0x00, 0xd9, 0x00, 0xda, - 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xde, 0x00, 0xa6, 0x00, 0x76, 0x00, 0x9b, 0x00, 0xac, - 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x4c, 0x00, 0x87, - 0x00, 0xbc, 0x00, 0xcd, 0x00, 0xd4, 0x00, 0xd8, 0x00, 0xda, 0x00, 0xdb, - 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, - 0x00, 0xde, 0x00, 0xde, 0x01, 0x61, 0x00, 0x9f, 0x00, 0xc5, 0x00, 0xd2, - 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x6f, 0x00, 0xa2, - 0x00, 0xb9, 0x00, 0xc6, 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, - 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0x76, 0x00, 0x5b, 0x00, 0x75, 0x00, 0x96, 0x00, 0xab, 0x00, 0xc0, - 0x00, 0xcd, 0x00, 0xd2, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, - 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x11, 0x01, 0x39, 0x00, 0x87, 0x00, 0xaf, - 0x00, 0xc1, 0x00, 0xcb, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd7, 0x00, 0xd8, - 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, - 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, 0x00, 0xc9, 0x00, 0xd0, - 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x4f, 0x00, 0x7f, 0x00, 0x9c, - 0x00, 0xae, 0x00, 0xb9, 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, - 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x9b, 0x00, 0x75, - 0x00, 0x2b, 0x00, 0x5b, 0x00, 0x7f, 0x00, 0x9b, 0x00, 0xae, 0x00, 0xb9, - 0x00, 0xc2, 0x00, 0xc7, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, - 0x00, 0xd5, 0x00, 0xd6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x11, 0x01, 0x07, 0x0f, 0x00, 0x32, 0x00, 0x6e, 0x00, 0x93, 0x00, 0xaa, - 0x00, 0xb8, 0x00, 0xc1, 0x00, 0xc8, 0x00, 0xcc, 0x00, 0xd0, 0x00, 0xd2, - 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, 0x09, 0x03, 0x00, 0x1f, - 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, 0x00, 0xc3, 0x00, 0xc9, - 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, 0x00, 0xd7, 0x00, 0xd8, - 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x3c, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, - 0x00, 0xa5, 0x00, 0xb0, 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, - 0x00, 0xca, 0x00, 0xcd, 0x00, 0xac, 0x00, 0x96, 0x00, 0x5b, 0x00, 0x13, - 0x00, 0x41, 0x00, 0x66, 0x00, 0x82, 0x00, 0x97, 0x00, 0xa5, 0x00, 0xb0, - 0x00, 0xb8, 0x00, 0xbe, 0x00, 0xc3, 0x00, 0xc7, 0x00, 0xca, 0x00, 0xcd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x0c, 0x02, - 0x05, 0x19, 0x00, 0x31, 0x00, 0x5f, 0x00, 0x7f, 0x00, 0x96, 0x00, 0xa6, - 0x00, 0xb2, 0x00, 0xba, 0x00, 0xc0, 0x00, 0xc5, 0x00, 0xc9, 0x00, 0xcc, - 0x00, 0xce, 0x00, 0xd0, 0x13, 0x00, 0x01, 0x04, 0x00, 0x32, 0x00, 0x5f, - 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, 0x00, 0xbe, 0x00, 0xc4, - 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, 0x00, 0xd3, 0x00, 0xd4, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x01, 0x00, 0x2f, 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, - 0x00, 0xa0, 0x00, 0xa9, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, - 0x00, 0xbc, 0x00, 0xab, 0x00, 0x7f, 0x00, 0x41, 0x00, 0x03, 0x00, 0x2f, - 0x00, 0x54, 0x00, 0x6f, 0x00, 0x84, 0x00, 0x93, 0x00, 0xa0, 0x00, 0xa9, - 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbc, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x09, 0x0b, 0x04, 0x1e, - 0x00, 0x30, 0x00, 0x55, 0x00, 0x71, 0x00, 0x87, 0x00, 0x97, 0x00, 0xa4, - 0x00, 0xad, 0x00, 0xb5, 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc3, 0x00, 0xc7, - 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, 0x00, 0x5f, 0x00, 0x7a, - 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, 0x00, 0xbb, 0x00, 0xc0, - 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, - 0x00, 0x27, 0x00, 0x47, 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, - 0x00, 0x9c, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xcd, 0x00, 0xc0, - 0x00, 0x9b, 0x00, 0x66, 0x00, 0x2f, 0x00, 0x02, 0x00, 0x27, 0x00, 0x47, - 0x00, 0x60, 0x00, 0x74, 0x00, 0x84, 0x00, 0x91, 0x00, 0x9c, 0x00, 0xa4, - 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1c, 0x00, 0x17, 0x00, 0x0b, 0x03, 0x07, 0x12, 0x03, 0x22, 0x00, 0x30, - 0x00, 0x4e, 0x00, 0x67, 0x00, 0x7b, 0x00, 0x8b, 0x00, 0x98, 0x00, 0xa2, - 0x00, 0xaa, 0x00, 0xb1, 0x00, 0xb6, 0x00, 0xbb, 0x1a, 0x00, 0x11, 0x00, - 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, 0x00, 0x76, 0x00, 0x88, - 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, 0x00, 0xb8, 0x00, 0xbd, - 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, - 0x00, 0x3d, 0x00, 0x55, 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, - 0x00, 0x99, 0x00, 0xa0, 0x00, 0xd9, 0x00, 0xcd, 0x00, 0xae, 0x00, 0x82, - 0x00, 0x54, 0x00, 0x27, 0x00, 0x01, 0x00, 0x21, 0x00, 0x3d, 0x00, 0x55, - 0x00, 0x68, 0x00, 0x78, 0x00, 0x85, 0x00, 0x90, 0x00, 0x99, 0x00, 0xa0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x19, 0x00, - 0x0e, 0x00, 0x09, 0x0a, 0x06, 0x17, 0x02, 0x24, 0x00, 0x30, 0x00, 0x4a, - 0x00, 0x60, 0x00, 0x72, 0x00, 0x81, 0x00, 0x8e, 0x00, 0x98, 0x00, 0xa0, - 0x00, 0xa8, 0x00, 0xae, 0x1c, 0x00, 0x15, 0x00, 0x03, 0x00, 0x00, 0x14, - 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, 0x00, 0x82, 0x00, 0x90, - 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, 0x00, 0xb7, 0x00, 0xbb, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x36, - 0x00, 0x4b, 0x00, 0x5d, 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, - 0x00, 0xda, 0x00, 0xd2, 0x00, 0xb9, 0x00, 0x97, 0x00, 0x6f, 0x00, 0x47, - 0x00, 0x21, 0x00, 0x01, 0x00, 0x1d, 0x00, 0x36, 0x00, 0x4b, 0x00, 0x5d, - 0x00, 0x6d, 0x00, 0x7a, 0x00, 0x85, 0x00, 0x8e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x0a, 0x04, - 0x07, 0x0f, 0x05, 0x1b, 0x02, 0x26, 0x00, 0x30, 0x00, 0x46, 0x00, 0x5a, - 0x00, 0x6b, 0x00, 0x79, 0x00, 0x85, 0x00, 0x8f, 0x00, 0x98, 0x00, 0xa0, - 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, 0x00, 0x1f, 0x00, 0x36, - 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, 0x00, 0x8a, 0x00, 0x95, - 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, - 0x00, 0x55, 0x00, 0x64, 0x00, 0x70, 0x00, 0x7b, 0x00, 0xdb, 0x00, 0xd4, - 0x00, 0xc2, 0x00, 0xa5, 0x00, 0x84, 0x00, 0x60, 0x00, 0x3d, 0x00, 0x1d, - 0x00, 0x00, 0x00, 0x19, 0x00, 0x30, 0x00, 0x44, 0x00, 0x55, 0x00, 0x64, - 0x00, 0x70, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x15, 0x00, 0x0b, 0x00, 0x09, 0x0a, 0x06, 0x14, - 0x04, 0x1e, 0x02, 0x27, 0x00, 0x30, 0x00, 0x44, 0x00, 0x56, 0x00, 0x65, - 0x00, 0x72, 0x00, 0x7e, 0x00, 0x88, 0x00, 0x91, 0x1d, 0x00, 0x1a, 0x00, - 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, 0x00, 0x3c, 0x00, 0x4e, - 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, 0x00, 0x90, 0x00, 0x98, - 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, - 0x00, 0x5c, 0x00, 0x68, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc7, 0x00, 0xb0, - 0x00, 0x93, 0x00, 0x74, 0x00, 0x55, 0x00, 0x36, 0x00, 0x19, 0x00, 0x00, - 0x00, 0x17, 0x00, 0x2b, 0x00, 0x3e, 0x00, 0x4e, 0x00, 0x5c, 0x00, 0x68, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, - 0x17, 0x00, 0x0f, 0x00, 0x0a, 0x05, 0x08, 0x0e, 0x06, 0x17, 0x03, 0x20, - 0x01, 0x28, 0x00, 0x2f, 0x00, 0x42, 0x00, 0x52, 0x00, 0x60, 0x00, 0x6d, - 0x00, 0x78, 0x00, 0x82, 0x1e, 0x00, 0x1b, 0x00, 0x12, 0x00, 0x05, 0x00, - 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, 0x00, 0x50, 0x00, 0x5f, - 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, 0x00, 0x94, 0x00, 0x9b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x15, 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, - 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xcc, 0x00, 0xb8, 0x00, 0xa0, 0x00, 0x84, - 0x00, 0x68, 0x00, 0x4b, 0x00, 0x30, 0x00, 0x17, 0x00, 0x00, 0x00, 0x15, - 0x00, 0x28, 0x00, 0x39, 0x00, 0x48, 0x00, 0x55, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, - 0x0b, 0x02, 0x09, 0x09, 0x07, 0x12, 0x05, 0x1a, 0x03, 0x21, 0x01, 0x29, - 0x00, 0x2f, 0x00, 0x40, 0x00, 0x4f, 0x00, 0x5c, 0x00, 0x68, 0x00, 0x73, - 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x04, 0x00, 0x13, - 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, 0x00, 0x5f, 0x00, 0x6b, - 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x42, 0x00, 0xdd, 0x00, 0xd9, - 0x00, 0xcf, 0x00, 0xbe, 0x00, 0xa9, 0x00, 0x91, 0x00, 0x78, 0x00, 0x5d, - 0x00, 0x44, 0x00, 0x2b, 0x00, 0x15, 0x00, 0x00, 0x00, 0x13, 0x00, 0x24, - 0x00, 0x34, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x0a, 0x06, - 0x08, 0x0d, 0x06, 0x14, 0x04, 0x1c, 0x03, 0x23, 0x01, 0x29, 0x00, 0x2f, - 0x00, 0x3e, 0x00, 0x4c, 0x00, 0x59, 0x00, 0x64, 0x1e, 0x00, 0x1c, 0x00, - 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, 0x00, 0x1b, 0x00, 0x29, - 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, 0x00, 0x6a, 0x00, 0x74, - 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, - 0x00, 0x22, 0x00, 0x30, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc3, - 0x00, 0xb1, 0x00, 0x9c, 0x00, 0x85, 0x00, 0x6d, 0x00, 0x55, 0x00, 0x3e, - 0x00, 0x28, 0x00, 0x13, 0x00, 0x00, 0x00, 0x11, 0x00, 0x22, 0x00, 0x30, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1b, 0x00, 0x16, 0x00, 0x0f, 0x00, 0x0b, 0x03, 0x09, 0x09, 0x07, 0x10, - 0x06, 0x17, 0x04, 0x1e, 0x02, 0x24, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3d, - 0x00, 0x4a, 0x00, 0x56, 0x1e, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x10, 0x00, - 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, 0x00, 0x2e, 0x00, 0x3c, - 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x73, 0x00, 0x7b, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, - 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd3, 0x00, 0xc7, 0x00, 0xb7, 0x00, 0xa4, - 0x00, 0x90, 0x00, 0x7a, 0x00, 0x64, 0x00, 0x4e, 0x00, 0x39, 0x00, 0x24, - 0x00, 0x11, 0x00, 0x00, 0x00, 0x10, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, - 0x11, 0x00, 0x0b, 0x00, 0x0a, 0x06, 0x08, 0x0c, 0x07, 0x13, 0x05, 0x19, - 0x04, 0x1f, 0x02, 0x25, 0x01, 0x2a, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x48, - 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, 0x09, 0x00, 0x00, 0x01, - 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, 0x00, 0x3f, 0x00, 0x4a, - 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0xdd, 0x00, 0xdb, - 0x00, 0xd5, 0x00, 0xca, 0x00, 0xbc, 0x00, 0xab, 0x00, 0x99, 0x00, 0x85, - 0x00, 0x70, 0x00, 0x5c, 0x00, 0x48, 0x00, 0x34, 0x00, 0x22, 0x00, 0x10, - 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x18, 0x00, 0x13, 0x00, 0x0d, 0x00, - 0x0a, 0x04, 0x09, 0x09, 0x08, 0x0f, 0x06, 0x15, 0x05, 0x1b, 0x03, 0x20, - 0x02, 0x26, 0x01, 0x2b, 0x00, 0x2f, 0x00, 0x3b, 0x1f, 0x00, 0x1d, 0x00, - 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, 0x00, 0x08, 0x00, 0x13, - 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, 0x00, 0x4c, 0x00, 0x56, - 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcd, - 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8e, 0x00, 0x7b, 0x00, 0x68, - 0x00, 0x55, 0x00, 0x42, 0x00, 0x30, 0x00, 0x1f, 0x00, 0x0f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0b, 0x01, 0x0a, 0x07, - 0x08, 0x0c, 0x07, 0x11, 0x06, 0x17, 0x04, 0x1c, 0x03, 0x21, 0x02, 0x26, - 0x01, 0x2b, 0x00, 0x2f, 0x1f, 0x00, 0x1e, 0x00, 0x1a, 0x00, 0x15, 0x00, - 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, 0x00, 0x18, 0x00, 0x23, - 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x56, 0x00, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x07, 0x00, 0x0f, 0x00, 0x19, 0x00, 0x1c, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x04, 0x4c, 0x09, 0x11, - 0x11, 0x01, 0x17, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x3e, 0x00, 0x2b, 0x00, 0x34, 0x00, 0x32, 0x00, 0x2c, 0x00, 0x24, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x01, 0x61, 0x03, 0x23, - 0x09, 0x03, 0x13, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, - 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x0f, 0x00, 0x17, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x87, 0x01, 0x39, 0x07, 0x0f, 0x0c, 0x02, - 0x12, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x21, 0x00, - 0x27, 0x00, 0x2b, 0x00, 0x28, 0x00, 0x21, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, - 0x0a, 0x00, 0x11, 0x00, 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, - 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, - 0x12, 0x00, 0x16, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x00, 0xbc, 0x00, 0x87, 0x00, 0x32, 0x05, 0x19, 0x09, 0x0b, 0x0b, 0x03, - 0x0e, 0x00, 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, - 0x1b, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x34, 0x00, 0x27, 0x00, 0x0f, 0x00, 0x1a, 0x00, - 0x1c, 0x00, 0x18, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, - 0x00, 0xc5, 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, - 0x03, 0x00, 0x0a, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, - 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x0e, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, - 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xcd, 0x00, 0xaf, - 0x00, 0x6e, 0x00, 0x31, 0x04, 0x1e, 0x07, 0x12, 0x09, 0x0a, 0x0a, 0x04, - 0x0b, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, - 0x18, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x32, 0x00, 0x2b, 0x00, 0x1a, 0x00, 0x06, 0x00, 0x0d, 0x00, 0x0e, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x17, 0x00, 0x19, 0x00, 0x1a, 0x00, 0x1b, 0x00, - 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x00, 0xd2, 0x00, 0xbc, - 0x00, 0x8d, 0x00, 0x5f, 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, - 0x00, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, - 0x14, 0x00, 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, - 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0x93, 0x00, 0x5f, - 0x00, 0x30, 0x03, 0x22, 0x06, 0x17, 0x07, 0x0f, 0x09, 0x0a, 0x0a, 0x05, - 0x0b, 0x02, 0x0c, 0x00, 0x0f, 0x00, 0x11, 0x00, 0x13, 0x00, 0x15, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x28, 0x00, - 0x1c, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x06, 0x00, 0x0c, 0x00, 0x0f, 0x00, - 0x12, 0x00, 0x15, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, 0x1a, 0x00, - 0x1a, 0x00, 0x1b, 0x00, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, - 0x00, 0x5f, 0x00, 0x44, 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, - 0x00, 0x04, 0x01, 0x00, 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, - 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, - 0x00, 0xd8, 0x00, 0xcb, 0x00, 0xaa, 0x00, 0x7f, 0x00, 0x55, 0x00, 0x30, - 0x02, 0x24, 0x05, 0x1b, 0x06, 0x14, 0x08, 0x0e, 0x09, 0x09, 0x0a, 0x06, - 0x0b, 0x03, 0x0b, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x24, 0x00, 0x21, 0x00, 0x18, 0x00, 0x0e, 0x00, - 0x06, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x10, 0x00, - 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00, - 0x00, 0xda, 0x00, 0xd0, 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, - 0x00, 0x49, 0x00, 0x36, 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, - 0x00, 0x06, 0x00, 0x01, 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xda, 0x00, 0xd1, - 0x00, 0xb8, 0x00, 0x96, 0x00, 0x71, 0x00, 0x4e, 0x00, 0x30, 0x02, 0x26, - 0x04, 0x1e, 0x06, 0x17, 0x07, 0x12, 0x08, 0x0d, 0x09, 0x09, 0x0a, 0x06, - 0x0a, 0x04, 0x0b, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x12, 0x00, 0x0c, 0x00, 0x05, 0x00, - 0x00, 0x00, 0x04, 0x00, 0x08, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x15, 0x00, 0x16, 0x00, 0x00, 0xdb, 0x00, 0xd5, - 0x00, 0xc3, 0x00, 0xaa, 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, - 0x00, 0x3c, 0x00, 0x2f, 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, - 0x00, 0x08, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc1, 0x00, 0xa6, - 0x00, 0x87, 0x00, 0x67, 0x00, 0x4a, 0x00, 0x30, 0x02, 0x27, 0x03, 0x20, - 0x05, 0x1a, 0x06, 0x14, 0x07, 0x10, 0x08, 0x0c, 0x09, 0x09, 0x0a, 0x07, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1a, 0x00, 0x15, 0x00, 0x0f, 0x00, 0x0a, 0x00, 0x04, 0x00, 0x00, 0x00, - 0x04, 0x00, 0x07, 0x00, 0x0a, 0x00, 0x0d, 0x00, 0x0f, 0x00, 0x11, 0x00, - 0x13, 0x00, 0x14, 0x00, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, - 0x00, 0x9f, 0x00, 0x88, 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, - 0x00, 0x34, 0x00, 0x29, 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, - 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc8, 0x00, 0xb2, 0x00, 0x97, 0x00, 0x7b, - 0x00, 0x60, 0x00, 0x46, 0x00, 0x30, 0x01, 0x28, 0x03, 0x21, 0x04, 0x1c, - 0x06, 0x17, 0x07, 0x13, 0x08, 0x0f, 0x08, 0x0c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1b, 0x00, 0x17, 0x00, - 0x12, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x04, 0x00, 0x00, 0x00, 0x03, 0x00, - 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x10, 0x00, 0x11, 0x00, - 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, - 0x00, 0x82, 0x00, 0x70, 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, - 0x00, 0x2e, 0x00, 0x26, 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdc, 0x00, 0xd8, - 0x00, 0xcc, 0x00, 0xba, 0x00, 0xa4, 0x00, 0x8b, 0x00, 0x72, 0x00, 0x5a, - 0x00, 0x44, 0x00, 0x2f, 0x01, 0x29, 0x03, 0x23, 0x04, 0x1e, 0x05, 0x19, - 0x06, 0x15, 0x07, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x19, 0x00, 0x15, 0x00, 0x10, 0x00, - 0x0c, 0x00, 0x07, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x06, 0x00, - 0x08, 0x00, 0x0b, 0x00, 0x0d, 0x00, 0x0e, 0x00, 0x00, 0xdd, 0x00, 0xda, - 0x00, 0xd1, 0x00, 0xc4, 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, - 0x00, 0x6e, 0x00, 0x5f, 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, - 0x00, 0x2a, 0x00, 0x23, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd0, 0x00, 0xc0, - 0x00, 0xad, 0x00, 0x98, 0x00, 0x81, 0x00, 0x6b, 0x00, 0x56, 0x00, 0x42, - 0x00, 0x2f, 0x01, 0x29, 0x02, 0x24, 0x04, 0x1f, 0x05, 0x1b, 0x06, 0x17, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1d, 0x00, 0x1a, 0x00, 0x16, 0x00, 0x12, 0x00, 0x0e, 0x00, 0x0a, 0x00, - 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x03, 0x00, 0x05, 0x00, 0x08, 0x00, - 0x0a, 0x00, 0x0c, 0x00, 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, - 0x00, 0xbb, 0x00, 0xab, 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, - 0x00, 0x5f, 0x00, 0x53, 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, - 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd2, 0x00, 0xc5, 0x00, 0xb5, 0x00, 0xa2, - 0x00, 0x8e, 0x00, 0x79, 0x00, 0x65, 0x00, 0x52, 0x00, 0x40, 0x00, 0x2f, - 0x01, 0x2a, 0x02, 0x25, 0x03, 0x20, 0x04, 0x1c, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, - 0x18, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0d, 0x00, 0x09, 0x00, 0x06, 0x00, - 0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x05, 0x00, 0x07, 0x00, 0x09, 0x00, - 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, - 0x00, 0xa4, 0x00, 0x95, 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, - 0x00, 0x54, 0x00, 0x4a, 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xdd, 0x00, 0xdb, - 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xaa, 0x00, 0x98, 0x00, 0x85, - 0x00, 0x72, 0x00, 0x60, 0x00, 0x4f, 0x00, 0x3e, 0x00, 0x2f, 0x01, 0x2a, - 0x02, 0x26, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, 0x16, 0x00, - 0x13, 0x00, 0x0f, 0x00, 0x0c, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, - 0x00, 0x00, 0x02, 0x00, 0x04, 0x00, 0x06, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd7, 0x00, 0xcf, 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, - 0x00, 0x90, 0x00, 0x83, 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, - 0x00, 0x4c, 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd6, 0x00, 0xcc, - 0x00, 0xc0, 0x00, 0xb1, 0x00, 0xa0, 0x00, 0x8f, 0x00, 0x7e, 0x00, 0x6d, - 0x00, 0x5c, 0x00, 0x4c, 0x00, 0x3d, 0x00, 0x2f, 0x01, 0x2b, 0x02, 0x26, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x1a, 0x00, 0x17, 0x00, 0x14, 0x00, 0x11, 0x00, - 0x0e, 0x00, 0x0b, 0x00, 0x08, 0x00, 0x05, 0x00, 0x02, 0x00, 0x00, 0x00, - 0x02, 0x00, 0x04, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, - 0x00, 0xc8, 0x00, 0xbd, 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, - 0x00, 0x80, 0x00, 0x74, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xce, 0x00, 0xc3, 0x00, 0xb6, - 0x00, 0xa8, 0x00, 0x98, 0x00, 0x88, 0x00, 0x78, 0x00, 0x68, 0x00, 0x59, - 0x00, 0x4a, 0x00, 0x3c, 0x00, 0x2f, 0x01, 0x2b, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1c, 0x00, - 0x1a, 0x00, 0x18, 0x00, 0x15, 0x00, 0x13, 0x00, 0x10, 0x00, 0x0d, 0x00, - 0x0a, 0x00, 0x07, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, - 0x00, 0xde, 0x00, 0xdd, 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, - 0x00, 0xb7, 0x00, 0xab, 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, - 0x00, 0x73, 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd8, 0x00, 0xd0, 0x00, 0xc7, 0x00, 0xbb, 0x00, 0xae, 0x00, 0xa0, - 0x00, 0x91, 0x00, 0x82, 0x00, 0x73, 0x00, 0x64, 0x00, 0x56, 0x00, 0x48, - 0x00, 0x3b, 0x00, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1f, 0x00, 0x1e, 0x00, 0x1d, 0x00, 0x1b, 0x00, 0x19, 0x00, - 0x16, 0x00, 0x14, 0x00, 0x11, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x09, 0x00, - 0x06, 0x00, 0x04, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, - 0x00, 0xda, 0x00, 0xd4, 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, - 0x00, 0xa6, 0x00, 0x9b, 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, - 0x00, 0x68, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x03, 0x23, 0x09, 0x03, 0x13, 0x00, - 0x18, 0x00, 0x1a, 0x00, 0x1c, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, - 0x1e, 0x00, 0x1e, 0x00, 0x1e, 0x00, 0x1f, 0x00, 0x1f, 0x00, 0x1f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x61, 0x00, 0x9f, - 0x00, 0xc5, 0x00, 0xd2, 0x00, 0xd7, 0x00, 0xda, 0x00, 0xdb, 0x00, 0xdc, - 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xdd, 0x00, 0xde, 0x00, 0xde, 0x00, 0xde, - 0x00, 0xde, 0x00, 0xde, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x9f, 0x00, 0x5f, 0x00, 0x1f, 0x01, 0x04, 0x0a, 0x00, 0x11, 0x00, - 0x15, 0x00, 0x18, 0x00, 0x1a, 0x00, 0x1b, 0x00, 0x1c, 0x00, 0x1c, 0x00, - 0x1d, 0x00, 0x1d, 0x00, 0x1d, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x03, 0x23, 0x00, 0x5f, 0x00, 0x9f, 0x00, 0xbc, - 0x00, 0xc9, 0x00, 0xd0, 0x00, 0xd5, 0x00, 0xd7, 0x00, 0xd9, 0x00, 0xda, - 0x00, 0xdb, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdc, 0x00, 0xdd, 0x00, 0xdd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc5, 0x00, 0x9f, - 0x00, 0x5f, 0x00, 0x32, 0x00, 0x16, 0x00, 0x06, 0x03, 0x00, 0x0a, 0x00, - 0x0f, 0x00, 0x12, 0x00, 0x14, 0x00, 0x16, 0x00, 0x18, 0x00, 0x19, 0x00, - 0x1a, 0x00, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x03, 0x00, 0x1f, 0x00, 0x5f, 0x00, 0x8d, 0x00, 0xa8, 0x00, 0xb8, - 0x00, 0xc3, 0x00, 0xc9, 0x00, 0xce, 0x00, 0xd1, 0x00, 0xd4, 0x00, 0xd6, - 0x00, 0xd7, 0x00, 0xd8, 0x00, 0xd9, 0x00, 0xda, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xd2, 0x00, 0xbc, 0x00, 0x8d, 0x00, 0x5f, - 0x00, 0x3d, 0x00, 0x25, 0x00, 0x14, 0x00, 0x09, 0x00, 0x00, 0x05, 0x00, - 0x09, 0x00, 0x0d, 0x00, 0x10, 0x00, 0x12, 0x00, 0x14, 0x00, 0x15, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x00, 0x01, 0x04, - 0x00, 0x32, 0x00, 0x5f, 0x00, 0x81, 0x00, 0x99, 0x00, 0xaa, 0x00, 0xb6, - 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xc9, 0x00, 0xcc, 0x00, 0xcf, 0x00, 0xd1, - 0x00, 0xd3, 0x00, 0xd4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xa8, 0x00, 0x81, 0x00, 0x5f, 0x00, 0x44, - 0x00, 0x2f, 0x00, 0x1f, 0x00, 0x14, 0x00, 0x0b, 0x00, 0x04, 0x01, 0x00, - 0x05, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x16, 0x00, 0x3d, - 0x00, 0x5f, 0x00, 0x7a, 0x00, 0x8f, 0x00, 0x9f, 0x00, 0xab, 0x00, 0xb4, - 0x00, 0xbb, 0x00, 0xc0, 0x00, 0xc4, 0x00, 0xc8, 0x00, 0xcb, 0x00, 0xcd, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xda, 0x00, 0xd0, - 0x00, 0xb8, 0x00, 0x99, 0x00, 0x7a, 0x00, 0x5f, 0x00, 0x49, 0x00, 0x36, - 0x00, 0x28, 0x00, 0x1c, 0x00, 0x13, 0x00, 0x0c, 0x00, 0x06, 0x00, 0x01, - 0x02, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0x00, 0x11, 0x00, 0x00, 0x06, 0x00, 0x25, 0x00, 0x44, 0x00, 0x5f, - 0x00, 0x76, 0x00, 0x88, 0x00, 0x96, 0x00, 0xa2, 0x00, 0xab, 0x00, 0xb2, - 0x00, 0xb8, 0x00, 0xbd, 0x00, 0xc1, 0x00, 0xc5, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdb, 0x00, 0xd5, 0x00, 0xc3, 0x00, 0xaa, - 0x00, 0x8f, 0x00, 0x76, 0x00, 0x5f, 0x00, 0x4c, 0x00, 0x3c, 0x00, 0x2f, - 0x00, 0x24, 0x00, 0x1b, 0x00, 0x13, 0x00, 0x0d, 0x00, 0x08, 0x00, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x15, 0x00, - 0x03, 0x00, 0x00, 0x14, 0x00, 0x2f, 0x00, 0x49, 0x00, 0x5f, 0x00, 0x72, - 0x00, 0x82, 0x00, 0x90, 0x00, 0x9b, 0x00, 0xa4, 0x00, 0xab, 0x00, 0xb1, - 0x00, 0xb7, 0x00, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xc9, 0x00, 0xb6, 0x00, 0x9f, 0x00, 0x88, - 0x00, 0x72, 0x00, 0x5f, 0x00, 0x4e, 0x00, 0x40, 0x00, 0x34, 0x00, 0x29, - 0x00, 0x21, 0x00, 0x19, 0x00, 0x13, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1d, 0x00, 0x18, 0x00, 0x0a, 0x00, 0x00, 0x09, - 0x00, 0x1f, 0x00, 0x36, 0x00, 0x4c, 0x00, 0x5f, 0x00, 0x70, 0x00, 0x7e, - 0x00, 0x8a, 0x00, 0x95, 0x00, 0x9e, 0x00, 0xa5, 0x00, 0xab, 0x00, 0xb1, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xd9, - 0x00, 0xce, 0x00, 0xbe, 0x00, 0xab, 0x00, 0x96, 0x00, 0x82, 0x00, 0x70, - 0x00, 0x5f, 0x00, 0x50, 0x00, 0x43, 0x00, 0x38, 0x00, 0x2e, 0x00, 0x26, - 0x00, 0x1f, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0x00, 0x1a, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x14, 0x00, 0x28, - 0x00, 0x3c, 0x00, 0x4e, 0x00, 0x5f, 0x00, 0x6e, 0x00, 0x7b, 0x00, 0x86, - 0x00, 0x90, 0x00, 0x98, 0x00, 0xa0, 0x00, 0xa6, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd1, 0x00, 0xc4, - 0x00, 0xb4, 0x00, 0xa2, 0x00, 0x90, 0x00, 0x7e, 0x00, 0x6e, 0x00, 0x5f, - 0x00, 0x52, 0x00, 0x46, 0x00, 0x3c, 0x00, 0x32, 0x00, 0x2a, 0x00, 0x23, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1b, 0x00, - 0x12, 0x00, 0x05, 0x00, 0x00, 0x0b, 0x00, 0x1c, 0x00, 0x2f, 0x00, 0x40, - 0x00, 0x50, 0x00, 0x5f, 0x00, 0x6c, 0x00, 0x78, 0x00, 0x83, 0x00, 0x8c, - 0x00, 0x94, 0x00, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xdd, 0x00, 0xdb, 0x00, 0xd4, 0x00, 0xc9, 0x00, 0xbb, 0x00, 0xab, - 0x00, 0x9b, 0x00, 0x8a, 0x00, 0x7b, 0x00, 0x6c, 0x00, 0x5f, 0x00, 0x53, - 0x00, 0x48, 0x00, 0x3f, 0x00, 0x36, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1c, 0x00, 0x14, 0x00, 0x09, 0x00, - 0x00, 0x04, 0x00, 0x13, 0x00, 0x24, 0x00, 0x34, 0x00, 0x43, 0x00, 0x52, - 0x00, 0x5f, 0x00, 0x6b, 0x00, 0x76, 0x00, 0x80, 0x00, 0x88, 0x00, 0x90, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, - 0x00, 0xd6, 0x00, 0xcc, 0x00, 0xc0, 0x00, 0xb2, 0x00, 0xa4, 0x00, 0x95, - 0x00, 0x86, 0x00, 0x78, 0x00, 0x6b, 0x00, 0x5f, 0x00, 0x54, 0x00, 0x4a, - 0x00, 0x41, 0x00, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1e, 0x00, 0x1c, 0x00, 0x16, 0x00, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x0c, - 0x00, 0x1b, 0x00, 0x29, 0x00, 0x38, 0x00, 0x46, 0x00, 0x53, 0x00, 0x5f, - 0x00, 0x6a, 0x00, 0x74, 0x00, 0x7d, 0x00, 0x85, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd7, 0x00, 0xcf, - 0x00, 0xc4, 0x00, 0xb8, 0x00, 0xab, 0x00, 0x9e, 0x00, 0x90, 0x00, 0x83, - 0x00, 0x76, 0x00, 0x6a, 0x00, 0x5f, 0x00, 0x55, 0x00, 0x4c, 0x00, 0x43, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x1d, 0x00, - 0x18, 0x00, 0x10, 0x00, 0x05, 0x00, 0x00, 0x06, 0x00, 0x13, 0x00, 0x21, - 0x00, 0x2e, 0x00, 0x3c, 0x00, 0x48, 0x00, 0x54, 0x00, 0x5f, 0x00, 0x69, - 0x00, 0x73, 0x00, 0x7b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xde, 0x00, 0xdc, 0x00, 0xd8, 0x00, 0xd1, 0x00, 0xc8, 0x00, 0xbd, - 0x00, 0xb1, 0x00, 0xa5, 0x00, 0x98, 0x00, 0x8c, 0x00, 0x80, 0x00, 0x74, - 0x00, 0x69, 0x00, 0x5f, 0x00, 0x56, 0x00, 0x4d, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1d, 0x00, 0x19, 0x00, 0x12, 0x00, - 0x09, 0x00, 0x00, 0x01, 0x00, 0x0d, 0x00, 0x19, 0x00, 0x26, 0x00, 0x32, - 0x00, 0x3f, 0x00, 0x4a, 0x00, 0x55, 0x00, 0x5f, 0x00, 0x69, 0x00, 0x71, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, - 0x00, 0xd9, 0x00, 0xd3, 0x00, 0xcb, 0x00, 0xc1, 0x00, 0xb7, 0x00, 0xab, - 0x00, 0xa0, 0x00, 0x94, 0x00, 0x88, 0x00, 0x7d, 0x00, 0x73, 0x00, 0x69, - 0x00, 0x5f, 0x00, 0x56, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1f, 0x00, 0x1d, 0x00, 0x1a, 0x00, 0x14, 0x00, 0x0c, 0x00, 0x02, 0x00, - 0x00, 0x08, 0x00, 0x13, 0x00, 0x1f, 0x00, 0x2a, 0x00, 0x36, 0x00, 0x41, - 0x00, 0x4c, 0x00, 0x56, 0x00, 0x5f, 0x00, 0x68, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xde, 0x00, 0xdd, 0x00, 0xda, 0x00, 0xd4, - 0x00, 0xcd, 0x00, 0xc5, 0x00, 0xbb, 0x00, 0xb1, 0x00, 0xa6, 0x00, 0x9b, - 0x00, 0x90, 0x00, 0x85, 0x00, 0x7b, 0x00, 0x71, 0x00, 0x68, 0x00, 0x5f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1f, 0x00, 0x1e, 0x00, - 0x1a, 0x00, 0x15, 0x00, 0x0e, 0x00, 0x05, 0x00, 0x00, 0x03, 0x00, 0x0e, - 0x00, 0x18, 0x00, 0x23, 0x00, 0x2e, 0x00, 0x39, 0x00, 0x43, 0x00, 0x4d, - 0x00, 0x56, 0x00, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00 -}; - -/* clang-format on */ +extern const unsigned char areaTexBytes[]; #define SEARCHTEX_WIDTH 64 #define SEARCHTEX_HEIGHT 16 #define SEARCHTEX_PITCH SEARCHTEX_WIDTH #define SEARCHTEX_SIZE (SEARCHTEX_HEIGHT * SEARCHTEX_PITCH) -/* Don't re-wrap large data definitions. */ -/* clang-format off */ - /** * Stored in R8 format. Load it in the following format: * - DX10: DXGI_FORMAT_R8_UNORM */ -static const unsigned char searchTexBytes[] = { - 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, - 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0xfe, 0x7f, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xfe, 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0xfe, - 0xfe, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, - 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, - 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, - 0x7f, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x7f, 0x7f, 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x7f, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, -}; - -/* clang-format off */ +extern const unsigned char searchTexBytes[]; diff --git a/source/blender/draw/tests/shaders_test.cc b/source/blender/draw/tests/shaders_test.cc index a9810b4cc77..1abd056502d 100644 --- a/source/blender/draw/tests/shaders_test.cc +++ b/source/blender/draw/tests/shaders_test.cc @@ -15,13 +15,15 @@ #include "engines/eevee/eevee_private.h" #include "engines/gpencil/gpencil_engine.h" -#include "engines/image/image_private.h" +#include "engines/image/image_private.hh" #include "engines/overlay/overlay_private.h" #include "engines/workbench/workbench_private.h" #include "intern/draw_shader.h" namespace blender::draw { +using namespace blender::draw::image_engine; + static void test_workbench_glsl_shaders() { workbench_shader_library_ensure(); diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index afbd9b2c92d..69fabd004cc 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -51,6 +51,7 @@ #include "BKE_mask.h" #include "BKE_nla.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -2522,10 +2523,10 @@ static void ANIM_OT_channels_fcurves_enable(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } -/* ****************** Find / Set Filter Operator ******************** */ +/* ****************** Select Filter Textbox Operator ******************** */ /* XXX: make this generic? */ -static bool animchannels_find_poll(bContext *C) +static bool animchannels_select_filter_poll(bContext *C) { ScrArea *area = CTX_wm_area(C); @@ -2537,64 +2538,62 @@ static bool animchannels_find_poll(bContext *C) return ELEM(area->spacetype, SPACE_ACTION, SPACE_GRAPH, SPACE_NLA); } -/* find_invoke() - Get initial channels */ -static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int animchannels_select_filter_invoke(struct bContext *C, + struct wmOperator *op, + const struct wmEvent *UNUSED(event)) { - bAnimContext ac; + ScrArea *area = CTX_wm_area(C); + ARegion *region_ctx = CTX_wm_region(C); + ARegion *region_channels = BKE_area_find_region_type(area, RGN_TYPE_CHANNELS); - /* get editor data */ - if (ANIM_animdata_get_context(C, &ac) == 0) { - return OPERATOR_CANCELLED; + CTX_wm_region_set(C, region_channels); + + /* Show the channel region if it's hidden. This means that direct activation of the input field + * is impossible, as it may not exist yet. For that reason, the actual activation is deferred to + * the modal callback function; by the time it runs, the screen has been redrawn and the UI + * element is there to activate. */ + if (region_channels->flag & RGN_FLAG_HIDDEN) { + ED_region_toggle_hidden(C, region_channels); + ED_region_tag_redraw(region_channels); } - /* set initial filter text, and enable filter */ - RNA_string_set(op->ptr, "query", ac.ads->searchstr); + WM_event_add_modal_handler(C, op); - /* defer to popup */ - return WM_operator_props_popup(C, op, event); + CTX_wm_region_set(C, region_ctx); + return OPERATOR_RUNNING_MODAL; } -/* find_exec() - Called to set the value */ -static int animchannels_find_exec(bContext *C, wmOperator *op) +static int animchannels_select_filter_modal(bContext *C, + wmOperator *UNUSED(op), + const wmEvent *UNUSED(event)) { bAnimContext ac; - - /* get editor data */ if (ANIM_animdata_get_context(C, &ac) == 0) { return OPERATOR_CANCELLED; } - /* update filter text */ - RNA_string_get(op->ptr, "query", ac.ads->searchstr); - - /* redraw */ - WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + ARegion *region = CTX_wm_region(C); + if (UI_textbutton_activate_rna(C, region, ac.ads, "filter_text")) { + /* Redraw to make sure it shows the cursor after activating */ + WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, NULL); + } return OPERATOR_FINISHED; } -static void ANIM_OT_channels_find(wmOperatorType *ot) +static void ANIM_OT_channels_select_filter(wmOperatorType *ot) { /* identifiers */ - ot->name = "Find Channels"; - ot->idname = "ANIM_OT_channels_find"; - ot->description = "Filter the set of channels shown to only include those with matching names"; + ot->name = "Filter Channels"; + ot->idname = "ANIM_OT_channels_select_filter"; + ot->description = + "Start entering text which filters the set of channels shown to only include those with " + "matching names"; /* callbacks */ - ot->invoke = animchannels_find_invoke; - ot->exec = animchannels_find_exec; - ot->poll = animchannels_find_poll; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - ot->prop = RNA_def_string(ot->srna, - "query", - "Query", - sizeof(((bDopeSheet *)NULL)->searchstr), - "", - "Text to search for in channel names"); + ot->invoke = animchannels_select_filter_invoke; + ot->modal = animchannels_select_filter_modal; + ot->poll = animchannels_select_filter_poll; } /* ********************** Select All Operator *********************** */ @@ -3563,7 +3562,7 @@ void ED_operatortypes_animchannels(void) WM_operatortype_append(ANIM_OT_channel_select_keys); WM_operatortype_append(ANIM_OT_channels_rename); - WM_operatortype_append(ANIM_OT_channels_find); + WM_operatortype_append(ANIM_OT_channels_select_filter); WM_operatortype_append(ANIM_OT_channels_setting_enable); WM_operatortype_append(ANIM_OT_channels_setting_disable); diff --git a/source/blender/editors/animation/anim_deps.c b/source/blender/editors/animation/anim_deps.c index 97679723d84..088de80bb65 100644 --- a/source/blender/editors/animation/anim_deps.c +++ b/source/blender/editors/animation/anim_deps.c @@ -217,8 +217,6 @@ static void animchan_sync_fcurve_scene(bAnimListElem *ale) /* Check if this strip is selected. */ Editing *ed = SEQ_editing_get(scene); seq = SEQ_get_sequence_by_name(ed->seqbasep, seq_name, false); - MEM_freeN(seq_name); - if (seq == NULL) { return; } diff --git a/source/blender/editors/animation/anim_filter.c b/source/blender/editors/animation/anim_filter.c index b12e0ae5cab..e1d046428a8 100644 --- a/source/blender/editors/animation/anim_filter.c +++ b/source/blender/editors/animation/anim_filter.c @@ -3087,7 +3087,10 @@ static size_t animdata_filter_dopesheet_movieclips(bAnimContext *ac, } /* Helper for animdata_filter_dopesheet() - For checking if an object should be included or not */ -static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_mode) +static bool animdata_filter_base_is_ok(bDopeSheet *ads, + Base *base, + const eObjectMode object_mode, + int filter_mode) { Object *ob = base->object; @@ -3144,10 +3147,21 @@ static bool animdata_filter_base_is_ok(bDopeSheet *ads, Base *base, int filter_m } /* check selection and object type filters */ - if ((ads->filterflag & ADS_FILTER_ONLYSEL) && - !((base->flag & BASE_SELECTED) /*|| (base == sce->basact) */)) { - /* only selected should be shown */ - return false; + if (ads->filterflag & ADS_FILTER_ONLYSEL) { + if (object_mode & OB_MODE_POSE) { + /* When in pose-mode handle all pose-mode objects. + * This avoids problems with pose-mode where objects may be unselected, + * where a selected bone of an unselected object would be hidden. see: T81922. */ + if (!(base->object->mode & object_mode)) { + return false; + } + } + else { + /* only selected should be shown (ignore active) */ + if (!(base->flag & BASE_SELECTED)) { + return false; + } + } } /* check if object belongs to the filtering group if option to filter @@ -3185,7 +3199,7 @@ static Base **animdata_filter_ds_sorted_bases(bDopeSheet *ads, Base **sorted_bases = MEM_mallocN(sizeof(Base *) * tot_bases, "Dopesheet Usable Sorted Bases"); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if (animdata_filter_base_is_ok(ads, base, filter_mode)) { + if (animdata_filter_base_is_ok(ads, base, OB_MODE_OBJECT, filter_mode)) { sorted_bases[num_bases++] = base; } } @@ -3278,8 +3292,10 @@ static size_t animdata_filter_dopesheet(bAnimContext *ac, /* Filter and add contents of each base (i.e. object) without them sorting first * NOTE: This saves performance in cases where order doesn't matter */ + Object *obact = OBACT(view_layer); + const eObjectMode object_mode = obact ? obact->mode : OB_MODE_OBJECT; LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { - if (animdata_filter_base_is_ok(ads, base, filter_mode)) { + if (animdata_filter_base_is_ok(ads, base, object_mode, filter_mode)) { /* since we're still here, this object should be usable */ items += animdata_filter_dopesheet_ob(ac, anim_data, ads, base, filter_mode); } diff --git a/source/blender/editors/animation/anim_ipo_utils.c b/source/blender/editors/animation/anim_ipo_utils.c index 33b4882927a..05837ed17b9 100644 --- a/source/blender/editors/animation/anim_ipo_utils.c +++ b/source/blender/editors/animation/anim_ipo_utils.c @@ -126,9 +126,10 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) structname = RNA_struct_ui_name(ptr.type); } - /* For the VSE, a strip's 'Transform' or 'Crop' is a nested (under Sequence) struct, but - * displaying the struct name alone is no meaningful information (and also cannot be - * filtered well), same for modifiers. So display strip name alongside as well. */ + /* For the sequencer, a strip's 'Transform' or 'Crop' is a nested (under Sequence) + * struct, but displaying the struct name alone is no meaningful information + * (and also cannot be filtered well), same for modifiers. + * So display strip name alongside as well. */ if (GS(ptr.owner_id->name) == ID_SCE) { char stripname[256]; if (BLI_str_quoted_substr( @@ -144,6 +145,22 @@ int getname_anim_fcurve(char *name, ID *id, FCurve *fcu) } } } + /* For node sockets, it is useful to include the node name as well (multiple similar nodes + * are not distinguishable otherwise). Unfortunately, the node label cannot be retrieved + * from the rna path, for this to work access to the underlying node is needed (but finding + * the node iterates all nodes & sockets which would result in bad performance in some + * circumstances). */ + if (RNA_struct_is_a(ptr.type, &RNA_NodeSocket)) { + char nodename[256]; + if (BLI_str_quoted_substr(fcu->rna_path, "nodes[", nodename, sizeof(nodename))) { + const char *structname_all = BLI_sprintfN("%s : %s", nodename, structname); + if (free_structname) { + MEM_freeN((void *)structname); + } + structname = structname_all; + free_structname = 1; + } + } } /* Property Name is straightforward */ diff --git a/source/blender/editors/animation/anim_markers.c b/source/blender/editors/animation/anim_markers.c index 0de3f429bc7..e12433f4d4c 100644 --- a/source/blender/editors/animation/anim_markers.c +++ b/source/blender/editors/animation/anim_markers.c @@ -872,7 +872,7 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve ARegion *region = CTX_wm_region(C); View2D *v2d = ®ion->v2d; ListBase *markers = ED_context_get_markers(C); - if (!region_position_is_over_marker(v2d, markers, event->x - region->winrct.xmin)) { + if (!region_position_is_over_marker(v2d, markers, event->xy[0] - region->winrct.xmin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } } @@ -880,8 +880,8 @@ static int ed_marker_move_invoke(bContext *C, wmOperator *op, const wmEvent *eve if (ed_marker_move_init(C, op)) { MarkerMove *mm = op->customdata; - mm->evtx = event->x; - mm->firstx = event->x; + mm->evtx = event->xy[0]; + mm->firstx = event->xy[0]; mm->event_type = event->type; /* add temp handler */ @@ -993,11 +993,11 @@ static int ed_marker_move_modal(bContext *C, wmOperator *op, const wmEvent *even dx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); - if (event->x != mm->evtx) { /* XXX maybe init for first time */ + if (event->xy[0] != mm->evtx) { /* XXX maybe init for first time */ float fac; - mm->evtx = event->x; - fac = ((float)(event->x - mm->firstx) * dx); + mm->evtx = event->xy[0]; + fac = ((float)(event->xy[0] - mm->firstx) * dx); apply_keyb_grid(event->shift, event->ctrl, &fac, 0.0, FPS, 0.1 * FPS, 0); @@ -1354,7 +1354,8 @@ static int ed_marker_box_select_invoke(bContext *C, wmOperator *op, const wmEven View2D *v2d = ®ion->v2d; ListBase *markers = ED_context_get_markers(C); - bool over_marker = region_position_is_over_marker(v2d, markers, event->x - region->winrct.xmin); + bool over_marker = region_position_is_over_marker( + v2d, markers, event->xy[0] - region->winrct.xmin); bool tweak = RNA_boolean_get(op->ptr, "tweak"); if (tweak && over_marker) { diff --git a/source/blender/editors/animation/anim_motion_paths.c b/source/blender/editors/animation/anim_motion_paths.c index d130941b9bc..0d812198d04 100644 --- a/source/blender/editors/animation/anim_motion_paths.c +++ b/source/blender/editors/animation/anim_motion_paths.c @@ -84,7 +84,7 @@ Depsgraph *animviz_depsgraph_build(Main *bmain, /* Make a flat array of IDs for the DEG API. */ const int num_ids = BLI_listbase_count(targets); - ID **ids = MEM_malloc_arrayN(sizeof(ID *), num_ids, "animviz IDS"); + ID **ids = MEM_malloc_arrayN(num_ids, sizeof(ID *), "animviz IDS"); int current_id_index = 0; for (MPathTarget *mpt = targets->first; mpt != NULL; mpt = mpt->next) { ids[current_id_index++] = &mpt->ob->id; diff --git a/source/blender/editors/animation/anim_ops.c b/source/blender/editors/animation/anim_ops.c index 450d7cd100e..8232df968ed 100644 --- a/source/blender/editors/animation/anim_ops.c +++ b/source/blender/editors/animation/anim_ops.c @@ -74,9 +74,24 @@ static bool change_frame_poll(bContext *C) * this shouldn't show up in 3D editor (or others without 2D timeline view) via search */ if (area) { - if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP, SPACE_GRAPH)) { + if (ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_CLIP)) { return true; } + if (area->spacetype == SPACE_SEQ) { + /* Check the region type so tools (which are shared between preview/strip view) + * don't conflict with actions which can have the same key bound (2D cursor for example). */ + const ARegion *region = CTX_wm_region(C); + if (region && region->regiontype == RGN_TYPE_WINDOW) { + return true; + } + } + if (area->spacetype == SPACE_GRAPH) { + const SpaceGraph *sipo = area->spacedata.first; + /* Driver Editor's X axis is not time. */ + if (sipo->mode != SIPO_MODE_DRIVERS) { + return true; + } + } } CTX_wm_operator_poll_msg_set(C, "Expected an animation area to be active"); @@ -241,6 +256,11 @@ static bool use_sequencer_snapping(bContext *C) /* Modal Operator init */ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + ARegion *region = CTX_wm_region(C); + if (CTX_wm_space_seq(C) != NULL && region->regiontype == RGN_TYPE_PREVIEW) { + return OPERATOR_CANCELLED; + } + /* Change to frame that mouse is over before adding modal handler, * as user could click on a single frame (jump to frame) as well as * click-dragging over a range (modal scrubbing). diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c index bfaa76b3bf9..dbf379971fa 100644 --- a/source/blender/editors/animation/drivers.c +++ b/source/blender/editors/animation/drivers.c @@ -1115,7 +1115,7 @@ static int add_driver_button_invoke(bContext *C, wmOperator *op, const wmEvent * } /* 2) Show editing panel for setting up this driver */ - /* TODO: Use a different one from the editing popever, so we can have the single/all toggle? */ + /* TODO: Use a different one from the editing popover, so we can have the single/all toggle? */ UI_popover_panel_invoke(C, "GRAPH_PT_drivers_popover", true, op->reports); } diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index 40871fba2be..b94ee68e276 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -187,7 +187,7 @@ static PanelType *fmodifier_panel_register(ARegionType *region_type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = fmodifier_reorder; panel_type->get_list_data_expand_flag = get_fmodifier_expand_flag; panel_type->set_list_data_expand_flag = set_fmodifier_expand_flag; @@ -221,7 +221,7 @@ static PanelType *fmodifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = poll; - panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX; + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); @@ -403,10 +403,10 @@ static void generator_panel_draw(const bContext *C, Panel *panel) char xval[32]; /* The first value gets a "Coefficient" label. */ - BLI_strncpy(xval, "Coefficient", sizeof(xval)); + BLI_strncpy(xval, N_("Coefficient"), sizeof(xval)); for (int i = 0; i < data->arraysize; i++) { - uiItemFullR(col, ptr, prop, i, 0, 0, N_(xval), ICON_NONE); + uiItemFullR(col, ptr, prop, i, 0, 0, IFACE_(xval), ICON_NONE); BLI_snprintf(xval, sizeof(xval), "x^%d", i + 1); } break; @@ -420,17 +420,17 @@ static void generator_panel_draw(const bContext *C, Panel *panel) uiLayoutColumn(split, false); uiLayout *title_col = uiLayoutColumn(split, false); uiLayout *title_row = uiLayoutRow(title_col, true); - uiItemL(title_row, N_("A"), ICON_NONE); - uiItemL(title_row, N_("B"), ICON_NONE); + uiItemL(title_row, IFACE_("A"), ICON_NONE); + uiItemL(title_row, IFACE_("B"), ICON_NONE); } uiLayout *first_row = uiLayoutRow(col, true); - uiItemFullR(first_row, ptr, prop, 0, 0, 0, N_("y = (Ax + B)"), ICON_NONE); + uiItemFullR(first_row, ptr, prop, 0, 0, 0, IFACE_("y = (Ax + B)"), ICON_NONE); uiItemFullR(first_row, ptr, prop, 1, 0, 0, "", ICON_NONE); for (int i = 2; i < data->arraysize - 1; i++) { /* \u2715 is the multiplication symbol. */ uiLayout *row = uiLayoutRow(col, true); - uiItemFullR(row, ptr, prop, i, 0, 0, N_("\u2715 (Ax + B)"), ICON_NONE); + uiItemFullR(row, ptr, prop, i, 0, 0, IFACE_("\u2715 (Ax + B)"), ICON_NONE); uiItemFullR(row, ptr, prop, i + 1, 0, 0, "", ICON_NONE); } break; diff --git a/source/blender/editors/animation/keyframes_draw.c b/source/blender/editors/animation/keyframes_draw.c index ac7db9f4f46..6fba2d9c258 100644 --- a/source/blender/editors/animation/keyframes_draw.c +++ b/source/blender/editors/animation/keyframes_draw.c @@ -30,7 +30,6 @@ #include "BLI_dlrbTree.h" #include "BLI_listbase.h" #include "BLI_rect.h" -#include "BLI_task.h" #include "DNA_anim_types.h" #include "DNA_gpencil_types.h" @@ -146,31 +145,31 @@ void draw_keyframe_shape(float x, /* Handle type to outline shape. */ switch (handle_type) { case KEYFRAME_HANDLE_AUTO_CLAMP: - flags = 0x2; + flags = GPU_KEYFRAME_SHAPE_CIRCLE; break; /* circle */ case KEYFRAME_HANDLE_AUTO: - flags = 0x12; + flags = GPU_KEYFRAME_SHAPE_CIRCLE | GPU_KEYFRAME_SHAPE_INNER_DOT; break; /* circle with dot */ case KEYFRAME_HANDLE_VECTOR: - flags = 0xC; + flags = GPU_KEYFRAME_SHAPE_SQUARE; break; /* square */ case KEYFRAME_HANDLE_ALIGNED: - flags = 0x5; + flags = GPU_KEYFRAME_SHAPE_DIAMOND | GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL; break; /* clipped diamond */ case KEYFRAME_HANDLE_FREE: default: - flags = 1; /* diamond */ + flags = GPU_KEYFRAME_SHAPE_DIAMOND; /* diamond */ } /* Extreme type to arrow-like shading. */ if (extreme_type & KEYFRAME_EXTREME_MAX) { - flags |= 0x100; + flags |= GPU_KEYFRAME_SHAPE_ARROW_END_MAX; } if (extreme_type & KEYFRAME_EXTREME_MIN) { - flags |= 0x200; + flags |= GPU_KEYFRAME_SHAPE_ARROW_END_MIN; } - if (extreme_type & KEYFRAME_EXTREME_MIXED) { + if (extreme_type & GPU_KEYFRAME_SHAPE_ARROW_END_MIXED) { flags |= 0x400; } } @@ -505,25 +504,12 @@ AnimKeylistDrawList *ED_keylist_draw_list_create(void) return MEM_callocN(sizeof(AnimKeylistDrawList), __func__); } -static void ED_keylist_draw_list_elem_build_task(void *__restrict UNUSED(userdata), - void *item, - int UNUSED(index), - const TaskParallelTLS *__restrict UNUSED(tls)) -{ - AnimKeylistDrawListElem *elem = item; - ED_keylist_draw_list_elem_build_keylist(elem); - ED_keylist_draw_list_elem_prepare_for_drawing(elem); -} - static void ED_keylist_draw_list_build_keylists(AnimKeylistDrawList *draw_list) { - TaskParallelSettings settings; - BLI_parallel_range_settings_defaults(&settings); - /* Create a task per item, a single item is complex enough to deserve its own task. */ - settings.min_iter_per_thread = 1; - - BLI_task_parallel_listbase( - &draw_list->channels, NULL, ED_keylist_draw_list_elem_build_task, &settings); + LISTBASE_FOREACH (AnimKeylistDrawListElem *, elem, &draw_list->channels) { + ED_keylist_draw_list_elem_build_keylist(elem); + ED_keylist_draw_list_elem_prepare_for_drawing(elem); + } } static void ED_keylist_draw_list_draw_blocks(AnimKeylistDrawList *draw_list, View2D *v2d) @@ -584,7 +570,7 @@ static void ED_keylist_draw_list_draw_keys(AnimKeylistDrawList *draw_list, View2 sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, visible_key_len); diff --git a/source/blender/editors/animation/keyframes_keylist.cc b/source/blender/editors/animation/keyframes_keylist.cc index c1a18196a3a..7d08e416d0d 100644 --- a/source/blender/editors/animation/keyframes_keylist.cc +++ b/source/blender/editors/animation/keyframes_keylist.cc @@ -564,9 +564,9 @@ static void nupdate_ak_masklayshape(ActKeyColumn *ak, void *data) using KeylistCreateColumnFunction = std::function; using KeylistUpdateColumnFunction = std::function; -/* `ED_keylist_find_neighbour_front_to_back` is called before the runtime can be initialized so we +/* `ED_keylist_find_neighbor_front_to_back` is called before the runtime can be initialized so we * cannot use bin searching. */ -static ActKeyColumn *ED_keylist_find_neighbour_front_to_back(ActKeyColumn *cursor, float cfra) +static ActKeyColumn *ED_keylist_find_neighbor_front_to_back(ActKeyColumn *cursor, float cfra) { while (cursor->next && cursor->next->cfra <= cfra) { cursor = cursor->next; @@ -574,9 +574,9 @@ static ActKeyColumn *ED_keylist_find_neighbour_front_to_back(ActKeyColumn *curso return cursor; } -/* `ED_keylist_find_neighbour_back_to_front` is called before the runtime can be initialized so we +/* `ED_keylist_find_neighbor_back_to_front` is called before the runtime can be initialized so we * cannot use bin searching. */ -static ActKeyColumn *ED_keylist_find_neighbour_back_to_front(ActKeyColumn *cursor, float cfra) +static ActKeyColumn *ED_keylist_find_neighbor_back_to_front(ActKeyColumn *cursor, float cfra) { while (cursor->prev && cursor->prev->cfra >= cfra) { cursor = cursor->prev; @@ -585,14 +585,14 @@ static ActKeyColumn *ED_keylist_find_neighbour_back_to_front(ActKeyColumn *curso } /* - * `ED_keylist_find_exact_or_neighbour_column` is called before the runtime can be initialized so + * `ED_keylist_find_exact_or_neighbor_column` is called before the runtime can be initialized so * we cannot use bin searching. * * This function is called to add or update columns in the keylist. * Typically columns are sorted by frame number so keeping track of the last_accessed_column * reduces searching. */ -static ActKeyColumn *ED_keylist_find_exact_or_neighbour_column(AnimKeylist *keylist, float cfra) +static ActKeyColumn *ED_keylist_find_exact_or_neighbor_column(AnimKeylist *keylist, float cfra) { BLI_assert(!keylist->is_runtime_initialized); if (ED_keylist_is_empty(keylist)) { @@ -604,10 +604,10 @@ static ActKeyColumn *ED_keylist_find_exact_or_neighbour_column(AnimKeylist *keyl if (!is_cfra_eq(cursor->cfra, cfra)) { const bool walking_direction_front_to_back = cursor->cfra <= cfra; if (walking_direction_front_to_back) { - cursor = ED_keylist_find_neighbour_front_to_back(cursor, cfra); + cursor = ED_keylist_find_neighbor_front_to_back(cursor, cfra); } else { - cursor = ED_keylist_find_neighbour_back_to_front(cursor, cfra); + cursor = ED_keylist_find_neighbor_back_to_front(cursor, cfra); } } @@ -633,7 +633,7 @@ static void ED_keylist_add_or_update_column(AnimKeylist *keylist, return; } - ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbour_column(keylist, cfra); + ActKeyColumn *nearest = ED_keylist_find_exact_or_neighbor_column(keylist, cfra); if (is_cfra_eq(nearest->cfra, cfra)) { update_func(nearest, userdata); } @@ -774,7 +774,7 @@ static void add_bezt_to_keyblocks_list(AnimKeylist *keylist, BezTriple *bezt, co if (is_cfra_lt(bezt[1].vec[1][0], bezt[0].vec[1][0])) { /* Backtrack to find the right location. */ if (is_cfra_lt(bezt[1].vec[1][0], col->cfra)) { - ActKeyColumn *newcol = ED_keylist_find_exact_or_neighbour_column(keylist, col->cfra); + ActKeyColumn *newcol = ED_keylist_find_exact_or_neighbor_column(keylist, col->cfra); BLI_assert(newcol); BLI_assert(newcol->cfra == col->cfra); diff --git a/source/blender/editors/animation/keyframing.c b/source/blender/editors/animation/keyframing.c index 8dc4aed9f0e..0a9f1a36a28 100644 --- a/source/blender/editors/animation/keyframing.c +++ b/source/blender/editors/animation/keyframing.c @@ -425,7 +425,7 @@ int insert_bezt_fcurve(FCurve *fcu, const BezTriple *bezt, eInsertKeyFlags flag) if (flag & INSERTKEY_CYCLE_AWARE) { /* If replacing an end point of a cyclic curve without offset, * modify the other end too. */ - if ((i == 0 || i == fcu->totvert - 1) && + if (ELEM(i, 0, fcu->totvert - 1) && BKE_fcurve_get_cycle_type(fcu) == FCU_CYCLE_PERFECT) { replace_bezt_keyframe_ypos(&fcu->bezt[i == 0 ? fcu->totvert - 1 : 0], bezt); } @@ -792,12 +792,12 @@ static float *setting_get_rna_values( int *tmp_int; if (length > buffer_size) { - values = MEM_malloc_arrayN(sizeof(float), length, __func__); + values = MEM_malloc_arrayN(length, sizeof(float), __func__); } switch (RNA_property_type(prop)) { case PROP_BOOLEAN: - tmp_bool = MEM_malloc_arrayN(sizeof(*tmp_bool), length, __func__); + tmp_bool = MEM_malloc_arrayN(length, sizeof(*tmp_bool), __func__); RNA_property_boolean_get_array(ptr, prop, tmp_bool); for (int i = 0; i < length; i++) { values[i] = (float)tmp_bool[i]; @@ -805,7 +805,7 @@ static float *setting_get_rna_values( MEM_freeN(tmp_bool); break; case PROP_INT: - tmp_int = MEM_malloc_arrayN(sizeof(*tmp_int), length, __func__); + tmp_int = MEM_malloc_arrayN(length, sizeof(*tmp_int), __func__); RNA_property_int_get_array(ptr, prop, tmp_int); for (int i = 0; i < length; i++) { values[i] = (float)tmp_int[i]; @@ -1879,6 +1879,7 @@ static int insert_key_exec(bContext *C, wmOperator *op) float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */ int num_channels; + const bool confirm = op->flag & OP_IS_INVOKE; KeyingSet *ks = keyingset_get_from_op_with_error(op, op->type->prop, scene); if (ks == NULL) { @@ -1915,20 +1916,22 @@ static int insert_key_exec(bContext *C, wmOperator *op) } if (num_channels > 0) { - /* if the appropriate properties have been set, make a note that we've inserted something */ - if (RNA_boolean_get(op->ptr, "confirm_success")) { + /* send notifiers that keyframes have been changed */ + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); + } + + if (confirm) { + /* if called by invoke (from the UI), make a note that we've inserted keyframes */ + if (num_channels > 0) { BKE_reportf(op->reports, RPT_INFO, "Successfully added %d keyframes for keying set '%s'", num_channels, ks->name); } - - /* send notifiers that keyframes have been changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_ADDED, NULL); - } - else { - BKE_report(op->reports, RPT_WARNING, "Keying set failed to insert any keyframes"); + else { + BKE_report(op->reports, RPT_WARNING, "Keying set failed to insert any keyframes"); + } } return OPERATOR_FINISHED; @@ -1957,16 +1960,6 @@ void ANIM_OT_keyframe_insert(wmOperatorType *ot) RNA_def_enum_funcs(prop, ANIM_keying_sets_enum_itemf); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - prop = RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Insert", - "Show a popup when the keyframes get successfully added"); - RNA_def_property_flag(prop, PROP_HIDDEN); } /* Clone of 'ANIM_OT_keyframe_insert' which uses a name for the keying set instead of an enum. */ @@ -1990,16 +1983,6 @@ void ANIM_OT_keyframe_insert_by_name(wmOperatorType *ot) prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", ""); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - prop = RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Insert", - "Show a popup when the keyframes get successfully added"); - RNA_def_property_flag(prop, PROP_HIDDEN); } /* Insert Key Operator (With Menu) ------------------------ */ @@ -2027,8 +2010,6 @@ static int insert_key_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UN /* just call the exec() on the active keyingset */ RNA_enum_set(op->ptr, "type", 0); - RNA_boolean_set(op->ptr, "confirm_success", true); - return op->type->exec(C, op); } @@ -2057,17 +2038,6 @@ void ANIM_OT_keyframe_insert_menu(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - /* confirm whether a keyframe was added by showing a popup - * - by default, this is disabled so that if a menu is shown, this doesn't come up too - */ - /* XXX should this just be always on? */ - prop = RNA_def_boolean(ot->srna, - "confirm_success", - 0, - "Confirm Successful Insert", - "Show a popup when the keyframes get successfully added"); - RNA_def_property_flag(prop, PROP_HIDDEN); - /* whether the menu should always be shown * - by default, the menu should only be shown when there is no active Keying Set (2.5 behavior), * although in some cases it might be useful to always shown (pre 2.5 behavior) @@ -2094,6 +2064,7 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k Scene *scene = CTX_data_scene(C); float cfra = (float)CFRA; /* XXX for now, don't bother about all the yucky offset crap */ int num_channels; + const bool confirm = op->flag & OP_IS_INVOKE; /* try to delete keyframes for the channels specified by KeyingSet */ num_channels = ANIM_apply_keyingset(C, NULL, NULL, ks, MODIFYKEY_MODE_DELETE, cfra); @@ -2108,23 +2079,22 @@ static int delete_key_using_keying_set(bContext *C, wmOperator *op, KeyingSet *k } if (num_channels > 0) { - /* if the appropriate properties have been set, make a note that we've inserted something */ - PropertyRNA *prop = RNA_struct_find_property(op->ptr, "confirm_success"); - if (prop != NULL && RNA_property_boolean_get(op->ptr, prop)) { + WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL); + } + + if (confirm) { + /* if called by invoke (from the UI), make a note that we've removed keyframes */ + if (num_channels > 0) { BKE_reportf(op->reports, RPT_INFO, "Successfully removed %d keyframes for keying set '%s'", num_channels, ks->name); } - - /* send notifiers that keyframes have been changed */ - WM_event_add_notifier(C, NC_ANIMATION | ND_KEYFRAME | NA_REMOVED, NULL); + else { + BKE_report(op->reports, RPT_WARNING, "Keying set failed to remove any keyframes"); + } } - else { - BKE_report(op->reports, RPT_WARNING, "Keying set failed to remove any keyframes"); - } - return OPERATOR_FINISHED; } @@ -2151,15 +2121,6 @@ void ANIM_OT_keyframe_delete(wmOperatorType *ot) RNA_def_enum_funcs(prop, ANIM_keying_sets_enum_itemf); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Delete", - "Show a popup when the keyframes get successfully removed"); } void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) @@ -2182,15 +2143,6 @@ void ANIM_OT_keyframe_delete_by_name(wmOperatorType *ot) prop = RNA_def_string_file_path(ot->srna, "type", "Type", MAX_ID_NAME - 2, "", ""); RNA_def_property_flag(prop, PROP_HIDDEN); ot->prop = prop; - - /* confirm whether a keyframe was added by showing a popup - * - by default, this is enabled, since this operator is assumed to be called independently - */ - RNA_def_boolean(ot->srna, - "confirm_success", - 1, - "Confirm Successful Delete", - "Show a popup when the keyframes get successfully removed"); } /* Delete Key Operator ------------------------ */ @@ -2289,6 +2241,8 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) int selected_objects_success_len = 0; int success_multi = 0; + const bool confirm = op->flag & OP_IS_INVOKE; + CTX_DATA_BEGIN (C, Object *, ob, selected_objects) { ID *id = &ob->id; int success = 0; @@ -2368,22 +2322,25 @@ static int delete_key_v3d_without_keying_set(bContext *C, wmOperator *op) } CTX_DATA_END; - /* report success (or failure) */ if (selected_objects_success_len) { - BKE_reportf(op->reports, - RPT_INFO, - "%d object(s) successfully had %d keyframes removed", - selected_objects_success_len, - success_multi); - } - else { - BKE_reportf( - op->reports, RPT_ERROR, "No keyframes removed from %d object(s)", selected_objects_len); + /* send updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); } - /* send updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_KEYS, NULL); - + if (confirm) { + /* if called by invoke (from the UI), make a note that we've removed keyframes */ + if (selected_objects_success_len) { + BKE_reportf(op->reports, + RPT_INFO, + "%d object(s) successfully had %d keyframes removed", + selected_objects_success_len, + success_multi); + } + else { + BKE_reportf( + op->reports, RPT_ERROR, "No keyframes removed from %d object(s)", selected_objects_len); + } + } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/animation/keyingsets.c b/source/blender/editors/animation/keyingsets.c index 0206aabd359..e1fd3b07f46 100644 --- a/source/blender/editors/animation/keyingsets.c +++ b/source/blender/editors/animation/keyingsets.c @@ -610,7 +610,7 @@ void ANIM_keyingset_info_unregister(Main *bmain, KeyingSetInfo *ksi) KeyingSet *ks, *ksn; /* find relevant builtin KeyingSets which use this, and remove them */ - /* TODO: this isn't done now, since unregister is really only used atm when we + /* TODO: this isn't done now, since unregister is really only used at the moment when we * reload the scripts, which kindof defeats the purpose of "builtin"? */ for (ks = builtin_keyingsets.first; ks; ks = ksn) { ksn = ks->next; diff --git a/source/blender/editors/animation/time_scrub_ui.c b/source/blender/editors/animation/time_scrub_ui.c index 8aeb6a57124..2ddaa2c415e 100644 --- a/source/blender/editors/animation/time_scrub_ui.c +++ b/source/blender/editors/animation/time_scrub_ui.c @@ -91,8 +91,7 @@ static void draw_current_frame(const Scene *scene, bool display_seconds, const View2D *v2d, const rcti *scrub_region_rect, - int current_frame, - bool draw_line) + int current_frame) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; int frame_x = UI_view2d_view_to_region_x(v2d, current_frame); @@ -102,25 +101,36 @@ static void draw_current_frame(const Scene *scene, float text_width = UI_fontstyle_string_width(fstyle, frame_str); float box_width = MAX2(text_width + 8 * UI_DPI_FAC, 24 * UI_DPI_FAC); float box_padding = 3 * UI_DPI_FAC; + const int line_outline = max_ii(1, round_fl_to_int(1 * UI_DPI_FAC)); float bg_color[4]; UI_GetThemeColorShade4fv(TH_CFRAME, -5, bg_color); - if (draw_line) { - /* Draw vertical line to from the bottom of the current frame box to the bottom of the screen. - */ - const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_CFRAME); - immRectf(pos, - subframe_x - U.pixelsize, - scrub_region_rect->ymax - box_padding, - subframe_x + U.pixelsize, - 0.0f); - immUnbindProgram(); - } + /* Draw vertical line from the bottom of the current frame box to the bottom of the screen. */ + const float subframe_x = UI_view2d_view_to_region_x(v2d, BKE_scene_ctime_get(scene)); + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + GPU_blend(GPU_BLEND_ALPHA); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* Outline. */ + immUniformThemeColorShadeAlpha(TH_BACK, -25, -100); + immRectf(pos, + subframe_x - (line_outline + U.pixelsize), + scrub_region_rect->ymax - box_padding, + subframe_x + (line_outline + U.pixelsize), + 0.0f); + + /* Line. */ + immUniformThemeColor(TH_CFRAME); + immRectf(pos, + subframe_x - U.pixelsize, + scrub_region_rect->ymax - box_padding, + subframe_x + U.pixelsize, + 0.0f); + immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); UI_draw_roundbox_corner_set(UI_CNR_ALL); @@ -152,8 +162,7 @@ static void draw_current_frame(const Scene *scene, void ED_time_scrub_draw_current_frame(const ARegion *region, const Scene *scene, - bool display_seconds, - bool draw_line) + bool display_seconds) { const View2D *v2d = ®ion->v2d; GPU_matrix_push_projection(); @@ -162,7 +171,7 @@ void ED_time_scrub_draw_current_frame(const ARegion *region, rcti scrub_region_rect; get_time_scrub_region_rect(region, &scrub_region_rect); - draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra, draw_line); + draw_current_frame(scene, display_seconds, v2d, &scrub_region_rect, scene->r.cfra); GPU_matrix_pop_projection(); } @@ -199,7 +208,7 @@ bool ED_time_scrub_event_in_region(const ARegion *region, const wmEvent *event) { rcti rect = region->winrct; rect.ymin = rect.ymax - UI_TIME_SCRUB_MARGIN_Y; - return BLI_rcti_isect_pt(&rect, event->x, event->y); + return BLI_rcti_isect_pt_v(&rect, event->xy); } void ED_time_scrub_channel_search_draw(const bContext *C, ARegion *region, bDopeSheet *dopesheet) diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index f9950d27e97..3a6761ba915 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -216,6 +216,7 @@ void POSE_OT_relax(struct wmOperatorType *ot); void POSE_OT_push_rest(struct wmOperatorType *ot); void POSE_OT_relax_rest(struct wmOperatorType *ot); void POSE_OT_breakdown(struct wmOperatorType *ot); +void POSE_OT_blend_to_neighbors(struct wmOperatorType *ot); void POSE_OT_propagate(struct wmOperatorType *ot); diff --git a/source/blender/editors/armature/armature_naming.c b/source/blender/editors/armature/armature_naming.c index 35bd30377c8..de1c14a15ce 100644 --- a/source/blender/editors/armature/armature_naming.c +++ b/source/blender/editors/armature/armature_naming.c @@ -265,7 +265,7 @@ void ED_armature_bone_rename(Main *bmain, } } - if (BKE_modifiers_uses_armature(ob, arm)) { + if (BKE_modifiers_uses_armature(ob, arm) && BKE_object_supports_vertex_groups(ob)) { bDeformGroup *dg = BKE_object_defgroup_find_name(ob, oldname); if (dg) { BLI_strncpy(dg->name, newname, MAXBONENAME); diff --git a/source/blender/editors/armature/armature_ops.c b/source/blender/editors/armature/armature_ops.c index fbd89106de5..75b0455d026 100644 --- a/source/blender/editors/armature/armature_ops.c +++ b/source/blender/editors/armature/armature_ops.c @@ -150,6 +150,7 @@ void ED_operatortypes_armature(void) WM_operatortype_append(POSE_OT_push_rest); WM_operatortype_append(POSE_OT_relax_rest); WM_operatortype_append(POSE_OT_breakdown); + WM_operatortype_append(POSE_OT_blend_to_neighbors); } void ED_operatormacros_armature(void) diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index bd799c00373..937385f9ffa 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -673,11 +673,7 @@ static EditBone *get_nearest_editbonepoint( } if (use_cycle) { - static int last_mval[2] = {-100, -100}; - if ((len_manhattan_v2v2_int(vc->mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) == 0) { - use_cycle = false; - } - copy_v2_v2_int(last_mval, vc->mval); + use_cycle = !WM_cursor_test_motion_and_update(vc->mval); } const bool do_nearest = !(XRAY_ACTIVE(vc->v3d) || use_cycle); diff --git a/source/blender/editors/armature/pose_lib_2.c b/source/blender/editors/armature/pose_lib_2.c index 91a5dc67a21..002a4f74037 100644 --- a/source/blender/editors/armature/pose_lib_2.c +++ b/source/blender/editors/armature/pose_lib_2.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_math.h" #include "BLI_string.h" #include "BLT_translation.h" @@ -209,11 +210,11 @@ static void poselib_slide_mouse_update_blendfactor(PoseBlendData *pbd, const wmE if (pbd->release_confirm_info.use_release_confirm) { /* Release confirm calculates factor based on where the dragging was started from. */ const float range = 300 * U.pixelsize; - const float new_factor = (event->x - pbd->release_confirm_info.drag_start_xy[0]) / range; + const float new_factor = (event->xy[0] - pbd->release_confirm_info.drag_start_xy[0]) / range; poselib_blend_set_factor(pbd, new_factor); } else { - const float new_factor = (event->x - pbd->area->v1->vec.x) / ((float)pbd->area->winx); + const float new_factor = (event->xy[0] - pbd->area->v1->vec.x) / ((float)pbd->area->winx); poselib_blend_set_factor(pbd, new_factor); } } @@ -379,8 +380,7 @@ static bool poselib_blend_init_data(bContext *C, wmOperator *op, const wmEvent * if (pbd->release_confirm_info.use_release_confirm) { BLI_assert(event != NULL); - pbd->release_confirm_info.drag_start_xy[0] = event->x; - pbd->release_confirm_info.drag_start_xy[1] = event->y; + copy_v2_v2_int(pbd->release_confirm_info.drag_start_xy, event->xy); pbd->release_confirm_info.init_event_type = WM_userdef_event_type_from_keymap_type( event->type); } diff --git a/source/blender/editors/armature/pose_slide.c b/source/blender/editors/armature/pose_slide.c index f23376867af..ca024f1d4d6 100644 --- a/source/blender/editors/armature/pose_slide.c +++ b/source/blender/editors/armature/pose_slide.c @@ -117,6 +117,7 @@ typedef enum ePoseSlide_Modes { POSESLIDE_BREAKDOWN, POSESLIDE_PUSH_REST, POSESLIDE_RELAX_REST, + POSESLIDE_BLEND, } ePoseSlide_Modes; /** Transforms/Channels to Affect. */ @@ -423,6 +424,25 @@ static void pose_slide_apply_val(tPoseSlideOp *pso, FCurve *fcu, Object *ob, flo (*val) = ((sVal * w2) + (eVal * w1)); break; } + case POSESLIDE_BLEND: /* Blend the current pose with the previous (<50%) or next key (>50%). */ + { + /* FCurve value on current frame. */ + const float cVal = evaluate_fcurve(fcu, cframe); + const float factor = ED_slider_factor_get(pso->slider); + /* Convert factor to absolute 0-1 range. */ + const float blend_factor = fabs((factor - 0.5f) * 2); + + if (factor < 0.5) { + /* Blend to previous key. */ + (*val) = (cVal * (1 - blend_factor)) + (sVal * blend_factor); + } + else { + /* Blend to next key. */ + (*val) = (cVal * (1 - blend_factor)) + (eVal * blend_factor); + } + + break; + } /* Those are handled in pose_slide_rest_pose_apply. */ case POSESLIDE_PUSH_REST: case POSESLIDE_RELAX_REST: { @@ -614,8 +634,7 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) interp_qt_qtqt(quat_final, quat_prev, quat_next, ED_slider_factor_get(pso->slider)); } - else { - /* POSESLIDE_PUSH and POSESLIDE_RELAX. */ + else if (ELEM(pso->mode, POSESLIDE_PUSH, POSESLIDE_RELAX)) { float quat_breakdown[4]; float quat_curr[4]; @@ -638,6 +657,32 @@ static void pose_slide_apply_quat(tPoseSlideOp *pso, tPChanFCurveLink *pfl) interp_qt_qtqt(quat_final, quat_curr, quat_breakdown, ED_slider_factor_get(pso->slider)); } } + else if (pso->mode == POSESLIDE_BLEND) { + float quat_blend[4]; + float quat_curr[4]; + + copy_qt_qt(quat_curr, pchan->quat); + + if (ED_slider_factor_get(pso->slider) < 0.5) { + quat_blend[0] = evaluate_fcurve(fcu_w, prevFrameF); + quat_blend[1] = evaluate_fcurve(fcu_x, prevFrameF); + quat_blend[2] = evaluate_fcurve(fcu_y, prevFrameF); + quat_blend[3] = evaluate_fcurve(fcu_z, prevFrameF); + } + else { + quat_blend[0] = evaluate_fcurve(fcu_w, nextFrameF); + quat_blend[1] = evaluate_fcurve(fcu_x, nextFrameF); + quat_blend[2] = evaluate_fcurve(fcu_y, nextFrameF); + quat_blend[3] = evaluate_fcurve(fcu_z, nextFrameF); + } + + normalize_qt(quat_blend); + normalize_qt(quat_curr); + + const float blend_factor = fabs((ED_slider_factor_get(pso->slider) - 0.5f) * 2); + + interp_qt_qtqt(quat_final, quat_curr, quat_blend, blend_factor); + } /* Apply final to the pose bone, keeping compatible for similar keyframe positions. */ quat_to_compatible_quat(pchan->quat, quat_final, pchan->quat); @@ -868,6 +913,9 @@ static void pose_slide_draw_status(bContext *C, tPoseSlideOp *pso) case POSESLIDE_BREAKDOWN: strcpy(mode_str, TIP_("Breakdown")); break; + case POSESLIDE_BLEND: + strcpy(mode_str, TIP_("Blend To Neighbor")); + break; default: /* Unknown. */ @@ -994,7 +1042,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * const ActKeyColumn *nk = ED_keylist_find_next(pso->keylist, cframe); /* New set the frames. */ - /* Prev frame. */ + /* Previous frame. */ pso->prevFrame = (pk) ? (pk->cfra) : (pso->cframe - 1); RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); /* Next frame. */ @@ -1003,7 +1051,7 @@ static int pose_slide_invoke_common(bContext *C, wmOperator *op, const wmEvent * } else { /* Current frame itself is a keyframe, so just take keyframes on either side. */ - /* Prev frame. */ + /* Previous frame. */ pso->prevFrame = (ak->prev) ? (ak->prev->cfra) : (pso->cframe - 1); RNA_int_set(op->ptr, "prev_frame", pso->prevFrame); /* Next frame. */ @@ -1660,6 +1708,56 @@ void POSE_OT_breakdown(wmOperatorType *ot) pose_slide_opdef_properties(ot); } +/* ........................ */ +static int pose_slide_blend_to_neighbors_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + /* Initialize data. */ + if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) { + pose_slide_exit(C, op); + return OPERATOR_CANCELLED; + } + + /* Do common setup work. */ + return pose_slide_invoke_common(C, op, event); +} + +static int pose_slide_blend_to_neighbors_exec(bContext *C, wmOperator *op) +{ + tPoseSlideOp *pso; + + /* Initialize data (from RNA-props). */ + if (pose_slide_init(C, op, POSESLIDE_BLEND) == 0) { + pose_slide_exit(C, op); + return OPERATOR_CANCELLED; + } + + pso = op->customdata; + + /* Do common exec work. */ + return pose_slide_exec_common(C, op, pso); +} + +void POSE_OT_blend_to_neighbors(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Blend To Neighbor"; + ot->idname = "POSE_OT_blend_to_neighbor"; + ot->description = "Blend from current position to previous or next keyframe"; + + /* Callbacks. */ + ot->exec = pose_slide_blend_to_neighbors_exec; + ot->invoke = pose_slide_blend_to_neighbors_invoke; + ot->modal = pose_slide_modal; + ot->cancel = pose_slide_cancel; + ot->poll = ED_operator_posemode; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING | OPTYPE_GRAB_CURSOR_X; + + /* Properties. */ + pose_slide_opdef_properties(ot); +} + /* **************************************************** */ /* B) Pose Propagate */ diff --git a/source/blender/editors/armature/pose_transform.c b/source/blender/editors/armature/pose_transform.c index 3798ca308ed..279f79ac44b 100644 --- a/source/blender/editors/armature/pose_transform.c +++ b/source/blender/editors/armature/pose_transform.c @@ -1184,7 +1184,7 @@ static int pose_clear_transform_generic_exec(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = CTX_wm_view3d(C); FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, v3d, OB_ARMATURE, OB_MODE_POSE, ob_iter) { - /* XXX: UGLY HACK (for autokey + clear transforms) */ + /* XXX: UGLY HACK (for auto-key + clear transforms). */ Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_iter); ListBase dsources = {NULL, NULL}; bool changed = false; diff --git a/source/blender/editors/asset/CMakeLists.txt b/source/blender/editors/asset/CMakeLists.txt index 31c07580570..b9ef8e82bba 100644 --- a/source/blender/editors/asset/CMakeLists.txt +++ b/source/blender/editors/asset/CMakeLists.txt @@ -31,6 +31,7 @@ set(INC_SYS ) set(SRC + intern/asset_catalog.cc intern/asset_filter.cc intern/asset_handle.cc intern/asset_library_reference.cc @@ -39,7 +40,10 @@ set(SRC intern/asset_mark_clear.cc intern/asset_ops.cc intern/asset_temp_id_consumer.cc + intern/asset_type.cc + ED_asset_catalog.h + ED_asset_catalog.hh ED_asset_filter.h ED_asset_handle.h ED_asset_library.h @@ -47,6 +51,7 @@ set(SRC ED_asset_list.hh ED_asset_mark_clear.h ED_asset_temp_id_consumer.h + ED_asset_type.h intern/asset_library_reference.hh ) diff --git a/source/blender/editors/asset/ED_asset_catalog.h b/source/blender/editors/asset/ED_asset_catalog.h new file mode 100644 index 00000000000..451ec0d5984 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_catalog.h @@ -0,0 +1,39 @@ +/* + * 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 edasset + */ + +#pragma once + +#include "BLI_utildefines.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct Main; +struct AssetLibrary; + +void ED_asset_catalogs_save_from_main_path(struct AssetLibrary *library, const struct Main *bmain); + +void ED_asset_catalogs_set_save_catalogs_when_file_is_saved(bool should_save); +bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/ED_asset_catalog.hh b/source/blender/editors/asset/ED_asset_catalog.hh new file mode 100644 index 00000000000..8da8fc0d6c9 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_catalog.hh @@ -0,0 +1,42 @@ +/* + * 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 edasset + */ + +#pragma once + +#include "BKE_asset_catalog.hh" + +#include "BLI_string_ref.hh" + +struct AssetLibrary; +namespace blender::bke { +class AssetCatalog; +} // namespace blender::bke + +blender::bke::AssetCatalog *ED_asset_catalog_add(AssetLibrary *library, + blender::StringRefNull name, + blender::StringRef parent_path = nullptr); +void ED_asset_catalog_remove(AssetLibrary *library, const blender::bke::CatalogID &catalog_id); + +void ED_asset_catalog_rename(AssetLibrary *library, + blender::bke::CatalogID catalog_id, + blender::StringRefNull new_name); +void ED_asset_catalog_move(AssetLibrary *library, + blender::bke::CatalogID src_catalog_id, + blender::bke::CatalogID dst_parent_catalog_id); diff --git a/source/blender/editors/asset/ED_asset_mark_clear.h b/source/blender/editors/asset/ED_asset_mark_clear.h index 7524ec6b02a..8e6a8e11d69 100644 --- a/source/blender/editors/asset/ED_asset_mark_clear.h +++ b/source/blender/editors/asset/ED_asset_mark_clear.h @@ -26,10 +26,35 @@ extern "C" { struct ID; struct bContext; +struct Main; -bool ED_asset_mark_id(const struct bContext *C, struct ID *id); +/** + * Mark the datablock as asset. + * + * To ensure the datablock is saved, this sets Fake User. + * + * \return whether the datablock was marked as asset; false when it is not capable of becoming an + * asset, or when it already was an asset. */ +bool ED_asset_mark_id(struct ID *id); + +/** + * Generate preview image for the given datablock. + * + * The preview image might be generated using a background thread. + */ +void ED_asset_generate_preview(const struct bContext *C, struct ID *id); + +/** + * Remove the asset metadata, turning the ID into a "normal" ID. + * + * This clears the Fake User. If for some reason the datablock is meant to be saved anyway, the + * caller is responsible for explicitly setting the Fake User. + * + * \return whether the asset metadata was actually removed; false when the ID was not an asset. */ bool ED_asset_clear_id(struct ID *id); +void ED_assets_pre_save(struct Main *bmain); + bool ED_asset_can_mark_single_from_context(const struct bContext *C); #ifdef __cplusplus diff --git a/source/blender/editors/asset/ED_asset_type.h b/source/blender/editors/asset/ED_asset_type.h new file mode 100644 index 00000000000..36cbb4591e9 --- /dev/null +++ b/source/blender/editors/asset/ED_asset_type.h @@ -0,0 +1,59 @@ +/* + * 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 edasset + */ + +#pragma once + +#include "DNA_ID.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct ID; + +bool ED_asset_type_id_is_non_experimental(const struct ID *id); +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS \ + (FILTER_ID_MA | FILTER_ID_OB | FILTER_ID_AC | FILTER_ID_WO) + +/** + * Check if the asset type for \a id (which doesn't need to be an asset right now) can be an asset, + * respecting the "Extended Asset Browser" experimental feature flag. + */ +bool ED_asset_type_is_supported(const ID *id); + +/** + * Get the filter flags (subset of #FILTER_ID_ALL) representing the asset ID types that may be + * turned into assets, respecting the "Extended Asset Browser" experimental feature flag. + * \note Does not check for #BKE_id_can_be_asset(), so may return filter flags for IDs that can + * never be assets. + */ +int64_t ED_asset_types_supported_as_filter_flags(void); + +/** + * Utility: A string enumerating the non-experimental asset types. This is useful info to + * the user, it should be displayed in tooltips or messages. Macro to support concatenating static + * strings with this (not all UI code supports dynamic strings nicely). + * Should start with a consonant, so usages can prefix it with "a" (not "an"). + */ +#define ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING "Material, Object, Pose Action, or World" + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/asset/intern/asset_catalog.cc b/source/blender/editors/asset/intern/asset_catalog.cc new file mode 100644 index 00000000000..9634665be7b --- /dev/null +++ b/source/blender/editors/asset/intern/asset_catalog.cc @@ -0,0 +1,172 @@ +/* + * 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 edasset + */ + +#include "BKE_asset_catalog.hh" +#include "BKE_asset_library.hh" +#include "BKE_main.h" + +#include "BLI_string_utils.h" + +#include "ED_asset_catalog.h" +#include "ED_asset_catalog.hh" + +#include "WM_api.h" + +using namespace blender; +using namespace blender::bke; + +struct CatalogUniqueNameFnData { + const AssetCatalogService &catalog_service; + StringRef parent_path; +}; + +static bool catalog_name_exists_fn(void *arg, const char *name) +{ + CatalogUniqueNameFnData &fn_data = *static_cast(arg); + AssetCatalogPath fullpath = AssetCatalogPath(fn_data.parent_path) / name; + return fn_data.catalog_service.find_catalog_by_path(fullpath); +} + +static std::string catalog_name_ensure_unique(AssetCatalogService &catalog_service, + StringRefNull name, + StringRef parent_path) +{ + CatalogUniqueNameFnData fn_data = {catalog_service, parent_path}; + + char unique_name[MAX_NAME] = ""; + BLI_uniquename_cb( + catalog_name_exists_fn, &fn_data, name.c_str(), '.', unique_name, sizeof(unique_name)); + + return unique_name; +} + +AssetCatalog *ED_asset_catalog_add(::AssetLibrary *library, + StringRefNull name, + StringRef parent_path) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + return nullptr; + } + + std::string unique_name = catalog_name_ensure_unique(*catalog_service, name, parent_path); + AssetCatalogPath fullpath = AssetCatalogPath(parent_path) / unique_name; + + catalog_service->undo_push(); + bke::AssetCatalog *new_catalog = catalog_service->create_catalog(fullpath); + if (!new_catalog) { + return nullptr; + } + catalog_service->tag_has_unsaved_changes(new_catalog); + + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + return new_catalog; +} + +void ED_asset_catalog_remove(::AssetLibrary *library, const CatalogID &catalog_id) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + catalog_service->undo_push(); + catalog_service->tag_has_unsaved_changes(nullptr); + catalog_service->prune_catalogs_by_id(catalog_id); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); +} + +void ED_asset_catalog_rename(::AssetLibrary *library, + const CatalogID catalog_id, + const StringRefNull new_name) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + AssetCatalog *catalog = catalog_service->find_catalog(catalog_id); + + const AssetCatalogPath new_path = catalog->path.parent() / StringRef(new_name); + const AssetCatalogPath clean_new_path = new_path.cleanup(); + + if (new_path == catalog->path || clean_new_path == catalog->path) { + /* Nothing changed, so don't bother renaming for nothing. */ + return; + } + + catalog_service->undo_push(); + catalog_service->tag_has_unsaved_changes(catalog); + catalog_service->update_catalog_path(catalog_id, clean_new_path); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); +} + +void ED_asset_catalog_move(::AssetLibrary *library, + const CatalogID src_catalog_id, + const CatalogID dst_parent_catalog_id) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + AssetCatalog *src_catalog = catalog_service->find_catalog(src_catalog_id); + AssetCatalog *dst_catalog = catalog_service->find_catalog(dst_parent_catalog_id); + + const AssetCatalogPath new_path = dst_catalog->path / StringRef(src_catalog->path.name()); + const AssetCatalogPath clean_new_path = new_path.cleanup(); + + if (new_path == src_catalog->path || clean_new_path == src_catalog->path) { + /* Nothing changed, so don't bother renaming for nothing. */ + return; + } + + catalog_service->undo_push(); + catalog_service->tag_has_unsaved_changes(src_catalog); + catalog_service->update_catalog_path(src_catalog_id, clean_new_path); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); +} + +void ED_asset_catalogs_save_from_main_path(::AssetLibrary *library, const Main *bmain) +{ + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(library); + if (!catalog_service) { + BLI_assert_unreachable(); + return; + } + + /* Since writing to disk also means loading any on-disk changes, it may be a good idea to store + * an undo step. */ + catalog_service->undo_push(); + catalog_service->write_to_disk(bmain->name); +} + +void ED_asset_catalogs_set_save_catalogs_when_file_is_saved(const bool should_save) +{ + bke::AssetLibrary::save_catalogs_when_file_is_saved = should_save; +} + +bool ED_asset_catalogs_get_save_catalogs_when_file_is_saved() +{ + return bke::AssetLibrary::save_catalogs_when_file_is_saved; +} diff --git a/source/blender/editors/asset/intern/asset_filter.cc b/source/blender/editors/asset/intern/asset_filter.cc index 329342a30cd..c22bbc923eb 100644 --- a/source/blender/editors/asset/intern/asset_filter.cc +++ b/source/blender/editors/asset/intern/asset_filter.cc @@ -22,7 +22,6 @@ #include "BLI_listbase.h" -#include "DNA_ID.h" #include "DNA_asset_types.h" #include "ED_asset_filter.h" diff --git a/source/blender/editors/asset/intern/asset_handle.cc b/source/blender/editors/asset/intern/asset_handle.cc index 5c8d0b1349c..363bd9226da 100644 --- a/source/blender/editors/asset/intern/asset_handle.cc +++ b/source/blender/editors/asset/intern/asset_handle.cc @@ -26,7 +26,6 @@ #include -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BLO_readfile.h" diff --git a/source/blender/editors/asset/intern/asset_library_reference_enum.cc b/source/blender/editors/asset/intern/asset_library_reference_enum.cc index 5a8fed6fea1..1a2d3f5837a 100644 --- a/source/blender/editors/asset/intern/asset_library_reference_enum.cc +++ b/source/blender/editors/asset/intern/asset_library_reference_enum.cc @@ -27,7 +27,6 @@ #include "BKE_preferences.h" -#include "DNA_asset_types.h" #include "DNA_userdef_types.h" #include "UI_resources.h" @@ -55,7 +54,6 @@ int ED_asset_library_reference_to_enum_value(const AssetLibraryReference *librar return ASSET_LIBRARY_CUSTOM + library->custom_library_index; } - BLI_assert_unreachable(); return ASSET_LIBRARY_LOCAL; } @@ -80,14 +78,16 @@ AssetLibraryReference ED_asset_library_reference_from_enum_value(int value) /* Note that there is no check if the path exists here. If an invalid library path is used, the * Asset Browser can give a nice hint on what's wrong. */ - const bool is_valid = (user_library->name[0] && user_library->path[0]); if (!user_library) { library.type = ASSET_LIBRARY_LOCAL; library.custom_library_index = -1; } - else if (user_library && is_valid) { - library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; - library.type = ASSET_LIBRARY_CUSTOM; + else { + const bool is_valid = (user_library->name[0] && user_library->path[0]); + if (is_valid) { + library.custom_library_index = value - ASSET_LIBRARY_CUSTOM; + library.type = ASSET_LIBRARY_CUSTOM; + } } return library; } @@ -115,10 +115,12 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf() EnumPropertyItem *item = nullptr; int totitem = 0; + /* Add predefined items. */ + RNA_enum_items_add(&item, &totitem, predefined_items); + /* Add separator if needed. */ if (!BLI_listbase_is_empty(&U.asset_libraries)) { - const EnumPropertyItem sepr = {0, "", 0, "Custom", nullptr}; - RNA_enum_item_add(&item, &totitem, &sepr); + RNA_enum_item_add_separator(&item, &totitem); } int i = 0; @@ -143,14 +145,6 @@ const EnumPropertyItem *ED_asset_library_reference_to_rna_enum_itemf() RNA_enum_item_add(&item, &totitem, &tmp); } - if (totitem) { - const EnumPropertyItem sepr = {0, "", 0, "Built-in", nullptr}; - RNA_enum_item_add(&item, &totitem, &sepr); - } - - /* Add predefined items. */ - RNA_enum_items_add(&item, &totitem, predefined_items); - RNA_enum_item_end(&item, &totitem); return item; } diff --git a/source/blender/editors/asset/intern/asset_list.cc b/source/blender/editors/asset/intern/asset_list.cc index 57c25e1614b..b5ea054fb5d 100644 --- a/source/blender/editors/asset/intern/asset_list.cc +++ b/source/blender/editors/asset/intern/asset_list.cc @@ -32,7 +32,6 @@ #include "BLI_path_util.h" #include "BLI_utility_mixins.hh" -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BKE_preferences.h" @@ -40,7 +39,6 @@ #include "ED_fileselect.h" #include "WM_api.h" -#include "WM_types.h" /* XXX uses private header of file-space. */ #include "../space_file/filelist.h" @@ -157,7 +155,7 @@ void AssetList::setup() /* Relevant bits from file_refresh(). */ /* TODO pass options properly. */ - filelist_setrecursion(files, 1); + filelist_setrecursion(files, FILE_SELECT_MAX_RECURSIONS); filelist_setsorting(files, FILE_SORT_ALPHA, false); filelist_setlibrary(files, &library_ref_); filelist_setfilter_options( @@ -187,7 +185,7 @@ void AssetList::fetch(const bContext &C) if (filelist_needs_force_reset(files)) { filelist_readjob_stop(files, CTX_wm_manager(&C)); - filelist_clear(files); + filelist_clear_from_reset_tag(files); } if (filelist_needs_reading(files)) { @@ -295,9 +293,7 @@ int AssetList::size() const void AssetList::tagMainDataDirty() const { if (filelist_needs_reset_on_main_changes(filelist_)) { - /* Full refresh of the file list if local asset data was changed. Refreshing this view - * is cheap and users expect this to be updated immediately. */ - filelist_tag_force_reset(filelist_); + filelist_tag_force_reset_mainfiles(filelist_); } } @@ -390,7 +386,7 @@ std::optional AssetListStorage::asset_library_reference_to_file { switch (library_reference.type) { case ASSET_LIBRARY_CUSTOM: - return FILE_LOADLIB; + return FILE_ASSET_LIBRARY; case ASSET_LIBRARY_LOCAL: return FILE_MAIN_ASSET; } diff --git a/source/blender/editors/asset/intern/asset_mark_clear.cc b/source/blender/editors/asset/intern/asset_mark_clear.cc index ba348e38823..2e5bdb63359 100644 --- a/source/blender/editors/asset/intern/asset_mark_clear.cc +++ b/source/blender/editors/asset/intern/asset_mark_clear.cc @@ -20,18 +20,14 @@ * Functions for marking and clearing assets. */ -#include -#include +#include "DNA_ID.h" #include "BKE_asset.h" #include "BKE_context.h" +#include "BKE_icons.h" +#include "BKE_idtype.h" #include "BKE_lib_id.h" - -#include "BLO_readfile.h" - -#include "DNA_ID.h" -#include "DNA_asset_types.h" -#include "DNA_space_types.h" +#include "BKE_main.h" #include "UI_interface_icons.h" @@ -39,8 +35,9 @@ #include "ED_asset_list.h" #include "ED_asset_mark_clear.h" +#include "ED_asset_type.h" -bool ED_asset_mark_id(const bContext *C, ID *id) +bool ED_asset_mark_id(ID *id) { if (id->asset_data) { return false; @@ -51,9 +48,9 @@ bool ED_asset_mark_id(const bContext *C, ID *id) id_fake_user_set(id); + const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id); id->asset_data = BKE_asset_metadata_create(); - - UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); + id->asset_data->local_type_info = id_type_info->asset_type_info; /* Important for asset storage to update properly! */ ED_assetlist_storage_tag_main_data_dirty(); @@ -61,14 +58,23 @@ bool ED_asset_mark_id(const bContext *C, ID *id) return true; } +void ED_asset_generate_preview(const bContext *C, ID *id) +{ + PreviewImage *preview = BKE_previewimg_id_get(id); + if (preview) { + BKE_previewimg_clear(preview); + } + + UI_icon_render_id(C, nullptr, id, ICON_SIZE_PREVIEW, true); +} + bool ED_asset_clear_id(ID *id) { if (!id->asset_data) { return false; } BKE_asset_metadata_free(&id->asset_data); - /* Don't clear fake user here, there's no guarantee that it was actually set by - * #ED_asset_mark_id(), it might have been something/someone else. */ + id_fake_user_clear(id); /* Important for asset storage to update properly! */ ED_assetlist_storage_tag_main_data_dirty(); @@ -76,8 +82,27 @@ bool ED_asset_clear_id(ID *id) return true; } +void ED_assets_pre_save(struct Main *bmain) +{ + ID *id; + FOREACH_MAIN_ID_BEGIN (bmain, id) { + if (!id->asset_data || !id->asset_data->local_type_info) { + continue; + } + + if (id->asset_data->local_type_info->pre_save_fn) { + id->asset_data->local_type_info->pre_save_fn(id, id->asset_data); + } + } + FOREACH_MAIN_ID_END; +} + bool ED_asset_can_mark_single_from_context(const bContext *C) { /* Context needs a "id" pointer to be set for #ASSET_OT_mark()/#ASSET_OT_clear() to use. */ - return CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data != nullptr; + const ID *id = static_cast(CTX_data_pointer_get_type_silent(C, "id", &RNA_ID).data); + if (!id) { + return false; + } + return ED_asset_type_is_supported(id); } diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc index d69a2cae94d..f7c567c89f6 100644 --- a/source/blender/editors/asset/intern/asset_ops.cc +++ b/source/blender/editors/asset/intern/asset_ops.cc @@ -18,27 +18,27 @@ * \ingroup edasset */ +#include "BKE_asset_library.hh" #include "BKE_context.h" +#include "BKE_lib_id.h" +#include "BKE_main.h" #include "BKE_report.h" -#include "BLI_vector.hh" - #include "ED_asset.h" +/* XXX needs access to the file list, should all be done via the asset system in future. */ +#include "ED_fileselect.h" #include "RNA_access.h" +#include "RNA_define.h" #include "WM_api.h" -#include "WM_types.h" + +using namespace blender; /* -------------------------------------------------------------------- */ using PointerRNAVec = blender::Vector; -static bool asset_operation_poll(bContext * /*C*/) -{ - return U.experimental.use_extended_asset_browser; -} - /** * Return the IDs to operate on as PointerRNA vector. Either a single one ("id" context member) or * multiple ones ("selected_ids" context member). @@ -64,6 +64,53 @@ static PointerRNAVec asset_operation_get_ids_from_context(const bContext *C) return ids; } +/** + * Information about what's contained in a #PointerRNAVec, returned by + * #asset_operation_get_id_vec_stats_from_context(). + */ +struct IDVecStats { + bool has_asset = false; + bool has_supported_type = false; + bool is_single = false; +}; + +/** + * Helper to report stats about the IDs in context. Operator polls use this, also to report a + * helpful disabled hint to the user. + */ +static IDVecStats asset_operation_get_id_vec_stats_from_context(const bContext *C) +{ + PointerRNAVec pointers = asset_operation_get_ids_from_context(C); + IDVecStats stats; + + stats.is_single = pointers.size() == 1; + + for (PointerRNA &ptr : pointers) { + BLI_assert(RNA_struct_is_ID(ptr.type)); + + ID *id = static_cast(ptr.data); + if (ED_asset_type_is_supported(id)) { + stats.has_supported_type = true; + } + if (ID_IS_ASSET(id)) { + stats.has_asset = true; + } + } + + return stats; +} + +static const char *asset_operation_unsupported_type_msg(const bool is_single) +{ + const char *msg_single = + "Data-block does not support asset operations - must be " + "a " ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING; + const char *msg_multiple = + "No data-block selected that supports asset operations - select at least " + "one " ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING; + return is_single ? msg_single : msg_multiple; +} + /* -------------------------------------------------------------------- */ class AssetMarkHelper { @@ -94,7 +141,9 @@ void AssetMarkHelper::operator()(const bContext &C, PointerRNAVec &ids) continue; } - if (ED_asset_mark_id(&C, id)) { + if (ED_asset_mark_id(id)) { + ED_asset_generate_preview(&C, id); + stats.last_id = id; stats.tot_created++; } @@ -149,6 +198,18 @@ static int asset_mark_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool asset_mark_poll(bContext *C) +{ + IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C); + + if (!ctx_stats.has_supported_type) { + CTX_wm_operator_poll_msg_set(C, asset_operation_unsupported_type_msg(ctx_stats.is_single)); + return false; + } + + return true; +} + static void ASSET_OT_mark(wmOperatorType *ot) { ot->name = "Mark as Asset"; @@ -158,7 +219,7 @@ static void ASSET_OT_mark(wmOperatorType *ot) ot->idname = "ASSET_OT_mark"; ot->exec = asset_mark_exec; - ot->poll = asset_operation_poll; + ot->poll = asset_mark_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } @@ -166,7 +227,13 @@ static void ASSET_OT_mark(wmOperatorType *ot) /* -------------------------------------------------------------------- */ class AssetClearHelper { + const bool set_fake_user_; + public: + AssetClearHelper(const bool set_fake_user) : set_fake_user_(set_fake_user) + { + } + void operator()(PointerRNAVec &ids); void reportResults(const bContext *C, ReportList &reports) const; @@ -191,10 +258,16 @@ void AssetClearHelper::operator()(PointerRNAVec &ids) continue; } - if (ED_asset_clear_id(id)) { - stats.tot_cleared++; - stats.last_id = id; + if (!ED_asset_clear_id(id)) { + continue; } + + if (set_fake_user_) { + id_fake_user_set(id); + } + + stats.tot_cleared++; + stats.last_id = id; } } @@ -234,7 +307,8 @@ static int asset_clear_exec(bContext *C, wmOperator *op) { PointerRNAVec ids = asset_operation_get_ids_from_context(C); - AssetClearHelper clear_helper; + const bool set_fake_user = RNA_boolean_get(op->ptr, "set_fake_user"); + AssetClearHelper clear_helper(set_fake_user); clear_helper(ids); clear_helper.reportResults(C, *op->reports); @@ -248,18 +322,57 @@ static int asset_clear_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } +static bool asset_clear_poll(bContext *C) +{ + IDVecStats ctx_stats = asset_operation_get_id_vec_stats_from_context(C); + + if (!ctx_stats.has_asset) { + const char *msg_single = "Data-block is not marked as asset"; + const char *msg_multiple = "No data-block selected that is marked as asset"; + CTX_wm_operator_poll_msg_set(C, ctx_stats.is_single ? msg_single : msg_multiple); + return false; + } + if (!ctx_stats.has_supported_type) { + CTX_wm_operator_poll_msg_set(C, asset_operation_unsupported_type_msg(ctx_stats.is_single)); + return false; + } + + return true; +} + +static char *asset_clear_get_description(struct bContext *UNUSED(C), + struct wmOperatorType *UNUSED(op), + struct PointerRNA *values) +{ + const bool set_fake_user = RNA_boolean_get(values, "set_fake_user"); + if (!set_fake_user) { + return nullptr; + } + + return BLI_strdup( + "Delete all asset metadata, turning the selected asset data-blocks back into normal " + "data-blocks, and set Fake User to ensure the data-blocks will still be saved"); +} + static void ASSET_OT_clear(wmOperatorType *ot) { ot->name = "Clear Asset"; ot->description = "Delete all asset metadata and turn the selected asset data-blocks back into normal " "data-blocks"; + ot->get_description = asset_clear_get_description; ot->idname = "ASSET_OT_clear"; ot->exec = asset_clear_exec; - ot->poll = asset_operation_poll; + ot->poll = asset_clear_poll; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_boolean(ot->srna, + "set_fake_user", + false, + "Set Fake User", + "Ensure the data-block is saved, even when it is no longer marked as asset"); } /* -------------------------------------------------------------------- */ @@ -295,10 +408,252 @@ static void ASSET_OT_list_refresh(struct wmOperatorType *ot) /* -------------------------------------------------------------------- */ +static bool asset_catalog_operator_poll(bContext *C) +{ + const SpaceFile *sfile = CTX_wm_space_file(C); + return sfile && ED_fileselect_active_asset_library_get(sfile); +} + +static int asset_catalog_new_exec(bContext *C, wmOperator *op) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + char *parent_path = RNA_string_get_alloc(op->ptr, "parent_path", nullptr, 0, nullptr); + + blender::bke::AssetCatalog *new_catalog = ED_asset_catalog_add( + asset_library, "Catalog", parent_path); + + if (sfile) { + ED_fileselect_activate_asset_catalog(sfile, new_catalog->catalog_id); + } + + MEM_freeN(parent_path); + + WM_event_add_notifier_ex( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_catalog_new(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "New Asset Catalog"; + ot->description = "Create a new catalog to put assets in"; + ot->idname = "ASSET_OT_catalog_new"; + + /* api callbacks */ + ot->exec = asset_catalog_new_exec; + ot->poll = asset_catalog_operator_poll; + + RNA_def_string(ot->srna, + "parent_path", + nullptr, + 0, + "Parent Path", + "Optional path defining the location to put the new catalog under"); +} + +static int asset_catalog_delete_exec(bContext *C, wmOperator *op) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + struct AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + char *catalog_id_str = RNA_string_get_alloc(op->ptr, "catalog_id", nullptr, 0, nullptr); + bke::CatalogID catalog_id; + if (!BLI_uuid_parse_string(&catalog_id, catalog_id_str)) { + return OPERATOR_CANCELLED; + } + + ED_asset_catalog_remove(asset_library, catalog_id); + + MEM_freeN(catalog_id_str); + + WM_event_add_notifier_ex( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_catalog_delete(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Delete Asset Catalog"; + ot->description = + "Remove an asset catalog from the asset library (contained assets will not be affected and " + "show up as unassigned)"; + ot->idname = "ASSET_OT_catalog_delete"; + + /* api callbacks */ + ot->exec = asset_catalog_delete_exec; + ot->poll = asset_catalog_operator_poll; + + RNA_def_string(ot->srna, "catalog_id", nullptr, 0, "Catalog ID", "ID of the catalog to delete"); +} + +static bke::AssetCatalogService *get_catalog_service(bContext *C) +{ + const SpaceFile *sfile = CTX_wm_space_file(C); + if (!sfile) { + return nullptr; + } + + AssetLibrary *asset_lib = ED_fileselect_active_asset_library_get(sfile); + return BKE_asset_library_get_catalog_service(asset_lib); +} + +static int asset_catalog_undo_exec(bContext *C, wmOperator * /*op*/) +{ + bke::AssetCatalogService *catalog_service = get_catalog_service(C); + if (!catalog_service) { + return OPERATOR_CANCELLED; + } + + catalog_service->undo(); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + return OPERATOR_FINISHED; +} + +static bool asset_catalog_undo_poll(bContext *C) +{ + const bke::AssetCatalogService *catalog_service = get_catalog_service(C); + return catalog_service && catalog_service->is_undo_possbile(); +} + +static void ASSET_OT_catalog_undo(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Undo Catalog Edits"; + ot->description = "Undo the last edit to the asset catalogs"; + ot->idname = "ASSET_OT_catalog_undo"; + + /* api callbacks */ + ot->exec = asset_catalog_undo_exec; + ot->poll = asset_catalog_undo_poll; +} + +static int asset_catalog_redo_exec(bContext *C, wmOperator * /*op*/) +{ + bke::AssetCatalogService *catalog_service = get_catalog_service(C); + if (!catalog_service) { + return OPERATOR_CANCELLED; + } + + catalog_service->redo(); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + return OPERATOR_FINISHED; +} + +static bool asset_catalog_redo_poll(bContext *C) +{ + const bke::AssetCatalogService *catalog_service = get_catalog_service(C); + return catalog_service && catalog_service->is_redo_possbile(); +} + +static void ASSET_OT_catalog_redo(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Redo Catalog Edits"; + ot->description = "Redo the last undone edit to the asset catalogs"; + ot->idname = "ASSET_OT_catalog_redo"; + + /* api callbacks */ + ot->exec = asset_catalog_redo_exec; + ot->poll = asset_catalog_redo_poll; +} + +static int asset_catalog_undo_push_exec(bContext *C, wmOperator * /*op*/) +{ + bke::AssetCatalogService *catalog_service = get_catalog_service(C); + if (!catalog_service) { + return OPERATOR_CANCELLED; + } + + catalog_service->undo_push(); + return OPERATOR_FINISHED; +} + +static bool asset_catalog_undo_push_poll(bContext *C) +{ + return get_catalog_service(C) != nullptr; +} + +static void ASSET_OT_catalog_undo_push(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Store undo snapshot for asset catalog edits"; + ot->description = "Store the current state of the asset catalogs in the undo buffer"; + ot->idname = "ASSET_OT_catalog_undo_push"; + + /* api callbacks */ + ot->exec = asset_catalog_undo_push_exec; + ot->poll = asset_catalog_undo_push_poll; + + /* Generally artists don't need to find & use this operator, it's meant for scripts only. */ + ot->flag = OPTYPE_INTERNAL; +} + +/* -------------------------------------------------------------------- */ + +static bool asset_catalogs_save_poll(bContext *C) +{ + if (!asset_catalog_operator_poll(C)) { + return false; + } + + const Main *bmain = CTX_data_main(C); + if (!bmain->name[0]) { + CTX_wm_operator_poll_msg_set(C, "Cannot save asset catalogs before the Blender file is saved"); + return false; + } + + if (!BKE_asset_library_has_any_unsaved_catalogs()) { + CTX_wm_operator_poll_msg_set(C, "No changes to be saved"); + return false; + } + + return true; +} + +static int asset_catalogs_save_exec(bContext *C, wmOperator * /*op*/) +{ + const SpaceFile *sfile = CTX_wm_space_file(C); + ::AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + + ED_asset_catalogs_save_from_main_path(asset_library, CTX_data_main(C)); + + WM_event_add_notifier_ex( + CTX_wm_manager(C), CTX_wm_window(C), NC_ASSET | ND_ASSET_CATALOGS, nullptr); + + return OPERATOR_FINISHED; +} + +static void ASSET_OT_catalogs_save(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Save Asset Catalogs"; + ot->description = + "Make any edits to any catalogs permanent by writing the current set up to the asset " + "library"; + ot->idname = "ASSET_OT_catalogs_save"; + + /* api callbacks */ + ot->exec = asset_catalogs_save_exec; + ot->poll = asset_catalogs_save_poll; +} + +/* -------------------------------------------------------------------- */ + void ED_operatortypes_asset(void) { WM_operatortype_append(ASSET_OT_mark); WM_operatortype_append(ASSET_OT_clear); + WM_operatortype_append(ASSET_OT_catalog_new); + WM_operatortype_append(ASSET_OT_catalog_delete); + WM_operatortype_append(ASSET_OT_catalogs_save); + WM_operatortype_append(ASSET_OT_catalog_undo); + WM_operatortype_append(ASSET_OT_catalog_redo); + WM_operatortype_append(ASSET_OT_catalog_undo_push); + WM_operatortype_append(ASSET_OT_list_refresh); } diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc index f664eab5cbb..f136c08f129 100644 --- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc +++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc @@ -23,7 +23,6 @@ #include -#include "DNA_asset_types.h" #include "DNA_space_types.h" #include "BKE_report.h" diff --git a/source/blender/editors/asset/intern/asset_type.cc b/source/blender/editors/asset/intern/asset_type.cc new file mode 100644 index 00000000000..028c0cb9ffc --- /dev/null +++ b/source/blender/editors/asset/intern/asset_type.cc @@ -0,0 +1,58 @@ +/* + * 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 edasset + */ + +#include "BLI_utildefines.h" + +#include "DNA_userdef_types.h" + +#include "BKE_lib_id.h" + +#include "ED_asset_type.h" + +bool ED_asset_type_id_is_non_experimental(const ID *id) +{ + /* Remember to update #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_UI_STRING and + * #ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS() with this! */ + return ELEM(GS(id->name), ID_MA, ID_OB, ID_AC, ID_WO); +} + +bool ED_asset_type_is_supported(const ID *id) +{ + if (!BKE_id_can_be_asset(id)) { + return false; + } + + if (U.experimental.use_extended_asset_browser) { + /* The "Extended Asset Browser" experimental feature flag enables all asset types that can + * technically be assets. */ + return true; + } + + return ED_asset_type_id_is_non_experimental(id); +} + +int64_t ED_asset_types_supported_as_filter_flags() +{ + if (U.experimental.use_extended_asset_browser) { + return FILTER_ID_ALL; + } + + return ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS; +} diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 9b43e23bd32..029c8b05923 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -5409,7 +5409,7 @@ static int ed_editcurve_addvert(Curve *cu, add_v3_v3(bezt->vec[1], ofs); add_v3_v3(bezt->vec[2], ofs); - if (((nu->flagu & CU_NURB_CYCLIC) == 0) && (i == 0 || i == nu->pntsu - 1)) { + if (((nu->flagu & CU_NURB_CYCLIC) == 0) && ELEM(i, 0, nu->pntsu - 1)) { BKE_nurb_handle_calc_simple_auto(nu, bezt); } } @@ -5561,12 +5561,14 @@ static int add_vertex_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (use_proj) { const float mval[2] = {UNPACK2(event->mval)}; - struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - vc.scene, 0, vc.region, vc.v3d); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create(vc.scene, + 0); ED_transform_snap_object_project_view3d( snap_context, vc.depsgraph, + vc.region, + vc.v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = (vc.obedit != NULL) ? SNAP_NOT_ACTIVE : SNAP_ALL, diff --git a/source/blender/editors/curve/editcurve_add.c b/source/blender/editors/curve/editcurve_add.c index 75fb17e8cc1..614805a70f5 100644 --- a/source/blender/editors/curve/editcurve_add.c +++ b/source/blender/editors/curve/editcurve_add.c @@ -356,7 +356,7 @@ Nurb *ED_curve_add_nurbs_primitive( bp->vec[0] += fac * grid; fac = (float)b - 1.5f; bp->vec[1] += fac * grid; - if ((a == 1 || a == 2) && (b == 1 || b == 2)) { + if ((ELEM(a, 1, 2)) && (ELEM(b, 1, 2))) { bp->vec[2] += grid; } mul_m4_v3(mat, bp->vec); diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c index d029bb539ba..6f18798bd2a 100644 --- a/source/blender/editors/curve/editfont.c +++ b/source/blender/editors/curve/editfont.c @@ -42,11 +42,11 @@ #include "BKE_context.h" #include "BKE_curve.h" -#include "BKE_font.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_object.h" #include "BKE_report.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -2155,8 +2155,8 @@ void FONT_OT_open(wmOperatorType *ot) FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, - FILE_DEFAULTDISPLAY, - FILE_SORT_DEFAULT); + FILE_IMGDISPLAY, + FILE_SORT_ALPHA); } /** \} */ diff --git a/source/blender/editors/curve/editfont_undo.c b/source/blender/editors/curve/editfont_undo.c index 6eaf8971eb0..21a6564edf4 100644 --- a/source/blender/editors/curve/editfont_undo.c +++ b/source/blender/editors/curve/editfont_undo.c @@ -33,9 +33,9 @@ #include "DNA_scene_types.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_main.h" #include "BKE_undo_system.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index 6fd06b47656..34d3f737f58 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -98,64 +98,12 @@ static bool gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[2], zero_v2(margin); return false; } + margin[0] = ((handle_size * scale_xy[0])); margin[1] = ((handle_size * scale_xy[1])); return true; } -/* -------------------------------------------------------------------- */ - -static void gizmo_rect_pivot_from_scale_part(int part, float r_pt[2], bool r_constrain_axis[2]) -{ - bool x = true, y = true; - switch (part) { - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: { - ARRAY_SET_ITEMS(r_pt, 0.5, 0.0); - x = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: { - ARRAY_SET_ITEMS(r_pt, -0.5, 0.0); - x = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: { - ARRAY_SET_ITEMS(r_pt, 0.0, 0.5); - y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: { - ARRAY_SET_ITEMS(r_pt, 0.0, -0.5); - y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: { - ARRAY_SET_ITEMS(r_pt, 0.5, 0.5); - x = y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y: { - ARRAY_SET_ITEMS(r_pt, 0.5, -0.5); - x = y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y: { - ARRAY_SET_ITEMS(r_pt, -0.5, 0.5); - x = y = false; - break; - } - case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y: { - ARRAY_SET_ITEMS(r_pt, -0.5, -0.5); - x = y = false; - break; - } - default: - BLI_assert(0); - } - r_constrain_axis[0] = x; - r_constrain_axis[1] = y; -} - /* -------------------------------------------------------------------- */ /** \name Box Draw Style * @@ -400,6 +348,7 @@ static void cage2d_draw_box_interaction(const float color[4], ARRAY_SET_ITEMS(verts[1], r_rotate.xmin, r_rotate.ymax); ARRAY_SET_ITEMS(verts[2], r_rotate.xmax, r_rotate.ymax); ARRAY_SET_ITEMS(verts[3], r_rotate.xmax, r_rotate.ymin); + verts_len = 4; if (is_solid) { prim_type = GPU_PRIM_TRI_FAN; @@ -769,10 +718,10 @@ static int gizmo_cage2d_get_cursor(wmGizmo *gz) return WM_CURSOR_NSEW_SCROLL; case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: - return WM_CURSOR_X_MOVE; + return WM_CURSOR_NSEW_SCROLL; case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: - return WM_CURSOR_Y_MOVE; + return WM_CURSOR_NSEW_SCROLL; /* TODO: diagonal cursor. */ case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: @@ -937,6 +886,57 @@ static int gizmo_cage2d_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) return OPERATOR_RUNNING_MODAL; } +static void gizmo_rect_pivot_from_scale_part(int part, float r_pt[2], bool r_constrain_axis[2]) +{ + bool x = true, y = true; + switch (part) { + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X: { + ARRAY_SET_ITEMS(r_pt, 0.5, 0.0); + x = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X: { + ARRAY_SET_ITEMS(r_pt, -0.5, 0.0); + x = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y: { + ARRAY_SET_ITEMS(r_pt, 0.0, 0.5); + y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y: { + ARRAY_SET_ITEMS(r_pt, 0.0, -0.5); + y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y: { + ARRAY_SET_ITEMS(r_pt, 0.5, 0.5); + x = y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y: { + ARRAY_SET_ITEMS(r_pt, 0.5, -0.5); + x = y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y: { + ARRAY_SET_ITEMS(r_pt, -0.5, 0.5); + x = y = false; + break; + } + case ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y: { + ARRAY_SET_ITEMS(r_pt, -0.5, -0.5); + x = y = false; + break; + } + default: + BLI_assert(0); + } + r_constrain_axis[0] = x; + r_constrain_axis[1] = y; +} + static int gizmo_cage2d_modal(bContext *C, wmGizmo *gz, const wmEvent *event, diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c index 07117c0153b..aed58e31798 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage3d_gizmo.c @@ -220,7 +220,7 @@ static void cage3d_draw_circle_wire(const float r[3], immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", line_width * U.pixelsize); - imm_draw_cube_wire_3d(pos, (float[3]){0}, r); + imm_draw_cube_wire_3d(pos, (const float[3]){0}, r); #if 0 if (transform_flag & ED_GIZMO_CAGE2D_XFORM_FLAG_TRANSLATE) { diff --git a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c index 6345cd3525a..d4d4c889209 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/dial3d_gizmo.c @@ -307,7 +307,7 @@ static void dial_ghostarc_get_angles(const wmGizmo *gz, { DialInteraction *inter = gz->interaction_data; const RegionView3D *rv3d = region->regiondata; - const float mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + const float mval[2] = {event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}; /* We might need to invert the direction of the angles. */ float view_vec[3], axis_vec[3]; diff --git a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c index 68322ed56af..ec4837aec3c 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/move3d_gizmo.c @@ -289,6 +289,8 @@ static int gizmo_move_modal(bContext *C, if (ED_transform_snap_object_project_view3d( inter->snap_context_v3d, CTX_data_ensure_evaluated_depsgraph(C), + region, + CTX_wm_view3d(C), (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE), &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, @@ -381,8 +383,7 @@ static int gizmo_move_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) if (area) { switch (area->spacetype) { case SPACE_VIEW3D: { - inter->snap_context_v3d = ED_transform_snap_object_context_create_view3d( - CTX_data_scene(C), 0, CTX_wm_region(C), CTX_wm_view3d(C)); + inter->snap_context_v3d = ED_transform_snap_object_context_create(CTX_data_scene(C), 0); break; } default: diff --git a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c index ae2cc05c01b..60642158820 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/snap3d_gizmo.c @@ -27,422 +27,119 @@ * \brief Snap gizmo which exposes the location, normal and index in the props. */ +#include "MEM_guardedalloc.h" + #include "BLI_listbase.h" #include "BLI_math.h" -#include "DNA_scene_types.h" - #include "BKE_context.h" #include "BKE_global.h" #include "BKE_main.h" -#include "GPU_immediate.h" -#include "GPU_state.h" - #include "ED_gizmo_library.h" #include "ED_screen.h" #include "ED_transform_snap_object_context.h" #include "ED_view3d.h" -#include "UI_resources.h" /* icons */ +#include "UI_resources.h" #include "RNA_access.h" #include "RNA_define.h" -#include "DEG_depsgraph_query.h" - #include "WM_api.h" -#include "WM_types.h" /* own includes */ -#include "../gizmo_geometry.h" #include "../gizmo_library_intern.h" typedef struct SnapGizmo3D { wmGizmo gizmo; - - /* We could have other snap contexts, for now only support 3D view. */ - SnapObjectContext *snap_context_v3d; - - /* Copy of the parameters of the last event state in order to detect updates. */ - struct { - int x; - int y; -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - short shift, ctrl, alt, oskey; -#endif - } last_eventstate; - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - wmKeyMap *keymap; - int snap_on; - bool invert_snap; -#endif - - /* Setup. */ - eSnapGizmo flag; - float *prevpoint; - float prevpoint_stack[3]; - short snap_elem_force; - - /* Return values. */ - short snap_elem; - float loc[3]; - float nor[3]; - int elem_index[3]; - - /** Enabled when snap is activated, even if it didn't find anything. */ - bool is_enabled; + V3DSnapCursorState *snap_state; } SnapGizmo3D; -/* Checks if the current event is different from the one captured in the last update. */ -static bool eventstate_has_changed(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) +static void snap_gizmo_snap_elements_update(SnapGizmo3D *snap_gizmo) { - if (wm && wm->winactive) { - const wmEvent *event = wm->winactive->eventstate; - if ((event->x != snap_gizmo->last_eventstate.x) || - (event->y != snap_gizmo->last_eventstate.y)) { - return true; - } -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { - if ((event->ctrl != snap_gizmo->last_eventstate.ctrl) || - (event->shift != snap_gizmo->last_eventstate.shift) || - (event->alt != snap_gizmo->last_eventstate.alt) || - (event->oskey != snap_gizmo->last_eventstate.oskey)) { - return true; - } - } -#endif + wmGizmoProperty *gz_prop_snap; + gz_prop_snap = WM_gizmo_target_property_find(&snap_gizmo->gizmo, "snap_elements"); + + if (gz_prop_snap->prop) { + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->snap_elem_force |= RNA_property_enum_get(&gz_prop_snap->ptr, gz_prop_snap->prop); } - return false; -} - -/* Copies the current eventstate. */ -static void eventstate_save_xy(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) -{ - if (wm && wm->winactive) { - const wmEvent *event = wm->winactive->eventstate; - snap_gizmo->last_eventstate.x = event->x; - snap_gizmo->last_eventstate.y = event->y; - } -} - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK -static bool invert_snap(SnapGizmo3D *snap_gizmo, const wmWindowManager *wm) -{ - if (!wm || !wm->winactive) { - return false; - } - - const wmEvent *event = wm->winactive->eventstate; - if ((event->ctrl == snap_gizmo->last_eventstate.ctrl) && - (event->shift == snap_gizmo->last_eventstate.shift) && - (event->alt == snap_gizmo->last_eventstate.alt) && - (event->oskey == snap_gizmo->last_eventstate.oskey)) { - /* Nothing has changed. */ - return snap_gizmo->invert_snap; - } - - /* Save new eventstate. */ - snap_gizmo->last_eventstate.ctrl = event->ctrl; - snap_gizmo->last_eventstate.shift = event->shift; - snap_gizmo->last_eventstate.alt = event->alt; - snap_gizmo->last_eventstate.oskey = event->oskey; - - const int snap_on = snap_gizmo->snap_on; - - wmKeyMap *keymap = WM_keymap_active(wm, snap_gizmo->keymap); - for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { - if (kmi->flag & KMI_INACTIVE) { - continue; - } - - if (kmi->propvalue == snap_on) { - if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || - (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || - (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || - ((kmi->type == EVT_OSKEY) && event->oskey)) { - return true; - } - } - } - return false; -} -#endif - -static short snap_gizmo_snap_elements(SnapGizmo3D *snap_gizmo) -{ - int snap_elements = snap_gizmo->snap_elem_force; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(&snap_gizmo->gizmo, "snap_elements"); - if (gz_prop->prop) { - snap_elements |= RNA_property_enum_get(&gz_prop->ptr, gz_prop->prop); - } - snap_elements &= (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR); - return (ushort)snap_elements; } /* -------------------------------------------------------------------- */ /** \name ED_gizmo_library specific API * \{ */ -void ED_gizmotypes_snap_3d_draw_util(RegionView3D *rv3d, - const float loc_prev[3], - const float loc_curr[3], - const float normal[3], - const uchar color_line[4], - const uchar color_point[4], - const short snap_elem_type) +SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene, wmGizmo *UNUSED(gz)) { - if (!loc_prev && !loc_curr) { - return; - } + return ED_view3d_cursor_snap_context_ensure(scene); +} - float view_inv[4][4]; - copy_m4_m4(view_inv, rv3d->viewinv); +void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *UNUSED(gz), int flag) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->flag |= flag; +} - /* The size of the circle is larger than the vertex size. - * This prevents a drawing overlaps the other. */ - float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); +void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *UNUSED(gz), int flag) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->flag &= ~flag; +} - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); +bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *UNUSED(gz), int flag) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return (snap_state->flag & flag) != 0; +} - if (loc_curr) { - immUniformColor4ubv(color_point); - imm_drawcircball(loc_curr, ED_view3d_pixel_size(rv3d, loc_curr) * radius, view_inv, pos); +bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *UNUSED(gz)) +{ + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + return snap_data->is_snap_invert; +} - /* draw normal if needed */ - if (normal) { - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, loc_curr); - immVertex3f(pos, loc_curr[0] + normal[0], loc_curr[1] + normal[1], loc_curr[2] + normal[2]); - immEnd(); +bool ED_gizmotypes_snap_3d_is_enabled(const wmGizmo *UNUSED(gz)) +{ + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + return snap_data->is_enabled; +} + +void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, + wmGizmo *UNUSED(gz), + float r_loc[3], + float r_nor[3], + int r_elem_index[3], + int *r_snap_elem) +{ + V3DSnapCursorData *snap_data = NULL; + if (C) { + /* Snap values are updated too late at the cursor. Be sure to update ahead of time. */ + wmWindowManager *wm = CTX_wm_manager(C); + const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; + if (event) { + ARegion *region = CTX_wm_region(C); + int x = event->xy[0] - region->winrct.xmin; + int y = event->xy[1] - region->winrct.ymin; + snap_data = ED_view3d_cursor_snap_data_get(NULL, C, x, y); } } - - if (loc_prev) { - /* Draw an "X" indicating where the previous snap point is. - * This is useful for indicating perpendicular snap. */ - - /* v1, v2, v3 and v4 indicate the coordinates of the ends of the "X". */ - float vx[3], vy[3], v1[3], v2[3], v3[3], v4[4]; - - /* Multiply by 0.75f so that the final size of the "X" is close to that of - * the circle. - * (A closer value is 0.7071f, but we don't need to be exact here). */ - float x_size = 0.75f * radius * ED_view3d_pixel_size(rv3d, loc_prev); - - mul_v3_v3fl(vx, view_inv[0], x_size); - mul_v3_v3fl(vy, view_inv[1], x_size); - - add_v3_v3v3(v1, vx, vy); - sub_v3_v3v3(v2, vx, vy); - negate_v3_v3(v3, v1); - negate_v3_v3(v4, v2); - - add_v3_v3(v1, loc_prev); - add_v3_v3(v2, loc_prev); - add_v3_v3(v3, loc_prev); - add_v3_v3(v4, loc_prev); - - immUniformColor4ubv(color_line); - immBegin(GPU_PRIM_LINES, 4); - immVertex3fv(pos, v3); - immVertex3fv(pos, v1); - immVertex3fv(pos, v4); - immVertex3fv(pos, v2); - immEnd(); - - if (loc_curr && (snap_elem_type & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - /* Dashed line. */ - immUnbindProgram(); - - immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); - immUniform1f("dash_width", 6.0f * U.pixelsize); - immUniform1f("dash_factor", 1.0f / 4.0f); - immUniformColor4ubv(color_line); - - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, loc_prev); - immVertex3fv(pos, loc_curr); - immEnd(); - } + if (!snap_data) { + snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); } - immUnbindProgram(); -} - -SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(Scene *scene, - const ARegion *region, - const View3D *v3d, - wmGizmo *gz) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - if (snap_gizmo->snap_context_v3d == NULL) { - snap_gizmo->snap_context_v3d = ED_transform_snap_object_context_create_view3d( - scene, 0, region, v3d); - } - return snap_gizmo->snap_context_v3d; -} - -void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->flag |= flag; -} - -void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->flag &= ~flag; -} - -bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - return (snap_gizmo->flag & flag) != 0; -} - -bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz) -{ -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - return snap_gizmo->invert_snap; -#else - return false; -#endif -} - -bool ED_gizmotypes_snap_3d_is_enabled(const wmGizmo *gz) -{ - const SnapGizmo3D *snap_gizmo = (const SnapGizmo3D *)gz; - return snap_gizmo->is_enabled; -} - -short ED_gizmotypes_snap_3d_update(wmGizmo *gz, - struct Depsgraph *depsgraph, - const ARegion *region, - const View3D *v3d, - const wmWindowManager *wm, - const float mval_fl[2]) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->is_enabled = false; - - Scene *scene = DEG_get_input_scene(depsgraph); - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK - if (!(snap_gizmo->flag & ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE)) { - snap_gizmo->invert_snap = invert_snap(snap_gizmo, wm); - - const ToolSettings *ts = scene->toolsettings; - if (snap_gizmo->invert_snap != !(ts->snap_flag & SCE_SNAP)) { - snap_gizmo->snap_elem = 0; - return 0; - } - } -#endif - eventstate_save_xy(snap_gizmo, wm); - - snap_gizmo->is_enabled = true; - - float co[3], no[3]; - short snap_elem = 0; - int snap_elem_index[3] = {-1, -1, -1}; - int index = -1; - - ushort snap_elements = snap_gizmo_snap_elements(snap_gizmo); - - if (snap_elements) { - float prev_co[3] = {0.0f}; - if (snap_gizmo->prevpoint) { - copy_v3_v3(prev_co, snap_gizmo->prevpoint); - } - else { - snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; - } - - eSnapSelect snap_select = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_ONLY_ACTIVE) ? - SNAP_ONLY_ACTIVE : - SNAP_ALL; - - eSnapEditType edit_mode_type = (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL) ? - SNAP_GEOM_FINAL : - (snap_gizmo->flag & ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE) ? - SNAP_GEOM_CAGE : - SNAP_GEOM_EDIT; - - bool use_occlusion_test = (snap_gizmo->flag & ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE) ? false : - true; - - float dist_px = 12.0f * U.pixelsize; - - ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, gz); - snap_elem = ED_transform_snap_object_project_view3d_ex( - snap_gizmo->snap_context_v3d, - depsgraph, - snap_elements, - &(const struct SnapObjectParams){ - .snap_select = snap_select, - .edit_mode_type = edit_mode_type, - .use_occlusion_test = use_occlusion_test, - }, - mval_fl, - prev_co, - &dist_px, - co, - no, - &index, - NULL, - NULL); - } - - if (snap_elem == 0) { - RegionView3D *rv3d = region->regiondata; - ED_view3d_win_to_3d(v3d, region, rv3d->ofs, mval_fl, co); - zero_v3(no); - } - else if (snap_elem == SCE_SNAP_MODE_VERTEX) { - snap_elem_index[0] = index; - } - else if (snap_elem & - (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - snap_elem_index[1] = index; - } - else if (snap_elem == SCE_SNAP_MODE_FACE) { - snap_elem_index[2] = index; - } - - snap_gizmo->snap_elem = snap_elem; - copy_v3_v3(snap_gizmo->loc, co); - copy_v3_v3(snap_gizmo->nor, no); - copy_v3_v3_int(snap_gizmo->elem_index, snap_elem_index); - - return snap_elem; -} - -void ED_gizmotypes_snap_3d_data_get( - wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem) -{ - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - BLI_assert(snap_gizmo->is_enabled); if (r_loc) { - copy_v3_v3(r_loc, snap_gizmo->loc); + copy_v3_v3(r_loc, snap_data->loc); } if (r_nor) { - copy_v3_v3(r_nor, snap_gizmo->nor); + copy_v3_v3(r_nor, snap_data->nor); } if (r_elem_index) { - copy_v3_v3_int(r_elem_index, snap_gizmo->elem_index); + copy_v3_v3_int(r_elem_index, snap_data->elem_index); } if (r_snap_elem) { - *r_snap_elem = snap_gizmo->snap_elem; + *r_snap_elem = snap_data->snap_elem; } } @@ -452,116 +149,69 @@ void ED_gizmotypes_snap_3d_data_get( /** \name RNA callbacks * \{ */ -/* Based on 'rna_GizmoProperties_find_operator'. */ -static struct SnapGizmo3D *gizmo_snap_rna_find_operator(PointerRNA *ptr) -{ - IDProperty *properties = ptr->data; - for (bScreen *screen = G_MAIN->screens.first; screen; screen = screen->id.next) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - if (area->spacetype != SPACE_VIEW3D) { - continue; - } - LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { - if (region->regiontype == RGN_TYPE_WINDOW && region->gizmo_map) { - wmGizmoMap *gzmap = region->gizmo_map; - LISTBASE_FOREACH (wmGizmoGroup *, gzgroup, WM_gizmomap_group_list(gzmap)) { - LISTBASE_FOREACH (wmGizmo *, gz, &gzgroup->gizmos) { - if (gz->properties == properties) { - return (SnapGizmo3D *)gz; - } - } - } - } - } - } - } - return NULL; -} - -static int gizmo_snap_rna_snap_elements_force_get_fn(struct PointerRNA *ptr, +static int gizmo_snap_rna_snap_elements_force_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop)) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - return snap_gizmo->snap_elem_force; - } - return 0; + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->snap_elem_force; } -static void gizmo_snap_rna_snap_elements_force_set_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_snap_elements_force_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), int value) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - snap_gizmo->snap_elem_force = (short)value; - } + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->snap_elem_force = (short)value; } -static void gizmo_snap_rna_prevpoint_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_prevpoint_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(values, snap_gizmo->prevpoint_stack); + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + if (snap_state->prevpoint) { + copy_v3_v3(values, snap_state->prevpoint); } } -static void gizmo_snap_rna_prevpoint_set_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_prevpoint_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), const float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - if (values) { - copy_v3_v3(snap_gizmo->prevpoint_stack, values); - snap_gizmo->prevpoint = snap_gizmo->prevpoint_stack; - } - else { - snap_gizmo->prevpoint = NULL; - } - } + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + ED_view3d_cursor_snap_prevpoint_set(snap_state, values); } -static void gizmo_snap_rna_location_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_location_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(values, snap_gizmo->loc); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(values, snap_data->loc); } -static void gizmo_snap_rna_location_set_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_location_set_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), const float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(snap_gizmo->loc, values); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(snap_data->loc, values); } -static void gizmo_snap_rna_normal_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_normal_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), float *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3(values, snap_gizmo->nor); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(values, snap_data->nor); } -static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *ptr, +static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *UNUSED(ptr), struct PropertyRNA *UNUSED(prop), int *values) { - SnapGizmo3D *snap_gizmo = gizmo_snap_rna_find_operator(ptr); - if (snap_gizmo) { - copy_v3_v3_int(values, snap_gizmo->elem_index); - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3_int(values, snap_data->elem_index); } /** \} */ @@ -573,87 +223,51 @@ static void gizmo_snap_rna_snap_elem_index_get_fn(struct PointerRNA *ptr, static void snap_gizmo_setup(wmGizmo *gz) { gz->flag |= WM_GIZMO_NO_TOOLTIP; - -#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - snap_gizmo->keymap = WM_modalkeymap_find(gz->parent_gzgroup->type->keyconf, - "Generic Gizmo Tweak Modal Map"); - RNA_enum_value_from_id(snap_gizmo->keymap->modal_items, "SNAP_ON", &snap_gizmo->snap_on); -#endif + snap_gizmo->snap_state = ED_view3d_cursor_snap_active(); + if (snap_gizmo->snap_state) { + snap_gizmo->snap_state->draw_point = true; + snap_gizmo->snap_state->draw_plane = false; + } + + rgba_float_to_uchar(snap_gizmo->snap_state->color_point, gz->color); } -static void snap_gizmo_draw(const bContext *C, wmGizmo *gz) +static void snap_gizmo_draw(const bContext *UNUSED(C), wmGizmo *UNUSED(gz)) { - SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - if (snap_gizmo->snap_elem == 0) { - return; - } - - wmWindowManager *wm = CTX_wm_manager(C); - if (eventstate_has_changed(snap_gizmo, wm)) { - /* The eventstate has changed but the snap has not been updated. - * This means that the current position is no longer valid. */ - snap_gizmo->snap_elem = 0; - return; - } - - RegionView3D *rv3d = CTX_wm_region_data(C); - if (rv3d->rflag & RV3D_NAVIGATING) { - /* Don't draw the gizmo while navigating. It can be distracting. */ - snap_gizmo->snap_elem = 0; - return; - } - - uchar color_line[4], color_point[4]; - UI_GetThemeColor3ubv(TH_TRANSFORM, color_line); - color_line[3] = 128; - - rgba_float_to_uchar(color_point, gz->color); - - GPU_line_smooth(false); - - GPU_line_width(1.0f); - - const float *prev_point = (snap_gizmo_snap_elements(snap_gizmo) & - SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? - snap_gizmo->prevpoint : - NULL; - - ED_gizmotypes_snap_3d_draw_util( - rv3d, prev_point, snap_gizmo->loc, NULL, color_line, color_point, snap_gizmo->snap_elem); + /* All drawing is handled at the paint cursor. */ } static int snap_gizmo_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - wmWindowManager *wm = CTX_wm_manager(C); ARegion *region = CTX_wm_region(C); - /* FIXME: this hack is to ignore drag events, otherwise drag events - * cause momentary snap gizmo re-positioning at the drag-start location, see: T87511. */ - if (wm && wm->winactive) { - const wmEvent *event = wm->winactive->eventstate; - int mval_compare[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; - if (!equals_v2v2_int(mval_compare, mval)) { - return snap_gizmo->snap_elem ? 0 : -1; + /* Make sure the cursor is only drawn in the gizmo region. */ + snap_gizmo->snap_state->region = region; + + /* Snap Elements can change while the gizmo is active. Need to be updated somewhere. */ + snap_gizmo_snap_elements_update(snap_gizmo); + + /* Snap values are updated too late at the cursor. Be sure to update ahead of time. */ + int x, y; + { + wmWindowManager *wm = CTX_wm_manager(C); + const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; + if (event) { + x = event->xy[0] - region->winrct.xmin; + y = event->xy[1] - region->winrct.ymin; + } + else { + x = mval[0]; + y = mval[1]; } } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(snap_gizmo->snap_state, C, x, y); - if (!eventstate_has_changed(snap_gizmo, wm)) { - /* Performance, do not update. */ - return snap_gizmo->snap_elem ? 0 : -1; - } - - View3D *v3d = CTX_wm_view3d(C); - const float mval_fl[2] = {UNPACK2(mval)}; - short snap_elem = ED_gizmotypes_snap_3d_update( - gz, CTX_data_ensure_evaluated_depsgraph(C), region, v3d, wm, mval_fl); - - if (snap_elem) { - ED_region_tag_redraw_editor_overlays(region); + if (snap_data->snap_elem) { return 0; } - return -1; } @@ -675,9 +289,8 @@ static int snap_gizmo_invoke(bContext *UNUSED(C), static void snap_gizmo_free(wmGizmo *gz) { SnapGizmo3D *snap_gizmo = (SnapGizmo3D *)gz; - if (snap_gizmo->snap_context_v3d) { - ED_transform_snap_object_context_destroy(snap_gizmo->snap_context_v3d); - snap_gizmo->snap_context_v3d = NULL; + if (snap_gizmo->snap_state) { + ED_view3d_cursor_snap_deactive(snap_gizmo->snap_state); } } @@ -717,7 +330,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE, "Snap Elements", ""); - RNA_def_property_enum_funcs_runtime(prop, gizmo_snap_rna_snap_elements_force_get_fn, gizmo_snap_rna_snap_elements_force_set_fn, @@ -733,7 +345,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Point that defines the location of the perpendicular snap", FLT_MIN, FLT_MAX); - RNA_def_property_float_array_funcs_runtime( prop, gizmo_snap_rna_prevpoint_get_fn, gizmo_snap_rna_prevpoint_set_fn, NULL); @@ -748,7 +359,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Snap Point Location", FLT_MIN, FLT_MAX); - RNA_def_property_float_array_funcs_runtime( prop, gizmo_snap_rna_location_get_fn, gizmo_snap_rna_location_set_fn, NULL); @@ -762,7 +372,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Snap Point Normal", FLT_MIN, FLT_MAX); - RNA_def_property_float_array_funcs_runtime(prop, gizmo_snap_rna_normal_get_fn, NULL, NULL); prop = RNA_def_int_vector(gzt->srna, @@ -775,7 +384,6 @@ static void GIZMO_GT_snap_3d(wmGizmoType *gzt) "Array index of face, edge and vert snapped", INT_MIN, INT_MAX); - RNA_def_property_int_array_funcs_runtime( prop, gizmo_snap_rna_snap_elem_index_get_fn, NULL, NULL); diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index 65c5b8ee573..bdb4e485373 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -123,6 +123,8 @@ typedef struct tGPsdata { ARegion *region; /** needed for GP_STROKE_2DSPACE. */ View2D *v2d; + /** For operations that require occlusion testing. */ + ViewDepths *depths; /** for using the camera rect within the 3d view. */ rctf *subrect; rctf subrect_data; @@ -972,12 +974,13 @@ static void annotation_stroke_newfrombuffer(tGPsdata *p) depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points"); + const ViewDepths *depths = p->depths; for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1086,7 +1089,10 @@ static bool annotation_stroke_eraser_is_occluded(tGPsdata *p, const int mval_i[2] = {x, y}; float mval_3d[3]; - if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { + float p_depth; + if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) { + ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d); + const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d); const float depth_pt = ED_view3d_calc_depth_for_comparison(rv3d, &pt->x); @@ -1211,7 +1217,8 @@ static void annotation_stroke_doeraser(tGPsdata *p) if (p->flags & GP_PAINTFLAG_V3D_ERASER_DEPTH) { View3D *v3d = p->area->spacedata.first; view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + ED_view3d_depth_override( + p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths); } } @@ -1311,7 +1318,7 @@ static bool annotation_session_initdata(bContext *C, tGPsdata *p) p->align_flag = &ts->gpencil_v2d_align; /* check that gpencil data is allowed to be drawn */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { + if (!((sseq->mainb == SEQ_DRAW_IMG_IMBUF) && (region->regiontype == RGN_TYPE_PREVIEW))) { p->status = GP_STATUS_ERROR; return 0; } @@ -1418,7 +1425,7 @@ static void annotation_visible_on_space(tGPsdata *p) } case SPACE_SEQ: { SpaceSeq *sseq = (SpaceSeq *)area->spacedata.first; - sseq->flag |= SEQ_SHOW_GPENCIL; + sseq->flag |= SEQ_PREVIEW_SHOW_GPENCIL; break; } case SPACE_IMAGE: { @@ -1499,6 +1506,9 @@ static void annotation_session_cleanup(tGPsdata *p) static void annotation_session_free(tGPsdata *p) { + if (p->depths) { + ED_view3d_depths_free(p->depths); + } MEM_freeN(p); } @@ -2104,7 +2114,7 @@ static void annotation_draw_apply_event( p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; } } - /* We are using the temporal stabilizer flag atm, + /* We are using the temporal stabilizer flag at the moment, * but shift is not pressed as well as the permanent flag is not used, * so we don't need the cursor anymore. */ else if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { @@ -2560,7 +2570,7 @@ static int annotation_draw_modal(bContext *C, wmOperator *op, const wmEvent *eve if ((p->region) && (p->region->regiontype == RGN_TYPE_TOOLS)) { /* Change to whatever region is now under the mouse */ ARegion *current_region = BKE_area_find_region_xy( - p->area, RGN_TYPE_ANY, event->x, event->y); + p->area, RGN_TYPE_ANY, event->xy[0], event->xy[1]); if (current_region) { /* Assume that since we found the cursor in here, it is in bounds diff --git a/source/blender/editors/gpencil/gpencil_bake_animation.c b/source/blender/editors/gpencil/gpencil_bake_animation.c index 2d299230124..4467e6202b4 100644 --- a/source/blender/editors/gpencil/gpencil_bake_animation.c +++ b/source/blender/editors/gpencil/gpencil_bake_animation.c @@ -102,7 +102,7 @@ static bool gpencil_bake_grease_pencil_animation_poll(bContext *C) } /* Check if grease pencil or empty for dupli groups. */ - if ((obact == NULL) || ((obact->type != OB_GPENCIL) && (obact->type != OB_EMPTY))) { + if ((obact == NULL) || (!ELEM(obact->type, OB_GPENCIL, OB_EMPTY))) { return false; } @@ -213,7 +213,6 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); - ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); ListBase ob_selected_list = {NULL, NULL}; @@ -256,7 +255,7 @@ static int gpencil_bake_grease_pencil_animation_exec(bContext *C, wmOperator *op gsc.ob = ob_gpencil; /* Init snap context for geometry projection. */ - sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + sctx = ED_transform_snap_object_context_create(scene, 0); } /* Loop all frame range. */ diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index 75ddfa47c57..f6012883e1f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -3556,7 +3556,7 @@ static int gpencil_stroke_join_exec(bContext *C, wmOperator *op) int tot_strokes = 0; /** Alloc memory. */ - tJoinStrokes *strokes_list = MEM_malloc_arrayN(sizeof(tJoinStrokes), max_join_strokes, __func__); + tJoinStrokes *strokes_list = MEM_malloc_arrayN(max_join_strokes, sizeof(tJoinStrokes), __func__); tJoinStrokes *elem = NULL; /* Read all selected strokes to create a list. */ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { @@ -3758,7 +3758,6 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) bGPdata *gpd = ED_gpencil_data_get_active(C); Scene *scene = CTX_data_scene(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - ARegion *region = CTX_wm_region(C); int oldframe = (int)DEG_get_ctime(depsgraph); const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type"); const bool keep_original = RNA_boolean_get(op->ptr, "keep_original"); @@ -3767,7 +3766,7 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) /* Init snap context for geometry projection. */ SnapObjectContext *sctx = NULL; - sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + sctx = ED_transform_snap_object_context_create(scene, 0); bool changed = false; /* Init space conversion stuff. */ @@ -4729,7 +4728,7 @@ static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op) /* Remove unused slots. */ int actcol = ob_dst->actcol; for (int slot = 1; slot <= ob_dst->totcol; slot++) { - while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst->data, slot)) { + while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst, slot)) { ob_dst->actcol = slot; BKE_object_material_slot_remove(bmain, ob_dst); if (actcol >= slot) { diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index f5474a7cdc3..9860c75f290 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -127,6 +127,8 @@ typedef struct tGPDfill { struct bGPDstroke *gps_mouse; /** Pointer to report messages. */ struct ReportList *reports; + /** For operations that require occlusion testing. */ + struct ViewDepths *depths; /** flags */ short flag; /** avoid too fast events */ @@ -1374,7 +1376,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) /* need to restore the original projection settings before packing up */ view3d_region_operator_needs_opengl(tgpf->win, tgpf->region); ED_view3d_depth_override( - tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); + tgpf->depsgraph, tgpf->region, tgpf->v3d, NULL, V3D_DEPTH_NO_GPENCIL, &tgpf->depths); /* Since strokes are so fine, when using their depth we need a margin * otherwise they might get missed. */ @@ -1385,6 +1387,7 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) int interp_depth = 0; int found_depth = 0; + const ViewDepths *depths = tgpf->depths; tgpf->depth_arr = MEM_mallocN(sizeof(float) * totpoints, "depth_points"); for (i = 0, ptc = tgpf->sbuffer; i < totpoints; i++, ptc++) { @@ -1392,11 +1395,9 @@ static void gpencil_get_depth_array(tGPDfill *tgpf) int mval_i[2]; round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(tgpf->region, mval_i, depth_margin, tgpf->depth_arr + i) == - 0) && - (i && - (ED_view3d_autodist_depth_seg( - tgpf->region, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, tgpf->depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, tgpf->depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1771,6 +1772,11 @@ static void gpencil_fill_exit(bContext *C, wmOperator *op) ED_region_draw_cb_exit(tgpf->region->type, tgpf->draw_handle_3d); } + /* Remove depth buffer in cache. */ + if (tgpf->depths) { + ED_view3d_depths_free(tgpf->depths); + } + /* finally, free memory used by temp data */ MEM_freeN(tgpf); } @@ -2103,11 +2109,11 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) /* first time the event is not enabled to show help lines. */ if ((tgpf->oldkey != -1) || (!help_lines)) { ARegion *region = BKE_area_find_region_xy( - CTX_wm_area(C), RGN_TYPE_ANY, event->x, event->y); + CTX_wm_area(C), RGN_TYPE_ANY, event->xy[0], event->xy[1]); if (region) { bool in_bounds = false; /* Perform bounds check */ - in_bounds = BLI_rcti_isect_pt(®ion->winrct, event->x, event->y); + in_bounds = BLI_rcti_isect_pt_v(®ion->winrct, event->xy); if ((in_bounds) && (region->regiontype == RGN_TYPE_WINDOW)) { tgpf->mouse[0] = event->mval[0]; diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h index b6730cb123b..3f3fd4fff39 100644 --- a/source/blender/editors/gpencil/gpencil_intern.h +++ b/source/blender/editors/gpencil/gpencil_intern.h @@ -155,6 +155,8 @@ typedef struct tGPDprimitive { struct Material *material; /** current brush */ struct Brush *brush; + /** For operations that require occlusion testing. */ + struct ViewDepths *depths; /** Settings to pass to gp_points_to_xy(). */ GP_SpaceConversion gsc; diff --git a/source/blender/editors/gpencil/gpencil_interpolate.c b/source/blender/editors/gpencil/gpencil_interpolate.c index fdd9f44605e..22127d7ac3b 100644 --- a/source/blender/editors/gpencil/gpencil_interpolate.c +++ b/source/blender/editors/gpencil/gpencil_interpolate.c @@ -316,6 +316,9 @@ static void gpencil_stroke_pair_table(bContext *C, if (ELEM(NULL, gps_from, gps_to)) { continue; } + if ((gps_from->totpoints == 0) || (gps_to->totpoints == 0)) { + continue; + } /* Insert the pair entry in the hash table and the list of strokes to keep order. */ BLI_addtail(&tgpil->selected_strokes, BLI_genericNodeN(gps_from)); BLI_ghash_insert(tgpil->pair_strokes, gps_from, gps_to); @@ -583,7 +586,7 @@ static void gpencil_interpolate_set_points(bContext *C, tGPDinterpolate *tgpi) static void gpencil_mouse_update_shift(tGPDinterpolate *tgpi, wmOperator *op, const wmEvent *event) { float mid = (float)(tgpi->region->winx - tgpi->region->winrct.xmin) / 2.0f; - float mpos = event->x - tgpi->region->winrct.xmin; + float mpos = event->xy[0] - tgpi->region->winrct.xmin; if (mpos >= mid) { tgpi->shift = ((mpos - mid) * tgpi->high_limit) / mid; @@ -1333,6 +1336,9 @@ static int gpencil_interpolate_seq_exec(bContext *C, wmOperator *op) if (ELEM(NULL, gps_from, gps_to)) { continue; } + if ((gps_from->totpoints == 0) || (gps_to->totpoints == 0)) { + continue; + } /* if destination stroke is smaller, resize new_stroke to size of gps_to stroke */ if (gps_from->totpoints > gps_to->totpoints) { diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index 0939d53736b..11e02b9832f 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -188,7 +188,6 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Scene *scene = CTX_data_scene(C); - ARegion *region = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); ListBase ob_selected_list = {NULL, NULL}; @@ -262,7 +261,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) gsc.ob = ob_gpencil; /* Init snap context for geometry projection. */ - sctx = ED_transform_snap_object_context_create_view3d(scene, 0, region, CTX_wm_view3d(C)); + sctx = ED_transform_snap_object_context_create(scene, 0); /* Tag all existing strokes to avoid reprojections. */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { @@ -345,7 +344,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) /* Remove unused materials. */ int actcol = ob_gpencil->actcol; for (int slot = 1; slot <= ob_gpencil->totcol; slot++) { - while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil->data, slot)) { + while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) { ob_gpencil->actcol = slot; BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 9b157224178..f37f2cd549f 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -109,7 +109,6 @@ typedef enum eGP_StrokeAdd_Result { /* Runtime flags */ typedef enum eGPencil_PaintFlags { GP_PAINTFLAG_FIRSTRUN = (1 << 0), /* operator just started */ - GP_PAINTFLAG_STROKEADDED = (1 << 1), GP_PAINTFLAG_SELECTMASK = (1 << 3), GP_PAINTFLAG_HARD_ERASER = (1 << 4), GP_PAINTFLAG_STROKE_ERASER = (1 << 5), @@ -162,6 +161,8 @@ typedef struct tGPsdata { ARegion *region; /** needed for GP_STROKE_2DSPACE. */ View2D *v2d; + /** For operations that require occlusion testing. */ + ViewDepths *depths; /** for using the camera rect within the 3d view. */ rctf *subrect; rctf subrect_data; @@ -282,15 +283,6 @@ static void gpencil_update_cache(bGPdata *gpd) } } -static void gpencil_stroke_added_enable(tGPsdata *p) -{ - BLI_assert(p->gpf->strokes.last != NULL); - p->flags |= GP_PAINTFLAG_STROKEADDED; - - /* drawing batch cache is dirty now */ - gpencil_update_cache(p->gpd); -} - /* ------ */ /* Forward defines for some functions... */ @@ -879,7 +871,7 @@ static short gpencil_stroke_addpoint(tGPsdata *p, pt->time = (float)(curtime - p->inittime); /* point uv (only 3d view) */ - if ((p->area->spacetype == SPACE_VIEW3D) && (gpd->runtime.sbuffer_used > 0)) { + if (gpd->runtime.sbuffer_used > 0) { tGPspoint *ptb = (tGPspoint *)gpd->runtime.sbuffer + gpd->runtime.sbuffer_used - 1; bGPDspoint spt, spt2; @@ -1001,8 +993,6 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) gps->points = MEM_callocN(sizeof(bGPDspoint) * gps->totpoints, "gp_stroke_points"); gps->dvert = NULL; - /* drawing batch cache is dirty now */ - gpencil_update_cache(p->gpd); /* set pointer to first non-initialized point */ pt = gps->points + (gps->totpoints - totelem); if (gps->dvert != NULL) { @@ -1102,14 +1092,16 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) int found_depth = 0; depth_arr = MEM_mallocN(sizeof(float) * gpd->runtime.sbuffer_used, "depth_points"); + + const ViewDepths *depths = p->depths; int i; for (i = 0, ptc = gpd->runtime.sbuffer; i < gpd->runtime.sbuffer_used; i++, ptc++, pt++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(p->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - p->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1326,7 +1318,7 @@ static void gpencil_stroke_newfrombuffer(tGPsdata *p) BKE_gpencil_stroke_copy_to_keyframes(gpd, gpl, p->gpf, gps, tail); } - gpencil_stroke_added_enable(p); + gpencil_update_cache(p->gpd); } /* --- 'Eraser' for 'Paint' Tool ------ */ @@ -1347,8 +1339,7 @@ static bool gpencil_stroke_eraser_is_occluded( gp_settings = eraser->gpencil_settings; } - if ((gp_settings != NULL) && (p->area->spacetype == SPACE_VIEW3D) && - (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { + if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { RegionView3D *rv3d = p->region->regiondata; const int mval_i[2] = {x, y}; @@ -1359,7 +1350,10 @@ static bool gpencil_stroke_eraser_is_occluded( /* calculate difference matrix if parent object */ BKE_gpencil_layer_transform_matrix_get(p->depsgraph, obact, gpl, diff_mat); - if (ED_view3d_autodist_simple(p->region, mval_i, mval_3d, 0, NULL)) { + float p_depth; + if (ED_view3d_depth_read_cached(p->depths, mval_i, 0, &p_depth)) { + ED_view3d_depth_unproject_v3(p->region, mval_i, (double)p_depth, mval_3d); + const float depth_mval = ED_view3d_calc_depth_for_comparison(rv3d, mval_3d); mul_v3_m4v3(fpt, diff_mat, &pt->x); @@ -1595,10 +1589,12 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p, */ if (gpencil_stroke_inside_circle(mval, radius, pc0[0], pc0[1], pc2[0], pc2[1])) { - bool is_occluded_pt0, is_occluded_pt1, is_occluded_pt2 = true; - is_occluded_pt0 = (pt0 && ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0)) ? - ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) : - gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]); + bool is_occluded_pt0 = true, is_occluded_pt1 = true, is_occluded_pt2 = true; + if (pt0) { + is_occluded_pt0 = ((pt0->flag & GP_SPOINT_TEMP_TAG) != 0) ? + ((pt0->flag & GP_SPOINT_TEMP_TAG2) != 0) : + gpencil_stroke_eraser_is_occluded(p, gpl, pt0, pc0[0], pc0[1]); + } if (is_occluded_pt0) { is_occluded_pt1 = ((pt1->flag & GP_SPOINT_TEMP_TAG) != 0) ? ((pt1->flag & GP_SPOINT_TEMP_TAG2) != 0) : @@ -1741,12 +1737,10 @@ static void gpencil_stroke_doeraser(tGPsdata *p) rect.xmax = p->mval[0] + calc_radius; rect.ymax = p->mval[1] + calc_radius; - if (p->area->spacetype == SPACE_VIEW3D) { - if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { - View3D *v3d = p->area->spacedata.first; - view3d_region_operator_needs_opengl(p->win, p->region); - ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, NULL); - } + if ((gp_settings != NULL) && (gp_settings->flag & GP_BRUSH_OCCLUDE_ERASER)) { + View3D *v3d = p->area->spacedata.first; + view3d_region_operator_needs_opengl(p->win, p->region); + ED_view3d_depth_override(p->depsgraph, p->region, v3d, NULL, V3D_DEPTH_NO_GPENCIL, &p->depths); } /* loop over all layers too, since while it's easy to restrict editing to @@ -1973,48 +1967,35 @@ static bool gpencil_session_initdata(bContext *C, wmOperator *op, tGPsdata *p) unit_m4(p->imat); unit_m4(p->mat); - switch (curarea->spacetype) { - /* supported views first */ - case SPACE_VIEW3D: { - /* set current area - * - must verify that region data is 3D-view (and not something else) - */ - /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ - p->area = curarea; - p->region = region; - p->align_flag = &ts->gpencil_v3d_align; + /* set current area + * - must verify that region data is 3D-view (and not something else) + */ + /* CAUTION: If this is the "toolbar", then this will change on the first stroke */ + p->area = curarea; + p->region = region; + p->align_flag = &ts->gpencil_v3d_align; - if (region->regiondata == NULL) { - p->status = GP_STATUS_ERROR; - return 0; - } - - if ((!obact) || (obact->type != OB_GPENCIL)) { - View3D *v3d = p->area->spacedata.first; - /* if active object doesn't exist or isn't a GP Object, create one */ - const float *cur = p->scene->cursor.location; - - ushort local_view_bits = 0; - if (v3d->localvd) { - local_view_bits = v3d->local_view_uuid; - } - /* create new default object */ - obact = ED_gpencil_add_object(C, cur, local_view_bits); - } - /* assign object after all checks to be sure we have one active */ - p->ob = obact; - p->ob_eval = (Object *)DEG_get_evaluated_object(p->depsgraph, p->ob); - - break; - } - - /* unsupported views */ - default: { - p->status = GP_STATUS_ERROR; - return 0; - } + if (region->regiondata == NULL) { + p->status = GP_STATUS_ERROR; + return 0; } + if ((!obact) || (obact->type != OB_GPENCIL)) { + View3D *v3d = p->area->spacedata.first; + /* if active object doesn't exist or isn't a GP Object, create one */ + const float *cur = p->scene->cursor.location; + + ushort local_view_bits = 0; + if (v3d->localvd) { + local_view_bits = v3d->local_view_uuid; + } + /* create new default object */ + obact = ED_gpencil_add_object(C, cur, local_view_bits); + } + /* assign object after all checks to be sure we have one active */ + p->ob = obact; + p->ob_eval = (Object *)DEG_get_evaluated_object(p->depsgraph, p->ob); + /* get gp-data */ gpd_ptr = ED_gpencil_data_get_pointers(C, &p->ownerPtr); if ((gpd_ptr == NULL) || ED_gpencil_data_owner_is_annotation(&p->ownerPtr)) { @@ -2113,6 +2094,9 @@ static void gpencil_session_free(tGPsdata *p) if (p->rng != NULL) { BLI_rng_free(p->rng); } + if (p->depths != NULL) { + ED_view3d_depths_free(p->depths); + } MEM_freeN(p); } @@ -2250,17 +2234,15 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* when drawing in the camera view, in 2D space, set the subrect */ p->subrect = NULL; if ((*p->align_flag & GP_PROJECT_VIEWSPACE) == 0) { - if (p->area->spacetype == SPACE_VIEW3D) { - View3D *v3d = p->area->spacedata.first; - RegionView3D *rv3d = p->region->regiondata; + View3D *v3d = p->area->spacedata.first; + RegionView3D *rv3d = p->region->regiondata; - /* for camera view set the subrect */ - if (rv3d->persp == RV3D_CAMOB) { - /* no shift */ - ED_view3d_calc_camera_border( - p->scene, depsgraph, p->region, v3d, rv3d, &p->subrect_data, true); - p->subrect = &p->subrect_data; - } + /* for camera view set the subrect */ + if (rv3d->persp == RV3D_CAMOB) { + /* no shift */ + ED_view3d_calc_camera_border( + p->scene, depsgraph, p->region, v3d, rv3d, &p->subrect_data, true); + p->subrect = &p->subrect_data; } } @@ -2279,12 +2261,7 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* check if points will need to be made in view-aligned space */ if (*p->align_flag & GP_PROJECT_VIEWSPACE) { - switch (p->area->spacetype) { - case SPACE_VIEW3D: { - p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; - break; - } - } + p->gpd->runtime.sbuffer_sflag |= GP_STROKE_3DSPACE; } if (!changed) { /* Copy the brush to avoid a full tag (very slow). */ @@ -2437,15 +2414,9 @@ static void gpencil_draw_exit(bContext *C, wmOperator *op) p->eraser->size = p->radius; } - /* restore cursor to indicate end of drawing */ - if (p->area->spacetype != SPACE_VIEW3D) { - WM_cursor_modal_restore(CTX_wm_window(C)); - } - else { - /* drawing batch cache is dirty now */ - bGPdata *gpd = CTX_data_gpencil_data(C); - gpencil_update_cache(gpd); - } + /* drawing batch cache is dirty now */ + bGPdata *gpd = CTX_data_gpencil_data(C); + gpencil_update_cache(gpd); /* clear undo stack */ gpencil_undo_finish(); @@ -3707,7 +3678,7 @@ static int gpencil_draw_modal(bContext *C, wmOperator *op, const wmEvent *event) if ((p->region) && (p->region->regiontype == RGN_TYPE_TOOLS)) { /* Change to whatever region is now under the mouse */ ARegion *current_region = BKE_area_find_region_xy( - p->area, RGN_TYPE_ANY, event->x, event->y); + p->area, RGN_TYPE_ANY, event->xy[0], event->xy[1]); if (current_region) { /* Assume that since we found the cursor in here, it is in bounds diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index f8cfc130e35..7382aca9a87 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -795,15 +795,16 @@ static void gpencil_primitive_update_strokes(bContext *C, tGPDprimitive *tgpi) (ts->gpencil_v3d_align & GP_PROJECT_DEPTH_STROKE) ? V3D_DEPTH_GPENCIL_ONLY : V3D_DEPTH_NO_GPENCIL, - NULL); + &tgpi->depths); depth_arr = MEM_mallocN(sizeof(float) * gps->totpoints, "depth_points"); + const ViewDepths *depths = tgpi->depths; tGPspoint *ptc = &points2D[0]; for (int i = 0; i < gps->totpoints; i++, ptc++) { round_v2i_v2fl(mval_i, &ptc->x); - if ((ED_view3d_autodist_depth(tgpi->region, mval_i, depth_margin, depth_arr + i) == 0) && - (i && (ED_view3d_autodist_depth_seg( - tgpi->region, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { + if ((ED_view3d_depth_read_cached(depths, mval_i, depth_margin, depth_arr + i) == 0) && + (i && (ED_view3d_depth_read_cached_seg( + depths, mval_i, mval_prev, depth_margin + 1, depth_arr + i) == 0))) { interp_depth = true; } else { @@ -1154,6 +1155,11 @@ static void gpencil_primitive_exit(bContext *C, wmOperator *op) BLI_rng_free(tgpi->rng); } + /* Remove depth buffer in cache. */ + if (tgpi->depths) { + ED_view3d_depths_free(tgpi->depths); + } + MEM_freeN(tgpi); } diff --git a/source/blender/editors/gpencil/gpencil_utils.c b/source/blender/editors/gpencil/gpencil_utils.c index bb05b93ad81..c4319fd7ee2 100644 --- a/source/blender/editors/gpencil/gpencil_utils.c +++ b/source/blender/editors/gpencil/gpencil_utils.c @@ -979,7 +979,7 @@ bool gpencil_point_xy_to_3d(const GP_SpaceConversion *gsc, * to 3D coordinates. * * \param point2D: The screen-space 2D point data to convert. - * \param depth: Depth array (via #ED_view3d_autodist_depth()). + * \param depth: Depth array (via #ED_view3d_depth_read_cached()). * \param r_out: The resulting 2D point data. */ void gpencil_stroke_convertcoords_tpoint(Scene *scene, @@ -1292,6 +1292,7 @@ void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, depsgraph, region, v3d, xy, &ray_start[0], &ray_normal[0], true); if (ED_transform_snap_object_project_ray(sctx, depsgraph, + v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, }, diff --git a/source/blender/editors/gpencil/gpencil_weight_paint.c b/source/blender/editors/gpencil/gpencil_weight_paint.c index d14322e12b5..b3249294119 100644 --- a/source/blender/editors/gpencil/gpencil_weight_paint.c +++ b/source/blender/editors/gpencil/gpencil_weight_paint.c @@ -29,15 +29,18 @@ #include "BLT_translation.h" +#include "DNA_armature_types.h" #include "DNA_brush_types.h" #include "DNA_gpencil_types.h" +#include "BKE_action.h" #include "BKE_brush.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_deform.h" #include "BKE_gpencil.h" #include "BKE_main.h" +#include "BKE_modifier.h" #include "BKE_object_deform.h" #include "BKE_report.h" #include "DNA_meshdata_types.h" @@ -246,7 +249,22 @@ static bool brush_draw_apply(tGP_BrushWeightpaintData *gso, /* need a vertex group */ if (gso->vrgroup == -1) { if (gso->object) { - BKE_object_defgroup_add(gso->object); + Object *ob_armature = BKE_modifiers_is_deformed_by_armature(gso->object); + if ((ob_armature != NULL)) { + Bone *actbone = ((bArmature *)ob_armature->data)->act_bone; + if (actbone != NULL) { + bPoseChannel *pchan = BKE_pose_channel_find_name(ob_armature->pose, actbone->name); + if (pchan != NULL) { + bDeformGroup *dg = BKE_object_defgroup_find_name(gso->object, pchan->name); + if (dg == NULL) { + dg = BKE_object_defgroup_add_name(gso->object, pchan->name); + } + } + } + } + else { + BKE_object_defgroup_add(gso->object); + } DEG_relations_tag_update(gso->bmain); gso->vrgroup = 0; } diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 6a0a42ee77b..e9601220f2e 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -861,7 +861,7 @@ void ED_operatormacros_action(void); /* XXX: Should we be doing these here, or at all? */ /* Action Editor - Action Management */ -struct AnimData *ED_actedit_animdata_from_context(struct bContext *C); +struct AnimData *ED_actedit_animdata_from_context(struct bContext *C, struct ID **r_adt_id_owner); void ED_animedit_unlink_action(struct bContext *C, struct ID *id, struct AnimData *adt, diff --git a/source/blender/editors/include/ED_asset.h b/source/blender/editors/include/ED_asset.h index 8f19c97e671..3b1d87e8ff7 100644 --- a/source/blender/editors/include/ED_asset.h +++ b/source/blender/editors/include/ED_asset.h @@ -36,14 +36,17 @@ void ED_operatortypes_asset(void); } #endif +#include "../asset/ED_asset_catalog.h" #include "../asset/ED_asset_filter.h" #include "../asset/ED_asset_handle.h" #include "../asset/ED_asset_library.h" #include "../asset/ED_asset_list.h" #include "../asset/ED_asset_mark_clear.h" #include "../asset/ED_asset_temp_id_consumer.h" +#include "../asset/ED_asset_type.h" /* C++ only headers. */ #ifdef __cplusplus +# include "../asset/ED_asset_catalog.hh" # include "../asset/ED_asset_list.hh" #endif diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 82057c726a5..68b6e44371c 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -23,6 +23,8 @@ #pragma once +#include "DNA_uuid_types.h" + #ifdef __cplusplus extern "C" { #endif @@ -142,14 +144,20 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile); bool ED_fileselect_is_file_browser(const struct SpaceFile *sfile); bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile); +struct AssetLibrary *ED_fileselect_active_asset_library_get(const struct SpaceFile *sfile); struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile); -/* Activate the file that corresponds to the given ID. +void ED_fileselect_activate_asset_catalog(const struct SpaceFile *sfile, bUUID catalog_id); + +/* Activate and select the file that corresponds to the given ID. * Pass deferred=true to wait for the next refresh before activating. */ void ED_fileselect_activate_by_id(struct SpaceFile *sfile, struct ID *asset_id, const bool deferred); +void ED_fileselect_deselect_all(struct SpaceFile *sfile); +void ED_fileselect_activate_by_relpath(struct SpaceFile *sfile, const char *relative_path); + void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], bool *is_maximized); diff --git a/source/blender/editors/include/ED_gizmo_library.h b/source/blender/editors/include/ED_gizmo_library.h index 357d5e10fa7..4d922162ee9 100644 --- a/source/blender/editors/include/ED_gizmo_library.h +++ b/source/blender/editors/include/ED_gizmo_library.h @@ -41,11 +41,7 @@ void ED_gizmotypes_primitive_3d(void); void ED_gizmotypes_blank_3d(void); void ED_gizmotypes_snap_3d(void); -struct ARegion; -struct Depsgraph; struct Object; -struct SnapObjectContext; -struct View3D; struct bContext; struct wmGizmo; struct wmWindowManager; @@ -248,43 +244,22 @@ void ED_gizmotypes_dial_3d_draw_util(const float matrix_basis[4][4], struct Dial3dParams *params); /* snap3d_gizmo.c */ -#define USE_SNAP_DETECT_FROM_KEYMAP_HACK -void ED_gizmotypes_snap_3d_draw_util(struct RegionView3D *rv3d, - const float loc_prev[3], - const float loc_curr[3], - const float normal[3], - const uchar color_line[4], - const uchar color_point[4], - const short snap_elem_type); struct SnapObjectContext *ED_gizmotypes_snap_3d_context_ensure(struct Scene *scene, - const struct ARegion *region, - const struct View3D *v3d, struct wmGizmo *gz); -typedef enum { - ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE = 1 << 0, - ED_SNAPGIZMO_OCCLUSION_ALWAYS_TRUE = 1 << 1, - ED_SNAPGIZMO_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */ - ED_SNAPGIZMO_SNAP_ONLY_ACTIVE = 1 << 3, - ED_SNAPGIZMO_SNAP_EDIT_GEOM_FINAL = 1 << 4, - ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE = 1 << 5, -} eSnapGizmo; - -void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, eSnapGizmo flag); -void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, eSnapGizmo flag); -bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, eSnapGizmo flag); +void ED_gizmotypes_snap_3d_flag_set(struct wmGizmo *gz, int flag); +void ED_gizmotypes_snap_3d_flag_clear(struct wmGizmo *gz, int flag); +bool ED_gizmotypes_snap_3d_flag_test(struct wmGizmo *gz, int flag); bool ED_gizmotypes_snap_3d_invert_snap_get(struct wmGizmo *gz); bool ED_gizmotypes_snap_3d_is_enabled(const struct wmGizmo *gz); -short ED_gizmotypes_snap_3d_update(struct wmGizmo *gz, - struct Depsgraph *depsgraph, - const struct ARegion *region, - const struct View3D *v3d, - const struct wmWindowManager *wm, - const float mval_fl[2]); -void ED_gizmotypes_snap_3d_data_get( - struct wmGizmo *gz, float r_loc[3], float r_nor[3], int r_elem_index[3], int *r_snap_elem); +void ED_gizmotypes_snap_3d_data_get(const struct bContext *C, + struct wmGizmo *gz, + float r_loc[3], + float r_nor[3], + int r_elem_index[3], + int *r_snap_elem); #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_image.h b/source/blender/editors/include/ED_image.h index 6b0b9f4a27c..1400bcf5ee3 100644 --- a/source/blender/editors/include/ED_image.h +++ b/source/blender/editors/include/ED_image.h @@ -41,15 +41,25 @@ struct SpaceImage; struct bContext; struct wmOperator; struct wmWindowManager; +struct View2D; + +/* image_draw.c */ +float ED_space_image_zoom_level(const struct View2D *v2d, const int grid_dimension); +void ED_space_image_grid_steps(struct SpaceImage *sima, + float grid_steps[SI_GRID_STEPS_LEN], + const int grid_dimension); +float ED_space_image_increment_snap_value(const int grid_dimesnions, + const float grid_steps[SI_GRID_STEPS_LEN], + const float zoom_factor); /* image_edit.c, exported for transform */ -struct Image *ED_space_image(struct SpaceImage *sima); +struct Image *ED_space_image(const struct SpaceImage *sima); void ED_space_image_set(struct Main *bmain, struct SpaceImage *sima, struct Image *ima, bool automatic); void ED_space_image_auto_set(const struct bContext *C, struct SpaceImage *sima); -struct Mask *ED_space_image_get_mask(struct SpaceImage *sima); +struct Mask *ED_space_image_get_mask(const struct SpaceImage *sima); void ED_space_image_set_mask(struct bContext *C, struct SpaceImage *sima, struct Mask *mask); bool ED_space_image_get_position(struct SpaceImage *sima, @@ -102,9 +112,9 @@ void ED_image_point_pos__reverse(struct SpaceImage *sima, float r_co[2]); bool ED_image_slot_cycle(struct Image *image, int direction); -bool ED_space_image_show_render(struct SpaceImage *sima); -bool ED_space_image_show_paint(struct SpaceImage *sima); -bool ED_space_image_show_uvedit(struct SpaceImage *sima, struct Object *obedit); +bool ED_space_image_show_render(const struct SpaceImage *sima); +bool ED_space_image_show_paint(const struct SpaceImage *sima); +bool ED_space_image_show_uvedit(const struct SpaceImage *sima, struct Object *obedit); bool ED_space_image_paint_curve(const struct bContext *C); @@ -126,7 +136,10 @@ void ED_image_draw_info(struct Scene *scene, const int *zp, const float *zpf); -bool ED_space_image_show_cache(struct SpaceImage *sima); +bool ED_space_image_show_cache(const struct SpaceImage *sima); +bool ED_space_image_show_cache_and_mval_over(const struct SpaceImage *sima, + struct ARegion *region, + const int mval[2]); bool ED_image_should_save_modified(const struct Main *bmain); int ED_image_save_all_modified_info(const struct Main *bmain, struct ReportList *reports); diff --git a/source/blender/editors/include/ED_keyframes_draw.h b/source/blender/editors/include/ED_keyframes_draw.h index 61e37f20b1b..6a7037c3eed 100644 --- a/source/blender/editors/include/ED_keyframes_draw.h +++ b/source/blender/editors/include/ED_keyframes_draw.h @@ -41,7 +41,7 @@ struct bDopeSheet; struct bGPDlayer; /* draw simple diamond-shape keyframe */ -/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_DIAMOND, +/* caller should set up vertex format, bind GPU_SHADER_KEYFRAME_SHAPE, * immBegin(GPU_PRIM_POINTS, n), then call this n times */ typedef struct KeyframeShaderBindings { uint pos_id; diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h index 1d51a3e77cf..e68617f7867 100644 --- a/source/blender/editors/include/ED_node.h +++ b/source/blender/editors/include/ED_node.h @@ -49,7 +49,7 @@ typedef enum { NODE_RIGHT = 8, } NodeBorder; -#define NODE_GRID_STEPS 5 +#define NODE_GRID_STEP_SIZE 10 #define NODE_EDGE_PAN_INSIDE_PAD 2 #define NODE_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for node panning, use whole screen. */ #define NODE_EDGE_PAN_SPEED_RAMP 1 @@ -64,7 +64,6 @@ void ED_node_cursor_location_set(struct SpaceNode *snode, const float value[2]); int ED_node_tree_path_length(struct SpaceNode *snode); void ED_node_tree_path_get(struct SpaceNode *snode, char *value); -void ED_node_tree_path_get_fixedbuf(struct SpaceNode *snode, char *value, int max_length); void ED_node_tree_start(struct SpaceNode *snode, struct bNodeTree *ntree, diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 5397cd95ace..2a557f1abd3 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -122,6 +122,8 @@ void ED_object_xform_skip_child_container_item_ensure(struct XFormObjectSkipChil struct Object *ob_parent_recurse, int mode); +void ED_object_xform_array_m4(struct Object **objects, uint objects_len, const float matrix[4][4]); + /* object_ops.c */ void ED_operatortypes_object(void); void ED_operatormacros_object(void); @@ -202,7 +204,7 @@ void ED_object_parent(struct Object *ob, const char *substr); char *ED_object_ot_drop_named_material_tooltip(struct bContext *C, struct PointerRNA *properties, - const struct wmEvent *event); + const int mval[2]); /* bitflags for enter/exit editmode */ enum { @@ -339,7 +341,16 @@ typedef enum eObjectPathCalcRange { void ED_objects_recalculate_paths(struct bContext *C, struct Scene *scene, - eObjectPathCalcRange range); + eObjectPathCalcRange range, + struct ListBase *ld_objects); + +void ED_objects_recalculate_paths_selected(struct bContext *C, + struct Scene *scene, + eObjectPathCalcRange range); + +void ED_objects_recalculate_paths_visible(struct bContext *C, + struct Scene *scene, + eObjectPathCalcRange range); /* constraints */ struct ListBase *ED_object_constraint_active_list(struct Object *ob); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 60ef3e740c6..08b6c02a8d0 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -68,7 +68,7 @@ void ED_region_do_layout(struct bContext *C, struct ARegion *region); void ED_region_do_draw(struct bContext *C, struct ARegion *region); void ED_region_exit(struct bContext *C, struct ARegion *region); void ED_region_remove(struct bContext *C, struct ScrArea *area, struct ARegion *region); -void ED_region_pixelspace(struct ARegion *region); +void ED_region_pixelspace(const struct ARegion *region); void ED_region_update_rect(struct ARegion *region); void ED_region_floating_init(struct ARegion *region); void ED_region_tag_redraw(struct ARegion *region); @@ -178,6 +178,10 @@ void ED_area_update_region_sizes(struct wmWindowManager *wm, struct wmWindow *win, struct ScrArea *area); bool ED_area_has_shared_border(struct ScrArea *a, struct ScrArea *b); +ScrArea *ED_area_offscreen_create(struct wmWindow *win, eSpace_Type space_type); +void ED_area_offscreen_free(struct wmWindowManager *wm, + struct wmWindow *win, + struct ScrArea *area); ScrArea *ED_screen_areas_iter_first(const struct wmWindow *win, const bScreen *screen); ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); @@ -206,7 +210,10 @@ void ED_screen_ensure_updated(struct wmWindowManager *wm, struct bScreen *screen); void ED_screen_do_listen(struct bContext *C, struct wmNotifier *note); bool ED_screen_change(struct bContext *C, struct bScreen *screen); -void ED_screen_scene_change(struct bContext *C, struct wmWindow *win, struct Scene *scene); +void ED_screen_scene_change(struct bContext *C, + struct wmWindow *win, + struct Scene *scene, + const bool refresh_toolsystem); void ED_screen_set_active_region(struct bContext *C, struct wmWindow *win, const int xy[2]); void ED_screen_exit(struct bContext *C, struct wmWindow *window, struct bScreen *screen); void ED_screen_animation_timer(struct bContext *C, int redraws, int sync, int enable); @@ -310,6 +317,7 @@ bool ED_operator_regionactive(struct bContext *C); bool ED_operator_scene(struct bContext *C); bool ED_operator_scene_editable(struct bContext *C); bool ED_operator_objectmode(struct bContext *C); +bool ED_operator_objectmode_poll_msg(struct bContext *C); bool ED_operator_view3d_active(struct bContext *C); bool ED_operator_region_view3d_active(struct bContext *C); diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index 1a3aa7e5496..958df8f7707 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -72,8 +72,9 @@ void *ED_region_draw_cb_activate(struct ARegionType *art, void (*draw)(const struct bContext *, struct ARegion *, void *), void *customdata, int type); -void ED_region_draw_cb_draw(const struct bContext *, struct ARegion *, int); -void ED_region_draw_cb_exit(struct ARegionType *, void *); +void ED_region_draw_cb_draw(const struct bContext *C, struct ARegion *region, int type); +void ED_region_surface_draw_cb_draw(struct ARegionType *art, int type); +void ED_region_draw_cb_exit(struct ARegionType *art, void *handle); void ED_region_draw_cb_remove_by_type(struct ARegionType *art, void *draw_fn, void (*free)(void *)); diff --git a/source/blender/editors/include/ED_text.h b/source/blender/editors/include/ED_text.h index 2284c82b3d5..6e012ec1a91 100644 --- a/source/blender/editors/include/ED_text.h +++ b/source/blender/editors/include/ED_text.h @@ -34,6 +34,8 @@ struct UndoStep; struct UndoType; struct bContext; +bool ED_text_activate_in_screen(struct bContext *C, struct Text *text); + void ED_text_scroll_to_cursor(struct SpaceText *st, struct ARegion *region, bool center); bool ED_text_region_location_from_cursor(struct SpaceText *st, diff --git a/source/blender/editors/include/ED_time_scrub_ui.h b/source/blender/editors/include/ED_time_scrub_ui.h index 6420aaf5ef0..812cb31c9b0 100644 --- a/source/blender/editors/include/ED_time_scrub_ui.h +++ b/source/blender/editors/include/ED_time_scrub_ui.h @@ -33,8 +33,7 @@ struct wmEvent; void ED_time_scrub_draw_current_frame(const struct ARegion *region, const struct Scene *scene, - bool display_seconds, - bool draw_line); + bool display_seconds); void ED_time_scrub_draw(const struct ARegion *region, const struct Scene *scene, diff --git a/source/blender/editors/include/ED_transform.h b/source/blender/editors/include/ED_transform.h index bedd0e2fa35..b132e559baa 100644 --- a/source/blender/editors/include/ED_transform.h +++ b/source/blender/editors/include/ED_transform.h @@ -181,6 +181,13 @@ struct TransformBounds { /* Normalized axis */ float axis[3][3]; float axis_min[3], axis_max[3]; + + /** + * When #TransformCalcParams.use_local_axis is used. + * This is the local space matrix the caller may need to access. + */ + bool use_matrix_space; + float matrix_space[4][4]; }; struct TransformCalcParams { diff --git a/source/blender/editors/include/ED_transform_snap_object_context.h b/source/blender/editors/include/ED_transform_snap_object_context.h index 42e73bbf744..c4da1588117 100644 --- a/source/blender/editors/include/ED_transform_snap_object_context.h +++ b/source/blender/editors/include/ED_transform_snap_object_context.h @@ -44,6 +44,7 @@ typedef enum { SNAP_NOT_SELECTED = 1, SNAP_NOT_ACTIVE = 2, SNAP_ONLY_ACTIVE = 3, + SNAP_SELECTABLE = 4, } eSnapSelect; typedef enum { @@ -83,11 +84,6 @@ struct SnapObjectParams { typedef struct SnapObjectContext SnapObjectContext; SnapObjectContext *ED_transform_snap_object_context_create(struct Scene *scene, int flag); -SnapObjectContext *ED_transform_snap_object_context_create_view3d(struct Scene *scene, - int flag, - /* extra args for view3d */ - const struct ARegion *region, - const struct View3D *v3d); void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx); /* callbacks to filter how snap works */ @@ -100,6 +96,7 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -112,6 +109,7 @@ bool ED_transform_snap_object_project_ray_ex(struct SnapObjectContext *sctx, float r_obmat[4][4]); bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_origin[3], const float ray_direction[3], @@ -121,6 +119,7 @@ bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -130,6 +129,8 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const unsigned short snap_to, const struct SnapObjectParams *params, const float mval[2], @@ -139,20 +140,25 @@ short ED_transform_snap_object_project_view3d_ex(struct SnapObjectContext *sctx, float r_no[3], int *r_index, struct Object **r_ob, - float r_obmat[4][4]); -bool ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, - struct Depsgraph *depsgraph, - const unsigned short snap_to, - const struct SnapObjectParams *params, - const float mval[2], - const float prev_co[3], - float *dist_px, - /* return args */ - float r_loc[3], - float r_no[3]); + float r_obmat[4][4], + float r_face_nor[3]); +short ED_transform_snap_object_project_view3d(struct SnapObjectContext *sctx, + struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + const unsigned short snap_to, + const struct SnapObjectParams *params, + const float mval[2], + const float prev_co[3], + float *dist_px, + /* return args */ + float r_loc[3], + float r_no[3]); bool ED_transform_snap_object_project_all_view3d_ex(SnapObjectContext *sctx, struct Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const struct SnapObjectParams *params, const float mval[2], float ray_depth, diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 0105af843bb..df132e6ae80 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -30,6 +30,7 @@ extern "C" { #endif +struct GPUBatch; struct Main; struct bContext; diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index ea3d921f2c5..f3aba12a924 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -237,6 +237,18 @@ void ED_image_draw_cursor(struct ARegion *region, const float cursor[2]); void ED_uvedit_buttons_register(struct ARegionType *art); /* uvedit_islands.c */ + +struct UVMapUDIM_Params { + const struct Image *image; + /** Copied from #SpaceImage.tile_grid_shape */ + int grid_shape[2]; + bool use_target_udim; + int target_udim; +}; +bool ED_uvedit_udim_params_from_image_space(const struct SpaceImage *sima, + bool use_active, + struct UVMapUDIM_Params *udim_params); + struct UVPackIsland_Params { uint rotate : 1; /** -1 not to align to axis, otherwise 0,1 for X,Y. */ @@ -246,9 +258,14 @@ struct UVPackIsland_Params { uint use_seams : 1; uint correct_aspect : 1; }; + +bool uv_coords_isect_udim(const struct Image *image, + const int udim_grid[2], + const float coords[2]); void ED_uvedit_pack_islands_multi(const struct Scene *scene, Object **objects, const uint objects_len, + const struct UVMapUDIM_Params *udim_params, const struct UVPackIsland_Params *params); #ifdef __cplusplus diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index cf8dcbd7995..f9ccd916327 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -52,6 +52,7 @@ struct RegionView3D; struct RenderEngineType; struct Scene; struct ScrArea; +struct SnapObjectContext; struct View3D; struct ViewContext; struct ViewLayer; @@ -228,6 +229,83 @@ typedef enum { (V3D_PROJ_TEST_CLIP_CONTENT | V3D_PROJ_TEST_CLIP_NEAR | V3D_PROJ_TEST_CLIP_FAR | \ V3D_PROJ_TEST_CLIP_WIN) +/* view3d_snap.c */ +bool ED_view3d_snap_selected_to_location(struct bContext *C, + const float snap_target_global[3], + const int pivot_point); + +/* view3d_cursor_snap.c */ +#define USE_SNAP_DETECT_FROM_KEYMAP_HACK +typedef enum { + V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE = 1 << 0, + V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE = 1 << 1, + V3D_SNAPCURSOR_OCCLUSION_ALWAYS_FALSE = 1 << 2, /* TODO. */ + V3D_SNAPCURSOR_SNAP_ONLY_ACTIVE = 1 << 3, + V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL = 1 << 4, + V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE = 1 << 5, +} eV3DSnapCursor; + +typedef enum { + V3D_PLACE_DEPTH_SURFACE = 0, + V3D_PLACE_DEPTH_CURSOR_PLANE = 1, + V3D_PLACE_DEPTH_CURSOR_VIEW = 2, +} eV3DPlaceDepth; + +typedef enum { + V3D_PLACE_ORIENT_SURFACE = 0, + V3D_PLACE_ORIENT_DEFAULT = 1, +} eV3DPlaceOrient; + +typedef struct V3DSnapCursorData { + short snap_elem; + float loc[3]; + float nor[3]; + float obmat[4][4]; + int elem_index[3]; + float plane_omat[3][3]; + bool is_snap_invert; + + /** Enabled when snap is activated, even if it didn't find anything. */ + bool is_enabled; +} V3DSnapCursorData; + +typedef struct V3DSnapCursorState { + /* Setup. */ + eV3DSnapCursor flag; + eV3DPlaceDepth plane_depth; + eV3DPlaceOrient plane_orient; + uchar color_line[4]; + uchar color_point[4]; + uchar color_box[4]; + struct ARegion *region; /* Forces the cursor to be drawn only in this specific region. */ + float *prevpoint; + float box_dimensions[3]; + short snap_elem_force; /* If zero, use scene settings. */ + short plane_axis; + bool use_plane_axis_auto; + bool draw_point; + bool draw_plane; + bool draw_box; +} V3DSnapCursorState; + +void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state); +V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void); +V3DSnapCursorState *ED_view3d_cursor_snap_active(void); +void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state); +void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]); +V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, + const struct bContext *C, + const int x, + const int y); +struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(struct Scene *scene); +void ED_view3d_cursor_snap_draw_util(struct RegionView3D *rv3d, + const float loc_prev[3], + const float loc_curr[3], + const float normal[3], + const uchar color_line[4], + const uchar color_point[4], + const short snap_elem_type); + /* view3d_iterators.c */ /* foreach iterators */ @@ -531,12 +609,8 @@ bool ED_view3d_autodist_simple(struct ARegion *region, float mouse_worldloc[3], int margin, const float *force_depth); -bool ED_view3d_autodist_depth(struct ARegion *region, const int mval[2], int margin, float *depth); -bool ED_view3d_autodist_depth_seg(struct ARegion *region, - const int mval_sta[2], - const int mval_end[2], - int margin, - float *depth); +bool ED_view3d_depth_read_cached_seg( + const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth); /* select */ #define MAXPICKELEMS 2500 diff --git a/source/blender/editors/include/ED_view3d_offscreen.h b/source/blender/editors/include/ED_view3d_offscreen.h index c490e96031f..8b695e61a35 100644 --- a/source/blender/editors/include/ED_view3d_offscreen.h +++ b/source/blender/editors/include/ED_view3d_offscreen.h @@ -60,7 +60,7 @@ void ED_view3d_draw_offscreen(struct Depsgraph *depsgraph, void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph, struct Scene *scene, struct View3DShading *shading_override, - int drawtype, + eDrawType drawtype, int winx, int winy, unsigned int draw_flags, @@ -68,6 +68,7 @@ void ED_view3d_draw_offscreen_simple(struct Depsgraph *depsgraph, const float winmat[4][4], float clip_start, float clip_end, + bool is_xr_surface, bool is_image_render, bool draw_background, const char *viewname, diff --git a/source/blender/editors/include/UI_icons.h b/source/blender/editors/include/UI_icons.h index ebc26095940..94ce2f17c99 100644 --- a/source/blender/editors/include/UI_icons.h +++ b/source/blender/editors/include/UI_icons.h @@ -990,6 +990,16 @@ DEF_ICON_VECTOR(COLLECTION_COLOR_06) DEF_ICON_VECTOR(COLLECTION_COLOR_07) DEF_ICON_VECTOR(COLLECTION_COLOR_08) +DEF_ICON_VECTOR(SEQUENCE_COLOR_01) +DEF_ICON_VECTOR(SEQUENCE_COLOR_02) +DEF_ICON_VECTOR(SEQUENCE_COLOR_03) +DEF_ICON_VECTOR(SEQUENCE_COLOR_04) +DEF_ICON_VECTOR(SEQUENCE_COLOR_05) +DEF_ICON_VECTOR(SEQUENCE_COLOR_06) +DEF_ICON_VECTOR(SEQUENCE_COLOR_07) +DEF_ICON_VECTOR(SEQUENCE_COLOR_08) +DEF_ICON_VECTOR(SEQUENCE_COLOR_09) + /* Events. */ DEF_ICON_COLOR(EVENT_A) DEF_ICON_COLOR(EVENT_B) diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 916105b0f8e..725c9921d13 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -37,6 +37,7 @@ extern "C" { struct ARegion; struct AssetFilterSettings; struct AssetHandle; +struct AssetMetaData; struct AutoComplete; struct EnumPropertyItem; struct FileDirEntry; @@ -82,8 +83,13 @@ struct wmWindow; typedef struct uiBlock uiBlock; typedef struct uiBut uiBut; +typedef struct uiButExtraOpIcon uiButExtraOpIcon; typedef struct uiLayout uiLayout; typedef struct uiPopupBlockHandle uiPopupBlockHandle; +/* C handle for C++ #ui::AbstractTreeView type. */ +typedef struct uiTreeViewHandle uiTreeViewHandle; +/* C handle for C++ #ui::AbstractTreeViewItem type. */ +typedef struct uiTreeViewItemHandle uiTreeViewItemHandle; /* Defines */ @@ -91,6 +97,10 @@ typedef struct uiPopupBlockHandle uiPopupBlockHandle; #define UI_SEP_CHAR '|' #define UI_SEP_CHAR_S "|" +/* Separator for text in search menus (right pointing arrow). + * keep in sync with `string_search.cc`. */ +#define UI_MENU_ARROW_SEP "\xe2\x96\xb6" + /* names */ #define UI_MAX_DRAW_STR 400 #define UI_MAX_NAME_STR 128 @@ -237,7 +247,7 @@ enum { #define UI_DEFAULT_TEXT_POINTS 11 /* Larger size used for title text. */ -#define UI_DEFAULT_TITLE_POINTS 12 +#define UI_DEFAULT_TITLE_POINTS 11 #define UI_PANEL_WIDTH 340 #define UI_COMPACT_PANEL_WIDTH 160 @@ -247,7 +257,8 @@ enum { #define UI_PANEL_CATEGORY_MARGIN_WIDTH (U.widget_unit * 1.0f) -#define UI_PANEL_BOX_STYLE_MARGIN (U.widget_unit * 0.2f) +#define UI_PANEL_MARGIN_X (U.widget_unit * 0.4f) +#define UI_PANEL_MARGIN_Y (U.widget_unit * 0.1f) /* but->drawflag - these flags should only affect how the button is drawn. */ /* NOTE: currently, these flags *are not passed* to the widget's state() or draw() functions @@ -389,6 +400,8 @@ typedef enum { UI_BTYPE_GRIP = 57 << 9, UI_BTYPE_DECORATOR = 58 << 9, UI_BTYPE_DATASETROW = 59 << 9, + /* An item in a tree view. Parent items may be collapsible. */ + UI_BTYPE_TREEROW = 60 << 9, } eButType; #define BUTTYPE (63 << 9) @@ -411,10 +424,6 @@ typedef enum eButGradientType { * Functions to draw various shapes, taking theme settings into account. * Used for code that draws its own UI style elements. */ -void UI_draw_anti_tria( - float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]); -void UI_draw_anti_fan(float tri_array[][2], unsigned int length, const float color[4]); - void UI_draw_roundbox_corner_set(int type); void UI_draw_roundbox_aa(const struct rctf *rect, bool filled, float rad, const float color[4]); void UI_draw_roundbox_4fv(const struct rctf *rect, bool filled, float rad, const float col[4]); @@ -425,12 +434,6 @@ void UI_draw_roundbox_3ub_alpha(const struct rctf *rect, unsigned char alpha); void UI_draw_roundbox_3fv_alpha( const struct rctf *rect, bool filled, float rad, const float col[3], float alpha); -void UI_draw_roundbox_shade_x(const struct rctf *rect, - bool filled, - float rad, - float shadetop, - float shadedown, - const float col[4]); void UI_draw_roundbox_4fv_ex(const struct rctf *rect, const float inner1[4], const float inner2[4], @@ -773,6 +776,7 @@ void UI_but_drag_set_id(uiBut *but, struct ID *id); void UI_but_drag_set_asset(uiBut *but, const struct AssetHandle *asset, const char *path, + struct AssetMetaData *metadata, int import_type, /* eFileAssetImportType */ int icon, struct ImBuf *imb, @@ -784,7 +788,8 @@ void UI_but_drag_set_value(uiBut *but); void UI_but_drag_set_image( uiBut *but, const char *path, int icon, struct ImBuf *imb, float scale, const bool use_free); -bool UI_but_active_drop_name(struct bContext *C); +uiBut *UI_but_active_drop_name_button(const struct bContext *C); +bool UI_but_active_drop_name(const struct bContext *C); bool UI_but_active_drop_color(struct bContext *C); void UI_but_flag_enable(uiBut *but, int flag); @@ -1426,6 +1431,8 @@ typedef struct uiStringInfo { * Will fill them with translated strings, when possible. * Strings in uiStringInfo must be MEM_freeN'ed by caller. */ void UI_but_string_info_get(struct bContext *C, uiBut *but, ...) ATTR_SENTINEL(0); +void UI_but_extra_icon_string_info_get(struct bContext *C, uiButExtraOpIcon *extra_icon, ...) + ATTR_SENTINEL(0); /* Edit i18n stuff. */ /* Name of the main py op from i18n addon. */ @@ -1672,6 +1679,7 @@ void UI_but_datasetrow_component_set(uiBut *but, uint8_t geometry_component_type void UI_but_datasetrow_domain_set(uiBut *but, uint8_t attribute_domain); uint8_t UI_but_datasetrow_component_get(uiBut *but); uint8_t UI_but_datasetrow_domain_get(uiBut *but); +void UI_but_treerow_indentation_set(uiBut *but, int indentation); void UI_but_node_link_set(uiBut *but, struct bNodeSocket *socket, const float draw_color[4]); @@ -1717,6 +1725,8 @@ struct PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, const char *opname, short opcontext, int icon); +struct wmOperatorType *UI_but_extra_operator_icon_optype_get(struct uiButExtraOpIcon *extra_icon); +struct PointerRNA *UI_but_extra_operator_icon_opptr_get(struct uiButExtraOpIcon *extra_icon); /* Autocomplete * @@ -1761,6 +1771,7 @@ void UI_panel_label_offset(const struct uiBlock *block, int *r_x, int *r_y); int UI_panel_size_y(const struct Panel *panel); bool UI_panel_is_dragging(const struct Panel *panel); bool UI_panel_matches_search_filter(const struct Panel *panel); +bool UI_panel_can_be_pinned(const struct Panel *panel); bool UI_panel_category_is_visible(const struct ARegion *region); void UI_panel_category_add(struct ARegion *region, const char *name); @@ -2586,6 +2597,7 @@ typedef struct uiDragColorHandle { void ED_operatortypes_ui(void); void ED_keymap_ui(struct wmKeyConfig *keyconf); +void ED_dropboxes_ui(void); void ED_uilisttypes_ui(void); void UI_drop_color_copy(struct wmDrag *drag, struct wmDropBox *drop); @@ -2668,7 +2680,12 @@ void UI_fontstyle_draw_simple_backdrop(const struct uiFontStyle *fs, const float col_fg[4], const float col_bg[4]); -int UI_fontstyle_string_width(const struct uiFontStyle *fs, const char *str); +int UI_fontstyle_string_width(const struct uiFontStyle *fs, + const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2); +int UI_fontstyle_string_width_with_block_aspect(const struct uiFontStyle *fs, + const char *str, + const float aspect) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2); int UI_fontstyle_height_max(const struct uiFontStyle *fs); void UI_draw_icon_tri(float x, float y, char dir, const float[4]); @@ -2705,6 +2722,11 @@ struct ARegion *UI_tooltip_create_from_button(struct bContext *C, struct ARegion *butregion, uiBut *but, bool is_label); +struct ARegion *UI_tooltip_create_from_button_or_extra_icon(struct bContext *C, + struct ARegion *butregion, + uiBut *but, + uiButExtraOpIcon *extra_icon, + bool is_label); struct ARegion *UI_tooltip_create_from_gizmo(struct bContext *C, struct wmGizmo *gz); void UI_tooltip_free(struct bContext *C, struct bScreen *screen, struct ARegion *region); @@ -2754,6 +2776,25 @@ void UI_interface_tag_script_reload(void); /* Support click-drag motion which presses the button and closes a popover (like a menu). */ #define USE_UI_POPOVER_ONCE +bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item); +bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a, const uiTreeViewItemHandle *b); +bool UI_tree_view_item_drag_start(struct bContext *C, uiTreeViewItemHandle *item_); +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, + const struct wmDrag *drag, + const char **r_disabled_hint); +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item, const struct wmDrag *drag); +bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const struct ListBase *drags); +bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle); +void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle); + +void UI_tree_view_item_context_menu_build(struct bContext *C, + const uiTreeViewItemHandle *item, + uiLayout *column); + +uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const struct ARegion *region, + const int xy[2]) ATTR_NONNULL(1, 2); +uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const struct ARegion *region); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/include/UI_interface.hh b/source/blender/editors/include/UI_interface.hh new file mode 100644 index 00000000000..b14ee6c4a59 --- /dev/null +++ b/source/blender/editors/include/UI_interface.hh @@ -0,0 +1,72 @@ +/* + * 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 editorui + */ + +#pragma once + +#include + +#include "BLI_string_ref.hh" +#include "BLI_vector.hh" + +#include "UI_resources.h" + +namespace blender::nodes::geometry_nodes_eval_log { +struct GeometryAttributeInfo; +} + +struct uiBlock; +struct StructRNA; +struct uiSearchItems; + +namespace blender::ui { + +class AbstractTreeView; + +/** + * An item in a breadcrumb-like context. Currently this struct is very simple, but more + * could be added to it in the future, to support interactivity or tooltips, for example. + */ +struct ContextPathItem { + /* Text to display in the UI. */ + std::string name; + /* #BIFIconID */ + int icon; +}; + +void context_path_add_generic(Vector &path, + StructRNA &rna_type, + void *ptr, + const BIFIconID icon_override = ICON_NONE); + +void template_breadcrumbs(uiLayout &layout, Span context_path); + +void attribute_search_add_items( + StringRefNull str, + const bool is_output, + Span infos, + uiSearchItems *items, + const bool is_first); + +} // namespace blender::ui + +blender::ui::AbstractTreeView *UI_block_add_view( + uiBlock &block, + blender::StringRef idname, + std::unique_ptr tree_view); diff --git a/source/blender/editors/include/UI_resources.h b/source/blender/editors/include/UI_resources.h index dde8a637e05..61da496d344 100644 --- a/source/blender/editors/include/UI_resources.h +++ b/source/blender/editors/include/UI_resources.h @@ -64,7 +64,7 @@ typedef enum ThemeColorID { TH_TAB_OUTLINE, TH_HEADER, - TH_HEADERDESEL, + TH_HEADER_ACTIVE, TH_HEADER_TEXT, TH_HEADER_TEXT_HI, diff --git a/source/blender/editors/include/UI_tree_view.hh b/source/blender/editors/include/UI_tree_view.hh new file mode 100644 index 00000000000..0d18eedeac9 --- /dev/null +++ b/source/blender/editors/include/UI_tree_view.hh @@ -0,0 +1,463 @@ +/* + * 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 editorui + * + * API for simple creation of tree UIs supporting advanced features. + * https://wiki.blender.org/wiki/Source/Interface/Views + */ + +#pragma once + +#include +#include +#include +#include + +#include "DNA_defs.h" + +#include "BLI_function_ref.hh" +#include "BLI_vector.hh" + +#include "UI_resources.h" + +struct bContext; +struct PointerRNA; +struct uiBlock; +struct uiBut; +struct uiButTreeRow; +struct uiLayout; +struct wmEvent; +struct wmDrag; + +namespace blender::ui { + +class AbstractTreeView; +class AbstractTreeViewItem; +class AbstractTreeViewItemDropController; +class AbstractTreeViewItemDragController; + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Item Container + * \{ */ + +/** + * Helper base class to expose common child-item data and functionality to both #AbstractTreeView + * and #AbstractTreeViewItem. + * + * That means this type can be used whenever either a #AbstractTreeView or a + * #AbstractTreeViewItem is needed. + */ +class TreeViewItemContainer { + friend class AbstractTreeView; + friend class AbstractTreeViewItem; + + /* Private constructor, so only the friends above can create this! */ + TreeViewItemContainer() = default; + + protected: + Vector> children_; + /** Adding the first item to the root will set this, then it's passed on to all children. */ + TreeViewItemContainer *root_ = nullptr; + /** Pointer back to the owning item. */ + AbstractTreeViewItem *parent_ = nullptr; + + public: + enum class IterOptions { + None = 0, + SkipCollapsed = 1 << 0, + + /* Keep ENUM_OPERATORS() below updated! */ + }; + using ItemIterFn = FunctionRef; + + /** + * Convenience wrapper constructing the item by forwarding given arguments to the constructor of + * the type (\a ItemT). + * + * E.g. if your tree-item type has the following constructor: + * \code{.cpp} + * MyTreeItem(std::string str, int i); + * \endcode + * You can add an item like this: + * \code + * add_tree_item("blabla", 42); + * \endcode + */ + template inline ItemT &add_tree_item(Args &&...args); + /** + * Add an already constructed tree item to this parent. Ownership is moved to it. + * All tree items must be added through this, it handles important invariants! + */ + AbstractTreeViewItem &add_tree_item(std::unique_ptr item); + + protected: + void foreach_item_recursive(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; +}; + +ENUM_OPERATORS(TreeViewItemContainer::IterOptions, + TreeViewItemContainer::IterOptions::SkipCollapsed); + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Builders + * \{ */ + +class TreeViewBuilder { + uiBlock &block_; + + public: + TreeViewBuilder(uiBlock &block); + + void build_tree_view(AbstractTreeView &tree_view); +}; + +class TreeViewLayoutBuilder { + uiBlock &block_; + + friend TreeViewBuilder; + + public: + void build_row(AbstractTreeViewItem &item) const; + uiBlock &block() const; + uiLayout *current_layout() const; + + private: + /* Created through #TreeViewBuilder. */ + TreeViewLayoutBuilder(uiBlock &block); + + static void polish_layout(const uiBlock &block); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Base Class + * \{ */ + +class AbstractTreeView : public TreeViewItemContainer { + friend AbstractTreeViewItem; + friend TreeViewBuilder; + + /** + * Only one item can be renamed at a time. So the tree is informed about the renaming state to + * enforce that. + */ + std::unique_ptr> rename_buffer_; + + bool is_reconstructed_ = false; + + public: + virtual ~AbstractTreeView() = default; + + void foreach_item(ItemIterFn iter_fn, IterOptions options = IterOptions::None) const; + + /** Only one item can be renamed at a time. */ + bool is_renaming() const; + /** + * Check if the tree is fully (re-)constructed. That means, both #build_tree() and + * #update_from_old() have finished. + */ + bool is_reconstructed() const; + + protected: + virtual void build_tree() = 0; + + private: + /** + * Match the tree-view against an earlier version of itself (if any) and copy the old UI state + * (e.g. collapsed, active, selected, renaming, etc.) to the new one. See + * #AbstractTreeViewItem.update_from_old(). + */ + void update_from_old(uiBlock &new_block); + static void update_children_from_old_recursive(const TreeViewItemContainer &new_items, + const TreeViewItemContainer &old_items); + static AbstractTreeViewItem *find_matching_child(const AbstractTreeViewItem &lookup_item, + const TreeViewItemContainer &items); + + /** + * Items may want to do additional work when state changes. But these state changes can only be + * reliably detected after the tree has completed reconstruction (see #is_reconstructed()). So + * the actual state changes are done in a delayed manner through this function. + */ + void change_state_delayed(); + void build_layout_from_tree(const TreeViewLayoutBuilder &builder); +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Tree-View Item Type + * \{ */ + +/** \brief Abstract base class for defining a customizable tree-view item. + * + * The tree-view item defines how to build its data into a tree-row. There are implementations for + * common layouts, e.g. #BasicTreeViewItem. + * It also stores state information that needs to be persistent over redraws, like the collapsed + * state. + */ +class AbstractTreeViewItem : public TreeViewItemContainer { + friend class AbstractTreeView; + friend class TreeViewLayoutBuilder; + + public: + using IsActiveFn = std::function; + + private: + bool is_open_ = false; + bool is_active_ = false; + bool is_renaming_ = false; + + IsActiveFn is_active_fn_; + + protected: + /** This label is used for identifying an item (together with its parent's labels). */ + std::string label_{}; + /** Every item gets a button of type during the layout building #UI_BTYPE_TREEROW. */ + uiButTreeRow *tree_row_but_ = nullptr; + + public: + virtual ~AbstractTreeViewItem() = default; + + virtual void build_row(uiLayout &row) = 0; + virtual void build_context_menu(bContext &C, uiLayout &column) const; + + virtual void on_activate(); + /** + * Set a custom callback to check if this item should be active. There's a version without + * arguments for checking if the item is currently in an active state. + */ + virtual void is_active(IsActiveFn is_active_fn); + + /** + * Queries if the tree-view item supports renaming in principle. Renaming may still fail, e.g. if + * another item is already being renamed. + */ + virtual bool can_rename() const; + /** + * Try renaming the item, or the data it represents. Can assume + * #AbstractTreeViewItem::can_rename() returned true. Sub-classes that override this should + * usually call this, unless they have a custom #AbstractTreeViewItem.matches(). + * + * \return True if the renaming was successful. + */ + virtual bool rename(StringRefNull new_name); + + /** + * Copy persistent state (e.g. is-collapsed flag, selection, etc.) from a matching item of + * the last redraw to this item. If sub-classes introduce more advanced state they should + * override this and make it update their state accordingly. + */ + virtual void update_from_old(const AbstractTreeViewItem &old); + /** + * Compare this item to \a other to check if they represent the same data. + * Used to recognize an item from a previous redraw, to be able to keep its state (e.g. + * open/closed, active, etc.). Items are only matched if their parents also match. + * By default this just matches the item's label (if the parents match!). If that isn't + * good enough for a sub-class, that can override it. + */ + virtual bool matches(const AbstractTreeViewItem &other) const; + + /** + * If an item wants to support being dragged, it has to return a drag controller here. + * That is an object implementing #AbstractTreeViewItemDragController. + */ + virtual std::unique_ptr create_drag_controller() const; + /** + * If an item wants to support dropping data into it, it has to return a drop controller here. + * That is an object implementing #AbstractTreeViewItemDropController. + * + * \note This drop controller may be requested for each event. The tree-view doesn't keep a drop + * controller around currently. So it can not contain persistent state. + */ + virtual std::unique_ptr create_drop_controller() const; + + void begin_renaming(); + void end_renaming(); + + AbstractTreeView &get_tree_view() const; + int count_parents() const; + void deactivate(); + /** + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ + bool is_active() const; + /** + * Can be called from the #AbstractTreeViewItem::build_row() implementation, but not earlier. The + * hovered state can't be queried reliably otherwise. + * Note that this does a linear lookup in the old block, so isn't too great performance-wise. + */ + bool is_hovered() const; + void toggle_collapsed(); + /** + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise we + * can't be sure about the item state. + */ + bool is_collapsed() const; + void set_collapsed(bool collapsed); + bool is_collapsible() const; + bool is_renaming() const; + + void ensure_parents_uncollapsed(); + bool matches_including_parents(const AbstractTreeViewItem &other) const; + + uiButTreeRow *tree_row_button(); + + protected: + /** + * Activates this item, deactivates other items, calls the #AbstractTreeViewItem::on_activate() + * function and ensures this item's parents are not collapsed (so the item is visible). + * Requires the tree to have completed reconstruction, see #is_reconstructed(). Otherwise the + * actual item state is unknown, possibly calling state-change update functions incorrectly. + */ + void activate(); + + private: + static void rename_button_fn(bContext *, void *, char *); + static AbstractTreeViewItem *find_tree_item_from_rename_button(const uiBut &but); + static void tree_row_click_fn(struct bContext *, void *, void *); + static void collapse_chevron_click_fn(bContext *, void *but_arg1, void *); + static bool is_collapse_chevron_but(const uiBut *but); + + /** See #AbstractTreeView::change_state_delayed() */ + void change_state_delayed(); + + void add_treerow_button(uiBlock &block); + void add_indent(uiLayout &row) const; + void add_collapse_chevron(uiBlock &block) const; + void add_rename_button(uiLayout &row); + + bool has_active_child() const; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Drag 'n Drop + * \{ */ + +/** + * Class to enable dragging a tree-item. An item can return a drop controller for itself via a + * custom implementation of #AbstractTreeViewItem::create_drag_controller(). + */ +class AbstractTreeViewItemDragController { + public: + virtual ~AbstractTreeViewItemDragController() = default; + + virtual int get_drag_type() const = 0; + virtual void *create_drag_data() const = 0; +}; + +/** + * Class to customize the drop behavior of a tree-item, plus the behavior when dragging over this + * item. An item can return a drop controller for itself via a custom implementation of + * #AbstractTreeViewItem::create_drop_controller(). + */ +class AbstractTreeViewItemDropController { + protected: + AbstractTreeView &tree_view_; + + public: + AbstractTreeViewItemDropController(AbstractTreeView &tree_view); + virtual ~AbstractTreeViewItemDropController() = default; + + /** + * Check if the data dragged with \a drag can be dropped on the item this controller is for. + * \param r_disabled_hint: Return a static string to display to the user, explaining why dropping + * isn't possible on this item. Shouldn't be done too aggressively, e.g. + * don't set this if the drag-type can't be dropped here; only if it can + * but there's another reason it can't be dropped. + * Can assume this is a non-null pointer. + */ + virtual bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const = 0; + /** + * Custom text to display when dragging over a tree item. Should explain what happens when + * dropping the data onto this item. Will only be used if #AbstractTreeViewItem::can_drop() + * returns true, so the implementing override doesn't have to check that again. + * The returned value must be a translated string. + */ + virtual std::string drop_tooltip(const wmDrag &drag) const = 0; + /** + * Execute the logic to apply a drop of the data dragged with \a drag onto/into the item this + * controller is for. + */ + virtual bool on_drop(const wmDrag &drag) = 0; + + template inline TreeViewType &tree_view() const; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ +/** \name Predefined Tree-View Item Types + * + * Common, Basic Tree-View Item Types. + * \{ */ + +/** + * The most basic type, just a label with an icon. + */ +class BasicTreeViewItem : public AbstractTreeViewItem { + public: + using ActivateFn = std::function; + BIFIconID icon; + + explicit BasicTreeViewItem(StringRef label, BIFIconID icon = ICON_NONE); + + void build_row(uiLayout &row) override; + void add_label(uiLayout &layout, StringRefNull label_override = ""); + void on_activate(ActivateFn fn); + + protected: + /** + * Optionally passed to the #BasicTreeViewItem constructor. Called when activating this tree + * view item. This way users don't have to sub-class #BasicTreeViewItem, just to implement + * custom activation behavior (a common thing to do). + */ + ActivateFn activate_fn_; + + private: + static void tree_row_click_fn(struct bContext *C, void *arg1, void *arg2); + + void on_activate() override; +}; + +/** \} */ + +/* ---------------------------------------------------------------------- */ + +template +inline ItemT &TreeViewItemContainer::add_tree_item(Args &&...args) +{ + static_assert(std::is_base_of::value, + "Type must derive from and implement the AbstractTreeViewItem interface"); + + return dynamic_cast( + add_tree_item(std::make_unique(std::forward(args)...))); +} + +template TreeViewType &AbstractTreeViewItemDropController::tree_view() const +{ + static_assert(std::is_base_of::value, + "Type must derive from and implement the AbstractTreeView interface"); + return static_cast(tree_view_); +} + +} // namespace blender::ui diff --git a/source/blender/editors/include/UI_view2d.h b/source/blender/editors/include/UI_view2d.h index e3c02b4c249..122e5a7d839 100644 --- a/source/blender/editors/include/UI_view2d.h +++ b/source/blender/editors/include/UI_view2d.h @@ -123,6 +123,7 @@ void UI_view2d_region_reinit(struct View2D *v2d, short type, int winx, int winy) void UI_view2d_curRect_validate(struct View2D *v2d); void UI_view2d_curRect_reset(struct View2D *v2d); +bool UI_view2d_area_supports_sync(struct ScrArea *area); void UI_view2d_sync(struct bScreen *screen, struct ScrArea *area, struct View2D *v2dcur, int flag); /* Perform all required updates after `v2d->cur` as been modified. @@ -146,6 +147,10 @@ void UI_view2d_view_restore(const struct bContext *C); /* grid drawing */ void UI_view2d_multi_grid_draw( const struct View2D *v2d, int colorid, float step, int level_size, int totlevels); +void UI_view2d_dot_grid_draw(const struct View2D *v2d, + int grid_color_id, + float step, + int grid_levels); void UI_view2d_draw_lines_y__values(const struct View2D *v2d); void UI_view2d_draw_lines_x__values(const struct View2D *v2d); @@ -248,19 +253,20 @@ void UI_view2d_center_set(struct View2D *v2d, float x, float y); void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac); -char UI_view2d_mouse_in_scrollers_ex( - const struct ARegion *region, const struct View2D *v2d, int x, int y, int *r_scroll); +char UI_view2d_mouse_in_scrollers_ex(const struct ARegion *region, + const struct View2D *v2d, + const int xy[2], + int *r_scroll) ATTR_NONNULL(1, 2, 3, 4); char UI_view2d_mouse_in_scrollers(const struct ARegion *region, const struct View2D *v2d, - int x, - int y); + const int xy[2]) ATTR_NONNULL(1, 2, 3); char UI_view2d_rect_in_scrollers_ex(const struct ARegion *region, const struct View2D *v2d, const struct rcti *rect, - int *r_scroll); + int *r_scroll) ATTR_NONNULL(1, 2, 3); char UI_view2d_rect_in_scrollers(const struct ARegion *region, const struct View2D *v2d, - const struct rcti *rect); + const struct rcti *rect) ATTR_NONNULL(1, 2, 3); /* cached text drawing in v2d, to allow pixel-aligned draw as post process */ void UI_view2d_text_cache_add(struct View2D *v2d, @@ -309,6 +315,9 @@ typedef struct View2DEdgePanData { /** View2d we're operating in. */ struct View2D *v2d; + /** Panning should only start once being in the inside rect once (e.g. adding nodes can happen + * outside). */ + bool enabled; /** Inside distance in UI units from the edge of the region within which to start panning. */ float inside_pad; /** Outside distance in UI units from the edge of the region at which to stop panning. */ @@ -353,7 +362,8 @@ void UI_view2d_edge_pan_init(struct bContext *C, void UI_view2d_edge_pan_reset(struct View2DEdgePanData *vpd); /* Apply transform to view (i.e. adjust 'cur' rect). */ -void UI_view2d_edge_pan_apply(struct bContext *C, struct View2DEdgePanData *vpd, int x, int y); +void UI_view2d_edge_pan_apply(struct bContext *C, struct View2DEdgePanData *vpd, const int xy[2]) + ATTR_NONNULL(1, 2, 3); /* Apply transform to view using mouse events. */ void UI_view2d_edge_pan_apply_event(struct bContext *C, diff --git a/source/blender/editors/interface/CMakeLists.txt b/source/blender/editors/interface/CMakeLists.txt index 39dd6143eb9..84172c7efce 100644 --- a/source/blender/editors/interface/CMakeLists.txt +++ b/source/blender/editors/interface/CMakeLists.txt @@ -25,9 +25,11 @@ set(INC ../../depsgraph ../../draw ../../gpu + ../../functions ../../imbuf ../../makesdna ../../makesrna + ../../nodes ../../python ../../render ../../windowmanager @@ -41,7 +43,9 @@ set(SRC interface_anim.c interface_button_group.c interface_context_menu.c + interface_context_path.cc interface_draw.c + interface_dropboxes.cc interface_eyedropper.c interface_eyedropper_color.c interface_eyedropper_colorband.c @@ -68,13 +72,16 @@ set(SRC interface_style.c interface_template_asset_view.cc interface_template_list.cc + interface_template_attribute_search.cc interface_template_search_menu.c interface_template_search_operator.c interface_templates.c interface_undo.c interface_utils.c + interface_view.cc interface_widgets.c resources.c + tree_view.cc view2d.c view2d_draw.c view2d_edge_pan.c diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index fd75be5b847..dc9eaed5731 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -742,6 +742,18 @@ static bool ui_but_equals_old(const uiBut *but, const uiBut *oldbut) if (but->optype != oldbut->optype) { return false; } + if (but->dragtype != oldbut->dragtype) { + return false; + } + + if ((but->type == UI_BTYPE_TREEROW) && (oldbut->type == UI_BTYPE_TREEROW)) { + uiButTreeRow *but_treerow = (uiButTreeRow *)but; + uiButTreeRow *oldbut_treerow = (uiButTreeRow *)oldbut; + if (!but_treerow->tree_item || !oldbut_treerow->tree_item || + !UI_tree_view_item_matches(but_treerow->tree_item, oldbut_treerow->tree_item)) { + return false; + } + } return true; } @@ -856,10 +868,21 @@ static void ui_but_update_old_active_from_new(uiBut *oldbut, uiBut *but) oldbut->hardmax = but->hardmax; } - if (oldbut->type == UI_BTYPE_PROGRESS_BAR) { - uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut; - uiButProgressbar *progress_but = (uiButProgressbar *)but; - progress_oldbut->progress = progress_but->progress; + switch (oldbut->type) { + case UI_BTYPE_PROGRESS_BAR: { + uiButProgressbar *progress_oldbut = (uiButProgressbar *)oldbut; + uiButProgressbar *progress_but = (uiButProgressbar *)but; + progress_oldbut->progress = progress_but->progress; + break; + } + case UI_BTYPE_TREEROW: { + uiButTreeRow *treerow_oldbut = (uiButTreeRow *)oldbut; + uiButTreeRow *treerow_newbut = (uiButTreeRow *)but; + SWAP(uiTreeViewItemHandle *, treerow_newbut->tree_item, treerow_oldbut->tree_item); + break; + } + default: + break; } /* move/copy string from the new button to the old */ @@ -945,7 +968,13 @@ static bool ui_but_update_from_old_block(const bContext *C, found_active = true; } else { - const int flag_copy = UI_BUT_DRAG_MULTI; + int flag_copy = UI_BUT_DRAG_MULTI; + + /* Stupid special case: The active button may be inside (as in, overlapped on top) a tree-row + * button which we also want to keep highlighted then. */ + if (but->type == UI_BTYPE_TREEROW) { + flag_copy |= UI_ACTIVE; + } but->flag = (but->flag & ~flag_copy) | (oldbut->flag & flag_copy); @@ -994,6 +1023,9 @@ bool UI_but_active_only_ex( else if ((found == true) && (isactive == false)) { if (remove_on_failure) { BLI_remlink(&block->buttons, but); + if (but->layout) { + ui_layout_remove_but(but->layout, but); + } ui_but_free(C, but); } return false; @@ -1195,16 +1227,21 @@ void ui_but_add_shortcut(uiBut *but, const char *shortcut_str, const bool do_str * \{ */ static bool ui_but_event_operator_string_from_operator(const bContext *C, - uiBut *but, + wmOperatorCallParams *op_call_params, char *buf, const size_t buf_len) { - BLI_assert(but->optype != NULL); + BLI_assert(op_call_params->optype != NULL); bool found = false; - IDProperty *prop = (but->opptr) ? but->opptr->data : NULL; + IDProperty *prop = (op_call_params->opptr) ? op_call_params->opptr->data : NULL; - if (WM_key_event_operator_string( - C, but->optype->idname, but->opcontext, prop, true, buf, buf_len)) { + if (WM_key_event_operator_string(C, + op_call_params->optype->idname, + op_call_params->opcontext, + prop, + true, + buf, + buf_len)) { found = true; } return found; @@ -1289,7 +1326,12 @@ static bool ui_but_event_operator_string(const bContext *C, bool found = false; if (but->optype != NULL) { - found = ui_but_event_operator_string_from_operator(C, but, buf, buf_len); + found = ui_but_event_operator_string_from_operator( + C, + &(wmOperatorCallParams){ + .optype = but->optype, .opptr = but->opptr, .opcontext = but->opcontext}, + buf, + buf_len); } else if (UI_but_menutype_get(but) != NULL) { found = ui_but_event_operator_string_from_menu(C, but, buf, buf_len); @@ -1301,6 +1343,20 @@ static bool ui_but_event_operator_string(const bContext *C, return found; } +static bool ui_but_extra_icon_event_operator_string(const bContext *C, + uiButExtraOpIcon *extra_icon, + char *buf, + const size_t buf_len) +{ + wmOperatorType *extra_icon_optype = UI_but_extra_operator_icon_optype_get(extra_icon); + + if (extra_icon_optype) { + return ui_but_event_operator_string_from_operator(C, extra_icon->optype_params, buf, buf_len); + } + + return false; +} + static bool ui_but_event_property_operator_string(const bContext *C, uiBut *but, char *buf, @@ -1339,6 +1395,7 @@ static bool ui_but_event_property_operator_string(const bContext *C, const char *prop_enum_value_id = "value"; PointerRNA *ptr = &but->rnapoin; PropertyRNA *prop = but->rnaprop; + int prop_index = but->rnaindex; if ((but->type == UI_BTYPE_BUT_MENU) && (but->block->handle != NULL)) { uiBut *but_parent = but->block->handle->popup_create_vars.but; if ((but->type == UI_BTYPE_BUT_MENU) && (but_parent && but_parent->rnaprop) && @@ -1363,28 +1420,15 @@ static bool ui_but_event_property_operator_string(const bContext *C, return false; } - /* this version is only for finding hotkeys for properties - * (which get set via context using operators) */ - /* to avoid massive slowdowns on property panels, for now, we only check the - * hotkeys for Editor / Scene settings... - * - * TODO: userpref settings? - */ - char *data_path = NULL; + /* This version is only for finding hotkeys for properties. + * These are set set via a data-path which is appended to the context, + * manipulated using operators (see #ctx_toggle_opnames). */ if (ptr->owner_id) { ID *id = ptr->owner_id; if (GS(id->name) == ID_SCR) { - /* screen/editor property - * NOTE: in most cases, there is actually no info for backwards tracing - * how to get back to ID from the editor data we may be dealing with - */ - if (RNA_struct_is_a(ptr->type, &RNA_Space)) { - /* data should be directly on here... */ - data_path = BLI_sprintfN("space_data.%s", RNA_property_identifier(prop)); - } - else if (RNA_struct_is_a(ptr->type, &RNA_Area)) { + if (RNA_struct_is_a(ptr->type, &RNA_Area)) { /* data should be directly on here... */ const char *prop_id = RNA_property_identifier(prop); /* Hack since keys access 'type', UI shows 'ui_type'. */ @@ -1392,58 +1436,19 @@ static bool ui_but_event_property_operator_string(const bContext *C, prop_id = "type"; prop_enum_value >>= 16; prop = RNA_struct_find_property(ptr, prop_id); + prop_index = -1; opnames = ctx_enum_opnames_for_Area_ui_type; opnames_len = ARRAY_SIZE(ctx_enum_opnames_for_Area_ui_type); prop_enum_value_id = "space_type"; prop_enum_value_is_int = true; } - else { - data_path = BLI_sprintfN("area.%s", prop_id); - } - } - else { - /* special exceptions for common nested data in editors... */ - if (RNA_struct_is_a(ptr->type, &RNA_DopeSheet)) { - /* Dope-sheet filtering options. */ - data_path = BLI_sprintfN("space_data.dopesheet.%s", RNA_property_identifier(prop)); - } - else if (RNA_struct_is_a(ptr->type, &RNA_FileSelectParams)) { - /* File-browser options. */ - data_path = BLI_sprintfN("space_data.params.%s", RNA_property_identifier(prop)); - } } } - else if (GS(id->name) == ID_SCE) { - if (RNA_struct_is_a(ptr->type, &RNA_ToolSettings)) { - /* Tool-settings property: - * NOTE: tool-settings is usually accessed directly (i.e. not through scene). */ - data_path = RNA_path_from_ID_to_property(ptr, prop); - } - else { - /* scene property */ - char *path = RNA_path_from_ID_to_property(ptr, prop); - - if (path) { - data_path = BLI_sprintfN("scene.%s", path); - MEM_freeN(path); - } -#if 0 - else { - printf("ERROR in %s(): Couldn't get path for scene property - %s\n", - __func__, - RNA_property_identifier(prop)); - } -#endif - } - } - else { - // puts("other id"); - } - - // printf("prop shortcut: '%s' (%s)\n", RNA_property_identifier(prop), data_path); } + char *data_path = WM_context_path_resolve_property_full(C, ptr, prop, prop_index); + /* We have a data-path! */ bool found = false; if (data_path || (prop_enum_value_ok && prop_enum_value_id)) { @@ -1648,6 +1653,7 @@ static PointerRNA *ui_but_extra_operator_icon_add_ptr(uiBut *but, extra_op_icon->optype_params->optype); extra_op_icon->optype_params->opcontext = opcontext; extra_op_icon->highlighted = false; + extra_op_icon->disabled = false; BLI_addtail(&but->extra_op_icons, extra_op_icon); @@ -1684,6 +1690,16 @@ PointerRNA *UI_but_extra_operator_icon_add(uiBut *but, return NULL; } +wmOperatorType *UI_but_extra_operator_icon_optype_get(uiButExtraOpIcon *extra_icon) +{ + return extra_icon ? extra_icon->optype_params->optype : NULL; +} + +PointerRNA *UI_but_extra_operator_icon_opptr_get(uiButExtraOpIcon *extra_icon) +{ + return extra_icon->optype_params->opptr; +} + static bool ui_but_icon_extra_is_visible_text_clear(const uiBut *but) { BLI_assert(but->type == UI_BTYPE_TEXT); @@ -1842,18 +1858,19 @@ static void ui_but_validate(const uiBut *but) /** * Check if the operator \a ot poll is successful with the context given by \a but (optionally). * \param but: The button that might store context. Can be NULL for convenience (e.g. if there is - * no button to take context from, but we still want to poll the operator). + * no button to take context from, but we still want to poll the operator). */ -bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut *but) +bool ui_but_context_poll_operator_ex(bContext *C, + const uiBut *but, + const wmOperatorCallParams *optype_params) { bool result; - int opcontext = but ? but->opcontext : WM_OP_INVOKE_DEFAULT; if (but && but->context) { CTX_store_set(C, but->context); } - result = WM_operator_poll_context(C, ot, opcontext); + result = WM_operator_poll_context(C, optype_params->optype, optype_params->opcontext); if (but && but->context) { CTX_store_set(C, NULL); @@ -1862,6 +1879,13 @@ bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut * return result; } +bool ui_but_context_poll_operator(bContext *C, wmOperatorType *ot, const uiBut *but) +{ + const int opcontext = but ? but->opcontext : WM_OP_INVOKE_DEFAULT; + return ui_but_context_poll_operator_ex( + C, but, &(wmOperatorCallParams){.optype = ot, .opcontext = opcontext}); +} + void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_xy[2]) { wmWindow *window = CTX_wm_window(C); @@ -1892,6 +1916,12 @@ void UI_block_end_ex(const bContext *C, uiBlock *block, const int xy[2], int r_x } } + LISTBASE_FOREACH (uiButExtraOpIcon *, op_icon, &but->extra_op_icons) { + if (!ui_but_context_poll_operator_ex((bContext *)C, but, op_icon->optype_params)) { + op_icon->disabled = true; + } + } + const AnimationEvalContext anim_eval_context = BKE_animsys_eval_context_construct( depsgraph, (scene) ? scene->r.cfra : 0.0f); ui_but_anim_flag(but, &anim_eval_context); @@ -1958,7 +1988,7 @@ void UI_block_end(const bContext *C, uiBlock *block) { wmWindow *window = CTX_wm_window(C); - UI_block_end_ex(C, block, &window->eventstate->x, NULL); + UI_block_end_ex(C, block, window->eventstate->xy, NULL); } /* ************** BLOCK DRAWING FUNCTION ************* */ @@ -2203,6 +2233,15 @@ int ui_but_is_pushed_ex(uiBut *but, double *value) } } break; + case UI_BTYPE_TREEROW: { + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + + is_push = -1; + if (tree_row_but->tree_item) { + is_push = UI_tree_view_item_is_active(tree_row_but->tree_item); + } + break; + } default: is_push = -1; break; @@ -2528,7 +2567,7 @@ double ui_but_value_get(uiBut *but) void ui_but_value_set(uiBut *but, double value) { - /* value is a hsv value: convert to rgb */ + /* Value is a HSV value: convert to RGB. */ if (but->rnaprop) { PropertyRNA *prop = but->rnaprop; @@ -3447,6 +3486,7 @@ void UI_block_free(const bContext *C, uiBlock *block) BLI_freelistN(&block->color_pickers.list); ui_block_free_button_groups(block); + ui_block_free_views(block); MEM_freeN(block); } @@ -3942,6 +3982,10 @@ static void ui_but_alloc_info(const eButType type, alloc_size = sizeof(uiButDatasetRow); alloc_str = "uiButDatasetRow"; break; + case UI_BTYPE_TREEROW: + alloc_size = sizeof(uiButTreeRow); + alloc_str = "uiButTreeRow"; + break; default: alloc_size = sizeof(uiBut); alloc_str = "uiBut"; @@ -4141,6 +4185,7 @@ static uiBut *ui_def_but(uiBlock *block, UI_BTYPE_BUT_MENU, UI_BTYPE_SEARCH_MENU, UI_BTYPE_DATASETROW, + UI_BTYPE_TREEROW, UI_BTYPE_POPOVER)) { but->drawflag |= (UI_BUT_TEXT_LEFT | UI_BUT_ICON_LEFT); } @@ -4405,7 +4450,7 @@ static void ui_def_but_rna__panel_type(bContext *C, uiLayout *layout, void *but_ } else { char msg[256]; - SNPRINTF(msg, "Missing Panel: %s", panel_type); + SNPRINTF(msg, TIP_("Missing Panel: %s"), panel_type); uiItemL(layout, msg, ICON_NONE); } } @@ -4434,7 +4479,7 @@ static void ui_def_but_rna__menu_type(bContext *C, uiLayout *layout, void *but_p } else { char msg[256]; - SNPRINTF(msg, "Missing Menu: %s", menu_type); + SNPRINTF(msg, TIP_("Missing Menu: %s"), menu_type); uiItemL(layout, msg, ICON_NONE); } } @@ -6186,17 +6231,20 @@ void UI_but_drag_set_id(uiBut *but, ID *id) void UI_but_drag_set_asset(uiBut *but, const AssetHandle *asset, const char *path, + struct AssetMetaData *metadata, int import_type, int icon, struct ImBuf *imb, float scale) { - wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); + wmDragAsset *asset_drag = WM_drag_create_asset_data(asset, metadata, path, import_type); - BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); - asset_drag->path = path; - asset_drag->id_type = ED_asset_handle_get_id_type(asset); - asset_drag->import_type = import_type; + /* FIXME: This is temporary evil solution to get scene/viewlayer/etc in the copy callback of the + * #wmDropBox. + * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its + * copy callback. + * */ + asset_drag->evil_C = but->block->evil_C; but->dragtype = WM_DRAG_ASSET; ui_def_but_icon(but, icon, 0); /* no flag UI_HAS_ICON, so icon doesn't draw in button */ @@ -6878,6 +6926,15 @@ void UI_but_datasetrow_indentation_set(uiBut *but, int indentation) BLI_assert(indentation >= 0); } +void UI_but_treerow_indentation_set(uiBut *but, int indentation) +{ + uiButTreeRow *but_row = (uiButTreeRow *)but; + BLI_assert(but->type == UI_BTYPE_TREEROW); + + but_row->indentation = indentation; + BLI_assert(indentation >= 0); +} + /** * Adds a hint to the button which draws right aligned, grayed out and never clipped. */ @@ -6957,7 +7014,7 @@ void UI_but_focus_on_enter_event(wmWindow *win, uiBut *but) event.val = KM_PRESS; event.is_repeat = false; event.customdata = but; - event.customdatafree = false; + event.customdata_free = false; wm_event_add(win, &event); } @@ -7207,6 +7264,42 @@ void UI_but_string_info_get(bContext *C, uiBut *but, ...) } } +void UI_but_extra_icon_string_info_get(struct bContext *C, uiButExtraOpIcon *extra_icon, ...) +{ + va_list args; + uiStringInfo *si; + + wmOperatorType *optype = UI_but_extra_operator_icon_optype_get(extra_icon); + PointerRNA *opptr = UI_but_extra_operator_icon_opptr_get(extra_icon); + + va_start(args, extra_icon); + while ((si = (uiStringInfo *)va_arg(args, void *))) { + char *tmp = NULL; + + switch (si->type) { + case BUT_GET_LABEL: + tmp = BLI_strdup(WM_operatortype_name(optype, opptr)); + break; + case BUT_GET_TIP: + tmp = WM_operatortype_description(C, optype, opptr); + break; + case BUT_GET_OP_KEYMAP: { + char buf[128]; + if (ui_but_extra_icon_event_operator_string(C, extra_icon, buf, sizeof(buf))) { + tmp = BLI_strdup(buf); + } + } + /* Other types not supported. The caller should expect that outcome, no need to message or + * assert here. */ + default: + break; + } + + si->strinfo = tmp; + } + va_end(args); +} + /* Program Init/Exit */ void UI_init(void) diff --git a/source/blender/editors/interface/interface_button_group.c b/source/blender/editors/interface/interface_button_group.c index 4e7da4ada33..7054498d469 100644 --- a/source/blender/editors/interface/interface_button_group.c +++ b/source/blender/editors/interface/interface_button_group.c @@ -57,7 +57,7 @@ void ui_button_group_add_but(uiBlock *block, uiBut *but) uiButtonGroup *current_button_group = block->button_groups.last; /* We can't use the button directly because adding it to - * this list would mess with its prev and next pointers. */ + * this list would mess with its `prev` and `next` pointers. */ LinkData *button_link = BLI_genericNodeN(but); BLI_addtail(¤t_button_group->buttons, button_link); } diff --git a/source/blender/editors/interface/interface_context_menu.c b/source/blender/editors/interface/interface_context_menu.c index bb9e813ea50..72e7203c6ea 100644 --- a/source/blender/editors/interface/interface_context_menu.c +++ b/source/blender/editors/interface/interface_context_menu.c @@ -925,8 +925,19 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } } + { + const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); + uiButTreeRow *treerow_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, event->xy); + if (treerow_but) { + BLI_assert(treerow_but->but.type == UI_BTYPE_TREEROW); + UI_tree_view_item_context_menu_build( + C, treerow_but->tree_item, uiLayoutColumn(layout, false)); + uiItemS(layout); + } + } + /* If the button represents an id, it can set the "id" context pointer. */ - if (U.experimental.use_extended_asset_browser && ED_asset_can_mark_single_from_context(C)) { + if (ED_asset_can_mark_single_from_context(C)) { ID *id = CTX_data_pointer_get_type(C, "id", &RNA_ID).data; /* Gray out items depending on if data-block is an asset. Preferably this could be done via @@ -1201,11 +1212,10 @@ bool ui_popup_context_menu_for_button(bContext *C, uiBut *but, const wmEvent *ev } /* UI List item context menu. Scripts can add items to it, by default there's nothing shown. */ - ARegion *region = CTX_wm_region(C); + const ARegion *region = CTX_wm_menu(C) ? CTX_wm_menu(C) : CTX_wm_region(C); const bool is_inside_listbox = ui_list_find_mouse_over(region, event) != NULL; const bool is_inside_listrow = is_inside_listbox ? - ui_list_row_find_mouse_over(region, event->x, event->y) != - NULL : + ui_list_row_find_mouse_over(region, event->xy) != NULL : false; if (is_inside_listrow) { MenuType *mt = WM_menutype_find("UI_MT_list_item_context_menu", true); @@ -1247,6 +1257,9 @@ void ui_popup_context_menu_for_panel(bContext *C, ARegion *region, Panel *panel) if (panel->type->parent != NULL) { return; } + if (!UI_panel_can_be_pinned(panel)) { + return; + } PointerRNA ptr; RNA_pointer_create(&screen->id, &RNA_Panel, panel, &ptr); diff --git a/source/blender/editors/interface/interface_context_path.cc b/source/blender/editors/interface/interface_context_path.cc new file mode 100644 index 00000000000..b0f8d186afa --- /dev/null +++ b/source/blender/editors/interface/interface_context_path.cc @@ -0,0 +1,85 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edinterface + */ + +#include "BLI_vector.hh" + +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +#include "WM_api.h" + +namespace blender::ui { + +void context_path_add_generic(Vector &path, + StructRNA &rna_type, + void *ptr, + const BIFIconID icon_override) +{ + /* Add the null check here to make calling functions less verbose. */ + if (!ptr) { + return; + } + + PointerRNA rna_ptr; + RNA_pointer_create(nullptr, &rna_type, ptr, &rna_ptr); + char name[128]; + RNA_struct_name_get_alloc(&rna_ptr, name, sizeof(name), nullptr); + + /* Use a blank icon by default to check whether to retrieve it automatically from the type. */ + const BIFIconID icon = icon_override == ICON_NONE ? + static_cast(RNA_struct_ui_icon(rna_ptr.type)) : + icon_override; + + path.append({name, static_cast(icon)}); +} + +/* -------------------------------------------------------------------- */ +/** \name Breadcrumb Template + * \{ */ + +void template_breadcrumbs(uiLayout &layout, Span context_path) +{ + uiLayout *row = uiLayoutRow(&layout, true); + uiLayoutSetAlignment(&layout, UI_LAYOUT_ALIGN_LEFT); + + for (const int i : context_path.index_range()) { + uiLayout *sub_row = uiLayoutRow(row, true); + uiLayoutSetAlignment(sub_row, UI_LAYOUT_ALIGN_LEFT); + + if (i > 0) { + uiItemL(sub_row, "", ICON_RIGHTARROW_THIN); + } + uiItemL(sub_row, context_path[i].name.c_str(), context_path[i].icon); + } +} + +} // namespace blender::ui + +/** \} */ \ No newline at end of file diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index ebebf69bc11..e45a5fc61c6 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -178,35 +178,6 @@ void UI_draw_roundbox_4fv(const rctf *rect, bool filled, float rad, const float UI_draw_roundbox_4fv_ex(rect, (filled) ? col : NULL, NULL, 1.0f, col, U.pixelsize, rad); } -/* linear horizontal shade within button or in outline */ -/* view2d scrollers use it */ -void UI_draw_roundbox_shade_x( - const rctf *rect, bool filled, float rad, float shadetop, float shadedown, const float col[4]) -{ - float inner1[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float inner2[4] = {0.0f, 0.0f, 0.0f, 0.0f}; - float outline[4]; - - if (filled) { - inner1[0] = min_ff(1.0f, col[0] + shadetop); - inner1[1] = min_ff(1.0f, col[1] + shadetop); - inner1[2] = min_ff(1.0f, col[2] + shadetop); - inner1[3] = 1.0f; - inner2[0] = max_ff(0.0f, col[0] + shadedown); - inner2[1] = max_ff(0.0f, col[1] + shadedown); - inner2[2] = max_ff(0.0f, col[2] + shadedown); - inner2[3] = 1.0f; - } - - /* TODO: non-filled box don't have gradients. Just use middle color. */ - outline[0] = clamp_f(col[0] + shadetop + shadedown, 0.0f, 1.0f); - outline[1] = clamp_f(col[1] + shadetop + shadedown, 0.0f, 1.0f); - outline[2] = clamp_f(col[2] + shadetop + shadedown, 0.0f, 1.0f); - outline[3] = clamp_f(col[3] + shadetop + shadedown, 0.0f, 1.0f); - - UI_draw_roundbox_4fv_ex(rect, inner1, inner2, 1.0f, outline, U.pixelsize, rad); -} - void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]) { const int ofs_y = 4 * U.pixelsize; @@ -1860,13 +1831,13 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Also add the last points on the right and bottom edges to close off the fill polygon. */ const bool add_left_tri = profile->view_rect.xmin < 0.0f; const bool add_bottom_tri = profile->view_rect.ymin < 0.0f; - uint tot_points = (uint)PROF_TABLE_LEN(profile->path_len) + 1 + add_left_tri + add_bottom_tri; + int tot_points = BKE_curveprofile_table_size(profile) + 1 + add_left_tri + add_bottom_tri; const uint tot_triangles = tot_points - 2; /* Create array of the positions of the table's points. */ float(*table_coords)[2] = MEM_mallocN(sizeof(*table_coords) * tot_points, "table x coords"); - for (uint i = 0; i < (uint)PROF_TABLE_LEN(profile->path_len); - i++) { /* Only add the points from the table here. */ + for (uint i = 0; i < (uint)BKE_curveprofile_table_size(profile); i++) { + /* Only add the points from the table here. */ table_coords[i][0] = pts[i].x; table_coords[i][1] = pts[i].y; } @@ -1903,44 +1874,50 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, } /* Calculate the table point indices of the triangles for the profile's fill. */ - uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, "return tri indices"); - BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices); + if (tot_triangles > 0) { + uint(*tri_indices)[3] = MEM_mallocN(sizeof(*tri_indices) * tot_triangles, __func__); + BLI_polyfill_calc(table_coords, tot_points, -1, tri_indices); - /* Draw the triangles for the profile fill. */ - immUniformColor3ubvAlpha((const uchar *)wcol->item, 128); - GPU_blend(GPU_BLEND_ALPHA); - GPU_polygon_smooth(false); - immBegin(GPU_PRIM_TRIS, 3 * tot_triangles); - for (uint i = 0; i < tot_triangles; i++) { - for (uint j = 0; j < 3; j++) { - uint *tri = tri_indices[i]; - fx = rect->xmin + zoomx * (table_coords[tri[j]][0] - offsx); - fy = rect->ymin + zoomy * (table_coords[tri[j]][1] - offsy); - immVertex2f(pos, fx, fy); + /* Draw the triangles for the profile fill. */ + immUniformColor3ubvAlpha((const uchar *)wcol->item, 128); + GPU_blend(GPU_BLEND_ALPHA); + GPU_polygon_smooth(false); + immBegin(GPU_PRIM_TRIS, 3 * tot_triangles); + for (uint i = 0; i < tot_triangles; i++) { + for (uint j = 0; j < 3; j++) { + uint *tri = tri_indices[i]; + fx = rect->xmin + zoomx * (table_coords[tri[j]][0] - offsx); + fy = rect->ymin + zoomy * (table_coords[tri[j]][1] - offsy); + immVertex2f(pos, fx, fy); + } } + immEnd(); + MEM_freeN(tri_indices); } - immEnd(); - MEM_freeN(tri_indices); /* Draw the profile's path so the edge stands out a bit. */ tot_points -= (add_left_tri + add_left_tri); - GPU_line_width(1.0f); - immUniformColor3ubvAlpha((const uchar *)wcol->item, 255); - GPU_line_smooth(true); - immBegin(GPU_PRIM_LINE_STRIP, tot_points - 1); - for (uint i = 0; i < tot_points - 1; i++) { - fx = rect->xmin + zoomx * (table_coords[i][0] - offsx); - fy = rect->ymin + zoomy * (table_coords[i][1] - offsy); - immVertex2f(pos, fx, fy); + const int edges_len = tot_points - 1; + if (edges_len > 0) { + GPU_line_width(1.0f); + immUniformColor3ubvAlpha((const uchar *)wcol->item, 255); + GPU_line_smooth(true); + immBegin(GPU_PRIM_LINE_STRIP, tot_points); + for (int i = 0; i < tot_points; i++) { + fx = rect->xmin + zoomx * (table_coords[i][0] - offsx); + fy = rect->ymin + zoomy * (table_coords[i][1] - offsy); + immVertex2f(pos, fx, fy); + } + immEnd(); } - immEnd(); - MEM_freeN(table_coords); + + MEM_SAFE_FREE(table_coords); /* Draw the handles for the selected control points. */ pts = profile->path; - tot_points = (uint)profile->path_len; + const int path_len = tot_points = (uint)profile->path_len; int selected_free_points = 0; - for (uint i = 0; i < tot_points; i++) { + for (int i = 0; i < path_len; i++) { if (point_draw_handles(&pts[i])) { selected_free_points++; } @@ -1952,7 +1929,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, GPU_line_smooth(true); immBegin(GPU_PRIM_LINES, selected_free_points * 4); float ptx, pty; - for (uint i = 0; i < tot_points; i++) { + for (int i = 0; i < path_len; i++) { if (point_draw_handles(&pts[i])) { ptx = rect->xmin + zoomx * (pts[i].x - offsx); pty = rect->ymin + zoomy * (pts[i].y - offsy); @@ -1996,16 +1973,18 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Draw the control points. */ GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); - GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f))); - immBegin(GPU_PRIM_POINTS, tot_points); - for (uint i = 0; i < tot_points; i++) { - fx = rect->xmin + zoomx * (pts[i].x - offsx); - fy = rect->ymin + zoomy * (pts[i].y - offsy); - immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_vert_select : color_vert); - immVertex2f(pos, fx, fy); + if (path_len > 0) { + GPU_blend(GPU_BLEND_NONE); + GPU_point_size(max_ff(3.0f, min_ff(UI_DPI_FAC / but->block->aspect * 5.0f, 5.0f))); + immBegin(GPU_PRIM_POINTS, path_len); + for (int i = 0; i < path_len; i++) { + fx = rect->xmin + zoomx * (pts[i].x - offsx); + fy = rect->ymin + zoomy * (pts[i].y - offsy); + immAttr4fv(col, (pts[i].flag & PROF_SELECT) ? color_vert_select : color_vert); + immVertex2f(pos, fx, fy); + } + immEnd(); } - immEnd(); /* Draw the handle points. */ if (selected_free_points > 0) { @@ -2013,7 +1992,7 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, GPU_blend(GPU_BLEND_NONE); GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 4.0f, 4.0f))); immBegin(GPU_PRIM_POINTS, selected_free_points * 2); - for (uint i = 0; i < tot_points; i++) { + for (int i = 0; i < path_len; i++) { if (point_draw_handles(&pts[i])) { fx = rect->xmin + zoomx * (pts[i].h1_loc[0] - offsx); fy = rect->ymin + zoomy * (pts[i].h1_loc[1] - offsy); @@ -2031,11 +2010,11 @@ void ui_draw_but_CURVEPROFILE(ARegion *region, /* Draw the sampled points in addition to the control points if they have been created */ pts = profile->segments; - tot_points = (uint)profile->segments_len; - if (tot_points > 0 && pts) { + const int segments_len = (uint)profile->segments_len; + if (segments_len > 0 && pts) { GPU_point_size(max_ff(2.0f, min_ff(UI_DPI_FAC / but->block->aspect * 3.0f, 3.0f))); - immBegin(GPU_PRIM_POINTS, tot_points); - for (uint i = 0; i < tot_points; i++) { + immBegin(GPU_PRIM_POINTS, segments_len); + for (int i = 0; i < segments_len; i++) { fx = rect->xmin + zoomx * (pts[i].x - offsx); fy = rect->ymin + zoomy * (pts[i].y - offsy); immAttr4fv(col, color_sample); diff --git a/source/blender/editors/interface/interface_dropboxes.cc b/source/blender/editors/interface/interface_dropboxes.cc new file mode 100644 index 00000000000..81a1354cbe7 --- /dev/null +++ b/source/blender/editors/interface/interface_dropboxes.cc @@ -0,0 +1,95 @@ +/* + * 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 edinterface + */ + +#include "BKE_context.h" + +#include "DNA_space_types.h" + +#include "MEM_guardedalloc.h" + +#include "RNA_access.h" + +#include "WM_api.h" + +#include "UI_interface.h" + +static bool ui_tree_view_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, + event->xy); + if (!hovered_tree_item) { + return false; + } + + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } + + drag->free_disabled_info = false; + return UI_tree_view_item_can_drop(hovered_tree_item, drag, &drag->disabled_info); +} + +static char *ui_tree_view_drop_tooltip(bContext *C, + wmDrag *drag, + const int xy[2], + wmDropBox *UNUSED(drop)) +{ + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, xy); + if (!hovered_tree_item) { + return nullptr; + } + + return UI_tree_view_item_drop_tooltip(hovered_tree_item, drag); +} + +/* ---------------------------------------------------------------------- */ + +static bool ui_drop_name_poll(struct bContext *C, wmDrag *drag, const wmEvent *UNUSED(event)) +{ + return UI_but_active_drop_name(C) && (drag->type == WM_DRAG_ID); +} + +static void ui_drop_name_copy(wmDrag *drag, wmDropBox *drop) +{ + const ID *id = WM_drag_get_local_ID(drag, 0); + RNA_string_set(drop->ptr, "string", id->name + 2); +} + +/* ---------------------------------------------------------------------- */ + +void ED_dropboxes_ui() +{ + ListBase *lb = WM_dropboxmap_find("User Interface", SPACE_EMPTY, 0); + + WM_dropbox_add(lb, + "UI_OT_tree_view_drop", + ui_tree_view_drop_poll, + nullptr, + nullptr, + ui_tree_view_drop_tooltip); + WM_dropbox_add(lb, + "UI_OT_drop_name", + ui_drop_name_poll, + ui_drop_name_copy, + WM_drag_free_imported_drag_ID, + nullptr); +} diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 2e7b0ce532c..395ecc77ef4 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -24,6 +24,8 @@ #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "BLI_math_color.h" + #include "BKE_context.h" #include "BKE_screen.h" @@ -107,8 +109,13 @@ static void eyedropper_draw_cursor_text_ex(const int x, const int y, const char { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + /* Use the theme settings from tooltips. */ + const bTheme *btheme = UI_GetTheme(); + const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip; + + float col_fg[4], col_bg[4]; + rgba_uchar_to_float(col_fg, wcol->text); + rgba_uchar_to_float(col_bg, wcol->inner); UI_fontstyle_draw_simple_backdrop(fstyle, x, y + U.widget_unit, name, col_fg, col_bg); } @@ -119,8 +126,8 @@ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const cha return; } - const int x = window->eventstate->x; - const int y = window->eventstate->y; + const int x = window->eventstate->xy[0]; + const int y = window->eventstate->xy[1]; eyedropper_draw_cursor_text_ex(x, y, name); } @@ -146,8 +153,8 @@ void eyedropper_draw_cursor_text_region(const int x, const int y, const char *na uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event) { bScreen *screen = CTX_wm_screen(C); - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); - const ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, event->x, event->y); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); + const ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, event->xy[0], event->xy[1]); uiBut *but = ui_but_find_mouse_over(region, event); diff --git a/source/blender/editors/interface/interface_eyedropper_color.c b/source/blender/editors/interface/interface_eyedropper_color.c index 9d06fb2b27a..c3633e11f83 100644 --- a/source/blender/editors/interface/interface_eyedropper_color.c +++ b/source/blender/editors/interface/interface_eyedropper_color.c @@ -481,7 +481,7 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = eye->is_undo; if (eye->accum_tot == 0) { - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); } eyedropper_exit(C, op); /* Could support finished & undo-skip. */ @@ -490,23 +490,23 @@ static int eyedropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->accum_start = true; - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); break; case EYE_MODAL_SAMPLE_RESET: eye->accum_tot = 0; zero_v3(eye->accum_col); - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); break; } } else if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { if (eye->accum_start) { /* button is pressed so keep sampling */ - eyedropper_color_sample(C, eye, event->x, event->y); + eyedropper_color_sample(C, eye, event->xy[0], event->xy[1]); } if (eye->draw_handle_sample_text) { - eyedropper_color_sample_text_update(C, eye, event->x, event->y); + eyedropper_color_sample_text_update(C, eye, event->xy[0], event->xy[1]); ED_region_tag_redraw(CTX_wm_region(C)); } } diff --git a/source/blender/editors/interface/interface_eyedropper_colorband.c b/source/blender/editors/interface/interface_eyedropper_colorband.c index d32eb415b19..22320282766 100644 --- a/source/blender/editors/interface/interface_eyedropper_colorband.c +++ b/source/blender/editors/interface/interface_eyedropper_colorband.c @@ -233,7 +233,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_CANCELLED; case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = eye->is_undo; - eyedropper_colorband_sample_segment(C, eye, event->x, event->y); + eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); eyedropper_colorband_exit(C, op); /* Could support finished & undo-skip. */ @@ -242,10 +242,10 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ eye->sample_start = true; - eyedropper_colorband_sample_point(C, eye, event->x, event->y); + eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); - eye->last_x = event->x; - eye->last_y = event->y; + eye->last_x = event->xy[0]; + eye->last_y = event->xy[1]; break; case EYE_MODAL_SAMPLE_RESET: break; @@ -253,7 +253,7 @@ static int eyedropper_colorband_modal(bContext *C, wmOperator *op, const wmEvent } else if (event->type == MOUSEMOVE) { if (eye->sample_start) { - eyedropper_colorband_sample_segment(C, eye, event->x, event->y); + eyedropper_colorband_sample_segment(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); } } @@ -280,7 +280,7 @@ static int eyedropper_colorband_point_modal(bContext *C, wmOperator *op, const w } break; case EYE_MODAL_POINT_SAMPLE: - eyedropper_colorband_sample_point(C, eye, event->x, event->y); + eyedropper_colorband_sample_point(C, eye, event->xy[0], event->xy[1]); eyedropper_colorband_apply(C, op); if (eye->color_buffer_len == MAXCOLORBAND) { eyedropper_colorband_exit(C, op); diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index 4996c803dfe..261aa997d06 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -292,7 +292,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) return OPERATOR_CANCELLED; case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = ddr->is_undo; - const bool success = datadropper_id_sample(C, ddr, event->x, event->y); + const bool success = datadropper_id_sample(C, ddr, event->xy[0], event->xy[1]); datadropper_exit(C, op); if (success) { /* Could support finished & undo-skip. */ @@ -309,7 +309,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) wmWindow *win; ScrArea *area; - int mval[] = {event->x, event->y}; + int mval[] = {event->xy[0], event->xy[1]}; datadropper_win_area_find(C, mval, mval, &win, &area); /* Set the region for eyedropper cursor text drawing */ diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c index 311f718d950..4172c474f4a 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/interface_eyedropper_depth.c @@ -276,7 +276,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_CONFIRM: { const bool is_undo = ddr->is_undo; if (ddr->accum_tot == 0) { - depthdropper_depth_sample(C, ddr, event->x, event->y); + depthdropper_depth_sample(C, ddr, event->xy[0], event->xy[1]); } else { depthdropper_depth_set_accum(C, ddr); @@ -288,12 +288,12 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) case EYE_MODAL_SAMPLE_BEGIN: /* enable accum and make first sample */ ddr->accum_start = true; - depthdropper_depth_sample_accum(C, ddr, event->x, event->y); + depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); break; case EYE_MODAL_SAMPLE_RESET: ddr->accum_tot = 0; ddr->accum_depth = 0.0f; - depthdropper_depth_sample_accum(C, ddr, event->x, event->y); + depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); depthdropper_depth_set_accum(C, ddr); break; } @@ -301,7 +301,7 @@ static int depthdropper_modal(bContext *C, wmOperator *op, const wmEvent *event) else if (event->type == MOUSEMOVE) { if (ddr->accum_start) { /* button is pressed so keep sampling */ - depthdropper_depth_sample_accum(C, ddr, event->x, event->y); + depthdropper_depth_sample_accum(C, ddr, event->xy[0], event->xy[1]); depthdropper_depth_set_accum(C, ddr); } } diff --git a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c index 417807afff1..d76ff84bcad 100644 --- a/source/blender/editors/interface/interface_eyedropper_gpencil_color.c +++ b/source/blender/editors/interface/interface_eyedropper_gpencil_color.c @@ -292,7 +292,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_CANCELLED; } case EYE_MODAL_SAMPLE_CONFIRM: { - eyedropper_gpencil_color_sample(C, eye, event->x, event->y); + eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]); /* Create material. */ eyedropper_gpencil_color_set(C, event, eye); @@ -309,7 +309,7 @@ static int eyedropper_gpencil_modal(bContext *C, wmOperator *op, const wmEvent * } case MOUSEMOVE: case INBETWEEN_MOUSEMOVE: { - eyedropper_gpencil_color_sample(C, eye, event->x, event->y); + eyedropper_gpencil_color_sample(C, eye, event->xy[0], event->xy[1]); break; } default: { diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 77ae16d7cc7..51ebe5399b3 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -358,7 +358,7 @@ typedef struct uiHandleButtonMulti { * here so we can tell if this is a vertical motion or not. */ float drag_dir[2]; - /* values copied direct from event->x,y + /* values copied direct from event->xy * used to detect buttons between the current and initial mouse position */ int drag_start[2]; @@ -384,6 +384,8 @@ typedef struct uiHandleButtonData { /* booleans (could be made into flags) */ bool cancel, escapecancel; bool applied, applied_interactive; + /* Button is being applied through an extra icon. */ + bool apply_through_extra_icon; bool changed_cursor; wmTimer *flashtimer; @@ -393,9 +395,6 @@ typedef struct uiHandleButtonData { char *origstr; double value, origvalue, startvalue; float vec[3], origvec[3]; -#if 0 /* UNUSED */ - int togdual, togonly; -#endif ColorBand *coba; /* Tool-tip. */ @@ -1164,6 +1163,16 @@ static void ui_apply_but_ROW(bContext *C, uiBlock *block, uiBut *but, uiHandleBu data->applied = true; } +static void ui_apply_but_TREEROW(bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data) +{ + if (data->apply_through_extra_icon) { + /* Don't apply this, it would cause unintended tree-row toggling when clicking on extra icons. + */ + return; + } + ui_apply_but_ROW(C, block, but, data); +} + /** * \note Ownership of \a properties is moved here. The #uiAfterFunc owns it now. * @@ -1414,8 +1423,8 @@ static bool ui_multibut_states_tag(uiBut *but_active, seg[0][0] = data->multi_data.drag_start[0]; seg[0][1] = data->multi_data.drag_start[1]; - seg[1][0] = event->x; - seg[1][1] = event->y; + seg[1][0] = event->xy[0]; + seg[1][1] = event->xy[1]; BLI_assert(data->multi_data.init == BUTTON_MULTI_INIT_SETUP); @@ -1672,7 +1681,7 @@ static void ui_drag_toggle_set(bContext *C, uiDragToggleHandle *drag_info, const */ if (drag_info->is_xy_lock_init == false) { /* first store the buttons original coords */ - uiBut *but = ui_but_find_mouse_over_ex(region, xy_input[0], xy_input[1], true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, xy_input, true, NULL, NULL); if (but) { if (but->flag & UI_BUT_DRAG_LOCK) { @@ -1734,7 +1743,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void break; } case MOUSEMOVE: { - ui_drag_toggle_set(C, drag_info, &event->x); + ui_drag_toggle_set(C, drag_info, event->xy); break; } } @@ -1742,8 +1751,7 @@ static int ui_handler_region_drag_toggle(bContext *C, const wmEvent *event, void if (done) { wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - uiBut *but = ui_but_find_mouse_over_ex( - region, drag_info->xy_init[0], drag_info->xy_init[1], true, NULL, NULL); + uiBut *but = ui_but_find_mouse_over_ex(region, drag_info->xy_init, true, NULL, NULL); if (but) { ui_apply_but_undo(but); @@ -2052,7 +2060,8 @@ static bool ui_but_drag_init(bContext *C, WM_event_drag_threshold(event), (int)((UI_UNIT_Y / 2) * ui_block_to_window_scale(data->region, but->block))); - if (abs(data->dragstartx - event->x) + abs(data->dragstarty - event->y) > drag_threshold) { + if (abs(data->dragstartx - event->xy[0]) + abs(data->dragstarty - event->xy[1]) > + drag_threshold) { button_activate_state(C, but, BUTTON_STATE_EXIT); data->cancel = true; #ifdef USE_DRAG_TOGGLE @@ -2067,8 +2076,8 @@ static bool ui_but_drag_init(bContext *C, drag_info->pushed_state = ui_drag_toggle_but_pushed_state(but); drag_info->but_cent_start[0] = BLI_rctf_cent_x(&but->rect); drag_info->but_cent_start[1] = BLI_rctf_cent_y(&but->rect); - copy_v2_v2_int(drag_info->xy_init, &event->x); - copy_v2_v2_int(drag_info->xy_last, &event->x); + copy_v2_v2_int(drag_info->xy_init, event->xy); + copy_v2_v2_int(drag_info->xy_last, event->xy); /* needed for toggle drag on popups */ region_prev = CTX_wm_region(C); @@ -2136,6 +2145,12 @@ static bool ui_but_drag_init(bContext *C, return false; } } + else if (but->type == UI_BTYPE_TREEROW) { + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + if (tree_row_but->tree_item) { + UI_tree_view_item_drag_start(C, tree_row_but->tree_item); + } + } else { wmDrag *drag = WM_event_start_drag( C, @@ -2154,6 +2169,12 @@ static bool ui_but_drag_init(bContext *C, BLI_rctf_size_x(&but->rect), BLI_rctf_size_y(&but->rect)); } + + /* Special feature for assets: We add another drag item that supports multiple assets. It + * gets the assets from context. */ + if (ELEM(but->dragtype, WM_DRAG_ASSET, WM_DRAG_ID)) { + WM_event_start_drag(C, ICON_NONE, WM_DRAG_ASSET_LIST, NULL, 0, WM_DRAG_NOP); + } } return true; } @@ -2307,6 +2328,9 @@ static void ui_apply_but( case UI_BTYPE_ROW: ui_apply_but_ROW(C, block, but, data); break; + case UI_BTYPE_TREEROW: + ui_apply_but_TREEROW(C, block, but, data); + break; case UI_BTYPE_LISTROW: ui_apply_but_LISTROW(C, block, but, data); break; @@ -2418,39 +2442,6 @@ static void ui_apply_but( /** \} */ -/* -------------------------------------------------------------------- */ -/** \name Button Drop Event - * \{ */ - -/* only call if event type is EVT_DROP */ -static void ui_but_drop(bContext *C, const wmEvent *event, uiBut *but, uiHandleButtonData *data) -{ - ListBase *drags = event->customdata; /* drop event type has listbase customdata by default */ - - LISTBASE_FOREACH (wmDrag *, wmd, drags) { - /* TODO: asset dropping. */ - if (wmd->type == WM_DRAG_ID) { - /* align these types with UI_but_active_drop_name */ - if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - ID *id = WM_drag_get_local_ID(wmd, 0); - - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - - ui_textedit_string_set(but, data, id->name + 2); - - if (ELEM(but->type, UI_BTYPE_SEARCH_MENU)) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); - } - } - } -} - -/** \} */ - /* -------------------------------------------------------------------- */ /** \name Button Copy & Paste * \{ */ @@ -2648,15 +2639,9 @@ static void ui_but_copy_text(uiBut *but, char *output, int output_len_max) static void ui_but_paste_text(bContext *C, uiBut *but, uiHandleButtonData *data, char *buf_paste) { - button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); - ui_textedit_string_set(but, but->active, buf_paste); - - if (but->type == UI_BTYPE_SEARCH_MENU) { - but->changed = true; - ui_searchbox_update(C, data->searchbox, but, true); - } - - button_activate_state(C, but, BUTTON_STATE_EXIT); + BLI_assert(but->active == data); + UNUSED_VARS_NDEBUG(data); + ui_but_set_string_interactive(C, but, buf_paste); } static void ui_but_copy_colorband(uiBut *but) @@ -3000,6 +2985,24 @@ void ui_but_text_password_hide(char password_str[UI_MAX_PASSWORD_STR], /** \name Button Text Selection/Editing * \{ */ +/** + * Use handling code to set a string for the button. Handles the case where the string is set for a + * search button while the search menu is open, so the results are updated accordingly. + * This is basically the same as pasting the string into the button. + */ +void ui_but_set_string_interactive(bContext *C, uiBut *but, const char *value) +{ + button_activate_state(C, but, BUTTON_STATE_TEXT_EDITING); + ui_textedit_string_set(but, but->active, value); + + if (but->type == UI_BTYPE_SEARCH_MENU && but->active) { + but->changed = true; + ui_searchbox_update(C, but->active->searchbox, but, true); + } + + button_activate_state(C, but, BUTTON_STATE_EXIT); +} + void ui_but_active_string_clear_and_exit(bContext *C, uiBut *but) { if (!but->active) { @@ -3400,9 +3403,9 @@ static void ui_textedit_ime_begin(wmWindow *win, uiBut *UNUSED(but)) BLI_assert(win->ime_data == NULL); /* enable IME and position to cursor, it's a trick */ - x = win->eventstate->x; + x = win->eventstate->xy[0]; /* flip y and move down a bit, prevent the IME panel cover the edit button */ - y = win->eventstate->y - 12; + y = win->eventstate->xy[1] - 12; wm_window_IME_begin(win, x, y, 0, 0, true); } @@ -3716,18 +3719,18 @@ static void ui_do_but_textedit( /* exit on LMB only on RELEASE for searchbox, to mimic other popups, * and allow multiple menu levels */ if (data->searchbox) { - inbox = ui_searchbox_inside(data->searchbox, event->x, event->y); + inbox = ui_searchbox_inside(data->searchbox, event->xy); } /* for double click: we do a press again for when you first click on button * (selects all text, no cursor pos) */ if (ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) { - float mx = event->x; - float my = event->y; + float mx = event->xy[0]; + float my = event->xy[1]; ui_window_to_block_fl(data->region, block, &mx, &my); if (ui_but_contains_pt(but, mx, my)) { - ui_textedit_set_cursor_pos(but, data, event->x); + ui_textedit_set_cursor_pos(but, data, event->xy[0]); but->selsta = but->selend = but->pos; data->sel_pos_init = but->pos; @@ -3998,11 +4001,11 @@ static void ui_do_but_textedit_select( switch (event->type) { case MOUSEMOVE: { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); - ui_textedit_set_cursor_select(but, data, event->x); + ui_textedit_set_cursor_select(but, data, event->xy[0]); retval = WM_UI_HANDLER_BREAK; break; } @@ -4194,6 +4197,8 @@ static void ui_numedit_apply(bContext *C, uiBlock *block, uiBut *but, uiHandleBu static void ui_but_extra_operator_icon_apply(bContext *C, uiBut *but, uiButExtraOpIcon *op_icon) { + but->active->apply_through_extra_icon = true; + if (but->active->interactive) { ui_apply_but(C, but->block, but, but->active, true); } @@ -4336,7 +4341,7 @@ static uiBut *ui_but_list_row_text_activate(bContext *C, uiButtonActivateType activate_type) { ARegion *region = CTX_wm_region(C); - uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->x, event->y, true, NULL, NULL); + uiBut *labelbut = ui_but_find_mouse_over_ex(region, event->xy, true, NULL, NULL); if (labelbut && labelbut->type == UI_BTYPE_TEXT && !(labelbut->flag & UI_BUT_DISABLED)) { /* exit listrow */ @@ -4363,7 +4368,7 @@ static uiButExtraOpIcon *ui_but_extra_operator_icon_mouse_over_get(uiBut *but, { float xmax = but->rect.xmax; const float icon_size = 0.8f * BLI_rctf_size_y(&but->rect); /* ICON_SIZE_FROM_BUTRECT */ - int x = event->x, y = event->y; + int x = event->xy[0], y = event->xy[1]; ui_window_to_block(data->region, but->block, &x, &y); if (!BLI_rctf_isect_pt(&but->rect, x, y)) { @@ -4447,14 +4452,10 @@ static bool ui_do_but_ANY_drag_toggle( { if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->type == LEFTMOUSE && event->val == KM_PRESS && ui_but_is_drag_toggle(but)) { -# if 0 /* UNUSED */ - data->togdual = event->ctrl; - data->togonly = !event->shift; -# endif ui_apply_but(C, but->block, but, data, true); button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; *r_retval = WM_UI_HANDLER_BREAK; return true; } @@ -4540,7 +4541,7 @@ static int ui_do_but_HOTKEYEVT(bContext *C, if (event->type == LEFTMOUSE && event->val == KM_PRESS) { /* only cancel if click outside the button */ - if (ui_but_contains_point_px(but, but->active->region, event->x, event->y) == false) { + if (ui_but_contains_point_px(but, but->active->region, event->xy) == false) { /* data->cancel doesn't work, this button opens immediate */ if (but->flag & UI_BUT_IMMEDIATE) { ui_but_value_set(but, 0); @@ -4737,17 +4738,13 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons /* Behave like other menu items. */ do_activate = (event->val == KM_RELEASE); } - else { + else if (!ui_do_but_extra_operator_icon(C, but, data, event)) { /* Also use double-clicks to prevent fast clicks to leak to other handlers (T76481). */ do_activate = ELEM(event->val, KM_PRESS, KM_DBL_CLICK); } } if (do_activate) { -#if 0 /* UNUSED */ - data->togdual = event->ctrl; - data->togonly = !event->shift; -#endif button_activate_state(C, but, BUTTON_STATE_EXIT); return WM_UI_HANDLER_BREAK; } @@ -4799,6 +4796,47 @@ static int ui_do_but_TOG(bContext *C, uiBut *but, uiHandleButtonData *data, cons return WM_UI_HANDLER_CONTINUE; } +static int ui_do_but_TREEROW(bContext *C, + uiBut *but, + uiHandleButtonData *data, + const wmEvent *event) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + BLI_assert(tree_row_but->but.type == UI_BTYPE_TREEROW); + + if (data->state == BUTTON_STATE_HIGHLIGHT) { + if (event->type == LEFTMOUSE) { + switch (event->val) { + case KM_PRESS: + /* Extra icons have priority, don't mess with them. */ + if (ui_but_extra_operator_icon_mouse_over_get(but, data, event)) { + return WM_UI_HANDLER_BREAK; + } + button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; + return WM_UI_HANDLER_CONTINUE; + + case KM_CLICK: + button_activate_state(C, but, BUTTON_STATE_EXIT); + return WM_UI_HANDLER_BREAK; + + case KM_DBL_CLICK: + data->cancel = true; + UI_tree_view_item_begin_rename(tree_row_but->tree_item); + ED_region_tag_redraw(CTX_wm_region(C)); + return WM_UI_HANDLER_BREAK; + } + } + } + else if (data->state == BUTTON_STATE_WAIT_DRAG) { + /* Let "default" button handling take care of the drag logic. */ + return ui_do_but_EXIT(C, but, data, event); + } + + return WM_UI_HANDLER_CONTINUE; +} + static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -4810,16 +4848,16 @@ static int ui_do_but_EXIT(bContext *C, uiBut *but, uiHandleButtonData *data, con /* tell the button to wait and keep checking further events to * see if it should start dragging */ button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_CONTINUE; } } #ifdef USE_DRAG_TOGGLE if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && ui_but_is_drag_toggle(but)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_CONTINUE; } #endif @@ -5241,8 +5279,8 @@ static void ui_numedit_set_active(uiBut *but) BLI_rctf_size_y(&but->rect) * 0.7f); /* we can click on the side arrows to increment/decrement, * or click inside to edit the value directly */ - int mx = data->window->eventstate->x; - int my = data->window->eventstate->y; + int mx = data->window->eventstate->xy[0]; + int my = data->window->eventstate->xy[1]; ui_window_to_block(data->region, but->block, &mx, &my); if (mx < (but->rect.xmin + handle_width)) { @@ -5282,10 +5320,10 @@ static int ui_do_but_NUM( int retval = WM_UI_HANDLER_CONTINUE; /* mouse location scaled to fit the UI */ - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; /* mouse location kept at screen pixel coords */ - const int screen_mx = event->x; + const int screen_mx = event->xy[0]; BLI_assert(but->type == UI_BTYPE_NUM); @@ -5337,7 +5375,7 @@ static int ui_do_but_NUM( } #ifdef USE_DRAG_MULTINUM - copy_v2_v2_int(data->multi_data.drag_start, &event->x); + copy_v2_v2_int(data->multi_data.drag_start, event->xy); #endif } } @@ -5631,8 +5669,8 @@ static int ui_do_but_SLI( int click = 0; int retval = WM_UI_HANDLER_CONTINUE; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5691,7 +5729,7 @@ static int ui_do_but_SLI( } } #ifdef USE_DRAG_MULTINUM - copy_v2_v2_int(data->multi_data.drag_start, &event->x); + copy_v2_v2_int(data->multi_data.drag_start, event->xy); #endif } else if (data->state == BUTTON_STATE_NUM_EDITING) { @@ -5851,8 +5889,8 @@ static int ui_do_but_SCROLL( int retval = WM_UI_HANDLER_CONTINUE; const bool horizontal = (BLI_rctf_size_x(&but->rect) > BLI_rctf_size_y(&but->rect)); - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -5908,15 +5946,15 @@ static int ui_do_but_GRIP( * See T37739. */ - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { if (event->val == KM_PRESS) { if (event->type == LEFTMOUSE) { - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); retval = WM_UI_HANDLER_BREAK; } @@ -5979,16 +6017,16 @@ static int ui_do_but_BLOCK(bContext *C, uiBut *but, uiHandleButtonData *data, co if (event->type == LEFTMOUSE && but->dragpoin && event->val == KM_PRESS) { if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } } #ifdef USE_DRAG_TOGGLE if (event->type == LEFTMOUSE && event->val == KM_PRESS && (ui_but_is_drag_toggle(but))) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } #endif @@ -6165,8 +6203,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co ui_palette_set_active(color_but); if (ui_but_contains_point_px_icon(but, data->region, event)) { button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } } @@ -6174,8 +6212,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co if (event->type == LEFTMOUSE && event->val == KM_PRESS) { ui_palette_set_active(color_but); button_activate_state(C, but, BUTTON_STATE_WAIT_DRAG); - data->dragstartx = event->x; - data->dragstarty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; return WM_UI_HANDLER_BREAK; } #endif @@ -6201,7 +6239,7 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co hsv[2] = clamp_f(hsv[2] + 0.05f, 0.0f, 1.0f); } else { - const float fac = 0.005 * (event->y - event->prevy); + const float fac = 0.005 * (event->xy[1] - event->prev_xy[1]); hsv[2] = clamp_f(hsv[2] + fac, 0.0f, 1.0f); } @@ -6305,8 +6343,8 @@ static int ui_do_but_COLOR(bContext *C, uiBut *but, uiHandleButtonData *data, co static int ui_do_but_UNITVEC( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6599,8 +6637,8 @@ static int ui_do_but_HSVCUBE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { uiButHSVCube *hsv_but = (uiButHSVCube *)but; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -6875,8 +6913,8 @@ static int ui_do_but_HSVCIRCLE( { ColorPicker *cpicker = but->custom_data; float *hsv = cpicker->hsv_perceptual; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7004,8 +7042,8 @@ static bool ui_numedit_but_COLORBAND(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_COLORBAND( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7200,8 +7238,8 @@ static int ui_do_but_CURVE( Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7293,10 +7331,10 @@ static int ui_do_but_CURVE( data->dragsel = sel; - data->dragstartx = event->x; - data->dragstarty = event->y; - data->draglastx = event->x; - data->draglasty = event->y; + data->dragstartx = event->xy[0]; + data->dragstarty = event->xy[1]; + data->draglastx = event->xy[0]; + data->draglasty = event->xy[1]; button_activate_state(C, but, BUTTON_STATE_NUM_EDITING); return WM_UI_HANDLER_BREAK; @@ -7304,10 +7342,15 @@ static int ui_do_but_CURVE( } else if (data->state == BUTTON_STATE_NUM_EDITING) { if (event->type == MOUSEMOVE) { - if (event->x != data->draglastx || event->y != data->draglasty) { + if (event->xy[0] != data->draglastx || event->xy[1] != data->draglasty) { - if (ui_numedit_but_CURVE( - block, but, data, event->x, event->y, event->ctrl != 0, event->shift != 0)) { + if (ui_numedit_but_CURVE(block, + but, + data, + event->xy[0], + event->xy[1], + event->ctrl != 0, + event->shift != 0)) { ui_numedit_apply(C, block, but, data); } } @@ -7481,8 +7524,8 @@ static int ui_do_but_CURVEPROFILE( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { CurveProfile *profile = (CurveProfile *)but->poin; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); @@ -7564,7 +7607,7 @@ static int ui_do_but_CURVEPROFILE( dist_min_sq = square_f(U.dpi_fac * 8.0f); /* 8 pixel radius from each table point. */ /* Loop through the path's high resolution table and find what's near the click. */ - for (int i = 1; i <= PROF_TABLE_LEN(profile->path_len); i++) { + for (int i = 1; i <= BKE_curveprofile_table_size(profile); i++) { copy_v2_v2(f_xy_prev, f_xy); BLI_rctf_transform_pt_v(&but->rect, &profile->view_rect, f_xy, &table[i].x); @@ -7664,8 +7707,8 @@ static bool ui_numedit_but_HISTOGRAM(uiBut *but, uiHandleButtonData *data, int m static int ui_do_but_HISTOGRAM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7737,8 +7780,8 @@ static bool ui_numedit_but_WAVEFORM(uiBut *but, uiHandleButtonData *data, int mx static int ui_do_but_WAVEFORM( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7828,8 +7871,8 @@ static bool ui_numedit_but_TRACKPREVIEW( static int ui_do_but_TRACKPREVIEW( bContext *C, uiBlock *block, uiBut *but, uiHandleButtonData *data, const wmEvent *event) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(data->region, block, &mx, &my); if (data->state == BUTTON_STATE_HIGHLIGHT) { @@ -7885,7 +7928,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* Only hard-coded stuff here, button interactions with configurable * keymaps are handled using operators (see #ED_keymap_ui). */ - if ((data->state == BUTTON_STATE_HIGHLIGHT) || (event->type == EVT_DROP)) { + if (data->state == BUTTON_STATE_HIGHLIGHT) { /* handle copy and paste */ bool is_press_ctrl_but_no_shift = event->val == KM_PRESS && IS_EVENT_MOD(event, ctrl, oskey) && @@ -7911,6 +7954,14 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* handle menu */ if ((event->type == RIGHTMOUSE) && !IS_EVENT_MOD(event, shift, ctrl, alt, oskey) && (event->val == KM_PRESS)) { + /* For some button types that are typically representing entire sets of data, right-clicking + * to spawn the context menu should also activate the item. This makes it clear which item + * will be operated on. + * Apply the button immediately, so context menu polls get the right active item. */ + if (ELEM(but->type, UI_BTYPE_TREEROW)) { + ui_apply_but(C, but->block, but, but->active, true); + } + /* RMB has two options now */ if (ui_popup_context_menu_for_button(C, but, event)) { return WM_UI_HANDLER_BREAK; @@ -7926,11 +7977,6 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * return WM_UI_HANDLER_BREAK; } - /* handle drop */ - if (event->type == EVT_DROP) { - ui_but_drop(C, event, but, data); - } - if ((data->state == BUTTON_STATE_HIGHLIGHT) && ELEM(event->type, LEFTMOUSE, EVT_BUT_OPEN, EVT_PADENTER, EVT_RETKEY) && (event->val == KM_RELEASE) && @@ -7969,6 +8015,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * case UI_BTYPE_DATASETROW: retval = ui_do_but_TOG(C, but, data, event); break; + case UI_BTYPE_TREEROW: + retval = ui_do_but_TREEROW(C, but, data, event); + break; case UI_BTYPE_SCROLL: retval = ui_do_but_SCROLL(C, block, but, data, event); break; @@ -8079,7 +8128,7 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * if (fabsf(dot_v2v2(dir_nor_drag, dir_nor_y)) > DRAG_MULTINUM_THRESHOLD_VERTICAL) { data->multi_data.init = BUTTON_MULTI_INIT_SETUP; - data->multi_data.drag_lock_x = event->x; + data->multi_data.drag_lock_x = event->xy[0]; } else { data->multi_data.init = BUTTON_MULTI_INIT_DISABLE; @@ -8092,9 +8141,9 @@ static int ui_do_button(bContext *C, uiBlock *block, uiBut *but, const wmEvent * /* Check if we're don't setting buttons. */ if ((data->str && ELEM(data->state, BUTTON_STATE_TEXT_EDITING, BUTTON_STATE_NUM_EDITING)) || - ((abs(data->multi_data.drag_lock_x - event->x) > margin_x) && + ((abs(data->multi_data.drag_lock_x - event->xy[0]) > margin_x) && /* Just to be sure, check we're dragging more horizontally then vertically. */ - abs(event->prevx - event->x) > abs(event->prevy - event->y))) { + abs(event->prev_xy[0] - event->xy[0]) > abs(event->prev_xy[1] - event->xy[1]))) { if (data->multi_data.has_mbuts) { ui_multibut_states_create(but, data); data->multi_data.init = BUTTON_MULTI_INIT_ENABLE; @@ -8182,7 +8231,11 @@ static ARegion *ui_but_tooltip_init( uiBut *but = UI_region_active_but_get(region); *r_exit_on_event = false; if (but) { - return UI_tooltip_create_from_button(C, region, but, is_label); + const wmWindow *win = CTX_wm_window(C); + uiButExtraOpIcon *extra_icon = ui_but_extra_operator_icon_mouse_over_get( + but, but->active, win->eventstate); + + return UI_tooltip_create_from_button_or_extra_icon(C, region, but, extra_icon, is_label); } return NULL; } @@ -8742,7 +8795,7 @@ uiBlock *UI_region_block_find_mouse_over(const struct ARegion *region, const int xy[2], bool only_clip) { - return ui_block_find_mouse_over_ex(region, xy[0], xy[1], only_clip); + return ui_block_find_mouse_over_ex(region, xy, only_clip); } /** @@ -8964,7 +9017,7 @@ void ui_but_activate_event(bContext *C, ARegion *region, uiBut *but) event.val = KM_PRESS; event.is_repeat = false; event.customdata = but; - event.customdatafree = false; + event.customdata_free = false; ui_do_button(C, but->block, but, &event); } @@ -9141,7 +9194,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) /* always deactivate button for pie menus, * else moving to blank space will leave activated */ if ((!ui_block_is_menu(block) || ui_block_is_pie_menu(block)) && - !ui_but_contains_point_px(but, region, event->x, event->y)) { + !ui_but_contains_point_px(but, region, event->xy)) { exit = true; } else if (but_other && ui_but_is_editable(but_other) && (but_other != but)) { @@ -9152,7 +9205,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) data->cancel = true; button_activate_state(C, but, BUTTON_STATE_EXIT); } - else if (event->x != event->prevx || event->y != event->prevy) { + else if (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1]) { /* Re-enable tool-tip on mouse move. */ ui_blocks_set_tooltips(region, true); button_tooltip_timer_reset(C, but); @@ -9169,7 +9222,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) WM_event_remove_timer(data->wm, data->window, data->autoopentimer); data->autoopentimer = NULL; - if (ui_but_contains_point_px(but, region, event->x, event->y) || but->active) { + if (ui_but_contains_point_px(but, region, event->xy) || but->active) { button_activate_state(C, but, BUTTON_STATE_MENU_OPEN); } } @@ -9219,12 +9272,12 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) case MOUSEMOVE: { /* deselect the button when moving the mouse away */ /* also de-activate for buttons that only show highlights */ - if (ui_but_contains_point_px(but, region, event->x, event->y)) { + if (ui_but_contains_point_px(but, region, event->xy)) { /* Drag on a hold button (used in the toolbar) now opens it immediately. */ if (data->hold_action_timer) { if (but->flag & UI_SELECT) { - if (len_manhattan_v2v2_int(&event->x, &event->prevx) <= + if (len_manhattan_v2v2_int(event->xy, event->prev_xy) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { /* pass */ } @@ -9277,7 +9330,7 @@ static int ui_handle_button_event(bContext *C, const wmEvent *event, uiBut *but) uiBut *bt; if (data->menu && data->menu->region) { - if (ui_region_contains_point_px(data->menu->region, event->x, event->y)) { + if (ui_region_contains_point_px(data->menu->region, event->xy)) { break; } } @@ -9392,8 +9445,8 @@ static int ui_list_activate_hovered_row(bContext *C, } } - const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; - uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy[0], mouse_xy[1]); + const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; + uiBut *listrow = ui_list_row_find_mouse_over(region, mouse_xy); if (listrow) { wmOperatorType *custom_activate_optype = ui_list->dyn_data->custom_activate_optype; @@ -9419,9 +9472,8 @@ static bool ui_list_is_hovering_draggable_but(bContext *C, const wmEvent *event) { /* On a tweak event, uses the coordinates from where tweaking was started. */ - const int *mouse_xy = ISTWEAK(event->type) ? &event->prevclickx : &event->x; - const uiBut *hovered_but = ui_but_find_mouse_over_ex( - region, mouse_xy[0], mouse_xy[1], false, NULL, NULL); + const int *mouse_xy = ISTWEAK(event->type) ? event->prev_click_xy : event->xy; + const uiBut *hovered_but = ui_but_find_mouse_over_ex(region, mouse_xy, false, NULL, NULL); if (list->dyn_data->custom_drag_optype) { if (ui_but_context_poll_operator(C, list->dyn_data->custom_drag_optype, hovered_but)) { @@ -9524,8 +9576,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi } uiListDyn *dyn_data = ui_list->dyn_data; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, listbox->block, &mx, &my); /* Convert pan to scroll-wheel. */ @@ -9639,6 +9691,38 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi return retval; } +static int ui_handle_tree_hover(const wmEvent *event, const ARegion *region) +{ + bool has_treerows = false; + LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { + /* Avoid unnecessary work: Tree-rows are assumed to be inside tree-views. */ + if (BLI_listbase_is_empty(&block->views)) { + continue; + } + + LISTBASE_FOREACH (uiBut *, but, &block->buttons) { + if (but->type == UI_BTYPE_TREEROW) { + but->flag &= ~UI_ACTIVE; + has_treerows = true; + } + } + } + + if (!has_treerows) { + /* Avoid unnecessary lookup. */ + return WM_UI_HANDLER_CONTINUE; + } + + /* Always highlight the hovered tree-row, even if the mouse hovers another button inside of it. + */ + uiBut *hovered_row_but = ui_tree_row_find_mouse_over(region, event->xy); + if (hovered_row_but) { + hovered_row_but->flag |= UI_ACTIVE; + } + + return WM_UI_HANDLER_CONTINUE; +} + static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, uiBut *but) { uiHandleButtonData *data = but->active; @@ -9674,8 +9758,7 @@ static void ui_handle_button_return_submenu(bContext *C, const wmEvent *event, u button_activate_exit(C, but, data, true, false); } else if (menu->menuretval & UI_RETURN_OUT) { - if (event->type == MOUSEMOVE && - ui_but_contains_point_px(but, data->region, event->x, event->y)) { + if (event->type == MOUSEMOVE && ui_but_contains_point_px(but, data->region, event->xy)) { button_activate_state(C, but, BUTTON_STATE_HIGHLIGHT); } else { @@ -9815,7 +9898,7 @@ static bool ui_mouse_motion_towards_check(uiBlock *block, static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEvent *event) { keynav->is_keynav = true; - copy_v2_v2_int(keynav->event_xy, &event->x); + copy_v2_v2_int(keynav->event_xy, event->xy); } /** * Return true if key-input isn't blocking mouse-motion, @@ -9824,7 +9907,7 @@ static void ui_mouse_motion_keynav_init(struct uiKeyNavLock *keynav, const wmEve static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEvent *event) { if (keynav->is_keynav && - (len_manhattan_v2v2_int(keynav->event_xy, &event->x) > BUTTON_KEYNAV_PX_LIMIT)) { + (len_manhattan_v2v2_int(keynav->event_xy, event->xy) > BUTTON_KEYNAV_PX_LIMIT)) { keynav->is_keynav = false; } @@ -10014,13 +10097,13 @@ static int ui_handle_menu_button(bContext *C, const wmEvent *event, uiPopupBlock else if (!ui_block_is_menu(but->block) || ui_block_is_pie_menu(but->block)) { /* pass, skip for dialogs */ } - else if (!ui_region_contains_point_px(but->active->region, event->x, event->y)) { + else if (!ui_region_contains_point_px(but->active->region, event->xy)) { /* Pass, needed to click-exit outside of non-floating menus. */ ui_region_auto_open_clear(but->active->region); } else if ((!ELEM(event->type, MOUSEMOVE, WHEELUPMOUSE, WHEELDOWNMOUSE, MOUSEPAN)) && ISMOUSE(event->type)) { - if (!ui_but_contains_point_px(but, but->active->region, event->x, event->y)) { + if (!ui_but_contains_point_px(but, but->active->region, event->xy)) { but = NULL; } } @@ -10094,8 +10177,8 @@ static int ui_handle_menu_event(bContext *C, int retval = WM_UI_HANDLER_CONTINUE; - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, block, &mx, &my); /* check if mouse is inside block */ @@ -10116,8 +10199,8 @@ static int ui_handle_menu_event(bContext *C, if (event->type == MOUSEMOVE) { int mdiff[2]; - sub_v2_v2v2_int(mdiff, &event->x, menu->grab_xy_prev); - copy_v2_v2_int(menu->grab_xy_prev, &event->x); + sub_v2_v2v2_int(mdiff, event->xy, menu->grab_xy_prev); + copy_v2_v2_int(menu->grab_xy_prev, event->xy); add_v2_v2v2_int(menu->popup_create_vars.event_xy, menu->popup_create_vars.event_xy, mdiff); @@ -10134,7 +10217,7 @@ static int ui_handle_menu_event(bContext *C, /* if a button is activated modal, always reset the start mouse * position of the towards mechanism to avoid losing focus, * and don't handle events */ - ui_mouse_motion_towards_reinit(menu, &event->x); + ui_mouse_motion_towards_reinit(menu, event->xy); } } else if (event->type == TIMER) { @@ -10146,7 +10229,7 @@ static int ui_handle_menu_event(bContext *C, /* for ui_mouse_motion_towards_block */ if (event->type == MOUSEMOVE) { if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) { - ui_mouse_motion_towards_init(menu, &event->x); + ui_mouse_motion_towards_init(menu, event->xy); } /* add menu scroll timer, if needed */ @@ -10229,14 +10312,14 @@ static int ui_handle_menu_event(bContext *C, retval = WM_UI_HANDLER_BREAK; break; - /* Smooth scrolling for popovers. */ + /* Smooth scrolling for pocopy_v2_v2_int(&povers. */ case MOUSEPAN: { if (IS_EVENT_MOD(event, shift, ctrl, alt, oskey)) { /* pass */ } else if (!ui_block_is_menu(block)) { if (block->flag & (UI_BLOCK_CLIPTOP | UI_BLOCK_CLIPBOTTOM)) { - const float dy = event->y - event->prevy; + const float dy = event->xy[1] - event->prev_xy[1]; if (dy != 0.0f) { ui_menu_scroll_apply_offset_y(region, block, dy); @@ -10559,7 +10642,8 @@ static int ui_handle_menu_event(bContext *C, menu->menuretval = UI_RETURN_OUT; } } - else if (saferct && !BLI_rctf_isect_pt(&saferct->parent, event->x, event->y)) { + else if (saferct && !BLI_rctf_isect_pt( + &saferct->parent, (float)event->xy[0], (float)event->xy[1])) { if (block->flag & UI_BLOCK_OUT_1) { menu->menuretval = UI_RETURN_OK; } @@ -10618,13 +10702,13 @@ static int ui_handle_menu_event(bContext *C, #ifdef USE_DRAG_POPUP else if ((event->type == LEFTMOUSE) && (event->val == KM_PRESS) && (inside && is_floating && inside_title)) { - if (!but || !ui_but_contains_point_px(but, region, event->x, event->y)) { + if (!but || !ui_but_contains_point_px(but, region, event->xy)) { if (but) { UI_but_tooltip_timer_remove(C, but); } menu->is_grab = true; - copy_v2_v2_int(menu->grab_xy_prev, &event->x); + copy_v2_v2_int(menu->grab_xy_prev, event->xy); retval = WM_UI_HANDLER_BREAK; } } @@ -10635,7 +10719,7 @@ static int ui_handle_menu_event(bContext *C, if (inside == false && (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER))) { uiSafetyRct *saferct; - ui_mouse_motion_towards_check(block, menu, &event->x, is_parent_inside == false); + ui_mouse_motion_towards_check(block, menu, event->xy, is_parent_inside == false); /* Check for all parent rects, enables arrow-keys to be used. */ for (saferct = block->saferct.first; saferct; saferct = saferct->next) { @@ -10643,10 +10727,10 @@ static int ui_handle_menu_event(bContext *C, * events we check all preceding block rects too to make * arrow keys navigation work */ if (event->type != MOUSEMOVE || saferct == block->saferct.first) { - if (BLI_rctf_isect_pt(&saferct->parent, (float)event->x, (float)event->y)) { + if (BLI_rctf_isect_pt(&saferct->parent, (float)event->xy[0], (float)event->xy[1])) { break; } - if (BLI_rctf_isect_pt(&saferct->safety, (float)event->x, (float)event->y)) { + if (BLI_rctf_isect_pt(&saferct->safety, (float)event->xy[0], (float)event->xy[1])) { break; } } @@ -10690,7 +10774,7 @@ static int ui_handle_menu_event(bContext *C, } #endif - /* Don't handle double click events, rehandle as regular press/release. */ + /* Don't handle double click events, re-handle as regular press/release. */ if (retval == WM_UI_HANDLER_CONTINUE && event->val == KM_DBL_CLICK) { return retval; } @@ -10747,7 +10831,7 @@ static int ui_handle_menu_return_submenu(bContext *C, if (block->flag & (UI_BLOCK_MOVEMOUSE_QUIT | UI_BLOCK_POPOVER)) { /* for cases where close does not cascade, allow the user to * move the mouse back towards the menu without closing */ - ui_mouse_motion_towards_reinit(menu, &event->x); + ui_mouse_motion_towards_reinit(menu, event->xy); } if (menu->menuretval) { @@ -10855,7 +10939,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle const double duration = menu->scrolltimer->duration; - float event_xy[2] = {event->x, event->y}; + float event_xy[2] = {UNPACK2(event->xy)}; ui_window_to_block_fl(region, block, &event_xy[0], &event_xy[1]); @@ -11032,7 +11116,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle case EVT_XKEY: case EVT_YKEY: case EVT_ZKEY: { - if ((event->val == KM_PRESS || event->val == KM_DBL_CLICK) && + if ((ELEM(event->val, KM_PRESS, KM_DBL_CLICK)) && !IS_EVENT_MOD(event, shift, ctrl, oskey)) { LISTBASE_FOREACH (uiBut *, but, &block->buttons) { if (but->menu_key == event->type) { @@ -11110,8 +11194,8 @@ static int ui_handle_menus_recursive(bContext *C, if (do_recursion) { if (is_parent_inside == false) { - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(menu->region, block, &mx, &my); inside = BLI_rctf_isect_pt(&block->rect, mx, my); } @@ -11177,7 +11261,7 @@ static int ui_handle_menus_recursive(bContext *C, } if (do_towards_reinit) { - ui_mouse_motion_towards_reinit(menu, &event->x); + ui_mouse_motion_towards_reinit(menu, event->xy); } return retval; @@ -11238,10 +11322,15 @@ static int ui_region_handler(bContext *C, const wmEvent *event, void *UNUSED(use } /* Re-enable tool-tips. */ - if (event->type == MOUSEMOVE && (event->x != event->prevx || event->y != event->prevy)) { + if (event->type == MOUSEMOVE && + (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) { ui_blocks_set_tooltips(region, true); } + /* Always do this, to reliably update tree-row highlighting, even if the mouse hovers a button + * inside the row (it's an overlapping layout). */ + ui_handle_tree_hover(event, region); + /* delayed apply callbacks */ ui_apply_but_funcs_after(C); @@ -11335,7 +11424,8 @@ static int ui_handler_region_menu(bContext *C, const wmEvent *event, void *UNUSE } /* Re-enable tool-tips. */ - if (event->type == MOUSEMOVE && (event->x != event->prevx || event->y != event->prevy)) { + if (event->type == MOUSEMOVE && + (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) { ui_blocks_set_tooltips(region, true); } @@ -11429,7 +11519,8 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) } else { /* Re-enable tool-tips */ - if (event->type == MOUSEMOVE && (event->x != event->prevx || event->y != event->prevy)) { + if (event->type == MOUSEMOVE && + (event->xy[0] != event->prev_xy[0] || event->xy[1] != event->prev_xy[1])) { ui_blocks_set_tooltips(menu->region, true); } } @@ -11599,20 +11690,25 @@ void UI_screen_free_active_but(const bContext *C, bScreen *screen) } } -/* returns true if highlighted button allows drop of names */ -/* called in region context */ -bool UI_but_active_drop_name(bContext *C) +uiBut *UI_but_active_drop_name_button(const bContext *C) { ARegion *region = CTX_wm_region(C); uiBut *but = ui_region_find_active_but(region); if (but) { if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { - return true; + return but; } } - return false; + return NULL; +} + +/* returns true if highlighted button allows drop of names */ +/* called in region context */ +bool UI_but_active_drop_name(const bContext *C) +{ + return UI_but_active_drop_name_button(C) != NULL; } bool UI_but_active_drop_color(bContext *C) diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index f739830cfdb..c20129b4184 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -47,6 +47,7 @@ #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "RNA_access.h" @@ -309,7 +310,7 @@ static void vicon_keytype_draw_wrapper( sh_bindings.flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); immBegin(GPU_PRIM_POINTS, 1); @@ -480,6 +481,35 @@ DEF_ICON_COLLECTION_COLOR_DRAW(08, COLLECTION_COLOR_08); # undef DEF_ICON_COLLECTION_COLOR_DRAW +static void vicon_strip_color_draw( + short color_tag, int x, int y, int w, int UNUSED(h), float UNUSED(alpha)) +{ + bTheme *btheme = UI_GetTheme(); + const ThemeStripColor *strip_color = &btheme->strip_color[color_tag]; + + const float aspect = (float)ICON_DEFAULT_WIDTH / (float)w; + + UI_icon_draw_ex(x, y, ICON_SNAP_FACE, aspect, 1.0f, 0.0f, strip_color->color, true); +} + +# define DEF_ICON_STRIP_COLOR_DRAW(index, color) \ + static void vicon_strip_color_draw_##index(int x, int y, int w, int h, float alpha) \ + { \ + vicon_strip_color_draw(color, x, y, w, h, alpha); \ + } + +DEF_ICON_STRIP_COLOR_DRAW(01, SEQUENCE_COLOR_01); +DEF_ICON_STRIP_COLOR_DRAW(02, SEQUENCE_COLOR_02); +DEF_ICON_STRIP_COLOR_DRAW(03, SEQUENCE_COLOR_03); +DEF_ICON_STRIP_COLOR_DRAW(04, SEQUENCE_COLOR_04); +DEF_ICON_STRIP_COLOR_DRAW(05, SEQUENCE_COLOR_05); +DEF_ICON_STRIP_COLOR_DRAW(06, SEQUENCE_COLOR_06); +DEF_ICON_STRIP_COLOR_DRAW(07, SEQUENCE_COLOR_07); +DEF_ICON_STRIP_COLOR_DRAW(08, SEQUENCE_COLOR_08); +DEF_ICON_STRIP_COLOR_DRAW(09, SEQUENCE_COLOR_09); + +# undef DEF_ICON_STRIP_COLOR_DRAW + /* Dynamically render icon instead of rendering a plain color to a texture/buffer * This is not strictly a "vicon", as it needs access to icon->obj to get the color info, * but it works in a very similar way. @@ -995,6 +1025,16 @@ static void init_internal_icons(void) def_internal_vicon(ICON_COLLECTION_COLOR_06, vicon_collection_color_draw_06); def_internal_vicon(ICON_COLLECTION_COLOR_07, vicon_collection_color_draw_07); def_internal_vicon(ICON_COLLECTION_COLOR_08, vicon_collection_color_draw_08); + + def_internal_vicon(ICON_SEQUENCE_COLOR_01, vicon_strip_color_draw_01); + def_internal_vicon(ICON_SEQUENCE_COLOR_02, vicon_strip_color_draw_02); + def_internal_vicon(ICON_SEQUENCE_COLOR_03, vicon_strip_color_draw_03); + def_internal_vicon(ICON_SEQUENCE_COLOR_04, vicon_strip_color_draw_04); + def_internal_vicon(ICON_SEQUENCE_COLOR_05, vicon_strip_color_draw_05); + def_internal_vicon(ICON_SEQUENCE_COLOR_06, vicon_strip_color_draw_06); + def_internal_vicon(ICON_SEQUENCE_COLOR_07, vicon_strip_color_draw_07); + def_internal_vicon(ICON_SEQUENCE_COLOR_08, vicon_strip_color_draw_08); + def_internal_vicon(ICON_SEQUENCE_COLOR_09, vicon_strip_color_draw_09); } static void init_iconfile_list(struct ListBase *list) diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index d61104f094e..f766bb1465f 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -116,7 +116,7 @@ extern const char ui_radial_dir_to_numpad[8]; extern const short ui_radial_dir_to_angle[8]; /* internal panel drawing defines */ -#define PNL_HEADER (UI_UNIT_Y * 1.2) /* 24 default */ +#define PNL_HEADER (UI_UNIT_Y * 1.25) /* 24 default */ /* bit button defines */ /* Bit operations */ @@ -360,6 +360,14 @@ typedef struct uiButDatasetRow { int indentation; } uiButDatasetRow; +/** Derived struct for #UI_BTYPE_TREEROW. */ +typedef struct uiButTreeRow { + uiBut but; + + uiTreeViewItemHandle *tree_item; + int indentation; +} uiButTreeRow; + /** Derived struct for #UI_BTYPE_HSVCUBE. */ typedef struct uiButHSVCube { uiBut but; @@ -399,6 +407,7 @@ typedef struct uiButExtraOpIcon { struct wmOperatorCallParams *optype_params; bool highlighted; + bool disabled; } uiButExtraOpIcon; typedef struct ColorPicker { @@ -488,6 +497,11 @@ struct uiBlock { ListBase contexts; + /** A block can store "views" on data-sets. Currently tree-views (#AbstractTreeView) only. + * Others are imaginable, e.g. table-views, grid-views, etc. These are stored here to support + * state that is persistent over redraws (e.g. collapsed tree-view items). */ + ListBase views; + char name[UI_MAX_NAME_STR]; float winmat[4][4]; @@ -666,6 +680,7 @@ extern bool ui_but_string_eval_number(struct bContext *C, extern int ui_but_string_get_max_length(uiBut *but); /* Clear & exit the active button's string. */ extern void ui_but_active_string_clear_and_exit(struct bContext *C, uiBut *but) ATTR_NONNULL(); +extern void ui_but_set_string_interactive(struct bContext *C, uiBut *but, const char *value); extern uiBut *ui_but_drag_multi_edit_get(uiBut *but); void ui_def_but_icon(uiBut *but, const int icon, const int flag); @@ -681,6 +696,9 @@ void ui_but_range_set_hard(uiBut *but); void ui_but_range_set_soft(uiBut *but); bool ui_but_context_poll_operator(struct bContext *C, struct wmOperatorType *ot, const uiBut *but); +bool ui_but_context_poll_operator_ex(struct bContext *C, + const uiBut *but, + const struct wmOperatorCallParams *optype_params); extern void ui_but_update(uiBut *but); extern void ui_but_update_edited(uiBut *but); @@ -813,7 +831,7 @@ struct ARegion *ui_searchbox_create_menu(struct bContext *C, struct ARegion *butregion, uiButSearch *search_but); -bool ui_searchbox_inside(struct ARegion *region, int x, int y); +bool ui_searchbox_inside(struct ARegion *region, const int xy[2]) ATTR_NONNULL(1, 2); int ui_searchbox_find_index(struct ARegion *region, const char *name); void ui_searchbox_update(struct bContext *C, struct ARegion *region, uiBut *but, const bool reset); int ui_searchbox_autocomplete(struct bContext *C, struct ARegion *region, uiBut *but, char *str); @@ -1014,9 +1032,7 @@ enum { struct GPUBatch *ui_batch_roundbox_widget_get(void); struct GPUBatch *ui_batch_roundbox_shadow_get(void); -void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]); void ui_draw_menu_back(struct uiStyle *style, uiBlock *block, rcti *rect); -void ui_draw_box_opaque(rcti *rect, int roundboxalign); void ui_draw_popover_back(struct ARegion *region, struct uiStyle *style, uiBlock *block, @@ -1094,6 +1110,7 @@ void ui_resources_free(void); /* interface_layout.c */ void ui_layout_add_but(uiLayout *layout, uiBut *but); +void ui_layout_remove_but(uiLayout *layout, const uiBut *but); bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but); uiBut *ui_but_add_search(uiBut *but, PointerRNA *ptr, @@ -1145,35 +1162,36 @@ bool ui_but_contains_rect(const uiBut *but, const rctf *rect); bool ui_but_contains_point_px_icon(const uiBut *but, struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; -bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, int x, int y) - ATTR_WARN_UNUSED_RESULT; +bool ui_but_contains_point_px(const uiBut *but, const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2, 3) ATTR_WARN_UNUSED_RESULT; uiBut *ui_list_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; uiBut *ui_list_find_from_row(const struct ARegion *region, const uiBut *row_but) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, - int x, - int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_row_find_mouse_over(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; uiBut *ui_list_row_find_from_index(const struct ARegion *region, const int index, uiBut *listbox) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_tree_row_find_mouse_over(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2); +uiBut *ui_tree_row_find_active(const struct ARegion *region); typedef bool (*uiButFindPollFn)(const uiBut *but, const void *customdata); uiBut *ui_but_find_mouse_over_ex(const struct ARegion *region, - const int x, - const int y, + const int xy[2], const bool labeledit, const uiButFindPollFn find_poll, - const void *find_custom_data) ATTR_WARN_UNUSED_RESULT; + const void *find_custom_data) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_mouse_over(const struct ARegion *region, const struct wmEvent *event) ATTR_WARN_UNUSED_RESULT; uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) ATTR_WARN_UNUSED_RESULT; -uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, - int x, - int y) ATTR_WARN_UNUSED_RESULT; +uiBut *ui_list_find_mouse_over_ex(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; bool ui_but_contains_password(const uiBut *but) ATTR_WARN_UNUSED_RESULT; @@ -1193,10 +1211,8 @@ bool ui_block_is_popover(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_pie_menu(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; bool ui_block_is_popup_any(const uiBlock *block) ATTR_WARN_UNUSED_RESULT; -uiBlock *ui_block_find_mouse_over_ex(const struct ARegion *region, - const int x, - const int y, - bool only_clip); +uiBlock *ui_block_find_mouse_over_ex(const struct ARegion *region, const int xy[2], bool only_clip) + ATTR_NONNULL(1, 2); uiBlock *ui_block_find_mouse_over(const struct ARegion *region, const struct wmEvent *event, bool only_clip); @@ -1205,12 +1221,12 @@ uiBut *ui_region_find_first_but_test_flag(struct ARegion *region, int flag_include, int flag_exclude); uiBut *ui_region_find_active_but(struct ARegion *region) ATTR_WARN_UNUSED_RESULT; -bool ui_region_contains_point_px(const struct ARegion *region, - int x, - int y) ATTR_WARN_UNUSED_RESULT; +bool ui_region_contains_point_px(const struct ARegion *region, const int xy[2]) + ATTR_NONNULL(1, 2) ATTR_WARN_UNUSED_RESULT; bool ui_region_contains_rect_px(const struct ARegion *region, const rcti *rect_px); -struct ARegion *ui_screen_region_find_mouse_over_ex(struct bScreen *screen, int x, int y); +struct ARegion *ui_screen_region_find_mouse_over_ex(struct bScreen *screen, const int xy[2]) + ATTR_NONNULL(1, 2); struct ARegion *ui_screen_region_find_mouse_over(struct bScreen *screen, const struct wmEvent *event); @@ -1274,6 +1290,13 @@ bool ui_jump_to_target_button_poll(struct bContext *C); /* interface_queries.c */ void ui_interface_tag_script_reload_queries(void); +/* interface_view.cc */ +void ui_block_free_views(struct uiBlock *block); +uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiTreeViewHandle *new_view); +uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, + const uiTreeViewItemHandle *new_item_handle); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/interface/interface_layout.c b/source/blender/editors/interface/interface_layout.c index 66c75c63050..20e95ef4e9c 100644 --- a/source/blender/editors/interface/interface_layout.c +++ b/source/blender/editors/interface/interface_layout.c @@ -288,35 +288,89 @@ static bool ui_layout_variable_size(uiLayout *layout) return ui_layout_vary_direction(layout) == UI_ITEM_VARY_X || layout->variable_size; } -/* estimated size of text + icon */ -static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact) +/** + * Factors to apply to #UI_UNIT_X when calculating button width. + * This is used when the layout is a varying size, see #ui_layout_variable_size. + */ +struct uiTextIconPadFactor { + float text; + float icon; + float icon_only; +}; + +/** + * This adds over an icons width of padding even when no icon is used, + * this is done because most buttons need additional space (drop-down chevron for example). + * menus and labels use much smaller `text` values compared to this default. + * + * \note It may seem odd that the icon only adds 0.25 + * but taking margins into account its fine, + * except for #ui_text_pad_compact where a bit more margin is required. + */ +static const struct uiTextIconPadFactor ui_text_pad_default = { + .text = 1.50f, + .icon = 0.25f, + .icon_only = 0.0f, +}; + +/** #ui_text_pad_default scaled down. */ +static const struct uiTextIconPadFactor ui_text_pad_compact = { + .text = 1.25f, + .icon = 0.35f, + .icon_only = 0.0f, +}; + +/** Least amount of padding not to clip the text or icon. */ +static const struct uiTextIconPadFactor ui_text_pad_none = { + .text = 0.25f, + .icon = 1.50f, + .icon_only = 0.0f, +}; + +/** + * Estimated size of text + icon. + */ +static int ui_text_icon_width_ex(uiLayout *layout, + const char *name, + int icon, + const struct uiTextIconPadFactor *pad_factor) { const int unit_x = UI_UNIT_X * (layout->scale[0] ? layout->scale[0] : 1.0f); + /* When there is no text, always behave as if this is an icon-only button + * since it's not useful to return empty space. */ if (icon && !name[0]) { - return unit_x; /* icon only */ + return unit_x * (1.0f + pad_factor->icon_only); } if (ui_layout_variable_size(layout)) { if (!icon && !name[0]) { - return unit_x; /* No icon or name. */ + return unit_x * (1.0f + pad_factor->icon_only); } + if (layout->alignment != UI_LAYOUT_ALIGN_EXPAND) { layout->item.flag |= UI_ITEM_FIXED_SIZE; } - const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - float margin = compact ? 1.25 : 1.50; + + float margin = pad_factor->text; if (icon) { - /* It may seem odd that the icon only adds (unit_x / 4) - * but taking margins into account its fine, except - * in compact mode a bit more margin is required. */ - margin += compact ? 0.35 : 0.25; + margin += pad_factor->icon; } - return UI_fontstyle_string_width(fstyle, name) + (unit_x * margin); + + const float aspect = layout->root->block->aspect; + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + return UI_fontstyle_string_width_with_block_aspect(fstyle, name, aspect) + + (int)ceilf(unit_x * margin); } return unit_x * 10; } +static int ui_text_icon_width(uiLayout *layout, const char *name, int icon, bool compact) +{ + return ui_text_icon_width_ex( + layout, name, icon, compact ? &ui_text_pad_compact : &ui_text_pad_default); +} + static void ui_item_size(uiItem *item, int *r_w, int *r_h) { if (item->type == ITEM_BUTTON) { @@ -1285,7 +1339,7 @@ static void ui_item_menu_hold(struct bContext *C, ARegion *butregion, uiBut *but UI_menutype_draw(C, mt, layout); } else { - uiItemL(layout, "Menu Missing:", ICON_NONE); + uiItemL(layout, TIP_("Menu Missing:"), ICON_NONE); uiItemL(layout, menu_id, ICON_NONE); } UI_popup_menu_end(C, pup); @@ -2857,23 +2911,23 @@ static uiBut *ui_item_menu(uiLayout *layout, icon = ICON_BLANK1; } - int w = ui_text_icon_width(layout, name, icon, 1); - const int h = UI_UNIT_Y; - + struct uiTextIconPadFactor pad_factor = ui_text_pad_compact; if (layout->root->type == UI_LAYOUT_HEADER) { /* Ugly! */ if (icon == ICON_NONE && force_menu) { /* pass */ } else if (force_menu) { - w += 0.6f * UI_UNIT_X; + pad_factor.text = 1.85; + pad_factor.icon_only = 0.6f; } else { - if (name[0]) { - w -= UI_UNIT_X / 2; - } + pad_factor.text = 0.75f; } } + const int w = ui_text_icon_width_ex(layout, name, icon, &pad_factor); + const int h = UI_UNIT_Y; + if (heading_layout) { ui_layout_heading_label_add(layout, heading_layout, true, true); } @@ -3133,8 +3187,7 @@ static uiBut *uiItemL_(uiLayout *layout, const char *name, int icon) icon = ICON_BLANK1; } - const int w = ui_text_icon_width(layout, name, icon, 0); - + const int w = ui_text_icon_width_ex(layout, name, icon, &ui_text_pad_none); uiBut *but; if (icon && name[0]) { but = uiDefIconTextBut( @@ -5556,28 +5609,52 @@ void ui_layout_add_but(uiLayout *layout, uiBut *but) ui_button_group_add_but(uiLayoutGetBlock(layout), but); } -bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but) +static uiButtonItem *ui_layout_find_button_item(const uiLayout *layout, const uiBut *but) { - ListBase *child_list = layout->child_items_layout ? &layout->child_items_layout->items : - &layout->items; + const ListBase *child_list = layout->child_items_layout ? &layout->child_items_layout->items : + &layout->items; LISTBASE_FOREACH (uiItem *, item, child_list) { if (item->type == ITEM_BUTTON) { uiButtonItem *bitem = (uiButtonItem *)item; - if (bitem->but == old_but_ptr) { - bitem->but = new_but; - return true; + if (bitem->but == but) { + return bitem; } } else { - if (ui_layout_replace_but_ptr((uiLayout *)item, old_but_ptr, new_but)) { - return true; + uiButtonItem *nested_item = ui_layout_find_button_item((uiLayout *)item, but); + if (nested_item) { + return nested_item; } } } - return false; + return NULL; +} + +void ui_layout_remove_but(uiLayout *layout, const uiBut *but) +{ + uiButtonItem *bitem = ui_layout_find_button_item(layout, but); + if (!bitem) { + return; + } + + BLI_freelinkN(&layout->items, bitem); +} + +/** + * \return true if the button was successfully replaced. + */ +bool ui_layout_replace_but_ptr(uiLayout *layout, const void *old_but_ptr, uiBut *new_but) +{ + uiButtonItem *bitem = ui_layout_find_button_item(layout, old_but_ptr); + if (!bitem) { + return false; + } + + bitem->but = new_but; + return true; } void uiLayoutSetFixedSize(uiLayout *layout, bool fixed_size) diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index dd10d942fc9..c962a1107ae 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -849,6 +849,9 @@ bool UI_context_copy_to_selected_list(bContext *C, else if (RNA_struct_is_a(ptr->type, &RNA_NlaStrip)) { *r_lb = CTX_data_collection_get(C, "selected_nla_strips"); } + else if (RNA_struct_is_a(ptr->type, &RNA_MovieTrackingTrack)) { + *r_lb = CTX_data_collection_get(C, "selected_movieclip_tracks"); + } else if (RNA_struct_is_a(ptr->type, &RNA_Constraint) && (path_from_bone = RNA_path_resolve_from_type_to_property(ptr, prop, &RNA_PoseBone)) != NULL) { @@ -1381,16 +1384,7 @@ static int editsource_text_edit(bContext *C, /* naughty!, find text area to set, not good behavior * but since this is a developer tool lets allow it - campbell */ - ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0); - if (area) { - SpaceText *st = area->spacedata.first; - ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); - st->text = text; - if (region) { - ED_text_scroll_to_cursor(st, region, true); - } - } - else { + if (!ED_text_activate_in_screen(C, text)) { BKE_reportf(op->reports, RPT_INFO, "See '%s' in the text editor", text->id.name + 2); } @@ -1692,7 +1686,8 @@ static int ui_button_press_invoke(bContext *C, wmOperator *op, const wmEvent *ev bScreen *screen = CTX_wm_screen(C); const bool skip_depressed = RNA_boolean_get(op->ptr, "skip_depressed"); ARegion *region_prev = CTX_wm_region(C); - ARegion *region = screen ? BKE_screen_find_region_xy(screen, RGN_TYPE_ANY, event->x, event->y) : + ARegion *region = screen ? BKE_screen_find_region_xy( + screen, RGN_TYPE_ANY, event->xy[0], event->xy[1]) : NULL; if (region == NULL) { @@ -1868,6 +1863,39 @@ static void UI_OT_drop_color(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Drop Name Operator + * \{ */ + +static int drop_name_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + uiBut *but = UI_but_active_drop_name_button(C); + char *str = RNA_string_get_alloc(op->ptr, "string", NULL, 0, NULL); + + if (str) { + ui_but_set_string_interactive(C, but, str); + MEM_freeN(str); + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_drop_name(wmOperatorType *ot) +{ + ot->name = "Drop Name"; + ot->idname = "UI_OT_drop_name"; + ot->description = "Drop name to button"; + + ot->poll = ED_operator_regionactive; + ot->invoke = drop_name_invoke; + ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; + + RNA_def_string( + ot->srna, "string", NULL, 0, "String", "The string value to drop into the button"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name UI List Search Operator * \{ */ @@ -1926,6 +1954,93 @@ static void UI_OT_list_start_filter(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UI Tree-View Drop Operator + * \{ */ + +static bool ui_tree_view_drop_poll(bContext *C) +{ + const wmWindow *win = CTX_wm_window(C); + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at( + region, win->eventstate->xy); + + return hovered_tree_item != NULL; +} + +static int ui_tree_view_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *event) +{ + if (event->custom != EVT_DATA_DRAGDROP) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + const ARegion *region = CTX_wm_region(C); + uiTreeViewItemHandle *hovered_tree_item = UI_block_tree_view_find_item_at(region, event->xy); + + if (!UI_tree_view_item_drop_handle(hovered_tree_item, event->customdata)) { + return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; + } + + return OPERATOR_FINISHED; +} + +static void UI_OT_tree_view_drop(wmOperatorType *ot) +{ + ot->name = "Tree View drop"; + ot->idname = "UI_OT_tree_view_drop"; + ot->description = "Drag and drop items onto a tree item"; + + ot->invoke = ui_tree_view_drop_invoke; + ot->poll = ui_tree_view_drop_poll; + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Tree-View Item Rename Operator + * + * General purpose renaming operator for tree-views. Thanks to this, to add a rename button to + * context menus for example, tree-view API users don't have to implement own renaming operators + * with the same logic as they already have for their #ui::AbstractTreeViewItem::rename() override. + * + * \{ */ + +static bool ui_tree_view_item_rename_poll(bContext *C) +{ + const ARegion *region = CTX_wm_region(C); + const uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); + return active_item != NULL && UI_tree_view_item_can_rename(active_item); +} + +static int ui_tree_view_item_rename_exec(bContext *C, wmOperator *UNUSED(op)) +{ + ARegion *region = CTX_wm_region(C); + uiTreeViewItemHandle *active_item = UI_block_tree_view_find_active_item(region); + + UI_tree_view_item_begin_rename(active_item); + ED_region_tag_redraw(region); + + return OPERATOR_FINISHED; +} + +static void UI_OT_tree_view_item_rename(wmOperatorType *ot) +{ + ot->name = "Rename Tree-View Item"; + ot->idname = "UI_OT_tree_view_item_rename"; + ot->description = "Rename the active item in the tree"; + + ot->exec = ui_tree_view_item_rename_exec; + ot->poll = ui_tree_view_item_rename_poll; + /* Could get a custom tooltip via the `get_description()` callback and another overridable + * function of the tree-view. */ + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Operator & Keymap Registration * \{ */ @@ -1943,6 +2058,7 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_copy_to_selected_button); WM_operatortype_append(UI_OT_jump_to_target_button); WM_operatortype_append(UI_OT_drop_color); + WM_operatortype_append(UI_OT_drop_name); #ifdef WITH_PYTHON WM_operatortype_append(UI_OT_editsource); WM_operatortype_append(UI_OT_edittranslation_init); @@ -1953,6 +2069,9 @@ void ED_operatortypes_ui(void) WM_operatortype_append(UI_OT_list_start_filter); + WM_operatortype_append(UI_OT_tree_view_drop); + WM_operatortype_append(UI_OT_tree_view_item_rename); + /* external */ WM_operatortype_append(UI_OT_eyedropper_color); WM_operatortype_append(UI_OT_eyedropper_colorramp); diff --git a/source/blender/editors/interface/interface_panel.c b/source/blender/editors/interface/interface_panel.c index a64797af24f..072362492d8 100644 --- a/source/blender/editors/interface/interface_panel.c +++ b/source/blender/editors/interface/interface_panel.c @@ -1112,23 +1112,14 @@ static void panel_draw_highlight_border(const Panel *panel, const rcti *rect, const rcti *header_rect) { - const bool draw_box_style = panel->type->flag & PANEL_TYPE_DRAW_BOX; const bool is_subpanel = panel->type->parent != NULL; if (is_subpanel) { return; } - float radius; - if (draw_box_style) { - /* Use the theme for box widgets. */ - const uiWidgetColors *box_wcol = &UI_GetTheme()->tui.wcol_box; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - radius = box_wcol->roundness * U.widget_unit; - } - else { - UI_draw_roundbox_corner_set(UI_CNR_NONE); - radius = 0.0f; - } + const bTheme *btheme = UI_GetTheme(); + const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f; + UI_draw_roundbox_corner_set(UI_CNR_ALL); float color[4]; UI_GetThemeColor4fv(TH_SELECT_ACTIVE, color); @@ -1172,18 +1163,17 @@ static void panel_draw_aligned_widgets(const uiStyle *style, /* Draw collapse icon. */ { - rctf collapse_rect = { - .xmin = widget_rect.xmin, - .xmax = widget_rect.xmin + header_height, - .ymin = widget_rect.ymin, - .ymax = widget_rect.ymax, - }; - BLI_rctf_scale(&collapse_rect, 0.25f); - - float triangle_color[4]; - rgba_uchar_to_float(triangle_color, title_color); - - ui_draw_anti_tria_rect(&collapse_rect, UI_panel_is_closed(panel) ? 'h' : 'v', triangle_color); + const float size_y = BLI_rcti_size_y(&widget_rect); + GPU_blend(GPU_BLEND_ALPHA); + UI_icon_draw_ex(widget_rect.xmin + size_y * 0.2f, + widget_rect.ymin + size_y * 0.2f, + UI_panel_is_closed(panel) ? ICON_RIGHTARROW : ICON_DOWNARROW_HLT, + aspect * U.inv_dpi_fac, + 0.7f, + 0.0f, + title_color, + false); + GPU_blend(GPU_BLEND_NONE); } /* Draw text label. */ @@ -1243,7 +1233,6 @@ static void panel_draw_aligned_backdrop(const Panel *panel, const rcti *rect, const rcti *header_rect) { - const bool draw_box_style = panel->type->flag & PANEL_TYPE_DRAW_BOX; const bool is_subpanel = panel->type->parent != NULL; const bool is_open = !UI_panel_is_closed(panel); @@ -1251,90 +1240,52 @@ static void panel_draw_aligned_backdrop(const Panel *panel, return; } - const uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const bTheme *btheme = UI_GetTheme(); + const float radius = btheme->tui.panel_roundness * U.widget_unit * 0.5f; - /* Draw with an opaque box backdrop for box style panels. */ - if (draw_box_style) { - /* Use the theme for box widgets. */ - const uiWidgetColors *box_wcol = &UI_GetTheme()->tui.wcol_box; + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); - if (is_subpanel) { - /* Use rounded bottom corners for the last subpanel. */ - if (panel->next == NULL) { - UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT); - float color[4]; - UI_GetThemeColor4fv(TH_PANEL_SUB_BACK, color); - /* Change the width a little bit to line up with sides. */ - UI_draw_roundbox_aa( - &(const rctf){ - .xmin = rect->xmin + U.pixelsize, - .xmax = rect->xmax - U.pixelsize, - .ymin = rect->ymin + U.pixelsize, - .ymax = rect->ymax, - }, - true, - box_wcol->roundness * U.widget_unit, - color); - } - else { - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_PANEL_SUB_BACK); - immRectf(pos, rect->xmin + U.pixelsize, rect->ymin, rect->xmax - U.pixelsize, rect->ymax); - immUnbindProgram(); - } - } - else { - /* Expand the top a tiny bit to give header buttons equal size above and below. */ - rcti box_rect = { - .xmin = rect->xmin, - .xmax = rect->xmax, - .ymin = is_open ? rect->ymin : header_rect->ymin, - .ymax = header_rect->ymax + U.pixelsize, - }; - ui_draw_box_opaque(&box_rect, UI_CNR_ALL); + /* Panel backdrop. */ + if (is_open || panel->type->flag & PANEL_TYPE_NO_HEADER) { + float panel_backcolor[4]; + UI_draw_roundbox_corner_set(is_open ? UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT : UI_CNR_ALL); + UI_GetThemeColor4fv((is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK), panel_backcolor); - /* Mimic the border between aligned box widgets for the bottom of the header. */ - if (is_open) { - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(GPU_BLEND_ALPHA); - - /* Top line. */ - immUniformColor4ubv(box_wcol->outline); - immRectf(pos, rect->xmin, header_rect->ymin - U.pixelsize, rect->xmax, header_rect->ymin); - - /* Bottom "shadow" line. */ - immUniformThemeColor(TH_WIDGET_EMBOSS); - immRectf(pos, - rect->xmin, - header_rect->ymin - U.pixelsize, - rect->xmax, - header_rect->ymin - U.pixelsize - 1); - - GPU_blend(GPU_BLEND_NONE); - immUnbindProgram(); - } - } + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = rect->xmin, + .xmax = rect->xmax, + .ymin = rect->ymin, + .ymax = rect->ymax, + }, + true, + radius, + panel_backcolor); } - else { - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - GPU_blend(GPU_BLEND_ALPHA); - /* Panel backdrop. */ - if (is_open || panel->type->flag & PANEL_TYPE_NO_HEADER) { - immUniformThemeColor(is_subpanel ? TH_PANEL_SUB_BACK : TH_PANEL_BACK); - immRectf(pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax); - } + /* Panel header backdrops for non sub-panels. */ + if (!is_subpanel) { + float panel_headercolor[4]; + UI_GetThemeColor4fv(UI_panel_matches_search_filter(panel) ? TH_MATCH : TH_PANEL_HEADER, + panel_headercolor); + UI_draw_roundbox_corner_set(is_open ? UI_CNR_TOP_RIGHT | UI_CNR_TOP_LEFT : UI_CNR_ALL); - /* Panel header backdrops for non sub-panels. */ - if (!is_subpanel) { - immUniformThemeColor(UI_panel_matches_search_filter(panel) ? TH_MATCH : TH_PANEL_HEADER); - immRectf(pos, rect->xmin, header_rect->ymin, rect->xmax, header_rect->ymax); - } - - GPU_blend(GPU_BLEND_NONE); - immUnbindProgram(); + /* Change the width a little bit to line up with the sides. */ + UI_draw_roundbox_4fv( + &(const rctf){ + .xmin = rect->xmin, + .xmax = rect->xmax, + .ymin = header_rect->ymin, + .ymax = header_rect->ymax, + }, + true, + radius, + panel_headercolor); } + + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); } /** @@ -1599,7 +1550,7 @@ void UI_panel_category_draw_all(ARegion *region, const char *category_id_active) } BLF_position(fontid, rct->xmax - text_v_ofs, rct->ymin + tab_v_pad_text, 0.0f); - BLF_color3ubv(fontid, theme_col_text); + BLF_color3ubv(fontid, is_active ? theme_col_text_hi : theme_col_text); BLF_draw(fontid, category_id_draw, category_draw_len); GPU_blend(GPU_BLEND_NONE); @@ -1789,9 +1740,9 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra const int region_offset_x = panel_region_offset_x_get(region); for (int i = 0; i < active_panels_len; i++) { PanelSort *ps = &panel_sort[i]; - const bool use_box = ps->panel->type->flag & PANEL_TYPE_DRAW_BOX; + const bool no_header = ps->panel->type->flag & PANEL_TYPE_NO_HEADER; ps->panel->runtime.region_ofsx = region_offset_x; - ps->new_offset_x = region_offset_x + ((use_box) ? UI_PANEL_BOX_STYLE_MARGIN : 0); + ps->new_offset_x = region_offset_x + (no_header ? 0 : UI_PANEL_MARGIN_X); } /* Y offset. */ @@ -1799,10 +1750,7 @@ static bool uiAlignPanelStep(ARegion *region, const float factor, const bool dra PanelSort *ps = &panel_sort[i]; y -= get_panel_real_size_y(ps->panel); - const bool use_box = ps->panel->type->flag & PANEL_TYPE_DRAW_BOX; - if (use_box) { - y -= UI_PANEL_BOX_STYLE_MARGIN; - } + y -= UI_PANEL_MARGIN_Y; ps->new_offset_y = y; /* The header still draws offset by the size of closed panels, so apply the offset here. */ if (UI_panel_is_closed(ps->panel)) { @@ -1978,7 +1926,7 @@ static void ui_do_drag(const bContext *C, const wmEvent *event, Panel *panel) ARegion *region = CTX_wm_region(C); /* Keep the drag position in the region with a small pad to keep the panel visible. */ - const int y = clamp_i(event->y, region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); + const int y = clamp_i(event->xy[1], region->winrct.ymin, region->winrct.ymax + DRAG_REGION_PAD); float dy = (float)(y - data->starty); @@ -2093,7 +2041,7 @@ static int ui_panel_drag_collapse_handler(bContext *C, const wmEvent *event, voi switch (event->type) { case MOUSEMOVE: - ui_panel_drag_collapse(C, dragcol_data, &event->x); + ui_panel_drag_collapse(C, dragcol_data, event->xy); retval = WM_UI_HANDLER_BREAK; break; @@ -2122,7 +2070,7 @@ static void ui_panel_drag_collapse_handler_add(const bContext *C, const bool was uiPanelDragCollapseHandle *dragcol_data = MEM_mallocN(sizeof(*dragcol_data), __func__); dragcol_data->was_first_open = was_open; - copy_v2_v2_int(dragcol_data->xy_init, &event->x); + copy_v2_v2_int(dragcol_data->xy_init, event->xy); WM_event_add_ui_handler(C, &win->modalhandlers, @@ -2152,7 +2100,7 @@ static void ui_handle_panel_header(const bContext *C, BLI_assert(!(panel->type->flag & PANEL_TYPE_NO_HEADER)); const bool is_subpanel = (panel->type->parent != NULL); - const bool use_pin = UI_panel_category_is_visible(region) && !is_subpanel; + const bool use_pin = UI_panel_category_is_visible(region) && UI_panel_can_be_pinned(panel); const bool show_pin = use_pin && (panel->flag & PNL_PIN); const bool show_drag = !is_subpanel; @@ -2415,7 +2363,7 @@ int ui_handler_panel_region(bContext *C, } /* Scroll-bars can overlap panels now, they have handling priority. */ - if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->x, event->y)) { + if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy)) { return WM_UI_HANDLER_CONTINUE; } @@ -2458,8 +2406,8 @@ int ui_handler_panel_region(bContext *C, continue; } - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, block, &mx, &my); const uiPanelMouseState mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); @@ -2537,8 +2485,8 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm continue; } - int mx = event->x; - int my = event->y; + int mx = event->xy[0]; + int my = event->xy[1]; ui_window_to_block(region, block, &mx, &my); const int mouse_state = ui_panel_mouse_state_get(block, panel, mx, my); if (ELEM(mouse_state, PANEL_MOUSE_INSIDE_CONTENT, PANEL_MOUSE_INSIDE_HEADER)) { @@ -2549,6 +2497,11 @@ PointerRNA *UI_region_panel_custom_data_under_cursor(const bContext *C, const wm return NULL; } +bool UI_panel_can_be_pinned(const Panel *panel) +{ + return (panel->type->parent == NULL) && !(panel->type->flag & PANEL_TYPE_INSTANCED); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -2611,8 +2564,8 @@ static void panel_handle_data_ensure(const bContext *C, data->animtimer = WM_event_add_timer(CTX_wm_manager(C), win, TIMER, ANIMATION_INTERVAL); data->state = state; - data->startx = win->eventstate->x; - data->starty = win->eventstate->y; + data->startx = win->eventstate->xy[0]; + data->starty = win->eventstate->xy[1]; data->startofsx = panel->ofsx; data->startofsy = panel->ofsy; data->start_cur_xmin = region->v2d.cur.xmin; diff --git a/source/blender/editors/interface/interface_query.c b/source/blender/editors/interface/interface_query.c index 09429bb6df5..bdf93d7c82e 100644 --- a/source/blender/editors/interface/interface_query.c +++ b/source/blender/editors/interface/interface_query.c @@ -69,7 +69,8 @@ bool ui_but_is_toggle(const uiBut *but) UI_BTYPE_CHECKBOX, UI_BTYPE_CHECKBOX_N, UI_BTYPE_ROW, - UI_BTYPE_DATASETROW); + UI_BTYPE_DATASETROW, + UI_BTYPE_TREEROW); } /** @@ -221,14 +222,14 @@ bool ui_but_contains_rect(const uiBut *but, const rctf *rect) return BLI_rctf_isect(&but->rect, rect, NULL); } -bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, int x, int y) +bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, const int xy[2]) { uiBlock *block = but->block; - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return false; } - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); if (but->pie_dir != UI_RADIAL_NONE) { @@ -246,7 +247,7 @@ bool ui_but_contains_point_px(const uiBut *but, const ARegion *region, int x, in bool ui_but_contains_point_px_icon(const uiBut *but, ARegion *region, const wmEvent *event) { rcti rect; - int x = event->x, y = event->y; + int x = event->xy[0], y = event->xy[1]; ui_window_to_block(region, but->block, &x, &y); @@ -285,19 +286,18 @@ static uiBut *ui_but_find(const ARegion *region, /* x and y are only used in case event is NULL... */ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, - const int x, - const int y, + const int xy[2], const bool labeledit, const uiButFindPollFn find_poll, const void *find_custom_data) { uiBut *butover = NULL; - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return NULL; } LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { @@ -332,7 +332,7 @@ uiBut *ui_but_find_mouse_over_ex(const ARegion *region, uiBut *ui_but_find_mouse_over(const ARegion *region, const wmEvent *event) { - return ui_but_find_mouse_over_ex(region, event->x, event->y, event->ctrl != 0, NULL, NULL); + return ui_but_find_mouse_over_ex(region, event->xy, event->ctrl != 0, NULL, NULL); } uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) @@ -373,13 +373,13 @@ uiBut *ui_but_find_rect_over(const struct ARegion *region, const rcti *rect_px) return butover; } -uiBut *ui_list_find_mouse_over_ex(const ARegion *region, int x, int y) +uiBut *ui_list_find_mouse_over_ex(const ARegion *region, const int xy[2]) { - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return NULL; } LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block->buttons) { if (but->type == UI_BTYPE_LISTBOX && ui_but_contains_pt(but, mx, my)) { @@ -397,7 +397,7 @@ uiBut *ui_list_find_mouse_over(const ARegion *region, const wmEvent *event) /* If there is no info about the mouse, just act as if there is nothing underneath it. */ return NULL; } - return ui_list_find_mouse_over_ex(region, event->x, event->y); + return ui_list_find_mouse_over_ex(region, event->xy); } uiList *UI_list_find_mouse_over(const ARegion *region, const wmEvent *event) @@ -434,9 +434,9 @@ static bool ui_but_is_listrow(const uiBut *but, const void *UNUSED(customdata)) return but->type == UI_BTYPE_LISTROW; } -uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int x, const int y) +uiBut *ui_list_row_find_mouse_over(const ARegion *region, const int xy[2]) { - return ui_but_find_mouse_over_ex(region, x, y, false, ui_but_is_listrow, NULL); + return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_listrow, NULL); } struct ListRowFindIndexData { @@ -462,6 +462,31 @@ uiBut *ui_list_row_find_from_index(const ARegion *region, const int index, uiBut return ui_but_find(region, ui_but_is_listrow_at_index, &data); } +static bool ui_but_is_treerow(const uiBut *but, const void *UNUSED(customdata)) +{ + return but->type == UI_BTYPE_TREEROW; +} + +uiBut *ui_tree_row_find_mouse_over(const ARegion *region, const int xy[2]) +{ + return ui_but_find_mouse_over_ex(region, xy, false, ui_but_is_treerow, NULL); +} + +static bool ui_but_is_active_treerow(const uiBut *but, const void *customdata) +{ + if (!ui_but_is_treerow(but, customdata)) { + return false; + } + + const uiButTreeRow *treerow_but = (const uiButTreeRow *)but; + return UI_tree_view_item_is_active(treerow_but->tree_item); +} + +uiBut *ui_tree_row_find_active(const ARegion *region) +{ + return ui_but_find(region, ui_but_is_active_treerow, NULL); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -656,12 +681,9 @@ bool UI_block_can_add_separator(const uiBlock *block) /** \name Block (#uiBlock) Spatial * \{ */ -uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, - const int x, - const int y, - bool only_clip) +uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, const int xy[2], bool only_clip) { - if (!ui_region_contains_point_px(region, x, y)) { + if (!ui_region_contains_point_px(region, xy)) { return NULL; } LISTBASE_FOREACH (uiBlock *, block, ®ion->uiblocks) { @@ -670,7 +692,7 @@ uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, continue; } } - float mx = x, my = y; + float mx = xy[0], my = xy[1]; ui_window_to_block_fl(region, block, &mx, &my); if (BLI_rctf_isect_pt(&block->rect, mx, my)) { return block; @@ -681,7 +703,7 @@ uiBlock *ui_block_find_mouse_over_ex(const ARegion *region, uiBlock *ui_block_find_mouse_over(const ARegion *region, const wmEvent *event, bool only_clip) { - return ui_block_find_mouse_over_ex(region, event->x, event->y, only_clip); + return ui_block_find_mouse_over_ex(region, event->xy, only_clip); } /** \} */ @@ -721,11 +743,11 @@ uiBut *ui_region_find_first_but_test_flag(ARegion *region, int flag_include, int /** \name Region (#ARegion) Spatial * \{ */ -bool ui_region_contains_point_px(const ARegion *region, int x, int y) +bool ui_region_contains_point_px(const ARegion *region, const int xy[2]) { rcti winrct; ui_region_winrct_get_no_margin(region, &winrct); - if (!BLI_rcti_isect_pt(&winrct, x, y)) { + if (!BLI_rcti_isect_pt_v(&winrct, xy)) { return false; } @@ -736,11 +758,11 @@ bool ui_region_contains_point_px(const ARegion *region, int x, int y) */ if (region->v2d.mask.xmin != region->v2d.mask.xmax) { const View2D *v2d = ®ion->v2d; - int mx = x, my = y; + int mx = xy[0], my = xy[1]; ui_window_to_region(region, &mx, &my); if (!BLI_rcti_isect_pt(&v2d->mask, mx, my) || - UI_view2d_mouse_in_scrollers(region, ®ion->v2d, x, y)) { + UI_view2d_mouse_in_scrollers(region, ®ion->v2d, xy)) { return false; } } @@ -777,14 +799,14 @@ bool ui_region_contains_rect_px(const ARegion *region, const rcti *rect_px) * \{ */ /** Check if the cursor is over any popups. */ -ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, int x, int y) +ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, const int xy[2]) { LISTBASE_FOREACH (ARegion *, region, &screen->regionbase) { rcti winrct; ui_region_winrct_get_no_margin(region, &winrct); - if (BLI_rcti_isect_pt(&winrct, x, y)) { + if (BLI_rcti_isect_pt_v(&winrct, xy)) { return region; } } @@ -793,7 +815,7 @@ ARegion *ui_screen_region_find_mouse_over_ex(bScreen *screen, int x, int y) ARegion *ui_screen_region_find_mouse_over(bScreen *screen, const wmEvent *event) { - return ui_screen_region_find_mouse_over_ex(screen, event->x, event->y); + return ui_screen_region_find_mouse_over_ex(screen, event->xy); } /** \} */ diff --git a/source/blender/editors/interface/interface_region_menu_pie.c b/source/blender/editors/interface/interface_region_menu_pie.c index 05aa139e055..01562b25da1 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.c +++ b/source/blender/editors/interface/interface_region_menu_pie.c @@ -147,9 +147,9 @@ uiPieMenu *UI_pie_menu_begin(struct bContext *C, const char *title, int icon, co pie->layout = UI_block_layout( pie->block_radial, UI_LAYOUT_VERTICAL, UI_LAYOUT_PIEMENU, 0, 0, 200, 0, 0, style); - /* Note event->x/y is where we started dragging in case of KM_CLICK_DRAG. */ - pie->mx = event->x; - pie->my = event->y; + /* NOTE: #wmEvent.xy is where we started dragging in case of #KM_CLICK_DRAG. */ + pie->mx = event->xy[0]; + pie->my = event->xy[1]; /* create title button */ if (title[0]) { diff --git a/source/blender/editors/interface/interface_region_menu_popup.c b/source/blender/editors/interface/interface_region_menu_popup.c index d3c1a97e957..4e20466326e 100644 --- a/source/blender/editors/interface/interface_region_menu_popup.c +++ b/source/blender/editors/interface/interface_region_menu_popup.c @@ -338,8 +338,8 @@ uiPopupBlockHandle *ui_popup_menu_create( if (!but) { /* no button to start from, means we are a popup */ - pup->mx = window->eventstate->x; - pup->my = window->eventstate->y; + pup->mx = window->eventstate->xy[0]; + pup->my = window->eventstate->xy[1]; pup->popup = true; pup->block->flag |= UI_BLOCK_NO_FLIP; } @@ -468,8 +468,8 @@ void UI_popup_menu_end(bContext *C, uiPopupMenu *pup) ARegion *butregion = NULL; pup->popup = true; - pup->mx = window->eventstate->x; - pup->my = window->eventstate->y; + pup->mx = window->eventstate->xy[0]; + pup->my = window->eventstate->xy[1]; if (pup->but) { but = pup->but; diff --git a/source/blender/editors/interface/interface_region_popup.c b/source/blender/editors/interface/interface_region_popup.c index 55a162c883a..0e53100f91b 100644 --- a/source/blender/editors/interface/interface_region_popup.c +++ b/source/blender/editors/interface/interface_region_popup.c @@ -184,10 +184,10 @@ static void ui_popup_block_position(wmWindow *window, dir1 &= (UI_DIR_UP | UI_DIR_DOWN); } - if ((dir2 == 0) && (dir1 == UI_DIR_LEFT || dir1 == UI_DIR_RIGHT)) { + if ((dir2 == 0) && (ELEM(dir1, UI_DIR_LEFT, UI_DIR_RIGHT))) { dir2 = UI_DIR_DOWN; } - if ((dir2 == 0) && (dir1 == UI_DIR_UP || dir1 == UI_DIR_DOWN)) { + if ((dir2 == 0) && (ELEM(dir1, UI_DIR_UP, UI_DIR_DOWN))) { dir2 = UI_DIR_LEFT; } @@ -803,7 +803,7 @@ uiPopupBlockHandle *ui_popup_block_create(bContext *C, handle->popup_create_vars.arg_free = arg_free; handle->popup_create_vars.but = but; handle->popup_create_vars.butregion = but ? butregion : NULL; - copy_v2_v2_int(handle->popup_create_vars.event_xy, &window->eventstate->x); + copy_v2_v2_int(handle->popup_create_vars.event_xy, window->eventstate->xy); /* don't allow by default, only if popup type explicitly supports it */ handle->can_refresh = false; diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index c863b1f8bdf..5bea03dee63 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -290,11 +290,11 @@ int ui_searchbox_find_index(ARegion *region, const char *name) } /* x and y in screen-coords. */ -bool ui_searchbox_inside(ARegion *region, int x, int y) +bool ui_searchbox_inside(ARegion *region, const int xy[2]) { uiSearchboxData *data = region->regiondata; - return BLI_rcti_isect_pt(&data->bbox, x - region->winrct.xmin, y - region->winrct.ymin); + return BLI_rcti_isect_pt(&data->bbox, xy[0] - region->winrct.xmin, xy[1] - region->winrct.ymin); } /* string validated to be of correct length (but->hardmax) */ @@ -316,7 +316,11 @@ bool ui_searchbox_apply(uiBut *but, ARegion *region) const char *name_sep = data->use_shortcut_sep ? strrchr(name, UI_SEP_CHAR) : NULL; - BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); + /* Search button with dynamic string properties may have their own method of applying + * the search results, so only copy the result if there is a proper space for it. */ + if (but->hardmax != 0) { + BLI_strncpy(but->editstr, name, name_sep ? (name_sep - name) + 1 : data->items.maxstrlen); + } search_but->item_active = data->items.pointers[data->active]; @@ -400,8 +404,9 @@ bool ui_searchbox_event( * (a little confusing if this isn't the case, although it does work). */ rcti rect; ui_searchbox_butrect(&rect, data, data->active); - if (BLI_rcti_isect_pt( - &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) { + if (BLI_rcti_isect_pt(&rect, + event->xy[0] - region->winrct.xmin, + event->xy[1] - region->winrct.ymin)) { void *active = data->items.pointers[data->active]; if (search_but->item_context_menu_fn(C, search_but->arg, active, event)) { @@ -415,14 +420,14 @@ bool ui_searchbox_event( case MOUSEMOVE: { bool is_inside = false; - if (BLI_rcti_isect_pt(®ion->winrct, event->x, event->y)) { + if (BLI_rcti_isect_pt(®ion->winrct, event->xy[0], event->xy[1])) { rcti rect; int a; for (a = 0; a < data->items.totitem; a++) { ui_searchbox_butrect(&rect, data, a); if (BLI_rcti_isect_pt( - &rect, event->x - region->winrct.xmin, event->y - region->winrct.ymin)) { + &rect, event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin)) { is_inside = true; if (data->active != a) { data->active = a; @@ -877,7 +882,8 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, else { data->items.maxitem = SEARCH_ITEMS; } - data->items.maxstrlen = but->hardmax; + /* In case the button's string is dynamic, make sure there are buffers available. */ + data->items.maxstrlen = but->hardmax == 0 ? UI_MAX_NAME_STR : but->hardmax; data->items.totitem = 0; data->items.names = MEM_callocN(data->items.maxitem * sizeof(void *), "search names"); data->items.pointers = MEM_callocN(data->items.maxitem * sizeof(void *), "search pointers"); @@ -885,7 +891,7 @@ static ARegion *ui_searchbox_create_generic_ex(bContext *C, data->items.states = MEM_callocN(data->items.maxitem * sizeof(int), "search flags"); data->items.name_prefix_offsets = NULL; /* Lazy initialized as needed. */ for (int i = 0; i < data->items.maxitem; i++) { - data->items.names[i] = MEM_callocN(but->hardmax + 1, "search pointers"); + data->items.names[i] = MEM_callocN(data->items.maxstrlen + 1, "search pointers"); } return region; diff --git a/source/blender/editors/interface/interface_region_tooltip.c b/source/blender/editors/interface/interface_region_tooltip.c index a8f289702f8..eb25d896d26 100644 --- a/source/blender/editors/interface/interface_region_tooltip.c +++ b/source/blender/editors/interface/interface_region_tooltip.c @@ -761,7 +761,9 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is return data; } -static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) +static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C, + uiBut *but, + uiButExtraOpIcon *extra_icon) { uiStringInfo but_label = {BUT_GET_LABEL, NULL}; uiStringInfo but_tip = {BUT_GET_TIP, NULL}; @@ -774,20 +776,29 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) char buf[512]; + wmOperatorType *optype = extra_icon ? UI_but_extra_operator_icon_optype_get(extra_icon) : + but->optype; + PropertyRNA *rnaprop = extra_icon ? NULL : but->rnaprop; + /* create tooltip data */ uiTooltipData *data = MEM_callocN(sizeof(uiTooltipData), "uiTooltipData"); - UI_but_string_info_get(C, - but, - &but_label, - &but_tip, - &enum_label, - &enum_tip, - &op_keymap, - &prop_keymap, - &rna_struct, - &rna_prop, - NULL); + if (extra_icon) { + UI_but_extra_icon_string_info_get(C, extra_icon, &but_label, &but_tip, &op_keymap, NULL); + } + else { + UI_but_string_info_get(C, + but, + &but_label, + &but_tip, + &enum_label, + &enum_tip, + &op_keymap, + &prop_keymap, + &rna_struct, + &rna_prop, + NULL); + } /* Tip Label (only for buttons not already showing the label). * Check prefix instead of comparing because the button may include the shortcut. */ @@ -818,8 +829,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } /* special case enum rna buttons */ - if ((but->type & UI_BTYPE_ROW) && but->rnaprop && - RNA_property_flag(but->rnaprop) & PROP_ENUM_FLAG) { + if ((but->type & UI_BTYPE_ROW) && rnaprop && RNA_property_flag(rnaprop) & PROP_ENUM_FLAG) { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ .style = UI_TIP_STYLE_NORMAL, @@ -863,7 +873,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) if (ELEM(but->type, UI_BTYPE_TEXT, UI_BTYPE_SEARCH_MENU)) { /* better not show the value of a password */ - if ((but->rnaprop && (RNA_property_subtype(but->rnaprop) == PROP_PASSWORD)) == 0) { + if ((rnaprop && (RNA_property_subtype(rnaprop) == PROP_PASSWORD)) == 0) { /* full string */ ui_but_string_get(but, buf, sizeof(buf)); if (buf[0]) { @@ -878,15 +888,14 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } - if (but->rnaprop) { + if (rnaprop) { const int unit_type = UI_but_unit_type_get(but); if (unit_type == PROP_UNIT_ROTATION) { - if (RNA_property_type(but->rnaprop) == PROP_FLOAT) { - float value = RNA_property_array_check(but->rnaprop) ? - RNA_property_float_get_index( - &but->rnapoin, but->rnaprop, but->rnaindex) : - RNA_property_float_get(&but->rnapoin, but->rnaprop); + if (RNA_property_type(rnaprop) == PROP_FLOAT) { + float value = RNA_property_array_check(rnaprop) ? + RNA_property_float_get_index(&but->rnapoin, rnaprop, but->rnaindex) : + RNA_property_float_get(&but->rnapoin, rnaprop); uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ @@ -920,15 +929,15 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } } - else if (but->optype) { - PointerRNA *opptr; - char *str; - opptr = UI_but_operator_ptr_get(but); /* allocated when needed, the button owns it */ + else if (optype) { + PointerRNA *opptr = extra_icon ? UI_but_extra_operator_icon_opptr_get(extra_icon) : + /* allocated when needed, the button owns it */ + UI_but_operator_ptr_get(but); /* so the context is passed to fieldf functions (some py fieldf functions use it) */ WM_operator_properties_sanitize(opptr, false); - str = ui_tooltip_text_python_from_op(C, but->optype, opptr); + char *str = ui_tooltip_text_python_from_op(C, optype, opptr); /* operator info */ if (U.flag & USER_TOOLTIPS_PYTHON) { @@ -945,18 +954,20 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } /* button is disabled, we may be able to tell user why */ - if (but->flag & UI_BUT_DISABLED) { + if ((but->flag & UI_BUT_DISABLED) || extra_icon) { const char *disabled_msg = NULL; bool disabled_msg_free = false; /* if operator poll check failed, it can give pretty precise info why */ - if (but->optype) { + if (optype) { + const int opcontext = extra_icon ? extra_icon->optype_params->opcontext : but->opcontext; CTX_wm_operator_poll_msg_clear(C); - WM_operator_poll_context(C, but->optype, but->opcontext); + ui_but_context_poll_operator_ex( + C, but, &(wmOperatorCallParams){.optype = optype, .opcontext = opcontext}); disabled_msg = CTX_wm_operator_poll_msg_get(C, &disabled_msg_free); } /* alternatively, buttons can store some reasoning too */ - else if (but->disabled_info) { + else if (!extra_icon && but->disabled_info) { disabled_msg = TIP_(but->disabled_info); } @@ -973,7 +984,7 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) } } - if ((U.flag & USER_TOOLTIPS_PYTHON) && !but->optype && rna_struct.strinfo) { + if ((U.flag & USER_TOOLTIPS_PYTHON) && !optype && rna_struct.strinfo) { { uiTooltipField *field = text_field_add(data, &(uiTooltipFormat){ @@ -1002,9 +1013,9 @@ static uiTooltipData *ui_tooltip_data_from_button(bContext *C, uiBut *but) /* never fails */ /* move ownership (no need for re-alloc) */ - if (but->rnaprop) { + if (rnaprop) { field->text = RNA_path_full_property_py_ex( - CTX_data_main(C), &but->rnapoin, but->rnaprop, but->rnaindex, true); + CTX_data_main(C), &but->rnapoin, rnaprop, but->rnaindex, true); } else { field->text = RNA_path_full_struct_py(CTX_data_main(C), &but->rnapoin); @@ -1394,11 +1405,8 @@ static ARegion *ui_tooltip_create_with_data(bContext *C, /** \name ToolTip Public API * \{ */ -/** - * \param is_label: When true, show a small tip that only shows the name, - * otherwise show the full tooltip. - */ -ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label) +ARegion *UI_tooltip_create_from_button_or_extra_icon( + bContext *C, ARegion *butregion, uiBut *but, uiButExtraOpIcon *extra_icon, bool is_label) { wmWindow *win = CTX_wm_window(C); /* aspect values that shrink text are likely unreadable */ @@ -1415,7 +1423,11 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b } if (data == NULL) { - data = ui_tooltip_data_from_button(C, but); + data = ui_tooltip_data_from_button_or_extra_icon(C, but, extra_icon); + } + + if (data == NULL) { + data = ui_tooltip_data_from_button_or_extra_icon(C, but, NULL); } if (data == NULL) { @@ -1442,7 +1454,7 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b init_position[1] = but->rect.ymin; if (butregion) { ui_block_to_window_fl(butregion, but->block, &init_position[0], &init_position[1]); - init_position[0] = win->eventstate->x; + init_position[0] = win->eventstate->xy[0]; } init_position[1] -= (UI_POPUP_MARGIN / 2); } @@ -1453,11 +1465,20 @@ ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *b return region; } +/** + * \param is_label: When true, show a small tip that only shows the name, otherwise show the full + * tooltip. + */ +ARegion *UI_tooltip_create_from_button(bContext *C, ARegion *butregion, uiBut *but, bool is_label) +{ + return UI_tooltip_create_from_button_or_extra_icon(C, butregion, but, NULL, is_label); +} + ARegion *UI_tooltip_create_from_gizmo(bContext *C, wmGizmo *gz) { wmWindow *win = CTX_wm_window(C); const float aspect = 1.0f; - float init_position[2] = {win->eventstate->x, win->eventstate->y}; + float init_position[2] = {win->eventstate->xy[0], win->eventstate->xy[1]}; uiTooltipData *data = ui_tooltip_data_from_gizmo(C, gz); if (data == NULL) { @@ -1541,7 +1562,7 @@ ARegion *UI_tooltip_create_from_search_item_generic( const float aspect = 1.0f; const wmWindow *win = CTX_wm_window(C); float init_position[2]; - init_position[0] = win->eventstate->x; + init_position[0] = win->eventstate->xy[0]; init_position[1] = item_rect->ymin + searchbox_region->winrct.ymin - (UI_POPUP_MARGIN / 2); return ui_tooltip_create_with_data(C, data, init_position, NULL, aspect); diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 804156ba48c..92a9f14c77d 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -312,11 +312,8 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, const float decent = BLF_descender(fs->uifont_id); const float margin = height / 4.0f; - /* backdrop */ - const float color[4] = {col_bg[0], col_bg[1], col_bg[2], 0.5f}; - UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = x - margin, .xmax = x + width + margin, @@ -325,7 +322,7 @@ void UI_fontstyle_draw_simple_backdrop(const uiFontStyle *fs, }, true, margin, - color); + col_bg); } BLF_position(fs->uifont_id, x, y, 0.0f); @@ -379,6 +376,37 @@ int UI_fontstyle_string_width(const uiFontStyle *fs, const char *str) return (int)BLF_width(fs->uifont_id, str, BLF_DRAW_STR_DUMMY_MAX); } +/** + * Return the width of `str` with the spacing & kerning of `fs` with `aspect` + * (representing #uiBlock.aspect) applied. + * + * When calculating text width, the UI layout logic calculate widths without scale, + * only applying scale when drawing. This causes problems for fonts since kerning at + * smaller sizes often makes them wider than a scaled down version of the larger text. + * Resolve this by calculating the text at the on-screen size, + * returning the result scaled back to 1:1. See T92361. + */ +int UI_fontstyle_string_width_with_block_aspect(const uiFontStyle *fs, + const char *str, + const float aspect) +{ + uiFontStyle fs_buf; + if (aspect != 1.0f) { + fs_buf = *fs; + ui_fontscale(&fs_buf.points, aspect); + fs = &fs_buf; + } + + int width = UI_fontstyle_string_width(fs, str); + + if (aspect != 1.0f) { + /* While in most cases rounding up isn't important, it can make a difference + * with small fonts (3px or less), zooming out in the node-editor for e.g. */ + width = (int)ceilf(width * aspect); + } + return width; +} + int UI_fontstyle_height_max(const uiFontStyle *fs) { UI_fontstyle_set(fs); diff --git a/source/blender/editors/interface/interface_template_asset_view.cc b/source/blender/editors/interface/interface_template_asset_view.cc index f27b37a27de..d3ce7ebc3db 100644 --- a/source/blender/editors/interface/interface_template_asset_view.cc +++ b/source/blender/editors/interface/interface_template_asset_view.cc @@ -70,6 +70,7 @@ static void asset_view_item_but_drag_set(uiBut *but, UI_but_drag_set_asset(but, asset_handle, BLI_strdup(blend_path), + ED_asset_handle_get_metadata(asset_handle), FILE_ASSET_IMPORT_APPEND, ED_asset_handle_get_preview_icon_id(asset_handle), imbuf, diff --git a/source/blender/editors/interface/interface_template_attribute_search.cc b/source/blender/editors/interface/interface_template_attribute_search.cc new file mode 100644 index 00000000000..85a6147432b --- /dev/null +++ b/source/blender/editors/interface/interface_template_attribute_search.cc @@ -0,0 +1,126 @@ +/* + * 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 edinterface + */ + +#include "BLI_string_ref.hh" +#include "BLI_string_search.h" + +#include "DNA_customdata_types.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + +#include "BLT_translation.h" + +#include "NOD_geometry_nodes_eval_log.hh" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +using blender::nodes::geometry_nodes_eval_log::GeometryAttributeInfo; + +namespace blender::ui { + +static StringRef attribute_data_type_string(const CustomDataType type) +{ + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name); + return StringRef(IFACE_(name)); +} + +static StringRef attribute_domain_string(const AttributeDomain domain) +{ + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name); + return StringRef(IFACE_(name)); +} + +static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) +{ + const StringRef data_type_name = attribute_data_type_string(item.data_type); + const StringRef domain_name = attribute_domain_string(item.domain); + std::string search_item_text = domain_name + " " + UI_MENU_ARROW_SEP + item.name + UI_SEP_CHAR + + data_type_name; + + return UI_search_item_add( + items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0); +} + +void attribute_search_add_items(StringRefNull str, + const bool is_output, + Span infos, + uiSearchItems *seach_items, + const bool is_first) +{ + static GeometryAttributeInfo dummy_info; + + /* Any string may be valid, so add the current search string along with the hints. */ + if (str[0] != '\0') { + bool contained = false; + for (const GeometryAttributeInfo *attribute_info : infos) { + if (attribute_info->name == str) { + contained = true; + break; + } + } + if (!contained) { + dummy_info.name = str; + UI_search_item_add( + seach_items, str.c_str(), &dummy_info, is_output ? ICON_ADD : ICON_NONE, 0, 0); + } + } + + if (str[0] == '\0' && !is_first) { + /* Allow clearing the text field when the string is empty, but not on the first pass, + * or opening an attribute field for the first time would show this search item. */ + dummy_info.name = str; + UI_search_item_add(seach_items, str.c_str(), &dummy_info, ICON_X, 0, 0); + } + + /* Don't filter when the menu is first opened, but still run the search + * so the items are in the same order they will appear in while searching. */ + const char *string = is_first ? "" : str.c_str(); + + StringSearch *search = BLI_string_search_new(); + for (const GeometryAttributeInfo *item : infos) { + + /* Don't show the legacy "normal" attribute. */ + if (item->name == "normal" && item->domain == ATTR_DOMAIN_FACE) { + continue; + } + + BLI_string_search_add(search, item->name.c_str(), (void *)item); + } + + GeometryAttributeInfo **filtered_items; + const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); + + for (const int i : IndexRange(filtered_amount)) { + const GeometryAttributeInfo *item = filtered_items[i]; + if (!attribute_search_item_add(seach_items, *item)) { + break; + } + } + + MEM_freeN(filtered_items); + BLI_string_search_free(search); +} + +} // namespace blender::ui diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index 8246759ad36..845a7813da2 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -31,6 +31,7 @@ #include "BLT_translation.h" +#include "ED_asset.h" #include "ED_screen.h" #include "MEM_guardedalloc.h" @@ -216,7 +217,18 @@ static void uilist_filter_items_default(struct uiList *ui_list, RNA_PROP_BEGIN (dataptr, itemptr, prop) { bool do_order = false; - char *namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr); + char *namebuf; + if (RNA_struct_is_a(itemptr.type, &RNA_AssetHandle)) { + /* XXX The AssetHandle design is hacky and meant to be temporary. It can't have a proper + * name property, so for now this hardcoded exception is needed. */ + AssetHandle *asset_handle = (AssetHandle *)itemptr.data; + const char *asset_name = ED_asset_handle_get_name(asset_handle); + namebuf = BLI_strdup(asset_name); + } + else { + namebuf = RNA_struct_name_get_alloc(&itemptr, nullptr, 0, nullptr); + } + const char *name = namebuf ? namebuf : ""; if (filter[0]) { diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 672f1b64943..5877b4fe6d7 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -69,9 +69,6 @@ /** \name Menu Search Template Implementation * \{ */ -/* Unicode arrow. */ -#define MENU_SEP "\xe2\x96\xb6" - /** * Use when #menu_items_from_ui_create is called with `include_all_areas`. * so we can run the menu item in the area it was extracted from. @@ -350,24 +347,28 @@ static void menu_types_add_from_keymap_items(bContext *C, if (handler_base->poll == NULL || handler_base->poll(region, win->eventstate)) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - if (keymap && WM_keymap_poll(C, keymap)) { - LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { - if (kmi->flag & KMI_INACTIVE) { - continue; - } - if (STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie")) { - char menu_idname[MAX_NAME]; - RNA_string_get(kmi->ptr, "name", menu_idname); - MenuType *mt = WM_menutype_find(menu_idname, false); + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + if (keymap && WM_keymap_poll(C, keymap)) { + LISTBASE_FOREACH (wmKeyMapItem *, kmi, &keymap->items) { + if (kmi->flag & KMI_INACTIVE) { + continue; + } + if (STR_ELEM(kmi->idname, "WM_OT_call_menu", "WM_OT_call_menu_pie")) { + char menu_idname[MAX_NAME]; + RNA_string_get(kmi->ptr, "name", menu_idname); + MenuType *mt = WM_menutype_find(menu_idname, false); - if (mt && BLI_gset_add(menu_tagged, mt)) { - /* Unlikely, but possible this will be included twice. */ - BLI_linklist_prepend(menuid_stack_p, mt); + if (mt && BLI_gset_add(menu_tagged, mt)) { + /* Unlikely, but possible this will be included twice. */ + BLI_linklist_prepend(menuid_stack_p, mt); - void **kmi_p; - if (!BLI_ghash_ensure_p(menu_to_kmi, mt, &kmi_p)) { - *kmi_p = kmi; + void **kmi_p; + if (!BLI_ghash_ensure_p(menu_to_kmi, mt, &kmi_p)) { + *kmi_p = kmi; + } } } } @@ -411,7 +412,7 @@ static void menu_items_from_all_operators(bContext *C, struct MenuSearch_Data *d char uiname[256]; WM_operator_py_idname(idname_as_py, ot->idname); - SNPRINTF(uiname, "%s " MENU_SEP "%s", idname_as_py, ot_ui_name); + SNPRINTF(uiname, "%s " UI_MENU_ARROW_SEP "%s", idname_as_py, ot_ui_name); item->drawwstr_full = strdup_memarena(memarena, uiname); item->drawstr = ot_ui_name; @@ -837,7 +838,7 @@ static struct MenuSearch_Data *menu_items_from_ui_create( } while (menu_parent) { BLI_dynstr_append(dyn_str, menu_parent->drawstr); - BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + BLI_dynstr_append(dyn_str, " " UI_MENU_ARROW_SEP " "); menu_parent = menu_parent->temp_child; } } @@ -855,13 +856,13 @@ static struct MenuSearch_Data *menu_items_from_ui_create( BLI_dynstr_appendf(dyn_str, " (%s)", kmi_str); } - BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + BLI_dynstr_append(dyn_str, " " UI_MENU_ARROW_SEP " "); } /* Optional nested menu. */ if (item->drawstr_submenu != NULL) { BLI_dynstr_append(dyn_str, item->drawstr_submenu); - BLI_dynstr_append(dyn_str, " " MENU_SEP " "); + BLI_dynstr_append(dyn_str, " " UI_MENU_ARROW_SEP " "); } BLI_dynstr_append(dyn_str, item->drawstr); @@ -1098,8 +1099,8 @@ static struct ARegion *ui_search_menu_create_tooltip(struct bContext *C, /* Place the fake button at the cursor so the tool-tip is places properly. */ float tip_init[2]; const wmEvent *event = CTX_wm_window(C)->eventstate; - tip_init[0] = event->x; - tip_init[1] = event->y - (UI_UNIT_Y / 2); + tip_init[0] = event->xy[0]; + tip_init[1] = event->xy[1] - (UI_UNIT_Y / 2); ui_window_to_block_fl(region, block, &tip_init[0], &tip_init[1]); but->rect.xmin = tip_init[0]; @@ -1156,7 +1157,7 @@ void UI_but_func_menu_search(uiBut *but) UI_but_func_search_set_context_menu(but, ui_search_menu_create_context_menu); UI_but_func_search_set_tooltip(but, ui_search_menu_create_tooltip); - UI_but_func_search_set_sep_string(but, MENU_SEP); + UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); } void uiTemplateMenuSearch(uiLayout *layout) @@ -1173,6 +1174,4 @@ void uiTemplateMenuSearch(uiLayout *layout) UI_but_func_menu_search(but); } -#undef MENU_SEP - /** \} */ diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 0c9eb20af19..1d349aa0596 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -1112,24 +1112,41 @@ static void template_ID(const bContext *C, UI_but_flag_enable(but, UI_BUT_REDALERT); } - if (!ID_IS_LINKED(id) && !(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_OB, ID_WS)) && - (hide_buttons == false)) { - uiDefIconButR(block, - UI_BTYPE_ICON_TOGGLE, - 0, - ICON_FAKE_USER_OFF, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - &idptr, - "use_fake_user", - -1, - 0, - 0, - -1, - -1, - NULL); + if (!ID_IS_LINKED(id)) { + if (ID_IS_ASSET(id)) { + uiDefIconButO(block, + /* Using `_N` version allows us to get the 'active' state by default. */ + UI_BTYPE_ICON_TOGGLE_N, + "ASSET_OT_clear", + WM_OP_INVOKE_DEFAULT, + /* 'active' state of a toggle button uses icon + 1, so to get proper asset + * icon we need to pass its value - 1 here. */ + ICON_ASSET_MANAGER - 1, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + NULL); + } + else if (!(ELEM(GS(id->name), ID_GR, ID_SCE, ID_SCR, ID_OB, ID_WS)) && + (hide_buttons == false)) { + uiDefIconButR(block, + UI_BTYPE_ICON_TOGGLE, + 0, + ICON_FAKE_USER_OFF, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + &idptr, + "use_fake_user", + -1, + 0, + 0, + -1, + -1, + NULL); + } } } @@ -2364,7 +2381,7 @@ static eAutoPropButsReturn template_operator_property_buts_draw_single( /* poll() on this operator may still fail, * at the moment there is no nice feedback when this happens just fails silently. */ if (!WM_operator_repeat_check(C, op)) { - UI_block_lock_set(block, true, "Operator can't' redo"); + UI_block_lock_set(block, true, "Operator can't redo"); return return_info; } @@ -2726,7 +2743,7 @@ static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *co uiItemR(row, &ptr, "name", 0, "", ICON_NONE); } else { - uiItemL(row, con->name, ICON_NONE); + uiItemL(row, IFACE_(con->name), ICON_NONE); } /* proxy-protected constraints cannot be edited, so hide up/down + close buttons */ @@ -5823,6 +5840,11 @@ void uiTemplateRunningJobs(uiLayout *layout, bContext *C) icon = ICON_SEQUENCE; break; } + if (WM_jobs_test(wm, scene, WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL)) { + handle_event = B_STOPSEQ; + icon = ICON_SEQUENCE; + break; + } if (WM_jobs_test(wm, scene, WM_JOB_TYPE_CLIP_BUILD_PROXY)) { handle_event = B_STOPCLIP; icon = ICON_TRACKER; @@ -6120,8 +6142,8 @@ void uiTemplateInputStatus(uiLayout *layout, struct bContext *C) uiLayout *row = uiLayoutRow(col, true); uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_LEFT); - const char *msg = WM_window_cursor_keymap_status_get(win, i, 0); - const char *msg_drag = WM_window_cursor_keymap_status_get(win, i, 1); + const char *msg = TIP_(WM_window_cursor_keymap_status_get(win, i, 0)); + const char *msg_drag = TIP_(WM_window_cursor_keymap_status_get(win, i, 1)); if (msg || (msg_drag == NULL)) { uiItemL(row, msg ? msg : "", (ICON_MOUSE_LMB + i)); @@ -6476,12 +6498,15 @@ void uiTemplateCacheFile(uiLayout *layout, row = uiLayoutRow(layout, false); /* For Cycles, verify that experimental features are enabled. */ if (BKE_scene_uses_cycles(scene) && !BKE_scene_uses_cycles_experimental_features(scene)) { - uiItemL(row, - "The Cycles Alembic Procedural is only available with the experimental feature set", - ICON_INFO); + uiItemL( + row, + TIP_( + "The Cycles Alembic Procedural is only available with the experimental feature set"), + ICON_INFO); } else { - uiItemL(row, "The active render engine does not have an Alembic Procedural", ICON_INFO); + uiItemL( + row, TIP_("The active render engine does not have an Alembic Procedural"), ICON_INFO); } } diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc new file mode 100644 index 00000000000..4e38f245155 --- /dev/null +++ b/source/blender/editors/interface/interface_view.cc @@ -0,0 +1,186 @@ +/* + * 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 edinterface + * + * This part of the UI-View API is mostly needed to support persistent state of items within the + * view. Views are stored in #uiBlock's, and kept alive with it until after the next redraw. So we + * can compare the old view items with the new view items and keep state persistent for matching + * ones. + */ + +#include +#include + +#include "DNA_screen_types.h" + +#include "BLI_listbase.h" + +#include "interface_intern.h" + +#include "UI_interface.hh" +#include "UI_tree_view.hh" + +using namespace blender; +using namespace blender::ui; + +/** + * Wrapper to store views in a #ListBase. There's no `uiView` base class, we just store views as a + * #std::variant. + */ +struct ViewLink : public Link { + using TreeViewPtr = std::unique_ptr; + + std::string idname; + /* Note: Can't use std::get() on this until minimum macOS deployment target is 10.14. */ + std::variant view; +}; + +template T *get_view_from_link(ViewLink &link) +{ + auto *t_uptr = std::get_if>(&link.view); + return t_uptr ? t_uptr->get() : nullptr; +} + +/** + * Override this for all available tree types. + */ +AbstractTreeView *UI_block_add_view(uiBlock &block, + StringRef idname, + std::unique_ptr tree_view) +{ + ViewLink *view_link = OBJECT_GUARDED_NEW(ViewLink); + BLI_addtail(&block.views, view_link); + + view_link->view = std::move(tree_view); + view_link->idname = idname; + + return get_view_from_link(*view_link); +} + +void ui_block_free_views(uiBlock *block) +{ + LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) { + OBJECT_GUARDED_DELETE(link, ViewLink); + } +} + +/** + * \param x, y: Coordinate to find a tree-row item at, in window space. + */ +uiTreeViewItemHandle *UI_block_tree_view_find_item_at(const ARegion *region, const int xy[2]) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_mouse_over(region, xy); + if (!tree_row_but) { + return nullptr; + } + + return tree_row_but->tree_item; +} + +uiTreeViewItemHandle *UI_block_tree_view_find_active_item(const ARegion *region) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)ui_tree_row_find_active(region); + if (!tree_row_but) { + return nullptr; + } + + return tree_row_but->tree_item; +} + +static StringRef ui_block_view_find_idname(const uiBlock &block, const AbstractTreeView &view) +{ + /* First get the idname the of the view we're looking for. */ + LISTBASE_FOREACH (ViewLink *, view_link, &block.views) { + if (get_view_from_link(*view_link) == &view) { + return view_link->idname; + } + } + + return {}; +} + +static AbstractTreeView *ui_block_view_find_matching_in_old_block(const uiBlock &new_block, + const AbstractTreeView &new_view) +{ + uiBlock *old_block = new_block.oldblock; + if (!old_block) { + return nullptr; + } + + StringRef idname = ui_block_view_find_idname(new_block, new_view); + if (idname.is_empty()) { + return nullptr; + } + + LISTBASE_FOREACH (ViewLink *, old_view_link, &old_block->views) { + if (old_view_link->idname == idname) { + return get_view_from_link(*old_view_link); + } + } + + return nullptr; +} + +uiTreeViewHandle *ui_block_view_find_matching_in_old_block(const uiBlock *new_block, + const uiTreeViewHandle *new_view_handle) +{ + BLI_assert(new_block && new_view_handle); + const AbstractTreeView &new_view = reinterpret_cast(*new_view_handle); + + AbstractTreeView *old_view = ui_block_view_find_matching_in_old_block(*new_block, new_view); + return reinterpret_cast(old_view); +} + +uiButTreeRow *ui_block_view_find_treerow_in_old_block(const uiBlock *new_block, + const uiTreeViewItemHandle *new_item_handle) +{ + uiBlock *old_block = new_block->oldblock; + if (!old_block) { + return nullptr; + } + + const AbstractTreeViewItem &new_item = *reinterpret_cast( + new_item_handle); + const AbstractTreeView *old_tree_view = ui_block_view_find_matching_in_old_block( + *new_block, new_item.get_tree_view()); + if (!old_tree_view) { + return nullptr; + } + + LISTBASE_FOREACH (uiBut *, old_but, &old_block->buttons) { + if (old_but->type != UI_BTYPE_TREEROW) { + continue; + } + uiButTreeRow *old_treerow_but = (uiButTreeRow *)old_but; + if (!old_treerow_but->tree_item) { + continue; + } + AbstractTreeViewItem &old_item = *reinterpret_cast( + old_treerow_but->tree_item); + /* Check if the row is from the expected tree-view. */ + if (&old_item.get_tree_view() != old_tree_view) { + continue; + } + + if (UI_tree_view_item_matches(new_item_handle, old_treerow_but->tree_item)) { + return old_treerow_but; + } + } + + return nullptr; +} diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 0dc7c2d3f9a..3e9042d29a0 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -115,6 +115,7 @@ typedef enum { UI_WTYPE_PROGRESSBAR, UI_WTYPE_NODESOCKET, UI_WTYPE_DATASETROW, + UI_WTYPE_TREEROW, } uiWidgetTypeEnum; /* Button state argument shares bits with 'uiBut.flag'. @@ -517,7 +518,7 @@ GPUBatch *ui_batch_roundbox_shadow_get(void) /** \name Draw Triangle Arrow * \{ */ -void UI_draw_anti_tria( +static void draw_anti_tria( float x1, float y1, float x2, float y2, float x3, float y3, const float color[4]) { const float tri_arr[3][2] = {{x1, y1}, {x2, y2}, {x3, y3}}; @@ -558,66 +559,31 @@ void UI_draw_icon_tri(float x, float y, char dir, const float color[4]) const float f7 = 0.25 * U.widget_unit; if (dir == 'h') { - UI_draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); + draw_anti_tria(x - f3, y - f5, x - f3, y + f5, x + f7, y, color); } else if (dir == 't') { - UI_draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color); + draw_anti_tria(x - f5, y - f7, x + f5, y - f7, x, y + f3, color); } else { /* 'v' = vertical, down. */ - UI_draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color); + draw_anti_tria(x - f5, y + f3, x + f5, y + f3, x, y - f7, color); } } /* triangle 'icon' inside rect */ -void ui_draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) +static void draw_anti_tria_rect(const rctf *rect, char dir, const float color[4]) { if (dir == 'h') { const float half = 0.5f * BLI_rctf_size_y(rect); - UI_draw_anti_tria( + draw_anti_tria( rect->xmin, rect->ymin, rect->xmin, rect->ymax, rect->xmax, rect->ymin + half, color); } else { const float half = 0.5f * BLI_rctf_size_x(rect); - UI_draw_anti_tria( + draw_anti_tria( rect->xmin, rect->ymax, rect->xmax, rect->ymax, rect->xmin + half, rect->ymin, color); } } -void UI_draw_anti_fan(float tri_array[][2], uint length, const float color[4]) -{ - float draw_color[4]; - - copy_v4_v4(draw_color, color); - draw_color[3] *= 2.0f / WIDGET_AA_JITTER; - - GPU_blend(GPU_BLEND_ALPHA); - - const uint pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - immUniformColor4fv(draw_color); - - /* for each AA step */ - for (int j = 0; j < WIDGET_AA_JITTER; j++) { - immBegin(GPU_PRIM_TRI_FAN, length); - immVertex2f(pos, tri_array[0][0], tri_array[0][1]); - immVertex2f(pos, tri_array[1][0], tri_array[1][1]); - - /* We jitter only the middle of the fan, the extremes are pinned. */ - for (int i = 2; i < length - 1; i++) { - immVertex2f(pos, tri_array[i][0] + jit[j][0], tri_array[i][1] + jit[j][1]); - } - - immVertex2f(pos, tri_array[length - 1][0], tri_array[length - 1][1]); - immEnd(); - } - - immUnbindProgram(); - - GPU_blend(GPU_BLEND_NONE); -} - static void widget_init(uiWidgetBase *wtb) { wtb->totvert = wtb->halfwayvert = 0; @@ -1493,7 +1459,7 @@ static void widget_draw_submenu_tria(const uiBut *but, GPU_blend(GPU_BLEND_ALPHA); UI_widgetbase_draw_cache_flush(); GPU_blend(GPU_BLEND_NONE); - ui_draw_anti_tria_rect(&tria_rect, 'h', col); + draw_anti_tria_rect(&tria_rect, 'h', col); } static void ui_text_clip_give_prev_off(uiBut *but, const char *str) @@ -2262,7 +2228,10 @@ static void widget_draw_extra_icons(const uiWidgetColors *wcol, temp.xmin = temp.xmax - icon_size; - if (!op_icon->highlighted) { + if (op_icon->disabled) { + alpha_this *= 0.4f; + } + else if (!op_icon->highlighted) { alpha_this *= 0.75f; } @@ -3679,10 +3648,9 @@ static void widget_progressbar( widgetbase_draw(&wtb_bar, wcol); } -static void widget_datasetrow( - uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign)) +static void widget_treerow_exec( + uiWidgetColors *wcol, rcti *rect, int state, int UNUSED(roundboxalign), int indentation) { - uiButDatasetRow *but_componentrow = (uiButDatasetRow *)but; uiWidgetBase wtb; widget_init(&wtb); @@ -3695,16 +3663,30 @@ static void widget_datasetrow( widgetbase_draw(&wtb, wcol); } - BLI_rcti_resize(rect, - BLI_rcti_size_x(rect) - UI_UNIT_X * but_componentrow->indentation, - BLI_rcti_size_y(rect)); - BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * but_componentrow->indentation, 0); + BLI_rcti_resize(rect, BLI_rcti_size_x(rect) - UI_UNIT_X * indentation, BLI_rcti_size_y(rect)); + BLI_rcti_translate(rect, 0.5f * UI_UNIT_X * indentation, 0); +} + +static void widget_treerow( + uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) +{ + uiButTreeRow *tree_row = (uiButTreeRow *)but; + BLI_assert(but->type == UI_BTYPE_TREEROW); + widget_treerow_exec(wcol, rect, state, roundboxalign, tree_row->indentation); +} + +static void widget_datasetrow( + uiBut *but, uiWidgetColors *wcol, rcti *rect, int state, int roundboxalign) +{ + uiButDatasetRow *dataset_row = (uiButDatasetRow *)but; + BLI_assert(but->type == UI_BTYPE_DATASETROW); + widget_treerow_exec(wcol, rect, state, roundboxalign, dataset_row->indentation); } static void widget_nodesocket( uiBut *but, uiWidgetColors *wcol, rcti *rect, int UNUSED(state), int UNUSED(roundboxalign)) { - const int radi = 5; + const int radi = 0.25f * BLI_rcti_size_y(rect); uiWidgetBase wtb; widget_init(&wtb); @@ -4035,9 +4017,15 @@ static void widget_menu_itembut(uiWidgetColors *wcol, uiWidgetBase wtb; widget_init(&wtb); - /* not rounded, no outline */ + /* Padding on the sides. */ + const float padding = 0.125f * BLI_rcti_size_y(rect); + rect->xmin += padding; + rect->xmax -= padding; + + /* No outline. */ wtb.draw_outline = false; - round_box_edges(&wtb, 0, rect, 0.0f); + const float rad = wcol->roundness * BLI_rcti_size_y(rect); + round_box_edges(&wtb, UI_CNR_ALL, rect, rad); widgetbase_draw(&wtb, wcol); } @@ -4492,6 +4480,10 @@ static uiWidgetType *widget_type(uiWidgetTypeEnum type) wt.custom = widget_datasetrow; break; + case UI_WTYPE_TREEROW: + wt.custom = widget_treerow; + break; + case UI_WTYPE_NODESOCKET: wt.custom = widget_nodesocket; break; @@ -4605,6 +4597,9 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu switch (but->type) { case UI_BTYPE_LABEL: wt = widget_type(UI_WTYPE_ICON_LABEL); + if (!(but->flag & UI_HAS_ICON)) { + but->drawflag |= UI_BUT_NO_TEXT_PADDING; + } break; default: wt = widget_type(UI_WTYPE_ICON); @@ -4804,9 +4799,6 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu break; case UI_BTYPE_CURVE: - /* do not draw right to edge of rect */ - rect->xmin += (0.2f * UI_UNIT_X); - rect->xmax -= (0.2f * UI_UNIT_X); ui_draw_but_CURVE(region, but, &tui->wcol_regular, rect); break; @@ -4824,6 +4816,11 @@ void ui_draw_but(const bContext *C, struct ARegion *region, uiStyle *style, uiBu fstyle = &style->widgetlabel; break; + case UI_BTYPE_TREEROW: + wt = widget_type(UI_WTYPE_TREEROW); + fstyle = &style->widgetlabel; + break; + case UI_BTYPE_SCROLL: wt = widget_type(UI_WTYPE_SCROLL); break; @@ -4957,30 +4954,6 @@ void ui_draw_menu_back(uiStyle *UNUSED(style), uiBlock *block, rcti *rect) ui_draw_clip_tri(block, rect, wt); } -/** - * Uses the widget base drawing and colors from the box widget, but ensures an opaque - * inner color. - */ -void ui_draw_box_opaque(rcti *rect, int roundboxalign) -{ - uiWidgetType *wt = widget_type(UI_WTYPE_BOX); - - /* Alpha blend with the region's background color to force an opaque background. */ - uiWidgetColors *wcol = &wt->wcol; - wt->state(wt, 0, 0, UI_EMBOSS_UNDEFINED); - float background[4]; - UI_GetThemeColor4fv(TH_BACK, background); - float new_inner[4]; - rgba_uchar_to_float(new_inner, wcol->inner); - new_inner[0] = (new_inner[0] * new_inner[3]) + (background[0] * (1.0f - new_inner[3])); - new_inner[1] = (new_inner[1] * new_inner[3]) + (background[1] * (1.0f - new_inner[3])); - new_inner[2] = (new_inner[2] * new_inner[3]) + (background[2] * (1.0f - new_inner[3])); - new_inner[3] = 1.0f; - rgba_float_to_uchar(wcol->inner, new_inner); - - wt->custom(NULL, wcol, rect, 0, roundboxalign); -} - /** * Similar to 'widget_menu_back', however we can't use the widget preset system * because we need to pass in the original location so we know where to show the arrow. @@ -5348,7 +5321,7 @@ void ui_draw_menu_item(const uiFontStyle *fstyle, } } else { - BLI_assert_msg(0, "Unknwon menu item separator type"); + BLI_assert_msg(0, "Unknown menu item separator type"); } } } diff --git a/source/blender/editors/interface/resources.c b/source/blender/editors/interface/resources.c index e13b69a9763..aece2e58f1e 100644 --- a/source/blender/editors/interface/resources.c +++ b/source/blender/editors/interface/resources.c @@ -85,7 +85,7 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) ThemeSpace *ts = NULL; static uchar error[4] = {240, 0, 240, 255}; static uchar alert[4] = {240, 60, 60, 255}; - static uchar headerdesel[4] = {0, 0, 0, 255}; + static uchar header_active[4] = {0, 0, 0, 255}; static uchar back[4] = {0, 0, 0, 255}; static uchar setting = 0; const uchar *cp = error; @@ -249,15 +249,18 @@ const uchar *UI_ThemeGetColorPtr(bTheme *btheme, int spacetype, int colorid) case TH_HEADER: cp = ts->header; break; - case TH_HEADERDESEL: - /* We calculate a dynamic builtin header deselect color, also for pull-downs. */ + + case TH_HEADER_ACTIVE: cp = ts->header; - headerdesel[0] = cp[0] > 10 ? cp[0] - 10 : 0; - headerdesel[1] = cp[1] > 10 ? cp[1] - 10 : 0; - headerdesel[2] = cp[2] > 10 ? cp[2] - 10 : 0; - headerdesel[3] = cp[3]; - cp = headerdesel; + const int factor = 5; + /* Lighten the header color when editor is active. */ + header_active[0] = cp[0] > 245 ? cp[0] - factor : cp[0] + factor; + header_active[1] = cp[1] > 245 ? cp[1] - factor : cp[1] + factor; + header_active[2] = cp[2] > 245 ? cp[2] - factor : cp[2] + factor; + header_active[3] = cp[3]; + cp = header_active; break; + case TH_HEADER_TEXT: cp = ts->header_text; break; diff --git a/source/blender/editors/interface/tree_view.cc b/source/blender/editors/interface/tree_view.cc new file mode 100644 index 00000000000..fcc878c440c --- /dev/null +++ b/source/blender/editors/interface/tree_view.cc @@ -0,0 +1,790 @@ +/* + * 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 edinterface + */ + +#include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_context.h" + +#include "BLT_translation.h" + +#include "interface_intern.h" + +#include "UI_interface.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_tree_view.hh" + +namespace blender::ui { + +/* ---------------------------------------------------------------------- */ + +/** + * Add a tree-item to the container. This is the only place where items should be added, it handles + * important invariants! + */ +AbstractTreeViewItem &TreeViewItemContainer::add_tree_item( + std::unique_ptr item) +{ + children_.append(std::move(item)); + + /* The first item that will be added to the root sets this. */ + if (root_ == nullptr) { + root_ = this; + } + + AbstractTreeViewItem &added_item = *children_.last(); + added_item.root_ = root_; + if (root_ != this) { + /* Any item that isn't the root can be assumed to the a #AbstractTreeViewItem. Not entirely + * nice to static_cast this, but well... */ + added_item.parent_ = static_cast(this); + } + + return added_item; +} + +void TreeViewItemContainer::foreach_item_recursive(ItemIterFn iter_fn, IterOptions options) const +{ + for (const auto &child : children_) { + iter_fn(*child); + if (bool(options & IterOptions::SkipCollapsed) && child->is_collapsed()) { + continue; + } + + child->foreach_item_recursive(iter_fn, options); + } +} + +/* ---------------------------------------------------------------------- */ + +void AbstractTreeView::foreach_item(ItemIterFn iter_fn, IterOptions options) const +{ + foreach_item_recursive(iter_fn, options); +} + +bool AbstractTreeView::is_renaming() const +{ + return rename_buffer_ != nullptr; +} + +void AbstractTreeView::build_layout_from_tree(const TreeViewLayoutBuilder &builder) +{ + uiLayout *prev_layout = builder.current_layout(); + + uiLayout *box = uiLayoutBox(prev_layout); + uiLayoutColumn(box, false); + + foreach_item([&builder](AbstractTreeViewItem &item) { builder.build_row(item); }, + IterOptions::SkipCollapsed); + + UI_block_layout_set_current(&builder.block(), prev_layout); +} + +void AbstractTreeView::update_from_old(uiBlock &new_block) +{ + uiBlock *old_block = new_block.oldblock; + if (!old_block) { + /* Initial construction, nothing to update. */ + is_reconstructed_ = true; + return; + } + + uiTreeViewHandle *old_view_handle = ui_block_view_find_matching_in_old_block( + &new_block, reinterpret_cast(this)); + BLI_assert(old_view_handle); + + AbstractTreeView &old_view = reinterpret_cast(*old_view_handle); + + /* Update own persistent data. */ + /* Keep the rename buffer persistent while renaming! The rename button uses the buffer's + * pointer to identify itself over redraws. */ + rename_buffer_ = std::move(old_view.rename_buffer_); + old_view.rename_buffer_ = nullptr; + + update_children_from_old_recursive(*this, old_view); + + /* Finished (re-)constructing the tree. */ + is_reconstructed_ = true; +} + +void AbstractTreeView::update_children_from_old_recursive(const TreeViewItemContainer &new_items, + const TreeViewItemContainer &old_items) +{ + for (const auto &new_item : new_items.children_) { + AbstractTreeViewItem *matching_old_item = find_matching_child(*new_item, old_items); + if (!matching_old_item) { + continue; + } + + new_item->update_from_old(*matching_old_item); + + /* Recurse into children of the matched item. */ + update_children_from_old_recursive(*new_item, *matching_old_item); + } +} + +AbstractTreeViewItem *AbstractTreeView::find_matching_child( + const AbstractTreeViewItem &lookup_item, const TreeViewItemContainer &items) +{ + for (const auto &iter_item : items.children_) { + if (lookup_item.matches(*iter_item)) { + /* We have a matching item! */ + return iter_item.get(); + } + } + + return nullptr; +} + +bool AbstractTreeView::is_reconstructed() const +{ + return is_reconstructed_; +} + +void AbstractTreeView::change_state_delayed() +{ + BLI_assert_msg( + is_reconstructed(), + "These state changes are supposed to be delayed until reconstruction is completed"); + foreach_item([](AbstractTreeViewItem &item) { item.change_state_delayed(); }); +} + +/* ---------------------------------------------------------------------- */ + +void AbstractTreeViewItem::tree_row_click_fn(struct bContext * /*C*/, + void *but_arg1, + void * /*arg2*/) +{ + uiButTreeRow *tree_row_but = (uiButTreeRow *)but_arg1; + AbstractTreeViewItem &tree_item = reinterpret_cast( + *tree_row_but->tree_item); + + tree_item.activate(); +} + +void AbstractTreeViewItem::add_treerow_button(uiBlock &block) +{ + /* For some reason a width > (UI_UNIT_X * 2) make the layout system use all available width. */ + tree_row_but_ = (uiButTreeRow *)uiDefBut( + &block, UI_BTYPE_TREEROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + + tree_row_but_->tree_item = reinterpret_cast(this); + UI_but_func_set(&tree_row_but_->but, tree_row_click_fn, tree_row_but_, nullptr); +} + +void AbstractTreeViewItem::add_indent(uiLayout &row) const +{ + uiBlock *block = uiLayoutGetBlock(&row); + uiLayout *subrow = uiLayoutRow(&row, true); + uiLayoutSetFixedSize(subrow, true); + + const float indent_size = count_parents() * UI_DPI_ICON_SIZE; + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, indent_size, 0, nullptr, 0.0, 0.0, 0, 0, ""); + + /* Indent items without collapsing icon some more within their parent. Makes it clear that they + * are actually nested and not just a row at the same level without a chevron. */ + if (!is_collapsible() && parent_) { + uiDefBut(block, UI_BTYPE_SEPR, 0, "", 0, 0, 0.2f * UI_UNIT_X, 0, nullptr, 0.0, 0.0, 0, 0, ""); + } + + /* Restore. */ + UI_block_layout_set_current(block, &row); +} + +void AbstractTreeViewItem::collapse_chevron_click_fn(struct bContext *C, + void * /*but_arg1*/, + void * /*arg2*/) +{ + /* There's no data we could pass to this callback. It must be either the button itself or a + * consistent address to match buttons over redraws. So instead of passing it somehow, just + * lookup the hovered item via context here. */ + + const wmWindow *win = CTX_wm_window(C); + const ARegion *region = CTX_wm_region(C); + uiTreeViewItemHandle *hovered_item_handle = UI_block_tree_view_find_item_at(region, + win->eventstate->xy); + AbstractTreeViewItem *hovered_item = reinterpret_cast( + hovered_item_handle); + BLI_assert(hovered_item != nullptr); + + hovered_item->toggle_collapsed(); + /* When collapsing an item with an active child, make this collapsed item active instead so the + * active item stays visible. */ + if (hovered_item->has_active_child()) { + hovered_item->activate(); + } +} + +bool AbstractTreeViewItem::is_collapse_chevron_but(const uiBut *but) +{ + return but->type == UI_BTYPE_BUT_TOGGLE && ELEM(but->icon, ICON_TRIA_RIGHT, ICON_TRIA_DOWN) && + (but->func == collapse_chevron_click_fn); +} + +void AbstractTreeViewItem::add_collapse_chevron(uiBlock &block) const +{ + if (!is_collapsible()) { + return; + } + + const BIFIconID icon = is_collapsed() ? ICON_TRIA_RIGHT : ICON_TRIA_DOWN; + uiBut *but = uiDefIconBut( + &block, UI_BTYPE_BUT_TOGGLE, 0, icon, 0, 0, UI_UNIT_X, UI_UNIT_Y, nullptr, 0, 0, 0, 0, ""); + /* Note that we're passing the tree-row button here, not the chevron one. */ + UI_but_func_set(but, collapse_chevron_click_fn, nullptr, nullptr); + UI_but_flag_disable(but, UI_BUT_UNDO); + + /* Check if the query for the button matches the created button. */ + BLI_assert(is_collapse_chevron_but(but)); +} + +AbstractTreeViewItem *AbstractTreeViewItem::find_tree_item_from_rename_button( + const uiBut &rename_but) +{ + /* A minimal sanity check, can't do much more here. */ + BLI_assert(rename_but.type == UI_BTYPE_TEXT && rename_but.poin); + + LISTBASE_FOREACH (uiBut *, but, &rename_but.block->buttons) { + if (but->type != UI_BTYPE_TREEROW) { + continue; + } + + uiButTreeRow *tree_row_but = (uiButTreeRow *)but; + AbstractTreeViewItem *item = reinterpret_cast(tree_row_but->tree_item); + const AbstractTreeView &tree_view = item->get_tree_view(); + + if (item->is_renaming() && (tree_view.rename_buffer_->data() == rename_but.poin)) { + return item; + } + } + + return nullptr; +} + +void AbstractTreeViewItem::rename_button_fn(bContext *UNUSED(C), void *arg, char *UNUSED(origstr)) +{ + const uiBut *rename_but = static_cast(arg); + AbstractTreeViewItem *item = find_tree_item_from_rename_button(*rename_but); + BLI_assert(item); + + const AbstractTreeView &tree_view = item->get_tree_view(); + item->rename(tree_view.rename_buffer_->data()); + item->end_renaming(); +} + +void AbstractTreeViewItem::add_rename_button(uiLayout &row) +{ + uiBlock *block = uiLayoutGetBlock(&row); + eUIEmbossType previous_emboss = UI_block_emboss_get(block); + + uiLayoutRow(&row, false); + /* Enable emboss for the text button. */ + UI_block_emboss_set(block, UI_EMBOSS); + + AbstractTreeView &tree_view = get_tree_view(); + uiBut *rename_but = uiDefBut(block, + UI_BTYPE_TEXT, + 1, + "", + 0, + 0, + UI_UNIT_X * 10, + UI_UNIT_Y, + tree_view.rename_buffer_->data(), + 1.0f, + tree_view.rename_buffer_->max_size(), + 0, + 0, + ""); + + /* Gotta be careful with what's passed to the `arg1` here. Any tree data will be freed once the + * callback is executed. */ + UI_but_func_rename_set(rename_but, AbstractTreeViewItem::rename_button_fn, rename_but); + UI_but_flag_disable(rename_but, UI_BUT_UNDO); + + const bContext *evil_C = static_cast(block->evil_C); + ARegion *region = CTX_wm_region(evil_C); + /* Returns false if the button was removed. */ + if (UI_but_active_only(evil_C, region, block, rename_but) == false) { + end_renaming(); + } + + UI_block_emboss_set(block, previous_emboss); + UI_block_layout_set_current(block, &row); +} + +bool AbstractTreeViewItem::has_active_child() const +{ + bool found = false; + foreach_item_recursive([&found](const AbstractTreeViewItem &item) { + if (item.is_active()) { + found = true; + } + }); + + return found; +} + +void AbstractTreeViewItem::on_activate() +{ + /* Do nothing by default. */ +} + +void AbstractTreeViewItem::is_active(IsActiveFn is_active_fn) +{ + is_active_fn_ = is_active_fn; +} + +std::unique_ptr AbstractTreeViewItem::create_drag_controller() + const +{ + /* There's no drag controller (and hence no drag support) by default. */ + return nullptr; +} + +std::unique_ptr AbstractTreeViewItem::create_drop_controller() + const +{ + /* There's no drop controller (and hence no drop support) by default. */ + return nullptr; +} + +bool AbstractTreeViewItem::can_rename() const +{ + /* No renaming by default. */ + return false; +} + +bool AbstractTreeViewItem::rename(StringRefNull new_name) +{ + /* It is important to update the label after renaming, so #AbstractTreeViewItem::matches() + * recognizes the item. (It only compares labels by default.) */ + label_ = new_name; + return true; +} + +void AbstractTreeViewItem::build_context_menu(bContext & /*C*/, uiLayout & /*column*/) const +{ + /* No context menu by default. */ +} + +void AbstractTreeViewItem::update_from_old(const AbstractTreeViewItem &old) +{ + is_open_ = old.is_open_; + is_active_ = old.is_active_; + is_renaming_ = old.is_renaming_; +} + +bool AbstractTreeViewItem::matches(const AbstractTreeViewItem &other) const +{ + return label_ == other.label_; +} + +void AbstractTreeViewItem::begin_renaming() +{ + AbstractTreeView &tree_view = get_tree_view(); + if (tree_view.is_renaming() || !can_rename()) { + return; + } + + is_renaming_ = true; + + tree_view.rename_buffer_ = std::make_unique(); + std::copy(std::begin(label_), std::end(label_), std::begin(*tree_view.rename_buffer_)); +} + +void AbstractTreeViewItem::end_renaming() +{ + if (!is_renaming()) { + return; + } + + is_renaming_ = false; + + AbstractTreeView &tree_view = get_tree_view(); + tree_view.rename_buffer_ = nullptr; +} + +AbstractTreeView &AbstractTreeViewItem::get_tree_view() const +{ + return static_cast(*root_); +} + +int AbstractTreeViewItem::count_parents() const +{ + int i = 0; + for (TreeViewItemContainer *parent = parent_; parent; parent = parent->parent_) { + i++; + } + return i; +} + +void AbstractTreeViewItem::activate() +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "Item activation can't be done until reconstruction is completed"); + + if (is_active()) { + return; + } + + /* Deactivate other items in the tree. */ + get_tree_view().foreach_item([](auto &item) { item.deactivate(); }); + + on_activate(); + /* Make sure the active item is always visible. */ + ensure_parents_uncollapsed(); + + is_active_ = true; +} + +void AbstractTreeViewItem::deactivate() +{ + is_active_ = false; +} + +bool AbstractTreeViewItem::is_active() const +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_active_; +} + +bool AbstractTreeViewItem::is_hovered() const +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + BLI_assert_msg(tree_row_but_ != nullptr, + "Hovered state can't be queried before the tree row is being built"); + + const uiTreeViewItemHandle *this_handle = reinterpret_cast(this); + /* The new layout hasn't finished construction yet, so the final state of the button is unknown. + * Get the matching button from the previous redraw instead. */ + uiButTreeRow *old_treerow_but = ui_block_view_find_treerow_in_old_block(tree_row_but_->but.block, + this_handle); + return old_treerow_but && (old_treerow_but->but.flag & UI_ACTIVE); +} + +bool AbstractTreeViewItem::is_collapsed() const +{ + BLI_assert_msg(get_tree_view().is_reconstructed(), + "State can't be queried until reconstruction is completed"); + return is_collapsible() && !is_open_; +} + +void AbstractTreeViewItem::toggle_collapsed() +{ + is_open_ = !is_open_; +} + +void AbstractTreeViewItem::set_collapsed(bool collapsed) +{ + is_open_ = !collapsed; +} + +bool AbstractTreeViewItem::is_collapsible() const +{ + return !children_.is_empty(); +} + +bool AbstractTreeViewItem::is_renaming() const +{ + return is_renaming_; +} + +void AbstractTreeViewItem::ensure_parents_uncollapsed() +{ + for (AbstractTreeViewItem *parent = parent_; parent; parent = parent->parent_) { + parent->set_collapsed(false); + } +} + +bool AbstractTreeViewItem::matches_including_parents(const AbstractTreeViewItem &other) const +{ + if (!matches(other)) { + return false; + } + if (count_parents() != other.count_parents()) { + return false; + } + + for (AbstractTreeViewItem *parent = parent_, *other_parent = other.parent_; + parent && other_parent; + parent = parent->parent_, other_parent = other_parent->parent_) { + if (!parent->matches(*other_parent)) { + return false; + } + } + + return true; +} + +uiButTreeRow *AbstractTreeViewItem::tree_row_button() +{ + return tree_row_but_; +} + +void AbstractTreeViewItem::change_state_delayed() +{ + if (is_active_fn_()) { + activate(); + } +} +/* ---------------------------------------------------------------------- */ + +AbstractTreeViewItemDropController::AbstractTreeViewItemDropController(AbstractTreeView &tree_view) + : tree_view_(tree_view) +{ +} + +/* ---------------------------------------------------------------------- */ + +TreeViewBuilder::TreeViewBuilder(uiBlock &block) : block_(block) +{ +} + +void TreeViewBuilder::build_tree_view(AbstractTreeView &tree_view) +{ + tree_view.build_tree(); + tree_view.update_from_old(block_); + tree_view.change_state_delayed(); + tree_view.build_layout_from_tree(TreeViewLayoutBuilder(block_)); +} + +/* ---------------------------------------------------------------------- */ + +TreeViewLayoutBuilder::TreeViewLayoutBuilder(uiBlock &block) : block_(block) +{ +} + +/** + * Moves the button following the last added chevron closer to the list item. + * + * Iterates backwards over buttons until finding the tree-row button, which is assumed to be the + * first button added for the row, and can act as a delimiter that way. + */ +void TreeViewLayoutBuilder::polish_layout(const uiBlock &block) +{ + LISTBASE_FOREACH_BACKWARD (uiBut *, but, &block.buttons) { + if (AbstractTreeViewItem::is_collapse_chevron_but(but) && but->next && + /* Embossed buttons with padding-less text padding look weird, so don't touch them. */ + ELEM(but->next->emboss, UI_EMBOSS_NONE, UI_EMBOSS_NONE_OR_STATUS)) { + UI_but_drawflag_enable(static_cast(but->next), UI_BUT_NO_TEXT_PADDING); + } + + if (but->type == UI_BTYPE_TREEROW) { + break; + } + } +} + +void TreeViewLayoutBuilder::build_row(AbstractTreeViewItem &item) const +{ + uiBlock &block_ = block(); + + uiLayout *prev_layout = current_layout(); + eUIEmbossType previous_emboss = UI_block_emboss_get(&block_); + + uiLayout *overlap = uiLayoutOverlap(prev_layout); + + uiLayoutRow(overlap, false); + /* Every item gets one! Other buttons can be overlapped on top. */ + item.add_treerow_button(block_); + + /* After adding tree-row button (would disable hover highlighting). */ + UI_block_emboss_set(&block_, UI_EMBOSS_NONE); + + uiLayout *row = uiLayoutRow(overlap, true); + item.add_indent(*row); + item.add_collapse_chevron(block_); + + if (item.is_renaming()) { + item.add_rename_button(*row); + } + else { + item.build_row(*row); + } + polish_layout(block_); + + UI_block_emboss_set(&block_, previous_emboss); + UI_block_layout_set_current(&block_, prev_layout); +} + +uiBlock &TreeViewLayoutBuilder::block() const +{ + return block_; +} + +uiLayout *TreeViewLayoutBuilder::current_layout() const +{ + return block().curlayout; +} + +/* ---------------------------------------------------------------------- */ + +BasicTreeViewItem::BasicTreeViewItem(StringRef label, BIFIconID icon_) : icon(icon_) +{ + label_ = label; +} + +void BasicTreeViewItem::build_row(uiLayout &row) +{ + add_label(row); +} + +void BasicTreeViewItem::add_label(uiLayout &layout, StringRefNull label_override) +{ + const StringRefNull label = label_override.is_empty() ? StringRefNull(label_) : label_override; + + /* Some padding for labels without collapse chevron and no icon. Looks weird without. */ + if (icon == ICON_NONE && !is_collapsible()) { + uiItemS_ex(&layout, 0.8f); + } + uiItemL(&layout, IFACE_(label.c_str()), icon); +} + +void BasicTreeViewItem::on_activate() +{ + if (activate_fn_) { + activate_fn_(*this); + } +} + +void BasicTreeViewItem::on_activate(ActivateFn fn) +{ + activate_fn_ = fn; +} + +} // namespace blender::ui + +using namespace blender::ui; + +bool UI_tree_view_item_is_active(const uiTreeViewItemHandle *item_handle) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); + return item.is_active(); +} + +bool UI_tree_view_item_matches(const uiTreeViewItemHandle *a_handle, + const uiTreeViewItemHandle *b_handle) +{ + const AbstractTreeViewItem &a = reinterpret_cast(*a_handle); + const AbstractTreeViewItem &b = reinterpret_cast(*b_handle); + /* TODO should match the tree-view as well. */ + return a.matches_including_parents(b); +} + +/** + * Attempt to start dragging the tree-item \a item_. This will not work if the tree item doesn't + * support dragging, i.e. it won't create a drag-controller upon request. + * \return True if dragging started successfully, otherwise false. + */ +bool UI_tree_view_item_drag_start(bContext *C, uiTreeViewItemHandle *item_) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_); + const std::unique_ptr drag_controller = + item.create_drag_controller(); + if (!drag_controller) { + return false; + } + + WM_event_start_drag(C, + ICON_NONE, + drag_controller->get_drag_type(), + drag_controller->create_drag_data(), + 0, + WM_DRAG_FREE_DATA); + return true; +} + +bool UI_tree_view_item_can_drop(const uiTreeViewItemHandle *item_, + const wmDrag *drag, + const char **r_disabled_hint) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_); + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return false; + } + + return drop_controller->can_drop(*drag, r_disabled_hint); +} + +char *UI_tree_view_item_drop_tooltip(const uiTreeViewItemHandle *item_, const wmDrag *drag) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_); + const std::unique_ptr drop_controller = + item.create_drop_controller(); + if (!drop_controller) { + return nullptr; + } + + return BLI_strdup(drop_controller->drop_tooltip(*drag).c_str()); +} + +/** + * Let a tree-view item handle a drop event. + * \return True if the drop was handled by the tree-view item. + */ +bool UI_tree_view_item_drop_handle(uiTreeViewItemHandle *item_, const ListBase *drags) +{ + AbstractTreeViewItem &item = reinterpret_cast(*item_); + std::unique_ptr drop_controller = + item.create_drop_controller(); + + const char *disabled_hint_dummy = nullptr; + LISTBASE_FOREACH (const wmDrag *, drag, drags) { + if (drop_controller->can_drop(*drag, &disabled_hint_dummy)) { + return drop_controller->on_drop(*drag); + } + } + + return false; +} + +/** + * Can \a item_handle be renamed right now? Not that this isn't just a mere wrapper around + * #AbstractTreeViewItem::can_rename(). This also checks if there is another item being renamed, + * and returns false if so. + */ +bool UI_tree_view_item_can_rename(const uiTreeViewItemHandle *item_handle) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); + const AbstractTreeView &tree_view = item.get_tree_view(); + return !tree_view.is_renaming() && item.can_rename(); +} + +void UI_tree_view_item_begin_rename(uiTreeViewItemHandle *item_handle) +{ + AbstractTreeViewItem &item = reinterpret_cast(*item_handle); + item.begin_renaming(); +} + +void UI_tree_view_item_context_menu_build(bContext *C, + const uiTreeViewItemHandle *item_handle, + uiLayout *column) +{ + const AbstractTreeViewItem &item = reinterpret_cast(*item_handle); + item.build_context_menu(*C, *column); +} diff --git a/source/blender/editors/interface/view2d.c b/source/blender/editors/interface/view2d.c index 23c8a0d35bf..eea6512f0f8 100644 --- a/source/blender/editors/interface/view2d.c +++ b/source/blender/editors/interface/view2d.c @@ -32,6 +32,7 @@ #include "DNA_userdef_types.h" #include "BLI_array.h" +#include "BLI_easing.h" #include "BLI_link_utils.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -166,7 +167,7 @@ static void view2d_masks(View2D *v2d, const rcti *mask_scroll) scroll = view2d_scroll_mapped(v2d->scroll); - /* scrollers are based off regionsize + /* Scrollers are based off region-size: * - they can only be on one to two edges of the region they define * - if they overlap, they must not occupy the corners (which are reserved for other widgets) */ @@ -866,6 +867,11 @@ void UI_view2d_curRect_changed(const bContext *C, View2D *v2d) /* ------------------ */ +bool UI_view2d_area_supports_sync(ScrArea *area) +{ + return ELEM(area->spacetype, SPACE_ACTION, SPACE_NLA, SPACE_SEQ, SPACE_CLIP, SPACE_GRAPH); +} + /* Called by menus to activate it, or by view2d operators * to make sure 'related' views stay in synchrony */ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) @@ -903,6 +909,9 @@ void UI_view2d_sync(bScreen *screen, ScrArea *area, View2D *v2dcur, int flag) /* check if doing whole screen syncing (i.e. time/horizontal) */ if ((v2dcur->flag & V2D_VIEWSYNC_SCREEN_TIME) && (screen)) { LISTBASE_FOREACH (ScrArea *, area_iter, &screen->areabase) { + if (!UI_view2d_area_supports_sync(area_iter)) { + continue; + } LISTBASE_FOREACH (ARegion *, region, &area_iter->regionbase) { /* don't operate on self */ if (v2dcur != ®ion->v2d) { @@ -1283,6 +1292,114 @@ void UI_view2d_multi_grid_draw( immUnbindProgram(); } +static void grid_axis_start_and_count( + const float step, const float min, const float max, float *r_start, int *r_count) +{ + *r_start = min; + if (*r_start < 0.0f) { + *r_start += -(float)fmod(min, step); + } + else { + *r_start += step - (float)fabs(fmod(min, step)); + } + + if (*r_start > max) { + *r_count = 0; + } + else { + *r_count = (max - *r_start) / step + 1; + } +} + +typedef struct DotGridLevelInfo { + /* The factor applied to the #min_step argument. This could be easily computed in runtime, + * but seeing it together with the other values is helpful. */ + float step_factor; + /* The normalized zoom level at which the grid level starts to fade in. + * At lower zoom levels, the points will not be visible and the level will be skipped. */ + float fade_in_start_zoom; + /* The normalized zoom level at which the grid finishes fading in. + * At higher zoom levels, the points will be opaque. */ + float fade_in_end_zoom; +} DotGridLevelInfo; + +static const DotGridLevelInfo level_info[9] = { + {6.4f, -0.1f, 0.01f}, + {3.2f, 0.0f, 0.025f}, + {1.6f, 0.025f, 0.15f}, + {0.8f, 0.05f, 0.2f}, + {0.4f, 0.1f, 0.25f}, + {0.2f, 0.125f, 0.3f}, + {0.1f, 0.25f, 0.5f}, + {0.05f, 0.7f, 0.9f}, + {0.025f, 0.6f, 0.9f}, +}; + +/** + * Draw a multi-level grid of dots, with a dynamic number of levels based on the fading. + * + * \param grid_color_id: The theme color used for the points. Faded dynamically based on zoom. + * \param min_step: The base size of the grid. At different zoom levels, the visible grid may have + * a larger step size. + * \param grid_levels: The maximum grid depth. Larger grid levels will subdivide the grid more. + */ +void UI_view2d_dot_grid_draw(const View2D *v2d, + const int grid_color_id, + const float min_step, + const int grid_levels) +{ + BLI_assert(grid_levels > 0 && grid_levels < 10); + const float zoom_x = (float)(BLI_rcti_size_x(&v2d->mask) + 1) / BLI_rctf_size_x(&v2d->cur); + const float zoom_normalized = (zoom_x - v2d->minzoom) / (v2d->maxzoom - v2d->minzoom); + + GPUVertFormat *format = immVertexFormat(); + const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + const uint color_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_FLAT_COLOR); + GPU_point_size(3.0f * UI_DPI_FAC); + + float color[4]; + UI_GetThemeColor3fv(grid_color_id, color); + + for (int level = 0; level < grid_levels; level++) { + const DotGridLevelInfo *info = &level_info[level]; + const float step = min_step * info->step_factor * U.widget_unit; + + const float alpha_factor = (zoom_normalized - info->fade_in_start_zoom) / + (info->fade_in_end_zoom - info->fade_in_start_zoom); + color[3] = clamp_f(BLI_easing_cubic_ease_in_out(alpha_factor, 0.0f, 1.0f, 1.0f), 0.0f, 1.0f); + if (color[3] == 0.0f) { + break; + } + + int count_x; + float start_x; + grid_axis_start_and_count(step, v2d->cur.xmin, v2d->cur.xmax, &start_x, &count_x); + int count_y; + float start_y; + grid_axis_start_and_count(step, v2d->cur.ymin, v2d->cur.ymax, &start_y, &count_y); + if (count_x == 0 || count_y == 0) { + continue; + } + + immBegin(GPU_PRIM_POINTS, count_x * count_y); + + /* Theoretically drawing on top of lower grid levels could be avoided, but it would also + * increase the complexity of this loop, which isn't worth the time at the moment. */ + for (int i_y = 0; i_y < count_y; i_y++) { + const float y = start_y + step * i_y; + for (int i_x = 0; i_x < count_x; i_x++) { + const float x = start_x + step * i_x; + immAttr4fv(color_id, color); + immVertex2f(pos, x, y); + } + } + + immEnd(); + } + + immUnbindProgram(); +} /** \} */ /* -------------------------------------------------------------------- */ @@ -1916,8 +2033,10 @@ void UI_view2d_offset(struct View2D *v2d, float xfac, float yfac) * - 'v' = in vertical scroller. * - 0 = not in scroller. */ -char UI_view2d_mouse_in_scrollers_ex( - const ARegion *region, const View2D *v2d, int x, int y, int *r_scroll) +char UI_view2d_mouse_in_scrollers_ex(const ARegion *region, + const View2D *v2d, + const int xy[2], + int *r_scroll) { const int scroll = view2d_scroll_mapped(v2d->scroll); *r_scroll = scroll; @@ -1925,8 +2044,8 @@ char UI_view2d_mouse_in_scrollers_ex( if (scroll) { /* Move to region-coordinates. */ const int co[2] = { - x - region->winrct.xmin, - y - region->winrct.ymin, + xy[0] - region->winrct.xmin, + xy[1] - region->winrct.ymin, }; if (scroll & V2D_SCROLL_HORIZONTAL) { if (IN_2D_HORIZ_SCROLL(v2d, co)) { @@ -1970,10 +2089,10 @@ char UI_view2d_rect_in_scrollers_ex(const ARegion *region, return 0; } -char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, int x, int y) +char UI_view2d_mouse_in_scrollers(const ARegion *region, const View2D *v2d, const int xy[2]) { int scroll_dummy = 0; - return UI_view2d_mouse_in_scrollers_ex(region, v2d, x, y, &scroll_dummy); + return UI_view2d_mouse_in_scrollers_ex(region, v2d, xy, &scroll_dummy); } char UI_view2d_rect_in_scrollers(const ARegion *region, const View2D *v2d, const rcti *rect) diff --git a/source/blender/editors/interface/view2d_draw.c b/source/blender/editors/interface/view2d_draw.c index fd4dba30c1c..b1869fbf2f9 100644 --- a/source/blender/editors/interface/view2d_draw.c +++ b/source/blender/editors/interface/view2d_draw.c @@ -394,29 +394,33 @@ static void draw_vertical_scale_indicators(const ARegion *region, const int font_id = BLF_default(); UI_FontThemeColor(font_id, colorid); - BLF_enable(font_id, BLF_ROTATION); - BLF_rotation(font_id, M_PI_2); - BLF_batch_draw_begin(); - const float xpos = rect->xmax - 2.0f * UI_DPI_FAC; + BLF_enable(font_id, BLF_SHADOW); + const float shadow_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + BLF_shadow(font_id, 5, shadow_color); + BLF_shadow_offset(font_id, 1, -1); + + const float x_offset = 8.0f; + const float xpos = (rect->xmin + x_offset) * UI_DPI_FAC; const float ymin = rect->ymin; const float ymax = rect->ymax; + const float y_offset = (BLF_height(font_id, "0", 1) / 2.0f) - U.pixelsize; for (uint i = 0; i < steps; i++) { const float ypos_view = start + i * distance; const float ypos_region = UI_view2d_view_to_region_y(v2d, ypos_view + display_offset); char text[32]; to_string(to_string_data, ypos_view, distance, sizeof(text), text); - const float text_width = BLF_width(font_id, text, strlen(text)); - if (ypos_region - text_width / 2.0f >= ymin && ypos_region + text_width / 2.0f <= ymax) { - BLF_draw_default(xpos, ypos_region - text_width / 2.0f, 0.0f, text, sizeof(text)); + if (ypos_region - y_offset >= ymin && ypos_region + y_offset <= ymax) { + BLF_draw_default(xpos, ypos_region - y_offset, 0.0f, text, sizeof(text)); } } + BLF_disable(font_id, BLF_SHADOW); + BLF_batch_draw_end(); - BLF_disable(font_id, BLF_ROTATION); GPU_matrix_pop_projection(); } diff --git a/source/blender/editors/interface/view2d_edge_pan.c b/source/blender/editors/interface/view2d_edge_pan.c index 54e0d8f40ea..8d8b9a4fe48 100644 --- a/source/blender/editors/interface/view2d_edge_pan.c +++ b/source/blender/editors/interface/view2d_edge_pan.c @@ -92,6 +92,8 @@ void UI_view2d_edge_pan_init(bContext *C, vpd->delay = delay; vpd->zoom_influence = zoom_influence; + vpd->enabled = false; + /* Calculate translation factor, based on size of view. */ const float winx = (float)(BLI_rcti_size_x(&vpd->region->winrct) + 1); const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1); @@ -217,7 +219,7 @@ static void edge_pan_apply_delta(bContext *C, View2DEdgePanData *vpd, float dx, UI_view2d_sync(vpd->screen, vpd->area, v2d, V2D_LOCK_COPY); } -void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) +void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, const int xy[2]) { ARegion *region = vpd->region; @@ -227,20 +229,27 @@ void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) BLI_rcti_pad(&inside_rect, -vpd->inside_pad * U.widget_unit, -vpd->inside_pad * U.widget_unit); BLI_rcti_pad(&outside_rect, vpd->outside_pad * U.widget_unit, vpd->outside_pad * U.widget_unit); + /* Check if we can actually start the edge pan (e.g. adding nodes outside the view will start + * disabled). */ + if (BLI_rcti_isect_pt_v(&inside_rect, xy)) { + /* We are inside once, can start. */ + vpd->enabled = true; + } + int pan_dir_x = 0; int pan_dir_y = 0; - if ((vpd->outside_pad == 0) || BLI_rcti_isect_pt(&outside_rect, x, y)) { + if (vpd->enabled && ((vpd->outside_pad == 0) || BLI_rcti_isect_pt_v(&outside_rect, xy))) { /* Find whether the mouse is beyond X and Y edges. */ - if (x > inside_rect.xmax) { + if (xy[0] > inside_rect.xmax) { pan_dir_x = 1; } - else if (x < inside_rect.xmin) { + else if (xy[0] < inside_rect.xmin) { pan_dir_x = -1; } - if (y > inside_rect.ymax) { + if (xy[1] > inside_rect.ymax) { pan_dir_y = 1; } - else if (y < inside_rect.ymin) { + else if (xy[1] < inside_rect.ymin) { pan_dir_y = -1; } } @@ -252,11 +261,11 @@ void UI_view2d_edge_pan_apply(bContext *C, View2DEdgePanData *vpd, int x, int y) const float dtime = (float)(current_time - vpd->edge_pan_last_time); float dx = 0.0f, dy = 0.0f; if (pan_dir_x != 0) { - const float speed = edge_pan_speed(vpd, x, true, current_time); + const float speed = edge_pan_speed(vpd, xy[0], true, current_time); dx = dtime * speed * (float)pan_dir_x; } if (pan_dir_y != 0) { - const float speed = edge_pan_speed(vpd, y, false, current_time); + const float speed = edge_pan_speed(vpd, xy[1], false, current_time); dy = dtime * speed * (float)pan_dir_y; } vpd->edge_pan_last_time = current_time; @@ -272,7 +281,7 @@ void UI_view2d_edge_pan_apply_event(bContext *C, View2DEdgePanData *vpd, const w return; } - UI_view2d_edge_pan_apply(C, vpd, event->x, event->y); + UI_view2d_edge_pan_apply(C, vpd, event->xy); } void UI_view2d_edge_pan_cancel(bContext *C, View2DEdgePanData *vpd) diff --git a/source/blender/editors/interface/view2d_gizmo_navigate.c b/source/blender/editors/interface/view2d_gizmo_navigate.c index 30b4a7c097a..3ff5b471731 100644 --- a/source/blender/editors/interface/view2d_gizmo_navigate.c +++ b/source/blender/editors/interface/view2d_gizmo_navigate.c @@ -127,11 +127,24 @@ struct NavigateWidgetGroup { int region_size[2]; }; -static bool WIDGETGROUP_navigate_poll(const bContext *UNUSED(C), wmGizmoGroupType *UNUSED(gzgt)) +static bool WIDGETGROUP_navigate_poll(const bContext *C, wmGizmoGroupType *UNUSED(gzgt)) { if ((U.uiflag & USER_SHOW_GIZMO_NAVIGATE) == 0) { return false; } + ScrArea *area = CTX_wm_area(C); + if (area == NULL) { + return false; + } + switch (area->spacetype) { + case SPACE_SEQ: { + const SpaceSeq *sseq = area->spacedata.first; + if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_NAVIGATE)) { + return false; + } + break; + } + } return true; } diff --git a/source/blender/editors/interface/view2d_ops.c b/source/blender/editors/interface/view2d_ops.c index 1fd1b6c984d..0bca4e327cc 100644 --- a/source/blender/editors/interface/view2d_ops.c +++ b/source/blender/editors/interface/view2d_ops.c @@ -147,6 +147,8 @@ static void view_pan_init(bContext *C, wmOperator *op) const float winy = (float)(BLI_rcti_size_y(&vpd->region->winrct) + 1); vpd->facx = (BLI_rctf_size_x(&vpd->v2d->cur)) / winx; vpd->facy = (BLI_rctf_size_y(&vpd->v2d->cur)) / winy; + + vpd->v2d->flag |= V2D_IS_NAVIGATING; } /* apply transform to view (i.e. adjust 'cur' rect) */ @@ -190,6 +192,8 @@ static void view_pan_apply(bContext *C, wmOperator *op) /* Cleanup temp custom-data. */ static void view_pan_exit(wmOperator *op) { + v2dViewPanData *vpd = op->customdata; + vpd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); } @@ -220,13 +224,13 @@ static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event) View2D *v2d = vpd->v2d; /* set initial settings */ - vpd->startx = vpd->lastx = event->x; - vpd->starty = vpd->lasty = event->y; + vpd->startx = vpd->lastx = event->xy[0]; + vpd->starty = vpd->lasty = event->xy[1]; vpd->invoke_event = event->type; if (event->type == MOUSEPAN) { - RNA_int_set(op->ptr, "deltax", event->prevx - event->x); - RNA_int_set(op->ptr, "deltay", event->prevy - event->y); + RNA_int_set(op->ptr, "deltax", event->prev_xy[0] - event->xy[0]); + RNA_int_set(op->ptr, "deltay", event->prev_xy[1] - event->xy[1]); view_pan_apply(C, op); view_pan_exit(op); @@ -262,11 +266,11 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { /* calculate new delta transform, then store mouse-coordinates for next-time */ - RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->x)); - RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->y)); + RNA_int_set(op->ptr, "deltax", (vpd->lastx - event->xy[0])); + RNA_int_set(op->ptr, "deltay", (vpd->lasty - event->xy[1])); - vpd->lastx = event->x; - vpd->lasty = event->y; + vpd->lastx = event->xy[0]; + vpd->lasty = event->xy[1]; view_pan_apply(C, op); break; @@ -358,6 +362,7 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event View2DEdgePanData *vpd = op->customdata; if (event->val == KM_RELEASE || event->type == EVT_ESCKEY) { + vpd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); return (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH); } @@ -371,6 +376,8 @@ static int view_edge_pan_modal(bContext *C, wmOperator *op, const wmEvent *event static void view_edge_pan_cancel(bContext *UNUSED(C), wmOperator *op) { + v2dViewPanData *vpd = op->customdata; + vpd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); } @@ -680,6 +687,8 @@ static void view_zoomdrag_init(bContext *C, wmOperator *op) vzd->v2d = &vzd->region->v2d; /* False by default. Interactive callbacks (ie invoke()) can set it to true. */ vzd->zoom_to_mouse_pos = false; + + vzd->v2d->flag |= V2D_IS_NAVIGATING; } /* apply transform to view (i.e. adjust 'cur' rect) */ @@ -809,7 +818,8 @@ static void view_zoomstep_apply(bContext *C, wmOperator *op) static void view_zoomstep_exit(wmOperator *op) { UI_view2d_zoom_cache_reset(); - + v2dViewZoomData *vzd = op->customdata; + vzd->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_SAFE_FREE(op->customdata); } @@ -1041,6 +1051,7 @@ static void view_zoomdrag_exit(bContext *C, wmOperator *op) if (op->customdata) { v2dViewZoomData *vzd = op->customdata; + vzd->v2d->flag &= ~V2D_IS_NAVIGATING; if (vzd->timer) { WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), vzd->timer); @@ -1086,8 +1097,8 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even } if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { - vzd->lastx = event->prevx; - vzd->lasty = event->prevy; + vzd->lastx = event->prev_xy[0]; + vzd->lasty = event->prev_xy[1]; float facx, facy; float zoomfac = 0.01f; @@ -1145,8 +1156,8 @@ static int view_zoomdrag_invoke(bContext *C, wmOperator *op, const wmEvent *even } /* set initial settings */ - vzd->lastx = event->x; - vzd->lasty = event->y; + vzd->lastx = event->xy[0]; + vzd->lasty = event->xy[1]; RNA_float_set(op->ptr, "deltax", 0); RNA_float_set(op->ptr, "deltay", 0); @@ -1205,12 +1216,12 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event /* x-axis transform */ dist = BLI_rcti_size_x(&v2d->mask) / 2.0f; len_old[0] = zoomfac * fabsf(vzd->lastx - vzd->region->winrct.xmin - dist); - len_new[0] = zoomfac * fabsf(event->x - vzd->region->winrct.xmin - dist); + len_new[0] = zoomfac * fabsf(event->xy[0] - vzd->region->winrct.xmin - dist); /* y-axis transform */ dist = BLI_rcti_size_y(&v2d->mask) / 2.0f; len_old[1] = zoomfac * fabsf(vzd->lasty - vzd->region->winrct.ymin - dist); - len_new[1] = zoomfac * fabsf(event->y - vzd->region->winrct.ymin - dist); + len_new[1] = zoomfac * fabsf(event->xy[1] - vzd->region->winrct.ymin - dist); /* Calculate distance */ if (v2d->keepzoom & V2D_KEEPASPECT) { @@ -1226,8 +1237,8 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event dy *= BLI_rctf_size_y(&v2d->cur); } else { /* USER_ZOOM_CONTINUE or USER_ZOOM_DOLLY */ - float facx = zoomfac * (event->x - vzd->lastx); - float facy = zoomfac * (event->y - vzd->lasty); + float facx = zoomfac * (event->xy[0] - vzd->lastx); + float facy = zoomfac * (event->xy[1] - vzd->lasty); /* Only respect user setting zoom axis if the view does not have any zoom restrictions * any will be scaled uniformly */ @@ -1273,8 +1284,8 @@ static int view_zoomdrag_modal(bContext *C, wmOperator *op, const wmEvent *event * to starting point to determine rate of change. */ if (U.viewzoom != USER_ZOOM_CONTINUE) { /* XXX store this setting as RNA prop? */ - vzd->lastx = event->x; - vzd->lasty = event->y; + vzd->lastx = event->xy[0]; + vzd->lasty = event->xy[1]; } /* apply zooming */ @@ -1835,7 +1846,7 @@ static bool scroller_activate_poll(bContext *C) wmEvent *event = win->eventstate; /* check if mouse in scrollbars, if they're enabled */ - return (UI_view2d_mouse_in_scrollers(region, v2d, event->x, event->y) != 0); + return (UI_view2d_mouse_in_scrollers(region, v2d, event->xy) != 0); } /* initialize customdata for scroller manipulation operator */ @@ -1857,8 +1868,8 @@ static void scroller_activate_init(bContext *C, vsm->scroller = in_scroller; /* store mouse-coordinates, and convert mouse/screen coordinates to region coordinates */ - vsm->lastx = event->x; - vsm->lasty = event->y; + vsm->lastx = event->xy[0]; + vsm->lasty = event->xy[1]; /* 'zone' depends on where mouse is relative to bubble * - zooming must be allowed on this axis, otherwise, default to pan */ @@ -1911,6 +1922,8 @@ static void scroller_activate_init(bContext *C, vsm->scrollbar_orig = ((scrollers.vert_max + scrollers.vert_min) / 2) + region->winrct.ymin; } + vsm->v2d->flag |= V2D_IS_NAVIGATING; + ED_region_tag_redraw_no_rebuild(region); } @@ -1921,6 +1934,7 @@ static void scroller_activate_exit(bContext *C, wmOperator *op) v2dScrollerMove *vsm = op->customdata; vsm->v2d->scroll_ui &= ~(V2D_SCROLL_H_ACTIVE | V2D_SCROLL_V_ACTIVE); + vsm->v2d->flag &= ~V2D_IS_NAVIGATING; MEM_freeN(op->customdata); op->customdata = NULL; @@ -2010,11 +2024,11 @@ static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *e switch (vsm->scroller) { case 'h': /* horizontal scroller - so only horizontal movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->x - vsm->lastx); + vsm->delta = (float)(event->xy[0] - vsm->lastx); break; case 'v': /* vertical scroller - so only vertical movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->y - vsm->lasty); + vsm->delta = (float)(event->xy[1] - vsm->lasty); break; } } @@ -2023,18 +2037,18 @@ static int scroller_activate_modal(bContext *C, wmOperator *op, const wmEvent *e switch (vsm->scroller) { case 'h': /* horizontal scroller - so only horizontal movement * ('cur' moves with mouse) */ - vsm->delta = (float)(vsm->lastx - event->x); + vsm->delta = (float)(vsm->lastx - event->xy[0]); break; case 'v': /* vertical scroller - so only vertical movement * ('cur' moves with to mouse) */ - vsm->delta = (float)(vsm->lasty - event->y); + vsm->delta = (float)(vsm->lasty - event->xy[1]); break; } } /* store previous coordinates */ - vsm->lastx = event->x; - vsm->lasty = event->y; + vsm->lastx = event->xy[0]; + vsm->lasty = event->xy[1]; scroller_activate_apply(C, op); break; @@ -2076,7 +2090,7 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * View2D *v2d = ®ion->v2d; /* check if mouse in scrollbars, if they're enabled */ - const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->x, event->y); + const char in_scroller = UI_view2d_mouse_in_scrollers(region, v2d, event->xy); /* if in a scroller, init customdata then set modal handler which will * catch mouse-down to start doing useful stuff */ @@ -2090,11 +2104,11 @@ static int scroller_activate_invoke(bContext *C, wmOperator *op, const wmEvent * switch (vsm->scroller) { case 'h': /* horizontal scroller - so only horizontal movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->x - vsm->scrollbar_orig); + vsm->delta = (float)(event->xy[0] - vsm->scrollbar_orig); break; case 'v': /* vertical scroller - so only vertical movement * ('cur' moves opposite to mouse) */ - vsm->delta = (float)(event->y - vsm->scrollbar_orig); + vsm->delta = (float)(event->xy[1] - vsm->scrollbar_orig); break; } scroller_activate_apply(C, op); diff --git a/source/blender/editors/io/io_usd.c b/source/blender/editors/io/io_usd.c index d0007d9e5be..4e2ccea36ab 100644 --- a/source/blender/editors/io/io_usd.c +++ b/source/blender/editors/io/io_usd.c @@ -57,7 +57,7 @@ # include "io_usd.h" # include "usd.h" -# include "stdio.h" +# include const EnumPropertyItem rna_enum_usd_export_evaluation_mode_items[] = { {DAG_EVAL_RENDER, diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 35bf295a678..4ad2e57d266 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -18,6 +18,7 @@ set(INC ../include ../uvedit + ../../blenfont ../../blenkernel ../../blenlib ../../blentranslation diff --git a/source/blender/editors/mesh/editmesh_add_gizmo.c b/source/blender/editors/mesh/editmesh_add_gizmo.c index 9efcf0963b4..e2c75435af4 100644 --- a/source/blender/editors/mesh/editmesh_add_gizmo.c +++ b/source/blender/editors/mesh/editmesh_add_gizmo.c @@ -220,8 +220,8 @@ static void gizmo_mesh_placement_modal_from_setup(const bContext *C, wmGizmoGrou float location[3]; calc_initial_placement_point_from_view((bContext *)C, (float[2]){ - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, }, location, mat3); diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 0d74187b50e..332f1f98d81 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -705,7 +705,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else if (etype == MOUSEPAN) { - float delta = 0.02f * (event->y - event->prevy); + float delta = 0.02f * (event->xy[1] - event->prev_xy[1]); if (opdata->segments >= 1 && opdata->segments + delta < 1) { opdata->segments = 1; } @@ -764,8 +764,7 @@ static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event) } } /* Update offset accordingly to new offset_type. */ - if (!has_numinput && - (opdata->value_mode == OFFSET_VALUE || opdata->value_mode == OFFSET_VALUE_PERCENT)) { + if (!has_numinput && (ELEM(opdata->value_mode, OFFSET_VALUE, OFFSET_VALUE_PERCENT))) { edbm_bevel_mouse_set_value(op, event); } edbm_bevel_calc(op); diff --git a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c index ae37d6c8deb..7c289744eeb 100644 --- a/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c +++ b/source/blender/editors/mesh/editmesh_extrude_spin_gizmo.c @@ -461,7 +461,7 @@ void MESH_GGT_spin(struct wmGizmoGroupType *gzgt) gzgt->name = "Mesh Spin Init"; gzgt->idname = "MESH_GGT_spin"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -941,7 +941,7 @@ static void gizmo_mesh_spin_redo_setup(const bContext *C, wmGizmoGroup *gzgroup) /* Use cursor as fallback if it's not set by the 'ortho_axis_active'. */ if (is_zero_v3(ggd->data.orient_axis_relative)) { float cursor_co[3]; - const int mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + const int mval[2] = {event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}; float plane[4]; plane_from_point_normal_v3(plane, plane_co, plane_no); if (UNLIKELY(!ED_view3d_win_to_3d_on_plane_int(region, plane, mval, false, cursor_co))) { @@ -1063,7 +1063,7 @@ void MESH_GGT_spin_redo(struct wmGizmoGroupType *gzgt) gzgt->name = "Mesh Spin Redo"; gzgt->idname = "MESH_GGT_spin_redo"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/mesh/editmesh_intersect.c b/source/blender/editors/mesh/editmesh_intersect.c index f2691580a9d..ee227c58fe7 100644 --- a/source/blender/editors/mesh/editmesh_intersect.c +++ b/source/blender/editors/mesh/editmesh_intersect.c @@ -480,7 +480,8 @@ void MESH_OT_intersect_boolean(struct wmOperatorType *ot) false, "Swap", "Use with difference intersection to swap which side is kept"); - RNA_def_boolean(ot->srna, "use_self", false, "Self", "Do self-union or self-intersection"); + RNA_def_boolean( + ot->srna, "use_self", false, "Self Intersection", "Do self-union or self-intersection"); RNA_def_float_distance( ot->srna, "threshold", 0.000001f, 0.0, 0.01, "Merge Threshold", "", 0.0, 0.001); RNA_def_enum(ot->srna, diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 3e3593d18fd..b712cfc24ed 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -29,6 +29,8 @@ #include "MEM_guardedalloc.h" +#include "BLF_api.h" + #include "BLI_alloca.h" #include "BLI_array.h" #include "BLI_linklist.h" @@ -36,6 +38,7 @@ #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_smallhash.h" +#include "BLI_stack.h" #include "BLI_string.h" #include "BLT_translation.h" @@ -44,15 +47,20 @@ #include "BKE_context.h" #include "BKE_editmesh.h" #include "BKE_editmesh_bvh.h" +#include "BKE_layer.h" #include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_unit.h" #include "GPU_immediate.h" #include "GPU_matrix.h" #include "GPU_state.h" #include "ED_mesh.h" +#include "ED_numinput.h" #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_transform.h" #include "ED_view3d.h" #include "WM_api.h" @@ -69,15 +77,15 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" -#include "mesh_intern.h" /* own include */ +#include "mesh_intern.h" /* Own include. */ -/* detect isolated holes and fill them */ +/* Detect isolated holes and fill them. */ #define USE_NET_ISLAND_CONNECT -#define KMAXDIST (10 * U.dpi_fac) /* max mouse distance from edge before not detecting it */ +#define KMAXDIST (10 * U.dpi_fac) /* Max mouse distance from edge before not detecting it. */ -/* WARNING: knife float precision is fragile: - * be careful before making changes here see: (T43229, T42864, T42459, T41164). +/* WARNING: Knife float precision is fragile: + * Be careful before making changes here see: (T43229, T42864, T42459, T41164). */ #define KNIFE_FLT_EPS 0.00001f #define KNIFE_FLT_EPS_SQUARED (KNIFE_FLT_EPS * KNIFE_FLT_EPS) @@ -87,24 +95,37 @@ #define KNIFE_FLT_EPS_PX_EDGE 0.05f #define KNIFE_FLT_EPS_PX_FACE 0.05f +#define KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT 30.0f +#define KNIFE_MIN_ANGLE_SNAPPING_INCREMENT 0.0f +#define KNIFE_MAX_ANGLE_SNAPPING_INCREMENT 180.0f + typedef struct KnifeColors { uchar line[3]; uchar edge[3]; + uchar edge_extra[3]; uchar curpoint[3]; uchar curpoint_a[4]; uchar point[3]; uchar point_a[4]; + uchar xaxis[3]; + uchar yaxis[3]; + uchar zaxis[3]; + uchar axis_extra[3]; } KnifeColors; -/* knifetool operator */ +/* Knife-tool Operator. */ typedef struct KnifeVert { - BMVert *v; /* non-NULL if this is an original vert */ + Object *ob; + uint base_index; + BMVert *v; /* Non-NULL if this is an original vert. */ ListBase edges; ListBase faces; float co[3], cageco[3]; bool is_face, in_space; - bool is_cut; /* along a cut created by user input (will draw too) */ + bool is_cut; /* Along a cut created by user input (will draw too). */ + bool is_invalid; + bool is_splitting; /* Created when an edge was split. */ } KnifeVert; typedef struct Ref { @@ -114,19 +135,21 @@ typedef struct Ref { typedef struct KnifeEdge { KnifeVert *v1, *v2; - BMFace *basef; /* face to restrict face fill to */ + BMFace *basef; /* Face to restrict face fill to. */ ListBase faces; - BMEdge *e /* , *e_old */; /* non-NULL if this is an original edge */ - bool is_cut; /* along a cut created by user input (will draw too) */ + BMEdge *e; /* Non-NULL if this is an original edge. */ + bool is_cut; /* Along a cut created by user input (will draw too). */ + bool is_invalid; + int splits; /* Number of times this edge has been split. */ } KnifeEdge; typedef struct KnifeLineHit { float hit[3], cagehit[3]; - float schit[2]; /* screen coordinates for cagehit */ - float l; /* lambda along cut line */ - float perc; /* lambda along hit line */ - float m; /* depth front-to-back */ + float schit[2]; /* Screen coordinates for cagehit. */ + float l; /* Lambda along cut line. */ + float perc; /* Lambda along hit line. */ + float m; /* Depth front-to-back. */ /* Exactly one of kfe, v, or f should be non-NULL, * saying whether cut line crosses and edge, @@ -134,6 +157,8 @@ typedef struct KnifeLineHit { KnifeEdge *kfe; KnifeVert *v; BMFace *f; + Object *ob; + uint base_index; } KnifeLineHit; typedef struct KnifePosData { @@ -146,30 +171,59 @@ typedef struct KnifePosData { KnifeVert *vert; KnifeEdge *edge; BMFace *bmface; + Object *ob; /* Object of the vert, edge or bmface. */ + uint base_index; - /** When true, the cursor isn't over a face. */ + /* When true, the cursor isn't over a face. */ bool is_space; - float mval[2]; /* mouse screen position (may be non-integral if snapped to something) */ + float mval[2]; /* Mouse screen position (may be non-integral if snapped to something). */ } KnifePosData; +typedef struct KnifeMeasureData { + float cage[3]; + float mval[2]; + bool is_stored; +} KnifeMeasureData; + +typedef struct KnifeUndoFrame { + int cuts; /* Line hits cause multiple edges/cuts to be created at once. */ + int splits; /* Number of edges split. */ + KnifePosData pos; /* Store previous KnifePosData. */ + KnifeMeasureData mdata; + +} KnifeUndoFrame; + +typedef struct KnifeBVH { + BVHTree *tree; /* Knife Custom BVH Tree. */ + BMLoop *(*looptris)[3]; /* Used by #knife_bvh_raycast_cb to store the intersecting looptri. */ + float uv[2]; /* Used by #knife_bvh_raycast_cb to store the intersecting uv. */ + uint base_index; + + /* Use #bm_ray_cast_cb_elem_not_in_face_check. */ + bool (*filter_cb)(BMFace *f, void *userdata); + void *filter_data; + +} KnifeBVH; + /* struct for properties used while drawing */ typedef struct KnifeTool_OpData { - ARegion *region; /* region that knifetool was activated in */ - void *draw_handle; /* for drawing preview loop */ - ViewContext vc; /* NOTE: _don't_ use 'mval', instead use the one we define below. */ - float mval[2]; /* mouse value with snapping applied */ - // bContext *C; + ARegion *region; /* Region that knifetool was activated in. */ + void *draw_handle; /* For drawing preview loop. */ + ViewContext vc; /* Note: _don't_ use 'mval', instead use the one we define below. */ + float mval[2]; /* Mouse value with snapping applied. */ Scene *scene; - Object *ob; - BMEditMesh *em; + + /* Used for swapping current object when in multi-object edit mode. */ + Object **objects; + uint objects_len; MemArena *arena; - /* reused for edge-net filling */ + /* Reused for edge-net filling. */ struct { - /* cleared each use */ + /* Cleared each use. */ GSet *edge_visit; #ifdef USE_NET_ISLAND_CONNECT MemArena *arena; @@ -181,50 +235,44 @@ typedef struct KnifeTool_OpData { GHash *kedgefacemap; GHash *facetrimap; - BMBVHTree *bmbvh; + KnifeBVH bvh; + const float (**cagecos)[3]; BLI_mempool *kverts; BLI_mempool *kedges; + bool no_cuts; /* A cut has not been made yet. */ + + BLI_Stack *undostack; + BLI_Stack *splitstack; /* Store edge splits by #knife_split_edge. */ float vthresh; float ethresh; - /* used for drag-cutting */ + /* Used for drag-cutting. */ KnifeLineHit *linehits; int totlinehit; - /* Data for mouse-position-derived data */ - KnifePosData curr; /* current point under the cursor */ - KnifePosData prev; /* last added cut (a line draws from the cursor to this) */ - KnifePosData init; /* the first point in the cut-list, used for closing the loop */ + /* Data for mouse-position-derived data. */ + KnifePosData curr; /* Current point under the cursor. */ + KnifePosData prev; /* Last added cut (a line draws from the cursor to this). */ + KnifePosData init; /* The first point in the cut-list, used for closing the loop. */ - /** Number of knife edges `kedges`. */ + /* Number of knife edges `kedges`. */ int totkedge; - /** Number of knife vertices, `kverts`. */ + /* Number of knife vertices, `kverts`. */ int totkvert; BLI_mempool *refs; - /** - * Use this instead of #Object.imat since it's calculated using #invert_m4_m4_safe_ortho - * to support objects with zero scale on a single axis. - */ - float ob_imat[4][4]; - - float projmat[4][4]; - float projmat_inv[4][4]; - /* vector along view z axis (object space, normalized) */ - float proj_zaxis[3]; - KnifeColors colors; - /* run by the UI or not */ + /* Run by the UI or not. */ bool is_interactive; /* Operator options. */ - bool cut_through; /* preference, can be modified at runtime (that feature may go) */ - bool only_select; /* set on initialization */ - bool select_result; /* set on initialization */ + bool cut_through; /* Preference, can be modified at runtime (that feature may go). */ + bool only_select; /* Set on initialization. */ + bool select_result; /* Set on initialization. */ bool is_ortho; float ortho_extent; @@ -240,17 +288,38 @@ typedef struct KnifeTool_OpData { bool ignore_edge_snapping; bool ignore_vert_snapping; - /* use to check if we're currently dragging an angle snapped line */ + NumInput num; + float angle_snapping_increment; /* Degrees */ + + /* Use to check if we're currently dragging an angle snapped line. */ + short angle_snapping_mode; bool is_angle_snapping; bool angle_snapping; float angle; + /* Relative angle snapping reference edge. */ + KnifeEdge *snap_ref_edge; + int snap_ref_edges_count; + int snap_edge; /* Used by #KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE to choose an edge for snapping. */ - const float (*cagecos)[3]; + short constrain_axis; + short constrain_axis_mode; + bool axis_constrained; + char axis_string[2]; + + short dist_angle_mode; + bool show_dist_angle; + KnifeMeasureData mdata; /* Data for distance and angle drawing calculations. */ + + KnifeUndoFrame *undo; /* Current undo frame. */ + bool is_drag_undo; + + bool depth_test; } KnifeTool_OpData; enum { KNF_MODAL_CANCEL = 1, KNF_MODAL_CONFIRM, + KNF_MODAL_UNDO, KNF_MODAL_MIDPOINT_ON, KNF_MODAL_MIDPOINT_OFF, KNF_MODAL_NEW_CUT, @@ -258,45 +327,69 @@ enum { KNF_MODAL_IGNORE_SNAP_OFF, KNF_MODAL_ADD_CUT, KNF_MODAL_ANGLE_SNAP_TOGGLE, + KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE, KNF_MODAL_CUT_THROUGH_TOGGLE, + KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE, + KNF_MODAL_DEPTH_TEST_TOGGLE, KNF_MODAL_PANNING, + KNF_MODAL_X_AXIS, + KNF_MODAL_Y_AXIS, + KNF_MODAL_Z_AXIS, KNF_MODAL_ADD_CUT_CLOSED, }; +enum { + KNF_CONSTRAIN_ANGLE_MODE_NONE = 0, + KNF_CONSTRAIN_ANGLE_MODE_SCREEN = 1, + KNF_CONSTRAIN_ANGLE_MODE_RELATIVE = 2 +}; + +enum { + KNF_CONSTRAIN_AXIS_NONE = 0, + KNF_CONSTRAIN_AXIS_X = 1, + KNF_CONSTRAIN_AXIS_Y = 2, + KNF_CONSTRAIN_AXIS_Z = 3 +}; + +enum { + KNF_CONSTRAIN_AXIS_MODE_NONE = 0, + KNF_CONSTRAIN_AXIS_MODE_GLOBAL = 1, + KNF_CONSTRAIN_AXIS_MODE_LOCAL = 2 +}; + +enum { + KNF_MEASUREMENT_NONE = 0, + KNF_MEASUREMENT_BOTH = 1, + KNF_MEASUREMENT_DISTANCE = 2, + KNF_MEASUREMENT_ANGLE = 3 +}; + /* -------------------------------------------------------------------- */ /** \name Drawing * \{ */ -static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) +static void knifetool_raycast_planes(const KnifeTool_OpData *kcd, float r_v1[3], float r_v2[3]) { - float v1[3], v2[3]; float planes[4][4]; + planes_from_projmat( + kcd->vc.rv3d->persmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); - planes_from_projmat(kcd->projmat, planes[2], planes[0], planes[1], planes[3], NULL, NULL); - - /* ray-cast all planes */ + /* Ray-cast all planes. */ { float ray_dir[3]; float ray_hit_best[2][3] = {{UNPACK3(kcd->prev.cage)}, {UNPACK3(kcd->curr.cage)}}; float lambda_best[2] = {-FLT_MAX, FLT_MAX}; int i; - /* we (sometimes) need the lines to be at the same depth before projecting */ -#if 0 - sub_v3_v3v3(ray_dir, kcd->curr.cage, kcd->prev.cage); -#else { float curr_cage_adjust[3]; float co_depth[3]; copy_v3_v3(co_depth, kcd->prev.cage); - mul_m4_v3(kcd->ob->obmat, co_depth); ED_view3d_win_to_3d(kcd->vc.v3d, kcd->region, co_depth, kcd->curr.mval, curr_cage_adjust); - mul_m4_v3(kcd->ob_imat, curr_cage_adjust); sub_v3_v3v3(ray_dir, curr_cage_adjust, kcd->prev.cage); } -#endif for (i = 0; i < 4; i++) { float ray_hit[3]; @@ -318,9 +411,16 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) } } - copy_v3_v3(v1, ray_hit_best[0]); - copy_v3_v3(v2, ray_hit_best[1]); + copy_v3_v3(r_v1, ray_hit_best[0]); + copy_v3_v3(r_v2, ray_hit_best[1]); } +} + +static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) +{ + float v1[3], v2[3]; + + knifetool_raycast_planes(kcd, v1, v2); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -336,7 +436,447 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) immUnbindProgram(); } -/* modal loop selection drawing callback */ +static void knifetool_draw_orientation_locking(const KnifeTool_OpData *kcd) +{ + if (!compare_v3v3(kcd->prev.cage, kcd->curr.cage, KNIFE_FLT_EPSBIG)) { + float v1[3], v2[3]; + + /* This is causing buggy behavior when `prev.cage` and `curr.cage` are too close together. */ + knifetool_raycast_planes(kcd, v1, v2); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + switch (kcd->constrain_axis) { + case KNF_CONSTRAIN_AXIS_X: { + immUniformColor3ubv(kcd->colors.xaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Y: { + immUniformColor3ubv(kcd->colors.yaxis); + break; + } + case KNF_CONSTRAIN_AXIS_Z: { + immUniformColor3ubv(kcd->colors.zaxis); + break; + } + default: { + immUniformColor3ubv(kcd->colors.axis_extra); + break; + } + } + + GPU_line_width(2.0); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); + + immUnbindProgram(); + } +} + +static void knifetool_draw_visible_distances(const KnifeTool_OpData *kcd) +{ + GPU_matrix_push_projection(); + GPU_matrix_push(); + GPU_matrix_identity_set(); + wmOrtho2_region_pixelspace(kcd->region); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + char numstr[256]; + float numstr_size[2]; + float posit[2]; + const float bg_margin = 4.0f * U.dpi_fac; + const int font_size = 14.0f * U.pixelsize; + const int distance_precision = 4; + + /* Calculate distance and convert to string. */ + const float cut_len = len_v3v3(kcd->prev.cage, kcd->curr.cage); + + UnitSettings *unit = &kcd->scene->unit; + if (unit->system == USER_UNIT_NONE) { + BLI_snprintf(numstr, sizeof(numstr), "%.*f", distance_precision, cut_len); + } + else { + BKE_unit_value_as_string(numstr, + sizeof(numstr), + (double)(cut_len * unit->scale_length), + distance_precision, + B_UNIT_LENGTH, + unit, + false); + } + + BLF_enable(blf_mono_font, BLF_ROTATION); + BLF_size(blf_mono_font, font_size, U.dpi); + BLF_rotation(blf_mono_font, 0.0f); + BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); + + /* Center text. */ + mid_v2_v2v2(posit, kcd->prev.mval, kcd->curr.mval); + posit[0] -= numstr_size[0] / 2.0f; + posit[1] -= numstr_size[1] / 2.0f; + + /* Draw text background. */ + float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */ + immUniformColor4fv(color_back); + + GPU_blend(GPU_BLEND_ALPHA); + immRectf(pos, + posit[0] - bg_margin, + posit[1] - bg_margin, + posit[0] + bg_margin + numstr_size[0], + posit[1] + bg_margin + numstr_size[1]); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); + + /* Draw text. */ + uchar color_text[3]; + UI_GetThemeColor3ubv(TH_TEXT, color_text); + + BLF_color3ubv(blf_mono_font, color_text); + BLF_position(blf_mono_font, posit[0], posit[1], 0.0f); + BLF_draw(blf_mono_font, numstr, sizeof(numstr)); + BLF_disable(blf_mono_font, BLF_ROTATION); + + GPU_matrix_pop(); + GPU_matrix_pop_projection(); +} + +static void knifetool_draw_angle(const KnifeTool_OpData *kcd, + const float start[3], + const float mid[3], + const float end[3], + const float start_ss[2], + const float mid_ss[2], + const float end_ss[2], + const float angle) +{ + const RegionView3D *rv3d = kcd->region->regiondata; + const int arc_steps = 24; + const float arc_size = 64.0f * U.dpi_fac; + const float bg_margin = 4.0f * U.dpi_fac; + const float cap_size = 4.0f * U.dpi_fac; + const int font_size = 14 * U.pixelsize; + const int angle_precision = 3; + + /* Angle arc in 3d space. */ + GPU_blend(GPU_BLEND_ALPHA); + + const uint pos_3d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + { + float dir_tmp[3]; + float ar_coord[3]; + + float dir_a[3]; + float dir_b[3]; + float quat[4]; + float axis[3]; + float arc_angle; + + const float inverse_average_scale = 1 / + (kcd->curr.ob->obmat[0][0] + kcd->curr.ob->obmat[1][1] + + kcd->curr.ob->obmat[2][2]); + + const float px_scale = + 3.0f * inverse_average_scale * + (ED_view3d_pixel_size_no_ui_scale(rv3d, mid) * + min_fff(arc_size, len_v2v2(start_ss, mid_ss) / 2.0f, len_v2v2(end_ss, mid_ss) / 2.0f)); + + sub_v3_v3v3(dir_a, start, mid); + sub_v3_v3v3(dir_b, end, mid); + normalize_v3(dir_a); + normalize_v3(dir_b); + + cross_v3_v3v3(axis, dir_a, dir_b); + arc_angle = angle_normalized_v3v3(dir_a, dir_b); + + axis_angle_to_quat(quat, axis, arc_angle / arc_steps); + + copy_v3_v3(dir_tmp, dir_a); + + immUniformThemeColor3(TH_WIRE); + GPU_line_width(1.0); + + immBegin(GPU_PRIM_LINE_STRIP, arc_steps + 1); + for (int j = 0; j <= arc_steps; j++) { + madd_v3_v3v3fl(ar_coord, mid, dir_tmp, px_scale); + mul_qt_v3(quat, dir_tmp); + + immVertex3fv(pos_3d, ar_coord); + } + immEnd(); + } + + immUnbindProgram(); + + /* Angle text and background in 2d space. */ + GPU_matrix_push_projection(); + GPU_matrix_push(); + GPU_matrix_identity_set(); + wmOrtho2_region_pixelspace(kcd->region); + + uint pos_2d = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + /* Angle as string. */ + char numstr[256]; + float numstr_size[2]; + float posit[2]; + + UnitSettings *unit = &kcd->scene->unit; + if (unit->system == USER_UNIT_NONE) { + BLI_snprintf(numstr, sizeof(numstr), "%.*f°", angle_precision, RAD2DEGF(angle)); + } + else { + BKE_unit_value_as_string( + numstr, sizeof(numstr), (double)angle, angle_precision, B_UNIT_ROTATION, unit, false); + } + + BLF_enable(blf_mono_font, BLF_ROTATION); + BLF_size(blf_mono_font, font_size, U.dpi); + BLF_rotation(blf_mono_font, 0.0f); + BLF_width_and_height(blf_mono_font, numstr, sizeof(numstr), &numstr_size[0], &numstr_size[1]); + + posit[0] = mid_ss[0] + (cap_size * 2.0f); + posit[1] = mid_ss[1] - (numstr_size[1] / 2.0f); + + /* Draw text background. */ + float color_back[4] = {0.0f, 0.0f, 0.0f, 0.5f}; /* TODO: Replace with theme color. */ + immUniformColor4fv(color_back); + + GPU_blend(GPU_BLEND_ALPHA); + immRectf(pos_2d, + posit[0] - bg_margin, + posit[1] - bg_margin, + posit[0] + bg_margin + numstr_size[0], + posit[1] + bg_margin + numstr_size[1]); + GPU_blend(GPU_BLEND_NONE); + immUnbindProgram(); + + /* Draw text. */ + uchar color_text[3]; + UI_GetThemeColor3ubv(TH_TEXT, color_text); + + BLF_color3ubv(blf_mono_font, color_text); + BLF_position(blf_mono_font, posit[0], posit[1], 0.0f); + BLF_rotation(blf_mono_font, 0.0f); + BLF_draw(blf_mono_font, numstr, sizeof(numstr)); + BLF_disable(blf_mono_font, BLF_ROTATION); + + GPU_matrix_pop(); + GPU_matrix_pop_projection(); + + GPU_blend(GPU_BLEND_NONE); +} + +static void knifetool_draw_visible_angles(const KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeVert *kfv; + KnifeVert *tempkfv; + KnifeEdge *kfe; + KnifeEdge *tempkfe; + + if (kcd->curr.vert) { + kfv = kcd->curr.vert; + + float min_angle = FLT_MAX; + float angle = 0.0f; + float *end; + + kfe = ((Ref *)kfv->edges.first)->ref; + for (ref = kfv->edges.first; ref; ref = ref->next) { + tempkfe = ref->ref; + if (tempkfe->v1 != kfv) { + tempkfv = tempkfe->v1; + } + else { + tempkfv = tempkfe->v2; + } + angle = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, tempkfv->cageco); + if (angle < min_angle) { + min_angle = angle; + kfe = tempkfe; + end = tempkfv->cageco; + } + } + + if (min_angle > KNIFE_FLT_EPSBIG) { + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle(kcd, + kcd->prev.cage, + kcd->curr.cage, + end, + kcd->prev.mval, + kcd->curr.mval, + end_ss, + min_angle); + } + } + else if (kcd->curr.edge) { + kfe = kcd->curr.edge; + + /* Check for most recent cut (if cage is part of previous cut). */ + if (!compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) && + !compare_v3v3(kfe->v2->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG)) { + /* Determine acute angle. */ + float angle1 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v1->cageco); + float angle2 = angle_v3v3v3(kcd->prev.cage, kcd->curr.cage, kfe->v2->cageco); + + float angle; + float *end; + if (angle1 < angle2) { + angle = angle1; + end = kfe->v1->cageco; + } + else { + angle = angle2; + end = kfe->v2->cageco; + } + + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle( + kcd, kcd->prev.cage, kcd->curr.cage, end, kcd->prev.mval, kcd->curr.mval, end_ss, angle); + } + } + + if (kcd->prev.vert) { + kfv = kcd->prev.vert; + float min_angle = FLT_MAX; + float angle = 0.0f; + float *end; + + /* If using relative angle snapping, always draw angle to reference edge. */ + if (kcd->is_angle_snapping && kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + kfe = kcd->snap_ref_edge; + if (kfe->v1 != kfv) { + tempkfv = kfe->v1; + } + else { + tempkfv = kfe->v2; + } + min_angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco); + end = tempkfv->cageco; + } + else { + /* Choose minimum angle edge. */ + kfe = ((Ref *)kfv->edges.first)->ref; + for (ref = kfv->edges.first; ref; ref = ref->next) { + tempkfe = ref->ref; + if (tempkfe->v1 != kfv) { + tempkfv = tempkfe->v1; + } + else { + tempkfv = tempkfe->v2; + } + angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, tempkfv->cageco); + if (angle < min_angle) { + min_angle = angle; + kfe = tempkfe; + end = tempkfv->cageco; + } + } + } + + if (min_angle > KNIFE_FLT_EPSBIG) { + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle(kcd, + kcd->curr.cage, + kcd->prev.cage, + end, + kcd->curr.mval, + kcd->prev.mval, + end_ss, + min_angle); + } + } + else if (kcd->prev.edge) { + /* Determine acute angle. */ + kfe = kcd->prev.edge; + float angle1 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v1->cageco); + float angle2 = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kfe->v2->cageco); + + float angle; + float *end; + /* kcd->prev.edge can have one vertex part of cut and one part of mesh? */ + /* This never seems to happen for kcd->curr.edge. */ + if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v1->v) || kfe->v1->is_cut) { + angle = angle2; + end = kfe->v2->cageco; + } + else if ((!kcd->prev.vert || kcd->prev.vert->v == kfe->v2->v) || kfe->v2->is_cut) { + angle = angle1; + end = kfe->v1->cageco; + } + else { + if (angle1 < angle2) { + angle = angle1; + end = kfe->v1->cageco; + } + else { + angle = angle2; + end = kfe->v2->cageco; + } + } + + /* Last vertex in screen space. */ + float end_ss[2]; + ED_view3d_project_float_global(kcd->region, end, end_ss, V3D_PROJ_TEST_NOP); + + knifetool_draw_angle( + kcd, kcd->curr.cage, kcd->prev.cage, end, kcd->curr.mval, kcd->prev.mval, end_ss, angle); + } + else if (kcd->mdata.is_stored && !kcd->prev.is_space) { + float angle = angle_v3v3v3(kcd->curr.cage, kcd->prev.cage, kcd->mdata.cage); + knifetool_draw_angle(kcd, + kcd->curr.cage, + kcd->prev.cage, + kcd->mdata.cage, + kcd->curr.mval, + kcd->prev.mval, + kcd->mdata.mval, + angle); + } +} + +static void knifetool_draw_dist_angle(const KnifeTool_OpData *kcd) +{ + switch (kcd->dist_angle_mode) { + case KNF_MEASUREMENT_BOTH: { + knifetool_draw_visible_distances(kcd); + knifetool_draw_visible_angles(kcd); + break; + } + case KNF_MEASUREMENT_DISTANCE: { + knifetool_draw_visible_distances(kcd); + break; + } + case KNF_MEASUREMENT_ANGLE: { + knifetool_draw_visible_angles(kcd); + break; + } + } +} + +/* Modal loop selection drawing callback. */ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), void *arg) { const KnifeTool_OpData *kcd = arg; @@ -345,13 +885,6 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_matrix_push_projection(); GPU_polygon_offset(1.0f, 1.0f); - GPU_matrix_push(); - GPU_matrix_mul(kcd->ob->obmat); - - if (kcd->mode == MODE_DRAGGING && kcd->is_angle_snapping) { - knifetool_draw_angle_snapping(kcd); - } - GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); @@ -412,48 +945,8 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v immEnd(); } - if (kcd->totlinehit > 0) { - KnifeLineHit *lh; - int i, snapped_verts_count, other_verts_count; - float fcol[4]; - - GPU_blend(GPU_BLEND_ALPHA); - - GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); - GPU_vertbuf_data_alloc(vert, kcd->totlinehit); - - lh = kcd->linehits; - for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) { - if (lh->v) { - GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit); - } - else { - GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit); - } - } - - GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO); - GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); - - /* draw any snapped verts first */ - rgba_uchar_to_float(fcol, kcd->colors.point_a); - GPU_batch_uniform_4fv(batch, "color", fcol); - GPU_point_size(11 * UI_DPI_FAC); - if (snapped_verts_count > 0) { - GPU_batch_draw_range(batch, 0, snapped_verts_count); - } - - /* now draw the rest */ - rgba_uchar_to_float(fcol, kcd->colors.curpoint_a); - GPU_batch_uniform_4fv(batch, "color", fcol); - GPU_point_size(7 * UI_DPI_FAC); - if (other_verts_count > 0) { - GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count); - } - - GPU_batch_discard(batch); - - GPU_blend(GPU_BLEND_NONE); + if (kcd->depth_test) { + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } if (kcd->totkedge > 0) { @@ -467,7 +960,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v BLI_mempool_iternew(kcd->kedges, &iter); for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) { - if (!kfe->is_cut) { + if (!kfe->is_cut || kfe->is_invalid) { continue; } @@ -492,7 +985,7 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v BLI_mempool_iternew(kcd->kverts, &iter); for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) { - if (!kfv->is_cut) { + if (!kfv->is_cut || kfv->is_invalid) { continue; } @@ -505,12 +998,81 @@ static void knifetool_draw(const bContext *UNUSED(C), ARegion *UNUSED(region), v GPU_batch_discard(batch); } + /* Draw relative angle snapping reference edge. */ + if (kcd->is_angle_snapping && kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + immUniformColor3ubv(kcd->colors.edge_extra); + GPU_line_width(2.0); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, kcd->snap_ref_edge->v1->cageco); + immVertex3fv(pos, kcd->snap_ref_edge->v2->cageco); + immEnd(); + } + + if (kcd->totlinehit > 0) { + KnifeLineHit *lh; + int i, snapped_verts_count, other_verts_count; + float fcol[4]; + + GPU_blend(GPU_BLEND_ALPHA); + + GPUVertBuf *vert = GPU_vertbuf_create_with_format(format); + GPU_vertbuf_data_alloc(vert, kcd->totlinehit); + + lh = kcd->linehits; + for (i = 0, snapped_verts_count = 0, other_verts_count = 0; i < kcd->totlinehit; i++, lh++) { + if (lh->v) { + GPU_vertbuf_attr_set(vert, pos, snapped_verts_count++, lh->cagehit); + } + else { + GPU_vertbuf_attr_set(vert, pos, kcd->totlinehit - 1 - other_verts_count++, lh->cagehit); + } + } + + GPUBatch *batch = GPU_batch_create_ex(GPU_PRIM_POINTS, vert, NULL, GPU_BATCH_OWNS_VBO); + GPU_batch_program_set_builtin(batch, GPU_SHADER_3D_UNIFORM_COLOR); + + /* Draw any snapped verts first. */ + rgba_uchar_to_float(fcol, kcd->colors.point_a); + GPU_batch_uniform_4fv(batch, "color", fcol); + GPU_point_size(11 * UI_DPI_FAC); + if (snapped_verts_count > 0) { + GPU_batch_draw_range(batch, 0, snapped_verts_count); + } + + /* Now draw the rest. */ + rgba_uchar_to_float(fcol, kcd->colors.curpoint_a); + GPU_batch_uniform_4fv(batch, "color", fcol); + GPU_point_size(7 * UI_DPI_FAC); + if (other_verts_count > 0) { + GPU_batch_draw_range(batch, snapped_verts_count, other_verts_count); + } + + GPU_batch_discard(batch); + + GPU_blend(GPU_BLEND_NONE); + } + immUnbindProgram(); - GPU_matrix_pop(); + GPU_depth_test(GPU_DEPTH_NONE); + + if (kcd->mode == MODE_DRAGGING) { + if (kcd->is_angle_snapping) { + knifetool_draw_angle_snapping(kcd); + } + else if (kcd->axis_constrained) { + knifetool_draw_orientation_locking(kcd); + } + + if (kcd->show_dist_angle) { + knifetool_draw_dist_angle(kcd); + } + } + GPU_matrix_pop_projection(); - /* Reset default */ + /* Reset default. */ GPU_depth_test(GPU_DEPTH_LESS_EQUAL); } @@ -532,27 +1094,53 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k WM_modalkeymap_operator_items_to_string_buf( \ op->type, (_id), true, UI_MAX_SHORTCUT_STR, &available_len, &p) - BLI_snprintf(header, - sizeof(header), - TIP_("%s: confirm, %s: cancel, " - "%s: start/define cut, %s: close cut, %s: new cut, " - "%s: midpoint snap (%s), %s: ignore snap (%s), " - "%s: angle constraint (%s), %s: cut through (%s), " - "%s: panning"), - WM_MODALKEY(KNF_MODAL_CONFIRM), - WM_MODALKEY(KNF_MODAL_CANCEL), - WM_MODALKEY(KNF_MODAL_ADD_CUT), - WM_MODALKEY(KNF_MODAL_ADD_CUT_CLOSED), - WM_MODALKEY(KNF_MODAL_NEW_CUT), - WM_MODALKEY(KNF_MODAL_MIDPOINT_ON), - WM_bool_as_string(kcd->snap_midpoints), - WM_MODALKEY(KNF_MODAL_IGNORE_SNAP_ON), - WM_bool_as_string(kcd->ignore_edge_snapping), - WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE), - WM_bool_as_string(kcd->angle_snapping), - WM_MODALKEY(KNF_MODAL_CUT_THROUGH_TOGGLE), - WM_bool_as_string(kcd->cut_through), - WM_MODALKEY(KNF_MODAL_PANNING)); + BLI_snprintf( + header, + sizeof(header), + TIP_("%s: confirm, %s: cancel, %s: undo, " + "%s: start/define cut, %s: close cut, %s: new cut, " + "%s: midpoint snap (%s), %s: ignore snap (%s), " + "%s: angle constraint %.2f(%.2f) (%s%s%s%s), %s: cut through (%s), " + "%s: panning, %s%s%s: orientation lock (%s), " + "%s: distance/angle measurements (%s), " + "%s: x-ray (%s)"), + WM_MODALKEY(KNF_MODAL_CONFIRM), + WM_MODALKEY(KNF_MODAL_CANCEL), + WM_MODALKEY(KNF_MODAL_UNDO), + WM_MODALKEY(KNF_MODAL_ADD_CUT), + WM_MODALKEY(KNF_MODAL_ADD_CUT_CLOSED), + WM_MODALKEY(KNF_MODAL_NEW_CUT), + WM_MODALKEY(KNF_MODAL_MIDPOINT_ON), + WM_bool_as_string(kcd->snap_midpoints), + WM_MODALKEY(KNF_MODAL_IGNORE_SNAP_ON), + WM_bool_as_string(kcd->ignore_edge_snapping), + WM_MODALKEY(KNF_MODAL_ANGLE_SNAP_TOGGLE), + (kcd->angle >= 0.0f) ? RAD2DEGF(kcd->angle) : 360.0f + RAD2DEGF(kcd->angle), + (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + kcd->angle_snapping_increment <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) ? + kcd->angle_snapping_increment : + KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT, + kcd->angle_snapping ? + ((kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_SCREEN) ? "Screen" : "Relative") : + "OFF", + /* TODO: Can this be simplified? */ + (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? " - " : "", + (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? + WM_MODALKEY(KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE) : + "", + (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) ? ": cycle edge" : "", + /**/ + WM_MODALKEY(KNF_MODAL_CUT_THROUGH_TOGGLE), + WM_bool_as_string(kcd->cut_through), + WM_MODALKEY(KNF_MODAL_PANNING), + WM_MODALKEY(KNF_MODAL_X_AXIS), + WM_MODALKEY(KNF_MODAL_Y_AXIS), + WM_MODALKEY(KNF_MODAL_Z_AXIS), + (kcd->axis_constrained ? kcd->axis_string : WM_bool_as_string(kcd->axis_constrained)), + WM_MODALKEY(KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE), + WM_bool_as_string(kcd->show_dist_angle), + WM_MODALKEY(KNF_MODAL_DEPTH_TEST_TOGGLE), + WM_bool_as_string(!kcd->depth_test)); #undef WM_MODALKEY @@ -561,28 +1149,334 @@ static void knife_update_header(bContext *C, wmOperator *op, KnifeTool_OpData *k /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Knife BVH Utils + * \{ */ + +static bool knife_bm_face_is_select(BMFace *f) +{ + return (BM_elem_flag_test(f, BM_ELEM_SELECT) != 0); +} + +static bool knife_bm_face_is_not_hidden(BMFace *f) +{ + return (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == 0); +} + +static void knife_bvh_init(KnifeTool_OpData *kcd) +{ + Object *ob; + BMEditMesh *em; + + /* Test Function. */ + bool (*test_fn)(BMFace *); + if ((kcd->only_select && kcd->cut_through)) { + test_fn = knife_bm_face_is_select; + } + else { + test_fn = knife_bm_face_is_not_hidden; + } + + /* Construct BVH Tree. */ + float cos[3][3]; + const float epsilon = FLT_EPSILON * 2.0f; + int tottri = 0; + int ob_tottri = 0; + BMLoop *(*looptris)[3]; + BMFace *f_test = NULL, *f_test_prev = NULL; + bool test_fn_ret = false; + + /* Calculate tottri. */ + for (uint b = 0; b < kcd->objects_len; b++) { + ob_tottri = 0; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + + for (int i = 0; i < em->tottri; i++) { + f_test = em->looptris[i][0]->f; + if (f_test != f_test_prev) { + test_fn_ret = test_fn(f_test); + f_test_prev = f_test; + } + + if (test_fn_ret) { + ob_tottri++; + } + } + + tottri += ob_tottri; + } + + kcd->bvh.tree = BLI_bvhtree_new(tottri, epsilon, 8, 8); + + f_test_prev = NULL; + test_fn_ret = false; + + /* Add tri's for each object. + * TODO: + * test_fn can leave large gaps between bvh tree indices. + * Compacting bvh tree indices may be possible. + * Don't forget to update #knife_bvh_intersect_plane! + */ + tottri = 0; + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + looptris = em->looptris; + + for (int i = 0; i < em->tottri; i++) { + + f_test = looptris[i][0]->f; + if (f_test != f_test_prev) { + test_fn_ret = test_fn(f_test); + f_test_prev = f_test; + } + + if (!test_fn_ret) { + continue; + } + + copy_v3_v3(cos[0], kcd->cagecos[b][BM_elem_index_get(looptris[i][0]->v)]); + copy_v3_v3(cos[1], kcd->cagecos[b][BM_elem_index_get(looptris[i][1]->v)]); + copy_v3_v3(cos[2], kcd->cagecos[b][BM_elem_index_get(looptris[i][2]->v)]); + + /* Convert to world-space. */ + mul_m4_v3(ob->obmat, cos[0]); + mul_m4_v3(ob->obmat, cos[1]); + mul_m4_v3(ob->obmat, cos[2]); + + BLI_bvhtree_insert(kcd->bvh.tree, i + tottri, (float *)cos, 3); + } + + tottri += em->tottri; + } + + BLI_bvhtree_balance(kcd->bvh.tree); +} + +/* Wrapper for #BLI_bvhtree_free. */ +static void knife_bvh_free(KnifeTool_OpData *kcd) +{ + if (kcd->bvh.tree) { + BLI_bvhtree_free(kcd->bvh.tree); + kcd->bvh.tree = NULL; + } +} + +static void knife_bvh_raycast_cb(void *userdata, + int index, + const BVHTreeRay *ray, + BVHTreeRayHit *hit) +{ + KnifeTool_OpData *kcd = userdata; + BMLoop **ltri; + Object *ob; + BMEditMesh *em; + + float dist, uv[2]; + bool isect; + int tottri; + float tri_cos[3][3]; + + if (index != -1) { + tottri = 0; + uint b = 0; + for (; b < kcd->objects_len; b++) { + index -= tottri; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + tottri = em->tottri; + if (index < tottri) { + ltri = em->looptris[index]; + break; + } + } + + if (kcd->bvh.filter_cb) { + if (!kcd->bvh.filter_cb(ltri[0]->f, kcd->bvh.filter_data)) { + return; + } + } + + copy_v3_v3(tri_cos[0], kcd->cagecos[b][BM_elem_index_get(ltri[0]->v)]); + copy_v3_v3(tri_cos[1], kcd->cagecos[b][BM_elem_index_get(ltri[1]->v)]); + copy_v3_v3(tri_cos[2], kcd->cagecos[b][BM_elem_index_get(ltri[2]->v)]); + mul_m4_v3(ob->obmat, tri_cos[0]); + mul_m4_v3(ob->obmat, tri_cos[1]); + mul_m4_v3(ob->obmat, tri_cos[2]); + + isect = + (ray->radius > 0.0f ? + isect_ray_tri_epsilon_v3(ray->origin, + ray->direction, + tri_cos[0], + tri_cos[1], + tri_cos[2], + &dist, + uv, + ray->radius) : +#ifdef USE_KDOPBVH_WATERTIGHT + isect_ray_tri_watertight_v3( + ray->origin, ray->isect_precalc, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); +#else + isect_ray_tri_v3( + ray->origin, ray->direction, tri_cos[0], tri_cos[1], tri_cos[2], &dist, uv)); +#endif + + if (isect && dist < hit->dist) { + hit->dist = dist; + hit->index = index; + + copy_v3_v3(hit->no, ltri[0]->f->no); + + madd_v3_v3v3fl(hit->co, ray->origin, ray->direction, dist); + + kcd->bvh.looptris = em->looptris; + copy_v2_v2(kcd->bvh.uv, uv); + kcd->bvh.base_index = b; + } + } +} + +/* `co` is expected to be in world space. */ +static BMFace *knife_bvh_raycast(KnifeTool_OpData *kcd, + const float co[3], + const float dir[3], + const float radius, + float *r_dist, + float r_hitout[3], + float r_cagehit[3], + uint *r_base_index) +{ + BMFace *face; + BMLoop **ltri; + BVHTreeRayHit hit; + const float dist = r_dist ? *r_dist : FLT_MAX; + hit.dist = dist; + hit.index = -1; + + BLI_bvhtree_ray_cast(kcd->bvh.tree, co, dir, radius, &hit, knife_bvh_raycast_cb, kcd); + + /* Handle Hit */ + if (hit.index != -1 && hit.dist != dist) { + face = kcd->bvh.looptris[hit.index][0]->f; + + /* Hits returned in world space. */ + if (r_hitout) { + ltri = kcd->bvh.looptris[hit.index]; + interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + + if (r_cagehit) { + copy_v3_v3(r_cagehit, hit.co); + } + } + + if (r_dist) { + *r_dist = hit.dist; + } + + if (r_base_index) { + *r_base_index = kcd->bvh.base_index; + } + + return face; + } + return NULL; +} + +/* `co` is expected to be in world space. */ +static BMFace *knife_bvh_raycast_filter(KnifeTool_OpData *kcd, + const float co[3], + const float dir[3], + const float radius, + float *r_dist, + float r_hitout[3], + float r_cagehit[3], + uint *r_base_index, + bool (*filter_cb)(BMFace *f, void *userdata), + void *filter_userdata) +{ + kcd->bvh.filter_cb = filter_cb; + kcd->bvh.filter_data = filter_userdata; + + BMFace *face; + BMLoop **ltri; + BVHTreeRayHit hit; + const float dist = r_dist ? *r_dist : FLT_MAX; + hit.dist = dist; + hit.index = -1; + + BLI_bvhtree_ray_cast(kcd->bvh.tree, co, dir, radius, &hit, knife_bvh_raycast_cb, kcd); + + kcd->bvh.filter_cb = NULL; + kcd->bvh.filter_data = NULL; + + /* Handle Hit */ + if (hit.index != -1 && hit.dist != dist) { + face = kcd->bvh.looptris[hit.index][0]->f; + + /* Hits returned in world space. */ + if (r_hitout) { + ltri = kcd->bvh.looptris[hit.index]; + interp_v3_v3v3v3_uv(r_hitout, ltri[0]->v->co, ltri[1]->v->co, ltri[2]->v->co, kcd->bvh.uv); + + if (r_cagehit) { + copy_v3_v3(r_cagehit, hit.co); + } + } + + if (r_dist) { + *r_dist = hit.dist; + } + + if (r_base_index) { + *r_base_index = kcd->bvh.base_index; + } + + return face; + } + return NULL; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Geometry Utils * \{ */ static void knife_project_v2(const KnifeTool_OpData *kcd, const float co[3], float sco[2]) { - ED_view3d_project_float_v2_m4(kcd->region, co, sco, (float(*)[4])kcd->projmat); + ED_view3d_project_float_global(kcd->region, co, sco, V3D_PROJ_TEST_NOP); } +/* Ray is returned in world space. */ static void knife_input_ray_segment(KnifeTool_OpData *kcd, const float mval[2], const float ofs, float r_origin[3], float r_origin_ofs[3]) { - /* unproject to find view ray */ + /* Unproject to find view ray. */ ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], 0.0f, r_origin); ED_view3d_unproject_v3(kcd->vc.region, mval[0], mval[1], ofs, r_origin_ofs); +} - /* transform into object space */ - mul_m4_v3(kcd->ob_imat, r_origin); - mul_m4_v3(kcd->ob_imat, r_origin_ofs); +/* No longer used, but may be useful in the future. */ +static void UNUSED_FUNCTION(knifetool_recast_cageco)(KnifeTool_OpData *kcd, + float mval[3], + float r_cage[3]) +{ + float origin[3]; + float origin_ofs[3]; + float ray[3], ray_normal[3]; + float co[3]; /* Unused. */ + + knife_input_ray_segment(kcd, mval, 1.0f, origin, origin_ofs); + + sub_v3_v3v3(ray, origin_ofs, origin); + normalize_v3_v3(ray_normal, ray); + + knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, co, r_cage, NULL); } static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) @@ -599,11 +1493,11 @@ static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) l2 = v2->v ? BM_face_vert_share_loop(f, v2->v) : NULL; if ((l1 && l2) && BM_loop_is_adjacent(l1, l2)) { - /* boundary-case, always false to avoid edge-in-face checks below */ + /* Boundary-case, always false to avoid edge-in-face checks below. */ return false; } - /* find out if v1 and v2, if set, are part of the face */ + /* Find out if v1 and v2, if set, are part of the face. */ v1_inface = (l1 != NULL); v2_inface = (l2 != NULL); @@ -619,22 +1513,15 @@ static bool knife_verts_edge_in_face(KnifeVert *v1, KnifeVert *v2, BMFace *f) /* Can have case where v1 and v2 are on shared chain between two faces. * BM_face_splits_check_legal does visibility and self-intersection tests, * but it is expensive and maybe a bit buggy, so use a simple - * "is the midpoint in the face" test */ + * "is the midpoint in the face" test. */ mid_v3_v3v3(mid, v1->co, v2->co); return BM_face_point_inside_test(f, mid); } return false; } -static void knife_recalc_projmat(KnifeTool_OpData *kcd) +static void knife_recalc_ortho(KnifeTool_OpData *kcd) { - ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, kcd->projmat); - invert_m4_m4(kcd->projmat_inv, kcd->projmat); - - invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); - mul_v3_mat3_m4v3(kcd->proj_zaxis, kcd->ob_imat, kcd->vc.rv3d->viewinv[2]); - normalize_v3(kcd->proj_zaxis); - kcd->is_ortho = ED_view3d_clip_range_get( kcd->vc.depsgraph, kcd->vc.v3d, kcd->vc.rv3d, &kcd->clipsta, &kcd->clipend, true); } @@ -762,7 +1649,7 @@ static void knife_add_edge_faces_to_vert(KnifeTool_OpData *kcd, KnifeVert *kfv, } /* Find a face in common in the two faces lists. - * If more than one, return the first; if none, return NULL */ + * If more than one, return the first; if none, return NULL. */ static BMFace *knife_find_common_face(ListBase *faces1, ListBase *faces2) { Ref *ref1, *ref2; @@ -797,12 +1684,13 @@ static KnifeVert *new_knife_vert(KnifeTool_OpData *kcd, const float co[3], const static KnifeEdge *new_knife_edge(KnifeTool_OpData *kcd) { + KnifeEdge *kfe = BLI_mempool_calloc(kcd->kedges); kcd->totkedge++; - return BLI_mempool_calloc(kcd->kedges); + return kfe; } -/* get a KnifeVert wrapper for an existing BMVert */ -static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v) +/* Get a KnifeVert wrapper for an existing BMVert. */ +static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v, Object *ob, uint base_index) { KnifeVert *kfv = BLI_ghash_lookup(kcd->origvertmap, v); const float *cageco; @@ -812,13 +1700,20 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v) BMFace *f; if (BM_elem_index_get(v) >= 0) { - cageco = kcd->cagecos[BM_elem_index_get(v)]; + cageco = kcd->cagecos[base_index][BM_elem_index_get(v)]; } else { cageco = v->co; } - kfv = new_knife_vert(kcd, v->co, cageco); + + float cageco_ws[3]; + mul_v3_m4v3(cageco_ws, ob->obmat, cageco); + + kfv = new_knife_vert(kcd, v->co, cageco_ws); kfv->v = v; + kfv->ob = ob; + kfv->base_index = base_index; + BLI_ghash_insert(kcd->origvertmap, v, kfv); BM_ITER_ELEM (f, &bmiter, v, BM_FACES_OF_VERT) { knife_append_list(kcd, &kfv->faces, f); @@ -828,8 +1723,8 @@ static KnifeVert *get_bm_knife_vert(KnifeTool_OpData *kcd, BMVert *v) return kfv; } -/* get a KnifeEdge wrapper for an existing BMEdge */ -static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e) +/* Get a KnifeEdge wrapper for an existing BMEdge. */ +static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e, Object *ob, uint base_index) { KnifeEdge *kfe = BLI_ghash_lookup(kcd->origedgemap, e); if (!kfe) { @@ -838,8 +1733,8 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e) kfe = new_knife_edge(kcd); kfe->e = e; - kfe->v1 = get_bm_knife_vert(kcd, e->v1); - kfe->v2 = get_bm_knife_vert(kcd, e->v2); + kfe->v1 = get_bm_knife_vert(kcd, e->v1, ob, base_index); + kfe->v2 = get_bm_knife_vert(kcd, e->v2, ob, base_index); knife_add_to_vert_edges(kcd, kfe); @@ -853,7 +1748,10 @@ static KnifeEdge *get_bm_knife_edge(KnifeTool_OpData *kcd, BMEdge *e) return kfe; } -static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f) +static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, + Object *ob, + uint base_index, + BMFace *f) { ListBase *list = BLI_ghash_lookup(kcd->kedgefacemap, f); @@ -864,7 +1762,7 @@ static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f) list = knife_empty_list(kcd); BM_ITER_ELEM (e, &bmiter, f, BM_EDGES_OF_FACE) { - knife_append_list(kcd, list, get_bm_knife_edge(kcd, e)); + knife_append_list(kcd, list, get_bm_knife_edge(kcd, e, ob, base_index)); } BLI_ghash_insert(kcd->kedgefacemap, f, list); @@ -875,7 +1773,7 @@ static ListBase *knife_get_face_kedges(KnifeTool_OpData *kcd, BMFace *f) static void knife_edge_append_face(KnifeTool_OpData *kcd, KnifeEdge *kfe, BMFace *f) { - knife_append_list(kcd, knife_get_face_kedges(kcd, f), kfe); + knife_append_list(kcd, knife_get_face_kedges(kcd, kfe->v1->ob, kfe->v1->base_index, f), kfe); knife_append_list(kcd, &kfe->faces, f); } @@ -891,6 +1789,8 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, newkfe->v1 = kfe->v1; newkfe->v2 = new_knife_vert(kcd, co, cageco); + newkfe->v2->ob = kfe->v1->ob; + newkfe->v2->base_index = kfe->v1->base_index; newkfe->v2->is_cut = true; if (kfe->e) { knife_add_edge_faces_to_vert(kcd, newkfe->v2, kfe->e); @@ -898,7 +1798,7 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, else { /* kfe cuts across an existing face. * If v1 and v2 are in multiple faces together (e.g., if they - * are in doubled polys) then this arbitrarily chooses one of them */ + * are in doubled polys) then this arbitrarily chooses one of them. */ f = knife_find_common_face(&kfe->v1->faces, &kfe->v2->faces); if (f) { knife_append_list(kcd, &newkfe->v2->faces, f); @@ -910,6 +1810,7 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, BLI_remlink(&kfe->v1->edges, ref); kfe->v1 = newkfe->v2; + kfe->v1->is_splitting = true; BLI_addtail(&kfe->v1->edges, ref); for (ref = kfe->faces.first; ref; ref = ref->next) { @@ -921,11 +1822,32 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, newkfe->is_cut = kfe->is_cut; newkfe->e = kfe->e; + newkfe->splits++; + kfe->splits++; + + kcd->undo->splits++; + + BLI_stack_push(kcd->splitstack, (void *)&kfe); + BLI_stack_push(kcd->splitstack, (void *)&newkfe); + *r_kfe = newkfe; return newkfe->v2; } +/* Rejoin two edges split by #knife_split_edge. */ +static void knife_join_edge(KnifeEdge *newkfe, KnifeEdge *kfe) +{ + newkfe->is_invalid = true; + newkfe->v2->is_invalid = true; + + kfe->v1 = newkfe->v1; + + kfe->splits--; + kfe->v1->is_splitting = false; + kfe->v2->is_splitting = false; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -937,18 +1859,19 @@ static KnifeVert *knife_split_edge(KnifeTool_OpData *kcd, static void knife_start_cut(KnifeTool_OpData *kcd) { kcd->prev = kcd->curr; - kcd->curr.is_space = 0; /* TODO: why do we do this? */ + kcd->curr.is_space = 0; /* TODO: Why do we do this? */ + kcd->mdata.is_stored = false; if (kcd->prev.vert == NULL && kcd->prev.edge == NULL) { float origin[3], origin_ofs[3]; float ofs_local[3]; negate_v3_v3(ofs_local, kcd->vc.rv3d->ofs); - mul_m4_v3(kcd->ob_imat, ofs_local); knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); - if (!isect_line_plane_v3(kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->proj_zaxis)) { + if (!isect_line_plane_v3( + kcd->prev.cage, origin, origin_ofs, ofs_local, kcd->vc.rv3d->viewinv[2])) { zero_v3(kcd->prev.cage); } @@ -968,9 +1891,9 @@ static void linehit_to_knifepos(KnifePosData *kpos, KnifeLineHit *lh) copy_v2_v2(kpos->mval, lh->schit); } -/* primary key: lambda along cut - * secondary key: lambda along depth - * tertiary key: pointer comparisons of verts if both snapped to verts +/* Primary key: lambda along cut + * Secondary key: lambda along depth + * Tertiary key: pointer comparisons of verts if both snapped to verts */ static int linehit_compare(const void *vlh1, const void *vlh2) { @@ -1049,19 +1972,19 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd) } if (is_double) { - /* delete-in-place loop: copying from pos j to pos i+1 */ + /* Delete-in-place loop: copying from pos j to pos i+1. */ int i = 0; int j = 1; while (j < n) { KnifeLineHit *lhi = &linehits[i]; KnifeLineHit *lhj = &linehits[j]; if (lhj->l == -1.0f) { - j++; /* skip copying this one */ + j++; /* Skip copying this one. */ } else { - /* copy unless a no-op */ + /* Copy unless a no-op. */ if (lhi->l == -1.0f) { - /* could happen if linehits[0] is being deleted */ + /* Could happen if linehits[0] is being deleted. */ memcpy(&linehits[i], &linehits[j], sizeof(KnifeLineHit)); } else { @@ -1077,7 +2000,7 @@ static void prepare_linehits_for_cut(KnifeTool_OpData *kcd) } } -/* Add hit to list of hits in facehits[f], where facehits is a map, if not already there */ +/* Add hit to list of hits in facehits[f], where facehits is a map, if not already there. */ static void add_hit_to_facehits(KnifeTool_OpData *kcd, GHash *facehits, BMFace *f, @@ -1093,8 +2016,8 @@ static void add_hit_to_facehits(KnifeTool_OpData *kcd, } /** - * special purpose function, if the linehit is connected to a real edge/vert - * return true if \a co is outside the face. + * Special purpose function, if the linehit is connected to a real edge/vert. + * Return true if \a co is outside the face. */ static bool knife_add_single_cut__is_linehit_outside_face(BMFace *f, const KnifeLineHit *lh, @@ -1131,12 +2054,9 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, return; } - /* if the cut is on an edge, just tag that its a cut and return */ + /* If the cut is on an edge. */ if ((lh1->v && lh2->v) && (lh1->v->v && lh2->v && lh2->v->v) && (e_base = BM_edge_exists(lh1->v->v, lh2->v->v))) { - kfe = get_bm_knife_edge(kcd, e_base); - kfe->is_cut = true; - kfe->e = e_base; return; } if (knife_add_single_cut__is_linehit_outside_face(f, lh1, lh2->hit) || @@ -1144,7 +2064,7 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, return; } - /* Check if edge actually lies within face (might not, if this face is concave) */ + /* Check if edge actually lies within face (might not, if this face is concave). */ if ((lh1->v && !lh1->kfe) && (lh2->v && !lh2->kfe)) { if (!knife_verts_edge_in_face(lh1->v, lh2->v, f)) { return; @@ -1165,6 +2085,8 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, else { BLI_assert(lh1->f); kfe->v1 = new_knife_vert(kcd, lh1->hit, lh1->cagehit); + kfe->v1->ob = lh1->ob; + kfe->v1->base_index = lh1->base_index; kfe->v1->is_cut = true; kfe->v1->is_face = true; knife_append_list(kcd, &kfe->v1->faces, lh1->f); @@ -1176,23 +2098,27 @@ static void knife_add_single_cut(KnifeTool_OpData *kcd, } else if (lh2->kfe) { kfe->v2 = knife_split_edge(kcd, lh2->kfe, lh2->hit, lh2->cagehit, &kfe2); - lh2->v = kfe->v2; /* future uses of lh2 won't split again */ + lh2->v = kfe->v2; /* Future uses of lh2 won't split again. */ } else { BLI_assert(lh2->f); kfe->v2 = new_knife_vert(kcd, lh2->hit, lh2->cagehit); + kfe->v2->ob = lh2->ob; + kfe->v2->base_index = lh2->base_index; kfe->v2->is_cut = true; kfe->v2->is_face = true; knife_append_list(kcd, &kfe->v2->faces, lh2->f); - lh2->v = kfe->v2; /* record the KnifeVert for this hit */ + lh2->v = kfe->v2; /* Record the KnifeVert for this hit. */ } knife_add_to_vert_edges(kcd, kfe); - /* TODO: check if this is ever needed */ if (kfe->basef && !find_ref(&kfe->faces, kfe->basef)) { knife_edge_append_face(kcd, kfe, kfe->basef); } + + /* Update current undo frame cut count. */ + kcd->undo->cuts++; } /* Given a list of KnifeLineHits for one face, sorted by l @@ -1212,9 +2138,8 @@ static void knife_cut_face(KnifeTool_OpData *kcd, BMFace *f, ListBase *hits) } } -static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfedges) +static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMesh *bm, BMFace *f, ListBase *kfedges) { - BMesh *bm = kcd->em->bm; KnifeEdge *kfe; Ref *ref; int edge_array_len = BLI_listbase_count(kfedges); @@ -1222,7 +2147,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe BMEdge **edge_array = BLI_array_alloca(edge_array, edge_array_len); - /* point to knife edges we've created edges in, edge_array aligned */ + /* Point to knife edges we've created edges in, edge_array aligned. */ KnifeEdge **kfe_array = BLI_array_alloca(kfe_array, edge_array_len); BLI_assert(BLI_gset_len(kcd->edgenet.edge_visit) == 0); @@ -1232,6 +2157,10 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe bool is_new_edge = false; kfe = ref->ref; + if (kfe->is_invalid) { + continue; + } + if (kfe->e == NULL) { if (kfe->v1->v && kfe->v2->v) { kfe->e = BM_edge_exists(kfe->v1->v, kfe->v2->v); @@ -1240,7 +2169,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe if (kfe->e) { if (BM_edge_in_face(kfe->e, f)) { - /* shouldn't happen, but in this case - just ignore */ + /* Shouldn't happen, but in this case just ignore. */ continue; } } @@ -1292,7 +2221,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe } edge_array_len = edge_array_holes_len; - edge_array = edge_array_holes; /* owned by the arena */ + edge_array = edge_array_holes; /* Owned by the arena. */ } #endif @@ -1307,7 +2236,7 @@ static void knife_make_face_cuts(KnifeTool_OpData *kcd, BMFace *f, ListBase *kfe } } - /* remove dangling edges, not essential - but nice for users */ + /* Remove dangling edges, not essential - but nice for users. */ for (i = 0; i < edge_array_len_orig; i++) { if (kfe_array[i]) { if (BM_edge_is_wire(kfe_array[i]->e)) { @@ -1342,10 +2271,11 @@ static int sort_verts_by_dist_cb(void *co_p, const void *cur_a_p, const void *cu return 0; } -/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges */ -static void knife_make_cuts(KnifeTool_OpData *kcd) +/* Use the network of KnifeEdges and KnifeVerts accumulated to make real BMVerts and BMEdedges. */ +static void knife_make_cuts(KnifeTool_OpData *kcd, Object *ob) { - BMesh *bm = kcd->em->bm; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; KnifeEdge *kfe; KnifeVert *kfv; BMFace *f; @@ -1361,11 +2291,14 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) BLI_smallhash_init(fhash); BLI_smallhash_init(ehash); - /* put list of cutting edges for a face into fhash, keyed by face */ + /* Put list of cutting edges for a face into fhash, keyed by face. */ BLI_mempool_iternew(kcd->kedges, &iter); for (kfe = BLI_mempool_iterstep(&iter); kfe; kfe = BLI_mempool_iterstep(&iter)) { + if (kfe->is_invalid || kfe->v1->ob != ob) { + continue; + } - /* select edges that lie directly on the cut */ + /* Select edges that lie directly on the cut. */ if (kcd->select_result) { if (kfe->e && kfe->is_cut) { BM_edge_select_set(bm, kfe->e, true); @@ -1384,11 +2317,11 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) knife_append_list(kcd, list, kfe); } - /* put list of splitting vertices for an edge into ehash, keyed by edge */ + /* Put list of splitting vertices for an edge into ehash, keyed by edge. */ BLI_mempool_iternew(kcd->kverts, &iter); for (kfv = BLI_mempool_iterstep(&iter); kfv; kfv = BLI_mempool_iterstep(&iter)) { - if (kfv->v) { - continue; /* already have a BMVert */ + if (kfv->v || kfv->is_invalid || kfv->ob != ob) { + continue; /* Already have a BMVert. */ } for (ref = kfv->edges.first; ref; ref = ref->next) { kfe = ref->ref; @@ -1401,14 +2334,14 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) list = knife_empty_list(kcd); BLI_smallhash_insert(ehash, (uintptr_t)e, list); } - /* there can be more than one kfe in kfv's list with same e */ + /* There can be more than one kfe in kfv's list with same e. */ if (!find_ref(list, kfv)) { knife_append_list(kcd, list, kfv); } } } - /* split bmesh edges where needed */ + /* Split bmesh edges where needed. */ for (list = BLI_smallhash_iternew(ehash, &hiter, (uintptr_t *)&e); list; list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&e)) { BLI_listbase_sort_r(list, sort_verts_by_dist_cb, e->v1->co); @@ -1421,13 +2354,13 @@ static void knife_make_cuts(KnifeTool_OpData *kcd) } if (kcd->only_select) { - EDBM_flag_disable_all(kcd->em, BM_ELEM_SELECT); + EDBM_flag_disable_all(em, BM_ELEM_SELECT); } - /* do cuts for each face */ + /* Do cuts for each face. */ for (list = BLI_smallhash_iternew(fhash, &hiter, (uintptr_t *)&f); list; list = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) { - knife_make_face_cuts(kcd, f, list); + knife_make_face_cuts(kcd, bm, f, list); } BLI_smallhash_release(fhash); @@ -1448,6 +2381,21 @@ static void knife_add_cut(KnifeTool_OpData *kcd) GHashIterator giter; ListBase *list; + /* Allocate new undo frame on stack, unless cut is being dragged. */ + if (!kcd->is_drag_undo) { + kcd->undo = BLI_stack_push_r(kcd->undostack); + kcd->undo->pos = kcd->prev; + kcd->undo->cuts = 0; + kcd->undo->splits = 0; + kcd->undo->mdata = kcd->mdata; + kcd->is_drag_undo = true; + } + + /* Save values for angle drawing calculations. */ + copy_v3_v3(kcd->mdata.cage, kcd->prev.cage); + copy_v2_v2(kcd->mdata.mval, kcd->prev.mval); + kcd->mdata.is_stored = true; + prepare_linehits_for_cut(kcd); if (kcd->totlinehit == 0) { if (kcd->is_drag_hold == false) { @@ -1456,7 +2404,12 @@ static void knife_add_cut(KnifeTool_OpData *kcd) return; } - /* make facehits: map face -> list of linehits touching it */ + /* Consider most recent linehit in angle drawing calculations. */ + if (kcd->totlinehit >= 2) { + copy_v3_v3(kcd->mdata.cage, kcd->linehits[kcd->totlinehit - 2].cagehit); + } + + /* Make facehits: map face -> list of linehits touching it. */ facehits = BLI_ghash_ptr_new("knife facehits"); for (i = 0; i < kcd->totlinehit; i++) { KnifeLineHit *lh = &kcd->linehits[i]; @@ -1485,11 +2438,11 @@ static void knife_add_cut(KnifeTool_OpData *kcd) knife_cut_face(kcd, f, list); } - /* set up for next cut */ + /* Set up for next cut. */ kcd->prev = kcd->curr; if (kcd->prev.bmface) { - /* was "in face" but now we have a KnifeVert it is snapped to */ + /* Was "in face" but now we have a KnifeVert it is snapped to. */ KnifeLineHit *lh = &kcd->linehits[kcd->totlinehit - 1]; kcd->prev.vert = lh->v; kcd->prev.bmface = NULL; @@ -1529,7 +2482,7 @@ static void knife_finish_cut(KnifeTool_OpData *kcd) * to hash lookup routine; will reverse this in the get routine. * Doing this lazily rather than all at once for all faces. */ -static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index) +static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMEditMesh *em, BMFace *f, int index) { int i; @@ -1537,10 +2490,10 @@ static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index) return; } - BLI_assert(index >= 0 && index < kcd->em->tottri); - BLI_assert(kcd->em->looptris[index][0]->f == f); + BLI_assert(index >= 0 && index < em->tottri); + BLI_assert(em->looptris[index][0]->f == f); for (i = index - 1; i >= 0; i--) { - if (kcd->em->looptris[i][0]->f != f) { + if (em->looptris[i][0]->f != f) { i++; break; } @@ -1552,7 +2505,8 @@ static void set_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f, int index) BLI_ghash_insert(kcd->facetrimap, f, POINTER_FROM_INT(i + 1)); } -/* This should only be called for faces that have had a lowest face tri set by previous function */ +/* This should only be called for faces that have had a lowest face tri set by previous function. + */ static int get_lowest_face_tri(KnifeTool_OpData *kcd, BMFace *f) { int ans; @@ -1574,11 +2528,15 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, const float s[2], const float v1[3], const float v2[3], + Object *ob, + uint base_index, BMFace *f, const float face_tol_sq, float hit_co[3], float hit_cageco[3]) { + BMEditMesh *em = BKE_editmesh_from_object(ob); + int tottri, tri_i; float raydir[3]; float tri_norm[3], tri_plane[4]; @@ -1592,37 +2550,45 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, sub_v3_v3v3(raydir, v2, v1); normalize_v3(raydir); tri_i = get_lowest_face_tri(kcd, f); - tottri = kcd->em->tottri; + tottri = em->tottri; BLI_assert(tri_i >= 0 && tri_i < tottri); for (; tri_i < tottri; tri_i++) { - const float *lv1, *lv2, *lv3; + float lv[3][3]; float ray_tri_uv[2]; - tri = kcd->em->looptris[tri_i]; + tri = em->looptris[tri_i]; if (tri[0]->f != f) { break; } - lv1 = kcd->cagecos[BM_elem_index_get(tri[0]->v)]; - lv2 = kcd->cagecos[BM_elem_index_get(tri[1]->v)]; - lv3 = kcd->cagecos[BM_elem_index_get(tri[2]->v)]; - /* using epsilon test in case ray is directly through an internal + copy_v3_v3(lv[0], kcd->cagecos[base_index][BM_elem_index_get(tri[0]->v)]); + copy_v3_v3(lv[1], kcd->cagecos[base_index][BM_elem_index_get(tri[1]->v)]); + copy_v3_v3(lv[2], kcd->cagecos[base_index][BM_elem_index_get(tri[2]->v)]); + mul_m4_v3(ob->obmat, lv[0]); + mul_m4_v3(ob->obmat, lv[1]); + mul_m4_v3(ob->obmat, lv[2]); + + /* Using epsilon test in case ray is directly through an internal * tessellation edge and might not hit either tessellation tri with * an exact test; - * we will exclude hits near real edges by a later test */ - if (isect_ray_tri_epsilon_v3(v1, raydir, lv1, lv2, lv3, &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { - /* check if line coplanar with tri */ - normal_tri_v3(tri_norm, lv1, lv2, lv3); - plane_from_point_normal_v3(tri_plane, lv1, tri_norm); + * We will exclude hits near real edges by a later test. */ + if (isect_ray_tri_epsilon_v3( + v1, raydir, lv[0], lv[1], lv[2], &lambda, ray_tri_uv, KNIFE_FLT_EPS)) { + /* Check if line coplanar with tri. */ + normal_tri_v3(tri_norm, lv[0], lv[1], lv[2]); + plane_from_point_normal_v3(tri_plane, lv[0], tri_norm); if ((dist_squared_to_plane_v3(v1, tri_plane) < KNIFE_FLT_EPS) && (dist_squared_to_plane_v3(v2, tri_plane) < KNIFE_FLT_EPS)) { return false; } - interp_v3_v3v3v3_uv(hit_cageco, lv1, lv2, lv3, ray_tri_uv); - /* Now check that far enough away from verts and edges */ - list = knife_get_face_kedges(kcd, f); + interp_v3_v3v3v3_uv(hit_cageco, lv[0], lv[1], lv[2], ray_tri_uv); + /* Now check that far enough away from verts and edges. */ + list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { kfe = ref->ref; + if (kfe->is_invalid) { + continue; + } knife_project_v2(kcd, kfe->v1->cageco, se1); knife_project_v2(kcd, kfe->v2->cageco, se2); d = dist_squared_to_line_segment_v2(s, se1, se2); @@ -1642,19 +2608,24 @@ static bool knife_ray_intersect_face(KnifeTool_OpData *kcd, */ static void calc_ortho_extent(KnifeTool_OpData *kcd) { + Object *ob; + BMEditMesh *em; BMIter iter; BMVert *v; - BMesh *bm = kcd->em->bm; float min[3], max[3]; - INIT_MINMAX(min, max); - if (kcd->cagecos) { - minmax_v3v3_v3_array(min, max, kcd->cagecos, bm->totvert); - } - else { - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - minmax_v3v3_v3(min, max, v->co); + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + + if (kcd->cagecos[b]) { + minmax_v3v3_v3_array(min, max, kcd->cagecos[b], em->bm->totvert); + } + else { + BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { + minmax_v3v3_v3(min, max, v->co); + } } } @@ -1732,28 +2703,26 @@ static bool point_is_visible(KnifeTool_OpData *kcd, { BMFace *f_hit; - /* If box clipping on, make sure p is not clipped */ + /* If box clipping on, make sure p is not clipped. */ if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) && - ED_view3d_clipping_test(kcd->vc.rv3d, p, true)) { + ED_view3d_clipping_test(kcd->vc.rv3d, p, false)) { return false; } - /* If not cutting through, make sure no face is in front of p */ + /* If not cutting through, make sure no face is in front of p. */ if (!kcd->cut_through) { float dist; float view[3], p_ofs[3]; - /* TODO: I think there's a simpler way to get the required raycast ray */ + /* TODO: I think there's a simpler way to get the required raycast ray. */ ED_view3d_unproject_v3(kcd->vc.region, s[0], s[1], 0.0f, view); - mul_m4_v3(kcd->ob_imat, view); - - /* make p_ofs a little towards view, so ray doesn't hit p's face. */ + /* Make p_ofs a little towards view, so ray doesn't hit p's face. */ sub_v3_v3(view, p); dist = normalize_v3(view); copy_v3_v3(p_ofs, p); - /* avoid projecting behind the viewpoint */ + /* Avoid projecting behind the viewpoint. */ if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) { dist = kcd->vc.v3d->clip_end * 2.0f; } @@ -1774,20 +2743,21 @@ static bool point_is_visible(KnifeTool_OpData *kcd, } } - /* see if there's a face hit between p1 and the view */ + /* See if there's a face hit between p1 and the view. */ if (ele_test) { - f_hit = BKE_bmbvh_ray_cast_filter(kcd->bmbvh, - p_ofs, - view, - KNIFE_FLT_EPS, - &dist, - NULL, - NULL, - bm_ray_cast_cb_elem_not_in_face_check, - ele_test); + f_hit = knife_bvh_raycast_filter(kcd, + p_ofs, + view, + KNIFE_FLT_EPS, + &dist, + NULL, + NULL, + NULL, + bm_ray_cast_cb_elem_not_in_face_check, + ele_test); } else { - f_hit = BKE_bmbvh_ray_cast(kcd->bmbvh, p_ofs, view, KNIFE_FLT_EPS, &dist, NULL, NULL); + f_hit = knife_bvh_raycast(kcd, p_ofs, view, KNIFE_FLT_EPS, &dist, NULL, NULL, NULL); } if (f_hit) { @@ -1799,7 +2769,7 @@ static bool point_is_visible(KnifeTool_OpData *kcd, } /* Clip the line (v1, v2) to planes perpendicular to it and distances d from - * the closest point on the line to the origin */ + * the closest point on the line to the origin. */ static void clip_to_ortho_planes(float v1[3], float v2[3], const float center[3], const float d) { float closest[3], dir[3]; @@ -1821,12 +2791,12 @@ static void set_linehit_depth(KnifeTool_OpData *kcd, KnifeLineHit *lh) lh->m = dot_m4_v3_row_z(kcd->vc.rv3d->persmatob, lh->cagehit); } -/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line */ +/* Finds visible (or all, if cutting through) edges that intersects the current screen drag line. + */ static void knife_find_line_hits(KnifeTool_OpData *kcd) { - SmallHash faces, kfes, kfvs; + SmallHash faces, kfes, kfvs, fobs; float v1[3], v2[3], v3[3], v4[3], s1[2], s2[2]; - BVHTree *tree; int *results, *result; BMLoop **ls; BMFace *f; @@ -1858,7 +2828,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) copy_v3_v3(v1, kcd->prev.cage); copy_v3_v3(v2, kcd->curr.cage); - /* project screen line's 3d coordinates back into 2d */ + /* Project screen line's 3d coordinates back into 2d. */ knife_project_v2(kcd, v1, s1); knife_project_v2(kcd, v2, s2); @@ -1873,22 +2843,17 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* unproject screen line */ + /* Unproject screen line. */ ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s1, v1, v3, true); ED_view3d_win_to_segment_clipped(kcd->vc.depsgraph, kcd->region, kcd->vc.v3d, s2, v2, v4, true); - mul_m4_v3(kcd->ob_imat, v1); - mul_m4_v3(kcd->ob_imat, v2); - mul_m4_v3(kcd->ob_imat, v3); - mul_m4_v3(kcd->ob_imat, v4); - /* Numeric error, 'v1' -> 'v2', 'v2' -> 'v4' * can end up being ~2000 units apart with an orthogonal perspective. * * (from ED_view3d_win_to_segment_clipped() above) - * this gives precision error; rather than solving properly + * This gives precision error; rather than solving properly * (which may involve using doubles everywhere!), - * limit the distance between these points */ + * limit the distance between these points. */ if (kcd->is_ortho && (kcd->vc.rv3d->persp != RV3D_CAMOB)) { if (kcd->ortho_extent == 0.0f) { calc_ortho_extent(kcd); @@ -1910,8 +2875,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) * intersect the cut plane with rays v1-v3 and v2-v4. * This de-duplicates the candidates before doing more expensive intersection tests. */ - tree = BKE_bmbvh_tree_get(kcd->bmbvh); - results = BLI_bvhtree_intersect_plane(tree, plane, &tot); + results = BLI_bvhtree_intersect_plane(kcd->bvh.tree, plane, &tot); if (!results) { return; } @@ -1919,26 +2883,44 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) BLI_smallhash_init(&faces); BLI_smallhash_init(&kfes); BLI_smallhash_init(&kfvs); + BLI_smallhash_init(&fobs); + + Object *ob; + BMEditMesh *em; + uint b = 0; for (i = 0, result = results; i < tot; i++, result++) { - ls = (BMLoop **)kcd->em->looptris[*result]; - f = ls[0]->f; - set_lowest_face_tri(kcd, f, *result); + for (b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + if (*result >= 0 && *result < em->tottri) { + ls = (BMLoop **)em->looptris[*result]; + break; + } + *result -= em->tottri; + } - /* occlude but never cut unselected faces (when only_select is used) */ + f = ls[0]->f; + set_lowest_face_tri(kcd, em, f, *result); + + /* Occlude but never cut unselected faces (when only_select is used). */ if (kcd->only_select && !BM_elem_flag_test(f, BM_ELEM_SELECT)) { continue; } - /* for faces, store index of lowest hit looptri in hash */ + /* For faces, store index of lowest hit looptri in hash. */ if (BLI_smallhash_haskey(&faces, (uintptr_t)f)) { continue; } - /* don't care what the value is except that it is non-NULL, for iterator */ + /* Don't care what the value is except that it is non-NULL, for iterator. */ BLI_smallhash_insert(&faces, (uintptr_t)f, f); + BLI_smallhash_insert(&fobs, (uintptr_t)f, (void *)(uintptr_t)b); - list = knife_get_face_kedges(kcd, f); + list = knife_get_face_kedges(kcd, ob, b, f); for (ref = list->first; ref; ref = ref->next) { kfe = ref->ref; + if (kfe->is_invalid) { + continue; + } if (BLI_smallhash_haskey(&kfes, (uintptr_t)kfe)) { continue; } @@ -1950,7 +2932,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* Now go through the candidates and find intersections */ + /* Now go through the candidates and find intersections. */ /* These tolerances, in screen space, are for intermediate hits, * as ends are already snapped to screen. */ @@ -1971,9 +2953,9 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) line_tol_sq = line_tol * line_tol; face_tol_sq = face_tol * face_tol; - /* Assume these tolerances swamp floating point rounding errors in calculations below */ + /* Assume these tolerances swamp floating point rounding errors in calculations below. */ - /* first look for vertex hits */ + /* First look for vertex hits. */ for (val_p = BLI_smallhash_iternew_p(&kfvs, &hiter, (uintptr_t *)&v); val_p; val_p = BLI_smallhash_iternext_p(&hiter, (uintptr_t *)&v)) { KnifeEdge *kfe_hit = NULL; @@ -2001,12 +2983,14 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) hit.v = v; /* If this isn't from an existing BMVert, it may have been added to a BMEdge originally. - * knowing if the hit comes from an edge is important for edge-in-face checks later on - * see: #knife_add_single_cut -> #knife_verts_edge_in_face, T42611 */ + * Knowing if the hit comes from an edge is important for edge-in-face checks later on. + * See: #knife_add_single_cut -> #knife_verts_edge_in_face, T42611. */ if (kfe_hit) { hit.kfe = kfe_hit; } + hit.ob = v->ob; + hit.base_index = v->base_index; copy_v3_v3(hit.hit, v->co); copy_v3_v3(hit.cagehit, v->cageco); copy_v2_v2(hit.schit, s); @@ -2021,16 +3005,10 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* now edge hits; don't add if a vertex at end of edge should have hit */ + /* Now edge hits; don't add if a vertex at end of edge should have hit. */ for (val = BLI_smallhash_iternew(&kfes, &hiter, (uintptr_t *)&kfe); val; val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&kfe)) { - /* If we intersect any of the vertices, don't attempt to intersect the edge. */ - if (BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v1) || - BLI_smallhash_lookup(&kfvs, (intptr_t)kfe->v2)) { - continue; - } - knife_project_v2(kcd, kfe->v1->cageco, se1); knife_project_v2(kcd, kfe->v2->cageco, se2); int isect_kind = 1; @@ -2045,7 +3023,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) else { isect_kind = isect_seg_seg_v2_point_ex(s1, s2, se1, se2, 0.0f, sint); if (isect_kind == -1) { - /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2 */ + /* isect_seg_seg_v2_point doesn't do tolerance test around ends of s1-s2. */ closest_to_line_segment_v2(sint, s1, se1, se2); if (len_squared_v2v2(sint, s1) <= line_tol_sq) { isect_kind = 1; @@ -2066,14 +3044,14 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) lambda = d1 / d2; /* Can't just interpolate between ends of kfe because * that doesn't work with perspective transformation. - * Need to find 3d intersection of ray through sint */ + * Need to find 3d intersection of ray through sint. */ knife_input_ray_segment(kcd, sint, 1.0f, r1, r2); isect_kind = isect_line_line_v3( kfe->v1->cageco, kfe->v2->cageco, r1, r2, p_cage, p_cage_tmp); if (isect_kind >= 1 && point_is_visible(kcd, p_cage, sint, bm_elem_from_knife_edge(kfe))) { memset(&hit, 0, sizeof(hit)); if (kcd->snap_midpoints) { - /* choose intermediate point snap too */ + /* Choose intermediate point snap too. */ mid_v3_v3v3(p_cage, kfe->v1->cageco, kfe->v2->cageco); mid_v2_v2v2(sint, se1, se2); lambda = 0.5f; @@ -2081,6 +3059,8 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) hit.kfe = kfe; transform_point_by_seg_v3( hit.hit, p_cage, kfe->v1->co, kfe->v2->co, kfe->v1->cageco, kfe->v2->cageco); + hit.ob = kfe->v1->ob; + hit.base_index = kfe->v1->base_index; copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, sint); hit.perc = lambda; @@ -2091,7 +3071,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - /* now face hits; don't add if a vertex or edge in face should have hit */ + /* Now face hits; don't add if a vertex or edge in face should have hit. */ const bool use_hit_prev = (kcd->prev.vert == NULL) && (kcd->prev.edge == NULL); const bool use_hit_curr = (kcd->curr.vert == NULL) && (kcd->curr.edge == NULL) && !kcd->is_drag_hold; @@ -2100,10 +3080,16 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) val = BLI_smallhash_iternext(&hiter, (uintptr_t *)&f)) { float p[3], p_cage[3]; - if (use_hit_prev && knife_ray_intersect_face(kcd, s1, v1, v3, f, face_tol_sq, p, p_cage)) { + uint base_index = (uint)(uintptr_t)BLI_smallhash_lookup(&fobs, (uintptr_t)f); + ob = kcd->objects[base_index]; + + if (use_hit_prev && + knife_ray_intersect_face(kcd, s1, v1, v3, ob, base_index, f, face_tol_sq, p, p_cage)) { if (point_is_visible(kcd, p_cage, s1, (BMElem *)f)) { memset(&hit, 0, sizeof(hit)); hit.f = f; + hit.ob = ob; + hit.base_index = base_index; copy_v3_v3(hit.hit, p); copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s1); @@ -2112,10 +3098,13 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) } } - if (use_hit_curr && knife_ray_intersect_face(kcd, s2, v2, v4, f, face_tol_sq, p, p_cage)) { + if (use_hit_curr && + knife_ray_intersect_face(kcd, s2, v2, v4, ob, base_index, f, face_tol_sq, p, p_cage)) { if (point_is_visible(kcd, p_cage, s2, (BMElem *)f)) { memset(&hit, 0, sizeof(hit)); hit.f = f; + hit.ob = ob; + hit.base_index = base_index; copy_v3_v3(hit.hit, p); copy_v3_v3(hit.cagehit, p_cage); copy_v2_v2(hit.schit, s2); @@ -2129,7 +3118,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) kcd->linehits = linehits; kcd->totlinehit = BLI_array_len(linehits); - /* find position along screen line, used for sorting */ + /* Find position along screen line, used for sorting. */ for (i = 0; i < kcd->totlinehit; i++) { KnifeLineHit *lh = kcd->linehits + i; @@ -2139,6 +3128,7 @@ static void knife_find_line_hits(KnifeTool_OpData *kcd) BLI_smallhash_release(&faces); BLI_smallhash_release(&kfes); BLI_smallhash_release(&kfvs); + BLI_smallhash_release(&fobs); MEM_freeN(results); } @@ -2165,9 +3155,11 @@ static void knife_pos_data_clear(KnifePosData *kpd) * \{ */ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, - float co[3], - float cageco[3], - bool *is_space) + Object **r_ob, + uint *r_base_index, + bool *is_space, + float r_co[3], + float r_cageco[3]) { BMFace *f; float dist = KMAXDIST; @@ -2175,12 +3167,12 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, float origin_ofs[3]; float ray[3], ray_normal[3]; - /* unproject to find view ray */ + /* Unproject to find view ray. */ knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); sub_v3_v3v3(ray, origin_ofs, origin); normalize_v3_v3(ray_normal, ray); - f = BKE_bmbvh_ray_cast(kcd->bmbvh, origin, ray_normal, 0.0f, NULL, co, cageco); + f = knife_bvh_raycast(kcd, origin, ray_normal, 0.0f, NULL, r_co, r_cageco, r_base_index); if (f && kcd->only_select && BM_elem_flag_test(f, BM_ELEM_SELECT) == 0) { f = NULL; @@ -2190,7 +3182,10 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, *is_space = !f; } - if (!f) { + if (f) { + *r_ob = kcd->objects[*r_base_index]; + } + else { if (kcd->is_interactive) { /* Try to use back-buffer selection method if ray casting failed. * @@ -2202,12 +3197,12 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, f = EDBM_face_find_nearest(&vc, &dist); - /* cheat for now; just put in the origin instead + /* Cheat for now; just put in the origin instead * of a true coordinate on the face. * This just puts a point 1.0f in front of the view. */ - add_v3_v3v3(co, origin, ray); + add_v3_v3v3(r_co, origin, ray); /* Use this value for the cage location too as it's used to find near edges/vertices. */ - copy_v3_v3(cageco, co); + copy_v3_v3(r_cageco, r_co); } } @@ -2222,6 +3217,8 @@ static BMFace *knife_find_closest_face(KnifeTool_OpData *kcd, */ static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, const float radius, + Object *ob, + uint base_index, BMFace *f, const float cageco[3]) { @@ -2234,21 +3231,29 @@ static int knife_sample_screen_density_from_closest_face(KnifeTool_OpData *kcd, knife_project_v2(kcd, cageco, sco); - list = knife_get_face_kedges(kcd, f); + list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { KnifeEdge *kfe = ref->ref; int i; + if (kfe->is_invalid) { + continue; + } + for (i = 0; i < 2; i++) { KnifeVert *kfv = i ? kfe->v2 : kfe->v1; float kfv_sco[2]; + if (kfv->is_invalid) { + continue; + } + knife_project_v2(kcd, kfv->cageco, kfv_sco); dis_sq = len_squared_v2v2(kfv_sco, sco); if (dis_sq < radius_sq) { if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { - if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true) == 0) { + if (ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false) == 0) { c++; } } @@ -2275,27 +3280,27 @@ static float knife_snap_size(KnifeTool_OpData *kcd, float maxsize) if (!kcd->curr.is_space) { density = (float)knife_sample_screen_density_from_closest_face( - kcd, maxsize * 2.0f, kcd->curr.bmface, kcd->curr.cage); + kcd, maxsize * 2.0f, kcd->curr.ob, kcd->curr.base_index, kcd->curr.bmface, kcd->curr.cage); } return density ? min_ff(maxsize / ((float)density * 0.5f), maxsize) : maxsize; } -/* Snap to edge in a specified angle. +/* Snap to edge when in a constrained mode. * Returns 'lambda' calculated (in screen-space). */ -static bool knife_snap_edge_in_angle(KnifeTool_OpData *kcd, - const float sco[3], - const float kfv1_sco[2], - const float kfv2_sco[2], - float *r_dist_sq, - float *r_lambda) +static bool knife_snap_edge_constrained(KnifeTool_OpData *kcd, + const float sco[3], + const float kfv1_sco[2], + const float kfv2_sco[2], + float *r_dist_sq, + float *r_lambda) { - /* if snapping, check we're in bounds */ + /* If snapping, check we're in bounds. */ float sco_snap[2]; isect_line_line_v2_point(kfv1_sco, kfv2_sco, kcd->prev.mval, kcd->curr.mval, sco_snap); float lambda = line_point_factor_v2(sco_snap, kfv1_sco, kfv2_sco); - /* be strict about angle-snapping within edge */ + /* Be strict when constrained within edge. */ if ((lambda < 0.0f - KNIFE_FLT_EPSBIG) || (lambda > 1.0f + KNIFE_FLT_EPSBIG)) { return false; } @@ -2309,7 +3314,7 @@ static bool knife_snap_edge_in_angle(KnifeTool_OpData *kcd, return false; } -/* use when lambda is in screen-space */ +/* Use when lambda is in screen-space. */ static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd, float r_co[3], const float v1[3], @@ -2320,23 +3325,21 @@ static void knife_interp_v3_v3v3(const KnifeTool_OpData *kcd, interp_v3_v3v3(r_co, v1, v2, lambda_ss); } else { - /* transform into screen-space, interp, then transform back */ + /* Transform into screen-space, interp, then transform back. */ float v1_ss[3], v2_ss[3]; - mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->projmat, v1); - mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->projmat, v2); + mul_v3_project_m4_v3(v1_ss, (float(*)[4])kcd->vc.rv3d->persmat, v1); + mul_v3_project_m4_v3(v2_ss, (float(*)[4])kcd->vc.rv3d->persmat, v2); interp_v3_v3v3(r_co, v1_ss, v2_ss, lambda_ss); - mul_project_m4_v3((float(*)[4])kcd->projmat_inv, r_co); + mul_project_m4_v3((float(*)[4])kcd->vc.rv3d->persinv, r_co); } } -/* p is closest point on edge to the mouse cursor */ -static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, - BMFace *f, - float p[3], - float cagep[3]) +/* p is closest point on edge to the mouse cursor. */ +static KnifeEdge *knife_find_closest_edge_of_face( + KnifeTool_OpData *kcd, Object *ob, uint base_index, BMFace *f, float p[3], float cagep[3]) { float sco[2]; float maxdist; @@ -2361,21 +3364,27 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, knife_project_v2(kcd, cagep, sco); - /* look through all edges associated with this face */ - list = knife_get_face_kedges(kcd, f); + /* Look through all edges associated with this face. */ + list = knife_get_face_kedges(kcd, ob, base_index, f); for (ref = list->first; ref; ref = ref->next) { KnifeEdge *kfe = ref->ref; float kfv1_sco[2], kfv2_sco[2], test_cagep[3]; float lambda; - /* project edge vertices into screen space */ + if (kfe->is_invalid) { + continue; + } + + /* Project edge vertices into screen space. */ knife_project_v2(kcd, kfe->v1->cageco, kfv1_sco); knife_project_v2(kcd, kfe->v2->cageco, kfv2_sco); - /* check if we're close enough and calculate 'lambda' */ - if (kcd->is_angle_snapping) { + /* Check if we're close enough and calculate 'lambda'. */ + /* In constrained mode calculate lambda differently, unless constrained along kcd->prev.edge */ + if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kfe != kcd->prev.edge) && + (kcd->mode == MODE_DRAGGING)) { dis_sq = curdis_sq; - if (!knife_snap_edge_in_angle(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) { + if (!knife_snap_edge_constrained(kcd, sco, kfv1_sco, kfv2_sco, &dis_sq, &lambda)) { continue; } } @@ -2389,12 +3398,12 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, } } - /* now we have 'lambda' calculated (in screen-space) */ + /* Now we have 'lambda' calculated (in screen-space). */ knife_interp_v3_v3v3(kcd, test_cagep, kfe->v1->cageco, kfe->v2->cageco, lambda); if (RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d)) { - /* check we're in the view */ - if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, true)) { + /* Check we're in the view */ + if (ED_view3d_clipping_test(kcd->vc.rv3d, test_cagep, false)) { continue; } } @@ -2417,16 +3426,18 @@ static KnifeEdge *knife_find_closest_edge_of_face(KnifeTool_OpData *kcd, interp_v3_v3v3(p, cure->v1->co, cure->v2->co, lambda); } - /* update mouse coordinates to the snapped-to edge's screen coordinates - * this is important for angle snap, which uses the previous mouse position */ + /* Update mouse coordinates to the snapped-to edge's screen coordinates + * this is important for angle snap, which uses the previous mouse position. */ edgesnap = new_knife_vert(kcd, p, cagep); + edgesnap->ob = ob; + edgesnap->base_index = base_index; knife_project_v2(kcd, edgesnap->cageco, kcd->curr.mval); } return cure; } -/* find a vertex near the mouse cursor, if it exists */ +/* Find a vertex near the mouse cursor, if it exists. */ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, KnifeEdge *kfe, float p[3], @@ -2458,9 +3469,9 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, knife_project_v2(kcd, kfv->cageco, kfv_sco); - /* be strict about angle snapping, the vertex needs to be very close to the angle, - * or we ignore */ - if (kcd->is_angle_snapping) { + /* Be strict when in a constrained mode, the vertex needs to be very close to the cut line, + * or we ignore. */ + if ((kcd->is_angle_snapping || kcd->axis_constrained) && (kcd->mode == MODE_DRAGGING)) { if (dist_squared_to_line_segment_v2(kfv_sco, kcd->prev.mval, kcd->curr.mval) > KNIFE_FLT_EPSBIG) { continue; @@ -2470,7 +3481,7 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, dis_sq = len_squared_v2v2(kfv_sco, sco); if (dis_sq < curdis_sq && dis_sq < maxdist_sq) { if (!RV3D_CLIPPING_ENABLED(kcd->vc.v3d, kcd->vc.rv3d) || - !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, true)) { + !ED_view3d_clipping_test(kcd->vc.rv3d, kfv->cageco, false)) { curv = kfv; curdis_sq = dis_sq; copy_v2_v2(cur_kfv_sco, kfv_sco); @@ -2482,8 +3493,8 @@ static KnifeVert *knife_find_closest_vert_of_edge(KnifeTool_OpData *kcd, copy_v3_v3(p, curv->co); copy_v3_v3(cagep, curv->cageco); - /* update mouse coordinates to the snapped-to vertex's screen coordinates - * this is important for angle snap, which uses the previous mouse position */ + /* Update mouse coordinates to the snapped-to vertex's screen coordinates + * this is important for angle snap, which uses the previous mouse position. */ copy_v2_v2(kcd->curr.mval, cur_kfv_sco); } @@ -2510,12 +3521,21 @@ static float snap_v2_angle(float r[2], const float v[2], const float v_ref[2], f return angle + angle_delta; } -/* update both kcd->curr.mval and kcd->mval to snap to required angle */ -static bool knife_snap_angle(KnifeTool_OpData *kcd) +/* Update both kcd->curr.mval and kcd->mval to snap to required angle. */ +static bool knife_snap_angle_screen(KnifeTool_OpData *kcd) { - const float dvec_ref[2] = {0.0f, 1.0f}; + const float dvec_ref[2] = {1.0f, 0.0f}; float dvec[2], dvec_snap[2]; - float snap_step = DEG2RADF(45); + + float snap_step; + /* Currently user can input any float between 0 and 180. */ + if (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + kcd->angle_snapping_increment <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + snap_step = DEG2RADF(kcd->angle_snapping_increment); + } + else { + snap_step = DEG2RADF(KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT); + } sub_v2_v2v2(dvec, kcd->curr.mval, kcd->prev.mval); if (is_zero_v2(dvec)) { @@ -2531,6 +3551,273 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd) return true; } +/** + * Snaps a 3d vector to an angle, relative to \a v_ref, along the plane with normal \a plane_no. + */ +static float snap_v3_angle_plane( + float r[3], const float v[3], const float v_ref[3], const float plane_no[3], float snap_step) +{ + /* Calculate angle between current cut vector and reference vector. */ + float angle, angle_delta; + angle = angle_signed_on_axis_v3v3_v3(v, v_ref, plane_no); + /* Use this to calculate the angle to rotate by based on snap_step. */ + angle_delta = (roundf(angle / snap_step) * snap_step) - angle; + + /* Snap to angle. */ + rotate_v3_v3v3fl(r, v, plane_no, angle_delta); + return angle + angle_delta; +} + +/* Snap to required angle along the plane of the face nearest to kcd->prev. */ +static bool knife_snap_angle_relative(KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeEdge *kfe; + KnifeVert *kfv; + BMFace *f; + float refv[3]; + + /* Ray for kcd->curr. */ + float curr_origin[3]; + float curr_origin_ofs[3]; + float curr_ray[3], curr_ray_normal[3]; + float curr_co[3], curr_cage[3]; /* Unused. */ + + float plane[4]; + float ray_hit[3]; + float lambda; + + knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, curr_origin, curr_origin_ofs); + sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin); + normalize_v3_v3(curr_ray_normal, curr_ray); + + BMFace *fcurr = knife_bvh_raycast( + kcd, curr_origin, curr_ray_normal, 0.0f, NULL, curr_co, curr_cage, NULL); + + if (!fcurr) { + return false; + } + + /* Calculate a reference vector using previous cut segment, edge or vertex. + * If none exists then exit. */ + if (kcd->prev.vert) { + int count = 0; + for (ref = kcd->prev.vert->edges.first; ref; ref = ref->next) { + kfe = ((KnifeEdge *)(ref->ref)); + if (kfe->is_invalid) { + continue; + } + if (kfe->e) { + if (!BM_edge_in_face(kfe->e, fcurr)) { + continue; + } + } + if (count == kcd->snap_edge) { + kfv = compare_v3v3(kfe->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) ? kfe->v2 : kfe->v1; + sub_v3_v3v3(refv, kfv->cageco, kcd->prev.cage); + kcd->snap_ref_edge = kfe; + break; + } + count++; + } + } + else if (kcd->prev.edge) { + kfv = compare_v3v3(kcd->prev.edge->v1->cageco, kcd->prev.cage, KNIFE_FLT_EPSBIG) ? + kcd->prev.edge->v2 : + kcd->prev.edge->v1; + sub_v3_v3v3(refv, kfv->cageco, kcd->prev.cage); + kcd->snap_ref_edge = kcd->prev.edge; + } + else { + return false; + } + + /* Choose best face for plane. */ + BMFace *fprev = NULL; + if (kcd->prev.vert && kcd->prev.vert->v) { + for (ref = kcd->prev.vert->faces.first; ref; ref = ref->next) { + f = ((BMFace *)(ref->ref)); + if (f == fcurr) { + fprev = f; + } + } + } + else if (kcd->prev.edge) { + for (ref = kcd->prev.edge->faces.first; ref; ref = ref->next) { + f = ((BMFace *)(ref->ref)); + if (f == fcurr) { + fprev = f; + } + } + } + else { + /* Cut segment was started in a face. */ + float prev_origin[3]; + float prev_origin_ofs[3]; + float prev_ray[3], prev_ray_normal[3]; + float prev_co[3], prev_cage[3]; /* Unused. */ + + knife_input_ray_segment(kcd, kcd->prev.mval, 1.0f, prev_origin, prev_origin_ofs); + + sub_v3_v3v3(prev_ray, prev_origin_ofs, prev_origin); + normalize_v3_v3(prev_ray_normal, prev_ray); + + /* kcd->prev.face is usually not set. */ + fprev = knife_bvh_raycast( + kcd, prev_origin, prev_ray_normal, 0.0f, NULL, prev_co, prev_cage, NULL); + } + + if (!fprev || fprev != fcurr) { + return false; + } + + /* Re-calculate current ray in object space. */ + knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, curr_origin, curr_origin_ofs); + sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin); + normalize_v3_v3(curr_ray_normal, curr_ray); + + plane_from_point_normal_v3(plane, kcd->prev.cage, fprev->no); + + if (isect_ray_plane_v3(curr_origin, curr_ray_normal, plane, &lambda, false)) { + madd_v3_v3v3fl(ray_hit, curr_origin, curr_ray_normal, lambda); + + /* Calculate snap step. */ + float snap_step; + if (kcd->angle_snapping_increment > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + kcd->angle_snapping_increment <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + snap_step = DEG2RADF(kcd->angle_snapping_increment); + } + else { + snap_step = DEG2RADF(KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT); + } + + float v1[3]; + float v2[3]; + float rotated_vec[3]; + /* Maybe check for vectors being zero here? */ + sub_v3_v3v3(v1, ray_hit, kcd->prev.cage); + copy_v3_v3(v2, refv); + kcd->angle = snap_v3_angle_plane(rotated_vec, v1, v2, fprev->no, snap_step); + add_v3_v3(rotated_vec, kcd->prev.cage); + + knife_project_v2(kcd, rotated_vec, kcd->curr.mval); + copy_v2_v2(kcd->mval, kcd->curr.mval); + return true; + } + return false; +} + +static int knife_calculate_snap_ref_edges(KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeEdge *kfe; + + /* Ray for kcd->curr. */ + float curr_origin[3]; + float curr_origin_ofs[3]; + float curr_ray[3], curr_ray_normal[3]; + float curr_co[3], curr_cage[3]; /* Unused. */ + + knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, curr_origin, curr_origin_ofs); + sub_v3_v3v3(curr_ray, curr_origin_ofs, curr_origin); + normalize_v3_v3(curr_ray_normal, curr_ray); + + BMFace *fcurr = knife_bvh_raycast( + kcd, curr_origin, curr_ray_normal, 0.0f, NULL, curr_co, curr_cage, NULL); + + int count = 0; + + if (!fcurr) { + return count; + } + + if (kcd->prev.vert) { + for (ref = kcd->prev.vert->edges.first; ref; ref = ref->next) { + kfe = ((KnifeEdge *)(ref->ref)); + if (kfe->is_invalid) { + continue; + } + if (kfe->e) { + if (!BM_edge_in_face(kfe->e, fcurr)) { + continue; + } + } + count++; + } + } + else if (kcd->prev.edge) { + return 1; + } + return count; +} + +/* Reset the snapping angle num input. */ +static void knife_reset_snap_angle_input(KnifeTool_OpData *kcd) +{ + kcd->num.val[0] = 0; + while (kcd->num.str_cur > 0) { + kcd->num.str[kcd->num.str_cur - 1] = '\0'; + kcd->num.str_cur--; + } +} +/** + * Constrains the current cut to an axis. + * If scene orientation is set to anything other than global it takes priority. + * Otherwise kcd->constrain_axis_mode is used. + */ +static void knife_constrain_axis(bContext *C, KnifeTool_OpData *kcd) +{ + /* Obtain current mouse position in world space. */ + float curr_cage_adjusted[3]; + ED_view3d_win_to_3d( + kcd->vc.v3d, kcd->region, kcd->prev.cage, kcd->curr.mval, curr_cage_adjusted); + + /* Constrain axes. */ + Scene *scene = kcd->scene; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = (kcd->prev.ob) ? kcd->prev.ob : kcd->vc.obedit; + RegionView3D *rv3d = kcd->region->regiondata; + const short scene_orientation = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + /* Scene orientation takes priority. */ + const short orientation_type = scene_orientation ? scene_orientation : + kcd->constrain_axis_mode - 1; + const int pivot_point = scene->toolsettings->transform_pivot_point; + float mat[3][3]; + ED_transform_calc_orientation_from_type_ex( + scene, view_layer, kcd->vc.v3d, rv3d, obedit, obedit, orientation_type, pivot_point, mat); + + /* Apply orientation matrix (can be simplified?). */ + float co[3][3]; + copy_v3_v3(co[0], kcd->prev.cage); + copy_v3_v3(co[2], curr_cage_adjusted); + invert_m3(mat); + mul_m3_m3_pre(co, mat); + for (int i = 0; i <= 2; i++) { + if ((kcd->constrain_axis - 1) != i) { + /* kcd->curr_cage_adjusted[i] = prev_cage_adjusted[i]; */ + co[2][i] = co[0][i]; + } + } + invert_m3(mat); + mul_m3_m3_pre(co, mat); + copy_v3_v3(kcd->prev.cage, co[0]); + copy_v3_v3(curr_cage_adjusted, co[2]); + + /* Set mval to closest point on constrained line in screen space. */ + float curr_screenspace[2]; + float prev_screenspace[2]; + knife_project_v2(kcd, curr_cage_adjusted, curr_screenspace); + knife_project_v2(kcd, kcd->prev.cage, prev_screenspace); + float intersection[2]; + if (closest_to_line_v2(intersection, kcd->curr.mval, prev_screenspace, curr_screenspace)) { + copy_v2_v2(kcd->curr.mval, intersection); + } + else { + copy_v2_v2(kcd->curr.mval, curr_screenspace); + } + copy_v2_v2(kcd->mval, kcd->curr.mval); +} + /** * \return true when `kcd->curr.co` & `kcd->curr.cage` are set. * @@ -2539,7 +3826,7 @@ static bool knife_snap_angle(KnifeTool_OpData *kcd) * In this case the selection-buffer is used to select the face, * then the closest `vert` or `edge` is set, and those will enable `is_co_set`. */ -static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[2]) +static bool knife_snap_update_from_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) { knife_pos_data_clear(&kcd->curr); copy_v2_v2(kcd->curr.mval, mval); @@ -2547,20 +3834,37 @@ static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[ /* view matrix may have changed, reproject */ knife_project_v2(kcd, kcd->prev.cage, kcd->prev.mval); - if (kcd->angle_snapping && (kcd->mode == MODE_DRAGGING)) { - kcd->is_angle_snapping = knife_snap_angle(kcd); - } - else { - kcd->is_angle_snapping = false; + kcd->is_angle_snapping = false; + if (kcd->mode == MODE_DRAGGING) { + if (kcd->angle_snapping) { + if (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_SCREEN) { + kcd->is_angle_snapping = knife_snap_angle_screen(kcd); + } + else if (kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + kcd->is_angle_snapping = knife_snap_angle_relative(kcd); + if (kcd->is_angle_snapping) { + kcd->snap_ref_edges_count = knife_calculate_snap_ref_edges(kcd); + } + } + } + + if (kcd->axis_constrained) { + knife_constrain_axis(C, kcd); + } } { - kcd->curr.bmface = knife_find_closest_face( - kcd, kcd->curr.co, kcd->curr.cage, &kcd->curr.is_space); + kcd->curr.ob = kcd->vc.obedit; + kcd->curr.bmface = knife_find_closest_face(kcd, + &kcd->curr.ob, + &kcd->curr.base_index, + &kcd->curr.is_space, + kcd->curr.co, + kcd->curr.cage); if (kcd->curr.bmface) { kcd->curr.edge = knife_find_closest_edge_of_face( - kcd, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); + kcd, kcd->curr.ob, kcd->curr.base_index, kcd->curr.bmface, kcd->curr.co, kcd->curr.cage); } if (kcd->curr.edge) { @@ -2576,86 +3880,180 @@ static bool knife_snap_update_from_mval(KnifeTool_OpData *kcd, const float mval[ return kcd->curr.vert || kcd->curr.edge || (kcd->curr.bmface && !kcd->curr.is_space); } +/** + * TODO: Undo currently assumes that the most recent cut segment added is + * the last valid KnifeEdge in the kcd->kedges mempool. This could break in + * the future so it may be better to store the KnifeEdges for each KnifeUndoFrame + * on a stack. This stack could then be used instead of iterating over the mempool. + */ +static void knifetool_undo(KnifeTool_OpData *kcd) +{ + Ref *ref; + KnifeEdge *kfe, *newkfe; + KnifeEdge *lastkfe = NULL; + KnifeVert *v1, *v2; + KnifeUndoFrame *undo; + BLI_mempool_iter iterkfe; + + undo = BLI_stack_peek(kcd->undostack); + + /* Undo edge splitting. */ + for (int i = 0; i < undo->splits; i++) { + BLI_stack_pop(kcd->splitstack, &newkfe); + BLI_stack_pop(kcd->splitstack, &kfe); + knife_join_edge(newkfe, kfe); + } + + for (int i = 0; i < undo->cuts; i++) { + + BLI_mempool_iternew(kcd->kedges, &iterkfe); + for (kfe = BLI_mempool_iterstep(&iterkfe); kfe; kfe = BLI_mempool_iterstep(&iterkfe)) { + if (!kfe->is_cut || kfe->is_invalid || kfe->splits) { + continue; + } + lastkfe = kfe; + } + + if (lastkfe) { + lastkfe->is_invalid = true; + + /* TODO: Are they always guaranteed to be in this order? */ + v1 = lastkfe->v1; + v2 = lastkfe->v2; + + /* Only remove first vertex if it is the start segment of the cut. */ + if (!v1->is_invalid && !v1->is_splitting) { + v1->is_invalid = true; + /* If the first vertex is touching any other cut edges don't remove it. */ + for (ref = v1->edges.first; ref; ref = ref->next) { + kfe = ref->ref; + if (kfe->is_cut && !kfe->is_invalid) { + v1->is_invalid = false; + break; + } + } + } + + /* Only remove second vertex if it is the end segment of the cut. */ + if (!v2->is_invalid && !v2->is_splitting) { + v2->is_invalid = true; + /* If the second vertex is touching any other cut edges don't remove it. */ + for (ref = v2->edges.first; ref; ref = ref->next) { + kfe = ref->ref; + if (kfe->is_cut && !kfe->is_invalid) { + v2->is_invalid = false; + break; + } + } + } + } + } + + if (kcd->mode == MODE_DRAGGING) { + /* Restore kcd->prev. */ + kcd->prev = undo->pos; + } + + /* Restore data for distance and angle measurements. */ + kcd->mdata = undo->mdata; + + BLI_stack_discard(kcd->undostack); +} + /** \} */ /* -------------------------------------------------------------------- */ /** \name #KnifeTool_OpData (#op->customdata) Init and Free * \{ */ -static void knifetool_init_bmbvh(KnifeTool_OpData *kcd) +static void knifetool_init_cagecos(KnifeTool_OpData *kcd, Object *ob, uint base_index) { - BM_mesh_elem_index_ensure(kcd->em->bm, BM_VERT); Scene *scene_eval = (Scene *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->scene->id); - Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &kcd->ob->id); + Object *obedit_eval = (Object *)DEG_get_evaluated_id(kcd->vc.depsgraph, &ob->id); BMEditMesh *em_eval = BKE_editmesh_from_object(obedit_eval); - kcd->cagecos = (const float(*)[3])BKE_editmesh_vert_coords_alloc( - kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); + BM_mesh_elem_index_ensure(em_eval->bm, BM_VERT); - kcd->bmbvh = BKE_bmbvh_new_from_editmesh( - kcd->em, - BMBVH_RETURN_ORIG | - ((kcd->only_select && kcd->cut_through) ? BMBVH_RESPECT_SELECT : BMBVH_RESPECT_HIDDEN), - kcd->cagecos, - false); + kcd->cagecos[base_index] = (const float(*)[3])BKE_editmesh_vert_coords_alloc( + kcd->vc.depsgraph, em_eval, scene_eval, obedit_eval, NULL); } -static void knifetool_free_bmbvh(KnifeTool_OpData *kcd) +static void knifetool_free_cagecos(KnifeTool_OpData *kcd, uint base_index) { - if (kcd->bmbvh) { - BKE_bmbvh_free(kcd->bmbvh); - kcd->bmbvh = NULL; - } - - if (kcd->cagecos) { - MEM_freeN((void *)kcd->cagecos); - kcd->cagecos = NULL; + if (kcd->cagecos[base_index]) { + MEM_freeN((void *)kcd->cagecos[base_index]); + kcd->cagecos[base_index] = NULL; } } static void knife_init_colors(KnifeColors *colors) { - /* possible BMESH_TODO: add explicit themes or calculate these by + /* Possible BMESH_TODO: add explicit themes or calculate these by * figuring out contrasting colors with grid / edges / verts - * a la UI_make_axis_color */ + * a la UI_make_axis_color. */ UI_GetThemeColorType3ubv(TH_NURB_VLINE, SPACE_VIEW3D, colors->line); UI_GetThemeColorType3ubv(TH_NURB_ULINE, SPACE_VIEW3D, colors->edge); + UI_GetThemeColorType3ubv(TH_NURB_SEL_ULINE, SPACE_VIEW3D, colors->edge_extra); UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint); UI_GetThemeColorType3ubv(TH_HANDLE_SEL_VECT, SPACE_VIEW3D, colors->curpoint_a); colors->curpoint_a[3] = 102; UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point); UI_GetThemeColorType3ubv(TH_ACTIVE_SPLINE, SPACE_VIEW3D, colors->point_a); colors->point_a[3] = 102; + + UI_GetThemeColorType3ubv(TH_AXIS_X, SPACE_VIEW3D, colors->xaxis); + UI_GetThemeColorType3ubv(TH_AXIS_Y, SPACE_VIEW3D, colors->yaxis); + UI_GetThemeColorType3ubv(TH_AXIS_Z, SPACE_VIEW3D, colors->zaxis); + UI_GetThemeColorType3ubv(TH_TRANSFORM, SPACE_VIEW3D, colors->axis_extra); } /* called when modal loop selection gets set up... */ -static void knifetool_init(ViewContext *vc, +static void knifetool_init(bContext *C, + ViewContext *vc, KnifeTool_OpData *kcd, const bool only_select, const bool cut_through, + const bool xray, + const int visible_measurements, + const int angle_snapping, + const float angle_snapping_increment, const bool is_interactive) { kcd->vc = *vc; Scene *scene = vc->scene; - Object *obedit = vc->obedit; - /* assign the drawing handle for drawing preview line... */ + /* Assign the drawing handle for drawing preview line... */ kcd->scene = scene; - kcd->ob = obedit; kcd->region = vc->region; - invert_m4_m4_safe_ortho(kcd->ob_imat, kcd->ob->obmat); + kcd->objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + CTX_data_view_layer(C), CTX_wm_view3d(C), &kcd->objects_len); - kcd->em = BKE_editmesh_from_object(kcd->ob); + Object *ob; + BMEditMesh *em; + kcd->cagecos = MEM_callocN(sizeof(*kcd->cagecos) * kcd->objects_len, "knife cagecos"); + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); + knifetool_init_cagecos(kcd, ob, b); - /* cut all the way through the mesh if use_occlude_geometry button not pushed */ + /* Can't usefully select resulting edges in face mode. */ + kcd->select_result = (em->selectmode != SCE_SELECT_FACE); + } + knife_bvh_init(kcd); + + /* Cut all the way through the mesh if use_occlude_geometry button not pushed. */ kcd->is_interactive = is_interactive; kcd->cut_through = cut_through; kcd->only_select = only_select; - - knifetool_init_bmbvh(kcd); + kcd->depth_test = xray; + kcd->dist_angle_mode = visible_measurements; + kcd->show_dist_angle = (kcd->dist_angle_mode != KNF_MEASUREMENT_NONE); + kcd->angle_snapping_mode = angle_snapping; + kcd->angle_snapping = (kcd->angle_snapping_mode != KNF_CONSTRAIN_ANGLE_MODE_NONE); + kcd->angle_snapping_increment = angle_snapping_increment; kcd->arena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 15), "knife"); #ifdef USE_NET_ISLAND_CONNECT @@ -2666,7 +4064,7 @@ static void knifetool_init(ViewContext *vc, kcd->vthresh = KMAXDIST - 1; kcd->ethresh = KMAXDIST; - knife_recalc_projmat(kcd); + knife_recalc_ortho(kcd); ED_region_tag_redraw(kcd->region); @@ -2674,14 +4072,14 @@ static void knifetool_init(ViewContext *vc, kcd->kverts = BLI_mempool_create(sizeof(KnifeVert), 0, 512, BLI_MEMPOOL_ALLOW_ITER); kcd->kedges = BLI_mempool_create(sizeof(KnifeEdge), 0, 512, BLI_MEMPOOL_ALLOW_ITER); + kcd->undostack = BLI_stack_new(sizeof(KnifeUndoFrame), "knife undostack"); + kcd->splitstack = BLI_stack_new(sizeof(KnifeEdge *), "knife splitstack"); + kcd->origedgemap = BLI_ghash_ptr_new("knife origedgemap"); kcd->origvertmap = BLI_ghash_ptr_new("knife origvertmap"); kcd->kedgefacemap = BLI_ghash_ptr_new("knife kedgefacemap"); kcd->facetrimap = BLI_ghash_ptr_new("knife facetrimap"); - /* can't usefully select resulting edges in face mode */ - kcd->select_result = (kcd->em->selectmode != SCE_SELECT_FACE); - knife_pos_data_clear(&kcd->curr); knife_pos_data_clear(&kcd->prev); @@ -2691,6 +4089,18 @@ static void knifetool_init(ViewContext *vc, knife_init_colors(&kcd->colors); } + + kcd->no_cuts = true; + + kcd->axis_string[0] = ' '; + kcd->axis_string[1] = '\0'; + + /* Initialize number input handling for angle snapping. */ + initNumInput(&kcd->num); + kcd->num.idx_max = 0; + kcd->num.val_flag[0] |= NUM_NO_NEGATIVE; + kcd->num.unit_sys = scene->unit.system; + kcd->num.unit_type[0] = B_UNIT_NONE; } /* called when modal loop selection is done... */ @@ -2703,15 +4113,18 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) if (kcd->is_interactive) { WM_cursor_modal_restore(kcd->vc.win); - /* deactivate the extra drawing stuff in 3D-View */ + /* Deactivate the extra drawing stuff in 3D-View. */ ED_region_draw_cb_exit(kcd->region->type, kcd->draw_handle); } - /* free the custom data */ + /* Free the custom data. */ BLI_mempool_destroy(kcd->refs); BLI_mempool_destroy(kcd->kverts); BLI_mempool_destroy(kcd->kedges); + BLI_stack_free(kcd->undostack); + BLI_stack_free(kcd->splitstack); + BLI_ghash_free(kcd->origedgemap, NULL, NULL); BLI_ghash_free(kcd->origvertmap, NULL, NULL); BLI_ghash_free(kcd->kedgefacemap, NULL, NULL); @@ -2723,18 +4136,28 @@ static void knifetool_exit_ex(KnifeTool_OpData *kcd) #endif BLI_gset_free(kcd->edgenet.edge_visit, NULL); - /* tag for redraw */ + /* Tag for redraw. */ ED_region_tag_redraw(kcd->region); - knifetool_free_bmbvh(kcd); + /* Knife BVH cleanup. */ + for (int i = 0; i < kcd->objects_len; i++) { + knifetool_free_cagecos(kcd, i); + } + MEM_freeN(kcd->cagecos); + knife_bvh_free(kcd); + /* Line-hits cleanup. */ if (kcd->linehits) { MEM_freeN(kcd->linehits); } - /* destroy kcd itself */ + /* Free object bases. */ + MEM_freeN(kcd->objects); + + /* Destroy kcd itself. */ MEM_freeN(kcd); } + static void knifetool_exit(wmOperator *op) { KnifeTool_OpData *kcd = op->customdata; @@ -2748,24 +4171,24 @@ static void knifetool_exit(wmOperator *op) /** \name Mouse-Moving Event Updates * \{ */ -/* update active knife edge/vert pointers */ -static int knife_update_active(KnifeTool_OpData *kcd) +/* Update active knife edge/vert pointers. */ +static int knife_update_active(bContext *C, KnifeTool_OpData *kcd) { - /* if no hits are found this would normally default to (0, 0, 0) so instead + /* If no hits are found this would normally default to (0, 0, 0) so instead * get a point at the mouse ray closest to the previous point. * Note that drawing lines in `free-space` isn't properly supported * but there's no guarantee (0, 0, 0) has any geometry either - campbell */ - if (!knife_snap_update_from_mval(kcd, kcd->mval)) { + if (!knife_snap_update_from_mval(C, kcd, kcd->mval)) { float origin[3]; float origin_ofs[3]; knife_input_ray_segment(kcd, kcd->curr.mval, 1.0f, origin, origin_ofs); if (!isect_line_plane_v3( - kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->proj_zaxis)) { + kcd->curr.cage, origin, origin_ofs, kcd->prev.cage, kcd->vc.rv3d->viewinv[2])) { copy_v3_v3(kcd->curr.cage, kcd->prev.cage); - /* should never fail! */ + /* Should never fail! */ BLI_assert(0); } } @@ -2776,20 +4199,20 @@ static int knife_update_active(KnifeTool_OpData *kcd) return 1; } -static void knifetool_update_mval(KnifeTool_OpData *kcd, const float mval[2]) +static void knifetool_update_mval(bContext *C, KnifeTool_OpData *kcd, const float mval[2]) { - knife_recalc_projmat(kcd); + knife_recalc_ortho(kcd); copy_v2_v2(kcd->mval, mval); - if (knife_update_active(kcd)) { + if (knife_update_active(C, kcd)) { ED_region_tag_redraw(kcd->region); } } -static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2]) +static void knifetool_update_mval_i(bContext *C, KnifeTool_OpData *kcd, const int mval_i[2]) { const float mval[2] = {UNPACK2(mval_i)}; - knifetool_update_mval(kcd, mval); + knifetool_update_mval(C, kcd, mval); } /** \} */ @@ -2798,21 +4221,40 @@ static void knifetool_update_mval_i(KnifeTool_OpData *kcd, const int mval_i[2]) /** \name Finalization * \{ */ -/* called on tool confirmation */ +/* Called on tool confirmation. */ static void knifetool_finish_ex(KnifeTool_OpData *kcd) { - knife_make_cuts(kcd); + Object *ob; + BMEditMesh *em; + for (uint b = 0; b < kcd->objects_len; b++) { + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); - EDBM_selectmode_flush(kcd->em); - EDBM_update(kcd->ob->data, + knife_make_cuts(kcd, ob); + + EDBM_selectmode_flush(em); + EDBM_update(ob->data, + &(const struct EDBMUpdate_Params){ + .calc_looptri = true, + .calc_normals = true, + .is_destructive = true, + }); + } +} + +static void knifetool_finish_single_ex(KnifeTool_OpData *kcd, Object *ob, uint UNUSED(base_index)) +{ + knife_make_cuts(kcd, ob); + + BMEditMesh *em = BKE_editmesh_from_object(ob); + + EDBM_selectmode_flush(em); + EDBM_update(ob->data, &(const struct EDBMUpdate_Params){ .calc_looptri = true, .calc_normals = true, .is_destructive = true, }); - - /* Re-tessellating makes this invalid, don't use again by accident. */ - knifetool_free_bmbvh(kcd); } static void knifetool_finish(wmOperator *op) @@ -2838,22 +4280,37 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) static const EnumPropertyItem modal_items[] = { {KNF_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, {KNF_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + {KNF_MODAL_UNDO, "UNDO", 0, "Undo", ""}, {KNF_MODAL_MIDPOINT_ON, "SNAP_MIDPOINTS_ON", 0, "Snap to Midpoints On", ""}, {KNF_MODAL_MIDPOINT_OFF, "SNAP_MIDPOINTS_OFF", 0, "Snap to Midpoints Off", ""}, {KNF_MODAL_IGNORE_SNAP_ON, "IGNORE_SNAP_ON", 0, "Ignore Snapping On", ""}, {KNF_MODAL_IGNORE_SNAP_OFF, "IGNORE_SNAP_OFF", 0, "Ignore Snapping Off", ""}, {KNF_MODAL_ANGLE_SNAP_TOGGLE, "ANGLE_SNAP_TOGGLE", 0, "Toggle Angle Snapping", ""}, + {KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE, + "CYCLE_ANGLE_SNAP_EDGE", + 0, + "Cycle Angle Snapping Relative Edge", + ""}, {KNF_MODAL_CUT_THROUGH_TOGGLE, "CUT_THROUGH_TOGGLE", 0, "Toggle Cut Through", ""}, + {KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE, + "SHOW_DISTANCE_ANGLE_TOGGLE", + 0, + "Toggle Distance and Angle Measurements", + ""}, + {KNF_MODAL_DEPTH_TEST_TOGGLE, "DEPTH_TEST_TOGGLE", 0, "Toggle Depth Testing", ""}, {KNF_MODAL_NEW_CUT, "NEW_CUT", 0, "End Current Cut", ""}, {KNF_MODAL_ADD_CUT, "ADD_CUT", 0, "Add Cut", ""}, {KNF_MODAL_ADD_CUT_CLOSED, "ADD_CUT_CLOSED", 0, "Add Cut Closed", ""}, {KNF_MODAL_PANNING, "PANNING", 0, "Panning", ""}, + {KNF_MODAL_X_AXIS, "X_AXIS", 0, "X Axis Locking", ""}, + {KNF_MODAL_Y_AXIS, "Y_AXIS", 0, "Y Axis Locking", ""}, + {KNF_MODAL_Z_AXIS, "Z_AXIS", 0, "Z Axis Locking", ""}, {0, NULL, 0, NULL, NULL}, }; wmKeyMap *keymap = WM_modalkeymap_find(keyconf, "Knife Tool Modal Map"); - /* this function is called for each spacetype, only needs to add map once */ + /* This function is called for each spacetype, only needs to add map once. */ if (keymap && keymap->modal_items) { return NULL; } @@ -2865,28 +4322,67 @@ wmKeyMap *knifetool_modal_keymap(wmKeyConfig *keyconf) return keymap; } +/* Turn off angle snapping. */ +static void knifetool_disable_angle_snapping(KnifeTool_OpData *kcd) +{ + kcd->angle_snapping_mode = KNF_CONSTRAIN_ANGLE_MODE_NONE; + kcd->angle_snapping = false; + kcd->is_angle_snapping = false; +} + +/* Turn off orientation locking. */ +static void knifetool_disable_orientation_locking(KnifeTool_OpData *kcd) +{ + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_MODE_NONE; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE; + kcd->axis_constrained = false; +} + static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) { - Object *obedit = CTX_data_edit_object(C); KnifeTool_OpData *kcd = op->customdata; bool do_refresh = false; - if (!obedit || obedit->type != OB_MESH || BKE_editmesh_from_object(obedit) != kcd->em) { + if (!kcd->curr.ob || kcd->curr.ob->type != OB_MESH) { knifetool_exit(op); ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; } - em_setup_viewcontext(C, &kcd->vc); kcd->region = kcd->vc.region; - ED_view3d_init_mats_rv3d(obedit, kcd->vc.rv3d); /* needed to initialize clipping */ + ED_view3d_init_mats_rv3d(kcd->curr.ob, kcd->vc.rv3d); /* Needed to initialize clipping. */ if (kcd->mode == MODE_PANNING) { kcd->mode = kcd->prevmode; } - /* handle modal keymap */ + bool handled = false; + float snapping_increment_temp; + + if (kcd->angle_snapping) { + if (kcd->num.str_cur >= 3 || + kcd->angle_snapping_increment > KNIFE_MAX_ANGLE_SNAPPING_INCREMENT / 10) { + knife_reset_snap_angle_input(kcd); + } + knife_update_header(C, op, kcd); /* Update the angle multiple. */ + /* Modal numinput active, try to handle numeric inputs first... */ + if (event->val == KM_PRESS && hasNumInput(&kcd->num) && handleNumInput(C, &kcd->num, event)) { + handled = true; + applyNumInput(&kcd->num, &snapping_increment_temp); + /* Restrict number key input to 0 - 180 degree range. */ + if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + kcd->angle_snapping_increment = snapping_increment_temp; + } + knife_update_active(C, kcd); + knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); + return OPERATOR_RUNNING_MODAL; + } + } + + /* Handle modal keymap. */ if (event->type == EVT_MODAL_MAP) { switch (event->val) { case KNF_MODAL_CANCEL: @@ -2906,56 +4402,129 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_workspace_status_text(C, NULL); return OPERATOR_FINISHED; + case KNF_MODAL_UNDO: + if (BLI_stack_is_empty(kcd->undostack)) { + ED_region_tag_redraw(kcd->region); + knifetool_exit(op); + ED_workspace_status_text(C, NULL); + return OPERATOR_CANCELLED; + } + knifetool_undo(kcd); + knife_update_active(C, kcd); + ED_region_tag_redraw(kcd->region); + handled = true; + break; case KNF_MODAL_MIDPOINT_ON: kcd->snap_midpoints = true; - knife_recalc_projmat(kcd); - knife_update_active(kcd); + knife_recalc_ortho(kcd); + knife_update_active(C, kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; + handled = true; break; case KNF_MODAL_MIDPOINT_OFF: kcd->snap_midpoints = false; - knife_recalc_projmat(kcd); - knife_update_active(kcd); + knife_recalc_ortho(kcd); + knife_update_active(C, kcd); knife_update_header(C, op, kcd); ED_region_tag_redraw(kcd->region); do_refresh = true; + handled = true; break; case KNF_MODAL_IGNORE_SNAP_ON: ED_region_tag_redraw(kcd->region); kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = true; knife_update_header(C, op, kcd); do_refresh = true; + handled = true; break; case KNF_MODAL_IGNORE_SNAP_OFF: ED_region_tag_redraw(kcd->region); kcd->ignore_vert_snapping = kcd->ignore_edge_snapping = false; knife_update_header(C, op, kcd); do_refresh = true; + handled = true; break; case KNF_MODAL_ANGLE_SNAP_TOGGLE: - kcd->angle_snapping = !kcd->angle_snapping; + if (kcd->angle_snapping_mode != KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + kcd->angle_snapping_mode++; + kcd->snap_ref_edges_count = 0; + kcd->snap_edge = 0; + } + else { + kcd->angle_snapping_mode = KNF_CONSTRAIN_ANGLE_MODE_NONE; + } + kcd->angle_snapping = (kcd->angle_snapping_mode != KNF_CONSTRAIN_ANGLE_MODE_NONE); + kcd->angle_snapping_increment = RAD2DEGF( + RNA_float_get(op->ptr, "angle_snapping_increment")); + knifetool_disable_orientation_locking(kcd); + knife_reset_snap_angle_input(kcd); + knife_update_active(C, kcd); knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); do_refresh = true; + handled = true; + break; + case KNF_MODAL_CYCLE_ANGLE_SNAP_EDGE: + if (kcd->angle_snapping && kcd->angle_snapping_mode == KNF_CONSTRAIN_ANGLE_MODE_RELATIVE) { + if (kcd->snap_ref_edges_count) { + kcd->snap_edge++; + kcd->snap_edge %= kcd->snap_ref_edges_count; + } + } + do_refresh = true; + handled = true; break; case KNF_MODAL_CUT_THROUGH_TOGGLE: kcd->cut_through = !kcd->cut_through; knife_update_header(C, op, kcd); do_refresh = true; + handled = true; + break; + case KNF_MODAL_SHOW_DISTANCE_ANGLE_TOGGLE: + if (kcd->dist_angle_mode != KNF_MEASUREMENT_ANGLE) { + kcd->dist_angle_mode++; + } + else { + kcd->dist_angle_mode = KNF_MEASUREMENT_NONE; + } + kcd->show_dist_angle = (kcd->dist_angle_mode != KNF_MEASUREMENT_NONE); + knife_update_header(C, op, kcd); + do_refresh = true; + handled = true; + break; + case KNF_MODAL_DEPTH_TEST_TOGGLE: + kcd->depth_test = !kcd->depth_test; + ED_region_tag_redraw(kcd->region); + knife_update_header(C, op, kcd); + do_refresh = true; + handled = true; break; case KNF_MODAL_NEW_CUT: + /* If no cuts have been made, exit. + * Preserves right click cancel workflow which most tools use, + * but stops accidentally deleting entire cuts with right click. + */ + if (kcd->no_cuts) { + ED_region_tag_redraw(kcd->region); + knifetool_exit(op); + ED_workspace_status_text(C, NULL); + return OPERATOR_CANCELLED; + } ED_region_tag_redraw(kcd->region); knife_finish_cut(kcd); kcd->mode = MODE_IDLE; + handled = true; break; case KNF_MODAL_ADD_CUT: - knife_recalc_projmat(kcd); + kcd->no_cuts = false; + knife_recalc_ortho(kcd); - /* get the value of the event which triggered this one */ - if (event->prevval != KM_RELEASE) { + /* Get the value of the event which triggered this one. */ + if (event->prev_val != KM_RELEASE) { if (kcd->mode == MODE_DRAGGING) { knife_add_cut(kcd); } @@ -2965,7 +4534,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) kcd->init = kcd->curr; } - /* freehand drawing is incompatible with cut-through */ + /* Freehand drawing is incompatible with cut-through. */ if (kcd->cut_through == false) { kcd->is_drag_hold = true; /* No edge snapping while dragging (edges are too sticky when cuts are immediate). */ @@ -2975,12 +4544,14 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) else { kcd->is_drag_hold = false; kcd->ignore_edge_snapping = false; + kcd->is_drag_undo = false; - /* needed because the last face 'hit' is ignored when dragging */ - knifetool_update_mval(kcd, kcd->curr.mval); + /* Needed because the last face 'hit' is ignored when dragging. */ + knifetool_update_mval(C, kcd, kcd->curr.mval); } ED_region_tag_redraw(kcd->region); + handled = true; break; case KNF_MODAL_ADD_CUT_CLOSED: if (kcd->mode == MODE_DRAGGING) { @@ -2988,14 +4559,15 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Shouldn't be possible with default key-layout, just in case. */ if (kcd->is_drag_hold) { kcd->is_drag_hold = false; - knifetool_update_mval(kcd, kcd->curr.mval); + kcd->is_drag_undo = false; + knifetool_update_mval(C, kcd, kcd->curr.mval); } kcd->prev = kcd->curr; kcd->curr = kcd->init; knife_project_v2(kcd, kcd->curr.cage, kcd->curr.mval); - knifetool_update_mval(kcd, kcd->curr.mval); + knifetool_update_mval(C, kcd, kcd->curr.mval); knife_add_cut(kcd); @@ -3003,6 +4575,7 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) knife_finish_cut(kcd); kcd->mode = MODE_IDLE; } + handled = true; break; case KNF_MODAL_PANNING: if (event->val != KM_RELEASE) { @@ -3028,9 +4601,9 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) case WHEELDOWNMOUSE: case NDOF_MOTION: return OPERATOR_PASS_THROUGH; - case MOUSEMOVE: /* mouse moved somewhere to select another loop */ + case MOUSEMOVE: /* Mouse moved somewhere to select another loop. */ if (kcd->mode != MODE_PANNING) { - knifetool_update_mval_i(kcd, event->mval); + knifetool_update_mval_i(C, kcd, event->mval); if (kcd->is_drag_hold) { if (kcd->totlinehit >= 2) { @@ -3043,6 +4616,65 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) } } + if (kcd->angle_snapping) { + if (kcd->num.str_cur >= 3 || + kcd->angle_snapping_increment > KNIFE_MAX_ANGLE_SNAPPING_INCREMENT / 10) { + knife_reset_snap_angle_input(kcd); + } + if (event->type != EVT_MODAL_MAP) { + /* Modal number-input inactive, try to handle numeric inputs last. */ + if (!handled && event->val == KM_PRESS && handleNumInput(C, &kcd->num, event)) { + applyNumInput(&kcd->num, &snapping_increment_temp); + /* Restrict number key input to 0 - 180 degree range. */ + if (snapping_increment_temp > KNIFE_MIN_ANGLE_SNAPPING_INCREMENT && + snapping_increment_temp <= KNIFE_MAX_ANGLE_SNAPPING_INCREMENT) { + kcd->angle_snapping_increment = snapping_increment_temp; + } + knife_update_active(C, kcd); + knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); + return OPERATOR_RUNNING_MODAL; + } + } + } + + /* Constrain axes with X,Y,Z keys. */ + if (event->type == EVT_MODAL_MAP) { + if (ELEM(event->val, KNF_MODAL_X_AXIS, KNF_MODAL_Y_AXIS, KNF_MODAL_Z_AXIS)) { + if (event->val == KNF_MODAL_X_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_X) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_X; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'X'; + } + else if (event->val == KNF_MODAL_Y_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Y) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Y; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'Y'; + } + else if (event->val == KNF_MODAL_Z_AXIS && kcd->constrain_axis != KNF_CONSTRAIN_AXIS_Z) { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_Z; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_GLOBAL; + kcd->axis_string[0] = 'Z'; + } + else { + /* Cycle through modes with repeated key presses. */ + if (kcd->constrain_axis_mode != KNF_CONSTRAIN_AXIS_MODE_LOCAL) { + kcd->constrain_axis_mode++; + kcd->axis_string[0] += 32; /* Lower case. */ + } + else { + kcd->constrain_axis = KNF_CONSTRAIN_AXIS_NONE; + kcd->constrain_axis_mode = KNF_CONSTRAIN_AXIS_MODE_NONE; + } + } + kcd->axis_constrained = (kcd->constrain_axis != KNF_CONSTRAIN_AXIS_NONE); + knifetool_disable_angle_snapping(kcd); + knife_update_header(C, op, kcd); + ED_region_tag_redraw(kcd->region); + do_refresh = true; + } + } + if (kcd->mode == MODE_DRAGGING) { op->flag &= ~OP_IS_MODAL_CURSOR_REGION; } @@ -3051,12 +4683,12 @@ static int knifetool_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (do_refresh) { - /* we don't really need to update mval, - * but this happens to be the best way to refresh at the moment */ - knifetool_update_mval_i(kcd, event->mval); + /* We don't really need to update mval, + * but this happens to be the best way to refresh at the moment. */ + knifetool_update_mval_i(C, kcd, event->mval); } - /* keep going until the user confirms */ + /* Keep going until the user confirms. */ return OPERATOR_RUNNING_MODAL; } @@ -3064,39 +4696,64 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) { const bool only_select = RNA_boolean_get(op->ptr, "only_selected"); const bool cut_through = !RNA_boolean_get(op->ptr, "use_occlude_geometry"); + const bool xray = !RNA_boolean_get(op->ptr, "xray"); + const int visible_measurements = RNA_enum_get(op->ptr, "visible_measurements"); + const int angle_snapping = RNA_enum_get(op->ptr, "angle_snapping"); const bool wait_for_input = RNA_boolean_get(op->ptr, "wait_for_input"); + const float angle_snapping_increment = RAD2DEGF( + RNA_float_get(op->ptr, "angle_snapping_increment")); ViewContext vc; KnifeTool_OpData *kcd; em_setup_viewcontext(C, &vc); + /* alloc new customdata */ + kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); + + knifetool_init(C, + &vc, + kcd, + only_select, + cut_through, + xray, + visible_measurements, + angle_snapping, + angle_snapping_increment, + true); + if (only_select) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - if (em->bm->totfacesel == 0) { + Object *obedit; + BMEditMesh *em; + bool faces_selected = false; + + for (uint b = 0; b < kcd->objects_len; b++) { + obedit = kcd->objects[b]; + em = BKE_editmesh_from_object(obedit); + if (em->bm->totfacesel != 0) { + faces_selected = true; + } + } + + if (!faces_selected) { BKE_report(op->reports, RPT_ERROR, "Selected faces required"); + knifetool_cancel(C, op); return OPERATOR_CANCELLED; } } - /* alloc new customdata */ - kcd = op->customdata = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - - knifetool_init(&vc, kcd, only_select, cut_through, true); - op->flag |= OP_IS_MODAL_CURSOR_REGION; - /* add a modal handler for this operator - handles loop selection */ + /* Add a modal handler for this operator - handles loop selection. */ WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_KNIFE); WM_event_add_modal_handler(C, op); - knifetool_update_mval_i(kcd, event->mval); + knifetool_update_mval_i(C, kcd, event->mval); if (wait_for_input == false) { /* Avoid copy-paste logic. */ wmEvent event_modal = { - .prevval = KM_NOTHING, + .prev_val = KM_NOTHING, .type = EVT_MODAL_MAP, .val = KNF_MODAL_ADD_CUT, }; @@ -3112,28 +4769,72 @@ static int knifetool_invoke(bContext *C, wmOperator *op, const wmEvent *event) void MESH_OT_knife_tool(wmOperatorType *ot) { - /* description */ + /* Description. */ ot->name = "Knife Topology Tool"; ot->idname = "MESH_OT_knife_tool"; ot->description = "Cut new topology"; - /* callbacks */ + /* Callbacks. */ ot->invoke = knifetool_invoke; ot->modal = knifetool_modal; ot->cancel = knifetool_cancel; ot->poll = ED_operator_editmesh_view3d; - /* flags */ + /* Flags. */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_BLOCKING; - /* properties */ + /* Properties. */ PropertyRNA *prop; + static const EnumPropertyItem visible_measurements_items[] = { + {KNF_MEASUREMENT_NONE, "NONE", 0, "None", "Show no measurements"}, + {KNF_MEASUREMENT_BOTH, "BOTH", 0, "Both", "Show both distances and angles"}, + {KNF_MEASUREMENT_DISTANCE, "DISTANCE", 0, "Distance", "Show just distance measurements"}, + {KNF_MEASUREMENT_ANGLE, "ANGLE", 0, "Angle", "Show just angle measurements"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem angle_snapping_items[] = { + {KNF_CONSTRAIN_ANGLE_MODE_NONE, "NONE", 0, "None", "No angle snapping"}, + {KNF_CONSTRAIN_ANGLE_MODE_SCREEN, "SCREEN", 0, "Screen", "Screen space angle snapping"}, + {KNF_CONSTRAIN_ANGLE_MODE_RELATIVE, + "RELATIVE", + 0, + "Relative", + "Angle snapping relative to the previous cut edge"}, + {0, NULL, 0, NULL, NULL}, + }; + RNA_def_boolean(ot->srna, "use_occlude_geometry", true, "Occlude Geometry", "Only cut the front most geometry"); RNA_def_boolean(ot->srna, "only_selected", false, "Only Selected", "Only cut selected geometry"); + RNA_def_boolean(ot->srna, "xray", true, "X-Ray", "Show cuts hidden by geometry"); + + RNA_def_enum(ot->srna, + "visible_measurements", + visible_measurements_items, + KNF_MEASUREMENT_NONE, + "Measurements", + "Visible distance and angle measurements"); + RNA_def_enum(ot->srna, + "angle_snapping", + angle_snapping_items, + KNF_CONSTRAIN_ANGLE_MODE_NONE, + "Angle Snapping", + "Angle snapping mode"); + + prop = RNA_def_float(ot->srna, + "angle_snapping_increment", + DEG2RADF(KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT), + DEG2RADF(KNIFE_MIN_ANGLE_SNAPPING_INCREMENT), + DEG2RADF(KNIFE_MAX_ANGLE_SNAPPING_INCREMENT), + "Angle Snap Increment", + "The angle snap increment used when in constrained angle mode", + DEG2RADF(KNIFE_MIN_ANGLE_SNAPPING_INCREMENT), + DEG2RADF(KNIFE_MAX_ANGLE_SNAPPING_INCREMENT)); + RNA_def_property_subtype(prop, PROP_ANGLE); prop = RNA_def_boolean(ot->srna, "wait_for_input", true, "Wait for Input", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); @@ -3168,32 +4869,41 @@ static bool edbm_mesh_knife_point_isect(LinkNode *polys, const float cent_ss[2]) /** * \param use_tag: When set, tag all faces inside the polylines. */ -void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) +void EDBM_mesh_knife(bContext *C, ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_through) { KnifeTool_OpData *kcd; - /* init */ + /* Init. */ { const bool only_select = false; - const bool is_interactive = false; /* can enable for testing */ + const bool is_interactive = false; /* Can enable for testing. */ + const bool xray = false; + const int visible_measurements = KNF_MEASUREMENT_NONE; + const int angle_snapping = KNF_CONSTRAIN_ANGLE_MODE_NONE; + const float angle_snapping_increment = KNIFE_DEFAULT_ANGLE_SNAPPING_INCREMENT; kcd = MEM_callocN(sizeof(KnifeTool_OpData), __func__); - knifetool_init(vc, kcd, only_select, cut_through, is_interactive); + knifetool_init(C, + vc, + kcd, + only_select, + cut_through, + xray, + visible_measurements, + angle_snapping, + angle_snapping_increment, + is_interactive); kcd->ignore_edge_snapping = true; kcd->ignore_vert_snapping = true; - - if (use_tag) { - BM_mesh_elem_hflag_enable_all(kcd->em->bm, BM_EDGE, BM_ELEM_TAG, false); - } } - /* execute */ + /* Execute. */ { LinkNode *p = polys; - knife_recalc_projmat(kcd); + knife_recalc_ortho(kcd); while (p) { const float(*mval_fl)[2] = p->link; @@ -3201,7 +4911,7 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th int i; for (i = 0; i < mval_tot; i++) { - knifetool_update_mval(kcd, mval_fl[i]); + knifetool_update_mval(C, kcd, mval_fl[i]); if (i == 0) { knife_start_cut(kcd); kcd->mode = MODE_DRAGGING; @@ -3216,104 +4926,108 @@ void EDBM_mesh_knife(ViewContext *vc, LinkNode *polys, bool use_tag, bool cut_th } } - /* finish */ + /* Finish. */ { - knifetool_finish_ex(kcd); + Object *ob; + BMEditMesh *em; + for (uint b = 0; b < kcd->objects_len; b++) { - /* tag faces inside! */ - if (use_tag) { - BMesh *bm = kcd->em->bm; - float projmat[4][4]; + ob = kcd->objects[b]; + em = BKE_editmesh_from_object(ob); - BMEdge *e; - BMIter iter; - - bool keep_search; - - /* freed on knifetool_finish_ex, but we need again to check if points are visible */ - if (kcd->cut_through == false) { - knifetool_init_bmbvh(kcd); + if (use_tag) { + BM_mesh_elem_hflag_enable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false); } - ED_view3d_ob_project_mat_get(kcd->region->regiondata, kcd->ob, projmat); + knifetool_finish_single_ex(kcd, ob, b); - /* use face-loop tag to store if we have intersected */ + /* Tag faces inside! */ + if (use_tag) { + BMesh *bm = em->bm; + BMEdge *e; + BMIter iter; + bool keep_search; + + /* Use face-loop tag to store if we have intersected. */ #define F_ISECT_IS_UNKNOWN(f) BM_elem_flag_test(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) #define F_ISECT_SET_UNKNOWN(f) BM_elem_flag_enable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) #define F_ISECT_SET_OUTSIDE(f) BM_elem_flag_disable(BM_FACE_FIRST_LOOP(f), BM_ELEM_TAG) - { - BMFace *f; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - F_ISECT_SET_UNKNOWN(f); - BM_elem_flag_disable(f, BM_ELEM_TAG); - } - } - - /* tag all faces linked to cut edges */ - BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { - /* check are we tagged?, then we are an original face */ - if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { + { BMFace *f; - BMIter fiter; - BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { - float cent[3], cent_ss[2]; - BM_face_calc_point_in_face(f, cent); - knife_project_v2(kcd, cent, cent_ss); - if (edbm_mesh_knife_point_isect(polys, cent_ss)) { - BM_elem_flag_enable(f, BM_ELEM_TAG); - } + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + F_ISECT_SET_UNKNOWN(f); + BM_elem_flag_disable(f, BM_ELEM_TAG); } } - } - /* expand tags for faces which are not cut, but are inside the polys */ - do { - BMFace *f; - keep_search = false; - BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { - if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { - /* am I connected to a tagged face via an un-tagged edge - * (ie, not across a cut) */ - BMLoop *l_first = BM_FACE_FIRST_LOOP(f); - BMLoop *l_iter = l_first; - bool found = false; - - do { - if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { - /* now check if the adjacent faces is tagged */ - BMLoop *l_radial_iter = l_iter->radial_next; - if (l_radial_iter != l_iter) { - do { - if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { - found = true; - } - } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && - (found == false)); - } - } - } while ((l_iter = l_iter->next) != l_first && (found == false)); - - if (found) { + /* Tag all faces linked to cut edges. */ + BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) { + /* Check are we tagged?, then we are an original face. */ + if (BM_elem_flag_test(e, BM_ELEM_TAG) == false) { + BMFace *f; + BMIter fiter; + BM_ITER_ELEM (f, &fiter, e, BM_FACES_OF_EDGE) { float cent[3], cent_ss[2]; BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); knife_project_v2(kcd, cent, cent_ss); - if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && - edbm_mesh_knife_point_isect(polys, cent_ss)) { + if (edbm_mesh_knife_point_isect(polys, cent_ss)) { BM_elem_flag_enable(f, BM_ELEM_TAG); - keep_search = true; - } - else { - /* don't lose time on this face again, set it as outside */ - F_ISECT_SET_OUTSIDE(f); } } } } - } while (keep_search); + + /* Expand tags for faces which are not cut, but are inside the polys. */ + do { + BMFace *f; + keep_search = false; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (BM_elem_flag_test(f, BM_ELEM_TAG) == false && (F_ISECT_IS_UNKNOWN(f))) { + /* Am I connected to a tagged face via an un-tagged edge + * (ie, not across a cut)? */ + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + bool found = false; + + do { + if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG) != false) { + /* Now check if the adjacent faces is tagged. */ + BMLoop *l_radial_iter = l_iter->radial_next; + if (l_radial_iter != l_iter) { + do { + if (BM_elem_flag_test(l_radial_iter->f, BM_ELEM_TAG)) { + found = true; + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter && + (found == false)); + } + } + } while ((l_iter = l_iter->next) != l_first && (found == false)); + + if (found) { + float cent[3], cent_ss[2]; + BM_face_calc_point_in_face(f, cent); + mul_m4_v3(ob->obmat, cent); + knife_project_v2(kcd, cent, cent_ss); + if ((kcd->cut_through || point_is_visible(kcd, cent, cent_ss, (BMElem *)f)) && + edbm_mesh_knife_point_isect(polys, cent_ss)) { + BM_elem_flag_enable(f, BM_ELEM_TAG); + keep_search = true; + } + else { + /* Don't lose time on this face again, set it as outside. */ + F_ISECT_SET_OUTSIDE(f); + } + } + } + } + } while (keep_search); #undef F_ISECT_IS_UNKNOWN #undef F_ISECT_SET_UNKNOWN #undef F_ISECT_SET_OUTSIDE + } } knifetool_exit_ex(kcd); diff --git a/source/blender/editors/mesh/editmesh_knife_project.c b/source/blender/editors/mesh/editmesh_knife_project.c index 669a09b3fd3..3129fb047ab 100644 --- a/source/blender/editors/mesh/editmesh_knife_project.c +++ b/source/blender/editors/mesh/editmesh_knife_project.c @@ -158,7 +158,7 @@ static int knifeproject_exec(bContext *C, wmOperator *op) ED_view3d_viewcontext_init_object(&vc, obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit); - EDBM_mesh_knife(&vc, polys, true, cut_through); + EDBM_mesh_knife(C, &vc, polys, true, cut_through); /* select only tagged faces */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_SELECT, false); diff --git a/source/blender/editors/mesh/editmesh_loopcut.c b/source/blender/editors/mesh/editmesh_loopcut.c index 8b78b091fd2..24825ef1e3a 100644 --- a/source/blender/editors/mesh/editmesh_loopcut.c +++ b/source/blender/editors/mesh/editmesh_loopcut.c @@ -598,13 +598,13 @@ static int loopcut_modal(bContext *C, wmOperator *op, const wmEvent *event) break; case MOUSEPAN: if (event->alt == 0) { - cuts += 0.02f * (event->y - event->prevy); + cuts += 0.02f * (event->xy[1] - event->prev_xy[1]); if (cuts < 1 && lcd->cuts >= 1) { cuts = 1; } } else { - smoothness += 0.002f * (event->y - event->prevy); + smoothness += 0.002f * (event->xy[1] - event->prev_xy[1]); } handled = true; break; diff --git a/source/blender/editors/mesh/editmesh_mask_extract.c b/source/blender/editors/mesh/editmesh_mask_extract.c index cccfc7e934c..8cfcc96517c 100644 --- a/source/blender/editors/mesh/editmesh_mask_extract.c +++ b/source/blender/editors/mesh/editmesh_mask_extract.c @@ -389,13 +389,14 @@ static int face_set_extract_modal(bContext *C, wmOperator *op, const wmEvent *ev * the PBVH and update the active Face Set ID. */ bScreen *screen = CTX_wm_screen(C); ARegion *region = BKE_screen_find_main_region_at_xy( - screen, SPACE_VIEW3D, event->x, event->y); + screen, SPACE_VIEW3D, event->xy[0], event->xy[1]); if (!region) { return OPERATOR_CANCELLED; } - const float mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; + const float mval[2] = {event->xy[0] - region->winrct.xmin, + event->xy[1] - region->winrct.ymin}; Object *ob = CTX_data_active_object(C); const int face_set_id = ED_sculpt_face_sets_active_update_and_get(C, ob, mval); diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index d1df063d9d0..07ec8e184ba 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -930,7 +930,7 @@ static int edbm_rip_invoke__edge(bContext *C, const wmEvent *event, Object *obed /* NOTE: if the case of 3 edges has one change in loop stepping, * if this becomes more involved we may be better off splitting * the 3 edge case into its own else-if branch */ - if ((totedge_manifold == 4 || totedge_manifold == 3) || (all_manifold == false)) { + if ((ELEM(totedge_manifold, 4, 3)) || (all_manifold == false)) { BMLoop *l_a = e_best->l; BMLoop *l_b = l_a->radial_next; diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 2fcf8fa6f8f..e0768bcff24 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -884,9 +884,8 @@ static bool unified_findnearest(ViewContext *vc, BMFace **r_efa) { BMEditMesh *em = vc->em; - static short mval_prev[2] = {-1, -1}; - /* only cycle while the mouse remains still */ - const bool use_cycle = ((mval_prev[0] == vc->mval[0]) && (mval_prev[1] == vc->mval[1])); + + const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval); const float dist_init = ED_view3d_select_dist_px(); /* since edges select lines, we give dots advantage of ~20 pix */ const float dist_margin = (dist_init / 2); @@ -988,9 +987,6 @@ static bool unified_findnearest(ViewContext *vc, } } - mval_prev[0] = vc->mval[0]; - mval_prev[1] = vc->mval[1]; - /* Only one element type will be non-null. */ BLI_assert(((hit.v.ele != NULL) + (hit.e.ele != NULL) + (hit.f.ele != NULL)) <= 1); diff --git a/source/blender/editors/mesh/editmesh_utils.c b/source/blender/editors/mesh/editmesh_utils.c index c6a8e771362..3ba8f601937 100644 --- a/source/blender/editors/mesh/editmesh_utils.c +++ b/source/blender/editors/mesh/editmesh_utils.c @@ -166,7 +166,7 @@ bool EDBM_op_finish(BMEditMesh *em, BMOperator *bmop, wmOperator *op, const bool eBMOpErrorLevel level; while (BMO_error_pop(em->bm, &errmsg, NULL, &level)) { - ReportType type = RPT_INFO; + eReportType type = RPT_INFO; switch (level) { case BMO_ERROR_CANCEL: { changed_was_set = true; @@ -1701,8 +1701,8 @@ void EDBM_project_snap_verts( ED_view3d_init_mats_rv3d(obedit, region->regiondata); - struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - CTX_data_scene(C), 0, region, CTX_wm_view3d(C)); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create( + CTX_data_scene(C), 0); BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) { @@ -1711,6 +1711,8 @@ void EDBM_project_snap_verts( V3D_PROJ_RET_OK) { if (ED_transform_snap_object_project_view3d(snap_context, depsgraph, + region, + CTX_wm_view3d(C), SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_NOT_ACTIVE, diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 03c99e40d1e..abff3c70e67 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -150,7 +150,8 @@ void MESH_OT_face_split_by_edges(struct wmOperatorType *ot); /* *** editmesh_knife.c *** */ void MESH_OT_knife_tool(struct wmOperatorType *ot); void MESH_OT_knife_project(struct wmOperatorType *ot); -void EDBM_mesh_knife(struct ViewContext *vc, +void EDBM_mesh_knife(struct bContext *C, + struct ViewContext *vc, struct LinkNode *polys, bool use_tag, bool cut_through); diff --git a/source/blender/editors/object/object_add.c b/source/blender/editors/object/object_add.c index beadbf2689e..8b5894923ad 100644 --- a/source/blender/editors/object/object_add.c +++ b/source/blender/editors/object/object_add.c @@ -65,7 +65,6 @@ #include "BKE_displist.h" #include "BKE_duplilist.h" #include "BKE_effect.h" -#include "BKE_font.h" #include "BKE_geometry_set.h" #include "BKE_gpencil_curve.h" #include "BKE_gpencil_geom.h" @@ -75,6 +74,7 @@ #include "BKE_lattice.h" #include "BKE_layer.h" #include "BKE_lib_id.h" +#include "BKE_lib_override.h" #include "BKE_lib_query.h" #include "BKE_lib_remap.h" #include "BKE_light.h" @@ -91,6 +91,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_speaker.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "DEG_depsgraph.h" @@ -258,8 +259,8 @@ static bool object_add_drop_xy_get(bContext *C, wmOperator *op, int (*r_mval)[2] static int object_add_drop_xy_generic_invoke(bContext *C, wmOperator *op, const wmEvent *event) { if (!object_add_drop_xy_is_set(op)) { - RNA_int_set(op->ptr, "drop_x", event->x); - RNA_int_set(op->ptr, "drop_y", event->y); + RNA_int_set(op->ptr, "drop_x", event->xy[0]); + RNA_int_set(op->ptr, "drop_y", event->xy[1]); } return op->type->exec(C, op); } @@ -1322,6 +1323,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) const bool use_in_front = RNA_boolean_get(op->ptr, "use_in_front"); const bool use_lights = RNA_boolean_get(op->ptr, "use_lights"); const int stroke_depth_order = RNA_enum_get(op->ptr, "stroke_depth_order"); + const float stroke_depth_offset = RNA_float_get(op->ptr, "stroke_depth_offset"); ushort local_view_bits; float loc[3], rot[3]; @@ -1453,6 +1455,7 @@ static int object_gpencil_add_exec(bContext *C, wmOperator *op) if (stroke_depth_order == GP_DRAWMODE_3D) { gpd->draw_mode = GP_DRAWMODE_3D; } + md->stroke_depth_offset = stroke_depth_offset; } break; @@ -1486,13 +1489,14 @@ static void object_add_ui(bContext *UNUSED(C), wmOperator *op) uiItemR(layout, op->ptr, "type", 0, NULL, ICON_NONE); int type = RNA_enum_get(op->ptr, "type"); - if (type == GP_LRT_COLLECTION || type == GP_LRT_OBJECT || type == GP_LRT_SCENE) { + if (ELEM(type, GP_LRT_COLLECTION, GP_LRT_OBJECT, GP_LRT_SCENE)) { uiItemR(layout, op->ptr, "use_lights", 0, NULL, ICON_NONE); uiItemR(layout, op->ptr, "use_in_front", 0, NULL, ICON_NONE); bool in_front = RNA_boolean_get(op->ptr, "use_in_front"); - uiLayout *row = uiLayoutRow(layout, false); - uiLayoutSetActive(row, !in_front); - uiItemR(row, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !in_front); + uiItemR(col, op->ptr, "stroke_depth_offset", 0, NULL, ICON_NONE); + uiItemR(col, op->ptr, "stroke_depth_order", 0, NULL, ICON_NONE); } } @@ -1531,9 +1535,18 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) ot->prop = RNA_def_enum(ot->srna, "type", rna_enum_object_gpencil_type_items, 0, "Type", ""); RNA_def_boolean(ot->srna, "use_in_front", - false, - "In Front", + true, + "Show In Front", "Show line art grease pencil in front of everything"); + RNA_def_float(ot->srna, + "stroke_depth_offset", + 0.05f, + 0.0f, + FLT_MAX, + "Stroke Offset", + "Stroke offset for the line art modifier", + 0.0f, + 0.5f); RNA_def_boolean( ot->srna, "use_lights", false, "Use Lights", "Use lights for this grease pencil object"); RNA_def_enum( @@ -1542,7 +1555,7 @@ void OBJECT_OT_gpencil_add(wmOperatorType *ot) rna_enum_gpencil_add_stroke_depth_order_items, GP_DRAWMODE_3D, "Stroke Depth Order", - "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front'"); + "Defines how the strokes are ordered in 3D space for objects not displayed 'In Front')"); } /** \} */ @@ -1690,8 +1703,8 @@ static int collection_instance_add_exec(bContext *C, wmOperator *op) static int object_instance_add_invoke(bContext *C, wmOperator *op, const wmEvent *event) { if (!object_add_drop_xy_is_set(op)) { - RNA_int_set(op->ptr, "drop_x", event->x); - RNA_int_set(op->ptr, "drop_y", event->y); + RNA_int_set(op->ptr, "drop_x", event->xy[0]); + RNA_int_set(op->ptr, "drop_y", event->xy[1]); } if (!RNA_struct_property_is_set(op->ptr, "name")) { @@ -1996,6 +2009,7 @@ static int object_delete_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); const bool use_global = RNA_boolean_get(op->ptr, "use_global"); + const bool confirm = op->flag & OP_IS_INVOKE; uint changed_count = 0; uint tagged_count = 0; @@ -2015,6 +2029,15 @@ static int object_delete_exec(bContext *C, wmOperator *op) continue; } + if (!BKE_lib_override_library_id_is_user_deletable(bmain, &ob->id)) { + /* Can this case ever happen? */ + BKE_reportf(op->reports, + RPT_WARNING, + "Cannot delete object '%s' as it used by override collections", + ob->id.name + 2); + continue; + } + if (ID_REAL_USERS(ob) <= 1 && ID_EXTRA_USERS(ob) == 0 && BKE_library_ID_is_indirectly_used(bmain, ob)) { BKE_reportf(op->reports, @@ -2065,7 +2088,9 @@ static int object_delete_exec(bContext *C, wmOperator *op) BKE_id_multi_tagged_delete(bmain); } - BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", (changed_count + tagged_count)); + if (confirm) { + BKE_reportf(op->reports, RPT_INFO, "Deleted %u object(s)", (changed_count + tagged_count)); + } /* delete has to handle all open scenes */ BKE_main_id_tag_listbase(&bmain->scenes, LIB_TAG_DOIT, true); @@ -2120,7 +2145,7 @@ static void copy_object_set_idnew(bContext *C) Main *bmain = CTX_data_main(C); CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - BKE_libblock_relink_to_newid(&ob->id); + BKE_libblock_relink_to_newid(bmain, &ob->id, 0); } CTX_DATA_END; @@ -2353,7 +2378,7 @@ static void make_object_duplilist_real(bContext *C, Object *ob_dst = BLI_ghash_lookup(dupli_gh, dob); /* Remap new object to itself, and clear again newid pointer of orig object. */ - BKE_libblock_relink_to_newid(&ob_dst->id); + BKE_libblock_relink_to_newid(bmain, &ob_dst->id, 0); DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY); @@ -2828,8 +2853,7 @@ static int object_convert_exec(bContext *C, wmOperator *op) /* Remove unused materials. */ int actcol = ob_gpencil->actcol; for (int slot = 1; slot <= ob_gpencil->totcol; slot++) { - while (slot <= ob_gpencil->totcol && - !BKE_object_material_slot_used(ob_gpencil->data, slot)) { + while (slot <= ob_gpencil->totcol && !BKE_object_material_slot_used(ob_gpencil, slot)) { ob_gpencil->actcol = slot; BKE_object_material_slot_remove(CTX_data_main(C), ob_gpencil); @@ -3350,8 +3374,11 @@ Base *ED_object_add_duplicate( ob = basen->object; - /* link own references to the newly duplicated data T26816. */ - BKE_libblock_relink_to_newid(&ob->id); + /* Link own references to the newly duplicated data T26816. + * Note that this function can be called from edit-mode code, in which case we may have to + * enforce remapping obdata (by default this is forbidden in edit mode). */ + const int remap_flag = BKE_object_is_in_editmode(ob) ? ID_REMAP_FORCE_OBDATA_IN_EDITMODE : 0; + BKE_libblock_relink_to_newid(bmain, &ob->id, remap_flag); /* DAG_relations_tag_update(bmain); */ /* caller must do */ @@ -3457,19 +3484,6 @@ void OBJECT_OT_duplicate(wmOperatorType *ot) * Use for drag & drop. * \{ */ -static Base *object_add_ensure_in_view_layer(Main *bmain, ViewLayer *view_layer, Object *ob) -{ - Base *base = BKE_view_layer_base_find(view_layer, ob); - - if (!base) { - LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); - base = BKE_view_layer_base_find(view_layer, ob); - } - - return base; -} - static int object_add_named_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -3477,8 +3491,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op) ViewLayer *view_layer = CTX_data_view_layer(C); Base *basen; Object *ob; - const bool duplicate = RNA_boolean_get(op->ptr, "duplicate"); - const bool linked = duplicate && RNA_boolean_get(op->ptr, "linked"); + const bool linked = RNA_boolean_get(op->ptr, "linked"); const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag; char name[MAX_ID_NAME - 2]; @@ -3492,41 +3505,26 @@ static int object_add_named_exec(bContext *C, wmOperator *op) } /* prepare dupli */ - if (duplicate) { - basen = object_add_duplicate_internal( - bmain, - scene, - view_layer, - ob, - dupflag, - /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this - * function will only work if the object is already linked in the view layer, which is not - * the case here. So we have to do the new-ID relinking ourselves - * (#copy_object_set_idnew()). - */ - LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); - } - else { - /* basen is actually not a new base in this case. */ - basen = object_add_ensure_in_view_layer(bmain, view_layer, ob); - } + basen = object_add_duplicate_internal( + bmain, + scene, + view_layer, + ob, + dupflag, + /* Sub-process flag because the new-ID remapping (#BKE_libblock_relink_to_newid()) in this + * function will only work if the object is already linked in the view layer, which is not + * the case here. So we have to do the new-ID relinking ourselves + * (#copy_object_set_idnew()). + */ + LIB_ID_DUPLICATE_IS_SUBPROCESS | LIB_ID_DUPLICATE_IS_ROOT_ID); if (basen == NULL) { - BKE_report(op->reports, - RPT_ERROR, - duplicate ? "Object could not be duplicated" : - "Object could not be linked to the view layer"); + BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated"); return OPERATOR_CANCELLED; } basen->object->visibility_flag &= ~OB_HIDE_VIEWPORT; - int mval[2]; - if (object_add_drop_xy_get(C, op, &mval)) { - ED_object_location_from_view(C, basen->object->loc); - ED_view3d_cursor3d_position(C, mval, false, basen->object->loc); - } - /* object_add_duplicate_internal() doesn't deselect other objects, unlike object_add_common() or * BKE_view_layer_base_deselect_all(). */ ED_object_base_deselect_all(view_layer, NULL, SEL_DESELECT); @@ -3544,43 +3542,163 @@ static int object_add_named_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); ED_outliner_select_sync_from_object_tag(C); + PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); + if (RNA_property_is_set(op->ptr, prop_matrix)) { + Object *ob_add = basen->object; + RNA_property_float_get_array(op->ptr, prop_matrix, &ob_add->obmat[0][0]); + BKE_object_apply_mat4(ob_add, ob_add->obmat, true, true); + + DEG_id_tag_update(&ob_add->id, ID_RECALC_TRANSFORM); + } + else { + int mval[2]; + if (object_add_drop_xy_get(C, op, &mval)) { + ED_object_location_from_view(C, basen->object->loc); + ED_view3d_cursor3d_position(C, mval, false, basen->object->loc); + } + } + return OPERATOR_FINISHED; } void OBJECT_OT_add_named(wmOperatorType *ot) { /* identifiers */ - ot->name = "Add Named Object"; + ot->name = "Add Object"; ot->description = "Add named object"; ot->idname = "OBJECT_OT_add_named"; /* api callbacks */ ot->invoke = object_add_drop_xy_generic_invoke; ot->exec = object_add_named_exec; + ot->poll = ED_operator_objectmode_poll_msg; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + PropertyRNA *prop; + RNA_def_boolean(ot->srna, + "linked", + false, + "Linked", + "Duplicate object but not object data, linking to the original data"); + + RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); + + prop = RNA_def_float_matrix( + ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + + object_add_drop_xy_props(ot); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Object to Mouse Operator + * \{ */ + +/** + * Alternate behavior for dropping an asset that positions the appended object(s). + */ +static int object_transform_to_mouse_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob; + + if (RNA_struct_property_is_set(op->ptr, "name")) { + char name[MAX_ID_NAME - 2]; + RNA_string_get(op->ptr, "name", name); + ob = (Object *)BKE_libblock_find_name(bmain, ID_OB, name); + } + else { + ob = OBACT(view_layer); + } + + if (ob == NULL) { + BKE_report(op->reports, RPT_ERROR, "Object not found"); + return OPERATOR_CANCELLED; + } + + /* Don't transform a linked object. There's just nothing to do here in this case, so return + * #OPERATOR_FINISHED. */ + if (ID_IS_LINKED(ob)) { + return OPERATOR_FINISHED; + } + + /* Ensure the locations are updated so snap reads the evaluated active location. */ + CTX_data_ensure_evaluated_depsgraph(C); + + PropertyRNA *prop_matrix = RNA_struct_find_property(op->ptr, "matrix"); + if (RNA_property_is_set(op->ptr, prop_matrix)) { + uint objects_len; + Object **objects = BKE_view_layer_array_selected_objects(view_layer, NULL, &objects_len, {0}); + + float matrix[4][4]; + RNA_property_float_get_array(op->ptr, prop_matrix, &matrix[0][0]); + + float mat_src_unit[4][4]; + float mat_dst_unit[4][4]; + float final_delta[4][4]; + + normalize_m4_m4(mat_src_unit, ob->obmat); + normalize_m4_m4(mat_dst_unit, matrix); + invert_m4(mat_src_unit); + mul_m4_m4m4(final_delta, mat_dst_unit, mat_src_unit); + + ED_object_xform_array_m4(objects, objects_len, final_delta); + + MEM_freeN(objects); + } + else { + int mval[2]; + if (object_add_drop_xy_get(C, op, &mval)) { + float cursor[3]; + ED_object_location_from_view(C, cursor); + ED_view3d_cursor3d_position(C, mval, false, cursor); + + /* Use the active objects location since this is the ID which the user selected to drop. + * + * This transforms all selected objects, so that dropping a single object which links in + * other objects will have their relative transformation preserved. + * For example a child/parent relationship or other objects used with a boolean modifier. + * + * The caller is responsible for ensuring the selection state gives useful results. + * Link/append does this using #FILE_AUTOSELECT. */ + ED_view3d_snap_selected_to_location(C, cursor, V3D_AROUND_ACTIVE); + } + } + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_transform_to_mouse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Place Object Under Mouse"; + ot->description = "Snap selected item(s) to the mouse location"; + ot->idname = "OBJECT_OT_transform_to_mouse"; + + /* api callbacks */ + ot->invoke = object_add_drop_xy_generic_invoke; + ot->exec = object_transform_to_mouse_exec; ot->poll = ED_operator_objectmode; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; PropertyRNA *prop; + RNA_def_string(ot->srna, + "name", + NULL, + MAX_ID_NAME - 2, + "Name", + "Object name to place (when unset use the active object)"); - prop = RNA_def_boolean( - ot->srna, - "duplicate", - true, - "Duplicate", - "Create a duplicate of the object. If not set, only ensures the object is linked into the " - "active view layer, positions and selects/activates it (deselecting others)"); - RNA_def_property_flag(prop, PROP_HIDDEN); - - RNA_def_boolean(ot->srna, - "linked", - false, - "Linked", - "Duplicate object but not object data, linking to the original data (ignored if " - "'duplicate' is false)"); - - RNA_def_string(ot->srna, "name", NULL, MAX_ID_NAME - 2, "Name", "Object name to add"); + prop = RNA_def_float_matrix( + ot->srna, "matrix", 4, 4, NULL, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); object_add_drop_xy_props(ot); } diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 3a10a423e91..1b6b0c78037 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -198,7 +198,7 @@ static bool multiresbake_check(bContext *C, wmOperator *op) ok = false; } - if (ibuf->rect_float && !(ibuf->channels == 0 || ibuf->channels == 4)) { + if (ibuf->rect_float && !(ELEM(ibuf->channels, 0, 4))) { ok = false; } diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index adb6497aed6..700a4094f01 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -314,11 +314,8 @@ static void bake_targets_refresh(BakeTargets *targets) if (ima) { LISTBASE_FOREACH (ImageTile *, tile, &ima->tiles) { - if (tile->ok == IMA_OK_LOADED) { - BKE_image_free_gputextures(ima); - DEG_id_tag_update(&ima->id, 0); - break; - } + BKE_image_free_gputextures(ima); + DEG_id_tag_update(&ima->id, 0); } } } @@ -412,6 +409,7 @@ static bool is_noncolor_pass(eScenePassType pass_type) { return ELEM(pass_type, SCE_PASS_Z, + SCE_PASS_POSITION, SCE_PASS_NORMAL, SCE_PASS_VECTOR, SCE_PASS_INDEXOB, @@ -554,19 +552,10 @@ static bool bake_pass_filter_check(eScenePassType pass_type, return true; } - if ((pass_filter & R_BAKE_PASS_FILTER_AO) != 0) { - BKE_report( - reports, - RPT_ERROR, - "Combined bake pass Ambient Occlusion contribution requires an enabled light pass " - "(bake the Ambient Occlusion pass type instead)"); - } - else { - BKE_report(reports, - RPT_ERROR, - "Combined bake pass requires Emit, or a light pass with " - "Direct or Indirect contributions enabled"); - } + BKE_report(reports, + RPT_ERROR, + "Combined bake pass requires Emit, or a light pass with " + "Direct or Indirect contributions enabled"); return false; } @@ -705,7 +694,7 @@ static bool bake_targets_init_image_textures(const BakeAPIRender *bkr, } } - /* Overallocate in case there is more materials than images. */ + /* Over-allocate in case there is more materials than images. */ targets->num_materials = num_materials; targets->images = MEM_callocN(sizeof(BakeImage) * targets->num_materials, "BakeTargets.images"); targets->material_to_image = MEM_callocN(sizeof(int) * targets->num_materials, @@ -1540,22 +1529,22 @@ static int bake(const BakeAPIRender *bkr, if (md) { mode = md->mode; md->mode &= ~eModifierMode_Render; - } - /* Evaluate modifiers again. */ - me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false, false); - bake_targets_populate_pixels(bkr, &targets, ob_low, me_nores, pixel_array_low); + /* Evaluate modifiers again. */ + me_nores = BKE_mesh_new_from_object(NULL, ob_low_eval, false, false); + bake_targets_populate_pixels(bkr, &targets, ob_low, me_nores, pixel_array_low); + } RE_bake_normal_world_to_tangent(pixel_array_low, targets.num_pixels, targets.num_channels, targets.result, - me_nores, + (me_nores) ? me_nores : me_low_eval, bkr->normal_swizzle, ob_low_eval->obmat); - BKE_id_free(NULL, &me_nores->id); if (md) { + BKE_id_free(NULL, &me_nores->id); md->mode = mode; } } diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index 5697c2c973d..78440f52160 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1125,12 +1125,51 @@ static eAnimvizCalcRange object_path_convert_range(eObjectPathCalcRange range) return ANIMVIZ_CALC_RANGE_FULL; } +void ED_objects_recalculate_paths_selected(bContext *C, Scene *scene, eObjectPathCalcRange range) +{ + ListBase selected_objects = {NULL, NULL}; + CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + BLI_addtail(&selected_objects, BLI_genericNodeN(ob)); + } + CTX_DATA_END; + + ED_objects_recalculate_paths(C, scene, range, &selected_objects); + + BLI_freelistN(&selected_objects); +} + +void ED_objects_recalculate_paths_visible(bContext *C, Scene *scene, eObjectPathCalcRange range) +{ + ListBase visible_objects = {NULL, NULL}; + CTX_DATA_BEGIN (C, Object *, ob, visible_objects) { + BLI_addtail(&visible_objects, BLI_genericNodeN(ob)); + } + CTX_DATA_END; + + ED_objects_recalculate_paths(C, scene, range, &visible_objects); + + BLI_freelistN(&visible_objects); +} + +static bool has_object_motion_paths(Object *ob) +{ + return (ob->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; +} + +static bool has_pose_motion_paths(Object *ob) +{ + return ob->pose && (ob->pose->avs.path_bakeflag & MOTIONPATH_BAKE_HAS_PATHS) != 0; +} + /* For the objects with animation: update paths for those that have got them * This should selectively update paths that exist... * * To be called from various tools that do incremental updates */ -void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRange range) +void ED_objects_recalculate_paths(bContext *C, + Scene *scene, + eObjectPathCalcRange range, + ListBase *ld_objects) { /* Transform doesn't always have context available to do update. */ if (C == NULL) { @@ -1141,13 +1180,20 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang ViewLayer *view_layer = CTX_data_view_layer(C); ListBase targets = {NULL, NULL}; - /* loop over objects in scene */ - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { + LISTBASE_FOREACH (LinkData *, link, ld_objects) { + Object *ob = link->data; + /* set flag to force recalc, then grab path(s) from object */ - ob->avs.recalc |= ANIMVIZ_RECALC_PATHS; + if (has_object_motion_paths(ob)) { + ob->avs.recalc |= ANIMVIZ_RECALC_PATHS; + } + + if (has_pose_motion_paths(ob)) { + ob->pose->avs.recalc |= ANIMVIZ_RECALC_PATHS; + } + animviz_get_object_motionpaths(ob, &targets); } - CTX_DATA_END; Depsgraph *depsgraph; bool free_depsgraph = false; @@ -1172,12 +1218,13 @@ void ED_objects_recalculate_paths(bContext *C, Scene *scene, eObjectPathCalcRang if (range != OBJECT_PATH_CALC_RANGE_CURRENT_FRAME) { /* Tag objects for copy on write - so paths will draw/redraw * For currently frame only we update evaluated object directly. */ - CTX_DATA_BEGIN (C, Object *, ob, selected_editable_objects) { - if (ob->mpath) { + LISTBASE_FOREACH (LinkData *, link, ld_objects) { + Object *ob = link->data; + + if (has_object_motion_paths(ob) || has_pose_motion_paths(ob)) { DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } } - CTX_DATA_END; } /* Free temporary depsgraph. */ @@ -1229,10 +1276,10 @@ static int object_calculate_paths_exec(bContext *C, wmOperator *op) CTX_DATA_END; /* calculate the paths for objects that have them (and are tagged to get refreshed) */ - ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL); + ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; } @@ -1298,10 +1345,10 @@ static int object_update_paths_exec(bContext *C, wmOperator *UNUSED(op)) } /* calculate the paths for objects that have them (and are tagged to get refreshed) */ - ED_objects_recalculate_paths(C, scene, OBJECT_PATH_CALC_RANGE_FULL); + ED_objects_recalculate_paths_selected(C, scene, OBJECT_PATH_CALC_RANGE_FULL); /* notifiers for updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); + WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM | ND_POSE, NULL); return OPERATOR_FINISHED; } @@ -1311,7 +1358,7 @@ void OBJECT_OT_paths_update(wmOperatorType *ot) /* identifiers */ ot->name = "Update Object Paths"; ot->idname = "OBJECT_OT_paths_update"; - ot->description = "Recalculate paths for selected objects"; + ot->description = "Recalculate motion paths for selected objects"; /* api callbacks */ ot->exec = object_update_paths_exec; @@ -1323,6 +1370,47 @@ void OBJECT_OT_paths_update(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Update All Motion Paths Operator + * \{ */ + +static bool object_update_all_paths_poll(bContext *UNUSED(C)) +{ + return true; +} + +static int object_update_all_paths_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + + if (scene == NULL) { + return OPERATOR_CANCELLED; + } + + ED_objects_recalculate_paths_visible(C, scene, OBJECT_PATH_CALC_RANGE_FULL); + + WM_event_add_notifier(C, NC_OBJECT | ND_POSE | ND_TRANSFORM, NULL); + + return OPERATOR_FINISHED; +} + +void OBJECT_OT_paths_update_visible(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update All Object Paths"; + ot->idname = "OBJECT_OT_paths_update_visible"; + ot->description = "Recalculate all visible motion paths for objects and poses"; + + /* api callbacks */ + ot->exec = object_update_all_paths_exec; + ot->poll = object_update_all_paths_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Clear Motion Paths Operator * \{ */ @@ -1739,7 +1827,7 @@ void OBJECT_OT_mode_set_with_submode(wmOperatorType *ot) OBJECT_OT_mode_set(ot); /* identifiers */ - ot->name = "Set Object Mode with Submode"; + ot->name = "Set Object Mode with Sub-mode"; ot->idname = "OBJECT_OT_mode_set_with_submode"; /* properties */ diff --git a/source/blender/editors/object/object_intern.h b/source/blender/editors/object/object_intern.h index 50dd9322c5c..fe07ecef438 100644 --- a/source/blender/editors/object/object_intern.h +++ b/source/blender/editors/object/object_intern.h @@ -78,13 +78,13 @@ void OBJECT_OT_mode_set(struct wmOperatorType *ot); void OBJECT_OT_mode_set_with_submode(struct wmOperatorType *ot); void OBJECT_OT_editmode_toggle(struct wmOperatorType *ot); void OBJECT_OT_posemode_toggle(struct wmOperatorType *ot); -void OBJECT_OT_proxy_make(struct wmOperatorType *ot); void OBJECT_OT_shade_smooth(struct wmOperatorType *ot); void OBJECT_OT_shade_flat(struct wmOperatorType *ot); void OBJECT_OT_paths_calculate(struct wmOperatorType *ot); void OBJECT_OT_paths_update(struct wmOperatorType *ot); void OBJECT_OT_paths_clear(struct wmOperatorType *ot); void OBJECT_OT_paths_range_update(struct wmOperatorType *ot); +void OBJECT_OT_paths_update_visible(struct wmOperatorType *ot); void OBJECT_OT_forcefield_toggle(struct wmOperatorType *ot); void OBJECT_OT_move_to_collection(struct wmOperatorType *ot); @@ -106,6 +106,7 @@ void OBJECT_OT_select_same_collection(struct wmOperatorType *ot); /* object_add.c */ void OBJECT_OT_add(struct wmOperatorType *ot); void OBJECT_OT_add_named(struct wmOperatorType *ot); +void OBJECT_OT_transform_to_mouse(struct wmOperatorType *ot); void OBJECT_OT_metaball_add(struct wmOperatorType *ot); void OBJECT_OT_text_add(struct wmOperatorType *ot); void OBJECT_OT_armature_add(struct wmOperatorType *ot); diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index 2c58ef02486..0c1b394a916 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -497,51 +497,8 @@ static bool object_transfer_mode_to_base(bContext *C, wmOperator *op, Base *base return mode_transfered; } -static int object_transfer_mode_modal(bContext *C, wmOperator *op, const wmEvent *event) -{ - switch (event->type) { - case LEFTMOUSE: - if (event->val == KM_PRESS) { - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - - /* This ensures that the click was done in an viewport region. */ - bScreen *screen = CTX_wm_screen(C); - ARegion *region = BKE_screen_find_main_region_at_xy( - screen, SPACE_VIEW3D, event->x, event->y); - if (!region) { - return OPERATOR_CANCELLED; - } - - const int mval[2] = {event->x - region->winrct.xmin, event->y - region->winrct.ymin}; - Base *base_dst = ED_view3d_give_base_under_cursor(C, mval); - const bool mode_transfered = object_transfer_mode_to_base(C, op, base_dst); - if (!mode_transfered) { - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; - } - break; - case RIGHTMOUSE: { - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - return OPERATOR_CANCELLED; - } - } - return OPERATOR_RUNNING_MODAL; -} - static int object_transfer_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - const bool use_eyedropper = RNA_boolean_get(op->ptr, "use_eyedropper"); - if (use_eyedropper) { - ED_workspace_status_text(C, TIP_("Click in the viewport to select an object")); - WM_cursor_modal_set(CTX_wm_window(C), WM_CURSOR_EYEDROPPER); - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; - } - Object *ob_src = CTX_data_active_object(C); const eObjectMode src_mode = (eObjectMode)ob_src->mode; @@ -569,17 +526,12 @@ void OBJECT_OT_transfer_mode(wmOperatorType *ot) /* api callbacks */ ot->invoke = object_transfer_mode_invoke; - ot->modal = object_transfer_mode_modal; ot->poll = object_transfer_mode_poll; /* Undo push is handled by the operator. */ - ot->flag = OPTYPE_REGISTER; + ot->flag = OPTYPE_REGISTER | OPTYPE_DEPENDS_ON_CURSOR; - RNA_def_boolean(ot->srna, - "use_eyedropper", - false, - "Use Eyedropper", - "Pick the object to switch to using an eyedropper"); + ot->cursor_pending = WM_CURSOR_EYEDROPPER; RNA_def_boolean(ot->srna, "use_flash_on_transfer", diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index b9942bc563a..efe19785f31 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -1044,6 +1044,10 @@ bool edit_modifier_poll_generic(bContext *C, Object *ob = (ptr.owner_id) ? (Object *)ptr.owner_id : ED_object_active_context(C); ModifierData *mod = ptr.data; /* May be NULL. */ + if (mod == NULL && ob != NULL) { + mod = BKE_object_active_modifier(ob); + } + if (!ob || ID_IS_LINKED(ob)) { return false; } diff --git a/source/blender/editors/object/object_ops.c b/source/blender/editors/object/object_ops.c index aa9ae082317..b171da42522 100644 --- a/source/blender/editors/object/object_ops.c +++ b/source/blender/editors/object/object_ops.c @@ -56,13 +56,13 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_mode_set_with_submode); WM_operatortype_append(OBJECT_OT_editmode_toggle); WM_operatortype_append(OBJECT_OT_posemode_toggle); - WM_operatortype_append(OBJECT_OT_proxy_make); WM_operatortype_append(OBJECT_OT_shade_smooth); WM_operatortype_append(OBJECT_OT_shade_flat); WM_operatortype_append(OBJECT_OT_paths_calculate); WM_operatortype_append(OBJECT_OT_paths_update); WM_operatortype_append(OBJECT_OT_paths_clear); WM_operatortype_append(OBJECT_OT_paths_range_update); + WM_operatortype_append(OBJECT_OT_paths_update_visible); WM_operatortype_append(OBJECT_OT_forcefield_toggle); WM_operatortype_append(OBJECT_OT_transfer_mode); @@ -112,6 +112,7 @@ void ED_operatortypes_object(void) WM_operatortype_append(OBJECT_OT_volume_import); WM_operatortype_append(OBJECT_OT_add); WM_operatortype_append(OBJECT_OT_add_named); + WM_operatortype_append(OBJECT_OT_transform_to_mouse); WM_operatortype_append(OBJECT_OT_effector_add); WM_operatortype_append(OBJECT_OT_collection_instance_add); WM_operatortype_append(OBJECT_OT_data_instance_add); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 75269dffec8..b51644eebf3 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -330,167 +330,6 @@ void OBJECT_OT_vertex_parent_set(wmOperatorType *ot) /** \} */ -/* ------------------------------------------------------------------- */ -/** \name Make Proxy Operator - * \{ */ - -/* set the object to proxify */ -static int make_proxy_invoke(bContext *C, wmOperator *op, const wmEvent *event) -{ - Scene *scene = CTX_data_scene(C); - Object *ob = ED_object_active_context(C); - - /* sanity checks */ - if (!scene || ID_IS_LINKED(scene) || !ob) { - return OPERATOR_CANCELLED; - } - - /* Get object to work on - use a menu if we need to... */ - if (ob->instance_collection && ID_IS_LINKED(ob->instance_collection)) { - /* gives menu with list of objects in group */ - /* proxy_group_objects_menu(C, op, ob, ob->instance_collection); */ - WM_enum_search_invoke(C, op, event); - return OPERATOR_CANCELLED; - } - if (ID_IS_LINKED(ob)) { - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("OK?"), ICON_QUESTION); - uiLayout *layout = UI_popup_menu_layout(pup); - - /* create operator menu item with relevant properties filled in */ - PointerRNA opptr_dummy; - uiItemFullO_ptr( - layout, op->type, op->type->name, ICON_NONE, NULL, WM_OP_EXEC_REGION_WIN, 0, &opptr_dummy); - - /* present the menu and be done... */ - UI_popup_menu_end(C, pup); - - /* this invoke just calls another instance of this operator... */ - return OPERATOR_INTERFACE; - } - - /* error.. cannot continue */ - BKE_report(op->reports, RPT_ERROR, "Can only make proxy for a referenced object or collection"); - return OPERATOR_CANCELLED; -} - -static int make_proxy_exec(bContext *C, wmOperator *op) -{ - Main *bmain = CTX_data_main(C); - Object *ob, *gob = ED_object_active_context(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - - if (gob->instance_collection != NULL) { - const ListBase instance_collection_objects = BKE_collection_object_cache_get( - gob->instance_collection); - Base *base = BLI_findlink(&instance_collection_objects, RNA_enum_get(op->ptr, "object")); - ob = base->object; - } - else { - ob = gob; - gob = NULL; - } - - if (ob) { - Object *newob; - char name[MAX_ID_NAME + 4]; - - BLI_snprintf(name, sizeof(name), "%s_proxy", ((ID *)(gob ? gob : ob))->name + 2); - - /* Add new object for the proxy */ - newob = BKE_object_add_from(bmain, scene, view_layer, OB_EMPTY, name, gob ? gob : ob); - - /* set layers OK */ - BKE_object_make_proxy(bmain, newob, ob, gob); - - /* Set back pointer immediately so dependency graph knows that this is - * is a proxy and will act accordingly. Otherwise correctness of graph - * will depend on order of bases. - * - * TODO(sergey): We really need to get rid of this bi-directional links - * in proxies with something like library overrides. - */ - if (newob->proxy != NULL) { - newob->proxy->proxy_from = newob; - } - else { - BKE_report(op->reports, RPT_ERROR, "Unable to assign proxy"); - } - - /* depsgraph flushes are needed for the new data */ - DEG_relations_tag_update(bmain); - DEG_id_tag_update(&newob->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_ANIMATION); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, newob); - } - else { - BKE_report(op->reports, RPT_ERROR, "No object to make proxy for"); - return OPERATOR_CANCELLED; - } - - return OPERATOR_FINISHED; -} - -/* Generic itemf's for operators that take library args */ -static const EnumPropertyItem *proxy_collection_object_itemf(bContext *C, - PointerRNA *UNUSED(ptr), - PropertyRNA *UNUSED(prop), - bool *r_free) -{ - EnumPropertyItem item_tmp = {0}, *item = NULL; - int totitem = 0; - int i = 0; - Object *ob = ED_object_active_context(C); - - if (!ob || !ob->instance_collection) { - return DummyRNA_DEFAULT_items; - } - - /* find the object to affect */ - FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (ob->instance_collection, object) { - item_tmp.identifier = item_tmp.name = object->id.name + 2; - item_tmp.value = i++; - RNA_enum_item_add(&item, &totitem, &item_tmp); - } - FOREACH_COLLECTION_OBJECT_RECURSIVE_END; - - RNA_enum_item_end(&item, &totitem); - *r_free = true; - - return item; -} - -void OBJECT_OT_proxy_make(wmOperatorType *ot) -{ - PropertyRNA *prop; - - /* identifiers */ - ot->name = "Make Proxy"; - ot->idname = "OBJECT_OT_proxy_make"; - ot->description = "Add empty object to become local replacement data of a library-linked object"; - - /* callbacks */ - ot->invoke = make_proxy_invoke; - ot->exec = make_proxy_exec; - ot->poll = ED_operator_object_active; - - /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - - /* properties */ - /* XXX, relies on hard coded ID at the moment */ - prop = RNA_def_enum(ot->srna, - "object", - DummyRNA_DEFAULT_items, - 0, - "Proxy Object", - "Name of library-linked/collection object to make a proxy for"); - RNA_def_enum_funcs(prop, proxy_collection_object_itemf); - RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); - ot->prop = prop; -} - -/** \} */ - /* ------------------------------------------------------------------- */ /** \name Clear Parent Operator * \{ */ @@ -1838,18 +1677,28 @@ void OBJECT_OT_make_links_data(wmOperatorType *ot) /** \name Make Single User Operator * \{ */ -static void libblock_relink_collection(Collection *collection, const bool do_collection) +static bool single_data_needs_duplication(ID *id) +{ + /* NOTE: When dealing with linked data, we always make a local copy of it. + * While in theory we could rather make it local when it only has one user, this is difficult + * in practice with current code of this function. */ + return (id != NULL && (id->us > 1 || ID_IS_LINKED(id))); +} + +static void libblock_relink_collection(Main *bmain, + Collection *collection, + const bool do_collection) { if (do_collection) { - BKE_libblock_relink_to_newid(&collection->id); + BKE_libblock_relink_to_newid(bmain, &collection->id, 0); } for (CollectionObject *cob = collection->gobject.first; cob != NULL; cob = cob->next) { - BKE_libblock_relink_to_newid(&cob->ob->id); + BKE_libblock_relink_to_newid(bmain, &cob->ob->id, 0); } LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - libblock_relink_collection(child->collection, true); + libblock_relink_collection(bmain, child->collection, true); } } @@ -1863,7 +1712,8 @@ static Collection *single_object_users_collection(Main *bmain, /* Generate new copies for objects in given collection and all its children, * and optionally also copy collections themselves. */ if (copy_collections && !is_master_collection) { - Collection *collection_new = (Collection *)BKE_id_copy(bmain, &collection->id); + Collection *collection_new = (Collection *)BKE_id_copy_ex( + bmain, &collection->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS); id_us_min(&collection_new->id); collection = ID_NEW_SET(collection, collection_new); } @@ -1874,7 +1724,8 @@ static Collection *single_object_users_collection(Main *bmain, /* an object may be in more than one collection */ if ((ob->id.newid == NULL) && ((ob->flag & flag) == flag)) { if (!ID_IS_LINKED(ob) && BKE_object_scenes_users_get(bmain, ob) > 1) { - ID_NEW_SET(ob, BKE_id_copy(bmain, &ob->id)); + ID_NEW_SET( + ob, BKE_id_copy_ex(bmain, &ob->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); id_us_min(ob->id.newid); } } @@ -1917,10 +1768,10 @@ static void single_object_users( single_object_users_collection(bmain, scene, master_collection, flag, copy_collections, true); /* Will also handle the master collection. */ - BKE_libblock_relink_to_newid(&scene->id); + BKE_libblock_relink_to_newid(bmain, &scene->id, 0); /* Collection and object pointers in collections */ - libblock_relink_collection(scene->master_collection, false); + libblock_relink_collection(bmain, scene->master_collection, false); /* We also have to handle runtime things in UI. */ if (v3d) { @@ -1961,66 +1812,82 @@ static void single_obdata_users( FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) { if (!ID_IS_LINKED(ob)) { id = ob->data; - - if (id && id->us > 1 && !ID_IS_LINKED(id)) { + if (single_data_needs_duplication(id)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); switch (ob->type) { case OB_LAMP: - ob->data = la = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = la = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_CAMERA: - cam = ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + cam = ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); ID_NEW_REMAP(cam->dof.focus_object); break; case OB_MESH: /* Needed to remap texcomesh below. */ - me = ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); - if (me->key) { /* We do not need to set me->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)me->key); - } + me = ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_MBALL: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_CURVE: case OB_SURF: case OB_FONT: - ob->data = cu = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = cu = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); ID_NEW_REMAP(cu->bevobj); ID_NEW_REMAP(cu->taperobj); - if (cu->key) { /* We do not need to set cu->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)cu->key); - } break; case OB_LATTICE: - ob->data = lat = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); - if (lat->key) { /* We do not need to set lat->key->id.newid here... */ - BKE_animdata_copy_id_action(bmain, (ID *)lat->key); - } + ob->data = lat = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_ARMATURE: DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); BKE_pose_rebuild(bmain, ob, ob->data, true); break; case OB_SPEAKER: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_LIGHTPROBE: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_GPENCIL: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_HAIR: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_POINTCLOUD: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; case OB_VOLUME: - ob->data = ID_NEW_SET(ob->data, BKE_id_copy(bmain, ob->data)); + ob->data = ID_NEW_SET( + ob->data, + BKE_id_copy_ex(bmain, ob->data, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS)); break; default: printf("ERROR %s: can't copy %s\n", __func__, id->name); @@ -2031,13 +1898,6 @@ static void single_obdata_users( return; } - /* Copy animation data after object data became local, - * otherwise old and new object data will share the same - * AnimData structure, which is not what we want. - * (sergey) - */ - BKE_animdata_copy_id_action(bmain, (ID *)ob->data); - id_us_min(id); } } @@ -2056,8 +1916,16 @@ static void single_object_action_users( { FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) { if (!ID_IS_LINKED(ob)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_copy_id_action(bmain, &ob->id); + AnimData *adt = BKE_animdata_from_id(&ob->id); + if (adt == NULL) { + continue; + } + + ID *id_act = (ID *)adt->action; + if (single_data_needs_duplication(id_act)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID); + } } } FOREACH_OBJECT_FLAG_END; @@ -2070,10 +1938,14 @@ static void single_objectdata_action_users( if (!ID_IS_LINKED(ob) && ob->data != NULL) { ID *id_obdata = (ID *)ob->data; AnimData *adt = BKE_animdata_from_id(id_obdata); + if (adt == NULL) { + continue; + } + ID *id_act = (ID *)adt->action; - if (id_act && id_act->us > 1) { + if (single_data_needs_duplication(id_act)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - BKE_animdata_copy_id_action(bmain, id_obdata); + BKE_animdata_duplicate_id_action(bmain, &ob->id, USER_DUP_ACT | USER_DUP_LINKED_ID); } } } @@ -2089,18 +1961,12 @@ static void single_mat_users( FOREACH_OBJECT_FLAG_BEGIN (scene, view_layer, v3d, flag, ob) { if (!ID_IS_LINKED(ob)) { for (a = 1; a <= ob->totcol; a++) { - ma = BKE_object_material_get(ob, a); - if (ma) { - /* do not test for LIB_TAG_NEW or use newid: - * this functions guaranteed delivers single_users! */ - - if (ma->id.us > 1) { - man = (Material *)BKE_id_copy(bmain, &ma->id); - BKE_animdata_copy_id_action(bmain, &man->id); - - man->id.us = 0; - BKE_object_material_assign(bmain, ob, man, a, BKE_MAT_ASSIGN_USERPREF); - } + ma = BKE_object_material_get(ob, (short)a); + if (single_data_needs_duplication(&ma->id)) { + man = (Material *)BKE_id_copy_ex( + bmain, &ma->id, NULL, LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ACTIONS); + man->id.us = 0; + BKE_object_material_assign(bmain, ob, man, (short)a, BKE_MAT_ASSIGN_USERPREF); } } } @@ -2143,9 +2009,7 @@ static void tag_localizable_objects(bContext *C, const int mode) CTX_DATA_BEGIN (C, Object *, object, selected_objects) { object->id.tag |= LIB_TAG_DOIT; - /* If data is also gonna to become local, mark data we're interested in - * as gonna-to-be-local. - */ + /* If obdata is also going to become local, mark it as such too. */ if (mode == MAKE_LOCAL_SELECT_OBDATA && object->data) { ID *data_id = (ID *)object->data; data_id->tag |= LIB_TAG_DOIT; @@ -2727,10 +2591,10 @@ void OBJECT_OT_make_single_user(wmOperatorType *ot) char *ED_object_ot_drop_named_material_tooltip(bContext *C, PointerRNA *properties, - const wmEvent *event) + const int mval[2]) { int mat_slot = 0; - Object *ob = ED_view3d_give_material_slot_under_cursor(C, event->mval, &mat_slot); + Object *ob = ED_view3d_give_material_slot_under_cursor(C, mval, &mat_slot); if (ob == NULL) { return BLI_strdup(""); } @@ -2780,8 +2644,10 @@ static int drop_named_material_invoke(bContext *C, wmOperator *op, const wmEvent return OPERATOR_FINISHED; } -/* used for dropbox */ -/* assigns to object under cursor, only first material slot */ +/** + * Used for drop-box. + * Assigns to object under cursor, only first material slot. + */ void OBJECT_OT_drop_named_material(wmOperatorType *ot) { /* identifiers */ @@ -2790,7 +2656,7 @@ void OBJECT_OT_drop_named_material(wmOperatorType *ot) /* api callbacks */ ot->invoke = drop_named_material_invoke; - ot->poll = ED_operator_objectmode; + ot->poll = ED_operator_objectmode_poll_msg; /* flags */ ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index d56cb3c7548..3bdf7e0d34d 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -708,12 +708,19 @@ static bool mesh_is_manifold_consistent(Mesh *mesh) } if (is_manifold_consistent) { - /* check for wire edges */ for (uint i = 0; i < mesh->totedge; i++) { + /* Check for wire edges. */ if (edge_faces[i] == 0) { is_manifold_consistent = false; break; } + /* Check for zero length edges */ + MVert *v1 = &mesh->mvert[mesh->medge[i].v1]; + MVert *v2 = &mesh->mvert[mesh->medge[i].v2]; + if (compare_v3v3(v1->co, v2->co, 1e-4f)) { + is_manifold_consistent = false; + break; + } } } diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c index 66390f6f165..c7dfe911ce7 100644 --- a/source/blender/editors/object/object_utils.c +++ b/source/blender/editors/object/object_utils.c @@ -36,6 +36,7 @@ #include "BKE_armature.h" #include "BKE_editmesh.h" #include "BKE_lattice.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "DEG_depsgraph_query.h" @@ -430,3 +431,70 @@ void ED_object_data_xform_container_destroy(struct XFormObjectData_Container *xd } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Transform Object Array + * + * Low level object transform function, transforming objects by `matrix`. + * Simple alternative to full transform logic. + * \{ */ + +static bool object_parent_in_set(GSet *objects_set, Object *ob) +{ + for (Object *parent = ob->parent; parent; parent = parent->parent) { + if (BLI_gset_lookup(objects_set, parent)) { + return true; + } + } + return false; +} + +void ED_object_xform_array_m4(Object **objects, uint objects_len, const float matrix[4][4]) +{ + /* Filter out objects that have parents in `objects_set`. */ + { + GSet *objects_set = BLI_gset_ptr_new_ex(__func__, objects_len); + for (uint i = 0; i < objects_len; i++) { + BLI_gset_add(objects_set, objects[i]); + } + for (uint i = 0; i < objects_len;) { + if (object_parent_in_set(objects_set, objects[i])) { + objects[i] = objects[--objects_len]; + } + else { + i++; + } + } + BLI_gset_free(objects_set, NULL); + } + + /* Detect translation only matrix, prevent rotation/scale channels from being touched at all. */ + bool is_translation_only; + { + float test_m4_a[4][4], test_m4_b[4][4]; + unit_m4(test_m4_a); + copy_m4_m4(test_m4_b, matrix); + zero_v3(test_m4_b[3]); + is_translation_only = equals_m4m4(test_m4_a, test_m4_b); + } + + if (is_translation_only) { + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + add_v3_v3(ob->loc, matrix[3]); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } + else { + for (uint i = 0; i < objects_len; i++) { + float m4[4][4]; + Object *ob = objects[i]; + BKE_object_to_mat4(ob, m4); + mul_m4_m4m4(m4, matrix, m4); + BKE_object_apply_mat4(ob, m4, true, true); + DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); + } + } +} + +/** \} */ diff --git a/source/blender/editors/physics/particle_edit.c b/source/blender/editors/physics/particle_edit.c index 8afc5c583e0..97f3b96cd18 100644 --- a/source/blender/editors/physics/particle_edit.c +++ b/source/blender/editors/physics/particle_edit.c @@ -1000,7 +1000,7 @@ static void PE_update_mirror_cache(Object *ob, ParticleSystem *psys) tree = BLI_kdtree_3d_new(totpart); - /* insert particles into kd tree */ + /* Insert particles into KD-tree. */ LOOP_PARTICLES { key = pa->hair; diff --git a/source/blender/editors/physics/particle_object.c b/source/blender/editors/physics/particle_object.c index 3ac6dca3044..367d72b0ad7 100644 --- a/source/blender/editors/physics/particle_object.c +++ b/source/blender/editors/physics/particle_object.c @@ -1235,9 +1235,15 @@ static int copy_particle_systems_exec(bContext *C, wmOperator *op) const bool use_active = RNA_boolean_get(op->ptr, "use_active"); Scene *scene = CTX_data_scene(C); Object *ob_from = ED_object_active_context(C); - ParticleSystem *psys_from = - use_active ? CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data : - NULL; + + ParticleSystem *psys_from = NULL; + if (use_active) { + psys_from = CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data; + if (psys_from == NULL) { + /* Particle System context pointer is only valid in the Properties Editor. */ + psys_from = psys_get_current(ob_from); + } + } int changed_tot = 0; int fail = 0; diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index d5ad5a5eb84..49e7ebf6340 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -959,7 +959,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even * store spare */ /* ensure at least 1 area shows result */ - area = render_view_open(C, event->x, event->y, op->reports); + area = render_view_open(C, event->xy[0], event->xy[1], op->reports); /* job custom data */ rj = MEM_callocN(sizeof(RenderJob), "render job"); @@ -976,7 +976,6 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even rj->anim = is_animation; rj->write_still = is_write_still && !is_animation; rj->iuser.scene = scene; - rj->iuser.ok = 1; rj->reports = op->reports; rj->orig_layer = 0; rj->last_layer = 0; diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 834c023dde9..1e1a95f2965 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -303,7 +303,7 @@ static void screen_opengl_render_doit(const bContext *C, OGLRender *oglrender, R if (oglrender->is_sequencer) { SpaceSeq *sseq = oglrender->sseq; - struct bGPdata *gpd = (sseq && (sseq->flag & SEQ_SHOW_GPENCIL)) ? sseq->gpd : NULL; + struct bGPdata *gpd = (sseq && (sseq->flag & SEQ_PREVIEW_SHOW_GPENCIL)) ? sseq->gpd : NULL; /* use pre-calculated ImBuf (avoids deadlock), see: */ ImBuf *ibuf = oglrender->seq_data.ibufs_arr[oglrender->view_id]; @@ -836,7 +836,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) BKE_image_backup_render(oglrender->scene, oglrender->ima, true); oglrender->iuser.scene = scene; - oglrender->iuser.ok = 1; /* create render result */ RE_InitState(oglrender->re, NULL, &scene->r, &scene->view_layers, NULL, sizex, sizey, NULL); @@ -1263,7 +1262,7 @@ static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEven } oglrender = op->customdata; - render_view_open(C, event->x, event->y, op->reports); + render_view_open(C, event->xy[0], event->xy[1], op->reports); /* View may be changed above #USER_RENDER_DISPLAY_WINDOW. */ oglrender->win = CTX_wm_window(C); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 95351de45f0..4bf250b9d4f 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -479,15 +479,6 @@ static Scene *preview_prepare_scene( BKE_color_managed_view_settings_free(&sce->view_settings); BKE_color_managed_view_settings_copy(&sce->view_settings, &scene->view_settings); - /* prevent overhead for small renders and icons (32) */ - if (id && sp->sizex < 40) { - sce->r.tilex = sce->r.tiley = 64; - } - else { - sce->r.tilex = sce->r.xsch / 4; - sce->r.tiley = sce->r.ysch / 4; - } - if ((id && sp->pr_method == PR_ICON_RENDER) && id_type != ID_WO) { sce->r.alphamode = R_ALPHAPREMUL; } @@ -1352,7 +1343,7 @@ static ImBuf *icon_preview_imbuf_from_brush(Brush *brush) BLI_strncpy(path, brush->icon_filepath, sizeof(brush->icon_filepath)); BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&brush->id)); - /* use default colorspaces for brushes */ + /* Use default color-spaces for brushes. */ brush->icon_imbuf = IMB_loadiffname(path, flags, NULL); /* otherwise lets try to find it in other directories */ @@ -1485,14 +1476,8 @@ static void icon_preview_startjob(void *customdata, short *stop, short *do_updat return; } - ImageTile *tile = BKE_image_get_tile(ima, 0); - /* tile->ok is zero when Image cannot load */ - if (tile->ok == 0) { - return; - } - /* setup dummy image user */ - iuser.ok = iuser.framenr = 1; + iuser.framenr = 1; iuser.scene = sp->scene; /* elubie: this needs to be changed: here image is always loaded if not diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 8a3d8f9636b..b6e6869f4e2 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -48,7 +48,6 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_editmesh.h" -#include "BKE_font.h" #include "BKE_global.h" #include "BKE_image.h" #include "BKE_layer.h" @@ -61,6 +60,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_texture.h" +#include "BKE_vfont.h" #include "BKE_workspace.h" #include "BKE_world.h" @@ -690,7 +690,7 @@ static int material_slot_remove_unused_exec(bContext *C, wmOperator *op) Object *ob = objects[ob_index]; int actcol = ob->actcol; for (int slot = 1; slot <= ob->totcol; slot++) { - while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) { + while (slot <= ob->totcol && !BKE_object_material_slot_used(ob, slot)) { ob->actcol = slot; BKE_object_material_slot_remove(bmain, ob); diff --git a/source/blender/editors/render/render_view.c b/source/blender/editors/render/render_view.c index 97ecb67d6cc..7f27b6f585a 100644 --- a/source/blender/editors/render/render_view.c +++ b/source/blender/editors/render/render_view.c @@ -183,8 +183,8 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) else if (U.render_display_type == USER_RENDER_DISPLAY_SCREEN) { area = CTX_wm_area(C); - /* if the active screen is already in fullscreen mode, skip this and - * unset the area, so that the fullscreen area is just changed later */ + /* If the active screen is already in full-screen mode, skip this and + * unset the area, so that the full-screen area is just changed later. */ if (area && area->full) { area = NULL; } @@ -216,10 +216,10 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) ED_area_newspace(C, area, SPACE_IMAGE, true); sima = area->spacedata.first; - /* makes ESC go back to prev space */ + /* Makes "Escape" go back to previous space. */ sima->flag |= SI_PREVSPACE; - /* we already had a fullscreen here -> mark new space as a stacked fullscreen */ + /* We already had a full-screen here -> mark new space as a stacked full-screen. */ if (area->full) { area->flag |= AREA_FLAG_STACKED_FULLSCREEN; } @@ -231,7 +231,7 @@ ScrArea *render_view_open(bContext *C, int mx, int my, ReportList *reports) // XXX newspace(area, SPACE_IMAGE); sima = area->spacedata.first; - /* makes ESC go back to prev space */ + /* Makes "Escape" go back to previous space. */ sima->flag |= SI_PREVSPACE; } } @@ -275,7 +275,7 @@ static int render_view_cancel_exec(bContext *C, wmOperator *UNUSED(op)) ScrArea *area = CTX_wm_area(C); SpaceImage *sima = area->spacedata.first; - /* ensure image editor fullscreen and area fullscreen states are in sync */ + /* ensure image editor full-screen and area full-screen states are in sync */ if ((sima->flag & SI_FULLWINDOW) && !area->full) { sima->flag &= ~SI_FULLWINDOW; } @@ -365,7 +365,7 @@ static int render_view_show_invoke(bContext *C, wmOperator *op, const wmEvent *e } } else { - render_view_open(C, event->x, event->y, op->reports); + render_view_open(C, event->xy[0], event->xy[1], op->reports); } } diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 9546035375c..80c14371c16 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -134,7 +134,7 @@ static void region_draw_emboss(const ARegion *region, const rcti *scirct, int si GPU_blend(GPU_BLEND_NONE); } -void ED_region_pixelspace(ARegion *region) +void ED_region_pixelspace(const ARegion *region) { wmOrtho2_region_pixelspace(region); GPU_matrix_identity_set(); @@ -972,29 +972,33 @@ static void fullscreen_azone_init(ScrArea *area, ARegion *region) #define AZONEPAD_ICON (0.45f * U.widget_unit) static void region_azone_edge(AZone *az, ARegion *region) { + /* If region is overlapped (transparent background), move #AZone to content. + * Note this is an arbitrary amount that matches nicely with numbers elsewhere. */ + int overlap_padding = (region->overlap) ? (int)(0.4f * U.widget_unit) : 0; + switch (az->edge) { case AE_TOP_TO_BOTTOMRIGHT: az->x1 = region->winrct.xmin; - az->y1 = region->winrct.ymax - AZONEPAD_EDGE; + az->y1 = region->winrct.ymax - AZONEPAD_EDGE - overlap_padding; az->x2 = region->winrct.xmax; - az->y2 = region->winrct.ymax + AZONEPAD_EDGE; + az->y2 = region->winrct.ymax + AZONEPAD_EDGE - overlap_padding; break; case AE_BOTTOM_TO_TOPLEFT: az->x1 = region->winrct.xmin; - az->y1 = region->winrct.ymin + AZONEPAD_EDGE; + az->y1 = region->winrct.ymin + AZONEPAD_EDGE + overlap_padding; az->x2 = region->winrct.xmax; - az->y2 = region->winrct.ymin - AZONEPAD_EDGE; + az->y2 = region->winrct.ymin - AZONEPAD_EDGE + overlap_padding; break; case AE_LEFT_TO_TOPRIGHT: - az->x1 = region->winrct.xmin - AZONEPAD_EDGE; + az->x1 = region->winrct.xmin - AZONEPAD_EDGE + overlap_padding; az->y1 = region->winrct.ymin; - az->x2 = region->winrct.xmin + AZONEPAD_EDGE; + az->x2 = region->winrct.xmin + AZONEPAD_EDGE + overlap_padding; az->y2 = region->winrct.ymax; break; case AE_RIGHT_TO_TOPLEFT: - az->x1 = region->winrct.xmax + AZONEPAD_EDGE; + az->x1 = region->winrct.xmax + AZONEPAD_EDGE - overlap_padding; az->y1 = region->winrct.ymin; - az->x2 = region->winrct.xmax - AZONEPAD_EDGE; + az->x2 = region->winrct.xmax - AZONEPAD_EDGE - overlap_padding; az->y2 = region->winrct.ymax; break; } @@ -1284,8 +1288,8 @@ bool ED_region_is_overlap(int spacetype, int regiontype) RGN_TYPE_TOOLS, RGN_TYPE_UI, RGN_TYPE_TOOL_PROPS, - RGN_TYPE_HEADER, - RGN_TYPE_FOOTER)) { + RGN_TYPE_FOOTER, + RGN_TYPE_TOOL_HEADER)) { return true; } } @@ -1681,7 +1685,7 @@ static bool event_in_markers_region(const ARegion *region, const wmEvent *event) { rcti rect = region->winrct; rect.ymax = rect.ymin + UI_MARKER_MARGIN_Y; - return BLI_rcti_isect_pt(&rect, event->x, event->y); + return BLI_rcti_isect_pt(&rect, event->xy[0], event->xy[1]); } /** @@ -1699,6 +1703,9 @@ static void ed_default_handlers( wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "User Interface", 0, 0); WM_event_add_keymap_handler(handlers, keymap); + ListBase *dropboxes = WM_dropboxmap_find("User Interface", 0, 0); + WM_event_add_dropbox_handler(handlers, dropboxes); + /* user interface widgets */ UI_region_handlers_add(handlers); } @@ -1735,10 +1742,14 @@ static void ed_default_handlers( WM_event_add_keymap_handler(handlers, keymap); } if (flag & ED_KEYMAP_TOOL) { - WM_event_add_keymap_handler_dynamic( - ®ion->handlers, WM_event_get_keymap_from_toolsystem_fallback, area); - WM_event_add_keymap_handler_dynamic( - ®ion->handlers, WM_event_get_keymap_from_toolsystem, area); + if (flag & ED_KEYMAP_GIZMO) { + WM_event_add_keymap_handler_dynamic( + ®ion->handlers, WM_event_get_keymap_from_toolsystem_with_gizmos, area); + } + else { + WM_event_add_keymap_handler_dynamic( + ®ion->handlers, WM_event_get_keymap_from_toolsystem, area); + } } if (flag & ED_KEYMAP_FRAMES) { /* frame changing/jumping (for all spaces) */ @@ -1905,7 +1916,7 @@ void ED_area_update_region_sizes(wmWindowManager *wm, wmWindow *win, ScrArea *ar /* Some AZones use View2D data which is only updated in region init, so call that first! */ region_azones_add(screen, area, region); } - ED_area_azones_update(area, &win->eventstate->x); + ED_area_azones_update(area, win->eventstate->xy); area->flag &= ~AREA_FLAG_REGION_SIZE_UPDATE; } @@ -1929,7 +1940,7 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) rcti window_rect; WM_window_rect_calc(win, &window_rect); - /* set typedefinitions */ + /* Set type-definitions. */ area->type = BKE_spacetype_from_id(area->spacetype); if (area->type == NULL) { @@ -1994,6 +2005,68 @@ void ED_area_init(wmWindowManager *wm, wmWindow *win, ScrArea *area) } } +static void area_offscreen_init(ScrArea *area) +{ + area->type = BKE_spacetype_from_id(area->spacetype); + + if (area->type == NULL) { + area->spacetype = SPACE_VIEW3D; + area->type = BKE_spacetype_from_id(area->spacetype); + } + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + region->type = BKE_regiontype_from_id_or_first(area->type, region->regiontype); + } +} + +ScrArea *ED_area_offscreen_create(wmWindow *win, eSpace_Type space_type) +{ + ScrArea *area = MEM_callocN(sizeof(*area), __func__); + area->spacetype = space_type; + + screen_area_spacelink_add(WM_window_get_active_scene(win), area, space_type); + area_offscreen_init(area); + + return area; +} + +static void area_offscreen_exit(wmWindowManager *wm, wmWindow *win, ScrArea *area) +{ + if (area->type && area->type->exit) { + area->type->exit(wm, area); + } + + LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { + if (region->type && region->type->exit) { + region->type->exit(wm, region); + } + + WM_event_modal_handler_region_replace(win, region, NULL); + WM_draw_region_free(region, true); + + MEM_SAFE_FREE(region->headerstr); + + if (region->regiontimer) { + WM_event_remove_timer(wm, win, region->regiontimer); + region->regiontimer = NULL; + } + + if (wm->message_bus) { + WM_msgbus_clear_by_owner(wm->message_bus, region); + } + } + + WM_event_modal_handler_area_replace(win, area, NULL); +} + +void ED_area_offscreen_free(wmWindowManager *wm, wmWindow *win, ScrArea *area) +{ + area_offscreen_exit(wm, win, area); + + BKE_screen_area_free(area); + MEM_freeN(area); +} + static void region_update_rect(ARegion *region) { region->winx = BLI_rcti_size_x(®ion->winrct) + 1; @@ -2589,10 +2662,10 @@ static ThemeColorID region_background_color_id(const bContext *C, const ARegion case RGN_TYPE_HEADER: case RGN_TYPE_TOOL_HEADER: if (ED_screen_area_active(C) || ED_area_is_global(area)) { - return TH_HEADER; + return TH_HEADER_ACTIVE; } else { - return TH_HEADERDESEL; + return TH_HEADER; } case RGN_TYPE_PREVIEW: return TH_PREVIEW_BACK; @@ -2908,12 +2981,11 @@ void ED_region_panels_layout_ex(const bContext *C, margin_x = category_tabs_width; } - const int w = BLI_rctf_size_x(&v2d->cur) - margin_x; + const int width_no_header = BLI_rctf_size_x(&v2d->cur) - margin_x; + const int width = width_no_header - UI_PANEL_MARGIN_X * 2.0f; /* Works out to 10 * UI_UNIT_X or 20 * UI_UNIT_X. */ const int em = (region->type->prefsizex) ? 10 : 20; - const int w_box_panel = w - UI_PANEL_BOX_STYLE_MARGIN * 2.0f; - /* create panels */ UI_panels_begin(C, region); @@ -2949,13 +3021,13 @@ void ED_region_panels_layout_ex(const bContext *C, ®ion->panels, pt, panel, - (pt->flag & PANEL_TYPE_DRAW_BOX) ? w_box_panel : w, + (pt->flag & PANEL_TYPE_NO_HEADER) ? width_no_header : width, em, NULL, search_filter); } - /* Draw "polyinstantaited" panels that don't have a 1 to 1 correspondence with their types. */ + /* Draw "poly-instantiated" panels that don't have a 1 to 1 correspondence with their types. */ if (has_instanced_panel) { LISTBASE_FOREACH (Panel *, panel, ®ion->panels) { if (panel->type == NULL) { @@ -2983,7 +3055,7 @@ void ED_region_panels_layout_ex(const bContext *C, ®ion->panels, panel->type, panel, - (panel->type->flag & PANEL_TYPE_DRAW_BOX) ? w_box_panel : w, + (panel->type->flag & PANEL_TYPE_NO_HEADER) ? width_no_header : width, em, unique_panel_str, search_filter); @@ -2996,7 +3068,7 @@ void ED_region_panels_layout_ex(const bContext *C, /* before setting the view */ if (region_layout_based) { - /* XXX, only single panel support atm. + /* XXX, only single panel support at the moment. * Can't use x/y values calculated above because they're not using the real height of panels, * instead they calculate offsets for the next panel to start drawing. */ Panel *panel = region->panels.last; diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 2ccefb993c7..a8c63027254 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -49,11 +49,13 @@ #include "BKE_gpencil.h" #include "BKE_layer.h" #include "BKE_object.h" +#include "BKE_tracking.h" #include "RNA_access.h" #include "ED_anim_api.h" #include "ED_armature.h" +#include "ED_clip.h" #include "ED_gpencil.h" #include "SEQ_select.h" @@ -99,6 +101,7 @@ const char *screen_context_dir[] = { "active_nla_track", "active_nla_strip", "selected_nla_strips", /* nla editor */ + "selected_movieclip_tracks", "gpencil_data", "gpencil_data_owner", /* grease pencil data */ "annotation_data", @@ -709,6 +712,33 @@ static eContextResult screen_ctx_selected_nla_strips(const bContext *C, bContext } return CTX_RESULT_NO_DATA; } +static eContextResult screen_ctx_selected_movieclip_tracks(const bContext *C, + bContextDataResult *result) +{ + SpaceClip *space_clip = CTX_wm_space_clip(C); + if (space_clip == NULL) { + return CTX_RESULT_NO_DATA; + } + MovieClip *clip = ED_space_clip_get_clip(space_clip); + if (clip == NULL) { + return CTX_RESULT_NO_DATA; + } + MovieTracking *tracking = &clip->tracking; + if (tracking == NULL) { + return CTX_RESULT_NO_DATA; + } + + ListBase *tracks_list = BKE_tracking_get_active_tracks(tracking); + LISTBASE_FOREACH (MovieTrackingTrack *, track, tracks_list) { + if (!TRACK_SELECTED(track)) { + continue; + } + CTX_data_list_add(result, &clip->id, &RNA_MovieTrackingTrack, track); + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; +} static eContextResult screen_ctx_gpencil_data(const bContext *C, bContextDataResult *result) { wmWindow *win = CTX_wm_window(C); @@ -1073,9 +1103,14 @@ static eContextResult screen_ctx_ui_list(const bContext *C, bContextDataResult * { wmWindow *win = CTX_wm_window(C); ARegion *region = CTX_wm_region(C); - uiList *list = UI_list_find_mouse_over(region, win->eventstate); - CTX_data_pointer_set(result, NULL, &RNA_UIList, list); - return CTX_RESULT_OK; + if (region) { + uiList *list = UI_list_find_mouse_over(region, win->eventstate); + if (list) { + CTX_data_pointer_set(result, NULL, &RNA_UIList, list); + return CTX_RESULT_OK; + } + } + return CTX_RESULT_NO_DATA; } /* Registry of context callback functions. */ @@ -1138,6 +1173,7 @@ static void ensure_ed_screen_context_functions(void) register_context_function("active_nla_track", screen_ctx_active_nla_track); register_context_function("active_nla_strip", screen_ctx_active_nla_strip); register_context_function("selected_nla_strips", screen_ctx_selected_nla_strips); + register_context_function("selected_movieclip_tracks", screen_ctx_selected_movieclip_tracks); register_context_function("gpencil_data", screen_ctx_gpencil_data); register_context_function("gpencil_data_owner", screen_ctx_gpencil_data_owner); register_context_function("annotation_data", screen_ctx_annotation_data); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 1c068fdd6e4..fa0cfd16817 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -57,6 +57,7 @@ #include "UI_interface.h" #include "WM_message.h" +#include "WM_toolsystem.h" #include "DEG_depsgraph_query.h" @@ -574,6 +575,17 @@ bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area) return screen_area_join_ex(C, screen, sa2, area, true); } +void screen_area_spacelink_add(Scene *scene, ScrArea *area, eSpace_Type space_type) +{ + SpaceType *stype = BKE_spacetype_from_id(space_type); + SpaceLink *slink = stype->create(area, scene); + + area->regionbase = slink->regionbase; + + BLI_addhead(&area->spacedata, slink); + BLI_listbase_clear(&slink->regionbase); +} + /* ****************** EXPORTED API TO OTHER MODULES *************************** */ /* screen sets cursor based on active region */ @@ -634,10 +646,14 @@ void ED_screen_refresh(wmWindowManager *wm, wmWindow *win) { bScreen *screen = WM_window_get_active_screen(win); - /* exception for bg mode, we only need the screen context */ + /* Exception for background mode, we only need the screen context. */ if (!G.background) { - /* header size depends on DPI, let's verify */ - WM_window_set_dpi(win); + + /* Called even when creating the ghost window fails in #WM_window_open. */ + if (win->ghostwin) { + /* Header size depends on DPI, let's verify. */ + WM_window_set_dpi(win); + } ED_screen_global_areas_refresh(win); @@ -675,7 +691,7 @@ void ED_screens_init(Main *bmain, wmWindowManager *wm) ED_screen_refresh(wm, win); if (win->eventstate) { - ED_screen_set_active_region(NULL, win, &win->eventstate->x); + ED_screen_set_active_region(NULL, win, win->eventstate->xy); } } @@ -919,6 +935,10 @@ void ED_screen_set_active_region(bContext *C, wmWindow *win, const int xy[2]) } } } + + /* Ensure test-motion values are never shared between regions. */ + const bool use_cycle = !WM_cursor_test_motion_and_update((const int[2]){-1, -1}); + UNUSED_VARS(use_cycle); } /* Cursors, for time being set always on edges, @@ -948,7 +968,7 @@ int ED_screen_area_active(const bContext *C) ScrArea *area = CTX_wm_area(C); if (win && screen && area) { - AZone *az = ED_area_actionzone_find_xy(area, &win->eventstate->x); + AZone *az = ED_area_actionzone_find_xy(area, win->eventstate->xy); if (az && az->type == AZONE_REGION) { return 1; @@ -1023,13 +1043,7 @@ static void screen_global_area_refresh(wmWindow *win, } else { area = screen_area_create_with_geometry(&win->global_areas, rect, space_type); - SpaceType *stype = BKE_spacetype_from_id(space_type); - SpaceLink *slink = stype->create(area, WM_window_get_active_scene(win)); - - area->regionbase = slink->regionbase; - - BLI_addhead(&area->spacedata, slink); - BLI_listbase_clear(&slink->regionbase); + screen_area_spacelink_add(WM_window_get_active_scene(win), area, space_type); /* Data specific to global areas. */ area->global = MEM_callocN(sizeof(*area->global), __func__); @@ -1230,7 +1244,10 @@ static void screen_set_3dview_camera(Scene *scene, } } -void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene) +void ED_screen_scene_change(bContext *C, + wmWindow *win, + Scene *scene, + const bool refresh_toolsystem) { #if 0 ViewLayer *view_layer_old = WM_window_get_active_view_layer(win); @@ -1268,6 +1285,10 @@ void ED_screen_scene_change(bContext *C, wmWindow *win, Scene *scene) } } } + + if (refresh_toolsystem) { + WM_toolsystem_refresh_screen_window(win); + } } ScrArea *ED_screen_full_newspace(bContext *C, ScrArea *area, int type) diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index 4016ef84bfd..47229e5e2b5 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -62,7 +62,7 @@ typedef enum eScreenAxis { #define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac) /* Expanded interaction influence of area borders. */ -#define BORDERPADDING (U.dpi_fac + U.pixelsize) +#define BORDERPADDING ((2.0f * U.dpi_fac) + U.pixelsize) /* area.c */ void ED_area_data_copy(ScrArea *area_dst, ScrArea *area_src, const bool do_free); @@ -94,6 +94,7 @@ eScreenDir area_getorientation(ScrArea *sa_a, ScrArea *sa_b); void area_getoffsets( ScrArea *sa_a, ScrArea *sa_b, const eScreenDir dir, int *r_offset1, int *r_offset2); bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area); +void screen_area_spacelink_add(struct Scene *scene, ScrArea *area, eSpace_Type space_type); struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]); /* screen_geometry.c */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 674a2deb929..66140cba9c6 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -219,6 +219,20 @@ bool ED_operator_objectmode(bContext *C) return true; } +/** + * Same as #ED_operator_objectmode() but additionally sets a "disabled hint". That is, a message + * to be displayed to the user explaining why the operator can't be used in current context. + */ +bool ED_operator_objectmode_poll_msg(bContext *C) +{ + if (!ED_operator_objectmode(C)) { + CTX_wm_operator_poll_msg_set(C, "Only supported in object mode"); + return false; + } + + return true; +} + static bool ed_spacetype_test(bContext *C, int type) { if (ED_operator_areaactive(C)) { @@ -773,7 +787,7 @@ static bool actionzone_area_poll(bContext *C) bScreen *screen = WM_window_get_active_screen(win); if (screen && win && win->eventstate) { - const int *xy = &win->eventstate->x; + const int *xy = &win->eventstate->xy[0]; LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { LISTBASE_FOREACH (AZone *, az, &area->actionzones) { @@ -900,8 +914,7 @@ static AZone *area_actionzone_refresh_xy(ScrArea *area, const int xy[2], const b ARegion *region = az->region; View2D *v2d = ®ion->v2d; int scroll_flag = 0; - const int isect_value = UI_view2d_mouse_in_scrollers_ex( - region, v2d, xy[0], xy[1], &scroll_flag); + const int isect_value = UI_view2d_mouse_in_scrollers_ex(region, v2d, xy, &scroll_flag); /* Check if we even have scroll bars. */ if (((az->direction == AZ_SCROLL_HOR) && !(scroll_flag & V2D_SCROLL_HORIZONTAL)) || @@ -1052,7 +1065,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) event.val = KM_NOTHING; event.is_repeat = false; event.customdata = op->customdata; - event.customdatafree = true; + event.customdata_free = true; op->customdata = NULL; wm_event_add(win, &event); @@ -1061,7 +1074,7 @@ static void actionzone_apply(bContext *C, wmOperator *op, int type) static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) { bScreen *screen = CTX_wm_screen(C); - AZone *az = screen_actionzone_find_xy(screen, &event->x); + AZone *az = screen_actionzone_find_xy(screen, event->xy); /* Quick escape - Scroll azones only hide/unhide the scroll-bars, * they have their own handling. */ @@ -1073,8 +1086,8 @@ static int actionzone_invoke(bContext *C, wmOperator *op, const wmEvent *event) sActionzoneData *sad = op->customdata = MEM_callocN(sizeof(sActionzoneData), "sActionzoneData"); sad->sa1 = screen_actionzone_area(screen, az); sad->az = az; - sad->x = event->x; - sad->y = event->y; + sad->x = event->xy[0]; + sad->y = event->xy[1]; /* region azone directly reacts on mouse clicks */ if (ELEM(sad->az->type, AZONE_REGION, AZONE_FULLSCREEN)) { @@ -1098,8 +1111,8 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { - const int delta_x = (event->x - sad->x); - const int delta_y = (event->y - sad->y); + const int delta_x = (event->xy[0] - sad->x); + const int delta_y = (event->xy[1] - sad->y); /* Movement in dominant direction. */ const int delta_max = max_ii(abs(delta_x), abs(delta_y)); @@ -1131,12 +1144,13 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) WM_window_screen_rect_calc(win, &screen_rect); /* Have we dragged off the zone and are not on an edge? */ - if ((ED_area_actionzone_find_xy(sad->sa1, &event->x) != sad->az) && + if ((ED_area_actionzone_find_xy(sad->sa1, event->xy) != sad->az) && (screen_geom_area_map_find_active_scredge( - AREAMAP_FROM_SCREEN(screen), &screen_rect, event->x, event->y) == NULL)) { + AREAMAP_FROM_SCREEN(screen), &screen_rect, event->xy[0], event->xy[1]) == NULL)) { /* What area are we now in? */ - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + ScrArea *area = BKE_screen_find_area_xy( + screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); if (area == sad->sa1) { /* Same area, so possible split. */ @@ -1180,7 +1194,7 @@ static int actionzone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* gesture is large enough? */ if (is_gesture) { /* second area, for join when (sa1 != sa2) */ - sad->sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + sad->sa2 = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); /* apply sends event */ actionzone_apply(C, op, sad->az->type); actionzone_exit(op); @@ -1283,7 +1297,7 @@ static ScrEdge *screen_area_edge_from_cursor(const bContext *C, * * callbacks: * - * invoke() gets called on shift+lmb drag in action-zone + * invoke() gets called on Shift-LMB drag in action-zone * exec() execute without any user interaction, based on properties * call init(), add handler * @@ -1341,7 +1355,8 @@ static int area_swap_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: /* second area, for join */ - sad->sa2 = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y); + sad->sa2 = BKE_screen_find_area_xy( + CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy[0], event->xy[1]); break; case LEFTMOUSE: /* release LMB */ if (event->val == KM_RELEASE) { @@ -1942,8 +1957,8 @@ static int area_move_exec(bContext *C, wmOperator *op) /* interaction callback */ static int area_move_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - RNA_int_set(op->ptr, "x", event->x); - RNA_int_set(op->ptr, "y", event->y); + RNA_int_set(op->ptr, "x", event->xy[0]); + RNA_int_set(op->ptr, "y", event->xy[1]); if (!area_move_init(C, op)) { return OPERATOR_PASS_THROUGH; @@ -1975,7 +1990,7 @@ static int area_move_modal(bContext *C, wmOperator *op, const wmEvent *event) int x = RNA_int_get(op->ptr, "x"); int y = RNA_int_get(op->ptr, "y"); - const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->x - x : event->y - y; + const int delta = (md->dir_axis == SCREEN_AXIS_V) ? event->xy[0] - x : event->xy[1] - y; RNA_int_set(op->ptr, "delta", delta); area_move_apply(C, op); @@ -2077,7 +2092,7 @@ typedef struct sAreaSplitData { int bigger, smaller; /* constraints for moving new edge */ int delta; /* delta move edge */ int origmin, origsize; /* to calculate fac, for property storage */ - int previewmode; /* draw previewline, then split */ + int previewmode; /* draw preview-line, then split. */ void *draw_callback; /* call `screen_draw_split_preview` */ bool do_snap; @@ -2295,8 +2310,8 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) } /* The factor will be close to 1.0f when near the top-left and the bottom-right corners. */ - const float factor_v = ((float)(event->y - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy; - const float factor_h = ((float)(event->x - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx; + const float factor_v = ((float)(event->xy[1] - sad->sa1->v1->vec.y)) / (float)sad->sa1->winy; + const float factor_h = ((float)(event->xy[0] - sad->sa1->v1->vec.x)) / (float)sad->sa1->winx; const bool is_left = factor_v < 0.5f; const bool is_bottom = factor_h < 0.5f; const bool is_right = !is_left; @@ -2334,11 +2349,11 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) dir_axis = RNA_property_enum_get(op->ptr, prop_dir); if (dir_axis == SCREEN_AXIS_H) { RNA_property_float_set( - op->ptr, prop_factor, ((float)(event->x - area->v1->vec.x)) / (float)area->winx); + op->ptr, prop_factor, ((float)(event->xy[0] - area->v1->vec.x)) / (float)area->winx); } else { RNA_property_float_set( - op->ptr, prop_factor, ((float)(event->y - area->v1->vec.y)) / (float)area->winy); + op->ptr, prop_factor, ((float)(event->xy[1] - area->v1->vec.y)) / (float)area->winy); } if (!area_split_init(C, op)) { @@ -2353,7 +2368,7 @@ static int area_split_invoke(bContext *C, wmOperator *op, const wmEvent *event) RNA_property_int_get_array(op->ptr, prop_cursor, event_co); } else { - copy_v2_v2_int(event_co, &event->x); + copy_v2_v2_int(event_co, event->xy); } rcti window_rect; @@ -2493,7 +2508,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) if (update_factor) { const eScreenAxis dir_axis = RNA_property_enum_get(op->ptr, prop_dir); - sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->x - sd->origval : event->y - sd->origval; + sd->delta = (dir_axis == SCREEN_AXIS_V) ? event->xy[0] - sd->origval : + event->xy[1] - sd->origval; if (sd->previewmode == 0) { if (sd->do_snap) { @@ -2513,7 +2529,8 @@ static int area_split_modal(bContext *C, wmOperator *op, const wmEvent *event) ED_area_tag_redraw(sd->sarea); } /* area context not set */ - sd->sarea = BKE_screen_find_area_xy(CTX_wm_screen(C), SPACE_TYPE_ANY, event->x, event->y); + sd->sarea = BKE_screen_find_area_xy( + CTX_wm_screen(C), SPACE_TYPE_ANY, event->xy[0], event->xy[1]); if (sd->sarea) { ScrArea *area = sd->sarea; @@ -2625,8 +2642,8 @@ static int area_max_regionsize(ScrArea *area, ARegion *scalear, AZEdge edge) dist = BLI_rcti_size_y(&area->totrct); } - /* subtractwidth of regions on opposite side - * prevents dragging regions into other opposite regions */ + /* Subtract the width of regions on opposite side + * prevents dragging regions into other opposite regions. */ LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { if (region == scalear) { continue; @@ -2702,8 +2719,8 @@ static int region_scale_invoke(bContext *C, wmOperator *op, const wmEvent *event } rmd->area = sad->sa1; rmd->edge = az->edge; - rmd->origx = event->x; - rmd->origy = event->y; + rmd->origx = event->xy[0]; + rmd->origy = event->xy[1]; rmd->maxsize = area_max_regionsize(rmd->area, rmd->region, rmd->edge); /* if not set we do now, otherwise it uses type */ @@ -2790,7 +2807,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) (BLI_rcti_size_x(&rmd->region->v2d.mask) + 1); const int snap_size_threshold = (U.widget_unit * 2) / aspect; if (ELEM(rmd->edge, AE_LEFT_TO_TOPRIGHT, AE_RIGHT_TO_TOPLEFT)) { - delta = event->x - rmd->origx; + delta = event->xy[0] - rmd->origx; if (rmd->edge == AE_LEFT_TO_TOPRIGHT) { delta = -delta; } @@ -2823,7 +2840,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) } } else { - delta = event->y - rmd->origy; + delta = event->xy[1] - rmd->origy; if (rmd->edge == AE_BOTTOM_TO_TOPLEFT) { delta = -delta; } @@ -2865,7 +2882,7 @@ static int region_scale_modal(bContext *C, wmOperator *op, const wmEvent *event) } case LEFTMOUSE: if (event->val == KM_RELEASE) { - if (len_manhattan_v2v2_int(&event->x, &rmd->origx) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { + if (len_manhattan_v2v2_int(event->xy, &rmd->origx) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { if (rmd->region->flag & RGN_FLAG_HIDDEN) { region_scale_toggle_hidden(C, rmd); } @@ -3079,12 +3096,12 @@ static int keyframe_jump_exec(bContext *C, wmOperator *op) float cfra = (float)(CFRA); - /* init binarytree-list for getting keyframes */ + /* Initialize binary-tree-list for getting keyframes. */ struct AnimKeylist *keylist = ED_keylist_create(); - /* seed up dummy dopesheet context with flags to perform necessary filtering */ + /* Speed up dummy dope-sheet context with flags to perform necessary filtering. */ if ((scene->flag & SCE_KEYS_NO_SELONLY) == 0) { - /* only selected channels are included */ + /* Only selected channels are included. */ ads.filterflag |= ADS_FILTER_ONLYSEL; } @@ -3521,7 +3538,7 @@ static int area_join_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { - ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); jd->dir = area_getorientation(jd->sa1, jd->sa2); if (area == jd->sa1) { @@ -3611,7 +3628,7 @@ static void SCREEN_OT_area_join(wmOperatorType *ot) static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ScrArea *sa1, *sa2; - if (screen_area_edge_from_cursor(C, &event->x, &sa1, &sa2) == NULL) { + if (screen_area_edge_from_cursor(C, event->xy, &sa1, &sa2) == NULL) { return OPERATOR_CANCELLED; } @@ -3629,7 +3646,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent 0, &ptr); /* store initial mouse cursor position. */ - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", event->xy); RNA_enum_set(&ptr, "direction", SCREEN_AXIS_V); /* Horizontal Split */ @@ -3642,7 +3659,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent 0, &ptr); /* store initial mouse cursor position. */ - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", event->xy); RNA_enum_set(&ptr, "direction", SCREEN_AXIS_H); if (sa1 && sa2) { @@ -3659,7 +3676,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent WM_OP_INVOKE_DEFAULT, 0, &ptr); - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", event->xy); } /* Swap just needs two areas. */ @@ -3672,7 +3689,7 @@ static int screen_area_options_invoke(bContext *C, wmOperator *op, const wmEvent WM_OP_EXEC_DEFAULT, 0, &ptr); - RNA_int_set_array(&ptr, "cursor", &event->x); + RNA_int_set_array(&ptr, "cursor", event->xy); } UI_popup_menu_end(C, pup); @@ -4203,7 +4220,7 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ -/** \name Region Context Menu Operator (Header/Footer/Navbar) +/** \name Region Context Menu Operator (Header/Footer/Navigation-Bar) * \{ */ static void screen_area_menu_items(ScrArea *area, uiLayout *layout) @@ -4410,7 +4427,7 @@ static void SCREEN_OT_region_context_menu(wmOperatorType *ot) * Animation Step. * \{ */ static bool screen_animation_region_supports_time_follow(eSpace_Type spacetype, - eRegionType regiontype) + eRegion_Type regiontype) { return (regiontype == RGN_TYPE_WINDOW && ELEM(spacetype, SPACE_SEQ, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) || @@ -4418,7 +4435,7 @@ static bool screen_animation_region_supports_time_follow(eSpace_Type spacetype, } static bool match_region_with_redraws(const ScrArea *area, - eRegionType regiontype, + eRegion_Type regiontype, eScreen_Redraws_Flag redraws, bool from_anim_edit) { @@ -5055,11 +5072,23 @@ static int userpref_show_exec(bContext *C, wmOperator *op) int sizex = (500 + UI_NAVIGATION_REGION_WIDTH) * UI_DPI_FAC; int sizey = 520 * UI_DPI_FAC; + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "section"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + /* Set active section via RNA, so it can fail properly. */ + + PointerRNA pref_ptr; + RNA_pointer_create(NULL, &RNA_Preferences, &U, &pref_ptr); + PropertyRNA *active_section_prop = RNA_struct_find_property(&pref_ptr, "active_section"); + + RNA_property_enum_set(&pref_ptr, active_section_prop, RNA_property_enum_get(op->ptr, prop)); + RNA_property_update(C, &pref_ptr, active_section_prop); + } + /* changes context! */ if (WM_window_open(C, IFACE_("Blender Preferences"), - event->x, - event->y, + event->xy[0], + event->xy[1], sizex, sizey, SPACE_USERPREF, @@ -5088,14 +5117,24 @@ static int userpref_show_exec(bContext *C, wmOperator *op) static void SCREEN_OT_userpref_show(struct wmOperatorType *ot) { + PropertyRNA *prop; + /* identifiers */ - ot->name = "Show Preferences"; + ot->name = "Open Preferences..."; ot->description = "Edit user preferences and system settings"; ot->idname = "SCREEN_OT_userpref_show"; /* api callbacks */ ot->exec = userpref_show_exec; ot->poll = ED_operator_screenactive_nobackground; /* Not in background as this opens a window. */ + + prop = RNA_def_enum(ot->srna, + "section", + rna_enum_preference_section_items, + 0, + "", + "Section to activate in the Preferences"); + RNA_def_property_flag(prop, PROP_HIDDEN); } /** \} */ @@ -5125,8 +5164,8 @@ static int drivers_editor_show_exec(bContext *C, wmOperator *op) /* changes context! */ if (WM_window_open(C, IFACE_("Blender Drivers Editor"), - event->x, - event->y, + event->xy[0], + event->xy[1], sizex, sizey, SPACE_GRAPH, @@ -5194,8 +5233,8 @@ static int info_log_show_exec(bContext *C, wmOperator *op) /* changes context! */ if (WM_window_open(C, IFACE_("Blender Info Log"), - event->x, - event->y + shift_y, + event->xy[0], + event->xy[1] + shift_y, sizex, sizey, SPACE_INFO, diff --git a/source/blender/editors/screen/screen_user_menu.c b/source/blender/editors/screen/screen_user_menu.c index 733e8b694a6..bc370c64b0c 100644 --- a/source/blender/editors/screen/screen_user_menu.c +++ b/source/blender/editors/screen/screen_user_menu.c @@ -234,7 +234,7 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) } else { if (show_missing) { - SNPRINTF(label, "Missing: %s", umi_op->op_idname); + SNPRINTF(label, TIP_("Missing: %s"), umi_op->op_idname); uiItemL(menu->layout, label, ICON_NONE); } } @@ -248,7 +248,7 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) } else { if (show_missing) { - SNPRINTF(label, "Missing: %s", umi_mt->mt_idname); + SNPRINTF(label, TIP_("Missing: %s"), umi_mt->mt_idname); uiItemL(menu->layout, label, ICON_NONE); } } @@ -290,7 +290,7 @@ static void screen_user_menu_draw(const bContext *C, Menu *menu) } if (!ok) { if (show_missing) { - SNPRINTF(label, "Missing: %s.%s", umi_pr->context_data_path, umi_pr->prop_id); + SNPRINTF(label, TIP_("Missing: %s.%s"), umi_pr->context_data_path, umi_pr->prop_id); uiItemL(menu->layout, label, ICON_NONE); } } diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 8056e02d17a..5c4ba67d72a 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -166,7 +166,8 @@ static int screenshot_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (use_crop) { area = CTX_wm_area(C); bScreen *screen = CTX_wm_screen(C); - ScrArea *area_test = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, event->x, event->y); + ScrArea *area_test = BKE_screen_find_area_xy( + screen, SPACE_TYPE_ANY, event->xy[0], event->xy[1]); if (area_test != NULL) { area = area_test; } @@ -270,9 +271,10 @@ void SCREEN_OT_screenshot(wmOperatorType *ot) void SCREEN_OT_screenshot_area(wmOperatorType *ot) { - ot->name = "Save Screenshot (Area)"; + /* NOTE: the term "area" is a Blender internal name, "Editor" makes more sense for the UI. */ + ot->name = "Save Screenshot (Editor)"; ot->idname = "SCREEN_OT_screenshot_area"; - ot->description = "Capture a picture of the active area"; + ot->description = "Capture a picture of an editor"; screen_screenshot_impl(ot); diff --git a/source/blender/editors/screen/workspace_edit.c b/source/blender/editors/screen/workspace_edit.c index b99cb831bee..4b81e713080 100644 --- a/source/blender/editors/screen/workspace_edit.c +++ b/source/blender/editors/screen/workspace_edit.c @@ -310,7 +310,14 @@ static int workspace_append_activate_exec(bContext *C, wmOperator *op) RNA_string_get(op->ptr, "filepath", filepath); WorkSpace *appended_workspace = (WorkSpace *)WM_file_append_datablock( - bmain, CTX_data_scene(C), CTX_data_view_layer(C), CTX_wm_view3d(C), filepath, ID_WS, idname); + bmain, + CTX_data_scene(C), + CTX_data_view_layer(C), + CTX_wm_view3d(C), + filepath, + ID_WS, + idname, + BLO_LIBLINK_APPEND_RECURSIVE); if (appended_workspace) { /* Set defaults. */ diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index ab2b2f4b16b..dafc7f45e2e 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1103,7 +1103,7 @@ static void cursor_draw_point_with_symmetry(const uint gpuattr, float location[3], symm_rot_mat[4][4]; for (int i = 0; i <= symm; i++) { - if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5))))) { /* Axis Symmetry. */ flip_v3_v3(location, true_location, (char)i); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index b5113681955..6ba5c43f698 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -863,8 +863,8 @@ static int grab_clone_invoke(bContext *C, wmOperator *op, const wmEvent *event) cmv = MEM_callocN(sizeof(GrabClone), "GrabClone"); copy_v2_v2(cmv->startoffset, brush->clone.offset); - cmv->startx = event->x; - cmv->starty = event->y; + cmv->startx = event->xy[0]; + cmv->starty = event->xy[1]; op->customdata = cmv; WM_event_add_modal_handler(C, op); @@ -890,7 +890,7 @@ static int grab_clone_modal(bContext *C, wmOperator *op, const wmEvent *event) /* mouse moved, so move the clone image */ UI_view2d_region_to_view( ®ion->v2d, cmv->startx - xmin, cmv->starty - ymin, &startfx, &startfy); - UI_view2d_region_to_view(®ion->v2d, event->x - xmin, event->y - ymin, &fx, &fy); + UI_view2d_region_to_view(®ion->v2d, event->xy[0] - xmin, event->xy[1] - ymin, &fx, &fy); delta[0] = fx - startfx; delta[1] = fy - startfy; diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index a35e248a78c..63f61b6c5c1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -916,8 +916,6 @@ static bool paint_2d_ensure_tile_canvas(ImagePaintState *s, int i) s->tiles[i].cache.lastdiameter = -1; - s->tiles[i].iuser.ok = true; - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, &s->tiles[i].iuser, NULL); if (ibuf != NULL) { if (ibuf->channels != 4) { @@ -1683,7 +1681,6 @@ void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) for (int i = 0; i < s->num_tiles; i++) { s->tiles[i].iuser = sima->iuser; } - s->tiles[0].iuser.ok = true; zero_v2(s->tiles[0].uv_origin); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index a58b1947b0c..89263bec7b1 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -1659,7 +1659,9 @@ static float project_paint_uvpixel_mask(const ProjPaintState *ps, if (other_tpage && (ibuf_other = BKE_image_acquire_ibuf(other_tpage, NULL, NULL))) { const MLoopTri *lt_other = &ps->mlooptri_eval[tri_index]; - const float *lt_other_tri_uv[3] = {PS_LOOPTRI_AS_UV_3(ps->poly_to_loop_uv, lt_other)}; + const float *lt_other_tri_uv[3] = {ps->mloopuv_stencil_eval[lt_other->tri[0]].uv, + ps->mloopuv_stencil_eval[lt_other->tri[1]].uv, + ps->mloopuv_stencil_eval[lt_other->tri[2]].uv}; /* #BKE_image_acquire_ibuf - TODO: this may be slow. */ uchar rgba_ub[4]; @@ -5260,7 +5262,7 @@ static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), void *ph_v color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush_alpha; if (is_floatbuf) { - /* convert to premultipied */ + /* Convert to premutliplied. */ mul_v3_fl(color_f, color_f[3]); IMB_blend_color_float( projPixel->pixel.f_pt, projPixel->origColor.f_pt, color_f, ps->blend); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index f08771292a8..c6b519f711f 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -233,7 +233,8 @@ static bool palette_poll(bContext *C) { Paint *paint = BKE_paint_get_active_from_context(C); - if (paint && paint->palette != NULL) { + if (paint && paint->palette != NULL && !ID_IS_LINKED(paint->palette) && + !ID_IS_OVERRIDE_LIBRARY(paint->palette)) { return true; } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 9387b84f437..fede01a614b 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -295,7 +295,7 @@ static uint vpaint_blend(const VPaint *vp, uint color_blend = ED_vpaint_blend_tool(blend, color_curr, color_paint, alpha_i); - /* if no accumulate, clip color adding with colorig & orig alpha */ + /* If no accumulate, clip color adding with `color_orig` & `color_test`. */ if (!brush_use_accumulate(vp)) { uint color_test, a; char *cp, *ct, *co; @@ -2332,7 +2332,7 @@ static void wpaint_do_symmetrical_brush_actions( /* symm is a bit combination of XYZ - 1 is mirror * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ for (i = 1; i <= symm; i++) { - if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + if ((symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5))))) { cache->mirror_symmetry_pass = i; cache->radial_symmetry_pass = 0; SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); @@ -3350,7 +3350,7 @@ static void vpaint_do_symmetrical_brush_actions( /* symm is a bit combination of XYZ - 1 is mirror * X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ for (i = 1; i <= symm; i++) { - if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))) { + if (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5)))) { cache->mirror_symmetry_pass = i; cache->radial_symmetry_pass = 0; SCULPT_cache_calc_brushdata_symm(cache, i, 0, 0); diff --git a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c index 9f023dd6e63..f95e1d8d89c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_color_ops.c @@ -48,7 +48,7 @@ static bool vertex_weight_paint_mode_poll(bContext *C) { Object *ob = CTX_data_active_object(C); Mesh *me = BKE_mesh_from_object(ob); - return (ob && (ob->mode == OB_MODE_VERTEX_PAINT || ob->mode == OB_MODE_WEIGHT_PAINT)) && + return (ob && (ELEM(ob->mode, OB_MODE_VERTEX_PAINT, OB_MODE_WEIGHT_PAINT))) && (me && me->totpoly && me->dvert); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c index cb8dc838422..1077f66f10c 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c +++ b/source/blender/editors/sculpt_paint/paint_vertex_weight_ops.c @@ -339,8 +339,8 @@ static const EnumPropertyItem *weight_paint_sample_enum_itemf(bContext *C, uint index; const int mval[2] = { - win->eventstate->x - vc.region->winrct.xmin, - win->eventstate->y - vc.region->winrct.ymin, + win->eventstate->xy[0] - vc.region->winrct.xmin, + win->eventstate->xy[1] - vc.region->winrct.ymin, }; view3d_operator_needs_opengl(C); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 84f1907fb11..7d97830a698 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -30,6 +30,7 @@ #include "BLI_gsqueue.h" #include "BLI_hash.h" #include "BLI_math.h" +#include "BLI_math_color.h" #include "BLI_math_color_blend.h" #include "BLI_task.h" #include "BLI_utildefines.h" @@ -1168,7 +1169,7 @@ int SCULPT_nearest_vertex_get( bool SCULPT_is_symmetry_iteration_valid(char i, char symm) { - return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5))); + return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (!ELEM(i, 3, 5)))); } /* Checks if a vertex is inside the brush radius from any of its mirrored axis. */ @@ -7818,8 +7819,7 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) /* Restore the mesh before continuing with anchored stroke. */ if ((brush->flag & BRUSH_ANCHORED) || - ((brush->sculpt_tool == SCULPT_TOOL_GRAB || - brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) && + ((ELEM(brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ELASTIC_DEFORM)) && BKE_brush_use_size_pressure(brush)) || (brush->flag & BRUSH_DRAG_DOT)) { @@ -8181,7 +8181,7 @@ static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent /* For tablet rotation. */ ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); - if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { + if (ignore_background_click && !over_mesh(C, op, event->xy[0], event->xy[1])) { paint_stroke_free(C, op); return OPERATOR_PASS_THROUGH; } @@ -8808,10 +8808,12 @@ static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op)) for (int j = 0; j < c_poly->totloop; j++) { int loop_index = c_poly->loopstart + j; MLoop *c_loop = &loops[c_poly->loopstart + j]; - loopcols[loop_index].r = (char)(vertcols[c_loop->v].color[0] * 255); - loopcols[loop_index].g = (char)(vertcols[c_loop->v].color[1] * 255); - loopcols[loop_index].b = (char)(vertcols[c_loop->v].color[2] * 255); - loopcols[loop_index].a = (char)(vertcols[c_loop->v].color[3] * 255); + float srgb_color[4]; + linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color); + loopcols[loop_index].r = (char)(srgb_color[0] * 255); + loopcols[loop_index].g = (char)(srgb_color[1] * 255); + loopcols[loop_index].b = (char)(srgb_color[2] * 255); + loopcols[loop_index].a = (char)(srgb_color[3] * 255); } } @@ -8885,6 +8887,7 @@ static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op)) vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f); vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f); vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f); + srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color); } } diff --git a/source/blender/editors/sculpt_paint/sculpt_cloth.c b/source/blender/editors/sculpt_paint/sculpt_cloth.c index 1db2dd638f0..45929de4144 100644 --- a/source/blender/editors/sculpt_paint/sculpt_cloth.c +++ b/source/blender/editors/sculpt_paint/sculpt_cloth.c @@ -1528,7 +1528,7 @@ static int sculpt_cloth_filter_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } - const float len = event->prevclickx - event->x; + const float len = event->prev_click_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_detail.c b/source/blender/editors/sculpt_paint/sculpt_detail.c index 9cea7aa3e72..f3c7aadd7c5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_detail.c +++ b/source/blender/editors/sculpt_paint/sculpt_detail.c @@ -306,7 +306,7 @@ static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wm switch (event->type) { case LEFTMOUSE: if (event->val == KM_PRESS) { - const int ss_co[2] = {event->x, event->y}; + const int ss_co[2] = {event->xy[0], event->xy[1]}; int mode = RNA_enum_get(op->ptr, "mode"); sample_detail(C, ss_co[0], ss_co[1], mode); diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 20af95ba3cc..6acbe08c7f5 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -465,7 +465,7 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "topology dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "topology dist"); SculptFloodFill flood; SCULPT_floodfill_init(ss, &flood); @@ -515,7 +515,7 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_malloc_arrayN(sizeof(float), totvert, "normal dist"); + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "normal dist"); float *edge_factor = MEM_callocN(sizeof(float) * totvert, "mask edge factor"); for (int i = 0; i < totvert; i++) { edge_factor[i] = 1.0f; @@ -560,7 +560,7 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_malloc_arrayN(sizeof(float), totvert, "spherical dist"); + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "spherical dist"); for (int i = 0; i < totvert; i++) { dists[i] = FLT_MAX; } @@ -591,7 +591,7 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist"); BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); GSQueue *queue = BLI_gsqueue_new(sizeof(int)); @@ -652,7 +652,7 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) { SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "spherical dist"); /* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialized for * Multires. It also does not make sense to implement it for dyntopo as the result will be the @@ -863,7 +863,7 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, SculptSession *ss = ob->sculpt; const int totvert = SCULPT_vertex_count_get(ss); - float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "topology dist"); + float *dists = MEM_calloc_arrayN(totvert, sizeof(float), "topology dist"); BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); SculptFloodFill flood; diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c index 027cedf3b01..4997e929e51 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.c +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c @@ -1233,7 +1233,7 @@ static void sculpt_face_set_edit_fair_face_set(Object *ob, const int totvert = SCULPT_vertex_count_get(ss); Mesh *mesh = ob->data; - bool *fair_vertices = MEM_malloc_arrayN(sizeof(bool), totvert, "fair vertices"); + bool *fair_vertices = MEM_malloc_arrayN(totvert, sizeof(bool), "fair vertices"); SCULPT_boundary_info_ensure(ob); diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_color.c b/source/blender/editors/sculpt_paint/sculpt_filter_color.c index 7ebc0630c14..1eed0c88836 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_color.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_color.c @@ -238,7 +238,7 @@ static int sculpt_color_filter_modal(bContext *C, wmOperator *op, const wmEvent return OPERATOR_RUNNING_MODAL; } - const float len = event->prevclickx - event->x; + const float len = event->prev_click_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f; float fill_color[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c index 7abea1ba538..abccb57c92a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c +++ b/source/blender/editors/sculpt_paint/sculpt_filter_mesh.c @@ -511,7 +511,7 @@ static void mesh_filter_init_limit_surface_co(SculptSession *ss) FilterCache *filter_cache = ss->filter_cache; filter_cache->limit_surface_co = MEM_malloc_arrayN( - sizeof(float[3]), totvert, "limit surface co"); + totvert, sizeof(float[3]), "limit surface co"); for (int i = 0; i < totvert; i++) { SCULPT_vertex_limit_surface_get(ss, i, filter_cache->limit_surface_co[i]); } @@ -528,7 +528,7 @@ static void mesh_filter_sharpen_init(SculptSession *ss, filter_cache->sharpen_smooth_ratio = smooth_ratio; filter_cache->sharpen_intensify_detail_strength = intensify_detail_strength; filter_cache->sharpen_curvature_smooth_iterations = curvature_smooth_iterations; - filter_cache->sharpen_factor = MEM_malloc_arrayN(sizeof(float), totvert, "sharpen factor"); + filter_cache->sharpen_factor = MEM_malloc_arrayN(totvert, sizeof(float), "sharpen factor"); filter_cache->detail_directions = MEM_malloc_arrayN( totvert, sizeof(float[3]), "sharpen detail direction"); @@ -624,7 +624,7 @@ static int sculpt_mesh_filter_modal(bContext *C, wmOperator *op, const wmEvent * return OPERATOR_RUNNING_MODAL; } - const float len = event->prevclickx - event->x; + const float len = event->prev_click_xy[0] - event->xy[0]; filter_strength = filter_strength * -len * 0.001f * UI_DPI_FAC; SCULPT_vertex_random_access_ensure(ss); diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 9f19341df29..22375f1b0dd 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -170,10 +170,10 @@ static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent * SculptSession *ss = ob->sculpt; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; ARegion *region = CTX_wm_region(C); - float prevclick_f[2]; - copy_v2_v2(prevclick_f, op->customdata); - const int prevclick[2] = {(int)prevclick_f[0], (int)prevclick_f[1]}; - int len = (int)len_v2v2_int(prevclick, event->mval); + float prev_click_f[2]; + copy_v2_v2(prev_click_f, op->customdata); + const int prev_click[2] = {(int)prev_click_f[0], (int)prev_click_f[1]}; + int len = (int)len_v2v2_int(prev_click, event->mval); len = abs(len); int mask_speed = RNA_int_get(op->ptr, "mask_speed"); int mask_expand_update_it = len / mask_speed; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index e364a413345..247df05a655 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -399,7 +399,12 @@ void SCULPT_smooth(Sculpt *sd, void SCULPT_do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { SculptSession *ss = ob->sculpt; - if (ss->cache->bstrength <= 0.0f) { + + /* NOTE: The enhance brush needs to initialize its state on the first brush step. The stroke + * strength can become 0 during the stroke, but it can not change sign (the sign is determined + * in the beginning of the stroke. So here it is important to not switch to enhance brush in the + * middle of the stroke. */ + if (ss->cache->bstrength < 0.0f) { /* Invert mode, intensify details. */ SCULPT_enhance_details_brush(sd, ob, nodes, totnode); } diff --git a/source/blender/editors/sculpt_paint/sculpt_transform.c b/source/blender/editors/sculpt_paint/sculpt_transform.c index d85404ae517..b63272aa95b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_transform.c +++ b/source/blender/editors/sculpt_paint/sculpt_transform.c @@ -311,7 +311,7 @@ static int sculpt_set_pivot_position_exec(bContext *C, wmOperator *op) else if (mode == SCULPT_PIVOT_POSITION_ACTIVE_VERTEX) { copy_v3_v3(ss->pivot_pos, SCULPT_active_vertex_co_get(ss)); } - /* Pivot to raycast surface. */ + /* Pivot to ray-cast surface. */ else if (mode == SCULPT_PIVOT_POSITION_CURSOR_SURFACE) { float stroke_location[3]; float mouse[2]; diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 717d87c4972..a4fd2d81778 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -71,7 +71,7 @@ /* ACTION CREATION */ /* Helper function to find the active AnimData block from the Action Editor context */ -AnimData *ED_actedit_animdata_from_context(bContext *C) +AnimData *ED_actedit_animdata_from_context(bContext *C, ID **r_adt_id_owner) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); Object *ob = CTX_data_active_object(C); @@ -82,12 +82,18 @@ AnimData *ED_actedit_animdata_from_context(bContext *C) /* Currently, "Action Editor" means object-level only... */ if (ob) { adt = ob->adt; + if (r_adt_id_owner) { + *r_adt_id_owner = &ob->id; + } } } else if (saction->mode == SACTCONT_SHAPEKEY) { Key *key = BKE_key_from_object(ob); if (key) { adt = key->adt; + if (r_adt_id_owner) { + *r_adt_id_owner = &key->id; + } } } @@ -212,6 +218,7 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) bAction *oldact = NULL; AnimData *adt = NULL; + ID *adt_id_owner = NULL; /* hook into UI */ UI_context_active_but_prop_get_templateID(C, &ptr, &prop); @@ -225,13 +232,14 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) /* stash the old action to prevent it from being lost */ if (ptr.type == &RNA_AnimData) { adt = ptr.data; + adt_id_owner = ptr.owner_id; } else if (ptr.type == &RNA_SpaceDopeSheetEditor) { - adt = ED_actedit_animdata_from_context(C); + adt = ED_actedit_animdata_from_context(C, &adt_id_owner); } } else { - adt = ED_actedit_animdata_from_context(C); + adt = ED_actedit_animdata_from_context(C, &adt_id_owner); oldact = adt->action; } { @@ -239,8 +247,9 @@ static int action_new_exec(bContext *C, wmOperator *UNUSED(op)) /* Perform stashing operation - But only if there is an action */ if (adt && oldact) { + BLI_assert(adt_id_owner != NULL); /* stash the action */ - if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ptr.owner_id))) { + if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) { /* The stash operation will remove the user already * (and unlink the action from the AnimData action slot). * Hence, we must unset the ref to the action in the @@ -306,7 +315,7 @@ static bool action_pushdown_poll(bContext *C) { if (ED_operator_action_active(C)) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); /* Check for AnimData, Actions, and that tweak-mode is off. */ if (adt && saction->action) { @@ -326,7 +335,8 @@ static bool action_pushdown_poll(bContext *C) static int action_pushdown_exec(bContext *C, wmOperator *op) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + ID *adt_id_owner = NULL; + AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner); /* Do the deed... */ if (adt) { @@ -339,8 +349,7 @@ static int action_pushdown_exec(bContext *C, wmOperator *op) } /* action can be safely added */ - const Object *ob = CTX_data_active_object(C); - BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(ob)); + BKE_nla_action_pushdown(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner)); /* Stop displaying this action in this editor * NOTE: The editor itself doesn't set a user... @@ -373,7 +382,8 @@ void ACTION_OT_push_down(wmOperatorType *ot) static int action_stash_exec(bContext *C, wmOperator *op) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + ID *adt_id_owner = NULL; + AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner); /* Perform stashing operation */ if (adt) { @@ -385,8 +395,7 @@ static int action_stash_exec(bContext *C, wmOperator *op) } /* stash the action */ - Object *ob = CTX_data_active_object(C); - if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ob))) { + if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) { /* The stash operation will remove the user already, * so the flushing step later shouldn't double up * the user-count fixes. Hence, we must unset this ref @@ -439,7 +448,7 @@ void ACTION_OT_stash(wmOperatorType *ot) static bool action_stash_create_poll(bContext *C) { if (ED_operator_action_active(C)) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); /* Check tweak-mode is off (as you don't want to be tampering with the action in that case) */ /* NOTE: unlike for pushdown, @@ -471,7 +480,8 @@ static bool action_stash_create_poll(bContext *C) static int action_stash_create_exec(bContext *C, wmOperator *op) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + ID *adt_id_owner = NULL; + AnimData *adt = ED_actedit_animdata_from_context(C, &adt_id_owner); /* Check for no action... */ if (saction->action == NULL) { @@ -488,8 +498,7 @@ static int action_stash_create_exec(bContext *C, wmOperator *op) } /* stash the action */ - Object *ob = CTX_data_active_object(C); - if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(ob))) { + if (BKE_nla_action_stash(adt, ID_IS_OVERRIDE_LIBRARY(adt_id_owner))) { bAction *new_action = NULL; /* Create new action not based on the old one @@ -636,7 +645,7 @@ static bool action_unlink_poll(bContext *C) { if (ED_operator_action_active(C)) { SpaceAction *saction = (SpaceAction *)CTX_wm_space_data(C); - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); /* Only when there's an active action, in the right modes... */ if (saction->action && adt) { @@ -650,7 +659,7 @@ static bool action_unlink_poll(bContext *C) static int action_unlink_exec(bContext *C, wmOperator *op) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); bool force_delete = RNA_boolean_get(op->ptr, "force_delete"); if (adt && adt->action) { @@ -775,7 +784,7 @@ static bool action_layer_next_poll(bContext *C) { /* Action Editor's action editing modes only */ if (ED_operator_action_active(C)) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); if (adt) { /* only allow if we're in tweak-mode, and there's something above us... */ if (adt->flag & ADT_NLA_EDIT_ON) { @@ -809,7 +818,7 @@ static bool action_layer_next_poll(bContext *C) static int action_layer_next_exec(bContext *C, wmOperator *op) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); NlaTrack *act_track; Scene *scene = CTX_data_scene(C); @@ -886,7 +895,7 @@ static bool action_layer_prev_poll(bContext *C) { /* Action Editor's action editing modes only */ if (ED_operator_action_active(C)) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); if (adt) { if (adt->flag & ADT_NLA_EDIT_ON) { /* Tweak Mode: We need to check if there are any tracks below the active one @@ -920,7 +929,7 @@ static bool action_layer_prev_poll(bContext *C) static int action_layer_prev_exec(bContext *C, wmOperator *op) { - AnimData *adt = ED_actedit_animdata_from_context(C); + AnimData *adt = ED_actedit_animdata_from_context(C, NULL); NlaTrack *act_track; NlaTrack *nlt; diff --git a/source/blender/editors/space_action/action_edit.c b/source/blender/editors/space_action/action_edit.c index 3e38be243c9..f68446b1cae 100644 --- a/source/blender/editors/space_action/action_edit.c +++ b/source/blender/editors/space_action/action_edit.c @@ -276,7 +276,7 @@ static int actkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) scene = ac.scene; /* set the range directly */ - get_keyframe_extents(&ac, &min, &max, false); + get_keyframe_extents(&ac, &min, &max, true); scene->r.flag |= SCER_PRV_RANGE; scene->r.psfra = floorf(min); scene->r.pefra = ceilf(max); @@ -295,7 +295,7 @@ static int actkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) void ACTION_OT_previewrange_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Auto-Set Preview Range"; + ot->name = "Set Preview Range to Selected"; ot->idname = "ACTION_OT_previewrange_set"; ot->description = "Set Preview Range based on extents of selected Keyframes"; diff --git a/source/blender/editors/space_action/space_action.c b/source/blender/editors/space_action/space_action.c index f59429ba981..738eeb21e2e 100644 --- a/source/blender/editors/space_action/space_action.c +++ b/source/blender/editors/space_action/space_action.c @@ -247,7 +247,7 @@ static void action_main_region_draw_overlay(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME, true); + ED_time_scrub_draw_current_frame(region, scene, saction->flag & SACTION_DRAWTIME); /* scrollers */ UI_view2d_scrollers_draw(v2d, NULL); diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index b3b3eafb6e7..149067a94fe 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -177,6 +177,7 @@ void ED_spacemacros_init(void) ED_operatormacros_gpencil(); /* Register dropboxes (can use macros). */ + ED_dropboxes_ui(); const ListBase *spacetypes = BKE_spacetypes_list(); LISTBASE_FOREACH (const SpaceType *, type, spacetypes) { if (type->dropboxes) { @@ -263,9 +264,9 @@ void ED_region_draw_cb_exit(ARegionType *art, void *handle) } } -void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) +static void ed_region_draw_cb_draw(const bContext *C, ARegion *region, ARegionType *art, int type) { - LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, ®ion->type->drawcalls) { + LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &art->drawcalls) { if (rdc->type == type) { rdc->draw(C, region, rdc->customdata); @@ -275,6 +276,16 @@ void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) } } +void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) +{ + ed_region_draw_cb_draw(C, region, region->type, type); +} + +void ED_region_surface_draw_cb_draw(ARegionType *art, int type) +{ + ed_region_draw_cb_draw(NULL, NULL, art, type); +} + void ED_region_draw_cb_remove_by_type(ARegionType *art, void *draw_fn, void (*free)(void *)) { LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &art->drawcalls) { diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 91b0677ebaa..600b04f8563 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -242,55 +242,55 @@ static bool buttons_context_path_data(ButsContextPath *path, int type) PointerRNA *ptr = &path->ptr[path->len - 1]; /* if we already have a data, we're done */ - if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (type == -1 || type == OB_MESH)) { + if (RNA_struct_is_a(ptr->type, &RNA_Mesh) && (ELEM(type, -1, OB_MESH))) { return true; } if (RNA_struct_is_a(ptr->type, &RNA_Curve) && (type == -1 || ELEM(type, OB_CURVE, OB_SURF, OB_FONT))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (type == -1 || type == OB_ARMATURE)) { + if (RNA_struct_is_a(ptr->type, &RNA_Armature) && (ELEM(type, -1, OB_ARMATURE))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (type == -1 || type == OB_MBALL)) { + if (RNA_struct_is_a(ptr->type, &RNA_MetaBall) && (ELEM(type, -1, OB_MBALL))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (type == -1 || type == OB_LATTICE)) { + if (RNA_struct_is_a(ptr->type, &RNA_Lattice) && (ELEM(type, -1, OB_LATTICE))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (type == -1 || type == OB_CAMERA)) { + if (RNA_struct_is_a(ptr->type, &RNA_Camera) && (ELEM(type, -1, OB_CAMERA))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Light) && (type == -1 || type == OB_LAMP)) { + if (RNA_struct_is_a(ptr->type, &RNA_Light) && (ELEM(type, -1, OB_LAMP))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (type == -1 || type == OB_SPEAKER)) { + if (RNA_struct_is_a(ptr->type, &RNA_Speaker) && (ELEM(type, -1, OB_SPEAKER))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (type == -1 || type == OB_LIGHTPROBE)) { + if (RNA_struct_is_a(ptr->type, &RNA_LightProbe) && (ELEM(type, -1, OB_LIGHTPROBE))) { return true; } - if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (type == -1 || type == OB_GPENCIL)) { + if (RNA_struct_is_a(ptr->type, &RNA_GreasePencil) && (ELEM(type, -1, OB_GPENCIL))) { return true; } #ifdef WITH_HAIR_NODES - if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (type == -1 || type == OB_HAIR)) { + if (RNA_struct_is_a(ptr->type, &RNA_Hair) && (ELEM(type, -1, OB_HAIR))) { return true; } #endif #ifdef WITH_POINT_CLOUD - if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (type == -1 || type == OB_POINTCLOUD)) { + if (RNA_struct_is_a(ptr->type, &RNA_PointCloud) && (ELEM(type, -1, OB_POINTCLOUD))) { return true; } #endif - if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (type == -1 || type == OB_VOLUME)) { + if (RNA_struct_is_a(ptr->type, &RNA_Volume) && (ELEM(type, -1, OB_VOLUME))) { return true; } /* try to get an object in the path, no pinning supported here */ if (buttons_context_path_object(path)) { Object *ob = path->ptr[path->len - 1].data; - if (ob && (type == -1 || type == ob->type)) { + if (ob && (ELEM(type, -1, ob->type))) { RNA_id_pointer_create(ob->data, &path->ptr[path->len]); path->len++; diff --git a/source/blender/editors/space_clip/clip_dopesheet_draw.c b/source/blender/editors/space_clip/clip_dopesheet_draw.c index 8aaf3faffec..42fda3ef464 100644 --- a/source/blender/editors/space_clip/clip_dopesheet_draw.c +++ b/source/blender/editors/space_clip/clip_dopesheet_draw.c @@ -224,7 +224,7 @@ void clip_draw_dopesheet_main(SpaceClip *sc, ARegion *region, Scene *scene) uint flags_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f( "ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); diff --git a/source/blender/editors/space_clip/clip_ops.c b/source/blender/editors/space_clip/clip_ops.c index f5e4c4d55d9..b1f8949871b 100644 --- a/source/blender/editors/space_clip/clip_ops.c +++ b/source/blender/editors/space_clip/clip_ops.c @@ -393,8 +393,8 @@ static void view_pan_init(bContext *C, wmOperator *op, const wmEvent *event) WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } - vpd->x = event->x; - vpd->y = event->y; + vpd->x = event->xy[0]; + vpd->y = event->xy[1]; if (clip_view_has_locked_selection(C)) { vpd->vec = &sc->xlockof; @@ -454,8 +454,8 @@ static int view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *event) SpaceClip *sc = CTX_wm_space_clip(C); float offset[2]; - offset[0] = (event->prevx - event->x) / sc->zoom; - offset[1] = (event->prevy - event->y) / sc->zoom; + offset[0] = (event->prev_xy[0] - event->xy[0]) / sc->zoom; + offset[1] = (event->prev_xy[1] - event->xy[1]) / sc->zoom; RNA_float_set_array(op->ptr, "offset", offset); @@ -478,8 +478,8 @@ static int view_pan_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: copy_v2_v2(vpd->vec, &vpd->xorig); - offset[0] = (vpd->x - event->x) / sc->zoom; - offset[1] = (vpd->y - event->y) / sc->zoom; + offset[0] = (vpd->x - event->xy[0]) / sc->zoom; + offset[1] = (vpd->y - event->xy[1]) / sc->zoom; RNA_float_set_array(op->ptr, "offset", offset); view_pan_exec(C, op); break; @@ -575,8 +575,8 @@ static void view_zoom_init(bContext *C, wmOperator *op, const wmEvent *event) vpd->timer_lastdraw = PIL_check_seconds_timer(); } - vpd->x = event->x; - vpd->y = event->y; + vpd->x = event->xy[0]; + vpd->y = event->xy[1]; vpd->zoom = sc->zoom; vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); @@ -619,7 +619,7 @@ static int view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { float delta, factor; - delta = event->prevx - event->x + event->prevy - event->y; + delta = event->prev_xy[0] - event->xy[0] + event->prev_xy[1] - event->xy[1]; if (U.uiflag & USER_ZOOM_INVERT) { delta *= -1; @@ -646,14 +646,14 @@ static void view_zoom_apply( if (U.viewzoom != USER_ZOOM_SCALE) { if (U.uiflag & USER_ZOOM_HORIZ) { - delta = (float)(event->x - vpd->x); + delta = (float)(event->xy[0] - vpd->x); } else { - delta = (float)(event->y - vpd->y); + delta = (float)(event->xy[1] - vpd->y); } } else { - delta = event->x - vpd->x + event->y - vpd->y; + delta = event->xy[0] - vpd->x + event->xy[1] - vpd->y; } delta /= U.pixelsize; diff --git a/source/blender/editors/space_clip/tracking_ops.c b/source/blender/editors/space_clip/tracking_ops.c index ff62bcf0cfa..ee0406cde30 100644 --- a/source/blender/editors/space_clip/tracking_ops.c +++ b/source/blender/editors/space_clip/tracking_ops.c @@ -181,8 +181,8 @@ static int add_marker_at_click_modal(bContext *C, wmOperator *UNUSED(op), const ED_clip_point_stable_pos(sc, region, - event->x - region->winrct.xmin, - event->y - region->winrct.ymin, + event->xy[0] - region->winrct.xmin, + event->xy[1] - region->winrct.ymin, &pos[0], &pos[1]); @@ -417,7 +417,7 @@ static SlideMarkerData *create_slide_marker_data(SpaceClip *sc, data->pos = marker->pos; data->offset = track->offset; data->old_markers = MEM_callocN(sizeof(*data->old_markers) * track->markersnr, - "slide marekrs"); + "slide markers"); for (int a = 0; a < track->markersnr; a++) { copy_v2_v2(data->old_markers[a], track->markers[a].pos); } diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c index 0a99d1020f6..0e78a3e9a1e 100644 --- a/source/blender/editors/space_clip/tracking_ops_track.c +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -196,8 +196,8 @@ static bool track_markers_initjob(bContext *C, TrackMarkersJob *tmj, bool backwa /* XXX: silly to store this, but this data is needed to update scene and * movie-clip numbers when tracking is finished. This introduces * better feedback for artists. - * Maybe there's another way to solve this problem, but can't think - * better way atm. + * Maybe there's another way to solve this problem, + * but can't think better way at the moment. * Anyway, this way isn't more unstable as animation rendering * animation which uses the same approach (except storing screen). */ diff --git a/source/blender/editors/space_console/space_console.c b/source/blender/editors/space_console/space_console.c index 47d15efb6ca..84be24e96ac 100644 --- a/source/blender/editors/space_console/space_console.c +++ b/source/blender/editors/space_console/space_console.c @@ -149,7 +149,7 @@ static void console_cursor(wmWindow *win, ScrArea *UNUSED(area), ARegion *region { int wmcursor = WM_CURSOR_TEXT_EDIT; const wmEvent *event = win->eventstate; - if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->x, event->y)) { + if (UI_view2d_mouse_in_scrollers(region, ®ion->v2d, event->xy)) { wmcursor = WM_CURSOR_DEFAULT; } diff --git a/source/blender/editors/space_file/CMakeLists.txt b/source/blender/editors/space_file/CMakeLists.txt index 993a52b9084..4b508f16c1e 100644 --- a/source/blender/editors/space_file/CMakeLists.txt +++ b/source/blender/editors/space_file/CMakeLists.txt @@ -34,6 +34,7 @@ set(INC ) set(SRC + asset_catalog_tree_view.cc file_draw.c file_ops.c file_panels.c @@ -49,6 +50,7 @@ set(SRC ) set(LIB + bf_blenkernel ) if(WITH_HEADLESS) diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc new file mode 100644 index 00000000000..b3b81c5e07f --- /dev/null +++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc @@ -0,0 +1,655 @@ +/* + * 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) 2007 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spfile + */ + +#include "DNA_space_types.h" + +#include "BKE_asset.h" +#include "BKE_asset_catalog.hh" +#include "BKE_asset_library.hh" + +#include "BLI_string_ref.hh" + +#include "BLT_translation.h" + +#include "ED_asset.h" +#include "ED_fileselect.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" +#include "UI_tree_view.hh" + +#include "WM_api.h" +#include "WM_types.h" + +#include "file_intern.h" +#include "filelist.h" + +using namespace blender; +using namespace blender::bke; + +namespace blender::ed::asset_browser { + +class AssetCatalogTreeView : public ui::AbstractTreeView { + ::AssetLibrary *asset_library_; + /** The asset catalog tree this tree-view represents. */ + bke::AssetCatalogTree *catalog_tree_; + FileAssetSelectParams *params_; + SpaceFile &space_file_; + + friend class AssetCatalogTreeViewItem; + friend class AssetCatalogDropController; + + public: + AssetCatalogTreeView(::AssetLibrary *library, + FileAssetSelectParams *params, + SpaceFile &space_file); + + void build_tree() override; + + private: + ui::BasicTreeViewItem &build_catalog_items_recursive(ui::TreeViewItemContainer &view_parent_item, + AssetCatalogTreeItem &catalog); + + void add_all_item(); + void add_unassigned_item(); + bool is_active_catalog(CatalogID catalog_id) const; +}; + +/* ---------------------------------------------------------------------- */ + +class AssetCatalogTreeViewItem : public ui::BasicTreeViewItem { + /** The catalog tree item this tree view item represents. */ + AssetCatalogTreeItem &catalog_item_; + + public: + AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item); + + void on_activate() override; + + void build_row(uiLayout &row) override; + void build_context_menu(bContext &C, uiLayout &column) const override; + + bool can_rename() const override; + bool rename(StringRefNull new_name) override; + + /** Add drag support for catalog items. */ + std::unique_ptr create_drag_controller() const override; + /** Add dropping support for catalog items. */ + std::unique_ptr create_drop_controller() const override; +}; + +class AssetCatalogDragController : public ui::AbstractTreeViewItemDragController { + AssetCatalogTreeItem &catalog_item_; + + public: + explicit AssetCatalogDragController(AssetCatalogTreeItem &catalog_item); + + int get_drag_type() const override; + void *create_drag_data() const override; +}; + +class AssetCatalogDropController : public ui::AbstractTreeViewItemDropController { + AssetCatalogTreeItem &catalog_item_; + + public: + AssetCatalogDropController(AssetCatalogTreeView &tree_view, AssetCatalogTreeItem &catalog_item); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const wmDrag &drag) const override; + bool on_drop(const wmDrag &drag) override; + + ::AssetLibrary &get_asset_library() const; + + static bool has_droppable_asset(const wmDrag &drag, const char **r_disabled_hint); + static bool drop_assets_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name = ""); + + private: + bool drop_asset_catalog_into_catalog(const wmDrag &drag); + std::string drop_tooltip_asset_list(const wmDrag &drag) const; + std::string drop_tooltip_asset_catalog(const wmDrag &drag) const; +}; + +/** Only reason this isn't just `BasicTreeViewItem` is to add a '+' icon for adding a root level + * catalog. */ +class AssetCatalogTreeViewAllItem : public ui::BasicTreeViewItem { + using BasicTreeViewItem::BasicTreeViewItem; + + void build_row(uiLayout &row) override; +}; + +class AssetCatalogTreeViewUnassignedItem : public ui::BasicTreeViewItem { + using BasicTreeViewItem::BasicTreeViewItem; + + struct DropController : public ui::AbstractTreeViewItemDropController { + DropController(AssetCatalogTreeView &tree_view); + + bool can_drop(const wmDrag &drag, const char **r_disabled_hint) const override; + std::string drop_tooltip(const wmDrag &drag) const override; + bool on_drop(const wmDrag &drag) override; + }; + + std::unique_ptr create_drop_controller() const override; +}; + +/* ---------------------------------------------------------------------- */ + +AssetCatalogTreeView::AssetCatalogTreeView(::AssetLibrary *library, + FileAssetSelectParams *params, + SpaceFile &space_file) + : asset_library_(library), + catalog_tree_(BKE_asset_library_get_catalog_tree(library)), + params_(params), + space_file_(space_file) +{ +} + +void AssetCatalogTreeView::build_tree() +{ + add_all_item(); + + if (catalog_tree_) { + catalog_tree_->foreach_root_item([this](AssetCatalogTreeItem &item) { + ui::BasicTreeViewItem &child_view_item = build_catalog_items_recursive(*this, item); + + /* Open root-level items by default. */ + child_view_item.set_collapsed(false); + }); + } + + add_unassigned_item(); +} + +ui::BasicTreeViewItem &AssetCatalogTreeView::build_catalog_items_recursive( + ui::TreeViewItemContainer &view_parent_item, AssetCatalogTreeItem &catalog) +{ + ui::BasicTreeViewItem &view_item = view_parent_item.add_tree_item( + &catalog); + view_item.is_active([this, &catalog]() { return is_active_catalog(catalog.get_catalog_id()); }); + + catalog.foreach_child([&view_item, this](AssetCatalogTreeItem &child) { + build_catalog_items_recursive(view_item, child); + }); + return view_item; +} + +void AssetCatalogTreeView::add_all_item() +{ + FileAssetSelectParams *params = params_; + + AssetCatalogTreeViewAllItem &item = add_tree_item(IFACE_("All"), + ICON_HOME); + item.on_activate([params](ui::BasicTreeViewItem & /*item*/) { + params->asset_catalog_visibility = FILE_SHOW_ASSETS_ALL_CATALOGS; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + }); + item.is_active( + [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_ALL_CATALOGS; }); +} + +void AssetCatalogTreeView::add_unassigned_item() +{ + FileAssetSelectParams *params = params_; + + AssetCatalogTreeViewUnassignedItem &item = add_tree_item( + IFACE_("Unassigned"), ICON_FILE_HIDDEN); + + item.on_activate([params](ui::BasicTreeViewItem & /*item*/) { + params->asset_catalog_visibility = FILE_SHOW_ASSETS_WITHOUT_CATALOG; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); + }); + item.is_active( + [params]() { return params->asset_catalog_visibility == FILE_SHOW_ASSETS_WITHOUT_CATALOG; }); +} + +bool AssetCatalogTreeView::is_active_catalog(CatalogID catalog_id) const +{ + return (params_->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG) && + (params_->catalog_id == catalog_id); +} + +/* ---------------------------------------------------------------------- */ + +AssetCatalogTreeViewItem::AssetCatalogTreeViewItem(AssetCatalogTreeItem *catalog_item) + : BasicTreeViewItem(catalog_item->get_name()), catalog_item_(*catalog_item) +{ +} + +void AssetCatalogTreeViewItem::on_activate() +{ + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + tree_view.params_->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG; + tree_view.params_->catalog_id = catalog_item_.get_catalog_id(); + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, nullptr); +} + +void AssetCatalogTreeViewItem::build_row(uiLayout &row) +{ + const std::string label_override = catalog_item_.has_unsaved_changes() ? (label_ + "*") : label_; + add_label(row, label_override); + + if (!is_hovered()) { + return; + } + + uiButTreeRow *tree_row_but = tree_row_button(); + PointerRNA *props; + + props = UI_but_extra_operator_icon_add( + (uiBut *)tree_row_but, "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + RNA_string_set(props, "parent_path", catalog_item_.catalog_path().c_str()); +} + +void AssetCatalogTreeViewItem::build_context_menu(bContext &C, uiLayout &column) const +{ + PointerRNA props; + + uiItemFullO(&column, + "ASSET_OT_catalog_new", + "New Catalog", + ICON_NONE, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "parent_path", catalog_item_.catalog_path().c_str()); + + char catalog_id_str_buffer[UUID_STRING_LEN] = ""; + BLI_uuid_format(catalog_id_str_buffer, catalog_item_.get_catalog_id()); + uiItemFullO(&column, + "ASSET_OT_catalog_delete", + "Delete Catalog", + ICON_NONE, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "catalog_id", catalog_id_str_buffer); + uiItemO(&column, "Rename", ICON_NONE, "UI_OT_tree_view_item_rename"); + + /* Doesn't actually exist right now, but could be defined in Python. Reason that this isn't done + * in Python yet is that catalogs are not exposed in BPY, and we'd somehow pass the clicked on + * catalog to the menu draw callback (via context probably).*/ + MenuType *mt = WM_menutype_find("ASSETBROWSER_MT_catalog_context_menu", true); + if (!mt) { + return; + } + UI_menutype_draw(&C, mt, &column); +} + +bool AssetCatalogTreeViewItem::can_rename() const +{ + return true; +} + +bool AssetCatalogTreeViewItem::rename(StringRefNull new_name) +{ + /* Important to keep state. */ + BasicTreeViewItem::rename(new_name); + + const AssetCatalogTreeView &tree_view = static_cast( + get_tree_view()); + ED_asset_catalog_rename(tree_view.asset_library_, catalog_item_.get_catalog_id(), new_name); + return true; +} + +std::unique_ptr AssetCatalogTreeViewItem:: + create_drop_controller() const +{ + return std::make_unique( + static_cast(get_tree_view()), catalog_item_); +} + +std::unique_ptr AssetCatalogTreeViewItem:: + create_drag_controller() const +{ + return std::make_unique(catalog_item_); +} + +/* ---------------------------------------------------------------------- */ + +AssetCatalogDropController::AssetCatalogDropController(AssetCatalogTreeView &tree_view, + AssetCatalogTreeItem &catalog_item) + : ui::AbstractTreeViewItemDropController(tree_view), catalog_item_(catalog_item) +{ +} + +bool AssetCatalogDropController::can_drop(const wmDrag &drag, const char **r_disabled_hint) const +{ + if (drag.type == WM_DRAG_ASSET_CATALOG) { + /* Always supported. */ + return true; + } + if (drag.type == WM_DRAG_ASSET_LIST) { + return has_droppable_asset(drag, r_disabled_hint); + } + return false; +} + +std::string AssetCatalogDropController::drop_tooltip(const wmDrag &drag) const +{ + if (drag.type == WM_DRAG_ASSET_CATALOG) { + return drop_tooltip_asset_catalog(drag); + } + return drop_tooltip_asset_list(drag); +} + +std::string AssetCatalogDropController::drop_tooltip_asset_catalog(const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); + + const ::AssetLibrary *asset_library = tree_view().asset_library_; + bke::AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service(asset_library); + wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag); + AssetCatalog *src_catalog = catalog_service->find_catalog(catalog_drag->drag_catalog_id); + + return std::string(TIP_("Move Catalog")) + " '" + src_catalog->path.name() + "' " + + TIP_("into") + " '" + catalog_item_.get_name() + "'"; +} + +std::string AssetCatalogDropController::drop_tooltip_asset_list(const wmDrag &drag) const +{ + BLI_assert(drag.type == WM_DRAG_ASSET_LIST); + + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); + + /* Don't try to be smart by dynamically adding the 's' for the plural. Just makes translation + * harder, so use full literals. */ + std::string basic_tip = is_multiple_assets ? TIP_("Move assets to catalog") : + TIP_("Move asset to catalog"); + + return basic_tip + ": " + catalog_item_.get_name() + " (" + catalog_item_.catalog_path().str() + + ")"; +} + +bool AssetCatalogDropController::on_drop(const wmDrag &drag) +{ + if (drag.type == WM_DRAG_ASSET_CATALOG) { + return drop_asset_catalog_into_catalog(drag); + } + return drop_assets_into_catalog(tree_view(), + drag, + catalog_item_.get_catalog_id(), + catalog_item_.get_simple_name()); +} + +bool AssetCatalogDropController::drop_asset_catalog_into_catalog(const wmDrag &drag) +{ + BLI_assert(drag.type == WM_DRAG_ASSET_CATALOG); + wmDragAssetCatalog *catalog_drag = WM_drag_get_asset_catalog_data(&drag); + ED_asset_catalog_move( + &get_asset_library(), catalog_drag->drag_catalog_id, catalog_item_.get_catalog_id()); + + WM_main_add_notifier(NC_ASSET | ND_ASSET_CATALOGS, nullptr); + return true; +} + +bool AssetCatalogDropController::drop_assets_into_catalog(const AssetCatalogTreeView &tree_view, + const wmDrag &drag, + CatalogID catalog_id, + StringRefNull simple_name) +{ + BLI_assert(drag.type == WM_DRAG_ASSET_LIST); + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + if (!asset_drags) { + return false; + } + + LISTBASE_FOREACH (wmDragAssetListItem *, asset_item, asset_drags) { + if (asset_item->is_external) { + /* Only internal assets can be modified! */ + continue; + } + BKE_asset_metadata_catalog_id_set( + asset_item->asset_data.local_id->asset_data, catalog_id, simple_name.c_str()); + + /* Trigger re-run of filtering to update visible assets. */ + filelist_tag_needs_filtering(tree_view.space_file_.files); + file_select_deselect_all(&tree_view.space_file_, FILE_SEL_SELECTED | FILE_SEL_HIGHLIGHTED); + WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_LIST, nullptr); + } + + return true; +} + +bool AssetCatalogDropController::has_droppable_asset(const wmDrag &drag, + const char **r_disabled_hint) +{ + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + + *r_disabled_hint = nullptr; + /* There needs to be at least one asset from the current file. */ + LISTBASE_FOREACH (const wmDragAssetListItem *, asset_item, asset_drags) { + if (!asset_item->is_external) { + return true; + } + } + + *r_disabled_hint = "Only assets from this current file can be moved between catalogs"; + return false; +} + +::AssetLibrary &AssetCatalogDropController::get_asset_library() const +{ + return *tree_view().asset_library_; +} + +/* ---------------------------------------------------------------------- */ + +AssetCatalogDragController::AssetCatalogDragController(AssetCatalogTreeItem &catalog_item) + : catalog_item_(catalog_item) +{ +} + +int AssetCatalogDragController::get_drag_type() const +{ + return WM_DRAG_ASSET_CATALOG; +} + +void *AssetCatalogDragController::create_drag_data() const +{ + wmDragAssetCatalog *drag_catalog = (wmDragAssetCatalog *)MEM_callocN(sizeof(*drag_catalog), + __func__); + drag_catalog->drag_catalog_id = catalog_item_.get_catalog_id(); + return drag_catalog; +} + +/* ---------------------------------------------------------------------- */ + +void AssetCatalogTreeViewAllItem::build_row(uiLayout &row) +{ + ui::BasicTreeViewItem::build_row(row); + + PointerRNA *props; + + UI_but_extra_operator_icon_add( + (uiBut *)tree_row_button(), "ASSET_OT_catalogs_save", WM_OP_INVOKE_DEFAULT, ICON_FILE_TICK); + + props = UI_but_extra_operator_icon_add( + (uiBut *)tree_row_button(), "ASSET_OT_catalog_new", WM_OP_INVOKE_DEFAULT, ICON_ADD); + /* No parent path to use the root level. */ + RNA_string_set(props, "parent_path", nullptr); +} + +/* ---------------------------------------------------------------------- */ + +std::unique_ptr AssetCatalogTreeViewUnassignedItem:: + create_drop_controller() const +{ + return std::make_unique( + static_cast(get_tree_view())); +} + +AssetCatalogTreeViewUnassignedItem::DropController::DropController(AssetCatalogTreeView &tree_view) + : ui::AbstractTreeViewItemDropController(tree_view) +{ +} + +bool AssetCatalogTreeViewUnassignedItem::DropController::can_drop( + const wmDrag &drag, const char **r_disabled_hint) const +{ + if (drag.type != WM_DRAG_ASSET_LIST) { + return false; + } + return AssetCatalogDropController::has_droppable_asset(drag, r_disabled_hint); +} + +std::string AssetCatalogTreeViewUnassignedItem::DropController::drop_tooltip( + const wmDrag &drag) const +{ + const ListBase *asset_drags = WM_drag_asset_list_get(&drag); + const bool is_multiple_assets = !BLI_listbase_is_single(asset_drags); + + return is_multiple_assets ? TIP_("Move assets out of any catalog") : + TIP_("Move asset out of any catalog"); +} + +bool AssetCatalogTreeViewUnassignedItem::DropController::on_drop(const wmDrag &drag) +{ + /* Assign to nil catalog ID. */ + return AssetCatalogDropController::drop_assets_into_catalog( + tree_view(), drag, CatalogID{}); +} + +} // namespace blender::ed::asset_browser + +/* ---------------------------------------------------------------------- */ + +namespace blender::ed::asset_browser { + +class AssetCatalogFilterSettings { + public: + eFileSel_Params_AssetCatalogVisibility asset_catalog_visibility; + bUUID asset_catalog_id; + + std::unique_ptr catalog_filter; +}; + +} // namespace blender::ed::asset_browser + +using namespace blender::ed::asset_browser; + +FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings() +{ + AssetCatalogFilterSettings *filter_settings = OBJECT_GUARDED_NEW(AssetCatalogFilterSettings); + return reinterpret_cast(filter_settings); +} + +void file_delete_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle **filter_settings_handle) +{ + AssetCatalogFilterSettings **filter_settings = reinterpret_cast( + filter_settings_handle); + OBJECT_GUARDED_SAFE_DELETE(*filter_settings, AssetCatalogFilterSettings); +} + +/** + * \return True if the file list should update its filtered results (e.g. because filtering + * parameters changed). + */ +bool file_set_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + ::bUUID catalog_id) +{ + AssetCatalogFilterSettings *filter_settings = reinterpret_cast( + filter_settings_handle); + bool needs_update = false; + + if (filter_settings->asset_catalog_visibility != catalog_visibility) { + filter_settings->asset_catalog_visibility = catalog_visibility; + needs_update = true; + } + + if (filter_settings->asset_catalog_visibility == FILE_SHOW_ASSETS_FROM_CATALOG && + !BLI_uuid_equal(filter_settings->asset_catalog_id, catalog_id)) { + filter_settings->asset_catalog_id = catalog_id; + needs_update = true; + } + + return needs_update; +} + +void file_ensure_updated_catalog_filter_data( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const ::AssetLibrary *asset_library) +{ + AssetCatalogFilterSettings *filter_settings = reinterpret_cast( + filter_settings_handle); + const AssetCatalogService *catalog_service = BKE_asset_library_get_catalog_service( + asset_library); + + if (filter_settings->asset_catalog_visibility != FILE_SHOW_ASSETS_ALL_CATALOGS) { + filter_settings->catalog_filter = std::make_unique( + catalog_service->create_catalog_filter(filter_settings->asset_catalog_id)); + } +} + +bool file_is_asset_visible_in_catalog_filter_settings( + const FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const AssetMetaData *asset_data) +{ + const AssetCatalogFilterSettings *filter_settings = + reinterpret_cast(filter_settings_handle); + + switch (filter_settings->asset_catalog_visibility) { + case FILE_SHOW_ASSETS_WITHOUT_CATALOG: + return !filter_settings->catalog_filter->is_known(asset_data->catalog_id); + case FILE_SHOW_ASSETS_FROM_CATALOG: + return filter_settings->catalog_filter->contains(asset_data->catalog_id); + case FILE_SHOW_ASSETS_ALL_CATALOGS: + /* All asset files should be visible. */ + return true; + } + + BLI_assert_unreachable(); + return false; +} + +/* ---------------------------------------------------------------------- */ + +void file_create_asset_catalog_tree_view_in_layout(::AssetLibrary *asset_library, + uiLayout *layout, + SpaceFile *space_file, + FileAssetSelectParams *params) +{ + uiBlock *block = uiLayoutGetBlock(layout); + + UI_block_layout_set_current(block, layout); + + ui::AbstractTreeView *tree_view = UI_block_add_view( + *block, + "asset catalog tree view", + std::make_unique( + asset_library, params, *space_file)); + + ui::TreeViewBuilder builder(*block); + builder.build_tree_view(*tree_view); +} diff --git a/source/blender/editors/space_file/file_draw.c b/source/blender/editors/space_file/file_draw.c index 9a46579780e..66aabe39e44 100644 --- a/source/blender/editors/space_file/file_draw.c +++ b/source/blender/editors/space_file/file_draw.c @@ -190,6 +190,7 @@ static void file_draw_icon(const SpaceFile *sfile, UI_but_drag_set_asset(but, &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), + file->asset_data, asset_params->import_type, icon, preview_image, @@ -242,8 +243,9 @@ static void file_draw_string(int sx, } /** - * \param r_sx, r_sy: The lower right corner of the last line drawn. AKA the cursor position on - * completion. + * \param r_sx, r_sy: The lower right corner of the last line drawn, plus the height of the last + * line. This is the cursor position on completion to allow drawing more text + * behind that. */ static void file_draw_string_multiline(int sx, int sy, @@ -465,6 +467,17 @@ static void file_draw_preview(const SpaceFile *sfile, UI_icon_draw_ex(icon_x, icon_y, icon, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); } + const bool is_current_main_data = filelist_file_get_id(file) != NULL; + if (is_current_main_data) { + /* Smaller, fainter icon at the top-right indicating that the file represents data from the + * current file (from current #Main in fact). */ + float icon_x, icon_y; + const uchar light[4] = {255, 255, 255, 255}; + icon_x = xco + ex - UI_UNIT_X; + icon_y = yco + ey - UI_UNIT_Y; + UI_icon_draw_ex(icon_x, icon_y, ICON_FILE_BLEND, 1.0f / U.dpi_fac, 0.6f, 0.0f, light, false); + } + /* Contrasting outline around some preview types. */ if (show_outline) { GPUVertFormat *format = immVertexFormat(); @@ -504,6 +517,7 @@ static void file_draw_preview(const SpaceFile *sfile, UI_but_drag_set_asset(but, &(AssetHandle){.file_data = file}, BLI_strdup(blend_path), + file->asset_data, asset_params->import_type, icon, imb, @@ -1053,7 +1067,9 @@ void file_draw_list(const bContext *C, ARegion *region) layout->curr_size = params->thumbnail_size; } -static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion *region) +static void file_draw_invalid_library_hint(const bContext *C, + const SpaceFile *sfile, + ARegion *region) { const FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); @@ -1061,9 +1077,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion file_path_to_ui_path(asset_params->base_params.dir, library_ui_path, sizeof(library_ui_path)); uchar text_col[4]; - uchar text_alert_col[4]; UI_GetThemeColor4ubv(TH_TEXT, text_col); - UI_GetThemeColor4ubv(TH_REDALERT, text_alert_col); const View2D *v2d = ®ion->v2d; const int pad = sfile->layout->tile_border_x; @@ -1074,23 +1088,42 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion int sy = v2d->tot.ymax; { - const char *message = TIP_("Library not found"); - const int draw_string_str_len = strlen(message) + 2 + sizeof(library_ui_path); - char *draw_string = alloca(draw_string_str_len); - BLI_snprintf(draw_string, draw_string_str_len, "%s: %s", message, library_ui_path); - file_draw_string_multiline(sx, sy, draw_string, width, line_height, text_alert_col, NULL, &sy); + const char *message = TIP_("Path to asset library does not exist:"); + file_draw_string_multiline(sx, sy, message, width, line_height, text_col, NULL, &sy); + + sy -= line_height; + file_draw_string(sx, sy, library_ui_path, width, line_height, UI_STYLE_TEXT_LEFT, text_col); } - /* Next line, but separate it a bit further. */ - sy -= line_height; + /* Separate a bit further. */ + sy -= line_height * 2.2f; { UI_icon_draw(sx, sy - UI_UNIT_Y, ICON_INFO); const char *suggestion = TIP_( - "Set up the library or edit libraries in the Preferences, File Paths section"); + "Asset Libraries are local directories that can contain .blend files with assets inside.\n" + "Manage Asset Libraries from the File Paths section in Preferences"); file_draw_string_multiline( - sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, NULL); + sx + UI_UNIT_X, sy, suggestion, width - UI_UNIT_X, line_height, text_col, NULL, &sy); + + uiBlock *block = UI_block_begin(C, region, __func__, UI_EMBOSS); + uiBut *but = uiDefIconTextButO(block, + UI_BTYPE_BUT, + "SCREEN_OT_userpref_show", + WM_OP_INVOKE_DEFAULT, + ICON_PREFERENCES, + NULL, + sx + UI_UNIT_X, + sy - line_height - UI_UNIT_Y * 1.2f, + UI_UNIT_X * 8, + UI_UNIT_Y, + NULL); + PointerRNA *but_opptr = UI_but_operator_ptr_get(but); + RNA_enum_set(but_opptr, "section", USER_SECTION_FILE_PATHS); + + UI_block_end(C, block); + UI_block_draw(C, block); } } @@ -1098,7 +1131,7 @@ static void file_draw_invalid_library_hint(const SpaceFile *sfile, const ARegion * Draw a string hint if the file list is invalid. * \return true if the list is invalid and a hint was drawn. */ -bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) +bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region) { FileAssetSelectParams *asset_params = ED_fileselect_get_asset_params(sfile); /* Only for asset browser. */ @@ -1111,7 +1144,7 @@ bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region) return false; } - file_draw_invalid_library_hint(sfile, region); + file_draw_invalid_library_hint(C, sfile, region); return true; } diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 905c0aeb8e0..4be5d6d8008 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -23,12 +23,21 @@ #pragma once +#include "DNA_space_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + /* internal exports only */ struct ARegion; struct ARegionType; +struct AssetLibrary; struct FileSelectParams; +struct FileAssetSelectParams; struct SpaceFile; +struct uiLayout; struct View2D; /* file_draw.c */ @@ -38,7 +47,7 @@ struct View2D; void file_calc_previews(const bContext *C, ARegion *region); void file_draw_list(const bContext *C, ARegion *region); -bool file_draw_hint_if_invalid(const SpaceFile *sfile, const ARegion *region); +bool file_draw_hint_if_invalid(const bContext *C, const SpaceFile *sfile, ARegion *region); void file_draw_check_ex(bContext *C, struct ScrArea *area); void file_draw_check(bContext *C); @@ -70,6 +79,7 @@ void FILE_OT_directory_new(struct wmOperatorType *ot); void FILE_OT_previous(struct wmOperatorType *ot); void FILE_OT_next(struct wmOperatorType *ot); void FILE_OT_refresh(struct wmOperatorType *ot); +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot); void FILE_OT_filenum(struct wmOperatorType *ot); void FILE_OT_delete(struct wmOperatorType *ot); void FILE_OT_rename(struct wmOperatorType *ot); @@ -147,8 +157,40 @@ void file_on_reload_callback_register(struct SpaceFile *sfile, /* file_panels.c */ void file_tool_props_region_panels_register(struct ARegionType *art); void file_execute_region_panels_register(struct ARegionType *art); +void file_tools_region_panels_register(struct ARegionType *art); /* file_utils.c */ void file_tile_boundbox(const ARegion *region, FileLayout *layout, const int file, rcti *r_bounds); void file_path_to_ui_path(const char *path, char *r_pathi, int max_size); + +/* asset_catalog_tree_view.cc */ + +/* C-handle for #ed::asset_browser::AssetCatalogFilterSettings. */ +typedef struct FileAssetCatalogFilterSettingsHandle FileAssetCatalogFilterSettingsHandle; + +FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings(void); +void file_delete_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle **filter_settings_handle); +/** + * \return True if the stored filter settings were modified. + */ +bool file_set_asset_catalog_filter_settings( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + bUUID catalog_id); +void file_ensure_updated_catalog_filter_data( + FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const struct AssetLibrary *asset_library); +bool file_is_asset_visible_in_catalog_filter_settings( + const FileAssetCatalogFilterSettingsHandle *filter_settings_handle, + const AssetMetaData *asset_data); + +void file_create_asset_catalog_tree_view_in_layout(struct AssetLibrary *asset_library, + struct uiLayout *layout, + struct SpaceFile *space_file, + struct FileAssetSelectParams *params); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/editors/space_file/file_ops.c b/source/blender/editors/space_file/file_ops.c index 2f1acd2ca4d..844514759f3 100644 --- a/source/blender/editors/space_file/file_ops.c +++ b/source/blender/editors/space_file/file_ops.c @@ -536,7 +536,7 @@ static rcti file_select_mval_to_select_rect(const int mval[2]) return rect; } -static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static int file_select_exec(bContext *C, wmOperator *op) { ARegion *region = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); @@ -549,17 +549,27 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) const bool only_activate_if_selected = RNA_boolean_get(op->ptr, "only_activate_if_selected"); /* Used so right mouse clicks can do both, activate and spawn the context menu. */ const bool pass_through = RNA_boolean_get(op->ptr, "pass_through"); + bool wait_to_deselect_others = RNA_boolean_get(op->ptr, "wait_to_deselect_others"); if (region->regiontype != RGN_TYPE_WINDOW) { return OPERATOR_CANCELLED; } - rect = file_select_mval_to_select_rect(event->mval); + int mval[2]; + mval[0] = RNA_int_get(op->ptr, "mouse_x"); + mval[1] = RNA_int_get(op->ptr, "mouse_y"); + rect = file_select_mval_to_select_rect(mval); if (!ED_fileselect_layout_is_inside_pt(sfile->layout, ®ion->v2d, rect.xmin, rect.ymin)) { return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH; } + if (extend || fill) { + wait_to_deselect_others = false; + } + + int ret_val = OPERATOR_FINISHED; + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); if (sfile && params) { int idx = params->highlight_file; @@ -571,6 +581,9 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (only_activate_if_selected && is_selected) { /* Don't deselect other items. */ } + else if (wait_to_deselect_others && is_selected) { + ret_val = OPERATOR_RUNNING_MODAL; + } /* single select, deselect all selected first */ else if (!extend) { file_select_deselect_all(sfile, FILE_SEL_SELECTED); @@ -601,7 +614,10 @@ static int file_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) WM_event_add_mousemove(CTX_wm_window(C)); /* for directory changes */ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); - return pass_through ? (OPERATOR_FINISHED | OPERATOR_PASS_THROUGH) : OPERATOR_FINISHED; + if ((ret_val == OPERATOR_FINISHED) && pass_through) { + ret_val |= OPERATOR_PASS_THROUGH; + } + return ret_val; } void FILE_OT_select(wmOperatorType *ot) @@ -614,11 +630,14 @@ void FILE_OT_select(wmOperatorType *ot) ot->description = "Handle mouse clicks to select and activate items"; /* api callbacks */ - ot->invoke = file_select_invoke; + ot->invoke = WM_generic_select_invoke; + ot->exec = file_select_exec; + ot->modal = WM_generic_select_modal; /* Operator works for file or asset browsing */ ot->poll = ED_operator_file_active; /* properties */ + WM_operator_properties_generic_select(ot); prop = RNA_def_boolean(ot->srna, "extend", false, @@ -1430,7 +1449,7 @@ static int file_highlight_invoke(bContext *C, wmOperator *UNUSED(op), const wmEv ARegion *region = CTX_wm_region(C); SpaceFile *sfile = CTX_wm_space_file(C); - if (!file_highlight_set(sfile, region, event->x, event->y)) { + if (!file_highlight_set(sfile, region, event->xy[0], event->xy[1])) { return OPERATOR_PASS_THROUGH; } @@ -1931,8 +1950,36 @@ void FILE_OT_refresh(struct wmOperatorType *ot) /* api callbacks */ ot->exec = file_refresh_exec; - /* Operator works for file or asset browsing */ - ot->poll = ED_operator_file_active; /* <- important, handler is on window level */ + ot->poll = ED_operator_file_browsing_active; /* <- important, handler is on window level */ +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Refresh Asset Library Operator + * \{ */ + +static int file_asset_library_refresh_exec(bContext *C, wmOperator *UNUSED(unused)) +{ + wmWindowManager *wm = CTX_wm_manager(C); + SpaceFile *sfile = CTX_wm_space_file(C); + + ED_fileselect_clear(wm, sfile); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + + return OPERATOR_FINISHED; +} + +void FILE_OT_asset_library_refresh(struct wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Refresh Asset Library"; + ot->description = "Reread assets and asset catalogs from the asset library on disk"; + ot->idname = "FILE_OT_asset_library_refresh"; + + /* api callbacks */ + ot->exec = file_asset_library_refresh_exec; + ot->poll = ED_operator_asset_browsing_active; } /** \} */ @@ -2444,12 +2491,14 @@ static void file_expand_directory(bContext *C) if (BLI_path_is_rel(params->dir)) { /* Use of 'default' folder here is just to avoid an error message on '//' prefix. */ BLI_path_abs(params->dir, - G.relbase_valid ? BKE_main_blendfile_path(bmain) : BKE_appdir_folder_default()); + G.relbase_valid ? BKE_main_blendfile_path(bmain) : + BKE_appdir_folder_default_or_root()); } else if (params->dir[0] == '~') { char tmpstr[sizeof(params->dir) - 1]; BLI_strncpy(tmpstr, params->dir + 1, sizeof(tmpstr)); - BLI_join_dirfile(params->dir, sizeof(params->dir), BKE_appdir_folder_default(), tmpstr); + BLI_path_join( + params->dir, sizeof(params->dir), BKE_appdir_folder_default_or_root(), tmpstr, NULL); } else if (params->dir[0] == '\0') diff --git a/source/blender/editors/space_file/file_panels.c b/source/blender/editors/space_file/file_panels.c index 7032d55b331..0e468718a04 100644 --- a/source/blender/editors/space_file/file_panels.c +++ b/source/blender/editors/space_file/file_panels.c @@ -47,6 +47,7 @@ #include "WM_types.h" #include "file_intern.h" +#include "filelist.h" #include "fsmenu.h" #include @@ -57,6 +58,12 @@ static bool file_panel_operator_poll(const bContext *C, PanelType *UNUSED(pt)) return (sfile && sfile->op); } +static bool file_panel_asset_browsing_poll(const bContext *C, PanelType *UNUSED(pt)) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + return sfile && sfile->files && ED_fileselect_is_asset_browser(sfile); +} + static void file_panel_operator_header(const bContext *C, Panel *panel) { SpaceFile *sfile = CTX_wm_space_file(C); @@ -222,3 +229,42 @@ void file_execute_region_panels_register(ARegionType *art) pt->draw = file_panel_execution_buttons_draw; BLI_addtail(&art->paneltypes, pt); } + +static void file_panel_asset_catalog_buttons_draw(const bContext *C, Panel *panel) +{ + bScreen *screen = CTX_wm_screen(C); + SpaceFile *sfile = CTX_wm_space_file(C); + /* May be null if the library wasn't loaded yet. */ + struct AssetLibrary *asset_library = filelist_asset_library(sfile->files); + FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile); + BLI_assert(params != NULL); + + uiLayout *col = uiLayoutColumn(panel->layout, false); + uiLayout *row = uiLayoutRow(col, true); + + PointerRNA params_ptr; + RNA_pointer_create(&screen->id, &RNA_FileAssetSelectParams, params, ¶ms_ptr); + + uiItemR(row, ¶ms_ptr, "asset_library_ref", 0, "", ICON_NONE); + if (params->asset_library_ref.type != ASSET_LIBRARY_LOCAL) { + uiItemO(row, "", ICON_FILE_REFRESH, "FILE_OT_asset_library_refresh"); + } + + uiItemS(col); + + file_create_asset_catalog_tree_view_in_layout(asset_library, col, sfile, params); +} + +void file_tools_region_panels_register(ARegionType *art) +{ + PanelType *pt; + + pt = MEM_callocN(sizeof(PanelType), "spacetype file asset catalog buttons"); + strcpy(pt->idname, "FILE_PT_asset_catalog_buttons"); + strcpy(pt->label, N_("Asset Catalogs")); + strcpy(pt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); + pt->flag = PANEL_TYPE_NO_HEADER; + pt->poll = file_panel_asset_browsing_poll; + pt->draw = file_panel_asset_catalog_buttons_draw; + BLI_addtail(&art->paneltypes, pt); +} diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 511b5b255e9..a73fa2b9740 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -50,12 +50,14 @@ #include "BLI_task.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_uuid.h" #ifdef WIN32 # include "BLI_winstuff.h" #endif #include "BKE_asset.h" +#include "BKE_asset_library.h" #include "BKE_context.h" #include "BKE_global.h" #include "BKE_icons.h" @@ -87,6 +89,7 @@ #include "atomic_ops.h" +#include "file_intern.h" #include "filelist.h" #define FILEDIR_NBR_ENTRIES_UNSET -1 @@ -368,6 +371,8 @@ typedef struct FileListFilter { char filter_glob[FILE_MAXFILE]; char filter_search[66]; /* + 2 for heading/trailing implicit '*' wildcards. */ short flags; + + FileAssetCatalogFilterSettingsHandle *asset_catalog_filter; } FileListFilter; /* FileListFilter.flags */ @@ -386,6 +391,7 @@ typedef struct FileList { eFileSelectType type; /* The library this list was created for. Stored here so we know when to re-read. */ AssetLibraryReference *asset_library_ref; + struct AssetLibrary *asset_library; /* Non-owning pointer. */ short flags; @@ -420,6 +426,8 @@ typedef struct FileList { /* Filter an entry of current filelist. */ bool (*filter_fn)(struct FileListInternEntry *, const char *, FileListFilter *); + /* Executed before filtering individual items, to set up additional filter data. */ + void (*prepare_filter_fn)(const struct FileList *, FileListFilter *); short tags; /* FileListTags */ } FileList; @@ -427,11 +435,14 @@ typedef struct FileList { /* FileList.flags */ enum { FL_FORCE_RESET = 1 << 0, - FL_IS_READY = 1 << 1, - FL_IS_PENDING = 1 << 2, - FL_NEED_SORTING = 1 << 3, - FL_NEED_FILTERING = 1 << 4, - FL_SORT_INVERT = 1 << 5, + /* Don't do a full reset (unless #FL_FORCE_RESET is also set), only reset files representing main + * data (assets from the current file/#Main). */ + FL_FORCE_RESET_MAIN_FILES = 1 << 1, + FL_IS_READY = 1 << 2, + FL_IS_PENDING = 1 << 3, + FL_NEED_SORTING = 1 << 4, + FL_NEED_FILTERING = 1 << 5, + FL_SORT_INVERT = 1 << 6, }; /* FileList.tags */ @@ -471,6 +482,10 @@ static void filelist_readjob_dir(struct FileListReadJob *job_params, short *stop, short *do_update, float *progress); +static void filelist_readjob_asset_library(struct FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress); static void filelist_readjob_main_assets(struct FileListReadJob *job_params, short *stop, short *do_update, @@ -480,7 +495,6 @@ static void filelist_readjob_main_assets(struct FileListReadJob *job_params, static int groupname_to_code(const char *group); static uint64_t groupname_to_filter_id(const char *group); -static void filelist_filter_clear(FileList *filelist); static void filelist_cache_clear(FileListEntryCache *cache, size_t new_size); /* ********** Sort helpers ********** */ @@ -710,7 +724,7 @@ void filelist_sort(struct FileList *filelist) sort_cb, &(struct FileSortData){.inverted = (filelist->flags & FL_SORT_INVERT) != 0}); - filelist_filter_clear(filelist); + filelist_tag_needs_filtering(filelist); filelist->flags &= ~FL_NEED_SORTING; } } @@ -803,105 +817,201 @@ static bool is_filtered_hidden(const char *filename, return false; } +/** + * Apply the filter string as file path matching pattern. + * \return true when the file should be in the result set, false if it should be filtered out. */ +static bool is_filtered_file_relpath(const FileListInternEntry *file, const FileListFilter *filter) +{ + if (filter->filter_search[0] == '\0') { + return true; + } + + /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ + return fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) == 0; +} + +/** \return true when the file should be in the result set, false if it should be filtered out. */ +static bool is_filtered_file_type(const FileListInternEntry *file, const FileListFilter *filter) +{ + if (is_filtered_hidden(file->relpath, filter, file)) { + return false; + } + + if (FILENAME_IS_CURRPAR(file->relpath)) { + return false; + } + + /* We only check for types if some type are enabled in filtering. */ + if (filter->filter && (filter->flags & FLF_DO_FILTER)) { + if (file->typeflag & FILE_TYPE_DIR) { + if (file->typeflag & (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { + if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { + return false; + } + } + else { + if (!(filter->filter & FILE_TYPE_FOLDER)) { + return false; + } + } + } + else { + if (!(file->typeflag & filter->filter)) { + return false; + } + } + } + return true; +} + +/** \return true when the file should be in the result set, false if it should be filtered out. */ static bool is_filtered_file(FileListInternEntry *file, const char *UNUSED(root), FileListFilter *filter) { - bool is_filtered = !is_filtered_hidden(file->relpath, filter, file); + return is_filtered_file_type(file, filter) && is_filtered_file_relpath(file, filter); +} - if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { - /* We only check for types if some type are enabled in filtering. */ - if (filter->filter && (filter->flags & FLF_DO_FILTER)) { - if (file->typeflag & FILE_TYPE_DIR) { - if (file->typeflag & - (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { - is_filtered = false; - } - } - else { - if (!(filter->filter & FILE_TYPE_FOLDER)) { - is_filtered = false; - } - } +static bool is_filtered_id_file_type(const FileListInternEntry *file, + const char *id_group, + const char *name, + const FileListFilter *filter) +{ + if (!is_filtered_file_type(file, filter)) { + return false; + } + + /* We only check for types if some type are enabled in filtering. */ + if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { + if (id_group) { + if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { + return false; } - else { - if (!(file->typeflag & filter->filter)) { - is_filtered = false; - } - } - } - /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ - if (is_filtered && (filter->filter_search[0] != '\0')) { - if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { - is_filtered = false; + + uint64_t filter_id = groupname_to_filter_id(id_group); + if (!(filter_id & filter->filter_id)) { + return false; } } } - return is_filtered; + return true; } -static bool is_filtered_id_file(const FileListInternEntry *file, - const char *id_group, - const char *name, - const FileListFilter *filter) +/** + * Get the asset metadata of a file, if it represents an asset. This may either be of a local ID + * (ID in the current #Main) or read from an external asset library. + */ +static AssetMetaData *filelist_file_internal_get_asset_data(const FileListInternEntry *file) { - bool is_filtered = !is_filtered_hidden(file->relpath, filter, file); - if (is_filtered && !FILENAME_IS_CURRPAR(file->relpath)) { - /* We only check for types if some type are enabled in filtering. */ - if ((filter->filter || filter->filter_id) && (filter->flags & FLF_DO_FILTER)) { - if (file->typeflag & FILE_TYPE_DIR) { - if (file->typeflag & - (FILE_TYPE_BLENDERLIB | FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) { - if (!(filter->filter & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP))) { - is_filtered = false; - } - } - else { - if (!(filter->filter & FILE_TYPE_FOLDER)) { - is_filtered = false; - } - } - } - if (is_filtered && id_group) { - if (!name && (filter->flags & FLF_HIDE_LIB_DIR)) { - is_filtered = false; - } - else { - uint64_t filter_id = groupname_to_filter_id(id_group); - if (!(filter_id & filter->filter_id)) { - is_filtered = false; - } - } - } - } - /* If there's a filter string, apply it as filter even if FLF_DO_FILTER is not set. */ - if (is_filtered && (filter->filter_search[0] != '\0')) { - if (fnmatch(filter->filter_search, file->relpath, FNM_CASEFOLD) != 0) { - is_filtered = false; - } - } + const ID *local_id = file->local_data.id; + return local_id ? local_id->asset_data : file->imported_asset_data; +} + +static void prepare_filter_asset_library(const FileList *filelist, FileListFilter *filter) +{ + /* Not used yet for the asset view template. */ + if (!filter->asset_catalog_filter) { + return; } - return is_filtered; + file_ensure_updated_catalog_filter_data(filter->asset_catalog_filter, filelist->asset_library); } -static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +/** + * Copy a string from source to `dest`, but prefix and suffix it with a single space. + * Assumes `dest` has at least space enough for the two spaces. + */ +static void tag_copy_with_spaces(char *dest, const char *source, const size_t dest_size) +{ + BLI_assert(dest_size > 2); + const size_t source_length = BLI_strncpy_rlen(dest + 1, source, dest_size - 2); + dest[0] = ' '; + dest[source_length + 1] = ' '; + dest[source_length + 2] = '\0'; +} + +/** + * Return whether at least one tag matches the search filter. + * Tags are searched as "entire words", so instead of searching for "tag" in the + * filter string, this function searches for " tag ". Assumes the search filter + * starts and ends with a space. + * + * Here the tags on the asset are written in set notation: + * + * `asset_tag_matches_filter(" some tags ", {"some", "blue"})` -> true + * `asset_tag_matches_filter(" some tags ", {"som", "tag"})` -> false + * `asset_tag_matches_filter(" some tags ", {})` -> false + */ +static bool asset_tag_matches_filter(const char *filter_search, const AssetMetaData *asset_data) +{ + LISTBASE_FOREACH (const AssetTag *, asset_tag, &asset_data->tags) { + char tag_name[MAX_NAME + 2]; /* sizeof(AssetTag::name) + 2 */ + tag_copy_with_spaces(tag_name, asset_tag->name, sizeof(tag_name)); + if (BLI_strcasestr(filter_search, tag_name) != NULL) { + return true; + } + } + return false; +} + +static bool is_filtered_asset(FileListInternEntry *file, FileListFilter *filter) +{ + const AssetMetaData *asset_data = filelist_file_internal_get_asset_data(file); + + /* Not used yet for the asset view template. */ + if (filter->asset_catalog_filter && !file_is_asset_visible_in_catalog_filter_settings( + filter->asset_catalog_filter, asset_data)) { + return false; + } + + if (filter->filter_search[0] == '\0') { + /* If there is no filter text, everything matches. */ + return true; + } + + /* filter->filter_search contains "*the search text*". */ + char filter_search[66]; /* sizeof(FileListFilter::filter_search) */ + const size_t string_length = STRNCPY_RLEN(filter_search, filter->filter_search); + + /* When doing a name comparison, get rid of the leading/trailing asterisks. */ + filter_search[string_length - 1] = '\0'; + if (BLI_strcasestr(file->name, filter_search + 1) != NULL) { + return true; + } + + /* Replace the asterisks with spaces, so that we can do matching on " sometag "; that way + * an artist searching for "redder" doesn't result in a match for the tag "red". */ + filter_search[string_length - 1] = ' '; + filter_search[0] = ' '; + + return asset_tag_matches_filter(filter_search, asset_data); +} + +static bool is_filtered_lib_type(FileListInternEntry *file, + const char *root, + FileListFilter *filter) { - bool is_filtered; char path[FILE_MAX_LIBEXTRA], dir[FILE_MAX_LIBEXTRA], *group, *name; BLI_join_dirfile(path, sizeof(path), root, file->relpath); if (BLO_library_path_explode(path, dir, &group, &name)) { - is_filtered = is_filtered_id_file(file, group, name, filter); - } - else { - is_filtered = is_filtered_file(file, root, filter); + return is_filtered_id_file_type(file, group, name, filter); } + return is_filtered_file_type(file, filter); +} - return is_filtered; +static bool is_filtered_lib(FileListInternEntry *file, const char *root, FileListFilter *filter) +{ + return is_filtered_lib_type(file, root, filter) && is_filtered_file_relpath(file, filter); +} + +static bool is_filtered_asset_library(FileListInternEntry *file, + const char *root, + FileListFilter *filter) +{ + return is_filtered_lib_type(file, root, filter) && is_filtered_asset(file, filter); } static bool is_filtered_main(FileListInternEntry *file, @@ -916,10 +1026,11 @@ static bool is_filtered_main_assets(FileListInternEntry *file, FileListFilter *filter) { /* "Filtered" means *not* being filtered out... So return true if the file should be visible. */ - return is_filtered_id_file(file, file->relpath, file->name, filter); + return is_filtered_id_file_type(file, file->relpath, file->name, filter) && + is_filtered_asset(file, filter); } -static void filelist_filter_clear(FileList *filelist) +void filelist_tag_needs_filtering(FileList *filelist) { filelist->flags |= FL_NEED_FILTERING; } @@ -949,6 +1060,10 @@ void filelist_filter(FileList *filelist) } } + if (filelist->prepare_filter_fn) { + filelist->prepare_filter_fn(filelist, &filelist->filter_data); + } + filtered_tmp = MEM_mallocN(sizeof(*filtered_tmp) * (size_t)num_files, __func__); /* Filter remap & count how many files are left after filter in a single loop. */ @@ -1027,7 +1142,29 @@ void filelist_setfilter_options(FileList *filelist, if (update) { /* And now, free filtered data so that we know we have to filter again. */ - filelist_filter_clear(filelist); + filelist_tag_needs_filtering(filelist); + } +} + +/** + * \param catalog_id: The catalog that should be filtered by if \a catalog_visibility is + * #FILE_SHOW_ASSETS_FROM_CATALOG. May be NULL otherwise. + */ +void filelist_set_asset_catalog_filter_options( + FileList *filelist, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + const bUUID *catalog_id) +{ + if (!filelist->filter_data.asset_catalog_filter) { + /* There's no filter data yet. */ + filelist->filter_data.asset_catalog_filter = file_create_asset_catalog_filter_settings(); + } + + const bool needs_update = file_set_asset_catalog_filter_settings( + filelist->filter_data.asset_catalog_filter, catalog_visibility, *catalog_id); + + if (needs_update) { + filelist_tag_needs_filtering(filelist); } } @@ -1298,6 +1435,11 @@ int ED_file_icon(const FileDirEntry *file) filelist_geticon_ex(file, NULL, false, false); } +static bool filelist_intern_entry_is_main_file(const FileListInternEntry *intern_entry) +{ + return intern_entry->local_data.id != NULL; +} + /* ********** Main ********** */ static void parent_dir_until_exists_or_default_root(char *dir) @@ -1393,8 +1535,6 @@ static void filelist_direntryarr_free(FileDirEntryArr *array) #endif array->nbr_entries = FILEDIR_NBR_ENTRIES_UNSET; array->nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET; - array->entry_idx_start = -1; - array->entry_idx_end = -1; } static void filelist_intern_entry_free(FileListInternEntry *entry) @@ -1428,6 +1568,26 @@ static void filelist_intern_free(FileListIntern *filelist_intern) MEM_SAFE_FREE(filelist_intern->filtered); } +/** + * \return the number of main files removed. + */ +static int filelist_intern_free_main_files(FileListIntern *filelist_intern) +{ + int removed_counter = 0; + LISTBASE_FOREACH_MUTABLE (FileListInternEntry *, entry, &filelist_intern->entries) { + if (!filelist_intern_entry_is_main_file(entry)) { + continue; + } + + BLI_remlink(&filelist_intern->entries, entry); + filelist_intern_entry_free(entry); + removed_counter++; + } + + MEM_SAFE_FREE(filelist_intern->filtered); + return removed_counter; +} + static void filelist_cache_preview_runf(TaskPool *__restrict pool, void *taskdata) { FileListEntryCache *cache = BLI_task_pool_user_data(pool); @@ -1723,9 +1883,17 @@ void filelist_settype(FileList *filelist, short type) filelist->read_job_fn = filelist_readjob_lib; filelist->filter_fn = is_filtered_lib; break; + case FILE_ASSET_LIBRARY: + filelist->check_dir_fn = filelist_checkdir_lib; + filelist->read_job_fn = filelist_readjob_asset_library; + filelist->prepare_filter_fn = prepare_filter_asset_library; + filelist->filter_fn = is_filtered_asset_library; + filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA; + break; case FILE_MAIN_ASSET: filelist->check_dir_fn = filelist_checkdir_main_assets; filelist->read_job_fn = filelist_readjob_main_assets; + filelist->prepare_filter_fn = prepare_filter_asset_library; filelist->filter_fn = is_filtered_main_assets; filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS; break; @@ -1739,13 +1907,23 @@ void filelist_settype(FileList *filelist, short type) filelist->flags |= FL_FORCE_RESET; } -void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection) +static void filelist_clear_asset_library(FileList *filelist) +{ + /* The AssetLibraryService owns the AssetLibrary pointer, so no need for us to free it. */ + filelist->asset_library = NULL; + file_delete_asset_catalog_filter_settings(&filelist->filter_data.asset_catalog_filter); +} + +void filelist_clear_ex(struct FileList *filelist, + const bool do_asset_library, + const bool do_cache, + const bool do_selection) { if (!filelist) { return; } - filelist_filter_clear(filelist); + filelist_tag_needs_filtering(filelist); if (do_cache) { filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size); @@ -1758,11 +1936,63 @@ void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const boo if (do_selection && filelist->selection_state) { BLI_ghash_clear(filelist->selection_state, NULL, NULL); } + + if (do_asset_library) { + filelist_clear_asset_library(filelist); + } } -void filelist_clear(struct FileList *filelist) +static void filelist_clear_main_files(FileList *filelist, + const bool do_asset_library, + const bool do_cache, + const bool do_selection) { - filelist_clear_ex(filelist, true, true); + if (!filelist || !(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) { + return; + } + + filelist_tag_needs_filtering(filelist); + + if (do_cache) { + filelist_cache_clear(&filelist->filelist_cache, filelist->filelist_cache.size); + } + + const int removed_files = filelist_intern_free_main_files(&filelist->filelist_intern); + + filelist->filelist.nbr_entries -= removed_files; + filelist->filelist.nbr_entries_filtered = FILEDIR_NBR_ENTRIES_UNSET; + BLI_assert(filelist->filelist.nbr_entries > FILEDIR_NBR_ENTRIES_UNSET); + + if (do_selection && filelist->selection_state) { + BLI_ghash_clear(filelist->selection_state, NULL, NULL); + } + + if (do_asset_library) { + filelist_clear_asset_library(filelist); + } +} + +void filelist_clear(FileList *filelist) +{ + filelist_clear_ex(filelist, true, true, true); +} + +/** + * A "smarter" version of #filelist_clear() that calls partial clearing based on the filelist + * force-reset flags. + */ +void filelist_clear_from_reset_tag(FileList *filelist) +{ + /* Do a full clear if needed. */ + if (filelist->flags & FL_FORCE_RESET) { + filelist_clear(filelist); + return; + } + + if (filelist->flags & FL_FORCE_RESET_MAIN_FILES) { + filelist_clear_main_files(filelist, false, true, false); + return; + } } void filelist_free(struct FileList *filelist) @@ -1773,7 +2003,7 @@ void filelist_free(struct FileList *filelist) } /* No need to clear cache & selection_state, we free them anyway. */ - filelist_clear_ex(filelist, false, false); + filelist_clear_ex(filelist, true, false, false); filelist_cache_free(&filelist->filelist_cache); if (filelist->selection_state) { @@ -1788,6 +2018,11 @@ void filelist_free(struct FileList *filelist) filelist->flags &= ~(FL_NEED_SORTING | FL_NEED_FILTERING); } +AssetLibrary *filelist_asset_library(FileList *filelist) +{ + return filelist->asset_library; +} + void filelist_freelib(struct FileList *filelist) { if (filelist->libfiledata) { @@ -1881,7 +2116,7 @@ void filelist_setrecursion(struct FileList *filelist, const int recursion_level) bool filelist_needs_force_reset(FileList *filelist) { - return (filelist->flags & FL_FORCE_RESET) != 0; + return (filelist->flags & (FL_FORCE_RESET | FL_FORCE_RESET_MAIN_FILES)) != 0; } void filelist_tag_force_reset(FileList *filelist) @@ -1889,6 +2124,14 @@ void filelist_tag_force_reset(FileList *filelist) filelist->flags |= FL_FORCE_RESET; } +void filelist_tag_force_reset_mainfiles(FileList *filelist) +{ + if (!(filelist->tags & FILELIST_TAGS_USES_MAIN_DATA)) { + return; + } + filelist->flags |= FL_FORCE_RESET_MAIN_FILES; +} + bool filelist_is_ready(struct FileList *filelist) { return (filelist->flags & FL_IS_READY) != 0; @@ -2618,9 +2861,10 @@ int ED_file_extension_icon(const char *path) } } -int filelist_needs_reading(struct FileList *filelist) +int filelist_needs_reading(FileList *filelist) { - return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET); + return (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET) || + filelist_needs_force_reset(filelist); } uint filelist_entry_select_set(const FileList *filelist, @@ -2879,76 +3123,129 @@ static int filelist_readjob_list_dir(const char *root, return nbr_entries; } -static int filelist_readjob_list_lib(const char *root, ListBase *entries, const bool skip_currpar) +typedef enum ListLibOptions { + /* Will read both the groups + actual ids from the library. Reduces the amount of times that + * a library needs to be opened. */ + LIST_LIB_RECURSIVE = (1 << 0), + + /* Will only list assets. */ + LIST_LIB_ASSETS_ONLY = (1 << 1), + + /* Add given root as result. */ + LIST_LIB_ADD_PARENT = (1 << 2), +} ListLibOptions; + +static FileListInternEntry *filelist_readjob_list_lib_group_create(const int idcode, + const char *group_name) { - FileListInternEntry *entry; - LinkNode *ln, *names = NULL, *datablock_infos = NULL; - int i, nitems, idcode = 0, nbr_entries = 0; - char dir[FILE_MAX_LIBEXTRA], *group; - bool ok; + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + entry->relpath = BLI_strdup(group_name); + entry->typeflag |= FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR; + entry->blentype = idcode; + return entry; +} - struct BlendHandle *libfiledata = NULL; - - /* name test */ - ok = BLO_library_path_explode(root, dir, &group, NULL); - if (!ok) { - return nbr_entries; - } - - /* there we go */ - BlendFileReadReport bf_reports = {.reports = NULL}; - libfiledata = BLO_blendhandle_from_file(dir, &bf_reports); - if (libfiledata == NULL) { - return nbr_entries; - } - - /* memory for strings is passed into filelist[i].entry->relpath - * and freed in filelist_entry_free. */ - if (group) { - idcode = groupname_to_code(group); - datablock_infos = BLO_blendhandle_get_datablock_info(libfiledata, idcode, &nitems); - } - else { - names = BLO_blendhandle_get_linkable_groups(libfiledata); - nitems = BLI_linklist_count(names); - } - - BLO_blendhandle_close(libfiledata); - - if (!skip_currpar) { - entry = MEM_callocN(sizeof(*entry), __func__); - entry->relpath = BLI_strdup(FILENAME_PARENT); - entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); - BLI_addtail(entries, entry); - nbr_entries++; - } - - for (i = 0, ln = (datablock_infos ? datablock_infos : names); i < nitems; i++, ln = ln->next) { - struct BLODataBlockInfo *info = datablock_infos ? ln->link : NULL; - const char *blockname = info ? info->name : ln->link; - - entry = MEM_callocN(sizeof(*entry), __func__); - entry->relpath = BLI_strdup(blockname); +static void filelist_readjob_list_lib_add_datablocks(ListBase *entries, + LinkNode *datablock_infos, + const bool prefix_relpath_with_group_name, + const int idcode, + const char *group_name) +{ + for (LinkNode *ln = datablock_infos; ln; ln = ln->next) { + struct BLODataBlockInfo *info = ln->link; + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + if (prefix_relpath_with_group_name) { + entry->relpath = BLI_sprintfN("%s/%s", group_name, info->name); + } + else { + entry->relpath = BLI_strdup(info->name); + } entry->typeflag |= FILE_TYPE_BLENDERLIB; if (info && info->asset_data) { entry->typeflag |= FILE_TYPE_ASSET; /* Moves ownership! */ entry->imported_asset_data = info->asset_data; } - if (!(group && idcode)) { - entry->typeflag |= FILE_TYPE_DIR; - entry->blentype = groupname_to_code(blockname); - } - else { - entry->blentype = idcode; - } + entry->blentype = idcode; BLI_addtail(entries, entry); - nbr_entries++; + } +} + +static int filelist_readjob_list_lib(const char *root, + ListBase *entries, + const ListLibOptions options) +{ + char dir[FILE_MAX_LIBEXTRA], *group; + + struct BlendHandle *libfiledata = NULL; + + /* Check if the given root is actually a library. All folders are passed to + * `filelist_readjob_list_lib` and based on the number of found entries `filelist_readjob_do` + * will do a dir listing only when this function does not return any entries. */ + /* TODO: We should consider introducing its own function to detect if it is a lib and + * call it directly from `filelist_readjob_do` to increase readability. */ + const bool is_lib = BLO_library_path_explode(root, dir, &group, NULL); + if (!is_lib) { + return 0; } - BLI_linklist_freeN(datablock_infos ? datablock_infos : names); + /* Open the library file. */ + BlendFileReadReport bf_reports = {.reports = NULL}; + libfiledata = BLO_blendhandle_from_file(dir, &bf_reports); + if (libfiledata == NULL) { + return 0; + } - return nbr_entries; + /* Add current parent when requested. */ + int parent_len = 0; + if (options & LIST_LIB_ADD_PARENT) { + FileListInternEntry *entry = MEM_callocN(sizeof(*entry), __func__); + entry->relpath = BLI_strdup(FILENAME_PARENT); + entry->typeflag |= (FILE_TYPE_BLENDERLIB | FILE_TYPE_DIR); + BLI_addtail(entries, entry); + parent_len = 1; + } + + int group_len = 0; + int datablock_len = 0; + const bool group_came_from_path = group != NULL; + if (group_came_from_path) { + const int idcode = groupname_to_code(group); + LinkNode *datablock_infos = BLO_blendhandle_get_datablock_info( + libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &datablock_len); + filelist_readjob_list_lib_add_datablocks(entries, datablock_infos, false, idcode, group); + BLI_linklist_freeN(datablock_infos); + } + else { + LinkNode *groups = BLO_blendhandle_get_linkable_groups(libfiledata); + group_len = BLI_linklist_count(groups); + + for (LinkNode *ln = groups; ln; ln = ln->next) { + const char *group_name = ln->link; + const int idcode = groupname_to_code(group_name); + FileListInternEntry *group_entry = filelist_readjob_list_lib_group_create(idcode, + group_name); + BLI_addtail(entries, group_entry); + + if (options & LIST_LIB_RECURSIVE) { + int group_datablock_len; + LinkNode *group_datablock_infos = BLO_blendhandle_get_datablock_info( + libfiledata, idcode, options & LIST_LIB_ASSETS_ONLY, &group_datablock_len); + filelist_readjob_list_lib_add_datablocks( + entries, group_datablock_infos, true, idcode, group_name); + BLI_linklist_freeN(group_datablock_infos); + datablock_len += group_datablock_len; + } + } + + BLI_linklist_freeN(groups); + } + + BLO_blendhandle_close(libfiledata); + + /* Return the number of items added to entries. */ + int added_entries_len = group_len + datablock_len + parent_len; + return added_entries_len; } #if 0 @@ -3130,22 +3427,84 @@ typedef struct FileListReadJob { char main_name[FILE_MAX]; Main *current_main; struct FileList *filelist; + /** Set to request a partial read that only adds files representing #Main data (IDs). Used when + * #Main may have received changes of interest (e.g. asset removed or renamed). */ + bool only_main_data; /** Shallow copy of #filelist for thread-safe access. * * The job system calls #filelist_readjob_update which moves any read file from #tmp_filelist * into #filelist in a thread-safe way. * + * #tmp_filelist also keeps an `AssetLibrary *` so that it can be loaded in the same thread, and + * moved to #filelist once all categories are loaded. + * * NOTE: #tmp_filelist is freed in #filelist_readjob_free, so any copied pointers need to be set * to NULL to avoid double-freeing them. */ struct FileList *tmp_filelist; } FileListReadJob; -static void filelist_readjob_do(const bool do_lib, - FileListReadJob *job_params, - const short *stop, - short *do_update, - float *progress) +static void filelist_readjob_append_entries(FileListReadJob *job_params, + ListBase *from_entries, + int nbr_from_entries, + short *do_update) +{ + BLI_assert(BLI_listbase_count(from_entries) == nbr_from_entries); + if (nbr_from_entries <= 0) { + *do_update = false; + return; + } + + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + BLI_mutex_lock(&job_params->lock); + BLI_movelisttolist(&filelist->filelist.entries, from_entries); + filelist->filelist.nbr_entries += nbr_from_entries; + BLI_mutex_unlock(&job_params->lock); + + *do_update = true; +} + +static bool filelist_readjob_should_recurse_into_entry(const int max_recursion, + const bool is_lib, + const int current_recursion_level, + FileListInternEntry *entry) +{ + if (max_recursion == 0) { + /* Recursive loading is disabled. */ + return false; + } + if (!is_lib && current_recursion_level > max_recursion) { + /* No more levels of recursion left. */ + return false; + } + /* Show entries when recursion is set to `Blend file` even when `current_recursion_level` exceeds + * `max_recursion`. */ + if (!is_lib && (current_recursion_level >= max_recursion) && + ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) { + return false; + } + if (entry->typeflag & FILE_TYPE_BLENDERLIB) { + /* Libraries are already loaded recursively when recursive loaded is used. No need to add + * them another time. This loading is done with the `LIST_LIB_RECURSIVE` option. */ + return false; + } + if (!(entry->typeflag & FILE_TYPE_DIR)) { + /* Cannot recurse into regular file entries. */ + return false; + } + if (FILENAME_IS_CURRPAR(entry->relpath)) { + /* Don't schedule go to parent entry, (`..`) */ + return false; + } + + return true; +} + +static void filelist_readjob_recursive_dir_add_items(const bool do_lib, + FileListReadJob *job_params, + const short *stop, + short *do_update, + float *progress) { FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ ListBase entries = {0}; @@ -3157,13 +3516,6 @@ static void filelist_readjob_do(const bool do_lib, const int max_recursion = filelist->max_recursion; int nbr_done_dirs = 0, nbr_todo_dirs = 1; - // BLI_assert(filelist->filtered == NULL); - BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); - - /* A valid, but empty directory from now. */ - filelist->filelist.nbr_entries = 0; - todo_dirs = BLI_stack_new(sizeof(*td_dir), __func__); td_dir = BLI_stack_push_r(todo_dirs); td_dir->level = 1; @@ -3177,7 +3529,6 @@ static void filelist_readjob_do(const bool do_lib, while (!BLI_stack_is_empty(todo_dirs) && !(*stop)) { FileListInternEntry *entry; int nbr_entries = 0; - bool is_lib = do_lib; char *subdir; char rel_subdir[FILE_MAX_LIBEXTRA]; @@ -3200,58 +3551,59 @@ static void filelist_readjob_do(const bool do_lib, BLI_path_normalize_dir(root, rel_subdir); BLI_path_rel(rel_subdir, root); + bool is_lib = false; if (do_lib) { - nbr_entries = filelist_readjob_list_lib(subdir, &entries, skip_currpar); + ListLibOptions list_lib_options = 0; + if (!skip_currpar) { + list_lib_options |= LIST_LIB_ADD_PARENT; + } + + /* Libraries are loaded recursively when max_recursion is set. It doesn't check if there is + * still a recursion level over. */ + if (max_recursion > 0) { + list_lib_options |= LIST_LIB_RECURSIVE; + } + /* Only load assets when browsing an asset library. For normal file browsing we return all + * entries. `FLF_ASSETS_ONLY` filter can be enabled/disabled by the user.*/ + if (filelist->asset_library_ref) { + list_lib_options |= LIST_LIB_ASSETS_ONLY; + } + nbr_entries = filelist_readjob_list_lib(subdir, &entries, list_lib_options); + if (nbr_entries > 0) { + is_lib = true; + } } - if (!nbr_entries) { - is_lib = false; + + if (!is_lib) { nbr_entries = filelist_readjob_list_dir( subdir, &entries, filter_glob, do_lib, job_params->main_name, skip_currpar); } for (entry = entries.first; entry; entry = entry->next) { - BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath); - entry->uid = filelist_uid_generate(filelist); - /* Only thing we change in direntry here, so we need to free it first. */ + /* When loading entries recursive, the rel_path should be relative from the root dir. + * we combine the relative path to the subdir with the relative path of the entry. */ + BLI_join_dirfile(dir, sizeof(dir), rel_subdir, entry->relpath); MEM_freeN(entry->relpath); entry->relpath = BLI_strdup(dir + 2); /* + 2 to remove '//' * added by BLI_path_rel to rel_subdir. */ entry->name = fileentry_uiname(root, entry->relpath, entry->typeflag, dir); entry->free_name = true; - /* Here we decide whether current filedirentry is to be listed too, or not. */ - if (max_recursion && (is_lib || (recursion_level <= max_recursion))) { - if (((entry->typeflag & FILE_TYPE_DIR) == 0) || FILENAME_IS_CURRPAR(entry->relpath)) { - /* Skip... */ - } - else if (!is_lib && (recursion_level >= max_recursion) && - ((entry->typeflag & (FILE_TYPE_BLENDER | FILE_TYPE_BLENDER_BACKUP)) == 0)) { - /* Do not recurse in real directories in this case, only in .blend libs. */ - } - else { - /* We have a directory we want to list, add it to todo list! */ - BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); - BLI_path_normalize_dir(job_params->main_name, dir); - td_dir = BLI_stack_push_r(todo_dirs); - td_dir->level = recursion_level + 1; - td_dir->dir = BLI_strdup(dir); - nbr_todo_dirs++; - } + if (filelist_readjob_should_recurse_into_entry( + max_recursion, is_lib, recursion_level, entry)) { + /* We have a directory we want to list, add it to todo list! */ + BLI_join_dirfile(dir, sizeof(dir), root, entry->relpath); + BLI_path_normalize_dir(job_params->main_name, dir); + td_dir = BLI_stack_push_r(todo_dirs); + td_dir->level = recursion_level + 1; + td_dir->dir = BLI_strdup(dir); + nbr_todo_dirs++; } } - if (nbr_entries) { - BLI_mutex_lock(&job_params->lock); - - *do_update = true; - - BLI_movelisttolist(&filelist->filelist.entries, &entries); - filelist->filelist.nbr_entries += nbr_entries; - - BLI_mutex_unlock(&job_params->lock); - } + filelist_readjob_append_entries(job_params, &entries, nbr_entries, do_update); nbr_done_dirs++; *progress = (float)nbr_done_dirs / (float)nbr_todo_dirs; @@ -3268,6 +3620,24 @@ static void filelist_readjob_do(const bool do_lib, BLI_stack_free(todo_dirs); } +static void filelist_readjob_do(const bool do_lib, + FileListReadJob *job_params, + const short *stop, + short *do_update, + float *progress) +{ + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + + // BLI_assert(filelist->filtered == NULL); + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + /* A valid, but empty directory from now. */ + filelist->filelist.nbr_entries = 0; + + filelist_readjob_recursive_dir_add_items(do_lib, job_params, stop, do_update, progress); +} + static void filelist_readjob_dir(FileListReadJob *job_params, short *stop, short *do_update, @@ -3284,35 +3654,62 @@ static void filelist_readjob_lib(FileListReadJob *job_params, filelist_readjob_do(true, job_params, stop, do_update, progress); } -static void filelist_readjob_main(FileListReadJob *job_params, - short *stop, - short *do_update, - float *progress) +static void filelist_asset_library_path(const FileListReadJob *job_params, + char r_library_root_path[FILE_MAX]) { - /* TODO! */ - filelist_readjob_dir(job_params, stop, do_update, progress); + if (job_params->filelist->type == FILE_MAIN_ASSET) { + /* For the "Current File" library (#FILE_MAIN_ASSET) we get the asset library root path based + * on main. */ + BKE_asset_library_find_suitable_root_path_from_main(job_params->current_main, + r_library_root_path); + } + else { + BLI_strncpy(r_library_root_path, job_params->tmp_filelist->filelist.root, FILE_MAX); + } } /** - * \warning Acts on main, so NOT thread-safe! + * Load asset library data, which currently means loading the asset catalogs for the library. */ -static void filelist_readjob_main_assets(FileListReadJob *job_params, - short *UNUSED(stop), - short *do_update, - float *UNUSED(progress)) +static void filelist_readjob_load_asset_library_data(FileListReadJob *job_params, short *do_update) +{ + FileList *tmp_filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + + *do_update = false; + + if (job_params->filelist->asset_library_ref == NULL) { + return; + } + if (tmp_filelist->asset_library != NULL) { + /* Asset library already loaded. */ + return; + } + + char library_root_path[FILE_MAX]; + filelist_asset_library_path(job_params, library_root_path); + + /* Load asset catalogs, into the temp filelist for thread-safety. + * #filelist_readjob_endjob() will move it into the real filelist. */ + tmp_filelist->asset_library = BKE_asset_library_load(library_root_path); + *do_update = true; +} + +static void filelist_readjob_main_assets_add_items(FileListReadJob *job_params, + short *UNUSED(stop), + short *do_update, + float *UNUSED(progress)) { FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ - BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && - (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); - - /* A valid, but empty directory from now. */ - filelist->filelist.nbr_entries = 0; FileListInternEntry *entry; ListBase tmp_entries = {0}; ID *id_iter; int nbr_entries = 0; + /* Make sure no IDs are added/removed/reallocated in the main thread while this is running in + * parallel. */ + BKE_main_lock(job_params->current_main); + FOREACH_MAIN_ID_BEGIN (job_params->current_main, id_iter) { if (!id_iter->asset_data) { continue; @@ -3335,16 +3732,93 @@ static void filelist_readjob_main_assets(FileListReadJob *job_params, } FOREACH_MAIN_ID_END; + BKE_main_unlock(job_params->current_main); + if (nbr_entries) { *do_update = true; BLI_movelisttolist(&filelist->filelist.entries, &tmp_entries); filelist->filelist.nbr_entries += nbr_entries; - filelist->filelist.nbr_entries_filtered = filelist->filelist.entry_idx_start = - filelist->filelist.entry_idx_end = -1; + filelist->filelist.nbr_entries_filtered = -1; } } +/** + * Check if \a bmain is stored within the root path of \a filelist. This means either directly or + * in some nested directory. In other words, it checks if the \a filelist root path is contained in + * the path to \a bmain. + * This is irrespective of the recursion level displayed, it basically assumes unlimited recursion + * levels. + */ +static bool filelist_contains_main(const FileList *filelist, const Main *bmain) +{ + const char *main_path = BKE_main_blendfile_path(bmain); + return main_path[0] && BLI_path_contains(filelist->filelist.root, main_path); +} + +static void filelist_readjob_asset_library(FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress) +{ + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + /* A valid, but empty file-list from now. */ + filelist->filelist.nbr_entries = 0; + + /* NOP if already read. */ + filelist_readjob_load_asset_library_data(job_params, do_update); + + if (filelist_contains_main(filelist, job_params->current_main)) { + filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); + } + if (!job_params->only_main_data) { + filelist_readjob_recursive_dir_add_items(true, job_params, stop, do_update, progress); + } +} + +static void filelist_readjob_main(FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress) +{ + /* TODO! */ + filelist_readjob_dir(job_params, stop, do_update, progress); +} + +static void filelist_readjob_main_assets(FileListReadJob *job_params, + short *stop, + short *do_update, + float *progress) +{ + FileList *filelist = job_params->tmp_filelist; /* Use the thread-safe filelist queue. */ + BLI_assert(BLI_listbase_is_empty(&filelist->filelist.entries) && + (filelist->filelist.nbr_entries == FILEDIR_NBR_ENTRIES_UNSET)); + + filelist_readjob_load_asset_library_data(job_params, do_update); + + /* A valid, but empty file-list from now. */ + filelist->filelist.nbr_entries = 0; + + filelist_readjob_main_assets_add_items(job_params, stop, do_update, progress); +} + +/** + * Check if the read-job is requesting a partial reread of the file list only. + */ +static bool filelist_readjob_is_partial_read(const FileListReadJob *read_job) +{ + return read_job->only_main_data; +} + +/** + * \note This may trigger partial filelist reading. If the #FL_FORCE_RESET_MAIN_FILES flag is set, + * some current entries are kept and we just call the readjob to update the main files (see + * #FileListReadJob.only_main_data). + */ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update, float *progress) { FileListReadJob *flrj = flrjv; @@ -3363,18 +3837,29 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update flrj->tmp_filelist->filelist_intern.filtered = NULL; BLI_listbase_clear(&flrj->tmp_filelist->filelist_intern.entries); - filelist_uid_unset(&flrj->tmp_filelist->filelist_intern.curr_uid); + if (filelist_readjob_is_partial_read(flrj)) { + /* Don't unset the current UID on partial read, would give duplicates otherwise. */ + } + else { + filelist_uid_unset(&flrj->tmp_filelist->filelist_intern.curr_uid); + } flrj->tmp_filelist->libfiledata = NULL; memset(&flrj->tmp_filelist->filelist_cache, 0, sizeof(flrj->tmp_filelist->filelist_cache)); flrj->tmp_filelist->selection_state = NULL; flrj->tmp_filelist->asset_library_ref = NULL; + flrj->tmp_filelist->filter_data.asset_catalog_filter = NULL; BLI_mutex_unlock(&flrj->lock); flrj->tmp_filelist->read_job_fn(flrj, stop, do_update, progress); } +/** + * \note This may update for a partial filelist reading job. If the #FL_FORCE_RESET_MAIN_FILES flag + * is set, some current entries are kept and we just call the readjob to update the main + * files (see #FileListReadJob.only_main_data). + */ static void filelist_readjob_update(void *flrjv) { FileListReadJob *flrj = flrjv; @@ -3394,11 +3879,21 @@ static void filelist_readjob_update(void *flrjv) flrj->tmp_filelist->filelist.nbr_entries = 0; } + if (flrj->tmp_filelist->asset_library) { + flrj->filelist->asset_library = flrj->tmp_filelist->asset_library; + } + + /* Important for partial reads: Copy increased UID counter back to the real list. */ + if (flrj->tmp_filelist->filelist_intern.curr_uid > fl_intern->curr_uid) { + fl_intern->curr_uid = flrj->tmp_filelist->filelist_intern.curr_uid; + } + BLI_mutex_unlock(&flrj->lock); if (new_nbr_entries) { - /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! */ - filelist_clear_ex(flrj->filelist, true, false); + /* Do not clear selection cache, we can assume already 'selected' UIDs are still valid! Keep + * the asset library data we just read. */ + filelist_clear_ex(flrj->filelist, false, true, false); flrj->filelist->flags |= (FL_NEED_SORTING | FL_NEED_FILTERING); } @@ -3455,8 +3950,11 @@ void filelist_readjob_start(FileList *filelist, const int space_notifier, const flrj->filelist = filelist; flrj->current_main = bmain; BLI_strncpy(flrj->main_name, BKE_main_blendfile_path(bmain), sizeof(flrj->main_name)); + if ((filelist->flags & FL_FORCE_RESET_MAIN_FILES) && !(filelist->flags & FL_FORCE_RESET)) { + flrj->only_main_data = true; + } - filelist->flags &= ~(FL_FORCE_RESET | FL_IS_READY); + filelist->flags &= ~(FL_FORCE_RESET | FL_FORCE_RESET_MAIN_FILES | FL_IS_READY); filelist->flags |= FL_IS_PENDING; /* Init even for single threaded execution. Called functions use it. */ diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 1fb05e0f9ac..0048a349dca 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -31,6 +31,7 @@ struct AssetLibraryReference; struct BlendHandle; struct FileList; struct FileSelection; +struct bUUID; struct wmWindowManager; struct FileDirEntry; @@ -71,6 +72,11 @@ void filelist_setfilter_options(struct FileList *filelist, const bool filter_assets_only, const char *filter_glob, const char *filter_search); +void filelist_set_asset_catalog_filter_options( + struct FileList *filelist, + eFileSel_Params_AssetCatalogVisibility catalog_visibility, + const struct bUUID *catalog_id); +void filelist_tag_needs_filtering(struct FileList *filelist); void filelist_filter(struct FileList *filelist); void filelist_setlibrary(struct FileList *filelist, const struct AssetLibraryReference *asset_library_ref); @@ -86,7 +92,11 @@ int filelist_geticon(struct FileList *filelist, const int index, const bool is_m struct FileList *filelist_new(short type); void filelist_settype(struct FileList *filelist, short type); void filelist_clear(struct FileList *filelist); -void filelist_clear_ex(struct FileList *filelist, const bool do_cache, const bool do_selection); +void filelist_clear_ex(struct FileList *filelist, + const bool do_asset_library, + const bool do_cache, + const bool do_selection); +void filelist_clear_from_reset_tag(struct FileList *filelist); void filelist_free(struct FileList *filelist); const char *filelist_dir(struct FileList *filelist); @@ -108,6 +118,7 @@ bool filelist_file_cache_block(struct FileList *filelist, const int index); bool filelist_needs_force_reset(struct FileList *filelist); void filelist_tag_force_reset(struct FileList *filelist); +void filelist_tag_force_reset_mainfiles(struct FileList *filelist); bool filelist_pending(struct FileList *filelist); bool filelist_needs_reset_on_main_changes(const struct FileList *filelist); bool filelist_is_ready(struct FileList *filelist); @@ -141,6 +152,8 @@ void filelist_entry_parent_select_set(struct FileList *filelist, void filelist_setrecursion(struct FileList *filelist, const int recursion_level); +struct AssetLibrary *filelist_asset_library(struct FileList *filelist); + struct BlendHandle *filelist_lib(struct FileList *filelist); bool filelist_islibrary(struct FileList *filelist, char *dir, char **r_group); void filelist_freelib(struct FileList *filelist); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 11b06d2b414..11757975a62 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -120,19 +120,16 @@ static void fileselect_ensure_updated_asset_params(SpaceFile *sfile) asset_params->base_params.details_flags = U_default.file_space_data.details_flags; asset_params->asset_library_ref.type = ASSET_LIBRARY_LOCAL; asset_params->asset_library_ref.custom_library_index = -1; - asset_params->import_type = FILE_ASSET_IMPORT_APPEND; + asset_params->import_type = FILE_ASSET_IMPORT_APPEND_REUSE; } FileSelectParams *base_params = &asset_params->base_params; base_params->file[0] = '\0'; base_params->filter_glob[0] = '\0'; - /* TODO: this way of using filters to form categories is notably slower than specifying a - * "group" to read. That's because all types are read and filtering is applied afterwards. Would - * be nice if we could lazy-read individual groups. */ base_params->flag |= U_default.file_space_data.flag | FILE_ASSETS_ONLY | FILE_FILTER; base_params->flag &= ~FILE_DIRSEL_ONLY; base_params->filter |= FILE_TYPE_BLENDERLIB; - base_params->filter_id = FILTER_ID_OB | FILTER_ID_GR; + base_params->filter_id = FILTER_ID_ALL; base_params->display = FILE_IMGDISPLAY; base_params->sort = FILE_SORT_ALPHA; /* Asset libraries include all sub-directories, so enable maximal recursion. */ @@ -440,7 +437,8 @@ static void fileselect_refresh_asset_params(FileAssetSelectParams *asset_params) BLI_strncpy(base_params->dir, user_library->path, sizeof(base_params->dir)); break; } - base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : FILE_LOADLIB; + base_params->type = (library->type == ASSET_LIBRARY_LOCAL) ? FILE_MAIN_ASSET : + FILE_ASSET_LIBRARY; } void fileselect_refresh_params(SpaceFile *sfile) @@ -461,6 +459,15 @@ bool ED_fileselect_is_asset_browser(const SpaceFile *sfile) return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS); } +struct AssetLibrary *ED_fileselect_active_asset_library_get(const SpaceFile *sfile) +{ + if (!ED_fileselect_is_asset_browser(sfile) || !sfile->files) { + return NULL; + } + + return filelist_asset_library(sfile->files); +} + struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile) { if (!ED_fileselect_is_asset_browser(sfile)) { @@ -476,6 +483,18 @@ struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile) return filelist_file_get_id(file); } +void ED_fileselect_activate_asset_catalog(const SpaceFile *sfile, const bUUID catalog_id) +{ + if (!ED_fileselect_is_asset_browser(sfile)) { + return; + } + + FileAssetSelectParams *params = ED_fileselect_get_asset_params(sfile); + params->asset_catalog_visibility = FILE_SHOW_ASSETS_FROM_CATALOG; + params->catalog_id = catalog_id; + WM_main_add_notifier(NC_SPACE | ND_SPACE_ASSET_PARAMS, NULL); +} + static void on_reload_activate_by_id(SpaceFile *sfile, onReloadFnData custom_data) { ID *asset_id = (ID *)custom_data; @@ -507,20 +526,57 @@ void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool def const FileDirEntry *file = filelist_file_ex(files, file_index, false); if (filelist_file_get_id(file) != asset_id) { - filelist_entry_select_set(files, file, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); continue; } params->active_file = file_index; filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); - - /* Keep looping to deselect the other files. */ + break; } WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL); WM_main_add_notifier(NC_ASSET | NA_SELECTED, NULL); } +static void on_reload_select_by_relpath(SpaceFile *sfile, onReloadFnData custom_data) +{ + const char *relative_path = custom_data; + ED_fileselect_activate_by_relpath(sfile, relative_path); +} + +void ED_fileselect_activate_by_relpath(SpaceFile *sfile, const char *relative_path) +{ + /* If there are filelist operations running now ("pending" true) or soon ("force reset" true), + * there is a fair chance that the to-be-activated file at relative_path will only be present + * after these operations have completed. Defer activation until then. */ + struct FileList *files = sfile->files; + if (files == NULL || filelist_pending(files) || filelist_needs_force_reset(files)) { + /* Casting away the constness of `relative_path` is safe here, because eventually it just ends + * up in another call to this function, and then it's a const char* again. */ + file_on_reload_callback_register(sfile, on_reload_select_by_relpath, (char *)relative_path); + return; + } + + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + const int num_files_filtered = filelist_files_ensure(files); + + for (int file_index = 0; file_index < num_files_filtered; ++file_index) { + const FileDirEntry *file = filelist_file(files, file_index); + + if (STREQ(file->relpath, relative_path)) { + params->active_file = file_index; + filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + } + } + WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); +} + +void ED_fileselect_deselect_all(SpaceFile *sfile) +{ + file_select_deselect_all(sfile, FILE_SEL_SELECTED); + WM_main_add_notifier(NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); +} + /* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA * may also be remembered, but only conditionally. */ #define PARAMS_FLAGS_REMEMBERED (FILE_HIDE_DOT) @@ -935,6 +991,8 @@ static void file_attribute_columns_init(const FileSelectParams *params, FileLayo void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) { FileSelectParams *params = ED_fileselect_get_active_params(sfile); + /* Request a slightly more compact layout for asset browsing. */ + const bool compact = ED_fileselect_is_asset_browser(sfile); FileLayout *layout = NULL; View2D *v2d = ®ion->v2d; int numfiles; @@ -954,12 +1012,13 @@ void ED_fileselect_init_layout(struct SpaceFile *sfile, ARegion *region) layout->textheight = textheight; if (params->display == FILE_IMGDISPLAY) { + const float pad_fac = compact ? 0.15f : 0.3f; layout->prv_w = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_X; layout->prv_h = ((float)params->thumbnail_size / 20.0f) * UI_UNIT_Y; - layout->tile_border_x = 0.3f * UI_UNIT_X; - layout->tile_border_y = 0.3f * UI_UNIT_X; - layout->prv_border_x = 0.3f * UI_UNIT_X; - layout->prv_border_y = 0.3f * UI_UNIT_Y; + layout->tile_border_x = pad_fac * UI_UNIT_X; + layout->tile_border_y = pad_fac * UI_UNIT_X; + layout->prv_border_x = pad_fac * UI_UNIT_X; + layout->prv_border_y = pad_fac * UI_UNIT_Y; layout->tile_w = layout->prv_w + 2 * layout->prv_border_x; layout->tile_h = layout->prv_h + 2 * layout->prv_border_y + textheight; layout->width = (int)(BLI_rctf_size_x(&v2d->cur) - 2 * layout->tile_border_x); @@ -1271,7 +1330,7 @@ void file_params_rename_end(wmWindowManager *wm, /* Ensure smooth-scroll timer is active, even if not needed, because that way rename state is * handled properly. */ file_params_invoke_rename_postscroll(wm, win, sfile); - /* Also always activate the rename file, even if renaming was cancelled. */ + /* Also always activate the rename file, even if renaming was canceled. */ file_params_renamefile_activate(sfile, params); } diff --git a/source/blender/editors/space_file/fsmenu.c b/source/blender/editors/space_file/fsmenu.c index 776bb0b3bb7..97f22ca7d89 100644 --- a/source/blender/editors/space_file/fsmenu.c +++ b/source/blender/editors/space_file/fsmenu.c @@ -769,21 +769,22 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) FS_INSERT_LAST); const char *home = BLI_getenv("HOME"); - + if (home) { # define FS_MACOS_PATH(path, name, icon) \ BLI_snprintf(line, sizeof(line), path, home); \ fsmenu_insert_entry(fsmenu, FS_CATEGORY_OTHER, line, name, icon, FS_INSERT_LAST); - FS_MACOS_PATH("%s/", NULL, ICON_HOME) - FS_MACOS_PATH("%s/Desktop/", N_("Desktop"), ICON_DESKTOP) - FS_MACOS_PATH("%s/Documents/", N_("Documents"), ICON_DOCUMENTS) - FS_MACOS_PATH("%s/Downloads/", N_("Downloads"), ICON_IMPORT) - FS_MACOS_PATH("%s/Movies/", N_("Movies"), ICON_FILE_MOVIE) - FS_MACOS_PATH("%s/Music/", N_("Music"), ICON_FILE_SOUND) - FS_MACOS_PATH("%s/Pictures/", N_("Pictures"), ICON_FILE_IMAGE) - FS_MACOS_PATH("%s/Library/Fonts/", N_("Fonts"), ICON_FILE_FONT) + FS_MACOS_PATH("%s/", NULL, ICON_HOME) + FS_MACOS_PATH("%s/Desktop/", N_("Desktop"), ICON_DESKTOP) + FS_MACOS_PATH("%s/Documents/", N_("Documents"), ICON_DOCUMENTS) + FS_MACOS_PATH("%s/Downloads/", N_("Downloads"), ICON_IMPORT) + FS_MACOS_PATH("%s/Movies/", N_("Movies"), ICON_FILE_MOVIE) + FS_MACOS_PATH("%s/Music/", N_("Music"), ICON_FILE_SOUND) + FS_MACOS_PATH("%s/Pictures/", N_("Pictures"), ICON_FILE_IMAGE) + FS_MACOS_PATH("%s/Library/Fonts/", N_("Fonts"), ICON_FILE_FONT) # undef FS_MACOS_PATH + } /* Get mounted volumes better method OSX 10.6 and higher, see: * https://developer.apple.com/library/mac/#documentation/CoreFOundation/Reference/CFURLRef/Reference/reference.html @@ -958,7 +959,7 @@ void fsmenu_read_system(struct FSMenu *fsmenu, int read_bookmarks) found = 1; } if (endmntent(fp) == 0) { - fprintf(stderr, "could not close the list of mounted filesystems\n"); + fprintf(stderr, "could not close the list of mounted file-systems\n"); } } /* Check gvfs shares. */ diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index a4f36c2a6ee..b115c63a569 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -43,6 +43,7 @@ #include "WM_message.h" #include "WM_types.h" +#include "ED_asset.h" #include "ED_fileselect.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -303,15 +304,6 @@ static void file_ensure_valid_region_state(bContext *C, } } -/** - * Tag the space to recreate the file-list. - */ -static void file_tag_reset_list(ScrArea *area, SpaceFile *sfile) -{ - filelist_tag_force_reset(sfile->files); - ED_area_tag_refresh(area); -} - static void file_refresh(const bContext *C, ScrArea *area) { wmWindowManager *wm = CTX_wm_manager(C); @@ -326,7 +318,7 @@ static void file_refresh(const bContext *C, ScrArea *area) if (sfile->files && (sfile->tags & FILE_TAG_REBUILD_MAIN_FILES) && filelist_needs_reset_on_main_changes(sfile->files)) { - filelist_tag_force_reset(sfile->files); + filelist_tag_force_reset_mainfiles(sfile->files); } sfile->tags &= ~FILE_TAG_REBUILD_MAIN_FILES; @@ -335,9 +327,10 @@ static void file_refresh(const bContext *C, ScrArea *area) params->highlight_file = -1; /* added this so it opens nicer (ton) */ } - if (!U.experimental.use_extended_asset_browser && ED_fileselect_is_asset_browser(sfile)) { - /* Only poses supported as non-experimental right now. */ - params->filter_id = FILTER_ID_AC; + if (ED_fileselect_is_asset_browser(sfile)) { + /* Ask the asset code for appropriate ID filter flags for the supported assets, and mask others + * out. */ + params->filter_id &= ED_asset_types_supported_as_filter_flags(); } filelist_settype(sfile->files, params->type); @@ -355,6 +348,10 @@ static void file_refresh(const bContext *C, ScrArea *area) (params->flag & FILE_ASSETS_ONLY) != 0, params->filter_glob, params->filter_search); + if (asset_params) { + filelist_set_asset_catalog_filter_options( + sfile->files, asset_params->asset_catalog_visibility, &asset_params->catalog_id); + } /* Update the active indices of bookmarks & co. */ sfile->systemnr = fsmenu_get_active_indices(fsmenu, FS_CATEGORY_SYSTEM, params->dir); @@ -365,7 +362,7 @@ static void file_refresh(const bContext *C, ScrArea *area) if (filelist_needs_force_reset(sfile->files)) { filelist_readjob_stop(sfile->files, wm); - filelist_clear(sfile->files); + filelist_clear_from_reset_tag(sfile->files); } if (filelist_needs_reading(sfile->files)) { @@ -427,9 +424,8 @@ static void file_on_reload_callback_call(SpaceFile *sfile) static void file_reset_filelist_showing_main_data(ScrArea *area, SpaceFile *sfile) { if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { - /* Full refresh of the file list if local asset data was changed. Refreshing this view - * is cheap and users expect this to be updated immediately. */ - file_tag_reset_list(area, sfile); + filelist_tag_force_reset_mainfiles(sfile->files); + ED_area_tag_refresh(area); } } @@ -660,10 +656,10 @@ static void file_main_region_draw(const bContext *C, ARegion *region) /* on first read, find active file */ if (params->highlight_file == -1) { wmEvent *event = CTX_wm_window(C)->eventstate; - file_highlight_set(sfile, region, event->x, event->y); + file_highlight_set(sfile, region, event->xy[0], event->xy[1]); } - if (!file_draw_hint_if_invalid(sfile, region)) { + if (!file_draw_hint_if_invalid(C, sfile, region)) { file_draw_list(C, region); } @@ -692,6 +688,7 @@ static void file_operatortypes(void) WM_operatortype_append(FILE_OT_previous); WM_operatortype_append(FILE_OT_next); WM_operatortype_append(FILE_OT_refresh); + WM_operatortype_append(FILE_OT_asset_library_refresh); WM_operatortype_append(FILE_OT_bookmark_add); WM_operatortype_append(FILE_OT_bookmark_delete); WM_operatortype_append(FILE_OT_bookmark_cleanup); @@ -738,8 +735,18 @@ static void file_tools_region_draw(const bContext *C, ARegion *region) ED_region_panels(C, region); } -static void file_tools_region_listener(const wmRegionListenerParams *UNUSED(listener_params)) +static void file_tools_region_listener(const wmRegionListenerParams *listener_params) { + const wmNotifier *wmn = listener_params->notifier; + ARegion *region = listener_params->region; + + switch (wmn->category) { + case NC_SCENE: + if (ELEM(wmn->data, ND_MODE)) { + ED_region_tag_redraw(region); + } + break; + } } static void file_tool_props_region_listener(const wmRegionListenerParams *listener_params) @@ -754,6 +761,11 @@ static void file_tool_props_region_listener(const wmRegionListenerParams *listen ED_region_tag_redraw(region); } break; + case NC_SCENE: + if (ELEM(wmn->data, ND_MODE)) { + ED_region_tag_redraw(region); + } + break; } } @@ -875,6 +887,7 @@ const char *file_context_dir[] = { "active_file", "selected_files", "asset_library_ref", + "selected_asset_files", "id", NULL, }; @@ -932,6 +945,24 @@ static int /*eContextResult*/ file_context(const bContext *C, result, &screen->id, &RNA_AssetLibraryReference, &asset_params->asset_library_ref); return CTX_RESULT_OK; } + /** TODO temporary AssetHandle design: For now this returns the file entry. Would be better if it + * was `"selected_assets"` and returned the assets (e.g. as `AssetHandle`) directly. See comment + * for #AssetHandle for more info. */ + if (CTX_data_equals(member, "selected_asset_files")) { + const int num_files_filtered = filelist_files_ensure(sfile->files); + + for (int file_index = 0; file_index < num_files_filtered; file_index++) { + if (filelist_entry_is_selected(sfile->files, file_index)) { + FileDirEntry *entry = filelist_file(sfile->files, file_index); + if (entry->asset_data) { + CTX_data_list_add(result, &screen->id, &RNA_FileSelectEntry, entry); + } + } + } + + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; + } if (CTX_data_equals(member, "id")) { const FileDirEntry *file = filelist_file(sfile->files, params->active_file); if (file == NULL) { @@ -1035,6 +1066,7 @@ void ED_spacetype_file(void) art->init = file_tools_region_init; art->draw = file_tools_region_draw; BLI_addhead(&st->regiontypes, art); + file_tools_region_panels_register(art); /* regions: tool properties */ art = MEM_callocN(sizeof(ARegionType), "spacetype file operator region"); diff --git a/source/blender/editors/space_graph/graph_buttons.c b/source/blender/editors/space_graph/graph_buttons.c index f4c4b6cafcd..275616f3bcb 100644 --- a/source/blender/editors/space_graph/graph_buttons.c +++ b/source/blender/editors/space_graph/graph_buttons.c @@ -593,17 +593,17 @@ static void graph_panel_key_properties(const bContext *C, Panel *panel) else { if ((fcu->bezt == NULL) && (fcu->modifiers.first)) { /* modifiers only - so no keyframes to be active */ - uiItemL(layout, IFACE_("F-Curve only has F-Modifiers"), ICON_NONE); - uiItemL(layout, IFACE_("See Modifiers panel below"), ICON_INFO); + uiItemL(layout, TIP_("F-Curve only has F-Modifiers"), ICON_NONE); + uiItemL(layout, TIP_("See Modifiers panel below"), ICON_INFO); } else if (fcu->fpt) { /* samples only */ uiItemL(layout, - IFACE_("F-Curve doesn't have any keyframes as it only contains sampled points"), + TIP_("F-Curve doesn't have any keyframes as it only contains sampled points"), ICON_NONE); } else { - uiItemL(layout, IFACE_("No active keyframe on F-Curve"), ICON_NONE); + uiItemL(layout, TIP_("No active keyframe on F-Curve"), ICON_NONE); } } @@ -688,28 +688,30 @@ static void driver_dvar_invalid_name_query_cb(bContext *C, void *dvar_v, void *U DriverVar *dvar = (DriverVar *)dvar_v; if (dvar->flag & DVAR_FLAG_INVALID_EMPTY) { - uiItemL(layout, "It cannot be left blank", ICON_ERROR); + uiItemL(layout, TIP_("It cannot be left blank"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_START_NUM) { - uiItemL(layout, "It cannot start with a number", ICON_ERROR); + uiItemL(layout, TIP_("It cannot start with a number"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_START_CHAR) { uiItemL(layout, - "It cannot start with a special character," - " including '$', '@', '!', '~', '+', '-', '_', '.', or ' '", + TIP_("It cannot start with a special character," + " including '$', '@', '!', '~', '+', '-', '_', '.', or ' '"), ICON_NONE); } if (dvar->flag & DVAR_FLAG_INVALID_HAS_SPACE) { - uiItemL(layout, "It cannot contain spaces (e.g. 'a space')", ICON_ERROR); + uiItemL(layout, TIP_("It cannot contain spaces (e.g. 'a space')"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_HAS_DOT) { - uiItemL(layout, "It cannot contain dots (e.g. 'a.dot')", ICON_ERROR); + uiItemL(layout, TIP_("It cannot contain dots (e.g. 'a.dot')"), ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_HAS_SPECIAL) { - uiItemL(layout, "It cannot contain special (non-alphabetical/numeric) characters", ICON_ERROR); + uiItemL(layout, + TIP_("It cannot contain special (non-alphabetical/numeric) characters"), + ICON_ERROR); } if (dvar->flag & DVAR_FLAG_INVALID_PY_KEYWORD) { - uiItemL(layout, "It cannot be a reserved keyword in Python", ICON_INFO); + uiItemL(layout, TIP_("It cannot be a reserved keyword in Python"), ICON_INFO); } UI_popup_menu_end(C, pup); diff --git a/source/blender/editors/space_graph/graph_edit.c b/source/blender/editors/space_graph/graph_edit.c index 2955c4ef7ae..1967dfabd21 100644 --- a/source/blender/editors/space_graph/graph_edit.c +++ b/source/blender/editors/space_graph/graph_edit.c @@ -403,8 +403,8 @@ static int graphkeys_click_insert_invoke(bContext *C, wmOperator *op, const wmEv region = ac.region; v2d = ®ion->v2d; - mval[0] = (event->x - region->winrct.xmin); - mval[1] = (event->y - region->winrct.ymin); + mval[0] = (event->xy[0] - region->winrct.xmin); + mval[1] = (event->xy[1] - region->winrct.ymin); UI_view2d_region_to_view(v2d, mval[0], mval[1], &x, &y); @@ -1098,7 +1098,8 @@ static int graphkeys_sound_bake_exec(bContext *C, wmOperator *op) RNA_boolean_get(op->ptr, "use_square"), RNA_float_get(op->ptr, "sthreshold"), FPS, - &sbi.length); + &sbi.length, + 0); if (sbi.samples == NULL) { BKE_report(op->reports, RPT_ERROR, "Unsupported audio format"); @@ -1879,7 +1880,7 @@ static bool euler_filter_single_channel(FCurve *fcu) return false; } - /* Prev follows bezt, bezt = "current" point to be fixed. */ + /* `prev` follows bezt, bezt = "current" point to be fixed. */ /* Our method depends on determining a "difference" from the previous vert. */ bool is_modified = false; for (i = 1, prev = fcu->bezt, bezt = fcu->bezt + 1; i < fcu->totvert; i++, prev = bezt++) { diff --git a/source/blender/editors/space_graph/graph_slider_ops.c b/source/blender/editors/space_graph/graph_slider_ops.c index f04336cab84..22e03cfa46c 100644 --- a/source/blender/editors/space_graph/graph_slider_ops.c +++ b/source/blender/editors/space_graph/graph_slider_ops.c @@ -48,37 +48,10 @@ #include "graph_intern.h" -/* ******************** GRAPH SLIDER OPERATORS ************************* */ -/* This file contains a collection of operators to modify keyframes in the graph editor. All - * operators are modal and use a slider that allows the user to define a percentage to modify the - * operator. */ - -/* ******************** Decimate Keyframes Operator ************************* */ - -static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max) -{ - ListBase anim_data = {NULL, NULL}; - bAnimListElem *ale; - int filter; - - /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* Loop through filtered data and clean curves. */ - for (ale = anim_data.first; ale; ale = ale->next) { - if (!decimate_fcurve(ale, remove_ratio, error_sq_max)) { - /* The selection contains unsupported keyframe types! */ - WM_report(RPT_WARNING, "Decimate: Skipping non linear/bezier keyframes!"); - } - - ale->update |= ANIM_UPDATE_DEFAULT; - } - - ANIM_animdata_update(ac, &anim_data); - ANIM_animdata_freelist(&anim_data); -} +/* Used to obtain a list of animation channels for the operators to work on. */ +#define OPERATOR_DATA_FILTER \ + (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | ANIMFILTER_SEL | \ + ANIMFILTER_NODUPLIS) /* ------------------- */ @@ -110,20 +83,88 @@ typedef enum tDecimModes { DECIM_ERROR, } tDecimModes; +/* ******************** GRAPH SLIDER OPERATORS ************************* */ +/* This file contains a collection of operators to modify keyframes in the graph editor. All + * operators are modal and use a slider that allows the user to define a percentage to modify the + * operator. */ + +/* ******************** Utility Functions ************************* */ + +/* Construct a list with the original bezt arrays so we can restore them during modal operation. + * The data is stored on the struct that is passed.*/ +static void store_original_bezt_arrays(tDecimateGraphOp *dgo) +{ + ListBase anim_data = {NULL, NULL}; + bAnimContext *ac = &dgo->ac; + bAnimListElem *ale; + + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + /* Loop through filtered data and copy the curves. */ + for (ale = anim_data.first; ale; ale = ale->next) { + FCurve *fcu = (FCurve *)ale->key_data; + + if (fcu->bezt == NULL) { + /* This curve is baked, skip it. */ + continue; + } + + const int arr_size = sizeof(BezTriple) * fcu->totvert; + + tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy"); + BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array"); + + copy->tot_vert = fcu->totvert; + memcpy(bezts_copy, fcu->bezt, arr_size); + + copy->bezt = bezts_copy; + + LinkData *link = NULL; + + link = MEM_callocN(sizeof(LinkData), "Bezt Link"); + link->data = copy; + + BLI_addtail(&dgo->bezt_arr_list, link); + } + + ANIM_animdata_freelist(&anim_data); +} + +/* ******************** Decimate Keyframes Operator ************************* */ + +static void decimate_graph_keys(bAnimContext *ac, float remove_ratio, float error_sq_max) +{ + ListBase anim_data = {NULL, NULL}; + bAnimListElem *ale; + + /* Filter data. */ + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); + + /* Loop through filtered data and clean curves. */ + for (ale = anim_data.first; ale; ale = ale->next) { + if (!decimate_fcurve(ale, remove_ratio, error_sq_max)) { + /* The selection contains unsupported keyframe types! */ + WM_report(RPT_WARNING, "Decimate: Skipping non linear/bezier keyframes!"); + } + + ale->update |= ANIM_UPDATE_DEFAULT; + } + + ANIM_animdata_update(ac, &anim_data); + ANIM_animdata_freelist(&anim_data); +} + /* Overwrite the current bezts arrays with the original data. */ static void decimate_reset_bezts(tDecimateGraphOp *dgo) { ListBase anim_data = {NULL, NULL}; LinkData *link_bezt; bAnimListElem *ale; - int filter; bAnimContext *ac = &dgo->ac; /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); + ANIM_animdata_filter(ac, &anim_data, OPERATOR_DATA_FILTER, ac->data, ac->datatype); /* Loop through filtered data and reset bezts. */ for (ale = anim_data.first, link_bezt = dgo->bezt_arr_list.first; ale; ale = ale->next) { @@ -229,56 +270,14 @@ static int graphkeys_decimate_invoke(bContext *C, wmOperator *op, const wmEvent dgo->area = CTX_wm_area(C); dgo->region = CTX_wm_region(C); + store_original_bezt_arrays(dgo); + dgo->slider = ED_slider_create(C); ED_slider_init(dgo->slider, event); ED_slider_allow_overshoot_set(dgo->slider, false); decimate_draw_status(C, dgo); - /* Construct a list with the original bezt arrays so we can restore them during modal operation. - */ - { - ListBase anim_data = {NULL, NULL}; - bAnimContext *ac = &dgo->ac; - bAnimListElem *ale; - - int filter; - - /* Filter data. */ - filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_CURVE_VISIBLE | ANIMFILTER_FOREDIT | - ANIMFILTER_SEL | ANIMFILTER_NODUPLIS); - ANIM_animdata_filter(ac, &anim_data, filter, ac->data, ac->datatype); - - /* Loop through filtered data and copy the curves. */ - for (ale = anim_data.first; ale; ale = ale->next) { - FCurve *fcu = (FCurve *)ale->key_data; - - if (fcu->bezt == NULL) { - /* This curve is baked, skip it. */ - continue; - } - - const int arr_size = sizeof(BezTriple) * fcu->totvert; - - tBeztCopyData *copy = MEM_mallocN(sizeof(tBeztCopyData), "bezts_copy"); - BezTriple *bezts_copy = MEM_mallocN(arr_size, "bezts_copy_array"); - - copy->tot_vert = fcu->totvert; - memcpy(bezts_copy, fcu->bezt, arr_size); - - copy->bezt = bezts_copy; - - LinkData *link = NULL; - - link = MEM_callocN(sizeof(LinkData), "Bezt Link"); - link->data = copy; - - BLI_addtail(&dgo->bezt_arr_list, link); - } - - ANIM_animdata_freelist(&anim_data); - } - if (dgo->bezt_arr_list.first == NULL) { WM_report(RPT_WARNING, "Fcurve Decimate: Can't decimate baked channels. Unbake them and try again."); diff --git a/source/blender/editors/space_graph/graph_view.c b/source/blender/editors/space_graph/graph_view.c index c38c5f09a2a..56649c50cfd 100644 --- a/source/blender/editors/space_graph/graph_view.c +++ b/source/blender/editors/space_graph/graph_view.c @@ -213,7 +213,7 @@ static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) scene = ac.scene; /* Set the range directly. */ - get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, false, false); + get_graph_keyframe_extents(&ac, &min, &max, NULL, NULL, true, false); scene->r.flag |= SCER_PRV_RANGE; scene->r.psfra = round_fl_to_int(min); scene->r.pefra = round_fl_to_int(max); @@ -228,9 +228,9 @@ static int graphkeys_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) void GRAPH_OT_previewrange_set(wmOperatorType *ot) { /* Identifiers */ - ot->name = "Auto-Set Preview Range"; + ot->name = "Set Preview Range to Selected"; ot->idname = "GRAPH_OT_previewrange_set"; - ot->description = "Automatically set Preview Range based on range of keyframes"; + ot->description = "Set Preview Range based on range of selected keyframes"; /* API callbacks */ ot->exec = graphkeys_previewrange_exec; diff --git a/source/blender/editors/space_graph/space_graph.c b/source/blender/editors/space_graph/space_graph.c index 720d69eaf4f..0e2c9b85bc6 100644 --- a/source/blender/editors/space_graph/space_graph.c +++ b/source/blender/editors/space_graph/space_graph.c @@ -237,29 +237,27 @@ static void graph_main_region_draw(const bContext *C, ARegion *region) v2d->tot.xmax += 10.0f; } - if (((sipo->flag & SIPO_NODRAWCURSOR) == 0) || (sipo->mode == SIPO_MODE_DRIVERS)) { + if (((sipo->flag & SIPO_NODRAWCURSOR) == 0)) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); /* horizontal component of value-cursor (value line before the current frame line) */ - if ((sipo->flag & SIPO_NODRAWCURSOR) == 0) { - float y = sipo->cursorVal; + float y = sipo->cursorVal; - /* Draw a green line to indicate the cursor value */ - immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50); - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_width(2.0); + /* Draw a line to indicate the cursor value. */ + immUniformThemeColorShadeAlpha(TH_CFRAME, -10, -50); + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_width(2.0); - immBegin(GPU_PRIM_LINES, 2); - immVertex2f(pos, v2d->cur.xmin, y); - immVertex2f(pos, v2d->cur.xmax, y); - immEnd(); + immBegin(GPU_PRIM_LINES, 2); + immVertex2f(pos, v2d->cur.xmin, y); + immVertex2f(pos, v2d->cur.xmax, y); + immEnd(); - GPU_blend(GPU_BLEND_NONE); - } + GPU_blend(GPU_BLEND_NONE); - /* current frame or vertical component of vertical component of the cursor */ + /* Vertical component of of the cursor. */ if (sipo->mode == SIPO_MODE_DRIVERS) { /* cursor x-value */ float x = sipo->cursorTime; @@ -311,12 +309,17 @@ static void graph_main_region_draw_overlay(const bContext *C, ARegion *region) { /* draw entirely, view changes should be handled here */ const SpaceGraph *sipo = CTX_wm_space_graph(C); + + /* Driver Editor's X axis is not time. */ + if (sipo->mode == SIPO_MODE_DRIVERS) { + return; + } + const Scene *scene = CTX_data_scene(C); - const bool draw_vert_line = sipo->mode != SIPO_MODE_DRIVERS; View2D *v2d = ®ion->v2d; /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME, draw_vert_line); + ED_time_scrub_draw_current_frame(region, scene, sipo->flag & SIPO_DRAWTIME); /* scrollers */ /* FIXME: args for scrollers depend on the type of data being shown. */ diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index 86349a64681..6a50f6d42e8 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -729,10 +729,6 @@ static void rna_update_cb(bContext *C, void *arg_cb, void *UNUSED(arg)) { RNAUpdateCb *cb = (RNAUpdateCb *)arg_cb; - /* ideally this would be done by RNA itself, but there we have - * no image user available, so we just update this flag here */ - cb->iuser->ok = 1; - /* we call update here on the pointer property, this way the * owner of the image pointer can still define its own update * and notifier */ @@ -1060,7 +1056,7 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, bool color_ma if (imf->imtype == R_IMF_IMTYPE_CINEON) { #if 1 - uiItemL(col, IFACE_("Hard coded Non-Linear, Gamma:1.7"), ICON_NONE); + uiItemL(col, TIP_("Hard coded Non-Linear, Gamma:1.7"), ICON_NONE); #else uiItemR(col, imfptr, "use_cineon_log", 0, NULL, ICON_NONE); uiItemR(col, imfptr, "cineon_black", 0, NULL, ICON_NONE); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index d2af26aa1d7..22a43ea3794 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -34,6 +34,7 @@ #include "DNA_scene_types.h" #include "DNA_screen_types.h" #include "DNA_space_types.h" +#include "DNA_view2d_types.h" #include "PIL_time.h" @@ -111,13 +112,6 @@ static void draw_render_info( GPU_matrix_translate_2f(x, y); GPU_matrix_scale_2f(zoomx, zoomy); - RenderData *rd = RE_engine_get_render_data(re); - if (rd->mode & R_BORDER) { - /* TODO: round or floor instead of casting to int */ - GPU_matrix_translate_2f((int)(-rd->border.xmin * rd->xsch * rd->size * 0.01f), - (int)(-rd->border.ymin * rd->ysch * rd->size * 0.01f)); - } - uint pos = GPU_vertformat_attr_add( immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); @@ -506,7 +500,7 @@ void draw_image_main_helpers(const bContext *C, ARegion *region) } } -bool ED_space_image_show_cache(SpaceImage *sima) +bool ED_space_image_show_cache(const SpaceImage *sima) { Image *image = ED_space_image(sima); Mask *mask = NULL; @@ -522,6 +516,17 @@ bool ED_space_image_show_cache(SpaceImage *sima) return true; } +bool ED_space_image_show_cache_and_mval_over(const SpaceImage *sima, + ARegion *region, + const int mval[2]) +{ + const rcti *rect_visible = ED_region_visible_rect(region); + if (mval[1] > rect_visible->ymin + (16 * UI_DPI_FAC)) { + return false; + } + return ED_space_image_show_cache(sima); +} + void draw_image_cache(const bContext *C, ARegion *region) { SpaceImage *sima = CTX_wm_space_image(C); @@ -576,3 +581,62 @@ void draw_image_cache(const bContext *C, ARegion *region) ED_mask_draw_frames(mask, region, cfra, sfra, efra); } } + +float ED_space_image_zoom_level(const View2D *v2d, const int grid_dimension) +{ + /* UV-space length per pixel */ + float xzoom = (v2d->cur.xmax - v2d->cur.xmin) / ((float)(v2d->mask.xmax - v2d->mask.xmin)); + float yzoom = (v2d->cur.ymax - v2d->cur.ymin) / ((float)(v2d->mask.ymax - v2d->mask.ymin)); + + /* Zoom_factor for UV/Image editor is calculated based on: + * - Default grid size on startup, which is 256x256 pixels + * - How blend factor for grid lines is set up in the fragment shader `grid_frag.glsl`. */ + float zoom_factor; + zoom_factor = (xzoom + yzoom) / 2.0f; /* Average for accuracy. */ + zoom_factor *= 256.0f / (powf(grid_dimension, 2)); + return zoom_factor; +} + +void ED_space_image_grid_steps(SpaceImage *sima, + float grid_steps[SI_GRID_STEPS_LEN], + const int grid_dimension) +{ + if (sima->flag & SI_CUSTOM_GRID) { + for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { + grid_steps[step] = powf(1, step) * (1.0f / ((float)sima->custom_grid_subdiv)); + } + } + else { + for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { + grid_steps[step] = powf(grid_dimension, step) * + (1.0f / (powf(grid_dimension, SI_GRID_STEPS_LEN))); + } + } +} + +/** + * Calculate the increment snapping value for UV/image editor based on the zoom factor + * The code in here (except the offset part) is used in `grid_frag.glsl` (see `grid_res`) for + * drawing the grid overlay for the UV/Image editor. + */ +float ED_space_image_increment_snap_value(const int grid_dimesnions, + const float grid_steps[SI_GRID_STEPS_LEN], + const float zoom_factor) +{ + /* Small offset on each grid_steps[] so that snapping value doesn't change until grid lines are + * significantly visible. + * `Offset = 3/4 * (grid_steps[i] - (grid_steps[i] / grid_dimesnsions))` + * + * Refer `grid_frag.glsl` to find out when grid lines actually start appearing */ + + for (int step = 0; step < SI_GRID_STEPS_LEN; step++) { + float offset = (3.0f / 4.0f) * (grid_steps[step] - (grid_steps[step] / grid_dimesnions)); + + if ((grid_steps[step] - offset) > zoom_factor) { + return grid_steps[step]; + } + } + + /* Fallback */ + return grid_steps[0]; +} diff --git a/source/blender/editors/space_image/image_edit.c b/source/blender/editors/space_image/image_edit.c index 2174a4b9dc1..05124f49a20 100644 --- a/source/blender/editors/space_image/image_edit.c +++ b/source/blender/editors/space_image/image_edit.c @@ -52,7 +52,7 @@ #include "WM_types.h" /* NOTE: image_panel_properties() uses pointer to sima->image directly. */ -Image *ED_space_image(SpaceImage *sima) +Image *ED_space_image(const SpaceImage *sima) { return sima->image; } @@ -113,7 +113,7 @@ void ED_space_image_auto_set(const bContext *C, SpaceImage *sima) } } -Mask *ED_space_image_get_mask(SpaceImage *sima) +Mask *ED_space_image_get_mask(const SpaceImage *sima) { return sima->mask_info.mask; } @@ -378,7 +378,7 @@ void ED_image_point_pos__reverse(SpaceImage *sima, } /** - * This is more a user-level functionality, for going to next/prev used slot, + * This is more a user-level functionality, for going to `next/prev` used slot, * Stepping onto the last unused slot too. */ bool ED_image_slot_cycle(struct Image *image, int direction) @@ -434,7 +434,7 @@ void ED_space_image_scopes_update(const struct bContext *C, /* We also don't update scopes of render result during render. */ if (G.is_rendering) { const Image *image = sima->image; - if (image != NULL && (image->type == IMA_TYPE_R_RESULT || image->type == IMA_TYPE_COMPOSITE)) { + if (image != NULL && (ELEM(image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE))) { return; } } @@ -445,12 +445,12 @@ void ED_space_image_scopes_update(const struct bContext *C, &scene->display_settings); } -bool ED_space_image_show_render(SpaceImage *sima) +bool ED_space_image_show_render(const SpaceImage *sima) { return (sima->image && ELEM(sima->image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)); } -bool ED_space_image_show_paint(SpaceImage *sima) +bool ED_space_image_show_paint(const SpaceImage *sima) { if (ED_space_image_show_render(sima)) { return false; @@ -459,7 +459,7 @@ bool ED_space_image_show_paint(SpaceImage *sima) return (sima->mode == SI_MODE_PAINT); } -bool ED_space_image_show_uvedit(SpaceImage *sima, Object *obedit) +bool ED_space_image_show_uvedit(const SpaceImage *sima, Object *obedit) { if (sima) { if (ED_space_image_show_render(sima)) { diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 94d44e047a4..478e484924a 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -360,8 +360,8 @@ static void image_view_pan_init(bContext *C, wmOperator *op, const wmEvent *even WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } - vpd->x = event->x; - vpd->y = event->y; + vpd->x = event->xy[0]; + vpd->y = event->xy[1]; vpd->xof = sima->xof; vpd->yof = sima->yof; vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); @@ -406,8 +406,8 @@ static int image_view_pan_invoke(bContext *C, wmOperator *op, const wmEvent *eve SpaceImage *sima = CTX_wm_space_image(C); float offset[2]; - offset[0] = (event->prevx - event->x) / sima->zoom; - offset[1] = (event->prevy - event->y) / sima->zoom; + offset[0] = (event->prev_xy[0] - event->xy[0]) / sima->zoom; + offset[1] = (event->prev_xy[1] - event->xy[1]) / sima->zoom; RNA_float_set_array(op->ptr, "offset", offset); image_view_pan_exec(C, op); @@ -428,8 +428,8 @@ static int image_view_pan_modal(bContext *C, wmOperator *op, const wmEvent *even case MOUSEMOVE: sima->xof = vpd->xof; sima->yof = vpd->yof; - offset[0] = (vpd->x - event->x) / sima->zoom; - offset[1] = (vpd->y - event->y) / sima->zoom; + offset[0] = (vpd->x - event->xy[0]) / sima->zoom; + offset[1] = (vpd->y - event->xy[1]) / sima->zoom; RNA_float_set_array(op->ptr, "offset", offset); image_view_pan_exec(C, op); break; @@ -516,8 +516,8 @@ static void image_view_zoom_init(bContext *C, wmOperator *op, const wmEvent *eve WM_cursor_modal_set(win, WM_CURSOR_NSEW_SCROLL); } - vpd->origx = event->x; - vpd->origy = event->y; + vpd->origx = event->xy[0]; + vpd->origy = event->xy[1]; vpd->zoom = sima->zoom; vpd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); @@ -584,7 +584,7 @@ static int image_view_zoom_invoke(bContext *C, wmOperator *op, const wmEvent *ev UI_view2d_region_to_view( ®ion->v2d, event->mval[0], event->mval[1], &location[0], &location[1]); - delta = event->prevx - event->x + event->prevy - event->y; + delta = event->prev_xy[0] - event->xy[0] + event->prev_xy[1] - event->xy[1]; if (U.uiflag & USER_ZOOM_INVERT) { delta *= -1; @@ -675,8 +675,8 @@ static int image_view_zoom_modal(bContext *C, wmOperator *op, const wmEvent *eve const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); image_zoom_apply(vpd, op, - event->x, - event->y, + event->xy[0], + event->xy[1], U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0, (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); @@ -2227,7 +2227,7 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) iter = IMB_moviecacheIter_new(image->cache); while (!IMB_moviecacheIter_done(iter)) { ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf->userflags & IB_BITMAPDIRTY) { + if (ibuf != NULL && ibuf->userflags & IB_BITMAPDIRTY) { if (first_ibuf == NULL) { first_ibuf = ibuf; } @@ -2251,7 +2251,7 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) while (!IMB_moviecacheIter_done(iter)) { ibuf = IMB_moviecacheIter_getImBuf(iter); - if (ibuf->userflags & IB_BITMAPDIRTY) { + if (ibuf != NULL && ibuf->userflags & IB_BITMAPDIRTY) { char name[FILE_MAX]; BLI_strncpy(name, ibuf->name, sizeof(name)); @@ -3628,13 +3628,8 @@ static int change_frame_invoke(bContext *C, wmOperator *op, const wmEvent *event ARegion *region = CTX_wm_region(C); if (region->regiontype == RGN_TYPE_WINDOW) { - SpaceImage *sima = CTX_wm_space_image(C); - - /* Local coordinate visible rect inside region, to accommodate overlapping ui. */ - const rcti *rect_visible = ED_region_visible_rect(region); - const int region_bottom = rect_visible->ymin; - - if (event->mval[1] > (region_bottom + 16 * UI_DPI_FAC) || !ED_space_image_show_cache(sima)) { + const SpaceImage *sima = CTX_wm_space_image(C); + if (!ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { return OPERATOR_PASS_THROUGH; } } diff --git a/source/blender/editors/space_image/image_undo.c b/source/blender/editors/space_image/image_undo.c index cc6effd0f71..3fbcc8348c8 100644 --- a/source/blender/editors/space_image/image_undo.c +++ b/source/blender/editors/space_image/image_undo.c @@ -668,7 +668,6 @@ static UndoImageHandle *uhandle_add(ListBase *undo_handles, Image *image, ImageU uh->image_ref.ptr = image; uh->iuser = *iuser; uh->iuser.scene = NULL; - uh->iuser.ok = 1; BLI_addtail(undo_handles, uh); return uh; } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index de8e4684d45..516ffcd1e75 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -126,13 +126,7 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( simage->tile_grid_shape[0] = 1; simage->tile_grid_shape[1] = 1; - /* tool header */ - region = MEM_callocN(sizeof(ARegion), "tool header for image"); - - BLI_addtail(&simage->regionbase, region); - region->regiontype = RGN_TYPE_TOOL_HEADER; - region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; - region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; + simage->custom_grid_subdiv = 10; /* header */ region = MEM_callocN(sizeof(ARegion), "header for image"); @@ -141,6 +135,14 @@ static SpaceLink *image_create(const ScrArea *UNUSED(area), const Scene *UNUSED( region->regiontype = RGN_TYPE_HEADER; region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + /* tool header */ + region = MEM_callocN(sizeof(ARegion), "tool header for image"); + + BLI_addtail(&simage->regionbase, region); + region->regiontype = RGN_TYPE_TOOL_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; + /* buttons/list view */ region = MEM_callocN(sizeof(ARegion), "buttons for image"); @@ -256,7 +258,7 @@ static void image_keymap(struct wmKeyConfig *keyconf) static bool image_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { ScrArea *area = CTX_wm_area(C); - if (ED_region_overlap_isect_any_xy(area, &event->x)) { + if (ED_region_overlap_isect_any_xy(area, event->xy)) { return false; } if (drag->type == WM_DRAG_PATH) { diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index a99396ecdf0..8e37e5fe9a8 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -564,7 +564,7 @@ void FILE_OT_find_missing_files(wmOperatorType *ot) /** \name Report Box Operator * \{ */ -/* NOTE(matt): Hard to decide whether to keep this as an operator, +/* NOTE(@broken): Hard to decide whether to keep this as an operator, * or turn it into a hard_coded UI control feature, * handling TIMER events for all regions in `interface_handlers.c`. * Not sure how good that is to be accessing UI data from diff --git a/source/blender/editors/space_info/info_stats.c b/source/blender/editors/space_info/info_stats.c index e749e1a7947..13d15bc50a6 100644 --- a/source/blender/editors/space_info/info_stats.c +++ b/source/blender/editors/space_info/info_stats.c @@ -372,7 +372,7 @@ static void stats_object_sculpt(const Object *ob, SceneStats *stats) SculptSession *ss = ob->sculpt; - if (!ss) { + if (ss == NULL || ss->pbvh == NULL) { return; } diff --git a/source/blender/editors/space_nla/nla_draw.c b/source/blender/editors/space_nla/nla_draw.c index c1b308d213f..bf2d20cf4c9 100644 --- a/source/blender/editors/space_nla/nla_draw.c +++ b/source/blender/editors/space_nla/nla_draw.c @@ -152,7 +152,7 @@ static void nla_action_draw_keyframes( format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", BLI_rcti_size_x(&v2d->mask) + 1, BLI_rcti_size_y(&v2d->mask) + 1); immBegin(GPU_PRIM_POINTS, key_len); @@ -323,12 +323,19 @@ static void nla_draw_strip_curves(NlaStrip *strip, float yminc, float ymaxc, uin { const float yheight = ymaxc - yminc; - immUniformColor3f(0.7f, 0.7f, 0.7f); - /* draw with AA'd line */ GPU_line_smooth(true); GPU_blend(GPU_BLEND_ALPHA); + /* Fully opaque line on selected strips. */ + if (strip->flag & NLASTRIP_FLAG_SELECT) { + /* TODO: Use theme setting. */ + immUniformColor3f(1.0f, 1.0f, 1.0f); + } + else { + immUniformColor4f(1.0f, 1.0f, 1.0f, 0.5f); + } + /* influence -------------------------- */ if (strip->flag & NLASTRIP_FLAG_USR_INFLUENCE) { FCurve *fcu = BKE_fcurve_find(&strip->fcurves, "influence", 0); @@ -501,7 +508,7 @@ static void nla_draw_strip(SpaceNla *snla, /* strip is in normal track */ UI_draw_roundbox_corner_set(UI_CNR_ALL); /* all corners rounded */ - UI_draw_roundbox_shade_x( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = strip->start, .xmax = strip->end, @@ -509,9 +516,7 @@ static void nla_draw_strip(SpaceNla *snla, .ymax = ymaxc, }, true, - 0.0, - 0.5, - 0.1, + 0.0f, color); /* restore current vertex format & program (roundbox trashes it) */ @@ -545,11 +550,9 @@ static void nla_draw_strip(SpaceNla *snla, /* draw strip outline * - color used here is to indicate active vs non-active */ - if (strip->flag & NLASTRIP_FLAG_ACTIVE) { + if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT)) { /* strip should appear 'sunken', so draw a light border around it */ - color[0] = 0.9f; /* FIXME: hardcoded temp-hack colors */ - color[1] = 1.0f; - color[2] = 0.9f; + color[0] = color[1] = color[2] = 1.0f; /* FIXME: hardcoded temp-hack colors */ } else { /* strip should appear to stand out, so draw a dark border around it */ @@ -566,7 +569,7 @@ static void nla_draw_strip(SpaceNla *snla, } else { /* non-muted - draw solid, rounded outline */ - UI_draw_roundbox_shade_x( + UI_draw_roundbox_4fv( &(const rctf){ .xmin = strip->start, .xmax = strip->end, @@ -574,9 +577,7 @@ static void nla_draw_strip(SpaceNla *snla, .ymax = ymaxc, }, false, - 0.0, - 0.0, - 0.1, + 0.0f, color); /* restore current vertex format & program (roundbox trashes it) */ @@ -661,7 +662,7 @@ static void nla_draw_strip_text(AnimData *adt, } /* set text color - if colors (see above) are light, draw black text, otherwise draw white */ - if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_SELECT | NLASTRIP_FLAG_TWEAKUSER)) { + if (strip->flag & (NLASTRIP_FLAG_ACTIVE | NLASTRIP_FLAG_TWEAKUSER)) { col[0] = col[1] = col[2] = 0; } else { @@ -805,29 +806,6 @@ void draw_nla_main_data(bAnimContext *ac, SpaceNla *snla, ARegion *region) immRectf( pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - /* draw 'embossed' lines above and below the strip for effect */ - /* white base-lines */ - GPU_line_width(2.0f); - immUniformColor4f(1.0f, 1.0f, 1.0f, 0.3f); - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - immEnd(); - - /* black top-lines */ - GPU_line_width(1.0f); - immUniformColor3f(0.0f, 0.0f, 0.0f); - immBegin(GPU_PRIM_LINES, 4); - immVertex2f(pos, v2d->cur.xmin, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymin + NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmin, ymax - NLACHANNEL_SKIP); - immVertex2f(pos, v2d->cur.xmax, ymax - NLACHANNEL_SKIP); - immEnd(); - - /* TODO: these lines but better --^ */ - immUnbindProgram(); /* draw keyframes in the action */ diff --git a/source/blender/editors/space_nla/nla_edit.c b/source/blender/editors/space_nla/nla_edit.c index c75b874833a..62f8906d136 100644 --- a/source/blender/editors/space_nla/nla_edit.c +++ b/source/blender/editors/space_nla/nla_edit.c @@ -402,9 +402,9 @@ static int nlaedit_previewrange_exec(bContext *C, wmOperator *UNUSED(op)) void NLA_OT_previewrange_set(wmOperatorType *ot) { /* identifiers */ - ot->name = "Auto-Set Preview Range"; + ot->name = "Set Preview Range to Selected"; ot->idname = "NLA_OT_previewrange_set"; - ot->description = "Automatically set Preview Range based on range of keyframes"; + ot->description = "Set Preview Range based on extends of selected strips"; /* api callbacks */ ot->exec = nlaedit_previewrange_exec; diff --git a/source/blender/editors/space_nla/space_nla.c b/source/blender/editors/space_nla/space_nla.c index 987d06cfe5c..8b44c26f07c 100644 --- a/source/blender/editors/space_nla/space_nla.c +++ b/source/blender/editors/space_nla/space_nla.c @@ -289,7 +289,7 @@ static void nla_main_region_draw_overlay(const bContext *C, ARegion *region) View2D *v2d = ®ion->v2d; /* scrubbing region */ - ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME, true); + ED_time_scrub_draw_current_frame(region, scene, snla->flag & SNLA_DRAWTIME); /* scrollers */ UI_view2d_scrollers_draw(v2d, NULL); diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 80d3b43bf6b..600309c2c86 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -40,6 +40,7 @@ set(INC set(SRC drawnode.cc node_add.cc + node_context_path.cc node_draw.cc node_edit.cc node_geometry_attribute_search.cc diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc index a3fd8ab5f4a..9e65a6db9fb 100644 --- a/source/blender/editors/space_node/drawnode.cc +++ b/source/blender/editors/space_node/drawnode.cc @@ -73,6 +73,7 @@ #include "NOD_composite.h" #include "NOD_geometry.h" +#include "NOD_node_declaration.hh" #include "NOD_shader.h" #include "NOD_texture.h" #include "node_intern.h" /* own include */ @@ -164,6 +165,11 @@ static void node_buts_curvevec(uiLayout *layout, bContext *UNUSED(C), PointerRNA uiTemplateCurveMapping(layout, ptr, "mapping", 'v', false, false, false, false); } +static void node_buts_curvefloat(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiTemplateCurveMapping(layout, ptr, "mapping", 0, false, false, false, false); +} + #define SAMPLE_FLT_ISNONE FLT_MAX /* Bad bad, 2.5 will do better? ... no it won't! */ static float _sample_col[4] = {SAMPLE_FLT_ISNONE}; @@ -446,7 +452,7 @@ static void node_draw_frame(const bContext *C, const rctf *rct = &node->totr; UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(rct, true, BASIS_RAD, color); + UI_draw_roundbox_4fv(rct, true, BASIS_RAD, color); /* outline active and selected emphasis */ if (node->flag & SELECT) { @@ -986,7 +992,7 @@ static void node_shader_buts_vertex_color(uiLayout *layout, bContext *C, Pointer } } else { - uiItemL(layout, "No mesh in active object.", ICON_ERROR); + uiItemL(layout, TIP_("No mesh in active object"), ICON_ERROR); } } @@ -1182,6 +1188,9 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_CURVE_RGB: ntype->draw_buttons = node_buts_curvecol; break; + case SH_NODE_CURVE_FLOAT: + ntype->draw_buttons = node_buts_curvefloat; + break; case SH_NODE_MAPPING: ntype->draw_buttons = node_shader_buts_mapping; break; @@ -1562,7 +1571,7 @@ static void node_composit_buts_antialiasing(uiLayout *layout, bContext *UNUSED(C uiItemR(col, ptr, "corner_rounding", 0, nullptr, ICON_NONE); } -/* qdn: glare node */ +/* glare node */ static void node_composit_buts_glare(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "glare_type", DEFAULT_FLAGS, "", ICON_NONE); @@ -2078,7 +2087,7 @@ static void node_composit_buts_premulkey(uiLayout *layout, bContext *UNUSED(C), static void node_composit_buts_view_levels(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiItemR(layout, ptr, "channel", DEFAULT_FLAGS | UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "channel", DEFAULT_FLAGS, "", ICON_NONE); } static void node_composit_buts_colorbalance(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -2864,6 +2873,8 @@ static void node_composit_buts_denoise(uiLayout *layout, bContext *UNUSED(C), Po # endif #endif + uiItemL(layout, IFACE_("Prefilter:"), ICON_NONE); + uiItemR(layout, ptr, "prefilter", DEFAULT_FLAGS, nullptr, ICON_NONE); uiItemR(layout, ptr, "use_hdr", DEFAULT_FLAGS, nullptr, ICON_NONE); } @@ -3524,6 +3535,18 @@ static void node_file_output_socket_draw(bContext *C, } } +static bool socket_needs_attribute_search(bNode &node, bNodeSocket &socket) +{ + if (node.declaration == nullptr) { + return false; + } + if (socket.in_out == SOCK_OUT) { + return false; + } + const int socket_index = BLI_findindex(&node.inputs, &socket); + return node.declaration->inputs()[socket_index]->is_attribute_name(); +} + static void std_node_socket_draw( bContext *C, uiLayout *layout, PointerRNA *ptr, PointerRNA *node_ptr, const char *text) { @@ -3566,17 +3589,22 @@ static void std_node_socket_draw( } break; case SOCK_RGBA: { - uiLayout *row = uiLayoutSplit(layout, 0.4f, false); - uiItemL(row, text, 0); - uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + if (text[0] == '\0') { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, "", 0); + } + else { + uiLayout *row = uiLayoutSplit(layout, 0.4f, false); + uiItemL(row, text, 0); + uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + } break; } case SOCK_STRING: { uiLayout *row = uiLayoutSplit(layout, 0.4f, false); uiItemL(row, text, 0); - const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; - if (node_tree->type == NTREE_GEOMETRY) { + if (socket_needs_attribute_search(*node, *sock)) { + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; node_geometry_add_attribute_search_button(C, node_tree, node, ptr, row); } else { @@ -3590,7 +3618,39 @@ static void std_node_socket_draw( break; } case SOCK_IMAGE: { - uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; + if (node_tree->type == NTREE_GEOMETRY) { + if (text[0] == '\0') { + uiTemplateID(layout, + C, + ptr, + "default_value", + "image.new", + "image.open", + nullptr, + 0, + ICON_NONE, + nullptr); + } + else { + /* 0.3 split ratio is inconsistent, but use it here because the "New" button is large. */ + uiLayout *row = uiLayoutSplit(layout, 0.3f, false); + uiItemL(row, text, 0); + uiTemplateID(row, + C, + ptr, + "default_value", + "image.new", + "image.open", + nullptr, + 0, + ICON_NONE, + nullptr); + } + } + else { + uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0); + } break; } case SOCK_COLLECTION: { @@ -3928,11 +3988,17 @@ static struct { GPUBatch *batch_single; /* for single line */ GPUVertBuf *inst_vbo; uint p0_id, p1_id, p2_id, p3_id; - uint colid_id, muted_id; + uint colid_id, muted_id, start_color_id, end_color_id; uint dim_factor_id; + uint thickness_id; + uint dash_factor_id; + uint dash_alpha_id; GPUVertBufRaw p0_step, p1_step, p2_step, p3_step; - GPUVertBufRaw colid_step, muted_step; + GPUVertBufRaw colid_step, muted_step, start_color_step, end_color_step; GPUVertBufRaw dim_factor_step; + GPUVertBufRaw thickness_step; + GPUVertBufRaw dash_factor_step; + GPUVertBufRaw dash_alpha_step; uint count; bool enabled; } g_batch_link; @@ -3949,6 +4015,16 @@ static void nodelink_batch_reset() g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step); GPU_vertbuf_attr_get_raw_data( g_batch_link.inst_vbo, g_batch_link.dim_factor_id, &g_batch_link.dim_factor_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.thickness_id, &g_batch_link.thickness_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.dash_factor_id, &g_batch_link.dash_factor_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.dash_alpha_id, &g_batch_link.dash_alpha_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.start_color_id, &g_batch_link.start_color_step); + GPU_vertbuf_attr_get_raw_data( + g_batch_link.inst_vbo, g_batch_link.end_color_id, &g_batch_link.end_color_step); g_batch_link.count = 0; } @@ -4064,10 +4140,20 @@ static void nodelink_batch_init() &format_inst, "P3", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); g_batch_link.colid_id = GPU_vertformat_attr_add( &format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT); + g_batch_link.start_color_id = GPU_vertformat_attr_add( + &format_inst, "start_color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + g_batch_link.end_color_id = GPU_vertformat_attr_add( + &format_inst, "end_color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); g_batch_link.muted_id = GPU_vertformat_attr_add( &format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT); g_batch_link.dim_factor_id = GPU_vertformat_attr_add( &format_inst, "dim_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + g_batch_link.thickness_id = GPU_vertformat_attr_add( + &format_inst, "thickness", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + g_batch_link.dash_factor_id = GPU_vertformat_attr_add( + &format_inst, "dash_factor", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); + g_batch_link.dash_alpha_id = GPU_vertformat_attr_add( + &format_inst, "dash_alpha", GPU_COMP_F32, 1, GPU_FETCH_FLOAT); g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM); /* Alloc max count but only draw the range we need. */ GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE); @@ -4142,9 +4228,14 @@ static void nodelink_batch_add_link(const SpaceNode *snode, int th_col1, int th_col2, int th_col3, + const float start_color[4], + const float end_color[4], bool drawarrow, bool drawmuted, - float dim_factor) + float dim_factor, + float thickness, + float dash_factor, + float dash_alpha) { /* Only allow these colors. If more is needed, you need to modify the shader accordingly. */ BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT)); @@ -4161,9 +4252,14 @@ static void nodelink_batch_add_link(const SpaceNode *snode, colid[1] = nodelink_get_color_id(th_col2); colid[2] = nodelink_get_color_id(th_col3); colid[3] = drawarrow; + copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.start_color_step), start_color); + copy_v4_v4((float *)GPU_vertbuf_raw_step(&g_batch_link.end_color_step), end_color); char *muted = (char *)GPU_vertbuf_raw_step(&g_batch_link.muted_step); muted[0] = drawmuted; *(float *)GPU_vertbuf_raw_step(&g_batch_link.dim_factor_step) = dim_factor; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.thickness_step) = thickness; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_factor_step) = dash_factor; + *(float *)GPU_vertbuf_raw_step(&g_batch_link.dash_alpha_step) = dash_alpha; if (g_batch_link.count == NODELINK_GROUP_SIZE) { nodelink_batch_draw(snode); @@ -4171,7 +4267,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode, } /* don't do shadows if th_col3 is -1. */ -void node_draw_link_bezier(const View2D *v2d, +void node_draw_link_bezier(const bContext *C, + const View2D *v2d, const SpaceNode *snode, const bNodeLink *link, int th_col1, @@ -4179,6 +4276,20 @@ void node_draw_link_bezier(const View2D *v2d, int th_col3) { const float dim_factor = node_link_dim_factor(v2d, link); + float thickness = 1.5f; + float dash_factor = 1.0f; + + bTheme *btheme = UI_GetTheme(); + const float dash_alpha = btheme->space_node.dash_alpha; + + if (snode->edittree->type == NTREE_GEOMETRY) { + if (link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) { + /* Make field links a bit thinner. */ + thickness = 1.0f; + /* Draw field as dashes. */ + dash_factor = 0.75f; + } + } float vec[4][2]; const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT; @@ -4189,6 +4300,56 @@ void node_draw_link_bezier(const View2D *v2d, if (g_batch_link.batch == nullptr) { nodelink_batch_init(); } + /* Draw single link. */ + float colors[3][4] = {{0.0f}}; + if (th_col3 != -1) { + UI_GetThemeColor4fv(th_col3, colors[0]); + } + + if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && + snode->overlay.flag & SN_OVERLAY_SHOW_WIRE_COLORS && + ((link->fromsock == nullptr || link->fromsock->typeinfo->type >= 0) && + (link->tosock == nullptr || link->tosock->typeinfo->type >= 0))) { + PointerRNA from_node_ptr, to_node_ptr; + RNA_pointer_create((ID *)snode->edittree, &RNA_Node, link->fromnode, &from_node_ptr); + RNA_pointer_create((ID *)snode->edittree, &RNA_Node, link->tonode, &to_node_ptr); + if (link->fromsock) { + node_socket_color_get(C, snode->edittree, &from_node_ptr, link->fromsock, colors[1]); + } + else { + node_socket_color_get(C, snode->edittree, &to_node_ptr, link->tosock, colors[1]); + } + + if (link->tosock) { + node_socket_color_get(C, snode->edittree, &to_node_ptr, link->tosock, colors[2]); + } + else { + node_socket_color_get(C, snode->edittree, &from_node_ptr, link->fromsock, colors[2]); + } + } + else { + UI_GetThemeColor4fv(th_col1, colors[1]); + UI_GetThemeColor4fv(th_col2, colors[2]); + } + + /* Highlight links connected to selected nodes. */ + const bool is_fromnode_selected = link->fromnode && link->fromnode->flag & SELECT; + const bool is_tonode_selected = link->tonode && link->tonode->flag & SELECT; + if (is_fromnode_selected || is_tonode_selected) { + float color_selected[4]; + UI_GetThemeColor4fv(TH_EDGE_SELECT, color_selected); + const float alpha = color_selected[3]; + + /* Interpolate color if highlight color is not fully transparent. */ + if (alpha != 0.0) { + if (is_fromnode_selected) { + interp_v3_v3v3(colors[1], colors[1], color_selected, alpha); + } + if (is_tonode_selected) { + interp_v3_v3v3(colors[2], colors[2], color_selected, alpha); + } + } + } if (g_batch_link.enabled && !highlighted) { /* Add link to batch. */ @@ -4200,19 +4361,16 @@ void node_draw_link_bezier(const View2D *v2d, th_col1, th_col2, th_col3, + colors[1], + colors[2], drawarrow, drawmuted, - dim_factor); + dim_factor, + thickness, + dash_factor, + dash_alpha); } else { - /* Draw single link. */ - float colors[3][4] = {{0.0f}}; - if (th_col3 != -1) { - UI_GetThemeColor4fv(th_col3, colors[0]); - } - UI_GetThemeColor4fv(th_col1, colors[1]); - UI_GetThemeColor4fv(th_col2, colors[2]); - if (highlighted) { float link_preselection_highlight_color[4]; UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color); @@ -4228,13 +4386,16 @@ void node_draw_link_bezier(const View2D *v2d, GPU_batch_uniform_1i(batch, "doArrow", drawarrow); GPU_batch_uniform_1i(batch, "doMuted", drawmuted); GPU_batch_uniform_1f(batch, "dim_factor", dim_factor); + GPU_batch_uniform_1f(batch, "thickness", thickness); + GPU_batch_uniform_1f(batch, "dash_factor", dash_factor); + GPU_batch_uniform_1f(batch, "dash_alpha", dash_alpha); GPU_batch_draw(batch); } } } /* NOTE: this is used for fake links in groups too. */ -void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) +void node_draw_link(const bContext *C, View2D *v2d, SpaceNode *snode, bNodeLink *link) { int th_col1 = TH_WIRE_INNER, th_col2 = TH_WIRE_INNER, th_col3 = TH_WIRE; @@ -4263,15 +4424,6 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) else if (link->flag & NODE_LINK_MUTED) { th_col1 = th_col2 = TH_REDALERT; } - else { - /* Regular link, highlight if connected to selected node. */ - if (link->fromnode && link->fromnode->flag & SELECT) { - th_col1 = TH_EDGE_SELECT; - } - if (link->tonode && link->tonode->flag & SELECT) { - th_col2 = TH_EDGE_SELECT; - } - } } else { /* Invalid link. */ @@ -4279,8 +4431,15 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link) // th_col3 = -1; /* no shadow */ } } + /* Links from field to non-field sockets are not allowed. */ + if (snode->edittree->type == NTREE_GEOMETRY && !(link->flag & NODE_LINK_DRAGGED)) { + if ((link->fromsock && link->fromsock->display_shape == SOCK_DISPLAY_SHAPE_DIAMOND) && + (link->tosock && link->tosock->display_shape == SOCK_DISPLAY_SHAPE_CIRCLE)) { + th_col1 = th_col2 = th_col3 = TH_REDALERT; + } + } - node_draw_link_bezier(v2d, snode, link, th_col1, th_col2, th_col3); + node_draw_link_bezier(C, v2d, snode, link, th_col1, th_col2, th_col3); } void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder border, uint pos) diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc index 7b6ca5e6e61..cb66d0dbd2b 100644 --- a/source/blender/editors/space_node/node_add.cc +++ b/source/blender/editors/space_node/node_add.cc @@ -758,7 +758,7 @@ static bool node_add_file_poll(bContext *C) { const SpaceNode *snode = CTX_wm_space_node(C); return ED_operator_node_editable(C) && - ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT); + ELEM(snode->nodetree->type, NTREE_SHADER, NTREE_TEXTURE, NTREE_COMPOSIT, NTREE_GEOMETRY); } static int node_add_file_exec(bContext *C, wmOperator *op) @@ -784,6 +784,9 @@ static int node_add_file_exec(bContext *C, wmOperator *op) case NTREE_COMPOSIT: type = CMP_NODE_IMAGE; break; + case NTREE_GEOMETRY: + type = GEO_NODE_IMAGE_TEXTURE; + break; default: return OPERATOR_CANCELLED; } @@ -797,7 +800,14 @@ static int node_add_file_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - node->id = (ID *)ima; + if (type == GEO_NODE_IMAGE_TEXTURE) { + bNodeSocket *image_socket = (bNodeSocket *)node->inputs.first; + bNodeSocketValueImage *socket_value = (bNodeSocketValueImage *)image_socket->default_value; + socket_value->value = ima; + } + else { + node->id = (ID *)ima; + } /* When adding new image file via drag-drop we need to load imbuf in order * to get proper image source. diff --git a/source/blender/editors/space_node/node_context_path.cc b/source/blender/editors/space_node/node_context_path.cc new file mode 100644 index 00000000000..a0ff7f3ce25 --- /dev/null +++ b/source/blender/editors/space_node/node_context_path.cc @@ -0,0 +1,184 @@ +/* + * 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) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spnode + * \brief Node breadcrumbs drawing + */ + +#include "BLI_vector.hh" + +#include "DNA_node_types.h" + +#include "BKE_context.h" +#include "BKE_material.h" +#include "BKE_modifier.h" +#include "BKE_object.h" + +#include "BKE_screen.h" + +#include "RNA_access.h" + +#include "ED_screen.h" + +#include "UI_interface.h" +#include "UI_interface.hh" +#include "UI_resources.h" + +#include "UI_interface.hh" + +#include "node_intern.h" + +struct Mesh; +struct Curve; +struct Light; +struct World; +struct Material; + +namespace blender::ed::space_node { + +static void context_path_add_object_data(Vector &path, Object &object) +{ + if (object.type == OB_MESH && object.data) { + Mesh *mesh = (Mesh *)object.data; + ui::context_path_add_generic(path, RNA_Mesh, mesh); + } + if (object.type == OB_LAMP && object.data) { + Light *light = (Light *)object.data; + ui::context_path_add_generic(path, RNA_Light, light); + } + if (ELEM(object.type, OB_CURVE, OB_FONT, OB_SURF) && object.data) { + Curve *curve = (Curve *)object.data; + ui::context_path_add_generic(path, RNA_Curve, curve); + } +} + +static void context_path_add_node_tree_and_node_groups(const SpaceNode &snode, + Vector &path, + const bool skip_base = false) +{ + Vector tree_path = snode.treepath; + for (const bNodeTreePath *path_item : tree_path.as_span().drop_front(int(skip_base))) { + ui::context_path_add_generic(path, RNA_NodeTree, path_item->nodetree, ICON_NODETREE); + } +} + +static void get_context_path_node_shader(const bContext &C, + SpaceNode &snode, + Vector &path) +{ + if (snode.flag & SNODE_PIN) { + if (snode.shaderfrom == SNODE_SHADER_WORLD) { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + if (scene != nullptr) { + World *world = scene->world; + ui::context_path_add_generic(path, RNA_World, world); + } + /* Skip the base node tree here, because the world contains a node tree already. */ + context_path_add_node_tree_and_node_groups(snode, path, true); + } + else { + context_path_add_node_tree_and_node_groups(snode, path); + } + } + else { + Object *object = CTX_data_active_object(&C); + if (snode.shaderfrom == SNODE_SHADER_OBJECT && object != nullptr) { + ui::context_path_add_generic(path, RNA_Object, object); + if (!(object->matbits && object->matbits[object->actcol - 1])) { + context_path_add_object_data(path, *object); + } + Material *material = BKE_object_material_get(object, object->actcol); + ui::context_path_add_generic(path, RNA_Material, material); + } + else if (snode.shaderfrom == SNODE_SHADER_WORLD) { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + if (scene != nullptr) { + World *world = scene->world; + ui::context_path_add_generic(path, RNA_World, world); + } + } +#ifdef WITH_FREESTYLE + else if (snode.shaderfrom == SNODE_SHADER_LINESTYLE) { + ViewLayer *viewlayer = CTX_data_view_layer(&C); + FreestyleLineStyle *linestyle = BKE_linestyle_active_from_view_layer(viewlayer); + ui::context_path_add_generic(path, RNA_ViewLayer, viewlayer); + Material *mat = BKE_object_material_get(object, object->actcol); + ui::context_path_add_generic(path, RNA_Material, mat); + } +#endif + context_path_add_node_tree_and_node_groups(snode, path, true); + } +} + +static void get_context_path_node_compositor(const bContext &C, + SpaceNode &snode, + Vector &path) +{ + if (snode.flag & SNODE_PIN) { + context_path_add_node_tree_and_node_groups(snode, path); + } + else { + Scene *scene = CTX_data_scene(&C); + ui::context_path_add_generic(path, RNA_Scene, scene); + context_path_add_node_tree_and_node_groups(snode, path); + } +} + +static void get_context_path_node_geometry(const bContext &C, + SpaceNode &snode, + Vector &path) +{ + if (snode.flag & SNODE_PIN) { + context_path_add_node_tree_and_node_groups(snode, path); + } + else { + Object *object = CTX_data_active_object(&C); + ui::context_path_add_generic(path, RNA_Object, object); + ModifierData *modifier = BKE_object_active_modifier(object); + ui::context_path_add_generic(path, RNA_Modifier, modifier, ICON_MODIFIER); + context_path_add_node_tree_and_node_groups(snode, path); + } +} + +Vector context_path_for_space_node(const bContext &C) +{ + SpaceNode *snode = CTX_wm_space_node(&C); + if (snode == nullptr) { + return {}; + } + + Vector context_path; + + if (snode->edittree->type == NTREE_GEOMETRY) { + get_context_path_node_geometry(C, *snode, context_path); + } + else if (snode->edittree->type == NTREE_SHADER) { + get_context_path_node_shader(C, *snode, context_path); + } + else if (snode->edittree->type == NTREE_COMPOSIT) { + get_context_path_node_compositor(C, *snode, context_path); + } + + return context_path; +} + +} // namespace blender::ed::space_node diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index aa241071425..f76c29ef0d9 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -71,14 +71,17 @@ #include "ED_gpencil.h" #include "ED_node.h" +#include "ED_screen.h" #include "ED_space_api.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "UI_view2d.h" #include "RNA_access.h" #include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_node_declaration.hh" #include "FN_field_cpp_type.hh" @@ -722,23 +725,20 @@ int node_get_colorid(bNode *node) } } -static void node_draw_mute_line(const View2D *v2d, const SpaceNode *snode, const bNode *node) +static void node_draw_mute_line(const bContext *C, + const View2D *v2d, + const SpaceNode *snode, + const bNode *node) { GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (const bNodeLink *, link, &node->internal_links) { - node_draw_link_bezier(v2d, snode, link, TH_REDALERT, TH_REDALERT, -1); + node_draw_link_bezier(C, v2d, snode, link, TH_WIRE_INNER, TH_WIRE_INNER, TH_WIRE); } GPU_blend(GPU_BLEND_NONE); } -/* Flags used in gpu_shader_keyframe_diamond_frag.glsl. */ -#define MARKER_SHAPE_DIAMOND 0x1 -#define MARKER_SHAPE_SQUARE 0xC -#define MARKER_SHAPE_CIRCLE 0x2 -#define MARKER_SHAPE_INNER_DOT 0x10 - static void node_socket_draw(const bNodeSocket *sock, const float color[4], const float color_outline[4], @@ -757,16 +757,16 @@ static void node_socket_draw(const bNodeSocket *sock, switch (sock->display_shape) { case SOCK_DISPLAY_SHAPE_DIAMOND: case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: - flags = MARKER_SHAPE_DIAMOND; + flags = GPU_KEYFRAME_SHAPE_DIAMOND; break; case SOCK_DISPLAY_SHAPE_SQUARE: case SOCK_DISPLAY_SHAPE_SQUARE_DOT: - flags = MARKER_SHAPE_SQUARE; + flags = GPU_KEYFRAME_SHAPE_SQUARE; break; default: case SOCK_DISPLAY_SHAPE_CIRCLE: case SOCK_DISPLAY_SHAPE_CIRCLE_DOT: - flags = MARKER_SHAPE_CIRCLE; + flags = GPU_KEYFRAME_SHAPE_CIRCLE; break; } @@ -774,7 +774,7 @@ static void node_socket_draw(const bNodeSocket *sock, SOCK_DISPLAY_SHAPE_DIAMOND_DOT, SOCK_DISPLAY_SHAPE_SQUARE_DOT, SOCK_DISPLAY_SHAPE_CIRCLE_DOT)) { - flags |= MARKER_SHAPE_INNER_DOT; + flags |= GPU_KEYFRAME_SHAPE_INNER_DOT; } immAttr4fv(col_id, color); @@ -812,12 +812,10 @@ static void node_socket_outline_color_get(const bool selected, float r_outline_color[4]) { if (selected) { - UI_GetThemeColor4fv(TH_TEXT_HI, r_outline_color); - r_outline_color[3] = 0.9f; + UI_GetThemeColor4fv(TH_ACTIVE, r_outline_color); } else { - copy_v4_fl(r_outline_color, 0.0f); - r_outline_color[3] = 0.6f; + UI_GetThemeColor4fv(TH_WIRE, r_outline_color); } /* Until there is a better place for per socket color, @@ -830,18 +828,13 @@ static void node_socket_outline_color_get(const bool selected, /* Usual convention here would be node_socket_get_color(), but that's already used (for setting a * color property socket). */ void node_socket_color_get( - bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4]) + const bContext *C, bNodeTree *ntree, PointerRNA *node_ptr, bNodeSocket *sock, float r_color[4]) { PointerRNA ptr; BLI_assert(RNA_struct_is_a(node_ptr->type, &RNA_Node)); RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); - sock->typeinfo->draw_color(C, &ptr, node_ptr, r_color); - - bNode *node = (bNode *)node_ptr->data; - if (node->flag & NODE_MUTED) { - r_color[3] *= 0.25f; - } + sock->typeinfo->draw_color((bContext *)C, &ptr, node_ptr, r_color); } struct SocketTooltipData { @@ -859,60 +852,7 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal const GPointer value = value_log.value(); const CPPType &type = *value.type(); - if (const FieldCPPType *field_type = dynamic_cast(&type)) { - const CPPType &base_type = field_type->field_type(); - BUFFER_FOR_CPP_TYPE_VALUE(base_type, buffer); - const GField &field = field_type->get_gfield(value.get()); - if (field.node().depends_on_input()) { - if (base_type.is()) { - ss << TIP_("Integer Field"); - } - else if (base_type.is()) { - ss << TIP_("Float Field"); - } - else if (base_type.is()) { - ss << TIP_("Vector Field"); - } - else if (base_type.is()) { - ss << TIP_("Boolean Field"); - } - else if (base_type.is()) { - ss << TIP_("String Field"); - } - ss << TIP_(" based on:\n"); - - /* Use vector set to deduplicate inputs. */ - VectorSet> field_inputs; - field.node().foreach_field_input( - [&](const FieldInput &field_input) { field_inputs.add(field_input); }); - for (const FieldInput &field_input : field_inputs) { - ss << "\u2022 " << field_input.socket_inspection_name(); - if (field_input != field_inputs.as_span().last().get()) { - ss << ".\n"; - } - } - } - else { - blender::fn::evaluate_constant_field(field, buffer); - if (base_type.is()) { - ss << *(int *)buffer << TIP_(" (Integer)"); - } - else if (base_type.is()) { - ss << *(float *)buffer << TIP_(" (Float)"); - } - else if (base_type.is()) { - ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); - } - else if (base_type.is()) { - ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); - } - else if (base_type.is()) { - ss << *(std::string *)buffer << TIP_(" (String)"); - } - base_type.destruct(buffer); - } - } - else if (type.is()) { + if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_OB); } else if (type.is()) { @@ -921,11 +861,79 @@ static void create_inspection_string_for_generic_value(const geo_log::GenericVal else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_TE); } + else if (type.is()) { + id_to_inspection_string((ID *)*value.get(), ID_IM); + } else if (type.is()) { id_to_inspection_string((ID *)*value.get(), ID_GR); } } +static void create_inspection_string_for_gfield(const geo_log::GFieldValueLog &value_log, + std::stringstream &ss) +{ + const CPPType &type = value_log.type(); + const GField &field = value_log.field(); + const Span input_tooltips = value_log.input_tooltips(); + + if (input_tooltips.is_empty()) { + if (field) { + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + blender::fn::evaluate_constant_field(field, buffer); + if (type.is()) { + ss << *(int *)buffer << TIP_(" (Integer)"); + } + else if (type.is()) { + ss << *(float *)buffer << TIP_(" (Float)"); + } + else if (type.is()) { + ss << *(blender::float3 *)buffer << TIP_(" (Vector)"); + } + else if (type.is()) { + ss << ((*(bool *)buffer) ? TIP_("True") : TIP_("False")) << TIP_(" (Boolean)"); + } + else if (type.is()) { + ss << *(std::string *)buffer << TIP_(" (String)"); + } + type.destruct(buffer); + } + else { + /* Constant values should always be logged. */ + BLI_assert_unreachable(); + ss << "Value has not been logged"; + } + } + else { + if (type.is()) { + ss << TIP_("Integer field"); + } + else if (type.is()) { + ss << TIP_("Float field"); + } + else if (type.is()) { + ss << TIP_("Vector field"); + } + else if (type.is()) { + ss << TIP_("Boolean field"); + } + else if (type.is()) { + ss << TIP_("String field"); + } + else if (type.is()) { + ss << TIP_("Color field"); + } + ss << TIP_(" based on:\n"); + + for (const int i : input_tooltips.index_range()) { + const blender::StringRef tooltip = input_tooltips[i]; + ss << "\u2022 " << tooltip; + if (i < input_tooltips.size() - 1) { + ss << ".\n"; + } + } + } +} + static void create_inspection_string_for_geometry(const geo_log::GeometryValueLog &value_log, std::stringstream &ss) { @@ -1017,6 +1025,10 @@ static std::optional create_socket_inspection_string(bContext *C, dynamic_cast(value_log)) { create_inspection_string_for_generic_value(*generic_value_log, ss); } + if (const geo_log::GFieldValueLog *gfield_value_log = + dynamic_cast(value_log)) { + create_inspection_string_for_gfield(*gfield_value_log, ss); + } else if (const geo_log::GeometryValueLog *geo_value_log = dynamic_cast(value_log)) { create_inspection_string_for_geometry(*geo_value_log, ss); @@ -1040,7 +1052,7 @@ static void node_socket_draw_nested(const bContext *C, float color[4]; float outline_color[4]; - node_socket_color_get((bContext *)C, ntree, node_ptr, sock, color); + node_socket_color_get(C, ntree, node_ptr, sock, color); node_socket_outline_color_get(selected, sock->type, outline_color); node_socket_draw(sock, @@ -1091,12 +1103,37 @@ static void node_socket_draw_nested(const bContext *C, but, [](bContext *C, void *argN, const char *UNUSED(tip)) { SocketTooltipData *data = (SocketTooltipData *)argN; - std::optional str = create_socket_inspection_string( + std::optional socket_inspection_str = create_socket_inspection_string( C, *data->ntree, *data->node, *data->socket); - if (str.has_value()) { - return BLI_strdup(str->c_str()); + + std::stringstream output; + if (data->node->declaration != nullptr) { + ListBase *list; + Span decl_list; + + if (data->socket->in_out == SOCK_IN) { + list = &data->node->inputs; + decl_list = data->node->declaration->inputs(); + } + else { + list = &data->node->outputs; + decl_list = data->node->declaration->outputs(); + } + + const int socket_index = BLI_findindex(list, data->socket); + const blender::nodes::SocketDeclaration &socket_decl = *decl_list[socket_index]; + blender::StringRef description = socket_decl.description(); + if (!description.is_empty()) { + output << TIP_(description.data()) << ".\n\n"; + } } - return BLI_strdup(TIP_("The socket value has not been computed yet")); + if (socket_inspection_str.has_value()) { + output << *socket_inspection_str; + } + else { + output << TIP_("The socket value has not been computed yet"); + } + return BLI_strdup(output.str().c_str()); }, data, MEM_freeN); @@ -1132,8 +1169,8 @@ void ED_node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[ GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); - immUniform1f("outline_scale", 0.7f); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Single point. */ @@ -1277,8 +1314,8 @@ void node_draw_sockets(const View2D *v2d, GPU_blend(GPU_BLEND_ALPHA); GPU_program_point_size(true); - immBindBuiltinProgram(GPU_SHADER_KEYFRAME_DIAMOND); - immUniform1f("outline_scale", 0.7f); + immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE); + immUniform1f("outline_scale", 1.0f); immUniform2f("ViewportSize", -1.0f, -1.0f); /* Set handle size. */ @@ -1430,7 +1467,7 @@ void node_draw_sockets(const View2D *v2d, float color[4]; float outline_color[4]; - node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color); + node_socket_color_get(C, ntree, &node_ptr, socket, color); node_socket_outline_color_get(selected, socket->type, outline_color); node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy); @@ -1446,6 +1483,8 @@ static int node_error_type_to_icon(const geo_log::NodeWarningType type) return ICON_ERROR; case geo_log::NodeWarningType::Info: return ICON_INFO; + case geo_log::NodeWarningType::Legacy: + return ICON_ERROR; } BLI_assert(false); @@ -1456,6 +1495,8 @@ static uint8_t node_error_type_priority(const geo_log::NodeWarningType type) { switch (type) { case geo_log::NodeWarningType::Error: + return 4; + case geo_log::NodeWarningType::Legacy: return 3; case geo_log::NodeWarningType::Warning: return 2; @@ -1568,24 +1609,13 @@ static void node_draw_basis(const bContext *C, /* Shadow. */ node_draw_shadow(snode, node, BASIS_RAD, 1.0f); + rctf *rct = &node->totr; float color[4]; int color_id = node_get_colorid(node); - if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor3fv(TH_NODE, color); - color[3] = 0.25f; - } - else { - /* Opaque headers for regular nodes. */ - UI_GetThemeColor3fv(color_id, color); - color[3] = 1.0f; - } GPU_line_width(1.0f); - rctf *rct = &node->totr; - UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); - + /* Header. */ { const rctf rect = { rct->xmin, @@ -1593,7 +1623,19 @@ static void node_draw_basis(const bContext *C, rct->ymax - NODE_DY, rct->ymax, }; - UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color); + + float color_header[4]; + + /* Muted nodes get a mix of the background with the node color. */ + if (node->flag & NODE_MUTED) { + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.1f, color_header); + } + else { + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color_header); + } + + UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color_header); } /* Show/hide icons. */ @@ -1676,31 +1718,28 @@ static void node_draw_basis(const bContext *C, UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color); } - /* Open/close entirely. */ + /* Collapse/expand icon. */ { - int but_size = U.widget_unit * 0.8f; - /* XXX button uses a custom triangle draw below, so make it invisible without icon. */ + const int but_size = U.widget_unit * 0.8f; UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefBut(node->block, - UI_BTYPE_BUT_TOGGLE, - 0, - "", - rct->xmin + 0.35f * U.widget_unit, - rct->ymax - NODE_DY / 2.2f - but_size / 2, - but_size, - but_size, - nullptr, - 0, - 0, - 0, - 0, - ""); + + uiBut *but = uiDefIconBut(node->block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_DOWNARROW_HLT, + rct->xmin + (NODE_MARGIN_X / 3), + rct->ymax - NODE_DY / 2.2f - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); UI_block_emboss_set(node->block, UI_EMBOSS); - - UI_GetThemeColor4fv(TH_TEXT, color); - /* Custom draw function for this button. */ - UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, rct->ymax - NODE_DY / 2.2f, 'v', color); } char showname[128]; @@ -1710,7 +1749,7 @@ static void node_draw_basis(const bContext *C, UI_BTYPE_LABEL, 0, showname, - (int)(rct->xmin + NODE_MARGIN_X), + (int)(rct->xmin + NODE_MARGIN_X + 0.4f), (int)(rct->ymax - NODE_DY), (short)(iconofs - rct->xmin - (18.0f * U.dpi_fac)), (short)NODE_DY, @@ -1724,49 +1763,96 @@ static void node_draw_basis(const bContext *C, UI_but_flag_enable(but, UI_BUT_INACTIVE); } - /* Body. */ - if (nodeTypeUndefined(node)) { - /* Use warning color to indicate undefined types. */ - UI_GetThemeColor4fv(TH_REDALERT, color); - } - else if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor4fv(TH_NODE, color); - } - else if (node->flag & NODE_CUSTOM_COLOR) { - rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); - } - else { - UI_GetThemeColor4fv(TH_NODE, color); - } - + /* Wire across the node when muted/disabled. */ if (node->flag & NODE_MUTED) { - color[3] = 0.5f; + node_draw_mute_line(C, v2d, snode, node); } + /* Body. */ + const float outline_width = 1.0f; { - UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); + /* Use warning color to indicate undefined types. */ + if (nodeTypeUndefined(node)) { + UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); + } + /* Muted nodes get a mix of the background with the node color. */ + else if (node->flag & NODE_MUTED) { + UI_GetThemeColorBlend4f(TH_BACK, TH_NODE, 0.2f, color); + } + else if (node->flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + } + else { + UI_GetThemeColor4fv(TH_NODE, color); + } + + /* Draw selected nodes fully opaque. */ + if (node->flag & SELECT) { + color[3] = 1.0f; + } + + /* Draw muted nodes slightly transparent so the wires inside are visible. */ + if (node->flag & NODE_MUTED) { + color[3] -= 0.2f; + } + const rctf rect = { rct->xmin, rct->xmax, rct->ymin, + rct->ymax - (NODE_DY + outline_width), + }; + + UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT); + UI_draw_roundbox_4fv(&rect, true, BASIS_RAD, color); + } + + /* Header underline. */ + { + float color_underline[4]; + + if (node->flag & NODE_MUTED) { + UI_GetThemeColor4fv(TH_WIRE, color_underline); + } + else { + UI_GetThemeColorBlend4f(TH_BACK, color_id, 0.2f, color_underline); + } + + const rctf rect = { + rct->xmin, + rct->xmax, + rct->ymax - (NODE_DY + outline_width), rct->ymax - NODE_DY, }; - UI_draw_roundbox_aa(&rect, true, BASIS_RAD, color); + + UI_draw_roundbox_corner_set(UI_CNR_NONE); + UI_draw_roundbox_4fv(&rect, true, 0.0f, color_underline); } - /* Outline active and selected emphasis. */ - if (node->flag & SELECT) { - UI_GetThemeColorShadeAlpha4fv( - (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); + /* Outline. */ + { + const rctf rect = { + rct->xmin - outline_width, + rct->xmax + outline_width, + rct->ymin - outline_width, + rct->ymax + outline_width, + }; + + /* Color the outline according to active, selected, or undefined status. */ + float color_outline[4]; + + if (node->flag & SELECT) { + UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + } + else if (nodeTypeUndefined(node)) { + UI_GetThemeColor4fv(TH_REDALERT, color_outline); + } + else { + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); + } UI_draw_roundbox_corner_set(UI_CNR_ALL); - UI_draw_roundbox_aa(rct, false, BASIS_RAD, color); - } - - /* Disable lines. */ - if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); + UI_draw_roundbox_4fv(&rect, false, BASIS_RAD, color_outline); } node_draw_sockets(v2d, C, ntree, node, true, false); @@ -1801,46 +1887,45 @@ static void node_draw_hidden(const bContext *C, float scale; UI_view2d_scale_get(v2d, &scale, nullptr); + const int color_id = node_get_colorid(node); + /* Shadow. */ node_draw_shadow(snode, node, hiddenrad, 1.0f); + /* Wire across the node when muted/disabled. */ + if (node->flag & NODE_MUTED) { + node_draw_mute_line(C, v2d, snode, node); + } + /* Body. */ float color[4]; - int color_id = node_get_colorid(node); - if (node->flag & NODE_MUTED) { - /* Muted nodes are semi-transparent and colorless. */ - UI_GetThemeColor4fv(TH_NODE, color); - color[3] = 0.25f; - } - else { - UI_GetThemeColor4fv(color_id, color); - } + { + if (nodeTypeUndefined(node)) { + /* Use warning color to indicate undefined types. */ + UI_GetThemeColorBlend4f(TH_REDALERT, TH_NODE, 0.4f, color); + } + else if (node->flag & NODE_MUTED) { + /* Muted nodes get a mix of the background with the node color. */ + UI_GetThemeColorBlendShade4fv(TH_BACK, color_id, 0.1f, 0, color); + } + else if (node->flag & NODE_CUSTOM_COLOR) { + rgba_float_args_set(color, node->color[0], node->color[1], node->color[2], 1.0f); + } + else { + UI_GetThemeColorBlend4f(TH_NODE, color_id, 0.4f, color); + } - UI_draw_roundbox_aa(rct, true, hiddenrad, color); + /* Draw selected nodes fully opaque. */ + if (node->flag & SELECT) { + color[3] = 1.0f; + } - /* Outline active and selected emphasis. */ - if (node->flag & SELECT) { - UI_GetThemeColorShadeAlpha4fv( - (node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, 0, -40, color); + /* Draw muted nodes slightly transparent so the wires inside are visible. */ + if (node->flag & NODE_MUTED) { + color[3] -= 0.2f; + } - UI_draw_roundbox_aa(rct, false, hiddenrad, color); - } - - /* Custom color inline. */ - if (node->flag & NODE_CUSTOM_COLOR) { - GPU_blend(GPU_BLEND_ALPHA); - GPU_line_smooth(true); - - const rctf rect = { - rct->xmin + 1, - rct->xmax - 1, - rct->ymin + 1, - rct->ymax - 1, - }; - UI_draw_roundbox_3fv_alpha(&rect, false, hiddenrad, node->color, 1.0f); - - GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); + UI_draw_roundbox_4fv(rct, true, hiddenrad, color); } /* Title. */ @@ -1851,36 +1936,28 @@ static void node_draw_hidden(const bContext *C, UI_GetThemeColorBlendShade4fv(TH_SELECT, color_id, 0.4f, 10, color); } - /* Open / collapse icon. */ + /* Collapse/expand icon. */ { - int but_size = U.widget_unit * 0.8f; - /* XXX button uses a custom triangle draw below, so make it invisible without icon */ + const int but_size = U.widget_unit * 1.0f; UI_block_emboss_set(node->block, UI_EMBOSS_NONE); - uiBut *but = uiDefBut(node->block, - UI_BTYPE_BUT_TOGGLE, - 0, - "", - rct->xmin + 0.35f * U.widget_unit, - centy - but_size / 2, - but_size, - but_size, - nullptr, - 0, - 0, - 0, - 0, - ""); + + uiBut *but = uiDefIconBut(node->block, + UI_BTYPE_BUT_TOGGLE, + 0, + ICON_RIGHTARROW, + rct->xmin + (NODE_MARGIN_X / 3), + centy - but_size / 2, + but_size, + but_size, + nullptr, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle"); UI_block_emboss_set(node->block, UI_EMBOSS); - - UI_GetThemeColor4fv(TH_TEXT, color); - /* Custom draw function for this button. */ - UI_draw_icon_tri(rct->xmin + 0.65f * U.widget_unit, centy, 'h', color); - } - - /* Disable lines. */ - if (node->flag & NODE_MUTED) { - node_draw_mute_line(v2d, snode, node); } char showname[128]; @@ -1900,15 +1977,44 @@ static void node_draw_hidden(const bContext *C, 0, 0, ""); + + /* Outline. */ + { + const float outline_width = 1.0f; + const rctf rect = { + rct->xmin - outline_width, + rct->xmax + outline_width, + rct->ymin - outline_width, + rct->ymax + outline_width, + }; + + /* Color the outline according to active, selected, or undefined status. */ + float color_outline[4]; + + if (node->flag & SELECT) { + UI_GetThemeColor4fv((node->flag & NODE_ACTIVE) ? TH_ACTIVE : TH_SELECT, color_outline); + } + else if (nodeTypeUndefined(node)) { + UI_GetThemeColor4fv(TH_REDALERT, color_outline); + } + else { + UI_GetThemeColorBlendShade4fv(TH_BACK, TH_NODE, 0.4f, -20, color_outline); + } + + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&rect, false, hiddenrad, color_outline); + } + if (node->flag & NODE_MUTED) { UI_but_flag_enable(but, UI_BUT_INACTIVE); } /* Scale widget thing. */ uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + GPU_blend(GPU_BLEND_ALPHA); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColorShade(color_id, -10); + immUniformThemeColorShadeAlpha(TH_TEXT, -40, -180); float dx = 10.0f; immBegin(GPU_PRIM_LINES, 4); @@ -1919,7 +2025,7 @@ static void node_draw_hidden(const bContext *C, immVertex2f(pos, rct->xmax - dx - 3.0f * snode->runtime->aspect, centy + 4.0f); immEnd(); - immUniformThemeColorShade(color_id, 30); + immUniformThemeColorShadeAlpha(TH_TEXT, 0, -180); dx -= snode->runtime->aspect; immBegin(GPU_PRIM_LINES, 4); @@ -1931,6 +2037,7 @@ static void node_draw_hidden(const bContext *C, immEnd(); immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); node_draw_sockets(v2d, C, ntree, node, true, false); @@ -2098,7 +2205,7 @@ void node_draw_nodetree(const bContext *C, LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { if (!nodeLinkIsHidden(link)) { - node_draw_link(®ion->v2d, snode, link); + node_draw_link(C, ®ion->v2d, snode, link); } } nodelink_batch_end(snode); @@ -2115,15 +2222,34 @@ void node_draw_nodetree(const bContext *C, } } -/* Draw tree path info in lower left corner. */ -static void draw_tree_path(SpaceNode *snode) +/* Draw the breadcrumb on the bottom of the editor. */ +static void draw_tree_path(const bContext &C, ARegion ®ion) { - char info[256]; + using namespace blender; - ED_node_tree_path_get_fixedbuf(snode, info, sizeof(info)); + GPU_matrix_push_projection(); + wmOrtho2_region_pixelspace(®ion); - UI_FontThemeColor(BLF_default(), TH_TEXT_HI); - BLF_draw_default(1.5f * UI_UNIT_X, 1.5f * UI_UNIT_Y, 0.0f, info, sizeof(info)); + const rcti *rect = ED_region_visible_rect(®ion); + + const uiStyle *style = UI_style_get_dpi(); + const float padding_x = 16 * UI_DPI_FAC; + const int x = rect->xmin + padding_x; + const int y = region.winy - UI_UNIT_Y * 0.6f; + const int width = BLI_rcti_size_x(rect) - 2 * padding_x; + + uiBlock *block = UI_block_begin(&C, ®ion, __func__, UI_EMBOSS_NONE); + uiLayout *layout = UI_block_layout( + block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y, width, 1, 0, style); + + Vector context_path = ed::space_node::context_path_for_space_node(C); + ui::template_breadcrumbs(*layout, context_path); + + UI_block_layout_resolve(block, nullptr, nullptr); + UI_block_end(&C, block); + UI_block_draw(&C, block); + + GPU_matrix_pop_projection(); } static void snode_setup_v2d(SpaceNode *snode, ARegion *region, const float center[2]) @@ -2152,26 +2278,25 @@ static void draw_nodetree(const bContext *C, node_draw_nodetree(C, region, snode, ntree, parent_key); } -/* Shade the parent node group and add a `uiBlock` to clip mouse events. */ -static void draw_group_overlay(const bContext *C, ARegion *region) +/** + * Make the background slightly brighter to indicate that users are inside a node-group. + */ +static void draw_background_color(const SpaceNode *snode) { - const View2D *v2d = ®ion->v2d; - const rctf rect = v2d->cur; - float color[4]; + const int max_tree_length = 3; + const float bright_factor = 0.25f; - /* Shade node groups to separate them visually. */ - GPU_blend(GPU_BLEND_ALPHA); + /* We ignore the first element of the path since it is the top-most tree and it doesn't need to + * be brighter. We also set a cap to how many levels we want to set apart, to avoid the + * background from getting too bright. */ + const int clamped_tree_path_length = BLI_listbase_count_at_most(&snode->treepath, + max_tree_length); + const int depth = max_ii(0, clamped_tree_path_length - 1); - UI_GetThemeColorShadeAlpha4fv(TH_NODE_GROUP, 0, 0, color); - UI_draw_roundbox_corner_set(UI_CNR_NONE); - UI_draw_roundbox_4fv(&rect, true, 0, color); - GPU_blend(GPU_BLEND_NONE); - - /* Set the block bounds to clip mouse events from underlying nodes. */ - uiBlock *block = UI_block_begin(C, region, "node tree bounds block", UI_EMBOSS); - UI_block_bounds_set_explicit(block, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS); - UI_block_end(C, block); + float color[3]; + UI_GetThemeColor3fv(TH_BACK, color); + mul_v3_fl(color, 1.0f + bright_factor * depth); + GPU_clear_color(color[0], color[1], color[2], 1.0); } void node_draw_space(const bContext *C, ARegion *region) @@ -2187,22 +2312,20 @@ void node_draw_space(const bContext *C, ARegion *region) GPU_framebuffer_bind_no_srgb(framebuffer_overlay); UI_view2d_view_ortho(v2d); - UI_ThemeClearColor(TH_BACK); + draw_background_color(snode); GPU_depth_test(GPU_DEPTH_NONE); GPU_scissor_test(true); /* XXX `snode->runtime->cursor` set in coordinate-space for placing new nodes, * used for drawing noodles too. */ UI_view2d_region_to_view(®ion->v2d, - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, &snode->runtime->cursor[0], &snode->runtime->cursor[1]); snode->runtime->cursor[0] /= UI_DPI_FAC; snode->runtime->cursor[1] /= UI_DPI_FAC; - int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); - ED_region_draw_cb_draw(C, region, REGION_DRAW_PRE_VIEW); /* Only set once. */ @@ -2211,10 +2334,11 @@ void node_draw_space(const bContext *C, ARegion *region) /* Nodes. */ snode_set_context(C); + const int grid_levels = UI_GetThemeValueType(TH_NODE_GRID_LEVELS, SPACE_NODE); + UI_view2d_dot_grid_draw(v2d, TH_GRID, NODE_GRID_STEP_SIZE, grid_levels); + /* Draw parent node trees. */ if (snode->treepath.last) { - static const int max_depth = 2; - bNodeTreePath *path = (bNodeTreePath *)snode->treepath.last; /* Update tree path name (drawn in the bottom left). */ @@ -2235,36 +2359,11 @@ void node_draw_space(const bContext *C, ARegion *region) copy_v2_v2(snode->edittree->view_center, center); } - int depth = 0; - while (path->prev && depth < max_depth) { - path = path->prev; - depth++; - } - - /* Parent node trees in the background. */ - for (int curdepth = depth; curdepth > 0; path = path->next, curdepth--) { - bNodeTree *ntree = path->nodetree; - if (ntree) { - snode_setup_v2d(snode, region, path->view_center); - - draw_nodetree(C, region, ntree, path->parent_key); - - draw_group_overlay(C, region); - } - } - /* Top-level edit tree. */ bNodeTree *ntree = path->nodetree; if (ntree) { snode_setup_v2d(snode, region, center); - /* Grid, uses theme color based on node path depth. */ - UI_view2d_multi_grid_draw(v2d, - (depth > 0 ? TH_NODE_GROUP : TH_GRID), - ED_node_grid_size(), - NODE_GRID_STEPS, - grid_levels); - /* Backdrop. */ draw_nodespace_back_pix(C, region, snode, path->parent_key); @@ -2291,20 +2390,18 @@ void node_draw_space(const bContext *C, ARegion *region) GPU_line_smooth(true); LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) { LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) { - node_draw_link(v2d, snode, (bNodeLink *)linkdata->data); + node_draw_link(C, v2d, snode, (bNodeLink *)linkdata->data); } } GPU_line_smooth(false); GPU_blend(GPU_BLEND_NONE); - if (snode->flag & SNODE_SHOW_GPENCIL) { + if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode->flag & SNODE_SHOW_GPENCIL) { /* Draw grease-pencil annotations. */ ED_annotation_draw_view2d(C, true); } } else { - /* Default grid. */ - UI_view2d_multi_grid_draw(v2d, TH_GRID, ED_node_grid_size(), NODE_GRID_STEPS, grid_levels); /* Backdrop. */ draw_nodespace_back_pix(C, region, snode, NODE_INSTANCE_KEY_NONE); @@ -2316,14 +2413,16 @@ void node_draw_space(const bContext *C, ARegion *region) UI_view2d_view_restore(C); if (snode->treepath.last) { - if (snode->flag & SNODE_SHOW_GPENCIL) { + if (snode->overlay.flag & SN_OVERLAY_SHOW_OVERLAYS && snode->flag & SNODE_SHOW_GPENCIL) { /* Draw grease-pencil (screen strokes, and also paint-buffer). */ ED_annotation_draw_view2d(C, false); } } - /* Tree path info. */ - draw_tree_path(snode); + /* Draw context path. */ + if (snode->edittree) { + draw_tree_path(*C, *region); + } /* Scrollers. */ UI_view2d_scrollers_draw(v2d, nullptr); diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 411719cf6c0..d0ccbb03107 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -38,6 +38,7 @@ #include "BLT_translation.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_resources.h" #include "NOD_geometry_nodes_eval_log.hh" @@ -60,40 +61,6 @@ struct AttributeSearchData { /* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ BLI_STATIC_ASSERT(std::is_trivially_destructible_v, ""); -static StringRef attribute_data_type_string(const CustomDataType type) -{ - const char *name = nullptr; - RNA_enum_name_from_value(rna_enum_attribute_type_items, type, &name); - return StringRef(IFACE_(name)); -} - -static StringRef attribute_domain_string(const AttributeDomain domain) -{ - const char *name = nullptr; - RNA_enum_name_from_value(rna_enum_attribute_domain_items, domain, &name); - return StringRef(IFACE_(name)); -} - -/* Unicode arrow. */ -#define MENU_SEP "\xe2\x96\xb6" - -static bool attribute_search_item_add(uiSearchItems *items, const GeometryAttributeInfo &item) -{ - const StringRef data_type_name = attribute_data_type_string(item.data_type); - const StringRef domain_name = attribute_domain_string(item.domain); - std::string search_item_text = domain_name + " " + MENU_SEP + item.name + UI_SEP_CHAR + - data_type_name; - - return UI_search_item_add( - items, search_item_text.c_str(), (void *)&item, ICON_NONE, UI_BUT_HAS_SEP_CHAR, 0); -} - -static GeometryAttributeInfo &get_dummy_item_info() -{ - static GeometryAttributeInfo info; - return info; -} - static void attribute_search_update_fn( const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first) { @@ -107,51 +74,7 @@ static void attribute_search_update_fn( } blender::Vector infos = node_log->lookup_available_attributes(); - GeometryAttributeInfo &dummy_info = get_dummy_item_info(); - - /* Any string may be valid, so add the current search string along with the hints. */ - if (str[0] != '\0') { - bool contained = false; - for (const GeometryAttributeInfo *attribute_info : infos) { - if (attribute_info->name == str) { - contained = true; - break; - } - } - if (!contained) { - dummy_info.name = str; - UI_search_item_add(items, str, &dummy_info, ICON_ADD, 0, 0); - } - } - - if (str[0] == '\0' && !is_first) { - /* Allow clearing the text field when the string is empty, but not on the first pass, - * or opening an attribute field for the first time would show this search item. */ - dummy_info.name = str; - UI_search_item_add(items, str, &dummy_info, ICON_X, 0, 0); - } - - /* Don't filter when the menu is first opened, but still run the search - * so the items are in the same order they will appear in while searching. */ - const char *string = is_first ? "" : str; - - StringSearch *search = BLI_string_search_new(); - for (const GeometryAttributeInfo *item : infos) { - BLI_string_search_add(search, item->name.c_str(), (void *)item); - } - - GeometryAttributeInfo **filtered_items; - const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); - - for (const int i : IndexRange(filtered_amount)) { - const GeometryAttributeInfo *item = filtered_items[i]; - if (!attribute_search_item_add(items, *item)) { - break; - } - } - - MEM_freeN(filtered_items); - BLI_string_search_free(search); + blender::ui::attribute_search_add_items(str, true, infos, items, is_first); } static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) @@ -198,7 +121,7 @@ void node_geometry_add_attribute_search_button(const bContext *UNUSED(C), AttributeSearchData, {node_tree, node, (bNodeSocket *)socket_ptr->data}); UI_but_func_search_set_results_are_suggestions(but, true); - UI_but_func_search_set_sep_string(but, MENU_SEP); + UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); UI_but_func_search_set(but, nullptr, attribute_search_update_fn, diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index d35fd729131..383fe5afdf9 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -121,7 +121,7 @@ void node_draw_sockets(const struct View2D *v2d, void node_update_default(const struct bContext *C, struct bNodeTree *ntree, struct bNode *node); int node_select_area_default(struct bNode *node, int x, int y); int node_tweak_area_default(struct bNode *node, int x, int y); -void node_socket_color_get(struct bContext *C, +void node_socket_color_get(const struct bContext *C, struct bNodeTree *ntree, struct PointerRNA *node_ptr, struct bNodeSocket *sock, @@ -175,6 +175,7 @@ int space_node_view_flag(struct bContext *C, void NODE_OT_view_all(struct wmOperatorType *ot); void NODE_OT_view_selected(struct wmOperatorType *ot); +void NODE_OT_geometry_node_view_legacy(struct wmOperatorType *ot); void NODE_OT_backimage_move(struct wmOperatorType *ot); void NODE_OT_backimage_zoom(struct wmOperatorType *ot); @@ -185,8 +186,12 @@ void NODE_OT_backimage_sample(struct wmOperatorType *ot); void nodelink_batch_start(struct SpaceNode *snode); void nodelink_batch_end(struct SpaceNode *snode); -void node_draw_link(struct View2D *v2d, struct SpaceNode *snode, struct bNodeLink *link); -void node_draw_link_bezier(const struct View2D *v2d, +void node_draw_link(const struct bContext *C, + struct View2D *v2d, + struct SpaceNode *snode, + struct bNodeLink *link); +void node_draw_link_bezier(const struct bContext *C, + const struct View2D *v2d, const struct SpaceNode *snode, const struct bNodeLink *link, int th_col1, @@ -331,7 +336,7 @@ extern const char *node_context_dir[]; #define NODE_SOCKDY (0.1f * U.widget_unit) #define NODE_WIDTH(node) (node->width * UI_DPI_FAC) #define NODE_HEIGHT(node) (node->height * UI_DPI_FAC) -#define NODE_MARGIN_X (1.10f * U.widget_unit) +#define NODE_MARGIN_X (1.2f * U.widget_unit) #define NODE_SOCKSIZE (0.25f * U.widget_unit) #define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit) #define NODE_RESIZE_MARGIN (0.20f * U.widget_unit) @@ -340,3 +345,11 @@ extern const char *node_context_dir[]; #ifdef __cplusplus } #endif + +#ifdef __cplusplus +# include "BLI_vector.hh" +# include "UI_interface.hh" +namespace blender::ed::space_node { +Vector context_path_for_space_node(const bContext &C); +} +#endif diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index 610c2889e7a..0c54da65e9c 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -51,6 +51,7 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_view_all); WM_operatortype_append(NODE_OT_view_selected); + WM_operatortype_append(NODE_OT_geometry_node_view_legacy); WM_operatortype_append(NODE_OT_mute_toggle); WM_operatortype_append(NODE_OT_hide_toggle); @@ -155,6 +156,7 @@ void ED_operatormacros_node(void) OPTYPE_UNDO | OPTYPE_REGISTER); mot = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate"); RNA_boolean_set(mot->ptr, "remove_on_cancel", true); + RNA_boolean_set(mot->ptr, "view2d_edge_pan", true); WM_operatortype_macro_define(ot, "NODE_OT_attach"); WM_operatortype_macro_define(ot, "NODE_OT_insert_offset"); diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc index 7d95659e403..76aad684b4c 100644 --- a/source/blender/editors/space_node/node_relationships.cc +++ b/source/blender/editors/space_node/node_relationships.cc @@ -57,8 +57,12 @@ #include "BLT_translation.h" +#include "NOD_node_tree_ref.hh" + #include "node_intern.h" /* own include */ +using namespace blender::nodes::node_tree_ref_types; + /* -------------------------------------------------------------------- */ /** \name Relations Helpers * \{ */ @@ -220,6 +224,7 @@ static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bN if (node_connected_to_output(bmain, snode->edittree, node)) { oplink->flag |= NODE_LINK_TEST; } + oplink->flag |= NODE_LINK_DRAGGED; return linkdata; } @@ -611,160 +616,282 @@ static void snode_autoconnect(Main *bmain, /** \name Link Viewer Operator * \{ */ -static int node_link_viewer(const bContext *C, bNode *tonode) +namespace blender::ed::nodes::viewer_linking { + +/* Depending on the node tree type, different socket types are supported by viewer nodes. */ +static bool socket_can_be_viewed(const OutputSocketRef &socket) +{ + if (nodeSocketIsHidden(socket.bsocket())) { + return false; + } + if (socket.idname() == "NodeSocketVirtual") { + return false; + } + if (socket.tree().btree()->type != NTREE_GEOMETRY) { + return true; + } + return ELEM(socket.typeinfo()->type, + SOCK_GEOMETRY, + SOCK_FLOAT, + SOCK_VECTOR, + SOCK_INT, + SOCK_BOOLEAN, + SOCK_RGBA); +} + +static CustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type) +{ + switch (socket_type) { + case SOCK_FLOAT: + return CD_PROP_FLOAT; + case SOCK_INT: + return CD_PROP_INT32; + case SOCK_VECTOR: + return CD_PROP_FLOAT3; + case SOCK_BOOLEAN: + return CD_PROP_BOOL; + case SOCK_RGBA: + return CD_PROP_COLOR; + default: + /* Fallback. */ + return CD_AUTO_FROM_NAME; + } +} + +/** + * Find the socket to link to in a viewer node. + */ +static bNodeSocket *node_link_viewer_get_socket(bNodeTree *ntree, + bNode *viewer_node, + bNodeSocket *src_socket) +{ + if (viewer_node->type != GEO_NODE_VIEWER) { + /* In viewer nodes in the compositor, only the first input should be linked to. */ + return (bNodeSocket *)viewer_node->inputs.first; + } + /* For the geometry nodes viewer, find the socket with the correct type. */ + LISTBASE_FOREACH (bNodeSocket *, viewer_socket, &viewer_node->inputs) { + if (viewer_socket->type == src_socket->type) { + if (viewer_socket->type == SOCK_GEOMETRY) { + return viewer_socket; + } + NodeGeometryViewer *storage = (NodeGeometryViewer *)viewer_node->storage; + const CustomDataType data_type = socket_type_to_custom_data_type( + (eNodeSocketDatatype)src_socket->type); + BLI_assert(data_type != CD_AUTO_FROM_NAME); + storage->data_type = data_type; + nodeUpdate(ntree, viewer_node); + return viewer_socket; + } + } + return nullptr; +} + +static bool is_viewer_node(const NodeRef &node) +{ + return ELEM(node.bnode()->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER); +} + +static Vector find_viewer_nodes(const NodeTreeRef &tree) +{ + Vector viewer_nodes; + for (const NodeRef *node : tree.nodes()) { + if (is_viewer_node(*node)) { + viewer_nodes.append(node); + } + } + return viewer_nodes; +} + +static bool is_viewer_socket_in_viewer(const InputSocketRef &socket) +{ + const NodeRef &node = socket.node(); + BLI_assert(is_viewer_node(node)); + if (node.typeinfo()->type == GEO_NODE_VIEWER) { + return true; + } + return socket.index() == 0; +} + +static bool is_linked_to_viewer(const OutputSocketRef &socket, const NodeRef &viewer_node) +{ + for (const InputSocketRef *target_socket : socket.directly_linked_sockets()) { + if (&target_socket->node() != &viewer_node) { + continue; + } + if (!target_socket->is_available()) { + continue; + } + if (is_viewer_socket_in_viewer(*target_socket)) { + return true; + } + } + return false; +} + +static int get_default_viewer_type(const bContext *C) { SpaceNode *snode = CTX_wm_space_node(C); + return ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; +} - /* context check */ - if (tonode == nullptr || BLI_listbase_is_empty(&tonode->outputs)) { - return OPERATOR_CANCELLED; +static void remove_links_to_unavailable_viewer_sockets(bNodeTree &btree, bNode &viewer_node) +{ + LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &btree.links) { + if (link->tonode == &viewer_node) { + if (link->tosock->flag & SOCK_UNAVAIL) { + nodeRemLink(&btree, link); + } + } } - if (ELEM(tonode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { +} + +static const NodeRef *get_existing_viewer(const NodeTreeRef &tree) +{ + Vector viewer_nodes = find_viewer_nodes(tree); + + /* Check if there is already an active viewer node that should be used. */ + for (const NodeRef *viewer_node : viewer_nodes) { + if (viewer_node->bnode()->flag & NODE_DO_OUTPUT) { + return viewer_node; + } + } + + /* If no active but non-active viewers exist, make one active. */ + if (!viewer_nodes.is_empty()) { + viewer_nodes[0]->bnode()->flag |= NODE_DO_OUTPUT; + return viewer_nodes[0]; + } + return nullptr; +} + +static const OutputSocketRef *find_output_socket_to_be_viewed(const NodeRef *active_viewer_node, + const NodeRef &node_to_view) +{ + /* Check if any of the output sockets is selected, which is the case when the user just clicked + * on the socket. */ + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (output_socket->bsocket()->flag & SELECT) { + return output_socket; + } + } + + const OutputSocketRef *last_socket_linked_to_viewer = nullptr; + if (active_viewer_node != nullptr) { + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (!socket_can_be_viewed(*output_socket)) { + continue; + } + if (is_linked_to_viewer(*output_socket, *active_viewer_node)) { + last_socket_linked_to_viewer = output_socket; + } + } + } + if (last_socket_linked_to_viewer == nullptr) { + /* If no output is connected to a viewer, use the first output that can be viewed. */ + for (const OutputSocketRef *output_socket : node_to_view.outputs()) { + if (socket_can_be_viewed(*output_socket)) { + return output_socket; + } + } + } + else { + /* Pick the next socket to be linked to the viewer. */ + const int tot_outputs = node_to_view.outputs().size(); + for (const int offset : IndexRange(1, tot_outputs - 1)) { + const int index = (last_socket_linked_to_viewer->index() + offset) % tot_outputs; + const OutputSocketRef &output_socket = node_to_view.output(index); + if (!socket_can_be_viewed(output_socket)) { + continue; + } + if (is_linked_to_viewer(output_socket, *active_viewer_node)) { + continue; + } + return &output_socket; + } + } + return nullptr; +} + +static int link_socket_to_viewer(const bContext *C, + bNode *viewer_bnode, + bNode *bnode_to_view, + bNodeSocket *bsocket_to_view) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->edittree; + + if (viewer_bnode == nullptr) { + /* Create a new viewer node if none exists. */ + const int viewer_type = get_default_viewer_type(C); + viewer_bnode = node_add_node( + C, nullptr, viewer_type, bsocket_to_view->locx + 100, bsocket_to_view->locy); + if (viewer_bnode == nullptr) { + return OPERATOR_CANCELLED; + } + } + + bNodeSocket *viewer_bsocket = node_link_viewer_get_socket(btree, viewer_bnode, bsocket_to_view); + if (viewer_bsocket == nullptr) { return OPERATOR_CANCELLED; } - /* get viewer */ - bNode *viewer_node = nullptr; - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { - if (node->flag & NODE_DO_OUTPUT) { - viewer_node = node; - break; - } - } - } - /* no viewer, we make one active */ - if (viewer_node == nullptr) { - LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) { - if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER, GEO_NODE_VIEWER)) { - node->flag |= NODE_DO_OUTPUT; - viewer_node = node; - break; - } + bNodeLink *link_to_change = nullptr; + LISTBASE_FOREACH (bNodeLink *, link, &btree->links) { + if (link->tosock == viewer_bsocket) { + link_to_change = link; + break; } } - bNodeSocket *sock = nullptr; - bNodeLink *link = nullptr; - - /* try to find an already connected socket to cycle to the next */ - if (viewer_node) { - link = nullptr; - - for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->fromnode == tonode) { - if (link->tosock == viewer_node->inputs.first) { - break; - } - } - } - if (link) { - /* unlink existing connection */ - sock = link->fromsock; - nodeRemLink(snode->edittree, link); - - /* find a socket after the previously connected socket */ - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - for (sock = sock->next; sock; sock = sock->next) { - if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { - break; - } - } - } - else { - for (sock = sock->next; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } - } - } - } + if (link_to_change == nullptr) { + nodeAddLink(btree, bnode_to_view, bsocket_to_view, viewer_bnode, viewer_bsocket); + } + else { + link_to_change->fromnode = bnode_to_view; + link_to_change->fromsock = bsocket_to_view; + btree->update |= NTREE_UPDATE_LINKS; } - if (tonode) { - /* Find a selected socket that overrides the socket to connect to */ - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (sock2->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; - } - } - } - else { - LISTBASE_FOREACH (bNodeSocket *, sock2, &tonode->outputs) { - if (!nodeSocketIsHidden(sock2) && sock2->flag & SELECT) { - sock = sock2; - break; - } - } - } + remove_links_to_unavailable_viewer_sockets(*btree, *viewer_bnode); + + if (btree->type == NTREE_GEOMETRY) { + ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_bnode); } - /* find a socket starting from the first socket */ - if (!sock) { - if (ED_node_is_geometry(snode)) { - /* Geometry nodes viewer only supports geometry sockets for now. */ - for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { - if (sock->type == SOCK_GEOMETRY && !nodeSocketIsHidden(sock)) { - break; - } - } - } - else { - for (sock = (bNodeSocket *)tonode->outputs.first; sock; sock = sock->next) { - if (!nodeSocketIsHidden(sock)) { - break; - } - } - } - } - - if (sock) { - /* add a new viewer if none exists yet */ - if (!viewer_node) { - /* XXX location is a quick hack, just place it next to the linked socket */ - const int viewer_type = ED_node_is_compositor(snode) ? CMP_NODE_VIEWER : GEO_NODE_VIEWER; - viewer_node = node_add_node(C, nullptr, viewer_type, sock->locx + 100, sock->locy); - if (!viewer_node) { - return OPERATOR_CANCELLED; - } - - link = nullptr; - } - else { - /* get link to viewer */ - for (link = (bNodeLink *)snode->edittree->links.first; link; link = link->next) { - if (link->tonode == viewer_node && link->tosock == viewer_node->inputs.first) { - break; - } - } - } - - if (link == nullptr) { - nodeAddLink( - snode->edittree, tonode, sock, viewer_node, (bNodeSocket *)viewer_node->inputs.first); - } - else { - link->fromnode = tonode; - link->fromsock = sock; - /* make sure the dependency sorting is updated */ - snode->edittree->update |= NTREE_UPDATE_LINKS; - } - if (ED_node_is_geometry(snode)) { - ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(C), snode, viewer_node); - } - - ntreeUpdateTree(CTX_data_main(C), snode->edittree); - snode_update(snode, viewer_node); - DEG_id_tag_update(&snode->edittree->id, 0); - } + ntreeUpdateTree(CTX_data_main(C), btree); + snode_update(snode, viewer_bnode); + DEG_id_tag_update(&btree->id, 0); return OPERATOR_FINISHED; } +static int node_link_viewer(const bContext *C, bNode *bnode_to_view) +{ + if (bnode_to_view == nullptr) { + return OPERATOR_CANCELLED; + } + + SpaceNode *snode = CTX_wm_space_node(C); + bNodeTree *btree = snode->edittree; + + const NodeTreeRef tree{btree}; + const NodeRef &node_to_view = *tree.find_node(*bnode_to_view); + const NodeRef *active_viewer_node = get_existing_viewer(tree); + + const OutputSocketRef *socket_to_view = find_output_socket_to_be_viewed(active_viewer_node, + node_to_view); + if (socket_to_view == nullptr) { + return OPERATOR_FINISHED; + } + + bNodeSocket *bsocket_to_view = socket_to_view->bsocket(); + bNode *viewer_bnode = active_viewer_node ? active_viewer_node->bnode() : nullptr; + return link_socket_to_viewer(C, viewer_bnode, bnode_to_view, bsocket_to_view); +} + +} // namespace blender::ed::nodes::viewer_linking + static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceNode *snode = CTX_wm_space_node(C); @@ -776,7 +903,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op)) ED_preview_kill_jobs(CTX_wm_manager(C), CTX_data_main(C)); - if (node_link_viewer(C, node) == OPERATOR_CANCELLED) { + if (blender::ed::nodes::viewer_linking::node_link_viewer(C, node) == OPERATOR_CANCELLED) { return OPERATOR_CANCELLED; } @@ -894,6 +1021,8 @@ static void node_link_exit(bContext *C, wmOperator *op, bool apply_links) */ do_tag_update |= (link->flag & NODE_LINK_TEST) != 0; + link->flag &= ~NODE_LINK_DRAGGED; + if (apply_links && link->tosock && link->fromsock) { /* before actually adding the link, * let nodes perform special link insertion handling @@ -1097,6 +1226,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor *oplink = *link; oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; + oplink->flag |= NODE_LINK_DRAGGED; /* The link could be disconnected and in that case we * wouldn't be able to check whether tag update is @@ -1150,6 +1280,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor *oplink = *link_to_pick; oplink->next = oplink->prev = nullptr; oplink->flag |= NODE_LINK_VALID; + oplink->flag |= NODE_LINK_DRAGGED; oplink->flag &= ~NODE_LINK_TEST; if (node_connected_to_output(bmain, snode->edittree, link_to_pick->tonode)) { oplink->flag |= NODE_LINK_TEST; diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc index 648ede7abd5..f68d8589624 100644 --- a/source/blender/editors/space_node/node_templates.cc +++ b/source/blender/editors/space_node/node_templates.cc @@ -20,6 +20,7 @@ #include #include +#include #include "MEM_guardedalloc.h" @@ -39,7 +40,9 @@ #include "RNA_access.h" +#include "NOD_node_declaration.hh" #include "NOD_socket.h" +#include "NOD_socket_declarations.hh" #include "../interface/interface_intern.h" /* XXX bad level */ #include "UI_interface.h" @@ -49,17 +52,20 @@ #include "ED_undo.h" +using blender::Vector; +using blender::nodes::NodeDeclaration; + /************************* Node Socket Manipulation **************************/ /* describes an instance of a node type and a specific socket to link */ struct NodeLinkItem { - int socket_index; /* index for linking */ - int socket_type; /* socket type for compatibility check */ - const char *socket_name; /* ui label of the socket */ - const char *node_name; /* ui label of the node */ + int socket_index = -1; /* index for linking */ + int socket_type = SOCK_CUSTOM; /* socket type for compatibility check */ + const char *socket_name = nullptr; /* ui label of the socket */ + const char *node_name = nullptr; /* ui label of the node */ /* extra settings */ - bNodeTree *ngroup; /* group node tree */ + bNodeTree *ngroup = nullptr; /* group node tree */ }; /* Compare an existing node to a link item to see if it can be reused. @@ -319,15 +325,13 @@ struct NodeLinkArg { uiLayout *layout; }; -static void ui_node_link_items(NodeLinkArg *arg, - int in_out, - NodeLinkItem **r_items, - int *r_totitems) +static Vector ui_node_link_items(NodeLinkArg *arg, + int in_out, + std::optional &r_node_decl) { - /* XXX this should become a callback for node types! */ - NodeLinkItem *items = nullptr; - int totitems = 0; + Vector items; + /* XXX this should become a callback for node types! */ if (arg->node_type->type == NODE_GROUP) { bNodeTree *ngroup; int i; @@ -339,40 +343,66 @@ static void ui_node_link_items(NodeLinkArg *arg, !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { continue; } - - ListBase *lb = ((in_out == SOCK_IN) ? &ngroup->inputs : &ngroup->outputs); - totitems += BLI_listbase_count(lb); } - if (totitems > 0) { - items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); - - i = 0; - for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; - ngroup = (bNodeTree *)ngroup->id.next) { - const char *disabled_hint; - if ((ngroup->type != arg->ntree->type) || - !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { - continue; - } - - ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); - bNodeSocket *stemp; - int index; - for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; - stemp = stemp->next, index++, i++) { - NodeLinkItem *item = &items[i]; - - item->socket_index = index; - /* NOTE: int stemp->type is not fully reliable, not used for node group - * interface sockets. use the typeinfo->type instead. - */ - item->socket_type = stemp->typeinfo->type; - item->socket_name = stemp->name; - item->node_name = ngroup->id.name + 2; - item->ngroup = ngroup; - } + i = 0; + for (ngroup = (bNodeTree *)arg->bmain->nodetrees.first; ngroup; + ngroup = (bNodeTree *)ngroup->id.next) { + const char *disabled_hint; + if ((ngroup->type != arg->ntree->type) || + !nodeGroupPoll(arg->ntree, ngroup, &disabled_hint)) { + continue; } + + ListBase *lb = (in_out == SOCK_IN ? &ngroup->inputs : &ngroup->outputs); + bNodeSocket *stemp; + int index; + for (stemp = (bNodeSocket *)lb->first, index = 0; stemp; stemp = stemp->next, index++, i++) { + NodeLinkItem item; + item.socket_index = index; + /* NOTE: int stemp->type is not fully reliable, not used for node group + * interface sockets. use the typeinfo->type instead. + */ + item.socket_type = stemp->typeinfo->type; + item.socket_name = stemp->name; + item.node_name = ngroup->id.name + 2; + item.ngroup = ngroup; + + items.append(item); + } + } + } + else if (arg->node_type->declare != nullptr) { + using namespace blender; + using namespace blender::nodes; + + r_node_decl.emplace(NodeDeclaration()); + NodeDeclarationBuilder node_decl_builder{*r_node_decl}; + arg->node_type->declare(node_decl_builder); + Span socket_decls = (in_out == SOCK_IN) ? r_node_decl->inputs() : + r_node_decl->outputs(); + int index = 0; + for (const SocketDeclarationPtr &socket_decl_ptr : socket_decls) { + const SocketDeclaration &socket_decl = *socket_decl_ptr; + NodeLinkItem item; + item.socket_index = index++; + /* A socket declaration does not necessarily map to exactly one built-in socket type. So only + * check for the types that matter here. */ + if (dynamic_cast(&socket_decl)) { + item.socket_type = SOCK_RGBA; + } + else if (dynamic_cast(&socket_decl)) { + item.socket_type = SOCK_FLOAT; + } + else if (dynamic_cast(&socket_decl)) { + item.socket_type = SOCK_VECTOR; + } + else { + item.socket_type = SOCK_CUSTOM; + } + item.socket_name = socket_decl.name().c_str(); + item.node_name = arg->node_type->ui_name; + items.append(item); } } else { @@ -381,27 +411,18 @@ static void ui_node_link_items(NodeLinkArg *arg, bNodeSocketTemplate *stemp; int i; - for (stemp = socket_templates; stemp && stemp->type != -1; stemp++) { - totitems++; - } - - if (totitems > 0) { - items = (NodeLinkItem *)MEM_callocN(sizeof(NodeLinkItem) * totitems, "ui node link items"); - - i = 0; - for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) { - NodeLinkItem *item = &items[i]; - - item->socket_index = i; - item->socket_type = stemp->type; - item->socket_name = stemp->name; - item->node_name = arg->node_type->ui_name; - } + i = 0; + for (stemp = socket_templates; stemp && stemp->type != -1; stemp++, i++) { + NodeLinkItem item; + item.socket_index = i; + item.socket_type = stemp->type; + item.socket_name = stemp->name; + item.node_name = arg->node_type->ui_name; + items.append(item); } } - *r_items = items; - *r_totitems = totitems; + return items; } static void ui_node_link(bContext *C, void *arg_p, void *event_p) @@ -513,8 +534,6 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) /* generate UI */ for (int j = 0; j < sorted_ntypes.size(); j++) { bNodeType *ntype = sorted_ntypes[j]; - NodeLinkItem *items; - int totitems; char name[UI_MAX_NAME_STR]; const char *cur_node_name = nullptr; int num = 0; @@ -522,16 +541,17 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) arg->node_type = ntype; - ui_node_link_items(arg, SOCK_OUT, &items, &totitems); + std::optional node_decl; + Vector items = ui_node_link_items(arg, SOCK_OUT, node_decl); - for (int i = 0; i < totitems; i++) { - if (ui_compatible_sockets(items[i].socket_type, sock->type)) { + for (const NodeLinkItem &item : items) { + if (ui_compatible_sockets(item.socket_type, sock->type)) { num++; } } - for (int i = 0; i < totitems; i++) { - if (!ui_compatible_sockets(items[i].socket_type, sock->type)) { + for (const NodeLinkItem &item : items) { + if (!ui_compatible_sockets(item.socket_type, sock->type)) { continue; } @@ -546,8 +566,8 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) } if (num > 1) { - if (!cur_node_name || !STREQ(cur_node_name, items[i].node_name)) { - cur_node_name = items[i].node_name; + if (!cur_node_name || !STREQ(cur_node_name, item.node_name)) { + cur_node_name = item.node_name; /* XXX Do not use uiItemL here, * it would add an empty icon as we are in a menu! */ uiDefBut(block, @@ -566,11 +586,11 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) ""); } - BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(items[i].socket_name)); + BLI_snprintf(name, UI_MAX_NAME_STR, "%s", IFACE_(item.socket_name)); icon = ICON_BLANK1; } else { - BLI_strncpy(name, IFACE_(items[i].node_name), UI_MAX_NAME_STR); + BLI_strncpy(name, IFACE_(item.node_name), UI_MAX_NAME_STR); icon = ICON_NONE; } @@ -591,13 +611,9 @@ static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) TIP_("Add node to input")); argN = (NodeLinkArg *)MEM_dupallocN(arg); - argN->item = items[i]; + argN->item = item; UI_but_funcN_set(but, ui_node_link, argN, nullptr); } - - if (items) { - MEM_freeN(items); - } } } diff --git a/source/blender/editors/space_node/node_view.cc b/source/blender/editors/space_node/node_view.cc index f0db0539c4f..762b4b36a39 100644 --- a/source/blender/editors/space_node/node_view.cc +++ b/source/blender/editors/space_node/node_view.cc @@ -23,8 +23,10 @@ #include "DNA_node_types.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_rect.h" +#include "BLI_string_ref.hh" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -54,6 +56,8 @@ #include "node_intern.h" /* own include */ +using blender::StringRef; + /* -------------------------------------------------------------------- */ /** \name View All Operator * \{ */ @@ -700,3 +704,89 @@ void NODE_OT_backimage_sample(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View Geometry Nodes Legacy Operator + * + * This operator should be removed when the 2.93 legacy nodes are removed. + * \{ */ + +static int space_node_view_geometry_nodes_legacy(bContext *C, SpaceNode *snode, wmOperator *op) +{ + ARegion *region = CTX_wm_region(C); + + /* Only use the node editor's active node tree. Otherwise this will be too complicated. */ + bNodeTree *node_tree = snode->nodetree; + if (node_tree == nullptr || node_tree->type != NTREE_GEOMETRY) { + return OPERATOR_CANCELLED; + } + + bool found_legacy_node = false; + LISTBASE_FOREACH_BACKWARD (bNode *, node, &node_tree->nodes) { + StringRef idname{node->idname}; + if (idname.find("Legacy") == StringRef::not_found) { + node->flag &= ~NODE_SELECT; + } + else { + found_legacy_node = true; + node->flag |= NODE_SELECT; + } + } + + if (!found_legacy_node) { + WM_report(RPT_INFO, "Legacy node not found, may be in nested node group"); + } + + const int smooth_viewtx = WM_operator_smooth_viewtx_get(op); + if (space_node_view_flag(C, snode, region, NODE_SELECT, smooth_viewtx)) { + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static int geometry_node_view_legacy_exec(bContext *C, wmOperator *op) +{ + /* Allow running this operator directly in a specific node editor. */ + if (SpaceNode *snode = CTX_wm_space_node(C)) { + return space_node_view_geometry_nodes_legacy(C, snode, op); + } + + /* Since the operator is meant to be called from a button in the modifier panel, the node tree + * must be found from the screen, using the largest node editor if there is more than one. */ + if (ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0)) { + if (SpaceNode *snode = static_cast(area->spacedata.first)) { + ScrArea *old_area = CTX_wm_area(C); + ARegion *old_region = CTX_wm_region(C); + + /* Override the context since it is used by the View2D panning code. */ + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, static_cast(area->regionbase.last)); + const int result = space_node_view_geometry_nodes_legacy(C, snode, op); + CTX_wm_area_set(C, old_area); + CTX_wm_region_set(C, old_region); + return result; + } + } + + return OPERATOR_CANCELLED; +} + +static bool geometry_node_view_legacy_poll(bContext *C) +{ + /* Allow direct execution in a node editor, but also affecting any visible node editor. */ + return ED_operator_node_active(C) || BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_NODE, 0); +} + +void NODE_OT_geometry_node_view_legacy(wmOperatorType *ot) +{ + ot->name = "View Deprecated Geometry Nodes"; + ot->idname = "NODE_OT_geometry_node_view_legacy"; + ot->description = "Select and view legacy geometry nodes in the node editor"; + + ot->exec = geometry_node_view_legacy_exec; + ot->poll = geometry_node_view_legacy_poll; + + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ diff --git a/source/blender/editors/space_node/space_node.c b/source/blender/editors/space_node/space_node.c index 956fb3aa867..0b5d7cdda82 100644 --- a/source/blender/editors/space_node/space_node.c +++ b/source/blender/editors/space_node/space_node.c @@ -201,27 +201,6 @@ void ED_node_tree_path_get(SpaceNode *snode, char *value) } } -void ED_node_tree_path_get_fixedbuf(SpaceNode *snode, char *value, int max_length) -{ - int size; - - value[0] = '\0'; - int i = 0; - LISTBASE_FOREACH_INDEX (bNodeTreePath *, path, &snode->treepath, i) { - if (i == 0) { - size = BLI_strncpy_rlen(value, path->display_name, max_length); - } - else { - size = BLI_snprintf_rlen(value, max_length, "/%s", path->display_name); - } - max_length -= size; - if (max_length <= 0) { - break; - } - value += size; - } -} - void ED_node_set_active_viewer_key(SpaceNode *snode) { bNodeTreePath *path = snode->treepath.last; @@ -256,6 +235,8 @@ static SpaceLink *node_create(const ScrArea *UNUSED(area), const Scene *UNUSED(s snode->spacetype = SPACE_NODE; snode->flag = SNODE_SHOW_GPENCIL | SNODE_USE_ALPHA; + snode->overlay.flag |= SN_OVERLAY_SHOW_OVERLAYS; + snode->overlay.flag |= SN_OVERLAY_SHOW_WIRE_COLORS; /* backdrop */ snode->zoom = 1.0f; @@ -619,8 +600,8 @@ static void node_cursor(wmWindow *win, ScrArea *area, ARegion *region) /* convert mouse coordinates to v2d space */ UI_view2d_region_to_view(®ion->v2d, - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, &snode->runtime->cursor[0], &snode->runtime->cursor[1]); diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index a82f516b125..ed52eeab98c 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -868,7 +868,7 @@ static bool datastack_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) static char *datastack_drop_tooltip(bContext *UNUSED(C), wmDrag *drag, - const wmEvent *UNUSED(event), + const int UNUSED(xy[2]), struct wmDropBox *UNUSED(drop)) { StackDropData *drop_data = drag->poin; @@ -1201,11 +1201,13 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *collection_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int UNUSED(xy[2]), wmDropBox *UNUSED(drop)) { + wmWindowManager *wm = CTX_wm_manager(C); + const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL; CollectionDrop data; - if (!event->shift && collection_drop_init(C, drag, event, &data)) { + if (event && !event->shift && collection_drop_init(C, drag, event, &data)) { TreeElement *te = data.te; if (!data.from || event->ctrl) { return BLI_strdup(TIP_("Link inside Collection")); @@ -1309,7 +1311,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE } if (from) { - DEG_id_tag_update(&from->id, ID_RECALC_COPY_ON_WRITE); + DEG_id_tag_update(&from->id, ID_RECALC_COPY_ON_WRITE | ID_RECALC_GEOMETRY); } } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index c06a1010168..956c455c545 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -1841,16 +1841,20 @@ static bool outliner_draw_overrides_buts(uiBlock *block, if (tip == NULL) { tip = TIP_("Some sub-items require attention"); } - uiBut *bt = uiDefIconBlockBut(block, - NULL, - NULL, - 1, - ICON_ERROR, - (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), - te->ys, - UI_UNIT_X, - UI_UNIT_Y, - tip); + uiBut *bt = uiDefIconBut(block, + UI_BTYPE_BUT, + 1, + ICON_ERROR, + (int)(region->v2d.cur.xmax - OL_TOG_USER_BUTS_STATUS), + te->ys, + UI_UNIT_X, + UI_UNIT_Y, + NULL, + 0.0, + 0.0, + 0.0, + 0.0, + tip); UI_but_flag_enable(bt, but_flag); } any_item_has_warnings = any_item_has_warnings || item_has_warnings || any_child_has_warnings; @@ -2358,7 +2362,10 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) case eGpencilModifierType_Texture: data.icon = ICON_TEXTURE; break; - case eGpencilModifierType_Weight: + case eGpencilModifierType_WeightProximity: + data.icon = ICON_MOD_VERTEX_WEIGHT; + break; + case eGpencilModifierType_WeightAngle: data.icon = ICON_MOD_VERTEX_WEIGHT; break; diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index 581892ebb3a..5e409db0059 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -34,6 +34,7 @@ #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_shader_fx_types.h" +#include "DNA_text_types.h" #include "BLI_listbase.h" #include "BLI_utildefines.h" @@ -63,6 +64,7 @@ #include "ED_screen.h" #include "ED_select_utils.h" #include "ED_sequencer.h" +#include "ED_text.h" #include "ED_undo.h" #include "SEQ_select.h" @@ -737,6 +739,12 @@ static void tree_element_layer_collection_activate(bContext *C, TreeElement *te) WM_main_add_notifier(NC_SCENE | ND_LAYER | NS_LAYER_COLLECTION | NA_ACTIVATED, NULL); } +static void tree_element_text_activate(bContext *C, TreeElement *te) +{ + Text *text = (Text *)te->store_elem->id; + ED_text_activate_in_screen(C, text); +} + /* ---------------------------------------------- */ /* generic call for ID data check or make/check active in UI */ @@ -764,6 +772,9 @@ void tree_element_activate(bContext *C, case ID_CA: tree_element_camera_activate(C, tvc->scene, te); break; + case ID_TXT: + tree_element_text_activate(C, te); + break; } } diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 9e314701719..ae2b1870884 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -197,11 +197,24 @@ static void get_element_operation_type( static TreeElement *get_target_element(SpaceOutliner *space_outliner) { TreeElement *te = outliner_find_element_with_flag(&space_outliner->tree, TSE_ACTIVE); - BLI_assert(te); return te; } +static bool outliner_operation_tree_element_poll(bContext *C) +{ + if (!ED_operator_outliner_active(C)) { + return false; + } + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); + TreeElement *te = get_target_element(space_outliner); + if (te == NULL) { + return false; + } + + return true; +} + static void unlink_action_fn(bContext *C, ReportList *UNUSED(reports), Scene *UNUSED(scene), @@ -742,7 +755,7 @@ static void id_local_fn(bContext *C, } } else if (ID_IS_OVERRIDE_LIBRARY_REAL(tselem->id)) { - BKE_lib_override_library_free(&tselem->id->override_library, true); + BKE_lib_override_library_make_local(tselem->id); } } @@ -1426,7 +1439,7 @@ static void outliner_do_data_operation( } } -static Base *outline_batch_delete_hierarchy( +static Base *outliner_batch_delete_hierarchy( ReportList *reports, Main *bmain, ViewLayer *view_layer, Scene *scene, Base *base) { Base *child_base, *base_next; @@ -1444,7 +1457,7 @@ static Base *outline_batch_delete_hierarchy( /* pass */ } if (parent) { - base_next = outline_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base); + base_next = outliner_batch_delete_hierarchy(reports, bmain, view_layer, scene, child_base); } } @@ -1497,7 +1510,7 @@ static void object_batch_delete_hierarchy_fn(bContext *C, ED_object_editmode_exit(C, EM_FREEDATA); } - outline_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base); + outliner_batch_delete_hierarchy(reports, CTX_data_main(C), view_layer, scene, base); } } @@ -1868,6 +1881,10 @@ static bool outliner_id_operation_item_poll(bContext *C, PropertyRNA *UNUSED(prop), const int enum_value) { + if (!outliner_operation_tree_element_poll(C)) { + return false; + } + SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); TreeElement *te = get_target_element(space_outliner); TreeStoreElem *tselem = TREESTORE(te); @@ -2254,7 +2271,7 @@ void OUTLINER_OT_id_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_id_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->flag = 0; @@ -2361,7 +2378,7 @@ void OUTLINER_OT_lib_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_lib_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->prop = RNA_def_enum( ot->srna, "type", outliner_lib_op_type_items, 0, "Library Operation", ""); @@ -2420,14 +2437,8 @@ static int outliner_action_set_exec(bContext *C, wmOperator *op) Main *bmain = CTX_data_main(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - bAction *act; - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2482,7 +2493,7 @@ void OUTLINER_OT_action_set(wmOperatorType *ot) /* api callbacks */ ot->invoke = WM_enum_search_invoke; ot->exec = outliner_action_set_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -2531,12 +2542,6 @@ static int outliner_animdata_operation_exec(bContext *C, wmOperator *op) wmWindowManager *wm = CTX_wm_manager(C); SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2722,12 +2727,6 @@ static int outliner_data_operation_exec(bContext *C, wmOperator *op) { SpaceOutliner *space_outliner = CTX_wm_space_outliner(C); int scenelevel = 0, objectlevel = 0, idlevel = 0, datalevel = 0; - - /* check for invalid states */ - if (space_outliner == NULL) { - return OPERATOR_CANCELLED; - } - TreeElement *te = get_target_element(space_outliner); get_element_operation_type(te, &scenelevel, &objectlevel, &idlevel, &datalevel); @@ -2806,6 +2805,13 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, return DummyRNA_DEFAULT_items; } + TreeElement *te = get_target_element(space_outliner); + if (te == NULL) { + return DummyRNA_NULL_items; + } + + TreeStoreElem *tselem = TREESTORE(te); + static const EnumPropertyItem optype_sel_and_hide[] = { {OL_DOP_SELECT, "SELECT", 0, "Select", ""}, {OL_DOP_DESELECT, "DESELECT", 0, "Deselect", ""}, @@ -2816,9 +2822,6 @@ static const EnumPropertyItem *outliner_data_op_sets_enum_item_fn(bContext *C, static const EnumPropertyItem optype_sel_linked[] = { {OL_DOP_SELECT_LINKED, "SELECT_LINKED", 0, "Select Linked", ""}, {0, NULL, 0, NULL, NULL}}; - TreeElement *te = get_target_element(space_outliner); - TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == TSE_RNA_STRUCT) { return optype_sel_linked; } @@ -2835,7 +2838,7 @@ void OUTLINER_OT_data_operation(wmOperatorType *ot) /* callbacks */ ot->invoke = WM_menu_invoke; ot->exec = outliner_data_operation_exec; - ot->poll = ED_operator_outliner_active; + ot->poll = outliner_operation_tree_element_poll; ot->flag = 0; diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index c5ec656080a..5427ae31ac3 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1864,6 +1864,15 @@ static void outliner_filter_tree(SpaceOutliner *space_outliner, ViewLayer *view_ space_outliner, view_layer, &space_outliner->tree, search_string, exclude_filter); } +static void outliner_clear_newid_from_main(Main *bmain) +{ + ID *id_iter; + FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { + id_iter->newid = NULL; + } + FOREACH_MAIN_ID_END; +} + /* ======================================================= */ /* Main Tree Building API */ @@ -1926,5 +1935,7 @@ void outliner_build_tree(Main *mainvar, outliner_filter_tree(space_outliner, view_layer); outliner_restore_scrolling_position(space_outliner, region, &focus); - BKE_main_id_newptr_and_tag_clear(mainvar); + /* `ID.newid` pointer is abused when building tree, DO NOT call #BKE_main_id_newptr_and_tag_clear + * as this expects valid IDs in this pointer, not random unknown data. */ + outliner_clear_newid_from_main(mainvar); } diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 5feb157bfc8..c62ca468747 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -117,10 +117,8 @@ static TreeElement *outliner_find_item_at_x_in_row_recursive(const TreeElement * { TreeElement *child_te = parent_te->subtree.first; - bool over_element = false; - while (child_te) { - over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); + const bool over_element = (view_co_x > child_te->xs) && (view_co_x < child_te->xend); if ((child_te->flag & TE_ICONROW) && over_element) { return child_te; } diff --git a/source/blender/editors/space_outliner/tree/tree_display.hh b/source/blender/editors/space_outliner/tree/tree_display.hh index 96af8258010..8aaf396888f 100644 --- a/source/blender/editors/space_outliner/tree/tree_display.hh +++ b/source/blender/editors/space_outliner/tree/tree_display.hh @@ -106,7 +106,7 @@ class TreeDisplayLibraries final : public AbstractTreeDisplay { private: TreeElement *add_library_contents(Main &, ListBase &, Library *) const; - bool library_id_filter_poll(Library *lib, ID *id) const; + bool library_id_filter_poll(const Library *lib, ID *id) const; short id_filter_get() const; }; @@ -124,7 +124,7 @@ class TreeDisplayOverrideLibrary final : public AbstractTreeDisplay { private: TreeElement *add_library_contents(Main &, ListBase &, Library *) const; - bool override_library_id_filter_poll(Library *lib, ID *id) const; + bool override_library_id_filter_poll(const Library *lib, ID *id) const; short id_filter_get() const; }; diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index c6b700318dd..371813cfb3f 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -186,7 +186,7 @@ short TreeDisplayLibraries::id_filter_get() const return 0; } -bool TreeDisplayLibraries::library_id_filter_poll(Library *lib, ID *id) const +bool TreeDisplayLibraries::library_id_filter_poll(const Library *lib, ID *id) const { if (id->lib != lib) { return false; diff --git a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc index a17bf174a74..0e4636db69d 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_override_library.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_override_library.cc @@ -186,7 +186,7 @@ short TreeDisplayOverrideLibrary::id_filter_get() const return 0; } -bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(Library *lib, ID *id) const +bool TreeDisplayOverrideLibrary::override_library_id_filter_poll(const Library *lib, ID *id) const { if (id->lib != lib) { return false; diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 113d421ed91..36da7fe1944 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -91,7 +91,8 @@ static void tree_element_free(AbstractTreeElement **tree_element) *tree_element = nullptr; } -static void tree_element_expand(AbstractTreeElement &tree_element, SpaceOutliner &space_outliner) +static void tree_element_expand(const AbstractTreeElement &tree_element, + SpaceOutliner &space_outliner) { /* Most types can just expand. IDs optionally expand (hence the poll) and do additional, common * expanding. Could be done nicer, we could request a small "expander" helper object from the @@ -107,7 +108,7 @@ static void tree_element_expand(AbstractTreeElement &tree_element, SpaceOutliner * Needed for types that still expand in C, but need to execute the same post-expand logic. Can be * removed once all ID types expand entirely using the new design. */ -static void tree_element_post_expand_only(AbstractTreeElement &tree_element, +static void tree_element_post_expand_only(const AbstractTreeElement &tree_element, SpaceOutliner &space_outliner) { tree_element.postExpand(space_outliner); @@ -116,8 +117,8 @@ static void tree_element_post_expand_only(AbstractTreeElement &tree_element, * Needed for types that still expand in C, to poll if they should expand in current context. Can * be removed once all ID types expand entirely using the new design. */ -static bool tree_element_expand_poll(AbstractTreeElement &tree_element, - SpaceOutliner &space_outliner) +static bool tree_element_expand_poll(const AbstractTreeElement &tree_element, + const SpaceOutliner &space_outliner) { return tree_element.expandPoll(space_outliner); } diff --git a/source/blender/editors/space_sequencer/CMakeLists.txt b/source/blender/editors/space_sequencer/CMakeLists.txt index e1c193b0c15..bf8cf89699d 100644 --- a/source/blender/editors/space_sequencer/CMakeLists.txt +++ b/source/blender/editors/space_sequencer/CMakeLists.txt @@ -22,6 +22,7 @@ set(INC ../../blenlib ../../blentranslation ../../depsgraph + ../../draw ../../gpu ../../imbuf ../../makesdna @@ -45,6 +46,7 @@ set(SRC sequencer_proxy.c sequencer_scopes.c sequencer_select.c + sequencer_thumbnails.c sequencer_view.c space_sequencer.c diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 081f0241e94..e58e52b5e94 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -47,6 +47,7 @@ #include "BKE_mask.h" #include "BKE_movieclip.h" #include "BKE_report.h" +#include "BKE_sound.h" #include "IMB_imbuf.h" @@ -194,7 +195,7 @@ static int sequencer_generic_invoke_xy_guess_channel(bContext *C, int type) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if ((type == -1 || seq->type == type) && (seq->enddisp < timeline_frame) && + if ((ELEM(type, -1, seq->type)) && (seq->enddisp < timeline_frame) && (timeline_frame - seq->enddisp < proximity)) { tgt = seq; proximity = timeline_frame - seq->enddisp; @@ -643,7 +644,15 @@ static void sequencer_add_movie_multiple_strips(bContext *C, BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset; + double video_start_offset = -1; + double audio_start_offset = 0; + + if (RNA_boolean_get(op->ptr, "sound")) { + SoundStreamInfo sound_info; + if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { + audio_start_offset = video_start_offset = sound_info.start; + } + } load_data->channel++; seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); @@ -653,9 +662,30 @@ static void sequencer_add_movie_multiple_strips(bContext *C, } else { if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); + int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; + + int video_frame_offset = video_start_offset * FPS; + int audio_frame_offset = audio_start_offset * FPS; + + double video_frame_remainder = video_start_offset * FPS - video_frame_offset; + double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; + + double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; + + video_frame_offset -= minimum_frame_offset; + audio_frame_offset -= minimum_frame_offset; + + load_data->start_frame += audio_frame_offset; + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); + + int min_startdisp = MIN2(seq_movie->startdisp, seq_sound->startdisp); + int max_enddisp = MAX2(seq_movie->enddisp, seq_sound->enddisp); + + load_data->start_frame += max_enddisp - min_startdisp - audio_frame_offset; + } + else { + load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; } - load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); seq_build_proxy(C, seq_movie); @@ -672,7 +702,15 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa Sequence *seq_movie = NULL; Sequence *seq_sound = NULL; - double video_start_offset; + double video_start_offset = -1; + double audio_start_offset = 0; + + if (RNA_boolean_get(op->ptr, "sound")) { + SoundStreamInfo sound_info; + if (BKE_sound_stream_info_get(bmain, load_data->path, 0, &sound_info)) { + audio_start_offset = video_start_offset = sound_info.start; + } + } load_data->channel++; seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data, &video_start_offset); @@ -683,7 +721,21 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa return false; } if (RNA_boolean_get(op->ptr, "sound")) { - seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, video_start_offset); + int minimum_frame_offset = MIN2(video_start_offset, audio_start_offset) * FPS; + + int video_frame_offset = video_start_offset * FPS; + int audio_frame_offset = audio_start_offset * FPS; + + double video_frame_remainder = video_start_offset * FPS - video_frame_offset; + double audio_frame_remainder = audio_start_offset * FPS - audio_frame_offset; + + double audio_skip = (video_frame_remainder - audio_frame_remainder) / FPS; + + video_frame_offset -= minimum_frame_offset; + audio_frame_offset -= minimum_frame_offset; + + load_data->start_frame += audio_frame_offset; + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip); } seq_load_apply_generic_options(C, op, seq_sound); seq_load_apply_generic_options(C, op, seq_movie); @@ -1059,7 +1111,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) /* Adjust length. */ if (load_data.image.len == 1) { SEQ_transform_set_right_handle_frame(seq, load_data.image.end_frame); - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, SEQ_active_seqbase_get(ed), seq); } seq_load_apply_generic_options(C, op, seq); diff --git a/source/blender/editors/space_sequencer/sequencer_buttons.c b/source/blender/editors/space_sequencer/sequencer_buttons.c index 1e0ecfd890e..4ece7f6a481 100644 --- a/source/blender/editors/space_sequencer/sequencer_buttons.c +++ b/source/blender/editors/space_sequencer/sequencer_buttons.c @@ -78,9 +78,10 @@ static void metadata_panel_context_draw(const bContext *C, Panel *panel) SpaceSeq *space_sequencer = CTX_wm_space_seq(C); /* NOTE: We can only reliably show metadata for the original (current) * frame when split view is used. */ - const bool show_split = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && + const bool show_split = (scene->ed && + (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) && (space_sequencer->mainb == SEQ_DRAW_IMG_IMBUF)); - if (show_split && space_sequencer->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE) { + if (show_split && (space_sequencer->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE)) { return; } /* NOTE: We disable multiview for drawing, since we don't know what is the diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index b56ad48cec2..09cb21eca1e 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -71,6 +71,7 @@ #include "BIF_glutil.h" #include "SEQ_effects.h" +#include "SEQ_iterator.h" #include "SEQ_prefetch.h" #include "SEQ_proxy.h" #include "SEQ_relations.h" @@ -100,40 +101,56 @@ #define SEQ_HANDLE_SIZE 8.0f #define SEQ_SCROLLER_TEXT_OFFSET 8 #define MUTE_ALPHA 120 -#define OVERLAP_ALPHA 180 static Sequence *special_seq_update = NULL; -void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) +void color3ubv_from_seq(const Scene *curscene, + const Sequence *seq, + const bool show_strip_color_tag, + uchar r_col[3]) { + if (show_strip_color_tag && (uint)seq->color_tag < SEQUENCE_COLOR_TOT && + seq->color_tag != SEQUENCE_COLOR_NONE) { + bTheme *btheme = UI_GetTheme(); + const ThemeStripColor *strip_color = &btheme->strip_color[seq->color_tag]; + copy_v3_v3_uchar(r_col, strip_color->color); + return; + } + uchar blendcol[3]; + /* Sometimes the active theme is not the sequencer theme, e.g. when an operator invokes the file + * browser. This makes sure we get the right color values for the theme. */ + struct bThemeState theme_state; + UI_Theme_Store(&theme_state); + UI_SetTheme(SPACE_SEQ, RGN_TYPE_WINDOW); + switch (seq->type) { case SEQ_TYPE_IMAGE: - UI_GetThemeColor3ubv(TH_SEQ_IMAGE, col); + UI_GetThemeColor3ubv(TH_SEQ_IMAGE, r_col); break; case SEQ_TYPE_META: - UI_GetThemeColor3ubv(TH_SEQ_META, col); + UI_GetThemeColor3ubv(TH_SEQ_META, r_col); break; case SEQ_TYPE_MOVIE: - UI_GetThemeColor3ubv(TH_SEQ_MOVIE, col); + UI_GetThemeColor3ubv(TH_SEQ_MOVIE, r_col); break; case SEQ_TYPE_MOVIECLIP: - UI_GetThemeColor3ubv(TH_SEQ_MOVIECLIP, col); + UI_GetThemeColor3ubv(TH_SEQ_MOVIECLIP, r_col); break; case SEQ_TYPE_MASK: - UI_GetThemeColor3ubv(TH_SEQ_MASK, col); + UI_GetThemeColor3ubv(TH_SEQ_MASK, r_col); break; case SEQ_TYPE_SCENE: - UI_GetThemeColor3ubv(TH_SEQ_SCENE, col); + UI_GetThemeColor3ubv(TH_SEQ_SCENE, r_col); if (seq->scene == curscene) { - UI_GetColorPtrShade3ubv(col, col, 20); + UI_GetColorPtrShade3ubv(r_col, r_col, 20); } break; @@ -141,9 +158,9 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) case SEQ_TYPE_CROSS: case SEQ_TYPE_GAMCROSS: case SEQ_TYPE_WIPE: - col[0] = 130; - col[1] = 130; - col[2] = 130; + r_col[0] = 130; + r_col[1] = 130; + r_col[2] = 130; break; /* Effects. */ @@ -160,72 +177,74 @@ void color3ubv_from_seq(Scene *curscene, Sequence *seq, uchar col[3]) case SEQ_TYPE_ADJUSTMENT: case SEQ_TYPE_GAUSSIAN_BLUR: case SEQ_TYPE_COLORMIX: - UI_GetThemeColor3ubv(TH_SEQ_EFFECT, col); + UI_GetThemeColor3ubv(TH_SEQ_EFFECT, r_col); /* Slightly offset hue to distinguish different effects. */ if (seq->type == SEQ_TYPE_ADD) { - rgb_byte_set_hue_float_offset(col, 0.03); + rgb_byte_set_hue_float_offset(r_col, 0.03); } else if (seq->type == SEQ_TYPE_SUB) { - rgb_byte_set_hue_float_offset(col, 0.06); + rgb_byte_set_hue_float_offset(r_col, 0.06); } else if (seq->type == SEQ_TYPE_MUL) { - rgb_byte_set_hue_float_offset(col, 0.13); + rgb_byte_set_hue_float_offset(r_col, 0.13); } else if (seq->type == SEQ_TYPE_ALPHAOVER) { - rgb_byte_set_hue_float_offset(col, 0.16); + rgb_byte_set_hue_float_offset(r_col, 0.16); } else if (seq->type == SEQ_TYPE_ALPHAUNDER) { - rgb_byte_set_hue_float_offset(col, 0.23); + rgb_byte_set_hue_float_offset(r_col, 0.23); } else if (seq->type == SEQ_TYPE_OVERDROP) { - rgb_byte_set_hue_float_offset(col, 0.26); + rgb_byte_set_hue_float_offset(r_col, 0.26); } else if (seq->type == SEQ_TYPE_COLORMIX) { - rgb_byte_set_hue_float_offset(col, 0.33); + rgb_byte_set_hue_float_offset(r_col, 0.33); } else if (seq->type == SEQ_TYPE_GAUSSIAN_BLUR) { - rgb_byte_set_hue_float_offset(col, 0.43); + rgb_byte_set_hue_float_offset(r_col, 0.43); } else if (seq->type == SEQ_TYPE_GLOW) { - rgb_byte_set_hue_float_offset(col, 0.46); + rgb_byte_set_hue_float_offset(r_col, 0.46); } else if (seq->type == SEQ_TYPE_ADJUSTMENT) { - rgb_byte_set_hue_float_offset(col, 0.55); + rgb_byte_set_hue_float_offset(r_col, 0.55); } else if (seq->type == SEQ_TYPE_SPEED) { - rgb_byte_set_hue_float_offset(col, 0.65); + rgb_byte_set_hue_float_offset(r_col, 0.65); } else if (seq->type == SEQ_TYPE_TRANSFORM) { - rgb_byte_set_hue_float_offset(col, 0.75); + rgb_byte_set_hue_float_offset(r_col, 0.75); } else if (seq->type == SEQ_TYPE_MULTICAM) { - rgb_byte_set_hue_float_offset(col, 0.85); + rgb_byte_set_hue_float_offset(r_col, 0.85); } break; case SEQ_TYPE_COLOR: - UI_GetThemeColor3ubv(TH_SEQ_COLOR, col); + UI_GetThemeColor3ubv(TH_SEQ_COLOR, r_col); break; case SEQ_TYPE_SOUND_RAM: - UI_GetThemeColor3ubv(TH_SEQ_AUDIO, col); + UI_GetThemeColor3ubv(TH_SEQ_AUDIO, r_col); blendcol[0] = blendcol[1] = blendcol[2] = 128; if (seq->flag & SEQ_MUTE) { - UI_GetColorPtrBlendShade3ubv(col, blendcol, col, 0.5, 20); + UI_GetColorPtrBlendShade3ubv(r_col, blendcol, r_col, 0.5, 20); } break; case SEQ_TYPE_TEXT: - UI_GetThemeColor3ubv(TH_SEQ_TEXT, col); + UI_GetThemeColor3ubv(TH_SEQ_TEXT, r_col); break; default: - col[0] = 10; - col[1] = 255; - col[2] = 40; + r_col[0] = 10; + r_col[1] = 255; + r_col[2] = 40; break; } + + UI_Theme_Restore(&theme_state); } typedef struct WaveVizData { @@ -327,7 +346,8 @@ static void draw_seq_waveform_overlay(View2D *v2d, float y2, float frames_per_pixel) { - if (seq->sound && ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + if (seq->sound && ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || + (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { /* Make sure that the start drawing position is aligned to the pixels on the screen to avoid * flickering when moving around the strip. * To do this we figure out the fractional offset in pixel space by checking where the @@ -420,6 +440,10 @@ static void draw_seq_waveform_overlay(View2D *v2d, float sample_offset = start_sample + i * samples_per_pix; int p = sample_offset; + if (p < 0) { + continue; + } + if (p >= waveform->length) { break; } @@ -550,7 +574,13 @@ static void draw_seq_waveform_overlay(View2D *v2d, } } -static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, float x2, float y2) +static void drawmeta_contents(Scene *scene, + Sequence *seqm, + float x1, + float y1, + float x2, + float y2, + const bool show_strip_color_tag) { Sequence *seq; uchar col[4]; @@ -606,7 +636,7 @@ static void drawmeta_contents(Scene *scene, Sequence *seqm, float x1, float y1, rgb_float_to_uchar(col, colvars->col); } else { - color3ubv_from_seq(scene, seq, col); + color3ubv_from_seq(scene, seq, show_strip_color_tag, col); } if ((seqm->flag & SEQ_MUTE) || (seq->flag & SEQ_MUTE)) { @@ -872,12 +902,12 @@ static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq, const char *text_array[5]; int i = 0; - if (sseq->flag & SEQ_SHOW_STRIP_NAME) { + if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_NAME) { text_array[i++] = draw_seq_text_get_name(seq); } char source[FILE_MAX]; - if (sseq->flag & SEQ_SHOW_STRIP_SOURCE) { + if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) { draw_seq_text_get_source(seq, source, sizeof(source)); if (source[0] != '\0') { if (i != 0) { @@ -888,7 +918,7 @@ static size_t draw_seq_text_get_overlay_string(SpaceSeq *sseq, } char strip_duration_text[16]; - if (sseq->flag & SEQ_SHOW_STRIP_DURATION) { + if (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION) { const int strip_duration = seq->enddisp - seq->startdisp; SNPRINTF(strip_duration_text, "%d", strip_duration); if (i != 0) { @@ -945,7 +975,8 @@ static void draw_seq_text_overlay(View2D *v2d, UI_view2d_text_cache_add_rectf(v2d, &rect, overlay_string, overlay_string_len, col); } -static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint pos, float pixely) +static void draw_sequence_extensions_overlay( + Scene *scene, Sequence *seq, uint pos, float pixely, const bool show_strip_color_tag) { float x1, x2, y1, y2; uchar col[4], blend_col[3]; @@ -958,7 +989,7 @@ static void draw_sequence_extensions_overlay(Scene *scene, Sequence *seq, uint p GPU_blend(GPU_BLEND_ALPHA); - color3ubv_from_seq(scene, seq, col); + color3ubv_from_seq(scene, seq, show_strip_color_tag, col); if (seq->flag & SELECT) { UI_GetColorPtrShade3ubv(col, col, 50); } @@ -1028,7 +1059,8 @@ static void draw_seq_background(Scene *scene, float x2, float y1, float y2, - bool is_single_image) + bool is_single_image, + bool show_strip_color_tag) { uchar col[4]; GPU_blend(GPU_BLEND_ALPHA); @@ -1041,11 +1073,11 @@ static void draw_seq_background(Scene *scene, rgb_float_to_uchar(col, colvars->col); } else { - color3ubv_from_seq(scene, seq1, col); + color3ubv_from_seq(scene, seq1, show_strip_color_tag, col); } } else { - color3ubv_from_seq(scene, seq, col); + color3ubv_from_seq(scene, seq, show_strip_color_tag, col); } /* Draw muted strips semi-transparent. */ @@ -1100,7 +1132,7 @@ static void draw_seq_background(Scene *scene, rgb_float_to_uchar(col, colvars->col); } else { - color3ubv_from_seq(scene, seq2, col); + color3ubv_from_seq(scene, seq2, show_strip_color_tag, col); /* If the transition inputs are of the same type, draw the right side slightly darker. */ if (seq1->type == seq2->type) { UI_GetColorPtrShade3ubv(col, col, -15); @@ -1294,6 +1326,10 @@ static void draw_seq_strip(const bContext *C, /* Check if we are doing "solo preview". */ bool is_single_image = (char)SEQ_transform_single_image_check(seq); + /* Use the seq->color_tag to display the tag color. */ + const bool show_strip_color_tag = (sseq->timeline_overlay.flag & + SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG); + /* Draw strip body. */ x1 = (seq->startstill) ? seq->start : seq->startdisp; y1 = seq->machine + SEQ_STRIP_OFSBOTTOM; @@ -1306,8 +1342,9 @@ static void draw_seq_strip(const bContext *C, float text_margin_y; bool y_threshold; - if ((sseq->flag & SEQ_SHOW_STRIP_NAME) || (sseq->flag & SEQ_SHOW_STRIP_SOURCE) || - (sseq->flag & SEQ_SHOW_STRIP_DURATION)) { + if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_NAME) || + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_SOURCE) || + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_DURATION)) { /* Calculate height needed for drawing text on strip. */ text_margin_y = y2 - min_ff(0.40f, 20 * U.dpi_fac * pixely); @@ -1323,7 +1360,7 @@ static void draw_seq_strip(const bContext *C, uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - draw_seq_background(scene, seq, pos, x1, x2, y1, y2, is_single_image); + draw_seq_background(scene, seq, pos, x1, x2, y1, y2, is_single_image, show_strip_color_tag); /* Draw a color band inside color strip. */ if (seq->type == SEQ_TYPE_COLOR && y_threshold) { @@ -1331,10 +1368,11 @@ static void draw_seq_strip(const bContext *C, } /* Draw strip offsets when flag is enabled or during "solo preview". */ - if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + if (sseq->flag & SEQ_SHOW_OVERLAY) { if (!is_single_image && (seq->startofs || seq->endofs) && pixely > 0) { - if ((sseq->draw_flag & SEQ_DRAW_OFFSET_EXT) || (seq == special_seq_update)) { - draw_sequence_extensions_overlay(scene, seq, pos, pixely); + if ((sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_STRIP_OFFSETS) || + (seq == special_seq_update)) { + draw_sequence_extensions_overlay(scene, seq, pos, pixely, show_strip_color_tag); } } } @@ -1345,16 +1383,23 @@ static void draw_seq_strip(const bContext *C, if ((seq->type == SEQ_TYPE_META) || ((seq->type == SEQ_TYPE_SCENE) && (seq->flag & SEQ_SCENE_STRIPS))) { - drawmeta_contents(scene, seq, x1, y1, x2, y2); + drawmeta_contents(scene, seq, x1, y1, x2, y2, show_strip_color_tag); } - if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_FCURVES)) { + if ((sseq->flag & SEQ_SHOW_OVERLAY) && + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_THUMBNAILS) && + (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE))) { + draw_seq_strip_thumbnail(v2d, C, scene, seq, y1, y2, pixelx, pixely); + } + + if ((sseq->flag & SEQ_SHOW_OVERLAY) && + (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_FCURVES)) { draw_seq_fcurve_overlay(scene, v2d, seq, x1, y1, x2, y2, pixelx); } /* Draw sound strip waveform. */ - if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) && - (sseq->flag & SEQ_NO_WAVEFORMS) == 0) { + if ((seq->type == SEQ_TYPE_SOUND_RAM) && ((sseq->flag & SEQ_SHOW_OVERLAY)) && + (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0) { draw_seq_waveform_overlay(v2d, C, sseq, @@ -1394,13 +1439,14 @@ static void draw_seq_strip(const bContext *C, /* If a waveform is drawn, avoid drawing text when there is not enough vertical space. */ if (seq->type == SEQ_TYPE_SOUND_RAM) { - if (!y_threshold && (sseq->flag & SEQ_NO_WAVEFORMS) == 0 && - ((sseq->flag & SEQ_ALL_WAVEFORMS) || (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { + if (!y_threshold && (sseq->timeline_overlay.flag & SEQ_TIMELINE_NO_WAVEFORMS) == 0 && + ((sseq->timeline_overlay.flag & SEQ_TIMELINE_ALL_WAVEFORMS) || + (seq->flag & SEQ_AUDIO_DRAW_WAVEFORM))) { return; } } - if (sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + if (sseq->flag & SEQ_SHOW_OVERLAY) { /* Don't draw strip if there is not enough vertical or horizontal space. */ if (((x2 - x1) > 32 * pixelx * U.dpi_fac) && ((y2 - y1) > 8 * pixely * U.dpi_fac)) { /* Depending on the vertical space, draw text on top or in the center of strip. */ @@ -1643,7 +1689,7 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, imm_draw_box_wire_2d(shdr_pos, x1 - 0.5f, y1 - 0.5f, x2 + 0.5f, y2 + 0.5f); /* Draw safety border. */ - if (sseq->flag & SEQ_SHOW_SAFE_MARGINS) { + if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_MARGINS) { immUniformThemeColorBlend(TH_VIEW_OVERLAY, TH_BACK, 0.25f); UI_draw_safe_areas(shdr_pos, @@ -1656,7 +1702,7 @@ static void sequencer_draw_borders_overlay(const SpaceSeq *sseq, scene->safe_areas.title, scene->safe_areas.action); - if (sseq->flag & SEQ_SHOW_SAFE_CENTER) { + if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_SAFE_CENTER) { UI_draw_safe_areas(shdr_pos, &(const rctf){ .xmin = x1, @@ -1816,15 +1862,15 @@ static void sequencer_preview_get_rect(rctf *preview, sequencer_display_size(scene, viewrect); BLI_rctf_init(preview, -1.0f, 1.0f, -1.0f, 1.0f); - if (draw_overlay && sseq->overlay_type == SEQ_DRAW_OVERLAY_RECT) { + if (draw_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT)) { preview->xmax = v2d->tot.xmin + - (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->over_border.xmax); + (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->overlay_frame_rect.xmax); preview->xmin = v2d->tot.xmin + - (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->over_border.xmin); + (fabsf(BLI_rctf_size_x(&v2d->tot)) * scene->ed->overlay_frame_rect.xmin); preview->ymax = v2d->tot.ymin + - (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->over_border.ymax); + (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->overlay_frame_rect.ymax); preview->ymin = v2d->tot.ymin + - (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->over_border.ymin); + (fabsf(BLI_rctf_size_y(&v2d->tot)) * scene->ed->overlay_frame_rect.ymin); } else if (draw_backdrop) { float aspect = BLI_rcti_size_x(®ion->winrct) / (float)BLI_rcti_size_y(®ion->winrct); @@ -1912,8 +1958,8 @@ static void sequencer_draw_display_buffer(const bContext *C, rctf canvas; sequencer_preview_get_rect(&preview, scene, region, sseq, draw_overlay, draw_backdrop); - if (draw_overlay && sseq->overlay_type == SEQ_DRAW_OVERLAY_RECT) { - canvas = scene->ed->over_border; + if (draw_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_RECT)) { + canvas = scene->ed->overlay_frame_rect; } else { BLI_rctf_init(&canvas, 0.0f, 1.0f, 0.0f, 1.0f); @@ -2047,6 +2093,69 @@ static int sequencer_draw_get_transform_preview_frame(Scene *scene) return preview_frame; } +static void seq_draw_image_origin_and_outline(const bContext *C, Sequence *seq, bool is_active_seq) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + if ((seq->flag & SELECT) == 0) { + return; + } + if (ED_screen_animation_no_scrub(CTX_wm_manager(C))) { + return; + } + if ((sseq->flag & SEQ_SHOW_OVERLAY) == 0 || + (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_OUTLINE_SELECTED) == 0) { + return; + } + if (ELEM(sseq->mainb, SEQ_DRAW_IMG_WAVEFORM, SEQ_DRAW_IMG_VECTORSCOPE, SEQ_DRAW_IMG_HISTOGRAM)) { + return; + } + + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(CTX_data_scene(C), seq, origin); + + /* Origin. */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); + immUniform1f("outlineWidth", 1.5f); + immUniformColor3f(1.0f, 1.0f, 1.0f); + immUniform4f("outlineColor", 0.0f, 0.0f, 0.0f, 1.0f); + immUniform1f("size", 15.0f * U.pixelsize); + immBegin(GPU_PRIM_POINTS, 1); + immVertex2f(pos, origin[0], origin[1]); + immEnd(); + immUnbindProgram(); + + /* Outline. */ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(CTX_data_scene(C), seq, seq_image_quad); + + GPU_line_smooth(true); + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_width(2); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + + float col[3]; + if (is_active_seq) { + UI_GetThemeColor3fv(TH_SEQ_ACTIVE, col); + } + else { + UI_GetThemeColor3fv(TH_SEQ_SELECTED, col); + } + immUniformColor3fv(col); + immUniform1f("lineWidth", U.pixelsize); + immBegin(GPU_PRIM_LINE_LOOP, 4); + immVertex2f(pos, seq_image_quad[0][0], seq_image_quad[0][1]); + immVertex2f(pos, seq_image_quad[1][0], seq_image_quad[1][1]); + immVertex2f(pos, seq_image_quad[2][0], seq_image_quad[2][1]); + immVertex2f(pos, seq_image_quad[3][0], seq_image_quad[3][1]); + immEnd(); + immUnbindProgram(); + GPU_line_width(1); + GPU_blend(GPU_BLEND_NONE); + GPU_line_smooth(false); +} + void sequencer_draw_preview(const bContext *C, Scene *scene, ARegion *region, @@ -2063,7 +2172,7 @@ void sequencer_draw_preview(const bContext *C, struct ImBuf *scope = NULL; float viewrect[2]; const bool show_imbuf = ED_space_sequencer_check_show_imbuf(sseq); - const bool draw_gpencil = ((sseq->flag & SEQ_SHOW_GPENCIL) && sseq->gpd); + const bool draw_gpencil = ((sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_GPENCIL) && sseq->gpd); const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; sequencer_stop_running_jobs(C, scene); @@ -2098,7 +2207,8 @@ void sequencer_draw_preview(const bContext *C, UI_view2d_view_ortho(v2d); /* Draw background. */ - if (!draw_backdrop && (!draw_overlay || sseq->overlay_type == SEQ_DRAW_OVERLAY_REFERENCE)) { + if (!draw_backdrop && + (!draw_overlay || (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE))) { sequencer_preview_clear(); if (sseq->flag & SEQ_USE_ALPHA) { @@ -2114,22 +2224,38 @@ void sequencer_draw_preview(const bContext *C, C, scene, region, sseq, ibuf, scope, draw_overlay, draw_backdrop); /* Draw over image. */ - if (sseq->flag & SEQ_SHOW_METADATA && sseq->flag & SEQ_SHOW_STRIP_OVERLAY) { + if (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_METADATA && sseq->flag & SEQ_SHOW_OVERLAY) { ED_region_image_metadata_draw(0.0, 0.0, ibuf, &v2d->tot, 1.0, 1.0); } } - if (show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { sequencer_draw_borders_overlay(sseq, v2d, scene); } - if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)) { + if (!draw_backdrop && scene->ed != NULL) { + SeqCollection *collection = SEQ_query_rendered_strips(scene->ed->seqbasep, timeline_frame, 0); + Sequence *seq; + Sequence *active_seq = SEQ_select_active_get(scene); + SEQ_ITERATOR_FOREACH (seq, collection) { + seq_draw_image_origin_and_outline(C, seq, seq == active_seq); + } + SEQ_collection_free(collection); + } + + if (draw_gpencil && show_imbuf && (sseq->flag & SEQ_SHOW_OVERLAY)) { sequencer_draw_gpencil_overlay(C); } + #if 0 sequencer_draw_maskedit(C, scene, region, sseq); #endif + /* Draw registered callbacks. */ + GPU_framebuffer_bind(framebuffer_overlay); + ED_region_draw_cb_draw(C, region, REGION_DRAW_POST_VIEW); + GPU_framebuffer_bind_no_srgb(framebuffer_overlay); + /* Scope is freed in sequencer_check_scopes when `ibuf` changes and redraw is needed. */ if (ibuf) { IMB_freeImBuf(ibuf); @@ -2560,9 +2686,9 @@ static void draw_cache_view(const bContext *C) /* Draw sequencer timeline. */ static void draw_overlap_frame_indicator(const struct Scene *scene, const View2D *v2d) { - int overlap_frame = (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) ? - scene->ed->over_cfra : - scene->r.cfra + scene->ed->over_ofs; + int overlap_frame = (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) ? + scene->ed->overlay_frame_abs : + scene->r.cfra + scene->ed->overlay_frame_ofs; uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); @@ -2611,7 +2737,7 @@ void draw_timeline_seq(const bContext *C, ARegion *region) /* Get timeline bound-box, needed for the scroll-bars. */ SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &v2d->tot); draw_seq_backdrop(v2d); - if ((sseq->flag & SEQ_SHOW_STRIP_OVERLAY) && (sseq->flag & SEQ_SHOW_GRID)) { + if ((sseq->flag & SEQ_SHOW_OVERLAY) && (sseq->timeline_overlay.flag & SEQ_TIMELINE_SHOW_GRID)) { U.v2d_min_gridsize *= 3; UI_view2d_draw_lines_x__discrete_frames_or_seconds( v2d, scene, (sseq->flag & SEQ_DRAWFRAMES) == 0, false); @@ -2682,12 +2808,12 @@ void draw_timeline_seq_display(const bContext *C, ARegion *region) if (scene->ed != NULL) { UI_view2d_view_ortho(v2d); draw_cache_view(C); - if (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) { + if (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) { draw_overlap_frame_indicator(scene, v2d); } UI_view2d_view_restore(C); } - ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES), true); + ED_time_scrub_draw_current_frame(region, scene, !(sseq->flag & SEQ_DRAWFRAMES)); UI_view2d_scrollers_draw(v2d, NULL); } diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index b95b7fa0620..8c70f4e3f7a 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -63,6 +63,7 @@ #include "WM_types.h" #include "RNA_define.h" +#include "RNA_enum_types.h" /* For menu, popup, icons, etc. */ #include "ED_numinput.h" @@ -129,14 +130,13 @@ bool ED_space_sequencer_maskedit_poll(bContext *C) /* Are we displaying the seq output (not channels or histogram). */ bool ED_space_sequencer_check_show_imbuf(SpaceSeq *sseq) { - return (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW) && - ELEM(sseq->mainb, SEQ_DRAW_SEQUENCE, SEQ_DRAW_IMG_IMBUF)); + return (sseq->mainb == SEQ_DRAW_IMG_IMBUF) && + (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW)); } bool ED_space_sequencer_check_show_strip(SpaceSeq *sseq) { - return (ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW) && - ELEM(sseq->mainb, SEQ_DRAW_SEQUENCE, SEQ_DRAW_IMG_IMBUF)); + return ELEM(sseq->view, SEQ_VIEW_SEQUENCE, SEQ_VIEW_SEQUENCE_PREVIEW); } static bool sequencer_fcurves_targets_color_strip(const FCurve *fcurve) @@ -213,22 +213,38 @@ bool sequencer_strip_has_path_poll(bContext *C) bool sequencer_view_preview_poll(bContext *C) { SpaceSeq *sseq = CTX_wm_space_seq(C); - Editing *ed = SEQ_editing_get(CTX_data_scene(C)); - if (ed && sseq && (sseq->mainb == SEQ_DRAW_IMG_IMBUF)) { - return 1; + if (sseq == NULL) { + return false; + } + if (SEQ_editing_get(CTX_data_scene(C)) == NULL) { + return false; + } + if (!(ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW) && + (sseq->mainb == SEQ_DRAW_IMG_IMBUF))) { + return false; + } + ARegion *region = CTX_wm_region(C); + if (!(region && region->regiontype == RGN_TYPE_PREVIEW)) { + return false; } - return 0; + return true; } bool sequencer_view_strips_poll(bContext *C) { SpaceSeq *sseq = CTX_wm_space_seq(C); - if (sseq && ED_space_sequencer_check_show_strip(sseq)) { - return 1; + if (sseq == NULL) { + return false; } - - return 0; + if (!ED_space_sequencer_check_show_strip(sseq)) { + return false; + } + ARegion *region = CTX_wm_region(C); + if (!(region && region->regiontype == RGN_TYPE_WINDOW)) { + return false; + } + return true; } /** \} */ @@ -328,6 +344,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); Sequence *seq; int snap_frame; @@ -351,7 +368,7 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) SEQ_transform_handle_xlimits(seq, seq->flag & SEQ_LEFTSEL, seq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } } @@ -374,19 +391,19 @@ static int sequencer_snap_exec(bContext *C, wmOperator *op) if (!either_handle_selected) { SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } else if (seq->seq2 && (seq->seq2->flag & SELECT)) { if (!either_handle_selected) { SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } else if (seq->seq3 && (seq->seq3->flag & SELECT)) { if (!either_handle_selected) { SEQ_offset_animdata(scene, seq, (snap_frame - seq->startdisp)); } - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); } } } @@ -579,7 +596,6 @@ static int sequencer_slip_invoke(bContext *C, wmOperator *op, const wmEvent *eve static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) { /* Only data types supported for now. */ - Editing *ed = SEQ_editing_get(scene); bool changed = false; /* Iterate in reverse so meta-strips are iterated after their children. */ @@ -629,11 +645,15 @@ static bool sequencer_slip_recursively(Scene *scene, SlipData *data, int offset) * we can skip calculating for effects. * This way we can avoid an extra loop just for effects. */ if (!(seq->type & SEQ_TYPE_EFFECT)) { - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } } if (changed) { - SEQ_relations_free_imbuf(scene, &ed->seqbase, false); + for (int i = data->num_seq - 1; i >= 0; i--) { + Sequence *seq = data->seq_array[i]; + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + } } return changed; } @@ -807,7 +827,8 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even for (int i = 0; i < data->num_seq; i++) { Sequence *seq = data->seq_array[i]; SEQ_add_reload_new_file(bmain, scene, seq, false); - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } MEM_freeN(data->seq_array); @@ -867,9 +888,9 @@ static int sequencer_slip_modal(bContext *C, wmOperator *op, const wmEvent *even void SEQUENCER_OT_slip(struct wmOperatorType *ot) { /* Identifiers. */ - ot->name = "Trim Strips"; + ot->name = "Slip Strips"; ot->idname = "SEQUENCER_OT_slip"; - ot->description = "Trim the contents of the active strip"; + ot->description = "Slip the contents of selected strips"; /* Api callbacks. */ ot->invoke = sequencer_slip_invoke; @@ -1688,16 +1709,24 @@ static int sequencer_delete_exec(bContext *C, wmOperator *UNUSED(op)) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); + ListBase *seqbasep = SEQ_active_seqbase_get(SEQ_editing_get(scene)); SEQ_prefetch_stop(scene); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(seqbasep, scene->r.cfra, 0); + } + + LISTBASE_FOREACH (Sequence *, seq, seqbasep) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SELECT) { - SEQ_edit_flag_for_removal(scene, ed->seqbasep, seq); + SEQ_edit_flag_for_removal(scene, seqbasep, seq); } } - SEQ_edit_remove_flagged_sequences(scene, ed->seqbasep); + SEQ_edit_remove_flagged_sequences(scene, seqbasep); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); DEG_relations_tag_update(bmain); @@ -1760,7 +1789,8 @@ static int sequencer_offset_clear_exec(bContext *C, wmOperator *UNUSED(op)) /* Update lengths, etc. */ seq = ed->seqbasep->first; while (seq) { - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SEQ_time_update_sequence(scene, seqbase, seq); seq = seq->next; } @@ -1803,6 +1833,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); Sequence *seq, *seq_new; Strip *strip_new; @@ -1810,7 +1841,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) int start_ofs, timeline_frame, frame_end; int step = RNA_int_get(op->ptr, "length"); - seq = ed->seqbasep->first; /* Poll checks this is valid. */ + seq = seqbase->first; /* Poll checks this is valid. */ SEQ_prefetch_stop(scene); @@ -1820,7 +1851,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) /* Remove seq so overlap tests don't conflict, * see seq_free_sequence below for the real freeing. */ - BLI_remlink(ed->seqbasep, seq); + BLI_remlink(seqbase, seq); /* TODO: remove f-curve and assign to split image strips. * The old animation system would remove the user of `seq->ipo`. */ @@ -1831,8 +1862,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) /* New seq. */ se = SEQ_render_give_stripelem(seq, timeline_frame); - seq_new = SEQ_sequence_dupli_recursive( - scene, scene, ed->seqbasep, seq, SEQ_DUPE_UNIQUE_NAME); + seq_new = SEQ_sequence_dupli_recursive(scene, scene, seqbase, seq, SEQ_DUPE_UNIQUE_NAME); seq_new->start = start_ofs; seq_new->type = SEQ_TYPE_IMAGE; @@ -1850,12 +1880,12 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) BLI_strncpy(se_new->name, se->name, sizeof(se_new->name)); strip_new->stripdata = se_new; - SEQ_time_update_sequence(scene, seq_new); + SEQ_time_update_sequence(scene, seqbase, seq_new); if (step > 1) { seq_new->flag &= ~SEQ_OVERLAP; - if (SEQ_transform_test_overlap(ed->seqbasep, seq_new)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, seq_new, scene); + if (SEQ_transform_test_overlap(seqbase, seq_new)) { + SEQ_transform_seqbase_shuffle(seqbase, seq_new, scene); } } @@ -1874,7 +1904,7 @@ static int sequencer_separate_images_exec(bContext *C, wmOperator *op) } } - SEQ_sort(SEQ_active_seqbase_get(ed)); + SEQ_sort(seqbase); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -1991,7 +2021,7 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) SEQ_sequence_base_unique_name_recursive(scene, &ed->seqbase, seqm); seqm->start = meta_start_frame; seqm->len = meta_end_frame - meta_start_frame; - SEQ_time_update_sequence(scene, seqm); + SEQ_time_update_sequence(scene, active_seqbase, seqm); SEQ_select_active_set(scene, seqm); if (SEQ_transform_test_overlap(active_seqbase, seqm)) { SEQ_transform_seqbase_shuffle(active_seqbase, seqm, scene); @@ -2164,17 +2194,18 @@ static const EnumPropertyItem prop_side_lr_types[] = { static void swap_sequence(Scene *scene, Sequence *seqa, Sequence *seqb) { + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); int gap = seqb->startdisp - seqa->enddisp; int seq_a_start; int seq_b_start; seq_b_start = (seqb->start - seqb->startdisp) + seqa->startdisp; SEQ_transform_translate_sequence(scene, seqb, seq_b_start - seqb->start); - SEQ_time_update_sequence(scene, seqb); + SEQ_time_update_sequence(scene, seqbase, seqb); seq_a_start = (seqa->start - seqa->startdisp) + seqb->enddisp + gap; SEQ_transform_translate_sequence(scene, seqa, seq_a_start - seqa->start); - SEQ_time_update_sequence(scene, seqa); + SEQ_time_update_sequence(scene, seqbase, seqa); } static Sequence *find_next_prev_sequence(Scene *scene, Sequence *test, int lr, int sel) @@ -2233,6 +2264,7 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); Sequence *active_seq = SEQ_select_active_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); Sequence *seq, *iseq; int side = RNA_enum_get(op->ptr, "side"); @@ -2264,20 +2296,20 @@ static int sequencer_swap_exec(bContext *C, wmOperator *op) } /* XXX: Should be a generic function. */ - for (iseq = scene->ed->seqbasep->first; iseq; iseq = iseq->next) { + for (iseq = seqbase->first; iseq; iseq = iseq->next) { if ((iseq->type & SEQ_TYPE_EFFECT) && (seq_is_parent(iseq, active_seq) || seq_is_parent(iseq, seq))) { - SEQ_time_update_sequence(scene, iseq); + SEQ_time_update_sequence(scene, seqbase, iseq); } } /* Do this in a new loop since both effects need to be calculated first. */ - for (iseq = scene->ed->seqbasep->first; iseq; iseq = iseq->next) { + for (iseq = seqbase->first; iseq; iseq = iseq->next) { if ((iseq->type & SEQ_TYPE_EFFECT) && (seq_is_parent(iseq, active_seq) || seq_is_parent(iseq, seq))) { /* This may now overlap. */ - if (SEQ_transform_test_overlap(ed->seqbasep, iseq)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, iseq, scene); + if (SEQ_transform_test_overlap(seqbase, iseq)) { + SEQ_transform_seqbase_shuffle(seqbase, iseq, scene); } } } @@ -2591,8 +2623,9 @@ static int sequencer_swap_data_exec(bContext *C, wmOperator *op) seq_act->scene_sound = NULL; seq_other->scene_sound = NULL; - SEQ_time_update_sequence(scene, seq_act); - SEQ_time_update_sequence(scene, seq_other); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq_act); + SEQ_time_update_sequence(scene, seqbase, seq_other); if (seq_act->sound) { BKE_sound_add_scene_sound_defaults(scene, seq_act); @@ -2790,7 +2823,6 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); Sequence *seq = SEQ_select_active_get(scene); const bool is_relative_path = RNA_boolean_get(op->ptr, "relative_path"); const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); @@ -2846,10 +2878,11 @@ static int sequencer_change_path_exec(bContext *C, wmOperator *op) * Important not to set seq->len = len; allow the function to handle it. */ SEQ_add_reload_new_file(bmain, scene, seq, true); - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); /* Invalidate cache. */ - SEQ_relations_free_imbuf(scene, &ed->seqbase, false); + SEQ_relations_free_imbuf(scene, seqbase, false); } else if (ELEM(seq->type, SEQ_TYPE_SOUND_RAM, SEQ_TYPE_SOUND_HD)) { bSound *sound = seq->sound; @@ -3320,4 +3353,115 @@ void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot) "Scale fit fit_method"); } +static int sequencer_strip_color_tag_set_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_get(scene); + const short color_tag = RNA_enum_get(op->ptr, "color"); + + LISTBASE_FOREACH (Sequence *, seq, &ed->seqbase) { + if (seq->flag & SELECT) { + seq->color_tag = color_tag; + } + } + + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + return OPERATOR_FINISHED; +} + +static bool sequencer_strip_color_tag_set_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + if (scene == NULL) { + return false; + } + + Editing *ed = SEQ_editing_get(scene); + if (ed == NULL) { + return false; + } + + Sequence *act_seq = ed->act_seq; + return act_seq != NULL; +} + +void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Set Color Tag"; + ot->idname = "SEQUENCER_OT_strip_color_tag_set"; + ot->description = "Set a color tag for the selected strips"; + + /* Api callbacks. */ + ot->exec = sequencer_strip_color_tag_set_exec; + ot->poll = sequencer_strip_color_tag_set_poll; + + /* Flags. */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + RNA_def_enum( + ot->srna, "color", rna_enum_strip_color_items, SEQUENCE_COLOR_NONE, "Color Tag", ""); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Set 2D Cursor Operator + * \{ */ + +static int sequencer_set_2d_cursor_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + SpaceSeq *sseq = CTX_wm_space_seq(C); + + float cursor_pixel[2]; + RNA_float_get_array(op->ptr, "location", cursor_pixel); + + SEQ_image_preview_unit_from_px(scene, cursor_pixel, sseq->cursor); + + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + return OPERATOR_FINISHED; +} + +static int sequencer_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + ARegion *region = CTX_wm_region(C); + float cursor_pixel[2]; + UI_view2d_region_to_view( + ®ion->v2d, event->mval[0], event->mval[1], &cursor_pixel[0], &cursor_pixel[1]); + + RNA_float_set_array(op->ptr, "location", cursor_pixel); + + return sequencer_set_2d_cursor_exec(C, op); +} + +void SEQUENCER_OT_cursor_set(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Set 2D Cursor"; + ot->description = "Set 2D cursor location"; + ot->idname = "SEQUENCER_OT_cursor_set"; + + /* api callbacks */ + ot->exec = sequencer_set_2d_cursor_exec; + ot->invoke = sequencer_set_2d_cursor_invoke; + ot->poll = sequencer_view_preview_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + RNA_def_float_vector(ot->srna, + "location", + 2, + NULL, + -FLT_MAX, + FLT_MAX, + "Location", + "Cursor location in normalized preview coordinates", + -10.0f, + 10.0f); +} + /** \} */ diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 767ac76efe6..5982a0a8993 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -38,8 +38,11 @@ struct SpaceSeq; struct StripElem; struct bContext; struct rctf; +struct View2D; struct wmOperator; +#define OVERLAP_ALPHA 180 + /* sequencer_draw.c */ void draw_timeline_seq(const struct bContext *C, struct ARegion *region); void draw_timeline_seq_display(const struct bContext *C, struct ARegion *region); @@ -51,7 +54,10 @@ void sequencer_draw_preview(const struct bContext *C, int offset, bool draw_overlay, bool draw_backdrop); -void color3ubv_from_seq(struct Scene *curscene, struct Sequence *seq, unsigned char col[3]); +void color3ubv_from_seq(const struct Scene *curscene, + const struct Sequence *seq, + const bool show_strip_color_tag, + uchar r_col[3]); void sequencer_special_update_set(Sequence *seq); float sequence_handle_size_get_clamped(struct Sequence *seq, const float pixelx); @@ -68,6 +74,17 @@ struct ImBuf *sequencer_ibuf_get(struct Main *bmain, int frame_ofs, const char *viewname); +/* sequencer_thumbnails.c */ +void last_displayed_thumbnails_list_free(void *val); +void draw_seq_strip_thumbnail(struct View2D *v2d, + const struct bContext *C, + struct Scene *scene, + struct Sequence *seq, + float y1, + float y2, + float pixelx, + float pixely); + /* sequencer_edit.c */ struct View2D; void seq_rectf(struct Sequence *seq, struct rctf *rectf); @@ -147,6 +164,9 @@ void SEQUENCER_OT_set_range_to_strips(struct wmOperatorType *ot); void SEQUENCER_OT_strip_transform_clear(struct wmOperatorType *ot); void SEQUENCER_OT_strip_transform_fit(struct wmOperatorType *ot); +void SEQUENCER_OT_strip_color_tag_set(struct wmOperatorType *ot); +void SEQUENCER_OT_cursor_set(struct wmOperatorType *ot); + /* sequencer_select.c */ void SEQUENCER_OT_select_all(struct wmOperatorType *ot); void SEQUENCER_OT_select(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_sequencer/sequencer_ops.c b/source/blender/editors/space_sequencer/sequencer_ops.c index 48e6cfcdcd0..2c5f211b0e4 100644 --- a/source/blender/editors/space_sequencer/sequencer_ops.c +++ b/source/blender/editors/space_sequencer/sequencer_ops.c @@ -79,6 +79,9 @@ void sequencer_operatortypes(void) WM_operatortype_append(SEQUENCER_OT_strip_transform_clear); WM_operatortype_append(SEQUENCER_OT_strip_transform_fit); + WM_operatortype_append(SEQUENCER_OT_strip_color_tag_set); + WM_operatortype_append(SEQUENCER_OT_cursor_set); + /* sequencer_select.c */ WM_operatortype_append(SEQUENCER_OT_select_all); WM_operatortype_append(SEQUENCER_OT_select); diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index 80d3e2cbdaa..8a8a24f08ff 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -25,6 +25,8 @@ #include #include +#include "MEM_guardedalloc.h" + #include "BLI_blenlib.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -44,6 +46,7 @@ #include "SEQ_sequencer.h" #include "SEQ_time.h" #include "SEQ_transform.h" +#include "SEQ_utils.h" /* For menu, popup, icons, etc. */ @@ -385,6 +388,20 @@ void recurs_sel_seq(Sequence *seq_meta) } } +static bool seq_point_image_isect(const Scene *scene, const Sequence *seq, float point[2]) +{ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad); + return isect_point_quad_v2( + point, seq_image_quad[0], seq_image_quad[1], seq_image_quad[2], seq_image_quad[3]); +} + +static void sequencer_select_do_updates(bContext *C, Scene *scene) +{ + ED_outliner_select_sync_from_sequence_tag(C); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -399,9 +416,17 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) Editing *ed = SEQ_editing_get(scene); Sequence *seq; + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0); + } + if (action == SEL_TOGGLE) { action = SEL_SELECT; for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SEQ_ALLSEL) { action = SEL_DESELECT; break; @@ -410,6 +435,9 @@ static int sequencer_de_select_all_exec(bContext *C, wmOperator *op) } for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } switch (action) { case SEL_SELECT: seq->flag &= ~(SEQ_LEFTSEL + SEQ_RIGHTSEL); @@ -466,7 +494,15 @@ static int sequencer_select_inverse_exec(bContext *C, wmOperator *UNUSED(op)) Editing *ed = SEQ_editing_get(scene); Sequence *seq; + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(ed->seqbasep, scene->r.cfra, 0); + } + for (seq = ed->seqbasep->first; seq; seq = seq->next) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->flag & SELECT) { seq->flag &= ~SEQ_ALLSEL; } @@ -523,12 +559,6 @@ static void sequencer_select_set_active(Scene *scene, Sequence *seq) recurs_sel_seq(seq); } -static void sequencer_select_do_updates(bContext *C, Scene *scene) -{ - ED_outliner_select_sync_from_sequence_tag(C); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); -} - static void sequencer_select_side_of_frame(const bContext *C, const View2D *v2d, const int mval[2], @@ -626,6 +656,141 @@ static void sequencer_select_linked_handle(const bContext *C, } } +/** Collect sequencer that are candidates for being selected. */ +struct SeqSelect_Link { + struct SeqSelect_Link *next, *prev; + Sequence *seq; + /** Only use for center selection. */ + float center_dist_sq; +}; + +static int seq_sort_for_depth_select(const void *a, const void *b) +{ + const struct SeqSelect_Link *slink_a = a; + const struct SeqSelect_Link *slink_b = b; + + /* Exactly overlapping strips, sort by machine (so the top-most is first). */ + if (slink_a->seq->machine < slink_b->seq->machine) { + return 1; + } + if (slink_a->seq->machine > slink_b->seq->machine) { + return -1; + } + return 0; +} + +static int seq_sort_for_center_select(const void *a, const void *b) +{ + const struct SeqSelect_Link *slink_a = a; + const struct SeqSelect_Link *slink_b = b; + if (slink_a->center_dist_sq > slink_b->center_dist_sq) { + return 1; + } + if (slink_a->center_dist_sq < slink_b->center_dist_sq) { + return -1; + } + + /* Exactly overlapping strips, use depth. */ + return seq_sort_for_depth_select(a, b); +} + +/** + * Check if click happened on image which belongs to strip. + * If multiple strips are found, loop through them in order + * (depth (top-most first) or closest to mouse when `center` is true). + */ +static Sequence *seq_select_seq_from_preview( + const bContext *C, const int mval[2], const bool toggle, const bool extend, const bool center) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SpaceSeq *sseq = CTX_wm_space_seq(C); + View2D *v2d = UI_view2d_fromcontext(C); + + float mouseco_view[2]; + UI_view2d_region_to_view(v2d, mval[0], mval[1], &mouseco_view[0], &mouseco_view[1]); + + /* Always update the coordinates (check extended after). */ + const bool use_cycle = (!WM_cursor_test_motion_and_update(mval) || extend || toggle); + + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); + + /* Allow strips this far from the closest center to be included. + * This allows cycling over center points which are near enough + * to overlapping from the users perspective. */ + const float center_dist_sq_max = square_f(75.0f * U.pixelsize); + const float center_scale_px[2] = { + UI_view2d_scale_get_x(v2d), + UI_view2d_scale_get_y(v2d), + }; + + struct SeqSelect_Link *slink_active = NULL; + Sequence *seq_active = SEQ_select_active_get(scene); + ListBase strips_ordered = {NULL}; + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + bool isect = false; + float center_dist_sq_test = 0.0f; + if (center) { + /* Detect overlapping center points (scaled by the zoom level). */ + float co[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, co); + sub_v2_v2(co, mouseco_view); + mul_v2_v2(co, center_scale_px); + center_dist_sq_test = len_squared_v2(co); + isect = center_dist_sq_test <= center_dist_sq_max; + if (isect) { + /* Use an active strip penalty for "center" selection when cycle is enabled. */ + if (use_cycle && (seq == seq_active) && (seq_active->flag & SELECT)) { + center_dist_sq_test = square_f(sqrtf(center_dist_sq_test) + (3.0f * U.pixelsize)); + } + } + } + else { + isect = seq_point_image_isect(scene, seq, mouseco_view); + } + + if (isect) { + struct SeqSelect_Link *slink = MEM_callocN(sizeof(*slink), __func__); + slink->seq = seq; + slink->center_dist_sq = center_dist_sq_test; + BLI_addtail(&strips_ordered, slink); + + if (seq == seq_active) { + slink_active = slink; + } + } + } + SEQ_collection_free(strips); + + BLI_listbase_sort(&strips_ordered, + center ? seq_sort_for_center_select : seq_sort_for_depth_select); + + struct SeqSelect_Link *slink_select = strips_ordered.first; + Sequence *seq_select = NULL; + if (slink_select != NULL) { + /* Only use special behavior for the active strip when it's selected. */ + if ((center == false) && slink_active && (seq_active->flag & SELECT)) { + if (use_cycle) { + if (slink_active->next) { + slink_select = slink_active->next; + } + } + else { + /* Match object selection behavior: keep the current active item unless cycle is enabled. + * Clicking again in the same location will cycle away from the active object. */ + slink_select = slink_active; + } + } + seq_select = slink_select->seq; + } + + BLI_freelistN(&strips_ordered); + + return seq_select; +} + static bool element_already_selected(const Sequence *seq, const int handle_clicked) { const bool handle_already_selected = ((handle_clicked == SEQ_SIDE_LEFT) && @@ -638,14 +803,15 @@ static bool element_already_selected(const Sequence *seq, const int handle_click static void sequencer_select_strip_impl(const Editing *ed, Sequence *seq, const int handle_clicked, - const bool extend) + const bool extend, + const bool deselect, + const bool toggle) { - /* Deselect strip. */ - if (extend && (seq->flag & SELECT) && ed->act_seq == seq) { + const bool is_active = (ed->act_seq == seq); + + /* Exception for active strip handles. */ + if ((handle_clicked != SEQ_SIDE_NONE) && (seq->flag & SELECT) && is_active && toggle) { switch (handle_clicked) { - case SEQ_SIDE_NONE: - seq->flag &= ~SEQ_ALLSEL; - break; case SEQ_SIDE_LEFT: seq->flag ^= SEQ_LEFTSEL; break; @@ -653,8 +819,28 @@ static void sequencer_select_strip_impl(const Editing *ed, seq->flag ^= SEQ_RIGHTSEL; break; } + return; } - else { /* Select strip. */ + + /* Select strip. */ + /* Match object selection behavior. */ + int action = -1; + if (extend) { + action = 1; + } + else if (deselect) { + action = 0; + } + else { + if (!((seq->flag & SELECT) && is_active)) { + action = 1; + } + else if (toggle) { + action = 0; + } + } + + if (action == 1) { seq->flag |= SELECT; if (handle_clicked == SEQ_SIDE_LEFT) { seq->flag |= SEQ_LEFTSEL; @@ -663,6 +849,9 @@ static void sequencer_select_strip_impl(const Editing *ed, seq->flag |= SEQ_RIGHTSEL; } } + else if (action == 0) { + seq->flag &= ~SEQ_ALLSEL; + } } static int sequencer_select_exec(bContext *C, wmOperator *op) @@ -670,26 +859,45 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) View2D *v2d = UI_view2d_fromcontext(C); Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene); - const bool extend = RNA_boolean_get(op->ptr, "extend"); + ARegion *region = CTX_wm_region(C); if (ed == NULL) { return OPERATOR_CANCELLED; } + if (region->regiontype == RGN_TYPE_PREVIEW) { + const SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return OPERATOR_CANCELLED; + } + } + + bool extend = RNA_boolean_get(op->ptr, "extend"); + bool deselect = RNA_boolean_get(op->ptr, "deselect"); + bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all"); + bool toggle = RNA_boolean_get(op->ptr, "toggle"); + bool center = RNA_boolean_get(op->ptr, "center"); + int mval[2]; mval[0] = RNA_int_get(op->ptr, "mouse_x"); mval[1] = RNA_int_get(op->ptr, "mouse_y"); - int handle_clicked; - Sequence *seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); + int handle_clicked = SEQ_SIDE_NONE; + Sequence *seq = NULL; + if (region->regiontype == RGN_TYPE_PREVIEW) { + seq = seq_select_seq_from_preview(C, mval, toggle, extend, center); + } + else { + seq = find_nearest_seq(scene, v2d, &handle_clicked, mval); + } /* NOTE: `side_of_frame` and `linked_time` functionality is designed to be shared on one keymap, * therefore both properties can be true at the same time. */ if (seq && RNA_boolean_get(op->ptr, "linked_time")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } - sequencer_select_strip_impl(ed, seq, handle_clicked, extend); + sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle); select_linked_time(ed->seqbasep, seq); sequencer_select_do_updates(C, scene); sequencer_select_set_active(scene, seq); @@ -698,7 +906,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* Select left, right or overlapping the current frame. */ if (RNA_boolean_get(op->ptr, "side_of_frame")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_side_of_frame(C, v2d, mval, scene); @@ -708,7 +916,7 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* On Alt selection, select the strip and bordering handles. */ if (seq && RNA_boolean_get(op->ptr, "linked_handle")) { - if (!extend) { + if (!extend && !toggle) { ED_sequencer_deselect_all(scene); } sequencer_select_linked_handle(C, seq, handle_clicked); @@ -721,29 +929,42 @@ static int sequencer_select_exec(bContext *C, wmOperator *op) /* Clicking on already selected element falls on modal operation. * All strips are deselected on mouse button release unless extend mode is used. */ - if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !extend) { + if (seq && element_already_selected(seq, handle_clicked) && wait_to_deselect_others && !toggle) { return OPERATOR_RUNNING_MODAL; } - int ret_value = OPERATOR_CANCELLED; + bool changed = false; - if (!extend) { + /* Deselect everything */ + if (deselect_all || (seq && ((extend == false && deselect == false && toggle == false)))) { ED_sequencer_deselect_all(scene); - ret_value = OPERATOR_FINISHED; + changed = true; } /* Nothing to select, but strips could be deselected. */ if (!seq) { - sequencer_select_do_updates(C, scene); - return ret_value; + if (changed) { + sequencer_select_do_updates(C, scene); + } + return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } /* Do actual selection. */ - sequencer_select_strip_impl(ed, seq, handle_clicked, extend); - ret_value = OPERATOR_FINISHED; + sequencer_select_strip_impl(ed, seq, handle_clicked, extend, deselect, toggle); + sequencer_select_do_updates(C, scene); sequencer_select_set_active(scene, seq); - return ret_value; + return OPERATOR_FINISHED; +} + +static int sequencer_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + const int retval = WM_generic_select_invoke(C, op, event); + ARegion *region = CTX_wm_region(C); + if (region && (region->regiontype == RGN_TYPE_PREVIEW)) { + return WM_operator_flag_only_pass_through_on_press(retval, event); + } + return retval; } void SEQUENCER_OT_select(wmOperatorType *ot) @@ -757,7 +978,7 @@ void SEQUENCER_OT_select(wmOperatorType *ot) /* Api callbacks. */ ot->exec = sequencer_select_exec; - ot->invoke = WM_generic_select_invoke; + ot->invoke = sequencer_select_invoke; ot->modal = WM_generic_select_modal; ot->poll = ED_operator_sequencer_active; @@ -767,7 +988,14 @@ void SEQUENCER_OT_select(wmOperatorType *ot) /* Properties. */ WM_operator_properties_generic_select(ot); - prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection"); + WM_operator_properties_mouse_select(ot); + + prop = RNA_def_boolean( + ot->srna, + "center", + 0, + "Center", + "Use the object center when selecting, in edit mode used to extend object selection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); prop = RNA_def_boolean(ot->srna, @@ -1311,6 +1539,55 @@ void SEQUENCER_OT_select_side(wmOperatorType *ot) /** \name Box Select Operator * \{ */ +static bool seq_box_select_rect_image_isect(const Scene *scene, const Sequence *seq, rctf *rect) +{ + float seq_image_quad[4][2]; + SEQ_image_transform_final_quad_get(scene, seq, seq_image_quad); + float rect_quad[4][2] = {{rect->xmax, rect->ymax}, + {rect->xmax, rect->ymin}, + {rect->xmin, rect->ymin}, + {rect->xmin, rect->ymax}}; + + return seq_point_image_isect(scene, seq, rect_quad[0]) || + seq_point_image_isect(scene, seq, rect_quad[1]) || + seq_point_image_isect(scene, seq, rect_quad[2]) || + seq_point_image_isect(scene, seq, rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[0], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[1], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[2], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]) || + isect_point_quad_v2( + seq_image_quad[3], rect_quad[0], rect_quad[1], rect_quad[2], rect_quad[3]); +} + +static void seq_box_select_seq_from_preview(const bContext *C, rctf *rect, const eSelectOp mode) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SpaceSeq *sseq = CTX_wm_space_seq(C); + + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, sseq->chanshown); + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (!seq_box_select_rect_image_isect(scene, seq, rect)) { + continue; + } + + if (ELEM(mode, SEL_OP_ADD, SEL_OP_SET)) { + seq->flag |= SELECT; + } + else { + BLI_assert(mode == SEL_OP_SUB); + seq->flag &= ~SELECT; + } + } + + SEQ_collection_free(strips); +} + static int sequencer_box_select_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); @@ -1333,6 +1610,13 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(v2d, &rectf, &rectf); + ARegion *region = CTX_wm_region(C); + if (region->regiontype == RGN_TYPE_PREVIEW) { + seq_box_select_seq_from_preview(C, &rectf, sel_op); + sequencer_select_do_updates(C, scene); + return OPERATOR_FINISHED; + } + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { rctf rq; seq_rectf(seq, &rq); @@ -1378,9 +1662,7 @@ static int sequencer_box_select_exec(bContext *C, wmOperator *op) } } - ED_outliner_select_sync_from_sequence_tag(C); - - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER | NA_SELECTED, scene); + sequencer_select_do_updates(C, scene); return OPERATOR_FINISHED; } @@ -1470,7 +1752,8 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = { "EFFECT_LINK", 0, "Effect/Linked", - "Other strips affected by the active one (sharing some time, and below or effect-assigned)"}, + "Other strips affected by the active one (sharing some time, and below or " + "effect-assigned)"}, {SEQ_SELECT_GROUP_OVERLAP, "OVERLAP", 0, "Overlap", "Overlapping time"}, {0, NULL, 0, NULL, NULL}, }; @@ -1484,11 +1767,17 @@ static const EnumPropertyItem sequencer_prop_select_grouped_types[] = { #define SEQ_CHANNEL_CHECK(_seq, _chan) (ELEM((_chan), 0, (_seq)->machine)) -static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type(ListBase *seqbasep, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbasep) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == actseq->type) { seq->flag |= SELECT; changed = true; @@ -1498,12 +1787,18 @@ static bool select_grouped_type(Editing *ed, Sequence *actseq, const int channel return changed; } -static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type_basic(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const bool is_sound = SEQ_IS_SOUND(actseq); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (is_sound ? SEQ_IS_SOUND(seq) : !SEQ_IS_SOUND(seq))) { seq->flag |= SELECT; changed = true; @@ -1513,12 +1808,18 @@ static bool select_grouped_type_basic(Editing *ed, Sequence *actseq, const int c return changed; } -static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_type_effect(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const bool is_effect = SEQ_IS_EFFECT(actseq); - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (is_effect ? SEQ_IS_EFFECT(seq) : !SEQ_IS_EFFECT(seq))) { seq->flag |= SELECT; @@ -1529,7 +1830,10 @@ static bool select_grouped_type_effect(Editing *ed, Sequence *actseq, const int return changed; } -static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_data(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; const char *dir = actseq->strip ? actseq->strip->dir : NULL; @@ -1539,7 +1843,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } if (SEQ_HAS_PATH(actseq) && dir) { - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && SEQ_HAS_PATH(seq) && seq->strip && STREQ(seq->strip->dir, dir)) { seq->flag |= SELECT; @@ -1549,7 +1856,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_SCENE) { Scene *sce = actseq->scene; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_SCENE && seq->scene == sce) { seq->flag |= SELECT; changed = true; @@ -1558,7 +1865,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_MOVIECLIP) { MovieClip *clip = actseq->clip; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MOVIECLIP && seq->clip == clip) { seq->flag |= SELECT; @@ -1568,7 +1875,7 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel } else if (actseq->type == SEQ_TYPE_MASK) { struct Mask *mask = actseq->mask; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (SEQ_CHANNEL_CHECK(seq, channel) && seq->type == SEQ_TYPE_MASK && seq->mask == mask) { seq->flag |= SELECT; changed = true; @@ -1579,7 +1886,10 @@ static bool select_grouped_data(Editing *ed, Sequence *actseq, const int channel return changed; } -static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int channel) +static bool select_grouped_effect(ListBase *seqbase, + const bool is_preview, + Sequence *actseq, + const int channel) { bool changed = false; bool effects[SEQ_TYPE_MAX + 1]; @@ -1588,14 +1898,20 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann effects[i] = false; } - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && (seq->type & SEQ_TYPE_EFFECT) && ELEM(actseq, seq->seq1, seq->seq2, seq->seq3)) { effects[seq->type] = true; } } - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (SEQ_CHANNEL_CHECK(seq, channel) && effects[seq->type]) { if (seq->seq1) { seq->seq1->flag |= SELECT; @@ -1613,11 +1929,14 @@ static bool select_grouped_effect(Editing *ed, Sequence *actseq, const int chann return changed; } -static bool select_grouped_time_overlap(Editing *ed, Sequence *actseq) +static bool select_grouped_time_overlap(ListBase *seqbase, const bool is_preview, Sequence *actseq) { bool changed = false; - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } if (seq->startdisp < actseq->enddisp && seq->enddisp > actseq->startdisp) { seq->flag |= SELECT; changed = true; @@ -1646,12 +1965,11 @@ static void query_lower_channel_strips(Sequence *seq_reference, /* Select all strips within time range and with lower channel of initial selection. Then select * effect chains of these strips. */ -static bool select_grouped_effect_link(Editing *ed, +static bool select_grouped_effect_link(ListBase *seqbase, + const bool is_preview, Sequence *UNUSED(actseq), const int UNUSED(channel)) { - ListBase *seqbase = SEQ_active_seqbase_get(ed); - /* Get collection of strips. */ SeqCollection *collection = SEQ_query_selected_strips(seqbase); const int selected_strip_count = BLI_gset_len(collection->set); @@ -1664,6 +1982,9 @@ static bool select_grouped_effect_link(Editing *ed, /* Actual logic. */ Sequence *seq; SEQ_ITERATOR_FOREACH (seq, collection) { + if (is_preview && (seq->tmp_tag == false)) { + continue; + } seq->flag |= SELECT; } @@ -1679,9 +2000,17 @@ static bool select_grouped_effect_link(Editing *ed, static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); Sequence *actseq = SEQ_select_active_get(scene); + const bool is_preview = sequencer_view_preview_poll(C); + if (is_preview) { + SEQ_query_rendered_strips_to_tag(seqbase, scene->r.cfra, 0); + if (actseq && actseq->tmp_tag == false) { + actseq = NULL; + } + } + if (actseq == NULL) { BKE_report(op->reports, RPT_ERROR, "No active sequence!"); return OPERATOR_CANCELLED; @@ -1694,7 +2023,7 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) bool changed = false; if (!extend) { - LISTBASE_FOREACH (Sequence *, seq, SEQ_active_seqbase_get(ed)) { + LISTBASE_FOREACH (Sequence *, seq, seqbase) { seq->flag &= ~SELECT; changed = true; } @@ -1702,25 +2031,25 @@ static int sequencer_select_grouped_exec(bContext *C, wmOperator *op) switch (type) { case SEQ_SELECT_GROUP_TYPE: - changed |= select_grouped_type(ed, actseq, channel); + changed |= select_grouped_type(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_TYPE_BASIC: - changed |= select_grouped_type_basic(ed, actseq, channel); + changed |= select_grouped_type_basic(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_TYPE_EFFECT: - changed |= select_grouped_type_effect(ed, actseq, channel); + changed |= select_grouped_type_effect(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_DATA: - changed |= select_grouped_data(ed, actseq, channel); + changed |= select_grouped_data(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_EFFECT: - changed |= select_grouped_effect(ed, actseq, channel); + changed |= select_grouped_effect(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_EFFECT_LINK: - changed |= select_grouped_effect_link(ed, actseq, channel); + changed |= select_grouped_effect_link(seqbase, is_preview, actseq, channel); break; case SEQ_SELECT_GROUP_OVERLAP: - changed |= select_grouped_time_overlap(ed, actseq); + changed |= select_grouped_time_overlap(seqbase, is_preview, actseq); break; default: BLI_assert(0); diff --git a/source/blender/editors/space_sequencer/sequencer_thumbnails.c b/source/blender/editors/space_sequencer/sequencer_thumbnails.c new file mode 100644 index 00000000000..9efd78c47ff --- /dev/null +++ b/source/blender/editors/space_sequencer/sequencer_thumbnails.c @@ -0,0 +1,593 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup spseq + */ + +#include "BLI_blenlib.h" +#include "BLI_ghash.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_scene.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "ED_screen.h" + +#include "BIF_glutil.h" + +#include "SEQ_relations.h" +#include "SEQ_render.h" +#include "SEQ_sequencer.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "MEM_guardedalloc.h" + +/* Own include. */ +#include "sequencer_intern.h" + +typedef struct ThumbnailDrawJob { + SeqRenderData context; + GHash *sequences_ghash; + Scene *scene; + rctf *view_area; + float pixelx; + float pixely; +} ThumbnailDrawJob; + +typedef struct ThumbDataItem { + Sequence *seq_dupli; + Scene *scene; +} ThumbDataItem; + +static void thumbnail_hash_data_free(void *val) +{ + ThumbDataItem *item = val; + SEQ_sequence_free(item->scene, item->seq_dupli, 0); + MEM_freeN(val); +} + +static void thumbnail_freejob(void *data) +{ + ThumbnailDrawJob *tj = data; + BLI_ghash_free(tj->sequences_ghash, NULL, thumbnail_hash_data_free); + MEM_freeN(tj->view_area); + MEM_freeN(tj); +} + +static void thumbnail_endjob(void *data) +{ + ThumbnailDrawJob *tj = data; + WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, tj->scene); +} + +static bool check_seq_need_thumbnails(Sequence *seq, rctf *view_area) +{ + if (!ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { + return false; + } + if (min_ii(seq->startdisp, seq->start) > view_area->xmax) { + return false; + } + if (max_ii(seq->enddisp, seq->start + seq->len) < view_area->xmin) { + return false; + } + if (seq->machine + 1.0f < view_area->ymin) { + return false; + } + if (seq->machine > view_area->ymax) { + return false; + } + + return true; +} + +static void seq_get_thumb_image_dimensions(Sequence *seq, + float pixelx, + float pixely, + float *r_thumb_width, + float *r_thumb_height, + float *r_image_width, + float *r_image_height) +{ + float image_width = seq->strip->stripdata->orig_width; + float image_height = seq->strip->stripdata->orig_height; + + /* Fix the dimensions to be max SEQ_RENDER_THUMB_SIZE (256) for x or y. */ + float aspect_ratio = (float)image_width / image_height; + if (image_width > image_height) { + image_width = SEQ_RENDER_THUMB_SIZE; + image_height = round_fl_to_int(image_width / aspect_ratio); + } + else { + image_height = SEQ_RENDER_THUMB_SIZE; + image_width = round_fl_to_int(image_height * aspect_ratio); + } + + /* Calculate thumb dimensions. */ + float thumb_height = (SEQ_STRIP_OFSTOP - SEQ_STRIP_OFSBOTTOM) - (20 * U.dpi_fac * pixely); + aspect_ratio = ((float)image_width) / image_height; + float thumb_h_px = thumb_height / pixely; + float thumb_width = aspect_ratio * thumb_h_px * pixelx; + + if (r_thumb_height == NULL) { + *r_thumb_width = thumb_width; + return; + } + + *r_thumb_height = thumb_height; + *r_image_width = image_width; + *r_image_height = image_height; + *r_thumb_width = thumb_width; +} + +static float seq_thumbnail_get_start_frame(Sequence *seq, float frame_step, rctf *view_area) +{ + if (seq->start > view_area->xmin && seq->start < view_area->xmax) { + return seq->start; + } + + /* Drawing and caching both check to see if strip is in view area or not before calling this + * function so assuming strip/part of strip in view. */ + + int no_invisible_thumbs = (view_area->xmin - seq->start) / frame_step; + return ((no_invisible_thumbs - 1) * frame_step) + seq->start; +} + +static void thumbnail_start_job(void *data, + short *stop, + short *UNUSED(do_update), + float *UNUSED(progress)) +{ + ThumbnailDrawJob *tj = data; + float start_frame, frame_step; + + GHashIterator gh_iter; + + /* First pass: render visible images. */ + BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash); + while (!BLI_ghashIterator_done(&gh_iter) & !*stop) { + Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter); + ThumbDataItem *val = BLI_ghash_lookup(tj->sequences_ghash, seq_orig); + + if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { + seq_get_thumb_image_dimensions( + val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, NULL, NULL, NULL); + start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); + SEQ_render_thumbnails( + &tj->context, val->seq_dupli, seq_orig, start_frame, frame_step, tj->view_area, stop); + SEQ_relations_sequence_free_anim(val->seq_dupli); + } + BLI_ghashIterator_step(&gh_iter); + } + + /* Second pass: render "guaranteed" set of images. */ + BLI_ghashIterator_init(&gh_iter, tj->sequences_ghash); + while (!BLI_ghashIterator_done(&gh_iter) & !*stop) { + Sequence *seq_orig = BLI_ghashIterator_getKey(&gh_iter); + ThumbDataItem *val = BLI_ghash_lookup(tj->sequences_ghash, seq_orig); + + if (check_seq_need_thumbnails(seq_orig, tj->view_area)) { + seq_get_thumb_image_dimensions( + val->seq_dupli, tj->pixelx, tj->pixely, &frame_step, NULL, NULL, NULL); + start_frame = seq_thumbnail_get_start_frame(seq_orig, frame_step, tj->view_area); + SEQ_render_thumbnails_base_set(&tj->context, val->seq_dupli, seq_orig, tj->view_area, stop); + SEQ_relations_sequence_free_anim(val->seq_dupli); + } + BLI_ghashIterator_step(&gh_iter); + } +} + +static SeqRenderData sequencer_thumbnail_context_init(const bContext *C) +{ + struct Main *bmain = CTX_data_main(C); + struct Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + Scene *scene = CTX_data_scene(C); + SpaceSeq *sseq = CTX_wm_space_seq(C); + SeqRenderData context = {0}; + + /* Taking rectx and recty as 0 as dimensions not known here, and context is used to calculate + * hash key but not necessary as other variables of SeqRenderData are unique enough. */ + SEQ_render_new_render_data(bmain, depsgraph, scene, 0, 0, sseq->render_size, false, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, STEREO_LEFT_NAME); + context.use_proxies = false; + + return context; +} + +static GHash *sequencer_thumbnail_ghash_init(const bContext *C, View2D *v2d, Editing *ed) +{ + Scene *scene = CTX_data_scene(C); + + /* Set the data for thumbnail caching job. */ + GHash *thumb_data_hash = BLI_ghash_ptr_new("seq_duplicates_and_origs"); + + LISTBASE_FOREACH (Sequence *, seq, ed->seqbasep) { + ThumbDataItem *val_need_update = BLI_ghash_lookup(thumb_data_hash, seq); + if (val_need_update == NULL && check_seq_need_thumbnails(seq, &v2d->cur)) { + ThumbDataItem *val = MEM_callocN(sizeof(ThumbDataItem), "Thumbnail Hash Values"); + val->seq_dupli = SEQ_sequence_dupli_recursive(scene, scene, NULL, seq, 0); + val->scene = scene; + BLI_ghash_insert(thumb_data_hash, seq, val); + } + else { + if (val_need_update != NULL) { + val_need_update->seq_dupli->start = seq->start; + val_need_update->seq_dupli->startdisp = seq->startdisp; + } + } + } + + return thumb_data_hash; +} + +static void sequencer_thumbnail_init_job(const bContext *C, View2D *v2d, Editing *ed) +{ + wmJob *wm_job; + ThumbnailDrawJob *tj = NULL; + ScrArea *area = CTX_wm_area(C); + wm_job = WM_jobs_get(CTX_wm_manager(C), + CTX_wm_window(C), + CTX_data_scene(C), + "Draw Thumbnails", + 0, + WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL); + + /* Get the thumbnail job if it exists. */ + tj = WM_jobs_customdata_get(wm_job); + if (!tj) { + tj = MEM_callocN(sizeof(ThumbnailDrawJob), "Thumbnail cache job"); + + /* Duplicate value of v2d->cur and v2d->tot to have module separation. */ + rctf *view_area = MEM_callocN(sizeof(struct rctf), "viewport area"); + view_area->xmax = v2d->cur.xmax; + view_area->xmin = v2d->cur.xmin; + view_area->ymax = v2d->cur.ymax; + view_area->ymin = v2d->cur.ymin; + + tj->scene = CTX_data_scene(C); + tj->view_area = view_area; + tj->context = sequencer_thumbnail_context_init(C); + tj->sequences_ghash = sequencer_thumbnail_ghash_init(C, v2d, ed); + tj->pixelx = BLI_rctf_size_x(&v2d->cur) / BLI_rcti_size_x(&v2d->mask); + tj->pixely = BLI_rctf_size_y(&v2d->cur) / BLI_rcti_size_y(&v2d->mask); + WM_jobs_customdata_set(wm_job, tj, thumbnail_freejob); + WM_jobs_timer(wm_job, 0.1, NC_SCENE | ND_SEQUENCER, NC_SCENE | ND_SEQUENCER); + WM_jobs_callbacks(wm_job, thumbnail_start_job, NULL, NULL, thumbnail_endjob); + } + + if (!WM_jobs_is_running(wm_job)) { + G.is_break = false; + WM_jobs_start(CTX_wm_manager(C), wm_job); + } + else { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); + } + + ED_area_tag_redraw(area); +} + +static bool sequencer_thumbnail_v2d_is_navigating(const bContext *C) +{ + ARegion *region = CTX_wm_region(C); + View2D *v2d = ®ion->v2d; + return (v2d->flag & V2D_IS_NAVIGATING) != 0; +} + +static void sequencer_thumbnail_start_job_if_necessary(const bContext *C, + Editing *ed, + View2D *v2d, + bool thumbnail_is_missing) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + + if (sequencer_thumbnail_v2d_is_navigating(C)) { + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, NULL); + return; + } + + /* During rendering, cache is wiped, it doesn't make sense to render thumbnails. */ + if (G.is_rendering) { + return; + } + + /* Job start requested, but over area which has been processed. Unless `thumbnail_is_missing` is + * true, ignore this request as all images are in view. */ + if (v2d->cur.xmax == sseq->runtime.last_thumbnail_area.xmax && + v2d->cur.ymax == sseq->runtime.last_thumbnail_area.ymax && !thumbnail_is_missing) { + return; + } + + /* Stop the job first as view has changed. Pointless to continue old job. */ + if (v2d->cur.xmax != sseq->runtime.last_thumbnail_area.xmax || + v2d->cur.ymax != sseq->runtime.last_thumbnail_area.ymax) { + WM_jobs_stop(CTX_wm_manager(C), NULL, thumbnail_start_job); + } + + sequencer_thumbnail_init_job(C, v2d, ed); + sseq->runtime.last_thumbnail_area = v2d->cur; +} + +void last_displayed_thumbnails_list_free(void *val) +{ + BLI_gset_free(val, NULL); +} + +static GSet *last_displayed_thumbnails_list_ensure(const bContext *C, Sequence *seq) +{ + SpaceSeq *sseq = CTX_wm_space_seq(C); + if (sseq->runtime.last_displayed_thumbnails == NULL) { + sseq->runtime.last_displayed_thumbnails = BLI_ghash_ptr_new(__func__); + } + + GSet *displayed_thumbnails = BLI_ghash_lookup(sseq->runtime.last_displayed_thumbnails, seq); + if (displayed_thumbnails == NULL) { + displayed_thumbnails = BLI_gset_int_new(__func__); + BLI_ghash_insert(sseq->runtime.last_displayed_thumbnails, seq, displayed_thumbnails); + } + + return displayed_thumbnails; +} + +static void last_displayed_thumbnails_list_cleanup(GSet *previously_displayed, + float range_start, + float range_end) +{ + GSetIterator gset_iter; + BLI_gsetIterator_init(&gset_iter, previously_displayed); + while (!BLI_gsetIterator_done(&gset_iter)) { + int frame = (float)POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); + BLI_gsetIterator_step(&gset_iter); + + if (frame > range_start && frame < range_end) { + BLI_gset_remove(previously_displayed, POINTER_FROM_INT(frame), NULL); + } + } +} + +static int sequencer_thumbnail_closest_previous_frame_get(int timeline_frame, + GSet *previously_displayed) +{ + int best_diff = INT_MAX; + int best_frame = timeline_frame; + + /* Previously displayed thumbnails. */ + GSetIterator gset_iter; + BLI_gsetIterator_init(&gset_iter, previously_displayed); + while (!BLI_gsetIterator_done(&gset_iter)) { + int frame = POINTER_AS_INT(BLI_gsetIterator_getKey(&gset_iter)); + int diff = abs(frame - timeline_frame); + if (diff < best_diff) { + best_diff = diff; + best_frame = frame; + } + BLI_gsetIterator_step(&gset_iter); + } + return best_frame; +} + +static int sequencer_thumbnail_closest_guaranteed_frame_get(Sequence *seq, int timeline_frame) +{ + if (timeline_frame <= seq->startdisp) { + return seq->startdisp; + } + + /* Set of "guaranteed" thumbnails. */ + const int frame_index = timeline_frame - seq->startdisp; + const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq); + const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * frame_step; + const int nearest_guaranted_absolute_frame = relative_base_frame + seq->startdisp; + return nearest_guaranted_absolute_frame; +} + +static ImBuf *sequencer_thumbnail_closest_from_memory(const SeqRenderData *context, + Sequence *seq, + int timeline_frame, + GSet *previously_displayed, + rcti *crop, + bool clipped) +{ + int frame_previous = sequencer_thumbnail_closest_previous_frame_get(timeline_frame, + previously_displayed); + ImBuf *ibuf_previous = SEQ_get_thumbnail(context, seq, frame_previous, crop, clipped); + + int frame_guaranteed = sequencer_thumbnail_closest_guaranteed_frame_get(seq, timeline_frame); + ImBuf *ibuf_guaranteed = SEQ_get_thumbnail(context, seq, frame_guaranteed, crop, clipped); + + ImBuf *closest_in_memory = NULL; + + if (ibuf_previous && ibuf_guaranteed) { + if (abs(frame_previous - timeline_frame) < abs(frame_guaranteed - timeline_frame)) { + IMB_freeImBuf(ibuf_guaranteed); + closest_in_memory = ibuf_previous; + } + else { + IMB_freeImBuf(ibuf_previous); + closest_in_memory = ibuf_guaranteed; + } + } + + if (ibuf_previous == NULL) { + closest_in_memory = ibuf_guaranteed; + } + + if (ibuf_guaranteed == NULL) { + closest_in_memory = ibuf_previous; + } + + return closest_in_memory; +} + +void draw_seq_strip_thumbnail(View2D *v2d, + const bContext *C, + Scene *scene, + Sequence *seq, + float y1, + float y2, + float pixelx, + float pixely) +{ + bool clipped = false; + float image_height, image_width, thumb_width, thumb_height; + rcti crop; + + /* If width of the strip too small ignore drawing thumbnails. */ + if ((y2 - y1) / pixely <= 40 * U.dpi_fac) { + return; + } + + SeqRenderData context = sequencer_thumbnail_context_init(C); + + if ((seq->flag & SEQ_FLAG_SKIP_THUMBNAILS) != 0) { + return; + } + + seq_get_thumb_image_dimensions( + seq, pixelx, pixely, &thumb_width, &thumb_height, &image_width, &image_height); + + float thumb_y_end = y1 + thumb_height - pixely; + + float cut_off = 0; + float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + if (seq->type == SEQ_TYPE_IMAGE) { + upper_thumb_bound = seq->enddisp; + } + + float thumb_x_start = seq_thumbnail_get_start_frame(seq, thumb_width, &v2d->cur); + float thumb_x_end; + + while (thumb_x_start + thumb_width < v2d->cur.xmin) { + thumb_x_start += thumb_width; + } + + /* Ignore thumbs to the left of strip. */ + while (thumb_x_start + thumb_width < seq->startdisp) { + thumb_x_start += thumb_width; + } + + GSet *last_displayed_thumbnails = last_displayed_thumbnails_list_ensure(C, seq); + /* Cleanup thumbnail list outside of rendered range, which is cleaned up one by one to prevent + * flickering after zooming. */ + if (!sequencer_thumbnail_v2d_is_navigating(C)) { + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, -FLT_MAX, thumb_x_start); + } + + /* Start drawing. */ + while (thumb_x_start < upper_thumb_bound) { + thumb_x_end = thumb_x_start + thumb_width; + clipped = false; + + /* Checks to make sure that thumbs are loaded only when in view and within the confines of the + * strip. Some may not be required but better to have conditions for safety as x1 here is + * point to start caching from and not drawing. */ + if (thumb_x_start > v2d->cur.xmax) { + break; + } + + /* Set the clipping bound to show the left handle moving over thumbs and not shift thumbs. */ + if (IN_RANGE_INCL(seq->startdisp, thumb_x_start, thumb_x_end)) { + cut_off = seq->startdisp - thumb_x_start; + clipped = true; + } + + /* Clip if full thumbnail cannot be displayed. */ + if (thumb_x_end > (upper_thumb_bound)) { + thumb_x_end = upper_thumb_bound; + clipped = true; + if (thumb_x_end - thumb_x_start < 1) { + break; + } + } + + float zoom_x = thumb_width / image_width; + float zoom_y = thumb_height / image_height; + + float cropx_min = (cut_off / pixelx) / (zoom_y / pixely); + float cropx_max = ((thumb_x_end - thumb_x_start) / pixelx) / (zoom_y / pixely); + if (cropx_max == (thumb_x_end - thumb_x_start)) { + cropx_max = cropx_max + 1; + } + BLI_rcti_init(&crop, (int)(cropx_min), (int)cropx_max, 0, (int)(image_height)-1); + + int timeline_frame = round_fl_to_int(thumb_x_start); + + /* Get the image. */ + ImBuf *ibuf = SEQ_get_thumbnail(&context, seq, timeline_frame, &crop, clipped); + + if (!ibuf) { + sequencer_thumbnail_start_job_if_necessary(C, scene->ed, v2d, true); + + ibuf = sequencer_thumbnail_closest_from_memory( + &context, seq, timeline_frame, last_displayed_thumbnails, &crop, clipped); + } + /* Store recently rendered frames, so they can be reused when zooming. */ + else if (!sequencer_thumbnail_v2d_is_navigating(C)) { + /* Clear images in frame range occupied by new thumbnail. */ + last_displayed_thumbnails_list_cleanup( + last_displayed_thumbnails, thumb_x_start, thumb_x_end); + /* Insert new thumbnail frame to list. */ + BLI_gset_add(last_displayed_thumbnails, POINTER_FROM_INT(timeline_frame)); + } + + /* If there is no image still, abort. */ + if (!ibuf) { + break; + } + + /* Transparency on overlap. */ + if (seq->flag & SEQ_OVERLAP) { + GPU_blend(GPU_BLEND_ALPHA); + if (ibuf->rect) { + unsigned char *buf = (unsigned char *)ibuf->rect; + for (int pixel = ibuf->x * ibuf->y; pixel--; buf += 4) { + buf[3] = OVERLAP_ALPHA; + } + } + else if (ibuf->rect_float) { + float *buf = (float *)ibuf->rect_float; + for (int pixel = ibuf->x * ibuf->y; pixel--; buf += ibuf->channels) { + buf[3] = (OVERLAP_ALPHA / 255.0f); + } + } + } + + ED_draw_imbuf_ctx_clipping(C, + ibuf, + thumb_x_start + cut_off, + y1, + true, + thumb_x_start + cut_off, + y1, + thumb_x_end, + thumb_y_end, + zoom_x, + zoom_y); + IMB_freeImBuf(ibuf); + GPU_blend(GPU_BLEND_NONE); + cut_off = 0; + thumb_x_start += thumb_width; + } + last_displayed_thumbnails_list_cleanup(last_displayed_thumbnails, thumb_x_start, FLT_MAX); +} diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 981f793c896..2d2e7de7135 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -92,7 +92,14 @@ static int sequencer_view_all_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); const Editing *ed = SEQ_editing_get(scene); - SEQ_timeline_boundbox(scene, SEQ_active_seqbase_get(ed), &box); + SEQ_timeline_init_boundbox(scene, &box); + MetaStack *ms = SEQ_meta_stack_active_get(ed); + /* Use meta strip range instead of scene. */ + if (ms != NULL) { + box.xmin = ms->disp_range[0] - 1; + box.xmax = ms->disp_range[1] + 1; + } + SEQ_timeline_expand_boundbox(SEQ_active_seqbase_get(ed), &box); UI_view2d_smooth_view(C, region, &box, smooth_viewtx); return OPERATOR_FINISHED; } @@ -380,7 +387,7 @@ static int view_ghost_border_exec(bContext *C, wmOperator *op) CLAMP(rect.xmax, 0.0f, 1.0f); CLAMP(rect.ymax, 0.0f, 1.0f); - scene->ed->over_border = rect; + scene->ed->overlay_frame_rect = rect; WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); diff --git a/source/blender/editors/space_sequencer/space_sequencer.c b/source/blender/editors/space_sequencer/space_sequencer.c index 2a6e49edfb6..ed8305cd311 100644 --- a/source/blender/editors/space_sequencer/space_sequencer.c +++ b/source/blender/editors/space_sequencer/space_sequencer.c @@ -32,6 +32,7 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_ghash.h" #include "BLI_utildefines.h" #include "BKE_context.h" @@ -40,8 +41,11 @@ #include "BKE_screen.h" #include "BKE_sequencer_offscreen.h" +#include "GPU_state.h" + #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_transform.h" #include "ED_view3d.h" #include "ED_view3d_offscreen.h" /* Only for sequencer view3d drawing callback. */ @@ -51,6 +55,7 @@ #include "RNA_access.h" +#include "SEQ_transform.h" #include "SEQ_utils.h" #include "UI_interface.h" @@ -59,6 +64,9 @@ #include "IMB_imbuf.h" +/* Only for cursor drawing. */ +#include "DRW_engine.h" + /* Own include. */ #include "sequencer_intern.h" @@ -98,9 +106,21 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce sseq->chanshown = 0; sseq->view = SEQ_VIEW_SEQUENCE; sseq->mainb = SEQ_DRAW_IMG_IMBUF; - sseq->flag = SEQ_SHOW_GPENCIL | SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_SHOW_FCURVES | - SEQ_ZOOM_TO_FIT | SEQ_SHOW_STRIP_OVERLAY | SEQ_SHOW_STRIP_NAME | - SEQ_SHOW_STRIP_SOURCE | SEQ_SHOW_STRIP_DURATION | SEQ_SHOW_GRID; + sseq->flag = SEQ_USE_ALPHA | SEQ_SHOW_MARKERS | SEQ_ZOOM_TO_FIT | SEQ_SHOW_OVERLAY; + sseq->preview_overlay.flag = SEQ_PREVIEW_SHOW_GPENCIL | SEQ_PREVIEW_SHOW_OUTLINE_SELECTED; + sseq->timeline_overlay.flag = SEQ_TIMELINE_SHOW_STRIP_NAME | SEQ_TIMELINE_SHOW_STRIP_SOURCE | + SEQ_TIMELINE_SHOW_STRIP_DURATION | SEQ_TIMELINE_SHOW_GRID | + SEQ_TIMELINE_SHOW_FCURVES | SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG; + + BLI_rctf_init(&sseq->runtime.last_thumbnail_area, 0.0f, 0.0f, 0.0f, 0.0f); + sseq->runtime.last_displayed_thumbnails = NULL; + + /* Header. */ + region = MEM_callocN(sizeof(ARegion), "header for sequencer"); + + BLI_addtail(&sseq->regionbase, region); + region->regiontype = RGN_TYPE_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; /* Tool header. */ region = MEM_callocN(sizeof(ARegion), "tool header for sequencer"); @@ -110,13 +130,6 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; - /* Header. */ - region = MEM_callocN(sizeof(ARegion), "header for sequencer"); - - BLI_addtail(&sseq->regionbase, region); - region->regiontype = RGN_TYPE_HEADER; - region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; - /* Buttons/list view. */ region = MEM_callocN(sizeof(ARegion), "buttons for sequencer"); @@ -171,7 +184,7 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce region->v2d.cur = region->v2d.tot; region->v2d.min[0] = 10.0f; - region->v2d.min[1] = 0.5f; + region->v2d.min[1] = 4.0f; region->v2d.max[0] = MAXFRAMEF; region->v2d.max[1] = MAXSEQ; @@ -185,6 +198,8 @@ static SpaceLink *sequencer_create(const ScrArea *UNUSED(area), const Scene *sce region->v2d.keeptot = 0; region->v2d.align = V2D_ALIGN_NO_NEG_Y; + sseq->runtime.last_displayed_thumbnails = NULL; + return (SpaceLink *)sseq; } @@ -215,6 +230,12 @@ static void sequencer_free(SpaceLink *sl) if (scopes->histogram_ibuf) { IMB_freeImBuf(scopes->histogram_ibuf); } + + if (sseq->runtime.last_displayed_thumbnails) { + BLI_ghash_free( + sseq->runtime.last_displayed_thumbnails, NULL, last_displayed_thumbnails_list_free); + sseq->runtime.last_displayed_thumbnails = NULL; + } } /* Spacetype init callback. */ @@ -329,6 +350,7 @@ static SpaceLink *sequencer_duplicate(SpaceLink *sl) /* XXX sseq->gpd = gpencil_data_duplicate(sseq->gpd, false); */ memset(&sseqn->scopes, 0, sizeof(sseqn->scopes)); + memset(&sseqn->runtime, 0, sizeof(sseqn->runtime)); return (SpaceLink *)sseqn; } @@ -480,11 +502,72 @@ static void SEQUENCER_GGT_navigate(wmGizmoGroupType *gzgt) VIEW2D_GGT_navigate_impl(gzgt, "SEQUENCER_GGT_navigate"); } +static void SEQUENCER_GGT_gizmo2d(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_translate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Translate Gizmo"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_translate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_resize(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo Resize"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_resize"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_resize_callbacks_set(gzgt); +} + +static void SEQUENCER_GGT_gizmo2d_rotate(wmGizmoGroupType *gzgt) +{ + gzgt->name = "Sequencer Transform Gizmo Resize"; + gzgt->idname = "SEQUENCER_GGT_gizmo2d_rotate"; + + gzgt->flag |= (WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | + WM_GIZMOGROUPTYPE_DELAY_REFRESH_FOR_TWEAK); + + gzgt->gzmap_params.spaceid = SPACE_SEQ; + gzgt->gzmap_params.regionid = RGN_TYPE_PREVIEW; + + ED_widgetgroup_gizmo2d_rotate_callbacks_set(gzgt); +} + static void sequencer_gizmos(void) { wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure( &(const struct wmGizmoMapType_Params){SPACE_SEQ, RGN_TYPE_PREVIEW}); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_translate); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_resize); + WM_gizmogrouptype_append(SEQUENCER_GGT_gizmo2d_rotate); + WM_gizmogrouptype_append_and_link(gzmap_type, SEQUENCER_GGT_navigate); } @@ -692,41 +775,67 @@ static void sequencer_preview_region_view2d_changed(const bContext *C, ARegion * sseq->flag &= ~SEQ_ZOOM_TO_FIT; } +static bool is_cursor_visible(const SpaceSeq *sseq) +{ + if (G.moving & G_TRANSFORM_CURSOR) { + return true; + } + + if ((sseq->flag & SEQ_SHOW_OVERLAY) && + (sseq->preview_overlay.flag & SEQ_PREVIEW_SHOW_2D_CURSOR) != 0) { + return true; + } + return false; +} + static void sequencer_preview_region_draw(const bContext *C, ARegion *region) { ScrArea *area = CTX_wm_area(C); SpaceSeq *sseq = area->spacedata.first; Scene *scene = CTX_data_scene(C); wmWindowManager *wm = CTX_wm_manager(C); - const bool draw_overlay = (scene->ed && (scene->ed->over_flag & SEQ_EDIT_OVERLAY_SHOW) && - (sseq->flag & SEQ_SHOW_STRIP_OVERLAY)); + const bool draw_overlay = sseq->flag & SEQ_SHOW_OVERLAY; + const bool draw_frame_overlay = (scene->ed && + (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_SHOW) && + draw_overlay); + const bool is_playing = ED_screen_animation_playing(wm); - /* XXX temp fix for wrong setting in sseq->mainb */ - if (sseq->mainb == SEQ_DRAW_SEQUENCE) { - sseq->mainb = SEQ_DRAW_IMG_IMBUF; - } - - if (!draw_overlay || sseq->overlay_type != SEQ_DRAW_OVERLAY_REFERENCE) { + if (!(draw_frame_overlay && (sseq->overlay_frame_type == SEQ_OVERLAY_FRAME_TYPE_REFERENCE))) { sequencer_draw_preview(C, scene, region, sseq, scene->r.cfra, 0, false, false); } - if (draw_overlay && sseq->overlay_type != SEQ_DRAW_OVERLAY_CURRENT) { + if (draw_frame_overlay && sseq->overlay_frame_type != SEQ_OVERLAY_FRAME_TYPE_CURRENT) { int over_cfra; - if (scene->ed->over_flag & SEQ_EDIT_OVERLAY_ABS) { - over_cfra = scene->ed->over_cfra; + if (scene->ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { + over_cfra = scene->ed->overlay_frame_abs; } else { - over_cfra = scene->r.cfra + scene->ed->over_ofs; + over_cfra = scene->r.cfra + scene->ed->overlay_frame_ofs; } - if (over_cfra != scene->r.cfra || sseq->overlay_type != SEQ_DRAW_OVERLAY_RECT) { + if ((over_cfra != scene->r.cfra) || + (sseq->overlay_frame_type != SEQ_OVERLAY_FRAME_TYPE_RECT)) { sequencer_draw_preview( C, scene, region, sseq, scene->r.cfra, over_cfra - scene->r.cfra, true, false); } } - WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); + /* No need to show the cursor for scopes. */ + if ((is_playing == false) && (sseq->mainb == SEQ_DRAW_IMG_IMBUF) && is_cursor_visible(sseq)) { + GPU_color_mask(true, true, true, true); + GPU_depth_mask(false); + GPU_depth_test(GPU_DEPTH_NONE); + + float cursor_pixel[2]; + SEQ_image_preview_unit_to_px(scene, sseq->cursor, cursor_pixel); + + DRW_draw_cursor_2d_ex(region, cursor_pixel); + } + + if ((is_playing == false) && (sseq->gizmo_flag & SEQ_GIZMO_HIDE) == 0) { + WM_gizmomap_draw(region->gizmo_map, C, WM_GIZMOMAP_DRAWSTEP_2D); + } if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) { const rcti *rect = ED_region_visible_rect(region); @@ -741,6 +850,8 @@ static void sequencer_preview_region_listener(const wmRegionListenerParams *para ARegion *region = params->region; wmNotifier *wmn = params->notifier; + WM_gizmomap_tag_refresh(region->gizmo_map); + /* Context changes. */ switch (wmn->category) { case NC_GPENCIL: diff --git a/source/blender/editors/space_spreadsheet/CMakeLists.txt b/source/blender/editors/space_spreadsheet/CMakeLists.txt index e903feeec1b..192b80881ee 100644 --- a/source/blender/editors/space_spreadsheet/CMakeLists.txt +++ b/source/blender/editors/space_spreadsheet/CMakeLists.txt @@ -35,6 +35,7 @@ set(INC set(SRC space_spreadsheet.cc + spreadsheet_cache.cc spreadsheet_column.cc spreadsheet_context.cc spreadsheet_data_source.cc @@ -47,6 +48,7 @@ set(SRC spreadsheet_row_filter.cc spreadsheet_row_filter_ui.cc + spreadsheet_cache.hh spreadsheet_cell_value.hh spreadsheet_column.hh spreadsheet_column_values.hh @@ -65,4 +67,14 @@ set(SRC set(LIB ) +if(WITH_OPENVDB) + list(APPEND INC_SYS + ${OPENVDB_INCLUDE_DIRS} + ) + list(APPEND LIB + ${OPENVDB_LIBRARIES} + ) + add_definitions(-DWITH_OPENVDB ${OPENVDB_DEFINITIONS}) +endif() + blender_add_lib(bf_editor_space_spreadsheet "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc index a82648aeee0..d54af7ffe2c 100644 --- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc +++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc @@ -112,7 +112,7 @@ static void spreadsheet_free(SpaceLink *sl) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl; - MEM_SAFE_FREE(sspreadsheet->runtime); + delete sspreadsheet->runtime; LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) { spreadsheet_row_filter_free(row_filter); @@ -129,8 +129,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first; if (sspreadsheet->runtime == nullptr) { - sspreadsheet->runtime = (SpaceSpreadsheet_Runtime *)MEM_callocN( - sizeof(SpaceSpreadsheet_Runtime), __func__); + sspreadsheet->runtime = new SpaceSpreadsheet_Runtime(); } } @@ -138,7 +137,12 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl) { const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl; SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old); - sspreadsheet_new->runtime = (SpaceSpreadsheet_Runtime *)MEM_dupallocN(sspreadsheet_old->runtime); + if (sspreadsheet_old->runtime) { + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime); + } + else { + sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(); + } BLI_listbase_clear(&sspreadsheet_new->row_filters); LISTBASE_FOREACH (const SpreadsheetRowFilter *, src_filter, &sspreadsheet_old->row_filters) { @@ -294,16 +298,39 @@ static std::unique_ptr get_data_source(const bContext *C) return {}; } -static float get_column_width(const ColumnValues &values) +static float get_default_column_width(const ColumnValues &values) { - if (values.default_width > 0) { + if (values.default_width > 0.0f) { return values.default_width; } + static const float float_width = 3; + switch (values.type()) { + case SPREADSHEET_VALUE_TYPE_BOOL: + return 2.0f; + case SPREADSHEET_VALUE_TYPE_INT32: + return float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT: + return float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT2: + return 2.0f * float_width; + case SPREADSHEET_VALUE_TYPE_FLOAT3: + return 3.0f * float_width; + case SPREADSHEET_VALUE_TYPE_COLOR: + return 4.0f * float_width; + case SPREADSHEET_VALUE_TYPE_INSTANCES: + return 8.0f; + } + return float_width; +} + +static float get_column_width(const ColumnValues &values) +{ + float data_width = get_default_column_width(values); const int fontid = UI_style_get()->widget.uifont_id; BLF_size(fontid, UI_DEFAULT_TEXT_POINTS, U.dpi); const StringRefNull name = values.name(); const float name_width = BLF_width(fontid, name.data(), name.size()); - return std::max(name_width / UI_UNIT_X + 1.0f, 3.0f); + return std::max(name_width / UI_UNIT_X + 1.0f, data_width); } static float get_column_width_in_pixels(const ColumnValues &values) @@ -339,21 +366,28 @@ static void update_visible_columns(ListBase &columns, DataSource &data_source) } } - data_source.foreach_default_column_ids([&](const SpreadsheetColumnID &column_id) { - std::unique_ptr values = data_source.get_column_values(column_id); - if (values) { - if (used_ids.add(column_id)) { - SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id); - SpreadsheetColumn *new_column = spreadsheet_column_new(new_id); - BLI_addtail(&columns, new_column); - } - } - }); + data_source.foreach_default_column_ids( + [&](const SpreadsheetColumnID &column_id, const bool is_extra) { + std::unique_ptr values = data_source.get_column_values(column_id); + if (values) { + if (used_ids.add(column_id)) { + SpreadsheetColumnID *new_id = spreadsheet_column_id_copy(&column_id); + SpreadsheetColumn *new_column = spreadsheet_column_new(new_id); + if (is_extra) { + BLI_addhead(&columns, new_column); + } + else { + BLI_addtail(&columns, new_column); + } + } + } + }); } static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); + sspreadsheet->runtime->cache.set_all_unused(); spreadsheet_update_context_path(C); std::unique_ptr data_source = get_data_source(C); @@ -394,6 +428,9 @@ static void spreadsheet_main_region_draw(const bContext *C, ARegion *region) ED_region_tag_redraw(footer); ARegion *sidebar = BKE_area_find_region_type(CTX_wm_area(C), RGN_TYPE_UI); ED_region_tag_redraw(sidebar); + + /* Free all cache items that have not been used. */ + sspreadsheet->runtime->cache.remove_all_unused(); } static void spreadsheet_main_region_listener(const wmRegionListenerParams *params) diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc new file mode 100644 index 00000000000..2a399e018b6 --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "spreadsheet_cache.hh" + +namespace blender::ed::spreadsheet { + +void SpreadsheetCache::add(std::unique_ptr key, std::unique_ptr value) +{ + key->is_used = true; + cache_map_.add_overwrite(*key, std::move(value)); + keys_.append(std::move(key)); +} + +SpreadsheetCache::Value *SpreadsheetCache::lookup(const Key &key) +{ + std::unique_ptr *value = cache_map_.lookup_ptr(key); + if (value == nullptr) { + return nullptr; + } + const Key &stored_cache_key = cache_map_.lookup_key(key); + stored_cache_key.is_used = true; + return value->get(); +} + +SpreadsheetCache::Value &SpreadsheetCache::lookup_or_add( + std::unique_ptr key, FunctionRef()> create_value) +{ + Value *value = this->lookup(*key); + if (value != nullptr) { + return *value; + } + std::unique_ptr new_value = create_value(); + value = new_value.get(); + this->add(std::move(key), std::move(new_value)); + return *value; +} + +void SpreadsheetCache::set_all_unused() +{ + for (std::unique_ptr &key : keys_) { + key->is_used = false; + } +} + +void SpreadsheetCache::remove_all_unused() +{ + /* First remove the keys from the map and free the values. */ + for (auto it = cache_map_.keys().begin(); it != cache_map_.keys().end(); ++it) { + const Key &key = *it; + if (!key.is_used) { + cache_map_.remove(it); + } + } + /* Then free the keys. */ + for (int i = 0; i < keys_.size();) { + if (keys_[i]->is_used) { + i++; + } + else { + keys_.remove_and_reorder(i); + } + } +} + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh new file mode 100644 index 00000000000..d370bdab5c1 --- /dev/null +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cache.hh @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include + +#include "BLI_function_ref.hh" +#include "BLI_map.hh" +#include "BLI_vector.hh" + +namespace blender::ed::spreadsheet { + +/** + * A generic cache for the spreadsheet. Different data sources can cache custom data using custom + * keys. + * + * Elements are removed from the cache when they are not used during a redraw. + */ +class SpreadsheetCache { + public: + class Key { + public: + virtual ~Key() = default; + + mutable bool is_used = false; + + virtual uint64_t hash() const = 0; + + friend bool operator==(const Key &a, const Key &b) + { + return a.is_equal_to(b); + } + + private: + virtual bool is_equal_to(const Key &other) const = 0; + }; + + class Value { + public: + virtual ~Value() = default; + }; + + private: + Vector> keys_; + Map, std::unique_ptr> cache_map_; + + public: + /* Adding or looking up a key tags it as being used, so that it won't be removed. */ + void add(std::unique_ptr key, std::unique_ptr value); + Value *lookup(const Key &key); + Value &lookup_or_add(std::unique_ptr key, + FunctionRef()> create_value); + + void set_all_unused(); + void remove_all_unused(); + + template T &lookup_or_add(std::unique_ptr key) + { + return dynamic_cast( + this->lookup_or_add(std::move(key), []() { return std::make_unique(); })); + } +}; + +} // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh index 97170693cb3..c11b4a2b23d 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_cell_value.hh @@ -58,6 +58,7 @@ class CellValue { std::optional value_object; std::optional value_collection; std::optional value_geometry_set; + std::optional value_string; }; } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh index 68370cf6a44..877651d6530 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_column_values.hh @@ -97,9 +97,4 @@ std::unique_ptr column_values_from_function(const eSpreadsheetColu return column_values; } -static constexpr float default_float_column_width = 3; -static constexpr float default_float2_column_width = 2 * default_float_column_width; -static constexpr float default_float3_column_width = 3 * default_float_column_width; -static constexpr float default_color_column_width = 4 * default_float_column_width; - } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc index c38e765caee..e55a7cae6a6 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_context.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_context.cc @@ -373,6 +373,21 @@ void ED_spreadsheet_context_path_set_evaluated_object(SpaceSpreadsheet *sspreads BLI_addtail(&sspreadsheet->context_path, context); } +static bScreen *find_screen_to_search_for_context(wmWindow *window, + SpaceSpreadsheet *current_space) +{ + bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + if (ELEM(screen->state, SCREENMAXIMIZED, SCREENFULL)) { + /* If the spreadsheet is maximized, try to find the context in the unmaximized screen. */ + ScrArea *main_area = (ScrArea *)screen->areabase.first; + SpaceLink *sl = (SpaceLink *)main_area->spacedata.first; + if (sl == (SpaceLink *)current_space) { + return main_area->full; + } + } + return screen; +} + void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspreadsheet) { ED_spreadsheet_context_path_clear(sspreadsheet); @@ -385,9 +400,12 @@ void ED_spreadsheet_context_path_guess(const bContext *C, SpaceSpreadsheet *sspr if (sspreadsheet->object_eval_state == SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { SpaceLink *sl = (SpaceLink *)area->spacedata.first; + if (sl == nullptr) { + continue; + } if (sl->spacetype == SPACE_NODE) { SpaceNode *snode = (SpaceNode *)sl; if (snode->edittree != nullptr) { @@ -466,9 +484,12 @@ bool ED_spreadsheet_context_path_is_active(const bContext *C, SpaceSpreadsheet * } LISTBASE_FOREACH (wmWindow *, window, &wm->windows) { - bScreen *screen = BKE_workspace_active_screen_get(window->workspace_hook); + bScreen *screen = find_screen_to_search_for_context(window, sspreadsheet); LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { SpaceLink *sl = (SpaceLink *)area->spacedata.first; + if (sl == nullptr) { + continue; + } if (sl->spacetype != SPACE_NODE) { continue; } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh index 2ea7fb5809f..873735c81e5 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source.hh @@ -36,8 +36,12 @@ class DataSource { * Calls the callback with all the column ids that should be displayed as long as the user does * not manually add or remove columns. The column id can be stack allocated. Therefore, the * callback should not keep a reference to it (and copy it instead). + * + * The `is_extra` argument indicates that this column is special and should be drawn as the first + * column. (This can be made a bit more generic in the future when necessary.) */ - virtual void foreach_default_column_ids(FunctionRef fn) const + virtual void foreach_default_column_ids( + FunctionRef fn) const { UNUSED_VARS(fn); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc index 78d9f61d8d5..04784dc3e2b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.cc @@ -20,6 +20,7 @@ #include "BKE_mesh.h" #include "BKE_mesh_wrapper.h" #include "BKE_modifier.h" +#include "BKE_volume.h" #include "DNA_ID.h" #include "DNA_mesh_types.h" @@ -33,18 +34,107 @@ #include "NOD_geometry_nodes_eval_log.hh" +#include "BLT_translation.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + +#include "FN_field_cpp_type.hh" + #include "bmesh.h" #include "spreadsheet_data_source_geometry.hh" #include "spreadsheet_intern.hh" namespace geo_log = blender::nodes::geometry_nodes_eval_log; +using blender::fn::GField; namespace blender::ed::spreadsheet { -void GeometryDataSource::foreach_default_column_ids( - FunctionRef fn) const +static std::optional cpp_type_to_column_value_type( + const fn::CPPType &type) { + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_BOOL; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_INT32; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_FLOAT; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_FLOAT2; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_FLOAT3; + } + if (type.is()) { + return SPREADSHEET_VALUE_TYPE_COLOR; + } + return std::nullopt; +} + +void ExtraColumns::foreach_default_column_ids( + FunctionRef fn) const +{ + for (const auto &item : columns_.items()) { + SpreadsheetColumnID column_id; + column_id.name = (char *)item.key.c_str(); + fn(column_id, true); + } +} + +std::unique_ptr ExtraColumns::get_column_values( + const SpreadsheetColumnID &column_id) const +{ + const fn::GSpan *values = columns_.lookup_ptr(column_id.name); + if (values == nullptr) { + return {}; + } + eSpreadsheetColumnValueType column_type = *cpp_type_to_column_value_type(values->type()); + return column_values_from_function(column_type, + column_id.name, + values->size(), + [column_type, values](int index, CellValue &r_cell_value) { + const void *value = (*values)[index]; + switch (column_type) { + case SPREADSHEET_VALUE_TYPE_BOOL: + r_cell_value.value_bool = *(const bool *)value; + break; + case SPREADSHEET_VALUE_TYPE_INT32: + r_cell_value.value_int = *(const int *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT: + r_cell_value.value_float = *(const float *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT2: + r_cell_value.value_float2 = *(const float2 *)value; + break; + case SPREADSHEET_VALUE_TYPE_FLOAT3: + r_cell_value.value_float3 = *(const float3 *)value; + break; + case SPREADSHEET_VALUE_TYPE_COLOR: + r_cell_value.value_color = *( + const ColorGeometry4f *)value; + break; + case SPREADSHEET_VALUE_TYPE_STRING: + r_cell_value.value_string = *(const std::string *)value; + break; + case SPREADSHEET_VALUE_TYPE_INSTANCES: + break; + } + }); +} + +void GeometryDataSource::foreach_default_column_ids( + FunctionRef fn) const +{ + if (component_->attribute_domain_size(domain_) == 0) { + return; + } + + extra_columns_.foreach_default_column_ids(fn); component_->attribute_foreach( [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { if (meta_data.domain != domain_) { @@ -55,7 +145,7 @@ void GeometryDataSource::foreach_default_column_ids( } SpreadsheetColumnID column_id; column_id.name = (char *)attribute_id.name().data(); - fn(column_id); + fn(column_id, false); return true; }); } @@ -63,8 +153,17 @@ void GeometryDataSource::foreach_default_column_ids( std::unique_ptr GeometryDataSource::get_column_values( const SpreadsheetColumnID &column_id) const { + if (component_->attribute_domain_size(domain_) == 0) { + return {}; + } + std::lock_guard lock{mutex_}; + std::unique_ptr extra_column_values = extra_columns_.get_column_values(column_id); + if (extra_column_values) { + return extra_column_values; + } + bke::ReadAttributeLookup attribute = component_->attribute_try_get_for_read(column_id.name); if (!attribute) { return {}; @@ -86,14 +185,16 @@ std::unique_ptr GeometryDataSource::get_column_values( r_cell_value.value_float = value; }); case CD_PROP_INT32: - return column_values_from_function(SPREADSHEET_VALUE_TYPE_INT32, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - int value; - varray->get(index, &value); - r_cell_value.value_int = value; - }); + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_INT32, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + int value; + varray->get(index, &value); + r_cell_value.value_int = value; + }, + STREQ(column_id.name, "id") ? 5.5f : 0.0f); case CD_PROP_BOOL: return column_values_from_function(SPREADSHEET_VALUE_TYPE_BOOL, column_id.name, @@ -104,40 +205,34 @@ std::unique_ptr GeometryDataSource::get_column_values( r_cell_value.value_bool = value; }); case CD_PROP_FLOAT2: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT2, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - float2 value; - varray->get(index, &value); - r_cell_value.value_float2 = value; - }, - default_float2_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT2, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + float2 value; + varray->get(index, &value); + r_cell_value.value_float2 = value; + }); } case CD_PROP_FLOAT3: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - float3 value; - varray->get(index, &value); - r_cell_value.value_float3 = value; - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + float3 value; + varray->get(index, &value); + r_cell_value.value_float3 = value; + }); } case CD_PROP_COLOR: { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_COLOR, - column_id.name, - domain_size, - [varray](int index, CellValue &r_cell_value) { - ColorGeometry4f value; - varray->get(index, &value); - r_cell_value.value_color = value; - }, - default_color_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_COLOR, + column_id.name, + domain_size, + [varray](int index, CellValue &r_cell_value) { + ColorGeometry4f value; + varray->get(index, &value); + r_cell_value.value_color = value; + }); } default: break; @@ -293,18 +388,20 @@ void GeometryDataSource::apply_selection_filter(MutableSpan rows_included) } void InstancesDataSource::foreach_default_column_ids( - FunctionRef fn) const + FunctionRef fn) const { if (component_->instances_amount() == 0) { return; } + extra_columns_.foreach_default_column_ids(fn); + SpreadsheetColumnID column_id; column_id.name = (char *)"Name"; - fn(column_id); - for (const char *name : {"Position", "Rotation", "Scale", "ID"}) { + fn(column_id, false); + for (const char *name : {"Position", "Rotation", "Scale", "id"}) { column_id.name = (char *)name; - fn(column_id); + fn(column_id, false); } } @@ -315,6 +412,11 @@ std::unique_ptr InstancesDataSource::get_column_values( return {}; } + std::unique_ptr extra_column_values = extra_columns_.get_column_values(column_id); + if (extra_column_values) { + return extra_column_values; + } + const int size = this->tot_rows(); if (STREQ(column_id.name, "Name")) { Span reference_handles = component_->instance_reference_handles(); @@ -346,7 +448,6 @@ std::unique_ptr InstancesDataSource::get_column_values( } } }); - values->default_width = 8.0f; return values; } Span transforms = component_->instance_transforms(); @@ -357,38 +458,35 @@ std::unique_ptr InstancesDataSource::get_column_values( size, [transforms](int index, CellValue &r_cell_value) { r_cell_value.value_float3 = transforms[index].translation(); - }, - default_float3_column_width); + }); } if (STREQ(column_id.name, "Rotation")) { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - size, - [transforms](int index, CellValue &r_cell_value) { - r_cell_value.value_float3 = transforms[index].to_euler(); - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].to_euler(); + }); } if (STREQ(column_id.name, "Scale")) { - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_FLOAT3, - column_id.name, - size, - [transforms](int index, CellValue &r_cell_value) { - r_cell_value.value_float3 = transforms[index].scale(); - }, - default_float3_column_width); + return column_values_from_function(SPREADSHEET_VALUE_TYPE_FLOAT3, + column_id.name, + size, + [transforms](int index, CellValue &r_cell_value) { + r_cell_value.value_float3 = transforms[index].scale(); + }); } Span ids = component_->instance_ids(); - if (STREQ(column_id.name, "ID")) { - /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ - return column_values_from_function( - SPREADSHEET_VALUE_TYPE_INT32, - column_id.name, - size, - [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, - 5.5f); + if (!ids.is_empty()) { + if (STREQ(column_id.name, "id")) { + /* Make the column a bit wider by default, since the IDs tend to be large numbers. */ + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_INT32, + column_id.name, + size, + [ids](int index, CellValue &r_cell_value) { r_cell_value.value_int = ids[index]; }, + 5.5f); + } } return {}; } @@ -398,6 +496,89 @@ int InstancesDataSource::tot_rows() const return component_->instances_amount(); } +void VolumeDataSource::foreach_default_column_ids( + FunctionRef fn) const +{ + if (component_->is_empty()) { + return; + } + + for (const char *name : {"Grid Name", "Data Type", "Class"}) { + SpreadsheetColumnID column_id{(char *)name}; + fn(column_id, false); + } +} + +std::unique_ptr VolumeDataSource::get_column_values( + const SpreadsheetColumnID &column_id) const +{ + const Volume *volume = component_->get_for_read(); + if (volume == nullptr) { + return {}; + } + +#ifdef WITH_OPENVDB + const int size = this->tot_rows(); + if (STREQ(column_id.name, "Grid Name")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Grid Name"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + r_cell_value.value_string = BKE_volume_grid_name(volume_grid); + }, + 6.0f); + } + if (STREQ(column_id.name, "Data Type")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Type"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + const VolumeGridType type = BKE_volume_grid_type(volume_grid); + const char *name = nullptr; + RNA_enum_name_from_value(rna_enum_volume_grid_data_type_items, type, &name); + r_cell_value.value_string = IFACE_(name); + }, + 5.0f); + } + if (STREQ(column_id.name, "Class")) { + return column_values_from_function( + SPREADSHEET_VALUE_TYPE_STRING, + IFACE_("Class"), + size, + [volume](int index, CellValue &r_cell_value) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, index); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + openvdb::GridClass grid_class = grid->getGridClass(); + if (grid_class == openvdb::GridClass::GRID_FOG_VOLUME) { + r_cell_value.value_string = IFACE_("Fog Volume"); + } + else if (grid_class == openvdb::GridClass::GRID_LEVEL_SET) { + r_cell_value.value_string = IFACE_("Level Set"); + } + else { + r_cell_value.value_string = IFACE_("Unkown"); + } + }, + 5.0f); + } +#endif + + return {}; +} + +int VolumeDataSource::tot_rows() const +{ + const Volume *volume = component_->get_for_read(); + if (volume == nullptr) { + return 0; + } + return BKE_volume_num_grids(volume); +} + GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspreadsheet, Object *object_eval, const GeometryComponentType used_component_type) @@ -469,6 +650,36 @@ GeometrySet spreadsheet_get_display_geometry_set(const SpaceSpreadsheet *sspread return geometry_set; } +static void find_fields_to_evaluate(const SpaceSpreadsheet *sspreadsheet, + Map &r_fields) +{ + if (sspreadsheet->object_eval_state != SPREADSHEET_OBJECT_EVAL_STATE_VIEWER_NODE) { + return; + } + if (BLI_listbase_count(&sspreadsheet->context_path) <= 1) { + /* No viewer is currently referenced by the context path. */ + return; + } + const geo_log::NodeLog *node_log = geo_log::ModifierLog::find_node_by_spreadsheet_editor_context( + *sspreadsheet); + if (node_log == nullptr) { + return; + } + for (const geo_log::SocketLog &socket_log : node_log->input_logs()) { + const geo_log::ValueLog *value_log = socket_log.value(); + if (value_log == nullptr) { + continue; + } + if (const geo_log::GFieldValueLog *field_value_log = + dynamic_cast(value_log)) { + const GField &field = field_value_log->field(); + if (field) { + r_fields.add("Viewer", std::move(field)); + } + } + } +} + static GeometryComponentType get_display_component_type(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -481,6 +692,69 @@ static GeometryComponentType get_display_component_type(const bContext *C, Objec return GEO_COMPONENT_TYPE_MESH; } +class GeometryComponentCacheKey : public SpreadsheetCache::Key { + public: + /* Use the pointer to the geometry component as a key to detect when the geometry changed. */ + const GeometryComponent *component; + + GeometryComponentCacheKey(const GeometryComponent &component) : component(&component) + { + } + + uint64_t hash() const override + { + return get_default_hash(this->component); + } + + bool is_equal_to(const Key &other) const override + { + if (const GeometryComponentCacheKey *other_geo = + dynamic_cast(&other)) { + return this->component == other_geo->component; + } + return false; + } +}; + +class GeometryComponentCacheValue : public SpreadsheetCache::Value { + public: + /* Stores the result of fields evaluated on a geometry component. Without this, fields would have + * to be reevaluated on every redraw. */ + Map, fn::GArray<>> arrays; +}; + +static void add_fields_as_extra_columns(SpaceSpreadsheet *sspreadsheet, + const GeometryComponent &component, + ExtraColumns &r_extra_columns) +{ + Map fields_to_show; + find_fields_to_evaluate(sspreadsheet, fields_to_show); + + GeometryComponentCacheValue &cache = + sspreadsheet->runtime->cache.lookup_or_add( + std::make_unique(component)); + + const AttributeDomain domain = (AttributeDomain)sspreadsheet->attribute_domain; + const int domain_size = component.attribute_domain_size(domain); + for (const auto &item : fields_to_show.items()) { + StringRef name = item.key; + const GField &field = item.value; + + /* Use the cached evaluated array if it exists, otherwise evaluate the field now. */ + fn::GArray<> &evaluated_array = cache.arrays.lookup_or_add_cb({domain, field}, [&]() { + fn::GArray<> evaluated_array(field.cpp_type(), domain_size); + + bke::GeometryComponentFieldContext field_context{component, domain}; + fn::FieldEvaluator field_evaluator{field_context, domain_size}; + field_evaluator.add_with_destination(field, evaluated_array); + field_evaluator.evaluate(); + return evaluated_array; + }); + + r_extra_columns.add(std::move(name), evaluated_array.as_span()); + } +} + std::unique_ptr data_source_from_geometry(const bContext *C, Object *object_eval) { SpaceSpreadsheet *sspreadsheet = CTX_wm_space_spreadsheet(C); @@ -493,10 +767,18 @@ std::unique_ptr data_source_from_geometry(const bContext *C, Object return {}; } + const GeometryComponent &component = *geometry_set.get_component_for_read(component_type); + ExtraColumns extra_columns; + add_fields_as_extra_columns(sspreadsheet, component, extra_columns); + if (component_type == GEO_COMPONENT_TYPE_INSTANCES) { - return std::make_unique(geometry_set); + return std::make_unique(geometry_set, std::move(extra_columns)); } - return std::make_unique(object_eval, geometry_set, component_type, domain); + if (component_type == GEO_COMPONENT_TYPE_VOLUME) { + return std::make_unique(geometry_set); + } + return std::make_unique( + object_eval, geometry_set, component_type, domain, std::move(extra_columns)); } } // namespace blender::ed::spreadsheet diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh index d1b5dc6845e..a4114dd1f6a 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_data_source_geometry.hh @@ -28,12 +28,34 @@ struct bContext; namespace blender::ed::spreadsheet { +/** + * Contains additional named columns that should be displayed that are not stored on the geometry + * directly. This is used for displaying the evaluated fields connected to a viewer node. + */ +class ExtraColumns { + private: + /** Maps column names to their data. The data is actually stored in the spreadsheet cache. */ + Map columns_; + + public: + void add(std::string name, fn::GSpan data) + { + columns_.add(std::move(name), data); + } + + void foreach_default_column_ids( + FunctionRef fn) const; + + std::unique_ptr get_column_values(const SpreadsheetColumnID &column_id) const; +}; + class GeometryDataSource : public DataSource { private: Object *object_eval_; const GeometrySet geometry_set_; const GeometryComponent *component_; AttributeDomain domain_; + ExtraColumns extra_columns_; /* Some data is computed on the fly only when it is requested. Computing it does not change the * logical state of this data source. Therefore, the corresponding methods are const and need to @@ -45,11 +67,13 @@ class GeometryDataSource : public DataSource { GeometryDataSource(Object *object_eval, GeometrySet geometry_set, const GeometryComponentType component_type, - const AttributeDomain domain) + const AttributeDomain domain, + ExtraColumns extra_columns) : object_eval_(object_eval), geometry_set_(std::move(geometry_set)), component_(geometry_set_.get_component_for_read(component_type)), - domain_(domain) + domain_(domain), + extra_columns_(std::move(extra_columns)) { } @@ -62,7 +86,7 @@ class GeometryDataSource : public DataSource { void apply_selection_filter(MutableSpan rows_included) const; void foreach_default_column_ids( - FunctionRef fn) const override; + FunctionRef fn) const override; std::unique_ptr get_column_values( const SpreadsheetColumnID &column_id) const override; @@ -73,16 +97,38 @@ class GeometryDataSource : public DataSource { class InstancesDataSource : public DataSource { const GeometrySet geometry_set_; const InstancesComponent *component_; + ExtraColumns extra_columns_; public: - InstancesDataSource(GeometrySet geometry_set) + InstancesDataSource(GeometrySet geometry_set, ExtraColumns extra_columns) : geometry_set_(std::move(geometry_set)), - component_(geometry_set_.get_component_for_read()) + component_(geometry_set_.get_component_for_read()), + extra_columns_(std::move(extra_columns)) { } void foreach_default_column_ids( - FunctionRef fn) const override; + FunctionRef fn) const override; + + std::unique_ptr get_column_values( + const SpreadsheetColumnID &column_id) const override; + + int tot_rows() const override; +}; + +class VolumeDataSource : public DataSource { + const GeometrySet geometry_set_; + const VolumeComponent *component_; + + public: + VolumeDataSource(GeometrySet geometry_set) + : geometry_set_(std::move(geometry_set)), + component_(geometry_set_.get_component_for_read()) + { + } + + void foreach_default_column_ids( + FunctionRef fn) const override; std::unique_ptr get_column_values( const SpreadsheetColumnID &column_id) const override; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc index 2b31ce7d03c..b9a508d7764 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_draw.cc @@ -20,6 +20,7 @@ #include "DNA_windowmanager_types.h" #include "BKE_context.h" +#include "BKE_volume.h" #include "BLF_api.h" @@ -48,7 +49,7 @@ static int is_component_row_selected(struct uiBut *but, const void *arg) const bool is_domain_selected = (AttributeDomain)sspreadsheet->attribute_domain == domain; bool is_selected = is_component_selected && is_domain_selected; - if (component == GEO_COMPONENT_TYPE_INSTANCES) { + if (ELEM(component, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES)) { is_selected = is_component_selected; } @@ -81,7 +82,8 @@ DatasetDrawContext::DatasetDrawContext(const bContext *C) { const wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - mval_ = {win->eventstate->x - region->winrct.xmin, win->eventstate->y - region->winrct.ymin}; + mval_ = {win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin}; } GeometrySet DatasetDrawContext::geometry_set_from_component(GeometryComponentType component) @@ -140,6 +142,14 @@ static int element_count_from_instances(const GeometrySet &geometry_set) return 0; } +static int element_count_from_volume(const GeometrySet &geometry_set) +{ + if (const Volume *volume = geometry_set.get_volume_for_read()) { + return BKE_volume_num_grids(volume); + } + return 0; +} + static int element_count_from_component_domain(const GeometrySet &geometry_set, GeometryComponentType component, AttributeDomain domain) @@ -190,6 +200,10 @@ void DatasetRegionDrawer::draw_dataset_row(const int indentation, BLI_str_format_attribute_domain_size( element_count, element_count_from_instances(draw_context.current_geometry_set)); } + if (component == GEO_COMPONENT_TYPE_VOLUME) { + BLI_str_format_attribute_domain_size( + element_count, element_count_from_volume(draw_context.current_geometry_set)); + } else { BLI_str_format_attribute_domain_size( element_count, @@ -236,7 +250,7 @@ void DatasetRegionDrawer::draw_dataset_row(const int indentation, void DatasetRegionDrawer::draw_component_row(const DatasetComponentLayoutInfo &component_info) { - if (component_info.type == GEO_COMPONENT_TYPE_INSTANCES) { + if (ELEM(component_info.type, GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES)) { draw_dataset_row( 0, component_info.type, std::nullopt, component_info.icon, component_info.label, true); } diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc index abbad8c7088..f15af2e4d32 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_dataset_layout.cc @@ -74,6 +74,12 @@ constexpr DatasetComponentLayoutInfo DATASET_layout_hierarchy[] = { ATTR_INFO(ATTR_DOMAIN_POINT, N_("Point"), ICON_PARTICLE_POINT), }, }, + { + GEO_COMPONENT_TYPE_VOLUME, + N_("Volume Grids"), + ICON_VOLUME_DATA, + {}, + }, { GEO_COMPONENT_TYPE_INSTANCES, N_("Instances"), diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh index 8be5283fd63..8b050c2e69b 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh +++ b/source/blender/editors/space_spreadsheet/spreadsheet_intern.hh @@ -17,12 +17,24 @@ #pragma once #include "BKE_geometry_set.hh" +#include "spreadsheet_cache.hh" -typedef struct SpaceSpreadsheet_Runtime { - int visible_rows; - int tot_rows; - int tot_columns; -} SpaceSpreadsheet_Runtime; +struct SpaceSpreadsheet_Runtime { + public: + int visible_rows = 0; + int tot_rows = 0; + int tot_columns = 0; + + blender::ed::spreadsheet::SpreadsheetCache cache; + + SpaceSpreadsheet_Runtime() = default; + + /* The cache is not copied currently. */ + SpaceSpreadsheet_Runtime(const SpaceSpreadsheet_Runtime &other) + : visible_rows(other.visible_rows), tot_rows(other.tot_rows), tot_columns(other.tot_columns) + { + } +}; struct bContext; diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc index 1a5eac53306..202523c0e64 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_layout.cc @@ -93,7 +93,9 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { const int real_index = spreadsheet_layout_.row_indices[row_index]; const ColumnValues &column = *spreadsheet_layout_.columns[column_index].values; CellValue cell_value; - column.get_value(real_index, cell_value); + if (real_index < column.size()) { + column.get_value(real_index, cell_value); + } if (cell_value.value_int.has_value()) { const int value = *cell_value.value_int; @@ -226,6 +228,23 @@ class SpreadsheetLayoutDrawer : public SpreadsheetDrawer { 0, nullptr); } + else if (cell_value.value_string.has_value()) { + uiDefIconTextBut(params.block, + UI_BTYPE_LABEL, + 0, + ICON_NONE, + cell_value.value_string->c_str(), + params.xmin, + params.ymin, + params.width, + params.height, + nullptr, + 0, + 0, + 0, + 0, + nullptr); + } } void draw_float_vector(const CellDrawParams ¶ms, const Span values) const diff --git a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc index 219d03c1dcd..d56049990b4 100644 --- a/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc +++ b/source/blender/editors/space_spreadsheet/spreadsheet_row_filter_ui.cc @@ -336,7 +336,7 @@ void register_row_filter_panels(ARegionType ®ion_type) strcpy(panel_type->label, ""); strcpy(panel_type->category, "Filters"); strcpy(panel_type->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA); - panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_HEADER_EXPAND; + panel_type->flag = PANEL_TYPE_INSTANCED | PANEL_TYPE_HEADER_EXPAND; panel_type->draw_header = spreadsheet_filter_panel_draw_header; panel_type->draw = spreadsheet_filter_panel_draw; panel_type->get_list_data_expand_flag = get_filter_expand_flag; diff --git a/source/blender/editors/space_text/space_text.c b/source/blender/editors/space_text/space_text.c index 89e92231657..f8dc61f18f2 100644 --- a/source/blender/editors/space_text/space_text.c +++ b/source/blender/editors/space_text/space_text.c @@ -302,7 +302,7 @@ static void text_cursor(wmWindow *win, ScrArea *area, ARegion *region) int wmcursor = WM_CURSOR_TEXT_EDIT; if (st->text && BLI_rcti_isect_pt(&st->runtime.scroll_region_handle, - win->eventstate->x - region->winrct.xmin, + win->eventstate->xy[0] - region->winrct.xmin, st->runtime.scroll_region_handle.ymin)) { wmcursor = WM_CURSOR_DEFAULT; } diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index 99fcb2092c3..b541b65d676 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -48,6 +48,9 @@ #include "text_format.h" #include "text_intern.h" +#include "WM_api.h" +#include "WM_types.h" + /******************** text font drawing ******************/ typedef struct TextDrawContext { @@ -1734,6 +1737,23 @@ void text_update_character_width(SpaceText *st) text_font_end(&tdc); } +bool ED_text_activate_in_screen(bContext *C, Text *text) +{ + ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TEXT, 0); + if (area) { + SpaceText *st = area->spacedata.first; + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + st->text = text; + if (region) { + ED_text_scroll_to_cursor(st, region, true); + } + WM_event_add_notifier(C, NC_TEXT | ND_CURSOR, text); + return true; + } + + return false; +} + /* Moves the view to the cursor location, * also used to make sure the view isn't outside the file */ void ED_text_scroll_to_cursor(SpaceText *st, ARegion *region, const bool center) diff --git a/source/blender/editors/space_text/text_format.h b/source/blender/editors/space_text/text_format.h index 833f40730ad..fe7b3328030 100644 --- a/source/blender/editors/space_text/text_format.h +++ b/source/blender/editors/space_text/text_format.h @@ -75,7 +75,7 @@ typedef struct TextFormatType { } TextFormatType; enum { - /** Whitespace */ + /** White-space */ FMT_TYPE_WHITESPACE = '_', /** Comment text */ FMT_TYPE_COMMENT = '#', @@ -85,7 +85,7 @@ enum { FMT_TYPE_NUMERAL = 'n', /** String letters */ FMT_TYPE_STRING = 'l', - /** Decorator / Preprocessor directive */ + /** Decorator / Pre-processor directive */ FMT_TYPE_DIRECTIVE = 'd', /** Special variables (class, def) */ FMT_TYPE_SPECIAL = 'v', diff --git a/source/blender/editors/space_text/text_format_lua.c b/source/blender/editors/space_text/text_format_lua.c index 0cd2d9baa0b..cdc43246a2f 100644 --- a/source/blender/editors/space_text/text_format_lua.c +++ b/source/blender/editors/space_text/text_format_lua.c @@ -277,7 +277,7 @@ static void txtfmt_lua_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_osl.c b/source/blender/editors/space_text/text_format_osl.c index 97d9ec546ca..6fc65a19d98 100644 --- a/source/blender/editors/space_text/text_format_osl.c +++ b/source/blender/editors/space_text/text_format_osl.c @@ -170,7 +170,7 @@ static int txtfmt_osl_find_preprocessor(const char *string) { if (string[0] == '#') { int i = 1; - /* Whitespace is ok '# foo' */ + /* White-space is ok '# foo'. */ while (text_check_whitespace(string[i])) { i++; } @@ -298,7 +298,7 @@ static void txtfmt_osl_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_pov.c b/source/blender/editors/space_text/text_format_pov.c index ea3d0ec1478..0f103351eee 100644 --- a/source/blender/editors/space_text/text_format_pov.c +++ b/source/blender/editors/space_text/text_format_pov.c @@ -709,7 +709,7 @@ static int txtfmt_pov_find_bool(const char *string) /* Keep aligned args for readability. */ /* clang-format off */ - /* Built-in Constants */ + /* Built-in Constants. */ if (STR_LITERAL_STARTSWITH(string, "unofficial", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "false", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "no", len)) { i = len; @@ -719,7 +719,7 @@ static int txtfmt_pov_find_bool(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "on", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "pi", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "tau", len)) { i = len; - /* Encodings */ + /* Encodings. */ } else if (STR_LITERAL_STARTSWITH(string, "sint16be", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "sint16le", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "sint32be", len)) { i = len; @@ -732,7 +732,7 @@ static int txtfmt_pov_find_bool(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "uint8", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "ascii", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "utf8", len)) { i = len; - /* Filetypes */ + /* File-types. */ } else if (STR_LITERAL_STARTSWITH(string, "tiff", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "df3", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "exr", len)) { i = len; @@ -870,7 +870,7 @@ static void txtfmt_pov_format_line(SpaceText *st, TextLine *line, const bool do_ cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_pov_ini.c b/source/blender/editors/space_text/text_format_pov_ini.c index 259ad02a6b7..2ce8bc37868 100644 --- a/source/blender/editors/space_text/text_format_pov_ini.c +++ b/source/blender/editors/space_text/text_format_pov_ini.c @@ -279,7 +279,7 @@ static int txtfmt_ini_find_reserved(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "Dither", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "Flags", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "Font", len)) { i = len; - /* Filetypes */ + /* File-types. */ } else if (STR_LITERAL_STARTSWITH(string, "df3", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "exr", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "gif", len)) { i = len; @@ -292,7 +292,7 @@ static int txtfmt_ini_find_reserved(const char *string) } else if (STR_LITERAL_STARTSWITH(string, "sys", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "tga", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "tiff", len)) { i = len; - /* Encodings */ + /* Encodings. */ } else if (STR_LITERAL_STARTSWITH(string, "ascii", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "utf8", len)) { i = len; } else if (STR_LITERAL_STARTSWITH(string, "uint8", len)) { i = len; @@ -448,7 +448,7 @@ static void txtfmt_pov_ini_format_line(SpaceText *st, TextLine *line, const bool cont = (*str == '"') ? FMT_CONT_QUOTEDOUBLE : FMT_CONT_QUOTESINGLE; *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_format_py.c b/source/blender/editors/space_text/text_format_py.c index e2a01a8d85d..a8a3bfa3a68 100644 --- a/source/blender/editors/space_text/text_format_py.c +++ b/source/blender/editors/space_text/text_format_py.c @@ -423,7 +423,7 @@ static void txtfmt_py_format_line(SpaceText *st, TextLine *line, const bool do_n } *fmt = FMT_TYPE_STRING; } - /* Whitespace (all ws. has been converted to spaces) */ + /* White-space (all ws. has been converted to spaces). */ else if (*str == ' ') { *fmt = FMT_TYPE_WHITESPACE; } diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index c3bc474b98a..458a1be0308 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -29,6 +29,7 @@ #include "DNA_text_types.h" #include "BLI_blenlib.h" +#include "BLI_math.h" #include "BLI_math_base.h" #include "BLT_translation.h" @@ -2592,20 +2593,18 @@ static void text_scroll_apply(bContext *C, wmOperator *op, const wmEvent *event) { SpaceText *st = CTX_wm_space_text(C); TextScroll *tsc = op->customdata; - const int mval[2] = {event->x, event->y}; + const int mval[2] = {event->xy[0], event->xy[1]}; text_update_character_width(st); /* compute mouse move distance */ if (tsc->is_first) { - tsc->mval_prev[0] = mval[0]; - tsc->mval_prev[1] = mval[1]; + copy_v2_v2_int(tsc->mval_prev, mval); tsc->is_first = false; } if (event->type != MOUSEPAN) { - tsc->mval_delta[0] = mval[0] - tsc->mval_prev[0]; - tsc->mval_delta[1] = mval[1] - tsc->mval_prev[1]; + sub_v2_v2v2_int(tsc->mval_delta, mval, tsc->mval_prev); } /* accumulate scroll, in float values for events that give less than one @@ -2757,11 +2756,10 @@ static int text_scroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { text_update_character_width(st); - tsc->mval_prev[0] = event->x; - tsc->mval_prev[1] = event->y; + copy_v2_v2_int(tsc->mval_prev, event->xy); /* Sensitivity of scroll set to 4pix per line/char */ - tsc->mval_delta[0] = (event->x - event->prevx) * st->runtime.cwidth_px / 4; - tsc->mval_delta[1] = (event->y - event->prevy) * st->runtime.lheight_px / 4; + tsc->mval_delta[0] = (event->xy[0] - event->prev_xy[0]) * st->runtime.cwidth_px / 4; + tsc->mval_delta[1] = (event->xy[1] - event->prev_xy[1]) * st->runtime.lheight_px / 4; tsc->is_first = false; tsc->is_scrollbar = false; text_scroll_apply(C, op, event); diff --git a/source/blender/editors/space_userpref/userpref_ops.c b/source/blender/editors/space_userpref/userpref_ops.c index 63506678b70..d40229332fd 100644 --- a/source/blender/editors/space_userpref/userpref_ops.c +++ b/source/blender/editors/space_userpref/userpref_ops.c @@ -24,6 +24,7 @@ #include #include "DNA_screen_types.h" +#include "DNA_space_types.h" #include "BLI_listbase.h" #ifdef WIN32 @@ -139,23 +140,51 @@ static void PREFERENCES_OT_autoexec_path_remove(wmOperatorType *ot) /** \name Add Asset Library Operator * \{ */ -static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +static int preferences_asset_library_add_exec(bContext *UNUSED(C), wmOperator *op) { - BKE_preferences_asset_library_add(&U, NULL, NULL); + char *directory = RNA_string_get_alloc(op->ptr, "directory", NULL, 0, NULL); + + /* NULL is a valid directory path here. A library without path will be created then. */ + BKE_preferences_asset_library_add(&U, NULL, directory); U.runtime.is_dirty = true; + + /* There's no dedicated notifier for the Preferences. */ + WM_main_add_notifier(NC_WINDOW, NULL); + + MEM_freeN(directory); return OPERATOR_FINISHED; } +static int preferences_asset_library_add_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) +{ + if (!RNA_struct_property_is_set(op->ptr, "directory")) { + WM_event_add_fileselect(C, op); + return OPERATOR_RUNNING_MODAL; + } + + return preferences_asset_library_add_exec(C, op); +} + static void PREFERENCES_OT_asset_library_add(wmOperatorType *ot) { ot->name = "Add Asset Library"; ot->idname = "PREFERENCES_OT_asset_library_add"; - ot->description = - "Add a path to a .blend file to be used by the Asset Browser as source of assets"; + ot->description = "Add a directory to be used by the Asset Browser as source of assets"; ot->exec = preferences_asset_library_add_exec; + ot->invoke = preferences_asset_library_add_invoke; ot->flag = OPTYPE_INTERNAL; + + WM_operator_properties_filesel(ot, + FILE_TYPE_FOLDER, + FILE_SPECIAL, + FILE_OPENFILE, + WM_FILESEL_DIRECTORY, + FILE_DEFAULTDISPLAY, + FILE_SORT_DEFAULT); } /** \} */ diff --git a/source/blender/editors/space_view3d/CMakeLists.txt b/source/blender/editors/space_view3d/CMakeLists.txt index fe84a3b8ae9..19f869ed50b 100644 --- a/source/blender/editors/space_view3d/CMakeLists.txt +++ b/source/blender/editors/space_view3d/CMakeLists.txt @@ -44,6 +44,7 @@ set(SRC space_view3d.c view3d_buttons.c view3d_camera_control.c + view3d_cursor_snap.c view3d_draw.c view3d_edit.c view3d_gizmo_armature.c diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 72d0c11e192..6acf51aec6e 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -30,6 +30,7 @@ #include "DNA_material_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_view3d_types.h" #include "MEM_guardedalloc.h" @@ -39,12 +40,14 @@ #include "BLT_translation.h" +#include "BKE_asset.h" #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_lattice.h" +#include "BKE_layer.h" #include "BKE_main.h" #include "BKE_mball.h" #include "BKE_mesh.h" @@ -54,6 +57,7 @@ #include "BKE_workspace.h" #include "ED_object.h" +#include "ED_outliner.h" #include "ED_render.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -81,6 +85,7 @@ #endif #include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" #include "view3d_intern.h" /* own include */ @@ -276,6 +281,13 @@ static SpaceLink *view3d_create(const ScrArea *UNUSED(area), const Scene *scene) v3d->camera = scene->camera; } + /* header */ + region = MEM_callocN(sizeof(ARegion), "header for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + /* tool header */ region = MEM_callocN(sizeof(ARegion), "tool header for view3d"); @@ -284,13 +296,6 @@ static SpaceLink *view3d_create(const ScrArea *UNUSED(area), const Scene *scene) region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; - /* header */ - region = MEM_callocN(sizeof(ARegion), "header for view3d"); - - BLI_addtail(&v3d->regionbase, region); - region->regiontype = RGN_TYPE_HEADER; - region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; - /* tool shelf */ region = MEM_callocN(sizeof(ARegion), "toolshelf for view3d"); @@ -473,7 +478,7 @@ static void view3d_main_region_exit(wmWindowManager *wm, ARegion *region) static bool view3d_drop_in_main_region_poll(bContext *C, const wmEvent *event) { ScrArea *area = CTX_wm_area(C); - return ED_region_overlap_isect_any_xy(area, &event->x) == false; + return ED_region_overlap_isect_any_xy(area, event->xy) == false; } static ID_Type view3d_drop_id_in_main_region_poll_get_id_type(bContext *C, @@ -482,7 +487,7 @@ static ID_Type view3d_drop_id_in_main_region_poll_get_id_type(bContext *C, { const ScrArea *area = CTX_wm_area(C); - if (ED_region_overlap_isect_any_xy(area, &event->x)) { + if (ED_region_overlap_isect_any_xy(area, event->xy)) { return 0; } if (!view3d_drop_in_main_region_poll(C, event)) { @@ -514,10 +519,74 @@ static bool view3d_drop_id_in_main_region_poll(bContext *C, return WM_drag_is_ID_type(drag, id_type); } +static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) +{ + V3DSnapCursorState *state = drop->draw_data; + if (state) { + return; + } + + /* Don't use the snap cursor when linking the object. Object transform isn't editable then and + * would be reset on reload. */ + if (WM_drag_asset_will_import_linked(drag)) { + return; + } + + state = drop->draw_data = ED_view3d_cursor_snap_active(); + state->draw_plane = true; + + float dimensions[3] = {0.0f}; + if (drag->type == WM_DRAG_ID) { + Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); + BKE_object_dimensions_get(ob, dimensions); + } + else { + struct AssetMetaData *meta_data = WM_drag_get_asset_meta_data(drag, ID_OB); + IDProperty *dimensions_prop = BKE_asset_metadata_idprop_find(meta_data, "dimensions"); + if (dimensions_prop) { + copy_v3_v3(dimensions, IDP_Array(dimensions_prop)); + } + } + + if (!is_zero_v3(dimensions)) { + mul_v3_v3fl(state->box_dimensions, dimensions, 0.5f); + UI_GetThemeColor4ubv(TH_GIZMO_PRIMARY, state->color_box); + state->draw_box = true; + } +} + +static void view3d_ob_drop_draw_deactivate(struct wmDropBox *drop, wmDrag *UNUSED(drag)) +{ + V3DSnapCursorState *state = drop->draw_data; + if (state) { + ED_view3d_cursor_snap_deactive(state); + drop->draw_data = NULL; + } +} + static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB); } +static bool view3d_ob_drop_poll_external_asset(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) { + return false; + } + return true; +} + +/** + * \note the term local here refers to not being an external asset, + * poll will succeed for linked library objects. + */ +static bool view3d_ob_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) { + return false; + } + return true; +} static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { @@ -531,12 +600,22 @@ static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event static char *view3d_mat_drop_tooltip(bContext *C, wmDrag *drag, - const wmEvent *event, + const int xy[2], struct wmDropBox *drop) { const char *name = WM_drag_get_item_name(drag); + ARegion *region = CTX_wm_region(C); RNA_string_set(drop->ptr, "name", name); - return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, event); + int mval[2] = { + xy[0] - region->winrct.xmin, + xy[1] - region->winrct.ymin, + }; + return ED_object_ot_drop_named_material_tooltip(C, drop->ptr, mval); +} + +static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + return view3d_drop_id_in_main_region_poll(C, drag, event, ID_WO); } static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) @@ -550,7 +629,7 @@ static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEven static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), wmDrag *UNUSED(drag), - const wmEvent *UNUSED(event), + const int UNUSED(xy[2]), wmDropBox *UNUSED(drop)) { return BLI_strdup(TIP_("Create object instance from object-data")); @@ -558,7 +637,7 @@ static char *view3d_object_data_drop_tooltip(bContext *UNUSED(C), static bool view3d_ima_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) { - if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), &event->x)) { + if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), event->xy)) { return false; } if (drag->type == WM_DRAG_PATH) { @@ -620,12 +699,85 @@ static bool view3d_volume_drop_poll(bContext *UNUSED(C), return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME); } -static void view3d_ob_drop_copy(wmDrag *drag, wmDropBox *drop) +static void view3d_ob_drop_matrix_from_snap(V3DSnapCursorState *snap_state, + Object *ob, + float obmat_final[4][4]) { - ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, ID_OB); + V3DSnapCursorData *snap_data; + snap_data = ED_view3d_cursor_snap_data_get(snap_state, NULL, 0, 0); + BLI_assert(snap_state->draw_box || snap_state->draw_plane); + copy_m4_m3(obmat_final, snap_data->plane_omat); + copy_v3_v3(obmat_final[3], snap_data->loc); + + float scale[3]; + mat4_to_size(scale, ob->obmat); + rescale_m4(obmat_final, scale); + + BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float offset[3]; + BKE_boundbox_calc_center_aabb(bb, offset); + offset[2] = bb->vec[0][2]; + mul_mat3_m4_v3(obmat_final, offset); + sub_v3_v3(obmat_final[3], offset); + } +} + +static void view3d_ob_drop_copy_local_id(wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID(drag, ID_OB); RNA_string_set(drop->ptr, "name", id->name + 2); - RNA_boolean_set(drop->ptr, "duplicate", false); + /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ + BLI_assert(drag->type != WM_DRAG_ASSET); + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); +} + +static void view3d_ob_drop_copy_external_asset(wmDrag *drag, wmDropBox *drop) +{ + /* NOTE(@campbellbarton): Selection is handled here, de-selecting objects before append, + * using auto-select to ensure the new objects are selected. + * This is done so #OBJECT_OT_transform_to_mouse (which runs after this drop handler) + * can use the context setup here to place the objects. */ + BLI_assert(drag->type == WM_DRAG_ASSET); + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + bContext *C = asset_drag->evil_C; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + BKE_view_layer_base_deselect_all(view_layer); + + ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + + /* TODO(sergey): Only update relations for the current scene. */ + DEG_relations_tag_update(CTX_data_main(C)); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + + RNA_string_set(drop->ptr, "name", id->name + 2); + + Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); + if (base != NULL) { + BKE_view_layer_base_select_and_set_active(view_layer, base); + WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, scene); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_object_tag(C); + + V3DSnapCursorState *snap_state = drop->draw_data; + if (snap_state) { + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); + } } static void view3d_collection_drop_copy(wmDrag *drag, wmDropBox *drop) @@ -690,12 +842,31 @@ static void view3d_dropboxes(void) { ListBase *lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW); - WM_dropbox_add(lb, - "OBJECT_OT_add_named", - view3d_ob_drop_poll, - view3d_ob_drop_copy, - WM_drag_free_imported_drag_ID, - NULL); + struct wmDropBox *drop; + drop = WM_dropbox_add(lb, + "OBJECT_OT_add_named", + view3d_ob_drop_poll_local_id, + view3d_ob_drop_copy_local_id, + WM_drag_free_imported_drag_ID, + NULL); + + drop->draw = WM_drag_draw_item_name_fn; + drop->draw_activate = view3d_ob_drop_draw_activate; + drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + drop->opcontext = WM_OP_EXEC_DEFAULT; /* Not really needed. */ + + drop = WM_dropbox_add(lb, + "OBJECT_OT_transform_to_mouse", + view3d_ob_drop_poll_external_asset, + view3d_ob_drop_copy_external_asset, + WM_drag_free_imported_drag_ID, + NULL); + + drop->draw = WM_drag_draw_item_name_fn; + drop->draw_activate = view3d_ob_drop_draw_activate; + drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + drop->opcontext = WM_OP_INVOKE_DEFAULT; + WM_dropbox_add(lb, "OBJECT_OT_drop_named_material", view3d_mat_drop_poll, @@ -732,6 +903,12 @@ static void view3d_dropboxes(void) view3d_id_drop_copy_with_type, WM_drag_free_imported_drag_ID, view3d_object_data_drop_tooltip); + WM_dropbox_add(lb, + "VIEW3D_OT_drop_world", + view3d_world_drop_poll, + view3d_id_drop_copy, + WM_drag_free_imported_drag_ID, + NULL); } static void view3d_widgets(void) @@ -1555,11 +1732,13 @@ static void space_view3d_listener(const wmSpaceTypeListenerParams *params) switch (wmn->category) { case NC_SCENE: switch (wmn->data) { - case ND_WORLD: - if (v3d->flag2 & V3D_HIDE_OVERLAYS) { + case ND_WORLD: { + const bool use_scene_world = V3D_USES_SCENE_WORLD(v3d); + if (v3d->flag2 & V3D_HIDE_OVERLAYS || use_scene_world) { ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); } break; + } } break; case NC_WORLD: @@ -1600,6 +1779,7 @@ static void space_view3d_refresh(const bContext *C, ScrArea *area) const char *view3d_context_dir[] = { "active_object", + "selected_ids", NULL, }; @@ -1610,8 +1790,9 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes if (CTX_data_dir(member)) { CTX_data_dir_set(result, view3d_context_dir); + return CTX_RESULT_OK; } - else if (CTX_data_equals(member, "active_object")) { + if (CTX_data_equals(member, "active_object")) { /* In most cases the active object is the `view_layer->basact->object`. * For the 3D view however it can be NULL when hidden. * @@ -1635,13 +1816,21 @@ static int view3d_context(const bContext *C, const char *member, bContextDataRes } } - return 1; + return CTX_RESULT_OK; } - else { - return 0; /* not found */ + if (CTX_data_equals(member, "selected_ids")) { + ListBase selected_objects; + CTX_data_selected_objects(C, &selected_objects); + LISTBASE_FOREACH (CollectionPointerLink *, object_ptr_link, &selected_objects) { + ID *selected_id = object_ptr_link->ptr.owner_id; + CTX_data_id_list_add(result, selected_id); + } + BLI_freelistN(&selected_objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; } - return -1; /* found but not available */ + return CTX_RESULT_MEMBER_NOT_FOUND; } static void view3d_id_remap(ScrArea *area, SpaceLink *slink, ID *old_id, ID *new_id) @@ -1782,5 +1971,10 @@ void ED_spacetype_view3d(void) art = ED_area_type_hud(st->spaceid); BLI_addhead(&st->regiontypes, art); + /* regions: xr */ + art = MEM_callocN(sizeof(ARegionType), "spacetype view3d xr region"); + art->regionid = RGN_TYPE_XR; + BLI_addhead(&st->regiontypes, art); + BKE_spacetype_register(st); } diff --git a/source/blender/editors/space_view3d/view3d_cursor_snap.c b/source/blender/editors/space_view3d/view3d_cursor_snap.c new file mode 100644 index 00000000000..479acb3cb1a --- /dev/null +++ b/source/blender/editors/space_view3d/view3d_cursor_snap.c @@ -0,0 +1,982 @@ +/* + * 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) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup wm + * + * \brief Snap cursor. + */ + +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "BLI_listbase.h" +#include "BLI_rect.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_screen.h" + +#include "GPU_immediate.h" +#include "GPU_matrix.h" + +#include "ED_screen.h" +#include "ED_transform.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" + +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" + +#define STATE_INTERN_GET(state) \ + (SnapStateIntern *)((char *)state - offsetof(SnapStateIntern, snap_state)) + +typedef struct SnapStateIntern { + struct SnapStateIntern *next, *prev; + V3DSnapCursorState snap_state; +} SnapStateIntern; + +typedef struct SnapCursorDataIntern { + V3DSnapCursorState state_default; + ListBase state_intern; + V3DSnapCursorData snap_data; + + struct SnapObjectContext *snap_context_v3d; + const Scene *scene; + short snap_elem_hidden; + + float prevpoint_stack[3]; + + /* Copy of the parameters of the last event state in order to detect updates. */ + struct { + int x; + int y; +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + short shift, ctrl, alt, oskey; +#endif + } last_eventstate; + +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + struct wmKeyMap *keymap; + int snap_on; +#endif + + struct wmPaintCursor *handle; + + bool is_initiated; +} SnapCursorDataIntern; + +static SnapCursorDataIntern g_data_intern = { + .state_default = {.prevpoint = NULL, + .snap_elem_force = (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | + SCE_SNAP_MODE_FACE | SCE_SNAP_MODE_EDGE_PERPENDICULAR | + SCE_SNAP_MODE_EDGE_MIDPOINT), + .plane_axis = 2, + .color_point = {255, 255, 255, 255}, + .color_line = {255, 255, 255, 128}, + .color_box = {255, 255, 255, 128}, + .box_dimensions = {1.0f, 1.0f, 1.0f}, + .draw_point = true}}; + +/** + * Calculate a 3x3 orientation matrix from the surface under the cursor. + */ +static void v3d_cursor_poject_surface_normal(const float normal[3], + const float obmat[4][4], + float r_mat[3][3]) +{ + float mat[3][3]; + copy_m3_m4(mat, obmat); + normalize_m3(mat); + + float dot_best = fabsf(dot_v3v3(mat[0], normal)); + int i_best = 0; + for (int i = 1; i < 3; i++) { + float dot_test = fabsf(dot_v3v3(mat[i], normal)); + if (dot_test > dot_best) { + i_best = i; + dot_best = dot_test; + } + } + if (dot_v3v3(mat[i_best], normal) < 0.0f) { + negate_v3(mat[(i_best + 1) % 3]); + negate_v3(mat[(i_best + 2) % 3]); + } + copy_v3_v3(mat[i_best], normal); + orthogonalize_m3(mat, i_best); + normalize_m3(mat); + + copy_v3_v3(r_mat[0], mat[(i_best + 1) % 3]); + copy_v3_v3(r_mat[1], mat[(i_best + 2) % 3]); + copy_v3_v3(r_mat[2], mat[i_best]); +} + +/** + * Calculate 3D view incremental (grid) snapping. + * + * \note This could be moved to a public function. + */ +static bool v3d_cursor_snap_calc_incremental( + Scene *scene, View3D *v3d, ARegion *region, const float co_relative[3], float co[3]) +{ + const float grid_size = ED_view3d_grid_view_scale(scene, v3d, region, NULL); + if (UNLIKELY(grid_size == 0.0f)) { + return false; + } + + if (scene->toolsettings->snap_flag & SCE_SNAP_ABS_GRID) { + co_relative = NULL; + } + + if (co_relative != NULL) { + sub_v3_v3(co, co_relative); + } + mul_v3_fl(co, 1.0f / grid_size); + co[0] = roundf(co[0]); + co[1] = roundf(co[1]); + co[2] = roundf(co[2]); + mul_v3_fl(co, grid_size); + if (co_relative != NULL) { + add_v3_v3(co, co_relative); + } + + return true; +} + +/** + * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v. + */ +static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3]) +{ + float dot_best = -1.0f; + int axis_found = axis_align; + for (int i = 0; i < 3; i++) { + const float dot_test = fabsf(dot_v3v3(mat[i], v)); + if (dot_test > dot_best) { + dot_best = dot_test; + axis_found = i; + } + } + + if (axis_align != axis_found) { + float tmat[3][3]; + copy_m3_m3(tmat, mat); + const int offset = mod_i(axis_found - axis_align, 3); + for (int i = 0; i < 3; i++) { + copy_v3_v3(mat[i], tmat[(i + offset) % 3]); + } + return true; + } + return false; +} + +/* -------------------------------------------------------------------- */ +/** \name Drawings + * \{ */ + +static void v3d_cursor_plane_draw_grid(const int resolution, + const float scale, + const float scale_fade, + const float matrix[4][4], + const int plane_axis, + const float color[4]) +{ + BLI_assert(scale_fade <= scale); + const int resolution_min = resolution - 1; + float color_fade[4] = {UNPACK4(color)}; + const float *center = matrix[3]; + + GPU_blend(GPU_BLEND_ADDITIVE); + GPU_line_smooth(true); + GPU_line_width(1.0f); + + GPUVertFormat *format = immVertexFormat(); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + const uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR); + + const size_t coords_len = resolution * resolution; + float(*coords)[3] = MEM_mallocN(sizeof(*coords) * coords_len, __func__); + + const int axis_x = (plane_axis + 0) % 3; + const int axis_y = (plane_axis + 1) % 3; + const int axis_z = (plane_axis + 2) % 3; + + int i; + const float resolution_div = (float)1.0f / (float)resolution; + i = 0; + for (int x = 0; x < resolution; x++) { + const float x_fl = (x * resolution_div) - 0.5f; + for (int y = 0; y < resolution; y++) { + const float y_fl = (y * resolution_div) - 0.5f; + coords[i][axis_x] = 0.0f; + coords[i][axis_y] = x_fl * scale; + coords[i][axis_z] = y_fl * scale; + mul_m4_v3(matrix, coords[i]); + i += 1; + } + } + BLI_assert(i == (int)coords_len); + immBeginAtMost(GPU_PRIM_LINES, coords_len * 4); + i = 0; + for (int x = 0; x < resolution_min; x++) { + for (int y = 0; y < resolution_min; y++) { + + /* Add #resolution_div to ensure we fade-out entirely. */ +#define FADE(v) \ + max_ff(0.0f, (1.0f - square_f(((len_v3v3(v, center) / scale_fade) + resolution_div) * 2.0f))) + + const float *v0 = coords[(resolution * x) + y]; + const float *v1 = coords[(resolution * (x + 1)) + y]; + const float *v2 = coords[(resolution * x) + (y + 1)]; + + const float f0 = FADE(v0); + const float f1 = FADE(v1); + const float f2 = FADE(v2); + + if (f0 > 0.0f || f1 > 0.0f) { + color_fade[3] = color[3] * f0; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v0); + color_fade[3] = color[3] * f1; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v1); + } + if (f0 > 0.0f || f2 > 0.0f) { + color_fade[3] = color[3] * f0; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v0); + + color_fade[3] = color[3] * f2; + immAttr4fv(col_id, color_fade); + immVertex3fv(pos_id, v2); + } + +#undef FADE + + i++; + } + } + + MEM_freeN(coords); + + immEnd(); + + immUnbindProgram(); + + GPU_line_smooth(false); + GPU_blend(GPU_BLEND_NONE); +} + +static void v3d_cursor_plane_draw(const RegionView3D *rv3d, + const int plane_axis, + const float matrix[4][4]) +{ + /* Draw */ + float pixel_size; + + if (rv3d->is_persp) { + float center[3]; + negate_v3_v3(center, rv3d->ofs); + pixel_size = ED_view3d_pixel_size(rv3d, center); + } + else { + pixel_size = ED_view3d_pixel_size(rv3d, matrix[3]); + } + + if (pixel_size > FLT_EPSILON) { + + /* Arbitrary, 1.0 is a little too strong though. */ + float color_alpha = 0.75f; + if (rv3d->is_persp) { + /* Scale down the alpha when this is drawn very small, + * since the add shader causes the small size to show too dense & bright. */ + const float relative_pixel_scale = pixel_size / ED_view3d_pixel_size(rv3d, matrix[3]); + if (relative_pixel_scale < 1.0f) { + color_alpha *= max_ff(square_f(relative_pixel_scale), 0.3f); + } + } + + { + /* Extra adjustment when it's near view-aligned as it seems overly bright. */ + float view_vector[3]; + ED_view3d_global_to_vector(rv3d, matrix[3], view_vector); + float view_dot = fabsf(dot_v3v3(matrix[plane_axis], view_vector)); + color_alpha *= max_ff(0.3f, 1.0f - square_f(square_f(1.0f - view_dot))); + } + + const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; + + float final_scale = (scale_mod * pixel_size); + + const int lines_subdiv = 10; + int lines = lines_subdiv; + + float final_scale_fade = final_scale; + final_scale = ceil_power_of_10(final_scale); + + float fac = final_scale_fade / final_scale; + + float color[4] = {1, 1, 1, color_alpha}; + color[3] *= square_f(1.0f - fac); + if (color[3] > 0.0f) { + v3d_cursor_plane_draw_grid( + lines * lines_subdiv, final_scale, final_scale_fade, matrix, plane_axis, color); + } + + color[3] = color_alpha; + /* When the grid is large, we only need the 2x lines in the middle. */ + if (fac < 0.2f) { + lines = 1; + final_scale = final_scale_fade; + } + v3d_cursor_plane_draw_grid(lines, final_scale, final_scale_fade, matrix, plane_axis, color); + } +} + +static void cursor_box_draw(const float dimensions[3], uchar color[4]) +{ + GPUVertFormat *format = immVertexFormat(); + const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + GPU_blend(GPU_BLEND_ALPHA); + GPU_line_smooth(true); + GPU_line_width(1.0f); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4ubv(color); + imm_draw_cube_corners_3d(pos_id, (const float[3]){0.0f, 0.0f, dimensions[2]}, dimensions, 0.15f); + immUnbindProgram(); + + GPU_line_smooth(false); + GPU_blend(GPU_BLEND_NONE); +} + +void ED_view3d_cursor_snap_draw_util(RegionView3D *rv3d, + const float loc_prev[3], + const float loc_curr[3], + const float normal[3], + const uchar color_line[4], + const uchar color_point[4], + const short snap_elem_type) +{ + if (!loc_prev && !loc_curr) { + return; + } + + float view_inv[4][4]; + copy_m4_m4(view_inv, rv3d->viewinv); + + /* The size of the circle is larger than the vertex size. + * This prevents a drawing overlaps the other. */ + float radius = 2.5f * UI_GetThemeValuef(TH_VERTEX_SIZE); + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + + if (loc_curr) { + immUniformColor4ubv(color_point); + imm_drawcircball(loc_curr, ED_view3d_pixel_size(rv3d, loc_curr) * radius, view_inv, pos); + + /* draw normal if needed */ + if (normal) { + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, loc_curr); + immVertex3f(pos, loc_curr[0] + normal[0], loc_curr[1] + normal[1], loc_curr[2] + normal[2]); + immEnd(); + } + } + + if (loc_prev) { + /* Draw an "X" indicating where the previous snap point is. + * This is useful for indicating perpendicular snap. */ + + /* v1, v2, v3 and v4 indicate the coordinates of the ends of the "X". */ + float vx[3], vy[3], v1[3], v2[3], v3[3], v4[4]; + + /* Multiply by 0.75f so that the final size of the "X" is close to that of + * the circle. + * (A closer value is 0.7071f, but we don't need to be exact here). */ + float x_size = 0.75f * radius * ED_view3d_pixel_size(rv3d, loc_prev); + + mul_v3_v3fl(vx, view_inv[0], x_size); + mul_v3_v3fl(vy, view_inv[1], x_size); + + add_v3_v3v3(v1, vx, vy); + sub_v3_v3v3(v2, vx, vy); + negate_v3_v3(v3, v1); + negate_v3_v3(v4, v2); + + add_v3_v3(v1, loc_prev); + add_v3_v3(v2, loc_prev); + add_v3_v3(v3, loc_prev); + add_v3_v3(v4, loc_prev); + + immUniformColor4ubv(color_line); + immBegin(GPU_PRIM_LINES, 4); + immVertex3fv(pos, v3); + immVertex3fv(pos, v1); + immVertex3fv(pos, v4); + immVertex3fv(pos, v2); + immEnd(); + + if (loc_curr && (snap_elem_type & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + /* Dashed line. */ + immUnbindProgram(); + + immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR); + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2], viewport_size[3]); + immUniform1f("dash_width", 6.0f * U.pixelsize); + immUniform1f("dash_factor", 1.0f / 4.0f); + immUniformColor4ubv(color_line); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, loc_prev); + immVertex3fv(pos, loc_curr); + immEnd(); + } + } + + immUnbindProgram(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Event State + * \{ */ + +/* Checks if the current event is different from the one captured in the last update. */ +static bool v3d_cursor_eventstate_has_changed(SnapCursorDataIntern *data_intern, + V3DSnapCursorState *state, + const wmWindowManager *wm, + const int x, + const int y) +{ + if (wm && wm->winactive) { + const wmEvent *event = wm->winactive->eventstate; + if ((x != data_intern->last_eventstate.x) || (y != data_intern->last_eventstate.y)) { + return true; + } +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + if (!(state && (state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE))) { + if ((event->ctrl != data_intern->last_eventstate.ctrl) || + (event->shift != data_intern->last_eventstate.shift) || + (event->alt != data_intern->last_eventstate.alt) || + (event->oskey != data_intern->last_eventstate.oskey)) { + return true; + } + } +#endif + } + return false; +} + +/* Copies the current eventstate. */ +static void v3d_cursor_eventstate_save_xy(SnapCursorDataIntern *cursor_snap, + const int x, + const int y) +{ + cursor_snap->last_eventstate.x = x; + cursor_snap->last_eventstate.y = y; +} + +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK +static bool v3d_cursor_is_snap_invert(SnapCursorDataIntern *data_intern, const wmWindowManager *wm) +{ + if (!wm || !wm->winactive) { + return false; + } + + const wmEvent *event = wm->winactive->eventstate; + if ((event->ctrl == data_intern->last_eventstate.ctrl) && + (event->shift == data_intern->last_eventstate.shift) && + (event->alt == data_intern->last_eventstate.alt) && + (event->oskey == data_intern->last_eventstate.oskey)) { + /* Nothing has changed. */ + return data_intern->snap_data.is_snap_invert; + } + + /* Save new eventstate. */ + data_intern->last_eventstate.ctrl = event->ctrl; + data_intern->last_eventstate.shift = event->shift; + data_intern->last_eventstate.alt = event->alt; + data_intern->last_eventstate.oskey = event->oskey; + + const int snap_on = data_intern->snap_on; + + wmKeyMap *keymap = WM_keymap_active(wm, data_intern->keymap); + for (wmKeyMapItem *kmi = keymap->items.first; kmi; kmi = kmi->next) { + if (kmi->flag & KMI_INACTIVE) { + continue; + } + + if (kmi->propvalue == snap_on) { + if ((ELEM(kmi->type, EVT_LEFTCTRLKEY, EVT_RIGHTCTRLKEY) && event->ctrl) || + (ELEM(kmi->type, EVT_LEFTSHIFTKEY, EVT_RIGHTSHIFTKEY) && event->shift) || + (ELEM(kmi->type, EVT_LEFTALTKEY, EVT_RIGHTALTKEY) && event->alt) || + ((kmi->type == EVT_OSKEY) && event->oskey)) { + return true; + } + } + } + return false; +} +#endif + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Update + * \{ */ + +static short v3d_cursor_snap_elements(V3DSnapCursorState *snap_state, Scene *scene) +{ + short snap_elements = snap_state->snap_elem_force; + if (!snap_elements) { + return scene->toolsettings->snap_mode; + } + return snap_elements; +} + +static void v3d_cursor_snap_context_ensure(Scene *scene) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (data_intern->snap_context_v3d && (data_intern->scene != scene)) { + ED_transform_snap_object_context_destroy(data_intern->snap_context_v3d); + data_intern->snap_context_v3d = NULL; + } + if (data_intern->snap_context_v3d == NULL) { + data_intern->snap_context_v3d = ED_transform_snap_object_context_create(scene, 0); + data_intern->scene = scene; + } +} + +static void v3d_cursor_snap_update(V3DSnapCursorState *state, + const bContext *C, + wmWindowManager *wm, + Depsgraph *depsgraph, + Scene *scene, + ARegion *region, + View3D *v3d, + int x, + int y) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + V3DSnapCursorData *snap_data = &data_intern->snap_data; + v3d_cursor_snap_context_ensure(scene); + + float co[3], no[3], face_nor[3], obmat[4][4], omat[3][3]; + short snap_elem = 0; + int snap_elem_index[3] = {-1, -1, -1}; + int index = -1; + + const float mval_fl[2] = {x, y}; + zero_v3(no); + zero_v3(face_nor); + unit_m3(omat); + + ushort snap_elements = v3d_cursor_snap_elements(state, scene); + data_intern->snap_elem_hidden = 0; + const bool draw_plane = state->draw_plane || state->draw_box; + if (draw_plane && !(snap_elements & SCE_SNAP_MODE_FACE)) { + data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; + snap_elements |= SCE_SNAP_MODE_FACE; + } + + snap_data->is_enabled = true; +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + if (!(state->flag & V3D_SNAPCURSOR_TOGGLE_ALWAYS_TRUE)) { + snap_data->is_snap_invert = v3d_cursor_is_snap_invert(data_intern, wm); + + const ToolSettings *ts = scene->toolsettings; + if (snap_data->is_snap_invert != !(ts->snap_flag & SCE_SNAP)) { + snap_data->is_enabled = false; + if (!draw_plane) { + snap_data->snap_elem = 0; + return; + } + snap_elements = data_intern->snap_elem_hidden = SCE_SNAP_MODE_FACE; + } + } +#endif + + if (snap_elements & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | + SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + float prev_co[3] = {0.0f}; + if (state->prevpoint) { + copy_v3_v3(prev_co, state->prevpoint); + } + else { + snap_elements &= ~SCE_SNAP_MODE_EDGE_PERPENDICULAR; + } + + eSnapSelect snap_select = (state->flag & V3D_SNAPCURSOR_SNAP_ONLY_ACTIVE) ? SNAP_ONLY_ACTIVE : + SNAP_ALL; + + eSnapEditType edit_mode_type = (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_FINAL) ? + SNAP_GEOM_FINAL : + (state->flag & V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE) ? + SNAP_GEOM_CAGE : + SNAP_GEOM_EDIT; + + bool use_occlusion_test = (state->flag & V3D_SNAPCURSOR_OCCLUSION_ALWAYS_TRUE) ? false : true; + + float dist_px = 12.0f * U.pixelsize; + + snap_elem = ED_transform_snap_object_project_view3d_ex( + data_intern->snap_context_v3d, + depsgraph, + region, + v3d, + snap_elements, + &(const struct SnapObjectParams){ + .snap_select = snap_select, + .edit_mode_type = edit_mode_type, + .use_occlusion_test = use_occlusion_test, + }, + mval_fl, + prev_co, + &dist_px, + co, + no, + &index, + NULL, + obmat, + face_nor); + } + + if (is_zero_v3(face_nor)) { + face_nor[state->plane_axis] = 1.0f; + } + + if (draw_plane) { + RegionView3D *rv3d = region->regiondata; + bool orient_surface = snap_elem && (state->plane_orient == V3D_PLACE_ORIENT_SURFACE); + if (orient_surface) { + copy_m3_m4(omat, obmat); + } + else { + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + const int orient_index = BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + const int pivot_point = scene->toolsettings->transform_pivot_point; + ED_transform_calc_orientation_from_type_ex( + scene, view_layer, v3d, region->regiondata, ob, ob, orient_index, pivot_point, omat); + + if (state->use_plane_axis_auto) { + mat3_align_axis_to_v3(omat, state->plane_axis, rv3d->viewinv[2]); + } + } + + /* Non-orthogonal matrices cause the preview and final result not to match. + * + * While making orthogonal doesn't always work well (especially with gimbal orientation for + * e.g.) it's a corner case, without better alternatives as objects don't support shear. */ + orthogonalize_m3(omat, state->plane_axis); + + if (orient_surface) { + if (dot_v3v3(rv3d->viewinv[2], face_nor) < 0.0f) { + negate_v3(face_nor); + } + v3d_cursor_poject_surface_normal(face_nor, obmat, omat); + } + } + + float *co_depth = snap_elem ? co : scene->cursor.location; + snap_elem &= ~data_intern->snap_elem_hidden; + if (snap_elem == 0) { + float plane[4]; + if (state->plane_depth != V3D_PLACE_DEPTH_CURSOR_VIEW) { + const float *plane_normal = omat[state->plane_axis]; + plane_from_point_normal_v3(plane, co_depth, plane_normal); + } + + if ((state->plane_depth == V3D_PLACE_DEPTH_CURSOR_VIEW) || + !ED_view3d_win_to_3d_on_plane(region, plane, mval_fl, true, co)) { + ED_view3d_win_to_3d(v3d, region, co_depth, mval_fl, co); + } + + if (snap_data->is_enabled && (snap_elements & SCE_SNAP_MODE_INCREMENT)) { + v3d_cursor_snap_calc_incremental(scene, v3d, region, state->prevpoint, co); + } + } + else if (snap_elem == SCE_SNAP_MODE_VERTEX) { + snap_elem_index[0] = index; + } + else if (snap_elem & + (SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + snap_elem_index[1] = index; + } + else if (snap_elem == SCE_SNAP_MODE_FACE) { + snap_elem_index[2] = index; + } + + snap_data->snap_elem = snap_elem; + copy_v3_v3(snap_data->loc, co); + copy_v3_v3(snap_data->nor, no); + copy_m4_m4(snap_data->obmat, obmat); + copy_v3_v3_int(snap_data->elem_index, snap_elem_index); + + copy_m3_m3(snap_data->plane_omat, omat); + + v3d_cursor_eventstate_save_xy(data_intern, x, y); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Callbacks + * \{ */ + +static bool v3d_cursor_snap_pool_fn(bContext *C) +{ + if (G.moving) { + return false; + } + + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_VIEW3D) { + return false; + } + + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + RegionView3D *rv3d = region->regiondata; + if (rv3d->rflag & RV3D_NAVIGATING) { + /* Don't draw the cursor while navigating. It can be distracting. */ + return false; + }; + + V3DSnapCursorState *state = ED_view3d_cursor_snap_state_get(); + if (state->region && (state->region != region)) { + /* Some gizmos are still available even when the region is not available. + * We need to disable the cursor in these cases. */ + return false; + } + + return true; +} + +static void v3d_cursor_snap_draw_fn(bContext *C, int x, int y, void *UNUSED(customdata)) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + V3DSnapCursorState *state = ED_view3d_cursor_snap_state_get(); + V3DSnapCursorData *snap_data = &data_intern->snap_data; + + wmWindowManager *wm = CTX_wm_manager(C); + ScrArea *area = CTX_wm_area(C); + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + x -= region->winrct.xmin; + y -= region->winrct.ymin; + if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = DEG_get_input_scene(depsgraph); + View3D *v3d = CTX_wm_view3d(C); + v3d_cursor_snap_update(state, C, wm, depsgraph, scene, region, v3d, x, y); + } + + const bool draw_plane = state->draw_plane || state->draw_box; + if (!snap_data->snap_elem && !draw_plane) { + return; + } + + /* Setup viewport & matrix. */ + RegionView3D *rv3d = region->regiondata; + wmViewport(®ion->winrct); + GPU_matrix_projection_set(rv3d->winmat); + GPU_matrix_set(rv3d->viewmat); + + float matrix[4][4]; + if (draw_plane) { + copy_m4_m3(matrix, snap_data->plane_omat); + copy_v3_v3(matrix[3], snap_data->loc); + + v3d_cursor_plane_draw(rv3d, state->plane_axis, matrix); + } + + if (snap_data->snap_elem && (state->draw_point || state->draw_box)) { + const float *prev_point = (snap_data->snap_elem & SCE_SNAP_MODE_EDGE_PERPENDICULAR) ? + state->prevpoint : + NULL; + + GPU_line_smooth(false); + GPU_line_width(1.0f); + + ED_view3d_cursor_snap_draw_util(rv3d, + prev_point, + snap_data->loc, + NULL, + state->color_line, + state->color_point, + snap_data->snap_elem); + } + + if (state->draw_box) { + GPU_matrix_mul(matrix); + cursor_box_draw(state->box_dimensions, state->color_box); + } + + /* Restore matrix. */ + wmWindowViewport(CTX_wm_window(C)); +} + +/** \} */ + +V3DSnapCursorState *ED_view3d_cursor_snap_state_get(void) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (BLI_listbase_is_empty(&data_intern->state_intern)) { + return &g_data_intern.state_default; + } + return &((SnapStateIntern *)data_intern->state_intern.last)->snap_state; +} + +static void v3d_cursor_snap_activate(void) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + + if (!data_intern->handle) { + if (!data_intern->is_initiated) { + /* Only initiate intern data once. + * TODO: ED_view3d_cursor_snap_init */ + +#ifdef USE_SNAP_DETECT_FROM_KEYMAP_HACK + struct wmKeyConfig *keyconf = ((wmWindowManager *)G.main->wm.first)->defaultconf; + + data_intern->keymap = WM_modalkeymap_find(keyconf, "Generic Gizmo Tweak Modal Map"); + RNA_enum_value_from_id(data_intern->keymap->modal_items, "SNAP_ON", &data_intern->snap_on); +#endif + data_intern->is_initiated = true; + } + + struct wmPaintCursor *pc = WM_paint_cursor_activate( + SPACE_VIEW3D, RGN_TYPE_WINDOW, v3d_cursor_snap_pool_fn, v3d_cursor_snap_draw_fn, NULL); + data_intern->handle = pc; + } +} + +static void v3d_cursor_snap_free(void) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (data_intern->handle) { + if (G_MAIN->wm.first) { + WM_paint_cursor_end(data_intern->handle); + } + data_intern->handle = NULL; + } + if (data_intern->snap_context_v3d) { + ED_transform_snap_object_context_destroy(data_intern->snap_context_v3d); + data_intern->snap_context_v3d = NULL; + } +} + +void ED_view3d_cursor_snap_state_default_set(V3DSnapCursorState *state) +{ + g_data_intern.state_default = *state; +} + +V3DSnapCursorState *ED_view3d_cursor_snap_active(void) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (!data_intern->handle) { + v3d_cursor_snap_activate(); + } + + SnapStateIntern *state_intern = MEM_mallocN(sizeof(*state_intern), __func__); + state_intern->snap_state = g_data_intern.state_default; + BLI_addtail(&g_data_intern.state_intern, state_intern); + + return (V3DSnapCursorState *)&state_intern->snap_state; +} + +void ED_view3d_cursor_snap_deactive(V3DSnapCursorState *state) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (BLI_listbase_is_empty(&data_intern->state_intern)) { + return; + } + + SnapStateIntern *state_intern = STATE_INTERN_GET(state); + BLI_remlink(&data_intern->state_intern, state_intern); + MEM_freeN(state_intern); + if (BLI_listbase_is_empty(&data_intern->state_intern)) { + v3d_cursor_snap_free(); + } +} + +void ED_view3d_cursor_snap_prevpoint_set(V3DSnapCursorState *state, const float prev_point[3]) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (!state) { + state = ED_view3d_cursor_snap_state_get(); + } + if (prev_point) { + copy_v3_v3(data_intern->prevpoint_stack, prev_point); + state->prevpoint = data_intern->prevpoint_stack; + } + else { + state->prevpoint = NULL; + } +} + +V3DSnapCursorData *ED_view3d_cursor_snap_data_get(V3DSnapCursorState *state, + const bContext *C, + const int x, + const int y) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + if (C) { + wmWindowManager *wm = CTX_wm_manager(C); + if (v3d_cursor_eventstate_has_changed(data_intern, state, wm, x, y)) { + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = DEG_get_input_scene(depsgraph); + ScrArea *area = CTX_wm_area(C); + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + View3D *v3d = CTX_wm_view3d(C); + + if (!state) { + state = ED_view3d_cursor_snap_state_get(); + } + v3d_cursor_snap_update(state, C, wm, depsgraph, scene, region, v3d, x, y); + } + } + + return &data_intern->snap_data; +} + +struct SnapObjectContext *ED_view3d_cursor_snap_context_ensure(Scene *scene) +{ + SnapCursorDataIntern *data_intern = &g_data_intern; + v3d_cursor_snap_context_ensure(scene); + return data_intern->snap_context_v3d; +} diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index 86f79718a68..fceb6553cab 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -339,7 +339,18 @@ static void view3d_xr_mirror_setup(const wmWindowManager *wm, } view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, NULL, rect); - /* Reset overridden View3D data */ + /* Set draw flags. */ + SET_FLAG_FROM_TEST(v3d->flag2, + (wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) != 0, + V3D_XR_SHOW_CONTROLLERS); + SET_FLAG_FROM_TEST(v3d->flag2, + (wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) != + 0, + V3D_XR_SHOW_CUSTOM_OVERLAYS); + /* Hide navigation gizmo since it gets distorted if the view matrix has a scale factor. */ + v3d->gizmo_flag |= V3D_GIZMO_HIDE_NAVIGATE; + + /* Reset overridden View3D data. */ v3d->lens = lens_old; } #endif /* WITH_XR_OPENXR */ @@ -608,11 +619,10 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, } GPU_blend(GPU_BLEND_NONE); + immUniformThemeColor3(TH_BACK); + imm_draw_box_wire_2d(shdr_pos, x1i, y1i, x2i, y2i); } - immUniformThemeColor3(TH_BACK); - imm_draw_box_wire_2d(shdr_pos, x1i, y1i, x2i, y2i); - #ifdef VIEW3D_CAMERA_BORDER_HACK if (view3d_camera_border_hack_test == true) { immUniformColor3ubv(view3d_camera_border_hack_col); @@ -781,9 +791,9 @@ static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, immUniformThemeColorShadeAlpha(TH_VIEW_OVERLAY, 100, 255); /* TODO: Was using: - * UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color); - * We'll probably need a new imm_draw_line_roundbox_dashed dor that - though in practice the - * 2.0f round corner effect was nearly not visible anyway... */ + * `UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color);` + * We'll probably need a new imm_draw_line_roundbox_dashed or that - though in practice the + * 2.0f round corner effect was nearly not visible anyway. */ imm_draw_box_wire_2d(shdr_pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); } @@ -1750,7 +1760,7 @@ void ED_view3d_draw_offscreen(Depsgraph *depsgraph, void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, Scene *scene, View3DShading *shading_override, - int drawtype, + eDrawType drawtype, int winx, int winy, uint draw_flags, @@ -1758,6 +1768,7 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, const float winmat[4][4], float clip_start, float clip_end, + bool is_xr_surface, bool is_image_render, bool draw_background, const char *viewname, @@ -1787,23 +1798,37 @@ void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph, v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS; } - if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { - v3d.flag2 |= V3D_SHOW_ANNOTATION; + if ((draw_flags & ~V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS) == V3D_OFSDRAW_NONE) { + v3d.flag2 = V3D_HIDE_OVERLAYS; } - if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) { - v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y; - v3d.grid = 1.0f; - v3d.gridlines = 16; - v3d.gridsubdiv = 10; - - /* Show grid, disable other overlays (set all available _HIDE_ flags). */ + else { + if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) { + v3d.flag2 |= V3D_SHOW_ANNOTATION; + } + if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) { + v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y; + v3d.grid = 1.0f; + v3d.gridlines = 16; + v3d.gridsubdiv = 10; + } + if (draw_flags & V3D_OFSDRAW_SHOW_SELECTION) { + v3d.flag |= V3D_SELECT_OUTLINE; + } + if (draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) { + v3d.flag2 |= V3D_XR_SHOW_CONTROLLERS; + } + if (draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) { + v3d.flag2 |= V3D_XR_SHOW_CUSTOM_OVERLAYS; + } + /* Disable other overlays (set all available _HIDE_ flags). */ v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT | V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES | V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS; v3d.flag |= V3D_HIDE_HELPLINES; } - else { - v3d.flag2 = V3D_HIDE_OVERLAYS; + + if (is_xr_surface) { + v3d.flag |= V3D_XR_SESSION_SURFACE; } rv3d.persp = RV3D_PERSP; @@ -2181,13 +2206,17 @@ static void validate_object_select_id(struct Depsgraph *depsgraph, * Calling this function should be avoided during interactive drawing. */ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void *data) { - DefaultTextureList *dtxl = (DefaultTextureList *)GPU_viewport_texture_list_get(viewport); + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); - GPUFrameBuffer *tmp_fb = GPU_framebuffer_create(__func__); - GPU_framebuffer_texture_attach(tmp_fb, dtxl->depth, 0, 0); - GPU_framebuffer_bind(tmp_fb); + GPUFrameBuffer *depth_read_fb = NULL; + GPU_framebuffer_ensure_config(&depth_read_fb, + { + GPU_ATTACHMENT_TEXTURE(depth_tx), + GPU_ATTACHMENT_NONE, + }); - GPU_framebuffer_read_depth(tmp_fb, + GPU_framebuffer_bind(depth_read_fb); + GPU_framebuffer_read_depth(depth_read_fb, rect->xmin, rect->ymin, BLI_rcti_size_x(rect), @@ -2196,7 +2225,7 @@ static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void data); GPU_framebuffer_restore(); - GPU_framebuffer_free(tmp_fb); + GPU_framebuffer_free(depth_read_fb); } void ED_view3d_select_id_validate(ViewContext *vc) @@ -2266,13 +2295,17 @@ static ViewDepths *view3d_depths_create(ARegion *region) ViewDepths *d = MEM_callocN(sizeof(ViewDepths), "ViewDepths"); d->w = region->winx; d->h = region->winy; - d->depths = MEM_mallocN(sizeof(float) * d->w * d->h, "View depths"); { GPUViewport *viewport = WM_draw_region_get_viewport(region); - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); - GPU_framebuffer_read_depth(fbl->depth_only_fb, 0, 0, d->w, d->h, GPU_DATA_FLOAT, d->depths); - + GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport); + uint32_t *int_depths = GPU_texture_read(depth_tx, GPU_DATA_UINT_24_8, 0); + d->depths = (float *)int_depths; + /* Convert in-place. */ + int pixel_count = GPU_texture_width(depth_tx) * GPU_texture_height(depth_tx); + for (int i = 0; i < pixel_count; i++) { + d->depths[i] = (int_depths[i] >> 8u) / (float)0xFFFFFF; + } /* Assumed to be this as they are never changed. */ d->depth_range[0] = 0.0; d->depth_range[1] = 1.0; diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index 8ed134c7fd1..b85b424405e 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -34,10 +34,12 @@ #include "DNA_gpencil_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_world_types.h" #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_dial_2d.h" #include "BLI_math.h" #include "BLI_utildefines.h" @@ -45,7 +47,6 @@ #include "BKE_armature.h" #include "BKE_camera.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_gpencil_geom.h" #include "BKE_layer.h" #include "BKE_lib_id.h" @@ -55,6 +56,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_screen.h" +#include "BKE_vfont.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" @@ -189,7 +191,7 @@ typedef struct ViewOpsData { float dist; float camzoom; float quat[4]; - /** #wmEvent.x, y. */ + /** #wmEvent.xy. */ int event_xy[2]; /** Offset to use when #VIEWOPS_FLAG_USE_MOUSE_INIT is not set. * so we can simulate pressing in the middle of the screen. */ @@ -210,6 +212,9 @@ typedef struct ViewOpsData { * If we want the value before running the operator, add a separate member. */ char persp; + + /** Used for roll */ + Dial *dial; } init; /** Previous state (previous modal event handled). */ @@ -468,17 +473,16 @@ static void viewops_data_create(bContext *C, vod->init.dist = rv3d->dist; vod->init.camzoom = rv3d->camzoom; copy_qt_qt(vod->init.quat, rv3d->viewquat); - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; - vod->init.event_xy[1] = vod->prev.event_xy[1] = event->y; + copy_v2_v2_int(vod->init.event_xy, event->xy); + copy_v2_v2_int(vod->prev.event_xy, event->xy); if (viewops_flag & VIEWOPS_FLAG_USE_MOUSE_INIT) { - vod->init.event_xy_offset[0] = 0; - vod->init.event_xy_offset[1] = 0; + zero_v2_int(vod->init.event_xy_offset); } else { /* Simulate the event starting in the middle of the region. */ - vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->x; - vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->y; + vod->init.event_xy_offset[0] = BLI_rcti_cent_x(&vod->region->winrct) - event->xy[0]; + vod->init.event_xy_offset[1] = BLI_rcti_cent_y(&vod->region->winrct) - event->xy[1]; } vod->init.event_type = event->type; @@ -543,10 +547,9 @@ static void viewops_data_create(bContext *C, ED_view3d_win_to_vector(vod->region, (const float[2]){UNPACK2(event->mval)}, vod->init.mousevec); { - const int event_xy_offset[2] = { - event->x + vod->init.event_xy_offset[0], - event->y + vod->init.event_xy_offset[1], - }; + int event_xy_offset[2]; + add_v2_v2v2_int(event_xy_offset, event->xy, vod->init.event_xy_offset); + /* For rotation with trackball rotation. */ calctrackballvec(&vod->region->winrct, event_xy_offset, vod->init.trackvec); } @@ -577,6 +580,10 @@ static void viewops_data_free(bContext *C, wmOperator *op) WM_event_remove_timer(CTX_wm_manager(C), vod->timer->win, vod->timer); } + if (vod->init.dial) { + MEM_freeN(vod->init.dial); + } + MEM_freeN(vod); op->customdata = NULL; } @@ -666,7 +673,6 @@ static void viewrotate_apply_snap(ViewOpsData *vod) float zaxis_best[3]; int x, y, z; bool found = false; - bool is_axis_aligned = false; invert_qt_qt_normalized(viewquat_inv, vod->curr.viewquat); @@ -684,10 +690,6 @@ static void viewrotate_apply_snap(ViewOpsData *vod) if (angle_normalized_v3v3(zaxis_test, zaxis) < axis_limit) { copy_v3_v3(zaxis_best, zaxis_test); found = true; - - if (abs(x) + abs(y) + abs(z) == 1) { - is_axis_aligned = true; - } } } } @@ -760,7 +762,7 @@ static void viewrotate_apply_snap(ViewOpsData *vod) viewrotate_apply_dyn_ofs(vod, rv3d->viewquat); if (U.uiflag & USER_AUTOPERSP) { - if (is_axis_aligned) { + if (RV3D_VIEW_IS_AXIS(rv3d->view)) { if (rv3d->persp == RV3D_PERSP) { rv3d->persp = RV3D_ORTHO; } @@ -946,7 +948,7 @@ static int viewrotate_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (event_code == VIEW_APPLY) { - viewrotate_apply(vod, &event->x); + viewrotate_apply(vod, event->xy); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -997,18 +999,16 @@ static int viewrotate_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { if (event->is_direction_inverted) { - event_xy[0] = 2 * event->x - event->prevx; - event_xy[1] = 2 * event->y - event->prevy; + event_xy[0] = 2 * event->xy[0] - event->prev_xy[0]; + event_xy[1] = 2 * event->xy[1] - event->prev_xy[1]; } else { - event_xy[0] = event->prevx; - event_xy[1] = event->prevy; + copy_v2_v2_int(event_xy, event->prev_xy); } } else { /* MOUSEROTATE performs orbital rotation, so y axis delta is set to 0 */ - event_xy[0] = event->prevx; - event_xy[1] = event->y; + copy_v2_v2_int(event_xy, event->prev_xy); } viewrotate_apply(vod, event_xy); @@ -1790,7 +1790,7 @@ static int viewmove_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (event_code == VIEW_APPLY) { - viewmove_apply(vod, event->x, event->y); + viewmove_apply(vod, event->xy[0], event->xy[1]); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -1835,7 +1835,8 @@ static int viewmove_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEPAN) { /* invert it, trackpad scroll follows same principle as 2d windows this way */ - viewmove_apply(vod, 2 * event->x - event->prevx, 2 * event->y - event->prevy); + viewmove_apply( + vod, 2 * event->xy[0] - event->prev_xy[0], 2 * event->xy[1] - event->prev_xy[1]); viewops_data_free(C, op); @@ -1915,7 +1916,7 @@ void viewzoom_modal_keymap(wmKeyConfig *keyconf) /** * \param zoom_xy: Optionally zoom to window location - * (coords compatible w/ #wmEvent.x, y). Use when not NULL. + * (coords compatible w/ #wmEvent.xy). Use when not NULL. */ static void view_zoom_to_window_xy_camera(Scene *scene, Depsgraph *depsgraph, @@ -1968,7 +1969,7 @@ static void view_zoom_to_window_xy_camera(Scene *scene, /** * \param zoom_xy: Optionally zoom to window location - * (coords compatible w/ #wmEvent.x, y). Use when not NULL. + * (coords compatible w/ #wmEvent.xy). Use when not NULL. */ static void view_zoom_to_window_xy_3d(ARegion *region, float dfac, const int zoom_xy[2]) { @@ -2240,7 +2241,7 @@ static int viewzoom_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event_code == VIEW_APPLY) { const bool use_cursor_init = RNA_boolean_get(op->ptr, "use_cursor_init"); viewzoom_apply(vod, - &event->x, + event->xy, (eViewZoom_Style)U.viewzoom, (U.uiflag & USER_ZOOM_INVERT) != 0, (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); @@ -2365,8 +2366,8 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* if one or the other zoom position aren't set, set from event */ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { - RNA_int_set(op->ptr, "mx", event->x); - RNA_int_set(op->ptr, "my", event->y); + RNA_int_set(op->ptr, "mx", event->xy[0]); + RNA_int_set(op->ptr, "my", event->xy[1]); } if (RNA_struct_property_is_set(op->ptr, "delta")) { @@ -2376,15 +2377,15 @@ static int viewzoom_invoke(bContext *C, wmOperator *op, const wmEvent *event) if (ELEM(event->type, MOUSEZOOM, MOUSEPAN)) { if (U.uiflag & USER_ZOOM_HORIZ) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; } else { /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ - vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->x - - event->prevx; + vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - + event->prev_xy[0]; } viewzoom_apply(vod, - &event->prevx, + event->prev_xy, USER_ZOOM_DOLLY, (U.uiflag & USER_ZOOM_INVERT) != 0, (use_cursor_init && (U.uiflag & USER_ZOOM_TO_MOUSEPOS))); @@ -2563,7 +2564,7 @@ static int viewdolly_modal(bContext *C, wmOperator *op, const wmEvent *event) } if (event_code == VIEW_APPLY) { - viewdolly_apply(vod, &event->x, (U.uiflag & USER_ZOOM_INVERT) != 0); + viewdolly_apply(vod, event->xy, (U.uiflag & USER_ZOOM_INVERT) != 0); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -2679,8 +2680,8 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* if one or the other zoom position aren't set, set from event */ if (!RNA_struct_property_is_set(op->ptr, "mx") || !RNA_struct_property_is_set(op->ptr, "my")) { - RNA_int_set(op->ptr, "mx", event->x); - RNA_int_set(op->ptr, "my", event->y); + RNA_int_set(op->ptr, "mx", event->xy[0]); + RNA_int_set(op->ptr, "my", event->xy[1]); } if (RNA_struct_property_is_set(op->ptr, "delta")) { @@ -2697,14 +2698,14 @@ static int viewdolly_invoke(bContext *C, wmOperator *op, const wmEvent *event) /* Bypass Zoom invert flag for track pads (pass false always) */ if (U.uiflag & USER_ZOOM_HORIZ) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; } else { /* Set y move = x move as MOUSEZOOM uses only x axis to pass magnification value */ - vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->x - - event->prevx; + vod->init.event_xy[1] = vod->prev.event_xy[1] = vod->init.event_xy[1] + event->xy[0] - + event->prev_xy[0]; } - viewdolly_apply(vod, &event->prevx, (U.uiflag & USER_ZOOM_INVERT) == 0); + viewdolly_apply(vod, event->prev_xy, (U.uiflag & USER_ZOOM_INVERT) == 0); viewops_data_free(C, op); return OPERATOR_FINISHED; @@ -4352,18 +4353,9 @@ static void view_roll_angle( rv3d->view = RV3D_VIEW_USER; } -static void viewroll_apply(ViewOpsData *vod, int x, int UNUSED(y)) +static void viewroll_apply(ViewOpsData *vod, int x, int y) { - float angle = 0.0; - - { - float len1, len2, tot; - - tot = vod->region->winrct.xmax - vod->region->winrct.xmin; - len1 = (vod->region->winrct.xmax - x) / tot; - len2 = (vod->region->winrct.xmax - vod->init.event_xy[0]) / tot; - angle = (len1 - len2) * (float)M_PI * 4.0f; - } + float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y}); if (angle != 0.0f) { view_roll_angle(vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle); @@ -4409,12 +4401,19 @@ static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event) break; } } + else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) { + /* Note this does not remove auto-keys on locked cameras. */ + copy_qt_qt(vod->rv3d->viewquat, vod->init.quat); + ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d); + viewops_data_free(C, op); + return OPERATOR_CANCELLED; + } else if (event->type == vod->init.event_type && event->val == KM_RELEASE) { event_code = VIEW_CONFIRM; } if (event_code == VIEW_APPLY) { - viewroll_apply(vod, event->x, event->y); + viewroll_apply(vod, event->xy[0], event->xy[1]); if (ED_screen_animation_playing(CTX_wm_manager(C))) { use_autokey = true; } @@ -4517,6 +4516,9 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) viewops_data_alloc(C, op); viewops_data_create(C, op, event, viewops_flag_from_prefs()); vod = op->customdata; + vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct), + BLI_rcti_cent_y(&vod->region->winrct)}, + FLT_EPSILON); ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region); @@ -4525,8 +4527,8 @@ static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event) negate_v3(vod->init.mousevec); if (event->type == MOUSEROTATE) { - vod->init.event_xy[0] = vod->prev.event_xy[0] = event->x; - viewroll_apply(vod, event->prevx, event->prevy); + vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0]; + viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]); viewops_data_free(C, op); return OPERATOR_FINISHED; @@ -4864,6 +4866,60 @@ void VIEW3D_OT_background_image_remove(wmOperatorType *ot) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Drop World Operator + * \{ */ + +static int drop_world_exec(bContext *C, wmOperator *op) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + + char name[MAX_ID_NAME - 2]; + + RNA_string_get(op->ptr, "name", name); + World *world = (World *)BKE_libblock_find_name(bmain, ID_WO, name); + if (world == NULL) { + return OPERATOR_CANCELLED; + } + + id_us_min((ID *)scene->world); + id_us_plus(&world->id); + scene->world = world; + + DEG_id_tag_update(&scene->id, 0); + DEG_relations_tag_update(bmain); + + WM_event_add_notifier(C, NC_SCENE | ND_WORLD, scene); + + return OPERATOR_FINISHED; +} + +static bool drop_world_poll(bContext *C) +{ + return ED_operator_scene_editable(C); +} + +void VIEW3D_OT_drop_world(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Drop World"; + ot->description = "Drop a world into the scene"; + ot->idname = "VIEW3D_OT_drop_world"; + + /* api callbacks */ + ot->exec = drop_world_exec; + ot->poll = drop_world_poll; + + /* flags */ + ot->flag = OPTYPE_UNDO | OPTYPE_INTERNAL; + + /* properties */ + RNA_def_string(ot->srna, "name", "World", MAX_ID_NAME - 2, "Name", "World to assign"); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name View Clipping Planes Operator * @@ -5036,14 +5092,15 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, float ray_no[3]; float ray_co[3]; - struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create_view3d( - scene, 0, region, v3d); + struct SnapObjectContext *snap_context = ED_transform_snap_object_context_create(scene, 0); float obmat[4][4]; Object *ob_dummy = NULL; float dist_px = 0; if (ED_transform_snap_object_project_view3d_ex(snap_context, CTX_data_ensure_evaluated_depsgraph(C), + region, + v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, @@ -5057,7 +5114,8 @@ void ED_view3d_cursor3d_position_rotation(bContext *C, ray_no, NULL, &ob_dummy, - obmat) != 0) { + obmat, + NULL) != 0) { if (use_depth) { copy_v3_v3(cursor_co, ray_co); } diff --git a/source/blender/editors/space_view3d/view3d_gizmo_preselect.c b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c index 441182d7a5f..918ecb14752 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_preselect.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_preselect.c @@ -58,7 +58,7 @@ void VIEW3D_GGT_mesh_preselect_elem(wmGizmoGroupType *gzgt) gzgt->name = "Mesh Preselect Element"; gzgt->idname = "VIEW3D_GGT_mesh_preselect_elem"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; @@ -95,7 +95,7 @@ void VIEW3D_GGT_mesh_preselect_edgering(wmGizmoGroupType *gzgt) gzgt->name = "Mesh Preselect Edge Ring"; gzgt->idname = "VIEW3D_GGT_mesh_preselect_edgering"; - gzgt->flag = WM_GIZMOGROUPTYPE_3D; + gzgt->flag = WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP | WM_GIZMOGROUPTYPE_3D; gzgt->gzmap_params.spaceid = SPACE_VIEW3D; gzgt->gzmap_params.regionid = RGN_TYPE_WINDOW; diff --git a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c index f8278edbcae..34e3b808b50 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_ruler.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_ruler.c @@ -332,7 +332,8 @@ static void view3d_ruler_item_project(RulerInfo *ruler_info, float r_co[3], cons /** * Use for mouse-move events. */ -static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, +static bool view3d_ruler_item_mousemove(const bContext *C, + struct Depsgraph *depsgraph, RulerInfo *ruler_info, RulerItem *ruler_item, const int mval[2], @@ -356,8 +357,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, if (do_thickness && inter->co_index != 1) { Scene *scene = DEG_get_input_scene(depsgraph); View3D *v3d = ruler_info->area->spacedata.first; - SnapObjectContext *snap_context = ED_gizmotypes_snap_3d_context_ensure( - scene, ruler_info->region, v3d, snap_gizmo); + SnapObjectContext *snap_context = ED_gizmotypes_snap_3d_context_ensure(scene, snap_gizmo); const float mval_fl[2] = {UNPACK2(mval)}; float ray_normal[3]; float ray_start[3]; @@ -367,6 +367,8 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, if (ED_transform_snap_object_project_view3d(snap_context, depsgraph, + ruler_info->region, + v3d, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, @@ -382,6 +384,7 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, madd_v3_v3v3fl(ray_start, co, ray_normal, eps_bias); ED_transform_snap_object_project_ray(snap_context, depsgraph, + v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, .edit_mode_type = SNAP_GEOM_CAGE, @@ -399,7 +402,6 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, #endif { View3D *v3d = ruler_info->area->spacedata.first; - const float mval_fl[2] = {UNPACK2(mval)}; float *prev_point = NULL; if (inter->co_index != 1) { @@ -418,11 +420,8 @@ static bool view3d_ruler_item_mousemove(struct Depsgraph *depsgraph, snap_gizmo->ptr, ruler_info->snap_data.prop_prevpoint, prev_point); } - ED_gizmotypes_snap_3d_update( - snap_gizmo, depsgraph, ruler_info->region, v3d, ruler_info->wm, mval_fl); - if (ED_gizmotypes_snap_3d_is_enabled(snap_gizmo)) { - ED_gizmotypes_snap_3d_data_get(snap_gizmo, co, NULL, NULL, NULL); + ED_gizmotypes_snap_3d_data_get(C, snap_gizmo, co, NULL, NULL, NULL); } #ifdef USE_AXIS_CONSTRAINTS @@ -1048,7 +1047,8 @@ static int gizmo_ruler_modal(bContext *C, if (do_cursor_update) { if (ruler_info->state == RULER_STATE_DRAG) { struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - if (view3d_ruler_item_mousemove(depsgraph, + if (view3d_ruler_item_mousemove(C, + depsgraph, ruler_info, ruler_item, event->mval, @@ -1115,7 +1115,8 @@ static int gizmo_ruler_invoke(bContext *C, wmGizmo *gz, const wmEvent *event) /* update the new location */ struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); - view3d_ruler_item_mousemove(depsgraph, + view3d_ruler_item_mousemove(C, + depsgraph, ruler_info, ruler_item_pick, event->mval, @@ -1234,7 +1235,7 @@ static void WIDGETGROUP_ruler_setup(const bContext *C, wmGizmoGroup *gzgroup) (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */ SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT)); - ED_gizmotypes_snap_3d_flag_set(gizmo, ED_SNAPGIZMO_SNAP_EDIT_GEOM_CAGE); + ED_gizmotypes_snap_3d_flag_set(gizmo, V3D_SNAPCURSOR_SNAP_EDIT_GEOM_CAGE); WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}); wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_ruler_add", true); @@ -1319,7 +1320,8 @@ static int view3d_ruler_add_invoke(bContext *C, wmOperator *op, const wmEvent *e struct Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); /* snap the first point added, not essential but handy */ inter->co_index = 0; - view3d_ruler_item_mousemove(depsgraph, + view3d_ruler_item_mousemove(C, + depsgraph, ruler_info, ruler_item, event->mval, diff --git a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c index 0e0d59764e5..1de08e75f80 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_tool_generic.c @@ -140,13 +140,10 @@ static void WIDGETGROUP_tool_generic_refresh(const bContext *C, wmGizmoGroup *gz ToolSettings *ts = CTX_data_tool_settings(C); if (ts->workspace_tool_type != SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = false; WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true); return; } - gzgroup->use_fallback_keymap = true; - /* skip, we don't draw anything anyway */ { int orientation; diff --git a/source/blender/editors/space_view3d/view3d_intern.h b/source/blender/editors/space_view3d/view3d_intern.h index ab80928e0c1..a21fc006b02 100644 --- a/source/blender/editors/space_view3d/view3d_intern.h +++ b/source/blender/editors/space_view3d/view3d_intern.h @@ -80,6 +80,7 @@ void VIEW3D_OT_view_persportho(struct wmOperatorType *ot); void VIEW3D_OT_navigate(struct wmOperatorType *ot); void VIEW3D_OT_background_image_add(struct wmOperatorType *ot); void VIEW3D_OT_background_image_remove(struct wmOperatorType *ot); +void VIEW3D_OT_drop_world(struct wmOperatorType *ot); void VIEW3D_OT_view_orbit(struct wmOperatorType *ot); void VIEW3D_OT_view_roll(struct wmOperatorType *ot); void VIEW3D_OT_clip_border(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_view3d/view3d_navigate_fly.c b/source/blender/editors/space_view3d/view3d_navigate_fly.c index 5752837c40f..f48e436e014 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_fly.c +++ b/source/blender/editors/space_view3d/view3d_navigate_fly.c @@ -539,7 +539,7 @@ static void flyEvent(FlyInfo *fly, const wmEvent *event) /* Speed adjusting with mouse-pan (track-pad). */ case FLY_MODAL_SPEED: { - float fac = 0.02f * (event->prevy - event->y); + float fac = 0.02f * (event->prev_xy[1] - event->xy[1]); /* allowing to brake immediate */ if (fac > 0.0f && fly->speed < 0.0f) { diff --git a/source/blender/editors/space_view3d/view3d_navigate_walk.c b/source/blender/editors/space_view3d/view3d_navigate_walk.c index 1ac241013ed..83b8c04acb6 100644 --- a/source/blender/editors/space_view3d/view3d_navigate_walk.c +++ b/source/blender/editors/space_view3d/view3d_navigate_walk.c @@ -423,6 +423,7 @@ static bool walk_floor_distance_get(RegionView3D *rv3d, ret = ED_transform_snap_object_project_ray( walk->snap_context, walk->depsgraph, + walk->v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, /* Avoid having to convert the edit-mesh to a regular mesh. */ @@ -464,6 +465,7 @@ static bool walk_ray_cast(RegionView3D *rv3d, ret = ED_transform_snap_object_project_ray(walk->snap_context, walk->depsgraph, + walk->v3d, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, }, @@ -602,8 +604,7 @@ static bool initWalkInfo(bContext *C, WalkInfo *walk, wmOperator *op) walk->rv3d->rflag |= RV3D_NAVIGATING; - walk->snap_context = ED_transform_snap_object_context_create_view3d( - walk->scene, 0, walk->region, walk->v3d); + walk->snap_context = ED_transform_snap_object_context_create(walk->scene, 0); walk->v3d_camera_control = ED_view3d_cameracontrol_acquire( walk->depsgraph, walk->scene, walk->v3d, walk->rv3d); diff --git a/source/blender/editors/space_view3d/view3d_ops.c b/source/blender/editors/space_view3d/view3d_ops.c index 56dedbbdbb2..eb8c043319c 100644 --- a/source/blender/editors/space_view3d/view3d_ops.c +++ b/source/blender/editors/space_view3d/view3d_ops.c @@ -169,6 +169,7 @@ void view3d_operatortypes(void) WM_operatortype_append(VIEW3D_OT_view_persportho); WM_operatortype_append(VIEW3D_OT_background_image_add); WM_operatortype_append(VIEW3D_OT_background_image_remove); + WM_operatortype_append(VIEW3D_OT_drop_world); WM_operatortype_append(VIEW3D_OT_view_selected); WM_operatortype_append(VIEW3D_OT_view_lock_clear); WM_operatortype_append(VIEW3D_OT_view_lock_to_active); diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index aa3bf46d2e5..7ad512fd029 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -25,17 +25,7 @@ #include "MEM_guardedalloc.h" -#include "DNA_collection_types.h" -#include "DNA_object_types.h" -#include "DNA_scene_types.h" -#include "DNA_vfont_types.h" - -#include "BLI_math.h" -#include "BLI_utildefines.h" - #include "BKE_context.h" -#include "BKE_global.h" -#include "BKE_main.h" #include "RNA_access.h" #include "RNA_define.h" @@ -43,29 +33,23 @@ #include "WM_api.h" #include "WM_toolsystem.h" -#include "WM_types.h" -#include "ED_gizmo_library.h" #include "ED_gizmo_utils.h" #include "ED_screen.h" #include "ED_space_api.h" -#include "ED_transform.h" -#include "ED_transform_snap_object_context.h" #include "ED_view3d.h" #include "UI_resources.h" -#include "GPU_batch.h" #include "GPU_immediate.h" -#include "GPU_matrix.h" -#include "GPU_state.h" #include "view3d_intern.h" -static const char *view3d_gzgt_placement_id = "VIEW3D_GGT_placement"; +#define SNAP_MODE_GEOM \ + (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | \ + SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT) -static void preview_plane_cursor_setup(wmGizmoGroup *gzgroup); -static void preview_plane_cursor_visible_set(wmGizmoGroup *gzgroup, bool do_draw); +static const char *view3d_gzgt_placement_id = "VIEW3D_GGT_placement"; /** * Dot products below this will be considered view aligned. @@ -96,17 +80,6 @@ enum ePlace_Aspect { PLACE_ASPECT_FIXED = 2, }; -enum ePlace_Depth { - PLACE_DEPTH_SURFACE = 1, - PLACE_DEPTH_CURSOR_PLANE = 2, - PLACE_DEPTH_CURSOR_VIEW = 3, -}; - -enum ePlace_Orient { - PLACE_ORIENT_SURFACE = 1, - PLACE_ORIENT_DEFAULT = 2, -}; - enum ePlace_SnapTo { PLACE_SNAP_TO_GEOMETRY = 1, PLACE_SNAP_TO_DEFAULT = 2, @@ -178,6 +151,7 @@ struct InteractivePlaceData { float matrix_orient[3][3]; int orient_axis; + V3DSnapCursorState *snap_state; bool use_snap, is_snap_found, is_snap_invert; float snap_co[3]; @@ -198,9 +172,6 @@ struct InteractivePlaceData { /** When activated without a tool. */ bool wait_for_input; - /** Optional snap gizmo, needed for snapping. */ - wmGizmo *snap_gizmo; - enum ePlace_SnapTo snap_to; }; @@ -251,127 +222,8 @@ static int dot_v3_array_find_max_index(const float dirs[][3], return index_found; } -/** - * Re-order \a mat so \a axis_align uses its own axis which is closest to \a v. - */ -static bool mat3_align_axis_to_v3(float mat[3][3], const int axis_align, const float v[3]) -{ - float dot_best = -1.0f; - int axis_found = axis_align; - for (int i = 0; i < 3; i++) { - const float dot_test = fabsf(dot_v3v3(mat[i], v)); - if (dot_test > dot_best) { - dot_best = dot_test; - axis_found = i; - } - } - - if (axis_align != axis_found) { - float tmat[3][3]; - copy_m3_m3(tmat, mat); - const int offset = mod_i(axis_found - axis_align, 3); - for (int i = 0; i < 3; i++) { - copy_v3_v3(mat[i], tmat[(i + offset) % 3]); - } - return true; - } - return false; -} - -/* On-screen snap distance. */ -#define MVAL_MAX_PX_DIST 12.0f - -static bool idp_snap_point_from_gizmo_ex(wmGizmo *gz, const char *prop_id, float r_location[3]) -{ - if (gz->state & WM_GIZMO_STATE_HIGHLIGHT) { - PropertyRNA *prop_location = RNA_struct_find_property(gz->ptr, prop_id); - RNA_property_float_get_array(gz->ptr, prop_location, r_location); - return true; - } - return false; -} - -static bool idp_snap_point_from_gizmo(wmGizmo *gz, float r_location[3]) -{ - return idp_snap_point_from_gizmo_ex(gz, "location", r_location); -} - -static bool idp_snap_normal_from_gizmo(wmGizmo *gz, float r_normal[3]) -{ - return idp_snap_point_from_gizmo_ex(gz, "normal", r_normal); -} - -/** - * Calculate a 3x3 orientation matrix from the surface under the cursor. - */ -static bool idp_poject_surface_normal(SnapObjectContext *snap_context, - struct Depsgraph *depsgraph, - const float mval_fl[2], - const float mat_fallback[3][3], - const float normal_fallback[3], - float r_mat[3][3]) -{ - bool success = false; - float normal[3] = {0.0f}; - float co_dummy[3]; - /* We could use the index to get the orientation from the face. */ - Object *ob_snap; - float obmat[4][4]; - - if (ED_transform_snap_object_project_view3d_ex(snap_context, - depsgraph, - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - .edit_mode_type = SNAP_GEOM_EDIT, - }, - mval_fl, - NULL, - NULL, - co_dummy, - normal, - NULL, - &ob_snap, - obmat)) { - /* pass */ - } - else if (normal_fallback != NULL) { - copy_m4_m3(obmat, mat_fallback); - copy_v3_v3(normal, normal_fallback); - } - - if (!is_zero_v3(normal)) { - float mat[3][3]; - copy_m3_m4(mat, obmat); - normalize_m3(mat); - - float dot_best = fabsf(dot_v3v3(mat[0], normal)); - int i_best = 0; - for (int i = 1; i < 3; i++) { - float dot_test = fabsf(dot_v3v3(mat[i], normal)); - if (dot_test > dot_best) { - i_best = i; - dot_best = dot_test; - } - } - if (dot_v3v3(mat[i_best], normal) < 0.0f) { - negate_v3(mat[(i_best + 1) % 3]); - negate_v3(mat[(i_best + 2) % 3]); - } - copy_v3_v3(mat[i_best], normal); - orthogonalize_m3(mat, i_best); - normalize_m3(mat); - - copy_v3_v3(r_mat[0], mat[(i_best + 1) % 3]); - copy_v3_v3(r_mat[1], mat[(i_best + 2) % 3]); - copy_v3_v3(r_mat[2], mat[i_best]); - success = true; - } - - return success; -} - -static wmGizmoGroup *idp_gizmogroup_from_region(ARegion *region) +static UNUSED_FUNCTION_WITH_RETURN_TYPE(wmGizmoGroup *, + idp_gizmogroup_from_region)(ARegion *region) { wmGizmoMap *gzmap = region->gizmo_map; return gzmap ? WM_gizmomap_group_find(gzmap, view3d_gzgt_placement_id) : NULL; @@ -413,20 +265,6 @@ static bool idp_snap_calc_incremental( return true; } -static void idp_snap_gizmo_update_snap_elements(Scene *scene, - enum ePlace_SnapTo snap_to, - wmGizmo *gizmo) -{ - const int snap_mode = - (snap_to == PLACE_SNAP_TO_GEOMETRY) ? - (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | - /* SCE_SNAP_MODE_VOLUME | SCE_SNAP_MODE_GRID | SCE_SNAP_MODE_INCREMENT | */ - SCE_SNAP_MODE_EDGE_PERPENDICULAR | SCE_SNAP_MODE_EDGE_MIDPOINT) : - scene->toolsettings->snap_mode; - - RNA_enum_set(gizmo->ptr, "snap_elements_force", snap_mode); -} - /** \} */ /* -------------------------------------------------------------------- */ @@ -857,151 +695,25 @@ static void draw_primitive_view(const struct bContext *C, ARegion *UNUSED(region * Use by both the operator and placement cursor. * \{ */ -static void view3d_interactive_add_calc_plane(bContext *C, - Scene *scene, - View3D *v3d, - ARegion *region, - const float mval_fl[2], - wmGizmo *snap_gizmo, - const enum ePlace_SnapTo snap_to, - const enum ePlace_Depth plane_depth, - const enum ePlace_Orient plane_orient, - const int plane_axis, - const bool plane_axis_auto, - float r_co_src[3], - float r_matrix_orient[3][3]) +static bool view3d_interactive_add_calc_snap(bContext *UNUSED(C), + const wmEvent *UNUSED(event), + float r_co_src[3], + float r_matrix_orient[3][3], + bool *r_is_enabled, + bool *r_is_snap_invert) { - const RegionView3D *rv3d = region->regiondata; - ED_transform_calc_orientation_from_type(C, r_matrix_orient); - - /* Non-orthogonal matrices cause the preview and final result not to match. - * - * While making orthogonal doesn't always work well (especially with gimbal orientation for e.g.) - * it's a corner case, without better alternatives as objects don't support shear. */ - orthogonalize_m3(r_matrix_orient, plane_axis); - - SnapObjectContext *snap_context = NULL; - bool snap_context_free = false; - - /* Set the orientation. */ - if ((plane_orient == PLACE_ORIENT_SURFACE) || (plane_depth == PLACE_DEPTH_SURFACE)) { - snap_context = (snap_gizmo ? - ED_gizmotypes_snap_3d_context_ensure(scene, region, v3d, snap_gizmo) : - NULL); - if (snap_context == NULL) { - snap_context = ED_transform_snap_object_context_create_view3d(scene, 0, region, v3d); - snap_context_free = true; - } + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(NULL, NULL, 0, 0); + copy_v3_v3(r_co_src, snap_data->loc); + if (r_matrix_orient) { + copy_m3_m3(r_matrix_orient, snap_data->plane_omat); } - - if (plane_orient == PLACE_ORIENT_SURFACE) { - bool found_surface_or_normal = false; - float matrix_orient_surface[3][3]; - - /* Use the snap normal as a fallback in case the cursor isn't over a surface - * but snapping is enabled. */ - float normal_fallback[3]; - bool use_normal_fallback = snap_gizmo ? - idp_snap_normal_from_gizmo(snap_gizmo, normal_fallback) : - false; - - if ((snap_context != NULL) && - idp_poject_surface_normal(snap_context, - CTX_data_ensure_evaluated_depsgraph(C), - mval_fl, - use_normal_fallback ? r_matrix_orient : NULL, - use_normal_fallback ? normal_fallback : NULL, - matrix_orient_surface)) { - copy_m3_m3(r_matrix_orient, matrix_orient_surface); - found_surface_or_normal = true; - } - - if (!found_surface_or_normal && plane_axis_auto) { - /* Drawing into empty space, draw onto the plane most aligned to the view direction. */ - mat3_align_axis_to_v3(r_matrix_orient, plane_axis, rv3d->viewinv[2]); - } + if (r_is_enabled) { + *r_is_enabled = snap_data->is_enabled; } - - const bool is_snap_found = snap_gizmo ? idp_snap_point_from_gizmo(snap_gizmo, r_co_src) : false; - - if (is_snap_found) { - /* pass */ - } - else { - bool use_depth_fallback = true; - if (plane_depth == PLACE_DEPTH_CURSOR_VIEW) { - /* View plane. */ - ED_view3d_win_to_3d(v3d, region, scene->cursor.location, mval_fl, r_co_src); - use_depth_fallback = false; - } - else if (plane_depth == PLACE_DEPTH_SURFACE) { - if ((snap_context != NULL) && - ED_transform_snap_object_project_view3d(snap_context, - CTX_data_ensure_evaluated_depsgraph(C), - SCE_SNAP_MODE_FACE, - &(const struct SnapObjectParams){ - .snap_select = SNAP_ALL, - .edit_mode_type = SNAP_GEOM_EDIT, - }, - mval_fl, - NULL, - NULL, - r_co_src, - NULL)) { - use_depth_fallback = false; - } - } - - /* Use as fallback to surface. */ - if (use_depth_fallback || (plane_depth == PLACE_DEPTH_CURSOR_PLANE)) { - /* Cursor plane. */ - float plane[4]; - const float *plane_normal = r_matrix_orient[plane_axis]; - - const float view_axis_dot = fabsf(dot_v3v3(rv3d->viewinv[2], r_matrix_orient[plane_axis])); - if (view_axis_dot < eps_view_align) { - /* In this case, just project onto the view plane as it's important the location - * is _always_ under the mouse cursor, even if it turns out that won't lie on - * the original 'plane' that's been calculated for us. */ - plane_normal = rv3d->viewinv[2]; - } - - plane_from_point_normal_v3(plane, scene->cursor.location, plane_normal); - - if (view3d_win_to_3d_on_plane_maybe_fallback(region, plane, mval_fl, NULL, r_co_src)) { - use_depth_fallback = false; - } - - /* Even if the calculation works, it's possible the point found is behind the view, - * or very far away (past the far clipping). - * In either case creating objects won't be useful. */ - if (rv3d->is_persp) { - float dir[3]; - sub_v3_v3v3(dir, rv3d->viewinv[3], r_co_src); - const float dot = dot_v3v3(dir, rv3d->viewinv[2]); - if (dot < v3d->clip_start || dot > v3d->clip_end) { - use_depth_fallback = true; - } - } - } - - if (use_depth_fallback) { - float co_depth[3]; - /* Fallback to view center. */ - negate_v3_v3(co_depth, rv3d->ofs); - ED_view3d_win_to_3d(v3d, region, co_depth, mval_fl, r_co_src); - } - } - - if (!is_snap_found && ((snap_gizmo != NULL) && ED_gizmotypes_snap_3d_is_enabled(snap_gizmo))) { - if (snap_to == PLACE_SNAP_TO_DEFAULT) { - idp_snap_calc_incremental(scene, v3d, region, NULL, r_co_src); - } - } - - if (snap_context_free) { - ED_transform_snap_object_context_destroy(snap_context); + if (r_is_snap_invert) { + *r_is_snap_invert = snap_data->is_snap_invert; } + return snap_data->snap_elem != 0; } /** \} */ @@ -1012,11 +724,11 @@ static void view3d_interactive_add_calc_plane(bContext *C, static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEvent *event) { + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); - const int plane_axis = RNA_enum_get(op->ptr, "plane_axis"); - const bool plane_axis_auto = RNA_boolean_get(op->ptr, "plane_axis_auto"); + const int plane_axis = snap_state->plane_axis; const enum ePlace_SnapTo snap_to = RNA_enum_get(op->ptr, "snap_target"); - const enum ePlace_Depth plane_depth = RNA_enum_get(op->ptr, "plane_depth"); + const enum ePlace_Origin plane_origin[2] = { RNA_enum_get(op->ptr, "plane_origin_base"), RNA_enum_get(op->ptr, "plane_origin_depth"), @@ -1025,58 +737,24 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv RNA_enum_get(op->ptr, "plane_aspect_base"), RNA_enum_get(op->ptr, "plane_aspect_depth"), }; - const enum ePlace_Orient plane_orient = RNA_enum_get(op->ptr, "plane_orientation"); - - const float mval_fl[2] = {UNPACK2(event->mval)}; struct InteractivePlaceData *ipd = op->customdata; - /* Assign snap gizmo which is may be used as part of the tool. */ - { - wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(ipd->region); - if (gzgroup != NULL) { - if (gzgroup->gizmos.first) { - ipd->snap_gizmo = gzgroup->gizmos.first; - } - - /* Can be NULL when gizmos are disabled. */ - if (gzgroup->customdata != NULL) { - preview_plane_cursor_visible_set(gzgroup, false); - } - } - } - - /* For tweak events the snap target may have changed since dragging, - * update the snap target at the cursor location where tweak began. - * - * NOTE: we could investigating solving this in a more generic way, - * so each operator doesn't have to account for it. */ - if (ISTWEAK(event->type)) { - if (ipd->snap_gizmo != NULL) { - ED_gizmotypes_snap_3d_update(ipd->snap_gizmo, - CTX_data_ensure_evaluated_depsgraph(C), - ipd->region, - ipd->v3d, - G_MAIN->wm.first, - mval_fl); - } - } - ipd->launch_event = WM_userdef_event_type_from_keymap_type(event->type); - view3d_interactive_add_calc_plane(C, - ipd->scene, - ipd->v3d, - ipd->region, - mval_fl, - ipd->snap_gizmo, - snap_to, - plane_depth, - plane_orient, - plane_axis, - plane_axis_auto, - ipd->co_src, - ipd->matrix_orient); + V3DSnapCursorState *snap_state_new = ED_view3d_cursor_snap_active(); + if (snap_state_new) { + ipd->snap_state = snap_state = snap_state_new; + } + + snap_state->draw_point = true; + snap_state->draw_plane = true; + ipd->is_snap_found = + view3d_interactive_add_calc_snap( + C, event, ipd->co_src, ipd->matrix_orient, &ipd->use_snap, &ipd->is_snap_invert) != 0; + + snap_state->draw_plane = false; + ED_view3d_cursor_snap_prevpoint_set(snap_state, ipd->co_src); ipd->orient_axis = plane_axis; for (int i = 0; i < 2; i++) { @@ -1155,13 +833,6 @@ static void view3d_interactive_add_begin(bContext *C, wmOperator *op, const wmEv } } - ipd->is_snap_invert = ipd->snap_gizmo ? ED_gizmotypes_snap_3d_invert_snap_get(ipd->snap_gizmo) : - false; - { - const ToolSettings *ts = ipd->scene->toolsettings; - ipd->use_snap = (ipd->is_snap_invert == !(ts->snap_flag & SCE_SNAP)); - } - ipd->draw_handle_view = ED_region_draw_cb_activate( ipd->region->type, draw_primitive_view, ipd, REGION_DRAW_POST_VIEW); @@ -1237,20 +908,12 @@ static void view3d_interactive_add_exit(bContext *C, wmOperator *op) UNUSED_VARS(C); struct InteractivePlaceData *ipd = op->customdata; + ED_view3d_cursor_snap_deactive(ipd->snap_state); ED_region_draw_cb_exit(ipd->region->type, ipd->draw_handle_view); ED_region_tag_redraw(ipd->region); - { - wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(ipd->region); - if (gzgroup != NULL) { - if (gzgroup->customdata != NULL) { - preview_plane_cursor_visible_set(gzgroup, true); - } - } - } - MEM_freeN(ipd); } @@ -1368,6 +1031,8 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve if (ipd->step_index == STEP_BASE) { if (ELEM(event->type, ipd->launch_event, LEFTMOUSE)) { if (event->val == KM_RELEASE) { + ED_view3d_cursor_snap_prevpoint_set(ipd->snap_state, ipd->co_src); + /* Set secondary plane. */ /* Create normal. */ @@ -1515,19 +1180,8 @@ static int view3d_interactive_add_modal(bContext *C, wmOperator *op, const wmEve /* Calculate the snap location on mouse-move or when toggling snap. */ ipd->is_snap_found = false; if (ipd->use_snap) { - if (ipd->snap_gizmo != NULL) { - ED_gizmotypes_snap_3d_flag_set(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE); - if (ED_gizmotypes_snap_3d_update(ipd->snap_gizmo, - CTX_data_ensure_evaluated_depsgraph(C), - ipd->region, - ipd->v3d, - G_MAIN->wm.first, - mval_fl)) { - ED_gizmotypes_snap_3d_data_get(ipd->snap_gizmo, ipd->snap_co, NULL, NULL, NULL); - ipd->is_snap_found = true; - } - ED_gizmotypes_snap_3d_flag_clear(ipd->snap_gizmo, ED_SNAPGIZMO_TOGGLE_ALWAYS_TRUE); - } + ipd->is_snap_found = view3d_interactive_add_calc_snap( + C, event, ipd->snap_co, NULL, NULL, NULL); } if (ipd->step_index == STEP_BASE) { @@ -1597,6 +1251,98 @@ static bool view3d_interactive_add_poll(bContext *C) return ELEM(mode, CTX_MODE_OBJECT, CTX_MODE_EDIT_MESH); } +static int idp_rna_plane_axis_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->plane_axis; +} + +static void idp_rna_plane_axis_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->plane_axis = (short)value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static int idp_rna_plane_depth_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->plane_depth; +} + +static void idp_rna_plane_depth_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->plane_depth = value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static int idp_rna_plane_orient_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->plane_orient; +} + +static void idp_rna_plane_orient_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->plane_orient = value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static int idp_rna_snap_target_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + if (!snap_state->snap_elem_force) { + return PLACE_SNAP_TO_DEFAULT; + } + + /* Make sure you keep a consistent #snap_mode. */ + snap_state->snap_elem_force = SNAP_MODE_GEOM; + return PLACE_SNAP_TO_GEOMETRY; +} + +static void idp_rna_snap_target_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + int value) +{ + short snap_mode = 0; /* #toolsettings->snap_mode. */ + const enum ePlace_SnapTo snap_to = value; + if (snap_to == PLACE_SNAP_TO_GEOMETRY) { + snap_mode = SNAP_MODE_GEOM; + } + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->snap_elem_force = snap_mode; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + +static bool idp_rna_use_plane_axis_auto_get_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop)) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + return snap_state->use_plane_axis_auto; +} + +static void idp_rna_use_plane_axis_auto_set_fn(struct PointerRNA *UNUSED(ptr), + struct PropertyRNA *UNUSED(prop), + bool value) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + snap_state->use_plane_axis_auto = value; + ED_view3d_cursor_snap_state_default_set(snap_state); +} + void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) { /* identifiers */ @@ -1617,6 +1363,12 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) /* properties */ PropertyRNA *prop; + /* WORKAROUND: properties with `_funcs_runtime` should not be saved in keymaps. + * So reassign the #PROP_IDPROPERTY flag to trick the property as not being set. + * (See #RNA_property_is_set). */ + PropertyFlag unsalvageable = PROP_SKIP_SAVE | PROP_HIDDEN | PROP_PTR_NO_OWNERSHIP | + PROP_IDPROPERTY; + /* Normally not accessed directly, leave unset and check the active tool. */ static const EnumPropertyItem primitive_type[] = { {PLACE_PRIMITIVE_TYPE_CUBE, "CUBE", 0, "Cube", ""}, @@ -1626,6 +1378,7 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) {PLACE_PRIMITIVE_TYPE_SPHERE_ICO, "SPHERE_ICO", 0, "ICO Sphere", ""}, {0, NULL, 0, NULL, NULL}, }; + prop = RNA_def_property(ot->srna, "primitive_type", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text(prop, "Primitive", ""); RNA_def_property_enum_items(prop, primitive_type); @@ -1635,7 +1388,9 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_ui_text(prop, "Plane Axis", "The axis used for placing the base region"); RNA_def_property_enum_default(prop, 2); RNA_def_property_enum_items(prop, rna_enum_axis_xyz_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_plane_axis_get_fn, idp_rna_plane_axis_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); prop = RNA_def_boolean(ot->srna, "plane_axis_auto", @@ -1643,21 +1398,23 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) "Auto Axis", "Select the closest axis when placing objects " "(surface overrides)"); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_boolean_funcs_runtime( + prop, idp_rna_use_plane_axis_auto_get_fn, idp_rna_use_plane_axis_auto_set_fn); + RNA_def_property_flag(prop, unsalvageable); static const EnumPropertyItem plane_depth_items[] = { - {PLACE_DEPTH_SURFACE, + {V3D_PLACE_DEPTH_SURFACE, "SURFACE", 0, "Surface", "Start placing on the surface, using the 3D cursor position as a fallback"}, - {PLACE_DEPTH_CURSOR_PLANE, + {V3D_PLACE_DEPTH_CURSOR_PLANE, "CURSOR_PLANE", 0, "Cursor Plane", "Start placement using a point projected onto the orientation axis " "at the 3D cursor position"}, - {PLACE_DEPTH_CURSOR_VIEW, + {V3D_PLACE_DEPTH_CURSOR_VIEW, "CURSOR_VIEW", 0, "Cursor View", @@ -1666,17 +1423,19 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) }; prop = RNA_def_property(ot->srna, "plane_depth", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text(prop, "Position", "The initial depth used when placing the cursor"); - RNA_def_property_enum_default(prop, PLACE_DEPTH_SURFACE); + RNA_def_property_enum_default(prop, V3D_PLACE_DEPTH_SURFACE); RNA_def_property_enum_items(prop, plane_depth_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_plane_depth_get_fn, idp_rna_plane_depth_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); static const EnumPropertyItem plane_orientation_items[] = { - {PLACE_ORIENT_SURFACE, + {V3D_PLACE_ORIENT_SURFACE, "SURFACE", ICON_SNAP_NORMAL, "Surface", "Use the surface normal (using the transform orientation as a fallback)"}, - {PLACE_ORIENT_DEFAULT, + {V3D_PLACE_ORIENT_DEFAULT, "DEFAULT", ICON_ORIENTATION_GLOBAL, "Default", @@ -1685,9 +1444,11 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) }; prop = RNA_def_property(ot->srna, "plane_orientation", PROP_ENUM, PROP_NONE); RNA_def_property_ui_text(prop, "Orientation", "The initial depth used when placing the cursor"); - RNA_def_property_enum_default(prop, PLACE_ORIENT_SURFACE); + RNA_def_property_enum_default(prop, V3D_PLACE_ORIENT_SURFACE); RNA_def_property_enum_items(prop, plane_orientation_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_plane_orient_get_fn, idp_rna_plane_orient_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); static const EnumPropertyItem snap_to_items[] = { {PLACE_SNAP_TO_GEOMETRY, "GEOMETRY", 0, "Geometry", "Snap to all geometry"}, @@ -1698,7 +1459,9 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) RNA_def_property_ui_text(prop, "Snap to", "The target to use while snapping"); RNA_def_property_enum_default(prop, PLACE_SNAP_TO_GEOMETRY); RNA_def_property_enum_items(prop, snap_to_items); - RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_enum_funcs_runtime( + prop, idp_rna_snap_target_get_fn, idp_rna_snap_target_set_fn, NULL); + RNA_def_property_flag(prop, unsalvageable); { /* Plane Origin. */ static const EnumPropertyItem items[] = { @@ -1746,24 +1509,22 @@ void VIEW3D_OT_interactive_add(struct wmOperatorType *ot) * we could show a placement plane here. * \{ */ -static void WIDGETGROUP_placement_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup) +static void preview_plane_free_fn(void *customdata) { - wmGizmo *gizmo; + V3DSnapCursorState *snap_state = customdata; + ED_view3d_cursor_snap_deactive(snap_state); +} - { - /* The gizmo snap has to be the first gizmo. */ - const wmGizmoType *gzt_snap; - gzt_snap = WM_gizmotype_find("GIZMO_GT_snap_3d", true); - gizmo = WM_gizmo_new_ptr(gzt_snap, gzgroup, NULL); +static void WIDGETGROUP_placement_setup(const bContext *C, wmGizmoGroup *gzgroup) +{ + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_active(); + if (snap_state) { + snap_state->region = CTX_wm_region(C); + snap_state->draw_plane = true; - WM_gizmo_set_color(gizmo, (float[4]){1.0f, 1.0f, 1.0f, 1.0f}); - - /* Don't handle any events, this is for display only. */ - gizmo->flag |= WM_GIZMO_HIDDEN_KEYMAP; + gzgroup->customdata = snap_state; + gzgroup->customdata_free = preview_plane_free_fn; } - - /* Sets the gizmos custom-data which has its own free callback. */ - preview_plane_cursor_setup(gzgroup); } void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) @@ -1781,353 +1542,3 @@ void VIEW3D_GGT_placement(wmGizmoGroupType *gzgt) } /** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Placement Preview Plane - * - * Preview the plane that will be used for placement. - * - * Note that we might want to split this into its own file, - * for now this is coupled with the 3D view placement gizmo. - * \{ */ - -static void gizmo_plane_update_cursor(const bContext *C, - ARegion *region, - const int mval[2], - float r_co[3], - float r_matrix_orient[3][3], - int *r_plane_axis) -{ - wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_interactive_add", true); - BLI_assert(ot != NULL); - PointerRNA ptr; - - ScrArea *area = CTX_wm_area(C); - BLI_assert(region == CTX_wm_region(C)); - bToolRef *tref = area->runtime.tool; - WM_toolsystem_ref_properties_ensure_from_operator(tref, ot, &ptr); - - const enum ePlace_SnapTo snap_to = RNA_enum_get(&ptr, "snap_target"); - const int plane_axis = RNA_enum_get(&ptr, "plane_axis"); - const bool plane_axis_auto = RNA_boolean_get(&ptr, "plane_axis_auto"); - const enum ePlace_Depth plane_depth = RNA_enum_get(&ptr, "plane_depth"); - const enum ePlace_Orient plane_orient = RNA_enum_get(&ptr, "plane_orientation"); - - const float mval_fl[2] = {UNPACK2(mval)}; - - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - - /* Assign snap gizmo which is may be used as part of the tool. */ - wmGizmo *snap_gizmo = NULL; - { - wmGizmoGroup *gzgroup = idp_gizmogroup_from_region(region); - if ((gzgroup != NULL) && gzgroup->gizmos.first) { - snap_gizmo = gzgroup->gizmos.first; - } - } - - /* This ensures the snap gizmo has settings from this tool. - * This function call could be moved a more appropriate place, - * responding to the setting being changed for example, - * however setting the value isn't expensive, so do it here. */ - idp_snap_gizmo_update_snap_elements(scene, snap_to, snap_gizmo); - - view3d_interactive_add_calc_plane((bContext *)C, - scene, - v3d, - region, - mval_fl, - snap_gizmo, - snap_to, - plane_depth, - plane_orient, - plane_axis, - plane_axis_auto, - r_co, - r_matrix_orient); - *r_plane_axis = plane_axis; -} - -static void gizmo_plane_draw_grid(const int resolution, - const float scale, - const float scale_fade, - const float matrix[4][4], - const int plane_axis, - const float color[4]) -{ - BLI_assert(scale_fade <= scale); - const int resolution_min = resolution - 1; - float color_fade[4] = {UNPACK4(color)}; - const float *center = matrix[3]; - - GPU_blend(GPU_BLEND_ADDITIVE); - GPU_line_smooth(true); - GPU_line_width(1.0f); - - GPUVertFormat *format = immVertexFormat(); - const uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - const uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR); - - const size_t coords_len = resolution * resolution; - float(*coords)[3] = MEM_mallocN(sizeof(*coords) * coords_len, __func__); - - const int axis_x = (plane_axis + 0) % 3; - const int axis_y = (plane_axis + 1) % 3; - const int axis_z = (plane_axis + 2) % 3; - - int i; - const float resolution_div = (float)1.0f / (float)resolution; - i = 0; - for (int x = 0; x < resolution; x++) { - const float x_fl = (x * resolution_div) - 0.5f; - for (int y = 0; y < resolution; y++) { - const float y_fl = (y * resolution_div) - 0.5f; - coords[i][axis_x] = 0.0f; - coords[i][axis_y] = x_fl * scale; - coords[i][axis_z] = y_fl * scale; - mul_m4_v3(matrix, coords[i]); - i += 1; - } - } - BLI_assert(i == coords_len); - immBeginAtMost(GPU_PRIM_LINES, coords_len * 4); - i = 0; - for (int x = 0; x < resolution_min; x++) { - for (int y = 0; y < resolution_min; y++) { - - /* Add #resolution_div to ensure we fade-out entirely. */ -#define FADE(v) \ - max_ff(0.0f, (1.0f - square_f(((len_v3v3(v, center) / scale_fade) + resolution_div) * 2.0f))) - - const float *v0 = coords[(resolution * x) + y]; - const float *v1 = coords[(resolution * (x + 1)) + y]; - const float *v2 = coords[(resolution * x) + (y + 1)]; - - const float f0 = FADE(v0); - const float f1 = FADE(v1); - const float f2 = FADE(v2); - - if (f0 > 0.0f || f1 > 0.0f) { - color_fade[3] = color[3] * f0; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v0); - color_fade[3] = color[3] * f1; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v1); - } - if (f0 > 0.0f || f2 > 0.0f) { - color_fade[3] = color[3] * f0; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v0); - - color_fade[3] = color[3] * f2; - immAttr4fv(col_id, color_fade); - immVertex3fv(pos_id, v2); - } - -#undef FADE - - i++; - } - } - - MEM_freeN(coords); - - immEnd(); - - immUnbindProgram(); - - GPU_line_smooth(false); - GPU_blend(GPU_BLEND_NONE); -} - -/* -------------------------------------------------------------------- */ -/** \name Preview Plane Cursor - * \{ */ - -struct PlacementCursor { - /** - * Back-pointer to the gizmo-group that uses this cursor. - * Needed so we know that the cursor belongs to the region. - */ - wmGizmoGroup *gzgroup; - - /** - * Enable this while the modal operator is running, - * so the preview-plane doesn't show at the same time as add-object preview shape - * since it's distracting & not helpful. - */ - bool do_draw; - - void *paintcursor; - - int plane_axis; - float matrix[4][4]; - - /* Check if we need to re-calculate the plane matrix. */ - int mval_prev[2]; - float persmat_prev[4][4]; -}; - -static void cursor_plane_draw(bContext *C, int x, int y, void *customdata) -{ - struct PlacementCursor *plc = (struct PlacementCursor *)customdata; - ARegion *region = CTX_wm_region(C); - const RegionView3D *rv3d = region->regiondata; - - /* Early exit. - * Note that we can't do most of these checks in the poll function (besides global checks) - * so test them here instead. - * - * This cursor is only active while the gizmo is being used - * so it's not so important to have a poll function. */ - if (plc->do_draw == false) { - return; - } - if (G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) { - return; - } - if (rv3d->rflag & RV3D_NAVIGATING) { - return; - } - - /* Check this gizmo group is in the region. */ - { - wmGizmoMap *gzmap = region->gizmo_map; - wmGizmoGroup *gzgroup_test = WM_gizmomap_group_find_ptr(gzmap, plc->gzgroup->type); - if (gzgroup_test != plc->gzgroup) { - /* Wrong viewport. */ - return; - } - } - - const int mval[2] = {x - region->winrct.xmin, y - region->winrct.ymin}; - - /* Update matrix? */ - if ((plc->mval_prev[0] != mval[0]) || (plc->mval_prev[1] != mval[1]) || - !equals_m4m4(plc->persmat_prev, rv3d->persmat)) { - plc->mval_prev[0] = mval[0]; - plc->mval_prev[1] = mval[1]; - - float orient_matrix[3][3]; - float co[3]; - gizmo_plane_update_cursor(C, region, mval, co, orient_matrix, &plc->plane_axis); - copy_m4_m3(plc->matrix, orient_matrix); - copy_v3_v3(plc->matrix[3], co); - - copy_m4_m4(plc->persmat_prev, rv3d->persmat); - } - - /* Draw */ - float pixel_size; - - if (rv3d->is_persp) { - float center[3]; - negate_v3_v3(center, rv3d->ofs); - pixel_size = ED_view3d_pixel_size(rv3d, center); - } - else { - pixel_size = ED_view3d_pixel_size(rv3d, plc->matrix[3]); - } - - if (pixel_size > FLT_EPSILON) { - - /* Arbitrary, 1.0 is a little too strong though. */ - float color_alpha = 0.75f; - if (rv3d->is_persp) { - /* Scale down the alpha when this is drawn very small, - * since the add shader causes the small size to show too dense & bright. */ - const float relative_pixel_scale = pixel_size / ED_view3d_pixel_size(rv3d, plc->matrix[3]); - if (relative_pixel_scale < 1.0f) { - color_alpha *= max_ff(square_f(relative_pixel_scale), 0.3f); - } - } - - { - /* Extra adjustment when it's near view-aligned as it seems overly bright. */ - float view_vector[3]; - ED_view3d_global_to_vector(rv3d, plc->matrix[3], view_vector); - float view_dot = fabsf(dot_v3v3(plc->matrix[plc->plane_axis], view_vector)); - color_alpha *= max_ff(0.3f, 1.0f - square_f(square_f(1.0f - view_dot))); - } - - /* Setup viewport & matrix. */ - wmViewport(®ion->winrct); - GPU_matrix_push_projection(); - GPU_matrix_push(); - GPU_matrix_projection_set(rv3d->winmat); - GPU_matrix_set(rv3d->viewmat); - - const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; - - float final_scale = (scale_mod * pixel_size); - - const int lines_subdiv = 10; - int lines = lines_subdiv; - - float final_scale_fade = final_scale; - final_scale = ceil_power_of_10(final_scale); - - float fac = final_scale_fade / final_scale; - - float color[4] = {1, 1, 1, color_alpha}; - color[3] *= square_f(1.0f - fac); - if (color[3] > 0.0f) { - gizmo_plane_draw_grid(lines * lines_subdiv, - final_scale, - final_scale_fade, - plc->matrix, - plc->plane_axis, - color); - } - - color[3] = color_alpha; - /* When the grid is large, we only need the 2x lines in the middle. */ - if (fac < 0.2f) { - lines = 1; - final_scale = final_scale_fade; - } - gizmo_plane_draw_grid( - lines, final_scale, final_scale_fade, plc->matrix, plc->plane_axis, color); - - /* Restore matrix. */ - GPU_matrix_pop(); - GPU_matrix_pop_projection(); - } -} - -static void preview_plane_cursor_free(void *customdata) -{ - struct PlacementCursor *plc = customdata; - - /* The window manager is freed first on exit. */ - wmWindowManager *wm = G_MAIN->wm.first; - if (UNLIKELY(wm != NULL)) { - WM_paint_cursor_end(plc->paintcursor); - } - MEM_freeN(plc); -} - -static void preview_plane_cursor_setup(wmGizmoGroup *gzgroup) -{ - BLI_assert(gzgroup->customdata == NULL); - struct PlacementCursor *plc = MEM_callocN(sizeof(*plc), __func__); - plc->gzgroup = gzgroup; - plc->paintcursor = WM_paint_cursor_activate( - SPACE_VIEW3D, RGN_TYPE_WINDOW, NULL, cursor_plane_draw, plc); - gzgroup->customdata = plc; - gzgroup->customdata_free = preview_plane_cursor_free; - - preview_plane_cursor_visible_set(gzgroup, true); -} - -static void preview_plane_cursor_visible_set(wmGizmoGroup *gzgroup, bool do_draw) -{ - struct PlacementCursor *plc = gzgroup->customdata; - plc->do_draw = do_draw; -} - -/** \} */ diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 3f572bf9d5a..18820039c7f 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -2047,19 +2047,16 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, bool enumerate, bool *r_do_nearest) { - static int last_mval[2] = {-100, -100}; bool do_nearest = false; View3D *v3d = vc->v3d; /* define if we use solid nearest select or not */ if (use_cycle) { + /* Update the coordinates (even if the return value isn't used). */ + const bool has_motion = WM_cursor_test_motion_and_update(mval); if (!XRAY_ACTIVE(v3d)) { - do_nearest = true; - if (len_manhattan_v2v2_int(mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) { - do_nearest = false; - } + do_nearest = has_motion; } - copy_v2_v2_int(last_mval, mval); } else { if (!XRAY_ACTIVE(v3d)) { @@ -2082,7 +2079,7 @@ static int mixed_bones_object_selectbuffer_extended(ViewContext *vc, /** * \param has_bones: When true, skip non-bone hits, also allow bases to be used * that are visible but not select-able, - * since you may be in pose mode with an unselect-able object. + * since you may be in pose mode with an un-selectable object. * * \return the active base or NULL. */ @@ -2813,7 +2810,9 @@ static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *even { RNA_int_set_array(op->ptr, "location", event->mval); - return view3d_select_exec(C, op); + const int retval = view3d_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void VIEW3D_OT_select(wmOperatorType *ot) diff --git a/source/blender/editors/space_view3d/view3d_snap.c b/source/blender/editors/space_view3d/view3d_snap.c index 4482e5897ca..583a9ad75c2 100644 --- a/source/blender/editors/space_view3d/view3d_snap.c +++ b/source/blender/editors/space_view3d/view3d_snap.c @@ -58,7 +58,7 @@ #include "view3d_intern.h" -static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]); +static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_cursor[3]); static bool snap_calc_active_center(bContext *C, const bool select_only, float r_center[3]); /* -------------------------------------------------------------------- */ @@ -310,9 +310,11 @@ void VIEW3D_OT_snap_selected_to_grid(wmOperatorType *ot) * and be snapped by the selection pivot point (median, active), * or if every object origin should be snapped to the given location. */ -static int snap_selected_to_location(bContext *C, - const float snap_target_global[3], - const bool use_offset) +static bool snap_selected_to_location(bContext *C, + const float snap_target_global[3], + const bool use_offset, + const int pivot_point, + const bool use_toolsettings) { Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); @@ -326,12 +328,11 @@ static int snap_selected_to_location(bContext *C, int a; if (use_offset) { - if ((v3d && scene->toolsettings->transform_pivot_point == V3D_AROUND_ACTIVE) && - snap_calc_active_center(C, true, center_global)) { + if ((pivot_point == V3D_AROUND_ACTIVE) && snap_calc_active_center(C, true, center_global)) { /* pass */ } else { - snap_curs_to_sel_ex(C, center_global); + snap_curs_to_sel_ex(C, pivot_point, center_global); } sub_v3_v3v3(offset_global, snap_target_global, center_global); } @@ -341,7 +342,7 @@ static int snap_selected_to_location(bContext *C, ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, CTX_wm_view3d(C), &objects_len); + view_layer, v3d, &objects_len); for (uint ob_index = 0; ob_index < objects_len; ob_index++) { obedit = objects[ob_index]; @@ -435,18 +436,23 @@ static int snap_selected_to_location(bContext *C, } /* copy new position */ - if ((pchan->protectflag & OB_LOCK_LOCX) == 0) { - pchan->loc[0] = cursor_pose[0]; - } - if ((pchan->protectflag & OB_LOCK_LOCY) == 0) { - pchan->loc[1] = cursor_pose[1]; - } - if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) { - pchan->loc[2] = cursor_pose[2]; - } + if (use_toolsettings) { + if ((pchan->protectflag & OB_LOCK_LOCX) == 0) { + pchan->loc[0] = cursor_pose[0]; + } + if ((pchan->protectflag & OB_LOCK_LOCY) == 0) { + pchan->loc[1] = cursor_pose[1]; + } + if ((pchan->protectflag & OB_LOCK_LOCZ) == 0) { + pchan->loc[2] = cursor_pose[2]; + } - /* auto-keyframing */ - ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + /* auto-keyframing */ + ED_autokeyframe_pchan(C, scene, ob, pchan, ks); + } + else { + copy_v3_v3(pchan->loc, cursor_pose); + } } } @@ -484,9 +490,11 @@ static int snap_selected_to_location(bContext *C, objects_len = BLI_array_len(objects); } - const bool use_transform_skip_children = (scene->toolsettings->transform_flag & + const bool use_transform_skip_children = use_toolsettings && + (scene->toolsettings->transform_flag & SCE_XFORM_SKIP_CHILDREN); - const bool use_transform_data_origin = (scene->toolsettings->transform_flag & + const bool use_transform_data_origin = use_toolsettings && + (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN); struct XFormObjectSkipChild_Container *xcs = NULL; struct XFormObjectData_Container *xds = NULL; @@ -538,18 +546,23 @@ static int snap_selected_to_location(bContext *C, invert_m3_m3(imat, originmat); mul_m3_v3(imat, cursor_parent); } - if ((ob->protectflag & OB_LOCK_LOCX) == 0) { - ob->loc[0] += cursor_parent[0]; - } - if ((ob->protectflag & OB_LOCK_LOCY) == 0) { - ob->loc[1] += cursor_parent[1]; - } - if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { - ob->loc[2] += cursor_parent[2]; - } + if (use_toolsettings) { + if ((ob->protectflag & OB_LOCK_LOCX) == 0) { + ob->loc[0] += cursor_parent[0]; + } + if ((ob->protectflag & OB_LOCK_LOCY) == 0) { + ob->loc[1] += cursor_parent[1]; + } + if ((ob->protectflag & OB_LOCK_LOCZ) == 0) { + ob->loc[2] += cursor_parent[2]; + } - /* auto-keyframing */ - ED_autokeyframe_object(C, scene, ob, ks); + /* auto-keyframing */ + ED_autokeyframe_object(C, scene, ob, ks); + } + else { + add_v3_v3(ob->loc, cursor_parent); + } DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); } @@ -570,7 +583,21 @@ static int snap_selected_to_location(bContext *C, WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, NULL); - return OPERATOR_FINISHED; + return true; +} + +bool ED_view3d_snap_selected_to_location(bContext *C, + const float snap_target_global[3], + const int pivot_point) +{ + /* These could be passed as arguments if needed. */ + /* Always use pivot point. */ + const bool use_offset = true; + /* Disable object protected flags & auto-keyframing, + * so this can be used as a low level function. */ + const bool use_toolsettings = false; + return snap_selected_to_location( + C, snap_target_global, use_offset, pivot_point, use_toolsettings); } /** \} */ @@ -586,8 +613,12 @@ static int snap_selected_to_cursor_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); const float *snap_target_global = scene->cursor.location; + const int pivot_point = scene->toolsettings->transform_pivot_point; - return snap_selected_to_location(C, snap_target_global, use_offset); + if (snap_selected_to_location(C, snap_target_global, use_offset, pivot_point, true)) { + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } void VIEW3D_OT_snap_selected_to_cursor(wmOperatorType *ot) @@ -628,7 +659,10 @@ static int snap_selected_to_active_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - return snap_selected_to_location(C, snap_target_global, false); + if (!snap_selected_to_location(C, snap_target_global, false, -1, true)) { + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } void VIEW3D_OT_snap_selected_to_active(wmOperatorType *ot) @@ -752,7 +786,7 @@ static void bundle_midpoint(Scene *scene, Object *ob, float r_vec[3]) } /** Snaps the 3D cursor location to the median point of the selection. */ -static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) +static bool snap_curs_to_sel_ex(bContext *C, const int pivot_point, float r_cursor[3]) { Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); ViewLayer *view_layer_eval = DEG_get_evaluated_view_layer(depsgraph); @@ -849,12 +883,12 @@ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) return false; } - if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_BOUNDS) { - mid_v3_v3v3(cursor, min, max); + if (pivot_point == V3D_AROUND_CENTER_BOUNDS) { + mid_v3_v3v3(r_cursor, min, max); } else { mul_v3_fl(centroid, 1.0f / (float)count); - copy_v3_v3(cursor, centroid); + copy_v3_v3(r_cursor, centroid); } return true; } @@ -862,7 +896,8 @@ static bool snap_curs_to_sel_ex(bContext *C, float cursor[3]) static int snap_curs_to_sel_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); - if (snap_curs_to_sel_ex(C, scene->cursor.location)) { + const int pivot_point = scene->toolsettings->transform_pivot_point; + if (snap_curs_to_sel_ex(C, pivot_point, scene->cursor.location)) { WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, NULL); DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/space_view3d/view3d_utils.c b/source/blender/editors/space_view3d/view3d_utils.c index e09453b9957..dab1b55072a 100644 --- a/source/blender/editors/space_view3d/view3d_utils.c +++ b/source/blender/editors/space_view3d/view3d_utils.c @@ -1094,17 +1094,10 @@ bool ED_view3d_autodist_simple(ARegion *region, return ED_view3d_unproject_v3(region, centx, centy, depth, mouse_worldloc); } -bool ED_view3d_autodist_depth(ARegion *region, const int mval[2], int margin, float *depth) -{ - *depth = view_autodist_depth_margin(region, mval, margin); - - return (*depth != FLT_MAX); -} - static bool depth_segment_cb(int x, int y, void *userData) { struct { - ARegion *region; + const ViewDepths *vd; int margin; float depth; } *data = userData; @@ -1114,27 +1107,25 @@ static bool depth_segment_cb(int x, int y, void *userData) mval[0] = x; mval[1] = y; - depth = view_autodist_depth_margin(data->region, mval, data->margin); - - if (depth != FLT_MAX) { + if (ED_view3d_depth_read_cached(data->vd, mval, data->margin, &depth)) { data->depth = depth; return false; } return true; } -bool ED_view3d_autodist_depth_seg( - ARegion *region, const int mval_sta[2], const int mval_end[2], int margin, float *depth) +bool ED_view3d_depth_read_cached_seg( + const ViewDepths *vd, const int mval_sta[2], const int mval_end[2], int margin, float *depth) { struct { - ARegion *region; + const ViewDepths *vd; int margin; float depth; } data = {NULL}; int p1[2]; int p2[2]; - data.region = region; + data.vd = vd; data.margin = margin; data.depth = FLT_MAX; @@ -1691,6 +1682,8 @@ bool ED_view3d_depth_read_cached(const ViewDepths *vd, return true; } + /* GPencil and Anotations also need the returned depth value to be high so that it is invalid. */ + *r_depth = FLT_MAX; return false; } diff --git a/source/blender/editors/space_view3d/view3d_view.c b/source/blender/editors/space_view3d/view3d_view.c index f5da7c14a88..46a664f10fa 100644 --- a/source/blender/editors/space_view3d/view3d_view.c +++ b/source/blender/editors/space_view3d/view3d_view.c @@ -1730,7 +1730,12 @@ void ED_view3d_xr_shading_update(wmWindowManager *wm, const View3D *v3d, const S if (v3d->runtime.flag & V3D_RUNTIME_XR_SESSION_ROOT) { View3DShading *xr_shading = &wm->xr.session_settings.shading; /* Flags that shouldn't be overridden by the 3D View shading. */ - const int flag_copy = V3D_SHADING_WORLD_ORIENTATION; + int flag_copy = 0; + if (v3d->shading.type != + OB_SOLID) { /* Don't set V3D_SHADING_WORLD_ORIENTATION for solid shading since it results + in distorted lighting when the view matrix has a scale factor. */ + flag_copy |= V3D_SHADING_WORLD_ORIENTATION; + } BLI_assert(WM_xr_session_exists(&wm->xr)); diff --git a/source/blender/editors/transform/CMakeLists.txt b/source/blender/editors/transform/CMakeLists.txt index e9efed3cd61..64a720322c1 100644 --- a/source/blender/editors/transform/CMakeLists.txt +++ b/source/blender/editors/transform/CMakeLists.txt @@ -60,6 +60,7 @@ set(SRC transform_convert_particle.c transform_convert_sculpt.c transform_convert_sequencer.c + transform_convert_sequencer_image.c transform_convert_tracking.c transform_draw_cursors.c transform_generics.c diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index 58491f8c2d3..f212c7f5747 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -28,6 +28,7 @@ #include "DNA_gpencil_types.h" #include "DNA_mask_types.h" #include "DNA_mesh_types.h" +#include "DNA_screen_types.h" #include "BLI_math.h" #include "BLI_rect.h" @@ -47,6 +48,8 @@ #include "ED_screen.h" #include "ED_space_api.h" +#include "SEQ_transform.h" + #include "WM_api.h" #include "WM_message.h" #include "WM_types.h" @@ -127,6 +130,11 @@ void setTransformViewAspect(TransInfo *t, float r_aspect[3]) ED_space_image_get_uv_aspect(sima, &r_aspect[0], &r_aspect[1]); } } + else if (t->spacetype == SPACE_SEQ) { + if (t->options & CTX_CURSOR) { + SEQ_image_preview_unit_to_px(t->scene, r_aspect, r_aspect); + } + } else if (t->spacetype == SPACE_CLIP) { SpaceClip *sclip = t->area->spacedata.first; @@ -789,6 +797,25 @@ static bool transform_event_modal_constraint(TransInfo *t, short modal_type) if (constraint_new == CON_AXIS2) { return false; } + + if (t->data_type == TC_SEQ_IMAGE_DATA) { + /* Setup the 2d msg string so it writes out the transform space. */ + msg_2d = msg_3d; + + short orient_index = 1; + if (t->orient_curr == O_DEFAULT || ELEM(constraint_curr, -1, constraint_new)) { + /* Successive presses on existing axis, cycle orientation modes. */ + orient_index = (short)((t->orient_curr + 1) % (int)ARRAY_SIZE(t->orient)); + } + + transform_orientations_current_set(t, orient_index); + if (orient_index != 0) { + /* Make sure that we don't stop the constraint unless we are looped back around to + * "no constraint". */ + constraint_curr = -1; + } + } + if (constraint_curr == constraint_new) { stopConstraint(t); } @@ -998,7 +1025,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) case TFM_MODAL_PROPSIZE: /* MOUSEPAN usage... */ if (t->flag & T_PROP_EDIT) { - float fac = 1.0f + 0.005f * (event->y - event->prevy); + float fac = 1.0f + 0.005f * (event->xy[1] - event->prev_xy[1]); t->prop_size *= fac; if (t->spacetype == SPACE_VIEW3D && t->persp != RV3D_ORTHO) { t->prop_size = max_ff(min_ff(t->prop_size, ((View3D *)t->view)->clip_end), @@ -1070,8 +1097,8 @@ int transformEvent(TransInfo *t, const wmEvent *event) break; case TFM_MODAL_AUTOCONSTRAINT: case TFM_MODAL_AUTOCONSTRAINTPLANE: - if ((t->flag & T_RELEASE_CONFIRM) && (event->prevval == KM_RELEASE) && - event->prevtype == t->launch_event) { + if ((t->flag & T_RELEASE_CONFIRM) && (event->prev_val == KM_RELEASE) && + event->prev_type == t->launch_event) { /* Confirm transform if launch key is released after mouse move. */ t->state = TRANS_CONFIRM; } @@ -1110,13 +1137,13 @@ int transformEvent(TransInfo *t, const wmEvent *event) } break; case TFM_MODAL_PRECISION: - if (event->prevval == KM_PRESS) { + if (event->prev_val == KM_PRESS) { t->modifiers |= MOD_PRECISION; /* Shift is modifier for higher precision transform. */ t->mouse.precision = 1; t->redraw |= TREDRAW_HARD; } - else if (event->prevval == KM_RELEASE) { + else if (event->prev_val == KM_RELEASE) { t->modifiers &= ~MOD_PRECISION; t->mouse.precision = 0; t->redraw |= TREDRAW_HARD; @@ -1609,8 +1636,16 @@ static void initSnapSpatial(TransInfo *t, float r_snap[2]) } } else if (t->spacetype == SPACE_IMAGE) { - r_snap[0] = 0.0625f; - r_snap[1] = 0.03125f; + SpaceImage *sima = t->area->spacedata.first; + View2D *v2d = &t->region->v2d; + int grid_size = SI_GRID_STEPS_LEN; + float zoom_factor = ED_space_image_zoom_level(v2d, grid_size); + float grid_steps[SI_GRID_STEPS_LEN]; + + ED_space_image_grid_steps(sima, grid_steps, grid_size); + /* Snapping value based on what type of grid is used (adaptive-subdividing or custom-grid). */ + r_snap[0] = ED_space_image_increment_snap_value(grid_size, grid_steps, zoom_factor); + r_snap[1] = r_snap[0] / 2.0f; } else if (t->spacetype == SPACE_CLIP) { r_snap[0] = 0.125f; @@ -1703,11 +1738,13 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->draw_handle_cursor = WM_paint_cursor_activate( SPACE_TYPE_ANY, RGN_TYPE_ANY, transform_draw_cursor_poll, transform_draw_cursor_draw, t); } - else if (t->spacetype == SPACE_SEQ) { - t->draw_handle_view = ED_region_draw_cb_activate( - t->region->type, drawTransformView, t, REGION_DRAW_POST_VIEW); - } - else if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_CLIP, SPACE_NODE, SPACE_GRAPH, SPACE_ACTION)) { + else if (ELEM(t->spacetype, + SPACE_IMAGE, + SPACE_CLIP, + SPACE_NODE, + SPACE_GRAPH, + SPACE_ACTION, + SPACE_SEQ)) { t->draw_handle_view = ED_region_draw_cb_activate( t->region->type, drawTransformView, t, REGION_DRAW_POST_VIEW); t->draw_handle_cursor = WM_paint_cursor_activate( diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index d1a1937cef1..3076b3e207f 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -87,15 +87,16 @@ typedef enum { CTX_PAINT_CURVE = (1 << 7), CTX_POSE_BONE = (1 << 8), CTX_TEXTURE_SPACE = (1 << 9), + CTX_SEQUENCER_IMAGE = (1 << 10), - CTX_NO_PET = (1 << 10), - CTX_AUTOCONFIRM = (1 << 11), + CTX_NO_PET = (1 << 11), + CTX_AUTOCONFIRM = (1 << 12), /** When transforming object's, adjust the object data so it stays in the same place. */ - CTX_OBMODE_XFORM_OBDATA = (1 << 12), + CTX_OBMODE_XFORM_OBDATA = (1 << 13), /** Transform object parents without moving their children. */ - CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 13), + CTX_OBMODE_XFORM_SKIP_CHILDREN = (1 << 14), /** Enable edge scrolling in 2D views */ - CTX_VIEW2D_EDGE_PAN = (1 << 14), + CTX_VIEW2D_EDGE_PAN = (1 << 15), } eTContext; /** #TransInfo.flag */ @@ -221,6 +222,7 @@ typedef enum { TC_POSE, TC_ARMATURE_VERTS, TC_CURSOR_IMAGE, + TC_CURSOR_SEQUENCER, TC_CURSOR_VIEW3D, TC_CURVE_VERTS, TC_GRAPH_EDIT_DATA, @@ -240,6 +242,7 @@ typedef enum { TC_PARTICLE_VERTS, TC_SCULPT, TC_SEQ_DATA, + TC_SEQ_IMAGE_DATA, TC_TRACKING_DATA, } eTConvertType; @@ -363,18 +366,18 @@ typedef struct TransCon { * The last three parameters are pointers to the in/out/printable vectors. */ void (*applyVec)(const struct TransInfo *t, const struct TransDataContainer *tc, - struct TransData *td, + const struct TransData *td, const float in[3], float r_out[3]); /** Apply function pointer for size transformation. */ void (*applySize)(const struct TransInfo *t, const struct TransDataContainer *tc, - struct TransData *td, + const struct TransData *td, float r_smat[3][3]); /** Apply function pointer for rotation transformation */ void (*applyRot)(const struct TransInfo *t, const struct TransDataContainer *tc, - struct TransData *td, + const struct TransData *td, float r_axis[3], float *r_angle); } TransCon; diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 7135395ee2d..23ba335476c 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -393,7 +393,7 @@ static void planeProjection(const TransInfo *t, const float in[3], float out[3]) */ static void applyAxisConstraintVec(const TransInfo *t, const TransDataContainer *UNUSED(tc), - TransData *td, + const TransData *td, const float in[3], float out[3]) { @@ -477,7 +477,7 @@ static void applyAxisConstraintVec(const TransInfo *t, */ static void applyObjectConstraintVec(const TransInfo *t, const TransDataContainer *tc, - TransData *td, + const TransData *td, const float in[3], float out[3]) { @@ -502,7 +502,7 @@ static void applyObjectConstraintVec(const TransInfo *t, */ static void applyAxisConstraintSize(const TransInfo *t, const TransDataContainer *UNUSED(tc), - TransData *td, + const TransData *td, float r_smat[3][3]) { if (!td && t->con.mode & CON_APPLY) { @@ -528,7 +528,7 @@ static void applyAxisConstraintSize(const TransInfo *t, */ static void applyObjectConstraintSize(const TransInfo *t, const TransDataContainer *tc, - TransData *td, + const TransData *td, float r_smat[3][3]) { if (td && t->con.mode & CON_APPLY) { @@ -603,7 +603,7 @@ static void constraints_rotation_impl(const TransInfo *t, */ static void applyAxisConstraintRot(const TransInfo *t, const TransDataContainer *UNUSED(tc), - TransData *td, + const TransData *td, float r_axis[3], float *r_angle) { @@ -627,7 +627,7 @@ static void applyAxisConstraintRot(const TransInfo *t, */ static void applyObjectConstraintRot(const TransInfo *t, const TransDataContainer *tc, - TransData *td, + const TransData *td, float r_axis[3], float *r_angle) { @@ -755,7 +755,7 @@ void drawConstraint(TransInfo *t) { TransCon *tc = &(t->con); - if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE)) { + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE, SPACE_NODE, SPACE_SEQ)) { return; } if (!(tc->mode & CON_APPLY)) { @@ -921,6 +921,16 @@ static void drawObjectConstraint(TransInfo *t) } } + if (t->options & CTX_SEQUENCER_IMAGE) { + /* Because we construct an "L" shape to deform the sequence, we should skip + * all points except the first vertex. Otherwise we will draw the same axis constraint line + * 3 times for each strip. + */ + if (i % 3 != 0) { + continue; + } + } + if (t->flag & T_EDIT) { mul_v3_m4v3(co, tc->mat, td->center); diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c index d756e2c90a6..ff20f569a71 100644 --- a/source/blender/editors/transform/transform_convert.c +++ b/source/blender/editors/transform/transform_convert.c @@ -228,7 +228,7 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) * Used to find #TransData from the index returned by #BLI_kdtree_find_nearest. */ TransData **td_table = MEM_mallocN(sizeof(*td_table) * td_table_len, __func__); - /* Create and fill kd-tree of selected's positions - in global or proj_vec space. */ + /* Create and fill KD-tree of selected's positions - in global or proj_vec space. */ KDTree_3d *td_tree = BLI_kdtree_3d_new(td_table_len); int td_table_index = 0; @@ -945,6 +945,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) break; case TC_ARMATURE_VERTS: case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: case TC_CURSOR_VIEW3D: case TC_CURVE_VERTS: case TC_GPENCIL: @@ -955,6 +956,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) case TC_OBJECT_TEXSPACE: case TC_PAINT_CURVE_VERTS: case TC_PARTICLE_VERTS: + case TC_SEQ_IMAGE_DATA: case TC_NONE: default: break; @@ -963,6 +965,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t) int special_transform_moving(TransInfo *t) { + if (t->options & CTX_CURSOR) { + return G_TRANSFORM_CURSOR; + } if (t->spacetype == SPACE_SEQ) { return G_TRANSFORM_SEQ; } @@ -1036,12 +1041,14 @@ static void init_proportional_edit(TransInfo *t) case TC_POSE: /* Disable PET, its not usable in pose mode yet T32444. */ case TC_ARMATURE_VERTS: case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: case TC_CURSOR_VIEW3D: case TC_NLA_DATA: case TC_OBJECT_TEXSPACE: case TC_PAINT_CURVE_VERTS: case TC_SCULPT: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: case TC_NONE: default: @@ -1110,6 +1117,7 @@ static void init_TransDataContainers(TransInfo *t, case TC_ACTION_DATA: case TC_GRAPH_EDIT_DATA: case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: case TC_CURSOR_VIEW3D: case TC_MASKING_DATA: case TC_NLA_DATA: @@ -1120,6 +1128,7 @@ static void init_TransDataContainers(TransInfo *t, case TC_PARTICLE_VERTS: case TC_SCULPT: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: case TC_NONE: default: @@ -1204,6 +1213,7 @@ static eTFlag flags_from_data_type(eTConvertType data_type) case TC_NODE_DATA: case TC_PAINT_CURVE_VERTS: case TC_SEQ_DATA: + case TC_SEQ_IMAGE_DATA: case TC_TRACKING_DATA: return T_POINTS | T_2D_EDIT; case TC_ARMATURE_VERTS: @@ -1219,6 +1229,7 @@ static eTFlag flags_from_data_type(eTConvertType data_type) case TC_MESH_UV: return T_EDIT | T_POINTS | T_2D_EDIT; case TC_CURSOR_IMAGE: + case TC_CURSOR_SEQUENCER: return T_2D_EDIT; case TC_PARTICLE_VERTS: return T_POINTS; @@ -1245,6 +1256,9 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur if (t->spacetype == SPACE_IMAGE) { convert_type = TC_CURSOR_IMAGE; } + else if (t->spacetype == SPACE_SEQ) { + convert_type = TC_CURSOR_SEQUENCER; + } else { convert_type = TC_CURSOR_VIEW3D; } @@ -1282,7 +1296,12 @@ static eTConvertType convert_type_get(const TransInfo *t, Object **r_obj_armatur convert_type = TC_NLA_DATA; } else if (t->spacetype == SPACE_SEQ) { - convert_type = TC_SEQ_DATA; + if (t->options & CTX_SEQUENCER_IMAGE) { + convert_type = TC_SEQ_IMAGE_DATA; + } + else { + convert_type = TC_SEQ_DATA; + } } else if (t->spacetype == SPACE_GRAPH) { convert_type = TC_GRAPH_EDIT_DATA; @@ -1387,6 +1406,9 @@ void createTransData(bContext *C, TransInfo *t) case TC_CURSOR_IMAGE: createTransCursor_image(t); break; + case TC_CURSOR_SEQUENCER: + createTransCursor_sequencer(t); + break; case TC_CURSOR_VIEW3D: createTransCursor_view3d(t); break; @@ -1470,6 +1492,10 @@ void createTransData(bContext *C, TransInfo *t) t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transform. */ createTransSeqData(t); break; + case TC_SEQ_IMAGE_DATA: + t->obedit_type = -1; + createTransSeqImageData(t); + break; case TC_TRACKING_DATA: createTransTrackingData(C, t); break; @@ -1701,8 +1727,11 @@ void recalcData(TransInfo *t) case TC_CURSOR_IMAGE: recalcData_cursor_image(t); break; + case TC_CURSOR_SEQUENCER: + recalcData_cursor_sequencer(t); + break; case TC_CURSOR_VIEW3D: - recalcData_cursor(t); + recalcData_cursor_view3d(t); break; case TC_GRAPH_EDIT_DATA: recalcData_graphedit(t); @@ -1746,6 +1775,9 @@ void recalcData(TransInfo *t) case TC_SEQ_DATA: recalcData_sequencer(t); break; + case TC_SEQ_IMAGE_DATA: + recalcData_sequencer_image(t); + break; case TC_TRACKING_DATA: recalcData_tracking(t); break; diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h index 9cb0400cad9..e4f2ab05bec 100644 --- a/source/blender/editors/transform/transform_convert.h +++ b/source/blender/editors/transform/transform_convert.h @@ -85,9 +85,11 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t); /* transform_convert_cursor.c */ void createTransCursor_image(TransInfo *t); +void createTransCursor_sequencer(TransInfo *t); void createTransCursor_view3d(TransInfo *t); void recalcData_cursor_image(TransInfo *t); -void recalcData_cursor(TransInfo *t); +void recalcData_cursor_sequencer(TransInfo *t); +void recalcData_cursor_view3d(TransInfo *t); /* transform_convert_curve.c */ void createTransCurveVerts(TransInfo *t); @@ -218,6 +220,10 @@ void createTransSeqData(TransInfo *t); void recalcData_sequencer(TransInfo *t); void special_aftertrans_update__sequencer(bContext *C, TransInfo *t); +/* transform_convert_sequencer_image.c */ +void createTransSeqImageData(TransInfo *t); +void recalcData_sequencer_image(TransInfo *t); + /* transform_convert_tracking.c */ void createTransTrackingData(bContext *C, TransInfo *t); void recalcData_tracking(TransInfo *t); diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c index 8f896512410..1bbbbc6294e 100644 --- a/source/blender/editors/transform/transform_convert_armature.c +++ b/source/blender/editors/transform/transform_convert_armature.c @@ -1496,8 +1496,10 @@ static void bone_children_clear_transflag(int mode, short around, ListBase *lb) } } -/* Sets transform flags in the bones. - * Returns total number of bones with `BONE_TRANSFORM`. */ +/** + * Sets transform flags in the bones. + * Returns total number of bones with #BONE_TRANSFORM. + */ int transform_convert_pose_transflags_update(Object *ob, const int mode, const short around, @@ -1730,7 +1732,7 @@ void special_aftertrans_update__pose(bContext *C, TransInfo *t) BKE_pose_where_is(t->depsgraph, t->scene, pose_ob); } - /* set BONE_TRANSFORM flags for autokey, gizmo draw might have changed them */ + /* Set BONE_TRANSFORM flags for auto-key, gizmo draw might have changed them. */ if (!canceled && (t->mode != TFM_DUMMY)) { transform_convert_pose_transflags_update(ob, t->mode, t->around, NULL); } diff --git a/source/blender/editors/transform/transform_convert_cursor.c b/source/blender/editors/transform/transform_convert_cursor.c index 1f3eff31205..ed96eba7f6c 100644 --- a/source/blender/editors/transform/transform_convert_cursor.c +++ b/source/blender/editors/transform/transform_convert_cursor.c @@ -19,6 +19,8 @@ /** \file * \ingroup edtransform + * + * Instead of transforming the selection, move the 2D/3D cursor. */ #include "DNA_space_types.h" @@ -35,18 +37,12 @@ #include "transform_convert.h" /* -------------------------------------------------------------------- */ -/** \name Cursor Transform Creation - * - * Instead of transforming the selection, move the 2D/3D cursor. - * +/** \name Shared 2D Cursor Utilities * \{ */ -void createTransCursor_image(TransInfo *t) +static void createTransCursor_2D_impl(TransInfo *t, float cursor_location[2]) { TransData *td; - SpaceImage *sima = t->area->spacedata.first; - float *cursor_location = sima->cursor; - { BLI_assert(t->data_container_len == 1); TransDataContainer *tc = t->data_container; @@ -57,7 +53,7 @@ void createTransCursor_image(TransInfo *t) td->flag = TD_SELECTED; - /* UV coords are scaled by aspects (see UVsToTransData). This also applies for the Cursor in the + /* UV coords are scaled by aspects (see #UVsToTransData). This also applies for the Cursor in the * UV Editor which also means that for display and when the cursor coords are flushed * (recalcData_cursor_image), these are converted each time. */ cursor_location[0] = cursor_location[0] * t->aspect[0]; @@ -74,6 +70,64 @@ void createTransCursor_image(TransInfo *t) copy_v3_v3(td->iloc, cursor_location); } +static void recalcData_cursor_2D_impl(TransInfo *t) +{ + TransDataContainer *tc = t->data_container; + TransData *td = tc->data; + float aspect_inv[2]; + + aspect_inv[0] = 1.0f / t->aspect[0]; + aspect_inv[1] = 1.0f / t->aspect[1]; + + td->loc[0] = td->loc[0] * aspect_inv[0]; + td->loc[1] = td->loc[1] * aspect_inv[1]; + + DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Image Cursor + * \{ */ + +void createTransCursor_image(TransInfo *t) +{ + SpaceImage *sima = t->area->spacedata.first; + createTransCursor_2D_impl(t, sima->cursor); +} + +void recalcData_cursor_image(TransInfo *t) +{ + recalcData_cursor_2D_impl(t); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sequencer Cursor + * \{ */ + +void createTransCursor_sequencer(TransInfo *t) +{ + SpaceSeq *sseq = t->area->spacedata.first; + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return; + } + createTransCursor_2D_impl(t, sseq->cursor); +} + +void recalcData_cursor_sequencer(TransInfo *t) +{ + recalcData_cursor_2D_impl(t); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name View 3D Cursor + * \{ */ + void createTransCursor_view3d(TransInfo *t) { TransData *td; @@ -133,28 +187,7 @@ void createTransCursor_view3d(TransInfo *t) td->ext->rotOrder = cursor->rotation_mode; } -/** \} */ - -/* -------------------------------------------------------------------- */ -/** \name Recalc Cursor - * \{ */ - -void recalcData_cursor_image(TransInfo *t) -{ - TransDataContainer *tc = t->data_container; - TransData *td = tc->data; - float aspect_inv[2]; - - aspect_inv[0] = 1.0f / t->aspect[0]; - aspect_inv[1] = 1.0f / t->aspect[1]; - - td->loc[0] = td->loc[0] * aspect_inv[0]; - td->loc[1] = td->loc[1] * aspect_inv[1]; - - DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); -} - -void recalcData_cursor(TransInfo *t) +void recalcData_cursor_view3d(TransInfo *t) { DEG_id_tag_update(&t->scene->id, ID_RECALC_COPY_ON_WRITE); } diff --git a/source/blender/editors/transform/transform_convert_nla.c b/source/blender/editors/transform/transform_convert_nla.c index 7e5b80c2453..acef8a666e3 100644 --- a/source/blender/editors/transform/transform_convert_nla.c +++ b/source/blender/editors/transform/transform_convert_nla.c @@ -208,30 +208,18 @@ void createTransNlaData(bContext *C, TransInfo *t) /* just set tdn to assume that it only has one handle for now */ tdn->handle = -1; - /* now, link the transform data up to this data */ + /* Now, link the transform data up to this data. */ + td->loc = tdn->h1; + copy_v3_v3(td->iloc, tdn->h1); + if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) { - td->loc = tdn->h1; - copy_v3_v3(td->iloc, tdn->h1); - - /* store all the other gunk that is required by transform */ + /* Store all the other gunk that is required by transform. */ copy_v3_v3(td->center, center); - memset(td->axismtx, 0, sizeof(td->axismtx)); td->axismtx[2][2] = 1.0f; - - td->ext = NULL; - td->val = NULL; - td->flag |= TD_SELECTED; - td->dist = 0.0f; - unit_m3(td->mtx); unit_m3(td->smtx); } - else { - /* time scaling only needs single value */ - td->val = &tdn->h1[0]; - td->ival = tdn->h1[0]; - } td->extra = tdn; td++; @@ -241,30 +229,18 @@ void createTransNlaData(bContext *C, TransInfo *t) * then we're doing both, otherwise, only end */ tdn->handle = (tdn->handle) ? 2 : 1; - /* now, link the transform data up to this data */ + /* Now, link the transform data up to this data. */ + td->loc = tdn->h2; + copy_v3_v3(td->iloc, tdn->h2); + if (ELEM(t->mode, TFM_TRANSLATION, TFM_TIME_EXTEND)) { - td->loc = tdn->h2; - copy_v3_v3(td->iloc, tdn->h2); - - /* store all the other gunk that is required by transform */ + /* Store all the other gunk that is required by transform. */ copy_v3_v3(td->center, center); - memset(td->axismtx, 0, sizeof(td->axismtx)); td->axismtx[2][2] = 1.0f; - - td->ext = NULL; - td->val = NULL; - td->flag |= TD_SELECTED; - td->dist = 0.0f; - unit_m3(td->mtx); unit_m3(td->smtx); } - else { - /* time scaling only needs single value */ - td->val = &tdn->h2[0]; - td->ival = tdn->h2[0]; - } td->extra = tdn; td++; diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c index ecc7f01be33..da11666d445 100644 --- a/source/blender/editors/transform/transform_convert_node.c +++ b/source/blender/editors/transform/transform_convert_node.c @@ -173,9 +173,11 @@ void flushTransNodes(TransInfo *t) } else { /* Edge panning functions expect window coordinates, mval is relative to region */ - const float x = t->region->winrct.xmin + t->mval[0]; - const float y = t->region->winrct.ymin + t->mval[1]; - UI_view2d_edge_pan_apply(t->context, customdata, x, y); + const int xy[2] = { + t->region->winrct.xmin + t->mval[0], + t->region->winrct.ymin + t->mval[1], + }; + UI_view2d_edge_pan_apply(t->context, customdata, xy); } } diff --git a/source/blender/editors/transform/transform_convert_object.c b/source/blender/editors/transform/transform_convert_object.c index bcbac009948..7e4e0892420 100644 --- a/source/blender/editors/transform/transform_convert_object.c +++ b/source/blender/editors/transform/transform_convert_object.c @@ -388,7 +388,7 @@ static void set_trans_object_base_flags(TransInfo *t) if (parsel != NULL) { /* Rotation around local centers are allowed to propagate. */ if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL)) { + (ELEM(t->mode, TFM_ROTATION, TFM_TRACKBALL))) { base->flag_legacy |= BA_TRANSFORM_CHILD; } else { @@ -432,8 +432,7 @@ static int count_proportional_objects(TransInfo *t) /* Clear all flags we need. It will be used to detect dependencies. */ trans_object_base_deps_flag_prepare(view_layer); /* Rotations around local centers are allowed to propagate, so we take all objects. */ - if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (t->mode == TFM_ROTATION || t->mode == TFM_TRACKBALL))) { + if (!((t->around == V3D_AROUND_LOCAL_ORIGINS) && (ELEM(t->mode, TFM_ROTATION, TFM_TRACKBALL)))) { /* Mark all parents. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (BASE_SELECTED_EDITABLE(v3d, base) && BASE_SELECTABLE(v3d, base)) { @@ -910,7 +909,8 @@ void recalcData_objects(TransInfo *t) if (motionpath_update) { /* Update motion paths once for all transformed objects. */ - ED_objects_recalculate_paths(t->context, t->scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME); + ED_objects_recalculate_paths_selected( + t->context, t->scene, OBJECT_PATH_CALC_RANGE_CURRENT_FRAME); } if (t->options & CTX_OBMODE_XFORM_SKIP_CHILDREN) { @@ -958,25 +958,25 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) } BLI_freelistN(&pidlist); - /* pointcache refresh */ + /* Point-cache refresh. */ if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED)) { DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } - /* Needed for proper updating of "quick cached" dynamics. */ - /* Creates troubles for moving animated objects without */ - /* autokey though, probably needed is an anim sys override? */ - /* Please remove if some other solution is found. -jahka */ + /* Needed for proper updating of "quick cached" dynamics. + * Creates troubles for moving animated objects without + * auto-key though, probably needed is an animation-system override? + * NOTE(@jahka): Please remove if some other solution is found. */ DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM); - /* Set autokey if necessary */ + /* Set auto-key if necessary. */ if (!canceled) { autokeyframe_object(C, t->scene, t->view_layer, ob, t->mode); } motionpath_update |= motionpath_need_update_object(t->scene, ob); - /* restore rigid body transform */ + /* Restore rigid body transform. */ if (ob->rigidbody_object && canceled) { float ctime = BKE_scene_ctime_get(t->scene); if (BKE_rigidbody_check_sim_running(t->scene->rigidbody_world, ctime)) { @@ -994,7 +994,7 @@ void special_aftertrans_update__object(bContext *C, TransInfo *t) /* Update motion paths once for all transformed objects. */ const eObjectPathCalcRange range = canceled ? OBJECT_PATH_CALC_RANGE_CURRENT_FRAME : OBJECT_PATH_CALC_RANGE_CHANGED; - ED_objects_recalculate_paths(C, t->scene, range); + ED_objects_recalculate_paths_selected(C, t->scene, range); } clear_trans_object_base_flags(t); diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index a2698b342d0..70089164d8a 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -275,7 +275,7 @@ static void seq_transform_cancel(TransInfo *t, SeqCollection *transformed_strips SEQ_transform_seqbase_shuffle(seqbase, seq, t->scene); } - SEQ_time_update_sequence_bounds(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbase, seq); } } @@ -327,7 +327,8 @@ static void seq_transform_update_effects(TransInfo *t, SeqCollection *collection Sequence *seq; SEQ_ITERATOR_FOREACH (seq, collection) { if ((seq->type & SEQ_TYPE_EFFECT) && (seq->seq1 || seq->seq2 || seq->seq3)) { - SEQ_time_update_sequence(t->scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); + SEQ_time_update_sequence(t->scene, seqbase, seq); } } } @@ -457,6 +458,7 @@ static void seq_transform_handle_overwrite_split(const TransInfo *t, SEQ_edit_strip_split( bmain, scene, seqbase, split_strip, transformed->enddisp, SEQ_SPLIT_SOFT, NULL); SEQ_edit_flag_for_removal(scene, seqbase_active_get(t), split_strip); + SEQ_edit_remove_flagged_sequences(t->scene, seqbase_active_get(t)); } /* Trim strips by adjusting handle position. @@ -487,7 +489,9 @@ static void seq_transform_handle_overwrite_trim(const TransInfo *t, BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); SEQ_transform_set_right_handle_frame(seq, transformed->startdisp); } - SEQ_time_update_sequence(t->scene, seq); + + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); + SEQ_time_update_sequence(t->scene, seqbase, seq); } SEQ_collection_free(targets); } @@ -495,8 +499,8 @@ static void seq_transform_handle_overwrite_trim(const TransInfo *t, static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *transformed_strips) { SeqCollection *targets = query_overwrite_targets(t, transformed_strips); + SeqCollection *strips_to_delete = SEQ_collection_create(__func__); - bool strips_delete = false; Sequence *target; Sequence *transformed; SEQ_ITERATOR_FOREACH (target, targets) { @@ -508,13 +512,10 @@ static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *tr const eOvelapDescrition overlap = overlap_description_get(transformed, target); if (overlap == STRIP_OVERLAP_IS_FULL) { - /* Remove covered strip. */ - SEQ_edit_flag_for_removal(t->scene, seqbase_active_get(t), target); - strips_delete = true; + SEQ_collection_append_strip(target, strips_to_delete); } else if (overlap == STRIP_OVERLAP_IS_INSIDE) { seq_transform_handle_overwrite_split(t, transformed, target); - strips_delete = true; } else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { seq_transform_handle_overwrite_trim(t, transformed, target, overlap); @@ -524,9 +525,16 @@ static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *tr SEQ_collection_free(targets); - if (strips_delete) { + /* Remove covered strips. This must be done in separate loop, because `SEQ_edit_strip_split()` + * also uses `SEQ_edit_remove_flagged_sequences()`. See T91096. */ + if (SEQ_collection_len(strips_to_delete) > 0) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips_to_delete) { + SEQ_edit_flag_for_removal(t->scene, seqbase_active_get(t), seq); + } SEQ_edit_remove_flagged_sequences(t->scene, seqbase_active_get(t)); } + SEQ_collection_free(strips_to_delete); } static void seq_transform_handle_overlap_shuffle(const TransInfo *t, @@ -704,7 +712,9 @@ BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int s /* Calculate this strip and all nested strips. * Children are ALWAYS transformed first so we don't need to do this in another loop. */ - SEQ_time_update_sequence(sce, seq); + + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(sce)); + SEQ_time_update_sequence(sce, seqbase, seq); if (sel_flag == SELECT) { SEQ_offset_animdata(sce, seq, seq->start - old_start); @@ -744,13 +754,13 @@ static void flushTransSeq(TransInfo *t) SEQ_transform_set_left_handle_frame(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); - SEQ_time_update_sequence(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbasep, seq); break; case SEQ_RIGHTSEL: /* No vertical transform. */ SEQ_transform_set_right_handle_frame(seq, new_frame); SEQ_transform_handle_xlimits(seq, tdsq->flag & SEQ_LEFTSEL, tdsq->flag & SEQ_RIGHTSEL); SEQ_transform_fix_single_image_seq_offsets(seq); - SEQ_time_update_sequence(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbasep, seq); break; } } @@ -759,7 +769,7 @@ static void flushTransSeq(TransInfo *t) if (ELEM(t->mode, TFM_SEQ_SLIDE, TFM_TIME_TRANSLATE)) { for (seq = seqbasep->first; seq; seq = seq->next) { if (seq->seq1 || seq->seq2 || seq->seq3) { - SEQ_time_update_sequence(t->scene, seq); + SEQ_time_update_sequence(t->scene, seqbasep, seq); } } } diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c new file mode 100644 index 00000000000..e8af1680a41 --- /dev/null +++ b/source/blender/editors/transform/transform_convert_sequencer_image.c @@ -0,0 +1,216 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edtransform + */ + +#include "MEM_guardedalloc.h" + +#include "DNA_space_types.h" + +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_report.h" + +#include "SEQ_iterator.h" +#include "SEQ_relations.h" +#include "SEQ_sequencer.h" +#include "SEQ_time.h" +#include "SEQ_transform.h" +#include "SEQ_utils.h" + +#include "UI_view2d.h" + +#include "transform.h" +#include "transform_convert.h" + +/** Used for sequencer transform. */ +typedef struct TransDataSeq { + struct Sequence *seq; + float orig_origin_position[2]; + float orig_translation[2]; + float orig_scale[2]; + float orig_rotation; +} TransDataSeq; + +static TransData *SeqToTransData(const Scene *scene, + Sequence *seq, + TransData *td, + TransData2D *td2d, + TransDataSeq *tdseq, + int vert_index) +{ + const StripTransform *transform = seq->strip->transform; + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); + float vertex[2] = {origin[0], origin[1]}; + + /* Add control vertex, so rotation and scale can be calculated. + * All three vertices will form a "L" shape that is aligned to the local strip axis. + */ + if (vert_index == 1) { + vertex[0] += cosf(transform->rotation); + vertex[1] += sinf(transform->rotation); + } + else if (vert_index == 2) { + vertex[0] -= sinf(transform->rotation); + vertex[1] += cosf(transform->rotation); + } + + td2d->loc[0] = vertex[0]; + td2d->loc[1] = vertex[1]; + td2d->loc2d = NULL; + td->loc = td2d->loc; + copy_v3_v3(td->iloc, td->loc); + + td->center[0] = origin[0]; + td->center[1] = origin[1]; + + unit_m3(td->mtx); + unit_m3(td->smtx); + + axis_angle_to_mat3_single(td->axismtx, 'Z', transform->rotation); + normalize_m3(td->axismtx); + + tdseq->seq = seq; + copy_v2_v2(tdseq->orig_origin_position, origin); + tdseq->orig_translation[0] = transform->xofs; + tdseq->orig_translation[1] = transform->yofs; + tdseq->orig_scale[0] = transform->scale_x; + tdseq->orig_scale[1] = transform->scale_y; + tdseq->orig_rotation = transform->rotation; + + td->extra = (void *)tdseq; + td->ext = NULL; + td->flag |= TD_SELECTED; + td->dist = 0.0; + + return td; +} + +static void freeSeqData(TransInfo *UNUSED(t), + TransDataContainer *tc, + TransCustomData *UNUSED(custom_data)) +{ + TransData *td = (TransData *)tc->data; + MEM_freeN(td->extra); +} + +void createTransSeqImageData(TransInfo *t) +{ + Editing *ed = SEQ_editing_get(t->scene); + + if (ed == NULL) { + return; + } + + { + const SpaceSeq *sseq = t->area->spacedata.first; + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return; + } + } + + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, t->scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + const int count = SEQ_collection_len(strips); + if (count == 0) { + SEQ_collection_free(strips); + return; + } + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + tc->custom.type.free_cb = freeSeqData; + + tc->data_len = count * 3; /* 3 vertices per sequence are needed. */ + TransData *td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransSeq TransData"); + TransData2D *td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), + "TransSeq TransData2D"); + TransDataSeq *tdseq = MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq"); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + /* One `Sequence` needs 3 `TransData` entries - center point placed in image origin, then 2 + * points offset by 1 in X and Y direction respectively, so rotation and scale can be + * calculated from these points. */ + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 0); + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 1); + SeqToTransData(t->scene, seq, td++, td2d++, tdseq++, 2); + } + + SEQ_collection_free(strips); +} + +void recalcData_sequencer_image(TransInfo *t) +{ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransData *td = NULL; + TransData2D *td2d = NULL; + int i; + + for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) { + /* Origin. */ + float origin[2]; + copy_v2_v2(origin, td2d->loc); + i++, td++, td2d++; + + /* X and Y control points used to read scale and rotation. */ + float handle_x[2]; + copy_v2_v2(handle_x, td2d->loc); + sub_v2_v2(handle_x, origin); + i++, td++, td2d++; + float handle_y[2]; + copy_v2_v2(handle_y, td2d->loc); + sub_v2_v2(handle_y, origin); + + TransDataSeq *tdseq = td->extra; + Sequence *seq = tdseq->seq; + StripTransform *transform = seq->strip->transform; + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + + /* Calculate translation. */ + float translation[2]; + copy_v2_v2(translation, tdseq->orig_origin_position); + sub_v2_v2(translation, origin); + mul_v2_v2(translation, mirror); + + transform->xofs = tdseq->orig_translation[0] - translation[0]; + transform->yofs = tdseq->orig_translation[1] - translation[1]; + + /* Scale. */ + transform->scale_x = tdseq->orig_scale[0] * fabs(len_v2(handle_x)); + transform->scale_y = tdseq->orig_scale[1] * fabs(len_v2(handle_y)); + + /* Rotation. Scaling can cause negative rotation. */ + if (t->mode == TFM_ROTATION) { + const float orig_dir[2] = {cosf(tdseq->orig_rotation), sinf(tdseq->orig_rotation)}; + float rotation = angle_signed_v2v2(handle_x, orig_dir) * mirror[0] * mirror[1]; + transform->rotation = tdseq->orig_rotation + rotation; + transform->rotation += DEG2RAD(360.0); + transform->rotation = fmod(transform->rotation, DEG2RAD(360.0)); + } + SEQ_relations_invalidate_cache_preprocessed(t->scene, seq); + } +} diff --git a/source/blender/editors/transform/transform_draw_cursors.c b/source/blender/editors/transform/transform_draw_cursors.c index ead8eae0997..af1f3cb72a4 100644 --- a/source/blender/editors/transform/transform_draw_cursors.c +++ b/source/blender/editors/transform/transform_draw_cursors.c @@ -95,7 +95,7 @@ static void drawArrow(const uint pos_id, const enum eArrowDirection dir) bool transform_draw_cursor_poll(bContext *C) { ARegion *region = CTX_wm_region(C); - return (region && region->regiontype == RGN_TYPE_WINDOW) ? 1 : 0; + return (region && ELEM(region->regiontype, RGN_TYPE_WINDOW, RGN_TYPE_PREVIEW)) ? 1 : 0; } /** diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index c493b9bd102..8effb82173b 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -46,6 +46,8 @@ #include "BKE_modifier.h" #include "BKE_paint.h" +#include "SEQ_transform.h" + #include "ED_clip.h" #include "ED_image.h" #include "ED_object.h" @@ -59,6 +61,8 @@ #include "UI_resources.h" #include "UI_view2d.h" +#include "SEQ_sequencer.h" + #include "transform.h" #include "transform_convert.h" #include "transform_mode.h" @@ -69,42 +73,56 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis, short options) { + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_SEQ)) { + return; + } + float v1[3], v2[3], v3[3]; uchar col[3], col2[3]; if (t->spacetype == SPACE_VIEW3D) { View3D *v3d = t->view; - GPU_matrix_push(); - copy_v3_v3(v3, dir); mul_v3_fl(v3, v3d->clip_end); sub_v3_v3v3(v2, center, v3); add_v3_v3v3(v1, center, v3); - - if (options & DRAWLIGHT) { - col[0] = col[1] = col[2] = 220; - } - else { - UI_GetThemeColor3ubv(TH_GRID, col); - } - UI_make_axis_color(col, col2, axis); - - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformColor3ubv(col2); - - immBegin(GPU_PRIM_LINES, 2); - immVertex3fv(pos, v1); - immVertex3fv(pos, v2); - immEnd(); - - immUnbindProgram(); - - GPU_matrix_pop(); } + else if (t->spacetype == SPACE_SEQ) { + View2D *v2d = t->view; + + copy_v3_v3(v3, dir); + float max_dist = max_ff(BLI_rctf_size_x(&v2d->cur), BLI_rctf_size_y(&v2d->cur)); + mul_v3_fl(v3, max_dist); + + sub_v3_v3v3(v2, center, v3); + add_v3_v3v3(v1, center, v3); + } + + GPU_matrix_push(); + + if (options & DRAWLIGHT) { + col[0] = col[1] = col[2] = 220; + } + else { + UI_GetThemeColor3ubv(TH_GRID, col); + } + UI_make_axis_color(col, col2, axis); + + uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor3ubv(col2); + + immBegin(GPU_PRIM_LINES, 2); + immVertex3fv(pos, v1); + immVertex3fv(pos, v2); + immEnd(); + + immUnbindProgram(); + + GPU_matrix_pop(); } /** @@ -335,6 +353,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->options |= CTX_MASK; } } + else if (t->spacetype == SPACE_SEQ && region->regiontype == RGN_TYPE_PREVIEW) { + t->view = ®ion->v2d; + t->around = SEQ_tool_settings_pivot_point_get(t->scene); + t->options |= CTX_SEQUENCER_IMAGE; + } else { if (region) { /* XXX: For now, get View2D from the active region. */ @@ -878,12 +901,18 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) void calculateCenterCursor2D(TransInfo *t, float r_center[2]) { + float cursor_local_buf[2]; const float *cursor = NULL; if (t->spacetype == SPACE_IMAGE) { SpaceImage *sima = (SpaceImage *)t->area->spacedata.first; cursor = sima->cursor; } + if (t->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = (SpaceSeq *)t->area->spacedata.first; + SEQ_image_preview_unit_to_px(t->scene, sseq->cursor, cursor_local_buf); + cursor = cursor_local_buf; + } else if (t->spacetype == SPACE_CLIP) { SpaceClip *space_clip = (SpaceClip *)t->area->spacedata.first; cursor = space_clip->cursor; @@ -1062,7 +1091,7 @@ static void calculateCenter_FromAround(TransInfo *t, int around, float r_center[ calculateCenterMedian(t, r_center); break; case V3D_AROUND_CURSOR: - if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_CLIP)) { + if (ELEM(t->spacetype, SPACE_IMAGE, SPACE_SEQ, SPACE_CLIP)) { calculateCenterCursor2D(t, r_center); } else if (t->spacetype == SPACE_GRAPH) { diff --git a/source/blender/editors/transform/transform_gizmo_2d.c b/source/blender/editors/transform/transform_gizmo_2d.c index 0b677e2560b..7a23a4a92ce 100644 --- a/source/blender/editors/transform/transform_gizmo_2d.c +++ b/source/blender/editors/transform/transform_gizmo_2d.c @@ -32,6 +32,7 @@ #include "DNA_view3d_types.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_layer.h" #include "RNA_access.h" @@ -49,6 +50,11 @@ #include "ED_screen.h" #include "ED_uvedit.h" +#include "SEQ_iterator.h" +#include "SEQ_sequencer.h" +#include "SEQ_time.h" +#include "SEQ_transform.h" + #include "transform.h" /* own include */ /* -------------------------------------------------------------------- */ @@ -65,14 +71,40 @@ static bool gizmo2d_generic_poll(const bContext *C, wmGizmoGroupType *gzgt) return false; } + if (G.moving) { + return false; + } + ScrArea *area = CTX_wm_area(C); + if (area == NULL) { + return false; + } + + /* NOTE: below this is assumed to be a tool gizmo. + * If there are cases that need to check other flags - this function could be split. */ switch (area->spacetype) { case SPACE_IMAGE: { - SpaceImage *sima = area->spacedata.first; + const SpaceImage *sima = area->spacedata.first; Object *obedit = CTX_data_edit_object(C); if (!ED_space_image_show_uvedit(sima, obedit)) { return false; } + break; + } + case SPACE_SEQ: { + const SpaceSeq *sseq = area->spacedata.first; + if (sseq->gizmo_flag & (SEQ_GIZMO_HIDE | SEQ_GIZMO_HIDE_TOOL)) { + return false; + } + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return false; + } + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + if (ed == NULL) { + return false; + } + break; } } @@ -144,6 +176,7 @@ typedef struct GizmoGroup2D { float origin[2]; float min[2]; float max[2]; + float rotation; bool no_cage; @@ -212,7 +245,7 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min } ScrArea *area = CTX_wm_area(C); - bool changed = false; + bool has_select = false; if (area->spacetype == SPACE_IMAGE) { Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); @@ -220,31 +253,140 @@ static bool gizmo2d_calc_bounds(const bContext *C, float *r_center, float *r_min Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, NULL, &objects_len); if (ED_uvedit_minmax_multi(scene, objects, objects_len, r_min, r_max)) { - changed = true; + has_select = true; } MEM_freeN(objects); } + else if (area->spacetype == SPACE_SEQ) { + Scene *scene = CTX_data_scene(C); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + int selected_strips = SEQ_collection_len(strips); + if (selected_strips > 0) { + INIT_MINMAX2(r_min, r_max); + has_select = true; - if (changed == false) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + float quad[4][2]; + SEQ_image_transform_quad_get(scene, seq, selected_strips != 1, quad); + for (int i = 0; i < 4; i++) { + minmax_v2v2_v2(r_min, r_max, quad[i]); + } + } + } + SEQ_collection_free(strips); + if (selected_strips > 1) { + /* Don't draw the cage as transforming multiple strips isn't currently very useful as it + * doesn't behave as one would expect. + * + * This is because our current transform system doesn't support shearing which would make the + * scaling transforms of the bounding box behave weirdly. + * In addition to this, the rotation of the bounding box can not currently be hooked up + * properly to read the result from the transform system (when transforming multiple strips). + */ + mid_v2_v2v2(r_center, r_min, r_max); + zero_v2(r_min); + zero_v2(r_max); + return has_select; + } + } + + if (has_select == false) { zero_v2(r_min); zero_v2(r_max); } mid_v2_v2v2(r_center, r_min, r_max); - return changed; + return has_select; +} + +static int gizmo2d_calc_transform_orientation(const bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_SEQ) { + return V3D_ORIENT_GLOBAL; + } + + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + bool use_local_orient = SEQ_collection_len(strips) == 1; + SEQ_collection_free(strips); + + if (use_local_orient) { + return V3D_ORIENT_LOCAL; + } + return V3D_ORIENT_GLOBAL; +} + +static float gizmo2d_calc_rotation(const bContext *C) +{ + ScrArea *area = CTX_wm_area(C); + if (area->spacetype != SPACE_SEQ) { + return 0.0f; + } + + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_active_seqbase_get(ed); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + + if (SEQ_collection_len(strips) == 1) { + /* Only return the strip rotation if only one is selected. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + StripTransform *transform = seq->strip->transform; + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + SEQ_collection_free(strips); + return transform->rotation * mirror[0] * mirror[1]; + } + } + + SEQ_collection_free(strips); + return 0.0f; } static bool gizmo2d_calc_center(const bContext *C, float r_center[2]) { ScrArea *area = CTX_wm_area(C); + Scene *scene = CTX_data_scene(C); bool has_select = false; zero_v2(r_center); if (area->spacetype == SPACE_IMAGE) { SpaceImage *sima = area->spacedata.first; - Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); ED_uvedit_center_from_pivot_ex(sima, scene, view_layer, r_center, sima->around, &has_select); } + else if (area->spacetype == SPACE_SEQ) { + SpaceSeq *sseq = area->spacedata.first; + const int pivot_point = scene->toolsettings->sequencer_tool_settings->pivot_point; + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SeqCollection *strips = SEQ_query_rendered_strips(seqbase, scene->r.cfra, 0); + SEQ_filter_selected_strips(strips); + has_select = SEQ_collection_len(strips) != 0; + + if (pivot_point == V3D_AROUND_CURSOR) { + SEQ_image_preview_unit_to_px(scene, sseq->cursor, r_center); + } + else if (has_select) { + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + float origin[2]; + SEQ_image_transform_origin_offset_pixelspace_get(scene, seq, origin); + add_v2_v2(r_center, origin); + } + mul_v2_fl(r_center, 1.0f / SEQ_collection_len(strips)); + } + + SEQ_collection_free(strips); + } return has_select; } @@ -338,7 +480,7 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup } } - RNA_boolean_set(ptr, "release_confirm", 1); + RNA_boolean_set(ptr, "release_confirm", true); } { @@ -356,17 +498,17 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X, ot_resize, NULL); PropertyRNA *prop_release_confirm = RNA_struct_find_property(ptr, "release_confirm"); PropertyRNA *prop_constraint_axis = RNA_struct_find_property(ptr, "constraint_axis"); - RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); RNA_property_boolean_set(ptr, prop_release_confirm, true); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X, ot_resize, NULL); + RNA_property_boolean_set(ptr, prop_release_confirm, true); RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_x); - RNA_property_boolean_set(ptr, prop_release_confirm, true); ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y, ot_resize, NULL); - RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); RNA_property_boolean_set(ptr, prop_release_confirm, true); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); ptr = WM_gizmo_operator_set(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y, ot_resize, NULL); - RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); RNA_property_boolean_set(ptr, prop_release_confirm, true); + RNA_property_boolean_set_array(ptr, prop_constraint_axis, constraint_y); ptr = WM_gizmo_operator_set( ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y, ot_resize, NULL); @@ -385,97 +527,51 @@ static void gizmo2d_xform_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgroup } } -static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup) +static void rotate_around_center_v2(float point[2], const float center[2], const float angle) { - gizmo2d_xform_setup(C, gzgroup); - GizmoGroup2D *ggd = gzgroup->customdata; - ggd->no_cage = true; + float tmp[2]; + + sub_v2_v2v2(tmp, point, center); + rotate_v2_v2fl(point, tmp, angle); + add_v2_v2(point, center); } static void gizmo2d_xform_refresh(const bContext *C, wmGizmoGroup *gzgroup) { GizmoGroup2D *ggd = gzgroup->customdata; - float origin[3]; bool has_select; if (ggd->no_cage) { - has_select = gizmo2d_calc_center(C, origin); + has_select = gizmo2d_calc_center(C, ggd->origin); } else { - has_select = gizmo2d_calc_bounds(C, origin, ggd->min, ggd->max); + has_select = gizmo2d_calc_bounds(C, ggd->origin, ggd->min, ggd->max); + ggd->rotation = gizmo2d_calc_rotation(C); } - copy_v2_v2(ggd->origin, origin); + bool show_cage = !ggd->no_cage && !equals_v2v2(ggd->min, ggd->max); - if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { - Scene *scene = CTX_data_scene(C); - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - } - if (has_select == false) { + /* Nothing selected. Disable gizmo drawing and return. */ + ggd->cage->flag |= WM_GIZMO_HIDDEN; for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN; } - ggd->cage->flag |= WM_GIZMO_HIDDEN; + return; } - else { - if (show_cage) { - ggd->cage->flag &= ~WM_GIZMO_HIDDEN; - for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { - wmGizmo *gz = ggd->translate_xy[i]; - gz->flag |= WM_GIZMO_HIDDEN; - } - } - else { - ggd->cage->flag |= WM_GIZMO_HIDDEN; - for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { - wmGizmo *gz = ggd->translate_xy[i]; - gz->flag &= ~WM_GIZMO_HIDDEN; - } + + if (!show_cage) { + /* Disable cage gizmo drawing and return. */ + ggd->cage->flag |= WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + ggd->translate_xy[i]->flag &= ~WM_GIZMO_HIDDEN; } + return; + } - if (show_cage) { - wmGizmoOpElem *gzop; - float mid[2]; - const float *min = ggd->min; - const float *max = ggd->max; - mid_v2_v2v2(mid, min, max); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X); - PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override"); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], mid[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], mid[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], min[1], 0.0f}); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){max[0], min[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], max[1], 0.0f}); - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){min[0], min[1], 0.0f}); - - gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE); - RNA_property_float_set_array( - &gzop->ptr, prop_center_override, (float[3]){mid[0], mid[1], 0.0f}); - } + /* We will show the cage gizmo! Setup all necessary data. */ + ggd->cage->flag &= ~WM_GIZMO_HIDDEN; + for (int i = 0; i < ARRAY_SIZE(ggd->translate_xy); i++) { + ggd->translate_xy[i]->flag |= WM_GIZMO_HIDDEN; } } @@ -484,7 +580,6 @@ static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) ARegion *region = CTX_wm_region(C); GizmoGroup2D *ggd = gzgroup->customdata; float origin[3] = {UNPACK2(ggd->origin), 0.0f}; - const float origin_aa[3] = {UNPACK2(ggd->origin), 0.0f}; gizmo2d_origin_to_region(region, origin); @@ -494,9 +589,144 @@ static void gizmo2d_xform_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup) } UI_view2d_view_to_region_m4(®ion->v2d, ggd->cage->matrix_space); - WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa); + /* Define the bounding box of the gizmo in the offset transform matrix. */ + unit_m4(ggd->cage->matrix_offset); ggd->cage->matrix_offset[0][0] = (ggd->max[0] - ggd->min[0]); ggd->cage->matrix_offset[1][1] = (ggd->max[1] - ggd->min[1]); + + ScrArea *area = CTX_wm_area(C); + + if (area->spacetype == SPACE_SEQ) { + gizmo2d_calc_center(C, origin); + + float matrix_rotate[4][4]; + unit_m4(matrix_rotate); + copy_v3_v3(matrix_rotate[3], origin); + rotate_m4(matrix_rotate, 'Z', ggd->rotation); + unit_m4(ggd->cage->matrix_basis); + mul_m4_m4m4(ggd->cage->matrix_basis, matrix_rotate, ggd->cage->matrix_basis); + + float mid[2]; + sub_v2_v2v2(mid, origin, ggd->origin); + mul_v2_fl(mid, -1.0f); + copy_v2_v2(ggd->cage->matrix_offset[3], mid); + } + else { + const float origin_aa[3] = {UNPACK2(ggd->origin), 0.0f}; + WM_gizmo_set_matrix_offset_location(ggd->cage, origin_aa); + } +} + +static void gizmo2d_xform_invoke_prepare(const bContext *C, + wmGizmoGroup *gzgroup, + wmGizmo *UNUSED(gz), + const wmEvent *UNUSED(event)) +{ + GizmoGroup2D *ggd = gzgroup->customdata; + wmGizmoOpElem *gzop; + const float *mid = ggd->origin; + const float *min = ggd->min; + const float *max = ggd->max; + + /* Define the different transform center points that will be used when grabbing the corners or + * rotating with the gizmo. + * + * The coordinates are referred to as their cardinal directions: + * N + * o + *NW | NE + * x-----------x + * | | + *W| C |E + * | | + * x-----------x + *SW S SE + */ + float n[3] = {mid[0], max[1], 0.0f}; + float w[3] = {min[0], mid[1], 0.0f}; + float e[3] = {max[0], mid[1], 0.0f}; + float s[3] = {mid[0], min[1], 0.0f}; + + float nw[3] = {min[0], max[1], 0.0f}; + float ne[3] = {max[0], max[1], 0.0f}; + float sw[3] = {min[0], min[1], 0.0f}; + float se[3] = {max[0], min[1], 0.0f}; + + float c[3] = {mid[0], mid[1], 0.0f}; + + float orient_matrix[3][3]; + + ScrArea *area = CTX_wm_area(C); + + if (ggd->rotation != 0.0f && area->spacetype == SPACE_SEQ) { + float origin[3]; + gizmo2d_calc_center(C, origin); + /* We need to rotate the cardinal points so they align with the rotated bounding box. */ + + rotate_around_center_v2(n, origin, ggd->rotation); + rotate_around_center_v2(w, origin, ggd->rotation); + rotate_around_center_v2(e, origin, ggd->rotation); + rotate_around_center_v2(s, origin, ggd->rotation); + + rotate_around_center_v2(nw, origin, ggd->rotation); + rotate_around_center_v2(ne, origin, ggd->rotation); + rotate_around_center_v2(sw, origin, ggd->rotation); + rotate_around_center_v2(se, origin, ggd->rotation); + + rotate_around_center_v2(c, origin, ggd->rotation); + + axis_angle_to_mat3_single(orient_matrix, 'Z', ggd->rotation); + } + + int orient_type = gizmo2d_calc_transform_orientation(C); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X); + PropertyRNA *prop_center_override = RNA_struct_find_property(&gzop->ptr, "center_override"); + PropertyRNA *prop_mouse_dir = RNA_struct_find_property(&gzop->ptr, "mouse_dir_constraint"); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, e); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[0]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, w); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[0]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, n); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[1]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, s); + RNA_property_float_set_array(&gzop->ptr, prop_mouse_dir, orient_matrix[1]); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MIN_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, ne); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MIN_X_MAX_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, se); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MIN_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, nw); + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_SCALE_MAX_X_MAX_Y); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, sw); + + gzop = WM_gizmo_operator_get(ggd->cage, ED_GIZMO_CAGE2D_PART_ROTATE); + RNA_property_float_set_array(&gzop->ptr, prop_center_override, c); +} + +void ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType *gzgt) +{ + gzgt->poll = gizmo2d_generic_poll; + gzgt->setup = gizmo2d_xform_setup; + gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; + gzgt->refresh = gizmo2d_xform_refresh; + gzgt->draw_prepare = gizmo2d_xform_draw_prepare; + gzgt->invoke_prepare = gizmo2d_xform_invoke_prepare; +} + +static void gizmo2d_xform_setup_no_cage(const bContext *C, wmGizmoGroup *gzgroup) +{ + gizmo2d_xform_setup(C, gzgroup); + GizmoGroup2D *ggd = gzgroup->customdata; + ggd->no_cage = true; } static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C, @@ -509,15 +739,6 @@ static void gizmo2d_xform_no_cage_message_subscribe(const struct bContext *C, gizmo2d_pivot_point_message_subscribe(gzgroup, mbus, screen, area, region); } -void ED_widgetgroup_gizmo2d_xform_callbacks_set(wmGizmoGroupType *gzgt) -{ - gzgt->poll = gizmo2d_generic_poll; - gzgt->setup = gizmo2d_xform_setup; - gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; - gzgt->refresh = gizmo2d_xform_refresh; - gzgt->draw_prepare = gizmo2d_xform_draw_prepare; -} - void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt) { ED_widgetgroup_gizmo2d_xform_callbacks_set(gzgt); @@ -539,6 +760,7 @@ void ED_widgetgroup_gizmo2d_xform_no_cage_callbacks_set(wmGizmoGroupType *gzgt) typedef struct GizmoGroup_Resize2D { wmGizmo *gizmo_xy[3]; float origin[2]; + float rotation; } GizmoGroup_Resize2D; static GizmoGroup_Resize2D *gizmogroup2d_resize_init(wmGizmoGroup *gzgroup) @@ -571,6 +793,7 @@ static void gizmo2d_resize_refresh(const bContext *C, wmGizmoGroup *gzgroup) ggd->gizmo_xy[i]->flag &= ~WM_GIZMO_HIDDEN; } copy_v2_v2(ggd->origin, origin); + ggd->rotation = gizmo2d_calc_rotation(C); } } @@ -580,21 +803,18 @@ static void gizmo2d_resize_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup GizmoGroup_Resize2D *ggd = gzgroup->customdata; float origin[3] = {UNPACK2(ggd->origin), 0.0f}; - if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { - Scene *scene = CTX_data_scene(C); - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - } - gizmo2d_origin_to_region(region, origin); for (int i = 0; i < ARRAY_SIZE(ggd->gizmo_xy); i++) { wmGizmo *gz = ggd->gizmo_xy[i]; WM_gizmo_set_matrix_location(gz, origin); + + if (i < 2) { + float axis[3] = {0.0f}, rotated_axis[3]; + axis[i] = 1.0f; + rotate_v3_v3v3fl(rotated_axis, axis, (float[3]){0, 0, 1}, ggd->rotation); + WM_gizmo_set_matrix_rotation_from_z_axis(gz, rotated_axis); + } } } @@ -617,10 +837,6 @@ static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgrou /* set up widget data */ RNA_float_set(gz->ptr, "length", 1.0f); - float axis[3] = {0.0f}; - axis[i] = 1.0f; - WM_gizmo_set_matrix_rotation_from_z_axis(gz, axis); - RNA_enum_set(gz->ptr, "draw_style", ED_GIZMO_ARROW_STYLE_BOX); WM_gizmo_set_line_width(gz, GIZMO_AXIS_LINE_WIDTH); @@ -661,6 +877,18 @@ static void gizmo2d_resize_setup(const bContext *UNUSED(C), wmGizmoGroup *gzgrou } } +static void gizmo2d_resize_invoke_prepare(const bContext *C, + wmGizmoGroup *UNUSED(gzgroup), + wmGizmo *gz, + const wmEvent *UNUSED(event)) +{ + wmGizmoOpElem *gzop; + int orient_type = gizmo2d_calc_transform_orientation(C); + + gzop = WM_gizmo_operator_get(gz, 0); + RNA_enum_set(&gzop->ptr, "orient_type", orient_type); +} + static void gizmo2d_resize_message_subscribe(const struct bContext *C, struct wmGizmoGroup *gzgroup, struct wmMsgBus *mbus) @@ -678,6 +906,7 @@ void ED_widgetgroup_gizmo2d_resize_callbacks_set(wmGizmoGroupType *gzgt) gzgt->setup_keymap = WM_gizmogroup_setup_keymap_generic_maybe_drag; gzgt->refresh = gizmo2d_resize_refresh; gzgt->draw_prepare = gizmo2d_resize_draw_prepare; + gzgt->invoke_prepare = gizmo2d_resize_invoke_prepare; gzgt->message_subscribe = gizmo2d_resize_message_subscribe; } @@ -729,16 +958,6 @@ static void gizmo2d_rotate_draw_prepare(const bContext *C, wmGizmoGroup *gzgroup GizmoGroup_Rotate2D *ggd = gzgroup->customdata; float origin[3] = {UNPACK2(ggd->origin), 0.0f}; - if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { - Scene *scene = CTX_data_scene(C); - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - } - gizmo2d_origin_to_region(region, origin); wmGizmo *gz = ggd->gizmo; diff --git a/source/blender/editors/transform/transform_gizmo_3d.c b/source/blender/editors/transform/transform_gizmo_3d.c index 0fa179c4f74..e79fdc4890a 100644 --- a/source/blender/editors/transform/transform_gizmo_3d.c +++ b/source/blender/editors/transform/transform_gizmo_3d.c @@ -646,10 +646,8 @@ int ED_transform_calc_gizmo_stats(const bContext *C, Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C); ViewLayer *view_layer = CTX_data_view_layer(C); View3D *v3d = area->spacedata.first; - Object *obedit = CTX_data_edit_object(C); RegionView3D *rv3d = region->regiondata; Base *base; - Object *ob = OBACT(view_layer); bGPdata *gpd = CTX_data_gpencil_data(C); const bool is_gp_edit = GPENCIL_ANY_MODE(gpd); const bool is_curve_edit = GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd); @@ -660,6 +658,17 @@ int ED_transform_calc_gizmo_stats(const bContext *C, (params->orientation_index - 1) : BKE_scene_orientation_get_index(scene, SCE_ORIENT_DEFAULT); + Object *ob = OBACT(view_layer); + Object *obedit = OBEDIT_FROM_OBACT(ob); + if (ob && ob->mode & OB_MODE_WEIGHT_PAINT) { + Object *obpose = BKE_object_pose_armature_get(ob); + if (obpose != NULL) { + ob = obpose; + } + } + + tbounds->use_matrix_space = false; + /* transform widget matrix */ unit_m4(rv3d->twmat); @@ -682,13 +691,16 @@ int ED_transform_calc_gizmo_stats(const bContext *C, reset_tw_center(tbounds); copy_m3_m4(tbounds->axis, rv3d->twmat); - if (params->use_local_axis && (ob && ob->mode & OB_MODE_EDIT)) { + if (params->use_local_axis && (ob && ob->mode & (OB_MODE_EDIT | OB_MODE_POSE))) { float diff_mat[3][3]; copy_m3_m4(diff_mat, ob->obmat); normalize_m3(diff_mat); invert_m3(diff_mat); mul_m3_m3m3(tbounds->axis, tbounds->axis, diff_mat); normalize_m3(tbounds->axis); + + tbounds->use_matrix_space = true; + copy_m4_m4(tbounds->matrix_space, ob->obmat); } if (is_gp_edit) { @@ -963,8 +975,10 @@ int ED_transform_calc_gizmo_stats(const bContext *C, if (totsel_iter) { float mat_local[4][4]; - if (use_mat_local) { - mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); + if (params->use_local_axis) { + if (use_mat_local) { + mul_m4_m4m4(mat_local, ob->imat, ob_iter->obmat); + } } /* use channels to get stats */ @@ -1670,13 +1684,6 @@ static void WIDGETGROUP_gizmo_refresh(const bContext *C, wmGizmoGroup *gzgroup) RegionView3D *rv3d = region->regiondata; struct TransformBounds tbounds; - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - if (ggd->use_twtype_refresh) { ggd->twtype = v3d->gizmo_show_object & ggd->twtype_init; if (ggd->twtype != ggd->twtype_prev) { @@ -2105,13 +2112,6 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr struct TransformBounds tbounds; - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - const int orient_index = BKE_scene_orientation_get_index_from_flag(scene, SCE_ORIENT_SCALE); if ((ED_transform_calc_gizmo_stats(C, @@ -2124,10 +2124,8 @@ static void WIDGETGROUP_xform_cage_refresh(const bContext *C, wmGizmoGroup *gzgr WM_gizmo_set_flag(gz, WM_GIZMO_HIDDEN, true); } else { - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - if (ob && ob->mode & OB_MODE_EDIT) { - copy_m4_m4(gz->matrix_space, ob->obmat); + if (tbounds.use_matrix_space) { + copy_m4_m4(gz->matrix_space, tbounds.matrix_space); } else { unit_m4(gz->matrix_space); @@ -2316,13 +2314,6 @@ static void WIDGETGROUP_xform_shear_refresh(const bContext *C, wmGizmoGroup *gzg struct XFormShearWidgetGroup *xgzgroup = gzgroup->customdata; struct TransformBounds tbounds; - if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { - gzgroup->use_fallback_keymap = true; - } - else { - gzgroup->use_fallback_keymap = false; - } - /* Needed to test view orientation changes. */ copy_m3_m4(xgzgroup->prev.viewinv_m3, rv3d->viewinv); diff --git a/source/blender/editors/transform/transform_gizmo_extrude_3d.c b/source/blender/editors/transform/transform_gizmo_extrude_3d.c index ca4ed01c0f6..6e89c3de197 100644 --- a/source/blender/editors/transform/transform_gizmo_extrude_3d.c +++ b/source/blender/editors/transform/transform_gizmo_extrude_3d.c @@ -381,11 +381,9 @@ static void gizmo_mesh_extrude_refresh(const bContext *C, wmGizmoGroup *gzgroup) if (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK) { WM_gizmo_set_flag(ggd->invoke_view, WM_GIZMO_HIDDEN, false); - gzgroup->use_fallback_keymap = true; } else { WM_gizmo_set_flag(ggd->invoke_view, WM_GIZMO_HIDDEN, true); - gzgroup->use_fallback_keymap = false; } } diff --git a/source/blender/editors/transform/transform_mode.c b/source/blender/editors/transform/transform_mode.c index b9fb8a86752..5e0abbc1a08 100644 --- a/source/blender/editors/transform/transform_mode.c +++ b/source/blender/editors/transform/transform_mode.c @@ -75,7 +75,7 @@ bool transdata_check_local_center(const TransInfo *t, short around) /* implicit: (t->flag & T_EDIT) */ (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE, OB_GPENCIL)) || (t->spacetype == SPACE_GRAPH) || - (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE)))); + (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE | CTX_SEQUENCER_IMAGE)))); } /* Informs if the mode can be switched during modal. */ @@ -1054,6 +1054,11 @@ void ElementResize(const TransInfo *t, } if (t->options & (CTX_OBJECT | CTX_POSE_BONE)) { + if (t->options & CTX_POSE_BONE) { + /* Without this, the resulting location of scaled bones aren't correct, + * especially noticeable scaling root or disconnected bones around the cursor, see T92515. */ + mul_mat3_m4_v3(tc->poseobj->obmat, vec); + } mul_m3_v3(td->smtx, vec); } @@ -1082,9 +1087,24 @@ void transform_mode_init(TransInfo *t, wmOperator *op, const int mode) case TFM_ROTATION: initRotation(t); break; - case TFM_RESIZE: - initResize(t); + case TFM_RESIZE: { + float mouse_dir_constraint[3]; + if (op) { + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "mouse_dir_constraint"); + if (prop) { + RNA_property_float_get_array(op->ptr, prop, mouse_dir_constraint); + } + else { + /* Resize is expected to have this property. */ + BLI_assert(!STREQ(op->idname, "TRANSFORM_OT_resize")); + } + } + else { + zero_v3(mouse_dir_constraint); + } + initResize(t, mouse_dir_constraint); break; + } case TFM_SKIN_RESIZE: initSkinResize(t); break; diff --git a/source/blender/editors/transform/transform_mode.h b/source/blender/editors/transform/transform_mode.h index d8601000ddb..c561d1c8a4f 100644 --- a/source/blender/editors/transform/transform_mode.h +++ b/source/blender/editors/transform/transform_mode.h @@ -121,7 +121,7 @@ void initMirror(TransInfo *t); void initPushPull(TransInfo *t); /* transform_mode_resize.c */ -void initResize(TransInfo *t); +void initResize(TransInfo *t, float mouse_dir_constraint[3]); /* transform_mode_rotate.c */ void initRotation(TransInfo *t); diff --git a/source/blender/editors/transform/transform_mode_resize.c b/source/blender/editors/transform/transform_mode_resize.c index 65f4623b3be..28323460626 100644 --- a/source/blender/editors/transform/transform_mode_resize.c +++ b/source/blender/editors/transform/transform_mode_resize.c @@ -201,14 +201,45 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) ED_area_status_text(t->area, str); } -void initResize(TransInfo *t) +void initResize(TransInfo *t, float mouse_dir_constraint[3]) { t->mode = TFM_RESIZE; t->transform = applyResize; t->tsnap.applySnap = ApplySnapResize; t->tsnap.distance = ResizeBetween; - initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); + if (is_zero_v3(mouse_dir_constraint)) { + initMouseInputMode(t, &t->mouse, INPUT_SPRING_FLIP); + } + else { + int mval_start[2], mval_end[2]; + float mval_dir[3], t_mval[2]; + float viewmat[3][3]; + + copy_m3_m4(viewmat, t->viewmat); + mul_v3_m3v3(mval_dir, viewmat, mouse_dir_constraint); + normalize_v2(mval_dir); + if (is_zero_v2(mval_dir)) { + /* The screen space direction is orthogonal to the view. + * Fall back to constraining on the Y axis. */ + mval_dir[0] = 0; + mval_dir[1] = 1; + } + + mval_start[0] = t->center2d[0]; + mval_start[1] = t->center2d[1]; + + t_mval[0] = t->mval[0] - mval_start[0]; + t_mval[1] = t->mval[1] - mval_start[1]; + project_v2_v2v2(mval_dir, t_mval, mval_dir); + + mval_end[0] = t->center2d[0] + mval_dir[0]; + mval_end[1] = t->center2d[1] + mval_dir[1]; + + setCustomPoints(t, &t->mouse, mval_end, mval_start); + + initMouseInputMode(t, &t->mouse, INPUT_CUSTOM_RATIO); + } t->flag |= T_NULL_ONE; t->num.val_flag[0] |= NUM_NULL_ONE; diff --git a/source/blender/editors/transform/transform_mode_shrink_fatten.c b/source/blender/editors/transform/transform_mode_shrink_fatten.c index b96b8103392..b18e0aa0c7f 100644 --- a/source/blender/editors/transform/transform_mode_shrink_fatten.c +++ b/source/blender/editors/transform/transform_mode_shrink_fatten.c @@ -188,7 +188,9 @@ void initShrinkFatten(TransInfo *t) { /* If not in mesh edit mode, fallback to Resize. */ if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) { - initResize(t); + float no_mouse_dir_constraint[3]; + zero_v3(no_mouse_dir_constraint); + initResize(t, no_mouse_dir_constraint); } else { t->mode = TFM_SHRINKFATTEN; diff --git a/source/blender/editors/transform/transform_mode_timescale.c b/source/blender/editors/transform/transform_mode_timescale.c index 50fd714727b..0a7ae54982e 100644 --- a/source/blender/editors/transform/transform_mode_timescale.c +++ b/source/blender/editors/transform/transform_mode_timescale.c @@ -87,7 +87,7 @@ static void applyTimeScaleValue(TransInfo *t, float value) } /* now, calculate the new value */ - *(td->val) = ((td->ival - startx) * fac) + startx; + td->loc[0] = ((td->iloc[0] - startx) * fac) + startx; } } } diff --git a/source/blender/editors/transform/transform_ops.c b/source/blender/editors/transform/transform_ops.c index 3a4a9342e18..5ed340abf97 100644 --- a/source/blender/editors/transform/transform_ops.c +++ b/source/blender/editors/transform/transform_ops.c @@ -59,6 +59,7 @@ typedef struct TransformModeItem { void (*opfunc)(wmOperatorType *); } TransformModeItem; +static const float VecZero[3] = {0, 0, 0}; static const float VecOne[3] = {1, 1, 1}; static const char OP_TRANSLATION[] = "TRANSFORM_OT_translate"; @@ -783,6 +784,19 @@ static void TRANSFORM_OT_resize(struct wmOperatorType *ot) RNA_def_float_vector( ot->srna, "value", 3, VecOne, -FLT_MAX, FLT_MAX, "Scale", "", -FLT_MAX, FLT_MAX); + PropertyRNA *prop; + prop = RNA_def_float_vector(ot->srna, + "mouse_dir_constraint", + 3, + VecZero, + -FLT_MAX, + FLT_MAX, + "Mouse Directional Constraint", + "", + -FLT_MAX, + FLT_MAX); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + WM_operatortype_props_advanced_begin(ot); Transform_Properties(ot, diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c index 1e3acdf1071..a1ed66c96a3 100644 --- a/source/blender/editors/transform/transform_orientations.c +++ b/source/blender/editors/transform/transform_orientations.c @@ -30,6 +30,7 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" +#include "DNA_sequence_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" @@ -52,6 +53,8 @@ #include "ED_armature.h" +#include "SEQ_select.h" + #include "transform.h" #include "transform_orientations.h" @@ -602,6 +605,15 @@ short transform_orientation_matrix_get(bContext *C, return V3D_ORIENT_CUSTOM_MATRIX; } + if (t->spacetype == SPACE_SEQ && t->options & CTX_SEQUENCER_IMAGE) { + Scene *scene = t->scene; + Sequence *seq = SEQ_select_active_get(scene); + if (seq && seq->strip->transform && orient_index == V3D_ORIENT_LOCAL) { + axis_angle_to_mat3_single(r_spacemtx, 'Z', seq->strip->transform->rotation); + return orient_index; + } + } + Object *ob = CTX_data_active_object(C); Object *obedit = CTX_data_edit_object(C); Scene *scene = t->scene; diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 05a20a14477..7f27d5fb180 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -48,6 +48,7 @@ #include "ED_node.h" #include "ED_transform_snap_object_context.h" #include "ED_uvedit.h" +#include "ED_view3d.h" #include "UI_resources.h" #include "UI_view2d.h" @@ -247,7 +248,7 @@ void drawSnapping(const struct bContext *C, TransInfo *t) loc_cur = t->tsnap.snapPoint; } - ED_gizmotypes_snap_3d_draw_util( + ED_view3d_cursor_snap_draw_util( rv3d, loc_prev, loc_cur, normal, col, activeCol, t->tsnap.snapElem); GPU_depth_test(GPU_DEPTH_LESS_EQUAL); @@ -374,6 +375,8 @@ void applyProject(TransInfo *t) if (ED_transform_snap_object_project_view3d( t->tsnap.object_context, t->depsgraph, + t->region, + t->view, SCE_SNAP_MODE_FACE, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, @@ -590,6 +593,11 @@ static void initSnappingMode(TransInfo *t) t->tsnap.project = 0; t->tsnap.mode = ts->snap_uv_mode; + if ((t->tsnap.mode & SCE_SNAP_MODE_INCREMENT) && (ts->snap_uv_flag & SCE_SNAP_ABS_GRID) && + (t->mode == TFM_TRANSLATION)) { + t->tsnap.mode &= ~SCE_SNAP_MODE_INCREMENT; + t->tsnap.mode |= SCE_SNAP_MODE_GRID; + } } else if (t->spacetype == SPACE_SEQ) { t->tsnap.mode = SEQ_tool_settings_snap_mode_get(t->scene); @@ -608,7 +616,7 @@ static void initSnappingMode(TransInfo *t) t->tsnap.mode |= SCE_SNAP_MODE_GRID; } } - else if (ELEM(t->spacetype, SPACE_GRAPH, SPACE_ACTION, SPACE_NLA)) { + else if (ELEM(t->spacetype, SPACE_ACTION, SPACE_NLA)) { /* No incremental snapping. */ t->tsnap.mode = 0; } @@ -666,8 +674,7 @@ static void initSnappingMode(TransInfo *t) if (t->spacetype == SPACE_VIEW3D) { if (t->tsnap.object_context == NULL) { t->tsnap.use_backface_culling = snap_use_backface_culling(t); - t->tsnap.object_context = ED_transform_snap_object_context_create_view3d( - t->scene, 0, t->region, t->view); + t->tsnap.object_context = ED_transform_snap_object_context_create(t->scene, 0); if (t->data_type == TC_MESH_VERTS) { /* Ignore elements being transformed. */ @@ -1237,9 +1244,11 @@ short snapObjectsTransform( TransInfo *t, const float mval[2], float *dist_px, float r_loc[3], float r_no[3]) { float *target = (t->tsnap.status & TARGET_INIT) ? t->tsnap.snapTarget : t->center_global; - return ED_transform_snap_object_project_view3d_ex( + return ED_transform_snap_object_project_view3d( t->tsnap.object_context, t->depsgraph, + t->region, + t->view, t->settings->snap_mode, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, @@ -1251,10 +1260,7 @@ short snapObjectsTransform( target, dist_px, r_loc, - r_no, - NULL, - NULL, - NULL); + r_no); } /** \} */ @@ -1275,6 +1281,8 @@ bool peelObjectsTransform(TransInfo *t, ED_transform_snap_object_project_all_view3d_ex( t->tsnap.object_context, t->depsgraph, + t->region, + t->view, &(const struct SnapObjectParams){ .snap_select = t->tsnap.modeSelect, .edit_mode_type = (t->flag & T_EDIT) != 0 ? SNAP_GEOM_EDIT : SNAP_GEOM_FINAL, @@ -1315,7 +1323,7 @@ bool peelObjectsTransform(TransInfo *t, } } } - /* in this case has only one hit. treat as raycast */ + /* In this case has only one hit. treat as ray-cast. */ if (hit_max == NULL) { hit_max = hit_min; } @@ -1502,7 +1510,8 @@ bool transform_snap_grid(TransInfo *t, float *val) return false; } - if (t->spacetype != SPACE_VIEW3D) { + /* Don't do grid snapping if not in 3D viewport or UV editor */ + if (!ELEM(t->spacetype, SPACE_VIEW3D, SPACE_IMAGE)) { return false; } diff --git a/source/blender/editors/transform/transform_snap_animation.c b/source/blender/editors/transform/transform_snap_animation.c index 08335924ddf..8828f96247d 100644 --- a/source/blender/editors/transform/transform_snap_animation.c +++ b/source/blender/editors/transform/transform_snap_animation.c @@ -56,8 +56,10 @@ short getAnimEdit_SnapMode(TransInfo *t) } } else if (t->spacetype == SPACE_GRAPH) { + if ((t->mode == TFM_TRANSLATION) && activeSnap(t)) { + return autosnap; + } SpaceGraph *sipo = (SpaceGraph *)t->area->spacedata.first; - if (sipo) { autosnap = sipo->autosnap; } diff --git a/source/blender/editors/transform/transform_snap_object.c b/source/blender/editors/transform/transform_snap_object.c index 811f30c96e5..3254d56d795 100644 --- a/source/blender/editors/transform/transform_snap_object.c +++ b/source/blender/editors/transform/transform_snap_object.c @@ -69,17 +69,6 @@ enum eViewProj { VIEW_PROJ_PERSP = -1, }; -typedef struct SnapData { - float mval[2]; - float pmat[4][4]; /* perspective matrix */ - float win_size[2]; /* win x and y */ - enum eViewProj view_proj; - float clip_plane[MAX_CLIPPLANE_LEN][4]; - short clip_plane_len; - short snap_to_flag; - bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */ -} SnapData; - typedef struct SnapObjectData { enum { SNAP_MESH = 1, @@ -113,14 +102,6 @@ struct SnapObjectContext { int flag; - /* Optional: when performing screen-space projection. - * otherwise this doesn't take viewport into account. */ - bool use_v3d; - struct { - const struct View3D *v3d; - const struct ARegion *region; - } v3d_data; - /* Object -> SnapObjectData map */ struct { GHash *object_map; @@ -138,6 +119,21 @@ struct SnapObjectContext { void *user_data; } edit_mesh; } callbacks; + + struct { + Depsgraph *depsgraph; + const ARegion *region; + const View3D *v3d; + + float mval[2]; + float pmat[4][4]; /* perspective matrix */ + float win_size[2]; /* win x and y */ + enum eViewProj view_proj; + float clip_plane[MAX_CLIPPLANE_LEN][4]; + short clip_plane_len; + short snap_to_flag; + bool has_occlusion_plane; /* Ignore plane of occlusion in curves. */ + } runtime; }; /** \} */ @@ -457,27 +453,25 @@ typedef void (*IterSnapObjsCallback)(SnapObjectContext *sctx, * Walks through all objects in the scene to create the list of objects to snap. */ static void iter_snap_objects(SnapObjectContext *sctx, - Depsgraph *depsgraph, const struct SnapObjectParams *params, IterSnapObjsCallback sob_callback, void *data) { - ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); - const View3D *v3d = sctx->v3d_data.v3d; + ViewLayer *view_layer = DEG_get_input_view_layer(sctx->runtime.depsgraph); const eSnapSelect snap_select = params->snap_select; const eSnapEditType edit_mode_type = params->edit_mode_type; const bool use_backface_culling = params->use_backface_culling; Base *base_act = view_layer->basact; if (snap_select == SNAP_ONLY_ACTIVE) { - Object *obj_eval = DEG_get_evaluated_object(depsgraph, base_act->object); + Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base_act->object); sob_callback( sctx, obj_eval, obj_eval->obmat, edit_mode_type, use_backface_culling, true, data); return; } for (Base *base = view_layer->object_bases.first; base != NULL; base = base->next) { - if (!BASE_VISIBLE(v3d, base)) { + if (!BASE_VISIBLE(sctx->runtime.v3d, base)) { continue; } @@ -499,10 +493,15 @@ static void iter_snap_objects(SnapObjectContext *sctx, continue; } } + else if (snap_select == SNAP_SELECTABLE) { + if (!(base->flag & BASE_SELECTABLE)) { + continue; + } + } - Object *obj_eval = DEG_get_evaluated_object(depsgraph, base->object); + Object *obj_eval = DEG_get_evaluated_object(sctx->runtime.depsgraph, base->object); if (obj_eval->transflag & OB_DUPLI || BKE_object_has_geometry_set_instances(obj_eval)) { - ListBase *lb = object_duplilist(depsgraph, sctx->scene, obj_eval); + ListBase *lb = object_duplilist(sctx->runtime.depsgraph, sctx->scene, obj_eval); for (DupliObject *dupli_ob = lb->first; dupli_ob; dupli_ob = dupli_ob->next) { BLI_assert(DEG_is_evaluated_object(dupli_ob->ob)); sob_callback(sctx, @@ -724,7 +723,7 @@ static bool raycastMesh(SnapObjectContext *sctx, } /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); + BoundBox *bb = BKE_object_boundbox_get(ob_eval); if (bb) { /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!isect_ray_aabb_v3_simple( @@ -1031,12 +1030,6 @@ static void raycast_obj_fn(SnapObjectContext *sctx, bool retval = false; if (use_occlusion_test) { - if ((edit_mode_type == SNAP_GEOM_EDIT) && sctx->use_v3d && - XRAY_FLAG_ENABLED(sctx->v3d_data.v3d)) { - /* Use of occlude geometry in editing mode disabled. */ - return; - } - if (ELEM(ob_eval->dt, OB_BOUNDBOX, OB_WIRE)) { /* Do not hit objects that are in wire or bounding box * display mode. */ @@ -1147,6 +1140,7 @@ static void raycast_obj_fn(SnapObjectContext *sctx, */ static bool raycastObjects(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_dir[3], @@ -1162,6 +1156,17 @@ static bool raycastObjects(SnapObjectContext *sctx, float r_obmat[4][4], ListBase *r_hit_list) { + if (params->use_occlusion_test && v3d && XRAY_FLAG_ENABLED(v3d)) { + /* General testing of occlusion geometry is disabled if the snap is not intended for the edit + * cage. */ + if (params->edit_mode_type == SNAP_GEOM_EDIT) { + return false; + } + } + + sctx->runtime.depsgraph = depsgraph; + sctx->runtime.v3d = v3d; + struct RaycastObjUserData data = { .ray_start = ray_start, .ray_dir = ray_dir, @@ -1177,7 +1182,7 @@ static bool raycastObjects(SnapObjectContext *sctx, .ret = false, }; - iter_snap_objects(sctx, depsgraph, params, raycast_obj_fn, &data); + iter_snap_objects(sctx, params, raycast_obj_fn, &data); return data.ret; } @@ -1554,7 +1559,6 @@ static void cb_snap_tri_verts(void *userdata, * \{ */ static short snap_mesh_polygon(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, const float obmat[4][4], bool use_backface_culling, @@ -1568,16 +1572,16 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, short elem = 0; float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } BVHTreeNearest nearest = { @@ -1590,14 +1594,14 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); if (sod->type == SNAP_MESH) { BVHTreeFromMesh *treedata = &sod->treedata_mesh; const MPoly *mp = &sod->poly[*r_index]; const MLoop *ml = &treedata->loop[mp->loopstart]; - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { elem = SCE_SNAP_MODE_EDGE; BLI_assert(treedata->edge != NULL); for (int i = mp->totloop; i--; ml++) { @@ -1605,7 +1609,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, ml->e, &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } } @@ -1616,7 +1620,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, ml->v, &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } } @@ -1629,7 +1633,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BMFace *f = BM_face_at_index(em->bm, *r_index); BMLoop *l_iter, *l_first; l_iter = l_first = BM_FACE_FIRST_LOOP(f); - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { elem = SCE_SNAP_MODE_EDGE; BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_EDGE); BM_mesh_elem_table_ensure(em->bm, BM_VERT | BM_EDGE); @@ -1638,7 +1642,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BM_elem_index_get(l_iter->e), &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } while ((l_iter = l_iter->next) != l_first); } @@ -1651,7 +1655,7 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, BM_elem_index_get(l_iter->v), &neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest); } while ((l_iter = l_iter->next) != l_first); } @@ -1680,7 +1684,6 @@ static short snap_mesh_polygon(SnapObjectContext *sctx, } static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, const float obmat[4][4], float original_dist_px, @@ -1704,7 +1707,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); int vindex[2]; nearest2d.get_edge_verts_index(*r_index, nearest2d.userdata, vindex); @@ -1716,10 +1719,10 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, struct DistProjectedAABBPrecalc neasrest_precalc; { float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); } BVHTreeNearest nearest = { @@ -1736,7 +1739,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, /* do nothing */ } else { - short snap_to_flag = snapdata->snap_to_flag; + short snap_to_flag = sctx->runtime.snap_to_flag; int e_mode_len = ((snap_to_flag & SCE_SNAP_MODE_EDGE) != 0) + ((snap_to_flag & SCE_SNAP_MODE_VERTEX) != 0) + ((snap_to_flag & SCE_SNAP_MODE_EDGE_MIDPOINT) != 0); @@ -1785,7 +1788,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, } } - if (prev_co && (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { + if (prev_co && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { float v_near[3], va_g[3], vb_g[3]; mul_v3_m4v3(va_g, obmat, v_pair[0]); @@ -1797,7 +1800,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, if (len_squared_v3v3(prev_co, v_near) > FLT_EPSILON) { dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, sctx->runtime.pmat, sctx->runtime.win_size, sctx->runtime.mval); if (test_projected_vert_dist(&neasrest_precalc, NULL, @@ -1828,7 +1831,7 @@ static short snap_mesh_edge_verts_mixed(SnapObjectContext *sctx, return elem; } -static short snapArmature(SnapData *snapdata, +static short snapArmature(SnapObjectContext *sctx, Object *ob_eval, const float obmat[4][4], /* read/write args */ @@ -1840,35 +1843,39 @@ static short snapArmature(SnapData *snapdata, { short retval = 0; - if (snapdata->snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */ + if (sctx->runtime.snap_to_flag == SCE_SNAP_MODE_FACE) { /* Currently only edge and vert */ return retval; } float lpmat[4][4], dist_px_sq = square_f(*dist_px); - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); bool use_obedit = ((bArmature *)ob_eval->data)->edbo != NULL; if (use_obedit == false) { /* Test BoundBox */ BoundBox *bb = BKE_armature_boundbox_get(ob_eval); - if (bb && !snap_bound_box_check_dist( - bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + if (bb && !snap_bound_box_check_dist(bb->vec[0], + bb->vec[6], + lpmat, + sctx->runtime.win_size, + sctx->runtime.mval, + dist_px_sq)) { return retval; } } float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; bArmature *arm = ob_eval->data; if (arm->edbo) { @@ -1878,17 +1885,17 @@ static short snapArmature(SnapData *snapdata, if ((eBone->flag & (BONE_HIDDEN_A | BONE_ROOTSEL | BONE_TIPSEL)) == 0) { bool has_vert_snap = false; - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { has_vert_snap = test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, eBone->head, &dist_px_sq, r_loc); has_vert_snap |= test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, eBone->tail, &dist_px_sq, @@ -1898,10 +1905,10 @@ static short snapArmature(SnapData *snapdata, retval = SCE_SNAP_MODE_VERTEX; } } - if (!has_vert_snap && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (!has_vert_snap && sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { if (test_projected_edge_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, eBone->head, eBone->tail, @@ -1923,17 +1930,17 @@ static short snapArmature(SnapData *snapdata, const float *head_vec = pchan->pose_head; const float *tail_vec = pchan->pose_tail; - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { has_vert_snap = test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, head_vec, &dist_px_sq, r_loc); has_vert_snap |= test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, tail_vec, &dist_px_sq, @@ -1943,10 +1950,10 @@ static short snapArmature(SnapData *snapdata, retval = SCE_SNAP_MODE_VERTEX; } } - if (!has_vert_snap && snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (!has_vert_snap && sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { if (test_projected_edge_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, head_vec, tail_vec, @@ -1972,7 +1979,7 @@ static short snapArmature(SnapData *snapdata, return 0; } -static short snapCurve(SnapData *snapdata, +static short snapCurve(SnapObjectContext *sctx, Object *ob_eval, const float obmat[4][4], bool use_obedit, @@ -1986,7 +1993,7 @@ static short snapCurve(SnapData *snapdata, bool has_snap = false; /* only vertex snapping mode (eg control points and handles) supported for now) */ - if ((snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) == 0) { + if ((sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) == 0) { return 0; } @@ -1994,19 +2001,23 @@ static short snapCurve(SnapData *snapdata, float dist_px_sq = square_f(*dist_px); float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, lpmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, lpmat, sctx->runtime.win_size, sctx->runtime.mval); use_obedit = use_obedit && BKE_object_is_in_editmode(ob_eval); if (use_obedit == false) { /* Test BoundBox */ BoundBox *bb = BKE_curve_boundbox_get(ob_eval); - if (bb && !snap_bound_box_check_dist( - bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + if (bb && !snap_bound_box_check_dist(bb->vec[0], + bb->vec[6], + lpmat, + sctx->runtime.win_size, + sctx->runtime.mval, + dist_px_sq)) { return 0; } } @@ -2014,10 +2025,10 @@ static short snapCurve(SnapData *snapdata, float tobmat[4][4]; transpose_m4_m4(tobmat, obmat); - float(*clip_planes)[4] = snapdata->clip_plane; - int clip_plane_len = snapdata->clip_plane_len; + float(*clip_planes)[4] = sctx->runtime.clip_plane; + int clip_plane_len = sctx->runtime.clip_plane_len; - if (snapdata->has_occlusion_plane) { + if (sctx->runtime.has_occlusion_plane) { /* We snap to vertices even if occluded. */ clip_planes++; clip_plane_len--; @@ -2028,11 +2039,11 @@ static short snapCurve(SnapData *snapdata, mul_v4_m4v4(clip_planes_local[i], tobmat, clip_planes[i]); } - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; for (Nurb *nu = (use_obedit ? cu->editnurb->nurbs.first : cu->nurb.first); nu; nu = nu->next) { for (int u = 0; u < nu->pntsu; u++) { - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (use_obedit) { if (nu->bezt) { /* don't snap to selected (moving) or hidden */ @@ -2123,7 +2134,7 @@ static short snapCurve(SnapData *snapdata, } /* may extend later (for now just snaps to empty center) */ -static short snap_object_center(SnapData *snapdata, +static short snap_object_center(const SnapObjectContext *sctx, Object *ob_eval, const float obmat[4][4], /* read/write args */ @@ -2140,24 +2151,24 @@ static short snap_object_center(SnapData *snapdata, } /* for now only vertex supported */ - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, sctx->runtime.pmat, sctx->runtime.win_size, sctx->runtime.mval); float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; float dist_px_sq = square_f(*dist_px); float co[3]; copy_v3_v3(co, obmat[3]); if (test_projected_vert_dist(&neasrest_precalc, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, is_persp, co, &dist_px_sq, @@ -2179,7 +2190,6 @@ static short snap_object_center(SnapData *snapdata, } static short snapCamera(const SnapObjectContext *sctx, - SnapData *snapdata, Object *object, float obmat[4][4], /* read/write args */ @@ -2193,7 +2203,7 @@ static short snapCamera(const SnapObjectContext *sctx, Scene *scene = sctx->scene; - bool is_persp = snapdata->view_proj == VIEW_PROJ_PERSP; + bool is_persp = sctx->runtime.view_proj == VIEW_PROJ_PERSP; float dist_px_sq = square_f(*dist_px); float orig_camera_mat[4][4], orig_camera_imat[4][4], imat[4][4]; @@ -2201,7 +2211,7 @@ static short snapCamera(const SnapObjectContext *sctx, MovieTracking *tracking; if (clip == NULL) { - return snap_object_center(snapdata, object, obmat, dist_px, r_loc, r_no, r_index); + return snap_object_center(sctx, object, obmat, dist_px, r_loc, r_no, r_index); } if (object->transflag & OB_DUPLI) { return retval; @@ -2214,10 +2224,10 @@ static short snapCamera(const SnapObjectContext *sctx, invert_m4_m4(orig_camera_imat, orig_camera_mat); invert_m4_m4(imat, obmat); - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { struct DistProjectedAABBPrecalc neasrest_precalc; dist_squared_to_projected_aabb_precalc( - &neasrest_precalc, snapdata->pmat, snapdata->win_size, snapdata->mval); + &neasrest_precalc, sctx->runtime.pmat, sctx->runtime.win_size, sctx->runtime.mval); MovieTrackingObject *tracking_object; for (tracking_object = tracking->objects.first; tracking_object; @@ -2252,8 +2262,8 @@ static short snapCamera(const SnapObjectContext *sctx, mul_m4_v3(vertex_obmat, bundle_pos); if (test_projected_vert_dist(&neasrest_precalc, - snapdata->clip_plane, - snapdata->clip_plane_len, + sctx->runtime.clip_plane, + sctx->runtime.clip_plane_len, is_persp, bundle_pos, &dist_px_sq, @@ -2277,7 +2287,6 @@ static short snapCamera(const SnapObjectContext *sctx, } static short snapMesh(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, Mesh *me_eval, const float obmat[4][4], @@ -2290,23 +2299,24 @@ static short snapMesh(SnapObjectContext *sctx, float r_no[3], int *r_index) { - BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); + BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); if (me_eval->totvert == 0) { return 0; } - if (me_eval->totedge == 0 && !(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (me_eval->totedge == 0 && !(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { return 0; } float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); float dist_px_sq = square_f(*dist_px); /* Test BoundBox */ - BoundBox *bb = BKE_mesh_boundbox_get(ob_eval); - if (bb && !snap_bound_box_check_dist( - bb->vec[0], bb->vec[6], lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + BoundBox *bb = BKE_object_boundbox_get(ob_eval); + if (bb && + !snap_bound_box_check_dist( + bb->vec[0], bb->vec[6], lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { return 0; } @@ -2330,7 +2340,7 @@ static short snapMesh(SnapObjectContext *sctx, treedata_tmp.looptri_allocated)); } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { if (sod->has_loose_vert && sod->bvhtree[1] == NULL) { sod->bvhtree[1] = BKE_bvhtree_from_mesh_get( &treedata_tmp, me_eval, BVHTREE_FROM_LOOSEVERTS, 2); @@ -2353,7 +2363,7 @@ static short snapMesh(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2364,18 +2374,18 @@ static short snapMesh(SnapObjectContext *sctx, float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - if (sod->bvhtree[1] && (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (sod->bvhtree[1] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { /* snap to loose verts */ BLI_bvhtree_find_nearest_projected(sod->bvhtree[1], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_vert, &nearest2d); @@ -2383,15 +2393,15 @@ static short snapMesh(SnapObjectContext *sctx, last_index = nearest.index; } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { if (sod->bvhtree[0]) { /* snap to loose edges */ BLI_bvhtree_find_nearest_projected(sod->bvhtree[0], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_edge, &nearest2d); @@ -2401,10 +2411,10 @@ static short snapMesh(SnapObjectContext *sctx, /* snap to looptris */ BLI_bvhtree_find_nearest_projected(treedata->tree, lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_tri_edges, &nearest2d); @@ -2415,15 +2425,15 @@ static short snapMesh(SnapObjectContext *sctx, } } else { - BLI_assert(snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX); + BLI_assert(sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX); if (sod->bvhtree[0]) { /* snap to loose edge verts */ BLI_bvhtree_find_nearest_projected(sod->bvhtree[0], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_edge_verts, &nearest2d); @@ -2433,10 +2443,10 @@ static short snapMesh(SnapObjectContext *sctx, /* snap to looptri verts */ BLI_bvhtree_find_nearest_projected(treedata->tree, lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_tri_verts, &nearest2d); @@ -2468,7 +2478,6 @@ static short snapMesh(SnapObjectContext *sctx, } static short snapEditMesh(SnapObjectContext *sctx, - SnapData *snapdata, Object *ob_eval, BMEditMesh *em, const float obmat[4][4], @@ -2480,9 +2489,9 @@ static short snapEditMesh(SnapObjectContext *sctx, float r_no[3], int *r_index) { - BLI_assert(snapdata->snap_to_flag != SCE_SNAP_MODE_FACE); + BLI_assert(sctx->runtime.snap_to_flag != SCE_SNAP_MODE_FACE); - if ((snapdata->snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { + if ((sctx->runtime.snap_to_flag & ~SCE_SNAP_MODE_FACE) == SCE_SNAP_MODE_VERTEX) { if (em->bm->totvert == 0) { return 0; } @@ -2494,7 +2503,7 @@ static short snapEditMesh(SnapObjectContext *sctx, } float lpmat[4][4]; - mul_m4_m4m4(lpmat, snapdata->pmat, obmat); + mul_m4_m4m4(lpmat, sctx->runtime.pmat, obmat); float dist_px_sq = square_f(*dist_px); @@ -2504,11 +2513,11 @@ static short snapEditMesh(SnapObjectContext *sctx, /* was BKE_boundbox_ray_hit_check, see: cf6ca226fa58 */ if (!snap_bound_box_check_dist( - sod->min, sod->max, lpmat, snapdata->win_size, snapdata->mval, dist_px_sq)) { + sod->min, sod->max, lpmat, sctx->runtime.win_size, sctx->runtime.mval, dist_px_sq)) { return 0; } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX) { BVHTreeFromEditMesh treedata = {.tree = sod->bvhtree[0]}; if (treedata.tree == NULL) { @@ -2540,7 +2549,7 @@ static short snapEditMesh(SnapObjectContext *sctx, } } - if (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE) { + if (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE) { BVHTreeFromEditMesh treedata = {.tree = sod->bvhtree[1]}; if (treedata.tree == NULL) { @@ -2574,7 +2583,7 @@ static short snapEditMesh(SnapObjectContext *sctx, Nearest2dUserData nearest2d; nearest2d_data_init( - sod, snapdata->view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); + sod, sctx->runtime.view_proj == VIEW_PROJ_PERSP, use_backface_culling, &nearest2d); BVHTreeNearest nearest = { .index = -1, @@ -2585,35 +2594,35 @@ static short snapEditMesh(SnapObjectContext *sctx, float tobmat[4][4], clip_planes_local[MAX_CLIPPLANE_LEN][4]; transpose_m4_m4(tobmat, obmat); - for (int i = snapdata->clip_plane_len; i--;) { - mul_v4_m4v4(clip_planes_local[i], tobmat, snapdata->clip_plane[i]); + for (int i = sctx->runtime.clip_plane_len; i--;) { + mul_v4_m4v4(clip_planes_local[i], tobmat, sctx->runtime.clip_plane[i]); } - if (sod->bvhtree[0] && (snapdata->snap_to_flag & SCE_SNAP_MODE_VERTEX)) { + if (sod->bvhtree[0] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_VERTEX)) { BM_mesh_elem_table_ensure(em->bm, BM_VERT); BM_mesh_elem_index_ensure(em->bm, BM_VERT); BLI_bvhtree_find_nearest_projected(sod->bvhtree[0], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_vert, &nearest2d); } - if (sod->bvhtree[1] && (snapdata->snap_to_flag & SCE_SNAP_MODE_EDGE)) { + if (sod->bvhtree[1] && (sctx->runtime.snap_to_flag & SCE_SNAP_MODE_EDGE)) { int last_index = nearest.index; nearest.index = -1; BM_mesh_elem_table_ensure(em->bm, BM_EDGE | BM_VERT); BM_mesh_elem_index_ensure(em->bm, BM_EDGE | BM_VERT); BLI_bvhtree_find_nearest_projected(sod->bvhtree[1], lpmat, - snapdata->win_size, - snapdata->mval, + sctx->runtime.win_size, + sctx->runtime.mval, clip_planes_local, - snapdata->clip_plane_len, + sctx->runtime.clip_plane_len, &nearest, cb_snap_edge, &nearest2d); @@ -2650,7 +2659,6 @@ static short snapEditMesh(SnapObjectContext *sctx, } struct SnapObjUserData { - SnapData *snapdata; /* read/write args */ float *dist_px; /* return args */ @@ -2686,7 +2694,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, /* Operators only update the editmesh looptris of the original mesh. */ BMEditMesh *em_orig = BKE_editmesh_from_object(DEG_get_original_object(ob_eval)); retval = snapEditMesh(sctx, - dt->snapdata, ob_eval, em_orig, obmat, @@ -2703,7 +2710,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, } retval = snapMesh(sctx, - dt->snapdata, ob_eval, me_eval, obmat, @@ -2716,11 +2722,10 @@ static void snap_obj_fn(SnapObjectContext *sctx, break; } case OB_ARMATURE: - retval = snapArmature( - dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + retval = snapArmature(sctx, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CURVE: - retval = snapCurve(dt->snapdata, + retval = snapCurve(sctx, ob_eval, obmat, edit_mode_type == SNAP_GEOM_EDIT, @@ -2734,7 +2739,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, Mesh *mesh_eval = BKE_object_get_evaluated_mesh(ob_eval); if (mesh_eval) { retval |= snapMesh(sctx, - dt->snapdata, ob_eval, mesh_eval, obmat, @@ -2751,11 +2755,10 @@ static void snap_obj_fn(SnapObjectContext *sctx, case OB_GPENCIL: case OB_LAMP: retval = snap_object_center( - dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + sctx, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; case OB_CAMERA: - retval = snapCamera( - sctx, dt->snapdata, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); + retval = snapCamera(sctx, ob_eval, obmat, dt->dist_px, dt->r_loc, dt->r_no, dt->r_index); break; } @@ -2796,8 +2799,6 @@ static void snap_obj_fn(SnapObjectContext *sctx, * \param r_obmat: Object matrix (may not be #Object.obmat with dupli-instances). */ static short snapObjectsRay(SnapObjectContext *sctx, - Depsgraph *depsgraph, - SnapData *snapdata, const struct SnapObjectParams *params, /* read/write args */ /* Parameters below cannot be const, because they are assigned to a @@ -2811,7 +2812,6 @@ static short snapObjectsRay(SnapObjectContext *sctx, float r_obmat[4][4]) { struct SnapObjUserData data = { - .snapdata = snapdata, .dist_px = dist_px, .r_loc = r_loc, .r_no = r_no, @@ -2821,7 +2821,7 @@ static short snapObjectsRay(SnapObjectContext *sctx, .ret = 0, }; - iter_snap_objects(sctx, depsgraph, params, snap_obj_fn, &data); + iter_snap_objects(sctx, params, snap_obj_fn, &data); return data.ret; } @@ -2848,21 +2848,6 @@ SnapObjectContext *ED_transform_snap_object_context_create(Scene *scene, int fla return sctx; } -SnapObjectContext *ED_transform_snap_object_context_create_view3d(Scene *scene, - int flag, - /* extra args for view3d */ - const ARegion *region, - const View3D *v3d) -{ - SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, flag); - - sctx->use_v3d = true; - sctx->v3d_data.region = region; - sctx->v3d_data.v3d = v3d; - - return sctx; -} - static void snap_object_data_free(void *sod_v) { SnapObjectData *sod = sod_v; @@ -2896,6 +2881,7 @@ void ED_transform_snap_object_context_set_editmesh_callbacks( bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -2908,6 +2894,7 @@ bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, { return raycastObjects(sctx, depsgraph, + v3d, params, ray_start, ray_normal, @@ -2929,6 +2916,7 @@ bool ED_transform_snap_object_project_ray_ex(SnapObjectContext *sctx, */ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -2946,6 +2934,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, bool retval = raycastObjects(sctx, depsgraph, + v3d, params, ray_start, ray_normal, @@ -2978,6 +2967,7 @@ bool ED_transform_snap_object_project_ray_all(SnapObjectContext *sctx, */ static bool transform_snap_context_project_ray_impl(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_start[3], const float ray_normal[3], @@ -2988,14 +2978,25 @@ static bool transform_snap_context_project_ray_impl(SnapObjectContext *sctx, bool ret; /* try snap edge, then face if it fails */ - ret = ED_transform_snap_object_project_ray_ex( - sctx, depsgraph, params, ray_start, ray_normal, ray_depth, r_co, r_no, NULL, NULL, NULL); + ret = ED_transform_snap_object_project_ray_ex(sctx, + depsgraph, + v3d, + params, + ray_start, + ray_normal, + ray_depth, + r_co, + r_no, + NULL, + NULL, + NULL); return ret; } bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, Depsgraph *depsgraph, + const View3D *v3d, const struct SnapObjectParams *params, const float ray_origin[3], const float ray_direction[3], @@ -3010,12 +3011,14 @@ bool ED_transform_snap_object_project_ray(SnapObjectContext *sctx, } return transform_snap_context_project_ray_impl( - sctx, depsgraph, params, ray_origin, ray_direction, ray_depth, r_co, r_no); + sctx, depsgraph, v3d, params, ray_origin, ray_direction, ray_depth, r_co, r_no); } static short transform_snap_context_project_view3d_mixed_impl( SnapObjectContext *sctx, Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const ushort snap_to_flag, const struct SnapObjectParams *params, const float mval[2], @@ -3025,7 +3028,8 @@ static short transform_snap_context_project_view3d_mixed_impl( float r_no[3], int *r_index, Object **r_ob, - float r_obmat[4][4]) + float r_obmat[4][4], + float r_face_nor[3]) { BLI_assert((snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) != @@ -3042,21 +3046,18 @@ static short transform_snap_context_project_view3d_mixed_impl( float obmat[4][4]; int index = -1; - const ARegion *region = sctx->v3d_data.region; const RegionView3D *rv3d = region->regiondata; - bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(sctx->v3d_data.v3d); + bool use_occlusion_test = params->use_occlusion_test && !XRAY_ENABLED(v3d); + + sctx->runtime.depsgraph = depsgraph; + sctx->runtime.region = region; + sctx->runtime.v3d = v3d; if (snap_to_flag & SCE_SNAP_MODE_FACE || use_occlusion_test) { float ray_start[3], ray_normal[3]; - if (!ED_view3d_win_to_ray_clipped_ex(depsgraph, - sctx->v3d_data.region, - sctx->v3d_data.v3d, - mval, - NULL, - ray_normal, - ray_start, - true)) { + if (!ED_view3d_win_to_ray_clipped_ex( + depsgraph, region, v3d, mval, NULL, ray_normal, ray_start, true)) { return 0; } @@ -3064,6 +3065,7 @@ static short transform_snap_context_project_view3d_mixed_impl( has_hit = raycastObjects(sctx, depsgraph, + v3d, params, ray_start, ray_normal, @@ -3074,22 +3076,27 @@ static short transform_snap_context_project_view3d_mixed_impl( &ob_eval, obmat, NULL); + if (has_hit) { + if (r_face_nor) { + copy_v3_v3(r_face_nor, no); + } - if (has_hit && (snap_to_flag & SCE_SNAP_MODE_FACE)) { - retval = SCE_SNAP_MODE_FACE; + if ((snap_to_flag & SCE_SNAP_MODE_FACE)) { + retval = SCE_SNAP_MODE_FACE; - copy_v3_v3(r_loc, loc); - if (r_no) { - copy_v3_v3(r_no, no); - } - if (r_ob) { - *r_ob = ob_eval; - } - if (r_obmat) { - copy_m4_m4(r_obmat, obmat); - } - if (r_index) { - *r_index = index; + copy_v3_v3(r_loc, loc); + if (r_no) { + copy_v3_v3(r_no, no); + } + if (r_ob) { + *r_ob = ob_eval; + } + if (r_obmat) { + copy_m4_m4(r_obmat, obmat); + } + if (r_index) { + *r_index = index; + } } } } @@ -3099,24 +3106,28 @@ static short transform_snap_context_project_view3d_mixed_impl( short elem_test, elem = 0; float dist_px_tmp = *dist_px; - SnapData snapdata; - copy_m4_m4(snapdata.pmat, rv3d->persmat); - snapdata.win_size[0] = region->winx; - snapdata.win_size[1] = region->winy; - copy_v2_v2(snapdata.mval, mval); - snapdata.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; + copy_m4_m4(sctx->runtime.pmat, rv3d->persmat); + sctx->runtime.win_size[0] = region->winx; + sctx->runtime.win_size[1] = region->winy; + copy_v2_v2(sctx->runtime.mval, mval); + sctx->runtime.view_proj = rv3d->is_persp ? VIEW_PROJ_PERSP : VIEW_PROJ_ORTHO; /* First snap to edge instead of middle or perpendicular. */ - snapdata.snap_to_flag = snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE); + sctx->runtime.snap_to_flag = snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE); if (snap_to_flag & (SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR)) { - snapdata.snap_to_flag |= SCE_SNAP_MODE_EDGE; + sctx->runtime.snap_to_flag |= SCE_SNAP_MODE_EDGE; } - planes_from_projmat( - snapdata.pmat, NULL, NULL, NULL, NULL, snapdata.clip_plane[0], snapdata.clip_plane[1]); + planes_from_projmat(sctx->runtime.pmat, + NULL, + NULL, + NULL, + NULL, + sctx->runtime.clip_plane[0], + sctx->runtime.clip_plane[1]); - snapdata.clip_plane_len = 2; - snapdata.has_occlusion_plane = false; + sctx->runtime.clip_plane_len = 2; + sctx->runtime.has_occlusion_plane = false; /* By convention we only snap to the original elements of a curve. */ if (has_hit && ob_eval->type != OB_CURVE) { @@ -3124,7 +3135,7 @@ static short transform_snap_context_project_view3d_mixed_impl( float new_clipplane[4]; BLI_ASSERT_UNIT_V3(no); plane_from_point_normal_v3(new_clipplane, loc, no); - if (dot_v3v3(snapdata.clip_plane[0], new_clipplane) > 0.0f) { + if (dot_v3v3(sctx->runtime.clip_plane[0], new_clipplane) > 0.0f) { /* The plane is facing the wrong direction. */ negate_v4(new_clipplane); } @@ -3133,30 +3144,22 @@ static short transform_snap_context_project_view3d_mixed_impl( new_clipplane[3] += 0.01f; /* Try to snap only to the polygon. */ - elem_test = snap_mesh_polygon(sctx, - &snapdata, - ob_eval, - obmat, - params->use_backface_culling, - &dist_px_tmp, - loc, - no, - &index); + elem_test = snap_mesh_polygon( + sctx, ob_eval, obmat, params->use_backface_culling, &dist_px_tmp, loc, no, &index); if (elem_test) { elem = elem_test; } /* Add the new clip plane to the beginning of the list. */ - for (int i = snapdata.clip_plane_len; i != 0; i--) { - copy_v4_v4(snapdata.clip_plane[i], snapdata.clip_plane[i - 1]); + for (int i = sctx->runtime.clip_plane_len; i != 0; i--) { + copy_v4_v4(sctx->runtime.clip_plane[i], sctx->runtime.clip_plane[i - 1]); } - copy_v4_v4(snapdata.clip_plane[0], new_clipplane); - snapdata.clip_plane_len++; - snapdata.has_occlusion_plane = true; + copy_v4_v4(sctx->runtime.clip_plane[0], new_clipplane); + sctx->runtime.clip_plane_len++; + sctx->runtime.has_occlusion_plane = true; } - elem_test = snapObjectsRay( - sctx, depsgraph, &snapdata, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat); + elem_test = snapObjectsRay(sctx, params, &dist_px_tmp, loc, no, &index, &ob_eval, obmat); if (elem_test) { elem = elem_test; } @@ -3164,9 +3167,8 @@ static short transform_snap_context_project_view3d_mixed_impl( if ((elem == SCE_SNAP_MODE_EDGE) && (snap_to_flag & (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE_MIDPOINT | SCE_SNAP_MODE_EDGE_PERPENDICULAR))) { - snapdata.snap_to_flag = snap_to_flag; + sctx->runtime.snap_to_flag = snap_to_flag; elem = snap_mesh_edge_verts_mixed(sctx, - &snapdata, ob_eval, obmat, *dist_px, @@ -3194,6 +3196,10 @@ static short transform_snap_context_project_view3d_mixed_impl( if (r_index) { *r_index = index; } + if (r_face_nor && !has_hit) { + /* Fallback. */ + copy_v3_v3(r_face_nor, no); + } *dist_px = dist_px_tmp; } @@ -3204,6 +3210,8 @@ static short transform_snap_context_project_view3d_mixed_impl( short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const ushort snap_to, const struct SnapObjectParams *params, const float mval[2], @@ -3213,10 +3221,13 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, float r_no[3], int *r_index, Object **r_ob, - float r_obmat[4][4]) + float r_obmat[4][4], + float r_face_nor[3]) { return transform_snap_context_project_view3d_mixed_impl(sctx, depsgraph, + region, + v3d, snap_to, params, mval, @@ -3226,7 +3237,8 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, r_no, r_index, r_ob, - r_obmat); + r_obmat, + r_face_nor); } /** @@ -3242,18 +3254,22 @@ short ED_transform_snap_object_project_view3d_ex(SnapObjectContext *sctx, * \param r_no: hit normal (optional). * \return Snap success */ -bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, - Depsgraph *depsgraph, - const ushort snap_to, - const struct SnapObjectParams *params, - const float mval[2], - const float prev_co[3], - float *dist_px, - float r_loc[3], - float r_no[3]) +short ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, + Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, + const ushort snap_to, + const struct SnapObjectParams *params, + const float mval[2], + const float prev_co[3], + float *dist_px, + float r_loc[3], + float r_no[3]) { return ED_transform_snap_object_project_view3d_ex(sctx, depsgraph, + region, + v3d, snap_to, params, mval, @@ -3263,7 +3279,8 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, r_no, NULL, NULL, - NULL) != 0; + NULL, + NULL); } /** @@ -3271,6 +3288,8 @@ bool ED_transform_snap_object_project_view3d(SnapObjectContext *sctx, */ bool ED_transform_snap_object_project_all_view3d_ex(SnapObjectContext *sctx, Depsgraph *depsgraph, + const ARegion *region, + const View3D *v3d, const struct SnapObjectParams *params, const float mval[2], float ray_depth, @@ -3279,19 +3298,13 @@ bool ED_transform_snap_object_project_all_view3d_ex(SnapObjectContext *sctx, { float ray_start[3], ray_normal[3]; - if (!ED_view3d_win_to_ray_clipped_ex(depsgraph, - sctx->v3d_data.region, - sctx->v3d_data.v3d, - mval, - NULL, - ray_normal, - ray_start, - true)) { + if (!ED_view3d_win_to_ray_clipped_ex( + depsgraph, region, v3d, mval, NULL, ray_normal, ray_start, true)) { return false; } return ED_transform_snap_object_project_ray_all( - sctx, depsgraph, params, ray_start, ray_normal, ray_depth, sort, r_hit_list); + sctx, depsgraph, v3d, params, ray_start, ray_normal, ray_depth, sort, r_hit_list); } /** \} */ diff --git a/source/blender/editors/transform/transform_snap_sequencer.c b/source/blender/editors/transform/transform_snap_sequencer.c index e82a00bcc77..7bcf6812ce9 100644 --- a/source/blender/editors/transform/transform_snap_sequencer.c +++ b/source/blender/editors/transform/transform_snap_sequencer.c @@ -220,13 +220,13 @@ static void seq_snap_target_points_build(const TransInfo *t, int content_end = max_ii(seq->startdisp, seq->start + seq->len); /* Effects and single image strips produce incorrect content length. Skip these strips. */ if ((seq->type & SEQ_TYPE_EFFECT) != 0 || seq->len == 1) { - if (seq->anim_startofs == 0 && seq->startstill == 0) { - content_start = seq->startdisp; - } - if (seq->anim_endofs == 0 && seq->endstill == 0) { - content_end = seq->enddisp; - } + content_start = seq->startdisp; + content_end = seq->enddisp; } + + CLAMP(content_start, seq->startdisp, seq->enddisp); + CLAMP(content_end, seq->startdisp, seq->enddisp); + snap_data->target_snap_points[i] = content_start; snap_data->target_snap_points[i + 1] = content_end; i += 2; @@ -254,6 +254,10 @@ static int seq_snap_threshold_get_frame_distance(const TransInfo *t) TransSeqSnapData *transform_snap_sequencer_data_alloc(const TransInfo *t) { + if (t->data_type == TC_SEQ_IMAGE_DATA) { + return NULL; + } + TransSeqSnapData *snap_data = MEM_callocN(sizeof(TransSeqSnapData), __func__); ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(t->scene)); diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 22064e04e86..fa722d0646a 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -146,7 +146,7 @@ void ED_undo_push(bContext *C, const char *str) } } - UndoPushReturn push_retval; + eUndoPushReturn push_retval; /* Only apply limit if this is the last undo step. */ if (wm->undo_stack->step_active && (wm->undo_stack->step_active->next == NULL)) { diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index b396e348845..b339bfbdc47 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -103,8 +103,10 @@ set(SRC ../include/ED_view3d_offscreen.h ../include/UI_icons.h ../include/UI_interface.h + ../include/UI_interface.hh ../include/UI_interface_icons.h ../include/UI_resources.h + ../include/UI_tree_view.hh ../include/UI_view2d.h ) diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index fc351ab728c..721e32a3051 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -223,7 +223,7 @@ static void draw_main_line(const rctf *main_line_rect, static void draw_backdrop(const int fontid, const rctf *main_line_rect, - const float color_bg[4], + const uint8_t color_bg[4], const short region_y_size, const float base_tick_height) { @@ -241,7 +241,7 @@ static void draw_backdrop(const int fontid, .ymin = pad[1], .ymax = region_y_size - pad[1], }; - UI_draw_roundbox_aa(&backdrop_rect, true, 4.0f, color_bg); + UI_draw_roundbox_3ub_alpha(&backdrop_rect, true, 4.0f, color_bg, color_bg[3]); } /** @@ -260,19 +260,19 @@ static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void uint8_t color_line[4]; uint8_t color_handle[4]; uint8_t color_overshoot[4]; - float color_bg[4]; + uint8_t color_bg[4]; /* Get theme colors. */ - UI_GetThemeColor4ubv(TH_TEXT, color_text); - UI_GetThemeColor4ubv(TH_TEXT, color_line); - UI_GetThemeColor4ubv(TH_TEXT, color_overshoot); - UI_GetThemeColor4ubv(TH_ACTIVE, color_handle); - UI_GetThemeColor3fv(TH_BACK, color_bg); + UI_GetThemeColor4ubv(TH_HEADER_TEXT_HI, color_handle); + UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_text); + UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_line); + UI_GetThemeColor4ubv(TH_HEADER_TEXT, color_overshoot); + UI_GetThemeColor4ubv(TH_HEADER, color_bg); - color_bg[3] = 0.5f; - color_overshoot[0] = color_overshoot[0] * 0.7; - color_overshoot[1] = color_overshoot[1] * 0.7; - color_overshoot[2] = color_overshoot[2] * 0.7; + color_overshoot[0] = color_overshoot[0] * 0.8; + color_overshoot[1] = color_overshoot[1] * 0.8; + color_overshoot[2] = color_overshoot[2] * 0.8; + color_bg[3] = 160; /* Get the default font. */ const uiStyle *style = UI_style_get(); @@ -356,12 +356,11 @@ static void slider_draw(const struct bContext *UNUSED(C), ARegion *region, void static void slider_update_factor(tSlider *slider, const wmEvent *event) { - const float factor_delta = (event->x - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE; + const float factor_delta = (event->xy[0] - slider->last_cursor[0]) / SLIDE_PIXEL_DISTANCE; /* Reduced factor delta in precision mode (shift held). */ slider->raw_factor += slider->precision ? (factor_delta / 8) : factor_delta; slider->factor = slider->raw_factor; - slider->last_cursor[0] = event->x; - slider->last_cursor[1] = event->y; + copy_v2fl_v2i(slider->last_cursor, event->xy); if (!slider->overshoot) { slider->factor = clamp_f(slider->factor, 0, 1); @@ -403,8 +402,7 @@ tSlider *ED_slider_create(struct bContext *C) */ void ED_slider_init(struct tSlider *slider, const wmEvent *event) { - slider->last_cursor[0] = event->x; - slider->last_cursor[1] = event->y; + copy_v2fl_v2i(slider->last_cursor, event->xy); } /** @@ -533,8 +531,8 @@ void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_ wmWindow *win = CTX_wm_window(C); const float *mval_src = (float *)arg_info; const float mval_dst[2] = { - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, + win->eventstate->xy[0] - region->winrct.xmin, + win->eventstate->xy[1] - region->winrct.ymin, }; const uint shdr_pos = GPU_vertformat_attr_add( diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 73f328f85d7..348deec1166 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -33,6 +33,7 @@ #include "BLT_translation.h" +#include "BKE_collection.h" #include "BKE_global.h" #include "BKE_main.h" #include "BKE_material.h" @@ -121,48 +122,66 @@ void ED_editors_init(bContext *C) continue; } + /* Reset object to Object mode, so that code below can properly re-switch it to its + * previous mode if possible, re-creating its mode data, etc. */ ID *ob_data = ob->data; ob->mode = OB_MODE_OBJECT; DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); - if (obact && (ob->type == obact->type) && !ID_IS_LINKED(ob) && - !(ob_data && ID_IS_LINKED(ob_data))) { - if (mode == OB_MODE_EDIT) { - ED_object_editmode_enter_ex(bmain, scene, ob, 0); - } - else if (mode == OB_MODE_POSE) { - ED_object_posemode_enter_ex(bmain, ob); - } - else if (mode & OB_MODE_ALL_SCULPT) { - if (obact == ob) { - if (mode == OB_MODE_SCULPT) { - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, reports); - } - else if (mode == OB_MODE_VERTEX_PAINT) { - ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob); - } - else if (mode == OB_MODE_WEIGHT_PAINT) { - ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob); - } - else { - BLI_assert_unreachable(); - } + + /* Object mode is enforced if there is no active object, or if the active object's type is + * different. */ + if (obact == NULL || ob->type != obact->type) { + continue; + } + /* Object mode is enforced for linked data (or their obdata). */ + if (ID_IS_LINKED(ob) || (ob_data != NULL && ID_IS_LINKED(ob_data))) { + continue; + } + + /* Pose mode is very similar to Object one, we can apply it even on objects not in current + * scene. */ + if (mode == OB_MODE_POSE) { + ED_object_posemode_enter_ex(bmain, ob); + } + + /* Other edit/paint/etc. modes are only settable for objects in active scene currently. */ + if (!BKE_collection_has_object_recursive(scene->master_collection, ob)) { + continue; + } + + if (mode == OB_MODE_EDIT) { + ED_object_editmode_enter_ex(bmain, scene, ob, 0); + } + else if (mode & OB_MODE_ALL_SCULPT) { + if (obact == ob) { + if (mode == OB_MODE_SCULPT) { + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, true, reports); + } + else if (mode == OB_MODE_VERTEX_PAINT) { + ED_object_vpaintmode_enter_ex(bmain, depsgraph, scene, ob); + } + else if (mode == OB_MODE_WEIGHT_PAINT) { + ED_object_wpaintmode_enter_ex(bmain, depsgraph, scene, ob); } else { - /* Create data for non-active objects which need it for - * mode-switching but don't yet support multi-editing. */ - if (mode & OB_MODE_ALL_SCULPT) { - ob->mode = mode; - BKE_object_sculpt_data_create(ob); - } + BLI_assert_unreachable(); } } else { - /* TODO(campbell): avoid operator calls. */ - if (obact == ob) { - ED_object_mode_set(C, mode); + /* Create data for non-active objects which need it for + * mode-switching but don't yet support multi-editing. */ + if (mode & OB_MODE_ALL_SCULPT) { + ob->mode = mode; + BKE_object_sculpt_data_create(ob); } } } + else { + /* TODO(campbell): avoid operator calls. */ + if (obact == ob) { + ED_object_mode_set(C, mode); + } + } } /* image editor paint mode */ diff --git a/source/blender/editors/util/ed_util_imbuf.c b/source/blender/editors/util/ed_util_imbuf.c index fcbc0807893..38ae98c678f 100644 --- a/source/blender/editors/util/ed_util_imbuf.c +++ b/source/blender/editors/util/ed_util_imbuf.c @@ -312,7 +312,6 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e float fx, fy; if (ibuf == NULL) { - IMB_freeImBuf(ibuf); info->draw = 0; return; } @@ -387,13 +386,19 @@ static void sequencer_sample_apply(bContext *C, wmOperator *op, const wmEvent *e static void ed_imbuf_sample_apply(bContext *C, wmOperator *op, const wmEvent *event) { ScrArea *sa = CTX_wm_area(C); - - if (sa && sa->spacetype == SPACE_IMAGE) { - image_sample_apply(C, op, event); + if (sa == NULL) { + return; } - if (sa && sa->spacetype == SPACE_SEQ) { - sequencer_sample_apply(C, op, event); + switch (sa->spacetype) { + case SPACE_IMAGE: { + image_sample_apply(C, op, event); + break; + } + case SPACE_SEQ: { + sequencer_sample_apply(C, op, event); + break; + } } } @@ -446,7 +451,7 @@ void ED_imbuf_sample_draw(const bContext *C, ARegion *region, void *arg_info) rctf sample_rect_fl; BLI_rctf_init_pt_radius( &sample_rect_fl, - (float[2]){event->x - region->winrct.xmin, event->y - region->winrct.ymin}, + (float[2]){event->xy[0] - region->winrct.xmin, event->xy[1] - region->winrct.ymin}, (float)(info->sample_size / 2.0f) * sima->zoom); GPU_logic_op_xor_set(true); @@ -477,9 +482,29 @@ void ED_imbuf_sample_exit(bContext *C, wmOperator *op) int ED_imbuf_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *region = CTX_wm_region(C); - ImageSampleInfo *info; + ScrArea *sa = CTX_wm_area(C); + if (sa) { + switch (sa->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = sa->spacedata.first; + if (region->regiontype == RGN_TYPE_WINDOW) { + if (ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { + return OPERATOR_PASS_THROUGH; + } + } + if (!ED_space_image_has_buffer(sima)) { + return OPERATOR_CANCELLED; + } + break; + } + case SPACE_SEQ: { + /* Sequencer checks could be added. */ + break; + } + } + } - info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); + ImageSampleInfo *info = MEM_callocN(sizeof(ImageSampleInfo), "ImageSampleInfo"); info->art = region->type; info->draw_handle = ED_region_draw_cb_activate( @@ -487,22 +512,6 @@ int ED_imbuf_sample_invoke(bContext *C, wmOperator *op, const wmEvent *event) info->sample_size = RNA_int_get(op->ptr, "size"); op->customdata = info; - ScrArea *sa = CTX_wm_area(C); - - if (sa && sa->spacetype == SPACE_IMAGE) { - SpaceImage *sima = CTX_wm_space_image(C); - - if (region->regiontype == RGN_TYPE_WINDOW) { - if (event->mval[1] <= 16 && ED_space_image_show_cache(sima)) { - return OPERATOR_PASS_THROUGH; - } - } - - if (!ED_space_image_has_buffer(sima)) { - return OPERATOR_CANCELLED; - } - } - ed_imbuf_sample_apply(C, op, event); WM_event_add_modal_handler(C, op); @@ -535,37 +544,42 @@ void ED_imbuf_sample_cancel(bContext *C, wmOperator *op) bool ED_imbuf_sample_poll(bContext *C) { - ScrArea *sa = CTX_wm_area(C); - - if (sa && sa->spacetype == SPACE_IMAGE) { - SpaceImage *sima = CTX_wm_space_image(C); - if (sima == NULL) { - return false; - } - - Object *obedit = CTX_data_edit_object(C); - if (obedit) { - /* Disable when UV editing so it doesn't swallow all click events - * (use for setting cursor). */ - if (ED_space_image_show_uvedit(sima, obedit)) { - return false; - } - } - else if (sima->mode != SI_MODE_VIEW) { - return false; - } - - return true; + ScrArea *area = CTX_wm_area(C); + if (area == NULL) { + return false; } - if (sa && sa->spacetype == SPACE_SEQ) { - SpaceSeq *sseq = CTX_wm_space_seq(C); - - if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { - return false; + switch (area->spacetype) { + case SPACE_IMAGE: { + SpaceImage *sima = area->spacedata.first; + Object *obedit = CTX_data_edit_object(C); + if (obedit) { + /* Disable when UV editing so it doesn't swallow all click events + * (use for setting cursor). */ + if (ED_space_image_show_uvedit(sima, obedit)) { + return false; + } + } + else if (sima->mode != SI_MODE_VIEW) { + return false; + } + return true; } + case SPACE_SEQ: { + SpaceSeq *sseq = area->spacedata.first; - return sseq && SEQ_editing_get(CTX_data_scene(C)) != NULL; + if (sseq->mainb != SEQ_DRAW_IMG_IMBUF) { + return false; + } + if (SEQ_editing_get(CTX_data_scene(C)) == NULL) { + return false; + } + ARegion *region = CTX_wm_region(C); + if (!(region && (region->regiontype == RGN_TYPE_PREVIEW))) { + return false; + } + return true; + } } return false; diff --git a/source/blender/editors/util/numinput.c b/source/blender/editors/util/numinput.c index d0eed6a6eb1..d0f27770d9b 100644 --- a/source/blender/editors/util/numinput.c +++ b/source/blender/editors/util/numinput.c @@ -472,8 +472,7 @@ bool handleNumInput(bContext *C, NumInput *n, const wmEvent *event) return true; case EVT_PADPERIOD: case EVT_PERIODKEY: - /* Force numdot, some OSs/countries generate a comma char in this case, - * sic... (T37992) */ + /* Force number-pad "." since some OS's/countries generate a comma char, see: T37992 */ ascii[0] = '.'; utf8_buf = ascii; break; diff --git a/source/blender/editors/util/select_utils.c b/source/blender/editors/util/select_utils.c index 5681edd2f5c..99412079adf 100644 --- a/source/blender/editors/util/select_utils.c +++ b/source/blender/editors/util/select_utils.c @@ -18,14 +18,14 @@ * \ingroup edutil */ +#include + #include "BLI_kdtree.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "ED_select_utils.h" -#include "float.h" - /** 1: select, 0: deselect, -1: pass. */ int ED_select_op_action(const eSelectOp sel_op, const bool is_select, const bool is_inside) { diff --git a/source/blender/editors/uvedit/uvedit_islands.c b/source/blender/editors/uvedit/uvedit_islands.c index 56bcbc63de1..6159758dbcd 100644 --- a/source/blender/editors/uvedit/uvedit_islands.c +++ b/source/blender/editors/uvedit/uvedit_islands.c @@ -29,6 +29,7 @@ #include "DNA_meshdata_types.h" #include "DNA_scene_types.h" +#include "DNA_space_types.h" #include "BLI_boxpack_2d.h" #include "BLI_convexhull_2d.h" @@ -38,6 +39,7 @@ #include "BKE_customdata.h" #include "BKE_editmesh.h" +#include "BKE_image.h" #include "DEG_depsgraph.h" @@ -231,6 +233,101 @@ static void bm_face_array_uv_scale_y(BMFace **faces, /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UDIM packing helper functions + * \{ */ + +/** + * Returns true if UV coordinates lie on a valid tile in UDIM grid or tiled image. + */ +bool uv_coords_isect_udim(const Image *image, const int udim_grid[2], const float coords[2]) +{ + const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])}; + const bool is_tiled_image = image && (image->source == IMA_SRC_TILED); + + if (coords[0] < udim_grid[0] && coords[0] > 0 && coords[1] < udim_grid[1] && coords[1] > 0) { + return true; + } + /* Check if selection lies on a valid UDIM image tile. */ + if (is_tiled_image) { + LISTBASE_FOREACH (const ImageTile *, tile, &image->tiles) { + const int tile_index = tile->tile_number - 1001; + const int target_x = (tile_index % 10); + const int target_y = (tile_index / 10); + if (coords_floor[0] == target_x && coords_floor[1] == target_y) { + return true; + } + } + } + /* Probably not required since UDIM grid checks for 1001. */ + else if (image && !is_tiled_image) { + if (is_zero_v2(coords_floor)) { + return true; + } + } + + return false; +} + +/** + * Calculates distance to nearest UDIM image tile in UV space and its UDIM tile number. + */ +static float uv_nearest_image_tile_distance(const Image *image, + float coords[2], + float nearest_tile_co[2]) +{ + int nearest_image_tile_index = BKE_image_find_nearest_tile(image, coords); + if (nearest_image_tile_index == -1) { + nearest_image_tile_index = 1001; + } + + nearest_tile_co[0] = (nearest_image_tile_index - 1001) % 10; + nearest_tile_co[1] = (nearest_image_tile_index - 1001) / 10; + /* Add 0.5 to get tile center coordinates. */ + float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; + add_v2_fl(nearest_tile_center_co, 0.5f); + + return len_squared_v2v2(coords, nearest_tile_center_co); +} + +/** + * Calculates distance to nearest UDIM grid tile in UV space and its UDIM tile number. + */ +static float uv_nearest_grid_tile_distance(const int udim_grid[2], + float coords[2], + float nearest_tile_co[2]) +{ + const float coords_floor[2] = {floorf(coords[0]), floorf(coords[1])}; + + if (coords[0] > udim_grid[0]) { + nearest_tile_co[0] = udim_grid[0] - 1; + } + else if (coords[0] < 0) { + nearest_tile_co[0] = 0; + } + else { + nearest_tile_co[0] = coords_floor[0]; + } + + if (coords[1] > udim_grid[1]) { + nearest_tile_co[1] = udim_grid[1] - 1; + } + else if (coords[1] < 0) { + nearest_tile_co[1] = 0; + } + else { + nearest_tile_co[1] = coords_floor[1]; + } + + /* Add 0.5 to get tile center coordinates. */ + float nearest_tile_center_co[2] = {nearest_tile_co[0], nearest_tile_co[1]}; + add_v2_fl(nearest_tile_center_co, 0.5f); + + return len_squared_v2v2(coords, nearest_tile_center_co); +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Calculate UV Islands * @@ -359,6 +456,7 @@ static int bm_mesh_calc_uv_islands(const Scene *scene, void ED_uvedit_pack_islands_multi(const Scene *scene, Object **objects, const uint objects_len, + const struct UVMapUDIM_Params *udim_params, const struct UVPackIsland_Params *params) { /* Align to the Y axis, could make this configurable. */ @@ -407,8 +505,27 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, BoxPack *boxarray = MEM_mallocN(sizeof(*boxarray) * island_list_len, __func__); int index; + /* Coordinates of bounding box containing all selected UVs. */ + float selection_min_co[2], selection_max_co[2]; + INIT_MINMAX2(selection_min_co, selection_max_co); + LISTBASE_FOREACH_INDEX (struct FaceIsland *, island, &island_list, index) { + /* Skip calculation if using specified UDIM option. */ + if (udim_params && (udim_params->use_target_udim == false)) { + float bounds_min[2], bounds_max[2]; + INIT_MINMAX2(bounds_min, bounds_max); + for (int i = 0; i < island->faces_len; i++) { + BMFace *f = island->faces[i]; + BM_face_uv_minmax(f, bounds_min, bounds_max, island->cd_loop_uv_offset); + } + + selection_min_co[0] = MIN2(bounds_min[0], selection_min_co[0]); + selection_min_co[1] = MIN2(bounds_min[1], selection_min_co[1]); + selection_max_co[0] = MAX2(bounds_max[0], selection_max_co[0]); + selection_max_co[1] = MAX2(bounds_max[1], selection_max_co[1]); + } + if (params->rotate) { if (island->aspect_y != 1.0f) { bm_face_array_uv_scale_y( @@ -441,6 +558,13 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, } } + /* Center of bounding box containing all selected UVs. */ + float selection_center[2]; + if (udim_params && (udim_params->use_target_udim == false)) { + selection_center[0] = (selection_min_co[0] + selection_max_co[0]) / 2.0f; + selection_center[1] = (selection_min_co[1] + selection_max_co[1]) / 2.0f; + } + if (margin > 0.0f) { /* Logic matches behavior from #param_pack, * use area so multiply the margin by the area to give @@ -464,6 +588,53 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, const float scale[2] = {1.0f / boxarray_size[0], 1.0f / boxarray_size[1]}; + /* Tile offset. */ + float base_offset[2] = {0.0f, 0.0f}; + + /* CASE: ignore UDIM. */ + if (udim_params == NULL) { + /* pass */ + } + /* CASE: Active/specified(smart uv project) UDIM. */ + else if (udim_params->use_target_udim) { + + /* Calculate offset based on specified_tile_index. */ + base_offset[0] = (udim_params->target_udim - 1001) % 10; + base_offset[1] = (udim_params->target_udim - 1001) / 10; + } + + /* CASE: Closest UDIM. */ + else { + const Image *image = udim_params->image; + const int *udim_grid = udim_params->grid_shape; + /* Check if selection lies on a valid UDIM grid tile. */ + bool is_valid_udim = uv_coords_isect_udim(image, udim_grid, selection_center); + if (is_valid_udim) { + base_offset[0] = floorf(selection_center[0]); + base_offset[1] = floorf(selection_center[1]); + } + /* If selection doesn't lie on any UDIM then find the closest UDIM grid or image tile. */ + else { + float nearest_image_tile_co[2] = {FLT_MAX, FLT_MAX}; + float nearest_image_tile_dist = FLT_MAX, nearest_grid_tile_dist = FLT_MAX; + if (image) { + nearest_image_tile_dist = uv_nearest_image_tile_distance( + image, selection_center, nearest_image_tile_co); + } + + float nearest_grid_tile_co[2] = {0.0f, 0.0f}; + nearest_grid_tile_dist = uv_nearest_grid_tile_distance( + udim_grid, selection_center, nearest_grid_tile_co); + + base_offset[0] = (nearest_image_tile_dist < nearest_grid_tile_dist) ? + nearest_image_tile_co[0] : + nearest_grid_tile_co[0]; + base_offset[1] = (nearest_image_tile_dist < nearest_grid_tile_dist) ? + nearest_image_tile_co[1] : + nearest_grid_tile_co[1]; + } + } + for (int i = 0; i < island_list_len; i++) { struct FaceIsland *island = island_array[boxarray[i].index]; const float pivot[2] = { @@ -471,8 +642,8 @@ void ED_uvedit_pack_islands_multi(const Scene *scene, island->bounds_rect.ymin, }; const float offset[2] = { - (boxarray[i].x * scale[0]) - island->bounds_rect.xmin, - (boxarray[i].y * scale[1]) - island->bounds_rect.ymin, + ((boxarray[i].x * scale[0]) - island->bounds_rect.xmin) + base_offset[0], + ((boxarray[i].y * scale[1]) - island->bounds_rect.ymin) + base_offset[1], }; for (int j = 0; j < island->faces_len; j++) { BMFace *efa = island->faces[j]; diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 0757e177235..acdffd5ff98 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -1765,11 +1765,9 @@ static int uv_set_2d_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *e float location[2]; if (region->regiontype == RGN_TYPE_WINDOW) { - if (event->mval[1] <= 16) { - SpaceImage *sima = CTX_wm_space_image(C); - if (sima && ED_space_image_show_cache(sima)) { - return OPERATOR_PASS_THROUGH; - } + SpaceImage *sima = CTX_wm_space_image(C); + if (sima && ED_space_image_show_cache_and_mval_over(sima, region, event->mval)) { + return OPERATOR_PASS_THROUGH; } } diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index c0ccf1b7095..86390882bed 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -2122,7 +2122,9 @@ static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event) UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); RNA_float_set_array(op->ptr, "location", co); - return uv_select_exec(C, op); + const int retval = uv_select_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void UV_OT_select(wmOperatorType *ot) @@ -2281,7 +2283,9 @@ static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *eve UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); RNA_float_set_array(op->ptr, "location", co); - return uv_select_loop_exec(C, op); + const int retval = uv_select_loop_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void UV_OT_select_loop(wmOperatorType *ot) @@ -2341,7 +2345,9 @@ static int uv_select_edge_ring_invoke(bContext *C, wmOperator *op, const wmEvent UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); RNA_float_set_array(op->ptr, "location", co); - return uv_select_edge_ring_exec(C, op); + const int retval = uv_select_edge_ring_exec(C, op); + + return WM_operator_flag_only_pass_through_on_press(retval, event); } void UV_OT_select_edge_ring(wmOperatorType *ot) diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 3d5dabda23d..89490e59bd8 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -37,6 +37,7 @@ #include "BLI_alloca.h" #include "BLI_array.h" #include "BLI_linklist.h" +#include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_memarena.h" #include "BLI_string.h" @@ -64,6 +65,7 @@ #include "PIL_time.h" #include "UI_interface.h" +#include "UI_view2d.h" #include "ED_image.h" #include "ED_mesh.h" @@ -142,6 +144,60 @@ static bool ED_uvedit_ensure_uvs(Object *obedit) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name UDIM Access + * \{ */ + +bool ED_uvedit_udim_params_from_image_space(const SpaceImage *sima, + bool use_active, + struct UVMapUDIM_Params *udim_params) +{ + memset(udim_params, 0, sizeof(*udim_params)); + + udim_params->grid_shape[0] = 1; + udim_params->grid_shape[1] = 1; + udim_params->target_udim = 0; + udim_params->use_target_udim = false; + + if (sima == NULL) { + return false; + } + + udim_params->image = sima->image; + udim_params->grid_shape[0] = sima->tile_grid_shape[0]; + udim_params->grid_shape[1] = sima->tile_grid_shape[1]; + + if (use_active) { + int active_udim = 1001; + /* NOTE: Presently, when UDIM grid and tiled image are present together, only active tile for + * the tiled image is considered. */ + const Image *image = sima->image; + if (image && image->source == IMA_SRC_TILED) { + ImageTile *active_tile = BLI_findlink(&image->tiles, image->active_tile_index); + if (active_tile) { + active_udim = active_tile->tile_number; + } + } + else { + /* TODO: Support storing an active UDIM when there are no tiles present. + * Until then, use 2D cursor to find the active tile index for the UDIM grid. */ + if (uv_coords_isect_udim(sima->image, sima->tile_grid_shape, sima->cursor)) { + int tile_number = 1001; + tile_number += floorf(sima->cursor[1]) * 10; + tile_number += floorf(sima->cursor[0]); + active_udim = tile_number; + } + } + + udim_params->target_udim = active_udim; + udim_params->use_target_udim = true; + } + + return true; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Parametrizer Conversion * \{ */ @@ -1005,10 +1061,17 @@ static void uvedit_pack_islands_multi(const Scene *scene, } } +/* Packing targets. */ +enum { + PACK_UDIM_SRC_CLOSEST = 0, + PACK_UDIM_SRC_ACTIVE = 1, +}; + static int pack_islands_exec(bContext *C, wmOperator *op) { ViewLayer *view_layer = CTX_data_view_layer(C); const Scene *scene = CTX_data_scene(C); + const SpaceImage *sima = CTX_wm_space_image(C); const UnwrapOptions options = { .topology_from_uvs = true, @@ -1018,17 +1081,19 @@ static int pack_islands_exec(bContext *C, wmOperator *op) .correct_aspect = true, }; - bool rotate = RNA_boolean_get(op->ptr, "rotate"); - uint objects_len = 0; Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs( view_layer, CTX_wm_view3d(C), &objects_len); + /* Early exit in case no UVs are selected. */ if (!uvedit_have_selection_multi(scene, objects, objects_len, &options)) { MEM_freeN(objects); return OPERATOR_CANCELLED; } + /* RNA props */ + const bool rotate = RNA_boolean_get(op->ptr, "rotate"); + const int udim_source = RNA_enum_get(op->ptr, "udim_source"); if (RNA_struct_property_is_set(op->ptr, "margin")) { scene->toolsettings->uvcalc_margin = RNA_float_get(op->ptr, "margin"); } @@ -1036,9 +1101,15 @@ static int pack_islands_exec(bContext *C, wmOperator *op) RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin); } + struct UVMapUDIM_Params udim_params; + const bool use_active = (udim_source == PACK_UDIM_SRC_ACTIVE); + const bool use_udim_params = ED_uvedit_udim_params_from_image_space( + sima, use_active, &udim_params); + ED_uvedit_pack_islands_multi(scene, objects, objects_len, + use_udim_params ? &udim_params : NULL, &(struct UVPackIsland_Params){ .rotate = rotate, .rotate_align_axis = -1, @@ -1048,16 +1119,25 @@ static int pack_islands_exec(bContext *C, wmOperator *op) }); MEM_freeN(objects); - return OPERATOR_FINISHED; } void UV_OT_pack_islands(wmOperatorType *ot) { + static const EnumPropertyItem pack_target[] = { + {PACK_UDIM_SRC_CLOSEST, "CLOSEST_UDIM", 0, "Closest UDIM", "Pack islands to closest UDIM"}, + {PACK_UDIM_SRC_ACTIVE, + "ACTIVE_UDIM", + 0, + "Active UDIM", + "Pack islands to active UDIM image tile or UDIM grid tile where 2D cursor is located"}, + {0, NULL, 0, NULL, NULL}, + }; /* identifiers */ ot->name = "Pack Islands"; ot->idname = "UV_OT_pack_islands"; - ot->description = "Transform all islands so that they fill up the UV space as much as possible"; + ot->description = + "Transform all islands so that they fill up the UV/UDIM space as much as possible"; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1066,6 +1146,7 @@ void UV_OT_pack_islands(wmOperatorType *ot) ot->poll = ED_operator_uvedit; /* properties */ + RNA_def_enum(ot->srna, "udim_source", pack_target, PACK_UDIM_SRC_CLOSEST, "Pack to", ""); RNA_def_boolean(ot->srna, "rotate", true, "Rotate", "Rotate islands for best fit"); RNA_def_float_factor( ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); @@ -2206,6 +2287,7 @@ static int smart_project_exec(bContext *C, wmOperator *op) ED_uvedit_pack_islands_multi(scene, objects_changed, object_changed_len, + NULL, &(struct UVPackIsland_Params){ .rotate = true, /* We could make this optional. */ diff --git a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp index 937a10f26b1..0a82c237256 100644 --- a/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp +++ b/source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp @@ -94,17 +94,15 @@ BlenderStrokeRenderer::BlenderStrokeRenderer(Render *re, int render_count) freestyle_scene = BKE_scene_add(freestyle_bmain, name); freestyle_scene->r.cfra = old_scene->r.cfra; freestyle_scene->r.mode = old_scene->r.mode & ~(R_EDGE_FRS | R_BORDER); - freestyle_scene->r.xsch = re->rectx; // old_scene->r.xsch - freestyle_scene->r.ysch = re->recty; // old_scene->r.ysch - freestyle_scene->r.xasp = 1.0f; // old_scene->r.xasp; - freestyle_scene->r.yasp = 1.0f; // old_scene->r.yasp; - freestyle_scene->r.tilex = old_scene->r.tilex; - freestyle_scene->r.tiley = old_scene->r.tiley; + freestyle_scene->r.xsch = re->rectx; // old_scene->r.xsch + freestyle_scene->r.ysch = re->recty; // old_scene->r.ysch + freestyle_scene->r.xasp = 1.0f; // old_scene->r.xasp; + freestyle_scene->r.yasp = 1.0f; // old_scene->r.yasp; freestyle_scene->r.size = 100; // old_scene->r.size freestyle_scene->r.color_mgt_flag = 0; // old_scene->r.color_mgt_flag; freestyle_scene->r.scemode = (old_scene->r.scemode & ~(R_SINGLE_LAYER | R_NO_FRAME_UPDATE | R_MULTIVIEW)) & - (re->r.scemode | ~R_FULL_SAMPLE); + (re->r.scemode); freestyle_scene->r.flag = old_scene->r.flag; freestyle_scene->r.threads = old_scene->r.threads; freestyle_scene->r.border.xmin = old_scene->r.border.xmin; diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index c74fd60fe35..b31f4fd2303 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -86,8 +86,8 @@ static void load_post_callback(struct Main * /*main*/, } static bCallbackFuncStore load_post_callback_funcstore = { - nullptr, - nullptr, /* next, prev */ + nullptr, /* next */ + nullptr, /* prev */ load_post_callback, /* func */ nullptr, /* arg */ 0 /* alloc */ @@ -494,7 +494,7 @@ void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_r if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) { // Create a blank render pass output. RE_create_render_pass( - re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname); + re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true); } return; } @@ -530,7 +530,7 @@ void FRS_composite_result(Render *re, ViewLayer *view_layer, Render *freestyle_r if (view_layer->freestyle_config.flags & FREESTYLE_AS_RENDER_PASS) { RE_create_render_pass( - re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname); + re->result, RE_PASSNAME_FREESTYLE, 4, "RGBA", view_layer->name, re->viewname, true); dest = RE_RenderLayerGetPass(rl, RE_PASSNAME_FREESTYLE, re->viewname); } else { diff --git a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp index ab39b9ad883..bcb7af0e5a7 100644 --- a/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp +++ b/source/blender/freestyle/intern/python/StrokeShader/BPy_SmoothingShader.cpp @@ -63,7 +63,7 @@ static char SmoothingShader___doc__[] = "\n" ".. method:: shade(stroke)\n" "\n" - " Smoothes the stroke by moving the vertices to make the stroke\n" + " Smooths the stroke by moving the vertices to make the stroke\n" " smoother. Uses curvature flow to converge towards a curve of\n" " constant curvature. The diffusion method we use is anisotropic to\n" " prevent the diffusion across corners.\n" diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 856668f01d7..74625fe2826 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -43,6 +43,7 @@ set(SRC FN_cpp_type_make.hh FN_field.hh FN_field_cpp_type.hh + FN_generic_array.hh FN_generic_pointer.hh FN_generic_span.hh FN_generic_value_map.hh @@ -53,9 +54,9 @@ set(SRC FN_multi_function_builder.hh FN_multi_function_context.hh FN_multi_function_data_type.hh + FN_multi_function_parallel.hh FN_multi_function_param_type.hh FN_multi_function_params.hh - FN_multi_function_parallel.hh FN_multi_function_procedure.hh FN_multi_function_procedure_builder.hh FN_multi_function_procedure_executor.hh @@ -88,6 +89,7 @@ if(WITH_GTESTS) set(TEST_SRC tests/FN_cpp_type_test.cc tests/FN_field_test.cc + tests/FN_generic_array_test.cc tests/FN_generic_span_test.cc tests/FN_generic_vector_array_test.cc tests/FN_multi_function_procedure_test.cc diff --git a/source/blender/functions/FN_cpp_type.hh b/source/blender/functions/FN_cpp_type.hh index 7277bf99c12..643b2fc1f28 100644 --- a/source/blender/functions/FN_cpp_type.hh +++ b/source/blender/functions/FN_cpp_type.hh @@ -96,6 +96,7 @@ class CPPType : NonCopyable, NonMovable { int64_t size_ = 0; int64_t alignment_ = 0; uintptr_t alignment_mask_ = 0; + bool is_trivial_ = false; bool is_trivially_destructible_ = false; bool has_special_member_functions_ = false; @@ -340,7 +341,6 @@ class CPPType : NonCopyable, NonMovable { */ void copy_assign(const void *src, void *dst) const { - BLI_assert(src != dst); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -371,7 +371,7 @@ class CPPType : NonCopyable, NonMovable { */ void copy_construct(const void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -402,7 +402,6 @@ class CPPType : NonCopyable, NonMovable { */ void move_assign(void *src, void *dst) const { - BLI_assert(src != dst); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -433,7 +432,7 @@ class CPPType : NonCopyable, NonMovable { */ void move_construct(void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -464,7 +463,7 @@ class CPPType : NonCopyable, NonMovable { */ void relocate_assign(void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); @@ -495,7 +494,7 @@ class CPPType : NonCopyable, NonMovable { */ void relocate_construct(void *src, void *dst) const { - BLI_assert(src != dst); + BLI_assert(src != dst || is_trivial_); BLI_assert(this->pointer_can_point_to_instance(src)); BLI_assert(this->pointer_can_point_to_instance(dst)); diff --git a/source/blender/functions/FN_cpp_type_make.hh b/source/blender/functions/FN_cpp_type_make.hh index 088f6b469f4..74dbcabf81a 100644 --- a/source/blender/functions/FN_cpp_type_make.hh +++ b/source/blender/functions/FN_cpp_type_make.hh @@ -195,6 +195,7 @@ CPPType::CPPType(CPPTypeParam /* unused */, StringRef debug_name) debug_name_ = debug_name; size_ = (int64_t)sizeof(T); alignment_ = (int64_t)alignof(T); + is_trivial_ = std::is_trivial_v; is_trivially_destructible_ = std::is_trivially_destructible_v; if constexpr (std::is_default_constructible_v) { default_construct_ = default_construct_cb; diff --git a/source/blender/functions/FN_field.hh b/source/blender/functions/FN_field.hh index d4375b625ce..5e0302130af 100644 --- a/source/blender/functions/FN_field.hh +++ b/source/blender/functions/FN_field.hh @@ -77,29 +77,15 @@ class FieldNode { bool depends_on_input_; public: - FieldNode(bool is_input, bool depends_on_input) - : is_input_(is_input), depends_on_input_(depends_on_input) - { - } + FieldNode(bool is_input, bool depends_on_input); virtual ~FieldNode() = default; virtual const CPPType &output_cpp_type(int output_index) const = 0; - bool is_input() const - { - return is_input_; - } - - bool is_operation() const - { - return !is_input_; - } - - bool depends_on_input() const - { - return depends_on_input_; - } + bool is_input() const; + bool is_operation() const; + bool depends_on_input() const; /** * Invoke callback for every field input. It might be called multiple times for the same input. @@ -107,25 +93,8 @@ class FieldNode { */ virtual void foreach_field_input(FunctionRef foreach_fn) const = 0; - virtual uint64_t hash() const - { - return get_default_hash(this); - } - - friend bool operator==(const FieldNode &a, const FieldNode &b) - { - return a.is_equal_to(b); - } - - friend bool operator!=(const FieldNode &a, const FieldNode &b) - { - return !(a == b); - } - - virtual bool is_equal_to(const FieldNode &other) const - { - return this == &other; - } + virtual uint64_t hash() const; + virtual bool is_equal_to(const FieldNode &other) const; }; /** @@ -235,42 +204,20 @@ class FieldOperation : public FieldNode { * The multi-function used by this node. It is optionally owned. * Multi-functions with mutable or vector parameters are not supported currently. */ - std::unique_ptr owned_function_; + std::shared_ptr owned_function_; const MultiFunction *function_; /** Inputs to the operation. */ blender::Vector inputs_; public: - FieldOperation(std::unique_ptr function, Vector inputs = {}); + FieldOperation(std::shared_ptr function, Vector inputs = {}); FieldOperation(const MultiFunction &function, Vector inputs = {}); - Span inputs() const - { - return inputs_; - } - - const MultiFunction &multi_function() const - { - return *function_; - } - - const CPPType &output_cpp_type(int output_index) const override - { - int output_counter = 0; - for (const int param_index : function_->param_indices()) { - MFParamType param_type = function_->param_type(param_index); - if (param_type.is_output()) { - if (output_counter == output_index) { - return param_type.data_type().single_type(); - } - output_counter++; - } - } - BLI_assert_unreachable(); - return CPPType::get(); - } + Span inputs() const; + const MultiFunction &multi_function() const; + const CPPType &output_cpp_type(int output_index) const override; void foreach_field_input(FunctionRef foreach_fn) const override; }; @@ -280,9 +227,19 @@ class FieldContext; * A #FieldNode that represents an input to the entire field-tree. */ class FieldInput : public FieldNode { + public: + /* The order is also used for sorting in socket inspection. */ + enum class Category { + NamedAttribute = 0, + Generated = 1, + AnonymousAttribute = 2, + Unknown, + }; + protected: const CPPType *type_; std::string debug_name_; + Category category_ = Category::Unknown; public: FieldInput(const CPPType &type, std::string debug_name = ""); @@ -295,28 +252,12 @@ class FieldInput : public FieldNode { IndexMask mask, ResourceScope &scope) const = 0; - virtual std::string socket_inspection_name() const - { - return debug_name_; - } - - blender::StringRef debug_name() const - { - return debug_name_; - } - - const CPPType &cpp_type() const - { - return *type_; - } - - const CPPType &output_cpp_type(int output_index) const override - { - BLI_assert(output_index == 0); - UNUSED_VARS_NDEBUG(output_index); - return *type_; - } + virtual std::string socket_inspection_name() const; + blender::StringRef debug_name() const; + const CPPType &cpp_type() const; + Category category() const; + const CPPType &output_cpp_type(int output_index) const override; void foreach_field_input(FunctionRef foreach_fn) const override; }; @@ -461,9 +402,9 @@ Vector evaluate_fields(ResourceScope &scope, const FieldContext &context, Span dst_varrays = {}); -/* -------------------------------------------------------------------- - * Utility functions for simple field creation and evaluation. - */ +/* -------------------------------------------------------------------- */ +/** \name Utility functions for simple field creation and evaluation + * \{ */ void evaluate_constant_field(const GField &field, void *r_value); @@ -484,4 +425,131 @@ template Field make_constant_field(T value) GField make_field_constant_if_possible(GField field); +class IndexFieldInput final : public FieldInput { + public: + IndexFieldInput(); + + static GVArray *get_index_varray(IndexMask mask, ResourceScope &scope); + + const GVArray *get_varray_for_context(const FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final; + + uint64_t hash() const override; + bool is_equal_to(const fn::FieldNode &other) const override; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldNode Inline Methods + * \{ */ + +inline FieldNode::FieldNode(bool is_input, bool depends_on_input) + : is_input_(is_input), depends_on_input_(depends_on_input) +{ +} + +inline bool FieldNode::is_input() const +{ + return is_input_; +} + +inline bool FieldNode::is_operation() const +{ + return !is_input_; +} + +inline bool FieldNode::depends_on_input() const +{ + return depends_on_input_; +} + +inline uint64_t FieldNode::hash() const +{ + return get_default_hash(this); +} + +inline bool FieldNode::is_equal_to(const FieldNode &other) const +{ + return this == &other; +} + +inline bool operator==(const FieldNode &a, const FieldNode &b) +{ + return a.is_equal_to(b); +} + +inline bool operator!=(const FieldNode &a, const FieldNode &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldOperation Inline Methods + * \{ */ + +inline Span FieldOperation::inputs() const +{ + return inputs_; +} + +inline const MultiFunction &FieldOperation::multi_function() const +{ + return *function_; +} + +inline const CPPType &FieldOperation::output_cpp_type(int output_index) const +{ + int output_counter = 0; + for (const int param_index : function_->param_indices()) { + MFParamType param_type = function_->param_type(param_index); + if (param_type.is_output()) { + if (output_counter == output_index) { + return param_type.data_type().single_type(); + } + output_counter++; + } + } + BLI_assert_unreachable(); + return CPPType::get(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldInput Inline Methods + * \{ */ + +inline std::string FieldInput::socket_inspection_name() const +{ + return debug_name_; +} + +inline StringRef FieldInput::debug_name() const +{ + return debug_name_; +} + +inline const CPPType &FieldInput::cpp_type() const +{ + return *type_; +} + +inline FieldInput::Category FieldInput::category() const +{ + return category_; +} + +inline const CPPType &FieldInput::output_cpp_type(int output_index) const +{ + BLI_assert(output_index == 0); + UNUSED_VARS_NDEBUG(output_index); + return *type_; +} + +/** \} */ + } // namespace blender::fn diff --git a/source/blender/functions/FN_generic_array.hh b/source/blender/functions/FN_generic_array.hh new file mode 100644 index 00000000000..401e496a66c --- /dev/null +++ b/source/blender/functions/FN_generic_array.hh @@ -0,0 +1,270 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +/** \file + * \ingroup fn + * + * This is a generic counterpart to #blender::Array, used when the type is not known at runtime. + * + * `GArray` should generally only be used for passing data around in dynamic contexts. + * It does not support a few things that #blender::Array supports: + * - Small object optimization / inline buffer. + * - Exception safety and various more specific constructors. + */ + +#include "BLI_allocator.hh" + +#include "FN_cpp_type.hh" +#include "FN_generic_span.hh" + +namespace blender::fn { + +template< + /** + * The allocator used by this array. Should rarely be changed, except when you don't want that + * MEM_* functions are used internally. + */ + typename Allocator = GuardedAllocator> +class GArray { + protected: + /** The type of the data in the array, will be null after the array is default constructed, + * but a value should be assigned before any other interaction with the array. */ + const CPPType *type_ = nullptr; + void *data_ = nullptr; + int64_t size_ = 0; + + Allocator allocator_; + + public: + /** + * The default constructor creates an empty array, the only situation in which the type is + * allowed to be null. This default constructor exists so `GArray` can be used in containers, + * but the type should be supplied before doing anything else to the array. + */ + GArray(Allocator allocator = {}) noexcept : allocator_(allocator) + { + } + + GArray(NoExceptConstructor, Allocator allocator = {}) noexcept : GArray(allocator) + { + } + + /** + * Create and allocate a new array, with elements default constructed + * (which does not do anything for trivial types). + */ + GArray(const CPPType &type, int64_t size, Allocator allocator = {}) : GArray(type, allocator) + { + BLI_assert(size >= 0); + size_ = size; + data_ = this->allocate(size_); + type_->default_construct_n(data_, size_); + } + + /** + * Create an empty array with just a type. + */ + GArray(const CPPType &type, Allocator allocator = {}) : GArray(allocator) + { + type_ = &type; + } + + /** + * Take ownership of a buffer with a provided size. The buffer should be + * allocated with the same allocator provided to the constructor. + */ + GArray(const CPPType &type, void *buffer, int64_t size, Allocator allocator = {}) + : GArray(type, allocator) + { + BLI_assert(size >= 0); + BLI_assert(buffer != nullptr || size == 0); + BLI_assert(type_->pointer_has_valid_alignment(buffer)); + + data_ = buffer; + size_ = size; + } + + /** + * Create an array by copying values from a generic span. + */ + GArray(const GSpan span, Allocator allocator = {}) : GArray(span.type(), span.size(), allocator) + { + if (span.data() != nullptr) { + BLI_assert(span.size() != 0); + /* Use copy assign rather than construct since the memory is already initialized. */ + type_->copy_assign_n(span.data(), data_, size_); + } + } + + /** + * Create an array by copying values from another generic array. + */ + GArray(const GArray &other) : GArray(other.as_span(), other.allocator()) + { + } + + /** + * Create an array by taking ownership of another array's data, clearing the data in the other. + */ + GArray(GArray &&other) : GArray(other.type(), other.data(), other.size(), other.allocator()) + { + other.data_ = nullptr; + other.size_ = 0; + } + + ~GArray() + { + if (data_ != nullptr) { + type_->destruct_n(data_, size_); + this->deallocate(data_); + } + } + + GArray &operator=(const GArray &other) + { + return copy_assign_container(*this, other); + } + + GArray &operator=(GArray &&other) + { + return move_assign_container(*this, std::move(other)); + } + + const CPPType &type() const + { + BLI_assert(type_ != nullptr); + return *type_; + } + + bool is_empty() const + { + return size_ == 0; + } + + /** + * Return the number of elements in the array (not the size in bytes). + */ + int64_t size() const + { + return size_; + } + + /** + * Get a pointer to the beginning of the array. + */ + const void *data() const + { + return data_; + } + void *data() + { + return data_; + } + + const void *operator[](int64_t index) const + { + BLI_assert(index < size_); + return POINTER_OFFSET(data_, type_->size() * index); + } + + void *operator[](int64_t index) + { + BLI_assert(index < size_); + return POINTER_OFFSET(data_, type_->size() * index); + } + + operator GSpan() const + { + BLI_assert(type_ != nullptr); + return GSpan(*type_, data_, size_); + } + + operator GMutableSpan() + { + BLI_assert(type_ != nullptr); + return GMutableSpan(*type_, data_, size_); + } + + GSpan as_span() const + { + return *this; + } + + GMutableSpan as_mutable_span() + { + return *this; + } + + /** + * Access the allocator used by this array. + */ + Allocator &allocator() + { + return allocator_; + } + const Allocator &allocator() const + { + return allocator_; + } + + /** + * Destruct values and create a new array of the given size. The values in the new array are + * default constructed. + */ + void reinitialize(const int64_t new_size) + { + BLI_assert(new_size >= 0); + int64_t old_size = size_; + + type_->destruct_n(data_, size_); + size_ = 0; + + if (new_size <= old_size) { + type_->default_construct_n(data_, new_size); + } + else { + void *new_data = this->allocate(new_size); + try { + type_->default_construct_n(new_data, new_size); + } + catch (...) { + this->deallocate(new_data); + throw; + } + this->deallocate(data_); + data_ = new_data; + } + + size_ = new_size; + } + + private: + void *allocate(int64_t size) + { + const int64_t item_size = type_->size(); + const int64_t alignment = type_->alignment(); + return allocator_.allocate(static_cast(size) * item_size, alignment, AT); + } + + void deallocate(void *ptr) + { + allocator_.deallocate(ptr); + } +}; + +} // namespace blender::fn diff --git a/source/blender/functions/FN_generic_virtual_array.hh b/source/blender/functions/FN_generic_virtual_array.hh index 703118ba23e..8aad017e68b 100644 --- a/source/blender/functions/FN_generic_virtual_array.hh +++ b/source/blender/functions/FN_generic_virtual_array.hh @@ -27,6 +27,7 @@ #include "BLI_virtual_array.hh" +#include "FN_generic_array.hh" #include "FN_generic_span.hh" namespace blender::fn { @@ -398,6 +399,16 @@ template class GVArray_For_VArray : public GVArray { } }; +class GVArray_For_GArray : public GVArray_For_GSpan { + protected: + GArray<> array_; + + public: + GVArray_For_GArray(GArray<> array) : GVArray_For_GSpan(array.as_span()), array_(std::move(array)) + { + } +}; + /* Used to convert any generic virtual array into a typed one. */ template class VArray_For_GVArray : public VArray { protected: diff --git a/source/blender/functions/FN_multi_function_params.hh b/source/blender/functions/FN_multi_function_params.hh index fe4d2b90d80..d187985de9d 100644 --- a/source/blender/functions/FN_multi_function_params.hh +++ b/source/blender/functions/FN_multi_function_params.hh @@ -272,6 +272,22 @@ class MFParams { return span; } + /** + * Same as #uninitialized_single_output, but returns an empty span when the output is not + * required. + */ + template + MutableSpan uninitialized_single_output_if_required(int param_index, StringRef name = "") + { + return this->uninitialized_single_output_if_required(param_index, name).typed(); + } + GMutableSpan uninitialized_single_output_if_required(int param_index, StringRef name = "") + { + this->assert_correct_param(param_index, name, MFParamType::SingleOutput); + int data_index = builder_->signature_->data_index(param_index); + return builder_->mutable_spans_[data_index]; + } + template const VVectorArray &readonly_vector_input(int param_index, StringRef name = "") { diff --git a/source/blender/functions/FN_multi_function_procedure.hh b/source/blender/functions/FN_multi_function_procedure.hh index 4c06ce98ee3..a26eb1045a7 100644 --- a/source/blender/functions/FN_multi_function_procedure.hh +++ b/source/blender/functions/FN_multi_function_procedure.hh @@ -323,9 +323,9 @@ using MFDestructInstruction = fn::MFDestructInstruction; using MFProcedure = fn::MFProcedure; } // namespace multi_function_procedure_types -/* -------------------------------------------------------------------- - * MFInstructionCursor inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #MFInstructionCursor Inline Methods + * \{ */ inline MFInstructionCursor::MFInstructionCursor(MFCallInstruction &instruction) : type_(Call), instruction_(&instruction) @@ -367,9 +367,11 @@ inline MFInstructionCursor::Type MFInstructionCursor::type() const return type_; } -/* -------------------------------------------------------------------- - * MFVariable inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFVariable Inline Methods + * \{ */ inline MFDataType MFVariable::data_type() const { @@ -391,9 +393,11 @@ inline int MFVariable::id() const return id_; } -/* -------------------------------------------------------------------- - * MFInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFInstruction Inline Methods + * \{ */ inline MFInstructionType MFInstruction::type() const { @@ -405,9 +409,11 @@ inline Span MFInstruction::prev() const return prev_; } -/* -------------------------------------------------------------------- - * MFCallInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFCallInstruction Inline Methods + * \{ */ inline const MultiFunction &MFCallInstruction::fn() const { @@ -434,9 +440,11 @@ inline Span MFCallInstruction::params() const return params_; } -/* -------------------------------------------------------------------- - * MFBranchInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFBranchInstruction Inline Methods + * \{ */ inline MFVariable *MFBranchInstruction::condition() { @@ -468,9 +476,11 @@ inline const MFInstruction *MFBranchInstruction::branch_false() const return branch_false_; } -/* -------------------------------------------------------------------- - * MFDestructInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFDestructInstruction Inline Methods + * \{ */ inline MFVariable *MFDestructInstruction::variable() { @@ -492,9 +502,11 @@ inline const MFInstruction *MFDestructInstruction::next() const return next_; } -/* -------------------------------------------------------------------- - * MFDummyInstruction inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFDummyInstruction Inline Methods + * \{ */ inline MFInstruction *MFDummyInstruction::next() { @@ -506,9 +518,11 @@ inline const MFInstruction *MFDummyInstruction::next() const return next_; } -/* -------------------------------------------------------------------- - * MFProcedure inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #MFProcedure Inline Methods + * \{ */ inline Span MFProcedure::params() const { @@ -536,4 +550,6 @@ inline Span MFProcedure::variables() const return variables_; } +/** \} */ + } // namespace blender::fn diff --git a/source/blender/functions/intern/field.cc b/source/blender/functions/intern/field.cc index 6a4518ad4a6..4de5e71c910 100644 --- a/source/blender/functions/intern/field.cc +++ b/source/blender/functions/intern/field.cc @@ -260,20 +260,6 @@ static void build_multi_function_procedure_for_fields(MFProcedure &procedure, BLI_assert(procedure.validate()); } -/** - * Utility class that destructs elements from a partially initialized array. - */ -struct PartiallyInitializedArray : NonCopyable, NonMovable { - void *buffer; - IndexMask mask; - const CPPType *type; - - ~PartiallyInitializedArray() - { - this->type->destruct_indices(this->buffer, this->mask); - } -}; - /** * Evaluate fields in the given context. If possible, multiple fields should be evaluated together, * because that can be more efficient when they share common sub-fields. @@ -387,11 +373,11 @@ Vector evaluate_fields(ResourceScope &scope, /* Allocate a new buffer for the computed result. */ buffer = scope.linear_allocator().allocate(type.size() * array_size, type.alignment()); - /* Make sure that elements in the buffer will be destructed. */ - PartiallyInitializedArray &destruct_helper = scope.construct(); - destruct_helper.buffer = buffer; - destruct_helper.mask = mask; - destruct_helper.type = &type; + if (!type.is_trivially_destructible()) { + /* Destruct values in the end. */ + scope.add_destruct_call( + [buffer, mask, &type]() { type.destruct_indices(buffer, mask); }); + } r_varrays[out_index] = &scope.construct( GSpan{type, buffer, array_size}); @@ -418,7 +404,10 @@ Vector evaluate_fields(ResourceScope &scope, build_multi_function_procedure_for_fields( procedure, scope, field_tree_info, constant_fields_to_evaluate); MFProcedureExecutor procedure_executor{"Procedure", procedure}; - MFParamsBuilder mf_params{procedure_executor, 1}; + /* Run the code below even when the mask is empty, so that outputs are properly prepared. + * Higher level code can detect this as well and just skip evaluating the field. */ + const int mask_size = mask.is_empty() ? 0 : 1; + MFParamsBuilder mf_params{procedure_executor, mask_size}; MFContextBuilder mf_context; /* Provide inputs to the procedure executor. */ @@ -432,14 +421,14 @@ Vector evaluate_fields(ResourceScope &scope, /* Allocate memory where the computed value will be stored in. */ void *buffer = scope.linear_allocator().allocate(type.size(), type.alignment()); - /* Use this to make sure that the value is destructed in the end. */ - PartiallyInitializedArray &destruct_helper = scope.construct(); - destruct_helper.buffer = buffer; - destruct_helper.mask = IndexRange(1); - destruct_helper.type = &type; + if (!type.is_trivially_destructible() && mask_size > 0) { + BLI_assert(mask_size == 1); + /* Destruct value in the end. */ + scope.add_destruct_call([buffer, &type]() { type.destruct(buffer); }); + } /* Pass output buffer to the procedure executor. */ - mf_params.add_uninitialized_single_output({type, buffer, 1}); + mf_params.add_uninitialized_single_output({type, buffer, mask_size}); /* Create virtual array that can be used after the procedure has been executed below. */ const int out_index = constant_field_indices[i]; @@ -447,7 +436,7 @@ Vector evaluate_fields(ResourceScope &scope, type, array_size, buffer); } - procedure_executor.call(IndexRange(1), mf_params, mf_context); + procedure_executor.call(IndexRange(mask_size), mf_params, mf_context); } /* Copy data to supplied destination arrays if necessary. In some cases the evaluation above has @@ -468,7 +457,8 @@ Vector evaluate_fields(ResourceScope &scope, /* Still have to copy over the data in the destination provided by the caller. */ if (output_varray->is_span()) { /* Materialize into a span. */ - computed_varray->materialize_to_uninitialized(output_varray->get_internal_span().data()); + computed_varray->materialize_to_uninitialized(mask, + output_varray->get_internal_span().data()); } else { /* Slower materialize into a different structure. */ @@ -487,6 +477,12 @@ Vector evaluate_fields(ResourceScope &scope, void evaluate_constant_field(const GField &field, void *r_value) { + if (field.node().depends_on_input()) { + const CPPType &type = field.cpp_type(); + type.copy_construct(type.default_value(), r_value); + return; + } + ResourceScope scope; FieldContext context; Vector varrays = evaluate_fields(scope, {field}, IndexRange(1), context); @@ -525,11 +521,43 @@ const GVArray *FieldContext::get_varray_for_input(const FieldInput &field_input, return field_input.get_varray_for_context(*this, mask, scope); } +IndexFieldInput::IndexFieldInput() : FieldInput(CPPType::get(), "Index") +{ + category_ = Category::Generated; +} + +GVArray *IndexFieldInput::get_index_varray(IndexMask mask, ResourceScope &scope) +{ + auto index_func = [](int i) { return i; }; + return &scope.construct< + fn::GVArray_For_EmbeddedVArray>>( + mask.min_array_size(), mask.min_array_size(), index_func); +} + +const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &UNUSED(context), + IndexMask mask, + ResourceScope &scope) const +{ + /* TODO: Investigate a similar method to IndexRange::as_span() */ + return get_index_varray(mask, scope); +} + +uint64_t IndexFieldInput::hash() const +{ + /* Some random constant hash. */ + return 128736487678; +} + +bool IndexFieldInput::is_equal_to(const fn::FieldNode &other) const +{ + return dynamic_cast(&other) != nullptr; +} + /* -------------------------------------------------------------------- * FieldOperation. */ -FieldOperation::FieldOperation(std::unique_ptr function, +FieldOperation::FieldOperation(std::shared_ptr function, Vector inputs) : FieldOperation(*function, std::move(inputs)) { diff --git a/source/blender/functions/intern/generic_virtual_array.cc b/source/blender/functions/intern/generic_virtual_array.cc index 9a83d8cd497..ea54f1e7c00 100644 --- a/source/blender/functions/intern/generic_virtual_array.cc +++ b/source/blender/functions/intern/generic_virtual_array.cc @@ -18,9 +18,9 @@ namespace blender::fn { -/* -------------------------------------------------------------------- - * GVArray_For_ShallowCopy. - */ +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_ShallowCopy + * \{ */ class GVArray_For_ShallowCopy : public GVArray { private: @@ -48,10 +48,11 @@ class GVArray_For_ShallowCopy : public GVArray { varray_.materialize_to_uninitialized(mask, dst); } }; +/** \} */ -/* -------------------------------------------------------------------- - * GVArray. - */ +/* -------------------------------------------------------------------- */ +/** \name #GVArray + * \{ */ void GVArray::materialize(void *dst) const { @@ -142,9 +143,11 @@ GVArrayPtr GVArray::shallow_copy() const return std::make_unique(*this); } -/* -------------------------------------------------------------------- - * GVMutableArray. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVMutableArray + * \{ */ void GVMutableArray::set_by_copy_impl(const int64_t index, const void *value) { @@ -191,9 +194,11 @@ void GVMutableArray::fill(const void *value) } } -/* -------------------------------------------------------------------- - * GVArray_For_GSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_GSpan + * \{ */ void GVArray_For_GSpan::get_impl(const int64_t index, void *r_value) const { @@ -215,9 +220,11 @@ GSpan GVArray_For_GSpan::get_internal_span_impl() const return GSpan(*type_, data_, size_); } -/* -------------------------------------------------------------------- - * GVMutableArray_For_GMutableSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVMutableArray_For_GMutableSpan + * \{ */ void GVMutableArray_For_GMutableSpan::get_impl(const int64_t index, void *r_value) const { @@ -255,9 +262,11 @@ GSpan GVMutableArray_For_GMutableSpan::get_internal_span_impl() const return GSpan(*type_, data_, size_); } -/* -------------------------------------------------------------------- - * GVArray_For_SingleValueRef. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_SingleValueRef + * \{ */ void GVArray_For_SingleValueRef::get_impl(const int64_t UNUSED(index), void *r_value) const { @@ -290,9 +299,11 @@ void GVArray_For_SingleValueRef::get_internal_single_impl(void *r_value) const type_->copy_assign(value_, r_value); } -/* -------------------------------------------------------------------- - * GVArray_For_SingleValue. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_SingleValue + * \{ */ GVArray_For_SingleValue::GVArray_For_SingleValue(const CPPType &type, const int64_t size, @@ -309,9 +320,11 @@ GVArray_For_SingleValue::~GVArray_For_SingleValue() MEM_freeN((void *)value_); } -/* -------------------------------------------------------------------- - * GVArray_GSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_GSpan + * \{ */ GVArray_GSpan::GVArray_GSpan(const GVArray &varray) : GSpan(varray.type()), varray_(varray) { @@ -334,9 +347,11 @@ GVArray_GSpan::~GVArray_GSpan() } } -/* -------------------------------------------------------------------- - * GVMutableArray_GSpan. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVMutableArray_GSpan + * \{ */ GVMutableArray_GSpan::GVMutableArray_GSpan(GVMutableArray &varray, const bool copy_values_to_span) : GMutableSpan(varray.type()), varray_(varray) @@ -387,9 +402,11 @@ void GVMutableArray_GSpan::disable_not_applied_warning() show_not_saved_warning_ = false; } -/* -------------------------------------------------------------------- - * GVArray_For_SlicedGVArray. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_For_SlicedGVArray + * \{ */ void GVArray_For_SlicedGVArray::get_impl(const int64_t index, void *r_value) const { @@ -401,9 +418,11 @@ void GVArray_For_SlicedGVArray::get_to_uninitialized_impl(const int64_t index, v varray_.get_to_uninitialized(index + offset_, r_value); } -/* -------------------------------------------------------------------- - * GVArray_Slice. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #GVArray_Slice + * \{ */ GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice) { @@ -430,4 +449,6 @@ GVArray_Slice::GVArray_Slice(const GVArray &varray, const IndexRange slice) } } +/** \} */ + } // namespace blender::fn diff --git a/source/blender/functions/intern/multi_function_procedure.cc b/source/blender/functions/intern/multi_function_procedure.cc index fa95e8de71e..986c5dff0c4 100644 --- a/source/blender/functions/intern/multi_function_procedure.cc +++ b/source/blender/functions/intern/multi_function_procedure.cc @@ -419,6 +419,10 @@ bool MFProcedure::validate_initialization() const const MultiFunction &fn = *instruction->fn_; for (const int param_index : fn.param_indices()) { const MFParamType param_type = fn.param_type(param_index); + /* If the parameter was an unneeded output, it could be null. */ + if (!instruction->params_[param_index]) { + continue; + } const MFVariable &variable = *instruction->params_[param_index]; const InitState state = this->find_initialization_state_before_instruction(*instruction, variable); diff --git a/source/blender/functions/intern/multi_function_procedure_executor.cc b/source/blender/functions/intern/multi_function_procedure_executor.cc index b97282accdd..6d2d121bafd 100644 --- a/source/blender/functions/intern/multi_function_procedure_executor.cc +++ b/source/blender/functions/intern/multi_function_procedure_executor.cc @@ -1022,7 +1022,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction, } } - fn.call(IndexRange(1), params, context); + try { + fn.call(IndexRange(1), params, context); + } + catch (...) { + /* Multi-functions must not throw exceptions. */ + BLI_assert_unreachable(); + } } else { MFParamsBuilder params(fn, &mask); @@ -1038,7 +1044,13 @@ static void execute_call_instruction(const MFCallInstruction &instruction, } } - fn.call(mask, params, context); + try { + fn.call(mask, params, context); + } + catch (...) { + /* Multi-functions must not throw exceptions. */ + BLI_assert_unreachable(); + } } } diff --git a/source/blender/functions/tests/FN_generic_array_test.cc b/source/blender/functions/tests/FN_generic_array_test.cc new file mode 100644 index 00000000000..417ab479cca --- /dev/null +++ b/source/blender/functions/tests/FN_generic_array_test.cc @@ -0,0 +1,118 @@ +/* Apache License, Version 2.0 */ + +#include "testing/testing.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_array.hh" + +#include "FN_generic_array.hh" + +namespace blender::fn::tests { + +TEST(generic_array, TypeConstructor) +{ + GArray array(CPPType::get()); + EXPECT_TRUE(array.data() == nullptr); + EXPECT_EQ(array.size(), 0); + EXPECT_EQ(array.as_span().typed().size(), 0); + EXPECT_TRUE(array.is_empty()); +} + +TEST(generic_array, MoveConstructor) +{ + GArray array_a(CPPType::get(), (int64_t)10); + GMutableSpan span_a = array_a.as_mutable_span(); + MutableSpan typed_span_a = span_a.typed(); + typed_span_a.fill(42); + + const GArray array_b = std::move(array_a); + Span typed_span_b = array_b.as_span().typed(); + EXPECT_FALSE(array_b.data() == nullptr); + EXPECT_EQ(array_b.size(), 10); + EXPECT_EQ(typed_span_b[4], 42); + + /* Make sure the copy constructor cleaned up the original, but it shouldn't clear the type. */ + EXPECT_TRUE(array_a.data() == nullptr); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(array_a.size(), 0); /* NOLINT: bugprone-use-after-move */ + EXPECT_TRUE(array_a.is_empty()); /* NOLINT: bugprone-use-after-move */ + EXPECT_EQ(array_b.type(), array_a.type()); /* NOLINT: bugprone-use-after-move */ +} + +TEST(generic_array, CopyConstructor) +{ + GArray array_a(CPPType::get(), (int64_t)10); + GMutableSpan span_a = array_a.as_mutable_span(); + MutableSpan typed_span_a = span_a.typed(); + typed_span_a.fill(42); + + /* From span directly. */ + const GArray array_b = array_a.as_span(); + Span typed_span_b = array_b.as_span().typed(); + EXPECT_FALSE(array_b.data() == nullptr); + EXPECT_EQ(array_b.size(), 10); + EXPECT_EQ(typed_span_b[4], 42); + EXPECT_FALSE(array_a.is_empty()); + + /* From array. */ + const GArray array_c = array_a; + Span typed_span_c = array_c.as_span().typed(); + EXPECT_FALSE(array_c.data() == nullptr); + EXPECT_EQ(array_c.size(), 10); + EXPECT_EQ(typed_span_c[4], 42); + EXPECT_FALSE(array_a.is_empty()); +} + +TEST(generic_array, BufferAndSizeConstructor) +{ + int32_t *values = (int32_t *)MEM_malloc_arrayN(12, sizeof(int32_t), __func__); + void *buffer = (void *)values; + GArray array(CPPType::get(), buffer, 4); + EXPECT_FALSE(array.data() == nullptr); + EXPECT_EQ(array.size(), 4); + EXPECT_FALSE(array.is_empty()); + EXPECT_EQ(array.as_span().typed().size(), 4); + EXPECT_EQ(array[0], &values[0]); + EXPECT_EQ(array[1], &values[1]); + EXPECT_EQ(array[2], &values[2]); + EXPECT_EQ(array[3], &values[3]); +} + +TEST(generic_array, Reinitialize) +{ + GArray array(CPPType::get(), (int64_t)5); + EXPECT_FALSE(array.data() == nullptr); + GMutableSpan span = array.as_mutable_span(); + MutableSpan typed_span = span.typed(); + typed_span.fill(77); + EXPECT_FALSE(typed_span.data() == nullptr); + typed_span[2] = 8; + EXPECT_EQ(array[2], &typed_span[2]); + EXPECT_EQ(typed_span[0], 77); + EXPECT_EQ(typed_span[1], 77); + + array.reinitialize(10); + EXPECT_EQ(array.size(), 10); + span = array.as_mutable_span(); + EXPECT_EQ(span.size(), 10); + + typed_span = span.typed(); + EXPECT_FALSE(typed_span.data() == nullptr); + + array.reinitialize(0); + EXPECT_EQ(array.size(), 0); +} + +TEST(generic_array, InContainer) +{ + blender::Array> arrays; + for (GArray<> &array : arrays) { + array = GArray(CPPType::get(), (int64_t)5); + array.as_mutable_span().typed().fill(55); + } + for (GArray<> &array : arrays) { + EXPECT_EQ(array.as_span().typed()[3], 55); + } +} + +} // namespace blender::fn::tests diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt new file mode 100644 index 00000000000..03f736d4dde --- /dev/null +++ b/source/blender/geometry/CMakeLists.txt @@ -0,0 +1,54 @@ +# ***** 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) 2006, Blender Foundation +# All rights reserved. + +set(INC + . + ../blenkernel + ../blenlib + ../blentranslation + ../functions + ../makesdna + ../makesrna + ../../../intern/guardedalloc + ${CMAKE_BINARY_DIR}/source/blender/makesdna/intern +) + +set(SRC + intern/mesh_to_curve_convert.cc + GEO_mesh_to_curve.hh +) + +set(LIB + bf_blenkernel + bf_blenlib +) + +if(WITH_TBB) + add_definitions(-DWITH_TBB) + + list(APPEND INC_SYS + ${TBB_INCLUDE_DIRS} + ) + + list(APPEND LIB + ${TBB_LIBRARIES} + ) +endif() + +blender_add_lib(bf_geometry "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/source/blender/geometry/GEO_mesh_to_curve.hh b/source/blender/geometry/GEO_mesh_to_curve.hh new file mode 100644 index 00000000000..66459ab79a9 --- /dev/null +++ b/source/blender/geometry/GEO_mesh_to_curve.hh @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_index_mask.hh" + +#include "BKE_spline.hh" + +#pragma once + +struct Mesh; +class MeshComponent; + +/** \file + * \ingroup geo + */ + +namespace blender::geometry { + +std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, + const IndexMask selection); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc new file mode 100644 index 00000000000..24f0b6308ba --- /dev/null +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -0,0 +1,279 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_set.hh" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_attribute_access.hh" +#include "BKE_attribute_math.hh" +#include "BKE_geometry_set.hh" +#include "BKE_spline.hh" + +#include "GEO_mesh_to_curve.hh" + +namespace blender::geometry { + +template +static void copy_attribute_to_points(const VArray &source_data, + Span map, + MutableSpan dest_data) +{ + for (const int point_index : map.index_range()) { + const int vert_index = map[point_index]; + dest_data[point_index] = source_data[vert_index]; + } +} + +static void copy_attributes_to_points(CurveEval &curve, + const MeshComponent &mesh_component, + Span> point_to_vert_maps) +{ + MutableSpan splines = curve.splines(); + Set source_attribute_ids = mesh_component.attribute_ids(); + + /* Copy builtin control point attributes. */ + if (source_attribute_ids.contains("tilt")) { + const fn::GVArray_Typed tilt_attribute = mesh_component.attribute_get_for_read( + "tilt", ATTR_DOMAIN_POINT, 0.0f); + threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); + } + }); + source_attribute_ids.remove_contained("tilt"); + } + if (source_attribute_ids.contains("radius")) { + const fn::GVArray_Typed radius_attribute = mesh_component.attribute_get_for_read( + "radius", ATTR_DOMAIN_POINT, 1.0f); + threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { + for (const int i : range) { + copy_attribute_to_points( + *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); + } + }); + source_attribute_ids.remove_contained("radius"); + } + + for (const bke::AttributeIDRef &attribute_id : source_attribute_ids) { + /* Don't copy attributes that are built-in on meshes but not on curves. */ + if (mesh_component.attribute_is_builtin(attribute_id)) { + continue; + } + + if (!attribute_id.should_be_kept()) { + continue; + } + + const fn::GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read( + attribute_id, ATTR_DOMAIN_POINT); + /* Some attributes might not exist if they were builtin attribute on domains that don't + * have any elements, i.e. a face attribute on the output of the line primitive node. */ + if (!mesh_attribute) { + continue; + } + + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + /* Create attribute on the spline points. */ + splines[i]->attributes.create(attribute_id, data_type); + std::optional spline_attribute = splines[i]->attributes.get_for_write( + attribute_id); + BLI_assert(spline_attribute); + + /* Copy attribute based on the map for this spline. */ + attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_attribute_to_points( + mesh_attribute->typed(), point_to_vert_maps[i], spline_attribute->typed()); + }); + } + }); + } + + curve.assert_valid_point_attributes(); +} + +struct CurveFromEdgesOutput { + std::unique_ptr curve; + Vector> point_to_vert_maps; +}; + +static CurveFromEdgesOutput edges_to_curve(Span verts, Span> edges) +{ + std::unique_ptr curve = std::make_unique(); + Vector> point_to_vert_maps; + + /* Compute the number of edges connecting to each vertex. */ + Array neighbor_count(verts.size(), 0); + for (const std::pair &edge : edges) { + neighbor_count[edge.first]++; + neighbor_count[edge.second]++; + } + + /* Compute an offset into the array of neighbor edges based on the counts. */ + Array neighbor_offsets(verts.size()); + int start = 0; + for (const int i : verts.index_range()) { + neighbor_offsets[i] = start; + start += neighbor_count[i]; + } + + /* Use as an index into the "neighbor group" for each vertex. */ + Array used_slots(verts.size(), 0); + /* Calculate the indices of each vertex's neighboring edges. */ + Array neighbors(edges.size() * 2); + for (const int i : edges.index_range()) { + const int v1 = edges[i].first; + const int v2 = edges[i].second; + neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; + neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; + used_slots[v1]++; + used_slots[v2]++; + } + + /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ + Array unused_edges = std::move(used_slots); + + for (const int start_vert : verts.index_range()) { + /* The vertex will be part of a cyclic spline. */ + if (neighbor_count[start_vert] == 2) { + continue; + } + + /* The vertex has no connected edges, or they were already used. */ + if (unused_edges[start_vert] == 0) { + continue; + } + + for (const int i : IndexRange(neighbor_count[start_vert])) { + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert] + i]; + + if (unused_edges[next_vert] == 0) { + continue; + } + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we read a vertex with more than two connected edges. */ + while (true) { + int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + if (neighbor_count[current_vert] != 2) { + break; + } + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + } + + /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ + for (const int start_vert : verts.index_range()) { + if (unused_edges[start_vert] != 2) { + continue; + } + + int current_vert = start_vert; + int next_vert = neighbors[neighbor_offsets[current_vert]]; + + std::unique_ptr spline = std::make_unique(); + Vector point_to_vert_map; + spline->set_cyclic(true); + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + + /* Follow connected edges until we loop back to the start vertex. */ + while (next_vert != start_vert) { + const int last_vert = current_vert; + current_vert = next_vert; + + spline->add_point(verts[current_vert].co, 1.0f, 0.0f); + point_to_vert_map.append(current_vert); + unused_edges[current_vert]--; + unused_edges[last_vert]--; + + const int offset = neighbor_offsets[current_vert]; + const int next_a = neighbors[offset]; + const int next_b = neighbors[offset + 1]; + next_vert = (last_vert == next_a) ? next_b : next_a; + } + + spline->attributes.reallocate(spline->size()); + curve->add_spline(std::move(spline)); + point_to_vert_maps.append(std::move(point_to_vert_map)); + } + + curve->attributes.reallocate(curve->splines().size()); + return {std::move(curve), std::move(point_to_vert_maps)}; +} + +/** + * Get a separate array of the indices for edges in a selection (a boolean attribute). + * This helps to make the above algorithm simpler by removing the need to check for selection + * in many places. + */ +static Vector> get_selected_edges(const Mesh &mesh, const IndexMask selection) +{ + Vector> selected_edges; + for (const int i : selection) { + selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); + } + return selected_edges; +} + +/** + * Convert the mesh into one or many poly splines. Since splines cannot have branches, + * intersections of more than three edges will become breaks in splines. Attributes that + * are not built-in on meshes and not curves are transferred to the result curve. + */ +std::unique_ptr mesh_to_curve_convert(const MeshComponent &mesh_component, + const IndexMask selection) +{ + const Mesh &mesh = *mesh_component.get_for_read(); + Vector> selected_edges = get_selected_edges(*mesh_component.get_for_read(), + selection); + CurveFromEdgesOutput output = edges_to_curve({mesh.mvert, mesh.totvert}, selected_edges); + copy_attributes_to_points(*output.curve, mesh_component, output.point_to_vert_maps); + return std::move(output.curve); +} + +} // namespace blender::geometry diff --git a/source/blender/gpencil_modifiers/CMakeLists.txt b/source/blender/gpencil_modifiers/CMakeLists.txt index adf68e534bb..afcd551d0af 100644 --- a/source/blender/gpencil_modifiers/CMakeLists.txt +++ b/source/blender/gpencil_modifiers/CMakeLists.txt @@ -69,7 +69,8 @@ set(SRC intern/MOD_gpencilthick.c intern/MOD_gpenciltime.c intern/MOD_gpenciltint.c - intern/MOD_gpencilweight.c + intern/MOD_gpencilweight_angle.c + intern/MOD_gpencilweight_proximity.c MOD_gpencil_lineart.h MOD_gpencil_modifiertypes.h diff --git a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h index 043186155b7..d9285f44a37 100644 --- a/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h +++ b/source/blender/gpencil_modifiers/MOD_gpencil_modifiertypes.h @@ -44,7 +44,8 @@ extern GpencilModifierTypeInfo modifierType_Gpencil_Armature; extern GpencilModifierTypeInfo modifierType_Gpencil_Time; extern GpencilModifierTypeInfo modifierType_Gpencil_Multiply; extern GpencilModifierTypeInfo modifierType_Gpencil_Texture; -extern GpencilModifierTypeInfo modifierType_Gpencil_Weight; +extern GpencilModifierTypeInfo modifierType_Gpencil_WeightProximity; +extern GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle; extern GpencilModifierTypeInfo modifierType_Gpencil_Lineart; extern GpencilModifierTypeInfo modifierType_Gpencil_Dash; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c index e750c22f0e8..2e241ea5848 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_ui_common.c @@ -380,7 +380,7 @@ PanelType *gpencil_modifier_panel_register(ARegionType *region_type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = gpencil_modifier_reorder; panel_type->get_list_data_expand_flag = get_gpencil_modifier_expand_flag; panel_type->set_list_data_expand_flag = set_gpencil_modifier_expand_flag; @@ -413,7 +413,7 @@ PanelType *gpencil_modifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = gpencil_modifier_ui_poll; - panel_type->flag = (PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX); + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c index 5eb1eeab780..9ea146c77f2 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.c @@ -63,7 +63,8 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) INIT_GP_TYPE(Time); INIT_GP_TYPE(Multiply); INIT_GP_TYPE(Texture); - INIT_GP_TYPE(Weight); + INIT_GP_TYPE(WeightAngle); + INIT_GP_TYPE(WeightProximity); INIT_GP_TYPE(Lineart); INIT_GP_TYPE(Dash); #undef INIT_GP_TYPE @@ -72,7 +73,7 @@ void gpencil_modifier_type_init(GpencilModifierTypeInfo *types[]) /* verify if valid layer, material and pass index */ bool is_stroke_affected_by_modifier(Object *ob, char *mlayername, - Material *material, + const Material *material, const int mpassindex, const int gpl_passindex, const int minpoints, @@ -155,7 +156,7 @@ float get_modifier_point_weight(MDeformVert *dvert, bool inverse, int def_nr) MDeformWeight *dw = BKE_defvert_find_index(dvert, def_nr); weight = dw ? dw->weight : -1.0f; if ((weight >= 0.0f) && (inverse)) { - return -1.0f; + return 1.0f - weight; } if ((weight < 0.0f) && (!inverse)) { diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h index 30e54f44499..2878ad4c73a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencil_util.h @@ -31,7 +31,7 @@ struct bGPDstroke; bool is_stroke_affected_by_modifier(struct Object *ob, char *mlayername, - struct Material *material, + const struct Material *material, const int mpassindex, const int gpl_passindex, const int minpoints, diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c index ba33edd6a94..33cc3094a36 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencildash.c @@ -255,7 +255,7 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec BKE_gpencil_frame_active_set(depsgraph, gpd); bGPDframe *gpf = gpl->actframe; if (gpf == NULL) { - return; + continue; } apply_dash_for_frame(ob, gpl, gpd, gpf, (DashGpencilModifierData *)md); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c index 6aa0e6c152e..80b60547e92 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillength.c @@ -72,9 +72,14 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) } static bool gpencil_modify_stroke(bGPDstroke *gps, - float length, + const float length, const float overshoot_fac, - const short len_mode) + const short len_mode, + const bool use_curvature, + const int extra_point_count, + const float segment_influence, + const float max_angle, + const bool invert_curvature) { bool changed = false; if (length == 0.0f) { @@ -82,10 +87,18 @@ static bool gpencil_modify_stroke(bGPDstroke *gps, } if (length > 0.0f) { - BKE_gpencil_stroke_stretch(gps, length, overshoot_fac, len_mode); + changed = BKE_gpencil_stroke_stretch(gps, + length, + overshoot_fac, + len_mode, + use_curvature, + extra_point_count, + segment_influence, + max_angle, + invert_curvature); } else { - changed |= BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); + changed = BKE_gpencil_stroke_shrink(gps, fabs(length), len_mode); } return changed; @@ -96,12 +109,51 @@ static void applyLength(LengthGpencilModifierData *lmd, bGPdata *gpd, bGPDstroke bool changed = false; const float len = (lmd->mode == GP_LENGTH_ABSOLUTE) ? 1.0f : BKE_gpencil_stroke_length(gps, true); + const int totpoints = gps->totpoints; if (len < FLT_EPSILON) { return; } - changed |= gpencil_modify_stroke(gps, len * lmd->start_fac, lmd->overshoot_fac, 1); - changed |= gpencil_modify_stroke(gps, len * lmd->end_fac, lmd->overshoot_fac, 2); + /* Always do the stretching first since it might depend on points which could be deleted by the + * shrink. */ + float first_fac = lmd->start_fac; + int first_mode = 1; + float second_fac = lmd->end_fac; + int second_mode = 2; + if (first_fac < 0) { + SWAP(float, first_fac, second_fac); + SWAP(int, first_mode, second_mode); + } + + const int first_extra_point_count = ceil(first_fac * lmd->point_density); + const int second_extra_point_count = ceil(second_fac * lmd->point_density); + + changed |= gpencil_modify_stroke(gps, + len * first_fac, + lmd->overshoot_fac, + first_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + first_extra_point_count, + lmd->segment_influence, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); + /* HACK: The second #overshoot_fac needs to be adjusted because it is not + * done in the same stretch call, because it can have a different length. + * The adjustment needs to be stable when + * `ceil(overshoot_fac*(gps->totpoints - 2))` is used in stretch and never + * produce a result higher than `totpoints - 2`. */ + const float second_overshoot_fac = lmd->overshoot_fac * (totpoints - 2) / + ((float)gps->totpoints - 2) * + (1.0f - 0.1f / (totpoints - 1.0f)); + changed |= gpencil_modify_stroke(gps, + len * second_fac, + second_overshoot_fac, + second_mode, + lmd->flag & GP_LENGTH_USE_CURVATURE, + second_extra_point_count, + lmd->segment_influence, + lmd->max_angle, + lmd->flag & GP_LENGTH_INVERT_CURVATURE); if (changed) { BKE_gpencil_stroke_geometry_update(gpd, gps); @@ -117,20 +169,25 @@ static void deformStroke(GpencilModifierData *md, { bGPdata *gpd = ob->data; LengthGpencilModifierData *lmd = (LengthGpencilModifierData *)md; - if (is_stroke_affected_by_modifier(ob, - lmd->layername, - lmd->material, - lmd->pass_index, - lmd->layer_pass, - 1, - gpl, - gps, - lmd->flag & GP_LENGTH_INVERT_LAYER, - lmd->flag & GP_LENGTH_INVERT_PASS, - lmd->flag & GP_LENGTH_INVERT_LAYERPASS, - lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { - applyLength(lmd, gpd, gps); + if (!is_stroke_affected_by_modifier(ob, + lmd->layername, + lmd->material, + lmd->pass_index, + lmd->layer_pass, + 1, + gpl, + gps, + lmd->flag & GP_LENGTH_INVERT_LAYER, + lmd->flag & GP_LENGTH_INVERT_PASS, + lmd->flag & GP_LENGTH_INVERT_LAYERPASS, + lmd->flag & GP_LENGTH_INVERT_MATERIAL)) { + return; } + if ((gps->flag & GP_STROKE_CYCLIC) != 0) { + /* Don't affect cyclic strokes as they have no start/end. */ + return; + } + applyLength(lmd, gpd, gps); } static void bakeModifier(Main *UNUSED(bmain), @@ -168,10 +225,16 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayout *col = uiLayoutColumn(layout, true); - uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); - uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + if (RNA_enum_get(ptr, "mode") == GP_LENGTH_RELATIVE) { + uiItemR(col, ptr, "start_factor", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_factor", 0, IFACE_("End"), ICON_NONE); + } + else { + uiItemR(col, ptr, "start_length", 0, IFACE_("Start"), ICON_NONE); + uiItemR(col, ptr, "end_length", 0, IFACE_("End"), ICON_NONE); + } - uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Overshoot"), ICON_NONE); + uiItemR(layout, ptr, "overshoot_factor", UI_ITEM_R_SLIDER, IFACE_("Used Length"), ICON_NONE); gpencil_modifier_panel_end(layout, ptr); } @@ -181,10 +244,39 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) gpencil_modifier_masking_panel_draw(panel, true, false); } +static void curvature_header_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiItemR(layout, ptr, "use_curvature", 0, IFACE_("Curvature"), ICON_NONE); +} + +static void curvature_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + + uiLayoutSetPropSep(layout, true); + + uiLayout *col = uiLayoutColumn(layout, false); + + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_curvature")); + + uiItemR(col, ptr, "point_density", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "segment_influence", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "max_angle", 0, NULL, ICON_NONE); + uiItemR(col, ptr, "invert_curvature", 0, IFACE_("Invert"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( region_type, eGpencilModifierType_Length, panel_draw); + gpencil_modifier_subpanel_register( + region_type, "curvature", "", curvature_header_draw, curvature_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c index 01488a8b2de..b35ebd4be9a 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencillineart.c @@ -160,12 +160,14 @@ static void generateStrokes(GpencilModifierData *md, Depsgraph *depsgraph, Objec LineartCache *local_lc = gpd->runtime.lineart_cache; if (!gpd->runtime.lineart_cache) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (!(lmd->flags & LRT_GPENCIL_USE_CACHE)) { - MOD_lineart_compute_feature_lines(depsgraph, lmd, &local_lc); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); @@ -210,7 +212,8 @@ static void bakeModifier(Main *UNUSED(bmain), lmd->edge_types_override = lmd->edge_types; lmd->level_end_override = lmd->level_end; - MOD_lineart_compute_feature_lines(depsgraph, lmd, &gpd->runtime.lineart_cache); + MOD_lineart_compute_feature_lines( + depsgraph, lmd, &gpd->runtime.lineart_cache, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } @@ -261,7 +264,13 @@ static void updateDepsgraph(GpencilModifierData *md, else { add_this_collection(ctx->scene->master_collection, ctx, mode); } - if (ctx->scene->camera) { + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA && lmd->source_camera) { + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); + DEG_add_object_relation( + ctx->node, lmd->source_camera, DEG_OB_COMP_PARAMETERS, "Line Art Modifier"); + } + else if (ctx->scene->camera) { DEG_add_object_relation( ctx->node, ctx->scene->camera, DEG_OB_COMP_TRANSFORM, "Line Art Modifier"); DEG_add_object_relation( @@ -277,6 +286,7 @@ static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, walk(userData, ob, (ID **)&lmd->source_collection, IDWALK_CB_NOP); walk(userData, ob, (ID **)&lmd->source_object, IDWALK_CB_NOP); + walk(userData, ob, (ID **)&lmd->source_camera, IDWALK_CB_NOP); } static void panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -378,11 +388,16 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } - uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE); + uiLayout *row = uiLayoutRowWithHeading(layout, false, IFACE_("Custom Camera")); + uiItemR(row, ptr, "use_custom_camera", 0, "", 0); + uiLayout *subrow = uiLayoutRow(row, true); + uiLayoutSetActive(subrow, RNA_boolean_get(ptr, "use_custom_camera")); + uiLayoutSetPropSep(subrow, true); + uiItemR(subrow, ptr, "source_camera", 0, "", ICON_OBJECT_DATA); uiLayout *col = uiLayoutColumn(layout, true); @@ -391,7 +406,7 @@ static void options_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(col, ptr, "use_object_instances", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_clip_plane_boundaries", 0, NULL, ICON_NONE); uiItemR(col, ptr, "use_crease_on_smooth", 0, IFACE_("Crease On Smooth"), ICON_NONE); - uiItemR(layout, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); + uiItemR(col, ptr, "use_crease_on_sharp", 0, IFACE_("Crease On Sharp"), ICON_NONE); } static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) @@ -412,14 +427,23 @@ static void style_panel_draw(const bContext *UNUSED(C), Panel *panel) static void occlusion_panel_draw(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetPropSep(layout, true); uiLayoutSetEnabled(layout, !is_baked); - const bool use_multiple_levels = RNA_boolean_get(ptr, "use_multiple_levels"); + if (!show_in_front) { + uiItemL(layout, TIP_("Object is not in front"), ICON_INFO); + } + + layout = uiLayoutColumn(layout, false); + uiLayoutSetActive(layout, show_in_front); uiItemR(layout, ptr, "use_multiple_levels", 0, IFACE_("Range"), ICON_NONE); @@ -447,11 +471,14 @@ static bool anything_showing_through(PointerRNA *ptr) static void material_mask_panel_draw_header(const bContext *UNUSED(C), Panel *panel) { uiLayout *layout = panel->layout; - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); const bool is_baked = RNA_boolean_get(ptr, "is_baked"); + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + uiLayoutSetEnabled(layout, !is_baked); - uiLayoutSetActive(layout, anything_showing_through(ptr)); + uiLayoutSetActive(layout, show_in_front && anything_showing_through(ptr)); uiItemR(layout, ptr, "use_material_mask", 0, IFACE_("Material Mask"), ICON_NONE); } @@ -541,7 +568,7 @@ static void face_mark_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } @@ -569,7 +596,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } @@ -587,6 +614,7 @@ static void chaining_panel_draw(const bContext *UNUSED(C), Panel *panel) is_geom ? IFACE_("Geometry Threshold") : NULL, ICON_NONE); + uiItemR(layout, ptr, "smooth_tolerance", UI_ITEM_R_SLIDER, NULL, ICON_NONE); uiItemR(layout, ptr, "split_angle", UI_ITEM_R_SLIDER, NULL, ICON_NONE); } @@ -605,7 +633,7 @@ static void vgroup_panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetEnabled(layout, !is_baked); if (use_cache && !is_first) { - uiItemL(layout, "Cached from the first line art modifier.", ICON_INFO); + uiItemL(layout, TIP_("Cached from the first line art modifier"), ICON_INFO); return; } @@ -638,7 +666,7 @@ static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel) if (is_baked) { uiLayout *col = uiLayoutColumn(layout, false); uiLayoutSetPropSep(col, false); - uiItemL(col, IFACE_("Modifier has baked data"), ICON_NONE); + uiItemL(col, TIP_("Modifier has baked data"), ICON_NONE); uiItemR( col, ptr, "is_baked", UI_ITEM_R_TOGGLE, IFACE_("Continue Without Clearing"), ICON_NONE); } @@ -653,6 +681,32 @@ static void bake_panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemO(col, NULL, ICON_NONE, "OBJECT_OT_lineart_clear_all"); } +static void composition_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayout *layout = panel->layout; + + const bool show_in_front = RNA_boolean_get(&ob_ptr, "show_in_front"); + + uiLayoutSetPropSep(layout, true); + + uiItemR(layout, ptr, "overscan", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_image_boundary_trimming", 0, NULL, ICON_NONE); + + if (show_in_front) { + uiItemL(layout, TIP_("Object is shown in front"), ICON_ERROR); + } + + uiLayout *col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, !show_in_front); + + uiItemR(col, ptr, "stroke_depth_offset", UI_ITEM_R_SLIDER, IFACE_("Depth Offset"), ICON_NONE); + uiItemR( + col, ptr, "use_offset_towards_custom_camera", 0, IFACE_("Towards Custom Camera"), ICON_NONE); +} + static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( @@ -680,6 +734,8 @@ static void panelRegister(ARegionType *region_type) region_type, "chaining", "Chaining", NULL, chaining_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "vgroup", "Vertex Weight Transfer", NULL, vgroup_panel_draw, panel_type); + gpencil_modifier_subpanel_register( + region_type, "composition", "Composition", NULL, composition_panel_draw, panel_type); gpencil_modifier_subpanel_register( region_type, "bake", "Bake", NULL, bake_panel_draw, panel_type); } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c index fb75b1e99ac..2e55369ea97 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilopacity.c @@ -240,10 +240,10 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiItemR(layout, ptr, "hardness", 0, NULL, ICON_NONE); } else { - const bool is_normalized = RNA_boolean_get(ptr, "normalize_opacity"); + const bool is_normalized = RNA_boolean_get(ptr, "use_normalized_opacity"); const bool is_weighted = RNA_boolean_get(ptr, "use_weight_factor"); - uiItemR(layout, ptr, "normalize_opacity", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_normalized_opacity", 0, NULL, ICON_NONE); const char *text = (is_normalized) ? IFACE_("Strength") : IFACE_("Opacity Factor"); uiLayout *row = uiLayoutRow(layout, true); diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c index cac700e15f4..233992bbd31 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilthick.c @@ -191,8 +191,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) uiLayoutSetPropSep(layout, true); - uiItemR(layout, ptr, "normalize_thickness", 0, NULL, ICON_NONE); - if (RNA_boolean_get(ptr, "normalize_thickness")) { + uiItemR(layout, ptr, "use_normalized_thickness", 0, NULL, ICON_NONE); + if (RNA_boolean_get(ptr, "use_normalized_thickness")) { uiItemR(layout, ptr, "thickness", 0, NULL, ICON_NONE); } else { diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c index 7ce731c0dd6..a055dff9449 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpenciltint.c @@ -281,7 +281,7 @@ static void bakeModifier(Main *UNUSED(bmain), bGPdata *gpd = ob->data; int oldframe = (int)DEG_get_ctime(depsgraph); - if (mmd->object == NULL) { + if ((mmd->type == GP_TINT_GRADIENT) && (mmd->object == NULL)) { return; } diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c new file mode 100644 index 00000000000..a833be080a9 --- /dev/null +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_angle.c @@ -0,0 +1,259 @@ +/* + * 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) 2021, Blender Foundation + * This is a new part of Blender + */ + +/** \file + * \ingroup modifiers + */ + +#include + +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "DNA_defaults.h" +#include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_screen_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_deform.h" +#include "BKE_gpencil.h" +#include "BKE_gpencil_modifier.h" +#include "BKE_lib_query.h" +#include "BKE_modifier.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" +#include "DEG_depsgraph_query.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" + +#include "MOD_gpencil_modifiertypes.h" +#include "MOD_gpencil_ui_common.h" +#include "MOD_gpencil_util.h" + +static void initData(GpencilModifierData *md) +{ + WeightAngleGpencilModifierData *gpmd = (WeightAngleGpencilModifierData *)md; + + BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier)); + + MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(WeightAngleGpencilModifierData), modifier); +} + +static void copyData(const GpencilModifierData *md, GpencilModifierData *target) +{ + BKE_gpencil_modifier_copydata_generic(md, target); +} + +/* change stroke thickness */ +static void deformStroke(GpencilModifierData *md, + Depsgraph *UNUSED(depsgraph), + Object *ob, + bGPDlayer *gpl, + bGPDframe *UNUSED(gpf), + bGPDstroke *gps) +{ + WeightAngleGpencilModifierData *mmd = (WeightAngleGpencilModifierData *)md; + const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); + + if (!is_stroke_affected_by_modifier(ob, + mmd->layername, + mmd->material, + mmd->pass_index, + mmd->layer_pass, + 1, + gpl, + gps, + mmd->flag & GP_WEIGHT_INVERT_LAYER, + mmd->flag & GP_WEIGHT_INVERT_PASS, + mmd->flag & GP_WEIGHT_INVERT_LAYERPASS, + mmd->flag & GP_WEIGHT_INVERT_MATERIAL)) { + return; + } + + const int target_def_nr = BKE_object_defgroup_name_index(ob, mmd->target_vgname); + + if (target_def_nr == -1) { + return; + } + + /* Use default Z up. */ + float vec_axis[3] = {0.0f, 0.0f, 1.0f}; + float axis[3] = {0.0f, 0.0f, 0.0f}; + axis[mmd->axis] = 1.0f; + float vec_ref[3]; + /* Apply modifier rotation (sub 90 degrees for Y axis due Z-Up vector). */ + float rot_angle = mmd->angle - ((mmd->axis == 1) ? M_PI_2 : 0.0f); + rotate_normalized_v3_v3v3fl(vec_ref, vec_axis, axis, rot_angle); + + /* Apply the rotation of the object. */ + if (mmd->space == GP_SPACE_LOCAL) { + mul_mat3_m4_v3(ob->obmat, vec_ref); + } + + /* Ensure there is a vertex group. */ + BKE_gpencil_dvert_ensure(gps); + + float weight_pt = 1.0f; + for (int i = 0; i < gps->totpoints; i++) { + MDeformVert *dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + /* Verify point is part of vertex group. */ + float weight = get_modifier_point_weight( + dvert, (mmd->flag & GP_WEIGHT_INVERT_VGROUP) != 0, def_nr); + if (weight < 0.0f) { + continue; + } + + /* Special case for single points. */ + if (gps->totpoints == 1) { + weight_pt = 1.0f; + break; + } + + bGPDspoint *pt1 = (i > 0) ? &gps->points[i] : &gps->points[i + 1]; + bGPDspoint *pt2 = (i > 0) ? &gps->points[i - 1] : &gps->points[i]; + float fpt1[3], fpt2[3]; + mul_v3_m4v3(fpt1, ob->obmat, &pt1->x); + mul_v3_m4v3(fpt2, ob->obmat, &pt2->x); + + float vec[3]; + sub_v3_v3v3(vec, fpt1, fpt2); + float angle = angle_on_axis_v3v3_v3(vec_ref, vec, axis); + /* Use sin to get a value between 0 and 1. */ + weight_pt = 1.0f - sin(angle); + + /* Invert weight if required. */ + if (mmd->flag & GP_WEIGHT_INVERT_OUTPUT) { + weight_pt = 1.0f - weight_pt; + } + /* Assign weight. */ + dvert = gps->dvert != NULL ? &gps->dvert[i] : NULL; + if (dvert != NULL) { + MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr); + if (dw) { + dw->weight = (mmd->flag & GP_WEIGHT_MULTIPLY_DATA) ? dw->weight * weight_pt : weight_pt; + CLAMP(dw->weight, mmd->min_weight, 1.0f); + } + } + } +} + +static void bakeModifier(struct Main *UNUSED(bmain), + Depsgraph *depsgraph, + GpencilModifierData *md, + Object *ob) +{ + bGPdata *gpd = ob->data; + + LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) { + LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) { + deformStroke(md, depsgraph, ob, gpl, gpf, gps); + } + } + } +} + +static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) +{ + WeightAngleGpencilModifierData *mmd = (WeightAngleGpencilModifierData *)md; + + walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); +} + +static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) +{ + WeightAngleGpencilModifierData *mmd = (WeightAngleGpencilModifierData *)md; + + return (mmd->target_vgname[0] == '\0'); +} + +static void panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *row, *sub; + uiLayout *layout = panel->layout; + + PointerRNA ob_ptr; + PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); + + uiLayoutSetPropSep(layout, true); + row = uiLayoutRow(layout, true); + uiItemPointerR(row, ptr, "target_vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); + sub = uiLayoutRow(row, true); + bool has_output = RNA_string_length(ptr, "target_vertex_group") != 0; + uiLayoutSetPropDecorate(sub, false); + uiLayoutSetActive(sub, has_output); + uiItemR(sub, ptr, "use_invert_output", 0, "", ICON_ARROW_LEFTRIGHT); + + uiItemR(layout, ptr, "angle", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "axis", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "space", 0, NULL, ICON_NONE); + + uiItemR(layout, ptr, "minimum_weight", 0, NULL, ICON_NONE); + uiItemR(layout, ptr, "use_multiply", 0, NULL, ICON_NONE); + + gpencil_modifier_panel_end(layout, ptr); +} + +static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + gpencil_modifier_masking_panel_draw(panel, true, true); +} + +static void panelRegister(ARegionType *region_type) +{ + PanelType *panel_type = gpencil_modifier_panel_register( + region_type, eGpencilModifierType_WeightAngle, panel_draw); + + gpencil_modifier_subpanel_register( + region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); +} + +GpencilModifierTypeInfo modifierType_Gpencil_WeightAngle = { + /* name */ "Vertex Weight Angle", + /* structName */ "WeightAngleGpencilModifierData", + /* structSize */ sizeof(WeightAngleGpencilModifierData), + /* type */ eGpencilModifierTypeType_Gpencil, + /* flags */ 0, + + /* copyData */ copyData, + + /* deformStroke */ deformStroke, + /* generateStrokes */ NULL, + /* bakeModifier */ bakeModifier, + /* remapTime */ NULL, + + /* initData */ initData, + /* freeData */ NULL, + /* isDisabled */ isDisabled, + /* updateDepsgraph */ NULL, + /* dependsOnTime */ NULL, + /* foreachIDLink */ foreachIDLink, + /* foreachTexLink */ NULL, + /* panelRegister */ panelRegister, +}; diff --git a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c similarity index 66% rename from source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c rename to source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c index 686023a36d4..4079485de8d 100644 --- a/source/blender/gpencil_modifiers/intern/MOD_gpencilweight.c +++ b/source/blender/gpencil_modifiers/intern/MOD_gpencilweight_proximity.c @@ -58,11 +58,11 @@ static void initData(GpencilModifierData *md) { - WeightGpencilModifierData *gpmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *gpmd = (WeightProxGpencilModifierData *)md; BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(gpmd, modifier)); - MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(WeightGpencilModifierData), modifier); + MEMCPY_STRUCT_AFTER(gpmd, DNA_struct_default_get(WeightProxGpencilModifierData), modifier); } static void copyData(const GpencilModifierData *md, GpencilModifierData *target) @@ -72,7 +72,7 @@ static void copyData(const GpencilModifierData *md, GpencilModifierData *target) /* Calc distance between point and target object. */ static float calc_point_weight_by_distance(Object *ob, - WeightGpencilModifierData *mmd, + WeightProxGpencilModifierData *mmd, const float dist_max, const float dist_min, bGPDspoint *pt) @@ -83,13 +83,13 @@ static float calc_point_weight_by_distance(Object *ob, float dist = len_v3v3(mmd->object->obmat[3], gvert); if (dist > dist_max) { - weight = 0.0f; + weight = 1.0f; } else if (dist <= dist_max && dist > dist_min) { - weight = (dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f); + weight = 1.0f - ((dist_max - dist) / max_ff((dist_max - dist_min), 0.0001f)); } else { - weight = 1.0f; + weight = 0.0f; } return weight; @@ -103,9 +103,8 @@ static void deformStroke(GpencilModifierData *md, bGPDframe *UNUSED(gpf), bGPDstroke *gps) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; const int def_nr = BKE_object_defgroup_name_index(ob, mmd->vgname); - const eWeightGpencilModifierMode mode = mmd->mode; if (!is_stroke_affected_by_modifier(ob, mmd->layername, @@ -130,20 +129,6 @@ static void deformStroke(GpencilModifierData *md, return; } - /* Use default Z up. */ - float vec_axis[3] = {0.0f, 0.0f, 1.0f}; - float axis[3] = {0.0f, 0.0f, 0.0f}; - axis[mmd->axis] = 1.0f; - float vec_ref[3]; - /* Apply modifier rotation (sub 90 degrees for Y axis due Z-Up vector). */ - float rot_angle = mmd->angle - ((mmd->axis == 1) ? M_PI_2 : 0.0f); - rotate_normalized_v3_v3v3fl(vec_ref, vec_axis, axis, rot_angle); - - /* Apply the rotation of the object. */ - if (mmd->space == GP_SPACE_LOCAL) { - mul_mat3_m4_v3(ob->obmat, vec_ref); - } - /* Ensure there is a vertex group. */ BKE_gpencil_dvert_ensure(gps); @@ -157,36 +142,9 @@ static void deformStroke(GpencilModifierData *md, continue; } - switch (mode) { - case GP_WEIGHT_MODE_DISTANCE: { - if (mmd->object) { - bGPDspoint *pt = &gps->points[i]; - weight_pt = calc_point_weight_by_distance(ob, mmd, dist_max, dist_min, pt); - } - break; - } - case GP_WEIGHT_MODE_ANGLE: { - /* Special case for single points. */ - if (gps->totpoints == 1) { - weight_pt = 1.0f; - break; - } - - bGPDspoint *pt1 = (i > 0) ? &gps->points[i] : &gps->points[i + 1]; - bGPDspoint *pt2 = (i > 0) ? &gps->points[i - 1] : &gps->points[i]; - float fpt1[3], fpt2[3]; - mul_v3_m4v3(fpt1, ob->obmat, &pt1->x); - mul_v3_m4v3(fpt2, ob->obmat, &pt2->x); - - float vec[3]; - sub_v3_v3v3(vec, fpt1, fpt2); - float angle = angle_on_axis_v3v3_v3(vec_ref, vec, axis); - /* Use sin to get a value between 0 and 1. */ - weight_pt = 1.0f - sin(angle); - break; - } - default: - break; + if (mmd->object) { + bGPDspoint *pt = &gps->points[i]; + weight_pt = calc_point_weight_by_distance(ob, mmd, dist_max, dist_min, pt); } /* Invert weight if required. */ @@ -198,7 +156,7 @@ static void deformStroke(GpencilModifierData *md, if (dvert != NULL) { MDeformWeight *dw = BKE_defvert_ensure_index(dvert, target_def_nr); if (dw) { - dw->weight = (mmd->flag & GP_WEIGHT_BLEND_DATA) ? dw->weight * weight_pt : weight_pt; + dw->weight = (mmd->flag & GP_WEIGHT_MULTIPLY_DATA) ? dw->weight * weight_pt : weight_pt; CLAMP(dw->weight, mmd->min_weight, 1.0f); } } @@ -223,7 +181,7 @@ static void bakeModifier(struct Main *UNUSED(bmain), static void foreachIDLink(GpencilModifierData *md, Object *ob, IDWalkFunc walk, void *userData) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; walk(userData, ob, (ID **)&mmd->material, IDWALK_CB_USER); walk(userData, ob, (ID **)&mmd->object, IDWALK_CB_NOP); @@ -233,7 +191,7 @@ static void updateDepsgraph(GpencilModifierData *md, const ModifierUpdateDepsgraphContext *ctx, const int UNUSED(mode)) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; if (mmd->object != NULL) { DEG_add_object_relation( ctx->node, mmd->object, DEG_OB_COMP_TRANSFORM, "GPencil Weight Modifier"); @@ -244,54 +202,36 @@ static void updateDepsgraph(GpencilModifierData *md, static bool isDisabled(GpencilModifierData *md, int UNUSED(userRenderParams)) { - WeightGpencilModifierData *mmd = (WeightGpencilModifierData *)md; + WeightProxGpencilModifierData *mmd = (WeightProxGpencilModifierData *)md; - return (mmd->target_vgname[0] == '\0'); + return ((mmd->target_vgname[0] == '\0') || (mmd->object == NULL)); } -static void distance_panel_draw(const bContext *UNUSED(C), Panel *panel) -{ - PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, NULL); - - uiLayout *layout = panel->layout; - uiLayoutSetPropSep(layout, true); - - uiItemR(layout, ptr, "object", 0, NULL, ICON_CUBE); - uiLayout *sub = uiLayoutColumn(layout, true); - uiItemR(sub, ptr, "distance_start", 0, NULL, ICON_NONE); - uiItemR(sub, ptr, "distance_end", 0, "End", ICON_NONE); -} - -static void panel_draw(const bContext *C, Panel *panel) +static void panel_draw(const bContext *UNUSED(C), Panel *panel) { + uiLayout *row, *sub; uiLayout *layout = panel->layout; PointerRNA ob_ptr; PointerRNA *ptr = gpencil_modifier_panel_get_property_pointers(panel, &ob_ptr); uiLayoutSetPropSep(layout, true); - uiItemR(layout, ptr, "mode", 0, NULL, ICON_NONE); + row = uiLayoutRow(layout, true); + uiItemPointerR(row, ptr, "target_vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); + sub = uiLayoutRow(row, true); + bool has_output = RNA_string_length(ptr, "target_vertex_group") != 0; + uiLayoutSetPropDecorate(sub, false); + uiLayoutSetActive(sub, has_output); + uiItemR(sub, ptr, "use_invert_output", 0, "", ICON_ARROW_LEFTRIGHT); - const eWeightGpencilModifierMode mode = RNA_enum_get(ptr, "mode"); + uiItemR(layout, ptr, "object", 0, NULL, ICON_NONE); - uiItemPointerR(layout, ptr, "target_vertex_group", &ob_ptr, "vertex_groups", NULL, ICON_NONE); + sub = uiLayoutColumn(layout, true); + uiItemR(sub, ptr, "distance_start", 0, NULL, ICON_NONE); + uiItemR(sub, ptr, "distance_end", 0, NULL, ICON_NONE); uiItemR(layout, ptr, "minimum_weight", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_invert_output", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "use_blend", 0, NULL, ICON_NONE); - - switch (mode) { - case GP_WEIGHT_MODE_DISTANCE: - distance_panel_draw(C, panel); - break; - case GP_WEIGHT_MODE_ANGLE: - uiItemR(layout, ptr, "angle", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "axis", 0, NULL, ICON_NONE); - uiItemR(layout, ptr, "space", 0, NULL, ICON_NONE); - break; - default: - break; - } + uiItemR(layout, ptr, "use_multiply", 0, NULL, ICON_NONE); gpencil_modifier_panel_end(layout, ptr); } @@ -304,16 +244,16 @@ static void mask_panel_draw(const bContext *UNUSED(C), Panel *panel) static void panelRegister(ARegionType *region_type) { PanelType *panel_type = gpencil_modifier_panel_register( - region_type, eGpencilModifierType_Weight, panel_draw); + region_type, eGpencilModifierType_WeightProximity, panel_draw); gpencil_modifier_subpanel_register( region_type, "mask", "Influence", NULL, mask_panel_draw, panel_type); } -GpencilModifierTypeInfo modifierType_Gpencil_Weight = { - /* name */ "Vertex Weight", - /* structName */ "WeightGpencilModifierData", - /* structSize */ sizeof(WeightGpencilModifierData), +GpencilModifierTypeInfo modifierType_Gpencil_WeightProximity = { + /* name */ "Vertex Weight Proximity", + /* structName */ "WeightProxGpencilModifierData", + /* structSize */ sizeof(WeightProxGpencilModifierData), /* type */ eGpencilModifierTypeType_Gpencil, /* flags */ 0, diff --git a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h index 134d9707ade..d8926a63307 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h +++ b/source/blender/gpencil_modifiers/intern/lineart/MOD_lineart.h @@ -182,9 +182,9 @@ typedef struct LineartEdgeChain { typedef struct LineartEdgeChainItem { struct LineartEdgeChainItem *next, *prev; - /** Need z value for fading */ - float pos[3]; - /** For restoring position to 3d space */ + /** Need z value for fading, w value for image frame clipping. */ + float pos[4]; + /** For restoring position to 3d space. */ float gpos[3]; float normal[3]; unsigned char line_type; @@ -299,6 +299,7 @@ typedef struct LineartRenderBuffer { bool use_loose_as_contour; bool use_loose_edge_chain; bool use_geometry_space_chain; + bool use_image_boundary_trimming; bool filter_face_mark; bool filter_face_mark_invert; @@ -311,12 +312,15 @@ typedef struct LineartRenderBuffer { bool cam_is_persp; float cam_obmat[4][4]; double camera_pos[3]; + double active_camera_pos[3]; /* Stroke offset calculation may use active or selected camera. */ double near_clip, far_clip; float shift_x, shift_y; float crease_threshold; float chaining_image_threshold; float angle_splitting_threshold; + float chain_smooth_tolerance; + /* FIXME(Yiming): Temporary solution for speeding up calculation by not including lines that * are not in the selected source. This will not be needed after we have a proper scene-wise * cache running because multiple modifiers can then select results from that without further @@ -591,14 +595,20 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb); void MOD_lineart_chain_split_for_fixed_occlusion(LineartRenderBuffer *rb); void MOD_lineart_chain_connect(LineartRenderBuffer *rb); void MOD_lineart_chain_discard_short(LineartRenderBuffer *rb, const float threshold); +void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb); void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshold_rad); +void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance); +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera); int MOD_lineart_chain_count(const LineartEdgeChain *ec); void MOD_lineart_chain_clear_picked_flag(LineartCache *lc); bool MOD_lineart_compute_feature_lines(struct Depsgraph *depsgraph, struct LineartGpencilModifierData *lmd, - LineartCache **cached_result); + struct LineartCache **cached_result, + bool enable_stroke_offset); struct Scene; diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c index d86253e7fe0..f3110cf87b6 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_chain.c @@ -126,7 +126,7 @@ static LineartEdgeChainItem *lineart_chain_append_point(LineartRenderBuffer *rb, eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(eci->pos, fbcoord); + copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); @@ -156,7 +156,7 @@ static LineartEdgeChainItem *lineart_chain_prepend_point(LineartRenderBuffer *rb eci = lineart_mem_acquire(rb->chain_data_pool, sizeof(LineartEdgeChainItem)); - copy_v2_v2(eci->pos, fbcoord); + copy_v4_v4(eci->pos, fbcoord); copy_v3_v3(eci->gpos, gpos); eci->index = index; copy_v3_v3(eci->normal, normal); @@ -177,15 +177,15 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) int last_occlusion; unsigned char last_transparency; /* Used when converting from double. */ - float use_fbcoord[2]; + float use_fbcoord[4]; float use_gpos[3]; #define VERT_COORD_TO_FLOAT(a) \ - copy_v2fl_v2db(use_fbcoord, (a)->fbcoord); \ + copy_v4fl_v4db(use_fbcoord, (a)->fbcoord); \ copy_v3fl_v3db(use_gpos, (a)->gloc); #define POS_TO_FLOAT(lpos, gpos) \ - copy_v2fl_v2db(use_fbcoord, lpos); \ + copy_v3fl_v3db(use_fbcoord, lpos); \ copy_v3fl_v3db(use_gpos, gpos); LRT_ITER_ALL_LINES_BEGIN @@ -262,6 +262,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, ec, @@ -287,6 +288,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_prepend_point(rb, ec, @@ -340,6 +342,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, e->v1->fbcoord, e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, e->v1->gloc, e->v2->gloc, global_at); + use_fbcoord[3] = interpf(e->v2->fbcoord[3], e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, ec, @@ -403,6 +406,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); last_occlusion = es->prev ? es->prev->occlusion : last_occlusion; last_transparency = es->prev ? es->prev->material_mask_bits : last_transparency; POS_TO_FLOAT(lpos, gpos) @@ -430,6 +434,7 @@ void MOD_lineart_chain_feature_lines(LineartRenderBuffer *rb) double global_at = lfb[3] * es->at / (es->at * lfb[3] + (1 - es->at) * rfb[3]); interp_v3_v3v3_db(lpos, new_e->v1->fbcoord, new_e->v2->fbcoord, es->at); interp_v3_v3v3_db(gpos, new_e->v1->gloc, new_e->v2->gloc, global_at); + use_fbcoord[3] = interpf(new_e->v2->fbcoord[3], new_e->v1->fbcoord[3], global_at); POS_TO_FLOAT(lpos, gpos) lineart_chain_append_point(rb, ec, @@ -924,6 +929,144 @@ void MOD_lineart_chain_clear_picked_flag(LineartCache *lc) } } +void MOD_lineart_smooth_chains(LineartRenderBuffer *rb, float tolerance) +{ + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LineartEdgeChainItem *next_eci; + for (LineartEdgeChainItem *eci = ec->chain.first; eci; eci = next_eci) { + next_eci = eci->next; + LineartEdgeChainItem *eci2, *eci3, *eci4; + + /* Not enough point to do simplify. */ + if ((!(eci2 = eci->next)) || (!(eci3 = eci2->next))) { + continue; + } + + /* No need to care for different line types/occlusion and so on, because at this stage they + * are all the same within a chain. */ + + /* If p3 is within the p1-p2 segment of a width of "tolerance" */ + if (dist_to_line_segment_v2(eci3->pos, eci->pos, eci2->pos) < tolerance) { + /* And if p4 is on the extension of p1-p2 , we remove p3. */ + if ((eci4 = eci3->next) && (dist_to_line_v2(eci4->pos, eci->pos, eci2->pos) < tolerance)) { + BLI_remlink(&ec->chain, eci3); + next_eci = eci; + } + } + } + } +} + +static LineartEdgeChainItem *lineart_chain_create_crossing_point(LineartRenderBuffer *rb, + LineartEdgeChainItem *eci_inside, + LineartEdgeChainItem *eci_outside) +{ + float isec[2]; + float LU[2] = {-1.0f, 1.0f}, LB[2] = {-1.0f, -1.0f}, RU[2] = {1.0f, 1.0f}, RB[2] = {1.0f, -1.0f}; + bool found = false; + LineartEdgeChainItem *eci2 = eci_outside, *eci1 = eci_inside; + if (eci2->pos[0] < -1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, LB, isec) > 0); + } + if (!found && eci2->pos[0] > 1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, RU, RB, isec) > 0); + } + if (!found && eci2->pos[1] < -1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LB, RB, isec) > 0); + } + if (!found && eci2->pos[1] > 1.0f) { + found = (isect_seg_seg_v2_point(eci1->pos, eci2->pos, LU, RU, isec) > 0); + } + + if (UNLIKELY(!found)) { + return NULL; + } + + float ratio = (fabs(eci2->pos[0] - eci1->pos[0]) > fabs(eci2->pos[1] - eci1->pos[1])) ? + ratiof(eci1->pos[0], eci2->pos[0], isec[0]) : + ratiof(eci1->pos[1], eci2->pos[1], isec[1]); + float gratio = eci1->pos[3] * ratio / (ratio * eci1->pos[3] + (1 - ratio) * eci2->pos[3]); + + LineartEdgeChainItem *eci = lineart_mem_acquire(rb->chain_data_pool, + sizeof(LineartEdgeChainItem)); + memcpy(eci, eci1, sizeof(LineartEdgeChainItem)); + interp_v3_v3v3(eci->gpos, eci1->gpos, eci2->gpos, gratio); + interp_v3_v3v3(eci->pos, eci1->pos, eci2->pos, ratio); + eci->pos[3] = interpf(eci2->pos[3], eci1->pos[3], gratio); + eci->next = eci->prev = NULL; + return eci; +} + +#define LRT_ECI_INSIDE(eci) \ + ((eci)->pos[0] >= -1.0f && (eci)->pos[0] <= 1.0f && (eci)->pos[1] >= -1.0f && \ + (eci)->pos[1] <= 1.0f) + +void MOD_lineart_chain_clip_at_border(LineartRenderBuffer *rb) +{ + LineartEdgeChain *ec; + LineartEdgeChainItem *eci, *next_eci, *prev_eci, *new_eci; + bool is_inside, new_inside; + ListBase swap = {0}; + swap.first = rb->chains.first; + swap.last = rb->chains.last; + + rb->chains.last = rb->chains.first = NULL; + while ((ec = BLI_pophead(&swap)) != NULL) { + bool ec_added = false; + LineartEdgeChainItem *first_eci = (LineartEdgeChainItem *)ec->chain.first; + is_inside = LRT_ECI_INSIDE(first_eci) ? true : false; + if (!is_inside) { + ec->picked = true; + } + for (eci = first_eci->next; eci; eci = next_eci) { + next_eci = eci->next; + prev_eci = eci->prev; + + /* We only need to do something if the edge crossed from outside to the inside or from inside + * to the outside. */ + if ((new_inside = LRT_ECI_INSIDE(eci)) != is_inside) { + if (new_inside == false) { + /* Stroke goes out. */ + new_eci = lineart_chain_create_crossing_point(rb, prev_eci, eci); + + LineartEdgeChain *new_ec = lineart_mem_acquire(rb->chain_data_pool, + sizeof(LineartEdgeChain)); + memcpy(new_ec, ec, sizeof(LineartEdgeChain)); + new_ec->chain.first = next_eci; + eci->prev = NULL; + prev_eci->next = NULL; + ec->chain.last = prev_eci; + BLI_addtail(&ec->chain, new_eci); + BLI_addtail(&rb->chains, ec); + ec_added = true; + ec = new_ec; + + next_eci = eci->next; + is_inside = new_inside; + continue; + } + /* Stroke comes in. */ + new_eci = lineart_chain_create_crossing_point(rb, eci, prev_eci); + + ec->chain.first = eci; + eci->prev = NULL; + + BLI_addhead(&ec->chain, new_eci); + + ec_added = false; + + next_eci = eci->next; + is_inside = new_inside; + continue; + } + } + + if ((!ec_added) && is_inside) { + BLI_addtail(&rb->chains, ec); + } + } +} + /** * This should always be the last stage!, see the end of * #MOD_lineart_chain_split_for_fixed_occlusion(). @@ -980,3 +1123,45 @@ void MOD_lineart_chain_split_angle(LineartRenderBuffer *rb, float angle_threshol } } } + +void MOD_lineart_chain_offset_towards_camera(LineartRenderBuffer *rb, + float dist, + bool use_custom_camera) +{ + float dir[3]; + float cam[3]; + float view[3]; + float view_clamp[3]; + copy_v3fl_v3db(cam, rb->camera_pos); + copy_v3fl_v3db(view, rb->view_vector); + + if (use_custom_camera) { + copy_v3fl_v3db(cam, rb->camera_pos); + } + else { + copy_v3fl_v3db(cam, rb->active_camera_pos); + } + + if (rb->cam_is_persp) { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + sub_v3_v3v3(dir, cam, eci->gpos); + float orig_len = len_v3(dir); + normalize_v3(dir); + mul_v3_fl(dir, MIN2(dist, orig_len - rb->near_clip)); + add_v3_v3(eci->gpos, dir); + } + } + } + else { + LISTBASE_FOREACH (LineartEdgeChain *, ec, &rb->chains) { + LISTBASE_FOREACH (LineartEdgeChainItem *, eci, &ec->chain) { + sub_v3_v3v3(dir, cam, eci->gpos); + float len_lim = dot_v3v3(view, dir) - rb->near_clip; + normalize_v3_v3(view_clamp, view); + mul_v3_fl(view_clamp, MIN2(dist, len_lim)); + add_v3_v3(eci->gpos, view_clamp); + } + } + } +} diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c index 725cc0741f0..93e9062e910 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.c @@ -1830,7 +1830,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu if (usage == OBJECT_LRT_INTERSECTION_ONLY) { tri->flags |= LRT_TRIANGLE_INTERSECTION_ONLY; } - else if (usage == OBJECT_LRT_NO_INTERSECTION || usage == OBJECT_LRT_OCCLUSION_ONLY) { + else if (ELEM(usage, OBJECT_LRT_NO_INTERSECTION, OBJECT_LRT_OCCLUSION_ONLY)) { tri->flags |= LRT_TRIANGLE_NO_INTERSECTION; } @@ -1917,8 +1917,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *obi, LineartRenderBu la_e->flags = use_type; la_e->object_ref = orig_ob; BLI_addtail(&la_e->segments, la_s); - if (usage == OBJECT_LRT_INHERIT || usage == OBJECT_LRT_INCLUDE || - usage == OBJECT_LRT_NO_INTERSECTION) { + if (ELEM(usage, OBJECT_LRT_INHERIT, OBJECT_LRT_INCLUDE, OBJECT_LRT_NO_INTERSECTION)) { lineart_add_edge_to_list_thread(obi, la_e); } @@ -2168,6 +2167,12 @@ static void lineart_main_load_geometries( use_mesh = use_ob->data; } else { + /* If DEG_ITER_OBJECT_FLAG_DUPLI is set, the curve objects are going to have a mesh + * equivalent already in the object list, so ignore converting the original curve in this + * case. */ + if (allow_duplicates) { + continue; + } use_mesh = BKE_mesh_new_from_object(depsgraph, use_ob, true, true); } @@ -2993,7 +2998,7 @@ void MOD_lineart_destroy_render_data(LineartGpencilModifierData *lmd) } } -static LineartCache *lineart_init_cache() +static LineartCache *lineart_init_cache(void) { LineartCache *lc = MEM_callocN(sizeof(LineartCache), "Lineart Cache"); return lc; @@ -3011,6 +3016,8 @@ void MOD_lineart_clear_cache(struct LineartCache **lc) static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, LineartGpencilModifierData *lmd, + Object *camera, + Object *active_camera, LineartCache *lc) { LineartRenderBuffer *rb = MEM_callocN(sizeof(LineartRenderBuffer), "Line Art render buffer"); @@ -3019,10 +3026,10 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, lmd->render_buffer_ptr = rb; lc->rb_edge_types = lmd->edge_types_override; - if (!scene || !scene->camera || !lc) { + if (!scene || !camera || !lc) { return NULL; } - Camera *c = scene->camera->data; + Camera *c = camera->data; double clipping_offset = 0; if (lmd->calculation_flags & LRT_ALLOW_CLIPPING_BOUNDARIES) { @@ -3030,8 +3037,11 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, clipping_offset = 0.0001; } - copy_v3db_v3fl(rb->camera_pos, scene->camera->obmat[3]); - copy_m4_m4(rb->cam_obmat, scene->camera->obmat); + copy_v3db_v3fl(rb->camera_pos, camera->obmat[3]); + if (active_camera) { + copy_v3db_v3fl(rb->active_camera_pos, active_camera->obmat[3]); + } + copy_m4_m4(rb->cam_obmat, camera->obmat); rb->cam_is_persp = (c->type == CAM_PERSP); rb->near_clip = c->clip_start + clipping_offset; rb->far_clip = c->clip_end - clipping_offset; @@ -3056,8 +3066,9 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->shift_y /= (1 + rb->overscan); rb->crease_threshold = cos(M_PI - lmd->crease_threshold); - rb->angle_splitting_threshold = lmd->angle_splitting_threshold; rb->chaining_image_threshold = lmd->chaining_image_threshold; + rb->angle_splitting_threshold = lmd->angle_splitting_threshold; + rb->chain_smooth_tolerance = lmd->chain_smooth_tolerance; rb->fuzzy_intersections = (lmd->calculation_flags & LRT_INTERSECTION_AS_CONTOUR) != 0; rb->fuzzy_everything = (lmd->calculation_flags & LRT_EVERYTHING_AS_CONTOUR) != 0; @@ -3066,6 +3077,8 @@ static LineartRenderBuffer *lineart_create_render_buffer(Scene *scene, rb->use_loose_as_contour = (lmd->calculation_flags & LRT_LOOSE_AS_CONTOUR) != 0; rb->use_loose_edge_chain = (lmd->calculation_flags & LRT_CHAIN_LOOSE_EDGES) != 0; rb->use_geometry_space_chain = (lmd->calculation_flags & LRT_CHAIN_GEOMETRY_SPACE) != 0; + rb->use_image_boundary_trimming = (lmd->calculation_flags & LRT_USE_IMAGE_BOUNDARY_TRIMMING) != + 0; /* See lineart_edge_from_triangle() for how this option may impact performance. */ rb->allow_overlapping_edges = (lmd->calculation_flags & LRT_ALLOW_OVERLAPPING_EDGES) != 0; @@ -4070,11 +4083,13 @@ static LineartBoundingArea *lineart_bounding_area_next(LineartBoundingArea *this */ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, LineartGpencilModifierData *lmd, - LineartCache **cached_result) + LineartCache **cached_result, + bool enable_stroke_depth_offset) { LineartRenderBuffer *rb; Scene *scene = DEG_get_evaluated_scene(depsgraph); int intersections_only = 0; /* Not used right now, but preserve for future. */ + Object *use_camera; double t_start; @@ -4084,14 +4099,24 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, BKE_scene_camera_switch_update(scene); - if (!scene->camera) { - return false; + if (lmd->calculation_flags & LRT_USE_CUSTOM_CAMERA) { + if (!lmd->source_camera || + (use_camera = DEG_get_evaluated_object(depsgraph, lmd->source_camera))->type != + OB_CAMERA) { + return false; + } + } + else { + if (!scene->camera) { + return false; + } + use_camera = scene->camera; } LineartCache *lc = lineart_init_cache(); *cached_result = lc; - rb = lineart_create_render_buffer(scene, lmd, lc); + rb = lineart_create_render_buffer(scene, lmd, use_camera, scene->camera, lc); /* Triangle thread testing data size varies depending on the thread count. * See definition of LineartTriangleThread for details. */ @@ -4111,7 +4136,7 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* Get view vector before loading geometries, because we detect feature lines there. */ lineart_main_get_view_vector(rb); lineart_main_load_geometries( - depsgraph, scene, scene->camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); + depsgraph, scene, use_camera, rb, lmd->calculation_flags & LRT_ALLOW_DUPLI_OBJECTS); if (!rb->vertex_buffer_pointers.first) { /* No geometry loaded, return early. */ @@ -4172,10 +4197,26 @@ bool MOD_lineart_compute_feature_lines(Depsgraph *depsgraph, /* This configuration ensures there won't be accidental lost of short unchained segments. */ MOD_lineart_chain_discard_short(rb, MIN2(*t_image, 0.001f) - FLT_EPSILON); + if (rb->chain_smooth_tolerance > FLT_EPSILON) { + /* Keeping UI range of 0-1 for ease of read while scaling down the actual value for best + * effective range in image-space (Coordinate only goes from -1 to 1). This value is somewhat + * arbitrary, but works best for the moment. */ + MOD_lineart_smooth_chains(rb, rb->chain_smooth_tolerance / 50); + } + + if (rb->use_image_boundary_trimming) { + MOD_lineart_chain_clip_at_border(rb); + } + if (rb->angle_splitting_threshold > FLT_EPSILON) { MOD_lineart_chain_split_angle(rb, rb->angle_splitting_threshold); } + if (enable_stroke_depth_offset && lmd->stroke_depth_offset > FLT_EPSILON) { + MOD_lineart_chain_offset_towards_camera( + rb, lmd->stroke_depth_offset, lmd->flags & LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + } + /* Finally transfer the result list into cache. */ memcpy(&lc->chains, &rb->chains, sizeof(ListBase)); diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c index 988c90483a6..b74499daf6b 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_ops.c @@ -118,12 +118,12 @@ static bool bake_strokes(Object *ob, } LineartCache *local_lc = *lc; if (!(*lc)) { - MOD_lineart_compute_feature_lines(dg, lmd, lc); + MOD_lineart_compute_feature_lines(dg, lmd, lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } else { if (is_first || (!(lmd->flags & LRT_GPENCIL_USE_CACHE))) { - MOD_lineart_compute_feature_lines(dg, lmd, &local_lc); + MOD_lineart_compute_feature_lines(dg, lmd, &local_lc, (!(ob->dtx & OB_DRAW_IN_FRONT))); MOD_lineart_destroy_render_data(lmd); } MOD_lineart_chain_clear_picked_flag(local_lc); diff --git a/source/blender/gpu/CMakeLists.txt b/source/blender/gpu/CMakeLists.txt index b7dc3210c41..7a072900473 100644 --- a/source/blender/gpu/CMakeLists.txt +++ b/source/blender/gpu/CMakeLists.txt @@ -268,8 +268,8 @@ data_to_c_simple(shaders/gpu_shader_2D_edituvs_stretch_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_text_vert.glsl SRC) data_to_c_simple(shaders/gpu_shader_text_frag.glsl SRC) -data_to_c_simple(shaders/gpu_shader_keyframe_diamond_vert.glsl SRC) -data_to_c_simple(shaders/gpu_shader_keyframe_diamond_frag.glsl SRC) +data_to_c_simple(shaders/gpu_shader_keyframe_shape_vert.glsl SRC) +data_to_c_simple(shaders/gpu_shader_keyframe_shape_frag.glsl SRC) data_to_c_simple(shaders/gpu_shader_codegen_lib.glsl SRC) @@ -296,6 +296,7 @@ data_to_c_simple(shaders/material/gpu_shader_material_diffuse.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_displacement.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_eevee_specular.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_emission.glsl SRC) +data_to_c_simple(shaders/material/gpu_shader_material_float_curve.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_fractal_noise.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_fresnel.glsl SRC) data_to_c_simple(shaders/material/gpu_shader_material_gamma.glsl SRC) diff --git a/source/blender/gpu/GPU_batch.h b/source/blender/gpu/GPU_batch.h index 018e192bf37..911c8cc2e42 100644 --- a/source/blender/gpu/GPU_batch.h +++ b/source/blender/gpu/GPU_batch.h @@ -32,7 +32,7 @@ #include "GPU_shader.h" #include "GPU_vertex_buffer.h" -#define GPU_BATCH_VBO_MAX_LEN 6 +#define GPU_BATCH_VBO_MAX_LEN 16 #define GPU_BATCH_INST_VBO_MAX_LEN 2 #define GPU_BATCH_VAO_STATIC_LEN 3 #define GPU_BATCH_VAO_DYN_ALLOC_COUNT 16 @@ -54,11 +54,11 @@ typedef enum eGPUBatchFlag { GPU_BATCH_OWNS_INDEX = (GPU_BATCH_OWNS_INST_VBO_MAX << 1), /** Has been initialized. At least one VBO is set. */ - GPU_BATCH_INIT = (1 << 16), + GPU_BATCH_INIT = (1 << 26), /** Batch is initialized but its VBOs are still being populated. (optional) */ - GPU_BATCH_BUILDING = (1 << 16), + GPU_BATCH_BUILDING = (1 << 26), /** Cached data need to be rebuild. (VAO, PSO, ...) */ - GPU_BATCH_DIRTY = (1 << 17), + GPU_BATCH_DIRTY = (1 << 27), } eGPUBatchFlag; #define GPU_BATCH_OWNS_NONE GPU_BATCH_INVALID diff --git a/source/blender/gpu/GPU_immediate_util.h b/source/blender/gpu/GPU_immediate_util.h index 3ea809d59a7..0d3d39839b2 100644 --- a/source/blender/gpu/GPU_immediate_util.h +++ b/source/blender/gpu/GPU_immediate_util.h @@ -78,8 +78,12 @@ void imm_draw_box_checker_2d_ex(float x1, int checker_size); void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2); -void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]); -void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]); +void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]); +void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]); +void imm_draw_cube_corners_3d(uint pos, + const float center[3], + const float aspect[3], + const float factor); void imm_draw_cylinder_fill_normal_3d( uint pos, uint nor, float base, float top, float height, int slices, int stacks); diff --git a/source/blender/gpu/GPU_index_buffer.h b/source/blender/gpu/GPU_index_buffer.h index 197077cf76c..e4f1709173e 100644 --- a/source/blender/gpu/GPU_index_buffer.h +++ b/source/blender/gpu/GPU_index_buffer.h @@ -56,7 +56,7 @@ GPUIndexBuf *GPU_indexbuf_build_on_device(uint index_len); /* * Thread safe. * - * Function inspired by the reduction directives of multithread work APIs.. + * Function inspired by the reduction directives of multi-thread work API's. */ void GPU_indexbuf_join(GPUIndexBufBuilder *builder, const GPUIndexBufBuilder *builder_from); diff --git a/source/blender/gpu/GPU_material.h b/source/blender/gpu/GPU_material.h index 312da491a36..e64521768f9 100644 --- a/source/blender/gpu/GPU_material.h +++ b/source/blender/gpu/GPU_material.h @@ -175,10 +175,7 @@ GPUNodeLink *GPU_uniformbuf_link_out(struct GPUMaterial *mat, void GPU_material_output_link(GPUMaterial *material, GPUNodeLink *link); void GPU_material_add_output_link_aov(GPUMaterial *material, GPUNodeLink *link, int hash); -void GPU_material_sss_profile_create(GPUMaterial *material, - float radii[3], - const short *falloff_type, - const float *sharpness); +void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]); struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, int sample_len, struct GPUTexture **tex_profile); diff --git a/source/blender/gpu/GPU_shader.h b/source/blender/gpu/GPU_shader.h index 62b748b7edf..c6cfac79699 100644 --- a/source/blender/gpu/GPU_shader.h +++ b/source/blender/gpu/GPU_shader.h @@ -169,7 +169,7 @@ void GPU_shader_set_framebuffer_srgb_target(int use_srgb_to_linear); typedef enum eGPUBuiltinShader { /* specialized drawing */ GPU_SHADER_TEXT, - GPU_SHADER_KEYFRAME_DIAMOND, + GPU_SHADER_KEYFRAME_SHAPE, GPU_SHADER_SIMPLE_LIGHTING, /* for simple 2D drawing */ /** @@ -423,6 +423,19 @@ void GPU_shader_free_builtin_shaders(void); /* Determined by the maximum uniform buffer size divided by chunk size. */ #define GPU_MAX_UNIFORM_ATTR 8 +typedef enum eGPUKeyframeShapes { + GPU_KEYFRAME_SHAPE_DIAMOND = (1 << 0), + GPU_KEYFRAME_SHAPE_CIRCLE = (1 << 1), + GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL = (1 << 2), + GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL = (1 << 3), + GPU_KEYFRAME_SHAPE_INNER_DOT = (1 << 4), + GPU_KEYFRAME_SHAPE_ARROW_END_MAX = (1 << 8), + GPU_KEYFRAME_SHAPE_ARROW_END_MIN = (1 << 9), + GPU_KEYFRAME_SHAPE_ARROW_END_MIXED = (1 << 10), +} eGPUKeyframeShapes; +#define GPU_KEYFRAME_SHAPE_SQUARE \ + (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL) + #ifdef __cplusplus } #endif diff --git a/source/blender/gpu/GPU_state.h b/source/blender/gpu/GPU_state.h index a338728804c..1c1211d2a3e 100644 --- a/source/blender/gpu/GPU_state.h +++ b/source/blender/gpu/GPU_state.h @@ -42,7 +42,7 @@ typedef enum eGPUBarrier { GPU_BARRIER_SHADER_STORAGE = (1 << 2), } eGPUBarrier; -ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_TEXTURE_FETCH) +ENUM_OPERATORS(eGPUBarrier, GPU_BARRIER_SHADER_STORAGE) /** * Defines the fixed pipeline blending equation. diff --git a/source/blender/gpu/GPU_texture.h b/source/blender/gpu/GPU_texture.h index 9a1885160b6..deff9e47871 100644 --- a/source/blender/gpu/GPU_texture.h +++ b/source/blender/gpu/GPU_texture.h @@ -52,6 +52,14 @@ typedef enum eGPUSamplerState { GPU_SAMPLER_REPEAT = (GPU_SAMPLER_REPEAT_S | GPU_SAMPLER_REPEAT_T | GPU_SAMPLER_REPEAT_R), } eGPUSamplerState; +#define GPU_TEXTURE_FREE_SAFE(texture) \ + do { \ + if (texture != NULL) { \ + GPU_texture_free(texture); \ + texture = NULL; \ + } \ + } while (0) + /* `GPU_SAMPLER_MAX` is not a valid enum value, but only a limit. * It also creates a bad mask for the `NOT` operator in `ENUM_OPERATORS`. */ diff --git a/source/blender/gpu/GPU_viewport.h b/source/blender/gpu/GPU_viewport.h index 0ecd5f3eb7b..4d9970dac90 100644 --- a/source/blender/gpu/GPU_viewport.h +++ b/source/blender/gpu/GPU_viewport.h @@ -35,76 +35,15 @@ extern "C" { #endif -#define GPU_INFO_SIZE 512 /* IMA_MAX_RENDER_TEXT */ #define GLA_PIXEL_OFS 0.375f typedef struct GHash GHash; typedef struct GPUViewport GPUViewport; struct GPUFrameBuffer; - -/* Contains memory pools information. */ -typedef struct ViewportMemoryPool { - struct BLI_memblock *commands; - struct BLI_memblock *commands_small; - struct BLI_memblock *callbuffers; - struct BLI_memblock *obmats; - struct BLI_memblock *obinfos; - struct BLI_memblock *cullstates; - struct BLI_memblock *shgroups; - struct BLI_memblock *uniforms; - struct BLI_memblock *views; - struct BLI_memblock *passes; - struct BLI_memblock *images; - struct GPUUniformBuf **matrices_ubo; - struct GPUUniformBuf **obinfos_ubo; - struct GHash *obattrs_ubo_pool; - uint ubo_len; -} ViewportMemoryPool; - -/* All FramebufferLists are just the same pointers with different names. */ -typedef struct FramebufferList { - struct GPUFrameBuffer *framebuffers[1]; -} FramebufferList; - -typedef struct TextureList { - struct GPUTexture *textures[1]; -} TextureList; - -typedef struct PassList { - struct DRWPass *passes[1]; -} PassList; - -typedef struct StorageList { - void *storage[1]; /* Custom structs from the engine. */ -} StorageList; - -typedef struct ViewportEngineData { - void *engine_type; - - FramebufferList *fbl; - TextureList *txl; - PassList *psl; - StorageList *stl; - char info[GPU_INFO_SIZE]; - - TextureList *txl_stereo; - StorageList *stl_stereo; - /* We may want to put this elsewhere. */ - struct DRWTextStore *text_draw_cache; - - /* Profiling data. */ - double init_time; - double render_time; - double background_time; -} ViewportEngineData; - -typedef struct ViewportEngineData_Info { - int fbl_len; - int txl_len; - int psl_len; - int stl_len; -} ViewportEngineData_Info; +struct DefaultFramebufferList; +struct DefaultTextureList; +struct DRWData; GPUViewport *GPU_viewport_create(void); GPUViewport *GPU_viewport_stereo_create(void); @@ -129,35 +68,21 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, bool display_colorspace, bool do_overlay_merge); -ViewportMemoryPool *GPU_viewport_mempool_get(GPUViewport *viewport); -struct DRWInstanceDataList *GPU_viewport_instance_data_list_get(GPUViewport *viewport); +struct DRWData **GPU_viewport_data_get(GPUViewport *viewport); -void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type); -void *GPU_viewport_engine_data_get(GPUViewport *viewport, void *engine_handle); -void *GPU_viewport_framebuffer_list_get(GPUViewport *viewport); void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo_format); -void *GPU_viewport_texture_list_get(GPUViewport *viewport); -void GPU_viewport_size_get(const GPUViewport *viewport, int size[2]); -void GPU_viewport_size_set(GPUViewport *viewport, const int size[2]); -void GPU_viewport_active_view_set(GPUViewport *viewport, int view); - -/* Profiling. */ -double *GPU_viewport_cache_time_get(GPUViewport *viewport); void GPU_viewport_tag_update(GPUViewport *viewport); bool GPU_viewport_do_update(GPUViewport *viewport); +int GPU_viewport_active_view_get(GPUViewport *viewport); +bool GPU_viewport_is_stereo_get(GPUViewport *viewport); + GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport, int view); +GPUTexture *GPU_viewport_overlay_texture(GPUViewport *viewport, int view); +GPUTexture *GPU_viewport_depth_texture(GPUViewport *viewport); -/* Texture pool. */ -GPUTexture *GPU_viewport_texture_pool_query( - GPUViewport *viewport, void *engine, int width, int height, int format); - -bool GPU_viewport_engines_data_validate(GPUViewport *viewport, void **engine_handle_array); -void GPU_viewport_cache_release(GPUViewport *viewport); - -struct GPUFrameBuffer *GPU_viewport_framebuffer_default_get(GPUViewport *viewport); -struct GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport); +GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport); #ifdef __cplusplus } diff --git a/source/blender/gpu/intern/gpu_codegen.c b/source/blender/gpu/intern/gpu_codegen.c index bb1ebc0e85d..f0046e879a0 100644 --- a/source/blender/gpu/intern/gpu_codegen.c +++ b/source/blender/gpu/intern/gpu_codegen.c @@ -656,6 +656,8 @@ static const char *attr_prefix_get(CustomDataType type) return "c"; case CD_AUTO_FROM_NAME: return "a"; + case CD_HAIRLENGTH: + return "hl"; default: BLI_assert_msg(0, "GPUVertAttr Prefix type not found : This should not happen!"); return ""; @@ -675,7 +677,12 @@ static char *code_generate_interface(GPUNodeGraph *graph, int builtins) BLI_dynstr_append(ds, "\n"); LISTBASE_FOREACH (GPUMaterialAttribute *, attr, &graph->attributes) { - BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + if (attr->type == CD_HAIRLENGTH) { + BLI_dynstr_appendf(ds, "float var%d;\n", attr->id); + } + else { + BLI_dynstr_appendf(ds, "%s var%d;\n", gpu_data_type_to_string(attr->gputype), attr->id); + } } if (builtins & GPU_BARYCENTRIC_TEXCO) { BLI_dynstr_append(ds, "vec2 barycentricTexCo;\n"); @@ -711,6 +718,10 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); BLI_dynstr_append(ds, "DEFINE_ATTR(vec4, orco);\n"); } + else if (attr->type == CD_HAIRLENGTH) { + BLI_dynstr_append(ds, datatoc_gpu_shader_common_obinfos_lib_glsl); + BLI_dynstr_append(ds, "DEFINE_ATTR(float, hairLen);\n"); + } else if (attr->name[0] == '\0') { BLI_dynstr_appendf(ds, "DEFINE_ATTR(%s, %s);\n", type_str, prefix); BLI_dynstr_appendf(ds, "#define att%d %s\n", attr->id, prefix); @@ -755,6 +766,9 @@ static char *code_generate_vertex(GPUNodeGraph *graph, BLI_dynstr_appendf( ds, " var%d = orco_get(position, modelmatinv, OrcoTexCoFactors, orco);\n", attr->id); } + else if (attr->type == CD_HAIRLENGTH) { + BLI_dynstr_appendf(ds, " var%d = hair_len_get(hair_get_strand_id(), hairLen);\n", attr->id); + } else { const char *type_str = gpu_data_type_to_string(attr->gputype); BLI_dynstr_appendf(ds, " var%d = GET_ATTR(%s, att%d);\n", attr->id, type_str, attr->id); diff --git a/source/blender/gpu/intern/gpu_immediate_util.c b/source/blender/gpu/intern/gpu_immediate_util.c index d18dc862ce7..032974db8d1 100644 --- a/source/blender/gpu/intern/gpu_immediate_util.c +++ b/source/blender/gpu/intern/gpu_immediate_util.c @@ -391,12 +391,12 @@ void imm_draw_box_checker_2d(float x1, float y1, float x2, float y2) imm_draw_box_checker_2d_ex(x1, y1, x2, y2, checker_primary, checker_secondary, checker_size); } -void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) +void imm_draw_cube_fill_3d(uint pos, const float center[3], const float aspect[3]) { float coords[ARRAY_SIZE(cube_coords)][3]; for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { - madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); } immBegin(GPU_PRIM_TRIS, ARRAY_SIZE(cube_quad_index) * 3 * 2); @@ -412,12 +412,12 @@ void imm_draw_cube_fill_3d(uint pos, const float co[3], const float aspect[3]) immEnd(); } -void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) +void imm_draw_cube_wire_3d(uint pos, const float center[3], const float aspect[3]) { float coords[ARRAY_SIZE(cube_coords)][3]; for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { - madd_v3_v3v3v3(coords[i], co, cube_coords[i], aspect); + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); } immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 2); @@ -428,6 +428,36 @@ void imm_draw_cube_wire_3d(uint pos, const float co[3], const float aspect[3]) immEnd(); } +void imm_draw_cube_corners_3d(uint pos, + const float center[3], + const float aspect[3], + const float factor) +{ + float coords[ARRAY_SIZE(cube_coords)][3]; + + for (int i = 0; i < ARRAY_SIZE(cube_coords); i++) { + madd_v3_v3v3v3(coords[i], center, cube_coords[i], aspect); + } + + immBegin(GPU_PRIM_LINES, ARRAY_SIZE(cube_line_index) * 4); + for (int i = 0; i < ARRAY_SIZE(cube_line_index); i++) { + float vec[3], co[3]; + sub_v3_v3v3(vec, coords[cube_line_index[i][1]], coords[cube_line_index[i][0]]); + mul_v3_fl(vec, factor); + + copy_v3_v3(co, coords[cube_line_index[i][0]]); + immVertex3fv(pos, co); + add_v3_v3(co, vec); + immVertex3fv(pos, co); + + copy_v3_v3(co, coords[cube_line_index[i][1]]); + immVertex3fv(pos, co); + sub_v3_v3(co, vec); + immVertex3fv(pos, co); + } + immEnd(); +} + /** * Draw a cylinder. Replacement for gluCylinder. * _warning_ : Slow, better use it only if you no other choices. diff --git a/source/blender/gpu/intern/gpu_material.c b/source/blender/gpu/intern/gpu_material.c index 56e72fbeca9..6872a08e854 100644 --- a/source/blender/gpu/intern/gpu_material.c +++ b/source/blender/gpu/intern/gpu_material.c @@ -96,8 +96,6 @@ struct GPUMaterial { float sss_enabled; float sss_radii[3]; int sss_samples; - short int sss_falloff; - float sss_sharpness; bool sss_dirty; GPUTexture *coba_tex; /* 1D Texture array containing all color bands. */ @@ -266,18 +264,6 @@ static void sss_calculate_offsets(GPUSssKernelData *kd, int count, float exponen } } -#define GAUSS_TRUNCATE 12.46f -static float gaussian_profile(float r, float radius) -{ - const float v = radius * radius * (0.25f * 0.25f); - const float Rm = sqrtf(v * GAUSS_TRUNCATE); - - if (r >= Rm) { - return 0.0f; - } - return expf(-r * r / (2.0f * v)) / (2.0f * M_PI * v); -} - #define BURLEY_TRUNCATE 16.0f #define BURLEY_TRUNCATE_CDF 0.9963790093708328f // cdf(BURLEY_TRUNCATE) static float burley_profile(float r, float d) @@ -287,45 +273,15 @@ static float burley_profile(float r, float d) return (exp_r_d + exp_r_3_d) / (4.0f * d); } -static float cubic_profile(float r, float radius, float sharpness) -{ - float Rm = radius * (1.0f + sharpness); - - if (r >= Rm) { - return 0.0f; - } - /* custom variation with extra sharpness, to match the previous code */ - const float y = 1.0f / (1.0f + sharpness); - float Rmy, ry, ryinv; - - Rmy = powf(Rm, y); - ry = powf(r, y); - ryinv = (r > 0.0f) ? powf(r, y - 1.0f) : 0.0f; - - const float Rmy5 = (Rmy * Rmy) * (Rmy * Rmy) * Rmy; - const float f = Rmy - ry; - const float num = f * (f * f) * (y * ryinv); - - return (10.0f * num) / (Rmy5 * M_PI); -} - -static float eval_profile(float r, short falloff_type, float sharpness, float param) +static float eval_profile(float r, float param) { r = fabsf(r); - - if (ELEM(falloff_type, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { - return burley_profile(r, param) / BURLEY_TRUNCATE_CDF; - } - if (falloff_type == SHD_SUBSURFACE_CUBIC) { - return cubic_profile(r, param, sharpness); - } - - return gaussian_profile(r, param); + return burley_profile(r, param) / BURLEY_TRUNCATE_CDF; } /* Resolution for each sample of the precomputed kernel profile */ #define INTEGRAL_RESOLUTION 32 -static float eval_integral(float x0, float x1, short falloff_type, float sharpness, float param) +static float eval_integral(float x0, float x1, float param) { const float range = x1 - x0; const float step = range / INTEGRAL_RESOLUTION; @@ -333,7 +289,7 @@ static float eval_integral(float x0, float x1, short falloff_type, float sharpne for (int i = 0; i < INTEGRAL_RESOLUTION; i++) { float x = x0 + range * ((float)i + 0.5f) / (float)INTEGRAL_RESOLUTION; - float y = eval_profile(x, falloff_type, sharpness, param); + float y = eval_profile(x, param); integral += y * step; } @@ -341,8 +297,7 @@ static float eval_integral(float x0, float x1, short falloff_type, float sharpne } #undef INTEGRAL_RESOLUTION -static void compute_sss_kernel( - GPUSssKernelData *kd, const float radii[3], int sample_len, int falloff_type, float sharpness) +static void compute_sss_kernel(GPUSssKernelData *kd, const float radii[3], int sample_len) { float rad[3]; /* Minimum radius */ @@ -353,27 +308,15 @@ static void compute_sss_kernel( /* Christensen-Burley fitting */ float l[3], d[3]; - if (ELEM(falloff_type, SHD_SUBSURFACE_BURLEY, SHD_SUBSURFACE_RANDOM_WALK)) { - mul_v3_v3fl(l, rad, 0.25f * M_1_PI); - const float A = 1.0f; - const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); - /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */ - mul_v3_v3fl(d, l, 0.6f / s); - mul_v3_v3fl(rad, d, BURLEY_TRUNCATE); - kd->max_radius = MAX3(rad[0], rad[1], rad[2]); + mul_v3_v3fl(l, rad, 0.25f * M_1_PI); + const float A = 1.0f; + const float s = 1.9f - A + 3.5f * (A - 0.8f) * (A - 0.8f); + /* XXX 0.6f Out of nowhere to match cycles! Empirical! Can be tweak better. */ + mul_v3_v3fl(d, l, 0.6f / s); + mul_v3_v3fl(rad, d, BURLEY_TRUNCATE); + kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - copy_v3_v3(kd->param, d); - } - else if (falloff_type == SHD_SUBSURFACE_CUBIC) { - copy_v3_v3(kd->param, rad); - mul_v3_fl(rad, 1.0f + sharpness); - kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - } - else { - kd->max_radius = MAX3(rad[0], rad[1], rad[2]); - - copy_v3_v3(kd->param, rad); - } + copy_v3_v3(kd->param, d); /* Compute samples locations on the 1d kernel [-1..1] */ sss_calculate_offsets(kd, sample_len, SSS_EXPONENT); @@ -403,9 +346,9 @@ static void compute_sss_kernel( x0 *= kd->max_radius; x1 *= kd->max_radius; - kd->kernel[i][0] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[0]); - kd->kernel[i][1] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[1]); - kd->kernel[i][2] = eval_integral(x0, x1, falloff_type, sharpness, kd->param[2]); + kd->kernel[i][0] = eval_integral(x0, x1, kd->param[0]); + kd->kernel[i][1] = eval_integral(x0, x1, kd->param[1]); + kd->kernel[i][2] = eval_integral(x0, x1, kd->param[2]); sum[0] += kd->kernel[i][0]; sum[1] += kd->kernel[i][1]; @@ -439,8 +382,6 @@ static void compute_sss_kernel( #define INTEGRAL_RESOLUTION 512 static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, int resolution, - short falloff_type, - float sharpness, float **output) { float(*texels)[4]; @@ -463,9 +404,9 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, float dist = hypotf(r + r_step * 0.5f, d); float profile[3]; - profile[0] = eval_profile(dist, falloff_type, sharpness, kd->param[0]); - profile[1] = eval_profile(dist, falloff_type, sharpness, kd->param[1]); - profile[2] = eval_profile(dist, falloff_type, sharpness, kd->param[2]); + profile[0] = eval_profile(dist, kd->param[0]); + profile[1] = eval_profile(dist, kd->param[1]); + profile[2] = eval_profile(dist, kd->param[2]); /* Since the profile and configuration are radially symmetrical we * can just evaluate it once and weight it accordingly */ @@ -499,14 +440,9 @@ static void compute_sss_translucence_kernel(const GPUSssKernelData *kd, } #undef INTEGRAL_RESOLUTION -void GPU_material_sss_profile_create(GPUMaterial *material, - float radii[3], - const short *falloff_type, - const float *sharpness) +void GPU_material_sss_profile_create(GPUMaterial *material, float radii[3]) { copy_v3_v3(material->sss_radii, radii); - material->sss_falloff = (falloff_type) ? *falloff_type : 0.0; - material->sss_sharpness = (sharpness) ? *sharpness : 0.0; material->sss_dirty = true; material->sss_enabled = true; @@ -527,20 +463,14 @@ struct GPUUniformBuf *GPU_material_sss_profile_get(GPUMaterial *material, if (material->sss_dirty || (material->sss_samples != sample_len)) { GPUSssKernelData kd; - float sharpness = material->sss_sharpness; - - /* XXX Black magic but it seems to fit. Maybe because we integrate -1..1 */ - sharpness *= 0.5f; - - compute_sss_kernel(&kd, material->sss_radii, sample_len, material->sss_falloff, sharpness); + compute_sss_kernel(&kd, material->sss_radii, sample_len); /* Update / Create UBO */ GPU_uniformbuf_update(material->sss_profile, &kd); /* Update / Create Tex */ float *translucence_profile; - compute_sss_translucence_kernel( - &kd, 64, material->sss_falloff, sharpness, &translucence_profile); + compute_sss_translucence_kernel(&kd, 64, &translucence_profile); if (material->sss_tex_profile != NULL) { GPU_texture_free(material->sss_tex_profile); diff --git a/source/blender/gpu/intern/gpu_material_library.c b/source/blender/gpu/intern/gpu_material_library.c index 73a80c62bdc..74e0270c42a 100644 --- a/source/blender/gpu/intern/gpu_material_library.c +++ b/source/blender/gpu/intern/gpu_material_library.c @@ -62,6 +62,7 @@ extern char datatoc_gpu_shader_material_diffuse_glsl[]; extern char datatoc_gpu_shader_material_displacement_glsl[]; extern char datatoc_gpu_shader_material_eevee_specular_glsl[]; extern char datatoc_gpu_shader_material_emission_glsl[]; +extern char datatoc_gpu_shader_material_float_curve_glsl[]; extern char datatoc_gpu_shader_material_fractal_noise_glsl[]; extern char datatoc_gpu_shader_material_fresnel_glsl[]; extern char datatoc_gpu_shader_material_gamma_glsl[]; @@ -262,6 +263,11 @@ static GPUMaterialLibrary gpu_shader_material_emission_library = { .dependencies = {NULL}, }; +static GPUMaterialLibrary gpu_shader_material_float_curve_library = { + .code = datatoc_gpu_shader_material_float_curve_glsl, + .dependencies = {NULL}, +}; + static GPUMaterialLibrary gpu_shader_material_fresnel_library = { .code = datatoc_gpu_shader_material_fresnel_glsl, .dependencies = {NULL}, @@ -591,6 +597,7 @@ static GPUMaterialLibrary *gpu_material_libraries[] = { &gpu_shader_material_color_util_library, &gpu_shader_material_hash_library, &gpu_shader_material_noise_library, + &gpu_shader_material_float_curve_library, &gpu_shader_material_fractal_noise_library, &gpu_shader_material_add_shader_library, &gpu_shader_material_ambient_occlusion_library, diff --git a/source/blender/gpu/intern/gpu_material_library.h b/source/blender/gpu/intern/gpu_material_library.h index 782d89d6f2a..d3b12d3a2b7 100644 --- a/source/blender/gpu/intern/gpu_material_library.h +++ b/source/blender/gpu/intern/gpu_material_library.h @@ -27,7 +27,7 @@ #include "GPU_material.h" #define MAX_FUNCTION_NAME 64 -#define MAX_PARAMETER 32 +#define MAX_PARAMETER 36 struct GSet; diff --git a/source/blender/gpu/intern/gpu_select.c b/source/blender/gpu/intern/gpu_select.c index 88b704a84a1..3c89f082e9b 100644 --- a/source/blender/gpu/intern/gpu_select.c +++ b/source/blender/gpu/intern/gpu_select.c @@ -20,8 +20,8 @@ /** \file * \ingroup gpu * - * Interface for accessing gpu-related methods for selection. The semantics are - * similar to glRenderMode(GL_SELECT) from older OpenGL versions. + * Interface for accessing GPU-related methods for selection. The semantics are + * similar to `glRenderMode(GL_SELECT)` from older OpenGL versions. */ #include #include @@ -38,6 +38,10 @@ #include "gpu_select_private.h" +/* -------------------------------------------------------------------- */ +/** \name Internal Types + * \{ */ + /* Internal algorithm used */ enum { /** glBegin/EndQuery(GL_SAMPLES_PASSED... ), `gpu_select_query.c` @@ -61,6 +65,12 @@ typedef struct GPUSelectState { static GPUSelectState g_select_state = {0}; +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Public API + * \{ */ + /** * initialize and provide buffer for results */ @@ -149,12 +159,14 @@ uint GPU_select_end(void) return hits; } -/* ---------------------------------------------------------------------------- - * Caching +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Caching * * Support multiple begin/end's as long as they are within the initial region. - * Currently only used by ALGO_GL_PICK. - */ + * Currently only used by #ALGO_GL_PICK. + * \{ */ void GPU_select_cache_begin(void) { @@ -187,9 +199,11 @@ bool GPU_select_is_cached(void) return g_select_state.use_cache && gpu_select_pick_is_cached(); } -/* ---------------------------------------------------------------------------- - * Utilities - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utilities + * \{ */ /** * Helper function, nothing special but avoids doing inline since hits aren't sorted by depth @@ -269,3 +283,5 @@ void GPU_select_buffer_stride_realign(const rcti *src, const rcti *dst, uint *r_ } memset(r_buf, 0, (last_px_id + 1) * sizeof(*r_buf)); } + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_select_pick.c b/source/blender/gpu/intern/gpu_select_pick.c index 7fb704c29dd..a8907859bf4 100644 --- a/source/blender/gpu/intern/gpu_select_pick.c +++ b/source/blender/gpu/intern/gpu_select_pick.c @@ -51,9 +51,9 @@ /* Z-depth of cleared depth buffer */ #define DEPTH_MAX 0xffffffff -/* ---------------------------------------------------------------------------- - * SubRectStride - */ +/* -------------------------------------------------------------------- */ +/** \name #SubRectStride + * \{ */ /* For looping over a sub-region of a rect, could be moved into 'rct.c'. */ typedef struct SubRectStride { @@ -99,14 +99,16 @@ BLI_INLINE bool depth_is_filled(const depth_t *prev, const depth_t *curr) return (*prev != *curr) && (*curr != DEPTH_MAX); } -/* ---------------------------------------------------------------------------- - * DepthBufCache - * - * Result of reading glReadPixels, - * use for both cache and non-cached storage. - */ +/** \} */ -/* store result of glReadPixels */ +/* -------------------------------------------------------------------- */ +/** \name #DepthBufCache + * + * Result of reading #glReadPixels, + * use for both cache and non-cached storage. + * \{ */ + +/** Store result of #glReadPixels. */ typedef struct DepthBufCache { struct DepthBufCache *next, *prev; uint id; @@ -188,11 +190,13 @@ static bool depth_buf_subrect_depth_any_filled(const DepthBufCache *rect_src, return false; } -/* ---------------------------------------------------------------------------- - * DepthID +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #DepthID * * Internal structure for storing hits. - */ + * \{ */ typedef struct DepthID { uint id; @@ -225,6 +229,12 @@ static int depth_cmp(const void *v1, const void *v2) return 0; } +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Selection Begin/End/Load API + * \{ */ + /* depth sorting */ typedef struct GPUPickState { /* cache on initialization */ @@ -691,11 +701,13 @@ uint gpu_select_pick_end(void) return hits; } -/* ---------------------------------------------------------------------------- - * Caching +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Caching * * Support multiple begin/end's reusing depth buffers. - */ + * \{ */ void gpu_select_pick_cache_begin(void) { @@ -749,3 +761,5 @@ void gpu_select_pick_cache_load_id(void) } } } + +/** \} */ diff --git a/source/blender/gpu/intern/gpu_select_sample_query.cc b/source/blender/gpu/intern/gpu_select_sample_query.cc index 7b9b3020639..047ce0cfb35 100644 --- a/source/blender/gpu/intern/gpu_select_sample_query.cc +++ b/source/blender/gpu/intern/gpu_select_sample_query.cc @@ -20,8 +20,8 @@ /** \file * \ingroup gpu * - * Interface for accessing gpu-related methods for selection. The semantics will be - * similar to glRenderMode(GL_SELECT) since the goal is to maintain compatibility. + * Interface for accessing GPU-related methods for selection. The semantics will be + * similar to `glRenderMode(GL_SELECT)` since the goal is to maintain compatibility. */ #include diff --git a/source/blender/gpu/intern/gpu_shader_builtin.c b/source/blender/gpu/intern/gpu_shader_builtin.c index c5122b76001..9ea46788f44 100644 --- a/source/blender/gpu/intern/gpu_shader_builtin.c +++ b/source/blender/gpu/intern/gpu_shader_builtin.c @@ -121,8 +121,8 @@ extern char datatoc_gpu_shader_3D_line_dashed_uniform_color_vert_glsl[]; extern char datatoc_gpu_shader_text_vert_glsl[]; extern char datatoc_gpu_shader_text_frag_glsl[]; -extern char datatoc_gpu_shader_keyframe_diamond_vert_glsl[]; -extern char datatoc_gpu_shader_keyframe_diamond_frag_glsl[]; +extern char datatoc_gpu_shader_keyframe_shape_vert_glsl[]; +extern char datatoc_gpu_shader_keyframe_shape_frag_glsl[]; extern char datatoc_gpu_shader_gpencil_stroke_vert_glsl[]; extern char datatoc_gpu_shader_gpencil_stroke_frag_glsl[]; @@ -166,11 +166,11 @@ static const GPUShaderStages builtin_shader_stages[GPU_SHADER_BUILTIN_LEN] = { .vert = datatoc_gpu_shader_text_vert_glsl, .frag = datatoc_gpu_shader_text_frag_glsl, }, - [GPU_SHADER_KEYFRAME_DIAMOND] = + [GPU_SHADER_KEYFRAME_SHAPE] = { - .name = "GPU_SHADER_KEYFRAME_DIAMOND", - .vert = datatoc_gpu_shader_keyframe_diamond_vert_glsl, - .frag = datatoc_gpu_shader_keyframe_diamond_frag_glsl, + .name = "GPU_SHADER_KEYFRAME_SHAPE", + .vert = datatoc_gpu_shader_keyframe_shape_vert_glsl, + .frag = datatoc_gpu_shader_keyframe_shape_frag_glsl, }, [GPU_SHADER_SIMPLE_LIGHTING] = { diff --git a/source/blender/gpu/intern/gpu_texture.cc b/source/blender/gpu/intern/gpu_texture.cc index d5d13ea269f..2744c0c5e17 100644 --- a/source/blender/gpu/intern/gpu_texture.cc +++ b/source/blender/gpu/intern/gpu_texture.cc @@ -461,7 +461,7 @@ void GPU_texture_generate_mipmap(GPUTexture *tex) reinterpret_cast(tex)->generate_mipmap(); } -/* Copy a texture content to a similar texture. Only Mip 0 is copied. */ +/* Copy a texture content to a similar texture. Only MIP 0 is copied. */ void GPU_texture_copy(GPUTexture *dst_, GPUTexture *src_) { Texture *src = reinterpret_cast(src_); diff --git a/source/blender/gpu/intern/gpu_texture_private.hh b/source/blender/gpu/intern/gpu_texture_private.hh index 26be6f57312..19d83366e6f 100644 --- a/source/blender/gpu/intern/gpu_texture_private.hh +++ b/source/blender/gpu/intern/gpu_texture_private.hh @@ -66,7 +66,7 @@ ENUM_OPERATORS(eGPUTextureType, GPU_TEXTURE_CUBE_ARRAY) #endif /* Maximum number of FBOs a texture can be attached to. */ -#define GPU_TEX_MAX_FBO_ATTACHED 16 +#define GPU_TEX_MAX_FBO_ATTACHED 32 /** * Implementation of Textures. diff --git a/source/blender/gpu/intern/gpu_viewport.c b/source/blender/gpu/intern/gpu_viewport.c index dd63edea0db..ccd9a4c061b 100644 --- a/source/blender/gpu/intern/gpu_viewport.c +++ b/source/blender/gpu/intern/gpu_viewport.c @@ -48,22 +48,6 @@ #include "MEM_guardedalloc.h" -static const int default_fbl_len = (sizeof(DefaultFramebufferList)) / sizeof(void *); -static const int default_txl_len = (sizeof(DefaultTextureList)) / sizeof(void *); - -#define MAX_ENABLE_ENGINE 8 - -/* Maximum number of simultaneous engine enabled at the same time. - * Setting it lower than the real number will do lead to - * higher VRAM usage due to sub-efficient buffer reuse. */ -#define MAX_ENGINE_BUFFER_SHARING 5 - -typedef struct ViewportTempTexture { - struct ViewportTempTexture *next, *prev; - void *user[MAX_ENGINE_BUFFER_SHARING]; - GPUTexture *texture; -} ViewportTempTexture; - /* Struct storing a viewport specific GPUBatch. * The end-goal is to have a single batch shared across viewport and use a model matrix to place * the batch. Due to OCIO and Image/UV editor we are not able to use an model matrix yet. */ @@ -89,23 +73,17 @@ struct GPUViewport { /* Set the active view (for stereoscopic viewport rendering). */ int active_view; - /* If engine_handles mismatch we free all #ViewportEngineData in this viewport. */ - struct { - void *handle; - ViewportEngineData *data; - } engine_data[MAX_ENABLE_ENGINE]; - - DefaultFramebufferList *fbl; - DefaultTextureList *txl; - - ViewportMemoryPool vmempool; /* Used for rendering data structure. */ - struct DRWInstanceDataList *idatalist; /* Used for rendering data structure. */ - - ListBase - tex_pool; /* ViewportTempTexture list : Temporary textures shared across draw engines. */ - - /* Profiling data. */ - double cache_time; + /* Viewport Resources. */ + struct DRWData *draw_data; + /** Color buffers, one for each stereo view. Only one if not stereo viewport. */ + GPUTexture *color_render_tx[2]; + GPUTexture *color_overlay_tx[2]; + /** Depth buffer. Can be shared with GPUOffscreen. */ + GPUTexture *depth_tx; + /** Compositing framebuffer for stereo viewport. */ + GPUFrameBuffer *stereo_comp_fb; + /** Overlay framebuffer for drawing outside of DRW module. */ + GPUFrameBuffer *overlay_fb; /* Color management. */ ColorManagedViewSettings view_settings; @@ -123,12 +101,6 @@ enum { GPU_VIEWPORT_STEREO = (1 << 1), }; -static void gpu_viewport_buffers_free( - FramebufferList *fbl, int fbl_len, TextureList *txl, TextureList *txl_stereo, int txl_len); -static void gpu_viewport_storage_free(StorageList *stl, int stl_len); -static void gpu_viewport_passes_free(PassList *psl, int psl_len); -static void gpu_viewport_texture_pool_free(GPUViewport *viewport); - void GPU_viewport_tag_update(GPUViewport *viewport) { viewport->flag |= DO_UPDATE; @@ -144,12 +116,9 @@ bool GPU_viewport_do_update(GPUViewport *viewport) GPUViewport *GPU_viewport_create(void) { GPUViewport *viewport = MEM_callocN(sizeof(GPUViewport), "GPUViewport"); - viewport->fbl = MEM_callocN(sizeof(DefaultFramebufferList), "FramebufferList"); - viewport->txl = MEM_callocN(sizeof(DefaultTextureList), "TextureList"); - viewport->idatalist = DRW_instance_data_list_create(); viewport->do_color_management = false; viewport->size[0] = viewport->size[1] = -1; - viewport->active_view = -1; + viewport->active_view = 0; return viewport; } @@ -160,343 +129,55 @@ GPUViewport *GPU_viewport_stereo_create(void) return viewport; } -static void gpu_viewport_framebuffer_view_set(GPUViewport *viewport, int view) +struct DRWData **GPU_viewport_data_get(GPUViewport *viewport) { - /* Early check if the view is the latest requested. */ - if (viewport->active_view == view) { - return; - } - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; - - /* Only swap the texture when this is a Stereo Viewport. */ - if (((viewport->flag & GPU_VIEWPORT_STEREO) != 0)) { - SWAP(GPUTexture *, dtxl->color, dtxl->color_stereo); - SWAP(GPUTexture *, dtxl->color_overlay, dtxl->color_overlay_stereo); - - for (int i = 0; i < MAX_ENABLE_ENGINE; i++) { - if (viewport->engine_data[i].handle != NULL) { - ViewportEngineData *data = viewport->engine_data[i].data; - SWAP(StorageList *, data->stl, data->stl_stereo); - SWAP(TextureList *, data->txl, data->txl_stereo); - } - else { - break; - } - } - } - - GPU_framebuffer_ensure_config(&dfbl->default_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(dtxl->color), - }); - - GPU_framebuffer_ensure_config(&dfbl->overlay_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), - }); - - GPU_framebuffer_ensure_config(&dfbl->depth_only_fb, - { - GPU_ATTACHMENT_TEXTURE(dtxl->depth), - GPU_ATTACHMENT_NONE, - }); - - GPU_framebuffer_ensure_config(&dfbl->color_only_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color), - }); - - GPU_framebuffer_ensure_config(&dfbl->overlay_only_fb, - { - GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), - }); - - viewport->active_view = view; + return &viewport->draw_data; } -void *GPU_viewport_engine_data_create(GPUViewport *viewport, void *engine_type) +static void gpu_viewport_textures_create(GPUViewport *viewport) { - ViewportEngineData *data = MEM_callocN(sizeof(ViewportEngineData), "ViewportEngineData"); - int fbl_len, txl_len, psl_len, stl_len; - - DRW_engine_viewport_data_size_get(engine_type, &fbl_len, &txl_len, &psl_len, &stl_len); - - data->engine_type = engine_type; - - data->fbl = MEM_callocN((sizeof(void *) * fbl_len) + sizeof(FramebufferList), "FramebufferList"); - data->txl = MEM_callocN((sizeof(void *) * txl_len) + sizeof(TextureList), "TextureList"); - data->psl = MEM_callocN((sizeof(void *) * psl_len) + sizeof(PassList), "PassList"); - data->stl = MEM_callocN((sizeof(void *) * stl_len) + sizeof(StorageList), "StorageList"); - - if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0) { - data->txl_stereo = MEM_callocN((sizeof(void *) * txl_len) + sizeof(TextureList), - "TextureList"); - data->stl_stereo = MEM_callocN((sizeof(void *) * stl_len) + sizeof(StorageList), - "StorageList"); - } - - for (int i = 0; i < MAX_ENABLE_ENGINE; i++) { - if (viewport->engine_data[i].handle == NULL) { - viewport->engine_data[i].handle = engine_type; - viewport->engine_data[i].data = data; - return data; - } - } - - BLI_assert_msg(0, "Too many draw engines enabled at the same time"); - return NULL; -} - -static void gpu_viewport_engines_data_free(GPUViewport *viewport) -{ - int fbl_len, txl_len, psl_len, stl_len; - - for (int i = 0; i < MAX_ENABLE_ENGINE && viewport->engine_data[i].handle; i++) { - ViewportEngineData *data = viewport->engine_data[i].data; - - DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, &psl_len, &stl_len); - - gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, data->txl_stereo, txl_len); - gpu_viewport_passes_free(data->psl, psl_len); - gpu_viewport_storage_free(data->stl, stl_len); - - MEM_freeN(data->fbl); - MEM_freeN(data->txl); - MEM_freeN(data->psl); - MEM_freeN(data->stl); - - if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0) { - gpu_viewport_storage_free(data->stl_stereo, stl_len); - MEM_freeN(data->txl_stereo); - MEM_freeN(data->stl_stereo); - } - /* We could handle this in the DRW module */ - if (data->text_draw_cache) { - extern void DRW_text_cache_destroy(struct DRWTextStore * dt); - DRW_text_cache_destroy(data->text_draw_cache); - data->text_draw_cache = NULL; - } - - MEM_freeN(data); - - /* Mark as unused. */ - viewport->engine_data[i].handle = NULL; - } - - gpu_viewport_texture_pool_free(viewport); -} - -void *GPU_viewport_engine_data_get(GPUViewport *viewport, void *engine_handle) -{ - BLI_assert(engine_handle != NULL); - - for (int i = 0; i < MAX_ENABLE_ENGINE; i++) { - if (viewport->engine_data[i].handle == engine_handle) { - return viewport->engine_data[i].data; - } - } - return NULL; -} - -ViewportMemoryPool *GPU_viewport_mempool_get(GPUViewport *viewport) -{ - return &viewport->vmempool; -} - -struct DRWInstanceDataList *GPU_viewport_instance_data_list_get(GPUViewport *viewport) -{ - return viewport->idatalist; -} - -/* Note this function is only allowed to be called from `DRW_notify_view_update`. The rest - * should bind the correct viewport. - * - * The reason is that DRW_notify_view_update can be called from a different thread, but needs - * access to the engine data. */ -void GPU_viewport_active_view_set(GPUViewport *viewport, int view) -{ - gpu_viewport_framebuffer_view_set(viewport, view); -} - -void *GPU_viewport_framebuffer_list_get(GPUViewport *viewport) -{ - return viewport->fbl; -} - -void *GPU_viewport_texture_list_get(GPUViewport *viewport) -{ - return viewport->txl; -} - -void GPU_viewport_size_get(const GPUViewport *viewport, int size[2]) -{ - copy_v2_v2_int(size, viewport->size); -} - -/** - * Special case, this is needed for when we have a viewport without a frame-buffer output - * (occlusion queries for eg) - * but still need to set the size since it may be used for other calculations. - */ -void GPU_viewport_size_set(GPUViewport *viewport, const int size[2]) -{ - copy_v2_v2_int(viewport->size, size); -} - -double *GPU_viewport_cache_time_get(GPUViewport *viewport) -{ - return &viewport->cache_time; -} - -/** - * Try to find a texture corresponding to params into the texture pool. - * If no texture was found, create one and add it to the pool. - */ -GPUTexture *GPU_viewport_texture_pool_query( - GPUViewport *viewport, void *engine, int width, int height, int format) -{ - GPUTexture *tex; - - LISTBASE_FOREACH (ViewportTempTexture *, tmp_tex, &viewport->tex_pool) { - if ((GPU_texture_format(tmp_tex->texture) == format) && - (GPU_texture_width(tmp_tex->texture) == width) && - (GPU_texture_height(tmp_tex->texture) == height)) { - /* Search if the engine is not already using this texture */ - for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; i++) { - if (tmp_tex->user[i] == engine) { - break; - } - - if (tmp_tex->user[i] == NULL) { - tmp_tex->user[i] = engine; - return tmp_tex->texture; - } - } - } - } - - tex = GPU_texture_create_2d("temp_from_pool", width, height, 1, format, NULL); - /* Doing filtering for depth does not make sense when not doing shadow mapping, - * and enabling texture filtering on integer texture make them unreadable. */ - bool do_filter = !GPU_texture_depth(tex) && !GPU_texture_integer(tex); - GPU_texture_filter_mode(tex, do_filter); - - ViewportTempTexture *tmp_tex = MEM_callocN(sizeof(ViewportTempTexture), "ViewportTempTexture"); - tmp_tex->texture = tex; - tmp_tex->user[0] = engine; - BLI_addtail(&viewport->tex_pool, tmp_tex); - - return tex; -} - -static void gpu_viewport_texture_pool_clear_users(GPUViewport *viewport) -{ - ViewportTempTexture *tmp_tex_next; - - for (ViewportTempTexture *tmp_tex = viewport->tex_pool.first; tmp_tex; tmp_tex = tmp_tex_next) { - tmp_tex_next = tmp_tex->next; - bool no_user = true; - for (int i = 0; i < MAX_ENGINE_BUFFER_SHARING; i++) { - if (tmp_tex->user[i] != NULL) { - tmp_tex->user[i] = NULL; - no_user = false; - } - } - - if (no_user) { - GPU_texture_free(tmp_tex->texture); - BLI_freelinkN(&viewport->tex_pool, tmp_tex); - } - } -} - -static void gpu_viewport_texture_pool_free(GPUViewport *viewport) -{ - LISTBASE_FOREACH (ViewportTempTexture *, tmp_tex, &viewport->tex_pool) { - GPU_texture_free(tmp_tex->texture); - } - - BLI_freelistN(&viewport->tex_pool); -} - -/* Takes an NULL terminated array of engine_handle. Returns true is data is still valid. */ -bool GPU_viewport_engines_data_validate(GPUViewport *viewport, void **engine_handle_array) -{ - for (int i = 0; i < MAX_ENABLE_ENGINE && engine_handle_array[i]; i++) { - if (viewport->engine_data[i].handle != engine_handle_array[i]) { - gpu_viewport_engines_data_free(viewport); - return false; - } - } - return true; -} - -void GPU_viewport_cache_release(GPUViewport *viewport) -{ - for (int i = 0; i < MAX_ENABLE_ENGINE && viewport->engine_data[i].handle; i++) { - ViewportEngineData *data = viewport->engine_data[i].data; - int psl_len; - DRW_engine_viewport_data_size_get(data->engine_type, NULL, NULL, &psl_len, NULL); - gpu_viewport_passes_free(data->psl, psl_len); - } -} - -static void gpu_viewport_default_fb_create(GPUViewport *viewport) -{ - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; int *size = viewport->size; - bool ok = true; - dtxl->color = GPU_texture_create_2d("dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); - dtxl->color_overlay = GPU_texture_create_2d( - "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); + if (viewport->color_render_tx[0] == NULL) { + viewport->color_render_tx[0] = GPU_texture_create_2d( + "dtxl_color", UNPACK2(size), 1, GPU_RGBA16F, NULL); + viewport->color_overlay_tx[0] = GPU_texture_create_2d( + "dtxl_color_overlay", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); + } - if (viewport->flag & GPU_VIEWPORT_STEREO) { - dtxl->color_stereo = GPU_texture_create_2d( + if ((viewport->flag & GPU_VIEWPORT_STEREO) != 0 && viewport->color_render_tx[1] == NULL) { + viewport->color_render_tx[1] = GPU_texture_create_2d( "dtxl_color_stereo", UNPACK2(size), 1, GPU_RGBA16F, NULL); - dtxl->color_overlay_stereo = GPU_texture_create_2d( + viewport->color_overlay_tx[1] = GPU_texture_create_2d( "dtxl_color_overlay_stereo", UNPACK2(size), 1, GPU_SRGB8_A8, NULL); } /* Can be shared with GPUOffscreen. */ - if (dtxl->depth == NULL) { - dtxl->depth = GPU_texture_create_2d( + if (viewport->depth_tx == NULL) { + viewport->depth_tx = GPU_texture_create_2d( "dtxl_depth", UNPACK2(size), 1, GPU_DEPTH24_STENCIL8, NULL); } - if (!dtxl->depth || !dtxl->color) { - ok = false; - goto cleanup; - } - - gpu_viewport_framebuffer_view_set(viewport, 0); - - ok = ok && GPU_framebuffer_check_valid(dfbl->default_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->overlay_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->color_only_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->depth_only_fb, NULL); - ok = ok && GPU_framebuffer_check_valid(dfbl->overlay_only_fb, NULL); -cleanup: - if (!ok) { + if (!viewport->depth_tx || !viewport->color_render_tx[0] || !viewport->color_overlay_tx[0]) { GPU_viewport_free(viewport); - DRW_opengl_context_disable(); - return; + } +} + +static void gpu_viewport_textures_free(GPUViewport *viewport) +{ + GPU_FRAMEBUFFER_FREE_SAFE(viewport->stereo_comp_fb); + GPU_FRAMEBUFFER_FREE_SAFE(viewport->overlay_fb); + + for (int i = 0; i < 2; i++) { + GPU_TEXTURE_FREE_SAFE(viewport->color_render_tx[i]); + GPU_TEXTURE_FREE_SAFE(viewport->color_overlay_tx[i]); } - GPU_framebuffer_restore(); + GPU_TEXTURE_FREE_SAFE(viewport->depth_tx); } void GPU_viewport_bind(GPUViewport *viewport, int view, const rcti *rect) { - DefaultFramebufferList *dfbl = viewport->fbl; - int fbl_len, txl_len; - int rect_size[2]; /* add one pixel because of scissor test */ rect_size[0] = BLI_rcti_size_x(rect) + 1; @@ -504,39 +185,18 @@ void GPU_viewport_bind(GPUViewport *viewport, int view, const rcti *rect) DRW_opengl_context_enable(); - if (dfbl->default_fb) { - if (!equals_v2v2_int(viewport->size, rect_size)) { - gpu_viewport_buffers_free((FramebufferList *)viewport->fbl, - default_fbl_len, - (TextureList *)viewport->txl, - NULL, - default_txl_len); - - for (int i = 0; i < MAX_ENABLE_ENGINE && viewport->engine_data[i].handle; i++) { - ViewportEngineData *data = viewport->engine_data[i].data; - DRW_engine_viewport_data_size_get(data->engine_type, &fbl_len, &txl_len, NULL, NULL); - gpu_viewport_buffers_free(data->fbl, fbl_len, data->txl, data->txl_stereo, txl_len); - } - - gpu_viewport_texture_pool_free(viewport); - viewport->active_view = -1; - } + if (!equals_v2v2_int(viewport->size, rect_size)) { + copy_v2_v2_int(viewport->size, rect_size); + gpu_viewport_textures_free(viewport); + gpu_viewport_textures_create(viewport); } - copy_v2_v2_int(viewport->size, rect_size); - - gpu_viewport_texture_pool_clear_users(viewport); - - if (!dfbl->default_fb) { - gpu_viewport_default_fb_create(viewport); - } - gpu_viewport_framebuffer_view_set(viewport, view); + viewport->active_view = view; } +/* Should be called from DRW after DRW_opengl_context_enable. */ void GPU_viewport_bind_from_offscreen(GPUViewport *viewport, struct GPUOffScreen *ofs) { - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; GPUTexture *color, *depth; GPUFrameBuffer *fb; viewport->size[0] = GPU_offscreen_width(ofs); @@ -544,14 +204,12 @@ void GPU_viewport_bind_from_offscreen(GPUViewport *viewport, struct GPUOffScreen GPU_offscreen_viewport_data_get(ofs, &fb, &color, &depth); + gpu_viewport_textures_free(viewport); + /* This is the only texture we can share. */ - dtxl->depth = depth; + viewport->depth_tx = depth; - gpu_viewport_texture_pool_clear_users(viewport); - - if (!dfbl->default_fb) { - gpu_viewport_default_fb_create(viewport); - } + gpu_viewport_textures_create(viewport); } void GPU_viewport_colorspace_set(GPUViewport *viewport, @@ -608,21 +266,17 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo * done from a single viewport. See `wm_stereo.c` */ return; } - gpu_viewport_framebuffer_view_set(viewport, 0); - DefaultTextureList *dtxl = viewport->txl; - DefaultFramebufferList *dfbl = viewport->fbl; - /* The composite framebuffer object needs to be created in the window context. */ - GPU_framebuffer_ensure_config(&dfbl->stereo_comp_fb, + GPU_framebuffer_ensure_config(&viewport->stereo_comp_fb, { GPU_ATTACHMENT_NONE, - GPU_ATTACHMENT_TEXTURE(dtxl->color_overlay), - GPU_ATTACHMENT_TEXTURE(dtxl->color), + GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), + GPU_ATTACHMENT_TEXTURE(viewport->color_render_tx[0]), }); GPUVertFormat *vert_format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(vert_format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - GPU_framebuffer_bind(dfbl->stereo_comp_fb); + GPU_framebuffer_bind(viewport->stereo_comp_fb); GPU_matrix_push(); GPU_matrix_push_projection(); GPU_matrix_identity_set(); @@ -650,8 +304,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo } immUniform1i("stereoDisplaySettings", settings); - GPU_texture_bind(dtxl->color_stereo, 0); - GPU_texture_bind(dtxl->color_overlay_stereo, 1); + GPU_texture_bind(viewport->color_render_tx[1], 0); + GPU_texture_bind(viewport->color_overlay_tx[1], 1); immBegin(GPU_PRIM_TRI_STRIP, 4); @@ -662,8 +316,8 @@ void GPU_viewport_stereo_composite(GPUViewport *viewport, Stereo3dFormat *stereo immEnd(); - GPU_texture_unbind(dtxl->color_stereo); - GPU_texture_unbind(dtxl->color_overlay_stereo); + GPU_texture_unbind(viewport->color_render_tx[1]); + GPU_texture_unbind(viewport->color_overlay_tx[1]); immUnbindProgram(); GPU_matrix_pop_projection(); @@ -747,14 +401,14 @@ static void gpu_viewport_batch_free(GPUViewport *viewport) /** \} */ static void gpu_viewport_draw_colormanaged(GPUViewport *viewport, + int view, const rctf *rect_pos, const rctf *rect_uv, bool display_colorspace, bool do_overlay_merge) { - DefaultTextureList *dtxl = viewport->txl; - GPUTexture *color = dtxl->color; - GPUTexture *color_overlay = dtxl->color_overlay; + GPUTexture *color = viewport->color_render_tx[view]; + GPUTexture *color_overlay = viewport->color_overlay_tx[view]; bool use_ocio = false; @@ -808,12 +462,9 @@ void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, bool display_colorspace, bool do_overlay_merge) { - gpu_viewport_framebuffer_view_set(viewport, view); - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; - GPUTexture *color = dtxl->color; + GPUTexture *color = viewport->color_render_tx[view]; - if (dfbl->default_fb == NULL) { + if (color == NULL) { return; } @@ -854,7 +505,7 @@ void GPU_viewport_draw_to_screen_ex(GPUViewport *viewport, } gpu_viewport_draw_colormanaged( - viewport, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); + viewport, view, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); } /** @@ -877,10 +528,7 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, bool display_colorspace, bool do_overlay_merge) { - DefaultFramebufferList *dfbl = viewport->fbl; - DefaultTextureList *dtxl = viewport->txl; - - if (dfbl->default_fb == NULL) { + if (viewport->color_render_tx == NULL) { return; } @@ -902,10 +550,10 @@ void GPU_viewport_unbind_from_offscreen(GPUViewport *viewport, }; gpu_viewport_draw_colormanaged( - viewport, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); + viewport, 0, &pos_rect, &uv_rect, display_colorspace, do_overlay_merge); /* This one is from the offscreen. Don't free it with the viewport. */ - dtxl->depth = NULL; + viewport->depth_tx = NULL; } void GPU_viewport_unbind(GPUViewport *UNUSED(viewport)) @@ -914,149 +562,53 @@ void GPU_viewport_unbind(GPUViewport *UNUSED(viewport)) DRW_opengl_context_disable(); } +int GPU_viewport_active_view_get(GPUViewport *viewport) +{ + return viewport->active_view; +} + +bool GPU_viewport_is_stereo_get(GPUViewport *viewport) +{ + return (viewport->flag & GPU_VIEWPORT_STEREO) != 0; +} + GPUTexture *GPU_viewport_color_texture(GPUViewport *viewport, int view) { - DefaultFramebufferList *dfbl = viewport->fbl; - - if (dfbl->default_fb) { - DefaultTextureList *dtxl = viewport->txl; - if (viewport->active_view == view) { - return dtxl->color; - } - - return dtxl->color_stereo; - } - - return NULL; + return viewport->color_render_tx[view]; } -static void gpu_viewport_buffers_free( - FramebufferList *fbl, int fbl_len, TextureList *txl, TextureList *txl_stereo, int txl_len) +GPUTexture *GPU_viewport_overlay_texture(GPUViewport *viewport, int view) { - for (int i = 0; i < fbl_len; i++) { - GPUFrameBuffer *fb = fbl->framebuffers[i]; - if (fb) { - GPU_framebuffer_free(fb); - fbl->framebuffers[i] = NULL; - } - } - for (int i = 0; i < txl_len; i++) { - GPUTexture *tex = txl->textures[i]; - if (tex) { - GPU_texture_free(tex); - txl->textures[i] = NULL; - } - } - if (txl_stereo != NULL) { - for (int i = 0; i < txl_len; i++) { - GPUTexture *tex = txl_stereo->textures[i]; - if (tex) { - GPU_texture_free(tex); - txl_stereo->textures[i] = NULL; - } - } - } + return viewport->color_overlay_tx[view]; } -static void gpu_viewport_storage_free(StorageList *stl, int stl_len) +GPUTexture *GPU_viewport_depth_texture(GPUViewport *viewport) { - for (int i = 0; i < stl_len; i++) { - void *storage = stl->storage[i]; - if (storage) { - MEM_freeN(storage); - stl->storage[i] = NULL; - } - } + return viewport->depth_tx; } -static void gpu_viewport_passes_free(PassList *psl, int psl_len) +/* Overlay framebuffer for drawing outside of DRW module. */ +GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport) { - memset(psl->passes, 0, sizeof(*psl->passes) * psl_len); + GPU_framebuffer_ensure_config(&viewport->overlay_fb, + { + GPU_ATTACHMENT_TEXTURE(viewport->depth_tx), + GPU_ATTACHMENT_TEXTURE(viewport->color_overlay_tx[0]), + }); + return viewport->overlay_fb; } /* Must be executed inside Draw-manager OpenGL Context. */ void GPU_viewport_free(GPUViewport *viewport) { - gpu_viewport_engines_data_free(viewport); - - gpu_viewport_buffers_free((FramebufferList *)viewport->fbl, - default_fbl_len, - (TextureList *)viewport->txl, - NULL, - default_txl_len); - - gpu_viewport_texture_pool_free(viewport); - - MEM_freeN(viewport->fbl); - MEM_freeN(viewport->txl); - - if (viewport->vmempool.commands != NULL) { - BLI_memblock_destroy(viewport->vmempool.commands, NULL); - } - if (viewport->vmempool.commands_small != NULL) { - BLI_memblock_destroy(viewport->vmempool.commands_small, NULL); - } - if (viewport->vmempool.callbuffers != NULL) { - BLI_memblock_destroy(viewport->vmempool.callbuffers, NULL); - } - if (viewport->vmempool.obmats != NULL) { - BLI_memblock_destroy(viewport->vmempool.obmats, NULL); - } - if (viewport->vmempool.obinfos != NULL) { - BLI_memblock_destroy(viewport->vmempool.obinfos, NULL); - } - if (viewport->vmempool.cullstates != NULL) { - BLI_memblock_destroy(viewport->vmempool.cullstates, NULL); - } - if (viewport->vmempool.shgroups != NULL) { - BLI_memblock_destroy(viewport->vmempool.shgroups, NULL); - } - if (viewport->vmempool.uniforms != NULL) { - BLI_memblock_destroy(viewport->vmempool.uniforms, NULL); - } - if (viewport->vmempool.views != NULL) { - BLI_memblock_destroy(viewport->vmempool.views, NULL); - } - if (viewport->vmempool.passes != NULL) { - BLI_memblock_destroy(viewport->vmempool.passes, NULL); - } - if (viewport->vmempool.images != NULL) { - BLI_memblock_iter iter; - GPUTexture **tex; - BLI_memblock_iternew(viewport->vmempool.images, &iter); - while ((tex = BLI_memblock_iterstep(&iter))) { - GPU_texture_free(*tex); - } - BLI_memblock_destroy(viewport->vmempool.images, NULL); - } - if (viewport->vmempool.obattrs_ubo_pool != NULL) { - DRW_uniform_attrs_pool_free(viewport->vmempool.obattrs_ubo_pool); + if (viewport->draw_data) { + DRW_viewport_data_free(viewport->draw_data); } - for (int i = 0; i < viewport->vmempool.ubo_len; i++) { - GPU_uniformbuf_free(viewport->vmempool.matrices_ubo[i]); - GPU_uniformbuf_free(viewport->vmempool.obinfos_ubo[i]); - } - MEM_SAFE_FREE(viewport->vmempool.matrices_ubo); - MEM_SAFE_FREE(viewport->vmempool.obinfos_ubo); - - DRW_instance_data_list_free(viewport->idatalist); - MEM_freeN(viewport->idatalist); + gpu_viewport_textures_free(viewport); BKE_color_managed_view_settings_free(&viewport->view_settings); gpu_viewport_batch_free(viewport); MEM_freeN(viewport); } - -GPUFrameBuffer *GPU_viewport_framebuffer_default_get(GPUViewport *viewport) -{ - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); - return fbl->default_fb; -} - -GPUFrameBuffer *GPU_viewport_framebuffer_overlay_get(GPUViewport *viewport) -{ - DefaultFramebufferList *fbl = GPU_viewport_framebuffer_list_get(viewport); - return fbl->overlay_fb; -} diff --git a/source/blender/gpu/opengl/gl_state.cc b/source/blender/gpu/opengl/gl_state.cc index 1106e3dab50..d737cf88a13 100644 --- a/source/blender/gpu/opengl/gl_state.cc +++ b/source/blender/gpu/opengl/gl_state.cc @@ -52,6 +52,7 @@ GLStateManager::GLStateManager() glDisable(GL_DITHER); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPrimitiveRestartIndex((GLuint)0xFFFFFFFF); diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl index f07bd7f1d6f..134a7d00127 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_frag.glsl @@ -1,11 +1,40 @@ in float colorGradient; in vec4 finalColor; +in float lineU; +flat in float lineLength; +flat in float dashFactor; +flat in float dashAlpha; +flat in int isMainLine; out vec4 fragColor; +#define DASH_WIDTH 10.0 +#define ANTIALIAS 1.0 +#define MINIMUM_ALPHA 0.5 + void main() { fragColor = finalColor; + + if ((isMainLine != 0) && (dashFactor < 1.0)) { + float distance_along_line = lineLength * lineU; + float normalized_distance = fract(distance_along_line / DASH_WIDTH); + + /* Checking if `normalized_distance <= dashFactor` is already enough for a basic + * dash, however we want to handle a nice antialias. */ + + float dash_center = DASH_WIDTH * dashFactor * 0.5; + float normalized_distance_triangle = + 1.0 - abs((fract((distance_along_line - dash_center) / DASH_WIDTH)) * 2.0 - 1.0); + float t = ANTIALIAS / DASH_WIDTH; + float slope = 1.0 / (2.0 * t); + + float unclamped_alpha = 1.0 - slope * (normalized_distance_triangle - dashFactor + t); + float alpha = max(dashAlpha, min(unclamped_alpha, 1.0)); + + fragColor.a *= alpha; + } + fragColor.a *= smoothstep(1.0, 0.1, abs(colorGradient)); } diff --git a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl index aae7f641af8..8325568988c 100644 --- a/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_2D_nodelink_vert.glsl @@ -17,13 +17,18 @@ in vec2 P1; in vec2 P2; in vec2 P3; in ivec4 colid_doarrow; +in vec4 start_color; +in vec4 end_color; in ivec2 domuted; in float dim_factor; +in float thickness; +in float dash_factor; +in float dash_alpha; uniform vec4 colors[6]; -# define colStart colors[colid_doarrow[0]] -# define colEnd colors[colid_doarrow[1]] +# define colStart (colid_doarrow[0] < 3 ? start_color : colors[colid_doarrow[0]]) +# define colEnd (colid_doarrow[1] < 3 ? end_color : colors[colid_doarrow[1]]) # define colShadow colors[colid_doarrow[2]] # define doArrow (colid_doarrow[3] != 0) # define doMuted (domuted[0] != 0) @@ -41,6 +46,9 @@ uniform vec4 colors[3]; uniform bool doArrow; uniform bool doMuted; uniform float dim_factor; +uniform float thickness; +uniform float dash_factor; +uniform float dash_alpha; # define colShadow colors[0] # define colStart colors[1] @@ -54,9 +62,28 @@ uniform mat4 ModelViewProjectionMatrix; out float colorGradient; out vec4 finalColor; +out float lineU; +flat out float lineLength; +flat out float dashFactor; +flat out float dashAlpha; +flat out int isMainLine; + +/* Define where along the noodle the gradient will starts and ends. + * Use 0.25 instead of 0.35-0.65, because of a visual shift issue. */ +const float start_gradient_threshold = 0.25; +const float end_gradient_threshold = 0.55; void main(void) { + /* Parameters for the dashed line. */ + isMainLine = expand.y != 1.0 ? 0 : 1; + dashFactor = dash_factor; + dashAlpha = dash_alpha; + /* Approximate line length, no need for real bezier length calculation. */ + lineLength = distance(P0, P3); + /* TODO: Incorrect U, this leads to non-uniform dash distribution. */ + lineU = uv.x; + float t = uv.x; float t2 = t * t; float t2_3 = 3.0 * t2; @@ -93,7 +120,16 @@ void main(void) } else { /* Second pass */ - finalColor = mix(colStart, colEnd, uv.x); + if (uv.x < start_gradient_threshold) { + finalColor = colStart; + } + else if (uv.x > end_gradient_threshold) { + finalColor = colEnd; + } + else { + /* Add 0.1 to avoid a visual shift issue. */ + finalColor = mix(colStart, colEnd, uv.x + 0.1); + } expand_dist *= 0.5; if (doMuted) { finalColor[3] = 0.65; @@ -103,7 +139,7 @@ void main(void) finalColor[3] *= dim_factor; /* Expand into a line */ - gl_Position.xy += exp_axis * expandSize * expand_dist; + gl_Position.xy += exp_axis * expandSize * expand_dist * thickness; /* If the link is not muted or is not a reroute arrow the points are squashed to the center of * the line. Magic numbers are defined in drawnode.c */ diff --git a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl index f7bf3d33361..193a4190cbf 100644 --- a/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl +++ b/source/blender/gpu/shaders/gpu_shader_codegen_lib.glsl @@ -42,6 +42,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], const sampler return orco_madd[0].xyz + orco * orco_madd[1].xyz; } +float hair_len_get(int id, const samplerBuffer len) +{ + return texelFetch(len, id).x; +} + vec4 tangent_get(const samplerBuffer attr, mat3 normalmat) { /* Unsupported */ @@ -71,6 +76,11 @@ vec3 orco_get(vec3 local_pos, mat4 modelmatinv, vec4 orco_madd[2], vec4 orco) } } +float hair_len_get(int id, const float len) +{ + return len; +} + vec4 tangent_get(vec4 attr, mat3 normalmat) { vec4 tangent; diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl similarity index 63% rename from source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl rename to source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl index 1c4039bc590..a3b61dca8b4 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_frag.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_frag.glsl @@ -1,3 +1,16 @@ + +/* Values in GPU_shader.h. */ +#define GPU_KEYFRAME_SHAPE_DIAMOND (1 << 0) +#define GPU_KEYFRAME_SHAPE_CIRCLE (1 << 1) +#define GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL (1 << 2) +#define GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL (1 << 3) +#define GPU_KEYFRAME_SHAPE_INNER_DOT (1 << 4) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MAX (1 << 8) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIN (1 << 9) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIXED (1 << 10) +#define GPU_KEYFRAME_SHAPE_SQUARE \ + (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL) + flat in vec4 radii; flat in vec4 thresholds; @@ -27,24 +40,24 @@ void main() float outline_dist = -1.0; /* Diamond outline */ - if (test(0x1)) { + if (test(GPU_KEYFRAME_SHAPE_DIAMOND)) { outline_dist = max(outline_dist, radius - radii[0]); } /* Circle outline */ - if (test(0x2)) { + if (test(GPU_KEYFRAME_SHAPE_CIRCLE)) { radius = length(absPos); outline_dist = max(outline_dist, radius - radii[1]); } /* Top & Bottom clamp */ - if (test(0x4)) { + if (test(GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL)) { outline_dist = max(outline_dist, absPos.y - radii[2]); } /* Left & Right clamp */ - if (test(0x8)) { + if (test(GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)) { outline_dist = max(outline_dist, absPos.x - radii[2]); } @@ -53,20 +66,20 @@ void main() /* Inside the outline. */ if (outline_dist < 0) { /* Middle dot */ - if (test(0x10)) { - alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], radius)); + if (test(GPU_KEYFRAME_SHAPE_INNER_DOT)) { + alpha = max(alpha, 1 - smoothstep(thresholds[2], thresholds[3], length(absPos))); } /* Up and down arrow-like shading. */ - if (test(0x300)) { + if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MAX | GPU_KEYFRAME_SHAPE_ARROW_END_MIN)) { float ypos = -1.0; /* Up arrow (maximum) */ - if (test(0x100)) { + if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MAX)) { ypos = max(ypos, pos.y); } /* Down arrow (minimum) */ - if (test(0x200)) { + if (test(GPU_KEYFRAME_SHAPE_ARROW_END_MIN)) { ypos = max(ypos, -pos.y); } @@ -75,7 +88,7 @@ void main() float minmax_step = smoothstep(thresholds[0], thresholds[1], minmax_dist * minmax_scale); /* Reduced alpha for uncertain extremes. */ - float minmax_alpha = test(0x400) ? 0.55 : 0.85; + float minmax_alpha = test(GPU_KEYFRAME_SHAPE_ARROW_END_MIXED) ? 0.55 : 0.85; alpha = max(alpha, minmax_step * minmax_alpha); } diff --git a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl similarity index 72% rename from source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl rename to source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl index 2ba89230d80..18e8b76ba23 100644 --- a/source/blender/gpu/shaders/gpu_shader_keyframe_diamond_vert.glsl +++ b/source/blender/gpu/shaders/gpu_shader_keyframe_shape_vert.glsl @@ -1,4 +1,16 @@ +/* Values in GPU_shader.h. */ +#define GPU_KEYFRAME_SHAPE_DIAMOND (1 << 0) +#define GPU_KEYFRAME_SHAPE_CIRCLE (1 << 1) +#define GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL (1 << 2) +#define GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL (1 << 3) +#define GPU_KEYFRAME_SHAPE_INNER_DOT (1 << 4) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MAX (1 << 8) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIN (1 << 9) +#define GPU_KEYFRAME_SHAPE_ARROW_END_MIXED (1 << 10) +#define GPU_KEYFRAME_SHAPE_SQUARE \ + (GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL) + uniform mat4 ModelViewProjectionMatrix; uniform vec2 ViewportSize = vec2(-1, -1); uniform float outline_scale = 1.0; @@ -49,8 +61,9 @@ void main() finalOutlineColor = outlineColor; finalFlags = flags; - if (!test(0xF)) { - finalFlags |= 1; + if (!test(GPU_KEYFRAME_SHAPE_DIAMOND | GPU_KEYFRAME_SHAPE_CIRCLE | + GPU_KEYFRAME_SHAPE_CLIPPED_VERTICAL | GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL)) { + finalFlags |= GPU_KEYFRAME_SHAPE_DIAMOND; } /* Size-dependent line thickness. */ diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl new file mode 100644 index 00000000000..514409f7fdf --- /dev/null +++ b/source/blender/gpu/shaders/material/gpu_shader_material_float_curve.glsl @@ -0,0 +1,33 @@ +/* ext is vec4(in_x, in_dy, out_x, out_dy). */ +float curve_float_extrapolate(float x, float y, vec4 ext) +{ + if (x < 0.0) { + return y + x * ext.y; + } + else if (x > 1.0) { + return y + (x - 1.0) * ext.w; + } + else { + return y; + } +} + +#define RANGE_RESCALE(x, min, range) ((x - min) * range) + +void curve_float(float fac, + float vec, + sampler1DArray curvemap, + float layer, + float range, + vec4 ext, + out float outvec) +{ + float xyz_min = ext.x; + vec = RANGE_RESCALE(vec, xyz_min, range); + + outvec = texture(curvemap, vec2(vec, layer)).x; + + outvec = curve_float_extrapolate(vec, outvec, ext); + + outvec = mix(vec, outvec, fac); +} diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl index f25691c1a83..95f2be4bd44 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_fractal_noise.glsl @@ -5,7 +5,7 @@ float fractal_noise(float p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); @@ -34,7 +34,7 @@ float fractal_noise(vec2 p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); @@ -63,7 +63,7 @@ float fractal_noise(vec3 p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); @@ -92,7 +92,7 @@ float fractal_noise(vec4 p, float octaves, float roughness) float amp = 1.0; float maxamp = 0.0; float sum = 0.0; - octaves = clamp(octaves, 0.0, 16.0); + octaves = clamp(octaves, 0.0, 15.0); int n = int(octaves); for (int i = 0; i <= n; i++) { float t = noise(fscale * p); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl index 6330daa4391..6ffa6b59572 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_hair_info.glsl @@ -10,12 +10,15 @@ float wang_hash_noise(uint s) return fract(float(s) / 4294967296.0); } -void node_hair_info(out float is_strand, +void node_hair_info(float hair_length, + out float is_strand, out float intercept, + out float length, out float thickness, out vec3 tangent, out float random) { + length = hair_length; #ifdef HAIR_SHADER is_strand = 1.0; intercept = hairTime; diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl index d77259638fd..bba84c2be52 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_principled.glsl @@ -19,6 +19,8 @@ void node_bsdf_principled(vec4 base_color, float subsurface, vec3 subsurface_radius, vec4 subsurface_color, + float subsurface_ior, + float subsurface_anisotropy, float metallic, float specular, float specular_tint, @@ -201,6 +203,6 @@ void node_bsdf_principled(vec4 base_color, #else /* clang-format off */ /* Stub principled because it is not compatible with volumetrics. */ -# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, result) (result = CLOSURE_DEFAULT) +# define node_bsdf_principled(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, aa, bb, cc, dd, ee, ff, result) (result = CLOSURE_DEFAULT) /* clang-format on */ #endif diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl index a5fdc7a2337..f0f2f79c60e 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_shader_to_rgba.glsl @@ -1,14 +1,22 @@ #ifndef VOLUMETRICS + +CLOSURE_EVAL_FUNCTION_DECLARE_1(node_shader_to_rgba, Glossy) + void node_shader_to_rgba(Closure cl, out vec4 outcol, out float outalpha) { vec4 spec_accum = vec4(0.0); if (ssrToggle && FLAG_TEST(cl.flag, CLOSURE_SSR_FLAG)) { - vec3 V = cameraVec(worldPosition); + CLOSURE_VARS_DECLARE_1(Glossy); + vec3 vN = normal_decode(cl.ssr_normal, viewCameraVec(viewPosition)); vec3 N = transform_direction(ViewMatrixInverse, vN); - float roughness = cl.ssr_data.a; - float roughnessSquared = max(1e-3, roughness * roughness); - fallback_cubemap(N, V, worldPosition, viewPosition, roughness, roughnessSquared, spec_accum); + + in_Glossy_0.N = N; /* Normalized during eval. */ + in_Glossy_0.roughness = cl.ssr_data.a; + + CLOSURE_EVAL_FUNCTION_1(node_shader_to_rgba, Glossy); + + spec_accum.rgb = out_Glossy_0.radiance; } outalpha = saturate(1.0 - avg(cl.transmittance)); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl index 5129bf71903..d0c159cdf37 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_subsurface_scattering.glsl @@ -5,8 +5,8 @@ CLOSURE_EVAL_FUNCTION_DECLARE_1(node_subsurface_scattering, Diffuse) void node_subsurface_scattering(vec4 color, float scale, vec3 radius, - float sharpen, - float texture_blur, + float ior, + float anisotropy, vec3 N, float sss_id, out Closure result) @@ -20,15 +20,7 @@ void node_subsurface_scattering(vec4 color, result = CLOSURE_DEFAULT; - /* Not perfect for texture_blur values between 0.0 and 1.0. - * Interpolate between separated color and color applied on irradiance. */ - float one_minus_texture_blur = 1.0 - texture_blur; - vec3 sss_albedo = color.rgb * one_minus_texture_blur + texture_blur; - vec3 radiance_tint = color.rgb * texture_blur + one_minus_texture_blur; - /* Consider output radiance as irradiance. */ - out_Diffuse_0.radiance *= radiance_tint; - - closure_load_sss_data(scale, out_Diffuse_0.radiance, sss_albedo, int(sss_id), result); + closure_load_sss_data(scale, out_Diffuse_0.radiance, color.rgb, int(sss_id), result); /* TODO(fclem) Try to not use this. */ closure_load_ssr_data(vec3(0.0), 0.0, in_Diffuse_0.N, -1.0, result); diff --git a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl index 7ecca286acd..586385b7e86 100644 --- a/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl +++ b/source/blender/gpu/shaders/material/gpu_shader_material_tex_musgrave.glsl @@ -19,7 +19,7 @@ void node_tex_musgrave_fBm_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -59,7 +59,7 @@ void node_tex_musgrave_multi_fractal_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -100,7 +100,7 @@ void node_tex_musgrave_hetero_terrain_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -146,7 +146,7 @@ void node_tex_musgrave_hybrid_multi_fractal_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -196,7 +196,7 @@ void node_tex_musgrave_ridged_multi_fractal_1d(vec3 co, { float p = w * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -241,7 +241,7 @@ void node_tex_musgrave_fBm_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -281,7 +281,7 @@ void node_tex_musgrave_multi_fractal_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -322,7 +322,7 @@ void node_tex_musgrave_hetero_terrain_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -368,7 +368,7 @@ void node_tex_musgrave_hybrid_multi_fractal_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -418,7 +418,7 @@ void node_tex_musgrave_ridged_multi_fractal_2d(vec3 co, { vec2 p = co.xy * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -463,7 +463,7 @@ void node_tex_musgrave_fBm_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -503,7 +503,7 @@ void node_tex_musgrave_multi_fractal_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -544,7 +544,7 @@ void node_tex_musgrave_hetero_terrain_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -590,7 +590,7 @@ void node_tex_musgrave_hybrid_multi_fractal_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -640,7 +640,7 @@ void node_tex_musgrave_ridged_multi_fractal_3d(vec3 co, { vec3 p = co * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -685,7 +685,7 @@ void node_tex_musgrave_fBm_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 0.0; @@ -725,7 +725,7 @@ void node_tex_musgrave_multi_fractal_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float value = 1.0; @@ -766,7 +766,7 @@ void node_tex_musgrave_hetero_terrain_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -812,7 +812,7 @@ void node_tex_musgrave_hybrid_multi_fractal_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); @@ -862,7 +862,7 @@ void node_tex_musgrave_ridged_multi_fractal_4d(vec3 co, { vec4 p = vec4(co, w) * scale; float H = max(dimension, 1e-5); - float octaves = clamp(detail, 0.0, 16.0); + float octaves = clamp(detail, 0.0, 15.0); float lacunarity = max(lac, 1e-5); float pwHL = pow(lacunarity, -H); diff --git a/source/blender/gpu/tests/gpu_shader_builtin_test.cc b/source/blender/gpu/tests/gpu_shader_builtin_test.cc index f0061a6bf5c..523a7e5b881 100644 --- a/source/blender/gpu/tests/gpu_shader_builtin_test.cc +++ b/source/blender/gpu/tests/gpu_shader_builtin_test.cc @@ -32,7 +32,7 @@ static void test_shader_builtin() test_compile_builtin_shader(GPU_SHADER_3D_POINT_UNIFORM_SIZE_UNIFORM_COLOR_OUTLINE_AA); test_compile_builtin_shader(GPU_SHADER_TEXT, GPU_SHADER_CFG_DEFAULT); - test_compile_builtin_shader(GPU_SHADER_KEYFRAME_DIAMOND, GPU_SHADER_CFG_DEFAULT); + test_compile_builtin_shader(GPU_SHADER_KEYFRAME_SHAPE, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_SIMPLE_LIGHTING, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_UNIFORM_COLOR, GPU_SHADER_CFG_DEFAULT); test_compile_builtin_shader(GPU_SHADER_2D_FLAT_COLOR, GPU_SHADER_CFG_DEFAULT); diff --git a/source/blender/ikplugin/intern/itasc_plugin.cpp b/source/blender/ikplugin/intern/itasc_plugin.cpp index b9411f6dd2d..a9e1692ebf0 100644 --- a/source/blender/ikplugin/intern/itasc_plugin.cpp +++ b/source/blender/ikplugin/intern/itasc_plugin.cpp @@ -644,7 +644,7 @@ static bool base_callback(const iTaSC::Timestamp ×tamp, ikscene->baseFrame = iTaSC::F_identity; } next.setValue(&rootmat[0][0]); - /* if there is a polar target (only during solving otherwise we don't have end efffector) */ + /* If there is a polar target (only during solving otherwise we don't have end effector). */ if (ikscene->polarConstraint && timestamp.update) { /* compute additional rotation of base frame so that armature follows the polar target */ float imat[4][4]; /* IK tree base inverse matrix */ diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index dd8e6549e24..7bfd1074ac6 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -758,7 +758,7 @@ void IMB_processor_apply_threaded_scanlines(int total_scanlines, void IMB_transform(struct ImBuf *src, struct ImBuf *dst, - float transform_matrix[3][3], + float transform_matrix[4][4], struct rctf *src_crop, const eIMBInterpolationFilterMode filter); diff --git a/source/blender/imbuf/IMB_moviecache.h b/source/blender/imbuf/IMB_moviecache.h index 8e5f15a64a0..d32346a8418 100644 --- a/source/blender/imbuf/IMB_moviecache.h +++ b/source/blender/imbuf/IMB_moviecache.h @@ -59,7 +59,7 @@ void IMB_moviecache_set_priority_callback(struct MovieCache *cache, void IMB_moviecache_put(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); bool IMB_moviecache_put_if_possible(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); -struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey); +struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey, bool *r_is_cached_empty); void IMB_moviecache_remove(struct MovieCache *cache, void *userkey); bool IMB_moviecache_has_frame(struct MovieCache *cache, void *userkey); void IMB_moviecache_free(struct MovieCache *cache); diff --git a/source/blender/imbuf/intern/IMB_indexer.h b/source/blender/imbuf/intern/IMB_indexer.h index 37309ccc13a..6c66b11df4f 100644 --- a/source/blender/imbuf/intern/IMB_indexer.h +++ b/source/blender/imbuf/intern/IMB_indexer.h @@ -33,7 +33,7 @@ * a) different time-codes within one file (like DTS/PTS, Time-code-Track, * "implicit" time-codes within DV-files and HDV-files etc.) * b) seeking difficulties within FFMPEG for files with timestamp holes - * c) broken files that miss several frames / have varying framerates + * c) broken files that miss several frames / have varying frame-rates * d) use proxies accordingly * * ... we need index files, that provide us with diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index dbca16ca82b..c11c6d778a1 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -664,11 +664,6 @@ static int startffmpeg(struct anim *anim) anim->duration_in_frames = (int)(stream_dur * av_q2d(frame_rate) + 0.5f); } - double ctx_start = 0; - if (pFormatCtx->start_time != AV_NOPTS_VALUE) { - ctx_start = (double)pFormatCtx->start_time / AV_TIME_BASE; - } - frs_num = frame_rate.num; frs_den = frame_rate.den; @@ -683,7 +678,7 @@ static int startffmpeg(struct anim *anim) anim->frs_sec_base = frs_den; /* Save the relative start time for the video. IE the start time in relation to where playback * starts. */ - anim->start_offset = video_start - ctx_start; + anim->start_offset = video_start; anim->params = 0; @@ -1501,16 +1496,16 @@ static void free_anim_ffmpeg(struct anim *anim) #endif -/* Try next picture to read */ -/* No picture, try to open next animation */ -/* Succeed, remove first image from animation */ - -static ImBuf *anim_getnew(struct anim *anim) +/** + * Try to initialize the #anim struct. + * Returns true on success. + */ +static bool anim_getnew(struct anim *anim) { - struct ImBuf *ibuf = NULL; - + BLI_assert(anim->curtype == ANIM_NONE); if (anim == NULL) { - return NULL; + /* Nothing to initialize. */ + return false; } free_anim_movie(anim); @@ -1523,44 +1518,43 @@ static ImBuf *anim_getnew(struct anim *anim) free_anim_ffmpeg(anim); #endif - if (anim->curtype != 0) { - return NULL; - } anim->curtype = imb_get_anim_type(anim->name); switch (anim->curtype) { - case ANIM_SEQUENCE: - ibuf = IMB_loadiffname(anim->name, anim->ib_flags, anim->colorspace); + case ANIM_SEQUENCE: { + ImBuf *ibuf = IMB_loadiffname(anim->name, anim->ib_flags, anim->colorspace); if (ibuf) { BLI_strncpy(anim->first, anim->name, sizeof(anim->first)); anim->duration_in_frames = 1; + IMB_freeImBuf(ibuf); + } + else { + return false; } break; + } case ANIM_MOVIE: if (startmovie(anim)) { - return NULL; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); /* fake */ break; #ifdef WITH_AVI case ANIM_AVI: if (startavi(anim)) { printf("couldn't start avi\n"); - return NULL; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); break; #endif #ifdef WITH_FFMPEG case ANIM_FFMPEG: if (startffmpeg(anim)) { - return 0; + return false; } - ibuf = IMB_allocImBuf(anim->x, anim->y, 24, 0); break; #endif } - return ibuf; + return true; } struct ImBuf *IMB_anim_previewframe(struct anim *anim) @@ -1594,14 +1588,10 @@ struct ImBuf *IMB_anim_absolute(struct anim *anim, filter_y = (anim->ib_flags & IB_animdeinterlace); if (preview_size == IMB_PROXY_NONE) { - if (anim->curtype == 0) { - ibuf = anim_getnew(anim); - if (ibuf == NULL) { + if (anim->curtype == ANIM_NONE) { + if (!anim_getnew(anim)) { return NULL; } - - IMB_freeImBuf(ibuf); /* ???? */ - ibuf = NULL; } if (position < 0) { diff --git a/source/blender/imbuf/intern/bmp.c b/source/blender/imbuf/intern/bmp.c index 70bb70ec4fa..5b9d78f5614 100644 --- a/source/blender/imbuf/intern/bmp.c +++ b/source/blender/imbuf/intern/bmp.c @@ -151,8 +151,7 @@ ImBuf *imb_bmp_decode(const uchar *mem, size_t size, int flags, char colorspace[ } /* Validate and cross-check offsets and sizes. */ - if (x < 1 || - !(depth == 1 || depth == 4 || depth == 8 || depth == 16 || depth == 24 || depth == 32)) { + if (x < 1 || !(ELEM(depth, 1, 4, 8, 16, 24, 32))) { return NULL; } diff --git a/source/blender/imbuf/intern/colormanagement.c b/source/blender/imbuf/intern/colormanagement.c index 119ef3ff971..c8c6f39507f 100644 --- a/source/blender/imbuf/intern/colormanagement.c +++ b/source/blender/imbuf/intern/colormanagement.c @@ -342,7 +342,7 @@ static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, *cache_handle = NULL; - cache_ibuf = IMB_moviecache_get(moviecache, key); + cache_ibuf = IMB_moviecache_get(moviecache, key, NULL); *cache_handle = cache_ibuf; diff --git a/source/blender/imbuf/intern/imageprocess.c b/source/blender/imbuf/intern/imageprocess.c index 804c9c3eb89..0ec1e4c19d8 100644 --- a/source/blender/imbuf/intern/imageprocess.c +++ b/source/blender/imbuf/intern/imageprocess.c @@ -20,11 +20,10 @@ /** \file * \ingroup imbuf * - * This file was moved here from the src/ directory. It is meant to - * deal with endianness. It resided in a general blending lib. The - * other functions were only used during rendering. This single - * function remained. It should probably move to imbuf/intern/util.c, - * but we'll keep it here for the time being. (nzc) + * This file was moved here from the `src/` directory. + * It is meant to deal with endianness. It resided in a general blending lib. + * The other functions were only used during rendering. This single function remained. + * It should probably move to `imbuf/intern/util.c`, but we'll keep it here for the time being. */ #include @@ -375,38 +374,45 @@ typedef struct TransformUserData { rctf src_crop; } TransformUserData; -static void imb_transform_calc_start_uv(const float transform_matrix[3][3], float r_start_uv[2]) +static void imb_transform_calc_start_uv(const float transform_matrix[4][4], float r_start_uv[2]) { - float orig[2]; - orig[0] = 0.0f; - orig[1] = 0.0f; - mul_v2_m3v2(r_start_uv, transform_matrix, orig); + float r_start_uv_temp[3]; + float orig[3]; + zero_v3(orig); + mul_v3_m4v3(r_start_uv_temp, transform_matrix, orig); + copy_v2_v2(r_start_uv, r_start_uv_temp); } -static void imb_transform_calc_add_x(const float transform_matrix[3][3], +static void imb_transform_calc_add_x(const float transform_matrix[4][4], const float start_uv[2], const int width, float r_add_x[2]) { - float uv_max_x[2]; + float r_add_x_temp[3]; + float uv_max_x[3]; + zero_v3(uv_max_x); uv_max_x[0] = width; uv_max_x[1] = 0.0f; - mul_v2_m3v2(r_add_x, transform_matrix, uv_max_x); - sub_v2_v2(r_add_x, start_uv); - mul_v2_fl(r_add_x, 1.0f / width); + mul_v3_m4v3(r_add_x_temp, transform_matrix, uv_max_x); + sub_v2_v2(r_add_x_temp, start_uv); + mul_v2_fl(r_add_x_temp, 1.0f / width); + copy_v2_v2(r_add_x, r_add_x_temp); } -static void imb_transform_calc_add_y(const float transform_matrix[3][3], +static void imb_transform_calc_add_y(const float transform_matrix[4][4], const float start_uv[2], const int height, float r_add_y[2]) { - float uv_max_y[2]; + float r_add_y_temp[3]; + float uv_max_y[3]; + zero_v3(uv_max_y); uv_max_y[0] = 0.0f; uv_max_y[1] = height; - mul_v2_m3v2(r_add_y, transform_matrix, uv_max_y); - sub_v2_v2(r_add_y, start_uv); - mul_v2_fl(r_add_y, 1.0f / height); + mul_v3_m4v3(r_add_y_temp, transform_matrix, uv_max_y); + sub_v2_v2(r_add_y_temp, start_uv); + mul_v2_fl(r_add_y_temp, 1.0f / height); + copy_v2_v2(r_add_y, r_add_y_temp); } typedef void (*InterpolationColorFunction)( @@ -481,7 +487,7 @@ static ScanlineThreadFunc imb_transform_scanline_func(const eIMBInterpolationFil void IMB_transform(struct ImBuf *src, struct ImBuf *dst, - float transform_matrix[3][3], + float transform_matrix[4][4], struct rctf *src_crop, const eIMBInterpolationFilterMode filter) { diff --git a/source/blender/imbuf/intern/iris.c b/source/blender/imbuf/intern/iris.c index df516d2a5c4..6a7ad87d53d 100644 --- a/source/blender/imbuf/intern/iris.c +++ b/source/blender/imbuf/intern/iris.c @@ -274,7 +274,7 @@ struct ImBuf *imb_loadiris(const uchar *mem, size_t size, int flags, char colors return NULL; } - /* Could pe part of the magic check above, + /* Could be part of the magic check above, * by convention this check only requests the size needed to read it's magic though. */ if (size < HEADER_SIZE) { return NULL; diff --git a/source/blender/imbuf/intern/jp2.c b/source/blender/imbuf/intern/jp2.c index 117e0d97b2e..83e93f16f1b 100644 --- a/source/blender/imbuf/intern/jp2.c +++ b/source/blender/imbuf/intern/jp2.c @@ -848,7 +848,7 @@ static opj_image_t *ibuftoimage(ImBuf *ibuf, opj_cparameters_t *parameters) chanel_colormanage_cb = channel_colormanage_noop; } else { - /* standard linear-to-srgb conversion if float buffer wasn't managed */ + /* standard linear-to-SRGB conversion if float buffer wasn't managed */ chanel_colormanage_cb = linearrgb_to_srgb; } diff --git a/source/blender/imbuf/intern/jpeg.c b/source/blender/imbuf/intern/jpeg.c index 48b5b0c34db..c3a07d7face 100644 --- a/source/blender/imbuf/intern/jpeg.c +++ b/source/blender/imbuf/intern/jpeg.c @@ -40,8 +40,9 @@ #include "IMB_imbuf_types.h" #include "IMB_metadata.h" #include "imbuf.h" -#include "jerror.h" -#include "jpeglib.h" + +#include +#include #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index 6cc1932eff6..6e7b85a300a 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -87,6 +87,8 @@ typedef struct MovieCacheItem { ImBuf *ibuf; MEM_CacheLimiterHandleC *c_handle; void *priority_data; + /* Indicates that #ibuf is null, because there was an error during load. */ + bool added_empty; } MovieCacheItem; static unsigned int moviecache_hashhash(const void *keyv) @@ -120,8 +122,8 @@ static void moviecache_valfree(void *val) PRINT("%s: cache '%s' free item %p buffer %p\n", __func__, cache->name, item, item->ibuf); + MEM_CacheLimiter_unmanage(item->c_handle); if (item->ibuf) { - MEM_CacheLimiter_unmanage(item->c_handle); IMB_freeImBuf(item->ibuf); } @@ -141,11 +143,16 @@ static void check_unused_keys(MovieCache *cache) while (!BLI_ghashIterator_done(&gh_iter)) { const MovieCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); const MovieCacheItem *item = BLI_ghashIterator_getValue(&gh_iter); - bool remove; BLI_ghashIterator_step(&gh_iter); - remove = !item->ibuf; + if (item->added_empty) { + /* Don't remove entries that have been added empty. Those indicate that the image couldn't be + * loaded correctly. */ + continue; + } + + bool remove = !item->ibuf; if (remove) { PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item); @@ -309,7 +316,9 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo IMB_moviecache_init(); } - IMB_refImBuf(ibuf); + if (ibuf != NULL) { + IMB_refImBuf(ibuf); + } key = BLI_mempool_alloc(cache->keys_pool); key->cache_owner = cache; @@ -324,6 +333,7 @@ static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, boo item->cache_owner = cache; item->c_handle = NULL; item->priority_data = NULL; + item->added_empty = ibuf == NULL; if (cache->getprioritydatafp) { item->priority_data = cache->getprioritydatafp(userkey); @@ -365,7 +375,7 @@ bool IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibu size_t mem_in_use, mem_limit, elem_size; bool result = false; - elem_size = get_size_in_memory(ibuf); + elem_size = (ibuf == NULL) ? 0 : get_size_in_memory(ibuf); mem_limit = MEM_CacheLimiter_get_maximum(); BLI_mutex_lock(&limitor_lock); @@ -389,7 +399,7 @@ void IMB_moviecache_remove(MovieCache *cache, void *userkey) BLI_ghash_remove(cache->hash, &key, moviecache_keyfree, moviecache_valfree); } -ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) +ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey, bool *r_is_cached_empty) { MovieCacheKey key; MovieCacheItem *item; @@ -398,6 +408,10 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) key.userkey = userkey; item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key); + if (r_is_cached_empty) { + *r_is_cached_empty = false; + } + if (item) { if (item->ibuf) { BLI_mutex_lock(&limitor_lock); @@ -408,6 +422,9 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) return item->ibuf; } + if (r_is_cached_empty) { + *r_is_cached_empty = true; + } } return NULL; diff --git a/source/blender/imbuf/intern/oiio/openimageio_api.cpp b/source/blender/imbuf/intern/oiio/openimageio_api.cpp index 3ad902a241d..22533b04b58 100644 --- a/source/blender/imbuf/intern/oiio/openimageio_api.cpp +++ b/source/blender/imbuf/intern/oiio/openimageio_api.cpp @@ -221,7 +221,7 @@ struct ImBuf *imb_load_photoshop(const char *filename, int flags, char colorspac string ics = spec.get_string_attribute("oiio:ColorSpace"); BLI_strncpy(file_colorspace, ics.c_str(), IM_MAX_SPACE); - /* only use colorspaces exis */ + /* Only use color-spaces exist. */ if (colormanage_colorspace_get_named(file_colorspace)) { strcpy(colorspace, file_colorspace); } diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index cd323e72003..adf09f8dda8 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -555,7 +555,7 @@ static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flag int xstride = sizeof(float) * channels; int ystride = -xstride * width; - /* last scanline, stride negative */ + /* Last scan-line, stride negative. */ float *rect[4] = {nullptr, nullptr, nullptr, nullptr}; rect[0] = ibuf->rect_float + channels * (height - 1) * width; rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; @@ -654,7 +654,7 @@ struct ExrChannel { char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */ struct MultiViewChannelName *m; /* struct to store all multipart channel info */ - int xstride, ystride; /* step to next pixel, to next scanline */ + int xstride, ystride; /* step to next pixel, to next scan-line. */ float *rect; /* first pointer to write in */ char chan_id; /* quick lookup of channel char */ int view_id; /* quick lookup of channel view */ @@ -681,6 +681,8 @@ struct ExrLayer { ListBase passes; }; +static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data); + /* ********************** */ void *IMB_exr_get_handle(void) @@ -839,12 +841,12 @@ void IMB_exr_add_channel(void *handle, } /* used for output files (from RenderResult) (single and multilayer, single and multiview) */ -int IMB_exr_begin_write(void *handle, - const char *filename, - int width, - int height, - int compress, - const StampData *stamp) +bool IMB_exr_begin_write(void *handle, + const char *filename, + int width, + int height, + int compress, + const StampData *stamp) { ExrHandle *data = (ExrHandle *)handle; Header header(width, height); @@ -962,51 +964,64 @@ void IMB_exrtile_begin_write( } /* read from file */ -int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height) +bool IMB_exr_begin_read( + void *handle, const char *filename, int *width, int *height, const bool parse_channels) { ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; /* 32 is arbitrary, but zero length files crashes exr. */ - if (BLI_exists(filename) && BLI_file_size(filename) > 32) { - /* avoid crash/abort when we don't have permission to write here */ - try { - data->ifile_stream = new IFileStream(filename); - data->ifile = new MultiPartInputFile(*(data->ifile_stream)); - } - catch (const std::exception &) { - delete data->ifile; - delete data->ifile_stream; + if (!(BLI_exists(filename) && BLI_file_size(filename) > 32)) { + return false; + } - data->ifile = nullptr; - data->ifile_stream = nullptr; - } + /* avoid crash/abort when we don't have permission to write here */ + try { + data->ifile_stream = new IFileStream(filename); + data->ifile = new MultiPartInputFile(*(data->ifile_stream)); + } + catch (const std::exception &) { + delete data->ifile; + delete data->ifile_stream; - if (data->ifile) { - Box2i dw = data->ifile->header(0).dataWindow(); - data->width = *width = dw.max.x - dw.min.x + 1; - data->height = *height = dw.max.y - dw.min.y + 1; + data->ifile = nullptr; + data->ifile_stream = nullptr; + } - imb_exr_get_views(*data->ifile, *data->multiView); + if (!data->ifile) { + return false; + } - std::vector channels; - GetChannelsInMultiPartFile(*data->ifile, channels); + Box2i dw = data->ifile->header(0).dataWindow(); + data->width = *width = dw.max.x - dw.min.x + 1; + data->height = *height = dw.max.y - dw.min.y + 1; - for (const MultiViewChannelName &channel : channels) { - IMB_exr_add_channel( - data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false); - - echan = (ExrChannel *)data->channels.last; - echan->m->name = channel.name; - echan->m->view = channel.view; - echan->m->part_number = channel.part_number; - echan->m->internal_name = channel.internal_name; - } - - return 1; + if (parse_channels) { + /* Parse channels into view/layer/pass. */ + if (!imb_exr_multilayer_parse_channels_from_file(data)) { + return false; } } - return 0; + else { + /* Read view and channels without parsing. */ + imb_exr_get_views(*data->ifile, *data->multiView); + + std::vector channels; + GetChannelsInMultiPartFile(*data->ifile, channels); + + for (const MultiViewChannelName &channel : channels) { + IMB_exr_add_channel( + data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channel.name; + echan->m->view = channel.view; + echan->m->part_number = channel.part_number; + echan->m->internal_name = channel.internal_name; + } + } + + return true; } /* still clumsy name handling, layers/channels can be ordered as list in list later */ @@ -1112,7 +1127,7 @@ void IMB_exr_write_channels(void *handle) } for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - /* Writing starts from last scanline, stride negative. */ + /* Writing starts from last scan-line, stride negative. */ if (echan->use_half_float) { float *rect = echan->rect; half *cur = current_rect_half; @@ -1254,7 +1269,7 @@ void IMB_exr_read_channels(void *handle) if (!flip) { /* Inverse correct first pixel for data-window coordinates. */ rect -= echan->xstride * (dw.min.x - dw.min.y * data->width); - /* move to last scanline to flip to Blender convention */ + /* Move to last scan-line to flip to Blender convention. */ rect += echan->xstride * (data->height - 1) * data->width; ystride = -ystride; } @@ -1524,25 +1539,8 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname) return pass; } -/* creates channels, makes a hierarchy and assigns memory to channels */ -static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, - MultiPartInputFile &file, - int width, - int height) +static bool imb_exr_multilayer_parse_channels_from_file(ExrHandle *data) { - ExrLayer *lay; - ExrPass *pass; - ExrChannel *echan; - ExrHandle *data = (ExrHandle *)IMB_exr_get_handle(); - int a; - char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; - - data->ifile_stream = &file_stream; - data->ifile = &file; - - data->width = width; - data->height = height; - std::vector channels; GetChannelsInMultiPartFile(*data->ifile, channels); @@ -1552,7 +1550,7 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, IMB_exr_add_channel( data, nullptr, channel.name.c_str(), channel.view.c_str(), 0, 0, nullptr, false); - echan = (ExrChannel *)data->channels.last; + ExrChannel *echan = (ExrChannel *)data->channels.last; echan->m->name = channel.name; echan->m->view = channel.view; echan->m->part_number = channel.part_number; @@ -1561,7 +1559,9 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, /* now try to sort out how to assign memory to the channels */ /* first build hierarchical layer list */ - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + ExrChannel *echan = (ExrChannel *)data->channels.first; + for (; echan; echan = echan->next) { + char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; if (imb_exr_split_channel_name(echan, layname, passname)) { const char *view = echan->m->view.c_str(); @@ -1591,21 +1591,20 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, } if (echan) { printf("error, too many channels in one pass: %s\n", echan->m->name.c_str()); - IMB_exr_close(data); - return nullptr; + return false; } /* with some heuristics, try to merge the channels in buffers */ - for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) { - for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { + for (ExrLayer *lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) { + for (ExrPass *pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { if (pass->totchan) { - pass->rect = (float *)MEM_callocN(width * height * pass->totchan * sizeof(float), - "pass rect"); + pass->rect = (float *)MEM_callocN( + data->width * data->height * pass->totchan * sizeof(float), "pass rect"); if (pass->totchan == 1) { - echan = pass->chan[0]; + ExrChannel *echan = pass->chan[0]; echan->rect = pass->rect; echan->xstride = 1; - echan->ystride = width; + echan->ystride = data->width; pass->chan_id[0] = echan->chan_id; } else { @@ -1634,20 +1633,20 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, lookup[(unsigned int)'V'] = 1; lookup[(unsigned int)'A'] = 2; } - for (a = 0; a < pass->totchan; a++) { + for (int a = 0; a < pass->totchan; a++) { echan = pass->chan[a]; echan->rect = pass->rect + lookup[(unsigned int)echan->chan_id]; echan->xstride = pass->totchan; - echan->ystride = width * pass->totchan; + echan->ystride = data->width * pass->totchan; pass->chan_id[(unsigned int)lookup[(unsigned int)echan->chan_id]] = echan->chan_id; } } else { /* unknown */ - for (a = 0; a < pass->totchan; a++) { - echan = pass->chan[a]; + for (int a = 0; a < pass->totchan; a++) { + ExrChannel *echan = pass->chan[a]; echan->rect = pass->rect + a; echan->xstride = pass->totchan; - echan->ystride = width * pass->totchan; + echan->ystride = data->width * pass->totchan; pass->chan_id[a] = echan->chan_id; } } @@ -1656,6 +1655,28 @@ static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, } } + return true; +} + +/* creates channels, makes a hierarchy and assigns memory to channels */ +static ExrHandle *imb_exr_begin_read_mem(IStream &file_stream, + MultiPartInputFile &file, + int width, + int height) +{ + ExrHandle *data = (ExrHandle *)IMB_exr_get_handle(); + + data->ifile_stream = &file_stream; + data->ifile = &file; + + data->width = width; + data->height = height; + + if (!imb_exr_multilayer_parse_channels_from_file(data)) { + IMB_exr_close(data); + return nullptr; + } + return data; } @@ -1988,7 +2009,7 @@ struct ImBuf *imb_load_openexr(const unsigned char *mem, /* Inverse correct first pixel for data-window * coordinates (- dw.min.y because of y flip). */ first = ibuf->rect_float - 4 * (dw.min.x - dw.min.y * width); - /* but, since we read y-flipped (negative y stride) we move to last scanline */ + /* But, since we read y-flipped (negative y stride) we move to last scan-line. */ first += 4 * (height - 1) * width; if (num_rgb_channels > 0) { diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h index 556717ad618..82a5d161ded 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/intern/openexr/openexr_multi.h @@ -50,13 +50,14 @@ void IMB_exr_add_channel(void *handle, float *rect, bool use_half_float); -int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height); -int IMB_exr_begin_write(void *handle, - const char *filename, - int width, - int height, - int compress, - const struct StampData *stamp); +bool IMB_exr_begin_read( + void *handle, const char *filename, int *width, int *height, const bool parse_channels); +bool IMB_exr_begin_write(void *handle, + const char *filename, + int width, + int height, + int compress, + const struct StampData *stamp); void IMB_exrtile_begin_write( void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley); diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 51bc2094053..9b4d6178613 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -43,21 +43,22 @@ void IMB_exr_add_channel(void * /*handle*/, { } -int IMB_exr_begin_read(void * /*handle*/, - const char * /*filename*/, - int * /*width*/, - int * /*height*/) -{ - return 0; -} -int IMB_exr_begin_write(void * /*handle*/, +bool IMB_exr_begin_read(void * /*handle*/, const char * /*filename*/, - int /*width*/, - int /*height*/, - int /*compress*/, - const struct StampData * /*stamp*/) + int * /*width*/, + int * /*height*/, + const bool /*add_channels*/) { - return 0; + return false; +} +bool IMB_exr_begin_write(void * /*handle*/, + const char * /*filename*/, + int /*width*/, + int /*height*/, + int /*compress*/, + const struct StampData * /*stamp*/) +{ + return false; } void IMB_exrtile_begin_write(void * /*handle*/, const char * /*filename*/, diff --git a/source/blender/imbuf/intern/png.c b/source/blender/imbuf/intern/png.c index 399fd487065..aaf56b1daa0 100644 --- a/source/blender/imbuf/intern/png.c +++ b/source/blender/imbuf/intern/png.c @@ -23,7 +23,7 @@ * \todo Save floats as 16 bits per channel, currently readonly. */ -#include "png.h" +#include #include "BLI_fileops.h" #include "BLI_math.h" @@ -153,7 +153,7 @@ bool imb_savepng(struct ImBuf *ibuf, const char *filepath, int flags) chanel_colormanage_cb = channel_colormanage_noop; } else { - /* standard linear-to-srgb conversion if float buffer wasn't managed */ + /* Standard linear-to-SRGB conversion if float buffer wasn't managed. */ chanel_colormanage_cb = linearrgb_to_srgb; } diff --git a/source/blender/imbuf/intern/radiance_hdr.c b/source/blender/imbuf/intern/radiance_hdr.c index 94b2a62aa26..7f4e4dd31df 100644 --- a/source/blender/imbuf/intern/radiance_hdr.c +++ b/source/blender/imbuf/intern/radiance_hdr.c @@ -294,7 +294,7 @@ struct ImBuf *imb_loadhdr(const unsigned char *mem, break; } for (x = 0; x < width; x++) { - /* convert to ldr */ + /* Convert to LDR. */ RGBE2FLOAT(sline[x], fcol); *rect_float++ = fcol[RED]; *rect_float++ = fcol[GRN]; @@ -328,7 +328,7 @@ static int fwritecolrs( rgbe_scan = (RGBE *)MEM_mallocN(sizeof(RGBE) * width, "radhdr_write_tmpscan"); - /* convert scanline */ + /* Convert scan-line. */ for (size_t i = 0, j = 0; i < width; i++) { if (fpscan) { fcol[RED] = fpscan[j]; diff --git a/source/blender/imbuf/intern/readimage.c b/source/blender/imbuf/intern/readimage.c index 50210650f05..c75bdfa375c 100644 --- a/source/blender/imbuf/intern/readimage.c +++ b/source/blender/imbuf/intern/readimage.c @@ -126,7 +126,7 @@ ImBuf *IMB_ibImageFromMemory(const unsigned char *mem, } if ((flags & IB_test) == 0) { - fprintf(stderr, "%s: unknown fileformat (%s)\n", __func__, descr); + fprintf(stderr, "%s: unknown file-format (%s)\n", __func__, descr); } return NULL; diff --git a/source/blender/imbuf/intern/rotate.c b/source/blender/imbuf/intern/rotate.c index c2fc2190ce5..83dc29aa107 100644 --- a/source/blender/imbuf/intern/rotate.c +++ b/source/blender/imbuf/intern/rotate.c @@ -69,7 +69,7 @@ void IMB_flipy(struct ImBuf *ibuf) topf = ibuf->rect_float; bottomf = topf + 4 * ((y - 1) * x); - linef = MEM_mallocN(4 * x * sizeof(float), "linebuff"); + linef = MEM_mallocN(4 * x * sizeof(float), "linebuf"); y >>= 1; diff --git a/source/blender/imbuf/intern/targa.c b/source/blender/imbuf/intern/targa.c index 8ed0b8b535c..333e29e6d97 100644 --- a/source/blender/imbuf/intern/targa.c +++ b/source/blender/imbuf/intern/targa.c @@ -192,7 +192,7 @@ static bool makebody_tga(ImBuf *ibuf, FILE *file, int (*out)(unsigned int, FILE else { while (*rect++ == this) { /* seek for first different byte */ if (--bytes == 0) { - break; /* oor end of line */ + break; /* Or end of line. */ } } rect--; @@ -470,7 +470,7 @@ static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem if (psize & 2) { if (psize & 1) { - /* order = bgra */ + /* Order = BGRA. */ cp[0] = mem[3]; cp[1] = mem[0]; cp[2] = mem[1]; @@ -512,7 +512,7 @@ static void decodetarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem while (count > 0) { if (psize & 2) { if (psize & 1) { - /* order = bgra */ + /* Order = BGRA. */ cp[0] = mem[3]; cp[1] = mem[0]; cp[2] = mem[1]; @@ -589,7 +589,7 @@ static void ldtarga(struct ImBuf *ibuf, const unsigned char *mem, size_t mem_siz if (psize & 2) { if (psize & 1) { - /* order = bgra */ + /* Order = BGRA. */ cp[0] = mem[3]; cp[1] = mem[0]; cp[2] = mem[1]; diff --git a/source/blender/imbuf/intern/tiff.c b/source/blender/imbuf/intern/tiff.c index d9e1db27ef0..623c00fa670 100644 --- a/source/blender/imbuf/intern/tiff.c +++ b/source/blender/imbuf/intern/tiff.c @@ -52,7 +52,7 @@ #include "IMB_colormanagement.h" #include "IMB_colormanagement_intern.h" -#include "tiffio.h" +#include #ifdef WIN32 # include "utfconv.h" @@ -457,7 +457,7 @@ static int imb_read_tiff_pixels(ImBuf *ibuf, TIFF *image) } /* simple RGBA image */ - if (!(bitspersample == 32 || bitspersample == 16)) { + if (!(ELEM(bitspersample, 32, 16))) { success |= TIFFReadRGBAImage(image, ibuf->x, ibuf->y, tmpibuf->rect, 0); } /* contiguous channels: RGBRGBRGB */ @@ -895,9 +895,7 @@ bool imb_savetiff(ImBuf *ibuf, const char *filepath, int flags) copy_v3_v3(rgb, &fromf[from_i]); } else { - /* Standard linear-to-srgb conversion if float buffer - * wasn't managed. - */ + /* Standard linear-to-SRGB conversion if float buffer wasn't managed. */ linearrgb_to_srgb_v3_v3(rgb, &fromf[from_i]); } if (channels_in_float == 4) { diff --git a/source/blender/io/alembic/intern/abc_customdata.cc b/source/blender/io/alembic/intern/abc_customdata.cc index e3ed897458d..087d60f8896 100644 --- a/source/blender/io/alembic/intern/abc_customdata.cc +++ b/source/blender/io/alembic/intern/abc_customdata.cc @@ -247,13 +247,13 @@ void write_generated_coordinates(const OCompoundProperty &prop, CDStreamConfig & coords[vertex_idx].setValue(orco_yup[0], orco_yup[1], orco_yup[2]); } - if (!config.abc_ocro.valid()) { + if (!config.abc_orco.valid()) { /* Create the Alembic property and keep a reference so future frames can reuse it. */ - config.abc_ocro = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1); + config.abc_orco = OV3fGeomParam(prop, propNameOriginalCoordinates, false, kVertexScope, 1); } OV3fGeomParam::Sample sample(coords, kVertexScope); - config.abc_ocro.set(sample); + config.abc_orco.set(sample); } void write_custom_data(const OCompoundProperty &prop, diff --git a/source/blender/io/alembic/intern/abc_customdata.h b/source/blender/io/alembic/intern/abc_customdata.h index 4fba6a2f0f7..03e6f697f0c 100644 --- a/source/blender/io/alembic/intern/abc_customdata.h +++ b/source/blender/io/alembic/intern/abc_customdata.h @@ -79,8 +79,8 @@ struct CDStreamConfig { * UV map is kept alive by the Alembic mesh sample itself. */ std::map abc_uv_maps; - /* OCRO coordinates, aka Generated Coordinates. */ - Alembic::AbcGeom::OV3fGeomParam abc_ocro; + /* ORCO coordinates, aka Generated Coordinates. */ + Alembic::AbcGeom::OV3fGeomParam abc_orco; CDStreamConfig() : mloop(NULL), @@ -122,12 +122,6 @@ void read_custom_data(const std::string &iobject_full_name, const CDStreamConfig &config, const Alembic::Abc::ISampleSelector &iss); -void read_velocity(const Alembic::Abc::ICompoundProperty &prop, - const Alembic::Abc::PropertyHeader *prop_header, - const Alembic::Abc::ISampleSelector &selector, - const CDStreamConfig &config, - const char *velocity_name, - const float velocity_scale); typedef enum { ABC_UV_SCOPE_NONE, ABC_UV_SCOPE_LOOP, diff --git a/source/blender/io/alembic/intern/abc_reader_camera.h b/source/blender/io/alembic/intern/abc_reader_camera.h index 408e9623970..ca8dee80c9d 100644 --- a/source/blender/io/alembic/intern/abc_reader_camera.h +++ b/source/blender/io/alembic/intern/abc_reader_camera.h @@ -23,18 +23,18 @@ namespace blender::io::alembic { -class AbcCameraReader : public AbcObjectReader { +class AbcCameraReader final : public AbcObjectReader { Alembic::AbcGeom::ICameraSchema m_schema; public: AbcCameraReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; }; } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/abc_reader_curves.h b/source/blender/io/alembic/intern/abc_reader_curves.h index 11b23c8a8cf..df5d68d7850 100644 --- a/source/blender/io/alembic/intern/abc_reader_curves.h +++ b/source/blender/io/alembic/intern/abc_reader_curves.h @@ -31,24 +31,24 @@ struct Curve; namespace blender::io::alembic { -class AbcCurveReader : public AbcObjectReader { +class AbcCurveReader final : public AbcObjectReader { Alembic::AbcGeom::ICurvesSchema m_curves_schema; public: AbcCurveReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, const int read_flag, const char *velocity_name, const float velocity_scale, - const char **err_str); + const char **err_str) override; void read_curve_sample(Curve *cu, const Alembic::AbcGeom::ICurvesSchema &schema, diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.cc b/source/blender/io/alembic/intern/abc_reader_mesh.cc index eab94139f55..adf1a3e241c 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.cc +++ b/source/blender/io/alembic/intern/abc_reader_mesh.cc @@ -435,6 +435,13 @@ static V3fArraySamplePtr get_velocity_prop(const ICompoundProperty &schema, const ICompoundProperty &prop = ICompoundProperty(schema, header.getName()); if (has_property(prop, name)) { + /* Header cannot be null here, as its presence is checked via has_property, so it is safe + * to dereference. */ + const PropertyHeader *header = prop.getPropertyHeader(name); + if (!IV3fArrayProperty::matches(*header)) { + continue; + } + const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(prop, name, 0); if (velocity_prop) { return velocity_prop.getValue(selector); @@ -442,7 +449,7 @@ static V3fArraySamplePtr get_velocity_prop(const ICompoundProperty &schema, } } else if (header.isArray()) { - if (header.getName() == name) { + if (header.getName() == name && IV3fArrayProperty::matches(header)) { const IV3fArrayProperty &velocity_prop = IV3fArrayProperty(schema, name, 0); return velocity_prop.getValue(selector); } diff --git a/source/blender/io/alembic/intern/abc_reader_mesh.h b/source/blender/io/alembic/intern/abc_reader_mesh.h index d9f89cc8085..2e34ca8ded0 100644 --- a/source/blender/io/alembic/intern/abc_reader_mesh.h +++ b/source/blender/io/alembic/intern/abc_reader_mesh.h @@ -26,7 +26,7 @@ struct Mesh; namespace blender::io::alembic { -class AbcMeshReader : public AbcObjectReader { +class AbcMeshReader final : public AbcObjectReader { Alembic::AbcGeom::IPolyMeshSchema m_schema; CDStreamConfig m_mesh_data; @@ -60,7 +60,7 @@ class AbcMeshReader : public AbcObjectReader { std::map &r_mat_map); }; -class AbcSubDReader : public AbcObjectReader { +class AbcSubDReader final : public AbcObjectReader { Alembic::AbcGeom::ISubDSchema m_schema; CDStreamConfig m_mesh_data; @@ -68,17 +68,17 @@ class AbcSubDReader : public AbcObjectReader { public: AbcSubDReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + const char **err_str) const override; + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, const int read_flag, const char *velocity_name, const float velocity_scale, - const char **err_str); + const char **err_str) override; }; void read_mverts(MVert *mverts, diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.cc b/source/blender/io/alembic/intern/abc_reader_nurbs.cc index 25567aa8c24..4492d1e1ca0 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.cc +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.cc @@ -71,6 +71,26 @@ bool AbcNurbsReader::valid() const return true; } +bool AbcNurbsReader::accepts_object_type( + const Alembic::AbcCoreAbstract::v12::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const +{ + if (!Alembic::AbcGeom::INuPatch::matches(alembic_header)) { + *err_str = + "Object type mismatch, Alembic object path pointed to NURBS when importing, but not any " + "more."; + return false; + } + + if (ob->type != OB_CURVE) { + *err_str = "Object type mismatch, Alembic object path points to NURBS."; + return false; + } + + return true; +} + static bool set_knots(const FloatArraySamplePtr &knots, float *&nu_knots) { if (!knots || knots->size() < 2) { diff --git a/source/blender/io/alembic/intern/abc_reader_nurbs.h b/source/blender/io/alembic/intern/abc_reader_nurbs.h index e8be2efba9f..66e68cf6942 100644 --- a/source/blender/io/alembic/intern/abc_reader_nurbs.h +++ b/source/blender/io/alembic/intern/abc_reader_nurbs.h @@ -23,15 +23,19 @@ namespace blender::io::alembic { -class AbcNurbsReader : public AbcObjectReader { +class AbcNurbsReader final : public AbcObjectReader { std::vector> m_schemas; public: AbcNurbsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, + const Object *const ob, + const char **err_str) const override; + + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; private: void getNurbsPatches(const Alembic::Abc::IObject &obj); diff --git a/source/blender/io/alembic/intern/abc_reader_points.cc b/source/blender/io/alembic/intern/abc_reader_points.cc index 3aeacbd14fe..75ed18f57f2 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.cc +++ b/source/blender/io/alembic/intern/abc_reader_points.cc @@ -82,7 +82,7 @@ bool AbcPointsReader::accepts_object_type( void AbcPointsReader::readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) { Mesh *mesh = BKE_mesh_add(bmain, m_data_name.c_str()); - Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, nullptr); + Mesh *read_mesh = this->read_mesh(mesh, sample_sel, 0, "", 0.0f, nullptr); if (read_mesh != mesh) { BKE_mesh_nomain_to_mesh(read_mesh, mesh, m_object, &CD_MASK_MESH, true); @@ -127,6 +127,8 @@ void read_points_sample(const IPointsSchema &schema, struct Mesh *AbcPointsReader::read_mesh(struct Mesh *existing_mesh, const ISampleSelector &sample_sel, int read_flag, + const char * /*velocity_name*/, + const float /*velocity_scale*/, const char **err_str) { IPointsSchema::Sample sample; diff --git a/source/blender/io/alembic/intern/abc_reader_points.h b/source/blender/io/alembic/intern/abc_reader_points.h index aed66699c75..105d1276f7a 100644 --- a/source/blender/io/alembic/intern/abc_reader_points.h +++ b/source/blender/io/alembic/intern/abc_reader_points.h @@ -27,24 +27,26 @@ namespace blender::io::alembic { -class AbcPointsReader : public AbcObjectReader { +class AbcPointsReader final : public AbcObjectReader { Alembic::AbcGeom::IPointsSchema m_schema; Alembic::AbcGeom::IPointsSchema::Sample m_sample; public: AbcPointsReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; struct Mesh *read_mesh(struct Mesh *existing_mesh, const Alembic::Abc::ISampleSelector &sample_sel, int read_flag, - const char **err_str); + const char *velocity_name, + const float velocity_scale, + const char **err_str) override; }; void read_points_sample(const Alembic::AbcGeom::IPointsSchema &schema, diff --git a/source/blender/io/alembic/intern/abc_reader_transform.h b/source/blender/io/alembic/intern/abc_reader_transform.h index e515560912f..6810cc214b7 100644 --- a/source/blender/io/alembic/intern/abc_reader_transform.h +++ b/source/blender/io/alembic/intern/abc_reader_transform.h @@ -25,18 +25,18 @@ namespace blender::io::alembic { -class AbcEmptyReader : public AbcObjectReader { +class AbcEmptyReader final : public AbcObjectReader { Alembic::AbcGeom::IXformSchema m_schema; public: AbcEmptyReader(const Alembic::Abc::IObject &object, ImportSettings &settings); - bool valid() const; + bool valid() const override; bool accepts_object_type(const Alembic::AbcCoreAbstract::ObjectHeader &alembic_header, const Object *const ob, - const char **err_str) const; + const char **err_str) const override; - void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel); + void readObjectData(Main *bmain, const Alembic::Abc::ISampleSelector &sample_sel) override; }; } // namespace blender::io::alembic diff --git a/source/blender/io/alembic/intern/alembic_capi.cc b/source/blender/io/alembic/intern/alembic_capi.cc index 63565b902f3..d7b176eea50 100644 --- a/source/blender/io/alembic/intern/alembic_capi.cc +++ b/source/blender/io/alembic/intern/alembic_capi.cc @@ -460,7 +460,7 @@ static void import_startjob(void *user_data, short *stop, short *do_update, floa /* Decrement the ID ref-count because it is going to be incremented for each * modifier and constraint that it will be attached to, so since currently - * it is not used by anyone, its use count will off by one. */ + * it is not used by anyone, its use count will be off by one. */ id_us_min(&cache_file->id); cache_file->is_sequence = data->settings.is_sequence; diff --git a/source/blender/io/avi/intern/avi.c b/source/blender/io/avi/intern/avi.c index 88f2e1a259f..87058693378 100644 --- a/source/blender/io/avi/intern/avi.c +++ b/source/blender/io/avi/intern/avi.c @@ -145,7 +145,7 @@ static bool fcc_is_data(int fcc) fccs[2] = fcc >> 16; fccs[3] = fcc >> 24; - if (!isdigit(fccs[0]) || !isdigit(fccs[1]) || (fccs[2] != 'd' && fccs[2] != 'w')) { + if (!isdigit(fccs[0]) || !isdigit(fccs[1]) || (!ELEM(fccs[2], 'd', 'w'))) { return 0; } if (!ELEM(fccs[3], 'b', 'c')) { diff --git a/source/blender/io/avi/intern/avi_mjpeg.c b/source/blender/io/avi/intern/avi_mjpeg.c index 75059c202e5..8b132df7b8f 100644 --- a/source/blender/io/avi/intern/avi_mjpeg.c +++ b/source/blender/io/avi/intern/avi_mjpeg.c @@ -33,8 +33,8 @@ #include "BLI_math_base.h" #include "IMB_imbuf.h" -#include "jerror.h" -#include "jpeglib.h" +#include +#include #include "avi_mjpeg.h" diff --git a/source/blender/io/avi/intern/avi_rgb.c b/source/blender/io/avi/intern/avi_rgb.c index 8af728f0737..f1d83b9857b 100644 --- a/source/blender/io/avi/intern/avi_rgb.c +++ b/source/blender/io/avi/intern/avi_rgb.c @@ -20,7 +20,7 @@ /** \file * \ingroup avi * - * This is external code. Converts rgb-type avi-s. + * This is external code. Converts RGB-type AVI files. */ #include diff --git a/source/blender/io/collada/ArmatureImporter.cpp b/source/blender/io/collada/ArmatureImporter.cpp index f36b9aacd8b..72fb8820be5 100644 --- a/source/blender/io/collada/ArmatureImporter.cpp +++ b/source/blender/io/collada/ArmatureImporter.cpp @@ -955,7 +955,7 @@ void ArmatureImporter::make_shape_keys(bContext *C) COLLADAFW::UniqueIdArray &morphTargetIds = (*mc)->getMorphTargets(); COLLADAFW::FloatOrDoubleArray &morphWeights = (*mc)->getMorphWeights(); - /* Prereq: all the geometries must be imported and mesh objects must be made */ + /* Prerequisite: all the geometries must be imported and mesh objects must be made. */ Object *source_ob = this->mesh_importer->get_object_by_geom_uid((*mc)->getSource()); if (source_ob) { diff --git a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc index 3cda4d125d0..28d5eb59e5e 100644 --- a/source/blender/io/common/intern/abstract_hierarchy_iterator.cc +++ b/source/blender/io/common/intern/abstract_hierarchy_iterator.cc @@ -462,7 +462,6 @@ void AbstractHierarchyIterator::visit_dupli_object(DupliObject *dupli_object, context->weak_export = false; context->export_path = ""; context->original_export_path = ""; - context->export_path = ""; context->animation_check_include_parent = false; copy_m4_m4(context->matrix_world, dupli_object->mat); diff --git a/source/blender/io/usd/intern/usd_reader_material.cc b/source/blender/io/usd/intern/usd_reader_material.cc index 93e433e231c..317dfd2a62b 100644 --- a/source/blender/io/usd/intern/usd_reader_material.cc +++ b/source/blender/io/usd/intern/usd_reader_material.cc @@ -648,7 +648,7 @@ void USDMaterialReader::load_tex_image(const pxr::UsdShadeShader &usd_shader, color_space = file_input.GetAttr().GetColorSpace(); } - if (color_space == usdtokens::RAW || color_space == usdtokens::raw) { + if (ELEM(color_space, usdtokens::RAW, usdtokens::raw)) { STRNCPY(image->colorspace_settings.name, "Raw"); } } diff --git a/source/blender/io/usd/intern/usd_reader_mesh.cc b/source/blender/io/usd/intern/usd_reader_mesh.cc index 9c75bc8afae..5c8bd88e87a 100644 --- a/source/blender/io/usd/intern/usd_reader_mesh.cc +++ b/source/blender/io/usd/intern/usd_reader_mesh.cc @@ -390,8 +390,9 @@ void USDMeshReader::read_uvs(Mesh *mesh, const double motionSampleTime, const bo const UVSample &sample = uv_primvars[layer_idx]; - if (!(sample.interpolation == pxr::UsdGeomTokens->faceVarying || - sample.interpolation == pxr::UsdGeomTokens->vertex)) { + if (!(ELEM(sample.interpolation, + pxr::UsdGeomTokens->faceVarying, + pxr::UsdGeomTokens->vertex))) { std::cerr << "WARNING: unexpected interpolation type " << sample.interpolation << " for uv " << layer->name << std::endl; continue; @@ -781,9 +782,10 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, bool is_uv = false; /* Assume all UVs are stored in one of these primvar types */ - if (type == pxr::SdfValueTypeNames->TexCoord2hArray || - type == pxr::SdfValueTypeNames->TexCoord2fArray || - type == pxr::SdfValueTypeNames->TexCoord2dArray) { + if (ELEM(type, + pxr::SdfValueTypeNames->TexCoord2hArray, + pxr::SdfValueTypeNames->TexCoord2fArray, + pxr::SdfValueTypeNames->TexCoord2dArray)) { is_uv = true; } /* In some cases, the st primvar is stored as float2 values. */ @@ -795,7 +797,7 @@ Mesh *USDMeshReader::read_mesh(Mesh *existing_mesh, pxr::TfToken interp = p.GetInterpolation(); - if (!(interp == pxr::UsdGeomTokens->faceVarying || interp == pxr::UsdGeomTokens->vertex)) { + if (!(ELEM(interp, pxr::UsdGeomTokens->faceVarying, pxr::UsdGeomTokens->vertex))) { continue; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 5b7c99f2545..d829d707a71 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -392,7 +392,14 @@ typedef struct ID { * that references this ID (the bones of an armature or the modifiers of an object for e.g.). */ void *py_instance; - void *_pad1; + + /** + * Weak reference to an ID in a given library file, used to allow re-using already appended data + * in some cases, instead of appending it again. + * + * May be NULL. + */ + struct LibraryWeakReference *library_weak_reference; } ID; /** @@ -426,6 +433,26 @@ typedef struct Library { short versionfile, subversionfile; } Library; +/** + * A weak library/ID reference for local data that has been appended, to allow re-using that local + * data instead of creating a new copy of it in future appends. + * + * NOTE: This is by design a week reference, in other words code should be totally fine and perform + * a regular append if it cannot find a valid matching local ID. + * + * NOTE: There should always be only one single ID in current Main matching a given linked + * reference. + */ +typedef struct LibraryWeakReference { + /** Expected to match a `Library.filepath`. */ + char library_filepath[1024]; + + /** MAX_ID_NAME. May be different from the current local ID name. */ + char library_id_name[66]; + + char _pad[2]; +} LibraryWeakReference; + /* for PreviewImage->flag */ enum ePreviewImage_Flag { PRV_CHANGED = (1 << 0), diff --git a/source/blender/makesdna/DNA_asset_types.h b/source/blender/makesdna/DNA_asset_types.h index 2975915eccd..bd604b90b5a 100644 --- a/source/blender/makesdna/DNA_asset_types.h +++ b/source/blender/makesdna/DNA_asset_types.h @@ -22,6 +22,7 @@ #include "DNA_defs.h" #include "DNA_listBase.h" +#include "DNA_uuid_types.h" #ifdef __cplusplus extern "C" { @@ -55,11 +56,31 @@ typedef struct AssetFilterSettings { * more than that from the file. So pointers to other IDs or ID data are strictly forbidden. */ typedef struct AssetMetaData { + /** Runtime type, to reference event callbacks. Only valid for local assets. */ + struct AssetTypeInfo *local_type_info; + /** Custom asset meta-data. Cannot store pointers to IDs (#STRUCT_NO_DATABLOCK_IDPROPERTIES)! */ struct IDProperty *properties; + /** + * Asset Catalog identifier. Should not contain spaces. + * Mapped to a path in the asset catalog hierarchy by an #AssetCatalogService. + * Use #BKE_asset_metadata_catalog_id_set() to ensure a valid ID is set. + */ + struct bUUID catalog_id; + /** + * Short name of the asset's catalog. This is for debugging purposes only, to allow (partial) + * reconstruction of asset catalogs in the unfortunate case that the mapping from catalog UUID to + * catalog path is lost. The catalog's simple name is copied to #catalog_simple_name whenever + * #catalog_id is updated. */ + char catalog_simple_name[64]; /* MAX_NAME */ + + /** Optional name of the author for display in the UI. Dynamic length. */ + char *author; + /** Optional description of this asset for display in the UI. Dynamic length. */ char *description; + /** User defined tags for this asset. The asset manager uses these for filtering, but how they * function exactly (e.g. how they are registered to provide a list of searchable available tags) * is up to the asset-engine. */ diff --git a/source/blender/makesdna/DNA_cloth_types.h b/source/blender/makesdna/DNA_cloth_types.h index e2eea5e5422..49b2862032f 100644 --- a/source/blender/makesdna/DNA_cloth_types.h +++ b/source/blender/makesdna/DNA_cloth_types.h @@ -42,7 +42,7 @@ extern "C" { */ typedef struct ClothSimSettings { - /** UNUSED atm. */ + /** UNUSED. */ struct LinkNode *cache; /** See SB. */ float mingoal; diff --git a/source/blender/makesdna/DNA_constraint_types.h b/source/blender/makesdna/DNA_constraint_types.h index 4b4c24a7a4e..6dc2d00252f 100644 --- a/source/blender/makesdna/DNA_constraint_types.h +++ b/source/blender/makesdna/DNA_constraint_types.h @@ -81,8 +81,8 @@ typedef struct bConstraint { /** Local influence ipo or driver */ struct Ipo *ipo DNA_DEPRECATED; - /* below are readonly fields that are set at runtime - * by the solver for use in the GE (only IK atm) */ + /* Below are read-only fields that are set at runtime + * by the solver for use in the GE (only IK at the moment). */ /** Residual error on constraint expressed in blender unit. */ float lin_error; /** Residual error on constraint expressed in radiant. */ @@ -939,7 +939,7 @@ typedef enum eTrackToAxis_Modes { /* Shrinkwrap flags */ typedef enum eShrinkwrap_Flags { - /* Also raycast in the opposite direction. */ + /* Also ray-cast in the opposite direction. */ CON_SHRINKWRAP_PROJECT_OPPOSITE = (1 << 0), /* Invert the cull mode when projecting opposite. */ CON_SHRINKWRAP_PROJECT_INVERT_CULL = (1 << 1), diff --git a/source/blender/makesdna/DNA_curveprofile_types.h b/source/blender/makesdna/DNA_curveprofile_types.h index 7c76251adc3..447e94d2659 100644 --- a/source/blender/makesdna/DNA_curveprofile_types.h +++ b/source/blender/makesdna/DNA_curveprofile_types.h @@ -29,13 +29,6 @@ extern "C" { #endif -/** Number of points in high resolution table is dynamic up to a maximum. */ -#define PROF_TABLE_MAX 512 -/** Number of table points per control point. */ -#define PROF_RESOL 16 -/** Dynamic size of widget's high resolution table. Input should be profile->totpoint. */ -#define PROF_TABLE_LEN(n_pts) min_ii(PROF_TABLE_MAX, (((n_pts - 1)) * PROF_RESOL) + 1) - /** * Each control point that makes up the profile. * \note The flags use the same enum as Bezier curves, but they aren't guaranteed diff --git a/source/blender/makesdna/DNA_customdata_types.h b/source/blender/makesdna/DNA_customdata_types.h index 6acea8da15f..36bdd4915ff 100644 --- a/source/blender/makesdna/DNA_customdata_types.h +++ b/source/blender/makesdna/DNA_customdata_types.h @@ -84,7 +84,8 @@ typedef struct CustomData { * MUST be >= CD_NUMTYPES, but we can't use a define here. * Correct size is ensured in CustomData_update_typemap assert(). */ - int typemap[51]; + int typemap[52]; + char _pad[4]; /** Number of layers, size of layers array. */ int totlayer, maxlayer; /** In editmode, total size of all data layers. */ @@ -166,7 +167,9 @@ typedef enum CustomDataType { CD_PROP_BOOL = 50, - CD_NUMTYPES = 51, + CD_HAIRLENGTH = 51, + + CD_NUMTYPES = 52, } CustomDataType; /* Bits for CustomDataMask */ @@ -220,6 +223,8 @@ typedef enum CustomDataType { #define CD_MASK_PROP_FLOAT2 (1ULL << CD_PROP_FLOAT2) #define CD_MASK_PROP_BOOL (1ULL << CD_PROP_BOOL) +#define CD_MASK_HAIRLENGTH (1ULL << CD_HAIRLENGTH) + /** Multires loop data. */ #define CD_MASK_MULTIRES_GRIDS (CD_MASK_MDISPS | CD_GRID_PAINT_MASK) diff --git a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h index 450527c7443..1ad884bee8f 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_defaults.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_defaults.h @@ -283,7 +283,20 @@ .colorband = NULL, \ } -#define _DNA_DEFAULT_WeightGpencilModifierData \ +#define _DNA_DEFAULT_WeightProxGpencilModifierData \ + { \ + .target_vgname = "", \ + .material = NULL, \ + .layername = "", \ + .vgname = "", \ + .pass_index = 0, \ + .flag = 0, \ + .layer_pass = 0, \ + .dist_start = 0.0f, \ + .dist_end = 20.0f, \ + } + +#define _DNA_DEFAULT_WeightAngleGpencilModifierData \ { \ .target_vgname = "", \ .material = NULL, \ @@ -293,8 +306,6 @@ .flag = 0, \ .axis = 1, \ .layer_pass = 0, \ - .dist_start = 0.0f, \ - .dist_end = 20.0f, \ } #define _DNA_DEFAULT_LineartGpencilModifierData \ @@ -307,16 +318,21 @@ .calculation_flags = LRT_ALLOW_DUPLI_OBJECTS | LRT_ALLOW_CLIPPING_BOUNDARIES | LRT_USE_CREASE_ON_SHARP_EDGES, \ .angle_splitting_threshold = DEG2RAD(60.0f), \ .chaining_image_threshold = 0.001f, \ - .overscan = 0.1f,\ + .chain_smooth_tolerance = 0.2f,\ + .stroke_depth_offset = 0.05,\ } #define _DNA_DEFAULT_LengthGpencilModifierData \ { \ .start_fac = 0.1f,\ .end_fac = 0.1f,\ - .overshoot_fac = 0.01f,\ + .overshoot_fac = 0.1f,\ .pass_index = 0,\ .material = NULL,\ + .flag = GP_LENGTH_USE_CURVATURE,\ + .point_density = 30.0f,\ + .segment_influence = 0.0f,\ + .max_angle = DEG2RAD(170.0f),\ } #define _DNA_DEFAULT_DashGpencilModifierData \ diff --git a/source/blender/makesdna/DNA_gpencil_modifier_types.h b/source/blender/makesdna/DNA_gpencil_modifier_types.h index d3429329ef6..339714da255 100644 --- a/source/blender/makesdna/DNA_gpencil_modifier_types.h +++ b/source/blender/makesdna/DNA_gpencil_modifier_types.h @@ -55,8 +55,9 @@ typedef enum GpencilModifierType { eGpencilModifierType_Texture = 18, eGpencilModifierType_Lineart = 19, eGpencilModifierType_Length = 20, - eGpencilModifierType_Weight = 21, + eGpencilModifierType_WeightProximity = 21, eGpencilModifierType_Dash = 22, + eGpencilModifierType_WeightAngle = 23, /* Keep last. */ NUM_GREASEPENCIL_MODIFIER_TYPES, } GpencilModifierType; @@ -259,7 +260,7 @@ typedef struct ColorGpencilModifierData { int pass_index; /** Flags. */ int flag; - /** Hsv factors. */ + /** HSV factors. */ float hsv[3]; /** Modify stroke, fill or both. */ char modify_color; @@ -493,7 +494,10 @@ typedef struct LengthGpencilModifierData { float overshoot_fac; /** Modifier mode. */ int mode; - char _pad[4]; + /* Curvature parameters. */ + float point_density; + float segment_influence; + float max_angle; } LengthGpencilModifierData; typedef enum eLengthGpencil_Flag { @@ -501,6 +505,8 @@ typedef enum eLengthGpencil_Flag { GP_LENGTH_INVERT_PASS = (1 << 1), GP_LENGTH_INVERT_LAYERPASS = (1 << 2), GP_LENGTH_INVERT_MATERIAL = (1 << 3), + GP_LENGTH_USE_CURVATURE = (1 << 4), + GP_LENGTH_INVERT_CURVATURE = (1 << 5), } eLengthGpencil_Flag; typedef enum eLengthGpencil_Type { @@ -891,7 +897,7 @@ typedef enum eTextureGpencil_Mode { STROKE_AND_FILL = 2, } eTextureGpencil_Mode; -typedef struct WeightGpencilModifierData { +typedef struct WeightProxGpencilModifierData { GpencilModifierData modifier; /** Target vertexgroup name, MAX_VGROUP_NAME. */ char target_vgname[64]; @@ -909,22 +915,39 @@ typedef struct WeightGpencilModifierData { float min_weight; /** Custom index for passes. */ int layer_pass; - /** Calculation Mode. */ - short mode; - /** Axis. */ - short axis; - /** Angle */ - float angle; /** Start/end distances. */ float dist_start; float dist_end; - /** Space (Local/World). */ - short space; - char _pad[6]; /** Reference object */ struct Object *object; -} WeightGpencilModifierData; +} WeightProxGpencilModifierData; + +typedef struct WeightAngleGpencilModifierData { + GpencilModifierData modifier; + /** Target vertexgroup name, MAX_VGROUP_NAME. */ + char target_vgname[64]; + /** Material for filtering. */ + struct Material *material; + /** Layer name. */ + char layername[64]; + /** Optional vertexgroup filter name, MAX_VGROUP_NAME. */ + char vgname[64]; + /** Custom index for passes. */ + int pass_index; + /** Flags. */ + int flag; + /** Minimum valid weight (clamp value). */ + float min_weight; + /** Custom index for passes. */ + int layer_pass; + /** Axis. */ + short axis; + /** Space (Local/World). */ + short space; + /** Angle */ + float angle; +} WeightAngleGpencilModifierData; typedef enum eWeightGpencil_Flag { GP_WEIGHT_INVERT_LAYER = (1 << 0), @@ -932,15 +955,10 @@ typedef enum eWeightGpencil_Flag { GP_WEIGHT_INVERT_VGROUP = (1 << 2), GP_WEIGHT_INVERT_LAYERPASS = (1 << 3), GP_WEIGHT_INVERT_MATERIAL = (1 << 4), - GP_WEIGHT_BLEND_DATA = (1 << 5), + GP_WEIGHT_MULTIPLY_DATA = (1 << 5), GP_WEIGHT_INVERT_OUTPUT = (1 << 6), } eWeightGpencil_Flag; -typedef enum eWeightGpencilModifierMode { - GP_WEIGHT_MODE_DISTANCE = 0, - GP_WEIGHT_MODE_ANGLE = 1, -} eWeightGpencilModifierMode; - typedef enum eGpencilModifierSpace { GP_SPACE_LOCAL = 0, GP_SPACE_WORLD = 1, @@ -960,6 +978,7 @@ typedef enum eLineArtGPencilModifierFlags { LRT_GPENCIL_BINARY_WEIGHTS = (1 << 2) /* Deprecated, this is removed for lack of use case. */, LRT_GPENCIL_IS_BAKED = (1 << 3), LRT_GPENCIL_USE_CACHE = (1 << 4), + LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA = (1 << 5), } eLineArtGPencilModifierFlags; typedef enum eLineartGpencilMaskSwitches { @@ -986,6 +1005,8 @@ typedef struct LineartGpencilModifierData { short level_start; short level_end; + struct Object *source_camera; + struct Object *source_object; struct Collection *source_collection; @@ -1022,7 +1043,10 @@ typedef struct LineartGpencilModifierData { /** `0..PI` angle, for splitting strokes at sharp points. */ float angle_splitting_threshold; - /* Doubles as geometry threshold when geometry space chaining is enabled */ + /** Strength for smoothing jagged chains. */ + float chain_smooth_tolerance; + + /* CPU mode */ float chaining_image_threshold; /* Ported from SceneLineArt flags. */ @@ -1031,6 +1055,9 @@ typedef struct LineartGpencilModifierData { /* #eLineArtGPencilModifierFlags, modifier internal state. */ int flags; + /* Move strokes towards camera to avoid clipping while preserve depth for the viewport. */ + float stroke_depth_offset; + /* Runtime data. */ /* Because we can potentially only compute features lines once per modifier stack (Use Cache), we diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 68bd2961f23..0f570f8603d 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -49,6 +49,8 @@ struct MDeformVert; #define GPENCIL_MIN_FILL_FAC 0.05f #define GPENCIL_MAX_FILL_FAC 8.0f +#define GPENCIL_MAX_THICKNESS 5000 + /* ***************************************** */ /* GP Stroke Points */ diff --git a/source/blender/makesdna/DNA_gpu_types.h b/source/blender/makesdna/DNA_gpu_types.h index 8cea1451525..7d1e7d4e4f2 100644 --- a/source/blender/makesdna/DNA_gpu_types.h +++ b/source/blender/makesdna/DNA_gpu_types.h @@ -28,7 +28,7 @@ extern "C" { #endif /* Keep for 'Camera' versioning. */ -/** Properties for dof effect. */ +/** Properties for DOF effect. */ typedef struct GPUDOFSettings { /** Focal distance for depth of field. */ float focus_distance; diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 30ca9540735..64c8fd3e3a9 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -51,16 +51,13 @@ typedef struct ImageUser { /** Offset within movie, start frame in global time. */ int offset, sfra; /** Cyclic flag. */ - char _pad0, cycl; - char ok; + char cycl; /** Multiview current eye - for internal use of drawing routines. */ char multiview_eye; short pass; - char _pad1[2]; int tile; - int _pad2; /** Listbase indices, for menu browsing or retrieve buffer. */ short multi_index, view, layer; @@ -112,9 +109,7 @@ typedef struct ImageTile { struct ImageTile_Runtime runtime; - char ok; - char _pad[3]; - + char _pad[4]; int tile_number; char label[64]; } ImageTile; @@ -147,6 +142,12 @@ typedef enum eImageTextureResolution { IMA_TEXTURE_RESOLUTION_LEN } eImageTextureResolution; +typedef struct Image_Runtime { + /* Mutex used to guarantee thread-safe access to the cached ImBuf of the corresponding image ID. + */ + void *cache_mutex; +} Image_Runtime; + typedef struct Image { ID id; @@ -213,6 +214,8 @@ typedef struct Image { /** ImageView. */ ListBase views; struct Stereo3dFormat *stereo3d_format; + + Image_Runtime runtime; } Image; /* **************** IMAGE ********************* */ diff --git a/source/blender/makesdna/DNA_layer_types.h b/source/blender/makesdna/DNA_layer_types.h index 63e4597150c..520f989452c 100644 --- a/source/blender/makesdna/DNA_layer_types.h +++ b/source/blender/makesdna/DNA_layer_types.h @@ -68,7 +68,7 @@ typedef enum eViewLayerCryptomatteFlags { VIEW_LAYER_CRYPTOMATTE_OBJECT = (1 << 0), VIEW_LAYER_CRYPTOMATTE_MATERIAL = (1 << 1), VIEW_LAYER_CRYPTOMATTE_ASSET = (1 << 2), - VIEW_LAYER_CRYPTOMATTE_ACCURATE = (1 << 3), + /* VIEW_LAYER_CRYPTOMATTE_ACCURATE = (1 << 3), */ /* DEPRECATED */ } eViewLayerCryptomatteFlags; #define VIEW_LAYER_CRYPTOMATTE_ALL \ (VIEW_LAYER_CRYPTOMATTE_OBJECT | VIEW_LAYER_CRYPTOMATTE_MATERIAL | VIEW_LAYER_CRYPTOMATTE_ASSET) diff --git a/source/blender/makesdna/DNA_lineart_types.h b/source/blender/makesdna/DNA_lineart_types.h index bdc9bcb6980..d4440592a00 100644 --- a/source/blender/makesdna/DNA_lineart_types.h +++ b/source/blender/makesdna/DNA_lineart_types.h @@ -49,6 +49,8 @@ typedef enum eLineartMainFlags { LRT_ALLOW_OVERLAP_EDGE_TYPES = (1 << 14), LRT_USE_CREASE_ON_SMOOTH_SURFACES = (1 << 15), LRT_USE_CREASE_ON_SHARP_EDGES = (1 << 16), + LRT_USE_CUSTOM_CAMERA = (1 << 17), + LRT_USE_IMAGE_BOUNDARY_TRIMMING = (1 << 20), } eLineartMainFlags; typedef enum eLineartEdgeFlag { diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 97f14b2195d..3943c063c39 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -127,6 +127,10 @@ typedef struct Mesh_Runtime { /** Needed in case we need to lazily initialize the mesh. */ CustomData_MeshMasks cd_mask_extra; + /** Needed to ensure some thread-safety during render data pre-processing. */ + void *render_mutex; + void *_pad3; + } Mesh_Runtime; typedef struct Mesh { diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 31daa778b03..631db64ddd3 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -848,7 +848,7 @@ typedef struct CollisionModifierData { struct MVert *x; /** Position at the end of the frame. */ struct MVert *xnew; - /** Unused atm, but was discussed during sprint. */ + /** Unused at the moment, but was discussed during sprint. */ struct MVert *xold; /** New position at the actual inter-frame step. */ struct MVert *current_xnew; diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index c4cbc71762c..d5d2520ddf6 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -120,7 +120,10 @@ typedef struct bNodeSocket { /* XXX deprecated, kept for forward compatibility */ short stack_type DNA_DEPRECATED; char display_shape; - char _pad[1]; + + /* #AttributeDomain used when the geometry nodes modifier creates an attribute for a group + * output. */ + char attribute_domain; /* Runtime-only cache of the number of input links, for multi-input sockets. */ short total_inputs; @@ -445,6 +448,7 @@ typedef struct bNodeLink { #define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */ #define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */ #define NODE_LINK_MUTED (1 << 4) /* Link is muted. */ +#define NODE_LINK_DRAGGED (1 << 5) /* Node link is being dragged by the user. */ /* tree->edit_quality/tree->render_quality */ #define NTREE_QUALITY_HIGH 0 @@ -459,6 +463,16 @@ typedef struct bNodeLink { #define NTREE_CHUNKSIZE_512 512 #define NTREE_CHUNKSIZE_1024 1024 +/** Workaround to forward-declare C++ type in C header. */ +#ifdef __cplusplus +namespace blender::nodes { +struct FieldInferencingInterface; +} +using FieldInferencingInterfaceHandle = blender::nodes::FieldInferencingInterface; +#else +typedef struct FieldInferencingInterfaceHandle FieldInferencingInterfaceHandle; +#endif + /* the basis for a Node tree, all links and nodes reside internal here */ /* only re-usable node trees are in the library though, * materials and textures allocate own tree struct */ @@ -481,6 +495,8 @@ typedef struct bNodeTree { float view_center[2]; ListBase nodes, links; + /** Information about how inputs and outputs of the node group interact with fields. */ + FieldInferencingInterfaceHandle *field_inferencing_interface; /** Set init on fileread. */ int type, init; @@ -575,6 +591,9 @@ typedef enum eNodeTreeUpdate { NTREE_UPDATE_NODES = (1 << 1), /* nodes or sockets have been added or removed */ NTREE_UPDATE_GROUP_IN = (1 << 4), /* group inputs have changed */ NTREE_UPDATE_GROUP_OUT = (1 << 5), /* group outputs have changed */ + /* The field interface has changed. So e.g. an output that was always a field before is not + * anymore. This implies that the field type inferencing has to be done again. */ + NTREE_UPDATE_FIELD_INFERENCING = (1 << 6), /* group has changed (generic flag including all other group flags) */ NTREE_UPDATE_GROUP = (NTREE_UPDATE_GROUP_IN | NTREE_UPDATE_GROUP_OUT), } eNodeTreeUpdate; @@ -836,7 +855,7 @@ typedef struct NodeVertexCol { char name[64]; } NodeVertexCol; -/* qdn: Defocus blur node */ +/** Defocus blur node. */ typedef struct NodeDefocus { char bktype, _pad0, preview, gamco; short samples, no_zbuf; @@ -852,7 +871,7 @@ typedef struct NodeScriptDict { void *node; } NodeScriptDict; -/* qdn: glare node */ +/** glare node. */ typedef struct NodeGlare { char quality, type, iter; /* XXX angle is only kept for backward/forward compatibility, @@ -863,14 +882,14 @@ typedef struct NodeGlare { char _pad1[4]; } NodeGlare; -/* qdn: tonemap node */ +/** Tonemap node. */ typedef struct NodeTonemap { float key, offset, gamma; float f, m, a, c; int type; } NodeTonemap; -/* qdn: lens distortion node */ +/** Lens distortion node. */ typedef struct NodeLensDist { short jit, proj, fit; char _pad[2]; @@ -1032,6 +1051,11 @@ typedef struct NodeShaderTexPointDensity { char _pad2[4]; } NodeShaderTexPointDensity; +typedef struct NodeShaderPrincipled { + char use_subsurface_auto_radius; + char _pad[3]; +} NodeShaderPrincipled; + /* TEX_output */ typedef struct TexNodeOutput { char name[64]; @@ -1167,6 +1191,7 @@ typedef struct NodeCryptomatte { typedef struct NodeDenoise { char hdr; + char prefilter; } NodeDenoise; typedef struct NodeAttributeClamp { @@ -1216,6 +1241,11 @@ typedef struct NodeAttributeMix { uint8_t input_type_b; } NodeAttributeMix; +typedef struct NodeRandomValue { + /* CustomDataType. */ + uint8_t data_type; +} NodeRandomValue; + typedef struct NodeAttributeRandomize { /* CustomDataType. */ uint8_t data_type; @@ -1261,10 +1291,22 @@ typedef struct NodeAttributeCurveMap { CurveMapping *curve_rgb; } NodeAttributeCurveMap; +typedef struct NodeInputBool { + uint8_t boolean; +} NodeInputBool; + +typedef struct NodeInputInt { + int integer; +} NodeInputInt; + typedef struct NodeInputVector { float vector[3]; } NodeInputVector; +typedef struct NodeInputColor { + float color[4]; +} NodeInputColor; + typedef struct NodeInputString { char *string; } NodeInputString; @@ -1332,6 +1374,11 @@ typedef struct NodeGeometryAttributeProximity { uint8_t target_geometry_element; } NodeGeometryAttributeProximity; +typedef struct NodeGeometryProximity { + /* GeometryNodeProximityTargetType. */ + uint8_t target_element; +} NodeGeometryProximity; + typedef struct NodeGeometryVolumeToMesh { /* VolumeToMeshResolutionMode */ uint8_t resolution_mode; @@ -1397,6 +1444,11 @@ typedef struct NodeGeometryCurveSplineType { uint8_t spline_type; } NodeGeometryCurveSplineType; +typedef struct NodeGeometrySetCurveHandlePositions { + /* GeometryNodeCurveHandleMode. */ + uint8_t mode; +} NodeGeometrySetCurveHandlePositions; + typedef struct NodeGeometryCurveSetHandles { /* GeometryNodeCurveHandleType. */ uint8_t handle_type; @@ -1432,7 +1484,7 @@ typedef struct NodeGeometryCurvePrimitiveQuad { } NodeGeometryCurvePrimitiveQuad; typedef struct NodeGeometryCurveResample { - /* GeometryNodeCurveSampleMode. */ + /* GeometryNodeCurveResampleMode. */ uint8_t mode; } NodeGeometryCurveResample; @@ -1441,16 +1493,26 @@ typedef struct NodeGeometryCurveSubdivide { uint8_t cuts_type; } NodeGeometryCurveSubdivide; +typedef struct NodeGeometryCurveFillet { + /* GeometryNodeCurveFilletMode. */ + uint8_t mode; +} NodeGeometryCurveFillet; + typedef struct NodeGeometryCurveTrim { - /* GeometryNodeCurveInterpolateMode. */ + /* GeometryNodeCurveSampleMode. */ uint8_t mode; } NodeGeometryCurveTrim; typedef struct NodeGeometryCurveToPoints { - /* GeometryNodeCurveSampleMode. */ + /* GeometryNodeCurveResampleMode. */ uint8_t mode; } NodeGeometryCurveToPoints; +typedef struct NodeGeometryCurveSample { + /* GeometryNodeCurveSampleMode. */ + uint8_t mode; +} NodeGeometryCurveSample; + typedef struct NodeGeometryAttributeTransfer { /* AttributeDomain. */ int8_t domain; @@ -1458,19 +1520,38 @@ typedef struct NodeGeometryAttributeTransfer { uint8_t mapping; } NodeGeometryAttributeTransfer; +typedef struct NodeGeometryTransferAttribute { + /* CustomDataType. */ + int8_t data_type; + /* AttributeDomain. */ + int8_t domain; + /* GeometryNodeAttributeTransferMode. */ + uint8_t mode; + char _pad[1]; +} NodeGeometryTransferAttribute; + typedef struct NodeGeometryRaycast { /* GeometryNodeRaycastMapMode. */ uint8_t mapping; + /* CustomDataType. */ + int8_t data_type; + + /* Deprecated input types in new Ray-cast node. Can be removed when legacy nodes are no longer + * supported. */ uint8_t input_type_ray_direction; uint8_t input_type_ray_length; - char _pad[1]; } NodeGeometryRaycast; typedef struct NodeGeometryCurveFill { uint8_t mode; } NodeGeometryCurveFill; +typedef struct NodeGeometryMeshToPoints { + /* GeometryNodeMeshToPointsMode */ + uint8_t mode; +} NodeGeometryMeshToPoints; + typedef struct NodeGeometryAttributeCapture { /* CustomDataType. */ int8_t data_type; @@ -1478,6 +1559,38 @@ typedef struct NodeGeometryAttributeCapture { int8_t domain; } NodeGeometryAttributeCapture; +typedef struct NodeGeometryStringToCurves { + /* GeometryNodeStringToCurvesOverflowMode */ + uint8_t overflow; + /* GeometryNodeStringToCurvesAlignXMode */ + uint8_t align_x; + /* GeometryNodeStringToCurvesAlignYMode */ + uint8_t align_y; + char _pad[1]; +} NodeGeometryStringToCurves; + +typedef struct NodeGeometryDeleteGeometry { + /* AttributeDomain. */ + int8_t domain; + /* GeometryNodeDeleteGeometryMode. */ + int8_t mode; +} NodeGeometryDeleteGeometry; + +typedef struct NodeGeometrySeparateGeometry { + /* AttributeDomain. */ + int8_t domain; +} NodeGeometrySeparateGeometry; + +typedef struct NodeGeometryImageTexture { + int interpolation; + int extension; +} NodeGeometryImageTexture; + +typedef struct NodeGeometryViewer { + /* CustomDataType. */ + int8_t data_type; +} NodeGeometryViewer; + /* script node mode */ #define NODE_SCRIPT_INTERNAL 0 #define NODE_SCRIPT_EXTERNAL 1 @@ -1797,11 +1910,12 @@ enum { enum { #ifdef DNA_DEPRECATED_ALLOW SHD_SUBSURFACE_COMPATIBLE = 0, /* Deprecated */ -#endif SHD_SUBSURFACE_CUBIC = 1, SHD_SUBSURFACE_GAUSSIAN = 2, +#endif SHD_SUBSURFACE_BURLEY = 3, - SHD_SUBSURFACE_RANDOM_WALK = 4, + SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS = 4, + SHD_SUBSURFACE_RANDOM_WALK = 5, }; /* blur node */ @@ -1840,6 +1954,14 @@ typedef enum CMPNodeSetAlphaMode { CMP_NODE_SETALPHA_MODE_REPLACE_ALPHA = 1, } CMPNodeSetAlphaMode; +/* Denoise Node. */ +/* `NodeDenoise.prefilter` */ +typedef enum CMPNodeDenoisePrefilter { + CMP_NODE_DENOISE_PREFILTER_FAST = 0, + CMP_NODE_DENOISE_PREFILTER_NONE = 1, + CMP_NODE_DENOISE_PREFILTER_ACCURATE = 2 +} CMPNodeDenoisePrefilter; + #define CMP_NODE_PLANETRACKDEFORM_MBLUR_SAMPLES_MAX 64 /* Point Density shader node */ @@ -1882,6 +2004,12 @@ typedef enum GeometryNodeAttributeProximityTargetType { GEO_NODE_PROXIMITY_TARGET_FACES = 2, } GeometryNodeAttributeProximityTargetType; +typedef enum GeometryNodeProximityTargetType { + GEO_NODE_PROX_TARGET_POINTS = 0, + GEO_NODE_PROX_TARGET_EDGES = 1, + GEO_NODE_PROX_TARGET_FACES = 2, +} GeometryNodeProximityTargetType; + typedef enum GeometryNodeBooleanOperation { GEO_NODE_BOOLEAN_INTERSECT = 0, GEO_NODE_BOOLEAN_UNION = 1, @@ -1947,11 +2075,21 @@ typedef enum GeometryNodePointDistributeMode { GEO_NODE_POINT_DISTRIBUTE_POISSON = 1, } GeometryNodePointDistributeMode; +typedef enum GeometryNodeDistributePointsOnFacesMode { + GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM = 0, + GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON = 1, +} GeometryNodeDistributePointsOnFacesMode; + typedef enum GeometryNodeRotatePointsType { GEO_NODE_POINT_ROTATE_TYPE_EULER = 0, GEO_NODE_POINT_ROTATE_TYPE_AXIS_ANGLE = 1, } GeometryNodeRotatePointsType; +typedef enum FunctionNodeRotatePointsType { + FN_NODE_ROTATE_EULER_TYPE_EULER = 0, + FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE = 1, +} FunctionNodeRotatePointsType; + typedef enum GeometryNodeAttributeVectorRotateMode { GEO_NODE_VECTOR_ROTATE_TYPE_AXIS = 0, GEO_NODE_VECTOR_ROTATE_TYPE_AXIS_X = 1, @@ -1972,6 +2110,11 @@ typedef enum GeometryNodeRotatePointsSpace { GEO_NODE_POINT_ROTATE_SPACE_POINT = 1, } GeometryNodeRotatePointsSpace; +typedef enum FunctionNodeRotateEulerSpace { + FN_NODE_ROTATE_EULER_SPACE_OBJECT = 0, + FN_NODE_ROTATE_EULER_SPACE_LOCAL = 1, +} FunctionNodeRotateEulerSpace; + typedef enum GeometryNodeAlignRotationToVectorAxis { GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_X = 0, GEO_NODE_ALIGN_ROTATION_TO_VECTOR_AXIS_Y = 1, @@ -1985,6 +2128,19 @@ typedef enum GeometryNodeAlignRotationToVectorPivotAxis { GEO_NODE_ALIGN_ROTATION_TO_VECTOR_PIVOT_AXIS_Z = 3, } GeometryNodeAlignRotationToVectorPivotAxis; +typedef enum NodeAlignEulerToVectorAxis { + FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_X = 0, + FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Y = 1, + FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Z = 2, +} NodeAlignEulerToVectorAxis; + +typedef enum NodeAlignEulerToVectorPivotAxis { + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO = 0, + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_X = 1, + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Y = 2, + FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Z = 3, +} NodeAlignEulerToVectorPivotAxis; + typedef enum GeometryNodeTransformSpace { GEO_NODE_TRANSFORM_SPACE_ORIGINAL = 0, GEO_NODE_TRANSFORM_SPACE_RELATIVE = 1, @@ -2029,21 +2185,32 @@ typedef enum GeometryNodeCurvePrimitiveBezierSegmentMode { GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT_OFFSET = 1, } GeometryNodeCurvePrimitiveBezierSegmentMode; +typedef enum GeometryNodeCurveResampleMode { + GEO_NODE_CURVE_RESAMPLE_COUNT = 0, + GEO_NODE_CURVE_RESAMPLE_LENGTH = 1, + GEO_NODE_CURVE_RESAMPLE_EVALUATED = 2, +} GeometryNodeCurveResampleMode; + typedef enum GeometryNodeCurveSampleMode { - GEO_NODE_CURVE_SAMPLE_COUNT = 0, + GEO_NODE_CURVE_SAMPLE_FACTOR = 0, GEO_NODE_CURVE_SAMPLE_LENGTH = 1, - GEO_NODE_CURVE_SAMPLE_EVALUATED = 2, } GeometryNodeCurveSampleMode; -typedef enum GeometryNodeCurveInterpolateMode { - GEO_NODE_CURVE_INTERPOLATE_FACTOR = 0, - GEO_NODE_CURVE_INTERPOLATE_LENGTH = 1, -} GeometryNodeCurveInterpolateMode; +typedef enum GeometryNodeCurveFilletMode { + GEO_NODE_CURVE_FILLET_BEZIER = 0, + GEO_NODE_CURVE_FILLET_POLY = 1, +} GeometryNodeCurveFilletMode; typedef enum GeometryNodeAttributeTransferMapMode { + GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, + GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST = 1, +} GeometryNodeAttributeTransferMapMode; + +typedef enum GeometryNodeAttributeTransferMode { GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED = 0, GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST = 1, -} GeometryNodeAttributeTransferMapMode; + GEO_NODE_ATTRIBUTE_TRANSFER_INDEX = 2, +} GeometryNodeAttributeTransferMode; typedef enum GeometryNodeRaycastMapMode { GEO_NODE_RAYCAST_INTERPOLATED = 0, @@ -2055,6 +2222,41 @@ typedef enum GeometryNodeCurveFillMode { GEO_NODE_CURVE_FILL_MODE_NGONS = 1, } GeometryNodeCurveFillMode; +typedef enum GeometryNodeMeshToPointsMode { + GEO_NODE_MESH_TO_POINTS_VERTICES = 0, + GEO_NODE_MESH_TO_POINTS_EDGES = 1, + GEO_NODE_MESH_TO_POINTS_FACES = 2, + GEO_NODE_MESH_TO_POINTS_CORNERS = 3, +} GeometryNodeMeshToPointsMode; + +typedef enum GeometryNodeStringToCurvesOverflowMode { + GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW = 0, + GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT = 1, + GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE = 2, +} GeometryNodeStringToCurvesOverflowMode; + +typedef enum GeometryNodeStringToCurvesAlignXMode { + GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT = 0, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER = 1, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT = 2, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY = 3, + GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH = 4, +} GeometryNodeStringToCurvesAlignXMode; + +typedef enum GeometryNodeStringToCurvesAlignYMode { + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE = 0, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP = 1, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE = 2, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE = 3, + GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM = 4, +} GeometryNodeStringToCurvesAlignYMode; + +typedef enum GeometryNodeDeleteGeometryMode { + GEO_NODE_DELETE_GEOMETRY_MODE_ALL = 0, + GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE = 1, + GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE = 2, +} GeometryNodeDeleteGeometryMode; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_object_types.h b/source/blender/makesdna/DNA_object_types.h index 5a88ce7c9f5..e94541fdc7f 100644 --- a/source/blender/makesdna/DNA_object_types.h +++ b/source/blender/makesdna/DNA_object_types.h @@ -501,6 +501,9 @@ enum { /* check if the object type supports materials */ #define OB_TYPE_SUPPORT_MATERIAL(_type) \ (((_type) >= OB_MESH && (_type) <= OB_MBALL) || ((_type) >= OB_GPENCIL && (_type) <= OB_VOLUME)) +/** Does the object have some render-able geometry (unlike empties, cameras, etc.). */ +#define OB_TYPE_IS_GEOMETRY(_type) \ + (ELEM(_type, OB_MESH, OB_SURF, OB_FONT, OB_MBALL, OB_GPENCIL, OB_HAIR, OB_POINTCLOUD, OB_VOLUME)) #define OB_TYPE_SUPPORT_VGROUP(_type) (ELEM(_type, OB_MESH, OB_LATTICE, OB_GPENCIL)) #define OB_TYPE_SUPPORT_EDITMODE(_type) \ (ELEM(_type, OB_MESH, OB_FONT, OB_CURVE, OB_SURF, OB_MBALL, OB_LATTICE, OB_ARMATURE)) diff --git a/source/blender/makesdna/DNA_scene_defaults.h b/source/blender/makesdna/DNA_scene_defaults.h index 61707964191..9ecf94ebd6e 100644 --- a/source/blender/makesdna/DNA_scene_defaults.h +++ b/source/blender/makesdna/DNA_scene_defaults.h @@ -134,8 +134,6 @@ .border.ymin = 0.0f, \ .border.xmax = 1.0f, \ .border.ymax = 1.0f, \ - \ - .preview_start_resolution = 64, \ \ .line_thickness_mode = R_LINE_THICKNESS_ABSOLUTE, \ .unit_line_thickness = 1.0f, \ diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 7800e7f9efe..eeff5473d16 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -34,7 +34,7 @@ #include "DNA_ID.h" #include "DNA_color_types.h" /* color management */ -#include "DNA_customdata_types.h" /* Scene's runtime cddata masks. */ +#include "DNA_customdata_types.h" /* Scene's runtime custom-data masks. */ #include "DNA_layer_types.h" #include "DNA_listBase.h" #include "DNA_vec_types.h" @@ -261,7 +261,7 @@ typedef enum eScenePassType { SCE_PASS_UNUSED_3 = (1 << 4), /* SPEC */ SCE_PASS_SHADOW = (1 << 5), SCE_PASS_AO = (1 << 6), - SCE_PASS_UNUSED_4 = (1 << 7), /* REFLECT */ + SCE_PASS_POSITION = (1 << 7), SCE_PASS_NORMAL = (1 << 8), SCE_PASS_VECTOR = (1 << 9), SCE_PASS_UNUSED_5 = (1 << 10), /* REFRACT */ @@ -293,6 +293,7 @@ typedef enum eScenePassType { #define RE_PASSNAME_COMBINED "Combined" #define RE_PASSNAME_Z "Depth" #define RE_PASSNAME_VECTOR "Vector" +#define RE_PASSNAME_POSITION "Position" #define RE_PASSNAME_NORMAL "Normal" #define RE_PASSNAME_UV "UV" #define RE_PASSNAME_EMIT "Emit" @@ -592,7 +593,7 @@ typedef enum eBakeSaveMode { /** #BakeData.pass_filter */ typedef enum eBakePassFilter { R_BAKE_PASS_FILTER_NONE = 0, - R_BAKE_PASS_FILTER_AO = (1 << 0), + R_BAKE_PASS_FILTER_UNUSED = (1 << 0), R_BAKE_PASS_FILTER_EMIT = (1 << 1), R_BAKE_PASS_FILTER_DIFFUSE = (1 << 2), R_BAKE_PASS_FILTER_GLOSSY = (1 << 3), @@ -653,7 +654,8 @@ typedef struct RenderData { /** * render tile dimensions */ - int tilex, tiley; + int tilex DNA_DEPRECATED; + int tiley DNA_DEPRECATED; short planes DNA_DEPRECATED; short imtype DNA_DEPRECATED; @@ -764,13 +766,10 @@ typedef struct RenderData { /* Cycles baking */ struct BakeData bake; - int preview_start_resolution; + int _pad8; short preview_pixel_size; - /* Type of the debug pass to use. - * Only used when built with debug passes support. - */ - short debug_pass_type; + short _pad4; /* MultiView */ /** SceneRenderView. */ @@ -1344,6 +1343,7 @@ typedef struct SequencerToolSettings { /** When there are many snap points, 0-1 range corresponds to resolution from boundbox to all * possible snap points. */ int snap_distance; + int pivot_point; } SequencerToolSettings; typedef enum eSeqOverlapMode { @@ -1463,14 +1463,15 @@ typedef struct ToolSettings { char edge_mode_live_unwrap; - char _pad1[1]; - /* Transform */ char transform_pivot_point; char transform_flag; - char snap_mode, snap_node_mode; + char snap_mode; + char snap_node_mode; char snap_uv_mode; char snap_flag; + /** UV equivalent of `snap_flag`, limited to: #SCE_SNAP_ABS_GRID. */ + char snap_uv_flag; char snap_target; char snap_transform_mode_flag; @@ -1886,12 +1887,12 @@ enum { #define R_COMP_CROP (1 << 7) #define R_SCEMODE_UNUSED_8 (1 << 8) /* cleared */ #define R_SINGLE_LAYER (1 << 9) -#define R_EXR_TILE_FILE (1 << 10) +#define R_SCEMODE_UNUSED_10 (1 << 10) /* cleared */ #define R_SCEMODE_UNUSED_11 (1 << 11) /* cleared */ #define R_NO_IMAGE_LOAD (1 << 12) #define R_SCEMODE_UNUSED_13 (1 << 13) /* cleared */ #define R_NO_FRAME_UPDATE (1 << 14) -#define R_FULL_SAMPLE (1 << 15) +#define R_SCEMODE_UNUSED_15 (1 << 15) /* cleared */ #define R_SCEMODE_UNUSED_16 (1 << 16) /* cleared */ #define R_SCEMODE_UNUSED_17 (1 << 17) /* cleared */ #define R_TEXNODE_PREVIEW (1 << 18) diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index d5b7458ae7b..86fd6b9744a 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -650,7 +650,7 @@ enum { /* regiontype, first two are the default set */ /* Do NOT change order, append on end. Types are hardcoded needed */ -typedef enum eRegionType { +typedef enum eRegion_Type { RGN_TYPE_WINDOW = 0, RGN_TYPE_HEADER = 1, RGN_TYPE_CHANNELS = 2, @@ -666,9 +666,12 @@ typedef enum eRegionType { RGN_TYPE_EXECUTE = 10, RGN_TYPE_FOOTER = 11, RGN_TYPE_TOOL_HEADER = 12, + /* Region type used exclusively by internal code and add-ons to register draw callbacks to the XR + context (surface, mirror view). Does not represent any real region. */ + RGN_TYPE_XR = 13, -#define RGN_TYPE_LEN (RGN_TYPE_TOOL_HEADER + 1) -} eRegionType; +#define RGN_TYPE_LEN (RGN_TYPE_XR + 1) +} eRegion_Type; /* use for function args */ #define RGN_TYPE_ANY -1 diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 03c38eb71a0..fc23d3c69a3 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -74,12 +74,18 @@ typedef struct StripTransform { float scale_x; float scale_y; float rotation; + /** 0-1 range, use SEQ_image_transform_origin_offset_pixelspace_get to convert to pixel space. */ + float origin[2]; } StripTransform; typedef struct StripColorBalance { + int method; float lift[3]; float gamma[3]; float gain[3]; + float slope[3]; + float offset[3]; + float power[3]; int flag; char _pad[4]; /* float exposure; */ @@ -173,7 +179,9 @@ typedef struct Sequence { /** Starting and ending points of the strip in the sequence. */ int startdisp, enddisp; float sat; - float mul, handsize; + float mul; + char tmp_tag; + char _pad[3]; short anim_preseek; /* UNUSED. */ /** Streamindex for movie or sound files with several streams. */ @@ -230,15 +238,21 @@ typedef struct Sequence { int blend_mode; float blend_opacity; + /* Tag color showed if `SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG` is set. */ + int8_t color_tag; + + char alpha_mode; + char _pad4[2]; + + int cache_flag; + /* is sfra needed anymore? - it looks like its only used in one place */ /** Starting frame according to the timeline of the scene. */ int sfra; - char alpha_mode; - char _pad[2]; - /* Multiview */ char views_format; + char _pad1[3]; struct Stereo3dFormat *stereo3d_format; struct IDProperty *prop; @@ -246,9 +260,6 @@ typedef struct Sequence { /* modifiers */ ListBase modifiers; - int cache_flag; - int _pad2[3]; - SequenceRuntime runtime; } Sequence; @@ -280,9 +291,11 @@ typedef struct Editing { /** 1024 = FILE_MAX. */ char proxy_dir[1024]; - int over_ofs, over_cfra; - int over_flag, proxy_storage; - rctf over_border; + int proxy_storage; + + int overlay_frame_ofs, overlay_frame_abs; + int overlay_frame_flag; + rctf overlay_frame_rect; struct SeqCache *cache; @@ -429,6 +442,11 @@ typedef struct ColorBalanceModifierData { float color_multiply; } ColorBalanceModifierData; +enum { + SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN = 0, + SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER = 1, +}; + typedef struct CurvesModifierData { SequenceModifierData modifier; @@ -484,13 +502,13 @@ typedef struct SequencerScopes { struct ImBuf *histogram_ibuf; } SequencerScopes; -#define MAXSEQ 32 +#define MAXSEQ 128 #define SELECT 1 -/* Editor->over_flag */ -#define SEQ_EDIT_OVERLAY_SHOW 1 -#define SEQ_EDIT_OVERLAY_ABS 2 +/** #Editor.overlay_frame_flag */ +#define SEQ_EDIT_OVERLAY_FRAME_SHOW 1 +#define SEQ_EDIT_OVERLAY_FRAME_ABS 2 #define SEQ_STRIP_OFSBOTTOM 0.05f #define SEQ_STRIP_OFSTOP 0.95f @@ -516,7 +534,7 @@ enum { SEQ_OVERLAP = (1 << 3), SEQ_FILTERY = (1 << 4), SEQ_MUTE = (1 << 5), - SEQ_FLAG_UNUSED_6 = (1 << 6), /* cleared */ + SEQ_FLAG_SKIP_THUMBNAILS = (1 << 6), SEQ_REVERSE_FRAMES = (1 << 7), SEQ_IPO_FRAME_LOCKED = (1 << 8), SEQ_EFFECT_NOT_LOADED = (1 << 9), @@ -566,6 +584,9 @@ enum { #define SEQ_COLOR_BALANCE_INVERSE_GAIN 1 #define SEQ_COLOR_BALANCE_INVERSE_GAMMA 2 #define SEQ_COLOR_BALANCE_INVERSE_LIFT 4 +#define SEQ_COLOR_BALANCE_INVERSE_SLOPE 8 +#define SEQ_COLOR_BALANCE_INVERSE_OFFSET 16 +#define SEQ_COLOR_BALANCE_INVERSE_POWER 32 /* !!! has to be same as IMB_imbuf.h IMB_PROXY_... and IMB_TC_... */ @@ -722,8 +743,25 @@ enum { SEQ_CACHE_PREFETCH_ENABLE = (1 << 10), SEQ_CACHE_DISK_CACHE_ENABLE = (1 << 11), + SEQ_CACHE_STORE_THUMBNAIL = (1 << 12), }; +/* Sequence->color_tag. */ +typedef enum SequenceColorTag { + SEQUENCE_COLOR_NONE = -1, + SEQUENCE_COLOR_01, + SEQUENCE_COLOR_02, + SEQUENCE_COLOR_03, + SEQUENCE_COLOR_04, + SEQUENCE_COLOR_05, + SEQUENCE_COLOR_06, + SEQUENCE_COLOR_07, + SEQUENCE_COLOR_08, + SEQUENCE_COLOR_09, + + SEQUENCE_COLOR_TOT, +} SequenceColorTag; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index 5475e1bacd8..671cc182d7e 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -576,6 +576,47 @@ typedef enum eSpaceNla_Flag { /** \name Sequence Editor * \{ */ +typedef struct SequencerPreviewOverlay { + int flag; + char _pad0[4]; +} SequencerPreviewOverlay; + +/* SequencerPreviewOverlay.flag */ +typedef enum eSpaceSeq_SequencerPreviewOverlay_Flag { + SEQ_PREVIEW_SHOW_2D_CURSOR = (1 << 1), + SEQ_PREVIEW_SHOW_OUTLINE_SELECTED = (1 << 2), + SEQ_PREVIEW_SHOW_SAFE_MARGINS = (1 << 3), + SEQ_PREVIEW_SHOW_GPENCIL = (1 << 4), + SEQ_PREVIEW_SHOW_SAFE_CENTER = (1 << 9), + SEQ_PREVIEW_SHOW_METADATA = (1 << 10), +} eSpaceSeq_SequencerPreviewOverlay_Flag; + +typedef struct SequencerTimelineOverlay { + int flag; + char _pad0[4]; +} SequencerTimelineOverlay; + +/* SequencerTimelineOverlay.flag */ +typedef enum eSpaceSeq_SequencerTimelineOverlay_Flag { + SEQ_TIMELINE_SHOW_STRIP_OFFSETS = (1 << 1), + SEQ_TIMELINE_SHOW_THUMBNAILS = (1 << 2), + SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG = (1 << 3), /* use Sequence->color_tag */ + SEQ_TIMELINE_SHOW_FCURVES = (1 << 5), + SEQ_TIMELINE_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */ + SEQ_TIMELINE_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ + SEQ_TIMELINE_SHOW_STRIP_NAME = (1 << 14), + SEQ_TIMELINE_SHOW_STRIP_SOURCE = (1 << 15), + SEQ_TIMELINE_SHOW_STRIP_DURATION = (1 << 16), + SEQ_TIMELINE_SHOW_GRID = (1 << 18), +} eSpaceSeq_SequencerTimelineOverlay_Flag; + +typedef struct SpaceSeqRuntime { + /** Required for Thumbnail job start condition. */ + struct rctf last_thumbnail_area; + /** Stores lists of most recently displayed thumbnails. */ + struct GHash *last_displayed_thumbnails; +} SpaceSeqRuntime; + /* Sequencer */ typedef struct SpaceSeq { SpaceLink *next, *prev; @@ -601,26 +642,33 @@ typedef struct SpaceSeq { /** Deprecated, handled by View2D now. */ float zoom DNA_DEPRECATED; /** See SEQ_VIEW_* below. */ - int view; - int overlay_type; + char view; + char overlay_frame_type; /** Overlay an image of the editing on below the strips. */ - int draw_flag; + char draw_flag; + char gizmo_flag; char _pad[4]; + /** 2D cursor for transform. */ + float cursor[2]; + /** Grease-pencil data. */ struct bGPdata *gpd; /** Different scoped displayed in space. */ struct SequencerScopes scopes; + struct SequencerPreviewOverlay preview_overlay; + struct SequencerTimelineOverlay timeline_overlay; /** Multiview current eye - for internal use. */ char multiview_eye; char _pad2[7]; + + SpaceSeqRuntime runtime; } SpaceSeq; /* SpaceSeq.mainb */ typedef enum eSpaceSeq_RegionType { - SEQ_DRAW_SEQUENCE = 0, SEQ_DRAW_IMG_IMBUF = 1, SEQ_DRAW_IMG_WAVEFORM = 2, SEQ_DRAW_IMG_VECTORSCOPE = 3, @@ -630,7 +678,7 @@ typedef enum eSpaceSeq_RegionType { /* SpaceSeq.draw_flag */ typedef enum eSpaceSeq_DrawFlag { SEQ_DRAW_BACKDROP = (1 << 0), - SEQ_DRAW_OFFSET_EXT = (1 << 1), + SEQ_DRAW_UNUSED_1 = (1 << 1), SEQ_DRAW_TRANSFORM_PREVIEW = (1 << 2), } eSpaceSeq_DrawFlag; @@ -639,20 +687,18 @@ typedef enum eSpaceSeq_Flag { SEQ_DRAWFRAMES = (1 << 0), SEQ_MARKER_TRANS = (1 << 1), SEQ_DRAW_COLOR_SEPARATED = (1 << 2), - SEQ_SHOW_SAFE_MARGINS = (1 << 3), - SEQ_SHOW_GPENCIL = (1 << 4), - SEQ_SHOW_FCURVES = (1 << 5), - SEQ_USE_ALPHA = (1 << 6), /* use RGBA display mode for preview */ - SEQ_ALL_WAVEFORMS = (1 << 7), /* draw all waveforms */ - SEQ_NO_WAVEFORMS = (1 << 8), /* draw no waveforms */ - SEQ_SHOW_SAFE_CENTER = (1 << 9), - SEQ_SHOW_METADATA = (1 << 10), + SPACE_SEQ_FLAG_UNUSED_3 = (1 << 3), + SPACE_SEQ_FLAG_UNUSED_4 = (1 << 4), + SPACE_SEQ_FLAG_UNUSED_5 = (1 << 5), + SEQ_USE_ALPHA = (1 << 6), /* use RGBA display mode for preview */ + SPACE_SEQ_FLAG_UNUSED_9 = (1 << 9), + SPACE_SEQ_FLAG_UNUSED_10 = (1 << 10), SEQ_SHOW_MARKERS = (1 << 11), /* show markers region */ SEQ_ZOOM_TO_FIT = (1 << 12), - SEQ_SHOW_STRIP_OVERLAY = (1 << 13), - SEQ_SHOW_STRIP_NAME = (1 << 14), - SEQ_SHOW_STRIP_SOURCE = (1 << 15), - SEQ_SHOW_STRIP_DURATION = (1 << 16), + SEQ_SHOW_OVERLAY = (1 << 13), + SPACE_SEQ_FLAG_UNUSED_14 = (1 << 14), + SPACE_SEQ_FLAG_UNUSED_15 = (1 << 15), + SPACE_SEQ_FLAG_UNUSED_16 = (1 << 16), SEQ_USE_PROXIES = (1 << 17), SEQ_SHOW_GRID = (1 << 18), } eSpaceSeq_Flag; @@ -685,12 +731,21 @@ typedef struct MaskSpaceInfo { char _pad3[5]; } MaskSpaceInfo; +/** #SpaceSeq.gizmo_flag */ +enum { + /** All gizmos. */ + SEQ_GIZMO_HIDE = (1 << 0), + SEQ_GIZMO_HIDE_NAVIGATE = (1 << 1), + SEQ_GIZMO_HIDE_CONTEXT = (1 << 2), + SEQ_GIZMO_HIDE_TOOL = (1 << 3), +}; + /* SpaceSeq.mainb */ -typedef enum eSpaceSeq_OverlayType { - SEQ_DRAW_OVERLAY_RECT = 0, - SEQ_DRAW_OVERLAY_REFERENCE = 1, - SEQ_DRAW_OVERLAY_CURRENT = 2, -} eSpaceSeq_OverlayType; +typedef enum eSpaceSeq_OverlayFrameType { + SEQ_OVERLAY_FRAME_TYPE_RECT = 0, + SEQ_OVERLAY_FRAME_TYPE_REFERENCE = 1, + SEQ_OVERLAY_FRAME_TYPE_CURRENT = 2, +} eSpaceSeq_OverlayFrameType; /** \} */ @@ -718,7 +773,7 @@ typedef struct FileSelectParams { const ID *rename_id; void *_pad3; - /** List of filetypes to filter (FILE_MAXFILE). */ + /** List of file-types to filter (#FILE_MAXFILE). */ char filter_glob[256]; /** Text items name must match to be shown. */ @@ -764,14 +819,25 @@ typedef struct FileAssetSelectParams { FileSelectParams base_params; AssetLibraryReference asset_library_ref; + short asset_catalog_visibility; /* eFileSel_Params_AssetCatalogVisibility */ + char _pad[6]; + /** If #asset_catalog_visibility is #FILE_SHOW_ASSETS_FROM_CATALOG, this sets the ID of the + * catalog to show. */ + bUUID catalog_id; short import_type; /* eFileAssetImportType */ - char _pad[6]; + char _pad2[6]; } FileAssetSelectParams; typedef enum eFileAssetImportType { + /** Regular data-block linking. */ FILE_ASSET_IMPORT_LINK = 0, + /** Regular data-block appending (basically linking + "Make Local"). */ FILE_ASSET_IMPORT_APPEND = 1, + /** Append data-block with the #BLO_LIBLINK_APPEND_LOCAL_ID_REUSE flag enabled. Some typically + * heavy data dependencies (e.g. the image data-blocks of a material, the mesh of an object) may + * be reused from an earlier append. */ + FILE_ASSET_IMPORT_APPEND_REUSE = 2, } eFileAssetImportType; /** @@ -924,7 +990,10 @@ enum eFileDetails { typedef enum eFileSelectType { FILE_LOADLIB = 1, FILE_MAIN = 2, + /** Load assets from #Main. */ FILE_MAIN_ASSET = 3, + /** Load assets of an asset library containing external files. */ + FILE_ASSET_LIBRARY = 4, FILE_UNIX = 8, FILE_BLENDER = 8, /* don't display relative paths */ @@ -943,23 +1012,31 @@ typedef enum eFileSel_Action { * (WM and BLO code area, see #eBLOLibLinkFlags in BLO_readfile.h). */ typedef enum eFileSel_Params_Flag { - FILE_APPEND_SET_FAKEUSER = (1 << 0), + FILE_PARAMS_FLAG_UNUSED_1 = (1 << 0), FILE_RELPATH = (1 << 1), FILE_LINK = (1 << 2), FILE_HIDE_DOT = (1 << 3), FILE_AUTOSELECT = (1 << 4), FILE_ACTIVE_COLLECTION = (1 << 5), - FILE_APPEND_RECURSIVE = (1 << 6), + FILE_PARAMS_FLAG_UNUSED_2 = (1 << 6), FILE_DIRSEL_ONLY = (1 << 7), FILE_FILTER = (1 << 8), - FILE_OBDATA_INSTANCE = (1 << 9), - FILE_COLLECTION_INSTANCE = (1 << 10), + FILE_PARAMS_FLAG_UNUSED_3 = (1 << 9), + FILE_PARAMS_FLAG_UNUSED_4 = (1 << 10), FILE_SORT_INVERT = (1 << 11), FILE_HIDE_TOOL_PROPS = (1 << 12), FILE_CHECK_EXISTING = (1 << 13), FILE_ASSETS_ONLY = (1 << 14), + /** Enables filtering by asset catalog. */ + FILE_FILTER_ASSET_CATALOG = (1 << 15), } eFileSel_Params_Flag; +typedef enum eFileSel_Params_AssetCatalogVisibility { + FILE_SHOW_ASSETS_ALL_CATALOGS, + FILE_SHOW_ASSETS_FROM_CATALOG, + FILE_SHOW_ASSETS_WITHOUT_CATALOG, +} eFileSel_Params_AssetCatalogVisibility; + /* sfile->params->rename_flag */ /* NOTE: short flag. Defined as bitflags, but currently only used as exclusive status markers... */ typedef enum eFileSel_Params_RenameFlag { @@ -1074,7 +1151,6 @@ typedef struct FileDirEntryArr { ListBase entries; int nbr_entries; int nbr_entries_filtered; - int entry_idx_start, entry_idx_end; /** FILE_MAX. */ char root[1024]; @@ -1134,13 +1210,10 @@ typedef struct SpaceImage { char mode_prev; char pin; - char _pad1; - /** - * The currently active tile of the image when tile is enabled, - * is kept in sync with the active faces tile. - */ - short curtile; - short lock; + + char pixel_snap_mode; + + char lock; /** UV draw type. */ char dt_uv; /** Sticky selection type. */ @@ -1148,14 +1221,19 @@ typedef struct SpaceImage { char dt_uvstretch; char around; - int flag; + char _pad1[3]; - char pixel_snap_mode; - char _pad2[7]; + int flag; float uv_opacity; int tile_grid_shape[2]; + /** + * UV editor custom-grid. Value of `N` will produce `NxN` grid. + * Use when #SI_CUSTOM_GRID is set. + */ + int custom_grid_subdiv; + char _pad3[4]; MaskSpaceInfo mask_info; SpaceImageOverlay overlay; @@ -1211,6 +1289,7 @@ typedef enum eSpaceImage_Flag { SI_FLAG_UNUSED_7 = (1 << 7), /* cleared */ SI_FLAG_UNUSED_8 = (1 << 8), /* cleared */ SI_COORDFLOATS = (1 << 9), + SI_FLAG_UNUSED_10 = (1 << 10), SI_LIVE_UNWRAP = (1 << 11), SI_USE_ALPHA = (1 << 12), @@ -1222,7 +1301,7 @@ typedef enum eSpaceImage_Flag { SI_FULLWINDOW = (1 << 16), SI_FLAG_UNUSED_17 = (1 << 17), - SI_FLAG_UNUSED_18 = (1 << 18), /* cleared */ + SI_CUSTOM_GRID = (1 << 18), /** * This means that the image is drawn until it reaches the view edge, @@ -1248,6 +1327,9 @@ typedef enum eSpaceImageOverlay_Flag { SI_OVERLAY_SHOW_OVERLAYS = (1 << 0), } eSpaceImageOverlay_Flag; +/** Keep in sync with `STEPS_LEN` in `grid_frag.glsl`. */ +#define SI_GRID_STEPS_LEN 8 + /** \} */ /* -------------------------------------------------------------------- */ @@ -1344,7 +1426,7 @@ typedef enum eSpaceText_Flags { /* scrollable */ ST_SCROLL_SELECT = (1 << 0), - ST_FLAG_UNUSED_4 = (1 << 4), /* dirty */ + ST_FLAG_UNUSED_4 = (1 << 4), /* Cleared. */ ST_FIND_WRAP = (1 << 5), ST_FIND_ALL = (1 << 6), @@ -1427,6 +1509,15 @@ typedef struct bNodeTreePath { char display_name[64]; } bNodeTreePath; +typedef struct SpaceNodeOverlay { + int flag; +} SpaceNodeOverlay; + +typedef enum eSpaceNodeOverlay_Flag { + SN_OVERLAY_SHOW_OVERLAYS = (1 << 1), + SN_OVERLAY_SHOW_WIRE_COLORS = (1 << 2), +} eSpaceNodeOverlay_Flag; + typedef struct SpaceNode { SpaceLink *next, *prev; /** Storage of regions for inactive spaces. */ @@ -1480,6 +1571,9 @@ typedef struct SpaceNode { /** Grease-pencil data. */ struct bGPdata *gpd; + SpaceNodeOverlay overlay; + char _pad2[4]; + SpaceNode_Runtime *runtime; } SpaceNode; @@ -1912,6 +2006,7 @@ typedef enum eSpreadsheetColumnValueType { SPREADSHEET_VALUE_TYPE_FLOAT3 = 4, SPREADSHEET_VALUE_TYPE_COLOR = 5, SPREADSHEET_VALUE_TYPE_INSTANCES = 6, + SPREADSHEET_VALUE_TYPE_STRING = 7, } eSpreadsheetColumnValueType; /** diff --git a/source/blender/makesdna/DNA_tracking_types.h b/source/blender/makesdna/DNA_tracking_types.h index fb2d985d353..0e313183300 100644 --- a/source/blender/makesdna/DNA_tracking_types.h +++ b/source/blender/makesdna/DNA_tracking_types.h @@ -398,6 +398,8 @@ typedef struct MovieTrackingDopesheetChannel { int *segments; /** Longest segment length and total number of tracked frames. */ int max_segment, total_frames; + /** These numbers are valid only if tot_segment > 0. */ + int first_not_disabled_marker_framenr, last_not_disabled_marker_framenr; } MovieTrackingDopesheetChannel; typedef struct MovieTrackingDopesheetCoverageSegment { @@ -592,6 +594,8 @@ enum { TRACKING_DOPE_SORT_LONGEST = 1, TRACKING_DOPE_SORT_TOTAL = 2, TRACKING_DOPE_SORT_AVERAGE_ERROR = 3, + TRACKING_DOPE_SORT_START = 4, + TRACKING_DOPE_SORT_END = 5, }; /* MovieTrackingDopesheet->flag */ diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 4f86201ced2..4f91d6b2fbb 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -206,6 +206,9 @@ typedef struct ThemeUI { /** Intensity of the border icons. >0 will render an border around themed * icons. */ float icon_border_intensity; + float panel_roundness; + char _pad2[4]; + } ThemeUI; /* try to put them all in one, if needed a special struct can be created as well @@ -319,6 +322,8 @@ typedef struct ThemeSpace { unsigned char vertex_size, outline_width, obcenter_dia, facedot_size; unsigned char noodle_curving; unsigned char grid_levels; + char _pad5[3]; + float dash_alpha; /* syntax for textwindow and nodes */ unsigned char syntaxl[4], syntaxs[4]; /* in nodespace used for backdrop matte */ @@ -341,6 +346,7 @@ typedef struct ThemeSpace { unsigned char active_strip[4], selected_strip[4]; /** For dopesheet - scale factor for size of keyframes (i.e. height of channels). */ + char _pad7[1]; float keyframe_scale_fac; unsigned char editmesh_active[4]; @@ -458,6 +464,10 @@ typedef struct ThemeCollectionColor { unsigned char color[4]; } ThemeCollectionColor; +typedef struct ThemeStripColor { + unsigned char color[4]; +} ThemeStripColor; + /** * A theme. * @@ -500,8 +510,10 @@ typedef struct bTheme { /* See COLLECTION_COLOR_TOT for the number of collection colors. */ ThemeCollectionColor collection_color[8]; + /* See SEQUENCE_COLOR_TOT for the total number of strip colors. */ + ThemeStripColor strip_color[9]; + int active_theme_area; - char _pad0[4]; } bTheme; #define UI_THEMESPACE_START(btheme) \ @@ -635,7 +647,9 @@ typedef struct UserDef_Experimental { /* Debug options, always available. */ char use_undo_legacy; char no_override_auto_resync; + char no_proxy_to_override_conversion; char use_cycles_debug; + char use_geometry_nodes_legacy; char SANITIZE_AFTER_HERE; /* The following options are automatically sanitized (set to 0) * when the release cycle is not alpha. */ @@ -646,8 +660,7 @@ typedef struct UserDef_Experimental { char use_sculpt_tools_tilt; char use_extended_asset_browser; char use_override_templates; - char use_geometry_nodes_fields; - char _pad[4]; + char _pad[3]; /** `makesdna` does not allow empty structs. */ } UserDef_Experimental; @@ -1235,6 +1248,9 @@ typedef enum eDupli_ID_Flags { USER_DUP_HAIR = (1 << 14), USER_DUP_POINTCLOUD = (1 << 15), USER_DUP_VOLUME = (1 << 16), + USER_DUP_LATTICE = (1 << 17), + USER_DUP_CAMERA = (1 << 18), + USER_DUP_SPEAKER = (1 << 19), USER_DUP_OBDATA = (~0) & ((1 << 24) - 1), diff --git a/source/blender/makesdna/DNA_uuid_types.h b/source/blender/makesdna/DNA_uuid_types.h index 30c8beaa628..dcebfed6be7 100644 --- a/source/blender/makesdna/DNA_uuid_types.h +++ b/source/blender/makesdna/DNA_uuid_types.h @@ -28,15 +28,23 @@ extern "C" { /** * \brief Universally Unique Identifier according to RFC4122. + * + * Cannot be named simply `UUID`, because Windows already defines that type. */ -typedef struct UUID { +typedef struct bUUID { uint32_t time_low; uint16_t time_mid; uint16_t time_hi_and_version; uint8_t clock_seq_hi_and_reserved; uint8_t clock_seq_low; uint8_t node[6]; -} UUID; +} bUUID; + +/** + * Memory required for a string representation of a UUID according to RFC4122. + * This is 36 characters for the string + a trailing zero byte. + */ +#define UUID_STRING_LEN 37 #ifdef __cplusplus } diff --git a/source/blender/makesdna/DNA_view2d_types.h b/source/blender/makesdna/DNA_view2d_types.h index c385ac04bd3..f8166305fd9 100644 --- a/source/blender/makesdna/DNA_view2d_types.h +++ b/source/blender/makesdna/DNA_view2d_types.h @@ -132,6 +132,8 @@ enum { V2D_PIXELOFS_X = (1 << 2), /* apply pixel offsets on y-axis when setting view matrices */ V2D_PIXELOFS_Y = (1 << 3), + /* zoom, pan or similar action is in progress */ + V2D_IS_NAVIGATING = (1 << 9), /* view settings need to be set still... */ V2D_IS_INIT = (1 << 10), }; diff --git a/source/blender/makesdna/DNA_view3d_enums.h b/source/blender/makesdna/DNA_view3d_enums.h index aec52da1bf9..59012a0cd8d 100644 --- a/source/blender/makesdna/DNA_view3d_enums.h +++ b/source/blender/makesdna/DNA_view3d_enums.h @@ -30,6 +30,9 @@ typedef enum eV3DOffscreenDrawFlag { V3D_OFSDRAW_SHOW_ANNOTATION = (1 << 0), V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS = (1 << 1), V3D_OFSDRAW_SHOW_GRIDFLOOR = (1 << 2), + V3D_OFSDRAW_SHOW_SELECTION = (1 << 3), + V3D_OFSDRAW_XR_SHOW_CONTROLLERS = (1 << 4), + V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS = (1 << 5), } eV3DOffscreenDrawFlag; /** #View3DShading.light */ diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 4d88f6f0c15..9b5ed133feb 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -373,6 +373,7 @@ typedef struct View3D { #define V3D_HIDE_HELPLINES (1 << 2) #define V3D_FLAG_UNUSED_2 (1 << 3) /* cleared */ #define V3D_XR_SESSION_MIRROR (1 << 4) +#define V3D_XR_SESSION_SURFACE (1 << 5) #define V3D_FLAG_UNUSED_10 (1 << 10) /* cleared */ #define V3D_SELECT_OUTLINE (1 << 11) @@ -465,6 +466,8 @@ enum { #define V3D_FLAG2_UNUSED_13 (1 << 13) /* cleared */ #define V3D_FLAG2_UNUSED_14 (1 << 14) /* cleared */ #define V3D_FLAG2_UNUSED_15 (1 << 15) /* cleared */ +#define V3D_XR_SHOW_CONTROLLERS (1 << 16) +#define V3D_XR_SHOW_CUSTOM_OVERLAYS (1 << 17) /** #View3D.gp_flag (short) */ #define V3D_GP_FADE_OBJECTS (1 << 0) /* Fade all non GP objects */ @@ -497,6 +500,16 @@ enum { V3D_SHADING_STUDIOLIGHT_VIEW_ROTATION = (1 << 14), }; +#define V3D_USES_SCENE_LIGHTS(v3d) \ + ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS)) || \ + (((v3d)->shading.type == OB_RENDER) && \ + ((v3d)->shading.flag & V3D_SHADING_SCENE_LIGHTS_RENDER))) + +#define V3D_USES_SCENE_WORLD(v3d) \ + ((((v3d)->shading.type == OB_MATERIAL) && ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD)) || \ + (((v3d)->shading.type == OB_RENDER) && \ + ((v3d)->shading.flag & V3D_SHADING_SCENE_WORLD_RENDER))) + /** #View3DShading.cavity_type */ enum { V3D_SHADING_CAVITY_SSAO = 0, diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index a9016dd4edd..841edaf8724 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -59,7 +59,7 @@ struct wmTimer; #define KMAP_MAX_NAME 64 /* keep in sync with 'rna_enum_wm_report_items' in wm_rna.c */ -typedef enum ReportType { +typedef enum eReportType { RPT_DEBUG = (1 << 0), RPT_INFO = (1 << 1), RPT_OPERATOR = (1 << 2), @@ -69,7 +69,7 @@ typedef enum ReportType { RPT_ERROR_INVALID_INPUT = (1 << 6), RPT_ERROR_INVALID_CONTEXT = (1 << 7), RPT_ERROR_OUT_OF_MEMORY = (1 << 8), -} ReportType; +} eReportType; #define RPT_DEBUG_ALL (RPT_DEBUG) #define RPT_INFO_ALL (RPT_INFO) @@ -91,7 +91,7 @@ enum ReportListFlags { # typedef struct Report { struct Report *next, *prev; - /** ReportType. */ + /** eReportType. */ short type; short flag; /** `strlen(message)`, saves some time calculating the word wrap. */ @@ -103,9 +103,9 @@ typedef struct Report { /* saved in the wm, don't remove */ typedef struct ReportList { ListBase list; - /** ReportType. */ + /** eReportType. */ int printlevel; - /** ReportType. */ + /** eReportType. */ int storelevel; int flag; char _pad[4]; @@ -138,7 +138,14 @@ typedef struct wmWindowManager { ID id; /** Separate active from drawable. */ - struct wmWindow *windrawable, *winactive; + struct wmWindow *windrawable; + /** + * \note `CTX_wm_window(C)` is usually preferred. + * Avoid relying on this where possible as this may become NULL during when handling + * events that close or replace windows (opening a file for e.g.). + * While this happens rarely in practice, it can cause difficult to reproduce bugs. + */ + struct wmWindow *winactive; ListBase windows; /** Set on file read. */ diff --git a/source/blender/makesdna/DNA_workspace_types.h b/source/blender/makesdna/DNA_workspace_types.h index e0294d3534c..a0856588a58 100644 --- a/source/blender/makesdna/DNA_workspace_types.h +++ b/source/blender/makesdna/DNA_workspace_types.h @@ -29,6 +29,15 @@ extern "C" { #endif +/** #bToolRef_Runtime.flag */ +enum { + /** + * This tool should use the fallback key-map. + * Typically gizmos handle this but some tools (such as the knife tool) don't use a gizmo. + */ + TOOLREF_FLAG_FALLBACK_KEYMAP = (1 << 0), +}; + # # typedef struct bToolRef_Runtime { @@ -47,6 +56,8 @@ typedef struct bToolRef_Runtime { /** Index when a tool is a member of a group. */ int index; + /** Options: `TOOLREF_FLAG_*`. */ + int flag; } bToolRef_Runtime; /* Stored per mode. */ diff --git a/source/blender/makesdna/DNA_xr_types.h b/source/blender/makesdna/DNA_xr_types.h index a9d427777f7..f7da912f299 100644 --- a/source/blender/makesdna/DNA_xr_types.h +++ b/source/blender/makesdna/DNA_xr_types.h @@ -32,8 +32,8 @@ typedef struct XrSessionSettings { /** Shading settings, struct shared with 3D-View so settings are the same. */ struct View3DShading shading; - char _pad[7]; - + float base_scale; + char _pad[3]; char base_pose_type; /* #eXRSessionBasePoseType */ /** Object to take the location and rotation as base position from. */ Object *base_pose_object; @@ -42,7 +42,9 @@ typedef struct XrSessionSettings { /** View3D draw flags (V3D_OFSDRAW_NONE, V3D_OFSDRAW_SHOW_ANNOTATION, ...). */ char draw_flags; - char _pad2[3]; + /** Draw style for controller visualization. */ + char controller_draw_style; + char _pad2[2]; /** Clipping distance. */ float clip_start, clip_end; @@ -61,6 +63,13 @@ typedef enum eXRSessionBasePoseType { XR_BASE_POSE_CUSTOM = 2, } eXRSessionBasePoseType; +typedef enum eXrSessionControllerDrawStyle { + XR_CONTROLLER_DRAW_DARK = 0, + XR_CONTROLLER_DRAW_LIGHT = 1, + XR_CONTROLLER_DRAW_DARK_RAY = 2, + XR_CONTROLLER_DRAW_LIGHT_RAY = 3, +} eXrSessionControllerDrawStyle; + /** XR action type. Enum values match those in GHOST_XrActionType enum for consistency. */ typedef enum eXrActionType { XR_BOOLEAN_INPUT = 1, diff --git a/source/blender/makesdna/intern/dna_defaults.c b/source/blender/makesdna/intern/dna_defaults.c index 4cb8610f6ac..2dbbb35c3ca 100644 --- a/source/blender/makesdna/intern/dna_defaults.c +++ b/source/blender/makesdna/intern/dna_defaults.c @@ -318,7 +318,8 @@ SDNA_DEFAULT_DECL_STRUCT(TextureGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(ThickGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(TimeGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(TintGpencilModifierData); -SDNA_DEFAULT_DECL_STRUCT(WeightGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(WeightProxGpencilModifierData); +SDNA_DEFAULT_DECL_STRUCT(WeightAngleGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LineartGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(LengthGpencilModifierData); SDNA_DEFAULT_DECL_STRUCT(DashGpencilModifierData); @@ -548,7 +549,8 @@ const void *DNA_default_table[SDNA_TYPE_MAX] = { SDNA_DEFAULT_DECL(ThickGpencilModifierData), SDNA_DEFAULT_DECL(TimeGpencilModifierData), SDNA_DEFAULT_DECL(TintGpencilModifierData), - SDNA_DEFAULT_DECL(WeightGpencilModifierData), + SDNA_DEFAULT_DECL(WeightAngleGpencilModifierData), + SDNA_DEFAULT_DECL(WeightProxGpencilModifierData), SDNA_DEFAULT_DECL(LineartGpencilModifierData), SDNA_DEFAULT_DECL(LengthGpencilModifierData), SDNA_DEFAULT_DECL(DashGpencilModifierData), diff --git a/source/blender/makesdna/intern/dna_genfile.c b/source/blender/makesdna/intern/dna_genfile.c index 24d0d39292e..8aa2a07d071 100644 --- a/source/blender/makesdna/intern/dna_genfile.c +++ b/source/blender/makesdna/intern/dna_genfile.c @@ -525,7 +525,7 @@ static bool init_structDNA(SDNA *sdna, bool do_endian_swap, const char **r_error sdna->pointer_size = sdna->types_size[struct_info->type] / 2; - if (struct_info->members_len != 2 || (sdna->pointer_size != 4 && sdna->pointer_size != 8)) { + if (struct_info->members_len != 2 || (!ELEM(sdna->pointer_size, 4, 8))) { *r_error_message = "ListBase struct error! Needs it to calculate pointerize."; /* well, at least sizeof(ListBase) is error proof! (ton) */ return false; @@ -1546,9 +1546,9 @@ DNA_ReconstructInfo *DNA_reconstruct_info_create(const SDNA *oldsdna, reconstruct_info->oldsdna = oldsdna; reconstruct_info->newsdna = newsdna; reconstruct_info->compare_flags = compare_flags; - reconstruct_info->step_counts = MEM_malloc_arrayN(sizeof(int), newsdna->structs_len, __func__); + reconstruct_info->step_counts = MEM_malloc_arrayN(newsdna->structs_len, sizeof(int), __func__); reconstruct_info->steps = MEM_malloc_arrayN( - sizeof(ReconstructStep *), newsdna->structs_len, __func__); + newsdna->structs_len, sizeof(ReconstructStep *), __func__); /* Generate reconstruct steps for all structs. */ for (int new_struct_nr = 0; new_struct_nr < newsdna->structs_len; new_struct_nr++) { @@ -1800,7 +1800,7 @@ static void sdna_expand_names(SDNA *sdna) int names_expand_index = 0; for (int struct_nr = 0; struct_nr < sdna->structs_len; struct_nr++) { - /* We can't edit this memory 'sdna->structs' points to (readonly datatoc file). */ + /* We can't edit this memory 'sdna->structs' points to (read-only `datatoc` file). */ const SDNA_Struct *struct_old = sdna->structs[struct_nr]; const int array_size = sizeof(short) * 2 + sizeof(SDNA_StructMember) * struct_old->members_len; diff --git a/source/blender/makesdna/intern/dna_rename_defs.h b/source/blender/makesdna/intern/dna_rename_defs.h index 2feebbfd4f4..9b8bfb5af15 100644 --- a/source/blender/makesdna/intern/dna_rename_defs.h +++ b/source/blender/makesdna/intern/dna_rename_defs.h @@ -73,6 +73,10 @@ DNA_STRUCT_RENAME_ELEM(Curve, len_wchar, len_char32) DNA_STRUCT_RENAME_ELEM(Camera, clipend, clip_end) DNA_STRUCT_RENAME_ELEM(Camera, clipsta, clip_start) DNA_STRUCT_RENAME_ELEM(Collection, dupli_ofs, instance_offset) +DNA_STRUCT_RENAME_ELEM(Editing, over_border, overlay_frame_rect) +DNA_STRUCT_RENAME_ELEM(Editing, over_cfra, overlay_frame_abs) +DNA_STRUCT_RENAME_ELEM(Editing, over_flag, overlay_frame_flag) +DNA_STRUCT_RENAME_ELEM(Editing, over_ofs, overlay_frame_ofs) DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, cache_frame_pause_guiding, cache_frame_pause_guide) DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_alpha, guide_alpha) DNA_STRUCT_RENAME_ELEM(FluidDomainSettings, guiding_beta, guide_beta) @@ -92,6 +96,7 @@ DNA_STRUCT_RENAME_ELEM(Object, restrictflag, visibility_flag) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_group, instance_collection) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dup_ob, instance_object) DNA_STRUCT_RENAME_ELEM(ParticleSettings, dupliweights, instance_weights) +DNA_STRUCT_RENAME_ELEM(SpaceSeq, overlay_type, overlay_frame_type) DNA_STRUCT_RENAME_ELEM(Text, name, filepath) DNA_STRUCT_RENAME_ELEM(ThemeSpace, scrubbing_background, time_scrub_background) DNA_STRUCT_RENAME_ELEM(ThemeSpace, show_back_grad, background_type) diff --git a/source/blender/makesdna/intern/makesdna.c b/source/blender/makesdna/intern/makesdna.c index 0b7848b6662..114c0b40407 100644 --- a/source/blender/makesdna/intern/makesdna.c +++ b/source/blender/makesdna/intern/makesdna.c @@ -23,10 +23,8 @@ * \brief Struct muncher for making SDNA. * * \section aboutmakesdnac About makesdna tool - * Originally by Ton, some mods by Frank, and some cleaning and - * extension by Nzc. * - * Makesdna creates a .c file with a long string of numbers that + * `makesdna` creates a .c file with a long string of numbers that * encode the Blender file format. It is fast, because it is basically * a binary dump. There are some details to mind when reconstructing * the file (endianness and byte-alignment). @@ -36,9 +34,9 @@ * how much memory (on disk or in ram) is needed to store that struct, * and the offsets for reaching a particular one. * - * There is a facility to get verbose output from sdna. Search for - * \ref debugSDNA. This int can be set to 0 (no output) to some int. Higher - * numbers give more output. + * There is a facility to get verbose output from `sdna`. Search for + * \ref debugSDNA. This int can be set to 0 (no output) to some int. + * Higher numbers give more output. */ #define DNA_DEPRECATED_ALLOW @@ -1134,7 +1132,7 @@ static int calculate_struct_sizes(int firststruct, FILE *file_verify, const char * to the struct to resolve the problem. */ if ((size_64 % max_align_64 == 0) && (size_32 % max_align_32 == 4)) { fprintf(stderr, - "Sizeerror in 32 bit struct: %s (add paddding pointer)\n", + "Sizeerror in 32 bit struct: %s (add padding pointer)\n", types[structtype]); } else { diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index ce53e3390e1..188f933dba5 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -558,6 +558,7 @@ extern StructRNA RNA_ShaderFxWave; extern StructRNA RNA_ShaderNode; extern StructRNA RNA_ShaderNodeCameraData; extern StructRNA RNA_ShaderNodeCombineRGB; +extern StructRNA RNA_ShaderNodeFloatCurve; extern StructRNA RNA_ShaderNodeGamma; extern StructRNA RNA_ShaderNodeHueSaturation; extern StructRNA RNA_ShaderNodeInvert; diff --git a/source/blender/makesrna/RNA_enum_items.h b/source/blender/makesrna/RNA_enum_items.h index c8f44262020..fb18802483d 100644 --- a/source/blender/makesrna/RNA_enum_items.h +++ b/source/blender/makesrna/RNA_enum_items.h @@ -208,9 +208,13 @@ DEF_ENUM(rna_enum_preference_section_items) DEF_ENUM(rna_enum_attribute_type_items) DEF_ENUM(rna_enum_attribute_type_with_auto_items) DEF_ENUM(rna_enum_attribute_domain_items) +DEF_ENUM(rna_enum_attribute_domain_without_corner_items) DEF_ENUM(rna_enum_attribute_domain_with_auto_items) +DEF_ENUM(rna_enum_volume_grid_data_type_items) + DEF_ENUM(rna_enum_collection_color_items) +DEF_ENUM(rna_enum_strip_color_items) DEF_ENUM(rna_enum_subdivision_uv_smooth_items) DEF_ENUM(rna_enum_subdivision_boundary_smooth_items) diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index eb887e1881b..be612a1602b 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -131,7 +131,7 @@ static const EnumPropertyItem rna_enum_override_library_property_operation_items */ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { /* Datablocks */ - {FILTER_ID_AC, "filter_action", ICON_ANIM_DATA, "Actions", "Show Action data-blocks"}, + {FILTER_ID_AC, "filter_action", ICON_ACTION, "Actions", "Show Action data-blocks"}, {FILTER_ID_AR, "filter_armature", ICON_ARMATURE_DATA, @@ -173,7 +173,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { {FILTER_ID_MB, "filter_metaball", ICON_META_DATA, "Metaballs", "Show Metaball data-blocks"}, {FILTER_ID_MC, "filter_movie_clip", - ICON_TRACKER_DATA, + ICON_TRACKER, "Movie Clips", "Show Movie Clip data-blocks"}, {FILTER_ID_ME, "filter_mesh", ICON_MESH_DATA, "Meshes", "Show Mesh data-blocks"}, @@ -225,7 +225,6 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { # include "BLI_math_base.h" # include "BKE_anim_data.h" -# include "BKE_font.h" # include "BKE_global.h" /* XXX, remove me */ # include "BKE_idprop.h" # include "BKE_idtype.h" @@ -234,6 +233,7 @@ const struct IDFilterEnumPropertyItem rna_enum_id_type_filter_items[] = { # include "BKE_lib_remap.h" # include "BKE_library.h" # include "BKE_material.h" +# include "BKE_vfont.h" # include "DEG_depsgraph.h" # include "DEG_depsgraph_build.h" @@ -669,14 +669,22 @@ static ID *rna_ID_copy(ID *id, Main *bmain) return newid; } -static void rna_ID_asset_mark(ID *id, bContext *C) +static void rna_ID_asset_mark(ID *id) { - if (ED_asset_mark_id(C, id)) { + if (ED_asset_mark_id(id)) { WM_main_add_notifier(NC_ID | NA_EDITED, NULL); WM_main_add_notifier(NC_ASSET | NA_ADDED, NULL); } } +static void rna_ID_asset_generate_preview(ID *id, bContext *C) +{ + ED_asset_generate_preview(C, id); + + WM_main_add_notifier(NC_ID | NA_EDITED, NULL); + WM_main_add_notifier(NC_ASSET | NA_EDITED, NULL); +} + static void rna_ID_asset_clear(ID *id) { if (ED_asset_clear_id(id)) { @@ -1936,6 +1944,15 @@ static void rna_def_ID(BlenderRNA *brna) RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); RNA_def_property_ui_text(prop, "Library", "Library file the data-block is linked from"); + prop = RNA_def_pointer(srna, + "library_weak_reference", + "LibraryWeakReference", + "Library Weak Reference", + "Weak reference to a data-block in another library .blend file (used to " + "re-use already appended data instead of appending new copies)"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); + prop = RNA_def_property(srna, "asset_data", PROP_POINTER, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON); @@ -1977,13 +1994,17 @@ static void rna_def_ID(BlenderRNA *brna) func, "Enable easier reuse of the data-block through the Asset Browser, with the help of " "customizable metadata (like previews, descriptions and tags)"); - RNA_def_function_flag(func, FUNC_USE_CONTEXT); func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear"); RNA_def_function_ui_description( func, "Delete all asset metadata and turn the asset data-block back into a normal data-block"); + func = RNA_def_function(srna, "asset_generate_preview", "rna_ID_asset_generate_preview"); + RNA_def_function_ui_description( + func, "Generate preview image (might be scheduled in a background thread)"); + RNA_def_function_flag(func, FUNC_USE_CONTEXT); + func = RNA_def_function(srna, "override_create", "rna_ID_override_create"); RNA_def_function_ui_description(func, "Create an overridden local copy of this linked data-block (not " @@ -2143,6 +2164,31 @@ static void rna_def_library(BlenderRNA *brna) RNA_def_function_ui_description(func, "Reload this library and all its linked data-blocks"); } +static void rna_def_library_weak_reference(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "LibraryWeakReference", NULL); + RNA_def_struct_ui_text( + srna, + "LibraryWeakReference", + "Read-only external reference to a linked data-block and its library file"); + + prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "library_filepath"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "File Path", "Path to the library .blend file"); + + prop = RNA_def_property(srna, "id_name", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "library_id_name"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, + "ID name", + "Full ID name in the library .blend file (including the two leading 'id type' chars)"); +} + /** * \attention This is separate from the above. It allows for RNA functions to * return an IDProperty *. See MovieClip.metadata for a usage example. @@ -2175,6 +2221,7 @@ void RNA_def_ID(BlenderRNA *brna) rna_def_ID_properties(brna); rna_def_ID_materials(brna); rna_def_library(brna); + rna_def_library_weak_reference(brna); rna_def_idproperty_wrap_ptr(brna); } diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index fceb6d045c3..40b5f3ed1da 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -1445,8 +1445,9 @@ int RNA_property_int_clamp(PointerRNA *ptr, PropertyRNA *prop, int *value) return 0; } -/* this is the max length including \0 terminator. - * '0' used when their is no maximum */ +/** + * \return the maximum length including the \0 terminator. '0' is used when there is no maximum. + */ int RNA_property_string_maxlength(PropertyRNA *prop) { StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); @@ -2206,6 +2207,7 @@ void RNA_property_update(bContext *C, PointerRNA *ptr, PropertyRNA *prop) rna_property_update(C, CTX_data_main(C), CTX_data_scene(C), ptr, prop); } +/* NOTE: `scene` pointer may be NULL. */ void RNA_property_update_main(Main *bmain, Scene *scene, PointerRNA *ptr, PropertyRNA *prop) { rna_property_update(NULL, bmain, scene, ptr, prop); diff --git a/source/blender/makesrna/intern/rna_access_compare_override.c b/source/blender/makesrna/intern/rna_access_compare_override.c index f8a36c1b2e6..be8972dbff3 100644 --- a/source/blender/makesrna/intern/rna_access_compare_override.c +++ b/source/blender/makesrna/intern/rna_access_compare_override.c @@ -614,20 +614,25 @@ static bool rna_property_override_operation_apply(Main *bmain, } /* get and set the default values as appropriate for the various types */ - return override_apply(bmain, - ptr_dst, - ptr_src, - ptr_storage, - prop_dst, - prop_src, - prop_storage, - len_dst, - len_src, - len_storage, - ptr_item_dst, - ptr_item_src, - ptr_item_storage, - opop); + const bool sucess = override_apply(bmain, + ptr_dst, + ptr_src, + ptr_storage, + prop_dst, + prop_src, + prop_storage, + len_dst, + len_src, + len_storage, + ptr_item_dst, + ptr_item_src, + ptr_item_storage, + opop); + if (sucess) { + RNA_property_update_main(bmain, NULL, ptr_dst, prop_dst); + } + + return sucess; } /** diff --git a/source/blender/makesrna/intern/rna_asset.c b/source/blender/makesrna/intern/rna_asset.c index 1e583f4ca52..5d83da170b5 100644 --- a/source/blender/makesrna/intern/rna_asset.c +++ b/source/blender/makesrna/intern/rna_asset.c @@ -32,19 +32,67 @@ #ifdef RNA_RUNTIME # include "BKE_asset.h" +# include "BKE_asset_library.h" +# include "BKE_context.h" # include "BKE_idprop.h" # include "BLI_listbase.h" +# include "BLI_uuid.h" # include "ED_asset.h" +# include "ED_fileselect.h" # include "RNA_access.h" -static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data, - ReportList *reports, - const char *name, - bool skip_if_exists) +static bool rna_AssetMetaData_editable_from_owner_id(const ID *owner_id, + const AssetMetaData *asset_data, + const char **r_info) { + if (owner_id && asset_data && (owner_id->asset_data == asset_data)) { + return true; + } + + if (r_info) { + *r_info = + "Asset metadata from external asset libraries can't be edited, only assets stored in the " + "current file can"; + } + return false; +} + +int rna_AssetMetaData_editable(PointerRNA *ptr, const char **r_info) +{ + AssetMetaData *asset_data = ptr->data; + + return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, asset_data, r_info) ? + PROP_EDITABLE : + 0; +} + +static int rna_AssetTag_editable(PointerRNA *ptr, const char **r_info) +{ + AssetTag *asset_tag = ptr->data; + ID *owner_id = ptr->owner_id; + if (owner_id && owner_id->asset_data) { + BLI_assert_msg(BLI_findindex(&owner_id->asset_data->tags, asset_tag) != -1, + "The owner of the asset tag pointer is not the asset ID containing the tag"); + UNUSED_VARS_NDEBUG(asset_tag); + } + + return rna_AssetMetaData_editable_from_owner_id(ptr->owner_id, owner_id->asset_data, r_info) ? + PROP_EDITABLE : + 0; +} + +static AssetTag *rna_AssetMetaData_tag_new( + ID *id, AssetMetaData *asset_data, ReportList *reports, const char *name, bool skip_if_exists) +{ + const char *disabled_info = NULL; + if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) { + BKE_report(reports, RPT_WARNING, disabled_info); + return NULL; + } + AssetTag *tag = NULL; if (skip_if_exists) { @@ -64,10 +112,17 @@ static AssetTag *rna_AssetMetaData_tag_new(AssetMetaData *asset_data, return tag; } -static void rna_AssetMetaData_tag_remove(AssetMetaData *asset_data, +static void rna_AssetMetaData_tag_remove(ID *id, + AssetMetaData *asset_data, ReportList *reports, PointerRNA *tag_ptr) { + const char *disabled_info = NULL; + if (!rna_AssetMetaData_editable_from_owner_id(id, asset_data, &disabled_info)) { + BKE_report(reports, RPT_WARNING, disabled_info); + return; + } + AssetTag *tag = tag_ptr->data; if (BLI_findindex(&asset_data->tags, tag) == -1) { BKE_reportf(reports, RPT_ERROR, "Tag '%s' not found in given asset", tag->name); @@ -84,6 +139,40 @@ static IDProperty **rna_AssetMetaData_idprops(PointerRNA *ptr) return &asset_data->properties; } +static void rna_AssetMetaData_author_get(PointerRNA *ptr, char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->author) { + strcpy(value, asset_data->author); + } + else { + value[0] = '\0'; + } +} + +static int rna_AssetMetaData_author_length(PointerRNA *ptr) +{ + AssetMetaData *asset_data = ptr->data; + return asset_data->author ? strlen(asset_data->author) : 0; +} + +static void rna_AssetMetaData_author_set(PointerRNA *ptr, const char *value) +{ + AssetMetaData *asset_data = ptr->data; + + if (asset_data->author) { + MEM_freeN(asset_data->author); + } + + if (value[0]) { + asset_data->author = BLI_strdup(value); + } + else { + asset_data->author = NULL; + } +} + static void rna_AssetMetaData_description_get(PointerRNA *ptr, char *value) { AssetMetaData *asset_data = ptr->data; @@ -126,6 +215,59 @@ static void rna_AssetMetaData_active_tag_range( *max = *softmax = MAX2(asset_data->tot_tags - 1, 0); } +static void rna_AssetMetaData_catalog_id_get(PointerRNA *ptr, char *value) +{ + const AssetMetaData *asset_data = ptr->data; + BLI_uuid_format(value, asset_data->catalog_id); +} + +static int rna_AssetMetaData_catalog_id_length(PointerRNA *UNUSED(ptr)) +{ + return UUID_STRING_LEN - 1; +} + +static void rna_AssetMetaData_catalog_id_set(PointerRNA *ptr, const char *value) +{ + AssetMetaData *asset_data = ptr->data; + bUUID new_uuid; + + if (value[0] == '\0') { + BKE_asset_metadata_catalog_id_clear(asset_data); + return; + } + + if (!BLI_uuid_parse_string(&new_uuid, value)) { + // TODO(Sybren): raise ValueError exception once that's possible from an RNA setter. + printf("UUID %s not formatted correctly, ignoring new value\n", value); + return; + } + + /* This just sets the new UUID and clears the catalog simple name. The actual + * catalog simple name will be updated by some update function, as it + * needs the asset library from the context. */ + /* TODO(Sybren): write that update function. */ + BKE_asset_metadata_catalog_id_set(asset_data, new_uuid, ""); +} + +void rna_AssetMetaData_catalog_id_update(struct bContext *C, struct PointerRNA *ptr) +{ + SpaceFile *sfile = CTX_wm_space_file(C); + if (sfile == NULL) { + /* Until there is a proper Asset Service available, it's only possible to get the asset library + * from within the asset browser context. */ + return; + } + + AssetLibrary *asset_library = ED_fileselect_active_asset_library_get(sfile); + if (asset_library == NULL) { + /* The SpaceFile may not be an asset browser but a regular file browser. */ + return; + } + + AssetMetaData *asset_data = ptr->data; + BKE_asset_library_refresh_catalog_simplename(asset_library, asset_data); +} + static PointerRNA rna_AssetHandle_file_data_get(PointerRNA *ptr) { AssetHandle *asset_handle = ptr->data; @@ -185,6 +327,7 @@ static void rna_def_asset_tag(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Asset Tag", "User defined tag (name token)"); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_AssetTag_editable"); RNA_def_property_string_maxlength(prop, MAX_NAME); RNA_def_property_ui_text(prop, "Name", "The identifier that makes up this tag"); RNA_def_struct_name_property(srna, prop); @@ -205,7 +348,7 @@ static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop) /* Tag collection */ func = RNA_def_function(srna, "new", "rna_AssetMetaData_tag_new"); RNA_def_function_ui_description(func, "Add a new tag to this asset"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS); parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); parm = RNA_def_boolean(func, @@ -219,7 +362,7 @@ static void rna_def_asset_tags_api(BlenderRNA *brna, PropertyRNA *cprop) func = RNA_def_function(srna, "remove", "rna_AssetMetaData_tag_remove"); RNA_def_function_ui_description(func, "Remove an existing tag from this asset"); - RNA_def_function_flag(func, FUNC_USE_REPORTS); + RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_REPORTS); /* tag to remove */ parm = RNA_def_pointer(func, "tag", "AssetTag", "", "Removed tag"); RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR); @@ -238,7 +381,16 @@ static void rna_def_asset_data(BlenderRNA *brna) RNA_def_struct_idprops_func(srna, "rna_AssetMetaData_idprops"); RNA_def_struct_flag(srna, STRUCT_NO_DATABLOCK_IDPROPERTIES); /* Mandatory! */ + prop = RNA_def_property(srna, "author", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); + RNA_def_property_string_funcs(prop, + "rna_AssetMetaData_author_get", + "rna_AssetMetaData_author_length", + "rna_AssetMetaData_author_set"); + RNA_def_property_ui_text(prop, "Author", "Name of the creator of the asset"); + prop = RNA_def_property(srna, "description", PROP_STRING, PROP_NONE); + RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_string_funcs(prop, "rna_AssetMetaData_description_get", "rna_AssetMetaData_description_length", @@ -248,7 +400,7 @@ static void rna_def_asset_data(BlenderRNA *brna) prop = RNA_def_property(srna, "tags", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "AssetTag"); - RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_editable_func(prop, "rna_AssetMetaData_editable"); RNA_def_property_ui_text(prop, "Tags", "Custom tags (name tokens) for the asset, used for filtering and " @@ -258,6 +410,25 @@ static void rna_def_asset_data(BlenderRNA *brna) prop = RNA_def_property(srna, "active_tag", PROP_INT, PROP_NONE); RNA_def_property_int_funcs(prop, NULL, NULL, "rna_AssetMetaData_active_tag_range"); RNA_def_property_ui_text(prop, "Active Tag", "Index of the tag set for editing"); + + prop = RNA_def_property(srna, "catalog_id", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_AssetMetaData_catalog_id_get", + "rna_AssetMetaData_catalog_id_length", + "rna_AssetMetaData_catalog_id_set"); + RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE); + RNA_def_property_update(prop, 0, "rna_AssetMetaData_catalog_id_update"); + RNA_def_property_ui_text(prop, + "Catalog UUID", + "Identifier for the asset's catalog, used by Blender to look up the " + "asset's catalog path. Must be a UUID according to RFC4122"); + + prop = RNA_def_property(srna, "catalog_simple_name", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, + "Catalog Simple Name", + "Simple name of the asset's catalog, for debugging and " + "data recovery purposes"); } static void rna_def_asset_handle_api(StructRNA *srna) diff --git a/source/blender/makesrna/intern/rna_attribute.c b/source/blender/makesrna/intern/rna_attribute.c index 49e813e6a6c..dbf20896463 100644 --- a/source/blender/makesrna/intern/rna_attribute.c +++ b/source/blender/makesrna/intern/rna_attribute.c @@ -75,6 +75,14 @@ const EnumPropertyItem rna_enum_attribute_domain_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_attribute_domain_without_corner_items[] = { + {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, + {ATTR_DOMAIN_EDGE, "EDGE", 0, "Edge", "Attribute on mesh edge"}, + {ATTR_DOMAIN_FACE, "FACE", 0, "Face", "Attribute on mesh faces"}, + {ATTR_DOMAIN_CURVE, "CURVE", 0, "Spline", "Attribute on spline"}, + {0, NULL, 0, NULL, NULL}, +}; + const EnumPropertyItem rna_enum_attribute_domain_with_auto_items[] = { {ATTR_DOMAIN_AUTO, "AUTO", 0, "Auto", ""}, {ATTR_DOMAIN_POINT, "POINT", 0, "Point", "Attribute on point"}, @@ -315,8 +323,10 @@ static void rna_AttributeGroup_next_domain(ID *id, int(skip)(CollectionPropertyIterator *iter, void *data)) { do { - CustomDataLayer *prev_layers = (CustomDataLayer *)iter->internal.array.endptr - - iter->internal.array.length; + CustomDataLayer *prev_layers = (iter->internal.array.endptr == NULL) ? + NULL : + (CustomDataLayer *)iter->internal.array.endptr - + iter->internal.array.length; CustomData *customdata = BKE_id_attributes_iterator_next_domain(id, prev_layers); if (customdata == NULL) { return; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 25caa411979..1d3b8cd9f9c 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -734,6 +734,12 @@ static void rna_Brush_icon_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Poi WM_main_add_notifier(NC_BRUSH | NA_EDITED, br); } +static bool rna_Brush_imagetype_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + Image *image = (Image *)value.owner_id; + return image->type != IMA_TYPE_R_RESULT && image->type != IMA_TYPE_COMPOSITE; +} + static void rna_TextureSlot_brush_angle_update(bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); @@ -3434,6 +3440,7 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Clone Image", "Image for clone tool"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_Brush_update"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_Brush_imagetype_poll"); prop = RNA_def_property(srna, "clone_alpha", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "clone.alpha"); diff --git a/source/blender/makesrna/intern/rna_curve.c b/source/blender/makesrna/intern/rna_curve.c index 0bfb1200f49..8c89162f571 100644 --- a/source/blender/makesrna/intern/rna_curve.c +++ b/source/blender/makesrna/intern/rna_curve.c @@ -30,7 +30,7 @@ #include "BLT_translation.h" -#include "BKE_font.h" +#include "BKE_vfont.h" #include "RNA_access.h" #include "RNA_define.h" diff --git a/source/blender/makesrna/intern/rna_gpencil.c b/source/blender/makesrna/intern/rna_gpencil.c index f06c8a5325c..3f380cd1830 100644 --- a/source/blender/makesrna/intern/rna_gpencil.c +++ b/source/blender/makesrna/intern/rna_gpencil.c @@ -843,17 +843,17 @@ static float rna_GPencilStrokePoints_weight_get(bGPDstroke *stroke, return -1.0f; } - if (dvert->totweight <= vertex_group_index || vertex_group_index < 0) { - BKE_report(reports, RPT_ERROR, "Groups: index out of range"); - return -1.0f; - } - if (stroke->totpoints <= point_index || point_index < 0) { BKE_report(reports, RPT_ERROR, "GPencilStrokePoints: index out of range"); return -1.0f; } MDeformVert *pt_dvert = stroke->dvert + point_index; + if ((pt_dvert) && (pt_dvert->totweight <= vertex_group_index || vertex_group_index < 0)) { + BKE_report(reports, RPT_ERROR, "Groups: index out of range"); + return -1.0f; + } + MDeformWeight *dw = BKE_defvert_find_index(pt_dvert, vertex_group_index); if (dw) { return dw->weight; @@ -906,7 +906,8 @@ static void rna_GPencil_stroke_remove(ID *id, return; } - BLI_freelinkN(&frame->strokes, stroke); + BLI_remlink(&frame->strokes, stroke); + BKE_gpencil_free_stroke(stroke); RNA_POINTER_INVALIDATE(stroke_ptr); DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/makesrna/intern/rna_gpencil_modifier.c b/source/blender/makesrna/intern/rna_gpencil_modifier.c index 4fa33424994..c4efe5a0ea1 100644 --- a/source/blender/makesrna/intern/rna_gpencil_modifier.c +++ b/source/blender/makesrna/intern/rna_gpencil_modifier.c @@ -26,6 +26,7 @@ #include "DNA_brush_types.h" #include "DNA_cachefile_types.h" #include "DNA_gpencil_modifier_types.h" +#include "DNA_gpencil_types.h" #include "DNA_mesh_types.h" #include "DNA_object_force_types.h" #include "DNA_object_types.h" @@ -58,6 +59,23 @@ #include "WM_types.h" const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { + {0, "", 0, N_("Modify"), ""}, + {eGpencilModifierType_Texture, + "GP_TEXTURE", + ICON_MOD_UVPROJECT, + "Texture Mapping", + "Change stroke uv texture values"}, + {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"}, + {eGpencilModifierType_WeightAngle, + "GP_WEIGHT_ANGLE", + ICON_MOD_VERTEX_WEIGHT, + "Vertex Weight Angle", + "Generate Vertex Weights base on stroke angle"}, + {eGpencilModifierType_WeightProximity, + "GP_WEIGHT_PROXIMITY", + ICON_MOD_VERTEX_WEIGHT, + "Vertex Weight Proximity", + "Generate Vertex Weights base on distance to object"}, {0, "", 0, N_("Generate"), ""}, {eGpencilModifierType_Array, "GP_ARRAY", @@ -74,6 +92,11 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_DASH, "Dot Dash", "Generate dot-dash styled strokes"}, + {eGpencilModifierType_Length, + "GP_LENGTH", + ICON_MOD_LENGTH, + "Length", + "Extend or shrink strokes"}, {eGpencilModifierType_Lineart, "GP_LINEART", ICON_MOD_LINEART, @@ -99,11 +122,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_SUBSURF, "Subdivide", "Subdivide stroke adding more control points"}, - {eGpencilModifierType_Weight, - "GP_WEIGHT", - ICON_MOD_VERTEX_WEIGHT, - "Vertex Weight", - "Generate Vertex Weights"}, {0, "", 0, N_("Deform"), ""}, {eGpencilModifierType_Armature, "GP_ARMATURE", @@ -120,11 +138,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_LATTICE, "Lattice", "Deform strokes using lattice"}, - {eGpencilModifierType_Length, - "GP_LENGTH", - ICON_MOD_LENGTH, - "Length", - "Extend or shrink strokes"}, {eGpencilModifierType_Noise, "GP_NOISE", ICON_MOD_NOISE, "Noise", "Add noise to strokes"}, {eGpencilModifierType_Offset, "GP_OFFSET", @@ -137,7 +150,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_THICKNESS, "Thickness", "Change stroke thickness"}, - {eGpencilModifierType_Time, "GP_TIME", ICON_MOD_TIME, "Time Offset", "Offset keyframes"}, {0, "", 0, N_("Color"), ""}, {eGpencilModifierType_Color, "GP_COLOR", @@ -149,11 +161,6 @@ const EnumPropertyItem rna_enum_object_greasepencil_modifier_type_items[] = { ICON_MOD_OPACITY, "Opacity", "Opacity of the strokes"}, - {eGpencilModifierType_Texture, - "GP_TEXTURE", - ICON_TEXTURE, - "Texture Mapping", - "Change stroke uv texture values"}, {eGpencilModifierType_Tint, "GP_TINT", ICON_MOD_TINT, "Tint", "Tint strokes with new color"}, {0, NULL, 0, NULL, NULL}, }; @@ -244,8 +251,10 @@ static StructRNA *rna_GpencilModifier_refine(struct PointerRNA *ptr) return &RNA_TintGpencilModifier; case eGpencilModifierType_Time: return &RNA_TimeGpencilModifier; - case eGpencilModifierType_Weight: - return &RNA_WeightGpencilModifier; + case eGpencilModifierType_WeightProximity: + return &RNA_WeightProxGpencilModifier; + case eGpencilModifierType_WeightAngle: + return &RNA_WeightAngleGpencilModifier; case eGpencilModifierType_Color: return &RNA_ColorGpencilModifier; case eGpencilModifierType_Array: @@ -346,8 +355,10 @@ RNA_GP_MOD_VGROUP_NAME_SET(Offset, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Armature, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Texture, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Tint, vgname); -RNA_GP_MOD_VGROUP_NAME_SET(Weight, target_vgname); -RNA_GP_MOD_VGROUP_NAME_SET(Weight, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightProx, target_vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightProx, vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, target_vgname); +RNA_GP_MOD_VGROUP_NAME_SET(WeightAngle, vgname); RNA_GP_MOD_VGROUP_NAME_SET(Lineart, vgname); # undef RNA_GP_MOD_VGROUP_NAME_SET @@ -380,7 +391,7 @@ static void greasepencil_modifier_object_set(Object *self, RNA_GP_MOD_OBJECT_SET(Armature, object, OB_ARMATURE); RNA_GP_MOD_OBJECT_SET(Lattice, object, OB_LATTICE); RNA_GP_MOD_OBJECT_SET(Mirror, object, OB_EMPTY); -RNA_GP_MOD_OBJECT_SET(Weight, object, OB_EMPTY); +RNA_GP_MOD_OBJECT_SET(WeightProx, object, OB_EMPTY); # undef RNA_GP_MOD_OBJECT_SET @@ -554,11 +565,21 @@ static void rna_ThickGpencilModifier_material_set(PointerRNA *ptr, rna_GpencilModifier_material_set(ptr, value, ma_target, reports); } -static void rna_WeightGpencilModifier_material_set(PointerRNA *ptr, - PointerRNA value, - struct ReportList *reports) +static void rna_WeightProxGpencilModifier_material_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *reports) { - WeightGpencilModifierData *tmd = (WeightGpencilModifierData *)ptr->data; + WeightProxGpencilModifierData *tmd = (WeightProxGpencilModifierData *)ptr->data; + Material **ma_target = &tmd->material; + + rna_GpencilModifier_material_set(ptr, value, ma_target, reports); +} + +static void rna_WeightAngleGpencilModifier_material_set(PointerRNA *ptr, + PointerRNA value, + struct ReportList *reports) +{ + WeightAngleGpencilModifierData *tmd = (WeightAngleGpencilModifierData *)ptr->data; Material **ma_target = &tmd->material; rna_GpencilModifier_material_set(ptr, value, ma_target, reports); @@ -1288,7 +1309,7 @@ static void rna_def_modifier_gpencilthick(BlenderRNA *brna) prop, "Custom Curve", "Use a custom curve to define thickness change along the strokes"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "normalize_thickness", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_normalized_thickness", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_THICK_NORMALIZE); RNA_def_property_ui_text(prop, "Uniform Thickness", "Replace the stroke thickness"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); @@ -1844,7 +1865,7 @@ static void rna_def_modifier_gpencilopacity(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "normalize_opacity", PROP_BOOLEAN, PROP_NONE); + prop = RNA_def_property(srna, "use_normalized_opacity", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_OPACITY_NORMALIZE); RNA_def_property_ui_text(prop, "Uniform Opacity", "Replace the stroke opacity"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_opacity_update"); @@ -2665,7 +2686,7 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna) RNA_def_struct_ui_text( srna, "Texture Modifier", "Transform stroke texture coordinates Modifier"); RNA_def_struct_sdna(srna, "TextureGpencilModifierData"); - RNA_def_struct_ui_icon(srna, ICON_TEXTURE); + RNA_def_struct_ui_icon(srna, ICON_MOD_UVPROJECT); RNA_define_lib_overridable(true); @@ -2783,24 +2804,131 @@ static void rna_def_modifier_gpenciltexture(BlenderRNA *brna) RNA_define_lib_overridable(false); } -static void rna_def_modifier_gpencilweight(BlenderRNA *brna) +static void rna_def_modifier_gpencilweight_proximity(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "WeightProxGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Weight Modifier Proximity", "Calculate Vertex Weight dynamically"); + RNA_def_struct_sdna(srna, "WeightProxGpencilModifierData"); + RNA_def_struct_ui_icon(srna, ICON_MOD_VERTEX_WEIGHT); + + RNA_define_lib_overridable(true); + + prop = RNA_def_property(srna, "target_vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "target_vgname"); + RNA_def_property_ui_text(prop, "Vertex Group", "Output Vertex group"); + RNA_def_property_string_funcs( + prop, NULL, NULL, "rna_WeightProxGpencilModifier_target_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_multiply", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_MULTIPLY_DATA); + RNA_def_property_ui_text( + prop, + "Multiply Weights", + "Multiply the calculated weights with the existing values in the vertex group"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_invert_output", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_OUTPUT); + RNA_def_property_ui_text(prop, "Invert", "Invert output weight values"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "layername"); + RNA_def_property_ui_text(prop, "Layer", "Layer name"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "material", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, + NULL, + "rna_WeightProxGpencilModifier_material_set", + NULL, + "rna_GpencilModifier_material_poll"); + RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "vgname"); + RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightProxGpencilModifier_vgname_set"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + /* Distance reference object */ + prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); + RNA_def_property_ui_text(prop, "Target Object", "Object used as distance reference"); + RNA_def_property_pointer_funcs( + prop, NULL, "rna_WeightProxGpencilModifier_object_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + + prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "dist_start"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Lowest", "Distance mapping to 0.0 weight"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "min_weight"); + RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "dist_end"); + RNA_def_property_range(prop, 0.0, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0, 1000.0, 1.0, 2); + RNA_def_property_ui_text(prop, "Highest", "Distance mapping to 1.0 weight"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "pass_index"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_LAYER); + RNA_def_property_ui_text(prop, "Inverse Layers", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_materials", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_MATERIAL); + RNA_def_property_ui_text(prop, "Inverse Materials", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_material_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_PASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_vertex", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_VGROUP); + RNA_def_property_ui_text(prop, "Inverse VertexGroup", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "layer_pass", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "layer_pass"); + RNA_def_property_range(prop, 0, 100); + RNA_def_property_ui_text(prop, "Pass", "Layer pass index"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_layer_pass", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_LAYERPASS); + RNA_def_property_ui_text(prop, "Inverse Pass", "Inverse filter"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + RNA_define_lib_overridable(false); +} + +static void rna_def_modifier_gpencilweight_angle(BlenderRNA *brna) { StructRNA *srna; PropertyRNA *prop; - static const EnumPropertyItem mode_items[] = { - {GP_WEIGHT_MODE_DISTANCE, - "DISTANCE", - 0, - "Distance", - "Calculate weights depending on the distance to the target object"}, - {GP_WEIGHT_MODE_ANGLE, - "ANGLE", - 0, - "Angle", - "Calculate weights depending on the stroke orientation"}, - {0, NULL, 0, NULL, NULL}, - }; static const EnumPropertyItem axis_items[] = { {0, "X", 0, "X", ""}, {1, "Y", 0, "Y", ""}, @@ -2814,34 +2942,31 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - srna = RNA_def_struct(brna, "WeightGpencilModifier", "GpencilModifier"); - RNA_def_struct_ui_text(srna, "Weight Modifier", "Calculate Vertex Weight dynamically"); - RNA_def_struct_sdna(srna, "WeightGpencilModifierData"); + srna = RNA_def_struct(brna, "WeightAngleGpencilModifier", "GpencilModifier"); + RNA_def_struct_ui_text(srna, "Weight Modifier Angle", "Calculate Vertex Weight dynamically"); + RNA_def_struct_sdna(srna, "WeightAngleGpencilModifierData"); RNA_def_struct_ui_icon(srna, ICON_MOD_VERTEX_WEIGHT); RNA_define_lib_overridable(true); - prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "mode"); - RNA_def_property_enum_items(prop, mode_items); - RNA_def_property_ui_text(prop, "Mode", ""); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "target_vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "target_vgname"); - RNA_def_property_ui_text(prop, "Output", "Output Vertex group"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightGpencilModifier_target_vgname_set"); + RNA_def_property_ui_text(prop, "Vertex Group", "Output Vertex group"); + RNA_def_property_string_funcs( + prop, NULL, NULL, "rna_WeightAngleGpencilModifier_target_vgname_set"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "use_blend", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_BLEND_DATA); + prop = RNA_def_property(srna, "use_multiply", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_MULTIPLY_DATA); RNA_def_property_ui_text( - prop, "Blend", "Blend results with existing weights in output weight group"); + prop, + "Multiply Weights", + "Multiply the calculated weights with the existing values in the vertex group"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "use_invert_output", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_WEIGHT_INVERT_OUTPUT); - RNA_def_property_ui_text(prop, "Invert", "Invert weight values"); + RNA_def_property_ui_text(prop, "Invert", "Invert output weight values"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "angle", PROP_FLOAT, PROP_ANGLE); @@ -2871,7 +2996,7 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_pointer_funcs(prop, NULL, - "rna_WeightGpencilModifier_material_set", + "rna_WeightAngleGpencilModifier_material_set", NULL, "rna_GpencilModifier_material_poll"); RNA_def_property_ui_text(prop, "Material", "Material used for filtering effect"); @@ -2880,20 +3005,7 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) prop = RNA_def_property(srna, "vertex_group", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "vgname"); RNA_def_property_ui_text(prop, "Vertex Group", "Vertex group name for modulating the deform"); - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightGpencilModifier_vgname_set"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - - /* Distance reference object */ - prop = RNA_def_property(srna, "object", PROP_POINTER, PROP_NONE); - RNA_def_property_ui_text(prop, "Object", "Object used as distance reference"); - RNA_def_property_pointer_funcs(prop, NULL, "rna_WeightGpencilModifier_object_set", NULL, NULL); - RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); - - prop = RNA_def_property(srna, "distance_start", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "dist_start"); - RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); - RNA_def_property_ui_text(prop, "Distance Start", "Start value for distance calculation"); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_WeightAngleGpencilModifier_vgname_set"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "minimum_weight", PROP_FLOAT, PROP_FACTOR); @@ -2901,12 +3013,6 @@ static void rna_def_modifier_gpencilweight(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Minimum", "Minimum value for vertex weight"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "distance_end", PROP_FLOAT, PROP_NONE); - RNA_def_property_float_sdna(prop, NULL, "dist_end"); - RNA_def_property_ui_range(prop, 0, 1000.0, 1.0, 2); - RNA_def_property_ui_text(prop, "Distance End", "End value for distance calculation"); - RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); - prop = RNA_def_property(srna, "pass_index", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "pass_index"); RNA_def_property_range(prop, 0, 100); @@ -2967,6 +3073,12 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_define_lib_overridable(true); + prop = RNA_def_property(srna, "use_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_CUSTOM_CAMERA); + RNA_def_property_ui_text( + prop, "Use Custom Camera", "Use custom camera instead of the active camera"); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_fuzzy_intersections", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_INTERSECTION_AS_CONTOUR); RNA_def_property_ui_text(prop, @@ -3020,6 +3132,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "smooth_tolerance", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "chain_smooth_tolerance"); + RNA_def_property_ui_text( + prop, "Smooth Tolerance", "Strength of smoothing applied on jagged chains"); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.05f, 4); + RNA_def_property_range(prop, 0.0f, 30.0f); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_remove_doubles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_REMOVE_DOUBLES); RNA_def_property_ui_text( @@ -3086,6 +3206,30 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) "separate stroke for each overlapping type"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "stroke_depth_offset", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_ui_text(prop, + "Stroke Depth Offset", + "Move strokes slightly towards the camera to avoid clipping while " + "preserve depth for the viewport"); + RNA_def_property_ui_range(prop, 0.0, 0.5, 0.001, 4); + RNA_def_property_range(prop, -0.1, FLT_MAX); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "use_offset_towards_custom_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flags", LRT_GPENCIL_OFFSET_TOWARDS_CUSTOM_CAMERA); + RNA_def_property_ui_text(prop, + "Offset Towards Custom Camera", + "Offset strokes towards selected camera instead of the active camera"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "source_camera", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_SELF_CHECK); + RNA_def_property_struct_type(prop, "Object"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text( + prop, "Camera Object", "Use specified camera object for generating line art"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_dependency_update"); + prop = RNA_def_property(srna, "source_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, modifier_lineart_source_type); RNA_def_property_ui_text(prop, "Source Type", "Line art stroke source type"); @@ -3261,6 +3405,14 @@ static void rna_def_modifier_gpencillineart(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Crease On Sharp Edges", "Allow crease to show on sharp edges"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_image_boundary_trimming", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "calculation_flags", LRT_USE_IMAGE_BOUNDARY_TRIMMING); + RNA_def_property_ui_text( + prop, + "Image Boundary Trimming", + "Trim all edges right at the boundary of image(including overscan region)"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + RNA_define_lib_overridable(false); } @@ -3278,14 +3430,29 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) prop = RNA_def_property(srna, "start_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "start_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); - RNA_def_property_ui_text(prop, "Start Factor", "Length difference for each segment"); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_text( + prop, "Start Factor", "Added length to the start of each stroke relative to its length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "end_factor", PROP_FLOAT, PROP_NONE); RNA_def_property_float_sdna(prop, NULL, "end_fac"); - RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 1); - RNA_def_property_ui_text(prop, "End Factor", "Length difference for each segment"); + RNA_def_property_ui_range(prop, -10.0f, 10.0f, 0.1, 2); + RNA_def_property_ui_text( + prop, "End Factor", "Added length to the end of each stroke relative to its length"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "start_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "start_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_text( + prop, "Start Factor", "Absolute added length to the start of each stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "end_length", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "end_fac"); + RNA_def_property_ui_range(prop, -100.0f, 100.0f, 0.1f, 3); + RNA_def_property_ui_text(prop, "End Factor", "Absolute added length to the end of each stroke"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "overshoot_factor", PROP_FLOAT, PROP_FACTOR); @@ -3293,8 +3460,8 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_range(prop, 0.0f, 1.0f); RNA_def_property_ui_text( prop, - "Overshoot Factor", - "Defines how precise must follow the stroke trajectory for the overshoot extremes"); + "Used Length", + "Defines what portion of the stroke is used for the calculation of the extension"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); @@ -3303,6 +3470,44 @@ static void rna_def_modifier_gpencillength(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Mode", "Mode to define length"); RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "use_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_USE_CURVATURE); + RNA_def_property_ui_text(prop, "Use Curvature", "Follow the curvature of the stroke"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "invert_curvature", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", GP_LENGTH_INVERT_CURVATURE); + RNA_def_property_ui_text( + prop, "Invert Curvature", "Invert the curvature of the stroke's extension"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "point_density", PROP_FLOAT, PROP_NONE); + RNA_def_property_range(prop, 0.1f, 1000.0f); + RNA_def_property_ui_range(prop, 0.1f, 1000.0f, 1.0f, 1); + RNA_def_property_ui_scale_type(prop, PROP_SCALE_CUBIC); + RNA_def_property_ui_text( + prop, "Point Density", "Multiplied by Start/End for the total added point count"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "segment_influence", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, -2.0f, 3.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1f, 2); + RNA_def_property_ui_text(prop, + "Segment Influence", + "Factor to determine how much the length of the individual segments " + "should influence the final computed curvature. Higher factors makes " + "small segments influence the overall curvature less"); + RNA_def_property_update(prop, 0, "rna_GpencilModifier_update"); + + prop = RNA_def_property(srna, "max_angle", PROP_FLOAT, PROP_ANGLE); + RNA_def_property_ui_text(prop, + "Filter Angle", + "Ignore points on the stroke that deviate from their neighbors by more " + "than this angle when determining the extrapolation shape"); + RNA_def_property_range(prop, 0.0f, DEG2RAD(180.0f)); + RNA_def_property_ui_range(prop, 0.0f, DEG2RAD(179.5f), 10.0f, 1); + RNA_def_property_update(prop, NC_SCENE, "rna_GpencilModifier_update"); + prop = RNA_def_property(srna, "layer", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "layername"); RNA_def_property_ui_text(prop, "Layer", "Layer name"); @@ -3422,7 +3627,7 @@ static void rna_def_modifier_gpencildash(BlenderRNA *brna) prop = RNA_def_property(srna, "segment_active_index", PROP_INT, PROP_UNSIGNED); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text(prop, "Active Dash Segement Index", "Active index in the segment list"); + RNA_def_property_ui_text(prop, "Active Dash Segment Index", "Active index in the segment list"); prop = RNA_def_property(srna, "dash_offset", PROP_INT, PROP_NONE); RNA_def_property_ui_text( @@ -3552,7 +3757,8 @@ void RNA_def_greasepencil_modifier(BlenderRNA *brna) rna_def_modifier_gpencilarmature(brna); rna_def_modifier_gpencilmultiply(brna); rna_def_modifier_gpenciltexture(brna); - rna_def_modifier_gpencilweight(brna); + rna_def_modifier_gpencilweight_angle(brna); + rna_def_modifier_gpencilweight_proximity(brna); rna_def_modifier_gpencillineart(brna); rna_def_modifier_gpencillength(brna); rna_def_modifier_gpencildash(brna); diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index 4a013dc9bd7..2f42e521b52 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -161,7 +161,9 @@ static void rna_ImageUser_update(Main *bmain, Scene *scene, PointerRNA *ptr) ImageUser *iuser = ptr->data; ID *id = ptr->owner_id; - BKE_image_user_frame_calc(NULL, iuser, scene->r.cfra); + if (scene != NULL) { + BKE_image_user_frame_calc(NULL, iuser, scene->r.cfra); + } if (id) { if (GS(id->name) == ID_NT) { diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 06ff1918040..4b52ceb6241 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -75,7 +75,6 @@ static void rna_Image_save_render( void *lock; iuser.scene = scene; - iuser.ok = 1; ibuf = BKE_image_acquire_ibuf(image, &iuser, &lock); diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h index 0bb76fd933a..fd6664ff0de 100644 --- a/source/blender/makesrna/intern/rna_internal.h +++ b/source/blender/makesrna/intern/rna_internal.h @@ -267,6 +267,7 @@ void rna_def_mtex_common(struct BlenderRNA *brna, void rna_def_texpaint_slots(struct BlenderRNA *brna, struct StructRNA *srna); void rna_def_view_layer_common(struct BlenderRNA *brna, struct StructRNA *srna, const bool scene); +int rna_AssetMetaData_editable(struct PointerRNA *ptr, const char **r_info); PropertyRNA *rna_def_asset_library_reference_common(struct StructRNA *srna, const char *get, const char *set); diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 22a9b9d930a..29df7a53c44 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -44,11 +44,20 @@ typedef struct IDProperty IDProperty; /* Function Callbacks */ -typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr); +/** Update callback for an RNA property. + * + * \note This is NOT called automatically when writing into the property, it needs to be called + * manually (through #RNA_property_update or #RNA_property_update_main) when needed. + * + * \param bmain: the Main data-base to which `ptr` data belongs. + * \param active_scene: The current active scene (may be NULL in some cases). + * \param ptr: The RNA pointer data to update. */ +typedef void (*UpdateFunc)(struct Main *bmain, struct Scene *active_scene, struct PointerRNA *ptr); typedef void (*ContextPropUpdateFunc)(struct bContext *C, struct PointerRNA *ptr, struct PropertyRNA *prop); typedef void (*ContextUpdateFunc)(struct bContext *C, struct PointerRNA *ptr); + typedef int (*EditableFunc)(struct PointerRNA *ptr, const char **r_info); typedef int (*ItemEditableFunc)(struct PointerRNA *ptr, int index); typedef struct IDProperty **(*IDPropertiesFunc)(struct PointerRNA *ptr); diff --git a/source/blender/makesrna/intern/rna_key.c b/source/blender/makesrna/intern/rna_key.c index 9d8ed161738..c7a1c89af63 100644 --- a/source/blender/makesrna/intern/rna_key.c +++ b/source/blender/makesrna/intern/rna_key.c @@ -541,7 +541,7 @@ static void rna_ShapeKey_data_begin_mixed(CollectionPropertyIterator *iter, int point_count = rna_ShapeKey_curve_find_index(key, kb->totelem); ShapeKeyCurvePoint *points = MEM_malloc_arrayN( - sizeof(ShapeKeyCurvePoint), point_count, __func__); + point_count, sizeof(ShapeKeyCurvePoint), __func__); char *databuf = kb->data; int items_left = point_count; diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 9a33849b645..0276c8a3f8a 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -47,7 +47,6 @@ # include "BKE_collection.h" # include "BKE_curve.h" # include "BKE_displist.h" -# include "BKE_font.h" # include "BKE_gpencil.h" # include "BKE_hair.h" # include "BKE_icons.h" @@ -74,6 +73,7 @@ # include "BKE_speaker.h" # include "BKE_text.h" # include "BKE_texture.h" +# include "BKE_vfont.h" # include "BKE_volume.h" # include "BKE_workspace.h" # include "BKE_world.h" diff --git a/source/blender/makesrna/intern/rna_material.c b/source/blender/makesrna/intern/rna_material.c index 8d0d3adab8b..22a75c0d992 100644 --- a/source/blender/makesrna/intern/rna_material.c +++ b/source/blender/makesrna/intern/rna_material.c @@ -604,8 +604,10 @@ static void rna_def_material_greasepencil(BlenderRNA *brna) RNA_def_property_float_default(prop, 0.0f); RNA_def_property_range(prop, -DEG2RADF(90.0f), DEG2RADF(90.0f)); RNA_def_property_ui_range(prop, -DEG2RADF(90.0f), DEG2RADF(90.0f), 10, 3); - RNA_def_property_ui_text( - prop, "Rotation", "Additional rotation applied to dots and square strokes"); + RNA_def_property_ui_text(prop, + "Rotation", + "Additional rotation applied to dots and square texture of strokes. " + "Only applies in texture shading mode"); RNA_def_property_update(prop, NC_GPENCIL | ND_SHADING, "rna_MaterialGpencil_update"); /* pass index for future compositing and editing tools */ diff --git a/source/blender/makesrna/intern/rna_material_api.c b/source/blender/makesrna/intern/rna_material_api.c index f5b13fdbc81..5f89664458c 100644 --- a/source/blender/makesrna/intern/rna_material_api.c +++ b/source/blender/makesrna/intern/rna_material_api.c @@ -38,8 +38,8 @@ void RNA_api_material(StructRNA *UNUSED(srna)) { - /* FunctionRNA *func; */ - /* PropertyRNA *parm; */ + // FunctionRNA *func; + // PropertyRNA *parm; } #endif diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index c99dfea524f..028b6b6e11f 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -2746,7 +2746,7 @@ static void rna_def_modifier_boolean(BlenderRNA *brna) prop = RNA_def_property(srna, "use_self", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", eBooleanModifierFlag_Self); - RNA_def_property_ui_text(prop, "Self", "Allow self-intersection in operands"); + RNA_def_property_ui_text(prop, "Self Intersection", "Allow self-intersection in operands"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); prop = RNA_def_property(srna, "use_hole_tolerant", PROP_BOOLEAN, PROP_NONE); diff --git a/source/blender/makesrna/intern/rna_nla.c b/source/blender/makesrna/intern/rna_nla.c index 17c7b331c88..d0711f28a6e 100644 --- a/source/blender/makesrna/intern/rna_nla.c +++ b/source/blender/makesrna/intern/rna_nla.c @@ -751,14 +751,14 @@ static void rna_def_nlastrip(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Influence", "Amount the strip contributes to the current result"); /* XXX: Update temporarily disabled so that the property can be edited at all! - * Even autokey only applies after the curves have been re-evaluated, + * Even auto-key only applies after the curves have been re-evaluated, * causing the unkeyed values to be lost. */ RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL); prop = RNA_def_property(srna, "strip_time", PROP_FLOAT, PROP_TIME); RNA_def_property_ui_text(prop, "Strip Time", "Frame of referenced Action to evaluate"); /* XXX: Update temporarily disabled so that the property can be edited at all! - * Even autokey only applies after the curves have been re-evaluated, + * Even auto-key only applies after the curves have been re-evaluated, * causing the unkeyed values to be lost. */ RNA_def_property_update(prop, NC_ANIMATION | ND_NLA | NA_EDITED, /*"rna_NlaStrip_update"*/ NULL); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index 86e134799aa..01889a1b0a9 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2056,7 +2056,8 @@ static bool switch_type_supported(const EnumPropertyItem *item) SOCK_OBJECT, SOCK_COLLECTION, SOCK_TEXTURE, - SOCK_MATERIAL); + SOCK_MATERIAL, + SOCK_IMAGE); } static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C), @@ -2092,6 +2093,19 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeRandom_type_itemf( return itemf_function_check(rna_enum_attribute_type_items, attribute_random_type_supported); } +static bool random_value_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_BOOL, CD_PROP_INT32); +} +static const EnumPropertyItem *rna_FunctionNodeRandomValue_type_itemf(bContext *UNUSED(C), + PointerRNA *UNUSED(ptr), + PropertyRNA *UNUSED(prop), + bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, random_value_type_supported); +} + static const EnumPropertyItem *rna_GeometryNodeAttributeRandomize_operation_itemf( bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { @@ -2168,6 +2182,30 @@ static const EnumPropertyItem *rna_GeometryNodeAttributeFill_type_itemf(bContext return itemf_function_check(rna_enum_attribute_type_items, attribute_fill_type_supported); } +static bool transfer_attribute_type_supported(const EnumPropertyItem *item) +{ + return ELEM( + item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3, CD_PROP_COLOR, CD_PROP_BOOL, CD_PROP_INT32); +} + +static const EnumPropertyItem *rna_NodeGeometryTransferAttribute_type_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, transfer_attribute_type_supported); +} + +static bool attribute_statistic_type_supported(const EnumPropertyItem *item) +{ + return ELEM(item->value, CD_PROP_FLOAT, CD_PROP_FLOAT3); +} +static const EnumPropertyItem *rna_GeometryNodeAttributeStatistic_type_itemf( + bContext *UNUSED(C), PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), bool *r_free) +{ + *r_free = true; + return itemf_function_check(rna_enum_attribute_type_items, attribute_statistic_type_supported); +} + /** * This bit of ugly code makes sure the float / attribute option shows up instead of * vector / attribute if the node uses an operation that uses a float for input B or C. @@ -3718,7 +3756,7 @@ static void rna_Node_image_layer_update(Main *bmain, Scene *scene, PointerRNA *p rna_Node_update(bmain, scene, ptr); - if (scene->nodetree != NULL) { + if (scene != NULL && scene->nodetree != NULL) { ntreeCompositUpdateRLayers(scene->nodetree); } } @@ -3890,7 +3928,7 @@ static const EnumPropertyItem *rna_Node_view_layer_itemf(bContext *UNUSED(C), static void rna_Node_view_layer_update(Main *bmain, Scene *scene, PointerRNA *ptr) { rna_Node_update(bmain, scene, ptr); - if (scene->nodetree != NULL) { + if (scene != NULL && scene->nodetree != NULL) { ntreeCompositUpdateRLayers(scene->nodetree); } } @@ -4325,7 +4363,7 @@ static void rna_ShaderNodeScript_update(Main *bmain, Scene *scene, PointerRNA *p { bNodeTree *ntree = (bNodeTree *)ptr->owner_id; bNode *node = (bNode *)ptr->data; - RenderEngineType *engine_type = RE_engines_find(scene->r.engine); + RenderEngineType *engine_type = (scene != NULL) ? RE_engines_find(scene->r.engine) : NULL; if (engine_type && engine_type->update_script_node) { /* auto update node */ @@ -4659,11 +4697,18 @@ static const EnumPropertyItem node_subsurface_method_items[] = { 0, "Christensen-Burley", "Approximation to physically based volume scattering"}, + {SHD_SUBSURFACE_RANDOM_WALK_FIXED_RADIUS, + "RANDOM_WALK_FIXED_RADIUS", + 0, + "Random Walk (Fixed Radius)", + "Volumetric approximation to physically based volume scattering, using the scattering radius " + "as specified"}, {SHD_SUBSURFACE_RANDOM_WALK, "RANDOM_WALK", 0, "Random Walk", - "Volumetric approximation to physically based volume scattering"}, + "Volumetric approximation to physically based volume scattering, with scattering radius " + "automatically adjusted to match color textures"}, {0, NULL, 0, NULL, NULL}}; /* -- Common nodes ---------------------------------------------------------- */ @@ -4874,6 +4919,17 @@ static void def_vector_curve(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_float_curve(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "mapping", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "storage"); + RNA_def_property_struct_type(prop, "CurveMapping"); + RNA_def_property_ui_text(prop, "Mapping", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_time(StructRNA *srna) { PropertyRNA *prop; @@ -4946,6 +5002,44 @@ static void def_texture(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_input_color(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeInputColor", "storage"); + + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR); + RNA_def_property_array(prop, 4); + RNA_def_property_float_sdna(prop, NULL, "color"); + RNA_def_property_ui_text(prop, "Color", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_fn_input_bool(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeInputBool", "storage"); + + prop = RNA_def_property(srna, "boolean", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "boolean", 1); + RNA_def_property_ui_text(prop, "Boolean", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_fn_input_int(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeInputInt", "storage"); + + prop = RNA_def_property(srna, "integer", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "integer"); + RNA_def_property_int_default(prop, 1); + RNA_def_property_ui_text(prop, "Integer", "Input value used for unconnected socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_fn_input_vector(StructRNA *srna) { PropertyRNA *prop; @@ -5320,6 +5414,50 @@ static void def_sh_tex_image(StructRNA *srna) RNA_def_property_update(prop, 0, "rna_Node_update"); } +static void def_geo_image_texture(StructRNA *srna) +{ + static const EnumPropertyItem fn_tex_prop_interpolation_items[] = { + {SHD_INTERP_LINEAR, "Linear", 0, "Linear", "Linear interpolation"}, + {SHD_INTERP_CLOSEST, "Closest", 0, "Closest", "No interpolation (sample closest texel)"}, + {SHD_INTERP_CUBIC, "Cubic", 0, "Cubic", "Cubic interpolation"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem prop_image_extension[] = { + {SHD_IMAGE_EXTENSION_REPEAT, + "REPEAT", + 0, + "Repeat", + "Cause the image to repeat horizontally and vertically"}, + {SHD_IMAGE_EXTENSION_EXTEND, + "EXTEND", + 0, + "Extend", + "Extend by repeating edge pixels of the image"}, + {SHD_IMAGE_EXTENSION_CLIP, + "CLIP", + 0, + "Clip", + "Clip to image size and set exterior pixels as transparent"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryImageTexture", "storage"); + + prop = RNA_def_property(srna, "interpolation", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, fn_tex_prop_interpolation_items); + RNA_def_property_ui_text(prop, "Interpolation", "Method for smoothing values between pixels"); + RNA_def_property_update(prop, 0, "rna_Node_update"); + + prop = RNA_def_property(srna, "extension", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prop_image_extension); + RNA_def_property_ui_text( + prop, "Extension", "How the image is extrapolated past its original bounds"); + RNA_def_property_update(prop, 0, "rna_Node_update"); +} + static void def_sh_tex_gradient(StructRNA *srna) { static const EnumPropertyItem prop_gradient_type[] = { @@ -6133,35 +6271,12 @@ static void def_sh_ambient_occlusion(StructRNA *srna) static void def_sh_subsurface(StructRNA *srna) { - static const EnumPropertyItem prop_subsurface_falloff_items[] = { - {SHD_SUBSURFACE_CUBIC, "CUBIC", 0, "Cubic", "Simple cubic falloff function"}, - {SHD_SUBSURFACE_GAUSSIAN, - "GAUSSIAN", - 0, - "Gaussian", - "Normal distribution, multiple can be combined to fit more complex profiles"}, - {SHD_SUBSURFACE_BURLEY, - "BURLEY", - 0, - "Christensen-Burley", - "Approximation to physically based volume scattering"}, - {SHD_SUBSURFACE_RANDOM_WALK, - "RANDOM_WALK", - 0, - "Random Walk", - "Volumetric approximation to physically based volume scattering"}, - {0, NULL, 0, NULL, NULL}, - }; - PropertyRNA *prop; prop = RNA_def_property(srna, "falloff", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "custom1"); - RNA_def_property_enum_items(prop, prop_subsurface_falloff_items); - RNA_def_property_ui_text(prop, - "Falloff", - "Function to determine how much light nearby points contribute based " - "on their distance to the shading point"); + RNA_def_property_enum_items(prop, node_subsurface_method_items); + RNA_def_property_ui_text(prop, "Method", "Method for rendering subsurface scattering"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_ShaderNode_socket_update"); } @@ -6542,11 +6657,11 @@ static void def_cmp_levels(StructRNA *srna) PropertyRNA *prop; static const EnumPropertyItem channel_items[] = { - {1, "COMBINED_RGB", 0, "C", "Combined RGB"}, - {2, "RED", 0, "R", "Red Channel"}, - {3, "GREEN", 0, "G", "Green Channel"}, - {4, "BLUE", 0, "B", "Blue Channel"}, - {5, "LUMINANCE", 0, "L", "Luminance Channel"}, + {1, "COMBINED_RGB", 0, "Combined", "Combined RGB"}, + {2, "RED", 0, "Red", "Red Channel"}, + {3, "GREEN", 0, "Green", "Green Channel"}, + {4, "BLUE", 0, "Blue", "Blue Channel"}, + {5, "LUMINANCE", 0, "Luminance", "Luminance Channel"}, {0, NULL, 0, NULL, NULL}, }; @@ -8890,12 +9005,37 @@ static void def_cmp_denoise(StructRNA *srna) { PropertyRNA *prop; + static const EnumPropertyItem prefilter_items[] = { + {CMP_NODE_DENOISE_PREFILTER_NONE, + "NONE", + 0, + "None", + "No prefiltering, use when guiding passes are noise-free"}, + {CMP_NODE_DENOISE_PREFILTER_FAST, + "FAST", + 0, + "Fast", + "Denoise image and guiding passes together. Improves quality when guiding passes are noisy " + "using least amount of extra processing time"}, + {CMP_NODE_DENOISE_PREFILTER_ACCURATE, + "ACCURATE", + 0, + "Accurate", + "Prefilter noisy guiding passes before denoising image. Improves quality when guiding " + "passes are noisy using extra processing time"}, + {0, NULL, 0, NULL, NULL}}; + RNA_def_struct_sdna_from(srna, "NodeDenoise", "storage"); prop = RNA_def_property(srna, "use_hdr", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "hdr", 0); RNA_def_property_ui_text(prop, "HDR", "Process HDR images"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "prefilter", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, prefilter_items); + RNA_def_property_ui_text(prop, "", "Denoising prefilter"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } static void def_cmp_antialiasing(StructRNA *srna) @@ -9052,6 +9192,30 @@ static void def_geo_curve_primitive_bezier_segment(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_sample(StructRNA *srna) +{ + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_SAMPLE_FACTOR, + "FACTOR", + 0, + "Factor", + "Find sample positions on the curve using a factor of its total length"}, + {GEO_NODE_CURVE_SAMPLE_LENGTH, + "LENGTH", + 0, + "Length", + "Find sample positions on the curve using a distance from its beginning"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSample", "storage"); + + PropertyRNA *prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "Method for sampling input"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_triangulate(StructRNA *srna) { PropertyRNA *prop; @@ -9129,6 +9293,21 @@ static void def_geo_subdivision_surface(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_fn_random_value(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeRandomValue", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "data_type"); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_FunctionNodeRandomValue_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_attribute_randomize(StructRNA *srna) { PropertyRNA *prop; @@ -9194,6 +9373,29 @@ static void def_geo_attribute_convert(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_attribute_statistic(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeStatistic_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text( + prop, + "Data Type", + "The data type the attribute is converted to before calculating the results"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to read the data from"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_attribute_math(StructRNA *srna) { PropertyRNA *prop; @@ -9417,6 +9619,33 @@ static void def_geo_point_distribute(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_distribute_points_on_faces(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem rna_node_geometry_distribute_points_on_faces_mode_items[] = { + {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM, + "RANDOM", + 0, + "Random", + "Distribute points randomly on the surface"}, + {GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON, + "POISSON", + 0, + "Poisson Disk", + "Distribute the points randomly on the surface while taking a minimum distance between " + "points into account"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "distribute_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, rna_node_geometry_distribute_points_on_faces_mode_items); + RNA_def_property_enum_default(prop, GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM); + RNA_def_property_ui_text(prop, "Distribution Method", "Method to use for scattering points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_attribute_color_ramp(StructRNA *srna) { PropertyRNA *prop; @@ -9543,6 +9772,36 @@ static void def_geo_curve_set_handles(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_legacy_curve_set_handles(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSetHandles", "storage"); + + prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "handle_type"); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_curve_set_handle_positions(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometrySetCurveHandlePositions", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to update left and right handles"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_select_handles(StructRNA *srna) { PropertyRNA *prop; @@ -9561,6 +9820,24 @@ static void def_geo_curve_select_handles(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_handle_type_selection(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveSelectHandles", "storage"); + + prop = RNA_def_property(srna, "handle_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "handle_type"); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_type_items); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_node_geometry_curve_handle_side_items); + RNA_def_property_ui_text(prop, "Mode", "Whether to check the type of left and right handles"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_curve_primitive_circle(StructRNA *srna) { static const EnumPropertyItem mode_items[] = { @@ -9673,6 +9950,51 @@ static void def_geo_point_rotate(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_fn_rotate_euler(StructRNA *srna) +{ + static const EnumPropertyItem type_items[] = { + {FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE, + "AXIS_ANGLE", + ICON_NONE, + "Axis Angle", + "Rotate around an axis by an angle"}, + {FN_NODE_ROTATE_EULER_TYPE_EULER, + "EULER", + ICON_NONE, + "Euler", + "Rotate around the X, Y, and Z axes"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem space_items[] = { + {FN_NODE_ROTATE_EULER_SPACE_OBJECT, + "OBJECT", + ICON_NONE, + "Object", + "Rotate the input rotation in the local space of the object"}, + {FN_NODE_ROTATE_EULER_SPACE_LOCAL, + "LOCAL", + ICON_NONE, + "Local", + "Rotate the input rotation in its local space"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, type_items); + RNA_def_property_ui_text(prop, "Type", "Method used to describe the rotation"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "space", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_items(prop, space_items); + RNA_def_property_ui_text(prop, "Space", "Base orientation for rotation"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_align_rotation_to_vector(StructRNA *srna) { static const EnumPropertyItem axis_items[] = { @@ -9743,6 +10065,66 @@ static void def_geo_align_rotation_to_vector(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_fn_align_euler_to_vector(StructRNA *srna) +{ + static const EnumPropertyItem axis_items[] = { + {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_X, + "X", + ICON_NONE, + "X", + "Align the X axis with the vector"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Y, + "Y", + ICON_NONE, + "Y", + "Align the Y axis with the vector"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_AXIS_Z, + "Z", + ICON_NONE, + "Z", + "Align the Z axis with the vector"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem pivot_axis_items[] = { + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO, + "AUTO", + ICON_NONE, + "Auto", + "Automatically detect the best rotation axis to rotate towards the vector"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_X, + "X", + ICON_NONE, + "X", + "Rotate around the local X axis"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Y, + "Y", + ICON_NONE, + "Y", + "Rotate around the local Y axis"}, + {FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_Z, + "Z", + ICON_NONE, + "Z", + "Rotate around the local Z axis"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + prop = RNA_def_property(srna, "axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom1"); + RNA_def_property_enum_items(prop, axis_items); + RNA_def_property_ui_text(prop, "Axis", "Axis to align to the vector"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "pivot_axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "custom2"); + RNA_def_property_enum_items(prop, pivot_axis_items); + RNA_def_property_ui_text(prop, "Pivot Axis", "Axis to rotate around"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_point_scale(StructRNA *srna) { PropertyRNA *prop; @@ -9797,7 +10179,7 @@ static void def_geo_object_info(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_points_to_volume(StructRNA *srna) +static void def_geo_legacy_points_to_volume(StructRNA *srna) { PropertyRNA *prop; @@ -9828,6 +10210,32 @@ static void def_geo_points_to_volume(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_points_to_volume(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem resolution_mode_items[] = { + {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT, + "VOXEL_AMOUNT", + 0, + "Amount", + "Specify the approximate number of voxels along the diagonal"}, + {GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE, + "VOXEL_SIZE", + 0, + "Size", + "Specify the voxel side length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryPointsToVolume", "storage"); + + prop = RNA_def_property(srna, "resolution_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, resolution_mode_items); + RNA_def_property_ui_text(prop, "Resolution Mode", "How the voxel size is specified"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_collection_info(StructRNA *srna) { PropertyRNA *prop; @@ -9855,7 +10263,7 @@ static void def_geo_collection_info(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_attribute_proximity(StructRNA *srna) +static void def_geo_legacy_attribute_proximity(StructRNA *srna) { static const EnumPropertyItem target_geometry_element[] = { {GEO_NODE_PROXIMITY_TARGET_POINTS, @@ -9888,6 +10296,39 @@ static void def_geo_attribute_proximity(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_proximity(StructRNA *srna) +{ + static const EnumPropertyItem target_element_items[] = { + {GEO_NODE_PROX_TARGET_POINTS, + "POINTS", + ICON_NONE, + "Points", + "Calculate the proximity to the target's points (faster than the other modes)"}, + {GEO_NODE_PROX_TARGET_EDGES, + "EDGES", + ICON_NONE, + "Edges", + "Calculate the proximity to the target's edges"}, + {GEO_NODE_PROX_TARGET_FACES, + "FACES", + ICON_NONE, + "Faces", + "Calculate the proximity to the target's faces"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryProximity", "storage"); + + prop = RNA_def_property(srna, "target_element", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, target_element_items); + RNA_def_property_enum_default(prop, GEO_NODE_PROX_TARGET_FACES); + RNA_def_property_ui_text( + prop, "Target Geometry", "Element of the target geometry to calculate the distance from"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + static void def_geo_volume_to_mesh(StructRNA *srna) { PropertyRNA *prop; @@ -9901,12 +10342,12 @@ static void def_geo_volume_to_mesh(StructRNA *srna) {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT, "VOXEL_AMOUNT", 0, - "Voxel Amount", + "Amount", "Desired number of voxels along one axis"}, {VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE, "VOXEL_SIZE", 0, - "Voxel Size", + "Size", "Desired voxel side length"}, {0, NULL, 0, NULL, NULL}, }; @@ -9974,7 +10415,7 @@ static void def_geo_mesh_cylinder(StructRNA *srna) prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); RNA_def_property_ui_text(prop, "Fill Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } static void def_geo_mesh_cone(StructRNA *srna) @@ -9986,7 +10427,7 @@ static void def_geo_mesh_cone(StructRNA *srna) prop = RNA_def_property(srna, "fill_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_node_geometry_mesh_circle_fill_type_items); RNA_def_property_ui_text(prop, "Fill Type", ""); - RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } static void def_geo_mesh_line(StructRNA *srna) @@ -10091,18 +10532,18 @@ static void def_geo_curve_resample(StructRNA *srna) PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_SAMPLE_EVALUATED, + {GEO_NODE_CURVE_RESAMPLE_EVALUATED, "EVALUATED", 0, "Evaluated", "Output the input spline's evaluated points, based on the resolution attribute for NURBS " "and Bezier splines. Poly splines are unchanged"}, - {GEO_NODE_CURVE_SAMPLE_COUNT, + {GEO_NODE_CURVE_RESAMPLE_COUNT, "COUNT", 0, "Count", "Sample the specified number of points along each spline"}, - {GEO_NODE_CURVE_SAMPLE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", @@ -10119,7 +10560,7 @@ static void def_geo_curve_resample(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_curve_subdivide(StructRNA *srna) +static void def_geo_legacy_curve_subdivide(StructRNA *srna) { PropertyRNA *prop; @@ -10131,23 +10572,49 @@ static void def_geo_curve_subdivide(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } -static void def_geo_curve_to_points(StructRNA *srna) +static void def_geo_curve_fillet(StructRNA *srna) { PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_SAMPLE_EVALUATED, + {GEO_NODE_CURVE_FILLET_BEZIER, + "BEZIER", + 0, + "Bezier", + "Align Bezier handles to create circular arcs at each control point"}, + {GEO_NODE_CURVE_FILLET_POLY, + "POLY", + 0, + "Poly", + "Add control points along a circular arc (handle type is vector if Bezier Spline)"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveFillet", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to choose number of vertices on fillet"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_legacy_curve_to_points(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_RESAMPLE_EVALUATED, "EVALUATED", 0, "Evaluated", "Create points from the curve's evaluated points, based on the resolution attribute for " "NURBS and Bezier splines"}, - {GEO_NODE_CURVE_SAMPLE_COUNT, + {GEO_NODE_CURVE_RESAMPLE_COUNT, "COUNT", 0, "Count", "Sample each spline by evenly distributing the specified number of points"}, - {GEO_NODE_CURVE_SAMPLE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", @@ -10163,17 +10630,85 @@ static void def_geo_curve_to_points(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_curve_to_points(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_CURVE_RESAMPLE_EVALUATED, + "EVALUATED", + 0, + "Evaluated", + "Create points from the curve's evaluated points, based on the resolution attribute for " + "NURBS and Bezier splines"}, + {GEO_NODE_CURVE_RESAMPLE_COUNT, + "COUNT", + 0, + "Count", + "Sample each spline by evenly distributing the specified number of points"}, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, + "LENGTH", + 0, + "Length", + "Sample each spline by splitting it into segments with the specified length"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryCurveToPoints", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", "How to generate points from the input curve"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); +} + +static void def_geo_mesh_to_points(StructRNA *srna) +{ + PropertyRNA *prop; + + static EnumPropertyItem mode_items[] = { + {GEO_NODE_MESH_TO_POINTS_VERTICES, + "VERTICES", + 0, + "Vertices", + "Create a point in the point cloud for each selected vertex"}, + {GEO_NODE_MESH_TO_POINTS_EDGES, + "EDGES", + 0, + "Edges", + "Create a point in the point cloud for each selected edge"}, + {GEO_NODE_MESH_TO_POINTS_FACES, + "FACES", + 0, + "Faces", + "Create a point in the point cloud for each selected face"}, + {GEO_NODE_MESH_TO_POINTS_CORNERS, + "CORNERS", + 0, + "Corners", + "Create a point in the point cloud for each selected face corner"}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_struct_sdna_from(srna, "NodeGeometryMeshToPoints", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_curve_trim(StructRNA *srna) { PropertyRNA *prop; static EnumPropertyItem mode_items[] = { - {GEO_NODE_CURVE_INTERPOLATE_FACTOR, + {GEO_NODE_CURVE_SAMPLE_FACTOR, "FACTOR", 0, "Factor", "Find the endpoint positions using a factor of each spline's length"}, - {GEO_NODE_CURVE_INTERPOLATE_LENGTH, + {GEO_NODE_CURVE_RESAMPLE_LENGTH, "LENGTH", 0, "Length", @@ -10192,13 +10727,13 @@ static void def_geo_curve_trim(StructRNA *srna) static void def_geo_attribute_transfer(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { - {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, + {GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, "NEAREST_FACE_INTERPOLATED", 0, "Nearest Face Interpolated", "Transfer the attribute from the nearest face on a surface (loose points and edges are " "ignored)"}, - {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST, + {GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST, "NEAREST", 0, "Nearest", @@ -10223,6 +10758,53 @@ static void def_geo_attribute_transfer(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_transfer_attribute(StructRNA *srna) +{ + static EnumPropertyItem mapping_items[] = { + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED, + "NEAREST_FACE_INTERPOLATED", + 0, + "Nearest Face Interpolated", + "Transfer the attribute from the nearest face on a surface (loose points and edges are " + "ignored)"}, + {GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST, + "NEAREST", + 0, + "Nearest", + "Transfer the element from the nearest element (using face and edge centers for the " + "distance computation)"}, + {GEO_NODE_ATTRIBUTE_TRANSFER_INDEX, + "INDEX", + 0, + "Index", + "Transfer the data from the element with the corresponding index in the target geometry"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryTransferAttribute", "storage"); + + prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "mode"); + RNA_def_property_enum_items(prop, mapping_items); + RNA_def_property_ui_text(prop, "Mapping", "Mapping between geometries"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_NodeGeometryTransferAttribute_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "The type for the source and result data"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "The domain to use on the target geometry"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_geo_input_material(StructRNA *srna) { PropertyRNA *prop; @@ -10236,7 +10818,7 @@ static void def_geo_input_material(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } -static void def_geo_raycast(StructRNA *srna) +static void def_geo_legacy_raycast(StructRNA *srna) { static EnumPropertyItem mapping_items[] = { {GEO_NODE_RAYCAST_INTERPOLATED, @@ -10272,6 +10854,39 @@ static void def_geo_raycast(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); } +static void def_geo_raycast(StructRNA *srna) +{ + static EnumPropertyItem mapping_items[] = { + {GEO_NODE_RAYCAST_INTERPOLATED, + "INTERPOLATED", + 0, + "Interpolated", + "Interpolate the attribute from the corners of the hit face"}, + {GEO_NODE_RAYCAST_NEAREST, + "NEAREST", + 0, + "Nearest", + "Use the attribute value of the closest mesh element"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryRaycast", "storage"); + + prop = RNA_def_property(srna, "mapping", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mapping_items); + RNA_def_property_ui_text(prop, "Mapping", "Mapping from the target geometry to hit points"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", "Type of data stored in attribute"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); +} + static void def_geo_curve_fill(StructRNA *srna) { static const EnumPropertyItem mode_items[] = { @@ -10310,6 +10925,172 @@ static void def_geo_attribute_capture(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_geo_delete_geometry(StructRNA *srna) +{ + PropertyRNA *prop; + + static const EnumPropertyItem mode_items[] = { + {GEO_NODE_DELETE_GEOMETRY_MODE_ALL, "ALL", 0, "All", ""}, + {GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE, "EDGE_FACE", 0, "Only Edges & Faces", ""}, + {GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE, "ONLY_FACE", 0, "Only Faces", ""}, + {0, NULL, 0, NULL, NULL}, + }; + RNA_def_struct_sdna_from(srna, "NodeGeometryDeleteGeometry", "storage"); + + prop = RNA_def_property(srna, "mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, mode_items); + RNA_def_property_enum_default(prop, GEO_NODE_DELETE_GEOMETRY_MODE_ALL); + RNA_def_property_ui_text(prop, "Mode", "Which parts of the mesh component to delete"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_without_corner_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to delete in"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_string_to_curves(StructRNA *srna) +{ + static const EnumPropertyItem rna_node_geometry_string_to_curves_overflow_items[] = { + {GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW, + "OVERFLOW", + ICON_NONE, + "Overflow", + "Let the text use more space than the specified height"}, + {GEO_NODE_STRING_TO_CURVES_MODE_SCALE_TO_FIT, + "SCALE_TO_FIT", + ICON_NONE, + "Scale To Fit", + "Scale the text size to fit inside the width and height"}, + {GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE, + "TRUNCATE", + ICON_NONE, + "Truncate", + "Only output curves that fit within the width and height. Output the remainder to the " + "\"Remainder\" output"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem rna_node_geometry_string_to_curves_align_x_items[] = { + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT, + "LEFT", + ICON_ALIGN_LEFT, + "Left", + "Align text to the left"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_CENTER, + "CENTER", + ICON_ALIGN_CENTER, + "Center", + "Align text to the center"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_RIGHT, + "RIGHT", + ICON_ALIGN_RIGHT, + "Right", + "Align text to the right"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_JUSTIFY, + "JUSTIFY", + ICON_ALIGN_JUSTIFY, + "Justify", + "Align text to the left and the right"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_X_FLUSH, + "FLUSH", + ICON_ALIGN_FLUSH, + "Flush", + "Align text to the left and the right, with equal character spacing"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const EnumPropertyItem rna_node_geometry_string_to_curves_align_y_items[] = { + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE, + "TOP_BASELINE", + ICON_ALIGN_TOP, + "Top Baseline", + "Align text to the top baseline"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP, + "TOP", + ICON_ALIGN_TOP, + "Top", + "Align text to the top"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_MIDDLE, + "MIDDLE", + ICON_ALIGN_MIDDLE, + "Middle", + "Align text to the middle"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM_BASELINE, + "BOTTOM_BASELINE", + ICON_ALIGN_BOTTOM, + "Bottom Baseline", + "Align text to the bottom baseline"}, + {GEO_NODE_STRING_TO_CURVES_ALIGN_Y_BOTTOM, + "BOTTOM", + ICON_ALIGN_BOTTOM, + "Bottom", + "Align text to the bottom"}, + {0, NULL, 0, NULL, NULL}, + }; + + PropertyRNA *prop; + + prop = RNA_def_property(srna, "font", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "id"); + RNA_def_property_struct_type(prop, "VectorFont"); + RNA_def_property_ui_text(prop, "Font", "Font of the text. Falls back to the UI font by default"); + RNA_def_property_flag(prop, PROP_EDITABLE); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + RNA_def_struct_sdna_from(srna, "NodeGeometryStringToCurves", "storage"); + + prop = RNA_def_property(srna, "overflow", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "overflow"); + RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_overflow_items); + RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); + RNA_def_property_ui_text(prop, "Overflow", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update"); + + prop = RNA_def_property(srna, "align_x", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "align_x"); + RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_x_items); + RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT); + RNA_def_property_ui_text(prop, "Align X", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "align_y", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "align_y"); + RNA_def_property_enum_items(prop, rna_node_geometry_string_to_curves_align_y_items); + RNA_def_property_enum_default(prop, GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE); + RNA_def_property_ui_text(prop, "Align Y", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_separate_geometry(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometrySeparateGeometry", "storage"); + + prop = RNA_def_property(srna, "domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_without_corner_items); + RNA_def_property_enum_default(prop, ATTR_DOMAIN_POINT); + RNA_def_property_ui_text(prop, "Domain", "Which domain to separate on"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + +static void def_geo_viewer(StructRNA *srna) +{ + PropertyRNA *prop; + + RNA_def_struct_sdna_from(srna, "NodeGeometryViewer", "storage"); + + prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_type_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_GeometryNodeAttributeFill_type_itemf"); + RNA_def_property_enum_default(prop, CD_PROP_FLOAT); + RNA_def_property_ui_text(prop, "Data Type", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_GeometryNode_socket_update"); +} + /* -------------------------------------------------------------------------- */ static void rna_def_shader_node(BlenderRNA *brna) @@ -10576,6 +11357,14 @@ static void rna_def_node_socket_interface(BlenderRNA *brna) prop, "Hide Value", "Hide the socket input value even when the socket is not connected"); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + prop = RNA_def_property(srna, "attribute_domain", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, rna_enum_attribute_domain_items); + RNA_def_property_ui_text( + prop, + "Attribute Domain", + "Attribute domain used by the geometry nodes modifier to create an attribute output"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update"); + /* registration */ prop = RNA_def_property(srna, "bl_socket_idname", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "typeinfo->idname"); @@ -11984,6 +12773,8 @@ static void rna_def_nodetree(BlenderRNA *brna) prop = RNA_def_property(srna, "view_center", PROP_FLOAT, PROP_XYZ); RNA_def_property_array(prop, 2); RNA_def_property_float_sdna(prop, NULL, "view_center"); + RNA_def_property_ui_text( + prop, "", "The current location (offset) of the view for this Node Tree"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* AnimData */ diff --git a/source/blender/makesrna/intern/rna_object_api.c b/source/blender/makesrna/intern/rna_object_api.c index 10ba2b9acb1..10094ade711 100644 --- a/source/blender/makesrna/intern/rna_object_api.c +++ b/source/blender/makesrna/intern/rna_object_api.c @@ -69,7 +69,6 @@ static const EnumPropertyItem space_items[] = { # include "BKE_constraint.h" # include "BKE_context.h" # include "BKE_customdata.h" -# include "BKE_font.h" # include "BKE_global.h" # include "BKE_layer.h" # include "BKE_main.h" @@ -78,6 +77,7 @@ static const EnumPropertyItem space_items[] = { # include "BKE_modifier.h" # include "BKE_object.h" # include "BKE_report.h" +# include "BKE_vfont.h" # include "ED_object.h" # include "ED_screen.h" diff --git a/source/blender/makesrna/intern/rna_object_force.c b/source/blender/makesrna/intern/rna_object_force.c index 98d59bf3a1a..186222d2ca0 100644 --- a/source/blender/makesrna/intern/rna_object_force.c +++ b/source/blender/makesrna/intern/rna_object_force.c @@ -985,10 +985,12 @@ static void rna_def_pointcache_common(StructRNA *srna) prop = RNA_def_property(srna, "is_baked", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_BAKED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "", "The cache is baked"); prop = RNA_def_property(srna, "is_baking", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_BAKING); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "", "The cache is being baked"); prop = RNA_def_property(srna, "use_disk_cache", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_DISK_CACHE); @@ -999,11 +1001,13 @@ static void rna_def_pointcache_common(StructRNA *srna) prop = RNA_def_property(srna, "is_outdated", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_OUTDATED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_ui_text(prop, "Cache is outdated", ""); + RNA_def_property_ui_text(prop, "Cache Is Outdated", ""); prop = RNA_def_property(srna, "is_frame_skip", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", PTCACHE_FRAMES_SKIPPED); RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text( + prop, "", "Some frames were skipped while baking/saving that cache"); prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "name"); @@ -1056,8 +1060,8 @@ static void rna_def_ptcache_point_caches(BlenderRNA *brna, PropertyRNA *cprop) StructRNA *srna; PropertyRNA *prop; - /* FunctionRNA *func; */ - /* PropertyRNA *parm; */ + // FunctionRNA *func; + // PropertyRNA *parm; RNA_def_property_srna(cprop, "PointCaches"); srna = RNA_def_struct(brna, "PointCaches", NULL); diff --git a/source/blender/makesrna/intern/rna_palette.c b/source/blender/makesrna/intern/rna_palette.c index 67d6daf5a13..30604c7a3b8 100644 --- a/source/blender/makesrna/intern/rna_palette.c +++ b/source/blender/makesrna/intern/rna_palette.c @@ -37,12 +37,20 @@ # include "BKE_report.h" static PaletteColor *rna_Palette_color_new(Palette *palette) { + if (ID_IS_LINKED(palette) || ID_IS_OVERRIDE_LIBRARY(palette)) { + return NULL; + } + PaletteColor *color = BKE_palette_color_add(palette); return color; } static void rna_Palette_color_remove(Palette *palette, ReportList *reports, PointerRNA *color_ptr) { + if (ID_IS_LINKED(palette) || ID_IS_OVERRIDE_LIBRARY(palette)) { + return; + } + PaletteColor *color = color_ptr->data; if (BLI_findindex(&palette->colors, color) == -1) { @@ -58,6 +66,10 @@ static void rna_Palette_color_remove(Palette *palette, ReportList *reports, Poin static void rna_Palette_color_clear(Palette *palette) { + if (ID_IS_LINKED(palette) || ID_IS_OVERRIDE_LIBRARY(palette)) { + return; + } + BKE_palette_clear(palette); } @@ -141,6 +153,7 @@ static void rna_def_palettecolor(BlenderRNA *brna) prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_range(prop, 0.0, 1.0); RNA_def_property_float_sdna(prop, NULL, "rgb"); + RNA_def_property_flag(prop, PROP_LIB_EXCEPTION); RNA_def_property_array(prop, 3); RNA_def_property_ui_text(prop, "Color", ""); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 4400d198b4a..fcb46904e8d 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -52,6 +52,7 @@ const EnumPropertyItem rna_enum_render_pass_type_items[] = { {SCE_PASS_Z, "Z", 0, "Z", ""}, {SCE_PASS_SHADOW, "SHADOW", 0, "Shadow", ""}, {SCE_PASS_AO, "AO", 0, "Ambient Occlusion", ""}, + {SCE_PASS_POSITION, "POSITION", 0, "Position", ""}, {SCE_PASS_NORMAL, "NORMAL", 0, "Normal", ""}, {SCE_PASS_VECTOR, "VECTOR", 0, "Vector", ""}, {SCE_PASS_INDEXOB, "OBJECT_INDEX", 0, "Object Index", ""}, @@ -79,6 +80,7 @@ const EnumPropertyItem rna_enum_bake_pass_type_items[] = { {SCE_PASS_COMBINED, "COMBINED", 0, "Combined", ""}, {SCE_PASS_AO, "AO", 0, "Ambient Occlusion", ""}, {SCE_PASS_SHADOW, "SHADOW", 0, "Shadow", ""}, + {SCE_PASS_POSITION, "POSITION", 0, "Position", ""}, {SCE_PASS_NORMAL, "NORMAL", 0, "Normal", ""}, {SCE_PASS_UV, "UV", 0, "UV", ""}, {SCE_PASS_ROUGHNESS, "ROUGHNESS", 0, "ROUGHNESS", ""}, @@ -177,6 +179,40 @@ static void engine_render(RenderEngine *engine, Depsgraph *depsgraph) RNA_parameter_list_free(&list); } +static void engine_render_frame_finish(RenderEngine *engine) +{ + extern FunctionRNA rna_RenderEngine_render_frame_finish_func; + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(NULL, engine->type->rna_ext.srna, engine, &ptr); + func = &rna_RenderEngine_render_frame_finish_func; + + RNA_parameter_list_create(&list, &ptr, func); + engine->type->rna_ext.call(NULL, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + +static void engine_draw(RenderEngine *engine, const struct bContext *context, Depsgraph *depsgraph) +{ + extern FunctionRNA rna_RenderEngine_draw_func; + PointerRNA ptr; + ParameterList list; + FunctionRNA *func; + + RNA_pointer_create(NULL, engine->type->rna_ext.srna, engine, &ptr); + func = &rna_RenderEngine_draw_func; + + RNA_parameter_list_create(&list, &ptr, func); + RNA_parameter_set_lookup(&list, "context", &context); + RNA_parameter_set_lookup(&list, "depsgraph", &depsgraph); + engine->type->rna_ext.call(NULL, &ptr, func, &list); + + RNA_parameter_list_free(&list); +} + static void engine_bake(RenderEngine *engine, struct Depsgraph *depsgraph, struct Object *object, @@ -315,7 +351,7 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, RenderEngineType *et, dummyet = {NULL}; RenderEngine dummyengine = {NULL}; PointerRNA dummyptr; - int have_function[8]; + int have_function[9]; /* setup dummy engine & engine type to store static properties in */ dummyengine.type = &dummyet; @@ -358,11 +394,13 @@ static StructRNA *rna_RenderEngine_register(Main *bmain, et->update = (have_function[0]) ? engine_update : NULL; et->render = (have_function[1]) ? engine_render : NULL; - et->bake = (have_function[2]) ? engine_bake : NULL; - et->view_update = (have_function[3]) ? engine_view_update : NULL; - et->view_draw = (have_function[4]) ? engine_view_draw : NULL; - et->update_script_node = (have_function[5]) ? engine_update_script_node : NULL; - et->update_render_passes = (have_function[6]) ? engine_update_render_passes : NULL; + et->render_frame_finish = (have_function[2]) ? engine_render_frame_finish : NULL; + et->draw = (have_function[3]) ? engine_draw : NULL; + et->bake = (have_function[4]) ? engine_bake : NULL; + et->view_update = (have_function[5]) ? engine_view_update : NULL; + et->view_draw = (have_function[6]) ? engine_view_draw : NULL; + et->update_script_node = (have_function[7]) ? engine_update_script_node : NULL; + et->update_render_passes = (have_function[8]) ? engine_update_render_passes : NULL; RE_engines_register(et); @@ -519,6 +557,19 @@ static void rna_def_render_engine(BlenderRNA *brna) parm = RNA_def_pointer(func, "depsgraph", "Depsgraph", "", ""); RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + func = RNA_def_function(srna, "render_frame_finish", NULL); + RNA_def_function_ui_description( + func, "Perform finishing operations after all view layers in a frame were rendered"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); + + func = RNA_def_function(srna, "draw", NULL); + RNA_def_function_ui_description(func, "Draw render image"); + RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL); + parm = RNA_def_pointer(func, "context", "Context", "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "depsgraph", "Depsgraph", "", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + func = RNA_def_function(srna, "bake", NULL); RNA_def_function_ui_description(func, "Bake passes"); RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL | FUNC_ALLOW_WRITE); @@ -641,6 +692,14 @@ static void rna_def_render_engine(BlenderRNA *brna) parm = RNA_def_boolean(func, "do_break", 0, "Break", ""); RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "pass_by_index_get", "RE_engine_pass_by_index_get"); + parm = RNA_def_string(func, "layer", NULL, 0, "Layer", "Name of render layer to get pass for"); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "index", 0, 0, INT_MAX, "Index", "Index of pass to get", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_pointer(func, "render_pass", "RenderPass", "Index", "Index of pass to get"); + RNA_def_function_return(func, parm); + func = RNA_def_function(srna, "active_view_get", "RE_engine_active_view_get"); parm = RNA_def_string(func, "view", NULL, 0, "View", "Single view active"); RNA_def_function_return(func, parm); @@ -761,6 +820,22 @@ static void rna_def_render_engine(BlenderRNA *brna) func = RNA_def_function(srna, "free_blender_memory", "RE_engine_free_blender_memory"); RNA_def_function_ui_description(func, "Free Blender side memory of render engine"); + func = RNA_def_function(srna, "tile_highlight_set", "RE_engine_tile_highlight_set"); + RNA_def_function_ui_description(func, "Set highlighted state of the given tile"); + parm = RNA_def_int(func, "x", 0, 0, INT_MAX, "X", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "y", 0, 0, INT_MAX, "Y", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "width", 0, 0, INT_MAX, "Width", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_int(func, "height", 0, 0, INT_MAX, "Height", "", 0, INT_MAX); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + parm = RNA_def_boolean(func, "highlight", 0, "Highlight", ""); + RNA_def_parameter_flags(parm, 0, PARM_REQUIRED); + + func = RNA_def_function(srna, "tile_highlight_clear_all", "RE_engine_tile_highlight_clear_all"); + RNA_def_function_ui_description(func, "Clear highlight from all tiles"); + RNA_define_verify_sdna(0); prop = RNA_def_property(srna, "is_animation", PROP_BOOLEAN, PROP_NONE); @@ -777,11 +852,6 @@ static void rna_def_render_engine(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "layer_override", 1); RNA_def_property_array(prop, 20); - prop = RNA_def_property(srna, "tile_x", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, NULL, "tile_x"); - prop = RNA_def_property(srna, "tile_y", PROP_INT, PROP_UNSIGNED); - RNA_def_property_int_sdna(prop, NULL, "tile_y"); - prop = RNA_def_property(srna, "resolution_x", PROP_INT, PROP_PIXEL); RNA_def_property_int_sdna(prop, NULL, "resolution_x"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -880,12 +950,6 @@ static void rna_def_render_engine(BlenderRNA *brna) "Don't expose Cycles and Eevee shading nodes in the node editor user " "interface, so own nodes can be used instead"); - prop = RNA_def_property(srna, "bl_use_save_buffers", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_SAVE_BUFFERS); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - RNA_def_property_ui_text( - prop, "Use Save Buffers", "Support render to an on disk buffer during rendering"); - prop = RNA_def_property(srna, "bl_use_spherical_stereo", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "type->flag", RE_USE_SPHERICAL_STEREO); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); diff --git a/source/blender/makesrna/intern/rna_rigidbody.c b/source/blender/makesrna/intern/rna_rigidbody.c index 0b56a73efa2..c0fb904101d 100644 --- a/source/blender/makesrna/intern/rna_rigidbody.c +++ b/source/blender/makesrna/intern/rna_rigidbody.c @@ -215,9 +215,10 @@ static void rna_RigidBodyWorld_constraints_collection_update(Main *bmain, static void rna_RigidBodyOb_reset(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { - RigidBodyWorld *rbw = scene->rigidbody_world; - - BKE_rigidbody_cache_reset(rbw); + if (scene != NULL) { + RigidBodyWorld *rbw = scene->rigidbody_world; + BKE_rigidbody_cache_reset(rbw); + } } static void rna_RigidBodyOb_shape_update(Main *bmain, Scene *scene, PointerRNA *ptr) @@ -232,10 +233,12 @@ static void rna_RigidBodyOb_shape_update(Main *bmain, Scene *scene, PointerRNA * static void rna_RigidBodyOb_shape_reset(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) { - RigidBodyWorld *rbw = scene->rigidbody_world; - RigidBodyOb *rbo = (RigidBodyOb *)ptr->data; + if (scene != NULL) { + RigidBodyWorld *rbw = scene->rigidbody_world; + BKE_rigidbody_cache_reset(rbw); + } - BKE_rigidbody_cache_reset(rbw); + RigidBodyOb *rbo = (RigidBodyOb *)ptr->data; if (rbo->shared->physics_shape) { rbo->flag |= RBO_FLAG_NEEDS_RESHAPE; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index badaaa14aa4..3468cab9eea 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -532,7 +532,6 @@ const EnumPropertyItem rna_enum_stereo3d_interlace_type_items[] = { const EnumPropertyItem rna_enum_bake_pass_filter_type_items[] = { {R_BAKE_PASS_FILTER_NONE, "NONE", 0, "None", ""}, - {R_BAKE_PASS_FILTER_AO, "AO", 0, "Ambient Occlusion", ""}, {R_BAKE_PASS_FILTER_EMIT, "EMIT", 0, "Emit", ""}, {R_BAKE_PASS_FILTER_DIRECT, "DIRECT", 0, "Direct", ""}, {R_BAKE_PASS_FILTER_INDIRECT, "INDIRECT", 0, "Indirect", ""}, @@ -677,7 +676,9 @@ static void rna_ToolSettings_snap_mode_set(struct PointerRNA *ptr, int value) /* Grease Pencil update cache */ static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { - ED_gpencil_tag_scene_gpencil(scene); + if (scene != NULL) { + ED_gpencil_tag_scene_gpencil(scene); + } } static void rna_Gpencil_extend_selection(bContext *C, PointerRNA *UNUSED(ptr)) @@ -849,8 +850,9 @@ static void rna_Scene_camera_update(Main *bmain, Scene *UNUSED(scene_unused), Po DEG_relations_tag_update(bmain); } -static void rna_Scene_fps_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_fps_update(Main *bmain, Scene *UNUSED(active_scene), PointerRNA *ptr) { + Scene *scene = (Scene *)ptr->owner_id; DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_FPS | ID_RECALC_SEQUENCER_STRIPS); /* NOTE: Tag via dependency graph will take care of all the updates ion the evaluated domain, * however, changes in FPS actually modifies an original skip length, @@ -858,9 +860,9 @@ static void rna_Scene_fps_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(p SEQ_sound_update_length(bmain, scene); } -static void rna_Scene_listener_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_listener_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_LISTENER); + DEG_id_tag_update(ptr->owner_id, ID_RECALC_AUDIO_LISTENER); } static void rna_Scene_volume_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -886,8 +888,11 @@ static const char *rna_Scene_statistics_string_get(Scene *scene, return ED_info_statistics_string(bmain, scene, view_layer); } -static void rna_Scene_framelen_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_framelen_update(Main *UNUSED(bmain), + Scene *UNUSED(active_scene), + PointerRNA *ptr) { + Scene *scene = (Scene *)ptr->owner_id; scene->r.framelen = (float)scene->r.framapto / (float)scene->r.images; } @@ -1941,9 +1946,9 @@ static void rna_Scene_use_audio_set(PointerRNA *ptr, bool value) } } -static void rna_Scene_use_audio_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Scene_use_audio_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - DEG_id_tag_update(&scene->id, ID_RECALC_AUDIO_MUTE); + DEG_id_tag_update(ptr->owner_id, ID_RECALC_AUDIO_MUTE); } static int rna_Scene_sync_mode_get(PointerRNA *ptr) @@ -2200,9 +2205,9 @@ static void rna_SceneCamera_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Po } } -static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_SceneSequencer_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - SEQ_cache_cleanup(scene); + SEQ_cache_cleanup((Scene *)ptr->owner_id); } static char *rna_ToolSettings_path(PointerRNA *UNUSED(ptr)) @@ -3157,6 +3162,14 @@ static void rna_def_tool_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Snap UV Element", "Type of element to snap to"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "use_snap_uv_grid_absolute", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "snap_uv_flag", SCE_SNAP_ABS_GRID); + RNA_def_property_ui_text( + prop, + "Absolute Grid Snap", + "Absolute grid alignment while translating (based on the pivot center)"); + RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); /* header redraw */ + prop = RNA_def_property(srna, "snap_target", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "snap_target"); RNA_def_property_enum_items(prop, rna_enum_snap_target_items); @@ -3525,6 +3538,18 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem pivot_points[] = { + {V3D_AROUND_CENTER_BOUNDS, "CENTER", ICON_PIVOT_BOUNDBOX, "Bounding Box Center", ""}, + {V3D_AROUND_CENTER_MEDIAN, "MEDIAN", ICON_PIVOT_MEDIAN, "Median Point", ""}, + {V3D_AROUND_CURSOR, "CURSOR", ICON_PIVOT_CURSOR, "2D Cursor", "Pivot around the 2D cursor"}, + {V3D_AROUND_LOCAL_ORIGINS, + "INDIVIDUAL_ORIGINS", + ICON_PIVOT_INDIVIDUAL, + "Individual Origins", + "Pivot around each selected island's own median point"}, + {0, NULL, 0, NULL, NULL}, + + }; srna = RNA_def_struct(brna, "SequencerToolSettings", NULL); RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path"); RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", ""); @@ -3568,6 +3593,10 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) prop = RNA_def_property(srna, "overlap_mode", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, scale_overlap_modes); RNA_def_property_ui_text(prop, "Overlap Mode", "How to resolve overlap after transformation"); + + prop = RNA_def_property(srna, "pivot_point", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, pivot_points); + RNA_def_property_ui_text(prop, "Pivot Point", "Rotation or scaling pivot point"); } static void rna_def_unified_paint_settings(BlenderRNA *brna) @@ -4137,13 +4166,6 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce prop, "Cryptomatte Levels", "Sets how many unique objects can be distinguished per pixel"); RNA_def_property_ui_range(prop, 2.0, 16.0, 2.0, 0.0); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); - - prop = RNA_def_property(srna, "use_pass_cryptomatte_accurate", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "cryptomatte_flag", VIEW_LAYER_CRYPTOMATTE_ACCURATE); - RNA_def_property_boolean_default(prop, true); - RNA_def_property_ui_text( - prop, "Cryptomatte Accurate", "Generate a more accurate cryptomatte pass"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); } prop = RNA_def_property(srna, "use_solid", PROP_BOOLEAN, PROP_NONE); @@ -4237,6 +4259,16 @@ void rna_def_view_layer_common(BlenderRNA *brna, StructRNA *srna, const bool sce RNA_def_property_clear_flag(prop, PROP_EDITABLE); } + prop = RNA_def_property(srna, "use_pass_position", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "passflag", SCE_PASS_POSITION); + RNA_def_property_ui_text(prop, "Position", "Deliver position pass"); + if (scene) { + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_ViewLayer_pass_update"); + } + else { + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + } + prop = RNA_def_property(srna, "use_pass_normal", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "passflag", SCE_PASS_NORMAL); RNA_def_property_ui_text(prop, "Normal", "Deliver normal pass"); @@ -5108,10 +5140,6 @@ static void rna_def_bake_data(BlenderRNA *brna) RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); /* custom passes flags */ - prop = RNA_def_property(srna, "use_pass_ambient_occlusion", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "pass_filter", R_BAKE_PASS_FILTER_AO); - RNA_def_property_ui_text(prop, "Ambient Occlusion", "Add ambient occlusion contribution"); - prop = RNA_def_property(srna, "use_pass_emit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "pass_filter", R_BAKE_PASS_FILTER_EMIT); RNA_def_property_ui_text(prop, "Emit", "Add emission contribution"); @@ -5920,29 +5948,6 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Resolution %", "Percentage scale for render resolution"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, "rna_SceneSequencer_update"); - prop = RNA_def_property(srna, "tile_x", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "tilex"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_range(prop, 8, 65536); - RNA_def_property_ui_text(prop, "Tile X", "Horizontal tile size to use while rendering"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - - prop = RNA_def_property(srna, "tile_y", PROP_INT, PROP_PIXEL); - RNA_def_property_int_sdna(prop, NULL, "tiley"); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_range(prop, 8, 65536); - RNA_def_property_ui_text(prop, "Tile Y", "Vertical tile size to use while rendering"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - - prop = RNA_def_property(srna, "preview_start_resolution", PROP_INT, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_range(prop, 8, 16384); - RNA_def_property_ui_text(prop, - "Start Resolution", - "Resolution to start rendering preview at, " - "progressively increasing it to the full viewport size"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "preview_pixel_size", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "preview_pixel_size"); RNA_def_property_enum_items(prop, pixel_size_items); @@ -6199,24 +6204,6 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Movie Format", "When true the format is a movie"); - prop = RNA_def_property(srna, "use_save_buffers", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_EXR_TILE_FILE); - RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); - RNA_def_property_ui_text( - prop, - "Save Buffers", - "Save tiles for all RenderLayers and SceneNodes to files in the temp directory " - "(saves memory, required for Full Sample)"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - - prop = RNA_def_property(srna, "use_full_sample", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_FULL_SAMPLE); - RNA_def_property_ui_text(prop, - "Full Sample", - "Save for every anti-aliasing sample the entire RenderLayer results " - "(this solves anti-aliasing issues with compositing)"); - RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - prop = RNA_def_property(srna, "use_lock_interface", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_lock_interface", 1); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 3e292ea89e2..543d69842be 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -154,6 +154,7 @@ static void rna_Scene_ray_cast(Scene *scene, bool ret = ED_transform_snap_object_project_ray_ex(sctx, depsgraph, + NULL, &(const struct SnapObjectParams){ .snap_select = SNAP_ALL, }, diff --git a/source/blender/makesrna/intern/rna_screen.c b/source/blender/makesrna/intern/rna_screen.c index 912f47ba9aa..a8f28f6091c 100644 --- a/source/blender/makesrna/intern/rna_screen.c +++ b/source/blender/makesrna/intern/rna_screen.c @@ -46,6 +46,7 @@ const EnumPropertyItem rna_enum_region_type_items[] = { {RGN_TYPE_EXECUTE, "EXECUTE", 0, "Execute Buttons", ""}, {RGN_TYPE_FOOTER, "FOOTER", 0, "Footer", ""}, {RGN_TYPE_TOOL_HEADER, "TOOL_HEADER", 0, "Tool Header", ""}, + {RGN_TYPE_XR, "XR", 0, "XR", ""}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_sculpt_paint.c b/source/blender/makesrna/intern/rna_sculpt_paint.c index 1da08448c5b..a2db1536f55 100644 --- a/source/blender/makesrna/intern/rna_sculpt_paint.c +++ b/source/blender/makesrna/intern/rna_sculpt_paint.c @@ -130,7 +130,9 @@ const EnumPropertyItem rna_enum_symmetrize_direction_items[] = { static void rna_GPencil_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) { /* mark all grease pencil datablocks of the scene */ - ED_gpencil_tag_scene_gpencil(scene); + if (scene != NULL) { + ED_gpencil_tag_scene_gpencil(scene); + } } const EnumPropertyItem rna_enum_particle_edit_disconnected_hair_brush_items[] = { @@ -501,6 +503,12 @@ static void rna_ImaPaint_stencil_update(bContext *C, PointerRNA *UNUSED(ptr)) } } +static bool rna_ImaPaint_imagetype_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + Image *image = (Image *)value.owner_id; + return image->type != IMA_TYPE_R_RESULT && image->type != IMA_TYPE_COMPOSITE; +} + static void rna_ImaPaint_canvas_update(bContext *C, PointerRNA *UNUSED(ptr)) { Main *bmain = CTX_data_main(C); @@ -1033,17 +1041,20 @@ static void rna_def_image_paint(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE); RNA_def_property_ui_text(prop, "Stencil Image", "Image used as stencil"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_stencil_update"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll"); prop = RNA_def_property(srna, "canvas", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_EDITABLE | PROP_CONTEXT_UPDATE); RNA_def_property_ui_text(prop, "Canvas", "Image used as canvas"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, "rna_ImaPaint_canvas_update"); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll"); prop = RNA_def_property(srna, "clone_image", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "clone"); RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Clone Image", "Image used as clone source"); RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_def_property_pointer_funcs(prop, NULL, NULL, NULL, "rna_ImaPaint_imagetype_poll"); prop = RNA_def_property(srna, "stencil_color", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_range(prop, 0.0, 1.0); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index cd87e4d10c1..cc302c4dd89 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -81,6 +81,20 @@ const EnumPropertyItem rna_enum_sequence_modifier_type_items[] = { {0, NULL, 0, NULL, NULL}, }; +const EnumPropertyItem rna_enum_strip_color_items[] = { + {SEQUENCE_COLOR_NONE, "NONE", ICON_X, "None", "Assign no color tag to the collection"}, + {SEQUENCE_COLOR_01, "COLOR_01", ICON_SEQUENCE_COLOR_01, "Color 01", ""}, + {SEQUENCE_COLOR_02, "COLOR_02", ICON_SEQUENCE_COLOR_02, "Color 02", ""}, + {SEQUENCE_COLOR_03, "COLOR_03", ICON_SEQUENCE_COLOR_03, "Color 03", ""}, + {SEQUENCE_COLOR_04, "COLOR_04", ICON_SEQUENCE_COLOR_04, "Color 04", ""}, + {SEQUENCE_COLOR_05, "COLOR_05", ICON_SEQUENCE_COLOR_05, "Color 05", ""}, + {SEQUENCE_COLOR_06, "COLOR_06", ICON_SEQUENCE_COLOR_06, "Color 06", ""}, + {SEQUENCE_COLOR_07, "COLOR_07", ICON_SEQUENCE_COLOR_07, "Color 07", ""}, + {SEQUENCE_COLOR_08, "COLOR_08", ICON_SEQUENCE_COLOR_08, "Color 08", ""}, + {SEQUENCE_COLOR_09, "COLOR_09", ICON_SEQUENCE_COLOR_09, "Color 09", ""}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "BKE_global.h" @@ -289,7 +303,7 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) Editing *ed = SEQ_editing_get(scene); ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); Sequence *tseq; - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); /* ensure effects are always fit in length to their input */ @@ -298,7 +312,7 @@ static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) */ for (tseq = seqbase->first; tseq; tseq = tseq->next) { if (tseq->seq1 || tseq->seq2 || tseq->seq3) { - SEQ_time_update_sequence(scene, tseq); + SEQ_time_update_sequence(scene, seqbase, tseq); } } @@ -742,13 +756,16 @@ static IDProperty **rna_Sequence_idprops(PointerRNA *ptr) static bool rna_MovieSequence_reload_if_needed(ID *scene_id, Sequence *seq, Main *bmain) { Scene *scene = (Scene *)scene_id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + bool has_reloaded; bool can_produce_frames; SEQ_add_movie_reload_if_needed(bmain, scene, seq, &has_reloaded, &can_produce_frames); if (has_reloaded && can_produce_frames) { - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); SEQ_relations_invalidate_cache_raw(scene, seq); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -840,9 +857,9 @@ static int rna_Sequence_proxy_filepath_length(PointerRNA *ptr) return strlen(path); } -static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Sequence_audio_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) { - DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + DEG_id_tag_update(ptr->owner_id, ID_RECALC_SEQUENCER_STRIPS); } static void rna_Sequence_pan_range( @@ -936,12 +953,15 @@ static void rna_Sequence_filepath_update(Main *bmain, Scene *UNUSED(scene), Poin Scene *scene = (Scene *)ptr->owner_id; Sequence *seq = (Sequence *)(ptr->data); SEQ_add_reload_new_file(bmain, scene, seq, true); - SEQ_time_update_sequence(scene, seq); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + SEQ_time_update_sequence(scene, seqbase, seq); rna_Sequence_invalidate_raw_update(bmain, scene, ptr); } -static void rna_Sequence_sound_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) +static void rna_Sequence_sound_update(Main *bmain, Scene *UNUSED(active_scene), PointerRNA *ptr) { + Scene *scene = (Scene *)ptr->owner_id; DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS | ID_RECALC_AUDIO); DEG_relations_tag_update(bmain); } @@ -999,6 +1019,18 @@ static void rna_Sequence_opacity_set(PointerRNA *ptr, float value) seq->blend_opacity = value * 100.0f; } +static int rna_Sequence_color_tag_get(PointerRNA *ptr) +{ + Sequence *seq = (Sequence *)(ptr->data); + return seq->color_tag; +} + +static void rna_Sequence_color_tag_set(PointerRNA *ptr, int value) +{ + Sequence *seq = (Sequence *)(ptr->data); + seq->color_tag = value; +} + static bool colbalance_seq_cmp_fn(Sequence *seq, void *arg_pt) { SequenceSearchData *data = arg_pt; @@ -1089,13 +1121,13 @@ static void rna_SequenceEditor_overlay_lock_set(PointerRNA *ptr, bool value) } /* convert from abs to relative and back */ - if ((ed->over_flag & SEQ_EDIT_OVERLAY_ABS) == 0 && value) { - ed->over_cfra = scene->r.cfra + ed->over_ofs; - ed->over_flag |= SEQ_EDIT_OVERLAY_ABS; + if ((ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) == 0 && value) { + ed->overlay_frame_abs = scene->r.cfra + ed->overlay_frame_ofs; + ed->overlay_frame_flag |= SEQ_EDIT_OVERLAY_FRAME_ABS; } - else if ((ed->over_flag & SEQ_EDIT_OVERLAY_ABS) && !value) { - ed->over_ofs = ed->over_cfra - scene->r.cfra; - ed->over_flag &= ~SEQ_EDIT_OVERLAY_ABS; + else if ((ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) && !value) { + ed->overlay_frame_ofs = ed->overlay_frame_abs - scene->r.cfra; + ed->overlay_frame_flag &= ~SEQ_EDIT_OVERLAY_FRAME_ABS; } } @@ -1108,11 +1140,11 @@ static int rna_SequenceEditor_overlay_frame_get(PointerRNA *ptr) return scene->r.cfra; } - if (ed->over_flag & SEQ_EDIT_OVERLAY_ABS) { - return ed->over_cfra - scene->r.cfra; + if (ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { + return ed->overlay_frame_abs - scene->r.cfra; } else { - return ed->over_ofs; + return ed->overlay_frame_ofs; } } @@ -1125,11 +1157,11 @@ static void rna_SequenceEditor_overlay_frame_set(PointerRNA *ptr, int value) return; } - if (ed->over_flag & SEQ_EDIT_OVERLAY_ABS) { - ed->over_cfra = (scene->r.cfra + value); + if (ed->overlay_frame_flag & SEQ_EDIT_OVERLAY_FRAME_ABS) { + ed->overlay_frame_abs = (scene->r.cfra + value); } else { - ed->over_ofs = value; + ed->overlay_frame_ofs = value; } } @@ -1442,6 +1474,12 @@ static void rna_def_strip_transform(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Rotation", "Rotate around image center"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + prop = RNA_def_property(srna, "origin", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, NULL, "origin"); + RNA_def_property_ui_text(prop, "Origin", "Origin of image for transformation"); + RNA_def_property_ui_range(prop, 0, 1, 1, 3); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceTransform_update"); + RNA_def_struct_path_func(srna, "rna_SequenceTransform_path"); } @@ -1558,12 +1596,28 @@ static void rna_def_color_balance(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + static const EnumPropertyItem method_items[] = { + {SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN, "LIFT_GAMMA_GAIN", 0, "Lift/Gamma/Gain", ""}, + {SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER, + "OFFSET_POWER_SLOPE", + 0, + "Offset/Power/Slope (ASC-CDL)", + "ASC-CDL standard color correction"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "SequenceColorBalanceData", NULL); RNA_def_struct_ui_text(srna, "Sequence Color Balance Data", "Color balance parameters for a sequence strip and its modifiers"); RNA_def_struct_sdna(srna, "StripColorBalance"); + prop = RNA_def_property(srna, "correction_method", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "method"); + RNA_def_property_enum_items(prop, method_items); + RNA_def_property_ui_text(prop, "Correction Method", ""); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + prop = RNA_def_property(srna, "lift", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_ui_text(prop, "Lift", "Color balance lift (shadows)"); RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); @@ -1582,9 +1636,27 @@ static void rna_def_color_balance(BlenderRNA *brna) RNA_def_property_float_default(prop, 1.0f); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); - prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN); - RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`"); + prop = RNA_def_property(srna, "slope", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_ui_text(prop, "Slope", "Correction for highlights"); + RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "offset", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_ui_text(prop, "Offset", "Correction for entire tonal range"); + RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "power", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_ui_text(prop, "Power", "Correction for midtones"); + RNA_def_property_ui_range(prop, 0, 2, 0.1, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_lift", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_LIFT); + RNA_def_property_ui_text(prop, "Inverse Lift", "Invert the lift color"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); prop = RNA_def_property(srna, "invert_gamma", PROP_BOOLEAN, PROP_NONE); @@ -1592,9 +1664,24 @@ static void rna_def_color_balance(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Inverse Gamma", "Invert the gamma color"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); - prop = RNA_def_property(srna, "invert_lift", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_LIFT); - RNA_def_property_ui_text(prop, "Inverse Lift", "Invert the lift color"); + prop = RNA_def_property(srna, "invert_gain", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_GAIN); + RNA_def_property_ui_text(prop, "Inverse Gain", "Invert the gain color`"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_slope", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_SLOPE); + RNA_def_property_ui_text(prop, "Inverse Slope", "Invert the slope color`"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_offset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_OFFSET); + RNA_def_property_ui_text(prop, "Inverse Offset", "Invert the offset color"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); + + prop = RNA_def_property(srna, "invert_power", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_COLOR_BALANCE_INVERSE_POWER); + RNA_def_property_ui_text(prop, "Inverse Power", "Invert the power color"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_SequenceColorBalance_update"); /* not yet used */ @@ -1931,6 +2018,14 @@ static void rna_def_sequence(BlenderRNA *brna) RNA_def_property_update( prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_invalidate_preprocessed_update"); + prop = RNA_def_property(srna, "color_tag", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "color_tag"); + RNA_def_property_enum_funcs( + prop, "rna_Sequence_color_tag_get", "rna_Sequence_color_tag_set", NULL); + RNA_def_property_enum_items(prop, rna_enum_strip_color_items); + RNA_def_property_ui_text(prop, "Strip Color", "Color tag for a strip"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, NULL); + /* modifiers */ prop = RNA_def_property(srna, "modifiers", PROP_COLLECTION, PROP_NONE); RNA_def_property_struct_type(prop, "SequenceModifier"); @@ -2018,14 +2113,14 @@ static void rna_def_editor(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Active Strip", "Sequencer's active strip"); - prop = RNA_def_property(srna, "show_overlay", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "over_flag", SEQ_EDIT_OVERLAY_SHOW); + prop = RNA_def_property(srna, "show_overlay_frame", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay_frame_flag", SEQ_EDIT_OVERLAY_FRAME_SHOW); RNA_def_property_ui_text( prop, "Show Overlay", "Partial overlay on top of the sequencer with a frame offset"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "use_overlay_lock", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "over_flag", SEQ_EDIT_OVERLAY_ABS); + prop = RNA_def_property(srna, "use_overlay_frame_lock", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay_frame_flag", SEQ_EDIT_OVERLAY_FRAME_ABS); RNA_def_property_ui_text(prop, "Overlay Lock", ""); RNA_def_property_boolean_funcs(prop, NULL, "rna_SequenceEditor_overlay_lock_set"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index a0564d3435b..ec6c4c2f32f 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -64,12 +64,16 @@ static void rna_Sequence_update_rnafunc(ID *id, Sequence *self, bool do_data) { + Scene *scene = (Scene *)id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, self); + if (do_data) { - SEQ_relations_update_changed_seq_and_deps((Scene *)id, self, true, true); + SEQ_relations_update_changed_seq_and_deps(scene, self, true, true); // new_tstripdata(self); /* need 2.6x version of this. */ } - SEQ_time_update_sequence((Scene *)id, self); - SEQ_time_update_sequence_bounds((Scene *)id, self); + + SEQ_time_update_sequence(scene, seqbase, self); } static void rna_Sequence_swap_internal(Sequence *seq_self, @@ -323,8 +327,8 @@ static Sequence *rna_Sequences_new_movie(ID *id, SEQ_add_load_data_init(&load_data, name, file, frame_start, channel); load_data.fit_method = fit_method; load_data.allow_invalid_file = true; - double video_start_offset; - Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &video_start_offset); + double start_offset = -1; + Sequence *seq = SEQ_add_movie_strip(bmain, scene, seqbase, &load_data, &start_offset); DEG_relations_tag_update(bmain); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -586,6 +590,8 @@ static void rna_Sequences_meta_remove( static StripElem *rna_SequenceElements_append(ID *id, Sequence *seq, const char *filename) { Scene *scene = (Scene *)id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); StripElem *se; seq->strip->stripdata = se = MEM_reallocN(seq->strip->stripdata, @@ -594,7 +600,7 @@ static StripElem *rna_SequenceElements_append(ID *id, Sequence *seq, const char BLI_strncpy(se->name, filename, sizeof(se->name)); seq->len++; - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); return se; @@ -603,6 +609,8 @@ static StripElem *rna_SequenceElements_append(ID *id, Sequence *seq, const char static void rna_SequenceElements_pop(ID *id, Sequence *seq, ReportList *reports, int index) { Scene *scene = (Scene *)id; + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); StripElem *new_seq, *se; if (seq->len == 1) { @@ -635,7 +643,7 @@ static void rna_SequenceElements_pop(ID *id, Sequence *seq, ReportList *reports, MEM_freeN(seq->strip->stripdata); seq->strip->stripdata = new_seq; - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); WM_main_add_notifier(NC_SCENE | ND_SEQUENCER, scene); } diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index e3985b26eb0..03976967e9f 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -34,11 +34,15 @@ #include "BKE_node.h" #include "BKE_studiolight.h" +#include "ED_asset.h" #include "ED_spreadsheet.h" #include "ED_text.h" #include "BLI_listbase.h" #include "BLI_math.h" +#include "BLI_string.h" +#include "BLI_sys_types.h" +#include "BLI_uuid.h" #include "DNA_action_types.h" #include "DNA_gpencil_types.h" @@ -856,6 +860,14 @@ static void rna_Space_view2d_sync_set(PointerRNA *ptr, bool value) ARegion *region; area = rna_area_from_space(ptr); /* can be NULL */ + if ((area != NULL) && !UI_view2d_area_supports_sync(area)) { + BKE_reportf(NULL, + RPT_ERROR, + "'show_locked_time' is not supported for the '%s' editor", + area->type->name); + return; + } + region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); if (region) { View2D *v2d = ®ion->v2d; @@ -906,7 +918,7 @@ static void rna_GPencil_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *UN static void rna_SpaceView3D_camera_update(Main *bmain, Scene *scene, PointerRNA *ptr) { View3D *v3d = (View3D *)(ptr->data); - if (v3d->scenelock) { + if (v3d->scenelock && scene != NULL) { wmWindowManager *wm = bmain->wm.first; scene->camera = v3d->camera; @@ -1540,7 +1552,9 @@ static PointerRNA rna_SpaceImageEditor_uvedit_get(PointerRNA *ptr) static void rna_SpaceImageEditor_mode_update(Main *bmain, Scene *scene, PointerRNA *UNUSED(ptr)) { - ED_space_image_paint_update(bmain, bmain->wm.first, scene); + if (scene != NULL) { + ED_space_image_paint_update(bmain, bmain->wm.first, scene); + } } static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, int value) @@ -2326,7 +2340,26 @@ static void rna_Sequencer_view_type_update(Main *UNUSED(bmain), ED_area_tag_refresh(area); } +static char *rna_SpaceSequencerPreviewOverlay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("preview_overlay"); +} + +static char *rna_SpaceSequencerTimelineOverlay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("timeline_overlay"); +} + /* Space Node Editor */ +static PointerRNA rna_SpaceNode_overlay_get(PointerRNA *ptr) +{ + return rna_pointer_inherit_refine(ptr, &RNA_SpaceNodeOverlay, ptr->data); +} + +static char *rna_SpaceNodeOverlay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("overlay"); +} static void rna_SpaceNodeEditor_node_tree_set(PointerRNA *ptr, const PointerRNA value, @@ -2594,22 +2627,49 @@ static void rna_FileAssetSelectParams_asset_library_set(PointerRNA *ptr, int val params->asset_library_ref = ED_asset_library_reference_from_enum_value(value); } -static void rna_FileAssetSelectParams_asset_category_set(PointerRNA *ptr, uint64_t value) +static PointerRNA rna_FileAssetSelectParams_filter_id_get(PointerRNA *ptr) { - FileSelectParams *params = ptr->data; - params->filter_id = value; + return rna_pointer_inherit_refine(ptr, &RNA_FileAssetSelectIDFilter, ptr->data); } -static uint64_t rna_FileAssetSelectParams_asset_category_get(PointerRNA *ptr) +static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) { - FileSelectParams *params = ptr->data; - return params->filter_id; + const FileDirEntry *entry = ptr->data; + + /* Note that the owning ID of the RNA pointer (`ptr->owner_id`) has to be set carefully: + * Local IDs (`entry->id`) own their asset metadata themselves. Asset metadata from other blend + * files are owned by the file browser (`entry`). Only if this is set correctly, we can tell from + * the metadata RNA pointer if the metadata is stored locally and can thus be edited or not. */ + + if (entry->id) { + PointerRNA id_ptr; + RNA_id_pointer_create(entry->id, &id_ptr); + return rna_pointer_inherit_refine(&id_ptr, &RNA_AssetMetaData, entry->asset_data); + } + + return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data); +} + +static int rna_FileBrowser_FileSelectEntry_name_editable(PointerRNA *ptr, const char **r_info) +{ + const FileDirEntry *entry = ptr->data; + + /* This actually always returns 0 (the name is never editable) but we want to get a disabled + * message returned to `r_info` in some cases. */ + + if (entry->asset_data) { + PointerRNA asset_data_ptr = rna_FileBrowser_FileSelectEntry_asset_data_get(ptr); + /* Get disabled hint from asset metadata polling. */ + rna_AssetMetaData_editable(&asset_data_ptr, r_info); + } + + return 0; } static void rna_FileBrowser_FileSelectEntry_name_get(PointerRNA *ptr, char *value) { const FileDirEntry *entry = ptr->data; - strcpy(value, entry->name); + BLI_strncpy_utf8(value, entry->name, strlen(entry->name) + 1); } static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) @@ -2621,7 +2681,7 @@ static int rna_FileBrowser_FileSelectEntry_name_length(PointerRNA *ptr) static void rna_FileBrowser_FileSelectEntry_relative_path_get(PointerRNA *ptr, char *value) { const FileDirEntry *entry = ptr->data; - strcpy(value, entry->relpath); + BLI_strncpy_utf8(value, entry->relpath, strlen(entry->relpath) + 1); } static int rna_FileBrowser_FileSelectEntry_relative_path_length(PointerRNA *ptr) @@ -2662,12 +2722,6 @@ static int rna_FileBrowser_FileSelectEntry_preview_icon_id_get(PointerRNA *ptr) return ED_file_icon(entry); } -static PointerRNA rna_FileBrowser_FileSelectEntry_asset_data_get(PointerRNA *ptr) -{ - const FileDirEntry *entry = ptr->data; - return rna_pointer_inherit_refine(ptr, &RNA_AssetMetaData, entry->asset_data); -} - static StructRNA *rna_FileBrowser_params_typef(PointerRNA *ptr) { SpaceFile *sfile = ptr->data; @@ -3020,12 +3074,34 @@ static void rna_SpaceSpreadsheet_geometry_component_type_update(Main *UNUSED(bma PointerRNA *ptr) { SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)ptr->data; - if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_POINT_CLOUD) { - sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; - } - if (sspreadsheet->geometry_component_type == GEO_COMPONENT_TYPE_CURVE && - !ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { - sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + switch (sspreadsheet->geometry_component_type) { + case GEO_COMPONENT_TYPE_MESH: { + if (!ELEM(sspreadsheet->attribute_domain, + ATTR_DOMAIN_POINT, + ATTR_DOMAIN_EDGE, + ATTR_DOMAIN_FACE, + ATTR_DOMAIN_CORNER)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + if (!ELEM(sspreadsheet->attribute_domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + sspreadsheet->attribute_domain = ATTR_DOMAIN_POINT; + } + break; + } } } @@ -3143,6 +3219,17 @@ static void rna_SpaceSpreadsheet_context_path_guess(SpaceSpreadsheet *sspreadshe WM_main_add_notifier(NC_SPACE | ND_SPACE_SPREADSHEET, NULL); } +static void rna_FileAssetSelectParams_catalog_id_get(PointerRNA *ptr, char *value) +{ + const FileAssetSelectParams *params = ptr->data; + BLI_uuid_format(value, params->catalog_id); +} + +static int rna_FileAssetSelectParams_catalog_id_length(PointerRNA *UNUSED(ptr)) +{ + return UUID_STRING_LEN - 1; +} + #else static const EnumPropertyItem dt_uv_items[] = { @@ -3433,6 +3520,19 @@ static void rna_def_space_image_uv(BlenderRNA *brna) prop, "Tile Grid Shape", "How many tiles will be shown in the background"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "use_custom_grid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SI_CUSTOM_GRID); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Custom Grid", "Use a grid with a user-defined number of steps"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + + prop = RNA_def_property(srna, "custom_grid_subdivisions", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "custom_grid_subdiv"); + RNA_def_property_range(prop, 1, 5000); + RNA_def_property_ui_text( + prop, "Dynamic Grid Size", "Number of grid units in UV space that make one UV Unit"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "uv_opacity", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "uv_opacity"); RNA_def_property_range(prop, 0.0f, 1.0f); @@ -5329,6 +5429,129 @@ static void rna_def_space_image(BlenderRNA *brna) rna_def_space_mask_info(srna, NC_SPACE | ND_SPACE_IMAGE, "rna_SpaceImageEditor_mask_set"); } +static void rna_def_space_sequencer_preview_overlay(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SequencerPreviewOverlay", NULL); + RNA_def_struct_sdna(srna, "SequencerPreviewOverlay"); + RNA_def_struct_nested(brna, srna, "SpaceSequenceEditor"); + RNA_def_struct_path_func(srna, "rna_SpaceSequencerPreviewOverlay_path"); + RNA_def_struct_ui_text(srna, "Preview Overlay Settings", ""); + + prop = RNA_def_property(srna, "show_safe_areas", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_SAFE_MARGINS); + RNA_def_property_ui_text( + prop, "Safe Areas", "Show TV title safe and action safe areas in preview"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_SAFE_CENTER); + RNA_def_property_ui_text( + prop, "Center-Cut Safe Areas", "Show safe areas to fit content in a different aspect ratio"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_metadata", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_METADATA); + RNA_def_property_ui_text(prop, "Show Metadata", "Show metadata of first visible strip"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_GPENCIL); + RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_image_outline", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_OUTLINE_SELECTED); + RNA_def_property_ui_text(prop, "Image Outline", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_cursor", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_PREVIEW_SHOW_2D_CURSOR); + RNA_def_property_ui_text(prop, "2D cursor", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); +} + +static void rna_def_space_sequencer_timeline_overlay(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SequencerTimelineOverlay", NULL); + RNA_def_struct_sdna(srna, "SequencerTimelineOverlay"); + RNA_def_struct_nested(brna, srna, "SpaceSequenceEditor"); + RNA_def_struct_path_func(srna, "rna_SpaceSequencerTimelineOverlay_path"); + RNA_def_struct_ui_text(srna, "Timeline Overlay Settings", ""); + + static const EnumPropertyItem waveform_type_display_items[] = { + {SEQ_TIMELINE_NO_WAVEFORMS, + "NO_WAVEFORMS", + 0, + "Waveforms Off", + "Don't display waveforms for any sound strips"}, + {SEQ_TIMELINE_ALL_WAVEFORMS, + "ALL_WAVEFORMS", + 0, + "Waveforms On", + "Display waveforms for all sound strips"}, + {0, + "DEFAULT_WAVEFORMS", + 0, + "Use Strip Option", + "Display waveforms depending on strip setting"}, + {0, NULL, 0, NULL, NULL}, + }; + + prop = RNA_def_property(srna, "waveform_display_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); + RNA_def_property_enum_items(prop, waveform_type_display_items); + RNA_def_property_ui_text(prop, "Waveform Display", "How Waveforms are displayed"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_fcurves", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_FCURVES); + RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_name", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_NAME); + RNA_def_property_ui_text(prop, "Show Name", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_source", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_SOURCE); + RNA_def_property_ui_text( + prop, "Show Source", "Display path to source file, or name of source datablock"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_duration", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_DURATION); + RNA_def_property_ui_text(prop, "Show Duration", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_grid", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_GRID); + RNA_def_property_ui_text(prop, "Show Grid", "Show vertical grid lines"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_offset", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_OFFSETS); + RNA_def_property_ui_text(prop, "Show Offsets", "Display strip in/out offsets"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_thumbnails", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_THUMBNAILS); + RNA_def_property_ui_text(prop, "Show Thumbnails", "Show strip thumbnails"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_strip_tag_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_TIMELINE_SHOW_STRIP_COLOR_TAG); + RNA_def_property_ui_text( + prop, "Show Color Tags", "Display the strip color tags in the sequencer"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); +} + static void rna_def_space_sequencer(BlenderRNA *brna) { StructRNA *srna; @@ -5352,10 +5575,10 @@ static void rna_def_space_sequencer(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem overlay_type_items[] = { - {SEQ_DRAW_OVERLAY_RECT, "RECTANGLE", 0, "Rectangle", "Show rectangle area overlay"}, - {SEQ_DRAW_OVERLAY_REFERENCE, "REFERENCE", 0, "Reference", "Show reference frame only"}, - {SEQ_DRAW_OVERLAY_CURRENT, "CURRENT", 0, "Current", "Show current frame only"}, + static const EnumPropertyItem overlay_frame_type_items[] = { + {SEQ_OVERLAY_FRAME_TYPE_RECT, "RECTANGLE", 0, "Rectangle", "Show rectangle area overlay"}, + {SEQ_OVERLAY_FRAME_TYPE_REFERENCE, "REFERENCE", 0, "Reference", "Show reference frame only"}, + {SEQ_OVERLAY_FRAME_TYPE_CURRENT, "CURRENT", 0, "Current", "Show current frame only"}, {0, NULL, 0, NULL, NULL}, }; @@ -5369,25 +5592,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; - static const EnumPropertyItem waveform_type_display_items[] = { - {SEQ_NO_WAVEFORMS, - "NO_WAVEFORMS", - 0, - "Waveforms Off", - "Don't display waveforms for any sound strips"}, - {SEQ_ALL_WAVEFORMS, - "ALL_WAVEFORMS", - 0, - "Waveforms On", - "Display waveforms for all sound strips"}, - {0, - "DEFAULT_WAVEFORMS", - 0, - "Use Strip Option", - "Display waveforms depending on strip setting"}, - {0, NULL, 0, NULL, NULL}, - }; - srna = RNA_def_struct(brna, "SpaceSequenceEditor", "Space"); RNA_def_struct_sdna(srna, "SpaceSeq"); RNA_def_struct_ui_text(srna, "Space Sequence Editor", "Sequence editor space data"); @@ -5428,23 +5632,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Separate Colors", "Separate color channels in preview"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_safe_areas", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_SAFE_MARGINS); - RNA_def_property_ui_text( - prop, "Safe Areas", "Show TV title safe and action safe areas in preview"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_safe_center", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_SAFE_CENTER); - RNA_def_property_ui_text( - prop, "Center-Cut Safe Areas", "Show safe areas to fit content in a different aspect ratio"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_metadata", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_METADATA); - RNA_def_property_ui_text(prop, "Show Metadata", "Show metadata of first visible strip"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_seconds", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_negative_sdna(prop, NULL, "flag", SEQ_DRAWFRAMES); RNA_def_property_ui_text(prop, "Show Seconds", "Show timing in seconds not frames"); @@ -5458,11 +5645,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) "If any exists, show markers in a separate row at the bottom of the editor"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_annotation", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_GPENCIL); - RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "display_channel", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "chanshown"); RNA_def_property_ui_text( @@ -5478,12 +5660,6 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Display Channels", "Channels of the preview to display"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, "rna_SequenceEditor_update_cache"); - prop = RNA_def_property(srna, "waveform_display_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); - RNA_def_property_enum_items(prop, waveform_type_display_items); - RNA_def_property_ui_text(prop, "Waveform Display", "How Waveforms are displayed"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "use_zoom_to_fit", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_ZOOM_TO_FIT); RNA_def_property_ui_text( @@ -5522,9 +5698,9 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Grease Pencil", "Grease Pencil data for this Preview region"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "overlay_type", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "overlay_type"); - RNA_def_property_enum_items(prop, overlay_type_items); + prop = RNA_def_property(srna, "overlay_frame_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "overlay_frame_type"); + RNA_def_property_enum_items(prop, overlay_frame_type_items); RNA_def_property_ui_text(prop, "Overlay Type", "Overlay display method"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); @@ -5533,45 +5709,58 @@ static void rna_def_space_sequencer(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Use Backdrop", "Display result under strips"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_strip_offset", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_OFFSET_EXT); - RNA_def_property_ui_text(prop, "Show Offsets", "Display strip in/out offsets"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_fcurves", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_FCURVES); - RNA_def_property_ui_text(prop, "Show F-Curves", "Display strip opacity/volume curve"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_overlay", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_OVERLAY); - RNA_def_property_ui_text(prop, "Show Overlay", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_name", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_NAME); - RNA_def_property_ui_text(prop, "Show Name", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_source", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_SOURCE); - RNA_def_property_ui_text( - prop, "Show Source", "Display path to source file, or name of source datablock"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - - prop = RNA_def_property(srna, "show_strip_duration", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_STRIP_DURATION); - RNA_def_property_ui_text(prop, "Show Duration", ""); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_transform_preview", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw_flag", SEQ_DRAW_TRANSFORM_PREVIEW); RNA_def_property_ui_text(prop, "Transform Preview", "Show preview of the transformed frames"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); - prop = RNA_def_property(srna, "show_grid", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_GRID); - RNA_def_property_ui_text(prop, "Show Grid", "Show vertical grid lines"); + /* Gizmo toggles. */ + prop = RNA_def_property(srna, "show_gizmo", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE); + RNA_def_property_ui_text(prop, "Show Gizmo", "Show gizmos of all types"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_gizmo_navigate", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE_NAVIGATE); + RNA_def_property_ui_text(prop, "Navigate Gizmo", "Viewport navigation gizmo"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_gizmo_context", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE_CONTEXT); + RNA_def_property_ui_text(prop, "Context Gizmo", "Context sensitive gizmos for the active item"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "show_gizmo_tool", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "gizmo_flag", SEQ_GIZMO_HIDE_TOOL); + RNA_def_property_ui_text(prop, "Tool Gizmo", "Active tool gizmo"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + /* Overlay settings. */ + prop = RNA_def_property(srna, "show_overlays", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_SHOW_OVERLAY); + RNA_def_property_ui_text(prop, "Show Overlay", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + prop = RNA_def_property(srna, "preview_overlay", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SequencerPreviewOverlay"); + RNA_def_property_pointer_sdna(prop, NULL, "preview_overlay"); + RNA_def_property_ui_text(prop, "Preview Overlay Settings", "Settings for display of overlays"); + + prop = RNA_def_property(srna, "timeline_overlay", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SequencerTimelineOverlay"); + RNA_def_property_pointer_sdna(prop, NULL, "timeline_overlay"); + RNA_def_property_ui_text(prop, "Timeline Overlay Settings", "Settings for display of overlays"); + + rna_def_space_sequencer_preview_overlay(brna); + rna_def_space_sequencer_timeline_overlay(brna); + + /* transform */ + prop = RNA_def_property(srna, "cursor_location", PROP_FLOAT, PROP_XYZ); + RNA_def_property_float_sdna(prop, NULL, "cursor"); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "2D Cursor Location", "2D cursor location for this view"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_SEQUENCER, NULL); } @@ -6192,6 +6381,40 @@ static void rna_def_fileselect_idfilter(BlenderRNA *brna) } } +/* Filter for datablock types in the Asset Browser. */ +static void rna_def_fileselect_asset_idfilter(BlenderRNA *brna) +{ + StructRNA *srna = RNA_def_struct(brna, "FileAssetSelectIDFilter", NULL); + RNA_def_struct_sdna(srna, "FileSelectParams"); + RNA_def_struct_nested(brna, srna, "FileSelectParams"); + RNA_def_struct_ui_text(srna, + "File Select Asset Filter", + "Which asset types to show/hide, when browsing an asset library"); + + static char experimental_prop_names[INDEX_ID_MAX][MAX_NAME]; + + for (uint i = 0; rna_enum_id_type_filter_items[i].identifier; i++) { + const struct IDFilterEnumPropertyItem *item = &rna_enum_id_type_filter_items[i]; + const bool is_experimental = (ED_ASSET_TYPE_IDS_NON_EXPERIMENTAL_FLAGS & item->flag) == 0; + + const char *identifier = rna_enum_id_type_filter_items[i].identifier; + if (is_experimental) { + /* Create name for experimental property and store in static buffer. */ + snprintf(experimental_prop_names[i], + ARRAY_SIZE(experimental_prop_names[i]), + "experimental_%s", + identifier); + identifier = experimental_prop_names[i]; + } + + PropertyRNA *prop = RNA_def_property(srna, identifier, PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "filter_id", item->flag); + RNA_def_property_ui_text(prop, item->name, item->description); + RNA_def_property_ui_icon(prop, item->icon, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); + } +} + static void rna_def_fileselect_entry(BlenderRNA *brna) { PropertyRNA *prop; @@ -6199,16 +6422,17 @@ static void rna_def_fileselect_entry(BlenderRNA *brna) RNA_def_struct_sdna(srna, "FileDirEntry"); RNA_def_struct_ui_text(srna, "File Select Entry", "A file viewable in the File Browser"); - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_FILENAME); + RNA_def_property_editable_func(prop, "rna_FileBrowser_FileSelectEntry_name_editable"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_string_funcs(prop, "rna_FileBrowser_FileSelectEntry_name_get", "rna_FileBrowser_FileSelectEntry_name_length", NULL); RNA_def_property_ui_text(prop, "Name", ""); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_struct_name_property(srna, prop); - prop = RNA_def_property(srna, "relative_path", PROP_STRING, PROP_NONE); + prop = RNA_def_property(srna, "relative_path", PROP_STRING, PROP_FILEPATH); RNA_def_property_string_funcs(prop, "rna_FileBrowser_FileSelectEntry_relative_path_get", "rna_FileBrowser_FileSelectEntry_relative_path_length", @@ -6477,47 +6701,6 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; - /* XXX copied from rna_enum_id_type_filter_items. */ - static const EnumPropertyItem asset_category_items[] = { - {FILTER_ID_SCE, "SCENES", ICON_SCENE_DATA, "Scenes", "Show scenes"}, - {FILTER_ID_AC, "ANIMATIONS", ICON_ANIM_DATA, "Animations", "Show animation data"}, - {FILTER_ID_OB | FILTER_ID_GR, - "OBJECTS_AND_COLLECTIONS", - ICON_GROUP, - "Objects & Collections", - "Show objects and collections"}, - {FILTER_ID_AR | FILTER_ID_CU | FILTER_ID_LT | FILTER_ID_MB | FILTER_ID_ME - /* XXX avoid warning */ - // | FILTER_ID_HA | FILTER_ID_PT | FILTER_ID_VO - , - "GEOMETRY", - ICON_MESH_DATA, - "Geometry", - "Show meshes, curves, lattice, armatures and metaballs data"}, - {FILTER_ID_LS | FILTER_ID_MA | FILTER_ID_NT | FILTER_ID_TE, - "SHADING", - ICON_MATERIAL_DATA, - "Shading", - "Show materials, nodetrees, textures and Freestyle's linestyles"}, - {FILTER_ID_IM | FILTER_ID_MC | FILTER_ID_MSK | FILTER_ID_SO, - "IMAGES_AND_SOUNDS", - ICON_IMAGE_DATA, - "Images & Sounds", - "Show images, movie clips, sounds and masks"}, - {FILTER_ID_CA | FILTER_ID_LA | FILTER_ID_LP | FILTER_ID_SPK | FILTER_ID_WO, - "ENVIRONMENTS", - ICON_WORLD_DATA, - "Environment", - "Show worlds, lights, cameras and speakers"}, - {FILTER_ID_BR | FILTER_ID_GD | FILTER_ID_PA | FILTER_ID_PAL | FILTER_ID_PC | FILTER_ID_TXT | - FILTER_ID_VF | FILTER_ID_CF | FILTER_ID_WS, - "MISC", - ICON_GREASEPENCIL, - "Miscellaneous", - "Show other data types"}, - {0, NULL, 0, NULL, NULL}, - }; - static const EnumPropertyItem asset_import_type_items[] = { {FILE_ASSET_IMPORT_LINK, "LINK", 0, "Link", "Import the assets as linked data-block"}, {FILE_ASSET_IMPORT_APPEND, @@ -6525,6 +6708,14 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) 0, "Append", "Import the assets as copied data-block, with no link to the original asset data-block"}, + {FILE_ASSET_IMPORT_APPEND_REUSE, + "APPEND_REUSE", + 0, + "Append (Reuse Data)", + "Import the assets as copied data-block while avoiding multiple copies of nested, " + "typically heavy data. For example the textures of a material asset, or the mesh of an " + "object asset, don't have to be copied every time this asset is imported. The instances of " + "the asset share the data instead"}, {0, NULL, 0, NULL, NULL}, }; @@ -6538,14 +6729,22 @@ static void rna_def_fileselect_asset_params(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Asset Library", ""); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_PARAMS, NULL); - prop = RNA_def_property(srna, "asset_category", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_items(prop, asset_category_items); - RNA_def_property_enum_funcs(prop, - "rna_FileAssetSelectParams_asset_category_get", - "rna_FileAssetSelectParams_asset_category_set", - NULL); - RNA_def_property_ui_text(prop, "Asset Category", "Determine which kind of assets to display"); - RNA_def_property_update(prop, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + prop = RNA_def_property(srna, "catalog_id", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, + "rna_FileAssetSelectParams_catalog_id_get", + "rna_FileAssetSelectParams_catalog_id_length", + NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Catalog UUID", "The UUID of the catalog shown in the browser"); + + prop = RNA_def_property(srna, "filter_asset_id", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "FileAssetSelectIDFilter"); + RNA_def_property_pointer_funcs( + prop, "rna_FileAssetSelectParams_filter_id_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, + "Filter Asset Types", + "Which asset types to show/hide, when browsing an asset library"); prop = RNA_def_property(srna, "import_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, asset_import_type_items); @@ -6887,6 +7086,32 @@ static void rna_def_space_node_path_api(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_function_flag(func, FUNC_USE_CONTEXT); } +static void rna_def_space_node_overlay(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SpaceNodeOverlay", NULL); + RNA_def_struct_sdna(srna, "SpaceNode"); + RNA_def_struct_nested(brna, srna, "SpaceNodeEditor"); + RNA_def_struct_path_func(srna, "rna_SpaceNodeOverlay_path"); + RNA_def_struct_ui_text( + srna, "Overlay Settings", "Settings for display of overlays in the Node Editor"); + + prop = RNA_def_property(srna, "show_overlays", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", SN_OVERLAY_SHOW_OVERLAYS); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text(prop, "Show Overlays", "Display overlays like colored or dashed wires"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); + + prop = RNA_def_property(srna, "show_wire_color", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "overlay.flag", SN_OVERLAY_SHOW_WIRE_COLORS); + RNA_def_property_boolean_default(prop, true); + RNA_def_property_ui_text( + prop, "Show Wire Colors", "Color node links based on their connected sockets"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, NULL); +} + static void rna_def_space_node(BlenderRNA *brna) { StructRNA *srna; @@ -7070,6 +7295,15 @@ static void rna_def_space_node(BlenderRNA *brna) prop, "Auto-offset Direction", "Direction to offset nodes on insertion"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE_VIEW, NULL); + /* Overlays */ + prop = RNA_def_property(srna, "overlay", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "SpaceNodeOverlay"); + RNA_def_property_pointer_funcs(prop, "rna_SpaceNode_overlay_get", NULL, NULL, NULL); + RNA_def_property_ui_text( + prop, "Overlay Settings", "Settings for display of overlays in the Node Editor"); + + rna_def_space_node_overlay(brna); RNA_api_space_node(srna); } @@ -7719,6 +7953,7 @@ void RNA_def_space(BlenderRNA *brna) rna_def_fileselect_params(brna); rna_def_fileselect_asset_params(brna); rna_def_fileselect_idfilter(brna); + rna_def_fileselect_asset_idfilter(brna); rna_def_filemenu_entry(brna); rna_def_space_filebrowser(brna); rna_def_space_outliner(brna); diff --git a/source/blender/makesrna/intern/rna_space_api.c b/source/blender/makesrna/intern/rna_space_api.c index 205a88edf84..c5569683c9c 100644 --- a/source/blender/makesrna/intern/rna_space_api.c +++ b/source/blender/makesrna/intern/rna_space_api.c @@ -122,7 +122,8 @@ void RNA_api_space_filebrowser(StructRNA *srna) PropertyRNA *parm; func = RNA_def_function(srna, "activate_asset_by_id", "ED_fileselect_activate_by_id"); - RNA_def_function_ui_description(func, "Activate the asset entry that represents the given ID"); + RNA_def_function_ui_description( + func, "Activate and select the asset entry that represents the given ID"); parm = RNA_def_property(func, "id_to_activate", PROP_POINTER, PROP_NONE); RNA_def_property_struct_type(parm, "ID"); @@ -134,6 +135,18 @@ void RNA_api_space_filebrowser(StructRNA *srna) 0, "", "Whether to activate the ID immediately (false) or after the file browser refreshes (true)"); + + /* Select file by relative path. */ + func = RNA_def_function( + srna, "activate_file_by_relative_path", "ED_fileselect_activate_by_relpath"); + RNA_def_function_ui_description(func, + "Set active file and add to selection based on relative path to " + "current File Browser directory"); + RNA_def_property(func, "relative_path", PROP_STRING, PROP_FILEPATH); + + /* Deselect all files. */ + func = RNA_def_function(srna, "deselect_all", "ED_fileselect_deselect_all"); + RNA_def_function_ui_description(func, "Deselect all files"); } #endif diff --git a/source/blender/makesrna/intern/rna_tracking.c b/source/blender/makesrna/intern/rna_tracking.c index 336359a9dc0..c81588aa8b5 100644 --- a/source/blender/makesrna/intern/rna_tracking.c +++ b/source/blender/makesrna/intern/rna_tracking.c @@ -2437,6 +2437,8 @@ static void rna_def_trackingDopesheet(BlenderRNA *brna) 0, "Average Error", "Sort channels by average reprojection error of tracks after solve"}, + {TRACKING_DOPE_SORT_START, "START", 0, "Start Frame", "Sort channels by first frame number"}, + {TRACKING_DOPE_SORT_END, "END", 0, "End Frame", "Sort channels by last frame number"}, {0, NULL, 0, NULL, NULL}, }; diff --git a/source/blender/makesrna/intern/rna_ui.c b/source/blender/makesrna/intern/rna_ui.c index c506a533032..c73599c19ac 100644 --- a/source/blender/makesrna/intern/rna_ui.c +++ b/source/blender/makesrna/intern/rna_ui.c @@ -1373,7 +1373,6 @@ static void rna_def_panel(BlenderRNA *brna) 0, "Expand Header Layout", "Allow buttons in the header to stretch and shrink to fill the entire layout width"}, - {PANEL_TYPE_DRAW_BOX, "DRAW_BOX", 0, "Box Style", "Display panel with the box widget theme"}, {0, NULL, 0, NULL, NULL}, }; @@ -1452,6 +1451,11 @@ static void rna_def_panel(BlenderRNA *brna) RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); RNA_def_property_string_default(prop, BLT_I18NCONTEXT_DEFAULT_BPYRNA); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text(prop, + "", + "Specific translation context, only define when the label needs to be " + "disambiguated from others using the exact same label"); + RNA_define_verify_sdna(true); prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE); @@ -1461,14 +1465,21 @@ static void rna_def_panel(BlenderRNA *brna) // RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); RNA_def_property_clear_flag(prop, PROP_NEVER_NULL); /* check for NULL */ + RNA_def_property_ui_text(prop, "", "The panel tooltip"); prop = RNA_def_property(srna, "bl_category", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->category"); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, + "", + "The category (tab) in which the panel will be displayed, when applicable"); prop = RNA_def_property(srna, "bl_owner_id", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->owner_id"); RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, "", "The ID owning the data displayed in the panel, if any"); prop = RNA_def_property(srna, "bl_space_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "type->space_type"); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index 84f30e513d5..ab01c722c55 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -1560,6 +1560,13 @@ static void rna_def_userdef_theme_ui(BlenderRNA *brna) prop, "Text Cursor", "Color of the interface widgets text insertion cursor (caret)"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + prop = RNA_def_property(srna, "panel_roundness", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_ui_text( + prop, "Panel Roundness", "Roundness of the corners of panels and sub-panels"); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 0.4f); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + /* Transparent Grid */ prop = RNA_def_property(srna, "transparent_checker_primary", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "transparent_checker_primary"); @@ -2837,7 +2844,7 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "wire_select", PROP_FLOAT, PROP_COLOR_GAMMA); RNA_def_property_float_sdna(prop, NULL, "edge_select"); - RNA_def_property_array(prop, 3); + RNA_def_property_array(prop, 4); RNA_def_property_ui_text(prop, "Wire Select", ""); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); @@ -2904,10 +2911,16 @@ static void rna_def_userdef_theme_space_node(BlenderRNA *brna) prop = RNA_def_property(srna, "grid_levels", PROP_INT, PROP_NONE); RNA_def_property_int_sdna(prop, NULL, "grid_levels"); - RNA_def_property_int_default(prop, 2); - RNA_def_property_range(prop, 0, 2); + RNA_def_property_int_default(prop, 7); + RNA_def_property_range(prop, 0, 9); RNA_def_property_ui_text( - prop, "Grid Levels", "Amount of grid lines displayed in the background"); + prop, "Grid Levels", "Number of subdivisions for the dot grid displayed in the background"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); + + prop = RNA_def_property(srna, "dash_alpha", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_range(prop, 0.0f, 1.0f); + RNA_def_property_float_default(prop, 0.5f); + RNA_def_property_ui_text(prop, "Dashed Lines Opacity", "Opacity for the dashed lines in wires"); RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); prop = RNA_def_property(srna, "input_node", PROP_FLOAT, PROP_COLOR_GAMMA); @@ -3680,6 +3693,23 @@ static void rna_def_userdef_theme_collection_color(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); } +static void rna_def_userdef_theme_strip_color(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ThemeStripColor", NULL); + RNA_def_struct_sdna(srna, "ThemeStripColor"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Theme Strip Color", "Theme settings for strip colors"); + + prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA); + RNA_def_property_float_sdna(prop, NULL, "color"); + RNA_def_property_array(prop, 3); + RNA_def_property_ui_text(prop, "Color", "Strip Color"); + RNA_def_property_update(prop, 0, "rna_userdef_theme_update"); +} + static void rna_def_userdef_theme_space_clip(BlenderRNA *brna) { StructRNA *srna; @@ -4029,6 +4059,12 @@ static void rna_def_userdef_themes(BlenderRNA *brna) RNA_def_property_collection_sdna(prop, NULL, "collection_color", ""); RNA_def_property_struct_type(prop, "ThemeCollectionColor"); RNA_def_property_ui_text(prop, "Collection Color", ""); + + prop = RNA_def_property(srna, "strip_color", PROP_COLLECTION, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_collection_sdna(prop, NULL, "strip_color", ""); + RNA_def_property_struct_type(prop, "ThemeStripColor"); + RNA_def_property_ui_text(prop, "Strip Color", ""); } static void rna_def_userdef_addon(BlenderRNA *brna) @@ -4272,6 +4308,7 @@ static void rna_def_userdef_dothemes(BlenderRNA *brna) rna_def_userdef_theme_space_spreadsheet(brna); rna_def_userdef_theme_colorset(brna); rna_def_userdef_theme_collection_color(brna); + rna_def_userdef_theme_strip_color(brna); rna_def_userdef_themes(brna); } @@ -5144,6 +5181,11 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Duplicate Curve", "Causes curve data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_lattice", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_LATTICE); + RNA_def_property_ui_text( + prop, "Duplicate Lattice", "Causes lattice data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_text", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_FONT); RNA_def_property_ui_text( @@ -5159,6 +5201,16 @@ static void rna_def_userdef_edit(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Duplicate Armature", "Causes armature data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_camera", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_CAMERA); + RNA_def_property_ui_text( + prop, "Duplicate Camera", "Causes camera data to be duplicated with the object"); + + prop = RNA_def_property(srna, "use_duplicate_speaker", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_SPEAKER); + RNA_def_property_ui_text( + prop, "Duplicate Speaker", "Causes speaker data to be duplicated with the object"); + prop = RNA_def_property(srna, "use_duplicate_light", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "dupflag", USER_DUP_LAMP); RNA_def_property_ui_text( @@ -6285,6 +6337,13 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) "Enable library overrides automatic resync detection and process on file load. Disable when " "dealing with older .blend files that need manual Resync (Enforce) handling"); + prop = RNA_def_property(srna, "proxy_to_override_auto_conversion", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "no_proxy_to_override_conversion", 1); + RNA_def_property_ui_text( + prop, + "Proxy to Override Auto Conversion", + "Enable automatic conversion of proxies to library overrides on file load"); + prop = RNA_def_property(srna, "use_new_point_cloud_type", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "use_new_point_cloud_type", 1); RNA_def_property_ui_text( @@ -6324,9 +6383,10 @@ static void rna_def_userdef_experimental(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Override Templates", "Enable library override template in the python API"); - prop = RNA_def_property(srna, "use_geometry_nodes_fields", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_fields", 1); - RNA_def_property_ui_text(prop, "Geometry Nodes Fields", "Enable field nodes in geometry nodes"); + prop = RNA_def_property(srna, "use_geometry_nodes_legacy", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "use_geometry_nodes_legacy", 1); + RNA_def_property_ui_text( + prop, "Geometry Nodes Legacy", "Enable legacy geometry nodes in the menu"); } static void rna_def_userdef_addon_collection(BlenderRNA *brna, PropertyRNA *cprop) diff --git a/source/blender/makesrna/intern/rna_vfont.c b/source/blender/makesrna/intern/rna_vfont.c index 214a32372dd..f5f7742d753 100644 --- a/source/blender/makesrna/intern/rna_vfont.c +++ b/source/blender/makesrna/intern/rna_vfont.c @@ -30,7 +30,7 @@ #ifdef RNA_RUNTIME -# include "BKE_font.h" +# include "BKE_vfont.h" # include "DNA_object_types.h" # include "DEG_depsgraph.h" diff --git a/source/blender/makesrna/intern/rna_volume.c b/source/blender/makesrna/intern/rna_volume.c index 8ed53c9f70f..3100c1195f4 100644 --- a/source/blender/makesrna/intern/rna_volume.c +++ b/source/blender/makesrna/intern/rna_volume.c @@ -33,6 +33,26 @@ #include "BLI_math_base.h" +const EnumPropertyItem rna_enum_volume_grid_data_type_items[] = { + {VOLUME_GRID_BOOLEAN, "BOOLEAN", 0, "Boolean", "Boolean"}, + {VOLUME_GRID_FLOAT, "FLOAT", 0, "Float", "Single precision float"}, + {VOLUME_GRID_DOUBLE, "DOUBLE", 0, "Double", "Double precision"}, + {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, + {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, + {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, + {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, + {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, + {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, + {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, + {VOLUME_GRID_POINTS, + "POINTS", + 0, + "Points (Unsupported)", + "Points grid, currently unsupported by volume objects"}, + {VOLUME_GRID_UNKNOWN, "UNKNOWN", 0, "Unknown", "Unsupported data type"}, + {0, NULL, 0, NULL, NULL}, +}; + #ifdef RNA_RUNTIME # include "DEG_depsgraph.h" @@ -41,6 +61,16 @@ # include "WM_api.h" # include "WM_types.h" +static char *rna_VolumeRender_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("render"); +} + +static char *rna_VolumeDisplay_path(PointerRNA *UNUSED(ptr)) +{ + return BLI_strdup("display"); +} + /* Updates */ static void rna_Volume_update_display(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) @@ -234,30 +264,10 @@ static void rna_def_volume_grid(BlenderRNA *brna) prop, "rna_VolumeGrid_name_get", "rna_VolumeGrid_name_length", NULL); RNA_def_property_ui_text(prop, "Name", "Volume grid name"); - static const EnumPropertyItem data_type_items[] = { - {VOLUME_GRID_BOOLEAN, "BOOLEAN", 0, "Boolean", "Boolean"}, - {VOLUME_GRID_FLOAT, "FLOAT", 0, "Float", "Single precision float"}, - {VOLUME_GRID_DOUBLE, "DOUBLE", 0, "Double", "Double precision"}, - {VOLUME_GRID_INT, "INT", 0, "Integer", "32-bit integer"}, - {VOLUME_GRID_INT64, "INT64", 0, "Integer 64-bit", "64-bit integer"}, - {VOLUME_GRID_MASK, "MASK", 0, "Mask", "No data, boolean mask of active voxels"}, - {VOLUME_GRID_STRING, "STRING", 0, "String", "Text string"}, - {VOLUME_GRID_VECTOR_FLOAT, "VECTOR_FLOAT", 0, "Float Vector", "3D float vector"}, - {VOLUME_GRID_VECTOR_DOUBLE, "VECTOR_DOUBLE", 0, "Double Vector", "3D double vector"}, - {VOLUME_GRID_VECTOR_INT, "VECTOR_INT", 0, "Integer Vector", "3D integer vector"}, - {VOLUME_GRID_POINTS, - "POINTS", - 0, - "Points (Unsupported)", - "Points grid, currently unsupported by volume objects"}, - {VOLUME_GRID_UNKNOWN, "UNKNOWN", 0, "Unknown", "Unsupported data type"}, - {0, NULL, 0, NULL, NULL}, - }; - prop = RNA_def_property(srna, "data_type", PROP_ENUM, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_enum_funcs(prop, "rna_VolumeGrid_data_type_get", NULL, NULL); - RNA_def_property_enum_items(prop, data_type_items); + RNA_def_property_enum_items(prop, rna_enum_volume_grid_data_type_items); RNA_def_property_ui_text(prop, "Data Type", "Data type of voxel values"); prop = RNA_def_property(srna, "channels", PROP_INT, PROP_UNSIGNED); @@ -371,6 +381,7 @@ static void rna_def_volume_display(BlenderRNA *brna) srna = RNA_def_struct(brna, "VolumeDisplay", NULL); RNA_def_struct_ui_text(srna, "Volume Display", "Volume object display settings for 3D viewport"); RNA_def_struct_sdna(srna, "VolumeDisplay"); + RNA_def_struct_path_func(srna, "rna_VolumeDisplay_path"); prop = RNA_def_property(srna, "density", PROP_FLOAT, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); @@ -477,6 +488,7 @@ static void rna_def_volume_render(BlenderRNA *brna) srna = RNA_def_struct(brna, "VolumeRender", NULL); RNA_def_struct_ui_text(srna, "Volume Render", "Volume object render settings"); RNA_def_struct_sdna(srna, "VolumeRender"); + RNA_def_struct_path_func(srna, "rna_VolumeRender_path"); static const EnumPropertyItem space_items[] = { {VOLUME_SPACE_OBJECT, diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index c2d1ac67675..1cb1397a0ed 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -362,6 +362,8 @@ const EnumPropertyItem rna_enum_event_type_items[] = { 0, "ActionZone Fullscreen", "AZone FullScr"}, + /* xr */ + {EVT_XR_ACTION, "XR_ACTION", 0, "XR Action", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -700,6 +702,18 @@ static void rna_Event_tilt_get(PointerRNA *ptr, float *values) WM_event_tablet_data(event, NULL, values); } +static PointerRNA rna_Event_xr_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + wmEvent *event = ptr->data; + wmXrActionData *actiondata = WM_event_is_xr(event) ? event->customdata : NULL; + return rna_pointer_inherit_refine(ptr, &RNA_XrEventData, actiondata); +# else + UNUSED_VARS(ptr); + return PointerRNA_NULL; +# endif +} + static PointerRNA rna_PopupMenu_layout_get(PointerRNA *ptr) { struct uiPopupMenu *pup = ptr->data; @@ -1869,23 +1883,10 @@ static void rna_def_operator_options_runtime(BlenderRNA *brna) prop, "Focus Region", "Enable to use the region under the cursor for modal execution"); } -static void rna_def_operator(BlenderRNA *brna) +static void rna_def_operator_common(StructRNA *srna) { - StructRNA *srna; PropertyRNA *prop; - srna = RNA_def_struct(brna, "Operator", NULL); - RNA_def_struct_ui_text( - srna, "Operator", "Storage of an operator being executed, or registered after execution"); - RNA_def_struct_sdna(srna, "wmOperator"); - RNA_def_struct_refine_func(srna, "rna_Operator_refine"); -# ifdef WITH_PYTHON - RNA_def_struct_register_funcs( - srna, "rna_Operator_register", "rna_Operator_unregister", "rna_Operator_instance"); -# endif - RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); - RNA_def_struct_flag(srna, STRUCT_PUBLIC_NAMESPACE_INHERIT); - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_string_funcs(prop, "rna_Operator_name_get", "rna_Operator_name_length", NULL); @@ -1905,15 +1906,6 @@ static void rna_def_operator(BlenderRNA *brna) "Has Reports", "Operator has a set of reports (warnings and errors) from last execution"); - prop = RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE); - RNA_def_property_struct_type(prop, "UILayout"); - - prop = RNA_def_property(srna, "options", PROP_POINTER, PROP_NONE); - RNA_def_property_flag(prop, PROP_NEVER_NULL); - RNA_def_property_struct_type(prop, "OperatorOptions"); - RNA_def_property_pointer_funcs(prop, "rna_Operator_options_get", NULL, NULL, NULL); - RNA_def_property_ui_text(prop, "Options", "Runtime options"); - /* Registration */ prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); RNA_def_property_string_sdna(prop, NULL, "type->idname"); @@ -1967,6 +1959,45 @@ static void rna_def_operator(BlenderRNA *brna) RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); + prop = RNA_def_property(srna, "bl_cursor_pending", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "type->cursor_pending"); + RNA_def_property_enum_items(prop, rna_enum_window_cursor_items); + RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); + RNA_def_property_ui_text( + prop, + "Idle Cursor", + "Cursor to use when waiting for the user to select a location to activate the operator " + "(when ``bl_options`` has ``DEPENDS_ON_CURSOR`` set)"); +} + +static void rna_def_operator(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Operator", NULL); + RNA_def_struct_ui_text( + srna, "Operator", "Storage of an operator being executed, or registered after execution"); + RNA_def_struct_sdna(srna, "wmOperator"); + RNA_def_struct_refine_func(srna, "rna_Operator_refine"); +# ifdef WITH_PYTHON + RNA_def_struct_register_funcs( + srna, "rna_Operator_register", "rna_Operator_unregister", "rna_Operator_instance"); +# endif + RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); + RNA_def_struct_flag(srna, STRUCT_PUBLIC_NAMESPACE_INHERIT); + + rna_def_operator_common(srna); + + prop = RNA_def_property(srna, "layout", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "UILayout"); + + prop = RNA_def_property(srna, "options", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "OperatorOptions"); + RNA_def_property_pointer_funcs(prop, "rna_Operator_options_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Options", "Runtime options"); + prop = RNA_def_property(srna, "macros", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_sdna(prop, NULL, "macro", NULL); RNA_def_property_struct_type(prop, "Macro"); @@ -1986,7 +2017,6 @@ static void rna_def_operator(BlenderRNA *brna) static void rna_def_macro_operator(BlenderRNA *brna) { StructRNA *srna; - PropertyRNA *prop; srna = RNA_def_struct(brna, "Macro", NULL); RNA_def_struct_ui_text( @@ -2002,68 +2032,7 @@ static void rna_def_macro_operator(BlenderRNA *brna) RNA_def_struct_translation_context(srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); RNA_def_struct_flag(srna, STRUCT_PUBLIC_NAMESPACE_INHERIT); - prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); - RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_string_funcs(prop, "rna_Operator_name_get", "rna_Operator_name_length", NULL); - RNA_def_property_ui_text(prop, "Name", ""); - - prop = RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE); - RNA_def_property_flag(prop, PROP_NEVER_NULL); - RNA_def_property_struct_type(prop, "OperatorProperties"); - RNA_def_property_ui_text(prop, "Properties", ""); - RNA_def_property_pointer_funcs(prop, "rna_Operator_properties_get", NULL, NULL, NULL); - - /* Registration */ - prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->idname"); - RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_idname_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER); - RNA_def_struct_name_property(srna, prop); - - prop = RNA_def_property(srna, "bl_label", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->name"); - RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, NULL, NULL, "rna_Operator_bl_label_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER); - - prop = RNA_def_property(srna, "bl_translation_context", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->translation_context"); - RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, - "rna_Operator_bl_translation_context_get", - "rna_Operator_bl_translation_context_length", - "rna_Operator_bl_translation_context_set"); - RNA_def_property_string_default(prop, BLT_I18NCONTEXT_OPERATOR_DEFAULT); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - - prop = RNA_def_property(srna, "bl_description", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->description"); - RNA_def_property_string_maxlength(prop, RNA_DYN_DESCR_MAX); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, - "rna_Operator_bl_description_get", - "rna_Operator_bl_description_length", - "rna_Operator_bl_description_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - - prop = RNA_def_property(srna, "bl_undo_group", PROP_STRING, PROP_NONE); - RNA_def_property_string_sdna(prop, NULL, "type->undo_group"); - RNA_def_property_string_maxlength(prop, OP_MAX_TYPENAME); /* else it uses the pointer size! */ - RNA_def_property_string_funcs(prop, - "rna_Operator_bl_undo_group_get", - "rna_Operator_bl_undo_group_length", - "rna_Operator_bl_undo_group_set"); - // RNA_def_property_clear_flag(prop, PROP_EDITABLE); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL); - - prop = RNA_def_property(srna, "bl_options", PROP_ENUM, PROP_NONE); - RNA_def_property_enum_sdna(prop, NULL, "type->flag"); - RNA_def_property_enum_items(prop, rna_enum_operator_type_flag_items); - RNA_def_property_flag(prop, PROP_REGISTER_OPTIONAL | PROP_ENUM_FLAG); - RNA_def_property_ui_text(prop, "Options", "Options for this operator type"); + rna_def_operator_common(srna); RNA_api_macro(srna); } @@ -2170,13 +2139,13 @@ static void rna_def_event(BlenderRNA *brna) /* mouse */ prop = RNA_def_property(srna, "mouse_x", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "x"); + RNA_def_property_int_sdna(prop, NULL, "xy[0]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse X Position", "The window relative horizontal location of the mouse"); prop = RNA_def_property(srna, "mouse_y", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "y"); + RNA_def_property_int_sdna(prop, NULL, "xy[1]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse Y Position", "The window relative vertical location of the mouse"); @@ -2194,13 +2163,13 @@ static void rna_def_event(BlenderRNA *brna) prop, "Mouse Y Position", "The region relative vertical location of the mouse"); prop = RNA_def_property(srna, "mouse_prev_x", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "prevx"); + RNA_def_property_int_sdna(prop, NULL, "prev_xy[0]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse Previous X Position", "The window relative horizontal location of the mouse"); prop = RNA_def_property(srna, "mouse_prev_y", PROP_INT, PROP_NONE); - RNA_def_property_int_sdna(prop, NULL, "prevy"); + RNA_def_property_int_sdna(prop, NULL, "prev_xy[1]"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text( prop, "Mouse Previous Y Position", "The window relative vertical location of the mouse"); @@ -2228,6 +2197,13 @@ static void rna_def_event(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Absolute Motion", "The last motion event was an absolute input"); + /* xr */ + prop = RNA_def_property(srna, "xr", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "XrEventData"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_pointer_funcs(prop, "rna_Event_xr_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "XR", "XR event data"); + /* modifiers */ prop = RNA_def_property(srna, "shift", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "shift", 1); diff --git a/source/blender/makesrna/intern/rna_wm_api.c b/source/blender/makesrna/intern/rna_wm_api.c index 7c3b119abb9..06edf6f443e 100644 --- a/source/blender/makesrna/intern/rna_wm_api.c +++ b/source/blender/makesrna/intern/rna_wm_api.c @@ -64,6 +64,13 @@ const EnumPropertyItem rna_enum_window_cursor_items[] = { {WM_CURSOR_NS_SCROLL, "SCROLL_Y", 0, "Scroll-Y", ""}, {WM_CURSOR_NSEW_SCROLL, "SCROLL_XY", 0, "Scroll-XY", ""}, {WM_CURSOR_EYEDROPPER, "EYEDROPPER", 0, "Eyedropper", ""}, + {WM_CURSOR_PICK_AREA, "PICK_AREA", 0, "Pick Area", ""}, + {WM_CURSOR_STOP, "STOP", 0, "Stop", ""}, + {WM_CURSOR_COPY, "COPY", 0, "Copy", ""}, + {WM_CURSOR_CROSS, "CROSS", 0, "Cross", ""}, + {WM_CURSOR_MUTE, "MUTE", 0, "Mute", ""}, + {WM_CURSOR_ZOOM_IN, "ZOOM_IN", 0, "Zoom In", ""}, + {WM_CURSOR_ZOOM_OUT, "ZOOM_OUT", 0, "Zoom Out", ""}, {0, NULL, 0, NULL, NULL}, }; @@ -645,8 +652,8 @@ static wmEvent *rna_Window_event_add_simulate(wmWindow *win, e.type = type; e.val = value; e.is_repeat = false; - e.x = x; - e.y = y; + e.xy[0] = x; + e.xy[1] = y; e.shift = shift; e.ctrl = ctrl; diff --git a/source/blender/makesrna/intern/rna_workspace_api.c b/source/blender/makesrna/intern/rna_workspace_api.c index a2bb89dd5ee..15230f1198b 100644 --- a/source/blender/makesrna/intern/rna_workspace_api.c +++ b/source/blender/makesrna/intern/rna_workspace_api.c @@ -29,6 +29,7 @@ #include "DNA_object_types.h" #include "DNA_windowmanager_types.h" +#include "DNA_workspace_types.h" #include "RNA_enum_types.h" /* own include */ @@ -51,6 +52,7 @@ static void rna_WorkSpaceTool_setup(ID *id, const char *data_block, const char *op_idname, int index, + int options, const char *idname_fallback, const char *keymap_fallback) { @@ -62,6 +64,7 @@ static void rna_WorkSpaceTool_setup(ID *id, STRNCPY(tref_rt.data_block, data_block); STRNCPY(tref_rt.op, op_idname); tref_rt.index = index; + tref_rt.flag = options; /* While it's logical to assign both these values from setup, * it's useful to stored this in DNA for re-use, exceptional case: write to the 'tref'. */ @@ -131,6 +134,11 @@ void RNA_api_workspace_tool(StructRNA *srna) PropertyRNA *parm; FunctionRNA *func; + static EnumPropertyItem options_items[] = { + {TOOLREF_FLAG_FALLBACK_KEYMAP, "KEYMAP_FALLBACK", 0, "Fallback", ""}, + {0, NULL, 0, NULL, NULL}, + }; + func = RNA_def_function(srna, "setup", "rna_WorkSpaceTool_setup"); RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_CONTEXT); RNA_def_function_ui_description(func, "Set the tool settings"); @@ -146,6 +154,7 @@ void RNA_api_workspace_tool(StructRNA *srna) RNA_def_string(func, "data_block", NULL, MAX_NAME, "Data Block", ""); RNA_def_string(func, "operator", NULL, MAX_NAME, "Operator", ""); RNA_def_int(func, "index", 0, INT_MIN, INT_MAX, "Index", "", INT_MIN, INT_MAX); + RNA_def_enum_flag(func, "options", options_items, 0, "Tool Options", ""); RNA_def_string(func, "idname_fallback", NULL, MAX_NAME, "Fallback Identifier", ""); RNA_def_string(func, "keymap_fallback", NULL, KMAP_MAX_NAME, "Fallback Key Map", ""); diff --git a/source/blender/makesrna/intern/rna_world.c b/source/blender/makesrna/intern/rna_world.c index 826e6d21c36..68f11d71de2 100644 --- a/source/blender/makesrna/intern/rna_world.c +++ b/source/blender/makesrna/intern/rna_world.c @@ -128,6 +128,7 @@ static void rna_def_lighting(BlenderRNA *brna) prop = RNA_def_property(srna, "distance", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_float_sdna(prop, NULL, "aodist"); + RNA_def_property_range(prop, 0, FLT_MAX); RNA_def_property_ui_text( prop, "Distance", "Length of rays, defines how far away other faces give occlusion effect"); RNA_def_property_update(prop, 0, "rna_World_update"); diff --git a/source/blender/makesrna/intern/rna_xr.c b/source/blender/makesrna/intern/rna_xr.c index f24d28d1209..dd4cbeac174 100644 --- a/source/blender/makesrna/intern/rna_xr.c +++ b/source/blender/makesrna/intern/rna_xr.c @@ -892,6 +892,71 @@ static void rna_XrSessionState_viewer_pose_rotation_get(PointerRNA *ptr, float * # endif } +static void rna_XrSessionState_nav_location_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_location_get(xr, r_values); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrSessionState_nav_location_set(PointerRNA *ptr, const float *values) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_location_set(xr, values); +# else + UNUSED_VARS(ptr, values); +# endif +} + +static void rna_XrSessionState_nav_rotation_get(PointerRNA *ptr, float *r_values) +{ +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_rotation_get(xr, r_values); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +static void rna_XrSessionState_nav_rotation_set(PointerRNA *ptr, const float *values) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_rotation_set(xr, values); +# else + UNUSED_VARS(ptr, values); +# endif +} + +static float rna_XrSessionState_nav_scale_get(PointerRNA *ptr) +{ + float value; +# ifdef WITH_XR_OPENXR + const wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_scale_get(xr, &value); +# else + UNUSED_VARS(ptr); + value = 1.0f; +# endif + return value; +} + +static void rna_XrSessionState_nav_scale_set(PointerRNA *ptr, float value) +{ +# ifdef WITH_XR_OPENXR + wmXrData *xr = rna_XrSession_wm_xr_data_get(ptr); + WM_xr_session_state_nav_scale_set(xr, value); +# else + UNUSED_VARS(ptr, value); +# endif +} + static void rna_XrSessionState_actionmaps_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { # ifdef WITH_XR_OPENXR @@ -947,6 +1012,155 @@ static void rna_XrSessionState_selected_actionmap_set(PointerRNA *ptr, int value /** \} */ +/* -------------------------------------------------------------------- */ +/** \name XR Event Data + * \{ */ + +static void rna_XrEventData_action_set_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->action_set); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_action_set_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->action_set); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_action_get(PointerRNA *ptr, char *r_value) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + strcpy(r_value, data->action); +# else + UNUSED_VARS(ptr); + r_value[0] = '\0'; +# endif +} + +static int rna_XrEventData_action_length(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return strlen(data->action); +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static int rna_XrEventData_type_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return data->type; +# else + UNUSED_VARS(ptr); + return 0; +# endif +} + +static void rna_XrEventData_state_get(PointerRNA *ptr, float r_values[2]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v2_v2(r_values, data->state); +# else + UNUSED_VARS(ptr); + zero_v2(r_values); +# endif +} + +static void rna_XrEventData_state_other_get(PointerRNA *ptr, float r_values[2]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v2_v2(r_values, data->state_other); +# else + UNUSED_VARS(ptr); + zero_v2(r_values); +# endif +} + +static float rna_XrEventData_float_threshold_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return data->float_threshold; +# else + UNUSED_VARS(ptr); + return 0.0f; +# endif +} + +static void rna_XrEventData_controller_location_get(PointerRNA *ptr, float r_values[3]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v3_v3(r_values, data->controller_loc); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrEventData_controller_rotation_get(PointerRNA *ptr, float r_values[4]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_qt_qt(r_values, data->controller_rot); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +static void rna_XrEventData_controller_location_other_get(PointerRNA *ptr, float r_values[3]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_v3_v3(r_values, data->controller_loc_other); +# else + UNUSED_VARS(ptr); + zero_v3(r_values); +# endif +} + +static void rna_XrEventData_controller_rotation_other_get(PointerRNA *ptr, float r_values[4]) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + copy_qt_qt(r_values, data->controller_rot_other); +# else + UNUSED_VARS(ptr); + unit_qt(r_values); +# endif +} + +static bool rna_XrEventData_bimanual_get(PointerRNA *ptr) +{ +# ifdef WITH_XR_OPENXR + const wmXrActionData *data = ptr->data; + return data->bimanual; +# else + UNUSED_VARS(ptr); + return false; +# endif +} + +/** \} */ + #else /* RNA_RUNTIME */ /* -------------------------------------------------------------------- */ @@ -1410,6 +1624,22 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem controller_draw_styles[] = { + {XR_CONTROLLER_DRAW_DARK, "DARK", 0, "Dark", "Draw dark controller"}, + {XR_CONTROLLER_DRAW_LIGHT, "LIGHT", 0, "Light", "Draw light controller"}, + {XR_CONTROLLER_DRAW_DARK_RAY, + "DARK_RAY", + 0, + "Dark + Ray", + "Draw dark controller with aiming axis ray"}, + {XR_CONTROLLER_DRAW_LIGHT_RAY, + "LIGHT_RAY", + 0, + "Light + Ray", + "Draw light controller with aiming axis ray"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "XrSessionSettings", NULL); RNA_def_struct_ui_text(srna, "XR Session Settings", ""); @@ -1450,6 +1680,13 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "Rotation angle around the Z-Axis to apply the rotation deltas from the VR headset to"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "base_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_ui_text(prop, "Base Scale", "Uniform scale to apply to VR view"); + RNA_def_property_range(prop, 1e-6f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3); + RNA_def_property_float_default(prop, 1.0f); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "show_floor", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_GRIDFLOOR); RNA_def_property_ui_text(prop, "Display Grid Floor", "Show the ground plane grid"); @@ -1460,6 +1697,29 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Show Annotation", "Show annotations for this view"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "show_selection", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_SHOW_SELECTION); + RNA_def_property_ui_text(prop, "Show Selection", "Show selection outlines"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "show_controllers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_XR_SHOW_CONTROLLERS); + RNA_def_property_ui_text( + prop, "Show Controllers", "Show VR controllers (requires VR actions for controller poses)"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "show_custom_overlays", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "draw_flags", V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS); + RNA_def_property_ui_text(prop, "Show Custom Overlays", "Show custom VR overlays"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + + prop = RNA_def_property(srna, "controller_draw_style", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_enum_items(prop, controller_draw_styles); + RNA_def_property_ui_text( + prop, "Controller Draw Style", "Style to use when drawing VR controllers"); + RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); + prop = RNA_def_property(srna, "clip_start", PROP_FLOAT, PROP_DISTANCE); RNA_def_property_range(prop, 1e-6f, FLT_MAX); RNA_def_property_ui_range(prop, 0.001f, FLT_MAX, 10, 3); @@ -1487,7 +1747,9 @@ static void rna_def_xr_session_settings(BlenderRNA *brna) "rna_XrSessionSettings_use_absolute_tracking_get", "rna_XrSessionSettings_use_absolute_tracking_set"); RNA_def_property_ui_text( - prop, "Absolute Tracking", "Use unadjusted location/rotation as defined by the XR runtime"); + prop, + "Absolute Tracking", + "Allow the VR tracking origin to be defined independently of the headset location"); RNA_def_property_update(prop, NC_WM | ND_XR_DATA_CHANGED, NULL); } @@ -1793,6 +2055,32 @@ static void rna_def_xr_session_state(BlenderRNA *brna) "Viewer Pose Rotation", "Last known rotation of the viewer pose (center between the eyes) in world space"); + prop = RNA_def_property(srna, "navigation_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_location_get", "rna_XrSessionState_nav_location_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Location", + "Location offset to apply to base pose when determining viewer location"); + + prop = RNA_def_property(srna, "navigation_rotation", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_rotation_get", "rna_XrSessionState_nav_rotation_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Rotation", + "Rotation offset to apply to base pose when determining viewer rotation"); + + prop = RNA_def_property(srna, "navigation_scale", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_funcs( + prop, "rna_XrSessionState_nav_scale_get", "rna_XrSessionState_nav_scale_set", NULL); + RNA_def_property_ui_text( + prop, + "Navigation Scale", + "Additional scale multiplier to apply to base scale when determining viewer scale"); + prop = RNA_def_property(srna, "actionmaps", PROP_COLLECTION, PROP_NONE); RNA_def_property_collection_funcs(prop, "rna_XrSessionState_actionmaps_begin", @@ -1824,6 +2112,94 @@ static void rna_def_xr_session_state(BlenderRNA *brna) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name XR Event Data + * \{ */ + +static void rna_def_xr_eventdata(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "XrEventData", NULL); + RNA_def_struct_ui_text(srna, "XrEventData", "XR Data for Window Manager Event"); + + prop = RNA_def_property(srna, "action_set", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_action_set_get", "rna_XrEventData_action_set_length", NULL); + RNA_def_property_ui_text(prop, "Action Set", "XR action set name"); + + prop = RNA_def_property(srna, "action", PROP_STRING, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_string_funcs( + prop, "rna_XrEventData_action_get", "rna_XrEventData_action_length", NULL); + RNA_def_property_ui_text(prop, "Action", "XR action name"); + + prop = RNA_def_property(srna, "type", PROP_ENUM, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_enum_items(prop, rna_enum_xr_action_types); + RNA_def_property_enum_funcs(prop, "rna_XrEventData_type_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Type", "XR action type"); + + prop = RNA_def_property(srna, "state", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_state_get", NULL, NULL); + RNA_def_property_ui_text(prop, "State", "XR action values corresponding to type"); + + prop = RNA_def_property(srna, "state_other", PROP_FLOAT, PROP_NONE); + RNA_def_property_array(prop, 2); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_state_other_get", NULL, NULL); + RNA_def_property_ui_text( + prop, "State Other", "State of the other user path for bimanual actions"); + + prop = RNA_def_property(srna, "float_threshold", PROP_FLOAT, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_float_threshold_get", NULL, NULL); + RNA_def_property_ui_text(prop, "Float Threshold", "Input threshold for float/2D vector actions"); + + prop = RNA_def_property(srna, "controller_location", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_location_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Location", + "Location of the action's corresponding controller aim in world space"); + + prop = RNA_def_property(srna, "controller_rotation", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_rotation_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Rotation", + "Rotation of the action's corresponding controller aim in world space"); + + prop = RNA_def_property(srna, "controller_location_other", PROP_FLOAT, PROP_TRANSLATION); + RNA_def_property_array(prop, 3); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_location_other_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Location Other", + "Controller aim location of the other user path for bimanual actions"); + + prop = RNA_def_property(srna, "controller_rotation_other", PROP_FLOAT, PROP_QUATERNION); + RNA_def_property_array(prop, 4); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_float_funcs(prop, "rna_XrEventData_controller_rotation_other_get", NULL, NULL); + RNA_def_property_ui_text(prop, + "Controller Rotation Other", + "Controller aim rotation of the other user path for bimanual actions"); + + prop = RNA_def_property(srna, "bimanual", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_XrEventData_bimanual_get", NULL); + RNA_def_property_ui_text(prop, "Bimanual", "Whether bimanual interaction is occurring"); +} + +/** \} */ + void RNA_def_xr(BlenderRNA *brna) { RNA_define_animate_sdna(false); @@ -1831,6 +2207,7 @@ void RNA_def_xr(BlenderRNA *brna) rna_def_xr_actionmap(brna); rna_def_xr_session_settings(brna); rna_def_xr_session_state(brna); + rna_def_xr_eventdata(brna); RNA_define_animate_sdna(true); } diff --git a/source/blender/modifiers/intern/MOD_boolean.cc b/source/blender/modifiers/intern/MOD_boolean.cc index c5d6902e1bc..95167b5c82e 100644 --- a/source/blender/modifiers/intern/MOD_boolean.cc +++ b/source/blender/modifiers/intern/MOD_boolean.cc @@ -386,7 +386,7 @@ static void BMD_mesh_intersection(BMesh *bm, * Caller owns the returned array. */ static Array get_material_remap(Object *dest_ob, Object *src_ob) { - int n = dest_ob->totcol; + int n = src_ob->totcol; if (n <= 0) { n = 1; } diff --git a/source/blender/modifiers/intern/MOD_cloth.c b/source/blender/modifiers/intern/MOD_cloth.c index fa2f70e1a9c..c9d5ef73c49 100644 --- a/source/blender/modifiers/intern/MOD_cloth.c +++ b/source/blender/modifiers/intern/MOD_cloth.c @@ -194,7 +194,7 @@ static void copyData(const ModifierData *md, ModifierData *target, const int fla } BKE_ptcache_free_list(&tclmd->ptcaches); - if (flag & LIB_ID_CREATE_NO_MAIN) { + if (flag & LIB_ID_COPY_SET_COPIED_ON_WRITE) { /* Share the cache with the original object's modifier. */ tclmd->modifier.flag |= eModifierFlag_SharedCaches; tclmd->ptcaches = clmd->ptcaches; @@ -285,7 +285,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_collision.c b/source/blender/modifiers/intern/MOD_collision.c index 521a93b199f..02e1f61b824 100644 --- a/source/blender/modifiers/intern/MOD_collision.c +++ b/source/blender/modifiers/intern/MOD_collision.c @@ -263,7 +263,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_datatransfer.c b/source/blender/modifiers/intern/MOD_datatransfer.c index 4d670ba8afd..85876377198 100644 --- a/source/blender/modifiers/intern/MOD_datatransfer.c +++ b/source/blender/modifiers/intern/MOD_datatransfer.c @@ -163,7 +163,6 @@ static bool isDisabled(const struct Scene *UNUSED(scene), return !dtmd->ob_source || dtmd->ob_source->type != OB_MESH; } -#define HIGH_POLY_WARNING 10000 #define DT_TYPES_AFFECT_MESH \ (DT_TYPE_BWEIGHT_VERT | DT_TYPE_BWEIGHT_EDGE | DT_TYPE_CREASE | DT_TYPE_SHARP_EDGE | \ DT_TYPE_LNOR | DT_TYPE_SHARP_FACE) @@ -239,13 +238,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * BKE_modifier_set_error( ctx->object, (ModifierData *)dtmd, "Enable 'Auto Smooth' in Object Data Properties"); } - else if (result->totvert > HIGH_POLY_WARNING || - ((Mesh *)(ob_source->data))->totvert > HIGH_POLY_WARNING) { - BKE_modifier_set_error( - ctx->object, - md, - "Source or destination object has a high polygon count, computation might be slow"); - } return result; } @@ -490,7 +482,6 @@ static void panelRegister(ARegionType *region_type) region_type, "advanced", "Topology Mapping", NULL, advanced_panel_draw, panel_type); } -#undef HIGH_POLY_WARNING #undef DT_TYPES_AFFECT_MESH ModifierTypeInfo modifierType_DataTransfer = { diff --git a/source/blender/modifiers/intern/MOD_decimate.c b/source/blender/modifiers/intern/MOD_decimate.c index 56fcbbd8b7c..975f80a04f8 100644 --- a/source/blender/modifiers/intern/MOD_decimate.c +++ b/source/blender/modifiers/intern/MOD_decimate.c @@ -236,8 +236,8 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, &ob_ptr); int decimate_type = RNA_enum_get(ptr, "decimate_type"); - char count_info[32]; - snprintf(count_info, 32, "%s: %d", IFACE_("Face Count"), RNA_int_get(ptr, "face_count")); + char count_info[64]; + snprintf(count_info, 32, TIP_("Face Count: %d"), RNA_int_get(ptr, "face_count")); uiItemR(layout, ptr, "decimate_type", UI_ITEM_R_EXPAND, NULL, ICON_NONE); diff --git a/source/blender/modifiers/intern/MOD_dynamicpaint.c b/source/blender/modifiers/intern/MOD_dynamicpaint.c index 77ae5c4b6f1..a696ce216c7 100644 --- a/source/blender/modifiers/intern/MOD_dynamicpaint.c +++ b/source/blender/modifiers/intern/MOD_dynamicpaint.c @@ -193,7 +193,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_edgesplit.c b/source/blender/modifiers/intern/MOD_edgesplit.c index b21a536ad8a..1039bcb2b3b 100644 --- a/source/blender/modifiers/intern/MOD_edgesplit.c +++ b/source/blender/modifiers/intern/MOD_edgesplit.c @@ -20,10 +20,10 @@ /** \file * \ingroup modifiers * - * EdgeSplit modifier + * Edge Split modifier * * Splits edges in the mesh according to sharpness flag - * or edge angle (can be used to achieve autosmoothing) + * or edge angle (can be used to achieve auto-smoothing) */ #include "BLI_utildefines.h" diff --git a/source/blender/modifiers/intern/MOD_fluid.c b/source/blender/modifiers/intern/MOD_fluid.c index a14d582063a..a21eb603300 100644 --- a/source/blender/modifiers/intern/MOD_fluid.c +++ b/source/blender/modifiers/intern/MOD_fluid.c @@ -25,6 +25,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_task.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -112,6 +113,31 @@ static void requiredDataMask(Object *UNUSED(ob), } } +typedef struct FluidIsolationData { + Depsgraph *depsgraph; + Object *object; + Mesh *mesh; + FluidModifierData *fmd; + + Mesh *result; +} FluidIsolationData; + +#ifdef WITH_FLUID +static void fluid_modifier_do_isolated(void *userdata) +{ + FluidIsolationData *isolation_data = (FluidIsolationData *)userdata; + + Scene *scene = DEG_get_evaluated_scene(isolation_data->depsgraph); + + Mesh *result = BKE_fluid_modifier_do(isolation_data->fmd, + isolation_data->depsgraph, + scene, + isolation_data->object, + isolation_data->mesh); + isolation_data->result = result ? result : isolation_data->mesh; +} +#endif /* WITH_FLUID */ + static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *me) { #ifndef WITH_FLUID @@ -119,16 +145,24 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * return me; #else FluidModifierData *fmd = (FluidModifierData *)md; - Mesh *result = NULL; if (ctx->flag & MOD_APPLY_ORCO) { return me; } - Scene *scene = DEG_get_evaluated_scene(ctx->depsgraph); + /* Isolate execution of Mantaflow when running from dependency graph. The reason for this is + * because Mantaflow uses TBB to parallel its own computation which without isolation will start + * stealing tasks from dependency graph. Stealing tasks from the dependency graph might cause + * a recursive lock when Python drivers are used (because Mantaflow is interfaced via Python as + * well. */ + FluidIsolationData isolation_data; + isolation_data.depsgraph = ctx->depsgraph; + isolation_data.object = ctx->object; + isolation_data.mesh = me; + isolation_data.fmd = fmd; + BLI_task_isolate(fluid_modifier_do_isolated, &isolation_data); - result = BKE_fluid_modifier_do(fmd, ctx->depsgraph, scene, ctx->object, me); - return result ? result : me; + return isolation_data.result; #endif /* WITH_FLUID */ } @@ -213,7 +247,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 9a8af35109a..f452b0c7091 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -464,7 +464,7 @@ void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, BLI_assert(src_mesh.totedge == edge_map.size()); for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; - if (i_dst == -1 || i_dst == -2) { + if (ELEM(i_dst, -1, -2)) { continue; } diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 6b976b016e1..f87ff844acf 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -27,11 +27,13 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.hh" #include "BLI_float3.hh" #include "BLI_listbase.h" #include "BLI_multi_value_map.hh" #include "BLI_set.hh" #include "BLI_string.h" +#include "BLI_string_search.h" #include "BLI_utildefines.h" #include "DNA_collection_types.h" @@ -66,8 +68,11 @@ #include "BLO_read_write.h" #include "UI_interface.h" +#include "UI_interface.hh" #include "UI_resources.h" +#include "BLT_translation.h" + #include "WM_types.h" #include "RNA_access.h" @@ -82,14 +87,17 @@ #include "MOD_ui_common.h" #include "ED_spreadsheet.h" +#include "ED_undo.h" #include "NOD_derived_node_tree.hh" #include "NOD_geometry.h" #include "NOD_geometry_nodes_eval_log.hh" +#include "NOD_node_declaration.hh" #include "FN_field.hh" #include "FN_multi_function.hh" +using blender::Array; using blender::ColorGeometry4f; using blender::destruct_ptr; using blender::float3; @@ -101,12 +109,17 @@ using blender::Span; using blender::StringRef; using blender::StringRefNull; using blender::Vector; +using blender::bke::OutputAttribute; +using blender::fn::GField; using blender::fn::GMutablePointer; using blender::fn::GPointer; +using blender::nodes::FieldInferencingInterface; using blender::nodes::GeoNodeExecParams; +using blender::nodes::InputSocketFieldType; using blender::threading::EnumerableThreadSpecific; using namespace blender::fn::multi_function_types; using namespace blender::nodes::derived_node_tree_types; +using geo_log::GeometryAttributeInfo; static void initData(ModifierData *md) { @@ -144,6 +157,12 @@ static void addIdsUsedBySocket(const ListBase *sockets, Set &ids) ids.add(&texture->id); } } + else if (socket->type == SOCK_IMAGE) { + Image *image = ((bNodeSocketValueImage *)socket->default_value)->value; + if (image != nullptr) { + ids.add(&image->id); + } + } } } @@ -229,6 +248,7 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte add_collection_relation(ctx, *collection); break; } + case ID_IM: case ID_TE: { DEG_add_generic_id_relation(ctx->node, id, "Nodes Modifier"); } @@ -304,6 +324,17 @@ static bool socket_type_has_attribute_toggle(const bNodeSocket &socket) return ELEM(socket.type, SOCK_FLOAT, SOCK_VECTOR, SOCK_BOOLEAN, SOCK_RGBA, SOCK_INT); } +/** + * \return Whether using an attribute to input values of this type is supported, and the node + * group's input for this socket accepts a field rather than just single values. + */ +static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index) +{ + BLI_assert(node_tree.field_inferencing_interface != nullptr); + const FieldInferencingInterface &field_interface = *node_tree.field_inferencing_interface; + return field_interface.inputs[socket_index] != InputSocketFieldType::None; +} + static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) { switch (socket.type) { @@ -402,6 +433,12 @@ static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) idprop.id = (ID *)value->value; return IDP_New(IDP_ID, &idprop, socket.identifier); } + case SOCK_IMAGE: { + bNodeSocketValueImage *value = (bNodeSocketValueImage *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, socket.identifier); + } case SOCK_MATERIAL: { bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; IDPropertyTemplate idprop = {0}; @@ -430,6 +467,7 @@ static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDP case SOCK_OBJECT: case SOCK_COLLECTION: case SOCK_TEXTURE: + case SOCK_IMAGE: case SOCK_MATERIAL: return property.type == IDP_ID; } @@ -499,6 +537,12 @@ static void init_socket_cpp_value_from_property(const IDProperty &property, *(Tex **)r_value = texture; break; } + case SOCK_IMAGE: { + ID *id = IDP_Id(&property); + Image *image = (id && GS(id->name) == ID_IM) ? (Image *)id : nullptr; + *(Image **)r_value = image; + break; + } case SOCK_MATERIAL: { ID *id = IDP_Id(&property); Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr; @@ -529,7 +573,8 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings"); } - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { + int socket_index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { IDProperty *new_prop = id_property_create_from_socket(*socket); if (new_prop == nullptr) { /* Out of the set of supported input sockets, only @@ -587,6 +632,35 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) } } + LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { + if (!socket_type_has_attribute_toggle(*socket)) { + continue; + } + + const std::string idprop_name = socket->identifier + attribute_name_suffix; + IDProperty *new_prop = IDP_NewString("", idprop_name.c_str(), MAX_NAME); + if (socket->description[0] != '\0') { + IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop); + ui_data->description = BLI_strdup(socket->description); + } + IDP_AddToGroup(nmd->settings.properties, new_prop); + + if (old_properties != nullptr) { + IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, idprop_name.c_str()); + if (old_prop != nullptr) { + /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only + * want to replace the values). So release it temporarily and replace it after. */ + IDPropertyUIData *ui_data = new_prop->ui_data; + new_prop->ui_data = nullptr; + IDP_CopyPropertyContent(new_prop, old_prop); + if (new_prop->ui_data != nullptr) { + IDP_ui_data_free(new_prop); + } + new_prop->ui_data = ui_data; + } + } + } + if (old_properties != nullptr) { IDP_FreeProperty(old_properties); } @@ -622,37 +696,39 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd) } static void initialize_group_input(NodesModifierData &nmd, - const bNodeSocket &socket, + const OutputSocketRef &socket, void *r_value) { + const bNodeSocketType &socket_type = *socket.typeinfo(); + const bNodeSocket &bsocket = *socket.bsocket(); if (nmd.settings.properties == nullptr) { - socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); + socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); return; } const IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, - socket.identifier); + socket.identifier().c_str()); if (property == nullptr) { - socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); + socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); return; } - if (!id_property_type_matches_socket(socket, *property)) { - socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); + if (!id_property_type_matches_socket(bsocket, *property)) { + socket_type.get_geometry_nodes_cpp_value(bsocket, r_value); return; } - if (!socket_type_has_attribute_toggle(socket)) { + if (!input_has_attribute_toggle(*nmd.node_group, socket.index())) { init_socket_cpp_value_from_property( - *property, static_cast(socket.type), r_value); + *property, static_cast(bsocket.type), r_value); return; } const IDProperty *property_use_attribute = IDP_GetPropertyFromGroup( - nmd.settings.properties, (socket.identifier + use_attribute_suffix).c_str()); + nmd.settings.properties, (socket.identifier() + use_attribute_suffix).c_str()); const IDProperty *property_attribute_name = IDP_GetPropertyFromGroup( - nmd.settings.properties, (socket.identifier + attribute_name_suffix).c_str()); + nmd.settings.properties, (socket.identifier() + attribute_name_suffix).c_str()); if (property_use_attribute == nullptr || property_attribute_name == nullptr) { init_socket_cpp_value_from_property( - *property, static_cast(socket.type), r_value); + *property, static_cast(bsocket.type), r_value); return; } @@ -660,12 +736,12 @@ static void initialize_group_input(NodesModifierData &nmd, if (use_attribute) { const StringRef attribute_name{IDP_String(property_attribute_name)}; auto attribute_input = std::make_shared( - attribute_name, *socket.typeinfo->get_base_cpp_type()); + attribute_name, *socket_type.get_base_cpp_type()); new (r_value) blender::fn::GField(std::move(attribute_input), 0); } else { init_socket_cpp_value_from_property( - *property, static_cast(socket.type), r_value); + *property, static_cast(bsocket.type), r_value); } } @@ -688,32 +764,33 @@ static Vector find_spreadsheet_editors(Main *bmain) return spreadsheets; } -static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet, - NodesModifierData *nmd, - const ModifierEvalContext *ctx, - const DerivedNodeTree &tree) +static void find_sockets_to_preview_for_spreadsheet(SpaceSpreadsheet *sspreadsheet, + NodesModifierData *nmd, + const ModifierEvalContext *ctx, + const DerivedNodeTree &tree, + Set &r_sockets_to_preview) { Vector context_path = sspreadsheet->context_path; if (context_path.size() < 3) { - return {}; + return; } if (context_path[0]->type != SPREADSHEET_CONTEXT_OBJECT) { - return {}; + return; } if (context_path[1]->type != SPREADSHEET_CONTEXT_MODIFIER) { - return {}; + return; } SpreadsheetContextObject *object_context = (SpreadsheetContextObject *)context_path[0]; if (object_context->object != DEG_get_original_object(ctx->object)) { - return {}; + return; } SpreadsheetContextModifier *modifier_context = (SpreadsheetContextModifier *)context_path[1]; if (StringRef(modifier_context->modifier_name) != nmd->modifier.name) { - return {}; + return; } for (SpreadsheetContext *context : context_path.as_span().drop_front(2)) { if (context->type != SPREADSHEET_CONTEXT_NODE) { - return {}; + return; } } @@ -732,11 +809,11 @@ static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspre } } if (found_node == nullptr) { - return {}; + return; } context = context->child_context(*found_node); if (context == nullptr) { - return {}; + return; } } @@ -744,10 +821,13 @@ static DSocket try_get_socket_to_preview_for_spreadsheet(SpaceSpreadsheet *sspre for (const NodeRef *node_ref : tree_ref.nodes_by_type("GeometryNodeViewer")) { if (node_ref->name() == last_context->node_name) { const DNode viewer_node{context, node_ref}; - return viewer_node.input(0); + for (const InputSocketRef *input_socket : node_ref->inputs()) { + if (input_socket->is_available() && input_socket->is_logically_linked()) { + r_sockets_to_preview.add(DSocket{context, input_socket}); + } + } } } - return {}; } static void find_sockets_to_preview(NodesModifierData *nmd, @@ -761,10 +841,7 @@ static void find_sockets_to_preview(NodesModifierData *nmd, * intermediate geometries cached for display. */ Vector spreadsheets = find_spreadsheet_editors(bmain); for (SpaceSpreadsheet *sspreadsheet : spreadsheets) { - const DSocket socket = try_get_socket_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree); - if (socket) { - r_sockets_to_preview.add(socket); - } + find_sockets_to_preview_for_spreadsheet(sspreadsheet, nmd, ctx, tree, r_sockets_to_preview); } } @@ -776,21 +853,89 @@ static void clear_runtime_data(NodesModifierData *nmd) } } +static void store_field_on_geometry_component(GeometryComponent &component, + const StringRef attribute_name, + AttributeDomain domain, + const GField &field) +{ + /* If the attribute name corresponds to a built-in attribute, use the domain of the built-in + * attribute instead. */ + if (component.attribute_is_builtin(attribute_name)) { + component.attribute_try_create_builtin(attribute_name, AttributeInitDefault()); + std::optional meta_data = component.attribute_get_meta_data(attribute_name); + if (meta_data.has_value()) { + domain = meta_data->domain; + } + else { + return; + } + } + const CustomDataType data_type = blender::bke::cpp_type_to_custom_data_type(field.cpp_type()); + OutputAttribute attribute = component.attribute_try_get_for_output_only( + attribute_name, domain, data_type); + if (attribute) { + /* In the future we could also evaluate all output fields at once. */ + const int domain_size = component.attribute_domain_size(domain); + blender::bke::GeometryComponentFieldContext field_context{component, domain}; + blender::fn::FieldEvaluator field_evaluator{field_context, domain_size}; + field_evaluator.add_with_destination(field, attribute.varray()); + field_evaluator.evaluate(); + attribute.save(); + } +} + +static void store_output_value_in_geometry(GeometrySet &geometry_set, + NodesModifierData *nmd, + const InputSocketRef &socket, + const GPointer value) +{ + if (!socket_type_has_attribute_toggle(*socket.bsocket())) { + return; + } + const std::string prop_name = socket.identifier() + attribute_name_suffix; + const IDProperty *prop = IDP_GetPropertyFromGroup(nmd->settings.properties, prop_name.c_str()); + if (prop == nullptr) { + return; + } + const StringRefNull attribute_name = IDP_String(prop); + if (attribute_name.is_empty()) { + return; + } + const GField &field = *(const GField *)value.get(); + const bNodeSocket *interface_socket = (bNodeSocket *)BLI_findlink(&nmd->node_group->outputs, + socket.index()); + const AttributeDomain domain = (AttributeDomain)interface_socket->attribute_domain; + if (geometry_set.has_mesh()) { + MeshComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } + if (geometry_set.has_pointcloud()) { + PointCloudComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } + if (geometry_set.has_curve()) { + CurveComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } + if (geometry_set.has_instances()) { + InstancesComponent &component = geometry_set.get_component_for_write(); + store_field_on_geometry_component(component, attribute_name, domain, field); + } +} + /** * Evaluate a node group to compute the output geometry. - * Currently, this uses a fairly basic and inefficient algorithm that might compute things more - * often than necessary. It's going to be replaced soon. */ static GeometrySet compute_geometry(const DerivedNodeTree &tree, Span group_input_nodes, - const InputSocketRef &socket_to_compute, + const NodeRef &output_node, GeometrySet input_geometry_set, NodesModifierData *nmd, const ModifierEvalContext *ctx) { blender::ResourceScope scope; blender::LinearAllocator<> &allocator = scope.linear_allocator(); - blender::nodes::NodeMultiFunctions mf_by_node{tree, scope}; + blender::nodes::NodeMultiFunctions mf_by_node{tree}; Map group_inputs; @@ -817,16 +962,15 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, for (const OutputSocketRef *socket : remaining_input_sockets) { const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); - initialize_group_input(*nmd, *socket->bsocket(), value_in); + initialize_group_input(*nmd, *socket, value_in); group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); } } - /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ - input_geometry_set.clear(); - Vector group_outputs; - group_outputs.append({root_context, &socket_to_compute}); + for (const InputSocketRef *socket_ref : output_node.inputs().drop_back(1)) { + group_outputs.append({root_context, socket_ref}); + } std::optional geo_logger; @@ -837,8 +981,13 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, find_sockets_to_preview(nmd, ctx, tree, preview_sockets); eval_params.force_compute_sockets.extend(preview_sockets.begin(), preview_sockets.end()); geo_logger.emplace(std::move(preview_sockets)); + + geo_logger->log_input_geometry(input_geometry_set); } + /* Don't keep a reference to the input geometry components to avoid copies during evaluation. */ + input_geometry_set.clear(); + eval_params.input_values = group_inputs; eval_params.output_sockets = group_outputs; eval_params.mf_by_node = &mf_by_node; @@ -848,15 +997,22 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, eval_params.geo_logger = geo_logger.has_value() ? &*geo_logger : nullptr; blender::modifiers::geometry_nodes::evaluate_geometry_nodes(eval_params); + GeometrySet output_geometry_set = eval_params.r_output_values[0].relocate_out(); + if (geo_logger.has_value()) { + geo_logger->log_output_geometry(output_geometry_set); NodesModifierData *nmd_orig = (NodesModifierData *)BKE_modifier_get_original(&nmd->modifier); clear_runtime_data(nmd_orig); nmd_orig->runtime_eval_log = new geo_log::ModifierLog(*geo_logger); } - BLI_assert(eval_params.r_output_values.size() == 1); - GMutablePointer result = eval_params.r_output_values[0]; - return result.relocate_out(); + for (const InputSocketRef *socket : output_node.inputs().drop_front(1).drop_back(1)) { + GMutablePointer socket_value = eval_params.r_output_values[socket->index()]; + store_output_value_in_geometry(output_geometry_set, nmd, *socket, socket_value); + socket_value.destruct(); + } + + return output_geometry_set; } /** @@ -926,24 +1082,23 @@ static void modifyGeometry(ModifierData *md, const NodeTreeRef &root_tree_ref = tree.root_context().tree(); Span input_nodes = root_tree_ref.nodes_by_type("NodeGroupInput"); Span output_nodes = root_tree_ref.nodes_by_type("NodeGroupOutput"); - if (output_nodes.size() != 1) { return; } - Span group_outputs = output_nodes[0]->inputs().drop_back(1); - - if (group_outputs.size() == 0) { + const NodeRef &output_node = *output_nodes[0]; + Span group_outputs = output_node.inputs().drop_back(1); + if (group_outputs.is_empty()) { return; } - const InputSocketRef *group_output = group_outputs[0]; - if (group_output->idname() != "NodeSocketGeometry") { + const InputSocketRef *first_output_socket = group_outputs[0]; + if (first_output_socket->idname() != "NodeSocketGeometry") { return; } geometry_set = compute_geometry( - tree, input_nodes, *group_outputs[0], std::move(geometry_set), nmd, ctx); + tree, input_nodes, output_node, std::move(geometry_set), nmd, ctx); } static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh *mesh) @@ -952,12 +1107,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * modifyGeometry(md, ctx, geometry_set); - if (ctx->flag & MOD_APPLY_TO_BASE_MESH) { - /* In this case it makes sense to realize instances, otherwise in some cases there might be no - * results when applying the modifier. */ - geometry_set = blender::bke::geometry_set_realize_mesh_for_modifier(geometry_set); - } - Mesh *new_mesh = geometry_set.get_component_for_write().release(); if (new_mesh == nullptr) { return BKE_mesh_new_nomain(0, 0, 0, 0, 0); @@ -972,6 +1121,154 @@ static void modifyGeometrySet(ModifierData *md, modifyGeometry(md, ctx, *geometry_set); } +struct AttributeSearchData { + const geo_log::ModifierLog &modifier_log; + IDProperty &name_property; + bool is_output; +}; + +/* This class must not have a destructor, since it is used by buttons and freed with #MEM_freeN. */ +BLI_STATIC_ASSERT(std::is_trivially_destructible_v, ""); + +static void attribute_search_update_fn(const bContext *UNUSED(C), + void *arg, + const char *str, + uiSearchItems *items, + const bool is_first) +{ + AttributeSearchData *data = static_cast(arg); + + const geo_log::GeometryValueLog *geometry_log = data->is_output ? + data->modifier_log.output_geometry_log() : + data->modifier_log.input_geometry_log(); + if (geometry_log == nullptr) { + return; + } + + Span infos = geometry_log->attributes(); + + /* The shared attribute search code expects a span of pointers, so convert to that. */ + Array info_ptrs(infos.size()); + for (const int i : infos.index_range()) { + info_ptrs[i] = &infos[i]; + } + blender::ui::attribute_search_add_items( + str, data->is_output, info_ptrs.as_span(), items, is_first); +} + +static void attribute_search_exec_fn(bContext *C, void *data_v, void *item_v) +{ + if (item_v == nullptr) { + return; + } + AttributeSearchData &data = *static_cast(data_v); + const GeometryAttributeInfo &item = *static_cast(item_v); + + IDProperty &name_property = data.name_property; + BLI_assert(name_property.type == IDP_STRING); + IDP_AssignString(&name_property, item.name.c_str(), 0); + + ED_undo_push(C, "Assign Attribute Name"); +} + +static void add_attribute_search_button(uiLayout *layout, + const NodesModifierData &nmd, + PointerRNA *md_ptr, + const StringRefNull rna_path_attribute_name, + const bNodeSocket &socket, + const bool is_output) +{ + const geo_log::ModifierLog *log = static_cast(nmd.runtime_eval_log); + if (log == nullptr) { + uiItemR(layout, md_ptr, rna_path_attribute_name.c_str(), 0, "", ICON_NONE); + return; + } + + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = uiDefIconTextButR(block, + UI_BTYPE_SEARCH_MENU, + 0, + ICON_NONE, + "", + 0, + 0, + 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */ + UI_UNIT_Y, + md_ptr, + rna_path_attribute_name.c_str(), + 0, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + + const std::string use_attribute_prop_name = socket.identifier + attribute_name_suffix; + IDProperty *property = IDP_GetPropertyFromGroup(nmd.settings.properties, + use_attribute_prop_name.c_str()); + BLI_assert(property != nullptr); + if (property == nullptr) { + return; + } + + AttributeSearchData *data = OBJECT_GUARDED_NEW(AttributeSearchData, + {*log, *property, is_output}); + + UI_but_func_search_set_results_are_suggestions(but, true); + UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP); + UI_but_func_search_set(but, + nullptr, + attribute_search_update_fn, + static_cast(data), + true, + nullptr, + attribute_search_exec_fn, + nullptr); +} + +static void add_attribute_search_or_value_buttons(uiLayout *layout, + const NodesModifierData &nmd, + PointerRNA *md_ptr, + const bNodeSocket &socket) +{ + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const std::string rna_path = "[\"" + std::string(socket_id_esc) + "\"]"; + const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + + use_attribute_suffix + "\"]"; + const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + + attribute_name_suffix + "\"]"; + + uiLayout *split = uiLayoutSplit(layout, 0.4f, false); + uiLayout *name_row = uiLayoutRow(split, false); + uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(name_row, socket.name, ICON_NONE); + + uiLayout *row = uiLayoutRow(split, true); + + PointerRNA props; + uiItemFullO(row, + "object.geometry_nodes_input_attribute_toggle", + "", + ICON_SPREADSHEET, + nullptr, + WM_OP_INVOKE_DEFAULT, + 0, + &props); + RNA_string_set(&props, "modifier_name", nmd.modifier.name); + RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); + + const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; + if (use_attribute) { + add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, false); + uiItemL(row, "", ICON_BLANK1); + } + else { + uiItemR(row, md_ptr, rna_path.c_str(), 0, "", ICON_NONE); + uiItemDecoratorR(row, md_ptr, rna_path.c_str(), 0); + } +} + /* Drawing the properties manually with #uiItemR instead of #uiDefAutoButsRNA allows using * the node socket identifier for the property names, since they are unique, but also having * the correct label displayed in the UI. */ @@ -979,7 +1276,8 @@ static void draw_property_for_socket(uiLayout *layout, NodesModifierData *nmd, PointerRNA *bmain_ptr, PointerRNA *md_ptr, - const bNodeSocket &socket) + const bNodeSocket &socket, + const int socket_index) { /* The property should be created in #MOD_nodes_update_interface with the correct type. */ IDProperty *property = IDP_GetPropertyFromGroup(nmd->settings.properties, socket.identifier); @@ -1023,41 +1321,42 @@ static void draw_property_for_socket(uiLayout *layout, uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "textures", socket.name, ICON_TEXTURE); break; } + case SOCK_IMAGE: { + uiItemPointerR(layout, md_ptr, rna_path, bmain_ptr, "images", socket.name, ICON_IMAGE); + break; + } default: { - if (socket_type_has_attribute_toggle(socket) && - USER_EXPERIMENTAL_TEST(&U, use_geometry_nodes_fields)) { - const std::string rna_path_use_attribute = "[\"" + std::string(socket_id_esc) + - use_attribute_suffix + "\"]"; - const std::string rna_path_attribute_name = "[\"" + std::string(socket_id_esc) + - attribute_name_suffix + "\"]"; - - uiLayout *row = uiLayoutRow(layout, true); - const int use_attribute = RNA_int_get(md_ptr, rna_path_use_attribute.c_str()) != 0; - if (use_attribute) { - uiItemR(row, md_ptr, rna_path_attribute_name.c_str(), 0, socket.name, ICON_NONE); - } - else { - uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); - } - PointerRNA props; - uiItemFullO(row, - "object.geometry_nodes_input_attribute_toggle", - "", - ICON_SPREADSHEET, - nullptr, - WM_OP_INVOKE_DEFAULT, - 0, - &props); - RNA_string_set(&props, "modifier_name", nmd->modifier.name); - RNA_string_set(&props, "prop_path", rna_path_use_attribute.c_str()); + if (input_has_attribute_toggle(*nmd->node_group, socket_index)) { + add_attribute_search_or_value_buttons(layout, *nmd, md_ptr, socket); } else { - uiItemR(layout, md_ptr, rna_path, 0, socket.name, ICON_NONE); + uiLayout *row = uiLayoutRow(layout, false); + uiItemR(row, md_ptr, rna_path, 0, socket.name, ICON_NONE); + uiItemDecoratorR(row, md_ptr, rna_path, 0); } } } } +static void draw_property_for_output_socket(uiLayout *layout, + const NodesModifierData &nmd, + PointerRNA *md_ptr, + const bNodeSocket &socket) +{ + char socket_id_esc[sizeof(socket.identifier) * 2]; + BLI_str_escape(socket_id_esc, socket.identifier, sizeof(socket_id_esc)); + const std::string rna_path_attribute_name = "[\"" + StringRef(socket_id_esc) + + attribute_name_suffix + "\"]"; + + uiLayout *split = uiLayoutSplit(layout, 0.4f, false); + uiLayout *name_row = uiLayoutRow(split, false); + uiLayoutSetAlignment(name_row, UI_LAYOUT_ALIGN_RIGHT); + uiItemL(name_row, socket.name, ICON_NONE); + + uiLayout *row = uiLayoutRow(split, true); + add_attribute_search_button(row, nmd, md_ptr, rna_path_attribute_name, socket, true); +} + static void panel_draw(const bContext *C, Panel *panel) { uiLayout *layout = panel->layout; @@ -1067,7 +1366,9 @@ static void panel_draw(const bContext *C, Panel *panel) NodesModifierData *nmd = static_cast(ptr->data); uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, true); + /* Decorators are added manually for supported properties because the + * attribute/value toggle requires a manually built layout anyway. */ + uiLayoutSetPropDecorate(layout, false); uiTemplateID(layout, C, @@ -1084,29 +1385,72 @@ static void panel_draw(const bContext *C, Panel *panel) PointerRNA bmain_ptr; RNA_main_pointer_create(bmain, &bmain_ptr); - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { - draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket); + int socket_index; + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &nmd->node_group->inputs, socket_index) { + draw_property_for_socket(layout, nmd, &bmain_ptr, ptr, *socket, socket_index); } } /* Draw node warnings. */ + bool has_legacy_node = false; if (nmd->runtime_eval_log != nullptr) { const geo_log::ModifierLog &log = *static_cast(nmd->runtime_eval_log); - log.foreach_node_log([layout](const geo_log::NodeLog &node_log) { + log.foreach_node_log([&](const geo_log::NodeLog &node_log) { for (const geo_log::NodeWarning &warning : node_log.warnings()) { - if (warning.type != geo_log::NodeWarningType::Info) { + if (warning.type == geo_log::NodeWarningType::Legacy) { + has_legacy_node = true; + } + else if (warning.type != geo_log::NodeWarningType::Info) { uiItemL(layout, warning.message.c_str(), ICON_ERROR); } } }); } + if (has_legacy_node) { + uiLayout *row = uiLayoutRow(layout, false); + uiItemL(row, TIP_("Node tree has legacy node"), ICON_ERROR); + uiLayout *sub = uiLayoutRow(row, false); + uiLayoutSetAlignment(sub, UI_LAYOUT_ALIGN_RIGHT); + uiItemO(sub, "", ICON_VIEWZOOM, "NODE_OT_geometry_node_view_legacy"); + } + modifier_panel_end(layout, ptr); } +static void output_attribute_panel_draw(const bContext *UNUSED(C), Panel *panel) +{ + uiLayout *layout = panel->layout; + + PointerRNA *ptr = modifier_panel_get_property_pointers(panel, nullptr); + NodesModifierData *nmd = static_cast(ptr->data); + + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, true); + + bool has_output_attribute = false; + if (nmd->node_group != nullptr && nmd->settings.properties != nullptr) { + LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->outputs) { + if (socket_type_has_attribute_toggle(*socket)) { + has_output_attribute = true; + draw_property_for_output_socket(layout, *nmd, ptr, *socket); + } + } + } + if (!has_output_attribute) { + uiItemL(layout, TIP_("No group output attributes connected"), ICON_INFO); + } +} + static void panelRegister(ARegionType *region_type) { - modifier_panel_register(region_type, eModifierType_Nodes, panel_draw); + PanelType *panel_type = modifier_panel_register(region_type, eModifierType_Nodes, panel_draw); + modifier_subpanel_register(region_type, + "output_attributes", + N_("Output Attributes"), + nullptr, + output_attribute_panel_draw, + panel_type); } static void blendWrite(BlendWriter *writer, const ModifierData *md) diff --git a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc index f67f7f967c9..70d2bd9c7f5 100644 --- a/source/blender/modifiers/intern/MOD_nodes_evaluator.cc +++ b/source/blender/modifiers/intern/MOD_nodes_evaluator.cc @@ -17,6 +17,7 @@ #include "MOD_nodes_evaluator.hh" #include "NOD_geometry_exec.hh" +#include "NOD_socket_declarations.hh" #include "NOD_type_conversions.hh" #include "DEG_depsgraph_query.h" @@ -26,6 +27,8 @@ #include "FN_generic_value_map.hh" #include "FN_multi_function.hh" +#include "BLT_translation.h" + #include "BLI_enumerable_thread_specific.hh" #include "BLI_stack.hh" #include "BLI_task.h" @@ -123,6 +126,12 @@ struct InputState { * changed by others anymore. */ bool was_ready_for_execution = false; + + /** + * True when this input has to be computed for logging/debugging purposes, regardless of whether + * it is needed for some output. + */ + bool force_compute = false; }; struct OutputState { @@ -319,8 +328,50 @@ static const CPPType *get_socket_cpp_type(const DSocket socket) return get_socket_cpp_type(*socket.socket_ref()); } +/** + * \note This is not supposed to be a long term solution. Eventually we want that nodes can + * specify more complex defaults (other than just single values) in their socket declarations. + */ +static bool get_implicit_socket_input(const SocketRef &socket, void *r_value) +{ + const NodeRef &node = socket.node(); + const nodes::NodeDeclaration *node_declaration = node.declaration(); + if (node_declaration == nullptr) { + return false; + } + const nodes::SocketDeclaration &socket_declaration = *node_declaration->inputs()[socket.index()]; + if (socket_declaration.input_field_type() == nodes::InputSocketFieldType::Implicit) { + const bNode &bnode = *socket.bnode(); + if (socket.typeinfo()->type == SOCK_VECTOR) { + if (bnode.type == GEO_NODE_SET_CURVE_HANDLES) { + StringRef side = ((NodeGeometrySetCurveHandlePositions *)bnode.storage)->mode == + GEO_NODE_CURVE_HANDLE_LEFT ? + "handle_left" : + "handle_right"; + new (r_value) Field(bke::AttributeFieldInput::Create(side)); + return true; + } + new (r_value) Field(bke::AttributeFieldInput::Create("position")); + return true; + } + if (socket.typeinfo()->type == SOCK_INT) { + if (ELEM(bnode.type, FN_NODE_RANDOM_VALUE, GEO_NODE_INSTANCE_ON_POINTS)) { + new (r_value) Field(std::make_shared()); + return true; + } + new (r_value) Field(std::make_shared()); + return true; + } + } + return false; +} + static void get_socket_value(const SocketRef &socket, void *r_value) { + if (get_implicit_socket_input(socket, r_value)) { + return; + } + const bNodeSocketType *typeinfo = socket.typeinfo(); typeinfo->get_geometry_nodes_cpp_value(*socket.bsocket(), r_value); } @@ -452,6 +503,14 @@ class GeometryNodesEvaluator { this->initialize_node_state(item.node, *item.state, allocator); } }); + + /* Mark input sockets that have to be computed. */ + for (const DSocket &socket : params_.force_compute_sockets) { + NodeState &node_state = *node_states_.lookup_key_as(socket.node()).state; + if (socket->is_input()) { + node_state.inputs[socket->index()].force_compute = true; + } + } } void initialize_node_state(const DNode node, NodeState &node_state, LinearAllocator<> &allocator) @@ -509,15 +568,15 @@ class GeometryNodesEvaluator { } /* Count the number of potential users for this socket. */ socket.foreach_target_socket( - [&, this](const DInputSocket target_socket) { + [&, this](const DInputSocket target_socket, + const DOutputSocket::TargetSocketPathInfo &UNUSED(path_info)) { const DNode target_node = target_socket.node(); if (!this->node_states_.contains_as(target_node)) { /* The target node is not computed because it is not computed to the output. */ return; } output_state.potential_users += 1; - }, - {}); + }); if (output_state.potential_users == 0) { /* If it does not have any potential users, it is unused. It might become required again in * `schedule_initial_nodes`. */ @@ -698,7 +757,8 @@ class GeometryNodesEvaluator { return do_execute_node; } - /* A node is finished when it has computed all outputs that may be used. */ + /* A node is finished when it has computed all outputs that may be used have been computed and + * when no input is still forced to be computed. */ bool finish_node_if_possible(LockedNode &locked_node) { if (locked_node.node_state.node_has_finished) { @@ -707,35 +767,41 @@ class GeometryNodesEvaluator { } /* Check if there is any output that might be used but has not been computed yet. */ - bool has_remaining_output = false; for (OutputState &output_state : locked_node.node_state.outputs) { if (output_state.has_been_computed) { continue; } if (output_state.output_usage != ValueUsage::Unused) { - has_remaining_output = true; - break; + return false; } } - if (!has_remaining_output) { - /* If there are no remaining outputs, all the inputs can be destructed and/or can become - * unused. This can also trigger a chain reaction where nodes to the left become finished - * too. */ - for (const int i : locked_node.node->inputs().index_range()) { - const DInputSocket socket = locked_node.node.input(i); - InputState &input_state = locked_node.node_state.inputs[i]; - if (input_state.usage == ValueUsage::Maybe) { - this->set_input_unused(locked_node, socket); - } - else if (input_state.usage == ValueUsage::Required) { - /* The value was required, so it cannot become unused. However, we can destruct the - * value. */ - this->destruct_input_value_if_exists(locked_node, socket); + + /* Check if there is an input that still has to be computed. */ + for (InputState &input_state : locked_node.node_state.inputs) { + if (input_state.force_compute) { + if (!input_state.was_ready_for_execution) { + return false; } } - locked_node.node_state.node_has_finished = true; } - return locked_node.node_state.node_has_finished; + + /* If there are no remaining outputs, all the inputs can be destructed and/or can become + * unused. This can also trigger a chain reaction where nodes to the left become finished + * too. */ + for (const int i : locked_node.node->inputs().index_range()) { + const DInputSocket socket = locked_node.node.input(i); + InputState &input_state = locked_node.node_state.inputs[i]; + if (input_state.usage == ValueUsage::Maybe) { + this->set_input_unused(locked_node, socket); + } + else if (input_state.usage == ValueUsage::Required) { + /* The value was required, so it cannot become unused. However, we can destruct the + * value. */ + this->destruct_input_value_if_exists(locked_node, socket); + } + } + locked_node.node_state.node_has_finished = true; + return true; } bool prepare_node_outputs_for_execution(LockedNode &locked_node) @@ -841,9 +907,9 @@ class GeometryNodesEvaluator { } /* Use the multi-function implementation if it exists. */ - const MultiFunction *multi_function = params_.mf_by_node->try_get(node); - if (multi_function != nullptr) { - this->execute_multi_function_node(node, *multi_function, node_state); + const nodes::NodeMultiFunctions::Item &fn_item = params_.mf_by_node->try_get(node); + if (fn_item.fn != nullptr) { + this->execute_multi_function_node(node, fn_item, node_state); return; } @@ -856,13 +922,25 @@ class GeometryNodesEvaluator { NodeParamsProvider params_provider{*this, node, node_state}; GeoNodeExecParams params{params_provider}; + if (node->idname().find("Legacy") != StringRef::not_found) { + params.error_message_add(geo_log::NodeWarningType::Legacy, + TIP_("Legacy node will be removed before Blender 4.0")); + } bnode.typeinfo->geometry_node_execute(params); } void execute_multi_function_node(const DNode node, - const MultiFunction &fn, + const nodes::NodeMultiFunctions::Item &fn_item, NodeState &node_state) { + if (node->idname().find("Legacy") != StringRef::not_found) { + /* Create geometry nodes params just for creating an error message. */ + NodeParamsProvider params_provider{*this, node, node_state}; + GeoNodeExecParams params{params_provider}; + params.error_message_add(geo_log::NodeWarningType::Legacy, + TIP_("Legacy node will be removed before Blender 4.0")); + } + LinearAllocator<> &allocator = local_allocators_.local(); /* Prepare the inputs for the multi function. */ @@ -880,7 +958,13 @@ class GeometryNodesEvaluator { input_fields.append(std::move(*(GField *)single_value.value)); } - auto operation = std::make_shared(fn, std::move(input_fields)); + std::shared_ptr operation; + if (fn_item.owned_fn) { + operation = std::make_shared(fn_item.owned_fn, std::move(input_fields)); + } + else { + operation = std::make_shared(*fn_item.fn, std::move(input_fields)); + } /* Forward outputs. */ int output_index = 0; @@ -1063,7 +1147,7 @@ class GeometryNodesEvaluator { return; } bool will_be_triggered_by_other_node = false; - for (const DSocket origin_socket : origin_sockets) { + for (const DSocket &origin_socket : origin_sockets) { if (origin_socket->is_input()) { /* Load the value directly from the origin socket. In most cases this is an unlinked * group input. */ @@ -1173,43 +1257,61 @@ class GeometryNodesEvaluator { { BLI_assert(value_to_forward.get() != nullptr); - Vector sockets_to_log_to; - sockets_to_log_to.append(from_socket); - - Vector to_sockets; - auto handle_target_socket_fn = [&, this](const DInputSocket to_socket) { - if (this->should_forward_to_socket(to_socket)) { - to_sockets.append(to_socket); - } - }; - auto handle_skipped_socket_fn = [&](const DSocket socket) { - sockets_to_log_to.append(socket); - }; - from_socket.foreach_target_socket(handle_target_socket_fn, handle_skipped_socket_fn); - LinearAllocator<> &allocator = local_allocators_.local(); - const CPPType &from_type = *value_to_forward.type(); - Vector to_sockets_same_type; - for (const DInputSocket &to_socket : to_sockets) { - const CPPType &to_type = *get_socket_cpp_type(to_socket); - if (from_type == to_type) { - /* All target sockets that do not need a conversion will be handled afterwards. */ - to_sockets_same_type.append(to_socket); - /* Multi input socket values are logged once all values are available. */ - if (!to_socket->is_multi_input_socket()) { - sockets_to_log_to.append(to_socket); - } - continue; - } - this->forward_to_socket_with_different_type( - allocator, value_to_forward, from_socket, to_socket, to_type); - } - - this->log_socket_value(sockets_to_log_to, value_to_forward); + Vector log_original_value_sockets; + Vector forward_original_value_sockets; + log_original_value_sockets.append(from_socket); + from_socket.foreach_target_socket( + [&](const DInputSocket to_socket, const DOutputSocket::TargetSocketPathInfo &path_info) { + if (!this->should_forward_to_socket(to_socket)) { + return; + } + BLI_assert(to_socket == path_info.sockets.last()); + GMutablePointer current_value = value_to_forward; + for (const DSocket &next_socket : path_info.sockets) { + const DNode next_node = next_socket.node(); + const bool is_last_socket = to_socket == next_socket; + const bool do_conversion_if_necessary = is_last_socket || + next_node->is_group_output_node() || + (next_node->is_group_node() && + !next_node->is_muted()); + if (do_conversion_if_necessary) { + const CPPType &next_type = *get_socket_cpp_type(next_socket); + if (*current_value.type() != next_type) { + void *buffer = allocator.allocate(next_type.size(), next_type.alignment()); + this->convert_value(*current_value.type(), next_type, current_value.get(), buffer); + if (current_value.get() != value_to_forward.get()) { + current_value.destruct(); + } + current_value = {next_type, buffer}; + } + } + if (current_value.get() == value_to_forward.get()) { + /* Log the original value at the current socket. */ + log_original_value_sockets.append(next_socket); + } + else { + /* Multi-input sockets are logged when all values are available. */ + if (!(next_socket->is_input() && next_socket->as_input().is_multi_input_socket())) { + /* Log the converted value at the socket. */ + this->log_socket_value({next_socket}, current_value); + } + } + } + if (current_value.get() == value_to_forward.get()) { + /* The value has not been converted, so forward the original value. */ + forward_original_value_sockets.append(to_socket); + } + else { + /* The value has been converted. */ + this->add_value_to_input_socket(to_socket, from_socket, current_value); + } + }); + this->log_socket_value(log_original_value_sockets, value_to_forward); this->forward_to_sockets_with_same_type( - allocator, to_sockets_same_type, value_to_forward, from_socket); + allocator, forward_original_value_sockets, value_to_forward, from_socket); } bool should_forward_to_socket(const DInputSocket socket) @@ -1228,27 +1330,6 @@ class GeometryNodesEvaluator { return target_input_state.usage != ValueUsage::Unused; } - void forward_to_socket_with_different_type(LinearAllocator<> &allocator, - const GPointer value_to_forward, - const DOutputSocket from_socket, - const DInputSocket to_socket, - const CPPType &to_type) - { - const CPPType &from_type = *value_to_forward.type(); - - /* Allocate a buffer for the converted value. */ - void *buffer = allocator.allocate(to_type.size(), to_type.alignment()); - GMutablePointer value{to_type, buffer}; - - this->convert_value(from_type, to_type, value_to_forward.get(), buffer); - - /* Multi input socket values are logged once all values are available. */ - if (!to_socket->is_multi_input_socket()) { - this->log_socket_value({to_socket}, value); - } - this->add_value_to_input_socket(to_socket, from_socket, value); - } - void forward_to_sockets_with_same_type(LinearAllocator<> &allocator, Span to_sockets, GMutablePointer value_to_forward, @@ -1371,6 +1452,7 @@ class GeometryNodesEvaluator { } void *converted_buffer = allocator.allocate(required_type.size(), required_type.alignment()); this->convert_value(type, required_type, buffer, converted_buffer); + type.destruct(buffer); return {required_type, converted_buffer}; } diff --git a/source/blender/modifiers/intern/MOD_ocean.c b/source/blender/modifiers/intern/MOD_ocean.c index ff1055eff3b..4566cf93dd7 100644 --- a/source/blender/modifiers/intern/MOD_ocean.c +++ b/source/blender/modifiers/intern/MOD_ocean.c @@ -552,7 +552,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) modifier_panel_end(layout, ptr); #else /* WITH_OCEANSIM */ - uiItemL(layout, IFACE_("Built without Ocean modifier"), ICON_NONE); + uiItemL(layout, TIP_("Built without Ocean modifier"), ICON_NONE); #endif /* WITH_OCEANSIM */ } diff --git a/source/blender/modifiers/intern/MOD_particlesystem.c b/source/blender/modifiers/intern/MOD_particlesystem.c index 71fc7f3e424..2a4cc1c2747 100644 --- a/source/blender/modifiers/intern/MOD_particlesystem.c +++ b/source/blender/modifiers/intern/MOD_particlesystem.c @@ -277,7 +277,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) ModifierData *md = (ModifierData *)ptr->data; ParticleSystem *psys = ((ParticleSystemModifierData *)md)->psys; - uiItemL(layout, IFACE_("Settings are in the particle tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are in the particle tab"), ICON_NONE); if (!(ob->mode & OB_MODE_PARTICLE_EDIT)) { if (ELEM(psys->part->ren_as, PART_DRAW_GR, PART_DRAW_OB)) { diff --git a/source/blender/modifiers/intern/MOD_remesh.c b/source/blender/modifiers/intern/MOD_remesh.c index fef1f76c051..937a73fddd9 100644 --- a/source/blender/modifiers/intern/MOD_remesh.c +++ b/source/blender/modifiers/intern/MOD_remesh.c @@ -273,7 +273,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) modifier_panel_end(layout, ptr); #else /* WITH_MOD_REMESH */ - uiItemL(layout, IFACE_("Built without Remesh modifier"), ICON_NONE); + uiItemL(layout, TIP_("Built without Remesh modifier"), ICON_NONE); #endif /* WITH_MOD_REMESH */ } diff --git a/source/blender/modifiers/intern/MOD_skin.c b/source/blender/modifiers/intern/MOD_skin.c index 7d90935f678..07ce819e91c 100644 --- a/source/blender/modifiers/intern/MOD_skin.c +++ b/source/blender/modifiers/intern/MOD_skin.c @@ -1479,7 +1479,7 @@ static void quad_from_tris(BMEdge *e, BMFace *adj[2], BMVert *ndx[4]) ndx[j] = tri[0][i]; /* When the triangle edge cuts across our quad-to-be, * throw in the second triangle's vertex */ - if ((tri[0][i] == e->v1 || tri[0][i] == e->v2) && + if ((ELEM(tri[0][i], e->v1, e->v2)) && (tri[0][(i + 1) % 3] == e->v1 || tri[0][(i + 1) % 3] == e->v2)) { j++; ndx[j] = opp; diff --git a/source/blender/modifiers/intern/MOD_softbody.c b/source/blender/modifiers/intern/MOD_softbody.c index 4187f9087a0..46e960e10d4 100644 --- a/source/blender/modifiers/intern/MOD_softbody.c +++ b/source/blender/modifiers/intern/MOD_softbody.c @@ -92,7 +92,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_solidify_extrude.c b/source/blender/modifiers/intern/MOD_solidify_extrude.c index 8f9aa86e561..54a508ff5e2 100644 --- a/source/blender/modifiers/intern/MOD_solidify_extrude.c +++ b/source/blender/modifiers/intern/MOD_solidify_extrude.c @@ -1043,8 +1043,8 @@ Mesh *MOD_solidify_extrude_modifyMesh(ModifierData *md, const ModifierEvalContex #define SOLIDIFY_SIDE_NORMALS #ifdef SOLIDIFY_SIDE_NORMALS - /* Note that, due to the code setting cd_dirty_vert a few lines above, - * do_side_normals is always false. - Sybren */ + /* NOTE(@sybren): due to the code setting cd_dirty_vert a few lines above, + * do_side_normals is always false. */ const bool do_side_normals = !(result->runtime.cd_dirty_vert & CD_MASK_NORMAL); /* annoying to allocate these since we only need the edge verts, */ float(*edge_vert_nos)[3] = do_side_normals ? diff --git a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c index f654b69841e..d4aaefcfe05 100644 --- a/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c +++ b/source/blender/modifiers/intern/MOD_solidify_nonmanifold.c @@ -1168,9 +1168,9 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, add_index++; } if (last_split > split) { - const uint size = (split + edges_len) - (uint)last_split; + const uint edges_len_group = (split + edges_len) - (uint)last_split; NewEdgeRef **edges = MEM_malloc_arrayN( - size, sizeof(*edges), "edge_group split in solidify"); + edges_len_group, sizeof(*edges), "edge_group split in solidify"); memcpy(edges, g.edges + last_split, (edges_len - (uint)last_split) * sizeof(*edges)); @@ -1180,7 +1180,7 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, edge_groups[j + add_index] = (EdgeGroup){ .valid = true, .edges = edges, - .edges_len = size, + .edges_len = edges_len_group, .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, .is_orig_closed = g.is_orig_closed, .is_even_split = is_even_split, @@ -1193,14 +1193,14 @@ Mesh *MOD_solidify_nonmanifold_modifyMesh(ModifierData *md, }; } else { - const uint size = split - (uint)last_split; + const uint edges_len_group = split - (uint)last_split; NewEdgeRef **edges = MEM_malloc_arrayN( - size, sizeof(*edges), "edge_group split in solidify"); - memcpy(edges, g.edges + last_split, size * sizeof(*edges)); + edges_len_group, sizeof(*edges), "edge_group split in solidify"); + memcpy(edges, g.edges + last_split, edges_len_group * sizeof(*edges)); edge_groups[j + add_index] = (EdgeGroup){ .valid = true, .edges = edges, - .edges_len = size, + .edges_len = edges_len_group, .open_face_edge = MOD_SOLIDIFY_EMPTY_TAG, .is_orig_closed = g.is_orig_closed, .is_even_split = is_even_split, diff --git a/source/blender/modifiers/intern/MOD_subsurf.c b/source/blender/modifiers/intern/MOD_subsurf.c index db0b769684e..7470f2abb15 100644 --- a/source/blender/modifiers/intern/MOD_subsurf.c +++ b/source/blender/modifiers/intern/MOD_subsurf.c @@ -27,6 +27,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "BLT_translation.h" @@ -414,8 +415,12 @@ static void panel_draw(const bContext *C, Panel *panel) float preview = MAX2(RNA_float_get(&cycles_ptr, "preview_dicing_rate") * RNA_float_get(&ob_cycles_ptr, "dicing_rate"), 0.1f); - char output[64]; - snprintf(output, 64, "Final Scale: Render %.2f px, Viewport %.2f px", render, preview); + char output[256]; + BLI_snprintf(output, + sizeof(output), + TIP_("Final Scale: Render %.2f px, Viewport %.2f px"), + render, + preview); uiItemL(layout, output, ICON_NONE); uiItemS(layout); diff --git a/source/blender/modifiers/intern/MOD_surface.c b/source/blender/modifiers/intern/MOD_surface.c index 3f2d0a06db8..c8be2bd2829 100644 --- a/source/blender/modifiers/intern/MOD_surface.c +++ b/source/blender/modifiers/intern/MOD_surface.c @@ -207,7 +207,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) PointerRNA *ptr = modifier_panel_get_property_pointers(panel, NULL); - uiItemL(layout, IFACE_("Settings are inside the Physics tab"), ICON_NONE); + uiItemL(layout, TIP_("Settings are inside the Physics tab"), ICON_NONE); modifier_panel_end(layout, ptr); } diff --git a/source/blender/modifiers/intern/MOD_ui_common.c b/source/blender/modifiers/intern/MOD_ui_common.c index 6239ee45e59..9937a2342c3 100644 --- a/source/blender/modifiers/intern/MOD_ui_common.c +++ b/source/blender/modifiers/intern/MOD_ui_common.c @@ -108,7 +108,7 @@ void modifier_panel_end(uiLayout *layout, PointerRNA *ptr) ModifierData *md = ptr->data; if (md->error) { uiLayout *row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_(md->error), ICON_ERROR); + uiItemL(row, TIP_(md->error), ICON_ERROR); } } @@ -448,7 +448,7 @@ PanelType *modifier_panel_register(ARegionType *region_type, ModifierType type, /* Give the panel the special flag that says it was built here and corresponds to a * modifier rather than a #PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = modifier_reorder; panel_type->get_list_data_expand_flag = get_modifier_expand_flag; panel_type->set_list_data_expand_flag = set_modifier_expand_flag; @@ -482,7 +482,7 @@ PanelType *modifier_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = modifier_ui_poll; - panel_type->flag = (PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX); + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index b741461f820..f4ca9f51b1b 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -33,6 +33,7 @@ set(INC ../bmesh ../depsgraph ../functions + ../geometry ../gpu ../imbuf ../makesdna @@ -45,133 +46,169 @@ set(INC set(SRC - composite/nodes/node_composite_alphaOver.c - composite/nodes/node_composite_antialiasing.c - composite/nodes/node_composite_bilateralblur.c - composite/nodes/node_composite_blur.c - composite/nodes/node_composite_bokehblur.c - composite/nodes/node_composite_bokehimage.c - composite/nodes/node_composite_boxmask.c - composite/nodes/node_composite_brightness.c - composite/nodes/node_composite_channelMatte.c - composite/nodes/node_composite_chromaMatte.c - composite/nodes/node_composite_colorMatte.c - composite/nodes/node_composite_colorSpill.c - composite/nodes/node_composite_colorbalance.c - composite/nodes/node_composite_colorcorrection.c - composite/nodes/node_composite_common.c - composite/nodes/node_composite_composite.c - composite/nodes/node_composite_cornerpin.c - composite/nodes/node_composite_crop.c + composite/nodes/node_composite_alphaOver.cc + composite/nodes/node_composite_antialiasing.cc + composite/nodes/node_composite_bilateralblur.cc + composite/nodes/node_composite_blur.cc + composite/nodes/node_composite_bokehblur.cc + composite/nodes/node_composite_bokehimage.cc + composite/nodes/node_composite_boxmask.cc + composite/nodes/node_composite_brightness.cc + composite/nodes/node_composite_channelMatte.cc + composite/nodes/node_composite_chromaMatte.cc + composite/nodes/node_composite_colorMatte.cc + composite/nodes/node_composite_colorSpill.cc + composite/nodes/node_composite_colorbalance.cc + composite/nodes/node_composite_colorcorrection.cc + composite/nodes/node_composite_common.cc + composite/nodes/node_composite_composite.cc + composite/nodes/node_composite_cornerpin.cc + composite/nodes/node_composite_crop.cc composite/nodes/node_composite_cryptomatte.cc - composite/nodes/node_composite_curves.c - composite/nodes/node_composite_defocus.c - composite/nodes/node_composite_denoise.c - composite/nodes/node_composite_despeckle.c - composite/nodes/node_composite_diffMatte.c - composite/nodes/node_composite_dilate.c - composite/nodes/node_composite_directionalblur.c - composite/nodes/node_composite_displace.c - composite/nodes/node_composite_distanceMatte.c - composite/nodes/node_composite_doubleEdgeMask.c - composite/nodes/node_composite_ellipsemask.c - composite/nodes/node_composite_exposure.c - composite/nodes/node_composite_filter.c - composite/nodes/node_composite_flip.c - composite/nodes/node_composite_gamma.c - composite/nodes/node_composite_glare.c - composite/nodes/node_composite_hueSatVal.c - composite/nodes/node_composite_huecorrect.c - composite/nodes/node_composite_idMask.c - composite/nodes/node_composite_image.c - composite/nodes/node_composite_inpaint.c - composite/nodes/node_composite_invert.c - composite/nodes/node_composite_keying.c - composite/nodes/node_composite_keyingscreen.c - composite/nodes/node_composite_lensdist.c - composite/nodes/node_composite_levels.c - composite/nodes/node_composite_lummaMatte.c - composite/nodes/node_composite_mapRange.c - composite/nodes/node_composite_mapUV.c - composite/nodes/node_composite_mapValue.c - composite/nodes/node_composite_mask.c - composite/nodes/node_composite_math.c - composite/nodes/node_composite_mixrgb.c - composite/nodes/node_composite_movieclip.c - composite/nodes/node_composite_moviedistortion.c - composite/nodes/node_composite_normal.c - composite/nodes/node_composite_normalize.c - composite/nodes/node_composite_outputFile.c - composite/nodes/node_composite_pixelate.c - composite/nodes/node_composite_planetrackdeform.c - composite/nodes/node_composite_posterize.c - composite/nodes/node_composite_premulkey.c - composite/nodes/node_composite_rgb.c - composite/nodes/node_composite_rotate.c - composite/nodes/node_composite_scale.c - composite/nodes/node_composite_sepcombHSVA.c - composite/nodes/node_composite_sepcombRGBA.c - composite/nodes/node_composite_sepcombYCCA.c - composite/nodes/node_composite_sepcombYUVA.c - composite/nodes/node_composite_setalpha.c - composite/nodes/node_composite_splitViewer.c - composite/nodes/node_composite_stabilize2d.c - composite/nodes/node_composite_sunbeams.c - composite/nodes/node_composite_switch.c - composite/nodes/node_composite_switchview.c - composite/nodes/node_composite_texture.c - composite/nodes/node_composite_tonemap.c - composite/nodes/node_composite_trackpos.c - composite/nodes/node_composite_transform.c - composite/nodes/node_composite_translate.c - composite/nodes/node_composite_valToRgb.c - composite/nodes/node_composite_value.c - composite/nodes/node_composite_vecBlur.c - composite/nodes/node_composite_viewer.c - composite/nodes/node_composite_zcombine.c + composite/nodes/node_composite_curves.cc + composite/nodes/node_composite_defocus.cc + composite/nodes/node_composite_denoise.cc + composite/nodes/node_composite_despeckle.cc + composite/nodes/node_composite_diffMatte.cc + composite/nodes/node_composite_dilate.cc + composite/nodes/node_composite_directionalblur.cc + composite/nodes/node_composite_displace.cc + composite/nodes/node_composite_distanceMatte.cc + composite/nodes/node_composite_doubleEdgeMask.cc + composite/nodes/node_composite_ellipsemask.cc + composite/nodes/node_composite_exposure.cc + composite/nodes/node_composite_filter.cc + composite/nodes/node_composite_flip.cc + composite/nodes/node_composite_gamma.cc + composite/nodes/node_composite_glare.cc + composite/nodes/node_composite_hueSatVal.cc + composite/nodes/node_composite_huecorrect.cc + composite/nodes/node_composite_idMask.cc + composite/nodes/node_composite_image.cc + composite/nodes/node_composite_inpaint.cc + composite/nodes/node_composite_invert.cc + composite/nodes/node_composite_keying.cc + composite/nodes/node_composite_keyingscreen.cc + composite/nodes/node_composite_lensdist.cc + composite/nodes/node_composite_levels.cc + composite/nodes/node_composite_lummaMatte.cc + composite/nodes/node_composite_mapRange.cc + composite/nodes/node_composite_mapUV.cc + composite/nodes/node_composite_mapValue.cc + composite/nodes/node_composite_mask.cc + composite/nodes/node_composite_math.cc + composite/nodes/node_composite_mixrgb.cc + composite/nodes/node_composite_movieclip.cc + composite/nodes/node_composite_moviedistortion.cc + composite/nodes/node_composite_normal.cc + composite/nodes/node_composite_normalize.cc + composite/nodes/node_composite_outputFile.cc + composite/nodes/node_composite_pixelate.cc + composite/nodes/node_composite_planetrackdeform.cc + composite/nodes/node_composite_posterize.cc + composite/nodes/node_composite_premulkey.cc + composite/nodes/node_composite_rgb.cc + composite/nodes/node_composite_rotate.cc + composite/nodes/node_composite_scale.cc + composite/nodes/node_composite_sepcombHSVA.cc + composite/nodes/node_composite_sepcombRGBA.cc + composite/nodes/node_composite_sepcombYCCA.cc + composite/nodes/node_composite_sepcombYUVA.cc + composite/nodes/node_composite_setalpha.cc + composite/nodes/node_composite_splitViewer.cc + composite/nodes/node_composite_stabilize2d.cc + composite/nodes/node_composite_sunbeams.cc + composite/nodes/node_composite_switch.cc + composite/nodes/node_composite_switchview.cc + composite/nodes/node_composite_texture.cc + composite/nodes/node_composite_tonemap.cc + composite/nodes/node_composite_trackpos.cc + composite/nodes/node_composite_transform.cc + composite/nodes/node_composite_translate.cc + composite/nodes/node_composite_valToRgb.cc + composite/nodes/node_composite_value.cc + composite/nodes/node_composite_vecBlur.cc + composite/nodes/node_composite_viewer.cc + composite/nodes/node_composite_zcombine.cc - composite/node_composite_tree.c - composite/node_composite_util.c + composite/node_composite_tree.cc + composite/node_composite_util.cc + function/nodes/legacy/node_fn_random_float.cc + + function/nodes/node_fn_align_euler_to_vector.cc function/nodes/node_fn_boolean_math.cc function/nodes/node_fn_float_compare.cc function/nodes/node_fn_float_to_int.cc + function/nodes/node_fn_input_bool.cc + function/nodes/node_fn_input_color.cc + function/nodes/node_fn_input_int.cc + function/nodes/node_fn_input_special_characters.cc function/nodes/node_fn_input_string.cc function/nodes/node_fn_input_vector.cc - function/nodes/node_fn_random_float.cc + function/nodes/node_fn_random_value.cc + function/nodes/node_fn_replace_string.cc + function/nodes/node_fn_rotate_euler.cc + function/nodes/node_fn_slice_string.cc + function/nodes/node_fn_string_length.cc + function/nodes/node_fn_value_to_string.cc function/node_function_util.cc + geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc + geometry/nodes/legacy/node_geo_attribute_clamp.cc + geometry/nodes/legacy/node_geo_attribute_color_ramp.cc + geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc + geometry/nodes/legacy/node_geo_attribute_compare.cc + geometry/nodes/legacy/node_geo_attribute_convert.cc + geometry/nodes/legacy/node_geo_attribute_curve_map.cc + geometry/nodes/legacy/node_geo_attribute_fill.cc + geometry/nodes/legacy/node_geo_attribute_map_range.cc + geometry/nodes/legacy/node_geo_attribute_math.cc + geometry/nodes/legacy/node_geo_attribute_mix.cc + geometry/nodes/legacy/node_geo_attribute_proximity.cc + geometry/nodes/legacy/node_geo_attribute_randomize.cc + geometry/nodes/legacy/node_geo_attribute_sample_texture.cc + geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc + geometry/nodes/legacy/node_geo_attribute_transfer.cc + geometry/nodes/legacy/node_geo_attribute_vector_math.cc + geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc + geometry/nodes/legacy/node_geo_curve_endpoints.cc + geometry/nodes/legacy/node_geo_curve_reverse.cc + geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc + geometry/nodes/legacy/node_geo_curve_set_handles.cc + geometry/nodes/legacy/node_geo_curve_spline_type.cc + geometry/nodes/legacy/node_geo_curve_subdivide.cc + geometry/nodes/legacy/node_geo_curve_to_points.cc + geometry/nodes/legacy/node_geo_delete_geometry.cc + geometry/nodes/legacy/node_geo_edge_split.cc geometry/nodes/legacy/node_geo_material_assign.cc + geometry/nodes/legacy/node_geo_mesh_to_curve.cc + geometry/nodes/legacy/node_geo_point_distribute.cc + geometry/nodes/legacy/node_geo_point_instance.cc + geometry/nodes/legacy/node_geo_point_rotate.cc + geometry/nodes/legacy/node_geo_point_scale.cc + geometry/nodes/legacy/node_geo_point_separate.cc + geometry/nodes/legacy/node_geo_point_translate.cc + geometry/nodes/legacy/node_geo_points_to_volume.cc + geometry/nodes/legacy/node_geo_raycast.cc geometry/nodes/legacy/node_geo_select_by_material.cc + geometry/nodes/legacy/node_geo_subdivision_surface.cc + geometry/nodes/legacy/node_geo_volume_to_mesh.cc - geometry/nodes/node_geo_align_rotation_to_vector.cc geometry/nodes/node_geo_attribute_capture.cc - geometry/nodes/node_geo_attribute_clamp.cc - geometry/nodes/node_geo_attribute_color_ramp.cc - geometry/nodes/node_geo_attribute_combine_xyz.cc - geometry/nodes/node_geo_attribute_compare.cc - geometry/nodes/node_geo_attribute_convert.cc - geometry/nodes/node_geo_attribute_curve_map.cc - geometry/nodes/node_geo_attribute_fill.cc - geometry/nodes/node_geo_attribute_map_range.cc - geometry/nodes/node_geo_attribute_math.cc - geometry/nodes/node_geo_attribute_mix.cc - geometry/nodes/node_geo_attribute_proximity.cc - geometry/nodes/node_geo_attribute_randomize.cc geometry/nodes/node_geo_attribute_remove.cc - geometry/nodes/node_geo_attribute_sample_texture.cc - geometry/nodes/node_geo_attribute_separate_xyz.cc - geometry/nodes/node_geo_attribute_transfer.cc - geometry/nodes/node_geo_attribute_vector_math.cc - geometry/nodes/node_geo_attribute_vector_rotate.cc + geometry/nodes/node_geo_attribute_statistic.cc geometry/nodes/node_geo_boolean.cc geometry/nodes/node_geo_bounding_box.cc geometry/nodes/node_geo_collection_info.cc geometry/nodes/node_geo_common.cc geometry/nodes/node_geo_convex_hull.cc - geometry/nodes/node_geo_curve_endpoints.cc + geometry/nodes/node_geo_curve_endpoint_selection.cc geometry/nodes/node_geo_curve_fill.cc + geometry/nodes/node_geo_curve_fillet.cc + geometry/nodes/node_geo_curve_handle_type_selection.cc geometry/nodes/node_geo_curve_length.cc + geometry/nodes/node_geo_curve_parameter.cc geometry/nodes/node_geo_curve_primitive_bezier_segment.cc geometry/nodes/node_geo_curve_primitive_circle.cc geometry/nodes/node_geo_curve_primitive_line.cc @@ -181,7 +218,7 @@ set(SRC geometry/nodes/node_geo_curve_primitive_star.cc geometry/nodes/node_geo_curve_resample.cc geometry/nodes/node_geo_curve_reverse.cc - geometry/nodes/node_geo_curve_select_by_handle_type.cc + geometry/nodes/node_geo_curve_sample.cc geometry/nodes/node_geo_curve_set_handles.cc geometry/nodes/node_geo_curve_spline_type.cc geometry/nodes/node_geo_curve_subdivide.cc @@ -189,14 +226,27 @@ set(SRC geometry/nodes/node_geo_curve_to_points.cc geometry/nodes/node_geo_curve_trim.cc geometry/nodes/node_geo_delete_geometry.cc + geometry/nodes/node_geo_distribute_points_on_faces.cc geometry/nodes/node_geo_edge_split.cc + geometry/nodes/node_geo_image_texture.cc + geometry/nodes/node_geo_input_curve_handles.cc + geometry/nodes/node_geo_input_curve_tilt.cc + geometry/nodes/node_geo_input_id.cc + geometry/nodes/node_geo_input_index.cc + geometry/nodes/node_geo_input_material_index.cc geometry/nodes/node_geo_input_material.cc geometry/nodes/node_geo_input_normal.cc geometry/nodes/node_geo_input_position.cc - geometry/nodes/node_geo_input_index.cc + geometry/nodes/node_geo_input_radius.cc + geometry/nodes/node_geo_input_shade_smooth.cc + geometry/nodes/node_geo_input_spline_cyclic.cc + geometry/nodes/node_geo_input_spline_length.cc + geometry/nodes/node_geo_input_spline_resolution.cc + geometry/nodes/node_geo_input_tangent.cc + geometry/nodes/node_geo_instance_on_points.cc + geometry/nodes/node_geo_instances_to_points.cc geometry/nodes/node_geo_is_viewport.cc geometry/nodes/node_geo_join_geometry.cc - geometry/nodes/node_geo_material_assign.cc geometry/nodes/node_geo_material_replace.cc geometry/nodes/node_geo_material_selection.cc geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -209,23 +259,39 @@ set(SRC geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc geometry/nodes/node_geo_mesh_subdivide.cc geometry/nodes/node_geo_mesh_to_curve.cc + geometry/nodes/node_geo_mesh_to_points.cc geometry/nodes/node_geo_object_info.cc - geometry/nodes/node_geo_point_distribute.cc - geometry/nodes/node_geo_point_instance.cc - geometry/nodes/node_geo_point_rotate.cc - geometry/nodes/node_geo_point_scale.cc - geometry/nodes/node_geo_point_separate.cc - geometry/nodes/node_geo_point_translate.cc + geometry/nodes/node_geo_points_to_vertices.cc geometry/nodes/node_geo_points_to_volume.cc + geometry/nodes/node_geo_proximity.cc geometry/nodes/node_geo_raycast.cc + geometry/nodes/node_geo_realize_instances.cc + geometry/nodes/node_geo_rotate_instances.cc + geometry/nodes/node_geo_scale_instances.cc geometry/nodes/node_geo_separate_components.cc + geometry/nodes/node_geo_separate_geometry.cc + geometry/nodes/node_geo_set_curve_handles.cc + geometry/nodes/node_geo_set_curve_radius.cc + geometry/nodes/node_geo_set_curve_tilt.cc + geometry/nodes/node_geo_set_id.cc + geometry/nodes/node_geo_set_material_index.cc + geometry/nodes/node_geo_set_material.cc + geometry/nodes/node_geo_set_point_radius.cc geometry/nodes/node_geo_set_position.cc + geometry/nodes/node_geo_set_shade_smooth.cc + geometry/nodes/node_geo_set_spline_cyclic.cc + geometry/nodes/node_geo_set_spline_resolution.cc + geometry/nodes/node_geo_string_join.cc + geometry/nodes/node_geo_string_to_curves.cc geometry/nodes/node_geo_subdivision_surface.cc geometry/nodes/node_geo_switch.cc + geometry/nodes/node_geo_transfer_attribute.cc geometry/nodes/node_geo_transform.cc + geometry/nodes/node_geo_translate_instances.cc geometry/nodes/node_geo_triangulate.cc geometry/nodes/node_geo_viewer.cc geometry/nodes/node_geo_volume_to_mesh.cc + geometry/node_geometry_exec.cc geometry/node_geometry_tree.cc geometry/node_geometry_util.cc @@ -291,19 +357,19 @@ set(SRC shader/nodes/node_shader_squeeze.c shader/nodes/node_shader_subsurface_scattering.c shader/nodes/node_shader_tangent.c - shader/nodes/node_shader_tex_brick.c - shader/nodes/node_shader_tex_checker.c + shader/nodes/node_shader_tex_brick.cc + shader/nodes/node_shader_tex_checker.cc shader/nodes/node_shader_tex_coord.c shader/nodes/node_shader_tex_environment.c shader/nodes/node_shader_tex_gradient.cc - shader/nodes/node_shader_tex_image.c - shader/nodes/node_shader_tex_magic.c + shader/nodes/node_shader_tex_image.cc + shader/nodes/node_shader_tex_magic.cc shader/nodes/node_shader_tex_musgrave.cc shader/nodes/node_shader_tex_noise.cc shader/nodes/node_shader_tex_pointdensity.c shader/nodes/node_shader_tex_sky.c shader/nodes/node_shader_tex_voronoi.cc - shader/nodes/node_shader_tex_wave.c + shader/nodes/node_shader_tex_wave.cc shader/nodes/node_shader_tex_white_noise.cc shader/nodes/node_shader_uvAlongStroke.c shader/nodes/node_shader_uvmap.c @@ -350,20 +416,21 @@ set(SRC texture/node_texture_util.c intern/derived_node_tree.cc + intern/extern_implementations.cc intern/geometry_nodes_eval_log.cc intern/math_functions.cc - intern/node_common.c + intern/node_common.cc + intern/node_declaration.cc intern/node_exec.cc intern/node_geometry_exec.cc intern/node_multi_function.cc - intern/node_declaration.cc - intern/node_socket_declarations.cc intern/node_socket.cc + intern/node_socket_declarations.cc intern/node_tree_ref.cc intern/node_util.c intern/type_conversions.cc - composite/node_composite_util.h + composite/node_composite_util.hh function/node_function_util.hh shader/node_shader_util.h geometry/node_geometry_util.hh @@ -379,10 +446,11 @@ set(SRC NOD_math_functions.hh NOD_multi_function.hh NOD_node_declaration.hh - NOD_socket_declarations.hh NOD_node_tree_ref.hh NOD_shader.h NOD_socket.h + NOD_socket_declarations.hh + NOD_socket_declarations_geometry.hh NOD_static_types.h NOD_texture.h NOD_type_conversions.hh @@ -394,6 +462,7 @@ set(SRC set(LIB bf_bmesh bf_functions + bf_geometry bf_intern_sky ) diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 2cbbd31c97a..d243577f68d 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -145,7 +145,7 @@ void node_cmp_rlayers_register_pass(struct bNodeTree *ntree, struct Scene *scene, struct ViewLayer *view_layer, const char *name, - int type); + eNodeSocketDatatype type); const char *node_cmp_rlayers_sock_to_pass(int sock_index); void register_node_type_cmp_custom_group(bNodeType *ntype); diff --git a/source/blender/nodes/NOD_derived_node_tree.hh b/source/blender/nodes/NOD_derived_node_tree.hh index 60d84463a82..895f7ef6d5b 100644 --- a/source/blender/nodes/NOD_derived_node_tree.hh +++ b/source/blender/nodes/NOD_derived_node_tree.hh @@ -158,8 +158,19 @@ class DOutputSocket : public DSocket { DInputSocket get_corresponding_group_node_input() const; DInputSocket get_active_corresponding_group_output_socket() const; - void foreach_target_socket(FunctionRef target_fn, - FunctionRef skipped_fn) const; + struct TargetSocketPathInfo { + /** All sockets on the path from the current to the final target sockets, excluding `this`. */ + Vector sockets; + }; + + using ForeachTargetSocketFn = + FunctionRef; + + void foreach_target_socket(ForeachTargetSocketFn target_fn) const; + + private: + void foreach_target_socket(ForeachTargetSocketFn target_fn, + TargetSocketPathInfo &path_info) const; }; class DerivedNodeTree { @@ -202,9 +213,9 @@ using nodes::DSocket; using nodes::DTreeContext; } // namespace derived_node_tree_types -/* -------------------------------------------------------------------- - * DTreeContext inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DTreeContext Inline Methods + * \{ */ inline const NodeTreeRef &DTreeContext::tree() const { @@ -235,10 +246,11 @@ inline bool DTreeContext::is_root() const { return parent_context_ == nullptr; } +/** \} */ -/* -------------------------------------------------------------------- - * DNode inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DNode Inline Methods + * \{ */ inline DNode::DNode(const DTreeContext *context, const NodeRef *node_ref) : context_(context), node_ref_(node_ref) @@ -300,10 +312,11 @@ inline DOutputSocket DNode::output_by_identifier(StringRef identifier) const { return {context_, &node_ref_->output_by_identifier(identifier)}; } +/** \} */ -/* -------------------------------------------------------------------- - * DSocket inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DSocket Inline Methods + * \{ */ inline DSocket::DSocket(const DTreeContext *context, const SocketRef *socket_ref) : context_(context), socket_ref_(socket_ref) @@ -361,10 +374,11 @@ inline DNode DSocket::node() const BLI_assert(socket_ref_ != nullptr); return {context_, &socket_ref_->node()}; } +/** \} */ -/* -------------------------------------------------------------------- - * DInputSocket inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DInputSocket Inline Methods + * \{ */ inline DInputSocket::DInputSocket(const DTreeContext *context, const InputSocketRef *socket_ref) : DSocket(context, socket_ref) @@ -385,10 +399,11 @@ inline const InputSocketRef *DInputSocket::operator->() const { return (const InputSocketRef *)socket_ref_; } +/** \} */ -/* -------------------------------------------------------------------- - * DOutputSocket inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DOutputSocket Inline Methods + * \{ */ inline DOutputSocket::DOutputSocket(const DTreeContext *context, const OutputSocketRef *socket_ref) : DSocket(context, socket_ref) @@ -409,10 +424,11 @@ inline const OutputSocketRef *DOutputSocket::operator->() const { return (const OutputSocketRef *)socket_ref_; } +/** \} */ -/* -------------------------------------------------------------------- - * DerivedNodeTree inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #DerivedNodeTree Inline Methods + * \{ */ inline const DTreeContext &DerivedNodeTree::root_context() const { @@ -424,4 +440,6 @@ inline Span DerivedNodeTree::used_node_tree_refs() const return used_node_tree_refs_; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_function.h b/source/blender/nodes/NOD_function.h index 29f1a465491..81f0667fe1c 100644 --- a/source/blender/nodes/NOD_function.h +++ b/source/blender/nodes/NOD_function.h @@ -20,12 +20,24 @@ extern "C" { #endif +void register_node_type_fn_legacy_random_float(void); + +void register_node_type_fn_align_euler_to_vector(void); void register_node_type_fn_boolean_math(void); void register_node_type_fn_float_compare(void); void register_node_type_fn_float_to_int(void); +void register_node_type_fn_input_bool(void); +void register_node_type_fn_input_color(void); +void register_node_type_fn_input_int(void); +void register_node_type_fn_input_special_characters(void); void register_node_type_fn_input_string(void); void register_node_type_fn_input_vector(void); -void register_node_type_fn_random_float(void); +void register_node_type_fn_random_value(void); +void register_node_type_fn_replace_string(void); +void register_node_type_fn_rotate_euler(void); +void register_node_type_fn_slice_string(void); +void register_node_type_fn_string_length(void); +void register_node_type_fn_value_to_string(void); #ifdef __cplusplus } diff --git a/source/blender/nodes/NOD_geometry.h b/source/blender/nodes/NOD_geometry.h index a713da45f0b..ea3458af065 100644 --- a/source/blender/nodes/NOD_geometry.h +++ b/source/blender/nodes/NOD_geometry.h @@ -29,10 +29,28 @@ void register_node_tree_type_geo(void); void register_node_type_geo_group(void); void register_node_type_geo_custom_group(bNodeType *ntype); +void register_node_type_geo_legacy_attribute_proximity(void); +void register_node_type_geo_legacy_attribute_randomize(void); +void register_node_type_geo_legacy_attribute_transfer(void); +void register_node_type_geo_legacy_curve_endpoints(void); +void register_node_type_geo_legacy_curve_reverse(void); +void register_node_type_geo_legacy_curve_set_handles(void); +void register_node_type_geo_legacy_curve_spline_type(void); +void register_node_type_geo_legacy_curve_subdivide(void); +void register_node_type_geo_legacy_curve_to_points(void); +void register_node_type_geo_legacy_delete_geometry(void); +void register_node_type_geo_legacy_edge_split(void); void register_node_type_geo_legacy_material_assign(void); +void register_node_type_geo_legacy_mesh_to_curve(void); +void register_node_type_geo_legacy_points_to_volume(void); +void register_node_type_geo_legacy_raycast(void); +void register_node_type_geo_legacy_select_by_handle_type(void); void register_node_type_geo_legacy_select_by_material(void); +void register_node_type_geo_legacy_subdivision_surface(void); +void register_node_type_geo_legacy_volume_to_mesh(void); void register_node_type_geo_align_rotation_to_vector(void); +void register_node_type_geo_attribute_capture(void); void register_node_type_geo_attribute_clamp(void); void register_node_type_geo_attribute_color_ramp(void); void register_node_type_geo_attribute_combine_xyz(void); @@ -40,24 +58,24 @@ void register_node_type_geo_attribute_compare(void); void register_node_type_geo_attribute_convert(void); void register_node_type_geo_attribute_curve_map(void); void register_node_type_geo_attribute_fill(void); -void register_node_type_geo_attribute_capture(void); void register_node_type_geo_attribute_map_range(void); void register_node_type_geo_attribute_math(void); void register_node_type_geo_attribute_mix(void); -void register_node_type_geo_attribute_proximity(void); -void register_node_type_geo_attribute_randomize(void); void register_node_type_geo_attribute_remove(void); void register_node_type_geo_attribute_separate_xyz(void); -void register_node_type_geo_attribute_transfer(void); +void register_node_type_geo_attribute_statistic(void); void register_node_type_geo_attribute_vector_math(void); void register_node_type_geo_attribute_vector_rotate(void); void register_node_type_geo_boolean(void); void register_node_type_geo_bounding_box(void); void register_node_type_geo_collection_info(void); void register_node_type_geo_convex_hull(void); -void register_node_type_geo_curve_endpoints(void); +void register_node_type_geo_curve_endpoint_selection(void); void register_node_type_geo_curve_fill(void); +void register_node_type_geo_curve_fillet(void); +void register_node_type_geo_curve_handle_type_selection(void); void register_node_type_geo_curve_length(void); +void register_node_type_geo_curve_parameter(void); void register_node_type_geo_curve_primitive_bezier_segment(void); void register_node_type_geo_curve_primitive_circle(void); void register_node_type_geo_curve_primitive_line(void); @@ -67,6 +85,7 @@ void register_node_type_geo_curve_primitive_spiral(void); void register_node_type_geo_curve_primitive_star(void); void register_node_type_geo_curve_resample(void); void register_node_type_geo_curve_reverse(void); +void register_node_type_geo_curve_sample(void); void register_node_type_geo_curve_set_handles(void); void register_node_type_geo_curve_spline_type(void); void register_node_type_geo_curve_subdivide(void); @@ -74,14 +93,27 @@ void register_node_type_geo_curve_to_mesh(void); void register_node_type_geo_curve_to_points(void); void register_node_type_geo_curve_trim(void); void register_node_type_geo_delete_geometry(void); +void register_node_type_geo_distribute_points_on_faces(void); void register_node_type_geo_edge_split(void); +void register_node_type_geo_image_texture(void); +void register_node_type_geo_input_curve_handles(void); +void register_node_type_geo_input_curve_tilt(void); +void register_node_type_geo_input_id(void); void register_node_type_geo_input_index(void); +void register_node_type_geo_input_material_index(void); void register_node_type_geo_input_material(void); void register_node_type_geo_input_normal(void); void register_node_type_geo_input_position(void); +void register_node_type_geo_input_radius(void); +void register_node_type_geo_input_shade_smooth(void); +void register_node_type_geo_input_spline_cyclic(void); +void register_node_type_geo_input_spline_length(void); +void register_node_type_geo_input_spline_resolution(void); +void register_node_type_geo_input_tangent(void); +void register_node_type_geo_instance_on_points(void); +void register_node_type_geo_instances_to_points(void); void register_node_type_geo_is_viewport(void); void register_node_type_geo_join_geometry(void); -void register_node_type_geo_material_assign(void); void register_node_type_geo_material_replace(void); void register_node_type_geo_material_selection(void); void register_node_type_geo_mesh_primitive_circle(void); @@ -94,6 +126,7 @@ void register_node_type_geo_mesh_primitive_line(void); void register_node_type_geo_mesh_primitive_uv_sphere(void); void register_node_type_geo_mesh_subdivide(void); void register_node_type_geo_mesh_to_curve(void); +void register_node_type_geo_mesh_to_points(void); void register_node_type_geo_object_info(void); void register_node_type_geo_point_distribute(void); void register_node_type_geo_point_instance(void); @@ -101,15 +134,35 @@ void register_node_type_geo_point_rotate(void); void register_node_type_geo_point_scale(void); void register_node_type_geo_point_separate(void); void register_node_type_geo_point_translate(void); +void register_node_type_geo_points_to_vertices(void); void register_node_type_geo_points_to_volume(void); +void register_node_type_geo_proximity(void); void register_node_type_geo_raycast(void); +void register_node_type_geo_realize_instances(void); +void register_node_type_geo_rotate_instances(void); void register_node_type_geo_sample_texture(void); +void register_node_type_geo_scale_instances(void); void register_node_type_geo_select_by_handle_type(void); void register_node_type_geo_separate_components(void); +void register_node_type_geo_separate_geometry(void); +void register_node_type_geo_set_curve_handles(void); +void register_node_type_geo_set_curve_radius(void); +void register_node_type_geo_set_curve_tilt(void); +void register_node_type_geo_set_id(void); +void register_node_type_geo_set_material_index(void); +void register_node_type_geo_set_material(void); +void register_node_type_geo_set_point_radius(void); void register_node_type_geo_set_position(void); +void register_node_type_geo_set_shade_smooth(void); +void register_node_type_geo_set_spline_cyclic(void); +void register_node_type_geo_set_spline_resolution(void); +void register_node_type_geo_string_join(void); +void register_node_type_geo_string_to_curves(void); void register_node_type_geo_subdivision_surface(void); void register_node_type_geo_switch(void); +void register_node_type_geo_transfer_attribute(void); void register_node_type_geo_transform(void); +void register_node_type_geo_translate_instances(void); void register_node_type_geo_triangulate(void); void register_node_type_geo_viewer(void); void register_node_type_geo_volume_to_mesh(void); diff --git a/source/blender/nodes/NOD_geometry_exec.hh b/source/blender/nodes/NOD_geometry_exec.hh index dbb5f8b240d..6e1f21dbae0 100644 --- a/source/blender/nodes/NOD_geometry_exec.hh +++ b/source/blender/nodes/NOD_geometry_exec.hh @@ -33,6 +33,8 @@ struct ModifierData; namespace blender::nodes { +using bke::AnonymousAttributeFieldInput; +using bke::AttributeFieldInput; using bke::AttributeIDRef; using bke::geometry_set_realize_instances; using bke::GeometryComponentFieldContext; @@ -44,6 +46,8 @@ using bke::WeakAnonymousAttributeID; using bke::WriteAttributeLookup; using fn::CPPType; using fn::Field; +using fn::FieldContext; +using fn::FieldEvaluator; using fn::FieldInput; using fn::FieldOperation; using fn::GField; @@ -167,10 +171,16 @@ class GeoNodeExecParams { this->check_input_access(identifier, &CPPType::get()); #endif GMutablePointer gvalue = this->extract_input(identifier); - return gvalue.relocate_out(); + T value = gvalue.relocate_out(); + if constexpr (std::is_same_v) { + this->check_input_geometry_set(identifier, value); + } + return value; } } + void check_input_geometry_set(StringRef identifier, const GeometrySet &geometry_set) const; + /** * Get input as vector for multi input socket with the given identifier. * @@ -207,7 +217,11 @@ class GeoNodeExecParams { #endif GPointer gvalue = provider_->get_input(identifier); BLI_assert(gvalue.is_type()); - return *(const T *)gvalue.get(); + const T &value = *(const T *)gvalue.get(); + if constexpr (std::is_same_v) { + this->check_input_geometry_set(identifier, value); + } + return value; } } @@ -331,6 +345,8 @@ class GeoNodeExecParams { const GeometryComponent &component, const AttributeDomain default_domain) const; + std::string attribute_producer_name() const; + private: /* Utilities for detecting common errors at when using this class. */ void check_input_access(StringRef identifier, const CPPType *requested_type = nullptr) const; diff --git a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh index 00d97b24646..2a118057a03 100644 --- a/source/blender/nodes/NOD_geometry_nodes_eval_log.hh +++ b/source/blender/nodes/NOD_geometry_nodes_eval_log.hh @@ -76,6 +76,31 @@ class GenericValueLog : public ValueLog { } }; +class GFieldValueLog : public ValueLog { + private: + fn::GField field_; + const fn::CPPType &type_; + Vector input_tooltips_; + + public: + GFieldValueLog(fn::GField field, bool log_full_field); + + const fn::GField &field() const + { + return field_; + } + + Span input_tooltips() const + { + return input_tooltips_; + } + + const fn::CPPType &type() const + { + return type_; + } +}; + struct GeometryAttributeInfo { std::string name; AttributeDomain domain; @@ -109,7 +134,7 @@ class GeometryValueLog : public ValueLog { std::optional pointcloud_info; std::optional instances_info; - GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry); + GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry = false); Span attributes() const { @@ -131,6 +156,7 @@ enum class NodeWarningType { Error, Warning, Info, + Legacy, }; struct NodeWarning { @@ -180,21 +206,38 @@ class LocalGeoLogger { /** The root logger class. */ class GeoLogger { private: - /** The entire geometry of sockets in this set should be cached, because e.g. the spreadsheet - * displays the data. We don't log the entire geometry at all places, because that would require - * way too much memory. */ - Set log_full_geometry_sockets_; + /** + * Log the entire value for these sockets, because they may be inspected afterwards. + * We don't log everything, because that would take up too much memory and cause significant + * slowdowns. + */ + Set log_full_sockets_; threading::EnumerableThreadSpecific threadlocals_; + /* These are only optional since they don't have a default constructor. */ + std::unique_ptr input_geometry_log_; + std::unique_ptr output_geometry_log_; + friend LocalGeoLogger; + friend ModifierLog; public: - GeoLogger(Set log_full_geometry_sockets) - : log_full_geometry_sockets_(std::move(log_full_geometry_sockets)), + GeoLogger(Set log_full_sockets) + : log_full_sockets_(std::move(log_full_sockets)), threadlocals_([this]() { return LocalGeoLogger(*this); }) { } + void log_input_geometry(const GeometrySet &geometry) + { + input_geometry_log_ = std::make_unique(geometry); + } + + void log_output_geometry(const GeometrySet &geometry) + { + output_geometry_log_ = std::make_unique(geometry); + } + LocalGeoLogger &local() { return threadlocals_.local(); @@ -280,6 +323,9 @@ class ModifierLog { destruct_ptr root_tree_logs_; Vector> logged_values_; + std::unique_ptr input_geometry_log_; + std::unique_ptr output_geometry_log_; + public: ModifierLog(GeoLogger &logger); @@ -300,6 +346,9 @@ class ModifierLog { const SpaceSpreadsheet &sspreadsheet); void foreach_node_log(FunctionRef fn) const; + const GeometryValueLog *input_geometry_log() const; + const GeometryValueLog *output_geometry_log() const; + private: using LogByTreeContext = Map; diff --git a/source/blender/nodes/NOD_multi_function.hh b/source/blender/nodes/NOD_multi_function.hh index 58816544dc1..367ceaab9f7 100644 --- a/source/blender/nodes/NOD_multi_function.hh +++ b/source/blender/nodes/NOD_multi_function.hh @@ -33,15 +33,15 @@ class NodeMultiFunctions; */ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { private: - ResourceScope &resource_scope_; bNode &node_; bNodeTree &tree_; + std::shared_ptr owned_built_fn_; const MultiFunction *built_fn_ = nullptr; friend NodeMultiFunctions; public: - NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); + NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree); /** * Assign a multi-function for the current node. The input and output parameters of the function @@ -58,31 +58,33 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable { bNode &node(); bNodeTree &tree(); - - ResourceScope &resource_scope(); }; /** * Gives access to multi-functions for all nodes in a node tree that support them. */ class NodeMultiFunctions { + public: + struct Item { + const MultiFunction *fn = nullptr; + std::shared_ptr owned_fn; + }; + private: - Map map_; + Map map_; public: - NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); + NodeMultiFunctions(const DerivedNodeTree &tree); - const MultiFunction *try_get(const DNode &node) const; + const Item &try_get(const DNode &node) const; }; -/* -------------------------------------------------------------------- - * NodeMultiFunctionBuilder inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #NodeMultiFunctionBuilder Inline Methods + * \{ */ -inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, - bNode &node, - bNodeTree &tree) - : resource_scope_(resource_scope), node_(node), tree_(tree) +inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree) + : node_(node), tree_(tree) { } @@ -96,11 +98,6 @@ inline bNodeTree &NodeMultiFunctionBuilder::tree() return tree_; } -inline ResourceScope &NodeMultiFunctionBuilder::resource_scope() -{ - return resource_scope_; -} - inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) { built_fn_ = fn; @@ -108,23 +105,32 @@ inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) { - this->set_matching_fn(&fn); + built_fn_ = &fn; } template inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) { - const T &fn = resource_scope_.construct(std::forward(args)...); - this->set_matching_fn(&fn); + owned_built_fn_ = std::make_shared(std::forward(args)...); + built_fn_ = &*owned_built_fn_; } -/* -------------------------------------------------------------------- - * NodeMultiFunctions inline methods. - */ +/** \} */ -inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const +/* -------------------------------------------------------------------- */ +/** \name #NodeMultiFunctions Inline Methods + * \{ */ + +inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const { - return map_.lookup_default(node->bnode(), nullptr); + static Item empty_item; + const Item *item = map_.lookup_ptr(node->bnode()); + if (item == nullptr) { + return empty_item; + } + return *item; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_declaration.hh b/source/blender/nodes/NOD_node_declaration.hh index d64b76ccbb9..1481e69c00e 100644 --- a/source/blender/nodes/NOD_node_declaration.hh +++ b/source/blender/nodes/NOD_node_declaration.hh @@ -27,6 +27,55 @@ namespace blender::nodes { class NodeDeclarationBuilder; +enum class InputSocketFieldType { + /** The input is required to be a single value. */ + None, + /** The input can be a field. */ + IsSupported, + /** The input can be a field and is a field implicitly if nothing is connected. */ + Implicit, +}; + +enum class OutputSocketFieldType { + /** The output is always a single value. */ + None, + /** The output is always a field, independent of the inputs. */ + FieldSource, + /** If any input is a field, this output will be a field as well. */ + DependentField, + /** If any of a subset of inputs is a field, this out will be a field as well. + * The subset is defined by the vector of indices. */ + PartiallyDependent, +}; + +/** + * Contains information about how a node output's field state depends on inputs of the same node. + */ +class OutputFieldDependency { + private: + OutputSocketFieldType type_ = OutputSocketFieldType::None; + Vector linked_input_indices_; + + public: + static OutputFieldDependency ForFieldSource(); + static OutputFieldDependency ForDataSource(); + static OutputFieldDependency ForDependentField(); + static OutputFieldDependency ForPartiallyDependentField(Vector indices); + + OutputSocketFieldType field_type() const; + Span linked_input_indices() const; + + friend bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b); +}; + +/** + * Information about how a node interacts with fields. + */ +struct FieldInferencingInterface { + Vector inputs; + Vector outputs; +}; + /** * Describes a single input or output socket. This is subclassed for different socket types. */ @@ -34,9 +83,15 @@ class SocketDeclaration { protected: std::string name_; std::string identifier_; + std::string description_; bool hide_label_ = false; bool hide_value_ = false; bool is_multi_input_ = false; + bool no_mute_links_ = false; + bool is_attribute_name_ = false; + + InputSocketFieldType input_field_type_ = InputSocketFieldType::None; + OutputFieldDependency output_field_dependency_; friend NodeDeclarationBuilder; template friend class SocketDeclarationBuilder; @@ -49,7 +104,12 @@ class SocketDeclaration { virtual bNodeSocket &update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &socket) const; StringRefNull name() const; + StringRefNull description() const; StringRefNull identifier() const; + bool is_attribute_name() const; + + InputSocketFieldType input_field_type() const; + const OutputFieldDependency &output_field_dependency() const; protected: void set_common_flags(bNodeSocket &socket) const; @@ -93,6 +153,60 @@ class SocketDeclarationBuilder : public BaseSocketDeclarationBuilder { decl_->is_multi_input_ = value; return *(Self *)this; } + + Self &description(std::string value = "") + { + decl_->description_ = std::move(value); + return *(Self *)this; + } + Self &no_muted_links(bool value = true) + { + decl_->no_mute_links_ = value; + return *(Self *)this; + } + + Self &is_attribute_name(bool value = true) + { + decl_->is_attribute_name_ = value; + return *(Self *)this; + } + + /** The input socket allows passing in a field. */ + Self &supports_field() + { + decl_->input_field_type_ = InputSocketFieldType::IsSupported; + return *(Self *)this; + } + + /** The input supports a field and is a field by default when nothing is connected. */ + Self &implicit_field() + { + this->hide_value(); + decl_->input_field_type_ = InputSocketFieldType::Implicit; + return *(Self *)this; + } + + /** The output is always a field, regardless of any inputs. */ + Self &field_source() + { + decl_->output_field_dependency_ = OutputFieldDependency::ForFieldSource(); + return *(Self *)this; + } + + /** The output is a field if any of the inputs is a field. */ + Self &dependent_field() + { + decl_->output_field_dependency_ = OutputFieldDependency::ForDependentField(); + return *(Self *)this; + } + + /** The output is a field if any of the inputs with indices in the given list is a field. */ + Self &dependent_field(Vector input_dependencies) + { + decl_->output_field_dependency_ = OutputFieldDependency::ForPartiallyDependentField( + std::move(input_dependencies)); + return *(Self *)this; + } }; using SocketDeclarationPtr = std::unique_ptr; @@ -101,6 +215,7 @@ class NodeDeclaration { private: Vector inputs_; Vector outputs_; + bool is_function_node_ = false; friend NodeDeclarationBuilder; @@ -111,6 +226,11 @@ class NodeDeclaration { Span inputs() const; Span outputs() const; + bool is_function_node() const + { + return is_function_node_; + } + MEM_CXX_CLASS_ALLOC_FUNCS("NodeDeclaration") }; @@ -122,6 +242,15 @@ class NodeDeclarationBuilder { public: NodeDeclarationBuilder(NodeDeclaration &declaration); + /** + * All inputs support fields, and all outputs are fields if any of the inputs is a field. + * Calling field status definitions on each socket is unnecessary. + */ + void is_function_node(bool value = true) + { + declaration_.is_function_node_ = value; + } + template typename DeclType::Builder &add_input(StringRef name, StringRef identifier = ""); template @@ -134,9 +263,85 @@ class NodeDeclarationBuilder { Vector &r_decls); }; -/* -------------------------------------------------------------------- - * SocketDeclaration inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #OutputFieldDependency Inline Methods + * \{ */ + +inline OutputFieldDependency OutputFieldDependency::ForFieldSource() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::FieldSource; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForDataSource() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::None; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForDependentField() +{ + OutputFieldDependency field_dependency; + field_dependency.type_ = OutputSocketFieldType::DependentField; + return field_dependency; +} + +inline OutputFieldDependency OutputFieldDependency::ForPartiallyDependentField(Vector indices) +{ + OutputFieldDependency field_dependency; + if (indices.is_empty()) { + field_dependency.type_ = OutputSocketFieldType::None; + } + else { + field_dependency.type_ = OutputSocketFieldType::PartiallyDependent; + field_dependency.linked_input_indices_ = std::move(indices); + } + return field_dependency; +} + +inline OutputSocketFieldType OutputFieldDependency::field_type() const +{ + return type_; +} + +inline Span OutputFieldDependency::linked_input_indices() const +{ + return linked_input_indices_; +} + +inline bool operator==(const OutputFieldDependency &a, const OutputFieldDependency &b) +{ + return a.type_ == b.type_ && a.linked_input_indices_ == b.linked_input_indices_; +} + +inline bool operator!=(const OutputFieldDependency &a, const OutputFieldDependency &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #FieldInferencingInterface Inline Methods + * \{ */ + +inline bool operator==(const FieldInferencingInterface &a, const FieldInferencingInterface &b) +{ + return a.inputs == b.inputs && a.outputs == b.outputs; +} + +inline bool operator!=(const FieldInferencingInterface &a, const FieldInferencingInterface &b) +{ + return !(a == b); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #SocketDeclaration Inline Methods + * \{ */ inline StringRefNull SocketDeclaration::name() const { @@ -148,9 +353,31 @@ inline StringRefNull SocketDeclaration::identifier() const return identifier_; } -/* -------------------------------------------------------------------- - * NodeDeclarationBuilder inline methods. - */ +inline StringRefNull SocketDeclaration::description() const +{ + return description_; +} + +inline bool SocketDeclaration::is_attribute_name() const +{ + return is_attribute_name_; +} + +inline InputSocketFieldType SocketDeclaration::input_field_type() const +{ + return input_field_type_; +} + +inline const OutputFieldDependency &SocketDeclaration::output_field_dependency() const +{ + return output_field_dependency_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeDeclarationBuilder Inline Methods + * \{ */ inline NodeDeclarationBuilder::NodeDeclarationBuilder(NodeDeclaration &declaration) : declaration_(declaration) @@ -188,9 +415,11 @@ inline typename DeclType::Builder &NodeDeclarationBuilder::add_socket( return socket_decl_builder_ref; } -/* -------------------------------------------------------------------- - * NodeDeclaration inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeDeclaration Inline Methods + * \{ */ inline Span NodeDeclaration::inputs() const { @@ -202,4 +431,6 @@ inline Span NodeDeclaration::outputs() const return outputs_; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_node_tree_ref.hh b/source/blender/nodes/NOD_node_tree_ref.hh index 4f2565cbbaf..26a5dc9d60f 100644 --- a/source/blender/nodes/NOD_node_tree_ref.hh +++ b/source/blender/nodes/NOD_node_tree_ref.hh @@ -182,6 +182,7 @@ class NodeRef : NonCopyable, NonMovable { Span inputs() const; Span outputs() const; Span internal_links() const; + Span sockets(eNodeSocketInOut in_out) const; const InputSocketRef &input(int index) const; const OutputSocketRef &output(int index) const; @@ -189,13 +190,20 @@ class NodeRef : NonCopyable, NonMovable { const InputSocketRef &input_by_identifier(StringRef identifier) const; const OutputSocketRef &output_by_identifier(StringRef identifier) const; + bool any_input_is_directly_linked() const; + bool any_output_is_directly_linked() const; + bool any_socket_is_directly_linked(eNodeSocketInOut in_out) const; + bNode *bnode() const; bNodeTree *btree() const; PointerRNA *rna() const; StringRefNull idname() const; StringRefNull name() const; + StringRefNull label() const; + StringRefNull label_or_name() const; bNodeType *typeinfo() const; + const NodeDeclaration *declaration() const; int id() const; @@ -269,9 +277,28 @@ class NodeTreeRef : NonCopyable, NonMovable { Span links() const; + const NodeRef *find_node(const bNode &bnode) const; + bool has_link_cycles() const; bool has_undefined_nodes_or_sockets() const; + enum class ToposortDirection { + LeftToRight, + RightToLeft, + }; + + struct ToposortResult { + Vector sorted_nodes; + /** + * There can't be a correct topological sort of the nodes when there is a cycle. The nodes will + * still be sorted to some degree. The caller has to decide whether it can handle non-perfect + * sorts or not. + */ + bool has_cycle = false; + }; + + ToposortResult toposort(ToposortDirection direction) const; + bNodeTree *btree() const; StringRefNull name() const; @@ -303,9 +330,9 @@ using nodes::OutputSocketRef; using nodes::SocketRef; } // namespace node_tree_ref_types -/* -------------------------------------------------------------------- - * SocketRef inline methods. - */ +/* -------------------------------------------------------------------- */ +/** \name #SocketRef Inline Methods + * \{ */ inline Span SocketRef::logically_linked_sockets() const { @@ -444,9 +471,11 @@ template inline T *SocketRef::default_value() const return (T *)bsocket_->default_value; } -/* -------------------------------------------------------------------- - * InputSocketRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #InputSocketRef Inline Methods + * \{ */ inline Span InputSocketRef::logically_linked_sockets() const { @@ -463,9 +492,11 @@ inline bool InputSocketRef::is_multi_input_socket() const return bsocket_->flag & SOCK_MULTI_INPUT; } -/* -------------------------------------------------------------------- - * OutputSocketRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #OutputSocketRef Inline Methods + * \{ */ inline Span OutputSocketRef::logically_linked_sockets() const { @@ -477,9 +508,11 @@ inline Span OutputSocketRef::directly_linked_sockets() c return directly_linked_sockets_.cast(); } -/* -------------------------------------------------------------------- - * NodeRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeRef Inline Methods + * \{ */ inline const NodeTreeRef &NodeRef::tree() const { @@ -496,6 +529,12 @@ inline Span NodeRef::outputs() const return outputs_; } +inline Span NodeRef::sockets(const eNodeSocketInOut in_out) const +{ + return in_out == SOCK_IN ? inputs_.as_span().cast() : + outputs_.as_span().cast(); +} + inline Span NodeRef::internal_links() const { return internal_links_; @@ -548,11 +587,32 @@ inline StringRefNull NodeRef::name() const return bnode_->name; } +inline StringRefNull NodeRef::label() const +{ + return bnode_->label; +} + +inline StringRefNull NodeRef::label_or_name() const +{ + const StringRefNull label = this->label(); + if (!label.is_empty()) { + return label; + } + return this->name(); +} + inline bNodeType *NodeRef::typeinfo() const { return bnode_->typeinfo; } +/* Returns a pointer because not all nodes have declarations currently. */ +inline const NodeDeclaration *NodeRef::declaration() const +{ + nodeDeclarationEnsure(this->tree().btree(), bnode_); + return bnode_->declaration; +} + inline int NodeRef::id() const { return id_; @@ -603,9 +663,11 @@ template inline T *NodeRef::storage() const return (T *)bnode_->storage; } -/* -------------------------------------------------------------------- - * LinkRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #LinkRef Inline Methods + * \{ */ inline const OutputSocketRef &LinkRef::from() const { @@ -627,9 +689,11 @@ inline bool LinkRef::is_muted() const return blink_->flag & NODE_LINK_MUTED; } -/* -------------------------------------------------------------------- - * InternalLinkRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #InternalLinkRef Inline Methods + * \{ */ inline const InputSocketRef &InternalLinkRef::from() const { @@ -646,9 +710,11 @@ inline bNodeLink *InternalLinkRef::blink() const return blink_; } -/* -------------------------------------------------------------------- - * NodeTreeRef inline methods. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #NodeTreeRef Inline Methods + * \{ */ inline Span NodeTreeRef::nodes() const { @@ -696,4 +762,6 @@ inline StringRefNull NodeTreeRef::name() const return btree_->id.name + 2; } +/** \} */ + } // namespace blender::nodes diff --git a/source/blender/nodes/NOD_shader.h b/source/blender/nodes/NOD_shader.h index 2911e0fbea6..76c174201e8 100644 --- a/source/blender/nodes/NOD_shader.h +++ b/source/blender/nodes/NOD_shader.h @@ -49,6 +49,7 @@ void register_node_type_sh_normal(void); void register_node_type_sh_gamma(void); void register_node_type_sh_brightcontrast(void); void register_node_type_sh_mapping(void); +void register_node_type_sh_curve_float(void); void register_node_type_sh_curve_vec(void); void register_node_type_sh_curve_rgb(void); void register_node_type_sh_map_range(void); diff --git a/source/blender/nodes/NOD_socket_declarations.hh b/source/blender/nodes/NOD_socket_declarations.hh index 3d0cfdb5d5d..f7aea212f73 100644 --- a/source/blender/nodes/NOD_socket_declarations.hh +++ b/source/blender/nodes/NOD_socket_declarations.hh @@ -46,29 +46,10 @@ class Float : public SocketDeclaration { class FloatBuilder : public SocketDeclarationBuilder { public: - FloatBuilder &min(const float value) - { - decl_->soft_min_value_ = value; - return *this; - } - - FloatBuilder &max(const float value) - { - decl_->soft_max_value_ = value; - return *this; - } - - FloatBuilder &default_value(const float value) - { - decl_->default_value_ = value; - return *this; - } - - FloatBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } + FloatBuilder &min(const float value); + FloatBuilder &max(const float value); + FloatBuilder &default_value(const float value); + FloatBuilder &subtype(PropertySubType subtype); }; class IntBuilder; @@ -92,29 +73,10 @@ class Int : public SocketDeclaration { class IntBuilder : public SocketDeclarationBuilder { public: - IntBuilder &min(const int value) - { - decl_->soft_min_value_ = value; - return *this; - } - - IntBuilder &max(const int value) - { - decl_->soft_max_value_ = value; - return *this; - } - - IntBuilder &default_value(const int value) - { - decl_->default_value_ = value; - return *this; - } - - IntBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } + IntBuilder &min(const int value); + IntBuilder &max(const int value); + IntBuilder &default_value(const int value); + IntBuilder &subtype(PropertySubType subtype); }; class VectorBuilder; @@ -138,29 +100,10 @@ class Vector : public SocketDeclaration { class VectorBuilder : public SocketDeclarationBuilder { public: - VectorBuilder &default_value(const float3 value) - { - decl_->default_value_ = value; - return *this; - } - - VectorBuilder &subtype(PropertySubType subtype) - { - decl_->subtype_ = subtype; - return *this; - } - - VectorBuilder &min(const float min) - { - decl_->soft_min_value_ = min; - return *this; - } - - VectorBuilder &max(const float max) - { - decl_->soft_max_value_ = max; - return *this; - } + VectorBuilder &default_value(const float3 value); + VectorBuilder &subtype(PropertySubType subtype); + VectorBuilder &min(const float min); + VectorBuilder &max(const float max); }; class BoolBuilder; @@ -179,11 +122,7 @@ class Bool : public SocketDeclaration { class BoolBuilder : public SocketDeclarationBuilder { public: - BoolBuilder &default_value(const bool value) - { - decl_->default_value_ = value; - return *this; - } + BoolBuilder &default_value(const bool value); }; class ColorBuilder; @@ -203,29 +142,35 @@ class Color : public SocketDeclaration { class ColorBuilder : public SocketDeclarationBuilder { public: - ColorBuilder &default_value(const ColorGeometry4f value) - { - decl_->default_value_ = value; - return *this; - } + ColorBuilder &default_value(const ColorGeometry4f value); }; +class StringBuilder; + class String : public SocketDeclaration { + private: + std::string default_value_; + + friend StringBuilder; + public: - using Builder = SocketDeclarationBuilder; + using Builder = StringBuilder; bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; }; +class StringBuilder : public SocketDeclarationBuilder { + public: + StringBuilder &default_value(const std::string value); +}; + class IDSocketDeclaration : public SocketDeclaration { private: const char *idname_; public: - IDSocketDeclaration(const char *idname) : idname_(idname) - { - } + IDSocketDeclaration(const char *idname); bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; bool matches(const bNodeSocket &socket) const override; @@ -236,44 +181,214 @@ class Object : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Object() : IDSocketDeclaration("NodeSocketObject") - { - } + Object(); }; class Material : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Material() : IDSocketDeclaration("NodeSocketMaterial") - { - } + Material(); }; class Collection : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Collection() : IDSocketDeclaration("NodeSocketCollection") - { - } + Collection(); }; class Texture : public IDSocketDeclaration { public: using Builder = SocketDeclarationBuilder; - Texture() : IDSocketDeclaration("NodeSocketTexture") - { - } + Texture(); }; -class Geometry : public SocketDeclaration { +class Image : public IDSocketDeclaration { public: - using Builder = SocketDeclarationBuilder; + using Builder = SocketDeclarationBuilder; - bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; - bool matches(const bNodeSocket &socket) const override; + Image(); }; +/* -------------------------------------------------------------------- */ +/** \name #FloatBuilder Inline Methods + * \{ */ + +inline FloatBuilder &FloatBuilder::min(const float value) +{ + decl_->soft_min_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::max(const float value) +{ + decl_->soft_max_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::default_value(const float value) +{ + decl_->default_value_ = value; + return *this; +} + +inline FloatBuilder &FloatBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IntBuilder Inline Methods + * \{ */ + +inline IntBuilder &IntBuilder::min(const int value) +{ + decl_->soft_min_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::max(const int value) +{ + decl_->soft_max_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::default_value(const int value) +{ + decl_->default_value_ = value; + return *this; +} + +inline IntBuilder &IntBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #VectorBuilder Inline Methods + * \{ */ + +inline VectorBuilder &VectorBuilder::default_value(const float3 value) +{ + decl_->default_value_ = value; + return *this; +} + +inline VectorBuilder &VectorBuilder::subtype(PropertySubType subtype) +{ + decl_->subtype_ = subtype; + return *this; +} + +inline VectorBuilder &VectorBuilder::min(const float min) +{ + decl_->soft_min_value_ = min; + return *this; +} + +inline VectorBuilder &VectorBuilder::max(const float max) +{ + decl_->soft_max_value_ = max; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #BoolBuilder Inline Methods + * \{ */ + +inline BoolBuilder &BoolBuilder::default_value(const bool value) +{ + decl_->default_value_ = value; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #ColorBuilder Inline Methods + * \{ */ + +inline ColorBuilder &ColorBuilder::default_value(const ColorGeometry4f value) +{ + decl_->default_value_ = value; + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #StringBuilder Inline Methods + * \{ */ + +inline StringBuilder &StringBuilder::default_value(std::string value) +{ + decl_->default_value_ = std::move(value); + return *this; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IDSocketDeclaration and Children Inline Methods + * \{ */ + +inline IDSocketDeclaration::IDSocketDeclaration(const char *idname) : idname_(idname) +{ +} + +inline Object::Object() : IDSocketDeclaration("NodeSocketObject") +{ +} + +inline Material::Material() : IDSocketDeclaration("NodeSocketMaterial") +{ +} + +inline Collection::Collection() : IDSocketDeclaration("NodeSocketCollection") +{ +} + +inline Texture::Texture() : IDSocketDeclaration("NodeSocketTexture") +{ +} + +inline Image::Image() : IDSocketDeclaration("NodeSocketImage") +{ +} + +/** \} */ + } // namespace blender::nodes::decl + +/* -------------------------------------------------------------------- */ +/** \name External Template Instantiations + * + * Defined in `intern/extern_implementations.cc`. + * \{ */ + +namespace blender::nodes { +#define MAKE_EXTERN_SOCKET_DECLARATION(TYPE) \ + extern template class SocketDeclarationBuilder; \ + extern template TYPE::Builder &NodeDeclarationBuilder::add_input(StringRef, StringRef); \ + extern template TYPE::Builder &NodeDeclarationBuilder::add_output(StringRef, StringRef); + +MAKE_EXTERN_SOCKET_DECLARATION(decl::Float) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Int) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Vector) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Bool) +MAKE_EXTERN_SOCKET_DECLARATION(decl::Color) +MAKE_EXTERN_SOCKET_DECLARATION(decl::String) + +} // namespace blender::nodes + +/** \} */ diff --git a/source/blender/nodes/NOD_socket_declarations_geometry.hh b/source/blender/nodes/NOD_socket_declarations_geometry.hh new file mode 100644 index 00000000000..1531f82d67d --- /dev/null +++ b/source/blender/nodes/NOD_socket_declarations_geometry.hh @@ -0,0 +1,58 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "BKE_geometry_set.hh" + +#include "NOD_socket_declarations.hh" + +namespace blender::nodes::decl { + +class GeometryBuilder; + +class Geometry : public SocketDeclaration { + private: + blender::Vector supported_types_; + bool only_realized_data_ = false; + bool only_instances_ = false; + + friend GeometryBuilder; + + public: + using Builder = GeometryBuilder; + + bNodeSocket &build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const override; + bool matches(const bNodeSocket &socket) const override; + + Span supported_types() const; + bool only_realized_data() const; + bool only_instances() const; +}; + +class GeometryBuilder : public SocketDeclarationBuilder { + public: + GeometryBuilder &supported_type(GeometryComponentType supported_type); + GeometryBuilder &supported_type(blender::Vector supported_types); + GeometryBuilder &only_realized_data(bool value = true); + GeometryBuilder &only_instances(bool value = true); +}; + +} // namespace blender::nodes::decl + +namespace blender::nodes { +MAKE_EXTERN_SOCKET_DECLARATION(decl::Geometry) +} diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 51d59821d3c..20ad4d359f1 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -132,6 +132,7 @@ DefNode(ShaderNode, SH_NODE_VECTOR_DISPLACEMENT,def_sh_vector_displacement," DefNode(ShaderNode, SH_NODE_TEX_IES, def_sh_tex_ies, "TEX_IES", TexIES, "IES Texture", "" ) DefNode(ShaderNode, SH_NODE_TEX_WHITE_NOISE, def_sh_tex_white_noise, "TEX_WHITE_NOISE", TexWhiteNoise, "White Noise", "" ) DefNode(ShaderNode, SH_NODE_OUTPUT_AOV, def_sh_output_aov, "OUTPUT_AOV", OutputAOV, "AOV Output", "" ) +DefNode(ShaderNode, SH_NODE_CURVE_FLOAT, def_float_curve, "CURVE_FLOAT", FloatCurve, "Float Curve", "" ) DefNode(CompositorNode, CMP_NODE_VIEWER, def_cmp_viewer, "VIEWER", Viewer, "Viewer", "" ) DefNode(CompositorNode, CMP_NODE_RGB, 0, "RGB", RGB, "RGB", "" ) @@ -262,15 +263,27 @@ DefNode(TextureNode, TEX_NODE_PROC+TEX_NOISE, 0, "TEX_NO DefNode(TextureNode, TEX_NODE_PROC+TEX_STUCCI, 0, "TEX_STUCCI", TexStucci, "Stucci", "" ) DefNode(TextureNode, TEX_NODE_PROC+TEX_DISTNOISE, 0, "TEX_DISTNOISE", TexDistNoise, "Distorted Noise", "" ) +DefNode(FunctionNode, FN_NODE_LEGACY_RANDOM_FLOAT, 0, "LEGACY_RANDOM_FLOAT", LegacyRandomFloat, "Random Float", "") + +DefNode(FunctionNode, FN_NODE_ALIGN_EULER_TO_VECTOR, def_fn_align_euler_to_vector, "ALIGN_EULER_TO_VECTOR", AlignEulerToVector, "Align Euler To Vector", "") DefNode(FunctionNode, FN_NODE_BOOLEAN_MATH, def_boolean_math, "BOOLEAN_MATH", BooleanMath, "Boolean Math", "") -DefNode(FunctionNode, FN_NODE_FLOAT_COMPARE, def_float_compare, "FLOAT_COMPARE", FloatCompare, "Float Compare", "") +DefNode(FunctionNode, FN_NODE_COMPARE_FLOATS, def_float_compare, "COMPARE_FLOATS", CompareFloats, "Compare Floats", "") DefNode(FunctionNode, FN_NODE_FLOAT_TO_INT, def_float_to_int, "FLOAT_TO_INT", FloatToInt, "Float to Integer", "") +DefNode(FunctionNode, FN_NODE_INPUT_BOOL, def_fn_input_bool, "INPUT_BOOL", InputBool, "Boolean", "") +DefNode(FunctionNode, FN_NODE_INPUT_COLOR, def_fn_input_color, "INPUT_COLOR", InputColor, "Color", "") +DefNode(FunctionNode, FN_NODE_INPUT_INT, def_fn_input_int, "INPUT_INT", InputInt, "Integer", "") +DefNode(FunctionNode, FN_NODE_INPUT_SPECIAL_CHARACTERS, 0, "INPUT_SPECIAL_CHARACTERS", InputSpecialCharacters, "Special Characters", "") DefNode(FunctionNode, FN_NODE_INPUT_STRING, def_fn_input_string, "INPUT_STRING", InputString, "String", "") DefNode(FunctionNode, FN_NODE_INPUT_VECTOR, def_fn_input_vector, "INPUT_VECTOR", InputVector, "Vector", "") -DefNode(FunctionNode, FN_NODE_RANDOM_FLOAT, 0, "RANDOM_FLOAT", RandomFloat, "Random Float", "") +DefNode(FunctionNode, FN_NODE_RANDOM_VALUE, def_fn_random_value, "RANDOM_VALUE", RandomValue, "Random Value", "") +DefNode(FunctionNode, FN_NODE_REPLACE_STRING, 0, "REPLACE_STRING", ReplaceString, "Replace String", "") +DefNode(FunctionNode, FN_NODE_ROTATE_EULER, def_fn_rotate_euler, "ROTATE_EULER", RotateEuler, "Rotate Euler", "") +DefNode(FunctionNode, FN_NODE_SLICE_STRING, 0, "SLICE_STRING", SliceString, "Slice String", "") +DefNode(FunctionNode, FN_NODE_STRING_LENGTH, 0, "STRING_LENGTH", StringLength, "String Length", "") +DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToString, "Value to String", "") -DefNode(GeometryNode, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ALIGN_ROTATION_TO_VECTOR, def_geo_align_rotation_to_vector, "LEGACY_ALIGN_ROTATION_TO_VECTOR", LegacyAlignRotationToVector, "Align Rotation to Vector", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, def_geo_attribute_clamp, "LEGACY_ATTRIBUTE_CLAMP", LegacyAttributeClamp, "Attribute Clamp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COLOR_RAMP, def_geo_attribute_color_ramp, "LEGACY_ATTRIBUTE_COLOR_RAMP", LegacyAttributeColorRamp, "Attribute Color Ramp", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMBINE_XYZ, def_geo_attribute_combine_xyz, "LEGACY_ATTRIBUTE_COMBINE_XYZ", LegacyAttributeCombineXYZ, "Attribute Combine XYZ", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_COMPARE, def_geo_attribute_attribute_compare, "LEGACY_ATTRIBUTE_COMPARE", LegacyAttributeCompare, "Attribute Compare", "") @@ -280,18 +293,22 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_FILL, def_geo_attribute_fill, "L DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MAP_RANGE, def_geo_attribute_map_range, "LEGACY_ATTRIBUTE_MAP_RANGE", LegacyAttributeMapRange, "Attribute Map Range", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MATH, def_geo_attribute_math, "LEGACY_ATTRIBUTE_MATH", LegacyAttributeMath, "Attribute Math", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_MIX, def_geo_attribute_mix, "LEGACY_ATTRIBUTE_MIX", LegacyAttributeMix, "Attribute Mix", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_PROXIMITY, def_geo_legacy_attribute_proximity, "LEGACY_ATTRIBUTE_PROXIMITY", LegacyAttributeProximity, "Attribute Proximity", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, def_geo_attribute_randomize, "LEGACY_ATTRIBUTE_RANDOMIZE", LegacyAttributeRandomize, "Attribute Randomize", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SAMPLE_TEXTURE, 0, "LEGACY_ATTRIBUTE_SAMPLE_TEXTURE", LegacyAttributeSampleTexture, "Attribute Sample Texture", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_SEPARATE_XYZ, def_geo_attribute_separate_xyz, "LEGACY_ATTRIBUTE_SEPARATE_XYZ", LegacyAttributeSeparateXYZ, "Attribute Separate XYZ", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER, def_geo_attribute_transfer, "LEGACY_ATTRIBUTE_TRANSFER", LegacyAttributeTransfer, "Attribute Transfer", "") DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_MATH, def_geo_attribute_vector_math, "LEGACY_ATTRIBUTE_VECTOR_MATH", LegacyAttributeVectorMath, "Attribute Vector Math", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_ENDPOINTS, 0, "LEGACY_CURVE_ENDPOINTS", LegacyCurveEndpoints, "Curve Endpoints", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_REVERSE, 0, "LEGACY_CURVE_REVERSE", LegacyCurveReverse, "Curve Reverse", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SELECT_HANDLES, def_geo_curve_select_handles, "LEGACY_CURVE_SELECT_HANDLES", LegacyCurveSelectHandles, "Select by Handle Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SET_HANDLES, def_geo_legacy_curve_set_handles, "LEGACY_CURVE_SET_HANDLES", LegacyCurveSetHandles, "Set Handle Type", "") DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "LEGACY_CURVE_SPLINE_TYPE", LegacyCurveSplineType, "Set Spline Type", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, def_geo_legacy_curve_subdivide, "LEGACY_CURVE_SUBDIVIDE", LegacyCurveSubdivide, "Curve Subdivide", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_CURVE_TO_POINTS, def_geo_legacy_curve_to_points, "LEGACY_CURVE_TO_POINTS", LegacyCurveToPoints, "Curve to Points", "") DefNode(GeometryNode, GEO_NODE_LEGACY_DELETE_GEOMETRY, 0, "LEGACY_DELETE_GEOMETRY", LegacyDeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_EDGE_SPLIT, 0, "LEGACY_EDGE_SPLIT", LegacyEdgeSplit, "Edge Split", "") DefNode(GeometryNode, GEO_NODE_LEGACY_MATERIAL_ASSIGN, 0, "LEGACY_MATERIAL_ASSIGN", LegacyMaterialAssign, "Material Assign", "") DefNode(GeometryNode, GEO_NODE_LEGACY_MESH_TO_CURVE, 0, "LEGACY_MESH_TO_CURVE", LegacyMeshToCurve, "Mesh to Curve", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_DISTRIBUTE, def_geo_point_distribute, "LEGACY_POINT_DISTRIBUTE", LegacyPointDistribute, "Point Distribute", "") @@ -300,20 +317,22 @@ DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_ROTATE, def_geo_point_rotate, "LEGAC DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SCALE, def_geo_point_scale, "LEGACY_POINT_SCALE", LegacyPointScale, "Point Scale", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_SEPARATE, 0, "LEGACY_POINT_SEPARATE", LegacyPointSeparate, "Point Separate", "") DefNode(GeometryNode, GEO_NODE_LEGACY_POINT_TRANSLATE, def_geo_point_translate, "LEGACY_POINT_TRANSLATE", LegacyPointTranslate, "Point Translate", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") -DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_POINTS_TO_VOLUME, def_geo_legacy_points_to_volume, "LEGACY_POINTS_TO_VOLUME", LegacyPointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_RAYCAST, def_geo_legacy_raycast, "LEGACY_RAYCAST", LegacyRaycast, "Raycast", "") DefNode(GeometryNode, GEO_NODE_LEGACY_SELECT_BY_MATERIAL, 0, "LEGACY_SELECT_BY_MATERIAL", LegacySelectByMaterial, "Select by Material", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "LEGACY_SUBDIVISION_SURFACE", LegacySubdivisionSurface, "Subdivision Surface", "") +DefNode(GeometryNode, GEO_NODE_LEGACY_VOLUME_TO_MESH, def_geo_volume_to_mesh, "LEGACY_VOLUME_TO_MESH", LegacyVolumeToMesh, "Volume to Mesh", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_CAPTURE, def_geo_attribute_capture, "ATTRIBUTE_CAPTURE", AttributeCapture, "Attribute Capture", "") DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_REMOVE, 0, "ATTRIBUTE_REMOVE", AttributeRemove, "Attribute Remove", "") -DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, def_geo_attribute_vector_rotate, "LEGACY_ATTRIBUTE_VECTOR_ROTATE", LegacyAttributeVectorRotate, "Attribute Vector Rotate", "") -DefNode(GeometryNode, GEO_NODE_BOOLEAN, def_geo_boolean, "BOOLEAN", Boolean, "Boolean", "") +DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC", AttributeStatistic, "Attribute Statistic", "") DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "") +DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture, "CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "") DefNode(GeometryNode, GEO_NODE_COLLECTION_INFO, def_geo_collection_info, "COLLECTION_INFO", CollectionInfo, "Collection Info", "") DefNode(GeometryNode, GEO_NODE_CONVEX_HULL, 0, "CONVEX_HULL", ConvexHull, "Convex Hull", "") -DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINTS, 0, "CURVE_ENDPOINTS", CurveEndpoints, "Curve Endpoints", "") -DefNode(GeometryNode, GEO_NODE_CURVE_FILL, def_geo_curve_fill, "CURVE_FILL", CurveFill, "Curve Fill", "") +DefNode(GeometryNode, GEO_NODE_CURVE_ENDPOINT_SELECTION, 0, "CURVE_ENDPOINT_SELECTION", CurveEndpointSelection, "Endpoint Selection", "") +DefNode(GeometryNode, GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, def_geo_curve_handle_type_selection, "CURVE_HANDLE_TYPE_SELECTION", CurveHandleTypeSelection, "Handle Type Selection", "") DefNode(GeometryNode, GEO_NODE_CURVE_LENGTH, 0, "CURVE_LENGTH", CurveLength, "Curve Length", "") +DefNode(GeometryNode, GEO_NODE_CURVE_PARAMETER, 0, "CURVE_PARAMETER", CurveParameter, "Curve Parameter", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_BEZIER_SEGMENT, def_geo_curve_primitive_bezier_segment, "CURVE_PRIMITIVE_BEZIER_SEGMENT", CurvePrimitiveBezierSegment, "Bezier Segment", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_CIRCLE, def_geo_curve_primitive_circle, "CURVE_PRIMITIVE_CIRCLE", CurvePrimitiveCircle, "Curve Circle", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_LINE, def_geo_curve_primitive_line, "CURVE_PRIMITIVE_LINE", CurvePrimitiveLine, "Curve Line", "") @@ -321,20 +340,35 @@ DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRATIC_BEZIER, 0, "CURVE_PRIMI DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_QUADRILATERAL, def_geo_curve_primitive_quadrilateral, "CURVE_PRIMITIVE_QUADRILATERAL", CurvePrimitiveQuadrilateral, "Quadrilateral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_SPIRAL, 0, "CURVE_PRIMITIVE_SPIRAL", CurveSpiral, "Curve Spiral", "") DefNode(GeometryNode, GEO_NODE_CURVE_PRIMITIVE_STAR, 0, "CURVE_PRIMITIVE_STAR", CurveStar, "Star", "") -DefNode(GeometryNode, GEO_NODE_CURVE_RESAMPLE, def_geo_curve_resample, "CURVE_RESAMPLE", CurveResample, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SET_HANDLES, def_geo_curve_set_handles, "CURVE_SET_HANDLES", CurveSetHandles, "Set Handle Type", "") +DefNode(GeometryNode, GEO_NODE_CURVE_SPLINE_TYPE, def_geo_curve_spline_type, "CURVE_SPLINE_TYPE", CurveSplineType, "Set Spline Type", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_MESH, 0, "CURVE_TO_MESH", CurveToMesh, "Curve to Mesh", "") DefNode(GeometryNode, GEO_NODE_CURVE_TO_POINTS, def_geo_curve_to_points, "CURVE_TO_POINTS", CurveToPoints, "Curve to Points", "") -DefNode(GeometryNode, GEO_NODE_CURVE_TRIM, def_geo_curve_trim, "CURVE_TRIM", CurveTrim, "Curve Trim", "") -DefNode(GeometryNode, GEO_NODE_EDGE_SPLIT, 0, "EDGE_SPLIT", EdgeSplit, "Edge Split", "") +DefNode(GeometryNode, GEO_NODE_DELETE_GEOMETRY, def_geo_delete_geometry, "DELETE_GEOMETRY", DeleteGeometry, "Delete Geometry", "") +DefNode(GeometryNode, GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, def_geo_distribute_points_on_faces, "DISTRIBUTE_POINTS_ON_FACES", DistributePointsOnFaces, "Distribute Points on Faces", "") +DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "") +DefNode(GeometryNode, GEO_NODE_FILLET_CURVE, def_geo_curve_fillet, "FILLET_CURVE", FilletCurve, "Fillet Curve", "") +DefNode(GeometryNode, GEO_NODE_IMAGE_TEXTURE, def_geo_image_texture, "IMAGE_TEXTURE", ImageTexture, "Image Texture", "") +DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_HANDLES, 0, "INPUT_CURVE_HANDLES", InputCurveHandlePositions, "Curve Handle Positions", "") +DefNode(GeometryNode, GEO_NODE_INPUT_CURVE_TILT, 0, "INPUT_CURVE_TILT", InputCurveTilt, "Curve Tilt", "") +DefNode(GeometryNode, GEO_NODE_INPUT_ID, 0, "INPUT_ID", InputID, "ID", "") DefNode(GeometryNode, GEO_NODE_INPUT_INDEX, 0, "INDEX", InputIndex, "Index", "") +DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL_INDEX, 0, "INPUT_MATERIAL_INDEX", InputMaterialIndex, "Material Index", "") DefNode(GeometryNode, GEO_NODE_INPUT_MATERIAL, def_geo_input_material, "INPUT_MATERIAL", InputMaterial, "Material", "") DefNode(GeometryNode, GEO_NODE_INPUT_NORMAL, 0, "INPUT_NORMAL", InputNormal, "Normal", "") DefNode(GeometryNode, GEO_NODE_INPUT_POSITION, 0, "POSITION", InputPosition, "Position", "") +DefNode(GeometryNode, GEO_NODE_INPUT_RADIUS, 0, "INPUT_RADIUS", InputRadius, "Radius", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SHADE_SMOOTH, 0, "INPUT_SHADE_SMOOTH", InputShadeSmooth, "Is Shade Smooth", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_CYCLIC, 0, "INPUT_SPLINE_CYCLIC", InputSplineCyclic, "Is Spline Cyclic", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_LENGTH, 0, "SPLINE_LENGTH", SplineLength, "Spline Length", "") +DefNode(GeometryNode, GEO_NODE_INPUT_SPLINE_RESOLUTION, 0, "INPUT_SPLINE_RESOLUTION", InputSplineResolution, "Spline Resolution", "") +DefNode(GeometryNode, GEO_NODE_INPUT_TANGENT, 0, "INPUT_TANGENT", InputTangent, "Curve Tangent", "") +DefNode(GeometryNode, GEO_NODE_INSTANCE_ON_POINTS, 0, "INSTANCE_ON_POINTS", InstanceOnPoints, "Instance on Points", "") +DefNode(GeometryNode, GEO_NODE_INSTANCES_TO_POINTS, 0, "INSTANCES_TO_POINTS", InstancesToPoints, "Instances to Points", "") DefNode(GeometryNode, GEO_NODE_IS_VIEWPORT, 0, "IS_VIEWPORT", IsViewport, "Is Viewport", "") DefNode(GeometryNode, GEO_NODE_JOIN_GEOMETRY, 0, "JOIN_GEOMETRY", JoinGeometry, "Join Geometry", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_ASSIGN, 0, "MATERIAL_ASSIGN", MaterialAssign, "Material Assign", "") -DefNode(GeometryNode, GEO_NODE_MATERIAL_REPLACE, 0, "MATERIAL_REPLACE", MaterialReplace, "Material Replace", "") DefNode(GeometryNode, GEO_NODE_MATERIAL_SELECTION, 0, "MATERIAL_SELECTION", MaterialSelection, "Material Selection", "") +DefNode(GeometryNode, GEO_NODE_MESH_BOOLEAN, def_geo_boolean, "MESH_BOOLEAN", MeshBoolean, "Mesh Boolean", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CIRCLE, def_geo_mesh_circle, "MESH_PRIMITIVE_CIRCLE", MeshCircle, "Mesh Circle", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CONE, def_geo_mesh_cone, "MESH_PRIMITIVE_CONE", MeshCone, "Cone", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_CUBE, 0, "MESH_PRIMITIVE_CUBE", MeshCube, "Cube", "") @@ -343,15 +377,46 @@ DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_GRID, 0, "MESH_PRIMITIVE_GRID", Me DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_ICO_SPHERE, 0, "MESH_PRIMITIVE_ICO_SPHERE", MeshIcoSphere, "Ico Sphere", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_LINE, def_geo_mesh_line, "MESH_PRIMITIVE_LINE", MeshLine, "Mesh Line", "") DefNode(GeometryNode, GEO_NODE_MESH_PRIMITIVE_UV_SPHERE, 0, "MESH_PRIMITIVE_UV_SPHERE", MeshUVSphere, "UV Sphere", "") -DefNode(GeometryNode, GEO_NODE_MESH_SUBDIVIDE, 0, "MESH_SUBDIVIDE", MeshSubdivide, "Mesh Subdivide", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_CURVE, 0, "MESH_TO_CURVE", MeshToCurve, "Mesh to Curve", "") +DefNode(GeometryNode, GEO_NODE_MESH_TO_POINTS, def_geo_mesh_to_points, "MESH_TO_POINTS", MeshToPoints, "Mesh to Points", "") DefNode(GeometryNode, GEO_NODE_OBJECT_INFO, def_geo_object_info, "OBJECT_INFO", ObjectInfo, "Object Info", "") +DefNode(GeometryNode, GEO_NODE_POINTS_TO_VERTICES, 0, "POINTS_TO_VERTICES", PointsToVertices, "Points to Vertices", "") +DefNode(GeometryNode, GEO_NODE_POINTS_TO_VOLUME, def_geo_points_to_volume, "POINTS_TO_VOLUME", PointsToVolume, "Points to Volume", "") +DefNode(GeometryNode, GEO_NODE_PROXIMITY, def_geo_proximity, "PROXIMITY", Proximity, "Geometry Proximity", "") +DefNode(GeometryNode, GEO_NODE_RAYCAST, def_geo_raycast, "RAYCAST", Raycast, "Raycast", "") +DefNode(GeometryNode, GEO_NODE_REALIZE_INSTANCES, 0, "REALIZE_INSTANCES", RealizeInstances, "Realize Instances", "") +DefNode(GeometryNode, GEO_NODE_REPLACE_MATERIAL, 0, "REPLACE_MATERIAL", ReplaceMaterial, "Replace Material", "") +DefNode(GeometryNode, GEO_NODE_RESAMPLE_CURVE, def_geo_curve_resample, "RESAMPLE_CURVE", ResampleCurve, "Resample Curve", "") +DefNode(GeometryNode, GEO_NODE_REVERSE_CURVE, 0, "REVERSE_CURVE", ReverseCurve, "Reverse Curve", "") +DefNode(GeometryNode, GEO_NODE_ROTATE_INSTANCES, 0, "ROTATE_INSTANCES", RotateInstances, "Rotate Instances", "") +DefNode(GeometryNode, GEO_NODE_SAMPLE_CURVE, def_geo_curve_sample, "SAMPLE_CURVE", SampleCurve, "Sample Curve", "") +DefNode(GeometryNode, GEO_NODE_SCALE_INSTANCES, 0, "SCALE_INSTANCES", ScaleInstances, "Scale Instances", "") DefNode(GeometryNode, GEO_NODE_SEPARATE_COMPONENTS, 0, "SEPARATE_COMPONENTS", SeparateComponents, "Separate Components", "") +DefNode(GeometryNode, GEO_NODE_SEPARATE_GEOMETRY, def_geo_separate_geometry, "SEPARATE_GEOMETRY", SeparateGeometry, "Separate Geometry", "") +DefNode(GeometryNode, GEO_NODE_SET_CURVE_HANDLES, def_geo_curve_set_handle_positions, "SET_CURVE_HANDLES", SetCurveHandlePositions, "Set Handle Positions", "") +DefNode(GeometryNode, GEO_NODE_SET_CURVE_RADIUS, 0, "SET_CURVE_RADIUS", SetCurveRadius, "Set Curve Radius", "") +DefNode(GeometryNode, GEO_NODE_SET_CURVE_TILT, 0, "SET_CURVE_TILT", SetCurveTilt, "Set Curve Tilt", "") +DefNode(GeometryNode, GEO_NODE_SET_ID, 0, "SET_ID", SetID, "Set ID", "") +DefNode(GeometryNode, GEO_NODE_SET_MATERIAL_INDEX, 0, "SET_MATERIAL_INDEX", SetMaterialIndex, "Set Material Index", "") +DefNode(GeometryNode, GEO_NODE_SET_MATERIAL, 0, "SET_MATERIAL", SetMaterial, "Set Material", "") +DefNode(GeometryNode, GEO_NODE_SET_POINT_RADIUS, 0, "SET_POINT_RADIUS", SetPointRadius, "Set Point Radius", "") DefNode(GeometryNode, GEO_NODE_SET_POSITION, 0, "SET_POSITION", SetPosition, "Set Position", "") +DefNode(GeometryNode, GEO_NODE_SET_SHADE_SMOOTH, 0, "SET_SHADE_SMOOTH", SetShadeSmooth, "Set Shade Smooth", "") +DefNode(GeometryNode, GEO_NODE_SET_SPLINE_CYCLIC, 0, "SET_SPLINE_CYCLIC", SetSplineCyclic, "Set Spline Cyclic", "") +DefNode(GeometryNode, GEO_NODE_SET_SPLINE_RESOLUTION, 0, "SET_SPLINE_RESOLUTION", SetSplineResolution, "Set Spline Resolution", "") +DefNode(GeometryNode, GEO_NODE_SPLIT_EDGES, 0, "SPLIT_EDGES", SplitEdges, "Split Edges", "") +DefNode(GeometryNode, GEO_NODE_STRING_JOIN, 0, "STRING_JOIN", StringJoin, "Join Strings", "") +DefNode(GeometryNode, GEO_NODE_STRING_TO_CURVES, def_geo_string_to_curves, "STRING_TO_CURVES", StringToCurves, "String to Curves", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_CURVE, 0, "SUBDIVIDE_CURVE", SubdivideCurve, "Subdivide Curve", "") +DefNode(GeometryNode, GEO_NODE_SUBDIVIDE_MESH, 0, "SUBDIVIDE_MESH", SubdivideMesh, "Subdivide Mesh", "") DefNode(GeometryNode, GEO_NODE_SUBDIVISION_SURFACE, def_geo_subdivision_surface, "SUBDIVISION_SURFACE", SubdivisionSurface, "Subdivision Surface", "") DefNode(GeometryNode, GEO_NODE_SWITCH, def_geo_switch, "SWITCH", Switch, "Switch", "") +DefNode(GeometryNode, GEO_NODE_TRANSFER_ATTRIBUTE, def_geo_transfer_attribute, "ATTRIBUTE_TRANSFER", AttributeTransfer, "Transfer Attribute", "") DefNode(GeometryNode, GEO_NODE_TRANSFORM, 0, "TRANSFORM", Transform, "Transform", "") +DefNode(GeometryNode, GEO_NODE_TRANSLATE_INSTANCES, 0, "TRANSLATE_INSTANCES", TranslateInstances, "Translate Instances", "") DefNode(GeometryNode, GEO_NODE_TRIANGULATE, def_geo_triangulate, "TRIANGULATE", Triangulate, "Triangulate", "") -DefNode(GeometryNode, GEO_NODE_VIEWER, 0, "VIEWER", Viewer, "Viewer", "") +DefNode(GeometryNode, GEO_NODE_TRIM_CURVE, def_geo_curve_trim, "TRIM_CURVE", TrimCurve, "Trim Curve", "") +DefNode(GeometryNode, GEO_NODE_VIEWER, def_geo_viewer, "VIEWER", Viewer, "Viewer", "") DefNode(GeometryNode, GEO_NODE_VOLUME_TO_MESH, def_geo_volume_to_mesh, "VOLUME_TO_MESH", VolumeToMesh, "Volume to Mesh", "") /* undefine macros */ diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.cc similarity index 84% rename from source/blender/nodes/composite/node_composite_tree.c rename to source/blender/nodes/composite/node_composite_tree.cc index cc657d6f91d..a596a85b748 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.cc @@ -21,7 +21,7 @@ * \ingroup nodes */ -#include +#include #include "DNA_color_types.h" #include "DNA_node_types.h" @@ -41,7 +41,7 @@ #include "RNA_access.h" #include "NOD_composite.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" #ifdef WITH_COMPOSITOR # include "COM_compositor.h" @@ -55,7 +55,7 @@ static void composite_get_from_context(const bContext *C, { Scene *scene = CTX_data_scene(C); - *r_from = NULL; + *r_from = nullptr; *r_id = &scene->id; *r_ntree = scene->nodetree; } @@ -77,19 +77,16 @@ static void foreach_nodeclass(Scene *UNUSED(scene), void *calldata, bNodeClassCa static void free_node_cache(bNodeTree *UNUSED(ntree), bNode *node) { - bNodeSocket *sock; - - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (sock->cache) { - sock->cache = NULL; + sock->cache = nullptr; } } } static void free_cache(bNodeTree *ntree) { - bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { free_node_cache(ntree, node); } } @@ -98,16 +95,16 @@ static void free_cache(bNodeTree *ntree) static void localize(bNodeTree *localtree, bNodeTree *ntree) { - bNode *node = ntree->nodes.first; - bNode *local_node = localtree->nodes.first; - while (node != NULL) { + bNode *node = (bNode *)ntree->nodes.first; + bNode *local_node = (bNode *)localtree->nodes.first; + while (node != nullptr) { /* Ensure new user input gets handled ok. */ node->need_exec = 0; local_node->original = node; /* move over the compbufs */ - /* right after ntreeCopyTree() oldsock pointers are valid */ + /* right after #ntreeCopyTree() `oldsock` pointers are valid */ if (ELEM(node->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if (node->id) { @@ -115,16 +112,16 @@ static void localize(bNodeTree *localtree, bNodeTree *ntree) local_node->id = (ID *)node->id; } else { - local_node->id = NULL; + local_node->id = nullptr; } } } - bNodeSocket *output_sock = node->outputs.first; - bNodeSocket *local_output_sock = local_node->outputs.first; - while (output_sock != NULL) { + bNodeSocket *output_sock = (bNodeSocket *)node->outputs.first; + bNodeSocket *local_output_sock = (bNodeSocket *)local_node->outputs.first; + while (output_sock != nullptr) { local_output_sock->cache = output_sock->cache; - output_sock->cache = NULL; + output_sock->cache = nullptr; /* This is actually link to original: someone was just lazy enough and tried to save few * bytes in the cost of readability. */ local_output_sock->new_sock = output_sock; @@ -151,7 +148,7 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) /* move over the compbufs and previews */ BKE_node_preview_merge_tree(ntree, localtree, true); - for (lnode = localtree->nodes.first; lnode; lnode = lnode->next) { + for (lnode = (bNode *)localtree->nodes.first; lnode; lnode = lnode->next) { if (ntreeNodeExists(ntree, lnode->new_node)) { if (ELEM(lnode->type, CMP_NODE_VIEWER, CMP_NODE_SPLITVIEWER)) { if (lnode->id && (lnode->flag & NODE_DO_OUTPUT)) { @@ -165,18 +162,19 @@ static void local_merge(Main *bmain, bNodeTree *localtree, bNodeTree *ntree) * copied back to original node */ if (lnode->storage) { if (lnode->new_node->storage) { - BKE_tracking_distortion_free(lnode->new_node->storage); + BKE_tracking_distortion_free((MovieDistortion *)lnode->new_node->storage); } - lnode->new_node->storage = BKE_tracking_distortion_copy(lnode->storage); + lnode->new_node->storage = BKE_tracking_distortion_copy( + (MovieDistortion *)lnode->storage); } } - for (lsock = lnode->outputs.first; lsock; lsock = lsock->next) { + for (lsock = (bNodeSocket *)lnode->outputs.first; lsock; lsock = lsock->next) { if (ntreeOutputExists(lnode->new_node, lsock->new_sock)) { lsock->new_sock->cache = lsock->cache; - lsock->cache = NULL; - lsock->new_sock = NULL; + lsock->cache = nullptr; + lsock->new_sock = nullptr; } } } @@ -216,13 +214,13 @@ bNodeTreeType *ntreeType_Composite; void register_node_tree_type_cmp(void) { - bNodeTreeType *tt = ntreeType_Composite = MEM_callocN(sizeof(bNodeTreeType), - "compositor node tree type"); + bNodeTreeType *tt = ntreeType_Composite = (bNodeTreeType *)MEM_callocN( + sizeof(bNodeTreeType), "compositor node tree type"); tt->type = NTREE_COMPOSIT; strcpy(tt->idname, "CompositorNodeTree"); strcpy(tt->ui_name, N_("Compositor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Compositing nodes")); tt->free_cache = free_cache; @@ -241,9 +239,6 @@ void register_node_tree_type_cmp(void) ntreeTypeAdd(tt); } -extern void *COM_linker_hack; /* Quiet warning. */ -void *COM_linker_hack = NULL; - void ntreeCompositExecTree(Scene *scene, bNodeTree *ntree, RenderData *rd, @@ -276,13 +271,11 @@ void ntreeCompositExecTree(Scene *scene, */ void ntreeCompositUpdateRLayers(bNodeTree *ntree) { - bNode *node; - - if (ntree == NULL) { + if (ntree == nullptr) { return; } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_R_LAYERS) { node_cmp_rlayers_outputs(ntree, node); } @@ -295,13 +288,11 @@ void ntreeCompositRegisterPass(bNodeTree *ntree, const char *name, eNodeSocketDatatype type) { - bNode *node; - - if (ntree == NULL) { + if (ntree == nullptr) { return; } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { if (node->type == CMP_NODE_R_LAYERS) { node_cmp_rlayers_register_pass(ntree, node, scene, view_layer, name, type); } @@ -317,15 +308,14 @@ void ntreeCompositTagRender(Scene *scene) * This is still rather weak though, * ideally render struct would store own main AND original G_MAIN. */ - for (Scene *sce_iter = G_MAIN->scenes.first; sce_iter; sce_iter = sce_iter->id.next) { + for (Scene *sce_iter = (Scene *)G_MAIN->scenes.first; sce_iter; + sce_iter = (Scene *)sce_iter->id.next) { if (sce_iter->nodetree) { - bNode *node; - - for (node = sce_iter->nodetree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &sce_iter->nodetree->nodes) { if (node->id == (ID *)scene || node->type == CMP_NODE_COMPOSITE) { nodeUpdate(sce_iter->nodetree, node); } - else if (node->type == CMP_NODE_TEXTURE) /* uses scene sizex/sizey */ { + else if (node->type == CMP_NODE_TEXTURE) /* uses scene size_x/size_y */ { nodeUpdate(sce_iter->nodetree, node); } } @@ -336,13 +326,11 @@ void ntreeCompositTagRender(Scene *scene) /* XXX after render animation system gets a refresh, this call allows composite to end clean */ void ntreeCompositClearTags(bNodeTree *ntree) { - bNode *node; - - if (ntree == NULL) { + if (ntree == nullptr) { return; } - for (node = ntree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &ntree->nodes) { node->need_exec = 0; if (node->type == NODE_GROUP) { ntreeCompositClearTags((bNodeTree *)node->id); diff --git a/source/blender/nodes/composite/node_composite_util.c b/source/blender/nodes/composite/node_composite_util.cc similarity index 92% rename from source/blender/nodes/composite/node_composite_util.c rename to source/blender/nodes/composite/node_composite_util.cc index 6cc17d8c272..86aaec61bc3 100644 --- a/source/blender/nodes/composite/node_composite_util.c +++ b/source/blender/nodes/composite/node_composite_util.cc @@ -21,7 +21,7 @@ * \ingroup nodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" bool cmp_node_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, @@ -36,11 +36,10 @@ bool cmp_node_poll_default(bNodeType *UNUSED(ntype), void cmp_node_update_default(bNodeTree *UNUSED(ntree), bNode *node) { - bNodeSocket *sock; - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (sock->cache) { // free_compbuf(sock->cache); - // sock->cache = NULL; + // sock->cache = nullptr; } } node->need_exec = 1; diff --git a/source/blender/nodes/composite/node_composite_util.h b/source/blender/nodes/composite/node_composite_util.hh similarity index 92% rename from source/blender/nodes/composite/node_composite_util.h rename to source/blender/nodes/composite/node_composite_util.hh index 4fcccbb79f0..6fd82ffc93f 100644 --- a/source/blender/nodes/composite/node_composite_util.h +++ b/source/blender/nodes/composite/node_composite_util.hh @@ -47,20 +47,13 @@ /* only for forward declarations */ #include "NOD_composite.h" - -#ifdef __cplusplus -extern "C" { -#endif +#include "NOD_socket_declarations.hh" #define CMP_SCALE_MAX 12000 bool cmp_node_poll_default(struct bNodeType *ntype, struct bNodeTree *ntree, - const char **r_disabled_info); + const char **r_disabled_hint); void cmp_node_update_default(struct bNodeTree *ntree, struct bNode *node); void cmp_node_type_base( struct bNodeType *ntype, int type, const char *name, short nclass, short flag); - -#ifdef __cplusplus -} -#endif diff --git a/source/blender/nodes/composite/nodes/node_composite_alphaOver.c b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_alphaOver.c rename to source/blender/nodes/composite/nodes/node_composite_alphaOver.cc index 7a08bd51575..b6f64ed00c7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_alphaOver.c +++ b/source/blender/nodes/composite/nodes/node_composite_alphaOver.cc @@ -21,9 +21,10 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** ALPHAOVER ******************** */ + static bNodeSocketTemplate cmp_node_alphaover_in[] = { {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, diff --git a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_antialiasing.c rename to source/blender/nodes/composite/nodes/node_composite_antialiasing.cc index a5906c31093..23e63b9a53b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_antialiasing.c +++ b/source/blender/nodes/composite/nodes/node_composite_antialiasing.cc @@ -23,7 +23,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Anti-Aliasing (SMAA 1x) ******************** */ @@ -34,7 +34,8 @@ static bNodeSocketTemplate cmp_node_antialiasing_out[] = {{SOCK_RGBA, N_("Image" static void node_composit_init_antialiasing(bNodeTree *UNUSED(ntree), bNode *node) { - NodeAntiAliasingData *data = MEM_callocN(sizeof(NodeAntiAliasingData), "node antialiasing data"); + NodeAntiAliasingData *data = (NodeAntiAliasingData *)MEM_callocN(sizeof(NodeAntiAliasingData), + "node antialiasing data"); data->threshold = CMP_DEFAULT_SMAA_THRESHOLD; data->contrast_limit = CMP_DEFAULT_SMAA_CONTRAST_LIMIT; diff --git a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.c b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc similarity index 88% rename from source/blender/nodes/composite/nodes/node_composite_bilateralblur.c rename to source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc index 270a137280c..3e724d17a10 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bilateralblur.c +++ b/source/blender/nodes/composite/nodes/node_composite_bilateralblur.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** BILATERALBLUR ******************** */ static bNodeSocketTemplate cmp_node_bilateralblur_in[] = { @@ -36,8 +36,8 @@ static bNodeSocketTemplate cmp_node_bilateralblur_out[] = { static void node_composit_init_bilateralblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBilateralBlurData *nbbd = MEM_callocN(sizeof(NodeBilateralBlurData), - "node bilateral blur data"); + NodeBilateralBlurData *nbbd = (NodeBilateralBlurData *)MEM_callocN(sizeof(NodeBilateralBlurData), + "node bilateral blur data"); node->storage = nbbd; nbbd->iter = 1; nbbd->sigma_color = 0.3; diff --git a/source/blender/nodes/composite/nodes/node_composite_blur.c b/source/blender/nodes/composite/nodes/node_composite_blur.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_blur.c rename to source/blender/nodes/composite/nodes/node_composite_blur.cc index 92379f4552b..c5c0c21929e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_blur.c +++ b/source/blender/nodes/composite/nodes/node_composite_blur.cc @@ -22,7 +22,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** BLUR ******************** */ static bNodeSocketTemplate cmp_node_blur_in[] = { @@ -33,7 +33,7 @@ static bNodeSocketTemplate cmp_node_blur_out[] = {{SOCK_RGBA, N_("Image")}, {-1, static void node_composit_init_blur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBlurData *data = MEM_callocN(sizeof(NodeBlurData), "node blur data"); + NodeBlurData *data = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data"); data->filtertype = R_FILTER_GAUSS; node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_bokehblur.c rename to source/blender/nodes/composite/nodes/node_composite_bokehblur.cc index d724a83e5a2..f130a642e20 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehblur.c +++ b/source/blender/nodes/composite/nodes/node_composite_bokehblur.cc @@ -22,7 +22,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** BLUR ******************** */ static bNodeSocketTemplate cmp_node_bokehblur_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc similarity index 80% rename from source/blender/nodes/composite/nodes/node_composite_bokehimage.c rename to source/blender/nodes/composite/nodes/node_composite_bokehimage.cc index 744aba417be..3f8a7606d94 100644 --- a/source/blender/nodes/composite/nodes/node_composite_bokehimage.c +++ b/source/blender/nodes/composite/nodes/node_composite_bokehimage.cc @@ -21,18 +21,22 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** Bokeh image Tools ******************** */ -static bNodeSocketTemplate cmp_node_bokehimage_out[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_bokehimage_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_bokehimage(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBokehImage *data = MEM_callocN(sizeof(NodeBokehImage), "NodeBokehImage"); + NodeBokehImage *data = (NodeBokehImage *)MEM_callocN(sizeof(NodeBokehImage), "NodeBokehImage"); data->angle = 0.0f; data->flaps = 5; data->rounding = 0.0f; @@ -46,7 +50,7 @@ void register_node_type_cmp_bokehimage(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_BOKEHIMAGE, "Bokeh Image", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, cmp_node_bokehimage_out); + ntype.declare = blender::nodes::cmp_node_bokehimage_declare; node_type_init(&ntype, node_composit_init_bokehimage); node_type_storage( &ntype, "NodeBokehImage", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_boxmask.c b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_boxmask.c rename to source/blender/nodes/composite/nodes/node_composite_boxmask.cc index e646b9a9adf..cdf96065f97 100644 --- a/source/blender/nodes/composite/nodes/node_composite_boxmask.c +++ b/source/blender/nodes/composite/nodes/node_composite_boxmask.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** SCALAR MATH ******************** */ static bNodeSocketTemplate cmp_node_boxmask_in[] = { @@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_boxmask_out[] = { static void node_composit_init_boxmask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBoxMask *data = MEM_callocN(sizeof(NodeBoxMask), "NodeBoxMask"); + NodeBoxMask *data = (NodeBoxMask *)MEM_callocN(sizeof(NodeBoxMask), "NodeBoxMask"); data->x = 0.5; data->y = 0.5; data->width = 0.2; diff --git a/source/blender/nodes/composite/nodes/node_composite_brightness.c b/source/blender/nodes/composite/nodes/node_composite_brightness.cc similarity index 67% rename from source/blender/nodes/composite/nodes/node_composite_brightness.c rename to source/blender/nodes/composite/nodes/node_composite_brightness.cc index 5beecb55665..028afad3cf8 100644 --- a/source/blender/nodes/composite/nodes/node_composite_brightness.c +++ b/source/blender/nodes/composite/nodes/node_composite_brightness.cc @@ -21,20 +21,21 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" -/* **************** Brigh and contrsast ******************** */ +/* **************** Bright and Contrast ******************** */ -static bNodeSocketTemplate cmp_node_brightcontrast_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Bright"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Contrast"), 0.0f, 0.0f, 0.0f, 0.0f, -100.0f, 100.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_brightcontrast_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_brightcontrast_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Bright")).min(-100.0f).max(100.0f); + b.add_input(N_("Contrast")).min(-100.0f).max(100.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_brightcontrast(bNodeTree *UNUSED(ntree), bNode *node) { @@ -46,7 +47,7 @@ void register_node_type_cmp_brightcontrast(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_BRIGHTCONTRAST, "Bright/Contrast", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_brightcontrast_in, cmp_node_brightcontrast_out); + ntype.declare = blender::nodes::cmp_node_brightcontrast_declare; node_type_init(&ntype, node_composit_init_brightcontrast); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_channelMatte.c b/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_channelMatte.c rename to source/blender/nodes/composite/nodes/node_composite_channelMatte.cc index 9912c10b368..e211bc45b17 100644 --- a/source/blender/nodes/composite/nodes/node_composite_channelMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_channelMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Channel Matte Node ********************************* */ static bNodeSocketTemplate cmp_node_channel_matte_in[] = { @@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_channel_matte_out[] = { static void node_composit_init_channel_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = 1.0f; c->t2 = 0.0f; diff --git a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.c b/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_chromaMatte.c rename to source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc index 705566df35a..990778160df 100644 --- a/source/blender/nodes/composite/nodes/node_composite_chromaMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_chromaMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Chroma Key ********************************************************** */ static bNodeSocketTemplate cmp_node_chroma_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_chroma_out[] = { static void node_composit_init_chroma_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = DEG2RADF(30.0f); c->t2 = DEG2RADF(10.0f); diff --git a/source/blender/nodes/composite/nodes/node_composite_colorMatte.c b/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_colorMatte.c rename to source/blender/nodes/composite/nodes/node_composite_colorMatte.cc index f5cf7bcbf22..fc9a0075b14 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Color Key ********************************************************** */ static bNodeSocketTemplate cmp_node_color_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_color_out[] = { static void node_composit_init_color_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node color"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node color"); node->storage = c; c->t1 = 0.01f; c->t2 = 0.1f; diff --git a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c b/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_colorSpill.c rename to source/blender/nodes/composite/nodes/node_composite_colorSpill.cc index 8ff4bcdced3..7bdc2e8289e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorSpill.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorSpill.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Color Spill Suppression ********************************* */ static bNodeSocketTemplate cmp_node_color_spill_in[] = { @@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_color_spill_out[] = { static void node_composit_init_color_spill(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorspill *ncs = MEM_callocN(sizeof(NodeColorspill), "node colorspill"); + NodeColorspill *ncs = (NodeColorspill *)MEM_callocN(sizeof(NodeColorspill), "node colorspill"); node->storage = ncs; node->custom1 = 2; /* green channel */ node->custom2 = 0; /* simple limit algorithm */ diff --git a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc similarity index 75% rename from source/blender/nodes/composite/nodes/node_composite_colorbalance.c rename to source/blender/nodes/composite/nodes/node_composite_colorbalance.cc index 0525229697a..ef8af5f81a6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorbalance.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorbalance.cc @@ -21,19 +21,20 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Color Balance ********************************* */ -static bNodeSocketTemplate cmp_node_colorbalance_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_colorbalance_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_colorbalance_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes /* Sync functions update formula parameters for other modes, such that the result is comparable. * Note that the results are not exactly the same due to differences in color handling @@ -43,10 +44,9 @@ static bNodeSocketTemplate cmp_node_colorbalance_out[] = { void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorBalance *n = node->storage; - int c; + NodeColorBalance *n = (NodeColorBalance *)node->storage; - for (c = 0; c < 3; c++) { + for (int c = 0; c < 3; c++) { n->slope[c] = (2.0f - n->lift[c]) * n->gain[c]; n->offset[c] = (n->lift[c] - 1.0f) * n->gain[c]; n->power[c] = (n->gamma[c] != 0.0f) ? 1.0f / n->gamma[c] : 1000000.0f; @@ -55,10 +55,9 @@ void ntreeCompositColorBalanceSyncFromLGG(bNodeTree *UNUSED(ntree), bNode *node) void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorBalance *n = node->storage; - int c; + NodeColorBalance *n = (NodeColorBalance *)node->storage; - for (c = 0; c < 3; c++) { + for (int c = 0; c < 3; c++) { float d = n->slope[c] + n->offset[c]; n->lift[c] = (d != 0.0f ? n->slope[c] + 2.0f * n->offset[c] / d : 0.0f); n->gain[c] = d; @@ -68,7 +67,8 @@ void ntreeCompositColorBalanceSyncFromCDL(bNodeTree *UNUSED(ntree), bNode *node) static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorBalance *n = node->storage = MEM_callocN(sizeof(NodeColorBalance), "node colorbalance"); + NodeColorBalance *n = (NodeColorBalance *)MEM_callocN(sizeof(NodeColorBalance), + "node colorbalance"); n->lift[0] = n->lift[1] = n->lift[2] = 1.0f; n->gamma[0] = n->gamma[1] = n->gamma[2] = 1.0f; @@ -77,6 +77,7 @@ static void node_composit_init_colorbalance(bNodeTree *UNUSED(ntree), bNode *nod n->slope[0] = n->slope[1] = n->slope[2] = 1.0f; n->offset[0] = n->offset[1] = n->offset[2] = 0.0f; n->power[0] = n->power[1] = n->power[2] = 1.0f; + node->storage = n; } void register_node_type_cmp_colorbalance(void) @@ -84,7 +85,7 @@ void register_node_type_cmp_colorbalance(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COLORBALANCE, "Color Balance", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_colorbalance_in, cmp_node_colorbalance_out); + ntype.declare = blender::nodes::cmp_node_colorbalance_declare; node_type_size(&ntype, 400, 200, 400); node_type_init(&ntype, node_composit_init_colorbalance); node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.c b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc similarity index 74% rename from source/blender/nodes/composite/nodes/node_composite_colorcorrection.c rename to source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc index 45d39f8be8d..095fbef826a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_colorcorrection.c +++ b/source/blender/nodes/composite/nodes/node_composite_colorcorrection.cc @@ -21,24 +21,25 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" -/* ******************* Color Balance ********************************* */ -static bNodeSocketTemplate cmp_node_colorcorrection_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Mask"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; +/* ******************* Color Correction ********************************* */ -static bNodeSocketTemplate cmp_node_colorcorrection_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_colorcorrection_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Mask")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode *node) { - NodeColorCorrection *n = node->storage = MEM_callocN(sizeof(NodeColorCorrection), - "node colorcorrection"); + NodeColorCorrection *n = (NodeColorCorrection *)MEM_callocN(sizeof(NodeColorCorrection), + "node colorcorrection"); n->startmidtones = 0.2f; n->endmidtones = 0.7f; n->master.contrast = 1.0f; @@ -62,6 +63,7 @@ static void node_composit_init_colorcorrection(bNodeTree *UNUSED(ntree), bNode * n->highlights.lift = 0.0f; n->highlights.saturation = 1.0f; node->custom1 = 7; // red + green + blue enabled + node->storage = n; } void register_node_type_cmp_colorcorrection(void) @@ -69,7 +71,7 @@ void register_node_type_cmp_colorcorrection(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COLORCORRECTION, "Color Correction", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_colorcorrection_in, cmp_node_colorcorrection_out); + ntype.declare = blender::nodes::cmp_node_colorcorrection_declare; node_type_size(&ntype, 400, 200, 600); node_type_init(&ntype, node_composit_init_colorcorrection); node_type_storage( diff --git a/source/blender/nodes/composite/nodes/node_composite_common.c b/source/blender/nodes/composite/nodes/node_composite_common.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_common.c rename to source/blender/nodes/composite/nodes/node_composite_common.cc index 61abc80fe93..fecf6795ef7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_common.c +++ b/source/blender/nodes/composite/nodes/node_composite_common.cc @@ -26,7 +26,7 @@ #include "NOD_common.h" #include "node_common.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_node.h" @@ -46,10 +46,10 @@ void register_node_type_cmp_group(void) ntype.insert_link = node_insert_link_default; ntype.update_internal_links = node_update_internal_links_default; ntype.rna_ext.srna = RNA_struct_find("CompositorNodeGroup"); - BLI_assert(ntype.rna_ext.srna != NULL); + BLI_assert(ntype.rna_ext.srna != nullptr); RNA_struct_blender_type_set(ntype.rna_ext.srna, &ntype); - node_type_socket_templates(&ntype, NULL, NULL); + node_type_socket_templates(&ntype, nullptr, nullptr); node_type_size(&ntype, 140, 60, 400); node_type_label(&ntype, node_group_label); node_type_group_update(&ntype, node_group_update); @@ -60,13 +60,13 @@ void register_node_type_cmp_group(void) void register_node_type_cmp_custom_group(bNodeType *ntype) { /* These methods can be overridden but need a default implementation otherwise. */ - if (ntype->poll == NULL) { + if (ntype->poll == nullptr) { ntype->poll = cmp_node_poll_default; } - if (ntype->insert_link == NULL) { + if (ntype->insert_link == nullptr) { ntype->insert_link = node_insert_link_default; } - if (ntype->update_internal_links == NULL) { + if (ntype->update_internal_links == nullptr) { ntype->update_internal_links = node_update_internal_links_default; } } diff --git a/source/blender/nodes/composite/nodes/node_composite_composite.c b/source/blender/nodes/composite/nodes/node_composite_composite.cc similarity index 69% rename from source/blender/nodes/composite/nodes/node_composite_composite.c rename to source/blender/nodes/composite/nodes/node_composite_composite.cc index dee2ce6b3ec..4247e81e9b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_composite.c +++ b/source/blender/nodes/composite/nodes/node_composite_composite.cc @@ -21,25 +21,30 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** COMPOSITE ******************** */ -static bNodeSocketTemplate cmp_node_composite_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Z"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_composite_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); +} + +} // namespace blender::nodes void register_node_type_cmp_composite(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMPOSITE, "Composite", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_composite_in, NULL); + ntype.declare = blender::nodes::cmp_node_composite_declare; /* Do not allow muting for this node. */ - node_type_internal_links(&ntype, NULL); + node_type_internal_links(&ntype, nullptr); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_cornerpin.c b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_cornerpin.c rename to source/blender/nodes/composite/nodes/node_composite_cornerpin.cc index 135120c45aa..b5ca1fb015e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cornerpin.c +++ b/source/blender/nodes/composite/nodes/node_composite_cornerpin.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate inputs[] = { {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, diff --git a/source/blender/nodes/composite/nodes/node_composite_crop.c b/source/blender/nodes/composite/nodes/node_composite_crop.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_crop.c rename to source/blender/nodes/composite/nodes/node_composite_crop.cc index 868df5367c4..f07dba8a74b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_crop.c +++ b/source/blender/nodes/composite/nodes/node_composite_crop.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Crop ******************** */ @@ -36,7 +36,7 @@ static bNodeSocketTemplate cmp_node_crop_out[] = { static void node_composit_init_crop(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTwoXYs *nxy = MEM_callocN(sizeof(NodeTwoXYs), "node xy data"); + NodeTwoXYs *nxy = (NodeTwoXYs *)MEM_callocN(sizeof(NodeTwoXYs), "node xy data"); node->storage = nxy; nxy->x1 = 0; nxy->x2 = 0; diff --git a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc index 51dd73a86af..6657267b016 100644 --- a/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc +++ b/source/blender/nodes/composite/nodes/node_composite_cryptomatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BLI_assert.h" #include "BLI_dynstr.h" diff --git a/source/blender/nodes/composite/nodes/node_composite_curves.c b/source/blender/nodes/composite/nodes/node_composite_curves.cc similarity index 76% rename from source/blender/nodes/composite/nodes/node_composite_curves.c rename to source/blender/nodes/composite/nodes/node_composite_curves.cc index 470540d3337..5f99bb57768 100644 --- a/source/blender/nodes/composite/nodes/node_composite_curves.c +++ b/source/blender/nodes/composite/nodes/node_composite_curves.cc @@ -21,16 +21,20 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** CURVE Time ******************** */ -/* custom1 = sfra, custom2 = efra */ -static bNodeSocketTemplate cmp_node_time_out[] = { - {SOCK_FLOAT, N_("Fac")}, - {-1, ""}, -}; +namespace blender::nodes { +static void cmp_node_time_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Fac")); +} + +} // namespace blender::nodes + +/* custom1 = start_frame, custom2 = end_frame */ static void node_composit_init_curves_time(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 1; @@ -43,7 +47,7 @@ void register_node_type_cmp_curve_time(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TIME, "Time", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_time_out); + ntype.declare = blender::nodes::cmp_node_time_declare; node_type_size(&ntype, 140, 100, 320); node_type_init(&ntype, node_composit_init_curves_time); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); @@ -81,18 +85,20 @@ void register_node_type_cmp_curve_vec(void) } /* **************** CURVE RGB ******************** */ -static bNodeSocketTemplate cmp_node_curve_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_RGBA, N_("Black Level"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("White Level"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_curve_rgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_rgbcurves_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Fac")).default_value(1.0f).min(-1.0f).max(1.0f).subtype( + PROP_FACTOR); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Black Level")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input(N_("White Level")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_curve_rgb(bNodeTree *UNUSED(ntree), bNode *node) { @@ -104,7 +110,7 @@ void register_node_type_cmp_curve_rgb(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_curve_rgb_in, cmp_node_curve_rgb_out); + ntype.declare = blender::nodes::cmp_node_rgbcurves_declare; node_type_size(&ntype, 200, 140, 320); node_type_init(&ntype, node_composit_init_curve_rgb); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); diff --git a/source/blender/nodes/composite/nodes/node_composite_defocus.c b/source/blender/nodes/composite/nodes/node_composite_defocus.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_defocus.c rename to source/blender/nodes/composite/nodes/node_composite_defocus.cc index 3803f450f49..1103aff4366 100644 --- a/source/blender/nodes/composite/nodes/node_composite_defocus.c +++ b/source/blender/nodes/composite/nodes/node_composite_defocus.cc @@ -21,11 +21,11 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" -#include +#include -/* ************ qdn: Defocus node ****************** */ +/* ************ Defocus Node ****************** */ static bNodeSocketTemplate cmp_node_defocus_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {SOCK_FLOAT, N_("Z"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, @@ -38,8 +38,8 @@ static bNodeSocketTemplate cmp_node_defocus_out[] = { static void node_composit_init_defocus(bNodeTree *UNUSED(ntree), bNode *node) { - /* qdn: defocus node */ - NodeDefocus *nbd = MEM_callocN(sizeof(NodeDefocus), "node defocus data"); + /* defocus node */ + NodeDefocus *nbd = (NodeDefocus *)MEM_callocN(sizeof(NodeDefocus), "node defocus data"); nbd->bktype = 0; nbd->rotation = 0.0f; nbd->preview = 1; diff --git a/source/blender/nodes/composite/nodes/node_composite_denoise.c b/source/blender/nodes/composite/nodes/node_composite_denoise.cc similarity index 91% rename from source/blender/nodes/composite/nodes/node_composite_denoise.c rename to source/blender/nodes/composite/nodes/node_composite_denoise.cc index 040b350627e..ec085794462 100644 --- a/source/blender/nodes/composite/nodes/node_composite_denoise.c +++ b/source/blender/nodes/composite/nodes/node_composite_denoise.cc @@ -23,7 +23,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_denoise_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f}, @@ -34,8 +34,9 @@ static bNodeSocketTemplate cmp_node_denoise_out[] = {{SOCK_RGBA, N_("Image")}, { static void node_composit_init_denonise(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDenoise *ndg = MEM_callocN(sizeof(NodeDenoise), "node denoise data"); + NodeDenoise *ndg = (NodeDenoise *)MEM_callocN(sizeof(NodeDenoise), "node denoise data"); ndg->hdr = true; + ndg->prefilter = CMP_NODE_DENOISE_PREFILTER_ACCURATE; node->storage = ndg; } diff --git a/source/blender/nodes/composite/nodes/node_composite_despeckle.c b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_despeckle.c rename to source/blender/nodes/composite/nodes/node_composite_despeckle.cc index 18567ee2006..52d91dabeb1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_despeckle.c +++ b/source/blender/nodes/composite/nodes/node_composite_despeckle.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** FILTER ******************** */ static bNodeSocketTemplate cmp_node_despeckle_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_diffMatte.c b/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_diffMatte.c rename to source/blender/nodes/composite/nodes/node_composite_diffMatte.cc index 7871a9e8b04..1e1a48381b7 100644 --- a/source/blender/nodes/composite/nodes/node_composite_diffMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_diffMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* channel Difference Matte ********************************* */ static bNodeSocketTemplate cmp_node_diff_matte_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_diff_matte_out[] = { static void node_composit_init_diff_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = 0.1f; c->t2 = 0.1f; diff --git a/source/blender/nodes/composite/nodes/node_composite_dilate.c b/source/blender/nodes/composite/nodes/node_composite_dilate.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_dilate.c rename to source/blender/nodes/composite/nodes/node_composite_dilate.cc index 12f1f229258..57884a299da 100644 --- a/source/blender/nodes/composite/nodes/node_composite_dilate.c +++ b/source/blender/nodes/composite/nodes/node_composite_dilate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Dilate/Erode ******************** */ @@ -31,7 +31,8 @@ static bNodeSocketTemplate cmp_node_dilateerode_out[] = {{SOCK_FLOAT, N_("Mask") static void node_composit_init_dilateerode(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDilateErode *data = MEM_callocN(sizeof(NodeDilateErode), "NodeDilateErode"); + NodeDilateErode *data = (NodeDilateErode *)MEM_callocN(sizeof(NodeDilateErode), + "NodeDilateErode"); data->falloff = PROP_SMOOTH; node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_directionalblur.c b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc similarity index 92% rename from source/blender/nodes/composite/nodes/node_composite_directionalblur.c rename to source/blender/nodes/composite/nodes/node_composite_directionalblur.cc index 6dd60526edf..d9f82ba5009 100644 --- a/source/blender/nodes/composite/nodes/node_composite_directionalblur.c +++ b/source/blender/nodes/composite/nodes/node_composite_directionalblur.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_dblur_in[] = {{SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; @@ -30,7 +30,7 @@ static bNodeSocketTemplate cmp_node_dblur_out[] = {{SOCK_RGBA, N_("Image")}, {-1 static void node_composit_init_dblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeDBlurData *ndbd = MEM_callocN(sizeof(NodeDBlurData), "node dblur data"); + NodeDBlurData *ndbd = (NodeDBlurData *)MEM_callocN(sizeof(NodeDBlurData), "node dblur data"); node->storage = ndbd; ndbd->iter = 1; ndbd->center_x = 0.5; diff --git a/source/blender/nodes/composite/nodes/node_composite_displace.c b/source/blender/nodes/composite/nodes/node_composite_displace.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_displace.c rename to source/blender/nodes/composite/nodes/node_composite_displace.cc index 819a4f29b3a..b1ed7f05794 100644 --- a/source/blender/nodes/composite/nodes/node_composite_displace.c +++ b/source/blender/nodes/composite/nodes/node_composite_displace.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Displace ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.c b/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_distanceMatte.c rename to source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc index 3e659fe662b..3f8767ecd08 100644 --- a/source/blender/nodes/composite/nodes/node_composite_distanceMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_distanceMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* channel Distance Matte ********************************* */ static bNodeSocketTemplate cmp_node_distance_matte_in[] = { @@ -38,7 +38,7 @@ static bNodeSocketTemplate cmp_node_distance_matte_out[] = { static void node_composit_init_distance_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->channel = 1; c->t1 = 0.1f; diff --git a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c rename to source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc index 6f68b187775..7c9a48efc2d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.c +++ b/source/blender/nodes/composite/nodes/node_composite_doubleEdgeMask.cc @@ -20,7 +20,7 @@ /** \file * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Double Edge Mask ******************** */ static bNodeSocketTemplate cmp_node_doubleedgemask_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_ellipsemask.c rename to source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc index d5e1d519a1c..67196fb0d35 100644 --- a/source/blender/nodes/composite/nodes/node_composite_ellipsemask.c +++ b/source/blender/nodes/composite/nodes/node_composite_ellipsemask.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** SCALAR MATH ******************** */ static bNodeSocketTemplate cmp_node_ellipsemask_in[] = { @@ -34,7 +34,8 @@ static bNodeSocketTemplate cmp_node_ellipsemask_out[] = { static void node_composit_init_ellipsemask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeEllipseMask *data = MEM_callocN(sizeof(NodeEllipseMask), "NodeEllipseMask"); + NodeEllipseMask *data = (NodeEllipseMask *)MEM_callocN(sizeof(NodeEllipseMask), + "NodeEllipseMask"); data->x = 0.5; data->y = 0.5; data->width = 0.2; diff --git a/source/blender/nodes/composite/nodes/node_composite_exposure.c b/source/blender/nodes/composite/nodes/node_composite_exposure.cc similarity index 71% rename from source/blender/nodes/composite/nodes/node_composite_exposure.c rename to source/blender/nodes/composite/nodes/node_composite_exposure.cc index bd27e4a3d76..c1e64065f7e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_exposure.c +++ b/source/blender/nodes/composite/nodes/node_composite_exposure.cc @@ -21,26 +21,27 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Exposure ******************** */ -static bNodeSocketTemplate cmp_node_exposure_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Exposure"), 0.0f, 0.0f, 0.0f, 0.0f, -10.0f, 10.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_exposure_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_exposure_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Exposure")).min(-10.0f).max(10.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes void register_node_type_cmp_exposure(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_EXPOSURE, "Exposure", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_exposure_in, cmp_node_exposure_out); + ntype.declare = blender::nodes::cmp_node_exposure_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_filter.c b/source/blender/nodes/composite/nodes/node_composite_filter.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_filter.c rename to source/blender/nodes/composite/nodes/node_composite_filter.cc index d0ad090ece4..f07619877f4 100644 --- a/source/blender/nodes/composite/nodes/node_composite_filter.c +++ b/source/blender/nodes/composite/nodes/node_composite_filter.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** FILTER ******************** */ static bNodeSocketTemplate cmp_node_filter_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_flip.c b/source/blender/nodes/composite/nodes/node_composite_flip.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_flip.c rename to source/blender/nodes/composite/nodes/node_composite_flip.cc index 91a91bb5f5f..42aa3141f5c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_flip.c +++ b/source/blender/nodes/composite/nodes/node_composite_flip.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Flip ******************** */ static bNodeSocketTemplate cmp_node_flip_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_gamma.c b/source/blender/nodes/composite/nodes/node_composite_gamma.cc similarity index 69% rename from source/blender/nodes/composite/nodes/node_composite_gamma.c rename to source/blender/nodes/composite/nodes/node_composite_gamma.cc index ddcaf691fd2..74152a27485 100644 --- a/source/blender/nodes/composite/nodes/node_composite_gamma.c +++ b/source/blender/nodes/composite/nodes/node_composite_gamma.cc @@ -21,26 +21,31 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Gamma Tools ******************** */ -static bNodeSocketTemplate cmp_node_gamma_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Gamma"), 1.0f, 0.0f, 0.0f, 0.0f, 0.001f, 10.0f, PROP_UNSIGNED}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_gamma_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_gamma_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Gamma")) + .default_value(1.0f) + .min(0.001f) + .max(10.0f) + .subtype(PROP_UNSIGNED); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes void register_node_type_cmp_gamma(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_GAMMA, "Gamma", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_gamma_in, cmp_node_gamma_out); + ntype.declare = blender::nodes::cmp_node_gamma_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_glare.c b/source/blender/nodes/composite/nodes/node_composite_glare.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_glare.c rename to source/blender/nodes/composite/nodes/node_composite_glare.cc index a792fcc86cd..8a2fd1e1584 100644 --- a/source/blender/nodes/composite/nodes/node_composite_glare.c +++ b/source/blender/nodes/composite/nodes/node_composite_glare.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_glare_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, @@ -34,7 +34,7 @@ static bNodeSocketTemplate cmp_node_glare_out[] = { static void node_composit_init_glare(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGlare *ndg = MEM_callocN(sizeof(NodeGlare), "node glare data"); + NodeGlare *ndg = (NodeGlare *)MEM_callocN(sizeof(NodeGlare), "node glare data"); ndg->quality = 1; ndg->type = 2; ndg->iter = 3; diff --git a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc similarity index 57% rename from source/blender/nodes/composite/nodes/node_composite_hueSatVal.c rename to source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc index 494b6136a6e..21430035465 100644 --- a/source/blender/nodes/composite/nodes/node_composite_hueSatVal.c +++ b/source/blender/nodes/composite/nodes/node_composite_hueSatVal.cc @@ -21,28 +21,38 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Hue Saturation ******************** */ -static bNodeSocketTemplate cmp_node_hue_sat_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {SOCK_FLOAT, N_("Hue"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Saturation"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Value"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_hue_sat_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_huesatval_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Hue")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Saturation")) + .default_value(1.0f) + .min(0.0f) + .max(2.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Value")) + .default_value(1.0f) + .min(0.0f) + .max(2.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes void register_node_type_cmp_hue_sat(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_HUE_SAT, "Hue Saturation Value", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_hue_sat_in, cmp_node_hue_sat_out); + ntype.declare = blender::nodes::cmp_node_huesatval_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc similarity index 71% rename from source/blender/nodes/composite/nodes/node_composite_huecorrect.c rename to source/blender/nodes/composite/nodes/node_composite_huecorrect.cc index 6a5c918d9ae..83743bbed18 100644 --- a/source/blender/nodes/composite/nodes/node_composite_huecorrect.c +++ b/source/blender/nodes/composite/nodes/node_composite_huecorrect.cc @@ -21,27 +21,28 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_huecorrect_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; +namespace blender::nodes { -static bNodeSocketTemplate cmp_node_huecorrect_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +static void cmp_node_huecorrect_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_huecorrect(bNodeTree *UNUSED(ntree), bNode *node) { - CurveMapping *cumapping = node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); - int c; + node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); + + CurveMapping *cumapping = (CurveMapping *)node->storage; cumapping->preset = CURVE_PRESET_MID9; - for (c = 0; c < 3; c++) { + for (int c = 0; c < 3; c++) { CurveMap *cuma = &cumapping->cm[c]; BKE_curvemap_reset(cuma, &cumapping->clipr, cumapping->preset, CURVEMAP_SLOPE_POSITIVE); } @@ -55,7 +56,7 @@ void register_node_type_cmp_huecorrect(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_HUECORRECT, "Hue Correct", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_huecorrect_in, cmp_node_huecorrect_out); + ntype.declare = blender::nodes::cmp_node_huecorrect_declare; node_type_size(&ntype, 320, 140, 500); node_type_init(&ntype, node_composit_init_huecorrect); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); diff --git a/source/blender/nodes/composite/nodes/node_composite_idMask.c b/source/blender/nodes/composite/nodes/node_composite_idMask.cc similarity index 75% rename from source/blender/nodes/composite/nodes/node_composite_idMask.c rename to source/blender/nodes/composite/nodes/node_composite_idMask.cc index 84563e7560b..5121370567c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_idMask.c +++ b/source/blender/nodes/composite/nodes/node_composite_idMask.cc @@ -21,25 +21,26 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** ID Mask ******************** */ -static bNodeSocketTemplate cmp_node_idmask_in[] = { - {SOCK_FLOAT, N_("ID value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_idmask_out[] = { - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_idmask_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("ID value")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Alpha")); +} + +} // namespace blender::nodes void register_node_type_cmp_idmask(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_ID_MASK, "ID Mask", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_idmask_in, cmp_node_idmask_out); + ntype.declare = blender::nodes::cmp_node_idmask_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.cc similarity index 82% rename from source/blender/nodes/composite/nodes/node_composite_image.c rename to source/blender/nodes/composite/nodes/node_composite_image.cc index 243300b0a44..79cb0bd0f8c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BLI_linklist.h" #include "BLI_utildefines.h" @@ -45,7 +45,7 @@ static bNodeSocketTemplate cmp_node_rlayers_out[] = { {SOCK_VECTOR, N_(RE_PASSNAME_NORMAL), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_VECTOR, N_(RE_PASSNAME_UV), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_VECTOR, N_(RE_PASSNAME_VECTOR), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + {SOCK_VECTOR, N_(RE_PASSNAME_POSITION), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_RGBA, N_(RE_PASSNAME_DEPRECATED), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_RGBA, N_(RE_PASSNAME_SHADOW), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, @@ -72,7 +72,7 @@ static bNodeSocketTemplate cmp_node_rlayers_out[] = { {SOCK_RGBA, N_(RE_PASSNAME_SUBSURFACE_COLOR), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {-1, ""}, }; -#define MAX_LEGACY_SOCKET_INDEX 30 +#define NUM_LEGACY_SOCKETS (ARRAY_SIZE(cmp_node_rlayers_out) - 1) static void cmp_node_image_add_pass_output(bNodeTree *ntree, bNode *node, @@ -84,16 +84,17 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, LinkNodePair *available_sockets, int *prev_index) { - bNodeSocket *sock = BLI_findstring(&node->outputs, name, offsetof(bNodeSocket, name)); + bNodeSocket *sock = (bNodeSocket *)BLI_findstring( + &node->outputs, name, offsetof(bNodeSocket, name)); /* Replace if types don't match. */ if (sock && sock->type != type) { nodeRemoveSocket(ntree, node, sock); - sock = NULL; + sock = nullptr; } /* Create socket if it doesn't exist yet. */ - if (sock == NULL) { + if (sock == nullptr) { if (rres_index >= 0) { sock = node_add_socket_from_template( ntree, node, &cmp_node_rlayers_out[rres_index], SOCK_OUT); @@ -102,18 +103,19 @@ static void cmp_node_image_add_pass_output(bNodeTree *ntree, sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, name, name); } /* extra socket info */ - NodeImageLayer *sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer"); + NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer), + "node image layer"); sock->storage = sockdata; } - NodeImageLayer *sockdata = sock->storage; + NodeImageLayer *sockdata = (NodeImageLayer *)sock->storage; if (sockdata) { BLI_strncpy(sockdata->pass_name, passname, sizeof(sockdata->pass_name)); } /* Reorder sockets according to order that passes are added. */ const int after_index = (*prev_index)++; - bNodeSocket *after_sock = BLI_findlink(&node->outputs, after_index); + bNodeSocket *after_sock = (bNodeSocket *)BLI_findlink(&node->outputs, after_index); BLI_remlink(&node->outputs, sock); BLI_insertlinkafter(&node->outputs, after_sock, sock); @@ -128,8 +130,8 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, ImBuf *ibuf; int prev_index = -1; if (ima) { - ImageUser *iuser = node->storage; - ImageUser load_iuser = {NULL}; + ImageUser *iuser = (ImageUser *)node->storage; + ImageUser load_iuser = {nullptr}; int offset = BKE_image_sequence_guess_offset(ima); /* It is possible that image user in this node is not @@ -138,21 +140,18 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, * * So we manually construct image user to be sure first * image from sequence (that one which is set as filename - * for image datablock) is used for sockets detection - */ - load_iuser.ok = 1; + * for image data-block) is used for sockets detection. */ load_iuser.framenr = offset; /* make sure ima->type is correct */ - ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, NULL); + ibuf = BKE_image_acquire_ibuf(ima, &load_iuser, nullptr); if (ima->rr) { - RenderLayer *rl = BLI_findlink(&ima->rr->layers, iuser->layer); + RenderLayer *rl = (RenderLayer *)BLI_findlink(&ima->rr->layers, iuser->layer); if (rl) { - RenderPass *rpass; - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int type; + LISTBASE_FOREACH (RenderPass *, rpass, &rl->passes) { + eNodeSocketDatatype type; if (rpass->channels == 1) { type = SOCK_FLOAT; } @@ -182,7 +181,7 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, &prev_index); } } - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, nullptr); return; } } @@ -219,14 +218,14 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, available_sockets, &prev_index); } - BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(ima, ibuf, nullptr); } } -typedef struct RLayerUpdateData { +struct RLayerUpdateData { LinkNodePair *available_sockets; int prev_index; -} RLayerUpdateData; +}; void node_cmp_rlayers_register_pass(bNodeTree *ntree, bNode *node, @@ -235,13 +234,13 @@ void node_cmp_rlayers_register_pass(bNodeTree *ntree, const char *name, eNodeSocketDatatype type) { - RLayerUpdateData *data = node->storage; + RLayerUpdateData *data = (RLayerUpdateData *)node->storage; - if (scene == NULL || view_layer == NULL || data == NULL || node->id != (ID *)scene) { + if (scene == nullptr || view_layer == nullptr || data == nullptr || node->id != (ID *)scene) { return; } - ViewLayer *node_view_layer = BLI_findlink(&scene->view_layers, node->custom1); + ViewLayer *node_view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1); if (node_view_layer != view_layer) { return; } @@ -281,7 +280,7 @@ static void cmp_node_rlayer_create_outputs_cb(void *UNUSED(userdata), * unless we want to register that for every other temp Main we could generate??? */ ntreeCompositRegisterPass(scene->nodetree, scene, view_layer, name, type); - for (Scene *sce = G_MAIN->scenes.first; sce; sce = sce->id.next) { + for (Scene *sce = (Scene *)G_MAIN->scenes.first; sce; sce = (Scene *)sce->id.next) { if (sce->nodetree && sce != scene) { ntreeCompositRegisterPass(sce->nodetree, scene, view_layer, name, type); } @@ -297,16 +296,17 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, if (scene) { RenderEngineType *engine_type = RE_engines_find(scene->r.engine); if (engine_type && engine_type->update_render_passes) { - ViewLayer *view_layer = BLI_findlink(&scene->view_layers, node->custom1); + ViewLayer *view_layer = (ViewLayer *)BLI_findlink(&scene->view_layers, node->custom1); if (view_layer) { - RLayerUpdateData *data = MEM_mallocN(sizeof(RLayerUpdateData), "render layer update data"); + RLayerUpdateData *data = (RLayerUpdateData *)MEM_mallocN(sizeof(RLayerUpdateData), + "render layer update data"); data->available_sockets = available_sockets; data->prev_index = -1; node->storage = data; RenderEngine *engine = RE_engine_create(engine_type); RE_engine_update_render_passes( - engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, NULL); + engine, scene, view_layer, cmp_node_rlayer_create_outputs_cb, nullptr); RE_engine_free(engine); if ((scene->r.mode & R_EDGE_FRS) && @@ -315,7 +315,7 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, } MEM_freeN(data); - node->storage = NULL; + node->storage = nullptr; return; } @@ -348,8 +348,7 @@ static void cmp_node_rlayer_create_outputs(bNodeTree *ntree, static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rlayer) { bNodeSocket *sock, *sock_next; - LinkNodePair available_sockets = {NULL, NULL}; - int sock_index; + LinkNodePair available_sockets = {nullptr, nullptr}; /* XXX make callback */ if (rlayer) { @@ -369,20 +368,20 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl * the first 31 passes to belong to a specific pass type. * So, we keep those 31 always allocated before the others as well, * even if they have no links attached. */ - sock_index = 0; - for (sock = node->outputs.first; sock; sock = sock_next, sock_index++) { + int sock_index = 0; + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock_next, sock_index++) { sock_next = sock->next; if (BLI_linklist_index(available_sockets.list, sock) >= 0) { sock->flag &= ~(SOCK_UNAVAIL | SOCK_HIDDEN); } else { bNodeLink *link; - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (link->fromsock == sock) { break; } } - if (!link && (!rlayer || sock_index > MAX_LEGACY_SOCKET_INDEX)) { + if (!link && (!rlayer || sock_index >= NUM_LEGACY_SOCKETS)) { MEM_freeN(sock->storage); nodeRemoveSocket(ntree, node, sock); } @@ -392,7 +391,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node, bool rl } } - BLI_linklist_free(available_sockets.list, NULL); + BLI_linklist_free(available_sockets.list, nullptr); } static void cmp_node_image_update(bNodeTree *ntree, bNode *node) @@ -407,11 +406,10 @@ static void cmp_node_image_update(bNodeTree *ntree, bNode *node) static void node_composit_init_image(bNodeTree *ntree, bNode *node) { - ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->frames = 1; iuser->sfra = 1; - iuser->ok = 1; iuser->flag |= IMA_ANIM_ALWAYS; /* setup initial outputs */ @@ -420,10 +418,8 @@ static void node_composit_init_image(bNodeTree *ntree, bNode *node) static void node_composit_free_image(bNode *node) { - bNodeSocket *sock; - /* free extra socket info */ - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { MEM_freeN(sock->storage); } @@ -436,9 +432,9 @@ static void node_composit_copy_image(bNodeTree *UNUSED(dest_ntree), { dest_node->storage = MEM_dupallocN(src_node->storage); - const bNodeSocket *src_output_sock = src_node->outputs.first; - bNodeSocket *dest_output_sock = dest_node->outputs.first; - while (dest_output_sock != NULL) { + const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first; + bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first; + while (dest_output_sock != nullptr) { dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage); src_output_sock = src_output_sock->next; @@ -468,56 +464,27 @@ void node_cmp_rlayers_outputs(bNodeTree *ntree, bNode *node) const char *node_cmp_rlayers_sock_to_pass(int sock_index) { - const char *sock_to_passname[] = { - RE_PASSNAME_COMBINED, - RE_PASSNAME_COMBINED, - RE_PASSNAME_Z, - RE_PASSNAME_NORMAL, - RE_PASSNAME_UV, - RE_PASSNAME_VECTOR, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_SHADOW, - RE_PASSNAME_AO, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_DEPRECATED, - RE_PASSNAME_INDEXOB, - RE_PASSNAME_INDEXMA, - RE_PASSNAME_MIST, - RE_PASSNAME_EMIT, - RE_PASSNAME_ENVIRONMENT, - RE_PASSNAME_DIFFUSE_DIRECT, - RE_PASSNAME_DIFFUSE_INDIRECT, - RE_PASSNAME_DIFFUSE_COLOR, - RE_PASSNAME_GLOSSY_DIRECT, - RE_PASSNAME_GLOSSY_INDIRECT, - RE_PASSNAME_GLOSSY_COLOR, - RE_PASSNAME_TRANSM_DIRECT, - RE_PASSNAME_TRANSM_INDIRECT, - RE_PASSNAME_TRANSM_COLOR, - RE_PASSNAME_SUBSURFACE_DIRECT, - RE_PASSNAME_SUBSURFACE_INDIRECT, - RE_PASSNAME_SUBSURFACE_COLOR, - }; - if (sock_index > MAX_LEGACY_SOCKET_INDEX) { - return NULL; + if (sock_index >= NUM_LEGACY_SOCKETS) { + return nullptr; } - return sock_to_passname[sock_index]; + const char *name = cmp_node_rlayers_out[sock_index].name; + /* Exception for alpha, which is derived from Combined. */ + return (STREQ(name, "Alpha")) ? RE_PASSNAME_COMBINED : name; } static void node_composit_init_rlayers(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; int sock_index = 0; node->id = &scene->id; id_us_plus(node->id); - for (bNodeSocket *sock = node->outputs.first; sock; sock = sock->next, sock_index++) { - NodeImageLayer *sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer"); + for (bNodeSocket *sock = (bNodeSocket *)node->outputs.first; sock; + sock = sock->next, sock_index++) { + NodeImageLayer *sockdata = (NodeImageLayer *)MEM_callocN(sizeof(NodeImageLayer), + "node image layer"); sock->storage = sockdata; BLI_strncpy(sockdata->pass_name, @@ -541,13 +508,13 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), * Render layers node can only be used in local scene->nodetree, * since it directly links to the scene. */ - for (scene = G.main->scenes.first; scene; scene = scene->id.next) { + for (scene = (Scene *)G.main->scenes.first; scene; scene = (Scene *)scene->id.next) { if (scene->nodetree == ntree) { break; } } - if (scene == NULL) { + if (scene == nullptr) { *r_disabled_hint = "The node tree must be the compositing node tree of any scene in the file"; return false; } @@ -556,10 +523,8 @@ static bool node_composit_poll_rlayers(bNodeType *UNUSED(ntype), static void node_composit_free_rlayers(bNode *node) { - bNodeSocket *sock; - /* free extra socket info */ - for (sock = node->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { if (sock->storage) { MEM_freeN(sock->storage); } @@ -571,9 +536,9 @@ static void node_composit_copy_rlayers(bNodeTree *UNUSED(dest_ntree), const bNode *src_node) { /* copy extra socket info */ - const bNodeSocket *src_output_sock = src_node->outputs.first; - bNodeSocket *dest_output_sock = dest_node->outputs.first; - while (dest_output_sock != NULL) { + const bNodeSocket *src_output_sock = (bNodeSocket *)src_node->outputs.first; + bNodeSocket *dest_output_sock = (bNodeSocket *)dest_node->outputs.first; + while (dest_output_sock != nullptr) { dest_output_sock->storage = MEM_dupallocN(src_output_sock->storage); src_output_sock = src_output_sock->next; @@ -593,10 +558,10 @@ void register_node_type_cmp_rlayers(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_R_LAYERS, "Render Layers", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, cmp_node_rlayers_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_rlayers_out); ntype.initfunc_api = node_composit_init_rlayers; ntype.poll = node_composit_poll_rlayers; - node_type_storage(&ntype, NULL, node_composit_free_rlayers, node_composit_copy_rlayers); + node_type_storage(&ntype, nullptr, node_composit_free_rlayers, node_composit_copy_rlayers); node_type_update(&ntype, cmp_node_rlayers_update); node_type_init(&ntype, node_cmp_rlayers_outputs); node_type_size_preset(&ntype, NODE_SIZE_LARGE); diff --git a/source/blender/nodes/composite/nodes/node_composite_inpaint.c b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_inpaint.c rename to source/blender/nodes/composite/nodes/node_composite_inpaint.cc index 6c02bca9b39..d0ff97a2ca0 100644 --- a/source/blender/nodes/composite/nodes/node_composite_inpaint.c +++ b/source/blender/nodes/composite/nodes/node_composite_inpaint.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Inpaint/ ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_invert.c b/source/blender/nodes/composite/nodes/node_composite_invert.cc similarity index 74% rename from source/blender/nodes/composite/nodes/node_composite_invert.c rename to source/blender/nodes/composite/nodes/node_composite_invert.cc index d229261f208..dabf0452628 100644 --- a/source/blender/nodes/composite/nodes/node_composite_invert.c +++ b/source/blender/nodes/composite/nodes/node_composite_invert.cc @@ -21,15 +21,20 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** INVERT ******************** */ -static bNodeSocketTemplate cmp_node_invert_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}}; -static bNodeSocketTemplate cmp_node_invert_out[] = {{SOCK_RGBA, N_("Color")}, {-1, ""}}; +namespace blender::nodes { + +static void cmp_node_invert_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Fac")).default_value(1.0f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Color")); +} + +} // namespace blender::nodes static void node_composit_init_invert(bNodeTree *UNUSED(ntree), bNode *node) { @@ -42,7 +47,7 @@ void register_node_type_cmp_invert(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_INVERT, "Invert", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_invert_in, cmp_node_invert_out); + ntype.declare = blender::nodes::cmp_node_invert_declare; node_type_init(&ntype, node_composit_init_invert); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_keying.c b/source/blender/nodes/composite/nodes/node_composite_keying.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_keying.c rename to source/blender/nodes/composite/nodes/node_composite_keying.cc index 73e86a21ebe..d5547161069 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keying.c +++ b/source/blender/nodes/composite/nodes/node_composite_keying.cc @@ -27,7 +27,7 @@ #include "BLI_math_base.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -48,9 +48,7 @@ static bNodeSocketTemplate cmp_node_keying_out[] = { static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node) { - NodeKeyingData *data; - - data = MEM_callocN(sizeof(NodeKeyingData), "node keying data"); + NodeKeyingData *data = (NodeKeyingData *)MEM_callocN(sizeof(NodeKeyingData), "node keying data"); data->screen_balance = 0.5f; data->despill_balance = 0.5f; @@ -59,7 +57,6 @@ static void node_composit_init_keying(bNodeTree *UNUSED(ntree), bNode *node) data->edge_kernel_tolerance = 0.1f; data->clip_black = 0.0f; data->clip_white = 1.0f; - node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc similarity index 84% rename from source/blender/nodes/composite/nodes/node_composite_keyingscreen.c rename to source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc index e5e97cd72e4..c976a92b92d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_keyingscreen.c +++ b/source/blender/nodes/composite/nodes/node_composite_keyingscreen.cc @@ -26,7 +26,7 @@ #include "BLI_math_base.h" #include "BLI_math_color.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -37,10 +37,8 @@ static bNodeSocketTemplate cmp_node_keyingscreen_out[] = { static void node_composit_init_keyingscreen(bNodeTree *UNUSED(ntree), bNode *node) { - NodeKeyingScreenData *data; - - data = MEM_callocN(sizeof(NodeKeyingScreenData), "node keyingscreen data"); - + NodeKeyingScreenData *data = (NodeKeyingScreenData *)MEM_callocN(sizeof(NodeKeyingScreenData), + "node keyingscreen data"); node->storage = data; } @@ -49,7 +47,7 @@ void register_node_type_cmp_keyingscreen(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_KEYINGSCREEN, "Keying Screen", NODE_CLASS_MATTE, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_keyingscreen_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_keyingscreen_out); node_type_init(&ntype, node_composit_init_keyingscreen); node_type_storage( &ntype, "NodeKeyingScreenData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_lensdist.c b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_lensdist.c rename to source/blender/nodes/composite/nodes/node_composite_lensdist.cc index ce8c8c00e24..2a8dc035792 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lensdist.c +++ b/source/blender/nodes/composite/nodes/node_composite_lensdist.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_lensdist_in[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, @@ -36,7 +36,7 @@ static bNodeSocketTemplate cmp_node_lensdist_out[] = { static void node_composit_init_lensdist(bNodeTree *UNUSED(ntree), bNode *node) { - NodeLensDist *nld = MEM_callocN(sizeof(NodeLensDist), "node lensdist data"); + NodeLensDist *nld = (NodeLensDist *)MEM_callocN(sizeof(NodeLensDist), "node lensdist data"); nld->jit = nld->proj = nld->fit = 0; node->storage = nld; } diff --git a/source/blender/nodes/composite/nodes/node_composite_levels.c b/source/blender/nodes/composite/nodes/node_composite_levels.cc similarity index 76% rename from source/blender/nodes/composite/nodes/node_composite_levels.c rename to source/blender/nodes/composite/nodes/node_composite_levels.cc index 7c70ccf412a..54064f24e0d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_levels.c +++ b/source/blender/nodes/composite/nodes/node_composite_levels.cc @@ -21,19 +21,20 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** LEVELS ******************** */ -static bNodeSocketTemplate cmp_node_view_levels_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_view_levels_out[] = { - {SOCK_FLOAT, N_("Mean")}, - {SOCK_FLOAT, N_("Std Dev")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_levels_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_output(N_("Mean")); + b.add_output(N_("Std Dev")); +} + +} // namespace blender::nodes static void node_composit_init_view_levels(bNodeTree *UNUSED(ntree), bNode *node) { @@ -45,7 +46,7 @@ void register_node_type_cmp_view_levels(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VIEW_LEVELS, "Levels", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_view_levels_in, cmp_node_view_levels_out); + ntype.declare = blender::nodes::cmp_node_levels_declare; node_type_init(&ntype, node_composit_init_view_levels); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.c b/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc similarity index 93% rename from source/blender/nodes/composite/nodes/node_composite_lummaMatte.c rename to source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc index cb0f59c2f4a..600406d22b9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_lummaMatte.c +++ b/source/blender/nodes/composite/nodes/node_composite_lummaMatte.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* ******************* Luma Matte Node ********************************* */ static bNodeSocketTemplate cmp_node_luma_matte_in[] = { @@ -37,7 +37,7 @@ static bNodeSocketTemplate cmp_node_luma_matte_out[] = { static void node_composit_init_luma_matte(bNodeTree *UNUSED(ntree), bNode *node) { - NodeChroma *c = MEM_callocN(sizeof(NodeChroma), "node chroma"); + NodeChroma *c = (NodeChroma *)MEM_callocN(sizeof(NodeChroma), "node chroma"); node->storage = c; c->t1 = 1.0f; c->t2 = 0.0f; diff --git a/source/blender/nodes/composite/nodes/node_composite_mapRange.c b/source/blender/nodes/composite/nodes/node_composite_mapRange.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_mapRange.c rename to source/blender/nodes/composite/nodes/node_composite_mapRange.cc index cd95e73ba5c..808ad538e55 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mapRange.c +++ b/source/blender/nodes/composite/nodes/node_composite_mapRange.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** MAP VALUE ******************** */ static bNodeSocketTemplate cmp_node_map_range_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_mapUV.c b/source/blender/nodes/composite/nodes/node_composite_mapUV.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_mapUV.c rename to source/blender/nodes/composite/nodes/node_composite_mapUV.cc index e88a303e449..99032bd042e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mapUV.c +++ b/source/blender/nodes/composite/nodes/node_composite_mapUV.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Map UV ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_mapValue.c b/source/blender/nodes/composite/nodes/node_composite_mapValue.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_mapValue.c rename to source/blender/nodes/composite/nodes/node_composite_mapValue.cc index c93807c3801..25c00c2ba13 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mapValue.c +++ b/source/blender/nodes/composite/nodes/node_composite_mapValue.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** MAP VALUE ******************** */ static bNodeSocketTemplate cmp_node_map_value_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_mask.c b/source/blender/nodes/composite/nodes/node_composite_mask.cc similarity index 80% rename from source/blender/nodes/composite/nodes/node_composite_mask.c rename to source/blender/nodes/composite/nodes/node_composite_mask.cc index e6a5df6c24b..6428fadaa5f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mask.c +++ b/source/blender/nodes/composite/nodes/node_composite_mask.cc @@ -23,15 +23,22 @@ #include "DNA_mask_types.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" -/* **************** Translate ******************** */ +/* **************** Mask ******************** */ -static bNodeSocketTemplate cmp_node_mask_out[] = {{SOCK_FLOAT, "Mask"}, {-1, ""}}; +namespace blender::nodes { + +static void cmp_node_mask_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Mask")); +} + +} // namespace blender::nodes static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node) { - NodeMask *data = MEM_callocN(sizeof(NodeMask), "NodeMask"); + NodeMask *data = (NodeMask *)MEM_callocN(sizeof(NodeMask), "NodeMask"); data->size_x = data->size_y = 256; node->storage = data; @@ -41,7 +48,7 @@ static void node_composit_init_mask(bNodeTree *UNUSED(ntree), bNode *node) static void node_mask_label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen) { - if (node->id != NULL) { + if (node->id != nullptr) { BLI_strncpy(label, node->id->name + 2, maxlen); } else { @@ -54,7 +61,7 @@ void register_node_type_cmp_mask(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MASK, "Mask", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_mask_out); + ntype.declare = blender::nodes::cmp_node_mask_declare; node_type_init(&ntype, node_composit_init_mask); node_type_label(&ntype, node_mask_label); diff --git a/source/blender/nodes/composite/nodes/node_composite_math.c b/source/blender/nodes/composite/nodes/node_composite_math.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_math.c rename to source/blender/nodes/composite/nodes/node_composite_math.cc index 2191c6bcdc3..ecddcc2ad32 100644 --- a/source/blender/nodes/composite/nodes/node_composite_math.c +++ b/source/blender/nodes/composite/nodes/node_composite_math.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SCALAR MATH ******************** */ static bNodeSocketTemplate cmp_node_math_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_mixrgb.c b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_mixrgb.c rename to source/blender/nodes/composite/nodes/node_composite_mixrgb.cc index 9d3751c7da3..557116f5b7a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_mixrgb.c +++ b/source/blender/nodes/composite/nodes/node_composite_mixrgb.cc @@ -21,9 +21,10 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** MIX RGB ******************** */ + static bNodeSocketTemplate cmp_node_mix_rgb_in[] = { {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, diff --git a/source/blender/nodes/composite/nodes/node_composite_movieclip.c b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc similarity index 68% rename from source/blender/nodes/composite/nodes/node_composite_movieclip.c rename to source/blender/nodes/composite/nodes/node_composite_movieclip.cc index 4f5aef05425..5d63a1b8002 100644 --- a/source/blender/nodes/composite/nodes/node_composite_movieclip.c +++ b/source/blender/nodes/composite/nodes/node_composite_movieclip.cc @@ -21,26 +21,31 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_context.h" #include "BKE_lib_id.h" -static bNodeSocketTemplate cmp_node_movieclip_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Alpha")}, - {SOCK_FLOAT, N_("Offset X")}, - {SOCK_FLOAT, N_("Offset Y")}, - {SOCK_FLOAT, N_("Scale")}, - {SOCK_FLOAT, N_("Angle")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_movieclip_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Image")); + b.add_output(N_("Alpha")); + b.add_output(N_("Offset X")); + b.add_output(N_("Offset Y")); + b.add_output(N_("Scale")); + b.add_output(N_("Angle")); +} + +} // namespace blender::nodes static void init(const bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; Scene *scene = CTX_data_scene(C); - MovieClipUser *user = MEM_callocN(sizeof(MovieClipUser), "node movie clip user"); + MovieClipUser *user = (MovieClipUser *)MEM_callocN(sizeof(MovieClipUser), + "node movie clip user"); node->id = (ID *)scene->clip; id_us_plus(node->id); @@ -53,7 +58,7 @@ void register_node_type_cmp_movieclip(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_MOVIECLIP, "Movie Clip", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, cmp_node_movieclip_out); + ntype.declare = blender::nodes::cmp_node_movieclip_declare; ntype.initfunc_api = init; node_type_storage( &ntype, "MovieClipUser", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc similarity index 87% rename from source/blender/nodes/composite/nodes/node_composite_moviedistortion.c rename to source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc index 7e30d004b45..2bac30cc152 100644 --- a/source/blender/nodes/composite/nodes/node_composite_moviedistortion.c +++ b/source/blender/nodes/composite/nodes/node_composite_moviedistortion.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_context.h" #include "BKE_lib_id.h" @@ -50,7 +50,7 @@ static void label(bNodeTree *UNUSED(ntree), bNode *node, char *label, int maxlen static void init(const bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; Scene *scene = CTX_data_scene(C); node->id = (ID *)scene->clip; @@ -60,16 +60,16 @@ static void init(const bContext *C, PointerRNA *ptr) static void storage_free(bNode *node) { if (node->storage) { - BKE_tracking_distortion_free(node->storage); + BKE_tracking_distortion_free((MovieDistortion *)node->storage); } - node->storage = NULL; + node->storage = nullptr; } static void storage_copy(bNodeTree *UNUSED(dest_ntree), bNode *dest_node, const bNode *src_node) { if (src_node->storage) { - dest_node->storage = BKE_tracking_distortion_copy(src_node->storage); + dest_node->storage = BKE_tracking_distortion_copy((MovieDistortion *)src_node->storage); } } @@ -82,7 +82,7 @@ void register_node_type_cmp_moviedistortion(void) node_type_label(&ntype, label); ntype.initfunc_api = init; - node_type_storage(&ntype, NULL, storage_free, storage_copy); + node_type_storage(&ntype, nullptr, storage_free, storage_copy); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_normal.c b/source/blender/nodes/composite/nodes/node_composite_normal.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_normal.c rename to source/blender/nodes/composite/nodes/node_composite_normal.cc index 91300e66339..7531025daa5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normal.c +++ b/source/blender/nodes/composite/nodes/node_composite_normal.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** NORMAL ******************** */ static bNodeSocketTemplate cmp_node_normal_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_normalize.c b/source/blender/nodes/composite/nodes/node_composite_normalize.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_normalize.c rename to source/blender/nodes/composite/nodes/node_composite_normalize.cc index 26f2abc745f..7cc54e4eed6 100644 --- a/source/blender/nodes/composite/nodes/node_composite_normalize.c +++ b/source/blender/nodes/composite/nodes/node_composite_normalize.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** NORMALIZE single channel, useful for Z buffer ******************** */ static bNodeSocketTemplate cmp_node_normalize_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.cc similarity index 76% rename from source/blender/nodes/composite/nodes/node_composite_outputFile.c rename to source/blender/nodes/composite/nodes/node_composite_outputFile.cc index c10edd8d5ad..a372d2f7419 100644 --- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c +++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.cc @@ -23,13 +23,13 @@ #include "BLI_string_utils.h" #include "BLI_utildefines.h" -#include +#include #include "BKE_context.h" #include "RNA_access.h" -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "intern/openexr/openexr_multi.h" @@ -38,14 +38,15 @@ /* find unique path */ static bool unique_path_unique_check(void *arg, const char *name) { - struct { + struct Args { ListBase *lb; bNodeSocket *sock; - } *data = arg; - bNodeSocket *sock; - for (sock = data->lb->first; sock; sock = sock->next) { + }; + Args *data = (Args *)arg; + + LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) { if (sock != data->sock) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; if (STREQ(sockdata->path, name)) { return true; } @@ -67,11 +68,11 @@ void ntreeCompositOutputFileUniquePath(ListBase *list, data.sock = sock; /* See if we are given an empty string */ - if (ELEM(NULL, sock, defname)) { + if (ELEM(nullptr, sock, defname)) { return; } - sockdata = sock->storage; + sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_uniquename_cb( unique_path_unique_check, &data, defname, delim, sockdata->path, sizeof(sockdata->path)); } @@ -79,14 +80,15 @@ void ntreeCompositOutputFileUniquePath(ListBase *list, /* find unique EXR layer */ static bool unique_layer_unique_check(void *arg, const char *name) { - struct { + struct Args { ListBase *lb; bNodeSocket *sock; - } *data = arg; - bNodeSocket *sock; - for (sock = data->lb->first; sock; sock = sock->next) { + }; + Args *data = (Args *)arg; + + LISTBASE_FOREACH (bNodeSocket *, sock, data->lb) { if (sock != data->sock) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; if (STREQ(sockdata->layer, name)) { return true; } @@ -99,7 +101,6 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list, const char defname[], char delim) { - NodeImageMultiFileSocket *sockdata; struct { ListBase *lb; bNodeSocket *sock; @@ -108,11 +109,11 @@ void ntreeCompositOutputFileUniqueLayer(ListBase *list, data.sock = sock; /* See if we are given an empty string */ - if (ELEM(NULL, sock, defname)) { + if (ELEM(nullptr, sock, defname)) { return; } - sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_uniquename_cb( unique_layer_unique_check, &data, defname, delim, sockdata->layer, sizeof(sockdata->layer)); } @@ -122,12 +123,13 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, const char *name, ImageFormatData *im_format) { - NodeImageMultiFile *nimf = node->storage; - bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name); + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + bNodeSocket *sock = nodeAddStaticSocket( + ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name); /* create format data for the input socket */ - NodeImageMultiFileSocket *sockdata = MEM_callocN(sizeof(NodeImageMultiFileSocket), - "socket image format"); + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)MEM_callocN( + sizeof(NodeImageMultiFileSocket), "socket image format"); sock->storage = sockdata; BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path)); @@ -155,8 +157,8 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node) { - NodeImageMultiFile *nimf = node->storage; - bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input); + NodeImageMultiFile *nimf = (NodeImageMultiFile *)node->storage; + bNodeSocket *sock = (bNodeSocket *)BLI_findlink(&node->inputs, nimf->active_input); int totinputs = BLI_listbase_count(&node->inputs); if (!sock) { @@ -176,14 +178,14 @@ int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node) void ntreeCompositOutputFileSetPath(bNode *node, bNodeSocket *sock, const char *name) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path)); ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_'); } void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char *name) { - NodeImageMultiFileSocket *sockdata = sock->storage; + NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)sock->storage; BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer)); ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_'); } @@ -193,9 +195,10 @@ static void init_output_file(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = ptr->data; - NodeImageMultiFile *nimf = MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file"); - ImageFormatData *format = NULL; + bNode *node = (bNode *)ptr->data; + NodeImageMultiFile *nimf = (NodeImageMultiFile *)MEM_callocN(sizeof(NodeImageMultiFile), + "node image multi file"); + ImageFormatData *format = nullptr; node->storage = nimf; if (scene) { @@ -219,10 +222,8 @@ static void init_output_file(const bContext *C, PointerRNA *ptr) static void free_output_file(bNode *node) { - bNodeSocket *sock; - /* free storage data in sockets */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { MEM_freeN(sock->storage); } @@ -238,37 +239,35 @@ static void copy_output_file(bNodeTree *UNUSED(dest_ntree), dest_node->storage = MEM_dupallocN(src_node->storage); /* duplicate storage data in sockets */ - for (src_sock = src_node->inputs.first, dest_sock = dest_node->inputs.first; + for (src_sock = (bNodeSocket *)src_node->inputs.first, + dest_sock = (bNodeSocket *)dest_node->inputs.first; src_sock && dest_sock; - src_sock = src_sock->next, dest_sock = dest_sock->next) { + src_sock = src_sock->next, dest_sock = (bNodeSocket *)dest_sock->next) { dest_sock->storage = MEM_dupallocN(src_sock->storage); } } static void update_output_file(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock, *sock_next; PointerRNA ptr; /* XXX fix for T36706: remove invalid sockets added with bpy API. * This is not ideal, but prevents crashes from missing storage. * FileOutput node needs a redesign to support this properly. */ - for (sock = node->inputs.first; sock; sock = sock_next) { - sock_next = sock->next; - if (sock->storage == NULL) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (sock->storage == nullptr) { nodeRemoveSocket(ntree, node, sock); } } - for (sock = node->outputs.first; sock; sock = sock_next) { - sock_next = sock->next; + LISTBASE_FOREACH (bNodeSocket *, sock, &node->outputs) { nodeRemoveSocket(ntree, node, sock); } cmp_node_update_default(ntree, node); /* automatically update the socket type based on linked input */ - for (sock = node->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { if (sock->link) { RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr); RNA_enum_set(&ptr, "type", sock->link->fromsock->type); @@ -281,7 +280,7 @@ void register_node_type_cmp_output_file(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, NULL, NULL); + node_type_socket_templates(&ntype, nullptr, nullptr); ntype.initfunc_api = init_output_file; node_type_storage(&ntype, "NodeImageMultiFile", free_output_file, copy_output_file); node_type_update(&ntype, update_output_file); diff --git a/source/blender/nodes/composite/nodes/node_composite_pixelate.c b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_pixelate.c rename to source/blender/nodes/composite/nodes/node_composite_pixelate.cc index 6e8a28df76f..19975c21a0b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_pixelate.c +++ b/source/blender/nodes/composite/nodes/node_composite_pixelate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Pixelate ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c rename to source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc index ab5db41e5b5..e122b710b7b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.c +++ b/source/blender/nodes/composite/nodes/node_composite_planetrackdeform.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate cmp_node_planetrackdeform_in[] = { {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, {-1, ""}}; @@ -34,8 +34,8 @@ static bNodeSocketTemplate cmp_node_planetrackdeform_out[] = { static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodePlaneTrackDeformData *data = MEM_callocN(sizeof(NodePlaneTrackDeformData), - "node plane track deform data"); + NodePlaneTrackDeformData *data = (NodePlaneTrackDeformData *)MEM_callocN( + sizeof(NodePlaneTrackDeformData), "node plane track deform data"); data->motion_blur_samples = 16; data->motion_blur_shutter = 0.5f; node->storage = data; diff --git a/source/blender/nodes/composite/nodes/node_composite_posterize.c b/source/blender/nodes/composite/nodes/node_composite_posterize.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_posterize.c rename to source/blender/nodes/composite/nodes/node_composite_posterize.cc index 5093e581cdc..45a98e68b4b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_posterize.c +++ b/source/blender/nodes/composite/nodes/node_composite_posterize.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Posterize ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_premulkey.c b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc similarity index 76% rename from source/blender/nodes/composite/nodes/node_composite_premulkey.c rename to source/blender/nodes/composite/nodes/node_composite_premulkey.cc index be76bbf01cf..49068429a8d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_premulkey.c +++ b/source/blender/nodes/composite/nodes/node_composite_premulkey.cc @@ -21,25 +21,26 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Premul and Key Alpha Convert ******************** */ -static bNodeSocketTemplate cmp_node_premulkey_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_premulkey_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_premulkey_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes void register_node_type_cmp_premulkey(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_PREMULKEY, "Alpha Convert", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_premulkey_in, cmp_node_premulkey_out); + ntype.declare = blender::nodes::cmp_node_premulkey_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.c b/source/blender/nodes/composite/nodes/node_composite_rgb.cc similarity index 79% rename from source/blender/nodes/composite/nodes/node_composite_rgb.c rename to source/blender/nodes/composite/nodes/node_composite_rgb.cc index dae63f7a702..abe69d6a756 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.c +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -21,20 +21,25 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** RGB ******************** */ -static bNodeSocketTemplate cmp_node_rgb_out[] = { - {SOCK_RGBA, N_("RGBA"), 0.5f, 0.5f, 0.5f, 1.0f}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("RGBA")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); +} + +} // namespace blender::nodes void register_node_type_cmp_rgb(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_RGB, "RGB", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_rgb_out); + ntype.declare = blender::nodes::cmp_node_rgb_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_rotate.c b/source/blender/nodes/composite/nodes/node_composite_rotate.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_rotate.c rename to source/blender/nodes/composite/nodes/node_composite_rotate.cc index 7dd39d5eaa1..d28b35ec9fb 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rotate.c +++ b/source/blender/nodes/composite/nodes/node_composite_rotate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Rotate ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_scale.c b/source/blender/nodes/composite/nodes/node_composite_scale.cc similarity index 94% rename from source/blender/nodes/composite/nodes/node_composite_scale.c rename to source/blender/nodes/composite/nodes/node_composite_scale.cc index 963832de03a..3972fc0d949 100644 --- a/source/blender/nodes/composite/nodes/node_composite_scale.c +++ b/source/blender/nodes/composite/nodes/node_composite_scale.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Scale ******************** */ @@ -38,7 +38,7 @@ static void node_composite_update_scale(bNodeTree *UNUSED(ntree), bNode *node) bool use_xy_scale = ELEM(node->custom1, CMP_SCALE_RELATIVE, CMP_SCALE_ABSOLUTE); /* Only show X/Y scale factor inputs for modes using them! */ - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (STR_ELEM(sock->name, "X", "Y")) { if (use_xy_scale) { sock->flag &= ~SOCK_UNAVAIL; diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc similarity index 58% rename from source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc index 001b197e23a..83c54069658 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombHSVA.cc @@ -21,50 +21,53 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE HSVA ******************** */ -static bNodeSocketTemplate cmp_node_sephsva_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_sephsva_out[] = { - {SOCK_FLOAT, N_("H")}, - {SOCK_FLOAT, N_("S")}, - {SOCK_FLOAT, N_("V")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_sephsva_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("H")); + b.add_output(N_("S")); + b.add_output(N_("V")); + b.add_output(N_("A")); +} + +} // namespace blender::nodes void register_node_type_cmp_sephsva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPHSVA, "Separate HSVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sephsva_in, cmp_node_sephsva_out); - + ntype.declare = blender::nodes::cmp_node_sephsva_declare; nodeRegisterType(&ntype); } /* **************** COMBINE HSVA ******************** */ -static bNodeSocketTemplate cmp_node_combhsva_in[] = { - {SOCK_FLOAT, N_("H"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("S"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combhsva_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combhsva_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("H")).min(0.0f).max(1.0f); + b.add_input(N_("S")).min(0.0f).max(1.0f); + b.add_input(N_("V")).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes void register_node_type_cmp_combhsva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBHSVA, "Combine HSVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combhsva_in, cmp_node_combhsva_out); + ntype.declare = blender::nodes::cmp_node_combhsva_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc similarity index 58% rename from source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc index e08f27db254..049e798af0a 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombRGBA.cc @@ -21,50 +21,53 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE RGBA ******************** */ -static bNodeSocketTemplate cmp_node_seprgba_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_seprgba_out[] = { - {SOCK_FLOAT, N_("R")}, - {SOCK_FLOAT, N_("G")}, - {SOCK_FLOAT, N_("B")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_seprgba_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("R")); + b.add_output(N_("G")); + b.add_output(N_("B")); + b.add_output(N_("A")); +} + +} // namespace blender::nodes void register_node_type_cmp_seprgba(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPRGBA, "Separate RGBA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_seprgba_in, cmp_node_seprgba_out); + ntype.declare = blender::nodes::cmp_node_seprgba_declare; nodeRegisterType(&ntype); } /* **************** COMBINE RGBA ******************** */ -static bNodeSocketTemplate cmp_node_combrgba_in[] = { - {SOCK_FLOAT, N_("R"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("G"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combrgba_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combrgba_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("R")).min(0.0f).max(1.0f); + b.add_input(N_("G")).min(0.0f).max(1.0f); + b.add_input(N_("B")).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes void register_node_type_cmp_combrgba(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBRGBA, "Combine RGBA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combrgba_in, cmp_node_combrgba_out); + ntype.declare = blender::nodes::cmp_node_combrgba_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc similarity index 64% rename from source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc index b3884296600..eaf6ba5e9b2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYCCA.cc @@ -21,18 +21,22 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE YCCA ******************** */ -static bNodeSocketTemplate cmp_node_sepycca_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_sepycca_out[] = { - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("Cb")}, - {SOCK_FLOAT, N_("Cr")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_sepycca_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Y")); + b.add_output(N_("Cb")); + b.add_output(N_("Cr")); + b.add_output(N_("A")); +} + +} // namespace blender::nodes static void node_composit_init_mode_sepycca(bNodeTree *UNUSED(ntree), bNode *node) { @@ -44,24 +48,26 @@ void register_node_type_cmp_sepycca(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPYCCA, "Separate YCbCrA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sepycca_in, cmp_node_sepycca_out); + ntype.declare = blender::nodes::cmp_node_sepycca_declare; node_type_init(&ntype, node_composit_init_mode_sepycca); nodeRegisterType(&ntype); } /* **************** COMBINE YCCA ******************** */ -static bNodeSocketTemplate cmp_node_combycca_in[] = { - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Cb"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Cr"), 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combycca_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combycca_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Y")).min(0.0f).max(1.0f); + b.add_input(N_("Cb")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input(N_("Cr")).default_value(0.5f).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_mode_combycca(bNodeTree *UNUSED(ntree), bNode *node) { @@ -73,7 +79,7 @@ void register_node_type_cmp_combycca(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBYCCA, "Combine YCbCrA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combycca_in, cmp_node_combycca_out); + ntype.declare = blender::nodes::cmp_node_combycca_declare; node_type_init(&ntype, node_composit_init_mode_combycca); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc similarity index 58% rename from source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c rename to source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc index 4da79ec7981..bc7710122d1 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.c +++ b/source/blender/nodes/composite/nodes/node_composite_sepcombYUVA.cc @@ -21,48 +21,54 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SEPARATE YUVA ******************** */ -static bNodeSocketTemplate cmp_node_sepyuva_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate cmp_node_sepyuva_out[] = { - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("U")}, - {SOCK_FLOAT, N_("V")}, - {SOCK_FLOAT, N_("A")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_sepyuva_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Y")); + b.add_output(N_("U")); + b.add_output(N_("V")); + b.add_output(N_("A")); +} + +} // namespace blender::nodes void register_node_type_cmp_sepyuva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SEPYUVA, "Separate YUVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_sepyuva_in, cmp_node_sepyuva_out); + ntype.declare = blender::nodes::cmp_node_sepyuva_declare; nodeRegisterType(&ntype); } /* **************** COMBINE YUVA ******************** */ -static bNodeSocketTemplate cmp_node_combyuva_in[] = { - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("U"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("V"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("A"), 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_combyuva_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_combyuva_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Y")).min(0.0f).max(1.0f); + b.add_input(N_("U")).min(0.0f).max(1.0f); + b.add_input(N_("V")).min(0.0f).max(1.0f); + b.add_input(N_("A")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes void register_node_type_cmp_combyuva(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_COMBYUVA, "Combine YUVA", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_combyuva_in, cmp_node_combyuva_out); + ntype.declare = blender::nodes::cmp_node_combyuva_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_setalpha.c b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc similarity index 73% rename from source/blender/nodes/composite/nodes/node_composite_setalpha.c rename to source/blender/nodes/composite/nodes/node_composite_setalpha.cc index 1b44cc011e9..f59ba76f0c5 100644 --- a/source/blender/nodes/composite/nodes/node_composite_setalpha.c +++ b/source/blender/nodes/composite/nodes/node_composite_setalpha.cc @@ -21,22 +21,24 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** SET ALPHA ******************** */ -static bNodeSocketTemplate cmp_node_setalpha_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_setalpha_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_setalpha_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_input(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_setalpha(bNodeTree *UNUSED(ntree), bNode *node) { - NodeSetAlpha *settings = MEM_callocN(sizeof(NodeSetAlpha), __func__); + NodeSetAlpha *settings = (NodeSetAlpha *)MEM_callocN(sizeof(NodeSetAlpha), __func__); node->storage = settings; settings->mode = CMP_NODE_SETALPHA_MODE_APPLY; } @@ -46,7 +48,7 @@ void register_node_type_cmp_setalpha(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SETALPHA, "Set Alpha", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_setalpha_in, cmp_node_setalpha_out); + ntype.declare = blender::nodes::cmp_node_setalpha_declare; node_type_init(&ntype, node_composit_init_setalpha); node_type_storage( &ntype, "NodeSetAlpha", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc similarity index 88% rename from source/blender/nodes/composite/nodes/node_composite_splitViewer.c rename to source/blender/nodes/composite/nodes/node_composite_splitViewer.cc index 8afb3fd4841..68c5ecdf48e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_splitViewer.c +++ b/source/blender/nodes/composite/nodes/node_composite_splitViewer.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_global.h" #include "BKE_image.h" @@ -35,10 +35,9 @@ static bNodeSocketTemplate cmp_node_splitviewer_in[] = { static void node_composit_init_splitviewer(bNodeTree *UNUSED(ntree), bNode *node) { - ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; node->custom1 = 50; /* default 50% split */ node->id = (ID *)BKE_image_ensure_viewer(G.main, IMA_TYPE_COMPOSITE, "Viewer Node"); @@ -50,12 +49,12 @@ void register_node_type_cmp_splitviewer(void) cmp_node_type_base( &ntype, CMP_NODE_SPLITVIEWER, "Split Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_splitviewer_in, NULL); + node_type_socket_templates(&ntype, cmp_node_splitviewer_in, nullptr); node_type_init(&ntype, node_composit_init_splitviewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); /* Do not allow muting for this node. */ - node_type_internal_links(&ntype, NULL); + node_type_internal_links(&ntype, nullptr); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc similarity index 96% rename from source/blender/nodes/composite/nodes/node_composite_stabilize2d.c rename to source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc index b89f245c542..e5ce2e8ceb9 100644 --- a/source/blender/nodes/composite/nodes/node_composite_stabilize2d.c +++ b/source/blender/nodes/composite/nodes/node_composite_stabilize2d.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_context.h" #include "BKE_lib_id.h" @@ -40,7 +40,7 @@ static bNodeSocketTemplate cmp_node_stabilize2d_out[] = { static void init(const bContext *C, PointerRNA *ptr) { - bNode *node = ptr->data; + bNode *node = (bNode *)ptr->data; Scene *scene = CTX_data_scene(C); node->id = (ID *)scene->clip; diff --git a/source/blender/nodes/composite/nodes/node_composite_sunbeams.c b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc similarity index 92% rename from source/blender/nodes/composite/nodes/node_composite_sunbeams.c rename to source/blender/nodes/composite/nodes/node_composite_sunbeams.cc index 84ab2d30d34..73907d2e27f 100644 --- a/source/blender/nodes/composite/nodes/node_composite_sunbeams.c +++ b/source/blender/nodes/composite/nodes/node_composite_sunbeams.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" static bNodeSocketTemplate inputs[] = { {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, @@ -34,11 +34,10 @@ static bNodeSocketTemplate outputs[] = { static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeSunBeams *data = MEM_callocN(sizeof(NodeSunBeams), "sun beams node"); + NodeSunBeams *data = (NodeSunBeams *)MEM_callocN(sizeof(NodeSunBeams), "sun beams node"); data->source[0] = 0.5f; data->source[1] = 0.5f; - node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_switch.c b/source/blender/nodes/composite/nodes/node_composite_switch.cc similarity index 97% rename from source/blender/nodes/composite/nodes/node_composite_switch.c rename to source/blender/nodes/composite/nodes/node_composite_switch.cc index efbb3390e06..71226a6da0b 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switch.c +++ b/source/blender/nodes/composite/nodes/node_composite_switch.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** MIX RGB ******************** */ static bNodeSocketTemplate cmp_node_switch_in[] = { diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.cc similarity index 78% rename from source/blender/nodes/composite/nodes/node_composite_switchview.c rename to source/blender/nodes/composite/nodes/node_composite_switchview.cc index b09d5119bc4..a61712f7f8d 100644 --- a/source/blender/nodes/composite/nodes/node_composite_switchview.c +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.cc @@ -25,7 +25,7 @@ #include "BKE_context.h" #include "BKE_lib_id.h" -#include "../node_composite_util.h" +#include "../node_composite_util.hh" /* **************** SWITCH VIEW ******************** */ static bNodeSocketTemplate cmp_node_switch_view_out[] = { @@ -37,26 +37,23 @@ static bNodeSocket *ntreeCompositSwitchViewAddSocket(bNodeTree *ntree, bNode *node, const char *name) { - bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name); + bNodeSocket *sock = nodeAddStaticSocket( + ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, nullptr, name); return sock; } static void cmp_node_switch_view_sanitycheck(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock; - if (!BLI_listbase_is_empty(&node->inputs)) { return; } - sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View"); + bNodeSocket *sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View"); sock->flag |= SOCK_HIDDEN; } static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *sock; - SceneRenderView *srv; Scene *scene = (Scene *)node->id; /* only update when called from the operator button */ @@ -64,7 +61,7 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) return; } - if (scene == NULL) { + if (scene == nullptr) { nodeRemoveAllSockets(ntree, node); /* make sure there is always one socket */ cmp_node_switch_view_sanitycheck(ntree, node); @@ -72,11 +69,12 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) } /* remove the views that were removed */ - sock = node->inputs.last; + bNodeSocket *sock = (bNodeSocket *)node->inputs.last; while (sock) { - srv = BLI_findstring(&scene->r.views, sock->name, offsetof(SceneRenderView, name)); + SceneRenderView *srv = (SceneRenderView *)BLI_findstring( + &scene->r.views, sock->name, offsetof(SceneRenderView, name)); - if (srv == NULL) { + if (srv == nullptr) { bNodeSocket *sock_del = sock; sock = sock->prev; nodeRemoveSocket(ntree, node, sock_del); @@ -94,10 +92,10 @@ static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) } /* add the new views */ - for (srv = scene->r.views.first; srv; srv = srv->next) { - sock = BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name)); + LISTBASE_FOREACH (SceneRenderView *, srv, &scene->r.views) { + sock = (bNodeSocket *)BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name)); - if (sock == NULL) { + if (sock == nullptr) { sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); } @@ -117,10 +115,7 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr) { Scene *scene = CTX_data_scene(C); bNodeTree *ntree = (bNodeTree *)ptr->owner_id; - bNode *node = ptr->data; - SceneRenderView *srv; - bNodeSocket *sock; - int nr; + bNode *node = (bNode *)ptr->data; /* store scene for updates */ node->id = (ID *)scene; @@ -129,8 +124,8 @@ static void init_switch_view(const bContext *C, PointerRNA *ptr) if (scene) { RenderData *rd = &scene->r; - for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) { - sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); + LISTBASE_FOREACH (SceneRenderView *, srv, &rd->views) { + bNodeSocket *sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); if (srv->viewflag & SCE_VIEW_DISABLE) { sock->flag |= SOCK_HIDDEN; @@ -147,7 +142,7 @@ void register_node_type_cmp_switch_view(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out); + node_type_socket_templates(&ntype, nullptr, cmp_node_switch_view_out); ntype.initfunc_api = init_switch_view; diff --git a/source/blender/nodes/composite/nodes/node_composite_texture.c b/source/blender/nodes/composite/nodes/node_composite_texture.cc similarity index 66% rename from source/blender/nodes/composite/nodes/node_composite_texture.c rename to source/blender/nodes/composite/nodes/node_composite_texture.cc index 50be05fe5a6..55ae6a4185e 100644 --- a/source/blender/nodes/composite/nodes/node_composite_texture.c +++ b/source/blender/nodes/composite/nodes/node_composite_texture.cc @@ -21,26 +21,32 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** TEXTURE ******************** */ -static bNodeSocketTemplate cmp_node_texture_in[] = { - {SOCK_VECTOR, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -2.0f, 2.0f, PROP_TRANSLATION}, - {SOCK_VECTOR, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -10.0f, 10.0f, PROP_XYZ}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_texture_out[] = { - {SOCK_FLOAT, N_("Value")}, - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_texture_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Offset")).min(-2.0f).max(2.0f).subtype(PROP_TRANSLATION); + b.add_input(N_("Scale")) + .default_value({1.0f, 1.0f, 1.0f}) + .min(-10.0f) + .max(10.0f) + .subtype(PROP_XYZ); + b.add_output(N_("Value")); + b.add_output(N_("Color")); +} + +} // namespace blender::nodes void register_node_type_cmp_texture(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TEXTURE, "Texture", NODE_CLASS_INPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_texture_in, cmp_node_texture_out); + ntype.declare = blender::nodes::cmp_node_texture_declare; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_tonemap.c b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc similarity index 78% rename from source/blender/nodes/composite/nodes/node_composite_tonemap.c rename to source/blender/nodes/composite/nodes/node_composite_tonemap.cc index 5fc86c997f5..33d6f98201c 100644 --- a/source/blender/nodes/composite/nodes/node_composite_tonemap.c +++ b/source/blender/nodes/composite/nodes/node_composite_tonemap.cc @@ -21,20 +21,21 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_tonemap_in[] = { - {SOCK_RGBA, N_("Image"), 1.0f, 1.0f, 1.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_tonemap_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_tonemap_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Image")); +} + +} // namespace blender::nodes static void node_composit_init_tonemap(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTonemap *ntm = MEM_callocN(sizeof(NodeTonemap), "node tonemap data"); + NodeTonemap *ntm = (NodeTonemap *)MEM_callocN(sizeof(NodeTonemap), "node tonemap data"); ntm->type = 1; ntm->key = 0.18; ntm->offset = 1; @@ -53,7 +54,7 @@ void register_node_type_cmp_tonemap(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TONEMAP, "Tonemap", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, cmp_node_tonemap_in, cmp_node_tonemap_out); + ntype.declare = blender::nodes::cmp_node_tonemap_declare; node_type_init(&ntype, node_composit_init_tonemap); node_type_storage(&ntype, "NodeTonemap", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_trackpos.c b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc similarity index 70% rename from source/blender/nodes/composite/nodes/node_composite_trackpos.c rename to source/blender/nodes/composite/nodes/node_composite_trackpos.cc index d59ce9b8b7a..537f7e661db 100644 --- a/source/blender/nodes/composite/nodes/node_composite_trackpos.c +++ b/source/blender/nodes/composite/nodes/node_composite_trackpos.cc @@ -21,18 +21,23 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" -static bNodeSocketTemplate cmp_node_trackpos_out[] = { - {SOCK_FLOAT, N_("X")}, - {SOCK_FLOAT, N_("Y")}, - {SOCK_VECTOR, N_("Speed"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_VELOCITY}, - {-1, ""}, -}; +namespace blender::nodes { + +static void cmp_node_trackpos_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("X")); + b.add_output(N_("Y")); + b.add_output(N_("Speed")).subtype(PROP_VELOCITY); +} + +} // namespace blender::nodes static void init(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTrackPosData *data = MEM_callocN(sizeof(NodeTrackPosData), "node track position data"); + NodeTrackPosData *data = (NodeTrackPosData *)MEM_callocN(sizeof(NodeTrackPosData), + "node track position data"); node->storage = data; } @@ -42,7 +47,7 @@ void register_node_type_cmp_trackpos(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_TRACKPOS, "Track Position", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_trackpos_out); + ntype.declare = blender::nodes::cmp_node_trackpos_declare; node_type_init(&ntype, init); node_type_storage( &ntype, "NodeTrackPosData", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/composite/nodes/node_composite_transform.c b/source/blender/nodes/composite/nodes/node_composite_transform.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_transform.c rename to source/blender/nodes/composite/nodes/node_composite_transform.cc index be526c1059c..1695101cdbf 100644 --- a/source/blender/nodes/composite/nodes/node_composite_transform.c +++ b/source/blender/nodes/composite/nodes/node_composite_transform.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Transform ******************** */ diff --git a/source/blender/nodes/composite/nodes/node_composite_translate.c b/source/blender/nodes/composite/nodes/node_composite_translate.cc similarity index 89% rename from source/blender/nodes/composite/nodes/node_composite_translate.c rename to source/blender/nodes/composite/nodes/node_composite_translate.cc index 43337ec6f7e..0ee8a41a5ea 100644 --- a/source/blender/nodes/composite/nodes/node_composite_translate.c +++ b/source/blender/nodes/composite/nodes/node_composite_translate.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Translate ******************** */ @@ -38,7 +38,8 @@ static bNodeSocketTemplate cmp_node_translate_out[] = { static void node_composit_init_translate(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTranslateData *data = MEM_callocN(sizeof(NodeTranslateData), "node translate data"); + NodeTranslateData *data = (NodeTranslateData *)MEM_callocN(sizeof(NodeTranslateData), + "node translate data"); node->storage = data; } diff --git a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc similarity index 69% rename from source/blender/nodes/composite/nodes/node_composite_valToRgb.c rename to source/blender/nodes/composite/nodes/node_composite_valToRgb.cc index ed6dbfa2bf3..a0ab056e657 100644 --- a/source/blender/nodes/composite/nodes/node_composite_valToRgb.c +++ b/source/blender/nodes/composite/nodes/node_composite_valToRgb.cc @@ -21,18 +21,20 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** VALTORGB ******************** */ -static bNodeSocketTemplate cmp_node_valtorgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_valtorgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_valtorgb_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Image")); + b.add_output(N_("Alpha")); +} + +} // namespace blender::nodes static void node_composit_init_valtorgb(bNodeTree *UNUSED(ntree), bNode *node) { @@ -44,7 +46,7 @@ void register_node_type_cmp_valtorgb(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_valtorgb_in, cmp_node_valtorgb_out); + ntype.declare = blender::nodes::cmp_node_valtorgb_declare; node_type_size(&ntype, 240, 200, 320); node_type_init(&ntype, node_composit_init_valtorgb); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); @@ -53,21 +55,23 @@ void register_node_type_cmp_valtorgb(void) } /* **************** RGBTOBW ******************** */ -static bNodeSocketTemplate cmp_node_rgbtobw_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate cmp_node_rgbtobw_out[] = { - {SOCK_FLOAT, N_("Val"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_rgbtobw_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output(N_("Val")); +} + +} // namespace blender::nodes void register_node_type_cmp_rgbtobw(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, cmp_node_rgbtobw_in, cmp_node_rgbtobw_out); + ntype.declare = blender::nodes::cmp_node_rgbtobw_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_value.c b/source/blender/nodes/composite/nodes/node_composite_value.cc similarity index 80% rename from source/blender/nodes/composite/nodes/node_composite_value.c rename to source/blender/nodes/composite/nodes/node_composite_value.cc index 2ede2cb8c83..51214d23472 100644 --- a/source/blender/nodes/composite/nodes/node_composite_value.c +++ b/source/blender/nodes/composite/nodes/node_composite_value.cc @@ -21,20 +21,25 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** VALUE ******************** */ -static bNodeSocketTemplate cmp_node_value_out[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0, 0, 0, -FLT_MAX, FLT_MAX, PROP_NONE}, - {-1, ""}, -}; + +namespace blender::nodes { + +static void cmp_node_value_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Value")).default_value(0.5f); +} + +} // namespace blender::nodes void register_node_type_cmp_value(void) { static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, NULL, cmp_node_value_out); + ntype.declare = blender::nodes::cmp_node_value_declare; node_type_size_preset(&ntype, NODE_SIZE_SMALL); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/composite/nodes/node_composite_vecBlur.c b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc similarity index 90% rename from source/blender/nodes/composite/nodes/node_composite_vecBlur.c rename to source/blender/nodes/composite/nodes/node_composite_vecBlur.cc index 7005ea42afe..ce6ba659609 100644 --- a/source/blender/nodes/composite/nodes/node_composite_vecBlur.c +++ b/source/blender/nodes/composite/nodes/node_composite_vecBlur.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** VECTOR BLUR ******************** */ static bNodeSocketTemplate cmp_node_vecblur_in[] = { @@ -33,13 +33,13 @@ static bNodeSocketTemplate cmp_node_vecblur_out[] = {{SOCK_RGBA, N_("Image")}, { static void node_composit_init_vecblur(bNodeTree *UNUSED(ntree), bNode *node) { - NodeBlurData *nbd = MEM_callocN(sizeof(NodeBlurData), "node blur data"); + NodeBlurData *nbd = (NodeBlurData *)MEM_callocN(sizeof(NodeBlurData), "node blur data"); node->storage = nbd; nbd->samples = 32; nbd->fac = 1.0f; } -/* custom1: iterations, custom2: maxspeed (0 = nolimit) */ +/* custom1: iterations, custom2: max_speed (0 = no_limit). */ void register_node_type_cmp_vecblur(void) { static bNodeType ntype; diff --git a/source/blender/nodes/composite/nodes/node_composite_viewer.c b/source/blender/nodes/composite/nodes/node_composite_viewer.cc similarity index 72% rename from source/blender/nodes/composite/nodes/node_composite_viewer.c rename to source/blender/nodes/composite/nodes/node_composite_viewer.cc index b5f74d5c937..969e2409898 100644 --- a/source/blender/nodes/composite/nodes/node_composite_viewer.c +++ b/source/blender/nodes/composite/nodes/node_composite_viewer.cc @@ -21,24 +21,29 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" #include "BKE_global.h" #include "BKE_image.h" /* **************** VIEWER ******************** */ -static bNodeSocketTemplate cmp_node_viewer_in[] = { - {SOCK_RGBA, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, N_("Alpha"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Z"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE}, - {-1, ""}}; + +namespace blender::nodes { + +static void cmp_node_viewer_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).default_value({0.0f, 0.0f, 0.0f, 1.0f}); + b.add_input(N_("Alpha")).default_value(1.0f).min(0.0f).max(1.0f); + b.add_input(N_("Z")).default_value(1.0f).min(0.0f).max(1.0f); +} + +} // namespace blender::nodes static void node_composit_init_viewer(bNodeTree *UNUSED(ntree), bNode *node) { - ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); + ImageUser *iuser = (ImageUser *)MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; node->custom3 = 0.5f; node->custom4 = 0.5f; @@ -50,11 +55,11 @@ void register_node_type_cmp_viewer(void) static bNodeType ntype; cmp_node_type_base(&ntype, CMP_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, NODE_PREVIEW); - node_type_socket_templates(&ntype, cmp_node_viewer_in, NULL); + ntype.declare = blender::nodes::cmp_node_viewer_declare; node_type_init(&ntype, node_composit_init_viewer); node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage); - node_type_internal_links(&ntype, NULL); + node_type_internal_links(&ntype, nullptr); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/composite/nodes/node_composite_zcombine.c b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc similarity index 98% rename from source/blender/nodes/composite/nodes/node_composite_zcombine.c rename to source/blender/nodes/composite/nodes/node_composite_zcombine.cc index 5041b16c303..990f9fcd366 100644 --- a/source/blender/nodes/composite/nodes/node_composite_zcombine.c +++ b/source/blender/nodes/composite/nodes/node_composite_zcombine.cc @@ -21,7 +21,7 @@ * \ingroup cmpnodes */ -#include "node_composite_util.h" +#include "node_composite_util.hh" /* **************** Z COMBINE ******************** */ /* lazy coder NOTE: node->custom2 is abused to send signal. */ diff --git a/source/blender/nodes/function/nodes/node_fn_random_float.cc b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc similarity index 77% rename from source/blender/nodes/function/nodes/node_fn_random_float.cc rename to source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc index 1bd39aacdca..d98d49c7273 100644 --- a/source/blender/nodes/function/nodes/node_fn_random_float.cc +++ b/source/blender/nodes/function/nodes/legacy/node_fn_random_float.cc @@ -20,12 +20,13 @@ namespace blender::nodes { -static void fn_node_random_float_declare(NodeDeclarationBuilder &b) +static void fn_node_legacy_random_float_declare(NodeDeclarationBuilder &b) { - b.add_input("Min").min(-10000.0f).max(10000.0f); - b.add_input("Max").default_value(1.0f).min(-10000.0f).max(10000.0f); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Value"); + b.is_function_node(); + b.add_input(N_("Min")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Value")); }; } // namespace blender::nodes @@ -67,19 +68,19 @@ class RandomFloatFunction : public blender::fn::MultiFunction { } }; -static void fn_node_random_float_build_multi_function( +static void fn_node_legacy_random_float_build_multi_function( blender::nodes::NodeMultiFunctionBuilder &builder) { static RandomFloatFunction fn; builder.set_matching_fn(fn); } -void register_node_type_fn_random_float() +void register_node_type_fn_legacy_random_float() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_RANDOM_FLOAT, "Random Float", 0, 0); - ntype.declare = blender::nodes::fn_node_random_float_declare; - ntype.build_multi_function = fn_node_random_float_build_multi_function; + fn_node_type_base(&ntype, FN_NODE_LEGACY_RANDOM_FLOAT, "Random Float", 0, 0); + ntype.declare = blender::nodes::fn_node_legacy_random_float_declare; + ntype.build_multi_function = fn_node_legacy_random_float_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc new file mode 100644 index 00000000000..4088fa24ca7 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_align_euler_to_vector.cc @@ -0,0 +1,222 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_align_euler_to_vector_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input(N_("Factor")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Vector")).default_value({0.0, 0.0, 1.0}); + b.add_output(N_("Rotation")).subtype(PROP_EULER); +} + +static void fn_node_align_euler_to_vector_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "axis", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "pivot_axis", 0, IFACE_("Pivot"), ICON_NONE); +} + +static void align_rotations_auto_pivot(IndexMask mask, + const VArray &input_rotations, + const VArray &vectors, + const VArray &factors, + const float3 local_main_axis, + const MutableSpan output_rotations) +{ + threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) { + for (const int maski : mask_range) { + const int64_t i = mask[maski]; + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + output_rotations[i] = input_rotations[i]; + continue; + } + + float old_rotation[3][3]; + eul_to_mat3(old_rotation, input_rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + + const float3 new_axis = vector.normalized(); + float3 rotation_axis = float3::cross_high_precision(old_axis, new_axis); + if (is_zero_v3(rotation_axis)) { + /* The vectors are linearly dependent, so we fall back to another axis. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(1, 0, 0)); + if (is_zero_v3(rotation_axis)) { + /* This is now guaranteed to not be zero. */ + rotation_axis = float3::cross_high_precision(old_axis, float3(0, 1, 0)); + } + } + + const float full_angle = angle_normalized_v3v3(old_axis, new_axis); + const float angle = factors[i] * full_angle; + + float rotation[3][3]; + axis_angle_to_mat3(rotation, rotation_axis, angle); + + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); + + output_rotations[i] = new_rotation; + } + }); +} + +static void align_rotations_fixed_pivot(IndexMask mask, + const VArray &input_rotations, + const VArray &vectors, + const VArray &factors, + const float3 local_main_axis, + const float3 local_pivot_axis, + const MutableSpan output_rotations) +{ + threading::parallel_for(mask.index_range(), 512, [&](IndexRange mask_range) { + for (const int64_t maski : mask_range) { + const int64_t i = mask[maski]; + if (local_main_axis == local_pivot_axis) { + /* Can't compute any meaningful rotation angle in this case. */ + output_rotations[i] = input_rotations[i]; + continue; + } + + const float3 vector = vectors[i]; + if (is_zero_v3(vector)) { + output_rotations[i] = input_rotations[i]; + continue; + } + + float old_rotation[3][3]; + eul_to_mat3(old_rotation, input_rotations[i]); + float3 old_axis; + mul_v3_m3v3(old_axis, old_rotation, local_main_axis); + float3 pivot_axis; + mul_v3_m3v3(pivot_axis, old_rotation, local_pivot_axis); + + float full_angle = angle_signed_on_axis_v3v3_v3(vector, old_axis, pivot_axis); + if (full_angle > M_PI) { + /* Make sure the point is rotated as little as possible. */ + full_angle -= 2.0f * M_PI; + } + const float angle = factors[i] * full_angle; + + float rotation[3][3]; + axis_angle_to_mat3(rotation, pivot_axis, angle); + + float new_rotation_matrix[3][3]; + mul_m3_m3m3(new_rotation_matrix, rotation, old_rotation); + + float3 new_rotation; + mat3_to_eul(new_rotation, new_rotation_matrix); + + output_rotations[i] = new_rotation; + } + }); +} + +class MF_AlignEulerToVector : public fn::MultiFunction { + private: + int main_axis_mode_; + int pivot_axis_mode_; + + public: + MF_AlignEulerToVector(int main_axis_mode, int pivot_axis_mode) + : main_axis_mode_(main_axis_mode), pivot_axis_mode_(pivot_axis_mode) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Align Euler To Vector"}; + signature.single_input("Rotation"); + signature.single_input("Factor"); + signature.single_input("Vector"); + + signature.single_output("Rotation"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &input_rotations = params.readonly_single_input(0, "Rotation"); + const VArray &factors = params.readonly_single_input(1, "Factor"); + const VArray &vectors = params.readonly_single_input(2, "Vector"); + + auto output_rotations = params.uninitialized_single_output(3, "Rotation"); + + float3 local_main_axis = {0.0f, 0.0f, 0.0f}; + local_main_axis[main_axis_mode_] = 1; + + if (pivot_axis_mode_ == FN_NODE_ALIGN_EULER_TO_VECTOR_PIVOT_AXIS_AUTO) { + align_rotations_auto_pivot( + mask, input_rotations, vectors, factors, local_main_axis, output_rotations); + } + else { + float3 local_pivot_axis = {0.0f, 0.0f, 0.0f}; + local_pivot_axis[pivot_axis_mode_ - 1] = 1; + align_rotations_fixed_pivot(mask, + input_rotations, + vectors, + factors, + local_main_axis, + local_pivot_axis, + output_rotations); + } + } +}; + +static void fn_node_align_euler_to_vector_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + builder.construct_and_set_matching_fn(node.custom1, node.custom2); +} + +} // namespace blender::nodes + +void register_node_type_fn_align_euler_to_vector() +{ + static bNodeType ntype; + + fn_node_type_base( + &ntype, FN_NODE_ALIGN_EULER_TO_VECTOR, "Align Euler to Vector", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_align_euler_to_vector_declare; + ntype.draw_buttons = blender::nodes::fn_node_align_euler_to_vector_layout; + ntype.build_multi_function = blender::nodes::fn_node_align_euler_to_vector_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc index b71ee092de6..b44e8d54ff1 100644 --- a/source/blender/nodes/function/nodes/node_fn_boolean_math.cc +++ b/source/blender/nodes/function/nodes/node_fn_boolean_math.cc @@ -28,13 +28,12 @@ namespace blender::nodes { static void fn_node_boolean_math_declare(NodeDeclarationBuilder &b) { - b.add_input("Boolean", "Boolean"); - b.add_input("Boolean", "Boolean_001"); - b.add_output("Boolean"); + b.is_function_node(); + b.add_input(N_("Boolean"), "Boolean"); + b.add_input(N_("Boolean"), "Boolean_001"); + b.add_output(N_("Boolean")); }; -} // namespace blender::nodes - static void fn_node_boolean_math_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -58,13 +57,13 @@ static void node_boolean_math_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(bNode &bnode) { - static blender::fn::CustomMF_SI_SI_SO and_fn{ - "And", [](bool a, bool b) { return a && b; }}; - static blender::fn::CustomMF_SI_SI_SO or_fn{ - "Or", [](bool a, bool b) { return a || b; }}; - static blender::fn::CustomMF_SI_SO not_fn{"Not", [](bool a) { return !a; }}; + static fn::CustomMF_SI_SI_SO and_fn{"And", + [](bool a, bool b) { return a && b; }}; + static fn::CustomMF_SI_SI_SO or_fn{"Or", + [](bool a, bool b) { return a || b; }}; + static fn::CustomMF_SI_SO not_fn{"Not", [](bool a) { return !a; }}; switch (bnode.custom1) { case NODE_BOOLEAN_MATH_AND: @@ -79,22 +78,23 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) return nullptr; } -static void fn_node_boolean_math_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_boolean_math_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes + void register_node_type_fn_boolean_math() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_BOOLEAN_MATH, "Boolean Math", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_boolean_math_declare; - node_type_label(&ntype, node_boolean_math_label); - node_type_update(&ntype, node_boolean_math_update); - ntype.build_multi_function = fn_node_boolean_math_build_multi_function; - ntype.draw_buttons = fn_node_boolean_math_layout; + node_type_label(&ntype, blender::nodes::node_boolean_math_label); + node_type_update(&ntype, blender::nodes::node_boolean_math_update); + ntype.build_multi_function = blender::nodes::fn_node_boolean_math_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_boolean_math_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_compare.cc b/source/blender/nodes/function/nodes/node_fn_float_compare.cc index 4f4830afabc..2e1f2aaeeef 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_compare.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_compare.cc @@ -30,14 +30,13 @@ namespace blender::nodes { static void fn_node_float_compare_declare(NodeDeclarationBuilder &b) { - b.add_input("A").min(-10000.0f).max(10000.0f); - b.add_input("B").min(-10000.0f).max(10000.0f); - b.add_input("Epsilon").default_value(0.001f).min(-10000.0f).max(10000.0f); - b.add_output("Result"); + b.is_function_node(); + b.add_input(N_("A")).min(-10000.0f).max(10000.0f); + b.add_input(N_("B")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Epsilon")).default_value(0.001f).min(-10000.0f).max(10000.0f); + b.add_output(N_("Result")); }; -} // namespace blender::nodes - static void geo_node_float_compare_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); @@ -64,19 +63,19 @@ static void node_float_compare_label(bNodeTree *UNUSED(ntree), BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &node) +static const fn::MultiFunction *get_multi_function(bNode &node) { - static blender::fn::CustomMF_SI_SI_SO less_than_fn{ + static fn::CustomMF_SI_SI_SO less_than_fn{ "Less Than", [](float a, float b) { return a < b; }}; - static blender::fn::CustomMF_SI_SI_SO less_equal_fn{ + static fn::CustomMF_SI_SI_SO less_equal_fn{ "Less Equal", [](float a, float b) { return a <= b; }}; - static blender::fn::CustomMF_SI_SI_SO greater_than_fn{ + static fn::CustomMF_SI_SI_SO greater_than_fn{ "Greater Than", [](float a, float b) { return a > b; }}; - static blender::fn::CustomMF_SI_SI_SO greater_equal_fn{ + static fn::CustomMF_SI_SI_SO greater_equal_fn{ "Greater Equal", [](float a, float b) { return a >= b; }}; - static blender::fn::CustomMF_SI_SI_SI_SO equal_fn{ + static fn::CustomMF_SI_SI_SI_SO equal_fn{ "Equal", [](float a, float b, float epsilon) { return std::abs(a - b) <= epsilon; }}; - static blender::fn::CustomMF_SI_SI_SI_SO not_equal_fn{ + static fn::CustomMF_SI_SI_SI_SO not_equal_fn{ "Not Equal", [](float a, float b, float epsilon) { return std::abs(a - b) > epsilon; }}; switch (node.custom1) { @@ -98,22 +97,23 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &node) return nullptr; } -static void fn_node_float_compare_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_float_compare_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes + void register_node_type_fn_float_compare() { static bNodeType ntype; - fn_node_type_base(&ntype, FN_NODE_FLOAT_COMPARE, "Float Compare", NODE_CLASS_CONVERTER, 0); + fn_node_type_base(&ntype, FN_NODE_COMPARE_FLOATS, "Compare Floats", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_float_compare_declare; - node_type_label(&ntype, node_float_compare_label); - node_type_update(&ntype, node_float_compare_update); - ntype.build_multi_function = fn_node_float_compare_build_multi_function; - ntype.draw_buttons = geo_node_float_compare_layout; + node_type_label(&ntype, blender::nodes::node_float_compare_label); + node_type_update(&ntype, blender::nodes::node_float_compare_update); + ntype.build_multi_function = blender::nodes::fn_node_float_compare_build_multi_function; + ntype.draw_buttons = blender::nodes::geo_node_float_compare_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc index e59c78d2c04..e6ec925f945 100644 --- a/source/blender/nodes/function/nodes/node_fn_float_to_int.cc +++ b/source/blender/nodes/function/nodes/node_fn_float_to_int.cc @@ -29,12 +29,11 @@ namespace blender::nodes { static void fn_node_float_to_int_declare(NodeDeclarationBuilder &b) { - b.add_input("Float"); - b.add_output("Integer"); + b.is_function_node(); + b.add_input(N_("Float")); + b.add_output(N_("Integer")); }; -} // namespace blender::nodes - static void fn_node_float_to_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "rounding_mode", 0, "", ICON_NONE); @@ -50,16 +49,13 @@ static void node_float_to_int_label(bNodeTree *UNUSED(ntree), bNode *node, char BLI_strncpy(label, IFACE_(name), maxlen); } -static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) +static const fn::MultiFunction *get_multi_function(bNode &bnode) { - static blender::fn::CustomMF_SI_SO round_fn{"Round", - [](float a) { return (int)round(a); }}; - static blender::fn::CustomMF_SI_SO floor_fn{"Floor", - [](float a) { return (int)floor(a); }}; - static blender::fn::CustomMF_SI_SO ceil_fn{"Ceiling", - [](float a) { return (int)ceil(a); }}; - static blender::fn::CustomMF_SI_SO trunc_fn{"Truncate", - [](float a) { return (int)trunc(a); }}; + static fn::CustomMF_SI_SO round_fn{"Round", [](float a) { return (int)round(a); }}; + static fn::CustomMF_SI_SO floor_fn{"Floor", [](float a) { return (int)floor(a); }}; + static fn::CustomMF_SI_SO ceil_fn{"Ceiling", [](float a) { return (int)ceil(a); }}; + static fn::CustomMF_SI_SO trunc_fn{"Truncate", + [](float a) { return (int)trunc(a); }}; switch (static_cast(bnode.custom1)) { case FN_NODE_FLOAT_TO_INT_ROUND: @@ -76,21 +72,22 @@ static const blender::fn::MultiFunction *get_multi_function(bNode &bnode) return nullptr; } -static void fn_node_float_to_int_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_float_to_int_build_multi_function(NodeMultiFunctionBuilder &builder) { - const blender::fn::MultiFunction *fn = get_multi_function(builder.node()); + const fn::MultiFunction *fn = get_multi_function(builder.node()); builder.set_matching_fn(fn); } +} // namespace blender::nodes + void register_node_type_fn_float_to_int() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_FLOAT_TO_INT, "Float to Integer", NODE_CLASS_CONVERTER, 0); ntype.declare = blender::nodes::fn_node_float_to_int_declare; - node_type_label(&ntype, node_float_to_int_label); - ntype.build_multi_function = fn_node_float_to_int_build_multi_function; - ntype.draw_buttons = fn_node_float_to_int_layout; + node_type_label(&ntype, blender::nodes::node_float_to_int_label); + ntype.build_multi_function = blender::nodes::fn_node_float_to_int_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_float_to_int_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_bool.cc b/source/blender/nodes/function/nodes/node_fn_input_bool.cc new file mode 100644 index 00000000000..1358bf8a223 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_bool.cc @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_input_bool_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Boolean")); +}; + +static void fn_node_input_bool_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "boolean", UI_ITEM_R_EXPAND, IFACE_("Value"), ICON_NONE); +} + +static void fn_node_input_bool_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + NodeInputBool *node_storage = static_cast(bnode.storage); + builder.construct_and_set_matching_fn>(node_storage->boolean); +} + +static void fn_node_input_bool_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputBool *data = (NodeInputBool *)MEM_callocN(sizeof(NodeInputBool), __func__); + node->storage = data; +} + +} // namespace blender::nodes + +void register_node_type_fn_input_bool() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_BOOL, "Boolean", 0, 0); + ntype.declare = blender::nodes::fn_node_input_bool_declare; + node_type_init(&ntype, blender::nodes::fn_node_input_bool_init); + node_type_storage( + &ntype, "NodeInputBool", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_input_bool_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_bool_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc new file mode 100644 index 00000000000..43bb654b776 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -0,0 +1,65 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_input_color_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Color")); +}; + +static void fn_node_input_color_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiTemplateColorPicker(layout, ptr, "color", true, false, false, true); + uiItemR(layout, ptr, "color", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void fn_node_input_color_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + NodeInputColor *node_storage = static_cast(bnode.storage); + blender::ColorGeometry4f color = (ColorGeometry4f)node_storage->color; + builder.construct_and_set_matching_fn>(color); +} + +static void fn_node_input_color_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputColor *data = (NodeInputColor *)MEM_callocN(sizeof(NodeInputColor), __func__); + copy_v4_fl4(data->color, 0.5f, 0.5f, 0.5f, 1.0f); + node->storage = data; +} + +} // namespace blender::nodes + +void register_node_type_fn_input_color() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_COLOR, "Color", NODE_CLASS_INPUT, 0); + ntype.declare = blender::nodes::fn_node_input_color_declare; + node_type_init(&ntype, blender::nodes::fn_node_input_color_init); + node_type_storage( + &ntype, "NodeInputColor", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_input_color_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_color_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_int.cc b/source/blender/nodes/function/nodes/node_fn_input_int.cc new file mode 100644 index 00000000000..ddbb86e2661 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_int.cc @@ -0,0 +1,64 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +#include "BLI_hash.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_input_int_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Integer")); +}; + +static void fn_node_input_int_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayout *col = uiLayoutColumn(layout, true); + uiItemR(col, ptr, "integer", UI_ITEM_R_EXPAND, "", ICON_NONE); +} + +static void fn_node_input_int_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + NodeInputInt *node_storage = static_cast(bnode.storage); + builder.construct_and_set_matching_fn>(node_storage->integer); +} + +static void fn_node_input_int_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeInputInt *data = (NodeInputInt *)MEM_callocN(sizeof(NodeInputInt), __func__); + node->storage = data; +} + +} // namespace blender::nodes + +void register_node_type_fn_input_int() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_INPUT_INT, "Integer", 0, 0); + ntype.declare = blender::nodes::fn_node_input_int_declare; + node_type_init(&ntype, blender::nodes::fn_node_input_int_init); + node_type_storage( + &ntype, "NodeInputInt", node_free_standard_storage, node_copy_standard_storage); + ntype.build_multi_function = blender::nodes::fn_node_input_int_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_int_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc new file mode 100644 index 00000000000..c61af419e50 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_input_special_characters.cc @@ -0,0 +1,74 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_input_special_characters_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Line Break")); + b.add_output(N_("Tab")); +}; + +class MF_SpecialCharacters : public fn::MultiFunction { + public: + MF_SpecialCharacters() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Special Characters"}; + signature.single_output("Line Break"); + signature.single_output("Tab"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + MutableSpan lb = params.uninitialized_single_output(0, "Line Break"); + MutableSpan tab = params.uninitialized_single_output(1, "Tab"); + + for (const int i : mask) { + new (&lb[i]) std::string("\n"); + new (&tab[i]) std::string("\t"); + } + } +}; + +static void fn_node_input_special_characters_build_multi_function( + NodeMultiFunctionBuilder &builder) +{ + static MF_SpecialCharacters special_characters_fn; + builder.set_matching_fn(special_characters_fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_input_special_characters() +{ + static bNodeType ntype; + + fn_node_type_base( + &ntype, FN_NODE_INPUT_SPECIAL_CHARACTERS, "Special Characters", NODE_CLASS_INPUT, 0); + ntype.declare = blender::nodes::fn_node_input_special_characters_declare; + ntype.build_multi_function = + blender::nodes::fn_node_input_special_characters_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_input_string.cc b/source/blender/nodes/function/nodes/node_fn_input_string.cc index 4a8e898fb9b..dd2d1292601 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_string.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_string.cc @@ -23,24 +23,21 @@ namespace blender::nodes { static void fn_node_input_string_declare(NodeDeclarationBuilder &b) { - b.add_output("String"); + b.is_function_node(); + b.add_output(N_("String")); }; -} // namespace blender::nodes - static void fn_node_input_string_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiItemR(layout, ptr, "string", 0, "", ICON_NONE); } -static void fn_node_input_string_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_input_string_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); NodeInputString *node_storage = static_cast(bnode.storage); std::string string = std::string((node_storage->string) ? node_storage->string : ""); - builder.construct_and_set_matching_fn>( - std::move(string)); + builder.construct_and_set_matching_fn>(std::move(string)); } static void fn_node_input_string_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -74,15 +71,20 @@ static void fn_node_string_copy(bNodeTree *UNUSED(dest_ntree), dest_node->storage = destination_storage; } +} // namespace blender::nodes + void register_node_type_fn_input_string() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_STRING, "String", NODE_CLASS_INPUT, 0); ntype.declare = blender::nodes::fn_node_input_string_declare; - node_type_init(&ntype, fn_node_input_string_init); - node_type_storage(&ntype, "NodeInputString", fn_node_input_string_free, fn_node_string_copy); - ntype.build_multi_function = fn_node_input_string_build_multi_function; - ntype.draw_buttons = fn_node_input_string_layout; + node_type_init(&ntype, blender::nodes::fn_node_input_string_init); + node_type_storage(&ntype, + "NodeInputString", + blender::nodes::fn_node_input_string_free, + blender::nodes::fn_node_string_copy); + ntype.build_multi_function = blender::nodes::fn_node_input_string_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_string_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_input_vector.cc b/source/blender/nodes/function/nodes/node_fn_input_vector.cc index 9548df7b423..1e5fd186b5a 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_vector.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_vector.cc @@ -25,25 +25,23 @@ namespace blender::nodes { static void fn_node_input_vector_declare(NodeDeclarationBuilder &b) { - b.add_output("Vector"); + b.add_output(N_("Vector")); }; -} // namespace blender::nodes - static void fn_node_input_vector_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *col = uiLayoutColumn(layout, true); uiItemR(col, ptr, "vector", UI_ITEM_R_EXPAND, "", ICON_NONE); } -static void fn_node_vector_input_build_multi_function( - blender::nodes::NodeMultiFunctionBuilder &builder) +static void fn_node_input_vector_build_multi_function(NodeMultiFunctionBuilder &builder) { bNode &bnode = builder.node(); NodeInputVector *node_storage = static_cast(bnode.storage); - blender::float3 vector(node_storage->vector); - builder.construct_and_set_matching_fn>(vector); + float3 vector(node_storage->vector); + builder.construct_and_set_matching_fn>(vector); } + static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) { NodeInputVector *data = (NodeInputVector *)MEM_callocN(sizeof(NodeInputVector), @@ -51,16 +49,18 @@ static void fn_node_input_vector_init(bNodeTree *UNUSED(ntree), bNode *node) node->storage = data; } +} // namespace blender::nodes + void register_node_type_fn_input_vector() { static bNodeType ntype; fn_node_type_base(&ntype, FN_NODE_INPUT_VECTOR, "Vector", 0, 0); ntype.declare = blender::nodes::fn_node_input_vector_declare; - node_type_init(&ntype, fn_node_input_vector_init); + node_type_init(&ntype, blender::nodes::fn_node_input_vector_init); node_type_storage( &ntype, "NodeInputVector", node_free_standard_storage, node_copy_standard_storage); - ntype.build_multi_function = fn_node_vector_input_build_multi_function; - ntype.draw_buttons = fn_node_input_vector_layout; + ntype.build_multi_function = blender::nodes::fn_node_input_vector_build_multi_function; + ntype.draw_buttons = blender::nodes::fn_node_input_vector_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/function/nodes/node_fn_random_value.cc b/source/blender/nodes/function/nodes/node_fn_random_value.cc new file mode 100644 index 00000000000..d48b9f3461a --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_random_value.cc @@ -0,0 +1,299 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// #include "BLI_hash.h" +#include "BLI_noise.hh" + +#include "node_function_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void fn_node_random_value_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Min")).supports_field(); + b.add_input(N_("Max")).default_value({1.0f, 1.0f, 1.0f}).supports_field(); + b.add_input(N_("Min"), "Min_001").supports_field(); + b.add_input(N_("Max"), "Max_001").default_value(1.0f).supports_field(); + b.add_input(N_("Min"), "Min_002").min(-100000).max(100000).supports_field(); + b.add_input(N_("Max"), "Max_002") + .default_value(100) + .min(-100000) + .max(100000) + .supports_field(); + b.add_input(N_("Probability")) + .min(0.0f) + .max(1.0f) + .default_value(0.5f) + .subtype(PROP_FACTOR) + .supports_field(); + b.add_input(N_("ID")).implicit_field(); + b.add_input(N_("Seed")).default_value(0).min(-10000).max(10000).supports_field(); + + b.add_output(N_("Value")).dependent_field(); + b.add_output(N_("Value"), "Value_001").dependent_field(); + b.add_output(N_("Value"), "Value_002").dependent_field(); + b.add_output(N_("Value"), "Value_003").dependent_field(); +} + +static void fn_node_random_value_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static void fn_node_random_value_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeRandomValue *data = (NodeRandomValue *)MEM_callocN(sizeof(NodeRandomValue), __func__); + data->data_type = CD_PROP_FLOAT; + node->storage = data; +} + +static void fn_node_random_value_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeRandomValue &storage = *(const NodeRandomValue *)node->storage; + const CustomDataType data_type = static_cast(storage.data_type); + + bNodeSocket *sock_min_vector = (bNodeSocket *)node->inputs.first; + bNodeSocket *sock_max_vector = sock_min_vector->next; + bNodeSocket *sock_min_float = sock_max_vector->next; + bNodeSocket *sock_max_float = sock_min_float->next; + bNodeSocket *sock_min_int = sock_max_float->next; + bNodeSocket *sock_max_int = sock_min_int->next; + bNodeSocket *sock_probability = sock_max_int->next; + + bNodeSocket *sock_out_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *sock_out_float = sock_out_vector->next; + bNodeSocket *sock_out_int = sock_out_float->next; + bNodeSocket *sock_out_bool = sock_out_int->next; + + nodeSetSocketAvailability(sock_min_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(sock_max_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(sock_min_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(sock_max_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(sock_min_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(sock_max_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(sock_probability, data_type == CD_PROP_BOOL); + + nodeSetSocketAvailability(sock_out_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(sock_out_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(sock_out_int, data_type == CD_PROP_INT32); + nodeSetSocketAvailability(sock_out_bool, data_type == CD_PROP_BOOL); +} + +class RandomVectorFunction : public fn::MultiFunction { + public: + RandomVectorFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Min"); + signature.single_input("Max"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &min_values = params.readonly_single_input(0, "Min"); + const VArray &max_values = params.readonly_single_input(1, "Max"); + const VArray &ids = params.readonly_single_input(2, "ID"); + const VArray &seeds = params.readonly_single_input(3, "Seed"); + MutableSpan values = params.uninitialized_single_output(4, "Value"); + + for (int64_t i : mask) { + const float3 min_value = min_values[i]; + const float3 max_value = max_values[i]; + const int seed = seeds[i]; + const int id = ids[i]; + + const float x = noise::hash_to_float(seed, id, 0); + const float y = noise::hash_to_float(seed, id, 1); + const float z = noise::hash_to_float(seed, id, 2); + + values[i] = float3(x, y, z) * (max_value - min_value) + min_value; + } + } +}; + +class RandomFloatFunction : public fn::MultiFunction { + public: + RandomFloatFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Min"); + signature.single_input("Max"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &min_values = params.readonly_single_input(0, "Min"); + const VArray &max_values = params.readonly_single_input(1, "Max"); + const VArray &ids = params.readonly_single_input(2, "ID"); + const VArray &seeds = params.readonly_single_input(3, "Seed"); + MutableSpan values = params.uninitialized_single_output(4, "Value"); + + for (int64_t i : mask) { + const float min_value = min_values[i]; + const float max_value = max_values[i]; + const int seed = seeds[i]; + const int id = ids[i]; + + const float value = noise::hash_to_float(seed, id); + values[i] = value * (max_value - min_value) + min_value; + } + } +}; + +class RandomIntFunction : public fn::MultiFunction { + public: + RandomIntFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Min"); + signature.single_input("Max"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &min_values = params.readonly_single_input(0, "Min"); + const VArray &max_values = params.readonly_single_input(1, "Max"); + const VArray &ids = params.readonly_single_input(2, "ID"); + const VArray &seeds = params.readonly_single_input(3, "Seed"); + MutableSpan values = params.uninitialized_single_output(4, "Value"); + + for (int64_t i : mask) { + const float min_value = min_values[i]; + const float max_value = max_values[i]; + const int seed = seeds[i]; + const int id = ids[i]; + + const float value = noise::hash_to_float(id, seed); + values[i] = round_fl_to_int(value * (max_value - min_value) + min_value); + } + } +}; + +class RandomBoolFunction : public fn::MultiFunction { + public: + RandomBoolFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Random Value"}; + signature.single_input("Probability"); + signature.single_input("ID"); + signature.single_input("Seed"); + signature.single_output("Value"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &probabilities = params.readonly_single_input(0, "Probability"); + const VArray &ids = params.readonly_single_input(1, "ID"); + const VArray &seeds = params.readonly_single_input(2, "Seed"); + MutableSpan values = params.uninitialized_single_output(3, "Value"); + + for (int64_t i : mask) { + const int seed = seeds[i]; + const int id = ids[i]; + const float probability = probabilities[i]; + values[i] = noise::hash_to_float(id, seed) <= probability; + } + } +}; + +static void fn_node_random_value_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const NodeRandomValue &storage = *(const NodeRandomValue *)builder.node().storage; + const CustomDataType data_type = static_cast(storage.data_type); + + switch (data_type) { + case CD_PROP_FLOAT3: { + static RandomVectorFunction fn; + builder.set_matching_fn(fn); + break; + } + case CD_PROP_FLOAT: { + static RandomFloatFunction fn; + builder.set_matching_fn(fn); + break; + } + case CD_PROP_INT32: { + static RandomIntFunction fn; + builder.set_matching_fn(fn); + break; + } + case CD_PROP_BOOL: { + static RandomBoolFunction fn; + builder.set_matching_fn(fn); + break; + } + default: { + BLI_assert_unreachable(); + break; + } + } +} + +} // namespace blender::nodes + +void register_node_type_fn_random_value() +{ + static bNodeType ntype; + fn_node_type_base(&ntype, FN_NODE_RANDOM_VALUE, "Random Value", NODE_CLASS_CONVERTER, 0); + node_type_init(&ntype, blender::nodes::fn_node_random_value_init); + node_type_update(&ntype, blender::nodes::fn_node_random_value_update); + ntype.draw_buttons = blender::nodes::fn_node_random_value_layout; + ntype.declare = blender::nodes::fn_node_random_value_declare; + ntype.build_multi_function = blender::nodes::fn_node_random_value_build_multi_function; + node_type_storage( + &ntype, "NodeRandomValue", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_replace_string.cc b/source/blender/nodes/function/nodes/node_fn_replace_string.cc new file mode 100644 index 00000000000..881a3c68e7d --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_replace_string.cc @@ -0,0 +1,66 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_string_utf8.h" + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_replace_string_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("String")); + b.add_input(N_("Find")).description(N_("The string to find in the input string")); + b.add_input(N_("Replace")) + .description(N_("The string to replace each match with")); + b.add_output(N_("String")); +}; + +static std::string replace_all(std::string str, const std::string &from, const std::string &to) +{ + if (from.length() <= 0) { + return str; + } + const size_t step = to.length() > 0 ? to.length() : 1; + + size_t offset = 0; + while ((offset = str.find(from, offset)) != std::string::npos) { + str.replace(offset, from.length(), to); + offset += step; + } + return str; +} + +static void fn_node_replace_string_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + static fn::CustomMF_SI_SI_SI_SO substring_fn{ + "Replace", [](const std::string &str, const std::string &find, const std::string &replace) { + return replace_all(str, find, replace); + }}; + builder.set_matching_fn(&substring_fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_replace_string() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_REPLACE_STRING, "Replace String", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_replace_string_declare; + ntype.build_multi_function = blender::nodes::fn_node_replace_string_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc new file mode 100644 index 00000000000..fc4c3d8221f --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_rotate_euler.cc @@ -0,0 +1,139 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_listbase.h" +#include "BLI_string.h" + +#include "RNA_enum_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_rotate_euler_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Rotation")).subtype(PROP_EULER).hide_value(); + b.add_input(N_("Rotate By")).subtype(PROP_EULER); + b.add_input(N_("Axis")).default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); + b.add_input(N_("Angle")).subtype(PROP_ANGLE); + b.add_output(N_("Rotation")); +}; + +static void fn_node_rotate_euler_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *rotate_by_socket = static_cast(BLI_findlink(&node->inputs, 1)); + bNodeSocket *axis_socket = static_cast(BLI_findlink(&node->inputs, 2)); + bNodeSocket *angle_socket = static_cast(BLI_findlink(&node->inputs, 3)); + + nodeSetSocketAvailability(rotate_by_socket, + ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_EULER)); + nodeSetSocketAvailability(axis_socket, + ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE)); + nodeSetSocketAvailability(angle_socket, + ELEM(node->custom1, FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE)); +} + +static void fn_node_rotate_euler_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "type", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "space", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static const fn::MultiFunction *get_multi_function(bNode &bnode) +{ + static fn::CustomMF_SI_SI_SO obj_euler_rot{ + "Rotate Euler by Euler/Object", [](const float3 &input, const float3 &rotation) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + eul_to_mat3(rot_mat, rotation); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, rot_mat, input_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SI_SO obj_AA_rot{ + "Rotate Euler by AxisAngle/Object", + [](const float3 &input, const float3 &axis, float angle) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + axis_angle_to_mat3(rot_mat, axis, angle); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, rot_mat, input_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SO local_euler_rot{ + "Rotate Euler by Euler/Local", [](const float3 &input, const float3 &rotation) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + eul_to_mat3(rot_mat, rotation); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, input_mat, rot_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + static fn::CustomMF_SI_SI_SI_SO local_AA_rot{ + "Rotate Euler by AxisAngle/Local", [](const float3 &input, const float3 &axis, float angle) { + float input_mat[3][3]; + eul_to_mat3(input_mat, input); + float rot_mat[3][3]; + axis_angle_to_mat3(rot_mat, axis, angle); + float mat_res[3][3]; + mul_m3_m3m3(mat_res, input_mat, rot_mat); + float3 result; + mat3_to_eul(result, mat_res); + return result; + }}; + short type = bnode.custom1; + short space = bnode.custom2; + if (type == FN_NODE_ROTATE_EULER_TYPE_AXIS_ANGLE) { + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_AA_rot : &local_AA_rot; + } + if (type == FN_NODE_ROTATE_EULER_TYPE_EULER) { + return space == FN_NODE_ROTATE_EULER_SPACE_OBJECT ? &obj_euler_rot : &local_euler_rot; + } + BLI_assert_unreachable(); + return nullptr; +} + +static void fn_node_rotate_euler_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + const fn::MultiFunction *fn = get_multi_function(builder.node()); + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_rotate_euler() +{ + static bNodeType ntype; + fn_node_type_base(&ntype, FN_NODE_ROTATE_EULER, "Rotate Euler", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_rotate_euler_declare; + ntype.draw_buttons = blender::nodes::fn_node_rotate_euler_layout; + node_type_update(&ntype, blender::nodes::fn_node_rotate_euler_update); + ntype.build_multi_function = blender::nodes::fn_node_rotate_euler_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_slice_string.cc b/source/blender/nodes/function/nodes/node_fn_slice_string.cc new file mode 100644 index 00000000000..5cb753e8f34 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_slice_string.cc @@ -0,0 +1,53 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_string_utf8.h" + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_slice_string_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("String")); + b.add_input(N_("Position")); + b.add_input(N_("Length")).min(0).default_value(10); + b.add_output(N_("String")); +}; + +static void fn_node_slice_string_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + static blender::fn::CustomMF_SI_SI_SI_SO slice_fn{ + "Slice", [](const std::string &str, int a, int b) { + const int len = BLI_strlen_utf8(str.c_str()); + const int start = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a, 0, len)); + const int end = BLI_str_utf8_offset_from_index(str.c_str(), std::clamp(a + b, 0, len)); + return str.substr(start, std::max(end - start, 0)); + }}; + builder.set_matching_fn(&slice_fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_slice_string() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_SLICE_STRING, "Slice String", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_slice_string_declare; + ntype.build_multi_function = blender::nodes::fn_node_slice_string_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_string_length.cc b/source/blender/nodes/function/nodes/node_fn_string_length.cc new file mode 100644 index 00000000000..63429d35993 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_string_length.cc @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_string_utf8.h" + +#include + +#include "node_function_util.hh" + +namespace blender::nodes { + +static void fn_node_string_length_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("String")); + b.add_output(N_("Length")); +}; + +static void fn_node_string_length_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + static fn::CustomMF_SI_SO str_len_fn{ + "String Length", [](const std::string &a) { return BLI_strlen_utf8(a.c_str()); }}; + builder.set_matching_fn(&str_len_fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_string_length() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_STRING_LENGTH, "String Length", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_string_length_declare; + ntype.build_multi_function = blender::nodes::fn_node_string_length_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/function/nodes/node_fn_value_to_string.cc b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc new file mode 100644 index 00000000000..96a56760664 --- /dev/null +++ b/source/blender/nodes/function/nodes/node_fn_value_to_string.cc @@ -0,0 +1,50 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_function_util.hh" +#include + +namespace blender::nodes { + +static void fn_node_value_to_string_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Value")); + b.add_input(N_("Decimals")).min(0); + b.add_output(N_("String")); +}; + +static void fn_node_value_to_string_build_multi_function(NodeMultiFunctionBuilder &builder) +{ + static fn::CustomMF_SI_SI_SO to_str_fn{ + "Value To String", [](float a, int b) { + std::stringstream stream; + stream << std::fixed << std::setprecision(std::max(0, b)) << a; + return stream.str(); + }}; + builder.set_matching_fn(&to_str_fn); +} + +} // namespace blender::nodes + +void register_node_type_fn_value_to_string() +{ + static bNodeType ntype; + + fn_node_type_base(&ntype, FN_NODE_VALUE_TO_STRING, "Value to String", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::fn_node_value_to_string_declare; + ntype.build_multi_function = blender::nodes::fn_node_value_to_string_build_multi_function; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/node_geometry_tree.cc b/source/blender/nodes/geometry/node_geometry_tree.cc index d6b23c38ee4..9747bb63773 100644 --- a/source/blender/nodes/geometry/node_geometry_tree.cc +++ b/source/blender/nodes/geometry/node_geometry_tree.cc @@ -109,6 +109,7 @@ static bool geometry_node_tree_socket_type_valid(bNodeTreeType *UNUSED(ntreetype SOCK_GEOMETRY, SOCK_COLLECTION, SOCK_TEXTURE, + SOCK_IMAGE, SOCK_MATERIAL); } @@ -119,7 +120,7 @@ void register_node_tree_type_geo(void) tt->type = NTREE_GEOMETRY; strcpy(tt->idname, "GeometryNodeTree"); strcpy(tt->ui_name, N_("Geometry Node Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Geometry nodes")); tt->rna_ext.srna = &RNA_GeometryNodeTree; tt->update = geometry_node_tree_update; diff --git a/source/blender/nodes/geometry/node_geometry_util.hh b/source/blender/nodes/geometry/node_geometry_util.hh index 015ac0de002..167765fa131 100644 --- a/source/blender/nodes/geometry/node_geometry_util.hh +++ b/source/blender/nodes/geometry/node_geometry_util.hh @@ -32,6 +32,7 @@ #include "NOD_geometry.h" #include "NOD_geometry_exec.hh" #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "node_util.h" @@ -50,11 +51,15 @@ void update_attribute_input_socket_availabilities(bNode &node, Array get_geometry_element_ids_as_uints(const GeometryComponent &component, const AttributeDomain domain); -void transform_mesh(Mesh *mesh, +void transform_mesh(Mesh &mesh, const float3 translation, const float3 rotation, const float3 scale); +void transform_geometry_set(GeometrySet &geometry, + const float4x4 &transform, + const Depsgraph &depsgraph); + Mesh *create_line_mesh(const float3 start, const float3 delta, const int count); Mesh *create_grid_mesh(const int verts_x, @@ -65,7 +70,9 @@ Mesh *create_grid_mesh(const int verts_x, Mesh *create_cylinder_or_cone_mesh(const float radius_top, const float radius_bottom, const float depth, - const int verts_num, + const int circle_segments, + const int side_segments, + const int fill_segments, const GeometryNodeMeshCircleFillType fill_type); Mesh *create_cuboid_mesh(float3 size, int verts_x, int verts_y, int verts_z); @@ -77,6 +84,17 @@ void copy_point_attributes_based_on_mask(const GeometryComponent &in_component, GeometryComponent &result_component, Span masks, const bool invert); +/** + * Returns the parts of the geometry that are on the selection for the given domain. If the domain + * is not applicable for the component, e.g. face domain for point cloud, nothing happens to that + * component. If no component can work with the domain, then `error_message` is set to true. + */ +void separate_geometry(GeometrySet &geometry_set, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode, + const Field &selection_field, + const bool invert, + bool &r_is_error); struct CurveToPointsResults { int result_size; diff --git a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc similarity index 96% rename from source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc index d0bb906e8af..b92d4704d63 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_align_rotation_to_vector.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_align_rotation_to_vector.cc @@ -26,18 +26,18 @@ namespace blender::nodes { static void geo_node_align_rotation_to_vector_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Factor"); - b.add_input("Factor", "Factor_001") + b.add_input(N_("Geometry")); + b.add_input(N_("Factor")); + b.add_input(N_("Factor"), "Factor_001") .default_value(1.0f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input("Vector"); - b.add_input("Vector", "Vector_001") + b.add_input(N_("Vector")); + b.add_input(N_("Vector"), "Vector_001") .default_value({0.0, 0.0, 1.0}) .subtype(PROP_ANGLE); - b.add_output("Geometry"); + b.add_output(N_("Geometry")); } static void geo_node_align_rotation_to_vector_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc similarity index 92% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc index 97070184609..91ff114a480 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_clamp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_clamp.cc @@ -24,18 +24,18 @@ namespace blender::nodes { static void geo_node_attribute_clamp_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_input("Min"); - b.add_input("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("Min", "Min_001"); - b.add_input("Max", "Max_001").default_value(1.0f); - b.add_input("Min", "Min_002").min(-100000).max(100000); - b.add_input("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input("Min", "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input("Max", "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_input(N_("Min")); + b.add_input(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("Min"), "Min_001"); + b.add_input(N_("Max"), "Max_001").default_value(1.0f); + b.add_input(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input(N_("Min"), "Min_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("Max"), "Max_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output(N_("Geometry")); } static void geo_node_attribute_clamp_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -269,7 +269,7 @@ void register_node_type_geo_attribute_clamp() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LECAGY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_LEGACY_ATTRIBUTE_CLAMP, "Attribute Clamp", NODE_CLASS_ATTRIBUTE, 0); node_type_init(&ntype, blender::nodes::geo_node_attribute_clamp_init); node_type_update(&ntype, blender::nodes::geo_node_attribute_clamp_update); ntype.declare = blender::nodes::geo_node_attribute_clamp_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc similarity index 96% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc index aa054af3acd..ab4b6aad545 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_color_ramp.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_color_ramp.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_attribute_color_ramp_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_color_ramp_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc similarity index 93% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc index 569d5a824ca..d4c23380b4e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_combine_xyz.cc @@ -23,15 +23,15 @@ namespace blender::nodes { static void geo_node_attribute_combine_xyz_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("X"); - b.add_input("X", "X_001"); - b.add_input("Y"); - b.add_input("Y", "Y_001"); - b.add_input("Z"); - b.add_input("Z", "Z_001"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("X")); + b.add_input(N_("X"), "X_001"); + b.add_input(N_("Y")); + b.add_input(N_("Y"), "Y_001"); + b.add_input(N_("Z")); + b.add_input(N_("Z"), "Z_001"); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_combine_xyz_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc similarity index 95% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc index 0b9708dae14..e4e43a7b724 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_compare.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_compare.cc @@ -25,18 +25,18 @@ namespace blender::nodes { static void geo_node_attribute_compare_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("A", "A_002"); - b.add_input("A", "A_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("B", "B_002"); - b.add_input("B", "B_003").default_value({0.5, 0.5, 0.5, 1.0}); - b.add_input("Threshold").default_value(0.01f).min(0.0f); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("A"), "A_002"); + b.add_input(N_("A"), "A_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("B"), "B_002"); + b.add_input(N_("B"), "B_003").default_value({0.5, 0.5, 0.5, 1.0}); + b.add_input(N_("Threshold")).default_value(0.01f).min(0.0f); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_compare_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc similarity index 97% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc index a2382aa9d25..dc05fa2c125 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_convert.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_convert.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void geo_node_attribute_convert_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_convert_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc similarity index 97% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc index b9621b4ae92..669ac21436f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_curve_map.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_curve_map.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_attribute_curve_map_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_curve_map_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc similarity index 92% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc index 3c50ae5c837..5cb49dd83d0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_fill.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_fill.cc @@ -23,14 +23,14 @@ namespace blender::nodes { static void geo_node_attribute_fill_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Value", "Value"); - b.add_input("Value", "Value_001"); - b.add_input("Value", "Value_002"); - b.add_input("Value", "Value_003"); - b.add_input("Value", "Value_004"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")).is_attribute_name(); + b.add_input(N_("Value"), "Value"); + b.add_input(N_("Value"), "Value_001"); + b.add_input(N_("Value"), "Value_002"); + b.add_input(N_("Value"), "Value_003"); + b.add_input(N_("Value"), "Value_004"); + b.add_output(N_("Geometry")); } static void geo_node_attribute_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc similarity index 95% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc index 0ea3bbe1e45..978c75187fe 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_map_range.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_map_range.cc @@ -26,21 +26,21 @@ namespace blender::nodes { static void geo_node_attribute_map_range_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Result"); - b.add_input("From Min"); - b.add_input("From Max").default_value(1.0f); - b.add_input("To Min"); - b.add_input("To Max").default_value(1.0f); - b.add_input("Steps").default_value(4.0f); - b.add_input("From Min", "From Min_001"); - b.add_input("From Max", "From Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("To Min", "To Min_001"); - b.add_input("To Max", "To Max_001").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("Steps", "Steps_001").default_value({4.0f, 4.0f, 4.0f}); - b.add_input("Clamp"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Result")); + b.add_input(N_("From Min")); + b.add_input(N_("From Max")).default_value(1.0f); + b.add_input(N_("To Min")); + b.add_input(N_("To Max")).default_value(1.0f); + b.add_input(N_("Steps")).default_value(4.0f); + b.add_input(N_("From Min"), "From Min_001"); + b.add_input(N_("From Max"), "From Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("To Min"), "To Min_001"); + b.add_input(N_("To Max"), "To Max_001").default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("Steps"), "Steps_001").default_value({4.0f, 4.0f, 4.0f}); + b.add_input(N_("Clamp")); + b.add_output(N_("Geometry")); } static void fn_attribute_map_range_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc similarity index 96% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc index efa09215b45..55d35f87cda 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_math.cc @@ -29,15 +29,15 @@ namespace blender::nodes { static void geo_node_attribute_math_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("C"); - b.add_input("C", "C_001"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("C")); + b.add_input(N_("C"), "C_001"); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static bool operation_use_input_c(const NodeMathOperation operation) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc similarity index 93% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc index 74e05cb997d..b4205bc91b7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_mix.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_mix.cc @@ -29,23 +29,23 @@ namespace blender::nodes { static void geo_node_mix_attribute_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Factor"); - b.add_input("Factor", "Factor_001") + b.add_input(N_("Geometry")); + b.add_input(N_("Factor")); + b.add_input(N_("Factor"), "Factor_001") .default_value(0.5f) .min(0.0f) .max(1.0f) .subtype(PROP_FACTOR); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("A", "A_002"); - b.add_input("A", "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("B", "B_002"); - b.add_input("B", "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("A"), "A_002"); + b.add_input(N_("A"), "A_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("B"), "B_002"); + b.add_input(N_("B"), "B_003").default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_mix_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc similarity index 96% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc index 0cf411343cf..9e3a7984c53 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_proximity.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_proximity.cc @@ -30,11 +30,11 @@ namespace blender::nodes { static void geo_node_attribute_proximity_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Target"); - b.add_input("Distance"); - b.add_input("Position"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Target")); + b.add_input(N_("Distance")); + b.add_input(N_("Position")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_proximity_layout(uiLayout *layout, @@ -232,7 +232,7 @@ static void geo_node_attribute_proximity_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_attribute_proximity() +void register_node_type_geo_legacy_attribute_proximity() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc similarity index 88% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc index 60b9910399c..2901472d661 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_randomize.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_randomize.cc @@ -25,29 +25,29 @@ namespace blender::nodes { -static void geo_node_attribute_randomize_declare(NodeDeclarationBuilder &b) +static void geo_node_legacy_attribute_randomize_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute"); - b.add_input("Min"); - b.add_input("Max").default_value({1.0f, 1.0f, 1.0f}); - b.add_input("Min", "Min_001"); - b.add_input("Max", "Max_001").default_value(1.0f); - b.add_input("Min", "Min_002").min(-100000).max(100000); - b.add_input("Max", "Max_002").default_value(100).min(-100000).max(100000); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")); + b.add_input(N_("Min")); + b.add_input(N_("Max")).default_value({1.0f, 1.0f, 1.0f}); + b.add_input(N_("Min"), "Min_001"); + b.add_input(N_("Max"), "Max_001").default_value(1.0f); + b.add_input(N_("Min"), "Min_002").min(-100000).max(100000); + b.add_input(N_("Max"), "Max_002").default_value(100).min(-100000).max(100000); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Geometry")); } -static void geo_node_attribute_random_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) +static void geo_node_legacy_attribute_random_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) { uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); uiItemR(layout, ptr, "operation", 0, "", ICON_NONE); } -static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) +static void geo_node_legacy_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *node) { NodeAttributeRandomize *data = (NodeAttributeRandomize *)MEM_callocN( sizeof(NodeAttributeRandomize), __func__); @@ -57,7 +57,7 @@ static void geo_node_attribute_randomize_init(bNodeTree *UNUSED(tree), bNode *no node->storage = data; } -static void geo_node_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) +static void geo_node_legacy_attribute_randomize_update(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sock_min_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 2); bNodeSocket *sock_max_vector = sock_min_vector->next; @@ -280,7 +280,7 @@ static void randomize_attribute_on_component(GeometryComponent &component, attribute.save(); } -static void geo_node_random_attribute_exec(GeoNodeExecParams params) +static void geo_node_legacy_random_attribute_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); const std::string attribute_name = params.get_input("Attribute"); @@ -326,18 +326,18 @@ static void geo_node_random_attribute_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_attribute_randomize() +void register_node_type_geo_legacy_attribute_randomize() { static bNodeType ntype; geo_node_type_base( &ntype, GEO_NODE_LEGACY_ATTRIBUTE_RANDOMIZE, "Attribute Randomize", NODE_CLASS_ATTRIBUTE, 0); - node_type_init(&ntype, blender::nodes::geo_node_attribute_randomize_init); - node_type_update(&ntype, blender::nodes::geo_node_attribute_randomize_update); + node_type_init(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_init); + node_type_update(&ntype, blender::nodes::geo_node_legacy_attribute_randomize_update); - ntype.declare = blender::nodes::geo_node_attribute_randomize_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_random_attribute_exec; - ntype.draw_buttons = blender::nodes::geo_node_attribute_random_layout; + ntype.declare = blender::nodes::geo_node_legacy_attribute_randomize_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_random_attribute_exec; + ntype.draw_buttons = blender::nodes::geo_node_legacy_attribute_random_layout; node_type_storage( &ntype, "NodeAttributeRandomize", node_free_standard_storage, node_copy_standard_storage); nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc similarity index 95% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc index 52f97475941..19d6ced6eb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_sample_texture.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_sample_texture.cc @@ -32,11 +32,11 @@ namespace blender::nodes { static void geo_node_attribute_sample_texture_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Texture").hide_label(); - b.add_input("Mapping"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Texture")).hide_label(); + b.add_input(N_("Mapping")); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static AttributeDomain get_result_domain(const GeometryComponent &component, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc similarity index 95% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc index de0090406c6..809e75e73a3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_separate_xyz.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_separate_xyz.cc @@ -23,13 +23,13 @@ namespace blender::nodes { static void geo_node_attribute_separate_xyz_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Vector"); - b.add_input("Vector", "Vector_001"); - b.add_input("Result X"); - b.add_input("Result Y"); - b.add_input("Result Z"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Vector")); + b.add_input(N_("Vector"), "Vector_001"); + b.add_input(N_("Result X")); + b.add_input(N_("Result Y")); + b.add_input(N_("Result Z")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_separate_xyz_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc similarity index 97% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc index 874350cd714..3a9cd52661a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_transfer.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_transfer.cc @@ -33,11 +33,11 @@ namespace blender::nodes { static void geo_node_attribute_transfer_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Source Geometry"); - b.add_input("Source"); - b.add_input("Destination"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Source Geometry")); + b.add_input(N_("Source")); + b.add_input(N_("Destination")); + b.add_output(N_("Geometry")); } static void geo_node_attribute_transfer_layout(uiLayout *layout, @@ -284,7 +284,8 @@ static void transfer_attribute_nearest_face_interpolated(const GeometrySet &src_ Array positions(tot_samples); get_closest_mesh_looptris(*mesh, dst_positions, looptri_indices, {}, positions); - bke::mesh_surface_sample::MeshAttributeInterpolator interp(mesh, positions, looptri_indices); + bke::mesh_surface_sample::MeshAttributeInterpolator interp( + mesh, IndexMask(tot_samples), positions, looptri_indices); interp.sample_attribute( src_attribute, dst_attribute, bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED); @@ -404,7 +405,7 @@ static void transfer_attribute_nearest(const GeometrySet &src_geometry, data_type); for (const int i : IndexRange(tot_samples)) { if (pointcloud_distances_sq[i] < mesh_distances_sq[i]) { - /* Pointcloud point is closer. */ + /* Point-cloud point is closer. */ const int index = pointcloud_indices[i]; pointcloud_src_attribute.varray->get(index, buffer); dst_attribute->set_by_relocate(i, buffer); @@ -463,12 +464,12 @@ static void transfer_attribute(const GeoNodeExecParams ¶ms, "position", dst_domain, {0, 0, 0}); switch (mapping) { - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { transfer_attribute_nearest_face_interpolated( src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); break; } - case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + case GEO_NODE_LEGACY_ATTRIBUTE_TRANSFER_NEAREST: { transfer_attribute_nearest( src_geometry, dst_component, dst_positions, dst_domain, data_type, src_name, dst_name); break; @@ -511,7 +512,7 @@ static void geo_node_attribute_transfer_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_attribute_transfer() +void register_node_type_geo_legacy_attribute_transfer() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc similarity index 97% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc index 59903050f88..4c351846243 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_math.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_math.cc @@ -30,17 +30,17 @@ namespace blender::nodes { static void geo_node_attribute_vector_math_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("A"); - b.add_input("A", "A_001"); - b.add_input("B"); - b.add_input("B", "B_001"); - b.add_input("B", "B_002"); - b.add_input("C"); - b.add_input("C", "C_001"); - b.add_input("C", "C_002"); - b.add_input("Result"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("A")); + b.add_input(N_("A"), "A_001"); + b.add_input(N_("B")); + b.add_input(N_("B"), "B_001"); + b.add_input(N_("B"), "B_002"); + b.add_input(N_("C")); + b.add_input(N_("C"), "C_001"); + b.add_input(N_("C"), "C_002"); + b.add_input(N_("Result")); + b.add_output(N_("Geometry")); } static bool operation_use_input_b(const NodeVectorMathOperation operation) diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc similarity index 94% rename from source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc index adaa4de3029..9ab8ec25fb6 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_vector_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_attribute_vector_rotate.cc @@ -25,21 +25,21 @@ namespace blender::nodes { static void geo_node_attribute_vector_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Vector"); - b.add_input("Vector", "Vector_001").min(0.0f).max(1.0f).hide_value(); - b.add_input("Center"); - b.add_input("Center", "Center_001").subtype(PROP_XYZ); - b.add_input("Axis"); - b.add_input("Axis", "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); - b.add_input("Angle"); - b.add_input("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input("Rotation"); - b.add_input("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_input("Invert"); - b.add_input("Result"); + b.add_input(N_("Geometry")); + b.add_input(N_("Vector")); + b.add_input(N_("Vector"), "Vector_001").min(0.0f).max(1.0f).hide_value(); + b.add_input(N_("Center")); + b.add_input(N_("Center"), "Center_001").subtype(PROP_XYZ); + b.add_input(N_("Axis")); + b.add_input(N_("Axis"), "Axis_001").min(-1.0f).max(1.0f).subtype(PROP_XYZ); + b.add_input(N_("Angle")); + b.add_input(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input(N_("Rotation")); + b.add_input(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_input(N_("Invert")); + b.add_input(N_("Result")); - b.add_output("Geometry"); + b.add_output(N_("Geometry")); } static void geo_node_attribute_vector_rotate_layout(uiLayout *layout, @@ -332,7 +332,7 @@ void register_node_type_geo_attribute_vector_rotate() static bNodeType ntype; geo_node_type_base(&ntype, - GEO_NODE_ATTRIBUTE_VECTOR_ROTATE, + GEO_NODE_LEGACY_ATTRIBUTE_VECTOR_ROTATE, "Attribute Vector Rotate", NODE_CLASS_ATTRIBUTE, 0); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc similarity index 94% rename from source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc index 7853c5aa04a..8b81008ff34 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoints.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_endpoints.cc @@ -29,9 +29,9 @@ namespace blender::nodes { static void geo_node_curve_endpoints_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Start Points"); - b.add_output("End Points"); + b.add_input(N_("Geometry")); + b.add_output(N_("Start Points")); + b.add_output(N_("End Points")); } /** @@ -124,7 +124,7 @@ static void copy_endpoint_attributes(Span splines, end_data.tilts[i] = spline.tilts().last(); /* Copy the point attribute data over. */ - for (const auto &item : start_data.point_attributes.items()) { + for (const auto item : start_data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; @@ -133,7 +133,7 @@ static void copy_endpoint_attributes(Span splines, blender::fn::GVArray_For_GSpan(spline_span).get(0, point_span[i]); } - for (const auto &item : end_data.point_attributes.items()) { + for (const auto item : end_data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; GMutableSpan point_span = item.value; @@ -208,11 +208,12 @@ static void geo_node_curve_endpoints_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_curve_endpoints() +void register_node_type_geo_legacy_curve_endpoints() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_ENDPOINTS, "Curve Endpoints", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_endpoints_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoints_exec; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc new file mode 100644 index 00000000000..ba76fafe3e6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_reverse.cc @@ -0,0 +1,71 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); +} + +static void geo_node_curve_reverse_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + /* Retrieve data for write access so we can avoid new allocations for the reversed data. */ + CurveComponent &curve_component = geometry_set.get_component_for_write(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan splines = curve.splines(); + + const std::string selection_name = params.extract_input("Selection"); + GVArray_Typed selection = curve_component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_CURVE, true); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + if (selection[i]) { + splines[i]->reverse(); + } + } + }); + + params.set_output("Curve", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_curve_reverse() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_reverse_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc similarity index 96% rename from source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc index dfcae2e65b0..40d827ae141 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_select_by_handle_type.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_select_by_handle_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_select_by_handle_type_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")); + b.add_output(N_("Geometry")); } static void geo_node_curve_select_by_handle_type_layout(uiLayout *layout, @@ -123,7 +123,7 @@ static void geo_node_select_by_handle_type_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_select_by_handle_type() +void register_node_type_geo_legacy_select_by_handle_type() { static bNodeType ntype; diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc new file mode 100644 index 00000000000..4bac9cb976e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_set_handles.cc @@ -0,0 +1,144 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); +} + +static void geo_node_curve_set_handles_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSetHandles *data = (NodeGeometryCurveSetHandles *)MEM_callocN( + sizeof(NodeGeometryCurveSetHandles), __func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSetHandles *node_storage = + (NodeGeometryCurveSetHandles *)params.node().storage; + const GeometryNodeCurveHandleType type = (GeometryNodeCurveHandleType)node_storage->handle_type; + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; + + GeometrySet geometry_set = params.extract_input("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + /* Retrieve data for write access so we can avoid new allocations for the handles data. */ + CurveComponent &curve_component = geometry_set.get_component_for_write(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan splines = curve.splines(); + + const std::string selection_name = params.extract_input("Selection"); + GVArray_Typed selection = curve_component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_POINT, true); + + const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + int point_index = 0; + bool has_bezier_spline = false; + for (SplinePtr &spline : splines) { + if (spline->type() != Spline::Type::Bezier) { + point_index += spline->positions().size(); + continue; + } + + BezierSpline &bezier_spline = static_cast(*spline); + if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + /* In this case the automatically calculated handle types need to be "baked", because + * they're possibly changing from a type that is calculated automatically to a type that + * is positioned manually. */ + bezier_spline.ensure_auto_handles(); + } + has_bezier_spline = true; + for (int i_point : IndexRange(bezier_spline.size())) { + if (selection[point_index]) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier_spline.handle_types_left()[i_point] = new_handle_type; + } + if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { + bezier_spline.handle_types_right()[i_point] = new_handle_type; + } + } + point_index++; + } + bezier_spline.mark_cache_invalid(); + } + + if (!has_bezier_spline) { + params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); + } + + params.set_output("Curve", geometry_set); +} +} // namespace blender::nodes + +void register_node_type_geo_legacy_curve_set_handles() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init); + node_type_storage(&ntype, + "NodeGeometryCurveSetHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_curve_set_handles_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc new file mode 100644 index 00000000000..df53c96e6ca --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_spline_type.cc @@ -0,0 +1,302 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_legacy_curve_spline_type_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); +} + +static void geo_node_legacy_curve_spline_type_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "spline_type", 0, "", ICON_NONE); +} + +static void geo_node_legacy_curve_spline_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSplineType *data = (NodeGeometryCurveSplineType *)MEM_callocN( + sizeof(NodeGeometryCurveSplineType), __func__); + + data->spline_type = GEO_NODE_SPLINE_TYPE_POLY; + node->storage = data; +} + +template +static void scale_input_assign(const Span input, + const int scale, + const int offset, + const MutableSpan r_output) +{ + for (const int i : IndexRange(r_output.size())) { + r_output[i] = input[i * scale + offset]; + } +} + +template +static void scale_output_assign(const Span input, + const int scale, + const int offset, + const MutableSpan &r_output) +{ + for (const int i : IndexRange(input.size())) { + r_output[i * scale + offset] = input[i]; + } +} + +template +static void copy_attributes(const Spline &input_spline, Spline &output_spline, CopyFn copy_fn) +{ + input_spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = input_spline.attributes.get_for_read(attribute_id); + BLI_assert(src); + if (!output_spline.attributes.create(attribute_id, meta_data.data_type)) { + BLI_assert_unreachable(); + return false; + } + std::optional dst = output_spline.attributes.get_for_write(attribute_id); + if (!dst) { + BLI_assert_unreachable(); + return false; + } + + copy_fn(*src, *dst); + + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr convert_to_poly_spline(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr poly_to_nurbs(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.positions().size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + Spline::copy_base_settings(input, *output); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes = input.attributes; + return output; +} + +static SplinePtr bezier_to_nurbs(const Spline &input) +{ + const BezierSpline &bezier_spline = static_cast(input); + std::unique_ptr output = std::make_unique(); + output->resize(input.size() * 3); + + scale_output_assign(bezier_spline.handle_positions_left(), 3, 0, output->positions()); + scale_output_assign(input.radii(), 3, 0, output->radii()); + scale_output_assign(input.tilts(), 3, 0, output->tilts()); + + scale_output_assign(bezier_spline.positions(), 3, 1, output->positions()); + scale_output_assign(input.radii(), 3, 1, output->radii()); + scale_output_assign(input.tilts(), 3, 1, output->tilts()); + + scale_output_assign(bezier_spline.handle_positions_right(), 3, 2, output->positions()); + scale_output_assign(input.radii(), 3, 2, output->radii()); + scale_output_assign(input.tilts(), 3, 2, output->tilts()); + + Spline::copy_base_settings(input, *output); + output->weights().fill(1.0f); + output->set_resolution(12); + output->set_order(4); + output->set_cyclic(input.is_cyclic()); + output->knots_mode = NURBSpline::KnotsMode::Bezier; + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_output_assign(src.typed(), 3, 0, dst.typed()); + scale_output_assign(src.typed(), 3, 1, dst.typed()); + scale_output_assign(src.typed(), 3, 2, dst.typed()); + }); + }); + return output; +} + +static SplinePtr poly_to_bezier(const Spline &input) +{ + std::unique_ptr output = std::make_unique(); + output->resize(input.size()); + output->positions().copy_from(input.positions()); + output->radii().copy_from(input.radii()); + output->tilts().copy_from(input.tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Vector); + output->handle_types_right().fill(BezierSpline::HandleType::Vector); + output->set_resolution(12); + Spline::copy_base_settings(input, *output); + output->attributes = input.attributes; + return output; +} + +static SplinePtr nurbs_to_bezier(const Spline &input) +{ + const NURBSpline &nurbs_spline = static_cast(input); + std::unique_ptr output = std::make_unique(); + output->resize(input.size() / 3); + scale_input_assign(input.positions(), 3, 1, output->positions()); + scale_input_assign(input.positions(), 3, 0, output->handle_positions_left()); + scale_input_assign(input.positions(), 3, 2, output->handle_positions_right()); + scale_input_assign(input.radii(), 3, 2, output->radii()); + scale_input_assign(input.tilts(), 3, 2, output->tilts()); + output->handle_types_left().fill(BezierSpline::HandleType::Align); + output->handle_types_right().fill(BezierSpline::HandleType::Align); + output->set_resolution(nurbs_spline.resolution()); + Spline::copy_base_settings(input, *output); + output->attributes.reallocate(output->size()); + copy_attributes(input, *output, [](GSpan src, GMutableSpan dst) { + attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { + using T = decltype(dummy); + scale_input_assign(src.typed(), 3, 1, dst.typed()); + }); + }); + return output; +} + +static SplinePtr convert_to_bezier(const Spline &input, GeoNodeExecParams params) +{ + switch (input.type()) { + case Spline::Type::Bezier: + return input.copy(); + case Spline::Type::Poly: + return poly_to_bezier(input); + case Spline::Type::NURBS: + if (input.size() < 6) { + params.error_message_add( + NodeWarningType::Info, + TIP_("NURBS must have minimum of 6 points for Bezier Conversion")); + return input.copy(); + } + else { + if (input.size() % 3 != 0) { + params.error_message_add(NodeWarningType::Info, + TIP_("NURBS must have multiples of 3 points for full Bezier " + "conversion, curve truncated")); + } + return nurbs_to_bezier(input); + } + } + BLI_assert_unreachable(); + return {}; +} + +static SplinePtr convert_to_nurbs(const Spline &input) +{ + switch (input.type()) { + case Spline::Type::NURBS: + return input.copy(); + case Spline::Type::Bezier: + return bezier_to_nurbs(input); + case Spline::Type::Poly: + return poly_to_nurbs(input); + } + BLI_assert_unreachable(); + return {}; +} + +static void geo_node_legacy_curve_spline_type_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSplineType *storage = + (const NodeGeometryCurveSplineType *)params.node().storage; + const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type; + + GeometrySet geometry_set = params.extract_input("Curve"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + if (!geometry_set.has_curve()) { + params.set_output("Curve", geometry_set); + return; + } + + const CurveComponent *curve_component = geometry_set.get_component_for_read(); + const CurveEval &curve = *curve_component->get_for_read(); + + const std::string selection_name = params.extract_input("Selection"); + GVArray_Typed selection = curve_component->attribute_get_for_read( + selection_name, ATTR_DOMAIN_CURVE, true); + + std::unique_ptr new_curve = std::make_unique(); + for (const int i : curve.splines().index_range()) { + if (selection[i]) { + switch (output_type) { + case GEO_NODE_SPLINE_TYPE_POLY: + new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); + break; + case GEO_NODE_SPLINE_TYPE_BEZIER: + new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); + break; + case GEO_NODE_SPLINE_TYPE_NURBS: + new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); + break; + } + } + else { + new_curve->add_spline(curve.splines()[i]->copy()); + } + } + + new_curve->attributes = curve.attributes; + params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_curve_spline_type() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_curve_spline_type_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_curve_spline_type_exec; + node_type_init(&ntype, blender::nodes::geo_node_legacy_curve_spline_type_init); + node_type_storage(&ntype, + "NodeGeometryCurveSplineType", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_legacy_curve_spline_type_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc new file mode 100644 index 00000000000..f9b0a9d128e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_subdivide.cc @@ -0,0 +1,396 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::fn::GVArray_For_GSpan; +using blender::fn::GVArray_For_Span; +using blender::fn::GVArray_Typed; + +namespace blender::nodes { + +static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Cuts")); + b.add_input(N_("Cuts"), "Cuts_001").default_value(1).min(0).max(1000); + b.add_output(N_("Geometry")); +} + +static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE); +} + +static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN( + sizeof(NodeGeometryCurveSubdivide), __func__); + + data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER; + node->storage = data; +} + +static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; + + update_attribute_input_socket_availabilities( + *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type); +} + +static Array get_subdivided_offsets(const Spline &spline, + const VArray &cuts, + const int spline_offset) +{ + Array offsets(spline.segments_size() + 1); + int offset = 0; + for (const int i : IndexRange(spline.segments_size())) { + offsets[i] = offset; + offset = offset + std::max(cuts[spline_offset + i], 0) + 1; + } + offsets.last() = offset; + return offsets; +} + +template +static void subdivide_attribute(Span src, + const Span offsets, + const bool is_cyclic, + MutableSpan dst) +{ + const int src_size = src.size(); + threading::parallel_for(IndexRange(src_size - 1), 1024, [&](IndexRange range) { + for (const int i : range) { + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src[i]; + const float factor_delta = 1.0f / (cuts + 1.0f); + for (const int cut : IndexRange(cuts)) { + const float factor = (cut + 1) * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); + } + } + }); + + if (is_cyclic) { + const int i = src_size - 1; + const int cuts = offsets[i + 1] - offsets[i]; + dst[offsets[i]] = src.last(); + const float factor_delta = 1.0f / (cuts + 1.0f); + for (const int cut : IndexRange(cuts)) { + const float factor = (cut + 1) * factor_delta; + dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); + } + } + else { + dst.last() = src.last(); + } +} + +/** + * In order to generate a Bezier spline with the same shape as the input spline, apply the + * De Casteljau algorithm iteratively for the provided number of cuts, constantly updating the + * previous result point's right handle and the left handle at the end of the segment. + * + * \note Non-vector segments in the result spline are given free handles. This could possibly be + * improved with another pass that sets handles to aligned where possible, but currently that does + * not provide much benefit for the increased complexity. + */ +static void subdivide_bezier_segment(const BezierSpline &src, + const int index, + const int offset, + const int result_size, + Span src_positions, + Span src_handles_left, + Span src_handles_right, + MutableSpan dst_positions, + MutableSpan dst_handles_left, + MutableSpan dst_handles_right, + MutableSpan dst_type_left, + MutableSpan dst_type_right) +{ + const bool is_last_cyclic_segment = index == (src.size() - 1); + const int next_index = is_last_cyclic_segment ? 0 : index + 1; + + /* The first point in the segment is always copied. */ + dst_positions[offset] = src_positions[index]; + + if (src.segment_is_vector(index)) { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Vector; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Vector); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Vector); + + const float factor_delta = 1.0f / result_size; + for (const int cut : IndexRange(result_size)) { + const float factor = cut * factor_delta; + dst_positions[offset + cut] = attribute_math::mix2( + factor, src_positions[index], src_positions[next_index]); + } + } + else { + if (is_last_cyclic_segment) { + dst_type_left.first() = BezierSpline::HandleType::Free; + } + dst_type_left.slice(offset + 1, result_size).fill(BezierSpline::HandleType::Free); + dst_type_right.slice(offset, result_size).fill(BezierSpline::HandleType::Free); + + const int i_segment_last = is_last_cyclic_segment ? 0 : offset + result_size; + + /* Create a Bezier segment to update iteratively for every subdivision + * and references to the meaningful values for ease of use. */ + BezierSpline temp; + temp.resize(2); + float3 &segment_start = temp.positions().first(); + float3 &segment_end = temp.positions().last(); + float3 &handle_prev = temp.handle_positions_right().first(); + float3 &handle_next = temp.handle_positions_left().last(); + segment_start = src_positions[index]; + segment_end = src_positions[next_index]; + handle_prev = src_handles_right[index]; + handle_next = src_handles_left[next_index]; + + for (const int cut : IndexRange(result_size - 1)) { + const float parameter = 1.0f / (result_size - cut); + const BezierSpline::InsertResult insert = temp.calculate_segment_insertion(0, 1, parameter); + + /* Copy relevant temporary data to the result. */ + dst_handles_right[offset + cut] = insert.handle_prev; + dst_handles_left[offset + cut + 1] = insert.left_handle; + dst_positions[offset + cut + 1] = insert.position; + + /* Update the segment to prepare it for the next subdivision. */ + segment_start = insert.position; + handle_prev = insert.right_handle; + handle_next = insert.handle_next; + } + + /* Copy the handles for the last segment from the temporary spline. */ + dst_handles_right[offset + result_size - 1] = handle_prev; + dst_handles_left[i_segment_last] = handle_next; + } +} + +static void subdivide_bezier_spline(const BezierSpline &src, + const Span offsets, + BezierSpline &dst) +{ + Span src_positions = src.positions(); + Span src_handles_left = src.handle_positions_left(); + Span src_handles_right = src.handle_positions_right(); + MutableSpan dst_positions = dst.positions(); + MutableSpan dst_handles_left = dst.handle_positions_left(); + MutableSpan dst_handles_right = dst.handle_positions_right(); + MutableSpan dst_type_left = dst.handle_types_left(); + MutableSpan dst_type_right = dst.handle_types_right(); + + threading::parallel_for(IndexRange(src.size() - 1), 512, [&](IndexRange range) { + for (const int i : range) { + subdivide_bezier_segment(src, + i, + offsets[i], + offsets[i + 1] - offsets[i], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + }); + + if (src.is_cyclic()) { + const int i_last = src.size() - 1; + subdivide_bezier_segment(src, + i_last, + offsets[i_last], + offsets.last() - offsets[i_last], + src_positions, + src_handles_left, + src_handles_right, + dst_positions, + dst_handles_left, + dst_handles_right, + dst_type_left, + dst_type_right); + } + else { + dst_positions.last() = src_positions.last(); + } +} + +static void subdivide_builtin_attributes(const Spline &src_spline, + const Span offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + subdivide_attribute(src_spline.radii(), offsets, is_cyclic, dst_spline.radii()); + subdivide_attribute(src_spline.tilts(), offsets, is_cyclic, dst_spline.tilts()); + switch (src_spline.type()) { + case Spline::Type::Poly: { + const PolySpline &src = static_cast(src_spline); + PolySpline &dst = static_cast(dst_spline); + subdivide_attribute(src.positions(), offsets, is_cyclic, dst.positions()); + break; + } + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast(src_spline); + BezierSpline &dst = static_cast(dst_spline); + subdivide_bezier_spline(src, offsets, dst); + dst.mark_cache_invalid(); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast(src_spline); + NURBSpline &dst = static_cast(dst_spline); + subdivide_attribute(src.positions(), offsets, is_cyclic, dst.positions()); + subdivide_attribute(src.weights(), offsets, is_cyclic, dst.weights()); + break; + } + } +} + +static void subdivide_dynamic_attributes(const Spline &src_spline, + const Span offsets, + Spline &dst_spline) +{ + const bool is_cyclic = src_spline.is_cyclic(); + src_spline.attributes.foreach_attribute( + [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src = src_spline.attributes.get_for_read(attribute_id); + BLI_assert(src); + + if (!dst_spline.attributes.create(attribute_id, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. */ + BLI_assert_unreachable(); + } + + std::optional dst = dst_spline.attributes.get_for_write(attribute_id); + BLI_assert(dst); + + attribute_math::convert_to_static_type(dst->type(), [&](auto dummy) { + using T = decltype(dummy); + subdivide_attribute(src->typed(), offsets, is_cyclic, dst->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr subdivide_spline(const Spline &spline, + const VArray &cuts, + const int spline_offset) +{ + if (spline.size() <= 1) { + return spline.copy(); + } + + /* Since we expect to access each value many times, it should be worth it to make sure count + * of cuts is a real span (especially considering the note below). Using the offset at each + * point facilitates subdividing in parallel later. */ + Array offsets = get_subdivided_offsets(spline, cuts, spline_offset); + const int result_size = offsets.last() + int(!spline.is_cyclic()); + SplinePtr new_spline = spline.copy_only_settings(); + new_spline->resize(result_size); + subdivide_builtin_attributes(spline, offsets, *new_spline); + subdivide_dynamic_attributes(spline, offsets, *new_spline); + return new_spline; +} + +/** + * \note Passing the virtual array for the entire spline is possibly quite inefficient here when + * the attribute was on the point domain and stored separately for each spline already, and it + * prevents some other optimizations like skipping splines with a single attribute value of < 1. + * However, it allows the node to access builtin attribute easily, so it the makes most sense this + * way until the attribute API is refactored. + */ +static std::unique_ptr subdivide_curve(const CurveEval &input_curve, + const VArray &cuts) +{ + const Array control_point_offsets = input_curve.control_point_offsets(); + const Span input_splines = input_curve.splines(); + + std::unique_ptr output_curve = std::make_unique(); + output_curve->resize(input_splines.size()); + output_curve->attributes = input_curve.attributes; + MutableSpan output_splines = output_curve->splines(); + + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = subdivide_spline(*input_splines[i], cuts, control_point_offsets[i]); + } + }); + + return output_curve; +} + +static void geo_node_subdivide_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Geometry", geometry_set); + return; + } + + const CurveComponent &component = *geometry_set.get_component_for_read(); + GVArray_Typed cuts = params.get_input_attribute( + "Cuts", component, ATTR_DOMAIN_POINT, 0); + if (cuts->is_single() && cuts->get_internal_single() < 1) { + params.set_output("Geometry", geometry_set); + return; + } + + std::unique_ptr output_curve = subdivide_curve(*component.get_for_read(), *cuts); + + params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_curve_subdivide() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; + ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout; + node_type_storage(&ntype, + "NodeGeometryCurveSubdivide", + node_free_standard_storage, + node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update); + ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc new file mode 100644 index 00000000000..c171d485a6a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_curve_to_points.cc @@ -0,0 +1,360 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Count")).default_value(10).min(2).max(100000); + b.add_input(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output(N_("Geometry")); +} + +static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( + sizeof(NodeGeometryCurveToPoints), __func__); + + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; + node->storage = data; +} + +static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + + bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length_socket = count_socket->next; + + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); +} + +/** + * Evaluate splines in parallel to speed up the rest of the node's execution. + */ +static void evaluate_splines(Span splines) +{ + threading::parallel_for_each(splines, [](const SplinePtr &spline) { + /* These functions fill the corresponding caches on each spline. */ + spline->evaluated_positions(); + spline->evaluated_tangents(); + spline->evaluated_normals(); + spline->evaluated_lengths(); + }); +} + +static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, + const GeometryNodeCurveResampleMode mode, + const CurveEval &curve, + const Span splines) +{ + const int size = curve.splines().size(); + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + const int count = params.extract_input("Count"); + if (count < 1) { + return {0}; + } + Array offsets(size + 1); + for (const int i : offsets.index_range()) { + offsets[i] = count * i; + } + return offsets; + } + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { + /* Don't allow asymptotic count increase for low resolution values. */ + const float resolution = std::max(params.extract_input("Length"), 0.0001f); + Array offsets(size + 1); + int offset = 0; + for (const int i : IndexRange(size)) { + offsets[i] = offset; + offset += splines[i]->length() / resolution + 1; + } + offsets.last() = offset; + return offsets; + } + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { + return curve.evaluated_point_offsets(); + } + } + BLI_assert_unreachable(); + return {0}; +} + +static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, + const AttributeIDRef &attribute_id, + const CustomDataType data_type) +{ + points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); + WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); + BLI_assert(attribute); + return attribute.varray->get_internal_span(); +} + +template +static MutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, + const AttributeIDRef &attribute_id) +{ + GMutableSpan attribute = create_attribute_and_retrieve_span( + points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get())); + return attribute.typed(); +} + +CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, + const CurveEval &curve) +{ + CurveToPointsResults attributes; + + attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT); + + attributes.positions = create_attribute_and_retrieve_span(points, "position"); + attributes.radii = create_attribute_and_retrieve_span(points, "radius"); + attributes.tilts = create_attribute_and_retrieve_span(points, "tilt"); + + /* Because of the invariants of the curve component, we use the attributes of the + * first spline as a representative for the attribute meta data all splines. */ + curve.splines().first()->attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + attributes.point_attributes.add_new( + attribute_id, + create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type)); + return true; + }, + ATTR_DOMAIN_POINT); + + attributes.tangents = create_attribute_and_retrieve_span(points, "tangent"); + attributes.normals = create_attribute_and_retrieve_span(points, "normal"); + attributes.rotations = create_attribute_and_retrieve_span(points, "rotation"); + + return attributes; +} + +/** + * TODO: For non-poly splines, this has double copies that could be avoided as part + * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. + */ +static void copy_evaluated_point_attributes(Span splines, + Span offsets, + CurveToPointsResults &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + + data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); + spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); + spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size)); + + for (const Map::Item item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; + GMutableSpan point_span = item.value; + + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); + + spline.interpolate_to_evaluated(spline_span) + ->materialize(point_span.slice(offset, size).data()); + } + + data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); + data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); + } + }); +} + +static void copy_uniform_sample_point_attributes(Span splines, + Span offsets, + CurveToPointsResults &data) +{ + threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size == 0) { + continue; + } + + const Array uniform_samples = spline.sample_uniform_index_factors(size); + + spline.sample_with_index_factors( + spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); + + spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline.radii()), + uniform_samples, + data.radii.slice(offset, size)); + + spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline.tilts()), + uniform_samples, + data.tilts.slice(offset, size)); + + for (const Map::Item item : data.point_attributes.items()) { + const AttributeIDRef attribute_id = item.key; + GMutableSpan point_span = item.value; + + BLI_assert(spline.attributes.get_for_read(attribute_id)); + GSpan spline_span = *spline.attributes.get_for_read(attribute_id); + + spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span), + uniform_samples, + point_span.slice(offset, size)); + } + + spline.sample_with_index_factors( + spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); + for (float3 &tangent : data.tangents) { + tangent.normalize(); + } + + spline.sample_with_index_factors( + spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); + for (float3 &normals : data.normals) { + normals.normalize(); + } + } + }); +} + +/** + * \note Use attributes from the curve component rather than the attribute data directly on the + * attribute storage to allow reading the virtual spline attributes like "cyclic" and "resolution". + */ +static void copy_spline_domain_attributes(const CurveComponent &curve_component, + Span offsets, + PointCloudComponent &points) +{ + curve_component.attribute_foreach( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + if (meta_data.domain != ATTR_DOMAIN_CURVE) { + return true; + } + GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( + attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); + const CPPType &type = spline_attribute->type(); + + OutputAttribute result_attribute = points.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); + GMutableSpan result = result_attribute.as_span(); + + for (const int i : IndexRange(spline_attribute->size())) { + const int offset = offsets[i]; + const int size = offsets[i + 1] - offsets[i]; + if (size != 0) { + BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); + spline_attribute->get(i, buffer); + type.fill_assign_n(buffer, result[offset], size); + } + } + + result_attribute.save(); + return true; + }); +} + +static void geo_node_curve_to_points_exec(GeoNodeExecParams params) +{ + NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_curve()) { + params.set_output("Geometry", GeometrySet()); + return; + } + + const CurveComponent &curve_component = *geometry_set.get_component_for_read(); + const CurveEval &curve = *curve_component.get_for_read(); + const Span splines = curve.splines(); + curve.assert_valid_point_attributes(); + + evaluate_splines(splines); + + const Array offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const int total_size = offsets.last(); + if (total_size == 0) { + params.set_output("Geometry", GeometrySet()); + return; + } + + GeometrySet result = GeometrySet::create_with_pointcloud(BKE_pointcloud_new_nomain(total_size)); + PointCloudComponent &point_component = result.get_component_for_write(); + + CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, + curve); + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_LENGTH: + copy_uniform_sample_point_attributes(splines, offsets, new_attributes); + break; + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + copy_evaluated_point_attributes(splines, offsets, new_attributes); + break; + } + + copy_spline_domain_attributes(curve_component, offsets, point_component); + curve_create_default_rotation_attribute( + new_attributes.tangents, new_attributes.normals, new_attributes.rotations); + + /* The default radius is way too large for points, divide by 10. */ + for (float &radius : new_attributes.radii) { + radius *= 0.1f; + } + + params.set_output("Geometry", std::move(result)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_curve_to_points() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_CURVE_TO_POINTS, "Curve to Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_to_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_to_points_exec; + ntype.draw_buttons = blender::nodes::geo_node_curve_to_points_layout; + node_type_storage( + &ntype, "NodeGeometryCurveToPoints", node_free_standard_storage, node_copy_standard_storage); + node_type_init(&ntype, blender::nodes::geo_node_curve_to_points_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_to_points_update); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc new file mode 100644 index 00000000000..1d76a0532a1 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_delete_geometry.cc @@ -0,0 +1,677 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_array.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_customdata.h" +#include "BKE_mesh.h" +#include "BKE_pointcloud.h" +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +using blender::bke::CustomDataAttributes; + +/* Code from the mask modifier in MOD_mask.cc. */ +extern void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map); +extern void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map, + blender::Span edge_map); +extern void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + blender::Span vertex_map, + blender::Span edge_map, + blender::Span masked_poly_indices, + blender::Span new_loop_starts); + +namespace blender::nodes { + +static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")); + b.add_input(N_("Invert")); + b.add_output(N_("Geometry")); +} + +template static void copy_data(Span data, MutableSpan r_data, IndexMask mask) +{ + for (const int i_out : mask.index_range()) { + r_data[i_out] = data[mask[i_out]]; + } +} + +static void spline_copy_builtin_attributes(const Spline &spline, + Spline &r_spline, + const IndexMask mask) +{ + copy_data(spline.positions(), r_spline.positions(), mask); + copy_data(spline.radii(), r_spline.radii(), mask); + copy_data(spline.tilts(), r_spline.tilts(), mask); + switch (spline.type()) { + case Spline::Type::Poly: + break; + case Spline::Type::Bezier: { + const BezierSpline &src = static_cast(spline); + BezierSpline &dst = static_cast(r_spline); + copy_data(src.handle_positions_left(), dst.handle_positions_left(), mask); + copy_data(src.handle_positions_right(), dst.handle_positions_right(), mask); + copy_data(src.handle_types_left(), dst.handle_types_left(), mask); + copy_data(src.handle_types_right(), dst.handle_types_right(), mask); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src = static_cast(spline); + NURBSpline &dst = static_cast(r_spline); + copy_data(src.weights(), dst.weights(), mask); + break; + } + } +} + +static void copy_dynamic_attributes(const CustomDataAttributes &src, + CustomDataAttributes &dst, + const IndexMask mask) +{ + src.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src_attribute = src.get_for_read(attribute_id); + BLI_assert(src_attribute); + + if (!dst.create(attribute_id, meta_data.data_type)) { + /* Since the source spline of the same type had the attribute, adding it should work. + */ + BLI_assert_unreachable(); + } + + std::optional new_attribute = dst.get_for_write(attribute_id); + BLI_assert(new_attribute); + + attribute_math::convert_to_static_type(new_attribute->type(), [&](auto dummy) { + using T = decltype(dummy); + copy_data(src_attribute->typed(), new_attribute->typed(), mask); + }); + return true; + }, + ATTR_DOMAIN_POINT); +} + +static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) +{ + SplinePtr new_spline = spline.copy_only_settings(); + new_spline->resize(mask.size()); + + spline_copy_builtin_attributes(spline, *new_spline, mask); + copy_dynamic_attributes(spline.attributes, new_spline->attributes, mask); + + return new_spline; +} + +static std::unique_ptr curve_delete(const CurveEval &input_curve, + const StringRef name, + const bool invert) +{ + Span input_splines = input_curve.splines(); + std::unique_ptr output_curve = std::make_unique(); + + /* Keep track of which splines were copied to the result to copy spline domain attributes. */ + Vector copied_splines; + + if (input_curve.attributes.get_for_read(name)) { + GVArray_Typed selection = input_curve.attributes.get_for_read(name, false); + for (const int i : input_splines.index_range()) { + if (selection[i] == invert) { + output_curve->add_spline(input_splines[i]->copy()); + copied_splines.append(i); + } + } + } + else { + /* Reuse index vector for each spline. */ + Vector indices_to_copy; + + for (const int i : input_splines.index_range()) { + const Spline &spline = *input_splines[i]; + GVArray_Typed selection = spline.attributes.get_for_read(name, false); + + indices_to_copy.clear(); + for (const int i_point : IndexRange(spline.size())) { + if (selection[i_point] == invert) { + indices_to_copy.append(i_point); + } + } + + /* Avoid creating an empty spline. */ + if (indices_to_copy.is_empty()) { + continue; + } + + SplinePtr new_spline = spline_delete(spline, IndexMask(indices_to_copy)); + output_curve->add_spline(std::move(new_spline)); + copied_splines.append(i); + } + } + + if (copied_splines.is_empty()) { + return {}; + } + + output_curve->attributes.reallocate(output_curve->splines().size()); + copy_dynamic_attributes( + input_curve.attributes, output_curve->attributes, IndexMask(copied_splines)); + + return output_curve; +} + +static void delete_curve_selection(const CurveComponent &in_component, + CurveComponent &r_component, + const StringRef selection_name, + const bool invert) +{ + std::unique_ptr r_curve = curve_delete( + *in_component.get_for_read(), selection_name, invert); + if (r_curve) { + r_component.replace(r_curve.release()); + } + else { + r_component.clear(); + } +} + +static void delete_point_cloud_selection(const PointCloudComponent &in_component, + PointCloudComponent &out_component, + const StringRef selection_name, + const bool invert) +{ + const GVArray_Typed selection_attribute = in_component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_POINT, false); + VArray_Span selection{selection_attribute}; + + const int total = selection.count(invert); + if (total == 0) { + out_component.clear(); + return; + } + out_component.replace(BKE_pointcloud_new_nomain(total)); + + /* Invert the inversion, because this deletes the selected points instead of keeping them. */ + copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert); +} + +static void compute_selected_vertices_from_vertex_selection(const VArray &vertex_selection, + const bool invert, + MutableSpan r_vertex_map, + uint *r_num_selected_vertices) +{ + BLI_assert(vertex_selection.size() == r_vertex_map.size()); + + uint num_selected_vertices = 0; + for (const int i : r_vertex_map.index_range()) { + if (vertex_selection[i] != invert) { + r_vertex_map[i] = num_selected_vertices; + num_selected_vertices++; + } + else { + r_vertex_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; +} + +static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, + const VArray &vertex_selection, + const bool invert, + MutableSpan r_edge_map, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == r_edge_map.size()); + + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + + /* Only add the edge if both vertices will be in the new mesh. */ + if (vertex_selection[edge.v1] != invert && vertex_selection[edge.v2] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + +static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, + const VArray &vertex_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totvert == vertex_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_verts_in_selection = true; + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (vertex_selection[loop.v] == invert) { + all_verts_in_selection = false; + break; + } + } + + if (all_verts_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, then the two vertices of the + * edge are kept along with the edge. + */ +static void compute_selected_vertices_and_edges_from_edge_selection( + const Mesh &mesh, + const VArray &edge_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + uint *r_num_selected_vertices, + uint *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + uint num_selected_edges = 0; + uint num_selected_vertices = 0; + for (const int i : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[i]; + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + if (r_vertex_map[edge.v1] == -1) { + r_vertex_map[edge.v1] = num_selected_vertices; + num_selected_vertices++; + } + if (r_vertex_map[edge.v2] == -1) { + r_vertex_map[edge.v2] = num_selected_vertices; + num_selected_vertices++; + } + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; +} + +/** + * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that + * polygon is kept. + */ +static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, + const VArray &edge_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + + bool all_edges_in_selection = true; + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + if (edge_selection[loop.e] == invert) { + all_edges_in_selection = false; + break; + } + } + + if (all_edges_in_selection) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +/** + * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all + * vertices of that polygon or edge are in the selection. + */ +static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, + const VArray &vertex_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + compute_selected_vertices_from_vertex_selection( + vertex_selection, invert, r_vertex_map, r_num_selected_vertices); + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to + * that edge are kept as well. The polygons are kept if all edges are in the selection. + */ +static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, + const VArray &edge_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + r_vertex_map.fill(-1); + compute_selected_vertices_and_edges_from_edge_selection(mesh, + edge_selection, + invert, + r_vertex_map, + r_edge_map, + r_num_selected_vertices, + r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, + const VArray &poly_selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_vertex_map.fill(-1); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + uint num_selected_loops = 0; + uint num_selected_vertices = 0; + uint num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_vertex_map[loop.v] == -1) { + r_vertex_map[loop.v] = num_selected_vertices; + num_selected_vertices++; + } + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_vertices = num_selected_vertices; + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + +using FillMapsFunction = void (*)(const Mesh &mesh, + const VArray &selection, + const bool invert, + MutableSpan r_vertex_map, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + uint *r_num_selected_vertices, + uint *r_num_selected_edges, + uint *r_num_selected_polys, + uint *r_num_selected_loops); + +/** + * Delete the parts of the mesh that are in the selection. The `fill_maps_function` + * depends on the selection type: vertices, edges or faces. + */ +static Mesh *delete_mesh_selection(const Mesh &mesh_in, + const VArray &selection, + const bool invert, + FillMapsFunction fill_maps_function) +{ + Array vertex_map(mesh_in.totvert); + uint num_selected_vertices; + + Array edge_map(mesh_in.totedge); + uint num_selected_edges; + + Vector selected_poly_indices; + Vector new_loop_starts; + uint num_selected_polys; + uint num_selected_loops; + + /* Fill all the maps based on the selection. We delete everything + * in the selection instead of keeping it, so we need to invert it. */ + fill_maps_function(mesh_in, + selection, + !invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + + Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in, + num_selected_vertices, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + + /* Copy the selected parts of the mesh over to the new mesh. */ + copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map); + copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts); + BKE_mesh_calc_edges_loose(result); + /* Tag to recalculate normals later. */ + BKE_mesh_normals_tag_dirty(result); + + return result; +} + +static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name) +{ + std::optional selection_attribute = component.attribute_get_meta_data(name); + if (!selection_attribute) { + /* The node will not do anything in this case, but this function must return something. */ + return ATTR_DOMAIN_POINT; + } + + /* Corners can't be deleted separately, so interpolate corner attributes + * to the face domain. Note that this choice is somewhat arbitrary. */ + if (selection_attribute->domain == ATTR_DOMAIN_CORNER) { + return ATTR_DOMAIN_FACE; + } + + return selection_attribute->domain; +} + +static void delete_mesh_selection(MeshComponent &component, + const Mesh &mesh_in, + const StringRef selection_name, + const bool invert) +{ + /* Figure out the best domain to use. */ + const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); + + /* This already checks if the attribute exists, and displays a warning in that case. */ + GVArray_Typed selection = component.attribute_get_for_read( + selection_name, selection_domain, false); + + /* Check if there is anything to delete. */ + bool delete_nothing = true; + for (const int i : selection.index_range()) { + if (selection[i] != invert) { + delete_nothing = false; + break; + } + } + if (delete_nothing) { + return; + } + + Mesh *mesh_out; + switch (selection_domain) { + case ATTR_DOMAIN_POINT: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection); + break; + case ATTR_DOMAIN_EDGE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection); + break; + case ATTR_DOMAIN_FACE: + mesh_out = delete_mesh_selection( + mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection); + break; + default: + BLI_assert_unreachable(); + mesh_out = nullptr; + break; + } + component.replace(mesh_out); +} + +static void geo_node_delete_geometry_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + const bool invert = params.extract_input("Invert"); + const std::string selection_name = params.extract_input("Selection"); + if (selection_name.empty()) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + GeometrySet out_set(geometry_set); + if (geometry_set.has()) { + delete_point_cloud_selection(*geometry_set.get_component_for_read(), + out_set.get_component_for_write(), + selection_name, + invert); + } + if (geometry_set.has()) { + delete_mesh_selection(out_set.get_component_for_write(), + *geometry_set.get_mesh_for_read(), + selection_name, + invert); + } + if (geometry_set.has()) { + delete_curve_selection(*geometry_set.get_component_for_read(), + out_set.get_component_for_write(), + selection_name, + invert); + } + + params.set_output("Geometry", std::move(out_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_delete_geometry() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + + ntype.declare = blender::nodes::geo_node_delete_geometry_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc new file mode 100644 index 00000000000..8f2bf05d2b4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_edge_split.cc @@ -0,0 +1,89 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_modifier_types.h" + +#include "node_geometry_util.hh" + +extern "C" { +Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd); +} + +namespace blender::nodes { + +static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Edge Angle")).default_value(true); + b.add_input(N_("Angle")) + .default_value(DEG2RADF(30.0f)) + .min(0.0f) + .max(DEG2RADF(180.0f)) + .subtype(PROP_ANGLE); + b.add_input(N_("Sharp Edges")); + b.add_output(N_("Geometry")); +} + +static void geo_node_edge_split_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + const bool use_sharp_flag = params.extract_input("Sharp Edges"); + const bool use_edge_angle = params.extract_input("Edge Angle"); + + if (!use_edge_angle && !use_sharp_flag) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + const float split_angle = params.extract_input("Angle"); + const Mesh *mesh_in = geometry_set.get_mesh_for_read(); + + /* Use modifier struct to pass arguments to the modifier code. */ + EdgeSplitModifierData emd; + memset(&emd, 0, sizeof(EdgeSplitModifierData)); + emd.split_angle = split_angle; + if (use_edge_angle) { + emd.flags = MOD_EDGESPLIT_FROMANGLE; + } + if (use_sharp_flag) { + emd.flags |= MOD_EDGESPLIT_FROMFLAG; + } + + Mesh *mesh_out = doEdgeSplit(mesh_in, &emd); + geometry_set.replace_mesh(mesh_out); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_edge_split() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_LEGACY_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec; + ntype.declare = blender::nodes::geo_node_edge_split_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc index 7d3481c1067..333a17aa4e9 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_material_assign.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_legacy_material_assign_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Material").hide_label(true); - b.add_input("Selection"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Material")).hide_label(true); + b.add_input(N_("Selection")); + b.add_output(N_("Geometry")); } static void assign_material_to_faces(Mesh &mesh, const VArray &face_mask, Material *material) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc new file mode 100644 index 00000000000..9167096fd3d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_mesh_to_curve.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "GEO_mesh_to_curve.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Mesh")); + b.add_input(N_("Selection")); + b.add_output(N_("Curve")); +} + +static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Curve", GeometrySet()); + return; + } + + const MeshComponent &component = *geometry_set.get_component_for_read(); + const std::string selection_name = params.extract_input("Selection"); + if (!selection_name.empty() && !component.attribute_exists(selection_name)) { + params.error_message_add(NodeWarningType::Error, + TIP_("No attribute with name \"") + selection_name + "\""); + } + GVArray_Typed selection = component.attribute_get_for_read( + selection_name, ATTR_DOMAIN_EDGE, true); + + Vector selected_edge_indices; + for (const int64_t i : IndexRange(component.attribute_domain_size(ATTR_DOMAIN_EDGE))) { + if (selection[i]) { + selected_edge_indices.append(i); + } + } + + if (selected_edge_indices.size() == 0) { + params.set_output("Curve", GeometrySet()); + return; + } + + std::unique_ptr curve = geometry::mesh_to_curve_convert( + component, IndexMask(selected_edge_indices)); + + params.set_output("Curve", GeometrySet::create_with_curve(curve.release())); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_mesh_to_curve() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_mesh_to_curve_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_mesh_to_curve_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc similarity index 94% rename from source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc index 04b4003daed..210757f986d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_distribute.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_distribute.cc @@ -36,23 +36,22 @@ #include "node_geometry_util.hh" -using blender::bke::AttributeKind; using blender::bke::GeometryInstanceGroup; namespace blender::nodes { static void geo_node_point_distribute_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Distance Min").min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); - b.add_input("Density Max") + b.add_input(N_("Geometry")); + b.add_input(N_("Distance Min")).min(0.0f).max(100000.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Density Max")) .default_value(1.0f) .min(0.0f) .max(100000.0f) .subtype(PROP_NONE); - b.add_input("Density Attribute"); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Geometry"); + b.add_input(N_("Density Attribute")); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Geometry")); } static void geo_node_point_distribute_layout(uiLayout *layout, @@ -253,18 +252,26 @@ BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, { switch (source_domain) { case ATTR_DOMAIN_POINT: { - bke::mesh_surface_sample::sample_point_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_point_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_CORNER: { - bke::mesh_surface_sample::sample_corner_attribute( - mesh, looptri_indices, bary_coords, source_data, output_data); + bke::mesh_surface_sample::sample_corner_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); break; } case ATTR_DOMAIN_FACE: { bke::mesh_surface_sample::sample_face_attribute( - mesh, looptri_indices, source_data, output_data); + mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data); break; } default: { @@ -616,6 +623,11 @@ static void geo_node_point_distribute_exec(GeoNodeExecParams params) final_points_len += positions.size(); } + if (final_points_len == 0) { + params.set_output("Geometry", GeometrySet()); + return; + } + PointCloud *pointcloud = BKE_pointcloud_new_nomain(final_points_len); for (const int instance_index : positions_all.index_range()) { const int offset = instance_start_offsets[instance_index]; diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc similarity index 96% rename from source/blender/nodes/geometry/nodes/node_geo_point_instance.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc index fb45c22ced4..ffb2a0dd7ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_instance.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_instance.cc @@ -28,12 +28,12 @@ namespace blender::nodes { static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Object").hide_label(); - b.add_input("Collection").hide_label(); - b.add_input("Instance Geometry"); - b.add_input("Seed").min(-10000).max(10000); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Object")).hide_label(); + b.add_input(N_("Collection")).hide_label(); + b.add_input(N_("Instance Geometry")); + b.add_input(N_("Seed")).min(-10000).max(10000); + b.add_output(N_("Geometry")); } static void geo_node_point_instance_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -184,7 +184,7 @@ static void add_instances_from_component(InstancesComponent &instances, instances.resize(start_len + domain_size); MutableSpan handles = instances.instance_reference_handles().slice(start_len, domain_size); MutableSpan transforms = instances.instance_transforms().slice(start_len, domain_size); - MutableSpan instance_ids = instances.instance_ids().slice(start_len, domain_size); + MutableSpan instance_ids = instances.instance_ids_ensure().slice(start_len, domain_size); /* Skip all of the randomness handling if there is only a single possible instance * (anything except for collection mode with "Whole Collection" turned off). */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc similarity index 94% rename from source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc index 60c82360007..54d36dab98d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_rotate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_rotate.cc @@ -25,14 +25,16 @@ namespace blender::nodes { static void geo_node_point_rotate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Axis"); - b.add_input("Axis", "Axis_001").default_value({0.0, 0.0, 1.0}).subtype(PROP_XYZ); - b.add_input("Angle"); - b.add_input("Angle", "Angle_001").subtype(PROP_ANGLE); - b.add_input("Rotation"); - b.add_input("Rotation", "Rotation_001").subtype(PROP_EULER); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Axis")); + b.add_input(N_("Axis"), "Axis_001") + .default_value({0.0, 0.0, 1.0}) + .subtype(PROP_XYZ); + b.add_input(N_("Angle")); + b.add_input(N_("Angle"), "Angle_001").subtype(PROP_ANGLE); + b.add_input(N_("Rotation")); + b.add_input(N_("Rotation"), "Rotation_001").subtype(PROP_EULER); + b.add_output(N_("Geometry")); } static void geo_node_point_rotate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc similarity index 94% rename from source/blender/nodes/geometry/nodes/node_geo_point_scale.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc index 99adce149e9..934442ee8a3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_scale.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_scale.cc @@ -25,13 +25,13 @@ namespace blender::nodes { static void geo_node_point_scale_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Factor"); - b.add_input("Factor", "Factor_001") + b.add_input(N_("Geometry")); + b.add_input(N_("Factor")); + b.add_input(N_("Factor"), "Factor_001") .default_value({1.0f, 1.0f, 1.0f}) .subtype(PROP_XYZ); - b.add_input("Factor", "Factor_002").default_value(1.0f).min(0.0f); - b.add_output("Geometry"); + b.add_input(N_("Factor"), "Factor_002").default_value(1.0f).min(0.0f); + b.add_output(N_("Geometry")); } static void geo_node_point_scale_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc similarity index 97% rename from source/blender/nodes/geometry/nodes/node_geo_point_separate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc index 48b6676c1dd..accdaf78439 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_separate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_separate.cc @@ -27,10 +27,10 @@ namespace blender::nodes { static void geo_node_point_instance_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Mask"); - b.add_output("Geometry 1"); - b.add_output("Geometry 2"); + b.add_input(N_("Geometry")); + b.add_input(N_("Mask")); + b.add_output(N_("Geometry 1")); + b.add_output(N_("Geometry 2")); } template diff --git a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc similarity index 94% rename from source/blender/nodes/geometry/nodes/node_geo_point_translate.cc rename to source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc index f2fce45c57b..34f7641995f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_point_translate.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_point_translate.cc @@ -23,10 +23,10 @@ namespace blender::nodes { static void geo_node_point_translate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Translation"); - b.add_input("Translation", "Translation_001").subtype(PROP_TRANSLATION); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Translation")); + b.add_input(N_("Translation"), "Translation_001").subtype(PROP_TRANSLATION); + b.add_output(N_("Geometry")); } static void geo_node_point_translate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc new file mode 100644 index 00000000000..cf7f466c2a6 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_points_to_volume.cc @@ -0,0 +1,278 @@ +/* + * 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. + */ + +#ifdef WITH_OPENVDB +# include +# include +# include +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_volume.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Radius")); + b.add_input(N_("Radius"), "Radius_001").default_value(0.5f).min(0.0f); + b.add_output(N_("Geometry")); +} + +static void geo_node_points_to_volume_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); + uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE); +} + +static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( + sizeof(NodeGeometryPointsToVolume), __func__); + data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; + data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node->storage = data; + + bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius"); + bNodeSocketValueString *radius_attribute_socket_value = + (bNodeSocketValueString *)radius_attribute_socket->default_value; + STRNCPY(radius_attribute_socket_value->value, "radius"); +} + +static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)node->storage; + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == + GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); + nodeSetSocketAvailability( + voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); + + update_attribute_input_socket_availabilities( + *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); +} + +#ifdef WITH_OPENVDB +namespace { +/* Implements the interface required by #openvdb::tools::ParticlesToLevelSet. */ +struct ParticleList { + using PosType = openvdb::Vec3R; + + Span positions; + Span radii; + + size_t size() const + { + return (size_t)positions.size(); + } + + void getPos(size_t n, openvdb::Vec3R &xyz) const + { + xyz = &positions[n].x; + } + + void getPosRad(size_t n, openvdb::Vec3R &xyz, openvdb::Real &radius) const + { + xyz = &positions[n].x; + radius = radii[n]; + } +}; +} // namespace + +static openvdb::FloatGrid::Ptr generate_volume_from_points(const Span positions, + const Span radii, + const float density) +{ + /* Create a new grid that will be filled. #ParticlesToLevelSet requires the background value to + * be positive. It will be set to zero later on. */ + openvdb::FloatGrid::Ptr new_grid = openvdb::FloatGrid::create(1.0f); + + /* Create a narrow-band level set grid based on the positions and radii. */ + openvdb::tools::ParticlesToLevelSet op{*new_grid}; + /* Don't ignore particles based on their radius. */ + op.setRmin(0.0f); + op.setRmax(FLT_MAX); + ParticleList particles{positions, radii}; + op.rasterizeSpheres(particles); + op.finalize(); + + /* Convert the level set to a fog volume. This also sets the background value to zero. Inside the + * fog there will be a density of 1. */ + openvdb::tools::sdfToFogVolume(*new_grid); + + /* Take the desired density into account. */ + openvdb::tools::foreach (new_grid->beginValueOn(), + [&](const openvdb::FloatGrid::ValueOnIter &iter) { + iter.modifyValue([&](float &value) { value *= density; }); + }); + return new_grid; +} + +static float compute_voxel_size(const GeoNodeExecParams ¶ms, + Span positions, + const float radius) +{ + const NodeGeometryPointsToVolume &storage = + *(const NodeGeometryPointsToVolume *)params.node().storage; + + if (storage.resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE) { + return params.get_input("Voxel Size"); + } + + if (positions.is_empty()) { + return 0.0f; + } + + float3 min, max; + INIT_MINMAX(min, max); + minmax_v3v3_v3_array(min, max, (float(*)[3])positions.data(), positions.size()); + + const float voxel_amount = params.get_input("Voxel Amount"); + if (voxel_amount <= 1) { + return 0.0f; + } + + /* The voxel size adapts to the final size of the volume. */ + const float diagonal = float3::distance(min, max); + const float extended_diagonal = diagonal + 2.0f * radius; + const float voxel_size = extended_diagonal / voxel_amount; + return voxel_size; +} + +static void gather_point_data_from_component(const GeoNodeExecParams ¶ms, + const GeometryComponent &component, + Vector &r_positions, + Vector &r_radii) +{ + GVArray_Typed positions = component.attribute_get_for_read( + "position", ATTR_DOMAIN_POINT, {0, 0, 0}); + GVArray_Typed radii = params.get_input_attribute( + "Radius", component, ATTR_DOMAIN_POINT, 0.0f); + + for (const int i : IndexRange(positions.size())) { + r_positions.append(positions[i]); + r_radii.append(radii[i]); + } +} + +static void convert_to_grid_index_space(const float voxel_size, + MutableSpan positions, + MutableSpan radii) +{ + const float voxel_size_inv = 1.0f / voxel_size; + for (const int i : positions.index_range()) { + positions[i] *= voxel_size_inv; + /* Better align generated grid with source points. */ + positions[i] -= float3(0.5f); + radii[i] *= voxel_size_inv; + } +} + +static void initialize_volume_component_from_points(const GeometrySet &geometry_set_in, + GeometrySet &geometry_set_out, + const GeoNodeExecParams ¶ms) +{ + Vector positions; + Vector radii; + + if (geometry_set_in.has()) { + gather_point_data_from_component( + params, *geometry_set_in.get_component_for_read(), positions, radii); + } + if (geometry_set_in.has()) { + gather_point_data_from_component( + params, *geometry_set_in.get_component_for_read(), positions, radii); + } + if (geometry_set_in.has()) { + gather_point_data_from_component( + params, *geometry_set_in.get_component_for_read(), positions, radii); + } + + const float max_radius = *std::max_element(radii.begin(), radii.end()); + const float voxel_size = compute_voxel_size(params, positions, max_radius); + if (voxel_size == 0.0f || positions.is_empty()) { + return; + } + + Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); + BKE_volume_init_grids(volume); + + VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT); + openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast( + BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); + + const float density = params.get_input("Density"); + convert_to_grid_index_space(voxel_size, positions, radii); + openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); + /* This merge is cheap, because the #density_grid is empty. */ + density_grid->merge(*new_grid); + density_grid->transform().postScale(voxel_size); + + VolumeComponent &volume_component = geometry_set_out.get_component_for_write(); + volume_component.replace(volume); +} +#endif + +static void geo_node_points_to_volume_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input("Geometry"); + GeometrySet geometry_set_out; + + /* TODO: Read-only access to instances should be supported here, for now they are made real. */ + geometry_set_in = geometry_set_realize_instances(geometry_set_in); + +#ifdef WITH_OPENVDB + initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params); +#endif + + params.set_output("Geometry", std::move(geometry_set_out)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_points_to_volume() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); + node_type_storage(&ntype, + "NodeGeometryPointsToVolume", + node_free_standard_storage, + node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, blender::nodes::geo_node_points_to_volume_init); + node_type_update(&ntype, blender::nodes::geo_node_points_to_volume_update); + ntype.declare = blender::nodes::geo_node_points_to_volume_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_points_to_volume_exec; + ntype.draw_buttons = blender::nodes::geo_node_points_to_volume_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc new file mode 100644 index 00000000000..e6a81fc9627 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_raycast.cc @@ -0,0 +1,322 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_mesh_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_mesh_sample.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_raycast_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Target Geometry")); + b.add_input(N_("Ray Direction")); + b.add_input(N_("Ray Direction"), "Ray Direction_001") + .default_value({0.0f, 0.0f, 1.0f}); + b.add_input(N_("Ray Length")); + b.add_input(N_("Ray Length"), "Ray Length_001") + .default_value(100.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Target Attribute")); + b.add_input(N_("Is Hit")); + b.add_input(N_("Hit Position")); + b.add_input(N_("Hit Normal")); + b.add_input(N_("Hit Distance")); + b.add_input(N_("Hit Attribute")); + b.add_output(N_("Geometry")); +} + +static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); + uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE); + uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE); +} + +static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), + __func__); + data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; + data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + node->storage = data; +} + +static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; + update_attribute_input_socket_availabilities( + *node, + "Ray Direction", + (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); + update_attribute_input_socket_availabilities( + *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); +} + +static void raycast_to_mesh(const Mesh &mesh, + const VArray &ray_origins, + const VArray &ray_directions, + const VArray &ray_lengths, + const MutableSpan r_hit, + const MutableSpan r_hit_indices, + const MutableSpan r_hit_positions, + const MutableSpan r_hit_normals, + const MutableSpan r_hit_distances) +{ + BLI_assert(ray_origins.size() == ray_directions.size()); + BLI_assert(ray_origins.size() == ray_lengths.size()); + BLI_assert(ray_origins.size() == r_hit.size() || r_hit.is_empty()); + BLI_assert(ray_origins.size() == r_hit_indices.size() || r_hit_indices.is_empty()); + BLI_assert(ray_origins.size() == r_hit_positions.size() || r_hit_positions.is_empty()); + BLI_assert(ray_origins.size() == r_hit_normals.size() || r_hit_normals.is_empty()); + BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty()); + + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); + if (tree_data.tree == nullptr) { + free_bvhtree_from_mesh(&tree_data); + return; + } + + for (const int i : ray_origins.index_range()) { + const float ray_length = ray_lengths[i]; + const float3 ray_origin = ray_origins[i]; + const float3 ray_direction = ray_directions[i].normalized(); + + BVHTreeRayHit hit; + hit.index = -1; + hit.dist = ray_length; + if (BLI_bvhtree_ray_cast(tree_data.tree, + ray_origin, + ray_direction, + 0.0f, + &hit, + tree_data.raycast_callback, + &tree_data) != -1) { + if (!r_hit.is_empty()) { + r_hit[i] = hit.index >= 0; + } + if (!r_hit_indices.is_empty()) { + /* Index should always be a valid looptri index, use 0 when hit failed. */ + r_hit_indices[i] = max_ii(hit.index, 0); + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = hit.co; + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = hit.no; + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = hit.dist; + } + } + else { + if (!r_hit.is_empty()) { + r_hit[i] = false; + } + if (!r_hit_indices.is_empty()) { + r_hit_indices[i] = 0; + } + if (!r_hit_positions.is_empty()) { + r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_normals.is_empty()) { + r_hit_normals[i] = float3(0.0f, 0.0f, 0.0f); + } + if (!r_hit_distances.is_empty()) { + r_hit_distances[i] = ray_length; + } + } + } + + free_bvhtree_from_mesh(&tree_data); +} + +static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( + GeometryNodeRaycastMapMode map_mode) +{ + switch (map_mode) { + case GEO_NODE_RAYCAST_INTERPOLATED: + return bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED; + default: + case GEO_NODE_RAYCAST_NEAREST: + return bke::mesh_surface_sample::eAttributeMapMode::NEAREST; + } +} + +static void raycast_from_points(const GeoNodeExecParams ¶ms, + const GeometrySet &target_geometry, + GeometryComponent &dst_component, + const StringRef hit_name, + const StringRef hit_position_name, + const StringRef hit_normal_name, + const StringRef hit_distance_name, + const Span hit_attribute_names, + const Span hit_attribute_output_names) +{ + BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); + + const MeshComponent *src_mesh_component = + target_geometry.get_component_for_read(); + if (src_mesh_component == nullptr) { + return; + } + const Mesh *src_mesh = src_mesh_component->get_for_read(); + if (src_mesh == nullptr) { + return; + } + if (src_mesh->totpoly == 0) { + return; + } + + const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; + bke::mesh_surface_sample::eAttributeMapMode map_mode = get_map_mode( + (GeometryNodeRaycastMapMode)storage.mapping); + const AttributeDomain result_domain = ATTR_DOMAIN_POINT; + + GVArray_Typed ray_origins = dst_component.attribute_get_for_read( + "position", result_domain, {0, 0, 0}); + GVArray_Typed ray_directions = params.get_input_attribute( + "Ray Direction", dst_component, result_domain, {0, 0, 0}); + GVArray_Typed ray_lengths = params.get_input_attribute( + "Ray Length", dst_component, result_domain, 0); + + OutputAttribute_Typed hit_attribute = + dst_component.attribute_try_get_for_output_only(hit_name, result_domain); + OutputAttribute_Typed hit_position_attribute = + dst_component.attribute_try_get_for_output_only(hit_position_name, result_domain); + OutputAttribute_Typed hit_normal_attribute = + dst_component.attribute_try_get_for_output_only(hit_normal_name, result_domain); + OutputAttribute_Typed hit_distance_attribute = + dst_component.attribute_try_get_for_output_only(hit_distance_name, result_domain); + + /* Positions and looptri indices are always needed for interpolation, + * so create temporary arrays if no output attribute is given. */ + Array hit_indices; + Array hit_positions_internal; + if (!hit_attribute_names.is_empty()) { + hit_indices.reinitialize(ray_origins->size()); + + if (!hit_position_attribute) { + hit_positions_internal.reinitialize(ray_origins->size()); + } + } + const MutableSpan is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan(); + const MutableSpan hit_positions = hit_position_attribute ? + hit_position_attribute.as_span() : + hit_positions_internal; + const MutableSpan hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() : + MutableSpan(); + const MutableSpan hit_distances = hit_distance_attribute ? + hit_distance_attribute.as_span() : + MutableSpan(); + + raycast_to_mesh(*src_mesh, + ray_origins, + ray_directions, + ray_lengths, + is_hit, + hit_indices, + hit_positions, + hit_normals, + hit_distances); + + hit_attribute.save(); + hit_position_attribute.save(); + hit_normal_attribute.save(); + hit_distance_attribute.save(); + + /* Custom interpolated attributes */ + bke::mesh_surface_sample::MeshAttributeInterpolator interp( + src_mesh, IndexMask(ray_origins.size()), hit_positions, hit_indices); + for (const int i : hit_attribute_names.index_range()) { + const std::optional meta_data = src_mesh_component->attribute_get_meta_data( + hit_attribute_names[i]); + if (meta_data) { + ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( + hit_attribute_names[i]); + OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( + hit_attribute_output_names[i], result_domain, meta_data->data_type); + + interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); + + hit_attribute_output.save(); + } + } +} + +static void geo_node_raycast_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet target_geometry_set = params.extract_input("Target Geometry"); + + const std::string hit_name = params.extract_input("Is Hit"); + const std::string hit_position_name = params.extract_input("Hit Position"); + const std::string hit_normal_name = params.extract_input("Hit Normal"); + const std::string hit_distance_name = params.extract_input("Hit Distance"); + + const Array hit_names = {params.extract_input("Target Attribute")}; + const Array hit_output_names = {params.extract_input("Hit Attribute")}; + + geometry_set = bke::geometry_set_realize_instances(geometry_set); + target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); + + static const Array types = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + raycast_from_points(params, + target_geometry_set, + geometry_set.get_component_for_write(type), + hit_name, + hit_position_name, + hit_normal_name, + hit_distance_name, + hit_names, + hit_output_names); + } + } + + params.set_output("Geometry", geometry_set); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_raycast() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_init(&ntype, blender::nodes::geo_node_raycast_init); + node_type_update(&ntype, blender::nodes::geo_node_raycast_update); + node_type_storage( + &ntype, "NodeGeometryRaycast", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_raycast_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_raycast_exec; + ntype.draw_buttons = blender::nodes::geo_node_raycast_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc index eabdd2bcd5a..a8d6f33a5fd 100644 --- a/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_select_by_material.cc @@ -30,10 +30,10 @@ namespace blender::nodes { static void geo_node_legacy_select_by_material_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Material").hide_label(); - b.add_input("Selection"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Material")).hide_label(); + b.add_input(N_("Selection")); + b.add_output(N_("Geometry")); } static void select_mesh_by_material(const Mesh &mesh, diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc new file mode 100644 index 00000000000..295cd05fd01 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_subdivision_surface.cc @@ -0,0 +1,147 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_mesh.h" +#include "BKE_subdiv.h" +#include "BKE_subdiv_mesh.h" + +#include "DNA_modifier_types.h" +#include "UI_interface.h" +#include "UI_resources.h" +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Level")).default_value(1).min(0).max(6); + b.add_input(N_("Use Creases")); + b.add_output(N_("Geometry")); +} + +static void geo_node_subdivision_surface_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ +#ifdef WITH_OPENSUBDIV + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); +#else + UNUSED_VARS(layout, ptr); +#endif +} + +static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometrySubdivisionSurface *data = (NodeGeometrySubdivisionSurface *)MEM_callocN( + sizeof(NodeGeometrySubdivisionSurface), __func__); + data->uv_smooth = SUBSURF_UV_SMOOTH_PRESERVE_BOUNDARIES; + data->boundary_smooth = SUBSURF_BOUNDARY_SMOOTH_ALL; + node->storage = data; +} + +static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + geometry_set = geometry_set_realize_instances(geometry_set); + + if (!geometry_set.has_mesh()) { + params.set_output("Geometry", geometry_set); + return; + } + +#ifndef WITH_OPENSUBDIV + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenSubdiv")); +#else + const NodeGeometrySubdivisionSurface &storage = + *(const NodeGeometrySubdivisionSurface *)params.node().storage; + const int uv_smooth = storage.uv_smooth; + const int boundary_smooth = storage.boundary_smooth; + const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 30); + + /* Only process subdivision if level is greater than 0. */ + if (subdiv_level == 0) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + const bool use_crease = params.extract_input("Use Creases"); + const Mesh *mesh_in = geometry_set.get_mesh_for_read(); + + /* Initialize mesh settings. */ + SubdivToMeshSettings mesh_settings; + mesh_settings.resolution = (1 << subdiv_level) + 1; + mesh_settings.use_optimal_display = false; + + /* Initialize subdivision settings. */ + SubdivSettings subdiv_settings; + subdiv_settings.is_simple = false; + subdiv_settings.is_adaptive = false; + subdiv_settings.use_creases = use_crease; + subdiv_settings.level = subdiv_level; + + subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( + boundary_smooth); + subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( + uv_smooth); + + /* Apply subdivision to mesh. */ + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + + /* In case of bad topology, skip to input mesh. */ + if (subdiv == nullptr) { + params.set_output("Geometry", std::move(geometry_set)); + return; + } + + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); + BKE_mesh_normals_tag_dirty(mesh_out); + + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + mesh_component.replace(mesh_out); + + // BKE_subdiv_stats_print(&subdiv->stats); + BKE_subdiv_free(subdiv); + +#endif + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_subdivision_surface() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_SUBDIVISION_SURFACE, "Subdivision Surface", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_subdivision_surface_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_subdivision_surface_exec; + ntype.draw_buttons = blender::nodes::geo_node_subdivision_surface_layout; + node_type_init(&ntype, blender::nodes::geo_node_subdivision_surface_init); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_storage(&ntype, + "NodeGeometrySubdivisionSurface", + node_free_standard_storage, + node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc new file mode 100644 index 00000000000..39af5bf1fd2 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/legacy/node_geo_volume_to_mesh.cc @@ -0,0 +1,173 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DEG_depsgraph_query.h" +#ifdef WITH_OPENVDB +# include +# include +#endif + +#include "node_geometry_util.hh" + +#include "BKE_lib_id.h" +#include "BKE_material.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_volume.h" +#include "BKE_volume_to_mesh.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Density")); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Geometry")); +} + +static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); +} + +static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( + sizeof(NodeGeometryVolumeToMesh), __func__); + data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; + + bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); + bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; + STRNCPY(grid_socket_value->value, "density"); + + node->storage = data; +} + +static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)node->storage; + + bNodeSocket *voxel_size_socket = nodeFindSocket(node, SOCK_IN, "Voxel Size"); + bNodeSocket *voxel_amount_socket = nodeFindSocket(node, SOCK_IN, "Voxel Amount"); + nodeSetSocketAvailability(voxel_amount_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT); + nodeSetSocketAvailability(voxel_size_socket, + data->resolution_mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE); +} + +#ifdef WITH_OPENVDB + +static void create_mesh_from_volume(GeometrySet &geometry_set_in, + GeometrySet &geometry_set_out, + GeoNodeExecParams ¶ms) +{ + if (!geometry_set_in.has()) { + return; + } + + const NodeGeometryVolumeToMesh &storage = + *(const NodeGeometryVolumeToMesh *)params.node().storage; + + bke::VolumeToMeshResolution resolution; + resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; + if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { + resolution.settings.voxel_amount = params.get_input("Voxel Amount"); + if (resolution.settings.voxel_amount <= 0.0f) { + return; + } + } + else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { + resolution.settings.voxel_size = params.get_input("Voxel Size"); + if (resolution.settings.voxel_size <= 0.0f) { + return; + } + } + + const VolumeComponent *component = geometry_set_in.get_component_for_read(); + const Volume *volume = component->get_for_read(); + if (volume == nullptr) { + return; + } + + const Main *bmain = DEG_get_bmain(params.depsgraph()); + BKE_volume_load(volume, bmain); + + const std::string grid_name = params.get_input("Density"); + const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); + if (volume_grid == nullptr) { + return; + } + + float threshold = params.get_input("Threshold"); + float adaptivity = params.get_input("Adaptivity"); + + const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); + if (mesh == nullptr) { + return; + } + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MeshComponent &dst_component = geometry_set_out.get_component_for_write(); + dst_component.replace(mesh); +} + +#endif /* WITH_OPENVDB */ + +static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_in = params.extract_input("Geometry"); + GeometrySet geometry_set_out; + +#ifdef WITH_OPENVDB + create_mesh_from_volume(geometry_set_in, geometry_set_out, params); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); +#endif + + params.set_output("Geometry", geometry_set_out); +} + +} // namespace blender::nodes + +void register_node_type_geo_legacy_volume_to_mesh() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_LEGACY_VOLUME_TO_MESH, "Volume to Mesh", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; + node_type_storage( + &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); + node_type_size(&ntype, 170, 120, 700); + node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); + node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); + ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; + ntype.draw_buttons = blender::nodes::geo_node_volume_to_mesh_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc index 1fa71d3f57d..5cc8f1476f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_capture.cc @@ -25,19 +25,19 @@ namespace blender::nodes { static void geo_node_attribute_capture_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Value"); - b.add_input("Value", "Value_001"); - b.add_input("Value", "Value_002"); - b.add_input("Value", "Value_003"); - b.add_input("Value", "Value_004"); + b.add_input(N_("Geometry")); + b.add_input(N_("Value")).supports_field(); + b.add_input(N_("Value"), "Value_001").supports_field(); + b.add_input(N_("Value"), "Value_002").supports_field(); + b.add_input(N_("Value"), "Value_003").supports_field(); + b.add_input(N_("Value"), "Value_004").supports_field(); - b.add_output("Geometry"); - b.add_output("Attribute"); - b.add_output("Attribute", "Attribute_001"); - b.add_output("Attribute", "Attribute_002"); - b.add_output("Attribute", "Attribute_003"); - b.add_output("Attribute", "Attribute_004"); + b.add_output(N_("Geometry")); + b.add_output(N_("Attribute")).field_source(); + b.add_output(N_("Attribute"), "Attribute_001").field_source(); + b.add_output(N_("Attribute"), "Attribute_002").field_source(); + b.add_output(N_("Attribute"), "Attribute_003").field_source(); + b.add_output(N_("Attribute"), "Attribute_004").field_source(); } static void geo_node_attribute_capture_layout(uiLayout *layout, @@ -66,8 +66,8 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n node->storage; const CustomDataType data_type = static_cast(storage.data_type); - bNodeSocket *socket_value_attribute_name = (bNodeSocket *)node->inputs.first; - bNodeSocket *socket_value_vector = socket_value_attribute_name->next; + bNodeSocket *socket_value_geometry = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_value_vector = socket_value_geometry->next; bNodeSocket *socket_value_float = socket_value_vector->next; bNodeSocket *socket_value_color4f = socket_value_float->next; bNodeSocket *socket_value_boolean = socket_value_color4f->next; @@ -79,8 +79,8 @@ static void geo_node_attribute_capture_update(bNodeTree *UNUSED(ntree), bNode *n nodeSetSocketAvailability(socket_value_boolean, data_type == CD_PROP_BOOL); nodeSetSocketAvailability(socket_value_int32, data_type == CD_PROP_INT32); - bNodeSocket *out_socket_value_attribute_name = (bNodeSocket *)node->outputs.first; - bNodeSocket *out_socket_value_vector = out_socket_value_attribute_name->next; + bNodeSocket *out_socket_value_geometry = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_value_vector = out_socket_value_geometry->next; bNodeSocket *out_socket_value_float = out_socket_value_vector->next; bNodeSocket *out_socket_value_color4f = out_socket_value_float->next; bNodeSocket *out_socket_value_boolean = out_socket_value_color4f->next; @@ -117,8 +117,6 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - const bNode &node = params.node(); const NodeGeometryAttributeCapture &storage = *(const NodeGeometryAttributeCapture *) node.storage; @@ -146,20 +144,22 @@ static void geo_node_attribute_capture_exec(GeoNodeExecParams params) break; } - WeakAnonymousAttributeID anonymous_id{"Attribute Capture"}; + WeakAnonymousAttributeID anonymous_id{"Attribute"}; const CPPType &type = field.cpp_type(); static const Array types = { GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; - for (const GeometryComponentType type : types) { - if (geometry_set.has(type)) { - GeometryComponent &component = geometry_set.get_component_for_write(type); - try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + for (const GeometryComponentType type : types) { + if (geometry_set.has(type)) { + GeometryComponent &component = geometry_set.get_component_for_write(type); + try_capture_field_on_geometry(component, anonymous_id.get(), domain, field); + } } - } + }); - GField output_field{ - std::make_shared(std::move(anonymous_id), type)}; + GField output_field{std::make_shared( + std::move(anonymous_id), type, params.attribute_producer_name())}; switch (data_type) { case CD_PROP_FLOAT: { @@ -196,7 +196,7 @@ void register_node_type_geo_attribute_capture() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_ATTRIBUTE_CAPTURE, "Attribute Capture", NODE_CLASS_ATTRIBUTE, 0); + &ntype, GEO_NODE_CAPTURE_ATTRIBUTE, "Capture Attribute", NODE_CLASS_ATTRIBUTE, 0); node_type_storage(&ntype, "NodeGeometryAttributeCapture", node_free_standard_storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc index 21a9a338857..f80b8ccc971 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_remove.cc @@ -20,9 +20,9 @@ namespace blender::nodes { static void geo_node_attribute_remove_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Attribute").multi_input(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")).multi_input(); + b.add_output(N_("Geometry")); } static void remove_attribute(GeometryComponent &component, @@ -47,8 +47,6 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Geometry"); Vector attribute_names = params.extract_multi_input("Attribute"); - geometry_set = geometry_set_realize_instances(geometry_set); - if (geometry_set.has()) { remove_attribute( geometry_set.get_component_for_write(), params, attribute_names); @@ -61,6 +59,10 @@ static void geo_node_attribute_remove_exec(GeoNodeExecParams params) remove_attribute( geometry_set.get_component_for_write(), params, attribute_names); } + if (geometry_set.has()) { + remove_attribute( + geometry_set.get_component_for_write(), params, attribute_names); + } params.set_output("Geometry", geometry_set); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc new file mode 100644 index 00000000000..155bd8c8c28 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_statistic.cc @@ -0,0 +1,378 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLI_math_base_safe.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_attribute_statistic_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Attribute")).hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + + b.add_output(N_("Mean")); + b.add_output(N_("Median")); + b.add_output(N_("Sum")); + b.add_output(N_("Min")); + b.add_output(N_("Max")); + b.add_output(N_("Range")); + b.add_output(N_("Standard Deviation")); + b.add_output(N_("Variance")); + + b.add_output(N_("Mean"), "Mean_001"); + b.add_output(N_("Median"), "Median_001"); + b.add_output(N_("Sum"), "Sum_001"); + b.add_output(N_("Min"), "Min_001"); + b.add_output(N_("Max"), "Max_001"); + b.add_output(N_("Range"), "Range_001"); + b.add_output(N_("Standard Deviation"), "Standard Deviation_001"); + b.add_output(N_("Variance"), "Variance_001"); +} + +static void geo_node_attribute_statistic_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void geo_node_attribute_statistic_init(bNodeTree *UNUSED(tree), bNode *node) +{ + node->custom1 = CD_PROP_FLOAT; + node->custom2 = ATTR_DOMAIN_POINT; +} + +static void geo_node_attribute_statistic_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *socket_geo = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_float_attr = socket_geo->next; + bNodeSocket *socket_float3_attr = socket_float_attr->next; + + bNodeSocket *socket_float_mean = (bNodeSocket *)node->outputs.first; + bNodeSocket *socket_float_median = socket_float_mean->next; + bNodeSocket *socket_float_sum = socket_float_median->next; + bNodeSocket *socket_float_min = socket_float_sum->next; + bNodeSocket *socket_float_max = socket_float_min->next; + bNodeSocket *socket_float_range = socket_float_max->next; + bNodeSocket *socket_float_std = socket_float_range->next; + bNodeSocket *socket_float_variance = socket_float_std->next; + + bNodeSocket *socket_vector_mean = socket_float_variance->next; + bNodeSocket *socket_vector_median = socket_vector_mean->next; + bNodeSocket *socket_vector_sum = socket_vector_median->next; + bNodeSocket *socket_vector_min = socket_vector_sum->next; + bNodeSocket *socket_vector_max = socket_vector_min->next; + bNodeSocket *socket_vector_range = socket_vector_max->next; + bNodeSocket *socket_vector_std = socket_vector_range->next; + bNodeSocket *socket_vector_variance = socket_vector_std->next; + + const CustomDataType data_type = static_cast(node->custom1); + + nodeSetSocketAvailability(socket_float_attr, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_mean, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_median, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_sum, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_min, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_max, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_range, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_std, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_float_variance, data_type == CD_PROP_FLOAT); + + nodeSetSocketAvailability(socket_float3_attr, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_mean, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_median, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_sum, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_min, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_max, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_range, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_std, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_vector_variance, data_type == CD_PROP_FLOAT3); +} + +template static T compute_sum(const Span data) +{ + return std::accumulate(data.begin(), data.end(), T()); +} + +static float compute_variance(const Span data, const float mean) +{ + if (data.size() <= 1) { + return 0.0f; + } + + float sum_of_squared_differences = std::accumulate( + data.begin(), data.end(), 0.0f, [mean](float accumulator, float value) { + float difference = mean - value; + return accumulator + difference * difference; + }); + + return sum_of_squared_differences / (data.size() - 1); +} + +static float median_of_sorted_span(const Span data) +{ + if (data.is_empty()) { + return 0.0f; + } + + const float median = data[data.size() / 2]; + + /* For spans of even length, the median is the average of the middle two elements. */ + if (data.size() % 2 == 0) { + return (median + data[data.size() / 2 - 1]) * 0.5f; + } + return median; +} +static void set_empty(CustomDataType data_type, GeoNodeExecParams ¶ms) +{ + if (data_type == CD_PROP_FLOAT) { + params.set_output("Mean", 0.0f); + params.set_output("Median", 0.0f); + params.set_output("Sum", 0.0f); + params.set_output("Min", 0.0f); + params.set_output("Max", 0.0f); + params.set_output("Range", 0.0f); + params.set_output("Standard Deviation", 0.0f); + params.set_output("Variance", 0.0f); + } + else if (data_type == CD_PROP_FLOAT3) { + params.set_output("Mean_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Median_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Sum_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Min_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Max_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Range_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Standard Deviation_001", float3{0.0f, 0.0f, 0.0f}); + params.set_output("Variance_001", float3{0.0f, 0.0f, 0.0f}); + } +} + +static void geo_node_attribute_statistic_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.get_input("Geometry"); + + const bNode &node = params.node(); + const CustomDataType data_type = static_cast(node.custom1); + const AttributeDomain domain = static_cast(node.custom2); + + int64_t total_size = 0; + Vector components = geometry_set.get_components_for_read(); + + for (const GeometryComponent *component : components) { + if (component->attribute_domain_supported(domain)) { + total_size += component->attribute_domain_size(domain); + } + } + if (total_size == 0) { + set_empty(data_type, params); + return; + } + + switch (data_type) { + case CD_PROP_FLOAT: { + const Field input_field = params.get_input>("Attribute"); + Array data = Array(total_size); + int offset = 0; + for (const GeometryComponent *component : components) { + if (component->attribute_domain_supported(domain)) { + GeometryComponentFieldContext field_context{*component, domain}; + const int domain_size = component->attribute_domain_size(domain); + fn::FieldEvaluator data_evaluator{field_context, domain_size}; + MutableSpan component_result = data.as_mutable_span().slice(offset, domain_size); + data_evaluator.add_with_destination(input_field, component_result); + data_evaluator.evaluate(); + offset += domain_size; + } + } + + float mean = 0.0f; + float median = 0.0f; + float sum = 0.0f; + float min = 0.0f; + float max = 0.0f; + float range = 0.0f; + float standard_deviation = 0.0f; + float variance = 0.0f; + const bool sort_required = params.output_is_required("Min") || + params.output_is_required("Max") || + params.output_is_required("Range") || + params.output_is_required("Median"); + const bool sum_required = params.output_is_required("Sum") || + params.output_is_required("Mean"); + const bool variance_required = params.output_is_required("Standard Deviation") || + params.output_is_required("Variance"); + + if (total_size != 0) { + if (sort_required) { + std::sort(data.begin(), data.end()); + median = median_of_sorted_span(data); + + min = data.first(); + max = data.last(); + range = max - min; + } + if (sum_required || variance_required) { + sum = compute_sum(data); + mean = sum / total_size; + + if (variance_required) { + variance = compute_variance(data, mean); + standard_deviation = std::sqrt(variance); + } + } + } + + if (sum_required) { + params.set_output("Sum", sum); + params.set_output("Mean", mean); + } + if (sort_required) { + params.set_output("Min", min); + params.set_output("Max", max); + params.set_output("Range", range); + params.set_output("Median", median); + } + if (variance_required) { + params.set_output("Standard Deviation", standard_deviation); + params.set_output("Variance", variance); + } + break; + } + case CD_PROP_FLOAT3: { + const Field input_field = params.get_input>("Attribute_001"); + + Array data = Array(total_size); + int offset = 0; + for (const GeometryComponent *component : components) { + if (component->attribute_domain_supported(domain)) { + GeometryComponentFieldContext field_context{*component, domain}; + const int domain_size = component->attribute_domain_size(domain); + fn::FieldEvaluator data_evaluator{field_context, domain_size}; + MutableSpan component_result = data.as_mutable_span().slice(offset, domain_size); + data_evaluator.add_with_destination(input_field, component_result); + data_evaluator.evaluate(); + offset += domain_size; + } + } + + float3 median{0}; + float3 min{0}; + float3 max{0}; + float3 range{0}; + float3 sum{0}; + float3 mean{0}; + float3 variance{0}; + float3 standard_deviation{0}; + const bool sort_required = params.output_is_required("Min_001") || + params.output_is_required("Max_001") || + params.output_is_required("Range_001") || + params.output_is_required("Median_001"); + const bool sum_required = params.output_is_required("Sum_001") || + params.output_is_required("Mean_001"); + const bool variance_required = params.output_is_required("Standard Deviation_001") || + params.output_is_required("Variance_001"); + + Array data_x; + Array data_y; + Array data_z; + if (sort_required || variance_required) { + data_x.reinitialize(total_size); + data_y.reinitialize(total_size); + data_z.reinitialize(total_size); + for (const int i : data.index_range()) { + data_x[i] = data[i].x; + data_y[i] = data[i].y; + data_z[i] = data[i].z; + } + } + + if (total_size != 0) { + if (sort_required) { + std::sort(data_x.begin(), data_x.end()); + std::sort(data_y.begin(), data_y.end()); + std::sort(data_z.begin(), data_z.end()); + + const float x_median = median_of_sorted_span(data_x); + const float y_median = median_of_sorted_span(data_y); + const float z_median = median_of_sorted_span(data_z); + median = float3(x_median, y_median, z_median); + + min = float3(data_x.first(), data_y.first(), data_z.first()); + max = float3(data_x.last(), data_y.last(), data_z.last()); + range = max - min; + } + if (sum_required || variance_required) { + sum = compute_sum(data.as_span()); + mean = sum / total_size; + + if (variance_required) { + const float x_variance = compute_variance(data_x, mean.x); + const float y_variance = compute_variance(data_y, mean.y); + const float z_variance = compute_variance(data_z, mean.z); + variance = float3(x_variance, y_variance, z_variance); + standard_deviation = float3( + std::sqrt(variance.x), std::sqrt(variance.y), std::sqrt(variance.z)); + } + } + } + + if (sum_required) { + params.set_output("Sum_001", sum); + params.set_output("Mean_001", mean); + } + if (sort_required) { + params.set_output("Min_001", min); + params.set_output("Max_001", max); + params.set_output("Range_001", range); + params.set_output("Median_001", median); + } + if (variance_required) { + params.set_output("Standard Deviation_001", standard_deviation); + params.set_output("Variance_001", variance); + } + break; + } + default: + break; + } +} + +} // namespace blender::nodes + +void register_node_type_geo_attribute_statistic() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ATTRIBUTE_STATISTIC, "Attribute Statistic", NODE_CLASS_ATTRIBUTE, 0); + + ntype.declare = blender::nodes::geo_node_attribute_statistic_declare; + node_type_init(&ntype, blender::nodes::geo_node_attribute_statistic_init); + node_type_update(&ntype, blender::nodes::geo_node_attribute_statistic_update); + ntype.geometry_node_execute = blender::nodes::geo_node_attribute_statistic_exec; + ntype.draw_buttons = blender::nodes::geo_node_attribute_statistic_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc index 2a1c43a89fe..516f07b7ad3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_boolean.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_boolean.cc @@ -27,11 +27,13 @@ namespace blender::nodes { static void geo_node_boolean_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry 1"); - b.add_input("Geometry 2").multi_input(); - b.add_input("Self Intersection"); - b.add_input("Hole Tolerant"); - b.add_output("Geometry"); + b.add_input(N_("Mesh 1")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Mesh 2")).multi_input().supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Self Intersection")); + b.add_input(N_("Hole Tolerant")); + b.add_output(N_("Mesh")); } static void geo_node_boolean_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -51,12 +53,12 @@ static void geo_node_boolean_update(bNodeTree *UNUSED(ntree), bNode *node) case GEO_NODE_BOOLEAN_UNION: nodeSetSocketAvailability(geometry_1_socket, false); nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry")); + node_sock_label(geometry_2_socket, N_("Mesh")); break; case GEO_NODE_BOOLEAN_DIFFERENCE: nodeSetSocketAvailability(geometry_1_socket, true); nodeSetSocketAvailability(geometry_2_socket, true); - node_sock_label(geometry_2_socket, N_("Geometry 2")); + node_sock_label(geometry_2_socket, N_("Mesh 2")); break; } } @@ -82,11 +84,10 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) GeometrySet set_a; if (operation == GEO_NODE_BOOLEAN_DIFFERENCE) { - set_a = params.extract_input("Geometry 1"); + set_a = params.extract_input("Mesh 1"); /* Note that it technically wouldn't be necessary to realize the instances for the first * geometry input, but the boolean code expects the first shape for the difference operation * to be a single mesh. */ - set_a = geometry_set_realize_instances(set_a); const Mesh *mesh_in_a = set_a.get_mesh_for_read(); if (mesh_in_a != nullptr) { meshes.append(mesh_in_a); @@ -97,7 +98,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) /* The instance transform matrices are owned by the instance group, so we have to * keep all of them around for use during the boolean operation. */ Vector set_groups; - Vector geometry_sets = params.extract_multi_input("Geometry 2"); + Vector geometry_sets = params.extract_multi_input("Mesh 2"); for (const GeometrySet &geometry_set : geometry_sets) { bke::geometry_set_gather_instances(geometry_set, set_groups); } @@ -115,7 +116,7 @@ static void geo_node_boolean_exec(GeoNodeExecParams params) Mesh *result = blender::meshintersect::direct_mesh_boolean( meshes, transforms, float4x4::identity(), {}, use_self, hole_tolerant, operation); - params.set_output("Geometry", GeometrySet::create_with_mesh(result)); + params.set_output("Mesh", GeometrySet::create_with_mesh(result)); } } // namespace blender::nodes @@ -124,7 +125,7 @@ void register_node_type_geo_boolean() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_BOOLEAN, "Boolean", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_MESH_BOOLEAN, "Mesh Boolean", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_boolean_declare; ntype.draw_buttons = blender::nodes::geo_node_boolean_layout; ntype.updatefunc = blender::nodes::geo_node_boolean_update; diff --git a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc index fdc6b12095c..e7c9715934a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_bounding_box.cc @@ -14,150 +14,68 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BKE_spline.hh" -#include "BKE_volume.h" - #include "node_geometry_util.hh" namespace blender::nodes { static void geo_node_bounding_box_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Bounding Box"); - b.add_output("Min"); - b.add_output("Max"); -} - -using bke::GeometryInstanceGroup; - -static void compute_min_max_from_position_and_transform(const GeometryComponent &component, - Span transforms, - float3 &r_min, - float3 &r_max) -{ - GVArray_Typed positions = component.attribute_get_for_read( - "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - - for (const float4x4 &transform : transforms) { - for (const int i : positions.index_range()) { - const float3 position = positions[i]; - const float3 transformed_position = transform * position; - minmax_v3v3_v3(r_min, r_max, transformed_position); - } - } -} - -static void compute_min_max_from_volume_and_transforms(const VolumeComponent &volume_component, - Span transforms, - float3 &r_min, - float3 &r_max) -{ -#ifdef WITH_OPENVDB - const Volume *volume = volume_component.get_for_read(); - if (volume == nullptr) { - return; - } - for (const int i : IndexRange(BKE_volume_num_grids(volume))) { - const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); - openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - - for (const float4x4 &transform : transforms) { - openvdb::GridBase::ConstPtr instance_grid = BKE_volume_grid_shallow_transform(grid, - transform); - float3 grid_min = float3(FLT_MAX); - float3 grid_max = float3(-FLT_MAX); - if (BKE_volume_grid_bounds(instance_grid, grid_min, grid_max)) { - DO_MIN(grid_min, r_min); - DO_MAX(grid_max, r_max); - } - } - } -#else - UNUSED_VARS(volume_component, transforms, r_min, r_max); -#endif -} - -static void compute_min_max_from_curve_and_transforms(const CurveComponent &curve_component, - Span transforms, - float3 &r_min, - float3 &r_max) -{ - const CurveEval *curve = curve_component.get_for_read(); - if (curve == nullptr) { - return; - } - for (const SplinePtr &spline : curve->splines()) { - Span positions = spline->evaluated_positions(); - - for (const float4x4 &transform : transforms) { - for (const int i : positions.index_range()) { - const float3 position = positions[i]; - const float3 transformed_position = transform * position; - minmax_v3v3_v3(r_min, r_max, transformed_position); - } - } - } -} - -static void compute_geometry_set_instances_boundbox(const GeometrySet &geometry_set, - float3 &r_min, - float3 &r_max) -{ - Vector set_groups; - bke::geometry_set_gather_instances(geometry_set, set_groups); - - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - Span transforms = set_group.transforms; - - if (set.has()) { - compute_min_max_from_position_and_transform( - *set.get_component_for_read(), transforms, r_min, r_max); - } - if (set.has()) { - compute_min_max_from_position_and_transform( - *set.get_component_for_read(), transforms, r_min, r_max); - } - if (set.has()) { - compute_min_max_from_volume_and_transforms( - *set.get_component_for_read(), transforms, r_min, r_max); - } - if (set.has()) { - compute_min_max_from_curve_and_transforms( - *set.get_component_for_read(), transforms, r_min, r_max); - } - } + b.add_input(N_("Geometry")); + b.add_output(N_("Bounding Box")); + b.add_output(N_("Min")); + b.add_output(N_("Max")); } static void geo_node_bounding_box_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); + /* Compute the min and max of all realized geometry for the two + * vector outputs, which are only meant to consider real geometry. */ float3 min = float3(FLT_MAX); float3 max = float3(-FLT_MAX); - - if (geometry_set.has_instances()) { - compute_geometry_set_instances_boundbox(geometry_set, min, max); - } - else { - geometry_set.compute_boundbox_without_instances(&min, &max); - } - + geometry_set.compute_boundbox_without_instances(&min, &max); if (min == float3(FLT_MAX)) { - params.set_output("Bounding Box", GeometrySet()); params.set_output("Min", float3(0)); params.set_output("Max", float3(0)); } else { - const float3 scale = max - min; - const float3 center = min + scale / 2.0f; - Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); - transform_mesh(mesh, center, float3(0), float3(1)); - params.set_output("Bounding Box", GeometrySet::create_with_mesh(mesh)); params.set_output("Min", min); params.set_output("Max", max); } + + /* Generate the bounding box meshes inside each unique geometry set (including individually for + * every instance). Because geometry components are reference counted anyway, we can just + * repurpose the original geometry sets for the output. */ + if (params.output_is_required("Bounding Box")) { + geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) { + float3 sub_min = float3(FLT_MAX); + float3 sub_max = float3(-FLT_MAX); + + /* Reuse the min and max calculation if this is the main "real" geometry set. */ + if (&sub_geometry == &geometry_set) { + sub_min = min; + sub_max = max; + } + else { + sub_geometry.compute_boundbox_without_instances(&sub_min, &sub_max); + } + + if (sub_min == float3(FLT_MAX)) { + sub_geometry.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + } + else { + const float3 scale = sub_max - sub_min; + const float3 center = sub_min + scale / 2.0f; + Mesh *mesh = create_cuboid_mesh(scale, 2, 2, 2); + transform_mesh(*mesh, center, float3(0), float3(1)); + sub_geometry.replace_mesh(mesh); + sub_geometry.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + } + }); + + params.set_output("Bounding Box", std::move(geometry_set)); + } } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc index f4c295b06fb..f068e621596 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_collection_info.cc @@ -21,14 +21,25 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "BKE_collection.h" + #include "node_geometry_util.hh" +#include + namespace blender::nodes { static void geo_node_collection_info_declare(NodeDeclarationBuilder &b) { - b.add_input("Collection").hide_label(); - b.add_output("Geometry"); + b.add_input(N_("Collection")).hide_label(); + b.add_input(N_("Separate Children")) + .description( + N_("Output each child of the collection as a separate instance, sorted alphabetically")); + b.add_input(N_("Reset Children")) + .description( + N_("Reset the transforms of every child instance in the output. Only used when Separate " + "Children is enabled")); + b.add_output(N_("Geometry")); } static void geo_node_collection_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -44,6 +55,12 @@ static void geo_node_collection_info_node_init(bNodeTree *UNUSED(tree), bNode *n node->storage = data; } +struct InstanceListEntry { + int handle; + char *name; + float4x4 transform; +}; + static void geo_node_collection_info_exec(GeoNodeExecParams params) { Collection *collection = params.get_input("Collection"); @@ -54,26 +71,86 @@ static void geo_node_collection_info_exec(GeoNodeExecParams params) params.set_output("Geometry", geometry_set_out); return; } + const Object *self_object = params.self_object(); + const bool is_recursive = BKE_collection_has_object_recursive_instanced(collection, + (Object *)self_object); + if (is_recursive) { + params.error_message_add(NodeWarningType::Error, "Collection contains current object"); + params.set_output("Geometry", geometry_set_out); + return; + } const bNode &bnode = params.node(); NodeGeometryCollectionInfo *node_storage = (NodeGeometryCollectionInfo *)bnode.storage; - const bool transform_space_relative = (node_storage->transform_space == - GEO_NODE_TRANSFORM_SPACE_RELATIVE); + const bool use_relative_transform = (node_storage->transform_space == + GEO_NODE_TRANSFORM_SPACE_RELATIVE); InstancesComponent &instances = geometry_set_out.get_component_for_write(); - float transform_mat[4][4]; - unit_m4(transform_mat); - const Object *self_object = params.self_object(); + const bool separate_children = params.get_input("Separate Children"); + if (separate_children) { + const bool reset_children = params.get_input("Reset Children"); + Vector children_collections; + LISTBASE_FOREACH (CollectionChild *, collection_child, &collection->children) { + children_collections.append(collection_child->collection); + } + Vector children_objects; + LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) { + children_objects.append(collection_object->ob); + } - if (transform_space_relative) { - copy_v3_v3(transform_mat[3], collection->instance_offset); + instances.reserve(children_collections.size() + children_objects.size()); + Vector entries; + entries.reserve(children_collections.size() + children_objects.size()); - mul_m4_m4_pre(transform_mat, self_object->imat); + for (Collection *child_collection : children_collections) { + float4x4 transform = float4x4::identity(); + if (!reset_children) { + add_v3_v3(transform.values[3], child_collection->instance_offset); + if (use_relative_transform) { + mul_m4_m4_pre(transform.values, self_object->imat); + } + else { + sub_v3_v3(transform.values[3], collection->instance_offset); + } + } + const int handle = instances.add_reference(*child_collection); + entries.append({handle, &(child_collection->id.name[2]), transform}); + } + for (Object *child_object : children_objects) { + const int handle = instances.add_reference(*child_object); + float4x4 transform = float4x4::identity(); + if (!reset_children) { + if (use_relative_transform) { + transform = self_object->imat; + } + else { + sub_v3_v3(transform.values[3], collection->instance_offset); + } + mul_m4_m4_post(transform.values, child_object->obmat); + } + entries.append({handle, &(child_object->id.name[2]), transform}); + } + + std::sort(entries.begin(), + entries.end(), + [](const InstanceListEntry &a, const InstanceListEntry &b) { + return BLI_strcasecmp_natural(a.name, b.name) <= 0; + }); + for (const InstanceListEntry &entry : entries) { + instances.add_instance(entry.handle, entry.transform); + } } + else { + float4x4 transform = float4x4::identity(); + if (use_relative_transform) { + copy_v3_v3(transform.values[3], collection->instance_offset); + mul_m4_m4_pre(transform.values, self_object->imat); + } - const int handle = instances.add_reference(*collection); - instances.add_instance(handle, transform_mat, -1); + const int handle = instances.add_reference(*collection); + instances.add_instance(handle, transform); + } params.set_output("Geometry", geometry_set_out); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index f1e10e3d276..3cf682e161c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -32,8 +32,8 @@ namespace blender::nodes { static void geo_node_convex_hull_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Convex Hull"); + b.add_input(N_("Geometry")); + b.add_output(N_("Convex Hull")); } using bke::GeometryInstanceGroup; @@ -227,9 +227,13 @@ static Mesh *compute_hull(const GeometrySet &geometry_set) return hull_from_bullet(geometry_set.get_mesh_for_read(), positions); } +/* Since only positions are read from the instances, this can be used as an internal optimization + * to avoid the cost of realizing instances before the node. But disable this for now, since + * re-enabling that optimization will be a separate step. */ +# if 0 static void read_positions(const GeometryComponent &component, - Span transforms, - Vector *r_coords) + Span transforms, + Vector *r_coords) { GVArray_Typed positions = component.attribute_get_for_read( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); @@ -265,6 +269,31 @@ static void read_curve_positions(const CurveEval &curve, } } +static Mesh *convex_hull_from_instances(const GeometrySet &geometry_set) +{ + Vector set_groups; + bke::geometry_set_gather_instances(geometry_set, set_groups); + + Vector coords; + + for (const GeometryInstanceGroup &set_group : set_groups) { + const GeometrySet &set = set_group.geometry_set; + Span transforms = set_group.transforms; + + if (set.has_pointcloud()) { + read_positions(*set.get_component_for_read(), transforms, &coords); + } + if (set.has_mesh()) { + read_positions(*set.get_component_for_read(), transforms, &coords); + } + if (set.has_curve()) { + read_curve_positions(*set.get_curve_for_read(), transforms, &coords); + } + } + return hull_from_bullet(nullptr, coords); +} +# endif + #endif /* WITH_BULLET */ static void geo_node_convex_hull_exec(GeoNodeExecParams params) @@ -272,33 +301,14 @@ static void geo_node_convex_hull_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Geometry"); #ifdef WITH_BULLET - Mesh *mesh = nullptr; - if (geometry_set.has_instances()) { - Vector set_groups; - bke::geometry_set_gather_instances(geometry_set, set_groups); - Vector coords; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = compute_hull(geometry_set); + geometry_set.replace_mesh(mesh); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); - for (const GeometryInstanceGroup &set_group : set_groups) { - const GeometrySet &set = set_group.geometry_set; - Span transforms = set_group.transforms; - - if (set.has_pointcloud()) { - read_positions(*set.get_component_for_read(), transforms, &coords); - } - if (set.has_mesh()) { - read_positions(*set.get_component_for_read(), transforms, &coords); - } - if (set.has_curve()) { - read_curve_positions(*set.get_curve_for_read(), transforms, &coords); - } - } - mesh = hull_from_bullet(nullptr, coords); - } - else { - mesh = compute_hull(geometry_set); - } - params.set_output("Convex Hull", GeometrySet::create_with_mesh(mesh)); + params.set_output("Convex Hull", std::move(geometry_set)); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without Bullet")); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc new file mode 100644 index 00000000000..42d88cdb1e7 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_endpoint_selection.cc @@ -0,0 +1,153 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_endpoint_selection_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Start Size")) + .min(0) + .default_value(1) + .supports_field() + .description(N_("The amount of points to select from the start of each spline")); + b.add_input(N_("End Size")) + .min(0) + .default_value(1) + .supports_field() + .description(N_("The amount of points to select from the end of each spline")); + b.add_output(N_("Selection")) + .field_source() + .description( + N_("The selection from the start and end of the splines based on the input sizes")); +} + +static void select_by_spline(const int start, const int end, MutableSpan r_selection) +{ + const int size = r_selection.size(); + const int start_use = std::min(start, size); + const int end_use = std::min(end, size); + + r_selection.slice(0, start_use).fill(true); + r_selection.slice(size - end_use, end_use).fill(true); +} + +class EndpointFieldInput final : public fn::FieldInput { + Field start_size_; + Field end_size_; + + public: + EndpointFieldInput(Field start_size, Field end_size) + : FieldInput(CPPType::get(), "Endpoint Selection node"), + start_size_(start_size), + end_size_(end_size) + { + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() != GEO_COMPONENT_TYPE_CURVE || domain != ATTR_DOMAIN_POINT) { + return nullptr; + } + + const CurveComponent &curve_component = static_cast(component); + const CurveEval *curve = curve_component.get_for_read(); + + Array control_point_offsets = curve->control_point_offsets(); + + if (curve == nullptr || control_point_offsets.last() == 0) { + return nullptr; + } + + GeometryComponentFieldContext size_context{curve_component, ATTR_DOMAIN_CURVE}; + fn::FieldEvaluator evaluator{size_context, curve->splines().size()}; + evaluator.add(start_size_); + evaluator.add(end_size_); + evaluator.evaluate(); + const VArray &start_size = evaluator.get_evaluated(0); + const VArray &end_size = evaluator.get_evaluated(1); + + const int point_size = control_point_offsets.last(); + Array selection(point_size, false); + int current_point = 0; + MutableSpan selection_span = selection.as_mutable_span(); + for (int i : IndexRange(curve->splines().size())) { + const SplinePtr &spline = curve->splines()[i]; + if (start_size[i] <= 0 && end_size[i] <= 0) { + selection_span.slice(current_point, spline->size()).fill(false); + } + else { + int start_use = std::max(start_size[i], 0); + int end_use = std::max(end_size[i], 0); + select_by_spline( + start_use, end_use, selection_span.slice(current_point, spline->size())); + } + current_point += spline->size(); + } + return &scope.construct>>(std::move(selection)); + } + return nullptr; + }; + + uint64_t hash() const override + { + return get_default_hash_2(start_size_, end_size_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + if (const EndpointFieldInput *other_endpoint = dynamic_cast( + &other)) { + return start_size_ == other_endpoint->start_size_ && end_size_ == other_endpoint->end_size_; + } + return false; + } +}; + +static void geo_node_curve_endpoint_selection_exec(GeoNodeExecParams params) +{ + Field start_size = params.extract_input>("Start Size"); + Field end_size = params.extract_input>("End Size"); + Field selection_field{std::make_shared(start_size, end_size)}; + params.set_output("Selection", std::move(selection_field)); +} +} // namespace blender::nodes + +void register_node_type_geo_curve_endpoint_selection() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_CURVE_ENDPOINT_SELECTION, "Endpoint Selection", NODE_CLASS_INPUT, 0); + ntype.declare = blender::nodes::geo_node_curve_endpoint_selection_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_endpoint_selection_exec; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc index d8f40b0a0df..219effadec4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fill.cc @@ -35,8 +35,8 @@ namespace blender::nodes { static void geo_node_curve_fill_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_output("Mesh"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output(N_("Mesh")); } static void geo_node_curve_fill_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -124,37 +124,40 @@ static Mesh *cdt_to_mesh(const blender::meshintersect::CDT_result &resul return mesh; } -static Mesh *curve_fill_calculate(GeoNodeExecParams ¶ms, const CurveComponent &component) +static void curve_fill_calculate(GeometrySet &geometry_set, const GeometryNodeCurveFillMode mode) { - const CurveEval &curve = *component.get_for_read(); - if (curve.splines().size() == 0) { - return nullptr; + if (!geometry_set.has_curve()) { + return; } - const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; - const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; + const CurveEval &curve = *geometry_set.get_curve_for_read(); + if (curve.splines().is_empty()) { + geometry_set.replace_curve(nullptr); + return; + } const CDT_output_type output_type = (mode == GEO_NODE_CURVE_FILL_MODE_NGONS) ? CDT_CONSTRAINTS_VALID_BMESH_WITH_HOLES : CDT_INSIDE_WITH_HOLES; const blender::meshintersect::CDT_result results = do_cdt(curve, output_type); - return cdt_to_mesh(results); + Mesh *mesh = cdt_to_mesh(results); + + geometry_set.replace_mesh(mesh); + geometry_set.replace_curve(nullptr); } static void geo_node_curve_fill_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_curve()) { - params.set_output("Mesh", GeometrySet()); - return; - } + const NodeGeometryCurveFill &storage = *(const NodeGeometryCurveFill *)params.node().storage; + const GeometryNodeCurveFillMode mode = (GeometryNodeCurveFillMode)storage.mode; - Mesh *mesh = curve_fill_calculate(params, - *geometry_set.get_component_for_read()); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + geometry_set.modify_geometry_sets( + [&](GeometrySet &geometry_set) { curve_fill_calculate(geometry_set, mode); }); + + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes @@ -163,7 +166,7 @@ void register_node_type_geo_curve_fill() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_FILL, "Curve Fill", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_FILL_CURVE, "Fill Curve", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_curve_fill_init); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc new file mode 100644 index 00000000000..27d7d22b106 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_fillet.cc @@ -0,0 +1,647 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "DNA_node_types.h" + +#include "node_geometry_util.hh" + +#include "BKE_spline.hh" + +namespace blender::nodes { + +static void geo_node_curve_fillet_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Count")).default_value(1).min(1).max(1000).supports_field(); + b.add_input(N_("Radius")) + .min(0.0f) + .max(FLT_MAX) + .subtype(PropertySubType::PROP_DISTANCE) + .default_value(0.25f) + .supports_field(); + b.add_input(N_("Limit Radius")); + b.add_output(N_("Curve")); +} + +static void geo_node_curve_fillet_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_fillet_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveFillet *data = (NodeGeometryCurveFillet *)MEM_callocN( + sizeof(NodeGeometryCurveFillet), __func__); + + data->mode = GEO_NODE_CURVE_FILLET_BEZIER; + node->storage = data; +} + +struct FilletParam { + GeometryNodeCurveFilletMode mode; + + /* Number of points to be added. */ + const VArray *counts; + + /* Radii for fillet arc at all vertices. */ + const VArray *radii; + + /* Whether or not fillets are allowed to overlap. */ + bool limit_radius; +}; + +/* A data structure used to store fillet data about all vertices to be filleted. */ +struct FilletData { + Span positions; + Array directions, axes; + Array radii, angles; + Array counts; +}; + +static void geo_node_curve_fillet_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)node->storage; + const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; + + bNodeSocket *poly_socket = ((bNodeSocket *)node->inputs.first)->next; + + nodeSetSocketAvailability(poly_socket, mode == GEO_NODE_CURVE_FILLET_POLY); +} + +/* Function to get the center of a fillet. */ +static float3 get_center(const float3 vec_pos2prev, + const float3 pos, + const float3 axis, + const float angle) +{ + float3 vec_pos2center; + rotate_normalized_v3_v3v3fl(vec_pos2center, vec_pos2prev, axis, M_PI_2 - angle / 2.0f); + vec_pos2center *= 1.0f / sinf(angle / 2.0f); + + return vec_pos2center + pos; +} + +/* Function to get the center of the fillet using fillet data */ +static float3 get_center(const float3 vec_pos2prev, const FilletData &fd, const int index) +{ + const float angle = fd.angles[index]; + const float3 axis = fd.axes[index]; + const float3 pos = fd.positions[index]; + + return get_center(vec_pos2prev, pos, axis, angle); +} + +/* Calculate the direction vectors from each vertex to their previous vertex. */ +static Array calculate_directions(const Span positions) +{ + const int size = positions.size(); + Array directions(size); + + for (const int i : IndexRange(size - 1)) { + directions[i] = (positions[i + 1] - positions[i]).normalized(); + } + directions[size - 1] = (positions[0] - positions[size - 1]).normalized(); + + return directions; +} + +/* Calculate the axes around which the fillet is built. */ +static Array calculate_axes(const Span directions) +{ + const int size = directions.size(); + Array axes(size); + + axes[0] = float3::cross(-directions[size - 1], directions[0]).normalized(); + for (const int i : IndexRange(1, size - 1)) { + axes[i] = float3::cross(-directions[i - 1], directions[i]).normalized(); + } + + return axes; +} + +/* Calculate the angle of the arc formed by the fillet. */ +static Array calculate_angles(const Span directions) +{ + const int size = directions.size(); + Array angles(size); + + angles[0] = M_PI - angle_v3v3(-directions[size - 1], directions[0]); + for (const int i : IndexRange(1, size - 1)) { + angles[i] = M_PI - angle_v3v3(-directions[i - 1], directions[i]); + } + + return angles; +} + +/* Calculate the segment count in each filleted arc. */ +static Array calculate_counts(const FilletParam &fillet_param, + const int size, + const int spline_offset, + const bool cyclic) +{ + Array counts(size, 1); + if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { + for (const int i : IndexRange(size)) { + counts[i] = (*fillet_param.counts)[spline_offset + i]; + } + } + if (!cyclic) { + counts[0] = counts[size - 1] = 0; + } + + return counts; +} + +/* Calculate the radii for the vertices to be filleted. */ +static Array calculate_radii(const FilletParam &fillet_param, + const int size, + const int spline_offset) +{ + Array radii(size, 0.0f); + if (fillet_param.limit_radius) { + for (const int i : IndexRange(size)) { + radii[i] = std::max((*fillet_param.radii)[spline_offset + i], 0.0f); + } + } + else { + for (const int i : IndexRange(size)) { + radii[i] = (*fillet_param.radii)[spline_offset + i]; + } + } + + return radii; +} + +/* Calculate the number of vertices added per vertex on the source spline. */ +static int calculate_point_counts(MutableSpan point_counts, + const Span radii, + const Span counts) +{ + int added_count = 0; + for (const int i : IndexRange(point_counts.size())) { + /* Calculate number of points to be added for the vertex. */ + if (radii[i] != 0.0f) { + added_count += counts[i]; + point_counts[i] = counts[i] + 1; + } + } + + return added_count; +} + +static FilletData calculate_fillet_data(const Spline &spline, + const FilletParam &fillet_param, + int &added_count, + MutableSpan point_counts, + const int spline_offset) +{ + const int size = spline.size(); + + FilletData fd; + fd.directions = calculate_directions(spline.positions()); + fd.positions = spline.positions(); + fd.axes = calculate_axes(fd.directions); + fd.angles = calculate_angles(fd.directions); + fd.counts = calculate_counts(fillet_param, size, spline_offset, spline.is_cyclic()); + fd.radii = calculate_radii(fillet_param, size, spline_offset); + + added_count = calculate_point_counts(point_counts, fd.radii, fd.counts); + + return fd; +} + +/* Limit the radius based on angle and radii to prevent overlapping. */ +static void limit_radii(FilletData &fd, const bool cyclic) +{ + MutableSpan radii(fd.radii); + Span angles(fd.angles); + Span positions(fd.positions); + + const int size = radii.size(); + const int fillet_count = cyclic ? size : size - 2; + const int start = cyclic ? 0 : 1; + Array max_radii(size, FLT_MAX); + + if (cyclic) { + /* Calculate lengths between adjacent control points. */ + const float len_prev = float3::distance(positions[0], positions[size - 1]); + const float len_next = float3::distance(positions[0], positions[1]); + + /* Calculate tangent lengths of fillets in control points. */ + const float tan_len = radii[0] * tan(angles[0] / 2.0f); + const float tan_len_prev = radii[size - 1] * tan(angles[size - 1] / 2.0f); + const float tan_len_next = radii[1] * tan(angles[1] / 2.0f); + + float factor_prev = 1.0f, factor_next = 1.0f; + if (tan_len + tan_len_prev > len_prev) { + factor_prev = len_prev / (tan_len + tan_len_prev); + } + if (tan_len + tan_len_next > len_next) { + factor_next = len_next / (tan_len + tan_len_next); + } + + /* Scale max radii by calculated factors. */ + max_radii[0] = radii[0] * std::min(factor_next, factor_prev); + max_radii[1] = radii[1] * factor_next; + max_radii[size - 1] = radii[size - 1] * factor_prev; + } + + /* Initialize max_radii to largest possible radii. */ + float prev_dist = float3::distance(positions[1], positions[0]); + for (const int i : IndexRange(1, size - 2)) { + const float temp_dist = float3::distance(positions[i], positions[i + 1]); + max_radii[i] = std::min(prev_dist, temp_dist) / tan(angles[i] / 2.0f); + prev_dist = temp_dist; + } + + /* Max radii calculations for each index. */ + for (const int i : IndexRange(start, fillet_count - 1)) { + const float len_next = float3::distance(positions[i], positions[i + 1]); + const float tan_len = radii[i] * tan(angles[i] / 2.0f); + const float tan_len_next = radii[i + 1] * tan(angles[i + 1] / 2.0f); + + /* Scale down radii if too large for segment. */ + float factor = 1.0f; + if (tan_len + tan_len_next > len_next) { + factor = len_next / (tan_len + tan_len_next); + } + max_radii[i] = std::min(max_radii[i], radii[i] * factor); + max_radii[i + 1] = std::min(max_radii[i + 1], radii[i + 1] * factor); + } + + /* Assign the max_radii to the fillet data's radii. */ + for (const int i : IndexRange(size)) { + radii[i] = max_radii[i]; + } +} + +/* + * Create a mapping from each vertex in the destination spline to that of the source spline. + * Used for copying the data from the source spline. + */ +static Array create_dst_to_src_map(const Span point_counts, const int total_points) +{ + Array map(total_points); + MutableSpan map_span{map}; + int index = 0; + + for (const int i : point_counts.index_range()) { + map_span.slice(index, point_counts[i]).fill(i); + index += point_counts[i]; + } + + BLI_assert(index == total_points); + + return map; +} + +template +static void copy_attribute_by_mapping(const Span src, + MutableSpan dst, + const Span mapping) +{ + for (const int i : dst.index_range()) { + dst[i] = src[mapping[i]]; + } +} + +/* Copy radii and tilts from source spline to destination. Positions are handled later in update + * positions methods. */ +static void copy_common_attributes_by_mapping(const Spline &src, + Spline &dst, + const Span mapping) +{ + copy_attribute_by_mapping(src.radii(), dst.radii(), mapping); + copy_attribute_by_mapping(src.tilts(), dst.tilts(), mapping); + + dst.attributes.reallocate(1); + src.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + std::optional src_attribute = src.attributes.get_for_read(attribute_id); + if (dst.attributes.create(attribute_id, meta_data.data_type)) { + std::optional dst_attribute = dst.attributes.get_for_write(attribute_id); + if (dst_attribute) { + src_attribute->type().copy_assign(src_attribute->data(), dst_attribute->data()); + return true; + } + } + BLI_assert_unreachable(); + return false; + }, + ATTR_DOMAIN_POINT); +} + +/* Update the vertex positions and handle positions of a Bezier spline based on fillet data. */ +static void update_bezier_positions(const FilletData &fd, + BezierSpline &dst_spline, + const BezierSpline &src_spline, + const Span point_counts) +{ + Span radii(fd.radii); + Span angles(fd.angles); + Span axes(fd.axes); + Span positions(fd.positions); + Span directions(fd.directions); + + const int size = radii.size(); + + int i_dst = 0; + for (const int i_src : IndexRange(size)) { + const int count = point_counts[i_src]; + + /* Skip if the point count for the vertex is 1. */ + if (count == 1) { + dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; + dst_spline.handle_types_left()[i_dst] = src_spline.handle_types_left()[i_src]; + dst_spline.handle_types_right()[i_dst] = src_spline.handle_types_right()[i_src]; + dst_spline.handle_positions_left()[i_dst] = src_spline.handle_positions_left()[i_src]; + dst_spline.handle_positions_right()[i_dst] = src_spline.handle_positions_right()[i_src]; + i_dst++; + continue; + } + + /* Calculate the angle to be formed between any 2 adjacent vertices within the fillet. */ + const float segment_angle = angles[i_src] / (count - 1); + /* Calculate the handle length for each added vertex. Equation: L = 4R/3 * tan(A/4) */ + const float handle_length = 4.0f * radii[i_src] / 3.0f * tan(segment_angle / 4.0f); + /* Calculate the distance by which each vertex should be displaced from their initial position. + */ + const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); + + /* Position the end points of the arc and their handles. */ + const int end_i = i_dst + count - 1; + const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 next_dir = directions[i_src]; + dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; + dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; + dst_spline.handle_positions_right()[i_dst] = dst_spline.positions()[i_dst] - + handle_length * prev_dir; + dst_spline.handle_positions_left()[end_i] = dst_spline.positions()[end_i] - + handle_length * next_dir; + dst_spline.handle_types_right()[i_dst] = dst_spline.handle_types_left()[end_i] = + BezierSpline::HandleType::Align; + dst_spline.handle_types_left()[i_dst] = dst_spline.handle_types_right()[end_i] = + BezierSpline::HandleType::Vector; + dst_spline.mark_cache_invalid(); + + /* Calculate the center of the radius to be formed. */ + const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); + /* Calculate the vector of the radius formed by the first vertex. */ + float3 radius_vec = dst_spline.positions()[i_dst] - center; + const float radius = radius_vec.normalize_and_get_length(); + + dst_spline.handle_types_right().slice(1, count - 2).fill(BezierSpline::HandleType::Align); + dst_spline.handle_types_left().slice(1, count - 2).fill(BezierSpline::HandleType::Align); + + /* For each of the vertices in between the end points. */ + for (const int j : IndexRange(1, count - 2)) { + int index = i_dst + j; + /* Rotate the radius by the segment angle and determine its tangent (used for getting handle + * directions). */ + float3 new_radius_vec, tangent_vec; + rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); + rotate_normalized_v3_v3v3fl(tangent_vec, new_radius_vec, axes[i_src], M_PI_2); + radius_vec = new_radius_vec; + tangent_vec *= handle_length; + + /* Adjust the positions of the respective vertex and its handles. */ + dst_spline.positions()[index] = center + new_radius_vec * radius; + dst_spline.handle_positions_left()[index] = dst_spline.positions()[index] + tangent_vec; + dst_spline.handle_positions_right()[index] = dst_spline.positions()[index] - tangent_vec; + } + + i_dst += count; + } +} + +/* Update the vertex positions of a Poly spline based on fillet data. */ +static void update_poly_positions(const FilletData &fd, + Spline &dst_spline, + const Spline &src_spline, + const Span point_counts) +{ + Span radii(fd.radii); + Span angles(fd.angles); + Span axes(fd.axes); + Span positions(fd.positions); + Span directions(fd.directions); + + const int size = radii.size(); + + int i_dst = 0; + for (const int i_src : IndexRange(size)) { + const int count = point_counts[i_src]; + + /* Skip if the point count for the vertex is 1. */ + if (count == 1) { + dst_spline.positions()[i_dst] = src_spline.positions()[i_src]; + i_dst++; + continue; + } + + const float segment_angle = angles[i_src] / (count - 1); + const float displacement = radii[i_src] * tan(angles[i_src] / 2.0f); + + /* Position the end points of the arc. */ + const int end_i = i_dst + count - 1; + const float3 prev_dir = i_src == 0 ? -directions[size - 1] : -directions[i_src - 1]; + const float3 next_dir = directions[i_src]; + dst_spline.positions()[i_dst] = positions[i_src] + displacement * prev_dir; + dst_spline.positions()[end_i] = positions[i_src] + displacement * next_dir; + + /* Calculate the center of the radius to be formed. */ + const float3 center = get_center(dst_spline.positions()[i_dst] - positions[i_src], fd, i_src); + /* Calculate the vector of the radius formed by the first vertex. */ + float3 radius_vec = dst_spline.positions()[i_dst] - center; + + for (const int j : IndexRange(1, count - 2)) { + /* Rotate the radius by the segment angle */ + float3 new_radius_vec; + rotate_normalized_v3_v3v3fl(new_radius_vec, radius_vec, -axes[i_src], segment_angle); + radius_vec = new_radius_vec; + + dst_spline.positions()[i_dst + j] = center + new_radius_vec; + } + + i_dst += count; + } +} + +static SplinePtr fillet_spline(const Spline &spline, + const FilletParam &fillet_param, + const int spline_offset) +{ + const int size = spline.size(); + const bool cyclic = spline.is_cyclic(); + + if (size < 3) { + return spline.copy(); + } + + /* Initialize the point_counts with 1s (at least one vertex on dst for each vertex on src). */ + Array point_counts(size, 1); + + int added_count = 0; + /* Update point_counts array and added_count. */ + FilletData fd = calculate_fillet_data( + spline, fillet_param, added_count, point_counts, spline_offset); + if (fillet_param.limit_radius) { + limit_radii(fd, cyclic); + } + + const int total_points = added_count + size; + const Array dst_to_src = create_dst_to_src_map(point_counts, total_points); + SplinePtr dst_spline_ptr = spline.copy_only_settings(); + (*dst_spline_ptr).resize(total_points); + copy_common_attributes_by_mapping(spline, *dst_spline_ptr, dst_to_src); + + switch (spline.type()) { + case Spline::Type::Bezier: { + const BezierSpline &src_spline = static_cast(spline); + BezierSpline &dst_spline = static_cast(*dst_spline_ptr); + if (fillet_param.mode == GEO_NODE_CURVE_FILLET_POLY) { + dst_spline.handle_types_left().fill(BezierSpline::HandleType::Vector); + dst_spline.handle_types_right().fill(BezierSpline::HandleType::Vector); + update_poly_positions(fd, dst_spline, src_spline, point_counts); + } + else { + update_bezier_positions(fd, dst_spline, src_spline, point_counts); + } + break; + } + case Spline::Type::Poly: { + update_poly_positions(fd, *dst_spline_ptr, spline, point_counts); + break; + } + case Spline::Type::NURBS: { + const NURBSpline &src_spline = static_cast(spline); + NURBSpline &dst_spline = static_cast(*dst_spline_ptr); + copy_attribute_by_mapping(src_spline.weights(), dst_spline.weights(), dst_to_src); + update_poly_positions(fd, dst_spline, src_spline, point_counts); + break; + } + } + + return dst_spline_ptr; +} + +static std::unique_ptr fillet_curve(const CurveEval &input_curve, + const FilletParam &fillet_param) +{ + Span input_splines = input_curve.splines(); + + std::unique_ptr output_curve = std::make_unique(); + const int num_splines = input_splines.size(); + output_curve->resize(num_splines); + MutableSpan output_splines = output_curve->splines(); + Array spline_offsets = input_curve.control_point_offsets(); + + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + output_splines[i] = fillet_spline(*input_splines[i], fillet_param, spline_offsets[i]); + } + }); + output_curve->attributes = input_curve.attributes; + + return output_curve; +} + +static void calculate_curve_fillet(GeometrySet &geometry_set, + const GeometryNodeCurveFilletMode mode, + const Field &radius_field, + const std::optional> &count_field, + const bool limit_radius) +{ + if (!geometry_set.has_curve()) { + return; + } + + FilletParam fillet_param; + fillet_param.mode = mode; + + CurveComponent &component = geometry_set.get_component_for_write(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + fn::FieldEvaluator field_evaluator{field_context, domain_size}; + + field_evaluator.add(radius_field); + + if (mode == GEO_NODE_CURVE_FILLET_POLY) { + field_evaluator.add(*count_field); + } + + field_evaluator.evaluate(); + + fillet_param.radii = &field_evaluator.get_evaluated(0); + if (fillet_param.radii->is_single() && fillet_param.radii->get_internal_single() < 0.0f) { + return; + } + + if (mode == GEO_NODE_CURVE_FILLET_POLY) { + fillet_param.counts = &field_evaluator.get_evaluated(1); + } + + fillet_param.limit_radius = limit_radius; + + const CurveEval &input_curve = *geometry_set.get_curve_for_read(); + std::unique_ptr output_curve = fillet_curve(input_curve, fillet_param); + + geometry_set.replace_curve(output_curve.release()); +} + +static void geo_node_fillet_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + + NodeGeometryCurveFillet &node_storage = *(NodeGeometryCurveFillet *)params.node().storage; + const GeometryNodeCurveFilletMode mode = (GeometryNodeCurveFilletMode)node_storage.mode; + + Field radius_field = params.extract_input>("Radius"); + const bool limit_radius = params.extract_input("Limit Radius"); + + std::optional> count_field; + if (mode == GEO_NODE_CURVE_FILLET_POLY) { + count_field.emplace(params.extract_input>("Count")); + } + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + calculate_curve_fillet(geometry_set, mode, radius_field, count_field, limit_radius); + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_fillet() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_FILLET_CURVE, "Fillet Curve", NODE_CLASS_GEOMETRY, 0); + ntype.draw_buttons = blender::nodes::geo_node_curve_fillet_layout; + node_type_storage( + &ntype, "NodeGeometryCurveFillet", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_curve_fillet_declare; + node_type_init(&ntype, blender::nodes::geo_node_curve_fillet_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_fillet_update); + ntype.geometry_node_execute = blender::nodes::geo_node_fillet_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc new file mode 100644 index 00000000000..165f5da5f71 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_handle_type_selection.cc @@ -0,0 +1,176 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_handle_type_selection_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Selection")).field_source(); +} + +static void geo_node_curve_handle_type_selection_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); + uiItemR(layout, ptr, "handle_type", 0, "", ICON_NONE); +} + +static void geo_node_curve_handle_type_selection_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSelectHandles *data = (NodeGeometryCurveSelectHandles *)MEM_callocN( + sizeof(NodeGeometryCurveSelectHandles), __func__); + + data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + node->storage = data; +} + +static BezierSpline::HandleType handle_type_from_input_type(const GeometryNodeCurveHandleType type) +{ + switch (type) { + case GEO_NODE_CURVE_HANDLE_AUTO: + return BezierSpline::HandleType::Auto; + case GEO_NODE_CURVE_HANDLE_ALIGN: + return BezierSpline::HandleType::Align; + case GEO_NODE_CURVE_HANDLE_FREE: + return BezierSpline::HandleType::Free; + case GEO_NODE_CURVE_HANDLE_VECTOR: + return BezierSpline::HandleType::Vector; + } + BLI_assert_unreachable(); + return BezierSpline::HandleType::Auto; +} + +static void select_by_handle_type(const CurveEval &curve, + const BezierSpline::HandleType type, + const GeometryNodeCurveHandleMode mode, + const MutableSpan r_selection) +{ + int offset = 0; + for (const SplinePtr &spline : curve.splines()) { + if (spline->type() != Spline::Type::Bezier) { + r_selection.slice(offset, spline->size()).fill(false); + offset += spline->size(); + } + else { + BezierSpline *b = static_cast(spline.get()); + for (int i : IndexRange(b->size())) { + r_selection[offset++] = (mode & GEO_NODE_CURVE_HANDLE_LEFT && + b->handle_types_left()[i] == type) || + (mode & GEO_NODE_CURVE_HANDLE_RIGHT && + b->handle_types_right()[i] == type); + } + } + } +} + +class HandleTypeFieldInput final : public fn::FieldInput { + BezierSpline::HandleType type_; + GeometryNodeCurveHandleMode mode_; + + public: + HandleTypeFieldInput(BezierSpline::HandleType type, GeometryNodeCurveHandleMode mode) + : FieldInput(CPPType::get(), "Handle Type Selection node"), type_(type), mode_(mode) + { + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() != GEO_COMPONENT_TYPE_CURVE) { + return nullptr; + } + + const CurveComponent &curve_component = static_cast(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_POINT) { + Array selection(mask.min_array_size()); + select_by_handle_type(*curve, type_, mode_, selection); + return &scope.construct>>(std::move(selection)); + } + } + return nullptr; + }; + + uint64_t hash() const override + { + return get_default_hash_2((int)mode_, (int)type_); + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + if (const HandleTypeFieldInput *other_handle_selection = + dynamic_cast(&other)) { + return mode_ == other_handle_selection->mode_ && type_ == other_handle_selection->type_; + } + return false; + } +}; + +static void geo_node_curve_handle_type_selection_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveSelectHandles *storage = + (const NodeGeometryCurveSelectHandles *)params.node().storage; + const BezierSpline::HandleType handle_type = handle_type_from_input_type( + (GeometryNodeCurveHandleType)storage->handle_type); + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)storage->mode; + + Field selection_field{std::make_shared(handle_type, mode)}; + params.set_output("Selection", std::move(selection_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_handle_type_selection() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, + GEO_NODE_CURVE_HANDLE_TYPE_SELECTION, + "Handle Type Selection", + NODE_CLASS_GEOMETRY, + 0); + ntype.declare = blender::nodes::geo_node_curve_handle_type_selection_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_curve_handle_type_selection_exec; + node_type_init(&ntype, blender::nodes::geo_node_curve_handle_type_selection_init); + node_type_storage(&ntype, + "NodeGeometryCurveSelectHandles", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_curve_handle_type_selection_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc index 8fe054633a1..0d0dc0ec89c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_length.cc @@ -21,14 +21,13 @@ namespace blender::nodes { static void geo_node_curve_length_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_output("Length"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_output(N_("Length")); } static void geo_node_curve_length_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input("Curve"); - curve_set = bke::geometry_set_realize_instances(curve_set); if (!curve_set.has_curve()) { params.set_output("Length", 0.0f); return; diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc new file mode 100644 index 00000000000..4c89aba2e6d --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_parameter.cc @@ -0,0 +1,207 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_parameter_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Factor")).field_source(); +} + +/** + * A basic interpolation from the point domain to the spline domain would be useless, since the + * average parameter for each spline would just be 0.5, or close to it. Instead, the parameter for + * each spline is the portion of the total length at the start of the spline. + */ +static Array curve_parameter_spline_domain(const CurveEval &curve, const IndexMask mask) +{ + Span splines = curve.splines(); + float length = 0.0f; + Array parameters(splines.size()); + for (const int i : splines.index_range()) { + parameters[i] = length; + length += splines[i]->length(); + } + const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; + mask.foreach_index([&](const int64_t i) { parameters[i] *= total_length_inverse; }); + + return parameters; +} + +/** + * The parameter at each control point is the factor at the corresponding evaluated point. + */ +static void calculate_bezier_parameters(const BezierSpline &spline, MutableSpan parameters) +{ + Span offsets = spline.control_point_offsets(); + Span lengths = spline.evaluated_lengths(); + const float total_length = spline.length(); + const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; + + for (const int i : IndexRange(1, spline.size() - 1)) { + parameters[i] = lengths[offsets[i] - 1] * total_length_inverse; + } +} + +/** + * The parameter for poly splines is simply the evaluated lengths divided by the total length. + */ +static void calculate_poly_parameters(const PolySpline &spline, MutableSpan parameters) +{ + Span lengths = spline.evaluated_lengths(); + const float total_length = spline.length(); + const float total_length_inverse = total_length == 0.0f ? 0.0f : 1.0f / total_length; + + for (const int i : IndexRange(1, spline.size() - 1)) { + parameters[i] = lengths[i - 1] * total_length_inverse; + } +} + +/** + * Since NURBS control points do not necessarily coincide with the evaluated curve's path, and + * each control point doesn't correspond well to a specific evaluated point, the parameter at + * each point is not well defined. So instead, treat the control points as if they were a poly + * spline. + */ +static void calculate_nurbs_parameters(const NURBSpline &spline, MutableSpan parameters) +{ + Span positions = spline.positions(); + Array control_point_lengths(spline.size()); + + float length = 0.0f; + for (const int i : IndexRange(positions.size() - 1)) { + parameters[i] = length; + length += float3::distance(positions[i], positions[i + 1]); + } + + const float total_length_inverse = length == 0.0f ? 0.0f : 1.0f / length; + for (float ¶meter : parameters) { + parameter *= total_length_inverse; + } +} + +static Array curve_parameter_point_domain(const CurveEval &curve) +{ + Span splines = curve.splines(); + Array offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array parameters(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_factors{parameters.as_mutable_span().slice(offsets[i], spline.size())}; + spline_factors.first() = 0.0f; + switch (splines[i]->type()) { + case Spline::Type::Bezier: { + calculate_bezier_parameters(static_cast(spline), spline_factors); + break; + } + case Spline::Type::Poly: { + calculate_poly_parameters(static_cast(spline), spline_factors); + break; + } + case Spline::Type::NURBS: { + calculate_nurbs_parameters(static_cast(spline), spline_factors); + break; + } + } + } + }); + return parameters; +} + +static const GVArray *construct_curve_parameter_gvarray(const CurveEval &curve, + const IndexMask mask, + const AttributeDomain domain, + ResourceScope &scope) +{ + if (domain == ATTR_DOMAIN_POINT) { + Array parameters = curve_parameter_point_domain(curve); + return &scope.construct>>(std::move(parameters)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array parameters = curve_parameter_spline_domain(curve, mask); + return &scope.construct>>(std::move(parameters)); + } + + return nullptr; +} + +class CurveParameterFieldInput final : public fn::FieldInput { + public: + CurveParameterFieldInput() : fn::FieldInput(CPPType::get(), "Curve Parameter node") + { + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask mask, + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast(component); + const CurveEval *curve = curve_component.get_for_read(); + if (curve) { + return construct_curve_parameter_gvarray(*curve, mask, domain, scope); + } + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 29837456298; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_curve_parameter_exec(GeoNodeExecParams params) +{ + Field parameter_field{std::make_shared()}; + params.set_output("Factor", std::move(parameter_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_parameter() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_CURVE_PARAMETER, "Curve Parameter", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_parameter_exec; + ntype.declare = blender::nodes::geo_node_curve_parameter_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc index 313473e3442..673a5095044 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_bezier_segment.cc @@ -25,14 +25,32 @@ namespace blender::nodes { static void geo_node_curve_primitive_bezier_segment_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(16).min(1).max(256).subtype(PROP_UNSIGNED); - b.add_input("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Start Handle") + b.add_input(N_("Resolution")) + .default_value(16) + .min(1) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("The number of evaluated points on the curve")); + b.add_input(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the start control point of the curve")); + b.add_input(N_("Start Handle")) .default_value({-0.5f, 0.5f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input("End Handle").subtype(PROP_TRANSLATION); - b.add_input("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output("Curve"); + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the start handle used to define the shape of the curve. In Offset mode, " + "relative to Start point")); + b.add_input(N_("End Handle")) + .subtype(PROP_TRANSLATION) + .description( + N_("Position of the end handle used to define the shape of the curve. In Offset mode, " + "relative to End point")); + b.add_input(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the end control point of the curve")); + b.add_output(N_("Curve")); } static void geo_node_curve_primitive_bezier_segment_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc index f5eb83ea4fd..ffede480c75 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_circle.cc @@ -25,15 +25,36 @@ namespace blender::nodes { static void geo_node_curve_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(32).min(3).max(512); - b.add_input("Point 1") + b.add_input(N_("Resolution")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle")); + b.add_input(N_("Point 1")) .default_value({-1.0f, 0.0f, 0.0f}) - .subtype(PROP_TRANSLATION); - b.add_input("Point 2").default_value({0.0f, 1.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Point 3").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Curve"); - b.add_output("Center"); + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input(N_("Point 2")) + .default_value({0.0f, 1.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input(N_("Point 3")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description( + N_("One of the three points on the circle. The point order determines the circle's " + "direction")); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the points from the origin")); + b.add_output(N_("Curve")); + b.add_output(N_("Center")); } static void geo_node_curve_primitive_circle_layout(uiLayout *layout, @@ -77,7 +98,7 @@ static bool colinear_f3_f3_f3(const float3 p1, const float3 p2, const float3 p3) { const float3 a = (p2 - p1).normalized(); const float3 b = (p3 - p1).normalized(); - return (a == b || a == b * -1.0f); + return (ELEM(a, b, b * -1.0f)); } static std::unique_ptr create_point_circle_curve( @@ -132,7 +153,7 @@ static std::unique_ptr create_point_circle_curve( */ const float theta = theta_step * i; - positions[i] = center + r * cos(theta) * v1 + r * sin(theta) * v4; + positions[i] = center + r * sin(theta) * v1 + r * cos(theta) * v4; } spline->radii().fill(1.0f); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc index a3d2ada612f..37a5989d3f1 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_line.cc @@ -25,11 +25,22 @@ namespace blender::nodes { static void geo_node_curve_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input("Start").subtype(PROP_TRANSLATION); - b.add_input("End").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_input("Direction").default_value({0.0f, 0.0f, 1.0f}); - b.add_input("Length").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_output("Curve"); + b.add_input(N_("Start")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); + b.add_input(N_("End")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the second control point")); + b.add_input(N_("Direction")) + .default_value({0.0f, 0.0f, 1.0f}) + .description( + N_("Direction the line is going in. The length of this vector does not matter")); + b.add_input(N_("Length")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance between the two points")); + b.add_output(N_("Curve")); } static void geo_node_curve_primitive_line_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc index a54fd971ac4..27bf4a310df 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadratic_bezier.cc @@ -21,11 +21,25 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadratic_bezier_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(16).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input("Start").default_value({-1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("Middle").default_value({0.0f, 2.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_input("End").default_value({1.0f, 0.0f, 0.0f}).subtype(PROP_TRANSLATION); - b.add_output("Curve"); + b.add_input(N_("Resolution")) + .default_value(16) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("The number of edges on the curve")); + b.add_input(N_("Start")) + .default_value({-1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first control point")); + b.add_input(N_("Middle")) + .default_value({0.0f, 2.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the middle control point")); + b.add_input(N_("End")) + .default_value({1.0f, 0.0f, 0.0f}) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the last control point")); + b.add_output(N_("Curve")); } static std::unique_ptr create_quadratic_bezier_curve(const float3 p1, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc index 07ddaa8f61e..e00a502bf32 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_quadrilateral.cc @@ -23,18 +23,58 @@ namespace blender::nodes { static void geo_node_curve_primitive_quadrilateral_declare(NodeDeclarationBuilder &b) { - b.add_input("Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Height").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Bottom Width").default_value(4.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Top Width").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Offset").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input("Bottom Height").default_value(3.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Top Height").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input("Point 1").default_value({-1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 2").default_value({1.0f, -1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 3").default_value({1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_input("Point 4").default_value({-1.0f, 1.0f, 0.0f}).subtype(PROP_DISTANCE); - b.add_output("Curve"); + b.add_input(N_("Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input(N_("Height")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The Y axis size of the shape")); + b.add_input(N_("Bottom Width")) + .default_value(4.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input(N_("Top Width")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The X axis size of the shape")); + b.add_input(N_("Offset")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description( + N_("For Parallelogram, the relative X difference between the top and bottom edges. For " + "Trapezoid, the amount to move the top edge in the positive X axis")); + b.add_input(N_("Bottom Height")) + .default_value(3.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The distance between the bottom point and the X axis")); + b.add_input(N_("Top Height")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description(N_("The distance between the top point and the X axis")); + b.add_input(N_("Point 1")) + .default_value({-1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_input(N_("Point 2")) + .default_value({1.0f, -1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_input(N_("Point 3")) + .default_value({1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_input(N_("Point 4")) + .default_value({-1.0f, 1.0f, 0.0f}) + .subtype(PROP_DISTANCE) + .description(N_("The exact location of the point to use")); + b.add_output(N_("Curve")); } static void geo_node_curve_primitive_quadrilateral_layout(uiLayout *layout, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc index 0803d43e5c3..2a872fd82cb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_spiral.cc @@ -22,13 +22,33 @@ namespace blender::nodes { static void geo_node_curve_primitive_spiral_declare(NodeDeclarationBuilder &b) { - b.add_input("Resolution").default_value(32).min(1).max(1024).subtype(PROP_UNSIGNED); - b.add_input("Rotations").default_value(2.0f).min(0.0f); - b.add_input("Start Radius").default_value(1.0f).subtype(PROP_DISTANCE); - b.add_input("End Radius").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input("Height").default_value(2.0f).subtype(PROP_DISTANCE); - b.add_input("Reverse"); - b.add_output("Curve"); + b.add_input(N_("Resolution")) + .default_value(32) + .min(1) + .max(1024) + .subtype(PROP_UNSIGNED) + .description(N_("Number of points in one rotation of the spiral")); + b.add_input(N_("Rotations")) + .default_value(2.0f) + .min(0.0f) + .description(N_("Number of times the spiral makes a full rotation")); + b.add_input(N_("Start Radius")) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .description( + N_("Horizontal Distance from the Z axis at the start of the spiral")); + b.add_input(N_("End Radius")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description( + N_("Horizontal Distance from the Z axis at the end of the spiral")); + b.add_input(N_("Height")) + .default_value(2.0f) + .subtype(PROP_DISTANCE) + .description(N_("The height perpendicular to the base of the spiral")); + b.add_input(N_("Reverse")) + .description(N_("Switch the direction from clockwise to counterclockwise")); + b.add_output(N_("Curve")); } static std::unique_ptr create_spiral_curve(const float rotations, @@ -43,26 +63,18 @@ static std::unique_ptr create_spiral_curve(const float rotations, const int totalpoints = std::max(int(resolution * rotations), 1); const float delta_radius = (end_radius - start_radius) / (float)totalpoints; - float radius = start_radius; const float delta_height = height / (float)totalpoints; - const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints; - float theta = 0.0f; + const float delta_theta = (M_PI * 2 * rotations) / (float)totalpoints * + (direction ? 1.0f : -1.0f); for (const int i : IndexRange(totalpoints + 1)) { + const float theta = i * delta_theta; + const float radius = start_radius + i * delta_radius; const float x = radius * cos(theta); const float y = radius * sin(theta); const float z = delta_height * i; spline->add_point(float3(x, y, z), 1.0f, 0.0f); - - radius += delta_radius; - - if (direction) { - theta += delta_theta; - } - else { - theta -= delta_theta; - } } spline->attributes.reallocate(spline->size()); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc index 6261146562d..7f682b198b3 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_primitive_star.cc @@ -22,11 +22,26 @@ namespace blender::nodes { static void geo_node_curve_primitive_star_declare(NodeDeclarationBuilder &b) { - b.add_input("Points").default_value(8).min(3).max(256).subtype(PROP_UNSIGNED); - b.add_input("Inner Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Outer Radius").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Twist").subtype(PROP_ANGLE); - b.add_output("Curve"); + b.add_input(N_("Points")) + .default_value(8) + .min(3) + .max(256) + .subtype(PROP_UNSIGNED) + .description(N_("Number of points on each of the circles")); + b.add_input(N_("Inner Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the inner circle; can be larger than outer radius")); + b.add_input(N_("Outer Radius")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the outer circle; can be smaller than inner radius")); + b.add_input(N_("Twist")) + .subtype(PROP_ANGLE) + .description(N_("The counterclockwise rotation of the inner set of points")); + b.add_output(N_("Curve")); } static std::unique_ptr create_star_curve(const float inner_radius, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc index e89d500fe57..fb2021c307b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_resample.cc @@ -34,10 +34,15 @@ namespace blender::nodes { static void geo_node_curve_resample_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Count").default_value(10).min(1).max(100000); - b.add_input("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Count")).default_value(10).min(1).max(100000).supports_field(); + b.add_input(N_("Length")) + .default_value(0.1f) + .min(0.001f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_input(N_("Selection")).default_value(true).supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_resample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -50,26 +55,27 @@ static void geo_node_curve_resample_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveResample *data = (NodeGeometryCurveResample *)MEM_callocN( sizeof(NodeGeometryCurveResample), __func__); - data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } static void geo_node_curve_resample_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } struct SampleModeParam { - GeometryNodeCurveSampleMode mode; - std::optional length; - std::optional count; + GeometryNodeCurveResampleMode mode; + std::optional> length; + std::optional> count; + Field selection; }; static SplinePtr resample_spline(const Spline &src, const int count) @@ -78,7 +84,7 @@ static SplinePtr resample_spline(const Spline &src, const int count) Spline::copy_base_settings(src, *dst); if (src.evaluated_edges_size() < 1 || count == 1) { - dst->add_point(src.positions().first(), src.tilts().first(), src.radii().first()); + dst->add_point(src.positions().first(), src.radii().first(), src.tilts().first()); dst->attributes.reallocate(1); src.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { @@ -163,78 +169,124 @@ static SplinePtr resample_spline_evaluated(const Spline &src) return dst; } -static std::unique_ptr resample_curve(const CurveEval &input_curve, +static std::unique_ptr resample_curve(const CurveComponent *component, const SampleModeParam &mode_param) { - Span input_splines = input_curve.splines(); + const CurveEval *input_curve = component->get_for_read(); + GeometryComponentFieldContext field_context{*component, ATTR_DOMAIN_CURVE}; + const int domain_size = component->attribute_domain_size(ATTR_DOMAIN_CURVE); + + Span input_splines = input_curve->splines(); std::unique_ptr output_curve = std::make_unique(); output_curve->resize(input_splines.size()); MutableSpan output_splines = output_curve->splines(); - if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_COUNT) { + if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(*mode_param.count); + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray &cuts = evaluator.get_evaluated(0); + const VArray &selections = evaluator.get_evaluated(1); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { BLI_assert(mode_param.count); - output_splines[i] = resample_spline(*input_splines[i], *mode_param.count); + if (selections[i]) { + output_splines[i] = resample_spline(*input_splines[i], std::max(cuts[i], 1)); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } - else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(*mode_param.length); + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray &lengths = evaluator.get_evaluated(0); + const VArray &selections = evaluator.get_evaluated(1); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - const float length = input_splines[i]->length(); - const int count = std::max(int(length / *mode_param.length) + 1, 1); - output_splines[i] = resample_spline(*input_splines[i], count); + if (selections[i]) { + /* Don't allow asymptotic count increase for low resolution values. */ + const float divide_length = std::max(lengths[i], 0.0001f); + const float spline_length = input_splines[i]->length(); + const int count = std::max(int(spline_length / divide_length) + 1, 1); + output_splines[i] = resample_spline(*input_splines[i], count); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } - else if (mode_param.mode == GEO_NODE_CURVE_SAMPLE_EVALUATED) { + else if (mode_param.mode == GEO_NODE_CURVE_RESAMPLE_EVALUATED) { + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(mode_param.selection); + evaluator.evaluate(); + const VArray &selections = evaluator.get_evaluated(0); + threading::parallel_for(input_splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - output_splines[i] = resample_spline_evaluated(*input_splines[i]); + if (selections[i]) { + output_splines[i] = resample_spline_evaluated(*input_splines[i]); + } + else { + output_splines[i] = input_splines[i]->copy(); + } } }); } - - output_curve->attributes = input_curve.attributes; - + output_curve->attributes = input_curve->attributes; return output_curve; } +static void geometry_set_curve_resample(GeometrySet &geometry_set, + const SampleModeParam &mode_param) +{ + if (!geometry_set.has_curve()) { + return; + } + + std::unique_ptr output_curve = resample_curve( + geometry_set.get_component_for_read(), mode_param); + + geometry_set.replace_curve(output_curve.release()); +} + static void geo_node_resample_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - - if (!geometry_set.has_curve()) { - params.set_output("Geometry", GeometrySet()); - return; - } - - const CurveEval &input_curve = *geometry_set.get_curve_for_read(); NodeGeometryCurveResample &node_storage = *(NodeGeometryCurveResample *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + SampleModeParam mode_param; mode_param.mode = mode; - if (mode == GEO_NODE_CURVE_SAMPLE_COUNT) { - const int count = params.extract_input("Count"); + mode_param.selection = params.extract_input>("Selection"); + + if (mode == GEO_NODE_CURVE_RESAMPLE_COUNT) { + Field count = params.extract_input>("Count"); if (count < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_output("Curve", GeometrySet()); return; } mode_param.count.emplace(count); } - else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { - /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.extract_input("Length"), 0.0001f); + else if (mode == GEO_NODE_CURVE_RESAMPLE_LENGTH) { + Field resolution = params.extract_input>("Length"); mode_param.length.emplace(resolution); } - std::unique_ptr output_curve = resample_curve(input_curve, mode_param); + geometry_set.modify_geometry_sets( + [&](GeometrySet &geometry_set) { geometry_set_curve_resample(geometry_set, mode_param); }); - params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes @@ -243,7 +295,7 @@ void register_node_type_geo_curve_resample() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_RESAMPLE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_RESAMPLE_CURVE, "Resample Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_resample_declare; ntype.draw_buttons = blender::nodes::geo_node_curve_resample_layout; node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc index 32bcbe2c608..b1dc45a426a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_reverse.cc @@ -24,38 +24,40 @@ namespace blender::nodes { static void geo_node_curve_reverse_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_reverse_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_curve()) { - params.set_output("Curve", geometry_set); - return; - } - /* Retrieve data for write access so we can avoid new allocations for the reversed data. */ - CurveComponent &curve_component = geometry_set.get_component_for_write(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan splines = curve.splines(); - - const std::string selection_name = params.extract_input("Selection"); - GVArray_Typed selection = curve_component.attribute_get_for_read( - selection_name, ATTR_DOMAIN_CURVE, true); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - if (selection[i]) { - splines[i]->reverse(); - } + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; } + + Field selection_field = params.get_input>("Selection"); + CurveComponent &component = geometry_set.get_component_for_write(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + CurveEval &curve = *component.get_for_write(); + MutableSpan splines = curve.splines(); + threading::parallel_for(selection.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + splines[selection[i]]->reverse(); + } + }); }); - params.set_output("Curve", geometry_set); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes @@ -63,8 +65,7 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params) void register_node_type_geo_curve_reverse() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_REVERSE, "Curve Reverse", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_REVERSE_CURVE, "Reverse Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_reverse_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_reverse_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc new file mode 100644 index 00000000000..31b38c0dce7 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_sample.cc @@ -0,0 +1,290 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_curve_sample_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Factor")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input(N_("Length")).min(0.0f).subtype(PROP_DISTANCE).supports_field(); + + b.add_output(N_("Position")).dependent_field(); + b.add_output(N_("Tangent")).dependent_field(); + b.add_output(N_("Normal")).dependent_field(); +} + +static void geo_node_curve_sample_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_curve_sample_type_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryCurveSample *data = (NodeGeometryCurveSample *)MEM_callocN( + sizeof(NodeGeometryCurveSample), __func__); + data->mode = GEO_NODE_CURVE_SAMPLE_LENGTH; + node->storage = data; +} + +static void geo_node_curve_sample_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)node->storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + bNodeSocket *factor = ((bNodeSocket *)node->inputs.first)->next; + bNodeSocket *length = factor->next; + + nodeSetSocketAvailability(factor, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(length, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); +} + +template static T sample_with_lookup(const Spline::LookupResult lookup, Span data) +{ + return attribute_math::mix2( + lookup.factor, data[lookup.evaluated_index], data[lookup.next_evaluated_index]); +} + +class SampleCurveFunction : public fn::MultiFunction { + private: + /** + * The function holds a geometry set instead of a curve or a curve component in order to + * maintain a reference to the geometry while the field tree is being built, so that the + * curve is not freed before the function can execute. + */ + GeometrySet geometry_set_; + /** + * To support factor inputs, the node adds another field operation before this one to multiply by + * the curve's total length. Since that must calculate the spline lengths anyway, store them to + * reuse the calculation. + */ + Array spline_lengths_; + /** The last member of #spline_lengths_, extracted for convenience. */ + const float total_length_; + + public: + SampleCurveFunction(GeometrySet geometry_set, Array spline_lengths) + : geometry_set_(std::move(geometry_set)), + spline_lengths_(std::move(spline_lengths)), + total_length_(spline_lengths_.last()) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve Sample"}; + signature.single_input("Length"); + signature.single_output("Position"); + signature.single_output("Tangent"); + signature.single_output("Normal"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + MutableSpan sampled_positions = params.uninitialized_single_output_if_required( + 1, "Position"); + MutableSpan sampled_tangents = params.uninitialized_single_output_if_required( + 2, "Tangent"); + MutableSpan sampled_normals = params.uninitialized_single_output_if_required( + 3, "Normal"); + + auto return_default = [&]() { + if (!sampled_positions.is_empty()) { + sampled_positions.fill_indices(mask, {0, 0, 0}); + } + if (!sampled_tangents.is_empty()) { + sampled_tangents.fill_indices(mask, {0, 0, 0}); + } + if (!sampled_normals.is_empty()) { + sampled_normals.fill_indices(mask, {0, 0, 0}); + } + }; + + if (!geometry_set_.has_curve()) { + return return_default(); + } + + const CurveComponent *curve_component = geometry_set_.get_component_for_read(); + const CurveEval *curve = curve_component->get_for_read(); + Span splines = curve->splines(); + if (splines.is_empty()) { + return return_default(); + } + + const VArray &lengths_varray = params.readonly_single_input(0, "Length"); + const VArray_Span lengths{lengths_varray}; +#ifdef DEBUG + for (const float length : lengths) { + /* Lengths must be in range of the curve's total length. This is ensured in + * #get_length_input_field by adding another multi-function before this one + * to clamp the lengths. */ + BLI_assert(length >= 0.0f && length <= total_length_); + } +#endif + + Array spline_indices(mask.min_array_size()); + for (const int i : mask) { + const float *offset = std::lower_bound( + spline_lengths_.begin(), spline_lengths_.end(), lengths[i]); + const int index = offset - spline_lengths_.data() - 1; + spline_indices[i] = std::max(index, 0); + } + + /* Storing lookups in an array is unnecessary but will simplify custom attribute transfer. */ + Array lookups(mask.min_array_size()); + for (const int i : mask) { + const float length_in_spline = lengths[i] - spline_lengths_[spline_indices[i]]; + lookups[i] = splines[spline_indices[i]]->lookup_evaluated_length(length_in_spline); + } + + if (!sampled_positions.is_empty()) { + for (const int i : mask) { + const Spline::LookupResult &lookup = lookups[i]; + const Span evaluated_positions = splines[spline_indices[i]]->evaluated_positions(); + sampled_positions[i] = sample_with_lookup(lookup, evaluated_positions); + } + } + + if (!sampled_tangents.is_empty()) { + for (const int i : mask) { + const Spline::LookupResult &lookup = lookups[i]; + const Span evaluated_tangents = splines[spline_indices[i]]->evaluated_tangents(); + sampled_tangents[i] = sample_with_lookup(lookup, evaluated_tangents).normalized(); + } + } + + if (!sampled_normals.is_empty()) { + for (const int i : mask) { + const Spline::LookupResult &lookup = lookups[i]; + const Span evaluated_normals = splines[spline_indices[i]]->evaluated_normals(); + sampled_normals[i] = sample_with_lookup(lookup, evaluated_normals).normalized(); + } + } + } +}; + +/** + * Pre-process the lengths or factors used for the sampling, turning factors into lengths, and + * clamping between zero and the total length of the curve. Do this as a separate operation in the + * field tree to make the sampling simpler, and to let the evaluator optimize better. + * + * \todo Use a mutable single input instead when they are supported. + */ +static Field get_length_input_field(const GeoNodeExecParams ¶ms, + const float curve_total_length) +{ + const NodeGeometryCurveSample &node_storage = *(NodeGeometryCurveSample *)params.node().storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + /* Just make sure the length is in bounds of the curve. */ + Field length_field = params.get_input>("Length"); + auto clamp_fn = std::make_unique>( + __func__, [curve_total_length](float length) { + return std::clamp(length, 0.0f, curve_total_length); + }); + auto clamp_op = std::make_shared( + FieldOperation(std::move(clamp_fn), {std::move(length_field)})); + + return Field(std::move(clamp_op), 0); + } + + /* Convert the factor to a length and clamp it to the bounds of the curve. */ + Field factor_field = params.get_input>("Factor"); + auto clamp_fn = std::make_unique>( + __func__, [curve_total_length](float factor) { + const float length = factor * curve_total_length; + return std::clamp(length, 0.0f, curve_total_length); + }); + auto process_op = std::make_shared( + FieldOperation(std::move(clamp_fn), {std::move(factor_field)})); + + return Field(std::move(process_op), 0); +} + +static void geo_node_curve_sample_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + + auto return_default = [&]() { + params.set_output("Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Tangent", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Normal", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + }; + + const CurveComponent *component = geometry_set.get_component_for_read(); + if (component == nullptr) { + return return_default(); + } + + const CurveEval *curve = component->get_for_read(); + if (curve == nullptr) { + return return_default(); + } + + if (curve->splines().is_empty()) { + return return_default(); + } + + Array spline_lengths = curve->accumulated_spline_lengths(); + const float total_length = spline_lengths.last(); + if (total_length == 0.0f) { + return return_default(); + } + + Field length_field = get_length_input_field(params, total_length); + + auto sample_fn = std::make_unique(std::move(geometry_set), + std::move(spline_lengths)); + auto sample_op = std::make_shared( + FieldOperation(std::move(sample_fn), {length_field})); + + params.set_output("Position", Field(sample_op, 0)); + params.set_output("Tangent", Field(sample_op, 1)); + params.set_output("Normal", Field(sample_op, 2)); +} + +} // namespace blender::nodes + +void register_node_type_geo_curve_sample() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SAMPLE_CURVE, " Sample Curve", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_curve_sample_exec; + ntype.declare = blender::nodes::geo_node_curve_sample_declare; + node_type_init(&ntype, blender::nodes::geo_node_curve_sample_type_init); + node_type_update(&ntype, blender::nodes::geo_node_curve_sample_update); + node_type_storage( + &ntype, "NodeGeometryCurveSample", node_free_standard_storage, node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_curve_sample_layout; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc index 31c13134f79..8b0a6ca840c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_set_handles.cc @@ -23,11 +23,11 @@ namespace blender::nodes { -static void geo_node_curve_set_handles_decalre(NodeDeclarationBuilder &b) +static void geo_node_curve_set_handles_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_set_handles_layout(uiLayout *layout, @@ -44,7 +44,7 @@ static void geo_node_curve_set_handles_init(bNodeTree *UNUSED(tree), bNode *node sizeof(NodeGeometryCurveSetHandles), __func__); data->handle_type = GEO_NODE_CURVE_HANDLE_AUTO; - data->mode = GEO_NODE_CURVE_HANDLE_LEFT | GEO_NODE_CURVE_HANDLE_RIGHT; + data->mode = GEO_NODE_CURVE_HANDLE_LEFT; node->storage = data; } @@ -72,57 +72,63 @@ static void geo_node_curve_set_handles_exec(GeoNodeExecParams params) const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_curve()) { - params.set_output("Curve", geometry_set); - return; - } + Field selection_field = params.extract_input>("Selection"); - /* Retrieve data for write access so we can avoid new allocations for the handles data. */ - CurveComponent &curve_component = geometry_set.get_component_for_write(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan splines = curve.splines(); - - const std::string selection_name = params.extract_input("Selection"); - GVArray_Typed selection = curve_component.attribute_get_for_read( - selection_name, ATTR_DOMAIN_POINT, true); - - const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); - int point_index = 0; bool has_bezier_spline = false; - for (SplinePtr &spline : splines) { - if (spline->type() != Spline::Type::Bezier) { - point_index += spline->positions().size(); - continue; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; } - BezierSpline &bezier_spline = static_cast(*spline); - if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { - /* In this case the automatically calculated handle types need to be "baked", because - * they're possibly changing from a type that is calculated automatically to a type that - * is positioned manually. */ - bezier_spline.ensure_auto_handles(); - } - has_bezier_spline = true; - for (int i_point : IndexRange(bezier_spline.size())) { - if (selection[point_index]) { - if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { - bezier_spline.handle_types_left()[i_point] = new_handle_type; - } - if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { - bezier_spline.handle_types_right()[i_point] = new_handle_type; - } + /* Retrieve data for write access so we can avoid new allocations for the handles data. */ + CurveComponent &curve_component = geometry_set.get_component_for_write(); + CurveEval &curve = *curve_component.get_for_write(); + MutableSpan splines = curve.splines(); + + GeometryComponentFieldContext field_context{curve_component, ATTR_DOMAIN_POINT}; + const int domain_size = curve_component.attribute_domain_size(ATTR_DOMAIN_POINT); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray &selection = selection_evaluator.get_evaluated(0); + + const BezierSpline::HandleType new_handle_type = handle_type_from_input_type(type); + int point_index = 0; + + for (SplinePtr &spline : splines) { + if (spline->type() != Spline::Type::Bezier) { + point_index += spline->positions().size(); + continue; } - point_index++; - } - bezier_spline.mark_cache_invalid(); - } + has_bezier_spline = true; + BezierSpline &bezier_spline = static_cast(*spline); + if (ELEM(new_handle_type, BezierSpline::HandleType::Free, BezierSpline::HandleType::Align)) { + /* In this case the automatically calculated handle types need to be "baked", because + * they're possibly changing from a type that is calculated automatically to a type that + * is positioned manually. */ + bezier_spline.ensure_auto_handles(); + } + + for (int i_point : IndexRange(bezier_spline.size())) { + if (selection[point_index]) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + bezier_spline.handle_types_left()[i_point] = new_handle_type; + } + if (mode & GEO_NODE_CURVE_HANDLE_RIGHT) { + bezier_spline.handle_types_right()[i_point] = new_handle_type; + } + } + point_index++; + } + bezier_spline.mark_cache_invalid(); + } + }); if (!has_bezier_spline) { params.error_message_add(NodeWarningType::Info, TIP_("No Bezier splines in input curve")); } - - params.set_output("Curve", geometry_set); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes @@ -130,8 +136,8 @@ void register_node_type_geo_curve_set_handles() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_curve_set_handles_decalre; + &ntype, GEO_NODE_CURVE_SET_HANDLES, "Set Handle Type", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_curve_set_handles_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_set_handles_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_set_handles_init); node_type_storage(&ntype, diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc index 0ef107fd8a4..ae4453929ac 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_spline_type.cc @@ -27,9 +27,9 @@ namespace blender::nodes { static void geo_node_curve_spline_type_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_spline_type_layout(uiLayout *layout, @@ -245,41 +245,47 @@ static void geo_node_curve_spline_type_exec(GeoNodeExecParams params) const GeometryNodeSplineType output_type = (const GeometryNodeSplineType)storage->spline_type; GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_curve()) { - params.set_output("Curve", geometry_set); - return; - } + Field selection_field = params.extract_input>("Selection"); - const CurveComponent *curve_component = geometry_set.get_component_for_read(); - const CurveEval &curve = *curve_component->get_for_read(); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } - const std::string selection_name = params.extract_input("Selection"); - GVArray_Typed selection = curve_component->attribute_get_for_read( - selection_name, ATTR_DOMAIN_CURVE, true); + const CurveComponent *curve_component = geometry_set.get_component_for_read(); + const CurveEval &curve = *curve_component->get_for_read(); + GeometryComponentFieldContext field_context{*curve_component, ATTR_DOMAIN_CURVE}; + const int domain_size = curve_component->attribute_domain_size(ATTR_DOMAIN_CURVE); - std::unique_ptr new_curve = std::make_unique(); - for (const int i : curve.splines().index_range()) { - if (selection[i]) { - switch (output_type) { - case GEO_NODE_SPLINE_TYPE_POLY: - new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); - break; - case GEO_NODE_SPLINE_TYPE_BEZIER: - new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); - break; - case GEO_NODE_SPLINE_TYPE_NURBS: - new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); - break; + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray &selection = selection_evaluator.get_evaluated(0); + + std::unique_ptr new_curve = std::make_unique(); + for (const int i : curve.splines().index_range()) { + if (selection[i]) { + switch (output_type) { + case GEO_NODE_SPLINE_TYPE_POLY: + new_curve->add_spline(convert_to_poly_spline(*curve.splines()[i])); + break; + case GEO_NODE_SPLINE_TYPE_BEZIER: + new_curve->add_spline(convert_to_bezier(*curve.splines()[i], params)); + break; + case GEO_NODE_SPLINE_TYPE_NURBS: + new_curve->add_spline(convert_to_nurbs(*curve.splines()[i])); + break; + } + } + else { + new_curve->add_spline(curve.splines()[i]->copy()); } } - else { - new_curve->add_spline(curve.splines()[i]->copy()); - } - } + new_curve->attributes = curve.attributes; + geometry_set.replace_curve(new_curve.release()); + }); - new_curve->attributes = curve.attributes; - params.set_output("Curve", GeometrySet::create_with_curve(new_curve.release())); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes @@ -288,7 +294,7 @@ void register_node_type_geo_curve_spline_type() { static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_CURVE_SPLINE_TYPE, "Set Spline Type", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_spline_type_declare; ntype.geometry_node_execute = blender::nodes::geo_node_curve_spline_type_exec; node_type_init(&ntype, blender::nodes::geo_node_curve_spline_type_init); diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc index 0522f2b8981..b52de822c22 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_subdivide.cc @@ -33,34 +33,9 @@ namespace blender::nodes { static void geo_node_curve_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Cuts"); - b.add_input("Cuts", "Cuts_001").default_value(1).min(0).max(1000); - b.add_output("Geometry"); -} - -static void geo_node_curve_subdivide_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "cuts_type", 0, IFACE_("Cuts"), ICON_NONE); -} - -static void geo_node_curve_subdivide_init(bNodeTree *UNUSED(tree), bNode *node) -{ - NodeGeometryCurveSubdivide *data = (NodeGeometryCurveSubdivide *)MEM_callocN( - sizeof(NodeGeometryCurveSubdivide), __func__); - - data->cuts_type = GEO_NODE_ATTRIBUTE_INPUT_INTEGER; - node->storage = data; -} - -static void geo_node_curve_subdivide_update(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeGeometryPointTranslate &node_storage = *(NodeGeometryPointTranslate *)node->storage; - - update_attribute_input_socket_availabilities( - *node, "Cuts", (GeometryNodeAttributeInputMode)node_storage.input_type); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Cuts")).default_value(1).min(0).max(1000).supports_field(); + b.add_output(N_("Curve")); } static Array get_subdivided_offsets(const Spline &spline, @@ -88,9 +63,9 @@ static void subdivide_attribute(Span src, for (const int i : range) { const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src[i]; - const float factor_delta = 1.0f / (cuts + 1.0f); + const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; for (const int cut : IndexRange(cuts)) { - const float factor = (cut + 1) * factor_delta; + const float factor = cut * factor_delta; dst[offsets[i] + cut] = attribute_math::mix2(factor, src[i], src[i + 1]); } } @@ -100,9 +75,9 @@ static void subdivide_attribute(Span src, const int i = src_size - 1; const int cuts = offsets[i + 1] - offsets[i]; dst[offsets[i]] = src.last(); - const float factor_delta = 1.0f / (cuts + 1.0f); + const float factor_delta = cuts == 0 ? 1.0f : 1.0f / cuts; for (const int cut : IndexRange(cuts)) { - const float factor = (cut + 1) * factor_delta; + const float factor = cut * factor_delta; dst[offsets[i] + cut] = attribute_math::mix2(factor, src.last(), src.first()); } } @@ -308,8 +283,12 @@ static SplinePtr subdivide_spline(const Spline &spline, const VArray &cuts, const int spline_offset) { - /* Since we expect to access each value many times, it should be worth it to make sure the - * attribute is a real span (especially considering the note below). Using the offset at each + if (spline.size() <= 1) { + return spline.copy(); + } + + /* Since we expect to access each value many times, it should be worth it to make sure count + * of cuts is a real span (especially considering the note below). Using the offset at each * point facilitates subdividing in parallel later. */ Array offsets = get_subdivided_offsets(spline, cuts, spline_offset); const int result_size = offsets.last() + int(!spline.is_cyclic()); @@ -349,26 +328,31 @@ static std::unique_ptr subdivide_curve(const CurveEval &input_curve, static void geo_node_subdivide_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Curve"); + Field cuts_field = params.extract_input>("Cuts"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + return; + } - if (!geometry_set.has_curve()) { - params.set_output("Geometry", geometry_set); - return; - } + const CurveComponent &component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); - const CurveComponent &component = *geometry_set.get_component_for_read(); - GVArray_Typed cuts = params.get_input_attribute( - "Cuts", component, ATTR_DOMAIN_POINT, 0); - if (cuts->is_single() && cuts->get_internal_single() < 1) { - params.set_output("Geometry", geometry_set); - return; - } + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(cuts_field); + evaluator.evaluate(); + const VArray &cuts = evaluator.get_evaluated(0); - std::unique_ptr output_curve = subdivide_curve(*component.get_for_read(), *cuts); + if (cuts.is_single() && cuts.get_internal_single() < 1) { + return; + } - params.set_output("Geometry", GeometrySet::create_with_curve(output_curve.release())); + std::unique_ptr output_curve = subdivide_curve(*component.get_for_read(), cuts); + geometry_set.replace_curve(output_curve.release()); + }); + params.set_output("Curve", geometry_set); } } // namespace blender::nodes @@ -377,16 +361,8 @@ void register_node_type_geo_curve_subdivide() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_CURVE_SUBDIVIDE, "Curve Subdivide", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_CURVE, "Subdivide Curve", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_curve_subdivide_declare; - ntype.draw_buttons = blender::nodes::geo_node_curve_subdivide_layout; - node_type_storage(&ntype, - "NodeGeometryCurveSubdivide", - node_free_standard_storage, - node_copy_standard_storage); - node_type_init(&ntype, blender::nodes::geo_node_curve_subdivide_init); - node_type_update(&ntype, blender::nodes::geo_node_curve_subdivide_update); ntype.geometry_node_execute = blender::nodes::geo_node_subdivide_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc index f46440fd949..1977b465de4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_mesh.cc @@ -14,17 +14,10 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BLI_array.hh" -#include "BLI_float4x4.hh" -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_material.h" -#include "BKE_mesh.h" #include "BKE_spline.hh" +#include "BKE_curve_to_mesh.hh" + #include "UI_interface.h" #include "UI_resources.h" @@ -34,727 +27,49 @@ namespace blender::nodes { static void geo_node_curve_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Profile Curve"); - b.add_output("Mesh"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Profile Curve")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Fill Caps")) + .description( + N_("If the profile spline is cyclic, fill the ends of the generated mesh with N-gons")); + b.add_output(N_("Mesh")); } -/** Information about the creation of one curve spline and profile spline combination. */ -struct ResultInfo { - const Spline &spline; - const Spline &profile; - int vert_offset; - int edge_offset; - int loop_offset; - int poly_offset; - int spline_vert_len; - int spline_edge_len; - int profile_vert_len; - int profile_edge_len; -}; - -static void vert_extrude_to_mesh_data(const Spline &spline, - const float3 profile_vert, - MutableSpan r_verts, - MutableSpan r_edges, - const int vert_offset, - const int edge_offset) +static void geometry_set_curve_to_mesh(GeometrySet &geometry_set, + const GeometrySet &profile_set, + const bool fill_caps) { - Span positions = spline.evaluated_positions(); + const CurveEval *curve = geometry_set.get_curve_for_read(); + const CurveEval *profile_curve = profile_set.get_curve_for_read(); - for (const int i : IndexRange(positions.size() - 1)) { - MEdge &edge = r_edges[edge_offset + i]; - edge.v1 = vert_offset + i; - edge.v2 = vert_offset + i + 1; - edge.flag = ME_LOOSEEDGE; + if (profile_curve == nullptr) { + Mesh *mesh = bke::curve_to_wire_mesh(*curve); + geometry_set.replace_mesh(mesh); } - - if (spline.is_cyclic() && spline.evaluated_edges_size() > 1) { - MEdge &edge = r_edges[edge_offset + spline.evaluated_edges_size() - 1]; - edge.v1 = vert_offset; - edge.v2 = vert_offset + positions.size() - 1; - edge.flag = ME_LOOSEEDGE; + else { + Mesh *mesh = bke::curve_to_mesh_sweep(*curve, *profile_curve, fill_caps); + geometry_set.replace_mesh(mesh); } - - for (const int i : positions.index_range()) { - MVert &vert = r_verts[vert_offset + i]; - copy_v3_v3(vert.co, positions[i] + profile_vert); - } -} - -static void mark_edges_sharp(MutableSpan edges) -{ - for (MEdge &edge : edges) { - edge.flag |= ME_SHARP; - } -} - -static void spline_extrude_to_mesh_data(const ResultInfo &info, - MutableSpan r_verts, - MutableSpan r_edges, - MutableSpan r_loops, - MutableSpan r_polys) -{ - const Spline &spline = info.spline; - const Spline &profile = info.profile; - if (info.profile_vert_len == 1) { - vert_extrude_to_mesh_data(spline, - profile.evaluated_positions()[0], - r_verts, - r_edges, - info.vert_offset, - info.edge_offset); - return; - } - - /* Add the edges running along the length of the curve, starting at each profile vertex. */ - const int spline_edges_start = info.edge_offset; - for (const int i_profile : IndexRange(info.profile_vert_len)) { - const int profile_edge_offset = spline_edges_start + i_profile * info.spline_edge_len; - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; - - const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; - - MEdge &edge = r_edges[profile_edge_offset + i_ring]; - edge.v1 = ring_vert_offset + i_profile; - edge.v2 = next_ring_vert_offset + i_profile; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - - /* Add the edges running along each profile ring. */ - const int profile_edges_start = spline_edges_start + - info.profile_vert_len * info.spline_edge_len; - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - - const int ring_edge_offset = profile_edges_start + i_ring * info.profile_edge_len; - for (const int i_profile : IndexRange(info.profile_edge_len)) { - const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; - - MEdge &edge = r_edges[ring_edge_offset + i_profile]; - edge.v1 = ring_vert_offset + i_profile; - edge.v2 = ring_vert_offset + i_next_profile; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - - /* Calculate poly and corner indices. */ - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int i_next_ring = (i_ring == info.spline_vert_len - 1) ? 0 : i_ring + 1; - - const int ring_vert_offset = info.vert_offset + info.profile_vert_len * i_ring; - const int next_ring_vert_offset = info.vert_offset + info.profile_vert_len * i_next_ring; - - const int ring_edge_start = profile_edges_start + info.profile_edge_len * i_ring; - const int next_ring_edge_offset = profile_edges_start + info.profile_edge_len * i_next_ring; - - const int ring_poly_offset = info.poly_offset + i_ring * info.profile_edge_len; - const int ring_loop_offset = info.loop_offset + i_ring * info.profile_edge_len * 4; - - for (const int i_profile : IndexRange(info.profile_edge_len)) { - const int ring_segment_loop_offset = ring_loop_offset + i_profile * 4; - const int i_next_profile = (i_profile == info.profile_vert_len - 1) ? 0 : i_profile + 1; - - const int spline_edge_start = spline_edges_start + info.spline_edge_len * i_profile; - const int next_spline_edge_start = spline_edges_start + - info.spline_edge_len * i_next_profile; - - MPoly &poly = r_polys[ring_poly_offset + i_profile]; - poly.loopstart = ring_segment_loop_offset; - poly.totloop = 4; - poly.flag = ME_SMOOTH; - - MLoop &loop_a = r_loops[ring_segment_loop_offset]; - loop_a.v = ring_vert_offset + i_profile; - loop_a.e = ring_edge_start + i_profile; - MLoop &loop_b = r_loops[ring_segment_loop_offset + 1]; - loop_b.v = ring_vert_offset + i_next_profile; - loop_b.e = next_spline_edge_start + i_ring; - MLoop &loop_c = r_loops[ring_segment_loop_offset + 2]; - loop_c.v = next_ring_vert_offset + i_next_profile; - loop_c.e = next_ring_edge_offset + i_profile; - MLoop &loop_d = r_loops[ring_segment_loop_offset + 3]; - loop_d.v = next_ring_vert_offset + i_profile; - loop_d.e = spline_edge_start + i_ring; - } - } - - /* Calculate the positions of each profile ring profile along the spline. */ - Span positions = spline.evaluated_positions(); - Span tangents = spline.evaluated_tangents(); - Span normals = spline.evaluated_normals(); - Span profile_positions = profile.evaluated_positions(); - - GVArray_Typed radii = spline.interpolate_to_evaluated(spline.radii()); - for (const int i_ring : IndexRange(info.spline_vert_len)) { - float4x4 point_matrix = float4x4::from_normalized_axis_data( - positions[i_ring], normals[i_ring], tangents[i_ring]); - point_matrix.apply_scale(radii[i_ring]); - - const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; - for (const int i_profile : IndexRange(info.profile_vert_len)) { - MVert &vert = r_verts[ring_vert_start + i_profile]; - copy_v3_v3(vert.co, point_matrix * profile_positions[i_profile]); - } - } - - /* Mark edge loops from sharp vector control points sharp. */ - if (profile.type() == Spline::Type::Bezier) { - const BezierSpline &bezier_spline = static_cast(profile); - Span control_point_offsets = bezier_spline.control_point_offsets(); - for (const int i : IndexRange(bezier_spline.size())) { - if (bezier_spline.point_is_sharp(i)) { - mark_edges_sharp( - r_edges.slice(spline_edges_start + info.spline_edge_len * control_point_offsets[i], - info.spline_edge_len)); - } - } - } -} - -static inline int spline_extrude_vert_size(const Spline &curve, const Spline &profile) -{ - return curve.evaluated_points_size() * profile.evaluated_points_size(); -} - -static inline int spline_extrude_edge_size(const Spline &curve, const Spline &profile) -{ - /* Add the ring edges, with one ring for every curve vertex, and the edge loops - * that run along the length of the curve, starting on the first profile. */ - return curve.evaluated_points_size() * profile.evaluated_edges_size() + - curve.evaluated_edges_size() * profile.evaluated_points_size(); -} - -static inline int spline_extrude_loop_size(const Spline &curve, const Spline &profile) -{ - return curve.evaluated_edges_size() * profile.evaluated_edges_size() * 4; -} - -static inline int spline_extrude_poly_size(const Spline &curve, const Spline &profile) -{ - return curve.evaluated_edges_size() * profile.evaluated_edges_size(); -} - -struct ResultOffsets { - Array vert; - Array edge; - Array loop; - Array poly; -}; -static ResultOffsets calculate_result_offsets(Span profiles, Span curves) -{ - const int total = profiles.size() * curves.size(); - Array vert(total + 1); - Array edge(total + 1); - Array loop(total + 1); - Array poly(total + 1); - - int mesh_index = 0; - int vert_offset = 0; - int edge_offset = 0; - int loop_offset = 0; - int poly_offset = 0; - for (const int i_spline : curves.index_range()) { - for (const int i_profile : profiles.index_range()) { - vert[mesh_index] = vert_offset; - edge[mesh_index] = edge_offset; - loop[mesh_index] = loop_offset; - poly[mesh_index] = poly_offset; - vert_offset += spline_extrude_vert_size(*curves[i_spline], *profiles[i_profile]); - edge_offset += spline_extrude_edge_size(*curves[i_spline], *profiles[i_profile]); - loop_offset += spline_extrude_loop_size(*curves[i_spline], *profiles[i_profile]); - poly_offset += spline_extrude_poly_size(*curves[i_spline], *profiles[i_profile]); - mesh_index++; - } - } - vert.last() = vert_offset; - edge.last() = edge_offset; - loop.last() = loop_offset; - poly.last() = poly_offset; - - return {std::move(vert), std::move(edge), std::move(loop), std::move(poly)}; -} - -static AttributeDomain get_result_attribute_domain(const MeshComponent &component, - const AttributeIDRef &attribute_id) -{ - /* Only use a different domain if it is builtin and must only exist on one domain. */ - if (!component.attribute_is_builtin(attribute_id)) { - return ATTR_DOMAIN_POINT; - } - - std::optional meta_data = component.attribute_get_meta_data(attribute_id); - if (!meta_data) { - /* This function has to return something in this case, but it shouldn't be used, - * so return an output that will assert later if the code attempts to handle it. */ - return ATTR_DOMAIN_AUTO; - } - - return meta_data->domain; -} - -/** - * The data stored in the attribute and its domain from #OutputAttribute, to avoid calling - * `as_span()` for every single profile and curve spline combination, and for readability. - */ -struct ResultAttributeData { - GMutableSpan data; - AttributeDomain domain; -}; - -static std::optional create_attribute_and_get_span( - MeshComponent &component, - const AttributeIDRef &attribute_id, - AttributeMetaData meta_data, - Vector &r_attributes) -{ - const AttributeDomain domain = get_result_attribute_domain(component, attribute_id); - OutputAttribute attribute = component.attribute_try_get_for_output_only( - attribute_id, domain, meta_data.data_type); - if (!attribute) { - return std::nullopt; - } - - GMutableSpan span = attribute.as_span(); - r_attributes.append(std::move(attribute)); - return std::make_optional({span, domain}); -} - -/** - * Store the references to the attribute data from the curve and profile inputs. Here we rely on - * the invariants of the storage of curve attributes, that the order will be consistent between - * splines, and all splines will have the same attributes. - */ -struct ResultAttributes { - /** - * Result attributes on the mesh corresponding to each attribute on the curve input, in the same - * order. The data is optional only in case the attribute does not exist on the mesh for some - * reason, like "shade_smooth" when the result has no faces. - */ - Vector> curve_point_attributes; - Vector> curve_spline_attributes; - - /** - * Result attributes corresponding the attributes on the profile input, in the same order. The - * attributes are optional in case the attribute names correspond to a namse used by the curve - * input, in which case the curve input attributes take precedence. - */ - Vector> profile_point_attributes; - Vector> profile_spline_attributes; - - /** - * Because some builtin attributes are not stored contiguously, and the curve inputs might have - * attributes with those names, it's necessary to keep OutputAttributes around to give access to - * the result data in a contiguous array. - */ - Vector attributes; -}; -static ResultAttributes create_result_attributes(const CurveEval &curve, - const CurveEval &profile, - Mesh &mesh) -{ - MeshComponent mesh_component; - mesh_component.replace(&mesh, GeometryOwnershipType::Editable); - Set curve_attributes; - - /* In order to prefer attributes on the main curve input when there are name collisions, first - * check the attributes on the curve, then add attributes on the profile that are not also on the - * main curve input. */ - ResultAttributes result; - curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - curve_attributes.add_new(id); - result.curve_point_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - return true; - }, - ATTR_DOMAIN_POINT); - curve.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - curve_attributes.add_new(id); - result.curve_spline_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - return true; - }, - ATTR_DOMAIN_CURVE); - profile.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (curve_attributes.contains(id)) { - result.profile_point_attributes.append({}); - } - else { - result.profile_point_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - } - return true; - }, - ATTR_DOMAIN_POINT); - profile.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (curve_attributes.contains(id)) { - result.profile_spline_attributes.append({}); - } - else { - result.profile_spline_attributes.append( - create_attribute_and_get_span(mesh_component, id, meta_data, result.attributes)); - } - return true; - }, - ATTR_DOMAIN_CURVE); - - return result; -} - -template -static void copy_curve_point_data_to_mesh_verts(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int ring_vert_start = info.vert_offset + i_ring * info.profile_vert_len; - dst.slice(ring_vert_start, info.profile_vert_len).fill(src[i_ring]); - } -} - -template -static void copy_curve_point_data_to_mesh_edges(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - const int edges_start = info.edge_offset + info.profile_vert_len * info.spline_edge_len; - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int ring_edge_start = edges_start + info.profile_edge_len * i_ring; - dst.slice(ring_edge_start, info.profile_edge_len).fill(src[i_ring]); - } -} - -template -static void copy_curve_point_data_to_mesh_faces(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int ring_face_start = info.poly_offset + info.profile_edge_len * i_ring; - dst.slice(ring_face_start, info.profile_edge_len).fill(src[i_ring]); - } -} - -static void copy_curve_point_attribute_to_mesh(const GSpan src, - const ResultInfo &info, - ResultAttributeData &dst) -{ - GVArrayPtr interpolated_gvarray = info.spline.interpolate_to_evaluated(src); - GSpan interpolated = interpolated_gvarray->get_internal_span(); - - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - switch (dst.domain) { - case ATTR_DOMAIN_POINT: - copy_curve_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_EDGE: - copy_curve_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_FACE: - copy_curve_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_CORNER: - /* Unsupported for now, since there are no builtin attributes to convert into. */ - break; - default: - BLI_assert_unreachable(); - break; - } - }); -} - -template -static void copy_profile_point_data_to_mesh_verts(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_vert_len)) { - const int profile_vert_start = info.vert_offset + i_ring * info.profile_vert_len; - for (const int i_profile : IndexRange(info.profile_vert_len)) { - dst[profile_vert_start + i_profile] = src[i_profile]; - } - } -} - -template -static void copy_profile_point_data_to_mesh_edges(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_profile : IndexRange(info.profile_vert_len)) { - const int profile_edge_offset = info.edge_offset + i_profile * info.spline_edge_len; - dst.slice(profile_edge_offset, info.spline_edge_len).fill(src[i_profile]); - } -} - -template -static void copy_profile_point_data_to_mesh_faces(const Span src, - const ResultInfo &info, - MutableSpan dst) -{ - for (const int i_ring : IndexRange(info.spline_edge_len)) { - const int profile_face_start = info.poly_offset + i_ring * info.profile_edge_len; - for (const int i_profile : IndexRange(info.profile_edge_len)) { - dst[profile_face_start + i_profile] = src[i_profile]; - } - } -} - -static void copy_profile_point_attribute_to_mesh(const GSpan src, - const ResultInfo &info, - ResultAttributeData &dst) -{ - GVArrayPtr interpolated_gvarray = info.profile.interpolate_to_evaluated(src); - GSpan interpolated = interpolated_gvarray->get_internal_span(); - - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - switch (dst.domain) { - case ATTR_DOMAIN_POINT: - copy_profile_point_data_to_mesh_verts(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_EDGE: - copy_profile_point_data_to_mesh_edges(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_FACE: - copy_profile_point_data_to_mesh_faces(interpolated.typed(), info, dst.data.typed()); - break; - case ATTR_DOMAIN_CORNER: - /* Unsupported for now, since there are no builtin attributes to convert into. */ - break; - default: - BLI_assert_unreachable(); - break; - } - }); -} - -static void copy_point_domain_attributes_to_mesh(const ResultInfo &info, - ResultAttributes &attributes) -{ - if (!attributes.curve_point_attributes.is_empty()) { - int i = 0; - info.spline.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.curve_point_attributes[i]) { - copy_curve_point_attribute_to_mesh(*info.spline.attributes.get_for_read(id), - info, - *attributes.curve_point_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_POINT); - } - if (!attributes.profile_point_attributes.is_empty()) { - int i = 0; - info.profile.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.profile_point_attributes[i]) { - copy_profile_point_attribute_to_mesh(*info.profile.attributes.get_for_read(id), - info, - *attributes.profile_point_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_POINT); - } -} - -template -static void copy_spline_data_to_mesh(Span src, Span offsets, MutableSpan dst) -{ - for (const int i : IndexRange(src.size())) { - dst.slice(offsets[i], offsets[i + 1] - offsets[i]).fill(src[i]); - } -} - -/** - * Since the offsets for each combination of curve and profile spline are stored for every mesh - * domain, and this just needs to fill the chunks corresponding to each combination, we can use - * the same function for all mesh domains. - */ -static void copy_spline_attribute_to_mesh(const GSpan src, - const ResultOffsets &offsets, - ResultAttributeData &dst_attribute) -{ - attribute_math::convert_to_static_type(src.type(), [&](auto dummy) { - using T = decltype(dummy); - switch (dst_attribute.domain) { - case ATTR_DOMAIN_POINT: - copy_spline_data_to_mesh(src.typed(), offsets.vert, dst_attribute.data.typed()); - break; - case ATTR_DOMAIN_EDGE: - copy_spline_data_to_mesh(src.typed(), offsets.edge, dst_attribute.data.typed()); - break; - case ATTR_DOMAIN_FACE: - copy_spline_data_to_mesh(src.typed(), offsets.poly, dst_attribute.data.typed()); - break; - case ATTR_DOMAIN_CORNER: - copy_spline_data_to_mesh(src.typed(), offsets.loop, dst_attribute.data.typed()); - break; - default: - BLI_assert_unreachable(); - break; - } - }); -} - -static void copy_spline_domain_attributes_to_mesh(const CurveEval &curve, - const CurveEval &profile, - const ResultOffsets &offsets, - ResultAttributes &attributes) -{ - if (!attributes.curve_spline_attributes.is_empty()) { - int i = 0; - curve.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.curve_spline_attributes[i]) { - copy_spline_attribute_to_mesh(*curve.attributes.get_for_read(id), - offsets, - *attributes.curve_spline_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_CURVE); - } - if (!attributes.profile_spline_attributes.is_empty()) { - int i = 0; - profile.attributes.foreach_attribute( - [&](const AttributeIDRef &id, const AttributeMetaData &UNUSED(meta_data)) { - if (attributes.profile_spline_attributes[i]) { - copy_spline_attribute_to_mesh(*profile.attributes.get_for_read(id), - offsets, - *attributes.profile_spline_attributes[i]); - } - i++; - return true; - }, - ATTR_DOMAIN_CURVE); - } -} - -/** - * \note Normal calculation is by far the slowest part of calculations relating to the result mesh. - * Although it would be a sensible decision to use the better topology information available while - * generating the mesh to also generate the normals, that work may wasted if the output mesh is - * changed anyway in a way that affects the normals. So currently this code uses the safer / - * simpler solution of deferring normal calculation to the rest of Blender. - */ -static Mesh *curve_to_mesh_calculate(const CurveEval &curve, const CurveEval &profile) -{ - Span profiles = profile.splines(); - Span curves = curve.splines(); - - const ResultOffsets offsets = calculate_result_offsets(profiles, curves); - if (offsets.vert.last() == 0) { - return nullptr; - } - - Mesh *mesh = BKE_mesh_new_nomain( - offsets.vert.last(), offsets.edge.last(), 0, offsets.loop.last(), offsets.poly.last()); - BKE_id_material_eval_ensure_default_slot(&mesh->id); - mesh->flag |= ME_AUTOSMOOTH; - mesh->smoothresh = DEG2RADF(180.0f); - BKE_mesh_normals_tag_dirty(mesh); - - ResultAttributes attributes = create_result_attributes(curve, profile, *mesh); - - threading::parallel_for(curves.index_range(), 128, [&](IndexRange curves_range) { - for (const int i_spline : curves_range) { - const Spline &spline = *curves[i_spline]; - if (spline.evaluated_points_size() == 0) { - continue; - } - const int spline_start_index = i_spline * profiles.size(); - threading::parallel_for(profiles.index_range(), 128, [&](IndexRange profiles_range) { - for (const int i_profile : profiles_range) { - const Spline &profile = *profiles[i_profile]; - const int i_mesh = spline_start_index + i_profile; - ResultInfo info{ - spline, - profile, - offsets.vert[i_mesh], - offsets.edge[i_mesh], - offsets.loop[i_mesh], - offsets.poly[i_mesh], - spline.evaluated_points_size(), - spline.evaluated_edges_size(), - profile.evaluated_points_size(), - profile.evaluated_edges_size(), - }; - - spline_extrude_to_mesh_data(info, - {mesh->mvert, mesh->totvert}, - {mesh->medge, mesh->totedge}, - {mesh->mloop, mesh->totloop}, - {mesh->mpoly, mesh->totpoly}); - - copy_point_domain_attributes_to_mesh(info, attributes); - } - }); - } - }); - - copy_spline_domain_attributes_to_mesh(curve, profile, offsets, attributes); - - for (OutputAttribute &output_attribute : attributes.attributes) { - output_attribute.save(); - } - - return mesh; -} - -static CurveEval get_curve_single_vert() -{ - CurveEval curve; - std::unique_ptr spline = std::make_unique(); - spline->add_point(float3(0), 0, 0.0f); - curve.add_spline(std::move(spline)); - - return curve; } static void geo_node_curve_to_mesh_exec(GeoNodeExecParams params) { GeometrySet curve_set = params.extract_input("Curve"); GeometrySet profile_set = params.extract_input("Profile Curve"); + const bool fill_caps = params.extract_input("Fill Caps"); - curve_set = bke::geometry_set_realize_instances(curve_set); - profile_set = bke::geometry_set_realize_instances(profile_set); - - /* NOTE: Theoretically an "is empty" check would be more correct for errors. */ - if (profile_set.has_mesh() && !profile_set.has_curve()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in profile input")); - } - - if (!curve_set.has_curve()) { - if (curve_set.has_mesh()) { - params.error_message_add(NodeWarningType::Warning, - TIP_("No curve data available in curve input")); + bool has_curve = false; + curve_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + has_curve = true; + geometry_set_curve_to_mesh(geometry_set, profile_set, fill_caps); } - params.set_output("Mesh", GeometrySet()); - return; - } + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); - const CurveEval *profile_curve = profile_set.get_curve_for_read(); - - static const CurveEval vert_curve = get_curve_single_vert(); - - Mesh *mesh = curve_to_mesh_calculate(*curve_set.get_curve_for_read(), - (profile_curve == nullptr) ? vert_curve : *profile_curve); - params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", std::move(curve_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc index 74740ba244f..38d7fb99e87 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_to_points.cc @@ -30,10 +30,13 @@ namespace blender::nodes { static void geo_node_curve_to_points_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Count").default_value(10).min(2).max(100000); - b.add_input("Length").default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Count")).default_value(10).min(2).max(100000); + b.add_input(N_("Length")).default_value(0.1f).min(0.001f).subtype(PROP_DISTANCE); + b.add_output(N_("Points")); + b.add_output(N_("Tangent")).field_source(); + b.add_output(N_("Normal")).field_source(); + b.add_output(N_("Rotation")).field_source(); } static void geo_node_curve_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -46,45 +49,31 @@ static void geo_node_curve_to_points_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveToPoints *data = (NodeGeometryCurveToPoints *)MEM_callocN( sizeof(NodeGeometryCurveToPoints), __func__); - data->mode = GEO_NODE_CURVE_SAMPLE_COUNT; + data->mode = GEO_NODE_CURVE_RESAMPLE_COUNT; node->storage = data; } static void geo_node_curve_to_points_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)node->storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; bNodeSocket *count_socket = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *length_socket = count_socket->next; - nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_SAMPLE_COUNT); - nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); -} - -/** - * Evaluate splines in parallel to speed up the rest of the node's execution. - */ -static void evaluate_splines(Span splines) -{ - threading::parallel_for_each(splines, [](const SplinePtr &spline) { - /* These functions fill the corresponding caches on each spline. */ - spline->evaluated_positions(); - spline->evaluated_tangents(); - spline->evaluated_normals(); - spline->evaluated_lengths(); - }); + nodeSetSocketAvailability(count_socket, mode == GEO_NODE_CURVE_RESAMPLE_COUNT); + nodeSetSocketAvailability(length_socket, mode == GEO_NODE_CURVE_RESAMPLE_LENGTH); } static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, - const GeometryNodeCurveSampleMode mode, + const GeometryNodeCurveResampleMode mode, const CurveEval &curve, const Span splines) { const int size = curve.splines().size(); switch (mode) { - case GEO_NODE_CURVE_SAMPLE_COUNT: { - const int count = params.extract_input("Count"); + case GEO_NODE_CURVE_RESAMPLE_COUNT: { + const int count = params.get_input("Count"); if (count < 1) { return {0}; } @@ -94,9 +83,9 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, } return offsets; } - case GEO_NODE_CURVE_SAMPLE_LENGTH: { + case GEO_NODE_CURVE_RESAMPLE_LENGTH: { /* Don't allow asymptotic count increase for low resolution values. */ - const float resolution = std::max(params.extract_input("Length"), 0.0001f); + const float resolution = std::max(params.get_input("Length"), 0.0001f); Array offsets(size + 1); int offset = 0; for (const int i : IndexRange(size)) { @@ -106,7 +95,7 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, offsets.last() = offset; return offsets; } - case GEO_NODE_CURVE_SAMPLE_EVALUATED: { + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: { return curve.evaluated_point_offsets(); } } @@ -114,9 +103,12 @@ static Array calculate_spline_point_offsets(GeoNodeExecParams ¶ms, return {0}; } -static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, - const AttributeIDRef &attribute_id, - const CustomDataType data_type) +/** + * \note: Relies on the fact that all attributes on point clouds are stored contiguously. + */ +static GMutableSpan ensure_point_attribute(PointCloudComponent &points, + const AttributeIDRef &attribute_id, + const CustomDataType data_type) { points.attribute_try_create(attribute_id, ATTR_DOMAIN_POINT, data_type, AttributeInitDefault()); WriteAttributeLookup attribute = points.attribute_try_get_for_write(attribute_id); @@ -125,50 +117,75 @@ static GMutableSpan create_attribute_and_retrieve_span(PointCloudComponent &poin } template -static MutableSpan create_attribute_and_retrieve_span(PointCloudComponent &points, - const AttributeIDRef &attribute_id) +static MutableSpan ensure_point_attribute(PointCloudComponent &points, + const AttributeIDRef &attribute_id) { - GMutableSpan attribute = create_attribute_and_retrieve_span( + GMutableSpan attribute = ensure_point_attribute( points, attribute_id, bke::cpp_type_to_custom_data_type(CPPType::get())); return attribute.typed(); } -CurveToPointsResults curve_to_points_create_result_attributes(PointCloudComponent &points, - const CurveEval &curve) +namespace { +struct AnonymousAttributeIDs { + StrongAnonymousAttributeID tangent_id; + StrongAnonymousAttributeID normal_id; + StrongAnonymousAttributeID rotation_id; +}; + +struct ResultAttributes { + MutableSpan positions; + MutableSpan radii; + + Map point_attributes; + + MutableSpan tangents; + MutableSpan normals; + MutableSpan rotations; +}; +} // namespace + +static ResultAttributes create_attributes_for_transfer(PointCloudComponent &points, + const CurveEval &curve, + const AnonymousAttributeIDs &attributes) { - CurveToPointsResults attributes; + ResultAttributes outputs; - attributes.result_size = points.attribute_domain_size(ATTR_DOMAIN_POINT); + outputs.positions = ensure_point_attribute(points, "position"); + outputs.radii = ensure_point_attribute(points, "radius"); - attributes.positions = create_attribute_and_retrieve_span(points, "position"); - attributes.radii = create_attribute_and_retrieve_span(points, "radius"); - attributes.tilts = create_attribute_and_retrieve_span(points, "tilt"); + if (attributes.tangent_id) { + outputs.tangents = ensure_point_attribute(points, attributes.tangent_id.get()); + } + if (attributes.normal_id) { + outputs.normals = ensure_point_attribute(points, attributes.normal_id.get()); + } + if (attributes.rotation_id) { + outputs.rotations = ensure_point_attribute(points, attributes.rotation_id.get()); + } - /* Because of the invariants of the curve component, we use the attributes of the - * first spline as a representative for the attribute meta data all splines. */ + /* Because of the invariants of the curve component, we use the attributes of the first spline + * as a representative for the attribute meta data all splines. Attributes from the spline domain + * are handled separately. */ curve.splines().first()->attributes.foreach_attribute( - [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - attributes.point_attributes.add_new( - attribute_id, - create_attribute_and_retrieve_span(points, attribute_id, meta_data.data_type)); + [&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (id.should_be_kept()) { + outputs.point_attributes.add_new( + id, ensure_point_attribute(points, id, meta_data.data_type)); + } return true; }, ATTR_DOMAIN_POINT); - attributes.tangents = create_attribute_and_retrieve_span(points, "tangent"); - attributes.normals = create_attribute_and_retrieve_span(points, "normal"); - attributes.rotations = create_attribute_and_retrieve_span(points, "rotation"); - - return attributes; + return outputs; } /** * TODO: For non-poly splines, this has double copies that could be avoided as part * of a general look at optimizing uses of #Spline::interpolate_to_evaluated. */ -static void copy_evaluated_point_attributes(Span splines, - Span offsets, - CurveToPointsResults &data) +static void copy_evaluated_point_attributes(const Span splines, + const Span offsets, + ResultAttributes &data) { threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { for (const int i : range) { @@ -178,28 +195,30 @@ static void copy_evaluated_point_attributes(Span splines, data.positions.slice(offset, size).copy_from(spline.evaluated_positions()); spline.interpolate_to_evaluated(spline.radii())->materialize(data.radii.slice(offset, size)); - spline.interpolate_to_evaluated(spline.tilts())->materialize(data.tilts.slice(offset, size)); - for (const Map::Item &item : data.point_attributes.items()) { + for (const Map::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; - GMutableSpan point_span = item.value; + const GMutableSpan dst = item.value; BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); - spline.interpolate_to_evaluated(spline_span) - ->materialize(point_span.slice(offset, size).data()); + spline.interpolate_to_evaluated(spline_span)->materialize(dst.slice(offset, size).data()); } - data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); - data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); + if (!data.tangents.is_empty()) { + data.tangents.slice(offset, size).copy_from(spline.evaluated_tangents()); + } + if (!data.normals.is_empty()) { + data.normals.slice(offset, size).copy_from(spline.evaluated_normals()); + } } }); } -static void copy_uniform_sample_point_attributes(Span splines, - Span offsets, - CurveToPointsResults &data) +static void copy_uniform_sample_point_attributes(const Span splines, + const Span offsets, + ResultAttributes &data) { threading::parallel_for(splines.index_range(), 64, [&](IndexRange range) { for (const int i : range) { @@ -214,76 +233,60 @@ static void copy_uniform_sample_point_attributes(Span splines, spline.sample_with_index_factors( spline.evaluated_positions(), uniform_samples, data.positions.slice(offset, size)); - - spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline.radii()), + spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline.radii()), uniform_samples, data.radii.slice(offset, size)); - spline.sample_with_index_factors(spline.interpolate_to_evaluated(spline.tilts()), - uniform_samples, - data.tilts.slice(offset, size)); - - for (const Map::Item &item : data.point_attributes.items()) { + for (const Map::Item item : data.point_attributes.items()) { const AttributeIDRef attribute_id = item.key; - GMutableSpan point_span = item.value; + const GMutableSpan dst = item.value; BLI_assert(spline.attributes.get_for_read(attribute_id)); GSpan spline_span = *spline.attributes.get_for_read(attribute_id); spline.sample_with_index_factors(*spline.interpolate_to_evaluated(spline_span), uniform_samples, - point_span.slice(offset, size)); + dst.slice(offset, size)); } - spline.sample_with_index_factors( - spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); - for (float3 &tangent : data.tangents) { - tangent.normalize(); + if (!data.tangents.is_empty()) { + spline.sample_with_index_factors( + spline.evaluated_tangents(), uniform_samples, data.tangents.slice(offset, size)); + for (float3 &tangent : data.tangents) { + tangent.normalize(); + } } - spline.sample_with_index_factors( - spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); - for (float3 &normals : data.normals) { - normals.normalize(); + if (!data.normals.is_empty()) { + spline.sample_with_index_factors( + spline.evaluated_normals(), uniform_samples, data.normals.slice(offset, size)); + for (float3 &normals : data.normals) { + normals.normalize(); + } } } }); } -/** - * \note Use attributes from the curve component rather than the attribute data directly on the - * attribute storage to allow reading the virtual spline attributes like "cyclic" and "resolution". - */ -static void copy_spline_domain_attributes(const CurveComponent &curve_component, - Span offsets, +static void copy_spline_domain_attributes(const CurveEval &curve, + const Span offsets, PointCloudComponent &points) { - curve_component.attribute_foreach( + curve.attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (meta_data.domain != ATTR_DOMAIN_CURVE) { - return true; - } - GVArrayPtr spline_attribute = curve_component.attribute_get_for_read( - attribute_id, ATTR_DOMAIN_CURVE, meta_data.data_type); - const CPPType &type = spline_attribute->type(); + const GSpan curve_attribute = *curve.attributes.get_for_read(attribute_id); + const CPPType &type = curve_attribute.type(); + const GMutableSpan dst = ensure_point_attribute(points, attribute_id, meta_data.data_type); - OutputAttribute result_attribute = points.attribute_try_get_for_output_only( - attribute_id, ATTR_DOMAIN_POINT, meta_data.data_type); - GMutableSpan result = result_attribute.as_span(); - - for (const int i : IndexRange(spline_attribute->size())) { + for (const int i : curve.splines().index_range()) { const int offset = offsets[i]; const int size = offsets[i + 1] - offsets[i]; - if (size != 0) { - BUFFER_FOR_CPP_TYPE_VALUE(type, buffer); - spline_attribute->get(i, buffer); - type.fill_assign_n(buffer, result[offset], size); - } + type.fill_assign_n(curve_attribute[i], dst[offset], size); } - result_attribute.save(); return true; - }); + }, + ATTR_DOMAIN_CURVE); } void curve_create_default_rotation_attribute(Span tangents, @@ -301,55 +304,74 @@ void curve_create_default_rotation_attribute(Span tangents, static void geo_node_curve_to_points_exec(GeoNodeExecParams params) { NodeGeometryCurveToPoints &node_storage = *(NodeGeometryCurveToPoints *)params.node().storage; - const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; - GeometrySet geometry_set = params.extract_input("Geometry"); + const GeometryNodeCurveResampleMode mode = (GeometryNodeCurveResampleMode)node_storage.mode; + GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + AnonymousAttributeIDs attribute_outputs; + attribute_outputs.tangent_id = StrongAnonymousAttributeID("Tangent"); + attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); + attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); - if (!geometry_set.has_curve()) { - params.set_output("Geometry", GeometrySet()); - return; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_curve()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + const CurveEval &curve = *geometry_set.get_curve_for_read(); + const Span splines = curve.splines(); + curve.assert_valid_point_attributes(); + + const Array offsets = calculate_spline_point_offsets(params, mode, curve, splines); + const int total_size = offsets.last(); + if (total_size == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + geometry_set.replace_pointcloud(BKE_pointcloud_new_nomain(total_size)); + PointCloudComponent &points = geometry_set.get_component_for_write(); + ResultAttributes point_attributes = create_attributes_for_transfer( + points, curve, attribute_outputs); + + switch (mode) { + case GEO_NODE_CURVE_RESAMPLE_COUNT: + case GEO_NODE_CURVE_RESAMPLE_LENGTH: + copy_uniform_sample_point_attributes(splines, offsets, point_attributes); + break; + case GEO_NODE_CURVE_RESAMPLE_EVALUATED: + copy_evaluated_point_attributes(splines, offsets, point_attributes); + break; + } + + copy_spline_domain_attributes(curve, offsets, points); + + if (!point_attributes.rotations.is_empty()) { + curve_create_default_rotation_attribute( + point_attributes.tangents, point_attributes.normals, point_attributes.rotations); + } + + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES, GEO_COMPONENT_TYPE_POINT_CLOUD}); + }); + + params.set_output("Points", std::move(geometry_set)); + if (attribute_outputs.tangent_id) { + params.set_output( + "Tangent", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.tangent_id), + params.attribute_producer_name())); } - - const CurveComponent &curve_component = *geometry_set.get_component_for_read(); - const CurveEval &curve = *curve_component.get_for_read(); - const Span splines = curve.splines(); - curve.assert_valid_point_attributes(); - - evaluate_splines(splines); - - const Array offsets = calculate_spline_point_offsets(params, mode, curve, splines); - const int total_size = offsets.last(); - if (total_size == 0) { - params.set_output("Geometry", GeometrySet()); - return; + if (attribute_outputs.normal_id) { + params.set_output( + "Normal", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); } - - GeometrySet result = GeometrySet::create_with_pointcloud(BKE_pointcloud_new_nomain(total_size)); - PointCloudComponent &point_component = result.get_component_for_write(); - - CurveToPointsResults new_attributes = curve_to_points_create_result_attributes(point_component, - curve); - switch (mode) { - case GEO_NODE_CURVE_SAMPLE_COUNT: - case GEO_NODE_CURVE_SAMPLE_LENGTH: - copy_uniform_sample_point_attributes(splines, offsets, new_attributes); - break; - case GEO_NODE_CURVE_SAMPLE_EVALUATED: - copy_evaluated_point_attributes(splines, offsets, new_attributes); - break; + if (attribute_outputs.rotation_id) { + params.set_output( + "Rotation", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); } - - copy_spline_domain_attributes(curve_component, offsets, point_component); - curve_create_default_rotation_attribute( - new_attributes.tangents, new_attributes.normals, new_attributes.rotations); - - /* The default radius is way too large for points, divide by 10. */ - for (float &radius : new_attributes.radii) { - radius *= 0.1f; - } - - params.set_output("Geometry", std::move(result)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc index 4ad311d63c5..4e1a2910c7c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_curve_trim.cc @@ -28,12 +28,24 @@ namespace blender::nodes { static void geo_node_curve_trim_declare(NodeDeclarationBuilder &b) { - b.add_input("Curve"); - b.add_input("Start").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("End").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_input("Start", "Start_001").min(0.0f).subtype(PROP_DISTANCE); - b.add_input("End", "End_001").min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Curve"); + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Start")).min(0.0f).max(1.0f).subtype(PROP_FACTOR).supports_field(); + b.add_input(N_("End")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR) + .supports_field(); + b.add_input(N_("Start"), "Start_001") + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_input(N_("End"), "End_001") + .min(0.0f) + .default_value(1.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output(N_("Curve")); } static void geo_node_curve_trim_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -46,25 +58,24 @@ static void geo_node_curve_trim_init(bNodeTree *UNUSED(tree), bNode *node) NodeGeometryCurveTrim *data = (NodeGeometryCurveTrim *)MEM_callocN(sizeof(NodeGeometryCurveTrim), __func__); - data->mode = GEO_NODE_CURVE_INTERPOLATE_FACTOR; + data->mode = GEO_NODE_CURVE_SAMPLE_FACTOR; node->storage = data; } static void geo_node_curve_trim_update(bNodeTree *UNUSED(ntree), bNode *node) { const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)node->storage; - const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) - node_storage.mode; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; bNodeSocket *start_fac = ((bNodeSocket *)node->inputs.first)->next; bNodeSocket *end_fac = start_fac->next; bNodeSocket *start_len = end_fac->next; bNodeSocket *end_len = start_len->next; - nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); - nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR); - nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); - nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH); + nodeSetSocketAvailability(start_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(end_fac, mode == GEO_NODE_CURVE_SAMPLE_FACTOR); + nodeSetSocketAvailability(start_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); + nodeSetSocketAvailability(end_len, mode == GEO_NODE_CURVE_SAMPLE_LENGTH); } struct TrimLocation { @@ -124,19 +135,19 @@ static void linear_trim_to_output_data(const TrimLocation &start, /* Look up the control points to the left and right of factor, and get the factor between them. */ static TrimLocation lookup_control_point_position(const Spline::LookupResult &lookup, - Span control_point_offsets) + const BezierSpline &spline) { - const int *left_offset = std::lower_bound( - control_point_offsets.begin(), control_point_offsets.end(), lookup.evaluated_index); - const int index = left_offset - control_point_offsets.begin(); - const int left = control_point_offsets[index] > lookup.evaluated_index ? index - 1 : index; - const int right = left + 1; + Span offsets = spline.control_point_offsets(); - const float factor = std::clamp( - (lookup.evaluated_index + lookup.factor - control_point_offsets[left]) / - (control_point_offsets[right] - control_point_offsets[left]), - 0.0f, - 1.0f); + const int *offset = std::lower_bound(offsets.begin(), offsets.end(), lookup.evaluated_index); + const int index = offset - offsets.begin(); + + const int left = offsets[index] > lookup.evaluated_index ? index - 1 : index; + const int right = left == (spline.size() - 1) ? 0 : left + 1; + + const float offset_in_segment = lookup.evaluated_index + lookup.factor - offsets[left]; + const int segment_eval_size = offsets[left + 1] - offsets[left]; + const float factor = std::clamp(offset_in_segment / segment_eval_size, 0.0f, 1.0f); return {left, right, factor}; } @@ -236,10 +247,11 @@ static void trim_bezier_spline(Spline &spline, const Spline::LookupResult &end_lookup) { BezierSpline &bezier_spline = static_cast(spline); - Span control_offsets = bezier_spline.control_point_offsets(); - const TrimLocation start = lookup_control_point_position(start_lookup, control_offsets); - TrimLocation end = lookup_control_point_position(end_lookup, control_offsets); + const TrimLocation start = lookup_control_point_position(start_lookup, bezier_spline); + TrimLocation end = lookup_control_point_position(end_lookup, bezier_spline); + + const Span control_offsets = bezier_spline.control_point_offsets(); /* The number of control points in the resulting spline. */ const int size = end.right_index - start.left_index + 1; @@ -321,70 +333,226 @@ static void trim_bezier_spline(Spline &spline, bezier_spline.resize(size); } -static void geo_node_curve_trim_exec(GeoNodeExecParams params) +static void trim_spline(SplinePtr &spline, + const Spline::LookupResult start, + const Spline::LookupResult end) { - const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; - const GeometryNodeCurveInterpolateMode mode = (GeometryNodeCurveInterpolateMode) - node_storage.mode; + switch (spline->type()) { + case Spline::Type::Bezier: + trim_bezier_spline(*spline, start, end); + break; + case Spline::Type::Poly: + trim_poly_spline(*spline, start, end); + break; + case Spline::Type::NURBS: + spline = std::make_unique(trim_nurbs_spline(*spline, start, end)); + break; + } + spline->mark_cache_invalid(); +} - GeometrySet geometry_set = params.extract_input("Curve"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); +template +static void to_single_point_data(const TrimLocation &trim, MutableSpan data) +{ + data.first() = mix2(trim.factor, data[trim.left_index], data[trim.right_index]); +} +template +static void to_single_point_data(const TrimLocation &trim, Span src, MutableSpan dst) +{ + dst.first() = mix2(trim.factor, src[trim.left_index], src[trim.right_index]); +} + +static void to_single_point_bezier(Spline &spline, const Spline::LookupResult &lookup) +{ + BezierSpline &bezier = static_cast(spline); + + const TrimLocation trim = lookup_control_point_position(lookup, bezier); + + const BezierSpline::InsertResult new_point = bezier.calculate_segment_insertion( + trim.left_index, trim.right_index, trim.factor); + bezier.positions().first() = new_point.position; + bezier.handle_types_left().first() = BezierSpline::HandleType::Free; + bezier.handle_types_right().first() = BezierSpline::HandleType::Free; + bezier.handle_positions_left().first() = new_point.left_handle; + bezier.handle_positions_right().first() = new_point.right_handle; + + to_single_point_data(trim, bezier.radii()); + to_single_point_data(trim, bezier.tilts()); + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional data = spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { + using T = decltype(dummy); + to_single_point_data(trim, data->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + spline.resize(1); +} + +static void to_single_point_poly(Spline &spline, const Spline::LookupResult &lookup) +{ + const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; + + to_single_point_data(trim, spline.positions()); + to_single_point_data(trim, spline.radii()); + to_single_point_data(trim, spline.tilts()); + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &UNUSED(meta_data)) { + std::optional data = spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(data->type(), [&](auto dummy) { + using T = decltype(dummy); + to_single_point_data(trim, data->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + spline.resize(1); +} + +static PolySpline to_single_point_nurbs(const Spline &spline, const Spline::LookupResult &lookup) +{ + /* Since this outputs a poly spline, the evaluated indices are the control point indices. */ + const TrimLocation trim{lookup.evaluated_index, lookup.next_evaluated_index, lookup.factor}; + + /* Create poly spline and copy trimmed data to it. */ + PolySpline new_spline; + new_spline.resize(1); + + spline.attributes.foreach_attribute( + [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { + new_spline.attributes.create(attribute_id, meta_data.data_type); + std::optional src = spline.attributes.get_for_read(attribute_id); + std::optional dst = new_spline.attributes.get_for_write(attribute_id); + attribute_math::convert_to_static_type(src->type(), [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed eval_data = spline.interpolate_to_evaluated(src->typed()); + to_single_point_data(trim, eval_data->get_internal_span(), dst->typed()); + }); + return true; + }, + ATTR_DOMAIN_POINT); + + to_single_point_data(trim, spline.evaluated_positions(), new_spline.positions()); + + GVArray_Typed evaluated_radii = spline.interpolate_to_evaluated(spline.radii()); + to_single_point_data(trim, evaluated_radii->get_internal_span(), new_spline.radii()); + + GVArray_Typed evaluated_tilts = spline.interpolate_to_evaluated(spline.tilts()); + to_single_point_data(trim, evaluated_tilts->get_internal_span(), new_spline.tilts()); + + return new_spline; +} + +static void to_single_point_spline(SplinePtr &spline, const Spline::LookupResult &lookup) +{ + switch (spline->type()) { + case Spline::Type::Bezier: + to_single_point_bezier(*spline, lookup); + break; + case Spline::Type::Poly: + to_single_point_poly(*spline, lookup); + break; + case Spline::Type::NURBS: + spline = std::make_unique(to_single_point_nurbs(*spline, lookup)); + break; + } +} + +static void geometry_set_curve_trim(GeometrySet &geometry_set, + const GeometryNodeCurveSampleMode mode, + Field &start_field, + Field &end_field) +{ if (!geometry_set.has_curve()) { - params.set_output("Curve", std::move(geometry_set)); return; } - CurveComponent &curve_component = geometry_set.get_component_for_write(); - CurveEval &curve = *curve_component.get_for_write(); - MutableSpan splines = curve.splines(); + CurveComponent &component = geometry_set.get_component_for_write(); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); - const float start = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? - params.extract_input("Start") : - params.extract_input("Start_001"); - const float end = mode == GEO_NODE_CURVE_INTERPOLATE_FACTOR ? - params.extract_input("End") : - params.extract_input("End_001"); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add(start_field); + evaluator.add(end_field); + evaluator.evaluate(); + const blender::VArray &starts = evaluator.get_evaluated(0); + const blender::VArray &ends = evaluator.get_evaluated(1); + + CurveEval &curve = *geometry_set.get_curve_for_write(); + MutableSpan splines = curve.splines(); threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { for (const int i : range) { - Spline &spline = *splines[i]; + SplinePtr &spline = splines[i]; - /* Currently this node doesn't support cyclic splines, it could in the future though. */ - if (spline.is_cyclic()) { + /* Currently trimming cyclic splines is not supported. It could be in the future though. */ + if (spline->is_cyclic()) { continue; } - /* Return a spline with one point instead of implicitly - * reversing the spline or switching the parameters. */ - if (end < start) { - spline.resize(1); + if (spline->evaluated_edges_size() == 0) { continue; } - const Spline::LookupResult start_lookup = - (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(start, 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)); - const Spline::LookupResult end_lookup = - (mode == GEO_NODE_CURVE_INTERPOLATE_LENGTH) ? - spline.lookup_evaluated_length(std::clamp(end, 0.0f, spline.length())) : - spline.lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f)); - - switch (spline.type()) { - case Spline::Type::Bezier: - trim_bezier_spline(spline, start_lookup, end_lookup); - break; - case Spline::Type::Poly: - trim_poly_spline(spline, start_lookup, end_lookup); - break; - case Spline::Type::NURBS: - splines[i] = std::make_unique( - trim_nurbs_spline(spline, start_lookup, end_lookup)); - break; + const float length = spline->length(); + if (length == 0.0f) { + continue; + } + + const float start = starts[i]; + const float end = ends[i]; + + /* When the start and end samples are reversed, instead of implicitly reversing the spline + * or switching the parameters, create a single point spline with the end sample point. */ + if (end <= start) { + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + to_single_point_spline(spline, + spline->lookup_evaluated_length(std::clamp(start, 0.0f, length))); + } + else { + to_single_point_spline(spline, + spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f))); + } + continue; + } + + if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + trim_spline(spline, + spline->lookup_evaluated_length(std::clamp(start, 0.0f, length)), + spline->lookup_evaluated_length(std::clamp(end, 0.0f, length))); + } + else { + trim_spline(spline, + spline->lookup_evaluated_factor(std::clamp(start, 0.0f, 1.0f)), + spline->lookup_evaluated_factor(std::clamp(end, 0.0f, 1.0f))); } - splines[i]->mark_cache_invalid(); } }); +} + +static void geo_node_curve_trim_exec(GeoNodeExecParams params) +{ + const NodeGeometryCurveTrim &node_storage = *(NodeGeometryCurveTrim *)params.node().storage; + const GeometryNodeCurveSampleMode mode = (GeometryNodeCurveSampleMode)node_storage.mode; + + GeometrySet geometry_set = params.extract_input("Curve"); + + if (mode == GEO_NODE_CURVE_SAMPLE_FACTOR) { + Field start_field = params.extract_input>("Start"); + Field end_field = params.extract_input>("End"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + geometry_set_curve_trim(geometry_set, mode, start_field, end_field); + }); + } + else if (mode == GEO_NODE_CURVE_SAMPLE_LENGTH) { + Field start_field = params.extract_input>("Start_001"); + Field end_field = params.extract_input>("End_001"); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + geometry_set_curve_trim(geometry_set, mode, start_field, end_field); + }); + } params.set_output("Curve", std::move(geometry_set)); } @@ -394,7 +562,7 @@ static void geo_node_curve_trim_exec(GeoNodeExecParams params) void register_node_type_geo_curve_trim() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_CURVE_TRIM, "Curve Trim", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_TRIM_CURVE, "Trim Curve", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_curve_trim_exec; ntype.draw_buttons = blender::nodes::geo_node_curve_trim_layout; ntype.declare = blender::nodes::geo_node_curve_trim_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 1e2f652cd78..e0a3faaefb0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -14,6 +14,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "BLI_array.hh" #include "DNA_mesh_types.h" @@ -47,10 +50,36 @@ namespace blender::nodes { static void geo_node_delete_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Selection"); - b.add_input("Invert"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")) + .default_value(true) + .hide_value() + .supports_field() + .description(N_("The parts of the geometry to be deleted")); + b.add_output(N_("Geometry")); +} + +static void geo_node_delete_geometry_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + const bNode *node = static_cast(ptr->data); + const NodeGeometryDeleteGeometry &storage = *(const NodeGeometryDeleteGeometry *)node->storage; + const AttributeDomain domain = static_cast(storage.domain); + + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + /* Only show the mode when it is relevant. */ + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE, ATTR_DOMAIN_FACE)) { + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); + } +} + +static void geo_node_delete_geometry_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryDeleteGeometry *data = (NodeGeometryDeleteGeometry *)MEM_callocN( + sizeof(NodeGeometryDeleteGeometry), __func__); + data->domain = ATTR_DOMAIN_POINT; + data->mode = GEO_NODE_DELETE_GEOMETRY_MODE_ALL; + + node->storage = data; } template static void copy_data(Span data, MutableSpan r_data, IndexMask mask) @@ -60,6 +89,185 @@ template static void copy_data(Span data, MutableSpan r_data, } } +/** Utility function for making an IndexMask from a boolean selection. The indices vector should + * live at least as long as the returned IndexMask. + */ +static IndexMask index_mask_indices(Span mask, const bool invert, Vector &indices) +{ + for (const int i : mask.index_range()) { + if (mask[i] != invert) { + indices.append(i); + } + } + return IndexMask(indices); +} + +/** Utility function for making an IndexMask from an array of integers, where the negative integers + * are seen as false. The indices vector should live at least as long as the returned IndexMask. + */ +static IndexMask index_mask_indices(Span mask, + const int num_indices, + Vector &indices) +{ + indices.clear(); + indices.reserve(num_indices); + for (const int i : mask.index_range()) { + if (mask[i] >= 0) { + indices.append_unchecked(i); + } + } + return IndexMask(indices); +} + +/** + * Copies the attributes with a domain in `domains` to `result_component`. + */ +static void copy_attributes(const Map &attributes, + const GeometryComponent &in_component, + GeometryComponent &result_component, + const Span domains) +{ + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); + if (!attribute) { + continue; + } + + /* Only copy if it is on a domain we want. */ + if (!domains.contains(attribute.domain)) { + continue; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); + + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( + attribute_id, attribute.domain, data_type); + + if (!result_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Span span{*attribute.varray}; + MutableSpan out_span = result_attribute.as_span(); + out_span.copy_from(span); + }); + result_attribute.save(); + } +} + +/** + * For each attribute with a domain in `domains` it copies the parts of that attribute which lie in + * the mask to `result_component`. + */ +static void copy_attributes_based_on_mask(const Map &attributes, + const GeometryComponent &in_component, + GeometryComponent &result_component, + const AttributeDomain domain, + const IndexMask mask) +{ + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + ReadAttributeLookup attribute = in_component.attribute_try_get_for_read(attribute_id); + if (!attribute) { + continue; + } + + /* Only copy if it is on a domain we want. */ + if (domain != attribute.domain) { + continue; + } + const CustomDataType data_type = bke::cpp_type_to_custom_data_type(attribute.varray->type()); + + OutputAttribute result_attribute = result_component.attribute_try_get_for_output_only( + attribute_id, attribute.domain, data_type); + + if (!result_attribute) { + continue; + } + + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Span span{*attribute.varray}; + MutableSpan out_span = result_attribute.as_span(); + copy_data(span, out_span, mask); + }); + result_attribute.save(); + } +} + +static void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span edge_map) +{ + BLI_assert(src_mesh.totedge == edge_map.size()); + for (const int i_src : IndexRange(src_mesh.totedge)) { + const int i_dst = edge_map[i_src]; + if (ELEM(i_dst, -1, -2)) { + continue; + } + + const MEdge &e_src = src_mesh.medge[i_src]; + MEdge &e_dst = dst_mesh.medge[i_dst]; + + e_dst = e_src; + e_dst.v1 = e_src.v1; + e_dst.v2 = e_src.v2; + } +} + +/* Faces and edges changed but vertices are the same. */ +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span edge_map, + Span masked_poly_indices, + Span new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = ml_src[i].v; + ml_dst[i].e = edge_map[ml_src[i].e]; + } + } +} + +/* Only faces changed. */ +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span masked_poly_indices, + Span new_loop_starts) +{ + for (const int i_dst : masked_poly_indices.index_range()) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = ml_src[i].v; + ml_dst[i].e = ml_src[i].e; + } + } +} + static void spline_copy_builtin_attributes(const Spline &spline, Spline &r_spline, const IndexMask mask) @@ -115,6 +323,10 @@ static void copy_dynamic_attributes(const CustomDataAttributes &src, ATTR_DOMAIN_POINT); } +/** + * Deletes points in the spline. Those not in the mask are deleted. The spline is not split into + * multiple newer splines. + */ static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) { SplinePtr new_spline = spline.copy_only_settings(); @@ -126,9 +338,10 @@ static SplinePtr spline_delete(const Spline &spline, const IndexMask mask) return new_spline; } -static std::unique_ptr curve_delete(const CurveEval &input_curve, - const StringRef name, - const bool invert) +static std::unique_ptr curve_separate(const CurveEval &input_curve, + const Span selection, + const AttributeDomain selection_domain, + const bool invert) { Span input_splines = input_curve.splines(); std::unique_ptr output_curve = std::make_unique(); @@ -136,28 +349,34 @@ static std::unique_ptr curve_delete(const CurveEval &input_curve, /* Keep track of which splines were copied to the result to copy spline domain attributes. */ Vector copied_splines; - if (input_curve.attributes.get_for_read(name)) { - GVArray_Typed selection = input_curve.attributes.get_for_read(name, false); - for (const int i : input_splines.index_range()) { - if (selection[i] == invert) { + if (selection_domain == ATTR_DOMAIN_CURVE) { + /* Operates on each of the splines as a whole, i.e. not on the points in the splines + * themselves. */ + for (const int i : selection.index_range()) { + if (selection[i] != invert) { output_curve->add_spline(input_splines[i]->copy()); copied_splines.append(i); } } } else { + /* Operates on the points in the splines themselves. */ + /* Reuse index vector for each spline. */ Vector indices_to_copy; + int selection_index = 0; for (const int i : input_splines.index_range()) { const Spline &spline = *input_splines[i]; - GVArray_Typed selection = spline.attributes.get_for_read(name, false); indices_to_copy.clear(); for (const int i_point : IndexRange(spline.size())) { - if (selection[i_point] == invert) { + if (selection[selection_index] != invert) { + /* Append i_point instead of selection_index because we need indices local to the spline + * for copying. */ indices_to_copy.append(i_point); } + selection_index++; } /* Avoid creating an empty spline. */ @@ -182,49 +401,72 @@ static std::unique_ptr curve_delete(const CurveEval &input_curve, return output_curve; } -static void delete_curve_selection(const CurveComponent &in_component, - CurveComponent &r_component, - const StringRef selection_name, - const bool invert) +static void separate_curve_selection(GeometrySet &geometry_set, + const Field &selection_field, + const AttributeDomain selection_domain, + const bool invert) { - std::unique_ptr r_curve = curve_delete( - *in_component.get_for_read(), selection_name, invert); + const CurveComponent &src_component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{src_component, selection_domain}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_component.attribute_domain_size(selection_domain)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span &selection = selection_evaluator.get_evaluated(0); + std::unique_ptr r_curve = curve_separate( + *src_component.get_for_read(), selection, selection_domain, invert); if (r_curve) { - r_component.replace(r_curve.release()); + geometry_set.replace_curve(r_curve.release()); } else { - r_component.clear(); + geometry_set.replace_curve(nullptr); } } -static void delete_point_cloud_selection(const PointCloudComponent &in_component, - PointCloudComponent &out_component, - const StringRef selection_name, - const bool invert) +static void separate_point_cloud_selection(GeometrySet &geometry_set, + const Field &selection_field, + const bool invert) { - const GVArray_Typed selection_attribute = in_component.attribute_get_for_read( - selection_name, ATTR_DOMAIN_POINT, false); - VArray_Span selection{selection_attribute}; + const PointCloudComponent &src_points = + *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{src_points, ATTR_DOMAIN_POINT}; + + fn::FieldEvaluator selection_evaluator{field_context, + src_points.attribute_domain_size(ATTR_DOMAIN_POINT)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span &selection = selection_evaluator.get_evaluated(0); + + Vector indices; + const IndexMask mask = index_mask_indices(selection, invert, indices); + const int total = mask.size(); + PointCloud *pointcloud = BKE_pointcloud_new_nomain(total); - const int total = selection.count(invert); if (total == 0) { - out_component.clear(); + geometry_set.replace_pointcloud(pointcloud); return; } - out_component.replace(BKE_pointcloud_new_nomain(total)); - /* Invert the inversion, because this deletes the selected points instead of keeping them. */ - copy_point_attributes_based_on_mask(in_component, out_component, selection, !invert); + PointCloudComponent dst_points; + dst_points.replace(pointcloud, GeometryOwnershipType::Editable); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + + copy_attributes_based_on_mask(attributes, src_points, dst_points, ATTR_DOMAIN_POINT, mask); + geometry_set.replace_pointcloud(pointcloud); } -static void compute_selected_vertices_from_vertex_selection(const VArray &vertex_selection, +static void compute_selected_vertices_from_vertex_selection(const Span vertex_selection, const bool invert, MutableSpan r_vertex_map, - uint *r_num_selected_vertices) + int *r_num_selected_vertices) { BLI_assert(vertex_selection.size() == r_vertex_map.size()); - uint num_selected_vertices = 0; + int num_selected_vertices = 0; for (const int i : r_vertex_map.index_range()) { if (vertex_selection[i] != invert) { r_vertex_map[i] = num_selected_vertices; @@ -239,14 +481,14 @@ static void compute_selected_vertices_from_vertex_selection(const VArray & } static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, - const VArray &vertex_selection, + const Span vertex_selection, const bool invert, MutableSpan r_edge_map, - uint *r_num_selected_edges) + int *r_num_selected_edges) { BLI_assert(mesh.totedge == r_edge_map.size()); - uint num_selected_edges = 0; + int num_selected_edges = 0; for (const int i : IndexRange(mesh.totedge)) { const MEdge &edge = mesh.medge[i]; @@ -264,19 +506,19 @@ static void compute_selected_edges_from_vertex_selection(const Mesh &mesh, } static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, - const VArray &vertex_selection, + const Span vertex_selection, const bool invert, Vector &r_selected_poly_indices, Vector &r_loop_starts, - uint *r_num_selected_polys, - uint *r_num_selected_loops) + int *r_num_selected_polys, + int *r_num_selected_loops) { BLI_assert(mesh.totvert == vertex_selection.size()); r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - uint num_selected_loops = 0; + int num_selected_loops = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; @@ -306,17 +548,17 @@ static void compute_selected_polygons_from_vertex_selection(const Mesh &mesh, */ static void compute_selected_vertices_and_edges_from_edge_selection( const Mesh &mesh, - const VArray &edge_selection, + const Span edge_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, - uint *r_num_selected_vertices, - uint *r_num_selected_edges) + int *r_num_selected_vertices, + int *r_num_selected_edges) { BLI_assert(mesh.totedge == edge_selection.size()); - uint num_selected_edges = 0; - uint num_selected_vertices = 0; + int num_selected_edges = 0; + int num_selected_vertices = 0; for (const int i : IndexRange(mesh.totedge)) { const MEdge &edge = mesh.medge[i]; if (edge_selection[i] != invert) { @@ -340,22 +582,47 @@ static void compute_selected_vertices_and_edges_from_edge_selection( *r_num_selected_edges = num_selected_edges; } +/** + * Checks for every edge if it is in `edge_selection`. + */ +static void compute_selected_edges_from_edge_selection(const Mesh &mesh, + const Span edge_selection, + const bool invert, + MutableSpan r_edge_map, + int *r_num_selected_edges) +{ + BLI_assert(mesh.totedge == edge_selection.size()); + + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totedge)) { + if (edge_selection[i] != invert) { + r_edge_map[i] = num_selected_edges; + num_selected_edges++; + } + else { + r_edge_map[i] = -1; + } + } + + *r_num_selected_edges = num_selected_edges; +} + /** * Checks for every polygon if all the edges are in `edge_selection`. If they are, then that * polygon is kept. */ static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, - const VArray &edge_selection, + const Span edge_selection, const bool invert, Vector &r_selected_poly_indices, Vector &r_loop_starts, - uint *r_num_selected_polys, - uint *r_num_selected_loops) + int *r_num_selected_polys, + int *r_num_selected_loops) { r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - uint num_selected_loops = 0; + int num_selected_loops = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; @@ -379,21 +646,48 @@ static void compute_selected_polygons_from_edge_selection(const Mesh &mesh, *r_num_selected_loops = num_selected_loops; } +/** + * Checks for every edge and polygon if all its vertices are in `vertex_selection`. + */ +static void compute_selected_mesh_data_from_vertex_selection_edge_face( + const Mesh &mesh, + const Span vertex_selection, + const bool invert, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + + compute_selected_edges_from_vertex_selection( + mesh, vertex_selection, invert, r_edge_map, r_num_selected_edges); + + compute_selected_polygons_from_vertex_selection(mesh, + vertex_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + /** * Checks for every vertex if it is in `vertex_selection`. The polygons and edges are kept if all * vertices of that polygon or edge are in the selection. */ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, - const VArray &vertex_selection, + const Span vertex_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops) + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) { compute_selected_vertices_from_vertex_selection( vertex_selection, invert, r_vertex_map, r_num_selected_vertices); @@ -410,21 +704,47 @@ static void compute_selected_mesh_data_from_vertex_selection(const Mesh &mesh, r_num_selected_loops); } +/** + * Checks for every edge if it is in `edge_selection`. The polygons are kept if all edges are in + * the selection. + */ +static void compute_selected_mesh_data_from_edge_selection_edge_face( + const Mesh &mesh, + const Span edge_selection, + const bool invert, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + compute_selected_edges_from_edge_selection( + mesh, edge_selection, invert, r_edge_map, r_num_selected_edges); + compute_selected_polygons_from_edge_selection(mesh, + edge_selection, + invert, + r_selected_poly_indices, + r_loop_starts, + r_num_selected_polys, + r_num_selected_loops); +} + /** * Checks for every edge if it is in `edge_selection`. If it is, the vertices belonging to * that edge are kept as well. The polygons are kept if all edges are in the selection. */ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, - const VArray &edge_selection, + const Span edge_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops) + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) { r_vertex_map.fill(-1); compute_selected_vertices_and_edges_from_edge_selection(mesh, @@ -443,21 +763,98 @@ static void compute_selected_mesh_data_from_edge_selection(const Mesh &mesh, r_num_selected_loops); } +/** + * Checks for every polygon if it is in `poly_selection`. + */ +static void compute_selected_polygons_from_poly_selection(const Mesh &mesh, + const Span poly_selection, + const bool invert, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + } + } + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} +/** + * Checks for every polygon if it is in `poly_selection`. If it is, the edges + * belonging to that polygon are kept as well. + */ +static void compute_selected_mesh_data_from_poly_selection_edge_face( + const Mesh &mesh, + const Span poly_selection, + const bool invert, + MutableSpan r_edge_map, + Vector &r_selected_poly_indices, + Vector &r_loop_starts, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) +{ + BLI_assert(mesh.totpoly == poly_selection.size()); + BLI_assert(mesh.totedge == r_edge_map.size()); + r_edge_map.fill(-1); + + r_selected_poly_indices.reserve(mesh.totpoly); + r_loop_starts.reserve(mesh.totloop); + + int num_selected_loops = 0; + int num_selected_edges = 0; + for (const int i : IndexRange(mesh.totpoly)) { + const MPoly &poly_src = mesh.mpoly[i]; + /* We keep this one. */ + if (poly_selection[i] != invert) { + r_selected_poly_indices.append_unchecked(i); + r_loop_starts.append_unchecked(num_selected_loops); + num_selected_loops += poly_src.totloop; + + /* Add the vertices and the edges. */ + Span loops_src(&mesh.mloop[poly_src.loopstart], poly_src.totloop); + for (const MLoop &loop : loops_src) { + /* Check first if it has not yet been added. */ + if (r_edge_map[loop.e] == -1) { + r_edge_map[loop.e] = num_selected_edges; + num_selected_edges++; + } + } + } + } + *r_num_selected_edges = num_selected_edges; + *r_num_selected_polys = r_selected_poly_indices.size(); + *r_num_selected_loops = num_selected_loops; +} + /** * Checks for every polygon if it is in `poly_selection`. If it is, the edges and vertices * belonging to that polygon are kept as well. */ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, - const VArray &poly_selection, + const Span poly_selection, const bool invert, MutableSpan r_vertex_map, MutableSpan r_edge_map, Vector &r_selected_poly_indices, Vector &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops) + int *r_num_selected_vertices, + int *r_num_selected_edges, + int *r_num_selected_polys, + int *r_num_selected_loops) { BLI_assert(mesh.totpoly == poly_selection.size()); BLI_assert(mesh.totedge == r_edge_map.size()); @@ -467,9 +864,9 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, r_selected_poly_indices.reserve(mesh.totpoly); r_loop_starts.reserve(mesh.totloop); - uint num_selected_loops = 0; - uint num_selected_vertices = 0; - uint num_selected_edges = 0; + int num_selected_loops = 0; + int num_selected_vertices = 0; + int num_selected_edges = 0; for (const int i : IndexRange(mesh.totpoly)) { const MPoly &poly_src = mesh.mpoly[i]; /* We keep this one. */ @@ -499,104 +896,247 @@ static void compute_selected_mesh_data_from_poly_selection(const Mesh &mesh, *r_num_selected_loops = num_selected_loops; } -using FillMapsFunction = void (*)(const Mesh &mesh, - const VArray &selection, - const bool invert, - MutableSpan r_vertex_map, - MutableSpan r_edge_map, - Vector &r_selected_poly_indices, - Vector &r_loop_starts, - uint *r_num_selected_vertices, - uint *r_num_selected_edges, - uint *r_num_selected_polys, - uint *r_num_selected_loops); - /** - * Delete the parts of the mesh that are in the selection. The `fill_maps_function` - * depends on the selection type: vertices, edges or faces. + * Keep the parts of the mesh that are in the selection. */ -static Mesh *delete_mesh_selection(const Mesh &mesh_in, - const VArray &selection, - const bool invert, - FillMapsFunction fill_maps_function) +static void do_mesh_separation(GeometrySet &geometry_set, + const MeshComponent &in_component, + const VArray_Span &selection, + const bool invert, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode) { - Array vertex_map(mesh_in.totvert); - uint num_selected_vertices; - - Array edge_map(mesh_in.totedge); - uint num_selected_edges; - + /* Needed in all cases. */ Vector selected_poly_indices; Vector new_loop_starts; - uint num_selected_polys; - uint num_selected_loops; + int num_selected_polys; + int num_selected_loops; - /* Fill all the maps based on the selection. We delete everything - * in the selection instead of keeping it, so we need to invert it. */ - fill_maps_function(mesh_in, - selection, - !invert, - vertex_map, - edge_map, - selected_poly_indices, - new_loop_starts, - &num_selected_vertices, - &num_selected_edges, - &num_selected_polys, - &num_selected_loops); + const Mesh &mesh_in = *in_component.get_for_read(); + Mesh *mesh_out; + MeshComponent out_component; - Mesh *result = BKE_mesh_new_nomain_from_template(&mesh_in, + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_MESH, false, attributes); + + switch (mode) { + case GEO_NODE_DELETE_GEOMETRY_MODE_ALL: { + Array vertex_map(mesh_in.totvert); + int num_selected_vertices = 0; + + Array edge_map(mesh_in.totedge); + int num_selected_edges = 0; + + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_mesh_data_from_vertex_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_mesh_data_from_edge_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_mesh_data_from_poly_selection(mesh_in, + selection, + invert, + vertex_map, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_vertices, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, num_selected_vertices, num_selected_edges, 0, num_selected_loops, num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); - /* Copy the selected parts of the mesh over to the new mesh. */ - copy_masked_vertices_to_new_mesh(mesh_in, *result, vertex_map); - copy_masked_edges_to_new_mesh(mesh_in, *result, vertex_map, edge_map); - copy_masked_polys_to_new_mesh( - mesh_in, *result, vertex_map, edge_map, selected_poly_indices, new_loop_starts); - BKE_mesh_calc_edges_loose(result); + /* Copy the selected parts of the mesh over to the new mesh. */ + copy_masked_vertices_to_new_mesh(mesh_in, *mesh_out, vertex_map); + copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, vertex_map, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *mesh_out, vertex_map, edge_map, selected_poly_indices, new_loop_starts); + break; + } + case GEO_NODE_DELETE_GEOMETRY_MODE_EDGE_FACE: { + Array edge_map(mesh_in.totedge); + int num_selected_edges = 0; + + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_mesh_data_from_vertex_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_mesh_data_from_edge_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_mesh_data_from_poly_selection_edge_face(mesh_in, + selection, + invert, + edge_map, + selected_poly_indices, + new_loop_starts, + &num_selected_edges, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template(&mesh_in, + mesh_in.totvert, + num_selected_edges, + 0, + num_selected_loops, + num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + copy_attributes(attributes, in_component, out_component, {ATTR_DOMAIN_POINT}); + copy_masked_edges_to_new_mesh(mesh_in, *mesh_out, edge_map); + copy_masked_polys_to_new_mesh( + mesh_in, *mesh_out, edge_map, selected_poly_indices, new_loop_starts); + Vector indices; + copy_attributes_based_on_mask(attributes, + in_component, + out_component, + ATTR_DOMAIN_EDGE, + index_mask_indices(edge_map, num_selected_edges, indices)); + copy_attributes_based_on_mask( + attributes, + in_component, + out_component, + ATTR_DOMAIN_FACE, + index_mask_indices(selected_poly_indices, num_selected_polys, indices)); + break; + } + case GEO_NODE_DELETE_GEOMETRY_MODE_ONLY_FACE: { + /* Fill all the maps based on the selection. */ + switch (domain) { + case ATTR_DOMAIN_POINT: + compute_selected_polygons_from_vertex_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_EDGE: + compute_selected_polygons_from_edge_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + case ATTR_DOMAIN_FACE: + compute_selected_polygons_from_poly_selection(mesh_in, + selection, + invert, + selected_poly_indices, + new_loop_starts, + &num_selected_polys, + &num_selected_loops); + break; + default: + BLI_assert_unreachable(); + break; + } + mesh_out = BKE_mesh_new_nomain_from_template( + &mesh_in, mesh_in.totvert, mesh_in.totedge, 0, num_selected_loops, num_selected_polys); + out_component.replace(mesh_out, GeometryOwnershipType::Editable); + + /* Copy the selected parts of the mesh over to the new mesh. */ + memcpy(mesh_out->mvert, mesh_in.mvert, mesh_in.totvert * sizeof(MVert)); + memcpy(mesh_out->medge, mesh_in.medge, mesh_in.totedge * sizeof(MEdge)); + copy_attributes( + attributes, in_component, out_component, {ATTR_DOMAIN_POINT, ATTR_DOMAIN_EDGE}); + copy_masked_polys_to_new_mesh(mesh_in, *mesh_out, selected_poly_indices, new_loop_starts); + Vector indices; + const IndexMask mask = index_mask_indices( + selected_poly_indices, num_selected_polys, indices); + copy_attributes_based_on_mask( + attributes, in_component, out_component, ATTR_DOMAIN_FACE, mask); + break; + } + } + + BKE_mesh_calc_edges_loose(mesh_out); /* Tag to recalculate normals later. */ - BKE_mesh_normals_tag_dirty(result); - - return result; + BKE_mesh_normals_tag_dirty(mesh_out); + geometry_set.replace_mesh(mesh_out); } -static AttributeDomain get_mesh_selection_domain(MeshComponent &component, const StringRef name) +static void separate_mesh_selection(GeometrySet &geometry_set, + const Field &selection_field, + const AttributeDomain selection_domain, + const GeometryNodeDeleteGeometryMode mode, + const bool invert) { - std::optional selection_attribute = component.attribute_get_meta_data(name); - if (!selection_attribute) { - /* The node will not do anything in this case, but this function must return something. */ - return ATTR_DOMAIN_POINT; - } + const MeshComponent &src_component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{src_component, selection_domain}; - /* Corners can't be deleted separately, so interpolate corner attributes - * to the face domain. Note that this choice is somewhat arbitrary. */ - if (selection_attribute->domain == ATTR_DOMAIN_CORNER) { - return ATTR_DOMAIN_FACE; - } - - return selection_attribute->domain; -} - -static void delete_mesh_selection(MeshComponent &component, - const Mesh &mesh_in, - const StringRef selection_name, - const bool invert) -{ - /* Figure out the best domain to use. */ - const AttributeDomain selection_domain = get_mesh_selection_domain(component, selection_name); - - /* This already checks if the attribute exists, and displays a warning in that case. */ - GVArray_Typed selection = component.attribute_get_for_read( - selection_name, selection_domain, false); + fn::FieldEvaluator selection_evaluator{field_context, + src_component.attribute_domain_size(selection_domain)}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const VArray_Span &selection = selection_evaluator.get_evaluated(0); /* Check if there is anything to delete. */ bool delete_nothing = true; for (const int i : selection.index_range()) { - if (selection[i] != invert) { + if (selection[i] == invert) { delete_nothing = false; break; } @@ -605,61 +1145,63 @@ static void delete_mesh_selection(MeshComponent &component, return; } - Mesh *mesh_out; - switch (selection_domain) { - case ATTR_DOMAIN_POINT: - mesh_out = delete_mesh_selection( - mesh_in, selection, invert, compute_selected_mesh_data_from_vertex_selection); - break; - case ATTR_DOMAIN_EDGE: - mesh_out = delete_mesh_selection( - mesh_in, selection, invert, compute_selected_mesh_data_from_edge_selection); - break; - case ATTR_DOMAIN_FACE: - mesh_out = delete_mesh_selection( - mesh_in, selection, invert, compute_selected_mesh_data_from_poly_selection); - break; - default: - BLI_assert_unreachable(); - mesh_out = nullptr; - break; + do_mesh_separation(geometry_set, src_component, selection, invert, selection_domain, mode); +} + +void separate_geometry(GeometrySet &geometry_set, + const AttributeDomain domain, + const GeometryNodeDeleteGeometryMode mode, + const Field &selection_field, + const bool invert, + bool &r_is_error) +{ + bool some_valid_domain = false; + if (geometry_set.has_pointcloud()) { + if (domain == ATTR_DOMAIN_POINT) { + separate_point_cloud_selection(geometry_set, selection_field, invert); + some_valid_domain = true; + } } - component.replace(mesh_out); + if (geometry_set.has_mesh()) { + if (domain != ATTR_DOMAIN_CURVE) { + separate_mesh_selection(geometry_set, selection_field, domain, mode, invert); + some_valid_domain = true; + } + } + if (geometry_set.has_curve()) { + if (ELEM(domain, ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE)) { + separate_curve_selection(geometry_set, selection_field, domain, invert); + some_valid_domain = true; + } + } + r_is_error = !some_valid_domain && geometry_set.has_realized_data(); } static void geo_node_delete_geometry_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); - const bool invert = params.extract_input("Invert"); - const std::string selection_name = params.extract_input("Selection"); - if (selection_name.empty()) { - params.set_output("Geometry", std::move(geometry_set)); - return; + const Field selection_field = params.extract_input>("Selection"); + + const bNode &node = params.node(); + const NodeGeometryDeleteGeometry &storage = *(const NodeGeometryDeleteGeometry *)node.storage; + const AttributeDomain domain = static_cast(storage.domain); + const GeometryNodeDeleteGeometryMode mode = static_cast( + storage.mode); + + bool all_is_error = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + bool this_is_error = false; + /* Invert here because we want to keep the things not in the selection. */ + separate_geometry(geometry_set, domain, mode, selection_field, true, this_is_error); + all_is_error &= this_is_error; + }); + if (all_is_error) { + /* Only show this if none of the instances/components actually changed. */ + params.error_message_add(NodeWarningType::Info, TIP_("No geometry with given domain")); } - GeometrySet out_set(geometry_set); - if (geometry_set.has()) { - delete_point_cloud_selection(*geometry_set.get_component_for_read(), - out_set.get_component_for_write(), - selection_name, - invert); - } - if (geometry_set.has()) { - delete_mesh_selection(out_set.get_component_for_write(), - *geometry_set.get_mesh_for_read(), - selection_name, - invert); - } - if (geometry_set.has()) { - delete_curve_selection(*geometry_set.get_component_for_read(), - out_set.get_component_for_write(), - selection_name, - invert); - } - - params.set_output("Geometry", std::move(out_set)); + params.set_output("Geometry", std::move(geometry_set)); } } // namespace blender::nodes @@ -668,10 +1210,17 @@ void register_node_type_geo_delete_geometry() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_DELETE_GEOMETRY, "Delete Geometry", NODE_CLASS_GEOMETRY, 0); + + node_type_storage(&ntype, + "NodeGeometryDeleteGeometry", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, blender::nodes::geo_node_delete_geometry_init); ntype.declare = blender::nodes::geo_node_delete_geometry_declare; ntype.geometry_node_execute = blender::nodes::geo_node_delete_geometry_exec; + ntype.draw_buttons = blender::nodes::geo_node_delete_geometry_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc new file mode 100644 index 00000000000..fa439b04da0 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_distribute_points_on_faces.cc @@ -0,0 +1,593 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_kdtree.h" +#include "BLI_noise.hh" +#include "BLI_rand.hh" +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" +#include "BKE_pointcloud.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::bke::GeometryInstanceGroup; + +namespace blender::nodes { + +static void geo_node_point_distribute_points_on_faces_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Distance Min")).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Density Max")).default_value(10.0f).min(0.0f); + b.add_input(N_("Density")).default_value(10.0f).supports_field(); + b.add_input(N_("Density Factor")) + .default_value(1.0f) + .min(0.0f) + .max(1.0f) + .subtype(PROP_FACTOR) + .supports_field(); + b.add_input(N_("Seed")); + + b.add_output(N_("Points")); + b.add_output(N_("Normal")).field_source(); + b.add_output(N_("Rotation")).subtype(PROP_EULER).field_source(); +} + +static void geo_node_point_distribute_points_on_faces_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribute_method", 0, "", ICON_NONE); +} + +static void node_point_distribute_points_on_faces_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *sock_distance_min = (bNodeSocket *)BLI_findlink(&node->inputs, 2); + bNodeSocket *sock_density_max = (bNodeSocket *)sock_distance_min->next; + bNodeSocket *sock_density = sock_density_max->next; + bNodeSocket *sock_density_factor = sock_density->next; + nodeSetSocketAvailability(sock_distance_min, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); + nodeSetSocketAvailability(sock_density_max, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); + nodeSetSocketAvailability(sock_density, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM); + nodeSetSocketAvailability(sock_density_factor, + node->custom1 == GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON); +} + +/** + * Use an arbitrary choice of axes for a usable rotation attribute directly out of this node. + */ +static float3 normal_to_euler_rotation(const float3 normal) +{ + float quat[4]; + vec_to_quat(quat, normal, OB_NEGZ, OB_POSY); + float3 rotation; + quat_to_eul(rotation, quat); + return rotation; +} + +static void sample_mesh_surface(const Mesh &mesh, + const float base_density, + const Span density_factors, + const int seed, + Vector &r_positions, + Vector &r_bary_coords, + Vector &r_looptri_indices) +{ + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const int looptri_index : looptris.index_range()) { + const MLoopTri &looptri = looptris[looptri_index]; + const int v0_loop = looptri.tri[0]; + const int v1_loop = looptri.tri[1]; + const int v2_loop = looptri.tri[2]; + const int v0_index = mesh.mloop[v0_loop].v; + const int v1_index = mesh.mloop[v1_loop].v; + const int v2_index = mesh.mloop[v2_loop].v; + const float3 v0_pos = float3(mesh.mvert[v0_index].co); + const float3 v1_pos = float3(mesh.mvert[v1_index].co); + const float3 v2_pos = float3(mesh.mvert[v2_index].co); + + float looptri_density_factor = 1.0f; + if (!density_factors.is_empty()) { + const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]); + const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); + const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); + looptri_density_factor = (v0_density_factor + v1_density_factor + v2_density_factor) / 3.0f; + } + const float area = area_tri_v3(v0_pos, v1_pos, v2_pos); + + const int looptri_seed = noise::hash(looptri_index, seed); + RandomNumberGenerator looptri_rng(looptri_seed); + + const float points_amount_fl = area * base_density * looptri_density_factor; + const float add_point_probability = fractf(points_amount_fl); + const bool add_point = add_point_probability > looptri_rng.get_float(); + const int point_amount = (int)points_amount_fl + (int)add_point; + + for (int i = 0; i < point_amount; i++) { + const float3 bary_coord = looptri_rng.get_barycentric_coordinates(); + float3 point_pos; + interp_v3_v3v3v3(point_pos, v0_pos, v1_pos, v2_pos, bary_coord); + r_positions.append(point_pos); + r_bary_coords.append(bary_coord); + r_looptri_indices.append(looptri_index); + } + } +} + +BLI_NOINLINE static KDTree_3d *build_kdtree(Span positions) +{ + KDTree_3d *kdtree = BLI_kdtree_3d_new(positions.size()); + + int i_point = 0; + for (const float3 position : positions) { + BLI_kdtree_3d_insert(kdtree, i_point, position); + i_point++; + } + + BLI_kdtree_3d_balance(kdtree); + return kdtree; +} + +BLI_NOINLINE static void update_elimination_mask_for_close_points( + Span positions, const float minimum_distance, MutableSpan elimination_mask) +{ + if (minimum_distance <= 0.0f) { + return; + } + + KDTree_3d *kdtree = build_kdtree(positions); + + for (const int i : positions.index_range()) { + if (elimination_mask[i]) { + continue; + } + + struct CallbackData { + int index; + MutableSpan elimination_mask; + } callback_data = {i, elimination_mask}; + + BLI_kdtree_3d_range_search_cb( + kdtree, + positions[i], + minimum_distance, + [](void *user_data, int index, const float *UNUSED(co), float UNUSED(dist_sq)) { + CallbackData &callback_data = *static_cast(user_data); + if (index != callback_data.index) { + callback_data.elimination_mask[index] = true; + } + return true; + }, + &callback_data); + } + + BLI_kdtree_3d_free(kdtree); +} + +BLI_NOINLINE static void update_elimination_mask_based_on_density_factors( + const Mesh &mesh, + const Span density_factors, + const Span bary_coords, + const Span looptri_indices, + const MutableSpan elimination_mask) +{ + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + for (const int i : bary_coords.index_range()) { + if (elimination_mask[i]) { + continue; + } + + const MLoopTri &looptri = looptris[looptri_indices[i]]; + const float3 bary_coord = bary_coords[i]; + + const int v0_loop = looptri.tri[0]; + const int v1_loop = looptri.tri[1]; + const int v2_loop = looptri.tri[2]; + + const float v0_density_factor = std::max(0.0f, density_factors[v0_loop]); + const float v1_density_factor = std::max(0.0f, density_factors[v1_loop]); + const float v2_density_factor = std::max(0.0f, density_factors[v2_loop]); + + const float probablity = v0_density_factor * bary_coord.x + v1_density_factor * bary_coord.y + + v2_density_factor * bary_coord.z; + + const float hash = noise::hash_float_to_float(bary_coord); + if (hash > probablity) { + elimination_mask[i] = true; + } + } +} + +BLI_NOINLINE static void eliminate_points_based_on_mask(const Span elimination_mask, + Vector &positions, + Vector &bary_coords, + Vector &looptri_indices) +{ + for (int i = positions.size() - 1; i >= 0; i--) { + if (elimination_mask[i]) { + positions.remove_and_reorder(i); + bary_coords.remove_and_reorder(i); + looptri_indices.remove_and_reorder(i); + } + } +} + +BLI_NOINLINE static void interpolate_attribute(const Mesh &mesh, + const Span bary_coords, + const Span looptri_indices, + const AttributeDomain source_domain, + const GVArray &source_data, + GMutableSpan output_data) +{ + switch (source_domain) { + case ATTR_DOMAIN_POINT: { + bke::mesh_surface_sample::sample_point_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); + break; + } + case ATTR_DOMAIN_CORNER: { + bke::mesh_surface_sample::sample_corner_attribute(mesh, + looptri_indices, + bary_coords, + source_data, + IndexMask(output_data.size()), + output_data); + break; + } + case ATTR_DOMAIN_FACE: { + bke::mesh_surface_sample::sample_face_attribute( + mesh, looptri_indices, source_data, IndexMask(output_data.size()), output_data); + break; + } + default: { + /* Not supported currently. */ + return; + } + } +} + +BLI_NOINLINE static void propagate_existing_attributes( + const MeshComponent &mesh_component, + const Map &attributes, + GeometryComponent &point_component, + const Span bary_coords, + const Span looptri_indices) +{ + const Mesh &mesh = *mesh_component.get_for_read(); + + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + const CustomDataType output_data_type = entry.value.data_type; + /* The output domain is always #ATTR_DOMAIN_POINT, since we are creating a point cloud. */ + OutputAttribute attribute_out = point_component.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, output_data_type); + if (!attribute_out) { + continue; + } + + GMutableSpan out_span = attribute_out.as_span(); + + std::optional attribute_info = point_component.attribute_get_meta_data( + attribute_id); + if (!attribute_info) { + continue; + } + + const AttributeDomain source_domain = attribute_info->domain; + GVArrayPtr source_attribute = mesh_component.attribute_get_for_read( + attribute_id, source_domain, output_data_type, nullptr); + if (!source_attribute) { + continue; + } + + interpolate_attribute( + mesh, bary_coords, looptri_indices, source_domain, *source_attribute, out_span); + + attribute_out.save(); + } +} + +namespace { +struct AttributeOutputs { + StrongAnonymousAttributeID normal_id; + StrongAnonymousAttributeID rotation_id; +}; +} // namespace + +BLI_NOINLINE static void compute_attribute_outputs(const MeshComponent &mesh_component, + PointCloudComponent &point_component, + const Span bary_coords, + const Span looptri_indices, + const AttributeOutputs &attribute_outputs) +{ + OutputAttribute_Typed id_attribute = point_component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT); + MutableSpan ids = id_attribute.as_span(); + + OutputAttribute_Typed normal_attribute; + OutputAttribute_Typed rotation_attribute; + + MutableSpan normals; + MutableSpan rotations; + + if (attribute_outputs.normal_id) { + normal_attribute = point_component.attribute_try_get_for_output_only( + attribute_outputs.normal_id.get(), ATTR_DOMAIN_POINT); + normals = normal_attribute.as_span(); + } + if (attribute_outputs.rotation_id) { + rotation_attribute = point_component.attribute_try_get_for_output_only( + attribute_outputs.rotation_id.get(), ATTR_DOMAIN_POINT); + rotations = rotation_attribute.as_span(); + } + + const Mesh &mesh = *mesh_component.get_for_read(); + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const int i : bary_coords.index_range()) { + const int looptri_index = looptri_indices[i]; + const MLoopTri &looptri = looptris[looptri_index]; + const float3 &bary_coord = bary_coords[i]; + + const int v0_index = mesh.mloop[looptri.tri[0]].v; + const int v1_index = mesh.mloop[looptri.tri[1]].v; + const int v2_index = mesh.mloop[looptri.tri[2]].v; + const float3 v0_pos = float3(mesh.mvert[v0_index].co); + const float3 v1_pos = float3(mesh.mvert[v1_index].co); + const float3 v2_pos = float3(mesh.mvert[v2_index].co); + + ids[i] = noise::hash(noise::hash_float(bary_coord), looptri_index); + + float3 normal; + if (!normals.is_empty() || !rotations.is_empty()) { + normal_tri_v3(normal, v0_pos, v1_pos, v2_pos); + } + if (!normals.is_empty()) { + normals[i] = normal; + } + if (!rotations.is_empty()) { + rotations[i] = normal_to_euler_rotation(normal); + } + } + + id_attribute.save(); + + if (normal_attribute) { + normal_attribute.save(); + } + if (rotation_attribute) { + rotation_attribute.save(); + } +} + +static Array calc_full_density_factors_with_selection(const MeshComponent &component, + const Field &density_field, + const Field &selection_field) +{ + const AttributeDomain attribute_domain = ATTR_DOMAIN_CORNER; + GeometryComponentFieldContext field_context{component, attribute_domain}; + const int domain_size = component.attribute_domain_size(attribute_domain); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection_mask = selection_evaluator.get_evaluated_as_mask(0); + + Array densities(domain_size, 0.0f); + + fn::FieldEvaluator density_evaluator{field_context, &selection_mask}; + density_evaluator.add_with_destination(density_field, densities.as_mutable_span()); + density_evaluator.evaluate(); + return densities; +} + +static void distribute_points_random(const MeshComponent &component, + const Field &density_field, + const Field &selection_field, + const int seed, + Vector &positions, + Vector &bary_coords, + Vector &looptri_indices) +{ + const Array densities = calc_full_density_factors_with_selection( + component, density_field, selection_field); + const Mesh &mesh = *component.get_for_read(); + sample_mesh_surface(mesh, 1.0f, densities, seed, positions, bary_coords, looptri_indices); +} + +static void distribute_points_poisson_disk(const MeshComponent &mesh_component, + const float minimum_distance, + const float max_density, + const Field &density_factor_field, + const Field &selection_field, + const int seed, + Vector &positions, + Vector &bary_coords, + Vector &looptri_indices) +{ + const Mesh &mesh = *mesh_component.get_for_read(); + sample_mesh_surface(mesh, max_density, {}, seed, positions, bary_coords, looptri_indices); + + Array elimination_mask(positions.size(), false); + update_elimination_mask_for_close_points(positions, minimum_distance, elimination_mask); + + const Array density_factors = calc_full_density_factors_with_selection( + mesh_component, density_factor_field, selection_field); + + update_elimination_mask_based_on_density_factors( + mesh, density_factors, bary_coords, looptri_indices, elimination_mask.as_mutable_span()); + + eliminate_points_based_on_mask( + elimination_mask.as_span(), positions, bary_coords, looptri_indices); +} + +static void point_distribution_calculate(GeometrySet &geometry_set, + const Field selection_field, + const GeometryNodeDistributePointsOnFacesMode method, + const int seed, + const AttributeOutputs &attribute_outputs, + const GeoNodeExecParams ¶ms) +{ + if (!geometry_set.has_mesh()) { + return; + } + + const MeshComponent &mesh_component = *geometry_set.get_component_for_read(); + + Vector positions; + Vector bary_coords; + Vector looptri_indices; + + switch (method) { + case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_RANDOM: { + const Field density_field = params.get_input>("Density"); + distribute_points_random(mesh_component, + density_field, + selection_field, + seed, + positions, + bary_coords, + looptri_indices); + break; + } + case GEO_NODE_POINT_DISTRIBUTE_POINTS_ON_FACES_POISSON: { + const float minimum_distance = params.get_input("Distance Min"); + const float density_max = params.get_input("Density Max"); + const Field density_factors_field = params.get_input>("Density Factor"); + distribute_points_poisson_disk(mesh_component, + minimum_distance, + density_max, + density_factors_field, + selection_field, + seed, + positions, + bary_coords, + looptri_indices); + break; + } + } + + if (positions.is_empty()) { + return; + } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(positions.size()); + memcpy(pointcloud->co, positions.data(), sizeof(float3) * positions.size()); + uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + geometry_set.replace_pointcloud(pointcloud); + + PointCloudComponent &point_component = + geometry_set.get_component_for_write(); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + + /* Position is set separately. */ + attributes.remove("position"); + + propagate_existing_attributes( + mesh_component, attributes, point_component, bary_coords, looptri_indices); + + compute_attribute_outputs( + mesh_component, point_component, bary_coords, looptri_indices, attribute_outputs); +} + +static void geo_node_point_distribute_points_on_faces_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + + const GeometryNodeDistributePointsOnFacesMode method = + static_cast(params.node().custom1); + + const int seed = params.get_input("Seed") * 5383843; + const Field selection_field = params.extract_input>("Selection"); + + AttributeOutputs attribute_outputs; + if (params.output_is_required("Normal")) { + attribute_outputs.normal_id = StrongAnonymousAttributeID("Normal"); + } + if (params.output_is_required("Rotation")) { + attribute_outputs.rotation_id = StrongAnonymousAttributeID("Rotation"); + } + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + point_distribution_calculate( + geometry_set, selection_field, method, seed, attribute_outputs, params); + /* Keep instances because the original geometry set may contain instances that are processed as + * well. */ + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); + }); + + params.set_output("Points", std::move(geometry_set)); + + if (attribute_outputs.normal_id) { + params.set_output( + "Normal", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.normal_id), + params.attribute_producer_name())); + } + if (attribute_outputs.rotation_id) { + params.set_output( + "Rotation", + AnonymousAttributeFieldInput::Create(std::move(attribute_outputs.rotation_id), + params.attribute_producer_name())); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_distribute_points_on_faces() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, + GEO_NODE_DISTRIBUTE_POINTS_ON_FACES, + "Distribute Points on Faces", + NODE_CLASS_GEOMETRY, + 0); + node_type_update(&ntype, blender::nodes::node_point_distribute_points_on_faces_update); + node_type_size(&ntype, 170, 100, 320); + ntype.declare = blender::nodes::geo_node_point_distribute_points_on_faces_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_point_distribute_points_on_faces_exec; + ntype.draw_buttons = blender::nodes::geo_node_point_distribute_points_on_faces_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc index 867fecea251..f562fb29e90 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_edge_split.cc @@ -14,66 +14,71 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "DNA_modifier_types.h" +#include "BKE_mesh.h" +#include "BKE_mesh_runtime.h" + +#include "bmesh.h" +#include "bmesh_tools.h" #include "node_geometry_util.hh" -extern "C" { -Mesh *doEdgeSplit(const Mesh *mesh, EdgeSplitModifierData *emd); -} - namespace blender::nodes { static void geo_node_edge_split_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Edge Angle").default_value(true); - b.add_input("Angle") - .default_value(DEG2RADF(30.0f)) - .min(0.0f) - .max(DEG2RADF(180.0f)) - .subtype(PROP_ANGLE); - b.add_input("Sharp Edges"); - b.add_output("Geometry"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Mesh")); +} + +static Mesh *mesh_edge_split(const Mesh &mesh, const IndexMask selection) +{ + const BMeshCreateParams bmcp = {true}; + const BMAllocTemplate allocsize = {0, 0, 0, 0}; + BMesh *bm = BM_mesh_create(&allocsize, &bmcp); + + BMeshFromMeshParams params{}; + BM_mesh_bm_from_me(bm, &mesh, ¶ms); + + BM_mesh_elem_table_ensure(bm, BM_EDGE); + for (const int i : selection) { + BMEdge *edge = BM_edge_at_index(bm, i); + BM_elem_flag_enable(edge, BM_ELEM_TAG); + } + + BM_mesh_edgesplit(bm, false, true, false); + + Mesh *result = BKE_mesh_from_bmesh_for_eval_nomain(bm, nullptr, &mesh); + BM_mesh_free(bm); + + BKE_mesh_normals_tag_dirty(result); + + return result; } static void geo_node_edge_split_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Mesh"); - geometry_set = geometry_set_realize_instances(geometry_set); + const Field selection_field = params.extract_input>("Selection"); - if (!geometry_set.has_mesh()) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } - const bool use_sharp_flag = params.extract_input("Sharp Edges"); - const bool use_edge_angle = params.extract_input("Edge Angle"); + const MeshComponent &mesh_component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_EDGE}; + const int domain_size = mesh_component.attribute_domain_size(ATTR_DOMAIN_EDGE); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - if (!use_edge_angle && !use_sharp_flag) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } + geometry_set.replace_mesh(mesh_edge_split(*mesh_component.get_for_read(), selection)); + }); - const float split_angle = params.extract_input("Angle"); - const Mesh *mesh_in = geometry_set.get_mesh_for_read(); - - /* Use modifier struct to pass arguments to the modifier code. */ - EdgeSplitModifierData emd; - memset(&emd, 0, sizeof(EdgeSplitModifierData)); - emd.split_angle = split_angle; - if (use_edge_angle) { - emd.flags = MOD_EDGESPLIT_FROMANGLE; - } - if (use_sharp_flag) { - emd.flags |= MOD_EDGESPLIT_FROMFLAG; - } - - Mesh *mesh_out = doEdgeSplit(mesh_in, &emd); - geometry_set.replace_mesh(mesh_out); - - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes @@ -82,7 +87,7 @@ void register_node_type_geo_edge_split() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_EDGE_SPLIT, "Edge Split", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SPLIT_EDGES, "Split Edges", NODE_CLASS_GEOMETRY, 0); ntype.geometry_node_execute = blender::nodes::geo_node_edge_split_exec; ntype.declare = blender::nodes::geo_node_edge_split_declare; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc new file mode 100644 index 00000000000..e1c72fbd438 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_image_texture.cc @@ -0,0 +1,429 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "node_geometry_util.hh" + +#include "BKE_image.h" + +#include "BLI_float4.hh" +#include "BLI_threads.h" +#include "BLI_timeit.hh" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_image_texture_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Image")).hide_label(); + b.add_input(N_("Vector")) + .implicit_field() + .description(("Texture coordinates from 0 to 1")); + b.add_input(N_("Frame")).min(0).max(MAXFRAMEF); + b.add_output(N_("Color")).no_muted_links().dependent_field(); + b.add_output(N_("Alpha")).no_muted_links().dependent_field(); +} + +static void geo_node_image_texture_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "interpolation", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); + uiItemR(layout, ptr, "extension", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); +} + +static void geo_node_image_texture_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryImageTexture *tex = (NodeGeometryImageTexture *)MEM_callocN( + sizeof(NodeGeometryImageTexture), __func__); + node->storage = tex; +} + +class ImageFieldsFunction : public fn::MultiFunction { + private: + const int interpolation_; + const int extension_; + Image &image_; + ImageUser image_user_; + void *image_lock_; + ImBuf *image_buffer_; + + public: + ImageFieldsFunction(const int interpolation, + const int extension, + Image &image, + ImageUser image_user) + : interpolation_(interpolation), + extension_(extension), + image_(image), + image_user_(image_user) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + + image_buffer_ = BKE_image_acquire_ibuf(&image_, &image_user_, &image_lock_); + if (image_buffer_ == nullptr) { + throw std::runtime_error("cannot aquire image buffer"); + } + + if (image_buffer_->rect_float == nullptr) { + BLI_thread_lock(LOCK_IMAGE); + if (!image_buffer_->rect_float) { + IMB_float_from_rect(image_buffer_); + } + BLI_thread_unlock(LOCK_IMAGE); + } + + if (image_buffer_->rect_float == nullptr) { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + throw std::runtime_error("cannot get float buffer"); + } + } + + ~ImageFieldsFunction() override + { + BKE_image_release_ibuf(&image_, image_buffer_, image_lock_); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"ImageFunction"}; + signature.single_input("Vector"); + signature.single_output("Color"); + signature.single_output("Alpha"); + return signature.build(); + } + + static int wrap_periodic(int x, const int width) + { + x %= width; + if (x < 0) { + x += width; + } + return x; + } + + static int wrap_clamp(const int x, const int width) + { + return std::clamp(x, 0, width - 1); + } + + static float4 image_pixel_lookup(const ImBuf *ibuf, const int px, const int py) + { + if (px < 0 || py < 0 || px >= ibuf->x || py >= ibuf->y) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + return ((const float4 *)ibuf->rect_float)[px + py * ibuf->x]; + } + + static float frac(const float x, int *ix) + { + const int i = (int)x - ((x < 0.0f) ? 1 : 0); + *ix = i; + return x - (float)i; + } + + static float4 image_cubic_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float tx = frac(px * (float)width - 0.5f, &pix); + const float ty = frac(py * (float)height - 0.5f, &piy); + int ppix, ppiy, nnix, nniy; + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + ppix = wrap_periodic(pix - 1, width); + ppiy = wrap_periodic(piy - 1, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + nnix = wrap_periodic(pix + 2, width); + nniy = wrap_periodic(piy + 2, height); + break; + } + case SHD_IMAGE_EXTENSION_CLIP: { + ppix = pix - 1; + ppiy = piy - 1; + nix = pix + 1; + niy = piy + 1; + nnix = pix + 2; + nniy = piy + 2; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ppix = wrap_clamp(pix - 1, width); + ppiy = wrap_clamp(piy - 1, height); + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + nnix = wrap_clamp(pix + 2, width); + nniy = wrap_clamp(piy + 2, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + + const int xc[4] = {ppix, pix, nix, nnix}; + const int yc[4] = {ppiy, piy, niy, nniy}; + float u[4], v[4]; + + u[0] = (((-1.0f / 6.0f) * tx + 0.5f) * tx - 0.5f) * tx + (1.0f / 6.0f); + u[1] = ((0.5f * tx - 1.0f) * tx) * tx + (2.0f / 3.0f); + u[2] = ((-0.5f * tx + 0.5f) * tx + 0.5f) * tx + (1.0f / 6.0f); + u[3] = (1.0f / 6.0f) * tx * tx * tx; + + v[0] = (((-1.0f / 6.0f) * ty + 0.5f) * ty - 0.5f) * ty + (1.0f / 6.0f); + v[1] = ((0.5f * ty - 1.0f) * ty) * ty + (2.0f / 3.0f); + v[2] = ((-0.5f * ty + 0.5f) * ty + 0.5f) * ty + (1.0f / 6.0f); + v[3] = (1.0f / 6.0f) * ty * ty * ty; + + return (v[0] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[0])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[0])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[0])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[0])))) + + (v[1] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[1])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[1])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[1])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[1])))) + + (v[2] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[2])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[2])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[2])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[2])))) + + (v[3] * (u[0] * (image_pixel_lookup(ibuf, xc[0], yc[3])) + + u[1] * (image_pixel_lookup(ibuf, xc[1], yc[3])) + + u[2] * (image_pixel_lookup(ibuf, xc[2], yc[3])) + + u[3] * (image_pixel_lookup(ibuf, xc[3], yc[3])))); + } + + static float4 image_linear_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int pix, piy, nix, niy; + const float nfx = frac(px * (float)width - 0.5f, &pix); + const float nfy = frac(py * (float)height - 0.5f, &piy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_CLIP: { + nix = pix + 1; + niy = piy + 1; + break; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + nix = wrap_clamp(pix + 1, width); + niy = wrap_clamp(piy + 1, height); + pix = wrap_clamp(pix, width); + piy = wrap_clamp(piy, height); + break; + } + default: + case SHD_IMAGE_EXTENSION_REPEAT: + pix = wrap_periodic(pix, width); + piy = wrap_periodic(piy, height); + nix = wrap_periodic(pix + 1, width); + niy = wrap_periodic(piy + 1, height); + break; + } + + const float ptx = 1.0f - nfx; + const float pty = 1.0f - nfy; + + return image_pixel_lookup(ibuf, pix, piy) * ptx * pty + + image_pixel_lookup(ibuf, nix, piy) * nfx * pty + + image_pixel_lookup(ibuf, pix, niy) * ptx * nfy + + image_pixel_lookup(ibuf, nix, niy) * nfx * nfy; + } + + static float4 image_closest_texture_lookup(const ImBuf *ibuf, + const float px, + const float py, + const int extension) + { + const int width = ibuf->x; + const int height = ibuf->y; + int ix, iy; + const float tx = frac(px * (float)width - 0.5f, &ix); + const float ty = frac(py * (float)height - 0.5f, &iy); + + switch (extension) { + case SHD_IMAGE_EXTENSION_REPEAT: { + ix = wrap_periodic(ix, width); + iy = wrap_periodic(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + case SHD_IMAGE_EXTENSION_CLIP: { + if (tx < 0.0f || ty < 0.0f || tx > 1.0f || ty > 1.0f) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + if (ix < 0 || iy < 0 || ix > width || iy > height) { + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + ATTR_FALLTHROUGH; + } + case SHD_IMAGE_EXTENSION_EXTEND: { + ix = wrap_clamp(ix, width); + iy = wrap_clamp(iy, height); + return image_pixel_lookup(ibuf, ix, iy); + } + default: + return float4(0.0f, 0.0f, 0.0f, 0.0f); + } + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vectors = params.readonly_single_input(0, "Vector"); + MutableSpan r_color = params.uninitialized_single_output( + 1, "Color"); + MutableSpan r_alpha = params.uninitialized_single_output_if_required(2, "Alpha"); + + MutableSpan color_data{(float4 *)r_color.data(), r_color.size()}; + + /* Sample image texture. */ + switch (interpolation_) { + case SHD_INTERP_LINEAR: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_linear_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CLOSEST: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_closest_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + case SHD_INTERP_CUBIC: + case SHD_INTERP_SMART: + for (const int64_t i : mask) { + const float3 p = vectors[i]; + color_data[i] = image_cubic_texture_lookup(image_buffer_, p.x, p.y, extension_); + } + break; + } + + int alpha_mode = image_.alpha_mode; + if (IMB_colormanagement_space_name_is_data(image_.colorspace_settings.name)) { + alpha_mode = IMA_ALPHA_CHANNEL_PACKED; + } + + switch (alpha_mode) { + case IMA_ALPHA_STRAIGHT: { + /* #ColorGeometry expects premultiplied alpha, so convert from straight to that. */ + for (int64_t i : mask) { + straight_to_premul_v4(color_data[i]); + } + break; + } + case IMA_ALPHA_PREMUL: { + /* Alpha is premultiplied already, nothing to do. */ + break; + } + case IMA_ALPHA_CHANNEL_PACKED: { + /* Color and alpha channels shouldn't interact with each other, nothing to do. */ + break; + } + case IMA_ALPHA_IGNORE: { + /* The image should be treated as being opaque. */ + for (int64_t i : mask) { + color_data[i].w = 1.0f; + } + break; + } + } + + if (!r_alpha.is_empty()) { + for (int64_t i : mask) { + r_alpha[i] = r_color[i].a; + } + } + } +}; + +static void geo_node_image_texture_exec(GeoNodeExecParams params) +{ + auto return_default = [&]() { + params.set_output("Color", ColorGeometry4f(0.0f, 0.0f, 0.0f, 1.0f)); + params.set_output("Alpha", 1.0f); + }; + + Image *image = params.get_input("Image"); + if (image == nullptr) { + return return_default(); + } + + const bNode &node = params.node(); + NodeGeometryImageTexture *data = (NodeGeometryImageTexture *)node.storage; + + ImageUser image_user; + BKE_imageuser_default(&image_user); + image_user.cycl = false; + image_user.frames = INT_MAX; + image_user.sfra = 1; + image_user.framenr = BKE_image_is_animated(image) ? params.get_input("Frame") : 0; + + std::unique_ptr image_fn; + try { + image_fn = std::make_unique( + data->interpolation, data->extension, *image, image_user); + } + catch (const std::runtime_error &) { + return return_default(); + } + + Field vector_field = params.extract_input>("Vector"); + + auto image_op = std::make_shared( + FieldOperation(std::move(image_fn), {std::move(vector_field)})); + + params.set_output("Color", Field(image_op, 0)); + params.set_output("Alpha", Field(image_op, 1)); +} + +} // namespace blender::nodes + +void register_node_type_geo_image_texture(void) +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_IMAGE_TEXTURE, "Image Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::geo_node_image_texture_declare; + ntype.draw_buttons = blender::nodes::geo_node_image_texture_layout; + node_type_init(&ntype, blender::nodes::geo_node_image_texture_init); + node_type_storage( + &ntype, "NodeGeometryImageTexture", node_free_standard_storage, node_copy_standard_storage); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + ntype.geometry_node_execute = blender::nodes::geo_node_image_texture_exec; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc new file mode 100644 index 00000000000..b8df545d073 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_handles.cc @@ -0,0 +1,46 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_curve_handles_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Left")).field_source(); + b.add_output(N_("Right")).field_source(); +} + +static void geo_node_input_curve_handles_exec(GeoNodeExecParams params) +{ + Field left_field = AttributeFieldInput::Create("handle_left"); + Field right_field = AttributeFieldInput::Create("handle_right"); + params.set_output("Left", std::move(left_field)); + params.set_output("Right", std::move(right_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_curve_handles() +{ + static bNodeType ntype; + geo_node_type_base( + &ntype, GEO_NODE_INPUT_CURVE_HANDLES, "Curve Handle Positions", NODE_CLASS_INPUT, 0); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + ntype.geometry_node_execute = blender::nodes::geo_node_input_curve_handles_exec; + ntype.declare = blender::nodes::geo_node_input_curve_handles_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc new file mode 100644 index 00000000000..f32db3842db --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_curve_tilt.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_curve_tilt_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Tilt")).field_source(); +} + +static void geo_node_input_curve_tilt_exec(GeoNodeExecParams params) +{ + Field tilt_field = AttributeFieldInput::Create("tilt"); + params.set_output("Tilt", std::move(tilt_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_curve_tilt() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_CURVE_TILT, "Curve Tilt", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_curve_tilt_exec; + ntype.declare = blender::nodes::geo_node_input_curve_tilt_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_id.cc b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc new file mode 100644 index 00000000000..37d5bac0325 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_id.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_id_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("ID")).field_source(); +} + +static void geo_node_input_id_exec(GeoNodeExecParams params) +{ + Field position_field{std::make_shared()}; + params.set_output("ID", std::move(position_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_id() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_ID, "ID", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_id_exec; + ntype.declare = blender::nodes::geo_node_input_id_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc index c52ff3d448e..6200ac5e7a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_index.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_index.cc @@ -20,30 +20,12 @@ namespace blender::nodes { static void geo_node_input_index_declare(NodeDeclarationBuilder &b) { - b.add_output("Index"); + b.add_output(N_("Index")).field_source(); } -class IndexFieldInput final : public fn::FieldInput { - public: - IndexFieldInput() : FieldInput(CPPType::get(), "Index") - { - } - - const GVArray *get_varray_for_context(const fn::FieldContext &UNUSED(context), - IndexMask mask, - ResourceScope &scope) const final - { - /* TODO: Investigate a similar method to IndexRange::as_span() */ - auto index_func = [](int i) { return i; }; - return &scope.construct< - fn::GVArray_For_EmbeddedVArray>>( - mask.min_array_size(), mask.min_array_size(), index_func); - } -}; - static void geo_node_input_index_exec(GeoNodeExecParams params) { - Field index_field{std::make_shared()}; + Field index_field{std::make_shared()}; params.set_output("Index", std::move(index_field)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc index 8e805bd1359..fc41188dee5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_material.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material.cc @@ -23,7 +23,7 @@ namespace blender::nodes { static void geo_node_input_material_declare(NodeDeclarationBuilder &b) { - b.add_output("Material"); + b.add_output(N_("Material")); } static void geo_node_input_material_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc new file mode 100644 index 00000000000..5d5d9e40032 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_material_index.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_material_index_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Material Index")).field_source(); +} + +static void geo_node_input_material_index_exec(GeoNodeExecParams params) +{ + Field material_index_field = AttributeFieldInput::Create("material_index"); + params.set_output("Material Index", std::move(material_index_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_material_index() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_MATERIAL_INDEX, "Material Index", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_material_index_exec; + ntype.declare = blender::nodes::geo_node_input_material_index_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc index 07818f2a3ad..92b89313d23 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_normal.cc @@ -14,10 +14,13 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "BLI_task.hh" + #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "BKE_mesh.h" +#include "BKE_spline.hh" #include "node_geometry_util.hh" @@ -25,7 +28,7 @@ namespace blender::nodes { static void geo_node_input_normal_declare(NodeDeclarationBuilder &b) { - b.add_output("Normal"); + b.add_output(N_("Normal")).field_source(); } static GVArrayPtr mesh_face_normals(const Mesh &mesh, @@ -147,10 +150,100 @@ static const GVArray *construct_mesh_normals_gvarray(const MeshComponent &mesh_c } } +static void calculate_bezier_normals(const BezierSpline &spline, MutableSpan normals) +{ + Span offsets = spline.control_point_offsets(); + Span evaluated_normals = spline.evaluated_normals(); + for (const int i : IndexRange(spline.size())) { + normals[i] = evaluated_normals[offsets[i]]; + } +} + +static void calculate_poly_normals(const PolySpline &spline, MutableSpan normals) +{ + normals.copy_from(spline.evaluated_normals()); +} + +/** + * Because NURBS control points are not necessarily on the path, the normal at the control points + * is not well defined, so create a temporary poly spline to find the normals. This requires extra + * copying currently, but may be more efficient in the future if attributes have some form of CoW. + */ +static void calculate_nurbs_normals(const NURBSpline &spline, MutableSpan normals) +{ + PolySpline poly_spline; + poly_spline.resize(spline.size()); + poly_spline.positions().copy_from(spline.positions()); + normals.copy_from(poly_spline.evaluated_normals()); +} + +static Array curve_normal_point_domain(const CurveEval &curve) +{ + Span splines = curve.splines(); + Array offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array normals(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_normals{normals.as_mutable_span().slice(offsets[i], spline.size())}; + switch (splines[i]->type()) { + case Spline::Type::Bezier: + calculate_bezier_normals(static_cast(spline), spline_normals); + break; + case Spline::Type::Poly: + calculate_poly_normals(static_cast(spline), spline_normals); + break; + case Spline::Type::NURBS: + calculate_nurbs_normals(static_cast(spline), spline_normals); + break; + } + } + }); + return normals; +} + +static const GVArray *construct_curve_normal_gvarray(const CurveComponent &component, + const AttributeDomain domain, + ResourceScope &scope) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_POINT) { + const Span splines = curve->splines(); + + /* Use a reference to evaluated normals if possible to avoid an allocation and a copy. + * This is only possible when there is only one poly spline. */ + if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { + const PolySpline &spline = static_cast(*splines.first()); + return &scope.construct>(spline.evaluated_normals()); + } + + Array normals = curve_normal_point_domain(*curve); + return &scope.construct>>(std::move(normals)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array point_normals = curve_normal_point_domain(*curve); + GVArrayPtr gvarray = std::make_unique>>( + std::move(point_normals)); + GVArrayPtr spline_normals = component.attribute_try_adapt_domain( + std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + return scope.add_value(std::move(spline_normals)).get(); + } + + return nullptr; +} + class NormalFieldInput final : public fn::FieldInput { public: - NormalFieldInput() : fn::FieldInput(CPPType::get(), "Normal") + NormalFieldInput() : fn::FieldInput(CPPType::get(), "Normal node") { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, @@ -173,8 +266,8 @@ class NormalFieldInput final : public fn::FieldInput { return construct_mesh_normals_gvarray(mesh_component, *mesh, mask, domain, scope); } if (component.type() == GEO_COMPONENT_TYPE_CURVE) { - /* TODO: Add curve normals support. */ - return nullptr; + const CurveComponent &curve_component = static_cast(component); + return construct_curve_normal_gvarray(curve_component, domain, scope); } } return nullptr; diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc index c6365bf6809..a8477d4bc4f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_input_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_input_position.cc @@ -20,13 +20,12 @@ namespace blender::nodes { static void geo_node_input_position_declare(NodeDeclarationBuilder &b) { - b.add_output("Position"); + b.add_output(N_("Position")).field_source(); } static void geo_node_input_position_exec(GeoNodeExecParams params) { - Field position_field{ - std::make_shared("position", CPPType::get())}; + Field position_field{AttributeFieldInput::Create("position")}; params.set_output("Position", std::move(position_field)); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc new file mode 100644 index 00000000000..6d2c4c38cbe --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_radius.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_radius_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Radius")).default_value(1.0f).min(0.0f).field_source(); +} + +static void geo_node_input_radius_exec(GeoNodeExecParams params) +{ + Field radius_field = AttributeFieldInput::Create("radius"); + params.set_output("Radius", std::move(radius_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_radius() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_RADIUS, "Radius", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_radius_exec; + ntype.declare = blender::nodes::geo_node_input_radius_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc new file mode 100644 index 00000000000..dcd14b1c054 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_shade_smooth.cc @@ -0,0 +1,42 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_shade_smooth_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Smooth")).field_source(); +} + +static void geo_node_input_shade_smooth_exec(GeoNodeExecParams params) +{ + Field shade_smooth_field = AttributeFieldInput::Create("shade_smooth"); + params.set_output("Smooth", std::move(shade_smooth_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_shade_smooth() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_SHADE_SMOOTH, "Is Shade Smooth", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_shade_smooth_exec; + ntype.declare = blender::nodes::geo_node_input_shade_smooth_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc new file mode 100644 index 00000000000..a8ee6dd8b12 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_cyclic.cc @@ -0,0 +1,43 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_spline_cyclic_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Cyclic")).field_source(); +} + +static void geo_node_input_spline_cyclic_exec(GeoNodeExecParams params) +{ + Field cyclic_field = AttributeFieldInput::Create("cyclic"); + params.set_output("Cyclic", std::move(cyclic_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_spline_cyclic() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_SPLINE_CYCLIC, "Is Spline Cyclic", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_cyclic_exec; + ntype.declare = blender::nodes::geo_node_input_spline_cyclic_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc new file mode 100644 index 00000000000..895efa6f0ed --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_length.cc @@ -0,0 +1,110 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "BKE_spline.hh" + +namespace blender::nodes { + +static void geo_node_input_spline_length_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Length")).field_source(); +} + +static const GVArray *construct_spline_length_gvarray(const CurveComponent &component, + const AttributeDomain domain, + ResourceScope &scope) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + Span splines = curve->splines(); + auto length_fn = [splines](int i) { return splines[i]->length(); }; + + if (domain == ATTR_DOMAIN_CURVE) { + return &scope.construct< + fn::GVArray_For_EmbeddedVArray>>( + splines.size(), splines.size(), length_fn); + } + if (domain == ATTR_DOMAIN_POINT) { + GVArrayPtr length = std::make_unique< + fn::GVArray_For_EmbeddedVArray>>( + splines.size(), splines.size(), length_fn); + return scope + .add_value(component.attribute_try_adapt_domain( + std::move(length), ATTR_DOMAIN_CURVE, ATTR_DOMAIN_POINT)) + .get(); + } + + return nullptr; +} + +class SplineLengthFieldInput final : public fn::FieldInput { + public: + SplineLengthFieldInput() : fn::FieldInput(CPPType::get(), "Spline Length node") + { + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast(component); + return construct_spline_length_gvarray(curve_component, domain, scope); + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 3549623580; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_input_spline_length_exec(GeoNodeExecParams params) +{ + Field length_field{std::make_shared()}; + params.set_output("Length", std::move(length_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_spline_length() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_SPLINE_LENGTH, "Spline Length", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_length_exec; + ntype.declare = blender::nodes::geo_node_input_spline_length_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc new file mode 100644 index 00000000000..75fb8a13d38 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_spline_resolution.cc @@ -0,0 +1,43 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_spline_resolution_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Resolution")).field_source(); +} + +static void geo_node_input_spline_resolution_exec(GeoNodeExecParams params) +{ + Field resolution_field = AttributeFieldInput::Create("resolution"); + params.set_output("Resolution", std::move(resolution_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_spline_resolution() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INPUT_SPLINE_RESOLUTION, "Spline Resolution", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_spline_resolution_exec; + ntype.declare = blender::nodes::geo_node_input_spline_resolution_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc new file mode 100644 index 00000000000..6b1736fe2ac --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_input_tangent.cc @@ -0,0 +1,175 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_input_tangent_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Tangent")).field_source(); +} + +static void calculate_bezier_tangents(const BezierSpline &spline, MutableSpan tangents) +{ + Span offsets = spline.control_point_offsets(); + Span evaluated_tangents = spline.evaluated_tangents(); + for (const int i : IndexRange(spline.size())) { + tangents[i] = evaluated_tangents[offsets[i]]; + } +} + +static void calculate_poly_tangents(const PolySpline &spline, MutableSpan tangents) +{ + tangents.copy_from(spline.evaluated_tangents()); +} + +/** + * Because NURBS control points are not necessarily on the path, the tangent at the control points + * is not well defined, so create a temporary poly spline to find the tangents. This requires extra + * copying currently, but may be more efficient in the future if attributes have some form of CoW. + */ +static void calculate_nurbs_tangents(const NURBSpline &spline, MutableSpan tangents) +{ + PolySpline poly_spline; + poly_spline.resize(spline.size()); + poly_spline.positions().copy_from(spline.positions()); + tangents.copy_from(poly_spline.evaluated_tangents()); +} + +static Array curve_tangent_point_domain(const CurveEval &curve) +{ + Span splines = curve.splines(); + Array offsets = curve.control_point_offsets(); + const int total_size = offsets.last(); + Array tangents(total_size); + + threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { + for (const int i : range) { + const Spline &spline = *splines[i]; + MutableSpan spline_tangents{tangents.as_mutable_span().slice(offsets[i], spline.size())}; + switch (splines[i]->type()) { + case Spline::Type::Bezier: { + calculate_bezier_tangents(static_cast(spline), spline_tangents); + break; + } + case Spline::Type::Poly: { + calculate_poly_tangents(static_cast(spline), spline_tangents); + break; + } + case Spline::Type::NURBS: { + calculate_nurbs_tangents(static_cast(spline), spline_tangents); + break; + } + } + } + }); + return tangents; +} + +static const GVArray *construct_curve_tangent_gvarray(const CurveComponent &component, + const AttributeDomain domain, + ResourceScope &scope) +{ + const CurveEval *curve = component.get_for_read(); + if (curve == nullptr) { + return nullptr; + } + + if (domain == ATTR_DOMAIN_POINT) { + const Span splines = curve->splines(); + + /* Use a reference to evaluated tangents if possible to avoid an allocation and a copy. + * This is only possible when there is only one poly spline. */ + if (splines.size() == 1 && splines.first()->type() == Spline::Type::Poly) { + const PolySpline &spline = static_cast(*splines.first()); + return &scope.construct>(spline.evaluated_tangents()); + } + + Array tangents = curve_tangent_point_domain(*curve); + return &scope.construct>>(std::move(tangents)); + } + + if (domain == ATTR_DOMAIN_CURVE) { + Array point_tangents = curve_tangent_point_domain(*curve); + GVArrayPtr gvarray = std::make_unique>>( + std::move(point_tangents)); + GVArrayPtr spline_tangents = component.attribute_try_adapt_domain( + std::move(gvarray), ATTR_DOMAIN_POINT, ATTR_DOMAIN_CURVE); + return scope.add_value(std::move(spline_tangents)).get(); + } + + return nullptr; +} + +class TangentFieldInput final : public fn::FieldInput { + public: + TangentFieldInput() : fn::FieldInput(CPPType::get(), "Tangent node") + { + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const fn::FieldContext &context, + IndexMask UNUSED(mask), + ResourceScope &scope) const final + { + if (const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context)) { + + const GeometryComponent &component = geometry_context->geometry_component(); + const AttributeDomain domain = geometry_context->domain(); + + if (component.type() == GEO_COMPONENT_TYPE_CURVE) { + const CurveComponent &curve_component = static_cast(component); + return construct_curve_tangent_gvarray(curve_component, domain, scope); + } + } + return nullptr; + } + + uint64_t hash() const override + { + /* Some random constant hash. */ + return 91827364589; + } + + bool is_equal_to(const fn::FieldNode &other) const override + { + return dynamic_cast(&other) != nullptr; + } +}; + +static void geo_node_input_tangent_exec(GeoNodeExecParams params) +{ + Field tangent_field{std::make_shared()}; + params.set_output("Tangent", std::move(tangent_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_input_tangent() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_INPUT_TANGENT, "Curve Tangent", NODE_CLASS_INPUT, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_input_tangent_exec; + ntype.declare = blender::nodes::geo_node_input_tangent_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc new file mode 100644 index 00000000000..aff29d973d4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_instance_on_points.cc @@ -0,0 +1,229 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_collection_types.h" + +#include "BLI_hash.h" +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_instance_on_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Points")).description(N_("Points to instance on")); + b.add_input(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input(N_("Instance")) + .description(N_("Geometry that is instanced on the points")); + b.add_input(N_("Pick Instance")) + .supports_field() + .description("Place different instances on different points"); + b.add_input(N_("Instance Index")) + .implicit_field() + .description(N_( + "Index of the instance that used for each point. This is only used when Pick Instances " + "is on. By default the point index is used")); + b.add_input(N_("Rotation")) + .subtype(PROP_EULER) + .supports_field() + .description(N_("Rotation of the instances")); + b.add_input(N_("Scale")) + .default_value({1.0f, 1.0f, 1.0f}) + .subtype(PROP_XYZ) + .supports_field() + .description(N_("Scale of the instances")); + + b.add_output(N_("Instances")); +} + +static void add_instances_from_component(InstancesComponent &dst_component, + const GeometryComponent &src_component, + const GeometrySet &instance, + const GeoNodeExecParams ¶ms) +{ + const AttributeDomain domain = ATTR_DOMAIN_POINT; + const int domain_size = src_component.attribute_domain_size(domain); + + GeometryComponentFieldContext field_context{src_component, domain}; + const Field selection_field = params.get_input>("Selection"); + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + /* The initial size of the component might be non-zero when this function is called for multiple + * component types. */ + const int start_len = dst_component.instances_amount(); + const int select_len = selection.index_range().size(); + dst_component.resize(start_len + select_len); + + MutableSpan dst_handles = dst_component.instance_reference_handles().slice(start_len, + select_len); + MutableSpan dst_transforms = dst_component.instance_transforms().slice(start_len, + select_len); + + FieldEvaluator field_evaluator{field_context, domain_size}; + const VArray *pick_instance = nullptr; + const VArray *indices = nullptr; + const VArray *rotations = nullptr; + const VArray *scales = nullptr; + /* The evaluator could use the component's stable IDs as a destination directly, but only the + * selected indices should be copied. */ + field_evaluator.add(params.get_input>("Pick Instance"), &pick_instance); + field_evaluator.add(params.get_input>("Instance Index"), &indices); + field_evaluator.add(params.get_input>("Rotation"), &rotations); + field_evaluator.add(params.get_input>("Scale"), &scales); + field_evaluator.evaluate(); + + GVArray_Typed positions = src_component.attribute_get_for_read( + "position", domain, {0, 0, 0}); + + const InstancesComponent *src_instances = instance.get_component_for_read(); + + /* Maps handles from the source instances to handles on the new instance. */ + Array handle_mapping; + /* Only fill #handle_mapping when it may be used below. */ + if (src_instances != nullptr && + (!pick_instance->is_single() || pick_instance->get_internal_single())) { + Span src_references = src_instances->references(); + handle_mapping.reinitialize(src_references.size()); + for (const int src_instance_handle : src_references.index_range()) { + const InstanceReference &reference = src_references[src_instance_handle]; + const int dst_instance_handle = dst_component.add_reference(reference); + handle_mapping[src_instance_handle] = dst_instance_handle; + } + } + + const int full_instance_handle = dst_component.add_reference(instance); + /* Add this reference last, because it is the most likely one to be removed later on. */ + const int empty_reference_handle = dst_component.add_reference(InstanceReference()); + + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange selection_range) { + for (const int range_i : selection_range) { + const int64_t i = selection[range_i]; + + /* Compute base transform for every instances. */ + float4x4 &dst_transform = dst_transforms[range_i]; + dst_transform = float4x4::from_loc_eul_scale( + positions[i], rotations->get(i), scales->get(i)); + + /* Reference that will be used by this new instance. */ + int dst_handle = empty_reference_handle; + + const bool use_individual_instance = pick_instance->get(i); + if (use_individual_instance) { + if (src_instances != nullptr) { + const int src_instances_amount = src_instances->instances_amount(); + const int original_index = indices->get(i); + /* Use #mod_i instead of `%` to get the desirable wrap around behavior where -1 + * refers to the last element. */ + const int index = mod_i(original_index, std::max(src_instances_amount, 1)); + if (index < src_instances_amount) { + /* Get the reference to the source instance. */ + const int src_handle = src_instances->instance_reference_handles()[index]; + dst_handle = handle_mapping[src_handle]; + + /* Take transforms of the source instance into account. */ + mul_m4_m4_post(dst_transform.values, + src_instances->instance_transforms()[index].values); + } + } + } + else { + /* Use entire source geometry as instance. */ + dst_handle = full_instance_handle; + } + /* Set properties of new instance. */ + dst_handles[range_i] = dst_handle; + } + }); + + GVArrayPtr id_attribute = src_component.attribute_try_get_for_read( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + if (id_attribute) { + GVArray_Typed ids{*id_attribute}; + VArray_Span ids_span{ids}; + MutableSpan dst_ids = dst_component.instance_ids_ensure(); + for (const int64_t i : selection.index_range()) { + dst_ids[i] = ids_span[selection[i]]; + } + } + + if (pick_instance->is_single()) { + if (pick_instance->get_internal_single()) { + if (instance.has_realized_data()) { + params.error_message_add( + NodeWarningType::Info, + TIP_("Realized geometry is not used when pick instances is true")); + } + } + } +} + +static void geo_node_instance_on_points_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Points"); + GeometrySet instance = params.get_input("Instance"); + instance.ensure_owns_direct_data(); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), instance, params); + geometry_set.remove(GEO_COMPONENT_TYPE_MESH); + } + if (geometry_set.has()) { + add_instances_from_component(instances, + *geometry_set.get_component_for_read(), + instance, + params); + geometry_set.remove(GEO_COMPONENT_TYPE_POINT_CLOUD); + } + if (geometry_set.has()) { + add_instances_from_component( + instances, *geometry_set.get_component_for_read(), instance, params); + geometry_set.remove(GEO_COMPONENT_TYPE_CURVE); + } + }); + + /* Unused references may have been added above. Remove those now so that other nodes don't + * process them needlessly. + * This should eventually be moved into the loop above, but currently this is quite tricky + * because it might remove references that the loop still wants to iterate over. */ + InstancesComponent &instances = geometry_set.get_component_for_write(); + instances.remove_unused_references(); + + params.set_output("Instances", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_instance_on_points() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INSTANCE_ON_POINTS, "Instance on Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_instance_on_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_instance_on_points_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc new file mode 100644 index 00000000000..c3955426e69 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_instances_to_points.cc @@ -0,0 +1,119 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_pointcloud.h" +#include "DNA_pointcloud_types.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_instances_to_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Position")).implicit_field(); + b.add_input(N_("Radius")) + .default_value(0.05f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output(N_("Points")); +} + +template +static void copy_attribute_to_points(const VArray &src, + const IndexMask mask, + MutableSpan dst) +{ + for (const int i : mask.index_range()) { + dst[i] = src[mask[i]]; + } +} + +static void convert_instances_to_points(GeometrySet &geometry_set, + Field position_field, + Field radius_field, + const Field selection_field) +{ + const InstancesComponent &instances = *geometry_set.get_component_for_read(); + + const AttributeDomain attribute_domain = ATTR_DOMAIN_POINT; + GeometryComponentFieldContext field_context{instances, attribute_domain}; + const int domain_size = instances.attribute_domain_size(attribute_domain); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(std::move(selection_field)); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + if (selection.is_empty()) { + return; + } + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); + geometry_set.replace_pointcloud(pointcloud); + + PointCloudComponent &points = geometry_set.get_component_for_write(); + + fn::FieldEvaluator evaluator{field_context, &selection}; + evaluator.add(std::move(position_field)); + evaluator.add(std::move(radius_field)); + evaluator.evaluate(); + const VArray &positions = evaluator.get_evaluated(0); + copy_attribute_to_points(positions, selection, {(float3 *)pointcloud->co, pointcloud->totpoint}); + const VArray &radii = evaluator.get_evaluated(1); + copy_attribute_to_points(radii, selection, {pointcloud->radius, pointcloud->totpoint}); + + if (!instances.instance_ids().is_empty()) { + OutputAttribute_Typed id_attribute = points.attribute_try_get_for_output( + "id", ATTR_DOMAIN_POINT, CD_PROP_INT32); + MutableSpan ids = id_attribute.as_span(); + for (const int i : selection.index_range()) { + ids[i] = instances.instance_ids()[selection[i]]; + } + id_attribute.save(); + } +} + +static void geo_node_instances_to_points_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Instances"); + + if (geometry_set.has_instances()) { + convert_instances_to_points(geometry_set, + params.extract_input>("Position"), + params.extract_input>("Radius"), + params.extract_input>("Selection")); + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD}); + params.set_output("Points", std::move(geometry_set)); + } + else { + params.set_output("Points", GeometrySet()); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_instances_to_points() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_INSTANCES_TO_POINTS, "Instances to Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_instances_to_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_instances_to_points_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc index f8a1c764f61..8e0e98f7bd5 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_is_viewport.cc @@ -22,7 +22,7 @@ namespace blender::nodes { static void geo_node_is_viewport_declare(NodeDeclarationBuilder &b) { - b.add_output("Is Viewport"); + b.add_output(N_("Is Viewport")); } static void geo_node_is_viewport_exec(GeoNodeExecParams params) diff --git a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc index 93643298f92..110b4a30dc8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_join_geometry.cc @@ -33,8 +33,8 @@ namespace blender::nodes { static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry").multi_input(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).multi_input(); + b.add_output(N_("Geometry")); } static Mesh *join_mesh_topology_and_builtin_attributes(Span src_components) @@ -209,7 +209,7 @@ static void join_attributes(Span src_components, const Map info = get_final_attribute_info(src_components, ignored_attributes); - for (const Map::Item &item : info.items()) { + for (const Map::Item item : info.items()) { const AttributeIDRef attribute_id = item.key; const AttributeMetaData &meta_data = item.value; @@ -270,17 +270,16 @@ static void join_components(Span src_components, Geo } Span src_transforms = src_component->instance_transforms(); - Span src_ids = src_component->instance_ids(); Span src_reference_handles = src_component->instance_reference_handles(); for (const int i : src_transforms.index_range()) { const int src_handle = src_reference_handles[i]; const int dst_handle = handle_map[src_handle]; const float4x4 &transform = src_transforms[i]; - const int id = src_ids[i]; - dst_component.add_instance(dst_handle, transform, id); + dst_component.add_instance(dst_handle, transform); } } + join_attributes(to_base_components(src_components), dst_component, {"position"}); } static void join_components(Span src_components, GeometrySet &result) @@ -357,6 +356,24 @@ static void ensure_control_point_attribute(const AttributeIDRef &attribute_id, } } +/** + * Curve point domain attributes must be in the same order on every spline. The order might have + * been different on separate instances, so ensure that all splines have the same order. Note that + * because #Map is used, the order is not necessarily consistent every time, but it is the same for + * every spline, and that's what matters. + */ +static void sort_curve_point_attributes(const Map &info, + MutableSpan splines) +{ + Vector new_order; + for (const AttributeIDRef attribute_id : info.keys()) { + new_order.append(attribute_id); + } + for (SplinePtr &spline : splines) { + spline->attributes.reorder(new_order); + } +} + /** * Fill data for an attribute on the new curve based on all source curves. */ @@ -399,7 +416,7 @@ static void join_curve_attributes(const Map & Span src_components, CurveEval &result) { - for (const Map::Item &item : info.items()) { + for (const Map::Item item : info.items()) { const AttributeIDRef attribute_id = item.key; const AttributeMetaData meta_data = item.value; @@ -410,6 +427,8 @@ static void join_curve_attributes(const Map & ensure_control_point_attribute(attribute_id, meta_data.data_type, src_components, result); } } + + sort_curve_point_attributes(info, result.splines()); } static void join_curve_components(MutableSpan src_geometry_sets, GeometrySet &result) @@ -437,7 +456,7 @@ static void join_curve_components(MutableSpan src_geometry_sets, Ge /* Retrieve attribute info before moving the splines out of the input components. */ const Map info = get_final_attribute_info( {(const GeometryComponent **)src_components.data(), src_components.size()}, - {"position", "radius", "tilt", "cyclic", "resolution"}); + {"position", "radius", "tilt", "handle_left", "handle_right", "cyclic", "resolution"}); CurveComponent &dst_component = result.get_component_for_write(); CurveEval *dst_curve = new CurveEval(); @@ -450,6 +469,7 @@ static void join_curve_components(MutableSpan src_geometry_sets, Ge dst_curve->attributes.reallocate(dst_curve->splines().size()); join_curve_attributes(info, src_components, *dst_curve); + dst_curve->assert_valid_point_attributes(); dst_component.replace(dst_curve); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc index a9c3bfc6ce0..e4a62bd5267 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_replace.cc @@ -28,10 +28,10 @@ namespace blender::nodes { static void geo_node_material_replace_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Old"); - b.add_input("New"); - b.add_output("Geometry"); + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Old")); + b.add_input(N_("New")); + b.add_output(N_("Geometry")); } static void geo_node_material_replace_exec(GeoNodeExecParams params) @@ -40,11 +40,9 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params) Material *new_material = params.extract_input("New"); GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); - if (geometry_set.has()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - Mesh *mesh = mesh_component.get_for_write(); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = geometry_set.get_mesh_for_write(); if (mesh != nullptr) { for (const int i : IndexRange(mesh->totcol)) { if (mesh->mat[i] == old_material) { @@ -52,7 +50,7 @@ static void geo_node_material_replace_exec(GeoNodeExecParams params) } } } - } + }); params.set_output("Geometry", std::move(geometry_set)); } @@ -64,7 +62,7 @@ void register_node_type_geo_material_replace() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_MATERIAL_REPLACE, "Material Replace", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_REPLACE_MATERIAL, "Replace Material", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_material_replace_declare; ntype.geometry_node_execute = blender::nodes::geo_node_material_replace_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc index 22c24e34314..06c770820ee 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_material_selection.cc @@ -30,8 +30,8 @@ namespace blender::nodes { static void geo_node_material_selection_declare(NodeDeclarationBuilder &b) { - b.add_input("Material").hide_label(true); - b.add_output("Selection"); + b.add_input(N_("Material")).hide_label(true); + b.add_output(N_("Selection")).field_source(); } static void select_mesh_by_material(const Mesh &mesh, @@ -59,8 +59,9 @@ class MaterialSelectionFieldInput final : public fn::FieldInput { public: MaterialSelectionFieldInput(Material *material) - : fn::FieldInput(CPPType::get(), "Material Selection"), material_(material) + : fn::FieldInput(CPPType::get(), "Material Selection node"), material_(material) { + category_ = Category::Generated; } const GVArray *get_varray_for_context(const fn::FieldContext &context, @@ -100,13 +101,16 @@ class MaterialSelectionFieldInput final : public fn::FieldInput { uint64_t hash() const override { - /* Some random constant hash. */ - return 91619626; + return get_default_hash(material_); } bool is_equal_to(const fn::FieldNode &other) const override { - return dynamic_cast(&other) != nullptr; + if (const MaterialSelectionFieldInput *other_material_selection = + dynamic_cast(&other)) { + return material_ == other_material_selection->material_; + } + return false; } }; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 9c477c639a2..f1f95be107a 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -29,9 +29,16 @@ namespace blender::nodes { static void geo_node_mesh_primitive_circle_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices").default_value(32).min(3); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_input(N_("Vertices")) + .default_value(32) + .min(3) + .description(N_("Number of vertices on the circle")); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance of the vertices from the origin")); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_circle_layout(uiLayout *layout, @@ -204,7 +211,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) const int verts_num = params.extract_input("Vertices"); if (verts_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -212,7 +219,7 @@ static void geo_node_mesh_primitive_circle_exec(GeoNodeExecParams params) BLI_assert(BKE_mesh_is_valid(mesh)); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index 0d58476fc58..77dfeed6cbb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -29,20 +29,36 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cone_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices").default_value(32).min(3); - b.add_input("Radius Top").min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Radius Bottom").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); -} - -static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, - bContext *UNUSED(C), - PointerRNA *ptr) -{ - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); + b.add_input(N_("Vertices")) + .default_value(32) + .min(3) + .max(512) + .description(N_("Number of points on the circle at the top and bottom")); + b.add_input(N_("Side Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of edges running vertically along the side of the cone")); + b.add_input(N_("Fill Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("Number of concentric rings used to fill the round face")); + b.add_input(N_("Radius Top")) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the top circle of the cone")); + b.add_input(N_("Radius Bottom")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Radius of the bottom circle of the cone")); + b.add_input(N_("Depth")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Height of the generated cone")); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -55,139 +71,486 @@ static void geo_node_mesh_primitive_cone_init(bNodeTree *UNUSED(ntree), bNode *n node->storage = node_storage; } -static int vert_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +static void geo_node_mesh_primitive_cone_update(bNodeTree *UNUSED(ntree), bNode *node) { - int vert_total = 0; - if (!top_is_point) { - vert_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - vert_total++; - } + bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *rings_socket = vertices_socket->next; + bNodeSocket *fill_subdiv_socket = rings_socket->next; + + const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage; + const GeometryNodeMeshCircleFillType fill_type = + static_cast(storage.fill_type); + const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + nodeSetSocketAvailability(fill_subdiv_socket, has_fill); +} + +static void geo_node_mesh_primitive_cone_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiItemR(layout, ptr, "fill_type", 0, nullptr, ICON_NONE); +} + +struct ConeConfig { + float radius_top; + float radius_bottom; + float height; + int circle_segments; + int side_segments; + int fill_segments; + GeometryNodeMeshCircleFillType fill_type; + + bool top_is_point; + bool bottom_is_point; + /* The cone tip and a triangle fan filling are topologically identical. + * This simplifies the logic in some cases. */ + bool top_has_center_vert; + bool bottom_has_center_vert; + + /* Helpful quantities. */ + int tot_quad_rings; + int tot_edge_rings; + int tot_verts; + int tot_edges; + + /* Helpful vertex indices. */ + int first_vert; + int first_ring_verts_start; + int last_ring_verts_start; + int last_vert; + + /* Helpful edge indices. */ + int first_ring_edges_start; + int last_ring_edges_start; + int last_fan_edges_start; + int last_edge; + + ConeConfig(float radius_top, + float radius_bottom, + float depth, + int circle_segments, + int side_segments, + int fill_segments, + GeometryNodeMeshCircleFillType fill_type) + : radius_top(radius_top), + radius_bottom(radius_bottom), + height(0.5f * depth), + circle_segments(circle_segments), + side_segments(side_segments), + fill_segments(fill_segments), + fill_type(fill_type) + { + this->top_is_point = this->radius_top == 0.0f; + this->bottom_is_point = this->radius_bottom == 0.0f; + this->top_has_center_vert = this->top_is_point || + this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN; + this->bottom_has_center_vert = this->bottom_is_point || + this->fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN; + + this->tot_quad_rings = this->calculate_total_quad_rings(); + this->tot_edge_rings = this->calculate_total_edge_rings(); + this->tot_verts = this->calculate_total_verts(); + this->tot_edges = this->calculate_total_edges(); + + this->first_vert = 0; + this->first_ring_verts_start = this->top_has_center_vert ? 1 : first_vert; + this->last_vert = this->tot_verts - 1; + this->last_ring_verts_start = this->last_vert - this->circle_segments; + + this->first_ring_edges_start = this->top_has_center_vert ? this->circle_segments : 0; + this->last_ring_edges_start = this->first_ring_edges_start + + this->tot_quad_rings * this->circle_segments * 2; + this->last_fan_edges_start = this->tot_edges - this->circle_segments; + this->last_edge = this->tot_edges - 1; } - else { + + private: + int calculate_total_quad_rings(); + int calculate_total_edge_rings(); + int calculate_total_verts(); + int calculate_total_edges(); + + public: + int get_tot_corners() const; + int get_tot_faces() const; +}; + +int ConeConfig::calculate_total_quad_rings() +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int quad_rings = 0; + + if (!top_is_point) { + quad_rings += fill_segments - 1; + } + + quad_rings += (!top_is_point && !bottom_is_point) ? side_segments : (side_segments - 1); + + if (!bottom_is_point) { + quad_rings += fill_segments - 1; + } + + return quad_rings; +} + +int ConeConfig::calculate_total_edge_rings() +{ + if (top_is_point && bottom_is_point) { + return 0; + } + + int edge_rings = 0; + + if (!top_is_point) { + edge_rings += fill_segments; + } + + edge_rings += side_segments - 1; + + if (!bottom_is_point) { + edge_rings += fill_segments; + } + + return edge_rings; +} + +int ConeConfig::calculate_total_verts() +{ + if (top_is_point && bottom_is_point) { + return side_segments + 1; + } + + int vert_total = 0; + + if (top_has_center_vert) { vert_total++; } - if (!bottom_is_point) { - vert_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - vert_total++; - } + + if (!top_is_point) { + vert_total += circle_segments * fill_segments; } - else { + + vert_total += circle_segments * (side_segments - 1); + + if (!bottom_is_point) { + vert_total += circle_segments * fill_segments; + } + + if (bottom_has_center_vert) { vert_total++; } return vert_total; } -static int edge_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::calculate_total_edges() { if (top_is_point && bottom_is_point) { - return 1; + return side_segments; } int edge_total = 0; - if (!top_is_point) { - edge_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - edge_total += verts_num; - } + if (top_has_center_vert) { + edge_total += circle_segments; } - edge_total += verts_num; + edge_total += circle_segments * (tot_quad_rings * 2 + 1); - if (!bottom_is_point) { - edge_total += verts_num; - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - edge_total += verts_num; - } + if (bottom_has_center_vert) { + edge_total += circle_segments; } return edge_total; } -static int corner_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::get_tot_corners() const { if (top_is_point && bottom_is_point) { return 0; } int corner_total = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - corner_total += verts_num; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - corner_total += verts_num * 3; - } + + if (top_has_center_vert) { + corner_total += (circle_segments * 3); + } + else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += circle_segments; } - if (!top_is_point && !bottom_is_point) { - corner_total += verts_num * 4; - } - else { - corner_total += verts_num * 3; - } + corner_total += tot_quad_rings * (circle_segments * 4); - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - corner_total += verts_num; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - corner_total += verts_num * 3; - } + if (bottom_has_center_vert) { + corner_total += (circle_segments * 3); + } + else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + corner_total += circle_segments; } return corner_total; } -static int face_total(const GeometryNodeMeshCircleFillType fill_type, - const int verts_num, - const bool top_is_point, - const bool bottom_is_point) +int ConeConfig::get_tot_faces() const { if (top_is_point && bottom_is_point) { return 0; } int face_total = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - face_total += verts_num; - } + if (top_has_center_vert) { + face_total += circle_segments; + } + else if (!top_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; } - face_total += verts_num; + face_total += tot_quad_rings * circle_segments; - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - face_total++; - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - face_total += verts_num; - } + if (bottom_has_center_vert) { + face_total += circle_segments; + } + else if (!bottom_is_point && fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + face_total++; } return face_total; } -static void calculate_uvs(Mesh *mesh, - const bool top_is_point, - const bool bottom_is_point, - const int verts_num, - const GeometryNodeMeshCircleFillType fill_type) +static void calculate_cone_vertices(const MutableSpan &verts, const ConeConfig &config) +{ + Array circle(config.circle_segments); + const float angle_delta = 2.0f * (M_PI / static_cast(config.circle_segments)); + float angle = 0.0f; + for (const int i : IndexRange(config.circle_segments)) { + circle[i].x = std::cos(angle); + circle[i].y = std::sin(angle); + angle += angle_delta; + } + + int vert_index = 0; + + /* Top cone tip or triangle fan center. */ + if (config.top_has_center_vert) { + copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, config.height); + } + + /* Top fill including the outer edge of the fill. */ + if (!config.top_is_point) { + const float top_fill_radius_delta = config.radius_top / + static_cast(config.fill_segments); + for (const int i : IndexRange(config.fill_segments)) { + const float top_fill_radius = top_fill_radius_delta * (i + 1); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * top_fill_radius; + const float y = circle[j].y * top_fill_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, config.height); + } + } + } + + /* Rings along the side. */ + const float side_radius_delta = (config.radius_bottom - config.radius_top) / + static_cast(config.side_segments); + const float height_delta = 2.0f * config.height / static_cast(config.side_segments); + for (const int i : IndexRange(config.side_segments - 1)) { + const float ring_radius = config.radius_top + (side_radius_delta * (i + 1)); + const float ring_height = config.height - (height_delta * (i + 1)); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * ring_radius; + const float y = circle[j].y * ring_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, ring_height); + } + } + + /* Bottom fill including the outer edge of the fill. */ + if (!config.bottom_is_point) { + const float bottom_fill_radius_delta = config.radius_bottom / + static_cast(config.fill_segments); + for (const int i : IndexRange(config.fill_segments)) { + const float bottom_fill_radius = config.radius_bottom - (i * bottom_fill_radius_delta); + for (const int j : IndexRange(config.circle_segments)) { + const float x = circle[j].x * bottom_fill_radius; + const float y = circle[j].y * bottom_fill_radius; + copy_v3_fl3(verts[vert_index++].co, x, y, -config.height); + } + } + } + + /* Bottom cone tip or triangle fan center. */ + if (config.bottom_has_center_vert) { + copy_v3_fl3(verts[vert_index++].co, 0.0f, 0.0f, -config.height); + } +} + +static void calculate_cone_edges(const MutableSpan &edges, const ConeConfig &config) +{ + int edge_index = 0; + + /* Edges for top cone tip or triangle fan */ + if (config.top_has_center_vert) { + for (const int i : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = config.first_vert; + edge.v2 = config.first_ring_verts_start + i; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Rings and connecting edges between the rings. */ + for (const int i : IndexRange(config.tot_edge_rings)) { + const int this_ring_vert_start = config.first_ring_verts_start + (i * config.circle_segments); + const int next_ring_vert_start = this_ring_vert_start + config.circle_segments; + /* Edge rings. */ + for (const int j : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = this_ring_vert_start + j; + edge.v2 = this_ring_vert_start + ((j + 1) % config.circle_segments); + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + if (i == config.tot_edge_rings - 1) { + /* There is one fewer ring of connecting edges. */ + break; + } + /* Connecting edges. */ + for (const int j : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = this_ring_vert_start + j; + edge.v2 = next_ring_vert_start + j; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } + + /* Edges for bottom triangle fan or tip. */ + if (config.bottom_has_center_vert) { + for (const int i : IndexRange(config.circle_segments)) { + MEdge &edge = edges[edge_index++]; + edge.v1 = config.last_ring_verts_start + i; + edge.v2 = config.last_vert; + edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + } + } +} + +static void calculate_cone_faces(const MutableSpan &loops, + const MutableSpan &polys, + const ConeConfig &config) +{ + int loop_index = 0; + int poly_index = 0; + + if (config.top_has_center_vert) { + /* Top cone tip or center triangle fan in the fill. */ + const int top_center_vert = 0; + const int top_fan_edges_start = 0; + + for (const int i : IndexRange(config.circle_segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = config.first_ring_verts_start + i; + loop_a.e = config.first_ring_edges_start + i; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = config.first_ring_verts_start + ((i + 1) % config.circle_segments); + loop_b.e = top_fan_edges_start + ((i + 1) % config.circle_segments); + MLoop &loop_c = loops[loop_index++]; + loop_c.v = top_center_vert; + loop_c.e = top_fan_edges_start + i; + } + } + else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* Center n-gon in the fill. */ + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = config.circle_segments; + for (const int i : IndexRange(config.circle_segments)) { + MLoop &loop = loops[loop_index++]; + loop.v = i; + loop.e = i; + } + } + + /* Quads connect one edge ring to the next one. */ + if (config.tot_quad_rings > 0) { + for (const int i : IndexRange(config.tot_quad_rings)) { + const int this_ring_vert_start = config.first_ring_verts_start + + (i * config.circle_segments); + const int next_ring_vert_start = this_ring_vert_start + config.circle_segments; + + const int this_ring_edges_start = config.first_ring_edges_start + + (i * 2 * config.circle_segments); + const int next_ring_edges_start = this_ring_edges_start + (2 * config.circle_segments); + const int ring_connections_start = this_ring_edges_start + config.circle_segments; + + for (const int j : IndexRange(config.circle_segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 4; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = this_ring_vert_start + j; + loop_a.e = ring_connections_start + j; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = next_ring_vert_start + j; + loop_b.e = next_ring_edges_start + j; + MLoop &loop_c = loops[loop_index++]; + loop_c.v = next_ring_vert_start + ((j + 1) % config.circle_segments); + loop_c.e = ring_connections_start + ((j + 1) % config.circle_segments); + MLoop &loop_d = loops[loop_index++]; + loop_d.v = this_ring_vert_start + ((j + 1) % config.circle_segments); + loop_d.e = this_ring_edges_start + j; + } + } + } + + if (config.bottom_has_center_vert) { + /* Bottom cone tip or center triangle fan in the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = 3; + + MLoop &loop_a = loops[loop_index++]; + loop_a.v = config.last_ring_verts_start + i; + loop_a.e = config.last_fan_edges_start + i; + MLoop &loop_b = loops[loop_index++]; + loop_b.v = config.last_vert; + loop_b.e = config.last_fan_edges_start + (i + 1) % config.circle_segments; + MLoop &loop_c = loops[loop_index++]; + loop_c.v = config.last_ring_verts_start + (i + 1) % config.circle_segments; + loop_c.e = config.last_ring_edges_start + i; + } + } + else if (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* Center n-gon in the fill. */ + MPoly &poly = polys[poly_index++]; + poly.loopstart = loop_index; + poly.totloop = config.circle_segments; + + for (const int i : IndexRange(config.circle_segments)) { + /* Go backwards to reverse surface normal. */ + MLoop &loop = loops[loop_index++]; + loop.v = config.last_vert - i; + loop.e = config.last_edge - ((i + 1) % config.circle_segments); + } + } +} + +/** + * If the top is the cone tip or has a fill, it is unwrapped into a circle in the + * lower left quadrant of the UV. + * Likewise, if the bottom is the cone tip or has a fill, it is unwrapped into a circle + * in the lower right quadrant of the UV. + * If the mesh is a truncated cone or a cylinder, the side faces are unwrapped into + * a rectangle that fills the top half of the UV (or the entire UV, if there are no fills). + */ +static void calculate_cone_uvs(Mesh *mesh, const ConeConfig &config) { MeshComponent mesh_component; mesh_component.replace(mesh, GeometryOwnershipType::Editable); @@ -195,75 +558,110 @@ static void calculate_uvs(Mesh *mesh, mesh_component.attribute_try_get_for_output_only("uv_map", ATTR_DOMAIN_CORNER); MutableSpan uvs = uv_attribute.as_span(); - Array circle(verts_num); + Array circle(config.circle_segments); float angle = 0.0f; - const float angle_delta = 2.0f * M_PI / static_cast(verts_num); - for (const int i : IndexRange(verts_num)) { - circle[i].x = std::cos(angle) * 0.225f + 0.25f; - circle[i].y = std::sin(angle) * 0.225f + 0.25f; + const float angle_delta = 2.0f * M_PI / static_cast(config.circle_segments); + for (const int i : IndexRange(config.circle_segments)) { + circle[i].x = std::cos(angle) * 0.225f; + circle[i].y = std::sin(angle) * 0.225f; angle += angle_delta; } int loop_index = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; + + /* Left circle of the UV representing the top fill or top cone tip. */ + if (config.top_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { + const float2 center_left(0.25f, 0.25f); + const float radius_factor_delta = 1.0f / (config.top_is_point ? + static_cast(config.side_segments) : + static_cast(config.fill_segments)); + const int left_circle_segment_count = config.top_is_point ? config.side_segments : + config.fill_segments; + + if (config.top_has_center_vert) { + /* Cone tip itself or triangle fan center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_left; + uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] + + center_left; + uvs[loop_index++] = center_left; } } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; - uvs[loop_index++] = circle[(i + 1) % verts_num]; - uvs[loop_index++] = float2(0.25f, 0.25f); + else if (!config.top_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* N-gon at the center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_left; + } + } + /* The rest of the top fill is made out of quad rings. */ + for (const int i : IndexRange(1, left_circle_segment_count - 1)) { + const float inner_radius_factor = i * radius_factor_delta; + const float outer_radius_factor = (i + 1) * radius_factor_delta; + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = inner_radius_factor * circle[j] + center_left; + uvs[loop_index++] = outer_radius_factor * circle[j] + center_left; + uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] + + center_left; + uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] + + center_left; } } } - /* Create side corners and faces. */ - if (!top_is_point && !bottom_is_point) { - const float bottom = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f; - /* Quads connect the top and bottom. */ - for (const int i : IndexRange(verts_num)) { - const float vert = static_cast(i); - uvs[loop_index++] = float2(vert / verts_num, bottom); - uvs[loop_index++] = float2(vert / verts_num, 1.0f); - uvs[loop_index++] = float2((vert + 1.0f) / verts_num, 1.0f); - uvs[loop_index++] = float2((vert + 1.0f) / verts_num, bottom); - } - } - else { - /* Triangles connect the top and bottom section. */ - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f); - uvs[loop_index++] = float2(0.75f, 0.25f); - uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f); - } - } - else { - BLI_assert(!bottom_is_point); - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i]; - uvs[loop_index++] = circle[(i + 1) % verts_num]; - uvs[loop_index++] = float2(0.25f, 0.25f); + if (!config.top_is_point && !config.bottom_is_point) { + /* Mesh is a truncated cone or cylinder. The sides are unwrapped into a rectangle. */ + const float bottom = (config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? 0.0f : 0.5f; + const float x_delta = 1.0f / static_cast(config.circle_segments); + const float y_delta = (1.0f - bottom) / static_cast(config.side_segments); + + for (const int i : IndexRange(config.side_segments)) { + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = float2(j * x_delta, i * y_delta + bottom); + uvs[loop_index++] = float2(j * x_delta, (i + 1) * y_delta + bottom); + uvs[loop_index++] = float2((j + 1) * x_delta, (i + 1) * y_delta + bottom); + uvs[loop_index++] = float2((j + 1) * x_delta, i * y_delta + bottom); } } } - /* Create bottom corners and faces. */ - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - for (const int i : IndexRange(verts_num)) { + /* Right circle of the UV representing the bottom fill or bottom cone tip. */ + if (config.bottom_is_point || config.fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE) { + const float2 center_right(0.75f, 0.25f); + const float radius_factor_delta = 1.0f / (config.bottom_is_point ? + static_cast(config.side_segments) : + static_cast(config.fill_segments)); + const int right_circle_segment_count = config.bottom_is_point ? config.side_segments : + config.fill_segments; + + /* The bottom circle has to be created outside in to match the loop order. */ + for (const int i : IndexRange(right_circle_segment_count - 1)) { + const float outer_radius_factor = 1.0f - i * radius_factor_delta; + const float inner_radius_factor = 1.0f - (i + 1) * radius_factor_delta; + for (const int j : IndexRange(config.circle_segments)) { + uvs[loop_index++] = outer_radius_factor * circle[j] + center_right; + uvs[loop_index++] = inner_radius_factor * circle[j] + center_right; + uvs[loop_index++] = inner_radius_factor * circle[(j + 1) % config.circle_segments] + + center_right; + uvs[loop_index++] = outer_radius_factor * circle[(j + 1) % config.circle_segments] + + center_right; + } + } + + if (config.bottom_has_center_vert) { + /* Cone tip itself or triangle fan center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { + uvs[loop_index++] = radius_factor_delta * circle[i] + center_right; + uvs[loop_index++] = center_right; + uvs[loop_index++] = radius_factor_delta * circle[(i + 1) % config.circle_segments] + + center_right; + } + } + else if (!config.bottom_is_point && config.fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { + /* N-gon at the center of the fill. */ + for (const int i : IndexRange(config.circle_segments)) { /* Go backwards because of reversed face normal. */ - uvs[loop_index++] = circle[verts_num - 1 - i] + float2(0.5f, 0.0f); - } - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - uvs[loop_index++] = circle[i] + float2(0.5f, 0.0f); - uvs[loop_index++] = float2(0.75f, 0.25f); - uvs[loop_index++] = circle[(i + 1) % verts_num] + float2(0.5f, 0.0f); + uvs[loop_index++] = radius_factor_delta * circle[config.circle_segments - 1 - i] + + center_right; } } } @@ -271,268 +669,55 @@ static void calculate_uvs(Mesh *mesh, uv_attribute.save(); } +static Mesh *create_vertex_mesh() +{ + /* Returns a mesh with a single vertex at the origin. */ + Mesh *mesh = BKE_mesh_new_nomain(1, 0, 0, 0, 0); + copy_v3_fl3(mesh->mvert[0].co, 0.0f, 0.0f, 0.0f); + const short up[3] = {0, 0, SHRT_MAX}; + copy_v3_v3_short(mesh->mvert[0].no, up); + return mesh; +} + Mesh *create_cylinder_or_cone_mesh(const float radius_top, const float radius_bottom, const float depth, - const int verts_num, + const int circle_segments, + const int side_segments, + const int fill_segments, const GeometryNodeMeshCircleFillType fill_type) { - const bool top_is_point = radius_top == 0.0f; - const bool bottom_is_point = radius_bottom == 0.0f; - const float height = depth * 0.5f; + const ConeConfig config( + radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type); + /* Handle the case of a line / single point before everything else to avoid * the need to check for it later. */ - if (top_is_point && bottom_is_point) { - const bool single_vertex = height == 0.0f; - Mesh *mesh = BKE_mesh_new_nomain(single_vertex ? 1 : 2, single_vertex ? 0 : 1, 0, 0, 0); - copy_v3_v3(mesh->mvert[0].co, float3(0.0f, 0.0f, height)); - if (single_vertex) { - const short up[3] = {0, 0, SHRT_MAX}; - copy_v3_v3_short(mesh->mvert[0].no, up); - return mesh; + if (config.top_is_point && config.bottom_is_point) { + if (config.height == 0.0f) { + return create_vertex_mesh(); } - copy_v3_v3(mesh->mvert[1].co, float3(0.0f, 0.0f, -height)); - mesh->medge[0].v1 = 0; - mesh->medge[0].v2 = 1; - mesh->medge[0].flag |= ME_LOOSEEDGE; - BKE_mesh_normals_tag_dirty(mesh); - return mesh; + const float z_delta = -2.0f * config.height / static_cast(config.side_segments); + const float3 start(0.0f, 0.0f, config.height); + const float3 delta(0.0f, 0.0f, z_delta); + return create_line_mesh(start, delta, config.tot_verts); } Mesh *mesh = BKE_mesh_new_nomain( - vert_total(fill_type, verts_num, top_is_point, bottom_is_point), - edge_total(fill_type, verts_num, top_is_point, bottom_is_point), - 0, - corner_total(fill_type, verts_num, top_is_point, bottom_is_point), - face_total(fill_type, verts_num, top_is_point, bottom_is_point)); + config.tot_verts, config.tot_edges, 0, config.get_tot_corners(), config.get_tot_faces()); BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan verts{mesh->mvert, mesh->totvert}; MutableSpan loops{mesh->mloop, mesh->totloop}; MutableSpan edges{mesh->medge, mesh->totedge}; MutableSpan polys{mesh->mpoly, mesh->totpoly}; - /* Calculate vertex positions. */ - const int top_verts_start = 0; - const int bottom_verts_start = top_verts_start + (!top_is_point ? verts_num : 1); - const float angle_delta = 2.0f * (M_PI / static_cast(verts_num)); - for (const int i : IndexRange(verts_num)) { - const float angle = i * angle_delta; - const float x = std::cos(angle); - const float y = std::sin(angle); - if (!top_is_point) { - copy_v3_v3(verts[top_verts_start + i].co, float3(x * radius_top, y * radius_top, height)); - } - if (!bottom_is_point) { - copy_v3_v3(verts[bottom_verts_start + i].co, - float3(x * radius_bottom, y * radius_bottom, -height)); - } - } - if (top_is_point) { - copy_v3_v3(verts[top_verts_start].co, float3(0.0f, 0.0f, height)); - } - if (bottom_is_point) { - copy_v3_v3(verts[bottom_verts_start].co, float3(0.0f, 0.0f, -height)); - } - - /* Add center vertices for the triangle fans at the end. */ - const int top_center_vert_index = bottom_verts_start + (bottom_is_point ? 1 : verts_num); - const int bottom_center_vert_index = top_center_vert_index + (top_is_point ? 0 : 1); - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - if (!top_is_point) { - copy_v3_v3(verts[top_center_vert_index].co, float3(0.0f, 0.0f, height)); - } - if (!bottom_is_point) { - copy_v3_v3(verts[bottom_center_vert_index].co, float3(0.0f, 0.0f, -height)); - } - } - - /* Create top edges. */ - const int top_edges_start = 0; - const int top_fan_edges_start = (!top_is_point && - fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? - top_edges_start + verts_num : - top_edges_start; - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[top_edges_start + i]; - edge.v1 = top_verts_start + i; - edge.v2 = top_verts_start + (i + 1) % verts_num; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[top_fan_edges_start + i]; - edge.v1 = top_center_vert_index; - edge.v2 = top_verts_start + i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - } - - /* Create connecting edges. */ - const int connecting_edges_start = top_fan_edges_start + (!top_is_point ? verts_num : 0); - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[connecting_edges_start + i]; - edge.v1 = top_verts_start + (!top_is_point ? i : 0); - edge.v2 = bottom_verts_start + (!bottom_is_point ? i : 0); - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - - /* Create bottom edges. */ - const int bottom_edges_start = connecting_edges_start + verts_num; - const int bottom_fan_edges_start = (!bottom_is_point && - fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) ? - bottom_edges_start + verts_num : - bottom_edges_start; - if (!bottom_is_point) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[bottom_edges_start + i]; - edge.v1 = bottom_verts_start + i; - edge.v2 = bottom_verts_start + (i + 1) % verts_num; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MEdge &edge = edges[bottom_fan_edges_start + i]; - edge.v1 = bottom_center_vert_index; - edge.v2 = bottom_verts_start + i; - edge.flag = ME_EDGEDRAW | ME_EDGERENDER; - } - } - } - - /* Create top corners and faces. */ - int loop_index = 0; - int poly_index = 0; - if (!top_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = verts_num; - - for (const int i : IndexRange(verts_num)) { - MLoop &loop = loops[loop_index++]; - loop.v = top_verts_start + i; - loop.e = top_edges_start + i; - } - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = top_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = top_verts_start + (i + 1) % verts_num; - loop_b.e = top_fan_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_center_vert_index; - loop_c.e = top_fan_edges_start + i; - } - } - } - - /* Create side corners and faces. */ - if (!top_is_point && !bottom_is_point) { - /* Quads connect the top and bottom. */ - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 4; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = connecting_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start + i; - loop_b.e = bottom_edges_start + i; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = bottom_verts_start + (i + 1) % verts_num; - loop_c.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_d = loops[loop_index++]; - loop_d.v = top_verts_start + (i + 1) % verts_num; - loop_d.e = top_edges_start + i; - } - } - else { - /* Triangles connect the top and bottom section. */ - if (!top_is_point) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = top_verts_start + i; - loop_a.e = connecting_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start; - loop_b.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_verts_start + (i + 1) % verts_num; - loop_c.e = top_edges_start + i; - } - } - else { - BLI_assert(!bottom_is_point); - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = bottom_verts_start + i; - loop_a.e = bottom_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_verts_start + (i + 1) % verts_num; - loop_b.e = connecting_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = top_verts_start; - loop_c.e = connecting_edges_start + i; - } - } - } - - /* Create bottom corners and faces. */ - if (!bottom_is_point) { - if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NGON) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = verts_num; - - for (const int i : IndexRange(verts_num)) { - /* Go backwards to reverse surface normal. */ - MLoop &loop = loops[loop_index++]; - loop.v = bottom_verts_start + verts_num - 1 - i; - loop.e = bottom_edges_start + verts_num - 1 - (i + 1) % verts_num; - } - } - else if (fill_type == GEO_NODE_MESH_CIRCLE_FILL_TRIANGLE_FAN) { - for (const int i : IndexRange(verts_num)) { - MPoly &poly = polys[poly_index++]; - poly.loopstart = loop_index; - poly.totloop = 3; - - MLoop &loop_a = loops[loop_index++]; - loop_a.v = bottom_verts_start + i; - loop_a.e = bottom_fan_edges_start + i; - MLoop &loop_b = loops[loop_index++]; - loop_b.v = bottom_center_vert_index; - loop_b.e = bottom_fan_edges_start + (i + 1) % verts_num; - MLoop &loop_c = loops[loop_index++]; - loop_c.v = bottom_verts_start + (i + 1) % verts_num; - loop_c.e = bottom_edges_start + i; - } - } - } + calculate_cone_vertices(verts, config); + calculate_cone_edges(edges, config); + calculate_cone_faces(loops, polys, config); + calculate_cone_uvs(mesh, config); BKE_mesh_normals_tag_dirty(mesh); - calculate_uvs(mesh, top_is_point, bottom_is_point, verts_num, fill_type); - return mesh; } @@ -540,14 +725,28 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) { const bNode &node = params.node(); const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node.storage; - const GeometryNodeMeshCircleFillType fill_type = (const GeometryNodeMeshCircleFillType) storage.fill_type; - const int verts_num = params.extract_input("Vertices"); - if (verts_num < 3) { + const int circle_segments = params.extract_input("Vertices"); + if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); + return; + } + + const int side_segments = params.extract_input("Side Segments"); + if (side_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); + params.set_output("Mesh", GeometrySet()); + return; + } + + const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE; + const int fill_segments = no_fill ? 1 : params.extract_input("Fill Segments"); + if (fill_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); + params.set_output("Mesh", GeometrySet()); return; } @@ -556,12 +755,12 @@ static void geo_node_mesh_primitive_cone_exec(GeoNodeExecParams params) const float depth = params.extract_input("Depth"); Mesh *mesh = create_cylinder_or_cone_mesh( - radius_top, radius_bottom, depth, verts_num, fill_type); + radius_top, radius_bottom, depth, circle_segments, side_segments, fill_segments, fill_type); /* Transform the mesh so that the base of the cone is at the origin. */ BKE_mesh_translate(mesh, float3(0.0f, 0.0f, depth * 0.5f), false); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes @@ -572,6 +771,7 @@ void register_node_type_geo_mesh_primitive_cone() geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CONE, "Cone", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cone_init); + node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cone_update); node_type_storage( &ntype, "NodeGeometryMeshCone", node_free_standard_storage, node_copy_standard_storage); ntype.geometry_node_execute = blender::nodes::geo_node_mesh_primitive_cone_exec; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc index af8ce02b3c1..b5903f7b71e 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cube.cc @@ -26,11 +26,27 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cube_declare(NodeDeclarationBuilder &b) { - b.add_input("Size").default_value(float3(1)).min(0.0f).subtype(PROP_TRANSLATION); - b.add_input("Vertices X").default_value(2).min(2).max(1000); - b.add_input("Vertices Y").default_value(2).min(2).max(1000); - b.add_input("Vertices Z").default_value(2).min(2).max(1000); - b.add_output("Geometry"); + b.add_input(N_("Size")) + .default_value(float3(1)) + .min(0.0f) + .subtype(PROP_TRANSLATION) + .description(N_("Side length along each axis")); + b.add_input(N_("Vertices X")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the X side of the shape")); + b.add_input(N_("Vertices Y")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Y side of the shape")); + b.add_input(N_("Vertices Z")) + .default_value(2) + .min(2) + .max(1000) + .description(N_("Number of vertices for the Z side of the shape")); + b.add_output(N_("Mesh")); } struct CuboidConfig { @@ -456,12 +472,12 @@ static Mesh *create_cube_mesh(const float3 size, } if (verts_y == 1) { /* XZ plane. */ Mesh *mesh = create_grid_mesh(verts_x, verts_z, size.x, size.z); - transform_mesh(mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1)); + transform_mesh(*mesh, float3(0), float3(M_PI_2, 0.0f, 0.0f), float3(1)); return mesh; } /* YZ plane. */ Mesh *mesh = create_grid_mesh(verts_z, verts_y, size.z, size.y); - transform_mesh(mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1)); + transform_mesh(*mesh, float3(0), float3(0.0f, M_PI_2, 0.0f), float3(1)); return mesh; } @@ -476,13 +492,13 @@ static void geo_node_mesh_primitive_cube_exec(GeoNodeExecParams params) const int verts_z = params.extract_input("Vertices Z"); if (verts_x < 1 || verts_y < 1 || verts_z < 1) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 1")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } Mesh *mesh = create_cube_mesh(size, verts_x, verts_y, verts_z); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc index ed701c921ca..6edc9714f42 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cylinder.cc @@ -29,10 +29,32 @@ namespace blender::nodes { static void geo_node_mesh_primitive_cylinder_declare(NodeDeclarationBuilder &b) { - b.add_input("Vertices").default_value(32).min(3).max(4096); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Depth").default_value(2.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_input(N_("Vertices")) + .default_value(32) + .min(3) + .max(512) + .description(N_("The number of vertices on the top and bottom circles")); + b.add_input(N_("Side Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of rectangular segments along each side")); + b.add_input(N_("Fill Segments")) + .default_value(1) + .min(1) + .max(512) + .description(N_("The number of concentric rings used to fill the round faces")); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The radius of the cylinder")); + b.add_input(N_("Depth")) + .default_value(2.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("The height of the cylinder")); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_cylinder_layout(uiLayout *layout, @@ -54,6 +76,19 @@ static void geo_node_mesh_primitive_cylinder_init(bNodeTree *UNUSED(ntree), bNod node->storage = node_storage; } +static void geo_node_mesh_primitive_cylinder_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + bNodeSocket *vertices_socket = (bNodeSocket *)node->inputs.first; + bNodeSocket *rings_socket = vertices_socket->next; + bNodeSocket *fill_subdiv_socket = rings_socket->next; + + const NodeGeometryMeshCone &storage = *(const NodeGeometryMeshCone *)node->storage; + const GeometryNodeMeshCircleFillType fill_type = + static_cast(storage.fill_type); + const bool has_fill = fill_type != GEO_NODE_MESH_CIRCLE_FILL_NONE; + nodeSetSocketAvailability(fill_subdiv_socket, has_fill); +} + static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) { const bNode &node = params.node(); @@ -64,17 +99,33 @@ static void geo_node_mesh_primitive_cylinder_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); const float depth = params.extract_input("Depth"); - const int verts_num = params.extract_input("Vertices"); - if (verts_num < 3) { + const int circle_segments = params.extract_input("Vertices"); + if (circle_segments < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Vertices must be at least 3")); - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); + return; + } + + const int side_segments = params.extract_input("Side Segments"); + if (side_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Side Segments must be at least 1")); + params.set_output("Mesh", GeometrySet()); + return; + } + + const bool no_fill = fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE; + const int fill_segments = no_fill ? 1 : params.extract_input("Fill Segments"); + if (fill_segments < 1) { + params.error_message_add(NodeWarningType::Info, TIP_("Fill Segments must be at least 1")); + params.set_output("Mesh", GeometrySet()); return; } /* The cylinder is a special case of the cone mesh where the top and bottom radius are equal. */ - Mesh *mesh = create_cylinder_or_cone_mesh(radius, radius, depth, verts_num, fill_type); + Mesh *mesh = create_cylinder_or_cone_mesh( + radius, radius, depth, circle_segments, side_segments, fill_segments, fill_type); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes @@ -84,6 +135,7 @@ void register_node_type_geo_mesh_primitive_cylinder() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_MESH_PRIMITIVE_CYLINDER, "Cylinder", NODE_CLASS_GEOMETRY, 0); node_type_init(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_init); + node_type_update(&ntype, blender::nodes::geo_node_mesh_primitive_cylinder_update); node_type_storage( &ntype, "NodeGeometryMeshCylinder", node_free_standard_storage, node_copy_standard_storage); ntype.declare = blender::nodes::geo_node_mesh_primitive_cylinder_declare; diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index 858ef8648f8..73c679e18f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -29,11 +29,27 @@ namespace blender::nodes { static void geo_node_mesh_primitive_grid_declare(NodeDeclarationBuilder &b) { - b.add_input("Size X").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Size Y").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Vertices X").default_value(3).min(2).max(1000); - b.add_input("Vertices Y").default_value(3).min(2).max(1000); - b.add_output("Geometry"); + b.add_input(N_("Size X")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the X direction")); + b.add_input(N_("Size Y")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Side length of the plane in the Y direction")); + b.add_input(N_("Vertices X")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the X direction")); + b.add_input(N_("Vertices Y")) + .default_value(3) + .min(2) + .max(1000) + .description(N_("Number of vertices in the Y direction")); + b.add_output(N_("Mesh")); } static void calculate_uvs( @@ -160,7 +176,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) const int verts_x = params.extract_input("Vertices X"); const int verts_y = params.extract_input("Vertices Y"); if (verts_x < 1 || verts_y < 1) { - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } @@ -168,7 +184,7 @@ static void geo_node_mesh_primitive_grid_exec(GeoNodeExecParams params) BLI_assert(BKE_mesh_is_valid(mesh)); BKE_id_material_eval_ensure_default_slot(&mesh->id); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc index 5ea7165ac31..f49cc5e7e18 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_ico_sphere.cc @@ -28,9 +28,17 @@ namespace blender::nodes { static void geo_node_mesh_primitive_ico_sphere_declare(NodeDeclarationBuilder &b) { - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_input("Subdivisions").default_value(1).min(1).max(7); - b.add_output("Geometry"); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); + b.add_input(N_("Subdivisions")) + .default_value(1) + .min(1) + .max(7) + .description(N_("Number of subdivisions on top of the basic icosahedron")); + b.add_output(N_("Mesh")); } static Mesh *create_ico_sphere_mesh(const int subdivisions, const float radius) @@ -66,7 +74,7 @@ static void geo_node_mesh_primitive_ico_sphere_exec(GeoNodeExecParams params) const float radius = params.extract_input("Radius"); Mesh *mesh = create_ico_sphere_mesh(subdivisions, radius); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 031223b5ca6..df4efb2427c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -29,11 +29,26 @@ namespace blender::nodes { static void geo_node_mesh_primitive_line_declare(NodeDeclarationBuilder &b) { - b.add_input("Count").default_value(10).min(1).max(10000); - b.add_input("Resolution").default_value(1.0f).min(0.1f).subtype(PROP_DISTANCE); - b.add_input("Start Location").subtype(PROP_TRANSLATION); - b.add_input("Offset").default_value({0.0f, 0.0f, 1.0f}).subtype(PROP_TRANSLATION); - b.add_output("Geometry"); + b.add_input(N_("Count")) + .default_value(10) + .min(1) + .max(10000) + .description(N_("Number of vertices on the line")); + b.add_input(N_("Resolution")) + .default_value(1.0f) + .min(0.1f) + .subtype(PROP_DISTANCE) + .description(N_("Length of each individual edge")); + b.add_input(N_("Start Location")) + .subtype(PROP_TRANSLATION) + .description(N_("Position of the first vertex")); + b.add_input(N_("Offset")) + .default_value({0.0f, 0.0f, 1.0f}) + .subtype(PROP_TRANSLATION) + .description(N_( + "In offset mode, the distance between each socket on each axis. In end points mode, the " + "position of the final vertex")); + b.add_output(N_("Mesh")); } static void geo_node_mesh_primitive_line_layout(uiLayout *layout, @@ -154,7 +169,7 @@ static void geo_node_mesh_primitive_line_exec(GeoNodeExecParams params) mesh = create_line_mesh(start, delta, count); } - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index 6fd6cdf5747..3197a94c27b 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -29,10 +29,22 @@ namespace blender::nodes { static void geo_node_mesh_primitive_uv_shpere_declare(NodeDeclarationBuilder &b) { - b.add_input("Segments").default_value(32).min(3).max(1024); - b.add_input("Rings").default_value(16).min(2).max(1024); - b.add_input("Radius").default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); - b.add_output("Geometry"); + b.add_input(N_("Segments")) + .default_value(32) + .min(3) + .max(1024) + .description(N_("Horizontal resolution of the sphere")); + b.add_input(N_("Rings")) + .default_value(16) + .min(2) + .max(1024) + .description(N_("The number of horizontal rings")); + b.add_input(N_("Radius")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .description(N_("Distance from the generated points to the origin")); + b.add_output(N_("Mesh")); } static int sphere_vert_total(const int segments, const int rings) @@ -291,14 +303,14 @@ static void geo_node_mesh_primitive_uv_sphere_exec(GeoNodeExecParams params) if (rings_num < 3) { params.error_message_add(NodeWarningType::Info, TIP_("Rings must be at least 3")); } - params.set_output("Geometry", GeometrySet()); + params.set_output("Mesh", GeometrySet()); return; } const float radius = params.extract_input("Radius"); Mesh *mesh = create_uv_sphere_mesh(radius, segments_num, rings_num); - params.set_output("Geometry", GeometrySet::create_with_mesh(mesh)); + params.set_output("Mesh", GeometrySet::create_with_mesh(mesh)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index 9ca74fed9a7..d99c0c851a8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -27,33 +27,14 @@ namespace blender::nodes { static void geo_node_mesh_subdivide_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Level").default_value(1).min(0).max(6); - b.add_output("Geometry"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Level")).default_value(1).min(0).max(6); + b.add_output(N_("Mesh")); } -static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) +static void geometry_set_mesh_subdivide(GeometrySet &geometry_set, const int level) { - GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); - if (!geometry_set.has_mesh()) { - params.set_output("Geometry", geometry_set); - return; - } - -#ifndef WITH_OPENSUBDIV - params.error_message_add(NodeWarningType::Error, - TIP_("Disabled, Blender was compiled without OpenSubdiv")); - params.set_output("Geometry", std::move(geometry_set)); - return; -#endif - - /* See CCGSUBSURF_LEVEL_MAX for max limit. */ - const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 11); - - if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); return; } @@ -61,7 +42,7 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) /* Initialize mesh settings. */ SubdivToMeshSettings mesh_settings; - mesh_settings.resolution = (1 << subdiv_level) + 1; + mesh_settings.resolution = (1 << level) + 1; mesh_settings.use_optimal_display = false; /* Initialize subdivision settings. */ @@ -79,7 +60,6 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) /* In case of bad topology, skip to input mesh. */ if (subdiv == nullptr) { - params.set_output("Geometry", std::move(geometry_set)); return; } @@ -90,8 +70,31 @@ static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) mesh_component.replace(mesh_out); BKE_subdiv_free(subdiv); +} - params.set_output("Geometry", std::move(geometry_set)); +static void geo_node_mesh_subdivide_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + +#ifndef WITH_OPENSUBDIV + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenSubdiv")); + params.set_output("Mesh", std::move(geometry_set)); + return; +#endif + + /* See CCGSUBSURF_LEVEL_MAX for max limit. */ + const int subdiv_level = clamp_i(params.extract_input("Level"), 0, 11); + + if (subdiv_level == 0) { + params.set_output("Mesh", std::move(geometry_set)); + return; + } + + geometry_set.modify_geometry_sets( + [&](GeometrySet &geometry_set) { geometry_set_mesh_subdivide(geometry_set, subdiv_level); }); + + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes @@ -100,7 +103,7 @@ void register_node_type_geo_mesh_subdivide() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MESH_SUBDIVIDE, "Mesh Subdivide", NODE_CLASS_GEOMETRY, 0); + geo_node_type_base(&ntype, GEO_NODE_SUBDIVIDE_MESH, "Subdivide Mesh", NODE_CLASS_GEOMETRY, 0); ntype.declare = blender::nodes::geo_node_mesh_subdivide_declare; ntype.geometry_node_execute = blender::nodes::geo_node_mesh_subdivide_exec; nodeRegisterType(&ntype); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc index 11349dc7d42..11865c635b8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_curve.cc @@ -14,290 +14,46 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -#include "BLI_array.hh" -#include "BLI_task.hh" - -#include "DNA_mesh_types.h" -#include "DNA_meshdata_types.h" - -#include "BKE_attribute_math.hh" -#include "BKE_spline.hh" +#include "GEO_mesh_to_curve.hh" #include "node_geometry_util.hh" -using blender::Array; - namespace blender::nodes { -static void geo_node_mesh_to_curve_declare(NodeDeclarationBuilder &b) +static void geo_node_legacy_mesh_to_curve_declare(NodeDeclarationBuilder &b) { - b.add_input("Mesh"); - b.add_input("Selection"); - b.add_output("Curve"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_output(N_("Curve")); } -template -static void copy_attribute_to_points(const VArray &source_data, - Span map, - MutableSpan dest_data) -{ - for (const int point_index : map.index_range()) { - const int vert_index = map[point_index]; - dest_data[point_index] = source_data[vert_index]; - } -} - -static void copy_attributes_to_points(CurveEval &curve, - const MeshComponent &mesh_component, - Span> point_to_vert_maps) -{ - MutableSpan splines = curve.splines(); - Set source_attribute_ids = mesh_component.attribute_ids(); - - /* Copy builtin control point attributes. */ - if (source_attribute_ids.contains("tilt")) { - const GVArray_Typed tilt_attribute = mesh_component.attribute_get_for_read( - "tilt", ATTR_DOMAIN_POINT, 0.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points( - *tilt_attribute, point_to_vert_maps[i], splines[i]->tilts()); - } - }); - source_attribute_ids.remove_contained("tilt"); - } - if (source_attribute_ids.contains("radius")) { - const GVArray_Typed radius_attribute = mesh_component.attribute_get_for_read( - "radius", ATTR_DOMAIN_POINT, 1.0f); - threading::parallel_for(splines.index_range(), 256, [&](IndexRange range) { - for (const int i : range) { - copy_attribute_to_points( - *radius_attribute, point_to_vert_maps[i], splines[i]->radii()); - } - }); - source_attribute_ids.remove_contained("radius"); - } - - /* Don't copy other builtin control point attributes. */ - source_attribute_ids.remove("position"); - - /* Copy dynamic control point attributes. */ - for (const AttributeIDRef &attribute_id : source_attribute_ids) { - const GVArrayPtr mesh_attribute = mesh_component.attribute_try_get_for_read(attribute_id, - ATTR_DOMAIN_POINT); - /* Some attributes might not exist if they were builtin attribute on domains that don't - * have any elements, i.e. a face attribute on the output of the line primitive node. */ - if (!mesh_attribute) { - continue; - } - - const CustomDataType data_type = bke::cpp_type_to_custom_data_type(mesh_attribute->type()); - - threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) { - for (const int i : range) { - /* Create attribute on the spline points. */ - splines[i]->attributes.create(attribute_id, data_type); - std::optional spline_attribute = splines[i]->attributes.get_for_write( - attribute_id); - BLI_assert(spline_attribute); - - /* Copy attribute based on the map for this spline. */ - attribute_math::convert_to_static_type(mesh_attribute->type(), [&](auto dummy) { - using T = decltype(dummy); - copy_attribute_to_points( - mesh_attribute->typed(), point_to_vert_maps[i], spline_attribute->typed()); - }); - } - }); - } - - curve.assert_valid_point_attributes(); -} - -struct CurveFromEdgesOutput { - std::unique_ptr curve; - Vector> point_to_vert_maps; -}; - -static CurveFromEdgesOutput mesh_to_curve(Span verts, Span> edges) -{ - std::unique_ptr curve = std::make_unique(); - Vector> point_to_vert_maps; - - /* Compute the number of edges connecting to each vertex. */ - Array neighbor_count(verts.size(), 0); - for (const std::pair &edge : edges) { - neighbor_count[edge.first]++; - neighbor_count[edge.second]++; - } - - /* Compute an offset into the array of neighbor edges based on the counts. */ - Array neighbor_offsets(verts.size()); - int start = 0; - for (const int i : verts.index_range()) { - neighbor_offsets[i] = start; - start += neighbor_count[i]; - } - - /* Use as an index into the "neighbor group" for each vertex. */ - Array used_slots(verts.size(), 0); - /* Calculate the indices of each vertex's neighboring edges. */ - Array neighbors(edges.size() * 2); - for (const int i : edges.index_range()) { - const int v1 = edges[i].first; - const int v2 = edges[i].second; - neighbors[neighbor_offsets[v1] + used_slots[v1]] = v2; - neighbors[neighbor_offsets[v2] + used_slots[v2]] = v1; - used_slots[v1]++; - used_slots[v2]++; - } - - /* Now use the neighbor group offsets calculated above as a count used edges at each vertex. */ - Array unused_edges = std::move(used_slots); - - for (const int start_vert : verts.index_range()) { - /* The vertex will be part of a cyclic spline. */ - if (neighbor_count[start_vert] == 2) { - continue; - } - - /* The vertex has no connected edges, or they were already used. */ - if (unused_edges[start_vert] == 0) { - continue; - } - - for (const int i : IndexRange(neighbor_count[start_vert])) { - int current_vert = start_vert; - int next_vert = neighbors[neighbor_offsets[current_vert] + i]; - - if (unused_edges[next_vert] == 0) { - continue; - } - - std::unique_ptr spline = std::make_unique(); - Vector point_to_vert_map; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - - /* Follow connected edges until we read a vertex with more than two connected edges. */ - while (true) { - int last_vert = current_vert; - current_vert = next_vert; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - unused_edges[current_vert]--; - unused_edges[last_vert]--; - - if (neighbor_count[current_vert] != 2) { - break; - } - - const int offset = neighbor_offsets[current_vert]; - const int next_a = neighbors[offset]; - const int next_b = neighbors[offset + 1]; - next_vert = (last_vert == next_a) ? next_b : next_a; - } - - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - point_to_vert_maps.append(std::move(point_to_vert_map)); - } - } - - /* All remaining edges are part of cyclic splines (we skipped vertices with two edges before). */ - for (const int start_vert : verts.index_range()) { - if (unused_edges[start_vert] != 2) { - continue; - } - - int current_vert = start_vert; - int next_vert = neighbors[neighbor_offsets[current_vert]]; - - std::unique_ptr spline = std::make_unique(); - Vector point_to_vert_map; - spline->set_cyclic(true); - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - - /* Follow connected edges until we loop back to the start vertex. */ - while (next_vert != start_vert) { - const int last_vert = current_vert; - current_vert = next_vert; - - spline->add_point(verts[current_vert].co, 1.0f, 0.0f); - point_to_vert_map.append(current_vert); - unused_edges[current_vert]--; - unused_edges[last_vert]--; - - const int offset = neighbor_offsets[current_vert]; - const int next_a = neighbors[offset]; - const int next_b = neighbors[offset + 1]; - next_vert = (last_vert == next_a) ? next_b : next_a; - } - - spline->attributes.reallocate(spline->size()); - curve->add_spline(std::move(spline)); - point_to_vert_maps.append(std::move(point_to_vert_map)); - } - - curve->attributes.reallocate(curve->splines().size()); - return {std::move(curve), std::move(point_to_vert_maps)}; -} - -/** - * Get a separate array of the indices for edges in a selection (a boolean attribute). - * This helps to make the above algorithm simpler by removing the need to check for selection - * in many places. - */ -static Vector> get_selected_edges(GeoNodeExecParams params, - const MeshComponent &component) -{ - const Mesh &mesh = *component.get_for_read(); - const std::string selection_name = params.extract_input("Selection"); - if (!selection_name.empty() && !component.attribute_exists(selection_name)) { - params.error_message_add(NodeWarningType::Error, - TIP_("No attribute with name \"") + selection_name + "\""); - } - GVArray_Typed selection = component.attribute_get_for_read( - selection_name, ATTR_DOMAIN_EDGE, true); - - Vector> selected_edges; - for (const int i : IndexRange(mesh.totedge)) { - if (selection[i]) { - selected_edges.append({mesh.medge[i].v1, mesh.medge[i].v2}); - } - } - - return selected_edges; -} - -static void geo_node_mesh_to_curve_exec(GeoNodeExecParams params) +static void geo_node_legacy_mesh_to_curve_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Mesh"); - geometry_set = bke::geometry_set_realize_instances(geometry_set); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } - if (!geometry_set.has_mesh()) { - params.set_output("Curve", GeometrySet()); - return; - } + const MeshComponent &component = *geometry_set.get_component_for_read(); + GeometryComponentFieldContext context{component, ATTR_DOMAIN_EDGE}; + fn::FieldEvaluator evaluator{context, component.attribute_domain_size(ATTR_DOMAIN_EDGE)}; + evaluator.add(params.get_input>("Selection")); + evaluator.evaluate(); + const IndexMask selection = evaluator.get_evaluated_as_mask(0); + if (selection.size() == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } - const MeshComponent &component = *geometry_set.get_component_for_read(); - const Mesh &mesh = *component.get_for_read(); - Span verts = Span{mesh.mvert, mesh.totvert}; - Vector> selected_edges = get_selected_edges(params, component); - if (selected_edges.size() == 0) { - params.set_output("Curve", GeometrySet()); - return; - } + std::unique_ptr curve = geometry::mesh_to_curve_convert(component, selection); + geometry_set.replace_curve(curve.release()); + geometry_set.keep_only({GEO_COMPONENT_TYPE_CURVE, GEO_COMPONENT_TYPE_INSTANCES}); + }); - CurveFromEdgesOutput output = mesh_to_curve(verts, selected_edges); - copy_attributes_to_points(*output.curve, component, output.point_to_vert_maps); - - params.set_output("Curve", GeometrySet::create_with_curve(output.curve.release())); + params.set_output("Curve", std::move(geometry_set)); } } // namespace blender::nodes @@ -306,9 +62,8 @@ void register_node_type_geo_mesh_to_curve() { static bNodeType ntype; - geo_node_type_base( - &ntype, GEO_NODE_LEGACY_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_mesh_to_curve_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_curve_exec; + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_CURVE, "Mesh to Curve", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_legacy_mesh_to_curve_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_legacy_mesh_to_curve_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc new file mode 100644 index 00000000000..92911e89f59 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_to_points.cc @@ -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. + */ + +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_pointcloud.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using blender::Array; + +namespace blender::nodes { + +static void geo_node_mesh_to_points_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_input(N_("Position")).implicit_field(); + b.add_input(N_("Radius")) + .default_value(0.05f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output(N_("Points")); +} + +static void geo_node_mesh_to_points_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", 0, "", ICON_NONE); +} + +static void geo_node_mesh_to_points_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryMeshToPoints *data = (NodeGeometryMeshToPoints *)MEM_callocN( + sizeof(NodeGeometryMeshToPoints), __func__); + data->mode = GEO_NODE_MESH_TO_POINTS_VERTICES; + node->storage = data; +} + +template +static void copy_attribute_to_points(const VArray &src, + const IndexMask mask, + MutableSpan dst) +{ + for (const int i : mask.index_range()) { + dst[i] = src[mask[i]]; + } +} + +static void geometry_set_mesh_to_points(GeometrySet &geometry_set, + Field &position_field, + Field &radius_field, + Field &selection_field, + const AttributeDomain domain) +{ + const MeshComponent *mesh_component = geometry_set.get_component_for_read(); + if (mesh_component == nullptr) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + GeometryComponentFieldContext field_context{*mesh_component, domain}; + const int domain_size = mesh_component->attribute_domain_size(domain); + if (domain_size == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + PointCloud *pointcloud = BKE_pointcloud_new_nomain(selection.size()); + uninitialized_fill_n(pointcloud->radius, pointcloud->totpoint, 0.05f); + geometry_set.replace_pointcloud(pointcloud); + PointCloudComponent &point_component = + geometry_set.get_component_for_write(); + + /* Evaluating directly into the point cloud doesn't work because we are not using the full + * "min_array_size" array but compressing the selected elements into the final array with no + * gaps. */ + fn::FieldEvaluator evaluator{field_context, &selection}; + evaluator.add(position_field); + evaluator.add(radius_field); + evaluator.evaluate(); + copy_attribute_to_points(evaluator.get_evaluated(0), + selection, + {(float3 *)pointcloud->co, pointcloud->totpoint}); + copy_attribute_to_points( + evaluator.get_evaluated(1), selection, {pointcloud->radius, pointcloud->totpoint}); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_MESH}, GEO_COMPONENT_TYPE_POINT_CLOUD, false, attributes); + attributes.remove("position"); + + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + const CustomDataType data_type = entry.value.data_type; + GVArrayPtr src = mesh_component->attribute_get_for_read(attribute_id, domain, data_type); + OutputAttribute dst = point_component.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, data_type); + if (dst && src) { + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed src_typed{*src}; + copy_attribute_to_points(*src_typed, selection, dst.as_span().typed()); + }); + dst.save(); + } + } + + geometry_set.keep_only({GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_INSTANCES}); +} + +static void geo_node_mesh_to_points_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Mesh"); + Field position = params.extract_input>("Position"); + Field radius = params.extract_input>("Radius"); + Field selection = params.extract_input>("Selection"); + + /* Use another multi-function operation to make sure the input radius is greater than zero. + * TODO: Use mutable multi-function once that is supported. */ + static fn::CustomMF_SI_SO max_zero_fn( + __func__, [](float value) { return std::max(0.0f, value); }); + auto max_zero_op = std::make_shared( + FieldOperation(max_zero_fn, {std::move(radius)})); + Field positive_radius(std::move(max_zero_op), 0); + + const NodeGeometryMeshToPoints &storage = + *(const NodeGeometryMeshToPoints *)params.node().storage; + const GeometryNodeMeshToPointsMode mode = (GeometryNodeMeshToPointsMode)storage.mode; + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + switch (mode) { + case GEO_NODE_MESH_TO_POINTS_VERTICES: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_POINT); + break; + case GEO_NODE_MESH_TO_POINTS_EDGES: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_EDGE); + break; + case GEO_NODE_MESH_TO_POINTS_FACES: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_FACE); + break; + case GEO_NODE_MESH_TO_POINTS_CORNERS: + geometry_set_mesh_to_points( + geometry_set, position, positive_radius, selection, ATTR_DOMAIN_CORNER); + break; + } + }); + + params.set_output("Points", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_mesh_to_points() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_MESH_TO_POINTS, "Mesh to Points", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_mesh_to_points_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_mesh_to_points_exec; + node_type_init(&ntype, blender::nodes::geo_node_mesh_to_points_init); + ntype.draw_buttons = blender::nodes::geo_node_mesh_to_points_layout; + node_type_storage( + &ntype, "NodeGeometryMeshToPoints", node_free_standard_storage, node_copy_standard_storage); + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc index 389acc40f0f..3ba32c4b674 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_object_info.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_object_info.cc @@ -25,11 +25,15 @@ namespace blender::nodes { static void geo_node_object_info_declare(NodeDeclarationBuilder &b) { - b.add_input("Object").hide_label(); - b.add_output("Location"); - b.add_output("Rotation"); - b.add_output("Scale"); - b.add_output("Geometry"); + b.add_input(N_("Object")).hide_label(); + b.add_input(N_("As Instance")) + .description( + N_("Output the entire object as single instance. " + "This allows instancing non-geometry object types")); + b.add_output(N_("Location")); + b.add_output(N_("Rotation")); + b.add_output(N_("Scale")); + b.add_output(N_("Geometry")); } static void geo_node_object_info_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -54,12 +58,11 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) const Object *self_object = params.self_object(); if (object != nullptr) { - float transform[4][4]; - mul_m4_m4m4(transform, self_object->imat, object->obmat); + const float4x4 transform = float4x4(self_object->imat) * float4x4(object->obmat); float quaternion[4]; if (transform_space_relative) { - mat4_decompose(location, quaternion, scale, transform); + mat4_decompose(location, quaternion, scale, transform.values); } else { mat4_decompose(location, quaternion, scale, object->obmat); @@ -67,16 +70,23 @@ static void geo_node_object_info_exec(GeoNodeExecParams params) quat_to_eul(rotation, quaternion); if (object != self_object) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - const int handle = instances.add_reference(*object); - - if (transform_space_relative) { - instances.add_instance(handle, transform); + if (params.get_input("As Instance")) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + const int handle = instances.add_reference(*object); + if (transform_space_relative) { + instances.add_instance(handle, transform); + } + else { + float unit_transform[4][4]; + unit_m4(unit_transform); + instances.add_instance(handle, unit_transform); + } } else { - float unit_transform[4][4]; - unit_m4(unit_transform); - instances.add_instance(handle, unit_transform); + geometry_set = bke::object_get_evaluated_geometry_set(*object); + if (transform_space_relative) { + transform_geometry_set(geometry_set, transform, *params.depsgraph()); + } } } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc new file mode 100644 index 00000000000..3e0096824d3 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "BKE_attribute_math.hh" +#include "BKE_mesh.h" + +#include "node_geometry_util.hh" + +using blender::Array; + +namespace blender::nodes { + +static void geo_node_points_to_vertices_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input(N_("Selection")).default_value(true).supports_field().hide_value(); + b.add_output(N_("Mesh")); +} + +template +static void copy_attribute_to_vertices(const Span src, const IndexMask mask, MutableSpan dst) +{ + for (const int i : mask.index_range()) { + dst[i] = src[mask[i]]; + } +} + +/* One improvement would be to move the attribute arrays directly to the mesh when possible. */ +static void geometry_set_points_to_vertices(GeometrySet &geometry_set, + Field &selection_field) +{ + const PointCloudComponent *point_component = + geometry_set.get_component_for_read(); + if (point_component == nullptr) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + GeometryComponentFieldContext field_context{*point_component, ATTR_DOMAIN_POINT}; + const int domain_size = point_component->attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + geometry_set.keep_only({GEO_COMPONENT_TYPE_INSTANCES}); + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + Map attributes; + geometry_set.gather_attributes_for_propagation( + {GEO_COMPONENT_TYPE_POINT_CLOUD}, GEO_COMPONENT_TYPE_MESH, false, attributes); + + Mesh *mesh = BKE_mesh_new_nomain(selection.size(), 0, 0, 0, 0); + geometry_set.replace_mesh(mesh); + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + + for (Map::Item entry : attributes.items()) { + const AttributeIDRef attribute_id = entry.key; + const CustomDataType data_type = entry.value.data_type; + GVArrayPtr src = point_component->attribute_get_for_read( + attribute_id, ATTR_DOMAIN_POINT, data_type); + OutputAttribute dst = mesh_component.attribute_try_get_for_output_only( + attribute_id, ATTR_DOMAIN_POINT, data_type); + if (dst && src) { + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed src_typed{*src}; + VArray_Span src_typed_span{*src_typed}; + copy_attribute_to_vertices(src_typed_span, selection, dst.as_span().typed()); + }); + dst.save(); + } + } + + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); +} + +static void geo_node_points_to_vertices_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Points"); + Field selection_field = params.extract_input>("Selection"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + geometry_set_points_to_vertices(geometry_set, selection_field); + }); + + params.set_output("Mesh", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_points_to_vertices() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_POINTS_TO_VERTICES, "Points to Vertices", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_points_to_vertices_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_points_to_vertices_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc index d920c8de9f0..18d674a38a4 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_volume.cc @@ -32,13 +32,16 @@ namespace blender::nodes { static void geo_node_points_to_volume_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Density").default_value(1.0f).min(0.0f); - b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input("Radius"); - b.add_input("Radius", "Radius_001").default_value(0.5f).min(0.0f); - b.add_output("Geometry"); + b.add_input(N_("Points")); + b.add_input(N_("Density")).default_value(1.0f).min(0.0f); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Radius")) + .default_value(0.5f) + .min(0.0f) + .subtype(PROP_DISTANCE) + .supports_field(); + b.add_output(N_("Volume")); } static void geo_node_points_to_volume_layout(uiLayout *layout, @@ -48,7 +51,6 @@ static void geo_node_points_to_volume_layout(uiLayout *layout, uiLayoutSetPropSep(layout, true); uiLayoutSetPropDecorate(layout, false); uiItemR(layout, ptr, "resolution_mode", 0, IFACE_("Resolution"), ICON_NONE); - uiItemR(layout, ptr, "input_type_radius", 0, IFACE_("Radius"), ICON_NONE); } static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -56,13 +58,7 @@ static void geo_node_points_to_volume_init(bNodeTree *UNUSED(ntree), bNode *node NodeGeometryPointsToVolume *data = (NodeGeometryPointsToVolume *)MEM_callocN( sizeof(NodeGeometryPointsToVolume), __func__); data->resolution_mode = GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT; - data->input_type_radius = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; node->storage = data; - - bNodeSocket *radius_attribute_socket = nodeFindSocket(node, SOCK_IN, "Radius"); - bNodeSocketValueString *radius_attribute_socket_value = - (bNodeSocketValueString *)radius_attribute_socket->default_value; - STRNCPY(radius_attribute_socket_value->value, "radius"); } static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *node) @@ -75,9 +71,6 @@ static void geo_node_points_to_volume_update(bNodeTree *UNUSED(ntree), bNode *no GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_AMOUNT); nodeSetSocketAvailability( voxel_size_socket, data->resolution_mode == GEO_NODE_POINTS_TO_VOLUME_RESOLUTION_MODE_SIZE); - - update_attribute_input_socket_availabilities( - *node, "Radius", (GeometryNodeAttributeInputMode)data->input_type_radius); } #ifdef WITH_OPENVDB @@ -167,20 +160,25 @@ static float compute_voxel_size(const GeoNodeExecParams ¶ms, return voxel_size; } -static void gather_point_data_from_component(const GeoNodeExecParams ¶ms, +static void gather_point_data_from_component(GeoNodeExecParams ¶ms, const GeometryComponent &component, Vector &r_positions, Vector &r_radii) { GVArray_Typed positions = component.attribute_get_for_read( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - GVArray_Typed radii = params.get_input_attribute( - "Radius", component, ATTR_DOMAIN_POINT, 0.0f); - for (const int i : IndexRange(positions.size())) { - r_positions.append(positions[i]); - r_radii.append(radii[i]); - } + Field radius_field = params.get_input>("Radius"); + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + + r_positions.resize(r_positions.size() + domain_size); + positions->materialize(r_positions.as_mutable_span().take_back(domain_size)); + + r_radii.resize(r_radii.size() + domain_size); + fn::FieldEvaluator evaluator{field_context, domain_size}; + evaluator.add_with_destination(radius_field, r_radii.as_mutable_span().take_back(domain_size)); + evaluator.evaluate(); } static void convert_to_grid_index_space(const float voxel_size, @@ -196,24 +194,23 @@ static void convert_to_grid_index_space(const float voxel_size, } } -static void initialize_volume_component_from_points(const GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - const GeoNodeExecParams ¶ms) +static void initialize_volume_component_from_points(GeoNodeExecParams ¶ms, + GeometrySet &r_geometry_set) { Vector positions; Vector radii; - if (geometry_set_in.has()) { + if (r_geometry_set.has()) { gather_point_data_from_component( - params, *geometry_set_in.get_component_for_read(), positions, radii); + params, *r_geometry_set.get_component_for_read(), positions, radii); } - if (geometry_set_in.has()) { + if (r_geometry_set.has()) { gather_point_data_from_component( - params, *geometry_set_in.get_component_for_read(), positions, radii); + params, *r_geometry_set.get_component_for_read(), positions, radii); } - if (geometry_set_in.has()) { + if (r_geometry_set.has()) { gather_point_data_from_component( - params, *geometry_set_in.get_component_for_read(), positions, radii); + params, *r_geometry_set.get_component_for_read(), positions, radii); } const float max_radius = *std::max_element(radii.begin(), radii.end()); @@ -225,35 +222,31 @@ static void initialize_volume_component_from_points(const GeometrySet &geometry_ Volume *volume = (Volume *)BKE_id_new_nomain(ID_VO, nullptr); BKE_volume_init_grids(volume); - VolumeGrid *c_density_grid = BKE_volume_grid_add(volume, "density", VOLUME_GRID_FLOAT); - openvdb::FloatGrid::Ptr density_grid = openvdb::gridPtrCast( - BKE_volume_grid_openvdb_for_write(volume, c_density_grid, false)); - const float density = params.get_input("Density"); convert_to_grid_index_space(voxel_size, positions, radii); openvdb::FloatGrid::Ptr new_grid = generate_volume_from_points(positions, radii, density); - /* This merge is cheap, because the #density_grid is empty. */ - density_grid->merge(*new_grid); - density_grid->transform().postScale(voxel_size); + new_grid->transform().postScale(voxel_size); + BKE_volume_grid_add_vdb(*volume, "density", std::move(new_grid)); - VolumeComponent &volume_component = geometry_set_out.get_component_for_write(); - volume_component.replace(volume); + r_geometry_set.keep_only({GEO_COMPONENT_TYPE_VOLUME, GEO_COMPONENT_TYPE_INSTANCES}); + r_geometry_set.replace_volume(volume); } #endif static void geo_node_points_to_volume_exec(GeoNodeExecParams params) { - GeometrySet geometry_set_in = params.extract_input("Geometry"); - GeometrySet geometry_set_out; - - /* TODO: Read-only access to instances should be supported here, for now they are made real. */ - geometry_set_in = geometry_set_realize_instances(geometry_set_in); + GeometrySet geometry_set = params.extract_input("Points"); #ifdef WITH_OPENVDB - initialize_volume_component_from_points(geometry_set_in, geometry_set_out, params); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + initialize_volume_component_from_points(params, geometry_set); + }); + params.set_output("Volume", std::move(geometry_set)); +#else + params.error_message_add(NodeWarningType::Error, + TIP_("Disabled, Blender was compiled without OpenVDB")); + params.set_output("Volume", GeometrySet()); #endif - - params.set_output("Geometry", std::move(geometry_set_out)); } } // namespace blender::nodes @@ -263,7 +256,7 @@ void register_node_type_geo_points_to_volume() static bNodeType ntype; geo_node_type_base( - &ntype, GEO_NODE_LEGACY_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); + &ntype, GEO_NODE_POINTS_TO_VOLUME, "Points to Volume", NODE_CLASS_GEOMETRY, 0); node_type_storage(&ntype, "NodeGeometryPointsToVolume", node_free_standard_storage, diff --git a/source/blender/nodes/geometry/nodes/node_geo_proximity.cc b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc new file mode 100644 index 00000000000..c05476b982b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_proximity.cc @@ -0,0 +1,250 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" +#include "BLI_timeit.hh" + +#include "DNA_mesh_types.h" + +#include "BKE_bvhutils.h" +#include "BKE_geometry_set.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_proximity_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Target")) + .only_realized_data() + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + b.add_input(N_("Source Position")).implicit_field(); + b.add_output(N_("Position")).dependent_field(); + b.add_output(N_("Distance")).dependent_field(); +} + +static void geo_node_proximity_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "target_element", 0, "", ICON_NONE); +} + +static void geo_proximity_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryProximity *node_storage = (NodeGeometryProximity *)MEM_callocN( + sizeof(NodeGeometryProximity), __func__); + node_storage->target_element = GEO_NODE_PROX_TARGET_FACES; + node->storage = node_storage; +} + +static bool calculate_mesh_proximity(const VArray &positions, + const IndexMask mask, + const Mesh &mesh, + const GeometryNodeProximityTargetType type, + const MutableSpan r_distances, + const MutableSpan r_locations) +{ + BVHTreeFromMesh bvh_data; + switch (type) { + case GEO_NODE_PROX_TARGET_POINTS: + BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_VERTS, 2); + break; + case GEO_NODE_PROX_TARGET_EDGES: + BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_EDGES, 2); + break; + case GEO_NODE_PROX_TARGET_FACES: + BKE_bvhtree_from_mesh_get(&bvh_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + break; + } + + if (bvh_data.tree == nullptr) { + return false; + } + + threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { + BVHTreeNearest nearest; + copy_v3_fl(nearest.co, FLT_MAX); + nearest.index = -1; + + for (int i : range) { + const int index = mask[i]; + /* Use the distance to the last found point as upper bound to speedup the bvh lookup. */ + nearest.dist_sq = float3::distance_squared(nearest.co, positions[index]); + + BLI_bvhtree_find_nearest( + bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data); + + if (nearest.dist_sq < r_distances[index]) { + r_distances[index] = nearest.dist_sq; + if (!r_locations.is_empty()) { + r_locations[index] = nearest.co; + } + } + } + }); + + free_bvhtree_from_mesh(&bvh_data); + return true; +} + +static bool calculate_pointcloud_proximity(const VArray &positions, + const IndexMask mask, + const PointCloud &pointcloud, + MutableSpan r_distances, + MutableSpan r_locations) +{ + BVHTreeFromPointCloud bvh_data; + BKE_bvhtree_from_pointcloud_get(&bvh_data, &pointcloud, 2); + if (bvh_data.tree == nullptr) { + return false; + } + + threading::parallel_for(mask.index_range(), 512, [&](IndexRange range) { + BVHTreeNearest nearest; + copy_v3_fl(nearest.co, FLT_MAX); + nearest.index = -1; + + for (int i : range) { + const int index = mask[i]; + /* Use the distance to the closest point in the mesh to speedup the pointcloud bvh lookup. + * This is ok because we only need to find the closest point in the pointcloud if it's + * closer than the mesh. */ + nearest.dist_sq = r_distances[index]; + + BLI_bvhtree_find_nearest( + bvh_data.tree, positions[index], &nearest, bvh_data.nearest_callback, &bvh_data); + + if (nearest.dist_sq < r_distances[index]) { + r_distances[index] = nearest.dist_sq; + if (!r_locations.is_empty()) { + r_locations[index] = nearest.co; + } + } + } + }); + + free_bvhtree_from_pointcloud(&bvh_data); + return true; +} + +class ProximityFunction : public fn::MultiFunction { + private: + GeometrySet target_; + GeometryNodeProximityTargetType type_; + + public: + ProximityFunction(GeometrySet target, GeometryNodeProximityTargetType type) + : target_(std::move(target)), type_(type) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Geometry Proximity"}; + signature.single_input("Source Position"); + signature.single_output("Position"); + signature.single_output("Distance"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &src_positions = params.readonly_single_input(0, + "Source Position"); + MutableSpan positions = params.uninitialized_single_output_if_required( + 1, "Position"); + /* Make sure there is a distance array, used for finding the smaller distance when there are + * multiple components. Theoretically it would be possible to avoid using the distance array + * when there is only one component. However, this only adds an allocation and a single float + * comparison per vertex, so it's likely not worth it. */ + MutableSpan distances = params.uninitialized_single_output(2, "Distance"); + + distances.fill(FLT_MAX); + + bool success = false; + if (target_.has_mesh()) { + success |= calculate_mesh_proximity( + src_positions, mask, *target_.get_mesh_for_read(), type_, distances, positions); + } + + if (target_.has_pointcloud() && type_ == GEO_NODE_PROX_TARGET_POINTS) { + success |= calculate_pointcloud_proximity( + src_positions, mask, *target_.get_pointcloud_for_read(), distances, positions); + } + + if (!success) { + positions.fill(float3(0)); + distances.fill(0.0f); + return; + } + + if (params.single_output_is_required(2, "Distance")) { + threading::parallel_for(mask.index_range(), 2048, [&](IndexRange range) { + for (const int i : range) { + const int j = mask[i]; + distances[j] = std::sqrt(distances[j]); + } + }); + } + } +}; + +static void geo_node_proximity_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set_target = params.extract_input("Target"); + geometry_set_target.ensure_owns_direct_data(); + + auto return_default = [&]() { + params.set_output("Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Distance", fn::make_constant_field(0.0f)); + }; + + if (!geometry_set_target.has_mesh() && !geometry_set_target.has_pointcloud()) { + return return_default(); + } + + const NodeGeometryProximity &storage = *(const NodeGeometryProximity *)params.node().storage; + Field position_field = params.extract_input>("Source Position"); + + auto proximity_fn = std::make_unique( + std::move(geometry_set_target), + static_cast(storage.target_element)); + auto proximity_op = std::make_shared( + FieldOperation(std::move(proximity_fn), {std::move(position_field)})); + + params.set_output("Position", Field(proximity_op, 0)); + params.set_output("Distance", Field(proximity_op, 1)); +} + +} // namespace blender::nodes + +void register_node_type_geo_proximity() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_PROXIMITY, "Geometry Proximity", NODE_CLASS_GEOMETRY, 0); + node_type_init(&ntype, blender::nodes::geo_proximity_init); + node_type_storage( + &ntype, "NodeGeometryProximity", node_free_standard_storage, node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_proximity_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_proximity_exec; + ntype.draw_buttons = blender::nodes::geo_node_proximity_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc index 401a478f04c..34946b1115c 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_raycast.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_raycast.cc @@ -16,6 +16,7 @@ #include "DNA_mesh_types.h" +#include "BKE_attribute_math.hh" #include "BKE_bvhutils.h" #include "BKE_mesh_sample.hh" @@ -24,59 +25,102 @@ #include "node_geometry_util.hh" +using namespace blender::bke::mesh_surface_sample; + namespace blender::nodes { static void geo_node_raycast_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Target Geometry"); - b.add_input("Ray Direction"); - b.add_input("Ray Direction", "Ray Direction_001") - .default_value({0.0f, 0.0f, 1.0f}); - b.add_input("Ray Length"); - b.add_input("Ray Length", "Ray Length_001") + b.add_input(N_("Target Geometry")) + .only_realized_data() + .supported_type(GEO_COMPONENT_TYPE_MESH); + + b.add_input(N_("Attribute")).hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_004").hide_value().supports_field(); + + b.add_input(N_("Source Position")).implicit_field(); + b.add_input(N_("Ray Direction")) + .default_value({0.0f, 0.0f, -1.0f}) + .supports_field(); + b.add_input(N_("Ray Length")) .default_value(100.0f) .min(0.0f) - .subtype(PROP_DISTANCE); - b.add_input("Target Attribute"); - b.add_input("Is Hit"); - b.add_input("Hit Position"); - b.add_input("Hit Normal"); - b.add_input("Hit Distance"); - b.add_input("Hit Attribute"); - b.add_output("Geometry"); + .subtype(PROP_DISTANCE) + .supports_field(); + + b.add_output(N_("Is Hit")).dependent_field(); + b.add_output(N_("Hit Position")).dependent_field(); + b.add_output(N_("Hit Normal")).dependent_field(); + b.add_output(N_("Hit Distance")).dependent_field(); + + b.add_output(N_("Attribute")).dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_001").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_002").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_003").dependent_field({1, 2, 3, 4, 5, 6}); + b.add_output(N_("Attribute"), "Attribute_004").dependent_field({1, 2, 3, 4, 5, 6}); } static void geo_node_raycast_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "mapping", 0, IFACE_("Mapping"), ICON_NONE); - uiItemR(layout, ptr, "input_type_ray_direction", 0, IFACE_("Ray Direction"), ICON_NONE); - uiItemR(layout, ptr, "input_type_ray_length", 0, IFACE_("Ray Length"), ICON_NONE); + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); } static void geo_node_raycast_init(bNodeTree *UNUSED(tree), bNode *node) { NodeGeometryRaycast *data = (NodeGeometryRaycast *)MEM_callocN(sizeof(NodeGeometryRaycast), __func__); - data->input_type_ray_direction = GEO_NODE_ATTRIBUTE_INPUT_VECTOR; - data->input_type_ray_length = GEO_NODE_ATTRIBUTE_INPUT_FLOAT; + data->mapping = GEO_NODE_RAYCAST_INTERPOLATED; + data->data_type = CD_PROP_FLOAT; node->storage = data; } static void geo_node_raycast_update(bNodeTree *UNUSED(ntree), bNode *node) { - NodeGeometryRaycast *node_storage = (NodeGeometryRaycast *)node->storage; - update_attribute_input_socket_availabilities( - *node, - "Ray Direction", - (GeometryNodeAttributeInputMode)node_storage->input_type_ray_direction); - update_attribute_input_socket_availabilities( - *node, "Ray Length", (GeometryNodeAttributeInputMode)node_storage->input_type_ray_length); + const NodeGeometryRaycast &data = *(const NodeGeometryRaycast *)node->storage; + const CustomDataType data_type = static_cast(data.data_type); + + bNodeSocket *socket_vector = (bNodeSocket *)BLI_findlink(&node->inputs, 1); + bNodeSocket *socket_float = socket_vector->next; + bNodeSocket *socket_color4f = socket_float->next; + bNodeSocket *socket_boolean = socket_color4f->next; + bNodeSocket *socket_int32 = socket_boolean->next; + + nodeSetSocketAvailability(socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(socket_int32, data_type == CD_PROP_INT32); + + bNodeSocket *out_socket_vector = (bNodeSocket *)BLI_findlink(&node->outputs, 4); + bNodeSocket *out_socket_float = out_socket_vector->next; + bNodeSocket *out_socket_color4f = out_socket_float->next; + bNodeSocket *out_socket_boolean = out_socket_color4f->next; + bNodeSocket *out_socket_int32 = out_socket_boolean->next; + + nodeSetSocketAvailability(out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(out_socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(out_socket_int32, data_type == CD_PROP_INT32); } -static void raycast_to_mesh(const Mesh &mesh, +static eAttributeMapMode get_map_mode(GeometryNodeRaycastMapMode map_mode) +{ + switch (map_mode) { + case GEO_NODE_RAYCAST_INTERPOLATED: + return eAttributeMapMode::INTERPOLATED; + default: + case GEO_NODE_RAYCAST_NEAREST: + return eAttributeMapMode::NEAREST; + } +} + +static void raycast_to_mesh(IndexMask mask, + const Mesh &mesh, const VArray &ray_origins, const VArray &ray_directions, const VArray &ray_lengths, @@ -84,16 +128,9 @@ static void raycast_to_mesh(const Mesh &mesh, const MutableSpan r_hit_indices, const MutableSpan r_hit_positions, const MutableSpan r_hit_normals, - const MutableSpan r_hit_distances) + const MutableSpan r_hit_distances, + int &hit_count) { - BLI_assert(ray_origins.size() == ray_directions.size()); - BLI_assert(ray_origins.size() == ray_lengths.size()); - BLI_assert(ray_origins.size() == r_hit.size() || r_hit.is_empty()); - BLI_assert(ray_origins.size() == r_hit_indices.size() || r_hit_indices.is_empty()); - BLI_assert(ray_origins.size() == r_hit_positions.size() || r_hit_positions.is_empty()); - BLI_assert(ray_origins.size() == r_hit_normals.size() || r_hit_normals.is_empty()); - BLI_assert(ray_origins.size() == r_hit_distances.size() || r_hit_distances.is_empty()); - BVHTreeFromMesh tree_data; BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 4); if (tree_data.tree == nullptr) { @@ -101,7 +138,7 @@ static void raycast_to_mesh(const Mesh &mesh, return; } - for (const int i : ray_origins.index_range()) { + for (const int i : mask) { const float ray_length = ray_lengths[i]; const float3 ray_origin = ray_origins[i]; const float3 ray_direction = ray_directions[i].normalized(); @@ -116,12 +153,13 @@ static void raycast_to_mesh(const Mesh &mesh, &hit, tree_data.raycast_callback, &tree_data) != -1) { + hit_count++; if (!r_hit.is_empty()) { r_hit[i] = hit.index >= 0; } if (!r_hit_indices.is_empty()) { - /* Index should always be a valid looptri index, use 0 when hit failed. */ - r_hit_indices[i] = max_ii(hit.index, 0); + /* The caller must be able to handle invalid indices anyway, so don't clamp this value. */ + r_hit_indices[i] = hit.index; } if (!r_hit_positions.is_empty()) { r_hit_positions[i] = hit.co; @@ -138,7 +176,7 @@ static void raycast_to_mesh(const Mesh &mesh, r_hit[i] = false; } if (!r_hit_indices.is_empty()) { - r_hit_indices[i] = 0; + r_hit_indices[i] = -1; } if (!r_hit_positions.is_empty()) { r_hit_positions[i] = float3(0.0f, 0.0f, 0.0f); @@ -152,154 +190,240 @@ static void raycast_to_mesh(const Mesh &mesh, } } + /* We shouldn't be rebuilding the BVH tree when calling this function in parallel. */ + BLI_assert(tree_data.cached); free_bvhtree_from_mesh(&tree_data); } -static bke::mesh_surface_sample::eAttributeMapMode get_map_mode( - GeometryNodeRaycastMapMode map_mode) -{ - switch (map_mode) { - case GEO_NODE_RAYCAST_INTERPOLATED: - return bke::mesh_surface_sample::eAttributeMapMode::INTERPOLATED; - default: - case GEO_NODE_RAYCAST_NEAREST: - return bke::mesh_surface_sample::eAttributeMapMode::NEAREST; +class RaycastFunction : public fn::MultiFunction { + private: + GeometrySet target_; + GeometryNodeRaycastMapMode mapping_; + + /** The field for data evaluated on the target geometry. */ + std::optional target_context_; + std::unique_ptr target_evaluator_; + const GVArray *target_data_ = nullptr; + + /* Always evaluate the target domain data on the point domain. Eventually this could be + * exposed as an option or determined automatically from the field inputs in order to avoid + * losing information if the target field is on a different domain. */ + const AttributeDomain domain_ = ATTR_DOMAIN_POINT; + + fn::MFSignature signature_; + + public: + RaycastFunction(GeometrySet target, GField src_field, GeometryNodeRaycastMapMode mapping) + : target_(std::move(target)), mapping_((GeometryNodeRaycastMapMode)mapping) + { + target_.ensure_owns_direct_data(); + this->evaluate_target_field(std::move(src_field)); + signature_ = create_signature(); + this->set_signature(&signature_); } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Geometry Proximity"}; + signature.single_input("Source Position"); + signature.single_input("Ray Direction"); + signature.single_input("Ray Length"); + signature.single_output("Is Hit"); + signature.single_output("Hit Position"); + signature.single_output("Hit Normal"); + signature.single_output("Distance"); + if (target_data_) { + signature.single_output("Attribute", target_data_->type()); + } + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + /* Hit positions are always necessary for retrieving the attribute from the target if that + * output is required, so always retrieve a span from the evaluator in that case (it's + * expected that the evaluator is more likely to have a spare buffer that could be used). */ + MutableSpan hit_positions = + (target_data_) ? params.uninitialized_single_output(4, "Hit Position") : + params.uninitialized_single_output_if_required(4, "Hit Position"); + + Array hit_indices; + if (target_data_) { + hit_indices.reinitialize(mask.min_array_size()); + } + + BLI_assert(target_.has_mesh()); + const Mesh &mesh = *target_.get_mesh_for_read(); + + int hit_count = 0; + raycast_to_mesh(mask, + mesh, + params.readonly_single_input(0, "Source Position"), + params.readonly_single_input(1, "Ray Direction"), + params.readonly_single_input(2, "Ray Length"), + params.uninitialized_single_output_if_required(3, "Is Hit"), + hit_indices, + hit_positions, + params.uninitialized_single_output_if_required(5, "Hit Normal"), + params.uninitialized_single_output_if_required(6, "Distance"), + hit_count); + + if (target_data_) { + IndexMask hit_mask; + Vector hit_mask_indices; + if (hit_count < mask.size()) { + /* Not all rays hit the target. Create a corrected mask to avoid transferring attribute + * data to invalid indices. An alternative would be handling -1 indices in a separate case + * in #MeshAttributeInterpolator, but since it already has an IndexMask in its constructor, + * it's simpler to use that. */ + hit_mask_indices.reserve(hit_count); + for (const int64_t i : mask) { + if (hit_indices[i] != -1) { + hit_mask_indices.append(i); + } + hit_mask = IndexMask(hit_mask_indices); + } + } + else { + hit_mask = mask; + } + + GMutableSpan result = params.uninitialized_single_output_if_required(7, "Attribute"); + if (!result.is_empty()) { + MeshAttributeInterpolator interp(&mesh, hit_mask, hit_positions, hit_indices); + result.type().fill_assign_indices(result.type().default_value(), result.data(), mask); + interp.sample_data(*target_data_, domain_, get_map_mode(mapping_), result); + } + } + } + + private: + void evaluate_target_field(GField src_field) + { + if (!src_field) { + return; + } + const MeshComponent &mesh_component = *target_.get_component_for_read(); + target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); + const int domain_size = mesh_component.attribute_domain_size(domain_); + target_evaluator_ = std::make_unique(*target_context_, domain_size); + target_evaluator_->add(std::move(src_field)); + target_evaluator_->evaluate(); + target_data_ = &target_evaluator_->get_evaluated(0); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + if (params.output_is_required("Attribute_001")) { + return params.extract_input>("Attribute_001"); + } + break; + case CD_PROP_FLOAT3: + if (params.output_is_required("Attribute")) { + return params.extract_input>("Attribute"); + } + break; + case CD_PROP_COLOR: + if (params.output_is_required("Attribute_002")) { + return params.extract_input>("Attribute_002"); + } + break; + case CD_PROP_BOOL: + if (params.output_is_required("Attribute_003")) { + return params.extract_input>("Attribute_003"); + } + break; + case CD_PROP_INT32: + if (params.output_is_required("Attribute_004")) { + return params.extract_input>("Attribute_004"); + } + break; + default: + BLI_assert_unreachable(); + } + return {}; } -static void raycast_from_points(const GeoNodeExecParams ¶ms, - const GeometrySet &target_geometry, - GeometryComponent &dst_component, - const StringRef hit_name, - const StringRef hit_position_name, - const StringRef hit_normal_name, - const StringRef hit_distance_name, - const Span hit_attribute_names, - const Span hit_attribute_output_names) +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) { - BLI_assert(hit_attribute_names.size() == hit_attribute_output_names.size()); - - const MeshComponent *src_mesh_component = - target_geometry.get_component_for_read(); - if (src_mesh_component == nullptr) { - return; - } - const Mesh *src_mesh = src_mesh_component->get_for_read(); - if (src_mesh == nullptr) { - return; - } - if (src_mesh->totpoly == 0) { - return; - } - - const NodeGeometryRaycast &storage = *(const NodeGeometryRaycast *)params.node().storage; - bke::mesh_surface_sample::eAttributeMapMode map_mode = get_map_mode( - (GeometryNodeRaycastMapMode)storage.mapping); - const AttributeDomain result_domain = ATTR_DOMAIN_POINT; - - GVArray_Typed ray_origins = dst_component.attribute_get_for_read( - "position", result_domain, {0, 0, 0}); - GVArray_Typed ray_directions = params.get_input_attribute( - "Ray Direction", dst_component, result_domain, {0, 0, 0}); - GVArray_Typed ray_lengths = params.get_input_attribute( - "Ray Length", dst_component, result_domain, 0); - - OutputAttribute_Typed hit_attribute = - dst_component.attribute_try_get_for_output_only(hit_name, result_domain); - OutputAttribute_Typed hit_position_attribute = - dst_component.attribute_try_get_for_output_only(hit_position_name, result_domain); - OutputAttribute_Typed hit_normal_attribute = - dst_component.attribute_try_get_for_output_only(hit_normal_name, result_domain); - OutputAttribute_Typed hit_distance_attribute = - dst_component.attribute_try_get_for_output_only(hit_distance_name, result_domain); - - /* Positions and looptri indices are always needed for interpolation, - * so create temporary arrays if no output attribute is given. */ - Array hit_indices; - Array hit_positions_internal; - if (!hit_attribute_names.is_empty()) { - hit_indices.reinitialize(ray_origins->size()); - - if (!hit_position_attribute) { - hit_positions_internal.reinitialize(ray_origins->size()); + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field(field)); + break; } - } - const MutableSpan is_hit = hit_attribute ? hit_attribute.as_span() : MutableSpan(); - const MutableSpan hit_positions = hit_position_attribute ? - hit_position_attribute.as_span() : - hit_positions_internal; - const MutableSpan hit_normals = hit_normal_attribute ? hit_normal_attribute.as_span() : - MutableSpan(); - const MutableSpan hit_distances = hit_distance_attribute ? - hit_distance_attribute.as_span() : - MutableSpan(); - - raycast_to_mesh(*src_mesh, - ray_origins, - ray_directions, - ray_lengths, - is_hit, - hit_indices, - hit_positions, - hit_normals, - hit_distances); - - hit_attribute.save(); - hit_position_attribute.save(); - hit_normal_attribute.save(); - hit_distance_attribute.save(); - - /* Custom interpolated attributes */ - bke::mesh_surface_sample::MeshAttributeInterpolator interp(src_mesh, hit_positions, hit_indices); - for (const int i : hit_attribute_names.index_range()) { - const std::optional meta_data = src_mesh_component->attribute_get_meta_data( - hit_attribute_names[i]); - if (meta_data) { - ReadAttributeLookup hit_attribute = src_mesh_component->attribute_try_get_for_read( - hit_attribute_names[i]); - OutputAttribute hit_attribute_output = dst_component.attribute_try_get_for_output_only( - hit_attribute_output_names[i], result_domain, meta_data->data_type); - - interp.sample_attribute(hit_attribute, hit_attribute_output, map_mode); - - hit_attribute_output.save(); + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field(field)); + break; } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field(field)); + break; + } + default: + break; } } static void geo_node_raycast_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); - GeometrySet target_geometry_set = params.extract_input("Target Geometry"); + GeometrySet target = params.extract_input("Target Geometry"); + const NodeGeometryRaycast &data = *(const NodeGeometryRaycast *)params.node().storage; + const GeometryNodeRaycastMapMode mapping = static_cast(data.mapping); + const CustomDataType data_type = static_cast(data.data_type); - const std::string hit_name = params.extract_input("Is Hit"); - const std::string hit_position_name = params.extract_input("Hit Position"); - const std::string hit_normal_name = params.extract_input("Hit Normal"); - const std::string hit_distance_name = params.extract_input("Hit Distance"); + auto return_default = [&]() { + params.set_output("Is Hit", fn::make_constant_field(false)); + params.set_output("Hit Position", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Hit Normal", fn::make_constant_field({0.0f, 0.0f, 0.0f})); + params.set_output("Hit Distance", fn::make_constant_field(0.0f)); + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + output_attribute_field(params, fn::make_constant_field(T())); + }); + }; - const Array hit_names = {params.extract_input("Target Attribute")}; - const Array hit_output_names = {params.extract_input("Hit Attribute")}; - - geometry_set = bke::geometry_set_realize_instances(geometry_set); - target_geometry_set = bke::geometry_set_realize_instances(target_geometry_set); - - static const Array types = { - GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; - for (const GeometryComponentType type : types) { - if (geometry_set.has(type)) { - raycast_from_points(params, - target_geometry_set, - geometry_set.get_component_for_write(type), - hit_name, - hit_position_name, - hit_normal_name, - hit_distance_name, - hit_names, - hit_output_names); - } + if (target.is_empty()) { + return return_default(); } - params.set_output("Geometry", geometry_set); + if (!target.has_mesh()) { + return return_default(); + } + + if (target.get_mesh_for_read()->totpoly == 0) { + params.error_message_add(NodeWarningType::Error, TIP_("The target mesh must have faces")); + return return_default(); + } + + GField field = get_input_attribute_field(params, data_type); + const bool do_attribute_transfer = bool(field); + Field position_field = params.extract_input>("Source Position"); + Field direction_field = params.extract_input>("Ray Direction"); + Field length_field = params.extract_input>("Ray Length"); + + auto fn = std::make_unique(std::move(target), std::move(field), mapping); + auto op = std::make_shared(FieldOperation( + std::move(fn), + {std::move(position_field), std::move(direction_field), std::move(length_field)})); + + params.set_output("Is Hit", Field(op, 0)); + params.set_output("Hit Position", Field(op, 1)); + params.set_output("Hit Normal", Field(op, 2)); + params.set_output("Hit Distance", Field(op, 3)); + if (do_attribute_transfer) { + output_attribute_field(params, GField(op, 4)); + } } } // namespace blender::nodes @@ -308,8 +432,8 @@ void register_node_type_geo_raycast() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_LEGACY_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); - node_type_size_preset(&ntype, NODE_SIZE_LARGE); + geo_node_type_base(&ntype, GEO_NODE_RAYCAST, "Raycast", NODE_CLASS_GEOMETRY, 0); + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, blender::nodes::geo_node_raycast_init); node_type_update(&ntype, blender::nodes::geo_node_raycast_update); node_type_storage( diff --git a/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc new file mode 100644 index 00000000000..6c51c1f738f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_realize_instances.cc @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +namespace blender::nodes { + +static void geo_node_realize_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_output(N_("Geometry")); +} + +static void geo_node_realize_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + geometry_set = bke::geometry_set_realize_instances(geometry_set); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_realize_instances() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_REALIZE_INSTANCES, "Realize Instances", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_realize_instances_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_realize_instances_exec; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc new file mode 100644 index 00000000000..abf44b1aaf8 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_rotate_instances.cc @@ -0,0 +1,120 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_rotate_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Rotation")).subtype(PROP_EULER).supports_field(); + b.add_input(N_("Pivot Point")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input(N_("Local Space")).default_value(true).supports_field(); + b.add_output(N_("Instances")); +}; + +static void rotate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_POINT}; + const int domain_size = instances_component.instances_amount(); + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(params.extract_input>("Selection")); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + fn::FieldEvaluator transforms_evaluator{field_context, &selection}; + transforms_evaluator.add(params.extract_input>("Rotation")); + transforms_evaluator.add(params.extract_input>("Pivot Point")); + transforms_evaluator.add(params.extract_input>("Local Space")); + transforms_evaluator.evaluate(); + const VArray &rotations = transforms_evaluator.get_evaluated(0); + const VArray &pivots = transforms_evaluator.get_evaluated(1); + const VArray &local_spaces = transforms_evaluator.get_evaluated(2); + + MutableSpan instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + const float3 pivot = pivots[i]; + const float3 euler = rotations[i]; + float4x4 &instance_transform = instance_transforms[i]; + + float4x4 rotation_matrix; + float3 used_pivot; + + if (local_spaces[i]) { + /* Find rotation axis from the matrix. This should work even if the instance is skewed. */ + const float3 rotation_axis_x = instance_transform.values[0]; + const float3 rotation_axis_y = instance_transform.values[1]; + const float3 rotation_axis_z = instance_transform.values[2]; + + /* Create rotations around the individual axis. This could be optimized to skip some axis + * when the angle is zero. */ + float rotation_x[3][3], rotation_y[3][3], rotation_z[3][3]; + axis_angle_to_mat3(rotation_x, rotation_axis_x, euler.x); + axis_angle_to_mat3(rotation_y, rotation_axis_y, euler.y); + axis_angle_to_mat3(rotation_z, rotation_axis_z, euler.z); + + /* Combine the previously computed rotations into the final rotation matrix. */ + float rotation[3][3]; + mul_m3_series(rotation, rotation_z, rotation_y, rotation_x); + copy_m4_m3(rotation_matrix.values, rotation); + + /* Transform the passed in pivot into the local space of the instance. */ + used_pivot = instance_transform * pivot; + } + else { + used_pivot = pivot; + eul_to_mat4(rotation_matrix.values, euler); + } + /* Move the pivot to the origin so that we can rotate around it. */ + sub_v3_v3(instance_transform.values[3], used_pivot); + /* Perform the actual rotation. */ + mul_m4_m4_pre(instance_transform.values, rotation_matrix.values); + /* Undo the pivot shifting done before. */ + add_v3_v3(instance_transform.values[3], used_pivot); + } + }); +} + +static void geo_node_rotate_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Instances"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + rotate_instances(params, instances); + } + params.set_output("Instances", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_rotate_instances() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_ROTATE_INSTANCES, "Rotate Instances", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_rotate_instances_exec; + ntype.declare = blender::nodes::geo_node_rotate_instances_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc new file mode 100644 index 00000000000..ea2b458410e --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_scale_instances.cc @@ -0,0 +1,98 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_scale_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Scale")) + .subtype(PROP_XYZ) + .default_value({1, 1, 1}) + .supports_field(); + b.add_input(N_("Center")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input(N_("Local Space")).default_value(true).supports_field(); + b.add_output(N_("Instances")); +}; + +static void scale_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_POINT}; + + fn::FieldEvaluator selection_evaluator{field_context, instances_component.instances_amount()}; + selection_evaluator.add(params.extract_input>("Selection")); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + fn::FieldEvaluator transforms_evaluator{field_context, &selection}; + transforms_evaluator.add(params.extract_input>("Scale")); + transforms_evaluator.add(params.extract_input>("Center")); + transforms_evaluator.add(params.extract_input>("Local Space")); + transforms_evaluator.evaluate(); + const VArray &scales = transforms_evaluator.get_evaluated(0); + const VArray &pivots = transforms_evaluator.get_evaluated(1); + const VArray &local_spaces = transforms_evaluator.get_evaluated(2); + + MutableSpan instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + const float3 pivot = pivots[i]; + float4x4 &instance_transform = instance_transforms[i]; + + if (local_spaces[i]) { + instance_transform *= float4x4::from_location(pivot); + rescale_m4(instance_transform.values, scales[i]); + instance_transform *= float4x4::from_location(-pivot); + } + else { + const float4x4 original_transform = instance_transform; + instance_transform = float4x4::from_location(pivot); + rescale_m4(instance_transform.values, scales[i]); + instance_transform *= float4x4::from_location(-pivot); + instance_transform *= original_transform; + } + } + }); +} + +static void geo_node_scale_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Instances"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + scale_instances(params, instances); + } + params.set_output("Instances", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_scale_instances() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SCALE_INSTANCES, "Scale Instances", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_scale_instances_exec; + ntype.declare = blender::nodes::geo_node_scale_instances_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc index c63e4ec49d9..a16fb712b13 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_components.cc @@ -20,25 +20,23 @@ namespace blender::nodes { static void geo_node_join_geometry_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_output("Mesh"); - b.add_output("Point Cloud"); - b.add_output("Curve"); - b.add_output("Volume"); + b.add_input(N_("Geometry")); + b.add_output(N_("Mesh")); + b.add_output(N_("Point Cloud")); + b.add_output(N_("Curve")); + b.add_output(N_("Volume")); + b.add_output(N_("Instances")); } static void geo_node_separate_components_exec(GeoNodeExecParams params) { GeometrySet geometry_set = params.extract_input("Geometry"); - /* Note that it will be possible to skip realizing instances here when instancing - * geometry directly is supported by creating corresponding geometry instances. */ - geometry_set = bke::geometry_set_realize_instances(geometry_set); - GeometrySet meshes; GeometrySet point_clouds; GeometrySet volumes; GeometrySet curves; + GeometrySet instances; if (geometry_set.has()) { meshes.add(*geometry_set.get_component_for_read()); @@ -52,11 +50,15 @@ static void geo_node_separate_components_exec(GeoNodeExecParams params) if (geometry_set.has()) { volumes.add(*geometry_set.get_component_for_read()); } + if (geometry_set.has()) { + instances.add(*geometry_set.get_component_for_read()); + } params.set_output("Mesh", meshes); params.set_output("Point Cloud", point_clouds); params.set_output("Curve", curves); params.set_output("Volume", volumes); + params.set_output("Instances", instances); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc new file mode 100644 index 00000000000..28e214c0ccc --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_separate_geometry.cc @@ -0,0 +1,118 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_separate_geometry_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")) + .default_value(true) + .hide_value() + .supports_field() + .description(N_("The parts of the geometry that go into the first output")); + b.add_output(N_("Selection")) + .description(N_("The parts of the geometry in the selection")); + b.add_output(N_("Inverted")) + .description(N_("The parts of the geometry not in the selection")); +} + +static void geo_node_separate_geometry_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); +} + +static void geo_node_separate_geometry_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySeparateGeometry *data = (NodeGeometrySeparateGeometry *)MEM_callocN( + sizeof(NodeGeometrySeparateGeometry), __func__); + data->domain = ATTR_DOMAIN_POINT; + + node->storage = data; +} + +static void geo_node_separate_geometry_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + + const Field selection_field = params.extract_input>("Selection"); + + const bNode &node = params.node(); + const NodeGeometryDeleteGeometry &storage = *(const NodeGeometryDeleteGeometry *)node.storage; + const AttributeDomain domain = static_cast(storage.domain); + + bool all_is_error = false; + GeometrySet second_set(geometry_set); + if (params.output_is_required("Selection")) { + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + bool this_is_error = false; + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection_field, + false, + this_is_error); + all_is_error &= this_is_error; + }); + params.set_output("Selection", std::move(geometry_set)); + } + if (params.output_is_required("Inverted")) { + second_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + bool this_is_error = false; + separate_geometry(geometry_set, + domain, + GEO_NODE_DELETE_GEOMETRY_MODE_ALL, + selection_field, + true, + this_is_error); + all_is_error &= this_is_error; + }); + params.set_output("Inverted", std::move(second_set)); + } + if (all_is_error) { + /* Only show this if none of the instances/components actually changed. */ + params.error_message_add(NodeWarningType::Info, TIP_("No geometry with given domain")); + } +} + +} // namespace blender::nodes + +void register_node_type_geo_separate_geometry() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SEPARATE_GEOMETRY, "Separate Geometry", NODE_CLASS_GEOMETRY, 0); + + node_type_storage(&ntype, + "NodeGeometrySeparateGeometry", + node_free_standard_storage, + node_copy_standard_storage); + + node_type_init(&ntype, blender::nodes::geo_node_separate_geometry_init); + + ntype.declare = blender::nodes::geo_node_separate_geometry_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_separate_geometry_exec; + ntype.draw_buttons = blender::nodes::geo_node_separate_geometry_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc new file mode 100644 index 00000000000..b64aa266330 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_handles.cc @@ -0,0 +1,168 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_curve_handles_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Position")).implicit_field(); + b.add_output(N_("Curve")); +} + +static void geo_node_set_curve_handles_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + uiItemR(layout, ptr, "mode", UI_ITEM_R_EXPAND, nullptr, ICON_NONE); +} + +static void geo_node_set_curve_handles_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometrySetCurveHandlePositions *data = (NodeGeometrySetCurveHandlePositions *)MEM_callocN( + sizeof(NodeGeometrySetCurveHandlePositions), __func__); + + data->mode = GEO_NODE_CURVE_HANDLE_LEFT; + node->storage = data; +} + +static void set_position_in_component(const GeometryNodeCurveHandleMode mode, + GeometryComponent &component, + const Field &selection_field, + const Field &position_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + CurveComponent *curve_component = static_cast(&component); + CurveEval *curve = curve_component->get_for_write(); + + StringRef side = mode & GEO_NODE_CURVE_HANDLE_LEFT ? "handle_left" : "handle_right"; + + int current_point = 0; + int current_mask = 0; + + for (const SplinePtr &spline : curve->splines()) { + if (spline->type() == Spline::Type::Bezier) { + BezierSpline &bezier = static_cast(*spline); + for (int i : bezier.positions().index_range()) { + if (selection[current_mask] == current_point) { + if (mode & GEO_NODE_CURVE_HANDLE_LEFT) { + if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Vector) { + bezier.ensure_auto_handles(); + bezier.handle_types_left()[i] = BezierSpline::HandleType::Free; + } + else if (bezier.handle_types_left()[i] == BezierSpline::HandleType::Auto) { + bezier.ensure_auto_handles(); + bezier.handle_types_left()[i] = BezierSpline::HandleType::Align; + } + } + else { + if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Vector) { + bezier.ensure_auto_handles(); + bezier.handle_types_right()[i] = BezierSpline::HandleType::Free; + } + else if (bezier.handle_types_right()[i] == BezierSpline::HandleType::Auto) { + bezier.ensure_auto_handles(); + bezier.handle_types_right()[i] = BezierSpline::HandleType::Align; + } + } + current_mask++; + } + current_point++; + } + } + else { + for (int UNUSED(i) : spline->positions().index_range()) { + if (selection[current_mask] == current_point) { + current_mask++; + } + current_point++; + } + } + } + + OutputAttribute_Typed positions = component.attribute_try_get_for_output_only( + side, ATTR_DOMAIN_POINT); + fn::FieldEvaluator position_evaluator{field_context, &selection}; + position_evaluator.add_with_destination(position_field, positions.varray()); + position_evaluator.evaluate(); + positions.save(); +} + +static void geo_node_set_curve_handles_exec(GeoNodeExecParams params) +{ + const NodeGeometrySetCurveHandlePositions *node_storage = + (NodeGeometrySetCurveHandlePositions *)params.node().storage; + const GeometryNodeCurveHandleMode mode = (GeometryNodeCurveHandleMode)node_storage->mode; + + GeometrySet geometry_set = params.extract_input("Curve"); + Field selection_field = params.extract_input>("Selection"); + Field position_field = params.extract_input>("Position"); + + bool has_bezier = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve() && + geometry_set.get_curve_for_read()->has_spline_with_type(Spline::Type::Bezier)) { + has_bezier = true; + set_position_in_component(mode, + geometry_set.get_component_for_write(), + selection_field, + position_field); + } + }); + if (!has_bezier) { + params.error_message_add(NodeWarningType::Info, + TIP_("The input geometry does not contain a Bezier spline")); + } + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_curve_handles() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_CURVE_HANDLES, "Set Handle Positions", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_curve_handles_exec; + ntype.declare = blender::nodes::geo_node_set_curve_handles_declare; + ntype.minwidth = 100.0f; + node_type_init(&ntype, blender::nodes::geo_node_set_curve_handles_init); + node_type_storage(&ntype, + "NodeGeometrySetCurveHandlePositions", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_set_curve_handles_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc new file mode 100644 index 00000000000..e47ce7dea30 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_radius.cc @@ -0,0 +1,83 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_curve_radius_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Radius")) + .min(0.0f) + .default_value(1.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output(N_("Curve")); +} + +static void set_radius_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &radius_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed radii = component.attribute_try_get_for_output_only( + "radius", ATTR_DOMAIN_POINT); + fn::FieldEvaluator radii_evaluator{field_context, &selection}; + radii_evaluator.add_with_destination(radius_field, radii.varray()); + radii_evaluator.evaluate(); + radii.save(); +} + +static void geo_node_set_curve_radius_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + Field selection_field = params.extract_input>("Selection"); + Field radii_field = params.extract_input>("Radius"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_radius_in_component( + geometry_set.get_component_for_write(), selection_field, radii_field); + } + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_curve_radius() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_CURVE_RADIUS, "Set Curve Radius", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_curve_radius_exec; + ntype.declare = blender::nodes::geo_node_set_curve_radius_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc new file mode 100644 index 00000000000..a861c35f738 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_curve_tilt.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_curve_tilt_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Curve")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Tilt")).subtype(PROP_ANGLE).supports_field(); + b.add_output(N_("Curve")); +} + +static void set_tilt_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &tilt_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed tilts = component.attribute_try_get_for_output_only( + "tilt", ATTR_DOMAIN_POINT); + fn::FieldEvaluator tilt_evaluator{field_context, &selection}; + tilt_evaluator.add_with_destination(tilt_field, tilts.varray()); + tilt_evaluator.evaluate(); + tilts.save(); +} + +static void geo_node_set_curve_tilt_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Curve"); + Field selection_field = params.extract_input>("Selection"); + Field tilt_field = params.extract_input>("Tilt"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_tilt_in_component( + geometry_set.get_component_for_write(), selection_field, tilt_field); + } + }); + + params.set_output("Curve", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_curve_tilt() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_CURVE_TILT, "Set Curve Tilt", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_curve_tilt_exec; + ntype.declare = blender::nodes::geo_node_set_curve_tilt_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_id.cc b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc new file mode 100644 index 00000000000..77d8e786501 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_id.cc @@ -0,0 +1,94 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_id_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("ID")).implicit_field(); + b.add_output(N_("Geometry")); +} + +static void set_id_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &id_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + /* Since adding the ID attribute can change the result of the field evaluation (the random value + * node uses the index if the ID is unavailable), make sure that it isn't added before evaluating + * the field. However, as an optimization, use a faster code path when it already exists. */ + fn::FieldEvaluator id_evaluator{field_context, &selection}; + if (component.attribute_exists("id")) { + OutputAttribute_Typed id_attribute = component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT); + id_evaluator.add_with_destination(id_field, id_attribute.varray()); + id_evaluator.evaluate(); + id_attribute.save(); + } + else { + id_evaluator.add(id_field); + id_evaluator.evaluate(); + const VArray &result_ids = id_evaluator.get_evaluated(0); + OutputAttribute_Typed id_attribute = component.attribute_try_get_for_output_only( + "id", ATTR_DOMAIN_POINT); + result_ids.materialize(selection, id_attribute.as_span()); + id_attribute.save(); + } +} + +static void geo_node_set_id_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field id_field = params.extract_input>("ID"); + + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE}) { + if (geometry_set.has(type)) { + set_id_in_component(geometry_set.get_component_for_write(type), selection_field, id_field); + } + } + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_id() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_SET_ID, "Set ID", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_id_exec; + ntype.declare = blender::nodes::geo_node_set_id_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc similarity index 50% rename from source/blender/nodes/geometry/nodes/node_geo_material_assign.cc rename to source/blender/nodes/geometry/nodes/node_geo_set_material.cc index 43818947272..3817de02a38 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_material_assign.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material.cc @@ -21,17 +21,19 @@ #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" +#include "DNA_volume_types.h" #include "BKE_material.h" namespace blender::nodes { -static void geo_node_material_assign_declare(NodeDeclarationBuilder &b) +static void geo_node_set_material_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Material").hide_label(); - b.add_input("Selection").default_value(true).hide_value(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")) + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_VOLUME}); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Material")).hide_label(); + b.add_output(N_("Geometry")); } static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Material *material) @@ -57,29 +59,45 @@ static void assign_material_to_faces(Mesh &mesh, const IndexMask selection, Mate } } -static void geo_node_material_assign_exec(GeoNodeExecParams params) +static void geo_node_set_material_exec(GeoNodeExecParams params) { Material *material = params.extract_input("Material"); const Field selection_field = params.extract_input>("Selection"); GeometrySet geometry_set = params.extract_input("Geometry"); - geometry_set = geometry_set_realize_instances(geometry_set); + bool volume_selection_warning = false; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has()) { + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + Mesh *mesh = mesh_component.get_for_write(); + if (mesh != nullptr) { + GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; - if (geometry_set.has()) { - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - Mesh *mesh = mesh_component.get_for_write(); - if (mesh != nullptr) { + fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - GeometryComponentFieldContext field_context{mesh_component, ATTR_DOMAIN_FACE}; - - fn::FieldEvaluator selection_evaluator{field_context, mesh->totpoly}; - selection_evaluator.add(selection_field); - selection_evaluator.evaluate(); - const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); - - assign_material_to_faces(*mesh, selection, material); + assign_material_to_faces(*mesh, selection, material); + } } + if (geometry_set.has_volume()) { + Volume &volume = *geometry_set.get_volume_for_write(); + + if (selection_field.node().depends_on_input()) { + volume_selection_warning = true; + } + + BKE_id_material_eval_assign(&volume.id, 1, material); + } + }); + + if (volume_selection_warning) { + /* Only add the warning once, even if there are many unique volume instances. */ + params.error_message_add( + NodeWarningType::Info, + TIP_("Volumes only support a single material; selection input can not be a field")); } params.set_output("Geometry", std::move(geometry_set)); @@ -87,12 +105,12 @@ static void geo_node_material_assign_exec(GeoNodeExecParams params) } // namespace blender::nodes -void register_node_type_geo_material_assign() +void register_node_type_geo_set_material() { static bNodeType ntype; - geo_node_type_base(&ntype, GEO_NODE_MATERIAL_ASSIGN, "Material Assign", NODE_CLASS_GEOMETRY, 0); - ntype.declare = blender::nodes::geo_node_material_assign_declare; - ntype.geometry_node_execute = blender::nodes::geo_node_material_assign_exec; + geo_node_type_base(&ntype, GEO_NODE_SET_MATERIAL, "Set Material", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_set_material_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_set_material_exec; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc new file mode 100644 index 00000000000..a8bb1bd8644 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_material_index.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_material_index_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Material Index")).supports_field().min(0); + b.add_output(N_("Geometry")); +} + +static void set_material_index_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &index_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed indices = component.attribute_try_get_for_output_only( + "material_index", ATTR_DOMAIN_FACE); + fn::FieldEvaluator material_evaluator{field_context, &selection}; + material_evaluator.add_with_destination(index_field, indices.varray()); + material_evaluator.evaluate(); + indices.save(); +} + +static void geo_node_set_material_index_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field index_field = params.extract_input>("Material Index"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + set_material_index_in_component( + geometry_set.get_component_for_write(), selection_field, index_field); + } + }); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_material_index() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_MATERIAL_INDEX, "Set Material Index", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_material_index_exec; + ntype.declare = blender::nodes::geo_node_set_material_index_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc new file mode 100644 index 00000000000..9ff299542b4 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_point_radius.cc @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_point_radius_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Points")).supported_type(GEO_COMPONENT_TYPE_POINT_CLOUD); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Radius")) + .default_value(0.05f) + .min(0.0f) + .supports_field() + .subtype(PROP_DISTANCE); + b.add_output(N_("Points")); +} + +static void set_radius_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &radius_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed radii = component.attribute_try_get_for_output_only( + "radius", ATTR_DOMAIN_POINT); + fn::FieldEvaluator radii_evaluator{field_context, &selection}; + radii_evaluator.add_with_destination(radius_field, radii.varray()); + radii_evaluator.evaluate(); + radii.save(); +} + +static void geo_node_set_point_radius_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Points"); + Field selection_field = params.extract_input>("Selection"); + Field radii_field = params.extract_input>("Radius"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_pointcloud()) { + set_radius_in_component(geometry_set.get_component_for_write(), + selection_field, + radii_field); + } + }); + + params.set_output("Points", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_point_radius() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_POINT_RADIUS, "Set Point Radius", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_point_radius_exec; + ntype.declare = blender::nodes::geo_node_set_point_radius_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc index 4c754ddb643..4e564386a28 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_set_position.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_set_position.cc @@ -22,44 +22,64 @@ namespace blender::nodes { static void geo_node_set_position_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Position"); - b.add_input("Selection").default_value(true).hide_value(); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Position")).implicit_field(); + b.add_input(N_("Offset")).supports_field().subtype(PROP_TRANSLATION); + b.add_output(N_("Geometry")); } static void set_position_in_component(GeometryComponent &component, const Field &selection_field, - const Field &position_field) + const Field &position_field, + const Field &offset_field) { GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_POINT}; const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_POINT); + if (domain_size == 0) { + return; + } fn::FieldEvaluator selection_evaluator{field_context, domain_size}; selection_evaluator.add(selection_field); selection_evaluator.evaluate(); const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + fn::FieldEvaluator position_evaluator{field_context, &selection}; + position_evaluator.add(position_field); + position_evaluator.add(offset_field); + position_evaluator.evaluate(); + + /* TODO: We could have different code paths depending on whether the offset input is a single + * value or not */ + + const VArray &positions_input = position_evaluator.get_evaluated(0); + const VArray &offsets_input = position_evaluator.get_evaluated(1); + OutputAttribute_Typed positions = component.attribute_try_get_for_output( "position", ATTR_DOMAIN_POINT, {0, 0, 0}); - fn::FieldEvaluator position_evaluator{field_context, &selection}; - position_evaluator.add_with_destination(position_field, positions.varray()); - position_evaluator.evaluate(); + MutableSpan position_mutable = positions.as_span(); + + for (int i : selection) { + position_mutable[i] = positions_input[i] + offsets_input[i]; + } positions.save(); } static void geo_node_set_position_exec(GeoNodeExecParams params) { GeometrySet geometry = params.extract_input("Geometry"); - geometry = geometry_set_realize_instances(geometry); Field selection_field = params.extract_input>("Selection"); + Field offset_field = params.extract_input>("Offset"); Field position_field = params.extract_input>("Position"); - for (const GeometryComponentType type : - {GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}) { + for (const GeometryComponentType type : {GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES}) { if (geometry.has(type)) { set_position_in_component( - geometry.get_component_for_write(type), selection_field, position_field); + geometry.get_component_for_write(type), selection_field, position_field, offset_field); } } diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc new file mode 100644 index 00000000000..06e25c2ed55 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_shade_smooth.cc @@ -0,0 +1,78 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_shade_smooth_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Shade Smooth")).supports_field().default_value(true); + b.add_output(N_("Geometry")); +} + +static void set_smooth_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &shade_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_FACE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_FACE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed shades = component.attribute_try_get_for_output_only( + "shade_smooth", ATTR_DOMAIN_FACE); + fn::FieldEvaluator shade_evaluator{field_context, &selection}; + shade_evaluator.add_with_destination(shade_field, shades.varray()); + shade_evaluator.evaluate(); + shades.save(); +} + +static void geo_node_set_shade_smooth_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field shade_field = params.extract_input>("Shade Smooth"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_mesh()) { + set_smooth_in_component( + geometry_set.get_component_for_write(), selection_field, shade_field); + } + }); + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_shade_smooth() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_SHADE_SMOOTH, "Set Shade Smooth", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_shade_smooth_exec; + ntype.declare = blender::nodes::geo_node_set_shade_smooth_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc new file mode 100644 index 00000000000..ec751ae1d2b --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_cyclic.cc @@ -0,0 +1,79 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_spline_cyclic_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Cyclic")).supports_field(); + b.add_output(N_("Geometry")); +} + +static void set_cyclic_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &cyclic_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed cyclics = component.attribute_try_get_for_output_only( + "cyclic", ATTR_DOMAIN_CURVE); + fn::FieldEvaluator cyclic_evaluator{field_context, &selection}; + cyclic_evaluator.add_with_destination(cyclic_field, cyclics.varray()); + cyclic_evaluator.evaluate(); + cyclics.save(); +} + +static void geo_node_set_spline_cyclic_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field cyclic_field = params.extract_input>("Cyclic"); + + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + set_cyclic_in_component( + geometry_set.get_component_for_write(), selection_field, cyclic_field); + } + }); + + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_spline_cyclic() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_SPLINE_CYCLIC, "Set Spline Cyclic", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_spline_cyclic_exec; + ntype.declare = blender::nodes::geo_node_set_spline_cyclic_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc new file mode 100644 index 00000000000..ccf419975ca --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_set_spline_resolution.cc @@ -0,0 +1,95 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BKE_spline.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_set_spline_resolution_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Geometry")).supported_type(GEO_COMPONENT_TYPE_CURVE); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Resolution")).default_value(12).supports_field(); + b.add_output(N_("Geometry")); +} + +static void set_resolution_in_component(GeometryComponent &component, + const Field &selection_field, + const Field &resolution_field) +{ + GeometryComponentFieldContext field_context{component, ATTR_DOMAIN_CURVE}; + const int domain_size = component.attribute_domain_size(ATTR_DOMAIN_CURVE); + if (domain_size == 0) { + return; + } + + fn::FieldEvaluator selection_evaluator{field_context, domain_size}; + selection_evaluator.add(selection_field); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + OutputAttribute_Typed resolutions = component.attribute_try_get_for_output_only( + "resolution", ATTR_DOMAIN_CURVE); + fn::FieldEvaluator resolution_evaluator{field_context, &selection}; + resolution_evaluator.add_with_destination(resolution_field, resolutions.varray()); + resolution_evaluator.evaluate(); + resolutions.save(); +} + +static void geo_node_set_spline_resolution_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Geometry"); + Field selection_field = params.extract_input>("Selection"); + Field resolution_field = params.extract_input>("Resolution"); + + bool only_poly = true; + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (geometry_set.has_curve()) { + if (only_poly) { + for (const SplinePtr &spline : geometry_set.get_curve_for_read()->splines()) { + if (ELEM(spline->type(), Spline::Type::Bezier, Spline::Type::NURBS)) { + only_poly = false; + break; + } + } + } + set_resolution_in_component(geometry_set.get_component_for_write(), + selection_field, + resolution_field); + } + }); + + if (only_poly) { + params.error_message_add(NodeWarningType::Warning, + TIP_("Input geometry does not contain a Bezier or NURB spline")); + } + params.set_output("Geometry", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_set_spline_resolution() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_SET_SPLINE_RESOLUTION, "Set Spline Resolution", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_set_spline_resolution_exec; + ntype.declare = blender::nodes::geo_node_set_spline_resolution_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_join.cc b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc new file mode 100644 index 00000000000..98d0aca084a --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_string_join.cc @@ -0,0 +1,53 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_string_join_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Delimiter")); + b.add_input(N_("Strings")).multi_input().hide_value(); + b.add_output(N_("String")); +}; + +static void geo_node_string_join_exec(GeoNodeExecParams params) +{ + Vector strings = params.extract_multi_input("Strings"); + const std::string delim = params.extract_input("Delimiter"); + + std::string output; + for (const int i : strings.index_range()) { + output += strings[i]; + if (i < (strings.size() - 1)) { + output += delim; + } + } + params.set_output("String", std::move(output)); +} + +} // namespace blender::nodes + +void register_node_type_geo_string_join() +{ + static bNodeType ntype; + + geo_node_type_base(&ntype, GEO_NODE_STRING_JOIN, "Join Strings", NODE_CLASS_CONVERTER, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_string_join_exec; + ntype.declare = blender::nodes::geo_node_string_join_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc new file mode 100644 index 00000000000..95e94a22d81 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_string_to_curves.cc @@ -0,0 +1,317 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "DNA_curve_types.h" +#include "DNA_vfont_types.h" + +#include "BKE_curve.h" +#include "BKE_spline.hh" +#include "BKE_vfont.h" + +#include "BLI_hash.h" +#include "BLI_string_utf8.h" +#include "BLI_task.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_string_to_curves_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("String")); + b.add_input(N_("Size")).default_value(1.0f).min(0.0f).subtype(PROP_DISTANCE); + b.add_input(N_("Character Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Word Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Line Spacing")) + .default_value(1.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Text Box Width")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_input(N_("Text Box Height")) + .default_value(0.0f) + .min(0.0f) + .subtype(PROP_DISTANCE); + b.add_output(N_("Curves")); + b.add_output(N_("Remainder")); +} + +static void geo_node_string_to_curves_layout(uiLayout *layout, struct bContext *C, PointerRNA *ptr) +{ + uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); + uiTemplateID(layout, + C, + ptr, + "font", + nullptr, + "FONT_OT_open", + "FONT_OT_unlink", + UI_TEMPLATE_ID_FILTER_ALL, + false, + nullptr); + uiItemR(layout, ptr, "overflow", 0, "", ICON_NONE); + uiItemR(layout, ptr, "align_x", 0, "", ICON_NONE); + uiItemR(layout, ptr, "align_y", 0, "", ICON_NONE); +} + +static void geo_node_string_to_curves_init(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeGeometryStringToCurves *data = (NodeGeometryStringToCurves *)MEM_callocN( + sizeof(NodeGeometryStringToCurves), __func__); + + data->overflow = GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW; + data->align_x = GEO_NODE_STRING_TO_CURVES_ALIGN_X_LEFT; + data->align_y = GEO_NODE_STRING_TO_CURVES_ALIGN_Y_TOP_BASELINE; + node->storage = data; + node->id = (ID *)BKE_vfont_builtin_get(); +} + +static void geo_node_string_to_curves_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryStringToCurves *storage = (const NodeGeometryStringToCurves *)node->storage; + const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode) + storage->overflow; + bNodeSocket *socket_remainder = ((bNodeSocket *)node->outputs.first)->next; + nodeSetSocketAvailability(socket_remainder, overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE); + + bNodeSocket *height_socket = (bNodeSocket *)node->inputs.last; + bNodeSocket *width_socket = height_socket->prev; + nodeSetSocketAvailability(height_socket, overflow != GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW); + node_sock_label(width_socket, + overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? N_("Max Width") : + N_("Text Box Width")); +} + +struct TextLayout { + /* Position of each character. */ + Vector positions; + + /* The text that fit into the text box, with newline character sequences replaced. */ + std::string text; + + /* The text that didn't fit into the text box in 'Truncate' mode. May be empty. */ + std::string truncated_text; + + /* Font size could be modified if in 'Scale to fit'-mode. */ + float final_font_size; +}; + +static TextLayout get_text_layout(GeoNodeExecParams ¶ms) +{ + TextLayout layout; + layout.text = params.extract_input("String"); + if (layout.text.empty()) { + return {}; + } + + const NodeGeometryStringToCurves &storage = + *(const NodeGeometryStringToCurves *)params.node().storage; + const GeometryNodeStringToCurvesOverflowMode overflow = (GeometryNodeStringToCurvesOverflowMode) + storage.overflow; + const GeometryNodeStringToCurvesAlignXMode align_x = (GeometryNodeStringToCurvesAlignXMode) + storage.align_x; + const GeometryNodeStringToCurvesAlignYMode align_y = (GeometryNodeStringToCurvesAlignYMode) + storage.align_y; + + const float font_size = std::max(params.extract_input("Size"), 0.0f); + const float char_spacing = params.extract_input("Character Spacing"); + const float word_spacing = params.extract_input("Word Spacing"); + const float line_spacing = params.extract_input("Line Spacing"); + const float textbox_w = params.extract_input("Text Box Width"); + const float textbox_h = overflow == GEO_NODE_STRING_TO_CURVES_MODE_OVERFLOW ? + 0.0f : + params.extract_input("Text Box Height"); + VFont *vfont = (VFont *)params.node().id; + + Curve cu = {{nullptr}}; + cu.type = OB_FONT; + /* Set defaults */ + cu.resolu = 12; + cu.smallcaps_scale = 0.75f; + cu.wordspace = 1.0f; + /* Set values from inputs */ + cu.spacemode = align_x; + cu.align_y = align_y; + cu.fsize = font_size; + cu.spacing = char_spacing; + cu.wordspace = word_spacing; + cu.linedist = line_spacing; + cu.vfont = vfont; + cu.overflow = overflow; + cu.tb = (TextBox *)MEM_calloc_arrayN(MAXTEXTBOX, sizeof(TextBox), __func__); + cu.tb->w = textbox_w; + cu.tb->h = textbox_h; + cu.totbox = 1; + size_t len_bytes; + size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); + cu.len_char32 = len_chars; + cu.len = len_bytes; + cu.pos = len_chars; + /* The reason for the additional character here is unknown, but reflects other code elsewhere. */ + cu.str = (char *)MEM_mallocN(len_bytes + sizeof(char32_t), __func__); + cu.strinfo = (CharInfo *)MEM_callocN((len_chars + 1) * sizeof(CharInfo), __func__); + BLI_strncpy(cu.str, layout.text.c_str(), len_bytes + 1); + + struct CharTrans *chartransdata = nullptr; + int text_len; + bool text_free; + const char32_t *r_text = nullptr; + /* Mode FO_DUPLI used because it doesn't create curve splines. */ + BKE_vfont_to_curve_ex( + nullptr, &cu, FO_DUPLI, nullptr, &r_text, &text_len, &text_free, &chartransdata); + + if (text_free) { + MEM_freeN((void *)r_text); + } + + Span info{cu.strinfo, text_len}; + layout.final_font_size = cu.fsize_realtime; + layout.positions.reserve(text_len); + + for (const int i : IndexRange(text_len)) { + CharTrans &ct = chartransdata[i]; + layout.positions.append(float2(ct.xof, ct.yof) * layout.final_font_size); + + if ((info[i].flag & CU_CHINFO_OVERFLOW) && (cu.overflow == CU_OVERFLOW_TRUNCATE)) { + const int offset = BLI_str_utf8_offset_from_index(layout.text.c_str(), i + 1); + layout.truncated_text = layout.text.substr(offset); + layout.text = layout.text.substr(0, offset); + break; + } + } + + MEM_SAFE_FREE(chartransdata); + MEM_SAFE_FREE(cu.str); + MEM_SAFE_FREE(cu.strinfo); + MEM_SAFE_FREE(cu.tb); + + return layout; +} + +/* Returns a mapping of UTF-32 character code to instance handle. */ +static Map create_curve_instances(GeoNodeExecParams ¶ms, + const float fontsize, + const Span charcodes, + InstancesComponent &instance_component) +{ + VFont *vfont = (VFont *)params.node().id; + Map handles; + + for (int i : charcodes.index_range()) { + if (handles.contains(charcodes[i])) { + continue; + } + Curve cu = {{nullptr}}; + cu.type = OB_FONT; + cu.resolu = 12; + cu.vfont = vfont; + CharInfo charinfo = {0}; + charinfo.mat_nr = 1; + + BKE_vfont_build_char(&cu, &cu.nurb, charcodes[i], &charinfo, 0, 0, 0, i, 1); + std::unique_ptr curve_eval = curve_eval_from_dna_curve(cu); + BKE_nurbList_free(&cu.nurb); + float4x4 size_matrix = float4x4::identity(); + size_matrix.apply_scale(fontsize); + curve_eval->transform(size_matrix); + + GeometrySet geometry_set_curve = GeometrySet::create_with_curve(curve_eval.release()); + handles.add_new(charcodes[i], instance_component.add_reference(std::move(geometry_set_curve))); + } + return handles; +} + +static void add_instances_from_handles(InstancesComponent &instances, + const Map &char_handles, + const Span charcodes, + const Span positions) +{ + instances.resize(positions.size()); + MutableSpan handles = instances.instance_reference_handles(); + MutableSpan transforms = instances.instance_transforms(); + + threading::parallel_for(IndexRange(positions.size()), 256, [&](IndexRange range) { + for (const int i : range) { + handles[i] = char_handles.lookup(charcodes[i]); + transforms[i] = float4x4::from_location({positions[i].x, positions[i].y, 0}); + } + }); +} + +static void geo_node_string_to_curves_exec(GeoNodeExecParams params) +{ + TextLayout layout = get_text_layout(params); + + const NodeGeometryStringToCurves &storage = + *(const NodeGeometryStringToCurves *)params.node().storage; + if (storage.overflow == GEO_NODE_STRING_TO_CURVES_MODE_TRUNCATE) { + params.set_output("Remainder", std::move(layout.truncated_text)); + } + + if (layout.positions.size() == 0) { + params.set_output("Curves", GeometrySet()); + return; + } + + /* Convert UTF-8 encoded string to UTF-32. */ + size_t len_bytes; + size_t len_chars = BLI_strlen_utf8_ex(layout.text.c_str(), &len_bytes); + Array char_codes_with_null(len_chars + 1); + BLI_str_utf8_as_utf32(char_codes_with_null.data(), layout.text.c_str(), len_chars + 1); + const Span char_codes = char_codes_with_null.as_span().drop_back(1); + + /* Create and add instances. */ + GeometrySet geometry_set_out; + InstancesComponent &instances = geometry_set_out.get_component_for_write(); + Map char_handles = create_curve_instances( + params, layout.final_font_size, char_codes, instances); + add_instances_from_handles(instances, char_handles, char_codes, layout.positions); + + params.set_output("Curves", std::move(geometry_set_out)); +} + +} // namespace blender::nodes + +void register_node_type_geo_string_to_curves() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_STRING_TO_CURVES, "String to Curves", NODE_CLASS_GEOMETRY, 0); + ntype.declare = blender::nodes::geo_node_string_to_curves_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_string_to_curves_exec; + node_type_init(&ntype, blender::nodes::geo_node_string_to_curves_init); + node_type_update(&ntype, blender::nodes::geo_node_string_to_curves_update); + node_type_size(&ntype, 190, 120, 700); + node_type_storage(&ntype, + "NodeGeometryStringToCurves", + node_free_standard_storage, + node_copy_standard_storage); + ntype.draw_buttons = blender::nodes::geo_node_string_to_curves_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 4541bf3569f..2b3430a5ed0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -14,37 +14,40 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_modifier_types.h" + #include "BKE_mesh.h" #include "BKE_subdiv.h" #include "BKE_subdiv_mesh.h" -#include "DNA_modifier_types.h" #include "UI_interface.h" #include "UI_resources.h" + #include "node_geometry_util.hh" namespace blender::nodes { static void geo_node_subdivision_surface_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Level").default_value(1).min(0).max(6); - b.add_input("Use Creases"); - b.add_output("Geometry"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Level")).default_value(1).min(0).max(6); + b.add_input(N_("Crease")) + .default_value(0.0f) + .min(0.0f) + .max(1.0f) + .supports_field() + .subtype(PROP_FACTOR); + b.add_output(N_("Mesh")); } static void geo_node_subdivision_surface_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { -#ifdef WITH_OPENSUBDIV - uiLayoutSetPropSep(layout, true); - uiLayoutSetPropDecorate(layout, false); - uiItemR(layout, ptr, "uv_smooth", 0, nullptr, ICON_NONE); - uiItemR(layout, ptr, "boundary_smooth", 0, nullptr, ICON_NONE); -#else - UNUSED_VARS(layout, ptr); -#endif + uiItemR(layout, ptr, "uv_smooth", 0, "", ICON_NONE); + uiItemR(layout, ptr, "boundary_smooth", 0, "", ICON_NONE); } static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *node) @@ -58,19 +61,13 @@ static void geo_node_subdivision_surface_init(bNodeTree *UNUSED(ntree), bNode *n static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); - - geometry_set = geometry_set_realize_instances(geometry_set); - - if (!geometry_set.has_mesh()) { - params.set_output("Geometry", geometry_set); - return; - } - + GeometrySet geometry_set = params.extract_input("Mesh"); #ifndef WITH_OPENSUBDIV params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenSubdiv")); #else + Field crease_field = params.extract_input>("Crease"); + const NodeGeometrySubdivisionSurface &storage = *(const NodeGeometrySubdivisionSurface *)params.node().storage; const int uv_smooth = storage.uv_smooth; @@ -79,51 +76,73 @@ static void geo_node_subdivision_surface_exec(GeoNodeExecParams params) /* Only process subdivision if level is greater than 0. */ if (subdiv_level == 0) { - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); return; } - const bool use_crease = params.extract_input("Use Creases"); - const Mesh *mesh_in = geometry_set.get_mesh_for_read(); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + if (!geometry_set.has_mesh()) { + return; + } - /* Initialize mesh settings. */ - SubdivToMeshSettings mesh_settings; - mesh_settings.resolution = (1 << subdiv_level) + 1; - mesh_settings.use_optimal_display = false; + MeshComponent &mesh_component = geometry_set.get_component_for_write(); + AttributeDomain domain = ATTR_DOMAIN_EDGE; + GeometryComponentFieldContext field_context{mesh_component, domain}; + const int domain_size = mesh_component.attribute_domain_size(domain); - /* Initialize subdivision settings. */ - SubdivSettings subdiv_settings; - subdiv_settings.is_simple = false; - subdiv_settings.is_adaptive = false; - subdiv_settings.use_creases = use_crease; - subdiv_settings.level = subdiv_level; + if (domain_size == 0) { + return; + } - subdiv_settings.vtx_boundary_interpolation = BKE_subdiv_vtx_boundary_interpolation_from_subsurf( - boundary_smooth); - subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( - uv_smooth); + FieldEvaluator evaluator(field_context, domain_size); + evaluator.add(crease_field); + evaluator.evaluate(); + const VArray &creases = evaluator.get_evaluated(0); - /* Apply subdivision to mesh. */ - Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + OutputAttribute_Typed crease = mesh_component.attribute_try_get_for_output_only( + "crease", domain); + MutableSpan crease_span = crease.as_span(); + for (auto i : creases.index_range()) { + crease_span[i] = std::clamp(creases[i], 0.0f, 1.0f); + } + crease.save(); - /* In case of bad topology, skip to input mesh. */ - if (subdiv == nullptr) { - params.set_output("Geometry", std::move(geometry_set)); - return; - } + /* Initialize mesh settings. */ + SubdivToMeshSettings mesh_settings; + mesh_settings.resolution = (1 << subdiv_level) + 1; + mesh_settings.use_optimal_display = false; - Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); - BKE_mesh_normals_tag_dirty(mesh_out); + /* Initialize subdivision settings. */ + SubdivSettings subdiv_settings; + subdiv_settings.is_simple = false; + subdiv_settings.is_adaptive = false; + subdiv_settings.use_creases = !(creases.is_single() && creases.get_internal_single() == 0.0f); + subdiv_settings.level = subdiv_level; - MeshComponent &mesh_component = geometry_set.get_component_for_write(); - mesh_component.replace(mesh_out); + subdiv_settings.vtx_boundary_interpolation = + BKE_subdiv_vtx_boundary_interpolation_from_subsurf(boundary_smooth); + subdiv_settings.fvar_linear_interpolation = BKE_subdiv_fvar_interpolation_from_uv_smooth( + uv_smooth); - // BKE_subdiv_stats_print(&subdiv->stats); - BKE_subdiv_free(subdiv); + Mesh *mesh_in = mesh_component.get_for_write(); + /* Apply subdivision to mesh. */ + Subdiv *subdiv = BKE_subdiv_update_from_mesh(nullptr, &subdiv_settings, mesh_in); + + /* In case of bad topology, skip to input mesh. */ + if (subdiv == nullptr) { + return; + } + + Mesh *mesh_out = BKE_subdiv_to_mesh(subdiv, &mesh_settings, mesh_in); + BKE_mesh_normals_tag_dirty(mesh_out); + + mesh_component.replace(mesh_out); + + BKE_subdiv_free(subdiv); + }); #endif - - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_switch.cc b/source/blender/nodes/geometry/nodes/node_geo_switch.cc index ca857c4d2e3..7e07a552650 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_switch.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_switch.cc @@ -19,46 +19,68 @@ #include "UI_interface.h" #include "UI_resources.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" + +#include "BKE_material.h" + +#include "FN_multi_function_signature.hh" + namespace blender::nodes { static void geo_node_switch_declare(NodeDeclarationBuilder &b) { - b.add_input("Switch"); + b.add_input(N_("Switch")).default_value(false).supports_field(); + b.add_input(N_("Switch"), "Switch_001").default_value(false); - b.add_input("False"); - b.add_input("True"); - b.add_input("False", "False_001").min(-100000).max(100000); - b.add_input("True", "True_001").min(-100000).max(100000); - b.add_input("False", "False_002"); - b.add_input("True", "True_002"); - b.add_input("False", "False_003"); - b.add_input("True", "True_003"); - b.add_input("False", "False_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input("True", "True_004").default_value({0.8f, 0.8f, 0.8f, 1.0f}); - b.add_input("False", "False_005"); - b.add_input("True", "True_005"); - b.add_input("False", "False_006"); - b.add_input("True", "True_006"); - b.add_input("False", "False_007"); - b.add_input("True", "True_007"); - b.add_input("False", "False_008"); - b.add_input("True", "True_008"); - b.add_input("False", "False_009"); - b.add_input("True", "True_009"); - b.add_input("False", "False_010"); - b.add_input("True", "True_010"); + b.add_input(N_("False")).supports_field(); + b.add_input(N_("True")).supports_field(); + b.add_input(N_("False"), "False_001").min(-100000).max(100000).supports_field(); + b.add_input(N_("True"), "True_001").min(-100000).max(100000).supports_field(); + b.add_input(N_("False"), "False_002") + .default_value(false) + .hide_value() + .supports_field(); + b.add_input(N_("True"), "True_002") + .default_value(true) + .hide_value() + .supports_field(); + b.add_input(N_("False"), "False_003").supports_field(); + b.add_input(N_("True"), "True_003").supports_field(); + b.add_input(N_("False"), "False_004") + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .supports_field(); + b.add_input(N_("True"), "True_004") + .default_value({0.8f, 0.8f, 0.8f, 1.0f}) + .supports_field(); + b.add_input(N_("False"), "False_005").supports_field(); + b.add_input(N_("True"), "True_005").supports_field(); - b.add_output("Output"); - b.add_output("Output", "Output_001"); - b.add_output("Output", "Output_002"); - b.add_output("Output", "Output_003"); - b.add_output("Output", "Output_004"); - b.add_output("Output", "Output_005"); - b.add_output("Output", "Output_006"); - b.add_output("Output", "Output_007"); - b.add_output("Output", "Output_008"); - b.add_output("Output", "Output_009"); - b.add_output("Output", "Output_010"); + b.add_input(N_("False"), "False_006"); + b.add_input(N_("True"), "True_006"); + b.add_input(N_("False"), "False_007"); + b.add_input(N_("True"), "True_007"); + b.add_input(N_("False"), "False_008"); + b.add_input(N_("True"), "True_008"); + b.add_input(N_("False"), "False_009"); + b.add_input(N_("True"), "True_009"); + b.add_input(N_("False"), "False_010"); + b.add_input(N_("True"), "True_010"); + b.add_input(N_("False"), "False_011"); + b.add_input(N_("True"), "True_011"); + + b.add_output(N_("Output")).dependent_field(); + b.add_output(N_("Output"), "Output_001").dependent_field(); + b.add_output(N_("Output"), "Output_002").dependent_field(); + b.add_output(N_("Output"), "Output_003").dependent_field(); + b.add_output(N_("Output"), "Output_004").dependent_field(); + b.add_output(N_("Output"), "Output_005").dependent_field(); + b.add_output(N_("Output"), "Output_006"); + b.add_output(N_("Output"), "Output_007"); + b.add_output(N_("Output"), "Output_008"); + b.add_output(N_("Output"), "Output_009"); + b.add_output(N_("Output"), "Output_010"); + b.add_output(N_("Output"), "Output_011"); } static void geo_node_switch_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -77,91 +99,192 @@ static void geo_node_switch_update(bNodeTree *UNUSED(ntree), bNode *node) { NodeSwitch *node_storage = (NodeSwitch *)node->storage; int index = 0; - LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { - nodeSetSocketAvailability( - socket, index == 0 || socket->type == (eNodeSocketDatatype)node_storage->input_type); - index++; + bNodeSocket *field_switch = (bNodeSocket *)node->inputs.first; + bNodeSocket *non_field_switch = (bNodeSocket *)field_switch->next; + + const bool fields_type = ELEM((eNodeSocketDatatype)node_storage->input_type, + SOCK_FLOAT, + SOCK_INT, + SOCK_BOOLEAN, + SOCK_VECTOR, + SOCK_RGBA, + SOCK_STRING); + + nodeSetSocketAvailability(field_switch, fields_type); + nodeSetSocketAvailability(non_field_switch, !fields_type); + + LISTBASE_FOREACH_INDEX (bNodeSocket *, socket, &node->inputs, index) { + if (index <= 1) { + continue; + } + nodeSetSocketAvailability(socket, + socket->type == (eNodeSocketDatatype)node_storage->input_type); } + LISTBASE_FOREACH (bNodeSocket *, socket, &node->outputs) { nodeSetSocketAvailability(socket, socket->type == (eNodeSocketDatatype)node_storage->input_type); } } -template -static void output_input(GeoNodeExecParams ¶ms, - const bool input, - const StringRef input_suffix, - const StringRef output_identifier) +template class SwitchFieldsFunction : public fn::MultiFunction { + public: + SwitchFieldsFunction() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Switch"}; + signature.single_input("Switch"); + signature.single_input("False"); + signature.single_input("True"); + signature.single_output("Output"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &switches = params.readonly_single_input(0, "Switch"); + const VArray &falses = params.readonly_single_input(1, "False"); + const VArray &trues = params.readonly_single_input(2, "True"); + MutableSpan values = params.uninitialized_single_output_if_required(3, "Output"); + for (int64_t i : mask) { + new (&values[i]) T(switches[i] ? trues[i] : falses[i]); + } + } +}; + +template void switch_fields(GeoNodeExecParams ¶ms, const StringRef suffix) { - const std::string name_a = "False" + input_suffix; - const std::string name_b = "True" + input_suffix; - if (input) { - params.set_input_unused(name_a); - if (params.lazy_require_input(name_b)) { + if (params.lazy_require_input("Switch")) { + return; + } + + const std::string name_false = "False" + suffix; + const std::string name_true = "True" + suffix; + const std::string name_output = "Output" + suffix; + + Field switches_field = params.get_input>("Switch"); + if (switches_field.node().depends_on_input()) { + /* The switch has to be incorporated into the field. Both inputs have to be evaluated. */ + const bool require_false = params.lazy_require_input(name_false); + const bool require_true = params.lazy_require_input(name_true); + if (require_false | require_true) { return; } - params.set_output(output_identifier, params.extract_input(name_b)); + + Field falses_field = params.extract_input>(name_false); + Field trues_field = params.extract_input>(name_true); + + auto switch_fn = std::make_unique>(); + auto switch_op = std::make_shared(FieldOperation( + std::move(switch_fn), + {std::move(switches_field), std::move(falses_field), std::move(trues_field)})); + + params.set_output(name_output, Field(switch_op, 0)); } else { - params.set_input_unused(name_b); - if (params.lazy_require_input(name_a)) { + /* The switch input is constant, so just evaluate and forward one of the inputs. */ + const bool switch_value = fn::evaluate_constant_field(switches_field); + if (switch_value) { + params.set_input_unused(name_false); + if (params.lazy_require_input(name_true)) { + return; + } + params.set_output(name_output, params.extract_input>(name_true)); + } + else { + params.set_input_unused(name_true); + if (params.lazy_require_input(name_false)) { + return; + } + params.set_output(name_output, params.extract_input>(name_false)); + } + } +} + +template void switch_no_fields(GeoNodeExecParams ¶ms, const StringRef suffix) +{ + if (params.lazy_require_input("Switch_001")) { + return; + } + bool switch_value = params.get_input("Switch_001"); + + const std::string name_false = "False" + suffix; + const std::string name_true = "True" + suffix; + const std::string name_output = "Output" + suffix; + + if (switch_value) { + params.set_input_unused(name_false); + if (params.lazy_require_input(name_true)) { return; } - params.set_output(output_identifier, params.extract_input(name_a)); + params.set_output(name_output, params.extract_input(name_true)); + } + else { + params.set_input_unused(name_true); + if (params.lazy_require_input(name_false)) { + return; + } + params.set_output(name_output, params.extract_input(name_false)); } } static void geo_node_switch_exec(GeoNodeExecParams params) { - if (params.lazy_require_input("Switch")) { - return; - } const NodeSwitch &storage = *(const NodeSwitch *)params.node().storage; - const bool input = params.get_input("Switch"); - switch ((eNodeSocketDatatype)storage.input_type) { + const eNodeSocketDatatype data_type = static_cast(storage.input_type); + + switch (data_type) { + case SOCK_FLOAT: { - output_input(params, input, "", "Output"); + switch_fields(params, ""); break; } case SOCK_INT: { - output_input(params, input, "_001", "Output_001"); + switch_fields(params, "_001"); break; } case SOCK_BOOLEAN: { - output_input(params, input, "_002", "Output_002"); + switch_fields(params, "_002"); break; } case SOCK_VECTOR: { - output_input(params, input, "_003", "Output_003"); + switch_fields(params, "_003"); break; } case SOCK_RGBA: { - output_input(params, input, "_004", "Output_004"); + switch_fields(params, "_004"); break; } case SOCK_STRING: { - output_input(params, input, "_005", "Output_005"); + switch_fields(params, "_005"); break; } case SOCK_GEOMETRY: { - output_input(params, input, "_006", "Output_006"); + switch_no_fields(params, "_006"); break; } case SOCK_OBJECT: { - output_input(params, input, "_007", "Output_007"); + switch_no_fields(params, "_007"); break; } case SOCK_COLLECTION: { - output_input(params, input, "_008", "Output_008"); + switch_no_fields(params, "_008"); break; } case SOCK_TEXTURE: { - output_input(params, input, "_009", "Output_009"); + switch_no_fields(params, "_009"); break; } case SOCK_MATERIAL: { - output_input(params, input, "_010", "Output_010"); + switch_no_fields(params, "_010"); + break; + } + case SOCK_IMAGE: { + switch_no_fields(params, "_011"); break; } default: diff --git a/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc new file mode 100644 index 00000000000..a889678537f --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_transfer_attribute.cc @@ -0,0 +1,823 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_kdopbvh.h" +#include "BLI_task.hh" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_pointcloud_types.h" + +#include "BKE_attribute_math.hh" +#include "BKE_bvhutils.h" +#include "BKE_mesh_runtime.h" +#include "BKE_mesh_sample.hh" + +#include "FN_generic_array.hh" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_geometry_util.hh" + +using namespace blender::bke::mesh_surface_sample; +using blender::fn::GArray; + +namespace blender::nodes { + +static void geo_node_transfer_attribute_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Target")) + .only_realized_data() + .supported_type({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD}); + + b.add_input(N_("Attribute")).hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_001").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_002").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_003").hide_value().supports_field(); + b.add_input(N_("Attribute"), "Attribute_004").hide_value().supports_field(); + + b.add_input(N_("Source Position")).implicit_field(); + b.add_input(N_("Index")).implicit_field(); + + b.add_output(N_("Attribute")).dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_001").dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_002").dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_003").dependent_field({6, 7}); + b.add_output(N_("Attribute"), "Attribute_004").dependent_field({6, 7}); +} + +static void geo_node_transfer_attribute_layout(uiLayout *layout, + bContext *UNUSED(C), + PointerRNA *ptr) +{ + const bNode &node = *static_cast(ptr->data); + const NodeGeometryTransferAttribute &data = *static_cast( + node.storage); + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)data.mode; + + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); + uiItemR(layout, ptr, "mapping", 0, "", ICON_NONE); + if (mapping != GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED) { + uiItemR(layout, ptr, "domain", 0, "", ICON_NONE); + } +} + +static void geo_node_transfer_attribute_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryTransferAttribute *data = (NodeGeometryTransferAttribute *)MEM_callocN( + sizeof(NodeGeometryTransferAttribute), __func__); + data->data_type = CD_PROP_FLOAT; + data->mode = GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED; + node->storage = data; +} + +static void geo_node_transfer_attribute_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryTransferAttribute &data = *(const NodeGeometryTransferAttribute *) + node->storage; + const CustomDataType data_type = static_cast(data.data_type); + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)data.mode; + + bNodeSocket *socket_geometry = (bNodeSocket *)node->inputs.first; + bNodeSocket *socket_vector = socket_geometry->next; + bNodeSocket *socket_float = socket_vector->next; + bNodeSocket *socket_color4f = socket_float->next; + bNodeSocket *socket_boolean = socket_color4f->next; + bNodeSocket *socket_int32 = socket_boolean->next; + + bNodeSocket *socket_positions = socket_int32->next; + bNodeSocket *socket_indices = socket_positions->next; + + nodeSetSocketAvailability(socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(socket_int32, data_type == CD_PROP_INT32); + + nodeSetSocketAvailability(socket_positions, mapping != GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); + nodeSetSocketAvailability(socket_indices, mapping == GEO_NODE_ATTRIBUTE_TRANSFER_INDEX); + + bNodeSocket *out_socket_vector = (bNodeSocket *)node->outputs.first; + bNodeSocket *out_socket_float = out_socket_vector->next; + bNodeSocket *out_socket_color4f = out_socket_float->next; + bNodeSocket *out_socket_boolean = out_socket_color4f->next; + bNodeSocket *out_socket_int32 = out_socket_boolean->next; + + nodeSetSocketAvailability(out_socket_vector, data_type == CD_PROP_FLOAT3); + nodeSetSocketAvailability(out_socket_float, data_type == CD_PROP_FLOAT); + nodeSetSocketAvailability(out_socket_color4f, data_type == CD_PROP_COLOR); + nodeSetSocketAvailability(out_socket_boolean, data_type == CD_PROP_BOOL); + nodeSetSocketAvailability(out_socket_int32, data_type == CD_PROP_INT32); +} + +static void get_closest_in_bvhtree(BVHTreeFromMesh &tree_data, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(positions.size() == r_indices.size() || r_indices.is_empty()); + BLI_assert(positions.size() == r_distances_sq.size() || r_distances_sq.is_empty()); + BLI_assert(positions.size() == r_positions.size() || r_positions.is_empty()); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + if (!r_indices.is_empty()) { + r_indices[i] = nearest.index; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + if (!r_positions.is_empty()) { + r_positions[i] = nearest.co; + } + } +} + +static void get_closest_pointcloud_points(const PointCloud &pointcloud, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_indices, + const MutableSpan r_distances_sq) +{ + BLI_assert(positions.size() == r_indices.size()); + BLI_assert(pointcloud.totpoint > 0); + + BVHTreeFromPointCloud tree_data; + BKE_bvhtree_from_pointcloud_get(&tree_data, &pointcloud, 2); + + for (const int i : mask) { + BVHTreeNearest nearest; + nearest.dist_sq = FLT_MAX; + const float3 position = positions[i]; + BLI_bvhtree_find_nearest( + tree_data.tree, position, &nearest, tree_data.nearest_callback, &tree_data); + r_indices[i] = nearest.index; + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = nearest.dist_sq; + } + } + + free_bvhtree_from_pointcloud(&tree_data); +} + +static void get_closest_mesh_points(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_point_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totvert > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_VERTS, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_point_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_edges(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_edge_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totedge > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_EDGES, 2); + get_closest_in_bvhtree(tree_data, positions, mask, r_edge_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_looptris(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_looptri_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totpoly > 0); + BVHTreeFromMesh tree_data; + BKE_bvhtree_from_mesh_get(&tree_data, &mesh, BVHTREE_FROM_LOOPTRI, 2); + get_closest_in_bvhtree( + tree_data, positions, mask, r_looptri_indices, r_distances_sq, r_positions); + free_bvhtree_from_mesh(&tree_data); +} + +static void get_closest_mesh_polygons(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_poly_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totpoly > 0); + + Array looptri_indices(positions.size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, r_distances_sq, r_positions); + + const Span looptris{BKE_mesh_runtime_looptri_ensure(&mesh), + BKE_mesh_runtime_looptri_len(&mesh)}; + + for (const int i : mask) { + const MLoopTri &looptri = looptris[looptri_indices[i]]; + r_poly_indices[i] = looptri.poly; + } +} + +/* The closest corner is defined to be the closest corner on the closest face. */ +static void get_closest_mesh_corners(const Mesh &mesh, + const VArray &positions, + const IndexMask mask, + const MutableSpan r_corner_indices, + const MutableSpan r_distances_sq, + const MutableSpan r_positions) +{ + BLI_assert(mesh.totloop > 0); + Array poly_indices(positions.size()); + get_closest_mesh_polygons(mesh, positions, mask, poly_indices, {}, {}); + + for (const int i : mask) { + const float3 position = positions[i]; + const int poly_index = poly_indices[i]; + const MPoly &poly = mesh.mpoly[poly_index]; + + /* Find the closest vertex in the polygon. */ + float min_distance_sq = FLT_MAX; + const MVert *closest_mvert; + int closest_loop_index = 0; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vertex_index = loop.v; + const MVert &mvert = mesh.mvert[vertex_index]; + const float distance_sq = float3::distance_squared(position, mvert.co); + if (distance_sq < min_distance_sq) { + min_distance_sq = distance_sq; + closest_loop_index = loop_index; + closest_mvert = &mvert; + } + } + if (!r_corner_indices.is_empty()) { + r_corner_indices[i] = closest_loop_index; + } + if (!r_positions.is_empty()) { + r_positions[i] = closest_mvert->co; + } + if (!r_distances_sq.is_empty()) { + r_distances_sq[i] = min_distance_sq; + } + } +} + +template +void copy_with_indices(const VArray &src, + const IndexMask mask, + const Span indices, + const MutableSpan dst) +{ + if (src.is_empty()) { + return; + } + for (const int i : mask) { + dst[i] = src[indices[i]]; + } +} + +template +void copy_with_indices_clamped(const VArray &src, + const IndexMask mask, + const Span indices, + const MutableSpan dst) +{ + if (src.is_empty()) { + return; + } + const int max_index = src.size() - 1; + threading::parallel_for(mask.index_range(), 4096, [&](IndexRange range) { + for (const int i : range) { + const int index = mask[i]; + dst[index] = src[std::clamp(indices[index], 0, max_index)]; + } + }); +} + +template +void copy_with_indices_and_comparison(const VArray &src_1, + const VArray &src_2, + const Span distances_1, + const Span distances_2, + const IndexMask mask, + const Span indices_1, + const Span indices_2, + const MutableSpan dst) +{ + if (src_1.is_empty() || src_2.is_empty()) { + return; + } + for (const int i : mask) { + if (distances_1[i] < distances_2[i]) { + dst[i] = src_1[indices_1[i]]; + } + else { + dst[i] = src_2[indices_2[i]]; + } + } +} + +static bool component_is_available(const GeometrySet &geometry, + const GeometryComponentType type, + const AttributeDomain domain) +{ + if (!geometry.has(type)) { + return false; + } + const GeometryComponent &component = *geometry.get_component_for_read(type); + if (component.is_empty()) { + return false; + } + return component.attribute_domain_size(domain) != 0; +} + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the target geometry once and store + * it for later. + */ +class NearestInterpolatedTransferFunction : public fn::MultiFunction { + GeometrySet target_; + GField src_field_; + + /** + * This function is meant to sample the surface of a mesh rather than take the value from + * individual elements, so use the most complex domain, ensuring no information is lost. In the + * future, it should be possible to use the most complex domain required by the field inputs, to + * simplify sampling and avoid domain conversions. + */ + AttributeDomain domain_ = ATTR_DOMAIN_CORNER; + + fn::MFSignature signature_; + + std::optional target_context_; + std::unique_ptr target_evaluator_; + const GVArray *target_data_; + + public: + NearestInterpolatedTransferFunction(GeometrySet geometry, GField src_field) + : target_(std::move(geometry)), src_field_(std::move(src_field)) + { + target_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + this->evaluate_target_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest Interpolated"}; + signature.single_input("Position"); + signature.single_output("Attribute", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &positions = params.readonly_single_input(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); + + const MeshComponent &mesh_component = *target_.get_component_for_read(); + BLI_assert(mesh_component.has_mesh()); + const Mesh &mesh = *mesh_component.get_for_read(); + BLI_assert(mesh.totpoly > 0); + + /* Find closest points on the mesh surface. */ + Array looptri_indices(mask.min_array_size()); + Array sampled_positions(mask.min_array_size()); + get_closest_mesh_looptris(mesh, positions, mask, looptri_indices, {}, sampled_positions); + + MeshAttributeInterpolator interp(&mesh, mask, sampled_positions, looptri_indices); + interp.sample_data(*target_data_, domain_, eAttributeMapMode::INTERPOLATED, dst); + } + + private: + void evaluate_target_field() + { + const MeshComponent &mesh_component = *target_.get_component_for_read(); + target_context_.emplace(GeometryComponentFieldContext{mesh_component, domain_}); + const int domain_size = mesh_component.attribute_domain_size(domain_); + target_evaluator_ = std::make_unique(*target_context_, domain_size); + target_evaluator_->add(src_field_); + target_evaluator_->evaluate(); + target_data_ = &target_evaluator_->get_evaluated(0); + } +}; + +/** + * \note Multi-threading for this function is provided by the field evaluator. Since the #call + * function could be called many times, calculate the data from the target geometry once and store + * it for later. + */ +class NearestTransferFunction : public fn::MultiFunction { + GeometrySet target_; + GField src_field_; + AttributeDomain domain_; + + fn::MFSignature signature_; + + bool use_mesh_; + bool use_points_; + + /* Store data from the target as a virtual array, since we may only access a few indices. */ + std::optional mesh_context_; + std::unique_ptr mesh_evaluator_; + const GVArray *mesh_data_; + + std::optional point_context_; + std::unique_ptr point_evaluator_; + const GVArray *point_data_; + + public: + NearestTransferFunction(GeometrySet geometry, GField src_field, AttributeDomain domain) + : target_(std::move(geometry)), src_field_(std::move(src_field)), domain_(domain) + { + target_.ensure_owns_direct_data(); + signature_ = this->create_signature(); + this->set_signature(&signature_); + + this->use_mesh_ = component_is_available(target_, GEO_COMPONENT_TYPE_MESH, domain_); + this->use_points_ = component_is_available(target_, GEO_COMPONENT_TYPE_POINT_CLOUD, domain_); + + this->evaluate_target_field(); + } + + fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Attribute Transfer Nearest"}; + signature.single_input("Position"); + signature.single_output("Attribute", src_field_.cpp_type()); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &positions = params.readonly_single_input(0, "Position"); + GMutableSpan dst = params.uninitialized_single_output_if_required(1, "Attribute"); + + if (!use_mesh_ && !use_points_) { + dst.type().fill_construct_indices(dst.type().default_value(), dst.data(), mask); + return; + } + + const Mesh *mesh = use_mesh_ ? target_.get_mesh_for_read() : nullptr; + const PointCloud *pointcloud = use_points_ ? target_.get_pointcloud_for_read() : nullptr; + + const int tot_samples = mask.min_array_size(); + + Array point_indices; + Array point_distances; + + /* Depending on where what domain the source attribute lives, these indices are either vertex, + * corner, edge or polygon indices. */ + Array mesh_indices; + Array mesh_distances; + + /* If there is a point-cloud, find the closest points. */ + if (use_points_) { + point_indices.reinitialize(tot_samples); + if (use_mesh_) { + point_distances.reinitialize(tot_samples); + } + get_closest_pointcloud_points(*pointcloud, positions, mask, point_indices, point_distances); + } + + /* If there is a mesh, find the closest mesh elements. */ + if (use_mesh_) { + mesh_indices.reinitialize(tot_samples); + if (use_points_) { + mesh_distances.reinitialize(tot_samples); + } + switch (domain_) { + case ATTR_DOMAIN_POINT: { + get_closest_mesh_points(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_EDGE: { + get_closest_mesh_edges(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_FACE: { + get_closest_mesh_polygons(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + case ATTR_DOMAIN_CORNER: { + get_closest_mesh_corners(*mesh, positions, mask, mesh_indices, mesh_distances, {}); + break; + } + default: { + break; + } + } + } + + attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { + using T = decltype(dummy); + if (use_mesh_ && use_points_) { + GVArray_Typed src_mesh{*mesh_data_}; + GVArray_Typed src_point{*point_data_}; + copy_with_indices_and_comparison(*src_mesh, + *src_point, + mesh_distances, + point_distances, + mask, + mesh_indices, + point_indices, + dst.typed()); + } + else if (use_points_) { + GVArray_Typed src_point{*point_data_}; + copy_with_indices(*src_point, mask, point_indices, dst.typed()); + } + else if (use_mesh_) { + GVArray_Typed src_mesh{*mesh_data_}; + copy_with_indices(*src_mesh, mask, mesh_indices, dst.typed()); + } + }); + } + + private: + void evaluate_target_field() + { + if (use_mesh_) { + const MeshComponent &mesh = *target_.get_component_for_read(); + const int domain_size = mesh.attribute_domain_size(domain_); + mesh_context_.emplace(GeometryComponentFieldContext(mesh, domain_)); + mesh_evaluator_ = std::make_unique(*mesh_context_, domain_size); + mesh_evaluator_->add(src_field_); + mesh_evaluator_->evaluate(); + mesh_data_ = &mesh_evaluator_->get_evaluated(0); + } + + if (use_points_) { + const PointCloudComponent &points = *target_.get_component_for_read(); + const int domain_size = points.attribute_domain_size(domain_); + point_context_.emplace(GeometryComponentFieldContext(points, domain_)); + point_evaluator_ = std::make_unique(*point_context_, domain_size); + point_evaluator_->add(src_field_); + point_evaluator_->evaluate(); + point_data_ = &point_evaluator_->get_evaluated(0); + } + } +}; + +static const GeometryComponent *find_best_match_component(const GeometrySet &geometry, + const GeometryComponentType type, + const AttributeDomain domain) +{ + /* Prefer transferring from the same component type, if it exists. */ + if (component_is_available(geometry, type, domain)) { + return geometry.get_component_for_read(type); + } + + /* If there is no component of the same type, choose the other component based on a consistent + * order, rather than some more complicated heuristic. This is the same order visible in the + * spreadsheet and used in the ray-cast node. */ + static const Array supported_types = { + GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_POINT_CLOUD, GEO_COMPONENT_TYPE_CURVE}; + for (const GeometryComponentType src_type : supported_types) { + if (component_is_available(geometry, src_type, domain)) { + return geometry.get_component_for_read(src_type); + } + } + + return nullptr; +} + +/** + * Use a #FieldInput because it's necessary to know the field context in order to choose the + * corresponding component type from the input geometry, and only a #FieldInput receives the + * evaluation context to provide its data. + * + * The index-based transfer theoretically does not need realized data when there is only one + * instance geometry set in the target. A future optimization could be removing that limitation + * internally. + */ +class IndexTransferFieldInput : public FieldInput { + GeometrySet src_geometry_; + GField src_field_; + Field index_field_; + AttributeDomain domain_; + + public: + IndexTransferFieldInput(GeometrySet geometry, + GField src_field, + Field index_field, + const AttributeDomain domain) + : FieldInput(src_field.cpp_type(), "Attribute Transfer node"), + src_geometry_(std::move(geometry)), + src_field_(std::move(src_field)), + index_field_(std::move(index_field)), + domain_(domain) + { + src_geometry_.ensure_owns_direct_data(); + category_ = Category::Generated; + } + + const GVArray *get_varray_for_context(const FieldContext &context, + const IndexMask mask, + ResourceScope &scope) const final + { + const GeometryComponentFieldContext *geometry_context = + dynamic_cast(&context); + if (geometry_context == nullptr) { + return nullptr; + } + + FieldEvaluator index_evaluator{*geometry_context, &mask}; + index_evaluator.add(index_field_); + index_evaluator.evaluate(); + const VArray &index_varray = index_evaluator.get_evaluated(0); + /* The index virtual array is expected to be a span, since transferring the same index for + * every element is not very useful. */ + VArray_Span indices{index_varray}; + + const GeometryComponent *component = find_best_match_component( + src_geometry_, geometry_context->geometry_component().type(), domain_); + if (component == nullptr) { + return nullptr; + } + + GeometryComponentFieldContext target_context{*component, domain_}; + /* A potential improvement is to only copy the necessary values + * based on the indices retrieved from the index input field. */ + FieldEvaluator target_evaluator{target_context, component->attribute_domain_size(domain_)}; + target_evaluator.add(src_field_); + target_evaluator.evaluate(); + const GVArray &src_data = target_evaluator.get_evaluated(0); + + GArray dst(src_field_.cpp_type(), mask.min_array_size()); + + attribute_math::convert_to_static_type(src_data.type(), [&](auto dummy) { + using T = decltype(dummy); + GVArray_Typed src{src_data}; + copy_with_indices_clamped(*src, mask, indices, dst.as_mutable_span().typed()); + }); + + return &scope.construct(std::move(dst)); + } +}; + +static GField get_input_attribute_field(GeoNodeExecParams ¶ms, const CustomDataType data_type) +{ + switch (data_type) { + case CD_PROP_FLOAT: + return params.extract_input>("Attribute_001"); + case CD_PROP_FLOAT3: + return params.extract_input>("Attribute"); + case CD_PROP_COLOR: + return params.extract_input>("Attribute_002"); + case CD_PROP_BOOL: + return params.extract_input>("Attribute_003"); + case CD_PROP_INT32: + return params.extract_input>("Attribute_004"); + default: + BLI_assert_unreachable(); + } + return {}; +} + +static void output_attribute_field(GeoNodeExecParams ¶ms, GField field) +{ + switch (bke::cpp_type_to_custom_data_type(field.cpp_type())) { + case CD_PROP_FLOAT: { + params.set_output("Attribute_001", Field(field)); + break; + } + case CD_PROP_FLOAT3: { + params.set_output("Attribute", Field(field)); + break; + } + case CD_PROP_COLOR: { + params.set_output("Attribute_002", Field(field)); + break; + } + case CD_PROP_BOOL: { + params.set_output("Attribute_003", Field(field)); + break; + } + case CD_PROP_INT32: { + params.set_output("Attribute_004", Field(field)); + break; + } + default: + break; + } +} + +static void geo_node_transfer_attribute_exec(GeoNodeExecParams params) +{ + GeometrySet geometry = params.extract_input("Target"); + const bNode &node = params.node(); + const NodeGeometryTransferAttribute &data = *(const NodeGeometryTransferAttribute *)node.storage; + const GeometryNodeAttributeTransferMode mapping = (GeometryNodeAttributeTransferMode)data.mode; + const CustomDataType data_type = static_cast(data.data_type); + const AttributeDomain domain = static_cast(data.domain); + + GField field = get_input_attribute_field(params, data_type); + + auto return_default = [&]() { + attribute_math::convert_to_static_type(data_type, [&](auto dummy) { + using T = decltype(dummy); + output_attribute_field(params, fn::make_constant_field(T())); + }); + }; + + /* Since the instances are not used, there is no point in keeping + * a reference to them while the field is passed around. */ + geometry.remove(GEO_COMPONENT_TYPE_INSTANCES); + + GField output_field; + switch (mapping) { + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST_FACE_INTERPOLATED: { + const Mesh *mesh = geometry.get_mesh_for_read(); + if (mesh == nullptr) { + if (!geometry.is_empty()) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target geometry must contain a mesh")); + } + return return_default(); + } + if (mesh->totpoly == 0) { + /* Don't add a warning for empty meshes. */ + if (mesh->totvert != 0) { + params.error_message_add(NodeWarningType::Error, + TIP_("The target mesh must have faces")); + } + return return_default(); + } + auto fn = std::make_unique(std::move(geometry), + std::move(field)); + auto op = std::make_shared( + FieldOperation(std::move(fn), {params.extract_input>("Source Position")})); + output_field = GField(std::move(op)); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_NEAREST: { + if (geometry.has_curve() && !geometry.has_mesh() && !geometry.has_pointcloud()) { + return return_default(); + } + auto fn = std::make_unique( + std::move(geometry), std::move(field), domain); + auto op = std::make_shared( + FieldOperation(std::move(fn), {params.extract_input>("Source Position")})); + output_field = GField(std::move(op)); + break; + } + case GEO_NODE_ATTRIBUTE_TRANSFER_INDEX: { + Field indices = params.extract_input>("Index"); + std::shared_ptr input = std::make_shared( + std::move(geometry), std::move(field), std::move(indices), domain); + output_field = GField(std::move(input)); + break; + } + } + + output_attribute_field(params, std::move(output_field)); +} + +} // namespace blender::nodes + +void register_node_type_geo_transfer_attribute() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_TRANSFER_ATTRIBUTE, "Transfer Attribute", NODE_CLASS_ATTRIBUTE, 0); + node_type_init(&ntype, blender::nodes::geo_node_transfer_attribute_init); + node_type_update(&ntype, blender::nodes::geo_node_transfer_attribute_update); + node_type_storage(&ntype, + "NodeGeometryTransferAttribute", + node_free_standard_storage, + node_copy_standard_storage); + ntype.declare = blender::nodes::geo_node_transfer_attribute_declare; + ntype.geometry_node_execute = blender::nodes::geo_node_transfer_attribute_exec; + ntype.draw_buttons = blender::nodes::geo_node_transfer_attribute_layout; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_transform.cc b/source/blender/nodes/geometry/nodes/node_geo_transform.cc index d5eb067cad0..2c55a255b5d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_transform.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_transform.cc @@ -25,6 +25,7 @@ #include "DNA_volume_types.h" #include "BKE_mesh.h" +#include "BKE_pointcloud.h" #include "BKE_spline.hh" #include "BKE_volume.h" @@ -36,11 +37,11 @@ namespace blender::nodes { static void geo_node_transform_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Translation").subtype(PROP_TRANSLATION); - b.add_input("Rotation").subtype(PROP_EULER); - b.add_input("Scale").default_value({1, 1, 1}).subtype(PROP_XYZ); - b.add_output("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Translation")).subtype(PROP_TRANSLATION); + b.add_input(N_("Rotation")).subtype(PROP_EULER); + b.add_input(N_("Scale")).default_value({1, 1, 1}).subtype(PROP_XYZ); + b.add_output(N_("Geometry")); } static bool use_translate(const float3 rotation, const float3 scale) @@ -55,112 +56,142 @@ static bool use_translate(const float3 rotation, const float3 scale) return true; } -void transform_mesh(Mesh *mesh, +static void translate_mesh(Mesh &mesh, const float3 translation) +{ + if (!translation.is_zero()) { + BKE_mesh_translate(&mesh, translation, false); + } +} + +static void transform_mesh(Mesh &mesh, const float4x4 &transform) +{ + BKE_mesh_transform(&mesh, transform.values, false); + BKE_mesh_normals_tag_dirty(&mesh); +} + +void transform_mesh(Mesh &mesh, const float3 translation, const float3 rotation, const float3 scale) { - /* Use only translation if rotation and scale are zero. */ - if (use_translate(rotation, scale)) { - if (!translation.is_zero()) { - BKE_mesh_translate(mesh, translation, false); - } - } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - BKE_mesh_transform(mesh, matrix.values, false); - BKE_mesh_normals_tag_dirty(mesh); - } + const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); + transform_mesh(mesh, matrix); } -static void transform_pointcloud(PointCloud *pointcloud, - const float3 translation, - const float3 rotation, - const float3 scale) +static void translate_pointcloud(PointCloud &pointcloud, const float3 translation) { - /* Use only translation if rotation and scale don't apply. */ - if (use_translate(rotation, scale)) { - for (const int i : IndexRange(pointcloud->totpoint)) { - add_v3_v3(pointcloud->co[i], translation); - } - } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - for (const int i : IndexRange(pointcloud->totpoint)) { - float3 &co = *(float3 *)pointcloud->co[i]; - co = matrix * co; - } + CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); + BKE_pointcloud_update_customdata_pointers(&pointcloud); + for (const int i : IndexRange(pointcloud.totpoint)) { + add_v3_v3(pointcloud.co[i], translation); } } -static void transform_instances(InstancesComponent &instances, - const float3 translation, - const float3 rotation, - const float3 scale) +static void transform_pointcloud(PointCloud &pointcloud, const float4x4 &transform) +{ + CustomData_duplicate_referenced_layer(&pointcloud.pdata, CD_PROP_FLOAT3, pointcloud.totpoint); + BKE_pointcloud_update_customdata_pointers(&pointcloud); + for (const int i : IndexRange(pointcloud.totpoint)) { + float3 &co = *(float3 *)pointcloud.co[i]; + co = transform * co; + } +} + +static void translate_instances(InstancesComponent &instances, const float3 translation) { MutableSpan transforms = instances.instance_transforms(); - - /* Use only translation if rotation and scale don't apply. */ - if (use_translate(rotation, scale)) { - for (float4x4 &transform : transforms) { - add_v3_v3(transform.ptr()[3], translation); - } - } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - for (float4x4 &transform : transforms) { - transform = matrix * transform; - } + for (float4x4 &transform : transforms) { + add_v3_v3(transform.ptr()[3], translation); } } -static void transform_volume(Volume *volume, - const float3 translation, - const float3 rotation, - const float3 scale, - GeoNodeExecParams ¶ms) +static void transform_instances(InstancesComponent &instances, const float4x4 &transform) +{ + MutableSpan instance_transforms = instances.instance_transforms(); + for (float4x4 &instance_transform : instance_transforms) { + instance_transform = transform * instance_transform; + } +} + +static void transform_volume(Volume &volume, const float4x4 &transform, const Depsgraph &depsgraph) { #ifdef WITH_OPENVDB /* Scaling an axis to zero is not supported for volumes. */ + const float3 translation = transform.translation(); + const float3 rotation = transform.to_euler(); + const float3 scale = transform.scale(); const float3 limited_scale = { (scale.x == 0.0f) ? FLT_EPSILON : scale.x, (scale.y == 0.0f) ? FLT_EPSILON : scale.y, (scale.z == 0.0f) ? FLT_EPSILON : scale.z, }; + const float4x4 scale_limited_transform = float4x4::from_loc_eul_scale( + translation, rotation, limited_scale); - const Main *bmain = DEG_get_bmain(params.depsgraph()); - BKE_volume_load(volume, bmain); - - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, limited_scale); + const Main *bmain = DEG_get_bmain(&depsgraph); + BKE_volume_load(&volume, bmain); openvdb::Mat4s vdb_matrix; - memcpy(vdb_matrix.asPointer(), matrix, sizeof(float[4][4])); + memcpy(vdb_matrix.asPointer(), &scale_limited_transform, sizeof(float[4][4])); openvdb::Mat4d vdb_matrix_d{vdb_matrix}; - const int num_grids = BKE_volume_num_grids(volume); + const int num_grids = BKE_volume_num_grids(&volume); for (const int i : IndexRange(num_grids)) { - VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(volume, i); + VolumeGrid *volume_grid = BKE_volume_grid_get_for_write(&volume, i); - openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(volume, volume_grid, false); + openvdb::GridBase::Ptr grid = BKE_volume_grid_openvdb_for_write(&volume, volume_grid, false); openvdb::math::Transform &grid_transform = grid->transform(); grid_transform.postMult(vdb_matrix_d); } #else - UNUSED_VARS(volume, translation, rotation, scale, params); + UNUSED_VARS(volume, transform, depsgraph); #endif } -static void transform_curve(CurveEval &curve, - const float3 translation, - const float3 rotation, - const float3 scale) +static void translate_volume(Volume &volume, const float3 translation, const Depsgraph &depsgraph) { - if (use_translate(rotation, scale)) { - curve.translate(translation); + transform_volume(volume, float4x4::from_location(translation), depsgraph); +} + +void transform_geometry_set(GeometrySet &geometry, + const float4x4 &transform, + const Depsgraph &depsgraph) +{ + if (CurveEval *curve = geometry.get_curve_for_write()) { + curve->transform(transform); } - else { - const float4x4 matrix = float4x4::from_loc_eul_scale(translation, rotation, scale); - curve.transform(matrix); + if (Mesh *mesh = geometry.get_mesh_for_write()) { + transform_mesh(*mesh, transform); + } + if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) { + transform_pointcloud(*pointcloud, transform); + } + if (Volume *volume = geometry.get_volume_for_write()) { + transform_volume(*volume, transform, depsgraph); + } + if (geometry.has_instances()) { + transform_instances(geometry.get_component_for_write(), transform); + } +} + +static void translate_geometry_set(GeometrySet &geometry, + const float3 translation, + const Depsgraph &depsgraph) +{ + if (CurveEval *curve = geometry.get_curve_for_write()) { + curve->translate(translation); + } + if (Mesh *mesh = geometry.get_mesh_for_write()) { + translate_mesh(*mesh, translation); + } + if (PointCloud *pointcloud = geometry.get_pointcloud_for_write()) { + translate_pointcloud(*pointcloud, translation); + } + if (Volume *volume = geometry.get_volume_for_write()) { + translate_volume(*volume, translation, depsgraph); + } + if (geometry.has_instances()) { + translate_instances(geometry.get_component_for_write(), translation); } } @@ -171,25 +202,14 @@ static void geo_node_transform_exec(GeoNodeExecParams params) const float3 rotation = params.extract_input("Rotation"); const float3 scale = params.extract_input("Scale"); - if (geometry_set.has_mesh()) { - Mesh *mesh = geometry_set.get_mesh_for_write(); - transform_mesh(mesh, translation, rotation, scale); + /* Use only translation if rotation and scale don't apply. */ + if (use_translate(rotation, scale)) { + translate_geometry_set(geometry_set, translation, *params.depsgraph()); } - if (geometry_set.has_pointcloud()) { - PointCloud *pointcloud = geometry_set.get_pointcloud_for_write(); - transform_pointcloud(pointcloud, translation, rotation, scale); - } - if (geometry_set.has_instances()) { - InstancesComponent &instances = geometry_set.get_component_for_write(); - transform_instances(instances, translation, rotation, scale); - } - if (geometry_set.has_volume()) { - Volume *volume = geometry_set.get_volume_for_write(); - transform_volume(volume, translation, rotation, scale, params); - } - if (geometry_set.has_curve()) { - CurveEval *curve = geometry_set.get_curve_for_write(); - transform_curve(*curve, translation, rotation, scale); + else { + transform_geometry_set(geometry_set, + float4x4::from_loc_eul_scale(translation, rotation, scale), + *params.depsgraph()); } params.set_output("Geometry", std::move(geometry_set)); diff --git a/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc new file mode 100644 index 00000000000..fa05d858a07 --- /dev/null +++ b/source/blender/nodes/geometry/nodes/node_geo_translate_instances.cc @@ -0,0 +1,84 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_task.hh" + +#include "node_geometry_util.hh" + +namespace blender::nodes { + +static void geo_node_translate_instances_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Instances")).only_instances(); + b.add_input(N_("Selection")).default_value(true).hide_value().supports_field(); + b.add_input(N_("Translation")).subtype(PROP_TRANSLATION).supports_field(); + b.add_input(N_("Local Space")).default_value(true).supports_field(); + b.add_output(N_("Instances")); +}; + +static void translate_instances(GeoNodeExecParams ¶ms, InstancesComponent &instances_component) +{ + GeometryComponentFieldContext field_context{instances_component, ATTR_DOMAIN_POINT}; + + fn::FieldEvaluator selection_evaluator{field_context, instances_component.instances_amount()}; + selection_evaluator.add(params.extract_input>("Selection")); + selection_evaluator.evaluate(); + const IndexMask selection = selection_evaluator.get_evaluated_as_mask(0); + + fn::FieldEvaluator transforms_evaluator{field_context, &selection}; + transforms_evaluator.add(params.extract_input>("Translation")); + transforms_evaluator.add(params.extract_input>("Local Space")); + transforms_evaluator.evaluate(); + const VArray &translations = transforms_evaluator.get_evaluated(0); + const VArray &local_spaces = transforms_evaluator.get_evaluated(1); + + MutableSpan instance_transforms = instances_component.instance_transforms(); + + threading::parallel_for(selection.index_range(), 1024, [&](IndexRange range) { + for (const int i_selection : range) { + const int i = selection[i_selection]; + if (local_spaces[i]) { + instance_transforms[i] *= float4x4::from_location(translations[i]); + } + else { + add_v3_v3(instance_transforms[i].values[3], translations[i]); + } + } + }); +} + +static void geo_node_translate_instances_exec(GeoNodeExecParams params) +{ + GeometrySet geometry_set = params.extract_input("Instances"); + if (geometry_set.has_instances()) { + InstancesComponent &instances = geometry_set.get_component_for_write(); + translate_instances(params, instances); + } + params.set_output("Instances", std::move(geometry_set)); +} + +} // namespace blender::nodes + +void register_node_type_geo_translate_instances() +{ + static bNodeType ntype; + + geo_node_type_base( + &ntype, GEO_NODE_TRANSLATE_INSTANCES, "Translate Instances", NODE_CLASS_GEOMETRY, 0); + ntype.geometry_node_execute = blender::nodes::geo_node_translate_instances_exec; + ntype.declare = blender::nodes::geo_node_translate_instances_declare; + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc index 8886c7194db..c869846e1f8 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_triangulate.cc @@ -31,9 +31,9 @@ namespace blender::nodes { static void geo_node_triangulate_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Minimum Vertices").default_value(4).min(4).max(10000); - b.add_output("Geometry"); + b.add_input(N_("Mesh")).supported_type(GEO_COMPONENT_TYPE_MESH); + b.add_input(N_("Minimum Vertices")).default_value(4).min(4).max(10000); + b.add_output(N_("Mesh")); } static void geo_node_triangulate_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -50,7 +50,7 @@ static void geo_triangulate_init(bNodeTree *UNUSED(ntree), bNode *node) static void geo_node_triangulate_exec(GeoNodeExecParams params) { - GeometrySet geometry_set = params.extract_input("Geometry"); + GeometrySet geometry_set = params.extract_input("Mesh"); const int min_vertices = std::max(params.extract_input("Minimum Vertices"), 4); GeometryNodeTriangulateQuads quad_method = static_cast( @@ -58,16 +58,16 @@ static void geo_node_triangulate_exec(GeoNodeExecParams params) GeometryNodeTriangulateNGons ngon_method = static_cast( params.node().custom2); - geometry_set = geometry_set_realize_instances(geometry_set); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + /* #triangulate_mesh might modify the input mesh currently. */ + Mesh *mesh_in = geometry_set.get_mesh_for_write(); + if (mesh_in != nullptr) { + Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0); + geometry_set.replace_mesh(mesh_out); + } + }); - /* #triangulate_mesh might modify the input mesh currently. */ - Mesh *mesh_in = geometry_set.get_mesh_for_write(); - if (mesh_in != nullptr) { - Mesh *mesh_out = triangulate_mesh(mesh_in, quad_method, ngon_method, min_vertices, 0); - geometry_set.replace_mesh(mesh_out); - } - - params.set_output("Geometry", std::move(geometry_set)); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes diff --git a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc index 3331962341f..194d1a751ed 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_viewer.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_viewer.cc @@ -14,13 +14,69 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include "UI_interface.h" +#include "UI_resources.h" + #include "node_geometry_util.hh" namespace blender::nodes { static void geo_node_viewer_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); + b.add_input(N_("Geometry")); + b.add_input(N_("Value")).supports_field().hide_value(); + b.add_input(N_("Value"), "Value_001").supports_field().hide_value(); + b.add_input(N_("Value"), "Value_002").supports_field().hide_value(); + b.add_input(N_("Value"), "Value_003").supports_field().hide_value(); + b.add_input(N_("Value"), "Value_004").supports_field().hide_value(); } + +static void geo_node_viewer_init(bNodeTree *UNUSED(tree), bNode *node) +{ + NodeGeometryViewer *data = (NodeGeometryViewer *)MEM_callocN(sizeof(NodeGeometryViewer), + __func__); + data->data_type = CD_PROP_FLOAT; + + node->storage = data; +} + +static void geo_node_viewer_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "data_type", 0, "", ICON_NONE); +} + +static eNodeSocketDatatype custom_data_type_to_socket_type(const CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return SOCK_FLOAT; + case CD_PROP_INT32: + return SOCK_INT; + case CD_PROP_FLOAT3: + return SOCK_VECTOR; + case CD_PROP_BOOL: + return SOCK_BOOLEAN; + case CD_PROP_COLOR: + return SOCK_RGBA; + default: + BLI_assert_unreachable(); + return SOCK_FLOAT; + } +} + +static void geo_node_viewer_update(bNodeTree *UNUSED(ntree), bNode *node) +{ + const NodeGeometryViewer &storage = *(const NodeGeometryViewer *)node->storage; + const CustomDataType data_type = static_cast(storage.data_type); + const eNodeSocketDatatype socket_type = custom_data_type_to_socket_type(data_type); + + LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) { + if (socket->type == SOCK_GEOMETRY) { + continue; + } + nodeSetSocketAvailability(socket, socket->type == socket_type); + } +} + } // namespace blender::nodes void register_node_type_geo_viewer() @@ -28,6 +84,11 @@ void register_node_type_geo_viewer() static bNodeType ntype; geo_node_type_base(&ntype, GEO_NODE_VIEWER, "Viewer", NODE_CLASS_OUTPUT, 0); + node_type_storage( + &ntype, "NodeGeometryViewer", node_free_standard_storage, node_copy_standard_storage); + node_type_update(&ntype, blender::nodes::geo_node_viewer_update); + node_type_init(&ntype, blender::nodes::geo_node_viewer_init); ntype.declare = blender::nodes::geo_node_viewer_declare; + ntype.draw_buttons_ex = blender::nodes::geo_node_viewer_layout; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc index 229a35e0007..416d502dc59 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_volume_to_mesh.cc @@ -39,13 +39,12 @@ namespace blender::nodes { static void geo_node_volume_to_mesh_declare(NodeDeclarationBuilder &b) { - b.add_input("Geometry"); - b.add_input("Density"); - b.add_input("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); - b.add_input("Voxel Amount").default_value(64.0f).min(0.0f); - b.add_input("Threshold").default_value(0.1f).min(0.0f); - b.add_input("Adaptivity").min(0.0f).max(1.0f).subtype(PROP_FACTOR); - b.add_output("Geometry"); + b.add_input(N_("Volume")).supported_type(GEO_COMPONENT_TYPE_VOLUME); + b.add_input(N_("Voxel Size")).default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE); + b.add_input(N_("Voxel Amount")).default_value(64.0f).min(0.0f); + b.add_input(N_("Threshold")).default_value(0.1f).min(0.0f); + b.add_input(N_("Adaptivity")).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Mesh")); } static void geo_node_volume_to_mesh_layout(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -60,11 +59,6 @@ static void geo_node_volume_to_mesh_init(bNodeTree *UNUSED(ntree), bNode *node) NodeGeometryVolumeToMesh *data = (NodeGeometryVolumeToMesh *)MEM_callocN( sizeof(NodeGeometryVolumeToMesh), __func__); data->resolution_mode = VOLUME_TO_MESH_RESOLUTION_MODE_GRID; - - bNodeSocket *grid_socket = nodeFindSocket(node, SOCK_IN, "Density"); - bNodeSocketValueString *grid_socket_value = (bNodeSocketValueString *)grid_socket->default_value; - STRNCPY(grid_socket_value->value, "density"); - node->storage = data; } @@ -82,75 +76,120 @@ static void geo_node_volume_to_mesh_update(bNodeTree *UNUSED(ntree), bNode *node #ifdef WITH_OPENVDB -static void create_mesh_from_volume(GeometrySet &geometry_set_in, - GeometrySet &geometry_set_out, - GeoNodeExecParams ¶ms) +static bke::VolumeToMeshResolution get_resolution_param(const GeoNodeExecParams ¶ms) { - if (!geometry_set_in.has()) { - return; - } - const NodeGeometryVolumeToMesh &storage = *(const NodeGeometryVolumeToMesh *)params.node().storage; bke::VolumeToMeshResolution resolution; resolution.mode = (VolumeToMeshResolutionMode)storage.resolution_mode; if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_AMOUNT) { - resolution.settings.voxel_amount = params.get_input("Voxel Amount"); - if (resolution.settings.voxel_amount <= 0.0f) { - return; - } + resolution.settings.voxel_amount = std::max(params.get_input("Voxel Amount"), 0.0f); } else if (resolution.mode == VOLUME_TO_MESH_RESOLUTION_MODE_VOXEL_SIZE) { - resolution.settings.voxel_size = params.get_input("Voxel Size"); - if (resolution.settings.voxel_size <= 0.0f) { - return; - } + resolution.settings.voxel_size = std::max(params.get_input("Voxel Size"), 0.0f); } - const VolumeComponent *component = geometry_set_in.get_component_for_read(); - const Volume *volume = component->get_for_read(); + return resolution; +} + +static Mesh *create_mesh_from_volume_grids(Span grids, + const float threshold, + const float adaptivity, + const bke::VolumeToMeshResolution &resolution) +{ + Array mesh_data(grids.size()); + for (const int i : grids.index_range()) { + mesh_data[i] = bke::volume_to_mesh_data(*grids[i], resolution, threshold, adaptivity); + } + + int vert_offset = 0; + int poly_offset = 0; + int loop_offset = 0; + Array vert_offsets(mesh_data.size()); + Array poly_offsets(mesh_data.size()); + Array loop_offsets(mesh_data.size()); + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + vert_offsets[i] = vert_offset; + poly_offsets[i] = poly_offset; + loop_offsets[i] = loop_offset; + vert_offset += data.verts.size(); + poly_offset += (data.tris.size() + data.quads.size()); + loop_offset += (3 * data.tris.size() + 4 * data.quads.size()); + } + + Mesh *mesh = BKE_mesh_new_nomain(vert_offset, 0, 0, loop_offset, poly_offset); + BKE_id_material_eval_ensure_default_slot(&mesh->id); + MutableSpan verts{mesh->mvert, mesh->totvert}; + MutableSpan loops{mesh->mloop, mesh->totloop}; + MutableSpan polys{mesh->mpoly, mesh->totpoly}; + + for (const int i : grids.index_range()) { + const bke::OpenVDBMeshData &data = mesh_data[i]; + bke::fill_mesh_from_openvdb_data(data.verts, + data.tris, + data.quads, + vert_offsets[i], + poly_offsets[i], + loop_offsets[i], + verts, + polys, + loops); + } + + BKE_mesh_calc_edges(mesh, false, false); + BKE_mesh_normals_tag_dirty(mesh); + + return mesh; +} + +static Mesh *create_mesh_from_volume(GeometrySet &geometry_set, GeoNodeExecParams ¶ms) +{ + const Volume *volume = geometry_set.get_volume_for_read(); if (volume == nullptr) { - return; + return nullptr; } + const bke::VolumeToMeshResolution resolution = get_resolution_param(params); const Main *bmain = DEG_get_bmain(params.depsgraph()); BKE_volume_load(volume, bmain); - const std::string grid_name = params.get_input("Density"); - const VolumeGrid *volume_grid = BKE_volume_grid_find_for_read(volume, grid_name.c_str()); - if (volume_grid == nullptr) { - return; + Vector grids; + for (const int i : IndexRange(BKE_volume_num_grids(volume))) { + const VolumeGrid *volume_grid = BKE_volume_grid_get_for_read(volume, i); + openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); + grids.append(std::move(grid)); } - float threshold = params.get_input("Threshold"); - float adaptivity = params.get_input("Adaptivity"); - - const openvdb::GridBase::ConstPtr grid = BKE_volume_grid_openvdb_for_read(volume, volume_grid); - Mesh *mesh = bke::volume_to_mesh(*grid, resolution, threshold, adaptivity); - if (mesh == nullptr) { - return; + if (grids.is_empty()) { + return nullptr; } - BKE_id_material_eval_ensure_default_slot(&mesh->id); - MeshComponent &dst_component = geometry_set_out.get_component_for_write(); - dst_component.replace(mesh); + + return create_mesh_from_volume_grids(grids, + params.get_input("Threshold"), + params.get_input("Adaptivity"), + resolution); } #endif /* WITH_OPENVDB */ static void geo_node_volume_to_mesh_exec(GeoNodeExecParams params) { - GeometrySet geometry_set_in = params.extract_input("Geometry"); - GeometrySet geometry_set_out; + GeometrySet geometry_set = params.extract_input("Volume"); #ifdef WITH_OPENVDB - create_mesh_from_volume(geometry_set_in, geometry_set_out, params); + geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { + Mesh *mesh = create_mesh_from_volume(geometry_set, params); + geometry_set.replace_mesh(mesh); + geometry_set.keep_only({GEO_COMPONENT_TYPE_MESH, GEO_COMPONENT_TYPE_INSTANCES}); + }); #else params.error_message_add(NodeWarningType::Error, TIP_("Disabled, Blender was compiled without OpenVDB")); #endif - params.set_output("Geometry", geometry_set_out); + params.set_output("Mesh", std::move(geometry_set)); } } // namespace blender::nodes @@ -163,7 +202,7 @@ void register_node_type_geo_volume_to_mesh() ntype.declare = blender::nodes::geo_node_volume_to_mesh_declare; node_type_storage( &ntype, "NodeGeometryVolumeToMesh", node_free_standard_storage, node_copy_standard_storage); - node_type_size(&ntype, 200, 120, 700); + node_type_size(&ntype, 170, 120, 700); node_type_init(&ntype, blender::nodes::geo_node_volume_to_mesh_init); node_type_update(&ntype, blender::nodes::geo_node_volume_to_mesh_update); ntype.geometry_node_execute = blender::nodes::geo_node_volume_to_mesh_exec; diff --git a/source/blender/nodes/intern/derived_node_tree.cc b/source/blender/nodes/intern/derived_node_tree.cc index f7279cf7524..14d6c77299b 100644 --- a/source/blender/nodes/intern/derived_node_tree.cc +++ b/source/blender/nodes/intern/derived_node_tree.cc @@ -231,45 +231,90 @@ void DInputSocket::foreach_origin_socket(FunctionRef origin_fn) c /* Calls `target_fn` for every "real" target socket. "Real" means that reroutes, muted nodes * and node groups are handled by this function. Target sockets are on the nodes that use the value - * from this socket. The `skipped_fn` function is called for sockets that have been skipped during - * the search for target sockets (e.g. reroutes). */ -void DOutputSocket::foreach_target_socket(FunctionRef target_fn, - FunctionRef skipped_fn) const + * from this socket. */ +void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn) const { - for (const SocketRef *skipped_socket : socket_ref_->logically_linked_skipped_sockets()) { - skipped_fn.call_safe({context_, skipped_socket}); - } - for (const InputSocketRef *linked_socket : socket_ref_->as_output().logically_linked_sockets()) { - const NodeRef &linked_node = linked_socket->node(); - DInputSocket linked_dsocket{context_, linked_socket}; + TargetSocketPathInfo path_info; + this->foreach_target_socket(target_fn, path_info); +} - if (linked_node.is_group_output_node()) { +void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn, + TargetSocketPathInfo &path_info) const +{ + for (const LinkRef *link : socket_ref_->as_output().directly_linked_links()) { + if (link->is_muted()) { + continue; + } + const DInputSocket &linked_socket{context_, &link->to()}; + if (!linked_socket->is_available()) { + continue; + } + const DNode linked_node = linked_socket.node(); + if (linked_node->is_reroute_node()) { + const DInputSocket reroute_input = linked_socket; + const DOutputSocket reroute_output = linked_node.output(0); + path_info.sockets.append(reroute_input); + path_info.sockets.append(reroute_output); + reroute_output.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); + } + else if (linked_node->is_muted()) { + for (const InternalLinkRef *internal_link : linked_node->internal_links()) { + if (&internal_link->from() != linked_socket.socket_ref()) { + continue; + } + /* The internal link only forwards the first incoming link. */ + if (linked_socket->is_multi_input_socket()) { + if (linked_socket->directly_linked_links()[0] != link) { + continue; + } + } + const DInputSocket mute_input = linked_socket; + const DOutputSocket mute_output{context_, &internal_link->to()}; + path_info.sockets.append(mute_input); + path_info.sockets.append(mute_output); + mute_output.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); + break; + } + } + else if (linked_node->is_group_output_node()) { if (context_->is_root()) { /* This is a group output in the root node group. */ - target_fn(linked_dsocket); + path_info.sockets.append(linked_socket); + target_fn(linked_socket, path_info); + path_info.sockets.pop_last(); } else { /* Follow the links going out of the group node in the parent node group. */ - DOutputSocket socket_in_parent_group = - linked_dsocket.get_corresponding_group_node_output(); - skipped_fn.call_safe(linked_dsocket); - skipped_fn.call_safe(socket_in_parent_group); - socket_in_parent_group.foreach_target_socket(target_fn, skipped_fn); + const DOutputSocket socket_in_parent_group = + linked_socket.get_corresponding_group_node_output(); + path_info.sockets.append(linked_socket); + path_info.sockets.append(socket_in_parent_group); + socket_in_parent_group.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); + path_info.sockets.pop_last(); } } - else if (linked_node.is_group_node()) { + else if (linked_node->is_group_node()) { /* Follow the links within the nested node group. */ - Vector sockets_in_group = - linked_dsocket.get_corresponding_group_input_sockets(); - skipped_fn.call_safe(linked_dsocket); - for (DOutputSocket socket_in_group : sockets_in_group) { - skipped_fn.call_safe(socket_in_group); - socket_in_group.foreach_target_socket(target_fn, skipped_fn); + path_info.sockets.append(linked_socket); + const Vector sockets_in_group = + linked_socket.get_corresponding_group_input_sockets(); + for (const DOutputSocket &socket_in_group : sockets_in_group) { + path_info.sockets.append(socket_in_group); + socket_in_group.foreach_target_socket(target_fn, path_info); + path_info.sockets.pop_last(); } + path_info.sockets.pop_last(); } else { /* The normal case: just use the linked input socket as target. */ - target_fn(linked_dsocket); + path_info.sockets.append(linked_socket); + target_fn(linked_socket, path_info); + path_info.sockets.pop_last(); } } } diff --git a/source/blender/nodes/intern/extern_implementations.cc b/source/blender/nodes/intern/extern_implementations.cc new file mode 100644 index 00000000000..35de319f20b --- /dev/null +++ b/source/blender/nodes/intern/extern_implementations.cc @@ -0,0 +1,35 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" + +namespace blender::nodes { +#define MAKE_EXTERN_SOCKET_IMPLEMENTATION(TYPE) \ + template class SocketDeclarationBuilder; \ + template TYPE::Builder &NodeDeclarationBuilder::add_input(StringRef, StringRef); \ + template TYPE::Builder &NodeDeclarationBuilder::add_output(StringRef, StringRef); + +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Float) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Int) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Vector) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Bool) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Color) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::String) +MAKE_EXTERN_SOCKET_IMPLEMENTATION(decl::Geometry) + +#undef MAKE_EXTERN_SOCKET_IMPLEMENTATION +} // namespace blender::nodes diff --git a/source/blender/nodes/intern/geometry_nodes_eval_log.cc b/source/blender/nodes/intern/geometry_nodes_eval_log.cc index 3b3b643d0ae..ddd3c991518 100644 --- a/source/blender/nodes/intern/geometry_nodes_eval_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_eval_log.cc @@ -21,11 +21,20 @@ #include "DNA_modifier_types.h" #include "DNA_space_types.h" +#include "FN_field_cpp_type.hh" + +#include "BLT_translation.h" + namespace blender::nodes::geometry_nodes_eval_log { using fn::CPPType; +using fn::FieldCPPType; +using fn::FieldInput; +using fn::GField; ModifierLog::ModifierLog(GeoLogger &logger) + : input_geometry_log_(std::move(logger.input_geometry_log_)), + output_geometry_log_(std::move(logger.output_geometry_log_)) { root_tree_logs_ = allocator_.construct(); @@ -106,6 +115,15 @@ void ModifierLog::foreach_node_log(FunctionRef fn) const } } +const GeometryValueLog *ModifierLog::input_geometry_log() const +{ + return input_geometry_log_.get(); +} +const GeometryValueLog *ModifierLog::output_geometry_log() const +{ + return output_geometry_log_.get(); +} + const NodeLog *TreeLog::lookup_node_log(StringRef node_name) const { const destruct_ptr *node_log = node_logs_.lookup_ptr_as(node_name); @@ -157,17 +175,58 @@ const SocketLog *NodeLog::lookup_socket_log(const bNode &node, const bNodeSocket return this->lookup_socket_log((eNodeSocketInOut)socket.in_out, index); } +GFieldValueLog::GFieldValueLog(fn::GField field, bool log_full_field) : type_(field.cpp_type()) +{ + Set> field_inputs_set; + field.node().foreach_field_input( + [&](const FieldInput &field_input) { field_inputs_set.add(field_input); }); + + Vector> field_inputs; + field_inputs.extend(field_inputs_set.begin(), field_inputs_set.end()); + + std::sort( + field_inputs.begin(), field_inputs.end(), [](const FieldInput &a, const FieldInput &b) { + const int index_a = (int)a.category(); + const int index_b = (int)b.category(); + if (index_a == index_b) { + return a.socket_inspection_name().size() < b.socket_inspection_name().size(); + } + return index_a < index_b; + }); + + for (const FieldInput &field_input : field_inputs) { + input_tooltips_.append(field_input.socket_inspection_name()); + } + + if (log_full_field) { + field_ = std::move(field); + } +} + GeometryValueLog::GeometryValueLog(const GeometrySet &geometry_set, bool log_full_geometry) { - bke::geometry_set_instances_attribute_foreach( - geometry_set, - [&](const bke::AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { - if (attribute_id.is_named()) { + static std::array all_component_types = {GEO_COMPONENT_TYPE_CURVE, + GEO_COMPONENT_TYPE_INSTANCES, + GEO_COMPONENT_TYPE_MESH, + GEO_COMPONENT_TYPE_POINT_CLOUD, + GEO_COMPONENT_TYPE_VOLUME}; + + /* Keep track handled attribute names to make sure that we do not return the same name twice. + * Currently #GeometrySet::attribute_foreach does not do that. Note that this will merge + * attributes with the same name but different domains or data types on separate components. */ + Set names; + + geometry_set.attribute_foreach( + all_component_types, + true, + [&](const bke::AttributeIDRef &attribute_id, + const AttributeMetaData &meta_data, + const GeometryComponent &UNUSED(component)) { + if (attribute_id.is_named() && names.add(attribute_id.name())) { this->attributes_.append({attribute_id.name(), meta_data.domain, meta_data.data_type}); } - return true; - }, - 8); + }); + for (const GeometryComponent *component : geometry_set.get_components_for_read()) { component_types_.append(component->type()); switch (component->type()) { @@ -347,7 +406,7 @@ void LocalGeoLogger::log_value_for_sockets(Span sockets, GPointer value if (type.is()) { bool log_full_geometry = false; for (const DSocket &socket : sockets) { - if (main_logger_->log_full_geometry_sockets_.contains(socket)) { + if (main_logger_->log_full_sockets_.contains(socket)) { log_full_geometry = true; break; } @@ -358,6 +417,26 @@ void LocalGeoLogger::log_value_for_sockets(Span sockets, GPointer value geometry_set, log_full_geometry); values_.append({copied_sockets, std::move(value_log)}); } + else if (const FieldCPPType *field_type = dynamic_cast(&type)) { + GField field = field_type->get_gfield(value.get()); + bool log_full_field = false; + if (!field.node().depends_on_input()) { + /* Always log constant fields so that their value can be shown in socket inspection. + * In the future we can also evaluate the field here and only store the value. */ + log_full_field = true; + } + if (!log_full_field) { + for (const DSocket &socket : sockets) { + if (main_logger_->log_full_sockets_.contains(socket)) { + log_full_field = true; + break; + } + } + } + destruct_ptr value_log = allocator_->construct( + std::move(field), log_full_field); + values_.append({copied_sockets, std::move(value_log)}); + } else { void *buffer = allocator_->allocate(type.size(), type.alignment()); type.copy_construct(value.get(), buffer); diff --git a/source/blender/nodes/intern/node_common.c b/source/blender/nodes/intern/node_common.cc similarity index 67% rename from source/blender/nodes/intern/node_common.c rename to source/blender/nodes/intern/node_common.cc index b8c89d1db37..e5ec50858d9 100644 --- a/source/blender/nodes/intern/node_common.c +++ b/source/blender/nodes/intern/node_common.cc @@ -21,12 +21,16 @@ * \ingroup nodes */ -#include -#include +#include +#include #include "DNA_node_types.h" #include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_multi_value_map.hh" +#include "BLI_set.hh" +#include "BLI_stack.hh" #include "BLI_string.h" #include "BLI_utildefines.h" @@ -42,10 +46,10 @@ #include "node_common.h" #include "node_util.h" -enum { - REFINE_FORWARD = 1 << 0, - REFINE_BACKWARD = 1 << 1, -}; +using blender::Map; +using blender::MultiValueMap; +using blender::Set; +using blender::Stack; /* -------------------------------------------------------------------- */ /** \name Node Group @@ -53,24 +57,22 @@ enum { bNodeSocket *node_group_find_input_socket(bNode *groupnode, const char *identifier) { - bNodeSocket *sock; - for (sock = groupnode->inputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &groupnode->inputs) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } bNodeSocket *node_group_find_output_socket(bNode *groupnode, const char *identifier) { - bNodeSocket *sock; - for (sock = groupnode->outputs.first; sock; sock = sock->next) { + LISTBASE_FOREACH (bNodeSocket *, sock, &groupnode->outputs) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } /* groups display their internal tree name as label */ @@ -95,13 +97,12 @@ bool node_group_poll_instance(bNode *node, bNodeTree *nodetree, const char **dis bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_disabled_hint) { - bNode *node; bool valid = true; /* unspecified node group, generally allowed * (if anything, should be avoided on operator level) */ - if (grouptree == NULL) { + if (grouptree == nullptr) { return true; } @@ -110,7 +111,7 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis return false; } - for (node = grouptree->nodes.first; node; node = node->next) { + LISTBASE_FOREACH (bNode *, node, &grouptree->nodes) { if (node->typeinfo->poll_instance && !node->typeinfo->poll_instance(node, nodetree, r_disabled_hint)) { valid = false; @@ -121,12 +122,15 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis } /* used for both group nodes and interface nodes */ -static bNodeSocket *group_verify_socket( - bNodeTree *ntree, bNode *gnode, bNodeSocket *iosock, ListBase *verify_lb, int in_out) +static bNodeSocket *group_verify_socket(bNodeTree *ntree, + bNode *gnode, + bNodeSocket *iosock, + ListBase *verify_lb, + eNodeSocketInOut in_out) { bNodeSocket *sock; - for (sock = verify_lb->first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)verify_lb->first; sock; sock = sock->next) { if (STREQ(sock->identifier, iosock->identifier)) { break; } @@ -163,29 +167,32 @@ static bNodeSocket *group_verify_socket( } /* used for both group nodes and interface nodes */ -static void group_verify_socket_list( - bNodeTree *ntree, bNode *gnode, ListBase *iosock_lb, ListBase *verify_lb, int in_out) +static void group_verify_socket_list(bNodeTree *ntree, + bNode *gnode, + ListBase *iosock_lb, + ListBase *verify_lb, + eNodeSocketInOut in_out) { - bNodeSocket *iosock, *sock, *nextsock; + bNodeSocket *sock, *nextsock; /* step by step compare */ - iosock = iosock_lb->first; + bNodeSocket *iosock = (bNodeSocket *)iosock_lb->first; for (; iosock; iosock = iosock->next) { /* abusing new_sock pointer for verification here! only used inside this function */ iosock->new_sock = group_verify_socket(ntree, gnode, iosock, verify_lb, in_out); } /* leftovers are removed */ - for (sock = verify_lb->first; sock; sock = nextsock) { + for (sock = (bNodeSocket *)verify_lb->first; sock; sock = nextsock) { nextsock = sock->next; nodeRemoveSocket(ntree, gnode, sock); } /* and we put back the verified sockets */ - iosock = iosock_lb->first; + iosock = (bNodeSocket *)iosock_lb->first; for (; iosock; iosock = iosock->next) { if (iosock->new_sock) { BLI_addtail(verify_lb, iosock->new_sock); - iosock->new_sock = NULL; + iosock->new_sock = nullptr; } } } @@ -194,11 +201,11 @@ static void group_verify_socket_list( void node_group_update(struct bNodeTree *ntree, struct bNode *node) { /* check inputs and outputs, and remove or insert them */ - if (node->id == NULL) { + if (node->id == nullptr) { nodeRemoveAllSockets(ntree, node); } else if ((ID_IS_LINKED(node->id) && (node->id->tag & LIB_TAG_MISSING))) { - /* Missing datablock, leave sockets unchanged so that when it comes back + /* Missing data-block, leave sockets unchanged so that when it comes back * the links remain valid. */ } else { @@ -227,7 +234,7 @@ static void node_frame_init(bNodeTree *UNUSED(ntree), bNode *node) void register_node_type_frame(void) { /* frame type is used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_FRAME, "Frame", NODE_CLASS_LAYOUT, NODE_BACKGROUND); @@ -254,11 +261,11 @@ static void node_reroute_update_internal_links(bNodeTree *ntree, bNode *node) return; } - link = MEM_callocN(sizeof(bNodeLink), "internal node link"); + link = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "internal node link"); link->fromnode = node; - link->fromsock = node->inputs.first; + link->fromsock = (bNodeSocket *)node->inputs.first; link->tonode = node; - link->tosock = node->outputs.first; + link->tosock = (bNodeSocket *)node->outputs.first; /* internal link is always valid */ link->flag |= NODE_LINK_VALID; BLI_addtail(&node->internal_links, link); @@ -276,7 +283,7 @@ static void node_reroute_init(bNodeTree *ntree, bNode *node) void register_node_type_reroute(void) { /* frame type is used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "frame node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "frame node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_REROUTE, "Reroute", NODE_CLASS_LAYOUT, 0); @@ -286,76 +293,37 @@ void register_node_type_reroute(void) nodeRegisterType(ntype); } -static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, int flag) +static void propagate_reroute_type_from_start_socket( + bNodeSocket *start_socket, + const MultiValueMap &links_map, + Map &r_reroute_types) { - bNodeSocket *input = node->inputs.first; - bNodeSocket *output = node->outputs.first; - bNodeLink *link; - int type = SOCK_FLOAT; - const char *type_idname = nodeStaticSocketType(type, PROP_NONE); - - /* XXX it would be a little bit more efficient to restrict actual updates - * to reroute nodes connected to an updated node, but there's no reliable flag - * to indicate updated nodes (node->update is not set on linking). - */ - - node->done = 1; - - /* recursive update */ - for (link = ntree->links.first; link; link = link->next) { - bNode *fromnode = link->fromnode; - bNode *tonode = link->tonode; - if (!tonode || !fromnode) { - continue; + Stack nodes_to_check; + for (bNodeLink *link : links_map.lookup(start_socket)) { + if (link->tonode->type == NODE_REROUTE) { + nodes_to_check.push(link->tonode); } - if (nodeLinkIsHidden(link)) { - continue; + if (link->fromnode->type == NODE_REROUTE) { + nodes_to_check.push(link->fromnode); } - - if (flag & REFINE_FORWARD) { - if (tonode == node && fromnode->type == NODE_REROUTE && !fromnode->done) { - node_reroute_inherit_type_recursive(ntree, fromnode, REFINE_FORWARD); + } + const bNodeSocketType *current_type = start_socket->typeinfo; + while (!nodes_to_check.is_empty()) { + bNode *reroute_node = nodes_to_check.pop(); + BLI_assert(reroute_node->type == NODE_REROUTE); + if (r_reroute_types.add(reroute_node, current_type)) { + for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->inputs.first)) { + if (link->fromnode->type == NODE_REROUTE) { + nodes_to_check.push(link->fromnode); + } } - } - if (flag & REFINE_BACKWARD) { - if (fromnode == node && tonode->type == NODE_REROUTE && !tonode->done) { - node_reroute_inherit_type_recursive(ntree, tonode, REFINE_BACKWARD); + for (bNodeLink *link : links_map.lookup((bNodeSocket *)reroute_node->outputs.first)) { + if (link->tonode->type == NODE_REROUTE) { + nodes_to_check.push(link->tonode); + } } } } - - /* determine socket type from unambiguous input/output connection if possible */ - if (nodeSocketLinkLimit(input) == 1 && input->link) { - type = input->link->fromsock->type; - type_idname = nodeStaticSocketType(type, PROP_NONE); - } - else if (nodeSocketLinkLimit(output) == 1 && output->link) { - type = output->link->tosock->type; - type_idname = nodeStaticSocketType(type, PROP_NONE); - } - - if (input->type != type) { - bNodeSocket *ninput = nodeAddSocket(ntree, node, SOCK_IN, type_idname, "input", "Input"); - for (link = ntree->links.first; link; link = link->next) { - if (link->tosock == input) { - link->tosock = ninput; - ninput->link = link; - } - } - nodeRemoveSocket(ntree, node, input); - } - - if (output->type != type) { - bNodeSocket *noutput = nodeAddSocket(ntree, node, SOCK_OUT, type_idname, "output", "Output"); - for (link = ntree->links.first; link; link = link->next) { - if (link->fromsock == output) { - link->fromsock = noutput; - } - } - nodeRemoveSocket(ntree, node, output); - } - - nodeUpdateInternalLinks(ntree, node); } /* Global update function for Reroute node types. @@ -363,16 +331,58 @@ static void node_reroute_inherit_type_recursive(bNodeTree *ntree, bNode *node, i */ void ntree_update_reroute_nodes(bNodeTree *ntree) { - bNode *node; - - /* clear tags */ - for (node = ntree->nodes.first; node; node = node->next) { - node->done = 0; + /* Contains nodes that are linked to at least one reroute node. */ + Set nodes_linked_with_reroutes; + /* Contains all links that are linked to at least one reroute node. */ + MultiValueMap links_map; + /* Build acceleration data structures for the algorithm below. */ + LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) { + if (link->fromsock == nullptr || link->tosock == nullptr) { + continue; + } + if (link->fromnode->type != NODE_REROUTE && link->tonode->type != NODE_REROUTE) { + continue; + } + if (link->fromnode->type != NODE_REROUTE) { + nodes_linked_with_reroutes.add(link->fromnode); + } + if (link->tonode->type != NODE_REROUTE) { + nodes_linked_with_reroutes.add(link->tonode); + } + links_map.add(link->fromsock, link); + links_map.add(link->tosock, link); } - for (node = ntree->nodes.first; node; node = node->next) { - if (node->type == NODE_REROUTE && !node->done) { - node_reroute_inherit_type_recursive(ntree, node, REFINE_FORWARD | REFINE_BACKWARD); + /* Will contain the socket type for every linked reroute node. */ + Map reroute_types; + + /* Propagate socket types from left to right. */ + for (bNode *start_node : nodes_linked_with_reroutes) { + LISTBASE_FOREACH (bNodeSocket *, output_socket, &start_node->outputs) { + propagate_reroute_type_from_start_socket(output_socket, links_map, reroute_types); + } + } + + /* Propagate socket types from right to left. This affects reroute nodes that haven't been + * changed in the the loop above. */ + for (bNode *start_node : nodes_linked_with_reroutes) { + LISTBASE_FOREACH (bNodeSocket *, input_socket, &start_node->inputs) { + propagate_reroute_type_from_start_socket(input_socket, links_map, reroute_types); + } + } + + /* Actually update reroute nodes with changed types. */ + for (const auto item : reroute_types.items()) { + bNode *reroute_node = item.key; + const bNodeSocketType *socket_type = item.value; + bNodeSocket *input_socket = (bNodeSocket *)reroute_node->inputs.first; + bNodeSocket *output_socket = (bNodeSocket *)reroute_node->outputs.first; + + if (input_socket->typeinfo != socket_type) { + nodeModifySocketType(ntree, reroute_node, input_socket, socket_type->idname); + } + if (output_socket->typeinfo != socket_type) { + nodeModifySocketType(ntree, reroute_node, output_socket, socket_type->idname); } } } @@ -393,7 +403,7 @@ static bool node_is_connected_to_output_recursive(bNodeTree *ntree, bNode *node) } /* test all connected nodes, first positive find is sufficient to return true */ - for (link = ntree->links.first; link; link = link->next) { + for (link = (bNodeLink *)ntree->links.first; link; link = link->next) { if (link->fromnode == node) { if (node_is_connected_to_output_recursive(ntree, link->tonode)) { return true; @@ -408,7 +418,7 @@ bool BKE_node_is_connected_to_output(bNodeTree *ntree, bNode *node) bNode *tnode; /* clear flags */ - for (tnode = ntree->nodes.first; tnode; tnode = tnode->next) { + for (tnode = (bNode *)ntree->nodes.first; tnode; tnode = tnode->next) { tnode->done = 0; } @@ -419,9 +429,9 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree) { bNode *node; - for (node = ntree->nodes.first; node; node = node->next) { + for (node = (bNode *)ntree->nodes.first; node; node = node->next) { if (node->id == id) { - node->id = NULL; + node->id = nullptr; } } } @@ -440,17 +450,17 @@ static void node_group_input_init(bNodeTree *ntree, bNode *node) bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; - for (sock = node->outputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->outputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } void node_group_input_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *extsock = node->outputs.last; + bNodeSocket *extsock = (bNodeSocket *)node->outputs.last; bNodeLink *link, *linknext, *exposelink; /* Adding a tree socket and verifying will remove the extension socket! * This list caches the existing links from the extension socket @@ -460,14 +470,14 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) /* find links from the extension socket and store them */ BLI_listbase_clear(&tmplinks); - for (link = ntree->links.first; link; link = linknext) { + for (link = (bNodeLink *)ntree->links.first; link; link = linknext) { linknext = link->next; if (nodeLinkIsHidden(link)) { continue; } if (link->fromsock == extsock) { - bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link"); + bNodeLink *tlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "temporary link"); *tlink = *link; BLI_addtail(&tmplinks, tlink); @@ -476,8 +486,8 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) } /* find valid link to expose */ - exposelink = NULL; - for (link = tmplinks.first; link; link = link->next) { + exposelink = nullptr; + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { /* XXX Multiple sockets can be connected to the extension socket at once, * in that case the arbitrary first link determines name and type. * This could be improved by choosing the "best" type among all links, @@ -498,7 +508,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) newsock = node_group_input_find_socket(node, gsock->identifier); /* redirect links from the extension socket */ - for (link = tmplinks.first; link; link = link->next) { + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { nodeAddLink(ntree, node, newsock, link->tonode, link->tosock); } } @@ -518,7 +528,7 @@ void node_group_input_update(bNodeTree *ntree, bNode *node) void register_node_type_group_input(void) { /* used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_GROUP_INPUT, "Group Input", NODE_CLASS_INTERFACE, 0); @@ -537,17 +547,17 @@ static void node_group_output_init(bNodeTree *ntree, bNode *node) bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier) { bNodeSocket *sock; - for (sock = node->inputs.first; sock; sock = sock->next) { + for (sock = (bNodeSocket *)node->inputs.first; sock; sock = sock->next) { if (STREQ(sock->identifier, identifier)) { return sock; } } - return NULL; + return nullptr; } void node_group_output_update(bNodeTree *ntree, bNode *node) { - bNodeSocket *extsock = node->inputs.last; + bNodeSocket *extsock = (bNodeSocket *)node->inputs.last; bNodeLink *link, *linknext, *exposelink; /* Adding a tree socket and verifying will remove the extension socket! * This list caches the existing links to the extension socket @@ -557,14 +567,14 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) /* find links to the extension socket and store them */ BLI_listbase_clear(&tmplinks); - for (link = ntree->links.first; link; link = linknext) { + for (link = (bNodeLink *)ntree->links.first; link; link = linknext) { linknext = link->next; if (nodeLinkIsHidden(link)) { continue; } if (link->tosock == extsock) { - bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link"); + bNodeLink *tlink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), "temporary link"); *tlink = *link; BLI_addtail(&tmplinks, tlink); @@ -573,8 +583,8 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) } /* find valid link to expose */ - exposelink = NULL; - for (link = tmplinks.first; link; link = link->next) { + exposelink = nullptr; + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { /* XXX Multiple sockets can be connected to the extension socket at once, * in that case the arbitrary first link determines name and type. * This could be improved by choosing the "best" type among all links, @@ -596,7 +606,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) newsock = node_group_output_find_socket(node, gsock->identifier); /* redirect links to the extension socket */ - for (link = tmplinks.first; link; link = link->next) { + for (link = (bNodeLink *)tmplinks.first; link; link = link->next) { nodeAddLink(ntree, link->fromnode, link->fromsock, node, newsock); } } @@ -616,7 +626,7 @@ void node_group_output_update(bNodeTree *ntree, bNode *node) void register_node_type_group_output(void) { /* used for all tree types, needs dynamic allocation */ - bNodeType *ntype = MEM_callocN(sizeof(bNodeType), "node type"); + bNodeType *ntype = (bNodeType *)MEM_callocN(sizeof(bNodeType), "node type"); ntype->free_self = (void (*)(bNodeType *))MEM_freeN; node_type_base(ntype, NODE_GROUP_OUTPUT, "Group Output", NODE_CLASS_INTERFACE, 0); diff --git a/source/blender/nodes/intern/node_declaration.cc b/source/blender/nodes/intern/node_declaration.cc index f6b6cc49b2e..8a38b68ec59 100644 --- a/source/blender/nodes/intern/node_declaration.cc +++ b/source/blender/nodes/intern/node_declaration.cc @@ -69,6 +69,7 @@ void SocketDeclaration::set_common_flags(bNodeSocket &socket) const SET_FLAG_FROM_TEST(socket.flag, hide_value_, SOCK_HIDE_VALUE); SET_FLAG_FROM_TEST(socket.flag, hide_label_, SOCK_HIDE_LABEL); SET_FLAG_FROM_TEST(socket.flag, is_multi_input_, SOCK_MULTI_INPUT); + SET_FLAG_FROM_TEST(socket.flag, no_mute_links_, SOCK_NO_INTERNAL_LINK); } bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const @@ -88,6 +89,9 @@ bool SocketDeclaration::matches_common_data(const bNodeSocket &socket) const if (((socket.flag & SOCK_MULTI_INPUT) != 0) != is_multi_input_) { return false; } + if (((socket.flag & SOCK_NO_INTERNAL_LINK) != 0) != no_mute_links_) { + return false; + } return true; } diff --git a/source/blender/nodes/intern/node_geometry_exec.cc b/source/blender/nodes/intern/node_geometry_exec.cc index a3bbca90731..c7a3e795c33 100644 --- a/source/blender/nodes/intern/node_geometry_exec.cc +++ b/source/blender/nodes/intern/node_geometry_exec.cc @@ -36,6 +36,72 @@ void GeoNodeExecParams::error_message_add(const NodeWarningType type, std::strin local_logger.log_node_warning(provider_->dnode, type, std::move(message)); } +void GeoNodeExecParams::check_input_geometry_set(StringRef identifier, + const GeometrySet &geometry_set) const +{ + const int input_index = provider_->dnode->input_by_identifier(identifier).index(); + const SocketDeclaration &decl = *provider_->dnode->declaration()->inputs()[input_index]; + const decl::Geometry *geo_decl = dynamic_cast(&decl); + if (geo_decl == nullptr) { + return; + } + + const bool only_realized_data = geo_decl->only_realized_data(); + const bool only_instances = geo_decl->only_instances(); + const Span supported_types = geo_decl->supported_types(); + + if (only_realized_data) { + if (geometry_set.has_instances()) { + this->error_message_add(NodeWarningType::Info, + TIP_("Instances in input geometry are ignored")); + } + } + if (only_instances) { + if (geometry_set.has_realized_data()) { + this->error_message_add(NodeWarningType::Info, + TIP_("Realized data in input geometry is ignored")); + } + } + if (supported_types.is_empty()) { + /* Assume all types are supported. */ + return; + } + const Vector types_in_geometry = geometry_set.gather_component_types( + true, true); + for (const GeometryComponentType type : types_in_geometry) { + if (type == GEO_COMPONENT_TYPE_INSTANCES) { + continue; + } + if (supported_types.contains(type)) { + continue; + } + std::string message = TIP_("Input geometry has unsupported type: "); + switch (type) { + case GEO_COMPONENT_TYPE_MESH: { + message += TIP_("Mesh"); + break; + } + case GEO_COMPONENT_TYPE_POINT_CLOUD: { + message += TIP_("Point Cloud"); + break; + } + case GEO_COMPONENT_TYPE_INSTANCES: { + BLI_assert_unreachable(); + break; + } + case GEO_COMPONENT_TYPE_VOLUME: { + message += TIP_("Volume"); + break; + } + case GEO_COMPONENT_TYPE_CURVE: { + message += TIP_("Curve"); + break; + } + } + this->error_message_add(NodeWarningType::Info, std::move(message)); + } +} + const bNodeSocket *GeoNodeExecParams::find_available_socket(const StringRef name) const { for (const InputSocketRef *socket : provider_->dnode->inputs()) { @@ -183,6 +249,11 @@ AttributeDomain GeoNodeExecParams::get_highest_priority_input_domain( return default_domain; } +std::string GeoNodeExecParams::attribute_producer_name() const +{ + return provider_->dnode->label_or_name() + TIP_(" node"); +} + void GeoNodeExecParams::check_input_access(StringRef identifier, const CPPType *requested_type) const { diff --git a/source/blender/nodes/intern/node_multi_function.cc b/source/blender/nodes/intern/node_multi_function.cc index c91899ed8c2..6d79ed839b2 100644 --- a/source/blender/nodes/intern/node_multi_function.cc +++ b/source/blender/nodes/intern/node_multi_function.cc @@ -18,7 +18,7 @@ namespace blender::nodes { -NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) +NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree) { for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { bNodeTree *btree = tree_ref->btree(); @@ -27,11 +27,10 @@ NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScop if (bnode->typeinfo->build_multi_function == nullptr) { continue; } - NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; + NodeMultiFunctionBuilder builder{*bnode, *btree}; bnode->typeinfo->build_multi_function(builder); - const MultiFunction *fn = builder.built_fn_; - if (fn != nullptr) { - map_.add_new(bnode, fn); + if (builder.built_fn_ != nullptr) { + map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)}); } } } diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index 31260f95242..11356178d87 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -808,6 +808,7 @@ static bNodeSocketType *make_socket_type_string() MAKE_CPP_TYPE(Object, Object *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Collection, Collection *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Texture, Tex *, CPPTypeFlags::BasicType) +MAKE_CPP_TYPE(Image, Image *, CPPTypeFlags::BasicType) MAKE_CPP_TYPE(Material, Material *, CPPTypeFlags::BasicType) static bNodeSocketType *make_socket_type_object() @@ -858,6 +859,18 @@ static bNodeSocketType *make_socket_type_texture() return socktype; } +static bNodeSocketType *make_socket_type_image() +{ + bNodeSocketType *socktype = make_standard_socket_type(SOCK_IMAGE, PROP_NONE); + socktype->get_base_cpp_type = []() { return &blender::fn::CPPType::get(); }; + socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) { + *(Image **)r_value = ((bNodeSocketValueImage *)socket.default_value)->value; + }; + socktype->get_geometry_nodes_cpp_type = socktype->get_base_cpp_type; + socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value; + return socktype; +} + static bNodeSocketType *make_socket_type_material() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_MATERIAL, PROP_NONE); @@ -872,7 +885,7 @@ static bNodeSocketType *make_socket_type_material() void register_standard_node_socket_types(void) { - /* draw callbacks are set in drawnode.c to avoid bad-level calls */ + /* Draw callbacks are set in `drawnode.c` to avoid bad-level calls. */ nodeRegisterSocketType(make_socket_type_float(PROP_NONE)); nodeRegisterSocketType(make_socket_type_float(PROP_UNSIGNED)); @@ -906,14 +919,14 @@ void register_standard_node_socket_types(void) nodeRegisterSocketType(make_socket_type_object()); - nodeRegisterSocketType(make_standard_socket_type(SOCK_IMAGE, PROP_NONE)); - nodeRegisterSocketType(make_socket_type_geometry()); nodeRegisterSocketType(make_socket_type_collection()); nodeRegisterSocketType(make_socket_type_texture()); + nodeRegisterSocketType(make_socket_type_image()); + nodeRegisterSocketType(make_socket_type_material()); nodeRegisterSocketType(make_socket_type_virtual()); diff --git a/source/blender/nodes/intern/node_socket_declarations.cc b/source/blender/nodes/intern/node_socket_declarations.cc index 4b0dbad3cff..ed5691ebf7f 100644 --- a/source/blender/nodes/intern/node_socket_declarations.cc +++ b/source/blender/nodes/intern/node_socket_declarations.cc @@ -15,6 +15,7 @@ */ #include "NOD_socket_declarations.hh" +#include "NOD_socket_declarations_geometry.hh" #include "BKE_node.h" @@ -30,9 +31,9 @@ static void modify_subtype_except_for_storage(bNodeSocket &socket, int new_subty socket.typeinfo = socktype; } -/* -------------------------------------------------------------------- - * Float. - */ +/* -------------------------------------------------------------------- */ +/** \name #Float + * \{ */ bNodeSocket &Float::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -83,9 +84,11 @@ bNodeSocket &Float::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket & return socket; } -/* -------------------------------------------------------------------- - * Int. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Int + * \{ */ bNodeSocket &Int::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -136,9 +139,11 @@ bNodeSocket &Int::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket &so return socket; } -/* -------------------------------------------------------------------- - * Vector. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Vector + * \{ */ bNodeSocket &Vector::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -181,9 +186,11 @@ bNodeSocket &Vector::update_or_build(bNodeTree &ntree, bNode &node, bNodeSocket return socket; } -/* -------------------------------------------------------------------- - * Bool. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Bool + * \{ */ bNodeSocket &Bool::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -206,9 +213,11 @@ bool Bool::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * Color. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Color + * \{ */ bNodeSocket &Color::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -236,14 +245,17 @@ bool Color::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * String. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #String + * \{ */ bNodeSocket &String::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { bNodeSocket &socket = *nodeAddStaticSocket( &ntree, &node, in_out, SOCK_STRING, PROP_NONE, identifier_.c_str(), name_.c_str()); + STRNCPY(((bNodeSocketValueString *)socket.default_value)->value, default_value_.c_str()); this->set_common_flags(socket); return socket; } @@ -259,9 +271,11 @@ bool String::matches(const bNodeSocket &socket) const return true; } -/* -------------------------------------------------------------------- - * IDSocketDeclaration. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #IDSocketDeclaration + * \{ */ bNodeSocket &IDSocketDeclaration::build(bNodeTree &ntree, bNode &node, @@ -295,9 +309,11 @@ bNodeSocket &IDSocketDeclaration::update_or_build(bNodeTree &ntree, return socket; } -/* -------------------------------------------------------------------- - * Geometry. - */ +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name #Geometry + * \{ */ bNodeSocket &Geometry::build(bNodeTree &ntree, bNode &node, eNodeSocketInOut in_out) const { @@ -318,4 +334,46 @@ bool Geometry::matches(const bNodeSocket &socket) const return true; } +Span Geometry::supported_types() const +{ + return supported_types_; +} + +bool Geometry::only_realized_data() const +{ + return only_realized_data_; +} + +bool Geometry::only_instances() const +{ + return only_instances_; +} + +GeometryBuilder &GeometryBuilder::supported_type(GeometryComponentType supported_type) +{ + decl_->supported_types_ = {supported_type}; + return *this; +} + +GeometryBuilder &GeometryBuilder::supported_type( + blender::Vector supported_types) +{ + decl_->supported_types_ = std::move(supported_types); + return *this; +} + +GeometryBuilder &GeometryBuilder::only_realized_data(bool value) +{ + decl_->only_realized_data_ = value; + return *this; +} + +GeometryBuilder &GeometryBuilder::only_instances(bool value) +{ + decl_->only_instances_ = value; + return *this; +} + +/** \} */ + } // namespace blender::nodes::decl diff --git a/source/blender/nodes/intern/node_tree_ref.cc b/source/blender/nodes/intern/node_tree_ref.cc index 641d02af902..5481465aef6 100644 --- a/source/blender/nodes/intern/node_tree_ref.cc +++ b/source/blender/nodes/intern/node_tree_ref.cc @@ -19,6 +19,7 @@ #include "NOD_node_tree_ref.hh" #include "BLI_dot_export.hh" +#include "BLI_stack.hh" namespace blender::nodes { @@ -473,6 +474,155 @@ bool NodeTreeRef::has_undefined_nodes_or_sockets() const return false; } +bool NodeRef::any_input_is_directly_linked() const +{ + for (const SocketRef *socket : inputs_) { + if (!socket->directly_linked_sockets().is_empty()) { + return true; + } + } + return false; +} + +bool NodeRef::any_output_is_directly_linked() const +{ + for (const SocketRef *socket : outputs_) { + if (!socket->directly_linked_sockets().is_empty()) { + return true; + } + } + return false; +} + +bool NodeRef::any_socket_is_directly_linked(eNodeSocketInOut in_out) const +{ + if (in_out == SOCK_IN) { + return this->any_input_is_directly_linked(); + } + return this->any_output_is_directly_linked(); +} + +struct ToposortNodeState { + bool is_done = false; + bool is_in_stack = false; +}; + +static void toposort_from_start_node(const NodeTreeRef::ToposortDirection direction, + const NodeRef &start_node, + MutableSpan node_states, + NodeTreeRef::ToposortResult &result) +{ + struct Item { + const NodeRef *node; + /* Index of the next socket that is checked in the depth-first search. */ + int socket_index = 0; + /* Link index in the next socket that is checked in the depth-first search. */ + int link_index = 0; + }; + + /* Do a depth-first search to sort nodes topologically. */ + Stack nodes_to_check; + nodes_to_check.push({&start_node}); + while (!nodes_to_check.is_empty()) { + Item &item = nodes_to_check.peek(); + const NodeRef &node = *item.node; + const Span sockets = node.sockets( + direction == NodeTreeRef::ToposortDirection::LeftToRight ? SOCK_IN : SOCK_OUT); + + while (true) { + if (item.socket_index == sockets.size()) { + /* All sockets have already been visited. */ + break; + } + const SocketRef &socket = *sockets[item.socket_index]; + const Span linked_sockets = socket.directly_linked_sockets(); + if (item.link_index == linked_sockets.size()) { + /* All links connected to this socket have already been visited. */ + item.socket_index++; + item.link_index = 0; + continue; + } + const SocketRef &linked_socket = *linked_sockets[item.link_index]; + const NodeRef &linked_node = linked_socket.node(); + ToposortNodeState &linked_node_state = node_states[linked_node.id()]; + if (linked_node_state.is_done) { + /* The linked node has already been visited. */ + item.link_index++; + continue; + } + if (linked_node_state.is_in_stack) { + result.has_cycle = true; + } + else { + nodes_to_check.push({&linked_node}); + linked_node_state.is_in_stack = true; + } + break; + } + + /* If no other element has been pushed, the current node can be pushed to the sorted list. */ + if (&item == &nodes_to_check.peek()) { + ToposortNodeState &node_state = node_states[node.id()]; + node_state.is_done = true; + node_state.is_in_stack = false; + result.sorted_nodes.append(&node); + nodes_to_check.pop(); + } + } +} + +/** + * Sort nodes topologically from left to right or right to left. + * In the future the result if this could be cached on #NodeTreeRef. + */ +NodeTreeRef::ToposortResult NodeTreeRef::toposort(const ToposortDirection direction) const +{ + ToposortResult result; + result.sorted_nodes.reserve(nodes_by_id_.size()); + + Array node_states(nodes_by_id_.size()); + + for (const NodeRef *node : nodes_by_id_) { + if (node_states[node->id()].is_done) { + /* Ignore nodes that are done already. */ + continue; + } + if (node->any_socket_is_directly_linked( + direction == ToposortDirection::LeftToRight ? SOCK_OUT : SOCK_IN)) { + /* Ignore non-start nodes. */ + continue; + } + + toposort_from_start_node(direction, *node, node_states, result); + } + + /* Check if the loop above forgot some nodes because there is a cycle. */ + if (result.sorted_nodes.size() < nodes_by_id_.size()) { + result.has_cycle = true; + for (const NodeRef *node : nodes_by_id_) { + if (node_states[node->id()].is_done) { + /* Ignore nodes that are done already. */ + continue; + } + /* Start toposort at this node which is somewhere in the middle of a loop. */ + toposort_from_start_node(direction, *node, node_states, result); + } + } + + BLI_assert(result.sorted_nodes.size() == nodes_by_id_.size()); + return result; +} + +const NodeRef *NodeTreeRef::find_node(const bNode &bnode) const +{ + for (const NodeRef *node : this->nodes_by_type(bnode.typeinfo)) { + if (node->bnode_ == &bnode) { + return node; + } + } + return nullptr; +} + std::string NodeTreeRef::to_dot() const { dot::DirectedGraph digraph; diff --git a/source/blender/nodes/intern/node_util.c b/source/blender/nodes/intern/node_util.c index 1aec280fd2b..ba0cfeacb83 100644 --- a/source/blender/nodes/intern/node_util.c +++ b/source/blender/nodes/intern/node_util.c @@ -467,6 +467,14 @@ static int node_datatype_priority(eNodeSocketDatatype from, eNodeSocketDatatype return -1; } } + case SOCK_IMAGE: { + switch (from) { + case SOCK_IMAGE: + return 1; + default: + return -1; + } + } case SOCK_MATERIAL: { switch (from) { case SOCK_MATERIAL: diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c index c3a675fcd20..83ee0c2f411 100644 --- a/source/blender/nodes/shader/node_shader_tree.c +++ b/source/blender/nodes/shader/node_shader_tree.c @@ -201,7 +201,7 @@ void register_node_tree_type_sh(void) tt->type = NTREE_SHADER; strcpy(tt->idname, "ShaderNodeTree"); strcpy(tt->ui_name, N_("Shader Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Shader nodes")); tt->foreach_nodeclass = foreach_nodeclass; diff --git a/source/blender/nodes/shader/node_shader_util.c b/source/blender/nodes/shader/node_shader_util.c index abc2c7008c7..97041b3fdfd 100644 --- a/source/blender/nodes/shader/node_shader_util.c +++ b/source/blender/nodes/shader/node_shader_util.c @@ -40,7 +40,7 @@ static bool sh_fn_poll_default(bNodeType *UNUSED(ntype), bNodeTree *ntree, const char **r_disabled_hint) { - if (!STREQ(ntree->idname, "ShaderNodeTree") && !STREQ(ntree->idname, "GeometryNodeTree")) { + if (!STR_ELEM(ntree->idname, "ShaderNodeTree", "GeometryNodeTree")) { *r_disabled_hint = "Not a shader or geometry node tree"; return false; } diff --git a/source/blender/nodes/shader/node_shader_util.h b/source/blender/nodes/shader/node_shader_util.h index a75354d3381..c647b86a19a 100644 --- a/source/blender/nodes/shader/node_shader_util.h +++ b/source/blender/nodes/shader/node_shader_util.h @@ -73,6 +73,7 @@ # include "FN_multi_function_builder.hh" # include "NOD_multi_function.hh" +# include "NOD_socket_declarations.hh" # include "BLI_color.hh" # include "BLI_float3.hh" diff --git a/source/blender/nodes/shader/nodes/node_shader_brightness.c b/source/blender/nodes/shader/nodes/node_shader_brightness.c index d8f560277f2..4f375c666de 100644 --- a/source/blender/nodes/shader/nodes/node_shader_brightness.c +++ b/source/blender/nodes/shader/nodes/node_shader_brightness.c @@ -19,7 +19,7 @@ #include "node_shader_util.h" -/* **************** Brigh and contrsast ******************** */ +/* **************** Bright and contrast ******************** */ static bNodeSocketTemplate sh_node_brightcontrast_in[] = { {SOCK_RGBA, N_("Color"), 1.0f, 1.0f, 1.0f, 1.0f}, diff --git a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c index f601f3e9fd0..cb4f0594310 100644 --- a/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c +++ b/source/blender/nodes/shader/nodes/node_shader_bsdf_principled.c @@ -35,6 +35,8 @@ static bNodeSocketTemplate sh_node_bsdf_principled_in[] = { PROP_NONE, SOCK_COMPACT}, {SOCK_RGBA, N_("Subsurface Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, + {SOCK_FLOAT, N_("Subsurface IOR"), 1.4f, 0.0f, 0.0f, 0.0f, 1.01f, 3.8f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Subsurface Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Metallic"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Specular"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_FLOAT, N_("Specular Tint"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, @@ -74,7 +76,7 @@ static bNodeSocketTemplate sh_node_bsdf_principled_out[] = { static void node_shader_init_principled(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = SHD_GLOSSY_GGX; - node->custom2 = SHD_SUBSURFACE_BURLEY; + node->custom2 = SHD_SUBSURFACE_RANDOM_WALK; } #define socket_not_zero(sock) (in[sock].link || (clamp_f(in[sock].vec[0], 0.0f, 1.0f) > 1e-5f)) @@ -90,41 +92,40 @@ static int node_shader_gpu_bsdf_principled(GPUMaterial *mat, GPUNodeLink *sss_scale; /* Normals */ - if (!in[20].link) { - GPU_link(mat, "world_normals_get", &in[20].link); + if (!in[22].link) { + GPU_link(mat, "world_normals_get", &in[22].link); } /* Clearcoat Normals */ - if (!in[21].link) { - GPU_link(mat, "world_normals_get", &in[21].link); + if (!in[23].link) { + GPU_link(mat, "world_normals_get", &in[23].link); } #if 0 /* Not used at the moment. */ /* Tangents */ - if (!in[22].link) { + if (!in[24].link) { GPUNodeLink *orco = GPU_attribute(CD_ORCO, ""); - GPU_link(mat, "tangent_orco_z", orco, &in[22].link); + GPU_link(mat, "tangent_orco_z", orco, &in[24].link); GPU_link(mat, "node_tangent", GPU_builtin(GPU_WORLD_NORMAL), - in[22].link, + in[24].link, GPU_builtin(GPU_OBJECT_MATRIX), - &in[22].link); + &in[24].link); } #endif - bool use_diffuse = socket_not_one(4) && socket_not_one(15); + bool use_diffuse = socket_not_one(6) && socket_not_one(17); bool use_subsurf = socket_not_zero(1) && use_diffuse && node->sss_id > 0; - bool use_refract = socket_not_one(4) && socket_not_zero(15); - bool use_clear = socket_not_zero(12); + bool use_refract = socket_not_one(6) && socket_not_zero(17); + bool use_clear = socket_not_zero(14); /* SSS Profile */ if (use_subsurf) { - static short profile = SHD_SUBSURFACE_BURLEY; bNodeSocket *socket = BLI_findlink(&node->original->inputs, 2); bNodeSocketValueRGBA *socket_data = socket->default_value; /* For some reason it seems that the socket value is in ARGB format. */ - GPU_material_sss_profile_create(mat, &socket_data->value[1], &profile, NULL); + GPU_material_sss_profile_create(mat, &socket_data->value[1]); } if (in[2].link) { @@ -170,6 +171,7 @@ static void node_shader_update_principled(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sock; int distribution = node->custom1; + int sss_method = node->custom2; for (sock = node->inputs.first; sock; sock = sock->next) { if (STREQ(sock->name, "Transmission Roughness")) { @@ -180,6 +182,15 @@ static void node_shader_update_principled(bNodeTree *UNUSED(ntree), bNode *node) sock->flag |= SOCK_UNAVAIL; } } + + if (STR_ELEM(sock->name, "Subsurface IOR", "Subsurface Anisotropy")) { + if (sss_method == SHD_SUBSURFACE_BURLEY) { + sock->flag |= SOCK_UNAVAIL; + } + else { + sock->flag &= ~SOCK_UNAVAIL; + } + } } } diff --git a/source/blender/nodes/shader/nodes/node_shader_clamp.cc b/source/blender/nodes/shader/nodes/node_shader_clamp.cc index b90397e4892..57a992a4275 100644 --- a/source/blender/nodes/shader/nodes/node_shader_clamp.cc +++ b/source/blender/nodes/shader/nodes/node_shader_clamp.cc @@ -23,18 +23,19 @@ #include "node_shader_util.h" -/* **************** Clamp ******************** */ -static bNodeSocketTemplate sh_node_clamp_in[] = { - {SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_clamp_out[] = { - {SOCK_FLOAT, N_("Result")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_clamp_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Value")).min(0.0f).max(1.0f).default_value(1.0f); + b.add_input(N_("Min")).default_value(0.0f).min(-10000.0f).max(10000.0f); + b.add_input(N_("Max")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output(N_("Result")); }; +} // namespace blender::nodes + static void node_shader_init_clamp(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = NODE_CLAMP_MINMAX; /* clamp type */ @@ -79,7 +80,7 @@ void register_node_type_sh_clamp(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_CLAMP, "Clamp", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_clamp_in, sh_node_clamp_out); + ntype.declare = blender::nodes::sh_node_clamp_declare; node_type_init(&ntype, node_shader_init_clamp); node_type_gpu(&ntype, gpu_shader_clamp); ntype.build_multi_function = sh_node_clamp_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_curves.cc b/source/blender/nodes/shader/nodes/node_shader_curves.cc index df075d6e973..f8f0ee97eae 100644 --- a/source/blender/nodes/shader/nodes/node_shader_curves.cc +++ b/source/blender/nodes/shader/nodes/node_shader_curves.cc @@ -23,17 +23,17 @@ #include "node_shader_util.h" -/* **************** CURVE VEC ******************** */ -static bNodeSocketTemplate sh_node_curve_vec_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_curve_vec_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Vector")).min(-1.0f).max(1.0f); + b.add_output(N_("Vector")); }; -static bNodeSocketTemplate sh_node_curve_vec_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_exec_curve_vec(void *UNUSED(data), int UNUSED(thread), @@ -157,7 +157,7 @@ void register_node_type_sh_curve_vec(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_CURVE_VEC, "Vector Curves", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_curve_vec_in, sh_node_curve_vec_out); + ntype.declare = blender::nodes::sh_node_curve_vec_declare; node_type_init(&ntype, node_shader_init_curve_vec); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); @@ -169,16 +169,18 @@ void register_node_type_sh_curve_vec(void) } /* **************** CURVE RGB ******************** */ -static bNodeSocketTemplate sh_node_curve_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 1.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 1.0f}, - {-1, ""}, + +namespace blender::nodes { + +static void sh_node_curve_rgb_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Fac")).min(0.0f).max(1.0f).default_value(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Color")).default_value({1.0f, 1.0f, 1.0f, 1.0f}); + b.add_output(N_("Color")); }; -static bNodeSocketTemplate sh_node_curve_rgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_exec_curve_rgb(void *UNUSED(data), int UNUSED(thread), @@ -332,7 +334,7 @@ void register_node_type_sh_curve_rgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_CURVE_RGB, "RGB Curves", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_curve_rgb_in, sh_node_curve_rgb_out); + ntype.declare = blender::nodes::sh_node_curve_rgb_declare; node_type_init(&ntype, node_shader_init_curve_rgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); @@ -342,3 +344,147 @@ void register_node_type_sh_curve_rgb(void) nodeRegisterType(&ntype); } + +/* **************** CURVE FLOAT ******************** */ + +namespace blender::nodes { + +static void sh_node_curve_float_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Factor")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Value")).default_value(1.0f); + b.add_output(N_("Value")); +}; + +} // namespace blender::nodes + +static void node_shader_exec_curve_float(void *UNUSED(data), + int UNUSED(thread), + bNode *node, + bNodeExecData *UNUSED(execdata), + bNodeStack **in, + bNodeStack **out) +{ + float value; + float fac; + + nodestack_get_vec(&fac, SOCK_FLOAT, in[0]); + nodestack_get_vec(&value, SOCK_FLOAT, in[1]); + out[0]->vec[0] = BKE_curvemapping_evaluateF((CurveMapping *)node->storage, 0, value); + if (fac != 1.0f) { + out[0]->vec[0] = (1.0f - fac) * value + fac * out[0]->vec[0]; + } +} + +static void node_shader_init_curve_float(bNodeTree *UNUSED(ntree), bNode *node) +{ + node->storage = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f); +} + +static int gpu_shader_curve_float(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + float *array, layer; + int size; + + CurveMapping *cumap = (CurveMapping *)node->storage; + + BKE_curvemapping_table_F(cumap, &array, &size); + GPUNodeLink *tex = GPU_color_band(mat, size, array, &layer); + + float ext_xyz[4]; + float range_x; + + const CurveMap *cm = &cumap->cm[0]; + ext_xyz[0] = cm->mintable; + ext_xyz[2] = cm->maxtable; + range_x = 1.0f / max_ff(1e-8f, cm->maxtable - cm->mintable); + /* Compute extrapolation gradients. */ + if ((cumap->flag & CUMA_EXTEND_EXTRAPOLATE) != 0) { + ext_xyz[1] = (cm->ext_in[0] != 0.0f) ? (cm->ext_in[1] / (cm->ext_in[0] * range_x)) : 1e8f; + ext_xyz[3] = (cm->ext_out[0] != 0.0f) ? (cm->ext_out[1] / (cm->ext_out[0] * range_x)) : 1e8f; + } + else { + ext_xyz[1] = 0.0f; + ext_xyz[3] = 0.0f; + } + return GPU_stack_link(mat, + node, + "curve_float", + in, + out, + tex, + GPU_constant(&layer), + GPU_uniform(&range_x), + GPU_uniform(ext_xyz)); +} + +class CurveFloatFunction : public blender::fn::MultiFunction { + private: + const CurveMapping &cumap_; + + public: + CurveFloatFunction(const CurveMapping &cumap) : cumap_(cumap) + { + static blender::fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static blender::fn::MFSignature create_signature() + { + blender::fn::MFSignatureBuilder signature{"Curve Float"}; + signature.single_input("Factor"); + signature.single_input("Value"); + signature.single_output("Value"); + return signature.build(); + } + + void call(blender::IndexMask mask, + blender::fn::MFParams params, + blender::fn::MFContext UNUSED(context)) const override + { + const blender::VArray &fac = params.readonly_single_input(0, "Factor"); + const blender::VArray &val_in = params.readonly_single_input(1, "Value"); + blender::MutableSpan val_out = params.uninitialized_single_output(2, "Value"); + + for (int64_t i : mask) { + val_out[i] = BKE_curvemapping_evaluateF(&cumap_, 0, val_in[i]); + if (fac[i] != 1.0f) { + val_out[i] = (1.0f - fac[i]) * val_in[i] + fac[i] * val_out[i]; + } + } + } +}; + +static void sh_node_curve_float_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &bnode = builder.node(); + CurveMapping *cumap = (CurveMapping *)bnode.storage; + BKE_curvemapping_init(cumap); + builder.construct_and_set_matching_fn(*cumap); +} + +void register_node_type_sh_curve_float(void) +{ + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_CURVE_FLOAT, "Float Curve", NODE_CLASS_CONVERTER, 0); + ntype.declare = blender::nodes::sh_node_curve_float_declare; + node_type_init(&ntype, node_shader_init_curve_float); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + node_type_storage(&ntype, "CurveMapping", node_free_curves, node_copy_curves); + node_type_exec(&ntype, node_initexec_curves, nullptr, node_shader_exec_curve_float); + node_type_gpu(&ntype, gpu_shader_curve_float); + ntype.build_multi_function = sh_node_curve_float_build_multi_function; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_hair_info.c b/source/blender/nodes/shader/nodes/node_shader_hair_info.c index 843185befb6..c721fb9c77a 100644 --- a/source/blender/nodes/shader/nodes/node_shader_hair_info.c +++ b/source/blender/nodes/shader/nodes/node_shader_hair_info.c @@ -22,6 +22,7 @@ static bNodeSocketTemplate outputs[] = { {SOCK_FLOAT, N_("Is Strand"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_FLOAT, N_("Intercept"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, + {SOCK_FLOAT, N_("Length"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_FLOAT, N_("Thickness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, {SOCK_VECTOR, N_("Tangent Normal"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, // { SOCK_FLOAT, 0, N_("Fade"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, @@ -35,7 +36,11 @@ static int node_shader_gpu_hair_info(GPUMaterial *mat, GPUNodeStack *in, GPUNodeStack *out) { - return GPU_stack_link(mat, node, "node_hair_info", in, out); + /* Length: don't request length if not needed. */ + static const float zero = 0; + GPUNodeLink *length_link = (!out[2].hasoutput) ? GPU_constant(&zero) : + GPU_attribute(mat, CD_HAIRLENGTH, ""); + return GPU_stack_link(mat, node, "node_hair_info", in, out, length_link); } /* node type definition */ diff --git a/source/blender/nodes/shader/nodes/node_shader_map_range.cc b/source/blender/nodes/shader/nodes/node_shader_map_range.cc index f48e824ccb5..c866a154e8c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_map_range.cc +++ b/source/blender/nodes/shader/nodes/node_shader_map_range.cc @@ -25,21 +25,22 @@ #include "BLI_math_base_safe.h" -/* **************** Map Range ******************** */ -static bNodeSocketTemplate sh_node_map_range_in[] = { - {SOCK_FLOAT, N_("Value"), 1.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, PROP_NONE}, - {SOCK_FLOAT, N_("From Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("From Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("To Min"), 0.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("To Max"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Steps"), 4.0f, 1.0f, 1.0f, 1.0f, 0.0f, 10000.0f, PROP_NONE}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_map_range_out[] = { - {SOCK_FLOAT, N_("Result")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_map_range_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Value")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input(N_("From Min")).min(-10000.0f).max(10000.0f); + b.add_input(N_("From Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input(N_("To Min")).min(-10000.0f).max(10000.0f); + b.add_input(N_("To Max")).min(-10000.0f).max(10000.0f).default_value(1.0f); + b.add_input(N_("Steps")).min(-10000.0f).max(10000.0f).default_value(4.0f); + b.add_output(N_("Result")); }; +} // namespace blender::nodes + static void node_shader_update_map_range(bNodeTree *UNUSED(ntree), bNode *node) { bNodeSocket *sockSteps = nodeFindSocket(node, SOCK_IN, "Steps"); @@ -311,7 +312,7 @@ void register_node_type_sh_map_range(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_MAP_RANGE, "Map Range", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_map_range_in, sh_node_map_range_out); + ntype.declare = blender::nodes::sh_node_map_range_declare; node_type_init(&ntype, node_shader_init_map_range); node_type_update(&ntype, node_shader_update_map_range); node_type_gpu(&ntype, gpu_shader_map_range); diff --git a/source/blender/nodes/shader/nodes/node_shader_math.cc b/source/blender/nodes/shader/nodes/node_shader_math.cc index 66c50b6de46..284a5f1189f 100644 --- a/source/blender/nodes/shader/nodes/node_shader_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_math.cc @@ -26,13 +26,25 @@ #include "NOD_math_functions.hh" /* **************** SCALAR MATH ******************** */ -static bNodeSocketTemplate sh_node_math_in[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.5f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Value"), 0.0f, 0.5f, 0.5f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; -static bNodeSocketTemplate sh_node_math_out[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; +namespace blender::nodes { + +static void sh_node_math_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Value")).default_value(0.5f).min(-10000.0f).max(10000.0f); + b.add_input(N_("Value"), "Value_001") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_input(N_("Value"), "Value_002") + .default_value(0.5f) + .min(-10000.0f) + .max(10000.0f); + b.add_output(N_("Value")); +}; + +} // namespace blender::nodes static const char *gpu_shader_get_name(int mode) { @@ -153,7 +165,7 @@ void register_node_type_sh_math(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_MATH, "Math", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_math_in, sh_node_math_out); + ntype.declare = blender::nodes::sh_node_math_declare; node_type_label(&ntype, node_math_label); node_type_gpu(&ntype, gpu_shader_math); node_type_update(&ntype, node_math_update); diff --git a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc index ade35a40366..06fafff578e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_mixRgb.cc @@ -23,18 +23,19 @@ #include "node_shader_util.h" -/* **************** MIX RGB ******************** */ -static bNodeSocketTemplate sh_node_mix_rgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_RGBA, N_("Color1"), 0.5f, 0.5f, 0.5f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.5f, 0.5f, 0.5f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_mix_rgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_mix_rgb_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_input(N_("Color1")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_input(N_("Color2")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output(N_("Color")); }; +} // namespace blender::nodes + static void node_shader_exec_mix_rgb(void *UNUSED(data), int UNUSED(thread), bNode *node, @@ -187,7 +188,7 @@ void register_node_type_sh_mix_rgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_MIX_RGB, "Mix", NODE_CLASS_OP_COLOR, 0); - node_type_socket_templates(&ntype, sh_node_mix_rgb_in, sh_node_mix_rgb_out); + ntype.declare = blender::nodes::sh_node_mix_rgb_declare; node_type_label(&ntype, node_blend_label); node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_mix_rgb); node_type_gpu(&ntype, gpu_shader_mix_rgb); diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc index d9cbee33c0f..08a9e01786e 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombRGB.cc @@ -23,18 +23,19 @@ #include "node_shader_util.h" -/* **************** SEPARATE RGBA ******************** */ -static bNodeSocketTemplate sh_node_seprgb_in[] = { - {SOCK_RGBA, N_("Image"), 0.8f, 0.8f, 0.8f, 1.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_seprgb_out[] = { - {SOCK_FLOAT, N_("R")}, - {SOCK_FLOAT, N_("G")}, - {SOCK_FLOAT, N_("B")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_seprgb_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Image")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_output(N_("R")); + b.add_output(N_("G")); + b.add_output(N_("B")); }; +} // namespace blender::nodes + static void node_shader_exec_seprgb(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), @@ -107,7 +108,7 @@ void register_node_type_sh_seprgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_SEPRGB, "Separate RGB", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_seprgb_in, sh_node_seprgb_out); + ntype.declare = blender::nodes::sh_node_seprgb_declare; node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_seprgb); node_type_gpu(&ntype, gpu_shader_seprgb); ntype.build_multi_function = sh_node_seprgb_build_multi_function; @@ -115,18 +116,19 @@ void register_node_type_sh_seprgb(void) nodeRegisterType(&ntype); } -/* **************** COMBINE RGB ******************** */ -static bNodeSocketTemplate sh_node_combrgb_in[] = { - {SOCK_FLOAT, N_("R"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("G"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {SOCK_FLOAT, N_("B"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_UNSIGNED}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_combrgb_out[] = { - {SOCK_RGBA, N_("Image")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_combrgb_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("R")).min(0.0f).max(1.0f); + b.add_input(N_("G")).min(0.0f).max(1.0f); + b.add_input(N_("B")).min(0.0f).max(1.0f); + b.add_output(N_("Image")); }; +} // namespace blender::nodes + static void node_shader_exec_combrgb(void *UNUSED(data), int UNUSED(thread), bNode *UNUSED(node), @@ -166,7 +168,7 @@ void register_node_type_sh_combrgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_COMBRGB, "Combine RGB", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_combrgb_in, sh_node_combrgb_out); + ntype.declare = blender::nodes::sh_node_combrgb_declare; node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_combrgb); node_type_gpu(&ntype, gpu_shader_combrgb); ntype.build_multi_function = sh_node_combrgb_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc index 3048ed1e9fc..1bbfa629462 100644 --- a/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc +++ b/source/blender/nodes/shader/nodes/node_shader_sepcombXYZ.cc @@ -23,18 +23,19 @@ #include "node_shader_util.h" -/* **************** SEPARATE XYZ ******************** */ -static bNodeSocketTemplate sh_node_sepxyz_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_sepxyz_out[] = { - {SOCK_FLOAT, N_("X")}, - {SOCK_FLOAT, N_("Y")}, - {SOCK_FLOAT, N_("Z")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_sepxyz_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_output(N_("X")); + b.add_output(N_("Y")); + b.add_output(N_("Z")); }; +} // namespace blender::nodes + static int gpu_shader_sepxyz(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -92,25 +93,26 @@ void register_node_type_sh_sepxyz(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_SEPXYZ, "Separate XYZ", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_sepxyz_in, sh_node_sepxyz_out); + ntype.declare = blender::nodes::sh_node_sepxyz_declare; node_type_gpu(&ntype, gpu_shader_sepxyz); ntype.build_multi_function = sh_node_sepxyz_build_multi_function; nodeRegisterType(&ntype); } -/* **************** COMBINE XYZ ******************** */ -static bNodeSocketTemplate sh_node_combxyz_in[] = { - {SOCK_FLOAT, N_("X"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Y"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f}, - {SOCK_FLOAT, N_("Z"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_combxyz_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_combxyz_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("X")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Y")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Z")).min(-10000.0f).max(10000.0f); + b.add_output(N_("Vector")); }; +} // namespace blender::nodes + static int gpu_shader_combxyz(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -132,7 +134,7 @@ void register_node_type_sh_combxyz(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_COMBXYZ, "Combine XYZ", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_combxyz_in, sh_node_combxyz_out); + ntype.declare = blender::nodes::sh_node_combxyz_declare; node_type_gpu(&ntype, gpu_shader_combxyz); ntype.build_multi_function = sh_node_combxyz_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c index 4b91bcbd11c..e917858e0f2 100644 --- a/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c +++ b/source/blender/nodes/shader/nodes/node_shader_subsurface_scattering.c @@ -25,8 +25,8 @@ static bNodeSocketTemplate sh_node_subsurface_scattering_in[] = { {SOCK_RGBA, N_("Color"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, {SOCK_FLOAT, N_("Scale"), 1.0, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, {SOCK_VECTOR, N_("Radius"), 1.0f, 0.2f, 0.1f, 0.0f, 0.0f, 100.0f, PROP_NONE, SOCK_COMPACT}, - {SOCK_FLOAT, N_("Sharpness"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Texture Blur"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, + {SOCK_FLOAT, N_("IOR"), 1.4f, 0.0f, 0.0f, 0.0f, 1.01f, 3.8f, PROP_FACTOR}, + {SOCK_FLOAT, N_("Anisotropy"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, {SOCK_VECTOR, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, {-1, ""}, }; @@ -38,7 +38,8 @@ static bNodeSocketTemplate sh_node_subsurface_scattering_out[] = { static void node_shader_init_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) { - node->custom1 = SHD_SUBSURFACE_BURLEY; + node->custom1 = SHD_SUBSURFACE_RANDOM_WALK; + node->custom2 = true; } static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, @@ -54,11 +55,8 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, if (node->sss_id > 0) { bNodeSocket *socket = BLI_findlink(&node->original->inputs, 2); bNodeSocketValueRGBA *socket_data = socket->default_value; - bNodeSocket *socket_sharp = BLI_findlink(&node->original->inputs, 3); - bNodeSocketValueFloat *socket_data_sharp = socket_sharp->default_value; /* For some reason it seems that the socket value is in ARGB format. */ - GPU_material_sss_profile_create( - mat, &socket_data->value[1], &node->original->custom1, &socket_data_sharp->value); + GPU_material_sss_profile_create(mat, &socket_data->value[1]); /* sss_id is 0 only the node is not connected to any output. * In this case flagging the material would trigger a bug (see T68736). */ @@ -69,23 +67,6 @@ static int node_shader_gpu_subsurface_scattering(GPUMaterial *mat, mat, node, "node_subsurface_scattering", in, out, GPU_constant(&node->sss_id)); } -static void node_shader_update_subsurface_scattering(bNodeTree *UNUSED(ntree), bNode *node) -{ - bNodeSocket *sock; - int falloff = node->custom1; - - for (sock = node->inputs.first; sock; sock = sock->next) { - if (STREQ(sock->name, "Sharpness")) { - if (falloff == SHD_SUBSURFACE_CUBIC) { - sock->flag &= ~SOCK_UNAVAIL; - } - else { - sock->flag |= SOCK_UNAVAIL; - } - } - } -} - /* node type definition */ void register_node_type_sh_subsurface_scattering(void) { @@ -99,7 +80,6 @@ void register_node_type_sh_subsurface_scattering(void) node_type_init(&ntype, node_shader_init_subsurface_scattering); node_type_storage(&ntype, "", NULL, NULL); node_type_gpu(&ntype, node_shader_gpu_subsurface_scattering); - node_type_update(&ntype, node_shader_update_subsurface_scattering); nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c b/source/blender/nodes/shader/nodes/node_shader_tex_brick.c deleted file mode 100644 index 1b802f1dfd7..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_brick.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_brick_in[] = { - {SOCK_VECTOR, - N_("Vector"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_HIDE_VALUE | SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color1"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.2f, 0.2f, 0.2f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, - N_("Mortar"), - 0.0f, - 0.0f, - 0.0f, - 1.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Scale"), - 5.0f, - 0.0f, - 0.0f, - 0.0f, - -1000.0f, - 1000.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Mortar Size"), - 0.02f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.125f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Mortar Smooth"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Bias"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - -1.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Brick Width"), - 0.5f, - 0.0f, - 0.0f, - 0.0f, - 0.01f, - 100.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Row Height"), - 0.25f, - 0.0f, - 0.0f, - 0.0f, - 0.01f, - 100.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_brick_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexBrick *tex = MEM_callocN(sizeof(NodeTexBrick), "NodeTexBrick"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - - tex->offset = 0.5f; - tex->squash = 1.0f; - tex->offset_freq = 2; - tex->squash_freq = 2; - - node->storage = tex; - - LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { - if (STREQ(sock->name, "Mortar Smooth")) { - ((bNodeSocketValueFloat *)sock->default_value)->value = 0.1f; - } - } -} - -static int node_shader_gpu_tex_brick(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - NodeTexBrick *tex = (NodeTexBrick *)node->storage; - float offset_freq = tex->offset_freq; - float squash_freq = tex->squash_freq; - return GPU_stack_link(mat, - node, - "node_tex_brick", - in, - out, - GPU_uniform(&tex->offset), - GPU_constant(&offset_freq), - GPU_uniform(&tex->squash), - GPU_constant(&squash_freq)); -} - -/* node type definition */ -void register_node_type_sh_tex_brick(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_brick_in, sh_node_tex_brick_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tex_brick); - node_type_storage( - &ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_brick); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc new file mode 100644 index 00000000000..b840bd75e42 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_brick.cc @@ -0,0 +1,285 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +#include "BLI_float2.hh" +#include "BLI_float4.hh" + +namespace blender::nodes { + +static void sh_node_tex_brick_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input(N_("Mortar")).default_value({0.0f, 0.0f, 0.0f, 1.0f}).no_muted_links(); + b.add_input(N_("Scale")) + .min(-1000.0f) + .max(1000.0f) + .default_value(5.0f) + .no_muted_links(); + b.add_input(N_("Mortar Size")) + .min(0.0f) + .max(0.125f) + .default_value(0.02f) + .no_muted_links(); + b.add_input(N_("Mortar Smooth")).min(0.0f).max(1.0f).no_muted_links(); + b.add_input(N_("Bias")).min(-1.0f).max(1.0f).no_muted_links(); + b.add_input(N_("Brick Width")) + .min(0.01f) + .max(100.0f) + .default_value(0.5f) + .no_muted_links(); + b.add_input(N_("Row Height")) + .min(0.01f) + .max(100.0f) + .default_value(0.25f) + .no_muted_links(); + b.add_output(N_("Color")); + b.add_output(N_("Fac")); +}; + +} // namespace blender::nodes + +static void node_shader_init_tex_brick(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexBrick *tex = (NodeTexBrick *)MEM_callocN(sizeof(NodeTexBrick), "NodeTexBrick"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + + tex->offset = 0.5f; + tex->squash = 1.0f; + tex->offset_freq = 2; + tex->squash_freq = 2; + + node->storage = tex; + + LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) { + if (STREQ(sock->name, "Mortar Smooth")) { + ((bNodeSocketValueFloat *)sock->default_value)->value = 0.1f; + } + } +} + +static int node_shader_gpu_tex_brick(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + NodeTexBrick *tex = (NodeTexBrick *)node->storage; + float offset_freq = tex->offset_freq; + float squash_freq = tex->squash_freq; + return GPU_stack_link(mat, + node, + "node_tex_brick", + in, + out, + GPU_uniform(&tex->offset), + GPU_constant(&offset_freq), + GPU_uniform(&tex->squash), + GPU_constant(&squash_freq)); +} + +namespace blender::nodes { + +class BrickFunction : public fn::MultiFunction { + private: + const float offset_; + const int offset_freq_; + const float squash_; + const int squash_freq_; + + public: + BrickFunction(const float offset, + const int offset_freq, + const float squash, + const int squash_freq) + : offset_(offset), offset_freq_(offset_freq), squash_(squash), squash_freq_(squash_freq) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"BrickTexture"}; + signature.single_input("Vector"); + signature.single_input("Color1"); + signature.single_input("Color2"); + signature.single_input("Mortar"); + signature.single_input("Scale"); + signature.single_input("Mortar Size"); + signature.single_input("Mortar Smooth"); + signature.single_input("Bias"); + signature.single_input("Brick Width"); + signature.single_input("Row Height"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + /* Fast integer noise. */ + static float brick_noise(uint n) + { + n = (n + 1013) & 0x7fffffff; + n = (n >> 13) ^ n; + const uint nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff; + return 0.5f * ((float)nn / 1073741824.0f); + } + + static float smoothstepf(const float f) + { + const float ff = f * f; + return (3.0f * ff - 2.0f * ff * f); + } + + static float2 brick(float3 p, + float mortar_size, + float mortar_smooth, + float bias, + float brick_width, + float row_height, + float offset_amount, + int offset_frequency, + float squash_amount, + int squash_frequency) + { + float offset = 0.0f; + + const int rownum = (int)floorf(p.y / row_height); + + if (offset_frequency && squash_frequency) { + brick_width *= (rownum % squash_frequency) ? 1.0f : squash_amount; + offset = (rownum % offset_frequency) ? 0.0f : (brick_width * offset_amount); + } + + const int bricknum = (int)floorf((p.x + offset) / brick_width); + + const float x = (p.x + offset) - brick_width * bricknum; + const float y = p.y - row_height * rownum; + + const float tint = clamp_f( + brick_noise((rownum << 16) + (bricknum & 0xFFFF)) + bias, 0.0f, 1.0f); + float min_dist = std::min(std::min(x, y), std::min(brick_width - x, row_height - y)); + + float mortar; + if (min_dist >= mortar_size) { + mortar = 0.0f; + } + else if (mortar_smooth == 0.0f) { + mortar = 1.0f; + } + else { + min_dist = 1.0f - min_dist / mortar_size; + mortar = (min_dist < mortar_smooth) ? smoothstepf(min_dist / mortar_smooth) : 1.0f; + } + + return float2(tint, mortar); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &color1_values = params.readonly_single_input( + 1, "Color1"); + const VArray &color2_values = params.readonly_single_input( + 2, "Color2"); + const VArray &mortar_values = params.readonly_single_input( + 3, "Mortar"); + const VArray &scale = params.readonly_single_input(4, "Scale"); + const VArray &mortar_size = params.readonly_single_input(5, "Mortar Size"); + const VArray &mortar_smooth = params.readonly_single_input(6, "Mortar Smooth"); + const VArray &bias = params.readonly_single_input(7, "Bias"); + const VArray &brick_width = params.readonly_single_input(8, "Brick Width"); + const VArray &row_height = params.readonly_single_input(9, "Row Height"); + + MutableSpan r_color = + params.uninitialized_single_output_if_required(10, "Color"); + MutableSpan r_fac = params.uninitialized_single_output_if_required(11, "Fac"); + + const bool store_fac = !r_fac.is_empty(); + const bool store_color = !r_color.is_empty(); + + for (int64_t i : mask) { + const float2 f2 = brick(vector[i] * scale[i], + mortar_size[i], + mortar_smooth[i], + bias[i], + brick_width[i], + row_height[i], + offset_, + offset_freq_, + squash_, + squash_freq_); + + float4 color_data, color1, color2, mortar; + copy_v4_v4(color_data, color1_values[i]); + copy_v4_v4(color1, color1_values[i]); + copy_v4_v4(color2, color2_values[i]); + copy_v4_v4(mortar, mortar_values[i]); + const float tint = f2.x; + const float f = f2.y; + + if (f != 1.0f) { + const float facm = 1.0f - tint; + color_data = color1 * facm + color2 * tint; + } + + if (store_color) { + color_data = color_data * (1.0f - f) + mortar * f; + copy_v4_v4(r_color[i], color_data); + } + if (store_fac) { + r_fac[i] = f; + } + } + } +}; + +static void sh_node_brick_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexBrick *tex = (NodeTexBrick *)node.storage; + + builder.construct_and_set_matching_fn( + tex->offset, tex->offset_freq, tex->squash, tex->squash_freq); +} + +} // namespace blender::nodes + +void register_node_type_sh_tex_brick(void) +{ + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_BRICK, "Brick Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_brick_declare; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, node_shader_init_tex_brick); + node_type_storage( + &ntype, "NodeTexBrick", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_brick); + ntype.build_multi_function = blender::nodes::sh_node_brick_build_multi_function; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c b/source/blender/nodes/shader/nodes/node_shader_tex_checker.c deleted file mode 100644 index 75219f4c3f9..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_checker.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_checker_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_RGBA, N_("Color1"), 0.8f, 0.8f, 0.8f, 1.0f, 0.0f, 1.0f}, - {SOCK_RGBA, N_("Color2"), 0.2f, 0.2f, 0.2f, 1.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Scale"), - 5.0f, - 0.0f, - 0.0f, - 0.0f, - -1000.0f, - 1000.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_checker_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_checker(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexChecker *tex = MEM_callocN(sizeof(NodeTexChecker), "NodeTexChecker"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - - node->storage = tex; -} - -static int node_shader_gpu_tex_checker(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - return GPU_stack_link(mat, node, "node_tex_checker", in, out); -} - -/* node type definition */ -void register_node_type_sh_tex_checker(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_CHECKER, "Checker Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_checker_in, sh_node_tex_checker_out); - node_type_init(&ntype, node_shader_init_tex_checker); - node_type_storage( - &ntype, "NodeTexChecker", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_checker); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc new file mode 100644 index 00000000000..7c1223a6a32 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_checker.cc @@ -0,0 +1,139 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +namespace blender::nodes { + +static void sh_node_tex_checker_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input(N_("Color1")).default_value({0.8f, 0.8f, 0.8f, 1.0f}); + b.add_input(N_("Color2")).default_value({0.2f, 0.2f, 0.2f, 1.0f}); + b.add_input(N_("Scale")) + .min(-10000.0f) + .max(10000.0f) + .default_value(5.0f) + .no_muted_links(); + b.add_output(N_("Color")); + b.add_output(N_("Fac")); +}; + +} // namespace blender::nodes + +static void node_shader_init_tex_checker(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexChecker *tex = (NodeTexChecker *)MEM_callocN(sizeof(NodeTexChecker), "NodeTexChecker"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + + node->storage = tex; +} + +static int node_shader_gpu_tex_checker(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + return GPU_stack_link(mat, node, "node_tex_checker", in, out); +} + +namespace blender::nodes { + +class NodeTexChecker : public fn::MultiFunction { + public: + NodeTexChecker() + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"Checker"}; + signature.single_input("Vector"); + signature.single_input("Color1"); + signature.single_input("Color2"); + signature.single_input("Scale"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(blender::IndexMask mask, + fn::MFParams params, + fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &color1 = params.readonly_single_input( + 1, "Color1"); + const VArray &color2 = params.readonly_single_input( + 2, "Color2"); + const VArray &scale = params.readonly_single_input(3, "Scale"); + MutableSpan r_color = + params.uninitialized_single_output_if_required(4, "Color"); + MutableSpan r_fac = params.uninitialized_single_output(5, "Fac"); + + for (int64_t i : mask) { + /* Avoid precision issues on unit coordinates. */ + const float3 p = (vector[i] * scale[i] + 0.000001f) * 0.999999f; + + const int xi = abs((int)(floorf(p.x))); + const int yi = abs((int)(floorf(p.y))); + const int zi = abs((int)(floorf(p.z))); + + r_fac[i] = ((xi % 2 == yi % 2) == (zi % 2)) ? 1.0f : 0.0f; + } + + if (!r_color.is_empty()) { + for (int64_t i : mask) { + r_color[i] = (r_fac[i] == 1.0f) ? color1[i] : color2[i]; + } + } + } +}; + +static void sh_node_tex_checker_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + static NodeTexChecker fn; + builder.set_matching_fn(fn); +} + +} // namespace blender::nodes + +void register_node_type_sh_tex_checker(void) +{ + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_CHECKER, "Checker Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_checker_declare; + node_type_init(&ntype, node_shader_init_tex_checker); + node_type_storage( + &ntype, "NodeTexChecker", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_checker); + ntype.build_multi_function = blender::nodes::sh_node_tex_checker_build_multi_function; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc index 0c0d75179a9..33832c42b3c 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_gradient.cc @@ -19,27 +19,17 @@ #include "../node_shader_util.h" -/* **************** BLEND ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_gradient_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, +static void sh_node_tex_gradient_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).hide_value().implicit_field(); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Fac")).no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_gradient_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_gradient(bNodeTree *UNUSED(ntree), bNode *node) { @@ -66,17 +56,121 @@ static int node_shader_gpu_tex_gradient(GPUMaterial *mat, return GPU_stack_link(mat, node, "node_tex_gradient", in, out, GPU_constant(&gradient_type)); } -/* node type definition */ +namespace blender::nodes { + +class GradientFunction : public fn::MultiFunction { + private: + int gradient_type_; + + public: + GradientFunction(int gradient_type) : gradient_type_(gradient_type) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"GradientFunction"}; + signature.single_input("Vector"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + + MutableSpan r_color = + params.uninitialized_single_output_if_required(1, "Color"); + MutableSpan fac = params.uninitialized_single_output(2, "Fac"); + + const bool compute_color = !r_color.is_empty(); + + switch (gradient_type_) { + case SHD_BLEND_LINEAR: { + for (int64_t i : mask) { + fac[i] = vector[i].x; + } + break; + } + case SHD_BLEND_QUADRATIC: { + for (int64_t i : mask) { + const float r = std::max(vector[i].x, 0.0f); + fac[i] = r * r; + } + break; + } + case SHD_BLEND_EASING: { + for (int64_t i : mask) { + const float r = std::min(std::max(vector[i].x, 0.0f), 1.0f); + const float t = r * r; + fac[i] = (3.0f * t - 2.0f * t * r); + } + break; + } + case SHD_BLEND_DIAGONAL: { + for (int64_t i : mask) { + fac[i] = (vector[i].x + vector[i].y) * 0.5f; + } + break; + } + case SHD_BLEND_RADIAL: { + for (int64_t i : mask) { + fac[i] = atan2f(vector[i].y, vector[i].x) / (M_PI * 2.0f) + 0.5f; + } + break; + } + case SHD_BLEND_QUADRATIC_SPHERE: { + for (int64_t i : mask) { + /* Bias a little bit for the case where input is a unit length vector, + * to get exactly zero instead of a small random value depending + * on float precision. */ + const float r = std::max(0.999999f - vector[i].length(), 0.0f); + fac[i] = r * r; + } + break; + } + case SHD_BLEND_SPHERICAL: { + for (int64_t i : mask) { + /* Bias a little bit for the case where input is a unit length vector, + * to get exactly zero instead of a small random value depending + * on float precision. */ + fac[i] = std::max(0.999999f - vector[i].length(), 0.0f); + } + break; + } + } + if (compute_color) { + for (int64_t i : mask) { + r_color[i] = ColorGeometry4f(fac[i], fac[i], fac[i], 1.0f); + } + } + } +}; + +static void sh_node_gradient_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexGradient *tex = (NodeTexGradient *)node.storage; + builder.construct_and_set_matching_fn(tex->gradient_type); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_gradient(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_gradient_in, sh_node_tex_gradient_out); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_GRADIENT, "Gradient Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_gradient_declare; node_type_init(&ntype, node_shader_init_tex_gradient); node_type_storage( &ntype, "NodeTexGradient", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_gradient); + ntype.build_multi_function = blender::nodes::sh_node_gradient_tex_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.c b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc similarity index 88% rename from source/blender/nodes/shader/nodes/node_shader_tex_image.c rename to source/blender/nodes/shader/nodes/node_shader_tex_image.cc index 09d06c35a5f..f20fc85cbe0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_image.c +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -19,31 +19,21 @@ #include "../node_shader_util.h" -/* **************** OUTPUT ******************** */ +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_image_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {-1, ""}, +static void sh_node_tex_image_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).implicit_field(); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Alpha")).no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_image_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Alpha"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +}; // namespace blender::nodes static void node_shader_init_tex_image(bNodeTree *UNUSED(ntree), bNode *node) { - NodeTexImage *tex = MEM_callocN(sizeof(NodeTexImage), "NodeTexImage"); + NodeTexImage *tex = (NodeTexImage *)MEM_callocN(sizeof(NodeTexImage), "NodeTexImage"); BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); BKE_texture_colormapping_default(&tex->base.color_mapping); BKE_imageuser_default(&tex->iuser); @@ -58,12 +48,12 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, GPUNodeStack *out) { Image *ima = (Image *)node->id; - NodeTexImage *tex = node->storage; + NodeTexImage *tex = (NodeTexImage *)node->storage; /* We get the image user from the original node, since GPU image keeps * a pointer to it and the dependency refreshes the original. */ bNode *node_original = node->original ? node->original : node; - NodeTexImage *tex_original = node_original->storage; + NodeTexImage *tex_original = (NodeTexImage *)node_original->storage; ImageUser *iuser = &tex_original->iuser; if (!ima) { @@ -78,7 +68,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, node_shader_gpu_tex_mapping(mat, node, in, out); - eGPUSamplerState sampler_state = 0; + eGPUSamplerState sampler_state = GPU_SAMPLER_DEFAULT; switch (tex->extension) { case SHD_IMAGE_EXTENSION_REPEAT: @@ -94,7 +84,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat, if (tex->interpolation != SHD_INTERP_CLOSEST) { sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; /* TODO(fclem): For now assume mipmap is always enabled. */ - sampler_state |= true ? GPU_SAMPLER_MIPMAP : 0; + sampler_state |= GPU_SAMPLER_MIPMAP; } const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART); @@ -189,7 +179,7 @@ void register_node_type_sh_tex_image(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_TEX_IMAGE, "Image Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_image_in, sh_node_tex_image_out); + ntype.declare = blender::nodes::sh_node_tex_image_declare; node_type_init(&ntype, node_shader_init_tex_image); node_type_storage( &ntype, "NodeTexImage", node_free_standard_storage, node_copy_standard_storage); diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.c b/source/blender/nodes/shader/nodes/node_shader_tex_magic.c deleted file mode 100644 index 51721f8bb09..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_magic.c +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** OUTPUT ******************** */ - -static bNodeSocketTemplate sh_node_tex_magic_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Distortion"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_magic_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_magic(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexMagic *tex = MEM_callocN(sizeof(NodeTexMagic), "NodeTexMagic"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->depth = 2; - - node->storage = tex; -} - -static int node_shader_gpu_tex_magic(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - NodeTexMagic *tex = (NodeTexMagic *)node->storage; - float depth = tex->depth; - - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - return GPU_stack_link(mat, node, "node_tex_magic", in, out, GPU_constant(&depth)); -} - -/* node type definition */ -void register_node_type_sh_tex_magic(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_MAGIC, "Magic Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_magic_in, sh_node_tex_magic_out); - node_type_init(&ntype, node_shader_init_tex_magic); - node_type_storage( - &ntype, "NodeTexMagic", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_magic); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc new file mode 100644 index 00000000000..62e68d53d03 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_magic.cc @@ -0,0 +1,197 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +namespace blender::nodes { + +static void sh_node_tex_magic_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).implicit_field(); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Fac")).no_muted_links(); +}; + +} // namespace blender::nodes + +static void node_shader_init_tex_magic(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexMagic *tex = (NodeTexMagic *)MEM_callocN(sizeof(NodeTexMagic), "NodeTexMagic"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->depth = 2; + + node->storage = tex; +} + +static int node_shader_gpu_tex_magic(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + NodeTexMagic *tex = (NodeTexMagic *)node->storage; + float depth = tex->depth; + + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + return GPU_stack_link(mat, node, "node_tex_magic", in, out, GPU_constant(&depth)); +} + +namespace blender::nodes { + +class MagicFunction : public fn::MultiFunction { + private: + int depth_; + + public: + MagicFunction(int depth) : depth_(depth) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"MagicFunction"}; + signature.single_input("Vector"); + signature.single_input("Scale"); + signature.single_input("Distortion"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &scale = params.readonly_single_input(1, "Scale"); + const VArray &distortion = params.readonly_single_input(2, "Distortion"); + + MutableSpan r_color = params.uninitialized_single_output( + 3, "Color"); + MutableSpan r_fac = params.uninitialized_single_output_if_required(4, "Fac"); + + const bool compute_factor = !r_fac.is_empty(); + + for (int64_t i : mask) { + const float3 co = vector[i] * scale[i]; + const float distort = distortion[i]; + float x = sinf((co[0] + co[1] + co[2]) * 5.0f); + float y = cosf((-co[0] + co[1] - co[2]) * 5.0f); + float z = -cosf((-co[0] - co[1] + co[2]) * 5.0f); + + if (depth_ > 0) { + x *= distort; + y *= distort; + z *= distort; + y = -cosf(x - y + z); + y *= distort; + + if (depth_ > 1) { + x = cosf(x - y - z); + x *= distort; + + if (depth_ > 2) { + z = sinf(-x - y - z); + z *= distort; + + if (depth_ > 3) { + x = -cosf(-x + y - z); + x *= distort; + + if (depth_ > 4) { + y = -sinf(-x + y + z); + y *= distort; + + if (depth_ > 5) { + y = -cosf(-x + y + z); + y *= distort; + + if (depth_ > 6) { + x = cosf(x + y + z); + x *= distort; + + if (depth_ > 7) { + z = sinf(x + y - z); + z *= distort; + + if (depth_ > 8) { + x = -cosf(-x - y + z); + x *= distort; + + if (depth_ > 9) { + y = -sinf(x - y + z); + y *= distort; + } + } + } + } + } + } + } + } + } + } + + if (distort != 0.0f) { + const float d = distort * 2.0f; + x /= d; + y /= d; + z /= d; + } + + r_color[i] = ColorGeometry4f(0.5f - x, 0.5f - y, 0.5f - z, 1.0f); + } + if (compute_factor) { + for (int64_t i : mask) { + r_fac[i] = (r_color[i].r + r_color[i].g + r_color[i].b) * (1.0f / 3.0f); + } + } + } +}; + +static void sh_node_magic_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexMagic *tex = (NodeTexMagic *)node.storage; + builder.construct_and_set_matching_fn(tex->depth); +} + +} // namespace blender::nodes + +void register_node_type_sh_tex_magic(void) +{ + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_MAGIC, "Magic Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_magic_declare; + node_type_init(&ntype, node_shader_init_tex_magic); + node_type_storage( + &ntype, "NodeTexMagic", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_magic); + ntype.build_multi_function = blender::nodes::sh_node_magic_tex_build_multi_function; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc index f5e9aef3aad..81f9cd735eb 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_musgrave.cc @@ -19,33 +19,25 @@ #include "../node_shader_util.h" -/* **************** MUSGRAVE ******************** */ +#include "BLI_noise.hh" -static bNodeSocketTemplate sh_node_tex_musgrave_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Dimension"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Lacunarity"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {SOCK_FLOAT, N_("Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Gain"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_tex_musgrave_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).hide_value().implicit_field(); + b.add_input(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); + b.add_input(N_("Dimension")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input(N_("Lacunarity")).min(0.0f).max(1000.0f).default_value(2.0f); + b.add_input(N_("Offset")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Gain")).min(0.0f).max(1000.0f).default_value(1.0f); + b.add_output(N_("Fac")).no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_musgrave_out[] = { - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *node) { @@ -134,18 +126,422 @@ static void node_shader_update_tex_musgrave(bNodeTree *UNUSED(ntree), bNode *nod node_sock_label(outFacSock, "Height"); } +namespace blender::nodes { + +class MusgraveFunction : public fn::MultiFunction { + private: + const int dimensions_; + const int musgrave_type_; + + public: + MusgraveFunction(const int dimensions, const int musgrave_type) + : dimensions_(dimensions), musgrave_type_(musgrave_type) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(musgrave_type >= 0 && musgrave_type <= 4); + static std::array signatures{ + create_signature(1, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_FBM), + create_signature(2, SHD_MUSGRAVE_FBM), + create_signature(3, SHD_MUSGRAVE_FBM), + create_signature(4, SHD_MUSGRAVE_FBM), + + create_signature(1, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(2, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(3, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + create_signature(4, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL), + + create_signature(1, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(2, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(3, SHD_MUSGRAVE_HETERO_TERRAIN), + create_signature(4, SHD_MUSGRAVE_HETERO_TERRAIN), + }; + this->set_signature(&signatures[dimensions + musgrave_type * 4 - 1]); + } + + static fn::MFSignature create_signature(const int dimensions, const int musgrave_type) + { + fn::MFSignatureBuilder signature{"Musgrave"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + signature.single_input("Detail"); + signature.single_input("Dimension"); + signature.single_input("Lacunarity"); + if (ELEM(musgrave_type, + SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, + SHD_MUSGRAVE_HYBRID_MULTIFRACTAL, + SHD_MUSGRAVE_HETERO_TERRAIN)) { + signature.single_input("Offset"); + } + if (ELEM(musgrave_type, SHD_MUSGRAVE_RIDGED_MULTIFRACTAL, SHD_MUSGRAVE_HYBRID_MULTIFRACTAL)) { + signature.single_input("Gain"); + } + + signature.single_output("Fac"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_detail = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Detail"); + }; + auto get_dimension = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Dimension"); + }; + auto get_lacunarity = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Lacunarity"); + }; + auto get_offset = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Offset"); + }; + auto get_gain = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Gain"); + }; + + auto get_r_factor = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Fac"); + }; + + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + const VArray &scale = get_scale(param++); + const VArray &detail = get_detail(param++); + const VArray &dimension = get_dimension(param++); + const VArray &lacunarity = get_lacunarity(param++); + + switch (musgrave_type_) { + case SHD_MUSGRAVE_MULTIFRACTAL: { + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_multi_fractal( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_RIDGED_MULTIFRACTAL: { + const VArray &offset = get_offset(param++); + const VArray &gain = get_gain(param++); + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_ridged_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_HYBRID_MULTIFRACTAL: { + const VArray &offset = get_offset(param++); + const VArray &gain = get_gain(param++); + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_hybrid_multi_fractal( + position, dimension[i], lacunarity[i], detail[i], offset[i], gain[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_FBM: { + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_fBm( + position, dimension[i], lacunarity[i], detail[i]); + } + } + break; + } + } + break; + } + case SHD_MUSGRAVE_HETERO_TERRAIN: { + const VArray &offset = get_offset(param++); + MutableSpan r_factor = get_r_factor(param++); + const bool compute_factor = !r_factor.is_empty(); + switch (dimensions_) { + case 1: { + const VArray &w = get_w(0); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 2: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float2 position = float2(pxyz[0], pxyz[1]); + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 3: { + const VArray &vector = get_vector(0); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + case 4: { + const VArray &vector = get_vector(0); + const VArray &w = get_w(1); + if (compute_factor) { + for (int64_t i : mask) { + const float3 pxyz = vector[i] * scale[i]; + const float pw = w[i] * scale[i]; + const float4 position{pxyz[0], pxyz[1], pxyz[2], pw}; + r_factor[i] = noise::musgrave_hetero_terrain( + position, dimension[i], lacunarity[i], detail[i], offset[i]); + } + } + break; + } + } + break; + } + } + } +}; // namespace blender::nodes + +static void sh_node_musgrave_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexMusgrave *tex = (NodeTexMusgrave *)node.storage; + builder.construct_and_set_matching_fn(tex->dimensions, tex->musgrave_type); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_musgrave(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_musgrave_in, sh_node_tex_musgrave_out); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_MUSGRAVE, "Musgrave Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_musgrave_declare; node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); node_type_init(&ntype, node_shader_init_tex_musgrave); node_type_storage( &ntype, "NodeTexMusgrave", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_musgrave); node_type_update(&ntype, node_shader_update_tex_musgrave); + ntype.build_multi_function = blender::nodes::sh_node_musgrave_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc index de8e0916f4d..d28095edb96 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_noise.cc @@ -19,32 +19,28 @@ #include "../node_shader_util.h" -/* **************** NOISE ******************** */ +#include "BLI_noise.hh" -static bNodeSocketTemplate sh_node_tex_noise_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_tex_noise_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).implicit_field(); + b.add_input(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); + b.add_input(N_("Roughness")) + .min(0.0f) + .max(1.0f) + .default_value(0.5f) + .subtype(PROP_FACTOR); + b.add_input(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output(N_("Fac")).no_muted_links(); + b.add_output(N_("Color")).no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_noise_out[] = { - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) { @@ -90,18 +86,173 @@ static void node_shader_update_tex_noise(bNodeTree *UNUSED(ntree), bNode *node) nodeSetSocketAvailability(sockW, tex->dimensions == 1 || tex->dimensions == 4); } +namespace blender::nodes { + +class NoiseFunction : public fn::MultiFunction { + private: + int dimensions_; + + public: + NoiseFunction(int dimensions) : dimensions_(dimensions) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + static std::array signatures{ + create_signature(1), + create_signature(2), + create_signature(3), + create_signature(4), + }; + this->set_signature(&signatures[dimensions - 1]); + } + + static fn::MFSignature create_signature(int dimensions) + { + fn::MFSignatureBuilder signature{"Noise"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + + signature.single_input("Scale"); + signature.single_input("Detail"); + signature.single_input("Roughness"); + signature.single_input("Distortion"); + + signature.single_output("Fac"); + signature.single_output("Color"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + const VArray &scale = params.readonly_single_input(param++, "Scale"); + const VArray &detail = params.readonly_single_input(param++, "Detail"); + const VArray &roughness = params.readonly_single_input(param++, "Roughness"); + const VArray &distortion = params.readonly_single_input(param++, "Distortion"); + + MutableSpan r_factor = params.uninitialized_single_output_if_required(param++, + "Fac"); + MutableSpan r_color = + params.uninitialized_single_output_if_required(param++, "Color"); + + const bool compute_factor = !r_factor.is_empty(); + const bool compute_color = !r_color.is_empty(); + + switch (dimensions_) { + case 1: { + const VArray &w = params.readonly_single_input(0, "W"); + if (compute_factor) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float position = w[i] * scale[i]; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + case 2: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_factor) { + for (int64_t i : mask) { + const float2 position = vector[i] * scale[i]; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float2 position = vector[i] * scale[i]; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + case 3: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float3 position = vector[i] * scale[i]; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + case 4: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &w = params.readonly_single_input(1, "W"); + if (compute_factor) { + for (int64_t i : mask) { + const float3 position_vector = vector[i] * scale[i]; + const float position_w = w[i] * scale[i]; + const float4 position{ + position_vector[0], position_vector[1], position_vector[2], position_w}; + r_factor[i] = noise::perlin_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + } + } + if (compute_color) { + for (int64_t i : mask) { + const float3 position_vector = vector[i] * scale[i]; + const float position_w = w[i] * scale[i]; + const float4 position{ + position_vector[0], position_vector[1], position_vector[2], position_w}; + const float3 c = noise::perlin_float3_fractal_distorted( + position, detail[i], roughness[i], distortion[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + break; + } + } + } +}; + +static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexNoise *tex = (NodeTexNoise *)node.storage; + builder.construct_and_set_matching_fn(tex->dimensions); +} + +} // namespace blender::nodes + /* node type definition */ void register_node_type_sh_tex_noise(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_noise_in, sh_node_tex_noise_out); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_NOISE, "Noise Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_noise_declare; node_type_init(&ntype, node_shader_init_tex_noise); node_type_storage( &ntype, "NodeTexNoise", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_noise); node_type_update(&ntype, node_shader_update_tex_noise); + ntype.build_multi_function = blender::nodes::sh_node_noise_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc index 1cc715d99ea..422268b98c3 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_voronoi.cc @@ -19,53 +19,35 @@ #include "../node_shader_util.h" -/* **************** VORONOI ******************** */ +#include "BLI_noise.hh" -static bNodeSocketTemplate sh_node_tex_voronoi_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Smoothness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Exponent"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 32.0f}, - {SOCK_FLOAT, N_("Randomness"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_tex_voronoi_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).hide_value().implicit_field(); + b.add_input(N_("W")).min(-1000.0f).max(1000.0f); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Smoothness")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_input(N_("Exponent")).min(0.0f).max(32.0f).default_value(0.5f); + b.add_input(N_("Randomness")) + .min(0.0f) + .max(1.0f) + .default_value(1.0f) + .subtype(PROP_FACTOR); + b.add_output(N_("Distance")).no_muted_links(); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Position")).no_muted_links(); + b.add_output(N_("W")).no_muted_links(); + b.add_output(N_("Radius")).no_muted_links(); }; -static bNodeSocketTemplate sh_node_tex_voronoi_out[] = { - {SOCK_FLOAT, - N_("Distance"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_VECTOR, - N_("Position"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Radius"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_NONE, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; +} // namespace blender::nodes static void node_shader_init_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node) { @@ -163,6 +145,7 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); nodeSetSocketAvailability(inSmoothnessSock, tex->feature == SHD_VORONOI_SMOOTH_F1); + nodeSetSocketAvailability(outDistanceSock, tex->feature != SHD_VORONOI_N_SPHERE_RADIUS); nodeSetSocketAvailability(outColorSock, tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && @@ -174,21 +157,1163 @@ static void node_shader_update_tex_voronoi(bNodeTree *UNUSED(ntree), bNode *node nodeSetSocketAvailability(outWSock, tex->feature != SHD_VORONOI_DISTANCE_TO_EDGE && tex->feature != SHD_VORONOI_N_SPHERE_RADIUS && - (tex->dimensions == 1 || tex->dimensions == 4)); + (ELEM(tex->dimensions, 1, 4))); nodeSetSocketAvailability(outRadiusSock, tex->feature == SHD_VORONOI_N_SPHERE_RADIUS); } +namespace blender::nodes { + +class VoronoiMinowskiFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiMinowskiFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 2 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 2); + static std::array signatures{ + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[(dimensions - 1) + feature * 3 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_minowski"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input("Smoothness"); + } + signature.single_input("Exponent"); + signature.single_input("Randomness"); + signature.single_output("Distance"); + signature.single_output("Color"); + + if (dimensions != 1) { + signature.single_output("Position"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_output("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Smoothness"); + }; + auto get_exponent = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Exponent"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(vector[i] * scale[i], + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, + exponent[i], + rand, + SHD_VORONOI_F1, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2(p, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &exponent = get_exponent(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1(p, + smth, + exponent[i], + rand, + SHD_VORONOI_MINKOWSKI, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + } + break; + } + } + } +}; + +class VoronoiMetricFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + int metric_; + + public: + VoronoiMetricFunction(int dimensions, int feature, int metric) + : dimensions_(dimensions), feature_(feature), metric_(metric) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 0 && feature <= 4); + static std::array signatures{ + create_signature(1, SHD_VORONOI_F1), + create_signature(2, SHD_VORONOI_F1), + create_signature(3, SHD_VORONOI_F1), + create_signature(4, SHD_VORONOI_F1), + + create_signature(1, SHD_VORONOI_F2), + create_signature(2, SHD_VORONOI_F2), + create_signature(3, SHD_VORONOI_F2), + create_signature(4, SHD_VORONOI_F2), + + create_signature(1, SHD_VORONOI_SMOOTH_F1), + create_signature(2, SHD_VORONOI_SMOOTH_F1), + create_signature(3, SHD_VORONOI_SMOOTH_F1), + create_signature(4, SHD_VORONOI_SMOOTH_F1), + }; + this->set_signature(&signatures[dimensions + feature * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_metric"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + if (feature == SHD_VORONOI_SMOOTH_F1) { + signature.single_input("Smoothness"); + } + signature.single_input("Randomness"); + signature.single_output("Distance"); + signature.single_output("Color"); + + if (dimensions != 1) { + signature.single_output("Position"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_output("W"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_smoothness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Smoothness"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Distance"); + }; + auto get_r_color = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Color"); + }; + auto get_r_position = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "Position"); + }; + auto get_r_w = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output_if_required(param_index, "W"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(p, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float p = w[i] * scale[i]; + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(p, + smth, + rand, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_w ? &r_w[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_w) { + r_w[i] = safe_divide(r_w[i], scale[i]); + } + } + break; + } + } + break; + } + case 2: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f1(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_f2(float2(vector[i].x, vector[i].y) * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + float2 pos; + noise::voronoi_smooth_f1(float2(vector[i].x, vector[i].y) * scale[i], + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + pos = float2::safe_divide(pos, scale[i]); + r_position[i] = float3(pos.x, pos.y, 0.0f); + } + } + break; + } + } + break; + } + case 3: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f1(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_f2(vector[i] * scale[i], + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + { + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + float3 col; + noise::voronoi_smooth_f1(vector[i] * scale[i], + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position ? &r_position[i] : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position) { + r_position[i] = float3::safe_divide(r_position[i], scale[i]); + } + } + } + + break; + } + } + break; + } + case 4: { + switch (feature_) { + case SHD_VORONOI_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f1(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_F2: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_f2(p, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + case SHD_VORONOI_SMOOTH_F1: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &smoothness = get_smoothness(param++); + const VArray &randomness = get_randomness(param++); + MutableSpan r_distance = get_r_distance(param++); + MutableSpan r_color = get_r_color(param++); + MutableSpan r_position = get_r_position(param++); + MutableSpan r_w = get_r_w(param++); + const bool calc_distance = !r_distance.is_empty(); + const bool calc_color = !r_color.is_empty(); + const bool calc_position = !r_position.is_empty(); + const bool calc_w = !r_w.is_empty(); + for (int64_t i : mask) { + const float smth = std::min(std::max(smoothness[i] / 2.0f, 0.0f), 0.5f); + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + float3 col; + float4 pos; + noise::voronoi_smooth_f1(p, + smth, + 0.0f, + rand, + metric_, + calc_distance ? &r_distance[i] : nullptr, + calc_color ? &col : nullptr, + calc_position || calc_w ? &pos : nullptr); + if (calc_color) { + r_color[i] = ColorGeometry4f(col[0], col[1], col[2], 1.0f); + } + if (calc_position || calc_w) { + pos = float4::safe_divide(pos, scale[i]); + if (calc_position) { + r_position[i] = float3(pos.x, pos.y, pos.z); + } + if (calc_w) { + r_w[i] = pos.w; + } + } + } + break; + } + } + break; + } + } + } +}; + +class VoronoiEdgeFunction : public fn::MultiFunction { + private: + int dimensions_; + int feature_; + + public: + VoronoiEdgeFunction(int dimensions, int feature) : dimensions_(dimensions), feature_(feature) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + BLI_assert(feature >= 3 && feature <= 4); + static std::array signatures{ + create_signature(1, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(2, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(3, SHD_VORONOI_DISTANCE_TO_EDGE), + create_signature(4, SHD_VORONOI_DISTANCE_TO_EDGE), + + create_signature(1, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(2, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(3, SHD_VORONOI_N_SPHERE_RADIUS), + create_signature(4, SHD_VORONOI_N_SPHERE_RADIUS), + }; + this->set_signature(&signatures[dimensions + (feature - 3) * 4 - 1]); + } + + static fn::MFSignature create_signature(int dimensions, int feature) + { + fn::MFSignatureBuilder signature{"voronoi_edge"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + signature.single_input("Scale"); + signature.single_input("Randomness"); + + if (feature == SHD_VORONOI_DISTANCE_TO_EDGE) { + signature.single_output("Distance"); + } + if (feature == SHD_VORONOI_N_SPHERE_RADIUS) { + signature.single_output("Radius"); + } + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + auto get_vector = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Vector"); + }; + auto get_w = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "W"); + }; + auto get_scale = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Scale"); + }; + auto get_randomness = [&](int param_index) -> const VArray & { + return params.readonly_single_input(param_index, "Randomness"); + }; + auto get_r_distance = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Distance"); + }; + auto get_r_radius = [&](int param_index) -> MutableSpan { + return params.uninitialized_single_output(param_index, "Radius"); + }; + + int param = 0; + switch (dimensions_) { + case 1: { + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float p = w[i] * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 2: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float2 p = float2(vector[i].x, vector[i].y) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + case 3: { + const VArray &vector = get_vector(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_distance_to_edge(vector[i] * scale[i], rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + noise::voronoi_n_sphere_radius(vector[i] * scale[i], rand, &r_radius[i]); + } + break; + } + } + break; + } + case 4: { + const VArray &vector = get_vector(param++); + const VArray &w = get_w(param++); + const VArray &scale = get_scale(param++); + const VArray &randomness = get_randomness(param++); + switch (feature_) { + case SHD_VORONOI_DISTANCE_TO_EDGE: { + MutableSpan r_distance = get_r_distance(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_distance_to_edge(p, rand, &r_distance[i]); + } + break; + } + case SHD_VORONOI_N_SPHERE_RADIUS: { + MutableSpan r_radius = get_r_radius(param++); + for (int64_t i : mask) { + const float rand = std::min(std::max(randomness[i], 0.0f), 1.0f); + const float4 p = float4(vector[i].x, vector[i].y, vector[i].z, w[i]) * scale[i]; + noise::voronoi_n_sphere_radius(p, rand, &r_radius[i]); + } + break; + } + } + break; + } + } + }; +}; + +static void sh_node_voronoi_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexVoronoi *tex = (NodeTexVoronoi *)node.storage; + bool minowski = (tex->distance == SHD_VORONOI_MINKOWSKI && tex->dimensions != 1 && + !ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS)); + bool dist_radius = ELEM(tex->feature, SHD_VORONOI_DISTANCE_TO_EDGE, SHD_VORONOI_N_SPHERE_RADIUS); + if (dist_radius) { + builder.construct_and_set_matching_fn(tex->dimensions, tex->feature); + } + else if (minowski) { + builder.construct_and_set_matching_fn(tex->dimensions, tex->feature); + } + else { + builder.construct_and_set_matching_fn( + tex->dimensions, tex->feature, tex->distance); + } +} + +} // namespace blender::nodes + void register_node_type_sh_tex_voronoi(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_voronoi_in, sh_node_tex_voronoi_out); + sh_fn_node_type_base(&ntype, SH_NODE_TEX_VORONOI, "Voronoi Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_voronoi_declare; node_type_init(&ntype, node_shader_init_tex_voronoi); node_type_storage( &ntype, "NodeTexVoronoi", node_free_standard_storage, node_copy_standard_storage); node_type_gpu(&ntype, node_shader_gpu_tex_voronoi); node_type_update(&ntype, node_shader_update_tex_voronoi); + ntype.build_multi_function = blender::nodes::sh_node_voronoi_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c b/source/blender/nodes/shader/nodes/node_shader_tex_wave.c deleted file mode 100644 index bba568ed5b7..00000000000 --- a/source/blender/nodes/shader/nodes/node_shader_tex_wave.c +++ /dev/null @@ -1,101 +0,0 @@ -/* - * 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) 2005 Blender Foundation. - * All rights reserved. - */ - -#include "../node_shader_util.h" - -/* **************** WAVE ******************** */ - -static bNodeSocketTemplate sh_node_tex_wave_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_FLOAT, N_("Scale"), 5.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Distortion"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail"), 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 16.0f}, - {SOCK_FLOAT, N_("Detail Scale"), 1.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {SOCK_FLOAT, N_("Detail Roughness"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {SOCK_FLOAT, N_("Phase Offset"), 0.0f, 0.0f, 0.0f, 0.0f, -1000.0f, 1000.0f}, - {-1, ""}, -}; - -static bNodeSocketTemplate sh_node_tex_wave_out[] = { - {SOCK_RGBA, N_("Color"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_NO_INTERNAL_LINK}, - {SOCK_FLOAT, - N_("Fac"), - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 0.0f, - 1.0f, - PROP_FACTOR, - SOCK_NO_INTERNAL_LINK}, - {-1, ""}, -}; - -static void node_shader_init_tex_wave(bNodeTree *UNUSED(ntree), bNode *node) -{ - NodeTexWave *tex = MEM_callocN(sizeof(NodeTexWave), "NodeTexWave"); - BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); - BKE_texture_colormapping_default(&tex->base.color_mapping); - tex->wave_type = SHD_WAVE_BANDS; - tex->bands_direction = SHD_WAVE_BANDS_DIRECTION_X; - tex->rings_direction = SHD_WAVE_RINGS_DIRECTION_X; - tex->wave_profile = SHD_WAVE_PROFILE_SIN; - node->storage = tex; -} - -static int node_shader_gpu_tex_wave(GPUMaterial *mat, - bNode *node, - bNodeExecData *UNUSED(execdata), - GPUNodeStack *in, - GPUNodeStack *out) -{ - node_shader_gpu_default_tex_coord(mat, node, &in[0].link); - node_shader_gpu_tex_mapping(mat, node, in, out); - - NodeTexWave *tex = (NodeTexWave *)node->storage; - float wave_type = tex->wave_type; - float bands_direction = tex->bands_direction; - float rings_direction = tex->rings_direction; - float wave_profile = tex->wave_profile; - - return GPU_stack_link(mat, - node, - "node_tex_wave", - in, - out, - GPU_constant(&wave_type), - GPU_constant(&bands_direction), - GPU_constant(&rings_direction), - GPU_constant(&wave_profile)); -} - -/* node type definition */ -void register_node_type_sh_tex_wave(void) -{ - static bNodeType ntype; - - sh_node_type_base(&ntype, SH_NODE_TEX_WAVE, "Wave Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_wave_in, sh_node_tex_wave_out); - node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); - node_type_init(&ntype, node_shader_init_tex_wave); - node_type_storage(&ntype, "NodeTexWave", node_free_standard_storage, node_copy_standard_storage); - node_type_gpu(&ntype, node_shader_gpu_tex_wave); - - nodeRegisterType(&ntype); -} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc new file mode 100644 index 00000000000..fe534c605e9 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_wave.cc @@ -0,0 +1,234 @@ +/* + * 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) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +#include "BLI_noise.hh" + +namespace blender::nodes { + +static void sh_node_tex_wave_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).implicit_field(); + b.add_input(N_("Scale")).min(-1000.0f).max(1000.0f).default_value(5.0f); + b.add_input(N_("Distortion")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_input(N_("Detail")).min(0.0f).max(15.0f).default_value(2.0f); + b.add_input(N_("Detail Scale")).min(-1000.0f).max(1000.0f).default_value(1.0f); + b.add_input(N_("Detail Roughness")) + .min(0.0f) + .max(1.0f) + .default_value(0.5f) + .subtype(PROP_FACTOR); + b.add_input(N_("Phase Offset")).min(-1000.0f).max(1000.0f).default_value(0.0f); + b.add_output(N_("Color")).no_muted_links(); + b.add_output(N_("Fac")).no_muted_links(); +}; + +} // namespace blender::nodes + +static void node_shader_init_tex_wave(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexWave *tex = (NodeTexWave *)MEM_callocN(sizeof(NodeTexWave), "NodeTexWave"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + tex->wave_type = SHD_WAVE_BANDS; + tex->bands_direction = SHD_WAVE_BANDS_DIRECTION_X; + tex->rings_direction = SHD_WAVE_RINGS_DIRECTION_X; + tex->wave_profile = SHD_WAVE_PROFILE_SIN; + node->storage = tex; +} + +static int node_shader_gpu_tex_wave(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + node_shader_gpu_default_tex_coord(mat, node, &in[0].link); + node_shader_gpu_tex_mapping(mat, node, in, out); + + NodeTexWave *tex = (NodeTexWave *)node->storage; + float wave_type = tex->wave_type; + float bands_direction = tex->bands_direction; + float rings_direction = tex->rings_direction; + float wave_profile = tex->wave_profile; + + return GPU_stack_link(mat, + node, + "node_tex_wave", + in, + out, + GPU_constant(&wave_type), + GPU_constant(&bands_direction), + GPU_constant(&rings_direction), + GPU_constant(&wave_profile)); +} + +namespace blender::nodes { + +class WaveFunction : public fn::MultiFunction { + private: + int wave_type_; + int bands_direction_; + int rings_direction_; + int wave_profile_; + + public: + WaveFunction(int wave_type, int bands_direction, int rings_direction, int wave_profile) + : wave_type_(wave_type), + bands_direction_(bands_direction), + rings_direction_(rings_direction), + wave_profile_(wave_profile) + { + static fn::MFSignature signature = create_signature(); + this->set_signature(&signature); + } + + static fn::MFSignature create_signature() + { + fn::MFSignatureBuilder signature{"MagicFunction"}; + signature.single_input("Vector"); + signature.single_input("Scale"); + signature.single_input("Distortion"); + signature.single_input("Detail"); + signature.single_input("Detail Scale"); + signature.single_input("Detail Roughness"); + signature.single_input("Phase Offset"); + signature.single_output("Color"); + signature.single_output("Fac"); + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &scale = params.readonly_single_input(1, "Scale"); + const VArray &distortion = params.readonly_single_input(2, "Distortion"); + const VArray &detail = params.readonly_single_input(3, "Detail"); + const VArray &dscale = params.readonly_single_input(4, "Detail Scale"); + const VArray &droughness = params.readonly_single_input(5, "Detail Roughness"); + const VArray &phase = params.readonly_single_input(6, "Phase Offset"); + + MutableSpan r_color = + params.uninitialized_single_output_if_required(7, "Color"); + MutableSpan r_fac = params.uninitialized_single_output(8, "Fac"); + + for (int64_t i : mask) { + + float3 p = vector[i] * scale[i]; + /* Prevent precision issues on unit coordinates. */ + p = (p + 0.000001f) * 0.999999f; + + float n = 0.0f; + float val = 0.0f; + + switch (wave_type_) { + case SHD_WAVE_BANDS: + switch (bands_direction_) { + case SHD_WAVE_BANDS_DIRECTION_X: + n = p.x * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_Y: + n = p.y * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_Z: + n = p.z * 20.0f; + break; + case SHD_WAVE_BANDS_DIRECTION_DIAGONAL: + n = (p.x + p.y + p.z) * 10.0f; + break; + } + break; + case SHD_WAVE_RINGS: + float3 rp = p; + switch (rings_direction_) { + case SHD_WAVE_RINGS_DIRECTION_X: + rp *= float3(0.0f, 1.0f, 1.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_Y: + rp *= float3(1.0f, 0.0f, 1.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_Z: + rp *= float3(1.0f, 1.0f, 0.0f); + break; + case SHD_WAVE_RINGS_DIRECTION_SPHERICAL: + /* Ignore. */ + break; + } + n = len_v3(rp) * 20.0f; + break; + } + + n += phase[i]; + + if (distortion[i] != 0.0f) { + n += distortion[i] * + (noise::perlin_fractal(p * dscale[i], detail[i], droughness[i]) * 2.0f - 1.0f); + } + + switch (wave_profile_) { + case SHD_WAVE_PROFILE_SIN: + val = 0.5f + 0.5f * sinf(n - M_PI_2); + break; + case SHD_WAVE_PROFILE_SAW: + n /= M_PI * 2.0f; + val = n - floorf(n); + break; + case SHD_WAVE_PROFILE_TRI: + n /= M_PI * 2.0f; + val = fabsf(n - floorf(n + 0.5f)) * 2.0f; + break; + } + + r_fac[i] = val; + } + if (!r_color.is_empty()) { + for (int64_t i : mask) { + r_color[i] = ColorGeometry4f(r_fac[i], r_fac[i], r_fac[i], 1.0f); + } + } + } +}; + +static void sh_node_wave_tex_build_multi_function( + blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + NodeTexWave *tex = (NodeTexWave *)node.storage; + builder.construct_and_set_matching_fn( + tex->wave_type, tex->bands_direction, tex->rings_direction, tex->wave_profile); +} + +} // namespace blender::nodes + +void register_node_type_sh_tex_wave(void) +{ + static bNodeType ntype; + + sh_fn_node_type_base(&ntype, SH_NODE_TEX_WAVE, "Wave Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_wave_declare; + node_type_size_preset(&ntype, NODE_SIZE_MIDDLE); + node_type_init(&ntype, node_shader_init_tex_wave); + node_type_storage(&ntype, "NodeTexWave", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_wave); + ntype.build_multi_function = blender::nodes::sh_node_wave_tex_build_multi_function; + + nodeRegisterType(&ntype); +} diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc index 6e973189065..43ee9400551 100644 --- a/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc +++ b/source/blender/nodes/shader/nodes/node_shader_tex_white_noise.cc @@ -19,19 +19,21 @@ #include "../node_shader_util.h" -/* **************** WHITE NOISE **************** */ +#include "BLI_noise.hh" -static bNodeSocketTemplate sh_node_tex_white_noise_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("W"), 0.0f, 0.0f, 0.0f, 0.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; +namespace blender::nodes { -static bNodeSocketTemplate sh_node_tex_white_noise_out[] = { - {SOCK_FLOAT, N_("Value")}, - {SOCK_RGBA, N_("Color")}, - {-1, ""}, +static void sh_node_tex_white_noise_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f).implicit_field(); + b.add_input(N_("W")).min(-10000.0f).max(10000.0f); + b.add_output(N_("Value")); + b.add_output(N_("Color")); }; +} // namespace blender::nodes + static void node_shader_init_tex_white_noise(bNodeTree *UNUSED(ntree), bNode *node) { node->custom1 = 3; @@ -65,15 +67,141 @@ static void node_shader_update_tex_white_noise(bNodeTree *UNUSED(ntree), bNode * nodeSetSocketAvailability(sockW, node->custom1 == 1 || node->custom1 == 4); } +namespace blender::nodes { + +class WhiteNoiseFunction : public fn::MultiFunction { + private: + int dimensions_; + + public: + WhiteNoiseFunction(int dimensions) : dimensions_(dimensions) + { + BLI_assert(dimensions >= 1 && dimensions <= 4); + static std::array signatures{ + create_signature(1), + create_signature(2), + create_signature(3), + create_signature(4), + }; + this->set_signature(&signatures[dimensions - 1]); + } + + static fn::MFSignature create_signature(int dimensions) + { + fn::MFSignatureBuilder signature{"WhiteNoise"}; + + if (ELEM(dimensions, 2, 3, 4)) { + signature.single_input("Vector"); + } + if (ELEM(dimensions, 1, 4)) { + signature.single_input("W"); + } + + signature.single_output("Value"); + signature.single_output("Color"); + + return signature.build(); + } + + void call(IndexMask mask, fn::MFParams params, fn::MFContext UNUSED(context)) const override + { + int param = ELEM(dimensions_, 2, 3, 4) + ELEM(dimensions_, 1, 4); + + MutableSpan r_value = params.uninitialized_single_output_if_required(param++, + "Value"); + MutableSpan r_color = + params.uninitialized_single_output_if_required(param++, "Color"); + + const bool compute_value = !r_value.is_empty(); + const bool compute_color = !r_color.is_empty(); + + switch (dimensions_) { + case 1: { + const VArray &w = params.readonly_single_input(0, "W"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(w[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(w[i]); + } + } + break; + } + case 2: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(float2(vector[i].x, vector[i].y)); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(float2(vector[i].x, vector[i].y)); + } + } + break; + } + case 3: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3(vector[i]); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float(vector[i]); + } + } + break; + } + case 4: { + const VArray &vector = params.readonly_single_input(0, "Vector"); + const VArray &w = params.readonly_single_input(1, "W"); + if (compute_color) { + for (int64_t i : mask) { + const float3 c = noise::hash_float_to_float3( + float4(vector[i].x, vector[i].y, vector[i].z, w[i])); + r_color[i] = ColorGeometry4f(c[0], c[1], c[2], 1.0f); + } + } + if (compute_value) { + for (int64_t i : mask) { + r_value[i] = noise::hash_float_to_float( + float4(vector[i].x, vector[i].y, vector[i].z, w[i])); + } + } + break; + } + } + } +}; + +static void sh_node_noise_build_multi_function(blender::nodes::NodeMultiFunctionBuilder &builder) +{ + bNode &node = builder.node(); + builder.construct_and_set_matching_fn((int)node.custom1); +} + +} // namespace blender::nodes + void register_node_type_sh_tex_white_noise(void) { static bNodeType ntype; - sh_node_type_base(&ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); - node_type_socket_templates(&ntype, sh_node_tex_white_noise_in, sh_node_tex_white_noise_out); + sh_fn_node_type_base( + &ntype, SH_NODE_TEX_WHITE_NOISE, "White Noise Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_white_noise_declare; node_type_init(&ntype, node_shader_init_tex_white_noise); node_type_gpu(&ntype, gpu_shader_tex_white_noise); node_type_update(&ntype, node_shader_update_tex_white_noise); + ntype.build_multi_function = blender::nodes::sh_node_noise_build_multi_function; nodeRegisterType(&ntype); } diff --git a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc index 62f6b38c79f..e4f1b2c76f0 100644 --- a/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_valToRgb.cc @@ -29,17 +29,18 @@ #include "node_shader_util.h" -/* **************** VALTORGB ******************** */ -static bNodeSocketTemplate sh_node_valtorgb_in[] = { - {SOCK_FLOAT, N_("Fac"), 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR}, - {-1, ""}, -}; -static bNodeSocketTemplate sh_node_valtorgb_out[] = { - {SOCK_RGBA, N_("Color")}, - {SOCK_FLOAT, N_("Alpha")}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_valtorgb_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Fac")).default_value(0.5f).min(0.0f).max(1.0f).subtype(PROP_FACTOR); + b.add_output(N_("Color")); + b.add_output(N_("Alpha")); }; +} // namespace blender::nodes + static void node_shader_exec_valtorgb(void *UNUSED(data), int UNUSED(thread), bNode *node, @@ -176,7 +177,7 @@ void register_node_type_sh_valtorgb(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VALTORGB, "ColorRamp", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_valtorgb_in, sh_node_valtorgb_out); + ntype.declare = blender::nodes::sh_node_valtorgb_declare; node_type_init(&ntype, node_shader_init_valtorgb); node_type_size_preset(&ntype, NODE_SIZE_LARGE); node_type_storage(&ntype, "ColorBand", node_free_standard_storage, node_copy_standard_storage); @@ -187,11 +188,15 @@ void register_node_type_sh_valtorgb(void) nodeRegisterType(&ntype); } -/* **************** RGBTOBW ******************** */ -static bNodeSocketTemplate sh_node_rgbtobw_in[] = { - {SOCK_RGBA, N_("Color"), 0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 1.0f}, {-1, ""}}; -static bNodeSocketTemplate sh_node_rgbtobw_out[] = { - {SOCK_FLOAT, N_("Val"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, {-1, ""}}; +namespace blender::nodes { + +static void sh_node_rgbtobw_declare(NodeDeclarationBuilder &b) +{ + b.add_input(N_("Color")).default_value({0.5f, 0.5f, 0.5f, 1.0f}); + b.add_output(N_("Val")); +}; + +} // namespace blender::nodes static void node_shader_exec_rgbtobw(void *UNUSED(data), int UNUSED(thread), @@ -200,8 +205,8 @@ static void node_shader_exec_rgbtobw(void *UNUSED(data), bNodeStack **in, bNodeStack **out) { - /* stack order out: bw */ - /* stack order in: col */ + /* Stack order out: BW. */ + /* Stack order in: COL. */ float col[3]; nodestack_get_vec(col, SOCK_VECTOR, in[0]); @@ -222,7 +227,7 @@ void register_node_type_sh_rgbtobw(void) static bNodeType ntype; sh_node_type_base(&ntype, SH_NODE_RGBTOBW, "RGB to BW", NODE_CLASS_CONVERTER, 0); - node_type_socket_templates(&ntype, sh_node_rgbtobw_in, sh_node_rgbtobw_out); + ntype.declare = blender::nodes::sh_node_rgbtobw_declare; node_type_exec(&ntype, nullptr, nullptr, node_shader_exec_rgbtobw); node_type_gpu(&ntype, gpu_shader_rgbtobw); diff --git a/source/blender/nodes/shader/nodes/node_shader_value.cc b/source/blender/nodes/shader/nodes/node_shader_value.cc index 602d5a1cf56..b0f152d8526 100644 --- a/source/blender/nodes/shader/nodes/node_shader_value.cc +++ b/source/blender/nodes/shader/nodes/node_shader_value.cc @@ -23,12 +23,15 @@ #include "node_shader_util.h" -/* **************** VALUE ******************** */ -static bNodeSocketTemplate sh_node_value_out[] = { - {SOCK_FLOAT, N_("Value"), 0.5f, 0, 0, 0, -FLT_MAX, FLT_MAX, PROP_NONE}, - {-1, ""}, +namespace blender::nodes { + +static void sh_node_value_declare(NodeDeclarationBuilder &b) +{ + b.add_output(N_("Value")); }; +} // namespace blender::nodes + static int gpu_shader_value(GPUMaterial *mat, bNode *node, bNodeExecData *UNUSED(execdata), @@ -51,7 +54,7 @@ void register_node_type_sh_value(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VALUE, "Value", NODE_CLASS_INPUT, 0); - node_type_socket_templates(&ntype, nullptr, sh_node_value_out); + ntype.declare = blender::nodes::sh_node_value_declare; node_type_gpu(&ntype, gpu_shader_value); ntype.build_multi_function = sh_node_value_build_multi_function; diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc index 4424db6aed1..ca5aeea9a7d 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_math.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_math.cc @@ -25,16 +25,20 @@ #include "NOD_math_functions.hh" -/* **************** VECTOR MATH ******************** */ -static bNodeSocketTemplate sh_node_vector_math_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {SOCK_FLOAT, N_("Scale"), 1.0f, 1.0f, 1.0f, 1.0f, -10000.0f, 10000.0f, PROP_NONE}, - {-1, ""}}; +namespace blender::nodes { -static bNodeSocketTemplate sh_node_vector_math_out[] = { - {SOCK_VECTOR, N_("Vector")}, {SOCK_FLOAT, N_("Value")}, {-1, ""}}; +static void sh_node_vector_math_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).min(-10000.0f).max(10000.0f); + b.add_input(N_("Vector"), "Vector_001").min(-10000.0f).max(10000.0f); + b.add_input(N_("Vector"), "Vector_002").min(-10000.0f).max(10000.0f); + b.add_input(N_("Scale")).default_value(1.0f).min(-10000.0f).max(10000.0f); + b.add_output(N_("Vector")); + b.add_output(N_("Value")); +}; + +} // namespace blender::nodes static const char *gpu_shader_get_name(int mode) { @@ -274,7 +278,7 @@ void register_node_type_sh_vect_math(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_MATH, "Vector Math", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_vector_math_in, sh_node_vector_math_out); + ntype.declare = blender::nodes::sh_node_vector_math_declare; node_type_label(&ntype, node_vector_math_label); node_type_gpu(&ntype, gpu_shader_vector_math); node_type_update(&ntype, node_shader_update_vector_math); diff --git a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc index bc51b7e29ea..1ab643bc3fa 100644 --- a/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc +++ b/source/blender/nodes/shader/nodes/node_shader_vector_rotate.cc @@ -23,20 +23,21 @@ #include "../node_shader_util.h" -/* **************** Vector Rotate ******************** */ -static bNodeSocketTemplate sh_node_vector_rotate_in[] = { - {SOCK_VECTOR, N_("Vector"), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE}, - {SOCK_VECTOR, N_("Center"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_NONE}, - {SOCK_VECTOR, N_("Axis"), 0.0f, 0.0f, 1.0f, 0.0f, -1.0f, 1.0f, PROP_NONE, PROP_NONE}, - {SOCK_FLOAT, N_("Angle"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_ANGLE, PROP_NONE}, - {SOCK_VECTOR, N_("Rotation"), 0.0f, 0.0f, 0.0f, 1.0f, -FLT_MAX, FLT_MAX, PROP_EULER}, - {-1, ""}}; +namespace blender::nodes { -static bNodeSocketTemplate sh_node_vector_rotate_out[] = { - {SOCK_VECTOR, N_("Vector")}, - {-1, ""}, +static void sh_node_vector_rotate_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input(N_("Vector")).min(0.0f).max(1.0f).hide_value(); + b.add_input(N_("Center")); + b.add_input(N_("Axis")).min(-1.0f).max(1.0f).default_value({0.0f, 0.0f, 1.0f}); + b.add_input(N_("Angle")).subtype(PROP_ANGLE); + b.add_input(N_("Rotation")).subtype(PROP_EULER); + b.add_output(N_("Vector")); }; +} // namespace blender::nodes + static const char *gpu_shader_get_name(int mode) { switch (mode) { @@ -207,7 +208,7 @@ void register_node_type_sh_vector_rotate(void) static bNodeType ntype; sh_fn_node_type_base(&ntype, SH_NODE_VECTOR_ROTATE, "Vector Rotate", NODE_CLASS_OP_VECTOR, 0); - node_type_socket_templates(&ntype, sh_node_vector_rotate_in, sh_node_vector_rotate_out); + ntype.declare = blender::nodes::sh_node_vector_rotate_declare; node_type_gpu(&ntype, gpu_shader_vector_rotate); node_type_update(&ntype, node_shader_update_vector_rotate); ntype.build_multi_function = sh_node_vector_rotate_build_multi_function; diff --git a/source/blender/nodes/texture/node_texture_tree.c b/source/blender/nodes/texture/node_texture_tree.c index 7452007639c..14597050524 100644 --- a/source/blender/nodes/texture/node_texture_tree.c +++ b/source/blender/nodes/texture/node_texture_tree.c @@ -169,7 +169,7 @@ void register_node_tree_type_tex(void) tt->type = NTREE_TEXTURE; strcpy(tt->idname, "TextureNodeTree"); strcpy(tt->ui_name, N_("Texture Node Editor")); - tt->ui_icon = 0; /* defined in drawnode.c */ + tt->ui_icon = 0; /* Defined in `drawnode.c`. */ strcpy(tt->ui_description, N_("Texture nodes")); tt->foreach_nodeclass = foreach_nodeclass; diff --git a/source/blender/nodes/texture/nodes/node_texture_curves.c b/source/blender/nodes/texture/nodes/node_texture_curves.c index 70f7e731720..f61e3f36db5 100644 --- a/source/blender/nodes/texture/nodes/node_texture_curves.c +++ b/source/blender/nodes/texture/nodes/node_texture_curves.c @@ -26,7 +26,7 @@ /* **************** CURVE Time ******************** */ -/* custom1 = sfra, custom2 = efra */ +/* custom1 = start-frame, custom2 = end-frame. */ static bNodeSocketTemplate time_outputs[] = {{SOCK_FLOAT, N_("Value")}, {-1, ""}}; static void time_colorfn( diff --git a/source/blender/nodes/texture/nodes/node_texture_image.c b/source/blender/nodes/texture/nodes/node_texture_image.c index 0d10b3270d7..a85b963286b 100644 --- a/source/blender/nodes/texture/nodes/node_texture_image.c +++ b/source/blender/nodes/texture/nodes/node_texture_image.c @@ -101,7 +101,6 @@ static void init(bNodeTree *UNUSED(ntree), bNode *node) ImageUser *iuser = MEM_callocN(sizeof(ImageUser), "node image user"); node->storage = iuser; iuser->sfra = 1; - iuser->ok = 1; iuser->flag |= IMA_ANIM_ALWAYS; } diff --git a/source/blender/python/bmesh/bmesh_py_types.c b/source/blender/python/bmesh/bmesh_py_types.c index e5e601e0eb6..cbebe4746e9 100644 --- a/source/blender/python/bmesh/bmesh_py_types.c +++ b/source/blender/python/bmesh/bmesh_py_types.c @@ -1950,7 +1950,7 @@ static PyObject *bpy_bmface_calc_tangent_edge_diagonal(BPy_BMFace *self) PyDoc_STRVAR(bpy_bmface_calc_tangent_vert_diagonal_doc, ".. method:: calc_tangent_vert_diagonal()\n" "\n" - " Return face tangent based on the two most distent vertices.\n" + " Return face tangent based on the two most distant vertices.\n" "\n" " :return: a normalized vector.\n" " :rtype: :class:`mathutils.Vector`\n"); @@ -3464,7 +3464,7 @@ PyDoc_STRVAR(bpy_bmelemseq_doc, ":class:`BMVert`, :class:`BMEdge`, :class:`BMFace`, :class:`BMLoop`.\n" "\n" "When accessed via :class:`BMesh.verts`, :class:`BMesh.edges`, :class:`BMesh.faces`\n" - "there are also functions to create/remomove items.\n"); + "there are also functions to create/remove items.\n"); PyDoc_STRVAR(bpy_bmiter_doc, "Internal BMesh type for looping over verts/faces/edges,\n" "used for iterating over :class:`BMElemSeq` types.\n"); diff --git a/source/blender/python/generic/bl_math_py_api.c b/source/blender/python/generic/bl_math_py_api.c index f17aaa4ca5d..2211cc931da 100644 --- a/source/blender/python/generic/bl_math_py_api.c +++ b/source/blender/python/generic/bl_math_py_api.c @@ -77,14 +77,14 @@ static PyObject *py_bl_math_clamp(PyObject *UNUSED(self), PyObject *args) } PyDoc_STRVAR(py_bl_math_lerp_doc, - ".. function:: lerp(from, to, factor)\n" + ".. function:: lerp(from_value, to_value, factor)\n" "\n" " Linearly interpolate between two float values based on factor.\n" "\n" - " :arg from: The value to return when factor is 0.\n" - " :type from: float\n" - " :arg to: The value to return when factor is 1.\n" - " :type to: float\n" + " :arg from_value: The value to return when factor is 0.\n" + " :type from_value: float\n" + " :arg to_value: The value to return when factor is 1.\n" + " :type to_value: float\n" " :arg factor: The interpolation value, normally in [0.0, 1.0].\n" " :type factor: float\n" " :return: The interpolated value.\n" @@ -99,21 +99,21 @@ static PyObject *py_bl_math_lerp(PyObject *UNUSED(self), PyObject *args) return PyFloat_FromDouble(a * (1.0 - x) + b * x); } -PyDoc_STRVAR( - py_bl_math_smoothstep_doc, - ".. function:: smoothstep(from, to, value)\n" - "\n" - " Performs smooth interpolation between 0 and 1 as value changes between from and to.\n" - " Outside the range the function returns the same value as the nearest edge.\n" - "\n" - " :arg from: The edge value where the result is 0.\n" - " :type from: float\n" - " :arg to: The edge value where the result is 1.\n" - " :type to: float\n" - " :arg factor: The interpolation value.\n" - " :type factor: float\n" - " :return: The interpolated value in [0.0, 1.0].\n" - " :rtype: float\n"); +PyDoc_STRVAR(py_bl_math_smoothstep_doc, + ".. function:: smoothstep(from_value, to_value, value)\n" + "\n" + " Performs smooth interpolation between 0 and 1 as value changes between from and " + "to values.\n" + " Outside the range the function returns the same value as the nearest edge.\n" + "\n" + " :arg from_value: The edge value where the result is 0.\n" + " :type from_value: float\n" + " :arg to_value: The edge value where the result is 1.\n" + " :type to_value: float\n" + " :arg factor: The interpolation value.\n" + " :type factor: float\n" + " :return: The interpolated value in [0.0, 1.0].\n" + " :rtype: float\n"); static PyObject *py_bl_math_smoothstep(PyObject *UNUSED(self), PyObject *args) { double a, b, x; diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c index 7827bd48dfe..37ba27f7315 100644 --- a/source/blender/python/generic/idprop_py_ui_api.c +++ b/source/blender/python/generic/idprop_py_ui_api.c @@ -622,7 +622,9 @@ static PyObject *BPy_IDPropertyUIManager_update_from(BPy_IDPropertyUIManager *se IDP_ui_data_free(property); } - property->ui_data = IDP_ui_data_copy(ui_manager_src->property); + if (ui_manager_src->property && ui_manager_src->property->ui_data) { + property->ui_data = IDP_ui_data_copy(ui_manager_src->property); + } Py_RETURN_NONE; } diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c index 9dac5a4f21f..e847a5a5b5c 100644 --- a/source/blender/python/generic/py_capi_utils.c +++ b/source/blender/python/generic/py_capi_utils.c @@ -1649,7 +1649,30 @@ bool PyC_RunString_AsString(const char *imports[], #endif /** - * Don't use `bool` return type, so -1 can be used as an error value. + * + * Comparison with #PyObject_IsTrue + * ================================ + * + * Even though Python provides a way to retrieve the boolean value for an object, + * in many cases it's far too relaxed, with the following examples coercing values. + * + * \code{.py} + * data.value = "Text" # True. + * data.value = "" # False. + * data.value = {1, 2} # True + * data.value = {} # False. + * data.value = None # False. + * \endcode + * + * In practice this is often a mistake by the script author that doesn't behave as they expect. + * So it's better to be more strict for attribute assignment and function arguments, + * only accepting True/False 0/1. + * + * If coercing a value is desired, it can be done explicitly: `data.value = bool(value)` + * + * \see #PyC_ParseBool for use with #PyArg_ParseTuple and related functions. + * + * \note Don't use `bool` return type, so -1 can be used as an error value. */ int PyC_Long_AsBool(PyObject *value) { diff --git a/source/blender/python/gpu/gpu_py_texture.c b/source/blender/python/gpu/gpu_py_texture.c index 5d136921300..c034c31d828 100644 --- a/source/blender/python/gpu/gpu_py_texture.c +++ b/source/blender/python/gpu/gpu_py_texture.c @@ -536,7 +536,7 @@ PyDoc_STRVAR(pygpu_texture_from_image_doc, "premultiplied or straight alpha matching the image alpha mode.\n" "\n" " :arg image: The Image datablock.\n" - " :type image: `bpy.types.Image`\n" + " :type image: :class:`bpy.types.Image`\n" " :return: The GPUTexture used by the image.\n" " :rtype: :class:`gpu.types.GPUTexture`\n"); static PyObject *pygpu_texture_from_image(PyObject *UNUSED(self), PyObject *arg) diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 7a93a076621..95affa9dba9 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -246,7 +246,7 @@ void BPY_modules_update(void) #if 0 /* slow, this runs all the time poll, draw etc 100's of time a sec. */ PyObject *mod = PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0); PyModule_AddObject(mod, "data", BPY_rna_module()); - PyModule_AddObject(mod, "types", BPY_rna_types()); /* atm this does not need updating */ + PyModule_AddObject(mod, "types", BPY_rna_types()); /* This does not need updating. */ #endif /* refreshes the main struct */ diff --git a/source/blender/python/intern/bpy_msgbus.c b/source/blender/python/intern/bpy_msgbus.c index 75a5f6f72ae..9e165bbf1c0 100644 --- a/source/blender/python/intern/bpy_msgbus.c +++ b/source/blender/python/intern/bpy_msgbus.c @@ -208,6 +208,9 @@ static void bpy_msgbus_subscribe_value_free_data(struct wmMsgSubscribeKey *UNUSE PyDoc_STRVAR( bpy_msgbus_subscribe_rna_doc, ".. function:: subscribe_rna(key, owner, args, notify, options=set())\n" + "\n" + " Register a message bus subscription. It will be cleared when another blend file is\n" + " loaded, or can be cleared explicitly via :func:`bpy.msgbus.clear_by_owner`.\n" "\n" BPY_MSGBUS_RNA_MSGKEY_DOC " :arg owner: Handle for this subscription (compared by identity).\n" " :type owner: Any type.\n" @@ -215,7 +218,12 @@ PyDoc_STRVAR( "\n" " - ``PERSISTENT`` when set, the subscriber will be kept when remapping ID data.\n" "\n" - " :type options: set of str.\n"); + " :type options: set of str.\n" + "\n" + ".. note::\n" + "\n" + " All subscribers will be cleared on file-load. Subscribers can be re-registered on load,\n" + " see :mod:`bpy.app.handlers.load_post`.\n"); static PyObject *bpy_msgbus_subscribe_rna(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { const char *error_prefix = "subscribe_rna"; diff --git a/source/blender/python/intern/bpy_props.c b/source/blender/python/intern/bpy_props.c index ac624d365fa..6d384ed9358 100644 --- a/source/blender/python/intern/bpy_props.c +++ b/source/blender/python/intern/bpy_props.c @@ -3750,7 +3750,7 @@ PyDoc_STRVAR( " (e.g. returned by :class:`bpy.types.UILayout.icon`)\n" " :number: Unique value used as the identifier for this item (stored in file data).\n" " Use when the identifier may need to change. If the *ENUM_FLAG* option is used,\n" - " the values are bitmasks and should be powers of two.\n" + " the values are bit-masks and should be powers of two.\n" "\n" " When an item only contains 4 items they define ``(identifier, name, description, " "number)``.\n" diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 35acb56e66a..7b1877f3191 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -180,6 +180,15 @@ static PyObject *id_free_weakref_cb(PyObject *weakinfo_pair, PyObject *weakref); static PyMethodDef id_free_weakref_cb_def = { "id_free_weakref_cb", (PyCFunction)id_free_weakref_cb, METH_O, NULL}; +/** + * Only used when there are values left on exit (causing memory leaks). + */ +static void id_weakref_pool_free_value_fn(void *p) +{ + GHash *weakinfo_hash = p; + BLI_ghash_free(weakinfo_hash, NULL, NULL); +} + /* Adds a reference to the list, remember to decref. */ static GHash *id_weakref_pool_get(ID *id) { @@ -5486,7 +5495,7 @@ static PyObject *pyprop_array_foreach_getset(BPy_PropertyArrayRNA *self, } else { const char f = buf.format ? buf.format[0] : 0; - if ((prop_type == PROP_INT && (buf.itemsize != sizeof(int) || (f != 'l' && f != 'i'))) || + if ((prop_type == PROP_INT && (buf.itemsize != sizeof(int) || (!ELEM(f, 'l', 'i')))) || (prop_type == PROP_FLOAT && (buf.itemsize != sizeof(float) || f != 'f'))) { PyBuffer_Release(&buf); PyErr_Format(PyExc_TypeError, "incorrect sequence item type: %s", buf.format); @@ -7238,7 +7247,7 @@ static PyObject *pyrna_srna_ExternalType(StructRNA *srna) /* Sanity check, could skip this unless in debug mode. */ if (newclass) { PyObject *base_compare = pyrna_srna_PyBase(srna); - /* Can't do this because it gets superclasses values! */ + /* Can't do this because it gets super-classes values! */ // PyObject *slots = PyObject_GetAttrString(newclass, "__slots__"); /* Can do this, but faster not to. */ // PyObject *bases = PyObject_GetAttrString(newclass, "__bases__"); @@ -7633,7 +7642,7 @@ void BPY_rna_exit(void) printf("ID: %s\n", id->name); } } - BLI_ghash_free(id_weakref_pool, NULL, NULL); + BLI_ghash_free(id_weakref_pool, NULL, id_weakref_pool_free_value_fn); id_weakref_pool = NULL; #endif } @@ -8777,7 +8786,7 @@ void pyrna_free_types(void) * - Should still be fixed - Campbell */ PyDoc_STRVAR(pyrna_register_class_doc, - ".. method:: register_class(cls)\n" + ".. function:: register_class(cls)\n" "\n" " Register a subclass of a Blender type class.\n" "\n" @@ -8962,7 +8971,7 @@ static int pyrna_srna_contains_pointer_prop_srna(StructRNA *srna_props, } PyDoc_STRVAR(pyrna_unregister_class_doc, - ".. method:: unregister_class(cls)\n" + ".. function:: unregister_class(cls)\n" "\n" " Unload the Python class from blender.\n" "\n" diff --git a/source/blender/python/intern/bpy_rna_gizmo.c b/source/blender/python/intern/bpy_rna_gizmo.c index f121bfd6e36..adae68bd7b4 100644 --- a/source/blender/python/intern/bpy_rna_gizmo.c +++ b/source/blender/python/intern/bpy_rna_gizmo.c @@ -17,7 +17,7 @@ /** \file * \ingroup pythonintern * - * . + * This file defines utility methods for `bpy.types.Gizmo`. */ #include @@ -43,6 +43,104 @@ #include "bpy_rna.h" +/* -------------------------------------------------------------------- */ +/** \name Parsing Utility Functions + * + * Functions used as callbacks for #PyArg_ParseTuple `O&` format string. + * \{ */ + +struct BPyGizmoWithTarget { + wmGizmo *gz; /* Must be first. */ + wmGizmoProperty *gz_prop; +}; + +struct BPyGizmoWithTargetType { + wmGizmo *gz; /* Must be first. */ + const wmGizmoPropertyType *gz_prop_type; +}; + +static int py_rna_gizmo_parse(PyObject *o, void *p) +{ + /* No type checking (this is `self` not a user defined argument). */ + BLI_assert(BPy_StructRNA_Check(o)); + BLI_assert(((const BPy_StructRNA *)o)->ptr.type == &RNA_Gizmo); + + wmGizmo **gz_p = p; + *gz_p = ((const BPy_StructRNA *)o)->ptr.data; + return 1; +} + +static int py_rna_gizmo_target_id_parse(PyObject *o, void *p) +{ + struct BPyGizmoWithTarget *gizmo_with_target = p; + /* Must be set by `py_rna_gizmo_parse`. */ + wmGizmo *gz = gizmo_with_target->gz; + BLI_assert(gz != NULL); + + if (!PyUnicode_Check(o)) { + PyErr_Format(PyExc_TypeError, "expected a string (got %.200s)", Py_TYPE(o)->tp_name); + return 0; + } + const char *gz_prop_id = PyUnicode_AsUTF8(o); + wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, gz_prop_id); + if (gz_prop == NULL) { + PyErr_Format(PyExc_ValueError, + "Gizmo target property '%s.%s' not found!", + gz->type->idname, + gz_prop_id); + return 0; + } + gizmo_with_target->gz_prop = gz_prop; + return 1; +} + +static int py_rna_gizmo_target_id_parse_and_ensure_is_valid(PyObject *o, void *p) +{ + if (py_rna_gizmo_target_id_parse(o, p) == 0) { + return 0; + } + struct BPyGizmoWithTarget *gizmo_with_target = p; + wmGizmo *gz = gizmo_with_target->gz; + wmGizmoProperty *gz_prop = gizmo_with_target->gz_prop; + if (!WM_gizmo_target_property_is_valid(gz_prop)) { + const char *gz_prop_id = PyUnicode_AsUTF8(o); + PyErr_Format(PyExc_ValueError, + "Gizmo target property '%s.%s' has not been initialized, " + "Call \"target_set_prop\" first!", + gz->type->idname, + gz_prop_id); + return 0; + } + return 1; +} + +static int py_rna_gizmo_target_type_id_parse(PyObject *o, void *p) +{ + struct BPyGizmoWithTargetType *gizmo_with_target = p; + /* Must be set first. */ + wmGizmo *gz = gizmo_with_target->gz; + BLI_assert(gz != NULL); + + if (!PyUnicode_Check(o)) { + PyErr_Format(PyExc_TypeError, "expected a string (got %.200s)", Py_TYPE(o)->tp_name); + return 0; + } + const char *gz_prop_id = PyUnicode_AsUTF8(o); + const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, + gz_prop_id); + if (gz_prop_type == NULL) { + PyErr_Format(PyExc_ValueError, + "Gizmo target property '%s.%s' not found!", + gz->type->idname, + gz_prop_id); + return 0; + } + gizmo_with_target->gz_prop_type = gz_prop_type; + return 1; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Gizmo Target Property Define API * \{ */ @@ -240,12 +338,10 @@ static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject * const PyGILState_STATE gilstate = PyGILState_Ensure(); struct { - PyObject *self; - char *target; + struct BPyGizmoWithTargetType gz_with_target_type; PyObject *py_fn_slots[BPY_GIZMO_FN_SLOT_LEN]; } params = { - .self = NULL, - .target = NULL, + .gz_with_target_type = {NULL, NULL}, .py_fn_slots = {NULL}, }; @@ -253,29 +349,25 @@ static PyObject *bpy_gizmo_target_set_handler(PyObject *UNUSED(self), PyObject * * 'Gizmo.target_set_prop & target_set_operator' * (see: rna_wm_gizmo_api.c). conventions should match. */ static const char *const _keywords[] = {"self", "target", "get", "set", "range", NULL}; - static _PyArg_Parser _parser = {"Os|$OOO:target_set_handler", _keywords, 0}; + static _PyArg_Parser _parser = {"O&O&|$OOO:target_set_handler", _keywords, 0}; if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, - ¶ms.self, - ¶ms.target, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target_type.gz, + /* `target` */ + py_rna_gizmo_target_type_id_parse, + ¶ms.gz_with_target_type, + /* `get/set/range` */ ¶ms.py_fn_slots[BPY_GIZMO_FN_SLOT_GET], ¶ms.py_fn_slots[BPY_GIZMO_FN_SLOT_SET], ¶ms.py_fn_slots[BPY_GIZMO_FN_SLOT_RANGE_GET])) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - const wmGizmoPropertyType *gz_prop_type = WM_gizmotype_target_property_find(gz->type, - params.target); - if (gz_prop_type == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target_type.gz; + const wmGizmoPropertyType *gz_prop_type = params.gz_with_target_type.gz_prop_type; { const int slots_required = 2; @@ -338,29 +430,27 @@ PyDoc_STRVAR(bpy_gizmo_target_get_value_doc, static PyObject *bpy_gizmo_target_get_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { struct { - PyObject *self; - char *target; + struct BPyGizmoWithTarget gz_with_target; } params = { - .self = NULL, - .target = NULL, + .gz_with_target = {NULL, NULL}, }; static const char *const _keywords[] = {"self", "target", NULL}; - static _PyArg_Parser _parser = {"Os:target_get_value", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, ¶ms.self, ¶ms.target)) { + static _PyArg_Parser _parser = {"O&O&:target_get_value", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target.gz, + /* `target` */ + py_rna_gizmo_target_id_parse_and_ensure_is_valid, + ¶ms.gz_with_target)) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, params.target); - if (gz_prop == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target.gz; + wmGizmoProperty *gz_prop = params.gz_with_target.gz_prop; const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop); switch (gz_prop->type->data_type) { @@ -396,32 +486,31 @@ PyDoc_STRVAR(bpy_gizmo_target_set_value_doc, static PyObject *bpy_gizmo_target_set_value(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { struct { - PyObject *self; - char *target; + struct BPyGizmoWithTarget gz_with_target; PyObject *value; } params = { - .self = NULL, - .target = NULL, + .gz_with_target = {NULL, NULL}, .value = NULL, }; static const char *const _keywords[] = {"self", "target", "value", NULL}; - static _PyArg_Parser _parser = {"OsO:target_set_value", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast( - args, kw, &_parser, ¶ms.self, ¶ms.target, ¶ms.value)) { + static _PyArg_Parser _parser = {"O&O&O:target_set_value", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target.gz, + /* `target` */ + py_rna_gizmo_target_id_parse_and_ensure_is_valid, + ¶ms.gz_with_target, + /* `value` */ + ¶ms.value)) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, params.target); - if (gz_prop == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target.gz; + wmGizmoProperty *gz_prop = params.gz_with_target.gz_prop; const int array_len = WM_gizmo_target_property_array_length(gz, gz_prop); switch (gz_prop->type->data_type) { @@ -468,29 +557,27 @@ PyDoc_STRVAR(bpy_gizmo_target_get_range_doc, static PyObject *bpy_gizmo_target_get_range(PyObject *UNUSED(self), PyObject *args, PyObject *kw) { struct { - PyObject *self; - char *target; + struct BPyGizmoWithTarget gz_with_target; } params = { - .self = NULL, - .target = NULL, + .gz_with_target = {NULL, NULL}, }; static const char *const _keywords[] = {"self", "target", NULL}; - static _PyArg_Parser _parser = {"Os:target_get_range", _keywords, 0}; - if (!_PyArg_ParseTupleAndKeywordsFast(args, kw, &_parser, ¶ms.self, ¶ms.target)) { + static _PyArg_Parser _parser = {"O&O&:target_get_range", _keywords, 0}; + if (!_PyArg_ParseTupleAndKeywordsFast(args, + kw, + &_parser, + /* `self` */ + py_rna_gizmo_parse, + ¶ms.gz_with_target.gz, + /* `target` */ + py_rna_gizmo_target_id_parse_and_ensure_is_valid, + ¶ms.gz_with_target)) { goto fail; } - wmGizmo *gz = ((BPy_StructRNA *)params.self)->ptr.data; - - wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, params.target); - if (gz_prop == NULL) { - PyErr_Format(PyExc_ValueError, - "Gizmo target property '%s.%s' not found", - gz->type->idname, - params.target); - goto fail; - } + wmGizmo *gz = params.gz_with_target.gz; + wmGizmoProperty *gz_prop = params.gz_with_target.gz_prop; switch (gz_prop->type->data_type) { case PROP_FLOAT: { @@ -510,6 +597,10 @@ fail: /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Gizmo Module + * \{ */ + bool BPY_rna_gizmo_module(PyObject *mod_par) { static PyMethodDef method_def_array[] = { @@ -546,3 +637,5 @@ bool BPY_rna_gizmo_module(PyObject *mod_par) return false; } + +/** \} */ diff --git a/source/blender/python/mathutils/mathutils_Matrix.c b/source/blender/python/mathutils/mathutils_Matrix.c index ce04a143aae..28b91e13d47 100644 --- a/source/blender/python/mathutils/mathutils_Matrix.c +++ b/source/blender/python/mathutils/mathutils_Matrix.c @@ -526,7 +526,7 @@ static PyObject *C_Matrix_Rotation(PyObject *cls, PyObject *args) "cannot create a 2x2 rotation matrix around arbitrary axis"); return NULL; } - if ((matSize == 3 || matSize == 4) && (axis == NULL) && (vec == NULL)) { + if ((ELEM(matSize, 3, 4)) && (axis == NULL) && (vec == NULL)) { PyErr_SetString(PyExc_ValueError, "Matrix.Rotation(): " "axis of rotation for 3d and 4d matrices is required"); diff --git a/source/blender/python/mathutils/mathutils_geometry.c b/source/blender/python/mathutils/mathutils_geometry.c index 5868c76b28f..d47b59d0c76 100644 --- a/source/blender/python/mathutils/mathutils_geometry.c +++ b/source/blender/python/mathutils/mathutils_geometry.c @@ -1210,7 +1210,7 @@ PyDoc_STRVAR(M_Geometry_tessellate_polygon_doc, "\n" " :arg veclist_list: list of polylines\n" " :rtype: list\n"); -/* PolyFill function, uses Blenders scanfill to fill multiple poly lines */ +/* PolyFill function, uses Blenders scan-fill to fill multiple poly lines. */ static PyObject *M_Geometry_tessellate_polygon(PyObject *UNUSED(self), PyObject *polyLineSeq) { PyObject *tri_list; /* Return this list of tri's */ diff --git a/source/blender/python/mathutils/mathutils_noise.c b/source/blender/python/mathutils/mathutils_noise.c index 69d37b345c6..d5e27496af8 100644 --- a/source/blender/python/mathutils/mathutils_noise.c +++ b/source/blender/python/mathutils/mathutils_noise.c @@ -562,7 +562,7 @@ PyDoc_STRVAR(M_Noise_turbulence_vector_doc, " :type octaves: int\n" " :arg hard: Specifies whether returned turbulence is hard (sharp transitions) or " "soft (smooth transitions).\n" - " :type hard: :boolean\n" BPY_NOISE_BASIS_ENUM_DOC + " :type hard: boolean\n" BPY_NOISE_BASIS_ENUM_DOC " :arg amplitude_scale: The amplitude scaling factor.\n" " :type amplitude_scale: float\n" " :arg frequency_scale: The frequency scaling factor\n" diff --git a/source/blender/render/CMakeLists.txt b/source/blender/render/CMakeLists.txt index 0046474d064..494415a4077 100644 --- a/source/blender/render/CMakeLists.txt +++ b/source/blender/render/CMakeLists.txt @@ -59,7 +59,6 @@ set(SRC RE_pipeline.h RE_texture.h - intern/initrender.h intern/pipeline.h intern/render_result.h intern/render_types.h diff --git a/source/blender/render/RE_engine.h b/source/blender/render/RE_engine.h index dfc0d5d0e9f..2a3a5964262 100644 --- a/source/blender/render/RE_engine.h +++ b/source/blender/render/RE_engine.h @@ -40,6 +40,7 @@ struct RenderData; struct RenderEngine; struct RenderEngineType; struct RenderLayer; +struct RenderPass; struct RenderResult; struct ReportList; struct Scene; @@ -59,7 +60,7 @@ extern "C" { #define RE_USE_PREVIEW 4 #define RE_USE_POSTPROCESS 8 #define RE_USE_EEVEE_VIEWPORT 16 -#define RE_USE_SAVE_BUFFERS 32 +/* #define RE_USE_SAVE_BUFFERS_DEPRECATED 32 */ #define RE_USE_SHADING_NODES_CUSTOM 64 #define RE_USE_SPHERICAL_STEREO 128 #define RE_USE_STEREO_VIEWPORT 256 @@ -75,6 +76,7 @@ extern "C" { #define RE_ENGINE_DO_UPDATE 8 #define RE_ENGINE_RENDERING 16 #define RE_ENGINE_HIGHLIGHT_TILES 32 +#define RE_ENGINE_CAN_DRAW 64 extern ListBase R_engines; @@ -87,7 +89,20 @@ typedef struct RenderEngineType { int flag; void (*update)(struct RenderEngine *engine, struct Main *bmain, struct Depsgraph *depsgraph); + void (*render)(struct RenderEngine *engine, struct Depsgraph *depsgraph); + + /* Offline rendering is finished - no more view layers will be rendered. + * + * All the pending data is to be communicated from the engine back to Blender. In a possibly + * most memory-efficient manner (engine might free its database before making Blender to allocate + * full-frame render result). */ + void (*render_frame_finish)(struct RenderEngine *engine); + + void (*draw)(struct RenderEngine *engine, + const struct bContext *context, + struct Depsgraph *depsgraph); + void (*bake)(struct RenderEngine *engine, struct Depsgraph *depsgraph, struct Object *object, @@ -132,9 +147,6 @@ typedef struct RenderEngine { struct Object *camera_override; unsigned int layer_override; - int tile_x; - int tile_y; - struct Render *re; ListBase fullresult; char text[512]; /* IMA_MAX_RENDER_TEXT */ @@ -189,6 +201,10 @@ void RE_engine_end_result(RenderEngine *engine, bool merge_results); struct RenderResult *RE_engine_get_result(struct RenderEngine *engine); +struct RenderPass *RE_engine_pass_by_index_get(struct RenderEngine *engine, + const char *layer_name, + int index); + const char *RE_engine_active_view_get(RenderEngine *engine); void RE_engine_active_view_set(RenderEngine *engine, const char *viewname); float RE_engine_get_camera_shift_x(RenderEngine *engine, @@ -228,6 +244,24 @@ void RE_engine_register_pass(struct RenderEngine *engine, bool RE_engine_use_persistent_data(struct RenderEngine *engine); +struct RenderEngine *RE_engine_get(const struct Render *re); + +/* Acquire render engine for drawing via its `draw()` callback. + * + * If drawing is not possible false is returned. If drawing is possible then the engine is + * "acquired" so that it can not be freed by the render pipeline. + * + * Drawing is possible if the engine has the `draw()` callback and it is in its `render()` + * callback. */ +bool RE_engine_draw_acquire(struct Render *re); +void RE_engine_draw_release(struct Render *re); + +/* NOTE: Only used for Cycles's BLenderGPUDisplay integration with the draw manager. A subject + * for re-consideration. Do not use this functionality. */ +bool RE_engine_has_render_context(struct RenderEngine *engine); +void RE_engine_render_context_enable(struct RenderEngine *engine); +void RE_engine_render_context_disable(struct RenderEngine *engine); + /* Engine Types */ void RE_engines_init(void); @@ -252,6 +286,10 @@ void RE_bake_engine_set_engine_parameters(struct Render *re, void RE_engine_free_blender_memory(struct RenderEngine *engine); +void RE_engine_tile_highlight_set( + struct RenderEngine *engine, int x, int y, int width, int height, bool highlight); +void RE_engine_tile_highlight_clear_all(struct RenderEngine *engine); + #ifdef __cplusplus } #endif diff --git a/source/blender/render/RE_pipeline.h b/source/blender/render/RE_pipeline.h index cd839385bfb..0d2d93ae026 100644 --- a/source/blender/render/RE_pipeline.h +++ b/source/blender/render/RE_pipeline.h @@ -141,9 +141,6 @@ typedef struct RenderResult { volatile rcti renrect; volatile RenderLayer *renlay; - /* optional saved endresult on disk */ - int do_exr_tile; - /* for render results in Image, verify validity for sequences */ int framenr; @@ -236,7 +233,8 @@ void RE_create_render_pass(struct RenderResult *rr, int channels, const char *chan_id, const char *layername, - const char *viewname); + const char *viewname, + const bool allocate); /* obligatory initialize call, disprect is optional */ void RE_InitState(struct Render *re, diff --git a/source/blender/render/intern/bake.c b/source/blender/render/intern/bake.c index 76839651b5d..0f893ce8cd5 100644 --- a/source/blender/render/intern/bake.c +++ b/source/blender/render/intern/bake.c @@ -774,18 +774,6 @@ void RE_bake_pixels_populate(Mesh *me, /* ******************** NORMALS ************************ */ -/** - * convert a normalized normal to the -1.0 1.0 range - * the input is expected to be POS_X, POS_Y, POS_Z - */ -static void normal_uncompress(float out[3], const float in[3]) -{ - int i; - for (i = 0; i < 3; i++) { - out[i] = 2.0f * in[i] - 1.0f; - } -} - static void normal_compress(float out[3], const float in[3], const eBakeNormalSwizzle normal_swizzle[3]) @@ -934,7 +922,7 @@ void RE_bake_normal_world_to_tangent(const BakePixel pixel_array[], copy_v3_v3(tsm[2], normal); /* texture values */ - normal_uncompress(nor, &result[offset]); + copy_v3_v3(nor, &result[offset]); /* converts from world space to local space */ mul_transposed_mat3_m4_v3(mat, nor); @@ -976,7 +964,7 @@ void RE_bake_normal_world_to_object(const BakePixel pixel_array[], } offset = i * depth; - normal_uncompress(nor, &result[offset]); + copy_v3_v3(nor, &result[offset]); /* rotates only without translation */ mul_mat3_m4_v3(iobmat, nor); @@ -1004,7 +992,7 @@ void RE_bake_normal_world_to_world(const BakePixel pixel_array[], } offset = i * depth; - normal_uncompress(nor, &result[offset]); + copy_v3_v3(nor, &result[offset]); /* save back the values */ normal_compress(&result[offset], nor, normal_swizzle); @@ -1053,6 +1041,7 @@ int RE_pass_depth(const eScenePassType pass_type) } case SCE_PASS_COMBINED: case SCE_PASS_SHADOW: + case SCE_PASS_POSITION: case SCE_PASS_NORMAL: case SCE_PASS_VECTOR: case SCE_PASS_INDEXOB: /* XXX double check */ diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 5728b784714..5f8679b572d 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -62,7 +62,6 @@ #include "DRW_engine.h" -#include "initrender.h" #include "pipeline.h" #include "render_result.h" #include "render_types.h" @@ -208,11 +207,10 @@ static RenderResult *render_result_from_bake(RenderEngine *engine, int x, int y, /* Add render passes. */ RenderPass *result_pass = render_layer_add_pass( - rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA"); - RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA"); - RenderPass *differential_pass = render_layer_add_pass(rr, rl, 4, "BakeDifferential", "", "RGBA"); - - render_result_passes_allocated_ensure(rr); + rr, rl, engine->bake.depth, RE_PASSNAME_COMBINED, "", "RGBA", true); + RenderPass *primitive_pass = render_layer_add_pass(rr, rl, 4, "BakePrimitive", "", "RGBA", true); + RenderPass *differential_pass = render_layer_add_pass( + rr, rl, 4, "BakeDifferential", "", "RGBA", true); /* Fill render passes from bake pixel array, to be read by the render engine. */ for (int ty = 0; ty < h; ty++) { @@ -283,23 +281,45 @@ static void render_result_to_bake(RenderEngine *engine, RenderResult *rr) /* Render Results */ -static RenderPart *get_part_from_result(Render *re, RenderResult *result) -{ - rcti key = result->tilerect; - BLI_rcti_translate(&key, re->disprect.xmin, re->disprect.ymin); - - return BLI_ghash_lookup(re->parts, &key); -} - -static HighlightedTile highlighted_tile_from_result_get(Render *re, RenderResult *result) +static HighlightedTile highlighted_tile_from_result_get(Render *UNUSED(re), RenderResult *result) { HighlightedTile tile; tile.rect = result->tilerect; - BLI_rcti_translate(&tile.rect, re->disprect.xmin, re->disprect.ymin); return tile; } +static void engine_tile_highlight_set(RenderEngine *engine, + const HighlightedTile *tile, + bool highlight) +{ + if ((engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { + return; + } + + Render *re = engine->re; + + BLI_mutex_lock(&re->highlighted_tiles_mutex); + + if (re->highlighted_tiles == NULL) { + re->highlighted_tiles = BLI_gset_new( + BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, "highlighted tiles"); + } + + if (highlight) { + HighlightedTile **tile_in_set; + if (!BLI_gset_ensure_p_ex(re->highlighted_tiles, tile, (void ***)&tile_in_set)) { + *tile_in_set = MEM_mallocN(sizeof(HighlightedTile), __func__); + **tile_in_set = *tile; + } + } + else { + BLI_gset_remove(re->highlighted_tiles, tile, MEM_freeN); + } + + BLI_mutex_unlock(&re->highlighted_tiles_mutex); +} + RenderResult *RE_engine_begin_result( RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) { @@ -332,7 +352,7 @@ RenderResult *RE_engine_begin_result( disprect.ymin = y; disprect.ymax = y + h; - result = render_result_new(re, &disprect, RR_USE_MEM, layername, viewname); + result = render_result_new(re, &disprect, layername, viewname); /* TODO: make this thread safe. */ @@ -341,25 +361,12 @@ RenderResult *RE_engine_begin_result( render_result_clone_passes(re, result, viewname); render_result_passes_allocated_ensure(result); - RenderPart *pa; - - /* Copy EXR tile settings, so pipeline knows whether this is a result - * for Save Buffers enabled rendering. - */ - result->do_exr_tile = re->result->do_exr_tile; - BLI_addtail(&engine->fullresult, result); result->tilerect.xmin += re->disprect.xmin; result->tilerect.xmax += re->disprect.xmin; result->tilerect.ymin += re->disprect.ymin; result->tilerect.ymax += re->disprect.ymin; - - pa = get_part_from_result(re, result); - - if (pa) { - pa->status = PART_STATUS_IN_PROGRESS; - } } return result; @@ -405,7 +412,7 @@ void RE_engine_add_pass(RenderEngine *engine, return; } - RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL); + RE_create_render_pass(re->result, name, channels, chan_id, layername, NULL, false); } void RE_engine_end_result( @@ -426,53 +433,14 @@ void RE_engine_end_result( re_ensure_passes_allocated_thread_safe(re); - /* merge. on break, don't merge in result for preview renders, looks nicer */ - if (!highlight) { - /* for exr tile render, detect tiles that are done */ - RenderPart *pa = get_part_from_result(re, result); - - if (pa) { - pa->status = (!cancel && merge_results) ? PART_STATUS_MERGED : PART_STATUS_RENDERED; - } - else if (re->result->do_exr_tile) { - /* If written result does not match any tile and we are using save - * buffers, we are going to get OpenEXR save errors. */ - fprintf(stderr, "RenderEngine.end_result: dimensions do not match any OpenEXR tile.\n"); - } - } - if (re->engine && (re->engine->flag & RE_ENGINE_HIGHLIGHT_TILES)) { - BLI_mutex_lock(&re->highlighted_tiles_mutex); + const HighlightedTile tile = highlighted_tile_from_result_get(re, result); - if (re->highlighted_tiles == NULL) { - re->highlighted_tiles = BLI_gset_new( - BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, "highlighted tiles"); - } - - HighlightedTile tile = highlighted_tile_from_result_get(re, result); - if (highlight) { - void **tile_in_set; - if (!BLI_gset_ensure_p_ex(re->highlighted_tiles, &tile, &tile_in_set)) { - *tile_in_set = MEM_mallocN(sizeof(HighlightedTile), __func__); - memcpy(*tile_in_set, &tile, sizeof(tile)); - } - BLI_gset_add(re->highlighted_tiles, &tile); - } - else { - BLI_gset_remove(re->highlighted_tiles, &tile, MEM_freeN); - } - - BLI_mutex_unlock(&re->highlighted_tiles_mutex); + engine_tile_highlight_set(engine, &tile, highlight); } if (!cancel || merge_results) { - if (re->result->do_exr_tile) { - if (!cancel && merge_results) { - render_result_exr_file_merge(re->result, result, re->viewname); - render_result_merge(re->result, result); - } - } - else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) { + if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) { render_result_merge(re->result, result); } @@ -582,6 +550,27 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg) } } +RenderPass *RE_engine_pass_by_index_get(RenderEngine *engine, const char *layer_name, int index) +{ + Render *re = engine->re; + if (re == NULL) { + return NULL; + } + + RenderPass *pass = NULL; + + RenderResult *rr = RE_AcquireResultRead(re); + if (rr != NULL) { + const RenderLayer *layer = RE_GetRenderLayer(rr, layer_name); + if (layer != NULL) { + pass = BLI_findlink(&layer->passes, index); + } + } + RE_ReleaseResult(re); + + return pass; +} + const char *RE_engine_active_view_get(RenderEngine *engine) { Render *re = engine->re; @@ -837,12 +826,6 @@ bool RE_bake_engine(Render *re, engine->resolution_x = re->winx; engine->resolution_y = re->winy; - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - RE_parts_init(re); - engine->tile_x = re->r.tilex; - engine->tile_y = re->r.tiley; - BLI_rw_mutex_unlock(&re->partsmutex); - if (type->bake) { engine->depsgraph = depsgraph; @@ -870,21 +853,13 @@ bool RE_bake_engine(Render *re, engine->depsgraph = NULL; } - engine->tile_x = 0; - engine->tile_y = 0; engine->flag &= ~RE_ENGINE_RENDERING; - /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context - * while the UI drawing might also lock the OpenGL context and parts mutex. */ engine_depsgraph_free(engine); - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); RE_engine_free(engine); re->engine = NULL; - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); - if (BKE_reports_contain(re->reports, RPT_ERROR)) { G.is_break = true; } @@ -928,15 +903,23 @@ static void engine_render_view_layer(Render *re, DRW_render_context_enable(engine->re); } + BLI_mutex_lock(&engine->re->engine_draw_mutex); + re->engine->flag |= RE_ENGINE_CAN_DRAW; + BLI_mutex_unlock(&engine->re->engine_draw_mutex); + engine->type->render(engine, engine->depsgraph); + BLI_mutex_lock(&engine->re->engine_draw_mutex); + re->engine->flag &= ~RE_ENGINE_CAN_DRAW; + BLI_mutex_unlock(&engine->re->engine_draw_mutex); + if (use_gpu_context) { DRW_render_context_disable(engine->re); } } /* Optionally composite grease pencil over render result. */ - if (engine->has_grease_pencil && use_grease_pencil && !re->result->do_exr_tile) { + if (engine->has_grease_pencil && use_grease_pencil) { /* NOTE: External engine might have been requested to free its * dependency graph, which is only allowed if there is no grease * pencil (pipeline is taking care of that). */ @@ -981,16 +964,11 @@ bool RE_engine_render(Render *re, bool do_all) /* create render result */ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); if (re->result == NULL || !(re->r.scemode & R_BUTS_PREVIEW)) { - int savebuffers = RR_USE_MEM; - if (re->result) { render_result_free(re->result); } - if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) { - savebuffers = RR_USE_EXR; - } - re->result = render_result_new(re, &re->disprect, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); @@ -1035,32 +1013,15 @@ bool RE_engine_render(Render *re, bool do_all) engine->resolution_x = re->winx; engine->resolution_y = re->winy; - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - RE_parts_init(re); - engine->tile_x = re->partx; - engine->tile_y = re->party; - BLI_rw_mutex_unlock(&re->partsmutex); - - if (re->result->do_exr_tile) { - render_result_exr_file_begin(re, engine); - } - /* Clear UI drawing locks. */ if (re->draw_lock) { re->draw_lock(re->dlh, false); } - /* Render view layers. */ - bool delay_grease_pencil = false; - if (type->render) { FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { engine_render_view_layer(re, engine, view_layer_iter, true, true); - /* With save buffers there is no render buffer in memory for compositing, delay - * grease pencil in that case. */ - delay_grease_pencil = engine->has_grease_pencil && re->result->do_exr_tile; - if (RE_engine_test_break(engine)) { break; } @@ -1068,42 +1029,18 @@ bool RE_engine_render(Render *re, bool do_all) FOREACH_VIEW_LAYER_TO_RENDER_END; } + if (type->render_frame_finish) { + type->render_frame_finish(engine); + } + /* Clear tile data */ - engine->tile_x = 0; - engine->tile_y = 0; engine->flag &= ~RE_ENGINE_RENDERING; render_result_free_list(&engine->fullresult, engine->fullresult.first); - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - - /* For save buffers, read back from disk. */ - if (re->result->do_exr_tile) { - render_result_exr_file_end(re, engine); - } - - /* Perform delayed grease pencil rendering. */ - if (delay_grease_pencil) { - BLI_rw_mutex_unlock(&re->partsmutex); - - FOREACH_VIEW_LAYER_TO_RENDER_BEGIN (re, view_layer_iter) { - engine_render_view_layer(re, engine, view_layer_iter, false, true); - if (RE_engine_test_break(engine)) { - break; - } - } - FOREACH_VIEW_LAYER_TO_RENDER_END; - - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); - } - /* re->engine becomes zero if user changed active render engine during render */ if (!engine_keep_depsgraph(engine) || !re->engine) { - /* Free depsgraph outside of parts mutex lock, since this locks OpenGL context - * while the UI drawing might also lock the OpenGL context and parts mutex. */ - BLI_rw_mutex_unlock(&re->partsmutex); engine_depsgraph_free(engine); - BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); RE_engine_free(engine); re->engine = NULL; @@ -1115,9 +1052,6 @@ bool RE_engine_render(Render *re, bool do_all) BLI_rw_mutex_unlock(&re->resultmutex); } - RE_parts_free(re); - BLI_rw_mutex_unlock(&re->partsmutex); - if (BKE_reports_contain(re->reports, RPT_ERROR)) { G.is_break = true; } @@ -1179,3 +1113,81 @@ void RE_engine_free_blender_memory(RenderEngine *engine) } engine_depsgraph_free(engine); } + +struct RenderEngine *RE_engine_get(const Render *re) +{ + return re->engine; +} + +bool RE_engine_draw_acquire(Render *re) +{ + BLI_mutex_lock(&re->engine_draw_mutex); + + RenderEngine *engine = re->engine; + + if (engine == NULL || engine->type->draw == NULL || (engine->flag & RE_ENGINE_CAN_DRAW) == 0) { + BLI_mutex_unlock(&re->engine_draw_mutex); + return false; + } + + return true; +} + +void RE_engine_draw_release(Render *re) +{ + BLI_mutex_unlock(&re->engine_draw_mutex); +} + +void RE_engine_tile_highlight_set( + RenderEngine *engine, int x, int y, int width, int height, bool highlight) +{ + HighlightedTile tile; + BLI_rcti_init(&tile.rect, x, x + width, y, y + height); + + engine_tile_highlight_set(engine, &tile, highlight); +} + +void RE_engine_tile_highlight_clear_all(RenderEngine *engine) +{ + if ((engine->flag & RE_ENGINE_HIGHLIGHT_TILES) == 0) { + return; + } + + Render *re = engine->re; + + BLI_mutex_lock(&re->highlighted_tiles_mutex); + + if (re->highlighted_tiles != NULL) { + BLI_gset_clear(re->highlighted_tiles, MEM_freeN); + } + + BLI_mutex_unlock(&re->highlighted_tiles_mutex); +} + +/* -------------------------------------------------------------------- */ +/** \name OpenGL context manipulation. + * + * NOTE: Only used for Cycles's BLenderGPUDisplay integration with the draw manager. A subject + * for re-consideration. Do not use this functionality. + * \{ */ + +bool RE_engine_has_render_context(RenderEngine *engine) +{ + if (engine->re == NULL) { + return false; + } + + return RE_gl_context_get(engine->re) != NULL; +} + +void RE_engine_render_context_enable(RenderEngine *engine) +{ + DRW_render_context_enable(engine->re); +} + +void RE_engine_render_context_disable(RenderEngine *engine) +{ + DRW_render_context_disable(engine->re); +} + +/** \} */ diff --git a/source/blender/render/intern/initrender.c b/source/blender/render/intern/initrender.c index 3148625c866..2370d8e893b 100644 --- a/source/blender/render/intern/initrender.c +++ b/source/blender/render/intern/initrender.c @@ -43,9 +43,6 @@ #include "pipeline.h" #include "render_types.h" -/* Own includes */ -#include "initrender.h" - /* ****************** MASKS and LUTS **************** */ static float filt_quadratic(float x) @@ -244,91 +241,3 @@ void RE_GetViewPlane(Render *re, rctf *r_viewplane, rcti *r_disprect) BLI_rcti_init(r_disprect, 0, 0, 0, 0); } } - -/* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */ - -void RE_parts_free(Render *re) -{ - if (re->parts) { - BLI_ghash_free(re->parts, NULL, MEM_freeN); - re->parts = NULL; - } -} - -void RE_parts_clamp(Render *re) -{ - /* part size */ - re->partx = max_ii(1, min_ii(re->r.tilex, re->rectx)); - re->party = max_ii(1, min_ii(re->r.tiley, re->recty)); -} - -void RE_parts_init(Render *re) -{ - int nr, xd, yd, partx, party, xparts, yparts; - int xminb, xmaxb, yminb, ymaxb; - - RE_parts_free(re); - - re->parts = BLI_ghash_new( - BLI_ghashutil_inthash_v4_p, BLI_ghashutil_inthash_v4_cmp, "render parts"); - - /* Just for readable code. */ - xminb = re->disprect.xmin; - yminb = re->disprect.ymin; - xmaxb = re->disprect.xmax; - ymaxb = re->disprect.ymax; - - RE_parts_clamp(re); - - partx = re->partx; - party = re->party; - /* part count */ - xparts = (re->rectx + partx - 1) / partx; - yparts = (re->recty + party - 1) / party; - - for (nr = 0; nr < xparts * yparts; nr++) { - rcti disprect; - int rectx, recty; - - xd = (nr % xparts); - yd = (nr - xd) / xparts; - - disprect.xmin = xminb + xd * partx; - disprect.ymin = yminb + yd * party; - - /* ensure we cover the entire picture, so last parts go to end */ - if (xd < xparts - 1) { - disprect.xmax = disprect.xmin + partx; - if (disprect.xmax > xmaxb) { - disprect.xmax = xmaxb; - } - } - else { - disprect.xmax = xmaxb; - } - - if (yd < yparts - 1) { - disprect.ymax = disprect.ymin + party; - if (disprect.ymax > ymaxb) { - disprect.ymax = ymaxb; - } - } - else { - disprect.ymax = ymaxb; - } - - rectx = BLI_rcti_size_x(&disprect); - recty = BLI_rcti_size_y(&disprect); - - /* so, now can we add this part? */ - if (rectx > 0 && recty > 0) { - RenderPart *pa = MEM_callocN(sizeof(RenderPart), "new part"); - - pa->disprect = disprect; - pa->rectx = rectx; - pa->recty = recty; - - BLI_ghash_insert(re->parts, &pa->disprect, pa); - } - } -} diff --git a/source/blender/render/intern/pipeline.c b/source/blender/render/intern/pipeline.c index 5418f4035b1..1bf0dfe079c 100644 --- a/source/blender/render/intern/pipeline.c +++ b/source/blender/render/intern/pipeline.c @@ -102,7 +102,6 @@ #include "DEG_depsgraph.h" /* internal */ -#include "initrender.h" #include "pipeline.h" #include "render_result.h" #include "render_types.h" @@ -568,7 +567,7 @@ Render *RE_NewRender(const char *name) BLI_addtail(&RenderGlobal.renderlist, re); BLI_strncpy(re->name, name, RE_MAXNAME); BLI_rw_mutex_init(&re->resultmutex); - BLI_rw_mutex_init(&re->partsmutex); + BLI_mutex_init(&re->engine_draw_mutex); BLI_mutex_init(&re->highlighted_tiles_mutex); } @@ -633,7 +632,7 @@ void RE_FreeRender(Render *re) } BLI_rw_mutex_end(&re->resultmutex); - BLI_rw_mutex_end(&re->partsmutex); + BLI_mutex_end(&re->engine_draw_mutex); BLI_mutex_end(&re->highlighted_tiles_mutex); BLI_freelistN(&re->view_layers); @@ -722,26 +721,6 @@ void RE_FreePersistentData(const Scene *scene) /* ********* initialize state ******** */ -/* clear full sample and tile flags if needed */ -static int check_mode_full_sample(RenderData *rd) -{ - int scemode = rd->scemode; - - /* not supported by any current renderer */ - scemode &= ~R_FULL_SAMPLE; - -#ifdef WITH_OPENEXR - if (scemode & R_FULL_SAMPLE) { - scemode |= R_EXR_TILE_FILE; /* enable automatic */ - } -#else - /* can't do this without openexr support */ - scemode &= ~(R_EXR_TILE_FILE | R_FULL_SAMPLE); -#endif - - return scemode; -} - static void re_init_resolution(Render *re, Render *source, int winx, int winy, rcti *disprect) { re->winx = winx; @@ -839,8 +818,6 @@ void RE_InitState(Render *re, return; } - re->r.scemode = check_mode_full_sample(&re->r); - if (single_layer) { int index = BLI_findindex(render_layers, single_layer); if (index != -1) { @@ -890,9 +867,6 @@ void RE_InitState(Render *re, render_result_view_new(re->result, ""); } - /* ensure renderdatabase can use part settings correct */ - RE_parts_clamp(re); - BLI_rw_mutex_unlock(&re->resultmutex); RE_init_threadcount(re); @@ -1040,11 +1014,11 @@ static void render_result_uncrop(Render *re) /* weak is: it chances disprect from border */ render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); - render_result_passes_allocated_ensure(rres); + rres = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); rres->stamp_data = BKE_stamp_data_copy(re->result->stamp_data); render_result_clone_passes(re, rres, NULL); + render_result_passes_allocated_ensure(rres); render_result_merge(rres, re->result); render_result_free(re->result); @@ -1227,7 +1201,7 @@ static void do_render_compositor(Render *re) if ((re->r.mode & R_CROP) == 0) { render_result_disprect_to_full_resolution(re); } - re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + re->result = render_result_new(re, &re->disprect, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); @@ -1466,7 +1440,7 @@ static void do_render_full_pipeline(Render *re) /* ensure no images are in memory from previous animated sequences */ BKE_image_all_free_anim_ibufs(re->main, re->r.cfra); - SEQ_relations_free_all_anim_ibufs(re->scene, re->r.cfra); + SEQ_cache_cleanup(re->scene); if (RE_engine_render(re, true)) { /* in this case external render overrides all */ @@ -1647,7 +1621,7 @@ bool RE_is_rendering_allowed(Scene *scene, Object *camera_override, ReportList *reports) { - int scemode = check_mode_full_sample(&scene->r); + const int scemode = scene->r.scemode; if (scene->r.mode & R_BORDER) { if (scene->r.border.xmax <= scene->r.border.xmin || @@ -1657,17 +1631,6 @@ bool RE_is_rendering_allowed(Scene *scene, } } - if (scemode & (R_EXR_TILE_FILE | R_FULL_SAMPLE)) { - char str[FILE_MAX]; - - render_result_exr_file_path(scene, "", 0, str); - - if (!BLI_file_is_writable(str)) { - BKE_report(reports, RPT_ERROR, "Cannot save render buffers, check the temp default path"); - return 0; - } - } - if (RE_seq_render_active(scene, &scene->r)) { /* Sequencer */ if (scene->r.mode & R_BORDER) { @@ -1686,13 +1649,6 @@ bool RE_is_rendering_allowed(Scene *scene, BKE_report(reports, RPT_ERROR, "No render output node in scene"); return 0; } - - if (scemode & R_FULL_SAMPLE) { - if (compositor_needs_render(scene, 0) == 0) { - BKE_report(reports, RPT_ERROR, "Full sample AA not supported without 3D rendering"); - return 0; - } - } } else { /* Regular Render */ @@ -1710,14 +1666,6 @@ bool RE_is_rendering_allowed(Scene *scene, return 1; } -static void validate_render_settings(Render *re) -{ - if (RE_engine_is_external(re)) { - /* not supported yet */ - re->r.scemode &= ~R_FULL_SAMPLE; - } -} - static void update_physics_cache(Render *re, Scene *scene, ViewLayer *view_layer, @@ -1820,8 +1768,6 @@ static int render_init_from_main(Render *re, /* initstate makes new result, have to send changed tags around */ ntreeCompositTagRender(re->scene); - validate_render_settings(re); - re->display_init(re->dih, re->result); re->display_clear(re->dch, re->result); @@ -2871,7 +2817,7 @@ RenderPass *RE_create_gp_pass(RenderResult *rr, const char *layername, const cha BLI_freelinkN(&rl->passes, rp); } /* create a totally new pass */ - return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA"); + return render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, viewname, "RGBA", true); } bool RE_allow_render_generic_object(Object *ob) diff --git a/source/blender/render/intern/render_result.c b/source/blender/render/intern/render_result.c index 6cb6aabe885..0681bcd9aa5 100644 --- a/source/blender/render/intern/render_result.c +++ b/source/blender/render/intern/render_result.c @@ -213,12 +213,37 @@ static void set_pass_full_name( /********************************** New **************************************/ +static void render_layer_allocate_pass(RenderResult *rr, RenderPass *rp) +{ + if (rp->rect != NULL) { + return; + } + + const size_t rectsize = ((size_t)rr->rectx) * rr->recty * rp->channels; + rp->rect = MEM_callocN(sizeof(float) * rectsize, rp->name); + + if (STREQ(rp->name, RE_PASSNAME_VECTOR)) { + /* initialize to max speed */ + float *rect = rp->rect; + for (int x = rectsize - 1; x >= 0; x--) { + rect[x] = PASS_VECTOR_MAX; + } + } + else if (STREQ(rp->name, RE_PASSNAME_Z)) { + float *rect = rp->rect; + for (int x = rectsize - 1; x >= 0; x--) { + rect[x] = 10e10; + } + } +} + RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, const char *name, const char *viewname, - const char *chan_id) + const char *chan_id, + const bool allocate) { const int view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name)); RenderPass *rpass = MEM_callocN(sizeof(RenderPass), name); @@ -250,8 +275,13 @@ RenderPass *render_layer_add_pass(RenderResult *rr, BLI_addtail(&rl->passes, rpass); - /* The result contains non-allocated pass now, so tag it as such. */ - rr->passes_allocated = false; + if (allocate) { + render_layer_allocate_pass(rr, rpass); + } + else { + /* The result contains non-allocated pass now, so tag it as such. */ + rr->passes_allocated = false; + } return rpass; } @@ -260,8 +290,10 @@ RenderPass *render_layer_add_pass(RenderResult *rr, /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new( - Render *re, rcti *partrct, int savebuffers, const char *layername, const char *viewname) +RenderResult *render_result_new(Render *re, + rcti *partrct, + const char *layername, + const char *viewname) { RenderResult *rr; RenderLayer *rl; @@ -287,10 +319,6 @@ RenderResult *render_result_new( rr->tilerect.ymin = partrct->ymin - re->disprect.ymin; rr->tilerect.ymax = partrct->ymax - re->disprect.ymin; - if (savebuffers) { - rr->do_exr_tile = true; - } - rr->passes_allocated = false; render_result_views_new(rr, &re->r); @@ -314,10 +342,6 @@ RenderResult *render_result_new( rl->rectx = rectx; rl->recty = recty; - if (rr->do_exr_tile) { - rl->exrhandle = IMB_exr_get_handle(); - } - for (rv = rr->views.first; rv; rv = rv->next) { const char *view = rv->name; @@ -327,20 +351,16 @@ RenderResult *render_result_new( } } - if (rr->do_exr_tile) { - IMB_exr_add_view(rl->exrhandle, view); - } - #define RENDER_LAYER_ADD_PASS_SAFE(rr, rl, channels, name, viewname, chan_id) \ do { \ - if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id) == NULL) { \ + if (render_layer_add_pass(rr, rl, channels, name, viewname, chan_id, false) == NULL) { \ render_result_free(rr); \ return NULL; \ } \ } while (false) /* A renderlayer should always have a Combined pass. */ - render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA"); + render_layer_add_pass(rr, rl, 4, "Combined", view, "RGBA", false); if (view_layer->passflag & SCE_PASS_Z) { RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 1, RE_PASSNAME_Z, view, "Z"); @@ -351,6 +371,9 @@ RenderResult *render_result_new( if (view_layer->passflag & SCE_PASS_NORMAL) { RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_NORMAL, view, "XYZ"); } + if (view_layer->passflag & SCE_PASS_POSITION) { + RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_POSITION, view, "XYZ"); + } if (view_layer->passflag & SCE_PASS_UV) { RENDER_LAYER_ADD_PASS_SAFE(rr, rl, 3, RE_PASSNAME_UV, view, "UVA"); } @@ -424,11 +447,6 @@ RenderResult *render_result_new( rl->rectx = rectx; rl->recty = recty; - /* duplicate code... */ - if (rr->do_exr_tile) { - rl->exrhandle = IMB_exr_get_handle(); - } - for (rv = rr->views.first; rv; rv = rv->next) { const char *view = rv->name; @@ -438,12 +456,8 @@ RenderResult *render_result_new( } } - if (rr->do_exr_tile) { - IMB_exr_add_view(rl->exrhandle, view); - } - /* a renderlayer should always have a Combined pass */ - render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA"); + render_layer_add_pass(rr, rl, 4, RE_PASSNAME_COMBINED, view, "RGBA", false); } /* NOTE: this has to be in sync with `scene.c`. */ @@ -469,26 +483,7 @@ void render_result_passes_allocated_ensure(RenderResult *rr) continue; } - if (rp->rect != NULL) { - continue; - } - - const size_t rectsize = ((size_t)rr->rectx) * rr->recty * rp->channels; - rp->rect = MEM_callocN(sizeof(float) * rectsize, rp->name); - - if (STREQ(rp->name, RE_PASSNAME_VECTOR)) { - /* initialize to max speed */ - float *rect = rp->rect; - for (int x = rectsize - 1; x >= 0; x--) { - rect[x] = PASS_VECTOR_MAX; - } - } - else if (STREQ(rp->name, RE_PASSNAME_Z)) { - float *rect = rp->rect; - for (int x = rectsize - 1; x >= 0; x--) { - rect[x] = 10e10; - } - } + render_layer_allocate_pass(rr, rp); } } @@ -517,7 +512,7 @@ void render_result_clone_passes(Render *re, RenderResult *rr, const char *viewna &rl->passes, main_rp->fullname, offsetof(RenderPass, fullname)); if (!rp) { render_layer_add_pass( - rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id); + rr, rl, main_rp->channels, main_rp->name, main_rp->view, main_rp->chan_id, false); } } } @@ -528,7 +523,8 @@ void RE_create_render_pass(RenderResult *rr, int channels, const char *chan_id, const char *layername, - const char *viewname) + const char *viewname, + const bool allocate) { RenderLayer *rl; RenderPass *rp; @@ -558,7 +554,7 @@ void RE_create_render_pass(RenderResult *rr, } if (!rp) { - render_layer_add_pass(rr, rl, channels, name, view, chan_id); + render_layer_add_pass(rr, rl, channels, name, view, chan_id, allocate); } } } @@ -1089,227 +1085,6 @@ void render_result_single_layer_end(Render *re) re->pushedresult = NULL; } -/************************* EXR Tile File Rendering ***************************/ - -static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, const char *viewname) -{ - RenderLayer *rlp, *rl; - RenderPass *rpassp; - int partx, party; - - BLI_thread_lock(LOCK_IMAGE); - - for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { - rl = RE_GetRenderLayer(rr, rlp->name); - - /* should never happen but prevents crash if it does */ - BLI_assert(rl); - if (UNLIKELY(rl == NULL)) { - continue; - } - - /* passes are allocated in sync */ - for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { - const int xstride = rpassp->channels; - int a; - char fullname[EXR_PASS_MAXNAME]; - - for (a = 0; a < xstride; a++) { - set_pass_full_name(fullname, rpassp->name, a, viewname, rpassp->chan_id); - - IMB_exr_set_channel(rl->exrhandle, - rlp->name, - fullname, - xstride, - xstride * rrpart->rectx, - rpassp->rect + a); - } - } - } - - party = rrpart->tilerect.ymin; - partx = rrpart->tilerect.xmin; - - for (rlp = rrpart->layers.first; rlp; rlp = rlp->next) { - rl = RE_GetRenderLayer(rr, rlp->name); - - /* should never happen but prevents crash if it does */ - BLI_assert(rl); - if (UNLIKELY(rl == NULL)) { - continue; - } - - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname, false); - } - - BLI_thread_unlock(LOCK_IMAGE); -} - -void render_result_save_empty_result_tiles(Render *re) -{ - RenderResult *rr; - RenderLayer *rl; - - for (rr = re->result; rr; rr = rr->next) { - for (rl = rr->layers.first; rl; rl = rl->next) { - GHashIterator pa_iter; - GHASH_ITER (pa_iter, re->parts) { - RenderPart *pa = BLI_ghashIterator_getValue(&pa_iter); - if (pa->status != PART_STATUS_MERGED) { - int party = pa->disprect.ymin - re->disprect.ymin; - int partx = pa->disprect.xmin - re->disprect.xmin; - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname, true); - } - } - } - } -} - -/* Compute list of passes needed by render engine. */ -static void templates_register_pass_cb(void *userdata, - Scene *UNUSED(scene), - ViewLayer *UNUSED(view_layer), - const char *name, - int channels, - const char *chan_id, - eNodeSocketDatatype UNUSED(type)) -{ - ListBase *templates = userdata; - RenderPass *pass = MEM_callocN(sizeof(RenderPass), "RenderPassTemplate"); - - pass->channels = channels; - BLI_strncpy(pass->name, name, sizeof(pass->name)); - BLI_strncpy(pass->chan_id, chan_id, sizeof(pass->chan_id)); - - BLI_addtail(templates, pass); -} - -static void render_result_get_pass_templates(RenderEngine *engine, - Render *re, - RenderLayer *rl, - ListBase *templates) -{ - BLI_listbase_clear(templates); - - if (engine && engine->type->update_render_passes) { - ViewLayer *view_layer = BLI_findstring(&re->view_layers, rl->name, offsetof(ViewLayer, name)); - if (view_layer) { - RE_engine_update_render_passes( - engine, re->scene, view_layer, templates_register_pass_cb, templates); - } - } -} - -/* begin write of exr tile file */ -void render_result_exr_file_begin(Render *re, RenderEngine *engine) -{ - char str[FILE_MAX]; - - for (RenderResult *rr = re->result; rr; rr = rr->next) { - LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) { - /* Get passes needed by engine. Normally we would wait for the - * engine to create them, but for EXR file we need to know in - * advance. */ - ListBase templates; - render_result_get_pass_templates(engine, re, rl, &templates); - - /* Create render passes requested by engine. Only this part is - * mutex locked to avoid deadlock with Python GIL. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - LISTBASE_FOREACH (RenderPass *, pass, &templates) { - RE_create_render_pass( - re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); - } - BLI_rw_mutex_unlock(&re->resultmutex); - - BLI_freelistN(&templates); - - /* Open EXR file for writing. */ - render_result_exr_file_path(re->scene, rl->name, rr->sample_nr, str); - printf("write exr tmp file, %dx%d, %s\n", rr->rectx, rr->recty, str); - IMB_exrtile_begin_write(rl->exrhandle, str, 0, rr->rectx, rr->recty, re->partx, re->party); - } - } -} - -/* end write of exr tile file, read back first sample */ -void render_result_exr_file_end(Render *re, RenderEngine *engine) -{ - /* Preserve stamp data. */ - struct StampData *stamp_data = re->result->stamp_data; - re->result->stamp_data = NULL; - - /* Close EXR files. */ - for (RenderResult *rr = re->result; rr; rr = rr->next) { - LISTBASE_FOREACH (RenderLayer *, rl, &rr->layers) { - IMB_exr_close(rl->exrhandle); - rl->exrhandle = NULL; - } - - rr->do_exr_tile = false; - } - - /* Create new render result in memory instead of on disk. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_free_list(&re->fullresult, re->result); - re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); - re->result->stamp_data = stamp_data; - render_result_passes_allocated_ensure(re->result); - BLI_rw_mutex_unlock(&re->resultmutex); - - LISTBASE_FOREACH (RenderLayer *, rl, &re->result->layers) { - /* Get passes needed by engine. */ - ListBase templates; - render_result_get_pass_templates(engine, re, rl, &templates); - - /* Create render passes requested by engine. Only this part is - * mutex locked to avoid deadlock with Python GIL. */ - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - LISTBASE_FOREACH (RenderPass *, pass, &templates) { - RE_create_render_pass(re->result, pass->name, pass->channels, pass->chan_id, rl->name, NULL); - } - - BLI_freelistN(&templates); - - /* Render passes contents from file. */ - char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; - render_result_exr_file_path(re->scene, rl->name, 0, str); - printf("read exr tmp file: %s\n", str); - - if (!render_result_exr_file_read_path(re->result, rl, str)) { - printf("cannot read: %s\n", str); - } - BLI_rw_mutex_unlock(&re->resultmutex); - } -} - -/* save part into exr file */ -void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart, const char *viewname) -{ - for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next) { - save_render_result_tile(rr, rrpart, viewname); - } -} - -/* path to temporary exr file */ -void render_result_exr_file_path(Scene *scene, const char *layname, int sample, char *filepath) -{ - char name[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100]; - const char *fi = BLI_path_basename(BKE_main_blendfile_path_from_global()); - - if (sample == 0) { - BLI_snprintf(name, sizeof(name), "%s_%s_%s.exr", fi, scene->id.name + 2, layname); - } - else { - BLI_snprintf(name, sizeof(name), "%s_%s_%s%d.exr", fi, scene->id.name + 2, layname, sample); - } - - /* Make name safe for paths, see T43275. */ - BLI_filename_make_safe(name); - - BLI_join_dirfile(filepath, FILE_MAX, BKE_tempdir_session(), name); -} - /* called for reading temp files, and for external engines */ int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, @@ -1320,7 +1095,7 @@ int render_result_exr_file_read_path(RenderResult *rr, void *exrhandle = IMB_exr_get_handle(); int rectx, recty; - if (IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty) == 0) { + if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, false)) { printf("failed being read %s\n", filepath); IMB_exr_close(exrhandle); return 0; @@ -1412,20 +1187,32 @@ void render_result_exr_file_cache_write(Render *re) /* For cache, makes exact copy of render result */ bool render_result_exr_file_cache_read(Render *re) { - char str[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; + /* File path to cache. */ + char filepath[FILE_MAXFILE + MAX_ID_NAME + MAX_ID_NAME + 100] = ""; char *root = U.render_cachedir; + render_result_exr_file_cache_path(re->scene, root, filepath); - RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); + printf("read exr cache file: %s\n", filepath); - /* First try cache. */ - render_result_exr_file_cache_path(re->scene, root, str); + /* Try opening the file. */ + void *exrhandle = IMB_exr_get_handle(); + int rectx, recty; - printf("read exr cache file: %s\n", str); - if (!render_result_exr_file_read_path(re->result, NULL, str)) { - printf("cannot read: %s\n", str); + if (!IMB_exr_begin_read(exrhandle, filepath, &rectx, &recty, true)) { + printf("cannot read: %s\n", filepath); + IMB_exr_close(exrhandle); return false; } + + /* Read file contents into render result. */ + const char *colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + RE_FreeRenderResult(re->result); + + IMB_exr_read_channels(exrhandle); + re->result = render_result_new_from_exr(exrhandle, colorspace, false, rectx, recty); + + IMB_exr_close(exrhandle); + return true; } diff --git a/source/blender/render/intern/render_result.h b/source/blender/render/intern/render_result.h index 1fc64a4ea97..34b8143869c 100644 --- a/source/blender/render/intern/render_result.h +++ b/source/blender/render/intern/render_result.h @@ -25,9 +25,6 @@ #define PASS_VECTOR_MAX 10000.0f -#define RR_USE_MEM 0 -#define RR_USE_EXR 1 - #define RR_ALL_LAYERS NULL #define RR_ALL_VIEWS NULL @@ -51,7 +48,6 @@ extern "C" { struct RenderResult *render_result_new(struct Render *re, struct rcti *partrct, - int savebuffers, const char *layername, const char *viewname); @@ -81,28 +77,15 @@ void render_result_free_list(struct ListBase *lb, struct RenderResult *rr); void render_result_single_layer_begin(struct Render *re); void render_result_single_layer_end(struct Render *re); -/* EXR Tile File Render */ - -void render_result_save_empty_result_tiles(struct Render *re); -void render_result_exr_file_begin(struct Render *re, struct RenderEngine *engine); -void render_result_exr_file_end(struct Render *re, struct RenderEngine *engine); - /* render pass wrapper for gpencil */ struct RenderPass *render_layer_add_pass(struct RenderResult *rr, struct RenderLayer *rl, int channels, const char *name, const char *viewname, - const char *chan_id); + const char *chan_id, + const bool allocate); -void render_result_exr_file_merge(struct RenderResult *rr, - struct RenderResult *rrpart, - const char *viewname); - -void render_result_exr_file_path(struct Scene *scene, - const char *layname, - int sample, - char *filepath); int render_result_exr_file_read_path(struct RenderResult *rr, struct RenderLayer *rl_single, const char *filepath); diff --git a/source/blender/render/intern/render_types.h b/source/blender/render/intern/render_types.h index d2d2b499495..ca4f72350e1 100644 --- a/source/blender/render/intern/render_types.h +++ b/source/blender/render/intern/render_types.h @@ -47,30 +47,10 @@ struct ReportList; extern "C" { #endif -/* this is handed over to threaded hiding/passes/shading engine */ -typedef struct RenderPart { - struct RenderPart *next, *prev; - - RenderResult *result; /* result of part rendering */ - ListBase fullresult; /* optional full sample buffers */ - - rcti disprect; /* part coordinates within total picture */ - int rectx, recty; /* the size */ - int nr; /* nr is partnr */ - short status; -} RenderPart; - typedef struct HighlightedTile { rcti rect; } HighlightedTile; -enum { - /* PART_STATUS_NONE = 0, */ /* UNUSED */ - PART_STATUS_IN_PROGRESS = 1, - PART_STATUS_RENDERED = 2, - PART_STATUS_MERGED = 3, -}; - /* controls state of render, everything that's read-only during render stage */ struct Render { struct Render *next, *prev; @@ -91,6 +71,9 @@ struct Render { * to not conflict with writes, so no lock used for that */ ThreadRWMutex resultmutex; + /* Guard for drawing render result using engine's `draw()` callback. */ + ThreadMutex engine_draw_mutex; + /** Window size, display rect, viewplane. * \note Buffer width and height with percentage applied * without border & crop. convert to long before multiplying together to avoid overflow. */ @@ -101,10 +84,6 @@ struct Render { /* final picture width and height (within disprect) */ int rectx, recty; - /* real maximum size of parts after correction for minimum - * partx*xparts can be larger than rectx, in that case last part is smaller */ - int partx, party; - /* Camera transform, only used by Freestyle. */ float winmat[4][4]; @@ -120,9 +99,6 @@ struct Render { int active_view_layer; struct Object *camera_override; - ThreadRWMutex partsmutex; - struct GHash *parts; - ThreadMutex highlighted_tiles_mutex; struct GSet *highlighted_tiles; diff --git a/source/blender/render/intern/texture_image.c b/source/blender/render/intern/texture_image.c index 62aee564626..dfeaec2341e 100644 --- a/source/blender/render/intern/texture_image.c +++ b/source/blender/render/intern/texture_image.c @@ -959,7 +959,7 @@ static void alpha_clip_aniso( /* TXF alpha: we're doing the same alpha-clip here as box-sample, but I'm doubting * if this is actually correct for the all the filtering algorithms. */ - if (!(extflag == TXC_REPT || extflag == TXC_EXTD)) { + if (!(ELEM(extflag, TXC_REPT, TXC_EXTD))) { rf.xmin = minx * (ibuf->x); rf.xmax = maxx * (ibuf->x); rf.ymin = miny * (ibuf->y); @@ -1958,13 +1958,11 @@ int imagewraposa(Tex *tex, } if (texres->nor && (tex->imaflag & TEX_NORMALMAP)) { - /* qdn: normal from color - * The invert of the red channel is to make - * the normal map compliant with the outside world. - * It needs to be done because in Blender - * the normal used in the renderer points inward. It is generated - * this way in calc_vertexnormals(). Should this ever change - * this negate must be removed. */ + /* Normal from color: + * The invert of the red channel is to make the normal map compliant with the outside world. + * It needs to be done because in Blender the normal used in the renderer points inward. + * It is generated this way in #calc_vertexnormals(). + * Should this ever change this negate must be removed. */ texres->nor[0] = -2.0f * (texres->tr - 0.5f); texres->nor[1] = 2.0f * (texres->tg - 0.5f); texres->nor[2] = 2.0f * (texres->tb - 0.5f); diff --git a/source/blender/render/intern/texture_procedural.c b/source/blender/render/intern/texture_procedural.c index bea9dfbb0ed..5a94bc09b93 100644 --- a/source/blender/render/intern/texture_procedural.c +++ b/source/blender/render/intern/texture_procedural.c @@ -745,7 +745,7 @@ static int mg_distNoiseTex(const Tex *tex, const float texvec[3], TexResult *tex /* ------------------------------------------------------------------------- */ /* newnoise: Voronoi texture type * - * probably the slowest, especially with minkovsky, bumpmapping, could be done another way. + * probably the slowest, especially with minkovsky, bump-mapping, could be done another way. */ static int voronoiTex(const Tex *tex, const float texvec[3], TexResult *texres) diff --git a/source/blender/sequencer/CMakeLists.txt b/source/blender/sequencer/CMakeLists.txt index f060e6ad69b..eccc336141a 100644 --- a/source/blender/sequencer/CMakeLists.txt +++ b/source/blender/sequencer/CMakeLists.txt @@ -63,6 +63,8 @@ set(SRC SEQ_utils.h intern/clipboard.c + intern/disk_cache.c + intern/disk_cache.h intern/effects.c intern/effects.h intern/image_cache.c diff --git a/source/blender/sequencer/SEQ_add.h b/source/blender/sequencer/SEQ_add.h index 4025f1a4a04..d2a731d9953 100644 --- a/source/blender/sequencer/SEQ_add.h +++ b/source/blender/sequencer/SEQ_add.h @@ -88,7 +88,7 @@ struct Sequence *SEQ_add_movie_strip(struct Main *bmain, struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data, - double *r_video_start_offset); + double *r_start_offset); struct Sequence *SEQ_add_scene_strip(struct Scene *scene, struct ListBase *seqbase, struct SeqLoadData *load_data); diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index 4f7d603fd6a..4de7c09640b 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -94,11 +94,20 @@ SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference, SeqCollection *collection)); SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase); SeqCollection *SEQ_query_unselected_strips(struct ListBase *seqbase); -SeqCollection *SEQ_query_all_strips(struct ListBase *seqbase); -SeqCollection *SEQ_query_all_strips_recursive(struct ListBase *seqbase); +SeqCollection *SEQ_query_all_strips(ListBase *seqbase); +SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase); +SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel); void SEQ_query_strip_effect_chain(struct Sequence *seq_reference, struct ListBase *seqbase, SeqCollection *collection); +void SEQ_filter_selected_strips(SeqCollection *collection); + +/* Utilities to access these as tags. */ +int SEQ_query_rendered_strips_to_tag(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel); #ifdef __cplusplus } diff --git a/source/blender/sequencer/SEQ_render.h b/source/blender/sequencer/SEQ_render.h index c138daf1318..e99dc6d344f 100644 --- a/source/blender/sequencer/SEQ_render.h +++ b/source/blender/sequencer/SEQ_render.h @@ -27,6 +27,8 @@ extern "C" { #endif +#define SEQ_RENDER_THUMB_SIZE 256 + struct ListBase; struct Main; struct Scene; @@ -67,6 +69,25 @@ struct ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, struct ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context, float timeline_frame, struct Sequence *seq); +void SEQ_render_thumbnails(const struct SeqRenderData *context, + struct Sequence *seq, + struct Sequence *seq_orig, + float start_frame, + float frame_step, + rctf *view_area, + const short *stop); +struct ImBuf *SEQ_get_thumbnail(const struct SeqRenderData *context, + struct Sequence *seq, + float timeline_frame, + rcti *crop, + bool clipped); +int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const struct Sequence *seq); +void SEQ_render_thumbnails_base_set(const struct SeqRenderData *context, + struct Sequence *seq, + struct Sequence *seq_orig, + rctf *view_area, + const short *stop); + void SEQ_render_init_colorspace(struct Sequence *seq); void SEQ_render_new_render_data(struct Main *bmain, struct Depsgraph *depsgraph, diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index d7800d208a4..1b8982da0d2 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -64,6 +64,7 @@ short SEQ_tool_settings_snap_flag_get(struct Scene *scene); short SEQ_tool_settings_snap_mode_get(struct Scene *scene); int SEQ_tool_settings_snap_distance_get(struct Scene *scene); eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(struct Scene *scene); +int SEQ_tool_settings_pivot_point_get(struct Scene *scene); struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); struct Editing *SEQ_editing_get(const struct Scene *scene); struct Editing *SEQ_editing_ensure(struct Scene *scene); @@ -88,6 +89,7 @@ void SEQ_sequence_base_dupli_recursive(const struct Scene *scene_src, const struct ListBase *seqbase, int dupe_flag, const int flag); +bool SEQ_valid_strip_channel(struct Sequence *seq); /* Read and Write functions for .blend file data */ void SEQ_blend_write(struct BlendWriter *writer, struct ListBase *seqbase); diff --git a/source/blender/sequencer/SEQ_time.h b/source/blender/sequencer/SEQ_time.h index 732e9bb985a..df3c9a40409 100644 --- a/source/blender/sequencer/SEQ_time.h +++ b/source/blender/sequencer/SEQ_time.h @@ -32,6 +32,8 @@ struct Scene; struct Sequence; struct rctf; +void SEQ_timeline_init_boundbox(const struct Scene *scene, struct rctf *rect); +void SEQ_timeline_expand_boundbox(const struct ListBase *seqbase, struct rctf *rect); void SEQ_timeline_boundbox(const struct Scene *scene, const struct ListBase *seqbase, struct rctf *rect); @@ -42,8 +44,7 @@ int SEQ_time_find_next_prev_edit(struct Scene *scene, const bool do_skip_mute, const bool do_center, const bool do_unselected); -void SEQ_time_update_sequence(struct Scene *scene, struct Sequence *seq); -void SEQ_time_update_sequence_bounds(struct Scene *scene, struct Sequence *seq); +void SEQ_time_update_sequence(struct Scene *scene, struct ListBase *seqbase, struct Sequence *seq); bool SEQ_time_strip_intersects_frame(const struct Sequence *seq, const int timeline_frame); void SEQ_time_update_meta_strip_range(struct Scene *scene, struct Sequence *seq_meta); diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 1977835f627..18437680731 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -61,6 +61,26 @@ void SEQ_transform_offset_after_frame(struct Scene *scene, const int delta, const int timeline_frame); +/* Image transformation. */ +void SEQ_image_transform_mirror_factor_get(const struct Sequence *seq, float r_mirror[2]); +void SEQ_image_transform_origin_offset_pixelspace_get(const struct Scene *scene, + const struct Sequence *seq, + float r_origin[2]); +void SEQ_image_transform_quad_get(const struct Scene *scene, + const struct Sequence *seq, + bool apply_rotation, + float r_quad[4][2]); +void SEQ_image_transform_final_quad_get(const struct Scene *scene, + const struct Sequence *seq, + float r_quad[4][2]); + +void SEQ_image_preview_unit_to_px(const struct Scene *scene, + const float co_src[2], + float co_dst[2]); +void SEQ_image_preview_unit_from_px(const struct Scene *scene, + const float co_src[2], + float co_dst[2]); + #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/SEQ_utils.h b/source/blender/sequencer/SEQ_utils.h index 09de7bc67e9..d30a1b2d7ae 100644 --- a/source/blender/sequencer/SEQ_utils.h +++ b/source/blender/sequencer/SEQ_utils.h @@ -34,6 +34,7 @@ struct Mask; struct Scene; struct Sequence; struct StripElem; +struct SeqRenderData; void SEQ_sort(struct ListBase *seqbase); void SEQ_sequence_base_unique_name_recursive(struct Scene *scene, @@ -57,7 +58,6 @@ void SEQ_set_scale_to_fit(const struct Sequence *seq, const int preview_height, const eSeqImageFitMethod fit_method); void SEQ_ensure_unique_name(struct Sequence *seq, struct Scene *scene); - #ifdef __cplusplus } #endif diff --git a/source/blender/sequencer/intern/disk_cache.c b/source/blender/sequencer/intern/disk_cache.c new file mode 100644 index 00000000000..06d27717bdd --- /dev/null +++ b/source/blender/sequencer/intern/disk_cache.c @@ -0,0 +1,699 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup sequencer + */ + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "DNA_scene_types.h" +#include "DNA_sequence_types.h" +#include "DNA_space_types.h" /* for FILE_MAX. */ + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BLI_blenlib.h" +#include "BLI_endian_defines.h" +#include "BLI_endian_switch.h" +#include "BLI_fileops.h" +#include "BLI_fileops_types.h" +#include "BLI_ghash.h" +#include "BLI_listbase.h" +#include "BLI_mempool.h" +#include "BLI_path_util.h" +#include "BLI_threads.h" + +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "SEQ_prefetch.h" +#include "SEQ_relations.h" +#include "SEQ_render.h" +#include "SEQ_sequencer.h" + +#include "disk_cache.h" +#include "image_cache.h" +#include "prefetch.h" +#include "strip_time.h" + +/** + * Disk Cache Design Notes + * ======================= + * + * Disk cache uses directory specified in user preferences + * For each cached non-temp image, image data and supplementary info are written to HDD. + * Multiple(DCACHE_IMAGES_PER_FILE) images share the same file. + * Each of these files contains header DiskCacheHeader followed by image data. + * Zlib compression with user definable level can be used to compress image data(per image) + * Images are written in order in which they are rendered. + * Overwriting of individual entry is not possible. + * Stored images are deleted by invalidation, or when size of all files exceeds maximum + * size specified in user preferences. + * To distinguish 2 blend files with same name, scene->ed->disk_cache_timestamp + * is used as UID. Blend file can still be copied manually which may cause conflict. + */ + +/* Format string: + * `-x-%()-.dcf`. */ +#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" +#define DCACHE_IMAGES_PER_FILE 100 +#define DCACHE_CURRENT_VERSION 2 +#define COLORSPACE_NAME_MAX 64 /* XXX: defined in IMB intern. */ + +typedef struct DiskCacheHeaderEntry { + unsigned char encoding; + uint64_t frameno; + uint64_t size_compressed; + uint64_t size_raw; + uint64_t offset; + char colorspace_name[COLORSPACE_NAME_MAX]; +} DiskCacheHeaderEntry; + +typedef struct DiskCacheHeader { + DiskCacheHeaderEntry entry[DCACHE_IMAGES_PER_FILE]; +} DiskCacheHeader; + +typedef struct SeqDiskCache { + Main *bmain; + int64_t timestamp; + ListBase files; + ThreadMutex read_write_mutex; + size_t size_total; +} SeqDiskCache; + +typedef struct DiskCacheFile { + struct DiskCacheFile *next, *prev; + char path[FILE_MAX]; + char dir[FILE_MAXDIR]; + char file[FILE_MAX]; + BLI_stat_t fstat; + int cache_type; + int rectx; + int recty; + int render_size; + int view_id; + int start_frame; +} DiskCacheFile; + +static ThreadMutex cache_create_lock = BLI_MUTEX_INITIALIZER; + +static char *seq_disk_cache_base_dir(void) +{ + return U.sequencer_disk_cache_dir; +} + +static int seq_disk_cache_compression_level(void) +{ + switch (U.sequencer_disk_cache_compression) { + case USER_SEQ_DISK_CACHE_COMPRESSION_NONE: + return 0; + case USER_SEQ_DISK_CACHE_COMPRESSION_LOW: + return 1; + case USER_SEQ_DISK_CACHE_COMPRESSION_HIGH: + return 9; + } + + return U.sequencer_disk_cache_compression; +} + +static size_t seq_disk_cache_size_limit(void) +{ + return (size_t)U.sequencer_disk_cache_size_limit * (1024 * 1024 * 1024); +} + +bool seq_disk_cache_is_enabled(Main *bmain) +{ + return (U.sequencer_disk_cache_dir[0] != '\0' && U.sequencer_disk_cache_size_limit != 0 && + (U.sequencer_disk_cache_flag & SEQ_CACHE_DISK_CACHE_ENABLE) != 0 && + bmain->name[0] != '\0'); +} + +static DiskCacheFile *seq_disk_cache_add_file_to_list(SeqDiskCache *disk_cache, const char *path) +{ + + DiskCacheFile *cache_file = MEM_callocN(sizeof(DiskCacheFile), "SeqDiskCacheFile"); + char dir[FILE_MAXDIR], file[FILE_MAX]; + BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file)); + BLI_strncpy(cache_file->path, path, sizeof(cache_file->path)); + BLI_strncpy(cache_file->dir, dir, sizeof(cache_file->dir)); + BLI_strncpy(cache_file->file, file, sizeof(cache_file->file)); + sscanf(file, + DCACHE_FNAME_FORMAT, + &cache_file->cache_type, + &cache_file->rectx, + &cache_file->recty, + &cache_file->render_size, + &cache_file->view_id, + &cache_file->start_frame); + cache_file->start_frame *= DCACHE_IMAGES_PER_FILE; + BLI_addtail(&disk_cache->files, cache_file); + return cache_file; +} + +static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path) +{ + struct direntry *filelist, *fl; + uint nbr, i; + disk_cache->size_total = 0; + + i = nbr = BLI_filelist_dir_contents(path, &filelist); + fl = filelist; + while (i--) { + /* Don't follow links. */ + const eFileAttributes file_attrs = BLI_file_attributes(fl->path); + if (file_attrs & FILE_ATTR_ANY_LINK) { + fl++; + continue; + } + + char file[FILE_MAX]; + BLI_split_dirfile(fl->path, NULL, file, 0, sizeof(file)); + + bool is_dir = BLI_is_dir(fl->path); + if (is_dir && !FILENAME_IS_CURRPAR(file)) { + char subpath[FILE_MAX]; + BLI_strncpy(subpath, fl->path, sizeof(subpath)); + BLI_path_slash_ensure(subpath); + seq_disk_cache_get_files(disk_cache, subpath); + } + + if (!is_dir) { + const char *ext = BLI_path_extension(fl->path); + if (ext && ext[1] == 'd' && ext[2] == 'c' && ext[3] == 'f') { + DiskCacheFile *cache_file = seq_disk_cache_add_file_to_list(disk_cache, fl->path); + cache_file->fstat = fl->s; + disk_cache->size_total += cache_file->fstat.st_size; + } + } + fl++; + } + BLI_filelist_free(filelist, nbr); +} + +static DiskCacheFile *seq_disk_cache_get_oldest_file(SeqDiskCache *disk_cache) +{ + DiskCacheFile *oldest_file = disk_cache->files.first; + if (oldest_file == NULL) { + return NULL; + } + for (DiskCacheFile *cache_file = oldest_file->next; cache_file; cache_file = cache_file->next) { + if (cache_file->fstat.st_mtime < oldest_file->fstat.st_mtime) { + oldest_file = cache_file; + } + } + + return oldest_file; +} + +static void seq_disk_cache_delete_file(SeqDiskCache *disk_cache, DiskCacheFile *file) +{ + disk_cache->size_total -= file->fstat.st_size; + BLI_delete(file->path, false, false); + BLI_remlink(&disk_cache->files, file); + MEM_freeN(file); +} + +bool seq_disk_cache_enforce_limits(SeqDiskCache *disk_cache) +{ + BLI_mutex_lock(&disk_cache->read_write_mutex); + while (disk_cache->size_total > seq_disk_cache_size_limit()) { + DiskCacheFile *oldest_file = seq_disk_cache_get_oldest_file(disk_cache); + + if (!oldest_file) { + /* We shouldn't enforce limits with no files, do re-scan. */ + seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); + continue; + } + + if (BLI_exists(oldest_file->path) == 0) { + /* File may have been manually deleted during runtime, do re-scan. */ + BLI_freelistN(&disk_cache->files); + seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); + continue; + } + + seq_disk_cache_delete_file(disk_cache, oldest_file); + } + BLI_mutex_unlock(&disk_cache->read_write_mutex); + + return true; +} + +static DiskCacheFile *seq_disk_cache_get_file_entry_by_path(SeqDiskCache *disk_cache, char *path) +{ + DiskCacheFile *cache_file = disk_cache->files.first; + + for (; cache_file; cache_file = cache_file->next) { + if (BLI_strcasecmp(cache_file->path, path) == 0) { + return cache_file; + } + } + + return NULL; +} + +/* Update file size and timestamp. */ +static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, char *path) +{ + DiskCacheFile *cache_file; + int64_t size_before; + int64_t size_after; + + cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); + size_before = cache_file->fstat.st_size; + + if (BLI_stat(path, &cache_file->fstat) == -1) { + BLI_assert(false); + memset(&cache_file->fstat, 0, sizeof(BLI_stat_t)); + } + + size_after = cache_file->fstat.st_size; + disk_cache->size_total += size_after - size_before; +} + +/* Path format: + * /_seq_cache/-//DCACHE_FNAME_FORMAT + */ + +static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, char *path, size_t path_len) +{ + char cache_dir[FILE_MAX]; + BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), cache_dir, sizeof(cache_dir)); + /* Use suffix, so that the cache directory name does not conflict with the bmain's blend file. */ + const char *suffix = "_seq_cache"; + strncat(cache_dir, suffix, sizeof(cache_dir) - strlen(cache_dir) - 1); + BLI_strncpy(path, seq_disk_cache_base_dir(), path_len); + BLI_path_append(path, path_len, cache_dir); +} + +static void seq_disk_cache_get_dir( + SeqDiskCache *disk_cache, Scene *scene, Sequence *seq, char *path, size_t path_len) +{ + char scene_name[MAX_ID_NAME + 22]; /* + -%PRId64 */ + char seq_name[SEQ_NAME_MAXSTR]; + char project_dir[FILE_MAX]; + + seq_disk_cache_get_project_dir(disk_cache, project_dir, sizeof(project_dir)); + sprintf(scene_name, "%s-%" PRId64, scene->id.name, disk_cache->timestamp); + BLI_strncpy(seq_name, seq->name, sizeof(seq_name)); + BLI_filename_make_safe(scene_name); + BLI_filename_make_safe(seq_name); + BLI_strncpy(path, project_dir, path_len); + BLI_path_append(path, path_len, scene_name); + BLI_path_append(path, path_len, seq_name); +} + +static void seq_disk_cache_get_file_path(SeqDiskCache *disk_cache, + SeqCacheKey *key, + char *path, + size_t path_len) +{ + seq_disk_cache_get_dir(disk_cache, key->context.scene, key->seq, path, path_len); + int frameno = (int)key->frame_index / DCACHE_IMAGES_PER_FILE; + char cache_filename[FILE_MAXFILE]; + sprintf(cache_filename, + DCACHE_FNAME_FORMAT, + key->type, + key->context.rectx, + key->context.recty, + key->context.preview_render_size, + key->context.view_id, + frameno); + + BLI_path_append(path, path_len, cache_filename); +} + +static void seq_disk_cache_create_version_file(char *path) +{ + BLI_make_existing_file(path); + + FILE *file = BLI_fopen(path, "w"); + if (file) { + fprintf(file, "%d", DCACHE_CURRENT_VERSION); + fclose(file); + } +} + +static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) +{ + char path[FILE_MAX]; + char path_version_file[FILE_MAX]; + int version = 0; + + seq_disk_cache_get_project_dir(disk_cache, path, sizeof(path)); + BLI_strncpy(path_version_file, path, sizeof(path_version_file)); + BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version"); + + if (BLI_exists(path) && BLI_is_dir(path)) { + FILE *file = BLI_fopen(path_version_file, "r"); + + if (file) { + const int num_items_read = fscanf(file, "%d", &version); + if (num_items_read == 0) { + version = -1; + } + fclose(file); + } + + if (version != DCACHE_CURRENT_VERSION) { + BLI_delete(path, false, true); + seq_disk_cache_create_version_file(path_version_file); + } + } + else { + seq_disk_cache_create_version_file(path_version_file); + } +} + +static void seq_disk_cache_delete_invalid_files(SeqDiskCache *disk_cache, + Scene *scene, + Sequence *seq, + int invalidate_types, + int range_start, + int range_end) +{ + DiskCacheFile *next_file, *cache_file = disk_cache->files.first; + char cache_dir[FILE_MAX]; + seq_disk_cache_get_dir(disk_cache, scene, seq, cache_dir, sizeof(cache_dir)); + BLI_path_slash_ensure(cache_dir); + + while (cache_file) { + next_file = cache_file->next; + if (cache_file->cache_type & invalidate_types) { + if (STREQ(cache_dir, cache_file->dir)) { + int timeline_frame_start = seq_cache_frame_index_to_timeline_frame( + seq, cache_file->start_frame); + if (timeline_frame_start > range_start && timeline_frame_start <= range_end) { + seq_disk_cache_delete_file(disk_cache, cache_file); + } + } + } + cache_file = next_file; + } +} + +void seq_disk_cache_invalidate(SeqDiskCache *disk_cache, + Scene *scene, + Sequence *seq, + Sequence *seq_changed, + int invalidate_types) +{ + int start; + int end; + + BLI_mutex_lock(&disk_cache->read_write_mutex); + + start = seq_changed->startdisp - DCACHE_IMAGES_PER_FILE; + end = seq_changed->enddisp; + + seq_disk_cache_delete_invalid_files(disk_cache, scene, seq, invalidate_types, start, end); + + BLI_mutex_unlock(&disk_cache->read_write_mutex); +} + +static size_t deflate_imbuf_to_file(ImBuf *ibuf, + FILE *file, + int level, + DiskCacheHeaderEntry *header_entry) +{ + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + + /* Apply compression if wanted, otherwise just write directly to the file. */ + if (level > 0) { + return BLI_file_zstd_from_mem_at_pos( + data, header_entry->size_raw, file, header_entry->offset, level); + } + + fseek(file, header_entry->offset, SEEK_SET); + return fwrite(data, 1, header_entry->size_raw, file); +} + +static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) +{ + void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; + char header[4]; + fseek(file, header_entry->offset, SEEK_SET); + if (fread(header, 1, sizeof(header), file) != sizeof(header)) { + return 0; + } + + /* Check if the data is compressed or raw. */ + if (BLI_file_magic_is_zstd(header)) { + return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); + } + + fseek(file, header_entry->offset, SEEK_SET); + return fread(data, 1, header_entry->size_raw, file); +} + +static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) +{ + BLI_fseek(file, 0LL, SEEK_SET); + const size_t num_items_read = fread(header, sizeof(*header), 1, file); + if (num_items_read < 1) { + BLI_assert_msg(0, "unable to read disk cache header"); + perror("unable to read disk cache header"); + return false; + } + + for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { + if ((ENDIAN_ORDER == B_ENDIAN) && header->entry[i].encoding == 0) { + BLI_endian_switch_uint64(&header->entry[i].frameno); + BLI_endian_switch_uint64(&header->entry[i].offset); + BLI_endian_switch_uint64(&header->entry[i].size_compressed); + BLI_endian_switch_uint64(&header->entry[i].size_raw); + } + } + + return true; +} + +static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) +{ + BLI_fseek(file, 0LL, SEEK_SET); + return fwrite(header, sizeof(*header), 1, file); +} + +static int seq_disk_cache_add_header_entry(SeqCacheKey *key, ImBuf *ibuf, DiskCacheHeader *header) +{ + int i; + uint64_t offset = sizeof(*header); + + /* Lookup free entry, get offset for new data. */ + for (i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { + if (header->entry[i].size_compressed == 0) { + break; + } + } + + /* Attempt to write beyond set entry limit. + * Reset file header and start writing from beginning. + */ + if (i == DCACHE_IMAGES_PER_FILE) { + i = 0; + memset(header, 0, sizeof(*header)); + } + + /* Calculate offset for image data. */ + if (i > 0) { + offset = header->entry[i - 1].offset + header->entry[i - 1].size_compressed; + } + + if (ENDIAN_ORDER == B_ENDIAN) { + header->entry[i].encoding = 255; + } + else { + header->entry[i].encoding = 0; + } + + header->entry[i].offset = offset; + header->entry[i].frameno = key->frame_index; + + /* Store colorspace name of ibuf. */ + const char *colorspace_name; + if (ibuf->rect) { + header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels; + colorspace_name = IMB_colormanagement_get_rect_colorspace(ibuf); + } + else { + header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels * 4; + colorspace_name = IMB_colormanagement_get_float_colorspace(ibuf); + } + BLI_strncpy( + header->entry[i].colorspace_name, colorspace_name, sizeof(header->entry[i].colorspace_name)); + + return i; +} + +static int seq_disk_cache_get_header_entry(SeqCacheKey *key, DiskCacheHeader *header) +{ + for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { + if (header->entry[i].frameno == key->frame_index) { + return i; + } + } + + return -1; +} + +bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf *ibuf) +{ + BLI_mutex_lock(&disk_cache->read_write_mutex); + + char path[FILE_MAX]; + + seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); + BLI_make_existing_file(path); + + FILE *file = BLI_fopen(path, "rb+"); + if (!file) { + file = BLI_fopen(path, "wb+"); + if (!file) { + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return false; + } + seq_disk_cache_add_file_to_list(disk_cache, path); + } + + DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); + DiskCacheHeader header; + memset(&header, 0, sizeof(header)); + /* #BLI_make_existing_file() above may create an empty file. This is fine, don't attempt reading + * the header in that case. */ + if (cache_file->fstat.st_size != 0 && !seq_disk_cache_read_header(file, &header)) { + fclose(file); + seq_disk_cache_delete_file(disk_cache, cache_file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return false; + } + int entry_index = seq_disk_cache_add_header_entry(key, ibuf, &header); + + size_t bytes_written = deflate_imbuf_to_file( + ibuf, file, seq_disk_cache_compression_level(), &header.entry[entry_index]); + + if (bytes_written != 0) { + /* Last step is writing header, as image data can be overwritten, + * but missing data would cause problems. + */ + header.entry[entry_index].size_compressed = bytes_written; + seq_disk_cache_write_header(file, &header); + seq_disk_cache_update_file(disk_cache, path); + fclose(file); + + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return true; + } + + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return false; +} + +ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) +{ + BLI_mutex_lock(&disk_cache->read_write_mutex); + + char path[FILE_MAX]; + DiskCacheHeader header; + + seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); + BLI_make_existing_file(path); + + FILE *file = BLI_fopen(path, "rb"); + if (!file) { + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + + if (!seq_disk_cache_read_header(file, &header)) { + fclose(file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + int entry_index = seq_disk_cache_get_header_entry(key, &header); + + /* Item not found. */ + if (entry_index < 0) { + fclose(file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + + ImBuf *ibuf; + uint64_t size_char = (uint64_t)key->context.rectx * key->context.recty * 4; + uint64_t size_float = (uint64_t)key->context.rectx * key->context.recty * 16; + size_t expected_size; + + if (header.entry[entry_index].size_raw == size_char) { + expected_size = size_char; + ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rect); + IMB_colormanagement_assign_rect_colorspace(ibuf, header.entry[entry_index].colorspace_name); + } + else if (header.entry[entry_index].size_raw == size_float) { + expected_size = size_float; + ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rectfloat); + IMB_colormanagement_assign_float_colorspace(ibuf, header.entry[entry_index].colorspace_name); + } + else { + fclose(file); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + + size_t bytes_read = inflate_file_to_imbuf(ibuf, file, &header.entry[entry_index]); + + /* Sanity check. */ + if (bytes_read != expected_size) { + fclose(file); + IMB_freeImBuf(ibuf); + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return NULL; + } + BLI_file_touch(path); + seq_disk_cache_update_file(disk_cache, path); + fclose(file); + + BLI_mutex_unlock(&disk_cache->read_write_mutex); + return ibuf; +} + +SeqDiskCache *seq_disk_cache_create(Main *bmain, Scene *scene) +{ + SeqDiskCache *disk_cache = MEM_callocN(sizeof(SeqDiskCache), "SeqDiskCache"); + disk_cache->bmain = bmain; + BLI_mutex_init(&disk_cache->read_write_mutex); + seq_disk_cache_handle_versioning(disk_cache); + seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); + disk_cache->timestamp = scene->ed->disk_cache_timestamp; + BLI_mutex_unlock(&cache_create_lock); + return disk_cache; +} + +void seq_disk_cache_free(SeqDiskCache *disk_cache) +{ + BLI_freelistN(&disk_cache->files); + BLI_mutex_end(&disk_cache->read_write_mutex); + MEM_freeN(disk_cache); +} diff --git a/source/blender/sequencer/intern/disk_cache.h b/source/blender/sequencer/intern/disk_cache.h new file mode 100644 index 00000000000..a84bfaf5ea0 --- /dev/null +++ b/source/blender/sequencer/intern/disk_cache.h @@ -0,0 +1,56 @@ +/* + * 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) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup sequencer + */ + +#pragma once + +/** \file + * \ingroup sequencer + */ + +#ifdef __cplusplus +extern "C" { +#endif + +struct ImBuf; +struct Main; +struct Scene; +struct SeqCacheKey; +struct SeqDiskCache; +struct Sequence; + +struct SeqDiskCache *seq_disk_cache_create(struct Main *bmain, struct Scene *scene); +void seq_disk_cache_free(struct SeqDiskCache *disk_cache); +bool seq_disk_cache_is_enabled(struct Main *bmain); +struct ImBuf *seq_disk_cache_read_file(struct SeqDiskCache *disk_cache, struct SeqCacheKey *key); +bool seq_disk_cache_write_file(struct SeqDiskCache *disk_cache, + struct SeqCacheKey *key, + struct ImBuf *ibuf); +bool seq_disk_cache_enforce_limits(struct SeqDiskCache *disk_cache); +void seq_disk_cache_invalidate(struct SeqDiskCache *disk_cache, + struct Scene *scene, + struct Sequence *seq, + struct Sequence *seq_changed, + int invalidate_types); +#ifdef __cplusplus +} +#endif diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index 4448db013fe..b9ee23a9186 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -2451,7 +2451,6 @@ static void do_transform_effect(const SeqRenderData *context, int total_lines, ImBuf *out) { - Scene *scene = context->scene; TransformVars *transform = (TransformVars *)seq->effectdata; float scale_x, scale_y, translate_x, translate_y, rotate_radians; @@ -2469,10 +2468,14 @@ static void do_transform_effect(const SeqRenderData *context, /* Translate */ if (!transform->percent) { - float rd_s = (scene->r.size / 100.0f); + /* Compensate text size for preview render size. */ + double proxy_size_comp = context->scene->r.size / 100.0; + if (context->preview_render_size != SEQ_RENDER_SIZE_SCENE) { + proxy_size_comp = SEQ_rendersize_to_scale_factor(context->preview_render_size); + } - translate_x = transform->xIni * rd_s + (x / 2.0f); - translate_y = transform->yIni * rd_s + (y / 2.0f); + translate_x = transform->xIni * proxy_size_comp + (x / 2.0f); + translate_y = transform->yIni * proxy_size_comp + (y / 2.0f); } else { translate_x = x * (transform->xIni / 100.0f) + (x / 2.0f); @@ -3154,6 +3157,7 @@ void seq_effect_speed_rebuild_map(Scene *scene, Sequence *seq) float target_frame = 0; for (int frame_index = 1; frame_index < effect_strip_length; frame_index++) { target_frame += evaluate_fcurve(fcu, seq->startdisp + frame_index); + CLAMP(target_frame, 0, seq->seq1->len); v->frameMap[frame_index] = target_frame; } } diff --git a/source/blender/sequencer/intern/image_cache.c b/source/blender/sequencer/intern/image_cache.c index 86bd840ce31..c742fca0562 100644 --- a/source/blender/sequencer/intern/image_cache.c +++ b/source/blender/sequencer/intern/image_cache.c @@ -50,9 +50,9 @@ #include "SEQ_prefetch.h" #include "SEQ_relations.h" -#include "SEQ_render.h" #include "SEQ_sequencer.h" +#include "disk_cache.h" #include "image_cache.h" #include "prefetch.h" #include "strip_time.h" @@ -80,65 +80,9 @@ * entries one by one in reverse order to their creation. * * User can exclude caching of some images. Such entries will have is_temp_cache set. - * - * - * Disk Cache Design Notes - * ======================= - * - * Disk cache uses directory specified in user preferences - * For each cached non-temp image, image data and supplementary info are written to HDD. - * Multiple(DCACHE_IMAGES_PER_FILE) images share the same file. - * Each of these files contains header DiskCacheHeader followed by image data. - * Zlib compression with user definable level can be used to compress image data(per image) - * Images are written in order in which they are rendered. - * Overwriting of individual entry is not possible. - * Stored images are deleted by invalidation, or when size of all files exceeds maximum - * size specified in user preferences. - * To distinguish 2 blend files with same name, scene->ed->disk_cache_timestamp - * is used as UID. Blend file can still be copied manually which may cause conflict. - * */ -/* -x-%()-.dcf */ -#define DCACHE_FNAME_FORMAT "%d-%dx%d-%d%%(%d)-%d.dcf" -#define DCACHE_IMAGES_PER_FILE 100 -#define DCACHE_CURRENT_VERSION 2 -#define COLORSPACE_NAME_MAX 64 /* XXX: defined in imb intern */ - -typedef struct DiskCacheHeaderEntry { - unsigned char encoding; - uint64_t frameno; - uint64_t size_compressed; - uint64_t size_raw; - uint64_t offset; - char colorspace_name[COLORSPACE_NAME_MAX]; -} DiskCacheHeaderEntry; - -typedef struct DiskCacheHeader { - DiskCacheHeaderEntry entry[DCACHE_IMAGES_PER_FILE]; -} DiskCacheHeader; - -typedef struct SeqDiskCache { - Main *bmain; - int64_t timestamp; - ListBase files; - ThreadMutex read_write_mutex; - size_t size_total; -} SeqDiskCache; - -typedef struct DiskCacheFile { - struct DiskCacheFile *next, *prev; - char path[FILE_MAX]; - char dir[FILE_MAXDIR]; - char file[FILE_MAX]; - BLI_stat_t fstat; - int cache_type; - int rectx; - int recty; - int render_size; - int view_id; - int start_frame; -} DiskCacheFile; +#define THUMB_CACHE_LIMIT 5000 typedef struct SeqCache { Main *bmain; @@ -147,7 +91,8 @@ typedef struct SeqCache { struct BLI_mempool *keys_pool; struct BLI_mempool *items_pool; struct SeqCacheKey *last_key; - SeqDiskCache *disk_cache; + struct SeqDiskCache *disk_cache; + int thumbnail_count; } SeqCache; typedef struct SeqCacheItem { @@ -155,577 +100,7 @@ typedef struct SeqCacheItem { struct ImBuf *ibuf; } SeqCacheItem; -typedef struct SeqCacheKey { - struct SeqCache *cache_owner; - void *userkey; - struct SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame. */ - struct SeqCacheKey *link_next; /* Used for linking intermediate items to final frame. */ - struct Sequence *seq; - SeqRenderData context; - float frame_index; /* Usually same as timeline_frame. Mapped to media for RAW entries. */ - float timeline_frame; /* Only for reference - used for freeing when cache is full. */ - float cost; /* In short: render time(s) divided by playback frame duration(s) */ - bool is_temp_cache; /* this cache entry will be freed before rendering next frame */ - /* ID of task for assigning temp cache entries to particular task(thread, etc.) */ - eSeqTaskId task_id; - int type; -} SeqCacheKey; - static ThreadMutex cache_create_lock = BLI_MUTEX_INITIALIZER; -static float seq_cache_timeline_frame_to_frame_index(Sequence *seq, - float timeline_frame, - int type); -static float seq_cache_frame_index_to_timeline_frame(Sequence *seq, float frame_index); - -static char *seq_disk_cache_base_dir(void) -{ - return U.sequencer_disk_cache_dir; -} - -static int seq_disk_cache_compression_level(void) -{ - switch (U.sequencer_disk_cache_compression) { - case USER_SEQ_DISK_CACHE_COMPRESSION_NONE: - return 0; - case USER_SEQ_DISK_CACHE_COMPRESSION_LOW: - return 1; - case USER_SEQ_DISK_CACHE_COMPRESSION_HIGH: - return 9; - } - - return U.sequencer_disk_cache_compression; -} - -static size_t seq_disk_cache_size_limit(void) -{ - return (size_t)U.sequencer_disk_cache_size_limit * (1024 * 1024 * 1024); -} - -static bool seq_disk_cache_is_enabled(Main *bmain) -{ - return (U.sequencer_disk_cache_dir[0] != '\0' && U.sequencer_disk_cache_size_limit != 0 && - (U.sequencer_disk_cache_flag & SEQ_CACHE_DISK_CACHE_ENABLE) != 0 && - bmain->name[0] != '\0'); -} - -static DiskCacheFile *seq_disk_cache_add_file_to_list(SeqDiskCache *disk_cache, const char *path) -{ - - DiskCacheFile *cache_file = MEM_callocN(sizeof(DiskCacheFile), "SeqDiskCacheFile"); - char dir[FILE_MAXDIR], file[FILE_MAX]; - BLI_split_dirfile(path, dir, file, sizeof(dir), sizeof(file)); - BLI_strncpy(cache_file->path, path, sizeof(cache_file->path)); - BLI_strncpy(cache_file->dir, dir, sizeof(cache_file->dir)); - BLI_strncpy(cache_file->file, file, sizeof(cache_file->file)); - sscanf(file, - DCACHE_FNAME_FORMAT, - &cache_file->cache_type, - &cache_file->rectx, - &cache_file->recty, - &cache_file->render_size, - &cache_file->view_id, - &cache_file->start_frame); - cache_file->start_frame *= DCACHE_IMAGES_PER_FILE; - BLI_addtail(&disk_cache->files, cache_file); - return cache_file; -} - -static void seq_disk_cache_get_files(SeqDiskCache *disk_cache, char *path) -{ - struct direntry *filelist, *fl; - uint nbr, i; - disk_cache->size_total = 0; - - i = nbr = BLI_filelist_dir_contents(path, &filelist); - fl = filelist; - while (i--) { - /* Don't follow links. */ - const eFileAttributes file_attrs = BLI_file_attributes(fl->path); - if (file_attrs & FILE_ATTR_ANY_LINK) { - fl++; - continue; - } - - char file[FILE_MAX]; - BLI_split_dirfile(fl->path, NULL, file, 0, sizeof(file)); - - bool is_dir = BLI_is_dir(fl->path); - if (is_dir && !FILENAME_IS_CURRPAR(file)) { - char subpath[FILE_MAX]; - BLI_strncpy(subpath, fl->path, sizeof(subpath)); - BLI_path_slash_ensure(subpath); - seq_disk_cache_get_files(disk_cache, subpath); - } - - if (!is_dir) { - const char *ext = BLI_path_extension(fl->path); - if (ext && ext[1] == 'd' && ext[2] == 'c' && ext[3] == 'f') { - DiskCacheFile *cache_file = seq_disk_cache_add_file_to_list(disk_cache, fl->path); - cache_file->fstat = fl->s; - disk_cache->size_total += cache_file->fstat.st_size; - } - } - fl++; - } - BLI_filelist_free(filelist, nbr); -} - -static DiskCacheFile *seq_disk_cache_get_oldest_file(SeqDiskCache *disk_cache) -{ - DiskCacheFile *oldest_file = disk_cache->files.first; - if (oldest_file == NULL) { - return NULL; - } - for (DiskCacheFile *cache_file = oldest_file->next; cache_file; cache_file = cache_file->next) { - if (cache_file->fstat.st_mtime < oldest_file->fstat.st_mtime) { - oldest_file = cache_file; - } - } - - return oldest_file; -} - -static void seq_disk_cache_delete_file(SeqDiskCache *disk_cache, DiskCacheFile *file) -{ - disk_cache->size_total -= file->fstat.st_size; - BLI_delete(file->path, false, false); - BLI_remlink(&disk_cache->files, file); - MEM_freeN(file); -} - -static bool seq_disk_cache_enforce_limits(SeqDiskCache *disk_cache) -{ - BLI_mutex_lock(&disk_cache->read_write_mutex); - while (disk_cache->size_total > seq_disk_cache_size_limit()) { - DiskCacheFile *oldest_file = seq_disk_cache_get_oldest_file(disk_cache); - - if (!oldest_file) { - /* We shouldn't enforce limits with no files, do re-scan. */ - seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); - continue; - } - - if (BLI_exists(oldest_file->path) == 0) { - /* File may have been manually deleted during runtime, do re-scan. */ - BLI_freelistN(&disk_cache->files); - seq_disk_cache_get_files(disk_cache, seq_disk_cache_base_dir()); - continue; - } - - seq_disk_cache_delete_file(disk_cache, oldest_file); - } - BLI_mutex_unlock(&disk_cache->read_write_mutex); - - return true; -} - -static DiskCacheFile *seq_disk_cache_get_file_entry_by_path(SeqDiskCache *disk_cache, char *path) -{ - DiskCacheFile *cache_file = disk_cache->files.first; - - for (; cache_file; cache_file = cache_file->next) { - if (BLI_strcasecmp(cache_file->path, path) == 0) { - return cache_file; - } - } - - return NULL; -} - -/* Update file size and timestamp. */ -static void seq_disk_cache_update_file(SeqDiskCache *disk_cache, char *path) -{ - DiskCacheFile *cache_file; - int64_t size_before; - int64_t size_after; - - cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); - size_before = cache_file->fstat.st_size; - - if (BLI_stat(path, &cache_file->fstat) == -1) { - BLI_assert(false); - memset(&cache_file->fstat, 0, sizeof(BLI_stat_t)); - } - - size_after = cache_file->fstat.st_size; - disk_cache->size_total += size_after - size_before; -} - -/* Path format: - * /_seq_cache/-//DCACHE_FNAME_FORMAT - */ - -static void seq_disk_cache_get_project_dir(SeqDiskCache *disk_cache, char *path, size_t path_len) -{ - char cache_dir[FILE_MAX]; - BLI_split_file_part(BKE_main_blendfile_path(disk_cache->bmain), cache_dir, sizeof(cache_dir)); - /* Use suffix, so that the cache directory name does not conflict with the bmain's blend file. */ - const char *suffix = "_seq_cache"; - strncat(cache_dir, suffix, sizeof(cache_dir) - strlen(cache_dir) - 1); - BLI_strncpy(path, seq_disk_cache_base_dir(), path_len); - BLI_path_append(path, path_len, cache_dir); -} - -static void seq_disk_cache_get_dir( - SeqDiskCache *disk_cache, Scene *scene, Sequence *seq, char *path, size_t path_len) -{ - char scene_name[MAX_ID_NAME + 22]; /* + -%PRId64 */ - char seq_name[SEQ_NAME_MAXSTR]; - char project_dir[FILE_MAX]; - - seq_disk_cache_get_project_dir(disk_cache, project_dir, sizeof(project_dir)); - sprintf(scene_name, "%s-%" PRId64, scene->id.name, disk_cache->timestamp); - BLI_strncpy(seq_name, seq->name, sizeof(seq_name)); - BLI_filename_make_safe(scene_name); - BLI_filename_make_safe(seq_name); - BLI_strncpy(path, project_dir, path_len); - BLI_path_append(path, path_len, scene_name); - BLI_path_append(path, path_len, seq_name); -} - -static void seq_disk_cache_get_file_path(SeqDiskCache *disk_cache, - SeqCacheKey *key, - char *path, - size_t path_len) -{ - seq_disk_cache_get_dir(disk_cache, key->context.scene, key->seq, path, path_len); - int frameno = (int)key->frame_index / DCACHE_IMAGES_PER_FILE; - char cache_filename[FILE_MAXFILE]; - sprintf(cache_filename, - DCACHE_FNAME_FORMAT, - key->type, - key->context.rectx, - key->context.recty, - key->context.preview_render_size, - key->context.view_id, - frameno); - - BLI_path_append(path, path_len, cache_filename); -} - -static void seq_disk_cache_create_version_file(char *path) -{ - BLI_make_existing_file(path); - - FILE *file = BLI_fopen(path, "w"); - if (file) { - fprintf(file, "%d", DCACHE_CURRENT_VERSION); - fclose(file); - } -} - -static void seq_disk_cache_handle_versioning(SeqDiskCache *disk_cache) -{ - char path[FILE_MAX]; - char path_version_file[FILE_MAX]; - int version = 0; - - seq_disk_cache_get_project_dir(disk_cache, path, sizeof(path)); - BLI_strncpy(path_version_file, path, sizeof(path_version_file)); - BLI_path_append(path_version_file, sizeof(path_version_file), "cache_version"); - - if (BLI_exists(path) && BLI_is_dir(path)) { - FILE *file = BLI_fopen(path_version_file, "r"); - - if (file) { - const int num_items_read = fscanf(file, "%d", &version); - if (num_items_read == 0) { - version = -1; - } - fclose(file); - } - - if (version != DCACHE_CURRENT_VERSION) { - BLI_delete(path, false, true); - seq_disk_cache_create_version_file(path_version_file); - } - } - else { - seq_disk_cache_create_version_file(path_version_file); - } -} - -static void seq_disk_cache_delete_invalid_files(SeqDiskCache *disk_cache, - Scene *scene, - Sequence *seq, - int invalidate_types, - int range_start, - int range_end) -{ - DiskCacheFile *next_file, *cache_file = disk_cache->files.first; - char cache_dir[FILE_MAX]; - seq_disk_cache_get_dir(disk_cache, scene, seq, cache_dir, sizeof(cache_dir)); - BLI_path_slash_ensure(cache_dir); - - while (cache_file) { - next_file = cache_file->next; - if (cache_file->cache_type & invalidate_types) { - if (STREQ(cache_dir, cache_file->dir)) { - int timeline_frame_start = seq_cache_frame_index_to_timeline_frame( - seq, cache_file->start_frame); - if (timeline_frame_start > range_start && timeline_frame_start <= range_end) { - seq_disk_cache_delete_file(disk_cache, cache_file); - } - } - } - cache_file = next_file; - } -} - -static void seq_disk_cache_invalidate(Scene *scene, - Sequence *seq, - Sequence *seq_changed, - int invalidate_types) -{ - int start; - int end; - SeqDiskCache *disk_cache = scene->ed->cache->disk_cache; - - BLI_mutex_lock(&disk_cache->read_write_mutex); - - start = seq_changed->startdisp - DCACHE_IMAGES_PER_FILE; - end = seq_changed->enddisp; - - seq_disk_cache_delete_invalid_files(disk_cache, scene, seq, invalidate_types, start, end); - - BLI_mutex_unlock(&disk_cache->read_write_mutex); -} - -static size_t deflate_imbuf_to_file(ImBuf *ibuf, - FILE *file, - int level, - DiskCacheHeaderEntry *header_entry) -{ - void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; - - /* Apply compression if wanted, otherwise just write directly to the file. */ - if (level > 0) { - return BLI_file_zstd_from_mem_at_pos( - data, header_entry->size_raw, file, header_entry->offset, level); - } - - fseek(file, header_entry->offset, SEEK_SET); - return fwrite(data, 1, header_entry->size_raw, file); -} - -static size_t inflate_file_to_imbuf(ImBuf *ibuf, FILE *file, DiskCacheHeaderEntry *header_entry) -{ - void *data = (ibuf->rect != NULL) ? (void *)ibuf->rect : (void *)ibuf->rect_float; - char header[4]; - fseek(file, header_entry->offset, SEEK_SET); - if (fread(header, 1, sizeof(header), file) != sizeof(header)) { - return 0; - } - - /* Check if the data is compressed or raw. */ - if (BLI_file_magic_is_zstd(header)) { - return BLI_file_unzstd_to_mem_at_pos(data, header_entry->size_raw, file, header_entry->offset); - } - - fseek(file, header_entry->offset, SEEK_SET); - return fread(data, 1, header_entry->size_raw, file); -} - -static bool seq_disk_cache_read_header(FILE *file, DiskCacheHeader *header) -{ - BLI_fseek(file, 0LL, SEEK_SET); - const size_t num_items_read = fread(header, sizeof(*header), 1, file); - if (num_items_read < 1) { - BLI_assert_msg(0, "unable to read disk cache header"); - perror("unable to read disk cache header"); - return false; - } - - for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if ((ENDIAN_ORDER == B_ENDIAN) && header->entry[i].encoding == 0) { - BLI_endian_switch_uint64(&header->entry[i].frameno); - BLI_endian_switch_uint64(&header->entry[i].offset); - BLI_endian_switch_uint64(&header->entry[i].size_compressed); - BLI_endian_switch_uint64(&header->entry[i].size_raw); - } - } - - return true; -} - -static size_t seq_disk_cache_write_header(FILE *file, DiskCacheHeader *header) -{ - BLI_fseek(file, 0LL, SEEK_SET); - return fwrite(header, sizeof(*header), 1, file); -} - -static int seq_disk_cache_add_header_entry(SeqCacheKey *key, ImBuf *ibuf, DiskCacheHeader *header) -{ - int i; - uint64_t offset = sizeof(*header); - - /* Lookup free entry, get offset for new data. */ - for (i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if (header->entry[i].size_compressed == 0) { - break; - } - } - - /* Attempt to write beyond set entry limit. - * Reset file header and start writing from beginning. - */ - if (i == DCACHE_IMAGES_PER_FILE) { - i = 0; - memset(header, 0, sizeof(*header)); - } - - /* Calculate offset for image data. */ - if (i > 0) { - offset = header->entry[i - 1].offset + header->entry[i - 1].size_compressed; - } - - if (ENDIAN_ORDER == B_ENDIAN) { - header->entry[i].encoding = 255; - } - else { - header->entry[i].encoding = 0; - } - - header->entry[i].offset = offset; - header->entry[i].frameno = key->frame_index; - - /* Store colorspace name of ibuf. */ - const char *colorspace_name; - if (ibuf->rect) { - header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels; - colorspace_name = IMB_colormanagement_get_rect_colorspace(ibuf); - } - else { - header->entry[i].size_raw = ibuf->x * ibuf->y * ibuf->channels * 4; - colorspace_name = IMB_colormanagement_get_float_colorspace(ibuf); - } - BLI_strncpy( - header->entry[i].colorspace_name, colorspace_name, sizeof(header->entry[i].colorspace_name)); - - return i; -} - -static int seq_disk_cache_get_header_entry(SeqCacheKey *key, DiskCacheHeader *header) -{ - for (int i = 0; i < DCACHE_IMAGES_PER_FILE; i++) { - if (header->entry[i].frameno == key->frame_index) { - return i; - } - } - - return -1; -} - -static bool seq_disk_cache_write_file(SeqDiskCache *disk_cache, SeqCacheKey *key, ImBuf *ibuf) -{ - char path[FILE_MAX]; - - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); - - FILE *file = BLI_fopen(path, "rb+"); - if (!file) { - file = BLI_fopen(path, "wb+"); - if (!file) { - return false; - } - seq_disk_cache_add_file_to_list(disk_cache, path); - } - - DiskCacheFile *cache_file = seq_disk_cache_get_file_entry_by_path(disk_cache, path); - DiskCacheHeader header; - memset(&header, 0, sizeof(header)); - /* #BLI_make_existing_file() above may create an empty file. This is fine, don't attempt reading - * the header in that case. */ - if (cache_file->fstat.st_size != 0 && !seq_disk_cache_read_header(file, &header)) { - fclose(file); - seq_disk_cache_delete_file(disk_cache, cache_file); - return false; - } - int entry_index = seq_disk_cache_add_header_entry(key, ibuf, &header); - - size_t bytes_written = deflate_imbuf_to_file( - ibuf, file, seq_disk_cache_compression_level(), &header.entry[entry_index]); - - if (bytes_written != 0) { - /* Last step is writing header, as image data can be overwritten, - * but missing data would cause problems. - */ - header.entry[entry_index].size_compressed = bytes_written; - seq_disk_cache_write_header(file, &header); - seq_disk_cache_update_file(disk_cache, path); - fclose(file); - - return true; - } - - return false; -} - -static ImBuf *seq_disk_cache_read_file(SeqDiskCache *disk_cache, SeqCacheKey *key) -{ - char path[FILE_MAX]; - DiskCacheHeader header; - - seq_disk_cache_get_file_path(disk_cache, key, path, sizeof(path)); - BLI_make_existing_file(path); - - FILE *file = BLI_fopen(path, "rb"); - if (!file) { - return NULL; - } - - if (!seq_disk_cache_read_header(file, &header)) { - fclose(file); - return NULL; - } - int entry_index = seq_disk_cache_get_header_entry(key, &header); - - /* Item not found. */ - if (entry_index < 0) { - fclose(file); - return NULL; - } - - ImBuf *ibuf; - uint64_t size_char = (uint64_t)key->context.rectx * key->context.recty * 4; - uint64_t size_float = (uint64_t)key->context.rectx * key->context.recty * 16; - size_t expected_size; - - if (header.entry[entry_index].size_raw == size_char) { - expected_size = size_char; - ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rect); - IMB_colormanagement_assign_rect_colorspace(ibuf, header.entry[entry_index].colorspace_name); - } - else if (header.entry[entry_index].size_raw == size_float) { - expected_size = size_float; - ibuf = IMB_allocImBuf(key->context.rectx, key->context.recty, 32, IB_rectfloat); - IMB_colormanagement_assign_float_colorspace(ibuf, header.entry[entry_index].colorspace_name); - } - else { - fclose(file); - return NULL; - } - - size_t bytes_read = inflate_file_to_imbuf(ibuf, file, &header.entry[entry_index]); - - /* Sanity check. */ - if (bytes_read != expected_size) { - fclose(file); - IMB_freeImBuf(ibuf); - return NULL; - } - BLI_file_touch(path); - seq_disk_cache_update_file(disk_cache, path); - fclose(file); - - return ibuf; -} - -#undef DCACHE_FNAME_FORMAT -#undef DCACHE_IMAGES_PER_FILE -#undef COLORSPACE_NAME_MAX -#undef DCACHE_CURRENT_VERSION static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b) { @@ -776,14 +151,14 @@ static float seq_cache_timeline_frame_to_frame_index(Sequence *seq, float timeli /* With raw images, map timeline_frame to strip input media frame range. This means that static * images or extended frame range of movies will only generate one cache entry. No special * treatment in converting frame index to timeline_frame is needed. */ - if (type == SEQ_CACHE_STORE_RAW) { + if (ELEM(type, SEQ_CACHE_STORE_RAW, SEQ_CACHE_STORE_THUMBNAIL)) { return seq_give_frame_index(seq, timeline_frame); } return timeline_frame - seq->start; } -static float seq_cache_frame_index_to_timeline_frame(Sequence *seq, float frame_index) +float seq_cache_frame_index_to_timeline_frame(Sequence *seq, float frame_index) { return frame_index + seq->start; } @@ -875,7 +250,7 @@ static void seq_cache_put_ex(Scene *scene, SeqCacheKey *key, ImBuf *ibuf) if (BLI_ghash_reinsert(cache->hash, key, item, seq_cache_keyfree, seq_cache_valfree)) { IMB_refImBuf(ibuf); - if (!key->is_temp_cache) { + if (!key->is_temp_cache || key->type != SEQ_CACHE_STORE_THUMBNAIL) { cache->last_key = key; } } @@ -1129,28 +504,6 @@ static void seq_cache_set_temp_cache_linked(Scene *scene, SeqCacheKey *base) } } -static void seq_disk_cache_create(Main *bmain, Scene *scene) -{ - BLI_mutex_lock(&cache_create_lock); - SeqCache *cache = seq_cache_get_from_scene(scene); - - if (cache == NULL) { - return; - } - - if (cache->disk_cache != NULL) { - return; - } - - cache->disk_cache = MEM_callocN(sizeof(SeqDiskCache), "SeqDiskCache"); - cache->disk_cache->bmain = bmain; - BLI_mutex_init(&cache->disk_cache->read_write_mutex); - seq_disk_cache_handle_versioning(cache->disk_cache); - seq_disk_cache_get_files(cache->disk_cache, seq_disk_cache_base_dir()); - cache->disk_cache->timestamp = scene->ed->disk_cache_timestamp; - BLI_mutex_unlock(&cache_create_lock); -} - static void seq_cache_create(Main *bmain, Scene *scene) { BLI_mutex_lock(&cache_create_lock); @@ -1161,6 +514,7 @@ static void seq_cache_create(Main *bmain, Scene *scene) cache->hash = BLI_ghash_new(seq_cache_hashhash, seq_cache_hashcmp, "SeqCache hash"); cache->last_key = NULL; cache->bmain = bmain; + cache->thumbnail_count = 0; BLI_mutex_init(&cache->iterator_mutex); scene->ed->cache = cache; @@ -1217,7 +571,7 @@ void seq_cache_free_temp_cache(Scene *scene, short id, int timeline_frame) SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); BLI_ghashIterator_step(&gh_iter); - if (key->is_temp_cache && key->task_id == id) { + if (key->is_temp_cache && key->task_id == id && key->type != SEQ_CACHE_STORE_THUMBNAIL) { /* Use frame_index here to avoid freeing raw images if they are used for multiple frames. */ float frame_index = seq_cache_timeline_frame_to_frame_index( key->seq, timeline_frame, key->type); @@ -1243,9 +597,7 @@ void seq_cache_destruct(Scene *scene) BLI_mutex_end(&cache->iterator_mutex); if (cache->disk_cache != NULL) { - BLI_freelistN(&cache->disk_cache->files); - BLI_mutex_end(&cache->disk_cache->read_write_mutex); - MEM_freeN(cache->disk_cache); + seq_disk_cache_free(cache->disk_cache); } MEM_freeN(cache); @@ -1278,6 +630,7 @@ void SEQ_cache_cleanup(Scene *scene) BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); } cache->last_key = NULL; + cache->thumbnail_count = 0; seq_cache_unlock(scene); } @@ -1293,7 +646,7 @@ void seq_cache_cleanup_sequence(Scene *scene, } if (seq_disk_cache_is_enabled(cache->bmain) && cache->disk_cache != NULL) { - seq_disk_cache_invalidate(scene, seq, seq_changed, invalidate_types); + seq_disk_cache_invalidate(cache->disk_cache, scene, seq, seq_changed, invalidate_types); } seq_cache_lock(scene); @@ -1345,6 +698,46 @@ void seq_cache_cleanup_sequence(Scene *scene, seq_cache_unlock(scene); } +void seq_cache_thumbnail_cleanup(Scene *scene, rctf *view_area_safe) +{ + /* Add offsets to the left and right end to keep some frames in cache. */ + view_area_safe->xmax += 200; + view_area_safe->xmin -= 200; + view_area_safe->ymin -= 1; + view_area_safe->ymax += 1; + + SeqCache *cache = seq_cache_get_from_scene(scene); + if (!cache) { + return; + } + + GHashIterator gh_iter; + BLI_ghashIterator_init(&gh_iter, cache->hash); + while (!BLI_ghashIterator_done(&gh_iter)) { + SeqCacheKey *key = BLI_ghashIterator_getKey(&gh_iter); + BLI_ghashIterator_step(&gh_iter); + + const int frame_index = key->timeline_frame - key->seq->startdisp; + const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(key->seq); + const int relative_base_frame = round_fl_to_int((frame_index / (float)frame_step)) * + frame_step; + const int nearest_guaranted_absolute_frame = relative_base_frame + key->seq->startdisp; + + if (nearest_guaranted_absolute_frame == key->timeline_frame) { + continue; + } + + if ((key->type & SEQ_CACHE_STORE_THUMBNAIL) && + (key->timeline_frame > view_area_safe->xmax || + key->timeline_frame < view_area_safe->xmin || key->seq->machine > view_area_safe->ymax || + key->seq->machine < view_area_safe->ymin)) { + BLI_ghash_remove(cache->hash, key, seq_cache_keyfree, seq_cache_valfree); + cache->thumbnail_count--; + } + } + cache->last_key = NULL; +} + struct ImBuf *seq_cache_get(const SeqRenderData *context, Sequence *seq, float timeline_frame, @@ -1390,12 +783,10 @@ struct ImBuf *seq_cache_get(const SeqRenderData *context, /* Try disk cache: */ if (seq_disk_cache_is_enabled(context->bmain)) { if (cache->disk_cache == NULL) { - seq_disk_cache_create(context->bmain, context->scene); + cache->disk_cache = seq_disk_cache_create(context->bmain, context->scene); } - BLI_mutex_lock(&cache->disk_cache->read_write_mutex); ibuf = seq_disk_cache_read_file(cache->disk_cache, &key); - BLI_mutex_unlock(&cache->disk_cache->read_write_mutex); if (ibuf == NULL) { return NULL; @@ -1436,6 +827,37 @@ bool seq_cache_put_if_possible( return false; } +void seq_cache_thumbnail_put( + const SeqRenderData *context, Sequence *seq, float timeline_frame, ImBuf *i, rctf *view_area) +{ + Scene *scene = context->scene; + + if (!scene->ed->cache) { + seq_cache_create(context->bmain, scene); + } + + seq_cache_lock(scene); + SeqCache *cache = seq_cache_get_from_scene(scene); + SeqCacheKey *key = seq_cache_allocate_key( + cache, context, seq, timeline_frame, SEQ_CACHE_STORE_THUMBNAIL); + + /* Prevent reinserting, it breaks cache key linking. */ + if (BLI_ghash_haskey(cache->hash, key)) { + seq_cache_unlock(scene); + return; + } + + /* Limit cache to THUMB_CACHE_LIMIT (5000) images stored. */ + if (cache->thumbnail_count >= THUMB_CACHE_LIMIT) { + rctf view_area_safe = *view_area; + seq_cache_thumbnail_cleanup(scene, &view_area_safe); + } + + seq_cache_put_ex(scene, key, i); + cache->thumbnail_count++; + seq_cache_unlock(scene); +} + void seq_cache_put( const SeqRenderData *context, Sequence *seq, float timeline_frame, int type, ImBuf *i) { @@ -1475,9 +897,7 @@ void seq_cache_put( seq_disk_cache_create(context->bmain, context->scene); } - BLI_mutex_lock(&cache->disk_cache->read_write_mutex); seq_disk_cache_write_file(cache->disk_cache, key, i); - BLI_mutex_unlock(&cache->disk_cache->read_write_mutex); seq_disk_cache_enforce_limits(cache->disk_cache); } } diff --git a/source/blender/sequencer/intern/image_cache.h b/source/blender/sequencer/intern/image_cache.h index 63c559caee9..e7827c15305 100644 --- a/source/blender/sequencer/intern/image_cache.h +++ b/source/blender/sequencer/intern/image_cache.h @@ -27,15 +27,29 @@ extern "C" { #endif +#include "SEQ_render.h" /* Needed for #eSeqTaskId. */ + struct ImBuf; struct Main; struct Scene; struct SeqRenderData; struct Sequence; -#ifdef __cplusplus -} -#endif +typedef struct SeqCacheKey { + struct SeqCache *cache_owner; + void *userkey; + struct SeqCacheKey *link_prev; /* Used for linking intermediate items to final frame. */ + struct SeqCacheKey *link_next; /* Used for linking intermediate items to final frame. */ + struct Sequence *seq; + struct SeqRenderData context; + float frame_index; /* Usually same as timeline_frame. Mapped to media for RAW entries. */ + float timeline_frame; /* Only for reference - used for freeing when cache is full. */ + float cost; /* In short: render time(s) divided by playback frame duration(s) */ + bool is_temp_cache; /* this cache entry will be freed before rendering next frame */ + /* ID of task for assigning temp cache entries to particular task(thread, etc.) */ + eSeqTaskId task_id; + int type; +} SeqCacheKey; struct ImBuf *seq_cache_get(const struct SeqRenderData *context, struct Sequence *seq, @@ -46,6 +60,11 @@ void seq_cache_put(const struct SeqRenderData *context, float timeline_frame, int type, struct ImBuf *i); +void seq_cache_thumbnail_put(const struct SeqRenderData *context, + struct Sequence *seq, + float timeline_frame, + struct ImBuf *i, + rctf *view_area); bool seq_cache_put_if_possible(const struct SeqRenderData *context, struct Sequence *seq, float timeline_frame, @@ -60,7 +79,9 @@ void seq_cache_cleanup_sequence(struct Scene *scene, struct Sequence *seq_changed, int invalidate_types, bool force_seq_changed_range); +void seq_cache_thumbnail_cleanup(Scene *scene, rctf *view_area); bool seq_cache_is_full(void); +float seq_cache_frame_index_to_timeline_frame(struct Sequence *seq, float frame_index); #ifdef __cplusplus } diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index 58f68205f51..68f632ddb28 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -37,6 +37,8 @@ #include "BKE_scene.h" #include "SEQ_iterator.h" +#include "SEQ_time.h" +#include "render.h" /* -------------------------------------------------------------------- */ /** \Iterator API @@ -187,10 +189,12 @@ SeqCollection *SEQ_query_by_reference(Sequence *seq_reference, */ bool SEQ_collection_append_strip(Sequence *seq, SeqCollection *collection) { - if (BLI_gset_lookup(collection->set, seq) != NULL) { + void **key; + if (BLI_gset_ensure_p_ex(collection->set, seq, &key)) { return false; } - BLI_gset_insert(collection->set, seq); + + *key = (void *)seq; return true; } @@ -318,7 +322,9 @@ SeqCollection *SEQ_query_all_strips_recursive(ListBase *seqbase) SeqCollection *SEQ_query_all_strips(ListBase *seqbase) { SeqCollection *collection = SEQ_collection_create(__func__); - query_all_strips_recursive(seqbase, collection); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + SEQ_collection_append_strip(seq, collection); + } return collection; } @@ -340,6 +346,114 @@ SeqCollection *SEQ_query_selected_strips(ListBase *seqbase) return collection; } +static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame) +{ + SeqCollection *collection = SEQ_collection_create(__func__); + + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { + SEQ_collection_append_strip(seq, collection); + } + } + return collection; +} + +static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->machine <= channel) { + continue; + } + SEQ_collection_remove_strip(seq, collection); + } +} + +static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input) +{ + if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input || + seq_effect->seq3 == possibly_input) { + return true; + } + return false; +} + +/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. + * Order of applying these conditions is important. */ +static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) +{ + bool seq_have_effect_in_stack = false; + Sequence *seq_iter; + SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) { + /* Strips is below another strip with replace blending are not rendered. */ + if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) { + return false; + } + + if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) { + /* Strips in same channel or higher than its effect are rendered. */ + if (seq->machine >= seq_iter->machine) { + return true; + } + /* Mark that this strip has effect in stack, that is above the strip. */ + seq_have_effect_in_stack = true; + } + } + + /* All effects are rendered (with respect to conditions above). */ + if ((seq->type & SEQ_TYPE_EFFECT) != 0) { + return true; + } + + /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */ + if (seq_have_effect_in_stack) { + return false; + } + + return true; +} + +/* Remove strips we don't want to render from collection. */ +static void collection_filter_rendered_strips(SeqCollection *collection) +{ + Sequence *seq; + + /* Remove sound strips and muted strips from collection, because these are not rendered. + * Function #must_render_strip() don't have to check for these strips anymore. */ + SEQ_ITERATOR_FOREACH (seq, collection) { + if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { + SEQ_collection_remove_strip(seq, collection); + } + } + + SEQ_ITERATOR_FOREACH (seq, collection) { + if (must_render_strip(seq, collection)) { + continue; + } + SEQ_collection_remove_strip(seq, collection); + } +} + +/** + * Query strips that are rendered at \a timeline_frame when \a displayed channel is viewed + * + * \param seqbase: ListBase in which strips are queried + * \param timeline_frame: viewed frame + * \param displayed_channel: viewed channel. when set to 0, no channel filter is applied + * \return strip collection + */ +SeqCollection *SEQ_query_rendered_strips(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel) +{ + SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame); + if (displayed_channel != 0) { + collection_filter_channel_up_to_incl(collection, displayed_channel); + } + collection_filter_rendered_strips(collection); + return collection; +} + /** * Query all unselected strips in seqbase. * @@ -396,3 +510,39 @@ void SEQ_query_strip_effect_chain(Sequence *seq_reference, } } } + +void SEQ_filter_selected_strips(SeqCollection *collection) +{ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + if ((seq->flag & SELECT) == 0) { + SEQ_collection_remove_strip(seq, collection); + } + } +} + +static void seq_collection_to_tag(ListBase *seqbase, SeqCollection *collection) +{ + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + seq->tmp_tag = false; + } + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, collection) { + seq->tmp_tag = true; + } +} + +/* Utilities to access these as tags. */ +int SEQ_query_rendered_strips_to_tag(ListBase *seqbase, + const int timeline_frame, + const int displayed_channel) +{ + SeqCollection *collection = SEQ_query_rendered_strips( + seqbase, timeline_frame, displayed_channel); + + seq_collection_to_tag(seqbase, collection); + + const int len = SEQ_collection_len(collection); + SEQ_collection_free(collection); + return len; +} diff --git a/source/blender/sequencer/intern/modifier.c b/source/blender/sequencer/intern/modifier.c index 07d09f4ae17..1a63f4c4655 100644 --- a/source/blender/sequencer/intern/modifier.c +++ b/source/blender/sequencer/intern/modifier.c @@ -216,7 +216,7 @@ static void modifier_apply_threaded(ImBuf *ibuf, /** \name Color Balance Modifier * \{ */ -static StripColorBalance calc_cb(StripColorBalance *cb_) +static StripColorBalance calc_cb_lgg(StripColorBalance *cb_) { StripColorBalance cb = *cb_; int c; @@ -262,8 +262,52 @@ static StripColorBalance calc_cb(StripColorBalance *cb_) return cb; } +static StripColorBalance calc_cb_sop(StripColorBalance *cb_) +{ + StripColorBalance cb = *cb_; + int c; + + for (c = 0; c < 3; c++) { + if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_SLOPE) { + if (cb.slope[c] != 0.0f) { + cb.slope[c] = 1.0f / cb.slope[c]; + } + else { + cb.slope[c] = 1000000; + } + } + + if (cb.flag & SEQ_COLOR_BALANCE_INVERSE_OFFSET) { + cb.offset[c] = -1.0f * (cb.offset[c] - 1.0f); + } + else { + cb.offset[c] = cb.offset[c] - 1.0f; + } + + if (!(cb.flag & SEQ_COLOR_BALANCE_INVERSE_POWER)) { + if (cb.power[c] != 0.0f) { + cb.power[c] = 1.0f / cb.power[c]; + } + else { + cb.power[c] = 1000000; + } + } + } + + return cb; +} + +static StripColorBalance calc_cb(StripColorBalance *cb_) +{ + if (cb_->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + return calc_cb_lgg(cb_); + } + /* `cb_->method == SEQ_COLOR_BALANCE_METHOD_SLOPEOFFSETPOWER`. */ + return calc_cb_sop(cb_); +} + /* NOTE: lift is actually 2-lift. */ -MINLINE float color_balance_fl( +MINLINE float color_balance_fl_lgg( float in, const float lift, const float gain, const float gamma, const float mul) { float x = (((in - 1.0f) * lift) + 1.0f) * gain; @@ -278,12 +322,40 @@ MINLINE float color_balance_fl( return x; } -static void make_cb_table_float(float lift, float gain, float gamma, float *table, float mul) +MINLINE float color_balance_fl_sop(float in, + const float slope, + const float offset, + const float power, + const float pivot, + float mul) { - int y; + float x = in * slope + offset; - for (y = 0; y < 256; y++) { - float v = color_balance_fl((float)y * (1.0f / 255.0f), lift, gain, gamma, mul); + /* prevent NaN */ + if (x < 0.0f) { + x = 0.0f; + } + + x = powf(x / pivot, power) * pivot; + x *= mul; + CLAMP(x, FLT_MIN, FLT_MAX); + return x; +} + +static void make_cb_table_float_lgg(float lift, float gain, float gamma, float *table, float mul) +{ + for (int y = 0; y < 256; y++) { + float v = color_balance_fl_lgg((float)y * (1.0f / 255.0f), lift, gain, gamma, mul); + + table[y] = v; + } +} + +static void make_cb_table_float_sop( + float slope, float offset, float power, float pivot, float *table, float mul) +{ + for (int y = 0; y < 256; y++) { + float v = color_balance_fl_sop((float)y * (1.0f / 255.0f), slope, offset, power, pivot, mul); table[y] = v; } @@ -310,7 +382,13 @@ static void color_balance_byte_byte(StripColorBalance *cb_, straight_uchar_to_premul_float(p, cp); for (c = 0; c < 3; c++) { - float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + float t; + if (cb.method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + t = color_balance_fl_lgg(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + } + else { + t = color_balance_fl_sop(p[c], cb.slope[c], cb.offset[c], cb.power[c], 1.0, mul); + } if (m) { float m_normal = (float)m[c] / 255.0f; @@ -352,7 +430,12 @@ static void color_balance_byte_float(StripColorBalance *cb_, cb = calc_cb(cb_); for (c = 0; c < 3; c++) { - make_cb_table_float(cb.lift[c], cb.gain[c], cb.gamma[c], cb_tab[c], mul); + if (cb.method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + make_cb_table_float_lgg(cb.lift[c], cb.gain[c], cb.gamma[c], cb_tab[c], mul); + } + else { + make_cb_table_float_sop(cb.slope[c], cb.offset[c], cb.power[c], 1.0, cb_tab[c], mul); + } } for (i = 0; i < 256; i++) { @@ -397,7 +480,13 @@ static void color_balance_float_float(StripColorBalance *cb_, while (p < e) { int c; for (c = 0; c < 3; c++) { - float t = color_balance_fl(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + float t; + if (cb_->method == SEQ_COLOR_BALANCE_METHOD_LIFTGAMMAGAIN) { + t = color_balance_fl_lgg(p[c], cb.lift[c], cb.gain[c], cb.gamma[c], mul); + } + else { + t = color_balance_fl_sop(p[c], cb.slope[c], cb.offset[c], cb.power[c], 1.0, mul); + } if (m) { p[c] = p[c] * (1.0f - m[c]) + t * m[c]; @@ -507,11 +596,15 @@ static void colorBalance_init_data(SequenceModifierData *smd) int c; cbmd->color_multiply = 1.0f; + cbmd->color_balance.method = 0; for (c = 0; c < 3; c++) { cbmd->color_balance.lift[c] = 1.0f; cbmd->color_balance.gamma[c] = 1.0f; cbmd->color_balance.gain[c] = 1.0f; + cbmd->color_balance.slope[c] = 1.0f; + cbmd->color_balance.offset[c] = 1.0f; + cbmd->color_balance.power[c] = 1.0f; } } diff --git a/source/blender/sequencer/intern/render.c b/source/blender/sequencer/intern/render.c index 6c4502a3608..4405253586b 100644 --- a/source/blender/sequencer/intern/render.c +++ b/source/blender/sequencer/intern/render.c @@ -69,9 +69,11 @@ #include "SEQ_iterator.h" #include "SEQ_modifier.h" #include "SEQ_proxy.h" +#include "SEQ_relations.h" #include "SEQ_render.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" +#include "SEQ_transform.h" #include "SEQ_utils.h" #include "effects.h" @@ -262,94 +264,6 @@ StripElem *SEQ_render_give_stripelem(Sequence *seq, int timeline_frame) return se; } -static bool seq_is_effect_of(const Sequence *seq_effect, const Sequence *possibly_input) -{ - if (seq_effect->seq1 == possibly_input || seq_effect->seq2 == possibly_input || - seq_effect->seq3 == possibly_input) { - return true; - } - return false; -} - -/* Check if seq must be rendered. This depends on whole stack in some cases, not only seq itself. - * Order of applying these conditions is important. */ -static bool must_render_strip(const Sequence *seq, SeqCollection *strips_at_timeline_frame) -{ - bool seq_have_effect_in_stack = false; - Sequence *seq_iter; - SEQ_ITERATOR_FOREACH (seq_iter, strips_at_timeline_frame) { - /* Strips is below another strip with replace blending are not rendered. */ - if (seq_iter->blend_mode == SEQ_BLEND_REPLACE && seq->machine < seq_iter->machine) { - return false; - } - - if ((seq_iter->type & SEQ_TYPE_EFFECT) != 0 && seq_is_effect_of(seq_iter, seq)) { - /* Strips in same channel or higher than its effect are rendered. */ - if (seq->machine >= seq_iter->machine) { - return true; - } - /* Mark that this strip has effect in stack, that is above the strip. */ - seq_have_effect_in_stack = true; - } - } - - /* All effects are rendered (with respect to conditions above). */ - if ((seq->type & SEQ_TYPE_EFFECT) != 0) { - return true; - } - - /* If strip has effects in stack, and all effects are above this strip, it is not rendered. */ - if (seq_have_effect_in_stack) { - return false; - } - - return true; -} - -static SeqCollection *query_strips_at_frame(ListBase *seqbase, const int timeline_frame) -{ - SeqCollection *collection = SEQ_collection_create(__func__); - - LISTBASE_FOREACH (Sequence *, seq, seqbase) { - if (SEQ_time_strip_intersects_frame(seq, timeline_frame)) { - SEQ_collection_append_strip(seq, collection); - } - } - return collection; -} - -static void collection_filter_channel_up_to_incl(SeqCollection *collection, const int channel) -{ - Sequence *seq; - SEQ_ITERATOR_FOREACH (seq, collection) { - if (seq->machine <= channel) { - continue; - } - SEQ_collection_remove_strip(seq, collection); - } -} - -/* Remove strips we don't want to render from collection. */ -static void collection_filter_rendered_strips(SeqCollection *collection) -{ - Sequence *seq; - - /* Remove sound strips and muted strips from collection, because these are not rendered. - * Function #must_render_strip() don't have to check for these strips anymore. */ - SEQ_ITERATOR_FOREACH (seq, collection) { - if (seq->type == SEQ_TYPE_SOUND_RAM || (seq->flag & SEQ_MUTE) != 0) { - SEQ_collection_remove_strip(seq, collection); - } - } - - SEQ_ITERATOR_FOREACH (seq, collection) { - if (must_render_strip(seq, collection)) { - continue; - } - SEQ_collection_remove_strip(seq, collection); - } -} - static int seq_channel_cmp_fn(const void *a, const void *b) { return (*(Sequence **)a)->machine - (*(Sequence **)b)->machine; @@ -360,13 +274,7 @@ int seq_get_shown_sequences(ListBase *seqbase, const int chanshown, Sequence **r_seq_arr) { - SeqCollection *collection = query_strips_at_frame(seqbase, timeline_frame); - - if (chanshown != 0) { - collection_filter_channel_up_to_incl(collection, chanshown); - } - collection_filter_rendered_strips(collection); - + SeqCollection *collection = SEQ_query_rendered_strips(seqbase, timeline_frame, chanshown); const int strip_count = BLI_gset_len(collection->set); if (strip_count > MAXSEQ) { @@ -495,7 +403,7 @@ static void sequencer_image_crop_transform_matrix(const Sequence *seq, const ImBuf *out, const float image_scale_factor, const float preview_scale_factor, - float r_transform_matrix[3][3]) + float r_transform_matrix[4][4]) { const StripTransform *transform = seq->strip->transform; const float scale_x = transform->scale_x * image_scale_factor; @@ -504,13 +412,16 @@ static void sequencer_image_crop_transform_matrix(const Sequence *seq, const float image_center_offs_y = (out->y - in->y) / 2; const float translate_x = transform->xofs * preview_scale_factor + image_center_offs_x; const float translate_y = transform->yofs * preview_scale_factor + image_center_offs_y; - const float pivot[2] = {in->x / 2, in->y / 2}; - loc_rot_size_to_mat3(r_transform_matrix, - (const float[]){translate_x, translate_y}, - transform->rotation, - (const float[]){scale_x, scale_y}); - transform_pivot_set_m3(r_transform_matrix, pivot); - invert_m3(r_transform_matrix); + const float pivot[3] = {in->x * transform->origin[0], in->y * transform->origin[1], 0.0f}; + + float rotation_matrix[3][3]; + axis_angle_to_mat3_single(rotation_matrix, 'Z', transform->rotation); + loc_rot_size_to_mat4(r_transform_matrix, + (const float[]){translate_x, translate_y, 0.0f}, + rotation_matrix, + (const float[]){scale_x, scale_y, 1.0f}); + transform_pivot_set_m4(r_transform_matrix, pivot); + invert_m4(r_transform_matrix); } static void sequencer_image_crop_init(const Sequence *seq, @@ -527,6 +438,34 @@ static void sequencer_image_crop_init(const Sequence *seq, BLI_rctf_init(r_crop, left, in->x - right, bottom, in->y - top); } +static void sequencer_thumbnail_transform(ImBuf *in, ImBuf *out) +{ + float image_scale_factor = (float)out->x / in->x; + float transform_matrix[4][4]; + + /* Set to keep same loc,scale,rot but change scale to thumb size limit. */ + const float scale_x = 1 * image_scale_factor; + const float scale_y = 1 * image_scale_factor; + const float image_center_offs_x = (out->x - in->x) / 2; + const float image_center_offs_y = (out->y - in->y) / 2; + const float pivot[3] = {in->x / 2, in->y / 2, 0.0f}; + + float rotation_matrix[3][3]; + unit_m3(rotation_matrix); + loc_rot_size_to_mat4(transform_matrix, + (const float[]){image_center_offs_x, image_center_offs_y, 0.0f}, + rotation_matrix, + (const float[]){scale_x, scale_y, 1.0f}); + transform_pivot_set_m4(transform_matrix, pivot); + invert_m4(transform_matrix); + + /* No crop. */ + rctf source_crop; + BLI_rctf_init(&source_crop, 0, in->x, 0, in->y); + + IMB_transform(in, out, transform_matrix, &source_crop, IMB_FILTER_NEAREST); +} + static void sequencer_preprocess_transform_crop( ImBuf *in, ImBuf *out, const SeqRenderData *context, Sequence *seq, const bool is_proxy_image) { @@ -538,7 +477,7 @@ static void sequencer_preprocess_transform_crop( const bool do_scale_to_render_size = seq_need_scale_to_render_size(seq, is_proxy_image); const float image_scale_factor = do_scale_to_render_size ? 1.0f : preview_scale_factor; - float transform_matrix[3][3]; + float transform_matrix[4][4]; sequencer_image_crop_transform_matrix( seq, in, out, image_scale_factor, preview_scale_factor, transform_matrix); @@ -1950,6 +1889,8 @@ ImBuf *SEQ_render_give_ibuf(const SeqRenderData *context, float timeline_frame, } seq_cache_free_temp_cache(context->scene, context->task_id, timeline_frame); + /* Make sure we only keep the `anim` data for strips that are in view. */ + SEQ_relations_free_all_anim_ibufs(context->scene, timeline_frame); if (count && !out) { BLI_mutex_lock(&seq_render_mutex); @@ -1989,7 +1930,167 @@ ImBuf *SEQ_render_give_ibuf_direct(const SeqRenderData *context, seq_render_state_init(&state); ImBuf *ibuf = seq_render_strip(context, &state, seq, timeline_frame); - return ibuf; } + +/* Gets the direct image from source and scales to thumbnail size. */ +static ImBuf *seq_get_uncached_thumbnail(const SeqRenderData *context, + SeqRenderState *state, + Sequence *seq, + float timeline_frame) +{ + bool is_proxy_image = false; + ImBuf *ibuf = do_render_strip_uncached(context, state, seq, timeline_frame, &is_proxy_image); + + if (ibuf == NULL) { + return NULL; + } + + float aspect_ratio = (float)ibuf->x / ibuf->y; + int rectx, recty; + /* Calculate new dimensions - THUMB_SIZE (256) for x or y. */ + if (ibuf->x > ibuf->y) { + rectx = SEQ_RENDER_THUMB_SIZE; + recty = round_fl_to_int(rectx / aspect_ratio); + } + else { + recty = SEQ_RENDER_THUMB_SIZE; + rectx = round_fl_to_int(recty * aspect_ratio); + } + + /* Scale ibuf to thumbnail size. */ + ImBuf *scaled_ibuf = IMB_allocImBuf(rectx, recty, 32, ibuf->rect_float ? IB_rectfloat : IB_rect); + sequencer_thumbnail_transform(ibuf, scaled_ibuf); + seq_imbuf_assign_spaces(context->scene, scaled_ibuf); + IMB_freeImBuf(ibuf); + + return scaled_ibuf; +} + +/* Get cached thumbnails. */ +ImBuf *SEQ_get_thumbnail( + const SeqRenderData *context, Sequence *seq, float timeline_frame, rcti *crop, bool clipped) +{ + ImBuf *ibuf = seq_cache_get(context, seq, roundf(timeline_frame), SEQ_CACHE_STORE_THUMBNAIL); + + if (!clipped || ibuf == NULL) { + return ibuf; + } + + /* Do clipping. */ + ImBuf *ibuf_cropped = IMB_dupImBuf(ibuf); + if (crop->xmin < 0 || crop->ymin < 0) { + crop->xmin = 0; + crop->ymin = 0; + } + if (crop->xmax >= ibuf->x || crop->ymax >= ibuf->y) { + crop->xmax = ibuf->x - 1; + crop->ymax = ibuf->y - 1; + } + IMB_rect_crop(ibuf_cropped, crop); + IMB_freeImBuf(ibuf); + return ibuf_cropped; +} + +/* Render the series of thumbnails and store in cache. */ +void SEQ_render_thumbnails(const SeqRenderData *context, + Sequence *seq, + Sequence *seq_orig, + float start_frame, + float frame_step, + rctf *view_area, + const short *stop) +{ + SeqRenderState state; + seq_render_state_init(&state); + + /* Adding the hold offset value (seq->anim_startofs) to the start frame. Position of image not + * affected, but frame loaded affected. */ + start_frame = start_frame - frame_step; + float upper_thumb_bound = (seq->endstill) ? (seq->start + seq->len) : seq->enddisp; + upper_thumb_bound = (upper_thumb_bound > view_area->xmax) ? view_area->xmax + frame_step : + upper_thumb_bound; + + while ((start_frame < upper_thumb_bound) & !*stop) { + ImBuf *ibuf = seq_cache_get( + context, seq_orig, round_fl_to_int(start_frame), SEQ_CACHE_STORE_THUMBNAIL); + if (ibuf) { + IMB_freeImBuf(ibuf); + start_frame += frame_step; + continue; + } + + ibuf = seq_get_uncached_thumbnail(context, &state, seq, round_fl_to_int(start_frame)); + + if (ibuf) { + seq_cache_thumbnail_put(context, seq_orig, round_fl_to_int(start_frame), ibuf, view_area); + IMB_freeImBuf(ibuf); + seq_orig->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS; + } + else { + /* Can not open source file. */ + seq_orig->flag |= SEQ_FLAG_SKIP_THUMBNAILS; + return; + } + + start_frame += frame_step; + } +} + +/* Get frame step for equally spaced thumbnails. These thumbnails should always be present in + * memory, so they can be used when zooming.*/ +int SEQ_render_thumbnails_guaranteed_set_frame_step_get(const Sequence *seq) +{ + const int content_len = (seq->enddisp - seq->startdisp - seq->startstill - seq->endstill); + + /* Arbitrary, but due to performance reasons should be as low as possible. */ + const int thumbnails_base_set_count = min_ii(content_len / 100, 30); + if (thumbnails_base_set_count <= 0) { + return 0; + } + return content_len / thumbnails_base_set_count; +} + +/* Render set of evenly spaced thumbnails that are drawn when zooming. */ +void SEQ_render_thumbnails_base_set(const SeqRenderData *context, + Sequence *seq, + Sequence *seq_orig, + rctf *view_area, + const short *stop) +{ + SeqRenderState state; + seq_render_state_init(&state); + + int timeline_frame = seq->startdisp; + const int frame_step = SEQ_render_thumbnails_guaranteed_set_frame_step_get(seq); + + while (timeline_frame < seq->enddisp && !*stop) { + ImBuf *ibuf = seq_cache_get( + context, seq_orig, roundf(timeline_frame), SEQ_CACHE_STORE_THUMBNAIL); + if (ibuf) { + IMB_freeImBuf(ibuf); + + if (frame_step == 0) { + return; + } + + timeline_frame += frame_step; + continue; + } + + ibuf = seq_get_uncached_thumbnail(context, &state, seq, timeline_frame); + + if (ibuf) { + seq_cache_thumbnail_put(context, seq_orig, timeline_frame, ibuf, view_area); + IMB_freeImBuf(ibuf); + } + + if (frame_step == 0) { + return; + } + + timeline_frame += frame_step; + } +} + /** \} */ diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index bf5942090c9..3478c2d4f97 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -79,6 +79,8 @@ static Strip *seq_strip_alloc(int type) strip->transform = MEM_callocN(sizeof(struct StripTransform), "StripTransform"); strip->transform->scale_x = 1; strip->transform->scale_y = 1; + strip->transform->origin[0] = 0.5f; + strip->transform->origin[1] = 0.5f; strip->crop = MEM_callocN(sizeof(struct StripCrop), "StripCrop"); } @@ -142,6 +144,8 @@ Sequence *SEQ_sequence_alloc(ListBase *lb, int timeline_frame, int machine, int seq->strip = seq_strip_alloc(type); seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); + seq->color_tag = SEQUENCE_COLOR_NONE; + SEQ_relations_session_uuid_generate(seq); return seq; @@ -321,6 +325,7 @@ SequencerToolSettings *SEQ_tool_settings_init(void) SEQ_SNAP_TO_STRIP_HOLD; tool_settings->snap_distance = 15; tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE; + tool_settings->pivot_point = V3D_AROUND_LOCAL_ORIGINS; return tool_settings; } @@ -377,6 +382,12 @@ eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene) return tool_settings->overlap_mode; } +int SEQ_tool_settings_pivot_point_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); + return tool_settings->pivot_point; +} + /** * Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase * @@ -627,6 +638,18 @@ void SEQ_sequence_base_dupli_recursive(const Scene *scene_src, seq_new_fix_links_recursive(seq); } } + +bool SEQ_valid_strip_channel(Sequence *seq) +{ + if (seq->machine < 1) { + return false; + } + if (seq->machine > MAXSEQ) { + return false; + } + return true; +} + /* r_prefix + [" + escaped_name + "] + \0 */ #define SEQ_RNAPATH_MAXSTR ((30 + 2 + (SEQ_NAME_MAXSTR * 2) + 2) + 1) @@ -948,6 +971,8 @@ static bool seq_read_lib_cb(Sequence *seq, void *user_data) BLI_listbase_clear(&seq->anims); SEQ_modifier_blend_read_lib(reader, sce, &seq->modifiers); + + seq->flag &= ~SEQ_FLAG_SKIP_THUMBNAILS; return true; } diff --git a/source/blender/sequencer/intern/sound.c b/source/blender/sequencer/intern/sound.c index c53aacddcfe..86a37aca4a9 100644 --- a/source/blender/sequencer/intern/sound.c +++ b/source/blender/sequencer/intern/sound.c @@ -51,7 +51,7 @@ static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, for (seq = seqbase->first; seq; seq = seq->next) { if (seq->type == SEQ_TYPE_META) { if (sequencer_refresh_sound_length_recursive(bmain, scene, &seq->seqbase)) { - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); changed = true; } } @@ -67,7 +67,7 @@ static bool sequencer_refresh_sound_length_recursive(Main *bmain, Scene *scene, seq->endofs *= fac; seq->start += (old - seq->startofs); /* So that visual/"real" start frame does not change! */ - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); changed = true; } } @@ -111,12 +111,8 @@ void SEQ_sound_update_bounds(Scene *scene, Sequence *seq) /* We have to take into account start frame of the sequence's scene! */ int startofs = seq->startofs + seq->anim_startofs + seq->scene->r.sfra; - BKE_sound_move_scene_sound(scene, - seq->scene_sound, - seq->startdisp, - seq->enddisp, - startofs, - seq->sound->offset_time); + BKE_sound_move_scene_sound( + scene, seq->scene_sound, seq->startdisp, seq->enddisp, startofs, 0.0); } } else { diff --git a/source/blender/sequencer/intern/strip_add.c b/source/blender/sequencer/intern/strip_add.c index 9081c655d2f..6f635b5db5f 100644 --- a/source/blender/sequencer/intern/strip_add.c +++ b/source/blender/sequencer/intern/strip_add.c @@ -100,7 +100,7 @@ void SEQ_add_load_data_init(SeqLoadData *load_data, static void seq_add_generic_update(Scene *scene, ListBase *seqbase, Sequence *seq) { SEQ_sequence_base_unique_name_recursive(scene, &scene->ed->seqbase, seq); - SEQ_time_update_sequence_bounds(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); SEQ_sort(seqbase); SEQ_relations_invalidate_cache_composite(scene, seq); } @@ -403,26 +403,8 @@ Sequence *SEQ_add_sound_strip(Main *bmain, return NULL; } - /* If this sound it part of a video, then the sound might start after the video. - * In this case we need to then offset the start frame of the audio so it syncs up - * properly with the video. - */ - int start_frame_offset = info.start_offset * FPS; - double start_frame_offset_remainer = (info.start_offset * FPS - start_frame_offset) / FPS; - - if (start_frame_offset_remainer > FLT_EPSILON) { - /* We can't represent a fraction of a frame, so skip the first frame fraction of sound so we - * start on a "whole" frame. - */ - start_frame_offset++; - } - - sound->offset_time += start_frame_offset_remainer; - - Sequence *seq = SEQ_sequence_alloc(seqbase, - load_data->start_frame + start_frame_offset, - load_data->channel, - SEQ_TYPE_SOUND_RAM); + Sequence *seq = SEQ_sequence_alloc( + seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_SOUND_RAM); seq->sound = sound; seq->scene_sound = NULL; @@ -490,7 +472,7 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ /* Set frames start and length. */ seqm->start = load_data->start_frame; seqm->len = 1; - SEQ_time_update_sequence(scene, seqm); + SEQ_time_update_sequence(scene, seqbase, seqm); return seqm; } @@ -504,11 +486,8 @@ Sequence *SEQ_add_meta_strip(Scene *scene, ListBase *seqbase, SeqLoadData *load_ * \param load_data: SeqLoadData with information necessary to create strip * \return created strip */ -Sequence *SEQ_add_movie_strip(Main *bmain, - Scene *scene, - ListBase *seqbase, - SeqLoadData *load_data, - double *r_video_start_offset) +Sequence *SEQ_add_movie_strip( + Main *bmain, Scene *scene, ListBase *seqbase, SeqLoadData *load_data, double *r_start_offset) { char path[sizeof(load_data->path)]; BLI_strncpy(path, load_data->path, sizeof(path)); @@ -554,8 +533,40 @@ Sequence *SEQ_add_movie_strip(Main *bmain, return NULL; } + int video_frame_offset = 0; + float video_fps = 0.0f; + + if (anim_arr[0] != NULL) { + short fps_denom; + float fps_num; + + IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); + + video_fps = fps_denom / fps_num; + + /* Adjust scene's frame rate settings to match. */ + if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { + scene->r.frs_sec = fps_denom; + scene->r.frs_sec_base = fps_num; + } + + double video_start_offset = IMD_anim_get_offset(anim_arr[0]); + int minimum_frame_offset; + + if (*r_start_offset >= 0) { + minimum_frame_offset = MIN2(video_start_offset, *r_start_offset) * FPS; + } + else { + minimum_frame_offset = video_start_offset * FPS; + } + + video_frame_offset = video_start_offset * FPS - minimum_frame_offset; + + *r_start_offset = video_start_offset; + } + Sequence *seq = SEQ_sequence_alloc( - seqbase, load_data->start_frame, load_data->channel, SEQ_TYPE_MOVIE); + seqbase, load_data->start_frame + video_frame_offset, load_data->channel, SEQ_TYPE_MOVIE); /* Multiview settings. */ if (load_data->use_multiview) { @@ -579,27 +590,11 @@ Sequence *SEQ_add_movie_strip(Main *bmain, seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ - float video_fps = 0.0f; - if (anim_arr[0] != NULL) { seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); - *r_video_start_offset = IMD_anim_get_offset(anim_arr[0]); IMB_anim_load_metadata(anim_arr[0]); - short fps_denom; - float fps_num; - - IMB_anim_get_fps(anim_arr[0], &fps_denom, &fps_num, true); - - video_fps = fps_denom / fps_num; - - /* Adjust scene's frame rate settings to match. */ - if (load_data->flags & SEQ_LOAD_MOVIE_SYNC_FPS) { - scene->r.frs_sec = fps_denom; - scene->r.frs_sec_base = fps_num; - } - /* Set initial scale based on load_data->fit_method. */ orig_width = IMB_anim_get_image_width(anim_arr[0]); orig_height = IMB_anim_get_image_height(anim_arr[0]); @@ -649,7 +644,9 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo if (lock_range) { /* keep so we don't have to move the actual start and end points (only the data) */ - SEQ_time_update_sequence_bounds(scene, seq); + Editing *ed = SEQ_editing_get(scene); + ListBase *seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, seq); + SEQ_time_update_sequence(scene, seqbase, seq); prev_startdisp = seq->startdisp; prev_enddisp = seq->enddisp; } @@ -799,7 +796,8 @@ void SEQ_add_reload_new_file(Main *bmain, Scene *scene, Sequence *seq, const boo SEQ_transform_fix_single_image_seq_offsets(seq); } - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } void SEQ_add_movie_reload_if_needed(struct Main *bmain, diff --git a/source/blender/sequencer/intern/strip_edit.c b/source/blender/sequencer/intern/strip_edit.c index cfac243e68f..747f0eb3deb 100644 --- a/source/blender/sequencer/intern/strip_edit.c +++ b/source/blender/sequencer/intern/strip_edit.c @@ -267,8 +267,9 @@ bool SEQ_edit_move_strip_to_meta(Scene *scene, SEQ_relations_invalidate_cache_preprocessed(scene, seq); /* Update meta. */ + ListBase *meta_seqbase = SEQ_get_seqbase_by_seq(&ed->seqbase, dst_seqm); SEQ_time_update_meta_strip_range(scene, dst_seqm); - SEQ_time_update_sequence(scene, dst_seqm); + SEQ_time_update_sequence(scene, meta_seqbase, dst_seqm); if (SEQ_transform_test_overlap(&dst_seqm->seqbase, seq)) { SEQ_transform_seqbase_shuffle(&dst_seqm->seqbase, seq, scene); } @@ -359,6 +360,7 @@ static bool seq_edit_split_effect_intersect_check(const Sequence *seq, const int static void seq_edit_split_handle_strip_offsets(Main *bmain, Scene *scene, + ListBase *seqbase, Sequence *left_seq, Sequence *right_seq, const int timeline_frame, @@ -374,7 +376,7 @@ static void seq_edit_split_handle_strip_offsets(Main *bmain, SEQ_add_reload_new_file(bmain, scene, right_seq, false); break; } - SEQ_time_update_sequence(scene, right_seq); + SEQ_time_update_sequence(scene, seqbase, right_seq); } if (seq_edit_split_effect_intersect_check(left_seq, timeline_frame)) { @@ -387,7 +389,7 @@ static void seq_edit_split_handle_strip_offsets(Main *bmain, SEQ_add_reload_new_file(bmain, scene, left_seq, false); break; } - SEQ_time_update_sequence(scene, left_seq); + SEQ_time_update_sequence(scene, seqbase, left_seq); } } @@ -509,7 +511,8 @@ Sequence *SEQ_edit_strip_split(Main *bmain, SEQ_collection_append_strip(right_seq, strips_to_delete); } - seq_edit_split_handle_strip_offsets(bmain, scene, left_seq, right_seq, timeline_frame, method); + seq_edit_split_handle_strip_offsets( + bmain, scene, seqbase, left_seq, right_seq, timeline_frame, method); left_seq = left_seq->next; right_seq = right_seq->next; } diff --git a/source/blender/sequencer/intern/strip_relations.c b/source/blender/sequencer/intern/strip_relations.c index 46fdd2c3d14..d17a37cb9d8 100644 --- a/source/blender/sequencer/intern/strip_relations.c +++ b/source/blender/sequencer/intern/strip_relations.c @@ -330,7 +330,8 @@ static bool update_changed_seq_recurs( } if (len_change) { - SEQ_time_update_sequence(scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(scene)); + SEQ_time_update_sequence(scene, seqbase, seq); } } @@ -354,7 +355,6 @@ void SEQ_relations_update_changed_seq_and_deps(Scene *scene, } } -/* Unused */ static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame) { for (Sequence *seq = seqbase->first; seq != NULL; seq = seq->next) { @@ -367,7 +367,6 @@ static void sequencer_all_free_anim_ibufs(ListBase *seqbase, int timeline_frame) } } -/* Unused */ void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame) { Editing *ed = SEQ_editing_get(scene); @@ -375,7 +374,6 @@ void SEQ_relations_free_all_anim_ibufs(Scene *scene, int timeline_frame) return; } sequencer_all_free_anim_ibufs(&ed->seqbase, timeline_frame); - SEQ_cache_cleanup(scene); } static Sequence *sequencer_check_scene_recursion(Scene *scene, ListBase *seqbase) @@ -526,4 +524,4 @@ struct Sequence *SEQ_find_metastrip_by_sequence(ListBase *seqbase, Sequence *met } return NULL; -} \ No newline at end of file +} diff --git a/source/blender/sequencer/intern/strip_time.c b/source/blender/sequencer/intern/strip_time.c index fd6c0805c23..92ac580f3b1 100644 --- a/source/blender/sequencer/intern/strip_time.c +++ b/source/blender/sequencer/intern/strip_time.c @@ -131,12 +131,17 @@ static void seq_update_sound_bounds_recursive_impl(Scene *scene, endofs = seq->start + seq->len - end; } + double offset_time = 0.0f; + if (seq->sound != NULL) { + offset_time = seq->sound->offset_time; + } + BKE_sound_move_scene_sound(scene, seq->scene_sound, seq->start + startofs, seq->start + seq->len - endofs, startofs + seq->anim_startofs, - seq->sound->offset_time); + offset_time); } } } @@ -148,7 +153,7 @@ void seq_update_sound_bounds_recursive(Scene *scene, Sequence *metaseq) scene, metaseq, metaseq_start(metaseq), metaseq_end(metaseq)); } -void SEQ_time_update_sequence_bounds(Scene *scene, Sequence *seq) +static void seq_time_update_sequence_bounds(Scene *scene, Sequence *seq) { if (seq->startofs && seq->startstill) { seq->startstill = 0; @@ -195,7 +200,7 @@ void SEQ_time_update_meta_strip_range(Scene *scene, Sequence *seq_meta) SEQ_transform_set_right_handle_frame(seq_meta, seq_meta->enddisp); } -void SEQ_time_update_sequence(Scene *scene, Sequence *seq) +void SEQ_time_update_sequence(Scene *scene, ListBase *seqbase, Sequence *seq) { Sequence *seqm; @@ -203,7 +208,7 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) seqm = seq->seqbase.first; while (seqm) { if (seqm->seqbase.first) { - SEQ_time_update_sequence(scene, seqm); + SEQ_time_update_sequence(scene, &seqm->seqbase, seqm); } seqm = seqm->next; } @@ -241,22 +246,25 @@ void SEQ_time_update_sequence(Scene *scene, Sequence *seq) seq->len = seq->enddisp - seq->startdisp; } else { - SEQ_time_update_sequence_bounds(scene, seq); + seq_time_update_sequence_bounds(scene, seq); } } + else if (seq->type == SEQ_TYPE_META) { + seq_time_update_meta_strip(scene, seq); + } else { - if (seq->type == SEQ_TYPE_META) { - seq_time_update_meta_strip(scene, seq); - } - - Editing *ed = SEQ_editing_get(scene); - MetaStack *ms = SEQ_meta_stack_active_get(ed); - if (ms != NULL) { - SEQ_time_update_meta_strip_range(scene, ms->parseq); - } - - SEQ_time_update_sequence_bounds(scene, seq); + seq_time_update_sequence_bounds(scene, seq); } + + Editing *ed = SEQ_editing_get(scene); + + /* Strip is inside meta strip */ + if (seqbase != &ed->seqbase) { + Sequence *meta = SEQ_get_meta_by_seqbase(&ed->seqbase, seqbase); + SEQ_time_update_meta_strip_range(scene, meta); + } + + seq_time_update_sequence_bounds(scene, seq); } int SEQ_time_find_next_prev_edit(Scene *scene, @@ -368,19 +376,27 @@ float SEQ_time_sequence_get_fps(Scene *scene, Sequence *seq) } /** - * Define boundary rectangle of sequencer timeline and fill in rect data + * Initialize given rectangle with the Scene's timeline boundaries. * - * \param scene: Scene in which strips are located - * \param seqbase: ListBase in which strips are located - * \param rect: data structure describing rectangle, that will be filled in by this function + * \param scene: the Scene instance whose timeline boundaries are extracted from + * \param rect: output parameter to be filled with timeline boundaries */ -void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +void SEQ_timeline_init_boundbox(const Scene *scene, rctf *rect) { rect->xmin = scene->r.sfra; rect->xmax = scene->r.efra + 1; rect->ymin = 0.0f; rect->ymax = 8.0f; +} +/** + * Stretch the given rectangle to include the given strips boundaries + * + * \param seqbase: ListBase in which strips are located + * \param rect: output parameter to be filled with strips' boundaries + */ +void SEQ_timeline_expand_boundbox(const ListBase *seqbase, rctf *rect) +{ if (seqbase == NULL) { return; } @@ -398,6 +414,19 @@ void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *re } } +/** + * Define boundary rectangle of sequencer timeline and fill in rect data + * + * \param scene: Scene in which strips are located + * \param seqbase: ListBase in which strips are located + * \param rect: data structure describing rectangle, that will be filled in by this function + */ +void SEQ_timeline_boundbox(const Scene *scene, const ListBase *seqbase, rctf *rect) +{ + SEQ_timeline_init_boundbox(scene, rect); + SEQ_timeline_expand_boundbox(seqbase, rect); +} + static bool strip_exists_at_frame(SeqCollection *all_strips, const int timeline_frame) { Sequence *seq; diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 3a5f93a72b0..becf44a7a8e 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -253,7 +253,8 @@ void SEQ_transform_translate_sequence(Scene *evil_scene, Sequence *seq, int delt SEQ_transform_set_right_handle_frame(seq, seq->enddisp + delta); } - SEQ_time_update_sequence(evil_scene, seq); + ListBase *seqbase = SEQ_active_seqbase_get(SEQ_editing_get(evil_scene)); + SEQ_time_update_sequence(evil_scene, seqbase, seq); } /* return 0 if there weren't enough space */ @@ -266,7 +267,7 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, BLI_assert(ELEM(channel_delta, -1, 1)); test->machine += channel_delta; - SEQ_time_update_sequence(evil_scene, test); + SEQ_time_update_sequence(evil_scene, seqbasep, test); while (SEQ_transform_test_overlap(seqbasep, test)) { if ((channel_delta > 0) ? (test->machine >= MAXSEQ) : (test->machine < 1)) { break; @@ -275,10 +276,10 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, test->machine += channel_delta; /* XXX: I don't think this is needed since were only moving vertically, Campbell. */ - SEQ_time_update_sequence(evil_scene, test); + SEQ_time_update_sequence(evil_scene, seqbasep, test); } - if ((test->machine < 1) || (test->machine > MAXSEQ)) { + if (!SEQ_valid_strip_channel(test)) { /* Blender 2.4x would remove the strip. * nicer to move it to the end */ @@ -295,7 +296,7 @@ bool SEQ_transform_seqbase_shuffle_ex(ListBase *seqbasep, new_frame = new_frame + (test->start - test->startdisp); /* adjust by the startdisp */ SEQ_transform_translate_sequence(evil_scene, test, new_frame - test->start); - SEQ_time_update_sequence(evil_scene, test); + SEQ_time_update_sequence(evil_scene, seqbasep, test); return false; } @@ -355,7 +356,7 @@ static int shuffle_seq_time_offset(SeqCollection *strips_to_shuffle, } SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) { - SEQ_time_update_sequence_bounds(scene, seq); /* corrects dummy startdisp/enddisp values */ + SEQ_time_update_sequence(scene, seqbasep, seq); /* corrects dummy startdisp/enddisp values */ } return tot_ofs; @@ -408,7 +409,7 @@ void SEQ_transform_offset_after_frame(Scene *scene, LISTBASE_FOREACH (Sequence *, seq, seqbase) { if (seq->startdisp >= timeline_frame) { SEQ_transform_translate_sequence(scene, seq, delta); - SEQ_time_update_sequence(scene, seq); + SEQ_time_update_sequence(scene, seqbase, seq); SEQ_relations_invalidate_cache_preprocessed(scene, seq); } } @@ -421,3 +422,141 @@ void SEQ_transform_offset_after_frame(Scene *scene, } } } + +void SEQ_image_transform_mirror_factor_get(const Sequence *seq, float r_mirror[2]) +{ + r_mirror[0] = 1.0f; + r_mirror[1] = 1.0f; + + if ((seq->flag & SEQ_FLIPX) != 0) { + r_mirror[0] = -1.0f; + } + if ((seq->flag & SEQ_FLIPY) != 0) { + r_mirror[1] = -1.0f; + } +} + +/** + * Get strip transform origin offset from image center + * Note: This function does not apply axis mirror. + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param r_origin: return value + */ +void SEQ_image_transform_origin_offset_pixelspace_get(const Scene *scene, + const Sequence *seq, + float r_origin[2]) +{ + float image_size[2]; + StripElem *strip_elem = seq->strip->stripdata; + if (strip_elem == NULL) { + image_size[0] = scene->r.xsch; + image_size[1] = scene->r.ysch; + } + else { + image_size[0] = strip_elem->orig_width; + image_size[1] = strip_elem->orig_height; + } + + const StripTransform *transform = seq->strip->transform; + r_origin[0] = (image_size[0] * transform->origin[0]) - (image_size[0] * 0.5f) + transform->xofs; + r_origin[1] = (image_size[1] * transform->origin[1]) - (image_size[1] * 0.5f) + transform->yofs; + + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + mul_v2_v2(r_origin, mirror); +} + +static void seq_image_transform_quad_get_ex(const Scene *scene, + const Sequence *seq, + bool apply_rotation, + float r_quad[4][2]) +{ + StripTransform *transform = seq->strip->transform; + StripCrop *crop = seq->strip->crop; + + int image_size[2] = {scene->r.xsch, scene->r.ysch}; + if (ELEM(seq->type, SEQ_TYPE_MOVIE, SEQ_TYPE_IMAGE)) { + image_size[0] = seq->strip->stripdata->orig_width; + image_size[1] = seq->strip->stripdata->orig_height; + } + + float transform_matrix[4][4]; + + float rotation_matrix[3][3]; + axis_angle_to_mat3_single(rotation_matrix, 'Z', apply_rotation ? transform->rotation : 0.0f); + loc_rot_size_to_mat4(transform_matrix, + (const float[]){transform->xofs, transform->yofs, 0.0f}, + rotation_matrix, + (const float[]){transform->scale_x, transform->scale_y, 1.0f}); + const float origin[2] = {image_size[0] * transform->origin[0], + image_size[1] * transform->origin[1]}; + const float pivot[3] = {origin[0] - (image_size[0] / 2), origin[1] - (image_size[1] / 2), 0.0f}; + transform_pivot_set_m4(transform_matrix, pivot); + + float quad_temp[4][3]; + for (int i = 0; i < 4; i++) { + zero_v2(quad_temp[i]); + } + + quad_temp[0][0] = (image_size[0] / 2) - crop->right; + quad_temp[0][1] = (image_size[1] / 2) - crop->top; + quad_temp[1][0] = (image_size[0] / 2) - crop->right; + quad_temp[1][1] = (-image_size[1] / 2) + crop->bottom; + quad_temp[2][0] = (-image_size[0] / 2) + crop->left; + quad_temp[2][1] = (-image_size[1] / 2) + crop->bottom; + quad_temp[3][0] = (-image_size[0] / 2) + crop->left; + quad_temp[3][1] = (image_size[1] / 2) - crop->top; + + float mirror[2]; + SEQ_image_transform_mirror_factor_get(seq, mirror); + + for (int i = 0; i < 4; i++) { + mul_m4_v3(transform_matrix, quad_temp[i]); + mul_v2_v2(quad_temp[i], mirror); + copy_v2_v2(r_quad[i], quad_temp[i]); + } +} + +/** + * Get 4 corner points of strip image, optionally without rotation component applied + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param apply_rotation: Apply sequence rotation transform to the quad + * \param r_quad: array of 4 2D vectors + */ +void SEQ_image_transform_quad_get(const Scene *scene, + const Sequence *seq, + bool apply_rotation, + float r_quad[4][2]) +{ + seq_image_transform_quad_get_ex(scene, seq, apply_rotation, r_quad); +} + +/** + * Get 4 corner points of strip image. + * + * \param scene: Scene in which strips are located + * \param seq: Sequence to calculate image transform origin + * \param r_quad: array of 4 2D vectors + */ +void SEQ_image_transform_final_quad_get(const Scene *scene, + const Sequence *seq, + float r_quad[4][2]) +{ + seq_image_transform_quad_get_ex(scene, seq, true, r_quad); +} + +void SEQ_image_preview_unit_to_px(const Scene *scene, const float co_src[2], float co_dst[2]) +{ + co_dst[0] = co_src[0] * scene->r.xsch; + co_dst[1] = co_src[1] * scene->r.ysch; +} + +void SEQ_image_preview_unit_from_px(const Scene *scene, const float co_src[2], float co_dst[2]) +{ + co_dst[0] = co_src[0] / scene->r.xsch; + co_dst[1] = co_src[1] / scene->r.ysch; +} diff --git a/source/blender/sequencer/intern/utils.c b/source/blender/sequencer/intern/utils.c index 1d3e7e4a223..71686065882 100644 --- a/source/blender/sequencer/intern/utils.c +++ b/source/blender/sequencer/intern/utils.c @@ -42,6 +42,7 @@ #include "SEQ_edit.h" #include "SEQ_iterator.h" #include "SEQ_relations.h" +#include "SEQ_render.h" #include "SEQ_select.h" #include "SEQ_sequencer.h" #include "SEQ_time.h" @@ -449,6 +450,21 @@ ListBase *SEQ_get_seqbase_by_seq(ListBase *seqbase, Sequence *seq) return NULL; } +Sequence *SEQ_get_meta_by_seqbase(ListBase *seqbase_main, ListBase *meta_seqbase) +{ + SeqCollection *strips = SEQ_query_all_strips_recursive(seqbase_main); + + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, strips) { + if (seq->type == SEQ_TYPE_META && &seq->seqbase == meta_seqbase) { + break; + } + } + + SEQ_collection_free(strips); + return seq; +} + /** * Only use as last resort when the StripElem is available but no the Sequence. * (needed for RNA) diff --git a/source/blender/sequencer/intern/utils.h b/source/blender/sequencer/intern/utils.h index 7aee7d229c9..512647ed2e2 100644 --- a/source/blender/sequencer/intern/utils.h +++ b/source/blender/sequencer/intern/utils.h @@ -28,9 +28,12 @@ extern "C" { #endif struct Scene; +struct ListBase; bool sequencer_seq_generates_image(struct Sequence *seq); void seq_open_anim_file(struct Scene *scene, struct Sequence *seq, bool openfile); +Sequence *SEQ_get_meta_by_seqbase(struct ListBase *seqbase_main, struct ListBase *meta_seqbase); + #ifdef __cplusplus } #endif diff --git a/source/blender/shader_fx/intern/FX_ui_common.c b/source/blender/shader_fx/intern/FX_ui_common.c index 8cb1ea6b66c..de5bef5d8d5 100644 --- a/source/blender/shader_fx/intern/FX_ui_common.c +++ b/source/blender/shader_fx/intern/FX_ui_common.c @@ -101,7 +101,7 @@ void shaderfx_panel_end(uiLayout *layout, PointerRNA *ptr) ShaderFxData *fx = ptr->data; if (fx->error) { uiLayout *row = uiLayoutRow(layout, false); - uiItemL(row, IFACE_(fx->error), ICON_ERROR); + uiItemL(row, TIP_(fx->error), ICON_ERROR); } } @@ -254,7 +254,7 @@ PanelType *shaderfx_panel_register(ARegionType *region_type, ShaderFxType type, /* Give the panel the special flag that says it was built here and corresponds to a * shader effect rather than a PanelType. */ - panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_DRAW_BOX | PANEL_TYPE_INSTANCED; + panel_type->flag = PANEL_TYPE_HEADER_EXPAND | PANEL_TYPE_INSTANCED; panel_type->reorder = shaderfx_reorder; panel_type->get_list_data_expand_flag = get_shaderfx_expand_flag; panel_type->set_list_data_expand_flag = set_shaderfx_expand_flag; @@ -287,7 +287,7 @@ PanelType *shaderfx_subpanel_register(ARegionType *region_type, panel_type->draw_header = draw_header; panel_type->draw = draw; panel_type->poll = shaderfx_ui_poll; - panel_type->flag = (PANEL_TYPE_DEFAULT_CLOSED | PANEL_TYPE_DRAW_BOX); + panel_type->flag = PANEL_TYPE_DEFAULT_CLOSED; BLI_assert(parent != NULL); BLI_strncpy(panel_type->parent_id, parent->idname, BKE_ST_MAXNAME); diff --git a/source/blender/simulation/intern/ConstrainedConjugateGradient.h b/source/blender/simulation/intern/ConstrainedConjugateGradient.h index c5a2827a09f..c231d511733 100644 --- a/source/blender/simulation/intern/ConstrainedConjugateGradient.h +++ b/source/blender/simulation/intern/ConstrainedConjugateGradient.h @@ -164,13 +164,13 @@ struct traits< * \brief A conjugate gradient solver for sparse self-adjoint problems with additional constraints * * This class allows to solve for A.x = b sparse linear problems using a conjugate gradient - * algorithm. The sparse matrix A must be selfadjoint. The vectors x and b can be either dense or + * algorithm. The sparse matrix A must be self-adjoint. The vectors x and b can be either dense or * sparse. * * \tparam _MatrixType the type of the sparse matrix A, can be a dense or a sparse matrix. * \tparam _UpLo the triangular part that will be used for the computations. It can be Lower * or Upper. Default is Lower. - * \tparam _Preconditioner the type of the preconditioner. Default is DiagonalPreconditioner + * \tparam _Preconditioner the type of the pre-conditioner. Default is #DiagonalPreconditioner * * The maximal number of iterations and tolerance value can be controlled via the * setMaxIterations() and setTolerance() methods. The defaults are the size of the problem for the diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 4d65726fe2b..03b2fb49085 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -202,6 +202,7 @@ if(WITH_XR_OPENXR) xr/intern/wm_xr_action.c xr/intern/wm_xr_actionmap.c xr/intern/wm_xr_draw.c + xr/intern/wm_xr_operators.c xr/intern/wm_xr_session.c xr/wm_xr.h diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 189a231616e..112d76a3e65 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -41,6 +41,8 @@ extern "C" { #endif struct ARegion; +struct AssetHandle; +struct AssetLibraryReference; struct GHashIterator; struct GPUViewport; struct ID; @@ -176,12 +178,12 @@ void WM_opengl_context_dispose(void *context); void WM_opengl_context_activate(void *context); void WM_opengl_context_release(void *context); -/* WM_window_open alignment */ -typedef enum WindowAlignment { +/* #WM_window_open alignment */ +typedef enum eWindowAlignment { WIN_ALIGN_ABSOLUTE = 0, WIN_ALIGN_LOCATION_CENTER, WIN_ALIGN_PARENT_CENTER, -} WindowAlignment; +} eWindowAlignment; struct wmWindow *WM_window_open(struct bContext *C, const char *title, @@ -193,7 +195,7 @@ struct wmWindow *WM_window_open(struct bContext *C, bool toplevel, bool dialog, bool temp, - WindowAlignment alignment); + eWindowAlignment alignment); void WM_window_set_dpi(const wmWindow *win); @@ -212,14 +214,16 @@ struct ID *WM_file_link_datablock(struct Main *bmain, struct View3D *v3d, const char *filepath, const short id_code, - const char *id_name); + const char *id_name, + int flag); struct ID *WM_file_append_datablock(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct View3D *v3d, const char *filepath, const short id_code, - const char *id_name); + const char *id_name, + int flag); void WM_lib_reload(struct Library *lib, struct bContext *C, struct ReportList *reports); /* mouse cursors */ @@ -262,14 +266,24 @@ struct wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *han wmKeyMap *keymap, int priority); -typedef struct wmKeyMap *(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, - struct wmEventHandler_Keymap *handler) - ATTR_WARN_UNUSED_RESULT; +typedef struct wmEventHandler_KeymapResult { + wmKeyMap *keymaps[3]; + int keymaps_len; +} wmEventHandler_KeymapResult; -struct wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback( - struct wmWindowManager *wm, struct wmEventHandler_Keymap *handler); -struct wmKeyMap *WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm, - struct wmEventHandler_Keymap *handler); +typedef void(wmEventHandler_KeymapDynamicFn)(wmWindowManager *wm, + struct wmWindow *win, + struct wmEventHandler_Keymap *handler, + struct wmEventHandler_KeymapResult *km_result); + +void WM_event_get_keymap_from_toolsystem_with_gizmos(struct wmWindowManager *wm, + struct wmWindow *win, + struct wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result); +void WM_event_get_keymap_from_toolsystem(struct wmWindowManager *wm, + struct wmWindow *win, + struct wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result); struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic( ListBase *handlers, wmEventHandler_KeymapDynamicFn *keymap_fn, void *user_data); @@ -281,8 +295,10 @@ void WM_event_set_keymap_handler_post_callback(struct wmEventHandler_Keymap *han wmKeyMapItem *kmi, void *user_data), void *user_data); -wmKeyMap *WM_event_get_keymap_from_handler(wmWindowManager *wm, - struct wmEventHandler_Keymap *handler); +void WM_event_get_keymaps_from_handler(wmWindowManager *wm, + struct wmWindow *win, + struct wmEventHandler_Keymap *handler, + struct wmEventHandler_KeymapResult *km_result); wmKeyMapItem *WM_event_match_keymap_item(struct bContext *C, wmKeyMap *keymap, @@ -290,6 +306,7 @@ wmKeyMapItem *WM_event_match_keymap_item(struct bContext *C, wmKeyMapItem *WM_event_match_keymap_item_from_handlers(struct bContext *C, struct wmWindowManager *wm, + struct wmWindow *win, struct ListBase *handlers, const struct wmEvent *event); @@ -355,8 +372,8 @@ void WM_main_remap_editor_id_reference(struct ID *old_id, struct ID *new_id); /* reports */ void WM_report_banner_show(void); void WM_report_banners_cancel(struct Main *bmain); -void WM_report(ReportType type, const char *message); -void WM_reportf(ReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(2, 3); +void WM_report(eReportType type, const char *message); +void WM_reportf(eReportType type, const char *format, ...) ATTR_PRINTF_FORMAT(2, 3); struct wmEvent *wm_event_add_ex(struct wmWindow *win, const struct wmEvent *event_to_add, @@ -580,7 +597,7 @@ void WM_operator_py_idname(char *to, const char *from); bool WM_operator_py_idname_ok_or_report(struct ReportList *reports, const char *classname, const char *idname); -char *WM_context_path_resolve_property_full(struct bContext *C, +char *WM_context_path_resolve_property_full(const struct bContext *C, const PointerRNA *ptr, PropertyRNA *prop, int index); @@ -707,6 +724,8 @@ void WM_event_fileselect_event(struct wmWindowManager *wm, void *ophandle, int e void WM_operator_region_active_win_set(struct bContext *C); +int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent *event); + /* drag and drop */ struct wmDrag *WM_event_start_drag( struct bContext *C, int icon, int type, void *poin, double value, unsigned int flags); @@ -721,21 +740,44 @@ struct wmDropBox *WM_dropbox_add( void (*copy)(struct wmDrag *, struct wmDropBox *), void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *), WMDropboxTooltipFunc tooltip); +void WM_drag_draw_item_name_fn(struct bContext *C, + struct wmWindow *win, + struct wmDrag *drag, + const int xy[2]); +void WM_drag_draw_default_fn(struct bContext *C, + struct wmWindow *win, + struct wmDrag *drag, + const int xy[2]); ListBase *WM_dropboxmap_find(const char *idname, int spaceid, int regionid); /* ID drag and drop */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, int flag_extra); +bool WM_drag_asset_will_import_linked(const wmDrag *drag); void WM_drag_add_local_ID(struct wmDrag *drag, struct ID *id, struct ID *from_parent); struct ID *WM_drag_get_local_ID(const struct wmDrag *drag, short idcode); struct ID *WM_drag_get_local_ID_from_event(const struct wmEvent *event, short idcode); bool WM_drag_is_ID_type(const struct wmDrag *drag, int idcode); +wmDragAsset *WM_drag_create_asset_data(const struct AssetHandle *asset, + struct AssetMetaData *metadata, + const char *path, + int import_type); struct wmDragAsset *WM_drag_get_asset_data(const struct wmDrag *drag, int idcode); +struct AssetMetaData *WM_drag_get_asset_meta_data(const struct wmDrag *drag, int idcode); struct ID *WM_drag_get_local_ID_or_import_from_asset(const struct wmDrag *drag, int idcode); void WM_drag_free_imported_drag_ID(struct Main *bmain, struct wmDrag *drag, struct wmDropBox *drop); +struct wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const struct wmDrag *drag); + +void WM_drag_add_asset_list_item(wmDrag *drag, + const struct bContext *C, + const struct AssetLibraryReference *asset_library_ref, + const struct AssetHandle *asset); +const ListBase *WM_drag_asset_list_get(const wmDrag *drag); + const char *WM_drag_get_item_name(struct wmDrag *drag); /* Set OpenGL viewport and scissor */ @@ -787,6 +829,7 @@ enum { WM_JOB_TYPE_QUADRIFLOW_REMESH, WM_JOB_TYPE_TRACE_IMAGE, WM_JOB_TYPE_LINEART, + WM_JOB_TYPE_SEQ_DRAW_THUMBNAIL, /* add as needed, bake, seq proxy build * if having hard coded values is a problem */ }; @@ -893,6 +936,8 @@ int WM_event_modifier_flag(const struct wmEvent *event); bool WM_event_is_modal_tweak_exit(const struct wmEvent *event, int tweak_event); bool WM_event_is_last_mousemove(const struct wmEvent *event); bool WM_event_is_mouse_drag(const struct wmEvent *event); +bool WM_event_is_mouse_drag_or_press(const wmEvent *event); +bool WM_cursor_test_motion_and_update(const int mval[2]) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; int WM_event_drag_threshold(const struct wmEvent *event); bool WM_event_drag_test(const struct wmEvent *event, const int prev_xy[2]); @@ -912,6 +957,10 @@ float WM_event_ndof_to_axis_angle(const struct wmNDOFMotionData *ndof, float axi void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]); #endif /* WITH_INPUT_NDOF */ +#ifdef WITH_XR_OPENXR +bool WM_event_is_xr(const struct wmEvent *event); +#endif + float WM_event_tablet_data(const struct wmEvent *event, int *pen_flip, float tilt[2]); bool WM_event_is_tablet(const struct wmEvent *event); @@ -964,6 +1013,7 @@ bool WM_region_use_viewport(struct ScrArea *area, struct ARegion *region); bool WM_xr_session_exists(const wmXrData *xr); bool WM_xr_session_is_ready(const wmXrData *xr); struct wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr); +struct ScrArea *WM_xr_session_area_get(const wmXrData *xr); void WM_xr_session_base_pose_reset(wmXrData *xr); bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]); bool WM_xr_session_state_viewer_pose_rotation_get(const wmXrData *xr, float r_rotation[4]); @@ -982,6 +1032,15 @@ bool WM_xr_session_state_controller_aim_location_get(const wmXrData *xr, bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, unsigned int subaction_idx, float r_rotation[4]); +bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]); +void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]); +bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]); +void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]); +bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale); +void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale); +void WM_xr_session_state_navigation_reset(struct wmXrSessionState *state); + +struct ARegionType *WM_xr_surface_controller_region_type_get(void); /* wm_xr_actions.c */ /* XR action functions to be called pre-XR session start. diff --git a/source/blender/windowmanager/WM_toolsystem.h b/source/blender/windowmanager/WM_toolsystem.h index 018165634f2..eb89fca7b56 100644 --- a/source/blender/windowmanager/WM_toolsystem.h +++ b/source/blender/windowmanager/WM_toolsystem.h @@ -134,6 +134,7 @@ void WM_toolsystem_refresh_active(struct bContext *C); void WM_toolsystem_refresh_screen_area(struct WorkSpace *workspace, struct ViewLayer *view_layer, struct ScrArea *area); +void WM_toolsystem_refresh_screen_window(struct wmWindow *win); void WM_toolsystem_refresh_screen_all(struct Main *bmain); #ifdef __cplusplus diff --git a/source/blender/windowmanager/WM_types.h b/source/blender/windowmanager/WM_types.h index df6dc3af3cb..9f427a90353 100644 --- a/source/blender/windowmanager/WM_types.h +++ b/source/blender/windowmanager/WM_types.h @@ -119,7 +119,9 @@ struct wmWindowManager; #include "BLI_compiler_attrs.h" #include "DNA_listBase.h" +#include "DNA_uuid_types.h" #include "DNA_vec_types.h" +#include "DNA_xr_types.h" #include "RNA_types.h" /* exported types for WM */ @@ -226,6 +228,10 @@ enum { WM_OP_EXEC_SCREEN, }; +#define WM_OP_CONTEXT_HAS_AREA(type) (!ELEM(type, WM_OP_INVOKE_SCREEN, WM_OP_EXEC_SCREEN)) +#define WM_OP_CONTEXT_HAS_REGION(type) \ + (WM_OP_CONTEXT_HAS_AREA(type) && !ELEM(type, WM_OP_INVOKE_AREA, WM_OP_EXEC_AREA)) + /* property tags for RNA_OperatorProperties */ typedef enum eOperatorPropTags { OP_PROP_TAG_ADVANCED = (1 << 0), @@ -457,6 +463,10 @@ typedef struct wmNotifier { #define ND_ASSET_LIST (1 << 16) #define ND_ASSET_LIST_PREVIEW (2 << 16) #define ND_ASSET_LIST_READING (3 << 16) +/* Catalog data changed, requiring a redraw of catalog UIs. Note that this doesn't denote a + * reloading of asset libraries & their catalogs should happen. That only happens on explicit user + * action. */ +#define ND_ASSET_CATALOGS (4 << 16) /* subtype, 256 entries too */ #define NOTE_SUBTYPE 0x0000FF00 @@ -588,10 +598,10 @@ typedef struct wmTabletData { * - The previous values are only set for mouse button and keyboard events. * See: #ISMOUSE_BUTTON & #ISKEYBOARD macros. * - * - Previous x/y are exceptions: #wmEvent.prevx & #wmEvent.prevy + * - Previous x/y are exceptions: #wmEvent.prev * these are set on mouse motion, see #MOUSEMOVE & track-pad events. * - * - Modal key-map handling sets `prevval` & `prevtype` to `val` & `type`, + * - Modal key-map handling sets `prev_val` & `prev_type` to `val` & `type`, * this allows modal keys-maps to check the original values (needed in some cases). */ typedef struct wmEvent { @@ -602,7 +612,7 @@ typedef struct wmEvent { /** Press, release, scroll-value. */ short val; /** Mouse pointer position, screen coord. */ - int x, y; + int xy[2]; /** Region relative mouse position (name convention before Blender 2.5). */ int mval[2]; /** @@ -623,19 +633,19 @@ typedef struct wmEvent { char is_repeat; /** The previous value of `type`. */ - short prevtype; + short prev_type; /** The previous value of `val`. */ - short prevval; + short prev_val; /** The time when the key is pressed, see #PIL_check_seconds_timer. */ - double prevclicktime; + double prev_click_time; /** The location when the key is pressed (used to enforce drag thresholds). */ - int prevclickx, prevclicky; + int prev_click_xy[2]; /** - * The previous value of #wmEvent.x #wmEvent.y, + * The previous value of #wmEvent.xy, * Unlike other previous state variables, this is set on any mouse motion. - * Use `prevclickx` & `prevclicky` for the value at time of pressing. + * Use `prev_click` for the value at time of pressing. */ - int prevx, prevy; + int prev_xy[2]; /** Modifier states. */ /** 'oskey' is apple or windows-key, value denotes order of pressed. */ @@ -649,14 +659,14 @@ typedef struct wmEvent { /* Custom data. */ /** Custom data type, stylus, 6dof, see wm_event_types.h */ short custom; - short customdatafree; + short customdata_free; int pad2; /** Ascii, unicode, mouse-coords, angles, vectors, NDOF data, drag-drop info. */ void *customdata; /** * True if the operating system inverted the delta x/y values and resulting - * `prevx`, `prevy` values, for natural scroll direction. + * `prev_xy` values, for natural scroll direction. * For absolute scroll direction, the delta must be negated again. */ char is_direction_inverted; @@ -721,11 +731,41 @@ typedef struct wmXrActionState { }; int type; /* eXrActionType */ } wmXrActionState; + +typedef struct wmXrActionData { + /** Action set name. */ + char action_set[64]; + /** Action name. */ + char action[64]; + /** Type. */ + eXrActionType type; + /** State. Set appropriately based on type. */ + float state[2]; + /** State of the other sub-action path for bimanual actions. */ + float state_other[2]; + + /** Input threshold for float/vector2f actions. */ + float float_threshold; + + /** Controller aim pose corresponding to the action's sub-action path. */ + float controller_loc[3]; + float controller_rot[4]; + /** Controller aim pose of the other sub-action path for bimanual actions. */ + float controller_loc_other[3]; + float controller_rot_other[4]; + + /** Operator. */ + struct wmOperatorType *ot; + struct IDProperty *op_properties; + + /** Whether bimanual interaction is occurring. */ + bool bimanual; +} wmXrActionData; #endif /** Timer flags. */ typedef enum { - /** Do not attempt to free customdata pointer even if non-NULL. */ + /** Do not attempt to free custom-data pointer even if non-NULL. */ WM_TIMER_NO_FREE_CUSTOM_DATA = 1 << 0, } wmTimerFlags; @@ -868,6 +908,9 @@ typedef struct wmOperatorType { /** RNA integration */ ExtensionRNA rna_ext; + /** Cursor to use when waiting for cursor input, see: #OPTYPE_DEPENDS_ON_CURSOR. */ + int cursor_pending; + /** Flag last for padding */ short flag; @@ -915,12 +958,17 @@ typedef void (*wmPaintCursorDraw)(struct bContext *C, int, int, void *customdata #define WM_DRAG_ID 0 #define WM_DRAG_ASSET 1 -#define WM_DRAG_RNA 2 -#define WM_DRAG_PATH 3 -#define WM_DRAG_NAME 4 -#define WM_DRAG_VALUE 5 -#define WM_DRAG_COLOR 6 -#define WM_DRAG_DATASTACK 7 +/** The user is dragging multiple assets. This is only supported in few specific cases, proper + * multi-item support for dragging isn't supported well yet. Therefore this is kept separate from + * #WM_DRAG_ASSET. */ +#define WM_DRAG_ASSET_LIST 2 +#define WM_DRAG_RNA 3 +#define WM_DRAG_PATH 4 +#define WM_DRAG_NAME 5 +#define WM_DRAG_VALUE 6 +#define WM_DRAG_COLOR 7 +#define WM_DRAG_DATASTACK 8 +#define WM_DRAG_ASSET_CATALOG 9 typedef enum wmDragFlags { WM_DRAG_NOP = 0, @@ -943,12 +991,43 @@ typedef struct wmDragAsset { /* Always freed. */ const char *path; int id_type; + struct AssetMetaData *metadata; int import_type; /* eFileAssetImportType */ + + /* FIXME: This is temporary evil solution to get scene/view-layer/etc in the copy callback of the + * #wmDropBox. + * TODO: Handle link/append in operator called at the end of the drop process, and NOT in its + * copy callback. + * */ + struct bContext *evil_C; } wmDragAsset; +typedef struct wmDragAssetCatalog { + bUUID drag_catalog_id; +} wmDragAssetCatalog; + +/** + * For some specific cases we support dragging multiple assets (#WM_DRAG_ASSET_LIST). There is no + * proper support for dragging multiple items in the `wmDrag`/`wmDrop` API yet, so this is really + * just to enable specific features for assets. + * + * This struct basically contains a tagged union to either store a local ID pointer, or information + * about an externally stored asset. + */ +typedef struct wmDragAssetListItem { + struct wmDragAssetListItem *next, *prev; + + union { + struct ID *local_id; + wmDragAsset *external_info; + } asset_data; + + bool is_external; +} wmDragAssetListItem; + typedef char *(*WMDropboxTooltipFunc)(struct bContext *, struct wmDrag *, - const struct wmEvent *event, + const int xy[2], struct wmDropBox *drop); typedef struct wmDrag { @@ -966,12 +1045,22 @@ typedef struct wmDrag { float scale; int sx, sy; - /** If filled, draws operator tooltip/operator name. */ - char tooltip[200]; + /** Informs which dropbox is activated with the drag item. + * When this value changes, the #draw_activate and #draw_deactivate dropbox callbacks are + * triggered. + */ + struct wmDropBox *active_dropbox; + /* Text to show when the operator poll fails. Typically the message the + * operator set with CTX_wm_operator_poll_msg_set(). */ + const char *disabled_info; + bool free_disabled_info; + unsigned int flags; /** List of wmDragIDs, all are guaranteed to have the same ID type. */ ListBase ids; + /** List of `wmDragAssetListItem`s. */ + ListBase asset_items; } wmDrag; /** @@ -993,6 +1082,18 @@ typedef struct wmDropBox { */ void (*cancel)(struct Main *, struct wmDrag *, struct wmDropBox *); + /** Override the default drawing function. */ + void (*draw)(struct bContext *, struct wmWindow *, struct wmDrag *, const int *); + + /** Called when pool returns true the first time. */ + void (*draw_activate)(struct wmDropBox *, struct wmDrag *drag); + + /** Called when pool returns false the first time or when the drag event ends. */ + void (*draw_deactivate)(struct wmDropBox *, struct wmDrag *drag); + + /** Custom data for drawing. */ + void *draw_data; + /** Custom tooltip shown during dragging. */ WMDropboxTooltipFunc tooltip; diff --git a/source/blender/windowmanager/gizmo/WM_gizmo_types.h b/source/blender/windowmanager/gizmo/WM_gizmo_types.h index a1edc4196dc..ceaec94e70b 100644 --- a/source/blender/windowmanager/gizmo/WM_gizmo_types.h +++ b/source/blender/windowmanager/gizmo/WM_gizmo_types.h @@ -380,7 +380,7 @@ typedef struct wmGizmoType { /** * Returns screen-space bounding box in the window space - * (compatible with #wmEvent.x #wmEvent.y). + * (compatible with #wmEvent.xy). * * Used for tool-tip placement (otherwise the cursor location is used). */ @@ -502,8 +502,6 @@ typedef struct wmGizmoGroup { bool tag_remove; - bool use_fallback_keymap; - void *customdata; /** For freeing customdata from above. */ void (*customdata_free)(void *); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c index 213a3c2e342..b6d759eb95f 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_group.c @@ -265,6 +265,7 @@ void WM_gizmogroup_ensure_init(const bContext *C, wmGizmoGroup *gzgroup) { /* prepare for first draw */ if (UNLIKELY((gzgroup->init_flag & WM_GIZMOGROUP_INIT_SETUP) == 0)) { + gzgroup->type->setup(C, gzgroup); /* Not ideal, initialize keymap here, needed for RNA runtime generated gizmos. */ @@ -548,8 +549,8 @@ static int gizmo_tweak_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == EVT_MODAL_MAP) { event_modal_val = evil_event->val; - evil_event->type = evil_event->prevtype; - evil_event->val = evil_event->prevval; + evil_event->type = evil_event->prev_type; + evil_event->val = evil_event->prev_val; } int modal_retval = modal_fn(C, gz, event, mtweak->flag); diff --git a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c index 295196c701b..18e1a8420a6 100644 --- a/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c +++ b/source/blender/windowmanager/gizmo/intern/wm_gizmo_map.c @@ -1073,7 +1073,7 @@ void wm_gizmomap_modal_set( if ((gz->flag & WM_GIZMO_MOVE_CURSOR) && (event->tablet.is_motion_absolute == false)) { WM_cursor_grab_enable(win, WM_CURSOR_WRAP_XY, true, NULL); - copy_v2_v2_int(gzmap->gzmap_context.event_xy, &event->x); + copy_v2_v2_int(gzmap->gzmap_context.event_xy, event->xy); gzmap->gzmap_context.event_grabcursor = win->grabcursor; } else { diff --git a/source/blender/windowmanager/intern/wm.c b/source/blender/windowmanager/intern/wm.c index 0b7d5e5f1f4..47ee296823b 100644 --- a/source/blender/windowmanager/intern/wm.c +++ b/source/blender/windowmanager/intern/wm.c @@ -86,18 +86,23 @@ static void window_manager_foreach_id(ID *id, LibraryForeachIDData *data) wmWindowManager *wm = (wmWindowManager *)id; LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - BKE_LIB_FOREACHID_PROCESS(data, win->scene, IDWALK_CB_USER_ONE); + BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, win->scene, IDWALK_CB_USER_ONE); /* This pointer can be NULL during old files reading, better be safe than sorry. */ if (win->workspace_hook != NULL) { ID *workspace = (ID *)BKE_workspace_active_get(win->workspace_hook); - BKE_LIB_FOREACHID_PROCESS_ID(data, workspace, IDWALK_CB_NOP); + BKE_lib_query_foreachid_process(data, &workspace, IDWALK_CB_USER); /* Allow callback to set a different workspace. */ BKE_workspace_active_set(win->workspace_hook, (WorkSpace *)workspace); + if (BKE_lib_query_foreachid_iter_stop(data)) { + return; + } } + if (BKE_lib_query_foreachid_process_flags_get(data) & IDWALK_INCLUDE_UI) { LISTBASE_FOREACH (ScrArea *, area, &win->global_areas.areabase) { - BKE_screen_foreach_id_screen_area(data, area); + BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, + BKE_screen_foreach_id_screen_area(data, area)); } } } diff --git a/source/blender/windowmanager/intern/wm_dragdrop.c b/source/blender/windowmanager/intern/wm_dragdrop.c index 76bb93b681c..b9f0e09d106 100644 --- a/source/blender/windowmanager/intern/wm_dragdrop.c +++ b/source/blender/windowmanager/intern/wm_dragdrop.c @@ -34,13 +34,23 @@ #include "BLT_translation.h" #include "BLI_blenlib.h" +#include "BLI_math_color.h" #include "BIF_glutil.h" #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" +#include "BKE_main.h" +#include "BKE_screen.h" + +#include "GHOST_C-api.h" + +#include "BLO_readfile.h" + +#include "ED_asset.h" #include "GPU_shader.h" #include "GPU_state.h" @@ -50,17 +60,21 @@ #include "UI_interface.h" #include "UI_interface_icons.h" +#include "UI_resources.h" #include "RNA_access.h" #include "WM_api.h" #include "WM_types.h" #include "wm_event_system.h" +#include "wm_window.h" /* ****************************************************** */ static ListBase dropboxes = {NULL, NULL}; +static void wm_drag_free_asset_data(wmDragAsset **asset_data); + /* drop box maps are stored global for now */ /* these are part of blender's UI/space specs, and not like keymaps */ /* when editors become configurable, they can add own dropbox definitions */ @@ -167,10 +181,24 @@ wmDrag *WM_event_start_drag( } break; case WM_DRAG_ASSET: + case WM_DRAG_ASSET_CATALOG: /* Move ownership of poin to wmDrag. */ drag->poin = poin; drag->flags |= WM_DRAG_FREE_DATA; break; + /* The asset-list case is special: We get multiple assets from context and attach them to the + * drag item. */ + case WM_DRAG_ASSET_LIST: { + const AssetLibraryReference *asset_library = CTX_wm_asset_library_ref(C); + ListBase asset_file_links = CTX_data_collection_get(C, "selected_asset_files"); + LISTBASE_FOREACH (const CollectionPointerLink *, link, &asset_file_links) { + const FileDirEntry *asset_file = link->ptr.data; + const AssetHandle asset_handle = {asset_file}; + WM_drag_add_asset_list_item(drag, C, asset_library, &asset_handle); + } + BLI_freelistN(&asset_file_links); + break; + } default: drag->poin = poin; break; @@ -180,6 +208,31 @@ wmDrag *WM_event_start_drag( return drag; } +/** + * Additional work to cleanly end dragging. Additional because this doesn't actually remove the + * drag items. Should be called whenever dragging is stopped (successful or not, also when + * canceled). + */ +void wm_drags_exit(wmWindowManager *wm, wmWindow *win) +{ + bool any_active = false; + LISTBASE_FOREACH (const wmDrag *, drag, &wm->drags) { + if (drag->active_dropbox) { + any_active = true; + break; + } + } + + /* If there is no active drop-box #wm_drags_check_ops() set a stop-cursor, which needs to be + * restored. */ + if (!any_active) { + WM_cursor_modal_restore(win); + /* Ensure the correct area cursor is restored. */ + win->tag_cursor_refresh = true; + WM_event_add_mousemove(win); + } +} + void WM_event_drag_image(wmDrag *drag, ImBuf *imb, float scale, int sx, int sy) { drag->imb = imb; @@ -197,18 +250,32 @@ void WM_drag_data_free(int dragtype, void *poin) /* Not too nice, could become a callback. */ if (dragtype == WM_DRAG_ASSET) { - wmDragAsset *asset_drag = poin; - MEM_freeN((void *)asset_drag->path); + wmDragAsset *asset_data = poin; + wm_drag_free_asset_data(&asset_data); + } + else { + MEM_freeN(poin); } - MEM_freeN(poin); } void WM_drag_free(wmDrag *drag) { + if (drag->active_dropbox && drag->active_dropbox->draw_deactivate) { + drag->active_dropbox->draw_deactivate(drag->active_dropbox, drag); + } if (drag->flags & WM_DRAG_FREE_DATA) { WM_drag_data_free(drag->type, drag->poin); } + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } BLI_freelistN(&drag->ids); + LISTBASE_FOREACH_MUTABLE (wmDragAssetListItem *, asset_item, &drag->asset_items) { + if (asset_item->is_external) { + wm_drag_free_asset_data(&asset_item->asset_data.external_info); + } + BLI_freelinkN(&drag->asset_items, asset_item); + } MEM_freeN(drag); } @@ -220,11 +287,11 @@ void WM_drag_free_list(struct ListBase *lb) } } -static char *dropbox_tooltip(bContext *C, wmDrag *drag, const wmEvent *event, wmDropBox *drop) +static char *dropbox_tooltip(bContext *C, wmDrag *drag, const int xy[2], wmDropBox *drop) { char *tooltip = NULL; if (drop->tooltip) { - tooltip = drop->tooltip(C, drag, event, drop); + tooltip = drop->tooltip(C, drag, xy, drop); } if (!tooltip) { tooltip = BLI_strdup(WM_operatortype_name(drop->ot, drop->ptr)); @@ -239,15 +306,35 @@ static wmDropBox *dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { + if (drag->free_disabled_info) { + MEM_SAFE_FREE(drag->disabled_info); + } + drag->disabled_info = NULL; + LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_DROPBOX) { wmEventHandler_Dropbox *handler = (wmEventHandler_Dropbox *)handler_base; if (handler->dropboxes) { LISTBASE_FOREACH (wmDropBox *, drop, handler->dropboxes) { - if (drop->poll(C, drag, event) && - WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + if (!drop->poll(C, drag, event)) { + /* If the drop's poll fails, don't set the disabled-info. This would be too aggressive. + * Instead show it only if the drop box could be used in principle, but the operator + * can't be executed. */ + continue; + } + + if (WM_operator_poll_context(C, drop->ot, drop->opcontext)) { return drop; } + + /* Attempt to set the disabled hint when the poll fails. Will always be the last hint set + * when there are multiple failing polls (could allow multiple disabled-hints too). */ + bool free_disabled_info = false; + const char *disabled_hint = CTX_wm_operator_poll_msg_get(C, &free_disabled_info); + if (disabled_hint) { + drag->disabled_info = disabled_hint; + drag->free_disabled_info = free_disabled_info; + } } } } @@ -256,7 +343,7 @@ static wmDropBox *dropbox_active(bContext *C, } /* return active operator tooltip/name when mouse is in box */ -static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) +static wmDropBox *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); wmDropBox *drop = dropbox_active(C, &win->handlers, drag, event); @@ -268,50 +355,68 @@ static char *wm_dropbox_active(bContext *C, wmDrag *drag, const wmEvent *event) ARegion *region = CTX_wm_region(C); drop = dropbox_active(C, ®ion->handlers, drag, event); } - if (drop) { - return dropbox_tooltip(C, drag, event, drop); - } - return NULL; + return drop; } -static void wm_drop_operator_options(bContext *C, wmDrag *drag, const wmEvent *event) +/** + * Update dropping information for the current mouse position in \a event. + */ +static void wm_drop_update_active(bContext *C, wmDrag *drag, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); /* for multiwin drags, we only do this if mouse inside */ - if (event->x < 0 || event->y < 0 || event->x > winsize_x || event->y > winsize_y) { + if (event->xy[0] < 0 || event->xy[1] < 0 || event->xy[0] > winsize_x || + event->xy[1] > winsize_y) { return; } - drag->tooltip[0] = 0; - - /* check buttons (XXX todo rna and value) */ - if (UI_but_active_drop_name(C)) { - BLI_strncpy(drag->tooltip, IFACE_("Paste name"), sizeof(drag->tooltip)); - } - else { - char *tooltip = wm_dropbox_active(C, drag, event); - - if (tooltip) { - BLI_strncpy(drag->tooltip, tooltip, sizeof(drag->tooltip)); - MEM_freeN(tooltip); - // WM_cursor_modal_set(win, WM_CURSOR_COPY); + wmDropBox *drop_prev = drag->active_dropbox; + wmDropBox *drop = wm_dropbox_active(C, drag, event); + if (drop != drop_prev) { + if (drop_prev && drop_prev->draw_deactivate) { + drop_prev->draw_deactivate(drop_prev, drag); + BLI_assert(drop_prev->draw_data == NULL); } - // else - // WM_cursor_modal_restore(win); - /* unsure about cursor type, feels to be too much */ + if (drop && drop->draw_activate) { + drop->draw_activate(drop, drag); + } + drag->active_dropbox = drop; } } +void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop) +{ + /* Optionally copy drag information to operator properties. Don't call it if the + * operator fails anyway, it might do more than just set properties (e.g. + * typically import an asset). */ + if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { + drop->copy(drag, drop); + } + + wm_drags_exit(CTX_wm_manager(C), CTX_wm_window(C)); +} + /* called in inner handler loop, region context */ void wm_drags_check_ops(bContext *C, const wmEvent *event) { wmWindowManager *wm = CTX_wm_manager(C); + bool any_active = false; LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { - wm_drop_operator_options(C, drag, event); + wm_drop_update_active(C, drag, event); + + if (drag->active_dropbox) { + any_active = true; + } + } + + /* Change the cursor to display that dropping isn't possible here. But only if there is something + * being dragged actually. Cursor will be restored in #wm_drags_exit(). */ + if (!BLI_listbase_is_empty(&wm->drags)) { + WM_cursor_modal_set(CTX_wm_window(C), any_active ? WM_CURSOR_DEFAULT : WM_CURSOR_STOP); } } @@ -373,6 +478,31 @@ bool WM_drag_is_ID_type(const wmDrag *drag, int idcode) return WM_drag_get_local_ID(drag, idcode) || WM_drag_get_asset_data(drag, idcode); } +/** + * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + */ +wmDragAsset *WM_drag_create_asset_data(const AssetHandle *asset, + AssetMetaData *metadata, + const char *path, + int import_type) +{ + wmDragAsset *asset_drag = MEM_mallocN(sizeof(*asset_drag), "wmDragAsset"); + + BLI_strncpy(asset_drag->name, ED_asset_handle_get_name(asset), sizeof(asset_drag->name)); + asset_drag->metadata = metadata; + asset_drag->path = path; + asset_drag->id_type = ED_asset_handle_get_id_type(asset); + asset_drag->import_type = import_type; + + return asset_drag; +} + +static void wm_drag_free_asset_data(wmDragAsset **asset_data) +{ + MEM_freeN((char *)(*asset_data)->path); + MEM_SAFE_FREE(*asset_data); +} + wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) { if (drag->type != WM_DRAG_ASSET) { @@ -383,28 +513,90 @@ wmDragAsset *WM_drag_get_asset_data(const wmDrag *drag, int idcode) return (ELEM(idcode, 0, asset_drag->id_type)) ? asset_drag : NULL; } -static ID *wm_drag_asset_id_import(wmDragAsset *asset_drag) +struct AssetMetaData *WM_drag_get_asset_meta_data(const wmDrag *drag, int idcode) { + wmDragAsset *drag_asset = WM_drag_get_asset_data(drag, idcode); + if (drag_asset) { + return drag_asset->metadata; + } + + ID *local_id = WM_drag_get_local_ID(drag, idcode); + if (local_id) { + return local_id->asset_data; + } + + return NULL; +} + +/** + * \param flag_extra: Additional linking flags (from #eFileSel_Params_Flag). + */ +ID *WM_drag_asset_id_import(wmDragAsset *asset_drag, const int flag_extra) +{ + /* Only support passing in limited flags. */ + BLI_assert(flag_extra == (flag_extra & FILE_AUTOSELECT)); + eFileSel_Params_Flag flag = flag_extra | FILE_ACTIVE_COLLECTION; + const char *name = asset_drag->name; ID_Type idtype = asset_drag->id_type; + /* FIXME: Link/Append should happens in the operator called at the end of drop process, not from + * here. */ + + Main *bmain = CTX_data_main(asset_drag->evil_C); + Scene *scene = CTX_data_scene(asset_drag->evil_C); + ViewLayer *view_layer = CTX_data_view_layer(asset_drag->evil_C); + View3D *view3d = CTX_wm_view3d(asset_drag->evil_C); + switch ((eFileAssetImportType)asset_drag->import_type) { case FILE_ASSET_IMPORT_LINK: - return WM_file_link_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name); + return WM_file_link_datablock( + bmain, scene, view_layer, view3d, asset_drag->path, idtype, name, flag); case FILE_ASSET_IMPORT_APPEND: - return WM_file_append_datablock(G_MAIN, NULL, NULL, NULL, asset_drag->path, idtype, name); + return WM_file_append_datablock(bmain, + scene, + view_layer, + view3d, + asset_drag->path, + idtype, + name, + flag | BLO_LIBLINK_APPEND_RECURSIVE | + BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR); + case FILE_ASSET_IMPORT_APPEND_REUSE: + return WM_file_append_datablock(G_MAIN, + scene, + view_layer, + view3d, + asset_drag->path, + idtype, + name, + flag | BLO_LIBLINK_APPEND_RECURSIVE | + BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR | + BLO_LIBLINK_APPEND_LOCAL_ID_REUSE); } BLI_assert_unreachable(); return NULL; } +bool WM_drag_asset_will_import_linked(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET) { + return false; + } + + const wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + return asset_drag->import_type == FILE_ASSET_IMPORT_LINK; +} + /** * When dragging a local ID, return that. Otherwise, if dragging an asset-handle, link or append * that depending on what was chosen by the drag-box (currently append only in fact). * * Use #WM_drag_free_imported_drag_ID() as cancel callback of the drop-box, so that the asset * import is rolled back if the drop operator fails. + * + * \param flag: #eFileSel_Params_Flag passed to linking code. */ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) { @@ -422,11 +614,11 @@ ID *WM_drag_get_local_ID_or_import_from_asset(const wmDrag *drag, int idcode) } /* Link/append the asset. */ - return wm_drag_asset_id_import(asset_drag); + return WM_drag_asset_id_import(asset_drag, 0); } /** - * \brief Free asset ID imported for cancelled drop. + * \brief Free asset ID imported for canceled drop. * * If the asset was imported (linked/appended) using #WM_drag_get_local_ID_or_import_from_asset()` * (typically via a #wmDropBox.copy() callback), we want the ID to be removed again if the drop @@ -458,17 +650,86 @@ void WM_drag_free_imported_drag_ID(struct Main *bmain, wmDrag *drag, wmDropBox * } } +wmDragAssetCatalog *WM_drag_get_asset_catalog_data(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET_CATALOG) { + return NULL; + } + + return drag->poin; +} + +/** + * \note: Does not store \a asset in any way, so it's fine to pass a temporary. + */ +void WM_drag_add_asset_list_item( + wmDrag *drag, + /* Context only needed for the hack in #ED_asset_handle_get_full_library_path(). */ + const bContext *C, + const AssetLibraryReference *asset_library_ref, + const AssetHandle *asset) +{ + if (drag->type != WM_DRAG_ASSET_LIST) { + return; + } + + /* No guarantee that the same asset isn't added twice. */ + + /* Add to list. */ + wmDragAssetListItem *drag_asset = MEM_callocN(sizeof(*drag_asset), __func__); + ID *local_id = ED_asset_handle_get_local_id(asset); + if (local_id) { + drag_asset->is_external = false; + drag_asset->asset_data.local_id = local_id; + } + else { + AssetMetaData *metadata = ED_asset_handle_get_metadata(asset); + char asset_blend_path[FILE_MAX_LIBEXTRA]; + ED_asset_handle_get_full_library_path(C, asset_library_ref, asset, asset_blend_path); + drag_asset->is_external = true; + drag_asset->asset_data.external_info = WM_drag_create_asset_data( + asset, metadata, BLI_strdup(asset_blend_path), FILE_ASSET_IMPORT_APPEND); + } + BLI_addtail(&drag->asset_items, drag_asset); +} + +const ListBase *WM_drag_asset_list_get(const wmDrag *drag) +{ + if (drag->type != WM_DRAG_ASSET_LIST) { + return NULL; + } + + return &drag->asset_items; +} + /* ************** draw ***************** */ static void wm_drop_operator_draw(const char *name, int x, int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - const float col_fg[4] = {1.0f, 1.0f, 1.0f, 1.0f}; - const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + + /* Use the theme settings from tooltips. */ + const bTheme *btheme = UI_GetTheme(); + const uiWidgetColors *wcol = &btheme->tui.wcol_tooltip; + + float col_fg[4], col_bg[4]; + rgba_uchar_to_float(col_fg, wcol->text); + rgba_uchar_to_float(col_bg, wcol->inner); UI_fontstyle_draw_simple_backdrop(fstyle, x, y, name, col_fg, col_bg); } +static void wm_drop_redalert_draw(const char *redalert_str, int x, int y) +{ + const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; + const float col_bg[4] = {0.0f, 0.0f, 0.0f, 0.2f}; + float col_fg[4]; + + UI_GetThemeColor4fv(TH_REDALERT, col_fg); + + UI_fontstyle_draw_simple_backdrop(fstyle, x, y, redalert_str, col_fg, col_bg); +} + const char *WM_drag_get_item_name(wmDrag *drag) { switch (drag->type) { @@ -495,132 +756,172 @@ const char *WM_drag_get_item_name(wmDrag *drag) return ""; } -static void drag_rect_minmax(rcti *rect, int x1, int y1, int x2, int y2) +static void wm_drag_draw_icon(bContext *UNUSED(C), + wmWindow *UNUSED(win), + wmDrag *drag, + const int xy[2]) { - if (rect->xmin > x1) { - rect->xmin = x1; + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; + y = xy[1] - drag->sy / 2; + + float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ + IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); + immDrawPixelsTexScaled(&state, + x, + y, + drag->imb->x, + drag->imb->y, + GPU_RGBA8, + false, + drag->imb->rect, + drag->scale, + drag->scale, + 1.0f, + 1.0f, + col); } - if (rect->xmax < x2) { - rect->xmax = x2; - } - if (rect->ymin > y1) { - rect->ymin = y1; - } - if (rect->ymax < y2) { - rect->ymax = y2; + else { + int padding = 4 * UI_DPI_FAC; + x = xy[0] - 2 * padding; + y = xy[1] - 2 * UI_DPI_FAC; + + const uchar text_col[] = {255, 255, 255, 255}; + UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); } } -/* called in wm_draw.c */ -/* if rect set, do not draw */ -void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect) +static void wm_drag_draw_item_name(wmDrag *drag, const int x, const int y) { const uiFontStyle *fstyle = UI_FSTYLE_WIDGET; - wmWindowManager *wm = CTX_wm_manager(C); - const int winsize_y = WM_window_pixels_y(win); + const uchar text_col[] = {255, 255, 255, 255}; + UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); +} - int cursorx = win->eventstate->x; - int cursory = win->eventstate->y; - if (rect) { - rect->xmin = rect->xmax = cursorx; - rect->ymin = rect->ymax = cursory; +void WM_drag_draw_item_name_fn(bContext *UNUSED(C), + wmWindow *UNUSED(win), + wmDrag *drag, + const int xy[2]) +{ + int x = xy[0] + 10 * UI_DPI_FAC; + int y = xy[1] + 1 * UI_DPI_FAC; + + wm_drag_draw_item_name(drag, x, y); +} + +static void wm_drag_draw_tooltip(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + if (!CTX_wm_region(C)) { + /* Some callbacks require the region. */ + return; } + int iconsize = UI_DPI_ICON_SIZE; + int padding = 4 * UI_DPI_FAC; + + char *tooltip = NULL; + if (drag->active_dropbox) { + tooltip = dropbox_tooltip(C, drag, xy, drag->active_dropbox); + } + + if (!tooltip && !drag->disabled_info) { + return; + } + + const int winsize_y = WM_window_pixels_y(win); + int x, y; + if (drag->imb) { + x = xy[0] - drag->sx / 2; + + if (xy[1] + drag->sy / 2 + padding + iconsize < winsize_y) { + y = xy[1] + drag->sy / 2 + padding; + } + else { + y = xy[1] - drag->sy / 2 - padding - iconsize - padding - iconsize; + } + } + else { + x = xy[0] - 2 * padding; + + if (xy[1] + iconsize + iconsize < winsize_y) { + y = (xy[1] + iconsize) + padding; + } + else { + y = (xy[1] - iconsize) - padding; + } + } + + if (tooltip) { + wm_drop_operator_draw(tooltip, x, y); + MEM_freeN(tooltip); + } + else if (drag->disabled_info) { + wm_drop_redalert_draw(drag->disabled_info, x, y); + } +} + +static void wm_drag_draw_default(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + int xy_tmp[2] = {UNPACK2(xy)}; + + /* Image or icon. */ + wm_drag_draw_icon(C, win, drag, xy_tmp); + + /* Item name. */ + if (drag->imb) { + int iconsize = UI_DPI_ICON_SIZE; + xy_tmp[0] = xy[0] - (drag->sx / 2); + xy_tmp[1] = xy[1] - (drag->sy / 2) - iconsize; + } + else { + xy_tmp[0] = xy[0] + 10 * UI_DPI_FAC; + xy_tmp[1] = xy[1] + 1 * UI_DPI_FAC; + } + wm_drag_draw_item_name(drag, UNPACK2(xy_tmp)); + + /* Operator name with roundbox. */ + wm_drag_draw_tooltip(C, win, drag, xy); +} + +void WM_drag_draw_default_fn(bContext *C, wmWindow *win, wmDrag *drag, const int xy[2]) +{ + wm_drag_draw_default(C, win, drag, xy); +} + +/* Called in #wm_draw_window_onscreen. */ +void wm_drags_draw(bContext *C, wmWindow *win) +{ + int xy[2]; + if (ELEM(win->grabcursor, GHOST_kGrabWrap, GHOST_kGrabHide)) { + wm_cursor_position_get(win, &xy[0], &xy[1]); + } + else { + xy[0] = win->eventstate->xy[0]; + xy[1] = win->eventstate->xy[1]; + } + + bScreen *screen = CTX_wm_screen(C); + ScrArea *area = BKE_screen_find_area_xy(screen, SPACE_TYPE_ANY, UNPACK2(xy)); + ARegion *region = BKE_area_find_region_xy(area, RGN_TYPE_ANY, UNPACK2(xy)); + if (region) { + BLI_assert(!CTX_wm_area(C) && !CTX_wm_region(C)); + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + } + + wmWindowManager *wm = CTX_wm_manager(C); /* Should we support multi-line drag draws? Maybe not, more types mixed won't work well. */ GPU_blend(GPU_BLEND_ALPHA); LISTBASE_FOREACH (wmDrag *, drag, &wm->drags) { - const uchar text_col[] = {255, 255, 255, 255}; - int iconsize = UI_DPI_ICON_SIZE; - int padding = 4 * UI_DPI_FAC; - - /* image or icon */ - int x, y; - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2; - - if (rect) { - drag_rect_minmax(rect, x, y, x + drag->sx, y + drag->sy); - } - else { - float col[4] = {1.0f, 1.0f, 1.0f, 0.65f}; /* this blends texture */ - IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR); - immDrawPixelsTexScaled(&state, - x, - y, - drag->imb->x, - drag->imb->y, - GPU_RGBA8, - false, - drag->imb->rect, - drag->scale, - drag->scale, - 1.0f, - 1.0f, - col); - } - } - else { - x = cursorx - 2 * padding; - y = cursory - 2 * UI_DPI_FAC; - - if (rect) { - drag_rect_minmax(rect, x, y, x + iconsize, y + iconsize); - } - else { - UI_icon_draw_ex(x, y, drag->icon, U.inv_dpi_fac, 0.8, 0.0f, text_col, false); - } + if (drag->active_dropbox && drag->active_dropbox->draw) { + drag->active_dropbox->draw(C, win, drag, xy); + continue; } - /* item name */ - if (drag->imb) { - x = cursorx - drag->sx / 2; - y = cursory - drag->sy / 2 - iconsize; - } - else { - x = cursorx + 10 * UI_DPI_FAC; - y = cursory + 1 * UI_DPI_FAC; - } - - if (rect) { - int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); - drag_rect_minmax(rect, x, y, x + w, y + iconsize); - } - else { - UI_fontstyle_draw_simple(fstyle, x, y, WM_drag_get_item_name(drag), text_col); - } - - /* operator name with roundbox */ - if (drag->tooltip[0]) { - if (drag->imb) { - x = cursorx - drag->sx / 2; - - if (cursory + drag->sy / 2 + padding + iconsize < winsize_y) { - y = cursory + drag->sy / 2 + padding; - } - else { - y = cursory - drag->sy / 2 - padding - iconsize - padding - iconsize; - } - } - else { - x = cursorx - 2 * padding; - - if (cursory + iconsize + iconsize < winsize_y) { - y = (cursory + iconsize) + padding; - } - else { - y = (cursory - iconsize) - padding; - } - } - - if (rect) { - int w = UI_fontstyle_string_width(fstyle, WM_drag_get_item_name(drag)); - drag_rect_minmax(rect, x, y, x + w, y + iconsize); - } - else { - wm_drop_operator_draw(drag->tooltip, x, y); - } - } + wm_drag_draw_default(C, win, drag, xy); } GPU_blend(GPU_BLEND_NONE); + CTX_wm_area_set(C, NULL); + CTX_wm_region_set(C, NULL); } diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 328950cf8f9..8acce240046 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -121,7 +121,7 @@ static void wm_paintcursor_draw(bContext *C, ScrArea *area, ARegion *region) pc->draw(C, x, y, pc->customdata); } else { - pc->draw(C, win->eventstate->x, win->eventstate->y, pc->customdata); + pc->draw(C, win->eventstate->xy[0], win->eventstate->xy[1], pc->customdata); } GPU_scissor_test(false); @@ -856,9 +856,9 @@ static void wm_draw_window_onscreen(bContext *C, wmWindow *win, int view) wm_gesture_draw(win); } - /* needs pixel coords in screen */ + /* Needs pixel coords in screen. */ if (wm->drags.first) { - wm_drags_draw(C, win, NULL); + wm_drags_draw(C, win); } GPU_debug_group_end(); diff --git a/source/blender/windowmanager/intern/wm_event_query.c b/source/blender/windowmanager/intern/wm_event_query.c index e22285214f0..ef733837bf7 100644 --- a/source/blender/windowmanager/intern/wm_event_query.c +++ b/source/blender/windowmanager/intern/wm_event_query.c @@ -82,7 +82,7 @@ void WM_event_print(const wmEvent *event) const char *prev_val_id = unknown; event_ids_from_type_and_value(event->type, event->val, &type_id, &val_id); - event_ids_from_type_and_value(event->prevtype, event->prevval, &prev_type_id, &prev_val_id); + event_ids_from_type_and_value(event->prev_type, event->prev_val, &prev_type_id, &prev_val_id); printf( "wmEvent type:%d / %s, val:%d / %s,\n" @@ -93,9 +93,9 @@ void WM_event_print(const wmEvent *event) type_id, event->val, val_id, - event->prevtype, + event->prev_type, prev_type_id, - event->prevval, + event->prev_val, prev_val_id, event->shift, event->ctrl, @@ -103,8 +103,8 @@ void WM_event_print(const wmEvent *event) event->oskey, event->keymodifier, event->is_repeat, - event->x, - event->y, + event->xy[0], + event->xy[1], event->ascii, BLI_str_utf8_size(event->utf8_buf), event->utf8_buf, @@ -270,6 +270,34 @@ bool WM_event_is_mouse_drag(const wmEvent *event) return ISTWEAK(event->type) || (ISMOUSE_BUTTON(event->type) && (event->val == KM_CLICK_DRAG)); } +bool WM_event_is_mouse_drag_or_press(const wmEvent *event) +{ + return WM_event_is_mouse_drag(event) || + (ISMOUSE_BUTTON(event->type) && (event->val == KM_PRESS)); +} + +/** + * Detect motion between selection (callers should only use this for selection picking), + * typically mouse press/click events. + * + * \param mval: Region relative coordinates, call with (-1, -1) resets the last cursor location. + * \returns True when there was motion since last called. + * + * NOTE(@campbellbarton): The logic used here isn't foolproof. + * It's possible that users move the cursor past #WM_EVENT_CURSOR_MOTION_THRESHOLD then back to + * a position within the threshold (between mouse clicks). + * In practice users never reported this since the threshold is very small (a few pixels). + * To prevent the unlikely case of values matching from another region, + * changing regions resets this value to (-1, -1). + */ +bool WM_cursor_test_motion_and_update(const int mval[2]) +{ + static int mval_prev[2] = {-1, -1}; + bool use_cycle = (len_manhattan_v2v2_int(mval, mval_prev) <= WM_EVENT_CURSOR_MOTION_THRESHOLD); + copy_v2_v2_int(mval_prev, mval); + return !use_cycle; +} + /** \} */ /* -------------------------------------------------------------------- */ @@ -282,8 +310,8 @@ bool WM_event_is_mouse_drag(const wmEvent *event) int WM_event_drag_threshold(const struct wmEvent *event) { int drag_threshold; - if (ISMOUSE(event->prevtype)) { - BLI_assert(event->prevtype != MOUSEMOVE); + if (ISMOUSE(event->prev_type)) { + BLI_assert(event->prev_type != MOUSEMOVE); /* Using the previous type is important is we want to check the last pressed/released button, * The `event->type` would include #MOUSEMOVE which is always the case when dragging * and does not help us know which threshold to use. */ @@ -309,10 +337,8 @@ bool WM_event_drag_test_with_delta(const wmEvent *event, const int drag_delta[2] bool WM_event_drag_test(const wmEvent *event, const int prev_xy[2]) { - const int drag_delta[2] = { - prev_xy[0] - event->x, - prev_xy[1] - event->y, - }; + int drag_delta[2]; + sub_v2_v2v2_int(drag_delta, prev_xy, event->xy); return WM_event_drag_test_with_delta(event, drag_delta); } @@ -404,6 +430,19 @@ void WM_event_ndof_to_quat(const struct wmNDOFMotionData *ndof, float q[4]) /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Event XR Input Access + * \{ */ + +#ifdef WITH_XR_OPENXR +bool WM_event_is_xr(const struct wmEvent *event) +{ + return (event->type == EVT_XR_ACTION && event->custom == EVT_DATA_XR); +} +#endif + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Event Tablet Input Access * \{ */ @@ -457,7 +496,7 @@ bool WM_event_is_tablet(const struct wmEvent *event) int WM_event_absolute_delta_x(const struct wmEvent *event) { - int dx = event->x - event->prevx; + int dx = event->xy[0] - event->prev_xy[0]; if (!event->is_direction_inverted) { dx = -dx; @@ -468,7 +507,7 @@ int WM_event_absolute_delta_x(const struct wmEvent *event) int WM_event_absolute_delta_y(const struct wmEvent *event) { - int dy = event->y - event->prevy; + int dy = event->xy[1] - event->prev_xy[1]; if (!event->is_direction_inverted) { dy = -dy; diff --git a/source/blender/windowmanager/intern/wm_event_system.c b/source/blender/windowmanager/intern/wm_event_system.c index ae09786356a..798f60fba3d 100644 --- a/source/blender/windowmanager/intern/wm_event_system.c +++ b/source/blender/windowmanager/intern/wm_event_system.c @@ -152,25 +152,22 @@ wmEvent *WM_event_add_simulate(wmWindow *win, const wmEvent *event_to_add) /* Logic for setting previous value is documented on the #wmEvent struct, * see #wm_event_add_ghostevent for the implementation of logic this follows. */ - - win->eventstate->x = event->x; - win->eventstate->y = event->y; + copy_v2_v2_int(win->eventstate->xy, event->xy); if (event->type == MOUSEMOVE) { - win->eventstate->prevx = event->prevx = win->eventstate->x; - win->eventstate->prevy = event->prevy = win->eventstate->y; + copy_v2_v2_int(win->eventstate->prev_xy, win->eventstate->xy); + copy_v2_v2_int(event->prev_xy, win->eventstate->xy); } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { - win->eventstate->prevval = event->prevval = win->eventstate->val; - win->eventstate->prevtype = event->prevtype = win->eventstate->type; + win->eventstate->prev_val = event->prev_val = win->eventstate->val; + win->eventstate->prev_type = event->prev_type = win->eventstate->type; win->eventstate->val = event->val; win->eventstate->type = event->type; if (event->val == KM_PRESS) { if (event->is_repeat == false) { - win->eventstate->prevclickx = event->x; - win->eventstate->prevclicky = event->y; + copy_v2_v2_int(win->eventstate->prev_click_xy, event->xy); } } } @@ -189,7 +186,7 @@ void wm_event_free(wmEvent *event) #endif if (event->customdata) { - if (event->customdatafree) { + if (event->customdata_free) { /* NOTE: pointer to listbase struct elsewhere. */ if (event->custom == EVT_DATA_DRAGDROP) { ListBase *lb = event->customdata; @@ -505,8 +502,7 @@ void wm_event_do_notifiers(bContext *C) } } - if (note->window == win || - (note->window == NULL && (note->reference == NULL || note->reference == scene))) { + if (note->window == win || (note->window == NULL && (ELEM(note->reference, NULL, scene)))) { if (note->category == NC_SCENE) { if (note->data == ND_FRAME) { do_anim = true; @@ -581,7 +577,7 @@ void wm_event_do_notifiers(bContext *C) if ((note->category == NC_SPACE) && note->reference) { /* Filter out notifiers sent to other spaces. RNA sets the reference to the owning ID * though, the screen, so let notifiers through that reference the entire screen. */ - if (!ELEM(note->reference, area->spacedata.first, screen)) { + if (!ELEM(note->reference, area->spacedata.first, screen, scene)) { continue; } } @@ -804,7 +800,7 @@ static void wm_add_reports(ReportList *reports) } } -void WM_report(ReportType type, const char *message) +void WM_report(eReportType type, const char *message) { ReportList reports; BKE_reports_init(&reports, RPT_STORE); @@ -815,7 +811,7 @@ void WM_report(ReportType type, const char *message) BKE_reports_clear(&reports); } -void WM_reportf(ReportType type, const char *format, ...) +void WM_reportf(eReportType type, const char *format, ...) { va_list args; @@ -1286,8 +1282,8 @@ static void wm_region_mouse_co(bContext *C, wmEvent *event) ARegion *region = CTX_wm_region(C); if (region) { /* Compatibility convention. */ - event->mval[0] = event->x - region->winrct.xmin; - event->mval[1] = event->y - region->winrct.ymin; + event->mval[0] = event->xy[0] - region->winrct.xmin; + event->mval[1] = event->xy[1] - region->winrct.ymin; } else { /* These values are invalid (avoid odd behavior by relying on old mval values). */ @@ -1423,10 +1419,10 @@ static int wm_operator_invoke(bContext *C, } if (region && region->regiontype == RGN_TYPE_WINDOW && - BLI_rcti_isect_pt_v(®ion->winrct, &event->x)) { + BLI_rcti_isect_pt_v(®ion->winrct, event->xy)) { winrect = ®ion->winrct; } - else if (area && BLI_rcti_isect_pt_v(&area->totrct, &event->x)) { + else if (area && BLI_rcti_isect_pt_v(&area->totrct, event->xy)) { winrect = &area->totrct; } @@ -1790,7 +1786,10 @@ void WM_operator_name_call_ptr_with_depends_on_cursor( } wmWindow *win = CTX_wm_window(C); - ScrArea *area = CTX_wm_area(C); + /* The operator context is applied when the operator is called, + * the check for the area needs to be explicitly limited here. + * Useful so it's possible to screen-shot an area without drawing into it's header. */ + ScrArea *area = WM_OP_CONTEXT_HAS_AREA(opcontext) ? CTX_wm_area(C) : NULL; { char header_text[UI_MAX_DRAW_STR]; @@ -1806,7 +1805,7 @@ void WM_operator_name_call_ptr_with_depends_on_cursor( } } - WM_cursor_modal_set(win, WM_CURSOR_PICK_AREA); + WM_cursor_modal_set(win, ot->cursor_pending); uiOperatorWaitForInput *opwait = MEM_callocN(sizeof(*opwait), __func__); opwait->optype_params.optype = ot; @@ -1891,7 +1890,8 @@ static void wm_handler_op_context(bContext *C, wmEventHandler_Op *handler, const CTX_wm_area_set(C, area); if (op && (op->flag & OP_IS_MODAL_CURSOR_REGION)) { - region = BKE_area_find_region_xy(area, handler->context.region_type, event->x, event->y); + region = BKE_area_find_region_xy( + area, handler->context.region_type, event->xy[0], event->xy[1]); if (region) { handler->context.region = region; } @@ -2106,8 +2106,8 @@ static wmKeyMapItem *wm_eventmatch_modal_keymap_items(const wmKeyMap *keymap, } struct wmEvent_ModalMapStore { - short prevtype; - short prevval; + short prev_type; + short prev_val; bool dbl_click_disabled; }; @@ -2155,19 +2155,19 @@ static void wm_event_modalkeymap_begin(const bContext *C, } if (event_match != NULL) { - event_backup->prevtype = event->prevtype; - event_backup->prevval = event->prevval; + event_backup->prev_type = event->prev_type; + event_backup->prev_val = event->prev_val; - event->prevtype = event_match->type; - event->prevval = event_match->val; + event->prev_type = event_match->type; + event->prev_val = event_match->val; event->type = EVT_MODAL_MAP; event->val = kmi->propvalue; /* Avoid double-click events even in the case of 'EVT_MODAL_MAP', * since it's possible users configure double-click keymap items * which would break when modal functions expect press/release. */ - if (event->prevtype == KM_DBL_CLICK) { - event->prevtype = KM_PRESS; + if (event->prev_type == KM_DBL_CLICK) { + event->prev_type = KM_PRESS; event_backup->dbl_click_disabled = true; } } @@ -2193,11 +2193,11 @@ static void wm_event_modalkeymap_end(wmEvent *event, const struct wmEvent_ModalMapStore *event_backup) { if (event->type == EVT_MODAL_MAP) { - event->type = event->prevtype; - event->val = event->prevval; + event->type = event->prev_type; + event->val = event->prev_val; - event->prevtype = event_backup->prevtype; - event->prevval = event_backup->prevval; + event->prev_type = event_backup->prev_type; + event->prev_val = event_backup->prev_val; } if (event_backup->dbl_click_disabled) { @@ -2489,7 +2489,8 @@ static int wm_handler_fileselect_do(bContext *C, wm_window_make_drawable(wm, ctx_win); /* Ensure correct cursor position, otherwise, popups may close immediately after * opening (UI_BLOCK_MOVEMOUSE_QUIT). */ - wm_cursor_position_get(ctx_win, &ctx_win->eventstate->x, &ctx_win->eventstate->y); + wm_cursor_position_get( + ctx_win, &ctx_win->eventstate->xy[0], &ctx_win->eventstate->xy[1]); wm->winactive = ctx_win; /* Reports use this... */ if (handler->context.win == win) { handler->context.win = NULL; @@ -2665,7 +2666,34 @@ static int wm_handlers_do_keymap_with_keymap_handler( if (wm_eventmatch(event, kmi)) { struct wmEventHandler_KeymapPost keymap_post = handler->post; - PRINT("%s: item matched '%s'\n", __func__, kmi->idname); + if (do_debug_handler) { + /* Short representation of the key that was pressed, + * include this since it may differ from the event in minor details + * which can help looking up the key-map definition. */ + char kmi_buf[256]; + WM_keymap_item_to_string(kmi, false, kmi_buf, sizeof(kmi_buf)); + + /* The key-map item properties can further help distinguish this item from others. */ + char *kmi_props = NULL; + if (kmi->properties != NULL) { + wmOperatorType *ot = WM_operatortype_find(kmi->idname, 0); + if (ot) { + kmi_props = RNA_pointer_as_string_keywords(C, kmi->ptr, false, false, true, 512); + } + else { /* Fallback. */ + kmi_props = IDP_reprN(kmi->properties, NULL); + } + } + + printf("%s: item matched: \"%s\", %s(%s)\n", + __func__, + kmi_buf, + kmi->idname, + kmi_props ? kmi_props : ""); + if (kmi_props != NULL) { + MEM_freeN(kmi_props); + } + } action |= wm_handler_operator_call( C, handlers, &handler->head, event, kmi->ptr, kmi->idname); @@ -2782,7 +2810,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, * noticeable for the node editor - where dragging on a node should move it, see: T73212. * note we still allow for starting the gizmo drag outside, then travel 'inside' the node. */ if (region->type->clip_gizmo_events_by_ui) { - if (UI_region_block_find_mouse_over(region, &event->x, true)) { + if (UI_region_block_find_mouse_over(region, event->xy, true)) { if (gz != NULL && event->type != EVT_GIZMO_UPDATE) { if (restore_highlight_unless_activated == false) { WM_tooltip_clear(C, CTX_wm_window(C)); @@ -2947,7 +2975,7 @@ static int wm_handlers_do_gizmo_handler(bContext *C, /** \name Handle Single Event (All Handler Types) * \{ */ -static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers) +static int wm_handlers_do_intern(bContext *C, wmWindow *win, wmEvent *event, ListBase *handlers) { const bool do_debug_handler = (G.debug & G_DEBUG_HANDLERS) && @@ -2989,9 +3017,18 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* Handle all types here. */ if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - action |= wm_handlers_do_keymap_with_keymap_handler( - C, event, handlers, handler, keymap, do_debug_handler); + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); + int action_iter = WM_HANDLER_CONTINUE; + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + action_iter |= wm_handlers_do_keymap_with_keymap_handler( + C, event, handlers, handler, keymap, do_debug_handler); + if (action_iter & WM_HANDLER_BREAK) { + break; + } + } + action |= action_iter; /* Clear the tool-tip whenever a key binding is handled, without this tool-tips * are kept when a modal operators starts (annoying but otherwise harmless). */ @@ -3016,18 +3053,14 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* Other drop custom types allowed. */ if (event->custom == EVT_DATA_DRAGDROP) { ListBase *lb = (ListBase *)event->customdata; - LISTBASE_FOREACH (wmDrag *, drag, lb) { + LISTBASE_FOREACH_MUTABLE (wmDrag *, drag, lb) { if (drop->poll(C, drag, event)) { - /* Optionally copy drag information to operator properties. Don't call it if the - * operator fails anyway, it might do more than just set properties (e.g. - * typically import an asset). */ - if (drop->copy && WM_operator_poll_context(C, drop->ot, drop->opcontext)) { - drop->copy(drag, drop); - } + wm_drop_prepare(C, drag, drop); /* Pass single matched wmDrag onto the operator. */ BLI_remlink(lb, drag); - ListBase single_lb = {drag, drag}; + ListBase single_lb = {0}; + BLI_addtail(&single_lb, drag); event->customdata = &single_lb; int op_retval = wm_operator_call_internal( @@ -3056,6 +3089,8 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers break; } } + /* Always exit all drags on a drop event, even if poll didn't succeed. */ + wm_drags_exit(wm, win); } } } @@ -3121,7 +3156,7 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers /* This calls handlers twice - to solve (double-)click events. */ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) { - int action = wm_handlers_do_intern(C, event, handlers); + int action = wm_handlers_do_intern(C, CTX_wm_window(C), event, handlers); /* Will be NULL in the file read case. */ wmWindow *win = CTX_wm_window(C); @@ -3133,27 +3168,24 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) /* Test for CLICK_DRAG events. */ if (wm_action_not_handled(action)) { if (win->event_queue_check_drag) { - if (WM_event_drag_test(event, &event->prevclickx)) { + if (WM_event_drag_test(event, event->prev_click_xy)) { win->event_queue_check_drag_handled = true; - int x = event->x; - int y = event->y; + int xy[2] = {UNPACK2(event->xy)}; short val = event->val; short type = event->type; - event->x = event->prevclickx; - event->y = event->prevclicky; + copy_v2_v2_int(event->xy, event->prev_click_xy); event->val = KM_CLICK_DRAG; - event->type = event->prevtype; + event->type = event->prev_type; CLOG_INFO(WM_LOG_HANDLERS, 1, "handling PRESS_DRAG"); - action |= wm_handlers_do_intern(C, event, handlers); + action |= wm_handlers_do_intern(C, win, event, handlers); event->val = val; event->type = type; - event->x = x; - event->y = y; + copy_v2_v2_int(event->xy, xy); win->event_queue_check_click = false; if (!wm_action_not_handled(action)) { @@ -3168,7 +3200,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) } } else if (ISMOUSE_BUTTON(event->type) || ISKEYBOARD(event->type)) { - /* All events that don't set wmEvent.prevtype must be ignored. */ + /* All events that don't set wmEvent.prev_type must be ignored. */ /* Test for CLICK events. */ if (wm_action_not_handled(action)) { @@ -3186,32 +3218,29 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) win->event_queue_check_drag = false; } - if (event->prevtype == event->type) { + if (event->prev_type == event->type) { if (event->val == KM_RELEASE) { - if (event->prevval == KM_PRESS) { + if (event->prev_val == KM_PRESS) { if (win->event_queue_check_click == true) { - if (WM_event_drag_test(event, &event->prevclickx)) { + if (WM_event_drag_test(event, event->prev_click_xy)) { win->event_queue_check_click = false; win->event_queue_check_drag = false; } else { /* Position is where the actual click happens, for more * accurate selecting in case the mouse drifts a little. */ - int x = event->x; - int y = event->y; + int xy[2] = {UNPACK2(event->xy)}; - event->x = event->prevclickx; - event->y = event->prevclicky; + copy_v2_v2_int(event->xy, event->prev_click_xy); event->val = KM_CLICK; CLOG_INFO(WM_LOG_HANDLERS, 1, "handling CLICK"); - action |= wm_handlers_do_intern(C, event, handlers); + action |= wm_handlers_do_intern(C, win, event, handlers); event->val = KM_RELEASE; - event->x = x; - event->y = y; + copy_v2_v2_int(event->xy, xy); } } } @@ -3219,7 +3248,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) else if (event->val == KM_DBL_CLICK) { /* The underlying event is a press, so try and handle this. */ event->val = KM_PRESS; - action |= wm_handlers_do_intern(C, event, handlers); + action |= wm_handlers_do_intern(C, win, event, handlers); /* revert value if not handled */ if (wm_action_not_handled(action)) { @@ -3240,7 +3269,7 @@ static int wm_handlers_do(bContext *C, wmEvent *event, ListBase *handlers) /* pass */ } else { - if (ISKEYMODIFIER(event->prevtype)) { + if (ISKEYMODIFIER(event->prev_type)) { win->event_queue_check_click = false; } } @@ -3262,7 +3291,7 @@ static bool wm_event_inside_rect(const wmEvent *event, const rcti *rect) if (wm_event_always_pass(event)) { return true; } - if (BLI_rcti_isect_pt_v(rect, &event->x)) { + if (BLI_rcti_isect_pt_v(rect, event->xy)) { return true; } return false; @@ -3273,7 +3302,7 @@ static bool wm_event_inside_region(const wmEvent *event, const ARegion *region) if (wm_event_always_pass(event)) { return true; } - return ED_region_contains_xy(region, &event->x); + return ED_region_contains_xy(region, event->xy); } static ScrArea *area_event_inside(bContext *C, const int xy[2]) @@ -3332,11 +3361,11 @@ static void wm_paintcursor_test(bContext *C, const wmEvent *event) } /* If previous position was not in current region, we have to set a temp new context. */ - if (region == NULL || !BLI_rcti_isect_pt_v(®ion->winrct, &event->prevx)) { + if (region == NULL || !BLI_rcti_isect_pt_v(®ion->winrct, event->prev_xy)) { ScrArea *area = CTX_wm_area(C); - CTX_wm_area_set(C, area_event_inside(C, &event->prevx)); - CTX_wm_region_set(C, region_event_inside(C, &event->prevx)); + CTX_wm_area_set(C, area_event_inside(C, event->prev_xy)); + CTX_wm_region_set(C, region_event_inside(C, event->prev_xy)); wm_paintcursor_tag(C, wm->paintcursors.first, CTX_wm_region(C)); @@ -3358,6 +3387,7 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv screen->do_draw_drag = true; } else if (event->type == EVT_ESCKEY) { + wm_drags_exit(wm, win); WM_drag_free_list(&wm->drags); screen->do_draw_drag = true; @@ -3367,14 +3397,14 @@ static void wm_event_drag_and_drop_test(wmWindowManager *wm, wmWindow *win, wmEv /* Create customdata, first free existing. */ if (event->customdata) { - if (event->customdatafree) { + if (event->customdata_free) { MEM_freeN(event->customdata); } } event->custom = EVT_DATA_DRAGDROP; event->customdata = &wm->drags; - event->customdatafree = 1; + event->customdata_free = true; /* Clear drop icon. */ screen->do_draw_drag = true; @@ -3424,6 +3454,70 @@ static void wm_event_free_and_remove_from_queue_if_valid(wmEvent *event) * Handle events for all windows, run from the #WM_main event loop. * \{ */ +#ifdef WITH_XR_OPENXR +/** + * Special handling for XR events. + * + * Although XR events are added to regular window queues, they are handled in an "off-screen area" + * context that is owned entirely by XR runtime data and not tied to a window. + */ +static void wm_event_handle_xrevent(bContext *C, + wmWindowManager *wm, + wmWindow *win, + wmEvent *event) +{ + ScrArea *area = WM_xr_session_area_get(&wm->xr); + if (!area) { + return; + } + BLI_assert(area->spacetype == SPACE_VIEW3D && area->spacedata.first); + + /* Find a valid region for XR operator execution and modal handling. */ + ARegion *region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); + if (!region) { + return; + } + BLI_assert(WM_region_use_viewport(area, region)); /* For operators using GPU-based selection. */ + + CTX_wm_area_set(C, area); + CTX_wm_region_set(C, region); + + int action = wm_handlers_do(C, event, &win->modalhandlers); + + if ((action & WM_HANDLER_BREAK) == 0) { + wmXrActionData *actiondata = event->customdata; + if (actiondata->ot->modal && event->val == KM_RELEASE) { + /* Don't execute modal operators on release. */ + } + else { + PointerRNA properties = {.type = actiondata->ot->srna, .data = actiondata->op_properties}; + if (actiondata->ot->invoke) { + /* Invoke operator, either executing operator or transferring responsibility to window + * modal handlers. */ + wm_operator_invoke(C, + actiondata->ot, + event, + actiondata->op_properties ? &properties : NULL, + NULL, + false, + false); + } + else { + /* Execute operator. */ + wmOperator *op = wm_operator_create( + wm, actiondata->ot, actiondata->op_properties ? &properties : NULL, NULL); + if ((WM_operator_call(C, op) & OPERATOR_HANDLED) == 0) { + WM_operator_free(op); + } + } + } + } + + CTX_wm_region_set(C, NULL); + CTX_wm_area_set(C, NULL); +} +#endif /* WITH_XR_OPENXR */ + /* Called in main loop. */ /* Goes over entire hierarchy: events -> window -> screen -> area -> region. */ void wm_event_do_handlers(bContext *C) @@ -3521,18 +3615,29 @@ void wm_event_do_handlers(bContext *C) CTX_wm_window_set(C, win); +#ifdef WITH_XR_OPENXR + if (event->type == EVT_XR_ACTION) { + wm_event_handle_xrevent(C, wm, win, event); + BLI_remlink(&win->event_queue, event); + wm_event_free(event); + /* Skip mouse event handling below, which is unnecessary for XR events. */ + continue; + } +#endif + /* Clear tool-tip on mouse move. */ if (screen->tool_tip && screen->tool_tip->exit_on_event) { if (ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)) { - if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, &event->x) > U.move_threshold) { + if (len_manhattan_v2v2_int(screen->tool_tip->event_xy, event->xy) > + WM_EVENT_CURSOR_MOTION_THRESHOLD) { WM_tooltip_clear(C, win); } } } /* We let modal handlers get active area/region, also wm_paintcursor_test needs it. */ - CTX_wm_area_set(C, area_event_inside(C, &event->x)); - CTX_wm_region_set(C, region_event_inside(C, &event->x)); + CTX_wm_area_set(C, area_event_inside(C, event->xy)); + CTX_wm_region_set(C, region_event_inside(C, event->xy)); /* MVC demands to not draw in event handlers... * but we need to leave it for ogl selecting etc. */ @@ -3569,7 +3674,7 @@ void wm_event_do_handlers(bContext *C) if (event->type == MOUSEMOVE) { /* State variables in screen, cursors. * Also used in wm_draw.c, fails for modal handlers though. */ - ED_screen_set_active_region(C, win, &event->x); + ED_screen_set_active_region(C, win, event->xy); /* For regions having custom cursors. */ wm_paintcursor_test(C, event); } @@ -3591,7 +3696,7 @@ void wm_event_do_handlers(bContext *C) /* Update azones if needed - done here because it needs to be independent from redraws. */ if (area->flag & AREA_FLAG_ACTIONZONES_UPDATE) { - ED_area_azones_update(area, &event->x); + ED_area_azones_update(area, event->xy); } if (wm_event_inside_rect(event, &area->totrct)) { @@ -3644,8 +3749,8 @@ void wm_event_do_handlers(bContext *C) if ((action & WM_HANDLER_BREAK) == 0) { /* Also some non-modal handlers need active area/region. */ - CTX_wm_area_set(C, area_event_inside(C, &event->x)); - CTX_wm_region_set(C, region_event_inside(C, &event->x)); + CTX_wm_area_set(C, area_event_inside(C, event->xy)); + CTX_wm_region_set(C, region_event_inside(C, event->xy)); wm_region_mouse_co(C, event); @@ -3674,8 +3779,7 @@ void wm_event_do_handlers(bContext *C) } /* Update previous mouse position for following events to use. */ - win->eventstate->prevx = event->x; - win->eventstate->prevy = event->y; + copy_v2_v2_int(win->eventstate->prev_xy, event->xy); /* Unlink and free here, blender-quit then frees all. */ BLI_remlink(&win->event_queue, event); @@ -3685,10 +3789,10 @@ void wm_event_do_handlers(bContext *C) /* Only add mouse-move when the event queue was read entirely. */ if (win->addmousemove && win->eventstate) { wmEvent tevent = *(win->eventstate); - // printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y); + // printf("adding MOUSEMOVE %d %d\n", tevent.xy[0], tevent.xy[1]); tevent.type = MOUSEMOVE; - tevent.prevx = tevent.x; - tevent.prevy = tevent.y; + tevent.prev_xy[0] = tevent.xy[0]; + tevent.prev_xy[1] = tevent.xy[1]; tevent.is_repeat = false; wm_event_add(win, &tevent); win->addmousemove = 0; @@ -3723,7 +3827,7 @@ void WM_event_fileselect_event(wmWindowManager *wm, void *ophandle, int eventval } /* Operator is supposed to have a filled "path" property. */ -/* Optional property: filetype (XXX enum?) */ +/* Optional property: file-type (XXX enum?) */ /** * The idea here is to keep a handler alive on window queue, owning the operator. @@ -3905,17 +4009,46 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler(ListBase *handlers, wmKeyMap * * Follow #wmEventHandler_KeymapDynamicFn signature. */ -wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, - wmEventHandler_Keymap *handler) +static void wm_event_get_keymap_from_toolsystem_ex(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result, + /* Extra arguments. */ + const bool with_gizmos) { + memset(km_result, 0x0, sizeof(*km_result)); + + const char *keymap_id_list[ARRAY_SIZE(km_result->keymaps)]; + int keymap_id_list_len = 0; + + /* NOTE(@campbellbarton): If `win` is NULL, this function may not behave as expected. + * Assert since this should not happen in practice. + * If it does, the window could be looked up in `wm` using the `area`. + * Keep NULL checks in run-time code since any crashes here are difficult to redo. */ + BLI_assert_msg(win != NULL, "The window should always be set for tool interactions!"); + const Scene *scene = win ? win->scene : NULL; + ScrArea *area = handler->dynamic.user_data; handler->keymap_tool = NULL; bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; - if (tref_rt && tref_rt->keymap_fallback[0]) { - const char *keymap_id = NULL; + if (tref_rt && tref_rt->keymap[0]) { + keymap_id_list[keymap_id_list_len++] = tref_rt->keymap; + } + + bool is_gizmo_visible = false; + bool is_gizmo_highlight = false; + + if ((tref_rt && tref_rt->keymap_fallback[0]) && + (scene && (scene->toolsettings->workspace_tool_type == SCE_WORKSPACE_TOOL_FALLBACK))) { + bool add_keymap = false; /* Support for the gizmo owning the tool keymap. */ - if (tref_rt->gizmo_group[0] != '\0' && tref_rt->keymap_fallback[0] != '\0') { + + if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) { + add_keymap = true; + } + + if (with_gizmos && (tref_rt->gizmo_group[0] != '\0')) { wmGizmoMap *gzmap = NULL; wmGizmoGroup *gzgroup = NULL; LISTBASE_FOREACH (ARegion *, region, &area->regionbase) { @@ -3930,51 +4063,57 @@ wmKeyMap *WM_event_get_keymap_from_toolsystem_fallback(wmWindowManager *wm, if (gzgroup != NULL) { if (gzgroup->type->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { /* If all are hidden, don't override. */ - if (gzgroup->use_fallback_keymap) { - wmGizmo *highlight = wm_gizmomap_highlight_get(gzmap); - if (highlight == NULL) { - keymap_id = tref_rt->keymap_fallback; - } + is_gizmo_visible = true; + wmGizmo *highlight = wm_gizmomap_highlight_get(gzmap); + if (highlight) { + is_gizmo_highlight = true; } + add_keymap = true; } } } - if (keymap_id && keymap_id[0]) { - wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty( - &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW); - /* We shouldn't use keymaps from unrelated spaces. */ - if (km != NULL) { - handler->keymap_tool = area->runtime.tool; - return km; - } - printf( - "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname); + if (add_keymap) { + keymap_id_list[keymap_id_list_len++] = tref_rt->keymap_fallback; } } - return NULL; + + if (is_gizmo_visible && !is_gizmo_highlight) { + if (keymap_id_list_len == 2) { + SWAP(const char *, keymap_id_list[0], keymap_id_list[1]); + } + } + + for (int i = 0; i < keymap_id_list_len; i++) { + const char *keymap_id = keymap_id_list[i]; + BLI_assert(keymap_id && keymap_id[0]); + + wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty( + &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW); + /* We shouldn't use keymaps from unrelated spaces. */ + if (km == NULL) { + printf("Keymap: '%s' not found for tool '%s'\n", keymap_id, area->runtime.tool->idname); + continue; + } + handler->keymap_tool = area->runtime.tool; + km_result->keymaps[km_result->keymaps_len++] = km; + } } -wmKeyMap *WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, wmEventHandler_Keymap *handler) +void WM_event_get_keymap_from_toolsystem_with_gizmos(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) { - ScrArea *area = handler->dynamic.user_data; - handler->keymap_tool = NULL; - bToolRef_Runtime *tref_rt = area->runtime.tool ? area->runtime.tool->runtime : NULL; - if (tref_rt && tref_rt->keymap[0]) { - const char *keymap_id = tref_rt->keymap; - { - wmKeyMap *km = WM_keymap_list_find_spaceid_or_empty( - &wm->userconf->keymaps, keymap_id, area->spacetype, RGN_TYPE_WINDOW); - /* We shouldn't use keymaps from unrelated spaces. */ - if (km != NULL) { - handler->keymap_tool = area->runtime.tool; - return km; - } - printf( - "Keymap: '%s' not found for tool '%s'\n", tref_rt->keymap, area->runtime.tool->idname); - } - } - return NULL; + wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, true); +} + +void WM_event_get_keymap_from_toolsystem(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) +{ + wm_event_get_keymap_from_toolsystem_ex(wm, win, handler, km_result, false); } struct wmEventHandler_Keymap *WM_event_add_keymap_handler_dynamic( @@ -4024,10 +4163,10 @@ wmEventHandler_Keymap *WM_event_add_keymap_handler_priority(ListBase *handlers, static bool event_or_prev_in_rect(const wmEvent *event, const rcti *rect) { - if (BLI_rcti_isect_pt(rect, event->x, event->y)) { + if (BLI_rcti_isect_pt_v(rect, event->xy)) { return true; } - if (event->type == MOUSEMOVE && BLI_rcti_isect_pt(rect, event->prevx, event->prevy)) { + if (event->type == MOUSEMOVE && BLI_rcti_isect_pt_v(rect, event->prev_xy)) { return true; } return false; @@ -4496,14 +4635,14 @@ static void attach_ndof_data(wmEvent *event, const GHOST_TEventNDOFMotionData *g event->custom = EVT_DATA_NDOF_MOTION; event->customdata = data; - event->customdatafree = 1; + event->customdata_free = true; } #endif /* WITH_INPUT_NDOF */ /* Imperfect but probably usable... draw/enable drags to other windows. */ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *event) { - int mval[2] = {event->x, event->y}; + int mval[2] = {event->xy[0], event->xy[1]}; if (wm->windows.first == wm->windows.last) { return NULL; @@ -4525,8 +4664,7 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi wmWindow *win_other = WM_window_find_under_cursor(wm, win, win, mval, mval); if (win_other) { - event->x = mval[0]; - event->y = mval[1]; + copy_v2_v2_int(event->xy, mval); return win_other; } } @@ -4535,13 +4673,13 @@ static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *wi static bool wm_event_is_double_click(const wmEvent *event) { - if ((event->type == event->prevtype) && (event->prevval == KM_RELEASE) && + if ((event->type == event->prev_type) && (event->prev_val == KM_RELEASE) && (event->val == KM_PRESS)) { - if (ISMOUSE(event->type) && WM_event_drag_test(event, &event->prevclickx)) { + if (ISMOUSE(event->type) && WM_event_drag_test(event, event->prev_click_xy)) { /* Pass. */ } else { - if ((PIL_check_seconds_timer() - event->prevclicktime) * 1000 < U.dbl_click_time) { + if ((PIL_check_seconds_timer() - event->prev_click_time) * 1000 < U.dbl_click_time) { return true; } } @@ -4555,15 +4693,15 @@ static bool wm_event_is_double_click(const wmEvent *event) */ static void wm_event_prev_values_set(wmEvent *event, wmEvent *event_state) { - event->prevval = event_state->prevval = event_state->val; - event->prevtype = event_state->prevtype = event_state->type; + event->prev_val = event_state->prev_val = event_state->val; + event->prev_type = event_state->prev_type = event_state->type; } static void wm_event_prev_click_set(wmEvent *event, wmEvent *event_state) { - event->prevclicktime = event_state->prevclicktime = PIL_check_seconds_timer(); - event->prevclickx = event_state->prevclickx = event_state->x; - event->prevclicky = event_state->prevclicky = event_state->y; + event->prev_click_time = event_state->prev_click_time = PIL_check_seconds_timer(); + event->prev_click_xy[0] = event_state->prev_click_xy[0] = event_state->xy[0]; + event->prev_click_xy[1] = event_state->prev_click_xy[1] = event_state->xy[1]; } static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) @@ -4575,6 +4713,7 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) * them for better performance. */ if (event_last && event_last->type == MOUSEMOVE) { event_last->type = INBETWEEN_MOUSEMOVE; + event_last->is_repeat = false; } wmEvent *event_new = wm_event_add(win, event); @@ -4582,7 +4721,7 @@ static wmEvent *wm_event_add_mousemove(wmWindow *win, const wmEvent *event) event_last = win->eventstate; } - copy_v2_v2_int(&event_new->prevx, &event_last->x); + copy_v2_v2_int(event_new->prev_xy, event_last->xy); return event_new; } @@ -4592,16 +4731,16 @@ static wmEvent *wm_event_add_trackpad(wmWindow *win, const wmEvent *event, int d * for painting with mouse moves, for navigation using the accumulated value is ok. */ wmEvent *event_last = win->event_queue.last; if (event_last && event_last->type == event->type) { - deltax += event_last->x - event_last->prevx; - deltay += event_last->y - event_last->prevy; + deltax += event_last->xy[0] - event_last->prev_xy[0]; + deltay += event_last->xy[1] - event_last->prev_xy[1]; wm_event_free_last(win); } - /* Set prevx/prevy, the delta is computed from this in operators. */ + /* Set prev_xy, the delta is computed from this in operators. */ wmEvent *event_new = wm_event_add(win, event); - event_new->prevx = event_new->x - deltax; - event_new->prevy = event_new->y - deltay; + event_new->prev_xy[0] = event_new->xy[0] - deltax; + event_new->prev_xy[1] = event_new->xy[1] - deltay; return event_new; } @@ -4641,8 +4780,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void * If this was done it could leave `win->eventstate` previous and current value * set to the same key press/release state which doesn't make sense. */ - event.prevtype = event.type; - event.prevval = event.val; + event.prev_type = event.type; + event.prev_val = event.val; /* Ensure the event state is correct, any deviation from this may cause bugs. * @@ -4656,12 +4795,12 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void "Non-keyboard/mouse button found in 'win->eventstate->type = %d'", event_state->type); } - if ((event_state->prevtype || event_state->prevval) && /* Ignore cleared event state. */ - !(ISMOUSE_BUTTON(event_state->prevtype) || ISKEYBOARD(event_state->prevtype) || + if ((event_state->prev_type || event_state->prev_val) && /* Ignore cleared event state. */ + !(ISMOUSE_BUTTON(event_state->prev_type) || ISKEYBOARD(event_state->prev_type) || (event_state->type == EVENT_NONE))) { CLOG_WARN(WM_LOG_HANDLERS, - "Non-keyboard/mouse button found in 'win->eventstate->prevtype = %d'", - event_state->prevtype); + "Non-keyboard/mouse button found in 'win->eventstate->prev_type = %d'", + event_state->prev_type); } #endif @@ -4670,14 +4809,14 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void case GHOST_kEventCursorMove: { GHOST_TEventCursorData *cd = customdata; - copy_v2_v2_int(&event.x, &cd->x); - wm_stereo3d_mouse_offset_apply(win, &event.x); + copy_v2_v2_int(event.xy, &cd->x); + wm_stereo3d_mouse_offset_apply(win, event.xy); wm_tablet_data_from_ghost(&cd->tablet, &event.tablet); event.type = MOUSEMOVE; { wmEvent *event_new = wm_event_add_mousemove(win, &event); - copy_v2_v2_int(&event_state->x, &event_new->x); + copy_v2_v2_int(event_state->xy, event_new->xy); event_state->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } @@ -4688,14 +4827,14 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void wmEvent event_other = *win_other->eventstate; /* See comment for this operation on `event` for details. */ - event_other.prevtype = event_other.type; - event_other.prevval = event_other.val; + event_other.prev_type = event_other.type; + event_other.prev_val = event_other.val; - copy_v2_v2_int(&event_other.x, &event.x); + copy_v2_v2_int(event_other.xy, event.xy); event_other.type = MOUSEMOVE; { wmEvent *event_new = wm_event_add_mousemove(win_other, &event_other); - copy_v2_v2_int(&win_other->eventstate->x, &event_new->x); + copy_v2_v2_int(win_other->eventstate->xy, event_new->xy); win_other->eventstate->tablet.is_motion_absolute = event_new->tablet.is_motion_absolute; } } @@ -4722,8 +4861,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void break; } - event.x = event_state->x = pd->x; - event.y = event_state->y = pd->y; + event.xy[0] = event_state->xy[0] = pd->x; + event.xy[1] = event_state->xy[1] = pd->y; event.val = KM_NOTHING; /* The direction is inverted from the device due to system preferences. */ @@ -4787,10 +4926,10 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void wmEvent event_other = *win_other->eventstate; /* See comment for this operation on `event` for details. */ - event_other.prevtype = event_other.type; - event_other.prevval = event_other.val; + event_other.prev_type = event_other.type; + event_other.prev_val = event_other.val; - copy_v2_v2_int(&event_other.x, &event.x); + copy_v2_v2_int(event_other.xy, event.xy); event_other.type = event.type; event_other.val = event.val; @@ -4968,7 +5107,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void attach_ndof_data(&event, customdata); wm_event_add(win, &event); - CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.x, event.y); + CLOG_INFO(WM_LOG_HANDLERS, 1, "sending NDOF_MOTION, prev = %d %d", event.xy[0], event.xy[1]); break; } @@ -5038,6 +5177,24 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void #endif } +#ifdef WITH_XR_OPENXR +void wm_event_add_xrevent(wmWindow *win, wmXrActionData *actiondata, short val) +{ + BLI_assert(val == KM_PRESS || val == KM_RELEASE); + + wmEvent event = { + .type = EVT_XR_ACTION, + .val = val, + .is_repeat = false, + .custom = EVT_DATA_XR, + .customdata = actiondata, + .customdata_free = true, + }; + + wm_event_add(win, &event); +} +#endif /* WITH_XR_OPENXR */ + /** \} */ /* -------------------------------------------------------------------- */ @@ -5088,18 +5245,23 @@ void WM_set_locked_interface(wmWindowManager *wm, bool lock) /** \name Event / Keymap Matching API * \{ */ -wmKeyMap *WM_event_get_keymap_from_handler(wmWindowManager *wm, wmEventHandler_Keymap *handler) +void WM_event_get_keymaps_from_handler(wmWindowManager *wm, + wmWindow *win, + wmEventHandler_Keymap *handler, + wmEventHandler_KeymapResult *km_result) { - wmKeyMap *keymap; if (handler->dynamic.keymap_fn != NULL) { - keymap = handler->dynamic.keymap_fn(wm, handler); + handler->dynamic.keymap_fn(wm, win, handler, km_result); BLI_assert(handler->keymap == NULL); } else { - keymap = WM_keymap_active(wm, handler->keymap); + memset(km_result, 0x0, sizeof(*km_result)); + wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap); BLI_assert(keymap != NULL); + if (keymap != NULL) { + km_result->keymaps[km_result->keymaps_len++] = keymap; + } } - return keymap; } wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wmEvent *event) @@ -5115,10 +5277,8 @@ wmKeyMapItem *WM_event_match_keymap_item(bContext *C, wmKeyMap *keymap, const wm return NULL; } -wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, - wmWindowManager *wm, - ListBase *handlers, - const wmEvent *event) +wmKeyMapItem *WM_event_match_keymap_item_from_handlers( + bContext *C, wmWindowManager *wm, wmWindow *win, ListBase *handlers, const wmEvent *event) { LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { /* During this loop, UI handlers for nested menus can tag multiple handlers free. */ @@ -5128,11 +5288,15 @@ wmKeyMapItem *WM_event_match_keymap_item_from_handlers(bContext *C, else if (handler_base->poll == NULL || handler_base->poll(CTX_wm_region(C), event)) { if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - if (keymap && WM_keymap_poll(C, keymap)) { - wmKeyMapItem *kmi = WM_event_match_keymap_item(C, keymap, event); - if (kmi != NULL) { - return kmi; + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + if (WM_keymap_poll(C, keymap)) { + wmKeyMapItem *kmi = WM_event_match_keymap_item(C, keymap, event); + if (kmi != NULL) { + return kmi; + } } } } @@ -5355,7 +5519,8 @@ void WM_window_cursor_keymap_status_refresh(bContext *C, wmWindow *win) wm_eventemulation(&test_event, true); wmKeyMapItem *kmi = NULL; for (int handler_index = 0; handler_index < ARRAY_SIZE(handlers); handler_index++) { - kmi = WM_event_match_keymap_item_from_handlers(C, wm, handlers[handler_index], &test_event); + kmi = WM_event_match_keymap_item_from_handlers( + C, wm, win, handlers[handler_index], &test_event); if (kmi) { break; } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 2ce2bcc2f3c..ff9b56d7859 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -75,6 +75,7 @@ #include "BKE_addon.h" #include "BKE_appdir.h" +#include "BKE_asset_library.h" #include "BKE_autoexec.h" #include "BKE_blender.h" #include "BKE_blendfile.h" @@ -105,6 +106,7 @@ #include "IMB_imbuf_types.h" #include "IMB_thumbs.h" +#include "ED_asset.h" #include "ED_datafiles.h" #include "ED_fileselect.h" #include "ED_image.h" @@ -168,9 +170,13 @@ void WM_file_tag_modified(void) } } -bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm) +/** + * Check if there is data that would be lost when closing the current file without saving. + */ +bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm) { - return !wm->file_saved || ED_image_should_save_modified(bmain); + return !wm->file_saved || ED_image_should_save_modified(bmain) || + BKE_asset_library_has_any_unsaved_catalogs(); } /** \} */ @@ -204,6 +210,16 @@ static void wm_window_match_init(bContext *C, ListBase *wmlist) WM_event_remove_handlers(C, &win->modalhandlers); ED_screen_exit(C, win, WM_window_get_active_screen(win)); } + + /* NOTE(@campbellbarton): Clear the message bus so it's always cleared on file load. + * Otherwise it's cleared when "Load UI" is set (see #USER_FILENOUI & #wm_close_and_free). + * However it's _not_ cleared when the UI is kept. This complicates use from add-ons + * which can re-register subscribers on file-load. To support this use case, + * it's best to have predictable behavior - always clear. */ + if (wm->message_bus != NULL) { + WM_msgbus_destroy(wm->message_bus); + wm->message_bus = NULL; + } } /* reset active window */ @@ -516,7 +532,7 @@ static int wm_read_exotic(const char *name) /* check for compressed .blend */ FileReader *compressed_file = NULL; if (BLI_file_magic_is_gzip(header)) { - /* In earlier versions of Blender (before 3.0), compressed files used Gzip instead of Zstd. + /* In earlier versions of Blender (before 3.0), compressed files used `Gzip` instead of `Zstd`. * While these files will no longer be written, there still needs to be reading support. */ compressed_file = BLI_filereader_new_gzip(rawfile); } @@ -753,6 +769,18 @@ static void wm_file_read_post(bContext *C, const struct wmFileReadPost_Params *p WM_toolsystem_init(C); } } + + /* Keep last. */ + if (use_data) { + if (!G.background) { + /* Special case, when calling indirectly (from a Python script for example), + * the event loop wont run again to set the active window. + * Set the window here to allow scripts to continue running other operations, see: T92464. */ + if (wm->op_undo_depth > 0) { + CTX_wm_window_set(C, wm->windows.first); + } + } + } } /** \} */ @@ -857,6 +885,28 @@ static void file_read_reports_finalize(BlendFileReadReport *bf_reports) duration_lib_override_recursive_resync_seconds); } + if (bf_reports->count.linked_proxies != 0 || + bf_reports->count.proxies_to_lib_overrides_success != 0 || + bf_reports->count.proxies_to_lib_overrides_failures != 0) { + BKE_reportf(bf_reports->reports, + RPT_WARNING, + "Proxies are deprecated (%d proxies were automatically converted to library " + "overrides, %d proxies could not be converted and %d linked proxies were kept " + "untouched). If you need to keep proxies for the time being, please disable the " + "`Proxy to Override Auto Conversion` in Experimental user preferences", + bf_reports->count.proxies_to_lib_overrides_success, + bf_reports->count.proxies_to_lib_overrides_failures, + bf_reports->count.linked_proxies); + } + + if (bf_reports->count.vse_strips_skipped != 0) { + BKE_reportf(bf_reports->reports, + RPT_ERROR, + "%d sequence strips were not read because they were in a channel larger than %d", + bf_reports->count.vse_strips_skipped, + MAXSEQ); + } + BLI_linklist_free(bf_reports->resynced_lib_overrides_libraries, NULL); bf_reports->resynced_lib_overrides_libraries = NULL; } @@ -1606,6 +1656,7 @@ static ImBuf *blend_file_thumb_from_camera(const bContext *C, area = BKE_screen_find_big_area(screen, SPACE_VIEW3D, 0); if (area) { v3d = area->spacedata.first; + region = BKE_area_find_region_type(area, RGN_TYPE_WINDOW); } } @@ -1749,6 +1800,7 @@ static bool wm_file_write(bContext *C, /* Call pre-save callbacks before writing preview, * that way you can generate custom file thumbnail. */ BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); + ED_assets_pre_save(bmain); /* Enforce full override check/generation on file save. */ BKE_lib_override_library_main_operations_create(bmain, true); @@ -2066,6 +2118,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) } BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); + ED_assets_pre_save(bmain); /* check current window and close it if temp */ if (win && WM_window_is_temp_screen(win)) { @@ -2101,7 +2154,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) } printf("ok\n"); - + BKE_report(op->reports, RPT_INFO, "Startup file saved"); G.save_over = 0; BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST); @@ -3566,6 +3619,14 @@ static void wm_block_file_close_save_button(uiBlock *block, wmGenericCallback *p static const char *close_file_dialog_name = "file_close_popup"; +static void save_catalogs_when_file_is_closed_set_fn(bContext *UNUSED(C), + void *arg1, + void *UNUSED(arg2)) +{ + char *save_catalogs_when_file_is_closed = arg1; + ED_asset_catalogs_set_save_catalogs_when_file_is_saved(*save_catalogs_when_file_is_closed != 0); +} + static uiBlock *block_create__close_file_dialog(struct bContext *C, struct ARegion *region, void *arg1) @@ -3622,11 +3683,17 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, MEM_freeN(message); } + /* Used to determine if extra separators are needed. */ + bool has_extra_checkboxes = false; + /* Modified Images Checkbox. */ if (modified_images_count > 0) { char message[64]; BLI_snprintf(message, sizeof(message), "Save %u modified image(s)", modified_images_count); - uiItemS(layout); + /* Only the first checkbox should get extra separation. */ + if (!has_extra_checkboxes) { + uiItemS(layout); + } uiDefButBitC(block, UI_BTYPE_CHECKBOX, 1, @@ -3642,11 +3709,41 @@ static uiBlock *block_create__close_file_dialog(struct bContext *C, 0, 0, ""); + has_extra_checkboxes = true; + } + + if (BKE_asset_library_has_any_unsaved_catalogs()) { + static char save_catalogs_when_file_is_closed; + + save_catalogs_when_file_is_closed = ED_asset_catalogs_get_save_catalogs_when_file_is_saved(); + + /* Only the first checkbox should get extra separation. */ + if (!has_extra_checkboxes) { + uiItemS(layout); + } + uiBut *but = uiDefButBitC(block, + UI_BTYPE_CHECKBOX, + 1, + 0, + "Save modified asset catalogs", + 0, + 0, + 0, + UI_UNIT_Y, + &save_catalogs_when_file_is_closed, + 0, + 0, + 0, + 0, + ""); + UI_but_func_set( + but, save_catalogs_when_file_is_closed_set_fn, &save_catalogs_when_file_is_closed, NULL); + has_extra_checkboxes = true; } BKE_reports_clear(&reports); - uiItemS_ex(layout, modified_images_count > 0 ? 2.0f : 4.0f); + uiItemS_ex(layout, has_extra_checkboxes ? 2.0f : 4.0f); /* Buttons. */ #ifdef _WIN32 @@ -3727,7 +3824,7 @@ bool wm_operator_close_file_dialog_if_needed(bContext *C, wmGenericCallbackFn post_action_fn) { if (U.uiflag & USER_SAVE_PROMPT && - wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C))) { + wm_file_or_session_data_has_unsaved_changes(CTX_data_main(C), CTX_wm_manager(C))) { wmGenericCallback *callback = MEM_callocN(sizeof(*callback), __func__); callback->exec = post_action_fn; callback->user_data = IDP_CopyProperty(op->properties); diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c index d0117d9d57a..c3e0764f6c2 100644 --- a/source/blender/windowmanager/intern/wm_files_link.c +++ b/source/blender/windowmanager/intern/wm_files_link.c @@ -127,10 +127,10 @@ static int wm_link_append_invoke(bContext *C, wmOperator *op, const wmEvent *UNU return OPERATOR_RUNNING_MODAL; } -static short wm_link_append_flag(wmOperator *op) +static int wm_link_append_flag(wmOperator *op) { PropertyRNA *prop; - short flag = 0; + int flag = 0; if (RNA_boolean_get(op->ptr, "autoselect")) { flag |= FILE_AUTOSELECT; @@ -147,17 +147,20 @@ static short wm_link_append_flag(wmOperator *op) } else { if (RNA_boolean_get(op->ptr, "use_recursive")) { - flag |= FILE_APPEND_RECURSIVE; + flag |= BLO_LIBLINK_APPEND_RECURSIVE; } if (RNA_boolean_get(op->ptr, "set_fake")) { - flag |= FILE_APPEND_SET_FAKEUSER; + flag |= BLO_LIBLINK_APPEND_SET_FAKEUSER; + } + if (RNA_boolean_get(op->ptr, "do_reuse_local_id")) { + flag |= BLO_LIBLINK_APPEND_LOCAL_ID_REUSE; } } if (RNA_boolean_get(op->ptr, "instance_collections")) { - flag |= FILE_COLLECTION_INSTANCE; + flag |= BLO_LIBLINK_COLLECTION_INSTANCE; } if (RNA_boolean_get(op->ptr, "instance_object_data")) { - flag |= FILE_OBDATA_INSTANCE; + flag |= BLO_LIBLINK_OBDATA_INSTANCE; } return flag; @@ -174,6 +177,7 @@ typedef struct WMLinkAppendDataItem { char append_tag; ID *new_id; + Library *source_library; void *customdata; } WMLinkAppendDataItem; @@ -190,6 +194,9 @@ typedef struct WMLinkAppendData { /** Allows to easily find an existing items from an ID pointer. Used by append code. */ GHash *new_id_to_item; + /** Runtime info used by append code to manage re-use of already appended matching IDs. */ + GHash *library_weak_reference_mapping; + /* Internal 'private' data */ MemArena *memarena; } WMLinkAppendData; @@ -230,6 +237,8 @@ static void wm_link_append_data_free(WMLinkAppendData *lapp_data) BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL); } + BLI_assert(lapp_data->library_weak_reference_mapping == NULL); + BLI_memarena_free(lapp_data->memarena); } @@ -303,31 +312,6 @@ static bool object_in_any_collection(Main *bmain, Object *ob) return false; } -/** - * Shared operations to perform on the object's base after adding it to the scene. - */ -static void wm_append_loose_data_instantiate_object_base_instance_init( - Object *ob, bool set_selected, bool set_active, ViewLayer *view_layer, const View3D *v3d) -{ - Base *base = BKE_view_layer_base_find(view_layer, ob); - - if (v3d != NULL) { - base->local_view_bits |= v3d->local_view_uuid; - } - - if (set_selected) { - if (base->flag & BASE_SELECTABLE) { - base->flag |= BASE_SELECTED; - } - } - - if (set_active) { - view_layer->basact = base; - } - - BKE_scene_object_base_flag_sync_from_base(base); -} - static ID *wm_append_loose_data_instantiate_process_check(WMLinkAppendDataItem *item) { /* We consider that if we either kept it linked, or re-used already local data, instantiation @@ -391,9 +375,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, LinkNode *itemlink; Collection *active_collection = NULL; - const bool do_obdata = (lapp_data->flag & FILE_OBDATA_INSTANCE) != 0; + const bool do_obdata = (lapp_data->flag & BLO_LIBLINK_OBDATA_INSTANCE) != 0; - const bool object_set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; /* Do NOT make base active here! screws up GUI stuff, * if you want it do it at the editor level. */ const bool object_set_active = false; @@ -453,7 +436,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, * children. */ Collection *collection = (Collection *)id; - bool do_add_collection = false; + /* We always add collections directly selected by the user. */ + bool do_add_collection = (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0; LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) { Object *ob = coll_ob->ob; if (!object_in_any_scene(bmain, ob)) { @@ -467,21 +451,19 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, /* In case user requested instantiation of collections as empties, we do so for the one they * explicitly selected (originally directly linked IDs). */ - if ((lapp_data->flag & FILE_COLLECTION_INSTANCE) != 0 && + if ((lapp_data->flag & BLO_LIBLINK_COLLECTION_INSTANCE) != 0 && (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) { /* BKE_object_add(...) messes with the selection. */ Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2); ob->type = OB_EMPTY; ob->empty_drawsize = U.collection_instance_empty_size; - BKE_collection_object_add(bmain, active_collection, ob); - const bool set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0; /* TODO: why is it OK to make this active here but not in other situations? * See other callers of #object_base_instance_init */ const bool set_active = set_selected; - wm_append_loose_data_instantiate_object_base_instance_init( - ob, set_selected, set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, set_active); /* Assign the collection. */ ob->instance_collection = collection; @@ -528,10 +510,8 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, CLAMP_MIN(ob->id.us, 0); ob->mode = OB_MODE_OBJECT; - BKE_collection_object_add(bmain, active_collection, ob); - - wm_append_loose_data_instantiate_object_base_instance_init( - ob, object_set_selected, object_set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, object_set_active); } if (!do_obdata) { @@ -562,12 +542,22 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, id_us_plus(id); BKE_object_materials_test(bmain, ob, ob->data); - BKE_collection_object_add(bmain, active_collection, ob); - - wm_append_loose_data_instantiate_object_base_instance_init( - ob, object_set_selected, object_set_active, view_layer, v3d); + BLO_object_instantiate_object_base_instance_init( + bmain, active_collection, ob, view_layer, v3d, lapp_data->flag, object_set_active); copy_v3_v3(ob->loc, scene->cursor.location); + + id->tag &= ~LIB_TAG_DOIT; + } + + /* Finally, add rigid body objects and constraints to current RB world(s). */ + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + ID *id = wm_append_loose_data_instantiate_process_check(item); + if (id == NULL || GS(id->name) != ID_OB) { + continue; + } + BKE_rigidbody_ensure_local_object(bmain, (Object *)id); } } @@ -575,7 +565,10 @@ static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data, static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) { - if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK)) { + /* NOTE: It is important to also skip liboverride references here, as those should never be made + * local. */ + if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK | + IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE)) { return IDWALK_RET_NOP; } @@ -587,6 +580,23 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) } if (!BKE_idtype_idcode_is_linkable(GS(id->name))) { + /* While we do not want to add non-linkable ID (shape keys...) to the list of linked items, + * unfortunately they can use fully linkable valid IDs too, like actions. Those need to be + * processed, so we need to recursively deal with them here. */ + /* NOTE: Since we are by-passing checks in `BKE_library_foreach_ID_link` by manually calling it + * recursively, we need to take care of potential recursion cases ourselves (e.g.animdata of + * shapekey referencing the shapekey itself). */ + if (id != cb_data->id_self) { + BKE_library_foreach_ID_link( + cb_data->bmain, id, foreach_libblock_append_callback, data, IDWALK_NOP); + } + return IDWALK_RET_NOP; + } + + const bool do_recursive = (data->lapp_data->flag & BLO_LIBLINK_APPEND_RECURSIVE) != 0; + if (!do_recursive && cb_data->id_owner->lib != id->lib) { + /* When `do_recursive` is false, we only make local IDs from same library(-ies) as the + * initially directly linked ones. */ return IDWALK_RET_NOP; } @@ -594,7 +604,8 @@ static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data) if (item == NULL) { item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL); item->new_id = id; - /* Since we did not have an item for that ID yet, we now user did not selected it explicitly, + item->source_library = id->lib; + /* Since we did not have an item for that ID yet, we know user did not selected it explicitly, * it was rather linked indirectly. This info is important for instantiation of collections. */ item->append_tag |= WM_APPEND_TAG_INDIRECT; BLI_ghash_insert(data->lapp_data->new_id_to_item, id, item); @@ -621,12 +632,19 @@ static void wm_append_do(WMLinkAppendData *lapp_data, { BLI_assert((lapp_data->flag & FILE_LINK) == 0); - const bool do_recursive = (lapp_data->flag & FILE_APPEND_RECURSIVE) != 0; - const bool set_fakeuser = (lapp_data->flag & FILE_APPEND_SET_FAKEUSER) != 0; + const bool set_fakeuser = (lapp_data->flag & BLO_LIBLINK_APPEND_SET_FAKEUSER) != 0; + const bool do_reuse_local_id = (lapp_data->flag & BLO_LIBLINK_APPEND_LOCAL_ID_REUSE) != 0; + + const int make_local_common_flags = LIB_ID_MAKELOCAL_FULL_LIBRARY | + ((lapp_data->flag & BLO_LIBLINK_APPEND_ASSET_DATA_CLEAR) != + 0 ? + LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR : + 0); LinkNode *itemlink; - /* Generate a mapping between newly linked IDs and their items. */ + /* Generate a mapping between newly linked IDs and their items, and tag linked IDs used as + * liboverride references as already existing. */ lapp_data->new_id_to_item = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { WMLinkAppendDataItem *item = itemlink->link; @@ -635,8 +653,17 @@ static void wm_append_do(WMLinkAppendData *lapp_data, continue; } BLI_ghash_insert(lapp_data->new_id_to_item, id, item); + + /* This ensures that if a liboverride reference is also linked/used by some other appended + * data, it gets a local copy instead of being made directly local, so that the liboverride + * references remain valid (i.e. linked data). */ + if (ID_IS_OVERRIDE_LIBRARY_REAL(id)) { + id->override_library->reference->tag |= LIB_TAG_PRE_EXISTING; + } } + lapp_data->library_weak_reference_mapping = BKE_main_library_weak_reference_create(bmain); + /* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect * dependencies), this list will grow and we will process those IDs later, leading to a flatten * recursive processing of all the linked dependencies. */ @@ -648,9 +675,16 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } BLI_assert(item->customdata == NULL); - /* Clear tag previously used to mark IDs needing post-processing (instantiation of loose - * objects etc.). */ - id->tag &= ~LIB_TAG_DOIT; + /* In Append case linked IDs should never be marked as needing post-processing (instantiation + * of loose objects etc.). */ + BLI_assert((id->tag & LIB_TAG_DOIT) == 0); + + ID *existing_local_id = BKE_idtype_idcode_append_is_reusable(GS(id->name)) ? + BKE_main_library_weak_reference_search_item( + lapp_data->library_weak_reference_mapping, + id->lib->filepath, + id->name) : + NULL; if (item->append_action != WM_APPEND_ACT_UNSET) { /* Already set, pass. */ @@ -659,25 +693,38 @@ static void wm_append_do(WMLinkAppendData *lapp_data, CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name); item->append_action = WM_APPEND_ACT_KEEP_LINKED; } + else if (do_reuse_local_id && existing_local_id != NULL) { + CLOG_INFO(&LOG, 3, "Appended ID '%s' as a matching local one, re-using it...", id->name); + item->append_action = WM_APPEND_ACT_REUSE_LOCAL; + item->customdata = existing_local_id; + } else if (id->tag & LIB_TAG_PRE_EXISTING) { CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name); item->append_action = WM_APPEND_ACT_COPY_LOCAL; } else { - /* In future we could search for already existing matching local ID etc. */ CLOG_INFO(&LOG, 3, "Appended ID '%s' will be made local...", id->name); item->append_action = WM_APPEND_ACT_MAKE_LOCAL; } /* Only check dependencies if we are not keeping linked data, nor re-using existing local data. */ - if (do_recursive && - !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { + if (!ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { WMLinkAppendDataCallBack cb_data = { .lapp_data = lapp_data, .item = item, .reports = reports}; BKE_library_foreach_ID_link( bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP); } + + /* If we found a matching existing local id but are not re-using it, we need to properly clear + * its weak reference to linked data. */ + if (existing_local_id != NULL && + !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) { + BKE_main_library_weak_reference_remove_item(lapp_data->library_weak_reference_mapping, + id->lib->filepath, + id->name, + existing_local_id); + } } /* Effectively perform required operation on every linked ID. */ @@ -689,17 +736,20 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } ID *local_appended_new_id = NULL; + char lib_filepath[FILE_MAX]; + BLI_strncpy(lib_filepath, id->lib->filepath, sizeof(lib_filepath)); + char lib_id_name[MAX_ID_NAME]; + BLI_strncpy(lib_id_name, id->name, sizeof(lib_id_name)); + switch (item->append_action) { - case WM_APPEND_ACT_COPY_LOCAL: { - BKE_lib_id_make_local( - bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY); + case WM_APPEND_ACT_COPY_LOCAL: + BKE_lib_id_make_local(bmain, id, make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_COPY); local_appended_new_id = id->newid; break; - } case WM_APPEND_ACT_MAKE_LOCAL: BKE_lib_id_make_local(bmain, id, - LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_LOCAL | + make_local_common_flags | LIB_ID_MAKELOCAL_FORCE_LOCAL | LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING); BLI_assert(id->newid == NULL); local_appended_new_id = id; @@ -721,9 +771,13 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } if (local_appended_new_id != NULL) { - if (GS(local_appended_new_id->name) == ID_OB) { - BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id); + if (BKE_idtype_idcode_append_is_reusable(GS(local_appended_new_id->name))) { + BKE_main_library_weak_reference_add_item(lapp_data->library_weak_reference_mapping, + lib_filepath, + lib_id_name, + local_appended_new_id); } + if (set_fakeuser) { if (!ELEM(GS(local_appended_new_id->name), ID_OB, ID_GR)) { /* Do not set fake user on objects nor collections (instancing). */ @@ -733,6 +787,9 @@ static void wm_append_do(WMLinkAppendData *lapp_data, } } + BKE_main_library_weak_reference_destroy(lapp_data->library_weak_reference_mapping); + lapp_data->library_weak_reference_mapping = NULL; + /* Remap IDs as needed. */ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { WMLinkAppendDataItem *item = itemlink->link; @@ -745,7 +802,7 @@ static void wm_append_do(WMLinkAppendData *lapp_data, if (id == NULL) { continue; } - if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) { + if (ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_REUSE_LOCAL)) { BLI_assert(ID_IS_LINKED(id)); id = id->newid; if (id == NULL) { @@ -755,9 +812,30 @@ static void wm_append_do(WMLinkAppendData *lapp_data, BLI_assert(!ID_IS_LINKED(id)); - BKE_libblock_relink_to_newid_new(bmain, id); + BKE_libblock_relink_to_newid(bmain, id, 0); } + /* Remove linked IDs when a local existing data has been reused instead. */ + BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false); + for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) { + WMLinkAppendDataItem *item = itemlink->link; + + if (item->append_action != WM_APPEND_ACT_REUSE_LOCAL) { + continue; + } + + ID *id = item->new_id; + if (id == NULL) { + continue; + } + BLI_assert(ID_IS_LINKED(id)); + BLI_assert(id->newid != NULL); + + id->tag |= LIB_TAG_DOIT; + item->new_id = id->newid; + } + BKE_id_multi_tagged_delete(bmain); + /* Instantiate newly created (duplicated) IDs as needed. */ wm_append_loose_data_instantiate(lapp_data, bmain, scene, view_layer, v3d); @@ -911,6 +989,7 @@ static void wm_link_do(WMLinkAppendData *lapp_data, * This avoids trying to link same item with other libraries to come. */ BLI_bitmap_set_all(item->libraries, false, lapp_data->num_libraries); item->new_id = new_id; + item->source_library = new_id->lib; } } @@ -1009,7 +1088,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - short flag = wm_link_append_flag(op); + int flag = wm_link_append_flag(op); const bool do_append = (flag & FILE_LINK) == 0; /* sanity checks for flag */ @@ -1018,12 +1097,10 @@ static int wm_link_append_exec(bContext *C, wmOperator *op) RPT_WARNING, "Scene '%s' is linked, instantiation of objects is disabled", scene->id.name + 2); - flag &= ~(FILE_COLLECTION_INSTANCE | FILE_OBDATA_INSTANCE); + flag &= ~(BLO_LIBLINK_COLLECTION_INSTANCE | BLO_LIBLINK_OBDATA_INSTANCE); scene = NULL; } - /* We need to add nothing from #eBLOLibLinkFlags to flag here. */ - /* from here down, no error returns */ if (view_layer && RNA_boolean_get(op->ptr, "autoselect")) { @@ -1153,14 +1230,25 @@ static void wm_link_append_properties_common(wmOperatorType *ot, bool is_link) prop = RNA_def_boolean( ot->srna, "link", is_link, "Link", "Link the objects or data-blocks rather than appending"); RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + + prop = RNA_def_boolean( + ot->srna, + "do_reuse_local_id", + false, + "Re-Use Local Data", + "Try to re-use previously matching appended data-blocks instead of appending a new copy"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); + prop = RNA_def_boolean(ot->srna, "autoselect", true, "Select", "Select new objects"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "active_collection", true, "Active Collection", "Put new objects on the active collection"); RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean( ot->srna, "instance_collections", @@ -1251,13 +1339,14 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain, const char *filepath, const short id_code, const char *id_name, - const bool do_append) + const int flag) { + const bool do_append = (flag & FILE_LINK) == 0; /* Tag everything so we can make local only the new datablock. */ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true); /* Define working data, with just the one item we want to link. */ - WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? FILE_APPEND_RECURSIVE : 0); + WMLinkAppendData *lapp_data = wm_link_append_data_new(flag); wm_link_append_data_library_add(lapp_data, filepath); WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL); @@ -1266,13 +1355,13 @@ static ID *wm_file_link_append_datablock_ex(Main *bmain, /* Link datablock. */ wm_link_do(lapp_data, NULL, bmain, scene, view_layer, v3d); - /* Get linked datablock and free working data. */ - ID *id = item->new_id; - if (do_append) { wm_append_do(lapp_data, NULL, bmain, scene, view_layer, v3d); } + /* Get linked datablock and free working data. */ + ID *id = item->new_id; + wm_link_append_data_free(lapp_data); BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false); @@ -1290,10 +1379,12 @@ ID *WM_file_link_datablock(Main *bmain, View3D *v3d, const char *filepath, const short id_code, - const char *id_name) + const char *id_name, + int flag) { + flag |= FILE_LINK; return wm_file_link_append_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, false); + bmain, scene, view_layer, v3d, filepath, id_code, id_name, flag); } /* @@ -1306,10 +1397,12 @@ ID *WM_file_append_datablock(Main *bmain, View3D *v3d, const char *filepath, const short id_code, - const char *id_name) + const char *id_name, + int flag) { + BLI_assert((flag & FILE_LINK) == 0); ID *id = wm_file_link_append_datablock_ex( - bmain, scene, view_layer, v3d, filepath, id_code, id_name, true); + bmain, scene, view_layer, v3d, filepath, id_code, id_name, flag); return id; } diff --git a/source/blender/windowmanager/intern/wm_gesture.c b/source/blender/windowmanager/intern/wm_gesture.c index ae2cdb5608c..ab971901a20 100644 --- a/source/blender/windowmanager/intern/wm_gesture.c +++ b/source/blender/windowmanager/intern/wm_gesture.c @@ -73,14 +73,14 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new"); gesture->customdata = rect; - rect->xmin = event->x - gesture->winrct.xmin; - rect->ymin = event->y - gesture->winrct.ymin; + rect->xmin = event->xy[0] - gesture->winrct.xmin; + rect->ymin = event->xy[1] - gesture->winrct.ymin; if (type == WM_GESTURE_CIRCLE) { /* caller is responsible for initializing 'xmax' to radius. */ } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; } } else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) { @@ -88,8 +88,8 @@ wmGesture *WM_gesture_new(wmWindow *window, const ARegion *region, const wmEvent gesture->points_alloc = 1024; gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points"); - lasso[0] = event->x - gesture->winrct.xmin; - lasso[1] = event->y - gesture->winrct.ymin; + lasso[0] = event->xy[0] - gesture->winrct.xmin; + lasso[1] = event->xy[1] - gesture->winrct.ymin; gesture->points = 1; } diff --git a/source/blender/windowmanager/intern/wm_gesture_ops.c b/source/blender/windowmanager/intern/wm_gesture_ops.c index cc376d8f201..19b678d79d8 100644 --- a/source/blender/windowmanager/intern/wm_gesture_ops.c +++ b/source/blender/windowmanager/intern/wm_gesture_ops.c @@ -179,7 +179,8 @@ int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); const ARegion *region = CTX_wm_region(C); - const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input"); + const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && + RNA_boolean_get(op->ptr, "wait_for_input"); if (wait_for_input) { op->customdata = WM_gesture_new(win, region, event, WM_GESTURE_CROSS_RECT); @@ -244,17 +245,17 @@ int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->type) { case MOUSEMOVE: { if (gesture->type == WM_GESTURE_CROSS_RECT && gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; - rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; + rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin; } else if (gesture->move) { BLI_rcti_translate(rect, - (event->x - gesture->winrct.xmin) - rect->xmax, - (event->y - gesture->winrct.ymin) - rect->ymax); + (event->xy[0] - gesture->winrct.xmin) - rect->xmax, + (event->xy[1] - gesture->winrct.ymin) - rect->ymax); } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; } gesture_box_apply_rect(op); @@ -300,7 +301,8 @@ static void gesture_circle_apply(bContext *C, wmOperator *op); int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); - const bool wait_for_input = !ISTWEAK(event->type) && RNA_boolean_get(op->ptr, "wait_for_input"); + const bool wait_for_input = !WM_event_is_mouse_drag_or_press(event) && + RNA_boolean_get(op->ptr, "wait_for_input"); op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_CIRCLE); wmGesture *gesture = op->customdata; @@ -363,8 +365,8 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) if (event->type == MOUSEMOVE) { - rect->xmin = event->x - gesture->winrct.xmin; - rect->ymin = event->y - gesture->winrct.ymin; + rect->xmin = event->xy[0] - gesture->winrct.xmin; + rect->ymin = event->xy[1] - gesture->winrct.ymin; wm_gesture_tag_redraw(win); @@ -379,7 +381,7 @@ int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event) switch (event->val) { case GESTURE_MODAL_CIRCLE_SIZE: - fac = 0.3f * (event->y - event->prevy); + fac = 0.3f * (event->xy[1] - event->prev_xy[1]); if (fac > 0) { rect->xmax += ceil(fac); } @@ -498,8 +500,8 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) case MOUSEMOVE: case INBETWEEN_MOUSEMOVE: { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; const int val = wm_gesture_evaluate(gesture, event); if (val != 0) { @@ -508,8 +510,8 @@ static void gesture_tweak_modal(bContext *C, const wmEvent *event) wm_event_init_from_window(window, &tevent); /* We want to get coord from start of drag, * not from point where it becomes a tweak event, see T40549. */ - tevent.x = rect->xmin + gesture->winrct.xmin; - tevent.y = rect->ymin + gesture->winrct.ymin; + tevent.xy[0] = rect->xmin + gesture->winrct.xmin; + tevent.xy[1] = rect->ymin + gesture->winrct.ymin; if (gesture->event_type == LEFTMOUSE) { tevent.type = EVT_TWEAK_L; } @@ -694,8 +696,8 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) { short(*lasso)[2] = gesture->customdata; - const int x = ((event->x - gesture->winrct.xmin) - lasso[gesture->points - 1][0]); - const int y = ((event->y - gesture->winrct.ymin) - lasso[gesture->points - 1][1]); + const int x = ((event->xy[0] - gesture->winrct.xmin) - lasso[gesture->points - 1][0]); + const int y = ((event->xy[1] - gesture->winrct.ymin) - lasso[gesture->points - 1][1]); /* move the lasso */ if (gesture->move) { @@ -707,8 +709,8 @@ int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event) /* Make a simple distance check to get a smoother lasso * add only when at least 2 pixels between this and previous location. */ else if ((x * x + y * y) > pow2f(2.0f * UI_DPI_FAC)) { - lasso[gesture->points][0] = event->x - gesture->winrct.xmin; - lasso[gesture->points][1] = event->y - gesture->winrct.ymin; + lasso[gesture->points][0] = event->xy[0] - gesture->winrct.xmin; + lasso[gesture->points][1] = event->xy[1] - gesture->winrct.ymin; gesture->points++; } } @@ -871,7 +873,7 @@ int WM_gesture_straightline_invoke(bContext *C, wmOperator *op, const wmEvent *e op->customdata = WM_gesture_new(win, CTX_wm_region(C), event, WM_GESTURE_STRAIGHTLINE); - if (ISTWEAK(event->type)) { + if (WM_event_is_mouse_drag_or_press(event)) { wmGesture *gesture = op->customdata; gesture->is_active = true; } @@ -979,18 +981,18 @@ int WM_gesture_straightline_modal(bContext *C, wmOperator *op, const wmEvent *ev switch (event->type) { case MOUSEMOVE: { if (gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; - rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; + rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin; } else if (gesture->move) { BLI_rcti_translate(rect, - (event->x - gesture->winrct.xmin) - rect->xmax, - (event->y - gesture->winrct.ymin) - rect->ymax); + (event->xy[0] - gesture->winrct.xmin) - rect->xmax, + (event->xy[1] - gesture->winrct.ymin) - rect->ymax); gesture_straightline_apply(C, op); } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; gesture_straightline_apply(C, op); } @@ -1070,17 +1072,17 @@ int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmE switch (event->type) { case MOUSEMOVE: { if (gesture->is_active == false) { - rect->xmin = rect->xmax = event->x - gesture->winrct.xmin; - rect->ymin = rect->ymax = event->y - gesture->winrct.ymin; + rect->xmin = rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymin = rect->ymax = event->xy[1] - gesture->winrct.ymin; } else if (gesture->move) { BLI_rcti_translate(rect, - (event->x - gesture->winrct.xmin) - rect->xmax, - (event->y - gesture->winrct.ymin) - rect->ymax); + (event->xy[0] - gesture->winrct.xmin) - rect->xmax, + (event->xy[1] - gesture->winrct.ymin) - rect->ymax); } else { - rect->xmax = event->x - gesture->winrct.xmin; - rect->ymax = event->y - gesture->winrct.ymin; + rect->xmax = event->xy[0] - gesture->winrct.xmin; + rect->ymax = event->xy[1] - gesture->winrct.ymin; } if (gesture->use_snap) { diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index a8d2e000108..c382af03c4a 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -56,7 +56,6 @@ #include "BKE_blendfile.h" #include "BKE_callbacks.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_global.h" #include "BKE_icons.h" #include "BKE_image.h" @@ -69,6 +68,7 @@ #include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_sound.h" +#include "BKE_vfont.h" #include "BKE_addon.h" #include "BKE_appdir.h" @@ -119,6 +119,7 @@ #include "ED_space_api.h" #include "ED_undo.h" #include "ED_util.h" +#include "ED_view3d.h" #include "BLF_api.h" #include "BLT_lang.h" @@ -373,13 +374,6 @@ void WM_init(bContext *C, int argc, const char **argv) BLI_strncpy(G.lib, BKE_main_blendfile_path_from_global(), sizeof(G.lib)); -#ifdef WITH_COMPOSITOR - if (1) { - extern void *COM_linker_hack; - COM_linker_hack = COM_execute; - } -#endif - wm_homefile_read_post(C, params_file_read_post); } diff --git a/source/blender/windowmanager/intern/wm_jobs.c b/source/blender/windowmanager/intern/wm_jobs.c index 6494c337c10..2604105896d 100644 --- a/source/blender/windowmanager/intern/wm_jobs.c +++ b/source/blender/windowmanager/intern/wm_jobs.c @@ -230,7 +230,7 @@ bool WM_jobs_test(const wmWindowManager *wm, const void *owner, int job_type) LISTBASE_FOREACH (wmJob *, wm_job, &wm->jobs) { if (wm_job->owner == owner) { if (ELEM(job_type, WM_JOB_TYPE_ANY, wm_job->job_type)) { - if (wm_job->running || wm_job->suspended) { + if ((wm_job->flag & WM_JOB_PROGRESS) && (wm_job->running || wm_job->suspended)) { return true; } } diff --git a/source/blender/windowmanager/intern/wm_keymap.c b/source/blender/windowmanager/intern/wm_keymap.c index f955abaed53..658424b84a6 100644 --- a/source/blender/windowmanager/intern/wm_keymap.c +++ b/source/blender/windowmanager/intern/wm_keymap.c @@ -462,7 +462,9 @@ bool WM_keymap_poll(bContext *C, wmKeyMap *keymap) /* Empty key-maps may be missing more there may be a typo in the name. * Warn early to avoid losing time investigating each case. * When developing a customized Blender though you may want empty keymaps. */ - if (!U.app_template[0]) { + if (!U.app_template[0] && + /* Fallback key-maps may be intentionally empty, don't flood the output. */ + !BLI_str_endswith(keymap->idname, " (fallback)")) { CLOG_WARN(WM_LOG_KEYMAPS, "empty keymap '%s'", keymap->idname); } } @@ -1388,6 +1390,8 @@ static wmKeyMapItem *wm_keymap_item_find_in_keymap(wmKeyMap *keymap, } static wmKeyMapItem *wm_keymap_item_find_handlers(const bContext *C, + wmWindowManager *wm, + wmWindow *win, ListBase *handlers, const char *opname, int UNUSED(opcontext), @@ -1396,21 +1400,23 @@ static wmKeyMapItem *wm_keymap_item_find_handlers(const bContext *C, const struct wmKeyMapItemFind_Params *params, wmKeyMap **r_keymap) { - wmWindowManager *wm = CTX_wm_manager(C); - /* find keymap item in handlers */ LISTBASE_FOREACH (wmEventHandler *, handler_base, handlers) { if (handler_base->type == WM_HANDLER_TYPE_KEYMAP) { wmEventHandler_Keymap *handler = (wmEventHandler_Keymap *)handler_base; - wmKeyMap *keymap = WM_event_get_keymap_from_handler(wm, handler); - if (keymap && WM_keymap_poll((bContext *)C, keymap)) { - wmKeyMapItem *kmi = wm_keymap_item_find_in_keymap( - keymap, opname, properties, is_strict, params); - if (kmi != NULL) { - if (r_keymap) { - *r_keymap = keymap; + wmEventHandler_KeymapResult km_result; + WM_event_get_keymaps_from_handler(wm, win, handler, &km_result); + for (int km_index = 0; km_index < km_result.keymaps_len; km_index++) { + wmKeyMap *keymap = km_result.keymaps[km_index]; + if (WM_keymap_poll((bContext *)C, keymap)) { + wmKeyMapItem *kmi = wm_keymap_item_find_in_keymap( + keymap, opname, properties, is_strict, params); + if (kmi != NULL) { + if (r_keymap) { + *r_keymap = keymap; + } + return kmi; } - return kmi; } } } @@ -1430,6 +1436,7 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, const struct wmKeyMapItemFind_Params *params, wmKeyMap **r_keymap) { + wmWindowManager *wm = CTX_wm_manager(C); wmWindow *win = CTX_wm_window(C); ScrArea *area = CTX_wm_area(C); ARegion *region = CTX_wm_region(C); @@ -1437,17 +1444,25 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, /* look into multiple handler lists to find the item */ if (win) { - found = wm_keymap_item_find_handlers( - C, &win->modalhandlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + &win->modalhandlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); if (found == NULL) { found = wm_keymap_item_find_handlers( - C, &win->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + C, wm, win, &win->handlers, opname, opcontext, properties, is_strict, params, r_keymap); } } if (area && found == NULL) { found = wm_keymap_item_find_handlers( - C, &area->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + C, wm, win, &area->handlers, opname, opcontext, properties, is_strict, params, r_keymap); } if (found == NULL) { @@ -1458,8 +1473,16 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, } if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } } @@ -1469,8 +1492,16 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, } if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } else if (ELEM(opcontext, WM_OP_EXEC_REGION_PREVIEW, WM_OP_INVOKE_REGION_PREVIEW)) { @@ -1479,14 +1510,30 @@ static wmKeyMapItem *wm_keymap_item_find_props(const bContext *C, } if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } else { if (region) { - found = wm_keymap_item_find_handlers( - C, ®ion->handlers, opname, opcontext, properties, is_strict, params, r_keymap); + found = wm_keymap_item_find_handlers(C, + wm, + win, + ®ion->handlers, + opname, + opcontext, + properties, + is_strict, + params, + r_keymap); } } } diff --git a/source/blender/windowmanager/intern/wm_operator_type.c b/source/blender/windowmanager/intern/wm_operator_type.c index 39435721d1a..0e30df4ec99 100644 --- a/source/blender/windowmanager/intern/wm_operator_type.c +++ b/source/blender/windowmanager/intern/wm_operator_type.c @@ -110,6 +110,7 @@ static wmOperatorType *wm_operatortype_append__begin(void) /* Set the default i18n context now, so that opfunc can redefine it if needed! */ RNA_def_struct_translation_context(ot->srna, BLT_I18NCONTEXT_OPERATOR_DEFAULT); ot->translation_context = BLT_I18NCONTEXT_OPERATOR_DEFAULT; + ot->cursor_pending = WM_CURSOR_PICK_AREA; return ot; } diff --git a/source/blender/windowmanager/intern/wm_operator_utils.c b/source/blender/windowmanager/intern/wm_operator_utils.c index 81b597f7484..85a0a28de79 100644 --- a/source/blender/windowmanager/intern/wm_operator_utils.c +++ b/source/blender/windowmanager/intern/wm_operator_utils.c @@ -40,6 +40,24 @@ #include "ED_object.h" #include "ED_screen.h" +/* -------------------------------------------------------------------- */ +/** \name Generic Utilities + * \{ */ + +/** + * Only finish + pass through for press events (allowing press-tweak). + */ +int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent *event) +{ + if ((event->val != KM_PRESS) && + ((retval & OPERATOR_PASS_THROUGH) && (retval & OPERATOR_FINISHED))) { + retval &= ~OPERATOR_PASS_THROUGH; + } + return retval; +} + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Value Interaction Helper * diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index 81dcc5ccea0..1130ad9a558 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -423,7 +423,9 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr * `object.data.bones["Bones"].use_deform` such paths are not useful for key-shortcuts, * so this function supports returning data-paths directly to context members that aren't ID types. */ -static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr, bool *r_is_id) +static const char *wm_context_member_from_ptr(const bContext *C, + const PointerRNA *ptr, + bool *r_is_id) { const char *member_id = NULL; bool is_id = false; @@ -541,50 +543,52 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr case ID_SCR: { CTX_TEST_PTR_ID(C, "screen", ptr->owner_id); - SpaceLink *space_data = CTX_wm_space_data(C); - - TEST_PTR_DATA_TYPE("space_data", RNA_Space, ptr, space_data); TEST_PTR_DATA_TYPE("area", RNA_Area, ptr, CTX_wm_area(C)); TEST_PTR_DATA_TYPE("region", RNA_Region, ptr, CTX_wm_region(C)); - switch (space_data->spacetype) { - case SPACE_VIEW3D: { - const View3D *v3d = (View3D *)space_data; - const View3DShading *shading = &v3d->shading; + SpaceLink *space_data = CTX_wm_space_data(C); + if (space_data != NULL) { + TEST_PTR_DATA_TYPE("space_data", RNA_Space, ptr, space_data); - TEST_PTR_DATA_TYPE("space_data.overlay", RNA_View3DOverlay, ptr, v3d); - TEST_PTR_DATA_TYPE("space_data.shading", RNA_View3DShading, ptr, shading); - break; - } - case SPACE_GRAPH: { - const SpaceGraph *sipo = (SpaceGraph *)space_data; - const bDopeSheet *ads = sipo->ads; - TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); - break; - } - case SPACE_FILE: { - const SpaceFile *sfile = (SpaceFile *)space_data; - const FileSelectParams *params = ED_fileselect_get_active_params(sfile); - TEST_PTR_DATA_TYPE("space_data.params", RNA_FileSelectParams, ptr, params); - break; - } - case SPACE_IMAGE: { - const SpaceImage *sima = (SpaceImage *)space_data; - TEST_PTR_DATA_TYPE("space_data.overlay", RNA_SpaceImageOverlay, ptr, sima); - TEST_PTR_DATA_TYPE("space_data.uv_editor", RNA_SpaceUVEditor, ptr, sima); - break; - } - case SPACE_NLA: { - const SpaceNla *snla = (SpaceNla *)space_data; - const bDopeSheet *ads = snla->ads; - TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); - break; - } - case SPACE_ACTION: { - const SpaceAction *sact = (SpaceAction *)space_data; - const bDopeSheet *ads = &sact->ads; - TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); - break; + switch (space_data->spacetype) { + case SPACE_VIEW3D: { + const View3D *v3d = (View3D *)space_data; + const View3DShading *shading = &v3d->shading; + + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_View3DOverlay, ptr, v3d); + TEST_PTR_DATA_TYPE("space_data.shading", RNA_View3DShading, ptr, shading); + break; + } + case SPACE_GRAPH: { + const SpaceGraph *sipo = (SpaceGraph *)space_data; + const bDopeSheet *ads = sipo->ads; + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); + break; + } + case SPACE_FILE: { + const SpaceFile *sfile = (SpaceFile *)space_data; + const FileSelectParams *params = ED_fileselect_get_active_params(sfile); + TEST_PTR_DATA_TYPE("space_data.params", RNA_FileSelectParams, ptr, params); + break; + } + case SPACE_IMAGE: { + const SpaceImage *sima = (SpaceImage *)space_data; + TEST_PTR_DATA_TYPE("space_data.overlay", RNA_SpaceImageOverlay, ptr, sima); + TEST_PTR_DATA_TYPE("space_data.uv_editor", RNA_SpaceUVEditor, ptr, sima); + break; + } + case SPACE_NLA: { + const SpaceNla *snla = (SpaceNla *)space_data; + const bDopeSheet *ads = snla->ads; + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); + break; + } + case SPACE_ACTION: { + const SpaceAction *sact = (SpaceAction *)space_data; + const bDopeSheet *ads = &sact->ads; + TEST_PTR_DATA_TYPE("space_data.dopesheet", RNA_DopeSheet, ptr, ads); + break; + } } } @@ -607,7 +611,7 @@ static const char *wm_context_member_from_ptr(bContext *C, const PointerRNA *ptr /** * Calculate the path to `ptr` from context `C`, or return NULL if it can't be calculated. */ -char *WM_context_path_resolve_property_full(bContext *C, +char *WM_context_path_resolve_property_full(const bContext *C, const PointerRNA *ptr, PropertyRNA *prop, int index) @@ -2270,11 +2274,8 @@ static void radial_control_set_initial_mouse(RadialControl *rc, const wmEvent *e float d[2] = {0, 0}; float zoom[2] = {1, 1}; - rc->initial_mouse[0] = event->x; - rc->initial_mouse[1] = event->y; - - rc->initial_co[0] = event->x; - rc->initial_co[1] = event->y; + copy_v2_v2_int(rc->initial_mouse, event->xy); + copy_v2_v2_int(rc->initial_co, event->xy); switch (rc->subtype) { case PROP_NONE: @@ -2950,14 +2951,12 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even if (!has_numInput) { if (rc->slow_mode) { if (rc->subtype == PROP_ANGLE) { - const float position[2] = {event->x, event->y}; - /* calculate the initial angle here first */ delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0]; delta[1] = rc->initial_mouse[1] - rc->slow_mouse[1]; /* precision angle gets calculated from dial and gets added later */ - angle_precision = -0.1f * BLI_dial_angle(rc->dial, position); + angle_precision = -0.1f * BLI_dial_angle(rc->dial, (float[2]){UNPACK2(event->xy)}); } else { delta[0] = rc->initial_mouse[0] - rc->slow_mouse[0]; @@ -2970,7 +2969,7 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even dist = len_v2(delta); - delta[0] = event->x - rc->slow_mouse[0]; + delta[0] = event->xy[0] - rc->slow_mouse[0]; if (rc->zoom_prop) { delta[0] /= zoom[0]; @@ -2980,8 +2979,8 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even } } else { - delta[0] = rc->initial_mouse[0] - event->x; - delta[1] = rc->initial_mouse[1] - event->y; + delta[0] = (float)(rc->initial_mouse[0] - event->xy[0]); + delta[1] = (float)(rc->initial_mouse[1] - event->xy[1]); if (rc->zoom_prop) { RNA_property_float_get_array(&rc->zoom_ptr, rc->zoom_prop, zoom); delta[0] /= zoom[0]; @@ -3048,8 +3047,8 @@ static int radial_control_modal(bContext *C, wmOperator *op, const wmEvent *even case EVT_LEFTSHIFTKEY: case EVT_RIGHTSHIFTKEY: { if (event->val == KM_PRESS) { - rc->slow_mouse[0] = event->x; - rc->slow_mouse[1] = event->y; + rc->slow_mouse[0] = event->xy[0]; + rc->slow_mouse[1] = event->xy[1]; rc->slow_mode = true; if (rc->subtype == PROP_ANGLE) { const float initial_position[2] = {UNPACK2(rc->initial_mouse)}; @@ -3760,87 +3759,6 @@ static void WM_OT_stereo3d_set(wmOperatorType *ot) /** \} */ -#ifdef WITH_XR_OPENXR - -static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data) -{ - const bool session_exists = WM_xr_session_exists(xr_data); - - for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) { - if (slink->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)slink; - - if (v3d->flag & V3D_XR_SESSION_MIRROR) { - ED_view3d_xr_mirror_update(area, v3d, session_exists); - } - - if (session_exists) { - wmWindowManager *wm = bmain->wm.first; - const Scene *scene = WM_windows_scene_get_from_screen(wm, screen); - - ED_view3d_xr_shading_update(wm, v3d, scene); - } - /* Ensure no 3D View is tagged as session root. */ - else { - v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; - } - } - } - } - } - - WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); -} - -static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data) -{ - /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */ - wm_xr_session_update_screen(G_MAIN, xr_data); -} - -static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) -{ - Main *bmain = CTX_data_main(C); - wmWindowManager *wm = CTX_wm_manager(C); - wmWindow *win = CTX_wm_window(C); - View3D *v3d = CTX_wm_view3d(C); - - /* Lazy-create xr context - tries to dynlink to the runtime, reading active_runtime.json. */ - if (wm_xr_init(wm) == false) { - return OPERATOR_CANCELLED; - } - - v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT; - wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb); - wm_xr_session_update_screen(bmain, &wm->xr); - - WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL); - - return OPERATOR_FINISHED; -} - -static void WM_OT_xr_session_toggle(wmOperatorType *ot) -{ - /* identifiers */ - ot->name = "Toggle VR Session"; - ot->idname = "WM_OT_xr_session_toggle"; - ot->description = - "Open a view for use with virtual reality headsets, or close it if already " - "opened"; - - /* callbacks */ - ot->exec = wm_xr_session_toggle_exec; - ot->poll = ED_operator_view3d_active; - - /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the - * UI instead. Not meant as a permanent solution. */ - ot->flag = OPTYPE_INTERNAL; -} - -#endif /* WITH_XR_OPENXR */ - /* -------------------------------------------------------------------- */ /** \name Operator Registration & Keymaps * \{ */ @@ -3882,9 +3800,6 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_call_panel); WM_operatortype_append(WM_OT_radial_control); WM_operatortype_append(WM_OT_stereo3d_set); -#ifdef WITH_XR_OPENXR - WM_operatortype_append(WM_OT_xr_session_toggle); -#endif #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); #endif @@ -3892,6 +3807,10 @@ void wm_operatortypes_register(void) WM_operatortype_append(WM_OT_previews_clear); WM_operatortype_append(WM_OT_doc_view_manual_ui_context); +#ifdef WITH_XR_OPENXR + wm_xr_operatortypes_register(); +#endif + /* gizmos */ WM_operatortype_append(GIZMOGROUP_OT_gizmo_select); WM_operatortype_append(GIZMOGROUP_OT_gizmo_tweak); diff --git a/source/blender/windowmanager/intern/wm_playanim.c b/source/blender/windowmanager/intern/wm_playanim.c index fa21dbcb4bb..a41fa94e8c2 100644 --- a/source/blender/windowmanager/intern/wm_playanim.c +++ b/source/blender/windowmanager/intern/wm_playanim.c @@ -1845,7 +1845,6 @@ static char *wm_main_playanim_intern(int argc, const char **argv) } IMB_exit(); - BKE_images_exit(); DEG_free_node_types(); totblock = MEM_get_memory_blocks_in_use(); diff --git a/source/blender/windowmanager/intern/wm_splash_screen.c b/source/blender/windowmanager/intern/wm_splash_screen.c index 99c6bc39207..7513e4d31cc 100644 --- a/source/blender/windowmanager/intern/wm_splash_screen.c +++ b/source/blender/windowmanager/intern/wm_splash_screen.c @@ -212,8 +212,10 @@ static uiBlock *wm_block_create_splash(bContext *C, ARegion *region, void *UNUSE UI_but_func_set(but, wm_block_close, block, NULL); - wm_block_splash_add_label( - block, BKE_blender_version_string(), splash_width, splash_height - 13.0 * U.dpi_fac); + wm_block_splash_add_label(block, + BKE_blender_version_string(), + splash_width - 8.0 * U.dpi_fac, + splash_height - 13.0 * U.dpi_fac); const int layout_margin_x = U.dpi_fac * 26; uiLayout *layout = UI_block_layout(block, @@ -323,7 +325,7 @@ static uiBlock *wm_block_create_about(bContext *C, ARegion *region, void *UNUSED uiLayout *col = uiLayoutColumn(layout, true); - uiItemL_ex(col, N_("Blender"), ICON_NONE, true, false); + uiItemL_ex(col, IFACE_("Blender"), ICON_NONE, true, false); MenuType *mt = WM_menutype_find("WM_MT_splash_about", true); if (mt) { diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c index a7cdc0602bc..16a17484f4f 100644 --- a/source/blender/windowmanager/intern/wm_stereo.c +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -181,7 +181,7 @@ bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) * If needed, adjust \a r_mouse_xy * so that drawn cursor and handled mouse position are matching visually. */ -void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy) +void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2]) { if (!WM_stereo3d_enabled(win, false)) { return; diff --git a/source/blender/windowmanager/intern/wm_toolsystem.c b/source/blender/windowmanager/intern/wm_toolsystem.c index ca54895bb81..753f8687adc 100644 --- a/source/blender/windowmanager/intern/wm_toolsystem.c +++ b/source/blender/windowmanager/intern/wm_toolsystem.c @@ -333,7 +333,10 @@ void WM_toolsystem_ref_set_from_runtime(struct bContext *C, bool use_fallback_keymap = false; if (tref->idname_fallback[0] || tref->runtime->keymap_fallback[0]) { - if (tref_rt->gizmo_group[0]) { + if (tref_rt->flag & TOOLREF_FLAG_FALLBACK_KEYMAP) { + use_fallback_keymap = true; + } + else if (tref_rt->gizmo_group[0]) { wmGizmoGroupType *gzgt = WM_gizmogrouptype_find(tref_rt->gizmo_group, false); if (gzgt) { if (gzgt->flag & WM_GIZMOGROUPTYPE_TOOL_FALLBACK_KEYMAP) { @@ -576,25 +579,29 @@ void WM_toolsystem_refresh_screen_area(WorkSpace *workspace, ViewLayer *view_lay } } +void WM_toolsystem_refresh_screen_window(wmWindow *win) +{ + WorkSpace *workspace = WM_window_get_active_workspace(win); + bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0}; + LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { + space_type_has_tools[tref->space_type] = true; + } + bScreen *screen = WM_window_get_active_screen(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + area->runtime.tool = NULL; + area->runtime.is_tool_set = true; + if (space_type_has_tools[area->spacetype]) { + WM_toolsystem_refresh_screen_area(workspace, view_layer, area); + } + } +} + void WM_toolsystem_refresh_screen_all(Main *bmain) { /* Update all ScrArea's tools */ for (wmWindowManager *wm = bmain->wm.first; wm; wm = wm->id.next) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { - WorkSpace *workspace = WM_window_get_active_workspace(win); - bool space_type_has_tools[SPACE_TYPE_LAST + 1] = {0}; - LISTBASE_FOREACH (bToolRef *, tref, &workspace->tools) { - space_type_has_tools[tref->space_type] = true; - } - bScreen *screen = WM_window_get_active_screen(win); - ViewLayer *view_layer = WM_window_get_active_view_layer(win); - LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { - area->runtime.tool = NULL; - area->runtime.is_tool_set = true; - if (space_type_has_tools[area->spacetype]) { - WM_toolsystem_refresh_screen_area(workspace, view_layer, area); - } - } } } } diff --git a/source/blender/windowmanager/intern/wm_tooltip.c b/source/blender/windowmanager/intern/wm_tooltip.c index a9f0e01cfb5..3bcd122f08d 100644 --- a/source/blender/windowmanager/intern/wm_tooltip.c +++ b/source/blender/windowmanager/intern/wm_tooltip.c @@ -131,7 +131,7 @@ void WM_tooltip_init(bContext *C, wmWindow *win) CTX_wm_region_set(C, region_prev); } - copy_v2_v2_int(screen->tool_tip->event_xy, &win->eventstate->x); + copy_v2_v2_int(screen->tool_tip->event_xy, win->eventstate->xy); if (pass_prev != screen->tool_tip->pass) { /* The pass changed, add timer for next pass. */ wmWindowManager *wm = CTX_wm_manager(C); diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 887aed7ffc7..5f684a752d8 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -49,6 +49,7 @@ #include "BKE_icons.h" #include "BKE_layer.h" #include "BKE_main.h" +#include "BKE_report.h" #include "BKE_screen.h" #include "BKE_workspace.h" @@ -368,7 +369,8 @@ void wm_quit_with_optional_confirmation_prompt(bContext *C, wmWindow *win) CTX_wm_window_set(C, win); if (U.uiflag & USER_SAVE_PROMPT) { - if (wm_file_or_image_is_modified(CTX_data_main(C), CTX_wm_manager(C)) && !G.background) { + if (wm_file_or_session_data_has_unsaved_changes(CTX_data_main(C), CTX_wm_manager(C)) && + !G.background) { wm_window_raise(win); wm_confirm_quit(C); } @@ -520,7 +522,7 @@ void WM_window_set_dpi(const wmWindow *win) static void wm_window_update_eventstate(wmWindow *win) { /* Update mouse position when a window is activated. */ - wm_cursor_position_get(win, &win->eventstate->x, &win->eventstate->y); + wm_cursor_position_get(win, &win->eventstate->xy[0], &win->eventstate->xy[1]); } static void wm_window_ensure_eventstate(wmWindow *win) @@ -772,7 +774,7 @@ wmWindow *WM_window_open(bContext *C, bool toplevel, bool dialog, bool temp, - WindowAlignment alignment) + eWindowAlignment alignment) { Main *bmain = CTX_data_main(C); wmWindowManager *wm = CTX_wm_manager(C); @@ -852,7 +854,8 @@ wmWindow *WM_window_open(bContext *C, /* Set scene and view layer to match original window. */ STRNCPY(win->view_layer_name, view_layer->name); if (WM_window_get_active_scene(win) != scene) { - ED_screen_scene_change(C, win, scene); + /* No need to refresh the tool-system as the window has not yet finished being setup. */ + ED_screen_scene_change(C, win, scene, false); } screen->temp = temp; @@ -913,7 +916,7 @@ int wm_window_close_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op)) +int wm_window_new_exec(bContext *C, wmOperator *op) { wmWindow *win_src = CTX_wm_window(C); ScrArea *area = BKE_screen_find_big_area(CTX_wm_screen(C), SPACE_TYPE_ANY, 0); @@ -930,16 +933,23 @@ int wm_window_new_exec(bContext *C, wmOperator *UNUSED(op)) false, WIN_ALIGN_PARENT_CENTER) != NULL); - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + if (!ok) { + BKE_report(op->reports, RPT_ERROR, "Failed to create window"); + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } -int wm_window_new_main_exec(bContext *C, wmOperator *UNUSED(op)) +int wm_window_new_main_exec(bContext *C, wmOperator *op) { wmWindow *win_src = CTX_wm_window(C); bool ok = (wm_window_copy_test(C, win_src, true, false) != NULL); - - return ok ? OPERATOR_FINISHED : OPERATOR_CANCELLED; + if (!ok) { + BKE_report(op->reports, RPT_ERROR, "Failed to create window"); + return OPERATOR_CANCELLED; + } + return OPERATOR_FINISHED; } /* fullscreen operator callback */ @@ -989,8 +999,8 @@ void wm_cursor_position_to_ghost(wmWindow *win, int *x, int *y) void wm_cursor_position_get(wmWindow *win, int *r_x, int *r_y) { if (UNLIKELY(G.f & G_FLAG_EVENT_SIMULATE)) { - *r_x = win->eventstate->x; - *r_y = win->eventstate->y; + *r_x = win->eventstate->xy[0]; + *r_y = win->eventstate->xy[1]; return; } GHOST_GetCursorPosition(g_system, r_x, r_y); @@ -1265,8 +1275,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr wmEvent event; wm_event_init_from_window(win, &event); event.type = MOUSEMOVE; - event.prevx = event.x; - event.prevy = event.y; + copy_v2_v2_int(event.prev_xy, event.xy); event.is_repeat = false; wm_event_add(win, &event); @@ -1397,8 +1406,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr /* activate region */ event.type = MOUSEMOVE; - event.prevx = event.x; - event.prevy = event.y; + copy_v2_v2_int(event.prev_xy, event.xy); event.is_repeat = false; /* No context change! C->wm->windrawable is drawable, or for area queues. */ @@ -1413,7 +1421,7 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr C_void_ptr event.val = KM_RELEASE; event.custom = EVT_DATA_DRAGDROP; event.customdata = &wm->drags; - event.customdatafree = 1; + event.customdata_free = true; wm_event_add(win, &event); @@ -2098,11 +2106,11 @@ void WM_cursor_warp(wmWindow *win, int x, int y) wm_cursor_position_to_ghost(win, &x, &y); GHOST_SetCursorPosition(g_system, x, y); - win->eventstate->prevx = oldx; - win->eventstate->prevy = oldy; + win->eventstate->prev_xy[0] = oldx; + win->eventstate->prev_xy[1] = oldy; - win->eventstate->x = oldx; - win->eventstate->y = oldy; + win->eventstate->xy[0] = oldx; + win->eventstate->xy[1] = oldy; } } @@ -2264,13 +2272,13 @@ void WM_window_set_active_scene(Main *bmain, bContext *C, wmWindow *win, Scene * /* Set scene in parent and its child windows. */ if (win_parent->scene != scene) { - ED_screen_scene_change(C, win_parent, scene); + ED_screen_scene_change(C, win_parent, scene, true); changed = true; } LISTBASE_FOREACH (wmWindow *, win_child, &wm->windows) { if (win_child->parent == win_parent && win_child->scene != scene) { - ED_screen_scene_change(C, win_child, scene); + ED_screen_scene_change(C, win_child, scene, true); changed = true; } } @@ -2426,10 +2434,15 @@ void wm_window_IME_end(wmWindow *win) void *WM_opengl_context_create(void) { - /* On Windows there is a problem creating contexts that share lists - * from one context that is current in another thread. - * So we should call this function only on the main thread. - */ + /* On Windows there is a problem creating contexts that share resources (almost any object, + * including legacy display lists, but also textures) with a context which is current in another + * thread. This is a documented and behavior of both `::wglCreateContextAttribsARB()` and + * `::wglShareLists()`. + * + * Other platforms might successfully share resources from context which is active somewhere + * else, but to keep our code behave the same on all platform we expect contexts to only be + * created from the main thread. */ + BLI_assert(BLI_thread_is_main()); BLI_assert(GPU_framebuffer_active_get() == GPU_framebuffer_back_get()); diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index af696ddd8f9..cfef70b7dcc 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -86,7 +86,7 @@ void WM_OT_splash_about(wmOperatorType *ot); void wm_stereo3d_draw_sidebyside(wmWindow *win, int view); void wm_stereo3d_draw_topbottom(wmWindow *win, int view); -void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy); +void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2]); int wm_stereo3d_set_exec(bContext *C, wmOperator *op); int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event); void wm_stereo3d_set_draw(bContext *C, wmOperator *op); diff --git a/source/blender/windowmanager/wm_event_system.h b/source/blender/windowmanager/wm_event_system.h index 787c840de8a..40e4d905fcd 100644 --- a/source/blender/windowmanager/wm_event_system.h +++ b/source/blender/windowmanager/wm_event_system.h @@ -33,6 +33,10 @@ struct ARegion; struct GHOST_TabletData; struct ScrArea; +#ifdef WITH_XR_OPENXR +struct wmXrActionData; +#endif + #ifdef __cplusplus extern "C" { #endif @@ -147,6 +151,9 @@ void wm_event_free_handler(wmEventHandler *handler); void wm_event_do_handlers(bContext *C); void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, void *customdata); +#ifdef WITH_XR_OPENXR +void wm_event_add_xrevent(wmWindow *win, struct wmXrActionData *actiondata, short val); +#endif void wm_event_do_depsgraph(bContext *C, bool is_after_open_file); void wm_event_do_refresh_wm_and_depsgraph(bContext *C); @@ -165,8 +172,10 @@ void wm_tablet_data_from_ghost(const struct GHOST_TabletData *tablet_data, wmTab /* wm_dropbox.c */ void wm_dropbox_free(void); +void wm_drags_exit(wmWindowManager *wm, wmWindow *win); +void wm_drop_prepare(bContext *C, wmDrag *drag, wmDropBox *drop); void wm_drags_check_ops(bContext *C, const wmEvent *event); -void wm_drags_draw(bContext *C, wmWindow *win, rcti *rect); +void wm_drags_draw(bContext *C, wmWindow *win); #ifdef __cplusplus } diff --git a/source/blender/windowmanager/wm_event_types.h b/source/blender/windowmanager/wm_event_types.h index 905c57d901a..c175c211db6 100644 --- a/source/blender/windowmanager/wm_event_types.h +++ b/source/blender/windowmanager/wm_event_types.h @@ -34,6 +34,7 @@ enum { EVT_DATA_TIMER = 2, EVT_DATA_DRAGDROP = 3, EVT_DATA_NDOF_MOTION = 4, + EVT_DATA_XR = 5, }; /* tablet active, matches GHOST_TTabletMode */ @@ -341,6 +342,9 @@ enum { /* could become gizmo callback */ EVT_GIZMO_UPDATE = 0x5025, /* 20517 */ + + /* XR events: 0x503x */ + EVT_XR_ACTION = 0x5030, /* 20528 */ /* ********** End of Blender internal events. ********** */ }; diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 2fa5a68829e..135f31cf8ac 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -79,7 +79,7 @@ void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); bool wm_operator_close_file_dialog_if_needed(bContext *C, wmOperator *op, wmGenericCallbackFn exec_fn); -bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm); +bool wm_file_or_session_data_has_unsaved_changes(const Main *bmain, const wmWindowManager *wm); void WM_OT_save_homefile(struct wmOperatorType *ot); void WM_OT_save_userpref(struct wmOperatorType *ot); diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 8891840cb75..36bd03ed3ea 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -24,19 +24,22 @@ #include "BKE_global.h" #include "BKE_idprop.h" +#include "BKE_main.h" #include "BKE_report.h" +#include "DEG_depsgraph.h" + #include "DNA_scene_types.h" #include "DNA_windowmanager_types.h" -#include "DEG_depsgraph.h" - -#include "MEM_guardedalloc.h" +#include "ED_screen.h" #include "GHOST_C-api.h" #include "GPU_platform.h" +#include "MEM_guardedalloc.h" + #include "WM_api.h" #include "wm_surface.h" @@ -137,7 +140,7 @@ bool wm_xr_events_handle(wmWindowManager *wm) /* Process OpenXR action events. */ if (WM_xr_session_is_ready(&wm->xr)) { - wm_xr_session_actions_update(&wm->xr); + wm_xr_session_actions_update(wm); } /* wm_window_process_events() uses the return value to determine if it can put the main thread @@ -172,6 +175,12 @@ void wm_xr_runtime_data_free(wmXrRuntimeData **runtime) * first call, see comment above. */ (*runtime)->context = NULL; + if ((*runtime)->area) { + wmWindowManager *wm = G_MAIN->wm.first; + wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, (*runtime)); + ED_area_offscreen_free(wm, win, (*runtime)->area); + (*runtime)->area = NULL; + } wm_xr_session_data_free(&(*runtime)->session_state); WM_xr_actionmaps_clear(*runtime); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_action.c b/source/blender/windowmanager/xr/intern/wm_xr_action.c index ba347c537ec..b5a606b4298 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_action.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_action.c @@ -118,7 +118,7 @@ static wmXrAction *action_create(const char *action_name, action->states = MEM_calloc_arrayN(count, size, "XrAction_States"); action->states_prev = MEM_calloc_arrayN(count, size, "XrAction_StatesPrev"); - const bool is_float_action = (type == XR_FLOAT_INPUT || type == XR_VECTOR2F_INPUT); + const bool is_float_action = ELEM(type, XR_FLOAT_INPUT, XR_VECTOR2F_INPUT); const bool is_button_action = (is_float_action || type == XR_BOOLEAN_INPUT); if (is_float_action) { action->float_thresholds = MEM_calloc_arrayN( diff --git a/source/blender/windowmanager/xr/intern/wm_xr_draw.c b/source/blender/windowmanager/xr/intern/wm_xr_draw.c index bbb73fc2007..72d88bb3043 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_draw.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_draw.c @@ -24,12 +24,17 @@ #include +#include "BKE_context.h" + #include "BLI_listbase.h" #include "BLI_math.h" #include "ED_view3d_offscreen.h" #include "GHOST_C-api.h" +#include "GPU_batch_presets.h" +#include "GPU_immediate.h" +#include "GPU_matrix.h" #include "GPU_viewport.h" @@ -44,6 +49,16 @@ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]) copy_v3_v3(r_mat[3], pose->position); } +void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]) +{ + wm_xr_pose_to_mat(pose, r_mat); + + BLI_assert(scale > 0.0f); + mul_v3_fl(r_mat[0], scale); + mul_v3_fl(r_mat[1], scale); + mul_v3_fl(r_mat[2], scale); +} + void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) { float iquat[4]; @@ -52,15 +67,32 @@ void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]) translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); } +void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]) +{ + float iquat[4]; + invert_qt_qt_normalized(iquat, pose->orientation_quat); + quat_to_mat4(r_imat, iquat); + + BLI_assert(scale > 0.0f); + scale = 1.0f / scale; + mul_v3_fl(r_imat[0], scale); + mul_v3_fl(r_imat[1], scale); + mul_v3_fl(r_imat[2], scale); + + translate_m4(r_imat, -pose->position[0], -pose->position[1], -pose->position[2]); +} + static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, const GHOST_XrDrawViewInfo *draw_view, const XrSessionSettings *session_settings, - float r_view_mat[4][4], - float r_proj_mat[4][4]) + const wmXrSessionState *session_state, + float r_viewmat[4][4], + float r_projmat[4][4]) { GHOST_XrPose eye_pose; - float eye_inv[4][4], base_inv[4][4]; + float eye_inv[4][4], base_inv[4][4], nav_inv[4][4], m[4][4]; + /* Calculate inverse eye matrix. */ copy_qt_qt(eye_pose.orientation_quat, draw_view->eye_pose.orientation_quat); copy_v3_v3(eye_pose.position, draw_view->eye_pose.position); if ((session_settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { @@ -71,12 +103,14 @@ static void wm_xr_draw_matrices_create(const wmXrDrawData *draw_data, } wm_xr_pose_to_imat(&eye_pose, eye_inv); - /* Calculate the base pose matrix (in world space!). */ - wm_xr_pose_to_imat(&draw_data->base_pose, base_inv); - mul_m4_m4m4(r_view_mat, eye_inv, base_inv); + /* Apply base pose and navigation. */ + wm_xr_pose_scale_to_imat(&draw_data->base_pose, draw_data->base_scale, base_inv); + wm_xr_pose_scale_to_imat(&session_state->nav_pose_prev, session_state->nav_scale_prev, nav_inv); + mul_m4_m4m4(m, eye_inv, base_inv); + mul_m4_m4m4(r_viewmat, m, nav_inv); - perspective_m4_fov(r_proj_mat, + perspective_m4_fov(r_projmat, draw_view->fov.angle_left, draw_view->fov.angle_right, draw_view->fov.angle_up, @@ -126,7 +160,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) BLI_assert(WM_xr_session_is_ready(xr_data)); wm_xr_session_draw_data_update(session_state, settings, draw_view, draw_data); - wm_xr_draw_matrices_create(draw_data, draw_view, settings, viewmat, winmat); + wm_xr_draw_matrices_create(draw_data, draw_view, settings, session_state, viewmat, winmat); wm_xr_session_state_update(settings, draw_data, draw_view, session_state); if (!wm_xr_session_surface_offscreen_ensure(surface_data, draw_view)) { @@ -145,7 +179,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) ED_view3d_draw_offscreen_simple(draw_data->depsgraph, draw_data->scene, &settings->shading, - settings->shading.type, + (eDrawType)settings->shading.type, draw_view->width, draw_view->height, display_flags, @@ -153,6 +187,7 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) winmat, settings->clip_start, settings->clip_end, + true, false, true, NULL, @@ -172,3 +207,200 @@ void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata) wm_xr_draw_viewport_buffers_to_active_framebuffer(xr_data->runtime, surface_data, draw_view); } + +static GPUBatch *wm_xr_controller_model_batch_create(GHOST_XrContextHandle xr_context, + const char *subaction_path) +{ + GHOST_XrControllerModelData model_data; + + if (!GHOST_XrGetControllerModelData(xr_context, subaction_path, &model_data) || + model_data.count_vertices < 1) { + return NULL; + } + + GPUVertFormat format = {0}; + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "nor", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + GPUVertBuf *vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(vbo, model_data.count_vertices); + void *vbo_data = GPU_vertbuf_get_data(vbo); + memcpy( + vbo_data, model_data.vertices, model_data.count_vertices * sizeof(model_data.vertices[0])); + + GPUIndexBuf *ibo = NULL; + if (model_data.count_indices > 0 && ((model_data.count_indices % 3) == 0)) { + GPUIndexBufBuilder ibo_builder; + const unsigned int prim_len = model_data.count_indices / 3; + GPU_indexbuf_init(&ibo_builder, GPU_PRIM_TRIS, prim_len, model_data.count_vertices); + for (unsigned int i = 0; i < prim_len; ++i) { + const uint32_t *idx = &model_data.indices[i * 3]; + GPU_indexbuf_add_tri_verts(&ibo_builder, idx[0], idx[1], idx[2]); + } + ibo = GPU_indexbuf_build(&ibo_builder); + } + + return GPU_batch_create_ex(GPU_PRIM_TRIS, vbo, ibo, GPU_BATCH_OWNS_VBO | GPU_BATCH_OWNS_INDEX); +} + +static void wm_xr_controller_model_draw(const XrSessionSettings *settings, + GHOST_XrContextHandle xr_context, + wmXrSessionState *state) +{ + GHOST_XrControllerModelData model_data; + + float color[4]; + switch (settings->controller_draw_style) { + case XR_CONTROLLER_DRAW_DARK: + case XR_CONTROLLER_DRAW_DARK_RAY: + color[0] = color[1] = color[2] = 0.0f, color[3] = 0.4f; + break; + case XR_CONTROLLER_DRAW_LIGHT: + case XR_CONTROLLER_DRAW_LIGHT_RAY: + color[0] = 0.422f, color[1] = 0.438f, color[2] = 0.446f, color[3] = 0.4f; + break; + } + + GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_ALPHA); + + LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + GPUBatch *model = controller->model; + if (!model) { + model = controller->model = wm_xr_controller_model_batch_create(xr_context, + controller->subaction_path); + } + + if (model && + GHOST_XrGetControllerModelData(xr_context, controller->subaction_path, &model_data) && + model_data.count_components > 0) { + GPU_batch_program_set_builtin(model, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(model, "color", color); + + GPU_matrix_push(); + GPU_matrix_mul(controller->grip_mat); + for (unsigned int component_idx = 0; component_idx < model_data.count_components; + ++component_idx) { + const GHOST_XrControllerModelComponent *component = &model_data.components[component_idx]; + GPU_matrix_push(); + GPU_matrix_mul(component->transform); + GPU_batch_draw_range(model, + model->elem ? component->index_offset : component->vertex_offset, + model->elem ? component->index_count : component->vertex_count); + GPU_matrix_pop(); + } + GPU_matrix_pop(); + } + else { + /* Fallback. */ + const float scale = 0.05f; + GPUBatch *sphere = GPU_batch_preset_sphere(2); + GPU_batch_program_set_builtin(sphere, GPU_SHADER_3D_UNIFORM_COLOR); + GPU_batch_uniform_4fv(sphere, "color", color); + + GPU_matrix_push(); + GPU_matrix_mul(controller->grip_mat); + GPU_matrix_scale_1f(scale); + GPU_batch_draw(sphere); + GPU_matrix_pop(); + } + } +} + +static void wm_xr_controller_aim_draw(const XrSessionSettings *settings, wmXrSessionState *state) +{ + bool draw_ray; + switch (settings->controller_draw_style) { + case XR_CONTROLLER_DRAW_DARK: + case XR_CONTROLLER_DRAW_LIGHT: + draw_ray = false; + break; + case XR_CONTROLLER_DRAW_DARK_RAY: + case XR_CONTROLLER_DRAW_LIGHT_RAY: + draw_ray = true; + break; + } + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR); + + float viewport[4]; + GPU_viewport_size_get_f(viewport); + immUniform2fv("viewportSize", &viewport[2]); + + immUniform1f("lineWidth", 3.0f * U.pixelsize); + + if (draw_ray) { + const uchar color[4] = {89, 89, 255, 127}; + const float scale = settings->clip_end; + float ray[3]; + + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + GPU_blend(GPU_BLEND_ALPHA); + + immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 2); + + LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + const float(*mat)[4] = controller->aim_mat; + madd_v3_v3v3fl(ray, mat[3], mat[2], -scale); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, color); + immVertex3fv(pos, ray); + } + + immEnd(); + } + else { + const uchar r[4] = {255, 51, 82, 255}; + const uchar g[4] = {139, 220, 0, 255}; + const uchar b[4] = {40, 144, 255, 255}; + const float scale = 0.01f; + float x_axis[3], y_axis[3], z_axis[3]; + + GPU_depth_test(GPU_DEPTH_NONE); + GPU_blend(GPU_BLEND_NONE); + + immBegin(GPU_PRIM_LINES, (uint)BLI_listbase_count(&state->controllers) * 6); + + LISTBASE_FOREACH (wmXrController *, controller, &state->controllers) { + const float(*mat)[4] = controller->aim_mat; + madd_v3_v3v3fl(x_axis, mat[3], mat[0], scale); + madd_v3_v3v3fl(y_axis, mat[3], mat[1], scale); + madd_v3_v3v3fl(z_axis, mat[3], mat[2], scale); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, r); + immVertex3fv(pos, x_axis); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, g); + immVertex3fv(pos, y_axis); + + immAttrSkip(col); + immVertex3fv(pos, mat[3]); + immAttr4ubv(col, b); + immVertex3fv(pos, z_axis); + } + + immEnd(); + } + + immUnbindProgram(); +} + +void wm_xr_draw_controllers(const bContext *UNUSED(C), ARegion *UNUSED(region), void *customdata) +{ + wmXrData *xr = customdata; + const XrSessionSettings *settings = &xr->session_settings; + GHOST_XrContextHandle xr_context = xr->runtime->context; + wmXrSessionState *state = &xr->runtime->session_state; + + wm_xr_controller_model_draw(settings, xr_context, state); + wm_xr_controller_aim_draw(settings, state); +} diff --git a/source/blender/windowmanager/xr/intern/wm_xr_intern.h b/source/blender/windowmanager/xr/intern/wm_xr_intern.h index fc54e261f79..7de1f254224 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_intern.h +++ b/source/blender/windowmanager/xr/intern/wm_xr_intern.h @@ -33,16 +33,20 @@ typedef struct wmXrSessionState { GHOST_XrPose viewer_pose; /** The last known view matrix, calculated from above's viewer pose. */ float viewer_viewmat[4][4]; + /** The last known viewer matrix, without navigation applied. */ + float viewer_mat_base[4][4]; float focal_len; /** Copy of XrSessionSettings.base_pose_ data to detect changes that need * resetting to base pose. */ - char prev_base_pose_type; /* eXRSessionBasePoseType */ + char prev_base_pose_type; /* #eXRSessionBasePoseType */ Object *prev_base_pose_object; /** Copy of XrSessionSettings.flag created on the last draw call, stored to detect changes. */ int prev_settings_flag; /** Copy of wmXrDrawData.base_pose. */ GHOST_XrPose prev_base_pose; + /** Copy of wmXrDrawData.base_scale. */ + float prev_base_scale; /** Copy of GHOST_XrDrawViewInfo.local_pose. */ GHOST_XrPose prev_local_pose; /** Copy of wmXrDrawData.eye_position_ofs. */ @@ -51,8 +55,17 @@ typedef struct wmXrSessionState { bool force_reset_to_base_pose; bool is_view_data_set; + /** Current navigation transforms. */ + GHOST_XrPose nav_pose; + float nav_scale; + /** Navigation transforms from the last actions sync, used to calculate the viewer/controller + * poses. */ + GHOST_XrPose nav_pose_prev; + float nav_scale_prev; + bool is_navigation_dirty; + /** Last known controller data. */ - ListBase controllers; /* wmXrController */ + ListBase controllers; /* #wmXrController */ /** The currently active action set that will be updated on calls to * wm_xr_session_actions_update(). If NULL, all action sets will be treated as active and @@ -67,11 +80,14 @@ typedef struct wmXrRuntimeData { * be an invalid reference, i.e. the window may have been closed. */ wmWindow *session_root_win; + /** Off-screen area used for XR events. */ + struct ScrArea *area; + /** Although this struct is internal, RNA gets a handle to this for state information queries. */ wmXrSessionState session_state; wmXrSessionExitFn exit_fn; - ListBase actionmaps; /* XrActionMap */ + ListBase actionmaps; /* #XrActionMap */ short actactionmap; short selactionmap; } wmXrRuntimeData; @@ -84,7 +100,12 @@ typedef struct wmXrViewportPair { typedef struct { /** Off-screen buffers/viewports for each view. */ - ListBase viewports; /* wmXrViewportPair */ + ListBase viewports; /* #wmXrViewportPair */ + + /** Dummy region type for controller draw callback. */ + struct ARegionType *controller_art; + /** Controller draw callback handle. */ + void *controller_draw_handle; } wmXrSurfaceData; typedef struct wmXrDrawData { @@ -98,6 +119,8 @@ typedef struct wmXrDrawData { * space). With positional tracking enabled, it should be the same as the base pose, when * disabled it also contains a location delta from the moment the option was toggled. */ GHOST_XrPose base_pose; + /** Base scale (uniform, world space). */ + float base_scale; /** Offset to _substract_ from the OpenXR eye and viewer pose to get the wanted effective pose * (e.g. a pose exactly at the landmark position). */ float eye_position_ofs[3]; /* Local/view space. */ @@ -111,12 +134,18 @@ typedef struct wmXrController { /input/trigger/value, interaction_path = /user/hand/left/input/trigger/value). */ char subaction_path[64]; - /* Pose (in world space) that represents the user's hand when holding the controller.*/ + + /** Pose (in world space) that represents the user's hand when holding the controller. */ GHOST_XrPose grip_pose; float grip_mat[4][4]; - /* Pose (in world space) that represents the controller's aiming source. */ + float grip_mat_base[4][4]; + /** Pose (in world space) that represents the controller's aiming source. */ GHOST_XrPose aim_pose; float aim_mat[4][4]; + float aim_mat_base[4][4]; + + /** Controller model. */ + struct GPUBatch *model; } wmXrController; typedef struct wmXrAction { @@ -172,11 +201,15 @@ typedef struct wmXrActionSet { ListBase active_haptic_actions; } wmXrActionSet; +/* wm_xr.c */ wmXrRuntimeData *wm_xr_runtime_data_create(void); void wm_xr_runtime_data_free(wmXrRuntimeData **runtime); -void wm_xr_session_data_free(wmXrSessionState *state); -void wm_xr_session_draw_data_update(const wmXrSessionState *state, +/* wm_xr_session.c */ +void wm_xr_session_data_free(wmXrSessionState *state); +wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, + const wmXrRuntimeData *runtime_data); +void wm_xr_session_draw_data_update(wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, wmXrDrawData *draw_data); @@ -190,12 +223,16 @@ void *wm_xr_session_gpu_binding_context_create(void); void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle context); void wm_xr_session_actions_init(wmXrData *xr); -void wm_xr_session_actions_update(wmXrData *xr); +void wm_xr_session_actions_update(wmWindowManager *wm); void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, const wmXrAction *aim_action, wmXrData *xr); void wm_xr_session_controller_data_clear(wmXrSessionState *state); +/* wm_xr_draw.c */ void wm_xr_pose_to_mat(const GHOST_XrPose *pose, float r_mat[4][4]); +void wm_xr_pose_scale_to_mat(const GHOST_XrPose *pose, float scale, float r_mat[4][4]); void wm_xr_pose_to_imat(const GHOST_XrPose *pose, float r_imat[4][4]); +void wm_xr_pose_scale_to_imat(const GHOST_XrPose *pose, float scale, float r_imat[4][4]); void wm_xr_draw_view(const GHOST_XrDrawViewInfo *draw_view, void *customdata); +void wm_xr_draw_controllers(const struct bContext *C, struct ARegion *region, void *customdata); diff --git a/source/blender/windowmanager/xr/intern/wm_xr_operators.c b/source/blender/windowmanager/xr/intern/wm_xr_operators.c new file mode 100644 index 00000000000..112312bab7b --- /dev/null +++ b/source/blender/windowmanager/xr/intern/wm_xr_operators.c @@ -0,0 +1,1537 @@ +/* + * 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 wm + * + * \name Window-Manager XR Operators + * + * Collection of XR-related operators. + */ + +#include "BLI_kdopbvh.h" +#include "BLI_listbase.h" +#include "BLI_math.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_idprop.h" +#include "BKE_main.h" +#include "BKE_screen.h" + +#include "DEG_depsgraph.h" + +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_transform_snap_object_context.h" +#include "ED_view3d.h" + +#include "GHOST_Types.h" + +#include "GPU_immediate.h" + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "wm_xr_intern.h" + +/* -------------------------------------------------------------------- */ +/** \name Operator Conditions + * \{ */ + +/* op->poll */ +static bool wm_xr_operator_sessionactive(bContext *C) +{ + wmWindowManager *wm = CTX_wm_manager(C); + return WM_xr_session_is_ready(&wm->xr); +} + +static bool wm_xr_operator_test_event(const wmOperator *op, const wmEvent *event) +{ + if (event->type != EVT_XR_ACTION) { + return false; + } + + BLI_assert(event->custom == EVT_DATA_XR); + BLI_assert(event->customdata); + + wmXrActionData *actiondata = event->customdata; + return (actiondata->ot == op->type && + IDP_EqualsProperties(actiondata->op_properties, op->properties)); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Session Toggle + * + * Toggles an XR session, creating an XR context if necessary. + * \{ */ + +static void wm_xr_session_update_screen(Main *bmain, const wmXrData *xr_data) +{ + const bool session_exists = WM_xr_session_exists(xr_data); + + for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + LISTBASE_FOREACH (SpaceLink *, slink, &area->spacedata) { + if (slink->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)slink; + + if (v3d->flag & V3D_XR_SESSION_MIRROR) { + ED_view3d_xr_mirror_update(area, v3d, session_exists); + } + + if (session_exists) { + wmWindowManager *wm = bmain->wm.first; + const Scene *scene = WM_windows_scene_get_from_screen(wm, screen); + + ED_view3d_xr_shading_update(wm, v3d, scene); + } + /* Ensure no 3D View is tagged as session root. */ + else { + v3d->runtime.flag &= ~V3D_RUNTIME_XR_SESSION_ROOT; + } + } + } + } + } + + WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); +} + +static void wm_xr_session_update_screen_on_exit_cb(const wmXrData *xr_data) +{ + /* Just use G_MAIN here, storing main isn't reliable enough on file read or exit. */ + wm_xr_session_update_screen(G_MAIN, xr_data); +} + +static int wm_xr_session_toggle_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Main *bmain = CTX_data_main(C); + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + View3D *v3d = CTX_wm_view3d(C); + + /* Lazily-create XR context - tries to dynamic-link to the runtime, + * reading `active_runtime.json`. */ + if (wm_xr_init(wm) == false) { + return OPERATOR_CANCELLED; + } + + v3d->runtime.flag |= V3D_RUNTIME_XR_SESSION_ROOT; + wm_xr_session_toggle(wm, win, wm_xr_session_update_screen_on_exit_cb); + wm_xr_session_update_screen(bmain, &wm->xr); + + WM_event_add_notifier(C, NC_WM | ND_XR_DATA_CHANGED, NULL); + + return OPERATOR_FINISHED; +} + +static void WM_OT_xr_session_toggle(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Toggle VR Session"; + ot->idname = "WM_OT_xr_session_toggle"; + ot->description = + "Open a view for use with virtual reality headsets, or close it if already " + "opened"; + + /* callbacks */ + ot->exec = wm_xr_session_toggle_exec; + ot->poll = ED_operator_view3d_active; + + /* XXX INTERNAL just to hide it from the search menu by default, an Add-on will expose it in the + * UI instead. Not meant as a permanent solution. */ + ot->flag = OPTYPE_INTERNAL; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Grab Utilities + * \{ */ + +typedef struct XrGrabData { + float mat_prev[4][4]; + float mat_other_prev[4][4]; + bool bimanual_prev; + bool loc_lock, locz_lock, rot_lock, rotz_lock, scale_lock; +} XrGrabData; + +static void wm_xr_grab_init(wmOperator *op) +{ + BLI_assert(op->customdata == NULL); + + op->customdata = MEM_callocN(sizeof(XrGrabData), __func__); +} + +static void wm_xr_grab_uninit(wmOperator *op) +{ + MEM_SAFE_FREE(op->customdata); +} + +static void wm_xr_grab_update(wmOperator *op, const wmXrActionData *actiondata) +{ + XrGrabData *data = op->customdata; + + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + + if (actiondata->bimanual) { + quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other); + copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other); + data->bimanual_prev = true; + } + else { + data->bimanual_prev = false; + } +} + +static void orient_mat_z_normalized(float R[4][4], const float z_axis[3]) +{ + const float scale = len_v3(R[0]); + float x_axis[3], y_axis[3]; + + cross_v3_v3v3(y_axis, z_axis, R[0]); + normalize_v3(y_axis); + mul_v3_v3fl(R[1], y_axis, scale); + + cross_v3_v3v3(x_axis, R[1], z_axis); + normalize_v3(x_axis); + mul_v3_v3fl(R[0], x_axis, scale); + + mul_v3_v3fl(R[2], z_axis, scale); +} + +static void wm_xr_navlocks_apply(const float nav_mat[4][4], + const float nav_inv[4][4], + bool loc_lock, + bool locz_lock, + bool rotz_lock, + float r_prev[4][4], + float r_curr[4][4]) +{ + /* Locked in base pose coordinates. */ + float prev_base[4][4], curr_base[4][4]; + + mul_m4_m4m4(prev_base, nav_inv, r_prev); + mul_m4_m4m4(curr_base, nav_inv, r_curr); + + if (rotz_lock) { + const float z_axis[3] = {0.0f, 0.0f, 1.0f}; + orient_mat_z_normalized(prev_base, z_axis); + orient_mat_z_normalized(curr_base, z_axis); + } + + if (loc_lock) { + copy_v3_v3(curr_base[3], prev_base[3]); + } + else if (locz_lock) { + curr_base[3][2] = prev_base[3][2]; + } + + mul_m4_m4m4(r_prev, nav_mat, prev_base); + mul_m4_m4m4(r_curr, nav_mat, curr_base); +} + +/** + * Compute transformation delta for a one-handed grab interaction. + * + * \param actiondata: Contains current controller pose in world space. + * \param data: Contains previous controller pose in world space. + * + * The delta is computed as the difference between the current and previous + * controller poses i.e. delta = curr * prev^-1. + */ +static void wm_xr_grab_compute(const wmXrActionData *actiondata, + const XrGrabData *data, + const float nav_mat[4][4], + const float nav_inv[4][4], + bool reverse, + float r_delta[4][4]) +{ + const bool nav_lock = (nav_mat && nav_inv); + float prev[4][4], curr[4][4]; + + if (!data->rot_lock) { + copy_m4_m4(prev, data->mat_prev); + zero_v3(prev[3]); + quat_to_mat4(curr, actiondata->controller_rot); + } + else { + unit_m4(prev); + unit_m4(curr); + } + + if (!data->loc_lock || nav_lock) { + copy_v3_v3(prev[3], data->mat_prev[3]); + copy_v3_v3(curr[3], actiondata->controller_loc); + } + + if (nav_lock) { + wm_xr_navlocks_apply( + nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); + } + + if (reverse) { + invert_m4(curr); + mul_m4_m4m4(r_delta, prev, curr); + } + else { + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); + } +} + +/** + * Compute transformation delta for a two-handed (bimanual) grab interaction. + * + * \param actiondata: Contains current controller poses in world space. + * \param data: Contains previous controller poses in world space. + * + * The delta is computed as the difference (delta = curr * prev^-1) between the current + * and previous transformations, where the transformations themselves are determined as follows: + * - Translation: Averaged controller positions. + * - Rotation: Rotation of axis line between controllers. + * - Scale: Distance between controllers. + */ +static void wm_xr_grab_compute_bimanual(const wmXrActionData *actiondata, + const XrGrabData *data, + const float nav_mat[4][4], + const float nav_inv[4][4], + bool reverse, + float r_delta[4][4]) +{ + const bool nav_lock = (nav_mat && nav_inv); + float prev[4][4], curr[4][4]; + unit_m4(prev); + unit_m4(curr); + + if (!data->rot_lock) { + /* Rotation. */ + float x_axis_prev[3], x_axis_curr[3], y_axis_prev[3], y_axis_curr[3], z_axis_prev[3], + z_axis_curr[3]; + float m0[3][3], m1[3][3]; + quat_to_mat3(m0, actiondata->controller_rot); + quat_to_mat3(m1, actiondata->controller_rot_other); + + /* x-axis is the base line between the two controllers. */ + sub_v3_v3v3(x_axis_prev, data->mat_prev[3], data->mat_other_prev[3]); + sub_v3_v3v3(x_axis_curr, actiondata->controller_loc, actiondata->controller_loc_other); + /* y-axis is the average of the controllers' y-axes. */ + add_v3_v3v3(y_axis_prev, data->mat_prev[1], data->mat_other_prev[1]); + mul_v3_fl(y_axis_prev, 0.5f); + add_v3_v3v3(y_axis_curr, m0[1], m1[1]); + mul_v3_fl(y_axis_curr, 0.5f); + /* z-axis is the cross product of the two. */ + cross_v3_v3v3(z_axis_prev, x_axis_prev, y_axis_prev); + cross_v3_v3v3(z_axis_curr, x_axis_curr, y_axis_curr); + /* Fix the y-axis to be orthogonal. */ + cross_v3_v3v3(y_axis_prev, z_axis_prev, x_axis_prev); + cross_v3_v3v3(y_axis_curr, z_axis_curr, x_axis_curr); + /* Normalize. */ + normalize_v3_v3(prev[0], x_axis_prev); + normalize_v3_v3(prev[1], y_axis_prev); + normalize_v3_v3(prev[2], z_axis_prev); + normalize_v3_v3(curr[0], x_axis_curr); + normalize_v3_v3(curr[1], y_axis_curr); + normalize_v3_v3(curr[2], z_axis_curr); + } + + if (!data->loc_lock || nav_lock) { + /* Translation: translation of the averaged controller locations. */ + add_v3_v3v3(prev[3], data->mat_prev[3], data->mat_other_prev[3]); + mul_v3_fl(prev[3], 0.5f); + add_v3_v3v3(curr[3], actiondata->controller_loc, actiondata->controller_loc_other); + mul_v3_fl(curr[3], 0.5f); + } + + if (!data->scale_lock) { + /* Scaling: distance between controllers. */ + float scale, v[3]; + + sub_v3_v3v3(v, data->mat_prev[3], data->mat_other_prev[3]); + scale = len_v3(v); + mul_v3_fl(prev[0], scale); + mul_v3_fl(prev[1], scale); + mul_v3_fl(prev[2], scale); + + sub_v3_v3v3(v, actiondata->controller_loc, actiondata->controller_loc_other); + scale = len_v3(v); + mul_v3_fl(curr[0], scale); + mul_v3_fl(curr[1], scale); + mul_v3_fl(curr[2], scale); + } + + if (nav_lock) { + wm_xr_navlocks_apply( + nav_mat, nav_inv, data->loc_lock, data->locz_lock, data->rotz_lock, prev, curr); + } + + if (reverse) { + invert_m4(curr); + mul_m4_m4m4(r_delta, prev, curr); + } + else { + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Grab + * + * Navigates the scene by grabbing with XR controllers. + * \{ */ + +static int wm_xr_navigation_grab_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + + wm_xr_grab_init(op); + wm_xr_grab_update(op, actiondata); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_xr_navigation_grab_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static bool wm_xr_navigation_grab_can_do_bimanual(const wmXrActionData *actiondata, + const XrGrabData *data) +{ + /* Returns true if: 1) Bimanual interaction is currently occurring (i.e. inputs on both + controllers are pressed) and 2) bimanual interaction occurred on the last update. This second + part is needed to avoid "jumpy" navigation changes when transitioning from one-handed to + two-handed interaction (see #wm_xr_grab_compute/compute_bimanual() for how navigation deltas + are calculated). */ + return (actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_navigation_grab_is_bimanual_ending(const wmXrActionData *actiondata, + const XrGrabData *data) +{ + return (!actiondata->bimanual && data->bimanual_prev); +} + +static bool wm_xr_navigation_grab_is_locked(const XrGrabData *data, const bool bimanual) +{ + if (bimanual) { + return data->loc_lock && data->rot_lock && data->scale_lock; + } + /* Ignore scale lock, as one-handed interaction cannot change navigation scale. */ + return data->loc_lock && data->rot_lock; +} + +static void wm_xr_navigation_grab_apply(wmXrData *xr, + const wmXrActionData *actiondata, + const XrGrabData *data, + bool bimanual) +{ + GHOST_XrPose nav_pose; + float nav_scale; + float nav_mat[4][4], nav_inv[4][4], delta[4][4], out[4][4]; + + const bool need_navinv = (data->loc_lock || data->locz_lock || data->rotz_lock); + + WM_xr_session_state_nav_location_get(xr, nav_pose.position); + WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + wm_xr_pose_scale_to_mat(&nav_pose, nav_scale, nav_mat); + if (need_navinv) { + wm_xr_pose_scale_to_imat(&nav_pose, nav_scale, nav_inv); + } + + if (bimanual) { + wm_xr_grab_compute_bimanual( + actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + } + else { + wm_xr_grab_compute( + actiondata, data, need_navinv ? nav_mat : NULL, need_navinv ? nav_inv : NULL, true, delta); + } + + mul_m4_m4m4(out, delta, nav_mat); + + /* Limit scale to reasonable values. */ + nav_scale = len_v3(out[0]); + + if (!(nav_scale < xr->session_settings.clip_start || + nav_scale > xr->session_settings.clip_end)) { + WM_xr_session_state_nav_location_set(xr, out[3]); + if (!data->rot_lock) { + mat4_to_quat(nav_pose.orientation_quat, out); + normalize_qt(nav_pose.orientation_quat); + WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat); + } + if (!data->scale_lock && bimanual) { + WM_xr_session_state_nav_scale_set(xr, nav_scale); + } + } +} + +static void wm_xr_navigation_grab_bimanual_state_update(const wmXrActionData *actiondata, + XrGrabData *data) +{ + if (actiondata->bimanual) { + if (!data->bimanual_prev) { + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + quat_to_mat4(data->mat_other_prev, actiondata->controller_rot_other); + copy_v3_v3(data->mat_other_prev[3], actiondata->controller_loc_other); + } + data->bimanual_prev = true; + } + else { + if (data->bimanual_prev) { + quat_to_mat4(data->mat_prev, actiondata->controller_rot); + copy_v3_v3(data->mat_prev[3], actiondata->controller_loc); + } + data->bimanual_prev = false; + } +} + +static int wm_xr_navigation_grab_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + XrGrabData *data = op->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + + const bool do_bimanual = wm_xr_navigation_grab_can_do_bimanual(actiondata, data); + + data->loc_lock = RNA_boolean_get(op->ptr, "lock_location"); + data->locz_lock = RNA_boolean_get(op->ptr, "lock_location_z"); + data->rot_lock = RNA_boolean_get(op->ptr, "lock_rotation"); + data->rotz_lock = RNA_boolean_get(op->ptr, "lock_rotation_z"); + data->scale_lock = RNA_boolean_get(op->ptr, "lock_scale"); + + /* Check if navigation is locked. */ + if (!wm_xr_navigation_grab_is_locked(data, do_bimanual)) { + /* Prevent unwanted snapping (i.e. "jumpy" navigation changes when transitioning from + two-handed to one-handed interaction) at the end of a bimanual interaction. */ + if (!wm_xr_navigation_grab_is_bimanual_ending(actiondata, data)) { + wm_xr_navigation_grab_apply(xr, actiondata, data, do_bimanual); + } + } + + wm_xr_navigation_grab_bimanual_state_update(actiondata, data); + + /* Note: KM_PRESS and KM_RELEASE are the only two values supported by XR events during event + dispatching (see #wm_xr_session_action_states_interpret()). For modal XR operators, modal + handling starts when an input is "pressed" (action state exceeds the action threshold) and + ends when the input is "released" (state falls below the threshold). */ + switch (event->val) { + case KM_PRESS: + return OPERATOR_RUNNING_MODAL; + case KM_RELEASE: + wm_xr_grab_uninit(op); + return OPERATOR_FINISHED; + default: + BLI_assert_unreachable(); + wm_xr_grab_uninit(op); + return OPERATOR_CANCELLED; + } +} + +static void WM_OT_xr_navigation_grab(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Grab"; + ot->idname = "WM_OT_xr_navigation_grab"; + ot->description = "Navigate the VR scene by grabbing with controllers"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_grab_invoke; + ot->exec = wm_xr_navigation_grab_exec; + ot->modal = wm_xr_navigation_grab_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + RNA_def_boolean( + ot->srna, "lock_location", false, "Lock Location", "Prevent changes to viewer location"); + RNA_def_boolean( + ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation"); + RNA_def_boolean( + ot->srna, "lock_rotation", false, "Lock Rotation", "Prevent changes to viewer rotation"); + RNA_def_boolean(ot->srna, + "lock_rotation_z", + false, + "Lock Up Orientation", + "Prevent changes to viewer up orientation"); + RNA_def_boolean(ot->srna, "lock_scale", false, "Lock Scale", "Prevent changes to viewer scale"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Raycast Utilities + * \{ */ + +static const float g_xr_default_raycast_axis[3] = {0.0f, 0.0f, -1.0f}; +static const float g_xr_default_raycast_color[4] = {0.35f, 0.35f, 1.0f, 1.0f}; + +typedef struct XrRaycastData { + bool from_viewer; + float origin[3]; + float direction[3]; + float end[3]; + float color[4]; + void *draw_handle; +} XrRaycastData; + +static void wm_xr_raycast_draw(const bContext *UNUSED(C), + ARegion *UNUSED(region), + void *customdata) +{ + const XrRaycastData *data = customdata; + + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + + if (data->from_viewer) { + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); + immUniformColor4fv(data->color); + + GPU_depth_test(GPU_DEPTH_NONE); + GPU_point_size(7.0f); + + immBegin(GPU_PRIM_POINTS, 1); + immVertex3fv(pos, data->end); + immEnd(); + } + else { + uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_FLAT_COLOR); + + float viewport[4]; + GPU_viewport_size_get_f(viewport); + immUniform2fv("viewportSize", &viewport[2]); + + immUniform1f("lineWidth", 3.0f * U.pixelsize); + + GPU_depth_test(GPU_DEPTH_LESS_EQUAL); + + immBegin(GPU_PRIM_LINES, 2); + immAttrSkip(col); + immVertex3fv(pos, data->origin); + immAttr4fv(col, data->color); + immVertex3fv(pos, data->end); + immEnd(); + } + + immUnbindProgram(); +} + +static void wm_xr_raycast_init(wmOperator *op) +{ + BLI_assert(op->customdata == NULL); + + op->customdata = MEM_callocN(sizeof(XrRaycastData), __func__); + + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (!st) { + return; + } + + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (!art) { + return; + } + + XrRaycastData *data = op->customdata; + data->draw_handle = ED_region_draw_cb_activate( + art, wm_xr_raycast_draw, op->customdata, REGION_DRAW_POST_VIEW); +} + +static void wm_xr_raycast_uninit(wmOperator *op) +{ + if (!op->customdata) { + return; + } + + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + if (st) { + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_XR); + if (art) { + XrRaycastData *data = op->customdata; + ED_region_draw_cb_exit(art, data->draw_handle); + } + } + + MEM_freeN(op->customdata); +} + +static void wm_xr_raycast_update(wmOperator *op, + const wmXrData *xr, + const wmXrActionData *actiondata) +{ + XrRaycastData *data = op->customdata; + float ray_length, axis[3]; + + data->from_viewer = RNA_boolean_get(op->ptr, "from_viewer"); + RNA_float_get_array(op->ptr, "axis", axis); + RNA_float_get_array(op->ptr, "color", data->color); + + if (data->from_viewer) { + float viewer_rot[4]; + WM_xr_session_state_viewer_pose_location_get(xr, data->origin); + WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_rot); + mul_qt_v3(viewer_rot, axis); + ray_length = (xr->session_settings.clip_start + xr->session_settings.clip_end) / 2.0f; + } + else { + copy_v3_v3(data->origin, actiondata->controller_loc); + mul_qt_v3(actiondata->controller_rot, axis); + ray_length = xr->session_settings.clip_end; + } + + copy_v3_v3(data->direction, axis); + madd_v3_v3v3fl(data->end, data->origin, data->direction, ray_length); +} + +static void wm_xr_raycast(Scene *scene, + Depsgraph *depsgraph, + const float origin[3], + const float direction[3], + float *ray_dist, + bool selectable_only, + float r_location[3], + float r_normal[3], + int *r_index, + Object **r_ob, + float r_obmat[4][4]) +{ + /* Uses same raycast method as Scene.ray_cast(). */ + SnapObjectContext *sctx = ED_transform_snap_object_context_create(scene, 0); + + ED_transform_snap_object_project_ray_ex( + sctx, + depsgraph, + NULL, + &(const struct SnapObjectParams){ + .snap_select = (selectable_only ? SNAP_SELECTABLE : SNAP_ALL)}, + origin, + direction, + ray_dist, + r_location, + r_normal, + r_index, + r_ob, + r_obmat); + + ED_transform_snap_object_context_destroy(sctx); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Fly + * + * Navigates the scene by moving/turning relative to navigation space or the XR viewer or + * controller. + * \{ */ + +#define XR_DEFAULT_FLY_SPEED_MOVE 0.054f +#define XR_DEFAULT_FLY_SPEED_TURN 0.03f + +typedef enum eXrFlyMode { + XR_FLY_FORWARD = 0, + XR_FLY_BACK = 1, + XR_FLY_LEFT = 2, + XR_FLY_RIGHT = 3, + XR_FLY_UP = 4, + XR_FLY_DOWN = 5, + XR_FLY_TURNLEFT = 6, + XR_FLY_TURNRIGHT = 7, + XR_FLY_VIEWER_FORWARD = 8, + XR_FLY_VIEWER_BACK = 9, + XR_FLY_VIEWER_LEFT = 10, + XR_FLY_VIEWER_RIGHT = 11, + XR_FLY_CONTROLLER_FORWARD = 12, +} eXrFlyMode; + +typedef struct XrFlyData { + float viewer_rot[4]; + double time_prev; +} XrFlyData; + +static void wm_xr_fly_init(wmOperator *op, const wmXrData *xr) +{ + BLI_assert(op->customdata == NULL); + + XrFlyData *data = op->customdata = MEM_callocN(sizeof(XrFlyData), __func__); + + WM_xr_session_state_viewer_pose_rotation_get(xr, data->viewer_rot); + data->time_prev = PIL_check_seconds_timer(); +} + +static void wm_xr_fly_uninit(wmOperator *op) +{ + MEM_SAFE_FREE(op->customdata); +} + +static void wm_xr_fly_compute_move(eXrFlyMode mode, + float speed, + const float ref_quat[4], + const float nav_mat[4][4], + bool locz_lock, + float r_delta[4][4]) +{ + float ref_axes[3][3]; + quat_to_mat3(ref_axes, ref_quat); + + unit_m4(r_delta); + + switch (mode) { + /* Navigation space reference. */ + case XR_FLY_FORWARD: + madd_v3_v3fl(r_delta[3], ref_axes[1], speed); + return; + case XR_FLY_BACK: + madd_v3_v3fl(r_delta[3], ref_axes[1], -speed); + return; + case XR_FLY_LEFT: + madd_v3_v3fl(r_delta[3], ref_axes[0], -speed); + return; + case XR_FLY_RIGHT: + madd_v3_v3fl(r_delta[3], ref_axes[0], speed); + return; + case XR_FLY_UP: + case XR_FLY_DOWN: + if (!locz_lock) { + madd_v3_v3fl(r_delta[3], ref_axes[2], (mode == XR_FLY_UP) ? speed : -speed); + } + return; + /* Viewer/controller space reference. */ + case XR_FLY_VIEWER_FORWARD: + case XR_FLY_CONTROLLER_FORWARD: + negate_v3_v3(r_delta[3], ref_axes[2]); + break; + case XR_FLY_VIEWER_BACK: + copy_v3_v3(r_delta[3], ref_axes[2]); + break; + case XR_FLY_VIEWER_LEFT: + negate_v3_v3(r_delta[3], ref_axes[0]); + break; + case XR_FLY_VIEWER_RIGHT: + copy_v3_v3(r_delta[3], ref_axes[0]); + break; + /* Unused. */ + case XR_FLY_TURNLEFT: + case XR_FLY_TURNRIGHT: + BLI_assert_unreachable(); + return; + } + + if (locz_lock) { + /* Lock elevation in navigation space. */ + float z_axis[3], projected[3]; + + normalize_v3_v3(z_axis, nav_mat[2]); + project_v3_v3v3_normalized(projected, r_delta[3], z_axis); + sub_v3_v3(r_delta[3], projected); + + normalize_v3(r_delta[3]); + } + + mul_v3_fl(r_delta[3], speed); +} + +static void wm_xr_fly_compute_turn(eXrFlyMode mode, + float speed, + const float viewer_mat[4][4], + const float nav_mat[4][4], + const float nav_inv[4][4], + float r_delta[4][4]) +{ + BLI_assert(mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT); + + float z_axis[3], m[3][3], prev[4][4], curr[4][4]; + + /* Turn around Z-axis in navigation space. */ + normalize_v3_v3(z_axis, nav_mat[2]); + axis_angle_normalized_to_mat3(m, z_axis, (mode == XR_FLY_TURNLEFT) ? speed : -speed); + copy_m4_m3(r_delta, m); + + copy_m4_m4(prev, viewer_mat); + mul_m4_m4m4(curr, r_delta, viewer_mat); + + /* Lock location in base pose space. */ + wm_xr_navlocks_apply(nav_mat, nav_inv, true, false, false, prev, curr); + + invert_m4(prev); + mul_m4_m4m4(r_delta, curr, prev); +} + +static void wm_xr_basenav_rotation_calc(const wmXrData *xr, + const float nav_rotation[4], + float r_rotation[4]) +{ + /* Apply nav rotation to base pose Z-rotation. */ + float base_eul[3], base_quatz[4]; + quat_to_eul(base_eul, xr->runtime->session_state.prev_base_pose.orientation_quat); + axis_angle_to_quat_single(base_quatz, 'Z', base_eul[2]); + mul_qt_qtqt(r_rotation, nav_rotation, base_quatz); +} + +static int wm_xr_navigation_fly_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + wmWindowManager *wm = CTX_wm_manager(C); + + wm_xr_fly_init(op, &wm->xr); + + WM_event_add_modal_handler(C, op); + + return OPERATOR_RUNNING_MODAL; +} + +static int wm_xr_navigation_fly_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static int wm_xr_navigation_fly_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + if (event->val == KM_RELEASE) { + wm_xr_fly_uninit(op); + return OPERATOR_FINISHED; + } + + const wmXrActionData *actiondata = event->customdata; + XrFlyData *data = op->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + eXrFlyMode mode; + bool turn, locz_lock, dir_lock, speed_frame_based; + bool speed_interp_cubic = false; + float speed, speed_max, speed_p0[2], speed_p1[2]; + GHOST_XrPose nav_pose; + float nav_mat[4][4], delta[4][4], out[4][4]; + + const double time_now = PIL_check_seconds_timer(); + + mode = (eXrFlyMode)RNA_enum_get(op->ptr, "mode"); + turn = (mode == XR_FLY_TURNLEFT || mode == XR_FLY_TURNRIGHT); + + locz_lock = RNA_boolean_get(op->ptr, "lock_location_z"); + dir_lock = RNA_boolean_get(op->ptr, "lock_direction"); + speed_frame_based = RNA_boolean_get(op->ptr, "speed_frame_based"); + speed = RNA_float_get(op->ptr, "speed_min"); + speed_max = RNA_float_get(op->ptr, "speed_max"); + + PropertyRNA *prop = RNA_struct_find_property(op->ptr, "speed_interpolation0"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, speed_p0); + speed_interp_cubic = true; + } + else { + speed_p0[0] = speed_p0[1] = 0.0f; + } + + prop = RNA_struct_find_property(op->ptr, "speed_interpolation1"); + if (prop && RNA_property_is_set(op->ptr, prop)) { + RNA_property_float_get_array(op->ptr, prop, speed_p1); + speed_interp_cubic = true; + } + else { + speed_p1[0] = speed_p1[1] = 1.0f; + } + + /* Ensure valid interpolation. */ + if (speed_max < speed) { + speed_max = speed; + } + + /* Interpolate between min/max speeds based on button state. */ + switch (actiondata->type) { + case XR_BOOLEAN_INPUT: + speed = speed_max; + break; + case XR_FLOAT_INPUT: + case XR_VECTOR2F_INPUT: { + float state = (actiondata->type == XR_FLOAT_INPUT) ? fabsf(actiondata->state[0]) : + len_v2(actiondata->state); + float speed_t = (actiondata->float_threshold < 1.0f) ? + (state - actiondata->float_threshold) / + (1.0f - actiondata->float_threshold) : + 1.0f; + if (speed_interp_cubic) { + float start[2], end[2], p[2]; + + start[0] = 0.0f; + start[1] = speed; + speed_p0[1] = speed + speed_p0[1] * (speed_max - speed); + speed_p1[1] = speed + speed_p1[1] * (speed_max - speed); + end[0] = 1.0f; + end[1] = speed_max; + + interp_v2_v2v2v2v2_cubic(p, start, speed_p0, speed_p1, end, speed_t); + speed = p[1]; + } + else { + speed += speed_t * (speed_max - speed); + } + break; + } + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (!speed_frame_based) { + /* Adjust speed based on last update time. */ + speed *= time_now - data->time_prev; + } + data->time_prev = time_now; + + WM_xr_session_state_nav_location_get(xr, nav_pose.position); + WM_xr_session_state_nav_rotation_get(xr, nav_pose.orientation_quat); + wm_xr_pose_to_mat(&nav_pose, nav_mat); + + if (turn) { + if (dir_lock) { + unit_m4(delta); + } + else { + GHOST_XrPose viewer_pose; + float viewer_mat[4][4], nav_inv[4][4]; + + WM_xr_session_state_viewer_pose_location_get(xr, viewer_pose.position); + WM_xr_session_state_viewer_pose_rotation_get(xr, viewer_pose.orientation_quat); + wm_xr_pose_to_mat(&viewer_pose, viewer_mat); + wm_xr_pose_to_imat(&nav_pose, nav_inv); + + wm_xr_fly_compute_turn(mode, speed, viewer_mat, nav_mat, nav_inv, delta); + } + } + else { + float nav_scale, ref_quat[4]; + + /* Adjust speed for base and navigation scale. */ + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + speed *= xr->session_settings.base_scale * nav_scale; + + switch (mode) { + /* Move relative to navigation space. */ + case XR_FLY_FORWARD: + case XR_FLY_BACK: + case XR_FLY_LEFT: + case XR_FLY_RIGHT: + case XR_FLY_UP: + case XR_FLY_DOWN: + wm_xr_basenav_rotation_calc(xr, nav_pose.orientation_quat, ref_quat); + break; + /* Move relative to viewer. */ + case XR_FLY_VIEWER_FORWARD: + case XR_FLY_VIEWER_BACK: + case XR_FLY_VIEWER_LEFT: + case XR_FLY_VIEWER_RIGHT: + if (dir_lock) { + copy_qt_qt(ref_quat, data->viewer_rot); + } + else { + WM_xr_session_state_viewer_pose_rotation_get(xr, ref_quat); + } + break; + /* Move relative to controller. */ + case XR_FLY_CONTROLLER_FORWARD: + copy_qt_qt(ref_quat, actiondata->controller_rot); + break; + /* Unused. */ + case XR_FLY_TURNLEFT: + case XR_FLY_TURNRIGHT: + BLI_assert_unreachable(); + break; + } + + wm_xr_fly_compute_move(mode, speed, ref_quat, nav_mat, locz_lock, delta); + } + + mul_m4_m4m4(out, delta, nav_mat); + + WM_xr_session_state_nav_location_set(xr, out[3]); + if (turn) { + mat4_to_quat(nav_pose.orientation_quat, out); + WM_xr_session_state_nav_rotation_set(xr, nav_pose.orientation_quat); + } + + if (event->val == KM_PRESS) { + return OPERATOR_RUNNING_MODAL; + } + + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_fly_uninit(op); + return OPERATOR_CANCELLED; +} + +static void WM_OT_xr_navigation_fly(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Fly"; + ot->idname = "WM_OT_xr_navigation_fly"; + ot->description = "Move/turn relative to the VR viewer or controller"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_fly_invoke; + ot->exec = wm_xr_navigation_fly_exec; + ot->modal = wm_xr_navigation_fly_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + static const EnumPropertyItem fly_modes[] = { + {XR_FLY_FORWARD, "FORWARD", 0, "Forward", "Move along navigation forward axis"}, + {XR_FLY_BACK, "BACK", 0, "Back", "Move along navigation back axis"}, + {XR_FLY_LEFT, "LEFT", 0, "Left", "Move along navigation left axis"}, + {XR_FLY_RIGHT, "RIGHT", 0, "Right", "Move along navigation right axis"}, + {XR_FLY_UP, "UP", 0, "Up", "Move along navigation up axis"}, + {XR_FLY_DOWN, "DOWN", 0, "Down", "Move along navigation down axis"}, + {XR_FLY_TURNLEFT, + "TURNLEFT", + 0, + "Turn Left", + "Turn counter-clockwise around navigation up axis"}, + {XR_FLY_TURNRIGHT, "TURNRIGHT", 0, "Turn Right", "Turn clockwise around navigation up axis"}, + {XR_FLY_VIEWER_FORWARD, + "VIEWER_FORWARD", + 0, + "Viewer Forward", + "Move along viewer's forward axis"}, + {XR_FLY_VIEWER_BACK, "VIEWER_BACK", 0, "Viewer Back", "Move along viewer's back axis"}, + {XR_FLY_VIEWER_LEFT, "VIEWER_LEFT", 0, "Viewer Left", "Move along viewer's left axis"}, + {XR_FLY_VIEWER_RIGHT, "VIEWER_RIGHT", 0, "Viewer Right", "Move along viewer's right axis"}, + {XR_FLY_CONTROLLER_FORWARD, + "CONTROLLER_FORWARD", + 0, + "Controller Forward", + "Move along controller's forward axis"}, + {0, NULL, 0, NULL, NULL}, + }; + + static const float default_speed_p0[2] = {0.0f, 0.0f}; + static const float default_speed_p1[2] = {1.0f, 1.0f}; + + RNA_def_enum(ot->srna, "mode", fly_modes, XR_FLY_VIEWER_FORWARD, "Mode", "Fly mode"); + RNA_def_boolean( + ot->srna, "lock_location_z", false, "Lock Elevation", "Prevent changes to viewer elevation"); + RNA_def_boolean(ot->srna, + "lock_direction", + false, + "Lock Direction", + "Limit movement to viewer's initial direction"); + RNA_def_boolean(ot->srna, + "speed_frame_based", + true, + "Frame Based Speed", + "Apply fixed movement deltas every update"); + RNA_def_float(ot->srna, + "speed_min", + XR_DEFAULT_FLY_SPEED_MOVE / 3.0f, + 0.0f, + 1000.0f, + "Minimum Speed", + "Minimum move (turn) speed in meters (radians) per second or frame", + 0.0f, + 1000.0f); + RNA_def_float(ot->srna, + "speed_max", + XR_DEFAULT_FLY_SPEED_MOVE, + 0.0f, + 1000.0f, + "Maximum Speed", + "Maximum move (turn) speed in meters (radians) per second or frame", + 0.0f, + 1000.0f); + RNA_def_float_vector(ot->srna, + "speed_interpolation0", + 2, + default_speed_p0, + 0.0f, + 1.0f, + "Speed Interpolation 0", + "First cubic spline control point between min/max speeds", + 0.0f, + 1.0f); + RNA_def_float_vector(ot->srna, + "speed_interpolation1", + 2, + default_speed_p1, + 0.0f, + 1.0f, + "Speed Interpolation 1", + "Second cubic spline control point between min/max speeds", + 0.0f, + 1.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Teleport + * + * Casts a ray from an XR controller's pose and teleports to any hit geometry. + * \{ */ + +static void wm_xr_navigation_teleport(bContext *C, + wmXrData *xr, + const float origin[3], + const float direction[3], + float *ray_dist, + bool selectable_only, + const bool teleport_axes[3], + float teleport_t, + float teleport_ofs) +{ + Scene *scene = CTX_data_scene(C); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + float location[3]; + float normal[3]; + int index; + Object *ob = NULL; + float obmat[4][4]; + + wm_xr_raycast(scene, + depsgraph, + origin, + direction, + ray_dist, + selectable_only, + location, + normal, + &index, + &ob, + obmat); + + /* Teleport. */ + if (ob) { + float nav_location[3], nav_rotation[4], viewer_location[3]; + float nav_axes[3][3], projected[3], v0[3], v1[3]; + float out[3] = {0.0f, 0.0f, 0.0f}; + + WM_xr_session_state_nav_location_get(xr, nav_location); + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_viewer_pose_location_get(xr, viewer_location); + + wm_xr_basenav_rotation_calc(xr, nav_rotation, nav_rotation); + quat_to_mat3(nav_axes, nav_rotation); + + /* Project locations onto navigation axes. */ + for (int a = 0; a < 3; ++a) { + project_v3_v3v3_normalized(projected, nav_location, nav_axes[a]); + if (teleport_axes[a]) { + /* Interpolate between projected locations. */ + project_v3_v3v3_normalized(v0, location, nav_axes[a]); + project_v3_v3v3_normalized(v1, viewer_location, nav_axes[a]); + sub_v3_v3(v0, v1); + madd_v3_v3fl(projected, v0, teleport_t); + /* Subtract offset. */ + project_v3_v3v3_normalized(v0, normal, nav_axes[a]); + madd_v3_v3fl(projected, v0, teleport_ofs); + } + /* Add to final location. */ + add_v3_v3(out, projected); + } + + WM_xr_session_state_nav_location_set(xr, out); + } +} + +static int wm_xr_navigation_teleport_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + wm_xr_raycast_init(op); + + int retval = op->type->modal(C, op, event); + + if ((retval & OPERATOR_RUNNING_MODAL) != 0) { + WM_event_add_modal_handler(C, op); + } + + return retval; +} + +static int wm_xr_navigation_teleport_exec(bContext *UNUSED(C), wmOperator *UNUSED(op)) +{ + return OPERATOR_CANCELLED; +} + +static int wm_xr_navigation_teleport_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + if (!wm_xr_operator_test_event(op, event)) { + return OPERATOR_PASS_THROUGH; + } + + const wmXrActionData *actiondata = event->customdata; + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + + wm_xr_raycast_update(op, xr, actiondata); + + switch (event->val) { + case KM_PRESS: + return OPERATOR_RUNNING_MODAL; + case KM_RELEASE: { + XrRaycastData *data = op->customdata; + bool selectable_only, teleport_axes[3]; + float teleport_t, teleport_ofs, ray_dist; + + RNA_boolean_get_array(op->ptr, "teleport_axes", teleport_axes); + teleport_t = RNA_float_get(op->ptr, "interpolation"); + teleport_ofs = RNA_float_get(op->ptr, "offset"); + selectable_only = RNA_boolean_get(op->ptr, "selectable_only"); + ray_dist = RNA_float_get(op->ptr, "distance"); + + wm_xr_navigation_teleport(C, + xr, + data->origin, + data->direction, + &ray_dist, + selectable_only, + teleport_axes, + teleport_t, + teleport_ofs); + + wm_xr_raycast_uninit(op); + + return OPERATOR_FINISHED; + } + default: + /* XR events currently only support press and release. */ + BLI_assert_unreachable(); + wm_xr_raycast_uninit(op); + return OPERATOR_CANCELLED; + } +} + +static void WM_OT_xr_navigation_teleport(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Teleport"; + ot->idname = "WM_OT_xr_navigation_teleport"; + ot->description = "Set VR viewer location to controller raycast hit location"; + + /* callbacks */ + ot->invoke = wm_xr_navigation_teleport_invoke; + ot->exec = wm_xr_navigation_teleport_exec; + ot->modal = wm_xr_navigation_teleport_modal; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + static bool default_teleport_axes[3] = {true, true, true}; + + RNA_def_boolean_vector(ot->srna, + "teleport_axes", + 3, + default_teleport_axes, + "Teleport Axes", + "Enabled teleport axes in navigation space"); + RNA_def_float(ot->srna, + "interpolation", + 1.0f, + 0.0f, + 1.0f, + "Interpolation", + "Interpolation factor between viewer and hit locations", + 0.0f, + 1.0f); + RNA_def_float(ot->srna, + "offset", + 0.0f, + 0.0f, + FLT_MAX, + "Offset", + "Offset along hit normal to subtract from final location", + 0.0f, + FLT_MAX); + RNA_def_boolean(ot->srna, + "selectable_only", + true, + "Selectable Only", + "Only allow selectable objects to influence raycast result"); + RNA_def_float(ot->srna, + "distance", + BVH_RAYCAST_DIST_MAX, + 0.0, + BVH_RAYCAST_DIST_MAX, + "", + "Maximum raycast distance", + 0.0, + BVH_RAYCAST_DIST_MAX); + RNA_def_boolean( + ot->srna, "from_viewer", false, "From Viewer", "Use viewer pose as raycast origin"); + RNA_def_float_vector(ot->srna, + "axis", + 3, + g_xr_default_raycast_axis, + -1.0f, + 1.0f, + "Axis", + "Raycast axis in controller/viewer space", + -1.0f, + 1.0f); + RNA_def_float_color(ot->srna, + "color", + 4, + g_xr_default_raycast_color, + 0.0f, + 1.0f, + "Color", + "Raycast color", + 0.0f, + 1.0f); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name XR Navigation Reset + * + * Resets XR navigation deltas relative to session base pose. + * \{ */ + +static int wm_xr_navigation_reset_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmXrData *xr = &wm->xr; + bool reset_loc, reset_rot, reset_scale; + + reset_loc = RNA_boolean_get(op->ptr, "location"); + reset_rot = RNA_boolean_get(op->ptr, "rotation"); + reset_scale = RNA_boolean_get(op->ptr, "scale"); + + if (reset_loc) { + float loc[3]; + if (!reset_scale) { + float nav_rotation[4], nav_scale; + + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + /* Adjust location based on scale. */ + mul_v3_v3fl(loc, xr->runtime->session_state.prev_base_pose.position, nav_scale); + sub_v3_v3(loc, xr->runtime->session_state.prev_base_pose.position); + mul_qt_v3(nav_rotation, loc); + negate_v3(loc); + } + else { + zero_v3(loc); + } + WM_xr_session_state_nav_location_set(xr, loc); + } + + if (reset_rot) { + float rot[4]; + unit_qt(rot); + WM_xr_session_state_nav_rotation_set(xr, rot); + } + + if (reset_scale) { + if (!reset_loc) { + float nav_location[3], nav_rotation[4], nav_scale; + float nav_axes[3][3], v[3]; + + WM_xr_session_state_nav_location_get(xr, nav_location); + WM_xr_session_state_nav_rotation_get(xr, nav_rotation); + WM_xr_session_state_nav_scale_get(xr, &nav_scale); + + /* Offset any location changes when changing scale. */ + mul_v3_v3fl(v, xr->runtime->session_state.prev_base_pose.position, nav_scale); + sub_v3_v3(v, xr->runtime->session_state.prev_base_pose.position); + mul_qt_v3(nav_rotation, v); + add_v3_v3(nav_location, v); + + /* Reset elevation to base pose value. */ + quat_to_mat3(nav_axes, nav_rotation); + project_v3_v3v3_normalized(v, nav_location, nav_axes[2]); + sub_v3_v3(nav_location, v); + + WM_xr_session_state_nav_location_set(xr, nav_location); + } + WM_xr_session_state_nav_scale_set(xr, 1.0f); + } + + return OPERATOR_FINISHED; +} + +static void WM_OT_xr_navigation_reset(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "XR Navigation Reset"; + ot->idname = "WM_OT_xr_navigation_reset"; + ot->description = "Reset VR navigation deltas relative to session base pose"; + + /* callbacks */ + ot->exec = wm_xr_navigation_reset_exec; + ot->poll = wm_xr_operator_sessionactive; + + /* properties */ + RNA_def_boolean(ot->srna, "location", true, "Location", "Reset location deltas"); + RNA_def_boolean(ot->srna, "rotation", true, "Rotation", "Reset rotation deltas"); + RNA_def_boolean(ot->srna, "scale", true, "Scale", "Reset scale deltas"); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Operator Registration + * \{ */ + +void wm_xr_operatortypes_register(void) +{ + WM_operatortype_append(WM_OT_xr_session_toggle); + WM_operatortype_append(WM_OT_xr_navigation_grab); + WM_operatortype_append(WM_OT_xr_navigation_fly); + WM_operatortype_append(WM_OT_xr_navigation_teleport); + WM_operatortype_append(WM_OT_xr_navigation_reset); +} + +/** \} */ diff --git a/source/blender/windowmanager/xr/intern/wm_xr_session.c b/source/blender/windowmanager/xr/intern/wm_xr_session.c index 88bf3ff453c..3224869b04a 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_session.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_session.c @@ -21,8 +21,10 @@ #include "BKE_callbacks.h" #include "BKE_context.h" #include "BKE_global.h" +#include "BKE_idprop.h" #include "BKE_main.h" #include "BKE_scene.h" +#include "BKE_screen.h" #include "BLI_listbase.h" #include "BLI_math.h" @@ -30,18 +32,26 @@ #include "DEG_depsgraph.h" #include "DNA_camera_types.h" +#include "DNA_space_types.h" #include "DRW_engine.h" +#include "ED_screen.h" +#include "ED_space_api.h" + #include "GHOST_C-api.h" +#include "GPU_batch.h" #include "GPU_viewport.h" #include "MEM_guardedalloc.h" +#include "PIL_time.h" + #include "WM_api.h" #include "WM_types.h" +#include "wm_event_system.h" #include "wm_surface.h" #include "wm_window.h" #include "wm_xr_intern.h" @@ -56,16 +66,33 @@ static void wm_xr_session_create_cb(void) Main *bmain = G_MAIN; wmWindowManager *wm = bmain->wm.first; wmXrData *xr_data = &wm->xr; + wmXrSessionState *state = &xr_data->runtime->session_state; + XrSessionSettings *settings = &xr_data->session_settings; /* Get action set data from Python. */ BKE_callback_exec_null(bmain, BKE_CB_EVT_XR_SESSION_START_PRE); wm_xr_session_actions_init(xr_data); + + /* Initialize navigation. */ + WM_xr_session_state_navigation_reset(state); + if (settings->base_scale < FLT_EPSILON) { + settings->base_scale = 1.0f; + } + state->prev_base_scale = settings->base_scale; } static void wm_xr_session_controller_data_free(wmXrSessionState *state) { - BLI_freelistN(&state->controllers); + ListBase *lb = &state->controllers; + wmXrController *c; + + while ((c = BLI_pophead(lb))) { + if (c->model) { + GPU_batch_discard(c->model); + } + BLI_freelinkN(lb, c); + } } void wm_xr_session_data_free(wmXrSessionState *state) @@ -110,6 +137,9 @@ void wm_xr_session_toggle(wmWindowManager *wm, wmXrData *xr_data = &wm->xr; if (WM_xr_session_exists(xr_data)) { + /* Must set first, since #GHOST_XrSessionEnd() may immediately free the runtime. */ + xr_data->runtime->session_state.is_started = false; + GHOST_XrSessionEnd(xr_data->runtime->context); } else { @@ -148,7 +178,8 @@ bool WM_xr_session_is_ready(const wmXrData *xr) static void wm_xr_session_base_pose_calc(const Scene *scene, const XrSessionSettings *settings, - GHOST_XrPose *r_base_pose) + GHOST_XrPose *r_base_pose, + float *r_base_scale) { const Object *base_pose_object = ((settings->base_pose_type == XR_BASE_POSE_OBJECT) && settings->base_pose_object) ? @@ -179,6 +210,8 @@ static void wm_xr_session_base_pose_calc(const Scene *scene, copy_v3_fl(r_base_pose->position, 0.0f); axis_angle_to_quat_single(r_base_pose->orientation_quat, 'X', M_PI_2); } + + *r_base_scale = settings->base_scale; } static void wm_xr_session_draw_data_populate(wmXrData *xr_data, @@ -194,11 +227,12 @@ static void wm_xr_session_draw_data_populate(wmXrData *xr_data, r_draw_data->xr_data = xr_data; r_draw_data->surface_data = g_xr_surface->customdata; - wm_xr_session_base_pose_calc(r_draw_data->scene, settings, &r_draw_data->base_pose); + wm_xr_session_base_pose_calc( + r_draw_data->scene, settings, &r_draw_data->base_pose, &r_draw_data->base_scale); } -static wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, - const wmXrRuntimeData *runtime_data) +wmWindow *wm_xr_session_root_window_or_fallback_get(const wmWindowManager *wm, + const wmXrRuntimeData *runtime_data) { if (runtime_data->session_root_win && BLI_findindex(&wm->windows, runtime_data->session_root_win) != -1) { @@ -272,7 +306,7 @@ static wmXrSessionStateEvent wm_xr_session_state_to_event(const wmXrSessionState return SESSION_STATE_EVENT_NONE; } -void wm_xr_session_draw_data_update(const wmXrSessionState *state, +void wm_xr_session_draw_data_update(wmXrSessionState *state, const XrSessionSettings *settings, const GHOST_XrDrawViewInfo *draw_view, wmXrDrawData *draw_data) @@ -300,6 +334,8 @@ void wm_xr_session_draw_data_update(const wmXrSessionState *state, else { copy_v3_fl(draw_data->eye_position_ofs, 0.0f); } + /* Reset navigation. */ + WM_xr_session_state_navigation_reset(state); break; case SESSION_STATE_EVENT_POSITION_TRACKING_TOGGLE: if (use_position_tracking) { @@ -329,29 +365,32 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, wmXrSessionState *state) { GHOST_XrPose viewer_pose; - const bool use_position_tracking = settings->flag & XR_SESSION_USE_POSITION_TRACKING; - const bool use_absolute_tracking = settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING; + float viewer_mat[4][4], base_mat[4][4], nav_mat[4][4]; - mul_qt_qtqt(viewer_pose.orientation_quat, - draw_data->base_pose.orientation_quat, - draw_view->local_pose.orientation_quat); - copy_v3_v3(viewer_pose.position, draw_data->base_pose.position); - /* The local pose and the eye pose (which is copied from an earlier local pose) both are view - * space, so Y-up. In this case we need them in regular Z-up. */ - if (use_position_tracking) { - viewer_pose.position[0] += draw_view->local_pose.position[0]; - viewer_pose.position[1] -= draw_view->local_pose.position[2]; - viewer_pose.position[2] += draw_view->local_pose.position[1]; + /* Calculate viewer matrix. */ + copy_qt_qt(viewer_pose.orientation_quat, draw_view->local_pose.orientation_quat); + if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { + zero_v3(viewer_pose.position); } - if (!use_absolute_tracking) { - viewer_pose.position[0] -= draw_data->eye_position_ofs[0]; - viewer_pose.position[1] += draw_data->eye_position_ofs[2]; - viewer_pose.position[2] -= draw_data->eye_position_ofs[1]; + else { + copy_v3_v3(viewer_pose.position, draw_view->local_pose.position); } + if ((settings->flag & XR_SESSION_USE_ABSOLUTE_TRACKING) == 0) { + sub_v3_v3(viewer_pose.position, draw_data->eye_position_ofs); + } + wm_xr_pose_to_mat(&viewer_pose, viewer_mat); + + /* Apply base pose and navigation. */ + wm_xr_pose_scale_to_mat(&draw_data->base_pose, draw_data->base_scale, base_mat); + wm_xr_pose_scale_to_mat(&state->nav_pose_prev, state->nav_scale_prev, nav_mat); + mul_m4_m4m4(state->viewer_mat_base, base_mat, viewer_mat); + mul_m4_m4m4(viewer_mat, nav_mat, state->viewer_mat_base); + + /* Save final viewer pose and viewmat. */ + mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat); + wm_xr_pose_scale_to_imat( + &state->viewer_pose, draw_data->base_scale * state->nav_scale_prev, state->viewer_viewmat); - copy_v3_v3(state->viewer_pose.position, viewer_pose.position); - copy_qt_qt(state->viewer_pose.orientation_quat, viewer_pose.orientation_quat); - wm_xr_pose_to_imat(&viewer_pose, state->viewer_viewmat); /* No idea why, but multiplying by two seems to make it match the VR view more. */ state->focal_len = 2.0f * fov_to_focallength(draw_view->fov.angle_right - draw_view->fov.angle_left, @@ -359,7 +398,10 @@ void wm_xr_session_state_update(const XrSessionSettings *settings, copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); memcpy(&state->prev_base_pose, &draw_data->base_pose, sizeof(state->prev_base_pose)); + state->prev_base_scale = draw_data->base_scale; memcpy(&state->prev_local_pose, &draw_view->local_pose, sizeof(state->prev_local_pose)); + copy_v3_v3(state->prev_eye_position_ofs, draw_data->eye_position_ofs); + state->prev_settings_flag = settings->flag; state->prev_base_pose_type = settings->base_pose_type; state->prev_base_pose_object = settings->base_pose_object; @@ -373,6 +415,11 @@ wmXrSessionState *WM_xr_session_state_handle_get(const wmXrData *xr) return xr->runtime ? &xr->runtime->session_state : NULL; } +ScrArea *WM_xr_session_area_get(const wmXrData *xr) +{ + return xr->runtime ? xr->runtime->area : NULL; +} + bool WM_xr_session_state_viewer_pose_location_get(const wmXrData *xr, float r_location[3]) { if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { @@ -479,6 +526,74 @@ bool WM_xr_session_state_controller_aim_rotation_get(const wmXrData *xr, return true; } +bool WM_xr_session_state_nav_location_get(const wmXrData *xr, float r_location[3]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + zero_v3(r_location); + return false; + } + + copy_v3_v3(r_location, xr->runtime->session_state.nav_pose.position); + return true; +} + +void WM_xr_session_state_nav_location_set(wmXrData *xr, const float location[3]) +{ + if (WM_xr_session_exists(xr)) { + copy_v3_v3(xr->runtime->session_state.nav_pose.position, location); + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +bool WM_xr_session_state_nav_rotation_get(const wmXrData *xr, float r_rotation[4]) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + unit_qt(r_rotation); + return false; + } + + copy_qt_qt(r_rotation, xr->runtime->session_state.nav_pose.orientation_quat); + return true; +} + +void WM_xr_session_state_nav_rotation_set(wmXrData *xr, const float rotation[4]) +{ + if (WM_xr_session_exists(xr)) { + BLI_ASSERT_UNIT_QUAT(rotation); + copy_qt_qt(xr->runtime->session_state.nav_pose.orientation_quat, rotation); + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +bool WM_xr_session_state_nav_scale_get(const wmXrData *xr, float *r_scale) +{ + if (!WM_xr_session_is_ready(xr) || !xr->runtime->session_state.is_view_data_set) { + *r_scale = 1.0f; + return false; + } + + *r_scale = xr->runtime->session_state.nav_scale; + return true; +} + +void WM_xr_session_state_nav_scale_set(wmXrData *xr, float scale) +{ + if (WM_xr_session_exists(xr)) { + /* Clamp to reasonable values. */ + CLAMP(scale, xr->session_settings.clip_start, xr->session_settings.clip_end); + xr->runtime->session_state.nav_scale = scale; + xr->runtime->session_state.is_navigation_dirty = true; + } +} + +void WM_xr_session_state_navigation_reset(wmXrSessionState *state) +{ + zero_v3(state->nav_pose.position); + unit_qt(state->nav_pose.orientation_quat); + state->nav_scale = 1.0f; + state->is_navigation_dirty = true; +} + /* -------------------------------------------------------------------- */ /** \name XR-Session Actions * @@ -498,16 +613,21 @@ void wm_xr_session_actions_init(wmXrData *xr) static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, const float view_ofs[3], const float base_mat[4][4], + const float nav_mat[4][4], GHOST_XrPose *r_pose, - float r_mat[4][4]) + float r_mat[4][4], + float r_mat_base[4][4]) { float m[4][4]; /* Calculate controller matrix in world space. */ wm_xr_pose_to_mat(raw_pose, m); - /* Apply eye position and base pose offsets. */ + /* Apply eye position offset. */ sub_v3_v3(m[3], view_ofs); - mul_m4_m4m4(r_mat, base_mat, m); + + /* Apply base pose and navigation. */ + mul_m4_m4m4(r_mat_base, base_mat, m); + mul_m4_m4m4(r_mat, nav_mat, r_mat_base); /* Save final pose. */ mat4_to_loc_quat(r_pose->position, r_pose->orientation_quat, r_mat); @@ -516,13 +636,14 @@ static void wm_xr_session_controller_pose_calc(const GHOST_XrPose *raw_pose, static void wm_xr_session_controller_data_update(const XrSessionSettings *settings, const wmXrAction *grip_action, const wmXrAction *aim_action, + GHOST_XrContextHandle xr_context, wmXrSessionState *state) { BLI_assert(grip_action->count_subaction_paths == aim_action->count_subaction_paths); BLI_assert(grip_action->count_subaction_paths == BLI_listbase_count(&state->controllers)); unsigned int subaction_idx = 0; - float view_ofs[3], base_mat[4][4]; + float view_ofs[3], base_mat[4][4], nav_mat[4][4]; if ((settings->flag & XR_SESSION_USE_POSITION_TRACKING) == 0) { copy_v3_v3(view_ofs, state->prev_local_pose.position); @@ -534,45 +655,591 @@ static void wm_xr_session_controller_data_update(const XrSessionSettings *settin add_v3_v3(view_ofs, state->prev_eye_position_ofs); } - wm_xr_pose_to_mat(&state->prev_base_pose, base_mat); + wm_xr_pose_scale_to_mat(&state->prev_base_pose, state->prev_base_scale, base_mat); + wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, nav_mat); LISTBASE_FOREACH_INDEX (wmXrController *, controller, &state->controllers, subaction_idx) { wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)grip_action->states)[subaction_idx], view_ofs, base_mat, + nav_mat, &controller->grip_pose, - controller->grip_mat); + controller->grip_mat, + controller->grip_mat_base); wm_xr_session_controller_pose_calc(&((GHOST_XrPose *)aim_action->states)[subaction_idx], view_ofs, base_mat, + nav_mat, &controller->aim_pose, - controller->aim_mat); + controller->aim_mat, + controller->aim_mat_base); + + if (!controller->model) { + /* Notify GHOST to load/continue loading the controller model data. This can be called more + * than once since the model may not be available from the runtime yet. The batch itself will + * be created in wm_xr_draw_controllers(). */ + GHOST_XrLoadControllerModel(xr_context, controller->subaction_path); + } + else { + GHOST_XrUpdateControllerModelComponents(xr_context, controller->subaction_path); + } } } -void wm_xr_session_actions_update(wmXrData *xr) +static const GHOST_XrPose *wm_xr_session_controller_aim_pose_find(const wmXrSessionState *state, + const char *subaction_path) { + const wmXrController *controller = BLI_findstring( + &state->controllers, subaction_path, offsetof(wmXrController, subaction_path)); + return controller ? &controller->aim_pose : NULL; +} + +BLI_INLINE bool test_float_state(const float *state, float threshold, eXrAxisFlag flag) +{ + if ((flag & XR_AXIS0_POS) != 0) { + if (*state > threshold) { + return true; + } + } + else if ((flag & XR_AXIS0_NEG) != 0) { + if (*state < -threshold) { + return true; + } + } + else { + if (fabsf(*state) > threshold) { + return true; + } + } + return false; +} + +BLI_INLINE bool test_vec2f_state(const float state[2], float threshold, eXrAxisFlag flag) +{ + if ((flag & XR_AXIS0_POS) != 0) { + if (state[0] < 0.0f) { + return false; + } + } + else if ((flag & XR_AXIS0_NEG) != 0) { + if (state[0] > 0.0f) { + return false; + } + } + if ((flag & XR_AXIS1_POS) != 0) { + if (state[1] < 0.0f) { + return false; + } + } + else if ((flag & XR_AXIS1_NEG) != 0) { + if (state[1] > 0.0f) { + return false; + } + } + return (len_v2(state) > threshold); +} + +static bool wm_xr_session_modal_action_test(const ListBase *active_modal_actions, + const wmXrAction *action, + bool *r_found) +{ + if (r_found) { + *r_found = false; + } + + LISTBASE_FOREACH (LinkData *, ld, active_modal_actions) { + wmXrAction *active_modal_action = ld->data; + if (action == active_modal_action) { + if (r_found) { + *r_found = true; + } + return true; + } + if (action->ot == active_modal_action->ot && + IDP_EqualsProperties(action->op_properties, active_modal_action->op_properties)) { + /* Don't allow duplicate modal operators since this can lead to unwanted modal handler + * behavior. */ + return false; + } + } + + return true; +} + +static void wm_xr_session_modal_action_test_add(ListBase *active_modal_actions, + const wmXrAction *action) +{ + bool found; + if (wm_xr_session_modal_action_test(active_modal_actions, action, &found) && !found) { + LinkData *ld = MEM_callocN(sizeof(LinkData), __func__); + ld->data = (void *)action; + BLI_addtail(active_modal_actions, ld); + } +} + +static void wm_xr_session_modal_action_remove(ListBase *active_modal_actions, + const wmXrAction *action) +{ + LISTBASE_FOREACH (LinkData *, ld, active_modal_actions) { + if (action == ld->data) { + BLI_freelinkN(active_modal_actions, ld); + return; + } + } +} + +static wmXrHapticAction *wm_xr_session_haptic_action_find(ListBase *active_haptic_actions, + const wmXrAction *action, + const char *subaction_path) +{ + LISTBASE_FOREACH (wmXrHapticAction *, ha, active_haptic_actions) { + if ((action == ha->action) && (subaction_path == ha->subaction_path)) { + return ha; + } + } + return NULL; +} + +static void wm_xr_session_haptic_action_add(ListBase *active_haptic_actions, + const wmXrAction *action, + const char *subaction_path, + int64_t time_now) +{ + wmXrHapticAction *ha = wm_xr_session_haptic_action_find( + active_haptic_actions, action, subaction_path); + if (ha) { + /* Reset start time since OpenXR restarts haptics if they are already active. */ + ha->time_start = time_now; + } + else { + ha = MEM_callocN(sizeof(wmXrHapticAction), __func__); + ha->action = (wmXrAction *)action; + ha->subaction_path = subaction_path; + ha->time_start = time_now; + BLI_addtail(active_haptic_actions, ha); + } +} + +static void wm_xr_session_haptic_action_remove(ListBase *active_haptic_actions, + const wmXrAction *action) +{ + LISTBASE_FOREACH (wmXrHapticAction *, ha, active_haptic_actions) { + if (action == ha->action) { + BLI_freelinkN(active_haptic_actions, ha); + return; + } + } +} + +static void wm_xr_session_haptic_timers_check(ListBase *active_haptic_actions, int64_t time_now) +{ + LISTBASE_FOREACH_MUTABLE (wmXrHapticAction *, ha, active_haptic_actions) { + if (time_now - ha->time_start >= ha->action->haptic_duration) { + BLI_freelinkN(active_haptic_actions, ha); + } + } +} + +static void wm_xr_session_action_states_interpret(wmXrData *xr, + const char *action_set_name, + wmXrAction *action, + unsigned int subaction_idx, + ListBase *active_modal_actions, + ListBase *active_haptic_actions, + int64_t time_now, + bool modal, + bool haptic, + short *r_val) +{ + const char *haptic_subaction_path = ((action->haptic_flag & XR_HAPTIC_MATCHUSERPATHS) != 0) ? + action->subaction_paths[subaction_idx] : + NULL; + bool curr = false; + bool prev = false; + + switch (action->type) { + case XR_BOOLEAN_INPUT: { + const bool *state = &((bool *)action->states)[subaction_idx]; + bool *state_prev = &((bool *)action->states_prev)[subaction_idx]; + if (*state) { + curr = true; + } + if (*state_prev) { + prev = true; + } + *state_prev = *state; + break; + } + case XR_FLOAT_INPUT: { + const float *state = &((float *)action->states)[subaction_idx]; + float *state_prev = &((float *)action->states_prev)[subaction_idx]; + if (test_float_state( + state, action->float_thresholds[subaction_idx], action->axis_flags[subaction_idx])) { + curr = true; + } + if (test_float_state(state_prev, + action->float_thresholds[subaction_idx], + action->axis_flags[subaction_idx])) { + prev = true; + } + *state_prev = *state; + break; + } + case XR_VECTOR2F_INPUT: { + const float(*state)[2] = &((float(*)[2])action->states)[subaction_idx]; + float(*state_prev)[2] = &((float(*)[2])action->states_prev)[subaction_idx]; + if (test_vec2f_state(*state, + action->float_thresholds[subaction_idx], + action->axis_flags[subaction_idx])) { + curr = true; + } + if (test_vec2f_state(*state_prev, + action->float_thresholds[subaction_idx], + action->axis_flags[subaction_idx])) { + prev = true; + } + copy_v2_v2(*state_prev, *state); + break; + } + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (curr) { + if (!prev) { + if (modal || (action->op_flag == XR_OP_PRESS)) { + *r_val = KM_PRESS; + } + if (haptic && (action->haptic_flag & (XR_HAPTIC_PRESS | XR_HAPTIC_REPEAT)) != 0) { + /* Apply haptics. */ + if (WM_xr_haptic_action_apply(xr, + action_set_name, + action->haptic_name, + haptic_subaction_path, + &action->haptic_duration, + &action->haptic_frequency, + &action->haptic_amplitude)) { + wm_xr_session_haptic_action_add( + active_haptic_actions, action, haptic_subaction_path, time_now); + } + } + } + else if (modal) { + *r_val = KM_PRESS; + } + if (modal && !action->active_modal_path) { + /* Set active modal path. */ + action->active_modal_path = action->subaction_paths[subaction_idx]; + /* Add to active modal actions. */ + wm_xr_session_modal_action_test_add(active_modal_actions, action); + } + if (haptic && ((action->haptic_flag & XR_HAPTIC_REPEAT) != 0)) { + if (!wm_xr_session_haptic_action_find( + active_haptic_actions, action, haptic_subaction_path)) { + /* Apply haptics. */ + if (WM_xr_haptic_action_apply(xr, + action_set_name, + action->haptic_name, + haptic_subaction_path, + &action->haptic_duration, + &action->haptic_frequency, + &action->haptic_amplitude)) { + wm_xr_session_haptic_action_add( + active_haptic_actions, action, haptic_subaction_path, time_now); + } + } + } + } + else if (prev) { + if (modal || (action->op_flag == XR_OP_RELEASE)) { + *r_val = KM_RELEASE; + if (modal && (action->subaction_paths[subaction_idx] == action->active_modal_path)) { + /* Unset active modal path. */ + action->active_modal_path = NULL; + /* Remove from active modal actions. */ + wm_xr_session_modal_action_remove(active_modal_actions, action); + } + } + if (haptic) { + if ((action->haptic_flag & XR_HAPTIC_RELEASE) != 0) { + /* Apply haptics. */ + if (WM_xr_haptic_action_apply(xr, + action_set_name, + action->haptic_name, + haptic_subaction_path, + &action->haptic_duration, + &action->haptic_frequency, + &action->haptic_amplitude)) { + wm_xr_session_haptic_action_add( + active_haptic_actions, action, haptic_subaction_path, time_now); + } + } + else if ((action->haptic_flag & XR_HAPTIC_REPEAT) != 0) { + /* Stop any active haptics. */ + WM_xr_haptic_action_stop(xr, action_set_name, action->haptic_name, haptic_subaction_path); + wm_xr_session_haptic_action_remove(active_haptic_actions, action); + } + } + } +} + +static bool wm_xr_session_action_test_bimanual(const wmXrSessionState *session_state, + wmXrAction *action, + unsigned int subaction_idx, + unsigned int *r_subaction_idx_other, + const GHOST_XrPose **r_aim_pose_other) +{ + if ((action->action_flag & XR_ACTION_BIMANUAL) == 0) { + return false; + } + + bool bimanual = false; + + *r_subaction_idx_other = (subaction_idx == 0) ? + (unsigned int)min_ii(1, action->count_subaction_paths - 1) : + 0; + + switch (action->type) { + case XR_BOOLEAN_INPUT: { + const bool *state = &((bool *)action->states)[*r_subaction_idx_other]; + if (*state) { + bimanual = true; + } + break; + } + case XR_FLOAT_INPUT: { + const float *state = &((float *)action->states)[*r_subaction_idx_other]; + if (test_float_state(state, + action->float_thresholds[*r_subaction_idx_other], + action->axis_flags[*r_subaction_idx_other])) { + bimanual = true; + } + break; + } + case XR_VECTOR2F_INPUT: { + const float(*state)[2] = &((float(*)[2])action->states)[*r_subaction_idx_other]; + if (test_vec2f_state(*state, + action->float_thresholds[*r_subaction_idx_other], + action->axis_flags[*r_subaction_idx_other])) { + bimanual = true; + } + break; + } + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (bimanual) { + *r_aim_pose_other = wm_xr_session_controller_aim_pose_find( + session_state, action->subaction_paths[*r_subaction_idx_other]); + } + + return bimanual; +} + +static wmXrActionData *wm_xr_session_event_create(const char *action_set_name, + const wmXrAction *action, + const GHOST_XrPose *controller_aim_pose, + const GHOST_XrPose *controller_aim_pose_other, + unsigned int subaction_idx, + unsigned int subaction_idx_other, + bool bimanual) +{ + wmXrActionData *data = MEM_callocN(sizeof(wmXrActionData), __func__); + strcpy(data->action_set, action_set_name); + strcpy(data->action, action->name); + data->type = action->type; + + switch (action->type) { + case XR_BOOLEAN_INPUT: + data->state[0] = ((bool *)action->states)[subaction_idx] ? 1.0f : 0.0f; + if (bimanual) { + data->state_other[0] = ((bool *)action->states)[subaction_idx_other] ? 1.0f : 0.0f; + } + break; + case XR_FLOAT_INPUT: + data->state[0] = ((float *)action->states)[subaction_idx]; + if (bimanual) { + data->state_other[0] = ((float *)action->states)[subaction_idx_other]; + } + data->float_threshold = action->float_thresholds[subaction_idx]; + break; + case XR_VECTOR2F_INPUT: + copy_v2_v2(data->state, ((float(*)[2])action->states)[subaction_idx]); + if (bimanual) { + copy_v2_v2(data->state_other, ((float(*)[2])action->states)[subaction_idx_other]); + } + data->float_threshold = action->float_thresholds[subaction_idx]; + break; + case XR_POSE_INPUT: + case XR_VIBRATION_OUTPUT: + BLI_assert_unreachable(); + break; + } + + if (controller_aim_pose) { + copy_v3_v3(data->controller_loc, controller_aim_pose->position); + copy_qt_qt(data->controller_rot, controller_aim_pose->orientation_quat); + + if (bimanual && controller_aim_pose_other) { + copy_v3_v3(data->controller_loc_other, controller_aim_pose_other->position); + copy_qt_qt(data->controller_rot_other, controller_aim_pose_other->orientation_quat); + } + else { + data->controller_rot_other[0] = 1.0f; + } + } + else { + data->controller_rot[0] = 1.0f; + data->controller_rot_other[0] = 1.0f; + } + + data->ot = action->ot; + data->op_properties = action->op_properties; + + data->bimanual = bimanual; + + return data; +} + +/* Dispatch events to window queues. */ +static void wm_xr_session_events_dispatch(wmXrData *xr, + GHOST_XrContextHandle xr_context, + wmXrActionSet *action_set, + wmXrSessionState *session_state, + wmWindow *win) +{ + const char *action_set_name = action_set->name; + + const unsigned int count = GHOST_XrGetActionCount(xr_context, action_set_name); + if (count < 1) { + return; + } + + const int64_t time_now = (int64_t)(PIL_check_seconds_timer() * 1000); + + ListBase *active_modal_actions = &action_set->active_modal_actions; + ListBase *active_haptic_actions = &action_set->active_haptic_actions; + + wmXrAction **actions = MEM_calloc_arrayN(count, sizeof(*actions), __func__); + + GHOST_XrGetActionCustomdataArray(xr_context, action_set_name, (void **)actions); + + /* Check haptic action timers. */ + wm_xr_session_haptic_timers_check(active_haptic_actions, time_now); + + for (unsigned int action_idx = 0; action_idx < count; ++action_idx) { + wmXrAction *action = actions[action_idx]; + if (action && action->ot) { + const bool modal = action->ot->modal; + const bool haptic = (GHOST_XrGetActionCustomdata( + xr_context, action_set_name, action->haptic_name) != NULL); + + for (unsigned int subaction_idx = 0; subaction_idx < action->count_subaction_paths; + ++subaction_idx) { + short val = KM_NOTHING; + + /* Interpret action states (update modal/haptic action lists, apply haptics, etc). */ + wm_xr_session_action_states_interpret(xr, + action_set_name, + action, + subaction_idx, + active_modal_actions, + active_haptic_actions, + time_now, + modal, + haptic, + &val); + + const bool is_active_modal_action = wm_xr_session_modal_action_test( + active_modal_actions, action, NULL); + const bool is_active_modal_subaction = (!action->active_modal_path || + (action->subaction_paths[subaction_idx] == + action->active_modal_path)); + + if ((val != KM_NOTHING) && + (!modal || (is_active_modal_action && is_active_modal_subaction))) { + const GHOST_XrPose *aim_pose = wm_xr_session_controller_aim_pose_find( + session_state, action->subaction_paths[subaction_idx]); + const GHOST_XrPose *aim_pose_other = NULL; + unsigned int subaction_idx_other = 0; + + /* Test for bimanual interaction. */ + const bool bimanual = wm_xr_session_action_test_bimanual( + session_state, action, subaction_idx, &subaction_idx_other, &aim_pose_other); + + wmXrActionData *actiondata = wm_xr_session_event_create(action_set_name, + action, + aim_pose, + aim_pose_other, + subaction_idx, + subaction_idx_other, + bimanual); + wm_event_add_xrevent(win, actiondata, val); + } + } + } + } + + MEM_freeN(actions); +} + +void wm_xr_session_actions_update(wmWindowManager *wm) +{ + wmXrData *xr = &wm->xr; if (!xr->runtime) { return; } + XrSessionSettings *settings = &xr->session_settings; GHOST_XrContextHandle xr_context = xr->runtime->context; wmXrSessionState *state = &xr->runtime->session_state; wmXrActionSet *active_action_set = state->active_action_set; + if (state->is_navigation_dirty) { + memcpy(&state->nav_pose_prev, &state->nav_pose, sizeof(state->nav_pose_prev)); + state->nav_scale_prev = state->nav_scale; + state->is_navigation_dirty = false; + + /* Update viewer pose with any navigation changes since the last actions sync so that data + * is correct for queries. */ + float m[4][4], viewer_mat[4][4]; + wm_xr_pose_scale_to_mat(&state->nav_pose, state->nav_scale, m); + mul_m4_m4m4(viewer_mat, m, state->viewer_mat_base); + mat4_to_loc_quat(state->viewer_pose.position, state->viewer_pose.orientation_quat, viewer_mat); + wm_xr_pose_scale_to_imat( + &state->viewer_pose, settings->base_scale * state->nav_scale, state->viewer_viewmat); + } + int ret = GHOST_XrSyncActions(xr_context, active_action_set ? active_action_set->name : NULL); if (!ret) { return; } - /* Only update controller data for active action set. */ + /* Only update controller data and dispatch events for active action set. */ if (active_action_set) { + wmWindow *win = wm_xr_session_root_window_or_fallback_get(wm, xr->runtime); + if (active_action_set->controller_grip_action && active_action_set->controller_aim_action) { - wm_xr_session_controller_data_update(&xr->session_settings, + wm_xr_session_controller_data_update(settings, active_action_set->controller_grip_action, active_action_set->controller_aim_action, + xr_context, state); } + + if (win) { + /* Ensure an XR area exists for events. */ + if (!xr->runtime->area) { + xr->runtime->area = ED_area_offscreen_create(win, SPACE_VIEW3D); + } + + wm_xr_session_events_dispatch(xr, xr_context, active_action_set, state, win); + } } } @@ -598,11 +1265,33 @@ void wm_xr_session_controller_data_populate(const wmXrAction *grip_action, BLI_addtail(controllers, controller); } + + /* Activate draw callback. */ + if (g_xr_surface) { + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + if (surface_data && !surface_data->controller_draw_handle) { + if (surface_data->controller_art) { + surface_data->controller_draw_handle = ED_region_draw_cb_activate( + surface_data->controller_art, wm_xr_draw_controllers, xr, REGION_DRAW_POST_VIEW); + } + } + } } void wm_xr_session_controller_data_clear(wmXrSessionState *state) { wm_xr_session_controller_data_free(state); + + /* Deactivate draw callback. */ + if (g_xr_surface) { + wmXrSurfaceData *surface_data = g_xr_surface->customdata; + if (surface_data && surface_data->controller_draw_handle) { + if (surface_data->controller_art) { + ED_region_draw_cb_exit(surface_data->controller_art, surface_data->controller_draw_handle); + } + surface_data->controller_draw_handle = NULL; + } + } } /** \} */ /* XR-Session Actions */ @@ -628,7 +1317,7 @@ static void wm_xr_session_surface_draw(bContext *C) Main *bmain = CTX_data_main(C); wmXrDrawData draw_data; - if (!GHOST_XrSessionIsRunning(wm->xr.runtime->context)) { + if (!WM_xr_session_is_ready(&wm->xr)) { return; } @@ -728,6 +1417,11 @@ static void wm_xr_session_surface_free_data(wmSurface *surface) BLI_freelinkN(lb, vp); } + if (data->controller_art) { + BLI_freelistN(&data->controller_art->drawcalls); + MEM_freeN(data->controller_art); + } + MEM_freeN(surface->customdata); g_xr_surface = NULL; @@ -742,6 +1436,7 @@ static wmSurface *wm_xr_session_surface_create(void) wmSurface *surface = MEM_callocN(sizeof(*surface), __func__); wmXrSurfaceData *data = MEM_callocN(sizeof(*data), "XrSurfaceData"); + data->controller_art = MEM_callocN(sizeof(*(data->controller_art)), "XrControllerRegionType"); surface->draw = wm_xr_session_surface_draw; surface->free_data = wm_xr_session_surface_free_data; @@ -751,6 +1446,7 @@ static wmSurface *wm_xr_session_surface_create(void) surface->ghost_ctx = DRW_xr_opengl_context_get(); surface->gpu_ctx = DRW_xr_gpu_context_get(); + data->controller_art->regionid = RGN_TYPE_XR; surface->customdata = data; g_xr_surface = surface; @@ -784,4 +1480,14 @@ void wm_xr_session_gpu_binding_context_destroy(GHOST_ContextHandle UNUSED(contex WM_main_add_notifier(NC_WM | ND_XR_DATA_CHANGED, NULL); } +ARegionType *WM_xr_surface_controller_region_type_get(void) +{ + if (g_xr_surface) { + wmXrSurfaceData *data = g_xr_surface->customdata; + return data->controller_art; + } + + return NULL; +} + /** \} */ /* XR-Session Surface */ diff --git a/source/blender/windowmanager/xr/wm_xr.h b/source/blender/windowmanager/xr/wm_xr.h index 0f0fbe8bc00..caba6038c56 100644 --- a/source/blender/windowmanager/xr/wm_xr.h +++ b/source/blender/windowmanager/xr/wm_xr.h @@ -30,3 +30,6 @@ bool wm_xr_init(wmWindowManager *wm); void wm_xr_exit(wmWindowManager *wm); void wm_xr_session_toggle(wmWindowManager *wm, wmWindow *win, wmXrSessionExitFn session_exit_fn); bool wm_xr_events_handle(wmWindowManager *wm); + +/* wm_xr_operators.c */ +void wm_xr_operatortypes_register(void); diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 47fb2642da1..816d3a60fc3 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -515,11 +515,12 @@ if(UNIX AND NOT APPLE) DESTINATION "." ) - install( - PROGRAMS - ${CMAKE_SOURCE_DIR}/release/bin/blender-thumbnailer.py - DESTINATION "." - ) + if(WITH_BLENDER_THUMBNAILER) + install( + TARGETS blender-thumbnailer + DESTINATION "." + ) + endif() if(EXISTS ${LIBDIR}/mesa) install(DIRECTORY ${LIBDIR}/mesa/lib DESTINATION ".") @@ -559,10 +560,12 @@ if(UNIX AND NOT APPLE) FILES ${CMAKE_SOURCE_DIR}/release/freedesktop/icons/symbolic/apps/blender-symbolic.svg DESTINATION share/icons/hicolor/symbolic/apps ) - install( - PROGRAMS ${CMAKE_SOURCE_DIR}/release/bin/blender-thumbnailer.py - DESTINATION bin - ) + if(WITH_BLENDER_THUMBNAILER) + install( + TARGETS blender-thumbnailer + DESTINATION bin + ) + endif() set(BLENDER_TEXT_FILES_DESTINATION share/doc/blender) endif() @@ -636,6 +639,36 @@ if(UNIX AND NOT APPLE) PATTERN "*.h" EXCLUDE # some includes are not in include dirs PATTERN "*.a" EXCLUDE # ./core/lib/libnpymath.a - for linking, we don't need. ) + install( + DIRECTORY ${PYTHON_NUMPY_PATH}/Cython + DESTINATION ${TARGETDIR_VER}/python/${_target_LIB}/python${PYTHON_VERSION}/${_suffix} + PATTERN ".svn" EXCLUDE + PATTERN "__pycache__" EXCLUDE # * any cache * + PATTERN "*.pyc" EXCLUDE # * any cache * + PATTERN "*.pyo" EXCLUDE # * any cache * + ) + install( + FILES ${PYTHON_NUMPY_PATH}/cython.py + DESTINATION ${TARGETDIR_VER}/python/${_target_LIB}/python${PYTHON_VERSION}/${_suffix} + ) + unset(_suffix) + endif() + + if(WITH_PYTHON_INSTALL_ZSTANDARD) + # Install to the same directory as the source, so debian-like + # distros are happy with their policy. + set(_suffix "site-packages") + if(${PYTHON_NUMPY_PATH} MATCHES "dist-packages") + set(_suffix "dist-packages") + endif() + install( + DIRECTORY ${PYTHON_NUMPY_PATH}/zstandard + DESTINATION ${TARGETDIR_VER}/python/${_target_LIB}/python${PYTHON_VERSION}/${_suffix} + PATTERN ".svn" EXCLUDE + PATTERN "__pycache__" EXCLUDE # * any cache * + PATTERN "*.pyc" EXCLUDE # * any cache * + PATTERN "*.pyo" EXCLUDE # * any cache * + ) unset(_suffix) endif() @@ -957,6 +990,13 @@ elseif(WIN32) DESTINATION "." ) + if(WITH_BLENDER_THUMBNAILER) + install( + TARGETS BlendThumb + DESTINATION "." + ) + endif() + if(WITH_DRACO) install( PROGRAMS $ diff --git a/source/creator/creator.c b/source/creator/creator.c index 2ec4a2aa616..4171d60b5b6 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -54,7 +54,6 @@ #include "BKE_cachefile.h" #include "BKE_callbacks.h" #include "BKE_context.h" -#include "BKE_font.h" #include "BKE_global.h" #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" @@ -65,6 +64,7 @@ #include "BKE_particle.h" #include "BKE_shader_fx.h" #include "BKE_sound.h" +#include "BKE_vfont.h" #include "BKE_volume.h" #include "DEG_depsgraph.h" @@ -414,7 +414,6 @@ int main(int argc, BKE_idtype_init(); BKE_cachefiles_init(); - BKE_images_init(); BKE_modifier_init(); BKE_gpencil_modifier_init(); BKE_shaderfx_init(); diff --git a/source/creator/creator_signals.c b/source/creator/creator_signals.c index 29e12a96fe1..b74264fdddd 100644 --- a/source/creator/creator_signals.c +++ b/source/creator/creator_signals.c @@ -79,7 +79,7 @@ static void sig_handle_fpe(int UNUSED(sig)) } # endif -/* handling ctrl-c event in console */ +/* Handling `Ctrl-C` event in the console. */ # if !defined(WITH_HEADLESS) static void sig_handle_blender_esc(int sig) { @@ -258,7 +258,9 @@ void main_signal_setup_background(void) BLI_assert(G.background); # if !defined(WITH_HEADLESS) - signal(SIGINT, sig_handle_blender_esc); /* ctrl c out bg render */ + /* Support pressing `Ctrl-C` to close Blender in background-mode. + * Useful to be able to cancel a render operation. */ + signal(SIGINT, sig_handle_blender_esc); # endif } diff --git a/tests/performance/api/device.py b/tests/performance/api/device.py index b61ae42be36..1e930a12352 100644 --- a/tests/performance/api/device.py +++ b/tests/performance/api/device.py @@ -11,7 +11,7 @@ def get_cpu_name() -> str: return platform.processor() elif platform.system() == "Darwin": cmd = ['/usr/sbin/sysctl', "-n", "machdep.cpu.brand_string"] - return subprocess.check_output(cmd).strip().decode('utf-8') + return subprocess.check_output(cmd).strip().decode('utf-8', 'ignore') else: with open('/proc/cpuinfo') as f: for line in f: diff --git a/tests/performance/api/graph.py b/tests/performance/api/graph.py index e54adc194de..c7d3c5043ec 100644 --- a/tests/performance/api/graph.py +++ b/tests/performance/api/graph.py @@ -22,7 +22,7 @@ class TestGraph: for entry in queue.entries: if entry.status in ('done', 'outdated'): - device_name = entry.device_name + device_name = entry.device_name + " (" + entry.device_type + ")" if device_name in devices.keys(): devices[device_name].append(entry) else: diff --git a/tests/performance/benchmark b/tests/performance/benchmark index a58c339e9f8..80556674dcc 100755 --- a/tests/performance/benchmark +++ b/tests/performance/benchmark @@ -4,6 +4,7 @@ import api import argparse import fnmatch +import glob import pathlib import shutil import sys @@ -228,6 +229,9 @@ def cmd_reset(env: api.TestEnvironment, argv: List): config.queue.write() + if args.test == '*': + shutil.rmtree(config.logs_dir) + def cmd_run(env: api.TestEnvironment, argv: List, update_only: bool): # Run tests. parser = argparse.ArgumentParser() @@ -274,7 +278,17 @@ def cmd_graph(argv: List): parser.add_argument('-o', '--output', type=str, required=True) args = parser.parse_args(argv) - graph = api.TestGraph([pathlib.Path(path) for path in args.json_file]) + # For directories, use all json files in the directory. + json_files = [] + for path in args.json_file: + path = pathlib.Path(path) + if path.is_dir(): + for filepath in glob.iglob(str(path / '*.json')): + json_files.append(pathlib.Path(filepath)) + else: + json_files.append(path) + + graph = api.TestGraph(json_files) graph.write(pathlib.Path(args.output)) def main(): diff --git a/tests/performance/tests/animation.py b/tests/performance/tests/animation.py index 1a92f1a9718..c1a5c77860f 100644 --- a/tests/performance/tests/animation.py +++ b/tests/performance/tests/animation.py @@ -9,14 +9,20 @@ def _run(args): import time start_time = time.time() + elapsed_time = 0.0 + num_frames = 0 - scene = bpy.context.scene - for i in range(scene.frame_start, scene.frame_end): - scene.frame_set(scene.frame_start) + while elapsed_time < 10.0: + scene = bpy.context.scene + for i in range(scene.frame_start, scene.frame_end + 1): + scene.frame_set(i) - elapsed_time = time.time() - start_time + num_frames += scene.frame_end + 1 - scene.frame_start + elapsed_time = time.time() - start_time - result = {'time': elapsed_time} + time_per_frame = elapsed_time / num_frames + + result = {'time': time_per_frame} return result @@ -32,10 +38,10 @@ class AnimationTest(api.Test): def run(self, env, device_id): args = {} - result, _ = env.run_in_blender(_run, args) + result, _ = env.run_in_blender(_run, args, [self.filepath]) return result def generate(env): - filepaths = env.find_blend_files('animation') + filepaths = env.find_blend_files('animation/*') return [AnimationTest(filepath) for filepath in filepaths] diff --git a/tests/performance/tests/cycles.py b/tests/performance/tests/cycles.py index bac6b8a7ceb..e702fa445d2 100644 --- a/tests/performance/tests/cycles.py +++ b/tests/performance/tests/cycles.py @@ -17,6 +17,16 @@ def _run(args): scene.render.image_settings.file_format = 'PNG' scene.cycles.device = 'CPU' if device_type == 'CPU' else 'GPU' + if scene.cycles.use_adaptive_sampling: + # Render samples specified in file, no other way to measure + # adaptive sampling performance reliably. + scene.cycles.time_limit = 0.0 + else: + # Render for fixed amount of time so it's adaptive to the + # machine and devices. + scene.cycles.samples = 16384 + scene.cycles.time_limit = 10.0 + if scene.cycles.device == 'GPU': # Enable specified GPU in preferences. prefs = bpy.context.preferences @@ -62,12 +72,14 @@ class CyclesTest(api.Test): 'device_index': device_index, 'render_filepath': str(env.log_file.parent / (env.log_file.stem + '.png'))} - _, lines = env.run_in_blender(_run, args, ['--debug-cycles', '--verbose', '1', self.filepath]) + _, lines = env.run_in_blender(_run, args, ['--debug-cycles', '--verbose', '2', self.filepath]) # Parse render time from output prefix_time = "Render time (without synchronization): " prefix_memory = "Peak: " + prefix_time_per_sample = "Average time per sample: " time = None + time_per_sample = None memory = None for line in lines: line = line.strip() @@ -75,12 +87,20 @@ class CyclesTest(api.Test): if offset != -1: time = line[offset + len(prefix_time):] time = float(time) + offset = line.find(prefix_time_per_sample) + if offset != -1: + time_per_sample = line[offset + len(prefix_time_per_sample):] + time_per_sample = time_per_sample.split()[0] + time_per_sample = float(time_per_sample) offset = line.find(prefix_memory) if offset != -1: memory = line[offset + len(prefix_memory):] memory = memory.split()[0].replace(',', '') memory = float(memory) + if time_per_sample: + time = time_per_sample + if not (time and memory): raise Exception("Error parsing render time output") @@ -88,5 +108,5 @@ class CyclesTest(api.Test): def generate(env): - filepaths = env.find_blend_files('cycles-x/*') + filepaths = env.find_blend_files('cycles/*') return [CyclesTest(filepath) for filepath in filepaths] diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index a1b94abc317..92d46eb8d80 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -637,7 +637,6 @@ if(WITH_CYCLES OR WITH_OPENGL_RENDER_TESTS) set(render_tests bsdf denoise - denoise_animation displacement hair image_colorspace @@ -750,10 +749,20 @@ set(geo_node_tests points utilities vector - volume - ) +if(WITH_GMP) + list(APPEND geo_node_tests mesh/boolean) +endif() + +if(WITH_OPENVDB) + list(APPEND geo_node_tests volume) +endif() + +if(WITH_OPENSUBDIV) + list(APPEND geo_node_tests mesh/subdivision_tests) +endif() + foreach(geo_node_test ${geo_node_tests}) if(EXISTS "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/") file(GLOB files "${TEST_SRC_DIR}/modeling/geometry_nodes/${geo_node_test}/*.blend") @@ -823,6 +832,15 @@ if(WITH_ALEMBIC) ) endif() +if(WITH_USD) + add_blender_test( + usd_import_test + --python ${CMAKE_CURRENT_LIST_DIR}/bl_usd_import_test.py + -- + --testdir "${TEST_SRC_DIR}/usd" + ) +endif() + if(WITH_CODEC_FFMPEG) add_python_test( ffmpeg diff --git a/tests/python/bl_blendfile_liblink.py b/tests/python/bl_blendfile_liblink.py index 992bf6b89d9..6017d3f1a1e 100644 --- a/tests/python/bl_blendfile_liblink.py +++ b/tests/python/bl_blendfile_liblink.py @@ -10,7 +10,7 @@ from bl_blendfile_utils import TestHelper class TestBlendLibLinkHelper(TestHelper): - + def __init__(self, args): self.args = args @@ -34,7 +34,44 @@ class TestBlendLibLinkHelper(TestHelper): output_dir = self.args.output_dir self.ensure_path(output_dir) # Take care to keep the name unique so multiple test jobs can run at once. - output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib")) + output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_basic")) + + bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False) + + return output_lib_path + + def init_lib_data_indirect_lib(self): + output_dir = self.args.output_dir + self.ensure_path(output_dir) + + # Create an indirect library containing a material. + self.reset_blender() + + ma = bpy.data.materials.new("LibMaterial") + ma.use_fake_user = True + # Take care to keep the name unique so multiple test jobs can run at once. + output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_indirect_material")) + + bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False) + + # Create a main library containing object etc., and linking material from indirect library. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Material") + bpy.ops.wm.link(directory=link_dir, filename="LibMaterial") + ma = bpy.data.materials[0] + + me = bpy.data.meshes.new("LibMesh") + me.materials.append(ma) + ob = bpy.data.objects.new("LibMesh", me) + coll = bpy.data.collections.new("LibMesh") + coll.objects.link(ob) + bpy.context.scene.collection.children.link(coll) + + output_dir = self.args.output_dir + self.ensure_path(output_dir) + # Take care to keep the name unique so multiple test jobs can run at once. + output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_indirect_main")) bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False) @@ -158,15 +195,20 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): def test_append(self): output_dir = self.args.output_dir - output_lib_path = self.init_lib_data_basic() + output_lib_path = self.init_lib_data_indirect_lib() # Simple append of a single ObData. self.reset_blender() link_dir = os.path.join(output_lib_path, "Mesh") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=False) + instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False) + print(bpy.data.materials[:], bpy.data.materials[0].library, bpy.data.materials[0].users, bpy.data.materials[0].use_fake_user) + + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].use_fake_user is False) @@ -179,8 +221,11 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Mesh") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=True, set_fake=False, use_recursive=False) + instance_object_data=True, set_fake=False, use_recursive=False, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].use_fake_user is False) @@ -194,8 +239,11 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Mesh") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=True, use_recursive=False) + instance_object_data=False, set_fake=True, use_recursive=False, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].use_fake_user is True) @@ -208,11 +256,13 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Object") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=False) + instance_object_data=False, set_fake=False, use_recursive=False, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is not None) + assert(bpy.data.materials[0].users == 2) # Fake user is not cleared when linking. assert(len(bpy.data.meshes) == 1) - # This one fails currently, for unclear reasons. - assert(bpy.data.meshes[0].library is not None) + assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) assert(len(bpy.data.objects) == 1) assert(bpy.data.objects[0].library is None) @@ -224,8 +274,11 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Object") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=True) + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is None) + assert(bpy.data.materials[0].users == 1) # Fake user is cleared when appending. assert(len(bpy.data.meshes) == 1) assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) @@ -239,8 +292,11 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): link_dir = os.path.join(output_lib_path, "Collection") bpy.ops.wm.append(directory=link_dir, filename="LibMesh", - instance_object_data=False, set_fake=False, use_recursive=True) + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + assert(len(bpy.data.materials) == 1) + assert(bpy.data.materials[0].library is None) + assert(bpy.data.materials[0].users == 1) # Fake user is cleared when appending. assert(bpy.data.meshes[0].library is None) assert(bpy.data.meshes[0].users == 1) assert(len(bpy.data.objects) == 1) @@ -251,9 +307,132 @@ class TestBlendLibAppendBasic(TestBlendLibLinkHelper): assert(bpy.data.collections[0].users == 1) +class TestBlendLibAppendReuseID(TestBlendLibLinkHelper): + + def __init__(self, args): + self.args = args + + def test_append(self): + output_dir = self.args.output_dir + output_lib_path = self.init_lib_data_basic() + + # Append of a single Object, and then append it again. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Object") + bpy.ops.wm.append(directory=link_dir, filename="LibMesh", + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + + assert(len(bpy.data.meshes) == 1) + assert(bpy.data.meshes[0].library is None) + assert(bpy.data.meshes[0].use_fake_user is False) + assert(bpy.data.meshes[0].users == 1) + assert(bpy.data.meshes[0].library_weak_reference is not None) + assert(bpy.data.meshes[0].library_weak_reference.filepath == output_lib_path) + assert(bpy.data.meshes[0].library_weak_reference.id_name == "MELibMesh") + assert(len(bpy.data.objects) == 1) + for ob in bpy.data.objects: + assert(ob.library is None) + assert(ob.library_weak_reference is None) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + bpy.ops.wm.append(directory=link_dir, filename="LibMesh", + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=True) + + assert(len(bpy.data.meshes) == 1) + assert(bpy.data.meshes[0].library is None) + assert(bpy.data.meshes[0].use_fake_user is False) + assert(bpy.data.meshes[0].users == 2) + assert(bpy.data.meshes[0].library_weak_reference is not None) + assert(bpy.data.meshes[0].library_weak_reference.filepath == output_lib_path) + assert(bpy.data.meshes[0].library_weak_reference.id_name == "MELibMesh") + assert(len(bpy.data.objects) == 2) + for ob in bpy.data.objects: + assert(ob.library is None) + assert(ob.library_weak_reference is None) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + bpy.ops.wm.append(directory=link_dir, filename="LibMesh", + instance_object_data=False, set_fake=False, use_recursive=True, do_reuse_local_id=False) + + assert(len(bpy.data.meshes) == 2) + assert(bpy.data.meshes[0].library_weak_reference is None) + assert(bpy.data.meshes[1].library is None) + assert(bpy.data.meshes[1].use_fake_user is False) + assert(bpy.data.meshes[1].users == 1) + assert(bpy.data.meshes[1].library_weak_reference is not None) + assert(bpy.data.meshes[1].library_weak_reference.filepath == output_lib_path) + assert(bpy.data.meshes[1].library_weak_reference.id_name == "MELibMesh") + assert(len(bpy.data.objects) == 3) + for ob in bpy.data.objects: + assert(ob.library is None) + assert(ob.library_weak_reference is None) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + +class TestBlendLibLibraryReloadRelocate(TestBlendLibLinkHelper): + + def __init__(self, args): + self.args = args + + def test_link_reload(self): + output_dir = self.args.output_dir + output_lib_path = self.init_lib_data_basic() + + # Simple link of a single Object, and reload. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Object") + bpy.ops.wm.link(directory=link_dir, filename="LibMesh") + + assert(len(bpy.data.meshes) == 1) + assert(len(bpy.data.objects) == 1) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + orig_data = self.blender_data_to_tuple(bpy.data, "orig_data") + + bpy.ops.wm.lib_reload(library=bpy.data.objects[0].name) + + reload_data = self.blender_data_to_tuple(bpy.data, "reload_data") + + print(orig_data) + print(reload_data) + assert(orig_data == reload_data) + + def test_link_relocate(self): + output_dir = self.args.output_dir + output_lib_path = self.init_lib_data_basic() + + # Simple link of a single Object, and reload. + self.reset_blender() + + link_dir = os.path.join(output_lib_path, "Object") + bpy.ops.wm.link(directory=link_dir, filename="LibMesh") + + assert(len(bpy.data.meshes) == 1) + assert(len(bpy.data.objects) == 1) + assert(len(bpy.data.collections) == 0) # Scene's master collection is not listed here + + orig_data = self.blender_data_to_tuple(bpy.data, "orig_data") + + lib_path, lib_ext = os.path.splitext(output_lib_path) + new_lib_path = lib_path + "_relocate" + lib_ext + os.rename(output_lib_path, new_lib_path) + + bpy.ops.wm.lib_relocate(library=bpy.data.objects[0].name, directory="", filename=new_lib_path) + + relocate_data = self.blender_data_to_tuple(bpy.data, "relocate_data") + + print(orig_data) + print(relocate_data) + assert(orig_data == relocate_data) + + TESTS = ( TestBlendLibLinkSaveLoadBasic, TestBlendLibAppendBasic, + TestBlendLibAppendReuseID, + TestBlendLibLibraryReloadRelocate, ) diff --git a/tests/python/bl_bundled_modules.py b/tests/python/bl_bundled_modules.py index d3fe2861d9e..012eaf8246d 100644 --- a/tests/python/bl_bundled_modules.py +++ b/tests/python/bl_bundled_modules.py @@ -21,9 +21,14 @@ # Test that modules we ship with our Python installation are available import bz2 +import certifi import ctypes +import cython import lzma import numpy +import requests import sqlite3 import ssl +import urllib3 import zlib +import zstandard diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py index bdf625c1c2e..4cee39fafb0 100644 --- a/tests/python/bl_pyapi_idprop.py +++ b/tests/python/bl_pyapi_idprop.py @@ -4,9 +4,13 @@ import bpy import idprop import unittest -import numpy as np from array import array +# Run if `numpy` is installed. +try: + import numpy as np +except ImportError: + np = None class TestHelper: @@ -189,38 +193,6 @@ class TestIdPropertyGroupView(TestHelper, unittest.TestCase): class TestBufferProtocol(TestHelper, unittest.TestCase): - def test_int(self): - self.id["a"] = array("i", [1, 2, 3, 4, 5]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - self.assertEqual(len(a), 5) - a[2] = 10 - self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5]) - - def test_float(self): - self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - self.assertEqual(len(a), 4) - a[-1] = 10 - self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0]) - - def test_double(self): - self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - a[1] = 10 - self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0]) - - def test_full_update(self): - self.id["a"] = array("i", [1, 2, 3, 4, 5, 6]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - a[:] = [10, 20, 30, 40, 50, 60] - self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60]) - - def test_partial_update(self): - self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8]) - a = np.frombuffer(self.id["a"], self.id["a"].typecode) - a[1:5] = [10, 20, 30, 40] - self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8]) - def test_copy(self): self.id["a"] = array("i", [1, 2, 3, 4, 5]) self.id["b"] = self.id["a"] @@ -246,6 +218,42 @@ class TestBufferProtocol(TestHelper, unittest.TestCase): self.assertEqual(list(view1), list(view2)) self.assertEqual(view1.tobytes(), view2.tobytes()) + +if np is not None: + class TestBufferProtocol_Numpy(TestHelper, unittest.TestCase): + def test_int(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + self.assertEqual(len(a), 5) + a[2] = 10 + self.assertEqual(self.id["a"].to_list(), [1, 2, 10, 4, 5]) + + def test_float(self): + self.id["a"] = array("f", [1.0, 2.0, 3.0, 4.0]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + self.assertEqual(len(a), 4) + a[-1] = 10 + self.assertEqual(self.id["a"].to_list(), [1.0, 2.0, 3.0, 10.0]) + + def test_double(self): + self.id["a"] = array("d", [1.0, 2.0, 3.0, 4.0]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[1] = 10 + self.assertEqual(self.id["a"].to_list(), [1.0, 10.0, 3.0, 4.0]) + + def test_full_update(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5, 6]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[:] = [10, 20, 30, 40, 50, 60] + self.assertEqual(self.id["a"].to_list(), [10, 20, 30, 40, 50, 60]) + + def test_partial_update(self): + self.id["a"] = array("i", [1, 2, 3, 4, 5, 6, 7, 8]) + a = np.frombuffer(self.id["a"], self.id["a"].typecode) + a[1:5] = [10, 20, 30, 40] + self.assertEqual(self.id["a"].to_list(), [1, 10, 20, 30, 40, 6, 7, 8]) + + class TestRNAData(TestHelper, unittest.TestCase): def test_custom_properties_none(self): @@ -309,7 +317,6 @@ class TestRNAData(TestHelper, unittest.TestCase): self.assertEqual(rna_data["default"], [1, 2]) - if __name__ == '__main__': import sys sys.argv = [__file__] + (sys.argv[sys.argv.index("--") + 1:] if "--" in sys.argv else []) diff --git a/tests/python/bl_pyapi_idprop_datablock.py b/tests/python/bl_pyapi_idprop_datablock.py index ca52c1b01fe..5a7e4bee4fc 100644 --- a/tests/python/bl_pyapi_idprop_datablock.py +++ b/tests/python/bl_pyapi_idprop_datablock.py @@ -16,17 +16,25 @@ # # ##### END GPL LICENSE BLOCK ##### -import bpy -import sys -import os -import tempfile -import inspect -from bpy.types import UIList +# ./blender.bin --background -noaudio --python tests/python/bl_pyapi_idprop_datablock.py -- --verbose +import contextlib +import inspect +import io +import os +import re +import sys +import tempfile + +import bpy + +from bpy.types import UIList arr_len = 100 ob_cp_count = 100 -lib_path = os.path.join(tempfile.gettempdir(), "lib.blend") -test_path = os.path.join(tempfile.gettempdir(), "test.blend") + +# Set before execution. +lib_path = None +test_path = None def print_fail_msg_and_exit(msg): @@ -45,13 +53,40 @@ def print_fail_msg_and_exit(msg): os._exit(1) -def abort_if_false(expr, msg=None): +def expect_false_or_abort(expr, msg=None): if not expr: if not msg: msg = "test failed" print_fail_msg_and_exit(msg) +def expect_exception_or_abort(*, fn, ex): + try: + fn() + exception = False + except ex: + exception = True + if exception: + return # OK + print_fail_msg_and_exit("test failed") + + +def expect_ouput_or_abort(*, fn, match_stderr=None, match_stdout=None): + + stdout, stderr = io.StringIO(), io.StringIO() + + with (contextlib.redirect_stderr(stderr), contextlib.redirect_stdout(stdout)): + fn() + + for (handle, match) in ((stdout, match_stdout), (stderr, match_stderr)): + if not match: + continue + handle.seek(0) + output = handle.read() + if not re.match(match, output): + print_fail_msg_and_exit("%r not found in %r" % (match, output)) + + class TestClass(bpy.types.PropertyGroup): test_prop: bpy.props.PointerProperty(type=bpy.types.Object) name: bpy.props.StringProperty() @@ -67,14 +102,6 @@ def get_scene(lib_name, sce_name): return s -def check_crash(fnc, args=None): - try: - fnc(args) if args else fnc() - except: - return - print_fail_msg_and_exit("test failed") - - def init(): bpy.utils.register_class(TestClass) bpy.types.Object.prop_array = bpy.props.CollectionProperty( @@ -118,12 +145,13 @@ def make_lib(): def check_lib(): # check pointer - abort_if_false(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera']) + expect_false_or_abort(bpy.data.objects["Cube"].prop == bpy.data.objects['Camera']) # check array of pointers in duplicated object for i in range(0, arr_len): - abort_if_false(bpy.data.objects["Cube.001"].prop_array[i].test_prop == - bpy.data.objects['Light']) + expect_false_or_abort( + bpy.data.objects["Cube.001"].prop_array[i].test_prop == + bpy.data.objects['Light']) def check_lib_linking(): @@ -136,9 +164,9 @@ def check_lib_linking(): o = bpy.data.scenes["Scene_lib"].objects['Unique_Cube'] - abort_if_false(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Light']) - abort_if_false(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera']) - abort_if_false(o.prop.library == o.library) + expect_false_or_abort(o.prop_array[0].test_prop == bpy.data.scenes["Scene_lib"].objects['Light']) + expect_false_or_abort(o.prop == bpy.data.scenes["Scene_lib"].objects['Camera']) + expect_false_or_abort(o.prop.library == o.library) bpy.ops.wm.save_as_mainfile(filepath=test_path) @@ -158,9 +186,10 @@ def check_linked_scene_copying(): # check node's props # must point to own scene camera - abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and - not (intern_sce.node_tree.nodes['Render Layers']["prop"] == - extern_sce.node_tree.nodes['Render Layers']["prop"])) + expect_false_or_abort( + intern_sce.node_tree.nodes['Render Layers']["prop"] and + not (intern_sce.node_tree.nodes['Render Layers']["prop"] == + extern_sce.node_tree.nodes['Render Layers']["prop"])) def check_scene_copying(): @@ -179,8 +208,9 @@ def check_scene_copying(): # check node's props # must point to own scene camera - abort_if_false(not (first_sce.node_tree.nodes['Render Layers']["prop"] == - second_sce.node_tree.nodes['Render Layers']["prop"])) + expect_false_or_abort( + not (first_sce.node_tree.nodes['Render Layers']["prop"] == + second_sce.node_tree.nodes['Render Layers']["prop"])) # count users @@ -190,11 +220,11 @@ def test_users_counting(): n = 1000 for i in range(0, n): bpy.data.objects["Cube"]["a%s" % i] = bpy.data.objects["Light"].data - abort_if_false(bpy.data.objects["Light"].data.users == Light_us + n) + expect_false_or_abort(bpy.data.objects["Light"].data.users == Light_us + n) for i in range(0, int(n / 2)): bpy.data.objects["Cube"]["a%s" % i] = 1 - abort_if_false(bpy.data.objects["Light"].data.users == Light_us + int(n / 2)) + expect_false_or_abort(bpy.data.objects["Light"].data.users == Light_us + int(n / 2)) # linking @@ -227,8 +257,8 @@ def test_restrictions1(): # just panel for testing the poll callback with lots of objects class TEST_PT_DatablockProp(bpy.types.Panel): bl_label = "Datablock IDProp" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" + bl_space_type = 'PROPERTIES' + bl_region_type = 'WINDOW' bl_context = "render" def draw(self, context): @@ -236,16 +266,22 @@ def test_restrictions1(): self.layout.template_ID(context.scene, "prop1") self.layout.prop_search(context.scene, "prop2", bpy.data, "node_groups") - op = self.layout.operator("scene.test_op") + op = self.layout.operator(TEST_Op.bl_idname) op.str_prop = "test string" - def test_fnc(op): + def test_fn(op): op["ob"] = bpy.data.objects['Unique_Cube'] - check_crash(test_fnc, op) - abort_if_false(not hasattr(op, "id_prop")) + expect_exception_or_abort( + fn=lambda: test_fn(op), + ex=ImportError, + ) + expect_false_or_abort(not hasattr(op, "id_prop")) bpy.utils.register_class(TEST_PT_DatablockProp) - bpy.utils.register_class(TEST_Op) + expect_ouput_or_abort( + fn=lambda: bpy.utils.register_class(TEST_Op), + match_stderr="^ValueError: bpy_struct \"SCENE_OT_test_op\" registration error:", + ) def poll(self, value): return value.name in bpy.data.scenes["Scene_lib"].objects @@ -266,12 +302,18 @@ def test_restrictions1(): # NodeTree id_prop bpy.context.scene.prop2 = bpy.data.objects["Light.001"] - check_crash(sub_test) + expect_exception_or_abort( + fn=sub_test, + ex=TypeError, + ) bpy.context.scene.prop2 = bpy.data.node_groups.new("Shader", "ShaderNodeTree") - print("Please, test GUI performance manually on the Render tab, '%s' panel" % - TEST_PT_DatablockProp.bl_label, file=sys.stderr) + # NOTE: keep since the author thought this useful information. + # print( + # "Please, test GUI performance manually on the Render tab, '%s' panel" % + # TEST_PT_DatablockProp.bl_label, file=sys.stderr, + # ) sys.stderr.flush() @@ -308,25 +350,40 @@ def test_restrictions2(): bpy.types.Addon.a = bpy.props.PointerProperty(type=bpy.types.Object) - class TestUIList(UIList): + class TEST_UL_list(UIList): test: bpy.props.PointerProperty(type=bpy.types.Object) def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index): layout.prop(item, "name", text="", emboss=False, icon_value=icon) - check_crash(bpy.utils.register_class, TestPrefs) - check_crash(bpy.utils.register_class, TestUIList) + expect_exception_or_abort( + fn=lambda: bpy.utils.register_class(TestPrefs), + ex=ValueError, + ) + expect_exception_or_abort( + fn=lambda: bpy.utils.register_class(TEST_UL_list), + ex=ValueError, + ) bpy.utils.unregister_class(TestClassCollection) def main(): - init() - test_users_counting() - test_linking() - test_restrictions1() - check_crash(test_regressions) - test_restrictions2() + global lib_path + global test_path + with tempfile.TemporaryDirectory() as temp_dir: + lib_path = os.path.join(temp_dir, "lib.blend") + test_path = os.path.join(temp_dir, "test.blend") + + init() + test_users_counting() + test_linking() + test_restrictions1() + expect_exception_or_abort( + fn=test_regressions, + ex=AttributeError, + ) + test_restrictions2() if __name__ == "__main__": diff --git a/tests/python/bl_usd_import_test.py b/tests/python/bl_usd_import_test.py new file mode 100644 index 00000000000..6f157c3f928 --- /dev/null +++ b/tests/python/bl_usd_import_test.py @@ -0,0 +1,78 @@ +# ##### 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 ##### + +# + +import pathlib +import sys +import unittest + +import bpy + +args = None + +class AbstractUSDTest(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.testdir = args.testdir + + def setUp(self): + self.assertTrue(self.testdir.exists(), + 'Test dir {0} should exist'.format(self.testdir)) + + # Make sure we always start with a known-empty file. + bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend")) + +class USDImportTest(AbstractUSDTest): + + def test_import_prim_hierarchy(self): + """Test importing a simple object hierarchy from a USDA file.""" + + infile = str(self.testdir / "prim-hierarchy.usda") + + res = bpy.ops.wm.usd_import(filepath=infile) + self.assertEqual({'FINISHED'}, res) + + objects = bpy.context.scene.collection.objects + self.assertEqual(5, len(objects)) + + # Test the hierarchy. + self.assertIsNone(objects['World'].parent) + self.assertEqual(objects['World'], objects['Plane'].parent) + self.assertEqual(objects['World'], objects['Plane_001'].parent) + self.assertEqual(objects['World'], objects['Empty'].parent) + self.assertEqual(objects['Empty'], objects['Plane_002'].parent) + +def main(): + global args + import argparse + + if '--' in sys.argv: + argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:] + else: + argv = sys.argv + + parser = argparse.ArgumentParser() + parser.add_argument('--testdir', required=True, type=pathlib.Path) + args, remaining = parser.parse_known_args(argv) + + unittest.main(argv=remaining) + + +if __name__ == "__main__": + main() diff --git a/tests/python/cycles_render_tests.py b/tests/python/cycles_render_tests.py index ca0bc9f18b9..98b2cd4a200 100644 --- a/tests/python/cycles_render_tests.py +++ b/tests/python/cycles_render_tests.py @@ -18,40 +18,23 @@ BLACKLIST_OSL = [ ] BLACKLIST_OPTIX = [ - # No branched path on Optix. - 'T53854.blend', + # Ray intersection precision issues 'T50164.blend', - 'portal.blend', - 'denoise_sss.blend', - 'denoise_passes.blend', - 'distant_light.blend', - 'aov_position.blend', - 'subsurface_branched_path.blend', 'T43865.blend', ] BLACKLIST_GPU = [ - # Missing equiangular sampling on GPU. - 'area_light.blend', - 'denoise_hair.blend', - 'point_density_.*.blend', - 'point_light.blend', - 'shadow_catcher_bpt_.*.blend', - 'sphere_light.blend', - 'spot_light.blend', - 'T48346.blend', - 'world_volume.blend', # Uninvestigated differences with GPU. 'image_log.blend', - 'subsurface_behind_glass_branched.blend', 'T40964.blend', 'T45609.blend', - 'T48860.blend', 'smoke_color.blend', 'bevel_mblur.blend', # Inconsistency between Embree and Hair primitive on GPU. + 'denoise_hair.blend', 'hair_basemesh_intercept.blend', 'hair_instancer_uv.blend', + 'hair_length_info.blend', 'hair_particle_random.blend', 'principled_hair_.*.blend', 'transparent_shadow_hair.*.blend', diff --git a/tests/python/modules/render_report.py b/tests/python/modules/render_report.py index 560f8e33585..90f16dc80fb 100755 --- a/tests/python/modules/render_report.py +++ b/tests/python/modules/render_report.py @@ -410,7 +410,7 @@ class Report: failed = False except subprocess.CalledProcessError as e: if self.verbose: - print_message(e.output.decode("utf-8")) + print_message(e.output.decode("utf-8", 'ignore')) failed = e.returncode != 1 else: if not self.update: @@ -437,7 +437,7 @@ class Report: subprocess.check_output(command) except subprocess.CalledProcessError as e: if self.verbose: - print_message(e.output.decode("utf-8")) + print_message(e.output.decode("utf-8", 'ignore')) return not failed @@ -488,7 +488,7 @@ class Report: if verbose: print(" ".join(command)) if (verbose or crash) and output: - print(output.decode("utf-8")) + print(output.decode("utf-8", 'ignore')) # Detect missing filepaths and consider those errors for filepath, output_filepath in zip(remaining_filepaths[:], output_filepaths):